usb: dwc2: Fix disable all EP’s on disconnect [Linux 4.19.64]

This Linux kernel change "usb: dwc2: Fix disable all EP’s on disconnect" is included in the Linux 4.19.64 release. This change is authored by Minas Harutyunyan <minas.harutyunyan [at] synopsys.com> on Mon Dec 10 18:09:32 2018 +0400. The commit for this change in Linux stable tree is b544a68 (patch) which is from upstream commit 4fe4f9f. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream 4fe4f9f.

usb: dwc2: Fix disable all EP's on disconnect

commit 4fe4f9fecc36956fd53c8edf96dd0c691ef98ff9 upstream.

Disabling all EP's allow to reset EP's to initial state.
Introduced new function dwc2_hsotg_ep_disable_lock() which
before calling dwc2_hsotg_ep_disable() function acquire
hsotg->lock and release on exiting.
From dwc2_hsotg_ep_disable() function removed acquiring
hsotg->lock.
In dwc2_hsotg_core_init_disconnected() function when USB
reset interrupt asserted disabling all ep’s by
dwc2_hsotg_ep_disable() function.
This updates eliminating sparse imbalance warnings.

Reverted changes in dwc2_hostg_disconnect() function.
Introduced new function dwc2_hsotg_ep_disable_lock().
Changed dwc2_hsotg_ep_ops. Now disable point to
dwc2_hsotg_ep_disable_lock() function.
In functions dwc2_hsotg_udc_stop() and dwc2_hsotg_suspend()
dwc2_hsotg_ep_disable() function replaced by
dwc2_hsotg_ep_disable_lock() function.
In dwc2_hsotg_ep_disable() function removed acquiring
of hsotg->lock.

Fixes: dccf1bad4be7 ("usb: dwc2: Disable all EP's on disconnect")
Signed-off-by: Minas Harutyunyan <hminas@synopsys.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

There are 41 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 drivers/usb/dwc2/gadget.c | 41 +++++++++++++++++++++++------------------
 1 file changed, 23 insertions(+), 18 deletions(-)

diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index ed79bdf..3f68edd 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -3107,8 +3107,6 @@ static void kill_all_requests(struct dwc2_hsotg *hsotg,
        dwc2_hsotg_txfifo_flush(hsotg, ep->fifo_index);
 }

-static int dwc2_hsotg_ep_disable(struct usb_ep *ep);
-
 /**
  * dwc2_hsotg_disconnect - disconnect service
  * @hsotg: The device state.
@@ -3130,9 +3128,11 @@ void dwc2_hsotg_disconnect(struct dwc2_hsotg *hsotg)
    /* all endpoints should be shutdown */
    for (ep = 0; ep < hsotg->num_of_eps; ep++) {
        if (hsotg->eps_in[ep])
-           dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
+           kill_all_requests(hsotg, hsotg->eps_in[ep],
+                     -ESHUTDOWN);
        if (hsotg->eps_out[ep])
-           dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
+           kill_all_requests(hsotg, hsotg->eps_out[ep],
+                     -ESHUTDOWN);
    }

    call_gadget(hsotg, disconnect);
@@ -3176,6 +3176,7 @@ static void dwc2_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic)
            GINTSTS_PTXFEMP |  \
            GINTSTS_RXFLVL)

+static int dwc2_hsotg_ep_disable(struct usb_ep *ep);
 /**
  * dwc2_hsotg_core_init - issue softreset to the core
  * @hsotg: The device state
@@ -4004,10 +4005,8 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
    struct dwc2_hsotg *hsotg = hs_ep->parent;
    int dir_in = hs_ep->dir_in;
    int index = hs_ep->index;
-   unsigned long flags;
    u32 epctrl_reg;
    u32 ctrl;
-   int locked;

    dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep);

@@ -4023,10 +4022,6 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)

    epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);

-   locked = spin_is_locked(&hsotg->lock);
-   if (!locked)
-       spin_lock_irqsave(&hsotg->lock, flags);
-
    ctrl = dwc2_readl(hsotg, epctrl_reg);

    if (ctrl & DXEPCTL_EPENA)
@@ -4049,12 +4044,22 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
    hs_ep->fifo_index = 0;
    hs_ep->fifo_size = 0;

-   if (!locked)
-       spin_unlock_irqrestore(&hsotg->lock, flags);
-
    return 0;
 }

+static int dwc2_hsotg_ep_disable_lock(struct usb_ep *ep)
+{
+   struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
+   struct dwc2_hsotg *hsotg = hs_ep->parent;
+   unsigned long flags;
+   int ret;
+
+   spin_lock_irqsave(&hsotg->lock, flags);
+   ret = dwc2_hsotg_ep_disable(ep);
+   spin_unlock_irqrestore(&hsotg->lock, flags);
+   return ret;
+}
+
 /**
  * on_list - check request is on the given endpoint
  * @ep: The endpoint to check.
@@ -4202,7 +4207,7 @@ static int dwc2_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value)

 static const struct usb_ep_ops dwc2_hsotg_ep_ops = {
    .enable     = dwc2_hsotg_ep_enable,
-   .disable    = dwc2_hsotg_ep_disable,
+   .disable    = dwc2_hsotg_ep_disable_lock,
    .alloc_request  = dwc2_hsotg_ep_alloc_request,
    .free_request   = dwc2_hsotg_ep_free_request,
    .queue      = dwc2_hsotg_ep_queue_lock,
@@ -4342,9 +4347,9 @@ static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget)
    /* all endpoints should be shutdown */
    for (ep = 1; ep < hsotg->num_of_eps; ep++) {
        if (hsotg->eps_in[ep])
-           dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
+           dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep);
        if (hsotg->eps_out[ep])
-           dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
+           dwc2_hsotg_ep_disable_lock(&hsotg->eps_out[ep]->ep);
    }

    spin_lock_irqsave(&hsotg->lock, flags);
@@ -4792,9 +4797,9 @@ int dwc2_hsotg_suspend(struct dwc2_hsotg *hsotg)

        for (ep = 0; ep < hsotg->num_of_eps; ep++) {
            if (hsotg->eps_in[ep])
-               dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
+               dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep);
            if (hsotg->eps_out[ep])
-               dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
+               dwc2_hsotg_ep_disable_lock(&hsotg->eps_out[ep]->ep);
        }
    }

Leave a Reply

Your email address will not be published. Required fields are marked *