/* **************************************************************************
 *
 *  Copyright (C) 2011 AMLOGIC, INC.
 *
 *
 * remark: copy from @ trunk/drivers/usb/host  by Haixiang.Bao 2011.10.17
 *              haixiang.bao@amlogic.com
 *
 *
 ****************************************************************************/

#include <common.h>
#include <asm/io.h>
#include <asm/errno.h>
#include <asm/arch/usb.h>
#include <usb.h>
#include <malloc.h>
#include <linux/list.h>
#include <asm/cache.h>


#include "dwc_otg_regs.h"
#include "dwc_otg_hcd.h"

#define kmalloc(x,y)			malloc(x)
#define kfree(x)			    free(x)

#define min_t(type,x,y)	\
	({ type __x = (x); type __y = (y); __x < __y ? __x : __y; })

#define flush_cpu_cache()  do{ dcache_flush();}while(0)//flush_cache //do{_invalidate_dcache();AV_AHB_bus_invalidate();}while(0)	//for porting

/* ------------------------------------------------------- */
#define get_unaligned_16(ptr)				(((__u8 *)ptr)[0] | (((__u8 *)ptr)[1]<<8))
#define get_unaligned_32(ptr)				(((__u8 *)ptr)[0] | (((__u8 *)ptr)[1]<<8) | (((__u8 *)ptr)[2]<<16) | (((__u8 *)ptr)[3]<<24))
#define get_unaligned(ptr)				(((__u8 *)ptr)[0] | (((__u8 *)ptr)[1]<<8) | (((__u8 *)ptr)[2]<<16) | (((__u8 *)ptr)[3]<<24))

static dwc_otg_device_t dwc_otg_dev;
static int dwc_otg_port_init(dwc_otg_core_if_t * _core_if);
static int is_insert = 0;
/* The bits respond the channel buy_state. */
//static unsigned int chn_busy;
/*
void dwc_udelay(int dly)
{
	int i;
	for(i = 0;i<4000;i++);
}
*/
#define dwc_udelay(dly) udelay(dly)
/*
 * Host module config 
 */
static dwc_otg_core_params_t dwc_otg_module_params_host = {
    DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE,       /* otg_cap */
    0,                          /* dma_enable */
    -1,                         /* dma_burst_size */
    DWC_SPEED_PARAM_HIGH,       /* speed */
    0,                          /* host_support_fs_ls_low_power */
    0,                          /* host_ls_low_power_phy_clk */
    1,                          /* enable_dynamic_fifo */
    -1,                         /* data_fifo_size */
    0x100,                      /* dev_rx_fifo_size */
    0x100,                      /* dev_nperio_tx_fifo_size */
    {-1,                        /* dev_perio_tx_fifo_size *//* 0 */
     -1,                        /* 1 */
     -1,                        /* 2 */
     -1,                        /* 3 */
     -1,                        /* 4 */
     -1,                        /* 5 */
     -1,                        /* 6 */
     -1,                        /* 7 */
     -1,                        /* 8 */
     -1,                        /* 9 */
     -1,                        /* 10 */
     -1,                        /* 11 */
     -1,                        /* 12 */
     -1,                        /* 13 */
     -1},                       /* 14 */
    320,                        /* host_rx_fifo_size */
    256,                        /* host_nperio_tx_fifo_size */
    -1,                         /* host_perio_tx_fifo_size */
    65535,                      /* max_transfer_size */
    128,                        /* max_packet_count */
    6,                          /* host_channels */
    2,                          /* dev_endpoints */
    -1,                         /* phy_type */
    -1,                         /* phy_utmi_width */
    0,                          /* phy_ulpi_ddr */
    0,                          /* i2c_enable */
};

/**
 * This function disables the controller's Global Interrupt in the AHB Config
 * register.
 *
 * @param[in] _core_if Programming view of DWC_otg controller.
 */
static void
dwc_otg_disable_global_interrupts(dwc_otg_core_if_t * _core_if)
{
    gahbcfg_data_t  ahbcfg = {.d32 = 0 };
    ahbcfg.b.glblintrmsk = 0;   /* disable interrupts */
    dwc_modify_reg32(&_core_if->core_global_regs->gahbcfg, ahbcfg.d32, 0);
}
/**
 * This function enables the controller's Global Interrupt in the AHB Config
 * register.
 *
 * @param[in] _core_if Programming view of DWC_otg controller.
 */
extern void
dwc_otg_enable_global_interrupts(dwc_otg_core_if_t * _core_if)
{
    gahbcfg_data_t  ahbcfg = {.d32 = 0 };
    ahbcfg.b.glblintrmsk = 1;   /* Enable interrupts */
    dwc_modify_reg32(&_core_if->core_global_regs->gahbcfg, 0, ahbcfg.d32);
}

static dwc_otg_core_if_t *
dwc_otg_cil_init(const uint32_t * _reg_base_addr, dwc_otg_core_params_t * _core_params)
{

    dwc_otg_core_if_t *core_if = 0;
    // dwc_otg_dev_if_t *dev_if = 0;
    dwc_otg_host_if_t *host_if = 0;
    uint8_t        *reg_base = (uint8_t *) _reg_base_addr;
    int             i = 0;

    DWC_DEBUGPL(DBG_CILV, "%s(%p,%p)\n", __func__, _reg_base_addr, _core_params);

    core_if = (dwc_otg_core_if_t *) kmalloc(sizeof(dwc_otg_core_if_t), 0);

    if (core_if == 0) {
        DWC_DEBUGPL(DBG_CIL, "Allocation of dwc_otg_core_if_t failed\n");
        return 0;
    }

    memset(core_if, 0, sizeof(dwc_otg_core_if_t));

    core_if->core_params = _core_params;
    core_if->core_global_regs = (dwc_otg_core_global_regs_t *) reg_base;

    /*
     * Allocate the Host Mode structures.
     */
    host_if = (dwc_otg_host_if_t *) kmalloc(sizeof(dwc_otg_host_if_t), 0);

    if (host_if == 0) {
        DWC_DEBUGPL(DBG_CIL, "Allocation of dwc_otg_host_if_t failed\n");
        // kfree(dev_if);
        kfree(core_if);
        return 0;
    }

    host_if->host_global_regs = (dwc_otg_host_global_regs_t *)
        (reg_base + DWC_OTG_HOST_GLOBAL_REG_OFFSET);

    host_if->hprt0 = (uint32_t *) (reg_base + DWC_OTG_HOST_PORT_REGS_OFFSET);

    for (i = 0; i < MAX_EPS_CHANNELS; i++) {
        host_if->hc_regs[i] = (dwc_otg_hc_regs_t *)
            (reg_base + DWC_OTG_HOST_CHAN_REGS_OFFSET + (i * DWC_OTG_CHAN_REGS_OFFSET));
        DWC_DEBUGPL(DBG_CILV, "hc_reg[%d]->hcchar=%p\n", i, &host_if->hc_regs[i]->hcchar);
    }

    host_if->num_host_channels = MAX_EPS_CHANNELS;
    core_if->host_if = host_if;

    for (i = 0; i < MAX_EPS_CHANNELS; i++) {
        core_if->data_fifo[i] =
            (uint32_t *) (reg_base + DWC_OTG_DATA_FIFO_OFFSET + (i * DWC_OTG_DATA_FIFO_SIZE));
        DWC_DEBUGPL(DBG_CILV, "data_fifo[%d]=0x%08x\n", i, (unsigned) core_if->data_fifo[i]);
    }

    core_if->pcgcctl = (uint32_t *) (reg_base + DWC_OTG_PCGCCTL_OFFSET);

    /*
     * Store the contents of the hardware configuration registers here for
     * easy access later.
     */
    core_if->hwcfg1.d32 = dwc_read_reg32(&core_if->core_global_regs->ghwcfg1);
    core_if->hwcfg2.d32 = dwc_read_reg32(&core_if->core_global_regs->ghwcfg2);
    core_if->hwcfg3.d32 = dwc_read_reg32(&core_if->core_global_regs->ghwcfg3);
    core_if->hwcfg4.d32 = dwc_read_reg32(&core_if->core_global_regs->ghwcfg4);

    DWC_DEBUGPL(DBG_CILV, "hwcfg1=%08x\n", core_if->hwcfg1.d32);
    DWC_DEBUGPL(DBG_CILV, "hwcfg2=%08x\n", core_if->hwcfg2.d32);
    DWC_DEBUGPL(DBG_CILV, "hwcfg3=%08x\n", core_if->hwcfg3.d32);
    DWC_DEBUGPL(DBG_CILV, "hwcfg4=%08x\n", core_if->hwcfg4.d32);

    DWC_DEBUGPL(DBG_CILV, "op_mode=%0x\n", core_if->hwcfg2.b.op_mode);
    DWC_DEBUGPL(DBG_CILV, "arch=%0x\n", core_if->hwcfg2.b.architecture);
    DWC_DEBUGPL(DBG_CILV, "num_dev_ep=%d\n", core_if->hwcfg2.b.num_dev_ep);
    DWC_DEBUGPL(DBG_CILV, "num_host_chan=%d\n", core_if->hwcfg2.b.num_host_chan);
    DWC_DEBUGPL(DBG_CILV, "nonperio_tx_q_depth=0x%0x\n", core_if->hwcfg2.b.nonperio_tx_q_depth);
    DWC_DEBUGPL(DBG_CILV, "host_perio_tx_q_depth=0x%0x\n", core_if->hwcfg2.b.host_perio_tx_q_depth);
    DWC_DEBUGPL(DBG_CILV, "dev_token_q_depth=0x%0x\n", core_if->hwcfg2.b.dev_token_q_depth);

    DWC_DEBUGPL(DBG_CILV, "Total FIFO SZ=%d\n", core_if->hwcfg3.b.dfifo_depth);
    DWC_DEBUGPL(DBG_CILV, "xfer_size_cntr_width=%0x\n", core_if->hwcfg3.b.xfer_size_cntr_width);
    
    /*
     * Set the SRP sucess bit for FS-I2c
     */
    core_if->srp_success = 0;
    core_if->srp_timer_started = 0;
    core_if->temp_buffer = kmalloc(DWC_OTG_MAX_TRANSFER_SIZE, 0);
    if(!core_if->temp_buffer) {
        kfree(core_if->host_if);
        kfree(core_if);
        return 0;
    }        

    return core_if;

}
/**
 * This function initializes the commmon interrupts, used in both
 * device and host modes.
 *
 * @param[in] _core_if Programming view of the DWC_otg controller
 *
 */
static void
dwc_otg_enable_common_interrupts(dwc_otg_core_if_t * _core_if)
{
    dwc_otg_core_global_regs_t *global_regs = _core_if->core_global_regs;
    gintmsk_data_t  intr_mask = {.d32 = 0 };

    /*
     * Clear any pending OTG Interrupts 
     */
    dwc_write_reg32(&global_regs->gotgint, 0xFFFFFFFF);

    /*
     * Clear any pending interrupts 
     */
    dwc_write_reg32(&global_regs->gintsts, 0xFFFFFFFF);

    /*
     * Enable the interrupts in the GINTMSK. 
     */
    intr_mask.b.modemismatch = 1;
    intr_mask.b.otgintr = 1;

    if (!_core_if->dma_enable) {
        intr_mask.b.rxstsqlvl = 1;
    }

    intr_mask.b.conidstschng = 1;
    intr_mask.b.wkupintr = 1;
    intr_mask.b.disconnect = 1;
    intr_mask.b.usbsuspend = 1;
    intr_mask.b.sessreqintr = 1;
    dwc_write_reg32(&global_regs->gintmsk, intr_mask.d32);
}
/**
 * Do core a soft reset of the core.  Be careful with this because it
 * resets all the internal state machines of the core.
 */
void
dwc_otg_core_reset(dwc_otg_core_if_t * _core_if)
{
    dwc_otg_core_global_regs_t *global_regs = _core_if->core_global_regs;
    volatile grstctl_t greset = {.d32 = 0 };
    int             count = 0;

    DBG("%s\n", __func__);
    /*
     * Wait for AHB master IDLE state. 
     */
    do {
        dwc_udelay(10);
        greset.d32 = dwc_read_reg32(&global_regs->grstctl);
        if (++count > 100000) {
            DBG("%s() HANG! AHB Idle GRSTCTL=%0x\n", __func__, greset.d32);
            return;
        }
    }
    while (greset.b.ahbidle == 0);

    /*
     * Core Soft Reset 
     */
    count = 0;
    greset.b.csftrst = 1;
    dwc_write_reg32(&global_regs->grstctl, greset.d32);
    do {
        dwc_udelay(10);
        greset.d32 = dwc_read_reg32(&global_regs->grstctl);
        if (++count > 1000000) {
            DBG("%s() HANG! Soft Reset GRSTCTL=%0x\n", __func__, greset.d32);
            break;
        }
    }
    while (greset.b.csftrst == 1);

    /*
     * Wait for 3 PHY Clocks
     */
    wait_ms(100);
}

/**
 * Initializes the FSLSPClkSel field of the HCFG register depending on the PHY
 * type.
 */
static void
init_fslspclksel(dwc_otg_core_if_t * _core_if)
{
    uint32_t        val;
    hcfg_data_t     hcfg;

    if (_core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) {
        /*
         * Full speed PHY 
         */
        val = DWC_HCFG_48_MHZ;
    } else {
        /*
         * High speed PHY running at full speed or high speed 
         */
        val = DWC_HCFG_30_60_MHZ;
    }

    DWC_DEBUGPL(DBG_CIL, "Initializing HCFG.FSLSPClkSel to 0x%1x\n", val);
    hcfg.d32 = dwc_read_reg32(&_core_if->host_if->host_global_regs->hcfg);
    hcfg.b.fslspclksel = val;
    dwc_write_reg32(&_core_if->host_if->host_global_regs->hcfg, hcfg.d32);
}

/**
 * Initializes the DevSpd field of the DCFG register depending on the PHY type
 * and the enumeration speed of the device.
 */
static void
init_devspd(dwc_otg_core_if_t * _core_if)
{
    uint32_t        val;
    dcfg_data_t     dcfg;

    if (_core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) {
        /*
         * Full speed PHY 
         */
        val = 0x3;
    } else if (_core_if->core_params->speed == DWC_SPEED_PARAM_FULL) {
        /*
         * High speed PHY running at full speed 
         */
        val = 0x1;
    } else {
        /*
         * High speed PHY running at high speed 
         */
        val = 0x0;
    }

    DWC_DEBUGPL(DBG_CIL, "Initializing DCFG.DevSpd to 0x%1x\n", val);
    dcfg.d32 = dwc_read_reg32(&_core_if->dev_if->dev_global_regs->dcfg);
    dcfg.b.devspd = val;
    dwc_write_reg32(&_core_if->dev_if->dev_global_regs->dcfg, dcfg.d32);
}

/**
 * This function initializes the DWC_otg controller registers and
 * prepares the core for device mode or host mode operation.
 *
 * @param _core_if Programming view of the DWC_otg controller
 *
 */

int
dwc_otg_core_init(dwc_otg_core_if_t * _core_if)
{
    dwc_otg_core_global_regs_t *global_regs = _core_if->core_global_regs;
    // dwc_otg_dev_if_t *dev_if = _core_if->dev_if;
    gahbcfg_data_t  ahbcfg = {.d32 = 0 };
    gusbcfg_data_t  usbcfg = {.d32 = 0 };
    gi2cctl_data_t  i2cctl = {.d32 = 0 };

    DWC_DEBUGPL(DBG_CILV, "dwc_otg_core_init(%p)\n", _core_if);

    /*
     * Reset the Controller 
     */
    dwc_otg_core_reset(_core_if);

    _core_if->total_fifo_size = _core_if->hwcfg3.b.dfifo_depth;
    _core_if->rx_fifo_size = dwc_read_reg32(&global_regs->grxfsiz);
    _core_if->nperio_tx_fifo_size = dwc_read_reg32(&global_regs->gnptxfsiz) >> 16;

    DWC_DEBUGPL(DBG_CIL, "Total FIFO SZ=%d\n", _core_if->total_fifo_size);
    DWC_DEBUGPL(DBG_CIL, "Rx FIFO SZ=%d\n", _core_if->rx_fifo_size);
    DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO SZ=%d\n", _core_if->nperio_tx_fifo_size);

    /*
     * This programming sequence needs to happen in FS mode before any other
     * programming occurs 
     */
    if ((_core_if->core_params->speed == DWC_SPEED_PARAM_FULL) &&
        (_core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) {
        /*
         * If FS mode with FS PHY 
         */

        /*
         * core_init() is now called on every switch so only call the
         * following for the first time through. 
         */
        if (!_core_if->phy_init_done) {
            _core_if->phy_init_done = 1;
            DWC_DEBUGPL(DBG_CIL, "FS_PHY detected\n");
            usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg);
            usbcfg.b.physel = 1;
            dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32);

            /*
             * Reset after a PHY select 
             */
            dwc_otg_core_reset(_core_if);
        }

        /*
         * Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also do
         * this on HNP Dev/Host mode switches (done in dev_init and
         * host_init). 
         */
        if (dwc_otg_is_host_mode(_core_if)) {
            init_fslspclksel(_core_if);
        } else {
            init_devspd(_core_if);
        }

        if (_core_if->core_params->i2c_enable) {
            DWC_DEBUGPL(DBG_CIL, "FS_PHY Enabling I2c\n");
            /*
             * Program GUSBCFG.OtgUtmifsSel to I2C 
             */
            usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg);
            usbcfg.b.otgutmifssel = 1;
            dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32);

            /*
             * Program GI2CCTL.I2CEn 
             */
            i2cctl.d32 = dwc_read_reg32(&global_regs->gi2cctl);
            i2cctl.b.i2cdevaddr = 1;
            i2cctl.b.i2cen = 0;
            dwc_write_reg32(&global_regs->gi2cctl, i2cctl.d32);
            i2cctl.b.i2cen = 1;
            dwc_write_reg32(&global_regs->gi2cctl, i2cctl.d32);
        }

    }
    /*
     * endif speed == DWC_SPEED_PARAM_FULL 
     */
    else {
        /*
         * High speed PHY. 
         */
        if (!_core_if->phy_init_done) {
            _core_if->phy_init_done = 1;
            /*
             * HS PHY parameters.  These parameters are preserved during soft 
             * reset so only program the first time.  Do a soft reset
             * immediately after setting phyif.  
             */
            usbcfg.b.ulpi_utmi_sel = _core_if->core_params->phy_type;
            if (usbcfg.b.ulpi_utmi_sel == 1) {
                /*
                 * ULPI interface 
                 */
                usbcfg.b.phyif = 0;
                usbcfg.b.ddrsel = _core_if->core_params->phy_ulpi_ddr;
            } else {
                /*
                 * UTMI+ interface 
                 */
                if (_core_if->core_params->phy_utmi_width == 16) {
                    usbcfg.b.phyif = 1;
                } else {
                    usbcfg.b.phyif = 0;
                }
            }

            dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32);

            /*
             * Reset after setting the PHY parameters 
             */
            dwc_otg_core_reset(_core_if);
        }
    }

    /*
     * Program the GAHBCFG Register.
     */
    switch (_core_if->hwcfg2.b.architecture) {

    case DWC_SLAVE_ONLY_ARCH:
        DWC_DEBUGPL(DBG_CIL, "Slave Only Mode\n");
        ahbcfg.b.nptxfemplvl_txfemplvl = DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY;
        ahbcfg.b.ptxfemplvl = DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY;
        _core_if->dma_enable = 0;
        break;

    case DWC_EXT_DMA_ARCH:
        DWC_DEBUGPL(DBG_CIL, "External DMA Mode\n");
        ahbcfg.b.hburstlen = DWC_GAHBCFG_INT_DMA_BURST_INCR8;
        _core_if->dma_enable = (_core_if->core_params->dma_enable != 0);
        break;

    case DWC_INT_DMA_ARCH:
        DWC_DEBUGPL(DBG_CIL, "Internal DMA Mode\n");
        // ahbcfg.b.hburstlen =
        // _core_if->core_params->dma_burst_size;//config by out world
        ahbcfg.b.hburstlen = DWC_GAHBCFG_INT_DMA_BURST_INCR8;   // config by
        // out world
        _core_if->dma_enable = (_core_if->core_params->dma_enable != 0);
        break;

    }

    ahbcfg.b.dmaenable = _core_if->dma_enable;
    dwc_write_reg32(&global_regs->gahbcfg, ahbcfg.d32);

    // _core_if->en_multiple_tx_fifo = _core_if->hwcfg4.b.ded_fifo_en;


    /*
     * Program the GUSBCFG register. 
     */
    usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg);

    switch (_core_if->hwcfg2.b.op_mode) {
    case DWC_MODE_HNP_SRP_CAPABLE:
        usbcfg.b.hnpcap = (_core_if->core_params->otg_cap == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE);
        usbcfg.b.srpcap = (_core_if->core_params->otg_cap != DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE);
        break;

    case DWC_MODE_SRP_ONLY_CAPABLE:
        usbcfg.b.hnpcap = 0;
        usbcfg.b.srpcap = (_core_if->core_params->otg_cap != DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE);
        break;

    case DWC_MODE_NO_HNP_SRP_CAPABLE:
        usbcfg.b.hnpcap = 0;
        usbcfg.b.srpcap = 0;
        break;

    case DWC_MODE_SRP_CAPABLE_DEVICE:
        usbcfg.b.hnpcap = 0;
        usbcfg.b.srpcap = (_core_if->core_params->otg_cap != DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE);
        break;

    case DWC_MODE_NO_SRP_CAPABLE_DEVICE:
        usbcfg.b.hnpcap = 0;
        usbcfg.b.srpcap = 0;
        break;

    case DWC_MODE_SRP_CAPABLE_HOST:
        usbcfg.b.hnpcap = 0;
        usbcfg.b.srpcap = (_core_if->core_params->otg_cap != DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE);
        break;

    case DWC_MODE_NO_SRP_CAPABLE_HOST:
        usbcfg.b.hnpcap = 0;
        usbcfg.b.srpcap = 0;
        break;
    }

    dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32);

    /*
     * Enable common interrupts 
     */
    dwc_otg_enable_common_interrupts(_core_if);

    /*
     * Do device or host intialization based on mode during PCD and HCD
     * initialization 
     */
    if (dwc_otg_is_host_mode(_core_if)) {
        DWC_DEBUGPL(DBG_ANY, "Host Mode\n");
        _core_if->op_state = A_HOST;
    } else {
        ERR("Device Mode\n");
        _core_if->op_state = B_PERIPHERAL;
#ifdef DWC_DEVICE_ONLY
        dwc_otg_core_dev_init(_core_if);
#endif
    return -1;
    }
     return 0;

}
/** 
 * This function disables the Host Mode interrupts.
 *
 * @param _core_if Programming view of DWC_otg controller
 */
void
dwc_otg_disable_host_interrupts(dwc_otg_core_if_t * _core_if)
{
    dwc_otg_core_global_regs_t *global_regs = _core_if->core_global_regs;

    DWC_DEBUGPL(DBG_CILV, "%s()\n", __func__);

    dwc_write_reg32 (&global_regs->gintmsk, 0);
    dwc_modify_reg32 (&global_regs->gahbcfg, 1, 0);

}

/**
 * Halts the DWC_otg host mode operations in a clean manner. USB transfers are
 * stopped.
 */
void
dwc_otg_hcd_stop(dwc_otg_core_if_t * core_if)
{
    hprt0_data_t    hprt0 = {.d32 = 0 };
    pcgcctl_data_t hcgcctl = {.d32 = 0};

    if (core_if == 0)
        return;

    DBG("DWC OTG HCD STOP\n");

    /*
     * Turn off all host-specific interrupts. 
     */
    dwc_otg_disable_host_interrupts(core_if);

    /*
     * The root hub should be disconnected before this function is called.
     * The disconnect will clear the QTD lists (via ..._hcd_urb_dequeue)
     * and the QH lists (via ..._hcd_endpoint_disable).
     */

    /*
     * Turn off the vbus power 
     */
    DBG("PortPower off\n");
    hprt0.b.prtpwr = 0;
    dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32);

    hcgcctl.b.stoppclk  = 1;
    hcgcctl.b.gatehclk = 1;
    hcgcctl.b.pwrclmp = 1;
    //hcgcctl.b.rstpdwnmodule = 1;
	//printf("core_if->pcgcctl  : 0x%x, & = 0x%x\n",core_if->pcgcctl,&core_if->pcgcctl );
	
    dwc_write_reg32(core_if->pcgcctl,hcgcctl.d32);
    return;
}


/*
 * --- API functions ------------------------------------------------------ 
 */
int
submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int len, int interval)
{
    DBG("dev=%p pipe=%#lx buf=%p size=%d int=%d", dev, pipe, buffer, len, interval);

    return -1;
}
static inline int
max_transfer_len(struct usb_device *dev, unsigned long pipe)
{
    unsigned        mpck = usb_maxpacket(dev, pipe);

    /*
     * One PTD can transfer 1023 bytes but try to always transfer multiples
     * of endpoint buffer size 
     */
    return 1023 / mpck * mpck;
}

/**
 * Clears the transfer state for a host channel. This function is normally
 * called after a transfer is done and the host channel is being released.
 *
 * @param _core_if Programming view of DWC_otg controller.
 * @param _hc Identifies the host channel to clean up.
 */
void
dwc_otg_hc_cleanup(dwc_otg_core_if_t * _core_if, int hc_num)
{
    dwc_otg_hc_regs_t *hc_regs;

    /*
     * Clear channel interrupt enables and any unhandled channel interrupt
     * conditions.
     */
    hc_regs = _core_if->host_if->hc_regs[hc_num];
    dwc_write_reg32(&hc_regs->hcintmsk, 0);
    dwc_write_reg32(&hc_regs->hcint, 0xFFFFFFFF);
    dwc_write_reg32(&hc_regs->hctsiz, 0);
    dwc_write_reg32(&hc_regs->hcdma, 0);
    dwc_write_reg32(&hc_regs->hcchar, 0);

    flush_cpu_cache();

}

void dwc_otg_read_packet(dwc_otg_core_if_t * core_if, uint8_t * _dest, uint16_t _bytes,int hcnum)
{
	int i;
	int word_count = (_bytes + 3) / 4;
	volatile uint32_t *fifo = core_if->data_fifo[hcnum];
	uint32_t *data_buff = (uint32_t *) _dest;
	for (i = 0; i < word_count; i++, data_buff++) {
		*data_buff = dwc_read_reg32(fifo);
	}
	return;
}

static void dwc_otg_hcd_handle_rx_status_q_level_intr(dwc_otg_core_if_t * _core_if, int is_setup,int hcnum, void *buffer)
{
	host_grxsts_data_t grxsts;

	grxsts.d32 = dwc_read_reg32(&_core_if->core_global_regs->grxstsp);
	//printf("RxStsQ:ch%d,cnt=%d,pid=%d,PStatus=%d\n",grxsts.b.chnum,grxsts.b.bcnt,grxsts.b.dpid,grxsts.b.pktsts);

	switch (grxsts.b.pktsts) {
	case DWC_GRXSTS_PKTSTS_IN:
		/* Read the data into the host buffer. */
		if (grxsts.b.bcnt > 0) {
			dwc_otg_read_packet(_core_if, buffer, grxsts.b.bcnt, hcnum);
			/* Update the HC fields for the next packet received. */
			buffer += grxsts.b.bcnt;
		}
	case DWC_GRXSTS_PKTSTS_IN_XFER_COMP:
	case DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR:
	case DWC_GRXSTS_PKTSTS_CH_HALTED:
		/* Handled in interrupt, just ignore data */
		break;
	default:
		ERR("RX_STS_Q Interrupt: Unknown status %d\n",
			  grxsts.b.pktsts);
		break;
	}

	return;
}

void dwc_otg_hc_write_packet(dwc_otg_core_if_t * _core_if, void *buffer,int len, int chn_num)
{
	uint32_t i;
	uint32_t byte_count;
	uint32_t dword_count;

	uint32_t *data_buff = (uint32_t *)buffer;
	uint32_t *data_fifo = _core_if->data_fifo[chn_num];//just used channel0

	byte_count = len ;

	dword_count = (byte_count + 3) / 4;

	if ((((unsigned long)data_buff) & 0x3) == 0) {
		/* xfer_buff is DWORD aligned. */
		for (i = 0; i < dword_count; i++, data_buff++) {
			dwc_write_reg32(data_fifo, *data_buff);
		}
	} else {
		/* xfer_buff is not DWORD aligned. */

		for (i = 0; i < dword_count; i++, data_buff++) {
			dwc_write_reg32(data_fifo, get_unaligned(data_buff));
		}
	}
	//printf("dwc_otg_hc_write_packet finished  %d  %d chn%d\n", dword_count, len,chn_num);
}

static void dwc_otg_hc_do_ping(dwc_otg_core_if_t *_core_if, int hc_num)
{
	hcchar_data_t hcchar;
	hctsiz_data_t hctsiz;
	dwc_otg_hc_regs_t *hc_regs = _core_if->host_if->hc_regs[hc_num];

	DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc_num);

	hctsiz.d32 = 0;
	hctsiz.b.dopng = 1;
	hctsiz.b.pktcnt = 1;
	dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32);

	hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar);
	hcchar.b.chen = 1;
	hcchar.b.chdis = 0;
	dwc_write_reg32(&hc_regs->hcchar, hcchar.d32);
}

static int
dwc_otg_interrupt(dwc_otg_core_if_t * _core_if, int is_setup,int hcnum,void * buffer)
{
    gintsts_data_t  gintsts;
    dwc_otg_hc_regs_t *hc_regs;
    hctsiz_data_t   hctsiz;
    hcint_data_t    hcint;
    hcchar_data_t   hcchar;
    hcintmsk_data_t hcintmsk;
    // dwc_otg_host_if_t *host_if ;
    int             handled;
    dwc_otg_host_if_t *host_if = _core_if->host_if;

  do{
    gintsts.d32 = dwc_read_reg32(&_core_if->core_global_regs->gintsts);
    hc_regs = host_if->hc_regs[hcnum];
    hcint.d32 = dwc_read_reg32(&hc_regs->hcint);
    handled = 0;

    if (gintsts.b.rxstsqlvl && (!_core_if->dma_enable)){
	    dwc_otg_hcd_handle_rx_status_q_level_intr(_core_if,is_setup,hcnum,buffer);
        DBG("rxstsqlvl interrupt!\n");
        _core_if->host_if->do_ping = 0;	
        handled = 1;
    }
    if (gintsts.b.hcintr) {
        //DBG("dwc_otg_interrupt: hcintr interrupt!\n");
        if (hcint.b.xfercomp) {
            hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz);
            dwc_otg_hc_cleanup(_core_if, hcnum);	    
            if(is_setup)
                return 8;
            return (_core_if->transfer_size);// - hctsiz.b.xfersize);
        }
        if (hcint.b.ahberr) {
            ERR("ahberr interrupt!\n");
            hcintmsk.d32 = 0;
            hcintmsk.b.chhltd = 1;
            dwc_write_reg32(&hc_regs->hcintmsk, hcintmsk.d32);
            dwc_write_reg32(&hc_regs->hcint, ~hcintmsk.d32);
            hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar);
            if (hcchar.b.chen == 0)
                return -1;
            else {
                hcchar.b.chen = 1;
                hcchar.b.chdis = 1;
                dwc_write_reg32(&hc_regs->hcchar, hcchar.d32);
            }

            return -1;

        }
        if (hcint.b.nyet) {
            DBG("nyet interrupt!\n");
            _core_if->host_if->do_ping = 1;	
            //return -1;

        }
        if (hcint.b.ack) {
            DBG("ack interrupt!\n");
            _core_if->host_if->do_ping = 0;	
            //return -1;

        }	
        if (hcint.b.xacterr) {
            ERR("xacterr interrupt!\n");
            return -1;

        }
        if (hcint.b.bblerr) {
            ERR("bblerr interrupt!\n");
            return -1;
        }
        if (hcint.b.datatglerr) {
            ERR("datatglerr interrupt!\n");
            return -1;
        }

    }
    if(gintsts.b.portintr){
        ERR("hprt0:0x%08x\n",dwc_read_reg32(_core_if->host_if->hprt0));
        dwc_otg_port_init(_core_if);
        return -2;
    }

  }while(handled);

    if (gintsts.b.ptxfempty) {
        //DBG("ptxfempty interrupt!\n");
        return -1;
    }
    return -1;

}

/*
 * Do an USB transfer , dir = 0, out ;  dir = 1 , in
 */

static int
dwc_otg_submit_job(struct usb_device *dev, unsigned long pipe, int dir, void *buffer, int len,
                   int is_setup)
{

    dwc_otg_core_if_t *core_if = dwc_otg_dev.core_if;
    dwc_otg_hc_regs_t *hc_regs;
    hcchar_data_t   hcchar;
    hcsplt_data_t   hcsplt;
    hctsiz_data_t   hctsiz;
    int             devnum = usb_pipedevice(pipe);
    int             epnum = usb_pipeendpoint(pipe);
//    int             dir_in = usb_pipein(pipe);
    int             toggle,chg_flag = 0;
    static int             hcnum = 0;
    hcintmsk_data_t hcintmsk;
    dwc_otg_host_if_t *host_if = core_if->host_if;
    int stat, timeout;
    int allchannel_int_mask;
    hcint_data_t    hcint;
    dwc_otg_host_global_regs_t *host_global_regs = host_if->host_global_regs;

//    printf("dwc_otg_submit_job: dev: %d ,dev->parent: %x, dev->speed: %d\n",
//		dev->devnum,dev->parent,dev->speed);

//	struct usb_device * hub1 = usb_get_dev_index(1);

//	if(hub1)
//	    printf("root_hub speed %d\n",hub1->speed);
//	if(dev->parent != NULL) //not roothub
//		if(dev->parent->speed ==2 && dev->speed != 2 )
//			return -EOPNOTSUPP;
	
    int             type = usb_pipetype(pipe);
    if (len >= 4096) {
        ERR("Too big job!\n");
        dev->status = USB_ST_CRC_ERR;
        return -E2BIG;
    }

    if (type == PIPE_ISOCHRONOUS) {
        ERR("isochronous transfers not supported");
        dev->status = USB_ST_CRC_ERR;
        return -EOPNOTSUPP;
    }

    //hcnum++;
    if(hcnum > 6) hcnum = 0;

    hc_regs = host_if->hc_regs[hcnum];
  /*  
	if(!is_insert && (!core_if->dma_enable)){
		wait_ms(10);
		if(dwc_otg_interrupt(core_if, is_setup,hcnum,buffer) == -2)
			is_insert = 1;
		else{
			DBG("wait insert failed");
			return -1;
		}

	}	*/
    //chn_busy = chn_busy | 1<<hcnum ;
    /*
     * init hc char (corresponding devaddr and epnum)
     */
    hcchar.d32 = 0;
    hcchar.b.devaddr = devnum;
    hcchar.b.epnum = epnum;
    hcchar.b.epdir = dir;
    hcchar.b.lspddev = 0;
    
    /* set hc interrupt mask */
    hcintmsk.d32 = dwc_read_reg32(&hc_regs->hcintmsk);
    //hcintmsk.b.xfercompl = 1;
    hcintmsk.d32 = 7;
    dwc_write_reg32(&hc_regs->hcintmsk, hcintmsk.d32 );
    
    /* clear int status*/
    hcint.d32 = dwc_read_reg32(&hc_regs->hcint);

    dwc_write_reg32(&hc_regs->hcint, hcint.d32 );

    /* set all channel interrupt mask */
    allchannel_int_mask = dwc_read_reg32(&host_global_regs->haintmsk);
    allchannel_int_mask |= 1;
    dwc_write_reg32(&host_global_regs->haintmsk, allchannel_int_mask);
    
    switch (usb_pipetype(pipe)) {
    case PIPE_CONTROL:
        hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL;
        break;
    case PIPE_BULK:
        hcchar.b.eptype = DWC_OTG_EP_TYPE_BULK;
        break;
    case PIPE_INTERRUPT:
    case PIPE_ISOCHRONOUS:
        ERR("dwc_otg_submit_job: not support iso or interrupt transfer!\n");
        return -1;
    }

    hcchar.b.mps = usb_maxpacket(dev, pipe);

    dwc_write_reg32(&hc_regs->hcchar, hcchar.d32);
    hcsplt.d32 = 0;

    dwc_write_reg32(&hc_regs->hcsplt, hcsplt.d32);
    flush_cpu_cache();


    hctsiz.d32 = 0;

    hctsiz.b.pktcnt = (len  + usb_maxpacket(dev, pipe) - 1)/ usb_maxpacket(dev, pipe);
    if(hctsiz.b.pktcnt < 1)
        hctsiz.b.pktcnt = 1;
    if(hctsiz.b.pktcnt & 0x1)
        chg_flag = 1;
    toggle = usb_gettoggle(dev, epnum, dir);
    DBG("packet is :%d, max packet size = %d,toggle %d\n", 
		hctsiz.b.pktcnt, usb_maxpacket(dev, pipe), toggle);

    switch (toggle) {
    case 0:
        hctsiz.b.pid = DWC_OTG_HC_PID_DATA0;
        break;
    case 1:
        hctsiz.b.pid = DWC_OTG_HC_PID_DATA1;
        break;
    default:
        ERR("error toggle!\n");
        return -EPROTO;
    }
    if (is_setup)
        hctsiz.b.pid = DWC_OTG_HC_PID_SETUP;
    
    core_if->transfer_size = len;
    hctsiz.b.xfersize = len;

    if(!is_setup && dir == 0 && core_if->host_if->do_ping){
    	if (!core_if->dma_enable) {
		dwc_otg_hc_do_ping(core_if, 0);		
		return -10;//It's ping state,pls do it again!
	}
        hctsiz.b.dopng = 1;
    }
    else
        hctsiz.b.dopng = 0;

    DBG("ep%d packet is :%d, max packet size = %d = %d,b.len %d\n", hcchar.b.epnum,\
		hctsiz.b.pktcnt, usb_maxpacket(dev, pipe), hcchar.b.mps,hctsiz.b.xfersize);

    dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32);


    dwc_write_reg32(&hc_regs->hcdma, (uint32_t) buffer);
    flush_cpu_cache();

    hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar);

    hcchar.b.multicnt = 1;      // for no split transfer set this bit 1
    hcchar.b.chen = 1;
    hcchar.b.chdis = 0;

    dwc_write_reg32(&hc_regs->hcchar, hcchar.d32);

    if (!core_if->dma_enable && !dir && len > 0) {
        /* Load OUT packet into the appropriate Tx FIFO. */
        dwc_otg_hc_write_packet(core_if, buffer,len,hcnum);
    }
    else
        ;//printf("dmaable:%d, %s,len=%d\n",core_if->dma_enable,dir?"IN":"OUT",len);

    if (usb_pipebulk(pipe))
        timeout = 1000;
    else
        timeout = 100;
    /*
     * Wait for it to complete 
     */
    while(1) {
        udelay(20);
        /*
         * Check whether the controller is done 
         */
        stat = dwc_otg_interrupt(core_if, is_setup,hcnum,buffer);

        if (stat >= 0){
    	    if(chg_flag) 
			      usb_dotoggle(dev, epnum, dir);
		      break;
        }
            
        /*
         * Check the timeout 
         */
        if (--timeout)
       	    udelay(10);
        else {
	    hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz);
            DBG("TIMEOUT-%s-%d-%d-%d\n",dir?"IN":"OUT",len,hctsiz.b.pktcnt,usb_pipetype(pipe));
            break;
        }
    }
    return stat;
}
#define CTL_RETRY 5
int
submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
                   int len, struct devrequest *setup)
{
    int             epnum = usb_pipeendpoint(pipe);
    int             dir_in = usb_pipein(pipe);
    int             done;
    int             ret,retry;
    //char            *buffer;    
//    dwc_otg_core_if_t *core_if = dwc_otg_dev.core_if;
    
    /*
     * Setup phase 
     */
    retry = 0;
SETUP_RETRY:
    DBG("--- SETUP PHASE  --------------------try: %d\n",retry);
    DBG("dev=%ld ep=%ld buf=%x size=%d %s req:%d",
        usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, (dir_in?"dir_in":"dir_out"),setup->request);

    usb_settoggle(dev, epnum, 1, 0);
    ret = dwc_otg_submit_job(dev, pipe, 0, setup, sizeof(struct devrequest), 1);
    if (ret < 0) {
       if(retry++ < CTL_RETRY)
            goto SETUP_RETRY;
    else{ is_insert = 0;
           ERR("control setup phase error (ret = %d)", ret);
           return -1;
       }
    }

    /*
     * Data phase 
     */
    retry = 0;
DATA_RETRY:
    DBG("--- DATA PHASE   --------------------try: %d\n",retry);

   
    done = 0;
    usb_settoggle(dev, epnum, dir_in, 1);
    if (done < len) {

       dwc_udelay(100);
	 
        ret = dwc_otg_submit_job(dev, pipe,
                                 dir_in,
                                 (__u8 *) buffer, len, 0);
        
        if (ret < 0) {
                if(retry++ < CTL_RETRY)
                        goto DATA_RETRY;
                else{ is_insert = 0;
                        ERR("control data phase error (ret = %d)", ret);
                        return -1;
                }
        }
        //if (!dir_in && ret < usb_maxpacket(dev, pipe))  /* short packet */
        //    break;

        done += ret;
    }

    /*
     * Status phase 
     */
    retry = 0;
STATUS_RETRY:
    DBG("--- STATUS PHASE -------------------try: %d\n",retry);
    dwc_udelay(100);

    usb_settoggle(dev, epnum, !dir_in, 1);
    ret = dwc_otg_submit_job(dev, pipe, !dir_in, NULL, 0, 0);
    if (ret < 0) {
          if(retry++ < CTL_RETRY)
               goto STATUS_RETRY;
          else{ is_insert = 0;
               ERR("control status phase error (ret = %d)", ret);
               return -1;
          }
    }

    dev->act_len = done;
    DBG("return size = %d\n", done);
    dev->status = 0;

    return done;
}


int
submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buf, int len)
{
    int             dir_out = usb_pipeout(pipe);
    int             max = max_transfer_len(dev, pipe);
//    int             epnum = usb_pipeendpoint(pipe);

    int             done;
    int             ret;
//    int             toggle;
    int             retry;
    char            *buffer=NULL;
    dwc_otg_core_if_t *core_if = dwc_otg_dev.core_if;

    DBG("--- BULK -----------------------------------------\n");
    DBG("dev=%ld ep=%ld buf=%x size=%d %s",
       usb_pipedevice(pipe), usb_pipeendpoint(pipe), buf, len, (dir_out?"dir_out":"dir_in"));
    if(len == 31) 
		DBG("scsi cmd from CBW: 0x%X\n",*(((unsigned char*)buf)+15));

    done = 0;
    if(dir_out) {
        memcpy(core_if->temp_buffer, buf, len);
    }
    buffer = core_if->temp_buffer;
    retry = 5;
    while (done < len) {
RETRY: 
       dwc_udelay(100);
	
        ret = dwc_otg_submit_job(dev, pipe,
                                 !dir_out,
                                 (__u8 *) buffer + done, max > len - done ? len - done : max, 0);
        if (ret < 0 ) {
            if(retry--){
                DBG("error on bulk message, retry: %d",retry);
                goto RETRY;
            }else{ 
                is_insert = 0;
                ERR("error on bulk message (ret = %d)", ret);
                return -1;
            }
        }       

        done += ret;

        //usb_dotoggle(dev, epnum, !dir_out);
        
        if (!dir_out && ret < max)      /* short packet */
            break;
        
    }
    DBG("return size = %d\n", done);
    if(!dir_out) {
        memcpy(buf, buffer, len);
    }
    dev->act_len = done;
    dev->status = 0;

    return 0;
}
/**
 * Flush a Tx FIFO.
 *
 * @param _core_if Programming view of DWC_otg controller.
 * @param _num Tx FIFO to flush.
 */
extern void
dwc_otg_flush_tx_fifo(dwc_otg_core_if_t * _core_if, const int _num)
{
    dwc_otg_core_global_regs_t *global_regs = _core_if->core_global_regs;
    grstctl_t       greset = { 0 };
    int             count = 0;

    DWC_DEBUGPL((DBG_CIL | DBG_PCDV), "Flush Tx FIFO %d\n", _num);

    greset.b.txfflsh = 1;
    greset.b.txfnum = _num;
    dwc_write_reg32(&global_regs->grstctl, greset.d32);

    do {
        greset.d32 = dwc_read_reg32(&global_regs->grstctl);
        if (++count > 10000) {
            WARN("%s() HANG! GRSTCTL=%0x GNPTXSTS=0x%08x\n",
                 "dwc_otg_flush_tx_fifo", greset.d32, dwc_read_reg32(&global_regs->gnptxsts));
            break;
        }

    } while (greset.b.txfflsh == 1);
    /*
     * Wait for 3 PHY Clocks
     */
    wait_ms(1);
}

/**
 * Flush Rx FIFO.
 *
 * @param _core_if Programming view of DWC_otg controller.
 */
extern void
dwc_otg_flush_rx_fifo(dwc_otg_core_if_t * _core_if)
{
    dwc_otg_core_global_regs_t *global_regs = _core_if->core_global_regs;
    volatile grstctl_t greset = { 0 };
    int             count = 0;

    DWC_DEBUGPL((DBG_CIL | DBG_PCDV), "%s\n", "dwc_otg_flush_rx_fifo");
    /*
     * 
     */
    greset.b.rxfflsh = 1;
    dwc_write_reg32(&global_regs->grstctl, greset.d32);

    do {
        greset.d32 = dwc_read_reg32(&global_regs->grstctl);
        if (++count > 10000) {
            WARN("%s() HANG! GRSTCTL=%0x\n", "dwc_otg_flush_rx_fifo", greset.d32);
            break;
        }
    } while (greset.b.rxfflsh == 1);
    /*
     * Wait for 3 PHY Clocks
     */
    wait_ms(1);
}
static void
dwc_otg_set_vbus_power(dwc_otg_core_if_t * _core_if, int is_power_on)
{
	if( _core_if->set_vbus_power)
		 _core_if->set_vbus_power(is_power_on); 
}
/**
 * This function Reads HPRT0 in preparation to modify.  It keeps the
 * WC bits 0 so that if they are read as 1, they won't clear when you
 * write it back 
 */
static inline   uint32_t
dwc_otg_read_hprt0(dwc_otg_core_if_t * _core_if)
{
    hprt0_data_t    hprt0;
    hprt0.d32 = dwc_read_reg32(_core_if->host_if->hprt0);
    hprt0.b.prtena = 0;
    hprt0.b.prtconndet = 0;
    hprt0.b.prtenchng = 0;
    hprt0.b.prtovrcurrchng = 0;
    return hprt0.d32;
}
/** 
 * This function enables the Host mode interrupts.
 *
 * @param _core_if Programming view of DWC_otg controller
 */
void
dwc_otg_enable_host_interrupts(dwc_otg_core_if_t * _core_if)
{
    dwc_otg_core_global_regs_t *global_regs = _core_if->core_global_regs;
    gintmsk_data_t  intr_mask = { 0 };

    DWC_DEBUGPL(DBG_CIL, "%s()\n", "dwc_otg_enable_host_interrupts");

    /*
     * Disable all interrupts. 
     */
    dwc_write_reg32(&global_regs->gintmsk, 0);

    /*
     * Clear any pending interrupts. 
     */
    dwc_write_reg32(&global_regs->gintsts, 0xFFFFFFFF);

    /*
     * Enable the common interrupts 
     */
    dwc_otg_enable_common_interrupts(_core_if);

    /*
     * Enable host mode interrupts without disturbing common
     * interrupts.
     */
#ifndef NO_HOST_SOF
    intr_mask.b.sofintr = 1;
#endif
    intr_mask.b.portintr = 1;//6.1.1
    intr_mask.b.hcintr = 1;

    dwc_modify_reg32(&global_regs->gintmsk, intr_mask.d32, intr_mask.d32);
}

/**
 * This function initializes the DWC_otg controller registers for
 * host mode.
 *
 * This function flushes the Tx and Rx FIFOs and it flushes any entries in the
 * request queues. Host channels are reset to ensure that they are ready for
 * performing transfers.
 *
 * @param _core_if Programming view of DWC_otg controller
 *
 */
void
dwc_otg_core_host_init(dwc_otg_core_if_t * _core_if)
{
    dwc_otg_core_global_regs_t *global_regs = _core_if->core_global_regs;
    dwc_otg_host_if_t *host_if = _core_if->host_if;
    dwc_otg_core_params_t *params = _core_if->core_params;
    hprt0_data_t    hprt0 = { 0 };
    fifosize_data_t nptxfifosize;
    fifosize_data_t ptxfifosize;
    int             i;
    hcchar_data_t   hcchar;
    hcfg_data_t     hcfg;
    dwc_otg_hc_regs_t *hc_regs;
    int             num_channels;
    gotgctl_data_t  gotgctl = { 0 };

    DWC_DEBUGPL(DBG_CILV, "%s(%p)\n", "dwc_otg_core_host_init", _core_if);

    /*
     * Restart the Phy Clock 
     */
    dwc_write_reg32(_core_if->pcgcctl, 0);

    /*
     * Initialize Host Configuration Register 
     */
    init_fslspclksel(_core_if);
    if (_core_if->core_params->speed == DWC_SPEED_PARAM_FULL) {
        hcfg.d32 = dwc_read_reg32(&host_if->host_global_regs->hcfg);
        hcfg.b.fslssupp = 1;
        dwc_write_reg32(&host_if->host_global_regs->hcfg, hcfg.d32);
    }

    /*
     * Configure data FIFO sizes 6.1.1.11----6.1.1.13
     */
    if (_core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) {
        DWC_DEBUGPL(DBG_CIL, "Total FIFO Size=%d\n", _core_if->total_fifo_size);
        DWC_DEBUGPL(DBG_CIL, "Rx FIFO Size=%d\n", params->host_rx_fifo_size);
        DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO Size=%d\n", params->host_nperio_tx_fifo_size);
        DWC_DEBUGPL(DBG_CIL, "P Tx FIFO Size=%d\n", params->host_perio_tx_fifo_size);

        /*
         * Rx FIFO 
         */
        DWC_DEBUGPL(DBG_CIL, "initial grxfsiz=%08x\n", dwc_read_reg32(&global_regs->grxfsiz));
        dwc_write_reg32(&global_regs->grxfsiz, params->host_rx_fifo_size);
        DWC_DEBUGPL(DBG_CIL, "new grxfsiz=%08x\n", dwc_read_reg32(&global_regs->grxfsiz));

        /*
         * Non-periodic Tx FIFO 
         */
        DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n", dwc_read_reg32(&global_regs->gnptxfsiz));
        nptxfifosize.b.depth = params->host_nperio_tx_fifo_size;
        nptxfifosize.b.startaddr = params->host_rx_fifo_size;
        dwc_write_reg32(&global_regs->gnptxfsiz, nptxfifosize.d32);
        DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n", dwc_read_reg32(&global_regs->gnptxfsiz));

        /*
         * Periodic Tx FIFO 
         */
        DWC_DEBUGPL(DBG_CIL, "initial hptxfsiz=%08x\n", dwc_read_reg32(&global_regs->hptxfsiz));
        ptxfifosize.b.depth = params->host_perio_tx_fifo_size;
        ptxfifosize.b.startaddr = nptxfifosize.b.startaddr + nptxfifosize.b.depth;
        dwc_write_reg32(&global_regs->hptxfsiz, ptxfifosize.d32);
        DWC_DEBUGPL(DBG_CIL, "new hptxfsiz=%08x\n", dwc_read_reg32(&global_regs->hptxfsiz));
    }

    /*
     * Clear Host Set HNP Enable in the OTG Control Register 
     */
    gotgctl.b.hstsethnpen = 1;
    dwc_modify_reg32(&global_regs->gotgctl, gotgctl.d32, 0);

    /*
     * Make sure the FIFOs are flushed. 
     */
    dwc_otg_flush_tx_fifo(_core_if, 0x10 /* all Tx FIFOs */ );
    dwc_otg_flush_rx_fifo(_core_if);

    /*
     * Flush out any leftover queued requests. 
     */
    num_channels = _core_if->core_params->host_channels;
    for (i = 0; i < num_channels; i++) {
        hc_regs = _core_if->host_if->hc_regs[i];
        hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar);
        hcchar.b.chen = 0;
        hcchar.b.chdis = 1;
        hcchar.b.epdir = 0;
        dwc_write_reg32(&hc_regs->hcchar, hcchar.d32);
    }

    /*
     * Halt all channels to put them into a known state. 
     */
    for (i = 0; i < num_channels; i++) {
        int             count = 0;
        hc_regs = _core_if->host_if->hc_regs[i];
        hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar);
        hcchar.b.chen = 1;
        hcchar.b.chdis = 1;
        hcchar.b.epdir = 0;
        dwc_write_reg32(&hc_regs->hcchar, hcchar.d32);
        DWC_DEBUGPL(DBG_HCDV, "%s: Halt channel %d\n", "dwc_otg_core_host_init", i);
        do {
            hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar);
            if (++count > 1000) {
                ERR("%s: Unable to clear halt on channel %d\n", "dwc_otg_core_host_init", i);
                break;
            }
        } while (hcchar.b.chen);
    }

    /*
     * Turn on the vbus power. 
     */
    DBG("Init: Port Power? op_state=%d\n", _core_if->op_state);
    if (_core_if->op_state == A_HOST) {
        hprt0.d32 = dwc_otg_read_hprt0(_core_if);
        DBG("Init: Power Port (%d)\n", hprt0.b.prtpwr);
        if (hprt0.b.prtpwr == 0) {
            hprt0.b.prtpwr = 1;//6.1.1.3
            dwc_write_reg32(host_if->hprt0, hprt0.d32);
            /*
             * pull gpio to switch power 
             */
            dwc_otg_set_vbus_power(_core_if, 1);
        }
    } else {
        dwc_otg_set_vbus_power(_core_if, 0);    // Power off VBus
    }

    dwc_otg_enable_host_interrupts(_core_if);
}

static int      dwc_otg_hcd_enable = 0;

/* wait at least 10ms for the reset process to complete 6.1.1.6 */
static void
dwc_otg_reset_port(dwc_otg_core_if_t * _core_if)
{
    hprt0_data_t    hprt0;
    hprt0.d32 = dwc_otg_read_hprt0(_core_if);
    hprt0.b.prtrst = 1;
    dwc_write_reg32(_core_if->host_if->hprt0, hprt0.d32);

    wait_ms(60);

    hprt0.b.prtrst = 0;
    dwc_write_reg32(_core_if->host_if->hprt0, hprt0.d32);
    wait_ms(20);

}
static int
dwc_otg_port_init(dwc_otg_core_if_t * _core_if)
{
		int retry = 10;
    hprt0_data_t    hprt0,hprt0_modify;
    hprt0.d32 = 0;
/*
    hprt0.d32 = dwc_otg_read_hprt0(_core_if);

    hprt0_data_t    hprt0_modify;

    hprt0_modify.b.prtena = 0;
    hprt0_modify.b.prtconndet = 0;
    hprt0_modify.b.prtenchng = 0;
    hprt0_modify.b.prtovrcurrchng = 0;

    wait_ms(30);
*/
next:
    hprt0.d32 = dwc_read_reg32(_core_if->host_if->hprt0);
    DBG("%s port data is 0x%X", __func__, hprt0.d32);

    hprt0_modify.d32 = dwc_read_reg32(_core_if->host_if->hprt0);

    if (hprt0.b.prtconndet) {//wait prtconndet interrupt
        /*
         * clear detect intr
         */
        wait_ms(30);
        dwc_write_reg32(_core_if->host_if->hprt0, hprt0_modify.d32);
        wait_ms(30);
        /*
         * reset port  6.1.1.6
         */
        dwc_otg_reset_port(_core_if);
        hprt0_modify.b.prtconndet = 1;

    }else{
    	wait_ms(100);
    	if(retry--)
    		goto next;
    	INFO("No USB device found !");
    	return 0;
    }
    /*
     * Determine if the connected device is a high/full/low speed device 
     */
    hprt0.d32 = dwc_read_reg32(_core_if->host_if->hprt0);

    if (hprt0.b.prtenchng) {//6.1.1.8
        hprt0.d32 = dwc_read_reg32(_core_if->host_if->hprt0);

        hprt0_modify.b.prtena = 0;
        hprt0_modify.b.prtconndet = 0;
        hprt0_modify.b.prtenchng = 0;
        hprt0_modify.b.prtovrcurrchng = 0;
        hprt0_modify.b.prtenchng = 1;
        dwc_write_reg32(_core_if->host_if->hprt0, hprt0_modify.d32);

    }
  
//    _core_if->host_if->do_ping = 1; // init value
    _core_if->host_if->do_ping = 0; // init value

    if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_LOW_SPEED) {//6.1.1.9
        INFO("Lowspeed device found !\n");
    } else if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_FULL_SPEED) {
        INFO("Fullspeed device found !\n");
    } else if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) {
        INFO("Highspeed device found !\n");
    }

		return 1;
}
/*
 * --- Init functions ------------------------------------------------------ 
 */
int
usb_lowlevel_init(void)
{
    dwc_otg_device_t *dwc_otg_device = &dwc_otg_dev;
    int             retval = 0;
    int32_t         snpsid;
    int             i;
    amlogic_usb_config_t * usb_config;
    
    printf("dwc_usb driver version: %s\n",DWC_DRIVER_VERSION);
    
    usb_config = board_usb_start();

    if(!usb_config || !usb_config->base_addr){
    	ERR("Bad usb config or base addr! Need call board_usb_init() in board init\n");
    	retval = -1;
      goto fail;
		}

    memset(dwc_otg_device, 0, sizeof(dwc_otg_device_t));

		ERR("base addr: 0x%x",usb_config->base_addr);
    dwc_otg_device->base = (void *)usb_config->base_addr;

    snpsid = dwc_read_reg32((uint32_t *) ((uint8_t *) dwc_otg_device->base + 0x40));

    if ((snpsid & 0xFFFFF000) != 0x4F542000) {
        ERR("Bad value for SNPSID: 0x%08x\n", snpsid);
        retval = -1;
        goto fail;
    }
    //dwc_otg_module_params_host.dma_enable = 1;

    dwc_otg_device->core_if = dwc_otg_cil_init(dwc_otg_device->base, &dwc_otg_module_params_host);
    if (dwc_otg_device->core_if == 0) {
        ERR("CIL initialization failed!\n");
        retval = -1;
        goto fail;
    }
    dwc_otg_device->core_if->set_vbus_power = usb_config->set_vbus_power;
    
    /*
     * Disable the global interrupt until all the interrupt
     * handlers are installed.
     */
    dwc_otg_disable_global_interrupts(dwc_otg_device->core_if);
    /*
     * Initialize the DWC_otg core.
     */
    if(dwc_otg_core_init(dwc_otg_device->core_if))
	goto fail;
    for (i = 0; i < 2; i++) {
        dwc_otg_hc_cleanup(dwc_otg_device->core_if, i);
    }

    dwc_otg_core_host_init(dwc_otg_device->core_if);

    /*
     * Enable the global interrupt after all the interrupt
     * handlers are installed.
     */
    dwc_otg_enable_global_interrupts(dwc_otg_device->core_if);

    if(!dwc_otg_port_init(dwc_otg_device->core_if))/* host initialization 6.1.1.3----6.1.1.9*/
				goto fail;
				
    dwc_otg_hcd_enable = 1;
    dwc_otg_device->disabled = 0;
    return 0;
  fail:
    return -1;
}

int
usb_lowlevel_stop(void)
{
    dwc_otg_core_if_t *core_if = dwc_otg_dev.core_if;

    DBG("%s\n", __func__);
    if(!dwc_otg_hcd_enable)
        return 0;
#if 0    
    if(!core_if)
        return 0;    
#endif

    dwc_otg_hcd_stop(core_if);
    board_usb_stop();
#if 0
    if(core_if->temp_buffer) {
        kfree(core_if->temp_buffer);
    }

    if(core_if->host_if) {
        kfree(core_if->host_if);
    }
    
    if(core_if) {
        kfree(core_if);
    }        
    dwc_otg_dev.core_if = 0;
#endif    

    return 0;
}
