diff options
Diffstat (limited to 'drivers/usb/dwc2/gadget.c')
-rw-r--r-- | drivers/usb/dwc2/gadget.c | 334 |
1 files changed, 177 insertions, 157 deletions
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 83cb5577a52f..f0d9ccf1d665 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * Copyright (c) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com * @@ -107,7 +107,6 @@ static inline bool using_desc_dma(struct dwc2_hsotg *hsotg) /** * dwc2_gadget_incr_frame_num - Increments the targeted frame number. * @hs_ep: The endpoint - * @increment: The value to increment by * * This function will also check if the frame number overruns DSTS_SOFFN_LIMIT. * If an overrun occurs it will wrap the value and set the frame_overrun flag. @@ -190,6 +189,8 @@ static void dwc2_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg, /** * dwc2_hsotg_tx_fifo_count - return count of TX FIFOs in device mode + * + * @hsotg: Programming view of the DWC_otg controller */ int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg) { @@ -204,6 +205,8 @@ int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg) /** * dwc2_hsotg_tx_fifo_total_depth - return total FIFO depth available for * device mode TX FIFOs + * + * @hsotg: Programming view of the DWC_otg controller */ int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg) { @@ -227,6 +230,8 @@ int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg) /** * dwc2_hsotg_tx_fifo_average_depth - returns average depth of device mode * TX FIFOs + * + * @hsotg: Programming view of the DWC_otg controller */ int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg) { @@ -327,6 +332,7 @@ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg) } /** + * dwc2_hsotg_ep_alloc_request - allocate USB rerequest structure * @ep: USB endpoint to allocate request for. * @flags: Allocation flags * @@ -793,9 +799,7 @@ static void dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep *hs_ep, * @dma_buff: usb requests dma buffer. * @len: usb request transfer length. * - * Finds out index of first free entry either in the bottom or up half of - * descriptor chain depend on which is under SW control and not processed - * by HW. Then fills that descriptor with the data of the arrived usb request, + * Fills next free descriptor with the data of the arrived usb request, * frame info, sets Last and IOC bits increments next_desc. If filled * descriptor is not the first one, removes L bit from the previous descriptor * status. @@ -810,34 +814,17 @@ static int dwc2_gadget_fill_isoc_desc(struct dwc2_hsotg_ep *hs_ep, u32 mask = 0; maxsize = dwc2_gadget_get_desc_params(hs_ep, &mask); - if (len > maxsize) { - dev_err(hsotg->dev, "wrong len %d\n", len); - return -EINVAL; - } - - /* - * If SW has already filled half of chain, then return and wait for - * the other chain to be processed by HW. - */ - if (hs_ep->next_desc == MAX_DMA_DESC_NUM_GENERIC / 2) - return -EBUSY; - - /* Increment frame number by interval for IN */ - if (hs_ep->dir_in) - dwc2_gadget_incr_frame_num(hs_ep); - index = (MAX_DMA_DESC_NUM_GENERIC / 2) * hs_ep->isoc_chain_num + - hs_ep->next_desc; + index = hs_ep->next_desc; + desc = &hs_ep->desc_list[index]; - /* Sanity check of calculated index */ - if ((hs_ep->isoc_chain_num && index > MAX_DMA_DESC_NUM_GENERIC) || - (!hs_ep->isoc_chain_num && index > MAX_DMA_DESC_NUM_GENERIC / 2)) { - dev_err(hsotg->dev, "wrong index %d for iso chain\n", index); - return -EINVAL; + /* Check if descriptor chain full */ + if ((desc->status >> DEV_DMA_BUFF_STS_SHIFT) == + DEV_DMA_BUFF_STS_HREADY) { + dev_dbg(hsotg->dev, "%s: desc chain full\n", __func__); + return 1; } - desc = &hs_ep->desc_list[index]; - /* Clear L bit of previous desc if more than one entries in the chain */ if (hs_ep->next_desc) hs_ep->desc_list[index - 1].status &= ~DEV_DMA_L; @@ -865,8 +852,14 @@ static int dwc2_gadget_fill_isoc_desc(struct dwc2_hsotg_ep *hs_ep, desc->status &= ~DEV_DMA_BUFF_STS_MASK; desc->status |= (DEV_DMA_BUFF_STS_HREADY << DEV_DMA_BUFF_STS_SHIFT); + /* Increment frame number by interval for IN */ + if (hs_ep->dir_in) + dwc2_gadget_incr_frame_num(hs_ep); + /* Update index of last configured entry in the chain */ hs_ep->next_desc++; + if (hs_ep->next_desc >= MAX_DMA_DESC_NUM_GENERIC) + hs_ep->next_desc = 0; return 0; } @@ -875,11 +868,8 @@ static int dwc2_gadget_fill_isoc_desc(struct dwc2_hsotg_ep *hs_ep, * dwc2_gadget_start_isoc_ddma - start isochronous transfer in DDMA * @hs_ep: The isochronous endpoint. * - * Prepare first descriptor chain for isochronous endpoints. Afterwards + * Prepare descriptor chain for isochronous endpoints. Afterwards * write DMA address to HW and enable the endpoint. - * - * Switch between descriptor chains via isoc_chain_num to give SW opportunity - * to prepare second descriptor chain while first one is being processed by HW. */ static void dwc2_gadget_start_isoc_ddma(struct dwc2_hsotg_ep *hs_ep) { @@ -887,24 +877,34 @@ static void dwc2_gadget_start_isoc_ddma(struct dwc2_hsotg_ep *hs_ep) struct dwc2_hsotg_req *hs_req, *treq; int index = hs_ep->index; int ret; + int i; u32 dma_reg; u32 depctl; u32 ctrl; + struct dwc2_dma_desc *desc; if (list_empty(&hs_ep->queue)) { dev_dbg(hsotg->dev, "%s: No requests in queue\n", __func__); return; } + /* Initialize descriptor chain by Host Busy status */ + for (i = 0; i < MAX_DMA_DESC_NUM_GENERIC; i++) { + desc = &hs_ep->desc_list[i]; + desc->status = 0; + desc->status |= (DEV_DMA_BUFF_STS_HBUSY + << DEV_DMA_BUFF_STS_SHIFT); + } + + hs_ep->next_desc = 0; list_for_each_entry_safe(hs_req, treq, &hs_ep->queue, queue) { ret = dwc2_gadget_fill_isoc_desc(hs_ep, hs_req->req.dma, hs_req->req.length); - if (ret) { - dev_dbg(hsotg->dev, "%s: desc chain full\n", __func__); + if (ret) break; - } } + hs_ep->compl_desc = 0; depctl = hs_ep->dir_in ? DIEPCTL(index) : DOEPCTL(index); dma_reg = hs_ep->dir_in ? DIEPDMA(index) : DOEPDMA(index); @@ -914,10 +914,6 @@ static void dwc2_gadget_start_isoc_ddma(struct dwc2_hsotg_ep *hs_ep) ctrl = dwc2_readl(hsotg->regs + depctl); ctrl |= DXEPCTL_EPENA | DXEPCTL_CNAK; dwc2_writel(ctrl, hsotg->regs + depctl); - - /* Switch ISOC descriptor chain number being processed by SW*/ - hs_ep->isoc_chain_num = (hs_ep->isoc_chain_num ^ 1) & 0x1; - hs_ep->next_desc = 0; } /** @@ -1235,7 +1231,7 @@ static bool dwc2_gadget_target_frame_elapsed(struct dwc2_hsotg_ep *hs_ep) { struct dwc2_hsotg *hsotg = hs_ep->parent; u32 target_frame = hs_ep->target_frame; - u32 current_frame = dwc2_hsotg_read_frameno(hsotg); + u32 current_frame = hsotg->frame_number; bool frame_overrun = hs_ep->frame_overrun; if (!frame_overrun && current_frame >= target_frame) @@ -1291,6 +1287,9 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, struct dwc2_hsotg *hs = hs_ep->parent; bool first; int ret; + u32 maxsize = 0; + u32 mask = 0; + dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n", ep->name, req, req->length, req->buf, req->no_interrupt, @@ -1308,6 +1307,24 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, req->actual = 0; req->status = -EINPROGRESS; + /* In DDMA mode for ISOC's don't queue request if length greater + * than descriptor limits. + */ + if (using_desc_dma(hs) && hs_ep->isochronous) { + maxsize = dwc2_gadget_get_desc_params(hs_ep, &mask); + if (hs_ep->dir_in && req->length > maxsize) { + dev_err(hs->dev, "wrong length %d (maxsize=%d)\n", + req->length, maxsize); + return -EINVAL; + } + + if (!hs_ep->dir_in && req->length > hs_ep->ep.maxpacket) { + dev_err(hs->dev, "ISOC OUT: wrong length %d (mps=%d)\n", + req->length, hs_ep->ep.maxpacket); + return -EINVAL; + } + } + ret = dwc2_hsotg_handle_unaligned_buf_start(hs, hs_ep, hs_req); if (ret) return ret; @@ -1330,17 +1347,15 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, /* * Handle DDMA isochronous transfers separately - just add new entry - * to the half of descriptor chain that is not processed by HW. + * to the descriptor chain. * Transfer will be started once SW gets either one of NAK or * OutTknEpDis interrupts. */ - if (using_desc_dma(hs) && hs_ep->isochronous && - hs_ep->target_frame != TARGET_FRAME_INITIAL) { - ret = dwc2_gadget_fill_isoc_desc(hs_ep, hs_req->req.dma, - hs_req->req.length); - if (ret) - dev_dbg(hs->dev, "%s: ISO desc chain full\n", __func__); - + if (using_desc_dma(hs) && hs_ep->isochronous) { + if (hs_ep->target_frame != TARGET_FRAME_INITIAL) { + dwc2_gadget_fill_isoc_desc(hs_ep, hs_req->req.dma, + hs_req->req.length); + } return 0; } @@ -1350,8 +1365,15 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, return 0; } - while (dwc2_gadget_target_frame_elapsed(hs_ep)) + /* Update current frame number value. */ + hs->frame_number = dwc2_hsotg_read_frameno(hs); + while (dwc2_gadget_target_frame_elapsed(hs_ep)) { dwc2_gadget_incr_frame_num(hs_ep); + /* Update current frame number value once more as it + * changes here. + */ + hs->frame_number = dwc2_hsotg_read_frameno(hs); + } if (hs_ep->target_frame != TARGET_FRAME_INITIAL) dwc2_hsotg_start_req(hs, hs_ep, hs_req, false); @@ -2011,108 +2033,75 @@ static void dwc2_hsotg_complete_request(struct dwc2_hsotg *hsotg, * @hs_ep: The endpoint the request was on. * * Get first request from the ep queue, determine descriptor on which complete - * happened. SW based on isoc_chain_num discovers which half of the descriptor - * chain is currently in use by HW, adjusts dma_address and calculates index - * of completed descriptor based on the value of DEPDMA register. Update actual - * length of request, giveback to gadget. + * happened. SW discovers which descriptor currently in use by HW, adjusts + * dma_address and calculates index of completed descriptor based on the value + * of DEPDMA register. Update actual length of request, giveback to gadget. */ static void dwc2_gadget_complete_isoc_request_ddma(struct dwc2_hsotg_ep *hs_ep) { struct dwc2_hsotg *hsotg = hs_ep->parent; struct dwc2_hsotg_req *hs_req; struct usb_request *ureq; - int index; - dma_addr_t dma_addr; - u32 dma_reg; - u32 depdma; u32 desc_sts; u32 mask; - hs_req = get_ep_head(hs_ep); - if (!hs_req) { - dev_warn(hsotg->dev, "%s: ISOC EP queue empty\n", __func__); - return; - } - ureq = &hs_req->req; - - dma_addr = hs_ep->desc_list_dma; - - /* - * If lower half of descriptor chain is currently use by SW, - * that means higher half is being processed by HW, so shift - * DMA address to higher half of descriptor chain. - */ - if (!hs_ep->isoc_chain_num) - dma_addr += sizeof(struct dwc2_dma_desc) * - (MAX_DMA_DESC_NUM_GENERIC / 2); - - dma_reg = hs_ep->dir_in ? DIEPDMA(hs_ep->index) : DOEPDMA(hs_ep->index); - depdma = dwc2_readl(hsotg->regs + dma_reg); + desc_sts = hs_ep->desc_list[hs_ep->compl_desc].status; - index = (depdma - dma_addr) / sizeof(struct dwc2_dma_desc) - 1; - desc_sts = hs_ep->desc_list[index].status; + /* Process only descriptors with buffer status set to DMA done */ + while ((desc_sts & DEV_DMA_BUFF_STS_MASK) >> + DEV_DMA_BUFF_STS_SHIFT == DEV_DMA_BUFF_STS_DMADONE) { - mask = hs_ep->dir_in ? DEV_DMA_ISOC_TX_NBYTES_MASK : - DEV_DMA_ISOC_RX_NBYTES_MASK; - ureq->actual = ureq->length - - ((desc_sts & mask) >> DEV_DMA_ISOC_NBYTES_SHIFT); + hs_req = get_ep_head(hs_ep); + if (!hs_req) { + dev_warn(hsotg->dev, "%s: ISOC EP queue empty\n", __func__); + return; + } + ureq = &hs_req->req; + + /* Check completion status */ + if ((desc_sts & DEV_DMA_STS_MASK) >> DEV_DMA_STS_SHIFT == + DEV_DMA_STS_SUCC) { + mask = hs_ep->dir_in ? DEV_DMA_ISOC_TX_NBYTES_MASK : + DEV_DMA_ISOC_RX_NBYTES_MASK; + ureq->actual = ureq->length - ((desc_sts & mask) >> + DEV_DMA_ISOC_NBYTES_SHIFT); + + /* Adjust actual len for ISOC Out if len is + * not align of 4 + */ + if (!hs_ep->dir_in && ureq->length & 0x3) + ureq->actual += 4 - (ureq->length & 0x3); + } - /* Adjust actual length for ISOC Out if length is not align of 4 */ - if (!hs_ep->dir_in && ureq->length & 0x3) - ureq->actual += 4 - (ureq->length & 0x3); + dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); - dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); + hs_ep->compl_desc++; + if (hs_ep->compl_desc > (MAX_DMA_DESC_NUM_GENERIC - 1)) + hs_ep->compl_desc = 0; + desc_sts = hs_ep->desc_list[hs_ep->compl_desc].status; + } } /* - * dwc2_gadget_start_next_isoc_ddma - start next isoc request, if any. - * @hs_ep: The isochronous endpoint to be re-enabled. + * dwc2_gadget_handle_isoc_bna - handle BNA interrupt for ISOC. + * @hs_ep: The isochronous endpoint. * - * If ep has been disabled due to last descriptor servicing (IN endpoint) or - * BNA (OUT endpoint) check the status of other half of descriptor chain that - * was under SW control till HW was busy and restart the endpoint if needed. + * If EP ISOC OUT then need to flush RX FIFO to remove source of BNA + * interrupt. Reset target frame and next_desc to allow to start + * ISOC's on NAK interrupt for IN direction or on OUTTKNEPDIS + * interrupt for OUT direction. */ -static void dwc2_gadget_start_next_isoc_ddma(struct dwc2_hsotg_ep *hs_ep) +static void dwc2_gadget_handle_isoc_bna(struct dwc2_hsotg_ep *hs_ep) { struct dwc2_hsotg *hsotg = hs_ep->parent; - u32 depctl; - u32 dma_reg; - u32 ctrl; - u32 dma_addr = hs_ep->desc_list_dma; - unsigned char index = hs_ep->index; - - dma_reg = hs_ep->dir_in ? DIEPDMA(index) : DOEPDMA(index); - depctl = hs_ep->dir_in ? DIEPCTL(index) : DOEPCTL(index); - ctrl = dwc2_readl(hsotg->regs + depctl); - - /* - * EP was disabled if HW has processed last descriptor or BNA was set. - * So restart ep if SW has prepared new descriptor chain in ep_queue - * routine while HW was busy. - */ - if (!(ctrl & DXEPCTL_EPENA)) { - if (!hs_ep->next_desc) { - dev_dbg(hsotg->dev, "%s: No more ISOC requests\n", - __func__); - return; - } - - dma_addr += sizeof(struct dwc2_dma_desc) * - (MAX_DMA_DESC_NUM_GENERIC / 2) * - hs_ep->isoc_chain_num; - dwc2_writel(dma_addr, hsotg->regs + dma_reg); - - ctrl |= DXEPCTL_EPENA | DXEPCTL_CNAK; - dwc2_writel(ctrl, hsotg->regs + depctl); + if (!hs_ep->dir_in) + dwc2_flush_rx_fifo(hsotg); + dwc2_hsotg_complete_request(hsotg, hs_ep, get_ep_head(hs_ep), 0); - /* Switch ISOC descriptor chain number being processed by SW*/ - hs_ep->isoc_chain_num = (hs_ep->isoc_chain_num ^ 1) & 0x1; - hs_ep->next_desc = 0; - - dev_dbg(hsotg->dev, "%s: Restarted isochronous endpoint\n", - __func__); - } + hs_ep->target_frame = TARGET_FRAME_INITIAL; + hs_ep->next_desc = 0; + hs_ep->compl_desc = 0; } /** @@ -2441,6 +2430,7 @@ static u32 dwc2_hsotg_ep0_mps(unsigned int mps) * @ep: The index number of the endpoint * @mps: The maximum packet size in bytes * @mc: The multicount value + * @dir_in: True if direction is in. * * Configure the maximum packet size for the given endpoint, updating * the hardware control registers to reflect this. @@ -2731,6 +2721,8 @@ static void dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep *hs_ep) dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, -ENODATA); dwc2_gadget_incr_frame_num(hs_ep); + /* Update current frame number value. */ + hsotg->frame_number = dwc2_hsotg_read_frameno(hsotg); } while (dwc2_gadget_target_frame_elapsed(hs_ep)); dwc2_gadget_start_next_request(hs_ep); @@ -2738,7 +2730,7 @@ static void dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep *hs_ep) /** * dwc2_gadget_handle_out_token_ep_disabled - handle DXEPINT_OUTTKNEPDIS - * @hs_ep: The endpoint on which interrupt is asserted. + * @ep: The endpoint on which interrupt is asserted. * * This is starting point for ISOC-OUT transfer, synchronization done with * first out token received from host while corresponding EP is disabled. @@ -2763,7 +2755,7 @@ static void dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep *ep) */ tmp = dwc2_hsotg_read_frameno(hsotg); - dwc2_hsotg_complete_request(hsotg, ep, get_ep_head(ep), -ENODATA); + dwc2_hsotg_complete_request(hsotg, ep, get_ep_head(ep), 0); if (using_desc_dma(hsotg)) { if (ep->target_frame == TARGET_FRAME_INITIAL) { @@ -2816,18 +2808,25 @@ static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep) { struct dwc2_hsotg *hsotg = hs_ep->parent; int dir_in = hs_ep->dir_in; + u32 tmp; if (!dir_in || !hs_ep->isochronous) return; if (hs_ep->target_frame == TARGET_FRAME_INITIAL) { - hs_ep->target_frame = dwc2_hsotg_read_frameno(hsotg); + tmp = dwc2_hsotg_read_frameno(hsotg); if (using_desc_dma(hsotg)) { + dwc2_hsotg_complete_request(hsotg, hs_ep, + get_ep_head(hs_ep), 0); + + hs_ep->target_frame = tmp; + dwc2_gadget_incr_frame_num(hs_ep); dwc2_gadget_start_isoc_ddma(hs_ep); return; } + hs_ep->target_frame = tmp; if (hs_ep->interval > 1) { u32 ctrl = dwc2_readl(hsotg->regs + DIEPCTL(hs_ep->index)); @@ -2843,7 +2842,8 @@ static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep) get_ep_head(hs_ep), 0); } - dwc2_gadget_incr_frame_num(hs_ep); + if (!using_desc_dma(hsotg)) + dwc2_gadget_incr_frame_num(hs_ep); } /** @@ -2901,9 +2901,9 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, /* In DDMA handle isochronous requests separately */ if (using_desc_dma(hsotg) && hs_ep->isochronous) { - dwc2_gadget_complete_isoc_request_ddma(hs_ep); - /* Try to start next isoc request */ - dwc2_gadget_start_next_isoc_ddma(hs_ep); + /* XferCompl set along with BNA */ + if (!(ints & DXEPINT_BNAINTR)) + dwc2_gadget_complete_isoc_request_ddma(hs_ep); } else if (dir_in) { /* * We get OutDone from the FIFO, so we only @@ -2978,15 +2978,8 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, if (ints & DXEPINT_BNAINTR) { dev_dbg(hsotg->dev, "%s: BNA interrupt\n", __func__); - - /* - * Try to start next isoc request, if any. - * Sometimes the endpoint remains enabled after BNA interrupt - * assertion, which is not expected, hence we can enter here - * couple of times. - */ if (hs_ep->isochronous) - dwc2_gadget_start_next_isoc_ddma(hs_ep); + dwc2_gadget_handle_isoc_bna(hs_ep); } if (dir_in && !hs_ep->isochronous) { @@ -3197,6 +3190,7 @@ static void dwc2_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic) /** * dwc2_hsotg_core_init - issue softreset to the core * @hsotg: The device state + * @is_usb_reset: Usb resetting flag * * Issue a soft reset to the core, and await the core finishing it. */ @@ -3259,6 +3253,9 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, dcfg |= DCFG_DEVSPD_HS; } + if (hsotg->params.ipg_isoc_en) + dcfg |= DCFG_IPG_ISOC_SUPPORDED; + dwc2_writel(dcfg, hsotg->regs + DCFG); /* Clear any pending OTG interrupts */ @@ -3320,8 +3317,10 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, hsotg->regs + DOEPMSK); /* Enable BNA interrupt for DDMA */ - if (using_desc_dma(hsotg)) + if (using_desc_dma(hsotg)) { dwc2_set_bit(hsotg->regs + DOEPMSK, DOEPMSK_BNAMSK); + dwc2_set_bit(hsotg->regs + DIEPMSK, DIEPMSK_BNAININTRMSK); + } dwc2_writel(0, hsotg->regs + DAINTMSK); @@ -3427,7 +3426,7 @@ static void dwc2_gadget_handle_incomplete_isoc_in(struct dwc2_hsotg *hsotg) daintmsk = dwc2_readl(hsotg->regs + DAINTMSK); - for (idx = 1; idx <= hsotg->num_of_eps; idx++) { + for (idx = 1; idx < hsotg->num_of_eps; idx++) { hs_ep = hsotg->eps_in[idx]; /* Proceed only unmasked ISOC EPs */ if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk)) @@ -3473,7 +3472,7 @@ static void dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg *hsotg) daintmsk = dwc2_readl(hsotg->regs + DAINTMSK); daintmsk >>= DAINT_OUTEP_SHIFT; - for (idx = 1; idx <= hsotg->num_of_eps; idx++) { + for (idx = 1; idx < hsotg->num_of_eps; idx++) { hs_ep = hsotg->eps_out[idx]; /* Proceed only unmasked ISOC EPs */ if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk)) @@ -3647,7 +3646,7 @@ irq_retry: dwc2_writel(gintmsk, hsotg->regs + GINTMSK); dev_dbg(hsotg->dev, "GOUTNakEff triggered\n"); - for (idx = 1; idx <= hsotg->num_of_eps; idx++) { + for (idx = 1; idx < hsotg->num_of_eps; idx++) { hs_ep = hsotg->eps_out[idx]; /* Proceed only unmasked ISOC EPs */ if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk)) @@ -3789,6 +3788,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, unsigned int dir_in; unsigned int i, val, size; int ret = 0; + unsigned char ep_type; dev_dbg(hsotg->dev, "%s: ep %s: a 0x%02x, attr 0x%02x, mps 0x%04x, intr %d\n", @@ -3807,9 +3807,26 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, return -EINVAL; } + ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; mps = usb_endpoint_maxp(desc); mc = usb_endpoint_maxp_mult(desc); + /* ISOC IN in DDMA supported bInterval up to 10 */ + if (using_desc_dma(hsotg) && ep_type == USB_ENDPOINT_XFER_ISOC && + dir_in && desc->bInterval > 10) { + dev_err(hsotg->dev, + "%s: ISOC IN, DDMA: bInterval>10 not supported!\n", __func__); + return -EINVAL; + } + + /* High bandwidth ISOC OUT in DDMA not supported */ + if (using_desc_dma(hsotg) && ep_type == USB_ENDPOINT_XFER_ISOC && + !dir_in && mc > 1) { + dev_err(hsotg->dev, + "%s: ISOC OUT, DDMA: HB not supported!\n", __func__); + return -EINVAL; + } + /* note, we handle this here instead of dwc2_hsotg_set_ep_maxpacket */ epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); @@ -3850,15 +3867,15 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, hs_ep->halted = 0; hs_ep->interval = desc->bInterval; - switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + switch (ep_type) { case USB_ENDPOINT_XFER_ISOC: epctrl |= DXEPCTL_EPTYPE_ISO; epctrl |= DXEPCTL_SETEVENFR; hs_ep->isochronous = 1; hs_ep->interval = 1 << (desc->bInterval - 1); hs_ep->target_frame = TARGET_FRAME_INITIAL; - hs_ep->isoc_chain_num = 0; hs_ep->next_desc = 0; + hs_ep->compl_desc = 0; if (dir_in) { hs_ep->periodic = 1; mask = dwc2_readl(hsotg->regs + DIEPMSK); @@ -4301,7 +4318,6 @@ err: /** * dwc2_hsotg_udc_stop - stop the udc * @gadget: The usb gadget state - * @driver: The usb gadget driver * * Stop udc hw block and stay tunned for future transmissions */ @@ -4453,6 +4469,7 @@ static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = { * @hsotg: The device state. * @hs_ep: The endpoint to be initialised. * @epnum: The endpoint number + * @dir_in: True if direction is in. * * Initialise the given endpoint (as part of the probe and device state * creation) to give to the gadget driver. Setup the endpoint name, any @@ -4526,7 +4543,7 @@ static void dwc2_hsotg_initep(struct dwc2_hsotg *hsotg, /** * dwc2_hsotg_hw_cfg - read HW configuration registers - * @param: The device state + * @hsotg: Programming view of the DWC_otg controller * * Read the USB core HW configuration registers */ @@ -4582,7 +4599,8 @@ static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg) /** * dwc2_hsotg_dump - dump state of the udc - * @param: The device state + * @hsotg: Programming view of the DWC_otg controller + * */ static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg) { @@ -4633,7 +4651,8 @@ static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg) /** * dwc2_gadget_init - init function for gadget - * @dwc2: The data structure for the DWC2 driver. + * @hsotg: Programming view of the DWC_otg controller + * */ int dwc2_gadget_init(struct dwc2_hsotg *hsotg) { @@ -4730,7 +4749,8 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg) /** * dwc2_hsotg_remove - remove function for hsotg driver - * @pdev: The platform information for the driver + * @hsotg: Programming view of the DWC_otg controller + * */ int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg) { @@ -5011,7 +5031,7 @@ int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg) * * @hsotg: Programming view of the DWC_otg controller * @rem_wakeup: indicates whether resume is initiated by Device or Host. - * @param reset: indicates whether resume is initiated by Reset. + * @reset: indicates whether resume is initiated by Reset. * * Return non-zero if failed to exit from hibernation. */ |