Commit 72dc8df7 authored by Jun Li's avatar Jun Li Committed by Peter Chen

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: default avatarJun Li <jun.li@nxp.com>
Signed-off-by: default avatarPeter Chen <peter.chen@nxp.com>
parent d16ab536
...@@ -1530,13 +1530,18 @@ static const struct usb_ep_ops usb_ep_ops = { ...@@ -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) static void ci_hdrc_gadget_connect(struct usb_gadget *_gadget, int is_active)
{ {
struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
unsigned long flags;
if (is_active) { if (is_active) {
pm_runtime_get_sync(&_gadget->dev); pm_runtime_get_sync(&_gadget->dev);
hw_device_reset(ci); hw_device_reset(ci);
hw_device_state(ci, ci->ep0out->qh.dma); spin_lock_irqsave(&ci->lock, flags);
usb_gadget_set_state(_gadget, USB_STATE_POWERED); if (ci->driver) {
usb_udc_vbus_handler(_gadget, true); 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 { } else {
usb_udc_vbus_handler(_gadget, false); usb_udc_vbus_handler(_gadget, false);
if (ci->driver) if (ci->driver)
...@@ -1555,19 +1560,16 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) ...@@ -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); struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
unsigned long flags; unsigned long flags;
int gadget_ready = 0;
spin_lock_irqsave(&ci->lock, flags); spin_lock_irqsave(&ci->lock, flags);
ci->vbus_active = is_active; ci->vbus_active = is_active;
if (ci->driver)
gadget_ready = 1;
spin_unlock_irqrestore(&ci->lock, flags); spin_unlock_irqrestore(&ci->lock, flags);
if (ci->usb_phy) if (ci->usb_phy)
usb_phy_set_charger_state(ci->usb_phy, is_active ? usb_phy_set_charger_state(ci->usb_phy, is_active ?
USB_CHARGER_PRESENT : USB_CHARGER_ABSENT); USB_CHARGER_PRESENT : USB_CHARGER_ABSENT);
if (gadget_ready) if (ci->driver)
ci_hdrc_gadget_connect(_gadget, is_active); ci_hdrc_gadget_connect(_gadget, is_active);
return 0; return 0;
...@@ -1827,6 +1829,7 @@ static int ci_udc_stop(struct usb_gadget *gadget) ...@@ -1827,6 +1829,7 @@ static int ci_udc_stop(struct usb_gadget *gadget)
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&ci->lock, flags); spin_lock_irqsave(&ci->lock, flags);
ci->driver = NULL;
if (ci->vbus_active) { if (ci->vbus_active) {
hw_device_state(ci, 0); hw_device_state(ci, 0);
...@@ -1839,7 +1842,6 @@ static int ci_udc_stop(struct usb_gadget *gadget) ...@@ -1839,7 +1842,6 @@ static int ci_udc_stop(struct usb_gadget *gadget)
pm_runtime_put(&ci->gadget.dev); pm_runtime_put(&ci->gadget.dev);
} }
ci->driver = NULL;
spin_unlock_irqrestore(&ci->lock, flags); spin_unlock_irqrestore(&ci->lock, flags);
ci_udc_stop_for_otg_fsm(ci); ci_udc_stop_for_otg_fsm(ci);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment