From: Vahram Aharonyan <vahr...@synopsys.com>

For DDMA mode in case of isochronous transfers completion performed
differently than other transfer types. This is because each usb request
was mapped to one descriptor in the chain and SW gets xfercomplete
interrupt on all descriptors. The endpoint remains enabled until HW
processes last descriptor with "L" bit set or BNA interrupt gets
asserted for IN and OUT endpoints correspondingly.

Add function dwc2_gadget_complete_isoc_request_ddma() - completes one
isochronous request taken from endpoint's queue.

Add function dwc2_gadget_start_next_isoc_ddma() - tries to restart
isochronous endpoint if requests are pending. Check for EPENA. If the
endpoint was disabled, try to restart it after programming descriptor
chain prepared by SW earlier, switch SW to fill the other half of chain.

Signed-off-by: Vahram Aharonyan <vahr...@synopsys.com>
Signed-off-by: John Youn <johny...@synopsys.com>
---
 drivers/usb/dwc2/gadget.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 105 insertions(+)

diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 93657e2..4314e4d 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -1932,6 +1932,111 @@ static void dwc2_hsotg_complete_request(struct 
dwc2_hsotg *hsotg,
        }
 }
 
+/*
+ * dwc2_gadget_complete_isoc_request_ddma - complete an isoc request in DDMA
+ * @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.
+ */
+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);
+
+       index = (depdma - dma_addr) / sizeof(struct dwc2_dma_desc) - 1;
+       desc_sts = hs_ep->desc_list[index].status;
+
+       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);
+
+       dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
+}
+
+/*
+ * dwc2_gadget_start_next_isoc_ddma - start next isoc request, if any.
+ * @hs_ep: The isochronous endpoint to be re-enabled.
+ *
+ * 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.
+ */
+static void dwc2_gadget_start_next_isoc_ddma(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);
+
+               /* 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__);
+       }
+}
+
 /**
  * dwc2_hsotg_rx_data - receive data from the FIFO for an endpoint
  * @hsotg: The device state.
-- 
2.10.0

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to