summaryrefslogtreecommitdiffstats
path: root/drivers/usb/dwc3/gadget.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc3/gadget.c')
-rw-r--r--drivers/usb/dwc3/gadget.c141
1 files changed, 79 insertions, 62 deletions
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index cebaef7..859257a 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -61,16 +61,16 @@ void dwc3_map_buffer_to_dma(struct dwc3_request *req)
{
struct dwc3 *dwc = req->dep->dwc;
+ if (req->request.length == 0) {
+ /* req->request.dma = dwc->setup_buf_addr; */
+ return;
+ }
+
if (req->request.dma == DMA_ADDR_INVALID) {
req->request.dma = dma_map_single(dwc->dev, req->request.buf,
req->request.length, req->direction
? DMA_TO_DEVICE : DMA_FROM_DEVICE);
req->mapped = true;
- } else {
- dma_sync_single_for_device(dwc->dev, req->request.dma,
- req->request.length, req->direction
- ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
- req->mapped = false;
}
}
@@ -78,15 +78,17 @@ void dwc3_unmap_buffer_from_dma(struct dwc3_request *req)
{
struct dwc3 *dwc = req->dep->dwc;
+ if (req->request.length == 0) {
+ req->request.dma = DMA_ADDR_INVALID;
+ return;
+ }
+
if (req->mapped) {
dma_unmap_single(dwc->dev, req->request.dma,
req->request.length, req->direction
? DMA_TO_DEVICE : DMA_FROM_DEVICE);
req->mapped = 0;
- } else {
- dma_sync_single_for_cpu(dwc->dev, req->request.dma,
- req->request.length, req->direction
- ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ req->request.dma = DMA_ADDR_INVALID;
}
}
@@ -152,7 +154,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
{
struct dwc3_ep *dep = dwc->eps[ep];
- unsigned long timeout = 500;
+ u32 timeout = 500;
u32 reg;
dev_vdbg(dwc->dev, "%s: cmd '%s' params %08x %08x %08x\n",
@@ -168,13 +170,12 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
do {
reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep));
if (!(reg & DWC3_DEPCMD_CMDACT)) {
- dev_vdbg(dwc->dev, "CMD Compl Status %d DEPCMD %04x\n",
- ((reg & 0xf000) >> 12), reg);
+ dev_vdbg(dwc->dev, "Command Complete --> %d\n",
+ DWC3_DEPCMD_STATUS(reg));
return 0;
}
/*
- * XXX Figure out a sane timeout here. 500ms is way too much.
* We can't sleep here, because it is also called from
* interrupt context.
*/
@@ -182,7 +183,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
if (!timeout)
return -ETIMEDOUT;
- mdelay(1);
+ udelay(1);
} while (1);
}
@@ -358,34 +359,36 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
return 0;
}
-static void dwc3_gadget_nuke_reqs(struct dwc3_ep *dep, const int status)
+static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum);
+static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_request *req;
+ if (!list_empty(&dep->req_queued))
+ dwc3_stop_active_transfer(dwc, dep->number);
+
while (!list_empty(&dep->request_list)) {
req = next_request(&dep->request_list);
- dwc3_gadget_giveback(dep, req, status);
+ dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
}
- /* nuke queued TRBs as well on command complete */
- dep->flags |= DWC3_EP_WILL_SHUTDOWN;
}
/**
* __dwc3_gadget_ep_disable - Disables a HW endpoint
* @dep: the endpoint to disable
*
- * Caller should take care of locking
+ * This function also removes requests which are currently processed ny the
+ * hardware and those which are not yet scheduled.
+ * Caller should take care of locking.
*/
-static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum);
static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
{
struct dwc3 *dwc = dep->dwc;
u32 reg;
dep->flags &= ~DWC3_EP_ENABLED;
- dwc3_stop_active_transfer(dwc, dep->number);
- dwc3_gadget_nuke_reqs(dep, -ESHUTDOWN);
+ dwc3_remove_requests(dwc, dep);
reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
reg &= ~DWC3_DALEPENA_EP(dep->number);
@@ -632,7 +635,7 @@ static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep,
break;
case USB_ENDPOINT_XFER_ISOC:
- trb.trbctl = DWC3_TRBCTL_ISOCHRONOUS;
+ trb.trbctl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
/* IOC every DWC3_TRB_NUM / 4 so we can refill */
if (!(cur_slot % (DWC3_TRB_NUM / 4)))
@@ -870,8 +873,14 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value)
memset(&params, 0x00, sizeof(params));
if (value) {
- if (dep->number == 0 || dep->number == 1)
- dwc->ep0state = EP0_STALL;
+ if (dep->number == 0 || dep->number == 1) {
+ /*
+ * Whenever EP0 is stalled, we will restart
+ * the state machine, thus moving back to
+ * Setup Phase
+ */
+ dwc->ep0state = EP0_SETUP_PHASE;
+ }
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
DWC3_DEPCMD_SETSTALL, &params);
@@ -1063,7 +1072,7 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
{
u32 reg;
- unsigned long timeout = 500;
+ u32 timeout = 500;
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (is_on)
@@ -1082,13 +1091,10 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
if (reg & DWC3_DSTS_DEVCTRLHLT)
break;
}
- /*
- * XXX reduce the 500ms delay
- */
timeout--;
if (!timeout)
break;
- mdelay(1);
+ udelay(1);
} while (1);
dev_vdbg(dwc->dev, "gadget %s data soft-%s\n",
@@ -1135,13 +1141,10 @@ static int dwc3_gadget_start(struct usb_gadget *g,
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
- /*
- * REVISIT: power down scale might be different
- * depending on PHY used, need to pass that via platform_data
- */
- reg |= DWC3_GCTL_PWRDNSCALE(0x61a)
- | DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_DEVICE);
+ reg &= ~DWC3_GCTL_SCALEDOWN(3);
+ reg &= ~DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG);
reg &= ~DWC3_GCTL_DISSCRAMBLE;
+ reg |= DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_DEVICE);
/*
* WORKAROUND: DWC3 revisions <1.90a have a bug
@@ -1177,7 +1180,7 @@ static int dwc3_gadget_start(struct usb_gadget *g,
}
/* begin to receive SETUP packets */
- dwc->ep0state = EP0_IDLE;
+ dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -1309,11 +1312,17 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
dwc3_trb_to_nat(req->trb, &trb);
- if (trb.hwo) {
+ if (trb.hwo && status != -ESHUTDOWN)
+ /*
+ * We continue despite the error. There is not much we
+ * can do. If we don't clean in up we loop for ever. If
+ * we skip the TRB than it gets overwritten reused after
+ * a while since we use them in a ring buffer. a BUG()
+ * would help. Lets hope that if this occures, someone
+ * fixes the root cause instead of looking away :)
+ */
dev_err(dwc->dev, "%s's TRB (%p) still owned by HW\n",
dep->name, req->trb);
- continue;
- }
count = trb.length;
if (dep->direction) {
@@ -1360,8 +1369,10 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
status = -ECONNRESET;
clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status);
- if (clean_busy)
+ if (clean_busy) {
dep->flags &= ~DWC3_EP_BUSY;
+ dep->res_trans_idx = 0;
+ }
}
static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
@@ -1407,16 +1418,6 @@ static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep,
dwc3_cleanup_done_reqs(dwc, dep, &mod_ev, -ESHUTDOWN);
dep->flags &= ~DWC3_EP_BUSY;
/* pending requets are ignored and are queued on XferNotReady */
-
- if (dep->flags & DWC3_EP_WILL_SHUTDOWN) {
- while (!list_empty(&dep->req_queued)) {
- struct dwc3_request *req;
-
- req = next_request(&dep->req_queued);
- dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
- }
- dep->flags &= DWC3_EP_WILL_SHUTDOWN;
- }
}
static void dwc3_ep_cmd_compl(struct dwc3_ep *dep,
@@ -1524,6 +1525,7 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
dep = dwc->eps[epnum];
+ WARN_ON(!dep->res_trans_idx);
if (dep->res_trans_idx) {
cmd = DWC3_DEPCMD_ENDTRANSFER;
cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
@@ -1531,6 +1533,7 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
memset(&params, 0, sizeof(params));
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
WARN_ON_ONCE(ret);
+ dep->res_trans_idx = 0;
}
}
@@ -1545,7 +1548,7 @@ static void dwc3_stop_active_transfers(struct dwc3 *dwc)
if (!(dep->flags & DWC3_EP_ENABLED))
continue;
- __dwc3_gadget_ep_disable(dep);
+ dwc3_remove_requests(dwc, dep);
}
}
@@ -1718,7 +1721,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
memset(&params, 0x00, sizeof(params));
- dwc->ep0state = EP0_IDLE;
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
speed = reg & DWC3_DSTS_CONNECTSPD;
dwc->speed = speed;
@@ -1790,10 +1792,10 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
unsigned int evtinfo)
{
- dev_vdbg(dwc->dev, "%s\n", __func__);
-
/* The fith bit says SuperSpeed yes or no. */
dwc->link_state = evtinfo & DWC3_LINK_STATE_MASK;
+
+ dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state);
}
static void dwc3_gadget_interrupt(struct dwc3 *dwc,
@@ -1947,6 +1949,14 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
goto err2;
}
+ dwc->ep0_bounce = dma_alloc_coherent(dwc->dev,
+ 512, &dwc->ep0_bounce_addr, GFP_KERNEL);
+ if (!dwc->ep0_bounce) {
+ dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n");
+ ret = -ENOMEM;
+ goto err3;
+ }
+
dev_set_name(&dwc->gadget.dev, "gadget");
dwc->gadget.ops = &dwc3_gadget_ops;
@@ -1968,7 +1978,7 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
ret = dwc3_gadget_init_endpoints(dwc);
if (ret)
- goto err3;
+ goto err4;
irq = platform_get_irq(to_platform_device(dwc->dev), 0);
@@ -1977,7 +1987,7 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
if (ret) {
dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
irq, ret);
- goto err4;
+ goto err5;
}
/* Enable all but Start and End of Frame IRQs */
@@ -1996,27 +2006,31 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
if (ret) {
dev_err(dwc->dev, "failed to register gadget device\n");
put_device(&dwc->gadget.dev);
- goto err5;
+ goto err6;
}
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
if (ret) {
dev_err(dwc->dev, "failed to register udc\n");
- goto err6;
+ goto err7;
}
return 0;
-err6:
+err7:
device_unregister(&dwc->gadget.dev);
-err5:
+err6:
dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
free_irq(irq, dwc);
-err4:
+err5:
dwc3_gadget_free_endpoints(dwc);
+err4:
+ dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce,
+ dwc->ep0_bounce_addr);
+
err3:
dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2,
dwc->setup_buf, dwc->setup_buf_addr);
@@ -2049,6 +2063,9 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
dwc3_gadget_free_endpoints(dwc);
+ dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce,
+ dwc->ep0_bounce_addr);
+
dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2,
dwc->setup_buf, dwc->setup_buf_addr);
OpenPOWER on IntegriCloud