Commit 8b85e11c authored by Dmitry Osipenko's avatar Dmitry Osipenko Committed by Thierry Reding

usb: chipidea: tegra: Add runtime PM and OPP support

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: default avatarPeter Chen <peter.chen@kernel.org>
Reviewed-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
Signed-off-by: default avatarDmitry Osipenko <digetx@gmail.com>
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent c132bc88
......@@ -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,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment