Commit c6267a51 authored by Felipe Balbi's avatar Felipe Balbi

usb: dwc3: gadget: align transfers to wMaxPacketSize

Instead of passing quirk_ep_out_aligned_size, we can use one extra TRB
to align transfer to wMaxPacketSize.
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
parent 905dc04e
...@@ -725,6 +725,7 @@ struct dwc3_hwparams { ...@@ -725,6 +725,7 @@ struct dwc3_hwparams {
* @epnum: endpoint number to which this request refers * @epnum: endpoint number to which this request refers
* @trb: pointer to struct dwc3_trb * @trb: pointer to struct dwc3_trb
* @trb_dma: DMA address of @trb * @trb_dma: DMA address of @trb
* @unaligned: true for OUT endpoints with length not divisible by maxp
* @direction: IN or OUT direction flag * @direction: IN or OUT direction flag
* @mapped: true when request has been dma-mapped * @mapped: true when request has been dma-mapped
* @queued: true when request has been queued to HW * @queued: true when request has been queued to HW
...@@ -741,6 +742,7 @@ struct dwc3_request { ...@@ -741,6 +742,7 @@ struct dwc3_request {
struct dwc3_trb *trb; struct dwc3_trb *trb;
dma_addr_t trb_dma; dma_addr_t trb_dma;
unsigned unaligned:1;
unsigned direction:1; unsigned direction:1;
unsigned mapped:1; unsigned mapped:1;
unsigned started:1; unsigned started:1;
......
...@@ -992,12 +992,33 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, ...@@ -992,12 +992,33 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
int i; int i;
for_each_sg(sg, s, req->num_pending_sgs, i) { for_each_sg(sg, s, req->num_pending_sgs, i) {
unsigned int length = req->request.length;
unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
unsigned int rem = length % maxp;
unsigned chain = true; unsigned chain = true;
if (sg_is_last(s)) if (sg_is_last(s))
chain = false; chain = false;
dwc3_prepare_one_trb(dep, req, chain, i); if (rem && usb_endpoint_dir_out(dep->endpoint.desc) && !chain) {
struct dwc3 *dwc = dep->dwc;
struct dwc3_trb *trb;
req->unaligned = true;
/* prepare normal TRB */
dwc3_prepare_one_trb(dep, req, true, i);
/* Now prepare one extra TRB to align transfer size */
trb = &dep->trb_pool[dep->trb_enqueue];
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr,
maxp - rem, false, 0,
req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt);
} else {
dwc3_prepare_one_trb(dep, req, chain, i);
}
if (!dwc3_calc_trbs_left(dep)) if (!dwc3_calc_trbs_left(dep))
break; break;
...@@ -1007,7 +1028,28 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, ...@@ -1007,7 +1028,28 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
struct dwc3_request *req) struct dwc3_request *req)
{ {
dwc3_prepare_one_trb(dep, req, false, 0); unsigned int length = req->request.length;
unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
unsigned int rem = length % maxp;
if (rem && usb_endpoint_dir_out(dep->endpoint.desc)) {
struct dwc3 *dwc = dep->dwc;
struct dwc3_trb *trb;
req->unaligned = true;
/* prepare normal TRB */
dwc3_prepare_one_trb(dep, req, true, 0);
/* Now prepare one extra TRB to align transfer size */
trb = &dep->trb_pool[dep->trb_enqueue];
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
false, 0, req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt);
} else {
dwc3_prepare_one_trb(dep, req, false, 0);
}
} }
/* /*
...@@ -2031,6 +2073,16 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep, ...@@ -2031,6 +2073,16 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
if (chain && (trb->ctrl & DWC3_TRB_CTRL_HWO)) if (chain && (trb->ctrl & DWC3_TRB_CTRL_HWO))
trb->ctrl &= ~DWC3_TRB_CTRL_HWO; trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
/*
* If we're dealing with unaligned size OUT transfer, we will be left
* with one TRB pending in the ring. We need to manually clear HWO bit
* from that TRB.
*/
if (req->unaligned && (trb->ctrl & DWC3_TRB_CTRL_HWO)) {
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
return 1;
}
if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN) if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
return 1; return 1;
...@@ -2120,6 +2172,13 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, ...@@ -2120,6 +2172,13 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
event, status, chain); event, status, chain);
} }
if (req->unaligned) {
trb = &dep->trb_pool[dep->trb_dequeue];
ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
event, status, false);
req->unaligned = false;
}
req->request.actual = length - req->remaining; req->request.actual = length - req->remaining;
if ((req->request.actual < length) && req->num_pending_sgs) if ((req->request.actual < length) && req->num_pending_sgs)
...@@ -3058,12 +3117,6 @@ int dwc3_gadget_init(struct dwc3 *dwc) ...@@ -3058,12 +3117,6 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dwc->gadget.max_speed = dwc->maximum_speed; dwc->gadget.max_speed = dwc->maximum_speed;
/*
* Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize
* on ep out.
*/
dwc->gadget.quirk_ep_out_aligned_size = true;
/* /*
* REVISIT: Here we should clear all pending IRQs to be * REVISIT: Here we should clear all pending IRQs to be
* sure we're starting from a well known location. * sure we're starting from a well known location.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment