usb: dwc3: xilinx: fix usb3 non-wakeup source resume failure
authorPiyush Mehta <piyush.mehta@amd.com>
Mon, 12 Sep 2022 11:10:17 +0000 (16:40 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 22 Sep 2022 13:52:30 +0000 (15:52 +0200)
When USB is in super-speed mode and disabled as a wakeup source,
observed that on the resume path, lanes have not been configured
properly in the phy-zynqmp driver.
As a result, after the resume, USB device detection failed on host.

To resolved the above issue, added phy_init on resume and phy_exit
on suspend path, to configure the GT lanes correctly.
The re-initialization of phy, reset the device and re-enumerate
the USB subsystem.

This use-case is specific to Xilinx ZynqMP SoC.

Signed-off-by: Piyush Mehta <piyush.mehta@amd.com>
Link: https://lore.kernel.org/r/20220912111017.901321-3-piyush.mehta@amd.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/dwc3/dwc3-xilinx.c

index a0d0280..8607d4c 100644 (file)
@@ -47,6 +47,7 @@ struct dwc3_xlnx {
        struct device                   *dev;
        void __iomem                    *regs;
        int                             (*pltfm_init)(struct dwc3_xlnx *data);
+       struct phy                      *usb3_phy;
 };
 
 static void dwc3_xlnx_mask_phy_rst(struct dwc3_xlnx *priv_data, bool mask)
@@ -100,13 +101,12 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
        struct device           *dev = priv_data->dev;
        struct reset_control    *crst, *hibrst, *apbrst;
        struct gpio_desc        *reset_gpio;
-       struct phy              *usb3_phy;
        int                     ret = 0;
        u32                     reg;
 
-       usb3_phy = devm_phy_optional_get(dev, "usb3-phy");
-       if (IS_ERR(usb3_phy)) {
-               ret = PTR_ERR(usb3_phy);
+       priv_data->usb3_phy = devm_phy_optional_get(dev, "usb3-phy");
+       if (IS_ERR(priv_data->usb3_phy)) {
+               ret = PTR_ERR(priv_data->usb3_phy);
                dev_err_probe(dev, ret,
                              "failed to get USB3 PHY\n");
                goto err;
@@ -121,7 +121,7 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
         * in use but the usb3-phy entry is missing from the device tree.
         * Therefore, skip these operations in this case.
         */
-       if (!usb3_phy)
+       if (!priv_data->usb3_phy)
                goto skip_usb3_phy;
 
        crst = devm_reset_control_get_exclusive(dev, "usb_crst");
@@ -166,9 +166,9 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
                goto err;
        }
 
-       ret = phy_init(usb3_phy);
+       ret = phy_init(priv_data->usb3_phy);
        if (ret < 0) {
-               phy_exit(usb3_phy);
+               phy_exit(priv_data->usb3_phy);
                goto err;
        }
 
@@ -196,9 +196,9 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
                goto err;
        }
 
-       ret = phy_power_on(usb3_phy);
+       ret = phy_power_on(priv_data->usb3_phy);
        if (ret < 0) {
-               phy_exit(usb3_phy);
+               phy_exit(priv_data->usb3_phy);
                goto err;
        }
 
@@ -350,6 +350,8 @@ static int __maybe_unused dwc3_xlnx_suspend(struct device *dev)
 {
        struct dwc3_xlnx *priv_data = dev_get_drvdata(dev);
 
+       phy_exit(priv_data->usb3_phy);
+
        /* Disable the clocks */
        clk_bulk_disable(priv_data->num_clocks, priv_data->clks);
 
@@ -365,6 +367,16 @@ static int __maybe_unused dwc3_xlnx_resume(struct device *dev)
        if (ret)
                return ret;
 
+       ret = phy_init(priv_data->usb3_phy);
+       if (ret < 0)
+               return ret;
+
+       ret = phy_power_on(priv_data->usb3_phy);
+       if (ret < 0) {
+               phy_exit(priv_data->usb3_phy);
+               return ret;
+       }
+
        return 0;
 }