?? dwc_otg_pcd_intr.c
字號:
/* ========================================================================== * * 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. * ========================================================================== *///#ifndef DWC_HOST_ONLY#if 0#include <linux/interrupt.h>#include <linux/dma-mapping.h>#include <linux/version.h>#include "dwc_otg_driver.h"#include "dwc_otg_pcd.h"#define DEBUG_EP0/* 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 void dwc_otg_pcd_update_otg( dwc_otg_pcd_t *_pcd, const unsigned _reset );/** @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_PHASE: strcpy(str,"EP0_IN_STATUS_PHASE"); break; case EP0_OUT_STATUS_PHASE: strcpy(str,"EP0_OUT_STATUS_PHASE"); break; case EP0_STALL: strcpy(str,"EP0_STALL"); break; default: strcpy(str,"EP0_INVALID"); } DWC_DEBUGPL(DBG_ANY, "%s(%d)\n", str, _pcd->ep0state);#endif}/** * This function returns pointer to in ep struct with number ep_num */static inline dwc_otg_pcd_ep_t* get_in_ep( dwc_otg_pcd_t *_pcd, uint32_t ep_num){ int i; int num_in_eps = GET_CORE_IF(_pcd)->dev_if->num_in_eps; if(ep_num == 0) { return &_pcd->ep0; } else { for(i = 0; i < num_in_eps; ++i) { if(_pcd->in_ep[i].dwc_ep.num == ep_num) return &_pcd->in_ep[i]; } return 0; }}/** * This function returns pointer to out ep struct with number ep_num */static inline dwc_otg_pcd_ep_t* get_out_ep( dwc_otg_pcd_t *_pcd, uint32_t ep_num){ int i; int num_out_eps = GET_CORE_IF(_pcd)->dev_if->num_out_eps; if(ep_num == 0) { return &_pcd->ep0; } else { for(i = 0; i < num_out_eps; ++i) { if(_pcd->out_ep[i].dwc_ep.num == ep_num) return &_pcd->out_ep[i]; } return 0; }}/** * 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->ep0; 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); /* Setup and start the Transfer */ _ep->dwc_ep.start_xfer_buff = req->req.buf; _ep->dwc_ep.xfer_buff = req->req.buf; _ep->dwc_ep.xfer_len = req->req.length; _ep->dwc_ep.xfer_count = 0; _ep->dwc_ep.dma_addr = req->req.dma; _ep->dwc_ep.sent_zlp = 0; _ep->dwc_ep.total_len = _ep->dwc_ep.xfer_len; if(req->req.zero) { if((_ep->dwc_ep.xfer_len % _ep->dwc_ep.maxpacket == 0) && (_ep->dwc_ep.xfer_len != 0)) { _ep->dwc_ep.sent_zlp = 1; } } //DWC_ERROR(" -> starting transfer (start_next_req) %s %s\n", //_ep->ep.name, _ep->dwc_ep.is_in?"IN":"OUT"); 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; //DWC_DEBUGPL(DBG_PCD, "SOF\n"); /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.sofintr = 1; dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32); return 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(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; gintmsk_data_t gintmask = {.d32=0}; device_grxsts_data_t status; dwc_otg_pcd_ep_t *ep; gintsts_data_t gintsts;#ifdef DEBUG static char *dpid_str[] ={ "D0", "D2", "D1", "MDATA" };#endif //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 = get_out_ep(_pcd, 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"); 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; } 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 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 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); /* 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. * */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_dthrctl; } 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;}/** * 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; dwc_otg_dev_in_ep_regs_t *ep_regs; gnptxsts_data_t txstatus = {.d32 = 0}; gintsts_data_t gintsts; 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 = get_in_ep(_pcd, 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, 0 ); 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)); /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.nptxfempty = 1; dwc_write_reg32 (&global_regs->gintsts, gintsts.d32); return 1;}/** * This function is called when dedicated Tx FIFO Empty interrupt occurs. * The active request is checked for the next packet to be loaded into * apropriate Tx FIFO. */static int32_t write_empty_tx_fifo(dwc_otg_pcd_t *_pcd, uint32_t epnum){ dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -