usb: chipidea: tegra: Add runtime PM and OPP support
authorDmitry Osipenko <digetx@gmail.com>
Tue, 30 Nov 2021 23:23:26 +0000 (02:23 +0300)
committerThierry Reding <treding@nvidia.com>
Thu, 16 Dec 2021 13:05:23 +0000 (14:05 +0100)
The Tegra USB controller belongs to the core power domain and we're going
to enable GENPD support for the core domain. Now USB controller must be
resumed using runtime PM API in order to initialize the USB power state.
We already support runtime PM for the CI device, but CI's PM is separated
from the RPM managed by tegra-usb driver. Add runtime PM and OPP support
to the driver.

Acked-by: Peter Chen <peter.chen@kernel.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
drivers/usb/chipidea/ci_hdrc_tegra.c

index 60361141ac04fc2a94463cf8a85736daee45a207..a72a9474afea7f2e6f31aa9b3b8f220e1a439506 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/reset.h>
 
 #include <linux/usb.h>
@@ -15,6 +16,8 @@
 #include <linux/usb/of.h>
 #include <linux/usb/phy.h>
 
+#include <soc/tegra/common.h>
+
 #include "../host/ehci.h"
 
 #include "ci.h"
@@ -278,6 +281,8 @@ static int tegra_usb_probe(struct platform_device *pdev)
        if (!usb)
                return -ENOMEM;
 
+       platform_set_drvdata(pdev, usb);
+
        soc = of_device_get_match_data(&pdev->dev);
        if (!soc) {
                dev_err(&pdev->dev, "failed to match OF data\n");
@@ -296,11 +301,14 @@ static int tegra_usb_probe(struct platform_device *pdev)
                return err;
        }
 
-       err = clk_prepare_enable(usb->clk);
-       if (err < 0) {
-               dev_err(&pdev->dev, "failed to enable clock: %d\n", err);
+       err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
+       if (err)
+               return err;
+
+       pm_runtime_enable(&pdev->dev);
+       err = pm_runtime_resume_and_get(&pdev->dev);
+       if (err)
                return err;
-       }
 
        if (device_property_present(&pdev->dev, "nvidia,needs-double-reset"))
                usb->needs_double_reset = true;
@@ -320,8 +328,6 @@ static int tegra_usb_probe(struct platform_device *pdev)
        if (err)
                goto fail_power_off;
 
-       platform_set_drvdata(pdev, usb);
-
        /* setup and register ChipIdea HDRC device */
        usb->soc = soc;
        usb->data.name = "tegra-usb";
@@ -350,7 +356,9 @@ static int tegra_usb_probe(struct platform_device *pdev)
 phy_shutdown:
        usb_phy_shutdown(usb->phy);
 fail_power_off:
-       clk_disable_unprepare(usb->clk);
+       pm_runtime_put_sync_suspend(&pdev->dev);
+       pm_runtime_force_suspend(&pdev->dev);
+
        return err;
 }
 
@@ -360,15 +368,46 @@ static int tegra_usb_remove(struct platform_device *pdev)
 
        ci_hdrc_remove_device(usb->dev);
        usb_phy_shutdown(usb->phy);
+
+       pm_runtime_put_sync_suspend(&pdev->dev);
+       pm_runtime_force_suspend(&pdev->dev);
+
+       return 0;
+}
+
+static int __maybe_unused tegra_usb_runtime_resume(struct device *dev)
+{
+       struct tegra_usb *usb = dev_get_drvdata(dev);
+       int err;
+
+       err = clk_prepare_enable(usb->clk);
+       if (err < 0) {
+               dev_err(dev, "failed to enable clock: %d\n", err);
+               return err;
+       }
+
+       return 0;
+}
+
+static int __maybe_unused tegra_usb_runtime_suspend(struct device *dev)
+{
+       struct tegra_usb *usb = dev_get_drvdata(dev);
+
        clk_disable_unprepare(usb->clk);
 
        return 0;
 }
 
+static const struct dev_pm_ops tegra_usb_pm = {
+       SET_RUNTIME_PM_OPS(tegra_usb_runtime_suspend, tegra_usb_runtime_resume,
+                          NULL)
+};
+
 static struct platform_driver tegra_usb_driver = {
        .driver = {
                .name = "tegra-usb",
                .of_match_table = tegra_usb_of_match,
+               .pm = &tegra_usb_pm,
        },
        .probe = tegra_usb_probe,
        .remove = tegra_usb_remove,