diff mbox series

[3/8] iio: adc: stm32-adc: add stm32mp13 support

Message ID 20220928164114.48339-4-olivier.moysan@foss.st.com (mailing list archive)
State Changes Requested
Headers show
Series iio: stm32-adc: add support of adc for stm32mp13 | expand

Commit Message

Olivier Moysan Sept. 28, 2022, 4:41 p.m. UTC
Add STM32 ADC support for STM32MP13x SOCs family.

On STM32MP13x, each ADC peripheral has a single ADC block.
These ADC peripherals, ADC1 and ADC2, are fully independent.
This introduces changes in common registers handling.

Some features such as boost mode, channel preselection and
linear calibration are not supported by the STM32MP13x ADC.
Add diversity management for these features.

The STM32MP13x ADC introduces registers and bitfield variants
on existing features such as calibration factors and internal
channels. Add register diversity management.

Add also support of new internal channels VDDCPU and VDDQ_DDR.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
 drivers/iio/adc/stm32-adc-core.c |  21 +++
 drivers/iio/adc/stm32-adc-core.h |  32 +++++
 drivers/iio/adc/stm32-adc.c      | 212 +++++++++++++++++++++++++++----
 3 files changed, 237 insertions(+), 28 deletions(-)

Comments

Andy Shevchenko Sept. 28, 2022, 5:07 p.m. UTC | #1
On Wed, Sep 28, 2022 at 7:42 PM Olivier Moysan
<olivier.moysan@foss.st.com> wrote:
>
> Add STM32 ADC support for STM32MP13x SOCs family.
>
> On STM32MP13x, each ADC peripheral has a single ADC block.
> These ADC peripherals, ADC1 and ADC2, are fully independent.
> This introduces changes in common registers handling.
>
> Some features such as boost mode, channel preselection and
> linear calibration are not supported by the STM32MP13x ADC.
> Add diversity management for these features.
>
> The STM32MP13x ADC introduces registers and bitfield variants
> on existing features such as calibration factors and internal
> channels. Add register diversity management.
>
> Add also support of new internal channels VDDCPU and VDDQ_DDR.

for new

>
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
> ---
>  drivers/iio/adc/stm32-adc-core.c |  21 +++
>  drivers/iio/adc/stm32-adc-core.h |  32 +++++
>  drivers/iio/adc/stm32-adc.c      | 212 +++++++++++++++++++++++++++----
>  3 files changed, 237 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
> index 81d5db91c67b..6564aa61b595 100644
> --- a/drivers/iio/adc/stm32-adc-core.c
> +++ b/drivers/iio/adc/stm32-adc-core.c
> @@ -322,6 +322,16 @@ static const struct stm32_adc_common_regs stm32h7_adc_common_regs = {
>         .eocie_msk = STM32H7_EOCIE,
>  };
>
> +/* STM32MP13 common registers definitions */
> +static const struct stm32_adc_common_regs stm32mp13_adc_common_regs = {
> +       .csr = STM32H7_ADC_CSR,
> +       .ccr = STM32H7_ADC_CCR,
> +       .eoc_msk = { STM32H7_EOC_MST},
> +       .ovr_msk = { STM32H7_OVR_MST},
> +       .ier = STM32H7_ADC_IER,
> +       .eocie_msk = STM32H7_EOCIE,
> +};
> +
>  static const unsigned int stm32_adc_offset[STM32_ADC_MAX_ADCS] = {
>         0, STM32_ADC_OFFSET, STM32_ADC_OFFSET * 2,
>  };
> @@ -868,6 +878,14 @@ static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = {
>         .num_irqs = 2,
>  };
>
> +static const struct stm32_adc_priv_cfg stm32mp13_adc_priv_cfg = {
> +       .regs = &stm32mp13_adc_common_regs,
> +       .clk_sel = stm32h7_adc_clk_sel,
> +       .max_clk_rate_hz = 75000000,

75 * HZ_PER_MHZ ?

> +       .ipid = STM32MP13_IPIDR_NUMBER,
> +       .num_irqs = 1,
> +};
> +
>  static const struct of_device_id stm32_adc_of_match[] = {
>         {
>                 .compatible = "st,stm32f4-adc-core",
> @@ -878,6 +896,9 @@ static const struct of_device_id stm32_adc_of_match[] = {
>         }, {
>                 .compatible = "st,stm32mp1-adc-core",
>                 .data = (void *)&stm32mp1_adc_priv_cfg
> +       }, {
> +               .compatible = "st,stm32mp13-adc-core",
> +               .data = (void *)&stm32mp13_adc_priv_cfg
>         }, {
>         },
>  };
> diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
> index 2118ef63843d..658fef4308ac 100644
> --- a/drivers/iio/adc/stm32-adc-core.h
> +++ b/drivers/iio/adc/stm32-adc-core.h
> @@ -112,6 +112,11 @@
>  #define STM32MP1_ADC_IPDR              0x3F8
>  #define STM32MP1_ADC_SIDR              0x3FC
>
> +/* STM32MP13 - Registers for each ADC instance */
> +#define STM32MP13_ADC_DIFSEL           0xB0
> +#define STM32MP13_ADC_CALFACT          0xB4
> +#define STM32MP13_ADC2_OR              0xC8
> +
>  /* 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)
> @@ -161,6 +166,10 @@ enum stm32h7_adc_dmngt {
>         STM32H7_DMNGT_DMA_CIRC,         /* DMA circular mode */
>  };
>
> +/* STM32H7_ADC_DIFSEL - bit fields */
> +#define STM32H7_DIFSEL_SHIFT           0
> +#define STM32H7_DIFSEL_MASK            GENMASK(19, 0)
> +
>  /* STM32H7_ADC_CALFACT - bit fields */
>  #define STM32H7_CALFACT_D_SHIFT                16
>  #define STM32H7_CALFACT_D_MASK         GENMASK(26, 16)
> @@ -210,7 +219,30 @@ enum stm32h7_adc_dmngt {
>  /* STM32MP1_ADC_SIDR - bit fields */
>  #define STM32MP1_SIDR_MASK             GENMASK(31, 0)
>
> +/* STM32MP13_ADC_CFGR specific bit fields */
> +#define STM32MP13_DMAEN                        BIT(0)
> +#define STM32MP13_DMACFG               BIT(1)
> +#define STM32MP13_DFSDMCFG             BIT(2)
> +#define STM32MP13_RES_SHIFT            3
> +#define STM32MP13_RES_MASK             GENMASK(4, 3)
> +
> +/* STM32MP13_ADC_DIFSEL - bit fields */
> +#define STM32MP13_DIFSEL_SHIFT         0
> +#define STM32MP13_DIFSEL_MASK          GENMASK(18, 0)
> +
> +/* STM32MP13_ADC_CALFACT - bit fields */
> +#define STM32MP13_CALFACT_D_SHIFT      16
> +#define STM32MP13_CALFACT_D_MASK       GENMASK(22, 16)
> +#define STM32MP13_CALFACT_S_SHIFT      0
> +#define STM32MP13_CALFACT_S_MASK       GENMASK(6, 0)
> +
> +/* STM32MP13_ADC2_OR - bit fields */
> +#define STM32MP13_OP2                  BIT(2)
> +#define STM32MP13_OP1                  BIT(1)
> +#define STM32MP13_OP0                  BIT(0)
> +
>  #define STM32MP15_IPIDR_NUMBER         0x00110005
> +#define STM32MP13_IPIDR_NUMBER         0x00110006
>
>  /**
>   * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> index 3cda529f081d..362db64bbd69 100644
> --- a/drivers/iio/adc/stm32-adc.c
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -82,6 +82,8 @@ enum stm32_adc_extsel {
>  enum stm32_adc_int_ch {
>         STM32_ADC_INT_CH_NONE = -1,
>         STM32_ADC_INT_CH_VDDCORE,
> +       STM32_ADC_INT_CH_VDDCPU,
> +       STM32_ADC_INT_CH_VDDQ_DDR,
>         STM32_ADC_INT_CH_VREFINT,
>         STM32_ADC_INT_CH_VBAT,
>         STM32_ADC_INT_CH_NB,
> @@ -99,6 +101,8 @@ struct stm32_adc_ic {
>
>  static const struct stm32_adc_ic stm32_adc_ic[STM32_ADC_INT_CH_NB] = {
>         { "vddcore", STM32_ADC_INT_CH_VDDCORE },
> +       { "vddcpu", STM32_ADC_INT_CH_VDDCPU },
> +       { "vddq_ddr", STM32_ADC_INT_CH_VDDQ_DDR },
>         { "vrefint", STM32_ADC_INT_CH_VREFINT },
>         { "vbat", STM32_ADC_INT_CH_VBAT },
>  };
> @@ -160,9 +164,14 @@ struct stm32_adc_vrefint {
>   * @exten:             trigger control register & bitfield
>   * @extsel:            trigger selection register & bitfield
>   * @res:               resolution selection register & bitfield
> + * @difsel:            differential mode selection register & bitfield
> + * @calfact_s:         single-ended calibration factors register & bitfield
> + * @calfact_d:         differential calibration factors register & bitfield
>   * @smpr:              smpr1 & smpr2 registers offset array
>   * @smp_bits:          smpr1 & smpr2 index and bitfields
> - * @or_vdd:            option register & vddcore bitfield
> + * @or_vddcore:                option register & vddcore bitfield
> + * @or_vddcpu:         option register & vddcpu bitfield
> + * @or_vddq_ddr:       option register & vddq_ddr bitfield
>   * @ccr_vbat:          common register & vbat bitfield
>   * @ccr_vref:          common register & vrefint bitfield
>   */
> @@ -176,9 +185,14 @@ struct stm32_adc_regspec {
>         const struct stm32_adc_regs exten;
>         const struct stm32_adc_regs extsel;
>         const struct stm32_adc_regs res;
> +       const struct stm32_adc_regs difsel;
> +       const struct stm32_adc_regs calfact_s;
> +       const struct stm32_adc_regs calfact_d;
>         const u32 smpr[2];
>         const struct stm32_adc_regs *smp_bits;
> -       const struct stm32_adc_regs or_vdd;
> +       const struct stm32_adc_regs or_vddcore;
> +       const struct stm32_adc_regs or_vddcpu;
> +       const struct stm32_adc_regs or_vddq_ddr;
>         const struct stm32_adc_regs ccr_vbat;
>         const struct stm32_adc_regs ccr_vref;
>  };
> @@ -192,6 +206,9 @@ struct stm32_adc;
>   * @trigs:             external trigger sources
>   * @clk_required:      clock is required
>   * @has_vregready:     vregready status flag presence
> + * @has_boostmode:     boost mode support flag
> + * @has_linearcal:     linear calibration support flag
> + * @has_presel:                channel preselection support flag
>   * @prepare:           optional prepare routine (power-up, enable)
>   * @start_conv:                routine to start conversions
>   * @stop_conv:         routine to stop conversions
> @@ -206,6 +223,9 @@ struct stm32_adc_cfg {
>         struct stm32_adc_trig_info      *trigs;
>         bool clk_required;
>         bool has_vregready;
> +       bool has_boostmode;
> +       bool has_linearcal;
> +       bool has_presel;
>         int (*prepare)(struct iio_dev *);
>         void (*start_conv)(struct iio_dev *, bool dma);
>         void (*stop_conv)(struct iio_dev *);
> @@ -312,6 +332,13 @@ static const struct stm32_adc_info stm32h7_adc_info = {
>         .num_res = ARRAY_SIZE(stm32h7_adc_resolutions),
>  };
>
> +/* stm32mp13 can have up to 19 channels */
> +static const struct stm32_adc_info stm32mp13_adc_info = {
> +       .max_channels = 19,
> +       .resolutions = stm32f4_adc_resolutions,
> +       .num_res = ARRAY_SIZE(stm32f4_adc_resolutions),
> +};
> +
>  /*
>   * stm32f4_sq - describe regular sequence registers
>   * - L: sequence len (register & bit field)
> @@ -497,8 +524,43 @@ static const struct stm32_adc_regspec stm32h7_adc_regspec = {
>         .extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
>                     STM32H7_EXTSEL_SHIFT },
>         .res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT },
> +       .difsel = { STM32H7_ADC_DIFSEL, STM32H7_DIFSEL_MASK},
> +       .calfact_s = { STM32H7_ADC_CALFACT, STM32H7_CALFACT_S_MASK,
> +                      STM32H7_CALFACT_S_SHIFT },
> +       .calfact_d = { STM32H7_ADC_CALFACT, STM32H7_CALFACT_D_MASK,
> +                      STM32H7_CALFACT_D_SHIFT },
> +       .smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 },
> +       .smp_bits = stm32h7_smp_bits,
> +};
> +
> +/* STM32MP13 programmable sampling time (ADC clock cycles, rounded down) */
> +static const unsigned int stm32mp13_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = {
> +       2, 6, 12, 24, 47, 92, 247, 640,
> +};
> +
> +static const struct stm32_adc_regspec stm32mp13_adc_regspec = {
> +       .dr = STM32H7_ADC_DR,
> +       .ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE },
> +       .ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE },
> +       .isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC },
> +       .isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR },
> +       .sqr = stm32h7_sq,
> +       .exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT },
> +       .extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
> +                   STM32H7_EXTSEL_SHIFT },
> +       .res = { STM32H7_ADC_CFGR, STM32MP13_RES_MASK, STM32MP13_RES_SHIFT },
> +       .difsel = { STM32MP13_ADC_DIFSEL, STM32MP13_DIFSEL_MASK},
> +       .calfact_s = { STM32MP13_ADC_CALFACT, STM32MP13_CALFACT_S_MASK,
> +                      STM32MP13_CALFACT_S_SHIFT },
> +       .calfact_d = { STM32MP13_ADC_CALFACT, STM32MP13_CALFACT_D_MASK,
> +                      STM32MP13_CALFACT_D_SHIFT },
>         .smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 },
>         .smp_bits = stm32h7_smp_bits,
> +       .or_vddcore = { STM32MP13_ADC2_OR, STM32MP13_OP0 },
> +       .or_vddcpu = { STM32MP13_ADC2_OR, STM32MP13_OP1 },
> +       .or_vddq_ddr = { STM32MP13_ADC2_OR, STM32MP13_OP2 },
> +       .ccr_vbat = { STM32H7_ADC_CCR, STM32H7_VBATEN },
> +       .ccr_vref = { STM32H7_ADC_CCR, STM32H7_VREFEN },
>  };
>
>  static const struct stm32_adc_regspec stm32mp1_adc_regspec = {
> @@ -512,9 +574,14 @@ static const struct stm32_adc_regspec stm32mp1_adc_regspec = {
>         .extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
>                     STM32H7_EXTSEL_SHIFT },
>         .res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT },
> +       .difsel = { STM32H7_ADC_DIFSEL, STM32H7_DIFSEL_MASK},
> +       .calfact_s = { STM32H7_ADC_CALFACT, STM32H7_CALFACT_S_MASK,
> +                      STM32H7_CALFACT_S_SHIFT },
> +       .calfact_d = { STM32H7_ADC_CALFACT, STM32H7_CALFACT_D_MASK,
> +                      STM32H7_CALFACT_D_SHIFT },
>         .smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 },
>         .smp_bits = stm32h7_smp_bits,
> -       .or_vdd = { STM32MP1_ADC2_OR, STM32MP1_VDDCOREEN },
> +       .or_vddcore = { STM32MP1_ADC2_OR, STM32MP1_VDDCOREEN },
>         .ccr_vbat = { STM32H7_ADC_CCR, STM32H7_VBATEN },
>         .ccr_vref = { STM32H7_ADC_CCR, STM32H7_VREFEN },
>  };
> @@ -675,8 +742,18 @@ static void stm32_adc_int_ch_enable(struct iio_dev *indio_dev)
>                 switch (i) {
>                 case STM32_ADC_INT_CH_VDDCORE:
>                         dev_dbg(&indio_dev->dev, "Enable VDDCore\n");
> -                       stm32_adc_set_bits(adc, adc->cfg->regs->or_vdd.reg,
> -                                          adc->cfg->regs->or_vdd.mask);
> +                       stm32_adc_set_bits(adc, adc->cfg->regs->or_vddcore.reg,
> +                                          adc->cfg->regs->or_vddcore.mask);
> +                       break;
> +               case STM32_ADC_INT_CH_VDDCPU:
> +                       dev_dbg(&indio_dev->dev, "Enable VDDCPU\n");
> +                       stm32_adc_set_bits(adc, adc->cfg->regs->or_vddcpu.reg,
> +                                          adc->cfg->regs->or_vddcpu.mask);
> +                       break;
> +               case STM32_ADC_INT_CH_VDDQ_DDR:
> +                       dev_dbg(&indio_dev->dev, "Enable VDDQ_DDR\n");
> +                       stm32_adc_set_bits(adc, adc->cfg->regs->or_vddq_ddr.reg,
> +                                          adc->cfg->regs->or_vddq_ddr.mask);
>                         break;
>                 case STM32_ADC_INT_CH_VREFINT:
>                         dev_dbg(&indio_dev->dev, "Enable VREFInt\n");
> @@ -702,8 +779,16 @@ static void stm32_adc_int_ch_disable(struct stm32_adc *adc)
>
>                 switch (i) {
>                 case STM32_ADC_INT_CH_VDDCORE:
> -                       stm32_adc_clr_bits(adc, adc->cfg->regs->or_vdd.reg,
> -                                          adc->cfg->regs->or_vdd.mask);
> +                       stm32_adc_clr_bits(adc, adc->cfg->regs->or_vddcore.reg,
> +                                          adc->cfg->regs->or_vddcore.mask);
> +                       break;
> +               case STM32_ADC_INT_CH_VDDCPU:
> +                       stm32_adc_clr_bits(adc, adc->cfg->regs->or_vddcpu.reg,
> +                                          adc->cfg->regs->or_vddcpu.mask);
> +                       break;
> +               case STM32_ADC_INT_CH_VDDQ_DDR:
> +                       stm32_adc_clr_bits(adc, adc->cfg->regs->or_vddq_ddr.reg,
> +                                          adc->cfg->regs->or_vddq_ddr.mask);
>                         break;
>                 case STM32_ADC_INT_CH_VREFINT:
>                         stm32_adc_clr_bits_common(adc, adc->cfg->regs->ccr_vref.reg,
> @@ -801,6 +886,7 @@ static void stm32h7_adc_stop_conv(struct iio_dev *indio_dev)
>         if (ret)
>                 dev_warn(&indio_dev->dev, "stop failed\n");
>
> +       /* STM32H7_DMNGT_MASK covers STM32MP13_DMAEN & STM32MP13_DMACFG */
>         stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR, STM32H7_DMNGT_MASK);
>  }
>
> @@ -811,6 +897,17 @@ static void stm32h7_adc_irq_clear(struct iio_dev *indio_dev, u32 msk)
>         stm32_adc_set_bits(adc, adc->cfg->regs->isr_eoc.reg, msk);
>  }
>
> +static void stm32mp13_adc_start_conv(struct iio_dev *indio_dev, bool dma)
> +{
> +       struct stm32_adc *adc = iio_priv(indio_dev);
> +
> +       if (dma)
> +               stm32_adc_set_bits(adc, STM32H7_ADC_CFGR,
> +                                  STM32MP13_DMAEN | STM32MP13_DMACFG);
> +
> +       stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTART);
> +}
> +
>  static int stm32h7_adc_exit_pwr_down(struct iio_dev *indio_dev)
>  {
>         struct stm32_adc *adc = iio_priv(indio_dev);
> @@ -821,7 +918,8 @@ static int stm32h7_adc_exit_pwr_down(struct iio_dev *indio_dev)
>         stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
>         stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADVREGEN);
>
> -       if (adc->common->rate > STM32H7_BOOST_CLKRATE)
> +       if (adc->cfg->has_boostmode &&
> +           adc->common->rate > STM32H7_BOOST_CLKRATE)
>                 stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
>
>         /* Wait for startup time */
> @@ -843,7 +941,8 @@ static int stm32h7_adc_exit_pwr_down(struct iio_dev *indio_dev)
>
>  static void stm32h7_adc_enter_pwr_down(struct stm32_adc *adc)
>  {
> -       stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
> +       if (adc->cfg->has_boostmode)
> +               stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
>
>         /* Setting DEEPPWD disables ADC vreg and clears ADVREGEN */
>         stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
> @@ -901,6 +1000,9 @@ static int stm32h7_adc_read_selfcalib(struct iio_dev *indio_dev)
>         int i, ret;
>         u32 lincalrdyw_mask, val;
>
> +       if (!adc->cfg->has_linearcal)
> +               goto skip_linearcal;
> +
>         /* Read linearity calibration */
>         lincalrdyw_mask = STM32H7_LINCALRDYW6;
>         for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) {
> @@ -923,12 +1025,13 @@ static int stm32h7_adc_read_selfcalib(struct iio_dev *indio_dev)
>                 lincalrdyw_mask >>= 1;
>         }
>
> +skip_linearcal:
>         /* Read offset calibration */
> -       val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT);
> -       adc->cal.calfact_s = (val & STM32H7_CALFACT_S_MASK);
> -       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;
> +       val = stm32_adc_readl(adc, adc->cfg->regs->calfact_s.reg);
> +       adc->cal.calfact_s = (val & adc->cfg->regs->calfact_s.mask);
> +       adc->cal.calfact_s >>= adc->cfg->regs->calfact_s.shift;
> +       adc->cal.calfact_d = (val & adc->cfg->regs->calfact_d.mask);
> +       adc->cal.calfact_d >>= adc->cfg->regs->calfact_d.shift;
>         adc->cal.calibrated = true;
>
>         return 0;
> @@ -945,9 +1048,12 @@ static int stm32h7_adc_restore_selfcalib(struct iio_dev *indio_dev)
>         int i, ret;
>         u32 lincalrdyw_mask, val;
>
> -       val = (adc->cal.calfact_s << STM32H7_CALFACT_S_SHIFT) |
> -               (adc->cal.calfact_d << STM32H7_CALFACT_D_SHIFT);
> -       stm32_adc_writel(adc, STM32H7_ADC_CALFACT, val);
> +       val = (adc->cal.calfact_s << adc->cfg->regs->calfact_s.shift) |
> +               (adc->cal.calfact_d << adc->cfg->regs->calfact_d.shift);
> +       stm32_adc_writel(adc, adc->cfg->regs->calfact_s.reg, val);
> +
> +       if (!adc->cfg->has_linearcal)
> +               return 0;
>
>         lincalrdyw_mask = STM32H7_LINCALRDYW6;
>         for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) {
> @@ -1016,11 +1122,13 @@ static int stm32h7_adc_selfcalib(struct iio_dev *indio_dev)
>  {
>         struct stm32_adc *adc = iio_priv(indio_dev);
>         int ret;
> -       u32 val;
> +       u32 val, msk = STM32H7_ADCALDIF;
>
>         if (adc->cal.calibrated)
>                 return true;
>
> +       if (adc->cfg->has_linearcal)
> +               msk |= STM32H7_ADCALLIN;
>         /* ADC must be disabled for calibration */
>         stm32h7_adc_disable(indio_dev);
>
> @@ -1029,8 +1137,7 @@ static int stm32h7_adc_selfcalib(struct iio_dev *indio_dev)
>          * - Offset calibration for single ended inputs
>          * - No linearity calibration (do it later, before reading it)
>          */
> -       stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALDIF);
> -       stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALLIN);
> +       stm32_adc_clr_bits(adc, STM32H7_ADC_CR, msk);
>
>         /* Start calibration, then wait for completion */
>         stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL);
> @@ -1048,8 +1155,7 @@ static int stm32h7_adc_selfcalib(struct iio_dev *indio_dev)
>          * - Linearity calibration (needs to be done only once for single/diff)
>          *   will run simultaneously with offset calibration.
>          */
> -       stm32_adc_set_bits(adc, STM32H7_ADC_CR,
> -                          STM32H7_ADCALDIF | STM32H7_ADCALLIN);
> +       stm32_adc_set_bits(adc, STM32H7_ADC_CR, msk);
>         stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL);
>         ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
>                                            !(val & STM32H7_ADCAL), 100,
> @@ -1060,8 +1166,7 @@ static int stm32h7_adc_selfcalib(struct iio_dev *indio_dev)
>         }
>
>  out:
> -       stm32_adc_clr_bits(adc, STM32H7_ADC_CR,
> -                          STM32H7_ADCALDIF | STM32H7_ADCALLIN);
> +       stm32_adc_clr_bits(adc, STM32H7_ADC_CR, msk);
>
>         return ret;
>  }
> @@ -1093,7 +1198,7 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev)
>
>         stm32_adc_int_ch_enable(indio_dev);
>
> -       stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel);
> +       stm32_adc_writel(adc, adc->cfg->regs->difsel.reg, adc->difsel);
>
>         ret = stm32h7_adc_enable(indio_dev);
>         if (ret)
> @@ -1107,7 +1212,8 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev)
>         if (ret)
>                 goto disable;
>
> -       stm32_adc_writel(adc, STM32H7_ADC_PCSEL, adc->pcsel);
> +       if (adc->cfg->has_presel)
> +               stm32_adc_writel(adc, STM32H7_ADC_PCSEL, adc->pcsel);
>
>         return 0;
>
> @@ -1125,7 +1231,8 @@ static void stm32h7_adc_unprepare(struct iio_dev *indio_dev)
>  {
>         struct stm32_adc *adc = iio_priv(indio_dev);
>
> -       stm32_adc_writel(adc, STM32H7_ADC_PCSEL, 0);
> +       if (adc->cfg->has_presel)
> +               stm32_adc_writel(adc, STM32H7_ADC_PCSEL, 0);
>         stm32h7_adc_disable(indio_dev);
>         stm32_adc_int_ch_disable(adc);
>         stm32h7_adc_enter_pwr_down(adc);
> @@ -1857,7 +1964,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
>         adc->pcsel |= BIT(chan->channel);
>         if (differential) {
>                 /* pre-build diff channels mask */
> -               adc->difsel |= BIT(chan->channel);
> +               adc->difsel |= BIT(chan->channel) & adc->cfg->regs->difsel.mask;
>                 /* Also add negative input to pre-selected channels */
>                 adc->pcsel |= BIT(chan->channel2);
>         }
> @@ -1998,6 +2105,35 @@ static int stm32_adc_populate_int_ch(struct iio_dev *indio_dev, const char *ch_n
>
>         for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
>                 if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
> +                       /* Check internal channel availability */
> +                       switch (i) {
> +                       case STM32_ADC_INT_CH_VDDCORE:
> +                               if (!adc->cfg->regs->or_vddcore.reg)
> +                                       dev_warn(&indio_dev->dev,
> +                                                "%s channel not available\n", ch_name);
> +                               break;
> +                       case STM32_ADC_INT_CH_VDDCPU:
> +                               if (!adc->cfg->regs->or_vddcpu.reg)
> +                                       dev_warn(&indio_dev->dev,
> +                                                "%s channel not available\n", ch_name);
> +                               break;
> +                       case STM32_ADC_INT_CH_VDDQ_DDR:
> +                               if (!adc->cfg->regs->or_vddq_ddr.reg)
> +                                       dev_warn(&indio_dev->dev,
> +                                                "%s channel not available\n", ch_name);
> +                               break;
> +                       case STM32_ADC_INT_CH_VREFINT:
> +                               if (!adc->cfg->regs->ccr_vref.reg)
> +                                       dev_warn(&indio_dev->dev,
> +                                                "%s channel not available\n", ch_name);
> +                               break;
> +                       case STM32_ADC_INT_CH_VBAT:
> +                               if (!adc->cfg->regs->ccr_vbat.reg)
> +                                       dev_warn(&indio_dev->dev,
> +                                                "%s channel not available\n", ch_name);
> +                               break;
> +                       }
> +
>                         if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT) {
>                                 adc->int_ch[i] = chan;
>                                 break;
> @@ -2435,6 +2571,9 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = {
>         .regs = &stm32h7_adc_regspec,
>         .adc_info = &stm32h7_adc_info,
>         .trigs = stm32h7_adc_trigs,
> +       .has_boostmode = true,
> +       .has_linearcal = true,
> +       .has_presel = true,
>         .start_conv = stm32h7_adc_start_conv,
>         .stop_conv = stm32h7_adc_stop_conv,
>         .prepare = stm32h7_adc_prepare,
> @@ -2448,6 +2587,9 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
>         .adc_info = &stm32h7_adc_info,
>         .trigs = stm32h7_adc_trigs,
>         .has_vregready = true,
> +       .has_boostmode = true,
> +       .has_linearcal = true,
> +       .has_presel = true,
>         .start_conv = stm32h7_adc_start_conv,
>         .stop_conv = stm32h7_adc_stop_conv,
>         .prepare = stm32h7_adc_prepare,
> @@ -2457,10 +2599,24 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
>         .ts_vrefint_ns = 4300,
>  };
>
> +static const struct stm32_adc_cfg stm32mp13_adc_cfg = {
> +       .regs = &stm32mp13_adc_regspec,
> +       .adc_info = &stm32mp13_adc_info,
> +       .trigs = stm32h7_adc_trigs,
> +       .start_conv = stm32mp13_adc_start_conv,
> +       .stop_conv = stm32h7_adc_stop_conv,
> +       .prepare = stm32h7_adc_prepare,
> +       .unprepare = stm32h7_adc_unprepare,
> +       .smp_cycles = stm32mp13_adc_smp_cycles,
> +       .irq_clear = stm32h7_adc_irq_clear,
> +};
> +
>  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 },
> +       { .compatible = "st,stm32mp13-adc",
> +         .data = (void *)&stm32mp13_adc_cfg },

I think it would be nicer to have it on one line despite being longer than 80.

>         {},
>  };
>  MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
> --
> 2.25.1
>
Jonathan Cameron Oct. 2, 2022, 1:42 p.m. UTC | #2
On Wed, 28 Sep 2022 18:41:09 +0200
Olivier Moysan <olivier.moysan@foss.st.com> wrote:

> Add STM32 ADC support for STM32MP13x SOCs family.
> 
> On STM32MP13x, each ADC peripheral has a single ADC block.

Wrap this a bit nearer 80 chars. Personally I go with 75, but anywhere around
that is fine.

> These ADC peripherals, ADC1 and ADC2, are fully independent.
> This introduces changes in common registers handling.
> 
> Some features such as boost mode, channel preselection and
> linear calibration are not supported by the STM32MP13x ADC.
> Add diversity management for these features.
> 
> The STM32MP13x ADC introduces registers and bitfield variants
> on existing features such as calibration factors and internal
> channels. Add register diversity management.
> 
> Add also support of new internal channels VDDCPU and VDDQ_DDR.
> 
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
Hi Oliver

A few really trivial things inline,

Jonathan

> ---
>  drivers/iio/adc/stm32-adc-core.c |  21 +++
>  drivers/iio/adc/stm32-adc-core.h |  32 +++++
>  drivers/iio/adc/stm32-adc.c      | 212 +++++++++++++++++++++++++++----
>  3 files changed, 237 insertions(+), 28 deletions(-)
> 
> diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
> index 81d5db91c67b..6564aa61b595 100644
> --- a/drivers/iio/adc/stm32-adc-core.c
> +++ b/drivers/iio/adc/stm32-adc-core.c
> @@ -322,6 +322,16 @@ static const struct stm32_adc_common_regs stm32h7_adc_common_regs = {
>  	.eocie_msk = STM32H7_EOCIE,
>  };
>  
> +/* STM32MP13 common registers definitions */
> +static const struct stm32_adc_common_regs stm32mp13_adc_common_regs = {
> +	.csr = STM32H7_ADC_CSR,
> +	.ccr = STM32H7_ADC_CCR,
> +	.eoc_msk = { STM32H7_EOC_MST},

space before },
ouch. I see this odd balance is common in this driver.  Don't carry it on and
if you fancy adding the missing spaces to the rest of the driver that would be
great!

> +	.ovr_msk = { STM32H7_OVR_MST},
> +	.ier = STM32H7_ADC_IER,
> +	.eocie_msk = STM32H7_EOCIE,
> +};
> +
>  static const unsigned int stm32_adc_offset[STM32_ADC_MAX_ADCS] = {
>  	0, STM32_ADC_OFFSET, STM32_ADC_OFFSET * 2,
>  };
> @@ -868,6 +878,14 @@ static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = {
>  	.num_irqs = 2,
>  };
>  
> +static const struct stm32_adc_priv_cfg stm32mp13_adc_priv_cfg = {
> +	.regs = &stm32mp13_adc_common_regs,
> +	.clk_sel = stm32h7_adc_clk_sel,
> +	.max_clk_rate_hz = 75000000,
> +	.ipid = STM32MP13_IPIDR_NUMBER,
> +	.num_irqs = 1,
> +};
> +
>  static const struct of_device_id stm32_adc_of_match[] = {
>  	{
>  		.compatible = "st,stm32f4-adc-core",
> @@ -878,6 +896,9 @@ static const struct of_device_id stm32_adc_of_match[] = {
>  	}, {
>  		.compatible = "st,stm32mp1-adc-core",
>  		.data = (void *)&stm32mp1_adc_priv_cfg
> +	}, {
> +		.compatible = "st,stm32mp13-adc-core",
> +		.data = (void *)&stm32mp13_adc_priv_cfg
>  	}, {
>  	},
>  };
> diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
> index 2118ef63843d..658fef4308ac 100644
> --- a/drivers/iio/adc/stm32-adc-core.h
> +++ b/drivers/iio/adc/stm32-adc-core.h
> @@ -112,6 +112,11 @@
>  #define STM32MP1_ADC_IPDR		0x3F8
>  #define STM32MP1_ADC_SIDR		0x3FC
>  
> +/* STM32MP13 - Registers for each ADC instance */
> +#define STM32MP13_ADC_DIFSEL		0xB0
> +#define STM32MP13_ADC_CALFACT		0xB4
> +#define STM32MP13_ADC2_OR		0xC8
> +
>  /* 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)
> @@ -161,6 +166,10 @@ enum stm32h7_adc_dmngt {
>  	STM32H7_DMNGT_DMA_CIRC,		/* DMA circular mode */
>  };
>  
> +/* STM32H7_ADC_DIFSEL - bit fields */
> +#define STM32H7_DIFSEL_SHIFT		0

This shift is both unnecessary, as encoded in the mask, and unused
so you can definitely get rid of it.
Might be nice to clean the rest of these out as well in favour of
just using the masks and FIELD_PREP() etc for any places where the mask
is a compile time constant.

Please check the others are actually useful.


> +#define STM32H7_DIFSEL_MASK		GENMASK(19, 0)
> +
>  /* STM32H7_ADC_CALFACT - bit fields */
>  #define STM32H7_CALFACT_D_SHIFT		16
>  #define STM32H7_CALFACT_D_MASK		GENMASK(26, 16)
> @@ -210,7 +219,30 @@ enum stm32h7_adc_dmngt {
>  /* STM32MP1_ADC_SIDR - bit fields */
>  #define STM32MP1_SIDR_MASK		GENMASK(31, 0)
...
diff mbox series

Patch

diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
index 81d5db91c67b..6564aa61b595 100644
--- a/drivers/iio/adc/stm32-adc-core.c
+++ b/drivers/iio/adc/stm32-adc-core.c
@@ -322,6 +322,16 @@  static const struct stm32_adc_common_regs stm32h7_adc_common_regs = {
 	.eocie_msk = STM32H7_EOCIE,
 };
 
+/* STM32MP13 common registers definitions */
+static const struct stm32_adc_common_regs stm32mp13_adc_common_regs = {
+	.csr = STM32H7_ADC_CSR,
+	.ccr = STM32H7_ADC_CCR,
+	.eoc_msk = { STM32H7_EOC_MST},
+	.ovr_msk = { STM32H7_OVR_MST},
+	.ier = STM32H7_ADC_IER,
+	.eocie_msk = STM32H7_EOCIE,
+};
+
 static const unsigned int stm32_adc_offset[STM32_ADC_MAX_ADCS] = {
 	0, STM32_ADC_OFFSET, STM32_ADC_OFFSET * 2,
 };
@@ -868,6 +878,14 @@  static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = {
 	.num_irqs = 2,
 };
 
+static const struct stm32_adc_priv_cfg stm32mp13_adc_priv_cfg = {
+	.regs = &stm32mp13_adc_common_regs,
+	.clk_sel = stm32h7_adc_clk_sel,
+	.max_clk_rate_hz = 75000000,
+	.ipid = STM32MP13_IPIDR_NUMBER,
+	.num_irqs = 1,
+};
+
 static const struct of_device_id stm32_adc_of_match[] = {
 	{
 		.compatible = "st,stm32f4-adc-core",
@@ -878,6 +896,9 @@  static const struct of_device_id stm32_adc_of_match[] = {
 	}, {
 		.compatible = "st,stm32mp1-adc-core",
 		.data = (void *)&stm32mp1_adc_priv_cfg
+	}, {
+		.compatible = "st,stm32mp13-adc-core",
+		.data = (void *)&stm32mp13_adc_priv_cfg
 	}, {
 	},
 };
diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
index 2118ef63843d..658fef4308ac 100644
--- a/drivers/iio/adc/stm32-adc-core.h
+++ b/drivers/iio/adc/stm32-adc-core.h
@@ -112,6 +112,11 @@ 
 #define STM32MP1_ADC_IPDR		0x3F8
 #define STM32MP1_ADC_SIDR		0x3FC
 
+/* STM32MP13 - Registers for each ADC instance */
+#define STM32MP13_ADC_DIFSEL		0xB0
+#define STM32MP13_ADC_CALFACT		0xB4
+#define STM32MP13_ADC2_OR		0xC8
+
 /* 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)
@@ -161,6 +166,10 @@  enum stm32h7_adc_dmngt {
 	STM32H7_DMNGT_DMA_CIRC,		/* DMA circular mode */
 };
 
+/* STM32H7_ADC_DIFSEL - bit fields */
+#define STM32H7_DIFSEL_SHIFT		0
+#define STM32H7_DIFSEL_MASK		GENMASK(19, 0)
+
 /* STM32H7_ADC_CALFACT - bit fields */
 #define STM32H7_CALFACT_D_SHIFT		16
 #define STM32H7_CALFACT_D_MASK		GENMASK(26, 16)
@@ -210,7 +219,30 @@  enum stm32h7_adc_dmngt {
 /* STM32MP1_ADC_SIDR - bit fields */
 #define STM32MP1_SIDR_MASK		GENMASK(31, 0)
 
+/* STM32MP13_ADC_CFGR specific bit fields */
+#define STM32MP13_DMAEN			BIT(0)
+#define STM32MP13_DMACFG		BIT(1)
+#define STM32MP13_DFSDMCFG		BIT(2)
+#define STM32MP13_RES_SHIFT		3
+#define STM32MP13_RES_MASK		GENMASK(4, 3)
+
+/* STM32MP13_ADC_DIFSEL - bit fields */
+#define STM32MP13_DIFSEL_SHIFT		0
+#define STM32MP13_DIFSEL_MASK		GENMASK(18, 0)
+
+/* STM32MP13_ADC_CALFACT - bit fields */
+#define STM32MP13_CALFACT_D_SHIFT	16
+#define STM32MP13_CALFACT_D_MASK	GENMASK(22, 16)
+#define STM32MP13_CALFACT_S_SHIFT	0
+#define STM32MP13_CALFACT_S_MASK	GENMASK(6, 0)
+
+/* STM32MP13_ADC2_OR - bit fields */
+#define STM32MP13_OP2			BIT(2)
+#define STM32MP13_OP1			BIT(1)
+#define STM32MP13_OP0			BIT(0)
+
 #define STM32MP15_IPIDR_NUMBER		0x00110005
+#define STM32MP13_IPIDR_NUMBER		0x00110006
 
 /**
  * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 3cda529f081d..362db64bbd69 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -82,6 +82,8 @@  enum stm32_adc_extsel {
 enum stm32_adc_int_ch {
 	STM32_ADC_INT_CH_NONE = -1,
 	STM32_ADC_INT_CH_VDDCORE,
+	STM32_ADC_INT_CH_VDDCPU,
+	STM32_ADC_INT_CH_VDDQ_DDR,
 	STM32_ADC_INT_CH_VREFINT,
 	STM32_ADC_INT_CH_VBAT,
 	STM32_ADC_INT_CH_NB,
@@ -99,6 +101,8 @@  struct stm32_adc_ic {
 
 static const struct stm32_adc_ic stm32_adc_ic[STM32_ADC_INT_CH_NB] = {
 	{ "vddcore", STM32_ADC_INT_CH_VDDCORE },
+	{ "vddcpu", STM32_ADC_INT_CH_VDDCPU },
+	{ "vddq_ddr", STM32_ADC_INT_CH_VDDQ_DDR },
 	{ "vrefint", STM32_ADC_INT_CH_VREFINT },
 	{ "vbat", STM32_ADC_INT_CH_VBAT },
 };
@@ -160,9 +164,14 @@  struct stm32_adc_vrefint {
  * @exten:		trigger control register & bitfield
  * @extsel:		trigger selection register & bitfield
  * @res:		resolution selection register & bitfield
+ * @difsel:		differential mode selection register & bitfield
+ * @calfact_s:		single-ended calibration factors register & bitfield
+ * @calfact_d:		differential calibration factors register & bitfield
  * @smpr:		smpr1 & smpr2 registers offset array
  * @smp_bits:		smpr1 & smpr2 index and bitfields
- * @or_vdd:		option register & vddcore bitfield
+ * @or_vddcore:		option register & vddcore bitfield
+ * @or_vddcpu:		option register & vddcpu bitfield
+ * @or_vddq_ddr:	option register & vddq_ddr bitfield
  * @ccr_vbat:		common register & vbat bitfield
  * @ccr_vref:		common register & vrefint bitfield
  */
@@ -176,9 +185,14 @@  struct stm32_adc_regspec {
 	const struct stm32_adc_regs exten;
 	const struct stm32_adc_regs extsel;
 	const struct stm32_adc_regs res;
+	const struct stm32_adc_regs difsel;
+	const struct stm32_adc_regs calfact_s;
+	const struct stm32_adc_regs calfact_d;
 	const u32 smpr[2];
 	const struct stm32_adc_regs *smp_bits;
-	const struct stm32_adc_regs or_vdd;
+	const struct stm32_adc_regs or_vddcore;
+	const struct stm32_adc_regs or_vddcpu;
+	const struct stm32_adc_regs or_vddq_ddr;
 	const struct stm32_adc_regs ccr_vbat;
 	const struct stm32_adc_regs ccr_vref;
 };
@@ -192,6 +206,9 @@  struct stm32_adc;
  * @trigs:		external trigger sources
  * @clk_required:	clock is required
  * @has_vregready:	vregready status flag presence
+ * @has_boostmode:	boost mode support flag
+ * @has_linearcal:	linear calibration support flag
+ * @has_presel:		channel preselection support flag
  * @prepare:		optional prepare routine (power-up, enable)
  * @start_conv:		routine to start conversions
  * @stop_conv:		routine to stop conversions
@@ -206,6 +223,9 @@  struct stm32_adc_cfg {
 	struct stm32_adc_trig_info	*trigs;
 	bool clk_required;
 	bool has_vregready;
+	bool has_boostmode;
+	bool has_linearcal;
+	bool has_presel;
 	int (*prepare)(struct iio_dev *);
 	void (*start_conv)(struct iio_dev *, bool dma);
 	void (*stop_conv)(struct iio_dev *);
@@ -312,6 +332,13 @@  static const struct stm32_adc_info stm32h7_adc_info = {
 	.num_res = ARRAY_SIZE(stm32h7_adc_resolutions),
 };
 
+/* stm32mp13 can have up to 19 channels */
+static const struct stm32_adc_info stm32mp13_adc_info = {
+	.max_channels = 19,
+	.resolutions = stm32f4_adc_resolutions,
+	.num_res = ARRAY_SIZE(stm32f4_adc_resolutions),
+};
+
 /*
  * stm32f4_sq - describe regular sequence registers
  * - L: sequence len (register & bit field)
@@ -497,8 +524,43 @@  static const struct stm32_adc_regspec stm32h7_adc_regspec = {
 	.extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
 		    STM32H7_EXTSEL_SHIFT },
 	.res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT },
+	.difsel = { STM32H7_ADC_DIFSEL, STM32H7_DIFSEL_MASK},
+	.calfact_s = { STM32H7_ADC_CALFACT, STM32H7_CALFACT_S_MASK,
+		       STM32H7_CALFACT_S_SHIFT },
+	.calfact_d = { STM32H7_ADC_CALFACT, STM32H7_CALFACT_D_MASK,
+		       STM32H7_CALFACT_D_SHIFT },
+	.smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 },
+	.smp_bits = stm32h7_smp_bits,
+};
+
+/* STM32MP13 programmable sampling time (ADC clock cycles, rounded down) */
+static const unsigned int stm32mp13_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = {
+	2, 6, 12, 24, 47, 92, 247, 640,
+};
+
+static const struct stm32_adc_regspec stm32mp13_adc_regspec = {
+	.dr = STM32H7_ADC_DR,
+	.ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE },
+	.ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE },
+	.isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC },
+	.isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR },
+	.sqr = stm32h7_sq,
+	.exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT },
+	.extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
+		    STM32H7_EXTSEL_SHIFT },
+	.res = { STM32H7_ADC_CFGR, STM32MP13_RES_MASK, STM32MP13_RES_SHIFT },
+	.difsel = { STM32MP13_ADC_DIFSEL, STM32MP13_DIFSEL_MASK},
+	.calfact_s = { STM32MP13_ADC_CALFACT, STM32MP13_CALFACT_S_MASK,
+		       STM32MP13_CALFACT_S_SHIFT },
+	.calfact_d = { STM32MP13_ADC_CALFACT, STM32MP13_CALFACT_D_MASK,
+		       STM32MP13_CALFACT_D_SHIFT },
 	.smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 },
 	.smp_bits = stm32h7_smp_bits,
+	.or_vddcore = { STM32MP13_ADC2_OR, STM32MP13_OP0 },
+	.or_vddcpu = { STM32MP13_ADC2_OR, STM32MP13_OP1 },
+	.or_vddq_ddr = { STM32MP13_ADC2_OR, STM32MP13_OP2 },
+	.ccr_vbat = { STM32H7_ADC_CCR, STM32H7_VBATEN },
+	.ccr_vref = { STM32H7_ADC_CCR, STM32H7_VREFEN },
 };
 
 static const struct stm32_adc_regspec stm32mp1_adc_regspec = {
@@ -512,9 +574,14 @@  static const struct stm32_adc_regspec stm32mp1_adc_regspec = {
 	.extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
 		    STM32H7_EXTSEL_SHIFT },
 	.res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT },
+	.difsel = { STM32H7_ADC_DIFSEL, STM32H7_DIFSEL_MASK},
+	.calfact_s = { STM32H7_ADC_CALFACT, STM32H7_CALFACT_S_MASK,
+		       STM32H7_CALFACT_S_SHIFT },
+	.calfact_d = { STM32H7_ADC_CALFACT, STM32H7_CALFACT_D_MASK,
+		       STM32H7_CALFACT_D_SHIFT },
 	.smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 },
 	.smp_bits = stm32h7_smp_bits,
-	.or_vdd = { STM32MP1_ADC2_OR, STM32MP1_VDDCOREEN },
+	.or_vddcore = { STM32MP1_ADC2_OR, STM32MP1_VDDCOREEN },
 	.ccr_vbat = { STM32H7_ADC_CCR, STM32H7_VBATEN },
 	.ccr_vref = { STM32H7_ADC_CCR, STM32H7_VREFEN },
 };
@@ -675,8 +742,18 @@  static void stm32_adc_int_ch_enable(struct iio_dev *indio_dev)
 		switch (i) {
 		case STM32_ADC_INT_CH_VDDCORE:
 			dev_dbg(&indio_dev->dev, "Enable VDDCore\n");
-			stm32_adc_set_bits(adc, adc->cfg->regs->or_vdd.reg,
-					   adc->cfg->regs->or_vdd.mask);
+			stm32_adc_set_bits(adc, adc->cfg->regs->or_vddcore.reg,
+					   adc->cfg->regs->or_vddcore.mask);
+			break;
+		case STM32_ADC_INT_CH_VDDCPU:
+			dev_dbg(&indio_dev->dev, "Enable VDDCPU\n");
+			stm32_adc_set_bits(adc, adc->cfg->regs->or_vddcpu.reg,
+					   adc->cfg->regs->or_vddcpu.mask);
+			break;
+		case STM32_ADC_INT_CH_VDDQ_DDR:
+			dev_dbg(&indio_dev->dev, "Enable VDDQ_DDR\n");
+			stm32_adc_set_bits(adc, adc->cfg->regs->or_vddq_ddr.reg,
+					   adc->cfg->regs->or_vddq_ddr.mask);
 			break;
 		case STM32_ADC_INT_CH_VREFINT:
 			dev_dbg(&indio_dev->dev, "Enable VREFInt\n");
@@ -702,8 +779,16 @@  static void stm32_adc_int_ch_disable(struct stm32_adc *adc)
 
 		switch (i) {
 		case STM32_ADC_INT_CH_VDDCORE:
-			stm32_adc_clr_bits(adc, adc->cfg->regs->or_vdd.reg,
-					   adc->cfg->regs->or_vdd.mask);
+			stm32_adc_clr_bits(adc, adc->cfg->regs->or_vddcore.reg,
+					   adc->cfg->regs->or_vddcore.mask);
+			break;
+		case STM32_ADC_INT_CH_VDDCPU:
+			stm32_adc_clr_bits(adc, adc->cfg->regs->or_vddcpu.reg,
+					   adc->cfg->regs->or_vddcpu.mask);
+			break;
+		case STM32_ADC_INT_CH_VDDQ_DDR:
+			stm32_adc_clr_bits(adc, adc->cfg->regs->or_vddq_ddr.reg,
+					   adc->cfg->regs->or_vddq_ddr.mask);
 			break;
 		case STM32_ADC_INT_CH_VREFINT:
 			stm32_adc_clr_bits_common(adc, adc->cfg->regs->ccr_vref.reg,
@@ -801,6 +886,7 @@  static void stm32h7_adc_stop_conv(struct iio_dev *indio_dev)
 	if (ret)
 		dev_warn(&indio_dev->dev, "stop failed\n");
 
+	/* STM32H7_DMNGT_MASK covers STM32MP13_DMAEN & STM32MP13_DMACFG */
 	stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR, STM32H7_DMNGT_MASK);
 }
 
@@ -811,6 +897,17 @@  static void stm32h7_adc_irq_clear(struct iio_dev *indio_dev, u32 msk)
 	stm32_adc_set_bits(adc, adc->cfg->regs->isr_eoc.reg, msk);
 }
 
+static void stm32mp13_adc_start_conv(struct iio_dev *indio_dev, bool dma)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+
+	if (dma)
+		stm32_adc_set_bits(adc, STM32H7_ADC_CFGR,
+				   STM32MP13_DMAEN | STM32MP13_DMACFG);
+
+	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTART);
+}
+
 static int stm32h7_adc_exit_pwr_down(struct iio_dev *indio_dev)
 {
 	struct stm32_adc *adc = iio_priv(indio_dev);
@@ -821,7 +918,8 @@  static int stm32h7_adc_exit_pwr_down(struct iio_dev *indio_dev)
 	stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
 	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADVREGEN);
 
-	if (adc->common->rate > STM32H7_BOOST_CLKRATE)
+	if (adc->cfg->has_boostmode &&
+	    adc->common->rate > STM32H7_BOOST_CLKRATE)
 		stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
 
 	/* Wait for startup time */
@@ -843,7 +941,8 @@  static int stm32h7_adc_exit_pwr_down(struct iio_dev *indio_dev)
 
 static void stm32h7_adc_enter_pwr_down(struct stm32_adc *adc)
 {
-	stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
+	if (adc->cfg->has_boostmode)
+		stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
 
 	/* Setting DEEPPWD disables ADC vreg and clears ADVREGEN */
 	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
@@ -901,6 +1000,9 @@  static int stm32h7_adc_read_selfcalib(struct iio_dev *indio_dev)
 	int i, ret;
 	u32 lincalrdyw_mask, val;
 
+	if (!adc->cfg->has_linearcal)
+		goto skip_linearcal;
+
 	/* Read linearity calibration */
 	lincalrdyw_mask = STM32H7_LINCALRDYW6;
 	for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) {
@@ -923,12 +1025,13 @@  static int stm32h7_adc_read_selfcalib(struct iio_dev *indio_dev)
 		lincalrdyw_mask >>= 1;
 	}
 
+skip_linearcal:
 	/* Read offset calibration */
-	val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT);
-	adc->cal.calfact_s = (val & STM32H7_CALFACT_S_MASK);
-	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;
+	val = stm32_adc_readl(adc, adc->cfg->regs->calfact_s.reg);
+	adc->cal.calfact_s = (val & adc->cfg->regs->calfact_s.mask);
+	adc->cal.calfact_s >>= adc->cfg->regs->calfact_s.shift;
+	adc->cal.calfact_d = (val & adc->cfg->regs->calfact_d.mask);
+	adc->cal.calfact_d >>= adc->cfg->regs->calfact_d.shift;
 	adc->cal.calibrated = true;
 
 	return 0;
@@ -945,9 +1048,12 @@  static int stm32h7_adc_restore_selfcalib(struct iio_dev *indio_dev)
 	int i, ret;
 	u32 lincalrdyw_mask, val;
 
-	val = (adc->cal.calfact_s << STM32H7_CALFACT_S_SHIFT) |
-		(adc->cal.calfact_d << STM32H7_CALFACT_D_SHIFT);
-	stm32_adc_writel(adc, STM32H7_ADC_CALFACT, val);
+	val = (adc->cal.calfact_s << adc->cfg->regs->calfact_s.shift) |
+		(adc->cal.calfact_d << adc->cfg->regs->calfact_d.shift);
+	stm32_adc_writel(adc, adc->cfg->regs->calfact_s.reg, val);
+
+	if (!adc->cfg->has_linearcal)
+		return 0;
 
 	lincalrdyw_mask = STM32H7_LINCALRDYW6;
 	for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) {
@@ -1016,11 +1122,13 @@  static int stm32h7_adc_selfcalib(struct iio_dev *indio_dev)
 {
 	struct stm32_adc *adc = iio_priv(indio_dev);
 	int ret;
-	u32 val;
+	u32 val, msk = STM32H7_ADCALDIF;
 
 	if (adc->cal.calibrated)
 		return true;
 
+	if (adc->cfg->has_linearcal)
+		msk |= STM32H7_ADCALLIN;
 	/* ADC must be disabled for calibration */
 	stm32h7_adc_disable(indio_dev);
 
@@ -1029,8 +1137,7 @@  static int stm32h7_adc_selfcalib(struct iio_dev *indio_dev)
 	 * - Offset calibration for single ended inputs
 	 * - No linearity calibration (do it later, before reading it)
 	 */
-	stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALDIF);
-	stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALLIN);
+	stm32_adc_clr_bits(adc, STM32H7_ADC_CR, msk);
 
 	/* Start calibration, then wait for completion */
 	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL);
@@ -1048,8 +1155,7 @@  static int stm32h7_adc_selfcalib(struct iio_dev *indio_dev)
 	 * - Linearity calibration (needs to be done only once for single/diff)
 	 *   will run simultaneously with offset calibration.
 	 */
-	stm32_adc_set_bits(adc, STM32H7_ADC_CR,
-			   STM32H7_ADCALDIF | STM32H7_ADCALLIN);
+	stm32_adc_set_bits(adc, STM32H7_ADC_CR, msk);
 	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL);
 	ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
 					   !(val & STM32H7_ADCAL), 100,
@@ -1060,8 +1166,7 @@  static int stm32h7_adc_selfcalib(struct iio_dev *indio_dev)
 	}
 
 out:
-	stm32_adc_clr_bits(adc, STM32H7_ADC_CR,
-			   STM32H7_ADCALDIF | STM32H7_ADCALLIN);
+	stm32_adc_clr_bits(adc, STM32H7_ADC_CR, msk);
 
 	return ret;
 }
@@ -1093,7 +1198,7 @@  static int stm32h7_adc_prepare(struct iio_dev *indio_dev)
 
 	stm32_adc_int_ch_enable(indio_dev);
 
-	stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel);
+	stm32_adc_writel(adc, adc->cfg->regs->difsel.reg, adc->difsel);
 
 	ret = stm32h7_adc_enable(indio_dev);
 	if (ret)
@@ -1107,7 +1212,8 @@  static int stm32h7_adc_prepare(struct iio_dev *indio_dev)
 	if (ret)
 		goto disable;
 
-	stm32_adc_writel(adc, STM32H7_ADC_PCSEL, adc->pcsel);
+	if (adc->cfg->has_presel)
+		stm32_adc_writel(adc, STM32H7_ADC_PCSEL, adc->pcsel);
 
 	return 0;
 
@@ -1125,7 +1231,8 @@  static void stm32h7_adc_unprepare(struct iio_dev *indio_dev)
 {
 	struct stm32_adc *adc = iio_priv(indio_dev);
 
-	stm32_adc_writel(adc, STM32H7_ADC_PCSEL, 0);
+	if (adc->cfg->has_presel)
+		stm32_adc_writel(adc, STM32H7_ADC_PCSEL, 0);
 	stm32h7_adc_disable(indio_dev);
 	stm32_adc_int_ch_disable(adc);
 	stm32h7_adc_enter_pwr_down(adc);
@@ -1857,7 +1964,7 @@  static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
 	adc->pcsel |= BIT(chan->channel);
 	if (differential) {
 		/* pre-build diff channels mask */
-		adc->difsel |= BIT(chan->channel);
+		adc->difsel |= BIT(chan->channel) & adc->cfg->regs->difsel.mask;
 		/* Also add negative input to pre-selected channels */
 		adc->pcsel |= BIT(chan->channel2);
 	}
@@ -1998,6 +2105,35 @@  static int stm32_adc_populate_int_ch(struct iio_dev *indio_dev, const char *ch_n
 
 	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
 		if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
+			/* Check internal channel availability */
+			switch (i) {
+			case STM32_ADC_INT_CH_VDDCORE:
+				if (!adc->cfg->regs->or_vddcore.reg)
+					dev_warn(&indio_dev->dev,
+						 "%s channel not available\n", ch_name);
+				break;
+			case STM32_ADC_INT_CH_VDDCPU:
+				if (!adc->cfg->regs->or_vddcpu.reg)
+					dev_warn(&indio_dev->dev,
+						 "%s channel not available\n", ch_name);
+				break;
+			case STM32_ADC_INT_CH_VDDQ_DDR:
+				if (!adc->cfg->regs->or_vddq_ddr.reg)
+					dev_warn(&indio_dev->dev,
+						 "%s channel not available\n", ch_name);
+				break;
+			case STM32_ADC_INT_CH_VREFINT:
+				if (!adc->cfg->regs->ccr_vref.reg)
+					dev_warn(&indio_dev->dev,
+						 "%s channel not available\n", ch_name);
+				break;
+			case STM32_ADC_INT_CH_VBAT:
+				if (!adc->cfg->regs->ccr_vbat.reg)
+					dev_warn(&indio_dev->dev,
+						 "%s channel not available\n", ch_name);
+				break;
+			}
+
 			if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT) {
 				adc->int_ch[i] = chan;
 				break;
@@ -2435,6 +2571,9 @@  static const struct stm32_adc_cfg stm32h7_adc_cfg = {
 	.regs = &stm32h7_adc_regspec,
 	.adc_info = &stm32h7_adc_info,
 	.trigs = stm32h7_adc_trigs,
+	.has_boostmode = true,
+	.has_linearcal = true,
+	.has_presel = true,
 	.start_conv = stm32h7_adc_start_conv,
 	.stop_conv = stm32h7_adc_stop_conv,
 	.prepare = stm32h7_adc_prepare,
@@ -2448,6 +2587,9 @@  static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
 	.adc_info = &stm32h7_adc_info,
 	.trigs = stm32h7_adc_trigs,
 	.has_vregready = true,
+	.has_boostmode = true,
+	.has_linearcal = true,
+	.has_presel = true,
 	.start_conv = stm32h7_adc_start_conv,
 	.stop_conv = stm32h7_adc_stop_conv,
 	.prepare = stm32h7_adc_prepare,
@@ -2457,10 +2599,24 @@  static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
 	.ts_vrefint_ns = 4300,
 };
 
+static const struct stm32_adc_cfg stm32mp13_adc_cfg = {
+	.regs = &stm32mp13_adc_regspec,
+	.adc_info = &stm32mp13_adc_info,
+	.trigs = stm32h7_adc_trigs,
+	.start_conv = stm32mp13_adc_start_conv,
+	.stop_conv = stm32h7_adc_stop_conv,
+	.prepare = stm32h7_adc_prepare,
+	.unprepare = stm32h7_adc_unprepare,
+	.smp_cycles = stm32mp13_adc_smp_cycles,
+	.irq_clear = stm32h7_adc_irq_clear,
+};
+
 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 },
+	{ .compatible = "st,stm32mp13-adc",
+	  .data = (void *)&stm32mp13_adc_cfg },
 	{},
 };
 MODULE_DEVICE_TABLE(of, stm32_adc_of_match);