diff options
Diffstat (limited to 'drivers/iio/adc/stm32-adc.c')
-rw-r--r-- | drivers/iio/adc/stm32-adc.c | 303 |
1 files changed, 217 insertions, 86 deletions
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 378411853d75..205e1699f954 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -22,6 +22,7 @@ #include <linux/iopoll.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/of.h> #include <linux/of_device.h> @@ -148,6 +149,7 @@ enum stm32h7_adc_dmngt { #define STM32_ADC_MAX_SMP 7 /* SMPx range is [0..7] */ #define STM32_ADC_TIMEOUT_US 100000 #define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000)) +#define STM32_ADC_HW_STOP_DELAY_MS 100 #define STM32_DMA_BUFFER_SIZE PAGE_SIZE @@ -199,11 +201,13 @@ struct stm32_adc_trig_info { * @calfact_s: Calibration offset for single ended channels * @calfact_d: Calibration offset in differential * @lincalfact: Linearity calibration factor + * @calibrated: Indicates calibration status */ struct stm32_adc_calib { u32 calfact_s; u32 calfact_d; u32 lincalfact[STM32H7_LINCALFACT_NUM]; + bool calibrated; }; /** @@ -251,7 +255,6 @@ struct stm32_adc; * @trigs: external trigger sources * @clk_required: clock is required * @has_vregready: vregready status flag presence - * @selfcalib: optional routine for self-calibration * @prepare: optional prepare routine (power-up, enable) * @start_conv: routine to start conversions * @stop_conv: routine to stop conversions @@ -264,7 +267,6 @@ struct stm32_adc_cfg { struct stm32_adc_trig_info *trigs; bool clk_required; bool has_vregready; - int (*selfcalib)(struct stm32_adc *); int (*prepare)(struct stm32_adc *); void (*start_conv)(struct stm32_adc *, bool dma); void (*stop_conv)(struct stm32_adc *); @@ -623,6 +625,47 @@ static void stm32_adc_set_res(struct stm32_adc *adc) stm32_adc_writel(adc, res->reg, val); } +static int stm32_adc_hw_stop(struct device *dev) +{ + struct stm32_adc *adc = dev_get_drvdata(dev); + + if (adc->cfg->unprepare) + adc->cfg->unprepare(adc); + + if (adc->clk) + clk_disable_unprepare(adc->clk); + + return 0; +} + +static int stm32_adc_hw_start(struct device *dev) +{ + struct stm32_adc *adc = dev_get_drvdata(dev); + int ret; + + if (adc->clk) { + ret = clk_prepare_enable(adc->clk); + if (ret) + return ret; + } + + stm32_adc_set_res(adc); + + if (adc->cfg->prepare) { + ret = adc->cfg->prepare(adc); + if (ret) + goto err_clk_dis; + } + + return 0; + +err_clk_dis: + if (adc->clk) + clk_disable_unprepare(adc->clk); + + return ret; +} + /** * stm32f4_adc_start_conv() - Start conversions for regular channels. * @adc: stm32 adc instance @@ -777,6 +820,7 @@ static void stm32h7_adc_disable(struct stm32_adc *adc) /** * stm32h7_adc_read_selfcalib() - read calibration shadow regs, save result * @adc: stm32 adc instance + * Note: Must be called once ADC is enabled, so LINCALRDYW[1..6] are writable */ static int stm32h7_adc_read_selfcalib(struct stm32_adc *adc) { @@ -784,11 +828,6 @@ static int stm32h7_adc_read_selfcalib(struct stm32_adc *adc) int i, ret; u32 lincalrdyw_mask, val; - /* Enable adc so LINCALRDYW1..6 bits are writable */ - ret = stm32h7_adc_enable(adc); - if (ret) - return ret; - /* Read linearity calibration */ lincalrdyw_mask = STM32H7_LINCALRDYW6; for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) { @@ -801,7 +840,7 @@ static int stm32h7_adc_read_selfcalib(struct stm32_adc *adc) 100, STM32_ADC_TIMEOUT_US); if (ret) { dev_err(&indio_dev->dev, "Failed to read calfact\n"); - goto disable; + return ret; } val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT2); @@ -817,11 +856,9 @@ static int stm32h7_adc_read_selfcalib(struct stm32_adc *adc) adc->cal.calfact_s >>= STM32H7_CALFACT_S_SHIFT; adc->cal.calfact_d = (val & STM32H7_CALFACT_D_MASK); adc->cal.calfact_d >>= STM32H7_CALFACT_D_SHIFT; + adc->cal.calibrated = true; -disable: - stm32h7_adc_disable(adc); - - return ret; + return 0; } /** @@ -898,9 +935,9 @@ static int stm32h7_adc_restore_selfcalib(struct stm32_adc *adc) #define STM32H7_ADC_CALIB_TIMEOUT_US 100000 /** - * stm32h7_adc_selfcalib() - Procedure to calibrate ADC (from power down) + * stm32h7_adc_selfcalib() - Procedure to calibrate ADC * @adc: stm32 adc instance - * Exit from power down, calibrate ADC, then return to power down. + * Note: Must be called once ADC is out of power down. */ static int stm32h7_adc_selfcalib(struct stm32_adc *adc) { @@ -908,9 +945,8 @@ static int stm32h7_adc_selfcalib(struct stm32_adc *adc) int ret; u32 val; - ret = stm32h7_adc_exit_pwr_down(adc); - if (ret) - return ret; + if (adc->cal.calibrated) + return true; /* * Select calibration mode: @@ -927,7 +963,7 @@ static int stm32h7_adc_selfcalib(struct stm32_adc *adc) STM32H7_ADC_CALIB_TIMEOUT_US); if (ret) { dev_err(&indio_dev->dev, "calibration failed\n"); - goto pwr_dwn; + goto out; } /* @@ -944,18 +980,13 @@ static int stm32h7_adc_selfcalib(struct stm32_adc *adc) STM32H7_ADC_CALIB_TIMEOUT_US); if (ret) { dev_err(&indio_dev->dev, "calibration failed\n"); - goto pwr_dwn; + goto out; } +out: stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALDIF | STM32H7_ADCALLIN); - /* Read calibration result for future reference */ - ret = stm32h7_adc_read_selfcalib(adc); - -pwr_dwn: - stm32h7_adc_enter_pwr_down(adc); - return ret; } @@ -972,19 +1003,28 @@ pwr_dwn: */ static int stm32h7_adc_prepare(struct stm32_adc *adc) { - int ret; + int calib, ret; ret = stm32h7_adc_exit_pwr_down(adc); if (ret) return ret; + ret = stm32h7_adc_selfcalib(adc); + if (ret < 0) + goto pwr_dwn; + calib = ret; + stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel); ret = stm32h7_adc_enable(adc); if (ret) goto pwr_dwn; - ret = stm32h7_adc_restore_selfcalib(adc); + /* Either restore or read calibration result for future reference */ + if (calib) + ret = stm32h7_adc_restore_selfcalib(adc); + else + ret = stm32h7_adc_read_selfcalib(adc); if (ret) goto disable; @@ -1174,6 +1214,7 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev, int *res) { struct stm32_adc *adc = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; const struct stm32_adc_regspec *regs = adc->cfg->regs; long timeout; u32 val; @@ -1183,10 +1224,10 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev, adc->bufi = 0; - if (adc->cfg->prepare) { - ret = adc->cfg->prepare(adc); - if (ret) - return ret; + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + return ret; } /* Apply sampling time settings */ @@ -1224,8 +1265,8 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev, stm32_adc_conv_irq_disable(adc); - if (adc->cfg->unprepare) - adc->cfg->unprepare(adc); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); return ret; } @@ -1333,15 +1374,22 @@ static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *scan_mask) { struct stm32_adc *adc = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; int ret; + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + return ret; + } + adc->num_conv = bitmap_weight(scan_mask, indio_dev->masklength); ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask); - if (ret) - return ret; + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); - return 0; + return ret; } static int stm32_adc_of_xlate(struct iio_dev *indio_dev, @@ -1371,12 +1419,23 @@ static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev, unsigned *readval) { struct stm32_adc *adc = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + return ret; + } if (!readval) stm32_adc_writel(adc, reg, writeval); else *readval = stm32_adc_readl(adc, reg); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return 0; } @@ -1459,21 +1518,22 @@ static int stm32_adc_dma_start(struct iio_dev *indio_dev) return 0; } -static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev) +static int __stm32_adc_buffer_postenable(struct iio_dev *indio_dev) { struct stm32_adc *adc = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; int ret; - if (adc->cfg->prepare) { - ret = adc->cfg->prepare(adc); - if (ret) - return ret; + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + return ret; } ret = stm32_adc_set_trig(indio_dev, indio_dev->trig); if (ret) { dev_err(&indio_dev->dev, "Can't set trigger\n"); - goto err_unprepare; + goto err_pm_put; } ret = stm32_adc_dma_start(indio_dev); @@ -1482,10 +1542,6 @@ static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev) goto err_clr_trig; } - ret = iio_triggered_buffer_postenable(indio_dev); - if (ret < 0) - goto err_stop_dma; - /* Reset adc buffer index */ adc->bufi = 0; @@ -1496,39 +1552,58 @@ static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev) return 0; -err_stop_dma: - if (adc->dma_chan) - dmaengine_terminate_all(adc->dma_chan); err_clr_trig: stm32_adc_set_trig(indio_dev, NULL); -err_unprepare: - if (adc->cfg->unprepare) - adc->cfg->unprepare(adc); +err_pm_put: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); return ret; } -static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev) +static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev) { - struct stm32_adc *adc = iio_priv(indio_dev); int ret; + ret = iio_triggered_buffer_postenable(indio_dev); + if (ret < 0) + return ret; + + ret = __stm32_adc_buffer_postenable(indio_dev); + if (ret < 0) + iio_triggered_buffer_predisable(indio_dev); + + return ret; +} + +static void __stm32_adc_buffer_predisable(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + adc->cfg->stop_conv(adc); if (!adc->dma_chan) stm32_adc_conv_irq_disable(adc); - ret = iio_triggered_buffer_predisable(indio_dev); - if (ret < 0) - dev_err(&indio_dev->dev, "predisable failed\n"); - if (adc->dma_chan) dmaengine_terminate_all(adc->dma_chan); if (stm32_adc_set_trig(indio_dev, NULL)) dev_err(&indio_dev->dev, "Can't clear trigger\n"); - if (adc->cfg->unprepare) - adc->cfg->unprepare(adc); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} + +static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev) +{ + int ret; + + __stm32_adc_buffer_predisable(indio_dev); + + ret = iio_triggered_buffer_predisable(indio_dev); + if (ret < 0) + dev_err(&indio_dev->dev, "predisable failed\n"); return ret; } @@ -1867,32 +1942,17 @@ static int stm32_adc_probe(struct platform_device *pdev) } } - if (adc->clk) { - ret = clk_prepare_enable(adc->clk); - if (ret < 0) { - dev_err(&pdev->dev, "clk enable failed\n"); - return ret; - } - } - ret = stm32_adc_of_get_resolution(indio_dev); if (ret < 0) - goto err_clk_disable; - stm32_adc_set_res(adc); - - if (adc->cfg->selfcalib) { - ret = adc->cfg->selfcalib(adc); - if (ret) - goto err_clk_disable; - } + return ret; ret = stm32_adc_chan_of_init(indio_dev); if (ret < 0) - goto err_clk_disable; + return ret; ret = stm32_adc_dma_request(indio_dev); if (ret < 0) - goto err_clk_disable; + return ret; ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, @@ -1903,15 +1963,35 @@ static int stm32_adc_probe(struct platform_device *pdev) goto err_dma_disable; } + /* Get stm32-adc-core PM online */ + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_set_autosuspend_delay(dev, STM32_ADC_HW_STOP_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + + ret = stm32_adc_hw_start(dev); + if (ret) + goto err_buffer_cleanup; + ret = iio_device_register(indio_dev); if (ret) { dev_err(&pdev->dev, "iio dev register failed\n"); - goto err_buffer_cleanup; + goto err_hw_stop; } + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return 0; +err_hw_stop: + stm32_adc_hw_stop(dev); + err_buffer_cleanup: + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); iio_triggered_buffer_cleanup(indio_dev); err_dma_disable: @@ -1921,9 +2001,6 @@ err_dma_disable: adc->rx_buf, adc->rx_dma_buf); dma_release_channel(adc->dma_chan); } -err_clk_disable: - if (adc->clk) - clk_disable_unprepare(adc->clk); return ret; } @@ -1933,7 +2010,12 @@ static int stm32_adc_remove(struct platform_device *pdev) struct stm32_adc *adc = platform_get_drvdata(pdev); struct iio_dev *indio_dev = iio_priv_to_dev(adc); + pm_runtime_get_sync(&pdev->dev); iio_device_unregister(indio_dev); + stm32_adc_hw_stop(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); iio_triggered_buffer_cleanup(indio_dev); if (adc->dma_chan) { dma_free_coherent(adc->dma_chan->device->dev, @@ -1941,12 +2023,62 @@ static int stm32_adc_remove(struct platform_device *pdev) adc->rx_buf, adc->rx_dma_buf); dma_release_channel(adc->dma_chan); } - if (adc->clk) - clk_disable_unprepare(adc->clk); return 0; } +#if defined(CONFIG_PM_SLEEP) +static int stm32_adc_suspend(struct device *dev) +{ + struct stm32_adc *adc = dev_get_drvdata(dev); + struct iio_dev *indio_dev = iio_priv_to_dev(adc); + + if (iio_buffer_enabled(indio_dev)) + __stm32_adc_buffer_predisable(indio_dev); + + return pm_runtime_force_suspend(dev); +} + +static int stm32_adc_resume(struct device *dev) +{ + struct stm32_adc *adc = dev_get_drvdata(dev); + struct iio_dev *indio_dev = iio_priv_to_dev(adc); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret < 0) + return ret; + + if (!iio_buffer_enabled(indio_dev)) + return 0; + + ret = stm32_adc_update_scan_mode(indio_dev, + indio_dev->active_scan_mask); + if (ret < 0) + return ret; + + return __stm32_adc_buffer_postenable(indio_dev); +} +#endif + +#if defined(CONFIG_PM) +static int stm32_adc_runtime_suspend(struct device *dev) +{ + return stm32_adc_hw_stop(dev); +} + +static int stm32_adc_runtime_resume(struct device *dev) +{ + return stm32_adc_hw_start(dev); +} +#endif + +static const struct dev_pm_ops stm32_adc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(stm32_adc_suspend, stm32_adc_resume) + SET_RUNTIME_PM_OPS(stm32_adc_runtime_suspend, stm32_adc_runtime_resume, + NULL) +}; + static const struct stm32_adc_cfg stm32f4_adc_cfg = { .regs = &stm32f4_adc_regspec, .adc_info = &stm32f4_adc_info, @@ -1961,7 +2093,6 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = { .regs = &stm32h7_adc_regspec, .adc_info = &stm32h7_adc_info, .trigs = stm32h7_adc_trigs, - .selfcalib = stm32h7_adc_selfcalib, .start_conv = stm32h7_adc_start_conv, .stop_conv = stm32h7_adc_stop_conv, .prepare = stm32h7_adc_prepare, @@ -1974,7 +2105,6 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = { .adc_info = &stm32h7_adc_info, .trigs = stm32h7_adc_trigs, .has_vregready = true, - .selfcalib = stm32h7_adc_selfcalib, .start_conv = stm32h7_adc_start_conv, .stop_conv = stm32h7_adc_stop_conv, .prepare = stm32h7_adc_prepare, @@ -1996,6 +2126,7 @@ static struct platform_driver stm32_adc_driver = { .driver = { .name = "stm32-adc", .of_match_table = stm32_adc_of_match, + .pm = &stm32_adc_pm_ops, }, }; module_platform_driver(stm32_adc_driver); |