| /* ========================================================================== |
| * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_intr.c $ |
| * $Revision: #113 $ |
| * $Date: 2011/10/24 $ |
| * $Change: 1871160 $ |
| * |
| * 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 |
| |
| #include "dwc_otg_pcd.h" |
| |
| #ifdef DWC_UTE_CFI |
| #include "dwc_otg_cfi.h" |
| #endif |
| |
| #ifdef DWC_UTE_PER_IO |
| extern void complete_xiso_ep(dwc_otg_pcd_ep_t * ep); |
| #endif |
| //#define PRINT_CFI_DMA_DESCS |
| |
| #define DEBUG_EP0 |
| |
| /** |
| * This function updates OTG. |
| */ |
| static void dwc_otg_pcd_update_otg(dwc_otg_pcd_t * pcd, const unsigned reset) |
| { |
| |
| if (reset) { |
| pcd->b_hnp_enable = 0; |
| pcd->a_hnp_support = 0; |
| pcd->a_alt_hnp_support = 0; |
| } |
| |
| if (pcd->fops->hnp_changed) { |
| pcd->fops->hnp_changed(pcd); |
| } |
| } |
| |
| /** @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: |
| dwc_strcpy(str, "EP0_DISCONNECT"); |
| break; |
| case EP0_IDLE: |
| dwc_strcpy(str, "EP0_IDLE"); |
| break; |
| case EP0_IN_DATA_PHASE: |
| dwc_strcpy(str, "EP0_IN_DATA_PHASE"); |
| break; |
| case EP0_OUT_DATA_PHASE: |
| dwc_strcpy(str, "EP0_OUT_DATA_PHASE"); |
| break; |
| case EP0_IN_STATUS_PHASE: |
| dwc_strcpy(str, "EP0_IN_STATUS_PHASE"); |
| break; |
| case EP0_OUT_STATUS_PHASE: |
| dwc_strcpy(str, "EP0_OUT_STATUS_PHASE"); |
| break; |
| case EP0_STALL: |
| dwc_strcpy(str, "EP0_STALL"); |
| break; |
| default: |
| dwc_strcpy(str, "EP0_INVALID"); |
| } |
| |
| DWC_DEBUGPL(DBG_ANY, "%s(%d)\n", str, pcd->ep0state); |
| #endif |
| } |
| |
| /** |
| * This function calculate the size of the payload in the memory |
| * for out endpoints and prints size for debug purposes(used in |
| * 2.93a DevOutNak feature). |
| */ |
| static inline void print_memory_payload(dwc_otg_pcd_t * pcd, dwc_ep_t * ep) |
| { |
| #ifdef DEBUG |
| deptsiz_data_t deptsiz_init = {.d32 = 0 }; |
| deptsiz_data_t deptsiz_updt = {.d32 = 0 }; |
| int pack_num; |
| unsigned payload; |
| |
| deptsiz_init.d32 = pcd->core_if->start_doeptsiz_val[ep->num]; |
| deptsiz_updt.d32 = |
| DWC_READ_REG32(&pcd->core_if->dev_if-> |
| out_ep_regs[ep->num]->doeptsiz); |
| /* Payload will be */ |
| payload = deptsiz_init.b.xfersize - deptsiz_updt.b.xfersize; |
| /* Packet count is decremented every time a packet |
| * is written to the RxFIFO not in to the external memory |
| * So, if payload == 0, then it means no packet was sent to ext memory*/ |
| pack_num = (!payload) ? 0 : (deptsiz_init.b.pktcnt - deptsiz_updt.b.pktcnt); |
| DWC_DEBUGPL(DBG_PCDV, |
| "Payload for EP%d-%s\n", |
| ep->num, (ep->is_in ? "IN" : "OUT")); |
| DWC_DEBUGPL(DBG_PCDV, |
| "Number of transfered bytes = 0x%08x\n", payload); |
| DWC_DEBUGPL(DBG_PCDV, |
| "Number of transfered packets = %d\n", pack_num); |
| #endif |
| } |
| |
| |
| #ifdef DWC_UTE_CFI |
| static inline void print_desc(struct dwc_otg_dma_desc *ddesc, |
| const uint8_t * epname, int descnum) |
| { |
| CFI_INFO |
| ("%s DMA_DESC(%d) buf=0x%08x bytes=0x%04x; sp=0x%x; l=0x%x; sts=0x%02x; bs=0x%02x\n", |
| epname, descnum, ddesc->buf, ddesc->status.b.bytes, |
| ddesc->status.b.sp, ddesc->status.b.l, ddesc->status.b.sts, |
| ddesc->status.b.bs); |
| } |
| #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. |
| */ |
| dwc_otg_pcd_ep_t *get_ep_by_addr(dwc_otg_pcd_t * pcd, u16 wIndex) |
| { |
| dwc_otg_pcd_ep_t *ep; |
| uint32_t ep_num = UE_GET_ADDR(wIndex); |
| |
| if (ep_num == 0) { |
| ep = &pcd->ep0; |
| } else if (UE_GET_DIR(wIndex) == UE_DIR_IN) { /* in ep */ |
| ep = &pcd->in_ep[ep_num - 1]; |
| } else { |
| ep = &pcd->out_ep[ep_num - 1]; |
| } |
| |
| return ep; |
| } |
| |
| /** |
| * 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; |
| uint32_t max_transfer = |
| GET_CORE_IF(ep->pcd)->core_params->max_transfer_size; |
| |
| #ifdef DWC_UTE_CFI |
| struct dwc_otg_pcd *pcd; |
| pcd = ep->pcd; |
| #endif |
| |
| if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { |
| req = DWC_CIRCLEQ_FIRST(&ep->queue); |
| |
| #ifdef DWC_UTE_CFI |
| if (ep->dwc_ep.buff_mode != BM_STANDARD) { |
| ep->dwc_ep.cfi_req_len = req->length; |
| pcd->cfi->ops.build_descriptors(pcd->cfi, pcd, ep, req); |
| } else { |
| #endif |
| /* Setup and start the Transfer */ |
| if (req->dw_align_buf) { |
| ep->dwc_ep.dma_addr = req->dw_align_buf_dma; |
| ep->dwc_ep.start_xfer_buff = req->dw_align_buf; |
| ep->dwc_ep.xfer_buff = req->dw_align_buf; |
| } else { |
| ep->dwc_ep.dma_addr = req->dma; |
| ep->dwc_ep.start_xfer_buff = req->buf; |
| ep->dwc_ep.xfer_buff = req->buf; |
| } |
| ep->dwc_ep.sent_zlp = 0; |
| ep->dwc_ep.total_len = req->length; |
| ep->dwc_ep.xfer_len = 0; |
| ep->dwc_ep.xfer_count = 0; |
| |
| ep->dwc_ep.maxxfer = max_transfer; |
| if (GET_CORE_IF(ep->pcd)->dma_desc_enable) { |
| uint32_t out_max_xfer = DDMA_MAX_TRANSFER_SIZE |
| - (DDMA_MAX_TRANSFER_SIZE % 4); |
| if (ep->dwc_ep.is_in) { |
| if (ep->dwc_ep.maxxfer > |
| DDMA_MAX_TRANSFER_SIZE) { |
| ep->dwc_ep.maxxfer = |
| DDMA_MAX_TRANSFER_SIZE; |
| } |
| } else { |
| if (ep->dwc_ep.maxxfer > out_max_xfer) { |
| ep->dwc_ep.maxxfer = |
| out_max_xfer; |
| } |
| } |
| } |
| if (ep->dwc_ep.maxxfer < ep->dwc_ep.total_len) { |
| ep->dwc_ep.maxxfer -= |
| (ep->dwc_ep.maxxfer % ep->dwc_ep.maxpacket); |
| } |
| if (req->sent_zlp) { |
| if ((ep->dwc_ep.total_len % |
| ep->dwc_ep.maxpacket == 0) |
| && (ep->dwc_ep.total_len != 0)) { |
| ep->dwc_ep.sent_zlp = 1; |
| } |
| |
| } |
| #ifdef DWC_UTE_CFI |
| } |
| #endif |
| dwc_otg_ep_start_transfer(GET_CORE_IF(ep->pcd), &ep->dwc_ep); |
| } else if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) { |
| DWC_PRINTF("There are no more ISOC requests \n"); |
| ep->dwc_ep.frame_num = 0xFFFFFFFF; |
| } |
| } |
| |
| /** |
| * 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.bmRequestType, |
| pcd->setup_pkt->req.bRequest, |
| UGETW(pcd->setup_pkt->req.wValue), |
| UGETW(pcd->setup_pkt->req.wIndex), |
| UGETW(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: %d \n", 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); |
| dwc_otg_dev_if_t *dev_if = core_if->dev_if; |
| dwc_otg_dev_in_ep_regs_t *ep_regs; |
| dtxfsts_data_t txstatus = {.d32 = 0 }; |
| dwc_otg_pcd_ep_t *ep = 0; |
| uint32_t len = 0; |
| int dwords; |
| |
| ep = get_in_ep(pcd, epnum); |
| |
| DWC_DEBUGPL(DBG_PCD, "Dedicated TxFifo Empty: %d \n", 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(&dev_if->in_ep_regs[epnum]->dtxfsts); |
| DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum, txstatus.d32); |
| |
| while (txstatus.b.txfspcavail > dwords && |
| ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len && |
| ep->dwc_ep.xfer_len != 0) { |
| /* 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(&dev_if->in_ep_regs[epnum]->dtxfsts); |
| DWC_DEBUGPL(DBG_PCDV, "dtxfsts[%d]=0x%08x\n", epnum, |
| txstatus.d32); |
| } |
| |
| DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum, |
| DWC_READ_REG32(&dev_if->in_ep_regs[epnum]->dtxfsts)); |
| |
| 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, num_in_eps, num_out_eps; |
| dwc_otg_pcd_ep_t *ep; |
| |
| gintmsk_data_t intr_mask = {.d32 = 0 }; |
| |
| DWC_SPINLOCK(pcd->lock); |
| |
| num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps; |
| num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps; |
| |
| 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__); |
| DWC_SPINUNLOCK(pcd->lock); |
| return; |
| } |
| pcd->ep0state = EP0_DISCONNECT; |
| |
| /* Reset the OTG state. */ |
| dwc_otg_pcd_update_otg(pcd, 1); |
| |
| /* 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 */ |
| /**@todo NGS Flush Periodic 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 */ |
| ep = &pcd->ep0; |
| dwc_otg_request_nuke(ep); |
| /* prevent new request submissions, kill any outstanding requests */ |
| for (i = 0; i < num_in_eps; i++) { |
| dwc_otg_pcd_ep_t *ep = &pcd->in_ep[i]; |
| dwc_otg_request_nuke(ep); |
| } |
| /* prevent new request submissions, kill any outstanding requests */ |
| for (i = 0; i < num_out_eps; i++) { |
| dwc_otg_pcd_ep_t *ep = &pcd->out_ep[i]; |
| dwc_otg_request_nuke(ep); |
| } |
| |
| /* report disconnect; the driver is already quiesced */ |
| if (pcd->fops->disconnect) { |
| DWC_SPINUNLOCK(pcd->lock); |
| pcd->fops->disconnect(pcd); |
| DWC_SPINLOCK(pcd->lock); |
| } |
| DWC_SPINUNLOCK(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; |
| |
| DWC_PRINTF("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); |
| 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_PRINTF("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. |
| */ |
| static inline 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 }; |
| dwc_otg_dev_dma_desc_t *dma_desc; |
| depctl_data_t doepctl = {.d32 = 0 }; |
| |
| #ifdef VERBOSE |
| DWC_DEBUGPL(DBG_PCDV, "%s() doepctl0=%0x\n", __func__, |
| DWC_READ_REG32(&dev_if->out_ep_regs[0]->doepctl)); |
| #endif |
| |
| doeptsize0.b.supcnt = 3; |
| doeptsize0.b.pktcnt = 1; |
| doeptsize0.b.xfersize = 8 * 3; |
| |
| if (core_if->dma_enable) { |
| if (!core_if->dma_desc_enable) { |
| /** put here as for Hermes mode deptisz register should not be written */ |
| DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doeptsiz, |
| doeptsize0.d32); |
| |
| /** @todo dma needs to handle multiple setup packets (up to 3) */ |
| DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doepdma, |
| pcd->setup_pkt_dma_handle); |
| } else { |
| dev_if->setup_desc_index = |
| (dev_if->setup_desc_index + 1) & 1; |
| dma_desc = |
| dev_if->setup_desc_addr[dev_if->setup_desc_index]; |
| |
| /** DMA Descriptor Setup */ |
| dma_desc->status.b.bs = BS_HOST_BUSY; |
| dma_desc->status.b.l = 1; |
| dma_desc->status.b.ioc = 1; |
| dma_desc->status.b.bytes = pcd->ep0.dwc_ep.maxpacket; |
| dma_desc->buf = pcd->setup_pkt_dma_handle; |
| dma_desc->status.b.sts = 0; |
| dma_desc->status.b.bs = BS_HOST_READY; |
| |
| /** DOEPDMA0 Register write */ |
| DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doepdma, |
| dev_if-> |
| dma_setup_desc_addr |
| [dev_if->setup_desc_index]); |
| } |
| |
| } else { |
| /** put here as for Hermes mode deptisz register should not be written */ |
| DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doeptsiz, |
| doeptsize0.d32); |
| } |
| |
| /** DOEPCTL0 Register write */ |
| doepctl.b.epena = 1; |
| doepctl.b.cnak = 1; |
| DWC_WRITE_REG32(&dev_if->out_ep_regs[0]->doepctl, doepctl.d32); |
| |
| #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 }; |
| depctl_data_t diepctl = {.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; |
| pcgcctl_data_t power = {.d32 = 0 }; |
| |
| power.d32 = DWC_READ_REG32(core_if->pcgcctl); |
| if (power.b.stoppclk) { |
| power.d32 = 0; |
| power.b.stoppclk = 1; |
| DWC_MODIFY_REG32(core_if->pcgcctl, power.d32, 0); |
| |
| power.b.pwrclmp = 1; |
| DWC_MODIFY_REG32(core_if->pcgcctl, power.d32, 0); |
| |
| power.b.rstpdwnmodule = 1; |
| DWC_MODIFY_REG32(core_if->pcgcctl, power.d32, 0); |
| } |
| |
| core_if->lx_state = DWC_OTG_L0; |
| |
| DWC_PRINTF("USB RESET\n"); |
| #ifdef DWC_EN_ISOC |
| for (i = 1; i < 16; ++i) { |
| dwc_otg_pcd_ep_t *ep; |
| dwc_ep_t *dwc_ep; |
| ep = get_in_ep(pcd, i); |
| if (ep != 0) { |
| dwc_ep = &ep->dwc_ep; |
| dwc_ep->next_frame = 0xffffffff; |
| } |
| } |
| #endif /* DWC_EN_ISOC */ |
| |
| /* reset the HNP settings */ |
| dwc_otg_pcd_update_otg(pcd, 1); |
| |
| /* 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_out_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); |
| |
| if (!core_if->core_params->en_multiple_tx_fifo && core_if->dma_enable) { |
| core_if->start_predict = 0; |
| for (i = 0; i<= core_if->dev_if->num_in_eps; ++i) { |
| core_if->nextep_seq[i] = 0xff; // 0xff - EP not active |
| } |
| core_if->nextep_seq[0] = 0; |
| core_if->first_in_nextep_seq = 0; |
| diepctl.d32 = DWC_READ_REG32(&dev_if->in_ep_regs[0]->diepctl); |
| diepctl.b.nextep = 0; |
| DWC_WRITE_REG32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32); |
| |
| /* Update IN Endpoint Mismatch Count by active IN NP EP count + 1 */ |
| dcfg.d32 = DWC_READ_REG32(&dev_if->dev_global_regs->dcfg); |
| dcfg.b.epmscnt = 2; |
| DWC_WRITE_REG32(&dev_if->dev_global_regs->dcfg, dcfg.d32); |
| |
| DWC_DEBUGPL(DBG_PCDV,"%s first_in_nextep_seq= %2d; nextep_seq[]:\n", |
| __func__, core_if->first_in_nextep_seq); |
| for (i=0; i <= core_if->dev_if->num_in_eps; i++) { |
| DWC_DEBUGPL(DBG_PCDV, "%2d\n", core_if->nextep_seq[i]); |
| } |
| } |
| |
| if (core_if->multiproc_int_enable) { |
| daintmsk.b.inep0 = 1; |
| daintmsk.b.outep0 = 1; |
| DWC_WRITE_REG32(&dev_if->dev_global_regs->deachintmsk, |
| daintmsk.d32); |
| |
| doepmsk.b.setup = 1; |
| doepmsk.b.xfercompl = 1; |
| doepmsk.b.ahberr = 1; |
| doepmsk.b.epdisabled = 1; |
| |
| if (core_if->dma_desc_enable) { |
| doepmsk.b.stsphsercvd = 1; |
| doepmsk.b.bna = 1; |
| } |
| /* |
| doepmsk.b.babble = 1; |
| doepmsk.b.nyet = 1; |
| |
| if (core_if->dma_enable) { |
| doepmsk.b.nak = 1; |
| } |
| */ |
| DWC_WRITE_REG32(&dev_if->dev_global_regs->doepeachintmsk[0], |
| doepmsk.d32); |
| |
| diepmsk.b.xfercompl = 1; |
| diepmsk.b.timeout = 1; |
| diepmsk.b.epdisabled = 1; |
| diepmsk.b.ahberr = 1; |
| diepmsk.b.intknepmis = 1; |
| if (!core_if->en_multiple_tx_fifo && core_if->dma_enable) |
| diepmsk.b.intknepmis = 0; |
| |
| /* if (core_if->dma_desc_enable) { |
| diepmsk.b.bna = 1; |
| } |
| */ |
| /* |
| if (core_if->dma_enable) { |
| diepmsk.b.nak = 1; |
| } |
| */ |
| DWC_WRITE_REG32(&dev_if->dev_global_regs->diepeachintmsk[0], |
| diepmsk.d32); |
| } else { |
| 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; |
| |
| if (core_if->dma_desc_enable) { |
| doepmsk.b.stsphsercvd = 1; |
| doepmsk.b.bna = 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; |
| if (!core_if->en_multiple_tx_fifo && core_if->dma_enable) |
| diepmsk.b.intknepmis = 0; |
| /* |
| if (core_if->dma_desc_enable) { |
| diepmsk.b.bna = 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; |
| int speed = 0; |
| 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->ep0; |
| gintsts_data_t gintsts; |
| gusbcfg_data_t gusbcfg; |
| dwc_otg_core_global_regs_t *global_regs = |
| GET_CORE_IF(pcd)->core_global_regs; |
| uint8_t utmi16b, utmi8b; |
| int speed; |
| DWC_DEBUGPL(DBG_PCD, "SPEED ENUM\n"); |
| |
| if (GET_CORE_IF(pcd)->snpsid >= OTG_CORE_REV_2_60a) { |
| utmi16b = 6; //vahrama old value was 6; |
| utmi8b = 9; |
| } else { |
| utmi16b = 4; |
| utmi8b = 8; |
| } |
| 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; |
| |
| speed = get_device_speed(GET_CORE_IF(pcd)); |
| pcd->fops->connect(pcd, speed); |
| |
| /* Set USB turnaround time based on device speed and PHY interface. */ |
| gusbcfg.d32 = DWC_READ_REG32(&global_regs->gusbcfg); |
| if (speed == USB_SPEED_HIGH) { |
| if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type == |
| DWC_HWCFG2_HS_PHY_TYPE_ULPI) { |
| /* ULPI interface */ |
| gusbcfg.b.usbtrdtim = 9; |
| } |
| if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type == |
| DWC_HWCFG2_HS_PHY_TYPE_UTMI) { |
| /* UTMI+ interface */ |
| if (GET_CORE_IF(pcd)->hwcfg4.b.utmi_phy_data_width == 0) { |
| gusbcfg.b.usbtrdtim = utmi8b; |
| } else if (GET_CORE_IF(pcd)->hwcfg4. |
| b.utmi_phy_data_width == 1) { |
| gusbcfg.b.usbtrdtim = utmi16b; |
| } else if (GET_CORE_IF(pcd)-> |
| core_params->phy_utmi_width == 8) { |
| gusbcfg.b.usbtrdtim = utmi8b; |
| } else { |
| gusbcfg.b.usbtrdtim = utmi16b; |
| } |
| } |
| if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type == |
| DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI) { |
| /* UTMI+ OR ULPI interface */ |
| if (gusbcfg.b.ulpi_utmi_sel == 1) { |
| /* ULPI interface */ |
| gusbcfg.b.usbtrdtim = 9; |
| } else { |
| /* UTMI+ interface */ |
| if (GET_CORE_IF(pcd)-> |
| core_params->phy_utmi_width == 16) { |
| gusbcfg.b.usbtrdtim = utmi16b; |
| } else { |
| gusbcfg.b.usbtrdtim = utmi8b; |
| } |
| } |
| } |
| } else { |
| /* Full or low speed */ |
| gusbcfg.b.usbtrdtim = 9; |
| } |
| 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_WARN("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; |
| DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "EOP"); |
| |
| intr_mask.b.eopframe = 1; |
| DWC_MODIFY_REG32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, |
| intr_mask.d32, 0); |
| |
| /* 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_pcd_t * pcd) |
| { |
| gintsts_data_t gintsts; |
| dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); |
| dctl_data_t dctl; |
| gintmsk_data_t intr_mask = {.d32 = 0 }; |
| |
| if (!core_if->en_multiple_tx_fifo && core_if->dma_enable) { |
| core_if->start_predict = 1; |
| |
| DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, core_if); |
| |
| gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); |
| if (!gintsts.b.ginnakeff) { |
| /* Disable EP Mismatch interrupt */ |
| intr_mask.d32 = 0; |
| intr_mask.b.epmismatch = 1; |
| DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, intr_mask.d32, 0); |
| /* Enable the Global IN NAK Effective Interrupt */ |
| intr_mask.d32 = 0; |
| intr_mask.b.ginnakeff = 1; |
| DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, intr_mask.d32); |
| /* Set the global non-periodic IN NAK handshake */ |
| dctl.d32 = DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl); |
| dctl.b.sgnpinnak = 1; |
| DWC_WRITE_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32); |
| } else { |
| DWC_PRINTF("gintsts.b.ginnakeff = 1! dctl.b.sgnpinnak not set\n"); |
| } |
| /* Disabling of all EP's will be done in dwc_otg_pcd_handle_in_nak_effective() |
| * handler after Global IN NAK Effective interrupt will be asserted */ |
| } |
| /* Clear interrupt */ |
| gintsts.d32 = 0; |
| gintsts.b.epmismatch = 1; |
| DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, gintsts.d32); |
| |
| return 1; |
| } |
| |
| /** |
| * This interrupt is valid only in DMA mode. This interrupt indicates that the |
| * core has stopped fetching data for IN endpoints due to the unavailability of |
| * TxFIFO space or Request Queue space. This interrupt is used by the |
| * application for an endpoint mismatch algorithm. |
| * |
| * @param pcd The PCD |
| */ |
| int32_t dwc_otg_pcd_handle_ep_fetsusp_intr(dwc_otg_pcd_t * pcd) |
| { |
| gintsts_data_t gintsts; |
| gintmsk_data_t gintmsk_data; |
| dctl_data_t dctl; |
| dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); |
| DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, core_if); |
| |
| /* Clear the global non-periodic IN NAK handshake */ |
| dctl.d32 = 0; |
| dctl.b.cgnpinnak = 1; |
| DWC_MODIFY_REG32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32); |
| |
| /* Mask GINTSTS.FETSUSP interrupt */ |
| gintmsk_data.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk); |
| gintmsk_data.b.fetsusp = 0; |
| DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk_data.d32); |
| |
| /* Clear interrupt */ |
| gintsts.d32 = 0; |
| gintsts.b.fetsusp = 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->ep0; |
| usb_device_request_t *ctrl = &pcd->setup_pkt->req; |
| DWC_WARN("req %02x.%02x protocol STALL; err %d\n", |
| ctrl->bmRequestType, ctrl->bRequest, err_val); |
| |
| ep0->dwc_ep.is_in = 1; |
| dwc_otg_ep_set_stall(GET_CORE_IF(pcd), &ep0->dwc_ep); |
| pcd->ep0.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, |
| usb_device_request_t * ctrl) |
| { |
| int ret = 0; |
| DWC_SPINUNLOCK(pcd->lock); |
| ret = pcd->fops->setup(pcd, (uint8_t *) ctrl); |
| DWC_SPINLOCK(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; |
| } |
| } |
| |
| #ifdef DWC_UTE_CFI |
| /** |
| * This functions delegates the CFI setup commands to the gadget driver. |
| * This function will return a negative value to indicate a failure. |
| */ |
| static inline int cfi_gadget_setup(dwc_otg_pcd_t * pcd, |
| struct cfi_usb_ctrlrequest *ctrl_req) |
| { |
| int ret = 0; |
| |
| if (pcd->fops && pcd->fops->cfi_setup) { |
| DWC_SPINUNLOCK(pcd->lock); |
| ret = pcd->fops->cfi_setup(pcd, ctrl_req); |
| DWC_SPINLOCK(pcd->lock); |
| if (ret < 0) { |
| ep0_do_stall(pcd, ret); |
| return ret; |
| } |
| } |
| |
| return ret; |
| } |
| #endif |
| |
| /** |
| * This function starts the Zero-Length Packet for the IN status phase |
| * of a 2 stage control transfer. |
| */ |
| static inline void do_setup_in_status_phase(dwc_otg_pcd_t * pcd) |
| { |
| dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; |
| if (pcd->ep0state == EP0_STALL) { |
| return; |
| } |
| |
| pcd->ep0state = EP0_IN_STATUS_PHASE; |
| |
| /* Prepare for more SETUP Packets */ |
| 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.dma_addr = pcd->setup_pkt_dma_handle; |
| dwc_otg_ep0_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. |
| */ |
| static inline void do_setup_out_status_phase(dwc_otg_pcd_t * pcd) |
| { |
| dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; |
| if (pcd->ep0state == EP0_STALL) { |
| DWC_DEBUGPL(DBG_PCD, "EP0 STALLED\n"); |
| return; |
| } |
| pcd->ep0state = EP0_OUT_STATUS_PHASE; |
| |
| 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.dma_addr = pcd->setup_pkt_dma_handle; |
| dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep); |
| |
| /* Prepare for more SETUP Packets */ |
| if (GET_CORE_IF(pcd)->dma_enable == 0) { |
| 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) |
| { |
| if (ep->dwc_ep.stall_clear_flag == 0) |
| 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. */ |
| |
| /* |
| * Above fixme is solved by implmenting a tasklet to call the |
| * start_next_request(), outside of interrupt context at some |
| * time after the current time, after a clear-halt setup packet. |
| * Still need to implement ep mismatch in the future if a gadget |
| * ever uses more than one endpoint at once |
| */ |
| ep->queue_sof = 1; |
| DWC_TASK_SCHEDULE(pcd->start_xfer_tasklet); |
| } |
| /* 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! |
| * |
| */ |
| void do_test_mode(void *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 GET_STATUS Setup Commands. |
| */ |
| static inline void do_get_status(dwc_otg_pcd_t * pcd) |
| { |
| usb_device_request_t ctrl = pcd->setup_pkt->req; |
| dwc_otg_pcd_ep_t *ep; |
| dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; |
| uint16_t *status = pcd->status_buf; |
| dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); |
| |
| #ifdef DEBUG_EP0 |
| DWC_DEBUGPL(DBG_PCD, |
| "GET_STATUS %02x.%02x v%04x i%04x l%04x\n", |
| ctrl.bmRequestType, ctrl.bRequest, |
| UGETW(ctrl.wValue), UGETW(ctrl.wIndex), |
| UGETW(ctrl.wLength)); |
| #endif |
| |
| switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) { |
| case UT_DEVICE: |
| if(UGETW(ctrl.wIndex) == 0xF000) { /* OTG Status selector */ |
| DWC_PRINTF("wIndex - %d\n", UGETW(ctrl.wIndex)); |
| DWC_PRINTF("OTG VERSION - %d\n", core_if->otg_ver); |
| DWC_PRINTF("OTG CAP - %d, %d\n", core_if->core_params->otg_cap, |
| DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE); |
| if(core_if->otg_ver == 1 && |
| core_if->core_params->otg_cap == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) { |
| uint8_t *otgsts = (uint8_t*)pcd->status_buf; |
| *otgsts = (core_if->otg_sts & 0x1); |
| pcd->ep0_pending = 1; |
| ep0->dwc_ep.start_xfer_buff = (uint8_t *) otgsts; |
| ep0->dwc_ep.xfer_buff = (uint8_t *) otgsts; |
| ep0->dwc_ep.dma_addr = pcd->status_buf_dma_handle; |
| ep0->dwc_ep.xfer_len = 1; |
| ep0->dwc_ep.xfer_count = 0; |
| ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len; |
| dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep); |
| return; |
| } else { |
| ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); |
| return; |
| } |
| break; |
| } else { |
| *status = 0x1; /* Self powered */ |
| *status |= pcd->remote_wakeup_enable << 1; |
| break; |
| } |
| case UT_INTERFACE: |
| *status = 0; |
| break; |
| |
| case UT_ENDPOINT: |
| ep = get_ep_by_addr(pcd, UGETW(ctrl.wIndex)); |
| if (ep == 0 || UGETW(ctrl.wLength) > 2) { |
| ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); |
| return; |
| } |
| /** @todo check for EP stall */ |
| *status = ep->stopped; |
| break; |
| } |
| pcd->ep0_pending = 1; |
| ep0->dwc_ep.start_xfer_buff = (uint8_t *) status; |
| ep0->dwc_ep.xfer_buff = (uint8_t *) status; |
| ep0->dwc_ep.dma_addr = pcd->status_buf_dma_handle; |
| ep0->dwc_ep.xfer_len = 2; |
| ep0->dwc_ep.xfer_count = 0; |
| ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len; |
| dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep); |
| } |
| |
| /** |
| * 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; |
| usb_device_request_t ctrl = pcd->setup_pkt->req; |
| dwc_otg_pcd_ep_t *ep = 0; |
| int32_t otg_cap_param = core_if->core_params->otg_cap; |
| gotgctl_data_t gotgctl = {.d32 = 0 }; |
| |
| DWC_DEBUGPL(DBG_PCD, "SET_FEATURE:%02x.%02x v%04x i%04x l%04x\n", |
| ctrl.bmRequestType, ctrl.bRequest, |
| UGETW(ctrl.wValue), UGETW(ctrl.wIndex), |
| UGETW(ctrl.wLength)); |
| DWC_DEBUGPL(DBG_PCD, "otg_cap=%d\n", otg_cap_param); |
| |
| switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) { |
| case UT_DEVICE: |
| switch (UGETW(ctrl.wValue)) { |
| case UF_DEVICE_REMOTE_WAKEUP: |
| pcd->remote_wakeup_enable = 1; |
| break; |
| |
| case UF_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 = UGETW(ctrl.wIndex) >> 8; |
| DWC_TASK_SCHEDULE(pcd->test_mode_tasklet); |
| break; |
| |
| case UF_DEVICE_B_HNP_ENABLE: |
| DWC_DEBUGPL(DBG_PCDV, |
| "SET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n"); |
| |
| /* dev may initiate HNP */ |
| if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) { |
| pcd->b_hnp_enable = 1; |
| dwc_otg_pcd_update_otg(pcd, 0); |
| DWC_DEBUGPL(DBG_PCD, "Request B HNP\n"); |
| /**@todo Is the gotgctl.devhnpen cleared |
| * by a USB Reset? */ |
| gotgctl.b.devhnpen = 1; |
| gotgctl.b.hnpreq = 1; |
| DWC_WRITE_REG32(&global_regs->gotgctl, |
| gotgctl.d32); |
| } else { |
| ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); |
| return; |
| } |
| break; |
| |
| case UF_DEVICE_A_HNP_SUPPORT: |
| /* RH port supports HNP */ |
| DWC_DEBUGPL(DBG_PCDV, |
| "SET_FEATURE: USB_DEVICE_A_HNP_SUPPORT\n"); |
| if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) { |
| pcd->a_hnp_support = 1; |
| dwc_otg_pcd_update_otg(pcd, 0); |
| } else { |
| ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); |
| return; |
| } |
| break; |
| |
| case UF_DEVICE_A_ALT_HNP_SUPPORT: |
| /* other RH port does */ |
| DWC_DEBUGPL(DBG_PCDV, |
| "SET_FEATURE: USB_DEVICE_A_ALT_HNP_SUPPORT\n"); |
| if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) { |
| pcd->a_alt_hnp_support = 1; |
| dwc_otg_pcd_update_otg(pcd, 0); |
| } else { |
| ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); |
| return; |
| } |
| break; |
| |
| default: |
| ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); |
| return; |
| |
| } |
| do_setup_in_status_phase(pcd); |
| break; |
| |
| case UT_INTERFACE: |
| do_gadget_setup(pcd, &ctrl); |
| break; |
| |
| case UT_ENDPOINT: |
| if (UGETW(ctrl.wValue) == UF_ENDPOINT_HALT) { |
| ep = get_ep_by_addr(pcd, UGETW(ctrl.wIndex)); |
| if (ep == 0) { |
| ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); |
| 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) |
| { |
| usb_device_request_t 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.bmRequestType, ctrl.bRequest, |
| UGETW(ctrl.wValue), UGETW(ctrl.wIndex), |
| UGETW(ctrl.wLength)); |
| |
| switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) { |
| case UT_DEVICE: |
| switch (UGETW(ctrl.wValue)) { |
| case UF_DEVICE_REMOTE_WAKEUP: |
| pcd->remote_wakeup_enable = 0; |
| break; |
| |
| case UF_TEST_MODE: |
| /** @todo Add CLEAR_FEATURE for TEST modes. */ |
| break; |
| |
| default: |
| ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); |
| return; |
| } |
| do_setup_in_status_phase(pcd); |
| break; |
| |
| case UT_ENDPOINT: |
| ep = get_ep_by_addr(pcd, UGETW(ctrl.wIndex)); |
| if (ep == 0) { |
| ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); |
| return; |
| } |
| |
| pcd_clear_halt(pcd, ep); |
| |
| break; |
| } |
| } |
| |
| /** |
| * This function process the SET_ADDRESS Setup Commands. |
| */ |
| static inline void do_set_address(dwc_otg_pcd_t * pcd) |
| { |
| dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if; |
| usb_device_request_t ctrl = pcd->setup_pkt->req; |
| |
| if (ctrl.bmRequestType == UT_DEVICE) { |
| dcfg_data_t dcfg = {.d32 = 0 }; |
| |
| #ifdef DEBUG_EP0 |
| // DWC_DEBUGPL(DBG_PCDV, "SET_ADDRESS:%d\n", ctrl.wValue); |
| #endif |
| dcfg.b.devaddr = UGETW(ctrl.wValue); |
| DWC_MODIFY_REG32(&dev_if->dev_global_regs->dcfg, 0, dcfg.d32); |
| do_setup_in_status_phase(pcd); |
| } |
| } |
| |
| /** |
| * 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). |
| * |
| * <table> |
| * <tr><td>Command </td><td>Driver </td><td>Description</td></tr> |
| * |
| * <tr><td>GET_STATUS </td><td>PCD </td><td>Command is processed as |
| * defined in chapter 9 of the USB 2.0 Specification chapter 9 |
| * </td></tr> |
| * |
| * <tr><td>CLEAR_FEATURE </td><td>PCD </td><td>The Device and Endpoint |
| * requests are the ENDPOINT_HALT feature is procesed, all others the |
| * interface requests are ignored.</td></tr> |
| * |
| * <tr><td>SET_FEATURE </td><td>PCD </td><td>The Device and Endpoint |
| * requests are processed by the PCD. Interface requests are passed |
| * to the Gadget Driver.</td></tr> |
| * |
| * <tr><td>SET_ADDRESS </td><td>PCD </td><td>Program the DCFG reg, |
| * with device address received </td></tr> |
| * |
| * <tr><td>GET_DESCRIPTOR </td><td>Gadget Driver </td><td>Return the |
| * requested descriptor</td></tr> |
| * |
| * <tr><td>SET_DESCRIPTOR </td><td>Gadget Driver </td><td>Optional - |
| * not implemented by any of the existing Gadget Drivers.</td></tr> |
| * |
| * <tr><td>SET_CONFIGURATION </td><td>Gadget Driver </td><td>Disable |
| * all EPs and enable EPs for new configuration.</td></tr> |
| * |
| * <tr><td>GET_CONFIGURATION </td><td>Gadget Driver </td><td>Return |
| * the current configuration</td></tr> |
| * |
| * <tr><td>SET_INTERFACE </td><td>Gadget Driver </td><td>Disable all |
| * EPs and enable EPs for new configuration.</td></tr> |
| * |
| * <tr><td>GET_INTERFACE </td><td>Gadget Driver </td><td>Return the |
| * current interface.</td></tr> |
| * |
| * <tr><td>SYNC_FRAME </td><td>PCD </td><td>Display debug |
| * message.</td></tr> |
| * </table> |
| * |
| * 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; |
| usb_device_request_t ctrl = pcd->setup_pkt->req; |
| dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; |
| |
| deptsiz0_data_t doeptsize0 = {.d32 = 0 }; |
| |
| #ifdef DWC_UTE_CFI |
| int retval = 0; |
| struct cfi_usb_ctrlrequest cfi_req; |
| #endif |
| |
| #ifdef DEBUG_EP0 |
| DWC_DEBUGPL(DBG_PCD, "SETUP %02x.%02x v%04x i%04x l%04x\n", |
| ctrl.bmRequestType, ctrl.bRequest, |
| UGETW(ctrl.wValue), UGETW(ctrl.wIndex), |
| UGETW(ctrl.wLength)); |
| #endif |
| |
| doeptsize0.d32 = DWC_READ_REG32(&dev_if->out_ep_regs[0]->doeptsiz); |
| |
| /** @todo handle > 1 setup packet , assert error for now */ |
| |
| if (core_if->dma_enable && core_if->dma_desc_enable == 0 |
| && (doeptsize0.b.supcnt < 2)) { |
| DWC_ERROR |
| ("\n\n----------- CANNOT handle > 1 setup packet in DMA mode\n\n"); |
| } |
| |
| /* Clean up the request queue */ |
| dwc_otg_request_nuke(ep0); |
| ep0->stopped = 0; |
| |
| if (ctrl.bmRequestType & UE_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 (UGETW(ctrl.wLength) == 0) { |
| ep0->dwc_ep.is_in = 1; |
| pcd->ep0state = EP0_IN_STATUS_PHASE; |
| } |
| |
| if (UT_GET_TYPE(ctrl.bmRequestType) != UT_STANDARD) { |
| |
| #ifdef DWC_UTE_CFI |
| DWC_MEMCPY(&cfi_req, &ctrl, sizeof(usb_device_request_t)); |
| |
| //printk(KERN_ALERT "CFI: req_type=0x%02x; req=0x%02x\n", |
| ctrl.bRequestType, ctrl.bRequest); |
| if (UT_GET_TYPE(cfi_req.bRequestType) == UT_VENDOR) { |
| if (cfi_req.bRequest > 0xB0 && cfi_req.bRequest < 0xBF) { |
| retval = cfi_setup(pcd, &cfi_req); |
| if (retval < 0) { |
| ep0_do_stall(pcd, retval); |
| pcd->ep0_pending = 0; |
| return; |
| } |
| |
| /* if need gadget setup then call it and check the retval */ |
| if (pcd->cfi->need_gadget_att) { |
| retval = |
| cfi_gadget_setup(pcd, |
| &pcd-> |
| cfi->ctrl_req); |
| if (retval < 0) { |
| pcd->ep0_pending = 0; |
| return; |
| } |
| } |
| |
| if (pcd->cfi->need_status_in_complete) { |
| do_setup_in_status_phase(pcd); |
| } |
| return; |
| } |
| } |
| #endif |
| |
| /* handle non-standard (class/vendor) requests in the gadget driver */ |
| do_gadget_setup(pcd, &ctrl); |
| return; |
| } |
| |
| /** @todo NGS: Handle bad setup packet? */ |
| |
| /////////////////////////////////////////// |
| //// --- Standard Request handling --- //// |
| |
| switch (ctrl.bRequest) { |
| case UR_GET_STATUS: |
| do_get_status(pcd); |
| break; |
| |
| case UR_CLEAR_FEATURE: |
| do_clear_feature(pcd); |
| break; |
| |
| case UR_SET_FEATURE: |
| do_set_feature(pcd); |
| break; |
| |
| case UR_SET_ADDRESS: |
| do_set_address(pcd); |
| break; |
| |
| case UR_SET_INTERFACE: |
| case UR_SET_CONFIG: |
| // _pcd->request_config = 1; /* Configuration changed */ |
| do_gadget_setup(pcd, &ctrl); |
| break; |
| |
| case UR_SYNCH_FRAME: |
| 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; |
| dev_dma_desc_sts_t desc_sts; |
| dwc_otg_pcd_request_t *req; |
| int is_last = 0; |
| dwc_otg_pcd_t *pcd = ep->pcd; |
| |
| #ifdef DWC_UTE_CFI |
| struct cfi_usb_ctrlrequest *ctrlreq; |
| int retval = -DWC_E_NOT_SUPPORTED; |
| #endif |
| |
| if (pcd->ep0_pending && DWC_CIRCLEQ_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); |
| } else { |
| #ifdef DEBUG_EP0 |
| DWC_DEBUGPL(DBG_PCDV, "Do setup IN status phase\n"); |
| #endif |
| |
| #ifdef DWC_UTE_CFI |
| ctrlreq = &pcd->cfi->ctrl_req; |
| |
| if (UT_GET_TYPE(ctrlreq->bRequestType) == UT_VENDOR) { |
| if (ctrlreq->bRequest > 0xB0 |
| && ctrlreq->bRequest < 0xBF) { |
| |
| /* Return if the PCD failed to handle the request */ |
| if ((retval = |
| pcd->cfi->ops. |
| ctrl_write_complete(pcd->cfi, |
| pcd)) < 0) { |
| CFI_INFO |
| ("ERROR setting a new value in the PCD(%d)\n", |
| retval); |
| ep0_do_stall(pcd, retval); |
| pcd->ep0_pending = 0; |
| return 0; |
| } |
| |
| /* If the gadget needs to be notified on the request */ |
| if (pcd->cfi->need_gadget_att == 1) { |
| //retval = do_gadget_setup(pcd, &pcd->cfi->ctrl_req); |
| retval = |
| cfi_gadget_setup(pcd, |
| &pcd->cfi-> |
| ctrl_req); |
| |
| /* Return from the function if the gadget failed to process |
| * the request properly - this should never happen !!! |
| */ |
| if (retval < 0) { |
| CFI_INFO |
| ("ERROR setting a new value in the gadget(%d)\n", |
| retval); |
| pcd->ep0_pending = 0; |
| return 0; |
| } |
| } |
| |
| CFI_INFO("%s: RETVAL=%d\n", __func__, |
| retval); |
| /* If we hit here then the PCD and the gadget has properly |
| * handled the request - so send the ZLP IN to the host. |
| */ |
| /* @todo: MAS - decide whether we need to start the setup |
| * stage based on the need_setup value of the cfi object |
| */ |
| do_setup_in_status_phase(pcd); |
| pcd->ep0_pending = 0; |
| return 1; |
| } |
| } |
| #endif |
| |
| do_setup_in_status_phase(pcd); |
| } |
| pcd->ep0_pending = 0; |
| return 1; |
| } |
| |
| if (DWC_CIRCLEQ_EMPTY(&ep->queue)) { |
| return 0; |
| } |
| req = DWC_CIRCLEQ_FIRST(&ep->queue); |
| |
| if (pcd->ep0state == EP0_OUT_STATUS_PHASE |
| || pcd->ep0state == EP0_IN_STATUS_PHASE) { |
| is_last = 1; |
| } else if (ep->dwc_ep.is_in) { |
| deptsiz.d32 = DWC_READ_REG32(&in_ep_regs->dieptsiz); |
| if (core_if->dma_desc_enable != 0) |
| desc_sts = dev_if->in_desc_addr->status; |
| #ifdef DEBUG_EP0 |
| DWC_DEBUGPL(DBG_PCDV, "%d len=%d xfersize=%d pktcnt=%d\n", |
| ep->dwc_ep.num, ep->dwc_ep.xfer_len, |
| deptsiz.b.xfersize, deptsiz.b.pktcnt); |
| #endif |
| |
| if (((core_if->dma_desc_enable == 0) |
| && (deptsiz.b.xfersize == 0)) |
| || ((core_if->dma_desc_enable != 0) |
| && (desc_sts.b.bytes == 0))) { |
| req->actual = ep->dwc_ep.xfer_count; |
| /* Is a Zero Len Packet needed? */ |
| if (req->sent_zlp) { |
| #ifdef DEBUG_EP0 |
| DWC_DEBUGPL(DBG_PCD, "Setup Rx ZLP\n"); |
| #endif |
| req->sent_zlp = 0; |
| } |
| 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, "%d len=%d xsize=%d pktcnt=%d\n", |
| ep->dwc_ep.num, ep->dwc_ep.xfer_len, |
| deptsiz.b.xfersize, deptsiz.b.pktcnt); |
| #endif |
| req->actual = ep->dwc_ep.xfer_count; |
| |
| /* Is a Zero Len Packet needed? */ |
| if (req->sent_zlp) { |
| #ifdef DEBUG_EP0 |
| DWC_DEBUGPL(DBG_PCDV, "Setup Tx ZLP\n"); |
| #endif |
| req->sent_zlp = 0; |
| } |
| if (core_if->dma_desc_enable == 0) |
| do_setup_in_status_phase(pcd); |
| } |
| |
| /* Complete the request */ |
| if (is_last) { |
| dwc_otg_request_done(ep, req, 0); |
| ep->dwc_ep.start_xfer_buff = 0; |
| ep->dwc_ep.xfer_buff = 0; |
| ep->dwc_ep.xfer_len = 0; |
| return 1; |
| } |
| return 0; |
| } |
| |
| #ifdef DWC_UTE_CFI |
| /** |
| * This function calculates traverses all the CFI DMA descriptors and |
| * and accumulates the bytes that are left to be transfered. |
| * |
| * @return The total bytes left to transfered, or a negative value as failure |
| */ |
| static inline int cfi_calc_desc_residue(dwc_otg_pcd_ep_t * ep) |
| { |
| int32_t ret = 0; |
| int i; |
| struct dwc_otg_dma_desc *ddesc = NULL; |
| struct cfi_ep *cfiep; |
| |
| /* See if the pcd_ep has its respective cfi_ep mapped */ |
| cfiep = get_cfi_ep_by_pcd_ep(ep->pcd->cfi, ep); |
| if (!cfiep) { |
| CFI_INFO("%s: Failed to find ep\n", __func__); |
| return -1; |
| } |
| |
| ddesc = ep->dwc_ep.descs; |
| |
| for (i = 0; (i < cfiep->desc_count) && (i < MAX_DMA_DESCS_PER_EP); i++) { |
| |
| #if defined(PRINT_CFI_DMA_DESCS) |
| print_desc(ddesc, ep->ep.name, i); |
| #endif |
| ret += ddesc->status.b.bytes; |
| ddesc++; |
| } |
| |
| if (ret) |
| CFI_INFO("!!!!!!!!!! WARNING (%s) - residue=%d\n", __func__, |
| ret); |
| |
| return ret; |
| } |
| #endif |
| |
| /** |
| * 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]; |
| deptsiz_data_t deptsiz; |
| dev_dma_desc_sts_t desc_sts; |
| dwc_otg_pcd_request_t *req = 0; |
| dwc_otg_dev_dma_desc_t *dma_desc; |
| uint32_t byte_count = 0; |
| int is_last = 0; |
| int i; |
| |
| DWC_DEBUGPL(DBG_PCDV, "%s() %d-%s\n", __func__, ep->dwc_ep.num, |
| (ep->dwc_ep.is_in ? "IN" : "OUT")); |
| |
| /* Get any pending requests */ |
| if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { |
| req = DWC_CIRCLEQ_FIRST(&ep->queue); |
| if (!req) { |
| DWC_PRINTF("complete_ep 0x%p, req = NULL!\n", ep); |
| return; |
| } |
| } else { |
| DWC_PRINTF("complete_ep 0x%p, ep->queue empty!\n", ep); |
| return; |
| } |
| |
| DWC_DEBUGPL(DBG_PCD, "Requests %d\n", ep->pcd->request_pending); |
| |
| if (ep->dwc_ep.is_in) { |
| deptsiz.d32 = DWC_READ_REG32(&in_ep_regs->dieptsiz); |
| |
| if (core_if->dma_enable) { |
| if (core_if->dma_desc_enable == 0) { |
| if (deptsiz.b.xfersize == 0 |
| && deptsiz.b.pktcnt == 0) { |
| byte_count = |
| ep->dwc_ep.xfer_len - |
| ep->dwc_ep.xfer_count; |
| |
| ep->dwc_ep.xfer_buff += byte_count; |
| ep->dwc_ep.dma_addr += byte_count; |
| ep->dwc_ep.xfer_count += byte_count; |
| |
| DWC_DEBUGPL(DBG_PCDV, |
| "%d-%s len=%d xfersize=%d pktcnt=%d\n", |
| ep->dwc_ep.num, |
| (ep->dwc_ep. |
| is_in ? "IN" : "OUT"), |
| ep->dwc_ep.xfer_len, |
| deptsiz.b.xfersize, |
| deptsiz.b.pktcnt); |
| |
| if (ep->dwc_ep.xfer_len < |
| ep->dwc_ep.total_len) { |
| dwc_otg_ep_start_transfer |
| (core_if, &ep->dwc_ep); |
| } else if (ep->dwc_ep.sent_zlp) { |
| /* |
| * This fragment of code should initiate 0 |
| * length transfer in case if it is queued |
| * a transfer with size divisible to EPs max |
| * packet size and with usb_request zero field |
| * is set, which means that after data is transfered, |
| * it is also should be transfered |
| * a 0 length packet at the end. For Slave and |
| * Buffer DMA modes in this case SW has |
| * to initiate 2 transfers one with transfer size, |
| * and the second with 0 size. For Descriptor |
| * DMA mode SW is able to initiate a transfer, |
| * which will handle all the packets including |
| * the last 0 length. |
| */ |
| ep->dwc_ep.sent_zlp = 0; |
| dwc_otg_ep_start_zl_transfer |
| (core_if, &ep->dwc_ep); |
| } else { |
| is_last = 1; |
| } |
| } else { |
| if(ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) |
| { |
| req->actual = 0; |
| dwc_otg_request_done(ep, req, 0); |
| |
| 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); |
| } else |
| DWC_WARN |
| ("Incomplete transfer (%d - %s [siz=%d pkt=%d])\n", |
| ep->dwc_ep.num, |
| (ep->dwc_ep.is_in ? "IN" : "OUT"), |
| deptsiz.b.xfersize, |
| deptsiz.b.pktcnt); |
| } |
| } else { |
| dma_desc = ep->dwc_ep.desc_addr; |
| byte_count = 0; |
| ep->dwc_ep.sent_zlp = 0; |
| |
| #ifdef DWC_UTE_CFI |
| CFI_INFO("%s: BUFFER_MODE=%d\n", __func__, |
| ep->dwc_ep.buff_mode); |
| if (ep->dwc_ep.buff_mode != BM_STANDARD) { |
| int residue; |
| |
| residue = cfi_calc_desc_residue(ep); |
| if (residue < 0) |
| return; |
| |
| byte_count = residue; |
| } else { |
| #endif |
| for (i = 0; i < ep->dwc_ep.desc_cnt; |
| ++i) { |
| desc_sts = dma_desc->status; |
| byte_count += desc_sts.b.bytes; |
| dma_desc++; |
| } |
| #ifdef DWC_UTE_CFI |
| } |
| #endif |
| if (byte_count == 0) { |
| ep->dwc_ep.xfer_count = |
| ep->dwc_ep.total_len; |
| is_last = 1; |
| } else { |
| DWC_WARN("Incomplete transfer\n"); |
| } |
| } |
| } else { |
| if (deptsiz.b.xfersize == 0 && deptsiz.b.pktcnt == 0) { |
| DWC_DEBUGPL(DBG_PCDV, |
| "%d-%s len=%d xfersize=%d pktcnt=%d\n", |
| ep->dwc_ep.num, |
| ep->dwc_ep.is_in ? "IN" : "OUT", |
| ep->dwc_ep.xfer_len, |
| deptsiz.b.xfersize, |
| deptsiz.b.pktcnt); |
| |
| /* Check if the whole transfer was completed, |
| * if no, setup transfer for next portion of data |
| */ |
| if (ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) { |
| dwc_otg_ep_start_transfer(core_if, |
| &ep->dwc_ep); |
| } else if (ep->dwc_ep.sent_zlp) { |
| /* |
| * This fragment of code should initiate 0 |
| * length trasfer in case if it is queued |
| * a trasfer with size divisible to EPs max |
| * packet size and with usb_request zero field |
| * is set, which means that after data is transfered, |
| * it is also should be transfered |
| * a 0 length packet at the end. For Slave and |
| * Buffer DMA modes in this case SW has |
| * to initiate 2 transfers one with transfer size, |
| * and the second with 0 size. For Desriptor |
| * DMA mode SW is able to initiate a transfer, |
| * which will handle all the packets including |
| * the last 0 legth. |
| */ |
| ep->dwc_ep.sent_zlp = 0; |
| dwc_otg_ep_start_zl_transfer(core_if, |
| &ep->dwc_ep); |
| } else { |
| is_last = 1; |
| } |
| } else { |
| DWC_WARN |
| ("Incomplete transfer (%d-%s [siz=%d pkt=%d])\n", |
| ep->dwc_ep.num, |
| (ep->dwc_ep.is_in ? "IN" : "OUT"), |
| deptsiz.b.xfersize, deptsiz.b.pktcnt); |
| } |
| } |
| } else { |
| dwc_otg_dev_out_ep_regs_t *out_ep_regs = |
| dev_if->out_ep_regs[ep->dwc_ep.num]; |
| desc_sts.d32 = 0; |
| if (core_if->dma_enable) { |
| if (core_if->dma_desc_enable) { |
| dma_desc = ep->dwc_ep.desc_addr; |
| byte_count = 0; |
| ep->dwc_ep.sent_zlp = 0; |
| |
| #ifdef DWC_UTE_CFI |
| CFI_INFO("%s: BUFFER_MODE=%d\n", __func__, |
| ep->dwc_ep.buff_mode); |
| if (ep->dwc_ep.buff_mode != BM_STANDARD) { |
| int residue; |
| residue = cfi_calc_desc_residue(ep); |
| if (residue < 0) |
| return; |
| byte_count = residue; |
| } else { |
| #endif |
| |
| for (i = 0; i < ep->dwc_ep.desc_cnt; |
| ++i) { |
| desc_sts = dma_desc->status; |
| byte_count += desc_sts.b.bytes; |
| dma_desc++; |
| } |
| |
| #ifdef DWC_UTE_CFI |
| } |
| #endif |
| /* Checking for interrupt Out transfers with not |
| * dword aligned mps sizes |
| */ |
| if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_INTR && |
| (ep->dwc_ep.maxpacket%4)) { |
| ep->dwc_ep.xfer_count = ep->dwc_ep.total_len - byte_count; |
| if ((ep->dwc_ep.xfer_len % ep->dwc_ep.maxpacket) && |
| (ep->dwc_ep.xfer_len/ep->dwc_ep.maxpacket < MAX_DMA_DESC_CNT)) |
| ep->dwc_ep.xfer_len -= |
| (ep->dwc_ep.desc_cnt - 1) * ep->dwc_ep.maxpacket + |
| ep->dwc_ep.xfer_len % ep->dwc_ep.maxpacket; |
| else |
| ep->dwc_ep.xfer_len -= |
| ep->dwc_ep.desc_cnt * ep->dwc_ep.maxpacket; |
| if (ep->dwc_ep.xfer_len > 0) { |
| dwc_otg_ep_start_transfer(core_if, |
| &ep->dwc_ep); |
| } else { |
| is_last = 1; |
| } |
| } else { |
| ep->dwc_ep.xfer_count = ep->dwc_ep.total_len |
| - byte_count + |
| ((4 - (ep->dwc_ep.total_len & 0x3)) & 0x3); |
| is_last = 1; |
| } |
| } else { |
| deptsiz.d32 = 0; |
| deptsiz.d32 = |
| DWC_READ_REG32(&out_ep_regs->doeptsiz); |
| |
| byte_count = (ep->dwc_ep.xfer_len - |
| ep->dwc_ep.xfer_count - |
| deptsiz.b.xfersize); |
| ep->dwc_ep.xfer_buff += byte_count; |
| ep->dwc_ep.dma_addr += byte_count; |
| ep->dwc_ep.xfer_count += byte_count; |
| |
| /* Check if the whole transfer was completed, |
| * if no, setup transfer for next portion of data |
| */ |
| if (ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) { |
| dwc_otg_ep_start_transfer(core_if, |
| &ep->dwc_ep); |
| } else if (ep->dwc_ep.sent_zlp) { |
| /* |
| * This fragment of code should initiate 0 |
| * length trasfer in case if it is queued |
| * a trasfer with size divisible to EPs max |
| * packet size and with usb_request zero field |
| * is set, which means that after data is transfered, |
| * it is also should be transfered |
| * a 0 length packet at the end. For Slave and |
| * Buffer DMA modes in this case SW has |
| * to initiate 2 transfers one with transfer size, |
| * and the second with 0 size. For Desriptor |
| * DMA mode SW is able to initiate a transfer, |
| * which will handle all the packets including |
| * the last 0 legth. |
| */ |
| ep->dwc_ep.sent_zlp = 0; |
| dwc_otg_ep_start_zl_transfer(core_if, |
| &ep->dwc_ep); |
| } else { |
| is_last = 1; |
| } |
| } |
| } else { |
| /* Check if the whole transfer was completed, |
| * if no, setup transfer for next portion of data |
| */ |
| if (ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) { |
| dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep); |
| } else if (ep->dwc_ep.sent_zlp) { |
| /* |
| * This fragment of code should initiate 0 |
| * length transfer in case if it is queued |
| * a transfer with size divisible to EPs max |
| * packet size and with usb_request zero field |
| * is set, which means that after data is transfered, |
| * it is also should be transfered |
| * a 0 length packet at the end. For Slave and |
| * Buffer DMA modes in this case SW has |
| * to initiate 2 transfers one with transfer size, |
| * and the second with 0 size. For Descriptor |
| * DMA mode SW is able to initiate a transfer, |
| * which will handle all the packets including |
| * the last 0 length. |
| */ |
| ep->dwc_ep.sent_zlp = 0; |
| dwc_otg_ep_start_zl_transfer(core_if, |
| &ep->dwc_ep); |
| } else { |
| is_last = 1; |
| } |
| } |
| |
| DWC_DEBUGPL(DBG_PCDV, |
| "addr %p, %d-%s len=%d cnt=%d xsize=%d pktcnt=%d\n", |
| &out_ep_regs->doeptsiz, ep->dwc_ep.num, |
| ep->dwc_ep.is_in ? "IN" : "OUT", |
| ep->dwc_ep.xfer_len, ep->dwc_ep.xfer_count, |
| deptsiz.b.xfersize, deptsiz.b.pktcnt); |
| } |
| |
| /* Complete the request */ |
| if (is_last) { |
| #ifdef DWC_UTE_CFI |
| if (ep->dwc_ep.buff_mode != BM_STANDARD) { |
| req->actual = ep->dwc_ep.cfi_req_len - byte_count; |
| } else { |
| #endif |
| req->actual = ep->dwc_ep.xfer_count; |
| #ifdef DWC_UTE_CFI |
| } |
| #endif |
| if (req->dw_align_buf) { |
| if (!ep->dwc_ep.is_in) { |
| dwc_memcpy(req->buf, req->dw_align_buf, req->length); |
| } |
| DWC_DMA_FREE(req->length, req->dw_align_buf, |
| req->dw_align_buf_dma); |
| } |
| |
| dwc_otg_request_done(ep, req, 0); |
| |
| 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); |
| } |
| } |
| |
| #ifdef DWC_EN_ISOC |
| |
| /** |
| * This function BNA interrupt for Isochronous EPs |
| * |
| */ |
| static void dwc_otg_pcd_handle_iso_bna(dwc_otg_pcd_ep_t * ep) |
| { |
| dwc_ep_t *dwc_ep = &ep->dwc_ep; |
| volatile uint32_t *addr; |
| depctl_data_t depctl = {.d32 = 0 }; |
| dwc_otg_pcd_t *pcd = ep->pcd; |
| dwc_otg_dev_dma_desc_t *dma_desc; |
| int i; |
| |
| dma_desc = |
| dwc_ep->iso_desc_addr + dwc_ep->desc_cnt * (dwc_ep->proc_buf_num); |
| |
| if (dwc_ep->is_in) { |
| dev_dma_desc_sts_t sts = {.d32 = 0 }; |
| for (i = 0; i < dwc_ep->desc_cnt; ++i, ++dma_desc) { |
| sts.d32 = dma_desc->status.d32; |
| sts.b_iso_in.bs = BS_HOST_READY; |
| dma_desc->status.d32 = sts.d32; |
| } |
| } else { |
| dev_dma_desc_sts_t sts = {.d32 = 0 }; |
| for (i = 0; i < dwc_ep->desc_cnt; ++i, ++dma_desc) { |
| sts.d32 = dma_desc->status.d32; |
| sts.b_iso_out.bs = BS_HOST_READY; |
| dma_desc->status.d32 = sts.d32; |
| } |
| } |
| |
| if (dwc_ep->is_in == 0) { |
| addr = |
| &GET_CORE_IF(pcd)->dev_if->out_ep_regs[dwc_ep-> |
| num]->doepctl; |
| } else { |
| addr = |
| &GET_CORE_IF(pcd)->dev_if->in_ep_regs[dwc_ep->num]->diepctl; |
| } |
| depctl.b.epena = 1; |
| DWC_MODIFY_REG32(addr, depctl.d32, depctl.d32); |
| } |
| |
| /** |
| * This function sets latest iso packet information(non-PTI mode) |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param ep The EP to start the transfer on. |
| * |
| */ |
| void set_current_pkt_info(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) |
| { |
| deptsiz_data_t deptsiz = {.d32 = 0 }; |
| dma_addr_t dma_addr; |
| uint32_t offset; |
| |
| if (ep->proc_buf_num) |
| dma_addr = ep->dma_addr1; |
| else |
| dma_addr = ep->dma_addr0; |
| |
| if (ep->is_in) { |
| deptsiz.d32 = |
| DWC_READ_REG32(&core_if->dev_if-> |
| in_ep_regs[ep->num]->dieptsiz); |
| offset = ep->data_per_frame; |
| } else { |
| deptsiz.d32 = |
| DWC_READ_REG32(&core_if->dev_if-> |
| out_ep_regs[ep->num]->doeptsiz); |
| offset = |
| ep->data_per_frame + |
| (0x4 & (0x4 - (ep->data_per_frame & 0x3))); |
| } |
| |
| if (!deptsiz.b.xfersize) { |
| ep->pkt_info[ep->cur_pkt].length = ep->data_per_frame; |
| ep->pkt_info[ep->cur_pkt].offset = |
| ep->cur_pkt_dma_addr - dma_addr; |
| ep->pkt_info[ep->cur_pkt].status = 0; |
| } else { |
| ep->pkt_info[ep->cur_pkt].length = ep->data_per_frame; |
| ep->pkt_info[ep->cur_pkt].offset = |
| ep->cur_pkt_dma_addr - dma_addr; |
| ep->pkt_info[ep->cur_pkt].status = -DWC_E_NO_DATA; |
| } |
| ep->cur_pkt_addr += offset; |
| ep->cur_pkt_dma_addr += offset; |
| ep->cur_pkt++; |
| } |
| |
| /** |
| * This function sets latest iso packet information(DDMA mode) |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param dwc_ep The EP to start the transfer on. |
| * |
| */ |
| static void set_ddma_iso_pkts_info(dwc_otg_core_if_t * core_if, |
| dwc_ep_t * dwc_ep) |
| { |
| dwc_otg_dev_dma_desc_t *dma_desc; |
| dev_dma_desc_sts_t sts = {.d32 = 0 }; |
| iso_pkt_info_t *iso_packet; |
| uint32_t data_per_desc; |
| uint32_t offset; |
| int i, j; |
| |
| iso_packet = dwc_ep->pkt_info; |
| |
| /** Reinit closed DMA Descriptors*/ |
| /** ISO OUT EP */ |
| if (dwc_ep->is_in == 0) { |
| dma_desc = |
| dwc_ep->iso_desc_addr + |
| dwc_ep->desc_cnt * dwc_ep->proc_buf_num; |
| offset = 0; |
| |
| for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; |
| i += dwc_ep->pkt_per_frm) { |
| for (j = 0; j < dwc_ep->pkt_per_frm; ++j) { |
| data_per_desc = |
| ((j + 1) * dwc_ep->maxpacket > |
| dwc_ep-> |
| data_per_frame) ? dwc_ep->data_per_frame - |
| j * dwc_ep->maxpacket : dwc_ep->maxpacket; |
| data_per_desc += |
| (data_per_desc % 4) ? (4 - |
| data_per_desc % |
| 4) : 0; |
| |
| sts.d32 = dma_desc->status.d32; |
| |
| /* Write status in iso_packet_decsriptor */ |
| iso_packet->status = |
| sts.b_iso_out.rxsts + |
| (sts.b_iso_out.bs ^ BS_DMA_DONE); |
| if (iso_packet->status) { |
| iso_packet->status = -DWC_E_NO_DATA; |
| } |
| |
| /* Received data length */ |
| if (!sts.b_iso_out.rxbytes) { |
| iso_packet->length = |
| data_per_desc - |
| sts.b_iso_out.rxbytes; |
| } else { |
| iso_packet->length = |
| data_per_desc - |
| sts.b_iso_out.rxbytes + (4 - |
| dwc_ep->data_per_frame |
| % 4); |
| } |
| |
| iso_packet->offset = offset; |
| |
| offset += data_per_desc; |
| dma_desc++; |
| iso_packet++; |
| } |
| } |
| |
| for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) { |
| data_per_desc = |
| ((j + 1) * dwc_ep->maxpacket > |
| dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - |
| j * dwc_ep->maxpacket : dwc_ep->maxpacket; |
| data_per_desc += |
| (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0; |
| |
| sts.d32 = dma_desc->status.d32; |
| |
| /* Write status in iso_packet_decsriptor */ |
| iso_packet->status = |
| sts.b_iso_out.rxsts + |
| (sts.b_iso_out.bs ^ BS_DMA_DONE); |
| if (iso_packet->status) { |
| iso_packet->status = -DWC_E_NO_DATA; |
| } |
| |
| /* Received data length */ |
| iso_packet->length = |
| dwc_ep->data_per_frame - sts.b_iso_out.rxbytes; |
| |
| iso_packet->offset = offset; |
| |
| offset += data_per_desc; |
| iso_packet++; |
| dma_desc++; |
| } |
| |
| sts.d32 = dma_desc->status.d32; |
| |
| /* Write status in iso_packet_decsriptor */ |
| iso_packet->status = |
| sts.b_iso_out.rxsts + (sts.b_iso_out.bs ^ BS_DMA_DONE); |
| if (iso_packet->status) { |
| iso_packet->status = -DWC_E_NO_DATA; |
| } |
| /* Received data length */ |
| if (!sts.b_iso_out.rxbytes) { |
| iso_packet->length = |
| dwc_ep->data_per_frame - sts.b_iso_out.rxbytes; |
| } else { |
| iso_packet->length = |
| dwc_ep->data_per_frame - sts.b_iso_out.rxbytes + |
| (4 - dwc_ep->data_per_frame % 4); |
| } |
| |
| iso_packet->offset = offset; |
| } else { |
| /** ISO IN EP */ |
| |
| dma_desc = |
| dwc_ep->iso_desc_addr + |
| dwc_ep->desc_cnt * dwc_ep->proc_buf_num; |
| |
| for (i = 0; i < dwc_ep->desc_cnt - 1; i++) { |
| sts.d32 = dma_desc->status.d32; |
| |
| /* Write status in iso packet descriptor */ |
| iso_packet->status = |
| sts.b_iso_in.txsts + |
| (sts.b_iso_in.bs ^ BS_DMA_DONE); |
| if (iso_packet->status != 0) { |
| iso_packet->status = -DWC_E_NO_DATA; |
| |
| } |
| /* Bytes has been transfered */ |
| iso_packet->length = |
| dwc_ep->data_per_frame - sts.b_iso_in.txbytes; |
| |
| dma_desc++; |
| iso_packet++; |
| } |
| |
| sts.d32 = dma_desc->status.d32; |
| while (sts.b_iso_in.bs == BS_DMA_BUSY) { |
| sts.d32 = dma_desc->status.d32; |
| } |
| |
| /* Write status in iso packet descriptor ??? do be done with ERROR codes */ |
| iso_packet->status = |
| sts.b_iso_in.txsts + (sts.b_iso_in.bs ^ BS_DMA_DONE); |
| if (iso_packet->status != 0) { |
| iso_packet->status = -DWC_E_NO_DATA; |
| } |
| |
| /* Bytes has been transfered */ |
| iso_packet->length = |
| dwc_ep->data_per_frame - sts.b_iso_in.txbytes; |
| } |
| } |
| |
| /** |
| * This function reinitialize DMA Descriptors for Isochronous transfer |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param dwc_ep The EP to start the transfer on. |
| * |
| */ |
| static void reinit_ddma_iso_xfer(dwc_otg_core_if_t * core_if, dwc_ep_t * dwc_ep) |
| { |
| int i, j; |
| dwc_otg_dev_dma_desc_t *dma_desc; |
| dma_addr_t dma_ad; |
| volatile uint32_t *addr; |
| dev_dma_desc_sts_t sts = {.d32 = 0 }; |
| uint32_t data_per_desc; |
| |
| if (dwc_ep->is_in == 0) { |
| addr = &core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl; |
| } else { |
| addr = &core_if->dev_if->in_ep_regs[dwc_ep->num]->diepctl; |
| } |
| |
| if (dwc_ep->proc_buf_num == 0) { |
| /** Buffer 0 descriptors setup */ |
| dma_ad = dwc_ep->dma_addr0; |
| } else { |
| /** Buffer 1 descriptors setup */ |
| dma_ad = dwc_ep->dma_addr1; |
| } |
| |
| /** Reinit closed DMA Descriptors*/ |
| /** ISO OUT EP */ |
| if (dwc_ep->is_in == 0) { |
| dma_desc = |
| dwc_ep->iso_desc_addr + |
| dwc_ep->desc_cnt * dwc_ep->proc_buf_num; |
| |
| sts.b_iso_out.bs = BS_HOST_READY; |
| sts.b_iso_out.rxsts = 0; |
| sts.b_iso_out.l = 0; |
| sts.b_iso_out.sp = 0; |
| sts.b_iso_out.ioc = 0; |
| sts.b_iso_out.pid = 0; |
| sts.b_iso_out.framenum = 0; |
| |
| for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; |
| i += dwc_ep->pkt_per_frm) { |
| for (j = 0; j < dwc_ep->pkt_per_frm; ++j) { |
| data_per_desc = |
| ((j + 1) * dwc_ep->maxpacket > |
| dwc_ep-> |
| data_per_frame) ? dwc_ep->data_per_frame - |
| j * dwc_ep->maxpacket : dwc_ep->maxpacket; |
| data_per_desc += |
| (data_per_desc % 4) ? (4 - |
| data_per_desc % |
| 4) : 0; |
| sts.b_iso_out.rxbytes = data_per_desc; |
| dma_desc->buf = dma_ad; |
| dma_desc->status.d32 = sts.d32; |
| |
| dma_ad += data_per_desc; |
| dma_desc++; |
| } |
| } |
| |
| for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) { |
| |
| data_per_desc = |
| ((j + 1) * dwc_ep->maxpacket > |
| dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - |
| j * dwc_ep->maxpacket : dwc_ep->maxpacket; |
| data_per_desc += |
| (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0; |
| sts.b_iso_out.rxbytes = data_per_desc; |
| |
| dma_desc->buf = dma_ad; |
| dma_desc->status.d32 = sts.d32; |
| |
| dma_desc++; |
| dma_ad += data_per_desc; |
| } |
| |
| sts.b_iso_out.ioc = 1; |
| sts.b_iso_out.l = dwc_ep->proc_buf_num; |
| |
| data_per_desc = |
| ((j + 1) * dwc_ep->maxpacket > |
| dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - |
| j * dwc_ep->maxpacket : dwc_ep->maxpacket; |
| data_per_desc += |
| (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0; |
| sts.b_iso_out.rxbytes = data_per_desc; |
| |
| dma_desc->buf = dma_ad; |
| dma_desc->status.d32 = sts.d32; |
| } else { |
| /** ISO IN EP */ |
| |
| dma_desc = |
| dwc_ep->iso_desc_addr + |
| dwc_ep->desc_cnt * dwc_ep->proc_buf_num; |
| |
| sts.b_iso_in.bs = BS_HOST_READY; |
| sts.b_iso_in.txsts = 0; |
| sts.b_iso_in.sp = 0; |
| sts.b_iso_in.ioc = 0; |
| sts.b_iso_in.pid = dwc_ep->pkt_per_frm; |
| sts.b_iso_in.framenum = dwc_ep->next_frame; |
| sts.b_iso_in.txbytes = dwc_ep->data_per_frame; |
| sts.b_iso_in.l = 0; |
| |
| for (i = 0; i < dwc_ep->desc_cnt - 1; i++) { |
| dma_desc->buf = dma_ad; |
| dma_desc->status.d32 = sts.d32; |
| |
| sts.b_iso_in.framenum += dwc_ep->bInterval; |
| dma_ad += dwc_ep->data_per_frame; |
| dma_desc++; |
| } |
| |
| sts.b_iso_in.ioc = 1; |
| sts.b_iso_in.l = dwc_ep->proc_buf_num; |
| |
| dma_desc->buf = dma_ad; |
| dma_desc->status.d32 = sts.d32; |
| |
| dwc_ep->next_frame = |
| sts.b_iso_in.framenum + dwc_ep->bInterval * 1; |
| } |
| dwc_ep->proc_buf_num = (dwc_ep->proc_buf_num ^ 1) & 0x1; |
| } |
| |
| /** |
| * This function is to handle Iso EP transfer complete interrupt |
| * in case Iso out packet was dropped |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param dwc_ep The EP for wihich transfer complete was asserted |
| * |
| */ |
| static uint32_t handle_iso_out_pkt_dropped(dwc_otg_core_if_t * core_if, |
| dwc_ep_t * dwc_ep) |
| { |
| uint32_t dma_addr; |
| uint32_t drp_pkt; |
| uint32_t drp_pkt_cnt; |
| deptsiz_data_t deptsiz = {.d32 = 0 }; |
| depctl_data_t depctl = {.d32 = 0 }; |
| int i; |
| |
| deptsiz.d32 = |
| DWC_READ_REG32(&core_if->dev_if-> |
| out_ep_regs[dwc_ep->num]->doeptsiz); |
| |
| drp_pkt = dwc_ep->pkt_cnt - deptsiz.b.pktcnt; |
| drp_pkt_cnt = dwc_ep->pkt_per_frm - (drp_pkt % dwc_ep->pkt_per_frm); |
| |
| /* Setting dropped packets status */ |
| for (i = 0; i < drp_pkt_cnt; ++i) { |
| dwc_ep->pkt_info[drp_pkt].status = -DWC_E_NO_DATA; |
| drp_pkt++; |
| deptsiz.b.pktcnt--; |
| } |
| |
| if (deptsiz.b.pktcnt > 0) { |
| deptsiz.b.xfersize = |
| dwc_ep->xfer_len - (dwc_ep->pkt_cnt - |
| deptsiz.b.pktcnt) * dwc_ep->maxpacket; |
| } else { |
| deptsiz.b.xfersize = 0; |
| deptsiz.b.pktcnt = 0; |
| } |
| |
| DWC_WRITE_REG32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doeptsiz, |
| deptsiz.d32); |
| |
| if (deptsiz.b.pktcnt > 0) { |
| if (dwc_ep->proc_buf_num) { |
| dma_addr = |
| dwc_ep->dma_addr1 + dwc_ep->xfer_len - |
| deptsiz.b.xfersize; |
| } else { |
| dma_addr = |
| dwc_ep->dma_addr0 + dwc_ep->xfer_len - |
| deptsiz.b.xfersize;; |
| } |
| |
| DWC_WRITE_REG32(&core_if->dev_if-> |
| out_ep_regs[dwc_ep->num]->doepdma, dma_addr); |
| |
| /** Re-enable endpoint, clear nak */ |
| depctl.d32 = 0; |
| depctl.b.epena = 1; |
| depctl.b.cnak = 1; |
| |
| DWC_MODIFY_REG32(&core_if->dev_if-> |
| out_ep_regs[dwc_ep->num]->doepctl, depctl.d32, |
| depctl.d32); |
| return 0; |
| } else { |
| return 1; |
| } |
| } |
| |
| /** |
| * This function sets iso packets information(PTI mode) |
| * |
| * @param core_if Programming view of DWC_otg controller. |
| * @param ep The EP to start the transfer on. |
| * |
| */ |
| static uint32_t set_iso_pkts_info(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) |
| { |
| int i, j; |
| dma_addr_t dma_ad; |
| iso_pkt_info_t *packet_info = ep->pkt_info; |
| uint32_t offset; |
| uint32_t frame_data; |
| deptsiz_data_t deptsiz; |
| |
| if (ep->proc_buf_num == 0) { |
| /** Buffer 0 descriptors setup */ |
| dma_ad = ep->dma_addr0; |
| } else { |
| /** Buffer 1 descriptors setup */ |
| dma_ad = ep->dma_addr1; |
| } |
| |
| if (ep->is_in) { |
| deptsiz.d32 = |
| DWC_READ_REG32(&core_if->dev_if-> |
| in_ep_regs[ep->num]->dieptsiz); |
| } else { |
| deptsiz.d32 = |
| DWC_READ_REG32(&core_if->dev_if-> |
| out_ep_regs[ep->num]->doeptsiz); |
| } |
| |
| if (!deptsiz.b.xfersize) { |
| offset = 0; |
| for (i = 0; i < ep->pkt_cnt; i += ep->pkt_per_frm) { |
| frame_data = ep->data_per_frame; |
| for (j = 0; j < ep->pkt_per_frm; ++j) { |
| |
| /* Packet status - is not set as initially |
| * it is set to 0 and if packet was sent |
| successfully, status field will remain 0*/ |
| |
| /* Bytes has been transfered */ |
| packet_info->length = |
| (ep->maxpacket < |
| frame_data) ? ep->maxpacket : frame_data; |
| |
| /* Received packet offset */ |
| packet_info->offset = offset; |
| offset += packet_info->length; |
| frame_data -= packet_info->length; |
| |
| packet_info++; |
| } |
| } |
| return 1; |
| } else { |
| /* This is a workaround for in case of Transfer Complete with |
| * PktDrpSts interrupts merging - in this case Transfer complete |
| * interrupt for Isoc Out Endpoint is asserted without PktDrpSts |
| * set and with DOEPTSIZ register non zero. Investigations showed, |
| * that this happens when Out packet is dropped, but because of |
| * interrupts merging during first interrupt handling PktDrpSts |
| * bit is cleared and for next merged interrupts it is not reset. |
| * In this case SW hadles the interrupt as if PktDrpSts bit is set. |
| */ |
| if (ep->is_in) { |
| return 1; |
| } else { |
| return handle_iso_out_pkt_dropped(core_if, ep); |
| } |
| } |
| } |
| |
| /** |
| * This function is to handle Iso EP transfer complete interrupt |
| * |
| * @param pcd The PCD |
| * @param ep The EP for which transfer complete was asserted |
| * |
| */ |
| static void complete_iso_ep(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep) |
| { |
| dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd); |
| dwc_ep_t *dwc_ep = &ep->dwc_ep; |
| uint8_t is_last = 0; |
| |
| if (ep->dwc_ep.next_frame == 0xffffffff) { |
| DWC_WARN("Next frame is not set!\n"); |
| return; |
| } |
| |
| if (core_if->dma_enable) { |
| if (core_if->dma_desc_enable) { |
| set_ddma_iso_pkts_info(core_if, dwc_ep); |
| reinit_ddma_iso_xfer(core_if, dwc_ep); |
| is_last = 1; |
| } else { |
| if (core_if->pti_enh_enable) { |
| if (set_iso_pkts_info(core_if, dwc_ep)) { |
| dwc_ep->proc_buf_num = |
| (dwc_ep->proc_buf_num ^ 1) & 0x1; |
| dwc_otg_iso_ep_start_buf_transfer |
| (core_if, dwc_ep); |
| is_last = 1; |
| } |
| } else { |
| set_current_pkt_info(core_if, dwc_ep); |
| if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) { |
| is_last = 1; |
| dwc_ep->cur_pkt = 0; |
| dwc_ep->proc_buf_num = |
| (dwc_ep->proc_buf_num ^ 1) & 0x1; |
| if (dwc_ep->proc_buf_num) { |
| dwc_ep->cur_pkt_addr = |
| dwc_ep->xfer_buff1; |
| dwc_ep->cur_pkt_dma_addr = |
| dwc_ep->dma_addr1; |
| } else { |
| dwc_ep->cur_pkt_addr = |
| dwc_ep->xfer_buff0; |
| dwc_ep->cur_pkt_dma_addr = |
| dwc_ep->dma_addr0; |
| } |
| |
| } |
| dwc_otg_iso_ep_start_frm_transfer(core_if, |
| dwc_ep); |
| } |
| } |
| } else { |
| set_current_pkt_info(core_if, dwc_ep); |
| if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) { |
|