usb: dwc3: ep0: add handling for unaligned OUT transfers

In case we have transfers which aren't aligned
to wMaxPacketSize, we need to be careful with
how we start the transfer with the HW. OUT
transfers _must_ be aligned with wMaxPacketSize
and in order to guarantee that, we use a bounce
buffer.

Signed-off-by: Felipe Balbi <balbi@ti.com>
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index a6fc5c3..f1e0a5e 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -185,10 +185,29 @@
 	req->epnum		= dep->number;
 
 	list_add_tail(&req->list, &dep->request_list);
-	dwc3_map_buffer_to_dma(req);
+	if (req->request.length == 0) {
+		ret = dwc3_ep0_start_trans(dwc, dep->number,
+				dwc->ctrl_req_addr, 0);
+	} else if ((req->request.length % dep->endpoint.maxpacket)
+			&& (dep->number == 0)) {
+		dwc->ep0_bounced = true;
 
-	ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma,
-			req->request.length);
+		WARN_ON(req->request.length > dep->endpoint.maxpacket);
+
+		/*
+		 * REVISIT in case request length is bigger than EP0
+		 * wMaxPacketSize, we will need two chained TRBs to handle
+		 * the transfer.
+		 */
+		ret = dwc3_ep0_start_trans(dwc, dep->number,
+				dwc->ep0_bounce_addr, dep->endpoint.maxpacket);
+	} else {
+		dwc3_map_buffer_to_dma(req);
+
+		ret = dwc3_ep0_start_trans(dwc, dep->number,
+				req->request.dma, req->request.length);
+	}
+
 	if (ret < 0) {
 		list_del(&req->list);
 		dwc3_unmap_buffer_from_dma(req);
@@ -655,8 +674,16 @@
 
 	dwc3_trb_to_nat(dwc->ep0_trb, &trb);
 
-	transferred = ur->length - trb.length;
-	ur->actual += transferred;
+	if (dwc->ep0_bounced) {
+		struct dwc3_ep	*ep0 = dwc->eps[0];
+
+		transferred = min(ur->length, dep->endpoint.maxpacket - trb.length);
+		memcpy(ur->buf, dwc->ep0_bounce, transferred);
+		dwc->ep0_bounced = false;
+	} else {
+		transferred = ur->length - trb.length;
+		ur->actual += transferred;
+	}
 
 	if ((epnum & 1) && ur->actual < ur->length) {
 		/* for some reason we did not get everything out */