Skip to content

Commit

Permalink
usb: dwc3: simplify ZLP handling
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Felipe Balbi committed Apr 11, 2017
1 parent 4199c5f commit d6e5a54
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 83 deletions.
4 changes: 1 addition & 3 deletions drivers/usb/dwc3/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@

/* Global constants */
#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_EP0_SETUP_SIZE 512
#define DWC3_ENDPOINTS_NUM 32
Expand Down Expand Up @@ -748,6 +747,7 @@ struct dwc3_request {
unsigned direction:1;
unsigned mapped:1;
unsigned started:1;
unsigned zero:1;
};

/*
Expand All @@ -761,7 +761,6 @@ struct dwc3_scratchpad_array {
/**
* struct dwc3 - representation of our controller
* @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
* @ep0_trb: dma address of ep0_trb
* @ep0_usb_req: dummy req used while handling STD USB requests
Expand Down Expand Up @@ -857,7 +856,6 @@ struct dwc3_scratchpad_array {
struct dwc3 {
struct dwc3_trb *ep0_trb;
void *bounce;
void *zlp_buf;
void *scratchbuf;
u8 *setup_buf;
dma_addr_t ep0_trb_addr;
Expand Down
49 changes: 29 additions & 20 deletions drivers/usb/dwc3/ep0.c
Original file line number Diff line number Diff line change
Expand Up @@ -873,34 +873,19 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
transferred = ur->length - length;
ur->actual += transferred;

if (dwc->ep0_bounced) {
if ((IS_ALIGNED(ur->length, ep0->endpoint.maxpacket) &&
ur->length && ur->zero) || dwc->ep0_bounced) {
trb++;
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
trace_dwc3_complete_trb(ep0, trb);
ep0->trb_enqueue = 0;
dwc->ep0_bounced = false;
}

if ((epnum & 1) && ur->actual < ur->length) {
/* for some reason we did not get everything out */

if ((epnum & 1) && ur->actual < ur->length)
dwc3_ep0_stall_and_restart(dwc);
} else {
else
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,
Expand Down Expand Up @@ -1005,6 +990,30 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
DWC3_TRBCTL_CONTROL_DATA,
false);
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 {
ret = usb_gadget_map_request_by_dev(dwc->sysdev,
&req->request, dep->number);
Expand Down
89 changes: 29 additions & 60 deletions drivers/usb/dwc3/gadget.c
Original file line number Diff line number Diff line change
Expand Up @@ -1044,6 +1044,22 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
false, 0, req->request.stream_id,
req->request.short_not_ok,
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 {
dwc3_prepare_one_trb(dep, req, false, 0);
}
Expand Down Expand Up @@ -1259,31 +1275,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
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,
gfp_t gfp_flags)
{
Expand All @@ -1297,17 +1288,6 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,

spin_lock_irqsave(&dwc->lock, flags);
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);

return ret;
Expand Down Expand Up @@ -1387,7 +1367,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
dwc3_ep_inc_deq(dep);
}

if (r->unaligned) {
if (r->unaligned || r->zero) {
trb = r->trb + r->num_pending_sgs + 1;
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_deq(dep);
Expand All @@ -1398,7 +1378,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_deq(dep);

if (r->unaligned) {
if (r->unaligned || r->zero) {
trb = r->trb + 1;
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_deq(dep);
Expand Down Expand Up @@ -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
* 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;
return 1;
}
Expand Down Expand Up @@ -2258,11 +2238,12 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
event, status, chain);
}

if (req->unaligned) {
if (req->unaligned || req->zero) {
trb = &dep->trb_pool[dep->trb_dequeue];
ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
event, status, false);
req->unaligned = false;
req->zero = false;
}

req->request.actual = length - req->remaining;
Expand Down Expand Up @@ -3143,17 +3124,11 @@ int dwc3_gadget_init(struct dwc3 *dwc)
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_addr, GFP_KERNEL);
if (!dwc->bounce) {
ret = -ENOMEM;
goto err3;
goto err2;
}

init_completion(&dwc->ep0_in_setup);
Expand Down Expand Up @@ -3193,24 +3168,22 @@ int dwc3_gadget_init(struct dwc3 *dwc)

ret = dwc3_gadget_init_endpoints(dwc, dwc->num_eps);
if (ret)
goto err4;
goto err3;

ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
if (ret) {
dev_err(dwc->dev, "failed to register udc\n");
goto err5;
goto err4;
}

return 0;
err5:
dwc3_gadget_free_endpoints(dwc);

err4:
dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
dwc->bounce_addr);
dwc3_gadget_free_endpoints(dwc);

err3:
kfree(dwc->zlp_buf);
dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
dwc->bounce_addr);

err2:
kfree(dwc->setup_buf);
Expand All @@ -3228,16 +3201,12 @@ int dwc3_gadget_init(struct dwc3 *dwc)
void dwc3_gadget_exit(struct dwc3 *dwc)
{
usb_del_gadget_udc(&dwc->gadget);

dwc3_gadget_free_endpoints(dwc);

dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
dwc->bounce_addr);
dwc->bounce_addr);
kfree(dwc->setup_buf);
kfree(dwc->zlp_buf);

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)
Expand Down

0 comments on commit d6e5a54

Please sign in to comment.