}
}
-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) {
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);
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);
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
{
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)
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;
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) {
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)
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;
#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)
{
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)