1cd8ae19a9
Add backported patched from 5.x from usb related fixes Up kernel version to most recent lts
213 lines
7.0 KiB
Diff
213 lines
7.0 KiB
Diff
From 10209abe87f5ebfd482a00323f5236d6094d0865 Mon Sep 17 00:00:00 2001
|
|
From: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
|
|
Date: Mon, 21 Jan 2019 14:44:47 +0100
|
|
Subject: [PATCH 26/53] usb: dwc2: gadget: Add scatter-gather mode
|
|
|
|
This patch adds support for transferring requests, which are
|
|
non-contiguous in physical memory, i.e. the data buffer is described by
|
|
a scatter-list. This allows transferring large requests without relying
|
|
on error-prone contiguous buffer allocations. This way of allocating
|
|
requests is already implemented in functionfs and TCM USB functions and
|
|
automatically used if UDC driver advertises scatter-gather suppport.
|
|
|
|
Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
|
|
[mszyprow: fixed null pointer issue, rewrote commit message]
|
|
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
|
|
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
|
|
---
|
|
drivers/usb/dwc2/gadget.c | 113 +++++++++++++++++++++++++++++++---------------
|
|
1 file changed, 77 insertions(+), 36 deletions(-)
|
|
|
|
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
|
|
index e15d8a462085..6812a8a3a98b 100644
|
|
--- a/drivers/usb/dwc2/gadget.c
|
|
+++ b/drivers/usb/dwc2/gadget.c
|
|
@@ -768,22 +768,13 @@ static u32 dwc2_gadget_get_desc_params(struct dwc2_hsotg_ep *hs_ep, u32 *mask)
|
|
return desc_size;
|
|
}
|
|
|
|
-/*
|
|
- * dwc2_gadget_config_nonisoc_xfer_ddma - prepare non ISOC DMA desc chain.
|
|
- * @hs_ep: The endpoint
|
|
- * @dma_buff: DMA address to use
|
|
- * @len: Length of the transfer
|
|
- *
|
|
- * This function will iterate over descriptor chain and fill its entries
|
|
- * with corresponding information based on transfer data.
|
|
- */
|
|
-static void dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep *hs_ep,
|
|
+static void dwc2_gadget_fill_nonisoc_xfer_ddma_one(struct dwc2_hsotg_ep *hs_ep,
|
|
+ struct dwc2_dma_desc **desc,
|
|
dma_addr_t dma_buff,
|
|
- unsigned int len)
|
|
+ unsigned int len,
|
|
+ bool true_last)
|
|
{
|
|
- struct dwc2_hsotg *hsotg = hs_ep->parent;
|
|
int dir_in = hs_ep->dir_in;
|
|
- struct dwc2_dma_desc *desc = hs_ep->desc_list;
|
|
u32 mps = hs_ep->ep.maxpacket;
|
|
u32 maxsize = 0;
|
|
u32 offset = 0;
|
|
@@ -798,39 +789,77 @@ static void dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep *hs_ep,
|
|
hs_ep->desc_count = 1;
|
|
|
|
for (i = 0; i < hs_ep->desc_count; ++i) {
|
|
- desc->status = 0;
|
|
- desc->status |= (DEV_DMA_BUFF_STS_HBUSY
|
|
+ (*desc)->status = 0;
|
|
+ (*desc)->status |= (DEV_DMA_BUFF_STS_HBUSY
|
|
<< DEV_DMA_BUFF_STS_SHIFT);
|
|
|
|
if (len > maxsize) {
|
|
if (!hs_ep->index && !dir_in)
|
|
- desc->status |= (DEV_DMA_L | DEV_DMA_IOC);
|
|
+ (*desc)->status |= (DEV_DMA_L | DEV_DMA_IOC);
|
|
|
|
- desc->status |= (maxsize <<
|
|
- DEV_DMA_NBYTES_SHIFT & mask);
|
|
- desc->buf = dma_buff + offset;
|
|
+ (*desc)->status |=
|
|
+ maxsize << DEV_DMA_NBYTES_SHIFT & mask;
|
|
+ (*desc)->buf = dma_buff + offset;
|
|
|
|
len -= maxsize;
|
|
offset += maxsize;
|
|
} else {
|
|
- desc->status |= (DEV_DMA_L | DEV_DMA_IOC);
|
|
+ if (true_last)
|
|
+ (*desc)->status |= (DEV_DMA_L | DEV_DMA_IOC);
|
|
|
|
if (dir_in)
|
|
- desc->status |= (len % mps) ? DEV_DMA_SHORT :
|
|
- ((hs_ep->send_zlp) ? DEV_DMA_SHORT : 0);
|
|
- if (len > maxsize)
|
|
- dev_err(hsotg->dev, "wrong len %d\n", len);
|
|
+ (*desc)->status |= (len % mps) ? DEV_DMA_SHORT :
|
|
+ ((hs_ep->send_zlp && true_last) ?
|
|
+ DEV_DMA_SHORT : 0);
|
|
|
|
- desc->status |=
|
|
+ (*desc)->status |=
|
|
len << DEV_DMA_NBYTES_SHIFT & mask;
|
|
- desc->buf = dma_buff + offset;
|
|
+ (*desc)->buf = dma_buff + offset;
|
|
}
|
|
|
|
- desc->status &= ~DEV_DMA_BUFF_STS_MASK;
|
|
- desc->status |= (DEV_DMA_BUFF_STS_HREADY
|
|
+ (*desc)->status &= ~DEV_DMA_BUFF_STS_MASK;
|
|
+ (*desc)->status |= (DEV_DMA_BUFF_STS_HREADY
|
|
<< DEV_DMA_BUFF_STS_SHIFT);
|
|
- desc++;
|
|
+ (*desc)++;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * dwc2_gadget_config_nonisoc_xfer_ddma - prepare non ISOC DMA desc chain.
|
|
+ * @hs_ep: The endpoint
|
|
+ * @ureq: Request to transfer
|
|
+ * @offset: offset in bytes
|
|
+ * @len: Length of the transfer
|
|
+ *
|
|
+ * This function will iterate over descriptor chain and fill its entries
|
|
+ * with corresponding information based on transfer data.
|
|
+ */
|
|
+static void dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep *hs_ep,
|
|
+ struct usb_request *ureq,
|
|
+ unsigned int offset,
|
|
+ unsigned int len)
|
|
+{
|
|
+ struct dwc2_dma_desc *desc = hs_ep->desc_list;
|
|
+ struct scatterlist *sg;
|
|
+ int i;
|
|
+ u8 desc_count = 0;
|
|
+
|
|
+ /* non-DMA sg buffer */
|
|
+ if (!ureq->num_sgs) {
|
|
+ dwc2_gadget_fill_nonisoc_xfer_ddma_one(hs_ep, &desc,
|
|
+ ureq->dma + offset, len, true);
|
|
+ return;
|
|
}
|
|
+
|
|
+ /* DMA sg buffer */
|
|
+ for_each_sg(ureq->sg, sg, ureq->num_sgs, i) {
|
|
+ dwc2_gadget_fill_nonisoc_xfer_ddma_one(hs_ep, &desc,
|
|
+ sg_dma_address(sg) + sg->offset, sg_dma_len(sg),
|
|
+ sg_is_last(sg));
|
|
+ desc_count += hs_ep->desc_count;
|
|
+ }
|
|
+
|
|
+ hs_ep->desc_count = desc_count;
|
|
}
|
|
|
|
/*
|
|
@@ -944,7 +973,13 @@ static void dwc2_gadget_start_isoc_ddma(struct dwc2_hsotg_ep *hs_ep)
|
|
|
|
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,
|
|
+ dma_addr_t dma_addr = hs_req->req.dma;
|
|
+
|
|
+ if (hs_req->req.num_sgs) {
|
|
+ WARN_ON(hs_req->req.num_sgs > 1);
|
|
+ dma_addr = sg_dma_address(hs_req->req.sg);
|
|
+ }
|
|
+ ret = dwc2_gadget_fill_isoc_desc(hs_ep, dma_addr,
|
|
hs_req->req.length);
|
|
if (ret)
|
|
break;
|
|
@@ -1100,7 +1135,7 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
|
|
offset = ureq->actual;
|
|
|
|
/* Fill DDMA chain entries */
|
|
- dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, ureq->dma + offset,
|
|
+ dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, ureq, offset,
|
|
length);
|
|
|
|
/* write descriptor chain address to control register */
|
|
@@ -1399,7 +1434,13 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
|
|
*/
|
|
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,
|
|
+ dma_addr_t dma_addr = hs_req->req.dma;
|
|
+
|
|
+ if (hs_req->req.num_sgs) {
|
|
+ WARN_ON(hs_req->req.num_sgs > 1);
|
|
+ dma_addr = sg_dma_address(hs_req->req.sg);
|
|
+ }
|
|
+ dwc2_gadget_fill_isoc_desc(hs_ep, dma_addr,
|
|
hs_req->req.length);
|
|
}
|
|
return 0;
|
|
@@ -1987,13 +2028,12 @@ static void dwc2_hsotg_program_zlp(struct dwc2_hsotg *hsotg,
|
|
dev_dbg(hsotg->dev, "Receiving zero-length packet on ep%d\n",
|
|
index);
|
|
if (using_desc_dma(hsotg)) {
|
|
- /* Not specific buffer needed for ep0 ZLP */
|
|
- dma_addr_t dma = hs_ep->desc_list_dma;
|
|
-
|
|
if (!index)
|
|
dwc2_gadget_set_ep0_desc_chain(hsotg, hs_ep);
|
|
|
|
- dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, dma, 0);
|
|
+ /* Not specific buffer needed for ep0 ZLP */
|
|
+ dwc2_gadget_fill_nonisoc_xfer_ddma_one(hs_ep, &hs_ep->desc_list,
|
|
+ hs_ep->desc_list_dma, 0, true);
|
|
} else {
|
|
dwc2_writel(hsotg, DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
|
|
DXEPTSIZ_XFERSIZE(0),
|
|
@@ -4386,6 +4426,7 @@ static int dwc2_hsotg_udc_start(struct usb_gadget *gadget,
|
|
hsotg->enabled = 0;
|
|
spin_unlock_irqrestore(&hsotg->lock, flags);
|
|
|
|
+ gadget->sg_supported = using_desc_dma(hsotg);
|
|
dev_info(hsotg->dev, "bound driver %s\n", driver->driver.name);
|
|
|
|
return 0;
|
|
--
|
|
2.11.0
|
|
|