Skip to content

Commit

Permalink
usb: dwc2: drd: fix sleeping while spinlock atomic context
Browse files Browse the repository at this point in the history
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.

Change-Id: If30c9bf19249be8c5d07e229f4067e6d013092cb
Fixes: 5ec811a6ab7b ("usb: dwc2: add DRD support with role switch and override Vbus/ID signals")
Signed-off-by: Amelie Delaunay <amelie.delaunay@st.com>
Reviewed-on: https://gerrit.st.com/c/mpu/oe/st/linux-stm32/+/173585
Reviewed-by: CITOOLS <smet-aci-reviews@lists.codex.cro.st.com>
Reviewed-by: Fabrice GASNIER <fabrice.gasnier@st.com>
  • Loading branch information
ADESTM authored and atorgue committed Sep 8, 2020
1 parent 4f1e06a commit bbb6e11
Showing 1 changed file with 10 additions and 23 deletions.
33 changes: 10 additions & 23 deletions drivers/usb/dwc2/drd.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ static int dwc2_drd_role_sw_set(struct device *dev, enum usb_role role)
{
struct dwc2_hsotg *hsotg = dev_get_drvdata(dev);
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) ||
Expand All @@ -88,41 +89,27 @@ static int dwc2_drd_role_sw_set(struct device *dev, 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 {
if (dwc2_is_device_mode(hsotg)) {
if (!dwc2_ovr_bvalid(hsotg, false))
/* This set DCTL.SFTDISCON bit */
dwc2_hsotg_core_disconnect(hsotg);
if (!dwc2_ovr_bvalid(hsotg, false))
/* This set DCTL.SFTDISCON bit */
dwc2_hsotg_core_disconnect(hsotg);
} else {
dwc2_ovr_avalid(hsotg, false);
}
}

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");
Expand Down

0 comments on commit bbb6e11

Please sign in to comment.