blob: e10b0931a993ae223f7bbb5087248154e9a14259 [file] [log] [blame]
/*
* drivers/usb/host/dwc_otg_hcd.c
*
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#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(int index,enum usb_init_type init, void **controller)
{
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(BOARD_USB_MODE_HOST,index);
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;
dwc_otg_device->index = index;
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(int index)
{
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(BOARD_USB_MODE_HOST,dwc_otg_dev.index);
#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;
}