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>
This commit is contained in:
parent
d7c747c590
commit
7d24c1b5a7
|
@ -158,10 +158,12 @@ struct s3c_hsotg_ep {
|
||||||
* struct s3c_hsotg_req - data transfer request
|
* struct s3c_hsotg_req - data transfer request
|
||||||
* @req: The USB gadget request
|
* @req: The USB gadget request
|
||||||
* @queue: The list of requests for the endpoint this is queued for.
|
* @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 s3c_hsotg_req {
|
||||||
struct usb_request req;
|
struct usb_request req;
|
||||||
struct list_head queue;
|
struct list_head queue;
|
||||||
|
void *saved_req_buf;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
|
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
|
||||||
|
|
|
@ -726,6 +726,59 @@ static int s3c_hsotg_map_dma(struct dwc2_hsotg *hsotg,
|
||||||
return -EIO;
|
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,
|
static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
|
||||||
gfp_t gfp_flags)
|
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 s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||||
struct dwc2_hsotg *hs = hs_ep->parent;
|
struct dwc2_hsotg *hs = hs_ep->parent;
|
||||||
bool first;
|
bool first;
|
||||||
|
int ret;
|
||||||
|
|
||||||
dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n",
|
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,
|
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->actual = 0;
|
||||||
req->status = -EINPROGRESS;
|
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 we're using DMA, sync the buffers as necessary */
|
||||||
if (using_dma(hs)) {
|
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)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1326,6 +1384,8 @@ static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg,
|
||||||
if (hs_req->req.status == -EINPROGRESS)
|
if (hs_req->req.status == -EINPROGRESS)
|
||||||
hs_req->req.status = result;
|
hs_req->req.status = result;
|
||||||
|
|
||||||
|
s3c_hsotg_handle_unaligned_buf_complete(hsotg, hs_ep, hs_req);
|
||||||
|
|
||||||
hs_ep->req = NULL;
|
hs_ep->req = NULL;
|
||||||
list_del_init(&hs_req->queue);
|
list_del_init(&hs_req->queue);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue