MLK-22527-2 usb: cdns3: quit if the port is woken up during suspending
authorPeter Chen <peter.chen@nxp.com>
Mon, 1 Jul 2019 10:05:35 +0000 (18:05 +0800)
committerPeter Chen <peter.chen@nxp.com>
Tue, 10 Sep 2019 08:58:48 +0000 (16:58 +0800)
If the port receives the resume during the suspending, it needs to
quit instead of going on, it could keep controller status correct,
and eliminating below timeout warning message.

cdns-usb3 5b110000.usb3: wait lpm_clk_req timeout
cdns-usb3 5b110000.usb3: wait phy_refclk_req timeout

Reviewed-by: Jun Li <jun.li@nxp.com>
Signed-off-by: Peter Chen <peter.chen@nxp.com>
drivers/usb/cdns3/core.c
drivers/usb/cdns3/host.c

index ab2886a..c201d00 100644 (file)
@@ -795,13 +795,14 @@ static void cdns3_set_wakeup(void *none_core_regs, bool enable)
        }
 }
 
-static void cdns3_enter_suspend(struct cdns3 *cdns, bool suspend, bool wakeup)
+static int cdns3_enter_suspend(struct cdns3 *cdns, bool suspend, bool wakeup)
 {
        void __iomem *otg_regs = cdns->otg_regs;
        void __iomem *xhci_regs = cdns->xhci_regs;
        void __iomem *none_core_regs = cdns->none_core_regs;
        u32 value;
        int timeout_us = 100000;
+       int ret = 0;
 
        if (cdns->role == CDNS3_ROLE_GADGET) {
                if (suspend) {
@@ -810,14 +811,17 @@ static void cdns3_enter_suspend(struct cdns3 *cdns, bool suspend, bool wakeup)
                        value |= ALL_SW_RESET;
                        writel(value, cdns->none_core_regs + USB3_CORE_CTRL1);
                }
-               return;
+               return 0;
        } else if (cdns->role == CDNS3_ROLE_END) {
-               return;
+               return 0;
        }
 
        if (suspend) {
                if (cdns3_role(cdns)->suspend)
-                       cdns3_role(cdns)->suspend(cdns, wakeup);
+                       ret = cdns3_role(cdns)->suspend(cdns, wakeup);
+
+               if (ret)
+                       return ret;
 
                /* SW request low power when all usb ports allow to it ??? */
                value = readl(xhci_regs + XECP_PM_PMCSR);
@@ -867,7 +871,6 @@ static void cdns3_enter_suspend(struct cdns3 *cdns, bool suspend, bool wakeup)
                        dev_err(cdns->dev, "wait phy_refclk_req timeout\n");
 
                dev_dbg(cdns->dev, "phy_refclk_req cleared\n");
-
                cdns3_set_wakeup(none_core_regs, true);
        } else {
                value = readl(none_core_regs + USB3_INT_REG);
@@ -930,15 +933,25 @@ static void cdns3_enter_suspend(struct cdns3 *cdns, bool suspend, bool wakeup)
                if (timeout_us <= 0)
                        dev_err(cdns->dev, "wait xhci_power_on_ready timeout\n");
        }
+
+       return ret;
 }
 
-static void cdns3_controller_suspend(struct cdns3 *cdns, bool wakeup)
+static int cdns3_controller_suspend(struct cdns3 *cdns, bool wakeup)
 {
+       int ret = 0;
+
        disable_irq(cdns->irq);
-       cdns3_enter_suspend(cdns, true, wakeup);
+       ret = cdns3_enter_suspend(cdns, true, wakeup);
+       if (ret) {
+               enable_irq(cdns->irq);
+               return ret;
+       }
+
        usb_phy_set_suspend(cdns->usbphy, 1);
        cdns->in_lpm = true;
        enable_irq(cdns->irq);
+       return ret;
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -946,18 +959,22 @@ static int cdns3_suspend(struct device *dev)
 {
        struct cdns3 *cdns = dev_get_drvdata(dev);
        bool wakeup = device_may_wakeup(dev);
+       int ret;
 
        dev_dbg(dev, "at %s\n", __func__);
 
        if (pm_runtime_status_suspended(dev))
                pm_runtime_resume(dev);
 
-       cdns3_controller_suspend(cdns, wakeup);
+       ret = cdns3_controller_suspend(cdns, wakeup);
+       if (ret)
+               return ret;
+
        cdns3_disable_unprepare_clks(dev);
        if (wakeup)
                enable_irq_wake(cdns->irq);
 
-       return 0;
+       return ret;
 }
 
 static int cdns3_resume(struct device *dev)
@@ -991,6 +1008,7 @@ static int cdns3_resume(struct device *dev)
                        cdns3_role(cdns)->resume(cdns, true);
                }
        } else {
+               /* At resume path, never return error */
                cdns3_enter_suspend(cdns, false, false);
                if (cdns->wakeup_int) {
                        cdns->wakeup_int = false;
@@ -1025,6 +1043,7 @@ static int cdns3_resume(struct device *dev)
 static int cdns3_runtime_suspend(struct device *dev)
 {
        struct cdns3 *cdns = dev_get_drvdata(dev);
+       int ret;
 
        dev_dbg(dev, "at the begin of %s\n", __func__);
        if (cdns->in_lpm) {
@@ -1032,12 +1051,15 @@ static int cdns3_runtime_suspend(struct device *dev)
                return 0;
        }
 
-       cdns3_controller_suspend(cdns, true);
+       ret = cdns3_controller_suspend(cdns, true);
+       if (ret)
+               return ret;
+
        cdns3_disable_unprepare_clks(dev);
 
        dev_dbg(dev, "at the end of %s\n", __func__);
 
-       return 0;
+       return ret;
 }
 
 static int cdns3_runtime_resume(struct device *dev)
@@ -1055,6 +1077,7 @@ static int cdns3_runtime_resume(struct device *dev)
                return ret;
 
        usb_phy_set_suspend(cdns->usbphy, 0);
+       /* At resume path, never return error */
        cdns3_enter_suspend(cdns, false, false);
        cdns->in_lpm = 0;
 
index d0dd591..f6633de 100644 (file)
@@ -28,6 +28,8 @@
 #include "host-export.h"
 #include "cdns3-nxp-reg-def.h"
 
+#define XHCI_WAKEUP_STATUS     (PORT_RC | PORT_PLC)
+
 static struct hc_driver __read_mostly xhci_cdns3_hc_driver;
 
 static void xhci_cdns3_quirks(struct device *dev, struct xhci_hcd *xhci)
@@ -236,12 +238,28 @@ static int cdns3_host_suspend(struct cdns3 *cdns, bool do_wakeup)
 {
        struct device *dev = cdns->host_dev;
        struct xhci_hcd *xhci;
+       void __iomem *xhci_regs = cdns->xhci_regs;
+       u32 portsc_usb2, portsc_usb3;
+       int ret;
 
        if (!dev)
                return 0;
 
        xhci = hcd_to_xhci(dev_get_drvdata(dev));
-       return xhci_suspend(xhci, do_wakeup);
+       ret = xhci_suspend(xhci, do_wakeup);
+       if (ret)
+               return ret;
+
+       portsc_usb2 = readl(xhci_regs + 0x480);
+       portsc_usb3 = readl(xhci_regs + 0x490);
+       if ((portsc_usb2 & XHCI_WAKEUP_STATUS) ||
+               (portsc_usb3 & XHCI_WAKEUP_STATUS)) {
+               dev_dbg(cdns->dev, "wakeup occurs\n");
+               cdns3_role(cdns)->resume(cdns, false);
+               return -EBUSY;
+       }
+
+       return ret;
 }
 
 static int cdns3_host_resume(struct cdns3 *cdns, bool hibernated)