mtd: rawnand: tegra: Add runtime PM and OPP support
authorDmitry Osipenko <digetx@gmail.com>
Tue, 30 Nov 2021 23:23:30 +0000 (02:23 +0300)
committerThierry Reding <treding@nvidia.com>
Thu, 16 Dec 2021 13:05:24 +0000 (14:05 +0100)
The NAND on Tegra belongs to the core power domain and we're going to
enable GENPD support for the core domain. Now NAND must be resumed using
runtime PM API in order to initialize the NAND power state. Add runtime PM
and OPP support to the NAND driver.

Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
drivers/mtd/nand/raw/tegra_nand.c

index 32431bbe69b8e0396b17e5fae34f15ed711a281f..b36e5260ae274a165e8b30d08d9bf270d15133ea 100644 (file)
 #include <linux/mtd/rawnand.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/reset.h>
 
+#include <soc/tegra/common.h>
+
 #define COMMAND                                        0x00
 #define   COMMAND_GO                           BIT(31)
 #define   COMMAND_CLE                          BIT(30)
@@ -1151,6 +1154,7 @@ static int tegra_nand_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        ctrl->dev = &pdev->dev;
+       platform_set_drvdata(pdev, ctrl);
        nand_controller_init(&ctrl->controller);
        ctrl->controller.ops = &tegra_nand_controller_ops;
 
@@ -1166,14 +1170,23 @@ static int tegra_nand_probe(struct platform_device *pdev)
        if (IS_ERR(ctrl->clk))
                return PTR_ERR(ctrl->clk);
 
-       err = clk_prepare_enable(ctrl->clk);
+       err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
+       if (err)
+               return err;
+
+       /*
+        * This driver doesn't support active power management yet,
+        * so we will simply keep device resumed.
+        */
+       pm_runtime_enable(&pdev->dev);
+       err = pm_runtime_resume_and_get(&pdev->dev);
        if (err)
                return err;
 
        err = reset_control_reset(rst);
        if (err) {
                dev_err(ctrl->dev, "Failed to reset HW: %d\n", err);
-               goto err_disable_clk;
+               goto err_put_pm;
        }
 
        writel_relaxed(HWSTATUS_CMD_DEFAULT, ctrl->regs + HWSTATUS_CMD);
@@ -1188,21 +1201,20 @@ static int tegra_nand_probe(struct platform_device *pdev)
                               dev_name(&pdev->dev), ctrl);
        if (err) {
                dev_err(ctrl->dev, "Failed to get IRQ: %d\n", err);
-               goto err_disable_clk;
+               goto err_put_pm;
        }
 
        writel_relaxed(DMA_MST_CTRL_IS_DONE, ctrl->regs + DMA_MST_CTRL);
 
        err = tegra_nand_chips_init(ctrl->dev, ctrl);
        if (err)
-               goto err_disable_clk;
-
-       platform_set_drvdata(pdev, ctrl);
+               goto err_put_pm;
 
        return 0;
 
-err_disable_clk:
-       clk_disable_unprepare(ctrl->clk);
+err_put_pm:
+       pm_runtime_put_sync_suspend(ctrl->dev);
+       pm_runtime_force_suspend(ctrl->dev);
        return err;
 }
 
@@ -1219,11 +1231,40 @@ static int tegra_nand_remove(struct platform_device *pdev)
 
        nand_cleanup(chip);
 
+       pm_runtime_put_sync_suspend(ctrl->dev);
+       pm_runtime_force_suspend(ctrl->dev);
+
+       return 0;
+}
+
+static int __maybe_unused tegra_nand_runtime_resume(struct device *dev)
+{
+       struct tegra_nand_controller *ctrl = dev_get_drvdata(dev);
+       int err;
+
+       err = clk_prepare_enable(ctrl->clk);
+       if (err) {
+               dev_err(dev, "Failed to enable clock: %d\n", err);
+               return err;
+       }
+
+       return 0;
+}
+
+static int __maybe_unused tegra_nand_runtime_suspend(struct device *dev)
+{
+       struct tegra_nand_controller *ctrl = dev_get_drvdata(dev);
+
        clk_disable_unprepare(ctrl->clk);
 
        return 0;
 }
 
+static const struct dev_pm_ops tegra_nand_pm = {
+       SET_RUNTIME_PM_OPS(tegra_nand_runtime_suspend, tegra_nand_runtime_resume,
+                          NULL)
+};
+
 static const struct of_device_id tegra_nand_of_match[] = {
        { .compatible = "nvidia,tegra20-nand" },
        { /* sentinel */ }
@@ -1234,6 +1275,7 @@ static struct platform_driver tegra_nand_driver = {
        .driver = {
                .name = "tegra-nand",
                .of_match_table = tegra_nand_of_match,
+               .pm = &tegra_nand_pm,
        },
        .probe = tegra_nand_probe,
        .remove = tegra_nand_remove,