};
#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;
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");
#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;
}
}
+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;
}
.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;
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)
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;