usb: dwc3: gadget: use udc-core's reset notifier
[cascardo/linux.git] / drivers / usb / dwc3 / gadget.c
index 3818b26..f03b136 100644 (file)
@@ -525,12 +525,11 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
                if (!usb_endpoint_xfer_isoc(desc))
                        return 0;
 
-               memset(&trb_link, 0, sizeof(trb_link));
-
                /* Link TRB for ISOC. The HWO bit is never reset */
                trb_st_hw = &dep->trb_pool[0];
 
                trb_link = &dep->trb_pool[DWC3_TRB_NUM - 1];
+               memset(trb_link, 0, sizeof(*trb_link));
 
                trb_link->bpl = lower_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw));
                trb_link->bph = upper_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw));
@@ -581,7 +580,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
 
        /* make sure HW endpoint isn't stalled */
        if (dep->flags & DWC3_EP_STALL)
-               __dwc3_gadget_ep_set_halt(dep, 0);
+               __dwc3_gadget_ep_set_halt(dep, 0, false);
 
        reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
        reg &= ~DWC3_DALEPENA_EP(dep->number);
@@ -1141,8 +1140,14 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
        if (!dep->endpoint.desc) {
                dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
                                request, ep->name);
-               spin_unlock_irqrestore(&dwc->lock, flags);
-               return -ESHUTDOWN;
+               ret = -ESHUTDOWN;
+               goto out;
+       }
+
+       if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
+                               request, req->dep->name)) {
+               ret = -EINVAL;
+               goto out;
        }
 
        dev_vdbg(dwc->dev, "queing request %p to %s length %d\n",
@@ -1150,6 +1155,8 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
        trace_dwc3_ep_queue(req);
 
        ret = __dwc3_gadget_ep_queue(dep, req);
+
+out:
        spin_unlock_irqrestore(&dwc->lock, flags);
 
        return ret;
@@ -1202,15 +1209,28 @@ out0:
        return ret;
 }
 
-int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value)
+int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
 {
        struct dwc3_gadget_ep_cmd_params        params;
        struct dwc3                             *dwc = dep->dwc;
        int                                     ret;
 
+       if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+               dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name);
+               return -EINVAL;
+       }
+
        memset(&params, 0x00, sizeof(params));
 
        if (value) {
+               if (!protocol && ((dep->direction && dep->flags & DWC3_EP_BUSY) ||
+                               (!list_empty(&dep->req_queued) ||
+                                !list_empty(&dep->request_list)))) {
+                       dev_dbg(dwc->dev, "%s: pending request, cannot halt\n",
+                                       dep->name);
+                       return -EAGAIN;
+               }
+
                ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
                        DWC3_DEPCMD_SETSTALL, &params);
                if (ret)
@@ -1241,15 +1261,7 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value)
        int                             ret;
 
        spin_lock_irqsave(&dwc->lock, flags);
-
-       if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
-               dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name);
-               ret = -EINVAL;
-               goto out;
-       }
-
-       ret = __dwc3_gadget_ep_set_halt(dep, value);
-out:
+       ret = __dwc3_gadget_ep_set_halt(dep, value, false);
        spin_unlock_irqrestore(&dwc->lock, flags);
 
        return ret;
@@ -1260,15 +1272,18 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
        struct dwc3_ep                  *dep = to_dwc3_ep(ep);
        struct dwc3                     *dwc = dep->dwc;
        unsigned long                   flags;
+       int                             ret;
 
        spin_lock_irqsave(&dwc->lock, flags);
        dep->flags |= DWC3_EP_WEDGE;
-       spin_unlock_irqrestore(&dwc->lock, flags);
 
        if (dep->number == 0 || dep->number == 1)
-               return dwc3_gadget_ep0_set_halt(ep, 1);
+               ret = __dwc3_gadget_ep0_set_halt(ep, 1);
        else
-               return dwc3_gadget_ep_set_halt(ep, 1);
+               ret = __dwc3_gadget_ep_set_halt(dep, 1, false);
+       spin_unlock_irqrestore(&dwc->lock, flags);
+
+       return ret;
 }
 
 /* -------------------------------------------------------------------------- */
@@ -1615,8 +1630,7 @@ err0:
        return ret;
 }
 
-static int dwc3_gadget_stop(struct usb_gadget *g,
-               struct usb_gadget_driver *driver)
+static int dwc3_gadget_stop(struct usb_gadget *g)
 {
        struct dwc3             *dwc = gadget_to_dwc(g);
        unsigned long           flags;
@@ -2027,6 +2041,17 @@ static void dwc3_resume_gadget(struct dwc3 *dwc)
        if (dwc->gadget_driver && dwc->gadget_driver->resume) {
                spin_unlock(&dwc->lock);
                dwc->gadget_driver->resume(&dwc->gadget);
+       }
+}
+
+static void dwc3_reset_gadget(struct dwc3 *dwc)
+{
+       if (!dwc->gadget_driver)
+               return;
+
+       if (dwc->gadget.speed != USB_SPEED_UNKNOWN) {
+               spin_unlock(&dwc->lock);
+               usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver);
                spin_lock(&dwc->lock);
        }
 }
@@ -2133,6 +2158,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
 
        dwc->gadget.speed = USB_SPEED_UNKNOWN;
        dwc->setup_packet_pending = false;
+       usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
 }
 
 static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
@@ -2170,11 +2196,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
                        dwc3_gadget_disconnect_interrupt(dwc);
        }
 
-       /* after reset -> Default State */
-       usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT);
-
-       if (dwc->gadget.speed != USB_SPEED_UNKNOWN)
-               dwc3_disconnect_gadget(dwc);
+       dwc3_reset_gadget(dwc);
 
        reg = dwc3_readl(dwc->regs, DWC3_DCTL);
        reg &= ~DWC3_DCTL_TSTCTRL_MASK;
@@ -2280,11 +2302,20 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
                reg = dwc3_readl(dwc->regs, DWC3_DCTL);
                reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
 
+               reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold);
+
                /*
-                * TODO: This should be configurable. For now using
-                * maximum allowed HIRD threshold value of 0b1100
+                * When dwc3 revisions >= 2.40a, LPM Erratum is enabled and
+                * DCFG.LPMCap is set, core responses with an ACK and the
+                * BESL value in the LPM token is less than or equal to LPM
+                * NYET threshold.
                 */
-               reg |= DWC3_DCTL_HIRD_THRES(12);
+               WARN_ONCE(dwc->revision < DWC3_REVISION_240A
+                               && dwc->has_lpm_erratum,
+                               "LPM Erratum not available on dwc3 revisisions < 2.40a\n");
+
+               if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A)
+                       reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold);
 
                dwc3_writel(dwc->regs, DWC3_DCTL, reg);
        } else {
@@ -2737,26 +2768,13 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
                        dwc->ctrl_req, dwc->ctrl_req_addr);
 }
 
-int dwc3_gadget_prepare(struct dwc3 *dwc)
+int dwc3_gadget_suspend(struct dwc3 *dwc)
 {
        if (dwc->pullups_connected) {
                dwc3_gadget_disable_irq(dwc);
                dwc3_gadget_run_stop(dwc, true, true);
        }
 
-       return 0;
-}
-
-void dwc3_gadget_complete(struct dwc3 *dwc)
-{
-       if (dwc->pullups_connected) {
-               dwc3_gadget_enable_irq(dwc);
-               dwc3_gadget_run_stop(dwc, true, false);
-       }
-}
-
-int dwc3_gadget_suspend(struct dwc3 *dwc)
-{
        __dwc3_gadget_ep_disable(dwc->eps[0]);
        __dwc3_gadget_ep_disable(dwc->eps[1]);
 
@@ -2791,6 +2809,11 @@ int dwc3_gadget_resume(struct dwc3 *dwc)
 
        dwc3_writel(dwc->regs, DWC3_DCFG, dwc->dcfg);
 
+       if (dwc->pullups_connected) {
+               dwc3_gadget_enable_irq(dwc);
+               dwc3_gadget_run_stop(dwc, true, false);
+       }
+
        return 0;
 
 err1: