5.4-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jun Li jun.li@nxp.com
[ Upstream commit 72dc8df7920fc24eba0f586c56e900a1643ff2b3 ]
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 Stable-dep-of: b7a62611fab7 ("usb: chipidea: add USB PHY event") Signed-off-by: Sasha Levin sashal@kernel.org --- drivers/usb/chipidea/udc.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index e9ef6271e20d..9212c3842a1b 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1539,13 +1539,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) @@ -1564,19 +1569,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; @@ -1836,6 +1838,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); @@ -1848,7 +1851,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);