Skip to content

Commit cbe85c8

Browse files
Peter Chengregkh
authored andcommitted
usb: chipidea: udc: don't do hardware access if gadget has stopped
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: <[email protected]> #v4.9+ Signed-off-by: Peter Chen <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent de7b9aa commit cbe85c8

File tree

1 file changed

+24
-8
lines changed
  • drivers/usb/chipidea

1 file changed

+24
-8
lines changed

drivers/usb/chipidea/udc.c

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -709,12 +709,6 @@ static int _gadget_stop_activity(struct usb_gadget *gadget)
709709
struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget);
710710
unsigned long flags;
711711

712-
spin_lock_irqsave(&ci->lock, flags);
713-
ci->gadget.speed = USB_SPEED_UNKNOWN;
714-
ci->remote_wakeup = 0;
715-
ci->suspended = 0;
716-
spin_unlock_irqrestore(&ci->lock, flags);
717-
718712
/* flush all endpoints */
719713
gadget_for_each_ep(ep, gadget) {
720714
usb_ep_fifo_flush(ep);
@@ -732,6 +726,12 @@ static int _gadget_stop_activity(struct usb_gadget *gadget)
732726
ci->status = NULL;
733727
}
734728

729+
spin_lock_irqsave(&ci->lock, flags);
730+
ci->gadget.speed = USB_SPEED_UNKNOWN;
731+
ci->remote_wakeup = 0;
732+
ci->suspended = 0;
733+
spin_unlock_irqrestore(&ci->lock, flags);
734+
735735
return 0;
736736
}
737737

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

13051305
spin_lock_irqsave(hwep->lock, flags);
1306+
if (hwep->ci->gadget.speed == USB_SPEED_UNKNOWN) {
1307+
spin_unlock_irqrestore(hwep->lock, flags);
1308+
return 0;
1309+
}
13061310

13071311
/* only internal SW should disable ctrl endpts */
13081312

@@ -1392,6 +1396,10 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req,
13921396
return -EINVAL;
13931397

13941398
spin_lock_irqsave(hwep->lock, flags);
1399+
if (hwep->ci->gadget.speed == USB_SPEED_UNKNOWN) {
1400+
spin_unlock_irqrestore(hwep->lock, flags);
1401+
return 0;
1402+
}
13951403
retval = _ep_queue(ep, req, gfp_flags);
13961404
spin_unlock_irqrestore(hwep->lock, flags);
13971405
return retval;
@@ -1415,8 +1423,8 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req)
14151423
return -EINVAL;
14161424

14171425
spin_lock_irqsave(hwep->lock, flags);
1418-
1419-
hw_ep_flush(hwep->ci, hwep->num, hwep->dir);
1426+
if (hwep->ci->gadget.speed != USB_SPEED_UNKNOWN)
1427+
hw_ep_flush(hwep->ci, hwep->num, hwep->dir);
14201428

14211429
list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) {
14221430
dma_pool_free(hwep->td_pool, node->ptr, node->dma);
@@ -1487,6 +1495,10 @@ static void ep_fifo_flush(struct usb_ep *ep)
14871495
}
14881496

14891497
spin_lock_irqsave(hwep->lock, flags);
1498+
if (hwep->ci->gadget.speed == USB_SPEED_UNKNOWN) {
1499+
spin_unlock_irqrestore(hwep->lock, flags);
1500+
return;
1501+
}
14901502

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

@@ -1559,6 +1571,10 @@ static int ci_udc_wakeup(struct usb_gadget *_gadget)
15591571
int ret = 0;
15601572

15611573
spin_lock_irqsave(&ci->lock, flags);
1574+
if (ci->gadget.speed == USB_SPEED_UNKNOWN) {
1575+
spin_unlock_irqrestore(&ci->lock, flags);
1576+
return 0;
1577+
}
15621578
if (!ci->remote_wakeup) {
15631579
ret = -EOPNOTSUPP;
15641580
goto out;

0 commit comments

Comments
 (0)