On Wed, 18 Apr 2018 17:37:53 +0200
Fabrice Gasnier <fabrice.gasn...@st.com> 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.gasn...@st.com>
Looks good to me.

Applied to the togreg branch of iio.git and pushed out as testing for the
autobuilders to play with it.

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);

Reply via email to