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 v1.2.3 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 v1.2.3 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 v1.2.3 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/core.c | 3 ++- drivers/usb/cdns3/core.h | 4 ++++ drivers/usb/cdns3/host.c | 20 +++++++++++++++++--- 3 files changed, 23 insertions(+), 4 deletions(-) (limited to 'drivers/usb/cdns3/host.c') diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index f2dedce3a40e..54d841aa626f 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -559,7 +559,8 @@ static int cdns3_probe(struct platform_device *pdev) device_set_wakeup_capable(dev, true); pm_runtime_set_active(dev); pm_runtime_enable(dev); - pm_runtime_forbid(dev); + if (!(cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW)) + pm_runtime_forbid(dev); /* * The controller needs less time between bus and controller suspend, diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h index 8a40d53d5ede..3176f924293a 100644 --- a/drivers/usb/cdns3/core.h +++ b/drivers/usb/cdns3/core.h @@ -42,6 +42,8 @@ struct cdns3_role_driver { struct cdns3_platform_data { int (*platform_suspend)(struct device *dev, bool suspend, bool wakeup); + unsigned long quirks; +#define CDNS3_DEFAULT_PM_RUNTIME_ALLOW BIT(0) }; /** @@ -73,6 +75,7 @@ struct cdns3_platform_data { * @wakeup_pending: wakeup interrupt pending * @pdata: platform data from glue layer * @lock: spinlock structure + * @xhci_plat_data: xhci private data structure pointer */ struct cdns3 { struct device *dev; @@ -106,6 +109,7 @@ struct cdns3 { bool wakeup_pending; struct cdns3_platform_data *pdata; spinlock_t lock; + struct xhci_plat_priv *xhci_plat_data; }; int cdns3_hw_role_switch(struct cdns3 *cdns); 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 v1.2.3 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/core.c | 2 +- drivers/usb/cdns3/host.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb/cdns3/host.c') diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 54d841aa626f..0f08aebce86d 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -559,7 +559,7 @@ static int cdns3_probe(struct platform_device *pdev) device_set_wakeup_capable(dev, true); pm_runtime_set_active(dev); pm_runtime_enable(dev); - if (!(cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW)) + if (!(cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW))) pm_runtime_forbid(dev); /* 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 v1.2.3