summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMian Yousaf Kaukab <yousaf.kaukab@intel.com>2015-01-30 09:09:31 +0100
committerFelipe Balbi <balbi@ti.com>2015-01-30 10:31:13 -0600
commit7d24c1b5a77c71d885fca047d1f25721f6b366e7 (patch)
treed795985ed06c67578f3df6971f0785d84149a1ab
parentd7c747c590a98d2d75e4c2c97c875f0dcd5743ee (diff)
usb: dwc2: gadget: add unaligned buffers support
When using DMA, dwc2 requires buffers to be 4 bytes aligned. Use bounce buffers if they are not. Tested-by: Robert Baldyga <r.baldyga@samsung.com> Acked-by: John Youn <johnyoun@synopsys.com> Signed-off-by: Mian Yousaf Kaukab <yousaf.kaukab@intel.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
-rw-r--r--drivers/usb/dwc2/core.h2
-rw-r--r--drivers/usb/dwc2/gadget.c62
2 files changed, 63 insertions, 1 deletions
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index c750fd3d88b3..485b195126e7 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -158,10 +158,12 @@ struct s3c_hsotg_ep {
* struct s3c_hsotg_req - data transfer request
* @req: The USB gadget request
* @queue: The list of requests for the endpoint this is queued for.
+ * @saved_req_buf: variable to save req.buf when bounce buffers are used.
*/
struct s3c_hsotg_req {
struct usb_request req;
struct list_head queue;
+ void *saved_req_buf;
};
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index fc9462aff67f..5e91cf154bba 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -726,6 +726,59 @@ dma_error:
return -EIO;
}
+static int s3c_hsotg_handle_unaligned_buf_start(struct dwc2_hsotg *hsotg,
+ struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req)
+{
+ void *req_buf = hs_req->req.buf;
+
+ /* If dma is not being used or buffer is aligned */
+ if (!using_dma(hsotg) || !((long)req_buf & 3))
+ return 0;
+
+ WARN_ON(hs_req->saved_req_buf);
+
+ dev_dbg(hsotg->dev, "%s: %s: buf=%p length=%d\n", __func__,
+ hs_ep->ep.name, req_buf, hs_req->req.length);
+
+ hs_req->req.buf = kmalloc(hs_req->req.length, GFP_ATOMIC);
+ if (!hs_req->req.buf) {
+ hs_req->req.buf = req_buf;
+ dev_err(hsotg->dev,
+ "%s: unable to allocate memory for bounce buffer\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ /* Save actual buffer */
+ hs_req->saved_req_buf = req_buf;
+
+ if (hs_ep->dir_in)
+ memcpy(hs_req->req.buf, req_buf, hs_req->req.length);
+ return 0;
+}
+
+static void s3c_hsotg_handle_unaligned_buf_complete(struct dwc2_hsotg *hsotg,
+ struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req)
+{
+ /* If dma is not being used or buffer was aligned */
+ if (!using_dma(hsotg) || !hs_req->saved_req_buf)
+ return;
+
+ dev_dbg(hsotg->dev, "%s: %s: status=%d actual-length=%d\n", __func__,
+ hs_ep->ep.name, hs_req->req.status, hs_req->req.actual);
+
+ /* Copy data from bounce buffer on successful out transfer */
+ if (!hs_ep->dir_in && !hs_req->req.status)
+ memcpy(hs_req->saved_req_buf, hs_req->req.buf,
+ hs_req->req.actual);
+
+ /* Free bounce buffer */
+ kfree(hs_req->req.buf);
+
+ hs_req->req.buf = hs_req->saved_req_buf;
+ hs_req->saved_req_buf = NULL;
+}
+
static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
gfp_t gfp_flags)
{
@@ -733,6 +786,7 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
struct dwc2_hsotg *hs = hs_ep->parent;
bool first;
+ int ret;
dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n",
ep->name, req, req->length, req->buf, req->no_interrupt,
@@ -743,9 +797,13 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
req->actual = 0;
req->status = -EINPROGRESS;
+ ret = s3c_hsotg_handle_unaligned_buf_start(hs, hs_ep, hs_req);
+ if (ret)
+ return ret;
+
/* if we're using DMA, sync the buffers as necessary */
if (using_dma(hs)) {
- int ret = s3c_hsotg_map_dma(hs, hs_ep, req);
+ ret = s3c_hsotg_map_dma(hs, hs_ep, req);
if (ret)
return ret;
}
@@ -1326,6 +1384,8 @@ static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg,
if (hs_req->req.status == -EINPROGRESS)
hs_req->req.status = result;
+ s3c_hsotg_handle_unaligned_buf_complete(hsotg, hs_ep, hs_req);
+
hs_ep->req = NULL;
list_del_init(&hs_req->queue);