Commit d6e5a549 authored by Felipe Balbi's avatar Felipe Balbi

usb: dwc3: simplify ZLP handling

It's much simpler to just add one extra TRB chained to previous TRB to
handle ZLP. This helps us reduce pointless allocations and simplifies
the code a little bit.
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
parent 4199c5f8
...@@ -40,7 +40,6 @@ ...@@ -40,7 +40,6 @@
/* Global constants */ /* Global constants */
#define DWC3_PULL_UP_TIMEOUT 500 /* ms */ #define DWC3_PULL_UP_TIMEOUT 500 /* ms */
#define DWC3_ZLP_BUF_SIZE 1024 /* size of a superspeed bulk */
#define DWC3_BOUNCE_SIZE 1024 /* size of a superspeed bulk */ #define DWC3_BOUNCE_SIZE 1024 /* size of a superspeed bulk */
#define DWC3_EP0_SETUP_SIZE 512 #define DWC3_EP0_SETUP_SIZE 512
#define DWC3_ENDPOINTS_NUM 32 #define DWC3_ENDPOINTS_NUM 32
...@@ -748,6 +747,7 @@ struct dwc3_request { ...@@ -748,6 +747,7 @@ struct dwc3_request {
unsigned direction:1; unsigned direction:1;
unsigned mapped:1; unsigned mapped:1;
unsigned started:1; unsigned started:1;
unsigned zero:1;
}; };
/* /*
...@@ -761,7 +761,6 @@ struct dwc3_scratchpad_array { ...@@ -761,7 +761,6 @@ struct dwc3_scratchpad_array {
/** /**
* struct dwc3 - representation of our controller * struct dwc3 - representation of our controller
* @ep0_trb: trb which is used for the ctrl_req * @ep0_trb: trb which is used for the ctrl_req
* @zlp_buf: used when request->zero is set
* @setup_buf: used while precessing STD USB requests * @setup_buf: used while precessing STD USB requests
* @ep0_trb: dma address of ep0_trb * @ep0_trb: dma address of ep0_trb
* @ep0_usb_req: dummy req used while handling STD USB requests * @ep0_usb_req: dummy req used while handling STD USB requests
...@@ -857,7 +856,6 @@ struct dwc3_scratchpad_array { ...@@ -857,7 +856,6 @@ struct dwc3_scratchpad_array {
struct dwc3 { struct dwc3 {
struct dwc3_trb *ep0_trb; struct dwc3_trb *ep0_trb;
void *bounce; void *bounce;
void *zlp_buf;
void *scratchbuf; void *scratchbuf;
u8 *setup_buf; u8 *setup_buf;
dma_addr_t ep0_trb_addr; dma_addr_t ep0_trb_addr;
......
...@@ -873,34 +873,19 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, ...@@ -873,34 +873,19 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
transferred = ur->length - length; transferred = ur->length - length;
ur->actual += transferred; ur->actual += transferred;
if (dwc->ep0_bounced) { if ((IS_ALIGNED(ur->length, ep0->endpoint.maxpacket) &&
ur->length && ur->zero) || dwc->ep0_bounced) {
trb++; trb++;
trb->ctrl &= ~DWC3_TRB_CTRL_HWO; trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
trace_dwc3_complete_trb(ep0, trb);
ep0->trb_enqueue = 0; ep0->trb_enqueue = 0;
dwc->ep0_bounced = false; dwc->ep0_bounced = false;
} }
if ((epnum & 1) && ur->actual < ur->length) { if ((epnum & 1) && ur->actual < ur->length)
/* for some reason we did not get everything out */
dwc3_ep0_stall_and_restart(dwc); dwc3_ep0_stall_and_restart(dwc);
} else { else
dwc3_gadget_giveback(ep0, r, 0); dwc3_gadget_giveback(ep0, r, 0);
if (IS_ALIGNED(ur->length, ep0->endpoint.maxpacket) &&
ur->length && ur->zero) {
struct dwc3_ep *dep;
int ret;
dwc->ep0_next_event = DWC3_EP0_COMPLETE;
dep = dwc->eps[epnum];
dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr,
0, DWC3_TRBCTL_CONTROL_DATA, false);
ret = dwc3_ep0_start_trans(dep);
WARN_ON(ret < 0);
}
}
} }
static void dwc3_ep0_complete_status(struct dwc3 *dwc, static void dwc3_ep0_complete_status(struct dwc3 *dwc,
...@@ -1005,6 +990,30 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, ...@@ -1005,6 +990,30 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
DWC3_TRBCTL_CONTROL_DATA, DWC3_TRBCTL_CONTROL_DATA,
false); false);
ret = dwc3_ep0_start_trans(dep); ret = dwc3_ep0_start_trans(dep);
} else if (IS_ALIGNED(req->request.length, dep->endpoint.maxpacket) &&
req->request.length && req->request.zero) {
u32 maxpacket;
u32 rem;
ret = usb_gadget_map_request_by_dev(dwc->sysdev,
&req->request, dep->number);
if (ret)
return;
maxpacket = dep->endpoint.maxpacket;
rem = req->request.length % maxpacket;
/* prepare normal TRB */
dwc3_ep0_prepare_one_trb(dep, req->request.dma,
req->request.length,
DWC3_TRBCTL_CONTROL_DATA,
true);
/* Now prepare one extra TRB to align transfer size */
dwc3_ep0_prepare_one_trb(dep, dwc->bounce_addr,
0, DWC3_TRBCTL_CONTROL_DATA,
false);
ret = dwc3_ep0_start_trans(dep);
} else { } else {
ret = usb_gadget_map_request_by_dev(dwc->sysdev, ret = usb_gadget_map_request_by_dev(dwc->sysdev,
&req->request, dep->number); &req->request, dep->number);
......
...@@ -1044,6 +1044,22 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, ...@@ -1044,6 +1044,22 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
false, 0, req->request.stream_id, false, 0, req->request.stream_id,
req->request.short_not_ok, req->request.short_not_ok,
req->request.no_interrupt); req->request.no_interrupt);
} else if (req->request.zero && req->request.length &&
(IS_ALIGNED(req->request.length,dep->endpoint.maxpacket))) {
struct dwc3 *dwc = dep->dwc;
struct dwc3_trb *trb;
req->zero = true;
/* prepare normal TRB */
dwc3_prepare_one_trb(dep, req, true, 0);
/* Now prepare one extra TRB to handle ZLP */
trb = &dep->trb_pool[dep->trb_enqueue];
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
false, 0, req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt);
} else { } else {
dwc3_prepare_one_trb(dep, req, false, 0); dwc3_prepare_one_trb(dep, req, false, 0);
} }
...@@ -1259,31 +1275,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) ...@@ -1259,31 +1275,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
return ret; return ret;
} }
static void __dwc3_gadget_ep_zlp_complete(struct usb_ep *ep,
struct usb_request *request)
{
dwc3_gadget_ep_free_request(ep, request);
}
static int __dwc3_gadget_ep_queue_zlp(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_request *req;
struct usb_request *request;
struct usb_ep *ep = &dep->endpoint;
request = dwc3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
if (!request)
return -ENOMEM;
request->length = 0;
request->buf = dwc->zlp_buf;
request->complete = __dwc3_gadget_ep_zlp_complete;
req = to_dwc3_request(request);
return __dwc3_gadget_ep_queue(dep, req);
}
static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags) gfp_t gfp_flags)
{ {
...@@ -1297,17 +1288,6 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, ...@@ -1297,17 +1288,6 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
spin_lock_irqsave(&dwc->lock, flags); spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_queue(dep, req); ret = __dwc3_gadget_ep_queue(dep, req);
/*
* Okay, here's the thing, if gadget driver has requested for a ZLP by
* setting request->zero, instead of doing magic, we will just queue an
* extra usb_request ourselves so that it gets handled the same way as
* any other request.
*/
if (ret == 0 && request->zero && request->length &&
(request->length % ep->maxpacket == 0))
ret = __dwc3_gadget_ep_queue_zlp(dwc, dep);
spin_unlock_irqrestore(&dwc->lock, flags); spin_unlock_irqrestore(&dwc->lock, flags);
return ret; return ret;
...@@ -1387,7 +1367,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, ...@@ -1387,7 +1367,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
dwc3_ep_inc_deq(dep); dwc3_ep_inc_deq(dep);
} }
if (r->unaligned) { if (r->unaligned || r->zero) {
trb = r->trb + r->num_pending_sgs + 1; trb = r->trb + r->num_pending_sgs + 1;
trb->ctrl &= ~DWC3_TRB_CTRL_HWO; trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_deq(dep); dwc3_ep_inc_deq(dep);
...@@ -1398,7 +1378,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, ...@@ -1398,7 +1378,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
trb->ctrl &= ~DWC3_TRB_CTRL_HWO; trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_deq(dep); dwc3_ep_inc_deq(dep);
if (r->unaligned) { if (r->unaligned || r->zero) {
trb = r->trb + 1; trb = r->trb + 1;
trb->ctrl &= ~DWC3_TRB_CTRL_HWO; trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_deq(dep); dwc3_ep_inc_deq(dep);
...@@ -2164,7 +2144,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep, ...@@ -2164,7 +2144,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
* with one TRB pending in the ring. We need to manually clear HWO bit * with one TRB pending in the ring. We need to manually clear HWO bit
* from that TRB. * from that TRB.
*/ */
if (req->unaligned && (trb->ctrl & DWC3_TRB_CTRL_HWO)) { if ((req->zero || req->unaligned) && (trb->ctrl & DWC3_TRB_CTRL_HWO)) {
trb->ctrl &= ~DWC3_TRB_CTRL_HWO; trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
return 1; return 1;
} }
...@@ -2258,11 +2238,12 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, ...@@ -2258,11 +2238,12 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
event, status, chain); event, status, chain);
} }
if (req->unaligned) { if (req->unaligned || req->zero) {
trb = &dep->trb_pool[dep->trb_dequeue]; trb = &dep->trb_pool[dep->trb_dequeue];
ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb, ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
event, status, false); event, status, false);
req->unaligned = false; req->unaligned = false;
req->zero = false;
} }
req->request.actual = length - req->remaining; req->request.actual = length - req->remaining;
...@@ -3143,17 +3124,11 @@ int dwc3_gadget_init(struct dwc3 *dwc) ...@@ -3143,17 +3124,11 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err1; goto err1;
} }
dwc->zlp_buf = kzalloc(DWC3_ZLP_BUF_SIZE, GFP_KERNEL);
if (!dwc->zlp_buf) {
ret = -ENOMEM;
goto err2;
}
dwc->bounce = dma_alloc_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce = dma_alloc_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE,
&dwc->bounce_addr, GFP_KERNEL); &dwc->bounce_addr, GFP_KERNEL);
if (!dwc->bounce) { if (!dwc->bounce) {
ret = -ENOMEM; ret = -ENOMEM;
goto err3; goto err2;
} }
init_completion(&dwc->ep0_in_setup); init_completion(&dwc->ep0_in_setup);
...@@ -3193,24 +3168,22 @@ int dwc3_gadget_init(struct dwc3 *dwc) ...@@ -3193,24 +3168,22 @@ int dwc3_gadget_init(struct dwc3 *dwc)
ret = dwc3_gadget_init_endpoints(dwc, dwc->num_eps); ret = dwc3_gadget_init_endpoints(dwc, dwc->num_eps);
if (ret) if (ret)
goto err4; goto err3;
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget); ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
if (ret) { if (ret) {
dev_err(dwc->dev, "failed to register udc\n"); dev_err(dwc->dev, "failed to register udc\n");
goto err5; goto err4;
} }
return 0; return 0;
err5:
dwc3_gadget_free_endpoints(dwc);
err4: err4:
dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce, dwc3_gadget_free_endpoints(dwc);
dwc->bounce_addr);
err3: err3:
kfree(dwc->zlp_buf); dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
dwc->bounce_addr);
err2: err2:
kfree(dwc->setup_buf); kfree(dwc->setup_buf);
...@@ -3228,16 +3201,12 @@ int dwc3_gadget_init(struct dwc3 *dwc) ...@@ -3228,16 +3201,12 @@ int dwc3_gadget_init(struct dwc3 *dwc)
void dwc3_gadget_exit(struct dwc3 *dwc) void dwc3_gadget_exit(struct dwc3 *dwc)
{ {
usb_del_gadget_udc(&dwc->gadget); usb_del_gadget_udc(&dwc->gadget);
dwc3_gadget_free_endpoints(dwc); dwc3_gadget_free_endpoints(dwc);
dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce, dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
dwc->bounce_addr); dwc->bounce_addr);
kfree(dwc->setup_buf); kfree(dwc->setup_buf);
kfree(dwc->zlp_buf);
dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2, dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2,
dwc->ep0_trb, dwc->ep0_trb_addr); dwc->ep0_trb, dwc->ep0_trb_addr);
} }
int dwc3_gadget_suspend(struct dwc3 *dwc) int dwc3_gadget_suspend(struct dwc3 *dwc)
......
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