blob: 5051b682467d3d2c6093ed65e47a962385c2d00b [file] [log] [blame]
/* dwc controller pcd interrupt drivers */
/*
* (C) Copyright 2010 Amlogic, Inc
*
* Victor Wan, victor.wan@amlogic.com,
* 2010-03-30 @ Shanghai
*
*/
#include "platform.h"
#include "usb_ch9.h"
#include "dwc_pcd.h"
#include "dwc_pcd_irq.h"
//#define flush_cpu_cache()
extern void do_gadget_setup( pcd_struct_t *_pcd, struct usb_ctrlrequest * ctrl);
extern void do_vendor_request( pcd_struct_t *_pcd, struct usb_ctrlrequest * ctrl);
extern void do_vendor_out_complete( pcd_struct_t *_pcd, struct usb_ctrlrequest * ctrl);
extern void do_bulk_complete( pcd_struct_t *_pcd);
extern void do_modify_memory(u16 opcode,char * inbuff);
static void ep0_out_start(void);
static int32_t ep0_complete_request( pcd_struct_t * pcd);
/**
* This function starts the Zero-Length Packet for the IN status phase
* of a 2 stage control transfer.
*/
static void do_setup_in_status_phase( pcd_struct_t *_pcd)
{
dwc_ep_t *ep0 = &g_dwc_eps[0];
if (_pcd->ep0state == EP0_STALL) {
return;
}
_pcd->ep0state = EP0_STATUS;
DBG( "EP0 IN ZLP\n");
ep0->xfer_len = 0;
ep0->xfer_count = 0;
ep0->is_in = 1;
dwc_otg_ep_start_transfer( ep0 );
/* Prepare for more SETUP Packets */
ep0_out_start();
}
/**
* This function starts the Zero-Length Packet for the OUT status phase
* of a 2 stage control transfer.
*/
static void do_setup_out_status_phase( pcd_struct_t *_pcd)
{
dwc_ep_t *ep0 = &g_dwc_eps[0];
if (_pcd->ep0state == EP0_STALL) {
return;
}
_pcd->ep0state = EP0_STATUS;
/* Prepare for more SETUP Packets */
//ep0_out_start( GET_CORE_IF(_pcd), _pcd );
DBG( "EP0 OUT ZLP\n");
ep0->xfer_len = 0;
ep0->xfer_count = 0;
ep0->is_in = 0;
dwc_otg_ep_start_transfer( ep0 );
/* Prepare for more SETUP Packets */
ep0_out_start( );
}
#if (defined AML_USB_BURN_TOOL)
static void pcd_out_completed(pcd_struct_t *_pcd)
{
if (_pcd->cmdtype.out_complete && _pcd->cmdtype.in_complete)
{
_pcd->cmdtype.setup_complete = _pcd->cmdtype.out_complete = _pcd->cmdtype.in_complete = 0;
do_vendor_out_complete(_pcd,(struct usb_ctrlrequest*)&_pcd->setup_pkt);
}
}
static void pcd_in_completed(pcd_struct_t *_pcd)
{
do_vendor_in_complete(_pcd,(struct usb_ctrlrequest*)&_pcd->setup_pkt);
}
#endif//#if (defined AML_USB_BURN_TOOL)
static void pcd_setup( pcd_struct_t *_pcd )
{
struct usb_ctrlrequest ctrl = _pcd->setup_pkt.req;
dwc_ep_t *ep0 = &g_dwc_eps[0];
/*deptsiz0_data_t doeptsize0 = { 0};*/
if (_pcd->request_enable == 0)
return;
// _pcd->setup_pkt.d32[0] = 0;
// _pcd->setup_pkt.d32[1] = 0;
_pcd->status = 0;
_pcd->request_enable = 0;
/*doeptsize0.d32 = dwc_read_reg32( DWC_REG_OUT_EP_TSIZE(0));*/
if (ctrl.bRequestType & USB_DIR_IN) {
ep0->is_in = 1;
_pcd->ep0state = EP0_IN_DATA_PHASE;
} else {
ep0->is_in = 0;
_pcd->ep0state = EP0_OUT_DATA_PHASE;
}
if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
{
/* handle non-standard (class/vendor) requests in the gadget driver */
//do_gadget_setup(_pcd, &ctrl );
DBG("Vendor requset\n");
#if (defined AML_USB_BURN_TOOL)
do_vendor_request(_pcd, &ctrl );
dwc_otg_ep_req_start(_pcd,0);
#endif
return;
}
/** @todo NGS: Handle bad setup packet? */
switch (ctrl.bRequest)
{
case USB_REQ_GET_STATUS:
_pcd->status = 0;
switch (ctrl.bRequestType & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
_pcd->status = 0; /* Default Bus Powered, no Remote wakeup */
//_pcd->status |= 0x1; /* Self powered */
//_pcd->status |= 0x2;//_pcd->remote_wakeup_enable << 1;
break;
case USB_RECIP_INTERFACE:
_pcd->status = 0;
break;
}
_pcd->buf = (char *)&_pcd->status;
_pcd->length = 2;
dwc_otg_ep_req_start(_pcd,0);
break;
#if 0
case USB_RECIP_INTERFACE:
*status = 0;
break;
case USB_RECIP_ENDPOINT:
ep = get_ep_by_addr(_pcd, ctrl.wIndex);
if ( ep == 0 || ctrl.wLength > 2) {
ep0_do_stall(_pcd, -EOPNOTSUPP);
return;
}
/** @todo check for EP stall */
*status = ep->stopped;
break;
_pcd->ep0_pending = 1;
ep0->dwc_ep.start_xfer_buff = (uint8_t *)status;
ep0->dwc_ep.xfer_buff = (uint8_t *)status;
ep0->dwc_ep.dma_addr = _pcd->status_buf_dma_handle;
ep0->dwc_ep.xfer_len = 2;
ep0->dwc_ep.xfer_count = 0;
ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len;
dwc_otg_ep0_start_transfer( GET_CORE_IF(_pcd), &ep0->dwc_ep );
break;
case USB_REQ_CLEAR_FEATURE:
do_clear_feature( _pcd );
break;
case USB_REQ_SET_FEATURE:
do_set_feature( _pcd );
break;
#endif
case USB_REQ_SET_ADDRESS:
if (ctrl.bRequestType == USB_RECIP_DEVICE)
{
dcfg_data_t dcfg = { 0 };
printf("Set Addr %d\n",ctrl.wValue);
dcfg.b.devaddr = ctrl.wValue;
dwc_modify_reg32(DWC_REG_DCFG,0, dcfg.d32);
do_setup_in_status_phase( _pcd );
return;
}
break;
case USB_REQ_SET_INTERFACE:
case USB_REQ_SET_CONFIGURATION:
_pcd->request_config = 1; /* Configuration changed */
do_gadget_setup(_pcd, &ctrl );
dwc_otg_ep_req_start(_pcd,0);
break;
default:
/* Call the Gadget Driver's setup functions */
do_gadget_setup(_pcd, &ctrl );
dwc_otg_ep_req_start(_pcd,0);
break;
}
}
/**
* This function handles EP0 Control transfers.
*
* The state of the control tranfers are tracked in
* <code>ep0state</code>.
* is_in : 1 -- IN Trans
* is_in : 0 -- OUT/SETUP Trans
*/
static void handle_ep0( int is_in )
{
pcd_struct_t * _pcd = &this_pcd[0];//Oh, this_pcd
dwc_ep_t * ep0 = &g_dwc_eps[0];
switch (_pcd->ep0state)
{
case EP0_DISCONNECT:
DBG("EP0 DISCONNECT\n");
break;
case EP0_IDLE:
_pcd->request_config = 0;
DBG("Enter PCD Setup()\n");
pcd_setup( _pcd );
break;
case EP0_IN_DATA_PHASE:
if (ep0->xfer_count < ep0->total_len) {
DBG("FIX ME!! dwc_otg_ep0_continue_transfer!\n");
//dwc_otg_ep0_continue_transfer ( GET_CORE_IF(_pcd), &ep0->dwc_ep );
}
else {
ep0_complete_request( _pcd );
_pcd->ep0last_state = 1;
}
break;
case EP0_OUT_DATA_PHASE:
ep0_complete_request(_pcd );
_pcd->cmdtype.in_complete = 1;
_pcd->ep0last_state = 2;
break;
case EP0_STATUS:
ep0_complete_request( _pcd );
_pcd->ep0state = EP0_IDLE;
ep0->stopped = 1;
ep0->is_in = 0; /* OUT for next SETUP */
if (_pcd->ep0last_state == 1) {
pcd_in_completed(_pcd);
} else if (_pcd->ep0last_state == 2) {
pcd_out_completed(_pcd);
}
_pcd->ep0last_state = 0;
break;
case EP0_STALL:
ERR("EP0 STALLed, should not get here pcd_setup()\n");
break;
}
return ;
}
/**
* This function completes the request for the 'BULK' EP. If there are
* additional requests for the EP in the queue they will be started.
*/
static void complete_ep( int ep_num,int is_in )
{
deptsiz_data_t deptsiz;
pcd_struct_t *pcd = &this_pcd[ep_num];
dwc_ep_t *ep = &g_dwc_eps[ep_num];
if (is_in)
{
pcd->xfer_len = ep->xfer_count;////////////////
deptsiz.d32 = dwc_read_reg32(DWC_REG_IN_EP_TSIZE(ep_num));
if (deptsiz.b.xfersize == 0 && deptsiz.b.pktcnt == 0 &&
ep->xfer_count == ep->xfer_len)
{
ep->start_xfer_buff = 0;
ep->xfer_buff = 0;
ep->xfer_len = 0;
}
}
else
{/* OUT Transfer */
deptsiz.d32 = dwc_read_reg32(DWC_REG_OUT_EP_TSIZE(ep_num));
pcd->xfer_len = ep->xfer_count;
ep->start_xfer_buff = 0;
ep->xfer_buff = 0;
ep->xfer_len = 0;
}
#if (defined AML_USB_BURN_TOOL)
do_bulk_complete(pcd);
#endif
}
/**
* This function completes the ep0 control transfer.
*/
static int32_t ep0_complete_request( pcd_struct_t * pcd)
{
deptsiz0_data_t deptsiz;
int is_last = 0;
dwc_ep_t* ep = &g_dwc_eps[0];
DBG("ep0_complete_request()\n");
if (pcd->ep0state == EP0_STATUS)
{
is_last = 1;
}
else if (ep->xfer_len == 0)
{
ep->xfer_len = 0;
ep->xfer_count = 0;
ep->sent_zlp = 1;
dwc_otg_ep_start_transfer( ep );
return 1;
}
else if (ep->is_in)
{
deptsiz.d32 = dwc_read_reg32(DWC_REG_IN_EP_TSIZE(0) );
if (deptsiz.b.xfersize == 0) {
/* Is a Zero Len Packet needed? */
do_setup_out_status_phase(pcd);
}
}
else {
/* ep0-OUT */
do_setup_in_status_phase(pcd);
}
/* Complete the request */
if (is_last) {
ep->start_xfer_buff = 0;
ep->xfer_buff = 0;
ep->xfer_len = 0;
return 1;
}
return 0;
}
/**
* This function reads a packet from the Rx FIFO into the destination
* buffer. To read SETUP data use dwc_otg_read_setup_packet.
*
* @param _dest Destination buffer for the packet.
* @param _bytes Number of bytes to copy to the destination.
*/
static void dwc_otg_read_packet(uint8_t *_dest, uint16_t _bytes) //Elvis Fool, add 'static'
{
int i;
/*const char* str = (char*)_dest;*/
uint32_t* pu32 = (uint32_t*)_dest;
const unsigned lenIn32 = (_bytes>>2);
const unsigned rest = (_bytes & 3);
/**
* @todo Account for the case where _dest is not dword aligned. This
* requires reading data from the FIFO into a uint32_t temp buffer,
* then moving it into the data buffer.
*/
//DBG("dwc_otg_read_packet() dest: %p, len: %d\n",_dest,_bytes);
for (i = 0; i < lenIn32; ++i)
{
*pu32++ = dwc_read_reg32(DWC_REG_DATA_FIFO_START);
}
if (rest)
{
const unsigned fifoVal = dwc_read_reg32(DWC_REG_DATA_FIFO_START);
uint8_t* pBufDst8 = (uint8_t*)pu32;
const uint8_t* pBufSrc8 = (const uint8_t*)&fifoVal;
for (i = 0; i < rest; ++i)
{
*pBufDst8++ = *pBufSrc8++;
}
}
/*
*if(!strcmp("power", str))
* printf("%d>", _bytes),printf(str),printf("\n");
*/
return;
}
/**
* This function writes a packet into the Tx FIFO associated with the
* EP. For non-periodic EPs the non-periodic Tx FIFO is written. For
* periodic EPs the periodic Tx FIFO associated with the EP is written
* with all packets for the next micro-frame.
*
* @param _core_if Programming view of DWC_otg controller.
* @param _ep The EP to write packet for.
* @param _dma Indicates if DMA is being used.
*/
void dwc_otg_ep_write_packet( dwc_ep_t *_ep)
{
/**
* The buffer is padded to DWORD on a per packet basis in
* slave/dma mode if the MPS is not DWORD aligned. The last
* packet, if short, is also padded to a multiple of DWORD.
*
* ep->xfer_buff always starts DWORD aligned in memory and is a
* multiple of DWORD in length
*
* ep->xfer_len can be any number of bytes
*
* ep->xfer_count is a multiple of ep->maxpacket until the last
* packet
*
* FIFO access is DWORD */
uint32_t i;
uint32_t byte_count;
uint32_t dword_count;
uint32_t fifo;
uint8_t *data_buff = _ep->xfer_buff;
uint32_t temp_data ;
//DBG("dwc_otg_ep_write_packet() : %d\n",_ep->xfer_len);
if (_ep->xfer_count >= _ep->xfer_len) {
//DWC_WARN("%s() No data for EP%d!!!\n", "dwc_otg_ep_write_packet", _ep->num);
return;
}
/* Find the byte length of the packet either short packet or MPS */
byte_count = _ep->xfer_len - _ep->xfer_count;
if (byte_count > _ep->maxpacket) byte_count = _ep->maxpacket;
/* Find the DWORD length, padded by extra bytes as neccessary if MPS
* is not a multiple of DWORD */
dword_count = (byte_count + 3) / 4;
//fifo = _core_if->data_fifo[_ep->num];
fifo = DWC_REG_DATA_FIFO(_ep->num);
for (i=0; i<dword_count; i++) {
temp_data =get_unaligned(data_buff);
dwc_write_reg32( fifo, temp_data );
data_buff += 4;
}
_ep->xfer_count += byte_count;
_ep->xfer_buff += byte_count;
flush_cpu_cache();
}
/**
* This function reads a setup packet from the Rx FIFO into the destination
* buffer. This function is called from the Rx Status Queue Level (RxStsQLvl)
* Interrupt routine when a SETUP packet has been received in Slave mode.
*
* @param _core_if Programming view of DWC_otg controller.
* @param _dest Destination buffer for packet data.
*/
void dwc_otg_read_setup_packet(uint32_t *_dest)
{
/* Get the 8 bytes of a setup transaction data */
DBG("dwc_otg_read_setup_packet()\n");
/* Pop 2 DWORDS off the receive data FIFO into memory */
_dest[0] = dwc_read_reg32(DWC_REG_DATA_FIFO_START);
_dest[1] = dwc_read_reg32(DWC_REG_DATA_FIFO_START);
}
/**
* handle the IN EP disable interrupt.
*/
static void handle_in_ep_disable_intr(uint32_t _epnum)
{
#if 0
deptsiz_data_t dieptsiz = { 0 };
dctl_data_t dctl = { 0 };
depctl_data_t diepctl = { 0 };
dwc_ep_t *ep = &g_dwc_eps[_epnum];
if (ep->stopped) {
/* Flush the Tx FIFO */
/** @todo NGS: This is not the correct FIFO */
dwc_otg_flush_tx_fifo( core_if, 0 );
/* Clear the Global IN NP NAK */
dctl.d32 = 0;
dctl.b.cgnpinnak = 1;
dwc_modify_reg32(&dev_if->in_ep_regs[_epnum]->diepctl,
diepctl.d32, diepctl.d32);
/* Restart the transaction */
if (dieptsiz.b.pktcnt != 0 ||
dieptsiz.b.xfersize != 0) {
restart_transfer( _pcd, _epnum );
}
}
#endif
}
/**
* Handler for the IN EP timeout handshake interrupt.
*/
static void handle_in_ep_timeout_intr(uint32_t _epnum)
{
dctl_data_t dctl = { 0 };
dwc_ep_t *ep = &g_dwc_eps[_epnum];
gintmsk_data_t intr_mask = {0};
/* Disable the NP Tx Fifo Empty Interrrupt */
intr_mask.b.nptxfempty = 1;
dwc_modify_reg32( DWC_REG_GINTMSK, intr_mask.d32, 0);
/** @todo NGS Check EP type.
* Implement for Periodic EPs */
/*
* Non-periodic EP
*/
/* Enable the Global IN NAK Effective Interrupt */
intr_mask.b.ginnakeff = 1;
dwc_modify_reg32( DWC_REG_GINTMSK, 0, intr_mask.d32);
/* Set Global IN NAK */
dctl.b.sgnpinnak = 1;
dwc_modify_reg32( DWC_REG_DCTL,dctl.d32, dctl.d32);
ep->stopped = 1;
}
///////////////////////////////////////////////////////////////////////
/**
* This function handles the Rx Status Queue Level Interrupt, which
* indicates that there is a least one packet in the Rx FIFO. The
* packets are moved from the FIFO to memory, where they will be
* processed when the Endpoint Interrupt Register indicates Transfer
* Complete or SETUP Phase Done.
*
* Repeat the following until the Rx Status Queue is empty:
* -# Read the Receive Status Pop Register (GRXSTSP) to get Packet
* info
* -# If Receive FIFO is empty then skip to step Clear the interrupt
* and exit
* -# If SETUP Packet call dwc_otg_read_setup_packet to copy the
* SETUP data to the buffer
* -# If OUT Data Packet call dwc_otg_read_packet to copy the data
* to the destination buffer
*/
int32_t dwc_otg_pcd_handle_rx_status_q_level_intr(void)
{
gintmsk_data_t gintmask = { 0 };
device_grxsts_data_t status;
gintsts_data_t gintsts;
dwc_ep_t *ep;
DBG("dwc_otg_pcd_handle_rx_status_q_level_intr()\n");
/* Disable the Rx Status Queue Level interrupt */
gintmask.b.rxstsqlvl= 1;
dwc_modify_reg32( DWC_REG_GINTMSK, gintmask.d32, 0);
/* Get the Status from the top of the FIFO */
status.d32 = dwc_read_reg32( DWC_REG_GRXSTSP);
//DBG("rx status: ep%d, pktsts: %d\n",status.b.epnum,status.b.pktsts);
/* Get pointer to EP structure */
ep = &g_dwc_eps[status.b.epnum];
switch (status.b.pktsts)
{
case DWC_DSTS_GOUT_NAK:
DBG( "Global OUT NAK\n");
break;
case DWC_STS_DATA_UPDT:
DBG( "OUT Data Packet\n");
{
if (status.b.bcnt && ep->xfer_buff)
{
/** @todo NGS Check for buffer overflow? */
dwc_otg_read_packet( ep->xfer_buff,status.b.bcnt);
ep->xfer_count += status.b.bcnt;
ep->xfer_buff += status.b.bcnt;
if (!status.b.epnum)
{
/*const char* p = ep->xfer_buff - status.b.bcnt;*/
this_pcd[0].cmdtype.out_complete = 1;
/*
*if(!strcmp("power", p) || !strcmp("low_power", p)){
* printf("%s, bcnt %d, cnt %d, %d\n", p, status.b.bcnt, ep->xfer_count, this_pcd.cmdtype.in_complete);
*}
*/
}
}
}
break;
case DWC_STS_XFER_COMP:
DBG("OUT Complete\n");
break;
case DWC_DSTS_SETUP_COMP:
DBG("SETUP Complete\n");
break;
case DWC_DSTS_SETUP_UPDT:
DBG("SETUP update\n");
{
static int _is_first_setup_out_in_cmd = 1;
dwc_otg_read_setup_packet( this_pcd[0].setup_pkt.d32);
this_pcd[0].request_enable = 1;
ep->xfer_count += status.b.bcnt;
DBG("set %d, %d\n", status.b.bcnt, ep->xfer_count);
if (_is_first_setup_out_in_cmd) //first tplcmd/bulkcmd that in sequence 'setup + out + in'
{
struct usb_ctrlrequest request = this_pcd[0].setup_pkt.req;
if ( USB_TYPE_VENDOR == (request.bRequestType & USB_TYPE_MASK) )
{
unsigned bRequest = request.bRequest;
if ((AM_REQ_WR_LARGE_MEM == bRequest) || (AM_REQ_RD_LARGE_MEM == bRequest)
||(AM_REQ_TPL_CMD == bRequest) || (AM_REQ_TPL_STAT == bRequest)
|| (AM_REQ_DOWNLOAD == bRequest) || (AM_REQ_BULKCMD == bRequest))
{
__udelay(20);//delay for first command that consisted of consecutive 'ep0 out', i.e. 'setup + out + in'
_is_first_setup_out_in_cmd = 0;
}
}
}
}
break;
default:
//DBG( "Invalid Packet Status (0x%0x)\n", status.b.pktsts);
break;
}
/* Enable the Rx Status Queue Level interrupt */
dwc_modify_reg32( DWC_REG_GINTMSK, 0, gintmask.d32);
/* Clear interrupt */
gintsts.d32 = 0;
gintsts.b.rxstsqlvl = 1;
dwc_write_reg32 ( DWC_REG_GINTSTS, gintsts.d32);
return 1;
}
/**
* This interrupt occurs when the non-periodic Tx FIFO is half-empty.
* The active request is checked for the next packet to be loaded into
* the non-periodic Tx FIFO.
*/
int32_t dwc_otg_pcd_handle_np_tx_fifo_empty_intr(void)
{
gnptxsts_data_t txstatus = {0};
gintsts_data_t gintsts;
int epnum = 0;
dwc_ep_t *ep = 0;
uint32_t len = 0;
int dwords;
depctl_data_t depctl;
DBG("dwc_otg_pcd_handle_np_tx_fifo_empty_intr()\n");
/* Get the epnum from the IN Token Learning Queue. */
for (epnum=0; epnum < NUM_EP; epnum++)
{
ep = &g_dwc_eps[epnum];
/* IN endpoint ? */
if (epnum && !ep->is_in ) {
continue;
}
depctl.d32 = dwc_read_reg32(DWC_REG_IN_EP_REG(epnum));
if (depctl.b.epena != 1)
continue;
if (ep->type == DWC_OTG_EP_TYPE_INTR && ep->xfer_len == 0)
continue;
flush_cpu_cache();
len = ep->xfer_len - ep->xfer_count;
if (len > ep->maxpacket) {
len = ep->maxpacket;
}
dwords = (len + 3)/4;
//DBG("nptx: write data to fifo, ep%d , size %d\n",epnum,len);
/* While there is space in the queue and space in the FIFO and
* More data to tranfer, Write packets to the Tx FIFO */
txstatus.d32 = dwc_read_reg32( DWC_REG_GNPTXSTS );
while (txstatus.b.nptxqspcavail > 0 &&
txstatus.b.nptxfspcavail > dwords &&
ep->xfer_count < ep->xfer_len) {
flush_cpu_cache();
/* Write the FIFO */
dwc_otg_ep_write_packet( ep );
len = ep->xfer_len - ep->xfer_count;
if (len > ep->maxpacket) {
len = ep->maxpacket;
}
dwords = (len + 3)/4;
flush_cpu_cache();
//txstatus.d32 = dwc_read_reg32(DWC_REG_GNPTXSTS);
#if 1
/*
TODO: Remove these code.
Because, if code break from "while"(Line427), an incomplete-in-trans will occour.
Then the tansfer will break.
*/
int retry = 50000; //retry times
while (retry--)
{
txstatus.d32 = dwc_read_reg32(DWC_REG_GNPTXSTS);
if(txstatus.b.nptxqspcavail > 0 || //txstatus.b.nptxfspcavail <= dwords ||
ep->xfer_count >= ep->xfer_len)
break;
else
{
flush_cpu_cache();
}
}
if (retry <= 0)
{
//DWC_ERROR("TxFIFO FULL: Can't trans data to HOST !\n");
}
/* END todo */
#endif
}
}
/* Clear interrupt */
gintsts.d32 = 0;
gintsts.b.nptxfempty = 1;
dwc_write_reg32 (DWC_REG_GINTSTS, gintsts.d32);
return 1;
}
/**
* Read the device status register and set the device speed in the
* data structure.
* Set up EP0 to receive SETUP packets by calling dwc_ep0_activate.
*/
int32_t dwc_otg_pcd_handle_enum_done_intr(void)
{
gintsts_data_t gintsts;
gusbcfg_data_t gusbcfg;
/*dsts_data_t dsts;*/
depctl_data_t diepctl;
depctl_data_t doepctl;
dctl_data_t dctl ={0};
#if (defined CONFIG_USB_DEVICE_V2)
depctl_data_t depctl;
#endif
DBG("SPEED ENUM\n");
#ifdef CONFIG_USB_DEVICE_V2
set_usb_phy21_tuning_update();
#endif
/* Read the Device Status and Endpoint 0 Control registers */
/*dsts.d32 = dwc_read_reg32(DWC_REG_DSTS);*/
diepctl.d32 = dwc_read_reg32(DWC_REG_IN_EP_REG(0) );
doepctl.d32 = dwc_read_reg32(DWC_REG_OUT_EP_REG(0));
/* Set the MPS of the IN EP based on the enumeration speed */
diepctl.b.mps = DWC_DEP0CTL_MPS_64;
dwc_write_reg32(DWC_REG_IN_EP_REG(0) , diepctl.d32);
#if (defined CONFIG_USB_DEVICE_V2)
depctl.d32 = dwc_read_reg32(DWC_REG_IN_EP_REG(1));
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(DWC_REG_IN_EP_REG(1), depctl.d32);
}
depctl.d32 = dwc_read_reg32(DWC_REG_OUT_EP_REG(2));
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(DWC_REG_OUT_EP_REG(2), depctl.d32);
}
#endif
/* Enable OUT EP for receive */
doepctl.b.epena = 1;
dwc_write_reg32(DWC_REG_OUT_EP_REG(0), doepctl.d32);
dctl.b.cgnpinnak = 1;
dwc_modify_reg32(DWC_REG_DCTL, dctl.d32, dctl.d32);
if (this_pcd[0].ep0state == EP0_DISCONNECT) {
this_pcd[0].ep0state = EP0_IDLE;
} else if (this_pcd[0].ep0state == EP0_STALL) {
this_pcd[0].ep0state = EP0_IDLE;
}
this_pcd[0].ep0state = EP0_IDLE;
/* Set USB turnaround time based on device speed and PHY interface. */
gusbcfg.d32 = dwc_read_reg32(DWC_REG_GUSBCFG);
#if 0
if (_pcd->gadget.speed == USB_SPEED_HIGH) {
if (GET_CORE_IF(_pcd)->hwcfg2.b.hs_phy_type == DWC_HWCFG2_HS_PHY_TYPE_ULPI) {
/* ULPI interface */
gusbcfg.b.usbtrdtim = 9;
}
if (GET_CORE_IF(_pcd)->hwcfg2.b.hs_phy_type == DWC_HWCFG2_HS_PHY_TYPE_UTMI) {
/* UTMI+ interface */
if (GET_CORE_IF(_pcd)->core_params->phy_utmi_width == 16) {
gusbcfg.b.usbtrdtim = 5;
} else {
gusbcfg.b.usbtrdtim = 9;
}
}
if (GET_CORE_IF(_pcd)->hwcfg2.b.hs_phy_type == DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI) {
/* UTMI+ OR ULPI interface */
if (gusbcfg.b.ulpi_utmi_sel == 1) {
/* ULPI interface */
gusbcfg.b.usbtrdtim = 9;
} else {
/* UTMI+ interface */
if (GET_CORE_IF(_pcd)->core_params->phy_utmi_width == 16) {
gusbcfg.b.usbtrdtim = 5;
} else {
gusbcfg.b.usbtrdtim = 9;
}
}
}
} else {
/* Full or low speed */
gusbcfg.b.usbtrdtim = 9;
}
#else
/* Full or low speed */
#if (defined CONFIG_USB_DEVICE_V2)
gusbcfg.b.usbtrdtim = 9;
#else
gusbcfg.b.usbtrdtim = 5;
#endif
#endif
dwc_write_reg32(DWC_REG_GUSBCFG, gusbcfg.d32);
/* Clear interrupt */
gintsts.d32 = 0;
gintsts.b.enumdone = 1;
dwc_write_reg32(DWC_REG_GINTSTS,gintsts.d32 );
return 1;
}
///////////////////////////////////////////////////////////////////
/**
* This interrupt indicates that an OUT EP has a pending Interrupt.
* The sequence for handling the OUT EP interrupt is shown below:
* -# Read the Device All Endpoint Interrupt register
* -# Repeat the following for each OUT EP interrupt bit set (from
* LSB to MSB).
* -# Read the Device Endpoint Interrupt (DOEPINTn) register
* -# If "Transfer Complete" call the request complete function
* -# If "Endpoint Disabled" complete the EP disable procedure.
* -# If "AHB Error Interrupt" log error
* -# If "Setup Phase Done" process Setup Packet (See Standard USB
* Command Processing)
*/
static int32_t dwc_otg_pcd_handle_out_ep_intr(void)
{
#define CLEAR_OUT_EP_INTR(__epnum,__intr) \
do { \
doepint_data_t doepint = { 0 }; \
doepint.b.__intr = 1; \
dwc_write_reg32(DWC_REG_OUT_EP_INTR(__epnum), \
doepint.d32); \
} while (0)
uint32_t ep_intr;
doepint_data_t doepint = { 0 };
uint32_t epnum = 0;
// uint32_t epnum_trans = 0;
gintsts_data_t gintsts;
DBG( "dwc_otg_pcd_handle_out_ep_intr()\n" );
/* Read in the device interrupt bits */
ep_intr = (dwc_read_reg32(DWC_REG_DAINT) &
dwc_read_reg32( DWC_REG_DAINTMSK));
ep_intr =( (ep_intr & 0xffff0000) >> 16);
/* Clear the OUTEPINT in GINTSTS */
gintsts.d32 = 0;
gintsts.b.outepintr = 1;
dwc_write_reg32 (DWC_REG_GINTSTS, gintsts.d32);
dwc_write_reg32(DWC_REG_DAINT, 0xFFFF0000 );
while ( ep_intr ) {
if (ep_intr&0x1) {
doepint.d32 = (dwc_read_reg32( DWC_REG_OUT_EP_INTR(epnum)) &
dwc_read_reg32(DWC_REG_DOEPMSK));
/* Transfer complete */
if ( doepint.b.xfercompl ) {
DBG("EP%d OUT Xfer Complete\n", epnum);
/* Clear the bit in DOEPINTn for this interrupt */
CLEAR_OUT_EP_INTR(epnum,xfercompl);
if (epnum == 0) {
handle_ep0( 0 );
} else {
complete_ep( epnum,0 );
}
}
/* Endpoint disable */
if ( doepint.b.epdisabled ) {
DBG("EP%d OUT disabled\n", epnum);
/* Clear the bit in DOEPINTn for this interrupt */
CLEAR_OUT_EP_INTR(epnum,epdisabled);
}
/* AHB Error */
if ( doepint.b.ahberr ) {
DBG("EP%d OUT AHB Error\n", epnum);
CLEAR_OUT_EP_INTR(epnum,ahberr);
}
/* Setup Phase Done (contorl EPs) */
if ( doepint.b.setup ) {
handle_ep0( 0 );
CLEAR_OUT_EP_INTR(epnum,setup);
}
}
epnum++;
ep_intr >>=1;
}
return 1;
#undef CLEAR_OUT_EP_INTR
}
/**
* This interrupt indicates that an IN EP has a pending Interrupt.
* The sequence for handling the IN EP interrupt is shown below:
* -# Read the Device All Endpoint Interrupt register
* -# Repeat the following for each IN EP interrupt bit set (from
* LSB to MSB).
* -# Read the Device Endpoint Interrupt (DIEPINTn) register
* -# If "Transfer Complete" call the request complete function
* -# If "Endpoint Disabled" complete the EP disable procedure.
* -# If "AHB Error Interrupt" log error
* -# If "Time-out Handshake" log error
* -# If "IN Token Received when TxFIFO Empty" write packet to Tx
* FIFO.
* -# If "IN Token EP Mismatch" (disable, this is handled by EP
* Mismatch Interrupt)
*/
static int32_t dwc_otg_pcd_handle_in_ep_intr(void)
{
#define CLEAR_IN_EP_INTR(__epnum,__intr) \
do { \
diepint_data_t diepint = { 0 }; \
diepint.b.__intr = 1; \
dwc_write_reg32(DWC_REG_IN_EP_INTR(__epnum), \
diepint.d32); \
} while (0)
diepint_data_t diepint = { 0 };
// depctl_data_t diepctl = { 0 };
uint32_t ep_intr;
uint32_t epnum = 0;
gintmsk_data_t intr_mask = {0};
gintsts_data_t gintsts;
DBG( "dwc_otg_pcd_handle_in_ep_intr()\n" );
/* Read in the device interrupt bits */
ep_intr = (dwc_read_reg32(DWC_REG_DAINT) &
dwc_read_reg32( DWC_REG_DAINTMSK));
ep_intr =( (ep_intr & 0xffff) );
/* Clear the INEPINT in GINTSTS */
/* Clear all the interrupt bits for all IN endpoints in DAINT */
gintsts.d32 = 0;
gintsts.b.inepint = 1;
dwc_write_reg32 (DWC_REG_GINTSTS, gintsts.d32);
dwc_write_reg32(DWC_REG_DAINT, 0xFFFF );
flush_cpu_cache();
/* Service the Device IN interrupts for each endpoint */
while ( ep_intr ) {
if (ep_intr&0x1) {
diepint.d32 = (dwc_read_reg32( DWC_REG_IN_EP_INTR(epnum)) &
dwc_read_reg32(DWC_REG_DAINTMSK));
/* Transfer complete */
if ( diepint.b.xfercompl ) {
/* Disable the NP Tx FIFO Empty
* Interrrupt */
intr_mask.b.nptxfempty = 1;
dwc_modify_reg32( DWC_REG_GINTMSK, intr_mask.d32, 0);
/* Clear the bit in DIEPINTn for this interrupt */
CLEAR_IN_EP_INTR(epnum,xfercompl);
/* Complete the transfer */
if (epnum == 0) {
handle_ep0( 0 );
} else {
complete_ep( epnum,1 );
}
}
/* Endpoint disable */
if ( diepint.b.epdisabled ) {
handle_in_ep_disable_intr( epnum );
/* Clear the bit in DIEPINTn for this interrupt */
CLEAR_IN_EP_INTR(epnum,epdisabled);
}
/* AHB Error */
if ( diepint.b.ahberr ) {
/* Clear the bit in DIEPINTn for this interrupt */
CLEAR_IN_EP_INTR(epnum,ahberr);
}
/* TimeOUT Handshake (non-ISOC IN EPs) */
if ( diepint.b.timeout ) {
handle_in_ep_timeout_intr( epnum );
CLEAR_IN_EP_INTR(epnum,timeout);
}
/** IN Token received with TxF Empty */
if (diepint.b.intktxfemp) {
DWN_MSG("diepint.b.intktxfemp\n");
#if 0
if (!ep->stopped && epnum != 0) {
diepmsk_data_t diepmsk = { 0};
diepmsk.b.intktxfemp = 1;
dwc_modify_reg32( &dev_if->dev_global_regs->diepmsk, diepmsk.d32, 0 );
start_next_request(ep);
}
#endif
CLEAR_IN_EP_INTR(epnum,intktxfemp);
}
/** IN Token Received with EP mismatch */
if (diepint.b.intknepmis) {
DWN_MSG("intknepmis ep%d\n", epnum);
CLEAR_IN_EP_INTR(epnum,intknepmis);
}
/** IN Endpoint NAK Effective */
if (diepint.b.inepnakeff) {
CLEAR_IN_EP_INTR(epnum,inepnakeff);
}
}
epnum++;
ep_intr >>=1;
}
return 1;
#undef CLEAR_IN_EP_INTR
}
/**
* This function configures EP0 to receive SETUP packets.
*
* @todo NGS: Update the comments from the HW FS.
*
* -# Program the following fields in the endpoint specific registers
* for Control OUT EP 0, in order to receive a setup packet
* - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back
* setup packets)
* - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back
* to back setup packets)
* - In DMA mode, DOEPDMA0 Register with a memory address to
* store any setup packets received
*
*/
static void ep0_out_start(void)
{
deptsiz0_data_t doeptsize0 = { 0};
depctl_data_t doepctl = { 0 };
doeptsize0.b.supcnt = 3;
doeptsize0.b.pktcnt = 1;
doeptsize0.b.xfersize = 8*3;
DBG("ep0_out_start()\n");
dwc_write_reg32( DWC_REG_OUT_EP_TSIZE(0),doeptsize0.d32 );
// EP enable
doepctl.d32 = dwc_read_reg32(DWC_REG_OUT_EP_REG(0));
doepctl.b.epena = 1;
doepctl.d32 = 0x80008000;
dwc_write_reg32(DWC_REG_OUT_EP_REG(0),doepctl.d32);
flush_cpu_cache();
}
/**
* This interrupt occurs when a USB Reset is detected. When the USB
* Reset Interrupt occurs the device state is set to DEFAULT and the
* EP0 state is set to IDLE.
* -# Set the NAK bit for all OUT endpoints (DOEPCTLn.SNAK = 1)
* -# Unmask the following interrupt bits
* - DAINTMSK.INEP0 = 1 (Control 0 IN endpoint)
* - DAINTMSK.OUTEP0 = 1 (Control 0 OUT endpoint)
* - DOEPMSK.SETUP = 1
* - DOEPMSK.XferCompl = 1
* - DIEPMSK.XferCompl = 1
* - DIEPMSK.TimeOut = 1
* -# Program the following fields in the endpoint specific registers
* for Control OUT EP 0, in order to receive a setup packet
* - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back
* setup packets)
* - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back
* to back setup packets)
* - In DMA mode, DOEPDMA0 Register with a memory address to
* store any setup packets received
* At this point, all the required initialization, except for enabling
* the control 0 OUT endpoint is done, for receiving SETUP packets.
*/
int32_t dwc_otg_pcd_handle_usb_reset_intr(void)
{
depctl_data_t doepctl = { 0};
daint_data_t daintmsk = { 0};
doepmsk_data_t doepmsk = { 0};
diepmsk_data_t diepmsk = { 0};
dcfg_data_t dcfg = { 0 };
depctl_data_t diepctl = { 0};
depctl_data_t diepctl_rd = { 0};
grstctl_t resetctl = { 0 };
dctl_data_t dctl = { 0 };
int i = 0;
gintsts_data_t gintsts;
DBG("\nUSB RESET\n");
/* Clear the Remote Wakeup Signalling */
dctl.b.rmtwkupsig = 1;
dwc_modify_reg32( DWC_REG_DCTL,dctl.d32, 0 );
/* Disable all active IN EPs */
diepctl.b.epdis = 1;
diepctl.b.snak = 1;
for (i=0; i < NUM_EP; i++) {
diepctl_rd.d32 = dwc_read_reg32(DWC_REG_IN_EP_REG(i));
if (diepctl_rd.b.epena) {
dwc_write_reg32(DWC_REG_IN_EP_REG(i),diepctl.d32 );
}
}
/* Set NAK for all OUT EPs */
doepctl.b.snak = 1;
for (i=0; i < NUM_EP; i++) {
dwc_write_reg32(DWC_REG_OUT_EP_REG(i), doepctl.d32 );
}
/* Flush the NP Tx FIFO */
dwc_otg_flush_tx_fifo( 0 );
/* Flush the Learning Queue */
resetctl.b.intknqflsh = 1;
dwc_write_reg32( DWC_REG_GRSTCTL, resetctl.d32);
daintmsk.b.inep0 = 1;
daintmsk.b.outep0 = 1;
dwc_write_reg32( DWC_REG_DAINTMSK, daintmsk.d32 );
doepmsk.b.setup = 1;
doepmsk.b.xfercompl = 1;
doepmsk.b.ahberr = 1;
doepmsk.b.epdisabled = 1;
dwc_write_reg32( DWC_REG_DOEPMSK, doepmsk.d32 );
diepmsk.b.xfercompl = 1;
diepmsk.b.timeout = 1;
diepmsk.b.epdisabled = 1;
diepmsk.b.ahberr = 1;
dwc_write_reg32( DWC_REG_DIEPMSK, diepmsk.d32 );
/* Reset Device Address */
dcfg.d32 = dwc_read_reg32( DWC_REG_DCFG);
dcfg.b.devaddr = 0;
dwc_write_reg32( DWC_REG_DCFG, dcfg.d32);
/* setup EP0 to receive SETUP packets */
ep0_out_start();
/* Clear interrupt */
gintsts.d32 = 0;
gintsts.b.usbreset = 1;
dwc_write_reg32 ( DWC_REG_GINTSTS, gintsts.d32);
flush_cpu_cache();
return 1;
}
///////////////////////////////////////////////////////////////////
int dwc_common_irq(void)
{
int ret = 0;
gotgint_data_t gotgint;
gotgint.d32 = dwc_read_reg32(DWC_REG_GOTGINT);
if (gotgint.d32 == 0)
return 0;
if (gotgint.b.sesreqsucstschng) {
ERR("Session Request Success Status Change\n");
}
if (gotgint.b.sesenddet) {
ERR("Session End Detected, Line Disconected\n");
#if (defined AML_USB_BURN_TOOL)
cb_4_dis_connect_intr();
#endif
}
dwc_write_reg32(DWC_REG_GOTGINT,gotgint.d32); // clear intr
return ret;
}
int dwc_pcd_irq(void)
{
gintsts_data_t gintr_status;
gintsts_data_t gintr_msk;
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)
return 0;
DBG("irq gintmsk: 0x%08x\n",gintr_msk.d32);
DBG("irq gintrsts: 0x%08x\n",gintr_status.d32);
gintr_status.d32 = gintr_status.d32 & gintr_msk.d32;
DBG("irq gintmsk & gintrsts = 0x%08x\n",gintr_status.d32);
if (gintr_status.b.sofintr)
{
#if (defined CONFIG_USB_DEVICE_V2)
curTime_sof = get_timer(0);
_sofintr = 1;
#endif
if (_sofintr_not_occur) {
DWN_MSG("sof\n");
_sofintr_not_occur = 0;
}
}
if (gintr_status.b.rxstsqlvl) {
dwc_otg_pcd_handle_rx_status_q_level_intr();
#if (defined AML_USB_BURN_TOOL)
pcd_out_completed(&this_pcd[0]);
#endif
}
if (gintr_status.b.nptxfempty) {
dwc_otg_pcd_handle_np_tx_fifo_empty_intr( );
}
if (gintr_status.b.usbreset) {
dwc_otg_pcd_handle_usb_reset_intr( );
}
if (gintr_status.b.enumdone) {
dwc_otg_pcd_handle_enum_done_intr();
}
if (gintr_status.b.epmismatch) {
//dwc_otg_pcd_handle_ep_mismatch_intr( core_if );
}
if (gintr_status.b.inepint) {
dwc_otg_pcd_handle_in_ep_intr();
}
if (gintr_status.b.outepintr) {
dwc_otg_pcd_handle_out_ep_intr( );
}
#if 0
if (gintr_status.b.otgintr)
{
gotgint_data_t gotgint;
gotgint.d32 = dwc_read_reg32(DWC_REG_GOTGINT);
if (gotgint.b.sesenddet)
{
printf("dis-connect-intr\n");
cb_4_dis_connect_intr();
}
}
#endif//#if 0
dwc_write_reg32(DWC_REG_GINTSTS,gintr_status.d32);
flush_cpu_cache();
return 0;
}