lkml.org 
[lkml]   [2019]   [Sep]   [4]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.9 67/83] usb: chipidea: udc: dont do hardware access if gadget has stopped
    Date
    From: Peter Chen <peter.chen@nxp.com>

    commit cbe85c88ce80fb92956a0793518d415864dcead8 upstream.

    After _gadget_stop_activity is executed, we can consider the hardware
    operation for gadget has finished, and the udc can be stopped and enter
    low power mode. So, any later hardware operations (from usb_ep_ops APIs
    or usb_gadget_ops APIs) should be considered invalid, any deinitializatons
    has been covered at _gadget_stop_activity.

    I meet this problem when I plug out usb cable from PC using mass_storage
    gadget, my callstack like: vbus interrupt->.vbus_session->
    composite_disconnect ->pm_runtime_put_sync(&_gadget->dev),
    the composite_disconnect will call fsg_disable, but fsg_disable calls
    usb_ep_disable using async way, there are register accesses for
    usb_ep_disable. So sometimes, I get system hang due to visit register
    without clock, sometimes not.

    The Linux Kernel USB maintainer Alan Stern suggests this kinds of solution.
    See: http://marc.info/?l=linux-usb&m=138541769810983&w=2.

    Cc: <stable@vger.kernel.org> #v4.9+
    Signed-off-by: Peter Chen <peter.chen@nxp.com>
    Link: https://lore.kernel.org/r/20190820020503.27080-2-peter.chen@nxp.com
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    drivers/usb/chipidea/udc.c | 32 ++++++++++++++++++++++++--------
    1 file changed, 24 insertions(+), 8 deletions(-)

    --- a/drivers/usb/chipidea/udc.c
    +++ b/drivers/usb/chipidea/udc.c
    @@ -709,12 +709,6 @@ static int _gadget_stop_activity(struct
    struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget);
    unsigned long flags;

    - spin_lock_irqsave(&ci->lock, flags);
    - ci->gadget.speed = USB_SPEED_UNKNOWN;
    - ci->remote_wakeup = 0;
    - ci->suspended = 0;
    - spin_unlock_irqrestore(&ci->lock, flags);
    -
    /* flush all endpoints */
    gadget_for_each_ep(ep, gadget) {
    usb_ep_fifo_flush(ep);
    @@ -732,6 +726,12 @@ static int _gadget_stop_activity(struct
    ci->status = NULL;
    }

    + spin_lock_irqsave(&ci->lock, flags);
    + ci->gadget.speed = USB_SPEED_UNKNOWN;
    + ci->remote_wakeup = 0;
    + ci->suspended = 0;
    + spin_unlock_irqrestore(&ci->lock, flags);
    +
    return 0;
    }

    @@ -1306,6 +1306,10 @@ static int ep_disable(struct usb_ep *ep)
    return -EBUSY;

    spin_lock_irqsave(hwep->lock, flags);
    + if (hwep->ci->gadget.speed == USB_SPEED_UNKNOWN) {
    + spin_unlock_irqrestore(hwep->lock, flags);
    + return 0;
    + }

    /* only internal SW should disable ctrl endpts */

    @@ -1395,6 +1399,10 @@ static int ep_queue(struct usb_ep *ep, s
    return -EINVAL;

    spin_lock_irqsave(hwep->lock, flags);
    + if (hwep->ci->gadget.speed == USB_SPEED_UNKNOWN) {
    + spin_unlock_irqrestore(hwep->lock, flags);
    + return 0;
    + }
    retval = _ep_queue(ep, req, gfp_flags);
    spin_unlock_irqrestore(hwep->lock, flags);
    return retval;
    @@ -1418,8 +1426,8 @@ static int ep_dequeue(struct usb_ep *ep,
    return -EINVAL;

    spin_lock_irqsave(hwep->lock, flags);
    -
    - hw_ep_flush(hwep->ci, hwep->num, hwep->dir);
    + if (hwep->ci->gadget.speed != USB_SPEED_UNKNOWN)
    + hw_ep_flush(hwep->ci, hwep->num, hwep->dir);

    list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) {
    dma_pool_free(hwep->td_pool, node->ptr, node->dma);
    @@ -1490,6 +1498,10 @@ static void ep_fifo_flush(struct usb_ep
    }

    spin_lock_irqsave(hwep->lock, flags);
    + if (hwep->ci->gadget.speed == USB_SPEED_UNKNOWN) {
    + spin_unlock_irqrestore(hwep->lock, flags);
    + return;
    + }

    hw_ep_flush(hwep->ci, hwep->num, hwep->dir);

    @@ -1558,6 +1570,10 @@ static int ci_udc_wakeup(struct usb_gadg
    int ret = 0;

    spin_lock_irqsave(&ci->lock, flags);
    + if (ci->gadget.speed == USB_SPEED_UNKNOWN) {
    + spin_unlock_irqrestore(&ci->lock, flags);
    + return 0;
    + }
    if (!ci->remote_wakeup) {
    ret = -EOPNOTSUPP;
    goto out;

    \
     
     \ /
      Last update: 2019-09-04 20:02    [W:19.018 / U:0.008 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site