Skip to contentSkip to conversation

adding support for Microchip PAC193X Power Monitor

PATCH In review [email protected] 5 participants 22 messages Feb 20, 2023 – Mar 25, 2023

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover adding support for Microchip PAC193X Power Monitor

From: Marius Cristea <[email protected]>

Adding support for Microchip PAC193X series of Power Monitor with Accumulator chip family.

Differences related to previous patch:

v1:
- first version comitted to review

Marius Cristea (2):
dt-bindings: iio: adc: adding dt-bindings for PAC193X
iio: adc: adding support for pac193x

 .../bindings/iio/adc/microchip,pac193x.yaml   |  122 +
 MAINTAINERS                                   |    7 +
 drivers/iio/adc/Kconfig                       |   12 +
 drivers/iio/adc/Makefile                      |    1 +
 drivers/iio/adc/pac193x.c                     | 2072 +++++++++++++++++
 5 files changed, 2214 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml
 create mode 100644 drivers/iio/adc/pac193x.c
Signature · 1 line
2.34.1
#1 lore raw

PATCH 1/2 dt-bindings: iio: adc: adding dt-bindings for PAC193X

From: Marius Cristea <[email protected]>

This is the device tree schema for iio driver for Microchip PAC193X series of Power Monitors with Accumulator.

---
 .../bindings/iio/adc/microchip,pac193x.yaml   | 122 ++++++++++++++++++
 1 file changed, 122 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +122 -0
new file mode 100644
index 000000000000..e4ea560991e9
@@ -0,0 +1,122 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/microchip,pac193x.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip PAC193X Power Monitors with Accumulator
+
+maintainers:
+  - Marius Cristea <[email protected]>
+
+properties:
+  compatible:
+    enum:
+      - microchip,pac1934
+      - microchip,pac1933
+      - microchip,pac1932
+      - microchip,pac1931
+
+  reg:
+    maxItems: 1
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+  microchip,samp-rate:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: Sampling rate for all device's channels.
+    enum: [8, 64, 256, 1024]
+    default: 1024
+
+required:
+  - compatible
+  - reg
+  - microchip,samp-rate
+  - "#address-cells"
+  - "#size-cells"
+
+patternProperties:
+  "^channel([1-4])@[1-4]+$":
+    $ref: "adc.yaml"
+    type: object
+    description: Represents the external channels which are connected to the ADC.
+
+    properties:
+      reg:
+        description: The channel number.
+          It can have up to 4 channels, numbered from 1 to 4.
+        items:
+          - minimum: 1
+            maximum: 4
+
+      microchip,uohms-shunt-res:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description: |
+          Value in micro Ohms of the shunt resistor connected between
+          the SENSE+ and SENSE- inputs, across which the current is measured. Value
+          is needed to compute the scaling of the measured current.
+
+      microchip,rail-name:
+        $ref: /schemas/types.yaml#/definitions/string
+        description: Name of the monitored power rail.
+
+      microchip,bi-directional:
+        description: Whether the channel is bi-directional.
+        type: boolean
+
+    required:
+      - reg
+      - microchip,uohms-shunt-res
+      - microchip,rail-name
+
+    additionalProperties: false
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        pac193x: pac193x@10 {
+            compatible = "microchip,pac1934";
+            reg = <0x10>;
+
+            #address-cells = <1>;
+            #size-cells = <0>;
+            microchip,samp-rate = <64>;
+
+            channel1@1 {
+                reg = <0x1>;
+                microchip,uohms-shunt-res = <24900000>;
+                microchip,rail-name = "CPU";
+            };
+
+            channel2@2 {
+                reg = <0x2>;
+                microchip,uohms-shunt-res = <49900000>;
+                microchip,rail-name = "GPU";
+            };
+
+            channel3@3 {
+                reg = <0x3>;
+                microchip,uohms-shunt-res = <75000000>;
+                microchip,rail-name = "MEM";
+                microchip,bi-directional;
+            };
+
+            channel4@4 {
+                reg = <0x4>;
+                microchip,uohms-shunt-res = <100000000>;
+                microchip,rail-name = "NET";
+                microchip,bi-directional;
+            };
+        };
+    };
+
+...
Signature · 1 line
2.34.1
#2 lore raw

Re: [PATCH v1 1/2] dt-bindings: iio: adc: adding dt-bindings for PAC193X

Quoted 1 line · On 20/02/2023 13:32, [email protected] wrote:

From: Marius Cristea <[email protected]>

Subject: drop second/last, redundant "dt-bindings for". The "dt-bindings" prefix is already stating that these are bindings.

Quoted 3 lines

This is the device tree schema for iio driver for Microchip PAC193X series of Power Monitors with Accumulator.

Do not use "This commit/patch".
https://elixir.bootlin.com/linux/v5.17.1/source/Documentation/process/submitting-patches.rst#L95

Use imperative - also for subject.

Please use scripts/get_maintainers.pl to get a list of necessary people and lists to CC. It might happen, that command when run on an older kernel, gives you outdated entries. Therefore please be sure you base your patches on recent Linux kernel.

Quoted 21 lines
---
 .../bindings/iio/adc/microchip,pac193x.yaml   | 122 ++++++++++++++++++
 1 file changed, 122 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +8 -0
new file mode 100644
index 000000000000..e4ea560991e9
@@ -0,0 +1,122 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/microchip,pac193x.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip PAC193X Power Monitors with Accumulator
+

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 18

Quoted 10 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +10 -0
+maintainers:
+  - Marius Cristea <[email protected]>
+
+properties:
+  compatible:
+    enum:
+      - microchip,pac1934
+      - microchip,pac1933
+      - microchip,pac1932
+      - microchip,pac1931

Maybe order these numerically (ascending)?

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 31

Quoted 13 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +13 -0
+
+  reg:
+    maxItems: 1
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+  microchip,samp-rate:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: Sampling rate for all device's channels.

What are the units? rate is usually in hz, which should be expressed in unit suffix (property name)

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 35

Quoted 4 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +4 -0
+    enum: [8, 64, 256, 1024]
+    default: 1024
+
+required:

required goes after all properties, so after patternProperties

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 43

Quoted 8 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +8 -0
+  - compatible
+  - reg
+  - microchip,samp-rate
+  - "#address-cells"
+  - "#size-cells"
+
+patternProperties:
+  "^channel([1-4])@[1-4]+$":

Why first reg-pattern ([1-4])? For what do you need it?

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 44

Quoted 1 line
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +1 -0
+    $ref: "adc.yaml"

Drop quotes

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 56

Quoted 12 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +12 -0
+    type: object
+    description: Represents the external channels which are connected to the ADC.
+
+    properties:
+      reg:
+        description: The channel number.
+          It can have up to 4 channels, numbered from 1 to 4.
+        items:
+          - minimum: 1
+            maximum: 4
+
+      microchip,uohms-shunt-res:

Not a correct unit suffix. Use standard properties:
git grep shunt -- Documentation/devicetree/

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 65

Quoted 9 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +9 -0
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description: |
+          Value in micro Ohms of the shunt resistor connected between
+          the SENSE+ and SENSE- inputs, across which the current is measured. Value
+          is needed to compute the scaling of the measured current.
+
+      microchip,rail-name:
+        $ref: /schemas/types.yaml#/definitions/string
+        description: Name of the monitored power rail.

Why do you need it? Why this is a property of DT? Aren't you now duplicating label?

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 68

Quoted 3 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +3 -0
+
+      microchip,bi-directional:
+        description: Whether the channel is bi-directional.

Describe here what is a "bi-directional" channel for ADC... Maybe it is obvious, maybe not. For me it is not and none of other devices have it.

Quoted 8 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +8 -0
+        type: boolean
+
+    required:
+      - reg
+      - microchip,uohms-shunt-res
+      - microchip,rail-name
+
+    additionalProperties: false

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 86

Quoted 10 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +10 -0
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        pac193x: pac193x@10 {

Node names should be generic.
https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#generic-names-recommendation

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 94

Quoted 8 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +8 -0
+            compatible = "microchip,pac1934";
+            reg = <0x10>;
+
+            #address-cells = <1>;
+            #size-cells = <0>;
+            microchip,samp-rate = <64>;
+
+            channel1@1 {

1@1 is for sure not generic...

Best regards,
Krzysztof

maintainer
#3 lore raw

Re: [PATCH v1 1/2] dt-bindings: iio: adc: adding dt-bindings for PAC193X

Hi Marius

Welcome to IIO.

I'll comment on top of Krzystof's review to avoid duplication.

Quoted 4 lines
Quoted 4 lines
---
 .../bindings/iio/adc/microchip,pac193x.yaml   | 122 ++++++++++++++++++

Wild cards in names bite us far too often. Name this and the driver after one of the supported parts.

Quoted 30 lines
Quoted 17 lines
 1 file changed, 122 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +8 -0
new file mode 100644
index 000000000000..e4ea560991e9
@@ -0,0 +1,122 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/microchip,pac193x.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip PAC193X Power Monitors with Accumulator
+

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 18

Quoted 10 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +10 -0
+maintainers:
+  - Marius Cristea <[email protected]>
+
+properties:
+  compatible:
+    enum:
+      - microchip,pac1934
+      - microchip,pac1933
+      - microchip,pac1932
+      - microchip,pac1931  

Maybe order these numerically (ascending)?

Quoted 16 lines

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 31

Quoted 13 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +13 -0
+
+  reg:
+    maxItems: 1
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+  microchip,samp-rate:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: Sampling rate for all device's channels.  

What are the units? rate is usually in hz, which should be expressed in unit suffix (property name)]

It's unusual for sampling rate to be a property of the hardware and hence suitable for DT binding. Normally we make this a userspace control instead. If there is a reason for doing it from DT, that wants to be mentioned here.

Quoted 23 lines

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 35

Quoted 5 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +4 -0
+    enum: [8, 64, 256, 1024]
+    default: 1024
+
+required:  

required goes after all properties, so after patternProperties

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 43

Quoted 8 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +8 -0
+  - compatible
+  - reg
+  - microchip,samp-rate
+  - "#address-cells"
+  - "#size-cells"
+
+patternProperties:
+  "^channel([1-4])@[1-4]+$":  

Why first reg-pattern ([1-4])? For what do you need it?

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 44

Quoted 1 line
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +1 -0
+    $ref: "adc.yaml"  

Drop quotes

Quoted 15 lines

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 56

Quoted 12 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +12 -0
+    type: object
+    description: Represents the external channels which are connected to the ADC.
+
+    properties:
+      reg:
+        description: The channel number.
+          It can have up to 4 channels, numbered from 1 to 4.
+        items:
+          - minimum: 1
+            maximum: 4
+
+      microchip,uohms-shunt-res:  

Not a correct unit suffix. Use standard properties:
git grep shunt -- Documentation/devicetree/

Check for existing relevant properties. Here we have shunt-resistor-micro-ohms in common use in both IIO and hwmon.

Quoted 12 lines

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 65

Quoted 11 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +9 -0
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description: |
+          Value in micro Ohms of the shunt resistor connected between
+          the SENSE+ and SENSE- inputs, across which the current is measured. Value
+          is needed to compute the scaling of the measured current.
+
+      microchip,rail-name:
+        $ref: /schemas/types.yaml#/definitions/string
+        description: Name of the monitored power rail.  
Quoted 2 lines

Why do you need it? Why this is a property of DT? Aren't you now duplicating label?

Agreed. Sounds like label to me as well.

Quoted 8 lines

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 68

Quoted 4 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +3 -0
+
+      microchip,bi-directional:
+        description: Whether the channel is bi-directional.  

Describe here what is a "bi-directional" channel for ADC... Maybe it is obvious, maybe not. For me it is not and none of other devices have it.

From datasheet this seems to be a novel description of whether the ADC is measuring just positive or allows for negative voltage as well (i.e. the current through the shut resistor is going on the other direction). Can we relate this to a biopolar / unipolar decision on a simple voltage ADC? For that we have standard DT bindings in iio/adc/adc.yaml

Quoted 22 lines
Quoted 8 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +8 -0
+        type: boolean
+
+    required:
+      - reg
+      - microchip,uohms-shunt-res
+      - microchip,rail-name
+
+    additionalProperties: false

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 86

Quoted 10 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +10 -0
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        pac193x: pac193x@10 {  

Node names should be generic.
https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#generic-names-recommendation

Quoted 10 lines

Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml line 94

Quoted 8 lines
Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml new file +8 -0
+            compatible = "microchip,pac1934";
+            reg = <0x10>;
+
+            #address-cells = <1>;
+            #size-cells = <0>;
+            microchip,samp-rate = <64>;
+
+            channel1@1 {  

1@1 is for sure not generic...

Standard naming for channel nodes in adc.yaml
Some bindings predate that, but for new ones please match it.

Thanks,

Jonathan

Quoted 6 lines

Best regards,
Krzysztof

#4 lore raw

Re: [PATCH v1 1/2] dt-bindings: iio: adc: adding dt-bindings for PAC193X

Hi Jonathan,

Thank you so much! I'm happy to contribute to IIO.

I will remove some section because I agree with the propose solution. For the rest I will comment below..

Quoted 9 lines · Jonathan Cameron
Quoted 8 lines
Quoted 4 lines

+
+  microchip,samp-rate:
+    $ref: /schemas/types.yaml#/definitions/uint32 +    description: Sampling rate for all device's channels.

What are the units? rate is usually in hz, which should be expressed in
unit suffix (property name)]

Quoted 7 lines

It's unusual for sampling rate to be a property of the hardware and hence
suitable for DT binding. Normally we make this a userspace control instead.
If there is a reason for doing it from DT, that wants to be mentioned here.

Here I could change it into the datarate (as in iio/adc/ti,ads1015.yaml). The units are samples per second. My intention was to be alingned with the datasheet.

My intention was to let the user configure the sample rate as soon as posile during the startup (the PAC device's own power consumtion will increase with the sampling rate - default the chip will start with the maximum samples per second).

Quoted 13 lines
Quoted 4 lines
Quoted 9 lines
Quoted 3 lines

+
+      microchip,bi-directional:
+        description: Whether the channel is bi-directional.

Describe here what is a "bi-directional" channel for ADC... Maybe it is
obvious, maybe not. For me it is not and none of other devices have it.

Quoted 10 lines

From datasheet this seems to be a novel description of whether the ADC
is measuring just positive or allows for negative voltage as well (i.e.
the current through the shut resistor is going on the other direction).
Can we relate this to a biopolar / unipolar decision on a simple voltage ADC?
For that we have standard DT bindings in iio/adc/adc.yaml

Yes, the PAC device can measure voltages as unipolar or bipolar but also current as unidirectional or bidirectional (we are measuring the voltage across a shunt resistor). We are also reporting power and is not a "usual" thing to report power as unipolar or bipolar. Bi- directional power makes more sens if you are thinking to monitor a charge discharge cycle for a battery pack.

Thanks,
Marius

#5 lore raw

Re: [PATCH v1 1/2] dt-bindings: iio: adc: adding dt-bindings for PAC193X

Quoted 28 lines · On 06/03/2023 14:53, [email protected] wrote:

Hi Jonathan,

Thank you so much! I'm happy to contribute to IIO.

I will remove some section because I agree with the propose solution. For the rest I will comment below..

Quoted 9 lines · Jonathan Cameron
Quoted 8 lines
Quoted 4 lines

+
+  microchip,samp-rate:
+    $ref: /schemas/types.yaml#/definitions/uint32 +    description: Sampling rate for all device's channels.

What are the units? rate is usually in hz, which should be expressed in
unit suffix (property name)]

Quoted 7 lines

It's unusual for sampling rate to be a property of the hardware and hence
suitable for DT binding. Normally we make this a userspace control instead.
If there is a reason for doing it from DT, that wants to be mentioned here.

Quoted 8 lines

Here I could change it into the datarate (as in iio/adc/ti,ads1015.yaml). The units are samples per second. My intention was to be alingned with the datasheet.

My intention was to let the user configure the sample rate as soon as posile during the startup (the PAC device's own power consumtion will increase with the sampling rate - default the chip will start with the maximum samples per second).

Then maybe the default should be just lowest rate and user will adjust it further? Since this is user-space knob, why this should be even configurable per board?

Best regards,
Krzysztof

#6 lore raw

Re: [PATCH v1 1/2] dt-bindings: iio: adc: adding dt-bindings for PAC193X

In reply to [email protected]
Quoted 27 lines · On Mon, 6 Mar 2023 13:53:27 +0000 <[email protected]> wrote:

Hi Jonathan,

Thank you so much! I'm happy to contribute to IIO.

I will remove some section because I agree with the propose solution. For the rest I will comment below..

Quoted 9 lines · Jonathan Cameron
Quoted 8 lines
Quoted 4 lines

+
+  microchip,samp-rate:
+    $ref: /schemas/types.yaml#/definitions/uint32 +    description: Sampling rate for all device's channels.

What are the units? rate is usually in hz, which should be expressed in
unit suffix (property name)]

Quoted 7 lines

It's unusual for sampling rate to be a property of the hardware and hence
suitable for DT binding. Normally we make this a userspace control instead.
If there is a reason for doing it from DT, that wants to be mentioned here.

Quoted 8 lines

Here I could change it into the datarate (as in iio/adc/ti,ads1015.yaml). The units are samples per second. My intention was to be alingned with the datasheet.

My intention was to let the user configure the sample rate as soon as posile during the startup (the PAC device's own power consumtion will increase with the sampling rate - default the chip will start with the maximum samples per second).

Feel free to have the driver set it to the minimum frequency unconditionally in probe.

Quoted 26 lines
Quoted 15 lines
Quoted 6 lines
Quoted 9 lines
Quoted 3 lines

+
+      microchip,bi-directional:
+        description: Whether the channel is bi-directional.

Describe here what is a "bi-directional" channel for ADC... Maybe it is
obvious, maybe not. For me it is not and none of other devices have it.

Quoted 10 lines

From datasheet this seems to be a novel description of whether the ADC
is measuring just positive or allows for negative voltage as well (i.e.
the current through the shut resistor is going on the other direction).
Can we relate this to a biopolar / unipolar decision on a simple voltage ADC?
For that we have standard DT bindings in iio/adc/adc.yaml

Quoted 6 lines

Yes, the PAC device can measure voltages as unipolar or bipolar but also current as unidirectional or bidirectional (we are measuring the voltage across a shunt resistor). We are also reporting power and is not a "usual" thing to report power as unipolar or bipolar. Bi- directional power makes more sens if you are thinking to monitor a charge discharge cycle for a battery pack.

I'm still a little confused. If you are bipolar and hence can measure postive and negative voltage between two pins, how is that different to bidirectional? Other than for bidirection we are talking current rather than voltage.

Quoted 5 lines

Thanks,
Marius

#7 lore raw

PATCH 2/2 iio: adc: adding support for pac193x

In reply to [email protected]

From: Marius Cristea <[email protected]>

This is the iio driver for Microchip
PAC193X series of Power Monitor with Accumulator chip family.

---
 MAINTAINERS               |    7 +
 drivers/iio/adc/Kconfig   |   12 +
 drivers/iio/adc/Makefile  |    1 +
 drivers/iio/adc/pac193x.c | 2072 +++++++++++++++++++++++++++++++++++++
 4 files changed, 2092 insertions(+)
 create mode 100644 drivers/iio/adc/pac193x.c
MAINTAINERS +7 -0
index c81bbb771678..6675dc1b6b19 100644
@@ -8187,6 +8187,13 @@ S:	Maintained
 F:	Documentation/ABI/testing/sysfs-driver-intel-m10-bmc-sec-update
 F:	drivers/fpga/intel-m10-bmc-sec-update.c
 
+MICROCHIP PAC193X POWER/ENERGY MONITOR DRIVER
+M:	Marius Cristea <marius.cristea@@microchip.com>
+L:	[email protected]
+S:	Supported
+F:	Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml
+F:	drivers/iio/adc/pac193x.c
+
 MICROCHIP POLARFIRE FPGA DRIVERS
 M:	Conor Dooley <[email protected]>
 R:	Ivan Bornyakov <[email protected]>
drivers/iio/adc/Kconfig +12 -0
index 4a95c843a91b..ae4f60e290c4 100644
@@ -867,6 +867,18 @@ config NPCM_ADC
 	  This driver can also be built as a module. If so, the module
 	  will be called npcm_adc.
 
+config PAC193X
+	tristate "Microchip Technology PAC193X driver"
+	depends on I2C
+	depends on IIO
+	help
+	  Say yes here to build support for Microchip Technology's PAC1934,
+	  PAC1933, PAC1932, PAC1931 Single/Multi-Channel Power Monitor with
+	  Accumulator.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called pac193x.
+
 config PALMAS_GPADC
 	tristate "TI Palmas General Purpose ADC"
 	depends on MFD_PALMAS
drivers/iio/adc/Makefile +1 -0
index 36c18177322a..81824128c6cd 100644
@@ -80,6 +80,7 @@ obj-$(CONFIG_MP2629_ADC) += mp2629_adc.o
 obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
 obj-$(CONFIG_NAU7802) += nau7802.o
 obj-$(CONFIG_NPCM_ADC) += npcm_adc.o
+obj-$(CONFIG_PAC193X) += pac193x.o
 obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
 obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o
 obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
drivers/iio/adc/pac193x.c new file +2072 -0

2,072 lines not on this page · raw message

Signature · 1 line
2.34.1
#8 lore raw

Re: [PATCH v1 2/2] iio: adc: adding support for pac193x

I love your patch! Perhaps something to improve:

Hi,

I love your patch! Perhaps something to improve:

[auto build test WARNING on jic23-iio/togreg]
[also build test WARNING on linus/master v6.2 next-20230220] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/marius-cristea-microchip-com/dt-bindings-iio-adc-adding-dt-bindings-for-PAC193X/20230220-203540
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
patch link:    https://lore.kernel.org/r/20230220123232.413029-3-marius.cristea%40microchip.com
patch subject: [PATCH v1 2/2] iio: adc: adding support for pac193x
config: m68k-allmodconfig (https://download.01.org/0day-ci/archive/20230221/[email protected]/config)
compiler: m68k-linux-gcc (GCC) 12.1.0

reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/fd3be916ffe18735a98bdc55ccc0cb5f3097582c
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review marius-cristea-microchip-com/dt-bindings-iio-adc-adding-dt-bindings-for-PAC193X/20230220-203540 git checkout fd3be916ffe18735a98bdc55ccc0cb5f3097582c
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=m68k olddefconfig COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=m68k SHELL=/bin/bash drivers/iio/adc/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Link: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All warnings (new ones prefixed by >>):

drivers/iio/adc/pac193x.c: In function 'pac193x_acpi_get_acpi_match_entry':

>> drivers/iio/adc/pac193x.c:1402:21: warning: variable 'status' set but not used [-Wunused-but-set-variable]
    1402 |         acpi_status status;
         |                     ^~~~~~

vim +/status +1402 drivers/iio/adc/pac193x.c

1399
1400 static char *pac193x_acpi_get_acpi_match_entry(acpi_handle handle)
1401 {

> 1402		acpi_status status;

1403 union acpi_object *name_object;
1404 struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
1405
1406 status = acpi_evaluate_object(handle, "_HID", NULL, &buffer);
1407 name_object = buffer.pointer;
1408
1409 return name_object->string.pointer;
1410 }
1411

Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests
#9 lore raw

Re: [PATCH v1 2/2] iio: adc: adding support for pac193x

In reply to [email protected]
I love your patch! Perhaps something to improve:

Hi,

I love your patch! Perhaps something to improve:

[auto build test WARNING on jic23-iio/togreg]
[also build test WARNING on linus/master v6.2 next-20230220] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/marius-cristea-microchip-com/dt-bindings-iio-adc-adding-dt-bindings-for-PAC193X/20230220-203540
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
patch link:    https://lore.kernel.org/r/20230220123232.413029-3-marius.cristea%40microchip.com
patch subject: [PATCH v1 2/2] iio: adc: adding support for pac193x
config: powerpc-allmodconfig (https://download.01.org/0day-ci/archive/20230221/[email protected]/config)
compiler: powerpc-linux-gcc (GCC) 12.1.0

reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/fd3be916ffe18735a98bdc55ccc0cb5f3097582c
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review marius-cristea-microchip-com/dt-bindings-iio-adc-adding-dt-bindings-for-PAC193X/20230220-203540 git checkout fd3be916ffe18735a98bdc55ccc0cb5f3097582c
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=powerpc olddefconfig COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=powerpc SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Link: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All warnings (new ones prefixed by >>):

drivers/iio/adc/pac193x.c: In function 'pac193x_acpi_get_acpi_match_entry':
drivers/iio/adc/pac193x.c:1402:21: warning: variable 'status' set but not used [-Wunused-but-set-variable]

    1402 |         acpi_status status;
         |                     ^~~~~~
   drivers/iio/adc/pac193x.c: In function 'pac193x_match_acpi_device':
>> drivers/iio/adc/pac193x.c:1469:57: warning: '<<' in boolean context, did you mean '<'? [-Wint-in-bool-context]
    1469 |         chip_info->bi_dir[0] = (bi_dir_mask & (1 << 1)) << 1;
         |                                ~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
   drivers/iio/adc/pac193x.c:1470:57: warning: '<<' in boolean context, did you mean '<'? [-Wint-in-bool-context]
    1470 |         chip_info->bi_dir[0] = (bi_dir_mask & (1 << 2)) << 2;
         |                                ~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
   drivers/iio/adc/pac193x.c:1471:57: warning: '<<' in boolean context, did you mean '<'? [-Wint-in-bool-context]
    1471 |         chip_info->bi_dir[0] = (bi_dir_mask & (1 << 3)) << 3;
         |                                ~~~~~~~~~~~~~~~~~~~~~~~~~^~~~

vim +1469 drivers/iio/adc/pac193x.c

1411
1412 static const char *pac193x_match_acpi_device(struct i2c_client *client,
1413 struct pac193x_chip_info *chip_info)
1414 {
1415 char *name;
1416 acpi_handle handle;
1417 union acpi_object *rez;
1418 unsigned short bi_dir_mask;
1419 int idx, i;
1420
1421 handle = ACPI_HANDLE(&client->dev);
1422 name = pac193x_acpi_get_acpi_match_entry(handle);
1423 if (!name)
1424 return NULL;
1425
1426 rez = pac193x_acpi_eval_function(handle, 0, PAC193X_ACPI_GET_NAMES_AND_MOHMS_VALS);
1427
1428 if (!rez)
1429 return NULL;
1430
1431 for (i = 0; i < rez->package.count; i += 2) {
1432 idx = i / 2;
1433 chip_info->channel_names[idx] =
1434 devm_kmemdup(&client->dev, rez->package.elements[i].string.pointer,
1435 (size_t)rez->package.elements[i].string.length + 1,
1436 GFP_KERNEL);
1437 chip_info->channel_names[idx][rez->package.elements[i].string.length] = '\0';
1438 chip_info->shunts[idx] =
1439 rez->package.elements[i + 1].integer.value * 1000;
1440 chip_info->active_channels[idx] = (chip_info->shunts[idx] != 0);
1441 }
1442
1443 kfree(rez);
1444
1445 rez = pac193x_acpi_eval_function(handle, 1, PAC193X_ACPI_GET_UOHMS_VALS);
1446 if (!rez) {
1447 /*
1448 * initialising with default values
1449 * we assume all channels are unidectional(the mask is zero)
1450 * and assign the default sampling rate
1451 */
1452 chip_info->sample_rate_value = PAC193X_DEFAULT_CHIP_SAMP_SPEED;
1453 return name;
1454 }
1455
1456 for (i = 0; i < rez->package.count; i++) {
1457 idx = i;
1458 chip_info->shunts[idx] = rez->package.elements[i].integer.value;
1459 chip_info->active_channels[idx] = (chip_info->shunts[idx] != 0);
1460 }
1461
1462 kfree(rez);
1463
1464 rez = pac193x_acpi_eval_function(handle, 1, PAC193X_ACPI_GET_BIPOLAR_SETTINGS);
1465 if (!rez)
1466 return NULL;
1467 bi_dir_mask = rez->package.elements[0].integer.value;
1468 chip_info->bi_dir[0] = (bi_dir_mask & (1 << 0)) << 0;

> 1469		chip_info->bi_dir[0] = (bi_dir_mask & (1 << 1)) << 1;

1470 chip_info->bi_dir[0] = (bi_dir_mask & (1 << 2)) << 2;
1471 chip_info->bi_dir[0] = (bi_dir_mask & (1 << 3)) << 3;
1472 kfree(rez);
1473
1474 rez = pac193x_acpi_eval_function(handle, 1, PAC193X_ACPI_GET_SAMP);
1475 if (!rez)
1476 return NULL;
1477
1478 chip_info->sample_rate_value = rez->package.elements[0].integer.value;
1479 kfree(rez);
1480
1481 return name;
1482 }
1483

Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests
#10 lore raw

Re: [PATCH v1 2/2] iio: adc: adding support for pac193x

In reply to [email protected]
Quoted 5 lines · On 20/02/2023 13:32, [email protected] wrote:

From: Marius Cristea <[email protected]>

This is the iio driver for Microchip
PAC193X series of Power Monitor with Accumulator chip family.

Quoted 9 lines
---
 MAINTAINERS               |    7 +
 drivers/iio/adc/Kconfig   |   12 +
 drivers/iio/adc/Makefile  |    1 +
 drivers/iio/adc/pac193x.c | 2072 +++++++++++++++++++++++++++++++++++++
 4 files changed, 2092 insertions(+)
 create mode 100644 drivers/iio/adc/pac193x.c

Thank you for your patch. There is something to discuss/improve.

Quoted 12 lines
drivers/iio/adc/pac193x.c new file +12 -0
+
+#define PAC193X_NEG_PWR_CH1_BIDI(x)	((x) ? BIT(7) : 0)
+#define PAC193X_NEG_PWR_CH2_BIDI(x)	((x) ? BIT(6) : 0)
+#define PAC193X_NEG_PWR_CH3_BIDI(x)	((x) ? BIT(5) : 0)
+#define PAC193X_NEG_PWR_CH4_BIDI(x)	((x) ? BIT(4) : 0)
+#define PAC193X_NEG_PWR_CH1_BIDV(x)	((x) ? BIT(3) : 0)
+#define PAC193X_NEG_PWR_CH2_BIDV(x)	((x) ? BIT(2) : 0)
+#define PAC193X_NEG_PWR_CH3_BIDV(x)	((x) ? BIT(1) : 0)
+#define PAC193X_NEG_PWR_CH4_BIDV(x)	((x) ? BIT(0) : 0)
+
+/*
+ * Universal Unique Identifier (UUID),

drivers/iio/adc/pac193x.c line 261

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+ * 033771E0-1705-47B4-9535-D1BBE14D9A09, is
+ * reserved to Microchip for the PAC193X and must not be changed
+ */
+#define PAC193X_DSM_UUID		"033771E0-1705-47B4-9535-D1BBE14D9A09"
+
+enum pac193x_ids {
+	pac1934,
+	pac1933,
+	pac1932,
+	pac1931

Enums are usually uppercase.

Best regards,
Krzysztof

maintainer
#11 lore raw

Re: [PATCH v1 2/2] iio: adc: adding support for pac193x

Quoted 35 lines · On Tue, 21 Feb 2023 14:46:08 +0100 Krzysztof Kozlowski <[email protected]> wrote:
Quoted 5 lines · On 20/02/2023 13:32, [email protected] wrote:

From: Marius Cristea <[email protected]>

This is the iio driver for Microchip
PAC193X series of Power Monitor with Accumulator chip family.

Quoted 9 lines
---
 MAINTAINERS               |    7 +
 drivers/iio/adc/Kconfig   |   12 +
 drivers/iio/adc/Makefile  |    1 +
 drivers/iio/adc/pac193x.c | 2072 +++++++++++++++++++++++++++++++++++++
 4 files changed, 2092 insertions(+)
 create mode 100644 drivers/iio/adc/pac193x.c

Thank you for your patch. There is something to discuss/improve.

Quoted 16 lines
drivers/iio/adc/pac193x.c new file +16 -0
+
+#define PAC193X_NEG_PWR_CH1_BIDI(x)	((x) ? BIT(7) : 0)
+#define PAC193X_NEG_PWR_CH2_BIDI(x)	((x) ? BIT(6) : 0)
+#define PAC193X_NEG_PWR_CH3_BIDI(x)	((x) ? BIT(5) : 0)
+#define PAC193X_NEG_PWR_CH4_BIDI(x)	((x) ? BIT(4) : 0)
+#define PAC193X_NEG_PWR_CH1_BIDV(x)	((x) ? BIT(3) : 0)
+#define PAC193X_NEG_PWR_CH2_BIDV(x)	((x) ? BIT(2) : 0)
+#define PAC193X_NEG_PWR_CH3_BIDV(x)	((x) ? BIT(1) : 0)
+#define PAC193X_NEG_PWR_CH4_BIDV(x)	((x) ? BIT(0) : 0)
+
+/*
+ * Universal Unique Identifier (UUID),
+ * 033771E0-1705-47B4-9535-D1BBE14D9A09, is
+ * reserved to Microchip for the PAC193X and must not be changed
+ */
+#define PAC193X_DSM_UUID		"033771E0-1705-47B4-9535-D1BBE14D9A09"
Quoted 8 lines

drivers/iio/adc/pac193x.c line 261

Quoted 6 lines
drivers/iio/adc/pac193x.c new file +6 -0
+
+enum pac193x_ids {
+	pac1934,
+	pac1933,
+	pac1932,
+	pac1931  

Enums are usually uppercase.

I'm not sure there is anything in coding standard around that and a grep finds a mixture of the two when it comes to ones used for IDs. Mind you uppercase is fine :)

Quoted 6 lines

Best regards,
Krzysztof

maintainer
#12 lore raw

Re: [PATCH v1 2/2] iio: adc: adding support for pac193x

Quoted 47 lines · On Sat, 25 Feb 2023 17:19:54 +0000 Jonathan Cameron <[email protected]> wrote:
Quoted 35 lines · On Tue, 21 Feb 2023 14:46:08 +0100 Krzysztof Kozlowski <[email protected]> wrote:
Quoted 5 lines · On 20/02/2023 13:32, [email protected] wrote:

From: Marius Cristea <[email protected]>

This is the iio driver for Microchip
PAC193X series of Power Monitor with Accumulator chip family.

Quoted 9 lines
---
 MAINTAINERS               |    7 +
 drivers/iio/adc/Kconfig   |   12 +
 drivers/iio/adc/Makefile  |    1 +
 drivers/iio/adc/pac193x.c | 2072 +++++++++++++++++++++++++++++++++++++
 4 files changed, 2092 insertions(+)
 create mode 100644 drivers/iio/adc/pac193x.c

Thank you for your patch. There is something to discuss/improve.

Quoted 16 lines
drivers/iio/adc/pac193x.c new file +16 -0
+
+#define PAC193X_NEG_PWR_CH1_BIDI(x)	((x) ? BIT(7) : 0)
+#define PAC193X_NEG_PWR_CH2_BIDI(x)	((x) ? BIT(6) : 0)
+#define PAC193X_NEG_PWR_CH3_BIDI(x)	((x) ? BIT(5) : 0)
+#define PAC193X_NEG_PWR_CH4_BIDI(x)	((x) ? BIT(4) : 0)
+#define PAC193X_NEG_PWR_CH1_BIDV(x)	((x) ? BIT(3) : 0)
+#define PAC193X_NEG_PWR_CH2_BIDV(x)	((x) ? BIT(2) : 0)
+#define PAC193X_NEG_PWR_CH3_BIDV(x)	((x) ? BIT(1) : 0)
+#define PAC193X_NEG_PWR_CH4_BIDV(x)	((x) ? BIT(0) : 0)
+
+/*
+ * Universal Unique Identifier (UUID),
+ * 033771E0-1705-47B4-9535-D1BBE14D9A09, is
+ * reserved to Microchip for the PAC193X and must not be changed
+ */
+#define PAC193X_DSM_UUID		"033771E0-1705-47B4-9535-D1BBE14D9A09"
Quoted 8 lines

drivers/iio/adc/pac193x.c line 261

Quoted 6 lines
drivers/iio/adc/pac193x.c new file +6 -0
+
+enum pac193x_ids {
+	pac1934,
+	pac1933,
+	pac1932,
+	pac1931    

Enums are usually uppercase.

Quoted 3 lines

I'm not sure there is anything in coding standard around that and a grep finds a mixture of the two when it comes to ones used for IDs. Mind you uppercase is fine :)

I take it back. Is indeed in coding style doc. Glad checkpatch doesn't check for this though as we'd get 1000s of 'fixes' if it did and in most cases it doesn't hurt readability. I'll try and be more consistent on this in review going forwards!

Thanks!

Jonathan

Quoted 9 lines
Quoted 8 lines

Best regards,
Krzysztof

#13 lore raw

Re: [PATCH v1 2/2] iio: adc: adding support for pac193x

Hi Krzysztof,

Thank you for your review comments. I will handle all of them.

Thanks,
Marius

Quoted 82 lines · Jonathan Cameron

EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe

Quoted 50 lines · On Sat, 25 Feb 2023 17:19:54 +0000 Jonathan Cameron <[email protected]> wrote:
Quoted 38 lines · On Tue, 21 Feb 2023 14:46:08 +0100 Krzysztof Kozlowski <[email protected]> wrote:
Quoted 6 lines · On 20/02/2023 13:32, [email protected] wrote:

From: Marius Cristea <[email protected]>

This is the iio driver for Microchip
PAC193X series of Power Monitor with Accumulator chip family.

Quoted 9 lines

---
 MAINTAINERS               |    7 +
 drivers/iio/adc/Kconfig   |   12 +
 drivers/iio/adc/Makefile  |    1 +
 drivers/iio/adc/pac193x.c | 2072
+++++++++++++++++++++++++++++++++++++

 4 files changed, 2092 insertions(+)

 create mode 100644 drivers/iio/adc/pac193x.c

Thank you for your patch. There is something to discuss/improve.

Quoted 18 lines
drivers/iio/adc/pac193x.c new file +14 -0
+
+#define PAC193X_NEG_PWR_CH1_BIDI(x)      ((x) ? BIT(7) : 0)
+#define PAC193X_NEG_PWR_CH2_BIDI(x)      ((x) ? BIT(6) : 0)
+#define PAC193X_NEG_PWR_CH3_BIDI(x)      ((x) ? BIT(5) : 0)
+#define PAC193X_NEG_PWR_CH4_BIDI(x)      ((x) ? BIT(4) : 0)
+#define PAC193X_NEG_PWR_CH1_BIDV(x)      ((x) ? BIT(3) : 0)
+#define PAC193X_NEG_PWR_CH2_BIDV(x)      ((x) ? BIT(2) : 0)
+#define PAC193X_NEG_PWR_CH3_BIDV(x)      ((x) ? BIT(1) : 0)
+#define PAC193X_NEG_PWR_CH4_BIDV(x)      ((x) ? BIT(0) : 0)
+
+/*
+ * Universal Unique Identifier (UUID),
+ * 033771E0-1705-47B4-9535-D1BBE14D9A09, is
+ * reserved to Microchip for the PAC193X and must not be

changed
+ */
+#define PAC193X_DSM_UUID         "033771E0-1705-47B4-9535- D1BBE14D9A09"

Quoted 8 lines

drivers/iio/adc/pac193x.c line 261

Quoted 6 lines
drivers/iio/adc/pac193x.c new file +6 -0
+
+enum pac193x_ids {
+ pac1934,
+ pac1933,
+ pac1932,
+ pac1931

Enums are usually uppercase.

Quoted 5 lines

I'm not sure there is anything in coding standard around that and a grep finds
a mixture of the two when it comes to ones used for IDs.  Mind you uppercase
is fine :)

I take it back. Is indeed in coding style doc.  Glad checkpatch doesn't check for
this though as we'd get 1000s of 'fixes' if it did and in most cases it doesn't
hurt readability.  I'll try and be more consistent on this in review going forwards!

Thanks!

Jonathan

Quoted 9 lines
Quoted 8 lines

Best regards,
Krzysztof

maintainer
#14 lore raw

Re: [PATCH v1 2/2] iio: adc: adding support for pac193x

In reply to [email protected]
Quoted 6 lines · On Mon, 20 Feb 2023 14:32:32 +0200 <[email protected]> wrote:

From: Marius Cristea <[email protected]>

This is the iio driver for Microchip
PAC193X series of Power Monitor with Accumulator chip family.

Hi Marius,

As mentioned in DT binding review, wild cards in file names and driver names have caused us mess far too many times in the past. So don't do it, just pick a representative part from the list - if no other basis to chose go with the lowest number currently supported.

The custom ABI in here needs documentation before we can review it properly.

Comments inline.

It's a big driver, so I'm sure more stuff will come up next round just due to mental exhaustion reviewing it in one go!

Thanks,

Jonathan

Quoted 32 lines
---
 MAINTAINERS               |    7 +
 drivers/iio/adc/Kconfig   |   12 +
 drivers/iio/adc/Makefile  |    1 +
 drivers/iio/adc/pac193x.c | 2072 +++++++++++++++++++++++++++++++++++++
 4 files changed, 2092 insertions(+)
 create mode 100644 drivers/iio/adc/pac193x.c
MAINTAINERS +7 -0
index c81bbb771678..6675dc1b6b19 100644
@@ -8187,6 +8187,13 @@ S:	Maintained
 F:	Documentation/ABI/testing/sysfs-driver-intel-m10-bmc-sec-update
 F:	drivers/fpga/intel-m10-bmc-sec-update.c
 
+MICROCHIP PAC193X POWER/ENERGY MONITOR DRIVER
+M:	Marius Cristea <marius.cristea@@microchip.com>
+L:	[email protected]
+S:	Supported
+F:	Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml
+F:	drivers/iio/adc/pac193x.c
+
 MICROCHIP POLARFIRE FPGA DRIVERS
 M:	Conor Dooley <[email protected]>
 R:	Ivan Bornyakov <[email protected]>
drivers/iio/adc/Kconfig +0 -0
index 4a95c843a91b..ae4f60e290c4 100644
@@ -867,6 +867,18 @@ config NPCM_ADC
 	  This driver can also be built as a module. If so, the module

drivers/iio/adc/Kconfig config NPCM_ADC · line 877

Quoted 10 lines
drivers/iio/adc/Kconfig +8 -0
 	  will be called npcm_adc.
 
+config PAC193X
+	tristate "Microchip Technology PAC193X driver"
+	depends on I2C
+	depends on IIO
+	help
+	  Say yes here to build support for Microchip Technology's PAC1934,
+	  PAC1933, PAC1932, PAC1931 Single/Multi-Channel Power Monitor with
+	  Accumulator.

Order preferred as 1, 2, 3, 4
Nothing really wrong with this way around other than it not being the common choice so being slightly unexpected if someone is reading this fast.

Quoted 41 lines
drivers/iio/adc/Kconfig +4 -0
+
+	  This driver can also be built as a module. If so, the module
+	  will be called pac193x.
+
 config PALMAS_GPADC
 	tristate "TI Palmas General Purpose ADC"
 	depends on MFD_PALMAS
drivers/iio/adc/Makefile +1 -0
index 36c18177322a..81824128c6cd 100644
@@ -80,6 +80,7 @@ obj-$(CONFIG_MP2629_ADC) += mp2629_adc.o
 obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
 obj-$(CONFIG_NAU7802) += nau7802.o
 obj-$(CONFIG_NPCM_ADC) += npcm_adc.o
+obj-$(CONFIG_PAC193X) += pac193x.o
 obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
 obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o
 obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
drivers/iio/adc/pac193x.c new file +16 -0
new file mode 100644
index 000000000000..ece6bf028fe2
@@ -0,0 +1,2072 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * IIO driver for PAC193X Multi-Channel DC Power/Energy Monitor
+ *
+ * Copyright (C) 2017-2023 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Bogdan Bolocan <[email protected]>
+ * Author: Victor Tudose
+ * Author: Marius Cristea <[email protected]>
+ *
+ * Datasheet for PAC1931, PAC1932, PAC1933 and PAC1934 can be found here:
+ * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/PAC1931-Family-Data-Sheet-DS20005850E.pdf
+ *
+ */
+
+#include <linux/acpi.h>

drivers/iio/adc/pac193x.c line 26

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>

Check these includes to make sure they are all still relevant.

drivers/iio/adc/pac193x.c line 35

Quoted 9 lines
drivers/iio/adc/pac193x.c new file +9 -0
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/timer.h>
+#include <linux/util_macros.h>
+
+/*
+ * maxim should be (17 * 60 * 1000) around 17 minutes@1024 sps

Either spell it out of max

Quoted 60 lines
drivers/iio/adc/pac193x.c new file +60 -0
+ * till PAC193X register stars to saturate
+ */
+#define PAC193X_MAX_RFSH_LIMIT_MS		60000
+
+/* 50msec is the timeout for validity of the cached registers */
+#define PAC193X_MIN_POLLING_TIME_MS		50
+
+#define SHUNT_UOHMS_DEFAULT			100000
+/* 32000mV */
+#define PAC193X_VOLTAGE_MILLIVOLTS_MAX		32000
+/* voltage bits resolution when set for unsigned values */
+#define PAC193X_VOLTAGE_U_RES			16
+/* voltage bits resolution when set for signed values */
+#define PAC193X_VOLTAGE_S_RES			15
+/* 100mV maximum for current shunts */
+#define PAC193X_VSENSE_MILLIVOLTS_MAX		100
+/* voltage bits resolution when set for unsigned values */
+#define PAC193X_CURRENT_U_RES			16
+/* voltage bits resolution when set for signed values */
+#define PAC193X_CURRENT_S_RES			15
+
+/* power resolution is 28 bits when unsigned */
+#define PAC193X_POWER_U_RES			 28
+/* power resolution is 27 bits when signed */
+#define PAC193X_POWER_S_RES			27
+
+/* energy accumulation is 48 bits long */
+#define PAC193X_ENERGY_U_RES			48
+
+#define PAC193X_ENERGY_S_RES			47
+#define PAC193X_ENERGY_SHIFT_MAIN_VAL		32
+
+#define BIT_INDEX_31				31
+
+/*
+ * max signed value that can be stored on 32 bits and 8 digits fractional value
+ * (2^31 - 1) * 10^8 + 99999999
+ */
+#define PAC_193X_MAX_POWER_ACC			214748364799999999LL
+/*
+ * min signed value that can be stored on 32 bits and 8 digits fractional value
+ * -(2^31) * 10^8 - 99999999
+ */
+#define PAC_193X_MIN_POWER_ACC			-214748364899999999LL
+
+/* maximum value that device can measure - 32 V * 0.1 V */
+#define PAC193X_PRODUCT_VOLTAGE_PV_FSR		3200000000000UL
+
+#define PAC193X_MAX_NUM_CHANNELS		4
+#define PAC1931_NUM_CHANNELS			1
+#define PAC1932_NUM_CHANNELS			2
+#define PAC1933_NUM_CHANNELS			3
+#define PAC1934_NUM_CHANNELS			4
+#define PAC193X_CH_1				0
+#define PAC193X_CH_2				1
+#define PAC193X_CH_3				2
+#define PAC193X_CH_4				3
+#define PAC193X_MEAS_REG_LEN			76
+#define PAC193X_CTRL_REG_LEN			12
+

drivers/iio/adc/pac193x.c line 105

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+/*
+ * 1000usec is the minimum wait time for normal conversions when sample
+ * rate doesn't change
+ */
+#define PAC193X_MIN_UPDATE_WAIT_TIME_MS		1000
+
+#define PAC193X_DEFAULT_CHIP_SAMP_SPEED		1024
+
+/* I2C address map */
+#define PAC193X_REFRESH_REG			0x00

Naming with ADDR and without is confusing. What's the difference?

Quoted 126 lines
drivers/iio/adc/pac193x.c new file +126 -0
+#define PAC193X_CTRL_REG			0x01
+#define PAC193X_REFRESH_V_REG			0x1F
+#define PAC193X_ACC_COUNT_REG			0x02
+#define PAC193X_CTRL_STAT_REGS_ADDR		0x1C
+#define PAC193X_PID_REG_ADDR			0xFD
+#define PAC193X_MID_REG_ADDR			0xFE
+#define PAC193X_RID_REG_ADDR			0xFF
+
+/* PRODUCT ID REGISTER + MANUFACTURER ID REGISTER + REVISION ID REGISTER */
+#define PAC193X_ID_REG_LEN			0x03
+#define PAC193X_PID_IDX				0
+#define PAC193X_MID_IDX				1
+#define PAC193X_RID_IDX				2
+
+#define PAC193X_ACPI_ARG_COUNT			4
+#define PAC193X_ACPI_GET_NAMES_AND_MOHMS_VALS	1
+#define PAC193X_ACPI_GET_UOHMS_VALS		2
+#define PAC193X_ACPI_GET_BIPOLAR_SETTINGS	4
+#define PAC193X_ACPI_GET_SAMP			5
+
+#define PAC193X_VPOWER_ACC_1_ADDR		0x03
+#define PAC193X_VPOWER_ACC_2_ADDR		0x04
+#define PAC193X_VPOWER_ACC_3_ADDR		0x05
+#define PAC193X_VPOWER_ACC_4_ADDR		0x06
+#define PAC193X_VBUS_1_ADDR			0x07
+#define PAC193X_VBUS_2_ADDR			0x08
+#define PAC193X_VBUS_3_ADDR			0x09
+#define PAC193X_VBUS_4_ADDR			0x0A
+#define PAC193X_VSENSE_1_ADDR			0x0B
+#define PAC193X_VSENSE_2_ADDR			0x0C
+#define PAC193X_VSENSE_3_ADDR			0x0D
+#define PAC193X_VSENSE_4_ADDR			0x0E
+#define PAC193X_VBUS_AVG_1_ADDR			0x0F
+#define PAC193X_VBUS_AVG_2_ADDR			0x10
+#define PAC193X_VBUS_AVG_3_ADDR			0x11
+#define PAC193X_VBUS_AVG_4_ADDR			0x12
+#define PAC193X_VSENSE_AVG_1_ADDR		0x13
+#define PAC193X_VSENSE_AVG_2_ADDR		0x14
+#define PAC193X_VSENSE_AVG_3_ADDR		0x15
+#define PAC193X_VSENSE_AVG_4_ADDR		0x16
+#define PAC193X_VPOWER_1_ADDR			0x17
+#define PAC193X_VPOWER_2_ADDR			0x18
+#define PAC193X_VPOWER_3_ADDR			0x19
+#define PAC193X_VPOWER_4_ADDR			0x1A
+
+#define PAC193X_SAMPLE_RATE_SHIFT		6
+
+/*
+ * these indexes are exactly describing the element order within a single
+ * PAC193X phys channel IIO channel descriptor; see the static const struct
+ * iio_chan_spec pac193x_single_channel[] declaration
+ */
+#define IIO_EN					0
+#define IIO_POW					1
+#define IIO_VOLT				2
+#define IIO_CRT					3
+#define IIO_VOLTAVG				4
+#define IIO_CRTAVG				5
+
+#define PAC193X_VBUS_SENSE_REG_LEN		2
+#define PAC193X_ACC_REG_LEN			3
+#define PAC193X_VPOWER_REG_LEN			4
+#define PAC193X_VPOWER_ACC_REG_LEN		6
+#define PAC193X_MAX_REGISTER_LENGTH		6
+
+#define PAC193X_CUSTOM_ATTR_FOR_CHANNEL		2
+#define PAC193X_SHARED_DEVATTRS_COUNT		1
+
+/*
+ * relative offsets when using multi-byte reads/writes even though these
+ * bytes are read one after the other, they are not at adjacent memory
+ * locations within the I2C memory map. The chip can skip some addresses
+ */
+#define PAC193X_CHANNEL_DIS_REG_OFF		0
+#define PAC193X_NEG_PWR_REG_OFF			1
+
+/*
+ * when reading/writing multiple bytes from offset PAC193X_CHANNEL_DIS_REG_OFF,
+ * the chip jumps over the 0x1E (REFRESH_G) and 0x1F (REFRESH_V) offsets
+ */
+#define PAC193X_SLOW_REG_OFF			2
+#define PAC193X_CTRL_ACT_REG_OFF		3
+#define PAC193X_CHANNEL_DIS_ACT_REG_OFF		4
+#define PAC193X_NEG_PWR_ACT_REG_OFF		5
+#define PAC193X_CTRL_LAT_REG_OFF		6
+#define PAC193X_CHANNEL_DIS_LAT_REG_OFF		7
+#define PAC193X_NEG_PWR_LAT_REG_OFF		8
+#define PAC193X_PID_REG_OFF			9
+#define PAC193X_MID_REG_OFF			10
+#define PAC193X_REV_REG_OFF			11
+#define PAC193X_CTRL_STATUS_INFO_LEN		12
+
+#define PAC193X_MID				0x5D
+#define PAC1934_PID				0x5B
+#define PAC1933_PID				0x5A
+#define PAC1932_PID				0x59
+#define PAC1931_PID				0x58
+
+/* Scale constant = (10^8 / 2^28) * 3.2 * 10^9 */
+#define PAC193X_SCALE_CONSTANT			1192092895UL
+
+#define PAC193X_MAX_VPOWER_RSHIFTED_BY_28B	11921
+#define PAC193X_MAX_VSENSE_RSHIFTED_BY_16B	1525
+
+#define PAC193X_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
+
+#define PAC193X_CRTL_SAMPLE_RATE_MASK	GENMASK(7, 6)
+#define PAC193X_CRTL_SAMPLE_RATE_SET(x)	((u8)FIELD_PREP(PAC193X_CRTL_SAMPLE_RATE_MASK, (x)))
+#define PAC193X_CHAN_SLEEP_MASK		BIT(5)
+#define PAC193X_CHAN_SLEEP_SET		BIT(5)
+#define PAC193X_CHAN_SINLE_MASK		BIT(4)
+#define PAC193X_CHAN_SINLE_SHOT_SET	BIT(4)
+#define PAC193X_CHAN_ALERT_MASK		BIT(3)
+#define PAC193X_CHAN_ALERT_EN		BIT(3)
+#define PAC193X_CHAN_ALERT_CC_MASK	BIT(2)
+#define PAC193X_CHAN_ALERT_CC_EN	BIT(2)
+#define PAC193X_CHAN_OVF_ALERT_MASK	BIT(1)
+#define PAC193X_CHAN_OVF_ALERT_EN	BIT(1)
+#define PAC193X_CHAN_OVF_MASK		BIT(0)
+
+#define PAC193X_CHAN_DIS_CH1_OFF_MASK	BIT(7)
+#define PAC193X_CHAN_DIS_CH1_OFF(x)	((u8)FIELD_PREP(PAC193X_CHAN_DIS_CH1_OFF_MASK, !(x)))
+#define PAC193X_CHAN_DIS_CH2_OFF_MASK	BIT(6)
+#define PAC193X_CHAN_DIS_CH2_OFF(x)	((u8)FIELD_PREP(PAC193X_CHAN_DIS_CH2_OFF_MASK, !(x)))
+#define PAC193X_CHAN_DIS_CH3_OFF_MASK	BIT(5)
+#define PAC193X_CHAN_DIS_CH3_OFF(x)	((u8)FIELD_PREP(PAC193X_CHAN_DIS_CH3_OFF_MASK, !(x)))

drivers/iio/adc/pac193x.c line 241

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+#define PAC193X_CHAN_DIS_CH4_OFF_MASK	BIT(4)
+#define PAC193X_CHAN_DIS_CH4_OFF(x)	((u8)FIELD_PREP(PAC193X_CHAN_DIS_CH4_OFF_MASK, !(x)))
+#define PAC193X_SMBUS_TIMEOUT_MASK	BIT(3)
+#define PAC193X_SMBUS_TIMEOUT_EN(x)	FIELD_PREP(PAC193X_SMBUS_TIMEOUT_MASK, x)
+#define PAC193X_SMBUS_BYTECOUNT_MASK	BIT(2)
+#define PAC193X_SMBUS_BYTECOUNT_EN(x)	FIELD_PREP(PAC193X_SMBUS_BYTECOUNT_MASK, x)
+#define PAC193X_SMBUS_NO_SKIP_MASK	BIT(1)
+#define PAC193X_SMBUS_NO_SKIP_EN(x)	FIELD_PREP(PAC193X_SMBUS_NO_SKIP_MASK, x)
+
+#define PAC193X_NEG_PWR_CH1_BIDI(x)	((x) ? BIT(7) : 0)

To be more standard for kernel code, define a mask and use FIELD_PREP() for these as well.

Quoted 44 lines
drivers/iio/adc/pac193x.c new file +44 -0
+#define PAC193X_NEG_PWR_CH2_BIDI(x)	((x) ? BIT(6) : 0)
+#define PAC193X_NEG_PWR_CH3_BIDI(x)	((x) ? BIT(5) : 0)
+#define PAC193X_NEG_PWR_CH4_BIDI(x)	((x) ? BIT(4) : 0)
+#define PAC193X_NEG_PWR_CH1_BIDV(x)	((x) ? BIT(3) : 0)
+#define PAC193X_NEG_PWR_CH2_BIDV(x)	((x) ? BIT(2) : 0)
+#define PAC193X_NEG_PWR_CH3_BIDV(x)	((x) ? BIT(1) : 0)
+#define PAC193X_NEG_PWR_CH4_BIDV(x)	((x) ? BIT(0) : 0)
+
+/*
+ * Universal Unique Identifier (UUID),
+ * 033771E0-1705-47B4-9535-D1BBE14D9A09, is
+ * reserved to Microchip for the PAC193X and must not be changed
+ */
+#define PAC193X_DSM_UUID		"033771E0-1705-47B4-9535-D1BBE14D9A09"
+
+enum pac193x_ids {
+	pac1934,
+	pac1933,
+	pac1932,
+	pac1931
+};
+
+enum pac193x_samps {
+	pac193X_samp_1024sps,
+	pac193X_samp_256sps,
+	pac193X_samp_64sps,
+	pac193X_samp_8sps
+};
+
+/**
+ * struct pac193x_features - features of a pac193x instance
+ * @phys_channels: number of physical channels supported by the chip
+ * @prod_id: product ID
+ */
+struct pac193x_features {
+	u8 phys_channels;
+	u8 prod_id;
+};
+
+struct samp_rate_mapping {
+	u16 samp_rate;
+	u8 shift2value;
+};
+

drivers/iio/adc/pac193x.c line 295

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+static const unsigned int samp_rate_map_tbl[] = {
+	[pac193X_samp_1024sps] = 1024,
+	[pac193X_samp_256sps] = 256,
+	[pac193X_samp_64sps] = 64,
+	[pac193X_samp_8sps] = 8,
+};
+
+static const struct pac193x_features pac193x_chip_config[] = {
+	[pac1934] = {
+	    .phys_channels = PAC193X_MAX_NUM_CHANNELS,

Better as a number than a define.

Quoted 51 lines
drivers/iio/adc/pac193x.c new file +51 -0
+	    .prod_id = PAC1934_PID,
+	},
+	[pac1933] = {
+	    .phys_channels = PAC193X_MAX_NUM_CHANNELS - 1,
+	    .prod_id = PAC1933_PID,
+	},
+	[pac1932] = {
+	    .phys_channels = PAC193X_MAX_NUM_CHANNELS - 2,
+	    .prod_id = PAC1932_PID,
+	},
+	[pac1931] = {
+	    .phys_channels = PAC193X_MAX_NUM_CHANNELS - 3,
+	    .prod_id = PAC1931_PID,
+	},
+};
+
+/**
+ * struct reg_data - data from the registers
+ * @meas_regs: snapshot of raw measurements registers
+ * @ctrl_regs: snapshot of control registers
+ * @energy_sec_acc: snapshot of energy values
+ * @vpower_acc: accumulated vpower values
+ * @vpower: snapshot of vpower registers
+ * @vbus: snapshot of vbus registers
+ * @vbus_avg: averages of vbus registers
+ * @vsense: snapshot of vsense registers
+ * @vsense_avg: averages of vsense registers
+ * @num_enabled_channels: count of how many chip channels are currently enabled
+ */
+struct reg_data {
+	u8	meas_regs[PAC193X_MEAS_REG_LEN];
+	u8	ctrl_regs[PAC193X_CTRL_REG_LEN];
+	s64	energy_sec_acc[PAC193X_MAX_NUM_CHANNELS];
+	s64	vpower_acc[PAC193X_MAX_NUM_CHANNELS];
+	s32	vpower[PAC193X_MAX_NUM_CHANNELS];
+	s32	vbus[PAC193X_MAX_NUM_CHANNELS];
+	s32	vbus_avg[PAC193X_MAX_NUM_CHANNELS];
+	s32	vsense[PAC193X_MAX_NUM_CHANNELS];
+	s32	vsense_avg[PAC193X_MAX_NUM_CHANNELS];
+	u8	num_enabled_channels;
+};
+
+/**
+ * struct pac193x_chip_info - information about the chip
+ * @client: the i2c-client attached to the device
+ * @lock: lock used by the chip's mutex
+ * @tmr_forced_update: timers used
+ * @wq_chip: queued work used for refresh commands
+ * @work_chip_rfsh: work used for refresh commands
+ * @phys_channels: phys channels count
+ * @active_channels: array of values, true means that channel is active

drivers/iio/adc/pac193x.c line 356

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+ * @bi_dir: array of bools, true means that channel is bidirectional
+ * @chip_variant: chip variant
+ * @chip_revision: chip revision
+ * @shunts: shunts
+ * @chip_reg_data: chip reg data
+ * @sample_rate_value: sampling frequency
+ * @channel_names: channel names
+ * @pac193x_info: pac193x_info
+ * @jiffies_tstamp: chip's uptime
+ * @crt_samp_spd_bitfield:the current sampling speed

Order should match below.

Quoted 12 lines
drivers/iio/adc/pac193x.c new file +12 -0
+ */
+struct pac193x_chip_info {
+	struct i2c_client	*client;
+	struct mutex		lock; /*lock to prevent concurent reads/writes */
+	struct timer_list	tmr_forced_update;
+	struct workqueue_struct *wq_chip;
+	struct work_struct	work_chip_rfsh;
+	u8			phys_channels;
+	bool			active_channels[PAC193X_MAX_NUM_CHANNELS];
+	bool			bi_dir[PAC193X_MAX_NUM_CHANNELS];
+	u8			chip_variant;
+	u8			chip_revision;

drivers/iio/adc/pac193x.c line 378

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+	u32			shunts[PAC193X_MAX_NUM_CHANNELS];
+	struct reg_data		chip_reg_data;
+	s32			sample_rate_value;
+	char			*channel_names[PAC193X_MAX_NUM_CHANNELS];
+	u8			crt_samp_spd_bitfield;
+	unsigned long		jiffies_tstamp;
+	struct iio_info		pac193x_info;
+};
+
+struct __packed pac193x_uuid_format {

Not a standard UUID? uuid_t

drivers/iio/adc/pac193x.c line 390

Quoted 12 lines
drivers/iio/adc/pac193x.c new file +12 -0
+	u32	data1;
+	u16	data2;
+	u16	data3;
+	u8	data4[8];
+};
+
+#define to_pac193x_chip_info(d) container_of(d, struct pac193x_chip_info, work_chip_rfsh)
+
+/* macros to extract the parameters */
+#define MVACCS(x)		sign_extend64((u64)(x), 47)
+#define MVPOWERS(x)		sign_extend32((u32)(x), 27)
+#define MVBUS_SENSES(x)		sign_extend32((u32)(x), 15)

Prefix defines with PAC193X to avoid future namespace clasehs.
I'm not 100% convinced these aren't better handled inline though rther than vai these macros.

Quoted 80 lines
drivers/iio/adc/pac193x.c new file +80 -0
+
+#define PAC193X_VPOWER_ACC_CHANNEL(_index, _si, _address) {			\
+	.type = IIO_ENERGY,							\
+	.address = (_address),							\
+	.indexed = 1,								\
+	.channel = (_index),							\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)	|			\
+				BIT(IIO_CHAN_INFO_SCALE),			\
+	.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),		\
+	.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
+	.scan_index = (_si),							\
+	.scan_type = {								\
+		.sign = 'u',							\
+		.realbits = PAC193X_ENERGY_U_RES,				\
+		.storagebits = PAC193X_ENERGY_U_RES,				\
+		.shift = 0,							\
+		.endianness = IIO_CPU,						\
+	}									\
+}
+
+#define PAC193X_VBUS_CHANNEL(_index, _si, _address) {				\
+	.type = IIO_VOLTAGE,							\
+	.address = (_address),							\
+	.indexed = 1,								\
+	.channel = (_index),							\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),				\
+	.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),		\
+	.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
+	.scan_index = (_si),							\
+	.scan_type = {								\
+		.sign = 'u',							\
+		.realbits = PAC193X_VOLTAGE_U_RES,				\
+		.storagebits = PAC193X_VOLTAGE_U_RES,				\
+		.shift = 0,							\
+		.endianness = IIO_CPU,						\
+	}									\
+}
+
+#define PAC193X_VBUS_AVG_CHANNEL(_index, _si, _address) {			\
+	.type = IIO_VOLTAGE,							\
+	.address = (_address),							\
+	.indexed = 1,								\
+	.channel = (_index),							\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_AVERAGE_RAW)			\
+				| BIT(IIO_CHAN_INFO_SCALE),			\
+	.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),		\
+	.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
+	.scan_index = (_si),							\
+	.scan_type = {								\
+		.sign = 'u',							\
+		.realbits = PAC193X_VOLTAGE_U_RES,				\
+		.storagebits = PAC193X_VOLTAGE_U_RES,				\
+		.shift = 0,							\
+		.endianness = IIO_CPU,						\
+	}									\
+}
+
+#define PAC193X_VSENSE_CHANNEL(_index, _si, _address) {				\
+	.type = IIO_CURRENT,							\
+	.address = (_address),							\
+	.indexed = 1,								\
+	.channel = (_index),							\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),				\
+	.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),		\
+	.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
+	.scan_index = (_si),							\
+	.scan_type = {								\
+		.sign = 'u',							\
+		.realbits = PAC193X_CURRENT_U_RES,				\
+		.storagebits = PAC193X_CURRENT_U_RES,				\
+		.shift = 0,							\
+		.endianness = IIO_CPU,						\
+	}									\
+}
+
+#define PAC193X_VSENSE_AVG_CHANNEL(_index, _si, _address) {			\
+	.type = IIO_CURRENT,							\
+	.address = (_address),							\
+	.indexed = 1,								\
+	.channel = (_index),							\

drivers/iio/adc/pac193x.c

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+	.info_mask_separate = BIT(IIO_CHAN_INFO_AVERAGE_RAW) |			\
+			BIT(IIO_CHAN_INFO_SCALE),				\
+	.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),		\
+	.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
+	.scan_index = (_si),							\
+	.scan_type = {								\
+		.sign = 'u',							\
+		.realbits = PAC193X_CURRENT_U_RES,				\
+		.storagebits = PAC193X_CURRENT_U_RES,				\
+		.shift = 0,	

When shift is 0, it can be assumed as 'obvious' default and skipped. Rely on C to set it to 0 for us. \

Quoted 16 lines
drivers/iio/adc/pac193x.c new file +16 -0
+		.endianness = IIO_CPU,						\
+	}									\
+}
+
+#define PAC193X_VPOWER_CHANNEL(_index, _si, _address) {				\
+	.type = IIO_POWER,							\
+	.address = (_address),							\
+	.indexed = 1,								\
+	.channel = (_index),							\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |				\
+			BIT(IIO_CHAN_INFO_SCALE),				\
+	.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),		\
+	.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
+	.scan_index = (_si),							\
+	.scan_type = {								\
+		.sign = 'u',							\

drivers/iio/adc/pac193x.c line 506

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+		.realbits = PAC193X_POWER_U_RES,				\
+		.storagebits = 32,						\
+		.shift = 4,							\
+		.endianness = IIO_CPU,						\
+	}									\
+}
+
+#define PAC193X_SOFT_TIMESTAMP(_index) {					\
+	.type = IIO_TIMESTAMP,							\
+	.channel = -1,								\

What is this for?

Quoted 14 lines
drivers/iio/adc/pac193x.c new file +14 -0
+	.scan_index = (_index),							\
+}
+
+static const struct iio_chan_spec pac193x_single_channel[] = {
+	PAC193X_VPOWER_ACC_CHANNEL(0, 0, PAC193X_VPOWER_ACC_1_ADDR),
+	PAC193X_VPOWER_CHANNEL(0, 0, PAC193X_VPOWER_1_ADDR),
+	PAC193X_VBUS_CHANNEL(0, 0, PAC193X_VBUS_1_ADDR),
+	PAC193X_VSENSE_CHANNEL(0, 0, PAC193X_VSENSE_1_ADDR),
+	PAC193X_VBUS_AVG_CHANNEL(0, 0, PAC193X_VBUS_AVG_1_ADDR),
+	PAC193X_VSENSE_AVG_CHANNEL(0, 0, PAC193X_VSENSE_AVG_1_ADDR),
+};
+
+static const struct iio_chan_spec pac193x_ts[] = {
+	PAC193X_SOFT_TIMESTAMP(0),

drivers/iio/adc/pac193x.c line 530

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+};
+
+static inline u64 pac193x_get_mvaccu(u8 *addr)
+{
+	return (((u64)(*(u8 *)((addr) + 0)) << 40) |
+		((u64)(*(u8 *)((addr) + 1)) << 32) |
+		((u64)(*(u8 *)((addr) + 2)) << 24) |
+		((u64)(*(u8 *)((addr) + 3)) << 16) |
+		((u64)(*(u8 *)((addr) + 4)) << 8)  |
+		((u64)(*(u8 *)((addr) + 5)) << 0));

get_unaligned_be48()?

drivers/iio/adc/pac193x.c line 538

Quoted 8 lines
drivers/iio/adc/pac193x.c new file +8 -0
+}
+
+static inline u32 pac193x_get_vpoweru(u8 *addr)
+{
+	return (((u32)(*(u8 *)((addr) + 0)) << 20) |
+		((u32)(*(u8 *)((addr) + 1)) << 12) |
+		((u32)(*(u8 *)((addr) + 2)) << 4) |
+		((u32)(*(u8 *)((addr) + 3)) >> 4));

Hmm. Does this do same as

get_unaligned_be32(addr) >> 4 ?

drivers/iio/adc/pac193x.c line 546

Quoted 8 lines
drivers/iio/adc/pac193x.c new file +8 -0
+}
+
+static inline u16 pac193x_get_mvbus_senseu(u8 *addr)
+{
+	u16 tmp;
+
+	tmp = (u16)(*(u16 *)(addr));
+	be16_to_cpus(&tmp);

get_unaligned_be16?

Quoted 33 lines
drivers/iio/adc/pac193x.c new file +33 -0
+
+	return tmp;
+}
+
+/* Low-level I2c functions */
+static int pac193x_i2c_read(struct i2c_client *client, u8 reg_addr, void *databuf, u8 len)
+{
+	int ret;
+	struct i2c_msg msgs[2] = {
+		{ .addr = client->addr,
+		 .len = 1,
+		 .buf = (u8 *)&reg_addr,
+		 .flags = 0
+		},
+		{ .addr = client->addr,
+		 .len = len,
+		 .buf = databuf,
+		 .flags = I2C_M_RD
+		}
+	};
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int pac193x_i2c_write_byte(struct i2c_client *client, u8 reg_addr, u8 val)
+{
+	int ret;
+	u8 buf[2];
+	struct i2c_msg msg = {

drivers/iio/adc/pac193x.c line 589

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+		.addr = client->addr,
+		.len = sizeof(buf),
+		.buf = (u8 *)&buf,
+		.flags = 0
+	};
+
+	buf[0] = reg_addr;
+	buf[1] = val;
+
+	ret = i2c_transfer(client->adapter, &msg, 1);

i2c_smbus_write_byte_data()?

Quoted 12 lines
drivers/iio/adc/pac193x.c new file +12 -0
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"failed writing register 0x%02X\n", reg_addr);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int pac193x_i2c_send_byte(struct i2c_client *client, u8 reg_addr)
+{
+	int ret;

drivers/iio/adc/pac193x.c line 611

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+	u8 buf;
+	struct i2c_msg msg = {
+		.addr = client->addr,
+		.len = sizeof(buf),
+		.buf = (u8 *)&buf,
+		.flags = 0
+	};
+
+	buf = reg_addr;
+	ret = i2c_transfer(client->adapter, &msg, 1);

looks like it might match i2c_smbus_write()

Quoted 11 lines
drivers/iio/adc/pac193x.c new file +11 -0
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int pac193x_i2c_write(struct i2c_client *client, u8 reg_addr, u8 *data, int len)
+{
+	int ret;
+	u8 send[PAC193X_MAX_REGISTER_LENGTH + 1];
+	struct i2c_msg msg = {

drivers/iio/adc/pac193x.c line 632

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+		.addr = client->addr,
+		.len = len + 1,
+		.flags = 0
+	};
+
+	send[0] = reg_addr;
+	memcpy(&send[1], data, len * sizeof(u8));
+	msg.buf = send;
+
+	ret = i2c_transfer(client->adapter, &msg, 1);

i2c_master_send()

Quoted 7 lines
drivers/iio/adc/pac193x.c new file +7 -0
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"failed writing data from register 0x%02X\n", reg_addr);
+		return ret;
+	}
+
+	return 0;

drivers/iio/adc/pac193x.c line 649

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+}
+
+static int pac193x_match_samp_rate(struct pac193x_chip_info *chip_info, u32 new_samp_rate)
+{
+	int cnt;
+
+	for (cnt = 0; cnt < ARRAY_SIZE(samp_rate_map_tbl); cnt++) {
+		if (new_samp_rate == samp_rate_map_tbl[cnt]) {
+			chip_info->crt_samp_spd_bitfield = cnt;
+			break;
			return 0;

drivers/iio/adc/pac193x.c line 651

Quoted 2 lines
drivers/iio/adc/pac193x.c new file +2 -0
+		}
+	}

return -EINVAL;

Quoted 11 lines
drivers/iio/adc/pac193x.c new file +11 -0
+
+	if (cnt == ARRAY_SIZE(samp_rate_map_tbl))
+		/* not a valid sample rate value */
+		return -EINVAL;
+
+	return 0;
+}
+
+static ssize_t shunt_value_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);

drivers/iio/adc/pac193x.c line 672

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+	struct pac193x_chip_info *chip_info = iio_priv(indio_dev);
+	int len = 0;
+	int target = (int)(attr->attr.name[strlen(attr->attr.name) - 1] - '0') - 1;
+
+	len += sprintf(buf, "%u\n", chip_info->shunts[target]);
+
+	return len;
+}
+
+static ssize_t channel_name_show(struct device *dev,

Looks like per channel label support provided by the read_label iio_info callback.

Quoted 4 lines
drivers/iio/adc/pac193x.c new file +4 -0
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);

drivers/iio/adc/pac193x.c line 686

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+	struct pac193x_chip_info *chip_info = iio_priv(indio_dev);
+	int len = 0;
+	int target = (int)(attr->attr.name[strlen(attr->attr.name) - 1] - '0') - 1;
+
+	len += sprintf(buf, "%s\n", chip_info->channel_names[target]);
+
+	return len;
+}
+
+static ssize_t shunt_value_store(struct device *dev,

Shunt values aren't normally dynamic. Why do you need to write it from userspace? I'd expect that to come from firmware.

If it's needed the ext_info framework might be a better fit than direct implementation of the attribute.

Quoted 20 lines
drivers/iio/adc/pac193x.c new file +20 -0
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct pac193x_chip_info *chip_info = iio_priv(indio_dev);
+	int sh_val, target;
+
+	target = (int)(attr->attr.name[strlen(attr->attr.name) - 1] - '0') - 1;
+	if (kstrtouint(buf, 10, &sh_val)) {
+		dev_err(dev, "Shunt value is not valid\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&chip_info->lock);
+	chip_info->shunts[target] = sh_val;
+	mutex_unlock(&chip_info->lock);
+
+	return count;
+}
+

...

drivers/iio/adc/pac193x.c line 733

Quoted 12 lines
drivers/iio/adc/pac193x.c new file +12 -0
+static int pac193x_send_refresh(struct pac193x_chip_info *chip_info,
+				bool refresh_v, u32 wait_time)
+{
+	/* this function only sends REFRESH or REFRESH_V */
+	struct i2c_client *client = chip_info->client;
+	int ret;
+	u8 refresh_cmd, bidir_reg;
+	bool revision_bug = false;
+
+	if (chip_info->chip_revision == 2 || chip_info->chip_revision == 3) {
+		/*
+		 *chip rev 2 and 3 bug workaround

space after *

Quoted 59 lines
drivers/iio/adc/pac193x.c new file +59 -0
+		 * see: PAC193X Family Data Sheet Errata DS80000836A.pdf
+		 */
+		revision_bug = true;
+
+		bidir_reg = PAC193X_NEG_PWR_CH1_BIDI(chip_info->bi_dir[PAC193X_CH_1]) |
+			PAC193X_NEG_PWR_CH2_BIDI(chip_info->bi_dir[PAC193X_CH_2]) |
+			PAC193X_NEG_PWR_CH3_BIDI(chip_info->bi_dir[PAC193X_CH_3]) |
+			PAC193X_NEG_PWR_CH4_BIDI(chip_info->bi_dir[PAC193X_CH_4]) |
+			PAC193X_NEG_PWR_CH1_BIDV(chip_info->bi_dir[PAC193X_CH_1]) |
+			PAC193X_NEG_PWR_CH2_BIDV(chip_info->bi_dir[PAC193X_CH_2]) |
+			PAC193X_NEG_PWR_CH3_BIDV(chip_info->bi_dir[PAC193X_CH_3]) |
+			PAC193X_NEG_PWR_CH4_BIDV(chip_info->bi_dir[PAC193X_CH_4]);
+
+		/* write the updated registers back */
+		ret = pac193x_i2c_write_byte(client,
+					     PAC193X_CTRL_STAT_REGS_ADDR +
+					     PAC193X_NEG_PWR_REG_OFF,
+					     bidir_reg);
+		if (ret)
+			return ret;
+	}
+
+	/*
+	 * if refresh_v is not false, send REFRESH_V instead of REFRESH
+	 * (doesn't reset the accumulators)
+	 */
+	if (refresh_v)
+		refresh_cmd = PAC193X_REFRESH_V_REG;
+	else
+		refresh_cmd = PAC193X_REFRESH_REG;
+
+	ret = pac193x_i2c_send_byte(client, refresh_cmd);
+	if (ret) {
+		dev_err(&client->dev, "%s - cannot send 0x%02X\n",
+			__func__, refresh_cmd);
+		return ret;
+	}
+
+	if (revision_bug) {
+		/*
+		 * chip rev 2 and 3 bug workaround - write again the same register
+		 * write the updated registers back
+		 */
+		ret = pac193x_i2c_write_byte(client,
+					     PAC193X_CTRL_STAT_REGS_ADDR +
+					     PAC193X_NEG_PWR_REG_OFF, bidir_reg);
+		if (ret)
+			return ret;
+	}
+
+	/* register data retrieval timestamp */
+	chip_info->jiffies_tstamp = jiffies;
+
+	/* wait till the data is available */
+	usleep_range(wait_time, wait_time + 100);
+
+	return ret;
+}
+

...

drivers/iio/adc/pac193x.c line 975

Quoted 8 lines
drivers/iio/adc/pac193x.c new file +8 -0
+
+/**
+ * pac193x_read_raw() - data read function.
+ * @indio_dev: the struct iio_dev associated with this device instance
+ * @chan: the channel whose data is to be read
+ * @val: first element of returned value (typically INT)
+ * @val2: second element of returned value (typically MICRO)
+ * @mask: what we actually want to read as per the info_mask_* in iio_chan_spec.

As below. This doc doesn't add a huge amount as it's documenting IIO rather than this function.

Quoted 51 lines

+ */

drivers/iio/adc/pac193x.c new file +50 -0
+static int pac193x_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan, int *val,
+			    int *val2, long mask)
+{
+	struct pac193x_chip_info *chip_info = iio_priv(indio_dev);
+	s64 curr_energy, int_part;
+	int rem, ret, channel = chan->channel - 1;
+
+	ret = pac193x_retrieve_data(chip_info, PAC193X_MIN_UPDATE_WAIT_TIME_MS);
+	if (ret < 0)
+		return ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		switch (chan->type) {
+		case IIO_VOLTAGE:
+			switch (chan->address) {
+			case PAC193X_VBUS_1_ADDR:
+			case PAC193X_VBUS_2_ADDR:
+			case PAC193X_VBUS_3_ADDR:
+			case PAC193X_VBUS_4_ADDR:
+				*val = chip_info->chip_reg_data.vbus[channel];
+				return IIO_VAL_INT;
+			default:
+				return -EINVAL;
+			}
+			break;
+		case IIO_CURRENT:
+			switch (chan->address) {
+			case PAC193X_VSENSE_1_ADDR:
+			case PAC193X_VSENSE_2_ADDR:
+			case PAC193X_VSENSE_3_ADDR:
+			case PAC193X_VSENSE_4_ADDR:
+				*val = chip_info->chip_reg_data.vsense[channel];
+				return IIO_VAL_INT;
+			default:
+				return -EINVAL;
+			}
+			break;
+		case IIO_POWER:
+			switch (chan->address) {
+			case PAC193X_VPOWER_1_ADDR:
+			case PAC193X_VPOWER_2_ADDR:
+			case PAC193X_VPOWER_3_ADDR:
+			case PAC193X_VPOWER_4_ADDR:
+				*val = chip_info->chip_reg_data.vpower[channel];
+				return IIO_VAL_INT;
+			default:
+				return -EINVAL;
+			}

drivers/iio/adc/pac193x.c line 1036

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+			break;
+		case IIO_ENERGY:
+			switch (chan->address) {
+			case PAC193X_VPOWER_ACC_1_ADDR:
+			case PAC193X_VPOWER_ACC_2_ADDR:
+			case PAC193X_VPOWER_ACC_3_ADDR:
+			case PAC193X_VPOWER_ACC_4_ADDR:
+				/*
+				 * expresses the 64 bit energy value as a 32 bit integer part and
+				 * 32 bits (representing 8 digits) fractional value

I'm lost. So it's a 64 bit value?
We have IIO_VAL_INT_64. Does that not work here?

Quoted 9 lines
drivers/iio/adc/pac193x.c new file +9 -0
+				 */
+				curr_energy = chip_info->chip_reg_data.energy_sec_acc[channel];
+				int_part = div_s64_rem(curr_energy, 100000000, &rem);
+
+				/* rescale reminder to be printed as "nano" value */
+				rem = rem * 10;
+
+				*val = int_part;
+				*val2 = rem;

drivers/iio/adc/pac193x.c line 1055

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+				return IIO_VAL_INT_PLUS_NANO;
+			default:
+				return -EINVAL;
+			}
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case IIO_CHAN_INFO_AVERAGE_RAW:

I'd forgotten we had this ABI and had to look up the docs. Hmm. It's a bit clunky but I guess
still useful in this case. Longer term we should think about how to describe that and how it is different from oversampling. The problem with oversampling is it affects the main channel so we can't easily associate both an averaged value and a raw one like you have here.

Given lots of other changes requested and early in this kernel cycle, perhaps it will make sense to revisit this question at a later version of this patch (or maybe just leave it alone as too hard to do better!)

Quoted 115 lines
drivers/iio/adc/pac193x.c new file +115 -0
+		switch (chan->type) {
+		case IIO_VOLTAGE:
+			switch (chan->address) {
+			case PAC193X_VBUS_AVG_1_ADDR:
+			case PAC193X_VBUS_AVG_2_ADDR:
+			case PAC193X_VBUS_AVG_3_ADDR:
+			case PAC193X_VBUS_AVG_4_ADDR:
+				*val = chip_info->chip_reg_data.vbus_avg[channel];
+				return IIO_VAL_INT;
+
+			default:
+				return -EINVAL;
+			}
+			break;
+		case IIO_CURRENT:
+			switch (chan->address) {
+			case PAC193X_VSENSE_AVG_1_ADDR:
+			case PAC193X_VSENSE_AVG_2_ADDR:
+			case PAC193X_VSENSE_AVG_3_ADDR:
+			case PAC193X_VSENSE_AVG_4_ADDR:
+				*val = chip_info->chip_reg_data.vsense_avg[channel];
+				return IIO_VAL_INT;
+
+			default:
+				return -EINVAL;
+			}
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->address) {
+		/* Voltages - scale for millivolts */
+		case PAC193X_VBUS_1_ADDR:
+		case PAC193X_VBUS_2_ADDR:
+		case PAC193X_VBUS_3_ADDR:
+		case PAC193X_VBUS_4_ADDR:
+		case PAC193X_VBUS_AVG_1_ADDR:
+		case PAC193X_VBUS_AVG_2_ADDR:
+		case PAC193X_VBUS_AVG_3_ADDR:
+		case PAC193X_VBUS_AVG_4_ADDR:
+			*val = PAC193X_VOLTAGE_MILLIVOLTS_MAX;
+			if (chan->scan_type.sign == 'u')
+				*val2 = PAC193X_VOLTAGE_U_RES;
+			else
+				*val2 = PAC193X_VOLTAGE_S_RES;
+			return IIO_VAL_FRACTIONAL_LOG2;
+
+		/*
+		 * Currents - scale for mA - depends on the
+		 * channel's shunt value
+		 * (100mV * 1000000) / (2^16 * shunt(uohm))
+		 */
+		case PAC193X_VSENSE_1_ADDR:
+		case PAC193X_VSENSE_2_ADDR:
+		case PAC193X_VSENSE_3_ADDR:
+		case PAC193X_VSENSE_4_ADDR:
+		case PAC193X_VSENSE_AVG_1_ADDR:
+		case PAC193X_VSENSE_AVG_2_ADDR:
+		case PAC193X_VSENSE_AVG_3_ADDR:
+		case PAC193X_VSENSE_AVG_4_ADDR:
+			*val = PAC193X_MAX_VSENSE_RSHIFTED_BY_16B;
+			if (chan->scan_type.sign == 'u')
+				*val2 = chip_info->shunts[channel];
+			else
+				*val2 = chip_info->shunts[channel] >> 1;
+			return IIO_VAL_FRACTIONAL;
+
+		/*
+		 * Power - mW - it will use the combined scale
+		 * for current and voltage
+		 * current(mA) * voltage(mV) = power (uW)
+		 */
+		case PAC193X_VPOWER_1_ADDR:
+		case PAC193X_VPOWER_2_ADDR:
+		case PAC193X_VPOWER_3_ADDR:
+		case PAC193X_VPOWER_4_ADDR:
+			*val = PAC193X_MAX_VPOWER_RSHIFTED_BY_28B;
+			if (chan->scan_type.sign == 'u')
+				*val2 = chip_info->shunts[channel];
+			else
+				*val2 = chip_info->shunts[channel] >> 1;
+			return IIO_VAL_FRACTIONAL;
+		case PAC193X_VPOWER_ACC_1_ADDR:
+		case PAC193X_VPOWER_ACC_2_ADDR:
+		case PAC193X_VPOWER_ACC_3_ADDR:
+		case PAC193X_VPOWER_ACC_4_ADDR:
+
+			/*
+			 * expresses the 32 bit scale value
+			 * here compute the scale for energy (Watt-second or Joule)
+			 */
+			*val = PAC193X_SCALE_CONSTANT;
+
+			if (chan->scan_type.sign == 'u')
+				*val2 = chip_info->shunts[channel];
+			else
+				*val2 = chip_info->shunts[channel] >> 1;
+			return IIO_VAL_FRACTIONAL;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*val = chip_info->sample_rate_value;
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+
+	return -EINVAL;
+}
+
+/*

drivers/iio/adc/pac193x.c line 1180

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+ * pac193x_write_raw() - data write function.
+ * @indio_dev:	the struct iio_dev associated with this device instance
+ * @chan:	the channel whose data is to be written
+ * @val:	first element of value to set (typically INT)
+ * @val2:	second element of value to set (typically MICRO)
+ * @mask:	what we actually want to write as per the info_mask_* in iio_chan_spec.
+ *
+ * Note that all raw writes are assumed IIO_VAL_INT and info mask elements
+ * are assumed to be IIO_INT_PLUS_MICRO unless the callback write_raw_get_fmt
+ * in struct iio_info is provided by the driver.

These docs are more of IIO callbacks in general than this particular function. Not sure it's helpful to have them here.

drivers/iio/adc/pac193x.c line 1191

Quoted 11 lines

+ */

drivers/iio/adc/pac193x.c new file +10 -0
+static int pac193x_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+			     int val, int val2, long mask)
+{
+	struct pac193x_chip_info *chip_info = iio_priv(indio_dev);
+	struct i2c_client *client = chip_info->client;
+	int ret = -EINVAL;
+	s32 old_samp_rate;
+	u8 ctrl_reg;
+
+	if (iio_buffer_enabled(indio_dev))

If you need to not do it whilst the buffer is enabled
then you want iio_device_claim_direct_mode() and the release to ensure that you don't have a race against buffer being enabled.

However you don't support buffered mode so far. Hence what is this really here for?

drivers/iio/adc/pac193x.c line 1200

Quoted 9 lines
drivers/iio/adc/pac193x.c new file +9 -0
+		return -EBUSY;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = pac193x_match_samp_rate(chip_info, (u16)val);
+		if (ret)
+			return ret;
+
+		/* store the old sampling rate */

Given you have a good variable name the comment doesn't add anything.

drivers/iio/adc/pac193x.c line 1212

Quoted 12 lines
drivers/iio/adc/pac193x.c new file +12 -0
+		old_samp_rate = chip_info->sample_rate_value;
+
+		/* we have a valid sample rate */
+		chip_info->sample_rate_value = val;
+
+		/*
+		 * now lock the access to the chip, write the new
+		 * sampling value and trigger a snapshot(incl refresh)
+		 */
+		mutex_lock(&chip_info->lock);
+
+		/* enable ALERT pin */

Where is this wired to?

drivers/iio/adc/pac193x.c line 1219

Quoted 7 lines
drivers/iio/adc/pac193x.c new file +7 -0
+		ctrl_reg = PAC193X_CRTL_SAMPLE_RATE_SET(chip_info->crt_samp_spd_bitfield) |
+			PAC193X_CHAN_ALERT_EN;
+		ret = pac193x_i2c_write_byte(client, PAC193X_CTRL_REG, ctrl_reg);
+		if (ret) {
+			dev_err(&client->dev, "%s - can't update sample rate\n", __func__);
+			chip_info->sample_rate_value = old_samp_rate;
+			mutex_unlock(&chip_info->lock);

Fun thing about i2c bus write failures. You have no idea what failed. The value might be the old value or the new one after the failed write... So normally we don't bother too much if our values are out of sync. and that leads to easier unwinding. However, if you want to keep this as it is then that's fine.

Quoted 41 lines
drivers/iio/adc/pac193x.c new file +41 -0
+			return ret;
+		}
+
+		/*
+		 * unlock the access towards the chip - register
+		 * snapshot includes its own access lock
+		 */
+		mutex_unlock(&chip_info->lock);
+
+		/*
+		 * now, force a snapshot with refresh - call retrieve
+		 * data in order to update the refresh timer
+		 * alter the timestamp in order to force trigger a
+		 * register snapshot and a timestamp update
+		 */
+		chip_info->jiffies_tstamp -=
+			msecs_to_jiffies(PAC193X_MIN_POLLING_TIME_MS);
+		ret = pac193x_retrieve_data(chip_info, (1024 / old_samp_rate) * 1000);
+		if (ret < 0) {
+			dev_err(&client->dev,
+				"%s - cannot snapshot ctrl and measurement regs\n",
+				__func__);
+			return ret;
+		}
+
+		ret = 0;
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static void pac193x_work_periodic_rfsh(struct work_struct *work)
+{
+	struct pac193x_chip_info *chip_info = to_pac193x_chip_info(work);
+
+	/* do a REFRESH, then read */
+	pac193x_reg_snapshot(chip_info, true, false, PAC193X_MIN_UPDATE_WAIT_TIME_MS);
+}

drivers/iio/adc/pac193x.c line 1270

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+
+static void pac193x_read_reg_timeout(struct timer_list *t)
+{
+	struct pac193x_chip_info *chip_info = from_timer(chip_info, t, tmr_forced_update);
+
+	mod_timer(&chip_info->tmr_forced_update,
+		  jiffies + msecs_to_jiffies(PAC193X_MAX_RFSH_LIMIT_MS));
+
+	/* schedule the periodic reading from the chip */
+	queue_work(chip_info->wq_chip, &chip_info->work_chip_rfsh);

Why not queue_delayed_work()?

drivers/iio/adc/pac193x.c line 1279

Quoted 9 lines
drivers/iio/adc/pac193x.c new file +9 -0
+}
+
+static int pac193x_read_revision(struct pac193x_chip_info *chip_info, u8 *buf)
+{
+	int ret;
+	struct i2c_client *client = chip_info->client;
+
+	/*
+	 * try to identify the chip variant

why try? Is there a reason (other than hardware failure) that this might not work?

Quoted 9 lines
drivers/iio/adc/pac193x.c new file +9 -0
+	 * read the chip ID values
+	 */
+	ret = pac193x_i2c_read(client, PAC193X_PID_REG_ADDR, buf, PAC193X_ID_REG_LEN);
+	if (ret) {
+		dev_err(&client->dev, "cannot read revision\n");
+		return ret;
+	}
+
+	return 0;

drivers/iio/adc/pac193x.c line 1298

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+}
+
+static int pac193x_chip_identify(struct pac193x_chip_info *chip_info)
+{
+	int ret;
+	struct i2c_client *client = chip_info->client;
+	u8 rev_info[PAC193X_ID_REG_LEN];
+
+	ret = pac193x_read_revision(chip_info, (u8 *)rev_info);
+

Trivial: Drop blank line here. Keeps the error block next to the call and clearly associated with it.

Quoted 14 lines
drivers/iio/adc/pac193x.c new file +14 -0
+	if (ret) {
+		dev_err(&client->dev, "cannot read revision\n");
+		return ret;
+	}
+
+	if (rev_info[PAC193X_PID_IDX] != pac193x_chip_config[chip_info->chip_variant].prod_id) {
+		dev_err(&client->dev,
+			"chip's product ID doesn't match the exact one for this part\n");
+		return -EINVAL;
+	}
+
+	dev_dbg(&client->dev, "Chip revision: 0x%02X\n", rev_info[PAC193X_RID_IDX]);
+	chip_info->chip_revision = rev_info[PAC193X_RID_IDX];
+

drivers/iio/adc/pac193x.c line 1322

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+	return 0;
+}
+
+static int pac193x_get_variant(struct pac193x_chip_info *chip_info)
+{
+	u8 rev_info[PAC193X_ID_REG_LEN];
+	int ret;
+
+	ret = pac193x_read_revision(chip_info, (u8 *)rev_info);
+

As above, no blank line here preferred.

Though in this case

	if (ret)
		return 0; 

probably better, though that wants a comment on why a return of 0 is not an error.

drivers/iio/adc/pac193x.c line 1327

Quoted 5 lines
drivers/iio/adc/pac193x.c new file +5 -0
+	if (!ret) {
+		chip_info->chip_variant = rev_info[PAC193X_PID_IDX];
+		chip_info->chip_revision = rev_info[PAC193X_RID_IDX];
+		switch (chip_info->chip_variant) {
+		case PAC1934_PID:

You have an array of structures containing pids and number of channels. Better to search that for these than end up with this duplication.

Quoted 12 lines
drivers/iio/adc/pac193x.c new file +12 -0
+			chip_info->phys_channels = PAC1934_NUM_CHANNELS;
+			break;
+		case PAC1933_PID:
+			chip_info->phys_channels = PAC1933_NUM_CHANNELS;
+			break;
+		case PAC1932_PID:
+			chip_info->phys_channels = PAC1932_NUM_CHANNELS;
+			break;
+		case PAC1931_PID:
+			chip_info->phys_channels = PAC1931_NUM_CHANNELS;
+			break;
+		default:

drivers/iio/adc/pac193x.c line 1349

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int pac193x_setup_periodic_refresh(struct pac193x_chip_info *chip_info)
+{
+	chip_info->wq_chip = create_workqueue("wq_pac193x");

Add add a comment on why this needs it's own work queue rather than using a system one.

here I'd expect a devm_add_action_or_reset() to flush and free the queue.

drivers/iio/adc/pac193x.c line 1353

Quoted 4 lines
drivers/iio/adc/pac193x.c new file +4 -0
+	INIT_WORK(&chip_info->work_chip_rfsh, pac193x_work_periodic_rfsh);
+
+	/* setup the latest moment for reading the regs before saturation */
+	timer_setup(&chip_info->tmr_forced_update, pac193x_read_reg_timeout, 0);

And another devm_callback here to deal with the timer callback.

Quoted 57 lines
drivers/iio/adc/pac193x.c new file +57 -0
+
+	/* register the timer */
+	mod_timer(&chip_info->tmr_forced_update,
+		  jiffies + msecs_to_jiffies(PAC193X_MAX_RFSH_LIMIT_MS));
+
+	return 0;
+}
+
+static union acpi_object *pac193x_acpi_eval_function(acpi_handle handle,
+						     int revision,
+						     int function)
+{
+	acpi_status status;
+	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+	union acpi_object args[PAC193X_ACPI_ARG_COUNT];
+	struct acpi_object_list args_list;
+	uuid_t uuid;
+
+	uuid_parse(PAC193X_DSM_UUID, &uuid);
+
+	args[0].type = ACPI_TYPE_BUFFER;
+	args[0].buffer.length = sizeof(struct pac193x_uuid_format);
+	args[0].buffer.pointer = (u8 *)&uuid;
+
+	args[1].type = ACPI_TYPE_INTEGER;
+	args[1].integer.value = revision;
+
+	args[2].type = ACPI_TYPE_INTEGER;
+	args[2].integer.value = function;
+
+	args[3].type = ACPI_TYPE_PACKAGE;
+	args[3].package.count = 0;
+
+	args_list.count = PAC193X_ACPI_ARG_COUNT;
+	args_list.pointer = &args[0];
+
+	status = acpi_evaluate_object(handle, "_DSM", &args_list, &buffer);
+
+	if (ACPI_FAILURE(status)) {
+		kfree(buffer.pointer);
+		return NULL;
+	}
+
+	return buffer.pointer;
+}
+
+static char *pac193x_acpi_get_acpi_match_entry(acpi_handle handle)
+{
+	acpi_status status;
+	union acpi_object *name_object;
+	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+
+	status = acpi_evaluate_object(handle, "_HID", NULL, &buffer);
+	name_object = buffer.pointer;
+
+	return name_object->string.pointer;
+}

drivers/iio/adc/pac193x.c line 1420

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+
+static const char *pac193x_match_acpi_device(struct i2c_client *client,
+					     struct pac193x_chip_info *chip_info)
+{
+	char *name;
+	acpi_handle handle;
+	union acpi_object *rez;
+	unsigned short bi_dir_mask;
+	int idx, i;
+

Any docs for the ACPI stuff?
It looks plausible, but an example DSDT blob would make it easier to review.

drivers/iio/adc/pac193x.c line 1422

Quoted 2 lines
drivers/iio/adc/pac193x.c new file +2 -0
+	handle = ACPI_HANDLE(&client->dev);
+	name = pac193x_acpi_get_acpi_match_entry(handle);

Having to do this again is a bit nasty. The tricky corner is you might get here either by match against the ACPI match table or via a PRP0001 path.

You could use acpi_match_device_ids(acpi_companion(&client->dev)... ) to distinguish which case you have without rolling your own _HID read routine.

Quoted 54 lines
drivers/iio/adc/pac193x.c new file +54 -0
+	if (!name)
+		return NULL;
+
+	rez = pac193x_acpi_eval_function(handle, 0, PAC193X_ACPI_GET_NAMES_AND_MOHMS_VALS);
+
+	if (!rez)
+		return NULL;
+
+	for (i = 0; i < rez->package.count; i += 2) {
+		idx = i / 2;
+		chip_info->channel_names[idx] =
+			devm_kmemdup(&client->dev, rez->package.elements[i].string.pointer,
+				     (size_t)rez->package.elements[i].string.length + 1,
+				     GFP_KERNEL);
+		chip_info->channel_names[idx][rez->package.elements[i].string.length] = '\0';
+		chip_info->shunts[idx] =
+			rez->package.elements[i + 1].integer.value * 1000;
+		chip_info->active_channels[idx] = (chip_info->shunts[idx] != 0);
+	}
+
+	kfree(rez);
+
+	rez = pac193x_acpi_eval_function(handle, 1, PAC193X_ACPI_GET_UOHMS_VALS);
+	if (!rez) {
+		/*
+		 * initialising with default values
+		 * we assume all channels are unidectional(the mask is zero)
+		 * and assign the default sampling rate
+		 */
+		chip_info->sample_rate_value = PAC193X_DEFAULT_CHIP_SAMP_SPEED;
+		return name;
+	}
+
+	for (i = 0; i < rez->package.count; i++) {
+		idx = i;
+		chip_info->shunts[idx] = rez->package.elements[i].integer.value;
+		chip_info->active_channels[idx] = (chip_info->shunts[idx] != 0);
+	}
+
+	kfree(rez);
+
+	rez = pac193x_acpi_eval_function(handle, 1, PAC193X_ACPI_GET_BIPOLAR_SETTINGS);
+	if (!rez)
+		return NULL;
+	bi_dir_mask = rez->package.elements[0].integer.value;
+	chip_info->bi_dir[0] = (bi_dir_mask & (1 << 0)) << 0;
+	chip_info->bi_dir[0] = (bi_dir_mask & (1 << 1)) << 1;
+	chip_info->bi_dir[0] = (bi_dir_mask & (1 << 2)) << 2;
+	chip_info->bi_dir[0] = (bi_dir_mask & (1 << 3)) << 3;
+	kfree(rez);
+
+	rez = pac193x_acpi_eval_function(handle, 1, PAC193X_ACPI_GET_SAMP);
+	if (!rez)
+		return NULL;

drivers/iio/adc/pac193x.c line 1486

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+
+	chip_info->sample_rate_value = rez->package.elements[0].integer.value;
+	kfree(rez);
+
+	return name;
+}
+
+static const struct of_device_id pac193x_of_match[] = {
+	{ .compatible = "microchip,pac1934",
+	  .data = (void *)&pac193x_chip_config[pac1934]

Shouldn't need that cast as any const pointer should cast implicitly to a const void * which is what data is.

Also prefer

	{
		.compatible...
	},
	{

drivers/iio/adc/pac193x.c line 1499

Quoted 13 lines
drivers/iio/adc/pac193x.c new file +13 -0
+	},
+	{ .compatible = "microchip,pac1933",
+	  .data = (void *)&pac193x_chip_config[pac1933]
+	},
+	{ .compatible = "microchip,pac1932",
+	  .data = (void *)&pac193x_chip_config[pac1932]
+	},
+	{ .compatible = "microchip,pac1931",
+	  .data = (void *)&pac193x_chip_config[pac1931]
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, pac193x_of_match);

After other suggested changes you should be able to move this down next to the driver structure which is a more conventional place to find these tables.

drivers/iio/adc/pac193x.c line 1506

Quoted 7 lines
drivers/iio/adc/pac193x.c new file +7 -0
+
+static int pac193x_match_of_device(struct i2c_client *client,
+				   struct pac193x_chip_info *chip_info)
+{
+	struct device_node *node;
+	unsigned int current_channel;
+	int idx, ret;

Use generic property accessors linux/property.h for all these.

drivers/iio/adc/pac193x.c line 1512

Quoted 6 lines
drivers/iio/adc/pac193x.c new file +6 -0
+
+	ret = of_property_read_u32(client->dev.of_node, "microchip,samp-rate",
+				   &chip_info->sample_rate_value);
+	if (ret) {
+		dev_err(&client->dev,
+			"Cannot read sample rate value ...%d\n", ret);

only called from probe, so dev_err_probe better.

drivers/iio/adc/pac193x.c

Quoted 1 line
drivers/iio/adc/pac193x.c new file +1 -0
+		goto err_of_node_put;

put the node in the caller not in here as this function didn't get it.

Quoted 76 lines
drivers/iio/adc/pac193x.c new file +76 -0
+	}
+
+	ret = pac193x_match_samp_rate(chip_info, chip_info->sample_rate_value);
+	if (ret) {
+		dev_err(&client->dev,
+			"The given sample rate value is not supported: %d\n",
+			chip_info->sample_rate_value);
+		goto err_of_node_put;
+	}
+
+	current_channel = 1;
+	for_each_child_of_node(client->dev.of_node, node) {
+		ret = of_property_read_u32(node, "reg", &idx);
+		if (ret) {
+			dev_err(&client->dev,
+				"invalid channel_index, error: %d\n",
+				ret);
+			goto err_of_node_put;
+		}
+		/* adjust idx to match channel index (1 to 4) from the datasheet */
+		idx--;
+		if (current_channel >= (chip_info->phys_channels + 1) ||
+		    idx >= chip_info->phys_channels || idx < 0) {
+			dev_err(&client->dev,
+				"invalid channel_index %d value on %s\n",
+				(idx + 1), node->full_name);
+			goto err_of_node_put;
+		}
+
+		/* enable channel */
+		chip_info->active_channels[idx] = true;
+
+		ret = of_property_read_u32(node, "microchip,uohms-shunt-res",
+					   &chip_info->shunts[idx]);
+		if (ret) {
+			dev_err(&client->dev,
+				"%s: invalid shunt-resistor value: %d\n",
+				node->full_name, ret);
+			goto err_of_node_put;
+		}
+
+		ret = of_property_read_string(node, "microchip,rail-name",
+					      (const char **)&chip_info->channel_names[idx]);
+		if (ret) {
+			dev_err(&client->dev,
+				"%s: invalid rail-name value: %d\n",
+				node->full_name, ret);
+			goto err_of_node_put;
+		}
+
+		chip_info->bi_dir[idx] =
+			of_property_read_bool(node, "microchip,bi-directional");
+
+		current_channel++;
+	}
+
+	return 0;
+
+err_of_node_put:
+	of_node_put(node);
+	return ret;
+}
+
+static int pac193x_chip_configure(struct pac193x_chip_info *chip_info)
+{
+	int cnt, ret;
+	struct i2c_client *client = chip_info->client;
+	u8 regs[PAC193X_CTRL_STATUS_INFO_LEN], idx, ctrl_reg;
+	u32 wait_time;
+
+	/*
+	 * count how many channels are enabled and store
+	 * this information within the driver data
+	 */
+	cnt = 0;
+	chip_info->chip_reg_data.num_enabled_channels = 0;

drivers/iio/adc/pac193x.c line 1599

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+	while (cnt < chip_info->phys_channels) {
+		if (chip_info->active_channels[cnt])
+			chip_info->chip_reg_data.num_enabled_channels++;
+		cnt++;
+	}
+
+	/*
+	 * read whatever information was gathered before the driver was loaded
+	 * establish which channels are enabled/disabled and then establish the
+	 * information retrieval mode (using SKIP or no).

It's unusual to have a driver carry on with existing values. Much more common to either have it reset the device (to known default state) or write all the registers if such a reset doesn't exist.

One reason for that flow is that we really don't trust other software :) Another is that often we don't know if the device had any power until we turned it on. So it's much easier to review code where we control all the state.

If there is a strong reason for carrying on with existing settings please add comments here and something to the patch description to point out this unusual behaviour.

drivers/iio/adc/pac193x.c line 1603

Quoted 4 lines
drivers/iio/adc/pac193x.c new file +4 -0
+	 * Read the chip ID values
+	 */
+	ret = pac193x_i2c_read(client, PAC193X_CTRL_STAT_REGS_ADDR,
+			       (u8 *)regs, PAC193X_CTRL_STATUS_INFO_LEN);

sizeof(regs)
so a reviewer doesn't need to go check there is space.

drivers/iio/adc/pac193x.c line 1607

Quoted 4 lines
drivers/iio/adc/pac193x.c new file +4 -0
+	if (ret) {
+		dev_err(&client->dev,
+			"%s - cannot read regs from 0x%02X\n",
+			__func__, PAC193X_CTRL_STAT_REGS_ADDR);

As this is only called from probe, you can use dev_err_probe() for simpler code in here.

Quoted 26 lines
drivers/iio/adc/pac193x.c new file +26 -0
+		return ret;
+	}
+
+	/* write the CHANNEL_DIS and the NEG_PWR registers */
+	regs[PAC193X_CHANNEL_DIS_REG_OFF] =
+		PAC193X_CHAN_DIS_CH1_OFF(chip_info->active_channels[PAC193X_CH_1]) |
+		PAC193X_CHAN_DIS_CH2_OFF(chip_info->active_channels[PAC193X_CH_2]) |
+		PAC193X_CHAN_DIS_CH3_OFF(chip_info->active_channels[PAC193X_CH_3]) |
+		PAC193X_CHAN_DIS_CH4_OFF(chip_info->active_channels[PAC193X_CH_4]) |
+		PAC193X_SMBUS_TIMEOUT_EN(0) |
+		PAC193X_SMBUS_BYTECOUNT_EN(0) |
+		PAC193X_SMBUS_NO_SKIP_EN(0);
+
+	regs[PAC193X_NEG_PWR_REG_OFF] =
+		PAC193X_NEG_PWR_CH1_BIDI(chip_info->bi_dir[PAC193X_CH_1]) |
+		PAC193X_NEG_PWR_CH2_BIDI(chip_info->bi_dir[PAC193X_CH_2]) |
+		PAC193X_NEG_PWR_CH3_BIDI(chip_info->bi_dir[PAC193X_CH_3]) |
+		PAC193X_NEG_PWR_CH4_BIDI(chip_info->bi_dir[PAC193X_CH_4]) |
+		PAC193X_NEG_PWR_CH1_BIDV(chip_info->bi_dir[PAC193X_CH_1]) |
+		PAC193X_NEG_PWR_CH2_BIDV(chip_info->bi_dir[PAC193X_CH_2]) |
+		PAC193X_NEG_PWR_CH3_BIDV(chip_info->bi_dir[PAC193X_CH_3]) |
+		PAC193X_NEG_PWR_CH4_BIDV(chip_info->bi_dir[PAC193X_CH_4]);
+
+	/*
+	 * the current can be measured uni or bi-dir, but voltages are set only
+	 * for uni-directional operation

drivers/iio/adc/pac193x.c line 1643

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+	 * no SLOW triggered REFRESH, clear POR
+	 */
+	regs[PAC193X_SLOW_REG_OFF] = 0;
+
+	/* write the updated registers back */
+	ret = pac193x_i2c_write(client, PAC193X_CTRL_STAT_REGS_ADDR, (u8 *)regs, 3);
+	if (ret)
+		return ret;
+
+	/* enable the ALERT pin functionality */

Where is the alert pin wired to? I'd expect to see an interrupt and for this to be optional as people have a habit of not writing interrupt lines up (sigh)

drivers/iio/adc/pac193x.c line 1654

Quoted 11 lines
drivers/iio/adc/pac193x.c new file +11 -0
+	ctrl_reg = PAC193X_CRTL_SAMPLE_RATE_SET(chip_info->crt_samp_spd_bitfield) |
+		PAC193X_CHAN_ALERT_EN;
+	ret = pac193x_i2c_write_byte(client, PAC193X_CTRL_REG, ctrl_reg);
+	if (ret)
+		return ret;
+
+	/*
+	 * send a REFRESH to the chip, so the new settings take place
+	 * as well as resetting the accumulators
+	 */
+	ret = pac193x_i2c_send_byte(client, PAC193X_REFRESH_REG);

Naming of write_byte vs send_byte is a bit confusing. Perhaps call one write_reg()?

Quoted 12 lines
drivers/iio/adc/pac193x.c new file +12 -0
+	if (ret) {
+		dev_err(&client->dev,
+			"%s - cannot send 0x%02X\n",
+			__func__, PAC193X_REFRESH_REG);
+		return ret;
+	}
+
+	/*
+	 * get the current(in the chip) sampling speed and compute the
+	 * required timeout based on its value
+	 * the timeout is 1/sampling_speed
+	 */

drivers/iio/adc/pac193x.c line 1676

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+	idx = regs[PAC193X_CTRL_ACT_REG_OFF] >> PAC193X_SAMPLE_RATE_SHIFT;
+	wait_time = (1024 / samp_rate_map_tbl[idx]) * 1000;
+
+	/*
+	 * wait the maximum amount of time to be on the safe side - the
+	 * maximum wait time is for 8sps
+	 */
+	usleep_range(wait_time, wait_time + 100);
+
+	/* setup the refresh timeout */

As below, some of these comments are obvious, so drop them. Others are much more useful of course and want to be kept.

drivers/iio/adc/pac193x.c line 1677

Quoted 1 line
drivers/iio/adc/pac193x.c new file +1 -0
+	ret = pac193x_setup_periodic_refresh(chip_info);

return pac193x...

Quoted 132 lines
drivers/iio/adc/pac193x.c new file +132 -0
+
+	return ret;
+}
+
+static int pac193x_prep_iio_channels(struct pac193x_chip_info *chip_info, struct iio_dev *indio_dev)
+{
+	struct i2c_client *client;
+	struct iio_chan_spec *ch_sp;
+	int channel_size, channel_attribute_count, attribute_count, cnt;
+	void *dyn_ch_struct, *tmp_data;
+
+	client = chip_info->client;
+
+	/* find out dynamically how many IIO channels we need */
+	channel_attribute_count = 0;
+	channel_size = 0;
+	for (cnt = 0; cnt < chip_info->phys_channels; cnt++) {
+		if (chip_info->active_channels[cnt]) {
+			/* add the size of the properties of one chip physical channel */
+			channel_size += sizeof(pac193x_single_channel);
+			/* count how many enabled channels we have */
+			channel_attribute_count += ARRAY_SIZE(pac193x_single_channel);
+			dev_info(&client->dev, ":%s: Channel %d active\n", __func__, cnt + 1);
+		}
+	}
+
+	/* now add the timestamp channel size */
+	channel_size += sizeof(pac193x_ts);
+
+	/* add one more channel which is the timestamp */
+	attribute_count = channel_attribute_count + 1;
+
+	dyn_ch_struct = kzalloc(channel_size, GFP_KERNEL);
+	if (!dyn_ch_struct)
+		return -EINVAL;
+
+	tmp_data = dyn_ch_struct;
+
+	/* populate the dynamic channels and make all the adjustments */
+	for (cnt = 0; cnt < chip_info->phys_channels; cnt++) {
+		if (chip_info->active_channels[cnt]) {
+			memcpy(tmp_data, pac193x_single_channel, sizeof(pac193x_single_channel));
+			ch_sp = (struct iio_chan_spec *)tmp_data;
+			ch_sp[IIO_EN].channel = cnt  + 1;
+			ch_sp[IIO_EN].scan_index = cnt;
+			ch_sp[IIO_EN].address = cnt + PAC193X_VPOWER_ACC_1_ADDR;
+			ch_sp[IIO_POW].channel = cnt  + 1;
+			ch_sp[IIO_POW].scan_index = cnt;
+			ch_sp[IIO_POW].address = cnt + PAC193X_VPOWER_1_ADDR;
+			ch_sp[IIO_VOLT].channel = cnt  + 1;
+			ch_sp[IIO_VOLT].scan_index = cnt;
+			ch_sp[IIO_VOLT].address = cnt + PAC193X_VBUS_1_ADDR;
+			ch_sp[IIO_CRT].channel = cnt  + 1;
+			ch_sp[IIO_CRT].scan_index = cnt;
+			ch_sp[IIO_CRT].address = cnt + PAC193X_VSENSE_1_ADDR;
+			ch_sp[IIO_VOLTAVG].channel = cnt  + 1;
+			ch_sp[IIO_VOLTAVG].scan_index = cnt;
+			ch_sp[IIO_VOLTAVG].address = cnt + PAC193X_VBUS_AVG_1_ADDR;
+			ch_sp[IIO_CRTAVG].channel = cnt  + 1;
+			ch_sp[IIO_CRTAVG].scan_index = cnt;
+			ch_sp[IIO_CRTAVG].address = cnt + PAC193X_VSENSE_AVG_1_ADDR;
+
+			/*
+			 * now modify the parameters in all channels if the
+			 * whole chip rail(channel) is bi-directional
+			 */
+			if (chip_info->bi_dir[cnt]) {
+				ch_sp[IIO_EN].scan_type.sign = 's';
+				ch_sp[IIO_EN].scan_type.realbits = PAC193X_ENERGY_S_RES;
+				ch_sp[IIO_POW].scan_type.sign = 's';
+				ch_sp[IIO_POW].scan_type.realbits = PAC193X_POWER_S_RES;
+				ch_sp[IIO_VOLT].scan_type.sign = 's';
+				ch_sp[IIO_VOLT].scan_type.realbits = PAC193X_VOLTAGE_S_RES;
+				ch_sp[IIO_CRT].scan_type.sign = 's';
+				ch_sp[IIO_CRT].scan_type.realbits = PAC193X_CURRENT_S_RES;
+				ch_sp[IIO_VOLTAVG].scan_type.sign = 's';
+				ch_sp[IIO_VOLTAVG].scan_type.realbits = PAC193X_VOLTAGE_S_RES;
+				ch_sp[IIO_CRTAVG].scan_type.sign = 's';
+				ch_sp[IIO_CRTAVG].scan_type.realbits = PAC193X_CURRENT_S_RES;
+			}
+			tmp_data += sizeof(pac193x_single_channel);
+		}
+	}
+
+	/* now copy the timestamp channel */
+	memcpy(tmp_data, pac193x_ts, sizeof(pac193x_ts));
+	ch_sp = (struct iio_chan_spec *)tmp_data;
+	ch_sp[0].scan_index = attribute_count - 1;
+
+	/*
+	 * send the updated dynamic channel structure information towards IIO
+	 * prepare the required field for IIO class registration
+	 */
+	indio_dev->num_channels = attribute_count;
+	indio_dev->channels = devm_kmemdup(&client->dev,
+					   (const struct iio_chan_spec *)dyn_ch_struct,
+					   channel_size, GFP_KERNEL);
+
+	kfree(dyn_ch_struct);
+
+	if (!indio_dev->channels)
+		return -EINVAL;
+
+	return 0;
+}
+
+static ssize_t reset_accumulators_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct pac193x_chip_info *chip_info = iio_priv(indio_dev);
+	int ret, i;
+	u8 refresh_cmd = PAC193X_REFRESH_REG;
+
+	ret = pac193x_i2c_send_byte(chip_info->client, refresh_cmd);
+	if (ret) {
+		dev_err(&indio_dev->dev,
+			"%s - cannot send 0x%02X\n",
+			__func__, refresh_cmd);
+	}
+
+	for (i = 0 ; i < chip_info->phys_channels; i++)
+		chip_info->chip_reg_data.energy_sec_acc[i] = 0;
+
+	return count;
+}
+
+static IIO_DEVICE_ATTR(shunt_value_1, 0644, shunt_value_show, shunt_value_store, 0);
+static IIO_DEVICE_ATTR(shunt_value_2, 0644, shunt_value_show, shunt_value_store, 0);
+static IIO_DEVICE_ATTR(shunt_value_3, 0644, shunt_value_show, shunt_value_store, 0);
+static IIO_DEVICE_ATTR(shunt_value_4, 0644, shunt_value_show, shunt_value_store, 0);

drivers/iio/adc/pac193x.c line 1819

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+
+static IIO_DEVICE_ATTR(channel_name_1, 0444, channel_name_show, NULL, 0);
+static IIO_DEVICE_ATTR(channel_name_2, 0444, channel_name_show, NULL, 0);
+static IIO_DEVICE_ATTR(channel_name_3, 0444, channel_name_show, NULL, 0);
+static IIO_DEVICE_ATTR(channel_name_4, 0444, channel_name_show, NULL, 0);
+
+static IIO_DEVICE_ATTR(reset_accumulators, 0200, NULL, reset_accumulators_store, 0);
+
+static struct attribute *pac193x_all_attributes[] = {
+	PAC193X_DEV_ATTR(shunt_value_1),

These all need ABI documentation so that we can easily review what they do. Documenation/ABI/testing/sysfs-bus-iio-pac1931
Note that if they overlap with ABI used elsewhere we may need to move it to a more generic place (there can't be two lots of docs for the same ABI element)

Quoted 82 lines
drivers/iio/adc/pac193x.c new file +82 -0
+	PAC193X_DEV_ATTR(channel_name_1),
+	PAC193X_DEV_ATTR(shunt_value_2),
+	PAC193X_DEV_ATTR(channel_name_2),
+	PAC193X_DEV_ATTR(shunt_value_3),
+	PAC193X_DEV_ATTR(channel_name_3),
+	PAC193X_DEV_ATTR(shunt_value_4),
+	PAC193X_DEV_ATTR(channel_name_4),
+	PAC193X_DEV_ATTR(reset_accumulators),
+	NULL
+};
+
+static int pac193x_prep_custom_attributes(struct pac193x_chip_info *chip_info,
+					  struct iio_dev *indio_dev)
+{
+	int i, j, active_channels_count = 0;
+	struct attribute **pac193x_custom_attributes;
+	struct attribute_group *pac193x_group;
+	struct i2c_client *client = chip_info->client;
+
+	for (i = 0 ; i < chip_info->phys_channels; i++)
+		if (chip_info->active_channels[i])
+			active_channels_count++;
+
+	pac193x_group = devm_kzalloc(&client->dev, sizeof(*pac193x_group), GFP_KERNEL);
+
+	pac193x_custom_attributes = devm_kzalloc(&client->dev,
+						 (PAC193X_CUSTOM_ATTR_FOR_CHANNEL *
+						 active_channels_count +
+						 PAC193X_SHARED_DEVATTRS_COUNT)
+						 * sizeof(*pac193x_group) + 1,
+						 GFP_KERNEL);
+	j = 0;
+
+	for (i = 0 ; i < chip_info->phys_channels; i++) {
+		if (chip_info->active_channels[i]) {
+			pac193x_custom_attributes[PAC193X_CUSTOM_ATTR_FOR_CHANNEL * j] =
+			pac193x_all_attributes[PAC193X_CUSTOM_ATTR_FOR_CHANNEL * i];
+			pac193x_custom_attributes[PAC193X_CUSTOM_ATTR_FOR_CHANNEL * j + 1] =
+			pac193x_all_attributes[PAC193X_CUSTOM_ATTR_FOR_CHANNEL * i + 1];
+			j++;
+		}
+	}
+
+	for (i = 0; i < PAC193X_SHARED_DEVATTRS_COUNT; i++)
+		pac193x_custom_attributes[PAC193X_CUSTOM_ATTR_FOR_CHANNEL *
+			active_channels_count + i] =
+			pac193x_all_attributes[PAC193X_CUSTOM_ATTR_FOR_CHANNEL *
+			chip_info->phys_channels + i];
+
+	pac193x_group->attrs = pac193x_custom_attributes;
+	chip_info->pac193x_info.attrs = pac193x_group;
+
+	return 0;
+}
+
+static void pac193x_remove(struct i2c_client *client)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(&client->dev);
+	struct pac193x_chip_info *chip_info = iio_priv(indio_dev);
+
+	ret = try_to_del_timer_sync(&chip_info->tmr_forced_update);
+	if (ret < 0)
+		dev_err(&client->dev,
+			"%s - cannot delete the forced readout timer\n",
+			__func__);
+
+	if (chip_info->wq_chip) {
+		cancel_work_sync(&chip_info->work_chip_rfsh);
+		flush_workqueue(chip_info->wq_chip);
+		destroy_workqueue(chip_info->wq_chip);
+	}
+}
+
+static int pac193x_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	struct pac193x_chip_info *chip_info;
+	struct iio_dev *indio_dev;
+	const char *name = NULL;
+	const struct of_device_id *match;
+	int cnt, ret, dev_id = 0;
+

drivers/iio/adc/pac193x.c line 1911

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip_info));
+	if (!indio_dev) {
+		dev_err_probe(&indio_dev->dev, PTR_ERR(indio_dev),
+			      "Can't allocate iio device\n");
+		return -ENOMEM;
+	}
+
+	chip_info = iio_priv(indio_dev);
+
+	i2c_set_clientdata(client, indio_dev);

Assuming you follow suggestion to go fully devm managed handling of remove, I don't htink you will need this.

drivers/iio/adc/pac193x.c line 1914

Quoted 3 lines
drivers/iio/adc/pac193x.c new file +3 -0
+	chip_info->client = client;
+
+	memset(&chip_info->chip_reg_data, 0, sizeof(chip_info->chip_reg_data));

The iio_priv space is allocated with kzalloc so no need to zero here.

drivers/iio/adc/pac193x.c line 1917

Quoted 3 lines
drivers/iio/adc/pac193x.c new file +3 -0
+
+	if (ACPI_HANDLE(&client->dev)) {
+		pac193x_get_variant(chip_info);

Why is this ACPI specific?

If you can query the variant from the hardware, that is the right thing to use for all registration types. It can be helpful to call out if there is a mismatch though with a warning print, so comparing the result of the i2c call with the data is fine.

drivers/iio/adc/pac193x.c line 1930

Quoted 13 lines
drivers/iio/adc/pac193x.c new file +13 -0
+	} else {
+		dev_id = id->driver_data;
+
+		/* store the type of chip */
+		chip_info->chip_variant = dev_id;
+
+		/* get the maximum number of channels for the given chip id */
+		chip_info->phys_channels = pac193x_chip_config[dev_id].phys_channels;
+	}
+
+	/*
+	 * load default settings - all channels disabled,
+	 * uni directional flow, default shunt values

The concept of a default shunt value bothers me a little if this is an external resistor. How is there in any real sense a default? Can we just fail if it's not provided - or wrap the default up for just ACPI case where I guess there might be firmware that doesn't specify it?

drivers/iio/adc/pac193x.c line 1943

Quoted 13 lines
drivers/iio/adc/pac193x.c new file +13 -0
+	 */
+	for (cnt = 0; cnt < chip_info->phys_channels; cnt++) {
+		chip_info->active_channels[cnt] = false;
+		chip_info->bi_dir[cnt] = false;
+		chip_info->shunts[cnt] = SHUNT_UOHMS_DEFAULT;
+	}
+
+	chip_info->crt_samp_spd_bitfield = pac193X_samp_1024sps;
+
+	if (ACPI_HANDLE(&client->dev)) {
+		switch (chip_info->chip_variant) {
+		case PAC1934_PID:
+			client->dev.init_name = "pac1934";

Fairly sure init_name is only relevant prior to driver_add() which is long gone by the time probe is called.

Quoted 4 lines
drivers/iio/adc/pac193x.c new file +4 -0
+			break;
+		case PAC1933_PID:
+			client->dev.init_name = "pac1933";
+			break;

drivers/iio/adc/pac193x.c line 1957

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+		case PAC1932_PID:
+			client->dev.init_name = "pac1932";
+			break;
+		case PAC1931_PID:
+			client->dev.init_name = "pac1931";
+			break;
+		default:
+			return -EINVAL;
+		}
+		name = pac193x_match_acpi_device(client, chip_info);

This looks to be doing a lot more than matching (which has already happened via the i2c query above). Rename the function.

drivers/iio/adc/pac193x.c line 1965

Quoted 8 lines
drivers/iio/adc/pac193x.c new file +8 -0
+	} else {
+		/* identify the chip we have to deal with */
+		ret = pac193x_chip_identify(chip_info);
+		if (ret)
+			return -EINVAL;
+
+		/* check if we find the device within DT */
+		if (!client->dev.of_node || (!of_get_next_child(client->dev.of_node, NULL)))

You are checking for a child node? This isn't checking for the device itself so the comment doesn't match.

drivers/iio/adc/pac193x.c line 1972

Quoted 7 lines
drivers/iio/adc/pac193x.c new file +7 -0
+			return -EINVAL;
+
+		match = of_match_node(pac193x_of_match, client->dev.of_node);
+		if (match) {
+			ret = pac193x_match_of_device(client, chip_info);
+			if (!ret)
+				name = match->compatible;

I can't really follow why this complexity is all here. This should be using generic firmware properties from include/linux/property.h so that ACPI PNP0001 and various other less common firmware types work. Then you should be able to query the properties without a dance to match the node. Given the ACPI specific handling it's fine to try that first and then fallback to generic handling if it fails. Though note, don't base that decision on the ACPI_HANDLE as you may have a PRP0001 using firmware which is also ACPI but uses device tree binding properties.

Name should not be the compatible, but rather just pac1931 etc (no vendor). Given you have a bunch of different match codes, better to have a per type constant structure with all the chip differences and just put a string in there to use for the name. That way same thing works for ACPI and DT. Your pac193x_features structure is perfect for this.

Quoted 9 lines
drivers/iio/adc/pac193x.c new file +9 -0
+		}
+	}
+
+	if (!name) {
+		dev_err_probe(&indio_dev->dev, PTR_ERR(indio_dev),
+			      "parameter parsing returned an error\n");
+		return -EINVAL;
+	}
+

drivers/iio/adc/pac193x.c line 1991

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+	mutex_init(&chip_info->lock);
+
+	/*
+	 * do now any chip specific initialization (e.g. read/write
+	 * some registers), enable/disable certain channels, change the sampling
+	 * rate to the requested value
+	 */
+	ret = pac193x_chip_configure(chip_info);
+	if (ret < 0)
+		goto free_chan_attr_mem;

I'm not totally sure which parts of remove match with this, but I'd expect there to be devm_add_action_or_reset() calls to setup automated tear down alongside each aspect.

The one thing the call doesn't seem to do is free memory!

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+
+	/* prepare the channel information */
+	ret = pac193x_prep_iio_channels(chip_info, indio_dev);
+	if (ret < 0)
+		goto free_chan_attr_mem;
+
+	ret = pac193x_prep_custom_attributes(chip_info, indio_dev);
+	if (ret < 0) {
+		dev_err_probe(&indio_dev->dev, ret,
+			      "Can't configure custom attributes for PAC193X device\n");

drivers/iio/adc/pac193x.c line 2011

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+		goto free_chan_attr_mem;
+	}
+
+	chip_info->pac193x_info.read_raw = pac193x_read_raw;
+	chip_info->pac193x_info.read_avail = pac193x_read_avail;
+	chip_info->pac193x_info.write_raw = pac193x_write_raw;
+
+	indio_dev->info = &chip_info->pac193x_info;
+	indio_dev->name = name;
+	indio_dev->dev.parent = &client->dev;

Parent is set automatically by the devm_iio_device_alloc(). A driver should only be doing this if it needs to change the default for some reason (a few drivers do).

drivers/iio/adc/pac193x.c line 2015

Quoted 4 lines
drivers/iio/adc/pac193x.c new file +4 -0
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	/*
+	 * read whatever it has been accumulated in the chip so far

whatever has been

drivers/iio/adc/pac193x.c line 2022

Quoted 7 lines
drivers/iio/adc/pac193x.c new file +7 -0
+	 * and reset the accumulators
+	 */
+	ret = pac193x_reg_snapshot(chip_info, true, false, PAC193X_MIN_UPDATE_WAIT_TIME_MS);
+	if (ret < 0)
+		goto free_chan_attr_mem;
+
+	/* register with IIO */

Drop all comments that say things that can be easily seen from the code. They provide no value and can easily become wrong.

drivers/iio/adc/pac193x.c line 2023

Quoted 1 line
drivers/iio/adc/pac193x.c new file +1 -0
+	ret = devm_iio_device_register(&client->dev, indio_dev);

This results in the user interface being remove after you've called the pac193x_remove() which seems likely to be a nasty race.

There are two ways to use devm safely.
1) Stop using any devm_ calls at the first thing you manually undo in error paths / remove() callback.
2) Don't have any handing in error paths or remove callback because you have converted everything to device managed (devm_add_action_or_reset() etc to register custom callbacks).

2 is preferred unless things get really complex because it allows for easy step by step review.

drivers/iio/adc/pac193x.c line 2033

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+	if (ret < 0) {
+		dev_err_probe(&indio_dev->dev, ret,
+			      "Can't register IIO device\n");
+		goto free_chan_attr_mem;
+	}
+
+	return 0;
+
+free_chan_attr_mem:
+	pac193x_remove(client);

devm_add_action_or_reset() allows you to register an extra callback that will be called in the correct location. Ideally a remove flow is almost always the reverse of probe flow as that makes it much easier to avoid use after free and similar races. The code here is probably correct but it is harder to review.

You will probably want to break the _remove() function up into a couple of different callbacks, undoing individual steps in probe()

drivers/iio/adc/pac193x.c line 2039

Quoted 6 lines
drivers/iio/adc/pac193x.c new file +6 -0
+
+	return ret;
+}
+
+static const struct i2c_device_id pac193x_id[] = {
+	{ "pac1934", pac1934 },

Opposite order is more common.

drivers/iio/adc/pac193x.c line 2049

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +10 -0
+	{ "pac1933", pac1933 },
+	{ "pac1932", pac1932 },
+	{ "pac1931", pac1931 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, pac193x_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id pac193x_acpi_match[] = {
+	{"MCHP1930", 0},

Someone had a sense of humour if this ID doesn't actually cover a part called 1930.. :)

drivers/iio/adc/pac193x.c line 2050

Quoted 1 line
drivers/iio/adc/pac193x.c new file +1 -0
+	{ },

Trivial, but prefer no , on "NULL terminators" as we will never add anything after them.
.

drivers/iio/adc/pac193x.c line 2059

Quoted 9 lines
drivers/iio/adc/pac193x.c new file +9 -0
+};
+MODULE_DEVICE_TABLE(acpi, pac193x_acpi_match);
+#endif
+
+static struct i2c_driver pac193x_driver = {
+	.driver	 = {
+		.name = "pac193x",
+		.of_match_table = pac193x_of_match,
+		.acpi_match_table = ACPI_PTR(pac193x_acpi_match)

It is very rarely worth the complexity to save a tiny bit of size that results from ACPI_PTR() So unless I'm missing another reason for this (missing stubs maybe) then I'd not bother with that protection or the ifdef guards.

drivers/iio/adc/pac193x.c line 2061

Quoted 2 lines
drivers/iio/adc/pac193x.c new file +2 -0
+		},
+	.probe = pac193x_probe,

Use probe_new
This has very nearly gone away in upstream kernel.

Quoted 11 lines
drivers/iio/adc/pac193x.c new file +11 -0
+	.remove = pac193x_remove,
+	.id_table = pac193x_id,
+};
+
+module_i2c_driver(pac193x_driver);
+
+MODULE_AUTHOR("Bogdan Bolocan <[email protected]>");
+MODULE_AUTHOR("Victor Tudose");
+MODULE_AUTHOR("Marius Cristea <[email protected]>");
+MODULE_DESCRIPTION("IIO driver for PAC193X Multi-Channel DC Power/Energy Monitor");
+MODULE_LICENSE("GPL");
#15 lore raw

Re: [PATCH v1 2/2] iio: adc: adding support for pac193x

Hi Jonathan,

Please see my comments below. (I will take out section that I already
agree with):

Quoted 8 lines · Jonathan Cameron

a representative part from the list - if no other basis to chose go with the
lowest number currently supported.

The custom ABI in here needs documentation before we can review it properly.

I will comment all new ABI that I have added.

Quoted 36 lines

Comments inline.

It's a big driver, so I'm sure more stuff will come up next round just due to
mental exhaustion reviewing it in one go!

Quoted 20 lines

BIT(IIO_CHAN_INFO_SAMP_FREQ),      \
+     .scan_index =
(_si),                                                    \
+     .scan_type =
{                                                          \
+             .sign =
'u',                                                    \
+             .realbits =
PAC193X_POWER_U_RES,                                \
+             .storagebits =
32,                                              \
+             .shift =
4,                                                     \
+             .endianness =
IIO_CPU,                                          \
+
}
     \

(quoted diff) +2 -0
+}
+
Quoted 9 lines
Quoted 6 lines

+#define PAC193X_SOFT_TIMESTAMP(_index)
{                                     \
+     .type =
IIO_TIMESTAMP,                                                  \
+     .channel = -
1,                                                          \

What is this for?

I will take it out. Initially I want to have also "data buffering"
into the driver, but this functionality I will add it later. Data buffering (containing timestamp) will be useful when profiling power consumption.

Quoted 14 lines

drivers/iio/adc/pac193x.c line 672

Quoted 10 lines
drivers/iio/adc/pac193x.c new file +7 -0
+
+     len += sprintf(buf, "%u\n", chip_info->shunts[target]);
+
+     return len;
+}
+
+static ssize_t channel_name_show(struct device *dev,

Looks like per channel label support provided by the read_label iio_info callback.

I was trying to use the read_label iio_info callback but I end it up into a sysfs error related to duplicated labels.

Also I'm not sure if this read_label will help me in the end. What I was aiming to obtain here with the "channel_name_show" and the custom IIO attribute was to have "an umbrella" name/label for multiple IIO channels. For example on physical "channel 1" we will measure the voltage, the current and the power because all of those IIO channels will point to one board power supply (like VDD, VIO, V_GPU, V_DDR, etc).

Quoted 21 lines
Quoted 11 lines

+                              struct device_attribute *attr, +                              char *buf)
+{
+     struct iio_dev *indio_dev = dev_to_iio_dev(dev); +     struct pac193x_chip_info *chip_info = iio_priv(indio_dev); +     int len = 0;
+     int target = (int)(attr->attr.name[strlen(attr->attr.name) - 1] - '0') - 1;
+
+     len += sprintf(buf, "%s\n", chip_info-

Quoted 1 line

channel_names[target]);

drivers/iio/adc/pac193x.c line 686

Quoted 5 lines
drivers/iio/adc/pac193x.c new file +5 -0
+
+     return len;
+}
+
+static ssize_t shunt_value_store(struct device *dev,

Shunt values aren't normally dynamic.  Why do you need to write it from
userspace? I'd expect that to come from firmware.

Quoted 2 lines

If it's needed the ext_info framework might be a better fit than direct implementation of the attribute.

Yes, usually the shunt aren't normally dynamic, but what I'm aiming to get here is to let the user to overwrite the value after a calibration of the system. This will improve the accuracy of the reading even in the case the shunts are not of high precision ones.

Quoted 21 lines
Quoted 4 lines

+                     case PAC193X_VPOWER_4_ADDR:
+                             *val = chip_info-

Quoted 1 line

chip_reg_data.vpower[channel];

Quoted 16 lines

+                             return IIO_VAL_INT;
+                     default:
+                             return -EINVAL;
+                     }
+                     break;
+             case IIO_ENERGY:
+                     switch (chan->address) {
+                     case PAC193X_VPOWER_ACC_1_ADDR:
+                     case PAC193X_VPOWER_ACC_2_ADDR:
+                     case PAC193X_VPOWER_ACC_3_ADDR:
+                     case PAC193X_VPOWER_ACC_4_ADDR:
+                             /*
+                              * expresses the 64 bit energy value as a 32 bit integer part and
+                              * 32 bits (representing 8 digits) fractional value

Quoted 3 lines

I'm lost.  So it's a 64 bit value?
We have IIO_VAL_INT_64. Does that not work here?

Yes, we have the IIO_VAL_INT_64 and it will work here. The "issue" was that the IIO_VAL_INT_64 is not available before kernel v6.0 and we already release something to customers supporting Linux kernel v4.7 and v5.15. Also our latest release of Linux4Sam is based on the Linux kernel v5.15 (we are working to have a release based on v6.1).

Maybe I'm totally wrong here but my idea was to have something "more generic" that could be easily backported to an older kernel and to change this to IIO_VAL_INT_64 later.

Quoted 32 lines
Quoted 14 lines

+                              */
+                             curr_energy = chip_info-

Quoted 1 line

chip_reg_data.energy_sec_acc[channel];

+                             int_part = div_s64_rem(curr_energy, 100000000, &rem);
+
+                             /* rescale reminder to be printed as "nano" value */
+                             rem = rem * 10;
+
+                             *val = int_part;
+                             *val2 = rem;
+                             return IIO_VAL_INT_PLUS_NANO; +                     default:

Quoted 8 lines

+                             return -EINVAL;
+                     }
+                     break;
+             default:
+                     return -EINVAL;
+             }
+             break;
+     case IIO_CHAN_INFO_AVERAGE_RAW:

I'd forgotten we had this ABI and had to look up the docs.   Hmm. It's a bit clunky but I guess
still useful in this case.  Longer term we should think about how to describe that and how it
is different from oversampling. The problem with oversampling is it affects the main channel
so we can't easily associate both an averaged value  and a raw one like you have here.

Quoted 5 lines

Given lots of other changes requested and early in this kernel cycle, perhaps it will make sense to revisit
this question at a later version of this patch (or maybe just leave it alone as too hard
to do better!)

Here I was trying to map the IIO ABI to the PAC internal registers. We have some hardware registers that will do an average for us. Usually with oversampling someone could increase the bit number of the measurement, the average is just the sum of the numbers divided by how many numbers are and it can eliminate some noise out of the measurements.

Here the device has registers that will keep the current values (and will calculate the power based on those values) and also some average measurements registers (keeping the same precision) that could help the user to identifies trends.

Quoted 15 lines
Quoted 6 lines

+             ctrl_reg = PAC193X_CRTL_SAMPLE_RATE_SET(chip_info-

Quoted 1 line

crt_samp_spd_bitfield) |

Quoted 8 lines

+                     PAC193X_CHAN_ALERT_EN;
+             ret = pac193x_i2c_write_byte(client, PAC193X_CTRL_REG, ctrl_reg);
+             if (ret) {
+                     dev_err(&client->dev, "%s - can't update sample rate\n", __func__);
+                     chip_info->sample_rate_value = old_samp_rate; +                     mutex_unlock(&chip_info->lock);

Quoted 8 lines

Fun thing about i2c bus write failures. You have no idea what failed. The value might be
the old value or the new one after the failed write... So normally we don't bother too much if our values are
out of sync. and that leads to easier unwinding.  However, if you want to keep this as it
is then that's fine.

I would like to keep it, maybe there are some corner cases when it will help debugging (good to know in case of intermittent issues)

Quoted 15 lines
Quoted 15 lines
drivers/iio/adc/pac193x.c new file +3 -0
+static void pac193x_work_periodic_rfsh(struct work_struct *work)
+{
+     struct pac193x_chip_info *chip_info =

to_pac193x_chip_info(work);
+
+     /* do a REFRESH, then read */
+     pac193x_reg_snapshot(chip_info, true, false, PAC193X_MIN_UPDATE_WAIT_TIME_MS);

drivers/iio/adc/pac193x.c new file +5 -0
+}
+
+static void pac193x_read_reg_timeout(struct timer_list *t)
+{
+     struct pac193x_chip_info *chip_info = from_timer(chip_info,

t, tmr_forced_update);
+

Quoted 9 lines
Quoted 6 lines

+     mod_timer(&chip_info->tmr_forced_update,
+               jiffies + msecs_to_jiffies(PAC193X_MAX_RFSH_LIMIT_MS));
+
+     /* schedule the periodic reading from the chip */ +     queue_work(chip_info->wq_chip, &chip_info->work_chip_rfsh);

Why not queue_delayed_work()?

I will need to look deeper.

Quoted 61 lines
Quoted 61 lines

+                                                  int revision, +                                                  int function)

drivers/iio/adc/pac193x.c new file +26 -0
+{
+     acpi_status status;
+     struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+     union acpi_object args[PAC193X_ACPI_ARG_COUNT];
+     struct acpi_object_list args_list;
+     uuid_t uuid;
+
+     uuid_parse(PAC193X_DSM_UUID, &uuid);
+
+     args[0].type = ACPI_TYPE_BUFFER;
+     args[0].buffer.length = sizeof(struct pac193x_uuid_format);
+     args[0].buffer.pointer = (u8 *)&uuid;
+
+     args[1].type = ACPI_TYPE_INTEGER;
+     args[1].integer.value = revision;
+
+     args[2].type = ACPI_TYPE_INTEGER;
+     args[2].integer.value = function;
+
+     args[3].type = ACPI_TYPE_PACKAGE;
+     args[3].package.count = 0;
+
+     args_list.count = PAC193X_ACPI_ARG_COUNT;
+     args_list.pointer = &args[0];
+
+     status = acpi_evaluate_object(handle, "_DSM", &args_list,

&buffer);

drivers/iio/adc/pac193x.c new file +22 -0
+
+     if (ACPI_FAILURE(status)) {
+             kfree(buffer.pointer);
+             return NULL;
+     }
+
+     return buffer.pointer;
+}
+
+static char *pac193x_acpi_get_acpi_match_entry(acpi_handle handle)
+{
+     acpi_status status;
+     union acpi_object *name_object;
+     struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+
+     status = acpi_evaluate_object(handle, "_HID", NULL, &buffer);
+     name_object = buffer.pointer;
+
+     return name_object->string.pointer;
+}
+
+static const char *pac193x_match_acpi_device(struct i2c_client

*client,
+                                          struct pac193x_chip_info *chip_info)
+{
+     char *name;
+     acpi_handle handle;

Quoted 9 lines
Quoted 4 lines

+     union acpi_object *rez;
+     unsigned short bi_dir_mask;
+     int idx, i;
+

Any docs for the ACPI stuff?
It looks plausible, but an example DSDT blob would make it easier to review.

Here we have:
https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC193X-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf

Quoted 35 lines
Quoted 12 lines

+     /*
+      * count how many channels are enabled and store +      * this information within the driver data +      */
+     cnt = 0;
+     chip_info->chip_reg_data.num_enabled_channels = 0; +     while (cnt < chip_info->phys_channels) { +             if (chip_info->active_channels[cnt]) +                     chip_info-

Quoted 1 line

chip_reg_data.num_enabled_channels++;

Quoted 9 lines

+             cnt++;
+     }
+
+     /*
+      * read whatever information was gathered before the driver was loaded
+      * establish which channels are enabled/disabled and then establish the
+      * information retrieval mode (using SKIP or no).

It's unusual to have a driver carry on with existing values.  Much more common
to either have it reset the device (to known default state) or write all the
registers if such a reset doesn't exist.

One reason for that flow is that we really don't trust other software :)
Another is that often we don't know if the device had any power until we turned it
on.  So it's much easier to review code where we control all the state.

Quoted 6 lines

If there is a strong reason for carrying on with existing settings please add
comments here and something to the patch description to point out this unusual
behaviour.

There are some customers asking to measure the total power exchanged from the power-up of the system (like if you are spending a lot of time into the bootloader and drain the battery).

Quoted 27 lines
Quoted 23 lines

shunt_value_store, 0);
+static IIO_DEVICE_ATTR(shunt_value_2, 0644, shunt_value_show, shunt_value_store, 0);
+static IIO_DEVICE_ATTR(shunt_value_3, 0644, shunt_value_show, shunt_value_store, 0);
+static IIO_DEVICE_ATTR(shunt_value_4, 0644, shunt_value_show, shunt_value_store, 0);

(quoted diff) +2 -0
+
+static IIO_DEVICE_ATTR(channel_name_1, 0444, channel_name_show,

NULL, 0);
+static IIO_DEVICE_ATTR(channel_name_2, 0444, channel_name_show, NULL, 0);
+static IIO_DEVICE_ATTR(channel_name_3, 0444, channel_name_show, NULL, 0);
+static IIO_DEVICE_ATTR(channel_name_4, 0444, channel_name_show, NULL, 0);

(quoted diff) +2 -0
+
+static IIO_DEVICE_ATTR(reset_accumulators, 0200, NULL,

reset_accumulators_store, 0);

drivers/iio/adc/pac193x.c line 1819

Quoted 3 lines
drivers/iio/adc/pac193x.c new file +3 -0
+
+static struct attribute *pac193x_all_attributes[] = {
+     PAC193X_DEV_ATTR(shunt_value_1),
Quoted 8 lines

These all need ABI documentation so that we can easily review what they do.
Documenation/ABI/testing/sysfs-bus-iio-pac1931
Note that if they overlap with ABI used elsewhere we may need to move it to a more
generic place (there can't be two lots of docs for the same ABI element)

I will add comments into the code. The "shunt_value_show" will print the shunt value used to calculate current and power.
The "channel_name_show" is the name of the device channel used to easily identify what we are measuring (V_DDR, V_GPU, V_IO, etc) and it's an "umbrella" for multiple IIO measurements (voltage, current and power)

"reset_accumulators" will help the user to reinitialize the power on all channels.

Quoted 19 lines

drivers/iio/adc/pac193x.c line 1911

Quoted 9 lines

+     if (!indio_dev) {
+             dev_err_probe(&indio_dev->dev, PTR_ERR(indio_dev), +                           "Can't allocate iio device\n"); +             return -ENOMEM;
+     }

drivers/iio/adc/pac193x.c new file +4 -0
+
+     chip_info = iio_priv(indio_dev);
+
+     i2c_set_clientdata(client, indio_dev);

Assuming you follow suggestion to go fully devm managed handling of remove, I don't htink
you will need this.

Quoted 4 lines

+     chip_info->client = client;
+
+     memset(&chip_info->chip_reg_data, 0, sizeof(chip_info-

Quoted 1 line

chip_reg_data));

Quoted 7 lines

The iio_priv space is allocated with kzalloc so no need to zero here.

Quoted 3 lines

+
+     if (ACPI_HANDLE(&client->dev)) {
+             pac193x_get_variant(chip_info);

Why is this ACPI specific?

Not really. I will change it to work in both cases.

Quoted 21 lines

If you can query the variant from the hardware, that is the right thing to use
for all registration types.  It can be helpful to call out if there is
a mismatch though with a warning print, so comparing the result of the i2c call with the data is fine.

Quoted 13 lines

+     } else {
+             dev_id = id->driver_data;

drivers/iio/adc/pac193x.c new file +5 -0
+
+             /* store the type of chip */
+             chip_info->chip_variant = dev_id;
+
+             /* get the maximum number of channels for the given

chip id */
+             chip_info->phys_channels =
pac193x_chip_config[dev_id].phys_channels;
+     }
+
+     /*

Quoted 8 lines
Quoted 2 lines

+      * load default settings - all channels disabled, +      * uni directional flow, default shunt values

The concept of a default shunt value bothers me a little if this is an external resistor.  How is there in any real sense a default? Can we just fail if it's not provided - or wrap the default up for just ACPI case where I guess there might be firmware that doesn't specify it?

I'm doing some math with that resistor later in code and I was just making sure that I will not divide by 0, but indeed I could just exit in case the value is not correctly set.

Quoted 1 line

Thanks,
Marius

maintainer
#16 lore raw

Re: [PATCH v1 2/2] iio: adc: adding support for pac193x

Quoted 17 lines
Quoted 5 lines
Quoted 11 lines

drivers/iio/adc/pac193x.c line 672

Quoted 7 lines
drivers/iio/adc/pac193x.c new file +7 -0
+
+     len += sprintf(buf, "%u\n", chip_info->shunts[target]);
+
+     return len;
+}
+
+static ssize_t channel_name_show(struct device *dev,  

Looks like per channel label support provided by the read_label iio_info callback.

Quoted 2 lines

I was trying to use the read_label iio_info callback but I end it up into a sysfs error related to duplicated labels.

That's interesting. Can you provide more info on that. I'd like to understand why that's happening to you.

Quoted 8 lines

Also I'm not sure if this read_label will help me in the end. What I was aiming to obtain here with the "channel_name_show" and the custom IIO attribute was to have "an umbrella" name/label for multiple IIO channels. For example on physical "channel 1" we will measure the voltage, the current and the power because all of those IIO channels will point to one board power supply (like VDD, VIO, V_GPU, V_DDR, etc).

That should be possible with read_label as it's free form text. Perhaps I'm missing why that doesn't provide enough information for what you need.

Quoted 26 lines
Quoted 26 lines
Quoted 16 lines

+                              struct device_attribute *attr, +                              char *buf)
+{
+     struct iio_dev *indio_dev = dev_to_iio_dev(dev); +     struct pac193x_chip_info *chip_info = iio_priv(indio_dev); +     int len = 0;
+     int target = (int)(attr->attr.name[strlen(attr->attr.name) - 1] - '0') - 1;
+
+     len += sprintf(buf, "%s\n", chip_info-

Quoted 1 line

channel_names[target]);

drivers/iio/adc/pac193x.c line 686

Quoted 5 lines
drivers/iio/adc/pac193x.c new file +5 -0
+
+     return len;
+}
+
+static ssize_t shunt_value_store(struct device *dev,  

Shunt values aren't normally dynamic.  Why do you need to write it from
userspace? I'd expect that to come from firmware.

Quoted 8 lines
Quoted 2 lines

If it's needed the ext_info framework might be a better fit than direct implementation of the attribute.

Yes, usually the shunt aren't normally dynamic, but what I'm aiming to get here is to let the user to overwrite the value after a calibration of the system. This will improve the accuracy of the reading even in the case the shunts are not of high precision ones.

I'll go with maybe on this. Perhaps not a feature for the initial version of the driver, but one that is better to discuss in a follow up thread along with details of the expected calibration process etc.

Quoted 32 lines
Quoted 22 lines
Quoted 5 lines

+                     case PAC193X_VPOWER_4_ADDR:
+                             *val = chip_info-

Quoted 1 line

chip_reg_data.vpower[channel];

Quoted 16 lines

+                             return IIO_VAL_INT;
+                     default:
+                             return -EINVAL;
+                     }
+                     break;
+             case IIO_ENERGY:
+                     switch (chan->address) {
+                     case PAC193X_VPOWER_ACC_1_ADDR:
+                     case PAC193X_VPOWER_ACC_2_ADDR:
+                     case PAC193X_VPOWER_ACC_3_ADDR:
+                     case PAC193X_VPOWER_ACC_4_ADDR:
+                             /*
+                              * expresses the 64 bit energy value as a 32 bit integer part and
+                              * 32 bits (representing 8 digits) fractional value

Quoted 3 lines

I'm lost.  So it's a 64 bit value?
We have IIO_VAL_INT_64. Does that not work here?

Yes, we have the IIO_VAL_INT_64 and it will work here. The "issue" was that the IIO_VAL_INT_64 is not available before kernel v6.0 and we already release something to customers supporting Linux kernel v4.7 and v5.15. Also our latest release of Linux4Sam is based on the Linux kernel v5.15 (we are working to have a release based on v6.1).

Quoted 3 lines

Maybe I'm totally wrong here but my idea was to have something "more generic" that could be easily backported to an older kernel and to change this to IIO_VAL_INT_64 later.

As a general rule, we don't do this because it stops us from ever improving things like this. If you need IIO_VAL_INT_64 on an older kernel then backport that as well. Or provide a backported version of your driver doing something different from upstream version.

Quoted 49 lines
Quoted 34 lines
Quoted 16 lines

+                              */
+                             curr_energy = chip_info-

Quoted 1 line

chip_reg_data.energy_sec_acc[channel];

+                             int_part = div_s64_rem(curr_energy, 100000000, &rem);
+
+                             /* rescale reminder to be printed as "nano" value */
+                             rem = rem * 10;
+
+                             *val = int_part;
+                             *val2 = rem;
+                             return IIO_VAL_INT_PLUS_NANO; +                     default:

Quoted 8 lines

+                             return -EINVAL;
+                     }
+                     break;
+             default:
+                     return -EINVAL;
+             }
+             break;
+     case IIO_CHAN_INFO_AVERAGE_RAW:

I'd forgotten we had this ABI and had to look up the docs.   Hmm. It's a bit clunky but I guess
still useful in this case.  Longer term we should think about how to describe that and how it
is different from oversampling. The problem with oversampling is it affects the main channel
so we can't easily associate both an averaged value  and a raw one like you have here.

Quoted 5 lines

Given lots of other changes requested and early in this kernel cycle, perhaps it will make sense to revisit
this question at a later version of this patch (or maybe just leave it alone as too hard
to do better!)

Here I was trying to map the IIO ABI to the PAC internal registers. We have some hardware registers that will do an average for us. Usually with oversampling someone could increase the bit number of the measurement, the average is just the sum of the numbers divided by how many numbers are and it can eliminate some noise out of the measurements.

Quoted 4 lines

Here the device has registers that will keep the current values (and will calculate the power based on those values) and also some average measurements registers (keeping the same precision) that could help the user to identifies trends.

Understood. It was similar hardware that lead to this ABI existing in the first place. Whilst you are right that the purpose is somewhat different. In some cases oversampling in hardware doesn't increase the bit depth and ends up looking much the same as an average.

Quoted 27 lines
Quoted 17 lines
Quoted 8 lines

+             ctrl_reg = PAC193X_CRTL_SAMPLE_RATE_SET(chip_info-

Quoted 1 line

crt_samp_spd_bitfield) |

Quoted 8 lines

+                     PAC193X_CHAN_ALERT_EN;
+             ret = pac193x_i2c_write_byte(client, PAC193X_CTRL_REG, ctrl_reg);
+             if (ret) {
+                     dev_err(&client->dev, "%s - can't update sample rate\n", __func__);
+                     chip_info->sample_rate_value = old_samp_rate; +                     mutex_unlock(&chip_info->lock);

Quoted 8 lines

Fun thing about i2c bus write failures. You have no idea what failed. The value might be
the old value or the new one after the failed write... So normally we don't bother too much if our values are
out of sync. and that leads to easier unwinding.  However, if you want to keep this as it
is then that's fine.

Quoted 2 lines

I would like to keep it, maybe there are some corner cases when it will help debugging (good to know in case of intermittent issues)

I'm fine seeing the error print, but unless you succesfully hammer back in the old value with a new write, you have no guarantee of what the device thinks the value is.

Quoted 70 lines
Quoted 60 lines
Quoted 60 lines

+                                                  int revision, +                                                  int function)

drivers/iio/adc/pac193x.c new file +26 -0
+{
+     acpi_status status;
+     struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+     union acpi_object args[PAC193X_ACPI_ARG_COUNT];
+     struct acpi_object_list args_list;
+     uuid_t uuid;
+
+     uuid_parse(PAC193X_DSM_UUID, &uuid);
+
+     args[0].type = ACPI_TYPE_BUFFER;
+     args[0].buffer.length = sizeof(struct pac193x_uuid_format);
+     args[0].buffer.pointer = (u8 *)&uuid;
+
+     args[1].type = ACPI_TYPE_INTEGER;
+     args[1].integer.value = revision;
+
+     args[2].type = ACPI_TYPE_INTEGER;
+     args[2].integer.value = function;
+
+     args[3].type = ACPI_TYPE_PACKAGE;
+     args[3].package.count = 0;
+
+     args_list.count = PAC193X_ACPI_ARG_COUNT;
+     args_list.pointer = &args[0];
+
+     status = acpi_evaluate_object(handle, "_DSM", &args_list,

&buffer);

drivers/iio/adc/pac193x.c new file +22 -0
+
+     if (ACPI_FAILURE(status)) {
+             kfree(buffer.pointer);
+             return NULL;
+     }
+
+     return buffer.pointer;
+}
+
+static char *pac193x_acpi_get_acpi_match_entry(acpi_handle handle)
+{
+     acpi_status status;
+     union acpi_object *name_object;
+     struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+
+     status = acpi_evaluate_object(handle, "_HID", NULL, &buffer);
+     name_object = buffer.pointer;
+
+     return name_object->string.pointer;
+}
+
+static const char *pac193x_match_acpi_device(struct i2c_client

*client,
+                                          struct pac193x_chip_info *chip_info)
+{
+     char *name;
+     acpi_handle handle;

Quoted 9 lines
Quoted 4 lines

+     union acpi_object *rez;
+     unsigned short bi_dir_mask;
+     int idx, i;
+

Any docs for the ACPI stuff?
It looks plausible, but an example DSDT blob would make it easier to review.

Quoted 2 lines

Here we have:
https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC193X-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf

Yikes that's a long file name ;) Exactly what I was looking for. Thanks.

Please add that link to the driver somewhere. + ignore the inevitable checkpatch warning on line length.

Quoted 44 lines
Quoted 37 lines
Quoted 14 lines

+     /*
+      * count how many channels are enabled and store +      * this information within the driver data +      */
+     cnt = 0;
+     chip_info->chip_reg_data.num_enabled_channels = 0; +     while (cnt < chip_info->phys_channels) { +             if (chip_info->active_channels[cnt]) +                     chip_info-

Quoted 1 line

chip_reg_data.num_enabled_channels++;

Quoted 9 lines

+             cnt++;
+     }
+
+     /*
+      * read whatever information was gathered before the driver was loaded
+      * establish which channels are enabled/disabled and then establish the
+      * information retrieval mode (using SKIP or no).

It's unusual to have a driver carry on with existing values.  Much more common
to either have it reset the device (to known default state) or write all the
registers if such a reset doesn't exist.

One reason for that flow is that we really don't trust other software :)
Another is that often we don't know if the device had any power until we turned it
on.  So it's much easier to review code where we control all the state.

Quoted 6 lines

If there is a strong reason for carrying on with existing settings please add
comments here and something to the patch description to point out this unusual
behaviour.

Quoted 3 lines

There are some customers asking to measure the total power exchanged from the power-up of the system (like if you are spending a lot of time into the bootloader and drain the battery).

Ok. Add a comment on that.

Quoted 40 lines
Quoted 31 lines
Quoted 27 lines

shunt_value_store, 0);
+static IIO_DEVICE_ATTR(shunt_value_2, 0644, shunt_value_show, shunt_value_store, 0);
+static IIO_DEVICE_ATTR(shunt_value_3, 0644, shunt_value_show, shunt_value_store, 0);
+static IIO_DEVICE_ATTR(shunt_value_4, 0644, shunt_value_show, shunt_value_store, 0);

(quoted diff) +2 -0
+
+static IIO_DEVICE_ATTR(channel_name_1, 0444, channel_name_show,

NULL, 0);
+static IIO_DEVICE_ATTR(channel_name_2, 0444, channel_name_show, NULL, 0);
+static IIO_DEVICE_ATTR(channel_name_3, 0444, channel_name_show, NULL, 0);
+static IIO_DEVICE_ATTR(channel_name_4, 0444, channel_name_show, NULL, 0);

(quoted diff) +2 -0
+
+static IIO_DEVICE_ATTR(reset_accumulators, 0200, NULL,

reset_accumulators_store, 0);

drivers/iio/adc/pac193x.c line 1819

Quoted 3 lines
drivers/iio/adc/pac193x.c new file +3 -0
+
+static struct attribute *pac193x_all_attributes[] = {
+     PAC193X_DEV_ATTR(shunt_value_1),  
Quoted 8 lines

These all need ABI documentation so that we can easily review what they do.
Documenation/ABI/testing/sysfs-bus-iio-pac1931
Note that if they overlap with ABI used elsewhere we may need to move it to a more
generic place (there can't be two lots of docs for the same ABI element)

Quoted 2 lines

I will add comments into the code. The "shunt_value_show" will print the shunt value used to calculate current and power.

I'd prefer handling shut calibration as a future patch because it needs some thought and discussion + may delay the rest of the driver. Without that ability to tweak it I'm not sure it provides value to be able to read it now.

Quoted 4 lines

The "channel_name_show" is the name of the device channel used to easily identify what we are measuring (V_DDR, V_GPU, V_IO, etc) and it's an "umbrella" for multiple IIO measurements (voltage, current and power)

As above. I'd like to think a bit more on this one.

Quoted 3 lines

"reset_accumulators" will help the user to reinitialize the power on all channels.

This sounds like the rare case where the ENABLE IIO ABI may fit. We use that for things like step counters that also accumulate over time. If we can use that, I'd prefer it to custom ABI.

Quoted 61 lines
Quoted 21 lines

drivers/iio/adc/pac193x.c line 1911

Quoted 11 lines

+     if (!indio_dev) {
+             dev_err_probe(&indio_dev->dev, PTR_ERR(indio_dev), +                           "Can't allocate iio device\n"); +             return -ENOMEM;
+     }

drivers/iio/adc/pac193x.c new file +4 -0
+
+     chip_info = iio_priv(indio_dev);
+
+     i2c_set_clientdata(client, indio_dev);  

Assuming you follow suggestion to go fully devm managed handling of remove, I don't htink
you will need this.

Quoted 4 lines

+     chip_info->client = client;
+
+     memset(&chip_info->chip_reg_data, 0, sizeof(chip_info-

Quoted 1 line

chip_reg_data));

Quoted 7 lines

The iio_priv space is allocated with kzalloc so no need to zero here.

Quoted 3 lines

+
+     if (ACPI_HANDLE(&client->dev)) {
+             pac193x_get_variant(chip_info);

Why is this ACPI specific?

Not really. I will change it to work in both cases.

Quoted 21 lines

If you can query the variant from the hardware, that is the right thing to use
for all registration types.  It can be helpful to call out if there is
a mismatch though with a warning print, so comparing the result of the i2c call with the data is fine.

Quoted 13 lines

+     } else {
+             dev_id = id->driver_data;

drivers/iio/adc/pac193x.c new file +5 -0
+
+             /* store the type of chip */
+             chip_info->chip_variant = dev_id;
+
+             /* get the maximum number of channels for the given

chip id */
+             chip_info->phys_channels =
pac193x_chip_config[dev_id].phys_channels;
+     }
+
+     /*

Quoted 8 lines
Quoted 2 lines

+      * load default settings - all channels disabled, +      * uni directional flow, default shunt values

The concept of a default shunt value bothers me a little if this is an external resistor.  How is there in any real sense a default? Can we just fail if it's not provided - or wrap the default up for just ACPI case where I guess there might be firmware that doesn't specify it?

Quoted 3 lines

I'm doing some math with that resistor later in code and I was just making sure that I will not divide by 0, but indeed I could just exit in case the value is not correctly set.

Exit is best thing to do I think.

Jonathan

Quoted 6 lines
Quoted 2 lines

Thanks,
Marius

#17 lore raw

Re: [PATCH v1 2/2] iio: adc: adding support for pac193x

Quoted 19 lines · Jonathan Cameron

EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe

Quoted 16 lines
Quoted 15 lines

drivers/iio/adc/pac193x.c line 672

Quoted 11 lines
drivers/iio/adc/pac193x.c new file +7 -0
+
+     len += sprintf(buf, "%u\n", chip_info->shunts[target]);
+
+     return len;
+}
+
+static ssize_t channel_name_show(struct device *dev,

Looks like per channel label support provided by the read_label iio_info callback.

Quoted 7 lines
Quoted 3 lines

I was trying to use the read_label iio_info callback but I end it up
into a sysfs error related to duplicated labels.

That's interesting.  Can you provide more info on that. I'd like to understand why that's happening to you.

I have add the ".read_label" and the function asociated with it to the driver but here is the error:

root@sama5d27-wlsom1-ek-sd:~/work/pac193x# insmod pac1934.ko
pac193x 1-001c: :pac193x_prep_iio_channels: Channel 2 active
pac193x 1-001c: :pac193x_prep_iio_channels: Channel 3 active
pac193x 1-001c: :pac193x_prep_iio_channels: Channel 4 active
iio iio:device1: tried to double register : in_voltage2_label
pac193x 1-001c: Failed to register sysfs interfaces
iio iio:device1: error -EBUSY: Can't register IIO device
pac193x: probe of 1-001c failed with error -16

Here are the list of files in case I don't use read_label (only physical channels 2 to 4 are available):

root@sama5d27-wlsom1-ek-sd:~/work/pac193x# ls
/sys/bus/iio/devices/iio\:device1/
channel_name_2        in_current4_mean_raw  in_power2_raw         
in_voltage2_scale     power
channel_name_3        in_current4_raw       in_power2_scale       
in_voltage3_mean_raw  reset_accumulators
channel_name_4        in_current4_scale     in_power3_raw         
in_voltage3_raw       sampling_frequency_available
in_current2_mean_raw  in_energy2_raw        in_power3_scale       
in_voltage3_scale     shunt_value_2
in_current2_raw       in_energy2_scale      in_power4_raw         
in_voltage4_mean_raw  shunt_value_3
in_current2_scale     in_energy3_raw        in_power4_scale       
in_voltage4_raw       shunt_value_4
in_current3_mean_raw  in_energy3_scale      in_sampling_frequency 
in_voltage4_scale     subsystem
in_current3_raw       in_energy4_raw        in_voltage2_mean_raw   name
uevent
in_current3_scale     in_energy4_scale      in_voltage2_raw       
of_node               waiting_for_supplier
Quoted 12 lines
Quoted 11 lines

Also I'm not sure if this read_label will help me in the end. What I
was aiming to obtain here with the "channel_name_show" and the custom
IIO attribute was to have "an umbrella" name/label for multiple IIO channels. For example on physical "channel 1" we will measure the voltage, the current and the power because all of those IIO channels
will point to one board power supply (like VDD, VIO, V_GPU, V_DDR, etc).

Quoted 6 lines

That should be possible with read_label as it's free form text. Perhaps I'm missing why that doesn't provide enough information for what
you need.

Quoted 1 line

It seems it does more that I need. My concern is that the read_label will do a label for all available channels (like _raw or scale_ will do) and I want to have just up to a maximum of 4 labels each one will cover the multiple entries (e.g.: channel_name_2 will be a umbrela "label" for:

in_power2_raw         in_voltage2_scale  in_power2_scale        
in_current2_mean_raw  in_energy2_raw     shunt_value_2
in_current2_raw       in_energy2_scale   in_current2_scale     
in_voltage2_mean_raw  in_voltage2_raw
Quoted 40 lines
Quoted 32 lines
Quoted 28 lines
Quoted 17 lines

+                              struct device_attribute *attr, +                              char *buf)
+{
+     struct iio_dev *indio_dev = dev_to_iio_dev(dev); +     struct pac193x_chip_info *chip_info = iio_priv(indio_dev);
+     int len = 0;
+     int target = (int)(attr->attr.name[strlen(attr-

Quoted 1 line

attr.name) -

1] - '0') - 1;
+
+     len += sprintf(buf, "%s\n", chip_info-

Quoted 1 line

channel_names[target]);

drivers/iio/adc/pac193x.c line 686

Quoted 5 lines
drivers/iio/adc/pac193x.c new file +5 -0
+
+     return len;
+}
+
+static ssize_t shunt_value_store(struct device *dev,

Shunt values aren't normally dynamic.  Why do you need to write it
from
userspace? I'd expect that to come from firmware.

Quoted 2 lines

If it's needed the ext_info framework might be a better fit than direct implementation of the attribute.

Quoted 7 lines

Yes, usually the shunt aren't normally dynamic, but what I'm aiming to
get here is to let the user to overwrite the value after a calibration
of the system. This will improve the accuracy of the reading even in
the case the shunts are not of high precision ones.

Quoted 4 lines

I'll go with maybe on this.  Perhaps not a feature for the initial version of the driver, but one that is better to discuss in a follow up thread along with details of the expected calibration process etc.

Quoted 1 line

Calibration process could be easly be done. Just think that we have a linux board that measure some dinamic load that uses a connector. The load could be easily changed by the user with a "calibrated" load, read the measurement do the math expected versus read values and update the shunt value. Those values needs to be stored by the user somewhere to be reused after a reboot. After the board is calibrated a "normal" load could pluged in (one example is to monitor the charge/discharge current of a battery).

Quoted 52 lines
Quoted 42 lines
Quoted 32 lines
Quoted 27 lines

shunt_value_store, 0);
+static IIO_DEVICE_ATTR(shunt_value_2, 0644, shunt_value_show, shunt_value_store, 0);
+static IIO_DEVICE_ATTR(shunt_value_3, 0644, shunt_value_show, shunt_value_store, 0);
+static IIO_DEVICE_ATTR(shunt_value_4, 0644, shunt_value_show, shunt_value_store, 0);

(quoted diff) +2 -0
+
+static IIO_DEVICE_ATTR(channel_name_1, 0444,

channel_name_show,
NULL, 0);
+static IIO_DEVICE_ATTR(channel_name_2, 0444,
channel_name_show,
NULL, 0);
+static IIO_DEVICE_ATTR(channel_name_3, 0444,
channel_name_show,
NULL, 0);
+static IIO_DEVICE_ATTR(channel_name_4, 0444,
channel_name_show,
NULL, 0);

(quoted diff) +2 -0
+
+static IIO_DEVICE_ATTR(reset_accumulators, 0200, NULL,

reset_accumulators_store, 0);

drivers/iio/adc/pac193x.c line 1819

Quoted 3 lines
drivers/iio/adc/pac193x.c new file +3 -0
+
+static struct attribute *pac193x_all_attributes[] = {
+     PAC193X_DEV_ATTR(shunt_value_1),

These all need ABI documentation so that we can easily review

Quoted 9 lines

what
they do. Documenation/ABI/testing/sysfs-bus-iio-pac1931
Note that if they overlap with ABI used elsewhere we may need to move
it to a more
generic place (there can't be two lots of docs for the same ABI element)

Quoted 3 lines

I will add comments into the code. The "shunt_value_show" will print
the shunt value used to calculate current and power.

I'd prefer handling shut calibration as a future patch because it needs
some thought and discussion + may delay the rest of the driver. Without that
ability to tweak it I'm not sure it provides value to be able to read it now.

Quoted 8 lines
Quoted 5 lines

The "channel_name_show" is the name of the device channel used to easily identify what we are measuring (V_DDR, V_GPU, V_IO, etc) and it's an "umbrella" for multiple IIO measurements (voltage, current and
power)

As above. I'd like to think a bit more on this one.

OK.

Quoted 78 lines
Quoted 4 lines

"reset_accumulators" will help the user to reinitialize the power on
all channels.

This sounds like the rare case where the ENABLE IIO ABI may fit. We use that for things like step counters that also accumulate over time.  If we can use that, I'd prefer it to custom ABI.

Quoted 69 lines
Quoted 23 lines

drivers/iio/adc/pac193x.c line 1911

Quoted 12 lines

+     if (!indio_dev) {
+             dev_err_probe(&indio_dev->dev, PTR_ERR(indio_dev),
+                           "Can't allocate iio device\n"); +             return -ENOMEM;
+     }

drivers/iio/adc/pac193x.c new file +4 -0
+
+     chip_info = iio_priv(indio_dev);
+
+     i2c_set_clientdata(client, indio_dev);

Assuming you follow suggestion to go fully devm managed handling of
remove, I don't htink
you will need this.

Quoted 4 lines

+     chip_info->client = client;
+
+     memset(&chip_info->chip_reg_data, 0, sizeof(chip_info-

Quoted 1 line

chip_reg_data));

Quoted 8 lines

The iio_priv space is allocated with kzalloc so no need to zero here.

Quoted 3 lines

+
+     if (ACPI_HANDLE(&client->dev)) {
+             pac193x_get_variant(chip_info);

Why is this ACPI specific?

Not really. I will change it to work in both cases.

Quoted 34 lines

If you can query the variant from the hardware, that is the right thing to use
for all registration types.  It can be helpful to call out if there
is
a mismatch though with a warning print, so comparing the result of
the i2c call with the data is fine.

Quoted 8 lines

+     } else {
+             dev_id = id->driver_data;

drivers/iio/adc/pac193x.c new file +5 -0
+
+             /* store the type of chip */
+             chip_info->chip_variant = dev_id;
+
+             /* get the maximum number of channels for the

given

Quoted 8 lines

chip id */
+             chip_info->phys_channels = pac193x_chip_config[dev_id].phys_channels;
+     }
+
+     /*
+      * load default settings - all channels disabled, +      * uni directional flow, default shunt values

The concept of a default shunt value bothers me a little if this is
an external resistor.  How is there in any real sense a default? Can we just fail if it's not provided - or wrap the default up for
just ACPI case where I guess there might be firmware that doesn't specify it?

Quoted 8 lines
Quoted 4 lines

I'm doing some math with that resistor later in code and I was just making sure that I will not divide by 0, but indeed I could just exit
in case the value is not correctly set.

Exit is best thing to do I think.

OK.

Quoted 1 line
Quoted 1 line

Thanks,
Marius

maintainer
#18 lore raw

Re: [PATCH v1 2/2] iio: adc: adding support for pac193x

Quoted 31 lines · On Thu, 23 Mar 2023 15:15:18 +0000 <[email protected]> wrote:
Quoted 19 lines · Jonathan Cameron

EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe

Quoted 16 lines
Quoted 15 lines

drivers/iio/adc/pac193x.c line 672

Quoted 11 lines
drivers/iio/adc/pac193x.c new file +7 -0
+
+     len += sprintf(buf, "%u\n", chip_info->shunts[target]);
+
+     return len;
+}
+
+static ssize_t channel_name_show(struct device *dev,  

Looks like per channel label support provided by the read_label iio_info callback.

Quoted 7 lines
Quoted 3 lines

I was trying to use the read_label iio_info callback but I end it up
into a sysfs error related to duplicated labels.

That's interesting.  Can you provide more info on that. I'd like to understand why that's happening to you.

I have add the ".read_label" and the function asociated with it to the driver but here is the error:

Quoted 9 lines
root@sama5d27-wlsom1-ek-sd:~/work/pac193x# insmod pac1934.ko
pac193x 1-001c: :pac193x_prep_iio_channels: Channel 2 active
pac193x 1-001c: :pac193x_prep_iio_channels: Channel 3 active
pac193x 1-001c: :pac193x_prep_iio_channels: Channel 4 active
iio iio:device1: tried to double register : in_voltage2_label
pac193x 1-001c: Failed to register sysfs interfaces
iio iio:device1: error -EBUSY: Can't register IIO device
pac193x: probe of 1-001c failed with error -16

There can only be one registered iio_chan_spec entry for a given group of direction, type, channel, modifier (channel and modifier can be optional). This comes from inherent design restrictions of the events. Now I think what has happened here is that you are registering both _VBUS_CHANNEL and _VBUS_AVG channel with the same index. You can't do that. Whilst you don't currently have events, if they come along later there will be no way to distinguish those two.

So either roll them into one iio_chan_spec, or give them different index values.

Quoted 52 lines

Here are the list of files in case I don't use read_label (only physical channels 2 to 4 are available):

root@sama5d27-wlsom1-ek-sd:~/work/pac193x# ls
/sys/bus/iio/devices/iio\:device1/
channel_name_2        in_current4_mean_raw  in_power2_raw         
in_voltage2_scale     power
channel_name_3        in_current4_raw       in_power2_scale       
in_voltage3_mean_raw  reset_accumulators
channel_name_4        in_current4_scale     in_power3_raw         
in_voltage3_raw       sampling_frequency_available
in_current2_mean_raw  in_energy2_raw        in_power3_scale       
in_voltage3_scale     shunt_value_2
in_current2_raw       in_energy2_scale      in_power4_raw         
in_voltage4_mean_raw  shunt_value_3
in_current2_scale     in_energy3_raw        in_power4_scale       
in_voltage4_raw       shunt_value_4
in_current3_mean_raw  in_energy3_scale      in_sampling_frequency 
in_voltage4_scale     subsystem
in_current3_raw       in_energy4_raw        in_voltage2_mean_raw   name
uevent
in_current3_scale     in_energy4_scale      in_voltage2_raw       
of_node               waiting_for_supplier
Quoted 12 lines
Quoted 11 lines

Also I'm not sure if this read_label will help me in the end. What I
was aiming to obtain here with the "channel_name_show" and the custom
IIO attribute was to have "an umbrella" name/label for multiple IIO channels. For example on physical "channel 1" we will measure the voltage, the current and the power because all of those IIO channels
will point to one board power supply (like VDD, VIO, V_GPU, V_DDR, etc).

Quoted 6 lines

That should be possible with read_label as it's free form text. Perhaps I'm missing why that doesn't provide enough information for what
you need.

Quoted 1 line

It seems it does more that I need. My concern is that the read_label will do a label for all available channels (like _raw or scale_ will do) and I want to have just up to a maximum of 4 labels each one will cover the multiple entries (e.g.: channel_name_2 will be a umbrela "label" for:

Quoted 4 lines
in_power2_raw         in_voltage2_scale  in_power2_scale        
in_current2_mean_raw  in_energy2_raw     shunt_value_2
in_current2_raw       in_energy2_scale   in_current2_scale     
in_voltage2_mean_raw  in_voltage2_raw

Whilst it might not be ideal, label is standard ABI that normal code is going to know how to use. channel_name_2 is custom ABI that only code written against this one driver will use.

So go with the label version and don't worry about the duplication.

The association you are building between channel indexes is also unique to this driver, so you should not rely on that as generic code will not know that all the things numbered 2 refer to a particular set of inputs.

If each channel has a _label and those match, that association is explicit.

Channel numbers shouldn't really mean anything on their own.

Quoted 46 lines
Quoted 41 lines
Quoted 33 lines
Quoted 29 lines
Quoted 18 lines

+                              struct device_attribute *attr, +                              char *buf)
+{
+     struct iio_dev *indio_dev = dev_to_iio_dev(dev); +     struct pac193x_chip_info *chip_info = iio_priv(indio_dev);
+     int len = 0;
+     int target = (int)(attr->attr.name[strlen(attr-

Quoted 1 line

attr.name) -

1] - '0') - 1;
+
+     len += sprintf(buf, "%s\n", chip_info-

Quoted 1 line

channel_names[target]);

drivers/iio/adc/pac193x.c line 686

Quoted 5 lines
drivers/iio/adc/pac193x.c new file +5 -0
+
+     return len;
+}
+
+static ssize_t shunt_value_store(struct device *dev,  

Shunt values aren't normally dynamic.  Why do you need to write it
from
userspace? I'd expect that to come from firmware.

Quoted 2 lines

If it's needed the ext_info framework might be a better fit than direct implementation of the attribute.

Quoted 7 lines

Yes, usually the shunt aren't normally dynamic, but what I'm aiming to
get here is to let the user to overwrite the value after a calibration
of the system. This will improve the accuracy of the reading even in
the case the shunts are not of high precision ones.

Quoted 4 lines

I'll go with maybe on this.  Perhaps not a feature for the initial version of the driver, but one that is better to discuss in a follow up thread along with details of the expected calibration process etc.

Quoted 1 line
Quoted 8 lines

Calibration process could be easly be done. Just think that we have a linux board that measure some dinamic load that uses a connector. The load could be easily changed by the user with a "calibrated" load, read the measurement do the math expected versus read values and update the shunt value. Those values needs to be stored by the user somewhere to be reused after a reboot. After the board is calibrated a "normal" load could pluged in (one example is to monitor the charge/discharge current of a battery).

OK. I'd still advise postponing control of this. It's unusual and likely to delay / distract from review of the rest of the driver.

Jonathan

maintainer
#19 lore raw
In reply to [email protected]
Quoted 4 lines · On Mon, 20 Feb 2023 14:32:30 +0200 <[email protected]> wrote:

From: Marius Cristea <[email protected]>

Adding support for Microchip PAC193X series of Power Monitor with Accumulator chip family.

This device is at the messy boundary between IIO and HWMON. Perhaps call out the reasons you think IIO is more appropriate in this cover letter.
+ Often a good idea for these borderline parts to cc both mailing lists and maintainers.

Often the conclusion is that it is fine to have these in IIO because we can bridge to hwmon anyway with the iio-hwmon driver.

Quoted 20 lines

Differences related to previous patch:

v1:
- first version comitted to review

Marius Cristea (2):
dt-bindings: iio: adc: adding dt-bindings for PAC193X
iio: adc: adding support for pac193x

 .../bindings/iio/adc/microchip,pac193x.yaml   |  122 +
 MAINTAINERS                                   |    7 +
 drivers/iio/adc/Kconfig                       |   12 +
 drivers/iio/adc/Makefile                      |    1 +
 drivers/iio/adc/pac193x.c                     | 2072 +++++++++++++++++
 5 files changed, 2214 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml
 create mode 100644 drivers/iio/adc/pac193x.c
#20 lore raw

Hi Jonathan,

Please, see my comments below...

Quoted 18 lines · Jonathan Cameron

EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe

Quoted 4 lines · On Mon, 20 Feb 2023 14:32:30 +0200 <[email protected]> wrote:

From: Marius Cristea <[email protected]>

Adding support for Microchip PAC193X series of Power Monitor with Accumulator chip family.

This device is at the messy boundary between IIO and HWMON. Perhaps call out
the reasons you think IIO is more appropriate in this cover letter.
+ Often a good idea for these borderline parts to cc both mailing lists and
maintainers.

Quoted 4 lines

Often the conclusion is that it is fine to have these in IIO because we can
bridge to hwmon anyway with the iio-hwmon driver.

Indeed the driver (the device) is at the boundary between IIO and HWMON. I was thinking to start with a simple driver (this one that is more apropiate to be a HWMON) and add more functionality later (like data buffering that is quite important for example if someone wants to profile power consumtion of the procesor itself, or a pheriperic, or a battery)

Quoted 11 lines
Quoted 11 lines

Differences related to previous patch:

v1:
- first version comitted to review

Marius Cristea (2):
  dt-bindings: iio: adc: adding dt-bindings for PAC193X
  iio: adc: adding support for pac193x

Quoted 12 lines
Quoted 11 lines

 .../bindings/iio/adc/microchip,pac193x.yaml   |  122 + MAINTAINERS                                   |    7 + drivers/iio/adc/Kconfig                       |   12 + drivers/iio/adc/Makefile                      |    1 + drivers/iio/adc/pac193x.c                     | 2072
+++++++++++++++++

 5 files changed, 2214 insertions(+)

 create mode 100644 Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml create mode 100644 drivers/iio/adc/pac193x.c

Thanks,
Marius

maintainer
#21 lore raw
Quoted 29 lines · On Mon, 6 Mar 2023 14:03:52 +0000 <[email protected]> wrote:

Hi Jonathan,

Please, see my comments below...

Quoted 18 lines · Jonathan Cameron

EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe

Quoted 4 lines · On Mon, 20 Feb 2023 14:32:30 +0200 <[email protected]> wrote:

From: Marius Cristea <[email protected]>

Adding support for Microchip PAC193X series of Power Monitor with Accumulator chip family.

This device is at the messy boundary between IIO and HWMON. Perhaps call out
the reasons you think IIO is more appropriate in this cover letter.
+ Often a good idea for these borderline parts to cc both mailing lists and
maintainers.

Quoted 4 lines

Often the conclusion is that it is fine to have these in IIO because we can
bridge to hwmon anyway with the iio-hwmon driver.

Quoted 7 lines

Indeed the driver (the device) is at the boundary between IIO and HWMON. I was thinking to start with a simple driver (this one that is more apropiate to be a HWMON) and add more functionality later (like data buffering that is quite important for example if someone wants to profile power consumtion of the procesor itself, or a pheriperic, or a battery)

OK. List out some of the things you want to do later as the reasoning +CC hmwon maintainers just to be sure. They are usually fine with this sort of reasoning, but it is best to check anyway.

Thanks,

Jonathan

Quoted 28 lines
Quoted 12 lines
Quoted 12 lines

Differences related to previous patch:

v1:
- first version comitted to review

Marius Cristea (2):
  dt-bindings: iio: adc: adding dt-bindings for PAC193X
  iio: adc: adding support for pac193x

Quoted 12 lines
Quoted 11 lines

 .../bindings/iio/adc/microchip,pac193x.yaml   |  122 + MAINTAINERS                                   |    7 + drivers/iio/adc/Kconfig                       |   12 + drivers/iio/adc/Makefile                      |    1 + drivers/iio/adc/pac193x.c                     | 2072
+++++++++++++++++

 5 files changed, 2214 insertions(+)

 create mode 100644 Documentation/devicetree/bindings/iio/adc/microchip,pac193x.yaml create mode 100644 drivers/iio/adc/pac193x.c

Thanks,
Marius