blob: aae619c3c3dbf08aaf6960b40bf5283fefea9fd0 [file] [log] [blame]
/* dwc controller pcd drivers */
/*
* (C) Copyright 2010 Amlogic, Inc
*
* Victor Wan, victor.wan@amlogic.com,
* 2010-03-24 @ Shanghai
*
*/
#include "../v2_burning_i.h"
#include "platform.h"
#include "usb_ch9.h"
#include "dwc_pcd.h"
#include "dwc_pcd_irq.h"
pcd_struct_t this_pcd[NUM_EP];//FIXME:This PCD should point to different or cause bug!!
dwc_ep_t g_dwc_eps[NUM_EP];
int dwc_core_init(void)
{
gotgctl_data_t gctrldata;
int32_t snpsid;
memset(this_pcd, 0, sizeof(this_pcd));
DBG("DWC_REG_GSNPSID 0x%x\n", DWC_REG_GSNPSID+DWC_REG_BASE);
snpsid = dwc_read_reg32(DWC_REG_GSNPSID);
if ( !( 0x4F542000 == (snpsid & 0xFFFFF000) || 0x4F543000 == (snpsid & 0xFFFFF000) ) ) {
printf("%s,Bad value for SNPSID: 0x%08x\n", __func__, snpsid);
return -1;
}
#if 1
/*GOTGCTL*/
gctrldata.d32 = dwc_read_reg32(DWC_REG_GOTGCTL);//Can this GOTGCTL read before dwc_otg_core_init() ??
if (!gctrldata.b.asesvld || !gctrldata.b.bsesvld) {
printf("GOTGCTL=0x%08x, asesvld=%x, bsesvld=%x\n",
gctrldata.d32, gctrldata.b.asesvld, gctrldata.b.bsesvld);
return __LINE__;
}
#endif//
DBG("dwc core init is ok!\n");// show printf is ok.
/*
* Disable the global interrupt until all the interrupt
* handlers are installed.
*/
gahbcfg_data_t ahbcfg = {.d32 = 0 };
ahbcfg.b.glblintrmsk = 1; /* Enable interrupts bit */
dwc_modify_reg32(DWC_REG_GAHBCFG, ahbcfg.d32, 0);
/*
* Initialize the DWC_otg core.
*/
dwc_otg_core_init();
dwc_modify_reg32(DWC_REG_DCTL,0,2);// Disconnect data line
dwc_otg_pcd_init();
dwc_modify_reg32(DWC_REG_DCTL,2,0);// Connect data line
/*
* Enable the global interrupt after all the interrupt
* handlers are installed.
*/
dwc_otg_enable_global_interrupts();
return 0;
}
int dwc_otg_irq(void)
{
int ret1,ret2;
ret1 = dwc_common_irq();
ret2 = dwc_pcd_irq();
return (ret1+ret2);
}
void dwc_otg_pullup(int is_on)
{
if (is_on)
dwc_modify_reg32(DWC_REG_DCTL,2,0);// connect data line
else dwc_modify_reg32(DWC_REG_DCTL,0,2);// disconnect data line
}
static void dwc_otg_core_init() //Elvis Fool, add 'static'
{
gahbcfg_data_t ahbcfg = {.d32 = 0 };
gusbcfg_data_t usbcfg = {.d32 = 0 };
// gi2cctl_data_t i2cctl = {.d32 = 0 };
// hcfg_data_t hcfg;
#ifndef USE_FULL_SPEED
usbcfg.d32 = dwc_read_reg32(DWC_REG_GUSBCFG);
usbcfg.b.ulpi_ext_vbus_drv = 0;
usbcfg.b.term_sel_dl_pulse = 0;
dwc_write_reg32(DWC_REG_GUSBCFG,usbcfg.d32);
#endif
/*
* Reset the Controller
*/
dwc_otg_core_reset();
usbcfg.d32 = dwc_read_reg32(DWC_REG_GUSBCFG);
#if (defined CONFIG_USB_DEVICE_V2)
usbcfg.b.usbtrdtim = 9;
#else
usbcfg.b.usbtrdtim = 5;
#endif
usbcfg.b.srpcap = 0;
usbcfg.b.hnpcap = 0;
#ifdef USE_FULL_SPEED
dcfg_data_t dcfg;
printf("Full Speed\n");
usbcfg.b.physel = 1; // Work at full speed
dwc_write_reg32(DWC_REG_GUSBCFG, usbcfg.d32);
dwc_otg_core_reset();
/* for HOST
hcfg.d32 = dwc_read_reg32(DWC_REG_ hcfg);
hcfg.b.fslspclksel = val;
dwc_write_reg32(&_core_if->host_if->host_global_regs->hcfg, hcfg.d32);
*/
// for Device
dcfg.d32 = dwc_read_reg32(DWC_REG_DCFG);
dcfg.b.devspd = 1;//Hi speed phy run at Full speed
dwc_write_reg32(DWC_REG_DCFG, dcfg.d32);
#else
DBG("High Speed\n");
usbcfg.b.ulpi_utmi_sel = 1;
usbcfg.b.phyif = 0; // 16 bit
usbcfg.b.ddrsel = 0;
dwc_write_reg32(DWC_REG_GUSBCFG, usbcfg.d32);
dwc_otg_core_reset();
#endif
ahbcfg.b.dmaenable = 0;
dwc_write_reg32(DWC_REG_GAHBCFG, ahbcfg.d32);
/*
* Enable common interrupts
*/
dwc_otg_enable_common_interrupts();
/*
* Do device or host intialization based on mode during PCD and HCD
* initialization
*/
if (dwc_read_reg32(DWC_REG_GINTSTS) & 0x1) {
DBG("Host Mode\n");
return ;
} else {
DBG("Device Mode\n");
dwc_otg_core_dev_init();
}
}
/**
* This function initialized the PCD portion of the driver.
*
*/
static int dwc_otg_pcd_init()
{
return 0;
}
/**
* Do core a soft reset of the core. Be careful with this because it
* resets all the internal state machines of the core.
*/
static void dwc_otg_core_reset(void) //Elvis Fool, add 'static'
{
grstctl_t greset = {.d32 = 0 };
int count = 0;
/*
* Wait for AHB master IDLE state.
*/
do {
udelay(10);
greset.d32 = dwc_read_reg32(DWC_REG_GRSTCTL);
if (++count > 100000) {
//DBG("%s() HANG! AHB Idle GRSTCTL=%0x\n", dwc_otg_core_reset, greset.d32);
return;
}
}
while (greset.b.ahbidle == 0);
/*
* Core Soft Reset
*/
count = 0;
greset.b.csftrst = 1;
dwc_write_reg32(DWC_REG_GRSTCTL, greset.d32);
do {
greset.d32 = dwc_read_reg32(DWC_REG_GRSTCTL);
if (++count > 1000000) {
//DBG("%s() HANG! Soft Reset GRSTCTL=%0x\n", dwc_otg_core_reset, greset.d32);
break;
}
}
while (greset.b.csftrst == 1);
/*
* Wait for 3 PHY Clocks
*/
wait_ms(10);
}
static void dwc_otg_enable_common_interrupts()
{
gintmsk_data_t intr_mask = { 0};
/* Clear any pending OTG Interrupts */
dwc_write_reg32(DWC_REG_GOTGINT, 0xFFFFFFFF);
/* Clear any pending interrupts */
dwc_write_reg32(DWC_REG_GINTSTS, 0xFFFFFFFF);
/*
* Enable the interrupts in the GINTMSK.
*/
intr_mask.b.modemismatch = 1;
intr_mask.b.otgintr = 1;
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;
intr_mask.b.sofintr = 1;
dwc_write_reg32(DWC_REG_GINTMSK, intr_mask.d32);
}
/**
* This function enables the Device mode interrupts.
*
* @param _core_if Programming view of DWC_otg controller
*/
static void dwc_otg_enable_device_interrupts()
{
gintmsk_data_t intr_mask = { 0};
/* Disable all interrupts. */
dwc_write_reg32( DWC_REG_GINTMSK, 0);
/* Clear any pending interrupts */
dwc_write_reg32( DWC_REG_GINTSTS, 0xFFFFFFFF);
/* Enable the common interrupts */
dwc_otg_enable_common_interrupts( );
/* Enable interrupts */
intr_mask.b.usbreset = 1;
intr_mask.b.enumdone = 1;
intr_mask.b.epmismatch = 1;
intr_mask.b.inepintr = 1;
intr_mask.b.outepintr = 1;
intr_mask.b.erlysuspend = 1;
intr_mask.b.sofintr = 1;
dwc_modify_reg32( DWC_REG_GINTMSK, intr_mask.d32, intr_mask.d32);
}
/**
* This function enables the controller's Global Interrupt in the AHB Config
* register.
*
* @param[in] _core_if Programming view of DWC_otg controller.
*/
static void dwc_otg_enable_global_interrupts( )
{
gahbcfg_data_t ahbcfg = { 0};
ahbcfg.b.glblintrmsk = 1; /* Enable interrupts */
dwc_modify_reg32(DWC_REG_GAHBCFG, 0, ahbcfg.d32);
}
/**
* This function initializes the DWC_otg controller registers for
* device mode.
*
* @param _core_if Programming view of DWC_otg controller
*
*/
static void dwc_otg_core_dev_init(void)
{
dcfg_data_t dcfg = { 0};
grstctl_t resetctl = { 0 };
int i;
fifosize_data_t nptxfifosize;
/* Restart the Phy Clock */
dwc_write_reg32(DWC_REG_PCGCCTL, 0);
/* Device configuration register */
dcfg.d32 = dwc_read_reg32(DWC_REG_DCFG);
#ifdef USE_FULL_SPEED
dcfg.b.devspd = 1;//Hi speed phy run at Full speed
#else
dcfg.b.devspd = 0;
#endif
dcfg.b.perfrint = DWC_DCFG_FRAME_INTERVAL_80;
dwc_write_reg32(DWC_REG_DCFG, dcfg.d32);
/* Configure data FIFO sizes */
/* Rx FIFO */
dwc_write_reg32(DWC_REG_GRXFSIZ, 256 );
//DBG("new grxfsiz=%08x\n",dwc_read_reg32(DWC_REG_GRXFSIZ));
/* Non-periodic Tx FIFO */
nptxfifosize.b.depth = 256;
nptxfifosize.b.startaddr = 256;
dwc_write_reg32(DWC_REG_GNPTXFSIZ, nptxfifosize.d32 );
//DBG("new gnptxfsiz=%08x\n",dwc_read_reg32(DWC_REG_GNPTXFSIZ));
/* Flush the FIFOs */
dwc_otg_flush_tx_fifo( 0x10); /* all Tx FIFOs */
dwc_otg_flush_rx_fifo();
/* Flush the Learning Queue. */
resetctl.b.intknqflsh = 1;
dwc_write_reg32( DWC_REG_GRSTCTL, resetctl.d32);
/* Clear all pending Device Interrupts */
dwc_write_reg32( DWC_REG_DIEPMSK, 0 );
dwc_write_reg32( DWC_REG_DOEPMSK, 0 );
dwc_write_reg32( DWC_REG_DAINT, 0xFFFFFFFF );
dwc_write_reg32( DWC_REG_DAINTMSK, 0 );
for (i=0; i < NUM_EP; i++) {
depctl_data_t depctl;
depctl.d32 = dwc_read_reg32(DWC_REG_IN_EP_REG(i));
if (depctl.b.epena) {
depctl.d32 = 0;
depctl.b.epdis = 1;
depctl.b.snak = 1;
} else {
depctl.d32 = 0;
}
dwc_write_reg32( DWC_REG_IN_EP_REG(i),depctl.d32);
depctl.d32 = dwc_read_reg32(DWC_REG_OUT_EP_REG(i));
if (depctl.b.epena) {
depctl.d32 = 0;
depctl.b.epdis = 1;
depctl.b.snak = 1;
} else {
depctl.d32 = 0;
}
dwc_write_reg32( DWC_REG_OUT_EP_REG(i), depctl.d32);
/* Device IN/OUT Endpoint Transfer Size */
dwc_write_reg32(DWC_REG_IN_EP_TSIZE(i), 0);
dwc_write_reg32(DWC_REG_OUT_EP_TSIZE(i), 0);
/* Device IN/OUT Endpoint DMA Address Register */
dwc_write_reg32( DWC_REG_IN_EP_DMA(i), 0);
dwc_write_reg32( DWC_REG_OUT_EP_DMA(i), 0);
/* Device IN/OUT Endpoint Interrupt Register */
//dwc_write_reg32( DWC_REG_IN_EP_DMA(i), 0xFF);
//dwc_write_reg32( DWC_REG_OUT_EP_INTR(i), 0xFF);
}
//d wc_otg_set_vbus_power(_core_if, 0); //Power off VBus
dwc_otg_enable_device_interrupts();
DBG("init gintmsk: 0x%x\n",dwc_read_reg32(DWC_REG_GINTMSK));
}
/**
* Flush a Tx FIFO.
*
* @param _core_if Programming view of DWC_otg controller.
* @param _num Tx FIFO to flush.
*/
static void dwc_otg_flush_tx_fifo( const int _num ) //Elvis Fool, add 'static'
{
grstctl_t greset = { 0};
int count = 0;
DBG("dwc_otg_flush_tx_fifo: %d\n",_num);
greset.b.txfflsh = 1;
greset.b.txfnum = _num;
dwc_write_reg32(DWC_REG_GRSTCTL, greset.d32 );
do {
greset.d32 = dwc_read_reg32( DWC_REG_GRSTCTL);
if (++count > 10000) {
// ERR("%s() HANG! GRSTCTL=%0x GNPTXSTS=0x%08x\n",
// "dwc_otg_flush_tx_fifo", greset.d32,
// dwc_read_reg32( DWC_REG_GNPTXSTS));
break;
}
} while (greset.b.txfflsh == 1);
/* Wait for 3 PHY Clocks*/
udelay(1);
}
/**
* Flush Rx FIFO.
*
* @param _core_if Programming view of DWC_otg controller.
*/
static void dwc_otg_flush_rx_fifo( )
{
grstctl_t greset = { 0};
int count = 0;
DBG("dwc_otg_flush_rx_fifo\n");
greset.b.rxfflsh = 1;
dwc_write_reg32( DWC_REG_GRSTCTL, greset.d32 );
do {
greset.d32 = dwc_read_reg32( DWC_REG_GRSTCTL);
if (++count > 10000) {
//ERR("%s() HANG! GRSTCTL=%0x\n", "dwc_otg_flush_rx_fifo",
// greset.d32);
break;
}
} while (greset.b.rxfflsh == 1);
/* Wait for 3 PHY Clocks*/
udelay(1);
}
/**
* This function does the setup for a data transfer for EP0 and starts
* the transfer. For an IN transfer, the packets will be loaded into
* the appropriate Tx FIFO in the ISR. For OUT transfers, the packets are
* unloaded from the Rx FIFO in the ISR.
*
* @param _core_if Programming view of DWC_otg controller.
* @param _ep The EP0 data.
*/
void dwc_otg_ep_start_transfer(dwc_ep_t *_ep)
{
depctl_data_t depctl;
deptsiz_data_t deptsiz;
int epctl_reg,epctltsize_reg;
int ep_num = _ep->num;
int is_in = _ep->is_in;
int ep_mps = _ep->maxpacket;
//DBG("dwc_otg_ep_start_transfer: xfer_len:%d\n",_ep->xfer_len);
_ep->total_len = _ep->xfer_len;
if (is_in) {
epctl_reg = DWC_REG_IN_EP_REG(ep_num);
epctltsize_reg = DWC_REG_IN_EP_TSIZE(ep_num);
}else{
epctl_reg = DWC_REG_OUT_EP_REG(ep_num);
epctltsize_reg = DWC_REG_OUT_EP_TSIZE(ep_num);
}
depctl.d32 = dwc_read_reg32(epctl_reg);
deptsiz.d32 = dwc_read_reg32(epctltsize_reg);
/* Zero Length Packet? */
if (_ep->xfer_len == 0) {
deptsiz.b.xfersize = is_in?0:ep_mps;
deptsiz.b.pktcnt = 1;
}
else {
deptsiz.b.pktcnt = (_ep->xfer_len + (ep_mps - 1)) /ep_mps;
if (is_in && _ep->xfer_len < ep_mps)
deptsiz.b.xfersize = _ep->xfer_len;
else
deptsiz.b.xfersize = deptsiz.b.pktcnt * ep_mps;
/*deptsiz.b.xfersize = _ep->xfer_len - _ep->xfer_count;////////victor fixed*/
}
/* Fill size and count */
dwc_write_reg32(epctltsize_reg,deptsiz.d32);
/* EP enable */
depctl.b.cnak = 1;
depctl.b.epena = 1;
dwc_write_reg32 (epctl_reg, depctl.d32);
/* IN endpoint */
if (is_in) {
gintmsk_data_t intr_mask = {0};
//gnptxsts_data_t tx_status = { 0};
//tx_status.d32 = dwc_read_reg32(DWC_REG_GNPTXSTS);
//if (tx_status.b.nptxqspcavail == 0){
// return;
//}
/**
* Enable the Non-Periodic Tx FIFO empty interrupt, the
* data will be written into the fifo by the ISR.
*/
/* First clear it from GINTSTS */
intr_mask.b.nptxfempty = 1;
dwc_modify_reg32( DWC_REG_GINTSTS,intr_mask.d32, 0);
dwc_modify_reg32( DWC_REG_GINTMSK,intr_mask.d32, intr_mask.d32);
}
}
int dwc_otg_ep_req_start(pcd_struct_t *pcd,int ep_num)
{
dwc_ep_t *ep = &g_dwc_eps[ep_num];
ep->num = ep_num;
//DBG("dwc_otg_ep_req_start: ep%d\n",ep_num);
/* EP0 Transfer? */
if (ep_num == 0)
{
//DBG("EP0 State: %d\n",pcd->ep0state);
switch (pcd->ep0state)
{
case EP0_IN_DATA_PHASE:
break;
case EP0_OUT_DATA_PHASE:
if (pcd->request_config) {
/* Work around for SetConfig cmd */
/* Complete STATUS PHASE */
ep->is_in = 1;
pcd->ep0state = EP0_STATUS;
}
else if(pcd->length == 0)
{
/* Work around for MSC Reset cmd */
/* Complete STATUS PHASE */
ep->is_in = 1;
pcd->ep0state = EP0_STATUS;
}
break;
default:
return -1;
}
ep->start_xfer_buff = (uint8_t*)pcd->buf;
ep->xfer_buff = (uint8_t*)pcd->buf;
ep->xfer_len = pcd->length;
ep->xfer_count = 0;
ep->sent_zlp = 0;
ep->total_len = ep->xfer_len;
ep->maxpacket = 64;
dwc_otg_ep_start_transfer( ep );
}
else {
/* Setup and start the Transfer for Bulk */
ep->start_xfer_buff = (uint8_t*)pcd->bulk_buf + pcd->bulk_xfer_len;
ep->xfer_buff = (uint8_t*)pcd->bulk_buf + pcd->bulk_xfer_len;
ep->xfer_len = pcd->bulk_len;
/*ep->xfer_len = MIN(pcd->bulk_data_len , pcd->bulk_len);////Victor fixed!!*/
ep->xfer_count = 0;
ep->sent_zlp = 0;
ep->total_len = ep->xfer_len;
ep->maxpacket = BULK_EP_MPS;
ep->is_in = (ep_num == BULK_IN_EP_NUM);
//tranfer failed here after printf here
dwc_otg_bulk_ep_activate( ep );
/*printf("start_xfer_buf=0x%x, xfer_buf=0x%x, xfer_len 0x%x\n", ep->start_xfer_buff, ep->xfer_buff, ep->xfer_len);*/
dwc_otg_ep_start_transfer( ep );
}
return 0;
}
static void dwc_otg_bulk_ep_activate(dwc_ep_t *ep)
{
depctl_data_t depctl = {0};
daint_data_t daintmsk = {0};
int epctl;
int ep_num = ep->num;
if (ep->is_in) {
epctl = DWC_REG_IN_EP_REG(ep_num);
daintmsk.ep.in = 1<<BULK_IN_EP_NUM; //ep1:BULK_IN
}
else{
epctl = DWC_REG_OUT_EP_REG(ep_num);
daintmsk.ep.out = 1<<BULK_OUT_EP_NUM;//ep2:BULK_OUT
}
depctl.d32 = dwc_read_reg32(epctl);
if (!depctl.b.usbactep) {
depctl.b.mps = BULK_EP_MPS;
depctl.b.eptype = 2;//BULK_STYLE
depctl.b.setd0pid = 1;
depctl.b.txfnum = 0; //Non-Periodic TxFIFO
depctl.b.usbactep = 1;
dwc_write_reg32(epctl, depctl.d32);
}
dwc_modify_reg32(DWC_REG_DAINTMSK, 0, daintmsk.d32);
return;
}
int dwc_otg_bulk_ep_enable(int is_in)
{
depctl_data_t depctl = {0};
daint_data_t daintmsk = {0};
int epctl;
const int ep_num = is_in ? BULK_IN_EP_NUM : BULK_OUT_EP_NUM;
if (is_in) {
epctl = DWC_REG_IN_EP_REG(ep_num);
daintmsk.ep.in = 1<<BULK_IN_EP_NUM; //ep1:BULK_IN
}
else{
epctl = DWC_REG_OUT_EP_REG(ep_num);
daintmsk.ep.out = 1<<BULK_OUT_EP_NUM;//ep2:BULK_OUT
}
depctl.d32 = dwc_read_reg32(epctl);
if (!depctl.b.usbactep) {
depctl.b.mps = BULK_EP_MPS;
depctl.b.eptype = 2;//BULK_STYLE
depctl.b.setd0pid = 1;
depctl.b.txfnum = 0; //Non-Periodic TxFIFO
depctl.b.usbactep = 1;
dwc_write_reg32(epctl, depctl.d32);
}
dwc_modify_reg32(DWC_REG_DAINTMSK, 0, daintmsk.d32);
return 0;
}
void dwc_otg_power_off_phy(void)
{
#if (defined CONFIG_USB_DEVICE_V2)
gintsts_data_t gintr_status;
gintsts_data_t gintr_msk;
u32 count = 1000;
u32 sof = 0;
while (count--) {
gintr_msk.d32 = dwc_read_reg32(DWC_REG_GINTMSK);
gintr_status.d32 = dwc_read_reg32(DWC_REG_GINTSTS);
//if ((gintr_status.d32 & gintr_msk.d32)== 0)
//continue;
gintr_status.d32 = gintr_status.d32 & gintr_msk.d32;
if (gintr_status.b.sofintr) {
sof = 1;
dwc_write_reg32(DWC_REG_GINTSTS, gintr_status.d32);
break;
}
dwc_write_reg32(DWC_REG_GINTSTS, gintr_status.d32);
udelay(1);
}
if (!sof) {
ERR("sof timeout, reset usb phy tuning\n");
set_usb_phy21_tuning_update_reset();
mdelay(150);
}
return;
#else
//cause DWC_REG_GSNPSID value 0 after call this when g12
dwc_write_reg32(DWC_REG_PCGCCTL, 0xF);
#endif//#if (defined CONFIG_USB_DEVICE_V2)
}