From 26eef0a00649dc2b0697cc0a10bc5ce3db14fdfe Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 21 Apr 2022 19:22:44 -0700 Subject: [PATCH] UPSTREAM: usb: dwc3: gadget: Don't modify GEVNTCOUNT in pullup() If the GEVNTCOUNT indicates events in the event buffer, the driver needs to acknowledge them before the controller can halt. Simply let the interrupt handler acknowledges the remaining event generated by the controller while polling for DSTS.DEVCTLHLT. This avoids disabling irq and taking care of race condition between the interrupt handlers and pullup(). Change-Id: Ief1bc9deb10e02f84fdc952481a7bb98f9cf8b78 Signed-off-by: Thinh Nguyen Link: https://lore.kernel.org/r/ea306ec93c41ccafbdb5d16404ff3b6eca299613.1650593829.git.Thinh.Nguyen@synopsys.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Frank Wang (cherry picked from commit 8f8034f493b5eb1ad21ff392fd30c0cf9e71f73f) --- drivers/usb/dwc3/gadget.c | 35 ++++++++--------------------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 4a2959e49392..a0a9478073d7 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2640,8 +2640,9 @@ static int __dwc3_gadget_start(struct dwc3 *dwc); static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc) { - u32 count; + unsigned long flags; + spin_lock_irqsave(&dwc->lock, flags); dwc->connected = false; /* @@ -2653,22 +2654,15 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc) */ dwc3_stop_active_transfers(dwc); __dwc3_gadget_stop(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); /* - * In the Synopsys DesignWare Cores USB3 Databook Rev. 3.30a - * Section 1.3.4, it mentions that for the DEVCTRLHLT bit, the - * "software needs to acknowledge the events that are generated - * (by writing to GEVNTCOUNTn) while it is waiting for this bit - * to be set to '1'." + * Note: if the GEVNTCOUNT indicates events in the event buffer, the + * driver needs to acknowledge them before the controller can halt. + * Simply let the interrupt handler acknowledges and handle the + * remaining event generated by the controller while polling for + * DSTS.DEVCTLHLT. */ - count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); - count &= DWC3_GEVNTCOUNT_MASK; - if (count > 0) { - dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count); - dwc->ev_buf->lpos = (dwc->ev_buf->lpos + count) % - dwc->ev_buf->length; - } - return dwc3_gadget_run_stop(dwc, false, false); } @@ -2676,7 +2670,6 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) { struct dwc3 *dwc = gadget_to_dwc(g); struct dwc3_vendor *vdwc = container_of(dwc, struct dwc3_vendor, dwc); - unsigned long flags; int ret; is_on = !!is_on; @@ -2721,14 +2714,6 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) return 0; } - /* - * Synchronize and disable any further event handling while controller - * is being enabled/disabled. - */ - disable_irq(dwc->irq_gadget); - - spin_lock_irqsave(&dwc->lock, flags); - if (!is_on) { ret = dwc3_gadget_soft_disconnect(dwc); } else { @@ -2738,16 +2723,12 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) * device-initiated disconnect requires a core soft reset * (DCTL.CSftRst) before enabling the run/stop bit. */ - spin_unlock_irqrestore(&dwc->lock, flags); dwc3_core_soft_reset(dwc); - spin_lock_irqsave(&dwc->lock, flags); dwc3_event_buffers_setup(dwc); __dwc3_gadget_start(dwc); ret = dwc3_gadget_run_stop(dwc, true, false); } - spin_unlock_irqrestore(&dwc->lock, flags); - enable_irq(dwc->irq_gadget); pm_runtime_put(dwc->dev);