All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/7] add internal channels support
@ 2021-09-08 15:54 ` Olivier Moysan
  0 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: alsa-devel, devicetree, linux-arm-kernel, linux-iio,
	linux-kernel, linux-stm32

This patchset adds support of ADC2 internal channels VDDCORE, VREFINT and VBAT
on STM32MP15x SoCs. The generic IIO channel bindings is also introduced here
to provide this feature. The legacy channel binding is kept for backward compatibility.

Olivier Moysan (7):
  dt-bindings: iio: adc: add generic channel binding
  dt-bindings: iio: adc: add nvmem support for vrefint internal channel
  iio: adc stm32-adc: split channel init into several routines
  iio: adc: stm32-adc: add support of generic channels binding
  iio: adc: stm32-adc: add support of internal channels
  iio: adc: stm32-adc: add vrefint calibration support
  iio: adc: stm32-adc: use generic binding for sample-time

 .../bindings/iio/adc/st,stm32-adc.yaml        | 108 ++++-
 drivers/iio/adc/stm32-adc-core.h              |   8 +
 drivers/iio/adc/stm32-adc.c                   | 418 ++++++++++++++++--
 3 files changed, 482 insertions(+), 52 deletions(-)

-- 
2.17.1


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH 0/7] add internal channels support
@ 2021-09-08 15:54 ` Olivier Moysan
  0 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: devicetree, alsa-devel, linux-iio, linux-kernel, linux-stm32,
	linux-arm-kernel

This patchset adds support of ADC2 internal channels VDDCORE, VREFINT and VBAT
on STM32MP15x SoCs. The generic IIO channel bindings is also introduced here
to provide this feature. The legacy channel binding is kept for backward compatibility.

Olivier Moysan (7):
  dt-bindings: iio: adc: add generic channel binding
  dt-bindings: iio: adc: add nvmem support for vrefint internal channel
  iio: adc stm32-adc: split channel init into several routines
  iio: adc: stm32-adc: add support of generic channels binding
  iio: adc: stm32-adc: add support of internal channels
  iio: adc: stm32-adc: add vrefint calibration support
  iio: adc: stm32-adc: use generic binding for sample-time

 .../bindings/iio/adc/st,stm32-adc.yaml        | 108 ++++-
 drivers/iio/adc/stm32-adc-core.h              |   8 +
 drivers/iio/adc/stm32-adc.c                   | 418 ++++++++++++++++--
 3 files changed, 482 insertions(+), 52 deletions(-)

-- 
2.17.1


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH 0/7] add internal channels support
@ 2021-09-08 15:54 ` Olivier Moysan
  0 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: alsa-devel, devicetree, linux-arm-kernel, linux-iio,
	linux-kernel, linux-stm32

This patchset adds support of ADC2 internal channels VDDCORE, VREFINT and VBAT
on STM32MP15x SoCs. The generic IIO channel bindings is also introduced here
to provide this feature. The legacy channel binding is kept for backward compatibility.

Olivier Moysan (7):
  dt-bindings: iio: adc: add generic channel binding
  dt-bindings: iio: adc: add nvmem support for vrefint internal channel
  iio: adc stm32-adc: split channel init into several routines
  iio: adc: stm32-adc: add support of generic channels binding
  iio: adc: stm32-adc: add support of internal channels
  iio: adc: stm32-adc: add vrefint calibration support
  iio: adc: stm32-adc: use generic binding for sample-time

 .../bindings/iio/adc/st,stm32-adc.yaml        | 108 ++++-
 drivers/iio/adc/stm32-adc-core.h              |   8 +
 drivers/iio/adc/stm32-adc.c                   | 418 ++++++++++++++++--
 3 files changed, 482 insertions(+), 52 deletions(-)

-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH 1/7] dt-bindings: iio: adc: add generic channel binding
  2021-09-08 15:54 ` Olivier Moysan
  (?)
@ 2021-09-08 15:54   ` Olivier Moysan
  -1 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: alsa-devel, devicetree, linux-arm-kernel, linux-iio,
	linux-kernel, linux-stm32

Add ADC generic channel binding. This binding should
be used as an alternate to legacy channel properties
whenever possible.
ADC generic channel binding allows to identify supported
internal channels through the following reserved label names:
"vddcore", "vrefint" and "vbat".
This binding also allows to set a different sampling time
for each channel.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
Note: The schema here is too permissive as either legacy or generic
channels properties are required. These properties are mutually
exclusive, however all attempts to describe this constraint are
failing. In particular the following schemas and their variants have
shown unsucessful.

oneOf:
  - anyOf:
	- required:
		- st,adc-channels
	- required:
		- st,adc-diff-channels
  - anyOf:
	- required:
		- $nodename

- if:
  patternProperties:
    "^channel@([0-9]|1[0-9])$":
      type: object
then:
  properties:
    st,adc-channels: false
    st,adc-diff-channels: false
else:
  - anyOf:
      - required:
          - st,adc-channels
      - required:
          - st,adc-diff-channels
---
 .../bindings/iio/adc/st,stm32-adc.yaml        | 100 ++++++++++++++++--
 1 file changed, 93 insertions(+), 7 deletions(-)

diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
index a58334c3bb76..a1f6cbe144ba 100644
--- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
@@ -222,6 +222,12 @@ patternProperties:
       '#io-channel-cells':
         const: 1
 
+      '#address-cells':
+        const: 1
+
+      '#size-cells':
+        const: 0
+
       interrupts:
         description: |
           IRQ Line for the ADC instance. Valid values are:
@@ -265,7 +271,9 @@ patternProperties:
           <vinp vinn>, <vinp vinn>,... vinp and vinn are numbered from 0 to 19.
 
           Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is
-          required. Both properties can be used together. Some channels can be
+          required if no adc generic channel is defined. These legacy channel
+          properties are exclusive with adc generic channel bindings.
+          Both properties can be used together. Some channels can be
           used as single-ended and some other ones as differential (mixed). But
           channels can't be configured both as single-ended and differential.
         $ref: /schemas/types.yaml#/definitions/uint32-matrix
@@ -290,6 +298,44 @@ patternProperties:
           each channel.
         $ref: /schemas/types.yaml#/definitions/uint32-array
 
+    patternProperties:
+      "^channel@([0-9]|1[0-9])$":
+        type: object
+        $ref: "adc.yaml"
+        description: |
+          Represents the external channels which are connected to the ADC.
+
+        properties:
+          reg:
+            items:
+              minimum: 0
+              maximum: 19
+
+          label:
+            description: |
+              Unique name to identify which channel this is.
+              Reserved label names "vddcore", "vrefint" and "vbat"
+              are used to identify internal channels with matching names.
+
+          diff-channels:
+            $ref: /schemas/types.yaml#/definitions/uint32-array
+            items:
+              minimum: 0
+              maximum: 19
+
+          st,min-sample-time-nsecs:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            description: |
+              Minimum sampling time in nanoseconds. Depending on hardware (board)
+              e.g. high/low analog input source impedance, fine tune of ADC
+              sampling time may be recommended.
+
+        required:
+          - reg
+
+    dependencies:
+      $nodename: [ '#address-cells', '#size-cells' ]
+
     allOf:
       - if:
           properties:
@@ -369,12 +415,6 @@ patternProperties:
 
     additionalProperties: false
 
-    anyOf:
-      - required:
-          - st,adc-channels
-      - required:
-          - st,adc-diff-channels
-
     required:
       - compatible
       - reg
@@ -451,4 +491,50 @@ examples:
         // other adc child node follow...
       };
 
+  - |
+    // Example 3: with stm32mp157c to setup ADC2 with:
+    // - internal channels 13, 14, 15.
+      #include <dt-bindings/interrupt-controller/arm-gic.h>
+      #include <dt-bindings/clock/stm32mp1-clks.h>
+      adc122: adc@48003000 {
+        compatible = "st,stm32mp1-adc-core";
+        reg = <0x48003000 0x400>;
+        interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
+                     <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&rcc ADC12>, <&rcc ADC12_K>;
+        clock-names = "bus", "adc";
+        booster-supply = <&booster>;
+        vdd-supply = <&vdd>;
+        vdda-supply = <&vdda>;
+        vref-supply = <&vref>;
+        st,syscfg = <&syscfg>;
+        interrupt-controller;
+        #interrupt-cells = <1>;
+        #address-cells = <1>;
+        #size-cells = <0>;
+        adc@100 {
+          compatible = "st,stm32mp1-adc";
+          #io-channel-cells = <1>;
+          reg = <0x100>;
+          interrupts = <1>;
+          #address-cells = <1>;
+          #size-cells = <0>;
+          channel@13 {
+            reg = <13>;
+            label = "vrefint";
+            st,min-sample-time-nsecs = <9000>;
+          };
+          channel@14 {
+            reg = <14>;
+            label = "vddcore";
+            st,min-sample-time-nsecs = <9000>;
+          };
+          channel@15 {
+            reg = <15>;
+            label = "vbat";
+            st,min-sample-time-nsecs = <9000>;
+          };
+        };
+      };
+
 ...
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 1/7] dt-bindings: iio: adc: add generic channel binding
@ 2021-09-08 15:54   ` Olivier Moysan
  0 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: devicetree, alsa-devel, linux-iio, linux-kernel, linux-stm32,
	linux-arm-kernel

Add ADC generic channel binding. This binding should
be used as an alternate to legacy channel properties
whenever possible.
ADC generic channel binding allows to identify supported
internal channels through the following reserved label names:
"vddcore", "vrefint" and "vbat".
This binding also allows to set a different sampling time
for each channel.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
Note: The schema here is too permissive as either legacy or generic
channels properties are required. These properties are mutually
exclusive, however all attempts to describe this constraint are
failing. In particular the following schemas and their variants have
shown unsucessful.

oneOf:
  - anyOf:
	- required:
		- st,adc-channels
	- required:
		- st,adc-diff-channels
  - anyOf:
	- required:
		- $nodename

- if:
  patternProperties:
    "^channel@([0-9]|1[0-9])$":
      type: object
then:
  properties:
    st,adc-channels: false
    st,adc-diff-channels: false
else:
  - anyOf:
      - required:
          - st,adc-channels
      - required:
          - st,adc-diff-channels
---
 .../bindings/iio/adc/st,stm32-adc.yaml        | 100 ++++++++++++++++--
 1 file changed, 93 insertions(+), 7 deletions(-)

diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
index a58334c3bb76..a1f6cbe144ba 100644
--- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
@@ -222,6 +222,12 @@ patternProperties:
       '#io-channel-cells':
         const: 1
 
+      '#address-cells':
+        const: 1
+
+      '#size-cells':
+        const: 0
+
       interrupts:
         description: |
           IRQ Line for the ADC instance. Valid values are:
@@ -265,7 +271,9 @@ patternProperties:
           <vinp vinn>, <vinp vinn>,... vinp and vinn are numbered from 0 to 19.
 
           Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is
-          required. Both properties can be used together. Some channels can be
+          required if no adc generic channel is defined. These legacy channel
+          properties are exclusive with adc generic channel bindings.
+          Both properties can be used together. Some channels can be
           used as single-ended and some other ones as differential (mixed). But
           channels can't be configured both as single-ended and differential.
         $ref: /schemas/types.yaml#/definitions/uint32-matrix
@@ -290,6 +298,44 @@ patternProperties:
           each channel.
         $ref: /schemas/types.yaml#/definitions/uint32-array
 
+    patternProperties:
+      "^channel@([0-9]|1[0-9])$":
+        type: object
+        $ref: "adc.yaml"
+        description: |
+          Represents the external channels which are connected to the ADC.
+
+        properties:
+          reg:
+            items:
+              minimum: 0
+              maximum: 19
+
+          label:
+            description: |
+              Unique name to identify which channel this is.
+              Reserved label names "vddcore", "vrefint" and "vbat"
+              are used to identify internal channels with matching names.
+
+          diff-channels:
+            $ref: /schemas/types.yaml#/definitions/uint32-array
+            items:
+              minimum: 0
+              maximum: 19
+
+          st,min-sample-time-nsecs:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            description: |
+              Minimum sampling time in nanoseconds. Depending on hardware (board)
+              e.g. high/low analog input source impedance, fine tune of ADC
+              sampling time may be recommended.
+
+        required:
+          - reg
+
+    dependencies:
+      $nodename: [ '#address-cells', '#size-cells' ]
+
     allOf:
       - if:
           properties:
@@ -369,12 +415,6 @@ patternProperties:
 
     additionalProperties: false
 
-    anyOf:
-      - required:
-          - st,adc-channels
-      - required:
-          - st,adc-diff-channels
-
     required:
       - compatible
       - reg
@@ -451,4 +491,50 @@ examples:
         // other adc child node follow...
       };
 
+  - |
+    // Example 3: with stm32mp157c to setup ADC2 with:
+    // - internal channels 13, 14, 15.
+      #include <dt-bindings/interrupt-controller/arm-gic.h>
+      #include <dt-bindings/clock/stm32mp1-clks.h>
+      adc122: adc@48003000 {
+        compatible = "st,stm32mp1-adc-core";
+        reg = <0x48003000 0x400>;
+        interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
+                     <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&rcc ADC12>, <&rcc ADC12_K>;
+        clock-names = "bus", "adc";
+        booster-supply = <&booster>;
+        vdd-supply = <&vdd>;
+        vdda-supply = <&vdda>;
+        vref-supply = <&vref>;
+        st,syscfg = <&syscfg>;
+        interrupt-controller;
+        #interrupt-cells = <1>;
+        #address-cells = <1>;
+        #size-cells = <0>;
+        adc@100 {
+          compatible = "st,stm32mp1-adc";
+          #io-channel-cells = <1>;
+          reg = <0x100>;
+          interrupts = <1>;
+          #address-cells = <1>;
+          #size-cells = <0>;
+          channel@13 {
+            reg = <13>;
+            label = "vrefint";
+            st,min-sample-time-nsecs = <9000>;
+          };
+          channel@14 {
+            reg = <14>;
+            label = "vddcore";
+            st,min-sample-time-nsecs = <9000>;
+          };
+          channel@15 {
+            reg = <15>;
+            label = "vbat";
+            st,min-sample-time-nsecs = <9000>;
+          };
+        };
+      };
+
 ...
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 1/7] dt-bindings: iio: adc: add generic channel binding
@ 2021-09-08 15:54   ` Olivier Moysan
  0 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: alsa-devel, devicetree, linux-arm-kernel, linux-iio,
	linux-kernel, linux-stm32

Add ADC generic channel binding. This binding should
be used as an alternate to legacy channel properties
whenever possible.
ADC generic channel binding allows to identify supported
internal channels through the following reserved label names:
"vddcore", "vrefint" and "vbat".
This binding also allows to set a different sampling time
for each channel.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
Note: The schema here is too permissive as either legacy or generic
channels properties are required. These properties are mutually
exclusive, however all attempts to describe this constraint are
failing. In particular the following schemas and their variants have
shown unsucessful.

oneOf:
  - anyOf:
	- required:
		- st,adc-channels
	- required:
		- st,adc-diff-channels
  - anyOf:
	- required:
		- $nodename

- if:
  patternProperties:
    "^channel@([0-9]|1[0-9])$":
      type: object
then:
  properties:
    st,adc-channels: false
    st,adc-diff-channels: false
else:
  - anyOf:
      - required:
          - st,adc-channels
      - required:
          - st,adc-diff-channels
---
 .../bindings/iio/adc/st,stm32-adc.yaml        | 100 ++++++++++++++++--
 1 file changed, 93 insertions(+), 7 deletions(-)

diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
index a58334c3bb76..a1f6cbe144ba 100644
--- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
@@ -222,6 +222,12 @@ patternProperties:
       '#io-channel-cells':
         const: 1
 
+      '#address-cells':
+        const: 1
+
+      '#size-cells':
+        const: 0
+
       interrupts:
         description: |
           IRQ Line for the ADC instance. Valid values are:
@@ -265,7 +271,9 @@ patternProperties:
           <vinp vinn>, <vinp vinn>,... vinp and vinn are numbered from 0 to 19.
 
           Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is
-          required. Both properties can be used together. Some channels can be
+          required if no adc generic channel is defined. These legacy channel
+          properties are exclusive with adc generic channel bindings.
+          Both properties can be used together. Some channels can be
           used as single-ended and some other ones as differential (mixed). But
           channels can't be configured both as single-ended and differential.
         $ref: /schemas/types.yaml#/definitions/uint32-matrix
@@ -290,6 +298,44 @@ patternProperties:
           each channel.
         $ref: /schemas/types.yaml#/definitions/uint32-array
 
+    patternProperties:
+      "^channel@([0-9]|1[0-9])$":
+        type: object
+        $ref: "adc.yaml"
+        description: |
+          Represents the external channels which are connected to the ADC.
+
+        properties:
+          reg:
+            items:
+              minimum: 0
+              maximum: 19
+
+          label:
+            description: |
+              Unique name to identify which channel this is.
+              Reserved label names "vddcore", "vrefint" and "vbat"
+              are used to identify internal channels with matching names.
+
+          diff-channels:
+            $ref: /schemas/types.yaml#/definitions/uint32-array
+            items:
+              minimum: 0
+              maximum: 19
+
+          st,min-sample-time-nsecs:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            description: |
+              Minimum sampling time in nanoseconds. Depending on hardware (board)
+              e.g. high/low analog input source impedance, fine tune of ADC
+              sampling time may be recommended.
+
+        required:
+          - reg
+
+    dependencies:
+      $nodename: [ '#address-cells', '#size-cells' ]
+
     allOf:
       - if:
           properties:
@@ -369,12 +415,6 @@ patternProperties:
 
     additionalProperties: false
 
-    anyOf:
-      - required:
-          - st,adc-channels
-      - required:
-          - st,adc-diff-channels
-
     required:
       - compatible
       - reg
@@ -451,4 +491,50 @@ examples:
         // other adc child node follow...
       };
 
+  - |
+    // Example 3: with stm32mp157c to setup ADC2 with:
+    // - internal channels 13, 14, 15.
+      #include <dt-bindings/interrupt-controller/arm-gic.h>
+      #include <dt-bindings/clock/stm32mp1-clks.h>
+      adc122: adc@48003000 {
+        compatible = "st,stm32mp1-adc-core";
+        reg = <0x48003000 0x400>;
+        interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
+                     <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&rcc ADC12>, <&rcc ADC12_K>;
+        clock-names = "bus", "adc";
+        booster-supply = <&booster>;
+        vdd-supply = <&vdd>;
+        vdda-supply = <&vdda>;
+        vref-supply = <&vref>;
+        st,syscfg = <&syscfg>;
+        interrupt-controller;
+        #interrupt-cells = <1>;
+        #address-cells = <1>;
+        #size-cells = <0>;
+        adc@100 {
+          compatible = "st,stm32mp1-adc";
+          #io-channel-cells = <1>;
+          reg = <0x100>;
+          interrupts = <1>;
+          #address-cells = <1>;
+          #size-cells = <0>;
+          channel@13 {
+            reg = <13>;
+            label = "vrefint";
+            st,min-sample-time-nsecs = <9000>;
+          };
+          channel@14 {
+            reg = <14>;
+            label = "vddcore";
+            st,min-sample-time-nsecs = <9000>;
+          };
+          channel@15 {
+            reg = <15>;
+            label = "vbat";
+            st,min-sample-time-nsecs = <9000>;
+          };
+        };
+      };
+
 ...
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 2/7] dt-bindings: iio: adc: add nvmem support for vrefint internal channel
  2021-09-08 15:54 ` Olivier Moysan
  (?)
@ 2021-09-08 15:54   ` Olivier Moysan
  -1 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: alsa-devel, devicetree, linux-arm-kernel, linux-iio,
	linux-kernel, linux-stm32

Add support of nvmem. This allows to retrieve calibration data from OTP
for vrefint internal channel.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
 .../devicetree/bindings/iio/adc/st,stm32-adc.yaml         | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
index a1f6cbe144ba..772e6cf95dc0 100644
--- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
@@ -298,6 +298,14 @@ patternProperties:
           each channel.
         $ref: /schemas/types.yaml#/definitions/uint32-array
 
+      nvmem-cells:
+        items:
+          - description: Phandle to the calibration vrefint data provided by otp
+
+      nvmem-cell-names:
+        items:
+          - const: vrefint
+
     patternProperties:
       "^channel@([0-9]|1[0-9])$":
         type: object
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 2/7] dt-bindings: iio: adc: add nvmem support for vrefint internal channel
@ 2021-09-08 15:54   ` Olivier Moysan
  0 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: devicetree, alsa-devel, linux-iio, linux-kernel, linux-stm32,
	linux-arm-kernel

Add support of nvmem. This allows to retrieve calibration data from OTP
for vrefint internal channel.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
 .../devicetree/bindings/iio/adc/st,stm32-adc.yaml         | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
index a1f6cbe144ba..772e6cf95dc0 100644
--- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
@@ -298,6 +298,14 @@ patternProperties:
           each channel.
         $ref: /schemas/types.yaml#/definitions/uint32-array
 
+      nvmem-cells:
+        items:
+          - description: Phandle to the calibration vrefint data provided by otp
+
+      nvmem-cell-names:
+        items:
+          - const: vrefint
+
     patternProperties:
       "^channel@([0-9]|1[0-9])$":
         type: object
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 2/7] dt-bindings: iio: adc: add nvmem support for vrefint internal channel
@ 2021-09-08 15:54   ` Olivier Moysan
  0 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: alsa-devel, devicetree, linux-arm-kernel, linux-iio,
	linux-kernel, linux-stm32

Add support of nvmem. This allows to retrieve calibration data from OTP
for vrefint internal channel.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
 .../devicetree/bindings/iio/adc/st,stm32-adc.yaml         | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
index a1f6cbe144ba..772e6cf95dc0 100644
--- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
@@ -298,6 +298,14 @@ patternProperties:
           each channel.
         $ref: /schemas/types.yaml#/definitions/uint32-array
 
+      nvmem-cells:
+        items:
+          - description: Phandle to the calibration vrefint data provided by otp
+
+      nvmem-cell-names:
+        items:
+          - const: vrefint
+
     patternProperties:
       "^channel@([0-9]|1[0-9])$":
         type: object
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 3/7] iio: adc stm32-adc: split channel init into several routines
  2021-09-08 15:54 ` Olivier Moysan
  (?)
@ 2021-09-08 15:54   ` Olivier Moysan
  -1 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: alsa-devel, devicetree, linux-arm-kernel, linux-iio,
	linux-kernel, linux-stm32

Split stm32_adc_chan_of_init channel initialization function into
several routines to increase readability and prepare channel
generic binding handling.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
 drivers/iio/adc/stm32-adc.c | 131 ++++++++++++++++++++++++------------
 1 file changed, 87 insertions(+), 44 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 5088de835bb1..2f137d14f141 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -194,6 +194,7 @@ struct stm32_adc_cfg {
  * @smpr_val:		sampling time settings (e.g. smpr1 / smpr2)
  * @cal:		optional calibration data on some devices
  * @chan_name:		channel name array
+ * @num_diff:		number of differential channels
  */
 struct stm32_adc {
 	struct stm32_adc_common	*common;
@@ -217,6 +218,7 @@ struct stm32_adc {
 	u32			smpr_val[2];
 	struct stm32_adc_calib	cal;
 	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
+	u32			num_diff;
 };
 
 struct stm32_adc_diff_channel {
@@ -1706,17 +1708,11 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
 	}
 }
 
-static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
+static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm32_adc *adc)
 {
 	struct device_node *node = indio_dev->dev.of_node;
-	struct stm32_adc *adc = iio_priv(indio_dev);
 	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
-	struct stm32_adc_diff_channel diff[STM32_ADC_CH_MAX];
-	struct property *prop;
-	const __be32 *cur;
-	struct iio_chan_spec *channels;
-	int scan_index = 0, num_channels = 0, num_diff = 0, ret, i;
-	u32 val, smp = 0;
+	int num_channels = 0, ret;
 
 	ret = of_property_count_u32_elems(node, "st,adc-channels");
 	if (ret > adc_info->max_channels) {
@@ -1727,41 +1723,54 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 	}
 
 	ret = of_property_count_elems_of_size(node, "st,adc-diff-channels",
-					      sizeof(*diff));
+					      sizeof(struct stm32_adc_diff_channel));
 	if (ret > adc_info->max_channels) {
 		dev_err(&indio_dev->dev, "Bad st,adc-diff-channels?\n");
 		return -EINVAL;
 	} else if (ret > 0) {
-		int size = ret * sizeof(*diff) / sizeof(u32);
-
-		num_diff = ret;
+		adc->num_diff = ret;
 		num_channels += ret;
+	}
+
+	return num_channels;
+}
+
+static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
+				      struct stm32_adc *adc,
+				      struct iio_chan_spec *channels)
+{
+	struct device_node *node = indio_dev->dev.of_node;
+	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
+	struct stm32_adc_diff_channel diff[STM32_ADC_CH_MAX];
+	u32 num_diff = adc->num_diff;
+	int size = num_diff * sizeof(*diff) / sizeof(u32);
+	int scan_index = 0, val, ret, i;
+	struct property *prop;
+	const __be32 *cur;
+
+	if (num_diff) {
 		ret = of_property_read_u32_array(node, "st,adc-diff-channels",
 						 (u32 *)diff, size);
-		if (ret)
+		if (ret) {
+			dev_err(&indio_dev->dev, "Failed to get diff channels %d\n", ret);
 			return ret;
-	}
+		}
 
-	if (!num_channels) {
-		dev_err(&indio_dev->dev, "No channels configured\n");
-		return -ENODATA;
-	}
+		for (i = 0; i < num_diff; i++) {
+			if (diff[i].vinp >= adc_info->max_channels ||
+			    diff[i].vinn >= adc_info->max_channels) {
+				dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
+					diff[i].vinp, diff[i].vinn);
+				return -EINVAL;
+			}
 
-	/* Optional sample time is provided either for each, or all channels */
-	ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs");
-	if (ret > 1 && ret != num_channels) {
-		dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n");
-		return -EINVAL;
+			stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
+						diff[i].vinp, diff[i].vinn,
+						scan_index, true);
+			scan_index++;
+		}
 	}
 
-	if (timestamping)
-		num_channels++;
-
-	channels = devm_kcalloc(&indio_dev->dev, num_channels,
-				sizeof(struct iio_chan_spec), GFP_KERNEL);
-	if (!channels)
-		return -ENOMEM;
-
 	of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
 		if (val >= adc_info->max_channels) {
 			dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
@@ -1771,8 +1780,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 		/* Channel can't be configured both as single-ended & diff */
 		for (i = 0; i < num_diff; i++) {
 			if (val == diff[i].vinp) {
-				dev_err(&indio_dev->dev,
-					"channel %d miss-configured\n",	val);
+				dev_err(&indio_dev->dev, "channel %d misconfigured\n",	val);
 				return -EINVAL;
 			}
 		}
@@ -1781,19 +1789,54 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 		scan_index++;
 	}
 
-	for (i = 0; i < num_diff; i++) {
-		if (diff[i].vinp >= adc_info->max_channels ||
-		    diff[i].vinn >= adc_info->max_channels) {
-			dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
-				diff[i].vinp, diff[i].vinn);
-			return -EINVAL;
-		}
-		stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
-					diff[i].vinp, diff[i].vinn, scan_index,
-					true);
-		scan_index++;
+	return scan_index;
+}
+
+static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
+{
+	struct device_node *node = indio_dev->dev.of_node;
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
+	struct iio_chan_spec *channels;
+	int scan_index = 0, num_channels = 0, ret, i;
+	u32 smp = 0;
+
+	ret = stm32_adc_get_legacy_chan_count(indio_dev, adc);
+	if (ret < 0)
+		return ret;
+	num_channels = ret;
+
+	if (!num_channels) {
+		dev_err(&indio_dev->dev, "No channels configured\n");
+		return -ENODATA;
 	}
 
+	if (num_channels > adc_info->max_channels) {
+		dev_err(&indio_dev->dev, "Channel number [%d] exceeds %d\n",
+			num_channels, adc_info->max_channels);
+		return -EINVAL;
+	}
+
+	/* Optional sample time is provided either for each, or all channels */
+	ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs");
+	if (ret > 1 && ret != num_channels) {
+		dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n");
+		return -EINVAL;
+	}
+
+	if (timestamping)
+		num_channels++;
+
+	channels = devm_kcalloc(&indio_dev->dev, num_channels,
+				sizeof(struct iio_chan_spec), GFP_KERNEL);
+	if (!channels)
+		return -ENOMEM;
+
+	ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels);
+	if (ret < 0)
+		return ret;
+	scan_index = ret;
+
 	for (i = 0; i < scan_index; i++) {
 		/*
 		 * Using of_property_read_u32_index(), smp value will only be
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 3/7] iio: adc stm32-adc: split channel init into several routines
@ 2021-09-08 15:54   ` Olivier Moysan
  0 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: devicetree, alsa-devel, linux-iio, linux-kernel, linux-stm32,
	linux-arm-kernel

Split stm32_adc_chan_of_init channel initialization function into
several routines to increase readability and prepare channel
generic binding handling.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
 drivers/iio/adc/stm32-adc.c | 131 ++++++++++++++++++++++++------------
 1 file changed, 87 insertions(+), 44 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 5088de835bb1..2f137d14f141 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -194,6 +194,7 @@ struct stm32_adc_cfg {
  * @smpr_val:		sampling time settings (e.g. smpr1 / smpr2)
  * @cal:		optional calibration data on some devices
  * @chan_name:		channel name array
+ * @num_diff:		number of differential channels
  */
 struct stm32_adc {
 	struct stm32_adc_common	*common;
@@ -217,6 +218,7 @@ struct stm32_adc {
 	u32			smpr_val[2];
 	struct stm32_adc_calib	cal;
 	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
+	u32			num_diff;
 };
 
 struct stm32_adc_diff_channel {
@@ -1706,17 +1708,11 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
 	}
 }
 
-static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
+static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm32_adc *adc)
 {
 	struct device_node *node = indio_dev->dev.of_node;
-	struct stm32_adc *adc = iio_priv(indio_dev);
 	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
-	struct stm32_adc_diff_channel diff[STM32_ADC_CH_MAX];
-	struct property *prop;
-	const __be32 *cur;
-	struct iio_chan_spec *channels;
-	int scan_index = 0, num_channels = 0, num_diff = 0, ret, i;
-	u32 val, smp = 0;
+	int num_channels = 0, ret;
 
 	ret = of_property_count_u32_elems(node, "st,adc-channels");
 	if (ret > adc_info->max_channels) {
@@ -1727,41 +1723,54 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 	}
 
 	ret = of_property_count_elems_of_size(node, "st,adc-diff-channels",
-					      sizeof(*diff));
+					      sizeof(struct stm32_adc_diff_channel));
 	if (ret > adc_info->max_channels) {
 		dev_err(&indio_dev->dev, "Bad st,adc-diff-channels?\n");
 		return -EINVAL;
 	} else if (ret > 0) {
-		int size = ret * sizeof(*diff) / sizeof(u32);
-
-		num_diff = ret;
+		adc->num_diff = ret;
 		num_channels += ret;
+	}
+
+	return num_channels;
+}
+
+static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
+				      struct stm32_adc *adc,
+				      struct iio_chan_spec *channels)
+{
+	struct device_node *node = indio_dev->dev.of_node;
+	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
+	struct stm32_adc_diff_channel diff[STM32_ADC_CH_MAX];
+	u32 num_diff = adc->num_diff;
+	int size = num_diff * sizeof(*diff) / sizeof(u32);
+	int scan_index = 0, val, ret, i;
+	struct property *prop;
+	const __be32 *cur;
+
+	if (num_diff) {
 		ret = of_property_read_u32_array(node, "st,adc-diff-channels",
 						 (u32 *)diff, size);
-		if (ret)
+		if (ret) {
+			dev_err(&indio_dev->dev, "Failed to get diff channels %d\n", ret);
 			return ret;
-	}
+		}
 
-	if (!num_channels) {
-		dev_err(&indio_dev->dev, "No channels configured\n");
-		return -ENODATA;
-	}
+		for (i = 0; i < num_diff; i++) {
+			if (diff[i].vinp >= adc_info->max_channels ||
+			    diff[i].vinn >= adc_info->max_channels) {
+				dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
+					diff[i].vinp, diff[i].vinn);
+				return -EINVAL;
+			}
 
-	/* Optional sample time is provided either for each, or all channels */
-	ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs");
-	if (ret > 1 && ret != num_channels) {
-		dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n");
-		return -EINVAL;
+			stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
+						diff[i].vinp, diff[i].vinn,
+						scan_index, true);
+			scan_index++;
+		}
 	}
 
-	if (timestamping)
-		num_channels++;
-
-	channels = devm_kcalloc(&indio_dev->dev, num_channels,
-				sizeof(struct iio_chan_spec), GFP_KERNEL);
-	if (!channels)
-		return -ENOMEM;
-
 	of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
 		if (val >= adc_info->max_channels) {
 			dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
@@ -1771,8 +1780,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 		/* Channel can't be configured both as single-ended & diff */
 		for (i = 0; i < num_diff; i++) {
 			if (val == diff[i].vinp) {
-				dev_err(&indio_dev->dev,
-					"channel %d miss-configured\n",	val);
+				dev_err(&indio_dev->dev, "channel %d misconfigured\n",	val);
 				return -EINVAL;
 			}
 		}
@@ -1781,19 +1789,54 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 		scan_index++;
 	}
 
-	for (i = 0; i < num_diff; i++) {
-		if (diff[i].vinp >= adc_info->max_channels ||
-		    diff[i].vinn >= adc_info->max_channels) {
-			dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
-				diff[i].vinp, diff[i].vinn);
-			return -EINVAL;
-		}
-		stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
-					diff[i].vinp, diff[i].vinn, scan_index,
-					true);
-		scan_index++;
+	return scan_index;
+}
+
+static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
+{
+	struct device_node *node = indio_dev->dev.of_node;
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
+	struct iio_chan_spec *channels;
+	int scan_index = 0, num_channels = 0, ret, i;
+	u32 smp = 0;
+
+	ret = stm32_adc_get_legacy_chan_count(indio_dev, adc);
+	if (ret < 0)
+		return ret;
+	num_channels = ret;
+
+	if (!num_channels) {
+		dev_err(&indio_dev->dev, "No channels configured\n");
+		return -ENODATA;
 	}
 
+	if (num_channels > adc_info->max_channels) {
+		dev_err(&indio_dev->dev, "Channel number [%d] exceeds %d\n",
+			num_channels, adc_info->max_channels);
+		return -EINVAL;
+	}
+
+	/* Optional sample time is provided either for each, or all channels */
+	ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs");
+	if (ret > 1 && ret != num_channels) {
+		dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n");
+		return -EINVAL;
+	}
+
+	if (timestamping)
+		num_channels++;
+
+	channels = devm_kcalloc(&indio_dev->dev, num_channels,
+				sizeof(struct iio_chan_spec), GFP_KERNEL);
+	if (!channels)
+		return -ENOMEM;
+
+	ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels);
+	if (ret < 0)
+		return ret;
+	scan_index = ret;
+
 	for (i = 0; i < scan_index; i++) {
 		/*
 		 * Using of_property_read_u32_index(), smp value will only be
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 3/7] iio: adc stm32-adc: split channel init into several routines
@ 2021-09-08 15:54   ` Olivier Moysan
  0 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: alsa-devel, devicetree, linux-arm-kernel, linux-iio,
	linux-kernel, linux-stm32

Split stm32_adc_chan_of_init channel initialization function into
several routines to increase readability and prepare channel
generic binding handling.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
 drivers/iio/adc/stm32-adc.c | 131 ++++++++++++++++++++++++------------
 1 file changed, 87 insertions(+), 44 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 5088de835bb1..2f137d14f141 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -194,6 +194,7 @@ struct stm32_adc_cfg {
  * @smpr_val:		sampling time settings (e.g. smpr1 / smpr2)
  * @cal:		optional calibration data on some devices
  * @chan_name:		channel name array
+ * @num_diff:		number of differential channels
  */
 struct stm32_adc {
 	struct stm32_adc_common	*common;
@@ -217,6 +218,7 @@ struct stm32_adc {
 	u32			smpr_val[2];
 	struct stm32_adc_calib	cal;
 	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
+	u32			num_diff;
 };
 
 struct stm32_adc_diff_channel {
@@ -1706,17 +1708,11 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
 	}
 }
 
-static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
+static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm32_adc *adc)
 {
 	struct device_node *node = indio_dev->dev.of_node;
-	struct stm32_adc *adc = iio_priv(indio_dev);
 	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
-	struct stm32_adc_diff_channel diff[STM32_ADC_CH_MAX];
-	struct property *prop;
-	const __be32 *cur;
-	struct iio_chan_spec *channels;
-	int scan_index = 0, num_channels = 0, num_diff = 0, ret, i;
-	u32 val, smp = 0;
+	int num_channels = 0, ret;
 
 	ret = of_property_count_u32_elems(node, "st,adc-channels");
 	if (ret > adc_info->max_channels) {
@@ -1727,41 +1723,54 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 	}
 
 	ret = of_property_count_elems_of_size(node, "st,adc-diff-channels",
-					      sizeof(*diff));
+					      sizeof(struct stm32_adc_diff_channel));
 	if (ret > adc_info->max_channels) {
 		dev_err(&indio_dev->dev, "Bad st,adc-diff-channels?\n");
 		return -EINVAL;
 	} else if (ret > 0) {
-		int size = ret * sizeof(*diff) / sizeof(u32);
-
-		num_diff = ret;
+		adc->num_diff = ret;
 		num_channels += ret;
+	}
+
+	return num_channels;
+}
+
+static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
+				      struct stm32_adc *adc,
+				      struct iio_chan_spec *channels)
+{
+	struct device_node *node = indio_dev->dev.of_node;
+	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
+	struct stm32_adc_diff_channel diff[STM32_ADC_CH_MAX];
+	u32 num_diff = adc->num_diff;
+	int size = num_diff * sizeof(*diff) / sizeof(u32);
+	int scan_index = 0, val, ret, i;
+	struct property *prop;
+	const __be32 *cur;
+
+	if (num_diff) {
 		ret = of_property_read_u32_array(node, "st,adc-diff-channels",
 						 (u32 *)diff, size);
-		if (ret)
+		if (ret) {
+			dev_err(&indio_dev->dev, "Failed to get diff channels %d\n", ret);
 			return ret;
-	}
+		}
 
-	if (!num_channels) {
-		dev_err(&indio_dev->dev, "No channels configured\n");
-		return -ENODATA;
-	}
+		for (i = 0; i < num_diff; i++) {
+			if (diff[i].vinp >= adc_info->max_channels ||
+			    diff[i].vinn >= adc_info->max_channels) {
+				dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
+					diff[i].vinp, diff[i].vinn);
+				return -EINVAL;
+			}
 
-	/* Optional sample time is provided either for each, or all channels */
-	ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs");
-	if (ret > 1 && ret != num_channels) {
-		dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n");
-		return -EINVAL;
+			stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
+						diff[i].vinp, diff[i].vinn,
+						scan_index, true);
+			scan_index++;
+		}
 	}
 
-	if (timestamping)
-		num_channels++;
-
-	channels = devm_kcalloc(&indio_dev->dev, num_channels,
-				sizeof(struct iio_chan_spec), GFP_KERNEL);
-	if (!channels)
-		return -ENOMEM;
-
 	of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
 		if (val >= adc_info->max_channels) {
 			dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
@@ -1771,8 +1780,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 		/* Channel can't be configured both as single-ended & diff */
 		for (i = 0; i < num_diff; i++) {
 			if (val == diff[i].vinp) {
-				dev_err(&indio_dev->dev,
-					"channel %d miss-configured\n",	val);
+				dev_err(&indio_dev->dev, "channel %d misconfigured\n",	val);
 				return -EINVAL;
 			}
 		}
@@ -1781,19 +1789,54 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 		scan_index++;
 	}
 
-	for (i = 0; i < num_diff; i++) {
-		if (diff[i].vinp >= adc_info->max_channels ||
-		    diff[i].vinn >= adc_info->max_channels) {
-			dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
-				diff[i].vinp, diff[i].vinn);
-			return -EINVAL;
-		}
-		stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
-					diff[i].vinp, diff[i].vinn, scan_index,
-					true);
-		scan_index++;
+	return scan_index;
+}
+
+static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
+{
+	struct device_node *node = indio_dev->dev.of_node;
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
+	struct iio_chan_spec *channels;
+	int scan_index = 0, num_channels = 0, ret, i;
+	u32 smp = 0;
+
+	ret = stm32_adc_get_legacy_chan_count(indio_dev, adc);
+	if (ret < 0)
+		return ret;
+	num_channels = ret;
+
+	if (!num_channels) {
+		dev_err(&indio_dev->dev, "No channels configured\n");
+		return -ENODATA;
 	}
 
+	if (num_channels > adc_info->max_channels) {
+		dev_err(&indio_dev->dev, "Channel number [%d] exceeds %d\n",
+			num_channels, adc_info->max_channels);
+		return -EINVAL;
+	}
+
+	/* Optional sample time is provided either for each, or all channels */
+	ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs");
+	if (ret > 1 && ret != num_channels) {
+		dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n");
+		return -EINVAL;
+	}
+
+	if (timestamping)
+		num_channels++;
+
+	channels = devm_kcalloc(&indio_dev->dev, num_channels,
+				sizeof(struct iio_chan_spec), GFP_KERNEL);
+	if (!channels)
+		return -ENOMEM;
+
+	ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels);
+	if (ret < 0)
+		return ret;
+	scan_index = ret;
+
 	for (i = 0; i < scan_index; i++) {
 		/*
 		 * Using of_property_read_u32_index(), smp value will only be
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 4/7] iio: adc: stm32-adc: add support of generic channels binding
  2021-09-08 15:54 ` Olivier Moysan
  (?)
@ 2021-09-08 15:54   ` Olivier Moysan
  -1 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: alsa-devel, devicetree, linux-arm-kernel, linux-iio,
	linux-kernel, linux-stm32

Add support of generic IIO channel binding:
./devicetree/bindings/iio/adc/adc.yaml
Keep support of st,adc-channels and st,adc-diff-channels
for backward compatibility.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
 drivers/iio/adc/stm32-adc.c | 99 +++++++++++++++++++++++++++++++++----
 1 file changed, 90 insertions(+), 9 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 2f137d14f141..ae4a314854f7 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -35,7 +35,7 @@
 #define STM32H7_BOOST_CLKRATE		20000000UL
 
 #define STM32_ADC_CH_MAX		20	/* max number of channels */
-#define STM32_ADC_CH_SZ			10	/* max channel name size */
+#define STM32_ADC_CH_SZ			16	/* max channel name size */
 #define STM32_ADC_MAX_SQ		16	/* SQ1..SQ16 */
 #define STM32_ADC_MAX_SMP		7	/* SMPx range is [0..7] */
 #define STM32_ADC_TIMEOUT_US		100000
@@ -1732,6 +1732,11 @@ static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm
 		num_channels += ret;
 	}
 
+	if (!num_channels) {
+		dev_err(indio_dev->dev.parent, "No channel found\n");
+		return -ENODATA;
+	}
+
 	return num_channels;
 }
 
@@ -1792,6 +1797,73 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
 	return scan_index;
 }
 
+static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
+				       struct stm32_adc *adc,
+				       struct iio_chan_spec *channels)
+{
+	struct device_node *node = indio_dev->dev.of_node;
+	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
+	struct device_node *child;
+	const char *name;
+	int val, scan_index = 0, ret;
+	bool differential;
+	u32 vin[2];
+
+	for_each_available_child_of_node(node, child) {
+		ret = of_property_read_u32(child, "reg", &val);
+		if (ret) {
+			dev_err(&indio_dev->dev, "Missing channel index %d\n", ret);
+			goto err;
+		}
+
+		ret = of_property_read_string(child, "label", &name);
+		/* label is optional */
+		if (!ret) {
+			if (strlen(name) >= STM32_ADC_CH_SZ) {
+				dev_err(&indio_dev->dev, "Label %s exceeds %d characters\n",
+					name, STM32_ADC_CH_SZ);
+				return -EINVAL;
+			}
+			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
+		} else if (ret != -EINVAL) {
+			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
+			goto err;
+		}
+
+		if (val >= adc_info->max_channels) {
+			dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
+			ret = -EINVAL;
+			goto err;
+		}
+
+		differential = false;
+		ret = of_property_read_u32_array(child, "diff-channels", vin, 2);
+		/* diff-channels is optional */
+		if (!ret) {
+			differential = true;
+			if (vin[0] != val || vin[1] >= adc_info->max_channels) {
+				dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
+					vin[0], vin[1]);
+				goto err;
+			}
+		} else if (ret != -EINVAL) {
+			dev_err(&indio_dev->dev, "Invalid diff-channels property %d\n", ret);
+			goto err;
+		}
+
+		stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val,
+					vin[1], scan_index, differential);
+		scan_index++;
+	}
+
+	return scan_index;
+
+err:
+	of_node_put(child);
+
+	return ret;
+}
+
 static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 {
 	struct device_node *node = indio_dev->dev.of_node;
@@ -1800,15 +1872,21 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 	struct iio_chan_spec *channels;
 	int scan_index = 0, num_channels = 0, ret, i;
 	u32 smp = 0;
+	bool legacy = false;
 
-	ret = stm32_adc_get_legacy_chan_count(indio_dev, adc);
-	if (ret < 0)
-		return ret;
-	num_channels = ret;
-
+	num_channels = of_get_available_child_count(node);
+	/*
+	 * If no channels have been found, fallback to channels legacy properties.
+	 * Legacy channel properties will be ignored, if some channels are
+	 * already defined using the standard binding.
+	 */
 	if (!num_channels) {
-		dev_err(&indio_dev->dev, "No channels configured\n");
-		return -ENODATA;
+		ret = stm32_adc_get_legacy_chan_count(indio_dev, adc);
+		if (ret < 0)
+			return ret;
+
+		legacy = true;
+		num_channels = ret;
 	}
 
 	if (num_channels > adc_info->max_channels) {
@@ -1832,7 +1910,10 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 	if (!channels)
 		return -ENOMEM;
 
-	ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels);
+	if (legacy)
+		ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels);
+	else
+		ret = stm32_adc_generic_chan_init(indio_dev, adc, channels);
 	if (ret < 0)
 		return ret;
 	scan_index = ret;
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 4/7] iio: adc: stm32-adc: add support of generic channels binding
@ 2021-09-08 15:54   ` Olivier Moysan
  0 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: devicetree, alsa-devel, linux-iio, linux-kernel, linux-stm32,
	linux-arm-kernel

Add support of generic IIO channel binding:
./devicetree/bindings/iio/adc/adc.yaml
Keep support of st,adc-channels and st,adc-diff-channels
for backward compatibility.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
 drivers/iio/adc/stm32-adc.c | 99 +++++++++++++++++++++++++++++++++----
 1 file changed, 90 insertions(+), 9 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 2f137d14f141..ae4a314854f7 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -35,7 +35,7 @@
 #define STM32H7_BOOST_CLKRATE		20000000UL
 
 #define STM32_ADC_CH_MAX		20	/* max number of channels */
-#define STM32_ADC_CH_SZ			10	/* max channel name size */
+#define STM32_ADC_CH_SZ			16	/* max channel name size */
 #define STM32_ADC_MAX_SQ		16	/* SQ1..SQ16 */
 #define STM32_ADC_MAX_SMP		7	/* SMPx range is [0..7] */
 #define STM32_ADC_TIMEOUT_US		100000
@@ -1732,6 +1732,11 @@ static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm
 		num_channels += ret;
 	}
 
+	if (!num_channels) {
+		dev_err(indio_dev->dev.parent, "No channel found\n");
+		return -ENODATA;
+	}
+
 	return num_channels;
 }
 
@@ -1792,6 +1797,73 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
 	return scan_index;
 }
 
+static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
+				       struct stm32_adc *adc,
+				       struct iio_chan_spec *channels)
+{
+	struct device_node *node = indio_dev->dev.of_node;
+	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
+	struct device_node *child;
+	const char *name;
+	int val, scan_index = 0, ret;
+	bool differential;
+	u32 vin[2];
+
+	for_each_available_child_of_node(node, child) {
+		ret = of_property_read_u32(child, "reg", &val);
+		if (ret) {
+			dev_err(&indio_dev->dev, "Missing channel index %d\n", ret);
+			goto err;
+		}
+
+		ret = of_property_read_string(child, "label", &name);
+		/* label is optional */
+		if (!ret) {
+			if (strlen(name) >= STM32_ADC_CH_SZ) {
+				dev_err(&indio_dev->dev, "Label %s exceeds %d characters\n",
+					name, STM32_ADC_CH_SZ);
+				return -EINVAL;
+			}
+			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
+		} else if (ret != -EINVAL) {
+			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
+			goto err;
+		}
+
+		if (val >= adc_info->max_channels) {
+			dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
+			ret = -EINVAL;
+			goto err;
+		}
+
+		differential = false;
+		ret = of_property_read_u32_array(child, "diff-channels", vin, 2);
+		/* diff-channels is optional */
+		if (!ret) {
+			differential = true;
+			if (vin[0] != val || vin[1] >= adc_info->max_channels) {
+				dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
+					vin[0], vin[1]);
+				goto err;
+			}
+		} else if (ret != -EINVAL) {
+			dev_err(&indio_dev->dev, "Invalid diff-channels property %d\n", ret);
+			goto err;
+		}
+
+		stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val,
+					vin[1], scan_index, differential);
+		scan_index++;
+	}
+
+	return scan_index;
+
+err:
+	of_node_put(child);
+
+	return ret;
+}
+
 static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 {
 	struct device_node *node = indio_dev->dev.of_node;
@@ -1800,15 +1872,21 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 	struct iio_chan_spec *channels;
 	int scan_index = 0, num_channels = 0, ret, i;
 	u32 smp = 0;
+	bool legacy = false;
 
-	ret = stm32_adc_get_legacy_chan_count(indio_dev, adc);
-	if (ret < 0)
-		return ret;
-	num_channels = ret;
-
+	num_channels = of_get_available_child_count(node);
+	/*
+	 * If no channels have been found, fallback to channels legacy properties.
+	 * Legacy channel properties will be ignored, if some channels are
+	 * already defined using the standard binding.
+	 */
 	if (!num_channels) {
-		dev_err(&indio_dev->dev, "No channels configured\n");
-		return -ENODATA;
+		ret = stm32_adc_get_legacy_chan_count(indio_dev, adc);
+		if (ret < 0)
+			return ret;
+
+		legacy = true;
+		num_channels = ret;
 	}
 
 	if (num_channels > adc_info->max_channels) {
@@ -1832,7 +1910,10 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 	if (!channels)
 		return -ENOMEM;
 
-	ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels);
+	if (legacy)
+		ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels);
+	else
+		ret = stm32_adc_generic_chan_init(indio_dev, adc, channels);
 	if (ret < 0)
 		return ret;
 	scan_index = ret;
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 4/7] iio: adc: stm32-adc: add support of generic channels binding
@ 2021-09-08 15:54   ` Olivier Moysan
  0 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: alsa-devel, devicetree, linux-arm-kernel, linux-iio,
	linux-kernel, linux-stm32

Add support of generic IIO channel binding:
./devicetree/bindings/iio/adc/adc.yaml
Keep support of st,adc-channels and st,adc-diff-channels
for backward compatibility.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
 drivers/iio/adc/stm32-adc.c | 99 +++++++++++++++++++++++++++++++++----
 1 file changed, 90 insertions(+), 9 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 2f137d14f141..ae4a314854f7 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -35,7 +35,7 @@
 #define STM32H7_BOOST_CLKRATE		20000000UL
 
 #define STM32_ADC_CH_MAX		20	/* max number of channels */
-#define STM32_ADC_CH_SZ			10	/* max channel name size */
+#define STM32_ADC_CH_SZ			16	/* max channel name size */
 #define STM32_ADC_MAX_SQ		16	/* SQ1..SQ16 */
 #define STM32_ADC_MAX_SMP		7	/* SMPx range is [0..7] */
 #define STM32_ADC_TIMEOUT_US		100000
@@ -1732,6 +1732,11 @@ static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm
 		num_channels += ret;
 	}
 
+	if (!num_channels) {
+		dev_err(indio_dev->dev.parent, "No channel found\n");
+		return -ENODATA;
+	}
+
 	return num_channels;
 }
 
@@ -1792,6 +1797,73 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
 	return scan_index;
 }
 
+static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
+				       struct stm32_adc *adc,
+				       struct iio_chan_spec *channels)
+{
+	struct device_node *node = indio_dev->dev.of_node;
+	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
+	struct device_node *child;
+	const char *name;
+	int val, scan_index = 0, ret;
+	bool differential;
+	u32 vin[2];
+
+	for_each_available_child_of_node(node, child) {
+		ret = of_property_read_u32(child, "reg", &val);
+		if (ret) {
+			dev_err(&indio_dev->dev, "Missing channel index %d\n", ret);
+			goto err;
+		}
+
+		ret = of_property_read_string(child, "label", &name);
+		/* label is optional */
+		if (!ret) {
+			if (strlen(name) >= STM32_ADC_CH_SZ) {
+				dev_err(&indio_dev->dev, "Label %s exceeds %d characters\n",
+					name, STM32_ADC_CH_SZ);
+				return -EINVAL;
+			}
+			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
+		} else if (ret != -EINVAL) {
+			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
+			goto err;
+		}
+
+		if (val >= adc_info->max_channels) {
+			dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
+			ret = -EINVAL;
+			goto err;
+		}
+
+		differential = false;
+		ret = of_property_read_u32_array(child, "diff-channels", vin, 2);
+		/* diff-channels is optional */
+		if (!ret) {
+			differential = true;
+			if (vin[0] != val || vin[1] >= adc_info->max_channels) {
+				dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
+					vin[0], vin[1]);
+				goto err;
+			}
+		} else if (ret != -EINVAL) {
+			dev_err(&indio_dev->dev, "Invalid diff-channels property %d\n", ret);
+			goto err;
+		}
+
+		stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val,
+					vin[1], scan_index, differential);
+		scan_index++;
+	}
+
+	return scan_index;
+
+err:
+	of_node_put(child);
+
+	return ret;
+}
+
 static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 {
 	struct device_node *node = indio_dev->dev.of_node;
@@ -1800,15 +1872,21 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 	struct iio_chan_spec *channels;
 	int scan_index = 0, num_channels = 0, ret, i;
 	u32 smp = 0;
+	bool legacy = false;
 
-	ret = stm32_adc_get_legacy_chan_count(indio_dev, adc);
-	if (ret < 0)
-		return ret;
-	num_channels = ret;
-
+	num_channels = of_get_available_child_count(node);
+	/*
+	 * If no channels have been found, fallback to channels legacy properties.
+	 * Legacy channel properties will be ignored, if some channels are
+	 * already defined using the standard binding.
+	 */
 	if (!num_channels) {
-		dev_err(&indio_dev->dev, "No channels configured\n");
-		return -ENODATA;
+		ret = stm32_adc_get_legacy_chan_count(indio_dev, adc);
+		if (ret < 0)
+			return ret;
+
+		legacy = true;
+		num_channels = ret;
 	}
 
 	if (num_channels > adc_info->max_channels) {
@@ -1832,7 +1910,10 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 	if (!channels)
 		return -ENOMEM;
 
-	ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels);
+	if (legacy)
+		ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels);
+	else
+		ret = stm32_adc_generic_chan_init(indio_dev, adc, channels);
 	if (ret < 0)
 		return ret;
 	scan_index = ret;
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 5/7] iio: adc: stm32-adc: add support of internal channels
  2021-09-08 15:54 ` Olivier Moysan
  (?)
@ 2021-09-08 15:54   ` Olivier Moysan
  -1 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: alsa-devel, devicetree, linux-arm-kernel, linux-iio,
	linux-kernel, linux-stm32

Add support of ADC2 internal channels VDDCORE, VREFINT and VBAT.
The reserved label name "vddcore", "vrefint" and "vbat" must
be used in Device Tree channel node, to enable the corresponding
internal channel.

Note: This patch does not provide support of internal channels
for F4 and H7.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
 drivers/iio/adc/stm32-adc-core.h |   8 ++
 drivers/iio/adc/stm32-adc.c      | 123 ++++++++++++++++++++++++++++++-
 2 files changed, 128 insertions(+), 3 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
index 2322809bfd2f..7c924f463f67 100644
--- a/drivers/iio/adc/stm32-adc-core.h
+++ b/drivers/iio/adc/stm32-adc-core.h
@@ -102,6 +102,9 @@
 #define STM32H7_ADC_CALFACT		0xC4
 #define STM32H7_ADC_CALFACT2		0xC8
 
+/* STM32MP1 - ADC2 instance option register */
+#define STM32MP1_ADC2_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)
@@ -168,11 +171,16 @@ enum stm32h7_adc_dmngt {
 #define STM32H7_EOC_MST			BIT(2)
 
 /* STM32H7_ADC_CCR - bit fields */
+#define STM32H7_VBATEN			BIT(24)
+#define STM32H7_VREFEN			BIT(22)
 #define STM32H7_PRESC_SHIFT		18
 #define STM32H7_PRESC_MASK		GENMASK(21, 18)
 #define STM32H7_CKMODE_SHIFT		16
 #define STM32H7_CKMODE_MASK		GENMASK(17, 16)
 
+/* STM32MP1_ADC2_OR - bit fields */
+#define STM32MP1_VDDCOREEN		BIT(0)
+
 /**
  * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
  * @base:		control registers base cpu addr
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index ae4a314854f7..ef3d2af98025 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -41,6 +41,7 @@
 #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_CHAN_NONE		-1
 
 #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
 
@@ -77,6 +78,29 @@ enum stm32_adc_extsel {
 	STM32_EXT20,
 };
 
+enum stm32_adc_int_ch {
+	STM32_ADC_INT_CH_VDDCORE,
+	STM32_ADC_INT_CH_VREFINT,
+	STM32_ADC_INT_CH_VBAT,
+	STM32_ADC_INT_CH_NB,
+};
+
+/**
+ * struct stm32_adc_ic - ADC internal channels
+ * @name:	name of the internal channel
+ * @idx:	internal channel enum index
+ */
+struct stm32_adc_ic {
+	const char *name;
+	u32 idx;
+};
+
+static const struct stm32_adc_ic stm32_adc_ic[STM32_ADC_INT_CH_NB] = {
+	{ "vddcore", STM32_ADC_INT_CH_VDDCORE },
+	{ "vrefint", STM32_ADC_INT_CH_VREFINT },
+	{ "vbat", STM32_ADC_INT_CH_VBAT },
+};
+
 /**
  * struct stm32_adc_trig_info - ADC trigger info
  * @name:		name of the trigger, corresponding to its source
@@ -126,6 +150,9 @@ struct stm32_adc_regs {
  * @res:		resolution selection register & bitfield
  * @smpr:		smpr1 & smpr2 registers offset array
  * @smp_bits:		smpr1 & smpr2 index and bitfields
+ * @or_vdd:		option register & vddcore bitfield
+ * @ccr_vbat:		common register & vbat bitfield
+ * @ccr_vref:		common register & vrefint bitfield
  */
 struct stm32_adc_regspec {
 	const u32 dr;
@@ -139,6 +166,9 @@ struct stm32_adc_regspec {
 	const struct stm32_adc_regs res;
 	const u32 smpr[2];
 	const struct stm32_adc_regs *smp_bits;
+	const struct stm32_adc_regs or_vdd;
+	const struct stm32_adc_regs ccr_vbat;
+	const struct stm32_adc_regs ccr_vref;
 };
 
 struct stm32_adc;
@@ -195,6 +225,7 @@ struct stm32_adc_cfg {
  * @cal:		optional calibration data on some devices
  * @chan_name:		channel name array
  * @num_diff:		number of differential channels
+ * @int_ch:		internal channel indexes array
  */
 struct stm32_adc {
 	struct stm32_adc_common	*common;
@@ -219,6 +250,7 @@ struct stm32_adc {
 	struct stm32_adc_calib	cal;
 	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
 	u32			num_diff;
+	int			int_ch[STM32_ADC_INT_CH_NB];
 };
 
 struct stm32_adc_diff_channel {
@@ -451,6 +483,24 @@ static const struct stm32_adc_regspec stm32h7_adc_regspec = {
 	.smp_bits = stm32h7_smp_bits,
 };
 
+static const struct stm32_adc_regspec stm32mp1_adc_regspec = {
+	.dr = STM32H7_ADC_DR,
+	.ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE },
+	.ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE },
+	.isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC },
+	.isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR },
+	.sqr = stm32h7_sq,
+	.exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT },
+	.extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
+		    STM32H7_EXTSEL_SHIFT },
+	.res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT },
+	.smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 },
+	.smp_bits = stm32h7_smp_bits,
+	.or_vdd = { STM32MP1_ADC2_OR, STM32MP1_VDDCOREEN },
+	.ccr_vbat = { STM32H7_ADC_CCR, STM32H7_VBATEN },
+	.ccr_vref = { STM32H7_ADC_CCR, STM32H7_VREFEN },
+};
+
 /*
  * STM32 ADC registers access routines
  * @adc: stm32 adc instance
@@ -579,6 +629,61 @@ static int stm32_adc_hw_start(struct device *dev)
 	return ret;
 }
 
+static void stm32_adc_int_ch_enable(struct iio_dev *indio_dev)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	u32 i, val, bits = 0, offset = 0;
+
+	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
+		if (adc->int_ch[i] == STM32_ADC_CHAN_NONE)
+			continue;
+
+		switch (i) {
+		case STM32_ADC_INT_CH_VDDCORE:
+			dev_dbg(&indio_dev->dev, "Enable VDDCore\n");
+			stm32_adc_set_bits(adc, adc->cfg->regs->or_vdd.reg,
+					   adc->cfg->regs->or_vdd.mask);
+			break;
+		case STM32_ADC_INT_CH_VREFINT:
+			dev_dbg(&indio_dev->dev, "Enable VREFInt\n");
+			offset = adc->cfg->regs->ccr_vref.reg;
+			bits |= adc->cfg->regs->ccr_vref.mask;
+			break;
+		case STM32_ADC_INT_CH_VBAT:
+			dev_dbg(&indio_dev->dev, "Enable VBAT\n");
+			offset = adc->cfg->regs->ccr_vbat.reg;
+			bits |= adc->cfg->regs->ccr_vbat.mask;
+			break;
+		}
+	}
+
+	if (bits) {
+		val = readl_relaxed(adc->common->base + offset);
+		val |= bits;
+		writel_relaxed(val, adc->common->base + offset);
+	}
+}
+
+static void stm32_adc_int_ch_disable(struct stm32_adc *adc)
+{
+	u32 val, bits, offset, i;
+
+	stm32_adc_clr_bits(adc, adc->cfg->regs->or_vdd.reg,
+			   adc->cfg->regs->or_vdd.mask);
+
+	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
+		if (adc->int_ch[i] != STM32_ADC_CHAN_NONE) {
+			offset = adc->cfg->regs->ccr_vref.reg;
+			bits = adc->cfg->regs->ccr_vref.mask |
+				adc->cfg->regs->ccr_vbat.mask;
+			val = readl_relaxed(adc->common->base + offset);
+			val &= ~bits;
+			writel_relaxed(bits, adc->common->base + offset);
+			break;
+		}
+	}
+}
+
 /**
  * stm32f4_adc_start_conv() - Start conversions for regular channels.
  * @indio_dev: IIO device instance
@@ -947,11 +1052,13 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev)
 		goto pwr_dwn;
 	calib = ret;
 
+	stm32_adc_int_ch_enable(indio_dev);
+
 	stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel);
 
 	ret = stm32h7_adc_enable(indio_dev);
 	if (ret)
-		goto pwr_dwn;
+		goto ch_disable;
 
 	/* Either restore or read calibration result for future reference */
 	if (calib)
@@ -967,6 +1074,8 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev)
 
 disable:
 	stm32h7_adc_disable(indio_dev);
+ch_disable:
+	stm32_adc_int_ch_disable(adc);
 pwr_dwn:
 	stm32h7_adc_enter_pwr_down(adc);
 
@@ -978,6 +1087,7 @@ static void stm32h7_adc_unprepare(struct iio_dev *indio_dev)
 	struct stm32_adc *adc = iio_priv(indio_dev);
 
 	stm32h7_adc_disable(indio_dev);
+	stm32_adc_int_ch_disable(adc);
 	stm32h7_adc_enter_pwr_down(adc);
 }
 
@@ -1805,10 +1915,13 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
 	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
 	struct device_node *child;
 	const char *name;
-	int val, scan_index = 0, ret;
+	int val, scan_index = 0, ret, i;
 	bool differential;
 	u32 vin[2];
 
+	for (i = 0; i < STM32_ADC_INT_CH_NB; i++)
+		adc->int_ch[i] = STM32_ADC_CHAN_NONE;
+
 	for_each_available_child_of_node(node, child) {
 		ret = of_property_read_u32(child, "reg", &val);
 		if (ret) {
@@ -1825,6 +1938,10 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
 				return -EINVAL;
 			}
 			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
+			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
+				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
+					adc->int_ch[i] = val;
+			}
 		} else if (ret != -EINVAL) {
 			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
 			goto err;
@@ -2223,7 +2340,7 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = {
 };
 
 static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
-	.regs = &stm32h7_adc_regspec,
+	.regs = &stm32mp1_adc_regspec,
 	.adc_info = &stm32h7_adc_info,
 	.trigs = stm32h7_adc_trigs,
 	.has_vregready = true,
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 5/7] iio: adc: stm32-adc: add support of internal channels
@ 2021-09-08 15:54   ` Olivier Moysan
  0 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: devicetree, alsa-devel, linux-iio, linux-kernel, linux-stm32,
	linux-arm-kernel

Add support of ADC2 internal channels VDDCORE, VREFINT and VBAT.
The reserved label name "vddcore", "vrefint" and "vbat" must
be used in Device Tree channel node, to enable the corresponding
internal channel.

Note: This patch does not provide support of internal channels
for F4 and H7.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
 drivers/iio/adc/stm32-adc-core.h |   8 ++
 drivers/iio/adc/stm32-adc.c      | 123 ++++++++++++++++++++++++++++++-
 2 files changed, 128 insertions(+), 3 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
index 2322809bfd2f..7c924f463f67 100644
--- a/drivers/iio/adc/stm32-adc-core.h
+++ b/drivers/iio/adc/stm32-adc-core.h
@@ -102,6 +102,9 @@
 #define STM32H7_ADC_CALFACT		0xC4
 #define STM32H7_ADC_CALFACT2		0xC8
 
+/* STM32MP1 - ADC2 instance option register */
+#define STM32MP1_ADC2_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)
@@ -168,11 +171,16 @@ enum stm32h7_adc_dmngt {
 #define STM32H7_EOC_MST			BIT(2)
 
 /* STM32H7_ADC_CCR - bit fields */
+#define STM32H7_VBATEN			BIT(24)
+#define STM32H7_VREFEN			BIT(22)
 #define STM32H7_PRESC_SHIFT		18
 #define STM32H7_PRESC_MASK		GENMASK(21, 18)
 #define STM32H7_CKMODE_SHIFT		16
 #define STM32H7_CKMODE_MASK		GENMASK(17, 16)
 
+/* STM32MP1_ADC2_OR - bit fields */
+#define STM32MP1_VDDCOREEN		BIT(0)
+
 /**
  * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
  * @base:		control registers base cpu addr
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index ae4a314854f7..ef3d2af98025 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -41,6 +41,7 @@
 #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_CHAN_NONE		-1
 
 #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
 
@@ -77,6 +78,29 @@ enum stm32_adc_extsel {
 	STM32_EXT20,
 };
 
+enum stm32_adc_int_ch {
+	STM32_ADC_INT_CH_VDDCORE,
+	STM32_ADC_INT_CH_VREFINT,
+	STM32_ADC_INT_CH_VBAT,
+	STM32_ADC_INT_CH_NB,
+};
+
+/**
+ * struct stm32_adc_ic - ADC internal channels
+ * @name:	name of the internal channel
+ * @idx:	internal channel enum index
+ */
+struct stm32_adc_ic {
+	const char *name;
+	u32 idx;
+};
+
+static const struct stm32_adc_ic stm32_adc_ic[STM32_ADC_INT_CH_NB] = {
+	{ "vddcore", STM32_ADC_INT_CH_VDDCORE },
+	{ "vrefint", STM32_ADC_INT_CH_VREFINT },
+	{ "vbat", STM32_ADC_INT_CH_VBAT },
+};
+
 /**
  * struct stm32_adc_trig_info - ADC trigger info
  * @name:		name of the trigger, corresponding to its source
@@ -126,6 +150,9 @@ struct stm32_adc_regs {
  * @res:		resolution selection register & bitfield
  * @smpr:		smpr1 & smpr2 registers offset array
  * @smp_bits:		smpr1 & smpr2 index and bitfields
+ * @or_vdd:		option register & vddcore bitfield
+ * @ccr_vbat:		common register & vbat bitfield
+ * @ccr_vref:		common register & vrefint bitfield
  */
 struct stm32_adc_regspec {
 	const u32 dr;
@@ -139,6 +166,9 @@ struct stm32_adc_regspec {
 	const struct stm32_adc_regs res;
 	const u32 smpr[2];
 	const struct stm32_adc_regs *smp_bits;
+	const struct stm32_adc_regs or_vdd;
+	const struct stm32_adc_regs ccr_vbat;
+	const struct stm32_adc_regs ccr_vref;
 };
 
 struct stm32_adc;
@@ -195,6 +225,7 @@ struct stm32_adc_cfg {
  * @cal:		optional calibration data on some devices
  * @chan_name:		channel name array
  * @num_diff:		number of differential channels
+ * @int_ch:		internal channel indexes array
  */
 struct stm32_adc {
 	struct stm32_adc_common	*common;
@@ -219,6 +250,7 @@ struct stm32_adc {
 	struct stm32_adc_calib	cal;
 	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
 	u32			num_diff;
+	int			int_ch[STM32_ADC_INT_CH_NB];
 };
 
 struct stm32_adc_diff_channel {
@@ -451,6 +483,24 @@ static const struct stm32_adc_regspec stm32h7_adc_regspec = {
 	.smp_bits = stm32h7_smp_bits,
 };
 
+static const struct stm32_adc_regspec stm32mp1_adc_regspec = {
+	.dr = STM32H7_ADC_DR,
+	.ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE },
+	.ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE },
+	.isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC },
+	.isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR },
+	.sqr = stm32h7_sq,
+	.exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT },
+	.extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
+		    STM32H7_EXTSEL_SHIFT },
+	.res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT },
+	.smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 },
+	.smp_bits = stm32h7_smp_bits,
+	.or_vdd = { STM32MP1_ADC2_OR, STM32MP1_VDDCOREEN },
+	.ccr_vbat = { STM32H7_ADC_CCR, STM32H7_VBATEN },
+	.ccr_vref = { STM32H7_ADC_CCR, STM32H7_VREFEN },
+};
+
 /*
  * STM32 ADC registers access routines
  * @adc: stm32 adc instance
@@ -579,6 +629,61 @@ static int stm32_adc_hw_start(struct device *dev)
 	return ret;
 }
 
+static void stm32_adc_int_ch_enable(struct iio_dev *indio_dev)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	u32 i, val, bits = 0, offset = 0;
+
+	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
+		if (adc->int_ch[i] == STM32_ADC_CHAN_NONE)
+			continue;
+
+		switch (i) {
+		case STM32_ADC_INT_CH_VDDCORE:
+			dev_dbg(&indio_dev->dev, "Enable VDDCore\n");
+			stm32_adc_set_bits(adc, adc->cfg->regs->or_vdd.reg,
+					   adc->cfg->regs->or_vdd.mask);
+			break;
+		case STM32_ADC_INT_CH_VREFINT:
+			dev_dbg(&indio_dev->dev, "Enable VREFInt\n");
+			offset = adc->cfg->regs->ccr_vref.reg;
+			bits |= adc->cfg->regs->ccr_vref.mask;
+			break;
+		case STM32_ADC_INT_CH_VBAT:
+			dev_dbg(&indio_dev->dev, "Enable VBAT\n");
+			offset = adc->cfg->regs->ccr_vbat.reg;
+			bits |= adc->cfg->regs->ccr_vbat.mask;
+			break;
+		}
+	}
+
+	if (bits) {
+		val = readl_relaxed(adc->common->base + offset);
+		val |= bits;
+		writel_relaxed(val, adc->common->base + offset);
+	}
+}
+
+static void stm32_adc_int_ch_disable(struct stm32_adc *adc)
+{
+	u32 val, bits, offset, i;
+
+	stm32_adc_clr_bits(adc, adc->cfg->regs->or_vdd.reg,
+			   adc->cfg->regs->or_vdd.mask);
+
+	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
+		if (adc->int_ch[i] != STM32_ADC_CHAN_NONE) {
+			offset = adc->cfg->regs->ccr_vref.reg;
+			bits = adc->cfg->regs->ccr_vref.mask |
+				adc->cfg->regs->ccr_vbat.mask;
+			val = readl_relaxed(adc->common->base + offset);
+			val &= ~bits;
+			writel_relaxed(bits, adc->common->base + offset);
+			break;
+		}
+	}
+}
+
 /**
  * stm32f4_adc_start_conv() - Start conversions for regular channels.
  * @indio_dev: IIO device instance
@@ -947,11 +1052,13 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev)
 		goto pwr_dwn;
 	calib = ret;
 
+	stm32_adc_int_ch_enable(indio_dev);
+
 	stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel);
 
 	ret = stm32h7_adc_enable(indio_dev);
 	if (ret)
-		goto pwr_dwn;
+		goto ch_disable;
 
 	/* Either restore or read calibration result for future reference */
 	if (calib)
@@ -967,6 +1074,8 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev)
 
 disable:
 	stm32h7_adc_disable(indio_dev);
+ch_disable:
+	stm32_adc_int_ch_disable(adc);
 pwr_dwn:
 	stm32h7_adc_enter_pwr_down(adc);
 
@@ -978,6 +1087,7 @@ static void stm32h7_adc_unprepare(struct iio_dev *indio_dev)
 	struct stm32_adc *adc = iio_priv(indio_dev);
 
 	stm32h7_adc_disable(indio_dev);
+	stm32_adc_int_ch_disable(adc);
 	stm32h7_adc_enter_pwr_down(adc);
 }
 
@@ -1805,10 +1915,13 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
 	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
 	struct device_node *child;
 	const char *name;
-	int val, scan_index = 0, ret;
+	int val, scan_index = 0, ret, i;
 	bool differential;
 	u32 vin[2];
 
+	for (i = 0; i < STM32_ADC_INT_CH_NB; i++)
+		adc->int_ch[i] = STM32_ADC_CHAN_NONE;
+
 	for_each_available_child_of_node(node, child) {
 		ret = of_property_read_u32(child, "reg", &val);
 		if (ret) {
@@ -1825,6 +1938,10 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
 				return -EINVAL;
 			}
 			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
+			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
+				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
+					adc->int_ch[i] = val;
+			}
 		} else if (ret != -EINVAL) {
 			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
 			goto err;
@@ -2223,7 +2340,7 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = {
 };
 
 static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
-	.regs = &stm32h7_adc_regspec,
+	.regs = &stm32mp1_adc_regspec,
 	.adc_info = &stm32h7_adc_info,
 	.trigs = stm32h7_adc_trigs,
 	.has_vregready = true,
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 5/7] iio: adc: stm32-adc: add support of internal channels
@ 2021-09-08 15:54   ` Olivier Moysan
  0 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: alsa-devel, devicetree, linux-arm-kernel, linux-iio,
	linux-kernel, linux-stm32

Add support of ADC2 internal channels VDDCORE, VREFINT and VBAT.
The reserved label name "vddcore", "vrefint" and "vbat" must
be used in Device Tree channel node, to enable the corresponding
internal channel.

Note: This patch does not provide support of internal channels
for F4 and H7.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
 drivers/iio/adc/stm32-adc-core.h |   8 ++
 drivers/iio/adc/stm32-adc.c      | 123 ++++++++++++++++++++++++++++++-
 2 files changed, 128 insertions(+), 3 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
index 2322809bfd2f..7c924f463f67 100644
--- a/drivers/iio/adc/stm32-adc-core.h
+++ b/drivers/iio/adc/stm32-adc-core.h
@@ -102,6 +102,9 @@
 #define STM32H7_ADC_CALFACT		0xC4
 #define STM32H7_ADC_CALFACT2		0xC8
 
+/* STM32MP1 - ADC2 instance option register */
+#define STM32MP1_ADC2_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)
@@ -168,11 +171,16 @@ enum stm32h7_adc_dmngt {
 #define STM32H7_EOC_MST			BIT(2)
 
 /* STM32H7_ADC_CCR - bit fields */
+#define STM32H7_VBATEN			BIT(24)
+#define STM32H7_VREFEN			BIT(22)
 #define STM32H7_PRESC_SHIFT		18
 #define STM32H7_PRESC_MASK		GENMASK(21, 18)
 #define STM32H7_CKMODE_SHIFT		16
 #define STM32H7_CKMODE_MASK		GENMASK(17, 16)
 
+/* STM32MP1_ADC2_OR - bit fields */
+#define STM32MP1_VDDCOREEN		BIT(0)
+
 /**
  * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
  * @base:		control registers base cpu addr
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index ae4a314854f7..ef3d2af98025 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -41,6 +41,7 @@
 #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_CHAN_NONE		-1
 
 #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
 
@@ -77,6 +78,29 @@ enum stm32_adc_extsel {
 	STM32_EXT20,
 };
 
+enum stm32_adc_int_ch {
+	STM32_ADC_INT_CH_VDDCORE,
+	STM32_ADC_INT_CH_VREFINT,
+	STM32_ADC_INT_CH_VBAT,
+	STM32_ADC_INT_CH_NB,
+};
+
+/**
+ * struct stm32_adc_ic - ADC internal channels
+ * @name:	name of the internal channel
+ * @idx:	internal channel enum index
+ */
+struct stm32_adc_ic {
+	const char *name;
+	u32 idx;
+};
+
+static const struct stm32_adc_ic stm32_adc_ic[STM32_ADC_INT_CH_NB] = {
+	{ "vddcore", STM32_ADC_INT_CH_VDDCORE },
+	{ "vrefint", STM32_ADC_INT_CH_VREFINT },
+	{ "vbat", STM32_ADC_INT_CH_VBAT },
+};
+
 /**
  * struct stm32_adc_trig_info - ADC trigger info
  * @name:		name of the trigger, corresponding to its source
@@ -126,6 +150,9 @@ struct stm32_adc_regs {
  * @res:		resolution selection register & bitfield
  * @smpr:		smpr1 & smpr2 registers offset array
  * @smp_bits:		smpr1 & smpr2 index and bitfields
+ * @or_vdd:		option register & vddcore bitfield
+ * @ccr_vbat:		common register & vbat bitfield
+ * @ccr_vref:		common register & vrefint bitfield
  */
 struct stm32_adc_regspec {
 	const u32 dr;
@@ -139,6 +166,9 @@ struct stm32_adc_regspec {
 	const struct stm32_adc_regs res;
 	const u32 smpr[2];
 	const struct stm32_adc_regs *smp_bits;
+	const struct stm32_adc_regs or_vdd;
+	const struct stm32_adc_regs ccr_vbat;
+	const struct stm32_adc_regs ccr_vref;
 };
 
 struct stm32_adc;
@@ -195,6 +225,7 @@ struct stm32_adc_cfg {
  * @cal:		optional calibration data on some devices
  * @chan_name:		channel name array
  * @num_diff:		number of differential channels
+ * @int_ch:		internal channel indexes array
  */
 struct stm32_adc {
 	struct stm32_adc_common	*common;
@@ -219,6 +250,7 @@ struct stm32_adc {
 	struct stm32_adc_calib	cal;
 	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
 	u32			num_diff;
+	int			int_ch[STM32_ADC_INT_CH_NB];
 };
 
 struct stm32_adc_diff_channel {
@@ -451,6 +483,24 @@ static const struct stm32_adc_regspec stm32h7_adc_regspec = {
 	.smp_bits = stm32h7_smp_bits,
 };
 
+static const struct stm32_adc_regspec stm32mp1_adc_regspec = {
+	.dr = STM32H7_ADC_DR,
+	.ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE },
+	.ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE },
+	.isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC },
+	.isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR },
+	.sqr = stm32h7_sq,
+	.exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT },
+	.extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
+		    STM32H7_EXTSEL_SHIFT },
+	.res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT },
+	.smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 },
+	.smp_bits = stm32h7_smp_bits,
+	.or_vdd = { STM32MP1_ADC2_OR, STM32MP1_VDDCOREEN },
+	.ccr_vbat = { STM32H7_ADC_CCR, STM32H7_VBATEN },
+	.ccr_vref = { STM32H7_ADC_CCR, STM32H7_VREFEN },
+};
+
 /*
  * STM32 ADC registers access routines
  * @adc: stm32 adc instance
@@ -579,6 +629,61 @@ static int stm32_adc_hw_start(struct device *dev)
 	return ret;
 }
 
+static void stm32_adc_int_ch_enable(struct iio_dev *indio_dev)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	u32 i, val, bits = 0, offset = 0;
+
+	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
+		if (adc->int_ch[i] == STM32_ADC_CHAN_NONE)
+			continue;
+
+		switch (i) {
+		case STM32_ADC_INT_CH_VDDCORE:
+			dev_dbg(&indio_dev->dev, "Enable VDDCore\n");
+			stm32_adc_set_bits(adc, adc->cfg->regs->or_vdd.reg,
+					   adc->cfg->regs->or_vdd.mask);
+			break;
+		case STM32_ADC_INT_CH_VREFINT:
+			dev_dbg(&indio_dev->dev, "Enable VREFInt\n");
+			offset = adc->cfg->regs->ccr_vref.reg;
+			bits |= adc->cfg->regs->ccr_vref.mask;
+			break;
+		case STM32_ADC_INT_CH_VBAT:
+			dev_dbg(&indio_dev->dev, "Enable VBAT\n");
+			offset = adc->cfg->regs->ccr_vbat.reg;
+			bits |= adc->cfg->regs->ccr_vbat.mask;
+			break;
+		}
+	}
+
+	if (bits) {
+		val = readl_relaxed(adc->common->base + offset);
+		val |= bits;
+		writel_relaxed(val, adc->common->base + offset);
+	}
+}
+
+static void stm32_adc_int_ch_disable(struct stm32_adc *adc)
+{
+	u32 val, bits, offset, i;
+
+	stm32_adc_clr_bits(adc, adc->cfg->regs->or_vdd.reg,
+			   adc->cfg->regs->or_vdd.mask);
+
+	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
+		if (adc->int_ch[i] != STM32_ADC_CHAN_NONE) {
+			offset = adc->cfg->regs->ccr_vref.reg;
+			bits = adc->cfg->regs->ccr_vref.mask |
+				adc->cfg->regs->ccr_vbat.mask;
+			val = readl_relaxed(adc->common->base + offset);
+			val &= ~bits;
+			writel_relaxed(bits, adc->common->base + offset);
+			break;
+		}
+	}
+}
+
 /**
  * stm32f4_adc_start_conv() - Start conversions for regular channels.
  * @indio_dev: IIO device instance
@@ -947,11 +1052,13 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev)
 		goto pwr_dwn;
 	calib = ret;
 
+	stm32_adc_int_ch_enable(indio_dev);
+
 	stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel);
 
 	ret = stm32h7_adc_enable(indio_dev);
 	if (ret)
-		goto pwr_dwn;
+		goto ch_disable;
 
 	/* Either restore or read calibration result for future reference */
 	if (calib)
@@ -967,6 +1074,8 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev)
 
 disable:
 	stm32h7_adc_disable(indio_dev);
+ch_disable:
+	stm32_adc_int_ch_disable(adc);
 pwr_dwn:
 	stm32h7_adc_enter_pwr_down(adc);
 
@@ -978,6 +1087,7 @@ static void stm32h7_adc_unprepare(struct iio_dev *indio_dev)
 	struct stm32_adc *adc = iio_priv(indio_dev);
 
 	stm32h7_adc_disable(indio_dev);
+	stm32_adc_int_ch_disable(adc);
 	stm32h7_adc_enter_pwr_down(adc);
 }
 
@@ -1805,10 +1915,13 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
 	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
 	struct device_node *child;
 	const char *name;
-	int val, scan_index = 0, ret;
+	int val, scan_index = 0, ret, i;
 	bool differential;
 	u32 vin[2];
 
+	for (i = 0; i < STM32_ADC_INT_CH_NB; i++)
+		adc->int_ch[i] = STM32_ADC_CHAN_NONE;
+
 	for_each_available_child_of_node(node, child) {
 		ret = of_property_read_u32(child, "reg", &val);
 		if (ret) {
@@ -1825,6 +1938,10 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
 				return -EINVAL;
 			}
 			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
+			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
+				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
+					adc->int_ch[i] = val;
+			}
 		} else if (ret != -EINVAL) {
 			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
 			goto err;
@@ -2223,7 +2340,7 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = {
 };
 
 static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
-	.regs = &stm32h7_adc_regspec,
+	.regs = &stm32mp1_adc_regspec,
 	.adc_info = &stm32h7_adc_info,
 	.trigs = stm32h7_adc_trigs,
 	.has_vregready = true,
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 6/7] iio: adc: stm32-adc: add vrefint calibration support
  2021-09-08 15:54 ` Olivier Moysan
  (?)
@ 2021-09-08 15:54   ` Olivier Moysan
  -1 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: alsa-devel, devicetree, linux-arm-kernel, linux-iio,
	linux-kernel, linux-stm32

Add support of vrefint calibration.
If a channel is labeled as vrefint, get vrefint calibration
from non volatile memory for this channel.
A conversion on vrefint channel allows to update scale
factor according to vrefint deviation, compared to vrefint
calibration value.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
 drivers/iio/adc/stm32-adc.c | 88 ++++++++++++++++++++++++++++++++++---
 1 file changed, 82 insertions(+), 6 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index ef3d2af98025..9e52a7de9b16 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -21,6 +21,7 @@
 #include <linux/io.h>
 #include <linux/iopoll.h>
 #include <linux/module.h>
+#include <linux/nvmem-consumer.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/of.h>
@@ -42,6 +43,7 @@
 #define STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
 #define STM32_ADC_HW_STOP_DELAY_MS	100
 #define STM32_ADC_CHAN_NONE		-1
+#define STM32_ADC_VREFINT_VOLTAGE	3300
 
 #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
 
@@ -79,6 +81,7 @@ enum stm32_adc_extsel {
 };
 
 enum stm32_adc_int_ch {
+	STM32_ADC_INT_CH_NONE = -1,
 	STM32_ADC_INT_CH_VDDCORE,
 	STM32_ADC_INT_CH_VREFINT,
 	STM32_ADC_INT_CH_VBAT,
@@ -137,6 +140,16 @@ struct stm32_adc_regs {
 	int shift;
 };
 
+/**
+ * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data
+ * @vrefint_cal:	vrefint calibration value from nvmem
+ * @vrefint_data:	vrefint actual value
+ */
+struct stm32_adc_vrefint {
+	u32 vrefint_cal;
+	u32 vrefint_data;
+};
+
 /**
  * struct stm32_adc_regspec - stm32 registers definition
  * @dr:			data register offset
@@ -186,6 +199,7 @@ struct stm32_adc;
  * @unprepare:		optional unprepare routine (disable, power-down)
  * @irq_clear:		routine to clear irqs
  * @smp_cycles:		programmable sampling time (ADC clock cycles)
+ * @ts_vrefint_ns:	vrefint minimum sampling time in ns
  */
 struct stm32_adc_cfg {
 	const struct stm32_adc_regspec	*regs;
@@ -199,6 +213,7 @@ struct stm32_adc_cfg {
 	void (*unprepare)(struct iio_dev *);
 	void (*irq_clear)(struct iio_dev *indio_dev, u32 msk);
 	const unsigned int *smp_cycles;
+	const unsigned int ts_vrefint_ns;
 };
 
 /**
@@ -223,6 +238,7 @@ struct stm32_adc_cfg {
  * @pcsel:		bitmask to preselect channels on some devices
  * @smpr_val:		sampling time settings (e.g. smpr1 / smpr2)
  * @cal:		optional calibration data on some devices
+ * @vrefint:		internal reference voltage data
  * @chan_name:		channel name array
  * @num_diff:		number of differential channels
  * @int_ch:		internal channel indexes array
@@ -248,6 +264,7 @@ struct stm32_adc {
 	u32			pcsel;
 	u32			smpr_val[2];
 	struct stm32_adc_calib	cal;
+	struct stm32_adc_vrefint vrefint;
 	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
 	u32			num_diff;
 	int			int_ch[STM32_ADC_INT_CH_NB];
@@ -1331,15 +1348,35 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
 			ret = stm32_adc_single_conv(indio_dev, chan, val);
 		else
 			ret = -EINVAL;
+
+		/* If channel mask corresponds to vrefint, store data */
+		if (adc->int_ch[STM32_ADC_INT_CH_VREFINT] == chan->channel)
+			adc->vrefint.vrefint_data = *val;
+
 		iio_device_release_direct_mode(indio_dev);
 		return ret;
 
 	case IIO_CHAN_INFO_SCALE:
 		if (chan->differential) {
-			*val = adc->common->vref_mv * 2;
+			if (adc->vrefint.vrefint_data &&
+			    adc->vrefint.vrefint_cal) {
+				*val = STM32_ADC_VREFINT_VOLTAGE * 2 *
+				       adc->vrefint.vrefint_cal /
+				       adc->vrefint.vrefint_data;
+			} else {
+				*val = adc->common->vref_mv * 2;
+			}
 			*val2 = chan->scan_type.realbits;
 		} else {
-			*val = adc->common->vref_mv;
+			/* Use vrefint data if available */
+			if (adc->vrefint.vrefint_data &&
+			    adc->vrefint.vrefint_cal) {
+				*val = STM32_ADC_VREFINT_VOLTAGE *
+				       adc->vrefint.vrefint_cal /
+				       adc->vrefint.vrefint_data;
+			} else {
+				*val = adc->common->vref_mv;
+			}
 			*val2 = chan->scan_type.realbits;
 		}
 		return IIO_VAL_FRACTIONAL_LOG2;
@@ -1907,6 +1944,35 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
 	return scan_index;
 }
 
+static int stm32_adc_get_int_ch(struct iio_dev *indio_dev, const char *ch_name,
+				int chan)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	u16 vrefint;
+	int i, ret;
+
+	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
+		if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
+			adc->int_ch[i] = chan;
+			/* If channel is vrefint get calibration data. */
+			if (stm32_adc_ic[i].idx == STM32_ADC_INT_CH_VREFINT) {
+				ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint);
+				if (ret && ret != -ENOENT && ret != -EOPNOTSUPP) {
+					dev_err(&indio_dev->dev, "nvmem access error %d\n", ret);
+					return ret;
+				}
+				if (ret == -ENOENT)
+					dev_dbg(&indio_dev->dev,
+						"vrefint calibration not found\n");
+				else
+					adc->vrefint.vrefint_cal = vrefint;
+			}
+		}
+	}
+
+	return 0;
+}
+
 static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
 				       struct stm32_adc *adc,
 				       struct iio_chan_spec *channels)
@@ -1938,10 +2004,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
 				return -EINVAL;
 			}
 			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
-			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
-				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
-					adc->int_ch[i] = val;
-			}
+			ret = stm32_adc_get_int_ch(indio_dev, name, val);
+			if (ret)
+				goto err;
 		} else if (ret != -EINVAL) {
 			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
 			goto err;
@@ -2044,6 +2109,16 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 		 */
 		of_property_read_u32_index(node, "st,min-sample-time-nsecs",
 					   i, &smp);
+
+		/*
+		 * For vrefint channel, ensure that the sampling time cannot
+		 * be lower than the one specified in the datasheet
+		 */
+		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
+		    smp < adc->cfg->ts_vrefint_ns) {
+			smp = adc->cfg->ts_vrefint_ns;
+		}
+
 		/* Prepare sampling time settings */
 		stm32_adc_smpr_init(adc, channels[i].channel, smp);
 	}
@@ -2350,6 +2425,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
 	.unprepare = stm32h7_adc_unprepare,
 	.smp_cycles = stm32h7_adc_smp_cycles,
 	.irq_clear = stm32h7_adc_irq_clear,
+	.ts_vrefint_ns = 4300,
 };
 
 static const struct of_device_id stm32_adc_of_match[] = {
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 6/7] iio: adc: stm32-adc: add vrefint calibration support
@ 2021-09-08 15:54   ` Olivier Moysan
  0 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: devicetree, alsa-devel, linux-iio, linux-kernel, linux-stm32,
	linux-arm-kernel

Add support of vrefint calibration.
If a channel is labeled as vrefint, get vrefint calibration
from non volatile memory for this channel.
A conversion on vrefint channel allows to update scale
factor according to vrefint deviation, compared to vrefint
calibration value.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
 drivers/iio/adc/stm32-adc.c | 88 ++++++++++++++++++++++++++++++++++---
 1 file changed, 82 insertions(+), 6 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index ef3d2af98025..9e52a7de9b16 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -21,6 +21,7 @@
 #include <linux/io.h>
 #include <linux/iopoll.h>
 #include <linux/module.h>
+#include <linux/nvmem-consumer.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/of.h>
@@ -42,6 +43,7 @@
 #define STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
 #define STM32_ADC_HW_STOP_DELAY_MS	100
 #define STM32_ADC_CHAN_NONE		-1
+#define STM32_ADC_VREFINT_VOLTAGE	3300
 
 #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
 
@@ -79,6 +81,7 @@ enum stm32_adc_extsel {
 };
 
 enum stm32_adc_int_ch {
+	STM32_ADC_INT_CH_NONE = -1,
 	STM32_ADC_INT_CH_VDDCORE,
 	STM32_ADC_INT_CH_VREFINT,
 	STM32_ADC_INT_CH_VBAT,
@@ -137,6 +140,16 @@ struct stm32_adc_regs {
 	int shift;
 };
 
+/**
+ * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data
+ * @vrefint_cal:	vrefint calibration value from nvmem
+ * @vrefint_data:	vrefint actual value
+ */
+struct stm32_adc_vrefint {
+	u32 vrefint_cal;
+	u32 vrefint_data;
+};
+
 /**
  * struct stm32_adc_regspec - stm32 registers definition
  * @dr:			data register offset
@@ -186,6 +199,7 @@ struct stm32_adc;
  * @unprepare:		optional unprepare routine (disable, power-down)
  * @irq_clear:		routine to clear irqs
  * @smp_cycles:		programmable sampling time (ADC clock cycles)
+ * @ts_vrefint_ns:	vrefint minimum sampling time in ns
  */
 struct stm32_adc_cfg {
 	const struct stm32_adc_regspec	*regs;
@@ -199,6 +213,7 @@ struct stm32_adc_cfg {
 	void (*unprepare)(struct iio_dev *);
 	void (*irq_clear)(struct iio_dev *indio_dev, u32 msk);
 	const unsigned int *smp_cycles;
+	const unsigned int ts_vrefint_ns;
 };
 
 /**
@@ -223,6 +238,7 @@ struct stm32_adc_cfg {
  * @pcsel:		bitmask to preselect channels on some devices
  * @smpr_val:		sampling time settings (e.g. smpr1 / smpr2)
  * @cal:		optional calibration data on some devices
+ * @vrefint:		internal reference voltage data
  * @chan_name:		channel name array
  * @num_diff:		number of differential channels
  * @int_ch:		internal channel indexes array
@@ -248,6 +264,7 @@ struct stm32_adc {
 	u32			pcsel;
 	u32			smpr_val[2];
 	struct stm32_adc_calib	cal;
+	struct stm32_adc_vrefint vrefint;
 	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
 	u32			num_diff;
 	int			int_ch[STM32_ADC_INT_CH_NB];
@@ -1331,15 +1348,35 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
 			ret = stm32_adc_single_conv(indio_dev, chan, val);
 		else
 			ret = -EINVAL;
+
+		/* If channel mask corresponds to vrefint, store data */
+		if (adc->int_ch[STM32_ADC_INT_CH_VREFINT] == chan->channel)
+			adc->vrefint.vrefint_data = *val;
+
 		iio_device_release_direct_mode(indio_dev);
 		return ret;
 
 	case IIO_CHAN_INFO_SCALE:
 		if (chan->differential) {
-			*val = adc->common->vref_mv * 2;
+			if (adc->vrefint.vrefint_data &&
+			    adc->vrefint.vrefint_cal) {
+				*val = STM32_ADC_VREFINT_VOLTAGE * 2 *
+				       adc->vrefint.vrefint_cal /
+				       adc->vrefint.vrefint_data;
+			} else {
+				*val = adc->common->vref_mv * 2;
+			}
 			*val2 = chan->scan_type.realbits;
 		} else {
-			*val = adc->common->vref_mv;
+			/* Use vrefint data if available */
+			if (adc->vrefint.vrefint_data &&
+			    adc->vrefint.vrefint_cal) {
+				*val = STM32_ADC_VREFINT_VOLTAGE *
+				       adc->vrefint.vrefint_cal /
+				       adc->vrefint.vrefint_data;
+			} else {
+				*val = adc->common->vref_mv;
+			}
 			*val2 = chan->scan_type.realbits;
 		}
 		return IIO_VAL_FRACTIONAL_LOG2;
@@ -1907,6 +1944,35 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
 	return scan_index;
 }
 
+static int stm32_adc_get_int_ch(struct iio_dev *indio_dev, const char *ch_name,
+				int chan)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	u16 vrefint;
+	int i, ret;
+
+	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
+		if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
+			adc->int_ch[i] = chan;
+			/* If channel is vrefint get calibration data. */
+			if (stm32_adc_ic[i].idx == STM32_ADC_INT_CH_VREFINT) {
+				ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint);
+				if (ret && ret != -ENOENT && ret != -EOPNOTSUPP) {
+					dev_err(&indio_dev->dev, "nvmem access error %d\n", ret);
+					return ret;
+				}
+				if (ret == -ENOENT)
+					dev_dbg(&indio_dev->dev,
+						"vrefint calibration not found\n");
+				else
+					adc->vrefint.vrefint_cal = vrefint;
+			}
+		}
+	}
+
+	return 0;
+}
+
 static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
 				       struct stm32_adc *adc,
 				       struct iio_chan_spec *channels)
@@ -1938,10 +2004,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
 				return -EINVAL;
 			}
 			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
-			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
-				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
-					adc->int_ch[i] = val;
-			}
+			ret = stm32_adc_get_int_ch(indio_dev, name, val);
+			if (ret)
+				goto err;
 		} else if (ret != -EINVAL) {
 			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
 			goto err;
@@ -2044,6 +2109,16 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 		 */
 		of_property_read_u32_index(node, "st,min-sample-time-nsecs",
 					   i, &smp);
+
+		/*
+		 * For vrefint channel, ensure that the sampling time cannot
+		 * be lower than the one specified in the datasheet
+		 */
+		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
+		    smp < adc->cfg->ts_vrefint_ns) {
+			smp = adc->cfg->ts_vrefint_ns;
+		}
+
 		/* Prepare sampling time settings */
 		stm32_adc_smpr_init(adc, channels[i].channel, smp);
 	}
@@ -2350,6 +2425,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
 	.unprepare = stm32h7_adc_unprepare,
 	.smp_cycles = stm32h7_adc_smp_cycles,
 	.irq_clear = stm32h7_adc_irq_clear,
+	.ts_vrefint_ns = 4300,
 };
 
 static const struct of_device_id stm32_adc_of_match[] = {
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 6/7] iio: adc: stm32-adc: add vrefint calibration support
@ 2021-09-08 15:54   ` Olivier Moysan
  0 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: alsa-devel, devicetree, linux-arm-kernel, linux-iio,
	linux-kernel, linux-stm32

Add support of vrefint calibration.
If a channel is labeled as vrefint, get vrefint calibration
from non volatile memory for this channel.
A conversion on vrefint channel allows to update scale
factor according to vrefint deviation, compared to vrefint
calibration value.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
 drivers/iio/adc/stm32-adc.c | 88 ++++++++++++++++++++++++++++++++++---
 1 file changed, 82 insertions(+), 6 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index ef3d2af98025..9e52a7de9b16 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -21,6 +21,7 @@
 #include <linux/io.h>
 #include <linux/iopoll.h>
 #include <linux/module.h>
+#include <linux/nvmem-consumer.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/of.h>
@@ -42,6 +43,7 @@
 #define STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
 #define STM32_ADC_HW_STOP_DELAY_MS	100
 #define STM32_ADC_CHAN_NONE		-1
+#define STM32_ADC_VREFINT_VOLTAGE	3300
 
 #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
 
@@ -79,6 +81,7 @@ enum stm32_adc_extsel {
 };
 
 enum stm32_adc_int_ch {
+	STM32_ADC_INT_CH_NONE = -1,
 	STM32_ADC_INT_CH_VDDCORE,
 	STM32_ADC_INT_CH_VREFINT,
 	STM32_ADC_INT_CH_VBAT,
@@ -137,6 +140,16 @@ struct stm32_adc_regs {
 	int shift;
 };
 
+/**
+ * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data
+ * @vrefint_cal:	vrefint calibration value from nvmem
+ * @vrefint_data:	vrefint actual value
+ */
+struct stm32_adc_vrefint {
+	u32 vrefint_cal;
+	u32 vrefint_data;
+};
+
 /**
  * struct stm32_adc_regspec - stm32 registers definition
  * @dr:			data register offset
@@ -186,6 +199,7 @@ struct stm32_adc;
  * @unprepare:		optional unprepare routine (disable, power-down)
  * @irq_clear:		routine to clear irqs
  * @smp_cycles:		programmable sampling time (ADC clock cycles)
+ * @ts_vrefint_ns:	vrefint minimum sampling time in ns
  */
 struct stm32_adc_cfg {
 	const struct stm32_adc_regspec	*regs;
@@ -199,6 +213,7 @@ struct stm32_adc_cfg {
 	void (*unprepare)(struct iio_dev *);
 	void (*irq_clear)(struct iio_dev *indio_dev, u32 msk);
 	const unsigned int *smp_cycles;
+	const unsigned int ts_vrefint_ns;
 };
 
 /**
@@ -223,6 +238,7 @@ struct stm32_adc_cfg {
  * @pcsel:		bitmask to preselect channels on some devices
  * @smpr_val:		sampling time settings (e.g. smpr1 / smpr2)
  * @cal:		optional calibration data on some devices
+ * @vrefint:		internal reference voltage data
  * @chan_name:		channel name array
  * @num_diff:		number of differential channels
  * @int_ch:		internal channel indexes array
@@ -248,6 +264,7 @@ struct stm32_adc {
 	u32			pcsel;
 	u32			smpr_val[2];
 	struct stm32_adc_calib	cal;
+	struct stm32_adc_vrefint vrefint;
 	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
 	u32			num_diff;
 	int			int_ch[STM32_ADC_INT_CH_NB];
@@ -1331,15 +1348,35 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
 			ret = stm32_adc_single_conv(indio_dev, chan, val);
 		else
 			ret = -EINVAL;
+
+		/* If channel mask corresponds to vrefint, store data */
+		if (adc->int_ch[STM32_ADC_INT_CH_VREFINT] == chan->channel)
+			adc->vrefint.vrefint_data = *val;
+
 		iio_device_release_direct_mode(indio_dev);
 		return ret;
 
 	case IIO_CHAN_INFO_SCALE:
 		if (chan->differential) {
-			*val = adc->common->vref_mv * 2;
+			if (adc->vrefint.vrefint_data &&
+			    adc->vrefint.vrefint_cal) {
+				*val = STM32_ADC_VREFINT_VOLTAGE * 2 *
+				       adc->vrefint.vrefint_cal /
+				       adc->vrefint.vrefint_data;
+			} else {
+				*val = adc->common->vref_mv * 2;
+			}
 			*val2 = chan->scan_type.realbits;
 		} else {
-			*val = adc->common->vref_mv;
+			/* Use vrefint data if available */
+			if (adc->vrefint.vrefint_data &&
+			    adc->vrefint.vrefint_cal) {
+				*val = STM32_ADC_VREFINT_VOLTAGE *
+				       adc->vrefint.vrefint_cal /
+				       adc->vrefint.vrefint_data;
+			} else {
+				*val = adc->common->vref_mv;
+			}
 			*val2 = chan->scan_type.realbits;
 		}
 		return IIO_VAL_FRACTIONAL_LOG2;
@@ -1907,6 +1944,35 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
 	return scan_index;
 }
 
+static int stm32_adc_get_int_ch(struct iio_dev *indio_dev, const char *ch_name,
+				int chan)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	u16 vrefint;
+	int i, ret;
+
+	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
+		if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
+			adc->int_ch[i] = chan;
+			/* If channel is vrefint get calibration data. */
+			if (stm32_adc_ic[i].idx == STM32_ADC_INT_CH_VREFINT) {
+				ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint);
+				if (ret && ret != -ENOENT && ret != -EOPNOTSUPP) {
+					dev_err(&indio_dev->dev, "nvmem access error %d\n", ret);
+					return ret;
+				}
+				if (ret == -ENOENT)
+					dev_dbg(&indio_dev->dev,
+						"vrefint calibration not found\n");
+				else
+					adc->vrefint.vrefint_cal = vrefint;
+			}
+		}
+	}
+
+	return 0;
+}
+
 static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
 				       struct stm32_adc *adc,
 				       struct iio_chan_spec *channels)
@@ -1938,10 +2004,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
 				return -EINVAL;
 			}
 			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
-			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
-				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
-					adc->int_ch[i] = val;
-			}
+			ret = stm32_adc_get_int_ch(indio_dev, name, val);
+			if (ret)
+				goto err;
 		} else if (ret != -EINVAL) {
 			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
 			goto err;
@@ -2044,6 +2109,16 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 		 */
 		of_property_read_u32_index(node, "st,min-sample-time-nsecs",
 					   i, &smp);
+
+		/*
+		 * For vrefint channel, ensure that the sampling time cannot
+		 * be lower than the one specified in the datasheet
+		 */
+		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
+		    smp < adc->cfg->ts_vrefint_ns) {
+			smp = adc->cfg->ts_vrefint_ns;
+		}
+
 		/* Prepare sampling time settings */
 		stm32_adc_smpr_init(adc, channels[i].channel, smp);
 	}
@@ -2350,6 +2425,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
 	.unprepare = stm32h7_adc_unprepare,
 	.smp_cycles = stm32h7_adc_smp_cycles,
 	.irq_clear = stm32h7_adc_irq_clear,
+	.ts_vrefint_ns = 4300,
 };
 
 static const struct of_device_id stm32_adc_of_match[] = {
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 7/7] iio: adc: stm32-adc: use generic binding for sample-time
  2021-09-08 15:54 ` Olivier Moysan
  (?)
@ 2021-09-08 15:54   ` Olivier Moysan
  -1 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: alsa-devel, devicetree, linux-arm-kernel, linux-iio,
	linux-kernel, linux-stm32

Add st,min-sample-time-nsecs to channel generic binding.
Sample time can be defined par channel node. If a channel
is configured as differential, the same sample time applies
for both inputs.
Keep support of legacy st,min-sample-time-nsecs property
for backward compatibility.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
 drivers/iio/adc/stm32-adc.c | 75 +++++++++++++++++++++----------------
 1 file changed, 43 insertions(+), 32 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 9e52a7de9b16..2d52415770b3 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -1806,6 +1806,14 @@ static void stm32_adc_smpr_init(struct stm32_adc *adc, int channel, u32 smp_ns)
 	u32 period_ns, shift = smpr->shift, mask = smpr->mask;
 	unsigned int smp, r = smpr->reg;
 
+	/*
+	 * For vrefint channel, ensure that the sampling time cannot
+	 * be lower than the one specified in the datasheet
+	 */
+	if (channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
+	    smp_ns < adc->cfg->ts_vrefint_ns)
+		smp_ns = adc->cfg->ts_vrefint_ns;
+
 	/* Determine sampling time (ADC clock cycles) */
 	period_ns = NSEC_PER_SEC / adc->common->rate;
 	for (smp = 0; smp <= STM32_ADC_MAX_SMP; smp++)
@@ -1884,6 +1892,13 @@ static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm
 		return -ENODATA;
 	}
 
+	/* Optional sample time is provided either for each, or all channels */
+	ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs");
+	if (ret > 1 && ret != num_channels) {
+		dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n");
+		return -EINVAL;
+	}
+
 	return num_channels;
 }
 
@@ -1899,6 +1914,7 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
 	int scan_index = 0, val, ret, i;
 	struct property *prop;
 	const __be32 *cur;
+	u32 smp = 0;
 
 	if (num_diff) {
 		ret = of_property_read_u32_array(node, "st,adc-diff-channels",
@@ -1941,6 +1957,19 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
 		scan_index++;
 	}
 
+	for (i = 0; i < scan_index; i++) {
+		/*
+		 * Using of_property_read_u32_index(), smp value will only be
+		 * modified if valid u32 value can be decoded. This allows to
+		 * get either no value, 1 shared value for all indexes, or one
+		 * value per channel.
+		 */
+		of_property_read_u32_index(node, "st,min-sample-time-nsecs", i, &smp);
+
+		/* Prepare sampling time settings */
+		stm32_adc_smpr_init(adc, channels[i].channel, smp);
+	}
+
 	return scan_index;
 }
 
@@ -2035,6 +2064,19 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
 
 		stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val,
 					vin[1], scan_index, differential);
+
+		ret = of_property_read_u32(child, "st,min-sample-time-nsecs", &val);
+		/* st,min-sample-time-nsecs is optional */
+		if (!ret) {
+			stm32_adc_smpr_init(adc, channels[scan_index].channel, val);
+			if (vin[1])
+				stm32_adc_smpr_init(adc, vin[1], val);
+		} else if (ret != -EINVAL) {
+			dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs property %d\n",
+				ret);
+			goto err;
+		}
+
 		scan_index++;
 	}
 
@@ -2052,8 +2094,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 	struct stm32_adc *adc = iio_priv(indio_dev);
 	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
 	struct iio_chan_spec *channels;
-	int scan_index = 0, num_channels = 0, ret, i;
-	u32 smp = 0;
+	int scan_index = 0, num_channels = 0, ret;
 	bool legacy = false;
 
 	num_channels = of_get_available_child_count(node);
@@ -2077,13 +2118,6 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 		return -EINVAL;
 	}
 
-	/* Optional sample time is provided either for each, or all channels */
-	ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs");
-	if (ret > 1 && ret != num_channels) {
-		dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n");
-		return -EINVAL;
-	}
-
 	if (timestamping)
 		num_channels++;
 
@@ -2100,29 +2134,6 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 		return ret;
 	scan_index = ret;
 
-	for (i = 0; i < scan_index; i++) {
-		/*
-		 * Using of_property_read_u32_index(), smp value will only be
-		 * modified if valid u32 value can be decoded. This allows to
-		 * get either no value, 1 shared value for all indexes, or one
-		 * value per channel.
-		 */
-		of_property_read_u32_index(node, "st,min-sample-time-nsecs",
-					   i, &smp);
-
-		/*
-		 * For vrefint channel, ensure that the sampling time cannot
-		 * be lower than the one specified in the datasheet
-		 */
-		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
-		    smp < adc->cfg->ts_vrefint_ns) {
-			smp = adc->cfg->ts_vrefint_ns;
-		}
-
-		/* Prepare sampling time settings */
-		stm32_adc_smpr_init(adc, channels[i].channel, smp);
-	}
-
 	if (timestamping) {
 		struct iio_chan_spec *timestamp = &channels[scan_index];
 
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 7/7] iio: adc: stm32-adc: use generic binding for sample-time
@ 2021-09-08 15:54   ` Olivier Moysan
  0 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: devicetree, alsa-devel, linux-iio, linux-kernel, linux-stm32,
	linux-arm-kernel

Add st,min-sample-time-nsecs to channel generic binding.
Sample time can be defined par channel node. If a channel
is configured as differential, the same sample time applies
for both inputs.
Keep support of legacy st,min-sample-time-nsecs property
for backward compatibility.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
 drivers/iio/adc/stm32-adc.c | 75 +++++++++++++++++++++----------------
 1 file changed, 43 insertions(+), 32 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 9e52a7de9b16..2d52415770b3 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -1806,6 +1806,14 @@ static void stm32_adc_smpr_init(struct stm32_adc *adc, int channel, u32 smp_ns)
 	u32 period_ns, shift = smpr->shift, mask = smpr->mask;
 	unsigned int smp, r = smpr->reg;
 
+	/*
+	 * For vrefint channel, ensure that the sampling time cannot
+	 * be lower than the one specified in the datasheet
+	 */
+	if (channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
+	    smp_ns < adc->cfg->ts_vrefint_ns)
+		smp_ns = adc->cfg->ts_vrefint_ns;
+
 	/* Determine sampling time (ADC clock cycles) */
 	period_ns = NSEC_PER_SEC / adc->common->rate;
 	for (smp = 0; smp <= STM32_ADC_MAX_SMP; smp++)
@@ -1884,6 +1892,13 @@ static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm
 		return -ENODATA;
 	}
 
+	/* Optional sample time is provided either for each, or all channels */
+	ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs");
+	if (ret > 1 && ret != num_channels) {
+		dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n");
+		return -EINVAL;
+	}
+
 	return num_channels;
 }
 
@@ -1899,6 +1914,7 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
 	int scan_index = 0, val, ret, i;
 	struct property *prop;
 	const __be32 *cur;
+	u32 smp = 0;
 
 	if (num_diff) {
 		ret = of_property_read_u32_array(node, "st,adc-diff-channels",
@@ -1941,6 +1957,19 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
 		scan_index++;
 	}
 
+	for (i = 0; i < scan_index; i++) {
+		/*
+		 * Using of_property_read_u32_index(), smp value will only be
+		 * modified if valid u32 value can be decoded. This allows to
+		 * get either no value, 1 shared value for all indexes, or one
+		 * value per channel.
+		 */
+		of_property_read_u32_index(node, "st,min-sample-time-nsecs", i, &smp);
+
+		/* Prepare sampling time settings */
+		stm32_adc_smpr_init(adc, channels[i].channel, smp);
+	}
+
 	return scan_index;
 }
 
@@ -2035,6 +2064,19 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
 
 		stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val,
 					vin[1], scan_index, differential);
+
+		ret = of_property_read_u32(child, "st,min-sample-time-nsecs", &val);
+		/* st,min-sample-time-nsecs is optional */
+		if (!ret) {
+			stm32_adc_smpr_init(adc, channels[scan_index].channel, val);
+			if (vin[1])
+				stm32_adc_smpr_init(adc, vin[1], val);
+		} else if (ret != -EINVAL) {
+			dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs property %d\n",
+				ret);
+			goto err;
+		}
+
 		scan_index++;
 	}
 
@@ -2052,8 +2094,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 	struct stm32_adc *adc = iio_priv(indio_dev);
 	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
 	struct iio_chan_spec *channels;
-	int scan_index = 0, num_channels = 0, ret, i;
-	u32 smp = 0;
+	int scan_index = 0, num_channels = 0, ret;
 	bool legacy = false;
 
 	num_channels = of_get_available_child_count(node);
@@ -2077,13 +2118,6 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 		return -EINVAL;
 	}
 
-	/* Optional sample time is provided either for each, or all channels */
-	ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs");
-	if (ret > 1 && ret != num_channels) {
-		dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n");
-		return -EINVAL;
-	}
-
 	if (timestamping)
 		num_channels++;
 
@@ -2100,29 +2134,6 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 		return ret;
 	scan_index = ret;
 
-	for (i = 0; i < scan_index; i++) {
-		/*
-		 * Using of_property_read_u32_index(), smp value will only be
-		 * modified if valid u32 value can be decoded. This allows to
-		 * get either no value, 1 shared value for all indexes, or one
-		 * value per channel.
-		 */
-		of_property_read_u32_index(node, "st,min-sample-time-nsecs",
-					   i, &smp);
-
-		/*
-		 * For vrefint channel, ensure that the sampling time cannot
-		 * be lower than the one specified in the datasheet
-		 */
-		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
-		    smp < adc->cfg->ts_vrefint_ns) {
-			smp = adc->cfg->ts_vrefint_ns;
-		}
-
-		/* Prepare sampling time settings */
-		stm32_adc_smpr_init(adc, channels[i].channel, smp);
-	}
-
 	if (timestamping) {
 		struct iio_chan_spec *timestamp = &channels[scan_index];
 
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 7/7] iio: adc: stm32-adc: use generic binding for sample-time
@ 2021-09-08 15:54   ` Olivier Moysan
  0 siblings, 0 replies; 53+ messages in thread
From: Olivier Moysan @ 2021-09-08 15:54 UTC (permalink / raw)
  To: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	Olivier Moysan, Rob Herring
  Cc: alsa-devel, devicetree, linux-arm-kernel, linux-iio,
	linux-kernel, linux-stm32

Add st,min-sample-time-nsecs to channel generic binding.
Sample time can be defined par channel node. If a channel
is configured as differential, the same sample time applies
for both inputs.
Keep support of legacy st,min-sample-time-nsecs property
for backward compatibility.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
---
 drivers/iio/adc/stm32-adc.c | 75 +++++++++++++++++++++----------------
 1 file changed, 43 insertions(+), 32 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 9e52a7de9b16..2d52415770b3 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -1806,6 +1806,14 @@ static void stm32_adc_smpr_init(struct stm32_adc *adc, int channel, u32 smp_ns)
 	u32 period_ns, shift = smpr->shift, mask = smpr->mask;
 	unsigned int smp, r = smpr->reg;
 
+	/*
+	 * For vrefint channel, ensure that the sampling time cannot
+	 * be lower than the one specified in the datasheet
+	 */
+	if (channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
+	    smp_ns < adc->cfg->ts_vrefint_ns)
+		smp_ns = adc->cfg->ts_vrefint_ns;
+
 	/* Determine sampling time (ADC clock cycles) */
 	period_ns = NSEC_PER_SEC / adc->common->rate;
 	for (smp = 0; smp <= STM32_ADC_MAX_SMP; smp++)
@@ -1884,6 +1892,13 @@ static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm
 		return -ENODATA;
 	}
 
+	/* Optional sample time is provided either for each, or all channels */
+	ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs");
+	if (ret > 1 && ret != num_channels) {
+		dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n");
+		return -EINVAL;
+	}
+
 	return num_channels;
 }
 
@@ -1899,6 +1914,7 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
 	int scan_index = 0, val, ret, i;
 	struct property *prop;
 	const __be32 *cur;
+	u32 smp = 0;
 
 	if (num_diff) {
 		ret = of_property_read_u32_array(node, "st,adc-diff-channels",
@@ -1941,6 +1957,19 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
 		scan_index++;
 	}
 
+	for (i = 0; i < scan_index; i++) {
+		/*
+		 * Using of_property_read_u32_index(), smp value will only be
+		 * modified if valid u32 value can be decoded. This allows to
+		 * get either no value, 1 shared value for all indexes, or one
+		 * value per channel.
+		 */
+		of_property_read_u32_index(node, "st,min-sample-time-nsecs", i, &smp);
+
+		/* Prepare sampling time settings */
+		stm32_adc_smpr_init(adc, channels[i].channel, smp);
+	}
+
 	return scan_index;
 }
 
@@ -2035,6 +2064,19 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
 
 		stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val,
 					vin[1], scan_index, differential);
+
+		ret = of_property_read_u32(child, "st,min-sample-time-nsecs", &val);
+		/* st,min-sample-time-nsecs is optional */
+		if (!ret) {
+			stm32_adc_smpr_init(adc, channels[scan_index].channel, val);
+			if (vin[1])
+				stm32_adc_smpr_init(adc, vin[1], val);
+		} else if (ret != -EINVAL) {
+			dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs property %d\n",
+				ret);
+			goto err;
+		}
+
 		scan_index++;
 	}
 
@@ -2052,8 +2094,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 	struct stm32_adc *adc = iio_priv(indio_dev);
 	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
 	struct iio_chan_spec *channels;
-	int scan_index = 0, num_channels = 0, ret, i;
-	u32 smp = 0;
+	int scan_index = 0, num_channels = 0, ret;
 	bool legacy = false;
 
 	num_channels = of_get_available_child_count(node);
@@ -2077,13 +2118,6 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 		return -EINVAL;
 	}
 
-	/* Optional sample time is provided either for each, or all channels */
-	ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs");
-	if (ret > 1 && ret != num_channels) {
-		dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n");
-		return -EINVAL;
-	}
-
 	if (timestamping)
 		num_channels++;
 
@@ -2100,29 +2134,6 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
 		return ret;
 	scan_index = ret;
 
-	for (i = 0; i < scan_index; i++) {
-		/*
-		 * Using of_property_read_u32_index(), smp value will only be
-		 * modified if valid u32 value can be decoded. This allows to
-		 * get either no value, 1 shared value for all indexes, or one
-		 * value per channel.
-		 */
-		of_property_read_u32_index(node, "st,min-sample-time-nsecs",
-					   i, &smp);
-
-		/*
-		 * For vrefint channel, ensure that the sampling time cannot
-		 * be lower than the one specified in the datasheet
-		 */
-		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
-		    smp < adc->cfg->ts_vrefint_ns) {
-			smp = adc->cfg->ts_vrefint_ns;
-		}
-
-		/* Prepare sampling time settings */
-		stm32_adc_smpr_init(adc, channels[i].channel, smp);
-	}
-
 	if (timestamping) {
 		struct iio_chan_spec *timestamp = &channels[scan_index];
 
-- 
2.17.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH 0/7] add internal channels support
  2021-09-08 15:54 ` Olivier Moysan
  (?)
@ 2021-09-11 15:44   ` Jonathan Cameron
  -1 siblings, 0 replies; 53+ messages in thread
From: Jonathan Cameron @ 2021-09-11 15:44 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Lars-Peter Clausen, Maxime Coquelin, Rob Herring, alsa-devel,
	devicetree, linux-arm-kernel, linux-iio, linux-kernel,
	linux-stm32

On Wed, 8 Sep 2021 17:54:45 +0200
Olivier Moysan <olivier.moysan@foss.st.com> wrote:

> This patchset adds support of ADC2 internal channels VDDCORE, VREFINT and VBAT
> on STM32MP15x SoCs. The generic IIO channel bindings is also introduced here
> to provide this feature. The legacy channel binding is kept for backward compatibility.

Before I actually look at the patch, general naming comment.
Please make sure that the driver / device name appears in the cover letter title
and all the patches.  It makes it much easier for people to find the code relevant
to them.

Thank,

Jonathan

> 
> Olivier Moysan (7):
>   dt-bindings: iio: adc: add generic channel binding
>   dt-bindings: iio: adc: add nvmem support for vrefint internal channel
>   iio: adc stm32-adc: split channel init into several routines
>   iio: adc: stm32-adc: add support of generic channels binding
>   iio: adc: stm32-adc: add support of internal channels
>   iio: adc: stm32-adc: add vrefint calibration support
>   iio: adc: stm32-adc: use generic binding for sample-time
> 
>  .../bindings/iio/adc/st,stm32-adc.yaml        | 108 ++++-
>  drivers/iio/adc/stm32-adc-core.h              |   8 +
>  drivers/iio/adc/stm32-adc.c                   | 418 ++++++++++++++++--
>  3 files changed, 482 insertions(+), 52 deletions(-)
> 


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 0/7] add internal channels support
@ 2021-09-11 15:44   ` Jonathan Cameron
  0 siblings, 0 replies; 53+ messages in thread
From: Jonathan Cameron @ 2021-09-11 15:44 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: devicetree, alsa-devel, Lars-Peter Clausen, linux-iio,
	Alexandre Torgue, linux-kernel, Rob Herring, Maxime Coquelin,
	Arnaud Pouliquen, Fabrice Gasnier, linux-stm32, linux-arm-kernel

On Wed, 8 Sep 2021 17:54:45 +0200
Olivier Moysan <olivier.moysan@foss.st.com> wrote:

> This patchset adds support of ADC2 internal channels VDDCORE, VREFINT and VBAT
> on STM32MP15x SoCs. The generic IIO channel bindings is also introduced here
> to provide this feature. The legacy channel binding is kept for backward compatibility.

Before I actually look at the patch, general naming comment.
Please make sure that the driver / device name appears in the cover letter title
and all the patches.  It makes it much easier for people to find the code relevant
to them.

Thank,

Jonathan

> 
> Olivier Moysan (7):
>   dt-bindings: iio: adc: add generic channel binding
>   dt-bindings: iio: adc: add nvmem support for vrefint internal channel
>   iio: adc stm32-adc: split channel init into several routines
>   iio: adc: stm32-adc: add support of generic channels binding
>   iio: adc: stm32-adc: add support of internal channels
>   iio: adc: stm32-adc: add vrefint calibration support
>   iio: adc: stm32-adc: use generic binding for sample-time
> 
>  .../bindings/iio/adc/st,stm32-adc.yaml        | 108 ++++-
>  drivers/iio/adc/stm32-adc-core.h              |   8 +
>  drivers/iio/adc/stm32-adc.c                   | 418 ++++++++++++++++--
>  3 files changed, 482 insertions(+), 52 deletions(-)
> 


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 0/7] add internal channels support
@ 2021-09-11 15:44   ` Jonathan Cameron
  0 siblings, 0 replies; 53+ messages in thread
From: Jonathan Cameron @ 2021-09-11 15:44 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Lars-Peter Clausen, Maxime Coquelin, Rob Herring, alsa-devel,
	devicetree, linux-arm-kernel, linux-iio, linux-kernel,
	linux-stm32

On Wed, 8 Sep 2021 17:54:45 +0200
Olivier Moysan <olivier.moysan@foss.st.com> wrote:

> This patchset adds support of ADC2 internal channels VDDCORE, VREFINT and VBAT
> on STM32MP15x SoCs. The generic IIO channel bindings is also introduced here
> to provide this feature. The legacy channel binding is kept for backward compatibility.

Before I actually look at the patch, general naming comment.
Please make sure that the driver / device name appears in the cover letter title
and all the patches.  It makes it much easier for people to find the code relevant
to them.

Thank,

Jonathan

> 
> Olivier Moysan (7):
>   dt-bindings: iio: adc: add generic channel binding
>   dt-bindings: iio: adc: add nvmem support for vrefint internal channel
>   iio: adc stm32-adc: split channel init into several routines
>   iio: adc: stm32-adc: add support of generic channels binding
>   iio: adc: stm32-adc: add support of internal channels
>   iio: adc: stm32-adc: add vrefint calibration support
>   iio: adc: stm32-adc: use generic binding for sample-time
> 
>  .../bindings/iio/adc/st,stm32-adc.yaml        | 108 ++++-
>  drivers/iio/adc/stm32-adc-core.h              |   8 +
>  drivers/iio/adc/stm32-adc.c                   | 418 ++++++++++++++++--
>  3 files changed, 482 insertions(+), 52 deletions(-)
> 


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 1/7] dt-bindings: iio: adc: add generic channel binding
  2021-09-08 15:54   ` Olivier Moysan
  (?)
@ 2021-09-11 15:51     ` Jonathan Cameron
  -1 siblings, 0 replies; 53+ messages in thread
From: Jonathan Cameron @ 2021-09-11 15:51 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Lars-Peter Clausen, Maxime Coquelin, Rob Herring, alsa-devel,
	devicetree, linux-arm-kernel, linux-iio, linux-kernel,
	linux-stm32

On Wed, 8 Sep 2021 17:54:46 +0200
Olivier Moysan <olivier.moysan@foss.st.com> wrote:

dt-bindings: iio: adc: stm32-adc: ...

> Add ADC generic channel binding. This binding should
> be used as an alternate to legacy channel properties
> whenever possible.
> ADC generic channel binding allows to identify supported
> internal channels through the following reserved label names:
> "vddcore", "vrefint" and "vbat".
> This binding also allows to set a different sampling time
> for each channel.
> 
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
> ---
> Note: The schema here is too permissive as either legacy or generic
> channels properties are required. These properties are mutually
> exclusive, however all attempts to describe this constraint are
> failing. In particular the following schemas and their variants have
> shown unsucessful.
> 
Given yaml still drives me crazy I'll rely on Rob's expertise for suggestions
here.

The new binding text itself looks great to me.

> oneOf:
>   - anyOf:
> 	- required:
> 		- st,adc-channels
> 	- required:
> 		- st,adc-diff-channels
>   - anyOf:
> 	- required:
> 		- $nodename
> 
> - if:
>   patternProperties:
>     "^channel@([0-9]|1[0-9])$":
>       type: object
> then:
>   properties:
>     st,adc-channels: false
>     st,adc-diff-channels: false
> else:
>   - anyOf:
>       - required:
>           - st,adc-channels
>       - required:
>           - st,adc-diff-channels
> ---
>  .../bindings/iio/adc/st,stm32-adc.yaml        | 100 ++++++++++++++++--
>  1 file changed, 93 insertions(+), 7 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
> index a58334c3bb76..a1f6cbe144ba 100644
> --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
> @@ -222,6 +222,12 @@ patternProperties:
>        '#io-channel-cells':
>          const: 1
>  
> +      '#address-cells':
> +        const: 1
> +
> +      '#size-cells':
> +        const: 0
> +
>        interrupts:
>          description: |
>            IRQ Line for the ADC instance. Valid values are:
> @@ -265,7 +271,9 @@ patternProperties:
>            <vinp vinn>, <vinp vinn>,... vinp and vinn are numbered from 0 to 19.
>  
>            Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is
> -          required. Both properties can be used together. Some channels can be
> +          required if no adc generic channel is defined. These legacy channel
> +          properties are exclusive with adc generic channel bindings.
> +          Both properties can be used together. Some channels can be
>            used as single-ended and some other ones as differential (mixed). But
>            channels can't be configured both as single-ended and differential.
>          $ref: /schemas/types.yaml#/definitions/uint32-matrix
> @@ -290,6 +298,44 @@ patternProperties:
>            each channel.
>          $ref: /schemas/types.yaml#/definitions/uint32-array
>  
> +    patternProperties:
> +      "^channel@([0-9]|1[0-9])$":
> +        type: object
> +        $ref: "adc.yaml"
> +        description: |
> +          Represents the external channels which are connected to the ADC.
> +
> +        properties:
> +          reg:
> +            items:
> +              minimum: 0
> +              maximum: 19
> +
> +          label:
> +            description: |
> +              Unique name to identify which channel this is.
> +              Reserved label names "vddcore", "vrefint" and "vbat"
> +              are used to identify internal channels with matching names.
> +
> +          diff-channels:
> +            $ref: /schemas/types.yaml#/definitions/uint32-array
> +            items:
> +              minimum: 0
> +              maximum: 19
> +
> +          st,min-sample-time-nsecs:
> +            $ref: /schemas/types.yaml#/definitions/uint32
> +            description: |
> +              Minimum sampling time in nanoseconds. Depending on hardware (board)
> +              e.g. high/low analog input source impedance, fine tune of ADC
> +              sampling time may be recommended.
> +
> +        required:
> +          - reg
> +
> +    dependencies:
> +      $nodename: [ '#address-cells', '#size-cells' ]
> +
>      allOf:
>        - if:
>            properties:
> @@ -369,12 +415,6 @@ patternProperties:
>  
>      additionalProperties: false
>  
> -    anyOf:
> -      - required:
> -          - st,adc-channels
> -      - required:
> -          - st,adc-diff-channels
> -
>      required:
>        - compatible
>        - reg
> @@ -451,4 +491,50 @@ examples:
>          // other adc child node follow...
>        };
>  
> +  - |
> +    // Example 3: with stm32mp157c to setup ADC2 with:
> +    // - internal channels 13, 14, 15.
> +      #include <dt-bindings/interrupt-controller/arm-gic.h>
> +      #include <dt-bindings/clock/stm32mp1-clks.h>
> +      adc122: adc@48003000 {
> +        compatible = "st,stm32mp1-adc-core";
> +        reg = <0x48003000 0x400>;
> +        interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
> +                     <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
> +        clocks = <&rcc ADC12>, <&rcc ADC12_K>;
> +        clock-names = "bus", "adc";
> +        booster-supply = <&booster>;
> +        vdd-supply = <&vdd>;
> +        vdda-supply = <&vdda>;
> +        vref-supply = <&vref>;
> +        st,syscfg = <&syscfg>;
> +        interrupt-controller;
> +        #interrupt-cells = <1>;
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +        adc@100 {
> +          compatible = "st,stm32mp1-adc";
> +          #io-channel-cells = <1>;
> +          reg = <0x100>;
> +          interrupts = <1>;
> +          #address-cells = <1>;
> +          #size-cells = <0>;
> +          channel@13 {
> +            reg = <13>;
> +            label = "vrefint";
> +            st,min-sample-time-nsecs = <9000>;
> +          };
> +          channel@14 {
> +            reg = <14>;
> +            label = "vddcore";
> +            st,min-sample-time-nsecs = <9000>;
> +          };
> +          channel@15 {
> +            reg = <15>;
> +            label = "vbat";
> +            st,min-sample-time-nsecs = <9000>;
> +          };
> +        };
> +      };
> +
>  ...


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 1/7] dt-bindings: iio: adc: add generic channel binding
@ 2021-09-11 15:51     ` Jonathan Cameron
  0 siblings, 0 replies; 53+ messages in thread
From: Jonathan Cameron @ 2021-09-11 15:51 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: devicetree, alsa-devel, Lars-Peter Clausen, linux-iio,
	Alexandre Torgue, linux-kernel, Rob Herring, Maxime Coquelin,
	Arnaud Pouliquen, Fabrice Gasnier, linux-stm32, linux-arm-kernel

On Wed, 8 Sep 2021 17:54:46 +0200
Olivier Moysan <olivier.moysan@foss.st.com> wrote:

dt-bindings: iio: adc: stm32-adc: ...

> Add ADC generic channel binding. This binding should
> be used as an alternate to legacy channel properties
> whenever possible.
> ADC generic channel binding allows to identify supported
> internal channels through the following reserved label names:
> "vddcore", "vrefint" and "vbat".
> This binding also allows to set a different sampling time
> for each channel.
> 
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
> ---
> Note: The schema here is too permissive as either legacy or generic
> channels properties are required. These properties are mutually
> exclusive, however all attempts to describe this constraint are
> failing. In particular the following schemas and their variants have
> shown unsucessful.
> 
Given yaml still drives me crazy I'll rely on Rob's expertise for suggestions
here.

The new binding text itself looks great to me.

> oneOf:
>   - anyOf:
> 	- required:
> 		- st,adc-channels
> 	- required:
> 		- st,adc-diff-channels
>   - anyOf:
> 	- required:
> 		- $nodename
> 
> - if:
>   patternProperties:
>     "^channel@([0-9]|1[0-9])$":
>       type: object
> then:
>   properties:
>     st,adc-channels: false
>     st,adc-diff-channels: false
> else:
>   - anyOf:
>       - required:
>           - st,adc-channels
>       - required:
>           - st,adc-diff-channels
> ---
>  .../bindings/iio/adc/st,stm32-adc.yaml        | 100 ++++++++++++++++--
>  1 file changed, 93 insertions(+), 7 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
> index a58334c3bb76..a1f6cbe144ba 100644
> --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
> @@ -222,6 +222,12 @@ patternProperties:
>        '#io-channel-cells':
>          const: 1
>  
> +      '#address-cells':
> +        const: 1
> +
> +      '#size-cells':
> +        const: 0
> +
>        interrupts:
>          description: |
>            IRQ Line for the ADC instance. Valid values are:
> @@ -265,7 +271,9 @@ patternProperties:
>            <vinp vinn>, <vinp vinn>,... vinp and vinn are numbered from 0 to 19.
>  
>            Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is
> -          required. Both properties can be used together. Some channels can be
> +          required if no adc generic channel is defined. These legacy channel
> +          properties are exclusive with adc generic channel bindings.
> +          Both properties can be used together. Some channels can be
>            used as single-ended and some other ones as differential (mixed). But
>            channels can't be configured both as single-ended and differential.
>          $ref: /schemas/types.yaml#/definitions/uint32-matrix
> @@ -290,6 +298,44 @@ patternProperties:
>            each channel.
>          $ref: /schemas/types.yaml#/definitions/uint32-array
>  
> +    patternProperties:
> +      "^channel@([0-9]|1[0-9])$":
> +        type: object
> +        $ref: "adc.yaml"
> +        description: |
> +          Represents the external channels which are connected to the ADC.
> +
> +        properties:
> +          reg:
> +            items:
> +              minimum: 0
> +              maximum: 19
> +
> +          label:
> +            description: |
> +              Unique name to identify which channel this is.
> +              Reserved label names "vddcore", "vrefint" and "vbat"
> +              are used to identify internal channels with matching names.
> +
> +          diff-channels:
> +            $ref: /schemas/types.yaml#/definitions/uint32-array
> +            items:
> +              minimum: 0
> +              maximum: 19
> +
> +          st,min-sample-time-nsecs:
> +            $ref: /schemas/types.yaml#/definitions/uint32
> +            description: |
> +              Minimum sampling time in nanoseconds. Depending on hardware (board)
> +              e.g. high/low analog input source impedance, fine tune of ADC
> +              sampling time may be recommended.
> +
> +        required:
> +          - reg
> +
> +    dependencies:
> +      $nodename: [ '#address-cells', '#size-cells' ]
> +
>      allOf:
>        - if:
>            properties:
> @@ -369,12 +415,6 @@ patternProperties:
>  
>      additionalProperties: false
>  
> -    anyOf:
> -      - required:
> -          - st,adc-channels
> -      - required:
> -          - st,adc-diff-channels
> -
>      required:
>        - compatible
>        - reg
> @@ -451,4 +491,50 @@ examples:
>          // other adc child node follow...
>        };
>  
> +  - |
> +    // Example 3: with stm32mp157c to setup ADC2 with:
> +    // - internal channels 13, 14, 15.
> +      #include <dt-bindings/interrupt-controller/arm-gic.h>
> +      #include <dt-bindings/clock/stm32mp1-clks.h>
> +      adc122: adc@48003000 {
> +        compatible = "st,stm32mp1-adc-core";
> +        reg = <0x48003000 0x400>;
> +        interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
> +                     <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
> +        clocks = <&rcc ADC12>, <&rcc ADC12_K>;
> +        clock-names = "bus", "adc";
> +        booster-supply = <&booster>;
> +        vdd-supply = <&vdd>;
> +        vdda-supply = <&vdda>;
> +        vref-supply = <&vref>;
> +        st,syscfg = <&syscfg>;
> +        interrupt-controller;
> +        #interrupt-cells = <1>;
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +        adc@100 {
> +          compatible = "st,stm32mp1-adc";
> +          #io-channel-cells = <1>;
> +          reg = <0x100>;
> +          interrupts = <1>;
> +          #address-cells = <1>;
> +          #size-cells = <0>;
> +          channel@13 {
> +            reg = <13>;
> +            label = "vrefint";
> +            st,min-sample-time-nsecs = <9000>;
> +          };
> +          channel@14 {
> +            reg = <14>;
> +            label = "vddcore";
> +            st,min-sample-time-nsecs = <9000>;
> +          };
> +          channel@15 {
> +            reg = <15>;
> +            label = "vbat";
> +            st,min-sample-time-nsecs = <9000>;
> +          };
> +        };
> +      };
> +
>  ...


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 1/7] dt-bindings: iio: adc: add generic channel binding
@ 2021-09-11 15:51     ` Jonathan Cameron
  0 siblings, 0 replies; 53+ messages in thread
From: Jonathan Cameron @ 2021-09-11 15:51 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Lars-Peter Clausen, Maxime Coquelin, Rob Herring, alsa-devel,
	devicetree, linux-arm-kernel, linux-iio, linux-kernel,
	linux-stm32

On Wed, 8 Sep 2021 17:54:46 +0200
Olivier Moysan <olivier.moysan@foss.st.com> wrote:

dt-bindings: iio: adc: stm32-adc: ...

> Add ADC generic channel binding. This binding should
> be used as an alternate to legacy channel properties
> whenever possible.
> ADC generic channel binding allows to identify supported
> internal channels through the following reserved label names:
> "vddcore", "vrefint" and "vbat".
> This binding also allows to set a different sampling time
> for each channel.
> 
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
> ---
> Note: The schema here is too permissive as either legacy or generic
> channels properties are required. These properties are mutually
> exclusive, however all attempts to describe this constraint are
> failing. In particular the following schemas and their variants have
> shown unsucessful.
> 
Given yaml still drives me crazy I'll rely on Rob's expertise for suggestions
here.

The new binding text itself looks great to me.

> oneOf:
>   - anyOf:
> 	- required:
> 		- st,adc-channels
> 	- required:
> 		- st,adc-diff-channels
>   - anyOf:
> 	- required:
> 		- $nodename
> 
> - if:
>   patternProperties:
>     "^channel@([0-9]|1[0-9])$":
>       type: object
> then:
>   properties:
>     st,adc-channels: false
>     st,adc-diff-channels: false
> else:
>   - anyOf:
>       - required:
>           - st,adc-channels
>       - required:
>           - st,adc-diff-channels
> ---
>  .../bindings/iio/adc/st,stm32-adc.yaml        | 100 ++++++++++++++++--
>  1 file changed, 93 insertions(+), 7 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
> index a58334c3bb76..a1f6cbe144ba 100644
> --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
> @@ -222,6 +222,12 @@ patternProperties:
>        '#io-channel-cells':
>          const: 1
>  
> +      '#address-cells':
> +        const: 1
> +
> +      '#size-cells':
> +        const: 0
> +
>        interrupts:
>          description: |
>            IRQ Line for the ADC instance. Valid values are:
> @@ -265,7 +271,9 @@ patternProperties:
>            <vinp vinn>, <vinp vinn>,... vinp and vinn are numbered from 0 to 19.
>  
>            Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is
> -          required. Both properties can be used together. Some channels can be
> +          required if no adc generic channel is defined. These legacy channel
> +          properties are exclusive with adc generic channel bindings.
> +          Both properties can be used together. Some channels can be
>            used as single-ended and some other ones as differential (mixed). But
>            channels can't be configured both as single-ended and differential.
>          $ref: /schemas/types.yaml#/definitions/uint32-matrix
> @@ -290,6 +298,44 @@ patternProperties:
>            each channel.
>          $ref: /schemas/types.yaml#/definitions/uint32-array
>  
> +    patternProperties:
> +      "^channel@([0-9]|1[0-9])$":
> +        type: object
> +        $ref: "adc.yaml"
> +        description: |
> +          Represents the external channels which are connected to the ADC.
> +
> +        properties:
> +          reg:
> +            items:
> +              minimum: 0
> +              maximum: 19
> +
> +          label:
> +            description: |
> +              Unique name to identify which channel this is.
> +              Reserved label names "vddcore", "vrefint" and "vbat"
> +              are used to identify internal channels with matching names.
> +
> +          diff-channels:
> +            $ref: /schemas/types.yaml#/definitions/uint32-array
> +            items:
> +              minimum: 0
> +              maximum: 19
> +
> +          st,min-sample-time-nsecs:
> +            $ref: /schemas/types.yaml#/definitions/uint32
> +            description: |
> +              Minimum sampling time in nanoseconds. Depending on hardware (board)
> +              e.g. high/low analog input source impedance, fine tune of ADC
> +              sampling time may be recommended.
> +
> +        required:
> +          - reg
> +
> +    dependencies:
> +      $nodename: [ '#address-cells', '#size-cells' ]
> +
>      allOf:
>        - if:
>            properties:
> @@ -369,12 +415,6 @@ patternProperties:
>  
>      additionalProperties: false
>  
> -    anyOf:
> -      - required:
> -          - st,adc-channels
> -      - required:
> -          - st,adc-diff-channels
> -
>      required:
>        - compatible
>        - reg
> @@ -451,4 +491,50 @@ examples:
>          // other adc child node follow...
>        };
>  
> +  - |
> +    // Example 3: with stm32mp157c to setup ADC2 with:
> +    // - internal channels 13, 14, 15.
> +      #include <dt-bindings/interrupt-controller/arm-gic.h>
> +      #include <dt-bindings/clock/stm32mp1-clks.h>
> +      adc122: adc@48003000 {
> +        compatible = "st,stm32mp1-adc-core";
> +        reg = <0x48003000 0x400>;
> +        interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
> +                     <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
> +        clocks = <&rcc ADC12>, <&rcc ADC12_K>;
> +        clock-names = "bus", "adc";
> +        booster-supply = <&booster>;
> +        vdd-supply = <&vdd>;
> +        vdda-supply = <&vdda>;
> +        vref-supply = <&vref>;
> +        st,syscfg = <&syscfg>;
> +        interrupt-controller;
> +        #interrupt-cells = <1>;
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +        adc@100 {
> +          compatible = "st,stm32mp1-adc";
> +          #io-channel-cells = <1>;
> +          reg = <0x100>;
> +          interrupts = <1>;
> +          #address-cells = <1>;
> +          #size-cells = <0>;
> +          channel@13 {
> +            reg = <13>;
> +            label = "vrefint";
> +            st,min-sample-time-nsecs = <9000>;
> +          };
> +          channel@14 {
> +            reg = <14>;
> +            label = "vddcore";
> +            st,min-sample-time-nsecs = <9000>;
> +          };
> +          channel@15 {
> +            reg = <15>;
> +            label = "vbat";
> +            st,min-sample-time-nsecs = <9000>;
> +          };
> +        };
> +      };
> +
>  ...


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 4/7] iio: adc: stm32-adc: add support of generic channels binding
  2021-09-08 15:54   ` Olivier Moysan
  (?)
@ 2021-09-11 16:08     ` Jonathan Cameron
  -1 siblings, 0 replies; 53+ messages in thread
From: Jonathan Cameron @ 2021-09-11 16:08 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Lars-Peter Clausen, Maxime Coquelin, Rob Herring, alsa-devel,
	devicetree, linux-arm-kernel, linux-iio, linux-kernel,
	linux-stm32

On Wed, 8 Sep 2021 17:54:49 +0200
Olivier Moysan <olivier.moysan@foss.st.com> wrote:

> Add support of generic IIO channel binding:
> ./devicetree/bindings/iio/adc/adc.yaml
> Keep support of st,adc-channels and st,adc-diff-channels
> for backward compatibility.
> 
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>

Hi Olivier,

A few minor things inline.

Thanks,

Jonathan

> ---
>  drivers/iio/adc/stm32-adc.c | 99 +++++++++++++++++++++++++++++++++----
>  1 file changed, 90 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> index 2f137d14f141..ae4a314854f7 100644
> --- a/drivers/iio/adc/stm32-adc.c
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -35,7 +35,7 @@
>  #define STM32H7_BOOST_CLKRATE		20000000UL
>  
>  #define STM32_ADC_CH_MAX		20	/* max number of channels */
> -#define STM32_ADC_CH_SZ			10	/* max channel name size */
> +#define STM32_ADC_CH_SZ			16	/* max channel name size */
>  #define STM32_ADC_MAX_SQ		16	/* SQ1..SQ16 */
>  #define STM32_ADC_MAX_SMP		7	/* SMPx range is [0..7] */
>  #define STM32_ADC_TIMEOUT_US		100000
> @@ -1732,6 +1732,11 @@ static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm
>  		num_channels += ret;
>  	}
>  
> +	if (!num_channels) {
> +		dev_err(indio_dev->dev.parent, "No channel found\n");
> +		return -ENODATA;

I'd return 0 and handle this at the caller.   Both because it makes this
patch more readable and because a count of 0 isn't an error of the function
reading how many they are, but rather at a higher level.

> +	}
> +
>  	return num_channels;
>  }
>  
> @@ -1792,6 +1797,73 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
>  	return scan_index;
>  }
>  
> +static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
> +				       struct stm32_adc *adc,
> +				       struct iio_chan_spec *channels)
> +{
> +	struct device_node *node = indio_dev->dev.of_node;
> +	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
> +	struct device_node *child;
> +	const char *name;
> +	int val, scan_index = 0, ret;
> +	bool differential;
> +	u32 vin[2];
> +
> +	for_each_available_child_of_node(node, child) {
> +		ret = of_property_read_u32(child, "reg", &val);
> +		if (ret) {
> +			dev_err(&indio_dev->dev, "Missing channel index %d\n", ret);
> +			goto err;
> +		}
> +
> +		ret = of_property_read_string(child, "label", &name);
> +		/* label is optional */
> +		if (!ret) {
> +			if (strlen(name) >= STM32_ADC_CH_SZ) {
> +				dev_err(&indio_dev->dev, "Label %s exceeds %d characters\n",
> +					name, STM32_ADC_CH_SZ);
> +				return -EINVAL;
> +			}
> +			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
> +		} else if (ret != -EINVAL) {
> +			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
> +			goto err;
> +		}
> +
> +		if (val >= adc_info->max_channels) {
> +			dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
> +			ret = -EINVAL;
> +			goto err;
> +		}
> +
> +		differential = false;
> +		ret = of_property_read_u32_array(child, "diff-channels", vin, 2);
> +		/* diff-channels is optional */
> +		if (!ret) {
> +			differential = true;
> +			if (vin[0] != val || vin[1] >= adc_info->max_channels) {
> +				dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
> +					vin[0], vin[1]);
> +				goto err;
> +			}
> +		} else if (ret != -EINVAL) {
> +			dev_err(&indio_dev->dev, "Invalid diff-channels property %d\n", ret);
> +			goto err;
> +		}
> +
> +		stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val,
> +					vin[1], scan_index, differential);
> +		scan_index++;
> +	}
> +
> +	return scan_index;
> +
> +err:
> +	of_node_put(child);
> +
> +	return ret;
> +}
> +
>  static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
>  {
>  	struct device_node *node = indio_dev->dev.of_node;
> @@ -1800,15 +1872,21 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
>  	struct iio_chan_spec *channels;
>  	int scan_index = 0, num_channels = 0, ret, i;
>  	u32 smp = 0;
> +	bool legacy = false;
>  
> -	ret = stm32_adc_get_legacy_chan_count(indio_dev, adc);
> -	if (ret < 0)
> -		return ret;
> -	num_channels = ret;
> -
> +	num_channels = of_get_available_child_count(node);
> +	/*
> +	 * If no channels have been found, fallback to channels legacy properties.
> +	 * Legacy channel properties will be ignored, if some channels are

drop the ,

Legacy channel properties will be ignored if some channels...


> +	 * already defined using the standard binding.
> +	 */
>  	if (!num_channels) {
> -		dev_err(&indio_dev->dev, "No channels configured\n");
> -		return -ENODATA;
> +		ret = stm32_adc_get_legacy_chan_count(indio_dev, adc);
> +		if (ret < 0)
> +			return ret;
> +
> +		legacy = true;

Trivial but I would set legacy at top of this if block so it is nearer the comment.

> +		num_channels = ret;
>  	}
>  
>  	if (num_channels > adc_info->max_channels) {
> @@ -1832,7 +1910,10 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
>  	if (!channels)
>  		return -ENOMEM;
>  
> -	ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels);
> +	if (legacy)
> +		ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels);
> +	else
> +		ret = stm32_adc_generic_chan_init(indio_dev, adc, channels);
>  	if (ret < 0)
>  		return ret;
>  	scan_index = ret;


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 4/7] iio: adc: stm32-adc: add support of generic channels binding
@ 2021-09-11 16:08     ` Jonathan Cameron
  0 siblings, 0 replies; 53+ messages in thread
From: Jonathan Cameron @ 2021-09-11 16:08 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: devicetree, alsa-devel, Lars-Peter Clausen, linux-iio,
	Alexandre Torgue, linux-kernel, Rob Herring, Maxime Coquelin,
	Arnaud Pouliquen, Fabrice Gasnier, linux-stm32, linux-arm-kernel

On Wed, 8 Sep 2021 17:54:49 +0200
Olivier Moysan <olivier.moysan@foss.st.com> wrote:

> Add support of generic IIO channel binding:
> ./devicetree/bindings/iio/adc/adc.yaml
> Keep support of st,adc-channels and st,adc-diff-channels
> for backward compatibility.
> 
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>

Hi Olivier,

A few minor things inline.

Thanks,

Jonathan

> ---
>  drivers/iio/adc/stm32-adc.c | 99 +++++++++++++++++++++++++++++++++----
>  1 file changed, 90 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> index 2f137d14f141..ae4a314854f7 100644
> --- a/drivers/iio/adc/stm32-adc.c
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -35,7 +35,7 @@
>  #define STM32H7_BOOST_CLKRATE		20000000UL
>  
>  #define STM32_ADC_CH_MAX		20	/* max number of channels */
> -#define STM32_ADC_CH_SZ			10	/* max channel name size */
> +#define STM32_ADC_CH_SZ			16	/* max channel name size */
>  #define STM32_ADC_MAX_SQ		16	/* SQ1..SQ16 */
>  #define STM32_ADC_MAX_SMP		7	/* SMPx range is [0..7] */
>  #define STM32_ADC_TIMEOUT_US		100000
> @@ -1732,6 +1732,11 @@ static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm
>  		num_channels += ret;
>  	}
>  
> +	if (!num_channels) {
> +		dev_err(indio_dev->dev.parent, "No channel found\n");
> +		return -ENODATA;

I'd return 0 and handle this at the caller.   Both because it makes this
patch more readable and because a count of 0 isn't an error of the function
reading how many they are, but rather at a higher level.

> +	}
> +
>  	return num_channels;
>  }
>  
> @@ -1792,6 +1797,73 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
>  	return scan_index;
>  }
>  
> +static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
> +				       struct stm32_adc *adc,
> +				       struct iio_chan_spec *channels)
> +{
> +	struct device_node *node = indio_dev->dev.of_node;
> +	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
> +	struct device_node *child;
> +	const char *name;
> +	int val, scan_index = 0, ret;
> +	bool differential;
> +	u32 vin[2];
> +
> +	for_each_available_child_of_node(node, child) {
> +		ret = of_property_read_u32(child, "reg", &val);
> +		if (ret) {
> +			dev_err(&indio_dev->dev, "Missing channel index %d\n", ret);
> +			goto err;
> +		}
> +
> +		ret = of_property_read_string(child, "label", &name);
> +		/* label is optional */
> +		if (!ret) {
> +			if (strlen(name) >= STM32_ADC_CH_SZ) {
> +				dev_err(&indio_dev->dev, "Label %s exceeds %d characters\n",
> +					name, STM32_ADC_CH_SZ);
> +				return -EINVAL;
> +			}
> +			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
> +		} else if (ret != -EINVAL) {
> +			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
> +			goto err;
> +		}
> +
> +		if (val >= adc_info->max_channels) {
> +			dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
> +			ret = -EINVAL;
> +			goto err;
> +		}
> +
> +		differential = false;
> +		ret = of_property_read_u32_array(child, "diff-channels", vin, 2);
> +		/* diff-channels is optional */
> +		if (!ret) {
> +			differential = true;
> +			if (vin[0] != val || vin[1] >= adc_info->max_channels) {
> +				dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
> +					vin[0], vin[1]);
> +				goto err;
> +			}
> +		} else if (ret != -EINVAL) {
> +			dev_err(&indio_dev->dev, "Invalid diff-channels property %d\n", ret);
> +			goto err;
> +		}
> +
> +		stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val,
> +					vin[1], scan_index, differential);
> +		scan_index++;
> +	}
> +
> +	return scan_index;
> +
> +err:
> +	of_node_put(child);
> +
> +	return ret;
> +}
> +
>  static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
>  {
>  	struct device_node *node = indio_dev->dev.of_node;
> @@ -1800,15 +1872,21 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
>  	struct iio_chan_spec *channels;
>  	int scan_index = 0, num_channels = 0, ret, i;
>  	u32 smp = 0;
> +	bool legacy = false;
>  
> -	ret = stm32_adc_get_legacy_chan_count(indio_dev, adc);
> -	if (ret < 0)
> -		return ret;
> -	num_channels = ret;
> -
> +	num_channels = of_get_available_child_count(node);
> +	/*
> +	 * If no channels have been found, fallback to channels legacy properties.
> +	 * Legacy channel properties will be ignored, if some channels are

drop the ,

Legacy channel properties will be ignored if some channels...


> +	 * already defined using the standard binding.
> +	 */
>  	if (!num_channels) {
> -		dev_err(&indio_dev->dev, "No channels configured\n");
> -		return -ENODATA;
> +		ret = stm32_adc_get_legacy_chan_count(indio_dev, adc);
> +		if (ret < 0)
> +			return ret;
> +
> +		legacy = true;

Trivial but I would set legacy at top of this if block so it is nearer the comment.

> +		num_channels = ret;
>  	}
>  
>  	if (num_channels > adc_info->max_channels) {
> @@ -1832,7 +1910,10 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
>  	if (!channels)
>  		return -ENOMEM;
>  
> -	ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels);
> +	if (legacy)
> +		ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels);
> +	else
> +		ret = stm32_adc_generic_chan_init(indio_dev, adc, channels);
>  	if (ret < 0)
>  		return ret;
>  	scan_index = ret;


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 4/7] iio: adc: stm32-adc: add support of generic channels binding
@ 2021-09-11 16:08     ` Jonathan Cameron
  0 siblings, 0 replies; 53+ messages in thread
From: Jonathan Cameron @ 2021-09-11 16:08 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Lars-Peter Clausen, Maxime Coquelin, Rob Herring, alsa-devel,
	devicetree, linux-arm-kernel, linux-iio, linux-kernel,
	linux-stm32

On Wed, 8 Sep 2021 17:54:49 +0200
Olivier Moysan <olivier.moysan@foss.st.com> wrote:

> Add support of generic IIO channel binding:
> ./devicetree/bindings/iio/adc/adc.yaml
> Keep support of st,adc-channels and st,adc-diff-channels
> for backward compatibility.
> 
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>

Hi Olivier,

A few minor things inline.

Thanks,

Jonathan

> ---
>  drivers/iio/adc/stm32-adc.c | 99 +++++++++++++++++++++++++++++++++----
>  1 file changed, 90 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> index 2f137d14f141..ae4a314854f7 100644
> --- a/drivers/iio/adc/stm32-adc.c
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -35,7 +35,7 @@
>  #define STM32H7_BOOST_CLKRATE		20000000UL
>  
>  #define STM32_ADC_CH_MAX		20	/* max number of channels */
> -#define STM32_ADC_CH_SZ			10	/* max channel name size */
> +#define STM32_ADC_CH_SZ			16	/* max channel name size */
>  #define STM32_ADC_MAX_SQ		16	/* SQ1..SQ16 */
>  #define STM32_ADC_MAX_SMP		7	/* SMPx range is [0..7] */
>  #define STM32_ADC_TIMEOUT_US		100000
> @@ -1732,6 +1732,11 @@ static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm
>  		num_channels += ret;
>  	}
>  
> +	if (!num_channels) {
> +		dev_err(indio_dev->dev.parent, "No channel found\n");
> +		return -ENODATA;

I'd return 0 and handle this at the caller.   Both because it makes this
patch more readable and because a count of 0 isn't an error of the function
reading how many they are, but rather at a higher level.

> +	}
> +
>  	return num_channels;
>  }
>  
> @@ -1792,6 +1797,73 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
>  	return scan_index;
>  }
>  
> +static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
> +				       struct stm32_adc *adc,
> +				       struct iio_chan_spec *channels)
> +{
> +	struct device_node *node = indio_dev->dev.of_node;
> +	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
> +	struct device_node *child;
> +	const char *name;
> +	int val, scan_index = 0, ret;
> +	bool differential;
> +	u32 vin[2];
> +
> +	for_each_available_child_of_node(node, child) {
> +		ret = of_property_read_u32(child, "reg", &val);
> +		if (ret) {
> +			dev_err(&indio_dev->dev, "Missing channel index %d\n", ret);
> +			goto err;
> +		}
> +
> +		ret = of_property_read_string(child, "label", &name);
> +		/* label is optional */
> +		if (!ret) {
> +			if (strlen(name) >= STM32_ADC_CH_SZ) {
> +				dev_err(&indio_dev->dev, "Label %s exceeds %d characters\n",
> +					name, STM32_ADC_CH_SZ);
> +				return -EINVAL;
> +			}
> +			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
> +		} else if (ret != -EINVAL) {
> +			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
> +			goto err;
> +		}
> +
> +		if (val >= adc_info->max_channels) {
> +			dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
> +			ret = -EINVAL;
> +			goto err;
> +		}
> +
> +		differential = false;
> +		ret = of_property_read_u32_array(child, "diff-channels", vin, 2);
> +		/* diff-channels is optional */
> +		if (!ret) {
> +			differential = true;
> +			if (vin[0] != val || vin[1] >= adc_info->max_channels) {
> +				dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
> +					vin[0], vin[1]);
> +				goto err;
> +			}
> +		} else if (ret != -EINVAL) {
> +			dev_err(&indio_dev->dev, "Invalid diff-channels property %d\n", ret);
> +			goto err;
> +		}
> +
> +		stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val,
> +					vin[1], scan_index, differential);
> +		scan_index++;
> +	}
> +
> +	return scan_index;
> +
> +err:
> +	of_node_put(child);
> +
> +	return ret;
> +}
> +
>  static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
>  {
>  	struct device_node *node = indio_dev->dev.of_node;
> @@ -1800,15 +1872,21 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
>  	struct iio_chan_spec *channels;
>  	int scan_index = 0, num_channels = 0, ret, i;
>  	u32 smp = 0;
> +	bool legacy = false;
>  
> -	ret = stm32_adc_get_legacy_chan_count(indio_dev, adc);
> -	if (ret < 0)
> -		return ret;
> -	num_channels = ret;
> -
> +	num_channels = of_get_available_child_count(node);
> +	/*
> +	 * If no channels have been found, fallback to channels legacy properties.
> +	 * Legacy channel properties will be ignored, if some channels are

drop the ,

Legacy channel properties will be ignored if some channels...


> +	 * already defined using the standard binding.
> +	 */
>  	if (!num_channels) {
> -		dev_err(&indio_dev->dev, "No channels configured\n");
> -		return -ENODATA;
> +		ret = stm32_adc_get_legacy_chan_count(indio_dev, adc);
> +		if (ret < 0)
> +			return ret;
> +
> +		legacy = true;

Trivial but I would set legacy at top of this if block so it is nearer the comment.

> +		num_channels = ret;
>  	}
>  
>  	if (num_channels > adc_info->max_channels) {
> @@ -1832,7 +1910,10 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
>  	if (!channels)
>  		return -ENOMEM;
>  
> -	ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels);
> +	if (legacy)
> +		ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels);
> +	else
> +		ret = stm32_adc_generic_chan_init(indio_dev, adc, channels);
>  	if (ret < 0)
>  		return ret;
>  	scan_index = ret;


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 5/7] iio: adc: stm32-adc: add support of internal channels
  2021-09-08 15:54   ` Olivier Moysan
  (?)
@ 2021-09-11 16:17     ` Jonathan Cameron
  -1 siblings, 0 replies; 53+ messages in thread
From: Jonathan Cameron @ 2021-09-11 16:17 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Lars-Peter Clausen, Maxime Coquelin, Rob Herring, alsa-devel,
	devicetree, linux-arm-kernel, linux-iio, linux-kernel,
	linux-stm32

On Wed, 8 Sep 2021 17:54:50 +0200
Olivier Moysan <olivier.moysan@foss.st.com> wrote:

> Add support of ADC2 internal channels VDDCORE, VREFINT and VBAT.
> The reserved label name "vddcore", "vrefint" and "vbat" must
> be used in Device Tree channel node, to enable the corresponding
> internal channel.
> 
> Note: This patch does not provide support of internal channels
> for F4 and H7.
> 
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
One query inline about the fact that the description is very generic (allows
different register for each internal channel) but the code using it is not
quite so generic.  If we can make them both very generic it will be both
more logic to read and hopefully more extensible in the future.

J

> ---
>  drivers/iio/adc/stm32-adc-core.h |   8 ++
>  drivers/iio/adc/stm32-adc.c      | 123 ++++++++++++++++++++++++++++++-
>  2 files changed, 128 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
> index 2322809bfd2f..7c924f463f67 100644
> --- a/drivers/iio/adc/stm32-adc-core.h
> +++ b/drivers/iio/adc/stm32-adc-core.h
> @@ -102,6 +102,9 @@
>  #define STM32H7_ADC_CALFACT		0xC4
>  #define STM32H7_ADC_CALFACT2		0xC8
>  
> +/* STM32MP1 - ADC2 instance option register */
> +#define STM32MP1_ADC2_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)
> @@ -168,11 +171,16 @@ enum stm32h7_adc_dmngt {
>  #define STM32H7_EOC_MST			BIT(2)
>  
>  /* STM32H7_ADC_CCR - bit fields */
> +#define STM32H7_VBATEN			BIT(24)
> +#define STM32H7_VREFEN			BIT(22)
>  #define STM32H7_PRESC_SHIFT		18
>  #define STM32H7_PRESC_MASK		GENMASK(21, 18)
>  #define STM32H7_CKMODE_SHIFT		16
>  #define STM32H7_CKMODE_MASK		GENMASK(17, 16)
>  
> +/* STM32MP1_ADC2_OR - bit fields */
> +#define STM32MP1_VDDCOREEN		BIT(0)
> +
>  /**
>   * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
>   * @base:		control registers base cpu addr
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> index ae4a314854f7..ef3d2af98025 100644
> --- a/drivers/iio/adc/stm32-adc.c
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -41,6 +41,7 @@
>  #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_CHAN_NONE		-1
>  
>  #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
>  
> @@ -77,6 +78,29 @@ enum stm32_adc_extsel {
>  	STM32_EXT20,
>  };
>  
> +enum stm32_adc_int_ch {
> +	STM32_ADC_INT_CH_VDDCORE,
> +	STM32_ADC_INT_CH_VREFINT,
> +	STM32_ADC_INT_CH_VBAT,
> +	STM32_ADC_INT_CH_NB,
> +};
> +
> +/**
> + * struct stm32_adc_ic - ADC internal channels
> + * @name:	name of the internal channel
> + * @idx:	internal channel enum index
> + */
> +struct stm32_adc_ic {
> +	const char *name;
> +	u32 idx;
> +};
> +
> +static const struct stm32_adc_ic stm32_adc_ic[STM32_ADC_INT_CH_NB] = {
> +	{ "vddcore", STM32_ADC_INT_CH_VDDCORE },
> +	{ "vrefint", STM32_ADC_INT_CH_VREFINT },
> +	{ "vbat", STM32_ADC_INT_CH_VBAT },
> +};
> +
>  /**
>   * struct stm32_adc_trig_info - ADC trigger info
>   * @name:		name of the trigger, corresponding to its source
> @@ -126,6 +150,9 @@ struct stm32_adc_regs {
>   * @res:		resolution selection register & bitfield
>   * @smpr:		smpr1 & smpr2 registers offset array
>   * @smp_bits:		smpr1 & smpr2 index and bitfields
> + * @or_vdd:		option register & vddcore bitfield
> + * @ccr_vbat:		common register & vbat bitfield
> + * @ccr_vref:		common register & vrefint bitfield
>   */
>  struct stm32_adc_regspec {
>  	const u32 dr;
> @@ -139,6 +166,9 @@ struct stm32_adc_regspec {
>  	const struct stm32_adc_regs res;
>  	const u32 smpr[2];
>  	const struct stm32_adc_regs *smp_bits;
> +	const struct stm32_adc_regs or_vdd;
> +	const struct stm32_adc_regs ccr_vbat;
> +	const struct stm32_adc_regs ccr_vref;
>  };
>  
>  struct stm32_adc;
> @@ -195,6 +225,7 @@ struct stm32_adc_cfg {
>   * @cal:		optional calibration data on some devices
>   * @chan_name:		channel name array
>   * @num_diff:		number of differential channels
> + * @int_ch:		internal channel indexes array
>   */
>  struct stm32_adc {
>  	struct stm32_adc_common	*common;
> @@ -219,6 +250,7 @@ struct stm32_adc {
>  	struct stm32_adc_calib	cal;
>  	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
>  	u32			num_diff;
> +	int			int_ch[STM32_ADC_INT_CH_NB];
>  };
>  
>  struct stm32_adc_diff_channel {
> @@ -451,6 +483,24 @@ static const struct stm32_adc_regspec stm32h7_adc_regspec = {
>  	.smp_bits = stm32h7_smp_bits,
>  };
>  
> +static const struct stm32_adc_regspec stm32mp1_adc_regspec = {
> +	.dr = STM32H7_ADC_DR,
> +	.ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE },
> +	.ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE },
> +	.isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC },
> +	.isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR },
> +	.sqr = stm32h7_sq,
> +	.exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT },
> +	.extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
> +		    STM32H7_EXTSEL_SHIFT },
> +	.res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT },
> +	.smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 },
> +	.smp_bits = stm32h7_smp_bits,
> +	.or_vdd = { STM32MP1_ADC2_OR, STM32MP1_VDDCOREEN },
> +	.ccr_vbat = { STM32H7_ADC_CCR, STM32H7_VBATEN },
> +	.ccr_vref = { STM32H7_ADC_CCR, STM32H7_VREFEN },
> +};
> +
>  /*
>   * STM32 ADC registers access routines
>   * @adc: stm32 adc instance
> @@ -579,6 +629,61 @@ static int stm32_adc_hw_start(struct device *dev)
>  	return ret;
>  }
>  
> +static void stm32_adc_int_ch_enable(struct iio_dev *indio_dev)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	u32 i, val, bits = 0, offset = 0;
> +
> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> +		if (adc->int_ch[i] == STM32_ADC_CHAN_NONE)
> +			continue;
> +
> +		switch (i) {
> +		case STM32_ADC_INT_CH_VDDCORE:
> +			dev_dbg(&indio_dev->dev, "Enable VDDCore\n");
> +			stm32_adc_set_bits(adc, adc->cfg->regs->or_vdd.reg,
> +					   adc->cfg->regs->or_vdd.mask);
> +			break;
> +		case STM32_ADC_INT_CH_VREFINT:
> +			dev_dbg(&indio_dev->dev, "Enable VREFInt\n");
> +			offset = adc->cfg->regs->ccr_vref.reg;
> +			bits |= adc->cfg->regs->ccr_vref.mask;
> +			break;
> +		case STM32_ADC_INT_CH_VBAT:
> +			dev_dbg(&indio_dev->dev, "Enable VBAT\n");
> +			offset = adc->cfg->regs->ccr_vbat.reg;
> +			bits |= adc->cfg->regs->ccr_vbat.mask;
> +			break;
> +		}
> +	}
> +
> +	if (bits) {
> +		val = readl_relaxed(adc->common->base + offset);
> +		val |= bits;
> +		writel_relaxed(val, adc->common->base + offset);
> +	}
> +}
> +
> +static void stm32_adc_int_ch_disable(struct stm32_adc *adc)
> +{
> +	u32 val, bits, offset, i;
> +
> +	stm32_adc_clr_bits(adc, adc->cfg->regs->or_vdd.reg,
> +			   adc->cfg->regs->or_vdd.mask);
> +
> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> +		if (adc->int_ch[i] != STM32_ADC_CHAN_NONE) {

If this is true then I think no need to do the or_vdd clear either?

This code doesn't feel particularly generic given we are listing ccr_vbat
and ccr_vref separately.

How painful would it be to handle the two parts independently?
stm32_adc_clr_bits(adc, adc->cfg->regs->ccr_vbat.reg,
		  adc->cfg_regs->ccr_vbat.mask);
stm32_adc_clr_bits(adc, adc->cfg->regs->ccr_vref.reg,
		   adc->cfg_regs-ccr_vref.mask);


> +			offset = adc->cfg->regs->ccr_vref.reg;
> +			bits = adc->cfg->regs->ccr_vref.mask |
> +				adc->cfg->regs->ccr_vbat.mask;
> +			val = readl_relaxed(adc->common->base + offset);
> +			val &= ~bits;
> +			writel_relaxed(bits, adc->common->base + offset);
> +			break;
> +		}
> +	}
> +}
> +
>  /**
>   * stm32f4_adc_start_conv() - Start conversions for regular channels.
>   * @indio_dev: IIO device instance
> @@ -947,11 +1052,13 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev)
>  		goto pwr_dwn;
>  	calib = ret;
>  
> +	stm32_adc_int_ch_enable(indio_dev);
> +
>  	stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel);
>  
>  	ret = stm32h7_adc_enable(indio_dev);
>  	if (ret)
> -		goto pwr_dwn;
> +		goto ch_disable;
>  
>  	/* Either restore or read calibration result for future reference */
>  	if (calib)
> @@ -967,6 +1074,8 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev)
>  
>  disable:
>  	stm32h7_adc_disable(indio_dev);
> +ch_disable:
> +	stm32_adc_int_ch_disable(adc);
>  pwr_dwn:
>  	stm32h7_adc_enter_pwr_down(adc);
>  
> @@ -978,6 +1087,7 @@ static void stm32h7_adc_unprepare(struct iio_dev *indio_dev)
>  	struct stm32_adc *adc = iio_priv(indio_dev);
>  
>  	stm32h7_adc_disable(indio_dev);
> +	stm32_adc_int_ch_disable(adc);
>  	stm32h7_adc_enter_pwr_down(adc);
>  }
>  
> @@ -1805,10 +1915,13 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>  	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
>  	struct device_node *child;
>  	const char *name;
> -	int val, scan_index = 0, ret;
> +	int val, scan_index = 0, ret, i;
>  	bool differential;
>  	u32 vin[2];
>  
> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++)
> +		adc->int_ch[i] = STM32_ADC_CHAN_NONE;
> +
>  	for_each_available_child_of_node(node, child) {
>  		ret = of_property_read_u32(child, "reg", &val);
>  		if (ret) {
> @@ -1825,6 +1938,10 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>  				return -EINVAL;
>  			}
>  			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
> +			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> +				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
> +					adc->int_ch[i] = val;
> +			}
>  		} else if (ret != -EINVAL) {
>  			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
>  			goto err;
> @@ -2223,7 +2340,7 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = {
>  };
>  
>  static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
> -	.regs = &stm32h7_adc_regspec,
> +	.regs = &stm32mp1_adc_regspec,
>  	.adc_info = &stm32h7_adc_info,
>  	.trigs = stm32h7_adc_trigs,
>  	.has_vregready = true,


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 5/7] iio: adc: stm32-adc: add support of internal channels
@ 2021-09-11 16:17     ` Jonathan Cameron
  0 siblings, 0 replies; 53+ messages in thread
From: Jonathan Cameron @ 2021-09-11 16:17 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: devicetree, alsa-devel, Lars-Peter Clausen, linux-iio,
	Alexandre Torgue, linux-kernel, Rob Herring, Maxime Coquelin,
	Arnaud Pouliquen, Fabrice Gasnier, linux-stm32, linux-arm-kernel

On Wed, 8 Sep 2021 17:54:50 +0200
Olivier Moysan <olivier.moysan@foss.st.com> wrote:

> Add support of ADC2 internal channels VDDCORE, VREFINT and VBAT.
> The reserved label name "vddcore", "vrefint" and "vbat" must
> be used in Device Tree channel node, to enable the corresponding
> internal channel.
> 
> Note: This patch does not provide support of internal channels
> for F4 and H7.
> 
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
One query inline about the fact that the description is very generic (allows
different register for each internal channel) but the code using it is not
quite so generic.  If we can make them both very generic it will be both
more logic to read and hopefully more extensible in the future.

J

> ---
>  drivers/iio/adc/stm32-adc-core.h |   8 ++
>  drivers/iio/adc/stm32-adc.c      | 123 ++++++++++++++++++++++++++++++-
>  2 files changed, 128 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
> index 2322809bfd2f..7c924f463f67 100644
> --- a/drivers/iio/adc/stm32-adc-core.h
> +++ b/drivers/iio/adc/stm32-adc-core.h
> @@ -102,6 +102,9 @@
>  #define STM32H7_ADC_CALFACT		0xC4
>  #define STM32H7_ADC_CALFACT2		0xC8
>  
> +/* STM32MP1 - ADC2 instance option register */
> +#define STM32MP1_ADC2_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)
> @@ -168,11 +171,16 @@ enum stm32h7_adc_dmngt {
>  #define STM32H7_EOC_MST			BIT(2)
>  
>  /* STM32H7_ADC_CCR - bit fields */
> +#define STM32H7_VBATEN			BIT(24)
> +#define STM32H7_VREFEN			BIT(22)
>  #define STM32H7_PRESC_SHIFT		18
>  #define STM32H7_PRESC_MASK		GENMASK(21, 18)
>  #define STM32H7_CKMODE_SHIFT		16
>  #define STM32H7_CKMODE_MASK		GENMASK(17, 16)
>  
> +/* STM32MP1_ADC2_OR - bit fields */
> +#define STM32MP1_VDDCOREEN		BIT(0)
> +
>  /**
>   * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
>   * @base:		control registers base cpu addr
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> index ae4a314854f7..ef3d2af98025 100644
> --- a/drivers/iio/adc/stm32-adc.c
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -41,6 +41,7 @@
>  #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_CHAN_NONE		-1
>  
>  #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
>  
> @@ -77,6 +78,29 @@ enum stm32_adc_extsel {
>  	STM32_EXT20,
>  };
>  
> +enum stm32_adc_int_ch {
> +	STM32_ADC_INT_CH_VDDCORE,
> +	STM32_ADC_INT_CH_VREFINT,
> +	STM32_ADC_INT_CH_VBAT,
> +	STM32_ADC_INT_CH_NB,
> +};
> +
> +/**
> + * struct stm32_adc_ic - ADC internal channels
> + * @name:	name of the internal channel
> + * @idx:	internal channel enum index
> + */
> +struct stm32_adc_ic {
> +	const char *name;
> +	u32 idx;
> +};
> +
> +static const struct stm32_adc_ic stm32_adc_ic[STM32_ADC_INT_CH_NB] = {
> +	{ "vddcore", STM32_ADC_INT_CH_VDDCORE },
> +	{ "vrefint", STM32_ADC_INT_CH_VREFINT },
> +	{ "vbat", STM32_ADC_INT_CH_VBAT },
> +};
> +
>  /**
>   * struct stm32_adc_trig_info - ADC trigger info
>   * @name:		name of the trigger, corresponding to its source
> @@ -126,6 +150,9 @@ struct stm32_adc_regs {
>   * @res:		resolution selection register & bitfield
>   * @smpr:		smpr1 & smpr2 registers offset array
>   * @smp_bits:		smpr1 & smpr2 index and bitfields
> + * @or_vdd:		option register & vddcore bitfield
> + * @ccr_vbat:		common register & vbat bitfield
> + * @ccr_vref:		common register & vrefint bitfield
>   */
>  struct stm32_adc_regspec {
>  	const u32 dr;
> @@ -139,6 +166,9 @@ struct stm32_adc_regspec {
>  	const struct stm32_adc_regs res;
>  	const u32 smpr[2];
>  	const struct stm32_adc_regs *smp_bits;
> +	const struct stm32_adc_regs or_vdd;
> +	const struct stm32_adc_regs ccr_vbat;
> +	const struct stm32_adc_regs ccr_vref;
>  };
>  
>  struct stm32_adc;
> @@ -195,6 +225,7 @@ struct stm32_adc_cfg {
>   * @cal:		optional calibration data on some devices
>   * @chan_name:		channel name array
>   * @num_diff:		number of differential channels
> + * @int_ch:		internal channel indexes array
>   */
>  struct stm32_adc {
>  	struct stm32_adc_common	*common;
> @@ -219,6 +250,7 @@ struct stm32_adc {
>  	struct stm32_adc_calib	cal;
>  	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
>  	u32			num_diff;
> +	int			int_ch[STM32_ADC_INT_CH_NB];
>  };
>  
>  struct stm32_adc_diff_channel {
> @@ -451,6 +483,24 @@ static const struct stm32_adc_regspec stm32h7_adc_regspec = {
>  	.smp_bits = stm32h7_smp_bits,
>  };
>  
> +static const struct stm32_adc_regspec stm32mp1_adc_regspec = {
> +	.dr = STM32H7_ADC_DR,
> +	.ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE },
> +	.ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE },
> +	.isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC },
> +	.isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR },
> +	.sqr = stm32h7_sq,
> +	.exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT },
> +	.extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
> +		    STM32H7_EXTSEL_SHIFT },
> +	.res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT },
> +	.smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 },
> +	.smp_bits = stm32h7_smp_bits,
> +	.or_vdd = { STM32MP1_ADC2_OR, STM32MP1_VDDCOREEN },
> +	.ccr_vbat = { STM32H7_ADC_CCR, STM32H7_VBATEN },
> +	.ccr_vref = { STM32H7_ADC_CCR, STM32H7_VREFEN },
> +};
> +
>  /*
>   * STM32 ADC registers access routines
>   * @adc: stm32 adc instance
> @@ -579,6 +629,61 @@ static int stm32_adc_hw_start(struct device *dev)
>  	return ret;
>  }
>  
> +static void stm32_adc_int_ch_enable(struct iio_dev *indio_dev)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	u32 i, val, bits = 0, offset = 0;
> +
> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> +		if (adc->int_ch[i] == STM32_ADC_CHAN_NONE)
> +			continue;
> +
> +		switch (i) {
> +		case STM32_ADC_INT_CH_VDDCORE:
> +			dev_dbg(&indio_dev->dev, "Enable VDDCore\n");
> +			stm32_adc_set_bits(adc, adc->cfg->regs->or_vdd.reg,
> +					   adc->cfg->regs->or_vdd.mask);
> +			break;
> +		case STM32_ADC_INT_CH_VREFINT:
> +			dev_dbg(&indio_dev->dev, "Enable VREFInt\n");
> +			offset = adc->cfg->regs->ccr_vref.reg;
> +			bits |= adc->cfg->regs->ccr_vref.mask;
> +			break;
> +		case STM32_ADC_INT_CH_VBAT:
> +			dev_dbg(&indio_dev->dev, "Enable VBAT\n");
> +			offset = adc->cfg->regs->ccr_vbat.reg;
> +			bits |= adc->cfg->regs->ccr_vbat.mask;
> +			break;
> +		}
> +	}
> +
> +	if (bits) {
> +		val = readl_relaxed(adc->common->base + offset);
> +		val |= bits;
> +		writel_relaxed(val, adc->common->base + offset);
> +	}
> +}
> +
> +static void stm32_adc_int_ch_disable(struct stm32_adc *adc)
> +{
> +	u32 val, bits, offset, i;
> +
> +	stm32_adc_clr_bits(adc, adc->cfg->regs->or_vdd.reg,
> +			   adc->cfg->regs->or_vdd.mask);
> +
> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> +		if (adc->int_ch[i] != STM32_ADC_CHAN_NONE) {

If this is true then I think no need to do the or_vdd clear either?

This code doesn't feel particularly generic given we are listing ccr_vbat
and ccr_vref separately.

How painful would it be to handle the two parts independently?
stm32_adc_clr_bits(adc, adc->cfg->regs->ccr_vbat.reg,
		  adc->cfg_regs->ccr_vbat.mask);
stm32_adc_clr_bits(adc, adc->cfg->regs->ccr_vref.reg,
		   adc->cfg_regs-ccr_vref.mask);


> +			offset = adc->cfg->regs->ccr_vref.reg;
> +			bits = adc->cfg->regs->ccr_vref.mask |
> +				adc->cfg->regs->ccr_vbat.mask;
> +			val = readl_relaxed(adc->common->base + offset);
> +			val &= ~bits;
> +			writel_relaxed(bits, adc->common->base + offset);
> +			break;
> +		}
> +	}
> +}
> +
>  /**
>   * stm32f4_adc_start_conv() - Start conversions for regular channels.
>   * @indio_dev: IIO device instance
> @@ -947,11 +1052,13 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev)
>  		goto pwr_dwn;
>  	calib = ret;
>  
> +	stm32_adc_int_ch_enable(indio_dev);
> +
>  	stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel);
>  
>  	ret = stm32h7_adc_enable(indio_dev);
>  	if (ret)
> -		goto pwr_dwn;
> +		goto ch_disable;
>  
>  	/* Either restore or read calibration result for future reference */
>  	if (calib)
> @@ -967,6 +1074,8 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev)
>  
>  disable:
>  	stm32h7_adc_disable(indio_dev);
> +ch_disable:
> +	stm32_adc_int_ch_disable(adc);
>  pwr_dwn:
>  	stm32h7_adc_enter_pwr_down(adc);
>  
> @@ -978,6 +1087,7 @@ static void stm32h7_adc_unprepare(struct iio_dev *indio_dev)
>  	struct stm32_adc *adc = iio_priv(indio_dev);
>  
>  	stm32h7_adc_disable(indio_dev);
> +	stm32_adc_int_ch_disable(adc);
>  	stm32h7_adc_enter_pwr_down(adc);
>  }
>  
> @@ -1805,10 +1915,13 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>  	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
>  	struct device_node *child;
>  	const char *name;
> -	int val, scan_index = 0, ret;
> +	int val, scan_index = 0, ret, i;
>  	bool differential;
>  	u32 vin[2];
>  
> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++)
> +		adc->int_ch[i] = STM32_ADC_CHAN_NONE;
> +
>  	for_each_available_child_of_node(node, child) {
>  		ret = of_property_read_u32(child, "reg", &val);
>  		if (ret) {
> @@ -1825,6 +1938,10 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>  				return -EINVAL;
>  			}
>  			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
> +			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> +				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
> +					adc->int_ch[i] = val;
> +			}
>  		} else if (ret != -EINVAL) {
>  			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
>  			goto err;
> @@ -2223,7 +2340,7 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = {
>  };
>  
>  static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
> -	.regs = &stm32h7_adc_regspec,
> +	.regs = &stm32mp1_adc_regspec,
>  	.adc_info = &stm32h7_adc_info,
>  	.trigs = stm32h7_adc_trigs,
>  	.has_vregready = true,


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 5/7] iio: adc: stm32-adc: add support of internal channels
@ 2021-09-11 16:17     ` Jonathan Cameron
  0 siblings, 0 replies; 53+ messages in thread
From: Jonathan Cameron @ 2021-09-11 16:17 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Lars-Peter Clausen, Maxime Coquelin, Rob Herring, alsa-devel,
	devicetree, linux-arm-kernel, linux-iio, linux-kernel,
	linux-stm32

On Wed, 8 Sep 2021 17:54:50 +0200
Olivier Moysan <olivier.moysan@foss.st.com> wrote:

> Add support of ADC2 internal channels VDDCORE, VREFINT and VBAT.
> The reserved label name "vddcore", "vrefint" and "vbat" must
> be used in Device Tree channel node, to enable the corresponding
> internal channel.
> 
> Note: This patch does not provide support of internal channels
> for F4 and H7.
> 
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
One query inline about the fact that the description is very generic (allows
different register for each internal channel) but the code using it is not
quite so generic.  If we can make them both very generic it will be both
more logic to read and hopefully more extensible in the future.

J

> ---
>  drivers/iio/adc/stm32-adc-core.h |   8 ++
>  drivers/iio/adc/stm32-adc.c      | 123 ++++++++++++++++++++++++++++++-
>  2 files changed, 128 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
> index 2322809bfd2f..7c924f463f67 100644
> --- a/drivers/iio/adc/stm32-adc-core.h
> +++ b/drivers/iio/adc/stm32-adc-core.h
> @@ -102,6 +102,9 @@
>  #define STM32H7_ADC_CALFACT		0xC4
>  #define STM32H7_ADC_CALFACT2		0xC8
>  
> +/* STM32MP1 - ADC2 instance option register */
> +#define STM32MP1_ADC2_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)
> @@ -168,11 +171,16 @@ enum stm32h7_adc_dmngt {
>  #define STM32H7_EOC_MST			BIT(2)
>  
>  /* STM32H7_ADC_CCR - bit fields */
> +#define STM32H7_VBATEN			BIT(24)
> +#define STM32H7_VREFEN			BIT(22)
>  #define STM32H7_PRESC_SHIFT		18
>  #define STM32H7_PRESC_MASK		GENMASK(21, 18)
>  #define STM32H7_CKMODE_SHIFT		16
>  #define STM32H7_CKMODE_MASK		GENMASK(17, 16)
>  
> +/* STM32MP1_ADC2_OR - bit fields */
> +#define STM32MP1_VDDCOREEN		BIT(0)
> +
>  /**
>   * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
>   * @base:		control registers base cpu addr
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> index ae4a314854f7..ef3d2af98025 100644
> --- a/drivers/iio/adc/stm32-adc.c
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -41,6 +41,7 @@
>  #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_CHAN_NONE		-1
>  
>  #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
>  
> @@ -77,6 +78,29 @@ enum stm32_adc_extsel {
>  	STM32_EXT20,
>  };
>  
> +enum stm32_adc_int_ch {
> +	STM32_ADC_INT_CH_VDDCORE,
> +	STM32_ADC_INT_CH_VREFINT,
> +	STM32_ADC_INT_CH_VBAT,
> +	STM32_ADC_INT_CH_NB,
> +};
> +
> +/**
> + * struct stm32_adc_ic - ADC internal channels
> + * @name:	name of the internal channel
> + * @idx:	internal channel enum index
> + */
> +struct stm32_adc_ic {
> +	const char *name;
> +	u32 idx;
> +};
> +
> +static const struct stm32_adc_ic stm32_adc_ic[STM32_ADC_INT_CH_NB] = {
> +	{ "vddcore", STM32_ADC_INT_CH_VDDCORE },
> +	{ "vrefint", STM32_ADC_INT_CH_VREFINT },
> +	{ "vbat", STM32_ADC_INT_CH_VBAT },
> +};
> +
>  /**
>   * struct stm32_adc_trig_info - ADC trigger info
>   * @name:		name of the trigger, corresponding to its source
> @@ -126,6 +150,9 @@ struct stm32_adc_regs {
>   * @res:		resolution selection register & bitfield
>   * @smpr:		smpr1 & smpr2 registers offset array
>   * @smp_bits:		smpr1 & smpr2 index and bitfields
> + * @or_vdd:		option register & vddcore bitfield
> + * @ccr_vbat:		common register & vbat bitfield
> + * @ccr_vref:		common register & vrefint bitfield
>   */
>  struct stm32_adc_regspec {
>  	const u32 dr;
> @@ -139,6 +166,9 @@ struct stm32_adc_regspec {
>  	const struct stm32_adc_regs res;
>  	const u32 smpr[2];
>  	const struct stm32_adc_regs *smp_bits;
> +	const struct stm32_adc_regs or_vdd;
> +	const struct stm32_adc_regs ccr_vbat;
> +	const struct stm32_adc_regs ccr_vref;
>  };
>  
>  struct stm32_adc;
> @@ -195,6 +225,7 @@ struct stm32_adc_cfg {
>   * @cal:		optional calibration data on some devices
>   * @chan_name:		channel name array
>   * @num_diff:		number of differential channels
> + * @int_ch:		internal channel indexes array
>   */
>  struct stm32_adc {
>  	struct stm32_adc_common	*common;
> @@ -219,6 +250,7 @@ struct stm32_adc {
>  	struct stm32_adc_calib	cal;
>  	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
>  	u32			num_diff;
> +	int			int_ch[STM32_ADC_INT_CH_NB];
>  };
>  
>  struct stm32_adc_diff_channel {
> @@ -451,6 +483,24 @@ static const struct stm32_adc_regspec stm32h7_adc_regspec = {
>  	.smp_bits = stm32h7_smp_bits,
>  };
>  
> +static const struct stm32_adc_regspec stm32mp1_adc_regspec = {
> +	.dr = STM32H7_ADC_DR,
> +	.ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE },
> +	.ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE },
> +	.isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC },
> +	.isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR },
> +	.sqr = stm32h7_sq,
> +	.exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT },
> +	.extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
> +		    STM32H7_EXTSEL_SHIFT },
> +	.res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT },
> +	.smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 },
> +	.smp_bits = stm32h7_smp_bits,
> +	.or_vdd = { STM32MP1_ADC2_OR, STM32MP1_VDDCOREEN },
> +	.ccr_vbat = { STM32H7_ADC_CCR, STM32H7_VBATEN },
> +	.ccr_vref = { STM32H7_ADC_CCR, STM32H7_VREFEN },
> +};
> +
>  /*
>   * STM32 ADC registers access routines
>   * @adc: stm32 adc instance
> @@ -579,6 +629,61 @@ static int stm32_adc_hw_start(struct device *dev)
>  	return ret;
>  }
>  
> +static void stm32_adc_int_ch_enable(struct iio_dev *indio_dev)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	u32 i, val, bits = 0, offset = 0;
> +
> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> +		if (adc->int_ch[i] == STM32_ADC_CHAN_NONE)
> +			continue;
> +
> +		switch (i) {
> +		case STM32_ADC_INT_CH_VDDCORE:
> +			dev_dbg(&indio_dev->dev, "Enable VDDCore\n");
> +			stm32_adc_set_bits(adc, adc->cfg->regs->or_vdd.reg,
> +					   adc->cfg->regs->or_vdd.mask);
> +			break;
> +		case STM32_ADC_INT_CH_VREFINT:
> +			dev_dbg(&indio_dev->dev, "Enable VREFInt\n");
> +			offset = adc->cfg->regs->ccr_vref.reg;
> +			bits |= adc->cfg->regs->ccr_vref.mask;
> +			break;
> +		case STM32_ADC_INT_CH_VBAT:
> +			dev_dbg(&indio_dev->dev, "Enable VBAT\n");
> +			offset = adc->cfg->regs->ccr_vbat.reg;
> +			bits |= adc->cfg->regs->ccr_vbat.mask;
> +			break;
> +		}
> +	}
> +
> +	if (bits) {
> +		val = readl_relaxed(adc->common->base + offset);
> +		val |= bits;
> +		writel_relaxed(val, adc->common->base + offset);
> +	}
> +}
> +
> +static void stm32_adc_int_ch_disable(struct stm32_adc *adc)
> +{
> +	u32 val, bits, offset, i;
> +
> +	stm32_adc_clr_bits(adc, adc->cfg->regs->or_vdd.reg,
> +			   adc->cfg->regs->or_vdd.mask);
> +
> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> +		if (adc->int_ch[i] != STM32_ADC_CHAN_NONE) {

If this is true then I think no need to do the or_vdd clear either?

This code doesn't feel particularly generic given we are listing ccr_vbat
and ccr_vref separately.

How painful would it be to handle the two parts independently?
stm32_adc_clr_bits(adc, adc->cfg->regs->ccr_vbat.reg,
		  adc->cfg_regs->ccr_vbat.mask);
stm32_adc_clr_bits(adc, adc->cfg->regs->ccr_vref.reg,
		   adc->cfg_regs-ccr_vref.mask);


> +			offset = adc->cfg->regs->ccr_vref.reg;
> +			bits = adc->cfg->regs->ccr_vref.mask |
> +				adc->cfg->regs->ccr_vbat.mask;
> +			val = readl_relaxed(adc->common->base + offset);
> +			val &= ~bits;
> +			writel_relaxed(bits, adc->common->base + offset);
> +			break;
> +		}
> +	}
> +}
> +
>  /**
>   * stm32f4_adc_start_conv() - Start conversions for regular channels.
>   * @indio_dev: IIO device instance
> @@ -947,11 +1052,13 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev)
>  		goto pwr_dwn;
>  	calib = ret;
>  
> +	stm32_adc_int_ch_enable(indio_dev);
> +
>  	stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel);
>  
>  	ret = stm32h7_adc_enable(indio_dev);
>  	if (ret)
> -		goto pwr_dwn;
> +		goto ch_disable;
>  
>  	/* Either restore or read calibration result for future reference */
>  	if (calib)
> @@ -967,6 +1074,8 @@ static int stm32h7_adc_prepare(struct iio_dev *indio_dev)
>  
>  disable:
>  	stm32h7_adc_disable(indio_dev);
> +ch_disable:
> +	stm32_adc_int_ch_disable(adc);
>  pwr_dwn:
>  	stm32h7_adc_enter_pwr_down(adc);
>  
> @@ -978,6 +1087,7 @@ static void stm32h7_adc_unprepare(struct iio_dev *indio_dev)
>  	struct stm32_adc *adc = iio_priv(indio_dev);
>  
>  	stm32h7_adc_disable(indio_dev);
> +	stm32_adc_int_ch_disable(adc);
>  	stm32h7_adc_enter_pwr_down(adc);
>  }
>  
> @@ -1805,10 +1915,13 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>  	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
>  	struct device_node *child;
>  	const char *name;
> -	int val, scan_index = 0, ret;
> +	int val, scan_index = 0, ret, i;
>  	bool differential;
>  	u32 vin[2];
>  
> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++)
> +		adc->int_ch[i] = STM32_ADC_CHAN_NONE;
> +
>  	for_each_available_child_of_node(node, child) {
>  		ret = of_property_read_u32(child, "reg", &val);
>  		if (ret) {
> @@ -1825,6 +1938,10 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>  				return -EINVAL;
>  			}
>  			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
> +			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> +				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
> +					adc->int_ch[i] = val;
> +			}
>  		} else if (ret != -EINVAL) {
>  			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
>  			goto err;
> @@ -2223,7 +2340,7 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = {
>  };
>  
>  static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
> -	.regs = &stm32h7_adc_regspec,
> +	.regs = &stm32mp1_adc_regspec,
>  	.adc_info = &stm32h7_adc_info,
>  	.trigs = stm32h7_adc_trigs,
>  	.has_vregready = true,


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 6/7] iio: adc: stm32-adc: add vrefint calibration support
  2021-09-08 15:54   ` Olivier Moysan
  (?)
@ 2021-09-11 16:28     ` Jonathan Cameron
  -1 siblings, 0 replies; 53+ messages in thread
From: Jonathan Cameron @ 2021-09-11 16:28 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Lars-Peter Clausen, Maxime Coquelin, Rob Herring, alsa-devel,
	devicetree, linux-arm-kernel, linux-iio, linux-kernel,
	linux-stm32

On Wed, 8 Sep 2021 17:54:51 +0200
Olivier Moysan <olivier.moysan@foss.st.com> wrote:

> Add support of vrefint calibration.
> If a channel is labeled as vrefint, get vrefint calibration
> from non volatile memory for this channel.
> A conversion on vrefint channel allows to update scale
> factor according to vrefint deviation, compared to vrefint
> calibration value.

As I mention inline, whilst technically the ABI doesn't demand it
the expectation of much of userspace software is that _scale is
pseudo constant - that is it doesn't tend to change very often and when
it does it's normally because someone deliberately made it change.
As such most software reads it just once.

Normally we work around this by applying the maths in kernel and
not exposing the scale at all. Is this something that could be done here?

Jonathan

> 
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
> ---
>  drivers/iio/adc/stm32-adc.c | 88 ++++++++++++++++++++++++++++++++++---
>  1 file changed, 82 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> index ef3d2af98025..9e52a7de9b16 100644
> --- a/drivers/iio/adc/stm32-adc.c
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -21,6 +21,7 @@
>  #include <linux/io.h>
>  #include <linux/iopoll.h>
>  #include <linux/module.h>
> +#include <linux/nvmem-consumer.h>
>  #include <linux/platform_device.h>
>  #include <linux/pm_runtime.h>
>  #include <linux/of.h>
> @@ -42,6 +43,7 @@
>  #define STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
>  #define STM32_ADC_HW_STOP_DELAY_MS	100
>  #define STM32_ADC_CHAN_NONE		-1
> +#define STM32_ADC_VREFINT_VOLTAGE	3300
>  
>  #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
>  
> @@ -79,6 +81,7 @@ enum stm32_adc_extsel {
>  };
>  
>  enum stm32_adc_int_ch {
> +	STM32_ADC_INT_CH_NONE = -1,
>  	STM32_ADC_INT_CH_VDDCORE,
>  	STM32_ADC_INT_CH_VREFINT,
>  	STM32_ADC_INT_CH_VBAT,
> @@ -137,6 +140,16 @@ struct stm32_adc_regs {
>  	int shift;
>  };
>  
> +/**
> + * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data
> + * @vrefint_cal:	vrefint calibration value from nvmem
> + * @vrefint_data:	vrefint actual value
> + */
> +struct stm32_adc_vrefint {
> +	u32 vrefint_cal;
> +	u32 vrefint_data;
> +};
> +
>  /**
>   * struct stm32_adc_regspec - stm32 registers definition
>   * @dr:			data register offset
> @@ -186,6 +199,7 @@ struct stm32_adc;
>   * @unprepare:		optional unprepare routine (disable, power-down)
>   * @irq_clear:		routine to clear irqs
>   * @smp_cycles:		programmable sampling time (ADC clock cycles)
> + * @ts_vrefint_ns:	vrefint minimum sampling time in ns
>   */
>  struct stm32_adc_cfg {
>  	const struct stm32_adc_regspec	*regs;
> @@ -199,6 +213,7 @@ struct stm32_adc_cfg {
>  	void (*unprepare)(struct iio_dev *);
>  	void (*irq_clear)(struct iio_dev *indio_dev, u32 msk);
>  	const unsigned int *smp_cycles;
> +	const unsigned int ts_vrefint_ns;
>  };
>  
>  /**
> @@ -223,6 +238,7 @@ struct stm32_adc_cfg {
>   * @pcsel:		bitmask to preselect channels on some devices
>   * @smpr_val:		sampling time settings (e.g. smpr1 / smpr2)
>   * @cal:		optional calibration data on some devices
> + * @vrefint:		internal reference voltage data
>   * @chan_name:		channel name array
>   * @num_diff:		number of differential channels
>   * @int_ch:		internal channel indexes array
> @@ -248,6 +264,7 @@ struct stm32_adc {
>  	u32			pcsel;
>  	u32			smpr_val[2];
>  	struct stm32_adc_calib	cal;
> +	struct stm32_adc_vrefint vrefint;
>  	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
>  	u32			num_diff;
>  	int			int_ch[STM32_ADC_INT_CH_NB];
> @@ -1331,15 +1348,35 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
>  			ret = stm32_adc_single_conv(indio_dev, chan, val);
>  		else
>  			ret = -EINVAL;
> +
> +		/* If channel mask corresponds to vrefint, store data */
> +		if (adc->int_ch[STM32_ADC_INT_CH_VREFINT] == chan->channel)
> +			adc->vrefint.vrefint_data = *val;
> +
>  		iio_device_release_direct_mode(indio_dev);
>  		return ret;
>  
>  	case IIO_CHAN_INFO_SCALE:
>  		if (chan->differential) {
> -			*val = adc->common->vref_mv * 2;
> +			if (adc->vrefint.vrefint_data &&
> +			    adc->vrefint.vrefint_cal) {
> +				*val = STM32_ADC_VREFINT_VOLTAGE * 2 *
> +				       adc->vrefint.vrefint_cal /
> +				       adc->vrefint.vrefint_data;

Ah.. Dynamic scale.  This is always awkward when it occurs.
Given most / possibly all userspace software assumes a pseudo static scale
(not data dependent) we normally hide this by doing the maths internal to the
driver - sometimes meaning we need to present the particular channel as processed
not raw.

Is the expectation here that vrefint_data is actually very nearly constant? If
so then what you have here may be fine as anyone not aware the scale might change
will get very nearly the right value anyway.

> +			} else {
> +				*val = adc->common->vref_mv * 2;
> +			}
>  			*val2 = chan->scan_type.realbits;
>  		} else {
> -			*val = adc->common->vref_mv;
> +			/* Use vrefint data if available */
> +			if (adc->vrefint.vrefint_data &&
> +			    adc->vrefint.vrefint_cal) {
> +				*val = STM32_ADC_VREFINT_VOLTAGE *
> +				       adc->vrefint.vrefint_cal /
> +				       adc->vrefint.vrefint_data;
> +			} else {
> +				*val = adc->common->vref_mv;
> +			}
>  			*val2 = chan->scan_type.realbits;
>  		}
>  		return IIO_VAL_FRACTIONAL_LOG2;
> @@ -1907,6 +1944,35 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
>  	return scan_index;
>  }
>  
> +static int stm32_adc_get_int_ch(struct iio_dev *indio_dev, const char *ch_name,
> +				int chan)

Naming would suggest to me that it would return a channel rather than setting it
inside adc->int_ch[i]  Perhaps something like st32_adc_populate_int_ch() ?


> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	u16 vrefint;
> +	int i, ret;
> +
> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> +		if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
> +			adc->int_ch[i] = chan;
> +			/* If channel is vrefint get calibration data. */
> +			if (stm32_adc_ic[i].idx == STM32_ADC_INT_CH_VREFINT) {

I would reduce indentation by reversing the logic.

			if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT)
				continue;

			ret = 
> +				ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint);
> +				if (ret && ret != -ENOENT && ret != -EOPNOTSUPP) {
> +					dev_err(&indio_dev->dev, "nvmem access error %d\n", ret);
> +					return ret;
> +				}
> +				if (ret == -ENOENT)
> +					dev_dbg(&indio_dev->dev,
> +						"vrefint calibration not found\n");
> +				else
> +					adc->vrefint.vrefint_cal = vrefint;
> +			}
> +		}
> +	}
> +
> +	return 0;
> +}
> +
>  static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>  				       struct stm32_adc *adc,
>  				       struct iio_chan_spec *channels)
> @@ -1938,10 +2004,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>  				return -EINVAL;
>  			}
>  			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
> -			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> -				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
> -					adc->int_ch[i] = val;
> -			}
> +			ret = stm32_adc_get_int_ch(indio_dev, name, val);
> +			if (ret)
> +				goto err;
>  		} else if (ret != -EINVAL) {
>  			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
>  			goto err;
> @@ -2044,6 +2109,16 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
>  		 */
>  		of_property_read_u32_index(node, "st,min-sample-time-nsecs",
>  					   i, &smp);
> +
> +		/*
> +		 * For vrefint channel, ensure that the sampling time cannot
> +		 * be lower than the one specified in the datasheet
> +		 */
> +		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
> +		    smp < adc->cfg->ts_vrefint_ns) {
> +			smp = adc->cfg->ts_vrefint_ns;
> +		}

		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT])
			smp = max(smp, adc->cfg->ts_vrefint_ns);

> +
>  		/* Prepare sampling time settings */
>  		stm32_adc_smpr_init(adc, channels[i].channel, smp);
>  	}
> @@ -2350,6 +2425,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
>  	.unprepare = stm32h7_adc_unprepare,
>  	.smp_cycles = stm32h7_adc_smp_cycles,
>  	.irq_clear = stm32h7_adc_irq_clear,
> +	.ts_vrefint_ns = 4300,
>  };
>  
>  static const struct of_device_id stm32_adc_of_match[] = {


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 6/7] iio: adc: stm32-adc: add vrefint calibration support
@ 2021-09-11 16:28     ` Jonathan Cameron
  0 siblings, 0 replies; 53+ messages in thread
From: Jonathan Cameron @ 2021-09-11 16:28 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: devicetree, alsa-devel, Lars-Peter Clausen, linux-iio,
	Alexandre Torgue, linux-kernel, Rob Herring, Maxime Coquelin,
	Arnaud Pouliquen, Fabrice Gasnier, linux-stm32, linux-arm-kernel

On Wed, 8 Sep 2021 17:54:51 +0200
Olivier Moysan <olivier.moysan@foss.st.com> wrote:

> Add support of vrefint calibration.
> If a channel is labeled as vrefint, get vrefint calibration
> from non volatile memory for this channel.
> A conversion on vrefint channel allows to update scale
> factor according to vrefint deviation, compared to vrefint
> calibration value.

As I mention inline, whilst technically the ABI doesn't demand it
the expectation of much of userspace software is that _scale is
pseudo constant - that is it doesn't tend to change very often and when
it does it's normally because someone deliberately made it change.
As such most software reads it just once.

Normally we work around this by applying the maths in kernel and
not exposing the scale at all. Is this something that could be done here?

Jonathan

> 
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
> ---
>  drivers/iio/adc/stm32-adc.c | 88 ++++++++++++++++++++++++++++++++++---
>  1 file changed, 82 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> index ef3d2af98025..9e52a7de9b16 100644
> --- a/drivers/iio/adc/stm32-adc.c
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -21,6 +21,7 @@
>  #include <linux/io.h>
>  #include <linux/iopoll.h>
>  #include <linux/module.h>
> +#include <linux/nvmem-consumer.h>
>  #include <linux/platform_device.h>
>  #include <linux/pm_runtime.h>
>  #include <linux/of.h>
> @@ -42,6 +43,7 @@
>  #define STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
>  #define STM32_ADC_HW_STOP_DELAY_MS	100
>  #define STM32_ADC_CHAN_NONE		-1
> +#define STM32_ADC_VREFINT_VOLTAGE	3300
>  
>  #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
>  
> @@ -79,6 +81,7 @@ enum stm32_adc_extsel {
>  };
>  
>  enum stm32_adc_int_ch {
> +	STM32_ADC_INT_CH_NONE = -1,
>  	STM32_ADC_INT_CH_VDDCORE,
>  	STM32_ADC_INT_CH_VREFINT,
>  	STM32_ADC_INT_CH_VBAT,
> @@ -137,6 +140,16 @@ struct stm32_adc_regs {
>  	int shift;
>  };
>  
> +/**
> + * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data
> + * @vrefint_cal:	vrefint calibration value from nvmem
> + * @vrefint_data:	vrefint actual value
> + */
> +struct stm32_adc_vrefint {
> +	u32 vrefint_cal;
> +	u32 vrefint_data;
> +};
> +
>  /**
>   * struct stm32_adc_regspec - stm32 registers definition
>   * @dr:			data register offset
> @@ -186,6 +199,7 @@ struct stm32_adc;
>   * @unprepare:		optional unprepare routine (disable, power-down)
>   * @irq_clear:		routine to clear irqs
>   * @smp_cycles:		programmable sampling time (ADC clock cycles)
> + * @ts_vrefint_ns:	vrefint minimum sampling time in ns
>   */
>  struct stm32_adc_cfg {
>  	const struct stm32_adc_regspec	*regs;
> @@ -199,6 +213,7 @@ struct stm32_adc_cfg {
>  	void (*unprepare)(struct iio_dev *);
>  	void (*irq_clear)(struct iio_dev *indio_dev, u32 msk);
>  	const unsigned int *smp_cycles;
> +	const unsigned int ts_vrefint_ns;
>  };
>  
>  /**
> @@ -223,6 +238,7 @@ struct stm32_adc_cfg {
>   * @pcsel:		bitmask to preselect channels on some devices
>   * @smpr_val:		sampling time settings (e.g. smpr1 / smpr2)
>   * @cal:		optional calibration data on some devices
> + * @vrefint:		internal reference voltage data
>   * @chan_name:		channel name array
>   * @num_diff:		number of differential channels
>   * @int_ch:		internal channel indexes array
> @@ -248,6 +264,7 @@ struct stm32_adc {
>  	u32			pcsel;
>  	u32			smpr_val[2];
>  	struct stm32_adc_calib	cal;
> +	struct stm32_adc_vrefint vrefint;
>  	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
>  	u32			num_diff;
>  	int			int_ch[STM32_ADC_INT_CH_NB];
> @@ -1331,15 +1348,35 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
>  			ret = stm32_adc_single_conv(indio_dev, chan, val);
>  		else
>  			ret = -EINVAL;
> +
> +		/* If channel mask corresponds to vrefint, store data */
> +		if (adc->int_ch[STM32_ADC_INT_CH_VREFINT] == chan->channel)
> +			adc->vrefint.vrefint_data = *val;
> +
>  		iio_device_release_direct_mode(indio_dev);
>  		return ret;
>  
>  	case IIO_CHAN_INFO_SCALE:
>  		if (chan->differential) {
> -			*val = adc->common->vref_mv * 2;
> +			if (adc->vrefint.vrefint_data &&
> +			    adc->vrefint.vrefint_cal) {
> +				*val = STM32_ADC_VREFINT_VOLTAGE * 2 *
> +				       adc->vrefint.vrefint_cal /
> +				       adc->vrefint.vrefint_data;

Ah.. Dynamic scale.  This is always awkward when it occurs.
Given most / possibly all userspace software assumes a pseudo static scale
(not data dependent) we normally hide this by doing the maths internal to the
driver - sometimes meaning we need to present the particular channel as processed
not raw.

Is the expectation here that vrefint_data is actually very nearly constant? If
so then what you have here may be fine as anyone not aware the scale might change
will get very nearly the right value anyway.

> +			} else {
> +				*val = adc->common->vref_mv * 2;
> +			}
>  			*val2 = chan->scan_type.realbits;
>  		} else {
> -			*val = adc->common->vref_mv;
> +			/* Use vrefint data if available */
> +			if (adc->vrefint.vrefint_data &&
> +			    adc->vrefint.vrefint_cal) {
> +				*val = STM32_ADC_VREFINT_VOLTAGE *
> +				       adc->vrefint.vrefint_cal /
> +				       adc->vrefint.vrefint_data;
> +			} else {
> +				*val = adc->common->vref_mv;
> +			}
>  			*val2 = chan->scan_type.realbits;
>  		}
>  		return IIO_VAL_FRACTIONAL_LOG2;
> @@ -1907,6 +1944,35 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
>  	return scan_index;
>  }
>  
> +static int stm32_adc_get_int_ch(struct iio_dev *indio_dev, const char *ch_name,
> +				int chan)

Naming would suggest to me that it would return a channel rather than setting it
inside adc->int_ch[i]  Perhaps something like st32_adc_populate_int_ch() ?


> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	u16 vrefint;
> +	int i, ret;
> +
> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> +		if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
> +			adc->int_ch[i] = chan;
> +			/* If channel is vrefint get calibration data. */
> +			if (stm32_adc_ic[i].idx == STM32_ADC_INT_CH_VREFINT) {

I would reduce indentation by reversing the logic.

			if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT)
				continue;

			ret = 
> +				ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint);
> +				if (ret && ret != -ENOENT && ret != -EOPNOTSUPP) {
> +					dev_err(&indio_dev->dev, "nvmem access error %d\n", ret);
> +					return ret;
> +				}
> +				if (ret == -ENOENT)
> +					dev_dbg(&indio_dev->dev,
> +						"vrefint calibration not found\n");
> +				else
> +					adc->vrefint.vrefint_cal = vrefint;
> +			}
> +		}
> +	}
> +
> +	return 0;
> +}
> +
>  static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>  				       struct stm32_adc *adc,
>  				       struct iio_chan_spec *channels)
> @@ -1938,10 +2004,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>  				return -EINVAL;
>  			}
>  			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
> -			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> -				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
> -					adc->int_ch[i] = val;
> -			}
> +			ret = stm32_adc_get_int_ch(indio_dev, name, val);
> +			if (ret)
> +				goto err;
>  		} else if (ret != -EINVAL) {
>  			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
>  			goto err;
> @@ -2044,6 +2109,16 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
>  		 */
>  		of_property_read_u32_index(node, "st,min-sample-time-nsecs",
>  					   i, &smp);
> +
> +		/*
> +		 * For vrefint channel, ensure that the sampling time cannot
> +		 * be lower than the one specified in the datasheet
> +		 */
> +		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
> +		    smp < adc->cfg->ts_vrefint_ns) {
> +			smp = adc->cfg->ts_vrefint_ns;
> +		}

		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT])
			smp = max(smp, adc->cfg->ts_vrefint_ns);

> +
>  		/* Prepare sampling time settings */
>  		stm32_adc_smpr_init(adc, channels[i].channel, smp);
>  	}
> @@ -2350,6 +2425,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
>  	.unprepare = stm32h7_adc_unprepare,
>  	.smp_cycles = stm32h7_adc_smp_cycles,
>  	.irq_clear = stm32h7_adc_irq_clear,
> +	.ts_vrefint_ns = 4300,
>  };
>  
>  static const struct of_device_id stm32_adc_of_match[] = {


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 6/7] iio: adc: stm32-adc: add vrefint calibration support
@ 2021-09-11 16:28     ` Jonathan Cameron
  0 siblings, 0 replies; 53+ messages in thread
From: Jonathan Cameron @ 2021-09-11 16:28 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Lars-Peter Clausen, Maxime Coquelin, Rob Herring, alsa-devel,
	devicetree, linux-arm-kernel, linux-iio, linux-kernel,
	linux-stm32

On Wed, 8 Sep 2021 17:54:51 +0200
Olivier Moysan <olivier.moysan@foss.st.com> wrote:

> Add support of vrefint calibration.
> If a channel is labeled as vrefint, get vrefint calibration
> from non volatile memory for this channel.
> A conversion on vrefint channel allows to update scale
> factor according to vrefint deviation, compared to vrefint
> calibration value.

As I mention inline, whilst technically the ABI doesn't demand it
the expectation of much of userspace software is that _scale is
pseudo constant - that is it doesn't tend to change very often and when
it does it's normally because someone deliberately made it change.
As such most software reads it just once.

Normally we work around this by applying the maths in kernel and
not exposing the scale at all. Is this something that could be done here?

Jonathan

> 
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
> ---
>  drivers/iio/adc/stm32-adc.c | 88 ++++++++++++++++++++++++++++++++++---
>  1 file changed, 82 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> index ef3d2af98025..9e52a7de9b16 100644
> --- a/drivers/iio/adc/stm32-adc.c
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -21,6 +21,7 @@
>  #include <linux/io.h>
>  #include <linux/iopoll.h>
>  #include <linux/module.h>
> +#include <linux/nvmem-consumer.h>
>  #include <linux/platform_device.h>
>  #include <linux/pm_runtime.h>
>  #include <linux/of.h>
> @@ -42,6 +43,7 @@
>  #define STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
>  #define STM32_ADC_HW_STOP_DELAY_MS	100
>  #define STM32_ADC_CHAN_NONE		-1
> +#define STM32_ADC_VREFINT_VOLTAGE	3300
>  
>  #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
>  
> @@ -79,6 +81,7 @@ enum stm32_adc_extsel {
>  };
>  
>  enum stm32_adc_int_ch {
> +	STM32_ADC_INT_CH_NONE = -1,
>  	STM32_ADC_INT_CH_VDDCORE,
>  	STM32_ADC_INT_CH_VREFINT,
>  	STM32_ADC_INT_CH_VBAT,
> @@ -137,6 +140,16 @@ struct stm32_adc_regs {
>  	int shift;
>  };
>  
> +/**
> + * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data
> + * @vrefint_cal:	vrefint calibration value from nvmem
> + * @vrefint_data:	vrefint actual value
> + */
> +struct stm32_adc_vrefint {
> +	u32 vrefint_cal;
> +	u32 vrefint_data;
> +};
> +
>  /**
>   * struct stm32_adc_regspec - stm32 registers definition
>   * @dr:			data register offset
> @@ -186,6 +199,7 @@ struct stm32_adc;
>   * @unprepare:		optional unprepare routine (disable, power-down)
>   * @irq_clear:		routine to clear irqs
>   * @smp_cycles:		programmable sampling time (ADC clock cycles)
> + * @ts_vrefint_ns:	vrefint minimum sampling time in ns
>   */
>  struct stm32_adc_cfg {
>  	const struct stm32_adc_regspec	*regs;
> @@ -199,6 +213,7 @@ struct stm32_adc_cfg {
>  	void (*unprepare)(struct iio_dev *);
>  	void (*irq_clear)(struct iio_dev *indio_dev, u32 msk);
>  	const unsigned int *smp_cycles;
> +	const unsigned int ts_vrefint_ns;
>  };
>  
>  /**
> @@ -223,6 +238,7 @@ struct stm32_adc_cfg {
>   * @pcsel:		bitmask to preselect channels on some devices
>   * @smpr_val:		sampling time settings (e.g. smpr1 / smpr2)
>   * @cal:		optional calibration data on some devices
> + * @vrefint:		internal reference voltage data
>   * @chan_name:		channel name array
>   * @num_diff:		number of differential channels
>   * @int_ch:		internal channel indexes array
> @@ -248,6 +264,7 @@ struct stm32_adc {
>  	u32			pcsel;
>  	u32			smpr_val[2];
>  	struct stm32_adc_calib	cal;
> +	struct stm32_adc_vrefint vrefint;
>  	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
>  	u32			num_diff;
>  	int			int_ch[STM32_ADC_INT_CH_NB];
> @@ -1331,15 +1348,35 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
>  			ret = stm32_adc_single_conv(indio_dev, chan, val);
>  		else
>  			ret = -EINVAL;
> +
> +		/* If channel mask corresponds to vrefint, store data */
> +		if (adc->int_ch[STM32_ADC_INT_CH_VREFINT] == chan->channel)
> +			adc->vrefint.vrefint_data = *val;
> +
>  		iio_device_release_direct_mode(indio_dev);
>  		return ret;
>  
>  	case IIO_CHAN_INFO_SCALE:
>  		if (chan->differential) {
> -			*val = adc->common->vref_mv * 2;
> +			if (adc->vrefint.vrefint_data &&
> +			    adc->vrefint.vrefint_cal) {
> +				*val = STM32_ADC_VREFINT_VOLTAGE * 2 *
> +				       adc->vrefint.vrefint_cal /
> +				       adc->vrefint.vrefint_data;

Ah.. Dynamic scale.  This is always awkward when it occurs.
Given most / possibly all userspace software assumes a pseudo static scale
(not data dependent) we normally hide this by doing the maths internal to the
driver - sometimes meaning we need to present the particular channel as processed
not raw.

Is the expectation here that vrefint_data is actually very nearly constant? If
so then what you have here may be fine as anyone not aware the scale might change
will get very nearly the right value anyway.

> +			} else {
> +				*val = adc->common->vref_mv * 2;
> +			}
>  			*val2 = chan->scan_type.realbits;
>  		} else {
> -			*val = adc->common->vref_mv;
> +			/* Use vrefint data if available */
> +			if (adc->vrefint.vrefint_data &&
> +			    adc->vrefint.vrefint_cal) {
> +				*val = STM32_ADC_VREFINT_VOLTAGE *
> +				       adc->vrefint.vrefint_cal /
> +				       adc->vrefint.vrefint_data;
> +			} else {
> +				*val = adc->common->vref_mv;
> +			}
>  			*val2 = chan->scan_type.realbits;
>  		}
>  		return IIO_VAL_FRACTIONAL_LOG2;
> @@ -1907,6 +1944,35 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
>  	return scan_index;
>  }
>  
> +static int stm32_adc_get_int_ch(struct iio_dev *indio_dev, const char *ch_name,
> +				int chan)

Naming would suggest to me that it would return a channel rather than setting it
inside adc->int_ch[i]  Perhaps something like st32_adc_populate_int_ch() ?


> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	u16 vrefint;
> +	int i, ret;
> +
> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> +		if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
> +			adc->int_ch[i] = chan;
> +			/* If channel is vrefint get calibration data. */
> +			if (stm32_adc_ic[i].idx == STM32_ADC_INT_CH_VREFINT) {

I would reduce indentation by reversing the logic.

			if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT)
				continue;

			ret = 
> +				ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint);
> +				if (ret && ret != -ENOENT && ret != -EOPNOTSUPP) {
> +					dev_err(&indio_dev->dev, "nvmem access error %d\n", ret);
> +					return ret;
> +				}
> +				if (ret == -ENOENT)
> +					dev_dbg(&indio_dev->dev,
> +						"vrefint calibration not found\n");
> +				else
> +					adc->vrefint.vrefint_cal = vrefint;
> +			}
> +		}
> +	}
> +
> +	return 0;
> +}
> +
>  static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>  				       struct stm32_adc *adc,
>  				       struct iio_chan_spec *channels)
> @@ -1938,10 +2004,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>  				return -EINVAL;
>  			}
>  			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
> -			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> -				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
> -					adc->int_ch[i] = val;
> -			}
> +			ret = stm32_adc_get_int_ch(indio_dev, name, val);
> +			if (ret)
> +				goto err;
>  		} else if (ret != -EINVAL) {
>  			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
>  			goto err;
> @@ -2044,6 +2109,16 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
>  		 */
>  		of_property_read_u32_index(node, "st,min-sample-time-nsecs",
>  					   i, &smp);
> +
> +		/*
> +		 * For vrefint channel, ensure that the sampling time cannot
> +		 * be lower than the one specified in the datasheet
> +		 */
> +		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
> +		    smp < adc->cfg->ts_vrefint_ns) {
> +			smp = adc->cfg->ts_vrefint_ns;
> +		}

		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT])
			smp = max(smp, adc->cfg->ts_vrefint_ns);

> +
>  		/* Prepare sampling time settings */
>  		stm32_adc_smpr_init(adc, channels[i].channel, smp);
>  	}
> @@ -2350,6 +2425,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
>  	.unprepare = stm32h7_adc_unprepare,
>  	.smp_cycles = stm32h7_adc_smp_cycles,
>  	.irq_clear = stm32h7_adc_irq_clear,
> +	.ts_vrefint_ns = 4300,
>  };
>  
>  static const struct of_device_id stm32_adc_of_match[] = {


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 6/7] iio: adc: stm32-adc: add vrefint calibration support
  2021-09-11 16:28     ` Jonathan Cameron
@ 2021-09-15 10:02       ` Olivier MOYSAN
  -1 siblings, 0 replies; 53+ messages in thread
From: Olivier MOYSAN @ 2021-09-15 10:02 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Lars-Peter Clausen, Maxime Coquelin, Rob Herring, devicetree,
	linux-arm-kernel, linux-iio, linux-kernel, linux-stm32

Hi Jonathan,

On 9/11/21 6:28 PM, Jonathan Cameron wrote:
> On Wed, 8 Sep 2021 17:54:51 +0200
> Olivier Moysan <olivier.moysan@foss.st.com> wrote:
> 
>> Add support of vrefint calibration.
>> If a channel is labeled as vrefint, get vrefint calibration
>> from non volatile memory for this channel.
>> A conversion on vrefint channel allows to update scale
>> factor according to vrefint deviation, compared to vrefint
>> calibration value.
> 
> As I mention inline, whilst technically the ABI doesn't demand it
> the expectation of much of userspace software is that _scale is
> pseudo constant - that is it doesn't tend to change very often and when
> it does it's normally because someone deliberately made it change.
> As such most software reads it just once.
> 
> Normally we work around this by applying the maths in kernel and
> not exposing the scale at all. Is this something that could be done here?
> 
> Jonathan
> 
>>
>> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
>> ---
>>   drivers/iio/adc/stm32-adc.c | 88 ++++++++++++++++++++++++++++++++++---
>>   1 file changed, 82 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
>> index ef3d2af98025..9e52a7de9b16 100644
>> --- a/drivers/iio/adc/stm32-adc.c
>> +++ b/drivers/iio/adc/stm32-adc.c
>> @@ -21,6 +21,7 @@
>>   #include <linux/io.h>
>>   #include <linux/iopoll.h>
>>   #include <linux/module.h>
>> +#include <linux/nvmem-consumer.h>
>>   #include <linux/platform_device.h>
>>   #include <linux/pm_runtime.h>
>>   #include <linux/of.h>
>> @@ -42,6 +43,7 @@
>>   #define STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
>>   #define STM32_ADC_HW_STOP_DELAY_MS	100
>>   #define STM32_ADC_CHAN_NONE		-1
>> +#define STM32_ADC_VREFINT_VOLTAGE	3300
>>   
>>   #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
>>   
>> @@ -79,6 +81,7 @@ enum stm32_adc_extsel {
>>   };
>>   
>>   enum stm32_adc_int_ch {
>> +	STM32_ADC_INT_CH_NONE = -1,
>>   	STM32_ADC_INT_CH_VDDCORE,
>>   	STM32_ADC_INT_CH_VREFINT,
>>   	STM32_ADC_INT_CH_VBAT,
>> @@ -137,6 +140,16 @@ struct stm32_adc_regs {
>>   	int shift;
>>   };
>>   
>> +/**
>> + * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data
>> + * @vrefint_cal:	vrefint calibration value from nvmem
>> + * @vrefint_data:	vrefint actual value
>> + */
>> +struct stm32_adc_vrefint {
>> +	u32 vrefint_cal;
>> +	u32 vrefint_data;
>> +};
>> +
>>   /**
>>    * struct stm32_adc_regspec - stm32 registers definition
>>    * @dr:			data register offset
>> @@ -186,6 +199,7 @@ struct stm32_adc;
>>    * @unprepare:		optional unprepare routine (disable, power-down)
>>    * @irq_clear:		routine to clear irqs
>>    * @smp_cycles:		programmable sampling time (ADC clock cycles)
>> + * @ts_vrefint_ns:	vrefint minimum sampling time in ns
>>    */
>>   struct stm32_adc_cfg {
>>   	const struct stm32_adc_regspec	*regs;
>> @@ -199,6 +213,7 @@ struct stm32_adc_cfg {
>>   	void (*unprepare)(struct iio_dev *);
>>   	void (*irq_clear)(struct iio_dev *indio_dev, u32 msk);
>>   	const unsigned int *smp_cycles;
>> +	const unsigned int ts_vrefint_ns;
>>   };
>>   
>>   /**
>> @@ -223,6 +238,7 @@ struct stm32_adc_cfg {
>>    * @pcsel:		bitmask to preselect channels on some devices
>>    * @smpr_val:		sampling time settings (e.g. smpr1 / smpr2)
>>    * @cal:		optional calibration data on some devices
>> + * @vrefint:		internal reference voltage data
>>    * @chan_name:		channel name array
>>    * @num_diff:		number of differential channels
>>    * @int_ch:		internal channel indexes array
>> @@ -248,6 +264,7 @@ struct stm32_adc {
>>   	u32			pcsel;
>>   	u32			smpr_val[2];
>>   	struct stm32_adc_calib	cal;
>> +	struct stm32_adc_vrefint vrefint;
>>   	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
>>   	u32			num_diff;
>>   	int			int_ch[STM32_ADC_INT_CH_NB];
>> @@ -1331,15 +1348,35 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
>>   			ret = stm32_adc_single_conv(indio_dev, chan, val);
>>   		else
>>   			ret = -EINVAL;
>> +
>> +		/* If channel mask corresponds to vrefint, store data */
>> +		if (adc->int_ch[STM32_ADC_INT_CH_VREFINT] == chan->channel)
>> +			adc->vrefint.vrefint_data = *val;
>> +
>>   		iio_device_release_direct_mode(indio_dev);
>>   		return ret;
>>   
>>   	case IIO_CHAN_INFO_SCALE:
>>   		if (chan->differential) {
>> -			*val = adc->common->vref_mv * 2;
>> +			if (adc->vrefint.vrefint_data &&
>> +			    adc->vrefint.vrefint_cal) {
>> +				*val = STM32_ADC_VREFINT_VOLTAGE * 2 *
>> +				       adc->vrefint.vrefint_cal /
>> +				       adc->vrefint.vrefint_data;
> 
> Ah.. Dynamic scale.  This is always awkward when it occurs.
> Given most / possibly all userspace software assumes a pseudo static scale
> (not data dependent) we normally hide this by doing the maths internal to the
> driver - sometimes meaning we need to present the particular channel as processed
> not raw.
> 
> Is the expectation here that vrefint_data is actually very nearly constant? If
> so then what you have here may be fine as anyone not aware the scale might change
> will get very nearly the right value anyway.
> 

The need here is to compare the measured value of vrefint with the 
calibrated value saved in non volatile memory. The ratio between these 
two values can be used as a correction factor for the acquisitions on 
all other channels.

The vrefint data is expected to be close to the saved vrefint 
calibration value, and it should not vary strongly over time.
So, yes, we can indeed consider the scale as a pseudo constant. If the 
scale is not updated, the deviation with actual value should remain 
limited, as well.

You suggest above to hide scale tuning through processed channels.
If I follow this logic, when vrefint channel is available, all channels 
should be defined as processed channels (excepted vrefint channel)
In this case no scale is exposed for these channels, and the vrefint 
calibration ratio can be used to provide converted data directly.
Do you prefer this implementation ?

In this case I wonder how buffered data have to be managed. These data 
are still provided as raw data, but the scale factor is not more 
available to convert them. I guess that these data have to be converted 
internally also, either in dma callback or irq handler.
Is this correct ?

Regards
Olivier

>> +			} else {
>> +				*val = adc->common->vref_mv * 2;
>> +			}
>>   			*val2 = chan->scan_type.realbits;
>>   		} else {
>> -			*val = adc->common->vref_mv;
>> +			/* Use vrefint data if available */
>> +			if (adc->vrefint.vrefint_data &&
>> +			    adc->vrefint.vrefint_cal) {
>> +				*val = STM32_ADC_VREFINT_VOLTAGE *
>> +				       adc->vrefint.vrefint_cal /
>> +				       adc->vrefint.vrefint_data;
>> +			} else {
>> +				*val = adc->common->vref_mv;
>> +			}
>>   			*val2 = chan->scan_type.realbits;
>>   		}
>>   		return IIO_VAL_FRACTIONAL_LOG2;
>> @@ -1907,6 +1944,35 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
>>   	return scan_index;
>>   }
>>   
>> +static int stm32_adc_get_int_ch(struct iio_dev *indio_dev, const char *ch_name,
>> +				int chan)
> 
> Naming would suggest to me that it would return a channel rather than setting it
> inside adc->int_ch[i]  Perhaps something like st32_adc_populate_int_ch() ?
> 
> 
>> +{
>> +	struct stm32_adc *adc = iio_priv(indio_dev);
>> +	u16 vrefint;
>> +	int i, ret;
>> +
>> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
>> +		if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
>> +			adc->int_ch[i] = chan;
>> +			/* If channel is vrefint get calibration data. */
>> +			if (stm32_adc_ic[i].idx == STM32_ADC_INT_CH_VREFINT) {
> 
> I would reduce indentation by reversing the logic.
> 
> 			if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT)
> 				continue;
> 
> 			ret =
>> +				ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint);
>> +				if (ret && ret != -ENOENT && ret != -EOPNOTSUPP) {
>> +					dev_err(&indio_dev->dev, "nvmem access error %d\n", ret);
>> +					return ret;
>> +				}
>> +				if (ret == -ENOENT)
>> +					dev_dbg(&indio_dev->dev,
>> +						"vrefint calibration not found\n");
>> +				else
>> +					adc->vrefint.vrefint_cal = vrefint;
>> +			}
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>>   static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>>   				       struct stm32_adc *adc,
>>   				       struct iio_chan_spec *channels)
>> @@ -1938,10 +2004,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>>   				return -EINVAL;
>>   			}
>>   			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
>> -			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
>> -				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
>> -					adc->int_ch[i] = val;
>> -			}
>> +			ret = stm32_adc_get_int_ch(indio_dev, name, val);
>> +			if (ret)
>> +				goto err;
>>   		} else if (ret != -EINVAL) {
>>   			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
>>   			goto err;
>> @@ -2044,6 +2109,16 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
>>   		 */
>>   		of_property_read_u32_index(node, "st,min-sample-time-nsecs",
>>   					   i, &smp);
>> +
>> +		/*
>> +		 * For vrefint channel, ensure that the sampling time cannot
>> +		 * be lower than the one specified in the datasheet
>> +		 */
>> +		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
>> +		    smp < adc->cfg->ts_vrefint_ns) {
>> +			smp = adc->cfg->ts_vrefint_ns;
>> +		}
> 
> 		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT])
> 			smp = max(smp, adc->cfg->ts_vrefint_ns);
> 
>> +
>>   		/* Prepare sampling time settings */
>>   		stm32_adc_smpr_init(adc, channels[i].channel, smp);
>>   	}
>> @@ -2350,6 +2425,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
>>   	.unprepare = stm32h7_adc_unprepare,
>>   	.smp_cycles = stm32h7_adc_smp_cycles,
>>   	.irq_clear = stm32h7_adc_irq_clear,
>> +	.ts_vrefint_ns = 4300,
>>   };
>>   
>>   static const struct of_device_id stm32_adc_of_match[] = {
> 

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 6/7] iio: adc: stm32-adc: add vrefint calibration support
@ 2021-09-15 10:02       ` Olivier MOYSAN
  0 siblings, 0 replies; 53+ messages in thread
From: Olivier MOYSAN @ 2021-09-15 10:02 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Lars-Peter Clausen, Maxime Coquelin, Rob Herring, devicetree,
	linux-arm-kernel, linux-iio, linux-kernel, linux-stm32

Hi Jonathan,

On 9/11/21 6:28 PM, Jonathan Cameron wrote:
> On Wed, 8 Sep 2021 17:54:51 +0200
> Olivier Moysan <olivier.moysan@foss.st.com> wrote:
> 
>> Add support of vrefint calibration.
>> If a channel is labeled as vrefint, get vrefint calibration
>> from non volatile memory for this channel.
>> A conversion on vrefint channel allows to update scale
>> factor according to vrefint deviation, compared to vrefint
>> calibration value.
> 
> As I mention inline, whilst technically the ABI doesn't demand it
> the expectation of much of userspace software is that _scale is
> pseudo constant - that is it doesn't tend to change very often and when
> it does it's normally because someone deliberately made it change.
> As such most software reads it just once.
> 
> Normally we work around this by applying the maths in kernel and
> not exposing the scale at all. Is this something that could be done here?
> 
> Jonathan
> 
>>
>> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
>> ---
>>   drivers/iio/adc/stm32-adc.c | 88 ++++++++++++++++++++++++++++++++++---
>>   1 file changed, 82 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
>> index ef3d2af98025..9e52a7de9b16 100644
>> --- a/drivers/iio/adc/stm32-adc.c
>> +++ b/drivers/iio/adc/stm32-adc.c
>> @@ -21,6 +21,7 @@
>>   #include <linux/io.h>
>>   #include <linux/iopoll.h>
>>   #include <linux/module.h>
>> +#include <linux/nvmem-consumer.h>
>>   #include <linux/platform_device.h>
>>   #include <linux/pm_runtime.h>
>>   #include <linux/of.h>
>> @@ -42,6 +43,7 @@
>>   #define STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
>>   #define STM32_ADC_HW_STOP_DELAY_MS	100
>>   #define STM32_ADC_CHAN_NONE		-1
>> +#define STM32_ADC_VREFINT_VOLTAGE	3300
>>   
>>   #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
>>   
>> @@ -79,6 +81,7 @@ enum stm32_adc_extsel {
>>   };
>>   
>>   enum stm32_adc_int_ch {
>> +	STM32_ADC_INT_CH_NONE = -1,
>>   	STM32_ADC_INT_CH_VDDCORE,
>>   	STM32_ADC_INT_CH_VREFINT,
>>   	STM32_ADC_INT_CH_VBAT,
>> @@ -137,6 +140,16 @@ struct stm32_adc_regs {
>>   	int shift;
>>   };
>>   
>> +/**
>> + * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data
>> + * @vrefint_cal:	vrefint calibration value from nvmem
>> + * @vrefint_data:	vrefint actual value
>> + */
>> +struct stm32_adc_vrefint {
>> +	u32 vrefint_cal;
>> +	u32 vrefint_data;
>> +};
>> +
>>   /**
>>    * struct stm32_adc_regspec - stm32 registers definition
>>    * @dr:			data register offset
>> @@ -186,6 +199,7 @@ struct stm32_adc;
>>    * @unprepare:		optional unprepare routine (disable, power-down)
>>    * @irq_clear:		routine to clear irqs
>>    * @smp_cycles:		programmable sampling time (ADC clock cycles)
>> + * @ts_vrefint_ns:	vrefint minimum sampling time in ns
>>    */
>>   struct stm32_adc_cfg {
>>   	const struct stm32_adc_regspec	*regs;
>> @@ -199,6 +213,7 @@ struct stm32_adc_cfg {
>>   	void (*unprepare)(struct iio_dev *);
>>   	void (*irq_clear)(struct iio_dev *indio_dev, u32 msk);
>>   	const unsigned int *smp_cycles;
>> +	const unsigned int ts_vrefint_ns;
>>   };
>>   
>>   /**
>> @@ -223,6 +238,7 @@ struct stm32_adc_cfg {
>>    * @pcsel:		bitmask to preselect channels on some devices
>>    * @smpr_val:		sampling time settings (e.g. smpr1 / smpr2)
>>    * @cal:		optional calibration data on some devices
>> + * @vrefint:		internal reference voltage data
>>    * @chan_name:		channel name array
>>    * @num_diff:		number of differential channels
>>    * @int_ch:		internal channel indexes array
>> @@ -248,6 +264,7 @@ struct stm32_adc {
>>   	u32			pcsel;
>>   	u32			smpr_val[2];
>>   	struct stm32_adc_calib	cal;
>> +	struct stm32_adc_vrefint vrefint;
>>   	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
>>   	u32			num_diff;
>>   	int			int_ch[STM32_ADC_INT_CH_NB];
>> @@ -1331,15 +1348,35 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
>>   			ret = stm32_adc_single_conv(indio_dev, chan, val);
>>   		else
>>   			ret = -EINVAL;
>> +
>> +		/* If channel mask corresponds to vrefint, store data */
>> +		if (adc->int_ch[STM32_ADC_INT_CH_VREFINT] == chan->channel)
>> +			adc->vrefint.vrefint_data = *val;
>> +
>>   		iio_device_release_direct_mode(indio_dev);
>>   		return ret;
>>   
>>   	case IIO_CHAN_INFO_SCALE:
>>   		if (chan->differential) {
>> -			*val = adc->common->vref_mv * 2;
>> +			if (adc->vrefint.vrefint_data &&
>> +			    adc->vrefint.vrefint_cal) {
>> +				*val = STM32_ADC_VREFINT_VOLTAGE * 2 *
>> +				       adc->vrefint.vrefint_cal /
>> +				       adc->vrefint.vrefint_data;
> 
> Ah.. Dynamic scale.  This is always awkward when it occurs.
> Given most / possibly all userspace software assumes a pseudo static scale
> (not data dependent) we normally hide this by doing the maths internal to the
> driver - sometimes meaning we need to present the particular channel as processed
> not raw.
> 
> Is the expectation here that vrefint_data is actually very nearly constant? If
> so then what you have here may be fine as anyone not aware the scale might change
> will get very nearly the right value anyway.
> 

The need here is to compare the measured value of vrefint with the 
calibrated value saved in non volatile memory. The ratio between these 
two values can be used as a correction factor for the acquisitions on 
all other channels.

The vrefint data is expected to be close to the saved vrefint 
calibration value, and it should not vary strongly over time.
So, yes, we can indeed consider the scale as a pseudo constant. If the 
scale is not updated, the deviation with actual value should remain 
limited, as well.

You suggest above to hide scale tuning through processed channels.
If I follow this logic, when vrefint channel is available, all channels 
should be defined as processed channels (excepted vrefint channel)
In this case no scale is exposed for these channels, and the vrefint 
calibration ratio can be used to provide converted data directly.
Do you prefer this implementation ?

In this case I wonder how buffered data have to be managed. These data 
are still provided as raw data, but the scale factor is not more 
available to convert them. I guess that these data have to be converted 
internally also, either in dma callback or irq handler.
Is this correct ?

Regards
Olivier

>> +			} else {
>> +				*val = adc->common->vref_mv * 2;
>> +			}
>>   			*val2 = chan->scan_type.realbits;
>>   		} else {
>> -			*val = adc->common->vref_mv;
>> +			/* Use vrefint data if available */
>> +			if (adc->vrefint.vrefint_data &&
>> +			    adc->vrefint.vrefint_cal) {
>> +				*val = STM32_ADC_VREFINT_VOLTAGE *
>> +				       adc->vrefint.vrefint_cal /
>> +				       adc->vrefint.vrefint_data;
>> +			} else {
>> +				*val = adc->common->vref_mv;
>> +			}
>>   			*val2 = chan->scan_type.realbits;
>>   		}
>>   		return IIO_VAL_FRACTIONAL_LOG2;
>> @@ -1907,6 +1944,35 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
>>   	return scan_index;
>>   }
>>   
>> +static int stm32_adc_get_int_ch(struct iio_dev *indio_dev, const char *ch_name,
>> +				int chan)
> 
> Naming would suggest to me that it would return a channel rather than setting it
> inside adc->int_ch[i]  Perhaps something like st32_adc_populate_int_ch() ?
> 
> 
>> +{
>> +	struct stm32_adc *adc = iio_priv(indio_dev);
>> +	u16 vrefint;
>> +	int i, ret;
>> +
>> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
>> +		if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
>> +			adc->int_ch[i] = chan;
>> +			/* If channel is vrefint get calibration data. */
>> +			if (stm32_adc_ic[i].idx == STM32_ADC_INT_CH_VREFINT) {
> 
> I would reduce indentation by reversing the logic.
> 
> 			if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT)
> 				continue;
> 
> 			ret =
>> +				ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint);
>> +				if (ret && ret != -ENOENT && ret != -EOPNOTSUPP) {
>> +					dev_err(&indio_dev->dev, "nvmem access error %d\n", ret);
>> +					return ret;
>> +				}
>> +				if (ret == -ENOENT)
>> +					dev_dbg(&indio_dev->dev,
>> +						"vrefint calibration not found\n");
>> +				else
>> +					adc->vrefint.vrefint_cal = vrefint;
>> +			}
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>>   static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>>   				       struct stm32_adc *adc,
>>   				       struct iio_chan_spec *channels)
>> @@ -1938,10 +2004,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>>   				return -EINVAL;
>>   			}
>>   			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
>> -			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
>> -				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
>> -					adc->int_ch[i] = val;
>> -			}
>> +			ret = stm32_adc_get_int_ch(indio_dev, name, val);
>> +			if (ret)
>> +				goto err;
>>   		} else if (ret != -EINVAL) {
>>   			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
>>   			goto err;
>> @@ -2044,6 +2109,16 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
>>   		 */
>>   		of_property_read_u32_index(node, "st,min-sample-time-nsecs",
>>   					   i, &smp);
>> +
>> +		/*
>> +		 * For vrefint channel, ensure that the sampling time cannot
>> +		 * be lower than the one specified in the datasheet
>> +		 */
>> +		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
>> +		    smp < adc->cfg->ts_vrefint_ns) {
>> +			smp = adc->cfg->ts_vrefint_ns;
>> +		}
> 
> 		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT])
> 			smp = max(smp, adc->cfg->ts_vrefint_ns);
> 
>> +
>>   		/* Prepare sampling time settings */
>>   		stm32_adc_smpr_init(adc, channels[i].channel, smp);
>>   	}
>> @@ -2350,6 +2425,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
>>   	.unprepare = stm32h7_adc_unprepare,
>>   	.smp_cycles = stm32h7_adc_smp_cycles,
>>   	.irq_clear = stm32h7_adc_irq_clear,
>> +	.ts_vrefint_ns = 4300,
>>   };
>>   
>>   static const struct of_device_id stm32_adc_of_match[] = {
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 6/7] iio: adc: stm32-adc: add vrefint calibration support
  2021-09-15 10:02       ` Olivier MOYSAN
@ 2021-09-18 18:42         ` Jonathan Cameron
  -1 siblings, 0 replies; 53+ messages in thread
From: Jonathan Cameron @ 2021-09-18 18:42 UTC (permalink / raw)
  To: Olivier MOYSAN
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Lars-Peter Clausen, Maxime Coquelin, Rob Herring, devicetree,
	linux-arm-kernel, linux-iio, linux-kernel, linux-stm32

On Wed, 15 Sep 2021 12:02:45 +0200
Olivier MOYSAN <olivier.moysan@foss.st.com> wrote:

> Hi Jonathan,
> 
> On 9/11/21 6:28 PM, Jonathan Cameron wrote:
> > On Wed, 8 Sep 2021 17:54:51 +0200
> > Olivier Moysan <olivier.moysan@foss.st.com> wrote:
> >   
> >> Add support of vrefint calibration.
> >> If a channel is labeled as vrefint, get vrefint calibration
> >> from non volatile memory for this channel.
> >> A conversion on vrefint channel allows to update scale
> >> factor according to vrefint deviation, compared to vrefint
> >> calibration value.  
> > 
> > As I mention inline, whilst technically the ABI doesn't demand it
> > the expectation of much of userspace software is that _scale is
> > pseudo constant - that is it doesn't tend to change very often and when
> > it does it's normally because someone deliberately made it change.
> > As such most software reads it just once.
> > 
> > Normally we work around this by applying the maths in kernel and
> > not exposing the scale at all. Is this something that could be done here?
> > 
> > Jonathan
> >   
> >>
> >> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
> >> ---
> >>   drivers/iio/adc/stm32-adc.c | 88 ++++++++++++++++++++++++++++++++++---
> >>   1 file changed, 82 insertions(+), 6 deletions(-)
> >>
> >> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> >> index ef3d2af98025..9e52a7de9b16 100644
> >> --- a/drivers/iio/adc/stm32-adc.c
> >> +++ b/drivers/iio/adc/stm32-adc.c
> >> @@ -21,6 +21,7 @@
> >>   #include <linux/io.h>
> >>   #include <linux/iopoll.h>
> >>   #include <linux/module.h>
> >> +#include <linux/nvmem-consumer.h>
> >>   #include <linux/platform_device.h>
> >>   #include <linux/pm_runtime.h>
> >>   #include <linux/of.h>
> >> @@ -42,6 +43,7 @@
> >>   #define STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
> >>   #define STM32_ADC_HW_STOP_DELAY_MS	100
> >>   #define STM32_ADC_CHAN_NONE		-1
> >> +#define STM32_ADC_VREFINT_VOLTAGE	3300
> >>   
> >>   #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
> >>   
> >> @@ -79,6 +81,7 @@ enum stm32_adc_extsel {
> >>   };
> >>   
> >>   enum stm32_adc_int_ch {
> >> +	STM32_ADC_INT_CH_NONE = -1,
> >>   	STM32_ADC_INT_CH_VDDCORE,
> >>   	STM32_ADC_INT_CH_VREFINT,
> >>   	STM32_ADC_INT_CH_VBAT,
> >> @@ -137,6 +140,16 @@ struct stm32_adc_regs {
> >>   	int shift;
> >>   };
> >>   
> >> +/**
> >> + * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data
> >> + * @vrefint_cal:	vrefint calibration value from nvmem
> >> + * @vrefint_data:	vrefint actual value
> >> + */
> >> +struct stm32_adc_vrefint {
> >> +	u32 vrefint_cal;
> >> +	u32 vrefint_data;
> >> +};
> >> +
> >>   /**
> >>    * struct stm32_adc_regspec - stm32 registers definition
> >>    * @dr:			data register offset
> >> @@ -186,6 +199,7 @@ struct stm32_adc;
> >>    * @unprepare:		optional unprepare routine (disable, power-down)
> >>    * @irq_clear:		routine to clear irqs
> >>    * @smp_cycles:		programmable sampling time (ADC clock cycles)
> >> + * @ts_vrefint_ns:	vrefint minimum sampling time in ns
> >>    */
> >>   struct stm32_adc_cfg {
> >>   	const struct stm32_adc_regspec	*regs;
> >> @@ -199,6 +213,7 @@ struct stm32_adc_cfg {
> >>   	void (*unprepare)(struct iio_dev *);
> >>   	void (*irq_clear)(struct iio_dev *indio_dev, u32 msk);
> >>   	const unsigned int *smp_cycles;
> >> +	const unsigned int ts_vrefint_ns;
> >>   };
> >>   
> >>   /**
> >> @@ -223,6 +238,7 @@ struct stm32_adc_cfg {
> >>    * @pcsel:		bitmask to preselect channels on some devices
> >>    * @smpr_val:		sampling time settings (e.g. smpr1 / smpr2)
> >>    * @cal:		optional calibration data on some devices
> >> + * @vrefint:		internal reference voltage data
> >>    * @chan_name:		channel name array
> >>    * @num_diff:		number of differential channels
> >>    * @int_ch:		internal channel indexes array
> >> @@ -248,6 +264,7 @@ struct stm32_adc {
> >>   	u32			pcsel;
> >>   	u32			smpr_val[2];
> >>   	struct stm32_adc_calib	cal;
> >> +	struct stm32_adc_vrefint vrefint;
> >>   	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
> >>   	u32			num_diff;
> >>   	int			int_ch[STM32_ADC_INT_CH_NB];
> >> @@ -1331,15 +1348,35 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
> >>   			ret = stm32_adc_single_conv(indio_dev, chan, val);
> >>   		else
> >>   			ret = -EINVAL;
> >> +
> >> +		/* If channel mask corresponds to vrefint, store data */
> >> +		if (adc->int_ch[STM32_ADC_INT_CH_VREFINT] == chan->channel)
> >> +			adc->vrefint.vrefint_data = *val;
> >> +
> >>   		iio_device_release_direct_mode(indio_dev);
> >>   		return ret;
> >>   
> >>   	case IIO_CHAN_INFO_SCALE:
> >>   		if (chan->differential) {
> >> -			*val = adc->common->vref_mv * 2;
> >> +			if (adc->vrefint.vrefint_data &&
> >> +			    adc->vrefint.vrefint_cal) {
> >> +				*val = STM32_ADC_VREFINT_VOLTAGE * 2 *
> >> +				       adc->vrefint.vrefint_cal /
> >> +				       adc->vrefint.vrefint_data;  
> > 
> > Ah.. Dynamic scale.  This is always awkward when it occurs.
> > Given most / possibly all userspace software assumes a pseudo static scale
> > (not data dependent) we normally hide this by doing the maths internal to the
> > driver - sometimes meaning we need to present the particular channel as processed
> > not raw.
> > 
> > Is the expectation here that vrefint_data is actually very nearly constant? If
> > so then what you have here may be fine as anyone not aware the scale might change
> > will get very nearly the right value anyway.
> >   
> 
> The need here is to compare the measured value of vrefint with the 
> calibrated value saved in non volatile memory. The ratio between these 
> two values can be used as a correction factor for the acquisitions on 
> all other channels.
> 
> The vrefint data is expected to be close to the saved vrefint 
> calibration value, and it should not vary strongly over time.
> So, yes, we can indeed consider the scale as a pseudo constant. If the 
> scale is not updated, the deviation with actual value should remain 
> limited, as well.

Ok, so in that case we could probably get away with having it as you have
here, though for maximum precision we'd need userspace to occasionally check
the scale.

> 
> You suggest above to hide scale tuning through processed channels.
> If I follow this logic, when vrefint channel is available, all channels 
> should be defined as processed channels (excepted vrefint channel)
> In this case no scale is exposed for these channels, and the vrefint 
> calibration ratio can be used to provide converted data directly.
> Do you prefer this implementation ?

> 
> In this case I wonder how buffered data have to be managed. These data 
> are still provided as raw data, but the scale factor is not more 
> available to convert them. I guess that these data have to be converted 
> internally also, either in dma callback or irq handler.
> Is this correct ?

This is one of the holes in what IIO does today.  Without meta data in the
buffer (which is hard to define in a clean fashion) it is hard to have
a compact representation of the data in the presence of dynamic scaling.
The vast majority of devices don't inherently support such scaling so
this is only occasionally a problem. 

To support this at the moment you would indeed need to scale the data
before pushing it to the buffer which is obviously really ugly.

My gut feeling here is there are three possible approaches.

1) Ignore the dynamic nature of the calibration and pretend it's static.
2) Add an explicit 'calibration' sysfs attribute.
   This is a fairly common model for other sensor types which don't do
   dynamic calibration but instead require to you to start some special
   calibration sequence.
   As the calibration is not updated, except on explicit userspace action
   we can assume that the scale is static unless userspace is aware of
   the dynamic aspect.
3) Add a userspace control to turn on dynamic calibration.  That makes it
   opt in.  Everything will work reasonably well without it turned on
   as we'll hopefully have a static estimate of scale which is good enough.
   If aware software is using the device, it can enable this mode and
   sample the scale as often as it wants to.

I slightly favour option 3.  What do you think?  If we ever figure out
the meta data question for buffered case then we can make that work on top
of this.

Jonathan
> 
> Regards
> Olivier
> 
> >> +			} else {
> >> +				*val = adc->common->vref_mv * 2;
> >> +			}
> >>   			*val2 = chan->scan_type.realbits;
> >>   		} else {
> >> -			*val = adc->common->vref_mv;
> >> +			/* Use vrefint data if available */
> >> +			if (adc->vrefint.vrefint_data &&
> >> +			    adc->vrefint.vrefint_cal) {
> >> +				*val = STM32_ADC_VREFINT_VOLTAGE *
> >> +				       adc->vrefint.vrefint_cal /
> >> +				       adc->vrefint.vrefint_data;
> >> +			} else {
> >> +				*val = adc->common->vref_mv;
> >> +			}
> >>   			*val2 = chan->scan_type.realbits;
> >>   		}
> >>   		return IIO_VAL_FRACTIONAL_LOG2;
> >> @@ -1907,6 +1944,35 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
> >>   	return scan_index;
> >>   }
> >>   
> >> +static int stm32_adc_get_int_ch(struct iio_dev *indio_dev, const char *ch_name,
> >> +				int chan)  
> > 
> > Naming would suggest to me that it would return a channel rather than setting it
> > inside adc->int_ch[i]  Perhaps something like st32_adc_populate_int_ch() ?
> > 
> >   
> >> +{
> >> +	struct stm32_adc *adc = iio_priv(indio_dev);
> >> +	u16 vrefint;
> >> +	int i, ret;
> >> +
> >> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> >> +		if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
> >> +			adc->int_ch[i] = chan;
> >> +			/* If channel is vrefint get calibration data. */
> >> +			if (stm32_adc_ic[i].idx == STM32_ADC_INT_CH_VREFINT) {  
> > 
> > I would reduce indentation by reversing the logic.
> > 
> > 			if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT)
> > 				continue;
> > 
> > 			ret =  
> >> +				ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint);
> >> +				if (ret && ret != -ENOENT && ret != -EOPNOTSUPP) {
> >> +					dev_err(&indio_dev->dev, "nvmem access error %d\n", ret);
> >> +					return ret;
> >> +				}
> >> +				if (ret == -ENOENT)
> >> +					dev_dbg(&indio_dev->dev,
> >> +						"vrefint calibration not found\n");
> >> +				else
> >> +					adc->vrefint.vrefint_cal = vrefint;
> >> +			}
> >> +		}
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >>   static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
> >>   				       struct stm32_adc *adc,
> >>   				       struct iio_chan_spec *channels)
> >> @@ -1938,10 +2004,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
> >>   				return -EINVAL;
> >>   			}
> >>   			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
> >> -			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> >> -				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
> >> -					adc->int_ch[i] = val;
> >> -			}
> >> +			ret = stm32_adc_get_int_ch(indio_dev, name, val);
> >> +			if (ret)
> >> +				goto err;
> >>   		} else if (ret != -EINVAL) {
> >>   			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
> >>   			goto err;
> >> @@ -2044,6 +2109,16 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
> >>   		 */
> >>   		of_property_read_u32_index(node, "st,min-sample-time-nsecs",
> >>   					   i, &smp);
> >> +
> >> +		/*
> >> +		 * For vrefint channel, ensure that the sampling time cannot
> >> +		 * be lower than the one specified in the datasheet
> >> +		 */
> >> +		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
> >> +		    smp < adc->cfg->ts_vrefint_ns) {
> >> +			smp = adc->cfg->ts_vrefint_ns;
> >> +		}  
> > 
> > 		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT])
> > 			smp = max(smp, adc->cfg->ts_vrefint_ns);
> >   
> >> +
> >>   		/* Prepare sampling time settings */
> >>   		stm32_adc_smpr_init(adc, channels[i].channel, smp);
> >>   	}
> >> @@ -2350,6 +2425,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
> >>   	.unprepare = stm32h7_adc_unprepare,
> >>   	.smp_cycles = stm32h7_adc_smp_cycles,
> >>   	.irq_clear = stm32h7_adc_irq_clear,
> >> +	.ts_vrefint_ns = 4300,
> >>   };
> >>   
> >>   static const struct of_device_id stm32_adc_of_match[] = {  
> >   


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 6/7] iio: adc: stm32-adc: add vrefint calibration support
@ 2021-09-18 18:42         ` Jonathan Cameron
  0 siblings, 0 replies; 53+ messages in thread
From: Jonathan Cameron @ 2021-09-18 18:42 UTC (permalink / raw)
  To: Olivier MOYSAN
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Lars-Peter Clausen, Maxime Coquelin, Rob Herring, devicetree,
	linux-arm-kernel, linux-iio, linux-kernel, linux-stm32

On Wed, 15 Sep 2021 12:02:45 +0200
Olivier MOYSAN <olivier.moysan@foss.st.com> wrote:

> Hi Jonathan,
> 
> On 9/11/21 6:28 PM, Jonathan Cameron wrote:
> > On Wed, 8 Sep 2021 17:54:51 +0200
> > Olivier Moysan <olivier.moysan@foss.st.com> wrote:
> >   
> >> Add support of vrefint calibration.
> >> If a channel is labeled as vrefint, get vrefint calibration
> >> from non volatile memory for this channel.
> >> A conversion on vrefint channel allows to update scale
> >> factor according to vrefint deviation, compared to vrefint
> >> calibration value.  
> > 
> > As I mention inline, whilst technically the ABI doesn't demand it
> > the expectation of much of userspace software is that _scale is
> > pseudo constant - that is it doesn't tend to change very often and when
> > it does it's normally because someone deliberately made it change.
> > As such most software reads it just once.
> > 
> > Normally we work around this by applying the maths in kernel and
> > not exposing the scale at all. Is this something that could be done here?
> > 
> > Jonathan
> >   
> >>
> >> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
> >> ---
> >>   drivers/iio/adc/stm32-adc.c | 88 ++++++++++++++++++++++++++++++++++---
> >>   1 file changed, 82 insertions(+), 6 deletions(-)
> >>
> >> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> >> index ef3d2af98025..9e52a7de9b16 100644
> >> --- a/drivers/iio/adc/stm32-adc.c
> >> +++ b/drivers/iio/adc/stm32-adc.c
> >> @@ -21,6 +21,7 @@
> >>   #include <linux/io.h>
> >>   #include <linux/iopoll.h>
> >>   #include <linux/module.h>
> >> +#include <linux/nvmem-consumer.h>
> >>   #include <linux/platform_device.h>
> >>   #include <linux/pm_runtime.h>
> >>   #include <linux/of.h>
> >> @@ -42,6 +43,7 @@
> >>   #define STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
> >>   #define STM32_ADC_HW_STOP_DELAY_MS	100
> >>   #define STM32_ADC_CHAN_NONE		-1
> >> +#define STM32_ADC_VREFINT_VOLTAGE	3300
> >>   
> >>   #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
> >>   
> >> @@ -79,6 +81,7 @@ enum stm32_adc_extsel {
> >>   };
> >>   
> >>   enum stm32_adc_int_ch {
> >> +	STM32_ADC_INT_CH_NONE = -1,
> >>   	STM32_ADC_INT_CH_VDDCORE,
> >>   	STM32_ADC_INT_CH_VREFINT,
> >>   	STM32_ADC_INT_CH_VBAT,
> >> @@ -137,6 +140,16 @@ struct stm32_adc_regs {
> >>   	int shift;
> >>   };
> >>   
> >> +/**
> >> + * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data
> >> + * @vrefint_cal:	vrefint calibration value from nvmem
> >> + * @vrefint_data:	vrefint actual value
> >> + */
> >> +struct stm32_adc_vrefint {
> >> +	u32 vrefint_cal;
> >> +	u32 vrefint_data;
> >> +};
> >> +
> >>   /**
> >>    * struct stm32_adc_regspec - stm32 registers definition
> >>    * @dr:			data register offset
> >> @@ -186,6 +199,7 @@ struct stm32_adc;
> >>    * @unprepare:		optional unprepare routine (disable, power-down)
> >>    * @irq_clear:		routine to clear irqs
> >>    * @smp_cycles:		programmable sampling time (ADC clock cycles)
> >> + * @ts_vrefint_ns:	vrefint minimum sampling time in ns
> >>    */
> >>   struct stm32_adc_cfg {
> >>   	const struct stm32_adc_regspec	*regs;
> >> @@ -199,6 +213,7 @@ struct stm32_adc_cfg {
> >>   	void (*unprepare)(struct iio_dev *);
> >>   	void (*irq_clear)(struct iio_dev *indio_dev, u32 msk);
> >>   	const unsigned int *smp_cycles;
> >> +	const unsigned int ts_vrefint_ns;
> >>   };
> >>   
> >>   /**
> >> @@ -223,6 +238,7 @@ struct stm32_adc_cfg {
> >>    * @pcsel:		bitmask to preselect channels on some devices
> >>    * @smpr_val:		sampling time settings (e.g. smpr1 / smpr2)
> >>    * @cal:		optional calibration data on some devices
> >> + * @vrefint:		internal reference voltage data
> >>    * @chan_name:		channel name array
> >>    * @num_diff:		number of differential channels
> >>    * @int_ch:		internal channel indexes array
> >> @@ -248,6 +264,7 @@ struct stm32_adc {
> >>   	u32			pcsel;
> >>   	u32			smpr_val[2];
> >>   	struct stm32_adc_calib	cal;
> >> +	struct stm32_adc_vrefint vrefint;
> >>   	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
> >>   	u32			num_diff;
> >>   	int			int_ch[STM32_ADC_INT_CH_NB];
> >> @@ -1331,15 +1348,35 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
> >>   			ret = stm32_adc_single_conv(indio_dev, chan, val);
> >>   		else
> >>   			ret = -EINVAL;
> >> +
> >> +		/* If channel mask corresponds to vrefint, store data */
> >> +		if (adc->int_ch[STM32_ADC_INT_CH_VREFINT] == chan->channel)
> >> +			adc->vrefint.vrefint_data = *val;
> >> +
> >>   		iio_device_release_direct_mode(indio_dev);
> >>   		return ret;
> >>   
> >>   	case IIO_CHAN_INFO_SCALE:
> >>   		if (chan->differential) {
> >> -			*val = adc->common->vref_mv * 2;
> >> +			if (adc->vrefint.vrefint_data &&
> >> +			    adc->vrefint.vrefint_cal) {
> >> +				*val = STM32_ADC_VREFINT_VOLTAGE * 2 *
> >> +				       adc->vrefint.vrefint_cal /
> >> +				       adc->vrefint.vrefint_data;  
> > 
> > Ah.. Dynamic scale.  This is always awkward when it occurs.
> > Given most / possibly all userspace software assumes a pseudo static scale
> > (not data dependent) we normally hide this by doing the maths internal to the
> > driver - sometimes meaning we need to present the particular channel as processed
> > not raw.
> > 
> > Is the expectation here that vrefint_data is actually very nearly constant? If
> > so then what you have here may be fine as anyone not aware the scale might change
> > will get very nearly the right value anyway.
> >   
> 
> The need here is to compare the measured value of vrefint with the 
> calibrated value saved in non volatile memory. The ratio between these 
> two values can be used as a correction factor for the acquisitions on 
> all other channels.
> 
> The vrefint data is expected to be close to the saved vrefint 
> calibration value, and it should not vary strongly over time.
> So, yes, we can indeed consider the scale as a pseudo constant. If the 
> scale is not updated, the deviation with actual value should remain 
> limited, as well.

Ok, so in that case we could probably get away with having it as you have
here, though for maximum precision we'd need userspace to occasionally check
the scale.

> 
> You suggest above to hide scale tuning through processed channels.
> If I follow this logic, when vrefint channel is available, all channels 
> should be defined as processed channels (excepted vrefint channel)
> In this case no scale is exposed for these channels, and the vrefint 
> calibration ratio can be used to provide converted data directly.
> Do you prefer this implementation ?

> 
> In this case I wonder how buffered data have to be managed. These data 
> are still provided as raw data, but the scale factor is not more 
> available to convert them. I guess that these data have to be converted 
> internally also, either in dma callback or irq handler.
> Is this correct ?

This is one of the holes in what IIO does today.  Without meta data in the
buffer (which is hard to define in a clean fashion) it is hard to have
a compact representation of the data in the presence of dynamic scaling.
The vast majority of devices don't inherently support such scaling so
this is only occasionally a problem. 

To support this at the moment you would indeed need to scale the data
before pushing it to the buffer which is obviously really ugly.

My gut feeling here is there are three possible approaches.

1) Ignore the dynamic nature of the calibration and pretend it's static.
2) Add an explicit 'calibration' sysfs attribute.
   This is a fairly common model for other sensor types which don't do
   dynamic calibration but instead require to you to start some special
   calibration sequence.
   As the calibration is not updated, except on explicit userspace action
   we can assume that the scale is static unless userspace is aware of
   the dynamic aspect.
3) Add a userspace control to turn on dynamic calibration.  That makes it
   opt in.  Everything will work reasonably well without it turned on
   as we'll hopefully have a static estimate of scale which is good enough.
   If aware software is using the device, it can enable this mode and
   sample the scale as often as it wants to.

I slightly favour option 3.  What do you think?  If we ever figure out
the meta data question for buffered case then we can make that work on top
of this.

Jonathan
> 
> Regards
> Olivier
> 
> >> +			} else {
> >> +				*val = adc->common->vref_mv * 2;
> >> +			}
> >>   			*val2 = chan->scan_type.realbits;
> >>   		} else {
> >> -			*val = adc->common->vref_mv;
> >> +			/* Use vrefint data if available */
> >> +			if (adc->vrefint.vrefint_data &&
> >> +			    adc->vrefint.vrefint_cal) {
> >> +				*val = STM32_ADC_VREFINT_VOLTAGE *
> >> +				       adc->vrefint.vrefint_cal /
> >> +				       adc->vrefint.vrefint_data;
> >> +			} else {
> >> +				*val = adc->common->vref_mv;
> >> +			}
> >>   			*val2 = chan->scan_type.realbits;
> >>   		}
> >>   		return IIO_VAL_FRACTIONAL_LOG2;
> >> @@ -1907,6 +1944,35 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
> >>   	return scan_index;
> >>   }
> >>   
> >> +static int stm32_adc_get_int_ch(struct iio_dev *indio_dev, const char *ch_name,
> >> +				int chan)  
> > 
> > Naming would suggest to me that it would return a channel rather than setting it
> > inside adc->int_ch[i]  Perhaps something like st32_adc_populate_int_ch() ?
> > 
> >   
> >> +{
> >> +	struct stm32_adc *adc = iio_priv(indio_dev);
> >> +	u16 vrefint;
> >> +	int i, ret;
> >> +
> >> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> >> +		if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
> >> +			adc->int_ch[i] = chan;
> >> +			/* If channel is vrefint get calibration data. */
> >> +			if (stm32_adc_ic[i].idx == STM32_ADC_INT_CH_VREFINT) {  
> > 
> > I would reduce indentation by reversing the logic.
> > 
> > 			if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT)
> > 				continue;
> > 
> > 			ret =  
> >> +				ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint);
> >> +				if (ret && ret != -ENOENT && ret != -EOPNOTSUPP) {
> >> +					dev_err(&indio_dev->dev, "nvmem access error %d\n", ret);
> >> +					return ret;
> >> +				}
> >> +				if (ret == -ENOENT)
> >> +					dev_dbg(&indio_dev->dev,
> >> +						"vrefint calibration not found\n");
> >> +				else
> >> +					adc->vrefint.vrefint_cal = vrefint;
> >> +			}
> >> +		}
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >>   static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
> >>   				       struct stm32_adc *adc,
> >>   				       struct iio_chan_spec *channels)
> >> @@ -1938,10 +2004,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
> >>   				return -EINVAL;
> >>   			}
> >>   			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
> >> -			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> >> -				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
> >> -					adc->int_ch[i] = val;
> >> -			}
> >> +			ret = stm32_adc_get_int_ch(indio_dev, name, val);
> >> +			if (ret)
> >> +				goto err;
> >>   		} else if (ret != -EINVAL) {
> >>   			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
> >>   			goto err;
> >> @@ -2044,6 +2109,16 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
> >>   		 */
> >>   		of_property_read_u32_index(node, "st,min-sample-time-nsecs",
> >>   					   i, &smp);
> >> +
> >> +		/*
> >> +		 * For vrefint channel, ensure that the sampling time cannot
> >> +		 * be lower than the one specified in the datasheet
> >> +		 */
> >> +		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
> >> +		    smp < adc->cfg->ts_vrefint_ns) {
> >> +			smp = adc->cfg->ts_vrefint_ns;
> >> +		}  
> > 
> > 		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT])
> > 			smp = max(smp, adc->cfg->ts_vrefint_ns);
> >   
> >> +
> >>   		/* Prepare sampling time settings */
> >>   		stm32_adc_smpr_init(adc, channels[i].channel, smp);
> >>   	}
> >> @@ -2350,6 +2425,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
> >>   	.unprepare = stm32h7_adc_unprepare,
> >>   	.smp_cycles = stm32h7_adc_smp_cycles,
> >>   	.irq_clear = stm32h7_adc_irq_clear,
> >> +	.ts_vrefint_ns = 4300,
> >>   };
> >>   
> >>   static const struct of_device_id stm32_adc_of_match[] = {  
> >   


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 1/7] dt-bindings: iio: adc: add generic channel binding
  2021-09-08 15:54   ` Olivier Moysan
  (?)
@ 2021-09-21 17:08     ` Rob Herring
  -1 siblings, 0 replies; 53+ messages in thread
From: Rob Herring @ 2021-09-21 17:08 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	alsa-devel, devicetree, linux-arm-kernel, linux-iio,
	linux-kernel, linux-stm32

On Wed, Sep 08, 2021 at 05:54:46PM +0200, Olivier Moysan wrote:
> Add ADC generic channel binding. This binding should
> be used as an alternate to legacy channel properties
> whenever possible.
> ADC generic channel binding allows to identify supported
> internal channels through the following reserved label names:
> "vddcore", "vrefint" and "vbat".
> This binding also allows to set a different sampling time
> for each channel.
> 
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
> ---
> Note: The schema here is too permissive as either legacy or generic
> channels properties are required. These properties are mutually
> exclusive, however all attempts to describe this constraint are
> failing. In particular the following schemas and their variants have
> shown unsucessful.
> 
> oneOf:
>   - anyOf:
> 	- required:
> 		- st,adc-channels
> 	- required:
> 		- st,adc-diff-channels
>   - anyOf:
> 	- required:
> 		- $nodename
> 
> - if:
>   patternProperties:
>     "^channel@([0-9]|1[0-9])$":
>       type: object

This is considered true when you don't match this property. You would 
need 'required', but that doesn't work for patterns. That's a 
json-schema limitation that's on their radar to address.

My advice is just mark the legacy nodes 'deprecated'. That doesn't do 
anything yet, but I plan to add a 'disallow deprecated properties/nodes'
mode.

> then:
>   properties:
>     st,adc-channels: false
>     st,adc-diff-channels: false
> else:
>   - anyOf:
>       - required:
>           - st,adc-channels
>       - required:
>           - st,adc-diff-channels
> ---
>  .../bindings/iio/adc/st,stm32-adc.yaml        | 100 ++++++++++++++++--
>  1 file changed, 93 insertions(+), 7 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
> index a58334c3bb76..a1f6cbe144ba 100644
> --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
> @@ -222,6 +222,12 @@ patternProperties:
>        '#io-channel-cells':
>          const: 1
>  
> +      '#address-cells':
> +        const: 1
> +
> +      '#size-cells':
> +        const: 0
> +
>        interrupts:
>          description: |
>            IRQ Line for the ADC instance. Valid values are:
> @@ -265,7 +271,9 @@ patternProperties:
>            <vinp vinn>, <vinp vinn>,... vinp and vinn are numbered from 0 to 19.
>  
>            Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is
> -          required. Both properties can be used together. Some channels can be
> +          required if no adc generic channel is defined. These legacy channel
> +          properties are exclusive with adc generic channel bindings.
> +          Both properties can be used together. Some channels can be
>            used as single-ended and some other ones as differential (mixed). But
>            channels can't be configured both as single-ended and differential.
>          $ref: /schemas/types.yaml#/definitions/uint32-matrix
> @@ -290,6 +298,44 @@ patternProperties:
>            each channel.
>          $ref: /schemas/types.yaml#/definitions/uint32-array
>  
> +    patternProperties:
> +      "^channel@([0-9]|1[0-9])$":
> +        type: object
> +        $ref: "adc.yaml"
> +        description: |
> +          Represents the external channels which are connected to the ADC.
> +
> +        properties:
> +          reg:
> +            items:
> +              minimum: 0
> +              maximum: 19
> +
> +          label:
> +            description: |
> +              Unique name to identify which channel this is.
> +              Reserved label names "vddcore", "vrefint" and "vbat"
> +              are used to identify internal channels with matching names.
> +
> +          diff-channels:
> +            $ref: /schemas/types.yaml#/definitions/uint32-array
> +            items:
> +              minimum: 0
> +              maximum: 19
> +
> +          st,min-sample-time-nsecs:
> +            $ref: /schemas/types.yaml#/definitions/uint32
> +            description: |
> +              Minimum sampling time in nanoseconds. Depending on hardware (board)
> +              e.g. high/low analog input source impedance, fine tune of ADC
> +              sampling time may be recommended.
> +
> +        required:
> +          - reg
> +
> +    dependencies:
> +      $nodename: [ '#address-cells', '#size-cells' ]

Drop this. $nodename is not a real property.

> +
>      allOf:
>        - if:
>            properties:
> @@ -369,12 +415,6 @@ patternProperties:
>  
>      additionalProperties: false
>  
> -    anyOf:
> -      - required:
> -          - st,adc-channels
> -      - required:
> -          - st,adc-diff-channels
> -
>      required:
>        - compatible
>        - reg
> @@ -451,4 +491,50 @@ examples:
>          // other adc child node follow...
>        };
>  
> +  - |
> +    // Example 3: with stm32mp157c to setup ADC2 with:
> +    // - internal channels 13, 14, 15.
> +      #include <dt-bindings/interrupt-controller/arm-gic.h>
> +      #include <dt-bindings/clock/stm32mp1-clks.h>
> +      adc122: adc@48003000 {
> +        compatible = "st,stm32mp1-adc-core";
> +        reg = <0x48003000 0x400>;
> +        interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
> +                     <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
> +        clocks = <&rcc ADC12>, <&rcc ADC12_K>;
> +        clock-names = "bus", "adc";
> +        booster-supply = <&booster>;
> +        vdd-supply = <&vdd>;
> +        vdda-supply = <&vdda>;
> +        vref-supply = <&vref>;
> +        st,syscfg = <&syscfg>;
> +        interrupt-controller;
> +        #interrupt-cells = <1>;
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +        adc@100 {
> +          compatible = "st,stm32mp1-adc";
> +          #io-channel-cells = <1>;
> +          reg = <0x100>;
> +          interrupts = <1>;
> +          #address-cells = <1>;
> +          #size-cells = <0>;
> +          channel@13 {
> +            reg = <13>;
> +            label = "vrefint";
> +            st,min-sample-time-nsecs = <9000>;
> +          };
> +          channel@14 {
> +            reg = <14>;
> +            label = "vddcore";
> +            st,min-sample-time-nsecs = <9000>;
> +          };
> +          channel@15 {
> +            reg = <15>;
> +            label = "vbat";
> +            st,min-sample-time-nsecs = <9000>;
> +          };
> +        };
> +      };
> +
>  ...
> -- 
> 2.17.1
> 
> 

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 1/7] dt-bindings: iio: adc: add generic channel binding
@ 2021-09-21 17:08     ` Rob Herring
  0 siblings, 0 replies; 53+ messages in thread
From: Rob Herring @ 2021-09-21 17:08 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: devicetree, alsa-devel, Lars-Peter Clausen, linux-iio,
	Alexandre Torgue, linux-kernel, linux-arm-kernel,
	Maxime Coquelin, Arnaud Pouliquen, Fabrice Gasnier, linux-stm32,
	Jonathan Cameron

On Wed, Sep 08, 2021 at 05:54:46PM +0200, Olivier Moysan wrote:
> Add ADC generic channel binding. This binding should
> be used as an alternate to legacy channel properties
> whenever possible.
> ADC generic channel binding allows to identify supported
> internal channels through the following reserved label names:
> "vddcore", "vrefint" and "vbat".
> This binding also allows to set a different sampling time
> for each channel.
> 
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
> ---
> Note: The schema here is too permissive as either legacy or generic
> channels properties are required. These properties are mutually
> exclusive, however all attempts to describe this constraint are
> failing. In particular the following schemas and their variants have
> shown unsucessful.
> 
> oneOf:
>   - anyOf:
> 	- required:
> 		- st,adc-channels
> 	- required:
> 		- st,adc-diff-channels
>   - anyOf:
> 	- required:
> 		- $nodename
> 
> - if:
>   patternProperties:
>     "^channel@([0-9]|1[0-9])$":
>       type: object

This is considered true when you don't match this property. You would 
need 'required', but that doesn't work for patterns. That's a 
json-schema limitation that's on their radar to address.

My advice is just mark the legacy nodes 'deprecated'. That doesn't do 
anything yet, but I plan to add a 'disallow deprecated properties/nodes'
mode.

> then:
>   properties:
>     st,adc-channels: false
>     st,adc-diff-channels: false
> else:
>   - anyOf:
>       - required:
>           - st,adc-channels
>       - required:
>           - st,adc-diff-channels
> ---
>  .../bindings/iio/adc/st,stm32-adc.yaml        | 100 ++++++++++++++++--
>  1 file changed, 93 insertions(+), 7 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
> index a58334c3bb76..a1f6cbe144ba 100644
> --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
> @@ -222,6 +222,12 @@ patternProperties:
>        '#io-channel-cells':
>          const: 1
>  
> +      '#address-cells':
> +        const: 1
> +
> +      '#size-cells':
> +        const: 0
> +
>        interrupts:
>          description: |
>            IRQ Line for the ADC instance. Valid values are:
> @@ -265,7 +271,9 @@ patternProperties:
>            <vinp vinn>, <vinp vinn>,... vinp and vinn are numbered from 0 to 19.
>  
>            Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is
> -          required. Both properties can be used together. Some channels can be
> +          required if no adc generic channel is defined. These legacy channel
> +          properties are exclusive with adc generic channel bindings.
> +          Both properties can be used together. Some channels can be
>            used as single-ended and some other ones as differential (mixed). But
>            channels can't be configured both as single-ended and differential.
>          $ref: /schemas/types.yaml#/definitions/uint32-matrix
> @@ -290,6 +298,44 @@ patternProperties:
>            each channel.
>          $ref: /schemas/types.yaml#/definitions/uint32-array
>  
> +    patternProperties:
> +      "^channel@([0-9]|1[0-9])$":
> +        type: object
> +        $ref: "adc.yaml"
> +        description: |
> +          Represents the external channels which are connected to the ADC.
> +
> +        properties:
> +          reg:
> +            items:
> +              minimum: 0
> +              maximum: 19
> +
> +          label:
> +            description: |
> +              Unique name to identify which channel this is.
> +              Reserved label names "vddcore", "vrefint" and "vbat"
> +              are used to identify internal channels with matching names.
> +
> +          diff-channels:
> +            $ref: /schemas/types.yaml#/definitions/uint32-array
> +            items:
> +              minimum: 0
> +              maximum: 19
> +
> +          st,min-sample-time-nsecs:
> +            $ref: /schemas/types.yaml#/definitions/uint32
> +            description: |
> +              Minimum sampling time in nanoseconds. Depending on hardware (board)
> +              e.g. high/low analog input source impedance, fine tune of ADC
> +              sampling time may be recommended.
> +
> +        required:
> +          - reg
> +
> +    dependencies:
> +      $nodename: [ '#address-cells', '#size-cells' ]

Drop this. $nodename is not a real property.

> +
>      allOf:
>        - if:
>            properties:
> @@ -369,12 +415,6 @@ patternProperties:
>  
>      additionalProperties: false
>  
> -    anyOf:
> -      - required:
> -          - st,adc-channels
> -      - required:
> -          - st,adc-diff-channels
> -
>      required:
>        - compatible
>        - reg
> @@ -451,4 +491,50 @@ examples:
>          // other adc child node follow...
>        };
>  
> +  - |
> +    // Example 3: with stm32mp157c to setup ADC2 with:
> +    // - internal channels 13, 14, 15.
> +      #include <dt-bindings/interrupt-controller/arm-gic.h>
> +      #include <dt-bindings/clock/stm32mp1-clks.h>
> +      adc122: adc@48003000 {
> +        compatible = "st,stm32mp1-adc-core";
> +        reg = <0x48003000 0x400>;
> +        interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
> +                     <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
> +        clocks = <&rcc ADC12>, <&rcc ADC12_K>;
> +        clock-names = "bus", "adc";
> +        booster-supply = <&booster>;
> +        vdd-supply = <&vdd>;
> +        vdda-supply = <&vdda>;
> +        vref-supply = <&vref>;
> +        st,syscfg = <&syscfg>;
> +        interrupt-controller;
> +        #interrupt-cells = <1>;
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +        adc@100 {
> +          compatible = "st,stm32mp1-adc";
> +          #io-channel-cells = <1>;
> +          reg = <0x100>;
> +          interrupts = <1>;
> +          #address-cells = <1>;
> +          #size-cells = <0>;
> +          channel@13 {
> +            reg = <13>;
> +            label = "vrefint";
> +            st,min-sample-time-nsecs = <9000>;
> +          };
> +          channel@14 {
> +            reg = <14>;
> +            label = "vddcore";
> +            st,min-sample-time-nsecs = <9000>;
> +          };
> +          channel@15 {
> +            reg = <15>;
> +            label = "vbat";
> +            st,min-sample-time-nsecs = <9000>;
> +          };
> +        };
> +      };
> +
>  ...
> -- 
> 2.17.1
> 
> 

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 1/7] dt-bindings: iio: adc: add generic channel binding
@ 2021-09-21 17:08     ` Rob Herring
  0 siblings, 0 replies; 53+ messages in thread
From: Rob Herring @ 2021-09-21 17:08 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Jonathan Cameron, Lars-Peter Clausen, Maxime Coquelin,
	alsa-devel, devicetree, linux-arm-kernel, linux-iio,
	linux-kernel, linux-stm32

On Wed, Sep 08, 2021 at 05:54:46PM +0200, Olivier Moysan wrote:
> Add ADC generic channel binding. This binding should
> be used as an alternate to legacy channel properties
> whenever possible.
> ADC generic channel binding allows to identify supported
> internal channels through the following reserved label names:
> "vddcore", "vrefint" and "vbat".
> This binding also allows to set a different sampling time
> for each channel.
> 
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
> ---
> Note: The schema here is too permissive as either legacy or generic
> channels properties are required. These properties are mutually
> exclusive, however all attempts to describe this constraint are
> failing. In particular the following schemas and their variants have
> shown unsucessful.
> 
> oneOf:
>   - anyOf:
> 	- required:
> 		- st,adc-channels
> 	- required:
> 		- st,adc-diff-channels
>   - anyOf:
> 	- required:
> 		- $nodename
> 
> - if:
>   patternProperties:
>     "^channel@([0-9]|1[0-9])$":
>       type: object

This is considered true when you don't match this property. You would 
need 'required', but that doesn't work for patterns. That's a 
json-schema limitation that's on their radar to address.

My advice is just mark the legacy nodes 'deprecated'. That doesn't do 
anything yet, but I plan to add a 'disallow deprecated properties/nodes'
mode.

> then:
>   properties:
>     st,adc-channels: false
>     st,adc-diff-channels: false
> else:
>   - anyOf:
>       - required:
>           - st,adc-channels
>       - required:
>           - st,adc-diff-channels
> ---
>  .../bindings/iio/adc/st,stm32-adc.yaml        | 100 ++++++++++++++++--
>  1 file changed, 93 insertions(+), 7 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
> index a58334c3bb76..a1f6cbe144ba 100644
> --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
> @@ -222,6 +222,12 @@ patternProperties:
>        '#io-channel-cells':
>          const: 1
>  
> +      '#address-cells':
> +        const: 1
> +
> +      '#size-cells':
> +        const: 0
> +
>        interrupts:
>          description: |
>            IRQ Line for the ADC instance. Valid values are:
> @@ -265,7 +271,9 @@ patternProperties:
>            <vinp vinn>, <vinp vinn>,... vinp and vinn are numbered from 0 to 19.
>  
>            Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is
> -          required. Both properties can be used together. Some channels can be
> +          required if no adc generic channel is defined. These legacy channel
> +          properties are exclusive with adc generic channel bindings.
> +          Both properties can be used together. Some channels can be
>            used as single-ended and some other ones as differential (mixed). But
>            channels can't be configured both as single-ended and differential.
>          $ref: /schemas/types.yaml#/definitions/uint32-matrix
> @@ -290,6 +298,44 @@ patternProperties:
>            each channel.
>          $ref: /schemas/types.yaml#/definitions/uint32-array
>  
> +    patternProperties:
> +      "^channel@([0-9]|1[0-9])$":
> +        type: object
> +        $ref: "adc.yaml"
> +        description: |
> +          Represents the external channels which are connected to the ADC.
> +
> +        properties:
> +          reg:
> +            items:
> +              minimum: 0
> +              maximum: 19
> +
> +          label:
> +            description: |
> +              Unique name to identify which channel this is.
> +              Reserved label names "vddcore", "vrefint" and "vbat"
> +              are used to identify internal channels with matching names.
> +
> +          diff-channels:
> +            $ref: /schemas/types.yaml#/definitions/uint32-array
> +            items:
> +              minimum: 0
> +              maximum: 19
> +
> +          st,min-sample-time-nsecs:
> +            $ref: /schemas/types.yaml#/definitions/uint32
> +            description: |
> +              Minimum sampling time in nanoseconds. Depending on hardware (board)
> +              e.g. high/low analog input source impedance, fine tune of ADC
> +              sampling time may be recommended.
> +
> +        required:
> +          - reg
> +
> +    dependencies:
> +      $nodename: [ '#address-cells', '#size-cells' ]

Drop this. $nodename is not a real property.

> +
>      allOf:
>        - if:
>            properties:
> @@ -369,12 +415,6 @@ patternProperties:
>  
>      additionalProperties: false
>  
> -    anyOf:
> -      - required:
> -          - st,adc-channels
> -      - required:
> -          - st,adc-diff-channels
> -
>      required:
>        - compatible
>        - reg
> @@ -451,4 +491,50 @@ examples:
>          // other adc child node follow...
>        };
>  
> +  - |
> +    // Example 3: with stm32mp157c to setup ADC2 with:
> +    // - internal channels 13, 14, 15.
> +      #include <dt-bindings/interrupt-controller/arm-gic.h>
> +      #include <dt-bindings/clock/stm32mp1-clks.h>
> +      adc122: adc@48003000 {
> +        compatible = "st,stm32mp1-adc-core";
> +        reg = <0x48003000 0x400>;
> +        interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
> +                     <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
> +        clocks = <&rcc ADC12>, <&rcc ADC12_K>;
> +        clock-names = "bus", "adc";
> +        booster-supply = <&booster>;
> +        vdd-supply = <&vdd>;
> +        vdda-supply = <&vdda>;
> +        vref-supply = <&vref>;
> +        st,syscfg = <&syscfg>;
> +        interrupt-controller;
> +        #interrupt-cells = <1>;
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +        adc@100 {
> +          compatible = "st,stm32mp1-adc";
> +          #io-channel-cells = <1>;
> +          reg = <0x100>;
> +          interrupts = <1>;
> +          #address-cells = <1>;
> +          #size-cells = <0>;
> +          channel@13 {
> +            reg = <13>;
> +            label = "vrefint";
> +            st,min-sample-time-nsecs = <9000>;
> +          };
> +          channel@14 {
> +            reg = <14>;
> +            label = "vddcore";
> +            st,min-sample-time-nsecs = <9000>;
> +          };
> +          channel@15 {
> +            reg = <15>;
> +            label = "vbat";
> +            st,min-sample-time-nsecs = <9000>;
> +          };
> +        };
> +      };
> +
>  ...
> -- 
> 2.17.1
> 
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 2/7] dt-bindings: iio: adc: add nvmem support for vrefint internal channel
  2021-09-08 15:54   ` Olivier Moysan
  (?)
@ 2021-09-21 17:10     ` Rob Herring
  -1 siblings, 0 replies; 53+ messages in thread
From: Rob Herring @ 2021-09-21 17:10 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: linux-arm-kernel, Alexandre Torgue, Jonathan Cameron,
	Rob Herring, linux-stm32, Maxime Coquelin, linux-iio, devicetree,
	linux-kernel, Fabrice Gasnier, alsa-devel, Arnaud Pouliquen,
	Lars-Peter Clausen

On Wed, 08 Sep 2021 17:54:47 +0200, Olivier Moysan wrote:
> Add support of nvmem. This allows to retrieve calibration data from OTP
> for vrefint internal channel.
> 
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
> ---
>  .../devicetree/bindings/iio/adc/st,stm32-adc.yaml         | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 

Acked-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 2/7] dt-bindings: iio: adc: add nvmem support for vrefint internal channel
@ 2021-09-21 17:10     ` Rob Herring
  0 siblings, 0 replies; 53+ messages in thread
From: Rob Herring @ 2021-09-21 17:10 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: devicetree, alsa-devel, Lars-Peter Clausen, linux-iio,
	Alexandre Torgue, linux-kernel, Rob Herring, linux-arm-kernel,
	Maxime Coquelin, Arnaud Pouliquen, Fabrice Gasnier, linux-stm32,
	Jonathan Cameron

On Wed, 08 Sep 2021 17:54:47 +0200, Olivier Moysan wrote:
> Add support of nvmem. This allows to retrieve calibration data from OTP
> for vrefint internal channel.
> 
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
> ---
>  .../devicetree/bindings/iio/adc/st,stm32-adc.yaml         | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 

Acked-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 2/7] dt-bindings: iio: adc: add nvmem support for vrefint internal channel
@ 2021-09-21 17:10     ` Rob Herring
  0 siblings, 0 replies; 53+ messages in thread
From: Rob Herring @ 2021-09-21 17:10 UTC (permalink / raw)
  To: Olivier Moysan
  Cc: linux-arm-kernel, Alexandre Torgue, Jonathan Cameron,
	Rob Herring, linux-stm32, Maxime Coquelin, linux-iio, devicetree,
	linux-kernel, Fabrice Gasnier, alsa-devel, Arnaud Pouliquen,
	Lars-Peter Clausen

On Wed, 08 Sep 2021 17:54:47 +0200, Olivier Moysan wrote:
> Add support of nvmem. This allows to retrieve calibration data from OTP
> for vrefint internal channel.
> 
> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
> ---
>  .../devicetree/bindings/iio/adc/st,stm32-adc.yaml         | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 

Acked-by: Rob Herring <robh@kernel.org>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 6/7] iio: adc: stm32-adc: add vrefint calibration support
  2021-09-18 18:42         ` Jonathan Cameron
@ 2021-09-22  7:53           ` Olivier MOYSAN
  -1 siblings, 0 replies; 53+ messages in thread
From: Olivier MOYSAN @ 2021-09-22  7:53 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Lars-Peter Clausen, Maxime Coquelin, Rob Herring, devicetree,
	linux-arm-kernel, linux-iio, linux-kernel, linux-stm32

Hi Jonathan,

On 9/18/21 8:42 PM, Jonathan Cameron wrote:
> On Wed, 15 Sep 2021 12:02:45 +0200
> Olivier MOYSAN <olivier.moysan@foss.st.com> wrote:
> 
>> Hi Jonathan,
>>
>> On 9/11/21 6:28 PM, Jonathan Cameron wrote:
>>> On Wed, 8 Sep 2021 17:54:51 +0200
>>> Olivier Moysan <olivier.moysan@foss.st.com> wrote:
>>>    
>>>> Add support of vrefint calibration.
>>>> If a channel is labeled as vrefint, get vrefint calibration
>>>> from non volatile memory for this channel.
>>>> A conversion on vrefint channel allows to update scale
>>>> factor according to vrefint deviation, compared to vrefint
>>>> calibration value.
>>>
>>> As I mention inline, whilst technically the ABI doesn't demand it
>>> the expectation of much of userspace software is that _scale is
>>> pseudo constant - that is it doesn't tend to change very often and when
>>> it does it's normally because someone deliberately made it change.
>>> As such most software reads it just once.
>>>
>>> Normally we work around this by applying the maths in kernel and
>>> not exposing the scale at all. Is this something that could be done here?
>>>
>>> Jonathan
>>>    
>>>>
>>>> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
>>>> ---
>>>>    drivers/iio/adc/stm32-adc.c | 88 ++++++++++++++++++++++++++++++++++---
>>>>    1 file changed, 82 insertions(+), 6 deletions(-)
>>>>
>>>> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
>>>> index ef3d2af98025..9e52a7de9b16 100644
>>>> --- a/drivers/iio/adc/stm32-adc.c
>>>> +++ b/drivers/iio/adc/stm32-adc.c
>>>> @@ -21,6 +21,7 @@
>>>>    #include <linux/io.h>
>>>>    #include <linux/iopoll.h>
>>>>    #include <linux/module.h>
>>>> +#include <linux/nvmem-consumer.h>
>>>>    #include <linux/platform_device.h>
>>>>    #include <linux/pm_runtime.h>
>>>>    #include <linux/of.h>
>>>> @@ -42,6 +43,7 @@
>>>>    #define STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
>>>>    #define STM32_ADC_HW_STOP_DELAY_MS	100
>>>>    #define STM32_ADC_CHAN_NONE		-1
>>>> +#define STM32_ADC_VREFINT_VOLTAGE	3300
>>>>    
>>>>    #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
>>>>    
>>>> @@ -79,6 +81,7 @@ enum stm32_adc_extsel {
>>>>    };
>>>>    
>>>>    enum stm32_adc_int_ch {
>>>> +	STM32_ADC_INT_CH_NONE = -1,
>>>>    	STM32_ADC_INT_CH_VDDCORE,
>>>>    	STM32_ADC_INT_CH_VREFINT,
>>>>    	STM32_ADC_INT_CH_VBAT,
>>>> @@ -137,6 +140,16 @@ struct stm32_adc_regs {
>>>>    	int shift;
>>>>    };
>>>>    
>>>> +/**
>>>> + * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data
>>>> + * @vrefint_cal:	vrefint calibration value from nvmem
>>>> + * @vrefint_data:	vrefint actual value
>>>> + */
>>>> +struct stm32_adc_vrefint {
>>>> +	u32 vrefint_cal;
>>>> +	u32 vrefint_data;
>>>> +};
>>>> +
>>>>    /**
>>>>     * struct stm32_adc_regspec - stm32 registers definition
>>>>     * @dr:			data register offset
>>>> @@ -186,6 +199,7 @@ struct stm32_adc;
>>>>     * @unprepare:		optional unprepare routine (disable, power-down)
>>>>     * @irq_clear:		routine to clear irqs
>>>>     * @smp_cycles:		programmable sampling time (ADC clock cycles)
>>>> + * @ts_vrefint_ns:	vrefint minimum sampling time in ns
>>>>     */
>>>>    struct stm32_adc_cfg {
>>>>    	const struct stm32_adc_regspec	*regs;
>>>> @@ -199,6 +213,7 @@ struct stm32_adc_cfg {
>>>>    	void (*unprepare)(struct iio_dev *);
>>>>    	void (*irq_clear)(struct iio_dev *indio_dev, u32 msk);
>>>>    	const unsigned int *smp_cycles;
>>>> +	const unsigned int ts_vrefint_ns;
>>>>    };
>>>>    
>>>>    /**
>>>> @@ -223,6 +238,7 @@ struct stm32_adc_cfg {
>>>>     * @pcsel:		bitmask to preselect channels on some devices
>>>>     * @smpr_val:		sampling time settings (e.g. smpr1 / smpr2)
>>>>     * @cal:		optional calibration data on some devices
>>>> + * @vrefint:		internal reference voltage data
>>>>     * @chan_name:		channel name array
>>>>     * @num_diff:		number of differential channels
>>>>     * @int_ch:		internal channel indexes array
>>>> @@ -248,6 +264,7 @@ struct stm32_adc {
>>>>    	u32			pcsel;
>>>>    	u32			smpr_val[2];
>>>>    	struct stm32_adc_calib	cal;
>>>> +	struct stm32_adc_vrefint vrefint;
>>>>    	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
>>>>    	u32			num_diff;
>>>>    	int			int_ch[STM32_ADC_INT_CH_NB];
>>>> @@ -1331,15 +1348,35 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
>>>>    			ret = stm32_adc_single_conv(indio_dev, chan, val);
>>>>    		else
>>>>    			ret = -EINVAL;
>>>> +
>>>> +		/* If channel mask corresponds to vrefint, store data */
>>>> +		if (adc->int_ch[STM32_ADC_INT_CH_VREFINT] == chan->channel)
>>>> +			adc->vrefint.vrefint_data = *val;
>>>> +
>>>>    		iio_device_release_direct_mode(indio_dev);
>>>>    		return ret;
>>>>    
>>>>    	case IIO_CHAN_INFO_SCALE:
>>>>    		if (chan->differential) {
>>>> -			*val = adc->common->vref_mv * 2;
>>>> +			if (adc->vrefint.vrefint_data &&
>>>> +			    adc->vrefint.vrefint_cal) {
>>>> +				*val = STM32_ADC_VREFINT_VOLTAGE * 2 *
>>>> +				       adc->vrefint.vrefint_cal /
>>>> +				       adc->vrefint.vrefint_data;
>>>
>>> Ah.. Dynamic scale.  This is always awkward when it occurs.
>>> Given most / possibly all userspace software assumes a pseudo static scale
>>> (not data dependent) we normally hide this by doing the maths internal to the
>>> driver - sometimes meaning we need to present the particular channel as processed
>>> not raw.
>>>
>>> Is the expectation here that vrefint_data is actually very nearly constant? If
>>> so then what you have here may be fine as anyone not aware the scale might change
>>> will get very nearly the right value anyway.
>>>    
>>
>> The need here is to compare the measured value of vrefint with the
>> calibrated value saved in non volatile memory. The ratio between these
>> two values can be used as a correction factor for the acquisitions on
>> all other channels.
>>
>> The vrefint data is expected to be close to the saved vrefint
>> calibration value, and it should not vary strongly over time.
>> So, yes, we can indeed consider the scale as a pseudo constant. If the
>> scale is not updated, the deviation with actual value should remain
>> limited, as well.
> 
> Ok, so in that case we could probably get away with having it as you have
> here, though for maximum precision we'd need userspace to occasionally check
> the scale.
> 
>>
>> You suggest above to hide scale tuning through processed channels.
>> If I follow this logic, when vrefint channel is available, all channels
>> should be defined as processed channels (excepted vrefint channel)
>> In this case no scale is exposed for these channels, and the vrefint
>> calibration ratio can be used to provide converted data directly.
>> Do you prefer this implementation ?
> 
>>
>> In this case I wonder how buffered data have to be managed. These data
>> are still provided as raw data, but the scale factor is not more
>> available to convert them. I guess that these data have to be converted
>> internally also, either in dma callback or irq handler.
>> Is this correct ?
> 
> This is one of the holes in what IIO does today.  Without meta data in the
> buffer (which is hard to define in a clean fashion) it is hard to have
> a compact representation of the data in the presence of dynamic scaling.
> The vast majority of devices don't inherently support such scaling so
> this is only occasionally a problem.
> 
> To support this at the moment you would indeed need to scale the data
> before pushing it to the buffer which is obviously really ugly.
> 
> My gut feeling here is there are three possible approaches.
> 
> 1) Ignore the dynamic nature of the calibration and pretend it's static.
> 2) Add an explicit 'calibration' sysfs attribute.
>     This is a fairly common model for other sensor types which don't do
>     dynamic calibration but instead require to you to start some special
>     calibration sequence.
>     As the calibration is not updated, except on explicit userspace action
>     we can assume that the scale is static unless userspace is aware of
>     the dynamic aspect.
> 3) Add a userspace control to turn on dynamic calibration.  That makes it
>     opt in.  Everything will work reasonably well without it turned on
>     as we'll hopefully have a static estimate of scale which is good enough.
>     If aware software is using the device, it can enable this mode and
>     sample the scale as often as it wants to.
> 
> I slightly favour option 3.  What do you think?  If we ever figure out
> the meta data question for buffered case then we can make that work on top
> of this.
> 
> Jonathan

This discussion made me revisit the calibration aspects in the ADC driver.

We have three types of calibration in ADC:

- Linear calibration: this calibration is not voltage or temperature 
dependent. So, it can be done once at boot time, as this is done currently.

- offset calibration: this calibration has a voltage and temperature 
dependency. This calibration is currently done once at boot. But it 
would be relevant to allow application to request a new offset 
calibration, when supply or temperature change over time.
Here the 'calibration' sysfs attribute you suggested in option 2, would 
be convenient I think. I plan to submit this improvement in a separate 
patch.

- vref compensation: the vrefint channel offers a way to evaluate vref 
deviation. Here I need to change a bit the logic. I think that putting 
intelligence in the driver is not the best way at the end. This hides 
voltage deviation information, where it could be useful to check if an 
offset calibration is needed. Moreover we get a lack of consistency 
between raw and buffered data.
It looks that processed type is the good way to expose vrefint channel. 
This allows to provide the actual vref voltage. And we can let the 
application decide how to manage this information. (trigger offset 
calibration / compensate raw data)
I'm going to send a v2 serie with this change for vrefint support
(plus the corrections for other comments)

Regards
Olivier

>>
>> Regards
>> Olivier
>>
>>>> +			} else {
>>>> +				*val = adc->common->vref_mv * 2;
>>>> +			}
>>>>    			*val2 = chan->scan_type.realbits;
>>>>    		} else {
>>>> -			*val = adc->common->vref_mv;
>>>> +			/* Use vrefint data if available */
>>>> +			if (adc->vrefint.vrefint_data &&
>>>> +			    adc->vrefint.vrefint_cal) {
>>>> +				*val = STM32_ADC_VREFINT_VOLTAGE *
>>>> +				       adc->vrefint.vrefint_cal /
>>>> +				       adc->vrefint.vrefint_data;
>>>> +			} else {
>>>> +				*val = adc->common->vref_mv;
>>>> +			}
>>>>    			*val2 = chan->scan_type.realbits;
>>>>    		}
>>>>    		return IIO_VAL_FRACTIONAL_LOG2;
>>>> @@ -1907,6 +1944,35 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
>>>>    	return scan_index;
>>>>    }
>>>>    
>>>> +static int stm32_adc_get_int_ch(struct iio_dev *indio_dev, const char *ch_name,
>>>> +				int chan)
>>>
>>> Naming would suggest to me that it would return a channel rather than setting it
>>> inside adc->int_ch[i]  Perhaps something like st32_adc_populate_int_ch() ?
>>>
>>>    
>>>> +{
>>>> +	struct stm32_adc *adc = iio_priv(indio_dev);
>>>> +	u16 vrefint;
>>>> +	int i, ret;
>>>> +
>>>> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
>>>> +		if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
>>>> +			adc->int_ch[i] = chan;
>>>> +			/* If channel is vrefint get calibration data. */
>>>> +			if (stm32_adc_ic[i].idx == STM32_ADC_INT_CH_VREFINT) {
>>>
>>> I would reduce indentation by reversing the logic.
>>>
>>> 			if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT)
>>> 				continue;
>>>
>>> 			ret =
>>>> +				ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint);
>>>> +				if (ret && ret != -ENOENT && ret != -EOPNOTSUPP) {
>>>> +					dev_err(&indio_dev->dev, "nvmem access error %d\n", ret);
>>>> +					return ret;
>>>> +				}
>>>> +				if (ret == -ENOENT)
>>>> +					dev_dbg(&indio_dev->dev,
>>>> +						"vrefint calibration not found\n");
>>>> +				else
>>>> +					adc->vrefint.vrefint_cal = vrefint;
>>>> +			}
>>>> +		}
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>>    static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>>>>    				       struct stm32_adc *adc,
>>>>    				       struct iio_chan_spec *channels)
>>>> @@ -1938,10 +2004,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>>>>    				return -EINVAL;
>>>>    			}
>>>>    			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
>>>> -			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
>>>> -				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
>>>> -					adc->int_ch[i] = val;
>>>> -			}
>>>> +			ret = stm32_adc_get_int_ch(indio_dev, name, val);
>>>> +			if (ret)
>>>> +				goto err;
>>>>    		} else if (ret != -EINVAL) {
>>>>    			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
>>>>    			goto err;
>>>> @@ -2044,6 +2109,16 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
>>>>    		 */
>>>>    		of_property_read_u32_index(node, "st,min-sample-time-nsecs",
>>>>    					   i, &smp);
>>>> +
>>>> +		/*
>>>> +		 * For vrefint channel, ensure that the sampling time cannot
>>>> +		 * be lower than the one specified in the datasheet
>>>> +		 */
>>>> +		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
>>>> +		    smp < adc->cfg->ts_vrefint_ns) {
>>>> +			smp = adc->cfg->ts_vrefint_ns;
>>>> +		}
>>>
>>> 		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT])
>>> 			smp = max(smp, adc->cfg->ts_vrefint_ns);
>>>    
>>>> +
>>>>    		/* Prepare sampling time settings */
>>>>    		stm32_adc_smpr_init(adc, channels[i].channel, smp);
>>>>    	}
>>>> @@ -2350,6 +2425,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
>>>>    	.unprepare = stm32h7_adc_unprepare,
>>>>    	.smp_cycles = stm32h7_adc_smp_cycles,
>>>>    	.irq_clear = stm32h7_adc_irq_clear,
>>>> +	.ts_vrefint_ns = 4300,
>>>>    };
>>>>    
>>>>    static const struct of_device_id stm32_adc_of_match[] = {
>>>    
> 

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 6/7] iio: adc: stm32-adc: add vrefint calibration support
@ 2021-09-22  7:53           ` Olivier MOYSAN
  0 siblings, 0 replies; 53+ messages in thread
From: Olivier MOYSAN @ 2021-09-22  7:53 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Lars-Peter Clausen, Maxime Coquelin, Rob Herring, devicetree,
	linux-arm-kernel, linux-iio, linux-kernel, linux-stm32

Hi Jonathan,

On 9/18/21 8:42 PM, Jonathan Cameron wrote:
> On Wed, 15 Sep 2021 12:02:45 +0200
> Olivier MOYSAN <olivier.moysan@foss.st.com> wrote:
> 
>> Hi Jonathan,
>>
>> On 9/11/21 6:28 PM, Jonathan Cameron wrote:
>>> On Wed, 8 Sep 2021 17:54:51 +0200
>>> Olivier Moysan <olivier.moysan@foss.st.com> wrote:
>>>    
>>>> Add support of vrefint calibration.
>>>> If a channel is labeled as vrefint, get vrefint calibration
>>>> from non volatile memory for this channel.
>>>> A conversion on vrefint channel allows to update scale
>>>> factor according to vrefint deviation, compared to vrefint
>>>> calibration value.
>>>
>>> As I mention inline, whilst technically the ABI doesn't demand it
>>> the expectation of much of userspace software is that _scale is
>>> pseudo constant - that is it doesn't tend to change very often and when
>>> it does it's normally because someone deliberately made it change.
>>> As such most software reads it just once.
>>>
>>> Normally we work around this by applying the maths in kernel and
>>> not exposing the scale at all. Is this something that could be done here?
>>>
>>> Jonathan
>>>    
>>>>
>>>> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
>>>> ---
>>>>    drivers/iio/adc/stm32-adc.c | 88 ++++++++++++++++++++++++++++++++++---
>>>>    1 file changed, 82 insertions(+), 6 deletions(-)
>>>>
>>>> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
>>>> index ef3d2af98025..9e52a7de9b16 100644
>>>> --- a/drivers/iio/adc/stm32-adc.c
>>>> +++ b/drivers/iio/adc/stm32-adc.c
>>>> @@ -21,6 +21,7 @@
>>>>    #include <linux/io.h>
>>>>    #include <linux/iopoll.h>
>>>>    #include <linux/module.h>
>>>> +#include <linux/nvmem-consumer.h>
>>>>    #include <linux/platform_device.h>
>>>>    #include <linux/pm_runtime.h>
>>>>    #include <linux/of.h>
>>>> @@ -42,6 +43,7 @@
>>>>    #define STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
>>>>    #define STM32_ADC_HW_STOP_DELAY_MS	100
>>>>    #define STM32_ADC_CHAN_NONE		-1
>>>> +#define STM32_ADC_VREFINT_VOLTAGE	3300
>>>>    
>>>>    #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
>>>>    
>>>> @@ -79,6 +81,7 @@ enum stm32_adc_extsel {
>>>>    };
>>>>    
>>>>    enum stm32_adc_int_ch {
>>>> +	STM32_ADC_INT_CH_NONE = -1,
>>>>    	STM32_ADC_INT_CH_VDDCORE,
>>>>    	STM32_ADC_INT_CH_VREFINT,
>>>>    	STM32_ADC_INT_CH_VBAT,
>>>> @@ -137,6 +140,16 @@ struct stm32_adc_regs {
>>>>    	int shift;
>>>>    };
>>>>    
>>>> +/**
>>>> + * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data
>>>> + * @vrefint_cal:	vrefint calibration value from nvmem
>>>> + * @vrefint_data:	vrefint actual value
>>>> + */
>>>> +struct stm32_adc_vrefint {
>>>> +	u32 vrefint_cal;
>>>> +	u32 vrefint_data;
>>>> +};
>>>> +
>>>>    /**
>>>>     * struct stm32_adc_regspec - stm32 registers definition
>>>>     * @dr:			data register offset
>>>> @@ -186,6 +199,7 @@ struct stm32_adc;
>>>>     * @unprepare:		optional unprepare routine (disable, power-down)
>>>>     * @irq_clear:		routine to clear irqs
>>>>     * @smp_cycles:		programmable sampling time (ADC clock cycles)
>>>> + * @ts_vrefint_ns:	vrefint minimum sampling time in ns
>>>>     */
>>>>    struct stm32_adc_cfg {
>>>>    	const struct stm32_adc_regspec	*regs;
>>>> @@ -199,6 +213,7 @@ struct stm32_adc_cfg {
>>>>    	void (*unprepare)(struct iio_dev *);
>>>>    	void (*irq_clear)(struct iio_dev *indio_dev, u32 msk);
>>>>    	const unsigned int *smp_cycles;
>>>> +	const unsigned int ts_vrefint_ns;
>>>>    };
>>>>    
>>>>    /**
>>>> @@ -223,6 +238,7 @@ struct stm32_adc_cfg {
>>>>     * @pcsel:		bitmask to preselect channels on some devices
>>>>     * @smpr_val:		sampling time settings (e.g. smpr1 / smpr2)
>>>>     * @cal:		optional calibration data on some devices
>>>> + * @vrefint:		internal reference voltage data
>>>>     * @chan_name:		channel name array
>>>>     * @num_diff:		number of differential channels
>>>>     * @int_ch:		internal channel indexes array
>>>> @@ -248,6 +264,7 @@ struct stm32_adc {
>>>>    	u32			pcsel;
>>>>    	u32			smpr_val[2];
>>>>    	struct stm32_adc_calib	cal;
>>>> +	struct stm32_adc_vrefint vrefint;
>>>>    	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
>>>>    	u32			num_diff;
>>>>    	int			int_ch[STM32_ADC_INT_CH_NB];
>>>> @@ -1331,15 +1348,35 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
>>>>    			ret = stm32_adc_single_conv(indio_dev, chan, val);
>>>>    		else
>>>>    			ret = -EINVAL;
>>>> +
>>>> +		/* If channel mask corresponds to vrefint, store data */
>>>> +		if (adc->int_ch[STM32_ADC_INT_CH_VREFINT] == chan->channel)
>>>> +			adc->vrefint.vrefint_data = *val;
>>>> +
>>>>    		iio_device_release_direct_mode(indio_dev);
>>>>    		return ret;
>>>>    
>>>>    	case IIO_CHAN_INFO_SCALE:
>>>>    		if (chan->differential) {
>>>> -			*val = adc->common->vref_mv * 2;
>>>> +			if (adc->vrefint.vrefint_data &&
>>>> +			    adc->vrefint.vrefint_cal) {
>>>> +				*val = STM32_ADC_VREFINT_VOLTAGE * 2 *
>>>> +				       adc->vrefint.vrefint_cal /
>>>> +				       adc->vrefint.vrefint_data;
>>>
>>> Ah.. Dynamic scale.  This is always awkward when it occurs.
>>> Given most / possibly all userspace software assumes a pseudo static scale
>>> (not data dependent) we normally hide this by doing the maths internal to the
>>> driver - sometimes meaning we need to present the particular channel as processed
>>> not raw.
>>>
>>> Is the expectation here that vrefint_data is actually very nearly constant? If
>>> so then what you have here may be fine as anyone not aware the scale might change
>>> will get very nearly the right value anyway.
>>>    
>>
>> The need here is to compare the measured value of vrefint with the
>> calibrated value saved in non volatile memory. The ratio between these
>> two values can be used as a correction factor for the acquisitions on
>> all other channels.
>>
>> The vrefint data is expected to be close to the saved vrefint
>> calibration value, and it should not vary strongly over time.
>> So, yes, we can indeed consider the scale as a pseudo constant. If the
>> scale is not updated, the deviation with actual value should remain
>> limited, as well.
> 
> Ok, so in that case we could probably get away with having it as you have
> here, though for maximum precision we'd need userspace to occasionally check
> the scale.
> 
>>
>> You suggest above to hide scale tuning through processed channels.
>> If I follow this logic, when vrefint channel is available, all channels
>> should be defined as processed channels (excepted vrefint channel)
>> In this case no scale is exposed for these channels, and the vrefint
>> calibration ratio can be used to provide converted data directly.
>> Do you prefer this implementation ?
> 
>>
>> In this case I wonder how buffered data have to be managed. These data
>> are still provided as raw data, but the scale factor is not more
>> available to convert them. I guess that these data have to be converted
>> internally also, either in dma callback or irq handler.
>> Is this correct ?
> 
> This is one of the holes in what IIO does today.  Without meta data in the
> buffer (which is hard to define in a clean fashion) it is hard to have
> a compact representation of the data in the presence of dynamic scaling.
> The vast majority of devices don't inherently support such scaling so
> this is only occasionally a problem.
> 
> To support this at the moment you would indeed need to scale the data
> before pushing it to the buffer which is obviously really ugly.
> 
> My gut feeling here is there are three possible approaches.
> 
> 1) Ignore the dynamic nature of the calibration and pretend it's static.
> 2) Add an explicit 'calibration' sysfs attribute.
>     This is a fairly common model for other sensor types which don't do
>     dynamic calibration but instead require to you to start some special
>     calibration sequence.
>     As the calibration is not updated, except on explicit userspace action
>     we can assume that the scale is static unless userspace is aware of
>     the dynamic aspect.
> 3) Add a userspace control to turn on dynamic calibration.  That makes it
>     opt in.  Everything will work reasonably well without it turned on
>     as we'll hopefully have a static estimate of scale which is good enough.
>     If aware software is using the device, it can enable this mode and
>     sample the scale as often as it wants to.
> 
> I slightly favour option 3.  What do you think?  If we ever figure out
> the meta data question for buffered case then we can make that work on top
> of this.
> 
> Jonathan

This discussion made me revisit the calibration aspects in the ADC driver.

We have three types of calibration in ADC:

- Linear calibration: this calibration is not voltage or temperature 
dependent. So, it can be done once at boot time, as this is done currently.

- offset calibration: this calibration has a voltage and temperature 
dependency. This calibration is currently done once at boot. But it 
would be relevant to allow application to request a new offset 
calibration, when supply or temperature change over time.
Here the 'calibration' sysfs attribute you suggested in option 2, would 
be convenient I think. I plan to submit this improvement in a separate 
patch.

- vref compensation: the vrefint channel offers a way to evaluate vref 
deviation. Here I need to change a bit the logic. I think that putting 
intelligence in the driver is not the best way at the end. This hides 
voltage deviation information, where it could be useful to check if an 
offset calibration is needed. Moreover we get a lack of consistency 
between raw and buffered data.
It looks that processed type is the good way to expose vrefint channel. 
This allows to provide the actual vref voltage. And we can let the 
application decide how to manage this information. (trigger offset 
calibration / compensate raw data)
I'm going to send a v2 serie with this change for vrefint support
(plus the corrections for other comments)

Regards
Olivier

>>
>> Regards
>> Olivier
>>
>>>> +			} else {
>>>> +				*val = adc->common->vref_mv * 2;
>>>> +			}
>>>>    			*val2 = chan->scan_type.realbits;
>>>>    		} else {
>>>> -			*val = adc->common->vref_mv;
>>>> +			/* Use vrefint data if available */
>>>> +			if (adc->vrefint.vrefint_data &&
>>>> +			    adc->vrefint.vrefint_cal) {
>>>> +				*val = STM32_ADC_VREFINT_VOLTAGE *
>>>> +				       adc->vrefint.vrefint_cal /
>>>> +				       adc->vrefint.vrefint_data;
>>>> +			} else {
>>>> +				*val = adc->common->vref_mv;
>>>> +			}
>>>>    			*val2 = chan->scan_type.realbits;
>>>>    		}
>>>>    		return IIO_VAL_FRACTIONAL_LOG2;
>>>> @@ -1907,6 +1944,35 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
>>>>    	return scan_index;
>>>>    }
>>>>    
>>>> +static int stm32_adc_get_int_ch(struct iio_dev *indio_dev, const char *ch_name,
>>>> +				int chan)
>>>
>>> Naming would suggest to me that it would return a channel rather than setting it
>>> inside adc->int_ch[i]  Perhaps something like st32_adc_populate_int_ch() ?
>>>
>>>    
>>>> +{
>>>> +	struct stm32_adc *adc = iio_priv(indio_dev);
>>>> +	u16 vrefint;
>>>> +	int i, ret;
>>>> +
>>>> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
>>>> +		if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
>>>> +			adc->int_ch[i] = chan;
>>>> +			/* If channel is vrefint get calibration data. */
>>>> +			if (stm32_adc_ic[i].idx == STM32_ADC_INT_CH_VREFINT) {
>>>
>>> I would reduce indentation by reversing the logic.
>>>
>>> 			if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT)
>>> 				continue;
>>>
>>> 			ret =
>>>> +				ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint);
>>>> +				if (ret && ret != -ENOENT && ret != -EOPNOTSUPP) {
>>>> +					dev_err(&indio_dev->dev, "nvmem access error %d\n", ret);
>>>> +					return ret;
>>>> +				}
>>>> +				if (ret == -ENOENT)
>>>> +					dev_dbg(&indio_dev->dev,
>>>> +						"vrefint calibration not found\n");
>>>> +				else
>>>> +					adc->vrefint.vrefint_cal = vrefint;
>>>> +			}
>>>> +		}
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>>    static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>>>>    				       struct stm32_adc *adc,
>>>>    				       struct iio_chan_spec *channels)
>>>> @@ -1938,10 +2004,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
>>>>    				return -EINVAL;
>>>>    			}
>>>>    			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
>>>> -			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
>>>> -				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
>>>> -					adc->int_ch[i] = val;
>>>> -			}
>>>> +			ret = stm32_adc_get_int_ch(indio_dev, name, val);
>>>> +			if (ret)
>>>> +				goto err;
>>>>    		} else if (ret != -EINVAL) {
>>>>    			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
>>>>    			goto err;
>>>> @@ -2044,6 +2109,16 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
>>>>    		 */
>>>>    		of_property_read_u32_index(node, "st,min-sample-time-nsecs",
>>>>    					   i, &smp);
>>>> +
>>>> +		/*
>>>> +		 * For vrefint channel, ensure that the sampling time cannot
>>>> +		 * be lower than the one specified in the datasheet
>>>> +		 */
>>>> +		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
>>>> +		    smp < adc->cfg->ts_vrefint_ns) {
>>>> +			smp = adc->cfg->ts_vrefint_ns;
>>>> +		}
>>>
>>> 		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT])
>>> 			smp = max(smp, adc->cfg->ts_vrefint_ns);
>>>    
>>>> +
>>>>    		/* Prepare sampling time settings */
>>>>    		stm32_adc_smpr_init(adc, channels[i].channel, smp);
>>>>    	}
>>>> @@ -2350,6 +2425,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
>>>>    	.unprepare = stm32h7_adc_unprepare,
>>>>    	.smp_cycles = stm32h7_adc_smp_cycles,
>>>>    	.irq_clear = stm32h7_adc_irq_clear,
>>>> +	.ts_vrefint_ns = 4300,
>>>>    };
>>>>    
>>>>    static const struct of_device_id stm32_adc_of_match[] = {
>>>    
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 6/7] iio: adc: stm32-adc: add vrefint calibration support
  2021-09-22  7:53           ` Olivier MOYSAN
@ 2021-09-26 12:09             ` Jonathan Cameron
  -1 siblings, 0 replies; 53+ messages in thread
From: Jonathan Cameron @ 2021-09-26 12:09 UTC (permalink / raw)
  To: Olivier MOYSAN
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Lars-Peter Clausen, Maxime Coquelin, Rob Herring, devicetree,
	linux-arm-kernel, linux-iio, linux-kernel, linux-stm32

On Wed, 22 Sep 2021 09:53:58 +0200
Olivier MOYSAN <olivier.moysan@foss.st.com> wrote:

> Hi Jonathan,
> 
> On 9/18/21 8:42 PM, Jonathan Cameron wrote:
> > On Wed, 15 Sep 2021 12:02:45 +0200
> > Olivier MOYSAN <olivier.moysan@foss.st.com> wrote:
> >   
> >> Hi Jonathan,
> >>
> >> On 9/11/21 6:28 PM, Jonathan Cameron wrote:  
> >>> On Wed, 8 Sep 2021 17:54:51 +0200
> >>> Olivier Moysan <olivier.moysan@foss.st.com> wrote:
> >>>      
> >>>> Add support of vrefint calibration.
> >>>> If a channel is labeled as vrefint, get vrefint calibration
> >>>> from non volatile memory for this channel.
> >>>> A conversion on vrefint channel allows to update scale
> >>>> factor according to vrefint deviation, compared to vrefint
> >>>> calibration value.  
> >>>
> >>> As I mention inline, whilst technically the ABI doesn't demand it
> >>> the expectation of much of userspace software is that _scale is
> >>> pseudo constant - that is it doesn't tend to change very often and when
> >>> it does it's normally because someone deliberately made it change.
> >>> As such most software reads it just once.
> >>>
> >>> Normally we work around this by applying the maths in kernel and
> >>> not exposing the scale at all. Is this something that could be done here?
> >>>
> >>> Jonathan
> >>>      
> >>>>
> >>>> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
> >>>> ---
> >>>>    drivers/iio/adc/stm32-adc.c | 88 ++++++++++++++++++++++++++++++++++---
> >>>>    1 file changed, 82 insertions(+), 6 deletions(-)
> >>>>
> >>>> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> >>>> index ef3d2af98025..9e52a7de9b16 100644
> >>>> --- a/drivers/iio/adc/stm32-adc.c
> >>>> +++ b/drivers/iio/adc/stm32-adc.c
> >>>> @@ -21,6 +21,7 @@
> >>>>    #include <linux/io.h>
> >>>>    #include <linux/iopoll.h>
> >>>>    #include <linux/module.h>
> >>>> +#include <linux/nvmem-consumer.h>
> >>>>    #include <linux/platform_device.h>
> >>>>    #include <linux/pm_runtime.h>
> >>>>    #include <linux/of.h>
> >>>> @@ -42,6 +43,7 @@
> >>>>    #define STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
> >>>>    #define STM32_ADC_HW_STOP_DELAY_MS	100
> >>>>    #define STM32_ADC_CHAN_NONE		-1
> >>>> +#define STM32_ADC_VREFINT_VOLTAGE	3300
> >>>>    
> >>>>    #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
> >>>>    
> >>>> @@ -79,6 +81,7 @@ enum stm32_adc_extsel {
> >>>>    };
> >>>>    
> >>>>    enum stm32_adc_int_ch {
> >>>> +	STM32_ADC_INT_CH_NONE = -1,
> >>>>    	STM32_ADC_INT_CH_VDDCORE,
> >>>>    	STM32_ADC_INT_CH_VREFINT,
> >>>>    	STM32_ADC_INT_CH_VBAT,
> >>>> @@ -137,6 +140,16 @@ struct stm32_adc_regs {
> >>>>    	int shift;
> >>>>    };
> >>>>    
> >>>> +/**
> >>>> + * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data
> >>>> + * @vrefint_cal:	vrefint calibration value from nvmem
> >>>> + * @vrefint_data:	vrefint actual value
> >>>> + */
> >>>> +struct stm32_adc_vrefint {
> >>>> +	u32 vrefint_cal;
> >>>> +	u32 vrefint_data;
> >>>> +};
> >>>> +
> >>>>    /**
> >>>>     * struct stm32_adc_regspec - stm32 registers definition
> >>>>     * @dr:			data register offset
> >>>> @@ -186,6 +199,7 @@ struct stm32_adc;
> >>>>     * @unprepare:		optional unprepare routine (disable, power-down)
> >>>>     * @irq_clear:		routine to clear irqs
> >>>>     * @smp_cycles:		programmable sampling time (ADC clock cycles)
> >>>> + * @ts_vrefint_ns:	vrefint minimum sampling time in ns
> >>>>     */
> >>>>    struct stm32_adc_cfg {
> >>>>    	const struct stm32_adc_regspec	*regs;
> >>>> @@ -199,6 +213,7 @@ struct stm32_adc_cfg {
> >>>>    	void (*unprepare)(struct iio_dev *);
> >>>>    	void (*irq_clear)(struct iio_dev *indio_dev, u32 msk);
> >>>>    	const unsigned int *smp_cycles;
> >>>> +	const unsigned int ts_vrefint_ns;
> >>>>    };
> >>>>    
> >>>>    /**
> >>>> @@ -223,6 +238,7 @@ struct stm32_adc_cfg {
> >>>>     * @pcsel:		bitmask to preselect channels on some devices
> >>>>     * @smpr_val:		sampling time settings (e.g. smpr1 / smpr2)
> >>>>     * @cal:		optional calibration data on some devices
> >>>> + * @vrefint:		internal reference voltage data
> >>>>     * @chan_name:		channel name array
> >>>>     * @num_diff:		number of differential channels
> >>>>     * @int_ch:		internal channel indexes array
> >>>> @@ -248,6 +264,7 @@ struct stm32_adc {
> >>>>    	u32			pcsel;
> >>>>    	u32			smpr_val[2];
> >>>>    	struct stm32_adc_calib	cal;
> >>>> +	struct stm32_adc_vrefint vrefint;
> >>>>    	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
> >>>>    	u32			num_diff;
> >>>>    	int			int_ch[STM32_ADC_INT_CH_NB];
> >>>> @@ -1331,15 +1348,35 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
> >>>>    			ret = stm32_adc_single_conv(indio_dev, chan, val);
> >>>>    		else
> >>>>    			ret = -EINVAL;
> >>>> +
> >>>> +		/* If channel mask corresponds to vrefint, store data */
> >>>> +		if (adc->int_ch[STM32_ADC_INT_CH_VREFINT] == chan->channel)
> >>>> +			adc->vrefint.vrefint_data = *val;
> >>>> +
> >>>>    		iio_device_release_direct_mode(indio_dev);
> >>>>    		return ret;
> >>>>    
> >>>>    	case IIO_CHAN_INFO_SCALE:
> >>>>    		if (chan->differential) {
> >>>> -			*val = adc->common->vref_mv * 2;
> >>>> +			if (adc->vrefint.vrefint_data &&
> >>>> +			    adc->vrefint.vrefint_cal) {
> >>>> +				*val = STM32_ADC_VREFINT_VOLTAGE * 2 *
> >>>> +				       adc->vrefint.vrefint_cal /
> >>>> +				       adc->vrefint.vrefint_data;  
> >>>
> >>> Ah.. Dynamic scale.  This is always awkward when it occurs.
> >>> Given most / possibly all userspace software assumes a pseudo static scale
> >>> (not data dependent) we normally hide this by doing the maths internal to the
> >>> driver - sometimes meaning we need to present the particular channel as processed
> >>> not raw.
> >>>
> >>> Is the expectation here that vrefint_data is actually very nearly constant? If
> >>> so then what you have here may be fine as anyone not aware the scale might change
> >>> will get very nearly the right value anyway.
> >>>      
> >>
> >> The need here is to compare the measured value of vrefint with the
> >> calibrated value saved in non volatile memory. The ratio between these
> >> two values can be used as a correction factor for the acquisitions on
> >> all other channels.
> >>
> >> The vrefint data is expected to be close to the saved vrefint
> >> calibration value, and it should not vary strongly over time.
> >> So, yes, we can indeed consider the scale as a pseudo constant. If the
> >> scale is not updated, the deviation with actual value should remain
> >> limited, as well.  
> > 
> > Ok, so in that case we could probably get away with having it as you have
> > here, though for maximum precision we'd need userspace to occasionally check
> > the scale.
> >   
> >>
> >> You suggest above to hide scale tuning through processed channels.
> >> If I follow this logic, when vrefint channel is available, all channels
> >> should be defined as processed channels (excepted vrefint channel)
> >> In this case no scale is exposed for these channels, and the vrefint
> >> calibration ratio can be used to provide converted data directly.
> >> Do you prefer this implementation ?  
> >   
> >>
> >> In this case I wonder how buffered data have to be managed. These data
> >> are still provided as raw data, but the scale factor is not more
> >> available to convert them. I guess that these data have to be converted
> >> internally also, either in dma callback or irq handler.
> >> Is this correct ?  
> > 
> > This is one of the holes in what IIO does today.  Without meta data in the
> > buffer (which is hard to define in a clean fashion) it is hard to have
> > a compact representation of the data in the presence of dynamic scaling.
> > The vast majority of devices don't inherently support such scaling so
> > this is only occasionally a problem.
> > 
> > To support this at the moment you would indeed need to scale the data
> > before pushing it to the buffer which is obviously really ugly.
> > 
> > My gut feeling here is there are three possible approaches.
> > 
> > 1) Ignore the dynamic nature of the calibration and pretend it's static.
> > 2) Add an explicit 'calibration' sysfs attribute.
> >     This is a fairly common model for other sensor types which don't do
> >     dynamic calibration but instead require to you to start some special
> >     calibration sequence.
> >     As the calibration is not updated, except on explicit userspace action
> >     we can assume that the scale is static unless userspace is aware of
> >     the dynamic aspect.
> > 3) Add a userspace control to turn on dynamic calibration.  That makes it
> >     opt in.  Everything will work reasonably well without it turned on
> >     as we'll hopefully have a static estimate of scale which is good enough.
> >     If aware software is using the device, it can enable this mode and
> >     sample the scale as often as it wants to.
> > 
> > I slightly favour option 3.  What do you think?  If we ever figure out
> > the meta data question for buffered case then we can make that work on top
> > of this.
> > 
> > Jonathan  
> 
> This discussion made me revisit the calibration aspects in the ADC driver.
> 
> We have three types of calibration in ADC:
> 
> - Linear calibration: this calibration is not voltage or temperature 
> dependent. So, it can be done once at boot time, as this is done currently.
> 
> - offset calibration: this calibration has a voltage and temperature 
> dependency. This calibration is currently done once at boot. But it 
> would be relevant to allow application to request a new offset 
> calibration, when supply or temperature change over time.
> Here the 'calibration' sysfs attribute you suggested in option 2, would 
> be convenient I think. I plan to submit this improvement in a separate 
> patch.
> 
> - vref compensation: the vrefint channel offers a way to evaluate vref 
> deviation. Here I need to change a bit the logic. I think that putting 
> intelligence in the driver is not the best way at the end. This hides 
> voltage deviation information, where it could be useful to check if an 
> offset calibration is needed. Moreover we get a lack of consistency 
> between raw and buffered data.
> It looks that processed type is the good way to expose vrefint channel. 
> This allows to provide the actual vref voltage. And we can let the 
> application decide how to manage this information. (trigger offset 
> calibration / compensate raw data)
> I'm going to send a v2 serie with this change for vrefint support
> (plus the corrections for other comments)

Sounds like a good compromise solution to me.  It's not great that we
end up with a 'somewhat' device specific solution, but as I've not previously
seen this particular form of calibration I am not that bothered by it
being device specific.

As ever, these stm32 parts continue to cut a new trail!

Thanks,

Jonathan

> 
> Regards
> Olivier
> 
> >>
> >> Regards
> >> Olivier
> >>  
> >>>> +			} else {
> >>>> +				*val = adc->common->vref_mv * 2;
> >>>> +			}
> >>>>    			*val2 = chan->scan_type.realbits;
> >>>>    		} else {
> >>>> -			*val = adc->common->vref_mv;
> >>>> +			/* Use vrefint data if available */
> >>>> +			if (adc->vrefint.vrefint_data &&
> >>>> +			    adc->vrefint.vrefint_cal) {
> >>>> +				*val = STM32_ADC_VREFINT_VOLTAGE *
> >>>> +				       adc->vrefint.vrefint_cal /
> >>>> +				       adc->vrefint.vrefint_data;
> >>>> +			} else {
> >>>> +				*val = adc->common->vref_mv;
> >>>> +			}
> >>>>    			*val2 = chan->scan_type.realbits;
> >>>>    		}
> >>>>    		return IIO_VAL_FRACTIONAL_LOG2;
> >>>> @@ -1907,6 +1944,35 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
> >>>>    	return scan_index;
> >>>>    }
> >>>>    
> >>>> +static int stm32_adc_get_int_ch(struct iio_dev *indio_dev, const char *ch_name,
> >>>> +				int chan)  
> >>>
> >>> Naming would suggest to me that it would return a channel rather than setting it
> >>> inside adc->int_ch[i]  Perhaps something like st32_adc_populate_int_ch() ?
> >>>
> >>>      
> >>>> +{
> >>>> +	struct stm32_adc *adc = iio_priv(indio_dev);
> >>>> +	u16 vrefint;
> >>>> +	int i, ret;
> >>>> +
> >>>> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> >>>> +		if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
> >>>> +			adc->int_ch[i] = chan;
> >>>> +			/* If channel is vrefint get calibration data. */
> >>>> +			if (stm32_adc_ic[i].idx == STM32_ADC_INT_CH_VREFINT) {  
> >>>
> >>> I would reduce indentation by reversing the logic.
> >>>
> >>> 			if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT)
> >>> 				continue;
> >>>
> >>> 			ret =  
> >>>> +				ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint);
> >>>> +				if (ret && ret != -ENOENT && ret != -EOPNOTSUPP) {
> >>>> +					dev_err(&indio_dev->dev, "nvmem access error %d\n", ret);
> >>>> +					return ret;
> >>>> +				}
> >>>> +				if (ret == -ENOENT)
> >>>> +					dev_dbg(&indio_dev->dev,
> >>>> +						"vrefint calibration not found\n");
> >>>> +				else
> >>>> +					adc->vrefint.vrefint_cal = vrefint;
> >>>> +			}
> >>>> +		}
> >>>> +	}
> >>>> +
> >>>> +	return 0;
> >>>> +}
> >>>> +
> >>>>    static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
> >>>>    				       struct stm32_adc *adc,
> >>>>    				       struct iio_chan_spec *channels)
> >>>> @@ -1938,10 +2004,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
> >>>>    				return -EINVAL;
> >>>>    			}
> >>>>    			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
> >>>> -			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> >>>> -				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
> >>>> -					adc->int_ch[i] = val;
> >>>> -			}
> >>>> +			ret = stm32_adc_get_int_ch(indio_dev, name, val);
> >>>> +			if (ret)
> >>>> +				goto err;
> >>>>    		} else if (ret != -EINVAL) {
> >>>>    			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
> >>>>    			goto err;
> >>>> @@ -2044,6 +2109,16 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
> >>>>    		 */
> >>>>    		of_property_read_u32_index(node, "st,min-sample-time-nsecs",
> >>>>    					   i, &smp);
> >>>> +
> >>>> +		/*
> >>>> +		 * For vrefint channel, ensure that the sampling time cannot
> >>>> +		 * be lower than the one specified in the datasheet
> >>>> +		 */
> >>>> +		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
> >>>> +		    smp < adc->cfg->ts_vrefint_ns) {
> >>>> +			smp = adc->cfg->ts_vrefint_ns;
> >>>> +		}  
> >>>
> >>> 		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT])
> >>> 			smp = max(smp, adc->cfg->ts_vrefint_ns);
> >>>      
> >>>> +
> >>>>    		/* Prepare sampling time settings */
> >>>>    		stm32_adc_smpr_init(adc, channels[i].channel, smp);
> >>>>    	}
> >>>> @@ -2350,6 +2425,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
> >>>>    	.unprepare = stm32h7_adc_unprepare,
> >>>>    	.smp_cycles = stm32h7_adc_smp_cycles,
> >>>>    	.irq_clear = stm32h7_adc_irq_clear,
> >>>> +	.ts_vrefint_ns = 4300,
> >>>>    };
> >>>>    
> >>>>    static const struct of_device_id stm32_adc_of_match[] = {  
> >>>      
> >   


^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 6/7] iio: adc: stm32-adc: add vrefint calibration support
@ 2021-09-26 12:09             ` Jonathan Cameron
  0 siblings, 0 replies; 53+ messages in thread
From: Jonathan Cameron @ 2021-09-26 12:09 UTC (permalink / raw)
  To: Olivier MOYSAN
  Cc: Alexandre Torgue, Arnaud Pouliquen, Fabrice Gasnier,
	Lars-Peter Clausen, Maxime Coquelin, Rob Herring, devicetree,
	linux-arm-kernel, linux-iio, linux-kernel, linux-stm32

On Wed, 22 Sep 2021 09:53:58 +0200
Olivier MOYSAN <olivier.moysan@foss.st.com> wrote:

> Hi Jonathan,
> 
> On 9/18/21 8:42 PM, Jonathan Cameron wrote:
> > On Wed, 15 Sep 2021 12:02:45 +0200
> > Olivier MOYSAN <olivier.moysan@foss.st.com> wrote:
> >   
> >> Hi Jonathan,
> >>
> >> On 9/11/21 6:28 PM, Jonathan Cameron wrote:  
> >>> On Wed, 8 Sep 2021 17:54:51 +0200
> >>> Olivier Moysan <olivier.moysan@foss.st.com> wrote:
> >>>      
> >>>> Add support of vrefint calibration.
> >>>> If a channel is labeled as vrefint, get vrefint calibration
> >>>> from non volatile memory for this channel.
> >>>> A conversion on vrefint channel allows to update scale
> >>>> factor according to vrefint deviation, compared to vrefint
> >>>> calibration value.  
> >>>
> >>> As I mention inline, whilst technically the ABI doesn't demand it
> >>> the expectation of much of userspace software is that _scale is
> >>> pseudo constant - that is it doesn't tend to change very often and when
> >>> it does it's normally because someone deliberately made it change.
> >>> As such most software reads it just once.
> >>>
> >>> Normally we work around this by applying the maths in kernel and
> >>> not exposing the scale at all. Is this something that could be done here?
> >>>
> >>> Jonathan
> >>>      
> >>>>
> >>>> Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
> >>>> ---
> >>>>    drivers/iio/adc/stm32-adc.c | 88 ++++++++++++++++++++++++++++++++++---
> >>>>    1 file changed, 82 insertions(+), 6 deletions(-)
> >>>>
> >>>> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> >>>> index ef3d2af98025..9e52a7de9b16 100644
> >>>> --- a/drivers/iio/adc/stm32-adc.c
> >>>> +++ b/drivers/iio/adc/stm32-adc.c
> >>>> @@ -21,6 +21,7 @@
> >>>>    #include <linux/io.h>
> >>>>    #include <linux/iopoll.h>
> >>>>    #include <linux/module.h>
> >>>> +#include <linux/nvmem-consumer.h>
> >>>>    #include <linux/platform_device.h>
> >>>>    #include <linux/pm_runtime.h>
> >>>>    #include <linux/of.h>
> >>>> @@ -42,6 +43,7 @@
> >>>>    #define STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
> >>>>    #define STM32_ADC_HW_STOP_DELAY_MS	100
> >>>>    #define STM32_ADC_CHAN_NONE		-1
> >>>> +#define STM32_ADC_VREFINT_VOLTAGE	3300
> >>>>    
> >>>>    #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
> >>>>    
> >>>> @@ -79,6 +81,7 @@ enum stm32_adc_extsel {
> >>>>    };
> >>>>    
> >>>>    enum stm32_adc_int_ch {
> >>>> +	STM32_ADC_INT_CH_NONE = -1,
> >>>>    	STM32_ADC_INT_CH_VDDCORE,
> >>>>    	STM32_ADC_INT_CH_VREFINT,
> >>>>    	STM32_ADC_INT_CH_VBAT,
> >>>> @@ -137,6 +140,16 @@ struct stm32_adc_regs {
> >>>>    	int shift;
> >>>>    };
> >>>>    
> >>>> +/**
> >>>> + * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data
> >>>> + * @vrefint_cal:	vrefint calibration value from nvmem
> >>>> + * @vrefint_data:	vrefint actual value
> >>>> + */
> >>>> +struct stm32_adc_vrefint {
> >>>> +	u32 vrefint_cal;
> >>>> +	u32 vrefint_data;
> >>>> +};
> >>>> +
> >>>>    /**
> >>>>     * struct stm32_adc_regspec - stm32 registers definition
> >>>>     * @dr:			data register offset
> >>>> @@ -186,6 +199,7 @@ struct stm32_adc;
> >>>>     * @unprepare:		optional unprepare routine (disable, power-down)
> >>>>     * @irq_clear:		routine to clear irqs
> >>>>     * @smp_cycles:		programmable sampling time (ADC clock cycles)
> >>>> + * @ts_vrefint_ns:	vrefint minimum sampling time in ns
> >>>>     */
> >>>>    struct stm32_adc_cfg {
> >>>>    	const struct stm32_adc_regspec	*regs;
> >>>> @@ -199,6 +213,7 @@ struct stm32_adc_cfg {
> >>>>    	void (*unprepare)(struct iio_dev *);
> >>>>    	void (*irq_clear)(struct iio_dev *indio_dev, u32 msk);
> >>>>    	const unsigned int *smp_cycles;
> >>>> +	const unsigned int ts_vrefint_ns;
> >>>>    };
> >>>>    
> >>>>    /**
> >>>> @@ -223,6 +238,7 @@ struct stm32_adc_cfg {
> >>>>     * @pcsel:		bitmask to preselect channels on some devices
> >>>>     * @smpr_val:		sampling time settings (e.g. smpr1 / smpr2)
> >>>>     * @cal:		optional calibration data on some devices
> >>>> + * @vrefint:		internal reference voltage data
> >>>>     * @chan_name:		channel name array
> >>>>     * @num_diff:		number of differential channels
> >>>>     * @int_ch:		internal channel indexes array
> >>>> @@ -248,6 +264,7 @@ struct stm32_adc {
> >>>>    	u32			pcsel;
> >>>>    	u32			smpr_val[2];
> >>>>    	struct stm32_adc_calib	cal;
> >>>> +	struct stm32_adc_vrefint vrefint;
> >>>>    	char			chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
> >>>>    	u32			num_diff;
> >>>>    	int			int_ch[STM32_ADC_INT_CH_NB];
> >>>> @@ -1331,15 +1348,35 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
> >>>>    			ret = stm32_adc_single_conv(indio_dev, chan, val);
> >>>>    		else
> >>>>    			ret = -EINVAL;
> >>>> +
> >>>> +		/* If channel mask corresponds to vrefint, store data */
> >>>> +		if (adc->int_ch[STM32_ADC_INT_CH_VREFINT] == chan->channel)
> >>>> +			adc->vrefint.vrefint_data = *val;
> >>>> +
> >>>>    		iio_device_release_direct_mode(indio_dev);
> >>>>    		return ret;
> >>>>    
> >>>>    	case IIO_CHAN_INFO_SCALE:
> >>>>    		if (chan->differential) {
> >>>> -			*val = adc->common->vref_mv * 2;
> >>>> +			if (adc->vrefint.vrefint_data &&
> >>>> +			    adc->vrefint.vrefint_cal) {
> >>>> +				*val = STM32_ADC_VREFINT_VOLTAGE * 2 *
> >>>> +				       adc->vrefint.vrefint_cal /
> >>>> +				       adc->vrefint.vrefint_data;  
> >>>
> >>> Ah.. Dynamic scale.  This is always awkward when it occurs.
> >>> Given most / possibly all userspace software assumes a pseudo static scale
> >>> (not data dependent) we normally hide this by doing the maths internal to the
> >>> driver - sometimes meaning we need to present the particular channel as processed
> >>> not raw.
> >>>
> >>> Is the expectation here that vrefint_data is actually very nearly constant? If
> >>> so then what you have here may be fine as anyone not aware the scale might change
> >>> will get very nearly the right value anyway.
> >>>      
> >>
> >> The need here is to compare the measured value of vrefint with the
> >> calibrated value saved in non volatile memory. The ratio between these
> >> two values can be used as a correction factor for the acquisitions on
> >> all other channels.
> >>
> >> The vrefint data is expected to be close to the saved vrefint
> >> calibration value, and it should not vary strongly over time.
> >> So, yes, we can indeed consider the scale as a pseudo constant. If the
> >> scale is not updated, the deviation with actual value should remain
> >> limited, as well.  
> > 
> > Ok, so in that case we could probably get away with having it as you have
> > here, though for maximum precision we'd need userspace to occasionally check
> > the scale.
> >   
> >>
> >> You suggest above to hide scale tuning through processed channels.
> >> If I follow this logic, when vrefint channel is available, all channels
> >> should be defined as processed channels (excepted vrefint channel)
> >> In this case no scale is exposed for these channels, and the vrefint
> >> calibration ratio can be used to provide converted data directly.
> >> Do you prefer this implementation ?  
> >   
> >>
> >> In this case I wonder how buffered data have to be managed. These data
> >> are still provided as raw data, but the scale factor is not more
> >> available to convert them. I guess that these data have to be converted
> >> internally also, either in dma callback or irq handler.
> >> Is this correct ?  
> > 
> > This is one of the holes in what IIO does today.  Without meta data in the
> > buffer (which is hard to define in a clean fashion) it is hard to have
> > a compact representation of the data in the presence of dynamic scaling.
> > The vast majority of devices don't inherently support such scaling so
> > this is only occasionally a problem.
> > 
> > To support this at the moment you would indeed need to scale the data
> > before pushing it to the buffer which is obviously really ugly.
> > 
> > My gut feeling here is there are three possible approaches.
> > 
> > 1) Ignore the dynamic nature of the calibration and pretend it's static.
> > 2) Add an explicit 'calibration' sysfs attribute.
> >     This is a fairly common model for other sensor types which don't do
> >     dynamic calibration but instead require to you to start some special
> >     calibration sequence.
> >     As the calibration is not updated, except on explicit userspace action
> >     we can assume that the scale is static unless userspace is aware of
> >     the dynamic aspect.
> > 3) Add a userspace control to turn on dynamic calibration.  That makes it
> >     opt in.  Everything will work reasonably well without it turned on
> >     as we'll hopefully have a static estimate of scale which is good enough.
> >     If aware software is using the device, it can enable this mode and
> >     sample the scale as often as it wants to.
> > 
> > I slightly favour option 3.  What do you think?  If we ever figure out
> > the meta data question for buffered case then we can make that work on top
> > of this.
> > 
> > Jonathan  
> 
> This discussion made me revisit the calibration aspects in the ADC driver.
> 
> We have three types of calibration in ADC:
> 
> - Linear calibration: this calibration is not voltage or temperature 
> dependent. So, it can be done once at boot time, as this is done currently.
> 
> - offset calibration: this calibration has a voltage and temperature 
> dependency. This calibration is currently done once at boot. But it 
> would be relevant to allow application to request a new offset 
> calibration, when supply or temperature change over time.
> Here the 'calibration' sysfs attribute you suggested in option 2, would 
> be convenient I think. I plan to submit this improvement in a separate 
> patch.
> 
> - vref compensation: the vrefint channel offers a way to evaluate vref 
> deviation. Here I need to change a bit the logic. I think that putting 
> intelligence in the driver is not the best way at the end. This hides 
> voltage deviation information, where it could be useful to check if an 
> offset calibration is needed. Moreover we get a lack of consistency 
> between raw and buffered data.
> It looks that processed type is the good way to expose vrefint channel. 
> This allows to provide the actual vref voltage. And we can let the 
> application decide how to manage this information. (trigger offset 
> calibration / compensate raw data)
> I'm going to send a v2 serie with this change for vrefint support
> (plus the corrections for other comments)

Sounds like a good compromise solution to me.  It's not great that we
end up with a 'somewhat' device specific solution, but as I've not previously
seen this particular form of calibration I am not that bothered by it
being device specific.

As ever, these stm32 parts continue to cut a new trail!

Thanks,

Jonathan

> 
> Regards
> Olivier
> 
> >>
> >> Regards
> >> Olivier
> >>  
> >>>> +			} else {
> >>>> +				*val = adc->common->vref_mv * 2;
> >>>> +			}
> >>>>    			*val2 = chan->scan_type.realbits;
> >>>>    		} else {
> >>>> -			*val = adc->common->vref_mv;
> >>>> +			/* Use vrefint data if available */
> >>>> +			if (adc->vrefint.vrefint_data &&
> >>>> +			    adc->vrefint.vrefint_cal) {
> >>>> +				*val = STM32_ADC_VREFINT_VOLTAGE *
> >>>> +				       adc->vrefint.vrefint_cal /
> >>>> +				       adc->vrefint.vrefint_data;
> >>>> +			} else {
> >>>> +				*val = adc->common->vref_mv;
> >>>> +			}
> >>>>    			*val2 = chan->scan_type.realbits;
> >>>>    		}
> >>>>    		return IIO_VAL_FRACTIONAL_LOG2;
> >>>> @@ -1907,6 +1944,35 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
> >>>>    	return scan_index;
> >>>>    }
> >>>>    
> >>>> +static int stm32_adc_get_int_ch(struct iio_dev *indio_dev, const char *ch_name,
> >>>> +				int chan)  
> >>>
> >>> Naming would suggest to me that it would return a channel rather than setting it
> >>> inside adc->int_ch[i]  Perhaps something like st32_adc_populate_int_ch() ?
> >>>
> >>>      
> >>>> +{
> >>>> +	struct stm32_adc *adc = iio_priv(indio_dev);
> >>>> +	u16 vrefint;
> >>>> +	int i, ret;
> >>>> +
> >>>> +	for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> >>>> +		if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
> >>>> +			adc->int_ch[i] = chan;
> >>>> +			/* If channel is vrefint get calibration data. */
> >>>> +			if (stm32_adc_ic[i].idx == STM32_ADC_INT_CH_VREFINT) {  
> >>>
> >>> I would reduce indentation by reversing the logic.
> >>>
> >>> 			if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT)
> >>> 				continue;
> >>>
> >>> 			ret =  
> >>>> +				ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint);
> >>>> +				if (ret && ret != -ENOENT && ret != -EOPNOTSUPP) {
> >>>> +					dev_err(&indio_dev->dev, "nvmem access error %d\n", ret);
> >>>> +					return ret;
> >>>> +				}
> >>>> +				if (ret == -ENOENT)
> >>>> +					dev_dbg(&indio_dev->dev,
> >>>> +						"vrefint calibration not found\n");
> >>>> +				else
> >>>> +					adc->vrefint.vrefint_cal = vrefint;
> >>>> +			}
> >>>> +		}
> >>>> +	}
> >>>> +
> >>>> +	return 0;
> >>>> +}
> >>>> +
> >>>>    static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
> >>>>    				       struct stm32_adc *adc,
> >>>>    				       struct iio_chan_spec *channels)
> >>>> @@ -1938,10 +2004,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
> >>>>    				return -EINVAL;
> >>>>    			}
> >>>>    			strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
> >>>> -			for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
> >>>> -				if (!strncmp(stm32_adc_ic[i].name, name, STM32_ADC_CH_SZ))
> >>>> -					adc->int_ch[i] = val;
> >>>> -			}
> >>>> +			ret = stm32_adc_get_int_ch(indio_dev, name, val);
> >>>> +			if (ret)
> >>>> +				goto err;
> >>>>    		} else if (ret != -EINVAL) {
> >>>>    			dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
> >>>>    			goto err;
> >>>> @@ -2044,6 +2109,16 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
> >>>>    		 */
> >>>>    		of_property_read_u32_index(node, "st,min-sample-time-nsecs",
> >>>>    					   i, &smp);
> >>>> +
> >>>> +		/*
> >>>> +		 * For vrefint channel, ensure that the sampling time cannot
> >>>> +		 * be lower than the one specified in the datasheet
> >>>> +		 */
> >>>> +		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT] &&
> >>>> +		    smp < adc->cfg->ts_vrefint_ns) {
> >>>> +			smp = adc->cfg->ts_vrefint_ns;
> >>>> +		}  
> >>>
> >>> 		if (channels[i].channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT])
> >>> 			smp = max(smp, adc->cfg->ts_vrefint_ns);
> >>>      
> >>>> +
> >>>>    		/* Prepare sampling time settings */
> >>>>    		stm32_adc_smpr_init(adc, channels[i].channel, smp);
> >>>>    	}
> >>>> @@ -2350,6 +2425,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
> >>>>    	.unprepare = stm32h7_adc_unprepare,
> >>>>    	.smp_cycles = stm32h7_adc_smp_cycles,
> >>>>    	.irq_clear = stm32h7_adc_irq_clear,
> >>>> +	.ts_vrefint_ns = 4300,
> >>>>    };
> >>>>    
> >>>>    static const struct of_device_id stm32_adc_of_match[] = {  
> >>>      
> >   


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 53+ messages in thread

end of thread, other threads:[~2021-09-26 12:09 UTC | newest]

Thread overview: 53+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-08 15:54 [PATCH 0/7] add internal channels support Olivier Moysan
2021-09-08 15:54 ` Olivier Moysan
2021-09-08 15:54 ` Olivier Moysan
2021-09-08 15:54 ` [PATCH 1/7] dt-bindings: iio: adc: add generic channel binding Olivier Moysan
2021-09-08 15:54   ` Olivier Moysan
2021-09-08 15:54   ` Olivier Moysan
2021-09-11 15:51   ` Jonathan Cameron
2021-09-11 15:51     ` Jonathan Cameron
2021-09-11 15:51     ` Jonathan Cameron
2021-09-21 17:08   ` Rob Herring
2021-09-21 17:08     ` Rob Herring
2021-09-21 17:08     ` Rob Herring
2021-09-08 15:54 ` [PATCH 2/7] dt-bindings: iio: adc: add nvmem support for vrefint internal channel Olivier Moysan
2021-09-08 15:54   ` Olivier Moysan
2021-09-08 15:54   ` Olivier Moysan
2021-09-21 17:10   ` Rob Herring
2021-09-21 17:10     ` Rob Herring
2021-09-21 17:10     ` Rob Herring
2021-09-08 15:54 ` [PATCH 3/7] iio: adc stm32-adc: split channel init into several routines Olivier Moysan
2021-09-08 15:54   ` Olivier Moysan
2021-09-08 15:54   ` Olivier Moysan
2021-09-08 15:54 ` [PATCH 4/7] iio: adc: stm32-adc: add support of generic channels binding Olivier Moysan
2021-09-08 15:54   ` Olivier Moysan
2021-09-08 15:54   ` Olivier Moysan
2021-09-11 16:08   ` Jonathan Cameron
2021-09-11 16:08     ` Jonathan Cameron
2021-09-11 16:08     ` Jonathan Cameron
2021-09-08 15:54 ` [PATCH 5/7] iio: adc: stm32-adc: add support of internal channels Olivier Moysan
2021-09-08 15:54   ` Olivier Moysan
2021-09-08 15:54   ` Olivier Moysan
2021-09-11 16:17   ` Jonathan Cameron
2021-09-11 16:17     ` Jonathan Cameron
2021-09-11 16:17     ` Jonathan Cameron
2021-09-08 15:54 ` [PATCH 6/7] iio: adc: stm32-adc: add vrefint calibration support Olivier Moysan
2021-09-08 15:54   ` Olivier Moysan
2021-09-08 15:54   ` Olivier Moysan
2021-09-11 16:28   ` Jonathan Cameron
2021-09-11 16:28     ` Jonathan Cameron
2021-09-11 16:28     ` Jonathan Cameron
2021-09-15 10:02     ` Olivier MOYSAN
2021-09-15 10:02       ` Olivier MOYSAN
2021-09-18 18:42       ` Jonathan Cameron
2021-09-18 18:42         ` Jonathan Cameron
2021-09-22  7:53         ` Olivier MOYSAN
2021-09-22  7:53           ` Olivier MOYSAN
2021-09-26 12:09           ` Jonathan Cameron
2021-09-26 12:09             ` Jonathan Cameron
2021-09-08 15:54 ` [PATCH 7/7] iio: adc: stm32-adc: use generic binding for sample-time Olivier Moysan
2021-09-08 15:54   ` Olivier Moysan
2021-09-08 15:54   ` Olivier Moysan
2021-09-11 15:44 ` [PATCH 0/7] add internal channels support Jonathan Cameron
2021-09-11 15:44   ` Jonathan Cameron
2021-09-11 15:44   ` Jonathan Cameron

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.