diff options
author | Fabrice Gasnier <fabrice.gasnier@st.com> | 2019-02-28 11:45:18 +0100 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2019-02-28 14:20:48 +0000 |
commit | be5295f8a15fee6d92e78c822e9960c42638e32a (patch) | |
tree | 941ffbf4a678c185f14165df810ecd13ac777922 /drivers/regulator/stm32-vrefbuf.c | |
parent | 0e819b51fb9b2558d7cb85400a47370a631325dc (diff) |
regulator: stm32-vrefbuf: add power management support
Add support for suspend/resume and runtime PM to stm32-vrefbuf driver.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'drivers/regulator/stm32-vrefbuf.c')
-rw-r--r-- | drivers/regulator/stm32-vrefbuf.c | 121 |
1 files changed, 114 insertions, 7 deletions
diff --git a/drivers/regulator/stm32-vrefbuf.c b/drivers/regulator/stm32-vrefbuf.c index e0a9c445ed67..ba2f24949dc9 100644 --- a/drivers/regulator/stm32-vrefbuf.c +++ b/drivers/regulator/stm32-vrefbuf.c @@ -15,6 +15,7 @@ #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/of_regulator.h> +#include <linux/pm_runtime.h> /* STM32 VREFBUF registers */ #define STM32_VREFBUF_CSR 0x00 @@ -25,9 +26,12 @@ #define STM32_HIZ BIT(1) #define STM32_ENVR BIT(0) +#define STM32_VREFBUF_AUTO_SUSPEND_DELAY_MS 10 + struct stm32_vrefbuf { void __iomem *base; struct clk *clk; + struct device *dev; }; static const unsigned int stm32_vrefbuf_voltages[] = { @@ -38,9 +42,16 @@ static const unsigned int stm32_vrefbuf_voltages[] = { static int stm32_vrefbuf_enable(struct regulator_dev *rdev) { struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev); - u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); + u32 val; int ret; + ret = pm_runtime_get_sync(priv->dev); + if (ret < 0) { + pm_runtime_put_noidle(priv->dev); + return ret; + } + + val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); val = (val & ~STM32_HIZ) | STM32_ENVR; writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); @@ -59,45 +70,95 @@ static int stm32_vrefbuf_enable(struct regulator_dev *rdev) writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); } + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); + return ret; } static int stm32_vrefbuf_disable(struct regulator_dev *rdev) { struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev); - u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); + u32 val; + int ret; + ret = pm_runtime_get_sync(priv->dev); + if (ret < 0) { + pm_runtime_put_noidle(priv->dev); + return ret; + } + + val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); val = (val & ~STM32_ENVR) | STM32_HIZ; writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); + return 0; } static int stm32_vrefbuf_is_enabled(struct regulator_dev *rdev) { struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev); + int ret; + + ret = pm_runtime_get_sync(priv->dev); + if (ret < 0) { + pm_runtime_put_noidle(priv->dev); + return ret; + } + + ret = readl_relaxed(priv->base + STM32_VREFBUF_CSR) & STM32_ENVR; - return readl_relaxed(priv->base + STM32_VREFBUF_CSR) & STM32_ENVR; + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); + + return ret; } static int stm32_vrefbuf_set_voltage_sel(struct regulator_dev *rdev, unsigned sel) { struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev); - u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); + u32 val; + int ret; + ret = pm_runtime_get_sync(priv->dev); + if (ret < 0) { + pm_runtime_put_noidle(priv->dev); + return ret; + } + + val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); val = (val & ~STM32_VRS) | FIELD_PREP(STM32_VRS, sel); writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); + return 0; } static int stm32_vrefbuf_get_voltage_sel(struct regulator_dev *rdev) { struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev); - u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); + u32 val; + int ret; - return FIELD_GET(STM32_VRS, val); + ret = pm_runtime_get_sync(priv->dev); + if (ret < 0) { + pm_runtime_put_noidle(priv->dev); + return ret; + } + + val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); + ret = FIELD_GET(STM32_VRS, val); + + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); + + return ret; } static const struct regulator_ops stm32_vrefbuf_volt_ops = { @@ -130,6 +191,7 @@ static int stm32_vrefbuf_probe(struct platform_device *pdev) priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + priv->dev = &pdev->dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->base = devm_ioremap_resource(&pdev->dev, res); @@ -140,10 +202,17 @@ static int stm32_vrefbuf_probe(struct platform_device *pdev) if (IS_ERR(priv->clk)) return PTR_ERR(priv->clk); + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, + STM32_VREFBUF_AUTO_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + ret = clk_prepare_enable(priv->clk); if (ret) { dev_err(&pdev->dev, "clk prepare failed with error %d\n", ret); - return ret; + goto err_pm_stop; } config.dev = &pdev->dev; @@ -161,10 +230,17 @@ static int stm32_vrefbuf_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, rdev); + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + return 0; err_clk_dis: clk_disable_unprepare(priv->clk); +err_pm_stop: + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); return ret; } @@ -174,12 +250,42 @@ static int stm32_vrefbuf_remove(struct platform_device *pdev) struct regulator_dev *rdev = platform_get_drvdata(pdev); struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev); + pm_runtime_get_sync(&pdev->dev); regulator_unregister(rdev); clk_disable_unprepare(priv->clk); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); return 0; }; +static int __maybe_unused stm32_vrefbuf_runtime_suspend(struct device *dev) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int __maybe_unused stm32_vrefbuf_runtime_resume(struct device *dev) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev); + + return clk_prepare_enable(priv->clk); +} + +static const struct dev_pm_ops stm32_vrefbuf_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(stm32_vrefbuf_runtime_suspend, + stm32_vrefbuf_runtime_resume, + NULL) +}; + static const struct of_device_id stm32_vrefbuf_of_match[] = { { .compatible = "st,stm32-vrefbuf", }, {}, @@ -192,6 +298,7 @@ static struct platform_driver stm32_vrefbuf_driver = { .driver = { .name = "stm32-vrefbuf", .of_match_table = of_match_ptr(stm32_vrefbuf_of_match), + .pm = &stm32_vrefbuf_pm_ops, }, }; module_platform_driver(stm32_vrefbuf_driver); |