/* ========================================================================== * $File: //dwh/usb_iip/dev/software/otg_ipmate/linux/drivers/dwc_otg_pcd_intr.c $ * $Revision: #8 $ * $Date: 2006/04/21 $ * $Change: 631780 $ * * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless * otherwise expressly agreed to in writing between Synopsys and you. * * The Software IS NOT an item of Licensed Software or Licensed Product under * any End User Software License Agreement or Agreement for Licensed Product * with Synopsys or any supplement thereto. You are permitted to use and * redistribute this Software in source and binary forms, with or without * modification, provided that redistributions of source code must retain this * notice. You may not view, use, disclose, copy or distribute this file or * any information contained herein except pursuant to this license grant from * Synopsys. If you do not agree with this notice, including the disclaimer * below, then you are not authorized to use the Software. * * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * ========================================================================== */ #include #include "ifxusb_version.h" #ifdef DWC_IS_DEVICE #include #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) #include #endif #include "dwc_otg_driver.h" #include "dwc_otg_pcd.h" #if 0 //--3M struct{ uint32_t GOTGCTL ; uint32_t GOTGINT ; uint32_t GAHBCFG ; uint32_t GUSBCFG ; uint32_t GRSTCTL ; uint32_t GINTSTS ; uint32_t GINTMSK ; //uint32_t GRXSTSR ; uint32_t GRXFSIZ ; uint32_t GNPTXFSIZ ; uint32_t GNPTXSTS ; uint32_t GI2CCTL ; uint32_t GPVNDCTL ; uint32_t GGPIO ; uint32_t GUID ; uint32_t GSNPSID ; uint32_t GHWCFG1 ; uint32_t GHWCFG2 ; uint32_t GHWCFG3 ; uint32_t GHWCFG4 ; uint32_t HPTXFSIZ ; uint32_t DPTXFSIZ[5]; uint32_t DCFG; uint32_t DCTL; uint32_t DSTS; uint32_t DIEPMSK; uint32_t DOEPMSK; uint32_t DAINT; uint32_t DAINTMSK; uint32_t DTKNQR1; uint32_t DTKNQR2; uint32_t DVBUSID; uint32_t DVBUSPULSE; uint32_t DTKNQR3; uint32_t DTKNQR4; uint32_t DIEPCTL[15]; uint32_t DIEPINT[15]; uint32_t DIETSIZ[15]; uint32_t DIEPDMA[15]; uint32_t DOEPCTL[15]; uint32_t DOEPFN[15]; uint32_t DOEPINT[15]; uint32_t DOETSIZ[15]; uint32_t DOEPDMA[15]; }Psv; #endif // --3M //Make options //#define DEBUG //#define GADGET_UNALIGNED_BUFFER_ADJUST //#define GADGET_UNALIGNED_BUFFER_CHECK //Compiling options //#define DEBUG_EP0 //#define USE_INTERRUPT_TRANSFER_TIMES #define USING_TIMEOUT_FOR_OUT //#define NoPinNext #define USE_MISMATCH_COUNTDOWN 3 #ifdef USE_INTERRUPT_TRANSFER_TIMES static int interrupt_transfer_times = 0; #endif #ifdef USE_MISMATCH_COUNTDOWN static int mismatch_countdown = USE_MISMATCH_COUNTDOWN; #endif static int usb_reset = 0; /* request functions defined in "dwc_otg_pcd.c" */ extern void request_done( dwc_otg_pcd_ep_t *_ep, dwc_otg_pcd_request_t *_req, int _status); extern void request_nuke( dwc_otg_pcd_ep_t *_ep ); extern int DINDBG; int POPDBG; /** @file * This file contains the implementation of the PCD Interrupt handlers. * * The PCD handles the device interrupts. Many conditions can cause a * device interrupt. When an interrupt occurs, the device interrupt * service routine determines the cause of the interrupt and * dispatches handling to the appropriate function. These interrupt * handling functions are described below. * All interrupt registers are processed from LSB to MSB. */ /** * This function prints the ep0 state for debug purposes. */ static inline void print_ep0_state( dwc_otg_pcd_t *_pcd ) { //#ifdef DEBUG char str[40]; switch (_pcd->ep0state) { case EP0_DISCONNECT: strcpy(str, "EP0_DISCONNECT"); break; case EP0_IDLE: strcpy(str, "EP0_IDLE"); break; case EP0_IN_DATA_PHASE: strcpy(str, "EP0_IN_DATA_PHASE"); break; case EP0_OUT_DATA_PHASE: strcpy(str, "EP0_OUT_DATA_PHASE"); break; case EP0_IN_STATUS: strcpy(str,"EP0_IN_STATUS"); break; case EP0_OUT_STATUS: strcpy(str,"EP0_OUT_STATUS"); break; case EP0_STALL: strcpy(str,"EP0_STALL"); break; default: strcpy(str,"EP0_INVALID"); } DWC_DEBUGPL(DBG_ANY, "%s(%d)\n", str, _pcd->ep0state); //printk( "%s(%d)\n", str, _pcd->ep0state); //#endif } /** * This functions gets a pointer to an EP from the wIndex address * value of the control request. */ static dwc_otg_pcd_ep_t *get_ep_by_addr (dwc_otg_pcd_t *_pcd, u16 _wIndex) { dwc_otg_pcd_ep_t *ep; if ((_wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) return &_pcd->ep[0]; list_for_each_entry( ep, &_pcd->gadget.ep_list, ep.ep_list) { u8 bEndpointAddress; if (!ep->desc) continue; bEndpointAddress = ep->desc->bEndpointAddress; if ((_wIndex ^ bEndpointAddress) & USB_DIR_IN) continue; if ((_wIndex & 0x0f) == (bEndpointAddress & 0x0f)) return ep; } return NULL; } /** * This function checks the EP request queue, if the queue is not * empty the next request is started. */ void start_next_request( dwc_otg_pcd_ep_t *_ep ) { dwc_otg_pcd_request_t *req = 0; if (!list_empty(&_ep->queue)) { req = list_entry(_ep->queue.next, dwc_otg_pcd_request_t, queue); _ep->dwc_ep.start_xfer_buff=NULL; _ep->dwc_ep.xfer_len = 0; _ep->dwc_ep.xfer_count = 0; _ep->dwc_ep.sent_zlp = 0; _ep->dwc_ep.len_in_xfer = 0; #if defined(GADGET_UNALIGNED_BUFFER_ADJUST) if( _ep->dwc_ep.is_in) { _ep->using_aligned_tx_buf=0; if (req->req.length && (((unsigned long)req->req.buf) & 3)) { if(_ep->aligned_tx_buf && _ep->aligned_tx_buf_len && _ep->aligned_tx_buf_len < req->req.length) { usb_free_buf(_ep->aligned_tx_buf); _ep->aligned_tx_buf=NULL; _ep->aligned_tx_buf_len=0; } if(! _ep->aligned_tx_buf || !_ep->aligned_tx_buf_len) { _ep->aligned_tx_buf = usb_alloc_buf(req->req.length, 0); if(_ep->aligned_tx_buf) _ep->aligned_tx_buf_len = req->req.length; } if(_ep->aligned_tx_buf && _ep->aligned_tx_buf_len >= req->req.length) { _ep->dwc_ep.start_xfer_buff=_ep->aligned_tx_buf; memcpy(_ep->aligned_tx_buf, req->req.buf, req->req.length); _ep->using_aligned_tx_buf=1; } else DWC_WARN("%s():%d\n",__func__,__LINE__); } else _ep->dwc_ep.start_xfer_buff = req->req.buf; } else //is out { _ep->using_aligned_rx_buf=0; if (((unsigned long)req->req.buf) & 3) { if( _ep->aligned_rx_buf && _ep->aligned_rx_buf_len && req->req.length > _ep->aligned_rx_buf_len ) { usb_free_buf(_ep->aligned_rx_buf); _ep->aligned_rx_buf=NULL; _ep->aligned_rx_buf_len=0; } if(! _ep->aligned_rx_buf || !_ep->aligned_rx_buf_len) { if(req->req.length > _ep->dwc_ep.maxpacket) { _ep->aligned_rx_buf = usb_alloc_buf(req->req.length, 1); if(_ep->aligned_rx_buf) _ep->aligned_rx_buf_len = req->req.length } else { _ep->aligned_rx_buf = usb_alloc_buf(_ep->dwc_ep.maxpacket, 1); if(_ep->aligned_rx_buf) _ep->aligned_rx_buf_len = _ep->dwc_ep.maxpacket } } if(_ep->aligned_rx_buf) { _ep->dwc_ep.start_xfer_buff=_ep->aligned_rx_buf; _ep->using_aligned_rx_buf=1; } else DWC_WARN("%s():%d\n",__func__,__LINE__); } else _ep->dwc_ep.start_xfer_buff = req->req.buf; } #elif defined(GADGET_UNALIGNED_BUFFER_CHECK) if ( _ep->dwc_ep.is_in && req->req.length && (((unsigned long)req->req.buf) & 3)) DWC_WARN("UNALIGNED BUFFER in REQUEST\n"); else if ( !_ep->dwc_ep.is_in && (((unsigned long)req->req.buf) & 3)) DWC_WARN("UNALIGNED BUFFER in REQUEST\n"); else _ep->dwc_ep.start_xfer_buff = req->req.buf; #else _ep->dwc_ep.start_xfer_buff = req->req.buf; #endif /* Setup and start the Transfer */ if(_ep->dwc_ep.start_xfer_buff) { _ep->dwc_ep.xfer_buff = _ep->dwc_ep.start_xfer_buff; _ep->dwc_ep.len_in_xfer = 0; _ep->dwc_ep.xfer_count = 0; _ep->dwc_ep.sent_zlp = 0; _ep->dwc_ep.xfer_len = req->req.length; //if(_ep->dwc_ep.num==1) printk(KERN_INFO "%s() Size=%d\n",__func__,_ep->dwc_ep.xfer_len); dwc_otg_ep_start_transfer( GET_CORE_IF(_ep->pcd), &_ep->dwc_ep ); } } } /** * This function handles the SOF Interrupts. At this time the SOF * Interrupt is disabled. */ int32_t dwc_otg_pcd_handle_sof_intr(dwc_otg_pcd_t *_pcd) { dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd); gintsts_data_t gintsts; #if defined(VERBOSE) DWC_PRINT("SOF Interrupt Detected\n"); #endif /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.sofintr = 1; dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32); return 1; } #if 0 //-- 3M /** * 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 */ void dwc_otg_dump_Psv(dwc_otg_core_if_t *_core_if) { int i; volatile uint32_t *addr; DWC_PRINT("Core Global Registers\n"); addr=&_core_if->core_global_regs->gotgctl; DWC_PRINT("GOTGCTL 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GOTGCTL); addr=&_core_if->core_global_regs->gotgint; DWC_PRINT("GOTGINT 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GOTGINT); addr=&_core_if->core_global_regs->gahbcfg; DWC_PRINT("GAHBCFG 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GAHBCFG); addr=&_core_if->core_global_regs->gusbcfg; DWC_PRINT("GUSBCFG 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GUSBCFG); addr=&_core_if->core_global_regs->grstctl; DWC_PRINT("GRSTCTL 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GRSTCTL); addr=&_core_if->core_global_regs->gintsts; DWC_PRINT("GINTSTS 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GINTSTS); addr=&_core_if->core_global_regs->gintmsk; DWC_PRINT("GINTMSK 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GINTMSK); //addr=&_core_if->core_global_regs->grxstsr; //DWC_PRINT("GRXSTSR 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GRXSTSR); addr=&_core_if->core_global_regs->grxfsiz; DWC_PRINT("GRXFSIZ 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GRXFSIZ); addr=&_core_if->core_global_regs->gnptxfsiz; DWC_PRINT("GNPTXFSIZ 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GNPTXFSIZ); addr=&_core_if->core_global_regs->gnptxsts; DWC_PRINT("GNPTXSTS 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GNPTXSTS); addr=&_core_if->core_global_regs->gi2cctl; DWC_PRINT("GI2CCTL 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GI2CCTL); addr=&_core_if->core_global_regs->gpvndctl; DWC_PRINT("GPVNDCTL 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GPVNDCTL); addr=&_core_if->core_global_regs->ggpio; DWC_PRINT("GGPIO 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GGPIO); addr=&_core_if->core_global_regs->guid; DWC_PRINT("GUID 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GUID); addr=&_core_if->core_global_regs->gsnpsid; DWC_PRINT("GSNPSID 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GSNPSID); addr=&_core_if->core_global_regs->ghwcfg1; DWC_PRINT("GHWCFG1 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GHWCFG1); addr=&_core_if->core_global_regs->ghwcfg2; DWC_PRINT("GHWCFG2 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GHWCFG2); addr=&_core_if->core_global_regs->ghwcfg3; DWC_PRINT("GHWCFG3 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GHWCFG3); addr=&_core_if->core_global_regs->ghwcfg4; DWC_PRINT("GHWCFG4 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.GHWCFG4); addr=&_core_if->core_global_regs->hptxfsiz; DWC_PRINT("HPTXFSIZ 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.HPTXFSIZ); for (i=0; i< 5; i++) { addr=&_core_if->core_global_regs->dptxfsiz[i]; DWC_PRINT("DPTXFSIZ[%d] 0x%08X<=0x%08X\n",i,dwc_read_reg32(addr),Psv.DPTXFSIZ[i]); } DWC_PRINT("Device Global Registers\n"); addr=&_core_if->dev_if->dev_global_regs->dcfg; DWC_PRINT("DCFG 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DCFG ); addr=&_core_if->dev_if->dev_global_regs->dctl; DWC_PRINT("DCTL 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DCTL); addr=&_core_if->dev_if->dev_global_regs->dsts; DWC_PRINT("DSTS 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DSTS ); addr=&_core_if->dev_if->dev_global_regs->diepmsk; DWC_PRINT("DIEPMSK 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DIEPMSK); addr=&_core_if->dev_if->dev_global_regs->doepmsk; DWC_PRINT("DOEPMSK 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DOEPMSK); addr=&_core_if->dev_if->dev_global_regs->daint; DWC_PRINT("DAINT 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DAINT); addr=&_core_if->dev_if->dev_global_regs->daintmsk; DWC_PRINT("DAINTMSK 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DAINTMSK); addr=&_core_if->dev_if->dev_global_regs->dtknqr1; DWC_PRINT("DTKNQR1 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DTKNQR1); if (_core_if->hwcfg2.b.dev_token_q_depth > 6) { addr=&_core_if->dev_if->dev_global_regs->dtknqr2; DWC_PRINT("DTKNQR2 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DTKNQR2); } addr=&_core_if->dev_if->dev_global_regs->dvbusdis; DWC_PRINT("DVBUSID 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DVBUSID); addr=&_core_if->dev_if->dev_global_regs->dvbuspulse; DWC_PRINT("DVBUSPULSE 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DVBUSPULSE); if (_core_if->hwcfg2.b.dev_token_q_depth > 14) { addr=&_core_if->dev_if->dev_global_regs->dtknqr3; DWC_PRINT("DTKNQR3 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DTKNQR3); } if (_core_if->hwcfg2.b.dev_token_q_depth > 22) { addr=&_core_if->dev_if->dev_global_regs->dtknqr4; DWC_PRINT("DTKNQR4 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DTKNQR4); } for (i=0; i< 10; i++) { DWC_PRINT("Device IN EP %d Registers\n",i,dwc_read_reg32(addr), i); addr=&_core_if->dev_if->in_ep_regs[i]->diepctl; DWC_PRINT("DIEPCTL 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DIEPCTL[i]); addr=&_core_if->dev_if->in_ep_regs[i]->diepint; DWC_PRINT("DIEPINT 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DIEPINT[i]); addr=&_core_if->dev_if->in_ep_regs[i]->dieptsiz; DWC_PRINT("DIETSIZ 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DIETSIZ[i]); addr=&_core_if->dev_if->in_ep_regs[i]->diepdma; DWC_PRINT("DIEPDMA 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DIEPDMA[i]); DWC_PRINT("Device OUT EP %d Registers\n",i,dwc_read_reg32(addr), i); addr=&_core_if->dev_if->out_ep_regs[i]->doepctl; DWC_PRINT("DOEPCTL 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DOEPCTL[i]); addr=&_core_if->dev_if->out_ep_regs[i]->doepfn; DWC_PRINT("DOEPFN 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DOEPFN[i]); addr=&_core_if->dev_if->out_ep_regs[i]->doepint; DWC_PRINT("DOEPINT 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DOEPINT[i]); addr=&_core_if->dev_if->out_ep_regs[i]->doeptsiz; DWC_PRINT("DOETSIZ 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DOETSIZ[i]); addr=&_core_if->dev_if->out_ep_regs[i]->doepdma; DWC_PRINT("DOEPDMA 0x%08X<=0x%08X\n",dwc_read_reg32(addr),Psv.DOEPDMA[i]); } return; } void rx_timeoutfunc(dwc_otg_pcd_t *_pcd) { dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd); dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; gintsts_data_t gintsts; gintmsk_data_t gintmask = {.d32=0}; int isinvalid=0; depctl_data_t doepctl; device_grxsts_data_t status; int doinvalid=0; /* Disable the Rx Status Queue Level interrupt */ gintmask.b.rxstsqlvl= 1; dwc_modify_reg32( &global_regs->gintmsk, gintmask.d32, 0); // return 1; //printk(KERN_INFO "%s()\n",__func__); { deptsiz0_data_t sz0; deptsiz_data_t sz; printk(KERN_INFO "GINTSTS %08x\n",dwc_read_reg32 (&global_regs->gintsts)); doepctl.d32=dwc_read_reg32( &core_if->dev_if->out_ep_regs[0]->doepctl); printk(KERN_INFO "EP0CTL en:%d/%d/%d naksts:%d\n" ,doepctl.b.usbactep ,doepctl.b.epena ,doepctl.b.epdis ,doepctl.b.naksts ); sz0.d32=dwc_read_reg32( &core_if->dev_if->out_ep_regs[0]->doeptsiz); printk(KERN_INFO " sz0.b.supcnt=%d sz0.b.pktcnt=%d sz0.b.xfersize=%d dma=%08x int=%08x\n", sz0.b.supcnt,sz0.b.pktcnt,sz0.b.xfersize, dwc_read_reg32(&core_if->dev_if->out_ep_regs[0]->doepdma), dwc_read_reg32(&core_if->dev_if->out_ep_regs[0]->doepint)); printk(KERN_INFO " EP0 sw buf %08x/%08x len %d/%d pkt %d/%d\n" ,_pcd->ep[0].dwc_ep.start_xfer_buff ,_pcd->ep[0].dwc_ep.xfer_buff ,_pcd->ep[0].dwc_ep.len_in_xfer ,_pcd->ep[0].dwc_ep.xfer_len ,_pcd->ep[0].dwc_ep.xfer_count ,_pcd->ep[0].dwc_ep.packet_in_xfer ); doepctl.d32=dwc_read_reg32( &core_if->dev_if->out_ep_regs[2]->doepctl); printk(KERN_INFO "EP2CTL en:%d/%d/%d naksts:%d\n" ,doepctl.b.usbactep ,doepctl.b.epena ,doepctl.b.epdis,doepctl.b.naksts ); sz.d32=dwc_read_reg32( &core_if->dev_if->out_ep_regs[2]->doeptsiz); printk(KERN_INFO " sz.b.pktcnt=%d sz.b.xfersize=%d dma=%08x int=%08x\n", sz.b.pktcnt,sz.b.xfersize, dwc_read_reg32(&core_if->dev_if->out_ep_regs[2]->doepdma), dwc_read_reg32(&core_if->dev_if->out_ep_regs[2]->doepint)); printk(KERN_INFO " EP2 sw buf %08x/%08x len %d/%d pkt %d/%d\n" ,_pcd->ep[2].dwc_ep.start_xfer_buff ,_pcd->ep[2].dwc_ep.xfer_buff ,_pcd->ep[2].dwc_ep.len_in_xfer ,_pcd->ep[2].dwc_ep.xfer_len ,_pcd->ep[2].dwc_ep.packet_in_xfer ,_pcd->ep[2].dwc_ep.xfer_count ); } dwc_otg_dump_Psv(core_if); } #endif //--3M int32_t dwc_otg_pcd_handle_rx_status_q_level_intr(dwc_otg_pcd_t *_pcd) { dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd); dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; gintsts_data_t gintsts; #ifdef USE_INTERNAL_DMA gintmsk_data_t gintmask = {.d32=0}; // int isinvalid=0; // depctl_data_t doepctl; device_grxsts_data_t status; // int doinvalid=0; /* Disable the Rx Status Queue Level interrupt */ gintmask.b.rxstsqlvl= 1; dwc_modify_reg32( &global_regs->gintmsk, gintmask.d32, 0); // DMA SWITCH { gahbcfg_data_t gahbcfg = {.d32=0}; gahbcfg.b.dmaenable=1; dwc_modify_reg32(&global_regs->gintsts, gahbcfg.d32,0); status.d32 = dwc_read_reg32( &global_regs->grxstsr); printk(KERN_INFO "%s()%d GRXSTSR %08x, reserv:%02x fx:%01x pksts:%01x dpid:%1x bcnt:%03x epnum:%01x\n", __func__,__LINE__,status.d32, status.b.reserved,status.b.fn,status.b.pktsts,status.b.dpid,status.b.bcnt,status.b.epnum); MDELAY(5); dwc_modify_reg32(&global_regs->gintsts, 0,gahbcfg.d32); } gintsts.d32 = 0; gintsts.b.rxstsqlvl = 1; dwc_write_reg32 (&global_regs->gintsts, gintsts.d32); #ifdef VERBOSE DWC_PRINT("Rx Queue Level Interrupt Detected\n"); #endif //VERBOSE #else //USE_INTERNAL_DMA device_grxsts_data_t status; dwc_otg_pcd_ep_t *ep; #ifdef DEBUG static char *dpid_str[] ={ "D0", "D2", "D1", "MDATA" }; #endif //DEBUG //DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _pcd); /* Disable the Rx Status Queue Level interrupt */ gintmask.b.rxstsqlvl= 1; dwc_modify_reg32( &global_regs->gintmsk, gintmask.d32, 0); /* Get the Status from the top of the FIFO */ status.d32 = dwc_read_reg32( &global_regs->grxstsp ); DWC_DEBUGPL(DBG_PCD, "EP:%d BCnt:%d DPID:%s " "pktsts:%x Frame:%d(0x%0x)\n", status.b.epnum, status.b.bcnt, dpid_str[status.b.dpid], status.b.pktsts, status.b.fn, status.b.fn); /* Get pointer to EP structure */ ep = &_pcd->ep[ status.b.epnum ]; switch (status.b.pktsts) { case DWC_DSTS_GOUT_NAK: DWC_DEBUGPL(DBG_PCDV, "Global OUT NAK\n"); break; case DWC_STS_DATA_UPDT: DWC_DEBUGPL(DBG_PCDV, "OUT Data Packet\n"); // --3M #ifndef USE_INTERNAL_DMA if (status.b.bcnt && ep->dwc_ep.xfer_buff) { /** @todo NGS Check for buffer overflow? */ dwc_otg_read_packet( core_if, ep->dwc_ep.xfer_buff, status.b.bcnt); ep->dwc_ep.xfer_count += status.b.bcnt; ep->dwc_ep.xfer_buff += status.b.bcnt; } // --3M #endif break; case DWC_STS_XFER_COMP: DWC_DEBUGPL(DBG_PCDV, "OUT Complete\n"); break; case DWC_DSTS_SETUP_COMP: #ifdef DEBUG_EP0 DWC_DEBUGPL(DBG_PCDV, "Setup Complete\n"); #endif //DEBUG_EP0 break; case DWC_DSTS_SETUP_UPDT: dwc_otg_read_setup_packet( core_if, _pcd->setup_pkt->d32); #ifdef DEBUG_EP0 DWC_DEBUGPL(DBG_PCD, "SETUP PKT: %02x.%02x v%04x i%04x l%04x\n", _pcd->setup_pkt->req.bRequestType, _pcd->setup_pkt->req.bRequest, _pcd->setup_pkt->req.wValue, _pcd->setup_pkt->req.wIndex, _pcd->setup_pkt->req.wLength); #endif //DEBUG_EP0 ep->dwc_ep.xfer_count += status.b.bcnt; break; default: DWC_DEBUGPL(DBG_PCDV, "Invalid Packet Status (0x%0x)\n", status.b.pktsts); break; } /* Enable the Rx Status Queue Level interrupt */ dwc_modify_reg32( &global_regs->gintmsk, 0, gintmask.d32); #endif //USE_INTERNAL_DMA /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.rxstsqlvl = 1; dwc_write_reg32 (&global_regs->gintsts, gintsts.d32); //DWC_DEBUGPL(DBG_PCDV, "EXIT: %s\n", __func__); return 1; } /** * This function examines the Device IN Token Learning Queue to * determine the EP number of the last IN token received. This * implementation is for the Mass Storage device where there are only * 2 IN EPs (Control-IN and BULK-IN). * * The EP numbers for the first six IN Tokens are in DTKNQR1 and there * are 8 EP Numbers in each of the other possible DTKNQ Registers. * * @param _core_if Programming view of DWC_otg controller. * */ #ifndef USE_INTERNAL_DMA static inline int get_ep_of_last_in_token(dwc_otg_core_if_t *_core_if) { dwc_otg_device_global_regs_t *dev_global_regs = _core_if->dev_if->dev_global_regs; const uint32_t TOKEN_Q_DEPTH = _core_if->hwcfg2.b.dev_token_q_depth; /* Number of Token Queue Registers */ const int DTKNQ_REG_CNT = (TOKEN_Q_DEPTH + 7) / 8; dtknq1_data_t dtknqr1; uint32_t in_tkn_epnums[4]; int ndx = 0; int i = 0; volatile uint32_t *addr = &dev_global_regs->dtknqr1; int epnum = 0; //DWC_DEBUGPL(DBG_PCD,"dev_token_q_depth=%d\n",TOKEN_Q_DEPTH); /* Read the DTKNQ Registers */ for (i = 0; i < DTKNQ_REG_CNT; i++) { in_tkn_epnums[i] = dwc_read_reg32(addr); DWC_DEBUGPL(DBG_PCDV, "DTKNQR%d=0x%08x\n", i+1, in_tkn_epnums[i]); if (addr == &dev_global_regs->dvbusdis) addr = &dev_global_regs->dtknqr3; else ++addr; } /* Copy the DTKNQR1 data to the bit field. */ dtknqr1.d32 = in_tkn_epnums[0]; /* Get the EP numbers */ in_tkn_epnums[0] = dtknqr1.b.epnums0_5; ndx = dtknqr1.b.intknwptr - 1; //DWC_DEBUGPL(DBG_PCDV,"ndx=%d\n",ndx); if (ndx == -1) { /** @todo Find a simpler way to calculate the max * queue position.*/ int cnt = TOKEN_Q_DEPTH; if (TOKEN_Q_DEPTH <= 6) cnt = TOKEN_Q_DEPTH - 1; else if (TOKEN_Q_DEPTH <= 14) cnt = TOKEN_Q_DEPTH - 7; else if (TOKEN_Q_DEPTH <= 22) cnt = TOKEN_Q_DEPTH - 15; else cnt = TOKEN_Q_DEPTH - 23; epnum = (in_tkn_epnums[ DTKNQ_REG_CNT - 1 ] >> (cnt * 4)) & 0xF; } else { if (ndx <= 5) { epnum = (in_tkn_epnums[0] >> (ndx * 4)) & 0xF; } else if (ndx <= 13 ) { ndx -= 6; epnum = (in_tkn_epnums[1] >> (ndx * 4)) & 0xF; } else if (ndx <= 21 ) { ndx -= 14; epnum = (in_tkn_epnums[2] >> (ndx * 4)) & 0xF; } else if (ndx <= 29 ) { ndx -= 22; epnum = (in_tkn_epnums[3] >> (ndx * 4)) & 0xF; } } //DWC_DEBUGPL(DBG_PCD,"epnum=%d\n",epnum); return epnum; } #endif //USE_INTERNAL_DMA /** * 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(dwc_otg_pcd_t *_pcd) { dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd); dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; gintsts_data_t gintsts; #ifdef USE_INTERNAL_DMA gintmsk_data_t gintmask = {.d32=0}; /* Disable the NP Tx Empty interrupt */ gintmask.b.nptxfempty= 1; dwc_modify_reg32( &global_regs->gintmsk, gintmask.d32, 0); #ifdef VERBOSE DWC_PRINT("NP Tx Empty Interrupt Detected\n"); #endif #else //USE_INTERNAL_DMA dwc_otg_dev_in_ep_regs_t *ep_regs; gnptxsts_data_t txstatus = {.d32 = 0}; int epnum = 0; dwc_otg_pcd_ep_t *ep = 0; uint32_t len = 0; int dwords; /* Get the epnum from the IN Token Learning Queue. */ epnum = get_ep_of_last_in_token(core_if); ep = &_pcd->ep[epnum]; DWC_DEBUGPL(DBG_PCD, "NP TxFifo Empty: %s(%d) \n", ep->ep.name, epnum ); ep_regs = core_if->dev_if->in_ep_regs[epnum]; len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count; if (len > ep->dwc_ep.maxpacket) len = ep->dwc_ep.maxpacket; dwords = (len + 3)/4; /* 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( &global_regs->gnptxsts ); DWC_DEBUGPL(DBG_PCDV, "b4 GNPTXSTS=0x%08x\n",txstatus.d32); while (txstatus.b.nptxqspcavail > 0 && txstatus.b.nptxfspcavail > dwords && ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len) { /* Write the FIFO */ dwc_otg_ep_write_packet( core_if, &ep->dwc_ep ); len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count; if (len > ep->dwc_ep.maxpacket) len = ep->dwc_ep.maxpacket; dwords = (len + 3)/4; txstatus.d32 = dwc_read_reg32(&global_regs->gnptxsts); DWC_DEBUGPL(DBG_PCDV,"GNPTXSTS=0x%08x\n",txstatus.d32); } DWC_DEBUGPL(DBG_PCDV, "GNPTXSTS=0x%08x\n", dwc_read_reg32( &global_regs->gnptxsts)); #endif //USE_INTERNAL_DMA /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.nptxfempty = 1; dwc_write_reg32 (&global_regs->gintsts, gintsts.d32); return 1; } /** * This function is called when the Device is disconnected. It stops * any active requests and informs the Gadget driver of the * disconnect. */ void dwc_otg_pcd_stop(dwc_otg_pcd_t *_pcd) { int i; gintmsk_data_t intr_mask = {.d32 = 0}; DWC_DEBUGPL(DBG_PCDV, "%s() \n", __func__ ); /* don't disconnect drivers more than once */ if (_pcd->ep0state == EP0_DISCONNECT) { DWC_DEBUGPL(DBG_ANY, "%s() Already Disconnected\n", __func__ ); return; } _pcd->ep0state = EP0_DISCONNECT; /* Disable the NP Tx Fifo Empty Interrupt. */ intr_mask.b.nptxfempty = 1; dwc_modify_reg32(&GET_CORE_IF(_pcd)->core_global_regs->gintmsk, intr_mask.d32, 0); /* Flush the FIFOs */ dwc_otg_flush_tx_fifo( GET_CORE_IF(_pcd), 0x10); dwc_otg_flush_rx_fifo( GET_CORE_IF(_pcd) ); /* prevent new request submissions, kill any outstanding requests */ for (i = 0; i < _pcd->num_eps; i++) { dwc_otg_pcd_ep_t *ep = &_pcd->ep[i]; request_nuke(ep); #if defined(GADGET_UNALIGNED_BUFFER_ADJUST) ep->using_aligned_tx_buf=0; ep->using_aligned_rx_buf=0; if(ep->aligned_tx_buf) usb_free_buf(ep->aligned_tx_buf); if(ep->aligned_rx_buf) usb_free_buf(ep->aligned_rx_buf); ep->aligned_tx_buf=NULL; ep->aligned_tx_buf_len=0 ep->aligned_rx_buf=NULL; ep->aligned_rx_buf_len=0; #endif } /* report disconnect; the driver is already quiesced */ if (_pcd->driver && _pcd->driver->disconnect) { SPIN_UNLOCK(&_pcd->lock); _pcd->driver->disconnect(&_pcd->gadget); SPIN_LOCK(&_pcd->lock); } } /** * This interrupt indicates that ... */ int32_t dwc_otg_pcd_handle_i2c_intr(dwc_otg_pcd_t *_pcd) { gintmsk_data_t intr_mask = { .d32 = 0}; gintsts_data_t gintsts; #if 0 DWC_PRINT("INTERRUPT Handler not implemented for %s\n", "i2cintr"); intr_mask.b.i2cintr = 1; dwc_modify_reg32( &GET_CORE_IF(_pcd)->core_global_regs->gintmsk, intr_mask.d32, 0 ); /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.i2cintr = 1; dwc_write_reg32 (&GET_CORE_IF(_pcd)->core_global_regs->gintsts, gintsts.d32); #endif intr_mask.b.enumdone = 1; dwc_modify_reg32( &GET_CORE_IF(_pcd)->core_global_regs->gintmsk, intr_mask.d32, 0 ); /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.enumdone = 1; dwc_write_reg32 (&GET_CORE_IF(_pcd)->core_global_regs->gintsts, gintsts.d32); return 1; } /** * This interrupt indicates that ... */ int32_t dwc_otg_pcd_handle_early_suspend_intr(dwc_otg_pcd_t *_pcd) { gintsts_data_t gintsts; #if defined(VERBOSE) DWC_PRINT("Early Suspend Detected\n"); #endif /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.erlysuspend = 1; dwc_write_reg32( &GET_CORE_IF(_pcd)->core_global_regs->gintsts, gintsts.d32); return 1; } /** * This function configures EPO 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 * * @param _core_if Programming view of DWC_otg controller. * @param _pcd Programming view of the PCD. */ void ep0_out_start( dwc_otg_core_if_t *_core_if, dwc_otg_pcd_t *_pcd ) { dwc_otg_dev_if_t *dev_if = _core_if->dev_if; deptsiz0_data_t doeptsize0 = { .d32 = 0}; // dcfg_data_t dcfg; #ifdef VERBOSE DWC_DEBUGPL(DBG_PCDV,"%s() doepctl0=%0x\n", __func__, dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl)); #endif #if 0 { doepint_data_t doepint = {.d32=0}; depctl_data_t doepctl = { .d32 = 0 }; doepint.d32=dwc_read_reg32(&dev_if->out_ep_regs[0]->doepint); doepctl.d32 = dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl); if(doepint.b.outtknepdis && !doepctl.b.naksts) printk(KERN_INFO "%s() OUTTKNEPDIS doepint=%08x nak=%d\n", __func__, doepint.d32,doepctl.b.naksts); } #endif { doepint_data_t doepint = {.d32=0}; doepint.b.outtknepdis = 1; dwc_write_reg32(&dev_if->out_ep_regs[0]->doepint, doepint.d32); } doeptsize0.d32 = dwc_read_reg32( &dev_if->out_ep_regs[0]->doeptsiz); //doeptsize0.b.supcnt = 3; doeptsize0.b.pktcnt = 1; doeptsize0.b.xfersize = 8*3; dwc_write_reg32( &dev_if->out_ep_regs[0]->doeptsiz, doeptsize0.d32 ); #ifdef USE_INTERNAL_DMA { depctl_data_t doepctl = { .d32 = 0 }; /** @todo dma needs to handle multiple setup packets (up to 3) */ { int i; uint32_t *adr; _pcd->setup_pkt->d32[0]=0; _pcd->setup_pkt->d32[1]=0; adr= (uint32_t *)_pcd->setup_pkt_buf; for(i=0;i<2*10;i++,adr++) *adr=0; } if((uint32_t)(_pcd->setup_pkt) != (uint32_t)(KSEG1ADDR(_pcd->setup_pkt))) dma_cache_wback_inv((unsigned long) _pcd->setup_pkt_buf, doeptsize0.b.xfersize); dwc_write_reg32(&dev_if->out_ep_regs[0]->doepdma, CPHYSADDR(_pcd->setup_pkt_buf)); // EP enable doepctl.d32 = dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl); doepctl.b.epena = 1; doepctl.b.epdis = 0; doepctl.b.snak = 1; doepctl.b.cnak = 0; doepctl.b.usbactep = 1; dwc_write_reg32(&dev_if->out_ep_regs[0]->doepctl, doepctl.d32); } #endif #ifdef VERBOSE DWC_DEBUGPL(DBG_PCDV,"doepctl0=%0x\n", dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl)); DWC_DEBUGPL(DBG_PCDV,"diepctl0=%0x\n", dwc_read_reg32(&dev_if->in_ep_regs[0]->diepctl)); #endif } /** * 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( dwc_otg_pcd_t * _pcd) { dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd); dwc_otg_dev_if_t *dev_if = core_if->dev_if; depctl_data_t doepctl = { .d32 = 0}; daint_data_t daintmsk = { .d32 = 0}; doepmsk_data_t doepmsk = { .d32 = 0}; diepmsk_data_t diepmsk = { .d32 = 0}; dcfg_data_t dcfg = { .d32=0 }; grstctl_t resetctl = { .d32=0 }; dctl_data_t dctl = {.d32=0}; int i = 0; gintsts_data_t gintsts; DWC_PRINT("USB RESET RECEIVED!\n"); #ifdef USE_INTERRUPT_TRANSFER_TIMES interrupt_transfer_times = 3; #endif #ifdef USE_MISMATCH_COUNTDOWN mismatch_countdown = USE_MISMATCH_COUNTDOWN; #endif /* Clear the Remote Wakeup Signalling */ dctl.b.rmtwkupsig = 1; dwc_modify_reg32( &core_if->dev_if->dev_global_regs->dctl, dctl.d32, 0 ); /* Set NAK for all OUT EPs */ doepctl.b.snak = 1; for (i=0; i < dev_if->num_eps; i++) dwc_write_reg32( &dev_if->out_ep_regs[i]->doepctl, doepctl.d32 ); /* Flush the NP Tx FIFO */ dwc_otg_flush_tx_fifo( core_if, 0x10 ); /* Flush the Learning Queue */ resetctl.b.intknqflsh = 1; dwc_write_reg32( &core_if->core_global_regs->grstctl, resetctl.d32); daintmsk.b.inep0 = 1; daintmsk.b.outep0 = 1; dwc_write_reg32( &dev_if->dev_global_regs->daintmsk, daintmsk.d32 ); doepmsk.b.setup = 1; doepmsk.b.xfercompl = 1; doepmsk.b.ahberr = 1; doepmsk.b.epdisabled = 1; // doepmsk.b.outtknepdis = 1; dwc_write_reg32( &dev_if->dev_global_regs->doepmsk, doepmsk.d32 ); diepmsk.b.xfercompl = 1; diepmsk.b.timeout = 1; diepmsk.b.epdisabled = 1; diepmsk.b.ahberr = 1; diepmsk.b.intknepmis = 1; dwc_write_reg32( &dev_if->dev_global_regs->diepmsk, diepmsk.d32 ); /* Reset Device Address */ dcfg.d32 = dwc_read_reg32( &dev_if->dev_global_regs->dcfg); dcfg.b.devaddr = 0; dwc_write_reg32( &dev_if->dev_global_regs->dcfg, dcfg.d32); /* setup EP0 to receive SETUP packets */ ep0_out_start( core_if, _pcd ); /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.usbreset = 1; dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32); return 1; } /** * Get the device speed from the device status register and convert it * to USB speed constant. * * @param _core_if Programming view of DWC_otg controller. */ static int get_device_speed( dwc_otg_core_if_t *_core_if ) { dsts_data_t dsts; enum usb_device_speed speed = USB_SPEED_UNKNOWN; dsts.d32 = dwc_read_reg32(&_core_if->dev_if->dev_global_regs->dsts); switch (dsts.b.enumspd) { case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ: speed = USB_SPEED_HIGH; break; case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ: case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ: speed = USB_SPEED_FULL; break; case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ: speed = USB_SPEED_LOW; break; } return speed; } /** * 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(dwc_otg_pcd_t *_pcd) { dwc_otg_pcd_ep_t *ep0 = &_pcd->ep[0]; gintsts_data_t gintsts; gusbcfg_data_t gusbcfg; dwc_otg_core_global_regs_t *global_regs = GET_CORE_IF(_pcd)->core_global_regs; DWC_DEBUGPL(DBG_PCD, "SPEED ENUM\n"); dwc_otg_ep0_activate( GET_CORE_IF(_pcd), &ep0->dwc_ep ); #ifdef DEBUG_EP0 print_ep0_state(_pcd); #endif if (_pcd->ep0state == EP0_DISCONNECT) _pcd->ep0state = EP0_IDLE; else if (_pcd->ep0state == EP0_STALL) _pcd->ep0state = EP0_IDLE; _pcd->ep0state = EP0_IDLE; ep0->stopped = 0; _pcd->gadget.speed = get_device_speed(GET_CORE_IF(_pcd)); /* Set USB turnaround time based on device speed and PHY interface. */ gusbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); if (_pcd->gadget.speed == USB_SPEED_HIGH) { gusbcfg.b.usbtrdtim = GET_CORE_IF(_pcd)->core_params->turn_around_time_hs; gusbcfg.b.toutcal = GET_CORE_IF(_pcd)->core_params->timeout_cal_hs; } else /* Full or low speed */ { gusbcfg.b.usbtrdtim = GET_CORE_IF(_pcd)->core_params->turn_around_time_fs; gusbcfg.b.toutcal = GET_CORE_IF(_pcd)->core_params->timeout_cal_fs; } dwc_write_reg32(&global_regs->gusbcfg, gusbcfg.d32); /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.enumdone = 1; dwc_write_reg32( &GET_CORE_IF(_pcd)->core_global_regs->gintsts, gintsts.d32 ); return 1; } /** * This interrupt indicates that the ISO OUT Packet was dropped due to * Rx FIFO full or Rx Status Queue Full. If this interrupt occurs * read all the data from the Rx FIFO. */ int32_t dwc_otg_pcd_handle_isoc_out_packet_dropped_intr(dwc_otg_pcd_t *_pcd ) { gintmsk_data_t intr_mask = { .d32 = 0}; gintsts_data_t gintsts; DWC_PRINT("INTERRUPT Handler not implemented for %s\n", "ISOC Out Dropped"); intr_mask.b.isooutdrop = 1; dwc_modify_reg32( &GET_CORE_IF(_pcd)->core_global_regs->gintmsk, intr_mask.d32, 0 ); /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.isooutdrop = 1; dwc_write_reg32 (&GET_CORE_IF(_pcd)->core_global_regs->gintsts, gintsts.d32); return 1; } /** * This interrupt indicates the end of the portion of the micro-frame * for periodic transactions. If there is a periodic transaction for * the next frame, load the packets into the EP periodic Tx FIFO. */ int32_t dwc_otg_pcd_handle_end_periodic_frame_intr(dwc_otg_pcd_t *_pcd ) { gintmsk_data_t intr_mask = { .d32 = 0}; gintsts_data_t gintsts; // depctl_data_t ctl; // deptsiz_data_t sz; // int i; //DWC_PRINT("INTERRUPT Handler not implemented for %s\n", "EOP"); #if defined(USE_PERIODIC_EP) for (i=0; i< GET_CORE_IF(_pcd)->dev_if->num_eps; i++) { if(_pcd->ep[i].dwc_ep.tx_fifo_num) { ctl.d32=dwc_read_reg32( &GET_CORE_IF(_pcd)->dev_if->in_ep_regs[i]->diepctl); sz.d32=dwc_read_reg32( &GET_CORE_IF(_pcd)->dev_if->in_ep_regs[i]->dieptsiz); if(ctl.b.epena && sz.b.pktcnt!=1) { intr_mask.b.eopframe = 1; dwc_modify_reg32( &GET_CORE_IF(_pcd)->core_global_regs->gintmsk, intr_mask.d32, 0 ); dwc_otg_ep_stop_perio(); } if(!ctl.b.epena) { intr_mask.b.eopframe = 1; dwc_modify_reg32( &GET_CORE_IF(_pcd)->core_global_regs->gintmsk, intr_mask.d32, 0 ); } } } #else intr_mask.b.eopframe = 1; dwc_modify_reg32( &GET_CORE_IF(_pcd)->core_global_regs->gintmsk, intr_mask.d32, 0 ); #endif /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.eopframe = 1; dwc_write_reg32( &GET_CORE_IF(_pcd)->core_global_regs->gintsts, gintsts.d32); return 1; } /** * This interrupt indicates that EP of the packet on the top of the * non-periodic Tx FIFO does not match EP of the IN Token received. * * The "Device IN Token Queue" Registers are read to determine the * order the IN Tokens have been received. The non-periodic Tx FIFO * is flushed, so it can be reloaded in the order seen in the IN Token * Queue. */ int32_t dwc_otg_pcd_handle_ep_mismatch_intr(dwc_otg_core_if_t *_core_if) { gintsts_data_t gintsts; DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _core_if); /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.epmismatch = 1; dwc_write_reg32 (&_core_if->core_global_regs->gintsts, gintsts.d32); return 1; } /** * This funcion stalls EP0. */ static inline void ep0_do_stall( dwc_otg_pcd_t *_pcd, const int err_val ) { dwc_otg_pcd_ep_t *ep0 = &_pcd->ep[0]; struct usb_ctrlrequest *ctrl = &_pcd->setup_pkt->req; DWC_WARN("req %02x.%02x protocol STALL; err %d\n", ctrl->bRequestType, ctrl->bRequest, err_val); ep0->dwc_ep.is_in = 1; dwc_otg_ep_set_stall( _pcd->otg_dev->core_if, &ep0->dwc_ep ); _pcd->ep[0].stopped = 1; _pcd->ep0state = EP0_IDLE; ep0_out_start( GET_CORE_IF(_pcd), _pcd ); } /** * This functions delegates the setup command to the gadget driver. */ static inline void do_gadget_setup( dwc_otg_pcd_t *_pcd, struct usb_ctrlrequest * _ctrl) { int ret = 0; if (_pcd->driver && _pcd->driver->setup) { SPIN_UNLOCK(&_pcd->lock); ret = _pcd->driver->setup(&_pcd->gadget, _ctrl); SPIN_LOCK(&_pcd->lock); if (ret < 0){ ep0_do_stall( _pcd, ret ); } /** @todo This is a g_file_storage gadget driver specific * workaround: a DELAYED_STATUS result from the fsg_setup * routine will result in the gadget queueing a EP0 IN status * phase for a two-stage control transfer. Exactly the same as * a SET_CONFIGURATION/SET_INTERFACE except that this is a class * specific request. Need a generic way to know when the gadget * driver will queue the status phase. Can we assume when we * call the gadget driver setup() function that it will always * queue and require the following flag? Need to look into * this. */ if (ret == 256 + 999) _pcd->request_config = 1; } } /** * This function starts the Zero-Length Packet for the IN status phase * of a 2 stage control transfer. (SETUP OUT XFER) */ static inline void do_setup_in_status_phase( dwc_otg_pcd_t *_pcd) { dwc_otg_pcd_ep_t *ep0 = &_pcd->ep[0]; if (_pcd->ep0state == EP0_STALL) return; _pcd->ep0state = EP0_IN_STATUS; /* Prepare for more SETUP Packets */ // ep0_out_start( GET_CORE_IF(_pcd), _pcd ); DWC_DEBUGPL(DBG_PCD, "EP0 IN ZLP\n"); ep0->dwc_ep.xfer_len = 0; ep0->dwc_ep.xfer_count = 0; ep0->dwc_ep.is_in = 1; ep0->dwc_ep.len_in_xfer = 0; ep0->dwc_ep.xfer_buff = (uint8_t *)(_pcd->setup_pkt_buf); ep0->dwc_ep.start_xfer_buff = (uint8_t *)(_pcd->setup_pkt_buf); dwc_otg_ep_start_transfer( GET_CORE_IF(_pcd), &ep0->dwc_ep ); /* Prepare for more SETUP Packets */ // ep0_out_start( GET_CORE_IF(_pcd), _pcd ); } /** * This function starts the Zero-Length Packet for the OUT status phase * of a 2 stage control transfer. (SETUP IN XFER) */ static inline void do_setup_out_status_phase( dwc_otg_pcd_t *_pcd) { dwc_otg_pcd_ep_t *ep0 = &_pcd->ep[0]; if (_pcd->ep0state == EP0_STALL) return; _pcd->ep0state = EP0_OUT_STATUS; DWC_DEBUGPL(DBG_PCD, "EP0 OUT ZLP\n"); ep0->dwc_ep.xfer_len = 0; ep0->dwc_ep.xfer_count = 0; ep0->dwc_ep.is_in = 0; ep0->dwc_ep.len_in_xfer = 0; ep0->dwc_ep.xfer_buff = (uint8_t *)(_pcd->setup_pkt_buf); ep0->dwc_ep.start_xfer_buff = (uint8_t *)(_pcd->setup_pkt_buf); dwc_otg_ep_start_rx_transfer( GET_CORE_IF(_pcd), &ep0->dwc_ep ); /* Prepare for more SETUP Packets */ //ep0_out_start( GET_CORE_IF(_pcd), _pcd ); } /** * Clear the EP halt (STALL) and if pending requests start the * transfer. */ static inline void pcd_clear_halt( dwc_otg_pcd_t *_pcd, dwc_otg_pcd_ep_t *_ep ) { dwc_otg_ep_clear_stall( GET_CORE_IF(_pcd), &_ep->dwc_ep ); /* Reactive the EP */ dwc_otg_ep_activate( GET_CORE_IF(_pcd), &_ep->dwc_ep ); if (_ep->stopped) { _ep->stopped = 0; /* If there is a request in the EP queue start it */ /** @todo FIXME: this causes an EP mismatch in DMA mode. * epmismatch not yet implemented. */ _ep->queue_sof = 1; tasklet_schedule (_pcd->start_xfer_tasklet); #ifndef USE_INTERNAL_DMA if (GET_CORE_IF(_pcd)->core_params->opt) start_next_request( _ep ); #endif } /* Start Control Status Phase */ do_setup_in_status_phase( _pcd ); } /** * This function is called when the SET_FEATURE TEST_MODE Setup packet * is sent from the host. The Device Control register is written with * the Test Mode bits set to the specified Test Mode. This is done as * a tasklet so that the "Status" phase of the control transfer * completes before transmitting the TEST packets. * * @todo This has not been tested since the tasklet struct was put * into the PCD struct! * */ static void do_test_mode( unsigned long _data ) { dctl_data_t dctl; dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *)_data; dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); int test_mode = pcd->test_mode; DWC_WARN("%s() has not been tested since being rewritten!\n", __func__); dctl.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dctl); switch (test_mode) { case 1: // TEST_J dctl.b.tstctl = 1; break; case 2: // TEST_K dctl.b.tstctl = 2; break; case 3: // TEST_SE0_NAK dctl.b.tstctl = 3; break; case 4: // TEST_PACKET dctl.b.tstctl = 4; break; case 5: // TEST_FORCE_ENABLE dctl.b.tstctl = 5; break; } dwc_write_reg32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32); } /** * This function process the SET_FEATURE Setup Commands. */ static inline void do_set_feature( dwc_otg_pcd_t *_pcd ) { dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd); // dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; struct usb_ctrlrequest ctrl = _pcd->setup_pkt->req; dwc_otg_pcd_ep_t *ep = 0; // gotgctl_data_t gotgctl = { .d32 = 0 }; DWC_DEBUGPL(DBG_PCD, "SET_FEATURE:%02x.%02x v%04x i%04x l%04x\n", ctrl.bRequestType, ctrl.bRequest, ctrl.wValue, ctrl.wIndex, ctrl.wLength); #if 0 DWC_DEBUGPL(DBG_PCD,"otg_cap=%d\n", otg_cap_param); #endif switch (ctrl.bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: switch (ctrl.wValue) { case USB_DEVICE_TEST_MODE: /* Setup the Test Mode tasklet to do the Test * Packet generation after the SETUP Status * phase has completed. */ /** @todo This has not been tested since the * tasklet struct was put into the PCD * struct! */ _pcd->test_mode_tasklet.next = 0; _pcd->test_mode_tasklet.state = 0; atomic_set( &_pcd->test_mode_tasklet.count, 0); _pcd->test_mode_tasklet.func = do_test_mode; _pcd->test_mode_tasklet.data = (unsigned long)_pcd; _pcd->test_mode = ctrl.wIndex >> 8; tasklet_schedule(&_pcd->test_mode_tasklet); break; case USB_DEVICE_REMOTE_WAKEUP: ep0_do_stall( _pcd, -EOPNOTSUPP); break; case USB_DEVICE_B_HNP_ENABLE: ep0_do_stall( _pcd, -EOPNOTSUPP); break; case USB_DEVICE_A_HNP_SUPPORT: ep0_do_stall( _pcd, -EOPNOTSUPP); break; case USB_DEVICE_A_ALT_HNP_SUPPORT: ep0_do_stall( _pcd, -EOPNOTSUPP); break; default: ep0_do_stall( _pcd, -EOPNOTSUPP); break; } do_setup_in_status_phase( _pcd ); break; case USB_RECIP_INTERFACE: do_gadget_setup(_pcd, &ctrl ); break; case USB_RECIP_ENDPOINT: if (ctrl.wValue == USB_ENDPOINT_HALT) { ep = get_ep_by_addr(_pcd, ctrl.wIndex); if (ep == 0) { ep0_do_stall(_pcd, -EOPNOTSUPP); return; } ep->stopped = 1; dwc_otg_ep_set_stall( core_if, &ep->dwc_ep ); } do_setup_in_status_phase( _pcd ); break; } } /** * This function process the CLEAR_FEATURE Setup Commands. */ static inline void do_clear_feature( dwc_otg_pcd_t *_pcd ){ struct usb_ctrlrequest ctrl = _pcd->setup_pkt->req; dwc_otg_pcd_ep_t *ep = 0; DWC_DEBUGPL(DBG_PCD, "CLEAR_FEATURE:%02x.%02x v%04x i%04x l%04x\n", ctrl.bRequestType, ctrl.bRequest, ctrl.wValue, ctrl.wIndex, ctrl.wLength); switch (ctrl.bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: switch (ctrl.wValue) { case USB_DEVICE_TEST_MODE: /** @todo Add CLEAR_FEATURE for TEST modes. */ break; } do_setup_in_status_phase( _pcd ); break; case USB_RECIP_ENDPOINT: ep = get_ep_by_addr(_pcd, ctrl.wIndex); if (ep == 0) { ep0_do_stall(_pcd, -EOPNOTSUPP); break; return; } pcd_clear_halt(_pcd, ep ); break; } } /** * This function processes SETUP commands. In Linux, the USB Command * processing is done in two places - the first being the PCD and the * second in the Gadget Driver (for example, the File-Backed Storage * Gadget Driver). * * * * * * * * * * * * * * * * * * * * * * * * * *
Command Driver Description
GET_STATUS PCD Command is processed as * defined in chapter 9 of the USB 2.0 Specification chapter 9 *
CLEAR_FEATURE PCD The Device and Endpoint * requests are the ENDPOINT_HALT feature is procesed, all others the * interface requests are ignored.
SET_FEATURE PCD The Device and Endpoint * requests are processed by the PCD. Interface requests are passed * to the Gadget Driver.
SET_ADDRESS PCD Program the DCFG reg, * with device address received
GET_DESCRIPTOR Gadget Driver Return the * requested descriptor
SET_DESCRIPTOR Gadget Driver Optional - * not implemented by any of the existing Gadget Drivers.
SET_CONFIGURATION Gadget Driver Disable * all EPs and enable EPs for new configuration.
GET_CONFIGURATION Gadget Driver Return * the current configuration
SET_INTERFACE Gadget Driver Disable all * EPs and enable EPs for new configuration.
GET_INTERFACE Gadget Driver Return the * current interface.
SYNC_FRAME PCD Display debug * message.
* * When the SETUP Phase Done interrupt occurs, the PCD SETUP commands are * processed by pcd_setup. Calling the Function Driver's setup function from * pcd_setup processes the gadget SETUP commands. */ static inline void pcd_setup( dwc_otg_pcd_t *_pcd ){ dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd); dwc_otg_dev_if_t *dev_if = core_if->dev_if; deptsiz0_data_t doeptsize0 = { .d32 = 0}; doepint_data_t doepint = { .d32 = 0}; dwc_otg_pcd_ep_t *ep; dwc_otg_pcd_ep_t *ep0 = &_pcd->ep[0]; uint16_t *status = _pcd->status_buf; struct usb_ctrlrequest ctrl; doepint.d32= dwc_read_reg32(&dev_if->out_ep_regs[0]->doepint); { uint32_t *dma; dma=(uint32_t *)(KSEG1ADDR(dwc_read_reg32( &dev_if->out_ep_regs[0]->doepdma ))); _pcd->setup_pkt->d32[0]=*(dma-2); _pcd->setup_pkt->d32[1]=*(dma-1); } ctrl = _pcd->setup_pkt->req; #if 1 le16_to_cpus(&(ctrl.wValue)); le16_to_cpus(&(ctrl.wIndex)); le16_to_cpus(&(ctrl.wLength)); #endif #ifdef DEBUG_EP0 DWC_DEBUGPL(DBG_PCD, "SETUP %02x.%02x v%04x i%04x l%04x\n", ctrl.bRequestType, ctrl.bRequest, ctrl.wValue, ctrl.wIndex, ctrl.wLength); #endif doeptsize0.d32 = dwc_read_reg32( &dev_if->out_ep_regs[0]->doeptsiz ); doeptsize0.b.supcnt=3; dwc_write_reg32( &dev_if->out_ep_regs[0]->doeptsiz,doeptsize0.d32 ); // clear interrupt { doepint_data_t doepint = {.d32=0}; doepint.b.outtknepdis = 1; doepint.b.back2backsetup = 1; dwc_write_reg32(&dev_if->out_ep_regs[0]->doepint, doepint.d32); } /* Clean up the request queue */ request_nuke( ep0 ); ep0->stopped = 0; if (ctrl.bRequestType & USB_DIR_IN) { ep0->dwc_ep.is_in = 1; _pcd->ep0state = EP0_IN_DATA_PHASE; } else { ep0->dwc_ep.is_in = 0; _pcd->ep0state = EP0_OUT_DATA_PHASE; #if defined(USE_PERIODIC_EP) dwc_otg_ep_stop_perio(); #endif } if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) { /* handle non-standard (class/vendor) requests in the gadget driver */ // if(ctrl.bRequest == 1) //USB_CDC_GET_ENCAPSULATED_COMMAND // clr_din_holding(); do_gadget_setup(_pcd, &ctrl ); return; } // if (ctrl.bRequestType & USB_DIR_IN) // clr_din_holding(); /** @todo NGS: Handle bad setup packet? */ switch (ctrl.bRequest) { case USB_REQ_GET_STATUS: #ifdef DEBUG_EP0 DWC_DEBUGPL(DBG_PCD, "GET_STATUS %02x.%02x v%04x i%04x l%04x\n", ctrl.bRequestType, ctrl.bRequest, ctrl.wValue, ctrl.wIndex, ctrl.wLength); #endif switch (ctrl.bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: *status = 0x1; /* Self powered */ break; 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.xfer_len = 2; ep0->dwc_ep.xfer_count = 0; ep0->dwc_ep.len_in_xfer = 0; dwc_otg_ep_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; case USB_REQ_SET_ADDRESS: if (ctrl.bRequestType == USB_RECIP_DEVICE) { dcfg_data_t dcfg = {.d32=0}; #ifdef DEBUG_EP0 DWC_DEBUGPL(DBG_PCDV, "SET_ADDRESS:%d\n", ctrl.wValue); #endif dcfg.d32=dwc_read_reg32( &dev_if->dev_global_regs->dcfg); dcfg.b.devaddr = ctrl.wValue; dwc_write_reg32( &dev_if->dev_global_regs->dcfg,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 ); break; default: /* Call the Gadget Driver's setup functions */ do_gadget_setup(_pcd, &ctrl ); break; } } /** * This function completes the ep0 control transfer. */ static int32_t ep0_complete_request( dwc_otg_pcd_ep_t *_ep ) { dwc_otg_core_if_t *core_if = GET_CORE_IF(_ep->pcd); dwc_otg_dev_if_t *dev_if = core_if->dev_if; dwc_otg_dev_in_ep_regs_t *in_ep_regs = dev_if->in_ep_regs[_ep->dwc_ep.num]; #ifdef DEBUG_EP0 dwc_otg_dev_out_ep_regs_t *out_ep_regs = dev_if->out_ep_regs[_ep->dwc_ep.num]; #endif deptsiz0_data_t deptsiz; dwc_otg_pcd_request_t *req; int is_last = 0; dwc_otg_pcd_t *pcd = _ep->pcd; #if defined(GADGET_UNALIGNED_BUFFER_ADJUST) if (!list_empty(&_ep->queue)) { req = list_entry(_ep->queue.next, dwc_otg_pcd_request_t, queue); if(!_ep->dwc_ep.is_in && req->req.buf && req->req.buf!= 0xFFFFFFFFF && _ep->using_aligned_rx_buf) { memcpy(req->req.buf, _ep->aligned_rx_buf, _ep->dwc_ep.xfer_count); _ep->using_aligned_rx_buf=0; } } #endif //DWC_DEBUGPL(DBG_PCDV, "%s() %s\n", __func__, _ep->ep.name); if (pcd->ep0_pending && list_empty(&_ep->queue)) { if (_ep->dwc_ep.is_in) { #ifdef DEBUG_EP0 DWC_DEBUGPL(DBG_PCDV, "Do setup OUT status phase\n"); #endif do_setup_out_status_phase(pcd); pcd->ep0state = EP0_OUT_STATUS; } else { #ifdef DEBUG_EP0 DWC_DEBUGPL(DBG_PCDV, "Do setup IN status phase\n"); #endif do_setup_in_status_phase(pcd); pcd->ep0state = EP0_IN_STATUS; } pcd->ep0_pending = 0; return 1; } if (list_empty(&_ep->queue)) return 0; req = list_entry(_ep->queue.next, dwc_otg_pcd_request_t, queue); if (pcd->ep0state == EP0_IN_STATUS ||pcd->ep0state == EP0_OUT_STATUS ) is_last = 1; else if (req->req.zero) { req->req.actual = _ep->dwc_ep.xfer_count; // do_setup_in_status_phase (pcd); req->req.zero = 0; _ep->dwc_ep.xfer_len = 0; _ep->dwc_ep.len_in_xfer = 0; _ep->dwc_ep.xfer_count = 0; _ep->dwc_ep.sent_zlp = 1; dwc_otg_ep_start_transfer( GET_CORE_IF(pcd), &_ep->dwc_ep ); return 1; } else if (_ep->dwc_ep.is_in) { deptsiz.d32 = dwc_read_reg32( &in_ep_regs->dieptsiz); #ifdef DEBUG_EP0 DWC_DEBUGPL(DBG_PCDV, "%s len=%d xfersize=%d pktcnt=%d\n", _ep->ep.name, _ep->dwc_ep.xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt); #endif if (deptsiz.b.xfersize == 0) { req->req.actual = _ep->dwc_ep.xfer_count; #ifdef DEBUG_EP0 DWC_DEBUGPL(DBG_PCD, "Setup Rx ZLP\n"); #endif do_setup_out_status_phase(pcd); } } else { /* ep0-OUT */ #ifdef DEBUG_EP0 deptsiz.d32 = dwc_read_reg32( &out_ep_regs->doeptsiz); DWC_DEBUGPL(DBG_PCDV, "%s len=%d xsize=%d pktcnt=%d\n", _ep->ep.name, _ep->dwc_ep.xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt); #endif req->req.actual = _ep->dwc_ep.xfer_count; #ifdef DEBUG_EP0 DWC_DEBUGPL(DBG_PCDV, "Setup Tx ZLP\n"); #endif do_setup_in_status_phase(pcd); } /* Complete the request */ if (is_last) { request_done(_ep, req, 0); _ep->dwc_ep.start_xfer_buff = 0; _ep->dwc_ep.xfer_buff = 0; _ep->dwc_ep.xfer_len = 0; _ep->dwc_ep.len_in_xfer = 0; return 1; } return 0; } /** * This function completes the request for the EP. If there are * additional requests for the EP in the queue they will be started. */ static void complete_ep( dwc_otg_pcd_ep_t *_ep ) { dwc_otg_core_if_t *core_if = GET_CORE_IF(_ep->pcd); dwc_otg_dev_if_t *dev_if = core_if->dev_if; dwc_otg_dev_in_ep_regs_t *in_ep_regs = dev_if->in_ep_regs[_ep->dwc_ep.num]; dwc_otg_dev_out_ep_regs_t *out_ep_regs = dev_if->out_ep_regs[_ep->dwc_ep.num]; deptsiz_data_t deptsiz; dwc_otg_pcd_request_t *req = 0; int is_last = 0; DWC_DEBUGPL(DBG_PCDV,"%s() %s-%s\n", __func__, _ep->ep.name, (_ep->dwc_ep.is_in?"IN":"OUT")); /* Get any pending requests */ if (!list_empty(&_ep->queue)) req = list_entry(_ep->queue.next, dwc_otg_pcd_request_t, queue); DWC_DEBUGPL(DBG_PCD, "Requests %d\n",_ep->pcd->request_pending); if (_ep->dwc_ep.is_in) { #ifdef USE_INTERNAL_DMA deptsiz.d32 = dwc_read_reg32( &in_ep_regs->dieptsiz); //if(POPDBG) printk(KERN_INFO "COMPLETE_EP: EP%d-%s %08x %d/%d %d/%d\n", // _ep->dwc_ep.num, (_ep->dwc_ep.is_in ?"IN":"OUT"), // deptsiz.d32,deptsiz.b.xfersize,_ep->dwc_ep.len_in_xfer,deptsiz.b.pktcnt,_ep->dwc_ep.packet_in_xfer ); if (deptsiz.b.xfersize == 0 && deptsiz.b.pktcnt == 0 ) { _ep->dwc_ep.xfer_count += _ep->dwc_ep.len_in_xfer; _ep->dwc_ep.xfer_buff += _ep->dwc_ep.len_in_xfer; _ep->dwc_ep.len_in_xfer=0; if(_ep->dwc_ep.xfer_count>=_ep->dwc_ep.xfer_len) { if(req) { req->req.actual = _ep->dwc_ep.xfer_count; request_done(_ep, req, 0); restart_cin(); } is_last = 1; } else{ if(_ep->dwc_ep.num==1) printk(KERN_INFO "%s() Size=%d/%d\n",__func__,_ep->dwc_ep.xfer_count,_ep->dwc_ep.xfer_len); dwc_otg_ep_start_transfer(core_if, &_ep->dwc_ep); } } else if(_ep->dwc_ep.tx_fifo_num){ } else { DWC_WARN("Incomplete transfer (%d)(%s-%s [siz=%d pkt=%d txfifo=%d]) orig[%d]\n",__LINE__, _ep->ep.name, (_ep->dwc_ep.is_in?"IN":"OUT"), deptsiz.b.xfersize, deptsiz.b.pktcnt,_ep->dwc_ep.tx_fifo_num, _ep->dwc_ep.xfer_len); if(req) { req->req.actual = 0; request_done(_ep, req, -EPIPE); } is_last = 1; } #else //USE_INTERNAL_DMA if (deptsiz.b.xfersize == 0 && deptsiz.b.pktcnt == 0 && _ep->dwc_ep.xfer_count == _ep->dwc_ep.xfer_len) { if(req) { req->req.actual = _ep->dwc_ep.xfer_count; request_done(_ep, req, 0); } is_last = 1; } else { DWC_WARN("Incomplete transfer (%d)(%s-%s [siz=%d pkt=%d txfifo=%d]) orig[%d]\n",__LINE__, _ep->ep.name, (_ep->dwc_ep.is_in?"IN":"OUT"), deptsiz.b.xfersize, deptsiz.b.pktcnt,_ep->dwc_ep.tx_fifo_num, _ep->dwc_ep.xfer_len); DWC_WARN("diepint = %08X diepctl = %08x\n", dwc_read_reg32( &in_ep_regs->diepint), dwc_read_reg32( &in_ep_regs->diepctl)); if(req) { req->req.actual = 0; request_done(_ep, req, , -EPIPE); } is_last = 1; } #endif //USE_INTERNAL_DMA } else // if (_ep->dwc_ep.is_in) // is outep { deptsiz.d32 = dwc_read_reg32( &out_ep_regs->doeptsiz); //printk(KERN_INFO "[r]\n"); //if(POPDBG) printk(KERN_INFO "COMPLETE_EP: EP%d-%s %08x %d/%d %d/%d\n", // _ep->dwc_ep.num, (_ep->dwc_ep.is_in ?"IN":"OUT"), // deptsiz.d32,deptsiz.b.xfersize,_ep->dwc_ep.len_in_xfer,deptsiz.b.pktcnt,_ep->dwc_ep.packet_in_xfer ); #ifdef DEBUG DWC_DEBUGPL(DBG_PCDV, "addr %p, %s len=%d cnt=%d xsize=%d pktcnt=%d\n", &out_ep_regs->doeptsiz, _ep->ep.name, _ep->dwc_ep.xfer_len, _ep->dwc_ep.xfer_count, deptsiz.b.xfersize, deptsiz.b.pktcnt); #endif if(req) { #ifdef USE_INTERNAL_DMA req->req.actual = _ep->dwc_ep.len_in_xfer - deptsiz.b.xfersize; #else req->req.actual = _ep->dwc_ep.xfer_count; #endif #if defined(GADGET_UNALIGNED_BUFFER_ADJUST) if(_ep->using_aligned_rx_buf && req->req.actual>0 && req->req.buf && req->req.buf!= 0xFFFFFFFFF ) { memcpy(req->req.buf, _ep->aligned_rx_buf, req->req.actual); _ep->using_aligned_rx_buf=0; } #endif request_done(_ep, req, 0); } else printk(KERN_INFO "%s() %d NO REQ\n",__func__,__LINE__); is_last = 1; } if (is_last) { _ep->dwc_ep.start_xfer_buff = 0; _ep->dwc_ep.xfer_buff = 0; _ep->dwc_ep.xfer_len = 0; /* If there is a request in the queue start it.*/ start_next_request( _ep ); } } /** * This function handles EP0 Control transfers. * * The state of the control tranfers are tracked in * ep0state. */ int skipping=0; static void handle_ep0( dwc_otg_pcd_t *_pcd ,int tp) { dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd); dwc_otg_pcd_ep_t *ep0 = &_pcd->ep[0]; deptsiz_data_t deptsiz; #ifdef DEBUG_EP0 DWC_DEBUGPL(DBG_PCDV, "%s()\n", __func__); print_ep0_state(_pcd); #endif switch (_pcd->ep0state){ case EP0_DISCONNECT: break; case EP0_IDLE: _pcd->request_config = 0; clr_pin_next(); //printk(KERN_INFO "%s()%d SET_DIN_HOLDING\n",__func__,__LINE__); set_din_holding(); pcd_setup( _pcd ); break; case EP0_IN_DATA_PHASE: #ifdef DEBUG_EP0 DWC_DEBUGPL(DBG_PCD, "DATA_IN EP%d-%s: type=%d, mps=%d\n", ep0->dwc_ep.num, (ep0->dwc_ep.is_in ?"IN":"OUT"), ep0->dwc_ep.type, ep0->dwc_ep.maxpacket ); #endif //if(POPDBG) printk(KERN_INFO "EP0_IN_DATA_PHASE: EP%d-%s inxfer=%d\n", // ep0->dwc_ep.num, (ep0->dwc_ep.is_in ?"IN":"OUT"), // ep0->dwc_ep.len_in_xfer ); ep0->dwc_ep.xfer_count += ep0->dwc_ep.len_in_xfer; ep0->dwc_ep.xfer_buff += ep0->dwc_ep.len_in_xfer; if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.xfer_len) dwc_otg_ep_start_transfer ( GET_CORE_IF(_pcd), &ep0->dwc_ep ); else ep0_complete_request( ep0 ); break; case EP0_OUT_DATA_PHASE: #ifdef DEBUG_EP0 DWC_DEBUGPL(DBG_PCD, "DATA_OUT EP%d-%s: type=%d, mps=%d\n", ep0->dwc_ep.num, (ep0->dwc_ep.is_in ?"IN":"OUT"), ep0->dwc_ep.type, ep0->dwc_ep.maxpacket ); #endif deptsiz.d32 = dwc_read_reg32( &core_if->dev_if->out_ep_regs[0]->doeptsiz); //if(POPDBG) printk(KERN_INFO "EP0_OUT_DATA_PHASE: EP%d-%s xfered=%d\n", // ep0->dwc_ep.num, (ep0->dwc_ep.is_in ?"IN":"OUT"), // (ep0->dwc_ep.len_in_xfer - deptsiz.b.xfersize) ); ep0->dwc_ep.xfer_count += (ep0->dwc_ep.len_in_xfer - deptsiz.b.xfersize); ep0->dwc_ep.xfer_buff += (ep0->dwc_ep.len_in_xfer - deptsiz.b.xfersize); if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.xfer_len) dwc_otg_ep_start_transfer ( GET_CORE_IF(_pcd), &ep0->dwc_ep ); else { struct usb_ctrlrequest ctrl; ctrl = _pcd->setup_pkt->req; if(ctrl.bRequest == 0) //USB_CDC_SEND_ENCAPSULATED_COMMAND { u32 MsgType; u32 *tmp; tmp = (u32 *) ep0->dwc_ep.start_xfer_buff; MsgType = *tmp; switch(MsgType) { case 0x01000000: //REMOTE_NDIS_PACKET_MSG\n"); case 0x02000000: //REMOTE_NDIS_INITIALIZE_MSG\n"); case 0x03000000: //REMOTE_NDIS_HALT_MSG\n"); case 0x04000000: //REMOTE_NDIS_QUERY_MSG\n"); case 0x05000000: //REMOTE_NDIS_SET_MSG\n"); case 0x06000000: //REMOTE_NDIS_RESET_MSG\n"); case 0x07000000: //REMOTE_NDIS_INDICATE_STATUS_MSG\n"); case 0x08000000: //REMOTE_NDIS_KEEPALIVE_MSG set_pin_next(); skipping=1; break; } } ep0_complete_request( ep0 ); } break; case EP0_OUT_STATUS: // ep0_out_start( core_if, _pcd ); case EP0_IN_STATUS: //if(POPDBG) printk(KERN_INFO "EP0_XX_STATUS: DATA_IN EP%d-%s\n", // ep0->dwc_ep.num, (ep0->dwc_ep.is_in ?"IN":"OUT")); if(skipping==0) { //printk(KERN_INFO "%s()%d CLR_DIN_HOLDING\n",__func__,__LINE__); clr_din_holding(); } skipping=0; ep0_complete_request( ep0 ); _pcd->ep0state = EP0_IDLE; ep0->stopped = 1; ep0->dwc_ep.is_in = 0; /* OUT for next SETUP */ { #ifdef USE_INTERNAL_DMA ep0_out_start( core_if, _pcd ); restart_din(); #else { int i; for (i=0; idev_if->num_eps; i++) { depctl_data_t diepctl; diepctl.d32 = dwc_read_reg32( &core_if->dev_if->in_ep_regs[i]->diepctl); if (_pcd->ep[i].queue_sof) { _pcd->ep[i].queue_sof = 0; start_next_request (&_pcd->ep[i]); } } } #endif } break; case EP0_STALL: DWC_ERROR("EP0 STALLed, should not get here pcd_setup()\n"); break; } #ifdef DEBUG_EP0 print_ep0_state(_pcd); #endif } /** * handle the IN EP disable interrupt. */ extern dwc_otg_core_if_t *tx_core_if; void DisableRestart (unsigned long data) { dwc_otg_ep_start_transfer(tx_core_if, (dwc_ep_t *)data); //tasklet_hi_schedule(&RxTasklet); } //static struct tasklet_struct DisableRestartTask = { // .next = NULL, // .state = 0, // .count = ATOMIC_INIT(0), // .func = DisableRestart, // .data = 0, //}; struct timer_list ep_restart_timer; void set_ep_restart_timer(unsigned long data) { init_timer( &ep_restart_timer ); ep_restart_timer.function = DisableRestart; ep_restart_timer.data = (unsigned long)data; ep_restart_timer.expires = jiffies + (HZ/5); add_timer(&ep_restart_timer); } static inline void handle_in_ep_disable_intr(dwc_otg_pcd_t *_pcd, const uint32_t _epnum) { dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd); dwc_otg_dev_if_t *dev_if = core_if->dev_if; dwc_otg_pcd_ep_t *ep; dwc_ep_t *dwc_ep; deptsiz_data_t dieptsiz = {.d32=0}; dctl_data_t dctl = {.d32=0}; // depctl_data_t diepctl = {.d32=0}; int i; ep = &_pcd->ep[ _epnum ]; dwc_ep = &_pcd->ep[ _epnum ].dwc_ep; dieptsiz.d32 = dwc_read_reg32(&dev_if->in_ep_regs[_epnum]->dieptsiz); DWC_DEBUGPL(DBG_PCD,"diepctl%d=%0x\n", _epnum, dwc_read_reg32(&dev_if->in_ep_regs[_epnum]->diepctl)); DWC_DEBUGPL(DBG_ANY, "pktcnt=%d size=%d\n", dieptsiz.b.pktcnt, dieptsiz.b.xfersize ); if(_pcd->gnaksource){ // come from global nak int allstopped=1; //if(dwc_ep->num==1) printk("disb with packet:%d/%d xfer_count:%d/%d \n", //dieptsiz.b.pktcnt,dwc_ep->packet_in_xfer, //dwc_ep->xfer_count,dwc_ep->xfer_len //); if(//dwc_ep->len_in_xfer>0 && dwc_ep->packet_in_xfer > dieptsiz.b.pktcnt && dwc_ep->xfer_count < dwc_ep->xfer_len) { int len; len=(dwc_ep->packet_in_xfer-dieptsiz.b.pktcnt)*dwc_ep->maxpacket; if(dwc_ep->xfer_count+len > dwc_ep->xfer_len) len = dwc_ep->xfer_len - dwc_ep->xfer_count; dwc_ep->xfer_count+=len; ((uint8_t *)dwc_ep->xfer_buff) +=len; } ep->gnpdisabling=0; for (i=0; i < dev_if->num_eps && allstopped; i++) { if(_pcd->ep[i].gnpdisabling) allstopped=0; } if(allstopped) { int timeout; /* Flush the Tx FIFO */ dwc_otg_flush_tx_fifo( core_if, 0 ); dctl.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dctl); if(dctl.b.gnpinnaksts) { timeout = 10; dctl.d32 = 0; dctl.b.cgnpinnak = 1; dwc_modify_reg32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32); dctl.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dctl); while(dctl.b.gnpinnaksts) { if(timeout == 0) { printk("DCTL_GNPIN_NAK_STS_BIT not cleared in 10 usec...\n"); break; } timeout--; udelay(1); dctl.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dctl); } } dwc_otg_jump_ep(core_if,-1, _pcd->gnaknext); if(_pcd->gnaksource==1) { clr_cin_holding(); restart_cin(); } else{ //if(_pcd->gnaknext==1) printk(KERN_INFO "%s()GNAK Size=%d/%d\n",__func__,_pcd->ep[_pcd->gnaknext].dwc_ep.xfer_count,_pcd->ep[_pcd->gnaknext].dwc_ep.xfer_len); dwc_otg_ep_start_transfer(core_if, &_pcd->ep[_pcd->gnaknext].dwc_ep); } _pcd->gnaksource=0; _pcd->gnaknext=0; } return; } else if(dwc_ep->type==DWC_OTG_EP_TYPE_INTR) return; else { if(dwc_ep->xfer_len==0) { if ( dieptsiz.b.pktcnt) { ep->stopped = 0; //if(dwc_ep->num==1) printk(KERN_INFO "%s()NotGNAK Size=%d/%d\n",__func__,dwc_ep->xfer_count,dwc_ep->xfer_len); dwc_otg_ep_start_transfer(core_if, dwc_ep); } } else { if(//dwc_ep->len_in_xfer>0 && dwc_ep->packet_in_xfer > dieptsiz.b.pktcnt && dwc_ep->xfer_count < dwc_ep->xfer_len) { int len; len=(dwc_ep->packet_in_xfer-dieptsiz.b.pktcnt)*dwc_ep->maxpacket; if(dwc_ep->xfer_count+len > dwc_ep->xfer_len) len = dwc_ep->xfer_len - dwc_ep->xfer_count; dwc_ep->xfer_count+=len; ((uint8_t *)dwc_ep->xfer_buff) +=len; } if(dwc_ep->xfer_countxfer_len) { ep->stopped = 0; //if(dwc_ep->num==1) printk(KERN_INFO "%s()NotGNAK Size=%d/%d\n",__func__,dwc_ep->xfer_count,dwc_ep->xfer_len); dwc_otg_ep_start_transfer(core_if, dwc_ep); } } } } /** * Handler for the IN EP timeout handshake interrupt. */ static inline void handle_in_ep_timeout_intr(dwc_otg_pcd_t *_pcd,const uint32_t _epnum) { dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd); dwc_otg_dev_if_t *dev_if = core_if->dev_if; #ifdef DEBUG deptsiz_data_t dieptsiz = {.d32=0}; #endif dctl_data_t dctl = {.d32=0}; // dwc_otg_pcd_ep_t *ep = &_pcd->ep[ _epnum ]; gintmsk_data_t intr_mask = {.d32 = 0}; DWC_WARN( "IN-EP %d Timeout\n",_epnum); /* Disable the NP Tx Fifo Empty Interrrupt */ #ifndef USE_INTERNAL_DMA intr_mask.b.nptxfempty = 1; dwc_modify_reg32( &core_if->core_global_regs->gintmsk, intr_mask.d32, 0); #endif /** @todo NGS Check EP type. * Implement for Periodic EPs */ /* * Non-periodic EP */ /* Set Global IN NAK */ #ifndef USE_ORIG dctl.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dctl); if (!dctl.b.gnpinnaksts) #endif { _pcd->gnaksource=3; _pcd->gnaknext=_epnum; dctl.d32 = 0; dctl.b.sgnpinnak = 1; dwc_modify_reg32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32); } _pcd->gnaksource=3; _pcd->gnaknext=_epnum; /* Enable the Global IN NAK Effective Interrupt */ intr_mask.b.ginnakeff = 1; dwc_modify_reg32( &core_if->core_global_regs->gintmsk, 0, intr_mask.d32); #ifdef DEBUG dieptsiz.d32 = dwc_read_reg32(&dev_if->in_ep_regs[_epnum]->dieptsiz); DWC_DEBUGPL(DBG_ANY, "pktcnt=%d size=%d\n", dieptsiz.b.pktcnt, dieptsiz.b.xfersize ); #endif #ifdef DISABLE_PERIODIC_EP /* * Set the NAK bit for this EP to * start the disable process. */ diepctl.d32 = 0; diepctl.b.snak = 1; dwc_modify_reg32(&dev_if->in_ep_regs[_epnum]->diepctl, diepctl.d32, diepctl.d32); ep->disabling = 1; ep->stopped = 1; #endif } /** * Handler for the IN EP Mismatch interrupt. */ //#ifdef USE_INTERRUPT_TRANSFER_TIMES static inline void handle_in_ep_mismatch_intr(dwc_otg_pcd_t *_pcd, const uint32_t _epnum) { dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd); dwc_otg_dev_if_t *dev_if = core_if->dev_if; #ifdef DEBUG deptsiz_data_t dieptsiz = {.d32=0}; #endif dctl_data_t dctl = {.d32=0}; // dwc_otg_pcd_ep_t *ep = &_pcd->ep[ _epnum ]; gintmsk_data_t intr_mask = {.d32 = 0}; /* Disable the NP Tx Fifo Empty Interrrupt */ #ifndef USE_INTERNAL_DMA intr_mask.b.nptxfempty = 1; dwc_modify_reg32( &core_if->core_global_regs->gintmsk, intr_mask.d32, 0); #endif /** @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( &core_if->core_global_regs->gintmsk, 0, intr_mask.d32); /* Set Global IN NAK */ #ifndef USE_ORIG dctl.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dctl); if (!(dctl.b.gnpinnaksts)) #endif { _pcd->gnaksource=2; _pcd->gnaknext=_epnum; dctl.b.sgnpinnak = 1; //printk(KERN_INFO "%s() %d SETTING GNPNAK\n",__func__,__LINE__); dwc_modify_reg32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32); } #ifdef DEBUG dieptsiz.d32 = dwc_read_reg32(&dev_if->in_ep_regs[_epnum]->dieptsiz); DWC_DEBUGPL(DBG_ANY, "pktcnt=%d size=%d\n", dieptsiz.b.pktcnt, dieptsiz.b.xfersize ); #endif #ifdef DISABLE_PERIODIC_EP /* * Set the NAK bit for this EP to * start the disable process. */ diepctl.d32 = 0; diepctl.b.snak = 1; dwc_modify_reg32(&dev_if->in_ep_regs[_epnum]->diepctl, diepctl.d32, diepctl.d32); ep->disabling = 1; ep->stopped = 1; #endif } //#endif /** * 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(dwc_otg_pcd_t *_pcd) { #define CLEAR_IN_EP_INTR(__core_if,__epnum,__intr) \ do { \ diepint_data_t diepint = {.d32=0}; \ diepint.b.__intr = 1; \ dwc_write_reg32(&__core_if->dev_if->in_ep_regs[__epnum]->diepint, \ diepint.d32); \ } while (0) dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd); dwc_otg_dev_if_t *dev_if = core_if->dev_if; diepint_data_t diepint = {.d32=0}; depctl_data_t diepctl = {.d32=0}; uint32_t ep_intr; uint32_t epnum = 0; dwc_otg_pcd_ep_t *ep; dwc_ep_t *dwc_ep; gintmsk_data_t intr_mask = {.d32 = 0}; DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _pcd); /* Read in the device interrupt bits */ ep_intr = dwc_otg_read_dev_all_in_ep_intr( core_if ); /* Service the Device IN interrupts for each endpoint */ while( ep_intr ) { if (ep_intr&0x1) { /* Get EP pointer */ intr_mask.d32=0; ep = &_pcd->ep[ epnum ]; dwc_ep = &_pcd->ep[ epnum ].dwc_ep; DWC_DEBUGPL(DBG_PCD, "EP%d-%s: type=%d, mps=%d\n", dwc_ep->num, (dwc_ep->is_in ?"IN":"OUT"), dwc_ep->type, dwc_ep->maxpacket ); diepint.d32 = dwc_otg_read_dev_in_ep_intr( core_if, dwc_ep ); /* Transfer complete */ if ( diepint.b.xfercompl ) { DWC_DEBUGPL(DBG_PCD,"EP%d IN Xfer Complete\n", epnum); /* Disable the NP Tx FIFO Empty * Interrrupt */ intr_mask.b.nptxfempty = 1; dwc_modify_reg32( &core_if->core_global_regs->gintmsk, intr_mask.d32, 0); /* Clear the bit in DIEPINTn for this interrupt */ CLEAR_IN_EP_INTR(core_if,epnum,xfercompl); /* Complete the transfer */ if (epnum == 0) { handle_ep0( _pcd ,1); } else { diepctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[epnum]->diepctl); if (diepctl.b.epena) printk(KERN_INFO "IN EP%d COMPLETE WITH ENABLE\n",epnum); complete_ep( ep ); } #ifdef USE_MISMATCH_COUNTDOWN mismatch_countdown = USE_MISMATCH_COUNTDOWN; #endif } /* Endpoint disable */ if ( diepint.b.epdisabled ) { DWC_DEBUGPL(DBG_ANY,"EP%d IN disabled\n", epnum); handle_in_ep_disable_intr( _pcd, epnum ); #ifdef USE_MISMATCH_COUNTDOWN mismatch_countdown = USE_MISMATCH_COUNTDOWN; #endif /* Clear the bit in DIEPINTn for this interrupt */ CLEAR_IN_EP_INTR(core_if,epnum,epdisabled); } /* AHB Error */ if ( diepint.b.ahberr ) { DWC_DEBUGPL(DBG_ANY,"EP%d IN AHB Error\n", epnum); /* Clear the bit in DIEPINTn for this interrupt */ CLEAR_IN_EP_INTR(core_if,epnum,ahberr); } /* TimeOUT Handshake (non-ISOC IN EPs) */ if ( diepint.b.timeout ) { DWC_DEBUGPL(DBG_ANY,"EP%d IN Time-out\n", epnum); handle_in_ep_timeout_intr( _pcd, epnum ); CLEAR_IN_EP_INTR(core_if,epnum,timeout); } /** IN Token received with TxF Empty */ if (diepint.b.intktxfemp) { DWC_DEBUGPL(DBG_ANY,"EP%d IN TKN TxFifo Empty\n",epnum); if (!ep->stopped && epnum != 0) { diepmsk_data_t diepmsk = { .d32 = 0}; diepmsk.b.intktxfemp = 1; dwc_modify_reg32( &dev_if->dev_global_regs->diepmsk, diepmsk.d32, 0 ); start_next_request(ep); } #ifdef USE_MISMATCH_COUNTDOWN mismatch_countdown = USE_MISMATCH_COUNTDOWN; #endif CLEAR_IN_EP_INTR(core_if,epnum,intktxfemp); } /** IN Token Received with EP mismatch */ if (diepint.b.intknepmis) { gnptxsts_data_t txstatus = {.d32 = 0}; DWC_DEBUGPL(DBG_ANY,"EP%d IN TKN EP Mismatch\n", epnum); txstatus.d32=dwc_read_reg32(&core_if->core_global_regs->gnptxsts ); MDELAY(10); #ifdef USE_INTERRUPT_TRANSFER_TIMES if (interrupt_transfer_times == 0) { diepmsk_data_t diepmsk; diepmsk.d32 = dwc_read_reg32(&dev_if->dev_global_regs->diepmsk); diepmsk.b.intknepmis = 0; // mask this interrupt. dwc_write_reg32(&dev_if->dev_global_regs->diepmsk, diepmsk.d32); return 1; } else interrupt_transfer_times--; #endif diepctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[epnum]->diepctl); #ifdef USE_MISMATCH_COUNTDOWN if(mismatch_countdown >0) mismatch_countdown --; else #endif if (diepctl.b.epena) { #if 0 printk(KERN_INFO "EP%d IN TKN EP Mismatch topep=0x%x token=%s teminate=%x q=0x%x spc=0x%x EPCTL=0x%08x SZ=0x%08x\n", epnum, txstatus.b.nptxqtop_chnep, (txstatus.b.nptxqtop_token==0)?"IO":(txstatus.b.nptxqtop_token==1)?"ZLP":(txstatus.b.nptxqtop_token==2)?"PING":"HALT" , txstatus.b.nptxqtop_terminate, txstatus.b.nptxqspcavail, txstatus.b.nptxfspcavail, diepctl.d32, dwc_read_reg32(&dev_if->in_ep_regs[epnum]->dieptsiz)); #endif handle_in_ep_mismatch_intr(_pcd, epnum); #ifdef USE_MISMATCH_COUNTDOWN mismatch_countdown = USE_MISMATCH_COUNTDOWN; #endif } CLEAR_IN_EP_INTR(core_if,epnum,intknepmis); } /** IN Endpoint HAK Effective */ if (diepint.b.inepnakeff) { DWC_DEBUGPL(DBG_ANY,"EP%d IN EP NAK Effective\n", epnum); /* Periodic EP */ #if 0 if (ep->disabling) { diepctl.d32 = 0; diepctl.b.snak = 1; diepctl.b.epdis = 1; dwc_modify_reg32(&dev_if->in_ep_regs[epnum]->diepctl, diepctl.d32, diepctl.d32); } #endif CLEAR_IN_EP_INTR(core_if,epnum,inepnakeff); } } epnum++; ep_intr >>=1; } return 1; #undef CLEAR_IN_EP_INTR } /** * 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) */ #if 0 //--3M #ifdef USING_TIMEOUT_FOR_OUT int ep_out_timer_is_on; struct timer_list ep_out_timer; void dwc_otg_ep_out_timeout( unsigned long _ptr ) { gintmsk_data_t gintmask = {.d32=0}; dwc_otg_pcd_t *pcd; dwc_otg_core_if_t *core_if; dwc_otg_core_global_regs_t *global_regs; printk(KERN_INFO "%s()CLK OUT\n",__func__); // del_timer(&ep_out_timer); ep_out_timer_is_on=0; pcd=(dwc_otg_pcd_t *) _ptr; core_if = GET_CORE_IF(pcd); global_regs = core_if->core_global_regs; rx_timeoutfunc(pcd); // gintmask.b.rxstsqlvl= 1; // dwc_modify_reg32( &global_regs->gintmsk,0, gintmask.d32); return; } #endif static void set_dwc_otg_ep_out_timeout(dwc_otg_pcd_t *_pcd) { #ifdef USING_TIMEOUT_FOR_OUT if(ep_out_timer_is_on) del_timer(&ep_out_timer); //printk(KERN_INFO "%s()CLK OUT\n",__func__); init_timer( &ep_out_timer ); ep_out_timer.function = dwc_otg_ep_out_timeout; ep_out_timer.data = (unsigned long)_pcd; ep_out_timer.expires = jiffies + (HZ*5); add_timer(&ep_out_timer); ep_out_timer_is_on=1; #endif } void Preserv(dwc_otg_core_if_t *_core_if) { int i; volatile uint32_t *addr; addr=&_core_if->core_global_regs->gotgctl; Psv.GOTGCTL =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->core_global_regs->gotgint; Psv.GOTGINT =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->core_global_regs->gahbcfg; Psv.GAHBCFG =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->core_global_regs->gusbcfg; Psv.GUSBCFG =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->core_global_regs->grstctl; Psv.GRSTCTL =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->core_global_regs->gintsts; Psv.GINTSTS =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->core_global_regs->gintmsk; Psv.GINTMSK =(uint32_t)dwc_read_reg32(addr); //addr=&_core_if->core_global_regs->grxstsr; //Psv.GRXSTSR =(uint32_t)dwc_read_reg32(addr); //addr=&_core_if->core_global_regs->grxstsp; //Psv.GRXSTSP =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->core_global_regs->grxfsiz; Psv.GRXFSIZ =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->core_global_regs->gnptxfsiz; Psv.GNPTXFSIZ =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->core_global_regs->gnptxsts; Psv.GNPTXSTS =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->core_global_regs->gi2cctl; Psv.GI2CCTL =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->core_global_regs->gpvndctl; Psv.GPVNDCTL =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->core_global_regs->ggpio; Psv.GGPIO =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->core_global_regs->guid; Psv.GUID =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->core_global_regs->gsnpsid; Psv.GSNPSID =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->core_global_regs->ghwcfg1; Psv.GHWCFG1 =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->core_global_regs->ghwcfg2; Psv.GHWCFG2 =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->core_global_regs->ghwcfg3; Psv.GHWCFG3 =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->core_global_regs->ghwcfg4; Psv.GHWCFG4 =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->core_global_regs->hptxfsiz; Psv.HPTXFSIZ =(uint32_t)dwc_read_reg32(addr); for (i=0; i< 5; i++) { addr=&_core_if->core_global_regs->dptxfsiz[i]; Psv.DPTXFSIZ[i] = (uint32_t)dwc_read_reg32(addr); } addr=&_core_if->dev_if->dev_global_regs->dcfg; Psv.DCFG =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->dev_if->dev_global_regs->dctl; Psv.DCTL =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->dev_if->dev_global_regs->dsts; Psv.DSTS =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->dev_if->dev_global_regs->diepmsk; Psv.DIEPMSK =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->dev_if->dev_global_regs->doepmsk; Psv.DOEPMSK =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->dev_if->dev_global_regs->daint; Psv.DAINT =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->dev_if->dev_global_regs->daintmsk; Psv.DAINTMSK =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->dev_if->dev_global_regs->dtknqr1; Psv.DTKNQR1 =(uint32_t)dwc_read_reg32(addr); if (_core_if->hwcfg2.b.dev_token_q_depth > 6) { addr=&_core_if->dev_if->dev_global_regs->dtknqr2; Psv.DTKNQR2 = (uint32_t)dwc_read_reg32(addr); } addr=&_core_if->dev_if->dev_global_regs->dvbusdis; Psv.DVBUSID =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->dev_if->dev_global_regs->dvbuspulse; Psv.DVBUSPULSE = (uint32_t)dwc_read_reg32(addr); if (_core_if->hwcfg2.b.dev_token_q_depth > 14) { addr=&_core_if->dev_if->dev_global_regs->dtknqr3; Psv.DTKNQR3 = (uint32_t) dwc_read_reg32(addr); } if (_core_if->hwcfg2.b.dev_token_q_depth > 22) { addr=&_core_if->dev_if->dev_global_regs->dtknqr4; Psv.DTKNQR4 = (uint32_t) dwc_read_reg32(addr); } for (i=0; i< _core_if->dev_if->num_eps; i++) { addr=&_core_if->dev_if->in_ep_regs[i]->diepctl; Psv.DIEPCTL[i] =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->dev_if->in_ep_regs[i]->diepint; Psv.DIEPINT[i] =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->dev_if->in_ep_regs[i]->dieptsiz; Psv.DIETSIZ[i] =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->dev_if->in_ep_regs[i]->diepdma; Psv.DIEPDMA[i] =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->dev_if->out_ep_regs[i]->doepctl; Psv.DOEPCTL[i] =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->dev_if->out_ep_regs[i]->doepfn; Psv.DOEPFN[i] =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->dev_if->out_ep_regs[i]->doepint; Psv.DOEPINT[i] =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->dev_if->out_ep_regs[i]->doeptsiz; Psv.DOETSIZ[i] =(uint32_t)dwc_read_reg32(addr); addr=&_core_if->dev_if->out_ep_regs[i]->doepdma; Psv.DOEPDMA[i] =(uint32_t)dwc_read_reg32(addr); } } #endif //--3M static int32_t dwc_otg_pcd_handle_out_ep_intr(dwc_otg_pcd_t *_pcd){ #define CLEAR_OUT_EP_INTR(__core_if,__epnum,__intr) \ do { \ doepint_data_t doepint = {.d32=0}; \ doepint.b.__intr = 1; \ dwc_write_reg32(&__core_if->dev_if->out_ep_regs[__epnum]->doepint, \ doepint.d32); \ } while (0) dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd); uint32_t ep_intr; doepint_data_t doepint = {.d32=0}; uint32_t epnum = 0; dwc_ep_t *dwc_ep; DWC_DEBUGPL(DBG_PCDV, "%s()\n", __func__); /* Read in the device interrupt bits */ ep_intr = dwc_otg_read_dev_all_out_ep_intr( core_if ); while( ep_intr ) { if (ep_intr&0x1) { /* Get EP pointer */ dwc_ep = &_pcd->ep[ epnum ].dwc_ep; #ifdef VERBOSE DWC_DEBUGPL(DBG_PCDV, "EP%d-%s: type=%d, mps=%d\n", dwc_ep->num, (dwc_ep->is_in ?"IN":"OUT"), dwc_ep->type, dwc_ep->maxpacket ); #endif doepint.d32 = dwc_otg_read_dev_out_ep_intr(core_if, dwc_ep); /* OUT Token Received when EndPoint is disabled */ if ( doepint.b.outtknepdis ) { DWC_DEBUGPL(DBG_PCD,"EP%d OUT Token Received when EndPoint is disabled\n", epnum); /* Clear the bit in DOEPINTn for this interrupt */ CLEAR_OUT_EP_INTR(core_if,epnum,outtknepdis); /* Currently do nothing */ } /* Transfer complete */ if ( doepint.b.xfercompl ) { DWC_DEBUGPL(DBG_PCD,"EP%d OUT Xfer Complete\n", epnum); /* Clear the bit in DOEPINTn for this interrupt */ CLEAR_OUT_EP_INTR(core_if,epnum,xfercompl); if (epnum == 0) { { depctl_data_t doepctl = {.d32=0}; doepctl.d32=dwc_read_reg32(&core_if->dev_if->out_ep_regs[0]->doepctl); doepctl.b.snak= 1; doepctl.b.cnak= 0; dwc_write_reg32(&core_if->dev_if->out_ep_regs[0]->doepctl, doepctl.d32); } handle_ep0( _pcd ,2); } else complete_ep( &_pcd->ep[ epnum ] ); } /* Endpoint disable */ if ( doepint.b.epdisabled ) { DWC_DEBUGPL(DBG_PCD,"EP%d OUT disabled\n", epnum); /* Clear the bit in DOEPINTn for this interrupt */ CLEAR_OUT_EP_INTR(core_if,epnum,epdisabled); } /* AHB Error */ if ( doepint.b.ahberr ) { DWC_DEBUGPL(DBG_PCD,"EP%d OUT AHB Error\n", epnum); CLEAR_OUT_EP_INTR(core_if,epnum,ahberr); } /* Setup Phase Done (contorl EPs) */ if ( doepint.b.setup ) { #ifdef DEBUG_EP0 DWC_DEBUGPL(DBG_PCD,"EP%d SETUP Done\n", epnum); #endif CLEAR_OUT_EP_INTR(core_if,epnum,setup); handle_ep0( _pcd ,0); // CLEAR_OUT_EP_INTR(core_if,epnum,setup); } } epnum++; ep_intr >>=1; } // set_dwc_otg_ep_out_timeout(_pcd); // Preserv(core_if); return 1; #undef CLEAR_OUT_EP_INTR } /** * Incomplete ISO IN Transfer Interrupt. * This interrupt indicates one of the following conditions occurred * while transmitting an ISOC transaction. * - Corrupted IN Token for ISOC EP. * - Packet not complete in FIFO. * The follow actions will be taken: * -# Determine the EP * -# Set incomplete flag in dwc_ep structure * -# Disable EP; when "Endpoint Disabled" interrupt is received * Flush FIFO */ int32_t dwc_otg_pcd_handle_incomplete_isoc_in_intr(dwc_otg_pcd_t *_pcd) { gintmsk_data_t intr_mask = { .d32 = 0}; gintsts_data_t gintsts; DWC_PRINT("INTERRUPT Handler not implemented for %s\n", "IN ISOC Incomplete"); intr_mask.b.incomplisoin = 1; dwc_modify_reg32( &GET_CORE_IF(_pcd)->core_global_regs->gintmsk, intr_mask.d32, 0 ); /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.incomplisoin = 1; dwc_write_reg32 (&GET_CORE_IF(_pcd)->core_global_regs->gintsts, gintsts.d32); return 1; } /** * Incomplete ISO OUT Transfer Interrupt. * * This interrupt indicates that the core has dropped an ISO OUT * packet. The following conditions can be the cause: * - FIFO Full, the entire packet would not fit in the FIFO. * - CRC Error * - Corrupted Token * The follow actions will be taken: * -# Determine the EP * -# Set incomplete flag in dwc_ep structure * -# Read any data from the FIFO * -# Disable EP. when "Endpoint Disabled" interrupt is received * re-enable EP. */ int32_t dwc_otg_pcd_handle_incomplete_isoc_out_intr(dwc_otg_pcd_t *_pcd) { /** @todo implement ISR */ gintmsk_data_t intr_mask = { .d32 = 0}; gintsts_data_t gintsts; DWC_PRINT("INTERRUPT Handler not implemented for %s\n", "OUT ISOC Incomplete"); intr_mask.b.incomplisoout = 1; dwc_modify_reg32( &GET_CORE_IF(_pcd)->core_global_regs->gintmsk, intr_mask.d32, 0 ); /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.incomplisoout = 1; dwc_write_reg32 (&GET_CORE_IF(_pcd)->core_global_regs->gintsts, gintsts.d32); return 1; } /** * This function handles the Global IN NAK Effective interrupt. * */ int32_t dwc_otg_pcd_handle_in_nak_effective( dwc_otg_pcd_t *_pcd ) { dwc_otg_dev_if_t *dev_if = GET_CORE_IF(_pcd)->dev_if; depctl_data_t diepctl = { .d32 = 0}; depctl_data_t diepctl_rd = { .d32 = 0}; gintmsk_data_t intr_mask = { .d32 = 0}; gintsts_data_t gintsts; int i,n=0,indis; DWC_DEBUGPL(DBG_PCD, "Global IN NAK Effective\n"); /* Disable the Global IN NAK Effective Interrupt */ intr_mask.b.ginnakeff = 1; dwc_modify_reg32( &GET_CORE_IF(_pcd)->core_global_regs->gintmsk, intr_mask.d32, 0); //if(_pcd->gnaksource>=1 && _pcd->gnaksource <=5) //printk(KERN_INFO "%s() %d Global IN NAK Effective SRC:%s NEXT:%d \n",__func__,__LINE__, //(_pcd->gnaksource==1)?"STOP":(_pcd->gnaksource==2)?"MISMATCH":(_pcd->gnaksource==3)?"TMOUTINT":(_pcd->gnaksource==4)?"CLK":"SWAP" //,_pcd->gnaknext); //else //printk(KERN_INFO "%s() %d Global IN NAK Effective SRC:UKNOWN(%d) NEXT:%d \n",__func__,__LINE__, //_pcd->gnaksource,_pcd->gnaknext); if(_pcd->gnaksource<1 || _pcd->gnaksource >5) printk(KERN_INFO "%s() %d Global IN NAK Effective SRC:UKNOWN(%d) NEXT:%d \n",__func__,__LINE__, _pcd->gnaksource,_pcd->gnaknext); /* Disable all active IN EPs */ diepctl.b.epdis = 1; diepctl.b.snak = 1; for (i=0; i < dev_if->num_eps; i++) { _pcd->ep[ i ].gnpdisabling=0; diepctl_rd.d32 = dwc_read_reg32(&dev_if->in_ep_regs[i]->diepctl); if (diepctl_rd.b.epena) { diepctl.d32=dwc_read_reg32( &dev_if->in_ep_regs[i]->diepctl ); diepctl.b.epdis = 1; diepctl.b.snak = 1; diepctl.b.epena = 0; diepctl.b.cnak = 0; dwc_write_reg32( &dev_if->in_ep_regs[i]->diepctl, diepctl.d32 ); _pcd->ep[ i ].gnpdisabling=1; indis=i; n++; } } if(n>0) { for (i=0; i < dev_if->num_eps; i++) { if(_pcd->ep[ i ].gnpdisabling) { diepctl_rd.d32 = dwc_read_reg32(&dev_if->in_ep_regs[i]->diepctl); if(diepctl_rd.b.epena) { diepctl.d32=dwc_read_reg32( &dev_if->in_ep_regs[i]->diepctl ); diepctl.b.epdis = 1; diepctl.b.epena = 0; if(diepctl_rd.b.naksts) diepctl.b.snak = 0; else diepctl.b.snak = 1; diepctl.b.cnak = 0; dwc_write_reg32( &dev_if->in_ep_regs[i]->diepctl, diepctl.d32 ); } } } } /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.ginnakeff = 1; dwc_write_reg32( &GET_CORE_IF(_pcd)->core_global_regs->gintsts, gintsts.d32); if(n==0 && _pcd->gnaksource>0 && _pcd->gnaknext>=0) { dctl_data_t dctl = {.d32=0}; int timeout ; dctl.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dctl); if(dctl.b.gnpinnaksts) { timeout = 10; dctl.d32 = 0; dctl.b.cgnpinnak = 1; dwc_modify_reg32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32); dctl.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dctl); while(dctl.b.gnpinnaksts) { if(timeout == 0) { printk("DCTL_GNPIN_NAK_STS_BIT not cleared in 10 usec...\n"); break; } timeout--; udelay(1); dctl.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dctl); } } for (i=0; i < dev_if->num_eps ; i++) { diepctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[i]->diepctl); diepctl.b.nextep=_pcd->gnaknext; dwc_write_reg32(&dev_if->in_ep_regs[i]->diepctl,diepctl.d32 ); } dwc_otg_jump_ep(GET_CORE_IF(_pcd), -1, _pcd->gnaknext); _pcd->ep[_pcd->gnaknext].stopped = 0; if(_pcd->gnaksource!=1) { //dwc_otg_ep_start_transfer(core_if, &_pcd->ep[_pcd->gnaknext].dwc_ep); //DisableRestartTask.data = (unsigned long)(&_pcd->ep[_pcd->gnaknext].dwc_ep); //tasklet_schedule(&DisableRestartTask); set_ep_restart_timer((unsigned long)(&_pcd->ep[_pcd->gnaknext].dwc_ep)); } else { clr_cin_holding(); restart_cin(); } } return 1; } /** * OUT NAK Effective. * */ int32_t dwc_otg_pcd_handle_out_nak_effective( dwc_otg_pcd_t *_pcd ) { gintmsk_data_t intr_mask = { .d32 = 0}; gintsts_data_t gintsts; DWC_PRINT("INTERRUPT Handler not implemented for %s\n", "Global IN NAK Effective\n"); /* Disable the Global IN NAK Effective Interrupt */ intr_mask.b.goutnakeff = 1; dwc_modify_reg32( &GET_CORE_IF(_pcd)->core_global_regs->gintmsk, intr_mask.d32, 0); /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.goutnakeff = 1; dwc_write_reg32 (&GET_CORE_IF(_pcd)->core_global_regs->gintsts, gintsts.d32); return 1; } /** * PCD interrupt handler. * * The PCD handles the device interrupts. Many conditions can cause a * device interrupt. When an interrupt occurs, the device interrupt * service routine determines the cause of the interrupt and * dispatches handling to the appropriate function. These interrupt * handling functions are described below. * * All interrupt registers are processed from LSB to MSB. * */ int32_t dwc_otg_pcd_handle_intr( dwc_otg_pcd_t *_pcd ) { dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd); #ifdef VERBOSE dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; #endif gintsts_data_t gintr_status; int32_t retval = 0; #ifdef VERBOSE DWC_DEBUGPL(DBG_ANY, "%s() gintsts=%08x gintmsk=%08x\n", __func__, dwc_read_reg32( &core_if->core_global_regs->gintsts), dwc_read_reg32( &core_if->core_global_regs->gintmsk) ); #endif SPIN_LOCK(&_pcd->lock); gintr_status.d32 =dwc_read_reg32( &core_if->core_global_regs->gintsts); gintr_status.d32&=dwc_read_reg32( &core_if->core_global_regs->gintmsk); if (!gintr_status.d32) { SPIN_UNLOCK(&_pcd->lock); return 0; } DWC_DEBUGPL(DBG_PCDV, "%s: gintsts&gintmsk=%08x\n", __func__, gintr_status.d32 ); if (gintr_status.b.sofintr) retval |= dwc_otg_pcd_handle_sof_intr( _pcd ); if (gintr_status.b.rxstsqlvl) retval |= dwc_otg_pcd_handle_rx_status_q_level_intr( _pcd ); if (gintr_status.b.nptxfempty) retval |= dwc_otg_pcd_handle_np_tx_fifo_empty_intr( _pcd ); if (gintr_status.b.ginnakeff) retval |= dwc_otg_pcd_handle_in_nak_effective( _pcd ); if (gintr_status.b.goutnakeff) retval |= dwc_otg_pcd_handle_out_nak_effective( _pcd ); if (gintr_status.b.i2cintr) retval |= dwc_otg_pcd_handle_i2c_intr( _pcd ); if (gintr_status.b.erlysuspend) retval |= dwc_otg_pcd_handle_early_suspend_intr( _pcd ); if (gintr_status.b.usbreset) { retval |= dwc_otg_pcd_handle_usb_reset_intr( _pcd ); usb_reset = 1; } if (gintr_status.b.enumdone) { if (usb_reset == 1) retval |= dwc_otg_pcd_handle_enum_done_intr( _pcd ); else retval |= dwc_otg_pcd_handle_i2c_intr( _pcd ); } if (gintr_status.b.isooutdrop) retval |= dwc_otg_pcd_handle_isoc_out_packet_dropped_intr( _pcd ); if (gintr_status.b.eopframe) retval |= dwc_otg_pcd_handle_end_periodic_frame_intr( _pcd ); if (gintr_status.b.epmismatch) retval |= dwc_otg_pcd_handle_ep_mismatch_intr( core_if ); if (gintr_status.b.inepint || gintr_status.b.outepintr) { if (gintr_status.b.inepint) retval |= dwc_otg_pcd_handle_in_ep_intr( _pcd ); else if (gintr_status.b.outepintr) retval |= dwc_otg_pcd_handle_out_ep_intr( _pcd ); } if (gintr_status.b.incomplisoin) retval |= dwc_otg_pcd_handle_incomplete_isoc_in_intr( _pcd ); if (gintr_status.b.incomplisoout) retval |= dwc_otg_pcd_handle_incomplete_isoc_out_intr( _pcd ); #ifdef VERBOSE DWC_DEBUGPL(DBG_PCDV, "%s() gintsts=%0x\n", __func__, dwc_read_reg32( &global_regs->gintsts)); #endif SPIN_UNLOCK(&_pcd->lock); return retval; } #endif /* DWC_IS_DEVICE */