diff options
author | Jun Li <jun.li@nxp.com> | 2019-09-10 08:54:57 +0200 |
---|---|---|
committer | Peter Chen <peter.chen@nxp.com> | 2019-11-18 09:45:30 +0100 |
commit | 72dc8df7920fc24eba0f586c56e900a1643ff2b3 (patch) | |
tree | c9caf1c2e45a8864e3e496bb62f03f475bdb6fc5 /drivers/usb | |
parent | usb: chipidea: udc: add new API ci_hdrc_gadget_connect (diff) | |
download | linux-72dc8df7920fc24eba0f586c56e900a1643ff2b3.tar.xz linux-72dc8df7920fc24eba0f586c56e900a1643ff2b3.zip |
usb: chipidea: udc: protect usb interrupt enable
We hit the problem with below sequence:
- ci_udc_vbus_session() update vbus_active flag and ci->driver
is valid,
- before calling the ci_hdrc_gadget_connect(),
usb_gadget_udc_stop() is called by application remove gadget
driver,
- ci_udc_vbus_session() will contine do ci_hdrc_gadget_connect() as
gadget_ready is 1, so udc interrupt is enabled, but ci->driver is
NULL.
- USB connection irq generated but ci->driver is NULL.
As udc irq only should be enabled when gadget driver is binded, so
add spinlock to protect the usb irq enable for vbus session handling.
Signed-off-by: Jun Li <jun.li@nxp.com>
Signed-off-by: Peter Chen <peter.chen@nxp.com>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/chipidea/udc.c | 18 |
1 files changed, 10 insertions, 8 deletions
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 8c4383862373..ffaf46f5d062 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1530,13 +1530,18 @@ static const struct usb_ep_ops usb_ep_ops = { static void ci_hdrc_gadget_connect(struct usb_gadget *_gadget, int is_active) { struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); + unsigned long flags; if (is_active) { pm_runtime_get_sync(&_gadget->dev); hw_device_reset(ci); - hw_device_state(ci, ci->ep0out->qh.dma); - usb_gadget_set_state(_gadget, USB_STATE_POWERED); - usb_udc_vbus_handler(_gadget, true); + spin_lock_irqsave(&ci->lock, flags); + if (ci->driver) { + hw_device_state(ci, ci->ep0out->qh.dma); + usb_gadget_set_state(_gadget, USB_STATE_POWERED); + usb_udc_vbus_handler(_gadget, true); + } + spin_unlock_irqrestore(&ci->lock, flags); } else { usb_udc_vbus_handler(_gadget, false); if (ci->driver) @@ -1555,19 +1560,16 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) { struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); unsigned long flags; - int gadget_ready = 0; spin_lock_irqsave(&ci->lock, flags); ci->vbus_active = is_active; - if (ci->driver) - gadget_ready = 1; spin_unlock_irqrestore(&ci->lock, flags); if (ci->usb_phy) usb_phy_set_charger_state(ci->usb_phy, is_active ? USB_CHARGER_PRESENT : USB_CHARGER_ABSENT); - if (gadget_ready) + if (ci->driver) ci_hdrc_gadget_connect(_gadget, is_active); return 0; @@ -1827,6 +1829,7 @@ static int ci_udc_stop(struct usb_gadget *gadget) unsigned long flags; spin_lock_irqsave(&ci->lock, flags); + ci->driver = NULL; if (ci->vbus_active) { hw_device_state(ci, 0); @@ -1839,7 +1842,6 @@ static int ci_udc_stop(struct usb_gadget *gadget) pm_runtime_put(&ci->gadget.dev); } - ci->driver = NULL; spin_unlock_irqrestore(&ci->lock, flags); ci_udc_stop_for_otg_fsm(ci); |