]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
usb: dwc3: Set SUSPENDENABLE soon after phy init
authorThinh Nguyen <Thinh.Nguyen@synopsys.com>
Thu, 30 Jan 2025 23:49:31 +0000 (23:49 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 13 Mar 2025 11:51:08 +0000 (12:51 +0100)
commit cc5bfc4e16fc1d1c520cd7bb28646e82b6e69217 upstream.

After phy initialization, some phy operations can only be executed while
in lower P states. Ensure GUSB3PIPECTL.SUSPENDENABLE and
GUSB2PHYCFG.SUSPHY are set soon after initialization to avoid blocking
phy ops.

Previously the SUSPENDENABLE bits are only set after the controller
initialization, which may not happen right away if there's no gadget
driver or xhci driver bound. Revise this to clear SUSPENDENABLE bits
only when there's mode switching (change in GCTL.PRTCAPDIR).

Fixes: 6d735722063a ("usb: dwc3: core: Prevent phy suspend during init")
Cc: stable <stable@kernel.org>
Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Link: https://lore.kernel.org/r/633aef0afee7d56d2316f7cc3e1b2a6d518a8cc9.1738280911.git.Thinh.Nguyen@synopsys.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/dwc3/core.c
drivers/usb/dwc3/core.h
drivers/usb/dwc3/drd.c

index 485d6804393d9d5107ed52eb99dfcd48509e6290..8c4a292634e5684d2d1c487eff052bac69527e7c 100644 (file)
@@ -123,11 +123,24 @@ void dwc3_enable_susphy(struct dwc3 *dwc, bool enable)
        dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
 }
 
-void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
+void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
 {
+       unsigned int hw_mode;
        u32 reg;
 
        reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+
+        /*
+         * For DRD controllers, GUSB3PIPECTL.SUSPENDENABLE and
+         * GUSB2PHYCFG.SUSPHY should be cleared during mode switching,
+         * and they can be set after core initialization.
+         */
+       hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
+       if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD && !ignore_susphy) {
+               if (DWC3_GCTL_PRTCAP(reg) != mode)
+                       dwc3_enable_susphy(dwc, false);
+       }
+
        reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
        reg |= DWC3_GCTL_PRTCAPDIR(mode);
        dwc3_writel(dwc->regs, DWC3_GCTL, reg);
@@ -207,7 +220,7 @@ static void __dwc3_set_mode(struct work_struct *work)
 
        spin_lock_irqsave(&dwc->lock, flags);
 
-       dwc3_set_prtcap(dwc, desired_dr_role);
+       dwc3_set_prtcap(dwc, desired_dr_role, false);
 
        spin_unlock_irqrestore(&dwc->lock, flags);
 
@@ -651,16 +664,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
         */
        reg &= ~DWC3_GUSB3PIPECTL_UX_EXIT_PX;
 
-       /*
-        * Above DWC_usb3.0 1.94a, it is recommended to set
-        * DWC3_GUSB3PIPECTL_SUSPHY to '0' during coreConsultant configuration.
-        * So default value will be '0' when the core is reset. Application
-        * needs to set it to '1' after the core initialization is completed.
-        *
-        * Similarly for DRD controllers, GUSB3PIPECTL.SUSPENDENABLE must be
-        * cleared after power-on reset, and it can be set after core
-        * initialization.
-        */
+       /* Ensure the GUSB3PIPECTL.SUSPENDENABLE is cleared prior to phy init. */
        reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
 
        if (dwc->u2ss_inp3_quirk)
@@ -733,15 +737,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
                break;
        }
 
-       /*
-        * Above DWC_usb3.0 1.94a, it is recommended to set
-        * DWC3_GUSB2PHYCFG_SUSPHY to '0' during coreConsultant configuration.
-        * So default value will be '0' when the core is reset. Application
-        * needs to set it to '1' after the core initialization is completed.
-        *
-        * Similarly for DRD controllers, GUSB2PHYCFG.SUSPHY must be cleared
-        * after power-on reset, and it can be set after core initialization.
-        */
+       /* Ensure the GUSB2PHYCFG.SUSPHY is cleared prior to phy init. */
        reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
 
        if (dwc->dis_enblslpm_quirk)
@@ -1158,6 +1154,25 @@ static int dwc3_core_init(struct dwc3 *dwc)
                }
        }
 
+       /*
+        * Above DWC_usb3.0 1.94a, it is recommended to set
+        * DWC3_GUSB3PIPECTL_SUSPHY and DWC3_GUSB2PHYCFG_SUSPHY to '0' during
+        * coreConsultant configuration. So default value will be '0' when the
+        * core is reset. Application needs to set it to '1' after the core
+        * initialization is completed.
+        *
+        * Certain phy requires to be in P0 power state during initialization.
+        * Make sure GUSB3PIPECTL.SUSPENDENABLE and GUSB2PHYCFG.SUSPHY are clear
+        * prior to phy init to maintain in the P0 state.
+        *
+        * After phy initialization, some phy operations can only be executed
+        * while in lower P states. Ensure GUSB3PIPECTL.SUSPENDENABLE and
+        * GUSB2PHYCFG.SUSPHY are set soon after initialization to avoid
+        * blocking phy ops.
+        */
+       if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A))
+               dwc3_enable_susphy(dwc, true);
+
        return 0;
 
 err4:
@@ -1245,7 +1260,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
 
        switch (dwc->dr_mode) {
        case USB_DR_MODE_PERIPHERAL:
-               dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+               dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, false);
 
                if (dwc->usb2_phy)
                        otg_set_vbus(dwc->usb2_phy->otg, false);
@@ -1257,7 +1272,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
                        return dev_err_probe(dev, ret, "failed to initialize gadget\n");
                break;
        case USB_DR_MODE_HOST:
-               dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
+               dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST, false);
 
                if (dwc->usb2_phy)
                        otg_set_vbus(dwc->usb2_phy->otg, true);
@@ -1300,7 +1315,7 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
        }
 
        /* de-assert DRVVBUS for HOST and OTG mode */
-       dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+       dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true);
 }
 
 static void dwc3_get_properties(struct dwc3 *dwc)
@@ -1882,7 +1897,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
                if (ret)
                        return ret;
 
-               dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+               dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true);
                dwc3_gadget_resume(dwc);
                break;
        case DWC3_GCTL_PRTCAP_HOST:
@@ -1890,7 +1905,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
                        ret = dwc3_core_init_for_resume(dwc);
                        if (ret)
                                return ret;
-                       dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
+                       dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST, true);
                        break;
                }
                /* Restore GUSB2PHYCFG bits that were modified in suspend */
@@ -1915,7 +1930,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
                if (ret)
                        return ret;
 
-               dwc3_set_prtcap(dwc, dwc->current_dr_role);
+               dwc3_set_prtcap(dwc, dwc->current_dr_role, true);
 
                dwc3_otg_init(dwc);
                if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) {
index d4a37a1539d2cf3e00b4455f321b57acf760aaa4..c2783d87e6c81e1197dbcbdedc0191b7ebd1b0d0 100644 (file)
@@ -1475,7 +1475,7 @@ struct dwc3_gadget_ep_cmd_params {
 #define DWC3_HAS_OTG                   BIT(3)
 
 /* prototypes */
-void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode);
+void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy);
 void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
 u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
 
index ba37bc72a220573e67edbffd79bcc71c65b29421..b3c6de3d232bd517c8bbfd03b12c2cc213f3ab37 100644 (file)
@@ -173,7 +173,7 @@ void dwc3_otg_init(struct dwc3 *dwc)
         * block "Initialize GCTL for OTG operation".
         */
        /* GCTL.PrtCapDir=2'b11 */
-       dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG);
+       dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG, true);
        /* GUSB2PHYCFG0.SusPHY=0 */
        reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
        reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
@@ -585,7 +585,7 @@ int dwc3_drd_init(struct dwc3 *dwc)
 
                dwc3_drd_update(dwc);
        } else {
-               dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG);
+               dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG, true);
 
                /* use OTG block to get ID event */
                irq = dwc3_otg_get_irq(dwc);