usb: cdns3: add hibernation pm notifier to set roothub wakeup 83/301883/2
authorminda.chen <minda.chen@starfivetech.com>
Tue, 17 Oct 2023 06:29:59 +0000 (15:29 +0900)
committerHoegeun Kwon <hoegeun.kwon@samsung.com>
Mon, 27 Nov 2023 06:16:34 +0000 (15:16 +0900)
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>
[hoegeun.kwon: cherry-pick the commit d86ba4820f59 from https://github.com/starfive-tech/linux/tree/JH7110_VisionFive2_6.1.y_devel]
Signed-off-by: Hoegeun Kwon <hoegeun.kwon@samsung.com>
Change-Id: Idd244d8d3e50fd8e84cb72ff5fcdb50ff4399788

drivers/usb/cdns3/cdns3-starfive.c
drivers/usb/cdns3/core.h
drivers/usb/cdns3/host.c

index 55ae4ac..f43da41 100644 (file)
@@ -176,6 +176,20 @@ static const unsigned int supported_cable[] = {
 };
 #endif
 
+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;
@@ -239,7 +253,7 @@ static int cdns_starfive_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       ret = of_platform_populate(node, NULL, NULL, dev);
+       ret = of_platform_populate(node, NULL, cdns_starfive_auxdata, dev);
        if (ret)
                return dev_err_probe(dev, ret, "Failed to create children\n");
 
index 2d332a7..f5ad30f 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 6164fc4..ac1276f 100644 (file)
@@ -24,7 +24,7 @@
 #define CFG_RXDET_P3_EN                BIT(15)
 #define LPM_2_STB_SWITCH_EN    BIT(25)
 
-static void xhci_cdns3_plat_start(struct usb_hcd *hcd)
+static void xhci_cdns3_plat_setup(struct usb_hcd *hcd)
 {
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
        u32 value;
@@ -45,9 +45,18 @@ static void xhci_cdns3_plat_start(struct usb_hcd *hcd)
        }
 }
 
+static void xhci_cdns3_plat_start(struct usb_hcd *hcd)
+{
+       xhci_cdns3_plat_setup(hcd);
+#ifdef CONFIG_PM_SLEEP
+       if (cdns3_hiber_data.pm_setup)
+               cdns3_hiber_data.pm_setup(hcd);
+#endif
+}
+
 static int xhci_cdns3_resume_quirk(struct usb_hcd *hcd)
 {
-       xhci_cdns3_plat_start(hcd);
+       xhci_cdns3_plat_setup(hcd);
        return 0;
 }
 
@@ -57,6 +66,67 @@ static const struct xhci_plat_priv xhci_plat_cdns3_xhci = {
        .resume_quirk = xhci_cdns3_resume_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 __cdns_host_init(struct cdns *cdns)
 {
        struct platform_device *xhci;
@@ -91,6 +161,12 @@ 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;
 
+#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)
@@ -118,6 +194,14 @@ err1:
 
 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;