Skip to content

Commit

Permalink
usb: dwc3: gadget: add support for SG lists
Browse files Browse the repository at this point in the history
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@ti.com>
  • Loading branch information
Felipe Balbi committed Dec 21, 2011
1 parent 42f8eb7 commit eeb720f
Showing 1 changed file with 98 additions and 17 deletions.
115 changes: 98 additions & 17 deletions drivers/usb/dwc3/gadget.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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++;
Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
}

/*
Expand Down Expand Up @@ -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;
}
}
}

Expand Down Expand Up @@ -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);

Expand Down

0 comments on commit eeb720f

Please sign in to comment.