diff --git a/drivers/bus/tegra-gmi.c b/drivers/bus/tegra-gmi.c
index a6570789f7af..51cac6822f20 100644
--- a/drivers/bus/tegra-gmi.c
+++ b/drivers/bus/tegra-gmi.c
@@ -13,8 +13,12 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
#include <linux/reset.h>
+#include <soc/tegra/common.h>
+
#define TEGRA_GMI_CONFIG 0x00
#define TEGRA_GMI_CONFIG_GO BIT(31)
#define TEGRA_GMI_BUS_WIDTH_32BIT BIT(30)
@@ -54,36 +58,20 @@ static int tegra_gmi_enable(struct tegra_gmi *gmi)
{
int err;
- err = clk_prepare_enable(gmi->clk);
- if (err < 0) {
- dev_err(gmi->dev, "failed to enable clock: %d\n", err);
+ pm_runtime_enable(gmi->dev);
+ err = pm_runtime_resume_and_get(gmi->dev);
+ if (err) {
+ pm_runtime_disable(gmi->dev);
return err;
}
- reset_control_assert(gmi->rst);
- usleep_range(2000, 4000);
- reset_control_deassert(gmi->rst);
-
- writel(gmi->snor_timing0, gmi->base + TEGRA_GMI_TIMING0);
- writel(gmi->snor_timing1, gmi->base + TEGRA_GMI_TIMING1);
-
- gmi->snor_config |= TEGRA_GMI_CONFIG_GO;
- writel(gmi->snor_config, gmi->base + TEGRA_GMI_CONFIG);
-
return 0;
}
static void tegra_gmi_disable(struct tegra_gmi *gmi)
{
- u32 config;
-
- /* stop GMI operation */
- config = readl(gmi->base + TEGRA_GMI_CONFIG);
- config &= ~TEGRA_GMI_CONFIG_GO;
- writel(config, gmi->base + TEGRA_GMI_CONFIG);
-
- reset_control_assert(gmi->rst);
- clk_disable_unprepare(gmi->clk);
+ pm_runtime_put(gmi->dev);
+ pm_runtime_disable(gmi->dev);
}
static int tegra_gmi_parse_dt(struct tegra_gmi *gmi)@@ -213,6 +201,7 @@ static int tegra_gmi_probe(struct platform_device *pdev)
if (!gmi)
return -ENOMEM;
+ platform_set_drvdata(pdev, gmi);
gmi->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -232,6 +221,10 @@ static int tegra_gmi_probe(struct platform_device *pdev)
return PTR_ERR(gmi->rst);
}
+ err = devm_tegra_core_dev_init_opp_table_simple(&pdev->dev);
+ if (err)
+ return err;
+
err = tegra_gmi_parse_dt(gmi);
if (err)
return err;
@@ -247,8 +240,6 @@ static int tegra_gmi_probe(struct platform_device *pdev)
return err;
}
- platform_set_drvdata(pdev, gmi);
-
return 0;
}
@@ -262,6 +253,58 @@ static int tegra_gmi_remove(struct platform_device *pdev)
return 0;
}
+static int __maybe_unused tegra_gmi_runtime_resume(struct device *dev)
+{
+ struct tegra_gmi *gmi = dev_get_drvdata(dev);
+ int err;
+
+ err = dev_pm_opp_sync(gmi->dev);
+ if (err) {
+ dev_err(gmi->dev, "failed to sync OPP: %d\n", err);
+ return err;
+ }
+
+ err = clk_prepare_enable(gmi->clk);
+ if (err < 0) {
+ dev_err(gmi->dev, "failed to enable clock: %d\n", err);
+ return err;
+ }
+
+ reset_control_assert(gmi->rst);
+ usleep_range(2000, 4000);
+ reset_control_deassert(gmi->rst);
+
+ writel(gmi->snor_timing0, gmi->base + TEGRA_GMI_TIMING0);
+ writel(gmi->snor_timing1, gmi->base + TEGRA_GMI_TIMING1);
+
+ gmi->snor_config |= TEGRA_GMI_CONFIG_GO;
+ writel(gmi->snor_config, gmi->base + TEGRA_GMI_CONFIG);
+
+ return 0;
+}
+
+static int __maybe_unused tegra_gmi_runtime_suspend(struct device *dev)
+{
+ struct tegra_gmi *gmi = dev_get_drvdata(dev);
+ u32 config;
+
+ /* stop GMI operation */
+ config = readl(gmi->base + TEGRA_GMI_CONFIG);
+ config &= ~TEGRA_GMI_CONFIG_GO;
+ writel(config, gmi->base + TEGRA_GMI_CONFIG);
+
+ reset_control_assert(gmi->rst);
+
+ clk_disable_unprepare(gmi->clk);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra_gmi_pm = {
+ SET_RUNTIME_PM_OPS(tegra_gmi_runtime_suspend, tegra_gmi_runtime_resume,
+ NULL)
+};
+
static const struct of_device_id tegra_gmi_id_table[] = {
{ .compatible = "nvidia,tegra20-gmi", },
{ .compatible = "nvidia,tegra30-gmi", },@@ -275,6 +318,7 @@ static struct platform_driver tegra_gmi_driver = {
.driver = {
.name = "tegra-gmi",
.of_match_table = tegra_gmi_id_table,
+ .pm = &tegra_gmi_pm,
},
};
module_platform_driver(tegra_gmi_driver);--
2.32.0