From dbb6aa5ac8670fc5ae576a2422bc5957e3297d69 Mon Sep 17 00:00:00 2001 From: "minda.chen" Date: Mon, 30 Jan 2023 01:12:36 -0800 Subject: [PATCH] usb: cdns3: add hibernation pm notifier to set roothub wakeup In hibernation resume case. usb devices maybe not emulated, roothub is suspended, In this case, usb devices will be resume fail. Set roothub active while hibernation resume. Signed-off-by: minda.chen --- drivers/usb/cdns3/cdns3-starfive.c | 17 +++++++- drivers/usb/cdns3/core.h | 1 + drivers/usb/cdns3/host.c | 86 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 1 deletion(-) diff --git a/drivers/usb/cdns3/cdns3-starfive.c b/drivers/usb/cdns3/cdns3-starfive.c index d05bf61..84f0f48 100644 --- a/drivers/usb/cdns3/cdns3-starfive.c +++ b/drivers/usb/cdns3/cdns3-starfive.c @@ -17,6 +17,7 @@ #include #include #include +#include "core.h" #define USB_STRAP_HOST (2 << 0x10) #define USB_STRAP_DEVICE (4 << 0X10) @@ -239,6 +240,20 @@ get_res_err: return ret; } +static struct cdns3_platform_data cdns_starfive_pdata = { +#ifdef CONFIG_PM_SLEEP + .quirks = CDNS3_REGISTER_PM_NOTIFIER, +#endif +}; + +static const struct of_dev_auxdata cdns_starfive_auxdata[] = { + { + .compatible = "cdns,usb3", + .platform_data = &cdns_starfive_pdata, + }, + {}, +}; + static int cdns_starfive_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -299,7 +314,7 @@ static int cdns_starfive_probe(struct platform_device *pdev) goto exit; } - ret = of_platform_populate(node, NULL, NULL, dev); + ret = of_platform_populate(node, NULL, cdns_starfive_auxdata, dev); if (ret) { dev_err(dev, "Failed to create children: %d\n", ret); goto exit; diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h index ab0cb68..7cd20b6 100644 --- a/drivers/usb/cdns3/core.h +++ b/drivers/usb/cdns3/core.h @@ -44,6 +44,7 @@ struct cdns3_platform_data { bool suspend, bool wakeup); unsigned long quirks; #define CDNS3_DEFAULT_PM_RUNTIME_ALLOW BIT(0) +#define CDNS3_REGISTER_PM_NOTIFIER BIT(1) }; /** diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c index 84dadfa..c25b381 100644 --- a/drivers/usb/cdns3/host.c +++ b/drivers/usb/cdns3/host.c @@ -30,6 +30,76 @@ static const struct xhci_plat_priv xhci_plat_cdns3_xhci = { .suspend_quirk = xhci_cdns3_suspend_quirk, }; +#ifdef CONFIG_PM_SLEEP +struct cdns_hiber_data { + struct usb_hcd *hcd; + struct usb_hcd *shared_hcd; + struct notifier_block pm_notifier; + int (*pm_setup)(struct usb_hcd *hcd); + int (*pm_remove)(struct cdns *cdns); +}; +static struct cdns_hiber_data cdns3_hiber_data; + +static int cdns_hiber_notifier(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct usb_hcd *hcd = cdns3_hiber_data.hcd; + struct usb_hcd *shared_hcd = cdns3_hiber_data.shared_hcd; + + switch (action) { + case PM_RESTORE_PREPARE: + if (hcd->state == HC_STATE_SUSPENDED) { + usb_hcd_resume_root_hub(hcd); + usb_disable_autosuspend(hcd->self.root_hub); + } + if (shared_hcd->state == HC_STATE_SUSPENDED) { + usb_hcd_resume_root_hub(shared_hcd); + usb_disable_autosuspend(shared_hcd->self.root_hub); + } + break; + case PM_POST_RESTORE: + usb_enable_autosuspend(hcd->self.root_hub); + usb_enable_autosuspend(shared_hcd->self.root_hub); + break; + default: + break; + } + + return NOTIFY_DONE; +} + +static int cdns_register_pm_notifier(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + cdns3_hiber_data.hcd = xhci->main_hcd; + cdns3_hiber_data.shared_hcd = xhci->shared_hcd; + cdns3_hiber_data.pm_notifier.notifier_call = cdns_hiber_notifier; + return register_pm_notifier(&cdns3_hiber_data.pm_notifier); +} + +static int cdns_unregister_pm_notifier(struct cdns *cdns) +{ + int ret; + + ret = unregister_pm_notifier(&cdns3_hiber_data.pm_notifier); + + cdns3_hiber_data.hcd = NULL; + cdns3_hiber_data.shared_hcd = NULL; + + return ret; +} +#endif + +static int xhci_cdns3_plat_setup(struct usb_hcd *hcd) +{ +#ifdef CONFIG_PM_SLEEP + if (cdns3_hiber_data.pm_setup) + cdns3_hiber_data.pm_setup(hcd); +#endif + return 0; +} + static int __cdns_host_init(struct cdns *cdns) { struct platform_device *xhci; @@ -64,6 +134,13 @@ static int __cdns_host_init(struct cdns *cdns) if (cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW)) cdns->xhci_plat_data->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW; + cdns->xhci_plat_data->plat_setup = xhci_cdns3_plat_setup; +#ifdef CONFIG_PM_SLEEP + if (cdns->pdata && (cdns->pdata->quirks & CDNS3_REGISTER_PM_NOTIFIER)) { + cdns3_hiber_data.pm_setup = cdns_register_pm_notifier; + cdns3_hiber_data.pm_remove = cdns_unregister_pm_notifier; + } +#endif ret = platform_device_add_data(xhci, cdns->xhci_plat_data, sizeof(struct xhci_plat_priv)); if (ret) @@ -89,6 +166,7 @@ err1: return ret; } + static int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -117,6 +195,14 @@ static int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd) static void cdns_host_exit(struct cdns *cdns) { +#ifdef CONFIG_PM_SLEEP + if (cdns3_hiber_data.pm_remove) { + cdns3_hiber_data.pm_remove(cdns); + cdns3_hiber_data.pm_remove = NULL; + cdns3_hiber_data.pm_setup = NULL; + } +#endif + kfree(cdns->xhci_plat_data); platform_device_unregister(cdns->host_dev); cdns->host_dev = NULL; -- 2.7.4