If GINTSTS.CURMODE is not yet reflecting the expecting mode after calling dwc2_force_mode, we enter in the dwc2_wait_for_mode function while under spinlock atomic context. To avoid this situation, move the call to dwc2_force_mode after the spinlock atomic context. Fixes: bc0f0d4a5853 ("usb: dwc2: override PHY input signals with usb role switch support") Reported-by: Martin Blumenstingl <martin.blumenstingl@xxxxxxxxxxxxxx> Signed-off-by: Amelie Delaunay <amelie.delaunay@xxxxxx> --- drivers/usb/dwc2/drd.c | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/drivers/usb/dwc2/drd.c b/drivers/usb/dwc2/drd.c index 5099841b1417..2d4176f5788e 100644 --- a/drivers/usb/dwc2/drd.c +++ b/drivers/usb/dwc2/drd.c @@ -70,6 +70,7 @@ static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role) { struct dwc2_hsotg *hsotg = usb_role_switch_get_drvdata(sw); unsigned long flags; + int already = 0; /* Skip session not in line with dr_mode */ if ((role == USB_ROLE_DEVICE && hsotg->dr_mode == USB_DR_MODE_HOST) || @@ -88,26 +89,9 @@ static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role) spin_lock_irqsave(&hsotg->lock, flags); if (role == USB_ROLE_HOST) { - if (dwc2_ovr_avalid(hsotg, true)) - goto unlock; - - if (hsotg->dr_mode == USB_DR_MODE_OTG) - /* - * This will raise a Connector ID Status Change - * Interrupt - connID A - */ - dwc2_force_mode(hsotg, true); + already = dwc2_ovr_avalid(hsotg, true); } else if (role == USB_ROLE_DEVICE) { - if (dwc2_ovr_bvalid(hsotg, true)) - goto unlock; - - if (hsotg->dr_mode == USB_DR_MODE_OTG) - /* - * This will raise a Connector ID Status Change - * Interrupt - connID B - */ - dwc2_force_mode(hsotg, false); - + already = dwc2_ovr_bvalid(hsotg, true); /* This clear DCTL.SFTDISCON bit */ dwc2_hsotg_core_connect(hsotg); } else { @@ -120,9 +104,12 @@ static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role) } } -unlock: spin_unlock_irqrestore(&hsotg->lock, flags); + if (!already && hsotg->dr_mode == USB_DR_MODE_OTG) + /* This will raise a Connector ID Status Change Interrupt */ + dwc2_force_mode(hsotg, role == USB_ROLE_HOST); + dev_dbg(hsotg->dev, "%s-session valid\n", role == USB_ROLE_NONE ? "No" : role == USB_ROLE_HOST ? "A" : "B"); -- 2.17.1