* [PATCH 0/8] iio: adc: stm32: add support for stm32mp23 and stm32mp25
@ 2026-09-11 15:51 Fabrice Gasnier
2026-09-11 15:51 ` [PATCH 1/8] dt-bindings: iio: adc: stm32-adc: " Fabrice Gasnier
` (7 more replies)
0 siblings, 8 replies; 13+ messages in thread
From: Fabrice Gasnier @ 2026-09-11 15:51 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin,
Alexandre Torgue
Cc: Marek Vasut, linux-iio, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, Fabrice Gasnier, Cheick Traore, Olivier Moysan,
Patrice Chotard, Cheick Traore
Add support for STM32MP23 and STM32MP25 Analog to Digital Converter.
It's a variant of other supported STM32MP ADCs. Add the DT bindings,
driver, SOC DTS and an example on stm32mp257f-ev1 board.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
---
Cheick Traore (1):
arm64: dts: st: add ADC nodes on stm32mp231
Fabrice Gasnier (4):
dt-bindings: iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25
iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25
arm64: dts: st: add ADC nodes on stm32mp251
arm64: dts: st: add ADC support on stm32mp257f-ev1 board
Olivier Moysan (1):
iio: adc: stm32-adc: manage characterization voltage diversity
Patrice Chotard (2):
arm64: dts: st: add vrefint calibration on stm32mp23
arm64: dts: st: add vrefint calibration on stm32mp25
.../devicetree/bindings/iio/adc/st,stm32-adc.yaml | 106 ++++-
arch/arm64/boot/dts/st/stm32mp231.dtsi | 117 ++++++
arch/arm64/boot/dts/st/stm32mp251.dtsi | 119 ++++++
arch/arm64/boot/dts/st/stm32mp257f-ev1.dts | 22 +
drivers/iio/adc/stm32-adc-core.c | 91 ++++-
drivers/iio/adc/stm32-adc-core.h | 23 ++
drivers/iio/adc/stm32-adc.c | 444 ++++++++++++++++++++-
7 files changed, 880 insertions(+), 42 deletions(-)
---
base-commit: 6d31021b7ce470983aeee0727f7417272c0a1cad
change-id: 20260911-adc-stm32mp25-v1-2ac7328559d9
Best regards,
--
Fabrice Gasnier <fabrice.gasnier@foss.st.com>
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH 1/8] dt-bindings: iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25 2026-09-11 15:51 [PATCH 0/8] iio: adc: stm32: add support for stm32mp23 and stm32mp25 Fabrice Gasnier @ 2026-09-11 15:51 ` Fabrice Gasnier 2026-09-11 18:08 ` Marek Vasut 2026-09-11 15:51 ` [PATCH 2/8] iio: adc: stm32-adc: manage characterization voltage diversity Fabrice Gasnier ` (6 subsequent siblings) 7 siblings, 1 reply; 13+ messages in thread From: Fabrice Gasnier @ 2026-09-11 15:51 UTC (permalink / raw) To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin, Alexandre Torgue Cc: Marek Vasut, linux-iio, devicetree, linux-stm32, linux-arm-kernel, linux-kernel, Fabrice Gasnier, Cheick Traore, Olivier Moysan Add stm32mp23 and stm32mp25 compatibles. Three ADCs can be found on these SoCs, split into two blocks: - ADC12 are tightly coupled (common registers). - ADC3 is managed independently. Each ADC is a 12-bits successive approximation analog-to-digital converter, with up to 20 multiplexed channels that can be configured as single ended or differential. New property "st,adc-trigger-sel " allow to make the difference between the ADCs and to apply the correct trigger configuration. STM32 ADC may be in a power domain: allow a single 'power-domains'. Restrict the last channel (19), as it is wired directly to ADC Vref+. It's meaningless to have direct measurement. Internal Vref with calibration data from bsec (nvmem) must be used instead e.g. vrefint. Restrict deprecated properties (st,adc-channels, st,adc-diff-channels, st,min-sample-time-nsecs). Co-developed-by: Cheick Traore <cheick.traore-ext@st.com> Signed-off-by: Cheick Traore <cheick.traore-ext@st.com> Co-developed-by: Olivier Moysan <olivier.moysan@foss.st.com> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com> --- .../devicetree/bindings/iio/adc/st,stm32-adc.yaml | 106 ++++++++++++++++++--- 1 file changed, 94 insertions(+), 12 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml index c4c4575d3fa9..0e0ceaa3d8d6 100644 --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml @@ -23,11 +23,18 @@ maintainers: properties: compatible: - enum: - - st,stm32f4-adc-core - - st,stm32h7-adc-core - - st,stm32mp1-adc-core - - st,stm32mp13-adc-core + oneOf: + - items: + - enum: + - st,stm32f4-adc-core + - st,stm32h7-adc-core + - st,stm32mp1-adc-core + - st,stm32mp13-adc-core + - items: + - enum: + - st,stm32mp23-adc-core + - st,stm32mp25-adc-core + - const: st,stm32-adc-core reg: maxItems: 1 @@ -78,6 +85,9 @@ properties: Phandle to the vdd input voltage. It can be used to supply ADC analog input switches on stm32mp1. + power-domains: + maxItems: 1 + st,syscfg: description: Phandle to system configuration controller. It can be used to control the @@ -215,6 +225,33 @@ allOf: maximum: 75000000 default: 75000000 + - if: + properties: + compatible: + contains: + enum: + - st,stm32mp23-adc-core + - st,stm32mp25-adc-core + + then: + properties: + clocks: + maxItems: 1 + + clock-names: + const: adc + + interrupts: + items: + - description: interrupt line for ADC1 or ADC3 + - description: interrupt line for ADC2 + minItems: 1 + + st,max-clk-rate-hz: + minimum: 700000 + maximum: 70000000 + default: 70000000 + additionalProperties: false required: @@ -239,11 +276,18 @@ patternProperties: properties: compatible: - enum: - - st,stm32f4-adc - - st,stm32h7-adc - - st,stm32mp1-adc - - st,stm32mp13-adc + oneOf: + - items: + - enum: + - st,stm32f4-adc + - st,stm32h7-adc + - st,stm32mp1-adc + - st,stm32mp13-adc + - items: + - enum: + - st,stm32mp23-adc + - st,stm32mp25-adc + - const: st,stm32-adc reg: description: | @@ -294,8 +338,8 @@ patternProperties: List of single-ended channels muxed for this ADC. It can have up to: - 16 channels, numbered from 0 to 15 (for in0..in15) on stm32f4 - 19 channels, numbered from 0 to 18 (for in0..in18) on stm32mp13. - - 20 channels, numbered from 0 to 19 (for in0..in19) on stm32h7 and - stm32mp1. + - 20 channels, numbered from 0 to 19 (for in0..in19) on stm32h7, + stm32mp1, stm32mp23 and stm32mp25. $ref: /schemas/types.yaml#/definitions/uint32-array deprecated: true @@ -344,6 +388,12 @@ patternProperties: items: - const: vrefint + st,adc-trigger-sel: + description: Selects the HW trigger configuration available on this ADC + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + default: 0 + patternProperties: "^channel@([0-9]|1[0-9])$": type: object @@ -486,6 +536,38 @@ patternProperties: maxItems: 19 items: minimum: 40 + + - if: + properties: + compatible: + contains: + enum: + - st,stm32mp23-adc + - st,stm32mp25-adc + + then: + properties: + reg: + enum: + - 0x0 + - 0x100 + + interrupts: + minimum: 0 + maximum: 1 + + assigned-resolution-bits: + enum: [6, 8, 10, 12] + default: 12 + + channel@19: false + st,adc-channels: false + st,adc-diff-channels: false + st,min-sample-time-nsecs: false + + required: + - st,adc-trigger-sel + additionalProperties: false required: -- 2.43.0 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 1/8] dt-bindings: iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25 2026-09-11 15:51 ` [PATCH 1/8] dt-bindings: iio: adc: stm32-adc: " Fabrice Gasnier @ 2026-09-11 18:08 ` Marek Vasut 0 siblings, 0 replies; 13+ messages in thread From: Marek Vasut @ 2026-09-11 18:08 UTC (permalink / raw) To: Fabrice Gasnier, Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin, Alexandre Torgue Cc: linux-iio, devicetree, linux-stm32, linux-arm-kernel, linux-kernel, Cheick Traore, Olivier Moysan On 9/11/26 5:51 PM, Fabrice Gasnier wrote: > Add stm32mp23 and stm32mp25 compatibles. Three ADCs can be found on > these SoCs, split into two blocks: > - ADC12 are tightly coupled (common registers). > - ADC3 is managed independently. > Each ADC is a 12-bits successive approximation analog-to-digital converter, > with up to 20 multiplexed channels that can be configured as single ended > or differential. > > New property "st,adc-trigger-sel " No need for space before " > allow to make the difference between > the ADCs and to apply the correct trigger configuration. I think "st,adc-trigger-sel" needs an example in the example bindings below. > STM32 ADC may be in a power domain: allow a single 'power-domains'. [...] > + st,adc-trigger-sel: > + description: Selects the HW trigger configuration available on this ADC > + $ref: /schemas/types.yaml#/definitions/uint32 > + enum: [0, 1] > + default: 0 What does 0 and 1 mean ? [...] ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 2/8] iio: adc: stm32-adc: manage characterization voltage diversity 2026-09-11 15:51 [PATCH 0/8] iio: adc: stm32: add support for stm32mp23 and stm32mp25 Fabrice Gasnier 2026-09-11 15:51 ` [PATCH 1/8] dt-bindings: iio: adc: stm32-adc: " Fabrice Gasnier @ 2026-09-11 15:51 ` Fabrice Gasnier 2026-09-11 16:10 ` Andy Shevchenko 2026-09-11 15:51 ` [PATCH 3/8] iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25 Fabrice Gasnier ` (5 subsequent siblings) 7 siblings, 1 reply; 13+ messages in thread From: Fabrice Gasnier @ 2026-09-11 15:51 UTC (permalink / raw) To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin, Alexandre Torgue Cc: Marek Vasut, linux-iio, devicetree, linux-stm32, linux-arm-kernel, linux-kernel, Fabrice Gasnier, Olivier Moysan From: Olivier Moysan <olivier.moysan@foss.st.com> The characterization voltage used to calibrate vrefint depends on SoC family. Add vref_charac field in stm32_adc_cfg structure to store the characterization voltage for each SoC, and use it in vref computation formula. Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com> --- drivers/iio/adc/stm32-adc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 5c6c06b269be..183b3474cad1 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -43,7 +43,6 @@ #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_ADC_VREFINT_VOLTAGE 3300 #define STM32_DMA_BUFFER_SIZE PAGE_SIZE @@ -211,6 +210,7 @@ struct stm32_adc; * @set_ovs: routine to set oversampling configuration * @smp_cycles: programmable sampling time (ADC clock cycles) * @ts_int_ch: pointer to array of internal channels minimum sampling time in ns + * @vref_charac: Vref characterization voltage (mv) used to measure vrefint calibration value */ struct stm32_adc_cfg { const struct stm32_adc_regspec *regs; @@ -230,6 +230,7 @@ struct stm32_adc_cfg { void (*set_ovs)(struct iio_dev *indio_dev, u32 ovs_idx); const unsigned int *smp_cycles; const unsigned int *ts_int_ch; + const int vref_charac; }; /** @@ -1610,7 +1611,7 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev, ret = -EINVAL; if (mask == IIO_CHAN_INFO_PROCESSED) - *val = STM32_ADC_VREFINT_VOLTAGE * adc->vrefint.vrefint_cal / *val; + *val = adc->cfg->vref_charac * adc->vrefint.vrefint_cal / *val; iio_device_release_direct(indio_dev); return ret; @@ -2702,6 +2703,7 @@ static const struct stm32_adc_cfg stm32f4_adc_cfg = { .stop_conv = stm32f4_adc_stop_conv, .smp_cycles = stm32f4_adc_smp_cycles, .irq_clear = stm32f4_adc_irq_clear, + .vref_charac = 3300, }; static const unsigned int stm32_adc_min_ts_h7[] = { 0, 0, 0, 4300, 9000 }; @@ -2723,6 +2725,7 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = { .irq_clear = stm32h7_adc_irq_clear, .ts_int_ch = stm32_adc_min_ts_h7, .set_ovs = stm32h7_adc_set_ovs, + .vref_charac = 3300, }; static const unsigned int stm32_adc_min_ts_mp1[] = { 100, 100, 100, 4300, 9800 }; @@ -2745,6 +2748,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = { .irq_clear = stm32h7_adc_irq_clear, .ts_int_ch = stm32_adc_min_ts_mp1, .set_ovs = stm32h7_adc_set_ovs, + .vref_charac = 3300, }; static const unsigned int stm32_adc_min_ts_mp13[] = { 100, 0, 0, 4300, 9800 }; @@ -2763,6 +2767,7 @@ static const struct stm32_adc_cfg stm32mp13_adc_cfg = { .irq_clear = stm32h7_adc_irq_clear, .ts_int_ch = stm32_adc_min_ts_mp13, .set_ovs = stm32mp13_adc_set_ovs, + .vref_charac = 3300, }; static const struct of_device_id stm32_adc_of_match[] = { -- 2.43.0 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 2/8] iio: adc: stm32-adc: manage characterization voltage diversity 2026-09-11 15:51 ` [PATCH 2/8] iio: adc: stm32-adc: manage characterization voltage diversity Fabrice Gasnier @ 2026-09-11 16:10 ` Andy Shevchenko 0 siblings, 0 replies; 13+ messages in thread From: Andy Shevchenko @ 2026-09-11 16:10 UTC (permalink / raw) To: Fabrice Gasnier Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin, Alexandre Torgue, Marek Vasut, linux-iio, devicetree, linux-stm32, linux-arm-kernel, linux-kernel, Olivier Moysan On Fri, Sep 11, 2026 at 05:51:24PM +0200, Fabrice Gasnier wrote: > The characterization voltage used to calibrate vrefint depends > on SoC family. Add vref_charac field in stm32_adc_cfg structure > to store the characterization voltage for each SoC, and use it > in vref computation formula. Vref ... > + * @vref_charac: Vref characterization voltage (mv) used to measure vrefint calibration value vref_char_mV or alike (use unit suffix and you may drop that mv in the parentheses). ... > + const int vref_charac; What does const buy you here? -- With Best Regards, Andy Shevchenko ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 3/8] iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25 2026-09-11 15:51 [PATCH 0/8] iio: adc: stm32: add support for stm32mp23 and stm32mp25 Fabrice Gasnier 2026-09-11 15:51 ` [PATCH 1/8] dt-bindings: iio: adc: stm32-adc: " Fabrice Gasnier 2026-09-11 15:51 ` [PATCH 2/8] iio: adc: stm32-adc: manage characterization voltage diversity Fabrice Gasnier @ 2026-09-11 15:51 ` Fabrice Gasnier 2026-09-11 16:22 ` Andy Shevchenko 2026-09-11 15:51 ` [PATCH 4/8] arm64: dts: st: add vrefint calibration on stm32mp23 Fabrice Gasnier ` (4 subsequent siblings) 7 siblings, 1 reply; 13+ messages in thread From: Fabrice Gasnier @ 2026-09-11 15:51 UTC (permalink / raw) To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin, Alexandre Torgue Cc: Marek Vasut, linux-iio, devicetree, linux-stm32, linux-arm-kernel, linux-kernel, Fabrice Gasnier, Cheick Traore, Olivier Moysan Add support for STM32MP23 STM32MP25 SoCs. They have 3 ADCs, split into two blocks: - ADC12 (ADC1 & ADC2) are tightly coupled. - ADC3 is managed independently. Trigger list slightly changes between STM32MP23 and STM32MP25. Each ADC is a 12-bits successive approximation analog-to-digital converter, with up to 20 multiplexed channels that can be configured as single ended or differential. ADC resolution ranges from 6 to 12 bits. It introduces diversity regarding IRQs, clocks, software calibration procedure, internal voltage channels, sampling time (prescaler) and trigger list. Most of the architecture, and the driver engine remains similar. So, handle the differences w.r.t. other STM32 ADCs family with a dedicated compatible and compatible data. Trigger list depends on ADC instance, retrieved with "st,adc-trigger-sel" property. Add support for vddcpu and vddgpu internal channels on stm32mp25. These channels are enabled by using ADC option register. Add VDDGPU to the list of supported channels in this driver and made available on stm32mp25. So update sampling information for all variants (with zero'ed data when it's not available). Co-developed-by: Cheick Traore <cheick.traore-ext@st.com> Signed-off-by: Cheick Traore <cheick.traore-ext@st.com> Co-developed-by: Olivier Moysan <olivier.moysan@foss.st.com> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com> --- drivers/iio/adc/stm32-adc-core.c | 91 ++++++-- drivers/iio/adc/stm32-adc-core.h | 23 +++ drivers/iio/adc/stm32-adc.c | 435 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 521 insertions(+), 28 deletions(-) diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index a42d82d61cb8..e3c3ac7f748d 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -50,6 +50,8 @@ * @ovr_msk: array of ovr (overrun flag) masks in csr for adc1..n * @ier: interrupt enable register offset for each adc * @eocie_msk: end of conversion interrupt enable mask in @ier + * @presc_msk: prescaler mask in ccr + * @presc_shift: prescaler bit shift in ccr */ struct stm32_adc_common_regs { u32 csr; @@ -58,6 +60,8 @@ struct stm32_adc_common_regs { u32 ovr_msk[STM32_ADC_MAX_ADCS]; u32 ier; u32 eocie_msk; + u32 presc_msk; + u32 presc_shift; }; struct stm32_adc_priv; @@ -66,6 +70,8 @@ struct stm32_adc_priv; * struct stm32_adc_priv_cfg - stm32 core compatible configuration data * @regs: common registers for all instances * @clk_sel: clock selection routine + * @presc: clock prescaler array + * @num_presc: number of clock prescalers in presc array * @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet) * @ipid: adc identification number * @has_syscfg: SYSCFG capability flags @@ -75,6 +81,8 @@ struct stm32_adc_priv; struct stm32_adc_priv_cfg { const struct stm32_adc_common_regs *regs; int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *); + int *presc; + int num_presc; u32 max_clk_rate_hz; u32 ipid; unsigned int has_syscfg; @@ -86,6 +94,7 @@ struct stm32_adc_priv_cfg { * struct stm32_adc_priv - stm32 ADC core private data * @irq: irq(s) for ADC block * @nb_adc_max: actual maximum number of instance per ADC block + * @nb_irqs: number of IRQs in the ADC block * @domain: irq domain reference * @aclk: clock reference for the analog circuitry * @bclk: bus clock common for all ADCs, depends on part used @@ -104,6 +113,7 @@ struct stm32_adc_priv_cfg { struct stm32_adc_priv { int irq[STM32_ADC_MAX_ADCS]; unsigned int nb_adc_max; + unsigned int nb_irqs; struct irq_domain *domain; struct clk *aclk; struct clk *bclk; @@ -128,20 +138,23 @@ static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com) /* STM32F4 ADC internal common clock prescaler division ratios */ static int stm32f4_pclk_div[] = {2, 4, 6, 8}; +/* STM32MP25 ADC internal common clock prescaler division ratios */ +static int stm32mp25_presc_div[] = {1, 2, 4, 6, 8, 10, 12, 16, 32, 64, 128, 256}; + /** - * stm32f4_adc_clk_sel() - Select stm32f4 ADC common clock prescaler + * stm32_adc_clk_sel() - Select stm32f4 ADC common clock prescaler * @pdev: platform device * @priv: stm32 ADC core private data * Select clock prescaler used for analog conversions, before using ADC. */ -static int stm32f4_adc_clk_sel(struct platform_device *pdev, - struct stm32_adc_priv *priv) +static int stm32_adc_clk_sel(struct platform_device *pdev, + struct stm32_adc_priv *priv) { unsigned long rate; u32 val; int i; - /* stm32f4 has one clk input for analog (mandatory), enforce it here */ + /* stm32f4/mp25 has one clk input for analog (mandatory), enforce it here */ if (!priv->aclk) { dev_err(&pdev->dev, "No 'adc' clock found\n"); return -ENOENT; @@ -153,20 +166,20 @@ static int stm32f4_adc_clk_sel(struct platform_device *pdev, return -EINVAL; } - for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) { - if ((rate / stm32f4_pclk_div[i]) <= priv->max_clk_rate) + for (i = 0; i < priv->cfg->num_presc; i++) { + if ((rate / priv->cfg->presc[i]) <= priv->max_clk_rate) break; } - if (i >= ARRAY_SIZE(stm32f4_pclk_div)) { + if (i >= priv->cfg->num_presc) { dev_err(&pdev->dev, "adc clk selection failed\n"); return -EINVAL; } - priv->common.rate = rate / stm32f4_pclk_div[i]; - val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR); - val &= ~STM32F4_ADC_ADCPRE_MASK; - val |= i << STM32F4_ADC_ADCPRE_SHIFT; - writel_relaxed(val, priv->common.base + STM32F4_ADC_CCR); + priv->common.rate = rate / priv->cfg->presc[i]; + val = readl_relaxed(priv->common.base + priv->cfg->regs->ccr); + val &= ~priv->cfg->regs->presc_msk; + val |= i << priv->cfg->regs->presc_shift; + writel_relaxed(val, priv->common.base + priv->cfg->regs->ccr); dev_dbg(&pdev->dev, "Using analog clock source at %ld kHz\n", priv->common.rate / 1000); @@ -314,6 +327,8 @@ static const struct stm32_adc_common_regs stm32f4_adc_common_regs = { .ovr_msk = { STM32F4_OVR1, STM32F4_OVR2, STM32F4_OVR3 }, .ier = STM32F4_ADC_CR1, .eocie_msk = STM32F4_EOCIE, + .presc_msk = STM32F4_ADC_ADCPRE_MASK, + .presc_shift = STM32F4_ADC_ADCPRE_SHIFT, }; /* STM32H7 common registers definitions */ @@ -336,6 +351,18 @@ static const struct stm32_adc_common_regs stm32mp13_adc_common_regs = { .eocie_msk = STM32H7_EOCIE, }; +/* STM32MP25 common registers definitions */ +static const struct stm32_adc_common_regs stm32mp25_adc_common_regs = { + .csr = STM32H7_ADC_CSR, + .ccr = STM32H7_ADC_CCR, + .eoc_msk = { STM32H7_EOC_MST, STM32H7_EOC_SLV}, + .ovr_msk = { STM32H7_OVR_MST, STM32H7_OVR_SLV}, + .ier = STM32H7_ADC_IER, + .eocie_msk = STM32H7_EOCIE, + .presc_msk = STM32H7_PRESC_MASK, + .presc_shift = STM32H7_PRESC_SHIFT, +}; + static const unsigned int stm32_adc_offset[STM32_ADC_MAX_ADCS] = { 0, STM32_ADC_OFFSET, STM32_ADC_OFFSET * 2, }; @@ -413,8 +440,10 @@ static int stm32_adc_irq_probe(struct platform_device *pdev, * Interrupt(s) must be provided, depending on the compatible: * - stm32f4/h7 shares a common interrupt line. * - stm32mp1, has one line per ADC + * - stm32mp25, has a dual ADC12 and standalone ADC3. Each ADC has an IRQ + * (two for ADC12, one for ADC3) */ - for (i = 0; i < priv->cfg->num_irqs; i++) { + for (i = 0; i < priv->nb_irqs; i++) { priv->irq[i] = platform_get_irq(pdev, i); if (priv->irq[i] < 0) return priv->irq[i]; @@ -429,7 +458,7 @@ static int stm32_adc_irq_probe(struct platform_device *pdev, return -ENOMEM; } - for (i = 0; i < priv->cfg->num_irqs; i++) + for (i = 0; i < priv->nb_irqs; i++) irq_set_chained_handler_and_data(priv->irq[i], stm32_adc_irq_handler, priv); @@ -446,7 +475,7 @@ static void stm32_adc_irq_remove(struct platform_device *pdev, irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq)); irq_domain_remove(priv->domain); - for (i = 0; i < priv->cfg->num_irqs; i++) + for (i = 0; i < priv->nb_irqs; i++) irq_set_chained_handler(priv->irq[i], NULL); } @@ -693,6 +722,17 @@ static int stm32_adc_probe_identification(struct platform_device *pdev, return -EINVAL; } + if (priv->cfg->ipid == STM32MP25_IPIDR_NUMBER) { + /* + * ADC diversity on STM32MP25 is handled here. There may be: + * - 2 IRQs for ADC12 block + * - 1 IRQ for ADC3 block. + * There cannot be more IRQs than the actual number of ADCs. + */ + if (priv->nb_irqs > priv->nb_adc_max) + priv->nb_irqs = priv->nb_adc_max; + } + val = readl_relaxed(priv->common.base + STM32MP1_ADC_VERR); dev_dbg(&pdev->dev, "ADC version: %lu.%lu\n", FIELD_GET(STM32MP1_MAJREV_MASK, val), @@ -720,6 +760,7 @@ static int stm32_adc_probe(struct platform_device *pdev) priv->cfg = device_get_match_data(dev); priv->nb_adc_max = priv->cfg->num_adcs; + priv->nb_irqs = priv->cfg->num_irqs; spin_lock_init(&priv->common.lock); priv->common.base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); @@ -850,7 +891,9 @@ static DEFINE_RUNTIME_DEV_PM_OPS(stm32_adc_core_pm_ops, static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = { .regs = &stm32f4_adc_common_regs, - .clk_sel = stm32f4_adc_clk_sel, + .clk_sel = stm32_adc_clk_sel, + .presc = stm32f4_pclk_div, + .num_presc = ARRAY_SIZE(stm32f4_pclk_div), .max_clk_rate_hz = 36000000, .num_irqs = 1, .num_adcs = 3, @@ -882,6 +925,16 @@ static const struct stm32_adc_priv_cfg stm32mp13_adc_priv_cfg = { .num_irqs = 1, }; +static const struct stm32_adc_priv_cfg stm32mp25_adc_priv_cfg = { + .regs = &stm32mp25_adc_common_regs, + .clk_sel = stm32_adc_clk_sel, + .presc = stm32mp25_presc_div, + .num_presc = ARRAY_SIZE(stm32mp25_presc_div), + .max_clk_rate_hz = 70000000, + .ipid = STM32MP25_IPIDR_NUMBER, + .num_irqs = 2, /* 2 IRQs for ADC12, 1 irq for ADC3 */ +}; + static const struct of_device_id stm32_adc_of_match[] = { { .compatible = "st,stm32f4-adc-core", @@ -895,6 +948,12 @@ static const struct of_device_id stm32_adc_of_match[] = { }, { .compatible = "st,stm32mp13-adc-core", .data = (void *)&stm32mp13_adc_priv_cfg + }, { + .compatible = "st,stm32mp23-adc-core", + .data = (void *)&stm32mp25_adc_priv_cfg + }, { + .compatible = "st,stm32mp25-adc-core", + .data = (void *)&stm32mp25_adc_priv_cfg }, { }, }; diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h index db50a9f3b922..0f40450844e9 100644 --- a/drivers/iio/adc/stm32-adc-core.h +++ b/drivers/iio/adc/stm32-adc-core.h @@ -121,6 +121,9 @@ #define STM32MP13_ADC_CALFACT 0xB4 #define STM32MP13_ADC2_OR 0xC8 +/* STM32MP25 - ADC2 and ADC3 option register */ +#define STM32MP25_ADC23_OR 0xD0 + /* 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) @@ -257,8 +260,28 @@ enum stm32h7_adc_dmngt { #define STM32MP13_OP1 BIT(1) #define STM32MP13_OP0 BIT(0) +/* STM32MP25 - Registers for each ADC instance */ +#define STM32MP25_ADC_CALFACT 0xC4 + +/* STM32MP25_ADC_CALFACT - bit fields */ +#define STM32MP25_CALFACT_CALADDOS BIT(31) +#define STM32MP25_CALFACT_D_MASK GENMASK(24, 16) +#define STM32MP25_CALFACT_S_MASK GENMASK(8, 0) + +/* STM32MP25_ADC_CFGR specific bit fields */ +#define STM32MP25_RES_SHIFT 2 +#define STM32MP25_RES_MASK GENMASK(3, 2) + +/* STM32MP25_ADC23_OR - specific bit fields */ +#define STM32MP25_VDDGPUEN BIT(4) +#define STM32MP25_VDDCPUEN BIT(3) +#define STM32MP25_VDDCOREEN BIT(2) +#define STM32MP25_SELBG BIT(1) +#define STM32MP25_SELREF BIT(0) + #define STM32MP15_IPIDR_NUMBER 0x00110005 #define STM32MP13_IPIDR_NUMBER 0x00110006 +#define STM32MP25_IPIDR_NUMBER 0x00110008 /** * 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 183b3474cad1..42827ce489dd 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -33,6 +33,9 @@ /* Number of linear calibration shadow registers / LINCALRDYW control bits */ #define STM32H7_LINCALFACT_NUM 6 +/* Number of loops in the calibration procedure to average data on STM32MP25 */ +#define STM32MP25_CALIB_LOOP 8 + /* BOOST bit must be set on STM32H7 when ADC clock is above 20MHz */ #define STM32H7_BOOST_CLKRATE 20000000UL @@ -77,6 +80,14 @@ enum stm32_adc_extsel { STM32_EXT18, STM32_EXT19, STM32_EXT20, + STM32_EXT21, + STM32_EXT22, + STM32_EXT23, + STM32_EXT24, + STM32_EXT25, + STM32_EXT26, + STM32_EXT27, + STM32_EXT28, }; enum stm32_adc_int_ch { @@ -86,6 +97,7 @@ enum stm32_adc_int_ch { STM32_ADC_INT_CH_VDDQ_DDR, STM32_ADC_INT_CH_VREFINT, STM32_ADC_INT_CH_VBAT, + STM32_ADC_INT_CH_VDDGPU, STM32_ADC_INT_CH_NB, }; @@ -105,6 +117,7 @@ static const struct stm32_adc_ic stm32_adc_ic[STM32_ADC_INT_CH_NB] = { { "vddq_ddr", STM32_ADC_INT_CH_VDDQ_DDR }, { "vrefint", STM32_ADC_INT_CH_VREFINT }, { "vbat", STM32_ADC_INT_CH_VBAT }, + { "vddgpu", STM32_ADC_INT_CH_VDDGPU }, }; /** @@ -165,6 +178,7 @@ struct stm32_adc_vrefint { * @smp_bits: smpr1 & smpr2 index and bitfields * @or_vddcore: option register & vddcore bitfield * @or_vddcpu: option register & vddcpu bitfield + * @or_vddgpu: option register & vddgpu bitfield * @or_vddq_ddr: option register & vddq_ddr bitfield * @ccr_vbat: common register & vbat bitfield * @ccr_vref: common register & vrefint bitfield @@ -184,6 +198,7 @@ struct stm32_adc_regspec { const struct stm32_adc_regs *smp_bits; const struct stm32_adc_regs or_vddcore; const struct stm32_adc_regs or_vddcpu; + const struct stm32_adc_regs or_vddgpu; const struct stm32_adc_regs or_vddq_ddr; const struct stm32_adc_regs ccr_vbat; const struct stm32_adc_regs ccr_vref; @@ -202,6 +217,7 @@ struct stm32_adc; * @has_linearcal: linear calibration support flag * @has_presel: channel preselection support flag * @has_oversampling: oversampling support flag + * @has_vregen: voltage regulator enable/disable flag * @prepare: optional prepare routine (power-up, enable) * @start_conv: routine to start conversions * @stop_conv: routine to stop conversions @@ -215,13 +231,14 @@ struct stm32_adc; struct stm32_adc_cfg { const struct stm32_adc_regspec *regs; const struct stm32_adc_info *adc_info; - const struct stm32_adc_trig_info *trigs; + const struct stm32_adc_trig_info *trigs[2]; bool clk_required; bool has_vregready; bool has_boostmode; bool has_linearcal; bool has_presel; bool has_oversampling; + bool has_vregen; int (*prepare)(struct iio_dev *); void (*start_conv)(struct iio_dev *, bool dma); void (*stop_conv)(struct iio_dev *); @@ -261,6 +278,7 @@ struct stm32_adc_cfg { * @int_ch: internal channel indexes array * @nsmps: number of channels with optional sample time * @ovs_idx: current oversampling ratio index (in oversampling array) + * @trigs: trigger list in use */ struct stm32_adc { struct stm32_adc_common *common; @@ -289,6 +307,7 @@ struct stm32_adc { int int_ch[STM32_ADC_INT_CH_NB]; int nsmps; int ovs_idx; + const struct stm32_adc_trig_info *trigs; }; struct stm32_adc_diff_channel { @@ -355,6 +374,20 @@ static const struct stm32_adc_info stm32mp13_adc_info = { .num_ovs = ARRAY_SIZE(stm32mp13_adc_oversampling_avail), }; +/* + * stm32mp25 can have up to 20 channels. + * Keep channel 19 (VREF+) hidden on STM32MP25: it is the ADC reference + * supply itself, so exposing it as a regular input is not meaningful. + * Reference voltage must be derived from VREFINT calibration data. + */ +static const struct stm32_adc_info stm32mp25_adc_info = { + .max_channels = STM32_ADC_CH_MAX - 1, + .resolutions = stm32f4_adc_resolutions, + .oversampling = stm32h7_adc_oversampling_avail, + .num_res = ARRAY_SIZE(stm32f4_adc_resolutions), + .num_ovs = ARRAY_SIZE(stm32h7_adc_oversampling_avail), +}; + /* * stm32f4_sq - describe regular sequence registers * - L: sequence len (register & bit field) @@ -545,6 +578,156 @@ static const struct stm32_adc_regspec stm32h7_adc_regspec = { .smp_bits = stm32h7_smp_bits, }; +/* STM32MP23 external trigger sources for ADC12 */ +static struct stm32_adc_trig_info stm32mp23_adc12_trigs[] = { + { TIM1_TRGO, STM32_EXT0 }, + { TIM1_TRGO2, STM32_EXT1 }, + { TIM8_TRGO, STM32_EXT2 }, + { TIM8_TRGO2, STM32_EXT3 }, + { TIM2_TRGO, STM32_EXT6 }, + { TIM3_TRGO, STM32_EXT7 }, + { TIM4_TRGO, STM32_EXT8 }, + { TIM5_TRGO, STM32_EXT9 }, + { TIM6_TRGO, STM32_EXT10 }, + { TIM15_TRGO, STM32_EXT11 }, + { TIM1_CH1, STM32_EXT12 }, + { TIM1_CH2, STM32_EXT13 }, + { TIM1_CH3, STM32_EXT14 }, + { TIM2_CH2, STM32_EXT18 }, + { TIM3_CH4, STM32_EXT19 }, + { TIM4_CH4, STM32_EXT20 }, + { TIM5_CH1, STM32_EXT21 }, + { TIM12_CH1, STM32_EXT22 }, + { LPTIM1_CH1, STM32_EXT24 }, + { LPTIM2_CH1, STM32_EXT25 }, + { LPTIM3_CH1, STM32_EXT26 }, + { LPTIM4_CH1, STM32_EXT27 }, + { LPTIM5_OUT, STM32_EXT28 }, + {}, +}; + +/* STM32MP23 external trigger sources for ADC3 */ +static struct stm32_adc_trig_info stm32mp23_adc3_trigs[] = { + { TIM1_TRGO, STM32_EXT0 }, + { TIM1_TRGO2, STM32_EXT1 }, + { TIM8_TRGO, STM32_EXT2 }, + { TIM8_TRGO2, STM32_EXT3 }, + { TIM2_TRGO, STM32_EXT6 }, + { TIM3_TRGO, STM32_EXT7 }, + { TIM4_TRGO, STM32_EXT8 }, + { TIM5_TRGO, STM32_EXT9 }, + { TIM6_TRGO, STM32_EXT10 }, + { TIM7_TRGO, STM32_EXT11 }, + { TIM15_TRGO, STM32_EXT12 }, + { TIM17_OC1, STM32_EXT13 }, + { TIM1_CH3, STM32_EXT14 }, + { TIM8_CH1, STM32_EXT15 }, + { TIM2_CH1, STM32_EXT17 }, + { TIM2_CH3, STM32_EXT18 }, + { TIM3_CH1, STM32_EXT19 }, + { TIM4_CH1, STM32_EXT20 }, + { TIM5_CH3, STM32_EXT21 }, + { TIM12_CH1, STM32_EXT22 }, + { LPTIM1_CH1, STM32_EXT24 }, + { LPTIM2_CH1, STM32_EXT25 }, + { LPTIM3_CH1, STM32_EXT26 }, + { LPTIM4_CH1, STM32_EXT27 }, + { LPTIM5_OUT, STM32_EXT28 }, + {}, +}; + +/* STM32MP25 external trigger sources for ADC12 */ +static struct stm32_adc_trig_info stm32mp25_adc12_trigs[] = { + { TIM1_TRGO, STM32_EXT0 }, + { TIM1_TRGO2, STM32_EXT1 }, + { TIM8_TRGO, STM32_EXT2 }, + { TIM8_TRGO2, STM32_EXT3 }, + { TIM20_TRGO, STM32_EXT4 }, + { TIM20_TRGO2, STM32_EXT5 }, + { TIM2_TRGO, STM32_EXT6 }, + { TIM3_TRGO, STM32_EXT7 }, + { TIM4_TRGO, STM32_EXT8 }, + { TIM5_TRGO, STM32_EXT9 }, + { TIM6_TRGO, STM32_EXT10 }, + { TIM15_TRGO, STM32_EXT11 }, + { TIM1_CH1, STM32_EXT12 }, + { TIM1_CH2, STM32_EXT13 }, + { TIM1_CH3, STM32_EXT14 }, + { TIM20_OC1, STM32_EXT15 }, + { TIM20_OC2, STM32_EXT16 }, + { TIM20_OC3, STM32_EXT17 }, + { TIM2_CH2, STM32_EXT18 }, + { TIM3_CH4, STM32_EXT19 }, + { TIM4_CH4, STM32_EXT20 }, + { TIM5_CH1, STM32_EXT21 }, + { TIM12_CH1, STM32_EXT22 }, + { LPTIM1_CH1, STM32_EXT24 }, + { LPTIM2_CH1, STM32_EXT25 }, + { LPTIM3_CH1, STM32_EXT26 }, + { LPTIM4_CH1, STM32_EXT27 }, + { LPTIM5_OUT, STM32_EXT28 }, + {}, +}; + +/* STM32MP25 external trigger sources for ADC3 */ +static struct stm32_adc_trig_info stm32mp25_adc3_trigs[] = { + { TIM1_TRGO, STM32_EXT0 }, + { TIM1_TRGO2, STM32_EXT1 }, + { TIM8_TRGO, STM32_EXT2 }, + { TIM8_TRGO2, STM32_EXT3 }, + { TIM20_TRGO, STM32_EXT4 }, + { TIM20_TRGO2, STM32_EXT5 }, + { TIM2_TRGO, STM32_EXT6 }, + { TIM3_TRGO, STM32_EXT7 }, + { TIM4_TRGO, STM32_EXT8 }, + { TIM5_TRGO, STM32_EXT9 }, + { TIM6_TRGO, STM32_EXT10 }, + { TIM7_TRGO, STM32_EXT11 }, + { TIM15_TRGO, STM32_EXT12 }, + { TIM17_OC1, STM32_EXT13 }, + { TIM1_CH3, STM32_EXT14 }, + { TIM8_CH1, STM32_EXT15 }, + { TIM20_OC1, STM32_EXT16 }, + { TIM2_CH1, STM32_EXT17 }, + { TIM2_CH3, STM32_EXT18 }, + { TIM3_CH1, STM32_EXT19 }, + { TIM4_CH1, STM32_EXT20 }, + { TIM5_CH3, STM32_EXT21 }, + { TIM12_CH1, STM32_EXT22 }, + { LPTIM1_CH1, STM32_EXT24 }, + { LPTIM2_CH1, STM32_EXT25 }, + { LPTIM3_CH1, STM32_EXT26 }, + { LPTIM4_CH1, STM32_EXT27 }, + { LPTIM5_OUT, STM32_EXT28 }, + {}, +}; + +/* STM32MP25 programmable sampling time (ADC clock cycles, rounded down) */ +static const unsigned int stm32mp25_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = { + 2, 3, 7, 12, 24, 47, 247, 1501, +}; + +static const struct stm32_adc_regspec stm32mp25_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, STM32MP25_RES_MASK, STM32MP25_RES_SHIFT }, + .difsel = { STM32H7_ADC_DIFSEL, STM32H7_DIFSEL_MASK}, + .smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 }, + .smp_bits = stm32h7_smp_bits, + .or_vddcore = { STM32MP25_ADC23_OR, STM32MP25_VDDCOREEN }, + .or_vddcpu = { STM32MP25_ADC23_OR, STM32MP25_VDDCPUEN }, + .or_vddgpu = { STM32MP25_ADC23_OR, STM32MP25_VDDGPUEN }, + .ccr_vbat = { STM32H7_ADC_CCR, STM32H7_VBATEN }, + .ccr_vref = { STM32H7_ADC_CCR, STM32H7_VREFEN }, +}; + /* 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, @@ -769,6 +952,11 @@ static void stm32_adc_int_ch_enable(struct iio_dev *indio_dev) stm32_adc_set_bits_common(adc, adc->cfg->regs->ccr_vbat.reg, adc->cfg->regs->ccr_vbat.mask); break; + case STM32_ADC_INT_CH_VDDGPU: + dev_dbg(&indio_dev->dev, "Enable VDDGPU\n"); + stm32_adc_set_bits(adc, adc->cfg->regs->or_vddgpu.reg, + adc->cfg->regs->or_vddgpu.mask); + break; } } } @@ -802,6 +990,10 @@ static void stm32_adc_int_ch_disable(struct stm32_adc *adc) stm32_adc_clr_bits_common(adc, adc->cfg->regs->ccr_vbat.reg, adc->cfg->regs->ccr_vbat.mask); break; + case STM32_ADC_INT_CH_VDDGPU: + stm32_adc_clr_bits(adc, adc->cfg->regs->or_vddgpu.reg, + adc->cfg->regs->or_vddgpu.mask); + break; } } } @@ -970,7 +1162,8 @@ static int stm32h7_adc_exit_pwr_down(struct iio_dev *indio_dev) /* 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); + if (adc->cfg->has_vregen) + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADVREGEN); if (adc->cfg->has_boostmode && adc->common->rate > STM32H7_BOOST_CLKRATE) @@ -1310,6 +1503,163 @@ static void stm32h7_adc_unprepare(struct iio_dev *indio_dev) stm32h7_adc_enter_pwr_down(adc); } +/* + * STM32MP25 offset calibration software procedure. Basically the calibration routine is + * expected to average (for example) 8 samples in calibration mode, for single-ended and + * differential channels, to calibrate the zero offset. In case offset is "negative", an + * additional offset can be added, to determine calibration factor. It must be kept later + * for all conversions. + */ +static int stm32mp25_adc_calib_get_average_data(struct iio_dev *indio_dev, u32 *average) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + const struct stm32_adc_regspec *regs = adc->cfg->regs; + u32 val, avg = 0; + int i, ret; + + /* Repeat several conversions in calibration mode, average the results */ + for (i = 0; i < STM32MP25_CALIB_LOOP; i++) { + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTART); + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + !(val & (STM32H7_ADSTART)), + 100, STM32_ADC_TIMEOUT_US); + if (ret) { + dev_err(&indio_dev->dev, "calibration average error %d\n", ret); + return ret; + } + + val = stm32_adc_readl(adc, regs->dr); + dev_vdbg(&indio_dev->dev, "dr[%d]=0x%08x\n", i, val); + avg += val; + } + + *average = DIV_ROUND_CLOSEST(avg, STM32MP25_CALIB_LOOP); + dev_vdbg(&indio_dev->dev, "average=0x%08x\n", *average); + + return 0; +} + +static int stm32mp25_adc_calib(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + u32 calfact = 0, average; + int ret; + + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL); + /* Clears CALADDOS (and old calibration data if any) */ + stm32_adc_writel(adc, STM32MP25_ADC_CALFACT, 0); + /* Select single ended input calibration */ + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALDIF); + /* Use default resolution (e.g. 12 bits) */ + stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR, STM32MP25_RES_MASK); + +retry: + ret = stm32mp25_adc_calib_get_average_data(indio_dev, &average); + if (ret) + goto out; + + /* If the averaged data is zero, retry with additional offset (set CALADDOS) */ + if (!average) { + if (!calfact) { + /* Averaged data is zero, retry with additional offset */ + calfact = STM32MP25_CALFACT_CALADDOS; + stm32_adc_writel(adc, STM32MP25_ADC_CALFACT, calfact); + goto retry; + } + /* Averaged data is still zero with additional offset, just warn about it */ + dev_warn(&indio_dev->dev, "Single-ended calibration average: 0\n"); + } + + calfact |= FIELD_PREP(STM32MP25_CALFACT_S_MASK, average); + + /* Select differential input calibration (keep previous CALADDOS value) */ + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALDIF); + ret = stm32mp25_adc_calib_get_average_data(indio_dev, &average); + if (ret) + goto out; + + /* + * If the averaged data is below 0x800 (half value in 12-bits mode), + * retry with additional offset + */ + if (average < BIT(adc->cfg->adc_info->resolutions[0] - 1)) { + if (!(calfact & STM32MP25_CALFACT_CALADDOS)) { + /* Retry the whole calibration with additional offset */ + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALDIF); + calfact = STM32MP25_CALFACT_CALADDOS; + stm32_adc_writel(adc, STM32MP25_ADC_CALFACT, calfact); + goto retry; + } + /* + * Averaged data is still below center value. It needs to be clamped to zero, + * so don't use the result here, warn about it. + */ + dev_warn(&indio_dev->dev, "Differential calibration clamped(0): 0x%x\n", average); + } else { + calfact |= FIELD_PREP(STM32MP25_CALFACT_D_MASK, average); + } + + stm32_adc_writel(adc, STM32MP25_ADC_CALFACT, calfact); + + dev_dbg(&indio_dev->dev, "set calfact_s=0x%03lx, calfact_d=0x%03lx, calados=%ld\n", + FIELD_GET(STM32MP25_CALFACT_S_MASK, calfact), + FIELD_GET(STM32MP25_CALFACT_D_MASK, calfact), + FIELD_GET(STM32MP25_CALFACT_CALADDOS, calfact)); +out: + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL); + stm32_adc_set_res(adc); + + return ret; +} + +static int stm32mp25_adc_prepare(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int ret; + + ret = stm32h7_adc_exit_pwr_down(indio_dev); + if (ret) + return ret; + + /* Must enable the ADC before running software-assisted calibration */ + ret = stm32h7_adc_enable(indio_dev); + if (ret) + goto pwr_dwn; + + /* Always run offset calibration */ + ret = stm32mp25_adc_calib(indio_dev); + if (ret) + goto adc_dis; + + stm32_adc_int_ch_enable(indio_dev); + + stm32_adc_writel(adc, adc->cfg->regs->difsel.reg, adc->difsel); + + if (adc->cfg->has_presel) + stm32_adc_writel(adc, STM32H7_ADC_PCSEL, adc->pcsel); + + return 0; + +adc_dis: + stm32h7_adc_disable(indio_dev); +pwr_dwn: + stm32h7_adc_enter_pwr_down(adc); + + return ret; +} + +static void stm32mp25_adc_unprepare(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + /* Undo things in the reverse order */ + if (adc->cfg->has_presel) + stm32_adc_writel(adc, STM32H7_ADC_PCSEL, 0); + stm32_adc_int_ch_disable(adc); + stm32h7_adc_disable(indio_dev); + stm32h7_adc_enter_pwr_down(adc); +} + /** * stm32_adc_conf_scan_seq() - Build regular channels scan sequence * @indio_dev: IIO device @@ -1379,15 +1729,15 @@ static int stm32_adc_get_trig_extsel(struct iio_dev *indio_dev, int i; /* lookup triggers registered by stm32 timer trigger driver */ - for (i = 0; adc->cfg->trigs[i].name; i++) { + for (i = 0; adc->trigs[i].name; i++) { /** * Checking both stm32 timer trigger type and trig name * should be safe against arbitrary trigger names. */ if ((is_stm32_timer_trigger(trig) || is_stm32_lptim_trigger(trig)) && - !strcmp(adc->cfg->trigs[i].name, trig->name)) { - return adc->cfg->trigs[i].extsel; + !strcmp(adc->trigs[i].name, trig->name)) { + return adc->trigs[i].extsel; } } @@ -2291,6 +2641,11 @@ static int stm32_adc_populate_int_ch(struct iio_dev *indio_dev, const char *ch_n dev_warn(&indio_dev->dev, "%s channel not available\n", ch_name); break; + case STM32_ADC_INT_CH_VDDGPU: + if (!adc->cfg->regs->or_vddgpu.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) { @@ -2506,6 +2861,7 @@ static int stm32_adc_probe(struct platform_device *pdev) irqreturn_t (*handler)(int irq, void *p) = NULL; struct stm32_adc *adc; bool timestamping = false; + u32 trig_id = 0; int ret; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc)); @@ -2558,6 +2914,18 @@ static int stm32_adc_probe(struct platform_device *pdev) if (ret < 0) return ret; + if (device_property_present(&pdev->dev, "st,adc-trigger-sel")) { + ret = device_property_read_u32(dev, "st,adc-trigger-sel", &trig_id); + if (ret) + return ret; + } + + adc->trigs = adc->cfg->trigs[trig_id]; + if (!adc->trigs) { + dev_err(&pdev->dev, "Can't get trigger list\n"); + return -EINVAL; + } + ret = stm32_adc_dma_request(dev, indio_dev); if (ret < 0) return ret; @@ -2697,7 +3065,7 @@ static const struct dev_pm_ops stm32_adc_pm_ops = { static const struct stm32_adc_cfg stm32f4_adc_cfg = { .regs = &stm32f4_adc_regspec, .adc_info = &stm32f4_adc_info, - .trigs = stm32f4_adc_trigs, + .trigs = { stm32f4_adc_trigs, }, .clk_required = true, .start_conv = stm32f4_adc_start_conv, .stop_conv = stm32f4_adc_stop_conv, @@ -2706,17 +3074,19 @@ static const struct stm32_adc_cfg stm32f4_adc_cfg = { .vref_charac = 3300, }; -static const unsigned int stm32_adc_min_ts_h7[] = { 0, 0, 0, 4300, 9000 }; +/* Internal channel indexes are mapped on stm32_adc_int_ch enum */ +static const unsigned int stm32_adc_min_ts_h7[] = { 0, 0, 0, 4300, 9000, 0 }; static_assert(ARRAY_SIZE(stm32_adc_min_ts_h7) == STM32_ADC_INT_CH_NB); static const struct stm32_adc_cfg stm32h7_adc_cfg = { .regs = &stm32h7_adc_regspec, .adc_info = &stm32h7_adc_info, - .trigs = stm32h7_adc_trigs, + .trigs = { stm32h7_adc_trigs, }, .has_boostmode = true, .has_linearcal = true, .has_presel = true, .has_oversampling = true, + .has_vregen = true, .start_conv = stm32h7_adc_start_conv, .stop_conv = stm32h7_adc_stop_conv, .prepare = stm32h7_adc_prepare, @@ -2728,18 +3098,19 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = { .vref_charac = 3300, }; -static const unsigned int stm32_adc_min_ts_mp1[] = { 100, 100, 100, 4300, 9800 }; +static const unsigned int stm32_adc_min_ts_mp1[] = { 100, 0, 0, 4300, 9800, 0 }; static_assert(ARRAY_SIZE(stm32_adc_min_ts_mp1) == STM32_ADC_INT_CH_NB); static const struct stm32_adc_cfg stm32mp1_adc_cfg = { .regs = &stm32mp1_adc_regspec, .adc_info = &stm32h7_adc_info, - .trigs = stm32h7_adc_trigs, + .trigs = { stm32h7_adc_trigs, }, .has_vregready = true, .has_boostmode = true, .has_linearcal = true, .has_presel = true, .has_oversampling = true, + .has_vregen = true, .start_conv = stm32h7_adc_start_conv, .stop_conv = stm32h7_adc_stop_conv, .prepare = stm32h7_adc_prepare, @@ -2751,14 +3122,15 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = { .vref_charac = 3300, }; -static const unsigned int stm32_adc_min_ts_mp13[] = { 100, 0, 0, 4300, 9800 }; +static const unsigned int stm32_adc_min_ts_mp13[] = { 1000, 1000, 1000, 4300, 9800, 0 }; static_assert(ARRAY_SIZE(stm32_adc_min_ts_mp13) == STM32_ADC_INT_CH_NB); static const struct stm32_adc_cfg stm32mp13_adc_cfg = { .regs = &stm32mp13_adc_regspec, .adc_info = &stm32mp13_adc_info, - .trigs = stm32h7_adc_trigs, + .trigs = { stm32h7_adc_trigs, }, .has_oversampling = true, + .has_vregen = true, .start_conv = stm32mp13_adc_start_conv, .stop_conv = stm32h7_adc_stop_conv, .prepare = stm32h7_adc_prepare, @@ -2770,11 +3142,50 @@ static const struct stm32_adc_cfg stm32mp13_adc_cfg = { .vref_charac = 3300, }; +static const unsigned int stm32_adc_min_ts_mp25[] = { 34, 34, 0, 34, 34, 34 }; +static_assert(ARRAY_SIZE(stm32_adc_min_ts_mp25) == STM32_ADC_INT_CH_NB); + +static const struct stm32_adc_cfg stm32mp23_adc_cfg = { + .regs = &stm32mp25_adc_regspec, + .adc_info = &stm32mp25_adc_info, + .trigs = { stm32mp23_adc12_trigs, stm32mp23_adc3_trigs }, + .has_oversampling = true, + .has_presel = true, + .start_conv = stm32h7_adc_start_conv, + .stop_conv = stm32h7_adc_stop_conv, + .prepare = stm32mp25_adc_prepare, + .unprepare = stm32mp25_adc_unprepare, + .smp_cycles = stm32mp25_adc_smp_cycles, + .irq_clear = stm32h7_adc_irq_clear, + .set_ovs = stm32h7_adc_set_ovs, + .ts_int_ch = stm32_adc_min_ts_mp25, + .vref_charac = 1800, +}; + +static const struct stm32_adc_cfg stm32mp25_adc_cfg = { + .regs = &stm32mp25_adc_regspec, + .adc_info = &stm32mp25_adc_info, + .trigs = { stm32mp25_adc12_trigs, stm32mp25_adc3_trigs }, + .has_oversampling = true, + .has_presel = true, + .start_conv = stm32h7_adc_start_conv, + .stop_conv = stm32h7_adc_stop_conv, + .prepare = stm32mp25_adc_prepare, + .unprepare = stm32mp25_adc_unprepare, + .smp_cycles = stm32mp25_adc_smp_cycles, + .irq_clear = stm32h7_adc_irq_clear, + .set_ovs = stm32h7_adc_set_ovs, + .ts_int_ch = stm32_adc_min_ts_mp25, + .vref_charac = 1800, +}; + 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 }, + { .compatible = "st,stm32mp23-adc", .data = (void *)&stm32mp23_adc_cfg }, + { .compatible = "st,stm32mp25-adc", .data = (void *)&stm32mp25_adc_cfg }, { } }; MODULE_DEVICE_TABLE(of, stm32_adc_of_match); -- 2.43.0 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 3/8] iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25 2026-09-11 15:51 ` [PATCH 3/8] iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25 Fabrice Gasnier @ 2026-09-11 16:22 ` Andy Shevchenko 0 siblings, 0 replies; 13+ messages in thread From: Andy Shevchenko @ 2026-09-11 16:22 UTC (permalink / raw) To: Fabrice Gasnier Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin, Alexandre Torgue, Marek Vasut, linux-iio, devicetree, linux-stm32, linux-arm-kernel, linux-kernel, Cheick Traore, Olivier Moysan On Fri, Sep 11, 2026 at 05:51:25PM +0200, Fabrice Gasnier wrote: > Add support for STM32MP23 STM32MP25 SoCs. They have 3 ADCs, split into > two blocks: > - ADC12 (ADC1 & ADC2) are tightly coupled. > - ADC3 is managed independently. > Trigger list slightly changes between STM32MP23 and STM32MP25. > > Each ADC is a 12-bits successive approximation analog-to-digital converter, > with up to 20 multiplexed channels that can be configured as single ended > or differential. ADC resolution ranges from 6 to 12 bits. > > It introduces diversity regarding IRQs, clocks, software calibration > procedure, internal voltage channels, sampling time (prescaler) and > trigger list. Most of the architecture, and the driver engine remains > similar. So, handle the differences w.r.t. other STM32 ADCs family with > a dedicated compatible and compatible data. > > Trigger list depends on ADC instance, retrieved with "st,adc-trigger-sel" > property. > > Add support for vddcpu and vddgpu internal channels on stm32mp25. > These channels are enabled by using ADC option register. > Add VDDGPU to the list of supported channels in this driver and made > available on stm32mp25. So update sampling information for all variants > (with zero'ed data when it's not available). ... > + * @presc: clock prescaler array > + * @num_presc: number of clock prescalers in presc array > + int *presc; > + int num_presc; Why are they signed? I do not see any justification in the kernel doc. ... > static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = { > .regs = &stm32f4_adc_common_regs, > - .clk_sel = stm32f4_adc_clk_sel, > + .clk_sel = stm32_adc_clk_sel, > + .presc = stm32f4_pclk_div, > + .num_presc = ARRAY_SIZE(stm32f4_pclk_div), > .max_clk_rate_hz = 36000000, > .num_irqs = 1, > .num_adcs = 3, > }; > +static const struct stm32_adc_priv_cfg stm32mp25_adc_priv_cfg = { > + .regs = &stm32mp25_adc_common_regs, > + .clk_sel = stm32_adc_clk_sel, > + .presc = stm32mp25_presc_div, > + .num_presc = ARRAY_SIZE(stm32mp25_presc_div), > + .max_clk_rate_hz = 70000000, > + .ipid = STM32MP25_IPIDR_NUMBER, > + .num_irqs = 2, /* 2 IRQs for ADC12, 1 irq for ADC3 */ > +}; Split this patch to two. One prepares the infrastructure for prescaller, and the other adds a new HW support. > static const struct of_device_id stm32_adc_of_match[] = { > { > .compatible = "st,stm32f4-adc-core", > @@ -895,6 +948,12 @@ static const struct of_device_id stm32_adc_of_match[] = { > }, { > .compatible = "st,stm32mp13-adc-core", > .data = (void *)&stm32mp13_adc_priv_cfg > + }, { > + .compatible = "st,stm32mp23-adc-core", > + .data = (void *)&stm32mp25_adc_priv_cfg > + }, { > + .compatible = "st,stm32mp25-adc-core", > + .data = (void *)&stm32mp25_adc_priv_cfg > }, { > }, > }; ... > struct stm32_adc_cfg { > const struct stm32_adc_regspec *regs; > const struct stm32_adc_info *adc_info; > - const struct stm32_adc_trig_info *trigs; > + const struct stm32_adc_trig_info *trigs[2]; Indentation looks suspicious. I would use just a single space. > bool clk_required; > bool has_vregready; > bool has_boostmode; > bool has_linearcal; > bool has_presel; > bool has_oversampling; > + bool has_vregen; > int (*prepare)(struct iio_dev *); > void (*start_conv)(struct iio_dev *, bool dma); > void (*stop_conv)(struct iio_dev *); ... > struct stm32_adc { > int int_ch[STM32_ADC_INT_CH_NB]; > int nsmps; > int ovs_idx; > + const struct stm32_adc_trig_info *trigs; Ditto. And this shows exactly why the tab formatting is not good. Just a single space makes it consistent independently on the future development. > }; ... > +/* STM32MP23 external trigger sources for ADC12 */ > +static struct stm32_adc_trig_info stm32mp23_adc12_trigs[] = { > + { TIM1_TRGO, STM32_EXT0 }, > + { TIM1_TRGO2, STM32_EXT1 }, > + { TIM8_TRGO, STM32_EXT2 }, > + { TIM8_TRGO2, STM32_EXT3 }, > + { TIM2_TRGO, STM32_EXT6 }, > + { TIM3_TRGO, STM32_EXT7 }, > + { TIM4_TRGO, STM32_EXT8 }, > + { TIM5_TRGO, STM32_EXT9 }, > + { TIM6_TRGO, STM32_EXT10 }, > + { TIM15_TRGO, STM32_EXT11 }, > + { TIM1_CH1, STM32_EXT12 }, > + { TIM1_CH2, STM32_EXT13 }, > + { TIM1_CH3, STM32_EXT14 }, > + { TIM2_CH2, STM32_EXT18 }, > + { TIM3_CH4, STM32_EXT19 }, > + { TIM4_CH4, STM32_EXT20 }, > + { TIM5_CH1, STM32_EXT21 }, > + { TIM12_CH1, STM32_EXT22 }, > + { LPTIM1_CH1, STM32_EXT24 }, > + { LPTIM2_CH1, STM32_EXT25 }, > + { LPTIM3_CH1, STM32_EXT26 }, > + { LPTIM4_CH1, STM32_EXT27 }, > + { LPTIM5_OUT, STM32_EXT28 }, > + {}, The style of the terminator entry is no comma and likely a space inside, but the latter is optional for custom arrays (the requirement is strong for the ID tables in IIO). Ditto for other similar cases. Looking at which I would recommend split the second patch mentioned above on per-compatible basis, so make it 1 preparatory, 1 with a new HW with some possible additional modifications, and 1 per each additional compatible line. > +}; ... > +/* > + * STM32MP25 offset calibration software procedure. Basically the calibration routine is > + * expected to average (for example) 8 samples in calibration mode, for single-ended and > + * differential channels, to calibrate the zero offset. In case offset is "negative", an > + * additional offset can be added, to determine calibration factor. It must be kept later > + * for all conversions. > + */ > +static int stm32mp25_adc_calib_get_average_data(struct iio_dev *indio_dev, u32 *average) > +{ > + struct stm32_adc *adc = iio_priv(indio_dev); > + const struct stm32_adc_regspec *regs = adc->cfg->regs; > + u32 val, avg = 0; > + int i, ret; Why is 'i' signed? > + > + /* Repeat several conversions in calibration mode, average the results */ > + for (i = 0; i < STM32MP25_CALIB_LOOP; i++) { Also I don't see the problem with for (unsigned int i = 0; i < STM32MP25_CALIB_LOOP; i++) { > + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTART); > + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, > + !(val & (STM32H7_ADSTART)), > + 100, STM32_ADC_TIMEOUT_US); > + if (ret) { > + dev_err(&indio_dev->dev, "calibration average error %d\n", ret); > + return ret; > + } > + > + val = stm32_adc_readl(adc, regs->dr); > + dev_vdbg(&indio_dev->dev, "dr[%d]=0x%08x\n", i, val); > + avg += val; > + } > + > + *average = DIV_ROUND_CLOSEST(avg, STM32MP25_CALIB_LOOP); > + dev_vdbg(&indio_dev->dev, "average=0x%08x\n", *average); > + > + return 0; > +} ... > +static int stm32mp25_adc_calib(struct iio_dev *indio_dev) > +{ > + struct stm32_adc *adc = iio_priv(indio_dev); > + u32 calfact = 0, average; > + int ret; > + > + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL); > + /* Clears CALADDOS (and old calibration data if any) */ > + stm32_adc_writel(adc, STM32MP25_ADC_CALFACT, 0); > + /* Select single ended input calibration */ > + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALDIF); > + /* Use default resolution (e.g. 12 bits) */ > + stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR, STM32MP25_RES_MASK); > + > +retry: > + ret = stm32mp25_adc_calib_get_average_data(indio_dev, &average); > + if (ret) > + goto out; > + > + /* If the averaged data is zero, retry with additional offset (set CALADDOS) */ > + if (!average) { > + if (!calfact) { > + /* Averaged data is zero, retry with additional offset */ > + calfact = STM32MP25_CALFACT_CALADDOS; > + stm32_adc_writel(adc, STM32MP25_ADC_CALFACT, calfact); > + goto retry; > + } > + /* Averaged data is still zero with additional offset, just warn about it */ > + dev_warn(&indio_dev->dev, "Single-ended calibration average: 0\n"); > + } OMG, try to refactor this to avoid this twisted goto chain. > + calfact |= FIELD_PREP(STM32MP25_CALFACT_S_MASK, average); > + > + /* Select differential input calibration (keep previous CALADDOS value) */ > + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALDIF); > + ret = stm32mp25_adc_calib_get_average_data(indio_dev, &average); > + if (ret) > + goto out; > + > + /* > + * If the averaged data is below 0x800 (half value in 12-bits mode), > + * retry with additional offset > + */ > + if (average < BIT(adc->cfg->adc_info->resolutions[0] - 1)) { > + if (!(calfact & STM32MP25_CALFACT_CALADDOS)) { > + /* Retry the whole calibration with additional offset */ > + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALDIF); > + calfact = STM32MP25_CALFACT_CALADDOS; > + stm32_adc_writel(adc, STM32MP25_ADC_CALFACT, calfact); > + goto retry; > + } > + /* > + * Averaged data is still below center value. It needs to be clamped to zero, > + * so don't use the result here, warn about it. > + */ > + dev_warn(&indio_dev->dev, "Differential calibration clamped(0): 0x%x\n", average); > + } else { > + calfact |= FIELD_PREP(STM32MP25_CALFACT_D_MASK, average); > + } > + > + stm32_adc_writel(adc, STM32MP25_ADC_CALFACT, calfact); > + > + dev_dbg(&indio_dev->dev, "set calfact_s=0x%03lx, calfact_d=0x%03lx, calados=%ld\n", > + FIELD_GET(STM32MP25_CALFACT_S_MASK, calfact), > + FIELD_GET(STM32MP25_CALFACT_D_MASK, calfact), > + FIELD_GET(STM32MP25_CALFACT_CALADDOS, calfact)); > +out: > + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL); > + stm32_adc_set_res(adc); > + > + return ret; > +} ... > + if (device_property_present(&pdev->dev, "st,adc-trigger-sel")) { > + ret = device_property_read_u32(dev, "st,adc-trigger-sel", &trig_id); Why the first parameter is inconsistent in two device property API calls? > + if (ret) > + return ret; No 'else' for the default? > + } ... > static const struct stm32_adc_cfg stm32f4_adc_cfg = { > .regs = &stm32f4_adc_regspec, > .adc_info = &stm32f4_adc_info, > - .trigs = stm32f4_adc_trigs, > + .trigs = { stm32f4_adc_trigs, }, When a single line the inner trailing comma is not needed. > .clk_required = true, > .start_conv = stm32f4_adc_start_conv, > .stop_conv = stm32f4_adc_stop_conv, > .vref_charac = 3300, > }; ... Overall feeling that this patch is cumbersome of several changes. Please, split. -- With Best Regards, Andy Shevchenko ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 4/8] arm64: dts: st: add vrefint calibration on stm32mp23 2026-09-11 15:51 [PATCH 0/8] iio: adc: stm32: add support for stm32mp23 and stm32mp25 Fabrice Gasnier ` (2 preceding siblings ...) 2026-09-11 15:51 ` [PATCH 3/8] iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25 Fabrice Gasnier @ 2026-09-11 15:51 ` Fabrice Gasnier 2026-09-11 18:10 ` Marek Vasut 2026-09-11 15:51 ` [PATCH 5/8] arm64: dts: st: add vrefint calibration on stm32mp25 Fabrice Gasnier ` (3 subsequent siblings) 7 siblings, 1 reply; 13+ messages in thread From: Fabrice Gasnier @ 2026-09-11 15:51 UTC (permalink / raw) To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin, Alexandre Torgue Cc: Marek Vasut, linux-iio, devicetree, linux-stm32, linux-arm-kernel, linux-kernel, Fabrice Gasnier, Patrice Chotard, Olivier Moysan From: Patrice Chotard <patrice.chotard@foss.st.com> Describe vrefint calibration cell to be retrieved by ADCs through bsec, on STM32MP23. Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com> Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com> --- arch/arm64/boot/dts/st/stm32mp231.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/st/stm32mp231.dtsi b/arch/arm64/boot/dts/st/stm32mp231.dtsi index 35e247af957c..959500140d98 100644 --- a/arch/arm64/boot/dts/st/stm32mp231.dtsi +++ b/arch/arm64/boot/dts/st/stm32mp231.dtsi @@ -790,6 +790,10 @@ part-number-otp@24 { reg = <0x24 0x4>; }; + vrefint: vrefin-cal@1b8 { + reg = <0x1b8 0x2>; + }; + package-otp@1e8 { reg = <0x1e8 0x1>; bits = <0 3>; -- 2.43.0 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 4/8] arm64: dts: st: add vrefint calibration on stm32mp23 2026-09-11 15:51 ` [PATCH 4/8] arm64: dts: st: add vrefint calibration on stm32mp23 Fabrice Gasnier @ 2026-09-11 18:10 ` Marek Vasut 0 siblings, 0 replies; 13+ messages in thread From: Marek Vasut @ 2026-09-11 18:10 UTC (permalink / raw) To: Fabrice Gasnier, Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin, Alexandre Torgue Cc: linux-iio, devicetree, linux-stm32, linux-arm-kernel, linux-kernel, Patrice Chotard, Olivier Moysan On 9/11/26 5:51 PM, Fabrice Gasnier wrote: > From: Patrice Chotard <patrice.chotard@foss.st.com> > > Describe vrefint calibration cell to be retrieved by ADCs through bsec, > on STM32MP23. > > Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com> > Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> > Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com> You have to implant Co-developed-by into this , please fix globally. ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 5/8] arm64: dts: st: add vrefint calibration on stm32mp25 2026-09-11 15:51 [PATCH 0/8] iio: adc: stm32: add support for stm32mp23 and stm32mp25 Fabrice Gasnier ` (3 preceding siblings ...) 2026-09-11 15:51 ` [PATCH 4/8] arm64: dts: st: add vrefint calibration on stm32mp23 Fabrice Gasnier @ 2026-09-11 15:51 ` Fabrice Gasnier 2026-09-11 15:51 ` [PATCH 6/8] arm64: dts: st: add ADC nodes on stm32mp231 Fabrice Gasnier ` (2 subsequent siblings) 7 siblings, 0 replies; 13+ messages in thread From: Fabrice Gasnier @ 2026-09-11 15:51 UTC (permalink / raw) To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin, Alexandre Torgue Cc: Marek Vasut, linux-iio, devicetree, linux-stm32, linux-arm-kernel, linux-kernel, Fabrice Gasnier, Patrice Chotard, Olivier Moysan From: Patrice Chotard <patrice.chotard@foss.st.com> Describe vrefint calibration cell to be retrieved by ADCs through bsec, on STM32MP25. Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com> Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com> --- arch/arm64/boot/dts/st/stm32mp251.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/st/stm32mp251.dtsi b/arch/arm64/boot/dts/st/stm32mp251.dtsi index a6cb5378aaf9..cebfcb985cdb 100644 --- a/arch/arm64/boot/dts/st/stm32mp251.dtsi +++ b/arch/arm64/boot/dts/st/stm32mp251.dtsi @@ -1777,6 +1777,10 @@ part-number-otp@24 { reg = <0x24 0x4>; }; + vrefint: vrefin-cal@1b8 { + reg = <0x1b8 0x2>; + }; + package-otp@1e8 { reg = <0x1e8 0x1>; bits = <0 3>; -- 2.43.0 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 6/8] arm64: dts: st: add ADC nodes on stm32mp231 2026-09-11 15:51 [PATCH 0/8] iio: adc: stm32: add support for stm32mp23 and stm32mp25 Fabrice Gasnier ` (4 preceding siblings ...) 2026-09-11 15:51 ` [PATCH 5/8] arm64: dts: st: add vrefint calibration on stm32mp25 Fabrice Gasnier @ 2026-09-11 15:51 ` Fabrice Gasnier 2026-09-11 15:51 ` [PATCH 7/8] arm64: dts: st: add ADC nodes on stm32mp251 Fabrice Gasnier 2026-09-11 15:51 ` [PATCH 8/8] arm64: dts: st: add ADC support on stm32mp257f-ev1 board Fabrice Gasnier 7 siblings, 0 replies; 13+ messages in thread From: Fabrice Gasnier @ 2026-09-11 15:51 UTC (permalink / raw) To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin, Alexandre Torgue Cc: Marek Vasut, linux-iio, devicetree, linux-stm32, linux-arm-kernel, linux-kernel, Fabrice Gasnier, Cheick Traore From: Cheick Traore <cheick.traore@foss.st.com> Add analog-to-digital converter support on STM32MP23 SoC. It has 3 ADCs, split into two blocks: - ADC12 (ADC1 & ADC2) are tightly coupled. - ADC3 is managed independently. Define internal voltage channels in the SOC dtsi file, exception made of vbat/4 channel (to avoid undesired load on it, in case no conversion is needed on this channel). Choice is let to board implementation (via DT) to define it. Signed-off-by: Cheick Traore <cheick.traore@foss.st.com> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com> --- arch/arm64/boot/dts/st/stm32mp231.dtsi | 113 +++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/arch/arm64/boot/dts/st/stm32mp231.dtsi b/arch/arm64/boot/dts/st/stm32mp231.dtsi index 959500140d98..3aa1efdf9402 100644 --- a/arch/arm64/boot/dts/st/stm32mp231.dtsi +++ b/arch/arm64/boot/dts/st/stm32mp231.dtsi @@ -646,6 +646,119 @@ uart7: serial@40370000 { status = "disabled"; }; + adc_12: adc@404e0000 { + compatible = "st,stm32mp23-adc-core", "st,stm32-adc-core"; + reg = <0x404e0000 0x400>; + interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&rcc CK_KER_ADC12>; + clock-names = "adc"; + interrupt-controller; + #interrupt-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 58>; + status = "disabled"; + + adc1: adc@0 { + compatible = "st,stm32mp23-adc", "st,stm32-adc"; + reg = <0x0>; + interrupt-parent = <&adc_12>; + interrupts = <0>; + dmas = <&hpdma 81 0x20 0x12>; + dma-names = "rx"; + #io-channel-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + nvmem-cells = <&vrefint>; + nvmem-cell-names = "vrefint"; + st,adc-trigger-sel = <0>; + status = "disabled"; + channel@14 { + reg = <14>; + label = "vrefint"; + }; + }; + + adc2: adc@100 { + compatible = "st,stm32mp23-adc", "st,stm32-adc"; + reg = <0x100>; + interrupt-parent = <&adc_12>; + interrupts = <1>; + dmas = <&hpdma 82 0x20 0x12>; + dma-names = "rx"; + #io-channel-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + nvmem-cells = <&vrefint>; + nvmem-cell-names = "vrefint"; + st,adc-trigger-sel = <0>; + status = "disabled"; + channel@14 { + reg = <14>; + label = "vrefint"; + }; + channel@15 { + reg = <15>; + label = "vddcore"; + }; + channel@17 { + reg = <17>; + label = "vddcpu"; + }; + channel@18 { + reg = <18>; + label = "vddgpu"; + }; + }; + }; + + adc_3: adc@404f0000 { + compatible = "st,stm32mp23-adc-core", "st,stm32-adc-core"; + reg = <0x404f0000 0x400>; + interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&rcc CK_KER_ADC3>; + clock-names = "adc"; + interrupt-controller; + #interrupt-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 59>; + status = "disabled"; + + adc3: adc@0 { + compatible = "st,stm32mp23-adc", "st,stm32-adc"; + reg = <0x0>; + interrupt-parent = <&adc_3>; + interrupts = <0>; + dmas = <&hpdma 83 0x20 0x12>; + dma-names = "rx"; + #io-channel-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + nvmem-cells = <&vrefint>; + nvmem-cell-names = "vrefint"; + st,adc-trigger-sel = <1>; + status = "disabled"; + channel@14 { + reg = <14>; + label = "vrefint"; + }; + channel@15 { + reg = <15>; + label = "vddcore"; + }; + channel@17 { + reg = <17>; + label = "vddcpu"; + }; + channel@18 { + reg = <18>; + label = "vddgpu"; + }; + }; + }; + rng: rng@42020000 { compatible = "st,stm32mp25-rng"; reg = <0x42020000 0x400>; -- 2.43.0 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 7/8] arm64: dts: st: add ADC nodes on stm32mp251 2026-09-11 15:51 [PATCH 0/8] iio: adc: stm32: add support for stm32mp23 and stm32mp25 Fabrice Gasnier ` (5 preceding siblings ...) 2026-09-11 15:51 ` [PATCH 6/8] arm64: dts: st: add ADC nodes on stm32mp231 Fabrice Gasnier @ 2026-09-11 15:51 ` Fabrice Gasnier 2026-09-11 15:51 ` [PATCH 8/8] arm64: dts: st: add ADC support on stm32mp257f-ev1 board Fabrice Gasnier 7 siblings, 0 replies; 13+ messages in thread From: Fabrice Gasnier @ 2026-09-11 15:51 UTC (permalink / raw) To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin, Alexandre Torgue Cc: Marek Vasut, linux-iio, devicetree, linux-stm32, linux-arm-kernel, linux-kernel, Fabrice Gasnier Add analog-to-digital converter support on STM32MP25 SoC. It has 3 ADCs, split into two blocks: - ADC12 (ADC1 & ADC2) are tightly coupled. - ADC3 is managed independently. Define internal voltage channels in the SOC dtsi file, exception made of vbat/4 channel (to avoid undesired load on it, in case no conversion is needed on this channel). Choice is let to board implementation (via DT) to define it. Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com> --- arch/arm64/boot/dts/st/stm32mp251.dtsi | 115 +++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/arch/arm64/boot/dts/st/stm32mp251.dtsi b/arch/arm64/boot/dts/st/stm32mp251.dtsi index cebfcb985cdb..8925265f1ac2 100644 --- a/arch/arm64/boot/dts/st/stm32mp251.dtsi +++ b/arch/arm64/boot/dts/st/stm32mp251.dtsi @@ -1450,6 +1450,121 @@ uart8: serial@40380000 { status = "disabled"; }; + adc_12: adc@404e0000 { + compatible = "st,stm32mp25-adc-core", "st,stm32-adc-core"; + reg = <0x404e0000 0x400>; + interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&rcc CK_KER_ADC12>; + clock-names = "adc"; + interrupt-controller; + #interrupt-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + power-domains = <&CLUSTER_PD>; + access-controllers = <&rifsc 58>; + status = "disabled"; + + adc1: adc@0 { + compatible = "st,stm32mp25-adc", "st,stm32-adc"; + reg = <0x0>; + interrupt-parent = <&adc_12>; + interrupts = <0>; + dmas = <&hpdma 81 0x20 0x12>; + dma-names = "rx"; + #io-channel-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + nvmem-cells = <&vrefint>; + nvmem-cell-names = "vrefint"; + st,adc-trigger-sel = <0>; + status = "disabled"; + channel@14 { + reg = <14>; + label = "vrefint"; + }; + }; + + adc2: adc@100 { + compatible = "st,stm32mp25-adc", "st,stm32-adc"; + reg = <0x100>; + interrupt-parent = <&adc_12>; + interrupts = <1>; + dmas = <&hpdma 82 0x20 0x12>; + dma-names = "rx"; + #io-channel-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + nvmem-cells = <&vrefint>; + nvmem-cell-names = "vrefint"; + st,adc-trigger-sel = <0>; + status = "disabled"; + channel@14 { + reg = <14>; + label = "vrefint"; + }; + channel@15 { + reg = <15>; + label = "vddcore"; + }; + channel@17 { + reg = <17>; + label = "vddcpu"; + }; + channel@18 { + reg = <18>; + label = "vddgpu"; + }; + }; + }; + + adc_3: adc@404f0000 { + compatible = "st,stm32mp25-adc-core", "st,stm32-adc-core"; + reg = <0x404f0000 0x400>; + interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&rcc CK_KER_ADC3>; + clock-names = "adc"; + interrupt-controller; + #interrupt-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + access-controllers = <&rifsc 59>; + power-domains = <&CLUSTER_PD>; + status = "disabled"; + + adc3: adc@0 { + compatible = "st,stm32mp25-adc", "st,stm32-adc"; + reg = <0x0>; + interrupt-parent = <&adc_3>; + interrupts = <0>; + dmas = <&hpdma 83 0x20 0x12>; + dma-names = "rx"; + #io-channel-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + nvmem-cells = <&vrefint>; + nvmem-cell-names = "vrefint"; + st,adc-trigger-sel = <1>; + status = "disabled"; + channel@14 { + reg = <14>; + label = "vrefint"; + }; + channel@15 { + reg = <15>; + label = "vddcore"; + }; + channel@17 { + reg = <17>; + label = "vddcpu"; + }; + channel@18 { + reg = <18>; + label = "vddgpu"; + }; + }; + }; + rng: rng@42020000 { compatible = "st,stm32mp25-rng"; reg = <0x42020000 0x400>; -- 2.43.0 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 8/8] arm64: dts: st: add ADC support on stm32mp257f-ev1 board 2026-09-11 15:51 [PATCH 0/8] iio: adc: stm32: add support for stm32mp23 and stm32mp25 Fabrice Gasnier ` (6 preceding siblings ...) 2026-09-11 15:51 ` [PATCH 7/8] arm64: dts: st: add ADC nodes on stm32mp251 Fabrice Gasnier @ 2026-09-11 15:51 ` Fabrice Gasnier 7 siblings, 0 replies; 13+ messages in thread From: Fabrice Gasnier @ 2026-09-11 15:51 UTC (permalink / raw) To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin, Alexandre Torgue Cc: Marek Vasut, linux-iio, devicetree, linux-stm32, linux-arm-kernel, linux-kernel, Fabrice Gasnier Add ADC support on stm32mp257f-ev1 board. ANA0 dedicated pin is wired on mikroBUS connector, through a resistor divided and operational amplifier. Configure adc1 channel0, and voltage divider in the DT. Keep it disabled by default, to allow ADC to be used freely for other purpose. --> | 470K | -+-- > | OpAmp (gain = 1) | --> ANA0 (to ADC1, ADC2 or ADC3) | v | 560K | | v --- GND Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com> --- arch/arm64/boot/dts/st/stm32mp257f-ev1.dts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts b/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts index 14e033f365e3..f66eccee2139 100644 --- a/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts +++ b/arch/arm64/boot/dts/st/stm32mp257f-ev1.dts @@ -160,6 +160,28 @@ mm_ospi1: mm-ospi@60000000 { no-map; }; }; + + vdiv_ana0: voltage-divider-ana0 { + compatible = "voltage-divider"; + io-channels = <&adc1 0>; + output-ohms = <560000>; + full-ohms = <1030000>; + status = "disabled"; + }; +}; + +&adc_12 { + /* Don't need a pinctrl for ANA0 dedicated pin e.g. Channel@0 */ + vdda-supply = <&scmi_vdda18adc>; + vref-supply = <&scmi_v1v8>; + status = "disabled"; + adc1: adc@0 { + status = "okay"; + channel@0 { + reg = <0>; + st,min-sample-time-ns = <400>; + }; + }; }; &arm_wdt { -- 2.43.0 ^ permalink raw reply related [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-09-11 18:10 UTC | newest] Thread overview: 13+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-09-11 15:51 [PATCH 0/8] iio: adc: stm32: add support for stm32mp23 and stm32mp25 Fabrice Gasnier 2026-09-11 15:51 ` [PATCH 1/8] dt-bindings: iio: adc: stm32-adc: " Fabrice Gasnier 2026-09-11 18:08 ` Marek Vasut 2026-09-11 15:51 ` [PATCH 2/8] iio: adc: stm32-adc: manage characterization voltage diversity Fabrice Gasnier 2026-09-11 16:10 ` Andy Shevchenko 2026-09-11 15:51 ` [PATCH 3/8] iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25 Fabrice Gasnier 2026-09-11 16:22 ` Andy Shevchenko 2026-09-11 15:51 ` [PATCH 4/8] arm64: dts: st: add vrefint calibration on stm32mp23 Fabrice Gasnier 2026-09-11 18:10 ` Marek Vasut 2026-09-11 15:51 ` [PATCH 5/8] arm64: dts: st: add vrefint calibration on stm32mp25 Fabrice Gasnier 2026-09-11 15:51 ` [PATCH 6/8] arm64: dts: st: add ADC nodes on stm32mp231 Fabrice Gasnier 2026-09-11 15:51 ` [PATCH 7/8] arm64: dts: st: add ADC nodes on stm32mp251 Fabrice Gasnier 2026-09-11 15:51 ` [PATCH 8/8] arm64: dts: st: add ADC support on stm32mp257f-ev1 board Fabrice Gasnier
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox