|
@@ -992,12 +992,33 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
|
|
|
int 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;
|
|
|
|
|
|
if (sg_is_last(s))
|
|
|
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))
|
|
|
break;
|
|
@@ -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,
|
|
|
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,
|
|
|
if (chain && (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)
|
|
|
return 1;
|
|
|
|
|
@@ -2120,6 +2172,13 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
|
|
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;
|
|
|
|
|
|
if ((req->request.actual < length) && req->num_pending_sgs)
|
|
@@ -3058,12 +3117,6 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|
|
|
|
|
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
|
|
|
* sure we're starting from a well known location.
|