usb: dwc3: gadget: Don't modify GEVNTCOUNT in pullup()

[ Upstream commit 8f8034f493b5eb1ad21ff392fd30c0cf9e71f73f ]

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().

Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Link: https://lore.kernel.org/r/ea306ec93c41ccafbdb5d16404ff3b6eca299613.1650593829.git.Thinh.Nguyen@synopsys.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Stable-dep-of: 040f2dbd2010 ("usb: dwc3: gadget: Avoid duplicate requests to enable Run/Stop")
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Thinh Nguyen 2022-04-21 19:22:44 -07:00 committed by Greg Kroah-Hartman
parent 67bf926f16
commit a5bdea59f4

View File

@ -2010,8 +2010,9 @@ static int __dwc3_gadget_start(struct dwc3 *dwc);
static int dwc3_gadget_soft_disconnect(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; dwc->connected = false;
/* /*
@ -2023,29 +2024,21 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
*/ */
dwc3_stop_active_transfers(dwc); dwc3_stop_active_transfers(dwc);
__dwc3_gadget_stop(dwc); __dwc3_gadget_stop(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
/* /*
* In the Synopsys DesignWare Cores USB3 Databook Rev. 3.30a * Note: if the GEVNTCOUNT indicates events in the event buffer, the
* Section 1.3.4, it mentions that for the DEVCTRLHLT bit, the * driver needs to acknowledge them before the controller can halt.
* "software needs to acknowledge the events that are generated * Simply let the interrupt handler acknowledges and handle the
* (by writing to GEVNTCOUNTn) while it is waiting for this bit * remaining event generated by the controller while polling for
* to be set to '1'." * 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); return dwc3_gadget_run_stop(dwc, false, false);
} }
static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
{ {
struct dwc3 *dwc = gadget_to_dwc(g); struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
int ret; int ret;
is_on = !!is_on; is_on = !!is_on;
@ -2089,14 +2082,6 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
return 0; 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) { if (!is_on) {
ret = dwc3_gadget_soft_disconnect(dwc); ret = dwc3_gadget_soft_disconnect(dwc);
} else { } else {
@ -2106,16 +2091,12 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
* device-initiated disconnect requires a core soft reset * device-initiated disconnect requires a core soft reset
* (DCTL.CSftRst) before enabling the run/stop bit. * (DCTL.CSftRst) before enabling the run/stop bit.
*/ */
spin_unlock_irqrestore(&dwc->lock, flags);
dwc3_core_soft_reset(dwc); dwc3_core_soft_reset(dwc);
spin_lock_irqsave(&dwc->lock, flags);
dwc3_event_buffers_setup(dwc); dwc3_event_buffers_setup(dwc);
__dwc3_gadget_start(dwc); __dwc3_gadget_start(dwc);
ret = dwc3_gadget_run_stop(dwc, true, false); ret = dwc3_gadget_run_stop(dwc, true, false);
} }
spin_unlock_irqrestore(&dwc->lock, flags);
enable_irq(dwc->irq_gadget);
pm_runtime_put(dwc->dev); pm_runtime_put(dwc->dev);