From b1234e3b3b265588bab1e7a28508621045b87efa Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 2 Sep 2020 17:57:32 +0800 Subject: usb: cdns3: add runtime PM support Introduce runtime PM and wakeup interrupt handler for cdns3, the runtime PM is default off since other cdns3 may not implement glue layer support for runtime PM. One typical wakeup event use case is xHCI runtime suspend will clear USBCMD.RS bit, after that the xHCI will not trigger any interrupts, so its parent (cdns core device) needs to resume xHCI device when any (wakeup) events occurs at host port. When the controller is in low power mode, the lpm flag will be set. The interrupt triggered later than lpm flag is set considers as wakeup interrupt and handled at cdns_wakeup_irq. Once the wakeup occurs, it first disables interrupt to avoid later interrupt occurrence since the controller is in low power mode at that time, and access registers may be invalid at that time. At wakeup handler, it will call pm_request_resume to wakeup xHCI device, and at runtime resume handler, it will enable interrupt again. The API platform_suspend is introduced for glue layer to implement platform specific PM sequence. Reviewed-by: Pawel Laszczak Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/cdns3/host.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/usb/cdns3/host.c') diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c index 36c63d9ecd37..b3e2cb69762c 100644 --- a/drivers/usb/cdns3/host.c +++ b/drivers/usb/cdns3/host.c @@ -13,11 +13,13 @@ #include "core.h" #include "drd.h" #include "host-export.h" +#include static int __cdns3_host_init(struct cdns3 *cdns) { struct platform_device *xhci; int ret; + struct usb_hcd *hcd; cdns3_drd_host_on(cdns); @@ -43,6 +45,11 @@ static int __cdns3_host_init(struct cdns3 *cdns) goto err1; } + /* Glue needs to access xHCI region register for Power management */ + hcd = platform_get_drvdata(xhci); + if (hcd) + cdns->xhci_regs = hcd->regs; + return 0; err1: platform_device_put(xhci); -- cgit From ed22764847e8100f0af9af91ccfa58e5c559bd47 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 22 May 2020 17:56:30 +0800 Subject: usb: cdns3: host: add .suspend_quirk for xhci-plat.c cdns3 has some special PM sequence between xhci_bus_suspend and xhci_suspend, add quirk to implement it. Reviewed-by: Pawel Laszczak Signed-off-by: Peter Chen --- drivers/usb/cdns3/host.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'drivers/usb/cdns3/host.c') diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c index b3e2cb69762c..de8da737fa25 100644 --- a/drivers/usb/cdns3/host.c +++ b/drivers/usb/cdns3/host.c @@ -14,6 +14,18 @@ #include "drd.h" #include "host-export.h" #include +#include "../host/xhci.h" +#include "../host/xhci-plat.h" + +#define XECP_PORT_CAP_REG 0x8000 +#define XECP_AUX_CTRL_REG1 0x8120 + +#define CFG_RXDET_P3_EN BIT(15) +#define LPM_2_STB_SWITCH_EN BIT(25) + +static const struct xhci_plat_priv xhci_plat_cdns3_xhci = { + .suspend_quirk = xhci_cdns3_suspend_quirk, +}; static int __cdns3_host_init(struct cdns3 *cdns) { @@ -39,6 +51,11 @@ static int __cdns3_host_init(struct cdns3 *cdns) goto err1; } + ret = platform_device_add_data(xhci, &xhci_plat_cdns3_xhci, + sizeof(struct xhci_plat_priv)); + if (ret) + goto err1; + ret = platform_device_add(xhci); if (ret) { dev_err(cdns->dev, "failed to register xHCI device\n"); @@ -56,6 +73,32 @@ err1: return ret; } +int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + u32 value; + + if (pm_runtime_status_suspended(hcd->self.controller)) + return 0; + + /* set usbcmd.EU3S */ + value = readl(&xhci->op_regs->command); + value |= CMD_PM_INDEX; + writel(value, &xhci->op_regs->command); + + if (hcd->regs) { + value = readl(hcd->regs + XECP_AUX_CTRL_REG1); + value |= CFG_RXDET_P3_EN; + writel(value, hcd->regs + XECP_AUX_CTRL_REG1); + + value = readl(hcd->regs + XECP_PORT_CAP_REG); + value |= LPM_2_STB_SWITCH_EN; + writel(value, hcd->regs + XECP_PORT_CAP_REG); + } + + return 0; +} + static void cdns3_host_exit(struct cdns3 *cdns) { platform_device_unregister(cdns->host_dev); -- cgit From 68ed3f3d8a057bd34254e885a6306fedc0936e50 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 22 May 2020 18:08:31 +0800 Subject: usb: cdns3: host: add xhci_plat_priv quirk XHCI_SKIP_PHY_INIT cdns3 manages PHY by own DRD driver, so skip the management by HCD core. Reviewed-by: Jun Li Reviewed-by: Pawel Laszczak Signed-off-by: Peter Chen --- drivers/usb/cdns3/host.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb/cdns3/host.c') diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c index de8da737fa25..f84739327a16 100644 --- a/drivers/usb/cdns3/host.c +++ b/drivers/usb/cdns3/host.c @@ -24,6 +24,7 @@ #define LPM_2_STB_SWITCH_EN BIT(25) static const struct xhci_plat_priv xhci_plat_cdns3_xhci = { + .quirks = XHCI_SKIP_PHY_INIT, .suspend_quirk = xhci_cdns3_suspend_quirk, }; -- cgit From 1cc6edd8a96fb3229d8309c49967713b5c79524f Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 27 Jul 2020 17:53:37 +0800 Subject: usb: cdns3: host: disable BEI support The Cadence xHCI doesn't support BEI well, it causes the disconnection of ISOC devices can't be detected, so we disable it. Reviewed-by: Jun Li Signed-off-by: Peter Chen --- drivers/usb/cdns3/host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/cdns3/host.c') diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c index f84739327a16..b273ae2231d5 100644 --- a/drivers/usb/cdns3/host.c +++ b/drivers/usb/cdns3/host.c @@ -24,7 +24,7 @@ #define LPM_2_STB_SWITCH_EN BIT(25) static const struct xhci_plat_priv xhci_plat_cdns3_xhci = { - .quirks = XHCI_SKIP_PHY_INIT, + .quirks = XHCI_SKIP_PHY_INIT | XHCI_AVOID_BEI, .suspend_quirk = xhci_cdns3_suspend_quirk, }; -- cgit From 7cea9657756b2c83069a775c0671ff169bce456a Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Mon, 28 Sep 2020 15:20:03 +0800 Subject: usb: cdns3: add quirk for enable runtime pm by default Some vendors (eg: NXP) may want to enable runtime pm by default for power saving, add one quirk for it. Reviewed-by: Jun Li Signed-off-by: Peter Chen --- drivers/usb/cdns3/host.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'drivers/usb/cdns3/host.c') diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c index b273ae2231d5..08103785a17a 100644 --- a/drivers/usb/cdns3/host.c +++ b/drivers/usb/cdns3/host.c @@ -52,15 +52,25 @@ static int __cdns3_host_init(struct cdns3 *cdns) goto err1; } - ret = platform_device_add_data(xhci, &xhci_plat_cdns3_xhci, + cdns->xhci_plat_data = kmemdup(&xhci_plat_cdns3_xhci, + sizeof(struct xhci_plat_priv), GFP_KERNEL); + if (!cdns->xhci_plat_data) { + ret = -ENOMEM; + goto err1; + } + + if (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW) + cdns->xhci_plat_data->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW; + + ret = platform_device_add_data(xhci, cdns->xhci_plat_data, sizeof(struct xhci_plat_priv)); if (ret) - goto err1; + goto free_memory; ret = platform_device_add(xhci); if (ret) { dev_err(cdns->dev, "failed to register xHCI device\n"); - goto err1; + goto free_memory; } /* Glue needs to access xHCI region register for Power management */ @@ -69,6 +79,9 @@ static int __cdns3_host_init(struct cdns3 *cdns) cdns->xhci_regs = hcd->regs; return 0; + +free_memory: + kfree(cdns->xhci_plat_data); err1: platform_device_put(xhci); return ret; @@ -102,6 +115,7 @@ int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd) static void cdns3_host_exit(struct cdns3 *cdns) { + kfree(cdns->xhci_plat_data); platform_device_unregister(cdns->host_dev); cdns->host_dev = NULL; cdns3_drd_host_off(cdns); -- cgit From 448373d9db1a7000072f65103af19e20503f0c0c Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 23 Nov 2020 12:49:31 +0200 Subject: usb: cdns3: fix NULL pointer dereference on no platform data Some platforms (e.g. TI) will not have any platform data which will lead to NULL pointer dereference if we don't check for NULL pdata. Fixes: 7cea9657756b ("usb: cdns3: add quirk for enable runtime pm by default") Reported-by: Nishanth Menon Signed-off-by: Roger Quadros Acked-by: Pawel Laszczak Signed-off-by: Peter Chen --- drivers/usb/cdns3/host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/cdns3/host.c') diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c index 08103785a17a..ec89f2e5430f 100644 --- a/drivers/usb/cdns3/host.c +++ b/drivers/usb/cdns3/host.c @@ -59,7 +59,7 @@ static int __cdns3_host_init(struct cdns3 *cdns) goto err1; } - if (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW) + if (cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW)) cdns->xhci_plat_data->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW; ret = platform_device_add_data(xhci, cdns->xhci_plat_data, -- cgit