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

Add function dwc2_gadget_fill_isoc_desc() - initializes DMA descriptor
for isochronous transfer based on the received request data and endpoint
characteristics.

Added function dwc2_gadget_start_isoc_ddma() - prepare DMA chain for
isochronous transfer in DDMA, programs corresponding DMA address to
DEPDMA, enables the endpoint. This function is called once SW decides to
start isochronous IN or OUT transfer depend on reception of NAK or
OUTTknEPdis interrupts indicating first isochronous token arrival from
host.

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

diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 336c799..93657e2 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -737,6 +737,139 @@ static void dwc2_gadget_config_nonisoc_xfer_ddma(struct 
dwc2_hsotg_ep *hs_ep,
        }
 }
 
+/*
+ * dwc2_gadget_fill_isoc_desc - fills next isochronous descriptor in chain.
+ * @hs_ep: The isochronous endpoint.
+ * @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,
+ * 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.
+ */
+static int dwc2_gadget_fill_isoc_desc(struct dwc2_hsotg_ep *hs_ep,
+                                     dma_addr_t dma_buff, unsigned int len)
+{
+       struct dwc2_dma_desc *desc;
+       struct dwc2_hsotg *hsotg = hs_ep->parent;
+       u32 index;
+       u32 maxsize = 0;
+       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;
+
+       /* 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;
+       }
+
+       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;
+
+       dev_dbg(hsotg->dev, "%s: Filling ep %d, dir %s isoc desc # %d\n",
+               __func__, hs_ep->index, hs_ep->dir_in ? "in" : "out", index);
+
+       desc->status = 0;
+       desc->status |= (DEV_DMA_BUFF_STS_HBUSY << DEV_DMA_BUFF_STS_SHIFT);
+
+       desc->buf = dma_buff;
+       desc->status |= (DEV_DMA_L | DEV_DMA_IOC |
+                        ((len << DEV_DMA_NBYTES_SHIFT) & mask));
+
+       if (hs_ep->dir_in) {
+               desc->status |= ((hs_ep->mc << DEV_DMA_ISOC_PID_SHIFT) &
+                                DEV_DMA_ISOC_PID_MASK) |
+                               ((len % hs_ep->ep.maxpacket) ?
+                                DEV_DMA_SHORT : 0) |
+                               ((hs_ep->target_frame <<
+                                 DEV_DMA_ISOC_FRNUM_SHIFT) &
+                                DEV_DMA_ISOC_FRNUM_MASK);
+       }
+
+       desc->status &= ~DEV_DMA_BUFF_STS_MASK;
+       desc->status |= (DEV_DMA_BUFF_STS_HREADY << DEV_DMA_BUFF_STS_SHIFT);
+
+       /* Update index of last configured entry in the chain */
+       hs_ep->next_desc++;
+
+       return 0;
+}
+
+/*
+ * dwc2_gadget_start_isoc_ddma - start isochronous transfer in DDMA
+ * @hs_ep: The isochronous endpoint.
+ *
+ * Prepare first 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)
+{
+       struct dwc2_hsotg *hsotg = hs_ep->parent;
+       struct dwc2_hsotg_req *hs_req, *treq;
+       int index = hs_ep->index;
+       int ret;
+       u32 dma_reg;
+       u32 depctl;
+       u32 ctrl;
+
+       if (list_empty(&hs_ep->queue)) {
+               dev_dbg(hsotg->dev, "%s: No requests in queue\n", __func__);
+               return;
+       }
+
+       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__);
+                       break;
+               }
+       }
+
+       depctl = hs_ep->dir_in ? DIEPCTL(index) : DOEPCTL(index);
+       dma_reg = hs_ep->dir_in ? DIEPDMA(index) : DOEPDMA(index);
+
+       /* write descriptor chain address to control register */
+       dwc2_writel(hs_ep->desc_list_dma, hsotg->regs + dma_reg);
+
+       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;
+}
+
 /**
  * dwc2_hsotg_start_req - start a USB request from an endpoint's queue
  * @hsotg: The controller 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