rtt-f030/bsp/x1000/drivers/usbd/x1000_dwc.c

2028 lines
53 KiB
C
Raw Normal View History

2017-11-11 13:51:56 +08:00
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <drivers/usb_device.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <x1000.h>
#include <mips_regs.h>
#include <cache.h>
#include "x1000_dwc.h"
//#define DWC_DEBUG
#ifdef DWC_DEBUG
#define DWC_DBG(fmt, args...) rt_kprintf(fmt ,##args)
#else
#define DWC_DBG(fmt, args...)
#endif
#define UdcID (('U' << 24) | ('D' << 16) | ('C' << 16) | (':' << 16))
#define IS_SLAVE_MODE 0
#define IS_INTERN_DMA 2
#define IS_EXTERN_DMA 1
const char *ep0_state_string[] =
{
"EP_SETUP",
"EP_DATA",
"EP_STATUS",
"EP_SETUP_PHASEDONE",
};
#if DWC_FORCE_SPEED_FULL
#define DEP_EP_MAXPKT(n) \
({ \
int v = 0; \
if (n) \
v = 64; \
else \
v = 64; \
v; \
})
#else
#define DEP_EP_MAXPKT(n) \
({ \
int v = 0; \
if (n) \
v = 512; \
else \
v = 64; \
v; \
})
#endif
#define MAX_PKT_CNT 1023
ALIGN(32)
//static uint32_t setup_packet[64] = {0, 0, 0, 0, 0};
static int sleep_flag = 0;
/*
* static functions
*/
static void dwc_otg_device_init(dwc_handle *dwc);
static void dwc_otg_core_reset(dwc_handle *dwc);
static void dwc_otg_core_init(dwc_handle *dwc,uint8_t dma_enable);
static void dwc_otg_phy_suspend(int suspend);
static void udelay(uint32_t x)
{
volatile uint32_t n = 1000;
while(x--)
{
for (n = 0; n < 1000; ++n);
}
}
static void mdelay(uint32_t x)
{
while(x--)
udelay(1000);
}
static int dwc_get_utmi_width(dwc_handle *dwc)
{
return (REG_GHW_CFG4 >> 14) & 0x3;
}
static void dwc_otg_select_phy_width(dwc_handle *dwc)
{
REG_GUSB_CFG &= ~USBCFG_TRDTIME_MASK;
REG_GUSB_CFG |= (1 << 3);
REG_GUSB_CFG |= USBCFG_TRDTIME_6;
REG_CPM_USBPCR1 |= (3 << 18);
}
static void dwc_otg_write_packet(dwc_handle *dwc, uint8_t epnum)
{
int i;
uint32_t dwords;
uint32_t byte_count;
dwc_ep *pep;
epnum &= DWC_EPNO_MASK;
pep = dwc->dep[DWC_EP_IN_OFS + epnum];
byte_count = pep->xfer_len - pep->xfer_count;
if (byte_count > DEP_EP_MAXPKT(epnum))
byte_count = DEP_EP_MAXPKT(epnum);
dwords = (byte_count + 3) / 4;
for (i = 0; i < dwords; i++)
{
REG_EP_FIFO(epnum) = REG32((uint32_t * )(pep->xfer_buff) + i);
}
pep->xfer_count += byte_count;
pep->xfer_buff += byte_count;
}
void dwc_read_ep_packet(dwc_handle *dwc, uint8_t epnum, uint32_t count)
{
int i;
int dwords = (count + 3) / 4;
dwc_ep *pep;
epnum &= DWC_EPNO_MASK;
pep = dwc->dep[DWC_EP_OUT_OFS + epnum];
for (i = 0; i < dwords; i++)
REG32((uint32_t *)(pep->xfer_buff + pep->xfer_count / 4) + i) = REG_EP_FIFO(epnum);
pep->xfer_count += count;
}
void dwc_write_ep_packet(dwc_handle *dwc,uint8_t epnum)
{
uint32_t xfersize, finish, insize;
uint32_t dwords;
uint32_t txstatus = REG_DIEP_TXFSTS(epnum & 0x0F);
dwc_ep *pep;
epnum &= DWC_EPNO_MASK;
pep = dwc->dep[DWC_EP_IN_OFS + epnum];
insize = pep->xfer_len;
if (pep->xfer_len > DEP_EP_MAXPKT(epnum))
xfersize = DEP_EP_MAXPKT(epnum);
else
xfersize = pep->xfer_len;
dwords = (xfersize + 3) / 4;
DWC_DBG("txstatus (%x) dwords (%x) length (%x) xfer_count (%x) \n", txstatus, dwords, pep->xfer_len, pep->xfer_count);
while ((txstatus > dwords) && (pep->xfer_len > 0) && (pep->xfer_count < pep->xfer_len) )
{
dwc_otg_write_packet(dwc, epnum);
xfersize = pep->xfer_len - pep->xfer_count;
if (xfersize > DEP_EP_MAXPKT(epnum))
xfersize = DEP_EP_MAXPKT(epnum);
dwords = (xfersize + 3) / 4;
txstatus = REG_DIEP_TXFSTS(epnum);
}
finish = pep->xfer_count;
if (insize > finish)
{
uint32_t intr = REG_DIEP_INT(epnum);
while (!(intr & DEP_TXFIFO_EMPTY))
{
intr = REG_DIEP_INT(epnum);
}
HW_SendPKT(dwc,epnum, pep->xfer_buff, insize - finish);
}
return;
}
void dwc_handle_ep_data_in_phase(dwc_handle *dwc, uint8_t epnum)
{
uint32_t pktcnt, xfersize;
uint32_t dma_addr, dma_len;
dwc_ep *pep;
DWC_DBG("%s %d\n",__func__,__LINE__);
DWC_DBG("epnum = %d\n",epnum);
epnum &= DWC_EPNO_MASK;
pep = dwc->dep[DWC_EP_IN_OFS + epnum];
xfersize = pep->xfer_len;
pktcnt = (xfersize + DEP_EP_MAXPKT(epnum) - 1) / DEP_EP_MAXPKT(epnum);
if (pktcnt > 1023)
{
DWC_DBG("WARNING...\n");
while (1) ;
}
if (epnum == 0)
{
REG_DIEP_SIZE(epnum) &= ~(0x1fffff);
REG_DIEP_SIZE(epnum) |= (pktcnt << 19) | xfersize;
}
else
{
REG_DIEP_SIZE(epnum) &= ~(0x1fffffff);
REG_DIEP_SIZE(epnum) |= (pktcnt << 19) | xfersize;
}
if (dwc->is_dma != 0)
{
dma_addr = (uint32_t)(pep->xfer_buff);
dma_len = (((pep->xfer_len + 7) >> 3) << 3);
//dump data...
DWC_DBG("IN:\n");
{
int i;
for (i = 0; i < dma_len; ++i)
{
DWC_DBG("%02x ", *(unsigned char *)(dma_addr+i));
if ((i + 1) % 16 == 0)
DWC_DBG("\n");
}
}
DWC_DBG("\n");
REG_DIEP_DMA(epnum) = PHYS(pep->xfer_buff);
REG_DIEP_CTL(epnum) |= (DEP_ENA_BIT | DEP_CLEAR_NAK);
}
else
{
REG_DIEP_CTL(epnum) |= (DEP_ENA_BIT | DEP_CLEAR_NAK);
REG_DIEP_EMPMSK |= (1 << epnum);
}
return ;
}
void dwc_handle_ep_status_in_phase(dwc_handle *dwc, uint8_t epnum)
{
dwc_ep *pep;
DWC_DBG("%s %d\n",__func__,__LINE__);
DWC_DBG("epnum = %d\n",epnum);
epnum &= DWC_EPNO_MASK;
pep = dwc->dep[DWC_EP_IN_OFS + epnum];
pep->xfer_len = 0;
pep->xfer_count = 0;
if (epnum == 0)
{
REG_DIEP_SIZE(epnum) &= ~(0x1fffff);
REG_DIEP_SIZE(epnum) |= DOEPSIZE0_PKTCNT_BIT | (pep->xfer_len); // pktcnt->1 xfersize->0
}
else
{
REG_DIEP_SIZE(epnum) &= ~(0x1FFFFFFF);
REG_DIEP_SIZE(epnum) |= DOEPSIZE0_PKTCNT_BIT | (pep->xfer_len); // pktcnt->1 xfersize->0
}
if (dwc->is_dma == IS_INTERN_DMA)
{
// pep->xfer_buff = (void *)0xFFFFFFFF;
// REG_DIEP_DMA(epnum) = PHYS(pep->xfer_buff);
REG_DIEP_DMA(epnum) = PHYS(0xFFFFFFFF);
REG_DIEP_CTL(epnum) |= DEP_ENA_BIT | DEP_CLEAR_NAK;
}
else
{
REG_DIEP_CTL(epnum) |= DEP_ENA_BIT | DEP_CLEAR_NAK;
}
return ;
}
void dwc_handle_ep_data_out_phase(dwc_handle *dwc,uint8_t epnum)
{
uint32_t dma_addr, dma_len;
uint32_t pktcnt;
dwc_ep *pep;
DWC_DBG("%s %d\n",__func__,__LINE__);
DWC_DBG("epnum = %d\n",epnum);
epnum &= DWC_EPNO_MASK;
pep = dwc->dep[DWC_EP_OUT_OFS + epnum];
if (epnum == 0)
{
if (dwc->is_dma == IS_INTERN_DMA)
{
dma_len = pep->maxpacket;
dma_addr = (uint32_t) (pep->xfer_buff);
rt_hw_dcache_flush_range(dma_addr,dma_len);
REG_DOEP_DMA(epnum) = PHYS(pep->xfer_buff);
}
REG_DOEP_SIZE(epnum) = DOEPSIZE0_SUPCNT_3 | DOEPSIZE0_PKTCNT_BIT | (pep->maxpacket);
REG_DOEP_CTL(epnum) |= DEP_ENA_BIT | DEP_CLEAR_NAK;
}
else
{
if (pep->xfer_len > 0)
{
if (pep->xfer_len > MAX_PKT_CNT * DEP_EP_MAXPKT(epnum))
pep->xfer_len = MAX_PKT_CNT * DEP_EP_MAXPKT(epnum);
pktcnt = (pep->xfer_len + DEP_EP_MAXPKT(epnum) - 1) / DEP_EP_MAXPKT(epnum);
if (pktcnt > 1023)
{
DWC_DBG("WARNING...\n");
while (1) ;
}
REG_DOEP_SIZE(epnum) &= ~(0x1fffffff);
REG_DOEP_SIZE(epnum) |= (pktcnt << 19) | (pep->xfer_len);
}
if (dwc->is_dma == IS_INTERN_DMA)
{
dma_len = (((pep->xfer_len + 7) >> 3) << 3);
dma_addr = (uint32_t)(pep->xfer_buff);
rt_hw_dcache_flush_range(dma_addr, dma_len);
REG_DOEP_DMA(epnum) = PHYS(pep->xfer_buff);
}
/* Program the DOEPCTLn Register with endpoint charateristics,
* and set the Endpoint Enable and Clear NAK bit */
REG_DOEP_CTL(epnum) |= DEP_ENA_BIT | DEP_CLEAR_NAK;
}
}
int HW_SendPKT(dwc_handle *dwc, uint8_t epnum, const uint8_t *buf, int size)
{
uint32_t dma_addr, dma_len;
dwc_ep *pep;
rt_base_t level;
DWC_DBG("HW_SendPKT addr = %02x,size = %d\n",epnum,size);
epnum &= DWC_EPNO_MASK;
pep = dwc->dep[DWC_EP_IN_OFS + epnum];
pep->xfer_len = size; /* number of bytes to transfer */
pep->xfer_count = 0; /* number of bytes transfered */
// pep->xfer_buff = (uint8_t *)buf; /* pointer to transfer buffer */
if(size > 0)
{
memcpy(pep->xfer_buff,buf,size);
rt_hw_dcache_flush_range((rt_uint32_t)pep->xfer_buff,(rt_uint32_t)size);
}
if (pep->xfer_len > MAX_PKT_CNT * DEP_EP_MAXPKT(epnum))
pep->xfer_len = MAX_PKT_CNT * DEP_EP_MAXPKT(epnum);
pep->xfer_count = 0;
switch (pep->type)
{
case DWC_OTG_EP_TYPE_CONTROL:
if (pep->xfer_len > 0)
pep->ep_state = EP_DATA;
else
pep->ep_state = EP_STATUS;
/* 2 Stage */
if (pep->ep_state == EP_STATUS && pep->xfer_len == 0) /*EP_SETUP 0 EP_DATA 1 EP_STATUS 2*/
{
DWC_DBG("%s %d ep_state = %s\n", __func__, __LINE__, ep0_state_string[pep->ep_state]);
dwc_handle_ep_status_in_phase(dwc, 0);
return 0;
}
/* 3 Stage */
if (pep->ep_state == EP_DATA)
{
/* enable in data phase */
dwc_handle_ep_data_in_phase(dwc, epnum);
}
break;
case DWC_OTG_EP_TYPE_BULK:
if (pep->ep_state == EP_IDLE || pep->ep_state == EP_TRANSFERED)
{
pep->ep_state = EP_TRANSFERING;
if (pep->xfer_len == 0)
{
dwc_handle_ep_status_in_phase(dwc, epnum);
return 0;
}
dwc_handle_ep_data_in_phase(dwc, epnum);
}
break;
}
return pep->xfer_len;
}
int HW_GetPKT(dwc_handle *dwc, uint8_t epnum, uint8_t *buf,int size)
{
int i;
dwc_ep *pep;
DWC_DBG("HW_GetPKT:%d %d\n", epnum, dwc->is_dma);
epnum &= DWC_EPNO_MASK;
pep = dwc->dep[DWC_EP_OUT_OFS + epnum];
if ((size == 0) || (size > pep->xfer_count))
size = pep->xfer_count;
if (dwc->is_dma == IS_INTERN_DMA)
{
DWC_DBG("HW_GetPKT:%x %x \n", pep->ctrl_req_addr, UNCACHED(pep->xfer_buff));
memcpy((uint8_t*) buf, (uint8_t *) UNCACHED(pep->xfer_buff), size);
}
else
{
memcpy((uint8_t*) buf, (uint8_t *) (pep->xfer_buff), size);
}
return size;
}
static void dwc_otg_flush_rx_fifo(dwc_handle *dwc)
{
;
}
static void dwc_otg_flush_tx_fifo(dwc_handle *dwc,uint8_t epnum)
{
uint32_t gintsts;
uint32_t grstctl;
uint32_t cnt;
gintsts = REG_GINT_STS;
/* Step1: Check that GINTSTS.GinNakEff=0 if this
* bit is cleared then set Dctl.SGNPInNak = 1.
* Nak effective interrupt = H indicating the core
* is not reading from fifo*/
if ((gintsts & GINTSTS_GINNAK_EFF))
{
REG_OTG_DCTL |= DCTL_SGNPINNAK;
/* Step2: wait for GINTSTS.GINNakEff=1,which indicates
* the NAK setting has taken effect to all IN endpoints */
while (!(REG_GINT_STS & GINTSTS_GINNAK_EFF))
udelay(1);
}
/* Step3: wait for ahb master idle state */
while (!(REG_GRST_CTL & RSTCTL_AHB_IDLE))
udelay(1);
/* Step4: Check that GrstCtl.TxFFlsh=0, if it is 0, then write
* the TxFIFO number you want to flush to GrstCTL.TxFNum*/
grstctl = REG_GRST_CTL;
if (!(grstctl & RSTCTL_TXFIFO_FLUSH))
{
REG_GRST_CTL |= ((epnum & 0x0F) << 6);
}
/* Step5: Set GRSTCTL.TxFFlsh=1 and wait for it to clear */
REG_GRST_CTL |= RSTCTL_TXFIFO_FLUSH;
while (REG_GRST_CTL & RSTCTL_TXFIFO_FLUSH)
{
udelay(1);
}
/* Step6: Set the DCTL.GCNPinNak bit */
REG_OTG_DCTL |= DCTL_CLR_GNPINNAK;
}
static void dwc_set_in_nak(dwc_handle *dwc, int epnum)
{
int timeout = 5000;
epnum &= DWC_EPNO_MASK;
REG_DIEP_CTL(epnum) |= DEP_SET_NAK;
do
{
udelay(1);
if (timeout < 2)
{
DWC_DBG("dwc set in nak timeout epnum %d\n", epnum);
}
} while ((!(REG_DIEP_INT(epnum) & DEP_INEP_NAKEFF)) && (--timeout > 0));
}
static void dwc_set_out_nak(dwc_handle *dwc,int epnum)
{
epnum &= DWC_EPNO_MASK;
REG_DOEP_CTL(epnum) |= DEP_SET_NAK;
}
static void dwc_disable_in_ep(dwc_handle *dwc,int epnum)
{
int timeout = 100000;
epnum &= DWC_EPNO_MASK;
if (!(REG_DIEP_CTL(epnum) & DEP_ENA_BIT))
return ;
/*step 1 : set nak*/
dwc_set_in_nak(dwc,epnum);
/*step 2: disable endpoint*/
REG_DIEP_CTL(epnum) |= DEP_DISENA_BIT;
do
{
udelay(1);
if (timeout < 2)
{
DWC_DBG("dwc disable in ep timeout epnum : %d\n", epnum);
}
} while ( (!(REG_DIEP_INT(epnum) & DEP_EPDIS_INT)) && (--timeout > 0));
REG_DIEP_INT(epnum) = DEP_EPDIS_INT;
/*step 3: flush tx fifo*/
dwc_otg_flush_tx_fifo(dwc, epnum);
REG_DIEP_SIZE(epnum) = 0x0;
/*step 4: clear nak*/
if (epnum == 1)
REG_DIEP_CTL(1) |= DEP_CLEAR_NAK;
}
int dwc_enable_in_ep(dwc_handle *dwc,uint8_t epnum)
{
dwc_ep *pep = RT_NULL;
epnum &= DWC_EPNO_MASK;
pep = dwc->dep[DWC_EP_IN_OFS + epnum];
/* Program the endpoint register to configure them with the characteristics of valid endpoints */
REG_DIEP_CTL(epnum) &= ~DEP_PKTSIZE_MASK;
REG_DIEP_CTL(epnum) &= ~DEP_TYPE_MASK;
switch (dwc->speed)
{
case USB_SPEED_FULL:
case USB_SPEED_LOW:
REG_DIEP_CTL(epnum) |= DEP_FS_PKTSIZE;
break;
case USB_SPEED_HIGH:
REG_DIEP_CTL(epnum) |= DEP_HS_PKTSIZE;
break;
}
//tx fifo number
REG_DIEP_CTL(epnum) |= (epnum << 22);
//ep type
switch (pep->type)
{
case DWC_OTG_EP_TYPE_CONTROL:
REG_DIEP_CTL(epnum) |= USB_ACTIVE_EP | DEP_TYPE_CNTL;
break;
case DWC_OTG_EP_TYPE_ISOC:
REG_DIEP_CTL(epnum) |= USB_ACTIVE_EP | DEP_TYPE_ISO;
break;
case DWC_OTG_EP_TYPE_BULK:
REG_DIEP_CTL(epnum) |= USB_ACTIVE_EP | DEP_TYPE_BULK;
break;
case DWC_OTG_EP_TYPE_INTR:
REG_DIEP_CTL(epnum) |= USB_ACTIVE_EP | DEP_TYPE_INTR;
break;
}
/* DATA0 */
REG_DIEP_CTL(epnum) |= (1 << 28);
/* Enable EP INT */
REG_DAINT_MASK |= (0x01 << (DWC_EP_IN_OFS + epnum));
return 0;
}
int dwc_enable_out_ep(dwc_handle *dwc,uint8_t epnum)
{
uint32_t xfersize;
uint32_t dma_addr, dma_len, pktcnt;
dwc_ep *pep = RT_NULL;
epnum &= DWC_EPNO_MASK;
pep = dwc->dep[DWC_EP_OUT_OFS + epnum];
/* Program the endpoint register to configure them with the characteristics of valid endpoints */
REG_DOEP_CTL(epnum) &= ~DEP_PKTSIZE_MASK;
REG_DOEP_CTL(epnum) &= ~DEP_TYPE_MASK;
switch (dwc->speed)
{
case USB_SPEED_FULL:
case USB_SPEED_LOW:
REG_DOEP_CTL(epnum) |= DEP_FS_PKTSIZE;
break;
case USB_SPEED_HIGH:
REG_DOEP_CTL(epnum) |= DEP_HS_PKTSIZE;
break;
}
//ep type
switch (pep->type)
{
case DWC_OTG_EP_TYPE_CONTROL:
REG_DOEP_CTL(epnum) |= USB_ACTIVE_EP | DEP_TYPE_CNTL;
break;
case DWC_OTG_EP_TYPE_ISOC:
REG_DOEP_CTL(epnum) |= USB_ACTIVE_EP | DEP_TYPE_ISO;
break;
case DWC_OTG_EP_TYPE_BULK:
REG_DOEP_CTL(epnum) |= USB_ACTIVE_EP | DEP_TYPE_BULK;
break;
case DWC_OTG_EP_TYPE_INTR:
REG_DOEP_CTL(epnum) |= USB_ACTIVE_EP | DEP_TYPE_INTR;
break;
}
/* DATA0 */
REG_DOEP_CTL(epnum) |= (1 << 28);
/* Enable EP INT */
REG_DAINT_MASK |= (0x01 << (DWC_EP_OUT_OFS + epnum));
/* OUT-EP must init xfer buffer */
xfersize = pep->maxpacket * 2;
pktcnt = xfersize / DEP_EP_MAXPKT(epnum);
pep->xfer_len = xfersize;
pep->xfer_count = 0;
/* xfer_buffer has been initialized by up-layer */
// pep->xfer_buff = pep->xfer_buff;
DWC_DBG("%s %d xfer_buff: %x %x\n", __FUNCTION__, __LINE__, pep->xfer_buff, PHYS(pep->xfer_buff));
/* Program the DOEPSIZn register for the transfer size and corresponding packet count */
REG_DOEP_SIZE(epnum) &= ~(0x1fffffff);
REG_DOEP_SIZE(epnum) = (pktcnt << 19) | xfersize;
if (dwc->is_dma == IS_INTERN_DMA)
{
dma_addr = (uint32_t) (pep->xfer_buff);
dma_len = (((xfersize + 7) >> 3) << 3); //pep->xfer_len;
rt_hw_dcache_flush_range(dma_addr, dma_len);
/* Additionally, in DMA mode, program the DOEPDMAn register */
REG_DOEP_DMA(epnum) = PHYS(pep->xfer_buff);
}
/* Program the DOEPCTLn Register with endpoint charateristics,
* and set the Endpoint Enable and Clear NAK bit */
REG_DOEP_CTL(epnum) |= DEP_ENA_BIT | DEP_CLEAR_NAK;
return 0;
}
void dwc_set_address(dwc_handle *dwc,uint8_t address)
{
sleep_flag = 1;
REG_OTG_DCFG &= ~DCFG_DEV_ADDR_MASK;
REG_OTG_DCFG |= address << DCFG_DEV_ADDR_BIT;
}
void dwc_otg_ep0_out_start(dwc_handle *dwc)
{
dwc_ep *pep = dwc->dep[DWC_EP_OUT_OFS + 0];
DWC_DBG("%s %d\n",__func__,__LINE__);
pep->xfer_len = 64;
pep->xfer_count = 0;
pep->maxpacket = 64;
// pep->ctrl_req_addr = (uint32_t)(&setup_packet[0]);
pep->xfer_buff = pep->xfer_buff;
if (dwc->is_dma == IS_INTERN_DMA)
{
REG_DOEP_SIZE(0) = DOEPSIZE0_SUPCNT_3 | DOEPSIZE0_PKTCNT_BIT | (pep->maxpacket);
// REG_DOEP_DMA(0) = PHYS(pep->ctrl_req_addr);
REG_DOEP_DMA(0) = PHYS(pep->xfer_buff);
}
else
{
REG_DOEP_SIZE(0) = DOEPSIZE0_SUPCNT_3 | DOEPSIZE0_PKTCNT_BIT | (pep->maxpacket);
}
}
static void dwc_calculate_fifo_size(dwc_handle *dwc)
{
/*
* TODO: we are use "Dedicated FIFO Mode with No Thresholding"
* if need thresholding, the calculation algorithm may need change
*/
/**
* 3.2.1.1 FIFO SPRAM(Single-Port RAM) mapping:
*
* 1. One common RxFIFO, used in Host and Device modes
* 2. One common Periodic TxFIFO, used in Host mode
* 3. Separate IN endpoint transmit FIFO for each Device mode IN endpoints in Dedicated Transmit FIFO
* operation (OTG_EN_DED_TX_FIFO = 1)
* 4. The FIFO SPRAM is also used for storing some register values to save gates. In Scatter/Gather DMA
* mode, four SPRAM locations (four 35-bit words) are reserved for this. In DMA and Slave modes
* (non-Scatter/Gather mode), one SPRAM location (one 35-bit word) is used for storing the DMA epnum.
*
* NOTE: when the device is operating in Scatter/Gather mode, then the last
* locations of the SPRAM store the Base Descriptor epnum, Current
* Descriptor epnum, Current Buffer epnum and status quadlet
* information for each endpoint direction (4 locations per Endpoint).
* If an endpoint is bidirectional, then 4 locations will be used for IN,
* and another 4 for OUT
* 3.2.4.4 Endpoint Information Controller
* The last locations in the SPRAM are used to hold register values.
* Device Buffer DMA Mode:
* one location per endpoint direction is used in SPRAM to store the
* DIEPDMA and DOEPDMA value. The application writes data and then reads
* it from the same location
* For example, if there are ten bidirectional endpoints, then the last
* 20 SPRAM locations are reserved for storing the DMA epnum for IN
* and OUT endpoints
* Scatter/Gather DMA Mode:
* Four locations per endpoint direction are used in SPRAM to store the
* Base Descriptor epnum, Current Descriptor epnum, Current Buffer
* Pointer and the Status Quadlet.
* The application writes data to the base descriptor epnum.
* When the application reads the location where it wrote the base
* descriptor epnum, it receives the current descriptor epnum.
* For example, if there are ten bidirectional endpoints, then the last 80
* locations are reserved for storing these values.
*
* Figure 3-13
* ________________________
* | |
* | DI/OEPDMAn Register | Depends on the value of OTG_NUM_EPS
* | and Descriptor Status | and OTG_EP_DIRn, see not above
* | values |
* -------------------------
* | TxFIFO #n Packets | DIEPTXFn
* -------------------------
* | |
* | ................ |
* | |
* -------------------------
* | TxFIFO #1 Packets | DIEPTXF1
* -------------------------
* | TxFIFO #0 Packets |
* |( up to3 SETUP Packets)| GNPTXFSIZ
* ------------------------
* | |
* | Rx Packets | GRXFSIZ
* | |
* ------------------------- epnum = 0, Rx starting epnum fixed to 0
*
*/
/**
* Rx FIFO Allocation (rx_fifo_size)
*
* RAM for SETUP Packets: 4 * n + 6 locations must be Reserved in the receive FIFO to receive up to
* n SETUP packets on control endpoints, where n is the number of control endpoints the device
* core supports.
*
* One location for Global OUT NAK
*
* Status information is written to the FIFO along with each received packet. Therefore, a minimum
* space of (Largest Packet Size / 4) + 1 must be allotted to receive packets. If a high-bandwidth
* endpoint is enabled, or multiple isochronous endpoints are enabled, then at least two (Largest
* Packet Size / 4) + 1 spaces must be allotted to receive back-to-back packets. Typically, two
* (Largest Packet Size / 4) + 1 spaces are recommended so that when the previous packet is being
* transferred to AHB, the USB can receive the subsequent packet. If AHB latency is high, you must
* allocate enough space to receive multiple packets. This is critical to prevent dropping of any
* isochronous packets.
*
* Typically, one location for each OUT endpoint is recommended.
*
* one location for eatch endpoint for EPDisable is required
*/
/**
* Tx FIFO Allocation (tx_fifo_size[n])
*
* The minimum RAM space required for each IN Endpoint Transmit FIFO is the maximum packet size
* for that particular IN endpoint.
*
* More space allocated in the transmit IN Endpoint FIFO results in a better performance on the USB
*and can hide latencies on the AHB.
*/
uint32_t rx_fifo_size, i;
uint32_t np_txfifo_size = 0;
uint32_t tx_fifo_size;
uint16_t startaddr;
uint16_t fifocfg;
const int x = 1;
/* Step1: Recevice FIFO Size Register (GRXFSIZ) */
rx_fifo_size = (4 * 1 + 6) + (2) * (1024 / 4 + 1) + (2 * dwc->hwcfg2.b.num_dev_ep) + 1;
REG_GRXFIFO_SIZE = rx_fifo_size;
/* Step2: Program device in ep transmit fifo0 size register (GNPTXFSIZ) */
np_txfifo_size |= ((1 + 1) * (64 / 4) << 16); //depth
np_txfifo_size |= rx_fifo_size; //startaddr
REG_GNPTXFIFO_SIZE = np_txfifo_size;
#define DWC_TX_FIFO_SIZE ((1 + 1) * (512 / 4))
startaddr = ((1 + 1) * (64 / 4) << 16) + rx_fifo_size;
for (i=1; i<dwc->hwcfg4.b.num_in_eps; i++)
{
tx_fifo_size |= (DWC_TX_FIFO_SIZE << 16) | startaddr;
REG_GDIEP_TXF(i) = tx_fifo_size;
startaddr += DWC_TX_FIFO_SIZE;
}
/* Configure fifo start addr and depth for endpoint information controller */
REG_GDFIFO_CFG |= startaddr << 16;
fifocfg = REG_GHW_CFG3;
fifocfg = (fifocfg >> 16);
REG_GDFIFO_CFG |= fifocfg;
/* flush tx and rx fifo */
dwc_otg_flush_rx_fifo(dwc);
dwc_otg_flush_tx_fifo(dwc,0x10);
}
static void dwc_handle_enum_done_intr(dwc_handle *dwc)
{
dwc_ep *pep = dwc->dep[0];
/* Step1: Read the DSTS register to determine the enumeration speed */
uint32_t dsts = REG_OTG_DSTS;
uint32_t diep0ctl = REG_DIEP_CTL(0);
diep0ctl &= ~(0x3);
switch (dsts & DSTS_ENUM_SPEED_MASK)
{
case DSTS_ENUM_SPEED_HIGH:
DWC_DBG("High speed.\n");
dwc->speed = USB_SPEED_HIGH;
pep->maxpacket = 64;
diep0ctl |= DEP_EP0_MPS_64;
REG_OTG_DCFG &= ~1;
break;
case DSTS_ENUM_SPEED_FULL_30OR60:
case DSTS_ENUM_SPEED_FULL_48:
DWC_DBG("Full speed.\n");
dwc->speed = USB_SPEED_FULL;
pep->maxpacket = 64;
diep0ctl |= DEP_EP0_MPS_64;
REG_OTG_DCFG |= 1;
break;
case DSTS_ENUM_SPEED_LOW:
DWC_DBG("Low speed.\n");
dwc->speed = USB_SPEED_LOW;
pep->maxpacket = 8;
diep0ctl |= DEP_EP0_MPS_8;
break;
default:
DWC_DBG("Fault speed enumration\n");
break;
}
REG_OTG_DCTL |= DCTL_CLR_GNPINNAK;
/* Step2: Program the DIEPCTL0.MPS to set the maximum packet size */
REG_DIEP_CTL(0) = diep0ctl;
/* Step3: In Dma mode program the DOEPCTL0 register
* to enable control ouctrl_req_addrt endpoint0 to receive setup
* packet .*/
// dwc_otg_ep0_out_start(dwc);
if (dwc->is_dma == IS_INTERN_DMA)
{
rt_hw_dcache_flush_all();
DWC_DBG("0 doepsize %x ctl %x\n", REG_DOEP_SIZE(0), REG_DOEP_CTL(0));
REG_DOEP_CTL(0) |= DEP_ENA_BIT | DEP_CLEAR_NAK;
}
else
{
REG_DOEP_CTL(0) |= DEP_ENA_BIT | DEP_CLEAR_NAK;
}
/* Step4: unmask the SOF interrupt */
REG_GINT_MASK |= GINTMSK_START_FRAM;
REG_GINT_STS = GINTSTS_ENUM_DONE;
// dump_global_dwcreg();
return;
}
static void dwc_handle_early_suspend_intr(dwc_handle *dwc)
{
DWC_DBG("Handle early suspend intr.\n");
REG_GINT_STS = GINTSTS_USB_EARLYSUSPEND;
if (REG_OTG_DSTS & DSTS_ERRATIC_ERROR)
{
REG_OTG_DCTL |= DCTL_SOFT_DISCONN;
mdelay(100);
dwc_otg_core_reset(dwc);
dwc_otg_core_init(dwc,1);
dwc_otg_device_init(dwc);
dwc_calculate_fifo_size(dwc);
}
}
static void dwc_handle_suspend_intr(dwc_handle *dwc)
{
DWC_DBG("Handle suspend intr.\n");
REG_GINT_STS = GINTSTS_USB_SUSPEND;
DWC_DBG("==>%s,sleep_flag = %d\n",__func__,sleep_flag);
#if 0
if(sleep_flag)
{
while(!(REG_OTG_DSTS & 1))
{
printf("REG_OTG_DSTS is 0x%x\n",REG_OTG_DSTS);
}
sleep_flag = 0;
enable_irq(IRQ_OTG);
jz_pm_sleep();
}
#endif
}
static void dwc_handle_start_frame_intr(dwc_handle *dwc)
{
REG_GINT_STS = GINTSTS_START_FRAM;
}
static void dwc_handle_reset_intr(dwc_handle *dwc)
{
int i;
/* Step1: NAK OUT ep */
for (i=0; i<dwc->hwcfg2.b.num_dev_ep; i++)
{
REG_DOEP_CTL(i) |= DEP_SET_NAK;
}
/* Step2: unmask the following interrupt bits */
REG_DAINT_MASK = 0;
REG_DOEP_MASK = 0;
REG_DIEP_MASK = 0;
REG_DAINT_MASK |= (1 << 0) | (1 << 16); //inep0 outep0
REG_DOEP_MASK |= DEP_XFER_COMP | DEP_SETUP_PHASE_DONE | DEP_AHB_ERR; // xfercompl setupdone
REG_DIEP_MASK |= DEP_XFER_COMP | DEP_TIME_OUT | DEP_AHB_ERR; // xfercompl ahberr timeout
dwc->dep[0]->ep_state = EP_SETUP;
/* Step3: Device initalization */
dwc_otg_device_init(dwc);
/* Step4: Set up the data fifo ram for each of the fifo */
//dwc_calculate_fifo_size();
/* Step5: Reset Device Address */
REG_OTG_DCFG &= (~DCFG_DEV_ADDR_MASK);
/* Step6: setup EP0 to receive SETUP packets */
dwc_otg_ep0_out_start(dwc);
if (dwc->is_dma == IS_INTERN_DMA)
{
rt_hw_dcache_flush_all();
REG_DOEP_CTL(0) |= DEP_ENA_BIT | DEP_CLEAR_NAK;
}
else
{
REG_DOEP_CTL(0) |= DEP_ENA_BIT | DEP_CLEAR_NAK;
}
dwc_disable_in_ep(dwc,0);
REG_GINT_STS = GINTSTS_USB_RESET;
return;
}
void dwc_handle_rxfifo_nempty(dwc_handle *dwc)
{
dwc_ep *pep;
uint32_t *setup_buf;
uint32_t count;
uint32_t rxsts_pop = REG_GRXSTS_POP;
uint8_t epnum = (rxsts_pop & 0xf);
switch (rxsts_pop & GRXSTSP_PKSTS_MASK)
{
case GRXSTSP_PKSTS_GOUT_NAK:
DWC_DBG("GRXSTSP_PKSTS_GOUT_NAK.\n");
break;
case GRXSTSP_PKSTS_GOUT_RECV:
DWC_DBG("GRXSTSP_PKSTS_GOUT_RECV. - ");
count = (rxsts_pop & GRXSTSP_BYTE_CNT_MASK) >> GRXSTSP_BYTE_CNT_BIT;
if (count)
{
DWC_DBG("count:%d\n", count);
dwc_read_ep_packet(dwc,epnum, count);
}
break;
case GRXSTSP_PKSTS_TX_COMP:
DWC_DBG("GRXSTSP_PKSTS_TX_COMP.\n");
break;
case GRXSTSP_PKSTS_SETUP_COMP:
DWC_DBG("GRXSTSP_PKSTS_SETUP_COMP.\n");
break;
case GRXSTSP_PKSTS_SETUP_RECV:
DWC_DBG("GRXSTSP_PKSTS_SETUP_RECV. - ");
// setup_packet[0] = REG_EP_FIFO(0);
// setup_packet[1] = REG_EP_FIFO(0);
// DWC_DBG("%x %x\n", setup_packet[0], setup_packet[1]);
((uint8_t *)dwc->dep[0]->xfer_buff)[0] = REG_EP_FIFO(0);
((uint8_t *)dwc->dep[0]->xfer_buff)[1] = REG_EP_FIFO(1);
DWC_DBG("%x %x\n", ((uint8_t *)dwc->dep[0]->xfer_buff)[0], ((uint8_t *)dwc->dep[0]->xfer_buff)[1]);
break;
default:
break;
}
REG_GINT_STS = GINTSTS_RXFIFO_NEMPTY;
}
void dwc_ep0_in_intr(dwc_handle *dwc, uint8_t epnum)
{
uint32_t updated_size;
uint32_t dma_addr, dma_len;
uint8_t *ptr;
uint32_t intr = REG_DIEP_INT(epnum & 0x0F);
dwc_ep *pep ;
DWC_DBG("ep0 in intr:%x\n", intr);
epnum &= DWC_EPNO_MASK;
pep = dwc->dep[DWC_EP_IN_OFS + epnum];
udelay(1);
/* When the transfer size if 0 and the packet count is 0,
* the transfer complete interrupt for the endpoint is generated
* and the endpoint enable is cleared */
if (intr & DEP_XFER_COMP)
{
DWC_DBG("XFER_COMP\n");
REG_DIEP_INT(epnum) = DEP_XFER_COMP; // clear int
if (dwc->is_dma == IS_SLAVE_MODE)
REG_DIEP_EMPMSK &= ~(1 << epnum);
updated_size = (REG_DIEP_SIZE(epnum) & 0x7f);
pep->xfer_count = pep->xfer_len - updated_size; // number of bytes transfered
DWC_DBG("in xfer_count:%d xfer_len:%d updated_size:%d\n", pep->xfer_count, pep->xfer_len, updated_size);
if (pep->xfer_count != pep->xfer_len)
{
DWC_DBG("in xfer_count:%d xfer_len:%d updated_size:%d\n", pep->xfer_count, pep->xfer_len, updated_size);
pep->xfer_len -= pep->xfer_count;
ptr = (uint8_t *)pep->xfer_buff + pep->xfer_count;
HW_SendPKT(dwc, 0, ptr, pep->xfer_len);
return;
}
DWC_DBG("pep->ep_state = %s\n",ep0_state_string[pep->ep_state]);
switch(pep->ep_state)
{
case EP_DATA:
#if 1
/* 3 Stage */
if(pep->xfer_len == pep->maxpacket)
{
x1000_usbd_event_cb(0, USB_EVT_IN, RT_NULL);
}
else
#endif
{
pep->ep_state = EP_STATUS;
dwc_handle_ep_data_out_phase(dwc,0);
}
break;
case EP_STATUS:
pep->ep_state = EP_SETUP;
dwc_handle_ep_data_out_phase(dwc,0);
break;
}
}
if (dwc->is_dma == IS_SLAVE_MODE)
{
if ((intr & DEP_TXFIFO_EMPTY) && (REG_DIEP_EMPMSK & (1 << epnum)))
{
if (pep->xfer_len)
{
dwc_write_ep_packet(dwc,epnum);
}
REG_DIEP_INT(epnum) = DEP_TXFIFO_EMPTY;
}
}
if (intr & DEP_AHB_ERR)
{
DWC_DBG("1 AHB ERR\n");
REG_DIEP_INT(epnum) = DEP_AHB_ERR;
}
if (intr & DEP_TIME_OUT)
{
DWC_DBG("IN TIME_OUT.\n");
REG_DIEP_INT(epnum) = DEP_TIME_OUT;
}
}
void dwc_epn_in_intr(dwc_handle *dwc, uint8_t epnum)
{
uint32_t intr = REG_DIEP_INT(epnum & 0x0F);
uint32_t updated_size;
/* When the transfer size if 0 and the packet count is 0,
* the transfer complete interrupt for the endpoint is generated
* and the endpoint enable is cleared */
dwc_ep *pep;
epnum &= DWC_EPNO_MASK;
pep = dwc->dep[DWC_EP_IN_OFS + epnum];
if (intr & DEP_XFER_COMP)
{
DWC_DBG("1 IN XFER_COMP. %x\n", REG_DIEP_SIZE(epnum));
REG_DIEP_INT(epnum) = DEP_XFER_COMP;
REG_DIEP_CTL(epnum) |= DEP_SET_NAK;
if (pep->ep_state == EP_TRANSFERING)
{
if (dwc->is_dma == IS_SLAVE_MODE)
REG_DIEP_EMPMSK &= ~(1 << epnum);
updated_size = (REG_DIEP_SIZE(epnum) & 0x7ffff);
pep->xfer_count = pep->xfer_len - updated_size;
pep->ep_state = EP_TRANSFERED;
// rt_kprintf("updated_size = %d,xfer_len = %d,xfer_count = %d\n",updated_size,pep->xfer_len, pep->xfer_count);
// BusNotify(arg, UDC_PROTAL_SEND_FINISH, NULL, 0);
x1000_usbd_event_cb(epnum,USB_EVT_IN,0);
}
}
if (dwc->is_dma == IS_SLAVE_MODE)
{
if ((intr & DEP_TXFIFO_EMPTY) && (REG_DIEP_EMPMSK & (1 << epnum)))
{
REG_DIEP_EMPMSK &= ~(1 << epnum);
// DWC_DBG("TX FIFO EMPTY intr.\n");
if (pep->xfer_len)
{
dwc_write_ep_packet(dwc,epnum);
}
REG_DIEP_INT(epnum) = DEP_TXFIFO_EMPTY;
}
}
if (intr & DEP_AHB_ERR)
{
DWC_DBG("1 AHB ERR\n");
REG_DIEP_INT(epnum) = DEP_AHB_ERR;
}
if (intr & DEP_TIME_OUT)
{
DWC_DBG("IN TIME_OUT.\n");
REG_DIEP_INT(epnum) = DEP_TIME_OUT;
}
}
/*
* ep0 control transfer:
* 3 Stage:
* SetupPhase-------->IN DataPhase ---------> OUT StatusPhase
* Or 2 Stage:
* SetupPhase-------->IN StatusPhase
* */
typedef struct {
u8 bmRequestType;
u8 bRequest;
u16 wValue;
u16 wIndex;
u16 wLength;
} __attribute__ ((packed)) dwc_DeviceRequest;
int dwc_ep0_out_intr(dwc_handle *dwc, uint8_t epnum)
{
uint32_t intr, doep0size, dma_addr, dma_len;
uint8_t rem_supcnt, xfersize;
dwc_ep *pep = RT_NULL;
epnum &= DWC_EPNO_MASK;
pep = dwc->dep[DWC_EP_OUT_OFS + epnum];
intr = REG_DOEP_INT(epnum);
// printf("ep0 out intr:%x\n", intr);
udelay(1);
/* comp intrerrupt indeicates completion of the status out phase */
if (intr & DEP_XFER_COMP)
{
REG_DOEP_INT(epnum) = DEP_XFER_COMP;
DWC_DBG("pep->ep_state = %s\n",ep0_state_string[pep->ep_state]);
if (pep->ep_state == EP_STATUS)
{
pep->ep_state = EP_SETUP;
dwc_handle_ep_data_out_phase(dwc,0);
}
else if (pep->ep_state == EP_DATA)
{
DWC_DBG("*** EP0 DATA ***\n");
pep->xfer_count = pep->maxpacket - (REG_DOEP_SIZE(epnum) & 0x7ffff);
DWC_DBG("pep->xfer_count = %d\n",pep->xfer_count);
x1000_usbd_event_cb(0,USB_EVT_OUT,0);
}
else if (!(intr & (DEP_SETUP_PHASE_DONE | (1 << 15))))
{
DWC_DBG("error\n");
pep->ep_state = EP_SETUP;
dwc_handle_ep_data_out_phase(dwc, epnum);
}
else if (pep->ep_state != EP_SETUP)
{
DWC_DBG("ep0 state mismatch\n");
}
}
//IN Token
if (intr & DEP_INTOKEN_EPMISATCH)
{
if (pep->ep_state == EP_DATA)
{
pep->ep_state = EP_STATUS;
dwc_handle_ep_status_in_phase(dwc, epnum);
}
REG_DOEP_INT(epnum) = DEP_INTOKEN_EPMISATCH;
}
if (intr & DEP_AHB_ERR)
{
DWC_DBG("AHB ERR\n");
REG_DOEP_INT(0) = DEP_AHB_ERR;
}
if (intr & DEP_NAK_INT)
{
REG_DOEP_INT(0) = DEP_NAK_INT;
}
if (intr & (DEP_SETUP_PHASE_DONE | (1 << 15)))
{
DWC_DBG("SETUP_PHASE_DONE.\n");
/* read the DOEPTSIZn to determine the number of setup packets
* recevied and process the last recevied setup packet */
REG_DOEP_INT(epnum) = DEP_SETUP_PHASE_DONE | (1 << 15);
doep0size = REG_DOEP_SIZE(epnum);
xfersize = doep0size & 0x7ffff;
rem_supcnt = (doep0size & (0x3 << 29)) >> 29;
DWC_DBG("xfersize = %d,rem_supcnt = %d\n",xfersize,rem_supcnt);
if (intr & DEP_B2B_SETUP_RECV)
{
DWC_DBG("back to back setup recevie\n");
}
else
{
/* Read out the last packet from the rxfifo */
// rt_hw_dcache_invalidate_range((uint32_t)(pep->ctrl_req_addr), sizeof(dwc_DeviceRequest));
rt_hw_dcache_invalidate_range((uint32_t)(pep->xfer_buff), sizeof(dwc_DeviceRequest));
#if 0
{
dwc_DeviceRequest* device_req = (dwc_DeviceRequest *)(pep->ctrl_req_addr);
rt_kprintf("\n-------------\n");
rt_kprintf("bRequest: %x\n", device_req->bRequest);
rt_kprintf("bRequestType: %x\n", device_req->bmRequestType);
rt_kprintf("wIndex: %x\n", device_req->wIndex);
rt_kprintf("wLength: %x\n", device_req->wLength);
rt_kprintf("wValue: %x\n", device_req->wValue);
rt_kprintf("-------------\n");
}
#endif
/* At the end of the Setup stage, the appliaction must reporgram the
* DOEPTSIZn.SUPCnt field to 3 receive the next SETUP packet */
if (pep->ep_state == EP_SETUP)
{
if (dwc->is_dma == 2)
{
//printf("1 doepsize %x ctl %x\n", REG_DOEP_SIZE(0), REG_DOEP_CTL(0));
REG_DOEP_SIZE(epnum) = DOEPSIZE0_SUPCNT_3 | DOEPSIZE0_PKTCNT_BIT | (pep->maxpacket);
// REG_DOEP_DMA(epnum) = PHYS(pep->ctrl_req_addr);
REG_DOEP_DMA(epnum) = PHYS(pep->xfer_buff);
}
else
{
REG_DOEP_SIZE(epnum) = DOEPSIZE0_SUPCNT_3 | DOEPSIZE0_PKTCNT_BIT | (pep->maxpacket);
}
}
/* Setup Finish */
pep->xfer_count = sizeof(dwc_DeviceRequest);
// pep->xfer_buff = (void *) (pep->ctrl_req_addr);
// x1000_usbd_event_cb(0, USB_EVT_SETUP, (void *) (pep->ctrl_req_addr));
x1000_usbd_event_cb(0, USB_EVT_SETUP, pep->xfer_buff);
REG_DOEP_CTL(epnum) |= DEP_DISENA_BIT;
// REG_DOEP_CTL(epnum) |= DEP_SET_NAK;
}
}
return 0;
}
int dwc_epn_out_intr(dwc_handle *dwc, uint8_t epnum)
{
uint32_t intr, updated_size;
dwc_ep *pep ;
epnum &= DWC_EPNO_MASK;
pep = dwc->dep[DWC_EP_OUT_OFS + epnum];
udelay(1);
DWC_DBG("ep%d out_intr\n",epnum);
intr = REG_DOEP_INT(epnum);
if (intr & DEP_XFER_COMP)
{
REG_DOEP_INT(epnum) = DEP_XFER_COMP;
updated_size = REG_DOEP_SIZE(epnum) & 0x7ffff;
pep->xfer_count = pep->xfer_len - updated_size;
DWC_DBG("xfer_count = %d\n",pep->xfer_count);
// BusNotify((uint32_t)arg,UDC_PROTAL_RECEIVE_FINISH, (uint8_t *)pep->xfer_buff, pep->xfer_count);
x1000_usbd_event_cb(epnum,USB_EVT_OUT,0);
#if 0
pep->xfer_len = pep->maxpacket; /* number of bytes to transfer */
pep->xfer_count = 0; /* number of bytes transfered */
// pep->xfer_buff = pep->xfer_buff; /* pointer to transfer buffer */
dwc_handle_ep_data_out_phase(dwc, epnum);
DWC_DBG("REG_DOEP_SIZE: %x \n", REG_DOEP_SIZE(epnum));
#endif
}
if (intr & DEP_AHB_ERR)
{
DWC_DBG("1 AHB ERR\n");
REG_DOEP_INT(epnum) = DEP_AHB_ERR;
}
return 0;
}
static void dwc_handle_inep_intr(dwc_handle *dwc)
{
uint32_t ep_intr;
uint8_t epnum = 0;
ep_intr = (REG_OTG_DAINT & 0xffff);
DWC_DBG("\n\nEp IN %x - \n", ep_intr);
while (ep_intr)
{
if (ep_intr & 0x01)
{
if (epnum == 0)
{
dwc_ep0_in_intr(dwc, epnum);
}
else
{
dwc_epn_in_intr(dwc, epnum);
}
}
epnum++;
ep_intr >>= 1;
}
REG_GINT_STS = GINTSTS_IEP_INTR;
return ;
}
static void dwc_handle_outep_intr(dwc_handle *dwc)
{
uint32_t ep_intr, epnum = 0;
ep_intr = (REG_OTG_DAINT & 0xffff0000) >> 16;
DWC_DBG("\n\nEp OUT %x - \n", ep_intr);
while (ep_intr)
{
if (ep_intr & 0x01)
{
if (epnum == 0)
{
dwc_ep0_out_intr(dwc, 0);
}
else
{
dwc_epn_out_intr(dwc, epnum);
}
}
epnum ++;
ep_intr >>= 1;
}
REG_GINT_STS = GINTSTS_OEP_INTR;
}
static void dwc_otg_intr(dwc_handle *dwc)
{
REG_GINT_STS = GINTSTS_OTG_INTR;
}
void dwc_common_intr(dwc_handle *dwc,uint32_t intsts)
{
if (intsts & GINTSTS_USB_EARLYSUSPEND)
{
dwc_handle_early_suspend_intr(dwc);
}
if (intsts & GINTSTS_USB_SUSPEND)
{
dwc_handle_suspend_intr(dwc);
}
if (intsts & GINTSTS_USB_RESET)
{
dwc_handle_reset_intr(dwc);
}
if (intsts & GINTSTS_ENUM_DONE)
{
dwc_handle_enum_done_intr(dwc);
}
if (intsts & GINTSTS_START_FRAM)
{
dwc_handle_start_frame_intr(dwc);
}
}
void dwc_handle_resume_intr(dwc_handle *dwc)
{
DWC_DBG("Handle resume intr.\n");
REG_GINT_STS = GINTSTS_RSUME_DETE;
// dwc_otg_phy_suspend(0);
}
static void dwc_irq_handler(int vector,void *arg)
{
dwc_handle *dwc = (dwc_handle *)arg;
RT_ASSERT(dwc != RT_NULL);
rt_hw_interrupt_mask(IRQ_OTG);
rt_sem_release(dwc->isr_sem);
}
static void dwc_otg_core_reset(dwc_handle *dwc)
{
uint32_t greset = 0;
uint32_t cnt = 0;
REG_GRST_CTL |= RSTCTL_CORE_RST;
do
{
greset = REG_GRST_CTL;
if (cnt++ > 100000)
{
DWC_DBG("GRESET wait reset timeout.\n");
return;
}
udelay(10);
} while (greset & RSTCTL_CORE_RST);
cnt = 0;
do
{
udelay(10);
greset = REG_GRST_CTL;
if (cnt++ > 100000)
{
DWC_DBG("GRESET wait IDLE timeout.\n");
return;
}
} while ((greset & RSTCTL_AHB_IDLE) == 0);
/* wait for 3 phy clocks */
udelay(100);
}
static int dwc_otg_phy_is_suspend(void)
{
return (!(cpm_test_bit(7, CPM_OPCR)));
}
static void dwc_otg_phy_suspend(int suspend)
{
if (!suspend && dwc_otg_phy_is_suspend())
{
DWC_DBG("EN PHY\n");
cpm_set_bit(7, CPM_OPCR);
udelay(45);
}
else if (suspend && !dwc_otg_phy_is_suspend())
{
DWC_DBG("DIS PHY\n");
cpm_clear_bit(7, CPM_OPCR);
udelay(5);
}
}
static void dwc_otg_device_init(dwc_handle *dwc)
{
uint32_t dcfg = 0;
uint32_t pcgcctl;
uint32_t rx_fifo_size;
/* Restart the phy clock */
if (REG_PCGC_CTL & 0x1)
{
DWC_DBG("<<<<<< pcgcctl %x >>>>>\n", REG_PCGC_CTL);
REG_PCGC_CTL &= ~(0x1 | (1 << 2) | (1 << 3));
}
/* In dma mode GINTMSK_NPTXFIFO_EMPTY , GINTMSK_RXFIFO_NEMPTY must be masked*/
if (dwc->is_dma == IS_INTERN_DMA)
{
if (REG_GINT_MASK & (GINTMSK_NPTXFIFO_EMPTY | GINTMSK_RXFIFO_NEMPTY))
{
REG_GINT_MASK &= ~(GINTMSK_NPTXFIFO_EMPTY | GINTMSK_RXFIFO_NEMPTY);
}
}
else
{
REG_GINT_MASK |= (GINTMSK_NPTXFIFO_EMPTY | GINTMSK_RXFIFO_NEMPTY);
}
/* Program the DCFG register */
if (dwc->hwcfg4.b.desc_dma)
{
dcfg |= DCFG_DEV_DESC_DMA;
}
#if DWC_FORCE_SPEED_FULL
REG_OTG_DCFG |= 1; //dma buffer mode full speed
#else
REG_OTG_DCFG &= ~3; //dma buffer mode HIGH speed
#endif
/* Clear the DCTL.SftDiscon bit the core issues aconnect after ths bit is cleared */
REG_OTG_DCTL &= ~DCTL_SOFT_DISCONN;
REG_GINT_STS = 0xffffffff;
/* Program the GINTMSK */
REG_GINT_MASK |= GINTMSK_IEP_INTR | GINTMSK_OEP_INTR |GINTMSK_USB_RESET | GINTMSK_ENUM_DONE |GINTMSK_USB_EARLYSUSPEND | GINTMSK_USB_SUSPEND | (1 << 31);
}
static void dwc_otg_core_init(dwc_handle *dwc,uint8_t dma_enable)
{
uint32_t ahbcfg = 0, gusbcfg = 0, curmod = 0, tmp;
uint8_t arch;
DWC_DBG("Core Init...\n");
/* Step1: Read the GHWCFG1,2,3,4 to find the configuration parameters selected for DWC_otg core */
dwc->hwcfg1.d32 = REG_GHW_CFG1;
dwc->hwcfg2.d32 = REG_GHW_CFG2;
dwc->hwcfg3.d32 = REG_GHW_CFG3;
dwc->hwcfg4.d32 = REG_GHW_CFG4;
DWC_DBG("cfg1:%x 2:%x 3:%x 4:%x\n", dwc->hwcfg1, dwc->hwcfg2, dwc->hwcfg3, dwc->hwcfg4);
DWC_DBG("cfg2->arch %x\n", dwc->hwcfg2.b.architecture);
arch = dwc->hwcfg2.b.architecture;
switch (arch)
{
case IS_SLAVE_MODE:
dwc->is_dma = IS_SLAVE_MODE;
break;
case IS_EXTERN_DMA:
dwc->is_dma = IS_EXTERN_DMA;
break;
case IS_INTERN_DMA:
dwc->is_dma = IS_INTERN_DMA;
break;
}
/* Step2: Program the GAHBCFG register */
/* DMA Mode bit and Burst Length */
if (dwc->is_dma == IS_EXTERN_DMA)
{
DWC_DBG("DWC IS_EXTERN_DMA\n");
ahbcfg |= AHBCFG_DMA_ENA;
}
else if (dwc->is_dma == IS_INTERN_DMA)
{
if (dma_enable)
{
DWC_DBG("DWC IS_INTERN_DMA\n");
ahbcfg |= AHBCFG_DMA_ENA | (DWC_GAHBCFG_INT_DMA_BURST_INCR16 << 1);
}
else
{
ahbcfg |= AHBCFG_TXFE_LVL;
dwc->is_dma = 0;
}
}
else
{
DWC_DBG("DWC IS_SLAVE_MODE\n");
}
/* Step3: Program the GINTMSK register */
REG_GINT_MASK = 0;
/* Step4: Program the GUSBCFG register */
gusbcfg = REG_GUSB_CFG;
gusbcfg &= ~((1 << 4) | (1 << 6) | (1 << 8) | (1 << 9));
REG_GUSB_CFG = gusbcfg; // HNP SRP not support and select UTMI+
dwc_otg_select_phy_width(dwc);
dwc_otg_core_reset(dwc);
/* Global Interrupt Mask bit = 1 */
ahbcfg |= AHBCFG_GLOBLE_INTRMASK;
REG_GAHB_CFG = ahbcfg;
/* Step5: The software must unmask OTG Interrupt Mask bit ,
* MOde mismatch interrupt Mask bit in the GINTMSK */
REG_GINT_MASK |= (GINTMSK_MODE_MISMATCH | GINTMSK_OTG_INTR);
}
int dwc_set_config(dwc_handle *dwc)
{
return 0;
}
int dwc_set_ep_stall(dwc_handle *dwc,uint8_t epnum)
{
depctl_data_t depctl;
daint_data_t daintmsk;
dwc_ep *pep = RT_NULL;
if(epnum & USB_DIR_IN)
{
pep = dwc->dep[epnum & 0x0F + DWC_EP_IN_OFS];
}
else
{
pep = dwc->dep[epnum & 0x0F + DWC_EP_OUT_OFS];
}
epnum &= DWC_EPNO_MASK;
if (pep->is_in)
{
depctl.d32 = REG_DIEP_CTL(epnum);
depctl.b.stall = 1;
REG_DIEP_CTL(epnum) = depctl.d32;
}
else
{
depctl.d32 = REG_DOEP_CTL(epnum);
depctl.b.stall = 1;
REG_DOEP_CTL(epnum) = depctl.d32;
}
return 0;
}
int dwc_clr_ep_stall(dwc_handle *dwc,uint8_t epnum)
{
depctl_data_t depctl;
daint_data_t daintmsk;
dwc_ep *pep = RT_NULL;
if(epnum & USB_DIR_IN)
{
pep = dwc->dep[epnum & 0x0F + DWC_EP_IN_OFS];
}
else
{
pep = dwc->dep[epnum & 0x0F + DWC_EP_OUT_OFS];
}
epnum &= DWC_EPNO_MASK;
if (pep->is_in)
{
depctl.d32 = REG_DIEP_CTL(epnum);
depctl.b.stall = 0;
REG_DIEP_CTL(epnum) = depctl.d32;
}
else
{
depctl.d32 = REG_DOEP_CTL(epnum);
depctl.b.stall = 0;
REG_DOEP_CTL(epnum) = depctl.d32;
}
return 0;
}
int dwc_ep_disable(dwc_handle *dwc,uint8_t epnum)
{
depctl_data_t depctl;
daint_data_t daintmsk;
dwc_ep *pep = RT_NULL;
DWC_DBG("%s epnum = %02x \n",epnum);
if(epnum & USB_DIR_IN)
{
pep = dwc->dep[epnum & 0x0F + DWC_EP_IN_OFS];
}
else
{
pep = dwc->dep[epnum & 0x0F + DWC_EP_OUT_OFS];
}
epnum &= DWC_EPNO_MASK;
/* EP0 can not deactivate! */
if (epnum == 0)
return -1;
daintmsk.d32 = REG_DAINT_MASK;
if (pep->is_in)
{
depctl.d32 = REG_DIEP_CTL(epnum);
daintmsk.ep.in &= ~(1 << epnum);
}
else
{
depctl.d32 = REG_DOEP_CTL(epnum);
daintmsk.ep.out &= ~(1 << epnum);
}
if (!depctl.b.usbactep)
{
DWC_DBG("EP %d already deactivated\n", pep->num);
return 0;
}
depctl.b.usbactep = 0;
if (pep->is_in)
{
REG_DIEP_CTL(epnum) = depctl.d32;
}
else
{
REG_DOEP_CTL(epnum) = depctl.d32;
}
/* mask EP interrupts */
REG_DAINT_MASK = daintmsk.d32;
if(pep->is_in)
{/* Disable IN-EP */
}
else
{
/* Disable IN-EP */
}
DWC_DBG("EP %d deactivated\n", pep->num);
return 0;
}
static void x1000_usb_phy_switch(dwc_handle *dwc,uint8_t is_on)
{
uint32_t value;
if (is_on)
{
value = REG_CPM_OPCR;
REG_CPM_OPCR |= OPCR_OTGPHY0_ENABLE;
mdelay(1);
}
else
{
value = REG_CPM_OPCR;
REG_CPM_OPCR &= ~OPCR_OTGPHY0_ENABLE;
mdelay(1);
}
}
static void x1000_usb_set_device_only_mode(dwc_handle *dwc)
{
REG_CPM_USBPCR &= ~USBPCR_USB_MODE;
REG_CPM_USBPCR &= ~USBPCR_OTG_DISABLE;
}
static void x1000_usb_phy_init(dwc_handle *dwc)
{
REG_CPM_USBPCR |= USBPCR_POR;
mdelay(1);
REG_CPM_USBPCR &= ~USBPCR_POR;
mdelay(1);
}
/* usb device init */
static void dwc_gadget_init(dwc_handle *dwc)
{
uint32_t curmod;
int err;
// REG_CPM_CLKGR0 &= ~(1 << 2);
rt_hw_interrupt_mask(IRQ_OTG);
/* usb_cpm_init(); */
{
uint32_t ref_clk_div = 24 / 24; //24 / 24;
uint32_t usbpcr1;
/* select dwc otg */
REG_CPM_USBPCR1 |= USBPCR1_USB_SEL;
/* select utmi data bus width of port0 to 16bit/30M */
REG_CPM_USBPCR1 |= USBPCR1_WORD_IF0;
usbpcr1 = REG_CPM_USBPCR1;
usbpcr1 &= ~(0x3 << 24);
usbpcr1 |= (ref_clk_div << 24);
REG_CPM_USBPCR1 = usbpcr1;
/* fil */
REG_CPM_USBVBFIL = 0;
/* rdt */
REG_CPM_USBRDT = 0x96;
/* rdt - filload_en */
REG_CPM_USBRDT |= USBRDT_VBFIL_LD_EN;
/* TXRISETUNE & TXVREFTUNE. */
REG_CPM_USBPCR = 0x3f;
REG_CPM_USBPCR = 0x35;
#if 1
REG_CPM_USBPCR &= ~(1 << 31);
REG_CPM_USBPCR |= (1 << 23) | (1 << 24);
#endif
/* enable tx pre-emphasis */
REG_CPM_USBPCR |= USBPCR_TXPREEMPHTUNE;
/* OTGTUNE adjust */
REG_CPM_USBPCR = (7 << 14);
}
REG_CPM_USBPCR |= 1 << 20;
/* force usb device mode */
x1000_usb_set_device_only_mode(dwc);
x1000_usb_phy_init(dwc);
x1000_usb_phy_switch(dwc,1);
/* soft disconnect and soft reset */
REG_OTG_DCTL |= DCTL_SOFT_DISCONN;
udelay(3000);
/* reset dwc register */
dwc_otg_core_reset(dwc);
/* DWC OTG Core init */
dwc_otg_core_init(dwc,1);
/* Read Gintsts confirm the device or host mode */
curmod = REG_GINT_STS;
if (curmod & 0x1)
{
DWC_DBG("Curmod: Host Mode\n");
}
else
{
DWC_DBG("Curmod: Device Mode\n");
/* DWC OTG Device init */
dwc_otg_device_init(dwc);
/* DWC OTG Fifo init */
dwc_calculate_fifo_size(dwc);
}
/* End-point has been inited */
// dwc_init_endpoint(dwc);
}
static void x1000_usbd_isr_service(void *param)
{
dwc_handle *dwc = (dwc_handle *)param;
uint8_t err;
uint32_t intsts;
RT_ASSERT(dwc != RT_NULL);
while (1)
{
rt_sem_take(dwc->isr_sem, RT_WAITING_FOREVER);
intsts = REG_GINT_STS;
if (intsts & GINTSTS_OTG_INTR)
{
DWC_DBG("OTG_INTR\n");
dwc_otg_intr(dwc);
}
if ((intsts & GINTSTS_USB_EARLYSUSPEND)
|| (intsts & GINTSTS_USB_SUSPEND)
|| (intsts & GINTSTS_START_FRAM)
|| (intsts & GINTSTS_USB_RESET)
|| (intsts & GINTSTS_ENUM_DONE))
{
dwc_common_intr(dwc, intsts);
}
/* dwc in pio mode not dma mode */
if (intsts & GINTSTS_RXFIFO_NEMPTY)
{
DWC_DBG("GINTSTS_RXFIFO_NEMPTY!!\n");
if (dwc->is_dma == IS_SLAVE_MODE) dwc_handle_rxfifo_nempty(dwc);
REG_GINT_STS = GINTSTS_RXFIFO_NEMPTY;
}
if (intsts & GINTSTS_IEP_INTR)
{
DWC_DBG("IEP_INTR!!!\n");
dwc_handle_inep_intr(dwc);
}
if (intsts & GINTSTS_OEP_INTR)
{
DWC_DBG("OEP_INTR!!!\n");
dwc_handle_outep_intr(dwc);
}
if (intsts & GINTSTS_RSUME_DETE)
{
DWC_DBG("RESUME_INTR\n");
dwc_handle_resume_intr(dwc);
}
if (intsts & (1 << 31))
{
REG_GINT_STS = 1 << 31;
}
rt_hw_interrupt_umask(IRQ_OTG);
}
}
void x1000_usbd_init(dwc_handle *dwc)
{
uint32_t curmod = 0;
DWC_DBG("Init UDC %s %s\n",__DATE__,__TIME__);
if(dwc->isr_sem == RT_NULL)
{
dwc->isr_sem = rt_sem_create("dwcSem",0,RT_IPC_FLAG_FIFO);
if (!dwc->isr_sem)
{
DWC_DBG("%s %d sem create err\n", __func__, __LINE__);
while (1) ;
}
dwc->status.b.state = USB_CABLE_DISCONNECT;
dwc->status.b.event = 0;
}
dwc_gadget_init(dwc);
/* create a ISR service task */
{
rt_thread_t tid;
tid = rt_thread_create("dwcIntSv",
x1000_usbd_isr_service, (void *) dwc,
2048,
RT_THREAD_PRIORITY_MAX/5,
20);
if (tid != RT_NULL) rt_thread_startup(tid);
rt_kprintf("dwc interrupt service init done...\n");
}
/* request irq */
rt_hw_interrupt_install(IRQ_OTG,dwc_irq_handler,(void *)dwc,"otgISR");
rt_hw_interrupt_umask(IRQ_OTG);
DWC_DBG("[DWC] DWC request IRQ success %x\n", REG_GINT_MASK);
}