aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/dwc3/gadget.c
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2011-12-12 15:19:40 -0800
committerGreg Kroah-Hartman <gregkh@suse.de>2011-12-12 15:19:53 -0800
commit007d00d4c11b30b8fd7ff23b9d5aed3743e41f74 (patch)
treed5c307ba2ee0e2f56fbc284db21b8bccf95d8a40 /drivers/usb/dwc3/gadget.c
parentc91043adaf50ef13609003120f3471783460fb71 (diff)
parent68d3e668d245bb8300c7c6ddbc8508ddfe352e0f (diff)
Merge branch 'for-next/dwc3' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
* 'for-next/dwc3' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb: (392 commits) usb: dwc3: ep0: fix for possible early delayed_status usb: dwc3: gadget: fix stream enable bit usb: dwc3: ep0: fix GetStatus handling (again) usb: dwc3: ep0: use dwc3_request for ep0 requsts instead of usb_request usb: dwc3: use correct hwparam register for power mgm check usb: dwc3: omap: move to module_platform_driver usb: dwc3: workaround: missing disconnect event usb: dwc3: workaround: missing USB3 Reset event usb: dwc3: workaround: U1/U2 -> U0 transiton usb: dwc3: gadget: return early in dwc3_cleanup_done_reqs() usb: dwc3: ep0: handle delayed_status again usb: dwc3: ep0: push ep0state into xfernotready processing usb: dwc3: fix sparse errors usb: dwc3: fix few coding style problems usb: dwc3: move generic dwc3 code from gadget into core usb: dwc3: use a helper function for operation mode setting usb: dwc3: ep0: don't use ep0in for transfers usb: dwc3: ep0: use proper endianess in SetFeature for wIndex usb: dwc3: core: drop DWC3_EVENT_BUFFERS_MAX usb: dwc3: omap: add multiple instances support to OMAP ...
Diffstat (limited to 'drivers/usb/dwc3/gadget.c')
-rw-r--r--drivers/usb/dwc3/gadget.c197
1 files changed, 147 insertions, 50 deletions
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 25dbd8614e7..026c53cf164 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -251,7 +251,8 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
}
static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
- const struct usb_endpoint_descriptor *desc)
+ const struct usb_endpoint_descriptor *desc,
+ const struct usb_ss_ep_comp_descriptor *comp_desc)
{
struct dwc3_gadget_ep_cmd_params params;
@@ -264,7 +265,8 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
| DWC3_DEPCFG_XFER_NOT_READY_EN;
- if (usb_endpoint_xfer_bulk(desc) && dep->endpoint.max_streams) {
+ if (comp_desc && USB_SS_MAX_STREAMS(comp_desc->bmAttributes)
+ && usb_endpoint_xfer_bulk(desc)) {
params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE
| DWC3_DEPCFG_STREAM_EVENT_EN;
dep->stream_capable = true;
@@ -317,7 +319,8 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
* Caller should take care of locking
*/
static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
- const struct usb_endpoint_descriptor *desc)
+ const struct usb_endpoint_descriptor *desc,
+ const struct usb_ss_ep_comp_descriptor *comp_desc)
{
struct dwc3 *dwc = dep->dwc;
u32 reg;
@@ -329,7 +332,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
return ret;
}
- ret = dwc3_gadget_set_ep_config(dwc, dep, desc);
+ ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc);
if (ret)
return ret;
@@ -343,6 +346,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
return ret;
dep->desc = desc;
+ dep->comp_desc = comp_desc;
dep->type = usb_endpoint_type(desc);
dep->flags |= DWC3_EP_ENABLED;
@@ -405,6 +409,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
dep->stream_capable = false;
dep->desc = NULL;
+ dep->comp_desc = NULL;
dep->type = 0;
dep->flags = 0;
@@ -473,7 +478,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
dev_vdbg(dwc->dev, "Enabling %s\n", dep->name);
spin_lock_irqsave(&dwc->lock, flags);
- ret = __dwc3_gadget_ep_enable(dep, desc);
+ ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc);
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -745,8 +750,9 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
dep->flags |= DWC3_EP_BUSY;
dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc,
dep->number);
- if (!dep->res_trans_idx)
- printk_once(KERN_ERR "%s() res_trans_idx is invalid\n", __func__);
+
+ WARN_ON_ONCE(!dep->res_trans_idx);
+
return 0;
}
@@ -1155,35 +1161,9 @@ static int dwc3_gadget_start(struct usb_gadget *g,
dwc->gadget_driver = driver;
dwc->gadget.dev.driver = &driver->driver;
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
-
- 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);
-
- switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams0)) {
- case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
- reg &= ~DWC3_GCTL_DSBLCLKGTNG;
- break;
- default:
- dev_dbg(dwc->dev, "No power optimization available\n");
- }
-
- /*
- * WORKAROUND: DWC3 revisions <1.90a have a bug
- * when The device fails to connect at SuperSpeed
- * and falls back to high-speed mode which causes
- * the device to enter in a Connect/Disconnect loop
- */
- if (dwc->revision < DWC3_REVISION_190A)
- reg |= DWC3_GCTL_U2RSTECN;
-
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
-
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
reg &= ~(DWC3_DCFG_SPEED_MASK);
- reg |= DWC3_DCFG_SUPERSPEED;
+ reg |= dwc->maximum_speed;
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
dwc->start_config_issued = false;
@@ -1192,14 +1172,14 @@ static int dwc3_gadget_start(struct usb_gadget *g,
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
dep = dwc->eps[0];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc);
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
goto err0;
}
dep = dwc->eps[1];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc);
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
goto err1;
@@ -1290,11 +1270,10 @@ static int __devinit dwc3_gadget_init_endpoints(struct dwc3 *dwc)
&dwc->gadget.ep_list);
ret = dwc3_alloc_trb_pool(dep);
- if (ret) {
- dev_err(dwc->dev, "%s: failed to allocate TRB pool\n", dep->name);
+ if (ret)
return ret;
- }
}
+
INIT_LIST_HEAD(&dep->request_list);
INIT_LIST_HEAD(&dep->req_queued);
}
@@ -1334,8 +1313,10 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
do {
req = next_request(&dep->req_queued);
- if (!req)
- break;
+ if (!req) {
+ WARN_ON_ONCE(1);
+ return 1;
+ }
dwc3_trb_to_nat(req->trb, &trb);
@@ -1400,6 +1381,31 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
dep->flags &= ~DWC3_EP_BUSY;
dep->res_trans_idx = 0;
}
+
+ /*
+ * WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround.
+ * See dwc3_gadget_linksts_change_interrupt() for 1st half.
+ */
+ if (dwc->revision < DWC3_REVISION_183A) {
+ u32 reg;
+ int i;
+
+ for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
+ struct dwc3_ep *dep = dwc->eps[i];
+
+ if (!(dep->flags & DWC3_EP_ENABLED))
+ continue;
+
+ if (!list_empty(&dep->req_queued))
+ return;
+ }
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg |= dwc->u1u2;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ dwc->u1u2 = 0;
+ }
}
static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
@@ -1639,6 +1645,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
dwc->start_config_issued = false;
dwc->gadget.speed = USB_SPEED_UNKNOWN;
+ dwc->setup_packet_pending = false;
}
static void dwc3_gadget_usb3_phy_power(struct dwc3 *dwc, int on)
@@ -1675,6 +1682,37 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
dev_vdbg(dwc->dev, "%s\n", __func__);
+ /*
+ * WORKAROUND: DWC3 revisions <1.88a have an issue which
+ * would cause a missing Disconnect Event if there's a
+ * pending Setup Packet in the FIFO.
+ *
+ * There's no suggested workaround on the official Bug
+ * report, which states that "unless the driver/application
+ * is doing any special handling of a disconnect event,
+ * there is no functional issue".
+ *
+ * Unfortunately, it turns out that we _do_ some special
+ * handling of a disconnect event, namely complete all
+ * pending transfers, notify gadget driver of the
+ * disconnection, and so on.
+ *
+ * Our suggested workaround is to follow the Disconnect
+ * Event steps here, instead, based on a setup_packet_pending
+ * flag. Such flag gets set whenever we have a XferNotReady
+ * event on EP0 and gets cleared on XferComplete for the
+ * same endpoint.
+ *
+ * Refers to:
+ *
+ * STAR#9000466709: RTL: Device : Disconnect event not
+ * generated if setup packet pending in FIFO
+ */
+ if (dwc->revision < DWC3_REVISION_188A) {
+ if (dwc->setup_packet_pending)
+ dwc3_gadget_disconnect_interrupt(dwc);
+ }
+
/* Enable PHYs */
dwc3_gadget_usb2_phy_power(dwc, true);
dwc3_gadget_usb3_phy_power(dwc, true);
@@ -1755,6 +1793,22 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
switch (speed) {
case DWC3_DCFG_SUPERSPEED:
+ /*
+ * WORKAROUND: DWC3 revisions <1.90a have an issue which
+ * would cause a missing USB3 Reset event.
+ *
+ * In such situations, we should force a USB3 Reset
+ * event by calling our dwc3_gadget_reset_interrupt()
+ * routine.
+ *
+ * Refers to:
+ *
+ * STAR#9000483510: RTL: SS : USB3 reset event may
+ * not be generated always when the link enters poll
+ */
+ if (dwc->revision < DWC3_REVISION_190A)
+ dwc3_gadget_reset_interrupt(dwc);
+
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
dwc->gadget.ep0->maxpacket = 512;
dwc->gadget.speed = USB_SPEED_SUPER;
@@ -1781,14 +1835,14 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
dwc3_gadget_disable_phy(dwc, dwc->gadget.speed);
dep = dwc->eps[0];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc);
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
return;
}
dep = dwc->eps[1];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc);
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
return;
@@ -1818,8 +1872,55 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
unsigned int evtinfo)
{
- /* The fith bit says SuperSpeed yes or no. */
- dwc->link_state = evtinfo & DWC3_LINK_STATE_MASK;
+ enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
+
+ /*
+ * WORKAROUND: DWC3 Revisions <1.83a have an issue which, depending
+ * on the link partner, the USB session might do multiple entry/exit
+ * of low power states before a transfer takes place.
+ *
+ * Due to this problem, we might experience lower throughput. The
+ * suggested workaround is to disable DCTL[12:9] bits if we're
+ * transitioning from U1/U2 to U0 and enable those bits again
+ * after a transfer completes and there are no pending transfers
+ * on any of the enabled endpoints.
+ *
+ * This is the first half of that workaround.
+ *
+ * Refers to:
+ *
+ * STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us
+ * core send LGO_Ux entering U0
+ */
+ if (dwc->revision < DWC3_REVISION_183A) {
+ if (next == DWC3_LINK_STATE_U0) {
+ u32 u1u2;
+ u32 reg;
+
+ switch (dwc->link_state) {
+ case DWC3_LINK_STATE_U1:
+ case DWC3_LINK_STATE_U2:
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ u1u2 = reg & (DWC3_DCTL_INITU2ENA
+ | DWC3_DCTL_ACCEPTU2ENA
+ | DWC3_DCTL_INITU1ENA
+ | DWC3_DCTL_ACCEPTU1ENA);
+
+ if (!dwc->u1u2)
+ dwc->u1u2 = reg & u1u2;
+
+ reg &= ~u1u2;
+
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ }
+ }
+
+ dwc->link_state = next;
dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state);
}
@@ -1925,7 +2026,7 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
spin_lock(&dwc->lock);
- for (i = 0; i < DWC3_EVENT_BUFFERS_NUM; i++) {
+ for (i = 0; i < dwc->num_event_buffers; i++) {
irqreturn_t status;
status = dwc3_process_event_buf(dwc, i);
@@ -2076,7 +2177,6 @@ err0:
void dwc3_gadget_exit(struct dwc3 *dwc)
{
int irq;
- int i;
usb_del_gadget_udc(&dwc->gadget);
irq = platform_get_irq(to_platform_device(dwc->dev), 0);
@@ -2084,9 +2184,6 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
free_irq(irq, dwc);
- for (i = 0; i < ARRAY_SIZE(dwc->eps); i++)
- __dwc3_gadget_ep_disable(dwc->eps[i]);
-
dwc3_gadget_free_endpoints(dwc);
dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce,