diff options
Diffstat (limited to 'drivers/iio/dac/stm32-dac-core.c')
| -rw-r--r-- | drivers/iio/dac/stm32-dac-core.c | 138 | 
1 files changed, 107 insertions, 31 deletions
diff --git a/drivers/iio/dac/stm32-dac-core.c b/drivers/iio/dac/stm32-dac-core.c index d0fb3124de07..9e6b4cd0a5cc 100644 --- a/drivers/iio/dac/stm32-dac-core.c +++ b/drivers/iio/dac/stm32-dac-core.c @@ -11,6 +11,7 @@  #include <linux/delay.h>  #include <linux/module.h>  #include <linux/of_platform.h> +#include <linux/pm_runtime.h>  #include <linux/regulator/consumer.h>  #include <linux/reset.h> @@ -50,6 +51,41 @@ static const struct regmap_config stm32_dac_regmap_cfg = {  	.max_register = 0x3fc,  }; +static int stm32_dac_core_hw_start(struct device *dev) +{ +	struct stm32_dac_common *common = dev_get_drvdata(dev); +	struct stm32_dac_priv *priv = to_stm32_dac_priv(common); +	int ret; + +	ret = regulator_enable(priv->vref); +	if (ret < 0) { +		dev_err(dev, "vref enable failed: %d\n", ret); +		return ret; +	} + +	ret = clk_prepare_enable(priv->pclk); +	if (ret < 0) { +		dev_err(dev, "pclk enable failed: %d\n", ret); +		goto err_regulator_disable; +	} + +	return 0; + +err_regulator_disable: +	regulator_disable(priv->vref); + +	return ret; +} + +static void stm32_dac_core_hw_stop(struct device *dev) +{ +	struct stm32_dac_common *common = dev_get_drvdata(dev); +	struct stm32_dac_priv *priv = to_stm32_dac_priv(common); + +	clk_disable_unprepare(priv->pclk); +	regulator_disable(priv->vref); +} +  static int stm32_dac_probe(struct platform_device *pdev)  {  	struct device *dev = &pdev->dev; @@ -66,6 +102,8 @@ static int stm32_dac_probe(struct platform_device *pdev)  	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);  	if (!priv)  		return -ENOMEM; +	platform_set_drvdata(pdev, &priv->common); +  	cfg = (const struct stm32_dac_cfg *)  		of_match_device(dev->driver->of_match_table, dev)->data; @@ -74,11 +112,19 @@ static int stm32_dac_probe(struct platform_device *pdev)  	if (IS_ERR(mmio))  		return PTR_ERR(mmio); -	regmap = devm_regmap_init_mmio(dev, mmio, &stm32_dac_regmap_cfg); +	regmap = devm_regmap_init_mmio_clk(dev, "pclk", mmio, +					   &stm32_dac_regmap_cfg);  	if (IS_ERR(regmap))  		return PTR_ERR(regmap);  	priv->common.regmap = regmap; +	priv->pclk = devm_clk_get(dev, "pclk"); +	if (IS_ERR(priv->pclk)) { +		ret = PTR_ERR(priv->pclk); +		dev_err(dev, "pclk get failed\n"); +		return ret; +	} +  	priv->vref = devm_regulator_get(dev, "vref");  	if (IS_ERR(priv->vref)) {  		ret = PTR_ERR(priv->vref); @@ -86,33 +132,22 @@ static int stm32_dac_probe(struct platform_device *pdev)  		return ret;  	} -	ret = regulator_enable(priv->vref); -	if (ret < 0) { -		dev_err(dev, "vref enable failed\n"); -		return ret; -	} +	pm_runtime_get_noresume(dev); +	pm_runtime_set_active(dev); +	pm_runtime_enable(dev); + +	ret = stm32_dac_core_hw_start(dev); +	if (ret) +		goto err_pm_stop;  	ret = regulator_get_voltage(priv->vref);  	if (ret < 0) {  		dev_err(dev, "vref get voltage failed, %d\n", ret); -		goto err_vref; +		goto err_hw_stop;  	}  	priv->common.vref_mv = ret / 1000;  	dev_dbg(dev, "vref+=%dmV\n", priv->common.vref_mv); -	priv->pclk = devm_clk_get(dev, "pclk"); -	if (IS_ERR(priv->pclk)) { -		ret = PTR_ERR(priv->pclk); -		dev_err(dev, "pclk get failed\n"); -		goto err_vref; -	} - -	ret = clk_prepare_enable(priv->pclk); -	if (ret < 0) { -		dev_err(dev, "pclk enable failed\n"); -		goto err_vref; -	} -  	priv->rst = devm_reset_control_get_exclusive(dev, NULL);  	if (!IS_ERR(priv->rst)) {  		reset_control_assert(priv->rst); @@ -128,39 +163,79 @@ static int stm32_dac_probe(struct platform_device *pdev)  					 priv->common.hfsel ?  					 STM32H7_DAC_CR_HFSEL : 0);  		if (ret) -			goto err_pclk; +			goto err_hw_stop;  	} -	platform_set_drvdata(pdev, &priv->common);  	ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, dev);  	if (ret < 0) {  		dev_err(dev, "failed to populate DT children\n"); -		goto err_pclk; +		goto err_hw_stop;  	} +	pm_runtime_put(dev); +  	return 0; -err_pclk: -	clk_disable_unprepare(priv->pclk); -err_vref: -	regulator_disable(priv->vref); +err_hw_stop: +	stm32_dac_core_hw_stop(dev); +err_pm_stop: +	pm_runtime_disable(dev); +	pm_runtime_set_suspended(dev); +	pm_runtime_put_noidle(dev);  	return ret;  }  static int stm32_dac_remove(struct platform_device *pdev)  { -	struct stm32_dac_common *common = platform_get_drvdata(pdev); +	pm_runtime_get_sync(&pdev->dev); +	of_platform_depopulate(&pdev->dev); +	stm32_dac_core_hw_stop(&pdev->dev); +	pm_runtime_disable(&pdev->dev); +	pm_runtime_set_suspended(&pdev->dev); +	pm_runtime_put_noidle(&pdev->dev); + +	return 0; +} + +static int __maybe_unused stm32_dac_core_resume(struct device *dev) +{ +	struct stm32_dac_common *common = dev_get_drvdata(dev);  	struct stm32_dac_priv *priv = to_stm32_dac_priv(common); +	int ret; -	of_platform_depopulate(&pdev->dev); -	clk_disable_unprepare(priv->pclk); -	regulator_disable(priv->vref); +	if (priv->common.hfsel) { +		/* restore hfsel (maybe lost under low power state) */ +		ret = regmap_update_bits(priv->common.regmap, STM32_DAC_CR, +					 STM32H7_DAC_CR_HFSEL, +					 STM32H7_DAC_CR_HFSEL); +		if (ret) +			return ret; +	} + +	return pm_runtime_force_resume(dev); +} + +static int __maybe_unused stm32_dac_core_runtime_suspend(struct device *dev) +{ +	stm32_dac_core_hw_stop(dev);  	return 0;  } +static int __maybe_unused stm32_dac_core_runtime_resume(struct device *dev) +{ +	return stm32_dac_core_hw_start(dev); +} + +static const struct dev_pm_ops stm32_dac_core_pm_ops = { +	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, stm32_dac_core_resume) +	SET_RUNTIME_PM_OPS(stm32_dac_core_runtime_suspend, +			   stm32_dac_core_runtime_resume, +			   NULL) +}; +  static const struct stm32_dac_cfg stm32h7_dac_cfg = {  	.has_hfsel = true,  }; @@ -182,6 +257,7 @@ static struct platform_driver stm32_dac_driver = {  	.driver = {  		.name = "stm32-dac-core",  		.of_match_table = stm32_dac_of_match, +		.pm = &stm32_dac_core_pm_ops,  	},  };  module_platform_driver(stm32_dac_driver);  |