| From 8212937305f84ef73ea81036dafb80c557583d4b Mon Sep 17 00:00:00 2001 |
| From: Wesley Cheng <wcheng@codeaurora.org> |
| Date: Thu, 20 May 2021 21:23:57 -0700 |
| Subject: usb: dwc3: gadget: Disable gadget IRQ during pullup disable |
| |
| From: Wesley Cheng <wcheng@codeaurora.org> |
| |
| commit 8212937305f84ef73ea81036dafb80c557583d4b upstream. |
| |
| Current sequence utilizes dwc3_gadget_disable_irq() alongside |
| synchronize_irq() to ensure that no further DWC3 events are generated. |
| However, the dwc3_gadget_disable_irq() API only disables device |
| specific events. Endpoint events can still be generated. Briefly |
| disable the interrupt line, so that the cleanup code can run to |
| prevent device and endpoint events. (i.e. __dwc3_gadget_stop() and |
| dwc3_stop_active_transfers() respectively) |
| |
| Without doing so, it can lead to both the interrupt handler and the |
| pullup disable routine both writing to the GEVNTCOUNT register, which |
| will cause an incorrect count being read from future interrupts. |
| |
| Fixes: ae7e86108b12 ("usb: dwc3: Stop active transfers before halting the controller") |
| Signed-off-by: Wesley Cheng <wcheng@codeaurora.org> |
| Link: https://lore.kernel.org/r/1621571037-1424-1-git-send-email-wcheng@codeaurora.org |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/usb/dwc3/gadget.c | 11 +++++------ |
| 1 file changed, 5 insertions(+), 6 deletions(-) |
| |
| --- a/drivers/usb/dwc3/gadget.c |
| +++ b/drivers/usb/dwc3/gadget.c |
| @@ -2240,13 +2240,10 @@ static int dwc3_gadget_pullup(struct usb |
| } |
| |
| /* |
| - * Synchronize any pending event handling before executing the controller |
| - * halt routine. |
| + * Synchronize and disable any further event handling while controller |
| + * is being enabled/disabled. |
| */ |
| - if (!is_on) { |
| - dwc3_gadget_disable_irq(dwc); |
| - synchronize_irq(dwc->irq_gadget); |
| - } |
| + disable_irq(dwc->irq_gadget); |
| |
| spin_lock_irqsave(&dwc->lock, flags); |
| |
| @@ -2284,6 +2281,8 @@ static int dwc3_gadget_pullup(struct usb |
| |
| ret = dwc3_gadget_run_stop(dwc, is_on, false); |
| spin_unlock_irqrestore(&dwc->lock, flags); |
| + enable_irq(dwc->irq_gadget); |
| + |
| pm_runtime_put(dwc->dev); |
| |
| return ret; |