usb: cdns3: add hibernation pm notifier to set roothub wakeup
authorminda.chen <minda.chen@starfivetech.com>
Mon, 30 Jan 2023 09:12:36 +0000 (01:12 -0800)
committerminda.chen <minda.chen@starfivetech.com>
Wed, 1 Feb 2023 06:48:04 +0000 (14:48 +0800)
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 <minda.chen@starfivetech.com>
drivers/usb/cdns3/cdns3-starfive.c
drivers/usb/cdns3/core.h
drivers/usb/cdns3/host.c

index d05bf61b80521509cbe010a04d5c5a30767a5bc2..84f0f483a74932bfb4b0d83b69f91e598ba27477 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/regmap.h>
 #include <linux/reset.h>
 #include <linux/usb/otg.h>
+#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;
index ab0cb68acd2390c631a43089b91523fb290dbe1a..7cd20b6f8ef026d4fada3e7fffdc571569608192 100644 (file)
@@ -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)
 };
 
 /**
index 84dadfa726aa6a816283b610ff258e0d2b53990a..c25b381d37cc49c66bdcbeb361ecd05f848f5207 100644 (file)
@@ -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;