Re: [PATCH v2 2/3] iio: adc: stm32-adc: add support for STM32MP1
From: Jonathan Cameron
Date: Mon May 07 2018 - 13:21:55 EST
On Wed, 2 May 2018 09:44:50 +0200
Fabrice Gasnier <fabrice.gasnier@xxxxxx> wrote:
> Add support for STM32MP1 ADC. It's quite similar to STM32H7 ADC.
> Introduce new compatible to handle variants of this hardware such as
> vregready flag, trigger list, interrupts, clock rate.
>
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@xxxxxx>
Applied to the togreg branch of iio.git and pushed out as testing for the
autobuilders to play with it.
Thanks,
Jonathan
> ---
> drivers/iio/adc/stm32-adc-core.c | 66 +++++++++++++++++++++++++++++-----------
> drivers/iio/adc/stm32-adc.c | 47 +++++++++++++++++++++++++---
> 2 files changed, 91 insertions(+), 22 deletions(-)
>
> diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
> index 40be7d9..ca432e7 100644
> --- a/drivers/iio/adc/stm32-adc-core.c
> +++ b/drivers/iio/adc/stm32-adc-core.c
> @@ -34,9 +34,6 @@
> #define STM32F4_ADC_ADCPRE_SHIFT 16
> #define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
>
> -/* STM32 F4 maximum analog clock rate (from datasheet) */
> -#define STM32F4_ADC_MAX_CLK_RATE 36000000
> -
> /* STM32H7 - common registers for all ADC instances */
> #define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
> #define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08)
> @@ -51,9 +48,6 @@
> #define STM32H7_CKMODE_SHIFT 16
> #define STM32H7_CKMODE_MASK GENMASK(17, 16)
>
> -/* STM32 H7 maximum analog clock rate (from datasheet) */
> -#define STM32H7_ADC_MAX_CLK_RATE 36000000
> -
> /**
> * stm32_adc_common_regs - stm32 common registers, compatible dependent data
> * @csr: common status register offset
> @@ -74,15 +68,17 @@ struct stm32_adc_common_regs {
> * stm32_adc_priv_cfg - stm32 core compatible configuration data
> * @regs: common registers for all instances
> * @clk_sel: clock selection routine
> + * @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet)
> */
> struct stm32_adc_priv_cfg {
> const struct stm32_adc_common_regs *regs;
> int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *);
> + u32 max_clk_rate_hz;
> };
>
> /**
> * struct stm32_adc_priv - stm32 ADC core private data
> - * @irq: irq for ADC block
> + * @irq: irq(s) for ADC block
> * @domain: irq domain reference
> * @aclk: clock reference for the analog circuitry
> * @bclk: bus clock common for all ADCs, depends on part used
> @@ -91,7 +87,7 @@ struct stm32_adc_priv_cfg {
> * @common: common data for all ADC instances
> */
> struct stm32_adc_priv {
> - int irq;
> + int irq[STM32_ADC_MAX_ADCS];
> struct irq_domain *domain;
> struct clk *aclk;
> struct clk *bclk;
> @@ -133,7 +129,7 @@ static int stm32f4_adc_clk_sel(struct platform_device *pdev,
> }
>
> for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
> - if ((rate / stm32f4_pclk_div[i]) <= STM32F4_ADC_MAX_CLK_RATE)
> + if ((rate / stm32f4_pclk_div[i]) <= priv->cfg->max_clk_rate_hz)
> break;
> }
> if (i >= ARRAY_SIZE(stm32f4_pclk_div)) {
> @@ -222,7 +218,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev,
> if (ckmode)
> continue;
>
> - if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
> + if ((rate / div) <= priv->cfg->max_clk_rate_hz)
> goto out;
> }
> }
> @@ -242,7 +238,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev,
> if (!ckmode)
> continue;
>
> - if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
> + if ((rate / div) <= priv->cfg->max_clk_rate_hz)
> goto out;
> }
>
> @@ -328,11 +324,24 @@ static int stm32_adc_irq_probe(struct platform_device *pdev,
> struct stm32_adc_priv *priv)
> {
> struct device_node *np = pdev->dev.of_node;
> + unsigned int i;
> +
> + for (i = 0; i < STM32_ADC_MAX_ADCS; i++) {
> + priv->irq[i] = platform_get_irq(pdev, i);
> + if (priv->irq[i] < 0) {
> + /*
> + * At least one interrupt must be provided, make others
> + * optional:
> + * - stm32f4/h7 shares a common interrupt.
> + * - stm32mp1, has one line per ADC (either for ADC1,
> + * ADC2 or both).
> + */
> + if (i && priv->irq[i] == -ENXIO)
> + continue;
> + dev_err(&pdev->dev, "failed to get irq\n");
>
> - priv->irq = platform_get_irq(pdev, 0);
> - if (priv->irq < 0) {
> - dev_err(&pdev->dev, "failed to get irq\n");
> - return priv->irq;
> + return priv->irq[i];
> + }
> }
>
> priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0,
> @@ -343,8 +352,12 @@ static int stm32_adc_irq_probe(struct platform_device *pdev,
> return -ENOMEM;
> }
>
> - irq_set_chained_handler(priv->irq, stm32_adc_irq_handler);
> - irq_set_handler_data(priv->irq, priv);
> + for (i = 0; i < STM32_ADC_MAX_ADCS; i++) {
> + if (priv->irq[i] < 0)
> + continue;
> + irq_set_chained_handler(priv->irq[i], stm32_adc_irq_handler);
> + irq_set_handler_data(priv->irq[i], priv);
> + }
>
> return 0;
> }
> @@ -353,11 +366,17 @@ static void stm32_adc_irq_remove(struct platform_device *pdev,
> struct stm32_adc_priv *priv)
> {
> int hwirq;
> + unsigned int i;
>
> for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++)
> irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq));
> irq_domain_remove(priv->domain);
> - irq_set_chained_handler(priv->irq, NULL);
> +
> + for (i = 0; i < STM32_ADC_MAX_ADCS; i++) {
> + if (priv->irq[i] < 0)
> + continue;
> + irq_set_chained_handler(priv->irq[i], NULL);
> + }
> }
>
> static int stm32_adc_probe(struct platform_device *pdev)
> @@ -497,11 +516,19 @@ static int stm32_adc_remove(struct platform_device *pdev)
> static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = {
> .regs = &stm32f4_adc_common_regs,
> .clk_sel = stm32f4_adc_clk_sel,
> + .max_clk_rate_hz = 36000000,
> };
>
> static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = {
> .regs = &stm32h7_adc_common_regs,
> .clk_sel = stm32h7_adc_clk_sel,
> + .max_clk_rate_hz = 36000000,
> +};
> +
> +static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = {
> + .regs = &stm32h7_adc_common_regs,
> + .clk_sel = stm32h7_adc_clk_sel,
> + .max_clk_rate_hz = 40000000,
> };
>
> static const struct of_device_id stm32_adc_of_match[] = {
> @@ -512,6 +539,9 @@ static int stm32_adc_remove(struct platform_device *pdev)
> .compatible = "st,stm32h7-adc-core",
> .data = (void *)&stm32h7_adc_priv_cfg
> }, {
> + .compatible = "st,stm32mp1-adc-core",
> + .data = (void *)&stm32mp1_adc_priv_cfg
> + }, {
> },
> };
> MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> index 9a2583ca..3784118 100644
> --- a/drivers/iio/adc/stm32-adc.c
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -84,6 +84,7 @@
> #define STM32H7_ADC_CALFACT2 0xC8
>
> /* STM32H7_ADC_ISR - bit fields */
> +#define STM32MP1_VREGREADY BIT(12)
> #define STM32H7_EOC BIT(2)
> #define STM32H7_ADRDY BIT(0)
>
> @@ -249,6 +250,7 @@ struct stm32_adc_regspec {
> * @adc_info: per instance input channels definitions
> * @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
> @@ -261,6 +263,7 @@ struct stm32_adc_cfg {
> const struct stm32_adc_info *adc_info;
> 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);
> @@ -695,8 +698,12 @@ static void stm32h7_adc_stop_conv(struct stm32_adc *adc)
> stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR, STM32H7_DMNGT_MASK);
> }
>
> -static void stm32h7_adc_exit_pwr_down(struct stm32_adc *adc)
> +static int stm32h7_adc_exit_pwr_down(struct stm32_adc *adc)
> {
> + struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> + int ret;
> + u32 val;
> +
> /* Exit deep power down, then enable ADC voltage regulator */
> stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
> stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADVREGEN);
> @@ -705,7 +712,20 @@ static void stm32h7_adc_exit_pwr_down(struct stm32_adc *adc)
> stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
>
> /* Wait for startup time */
> - usleep_range(10, 20);
> + if (!adc->cfg->has_vregready) {
> + usleep_range(10, 20);
> + return 0;
> + }
> +
> + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_ISR, val,
> + val & STM32MP1_VREGREADY, 100,
> + STM32_ADC_TIMEOUT_US);
> + if (ret) {
> + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
> + dev_err(&indio_dev->dev, "Failed to exit power down\n");
> + }
> +
> + return ret;
> }
>
> static void stm32h7_adc_enter_pwr_down(struct stm32_adc *adc)
> @@ -888,7 +908,9 @@ static int stm32h7_adc_selfcalib(struct stm32_adc *adc)
> int ret;
> u32 val;
>
> - stm32h7_adc_exit_pwr_down(adc);
> + ret = stm32h7_adc_exit_pwr_down(adc);
> + if (ret)
> + return ret;
>
> /*
> * Select calibration mode:
> @@ -952,7 +974,10 @@ static int stm32h7_adc_prepare(struct stm32_adc *adc)
> {
> int ret;
>
> - stm32h7_adc_exit_pwr_down(adc);
> + ret = stm32h7_adc_exit_pwr_down(adc);
> + if (ret)
> + return ret;
> +
> stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel);
>
> ret = stm32h7_adc_enable(adc);
> @@ -1944,9 +1969,23 @@ static int stm32_adc_remove(struct platform_device *pdev)
> .smp_cycles = stm32h7_adc_smp_cycles,
> };
>
> +static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
> + .regs = &stm32h7_adc_regspec,
> + .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,
> + .unprepare = stm32h7_adc_unprepare,
> + .smp_cycles = stm32h7_adc_smp_cycles,
> +};
> +
> static const struct of_device_id stm32_adc_of_match[] = {
> { .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_cfg },
> { .compatible = "st,stm32h7-adc", .data = (void *)&stm32h7_adc_cfg },
> + { .compatible = "st,stm32mp1-adc", .data = (void *)&stm32mp1_adc_cfg },
> {},
> };
> MODULE_DEVICE_TABLE(of, stm32_adc_of_match);