|
|
@@ -65,6 +65,22 @@ void dwc3_map_buffer_to_dma(struct dwc3_request *req)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ if (req->request.num_sgs) {
|
|
|
+ int mapped;
|
|
|
+
|
|
|
+ mapped = dma_map_sg(dwc->dev, req->request.sg,
|
|
|
+ req->request.num_sgs,
|
|
|
+ req->direction ? DMA_TO_DEVICE
|
|
|
+ : DMA_FROM_DEVICE);
|
|
|
+ if (mapped < 0) {
|
|
|
+ dev_err(dwc->dev, "failed to map SGs\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ req->request.num_mapped_sgs = mapped;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
if (req->request.dma == DMA_ADDR_INVALID) {
|
|
|
req->request.dma = dma_map_single(dwc->dev, req->request.buf,
|
|
|
req->request.length, req->direction
|
|
|
@@ -82,6 +98,17 @@ void dwc3_unmap_buffer_from_dma(struct dwc3_request *req)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ if (req->request.num_mapped_sgs) {
|
|
|
+ req->request.dma = DMA_ADDR_INVALID;
|
|
|
+ dma_unmap_sg(dwc->dev, req->request.sg,
|
|
|
+ req->request.num_sgs,
|
|
|
+ req->direction ? DMA_TO_DEVICE
|
|
|
+ : DMA_FROM_DEVICE);
|
|
|
+
|
|
|
+ req->request.num_mapped_sgs = 0;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
if (req->mapped) {
|
|
|
dma_unmap_single(dwc->dev, req->request.dma,
|
|
|
req->request.length, req->direction
|
|
|
@@ -97,7 +124,11 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
|
|
struct dwc3 *dwc = dep->dwc;
|
|
|
|
|
|
if (req->queued) {
|
|
|
- dep->busy_slot++;
|
|
|
+ if (req->request.num_mapped_sgs)
|
|
|
+ dep->busy_slot += req->request.num_mapped_sgs;
|
|
|
+ else
|
|
|
+ dep->busy_slot++;
|
|
|
+
|
|
|
/*
|
|
|
* Skip LINK TRB. We can't use req->trb and check for
|
|
|
* DWC3_TRBCTL_LINK_TRB because it points the TRB we just
|
|
|
@@ -108,6 +139,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,13 +577,20 @@ 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 *dwc = dep->dwc;
|
|
|
struct dwc3_trb_hw *trb_hw;
|
|
|
struct dwc3_trb trb;
|
|
|
|
|
|
unsigned int cur_slot;
|
|
|
|
|
|
+ dev_vdbg(dwc->dev, "%s: req %p dma %08llx length %d%s%s\n",
|
|
|
+ dep->name, req, (unsigned long long) dma,
|
|
|
+ length, last ? " last" : "",
|
|
|
+ chain ? " chain" : "");
|
|
|
+
|
|
|
trb_hw = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
|
|
|
cur_slot = dep->free_slot;
|
|
|
dep->free_slot++;
|
|
|
@@ -561,15 +600,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 +643,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 +704,58 @@ 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_mapped_sgs > 0) {
|
|
|
+ struct usb_request *request = &req->request;
|
|
|
+ struct scatterlist *sg = request->sg;
|
|
|
+ struct scatterlist *s;
|
|
|
+ int i;
|
|
|
|
|
|
- /* Is this the last request? */
|
|
|
- if (list_empty(&dep->request_list))
|
|
|
- last_one = 1;
|
|
|
+ for_each_sg(sg, s, request->num_mapped_sgs, i) {
|
|
|
+ unsigned chain = true;
|
|
|
|
|
|
- dwc3_prepare_one_trb(dep, req, last_one);
|
|
|
+ length = sg_dma_len(s);
|
|
|
+ dma = sg_dma_address(s);
|
|
|
|
|
|
- if (last_one)
|
|
|
- break;
|
|
|
+ if (i == (request->num_mapped_sgs - 1)
|
|
|
+ || sg_is_last(s)) {
|
|
|
+ last_one = true;
|
|
|
+ chain = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ trbs_left--;
|
|
|
+ if (!trbs_left)
|
|
|
+ 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_is_last(&req->list, &dep->request_list))
|
|
|
+ last_one = 1;
|
|
|
+
|
|
|
+ dwc3_prepare_one_trb(dep, req, dma, length,
|
|
|
+ last_one, false);
|
|
|
+
|
|
|
+ if (last_one)
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -1989,6 +2069,7 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
|
|
|
dwc->gadget.max_speed = USB_SPEED_SUPER;
|
|
|
dwc->gadget.speed = USB_SPEED_UNKNOWN;
|
|
|
dwc->gadget.dev.parent = dwc->dev;
|
|
|
+ dwc->gadget.sg_supported = true;
|
|
|
|
|
|
dma_set_coherent_mask(&dwc->gadget.dev, dwc->dev->coherent_dma_mask);
|
|
|
|