From d62326d5ccf4ab5832c504360a96f2f03b542c2a Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 1 Jul 2019 18:05:35 +0800 Subject: [PATCH] MLK-22527-2 usb: cdns3: quit if the port is woken up during suspending 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 Signed-off-by: Peter Chen --- drivers/usb/cdns3/core.c | 45 ++++++++++++++++++++++++++++++---------- drivers/usb/cdns3/host.c | 20 +++++++++++++++++- 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index ab2886a39ee8..c201d00ac194 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -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; diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c index d0dd591a4aa5..f6633de87faf 100644 --- a/drivers/usb/cdns3/host.c +++ b/drivers/usb/cdns3/host.c @@ -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) -- 2.17.1