add support for SG lists on dwc3 driver. With this we can e.g. use VFS layer's SG lists on storage gadgets so that we can start bigger transfers and improve throughput. Signed-off-by: Felipe Balbi <balbi@xxxxxx> --- drivers/usb/dwc3/gadget.c | 75 +++++++++++++++++++++++++++++++++++--------- 1 files changed, 59 insertions(+), 16 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f9527b8..1d5ead1 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -108,6 +108,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, dep->busy_slot++; } list_del(&req->list); + req->trb = NULL; if (req->request.status == -EINPROGRESS) req->request.status = status; @@ -545,7 +546,8 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep, * @req: dwc3_request pointer */ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, - struct dwc3_request *req, unsigned last) + struct dwc3_request *req, dma_addr_t dma, + unsigned length, unsigned last, unsigned chain) { struct dwc3_trb_hw *trb_hw; struct dwc3_trb trb; @@ -561,15 +563,18 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, usb_endpoint_xfer_isoc(dep->desc)) return; - dwc3_gadget_move_request_queued(req); memset(&trb, 0, sizeof(trb)); - - req->trb = trb_hw; + if (!req->trb) { + dwc3_gadget_move_request_queued(req); + req->trb = trb_hw; + req->trb_dma = dwc3_trb_dma_offset(dep, trb_hw); + } if (usb_endpoint_xfer_isoc(dep->desc)) { trb.isp_imi = true; trb.csp = true; } else { + trb.chn = chain; trb.lst = last; } @@ -601,12 +606,11 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, BUG(); } - trb.length = req->request.length; - trb.bplh = req->request.dma; + trb.length = length; + trb.bplh = dma; trb.hwo = true; dwc3_trb_to_hw(&trb, trb_hw); - req->trb_dma = dwc3_trb_dma_offset(dep, trb_hw); } /* @@ -663,19 +667,57 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting) return; list_for_each_entry_safe(req, n, &dep->request_list, list) { - trbs_left--; + unsigned length; + dma_addr_t dma; - if (!trbs_left) - last_one = 1; + if (req->request.num_sgs > 0) { + struct scatterlist *sg = req->request.sg; + int i; - /* Is this the last request? */ - if (list_empty(&dep->request_list)) - last_one = 1; + for_each_sg(sg, sg, req->request.num_sgs, i) { + unsigned chain = true; - dwc3_prepare_one_trb(dep, req, last_one); + length = sg->length; + dma = sg->dma_address; - if (last_one) - break; + if (i == req->request.num_sgs) + chain = false; + + trbs_left--; + if (!trbs_left) + last_one = true; + + /* Is this the last request? */ + if (list_empty(&dep->request_list)) + last_one = true; + + if (last_one) + chain = false; + + dwc3_prepare_one_trb(dep, req, dma, length, + last_one, chain); + + if (last_one) + break; + } + } else { + dma = req->request.dma; + length = req->request.length; + trbs_left--; + + if (!trbs_left) + last_one = 1; + + /* Is this the last request? */ + if (list_empty(&dep->request_list)) + last_one = 1; + + dwc3_prepare_one_trb(dep, req, dma, length, + last_one, false); + + if (last_one) + break; + } } } @@ -1995,6 +2037,7 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) dwc->gadget.dev.dma_mask = dwc->dev->dma_mask; dwc->gadget.dev.release = dwc3_gadget_release; dwc->gadget.name = "dwc3-gadget"; + dwc->gadget.sg_table_size = DWC3_TRB_NUM; /* * REVISIT: Here we should clear all pending IRQs to be -- 1.7.8.rc3 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html