Skip to contentSkip to conversation

iio: adc: stm32: add support for stm32mp23 and stm32mp25

PATCH Superseded Fabrice Gasnier 5 participants 22 messages Sep 11 – Sep 21

Subsystems (3)

v1 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

cover iio: adc: stm32: add support for stm32mp23 and stm32mp25

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.

---
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,

Signature · 1 line
Fabrice Gasnier <[email protected]>
#1 lore raw

PATCH 1/8 dt-bindings: iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25

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

---
 .../devicetree/bindings/iio/adc/st,stm32-adc.yaml  | 106 ++++++++++++++++++---
 1 file changed, 94 insertions(+), 12 deletions(-)
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +94 -12
index c4c4575d3fa9..0e0ceaa3d8d6 100644
@@ -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:
Signature · 1 line
2.43.0
#2 lore raw

Re: [PATCH 1/8] dt-bindings: iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [Low] Documentation for the deprecated `st,adc-channels` property explicitly lists `stm32mp23` and…

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Low] Documentation for the deprecated st,adc-channels property explicitly lists stm32mp23 and stm32mp25 as supporting up to 20 channels, but the property is explicitly forbidden in the schema for these compatibles.
- [Low] The st,adc-trigger-sel property specifies default: 0 but is also strictly required for the stm32mp23 and stm32mp25 variants.
- [Low] The schema attempts to restrict channel 19 using channel@19: false, which fails to prevent the usage of channel 19 in reg or diff-channels within other channel nodes.
--

commit 928337c3337781d9cd13fb9cf442caeafdde7db6
Author: Fabrice Gasnier <[email protected]>

dt-bindings: iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25

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.

Quoted 4 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +0 -0
index c4c4575d3fa96..0e0ceaa3d8d6f 100644

[ ... ]

Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml patternProperties: · line 344

Quoted 10 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +2 -2
@@ -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

[Severity: Low]
The description explicitly adds stm32mp23 and stm32mp25 to the list of SoCs supporting this deprecated property, but the if-then block below enforces st,adc-channels: false for these variants. Does the documentation need to be updated to clarify this property is not supported on these new SoCs?

[ ... ]

Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml patternProperties: · line 396

Quoted 10 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +6 -0
@@ -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
+

[Severity: Low]
Does declaring a default value here make sense since this property is strictly required by the schema block below? A required property must be explicitly present in the device tree for validation to pass, which renders this default declaration useless.

[ ... ]

Quoted 19 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +15 -0
@@ -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

Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml patternProperties: · line 563

Quoted 10 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +10 -0
+
+            interrupts:
+              minimum: 0
+              maximum: 1
+
+            assigned-resolution-bits:
+              enum: [6, 8, 10, 12]
+              default: 12
+
+            channel@19: false

[Severity: Low]
The commit message mentions restricting the last channel (19) as it is wired directly to ADC Vref+. However, setting channel@19: false only prevents a node explicitly named channel@19. Could a user still bypass this restriction by using diff-channels = <X 19>; or reg = <19> inside another channel node name?

Quoted 6 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +6 -0
+            st,adc-channels: false
+            st,adc-diff-channels: false
+            st,min-sample-time-nsecs: false
+
+          required:
+            - st,adc-trigger-sel
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
#3 lore raw

Re: [PATCH 1/8] dt-bindings: iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25

In reply to Fabrice Gasnier
Quoted 9 lines · Fabrice Gasnier

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 "

Quoted 2 lines

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.

Quoted 1 line

STM32 ADC may be in a power domain: allow a single 'power-domains'.

[...]

Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml patternProperties: · line 395

Quoted 5 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +5 -0
+      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 ?

[...]

maintainer
#4 lore raw

Re: [PATCH 1/8] dt-bindings: iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25

In reply to Fabrice Gasnier
Quoted 19 lines · On Fri, 11 Sep 2026 17:51:23 +0200 Fabrice Gasnier…

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

Quoted 5 lines

Hi Fabrice, all,

Quoted 19 lines
---
 .../devicetree/bindings/iio/adc/st,stm32-adc.yaml  | 106 ++++++++++++++++++---
 1 file changed, 94 insertions(+), 12 deletions(-)
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +2 -5
index c4c4575d3fa9..0e0ceaa3d8d6 100644
@@ -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:

Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml maintainers: · line 37

Quoted 10 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +10 -0
+          - 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

What is this? Given these all vary in features
why would we fallback to a generic match for
these new compatibles?

Quoted 14 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +2 -5
 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:

Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml patternProperties: · line 290

Quoted 10 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +10 -0
+              - 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

Similar to above, what is this?

Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml patternProperties: · line 394

Quoted 8 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +4 -0
@@ -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]

Magic value that is meaningless to see in a dts. If they are effectively not compatible, give them different compatibles and drop this property. I'm unconvinced this property is more useful than that given just how opaque the description is.

#5 lore raw

Re: [PATCH 1/8] dt-bindings: iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25

Quoted 55 lines · Jonathan Cameron
Quoted 19 lines · On Fri, 11 Sep 2026 17:51:23 +0200 Fabrice Gasnier…

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

Quoted 5 lines

Hi Fabrice, all,

Quoted 25 lines
---
 .../devicetree/bindings/iio/adc/st,stm32-adc.yaml  | 106 ++++++++++++++++++---
 1 file changed, 94 insertions(+), 12 deletions(-)
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +8 -5
index c4c4575d3fa9..0e0ceaa3d8d6 100644
@@ -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:
Quoted 8 lines

Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml maintainers: · line 37

Quoted 4 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +4 -0
+          - enum:
+              - st,stm32mp23-adc-core
+              - st,stm32mp25-adc-core
+          - const: st,stm32-adc-core

What is this? Given these all vary in features
why would we fallback to a generic match for
these new compatibles?

Hi Jonathan, all

Ack, I'll drop it in v2,

Quoted 21 lines
Quoted 21 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +6 -5
 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
Quoted 8 lines

Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml patternProperties: · line 290

Quoted 6 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +6 -0
+                  - st,stm32mp13-adc
+          - items:
+              - enum:
+                  - st,stm32mp23-adc
+                  - st,stm32mp25-adc
+              - const: st,stm32-adc

Similar to above, what is this?

same,

Quoted 6 lines
Quoted 6 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +0 -0
@@ -344,6 +388,12 @@ patternProperties:
         items:
           - const: vrefint
 
Quoted 9 lines

Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml patternProperties: · line 394

Quoted 4 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +4 -0
+      st,adc-trigger-sel:
+        description: Selects the HW trigger configuration available on this ADC
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1]

Magic value that is meaningless to see in a dts. If they are effectively not compatible, give them different compatibles and drop this property. I'm unconvinced this property is more useful than that given just how opaque the description is.

That's exactly what I was wondering about. As you suggest, I can remove this property, and introduce dedicated compatibles in V2, to clearly identify the differences.

Need to distinguish:
- ADC12 are tightly coupled (common registers).
- ADC3 is managed independently.
Triggers are different between them. The purpose of st,adc-trigger-sel was to identify that.

Also MP23 and MP25 are also different (trigger list). Combining this would lead to introduce four compatibles, I'm thinking of:
- st,stm32mp23-adc12
- st,stm32mp23-adc3
- st,stm32mp25-adc12
- st,stm32mp25-adc3

I plan to update it in v2.

Thanks,
Fabrice

Quoted 3 lines
maintainer
#6 lore raw

Re: [PATCH 1/8] dt-bindings: iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25

Quoted 127 lines · On Tue, 15 Sep 2026 09:49:01 +0200 Fabrice Gasnier…
Quoted 55 lines · Jonathan Cameron
Quoted 19 lines · On Fri, 11 Sep 2026 17:51:23 +0200 Fabrice Gasnier…

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

Quoted 5 lines

Hi Fabrice, all,

Quoted 25 lines
---
 .../devicetree/bindings/iio/adc/st,stm32-adc.yaml  | 106 ++++++++++++++++++---
 1 file changed, 94 insertions(+), 12 deletions(-)
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +8 -5
index c4c4575d3fa9..0e0ceaa3d8d6 100644
@@ -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:
Quoted 8 lines

Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml maintainers: · line 37

Quoted 4 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +4 -0
+          - enum:
+              - st,stm32mp23-adc-core
+              - st,stm32mp25-adc-core
+          - const: st,stm32-adc-core  

What is this? Given these all vary in features
why would we fallback to a generic match for
these new compatibles?

Hi Jonathan, all

Ack, I'll drop it in v2,

Quoted 21 lines
Quoted 21 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +6 -5
 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
Quoted 8 lines

Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml patternProperties: · line 290

Quoted 6 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +6 -0
+                  - st,stm32mp13-adc
+          - items:
+              - enum:
+                  - st,stm32mp23-adc
+                  - st,stm32mp25-adc
+              - const: st,stm32-adc  

Similar to above, what is this?

same,

Quoted 6 lines
Quoted 6 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +0 -0
@@ -344,6 +388,12 @@ patternProperties:
         items:
           - const: vrefint
 
Quoted 9 lines

Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml patternProperties: · line 394

Quoted 4 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +4 -0
+      st,adc-trigger-sel:
+        description: Selects the HW trigger configuration available on this ADC
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1]  

Magic value that is meaningless to see in a dts. If they are effectively not compatible, give them different compatibles and drop this property. I'm unconvinced this property is more useful than that given just how opaque the description is.

That's exactly what I was wondering about. As you suggest, I can remove this property, and introduce dedicated compatibles in V2, to clearly identify the differences.

Need to distinguish:
- ADC12 are tightly coupled (common registers).
- ADC3 is managed independently.
Triggers are different between them. The purpose of st,adc-trigger-sel was to identify that.

Quoted 7 lines

Also MP23 and MP25 are also different (trigger list). Combining this would lead to introduce four compatibles, I'm thinking of:
- st,stm32mp23-adc12
- st,stm32mp23-adc3
- st,stm32mp25-adc12
- st,stm32mp25-adc3

Naming wise, I wonder if it looks too much like adc twelve? Not sure how to avoid that cleanly as adc1-2 seems odd too.

Quoted 9 lines

I plan to update it in v2.

Thanks,
Fabrice

Quoted 3 lines
#7 lore raw

Re: [PATCH 1/8] dt-bindings: iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25

Quoted 137 lines · Jonathan Cameron
Quoted 127 lines · On Tue, 15 Sep 2026 09:49:01 +0200 Fabrice Gasnier…
Quoted 55 lines · Jonathan Cameron
Quoted 19 lines · On Fri, 11 Sep 2026 17:51:23 +0200 Fabrice Gasnier…

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

Quoted 5 lines

Hi Fabrice, all,

Quoted 25 lines
---
 .../devicetree/bindings/iio/adc/st,stm32-adc.yaml  | 106 ++++++++++++++++++---
 1 file changed, 94 insertions(+), 12 deletions(-)
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +8 -5
index c4c4575d3fa9..0e0ceaa3d8d6 100644
@@ -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:
Quoted 8 lines

Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml maintainers: · line 37

Quoted 4 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +4 -0
+          - enum:
+              - st,stm32mp23-adc-core
+              - st,stm32mp25-adc-core
+          - const: st,stm32-adc-core  

What is this? Given these all vary in features
why would we fallback to a generic match for
these new compatibles?

Hi Jonathan, all

Ack, I'll drop it in v2,

Quoted 21 lines
Quoted 21 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +6 -5
 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
Quoted 8 lines

Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml patternProperties: · line 290

Quoted 6 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +6 -0
+                  - st,stm32mp13-adc
+          - items:
+              - enum:
+                  - st,stm32mp23-adc
+                  - st,stm32mp25-adc
+              - const: st,stm32-adc  

Similar to above, what is this?

same,

Quoted 6 lines
Quoted 6 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +0 -0
@@ -344,6 +388,12 @@ patternProperties:
         items:
           - const: vrefint
 
Quoted 9 lines

Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml patternProperties: · line 394

Quoted 4 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +4 -0
+      st,adc-trigger-sel:
+        description: Selects the HW trigger configuration available on this ADC
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1]  

Magic value that is meaningless to see in a dts. If they are effectively not compatible, give them different compatibles and drop this property. I'm unconvinced this property is more useful than that given just how opaque the description is.

That's exactly what I was wondering about. As you suggest, I can remove this property, and introduce dedicated compatibles in V2, to clearly identify the differences.

Need to distinguish:
- ADC12 are tightly coupled (common registers).
- ADC3 is managed independently.
Triggers are different between them. The purpose of st,adc-trigger-sel was to identify that.

Quoted 7 lines

Also MP23 and MP25 are also different (trigger list). Combining this would lead to introduce four compatibles, I'm thinking of:
- st,stm32mp23-adc12
- st,stm32mp23-adc3
- st,stm32mp25-adc12
- st,stm32mp25-adc3

Quoted 2 lines

Naming wise, I wonder if it looks too much like adc twelve? Not sure how to avoid that cleanly as adc1-2 seems odd too.

Hi Jonathan,

Indeed, so could introduce one compatible per Soc/ADC ?

+          - st,stm32mp23-adc1
+          - st,stm32mp23-adc2
+          - st,stm32mp23-adc3
+          - st,stm32mp25-adc1
+          - st,stm32mp25-adc2
+          - st,stm32mp25-adc3

There are some other differences between adc1 and adc2, e.g. in the reference manual:
- ADC1 is connected to 18 external channels + 2 internal channels
- ADC2 is connected to 14 external channels + 6 internal channels
- ADC3 is connected to 14 external channels + 6 internal channels

This is resolved currently by channels child nodes with labels for internal channels. So maybe better to name all of them (future proof) ?

Please advise,
Best Regards,
Fabrice

Quoted 11 lines
Quoted 10 lines

I plan to update it in v2.

Thanks,
Fabrice

Quoted 3 lines
maintainer
#8 lore raw

Re: [PATCH 1/8] dt-bindings: iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25

Quoted 152 lines · On Thu, 17 Sep 2026 09:27:10 +0200 Fabrice Gasnier…
Quoted 137 lines · Jonathan Cameron
Quoted 127 lines · On Tue, 15 Sep 2026 09:49:01 +0200 Fabrice Gasnier…
Quoted 55 lines · Jonathan Cameron
Quoted 19 lines · On Fri, 11 Sep 2026 17:51:23 +0200 Fabrice Gasnier…

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

Quoted 5 lines

Hi Fabrice, all,

Quoted 25 lines
---
 .../devicetree/bindings/iio/adc/st,stm32-adc.yaml  | 106 ++++++++++++++++++---
 1 file changed, 94 insertions(+), 12 deletions(-)
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +8 -5
index c4c4575d3fa9..0e0ceaa3d8d6 100644
@@ -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:
Quoted 8 lines

Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml maintainers: · line 37

Quoted 4 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +4 -0
+          - enum:
+              - st,stm32mp23-adc-core
+              - st,stm32mp25-adc-core
+          - const: st,stm32-adc-core    

What is this? Given these all vary in features
why would we fallback to a generic match for
these new compatibles?

Hi Jonathan, all

Ack, I'll drop it in v2,

Quoted 21 lines
Quoted 21 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +6 -5
 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
Quoted 8 lines

Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml patternProperties: · line 290

Quoted 6 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +6 -0
+                  - st,stm32mp13-adc
+          - items:
+              - enum:
+                  - st,stm32mp23-adc
+                  - st,stm32mp25-adc
+              - const: st,stm32-adc    

Similar to above, what is this?

same,

Quoted 6 lines
Quoted 6 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +0 -0
@@ -344,6 +388,12 @@ patternProperties:
         items:
           - const: vrefint
 
Quoted 9 lines

Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml patternProperties: · line 394

Quoted 4 lines
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +4 -0
+      st,adc-trigger-sel:
+        description: Selects the HW trigger configuration available on this ADC
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1]    

Magic value that is meaningless to see in a dts. If they are effectively not compatible, give them different compatibles and drop this property. I'm unconvinced this property is more useful than that given just how opaque the description is.

That's exactly what I was wondering about. As you suggest, I can remove this property, and introduce dedicated compatibles in V2, to clearly identify the differences.

Need to distinguish:
- ADC12 are tightly coupled (common registers).
- ADC3 is managed independently.
Triggers are different between them. The purpose of st,adc-trigger-sel was to identify that.

Quoted 7 lines

Also MP23 and MP25 are also different (trigger list). Combining this would lead to introduce four compatibles, I'm thinking of:
- st,stm32mp23-adc12
- st,stm32mp23-adc3
- st,stm32mp25-adc12
- st,stm32mp25-adc3

Quoted 2 lines

Naming wise, I wonder if it looks too much like adc twelve? Not sure how to avoid that cleanly as adc1-2 seems odd too.

Hi Jonathan,

Indeed, so could introduce one compatible per Soc/ADC ?

(quoted diff) +6 -0
+          - st,stm32mp23-adc1
+          - st,stm32mp23-adc2
+          - st,stm32mp23-adc3
+          - st,stm32mp25-adc1
+          - st,stm32mp25-adc2
+          - st,stm32mp25-adc3
Quoted 8 lines

There are some other differences between adc1 and adc2, e.g. in the reference manual:
- ADC1 is connected to 18 external channels + 2 internal channels
- ADC2 is connected to 14 external channels + 6 internal channels
- ADC3 is connected to 14 external channels + 6 internal channels

This is resolved currently by channels child nodes with labels for internal channels. So maybe better to name all of them (future proof) ?

Yes. Ideally the binding would be constraining the channel nodes and we'd need the compatible to do that.

Seems like we have a winner - separate compatibles :)

Jonathan

Quoted 18 lines

Please advise,
Best Regards,
Fabrice

Quoted 11 lines
Quoted 10 lines

I plan to update it in v2.

Thanks,
Fabrice

Quoted 3 lines
#9 lore raw

PATCH 2/8 iio: adc: stm32-adc: manage characterization voltage diversity

In reply to Fabrice Gasnier

From: Olivier Moysan <[email protected]>

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.

---
 drivers/iio/adc/stm32-adc.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/iio/adc/stm32-adc.c +7 -2
index 5c6c06b269be..183b3474cad1 100644
@@ -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[] = {
Signature · 1 line
2.43.0
#10 lore raw

Re: [PATCH 2/8] iio: adc: stm32-adc: manage characterization voltage diversity

Quoted 4 lines · Fabrice Gasnier

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

...

Quoted 1 line

+ * @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).

...

drivers/iio/adc/stm32-adc.c struct stm32_adc_cfg { · line 233

Quoted 1 line
drivers/iio/adc/stm32-adc.c +1 -0
+	const int vref_charac;

What does const buy you here?

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#11 lore raw

Re: [PATCH 2/8] iio: adc: stm32-adc: manage characterization voltage diversity

In reply to Fabrice Gasnier
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [Critical] Missing error check after ADC conversion allows division by zero or division by uninitialized memory if the conversion fails or returns zero. --

commit 0d19a7183285228478d5cfa82a3785b87f3e1bd1
Author: Olivier Moysan <[email protected]>

iio: adc: stm32-adc: manage characterization voltage diversity

This commit introduces a vref_charac field in stm32_adc_cfg to store SoC-specific characterization voltage. It updates the stm32_adc_read_raw function to use this field in the vref computation formula.

Quoted 3 lines
drivers/iio/adc/stm32-adc.c +0 -0

[ ... ]

drivers/iio/adc/stm32-adc.c static int stm32_adc_read_raw(struct iio_dev *indio_dev, · line 1614

Quoted 6 lines
drivers/iio/adc/stm32-adc.c +1 -1
@@ -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;

[Severity: Critical]
This isn't a bug introduced by this patch, but does this division need to check if the conversion actually succeeded?

Looking at the preceding code in stm32_adc_read_raw():

    if (chan->type == IIO_VOLTAGE)
        ret = stm32_adc_single_conv(indio_dev, chan, val);
    else
        ret = -EINVAL;

If a sysfs read (such as in_voltageX_input) is interrupted by a signal, stm32_adc_single_conv() can abort via wait_for_completion_interruptible_timeout() and return an error like -ERESTARTSYS. When this happens, *val remains uninitialized.

Since there is no error check on ret before the division, this code will proceed to divide by the uninitialized *val. If the kernel is compiled with CONFIG_INIT_STACK_ALL_ZERO=y, the uninitialized stack variable will be 0, which could lead to a division by zero kernel panic.

Should we verify that ret indicates success and that *val is non-zero before attempting the scaling calculation?

Quoted 3 lines
drivers/iio/adc/stm32-adc.c +0 -0
 		iio_device_release_direct(indio_dev);
 		return ret;
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
#12 lore raw

PATCH 3/8 iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25

In reply to Fabrice Gasnier

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

---
 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(-)
drivers/iio/adc/stm32-adc-core.c +75 -16
index a42d82d61cb8..e3c3ac7f748d 100644
@@ -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
 	}, {
 	},
 };
drivers/iio/adc/stm32-adc-core.h +23 -0
index db50a9f3b922..0f40450844e9 100644
@@ -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)
drivers/iio/adc/stm32-adc.c +423 -12

627 lines not on this page · raw message

Signature · 1 line
2.43.0
#13 lore raw

Re: [PATCH 3/8] iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25

Quoted 24 lines · Fabrice Gasnier

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

...

drivers/iio/adc/stm32-adc-core.c struct stm32_adc_priv; · line 85

Quoted 5 lines

+ * @presc: clock prescaler array
+ * @num_presc: number of clock prescalers in presc array

drivers/iio/adc/stm32-adc-core.c +2 -0
+	int *presc;
+	int num_presc;

Why are they signed? I do not see any justification in the kernel doc.

...

Quoted 11 lines

static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = {

(quoted diff) +3 -1
 	.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,
 
 };

Review

Quoted 10 lines
(quoted diff) +9 -0
 
+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.

Quoted 16 lines

static const struct of_device_id stm32_adc_of_match[] = {

drivers/iio/adc/stm32-adc-core.c +6 -0
 	{
 		.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
 	}, {
 	},
 };

...

drivers/iio/adc/stm32-adc.c struct stm32_adc; · line 234

Quoted 6 lines

struct stm32_adc_cfg {

drivers/iio/adc/stm32-adc.c +1 -1
 	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.

Quoted 10 lines
drivers/iio/adc/stm32-adc.c +1 -0
 	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 *);

...

drivers/iio/adc/stm32-adc.c struct stm32_adc { · line 310

Quoted 6 lines

struct stm32_adc {

drivers/iio/adc/stm32-adc.c +1 -0
 	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.

Quoted 1 line

};

...

Quoted 16 lines
drivers/iio/adc/stm32-adc.c +16 -0
+/* 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 },

drivers/iio/adc/stm32-adc.c static const struct stm32_adc_regspec stm32h7_adc_regspec = { · line 606

Quoted 10 lines
drivers/iio/adc/stm32-adc.c +10 -0
+	{ 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.

Quoted 1 line

+};

...

drivers/iio/adc/stm32-adc.c static void stm32h7_adc_unprepare(struct iio_dev *indio_dev) · line 1518

Quoted 13 lines
drivers/iio/adc/stm32-adc.c +13 -0
+/*
+ * 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?

drivers/iio/adc/stm32-adc.c static void stm32h7_adc_unprepare(struct iio_dev *indio_dev) · line 1521

Quoted 3 lines
drivers/iio/adc/stm32-adc.c +3 -0
+
+	/* 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++) {
Quoted 19 lines
drivers/iio/adc/stm32-adc.c +19 -0
+		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;
+}

...

Quoted 20 lines
drivers/iio/adc/stm32-adc.c +20 -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) */

drivers/iio/adc/stm32-adc.c static void stm32h7_adc_unprepare(struct iio_dev *indio_dev) · line 1571

Quoted 10 lines
drivers/iio/adc/stm32-adc.c +10 -0
+	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.

Quoted 41 lines
drivers/iio/adc/stm32-adc.c +41 -0
+	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;
+}

...

drivers/iio/adc/stm32-adc.c static int stm32_adc_probe(struct platform_device *pdev) · line 2918

Quoted 2 lines
drivers/iio/adc/stm32-adc.c +2 -0
+	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?

Review

Quoted 2 lines
(quoted diff) +2 -0
+		if (ret)
+			return ret;

No 'else' for the default?

Quoted 1 line
(quoted diff) +1 -0
+	}

...

drivers/iio/adc/stm32-adc.c static const struct dev_pm_ops stm32_adc_pm_ops = { · line 3068

Quoted 5 lines

static const struct stm32_adc_cfg stm32f4_adc_cfg = {

drivers/iio/adc/stm32-adc.c +1 -1
 	.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.

drivers/iio/adc/stm32-adc.c static const struct dev_pm_ops stm32_adc_pm_ops = {

Quoted 6 lines
drivers/iio/adc/stm32-adc.c +0 -0
 	.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.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#14 lore raw

Re: [PATCH 3/8] iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25

In reply to Fabrice Gasnier
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

New issues:
- [High] Missing bounds check on the st,adc-trigger-sel device tree property allows an out-of-bounds array read when indexing adc->cfg->trigs.
- [Medium] Missing ADC stop (ADSTP) on calibration timeout error path leaves ADC in a locked state.

Pre-existing issues:
- [High] Configuring an unsupported internal channel does not abort setup, leading to errant read-modify-write operations on register offset 0 (ISR), which silently clears unrelated pending interrupts. --

commit 07a141434d979402aadc3f3eaff7d932ee702232
Author: Fabrice Gasnier <[email protected]>

iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25

This commit adds support for the STM32MP23 and STM32MP25 SoCs to the stm32-adc driver. It introduces support for diversity in IRQs, clocks, internal voltage channels, and trigger lists by utilizing a dedicated compatible string and configuration data.

Quoted 2 lines
drivers/iio/adc/stm32-adc.c +0 -0

[ ... ]

Quoted 1 line
drivers/iio/adc/stm32-adc.c +0 -0
@@ -1503,6 +1503,163 @@ static void stm32h7_adc_unprepare(struct iio_dev *indio_dev)

[ ... ]

Quoted 20 lines
drivers/iio/adc/stm32-adc.c +20 -0
+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);

drivers/iio/adc/stm32-adc.c static void stm32h7_adc_unprepare(struct iio_dev *indio_dev) · line 1644

Quoted 10 lines
drivers/iio/adc/stm32-adc.c +10 -0
+
+	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);

[Severity: Medium]
If calibration times out, does this error path leave the ADC in a locked state?

If stm32mp25_adc_calib() times out waiting for ADSTART to clear, it returns an error that jumps to adc_dis. The error path directly calls stm32h7_adc_disable(), which tries to disable the ADC by setting ADDIS.

However, the hardware specification generally prohibits setting ADDIS while ADSTART=1. Should the driver first abort the conversion using an stm32h7_adc_stop_conv() call to set ADSTP and wait for ADSTART to clear before disabling the hardware?

[ ... ]

Quoted 12 lines
drivers/iio/adc/stm32-adc.c +5 -0
@@ -2641,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) {

drivers/iio/adc/stm32-adc.c

Quoted 3 lines
drivers/iio/adc/stm32-adc.c +0 -0
 				adc->int_ch[i] = chan;
 				break;
 			}

[Severity: High]
This is a pre-existing issue, but does this warning branch fail to abort channel registration?

If an unsupported internal channel like vddgpu is requested, the driver prints a warning but falls through and assigns it a valid int_ch below.

This causes a problem later during setup:

stm32_adc_int_ch_enable() {
    ...
    case STM32_ADC_INT_CH_VDDGPU:
        stm32_adc_set_bits(adc, adc->cfg->regs->or_vddgpu.reg,
                           adc->cfg->regs->or_vddgpu.mask);
    ...
}

Because the register offset is uninitialized (0), this performs a read-modify-write on offset 0, which corresponds to the ISR register. Since the ISR has read-clear-write-1 semantics, this will silently clear all currently pending unrelated interrupts.

[ ... ]

drivers/iio/adc/stm32-adc.c static int stm32_adc_probe(struct platform_device *pdev) · line 2923

Quoted 11 lines
drivers/iio/adc/stm32-adc.c +7 -0
@@ -2914,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];

[Severity: High]
Could an invalid st,adc-trigger-sel value lead to an out-of-bounds read here?

The st,adc-trigger-sel property is read directly into trig_id and immediately used to index the adc->cfg->trigs array, which has a fixed size of 2.

If a malformed Device Tree provides a larger value, this skips bounds validation and might cause a memory safety issue by dereferencing invalid pointers.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
maintainer
#15 lore raw

Re: [PATCH 3/8] iio: adc: stm32-adc: add support for stm32mp23 and stm32mp25

In reply to Fabrice Gasnier
Quoted 25 lines · On Fri, 11 Sep 2026 17:51:25 +0200 Fabrice Gasnier…

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

Quoted 5 lines

Hi. various comment inline. Some stuff will change if the two compatible solution works out instead of the trigger array stuff. There are also some bits I'd like as enabling refactors where you add the new stuff to support extra variability before bringing in a the device support that uses it. Tends to end up more readble.

drivers/iio/adc/stm32-adc-core.c static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com) · line 142

Quoted 5 lines

/* STM32F4 ADC internal common clock prescaler division ratios */ static int stm32f4_pclk_div[] = {2, 4, 6, 8};

drivers/iio/adc/stm32-adc-core.c +2 -0
+/* 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};

Spaces after { and before }
I don't mind the line being a bit longer. Hmm. Local style so maybe leave it for now.

Quoted 34 lines
drivers/iio/adc/stm32-adc-core.c +8 -7
+
 /**
- * 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) {

drivers/iio/adc/stm32-adc-core.c static int stm32f4_adc_clk_sel(struct platform_device *pdev, · line 178

Quoted 10 lines
drivers/iio/adc/stm32-adc-core.c +1 -5
 		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];

Where a refactor is needed do it first in an enabling patch with no new device support, then follow up with the new devices stuff.

You might want to break that enabling up into a couple of patches as there are multiple things that need to become configurable and it will get complex in one patch.

Quoted 22 lines
drivers/iio/adc/stm32-adc-core.c +7 -0
+	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,
 };
 
 
+
 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[] = {
 	}, {

drivers/iio/adc/stm32-adc-core.c static const struct of_device_id stm32_adc_of_match[] = { · line 958

Quoted 10 lines
drivers/iio/adc/stm32-adc-core.c +6 -0
 		.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
 	}, {
 	},

Feel free to delete this trailing comma whilst you are here.

Quoted 10 lines
(quoted diff) +0 -0
 };
drivers/iio/adc/stm32-adc.c +0 -0
index 183b3474cad1..42827ce489dd 100644
 
 
 enum stm32_adc_int_ch {

drivers/iio/adc/stm32-adc.c enum stm32_adc_int_ch { · line 101

Quoted 5 lines
drivers/iio/adc/stm32-adc.c +1 -0
@@ -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,

That one shouldn't have a comma as it's just there for counting and must always be last. Feel free to tidy that up whilst you are here!

drivers/iio/adc/stm32-adc.c static const struct stm32_adc_ic stm32_adc_ic[STM32_ADC_INT_CH_NB] = { · line 120

Quoted 7 lines
drivers/iio/adc/stm32-adc.c +1 -0
 };
 
@@ -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 },

A precursor making all these arrays use
[STM32_ADC_INT_CH_VBAT] = { "...." } would help avoid the need for static
magic below.

Quoted 1 line
drivers/iio/adc/stm32-adc.c +0 -0
 };

...

drivers/iio/adc/stm32-adc.c static const struct stm32_adc_regspec stm32h7_adc_regspec = { · line 669

Quoted 7 lines
drivers/iio/adc/stm32-adc.c +6 -0
+/* STM32MP25 external trigger sources for ADC12 */
+static struct stm32_adc_trig_info stm32mp25_adc12_trigs[] = {
+	{ TIM1_TRGO, STM32_EXT0 },
+	{ TIM1_TRGO2, STM32_EXT1 },
 
+	{ LPTIM5_OUT, STM32_EXT28 },
+	{},

As below. Look for others.

drivers/iio/adc/stm32-adc.c static const struct stm32_adc_regspec stm32h7_adc_regspec = { · line 702

Quoted 11 lines
drivers/iio/adc/stm32-adc.c +10 -0
+};
+
+/* STM32MP25 external trigger sources for ADC3 */
+static struct stm32_adc_trig_info stm32mp25_adc3_trigs[] = {
 
+	{ LPTIM1_CH1, STM32_EXT24 },
+	{ LPTIM2_CH1, STM32_EXT25 },
+	{ LPTIM3_CH1, STM32_EXT26 },
+	{ LPTIM4_CH1, STM32_EXT27 },
+	{ LPTIM5_OUT, STM32_EXT28 },
+	{},

No comma on terminating entries like this +

	{ }

preferred as style for IIO drivers. (random choice but I'd like consistency across drivers eventually!)

drivers/iio/adc/stm32-adc.c static int stm32h7_adc_exit_pwr_down(struct iio_dev *indio_dev) · line 1165

Quoted 8 lines
drivers/iio/adc/stm32-adc.c +2 -1
+};
@@ -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)

I'd prefer this extra flag came in during a percursor patch (and was always set during that). Will reduce the complexity of this patch a touch.

Quoted 14 lines
drivers/iio/adc/stm32-adc.c +7 -0
+		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.

drivers/iio/adc/stm32-adc.c static void stm32h7_adc_unprepare(struct iio_dev *indio_dev) · line 1521

Quoted 10 lines
drivers/iio/adc/stm32-adc.c +10 -0
+ */
+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++) {

for (unsigned int i = 0; i...

Look for other cases of this that can be simplified given this style is now allowed in the kernel (has been for last few years).

Quoted 19 lines
drivers/iio/adc/stm32-adc.c +19 -0
+		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;
+}

...

drivers/iio/adc/stm32-adc.c static const struct stm32_adc_cfg stm32f4_adc_cfg = { · line 3079

Quoted 5 lines
drivers/iio/adc/stm32-adc.c +2 -1
-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);

Given we only care about some entries, maybe we should have a precursor that drops all these static asserts and instead does

static const unsigned int stm32_adc_min_ts_h7[STM32_ADC_INT_CH_NB] = {
	[STM32_ADC_INT_CH_VDDQ_DDR] = 4300,
	.. Only the ones that need to be non 0.
};

drivers/iio/adc/stm32-adc.c static const struct stm32_adc_cfg stm32h7_adc_cfg = { · line 3101

Quoted 6 lines
drivers/iio/adc/stm32-adc.c +1 -1
@@ -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 };

As below. If these need to change for some reason, precursor patch. If it were just the extra entry that would be fine but numbers are changing for earlier entries.

Quoted 17 lines
drivers/iio/adc/stm32-adc.c +2 -1
 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,

drivers/iio/adc/stm32-adc.c static const struct stm32_adc_cfg stm32mp1_adc_cfg = { · line 3125

Quoted 5 lines
drivers/iio/adc/stm32-adc.c +1 -1
@@ -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 };

Why is this existing entry changing?

Quoted 15 lines
drivers/iio/adc/stm32-adc.c +2 -1
 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,
 };
#16 lore raw

PATCH 4/8 arm64: dts: st: add vrefint calibration on stm32mp23

In reply to Fabrice Gasnier

From: Patrice Chotard <[email protected]>

Describe vrefint calibration cell to be retrieved by ADCs through bsec, on STM32MP23.

---
 arch/arm64/boot/dts/st/stm32mp231.dtsi | 4 ++++
 1 file changed, 4 insertions(+)
arch/arm64/boot/dts/st/stm32mp231.dtsi +4 -0
index 35e247af957c..959500140d98 100644
@@ -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>;
Signature · 1 line
2.43.0
#18 lore raw

PATCH 5/8 arm64: dts: st: add vrefint calibration on stm32mp25

In reply to Fabrice Gasnier

From: Patrice Chotard <[email protected]>

Describe vrefint calibration cell to be retrieved by ADCs through bsec, on STM32MP25.

---
 arch/arm64/boot/dts/st/stm32mp251.dtsi | 4 ++++
 1 file changed, 4 insertions(+)
arch/arm64/boot/dts/st/stm32mp251.dtsi +4 -0
index a6cb5378aaf9..cebfcb985cdb 100644
@@ -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>;
Signature · 1 line
2.43.0
#19 lore raw

PATCH 6/8 arm64: dts: st: add ADC nodes on stm32mp231

In reply to Fabrice Gasnier

From: Cheick Traore <[email protected]>

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.

---
 arch/arm64/boot/dts/st/stm32mp231.dtsi | 113 +++++++++++++++++++++++++++++++++
 1 file changed, 113 insertions(+)
arch/arm64/boot/dts/st/stm32mp231.dtsi +113 -0
index 959500140d98..3aa1efdf9402 100644
@@ -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>;
Signature · 1 line
2.43.0
#20 lore raw

PATCH 7/8 arm64: dts: st: add ADC nodes on stm32mp251

In reply to 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.

---
 arch/arm64/boot/dts/st/stm32mp251.dtsi | 115 +++++++++++++++++++++++++++++++++
 1 file changed, 115 insertions(+)
arch/arm64/boot/dts/st/stm32mp251.dtsi +115 -0
index cebfcb985cdb..8925265f1ac2 100644
@@ -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>;
Signature · 1 line
2.43.0
#21 lore raw

PATCH 8/8 arm64: dts: st: add ADC support on stm32mp257f-ev1 board

In reply to 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
---
 arch/arm64/boot/dts/st/stm32mp257f-ev1.dts | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
arch/arm64/boot/dts/st/stm32mp257f-ev1.dts +22 -0
index 14e033f365e3..f66eccee2139 100644
@@ -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 {
Signature · 1 line
2.43.0