usb: dwc2: drd: restore role and overrides upon resume
authorFabrice Gasnier <fabrice.gasnier@foss.st.com>
Mon, 6 Dec 2021 15:56:43 +0000 (16:56 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 17 Dec 2021 15:52:51 +0000 (16:52 +0100)
Override enable bits may not be restored when going to low power mode
(e.g. when in DWC2_POWER_DOWN_PARAM_NONE).
These bits are set when probing/initializing drd (role switch). Restore
them upon resume from low power mode (in case these have been lost).

To achieve this, the last known role is restored upon resume. And the
override enable bits are always set when configuring aval, bval and vbval.

When resuming, forcing the role should be done only once, or this can cause
port changes in HOST mode for instance.
So, only restore FORCEDEVMODE/FORCEHOSTMODE when role_sw is unused

Acked-by: Minas Harutyunyan <Minas.Harutyunyan@synopsys.com>
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Link: https://lore.kernel.org/r/1638806203-6624-4-git-send-email-fabrice.gasnier@foss.st.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/dwc2/drd.c
drivers/usb/dwc2/platform.c

index 4f453ecdd8d67c7b8e921af5f3c39ee617950692..1b39c4776369b476223c9c2a880dc1521867c9a3 100644 (file)
 #include <linux/usb/role.h>
 #include "core.h"
 
+#define dwc2_ovr_gotgctl(gotgctl) \
+       ((gotgctl) |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN | GOTGCTL_VBVALOEN | \
+        GOTGCTL_DBNCE_FLTR_BYPASS)
+
 static void dwc2_ovr_init(struct dwc2_hsotg *hsotg)
 {
        unsigned long flags;
@@ -21,8 +25,7 @@ static void dwc2_ovr_init(struct dwc2_hsotg *hsotg)
        spin_lock_irqsave(&hsotg->lock, flags);
 
        gotgctl = dwc2_readl(hsotg, GOTGCTL);
-       gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN | GOTGCTL_VBVALOEN;
-       gotgctl |= GOTGCTL_DBNCE_FLTR_BYPASS;
+       dwc2_ovr_gotgctl(gotgctl);
        gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL);
        if (hsotg->role_sw_default_mode == USB_DR_MODE_HOST)
                gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL;
@@ -44,6 +47,9 @@ static int dwc2_ovr_avalid(struct dwc2_hsotg *hsotg, bool valid)
            (!valid && !(gotgctl & GOTGCTL_ASESVLD)))
                return -EALREADY;
 
+       /* Always enable overrides to handle the resume case */
+       dwc2_ovr_gotgctl(gotgctl);
+
        gotgctl &= ~GOTGCTL_BVALOVAL;
        if (valid)
                gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL;
@@ -63,6 +69,9 @@ static int dwc2_ovr_bvalid(struct dwc2_hsotg *hsotg, bool valid)
            (!valid && !(gotgctl & GOTGCTL_BSESVLD)))
                return -EALREADY;
 
+       /* Always enable overrides to handle the resume case */
+       dwc2_ovr_gotgctl(gotgctl);
+
        gotgctl &= ~GOTGCTL_AVALOVAL;
        if (valid)
                gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL;
@@ -196,6 +205,31 @@ void dwc2_drd_suspend(struct dwc2_hsotg *hsotg)
 void dwc2_drd_resume(struct dwc2_hsotg *hsotg)
 {
        u32 gintsts, gintmsk;
+       enum usb_role role;
+
+       if (hsotg->role_sw) {
+               /* get last known role (as the get ops isn't implemented by this driver) */
+               role = usb_role_switch_get_role(hsotg->role_sw);
+
+               if (role == USB_ROLE_NONE) {
+                       if (hsotg->role_sw_default_mode == USB_DR_MODE_HOST)
+                               role = USB_ROLE_HOST;
+                       else if (hsotg->role_sw_default_mode == USB_DR_MODE_PERIPHERAL)
+                               role = USB_ROLE_DEVICE;
+               }
+
+               /* restore last role that may have been lost */
+               if (role == USB_ROLE_HOST)
+                       dwc2_ovr_avalid(hsotg, true);
+               else if (role == USB_ROLE_DEVICE)
+                       dwc2_ovr_bvalid(hsotg, true);
+
+               dwc2_force_mode(hsotg, role == USB_ROLE_HOST);
+
+               dev_dbg(hsotg->dev, "resuming %s-session valid\n",
+                       role == USB_ROLE_NONE ? "No" :
+                       role == USB_ROLE_HOST ? "A" : "B");
+       }
 
        if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) {
                gintsts = dwc2_readl(hsotg, GINTSTS);
index a1feaa09be57d2e4eb48ca76bbb32c99ac77445f..5d41c6a9b74069458c7a2f24d165fa69eb8133a6 100644 (file)
@@ -727,10 +727,12 @@ static int __maybe_unused dwc2_resume(struct device *dev)
                spin_unlock_irqrestore(&dwc2->lock, flags);
        }
 
-       /* Need to restore FORCEDEVMODE/FORCEHOSTMODE */
-       dwc2_force_dr_mode(dwc2);
-
-       dwc2_drd_resume(dwc2);
+       if (!dwc2->role_sw) {
+               /* Need to restore FORCEDEVMODE/FORCEHOSTMODE */
+               dwc2_force_dr_mode(dwc2);
+       } else {
+               dwc2_drd_resume(dwc2);
+       }
 
        if (dwc2_is_device_mode(dwc2))
                ret = dwc2_hsotg_resume(dwc2);