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: Felipe Balbi <felipe.balbi@linux.intel.com>
This commit is contained in:
parent
4199c5f8bc
commit
d6e5a549cc
3 changed files with 59 additions and 83 deletions
|
@ -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 {
|
||||||
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 {
|
||||||
/**
|
/**
|
||||||
* 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 {
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
* 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,
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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 @@ err0:
|
||||||
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)
|
||||||
|
|
Loading…
Add table
Reference in a new issue