Skip to contentSkip to conversation

Support ROHM BD79124 ADC/GPO

PATCH Superseded Matti Vaittinen 5 participants 21 messages Feb 5, 2025 – Feb 17, 2025

Subsystems (4)

v2 of 10 Superseded Latest is v10 →

lore
Share & tools
lore

Matti Vaittinen

author maintainer
#0 lore raw

v2 cover Support ROHM BD79124 ADC/GPO

Support ROHM BD79124 ADC.

Quite usual stuff. 12-bit, 8-channel ADC with threshold monitoring.

Except that:
- each ADC input pin can be configured as a general purpose output.
- manually starting an ADC conversion and reading the result would require the I2C _master_ to do clock stretching(!) for the duration of the conversion... Let's just say this is not well supported.
- IC supports 'autonomous measurement mode' and storing latest results to the result registers. This mode is used by the driver due to the "peculiar" I2C when doing manual reads.

Furthermore, the ADC uses this continuous autonomous measuring, and the IC keeps producing new 'out of window' IRQs if measurements are out of window - the driver disables the event for 1 seconds when sending it to user. This prevents generating storm of events

Revision history:
RFC v1 => v2:
- Drop MFD and pinmux.
- Automatically re-enable events after 1 second.
- Export fwnode parsing helpers for finding the ADC channels.

---

Matti Vaittinen (5):
dt-bindings: ROHM BD79124 ADC/GPO
iio: adc: add helpers for parsing ADC nodes
iio: adc: Support ROHM BD79124 ADC
MAINTAINERS: Add IIO ADC helpers
MAINTAINERS: Add ROHM BD79124 ADC/GPO

 .../bindings/iio/adc/rohm,bd79124.yaml        |  114 ++
 MAINTAINERS                                   |   12 +
 drivers/iio/adc/Kconfig                       |   15 +
 drivers/iio/adc/Makefile                      |    2 +
 drivers/iio/adc/industrialio-adc.c            |  151 +++
 drivers/iio/adc/rohm-bd79124.c                | 1149 +++++++++++++++++
 include/linux/iio/adc-helpers.h               |   22 +
 7 files changed, 1465 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/rohm,bd79124.yaml
 create mode 100644 drivers/iio/adc/industrialio-adc.c
 create mode 100644 drivers/iio/adc/rohm-bd79124.c
 create mode 100644 include/linux/iio/adc-helpers.h

base-commit: 5bc55a333a2f7316b58edc7573e8e893f7acb532

Signature · 1 line
2.48.1
author maintainer
#1 lore raw

PATCH v2 1/5 dt-bindings: ROHM BD79124 ADC/GPO

Add binding document for the ROHM BD79124 ADC / GPO.

ROHM BD79124 is a 8-channel, 12-bit ADC. The input pins can also be used as general purpose outputs.

---
Revision history:
RFC v1 => v2:
- drop MFD and represent directly as ADC
- drop pinmux and treat all non ADC channel pins as GPOs

---
 .../bindings/iio/adc/rohm,bd79124.yaml        | 114 ++++++++++++++++++
 1 file changed, 114 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/rohm,bd79124.yaml
Documentation/devicetree/bindings/iio/adc/rohm,bd79124.yaml new file +114 -0
new file mode 100644
index 000000000000..50889dc6b9a8
@@ -0,0 +1,114 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/rohm,bd79124.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ROHM BD79124 ADC/GPO
+
+maintainers:
+  - Matti Vaittinen <[email protected]>
+
+description: |
+  The ROHM BD79124 is a 12-bit, 8-channel, SAR ADC. The ADC supports
+  an automatic measurement mode, with an alarm interrupt for out-of-window
+  measurements. ADC input pins can be also configured as general purpose
+  outputs.
+
+properties:
+  compatible:
+    const: rohm,bd79124
+
+  reg:
+    description:
+      I2C slave address.
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  gpio-controller: true
+
+  "#gpio-cells":
+    const: 1
+    description:
+      The pin number.
+
+  vdd-supply: true
+
+  iovdd-supply: true
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+patternProperties:
+  "^channel@[0-9a-f]+$":
+    type: object
+    $ref: /schemas/iio/adc/adc.yaml#
+    description: Represents ADC channel.
+
+    properties:
+      reg:
+        description: AIN pin number
+        minimum: 0
+        maximum: 7
+
+    required:
+      - reg
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - iovdd-supply
+  - vdd-supply
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/leds/common.h>
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        adc: adc@10 {
+            compatible = "rohm,bd79124";
+            reg = <0x10>;
+
+            interrupt-parent = <&gpio1>;
+            interrupts = <29 8>;
+
+            vdd-supply = <&dummyreg>;
+            iovdd-supply = <&dummyreg>;
+
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            channel@0 {
+                reg = <0>;
+            };
+            channel@1 {
+                reg = <1>;
+            };
+            channel@2 {
+                reg = <2>;
+            };
+            channel@3 {
+                reg = <3>;
+            };
+            channel@4 {
+                reg = <4>;
+            };
+            channel@5 {
+                reg = <5>;
+            };
+            channel@6 {
+                reg = <6>;
+            };
+        };
+    };
Signature · 1 line
2.48.1
#2 lore raw

Re: [PATCH v2 1/5] dt-bindings: ROHM BD79124 ADC/GPO

Quoted 60 lines · Matti Vaittinen

Add binding document for the ROHM BD79124 ADC / GPO.

ROHM BD79124 is a 8-channel, 12-bit ADC. The input pins can also be used as general purpose outputs.

---
Revision history:
RFC v1 => v2:
- drop MFD and represent directly as ADC
- drop pinmux and treat all non ADC channel pins as GPOs

---
 .../bindings/iio/adc/rohm,bd79124.yaml        | 114 ++++++++++++++++++
 1 file changed, 114 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/rohm,bd79124.yaml
Documentation/devicetree/bindings/iio/adc/rohm,bd79124.yaml new file +38 -0
new file mode 100644
index 000000000000..50889dc6b9a8
@@ -0,0 +1,114 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/rohm,bd79124.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ROHM BD79124 ADC/GPO
+
+maintainers:
+  - Matti Vaittinen <[email protected]>
+
+description: |
+  The ROHM BD79124 is a 12-bit, 8-channel, SAR ADC. The ADC supports
+  an automatic measurement mode, with an alarm interrupt for out-of-window
+  measurements. ADC input pins can be also configured as general purpose
+  outputs.
+
+properties:
+  compatible:
+    const: rohm,bd79124
+
+  reg:
+    description:
+      I2C slave address.
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  gpio-controller: true
+
+  "#gpio-cells":
+    const: 1
+    description:
+      The pin number.
+
+  vdd-supply: true
+

Documentation/devicetree/bindings/iio/adc/rohm,bd79124.yaml line 48

Quoted 10 lines
Documentation/devicetree/bindings/iio/adc/rohm,bd79124.yaml new file +10 -0
+  iovdd-supply: true
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+patternProperties:
+  "^channel@[0-9a-f]+$":

You can only have 8 channels, there's no need for this to be so permissive, right?
Otherwise, this looks good enough to me.

Quoted 69 lines
Documentation/devicetree/bindings/iio/adc/rohm,bd79124.yaml new file +66 -0
+    type: object
+    $ref: /schemas/iio/adc/adc.yaml#
+    description: Represents ADC channel.
+
+    properties:
+      reg:
+        description: AIN pin number
+        minimum: 0
+        maximum: 7
+
+    required:
+      - reg
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - iovdd-supply
+  - vdd-supply
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/leds/common.h>
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        adc: adc@10 {
+            compatible = "rohm,bd79124";
+            reg = <0x10>;
+
+            interrupt-parent = <&gpio1>;
+            interrupts = <29 8>;
+
+            vdd-supply = <&dummyreg>;
+            iovdd-supply = <&dummyreg>;
+
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            channel@0 {
+                reg = <0>;
+            };
+            channel@1 {
+                reg = <1>;
+            };
+            channel@2 {
+                reg = <2>;
+            };
+            channel@3 {
+                reg = <3>;
+            };
+            channel@4 {
+                reg = <4>;
+            };
+            channel@5 {
+                reg = <5>;
+            };
+            channel@6 {
+                reg = <6>;
+            };
+        };
+    };
Signature · 1 line
2.48.1
author maintainer
#3 lore raw

Re: [PATCH v2 1/5] dt-bindings: ROHM BD79124 ADC/GPO

Quoted 13 lines · Conor Dooley
Quoted 12 lines · Matti Vaittinen

Add binding document for the ROHM BD79124 ADC / GPO.

ROHM BD79124 is a 8-channel, 12-bit ADC. The input pins can also be used as general purpose outputs.

---
Revision history:
RFC v1 => v2:
- drop MFD and represent directly as ADC
- drop pinmux and treat all non ADC channel pins as GPOs
---

...

Quoted 6 lines

Documentation/devicetree/bindings/iio/adc/rohm,bd79124.yaml line 48

Quoted 2 lines
Documentation/devicetree/bindings/iio/adc/rohm,bd79124.yaml new file +2 -0
+patternProperties:
+  "^channel@[0-9a-f]+$":

You can only have 8 channels, there's no need for this to be so permissive, right?
Otherwise, this looks good enough to me.

Thanks Conor!

Indeed, I think we only need to accept channel@0 .. channel@7. Thanks!
(And reason for this being permissive is ... drum roll ... copy-paste :) That's my main method of creating the yaml bindings. Swearing, sweating, copying and running the 'make dt_binding_check' :rolleyes:

I'll wait for a while for comments to the other patches and fix this for v3 :)

Yours,
-- Matti

Reviewed-by
#4 lore raw

Re: [PATCH v2 1/5] dt-bindings: ROHM BD79124 ADC/GPO

Quoted 26 lines · Matti Vaittinen
Quoted 13 lines · Conor Dooley
Quoted 12 lines · Matti Vaittinen

Add binding document for the ROHM BD79124 ADC / GPO.

ROHM BD79124 is a 8-channel, 12-bit ADC. The input pins can also be used as general purpose outputs.

---
Revision history:
RFC v1 => v2:
- drop MFD and represent directly as ADC
- drop pinmux and treat all non ADC channel pins as GPOs
---

...

Quoted 6 lines

Documentation/devicetree/bindings/iio/adc/rohm,bd79124.yaml line 48

Quoted 2 lines
Documentation/devicetree/bindings/iio/adc/rohm,bd79124.yaml new file +2 -0
+patternProperties:
+  "^channel@[0-9a-f]+$":

You can only have 8 channels, there's no need for this to be so permissive, right?
Otherwise, this looks good enough to me.

Thanks Conor!

Quoted 8 lines

Indeed, I think we only need to accept channel@0 .. channel@7. Thanks! (And reason for this being permissive is ... drum roll ... copy-paste :) That's my main method of creating the yaml bindings. Swearing, sweating, copying and running the 'make dt_binding_check'
:rolleyes:

I'll wait for a while for comments to the other patches and fix this for v3 :)

with it reduced,

Cheers,
Conor.

author maintainer
#5 lore raw

PATCH v2 2/5 iio: adc: add helpers for parsing ADC nodes

In reply to Matti Vaittinen

There are ADC ICs which may have some of the AIN pins usable for other functions. These ICs may have some of the AIN pins wired so that they should not be used for ADC.

(Preferred?) way for marking pins which can be used as ADC inputs is to add corresponding channels@N nodes in the device tree as described in the ADC binding yaml.

Add couple of helper functions which can be used to retrieve the channel information from the device node.

---
Revision history:
RFC v1 => v2:
- New patch

I think it might be nice to have helpers for fetching also the other generic (non vendor specific) ADC properties (as listed in the Documentation/devicetree/bindings/iio/adc/adc.yaml) - but as I don't have use for those in BD79124 driver (at least not for now), I don't imnplement them yet. Anyways, this commit creates a place for such helpers.

---
 drivers/iio/adc/Kconfig            |   3 +
 drivers/iio/adc/Makefile           |   1 +
 drivers/iio/adc/industrialio-adc.c | 151 +++++++++++++++++++++++++++++
 include/linux/iio/adc-helpers.h    |  22 +++++
 4 files changed, 177 insertions(+)
 create mode 100644 drivers/iio/adc/industrialio-adc.c
 create mode 100644 include/linux/iio/adc-helpers.h
drivers/iio/adc/Kconfig +3 -0
index 849c90203071..37b70a65da6f 100644
@@ -6,6 +6,9 @@
 
 menu "Analog to digital converters"
 
+config IIO_ADC_HELPER
+	tristate
+
 config AB8500_GPADC
 	bool "ST-Ericsson AB8500 GPADC driver"
 	depends on AB8500_CORE && REGULATOR_AB8500
drivers/iio/adc/Makefile +1 -0
index ee19afba62b7..956c121a7544 100644
@@ -57,6 +57,7 @@ obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
 obj-$(CONFIG_GEHC_PMC_ADC) += gehc-pmc-adc.o
 obj-$(CONFIG_HI8435) += hi8435.o
 obj-$(CONFIG_HX711) += hx711.o
+obj-$(CONFIG_IIO_ADC_HELPER) += industrialio-adc.o
 obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
 obj-$(CONFIG_IMX8QXP_ADC) += imx8qxp-adc.o
 obj-$(CONFIG_IMX93_ADC) += imx93_adc.o
drivers/iio/adc/industrialio-adc.c new file +151 -0

151 lines not on this page · raw message

include/linux/iio/adc-helpers.h new file +22 -0
new file mode 100644
index 000000000000..3801b2d17517
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/* The industrial I/O ADC helpers
+ *
+ * Copyright (c) 2025 Matti Vaittinen <[email protected]>
+ */
+
+#ifndef _INDUSTRIAL_IO_ADC_HELPERS_H_
+#define _INDUSTRIAL_IO_ADC_HELPERS_H_
+
+#include <linux/iio/iio.h>
+
+struct device;
+struct fwnode_handle;
+
+int iio_adc_fwnode_num_channels(struct fwnode_handle *fwnode);
+int devm_iio_adc_device_alloc_chaninfo(struct device *dev,
+				       const struct iio_chan_spec *template,
+				       struct iio_chan_spec **cs);
+int iio_adc_device_get_channels(struct device *dev, int *channels,
+				int max_channels);
+#endif /* _INDUSTRIAL_IO_ADC_HELPERS_H_ */
Signature · 1 line
2.48.1
maintainer
#6 lore raw

Re: [PATCH v2 2/5] iio: adc: add helpers for parsing ADC nodes

Quoted 18 lines · On Wed, 5 Feb 2025 15:34:51 +0200 Matti Vaittinen <[email protected]>…

There are ADC ICs which may have some of the AIN pins usable for other functions. These ICs may have some of the AIN pins wired so that they should not be used for ADC.

(Preferred?) way for marking pins which can be used as ADC inputs is to add corresponding channels@N nodes in the device tree as described in the ADC binding yaml.

Add couple of helper functions which can be used to retrieve the channel information from the device node.

---
Revision history:
RFC v1 => v2:
- New patch

Quoted 6 lines

I think it might be nice to have helpers for fetching also the other generic (non vendor specific) ADC properties (as listed in the Documentation/devicetree/bindings/iio/adc/adc.yaml) - but as I don't have use for those in BD79124 driver (at least not for now), I don't imnplement them yet. Anyways, this commit creates a place for such helpers.

There is often a mix of vendor specific and not in channel nodes. Hence I'm not sure how widely this will be and it is driver specific which of the standard things make sense.

So before I'd consider a helper like this I'd want to see it alongside a bunch of users including some of the complex ones so that we know it generalizes well enough. It doesn't make sense to introduce it otherwise - just keep the code in the specific drivers instead.

It's an interesting idea, but not a trivial one :)

Jonathan

Quoted 41 lines
---
 drivers/iio/adc/Kconfig            |   3 +
 drivers/iio/adc/Makefile           |   1 +
 drivers/iio/adc/industrialio-adc.c | 151 +++++++++++++++++++++++++++++
 include/linux/iio/adc-helpers.h    |  22 +++++
 4 files changed, 177 insertions(+)
 create mode 100644 drivers/iio/adc/industrialio-adc.c
 create mode 100644 include/linux/iio/adc-helpers.h
drivers/iio/adc/Kconfig +3 -0
index 849c90203071..37b70a65da6f 100644
@@ -6,6 +6,9 @@
 
 menu "Analog to digital converters"
 
+config IIO_ADC_HELPER
+	tristate
+
 config AB8500_GPADC
 	bool "ST-Ericsson AB8500 GPADC driver"
 	depends on AB8500_CORE && REGULATOR_AB8500
drivers/iio/adc/Makefile +1 -0
index ee19afba62b7..956c121a7544 100644
@@ -57,6 +57,7 @@ obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
 obj-$(CONFIG_GEHC_PMC_ADC) += gehc-pmc-adc.o
 obj-$(CONFIG_HI8435) += hi8435.o
 obj-$(CONFIG_HX711) += hx711.o
+obj-$(CONFIG_IIO_ADC_HELPER) += industrialio-adc.o
 obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
 obj-$(CONFIG_IMX8QXP_ADC) += imx8qxp-adc.o
 obj-$(CONFIG_IMX93_ADC) += imx93_adc.o
drivers/iio/adc/industrialio-adc.c new file +0 -0
new file mode 100644
index 000000000000..366e4c8eb6c7

drivers/iio/adc/industrialio-adc.c line 28

Quoted 5 lines
drivers/iio/adc/industrialio-adc.c new file +3 -0
@@ -0,0 +1,151 @@
 
 
+
+/**
+ * iio_adc_device_get_channels - get ADC channel IDs

This sounds far too like the inkern interfaces. Need to associate it instead with the fwnode / properties stuff.

Quoted 3 lines

+ *
+ * Scan the device node for ADC channel information. Return an array of found
+ * IDs. Caller need to allocate the memory for the array and provide maximum

Caller needs to provide the memory

Quoted 9 lines

+ * number of IDs the array can store.
+ *
+ * @dev: Pointer to the ADC device
+ * @channels: Array where the found IDs will be stored.
+ * @max_channels: Number of IDs that fit in the array.
+ *
+ * Return: Number of found channels on succes. Negative value to
+ * indicate failure.
+ */

drivers/iio/adc/industrialio-adc.c line 48

Quoted 8 lines
drivers/iio/adc/industrialio-adc.c new file +8 -0
+int iio_adc_device_get_channels(struct device *dev, int *channels,
+				int max_channels)
+{
+	struct fwnode_handle *fwnode, *child;
+	int num_chan = 0, ret;
+
+	fwnode = dev_fwnode(dev);
+	if (!fwnode) {

As before, I'd relax this until we need to do it. We may never do so.

drivers/iio/adc/industrialio-adc.c line 54

Quoted 6 lines
drivers/iio/adc/industrialio-adc.c new file +6 -0
+		fwnode = dev_fwnode(dev->parent);
+		if (!fwnode)
+			return -ENODEV;
+	}
+	fwnode_for_each_child_node(fwnode, child) {
+		if (fwnode_name_eq(child, "channel")) {

As below. I'd flip the logic here and use a continue

Quoted 11 lines
drivers/iio/adc/industrialio-adc.c new file +11 -0
+			u32 ch;
+
+			if (num_chan == max_channels)
+				return -EINVAL;
+
+			ret = fwnode_property_read_u32(child, "reg", &ch);
+			if (ret)
+				return ret;
+
+			/*
+			 * We assume the channel IDs start from 0. If it seems

drivers/iio/adc/industrialio-adc.c line 75

Quoted 10 lines
drivers/iio/adc/industrialio-adc.c new file +10 -0
+			 * this is not a sane assumption, then we can relax
+			 * this check or add 'allowed ID range' parameter.
+			 *
+			 * Let's just start with this simple assumption.
+			 */
+			if (ch > max_channels)
+				return -ERANGE;
+
+			channels[num_chan] = ch;
+			num_chan++;
			channel[num_chan++] = ch;

So it is clear how the two are coupled.

Quoted 6 lines
drivers/iio/adc/industrialio-adc.c new file +6 -0
+		}
+	}
+
+	return num_chan;
+
+}

drivers/iio/adc/industrialio-adc.c line 91

Quoted 10 lines
drivers/iio/adc/industrialio-adc.c new file +10 -0
+EXPORT_SYMBOL_GPL(iio_adc_device_get_channels);
+
+/**
+ * devm_iio_adc_device_alloc_chaninfo - allocate and fill iio_chan_spec for adc
+ *
+ * Scan the device node for ADC channel information. Allocate and populate the
+ * iio_chan_spec structure corresponding to channels that are found. The memory
+ * for iio_chan_spec structure will be freed upon device detach. Try parent
+ * device node if given device has no fwnode associated to cover also MFD
+ * devices.

I'd leave that parent node check until we need it (unless you need it for this one!). Feels like infra structure that might never be used. That would let us for now use the device_for_each_child_node() handling.

Quoted 27 lines

+ *
+ * @dev: Pointer to the ADC device
+ * @template: Template iio_chan_spec from which the fields of all found and
+ * allocated channels are initialized.
+ * @cs: Location where pointer to allocated iio_chan_spec should be
+ * stored
+ *
+ * Return: Number of found channels on succes. Negative value to indicate
+ * failure.
+ */

drivers/iio/adc/industrialio-adc.c new file +17 -0
+int devm_iio_adc_device_alloc_chaninfo(struct device *dev,
+				       const struct iio_chan_spec *template,
+				       struct iio_chan_spec **cs)
+{
+	struct fwnode_handle *fwnode, *child;
+	struct iio_chan_spec *chan;
+	int num_chan = 0, ret;
+
+	fwnode = dev_fwnode(dev);
+	if (!fwnode) {
+		fwnode = dev_fwnode(dev->parent);
+		if (!fwnode)
+			return -ENODEV;
+	}
+
+	num_chan = iio_adc_fwnode_num_channels(fwnode);
+	if (num_chan < 0)

drivers/iio/adc/industrialio-adc.c line 128

Quoted 10 lines
drivers/iio/adc/industrialio-adc.c new file +10 -0
+		return num_chan;
+
+	*cs = devm_kcalloc(dev, num_chan, sizeof(**cs), GFP_KERNEL);
+	if (!*cs)
+		return -ENOMEM;
+
+	chan = &(*cs)[0];
+
+	fwnode_for_each_child_node(fwnode, child) {
+		if (fwnode_name_eq(child, "channel")) {

Flip logic probably and use a continue to reduce indent of the next bit (which may well get a lot more complex in future).

drivers/iio/adc/industrialio-adc.c line 62

Quoted 5 lines
drivers/iio/adc/industrialio-adc.c new file +5 -0
+			u32 ch;
+
+			ret = fwnode_property_read_u32(child, "reg", &ch);
+			if (ret)
+				return ret;

In general the association between reg and channel is more complex. We need to deal with a reasonable subset of cases (single-channel, diff-channels etc) where it isn't the case that chan == chan->channel.

drivers/iio/adc/industrialio-adc.c line 139

Quoted 6 lines
drivers/iio/adc/industrialio-adc.c new file +6 -0
+
+			*chan = *template;
+			chan->channel = ch;
+
+			if (num_chan > 1)
+				chan->indexed = 1;

I think set this whatever, probably in the template.
I don't want to see the interface change just because a given DT says only one channel is connected.

Quoted 12 lines
drivers/iio/adc/industrialio-adc.c new file +12 -0
+
+			chan++;
+		}
+	}
+
+	return num_chan;
+}
+EXPORT_SYMBOL_GPL(devm_iio_adc_device_alloc_chaninfo);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Matti Vaittinen <[email protected]>");
+MODULE_DESCRIPTION("IIO ADC fwnode parsing helpers");
author maintainer
#7 lore raw

Re: [PATCH v2 2/5] iio: adc: add helpers for parsing ADC nodes

Hi Jonathan,

Thanks for the review and all the comments!

Just a note that I am currently spending some quality time with rebuilding the floor of my house. Leaking floor drain can cause a bit of a work... :rolleyes: So, my time with upstream work is a bit limited - although writing an occasional bug or two can help one to keep the balance ;)

Anyways, my replies and new versions may be slower than usual..

Quoted 28 lines · Jonathan Cameron
Quoted 18 lines · On Wed, 5 Feb 2025 15:34:51 +0200 Matti Vaittinen <[email protected]>…

There are ADC ICs which may have some of the AIN pins usable for other functions. These ICs may have some of the AIN pins wired so that they should not be used for ADC.

(Preferred?) way for marking pins which can be used as ADC inputs is to add corresponding channels@N nodes in the device tree as described in the ADC binding yaml.

Add couple of helper functions which can be used to retrieve the channel information from the device node.

---
Revision history:
RFC v1 => v2:
- New patch

Quoted 6 lines

I think it might be nice to have helpers for fetching also the other generic (non vendor specific) ADC properties (as listed in the Documentation/devicetree/bindings/iio/adc/adc.yaml) - but as I don't have use for those in BD79124 driver (at least not for now), I don't imnplement them yet. Anyways, this commit creates a place for such helpers.

Quoted 3 lines

There is often a mix of vendor specific and not in channel nodes. Hence I'm not sure how widely this will be and it is driver specific which of the standard things make sense.

I definitely agree. Still, in my experience, no written standard standardizes use as well as written helpers ;) More we support parsing the generic helpers by the (add subsystem here)-core, more the driver writes will use the generic properties (instead of brewing vendor specific ones).

Quoted 6 lines

So before I'd consider a helper like this I'd want to see it alongside a bunch of users including some of the complex ones so that we know it generalizes well enough. It doesn't make sense to introduce it otherwise - just keep the code in the specific drivers instead.

It's an interesting idea, but not a trivial one :)

I agree it's not trivial. But I believe adding helpers one-by-one to cover 'normal' use-cases guide the use of the properties. Those who need something more exotic can always implement their custom handlers - and then a reviewer of such handler can ask "why" ;)

Quoted 43 lines
Quoted 41 lines
---
  drivers/iio/adc/Kconfig            |   3 +
  drivers/iio/adc/Makefile           |   1 +
  drivers/iio/adc/industrialio-adc.c | 151 +++++++++++++++++++++++++++++
  include/linux/iio/adc-helpers.h    |  22 +++++
  4 files changed, 177 insertions(+)

create mode 100644 drivers/iio/adc/industrialio-adc.c create mode 100644 include/linux/iio/adc-helpers.h

drivers/iio/adc/Kconfig +3 -0
index 849c90203071..37b70a65da6f 100644
@@ -6,6 +6,9 @@
 
  menu "Analog to digital converters"
 
+config IIO_ADC_HELPER
+	tristate
+
  config AB8500_GPADC
  	bool "ST-Ericsson AB8500 GPADC driver"
  	depends on AB8500_CORE && REGULATOR_AB8500
drivers/iio/adc/Makefile +1 -0
index ee19afba62b7..956c121a7544 100644
@@ -57,6 +57,7 @@ obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
  obj-$(CONFIG_GEHC_PMC_ADC) += gehc-pmc-adc.o
  obj-$(CONFIG_HI8435) += hi8435.o
  obj-$(CONFIG_HX711) += hx711.o
+obj-$(CONFIG_IIO_ADC_HELPER) += industrialio-adc.o
  obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
  obj-$(CONFIG_IMX8QXP_ADC) += imx8qxp-adc.o
  obj-$(CONFIG_IMX93_ADC) += imx93_adc.o
drivers/iio/adc/industrialio-adc.c new file +0 -0
new file mode 100644
index 000000000000..366e4c8eb6c7
@@ -0,0 +1,151 @@
Quoted 6 lines

drivers/iio/adc/industrialio-adc.c line 28

Quoted 3 lines
drivers/iio/adc/industrialio-adc.c new file +3 -0
+
+/**
+ * iio_adc_device_get_channels - get ADC channel IDs

This sounds far too like the inkern interfaces. Need to associate it instead with the fwnode / properties stuff.

I was juggling between using the 'fwnode' and 'device'. I took the 'device' approach in order to be consistent with the naming convention for the functions in property.h.

For example:
device_property_read_u<N>_array() and
fwnode_property_read_u<N>_array()

depending on if the node is found via device pointer, or if the fwnode is passed directly.

Quoted 6 lines

drivers/iio/adc/industrialio-adc.c line 31

Quoted 4 lines
drivers/iio/adc/industrialio-adc.c new file +3 -0
+ *
+ * Scan the device node for ADC channel information. Return an array of found
+ * IDs. Caller need to allocate the memory for the array and provide maximum

Caller needs to provide the memory

Thanks!

Quoted 12 lines
Quoted 12 lines
drivers/iio/adc/industrialio-adc.c new file +11 -0
+ * number of IDs the array can store.
+ *
+ * @dev:		Pointer to the ADC device
+ * @channels:		Array where the found IDs will be stored.
+ * @max_channels:	Number of IDs that fit in the array.
+ *
+ * Return:		Number of found channels on succes. Negative value to
+ *			indicate failure.
+ */
+int iio_adc_device_get_channels(struct device *dev, int *channels,
+				int max_channels)
Quoted 8 lines

drivers/iio/adc/industrialio-adc.c line 48

Quoted 6 lines
drivers/iio/adc/industrialio-adc.c new file +6 -0
+{
+	struct fwnode_handle *fwnode, *child;
+	int num_chan = 0, ret;
+
+	fwnode = dev_fwnode(dev);
+	if (!fwnode) {

As before, I'd relax this until we need to do it. We may never do so.

The BD79124 does not require this as I dropped the MFD, so this is ultimately your call :) I, however, would like to humbly suggest adding it now ;) I have changed some APIs in the regulator subsystem and in the clk subsystem to support cases where the device-tree node is in the parent (usual MFD device-case), and it has been somewhat scary... (What if somewhere in some of the existing device-trees the parent happens to have interesting properties - but it actually is not correct node? This becomes a potential source of regression when adding support to an existing API).

Furthermore, when the node is unconditionally taken from the given device-pointer, it is tempting for the caller to just pass the parent device as argument...

- If you have done this - please raise your hand. /me raises.
- If you have only later realized it can cause life-time issues when devm is used - please raise your hand. /me raises.
- If you have tried to kick your own behind when fixing the issues - please, raise your hand. /me raises. (and if you succeeded - congraz, you aren't as old and clumsy as I am).

Quoted 9 lines

drivers/iio/adc/industrialio-adc.c line 54

Quoted 7 lines
drivers/iio/adc/industrialio-adc.c new file +6 -0
+		fwnode = dev_fwnode(dev->parent);
+		if (!fwnode)
+			return -ENODEV;
+	}
+	fwnode_for_each_child_node(fwnode, child) {
+		if (fwnode_name_eq(child, "channel")) {

As below. I'd flip the logic here and use a continue

Makes sense.

Quoted 24 lines
Quoted 12 lines
drivers/iio/adc/industrialio-adc.c new file +11 -0
+			u32 ch;
+
+			if (num_chan == max_channels)
+				return -EINVAL;
+
+			ret = fwnode_property_read_u32(child, "reg", &ch);
+			if (ret)
+				return ret;
+
+			/*
+			 * We assume the channel IDs start from 0. If it seems

drivers/iio/adc/industrialio-adc.c line 75

Quoted 10 lines
drivers/iio/adc/industrialio-adc.c new file +10 -0
+			 * this is not a sane assumption, then we can relax
+			 * this check or add 'allowed ID range' parameter.
+			 *
+			 * Let's just start with this simple assumption.
+			 */
+			if (ch > max_channels)
+				return -ERANGE;
+
+			channels[num_chan] = ch;
+			num_chan++;
			channel[num_chan++] = ch;
Quoted 1 line

So it is clear how the two are coupled.

Ouch. I am not fan of this. I have a personal issue as I always need to wonder if this was the case where the ++foo and foo++ resulted different functionality. I end up doing a test to see in which index the result got stored. If you don't feel stronly about this, then I would like to keep the index increase and assignment in separate rows. I believe the coupling is still quite visible for as long as we keep the assignment and increase next to each other. But yeah, if you do feel strongly about this, then it can be implemented as you suggest.

Quoted 14 lines
Quoted 14 lines
drivers/iio/adc/industrialio-adc.c new file +13 -0
+		}
+	}
+
+	return num_chan;
+
+}
+EXPORT_SYMBOL_GPL(iio_adc_device_get_channels);
+
+/**
+ * devm_iio_adc_device_alloc_chaninfo - allocate and fill iio_chan_spec for adc
+ *
+ * Scan the device node for ADC channel information. Allocate and populate the
+ * iio_chan_spec structure corresponding to channels that are found. The memory
Quoted 8 lines

drivers/iio/adc/industrialio-adc.c line 91

Quoted 3 lines
drivers/iio/adc/industrialio-adc.c new file +3 -0
+ * for iio_chan_spec structure will be freed upon device detach. Try parent
+ * device node if given device has no fwnode associated to cover also MFD
+ * devices.

I'd leave that parent node check until we need it (unless you need it for this one!). Feels like infra structure that might never be used. That would let us for now use the device_for_each_child_node() handling.

As above, I adviece to implement the parent device use right from the beginning - but I can change this as BD79124 dropped MFD.

Quoted 32 lines
Quoted 32 lines
drivers/iio/adc/industrialio-adc.c new file +31 -0
+ *
+ * @dev:	Pointer to the ADC device
+ * @template:	Template iio_chan_spec from which the fields of all found and
+ *		allocated channels are initialized.
+ * @cs:		Location where pointer to allocated iio_chan_spec should be
+ *		stored
+ *
+ * Return:	Number of found channels on succes. Negative value to indicate
+ *		failure.
+ */
+int devm_iio_adc_device_alloc_chaninfo(struct device *dev,
+				       const struct iio_chan_spec *template,
+				       struct iio_chan_spec **cs)
+{
+	struct fwnode_handle *fwnode, *child;
+	struct iio_chan_spec *chan;
+	int num_chan = 0, ret;
+
+	fwnode = dev_fwnode(dev);
+	if (!fwnode) {
+		fwnode = dev_fwnode(dev->parent);
+		if (!fwnode)
+			return -ENODEV;
+	}
+
+	num_chan = iio_adc_fwnode_num_channels(fwnode);
+	if (num_chan < 0)
+		return num_chan;
+
+	*cs = devm_kcalloc(dev, num_chan, sizeof(**cs), GFP_KERNEL);
+	if (!*cs)
Quoted 9 lines

drivers/iio/adc/industrialio-adc.c line 128

Quoted 6 lines
drivers/iio/adc/industrialio-adc.c new file +6 -0
+		return -ENOMEM;
+
+	chan = &(*cs)[0];
+
+	fwnode_for_each_child_node(fwnode, child) {
+		if (fwnode_name_eq(child, "channel")) {

Flip logic probably and use a continue to reduce indent of the next bit (which may well get a lot more complex in future).

Right, thanks.

Quoted 9 lines

drivers/iio/adc/industrialio-adc.c line 62

Quoted 5 lines
drivers/iio/adc/industrialio-adc.c new file +5 -0
+			u32 ch;
+
+			ret = fwnode_property_read_u32(child, "reg", &ch);
+			if (ret)
+				return ret;

In general the association between reg and channel is more complex. We need to deal with a reasonable subset of cases (single-channel, diff-channels etc) where it isn't the case that chan == chan->channel.

I guess this is right. I, however, lacked of knowledge on how the differential channels etc. are handled :) Hence I just implemented what I believe is "the most common" approach, while leaving the rest to be implemented by those who need it.

Still, I agree that a generic looking API which silently produces bad results for a few of the use-cases is not nice. By the very minimum we should check if single-channel, diff-channels properties are present, and then error out with a warning print. That should give a good hint for those driver writers who are trying to use API for something unsupported.

Also, restrictions should be mentioned in the documentation.

Do you think we should use some more specific function naming, like "_simple" - suffix?

Quoted 11 lines

drivers/iio/adc/industrialio-adc.c line 139

Quoted 7 lines
drivers/iio/adc/industrialio-adc.c new file +6 -0
+
+			*chan = *template;
+			chan->channel = ch;
+
+			if (num_chan > 1)
+				chan->indexed = 1;

I think set this whatever, probably in the template.
I don't want to see the interface change just because a given DT says only one channel is connected.

Ah. I didn't think of that! Good point.

Yours,
-- Matti

maintainer
#8 lore raw

Re: [PATCH v2 2/5] iio: adc: add helpers for parsing ADC nodes

Quoted 56 lines · On Tue, 11 Feb 2025 10:52:51 +0200 Matti Vaittinen <[email protected]>…

Hi Jonathan,

Thanks for the review and all the comments!

Just a note that I am currently spending some quality time with rebuilding the floor of my house. Leaking floor drain can cause a bit of a work... :rolleyes: So, my time with upstream work is a bit limited - although writing an occasional bug or two can help one to keep the balance ;)

Anyways, my replies and new versions may be slower than usual..

Quoted 28 lines · Jonathan Cameron
Quoted 18 lines · On Wed, 5 Feb 2025 15:34:51 +0200 Matti Vaittinen <[email protected]>…

There are ADC ICs which may have some of the AIN pins usable for other functions. These ICs may have some of the AIN pins wired so that they should not be used for ADC.

(Preferred?) way for marking pins which can be used as ADC inputs is to add corresponding channels@N nodes in the device tree as described in the ADC binding yaml.

Add couple of helper functions which can be used to retrieve the channel information from the device node.

---
Revision history:
RFC v1 => v2:
- New patch

Quoted 6 lines

I think it might be nice to have helpers for fetching also the other generic (non vendor specific) ADC properties (as listed in the Documentation/devicetree/bindings/iio/adc/adc.yaml) - but as I don't have use for those in BD79124 driver (at least not for now), I don't imnplement them yet. Anyways, this commit creates a place for such helpers.

Quoted 3 lines

There is often a mix of vendor specific and not in channel nodes. Hence I'm not sure how widely this will be and it is driver specific which of the standard things make sense.

I definitely agree. Still, in my experience, no written standard standardizes use as well as written helpers ;) More we support parsing the generic helpers by the (add subsystem here)-core, more the driver writes will use the generic properties (instead of brewing vendor specific ones).

Quoted 5 lines

So before I'd consider a helper like this I'd want to see it alongside a bunch of users including some of the complex ones so that we know it generalizes well enough. It doesn't make sense to introduce it otherwise - just keep the code in the specific drivers instead.

Quoted 6 lines
Quoted 1 line

It's an interesting idea, but not a trivial one :)

I agree it's not trivial. But I believe adding helpers one-by-one to cover 'normal' use-cases guide the use of the properties. Those who need something more exotic can always implement their custom handlers - and then a reviewer of such handler can ask "why" ;)

I'd be fine with a series taking on the task of converting handling of all the documented properties in adc.yaml

If we do less than that it may never get wide adoption and we end up with a bit of generic looking infrastructure that isn't generic.

Quoted 56 lines
Quoted 45 lines
Quoted 43 lines

---
  drivers/iio/adc/Kconfig            |   3 +
  drivers/iio/adc/Makefile           |   1 +
  drivers/iio/adc/industrialio-adc.c | 151 +++++++++++++++++++++++++++++
  include/linux/iio/adc-helpers.h    |  22 +++++
  4 files changed, 177 insertions(+)

create mode 100644 drivers/iio/adc/industrialio-adc.c create mode 100644 include/linux/iio/adc-helpers.h

drivers/iio/adc/Kconfig +3 -0
index 849c90203071..37b70a65da6f 100644
@@ -6,6 +6,9 @@
 
  menu "Analog to digital converters"
 
+config IIO_ADC_HELPER
+	tristate
+
  config AB8500_GPADC
  	bool "ST-Ericsson AB8500 GPADC driver"
  	depends on AB8500_CORE && REGULATOR_AB8500
drivers/iio/adc/Makefile +1 -0
index ee19afba62b7..956c121a7544 100644
@@ -57,6 +57,7 @@ obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
  obj-$(CONFIG_GEHC_PMC_ADC) += gehc-pmc-adc.o
  obj-$(CONFIG_HI8435) += hi8435.o
  obj-$(CONFIG_HX711) += hx711.o
+obj-$(CONFIG_IIO_ADC_HELPER) += industrialio-adc.o
  obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
  obj-$(CONFIG_IMX8QXP_ADC) += imx8qxp-adc.o
  obj-$(CONFIG_IMX93_ADC) += imx93_adc.o
drivers/iio/adc/industrialio-adc.c new file +0 -0
new file mode 100644
index 000000000000..366e4c8eb6c7
@@ -0,0 +1,151 @@  
Quoted 6 lines

drivers/iio/adc/industrialio-adc.c line 28

Quoted 3 lines
drivers/iio/adc/industrialio-adc.c new file +3 -0
+
+/**
+ * iio_adc_device_get_channels - get ADC channel IDs  

This sounds far too like the inkern interfaces. Need to associate it instead with the fwnode / properties stuff.

I was juggling between using the 'fwnode' and 'device'. I took the 'device' approach in order to be consistent with the naming convention for the functions in property.h.

Quoted 6 lines

For example:
device_property_read_u<N>_array() and
fwnode_property_read_u<N>_array()

depending on if the node is found via device pointer, or if the fwnode is passed directly.

hmm. The bit to avoid is 'get'.

iio_adc_device_channels_from_properties() maybe?

Quoted 45 lines
Quoted 7 lines

drivers/iio/adc/industrialio-adc.c line 31

Quoted 5 lines
drivers/iio/adc/industrialio-adc.c new file +3 -0
+ *
+ * Scan the device node for ADC channel information. Return an array of found
+ * IDs. Caller need to allocate the memory for the array and provide maximum  

Caller needs to provide the memory

Thanks!

Quoted 12 lines
Quoted 12 lines
drivers/iio/adc/industrialio-adc.c new file +11 -0
+ * number of IDs the array can store.
+ *
+ * @dev:		Pointer to the ADC device
+ * @channels:		Array where the found IDs will be stored.
+ * @max_channels:	Number of IDs that fit in the array.
+ *
+ * Return:		Number of found channels on succes. Negative value to
+ *			indicate failure.
+ */
+int iio_adc_device_get_channels(struct device *dev, int *channels,
+				int max_channels)
Quoted 8 lines

drivers/iio/adc/industrialio-adc.c line 48

Quoted 6 lines
drivers/iio/adc/industrialio-adc.c new file +6 -0
+{
+	struct fwnode_handle *fwnode, *child;
+	int num_chan = 0, ret;
+
+	fwnode = dev_fwnode(dev);
+	if (!fwnode) {  

As before, I'd relax this until we need to do it. We may never do so.

The BD79124 does not require this as I dropped the MFD, so this is ultimately your call :) I, however, would like to humbly suggest adding it now ;) I have changed some APIs in the regulator subsystem and in the clk subsystem to support cases where the device-tree node is in the parent (usual MFD device-case), and it has been somewhat scary... (What if somewhere in some of the existing device-trees the parent happens to have interesting properties - but it actually is not correct node? This becomes a potential source of regression when adding support to an existing API).

Furthermore, when the node is unconditionally taken from the given device-pointer, it is tempting for the caller to just pass the parent device as argument...

Quoted 6 lines

- If you have done this - please raise your hand. /me raises.
- If you have only later realized it can cause life-time issues when devm is used - please raise your hand. /me raises.
- If you have tried to kick your own behind when fixing the issues - please, raise your hand. /me raises. (and if you succeeded - congraz, you aren't as old and clumsy as I am).

Maybe. I'd be happier if there was a single user included with a patch set doing this. I'm not sure this applies to any of the SoC ADCs which are MFD hosted but maybe it does.

Quoted 39 lines
Quoted 10 lines

drivers/iio/adc/industrialio-adc.c line 54

Quoted 8 lines
drivers/iio/adc/industrialio-adc.c new file +6 -0
+		fwnode = dev_fwnode(dev->parent);
+		if (!fwnode)
+			return -ENODEV;
+	}
+	fwnode_for_each_child_node(fwnode, child) {
+		if (fwnode_name_eq(child, "channel")) {  

As below. I'd flip the logic here and use a continue

Makes sense.

Quoted 24 lines
Quoted 12 lines
drivers/iio/adc/industrialio-adc.c new file +11 -0
+			u32 ch;
+
+			if (num_chan == max_channels)
+				return -EINVAL;
+
+			ret = fwnode_property_read_u32(child, "reg", &ch);
+			if (ret)
+				return ret;
+
+			/*
+			 * We assume the channel IDs start from 0. If it seems

drivers/iio/adc/industrialio-adc.c line 75

Quoted 10 lines
drivers/iio/adc/industrialio-adc.c new file +10 -0
+			 * this is not a sane assumption, then we can relax
+			 * this check or add 'allowed ID range' parameter.
+			 *
+			 * Let's just start with this simple assumption.
+			 */
+			if (ch > max_channels)
+				return -ERANGE;
+
+			channels[num_chan] = ch;
+			num_chan++;  
			channel[num_chan++] = ch;
Quoted 1 line

So it is clear how the two are coupled.

Quoted 8 lines

Ouch. I am not fan of this. I have a personal issue as I always need to wonder if this was the case where the ++foo and foo++ resulted different functionality. I end up doing a test to see in which index the result got stored. If you don't feel stronly about this, then I would like to keep the index increase and assignment in separate rows. I believe the coupling is still quite visible for as long as we keep the assignment and increase next to each other. But yeah, if you do feel strongly about this, then it can be implemented as you suggest.

It's that question of them staying next to each other that is where the included increment helps. I don't mind that much on this.

Quoted 93 lines
Quoted 15 lines
Quoted 15 lines
drivers/iio/adc/industrialio-adc.c new file +13 -0
+		}
+	}
+
+	return num_chan;
+
+}
+EXPORT_SYMBOL_GPL(iio_adc_device_get_channels);
+
+/**
+ * devm_iio_adc_device_alloc_chaninfo - allocate and fill iio_chan_spec for adc
+ *
+ * Scan the device node for ADC channel information. Allocate and populate the
+ * iio_chan_spec structure corresponding to channels that are found. The memory
Quoted 8 lines

drivers/iio/adc/industrialio-adc.c line 91

Quoted 3 lines
drivers/iio/adc/industrialio-adc.c new file +3 -0
+ * for iio_chan_spec structure will be freed upon device detach. Try parent
+ * device node if given device has no fwnode associated to cover also MFD
+ * devices.  

I'd leave that parent node check until we need it (unless you need it for this one!). Feels like infra structure that might never be used. That would let us for now use the device_for_each_child_node() handling.

As above, I adviece to implement the parent device use right from the beginning - but I can change this as BD79124 dropped MFD.

Quoted 32 lines
Quoted 32 lines
drivers/iio/adc/industrialio-adc.c new file +31 -0
+ *
+ * @dev:	Pointer to the ADC device
+ * @template:	Template iio_chan_spec from which the fields of all found and
+ *		allocated channels are initialized.
+ * @cs:		Location where pointer to allocated iio_chan_spec should be
+ *		stored
+ *
+ * Return:	Number of found channels on succes. Negative value to indicate
+ *		failure.
+ */
+int devm_iio_adc_device_alloc_chaninfo(struct device *dev,
+				       const struct iio_chan_spec *template,
+				       struct iio_chan_spec **cs)
+{
+	struct fwnode_handle *fwnode, *child;
+	struct iio_chan_spec *chan;
+	int num_chan = 0, ret;
+
+	fwnode = dev_fwnode(dev);
+	if (!fwnode) {
+		fwnode = dev_fwnode(dev->parent);
+		if (!fwnode)
+			return -ENODEV;
+	}
+
+	num_chan = iio_adc_fwnode_num_channels(fwnode);
+	if (num_chan < 0)
+		return num_chan;
+
+	*cs = devm_kcalloc(dev, num_chan, sizeof(**cs), GFP_KERNEL);
+	if (!*cs)
Quoted 9 lines

drivers/iio/adc/industrialio-adc.c line 128

Quoted 6 lines
drivers/iio/adc/industrialio-adc.c new file +6 -0
+		return -ENOMEM;
+
+	chan = &(*cs)[0];
+
+	fwnode_for_each_child_node(fwnode, child) {
+		if (fwnode_name_eq(child, "channel")) {  

Flip logic probably and use a continue to reduce indent of the next bit (which may well get a lot more complex in future).

Right, thanks.

Quoted 9 lines

drivers/iio/adc/industrialio-adc.c line 62

Quoted 5 lines
drivers/iio/adc/industrialio-adc.c new file +5 -0
+			u32 ch;
+
+			ret = fwnode_property_read_u32(child, "reg", &ch);
+			if (ret)
+				return ret;  

In general the association between reg and channel is more complex. We need to deal with a reasonable subset of cases (single-channel, diff-channels etc) where it isn't the case that chan == chan->channel.

I guess this is right. I, however, lacked of knowledge on how the differential channels etc. are handled :) Hence I just implemented what I believe is "the most common" approach, while leaving the rest to be implemented by those who need it.

Still, I agree that a generic looking API which silently produces bad results for a few of the use-cases is not nice. By the very minimum we should check if single-channel, diff-channels properties are present, and then error out with a warning print. That should give a good hint for those driver writers who are trying to use API for something unsupported.

Quoted 4 lines

Also, restrictions should be mentioned in the documentation.

Do you think we should use some more specific function naming, like "_simple" - suffix?

No to _simple. I think this needs to handle those cases before we take it at all They should all have enough docs in adc.yaml to work out what happens.

The most complex cases are all Analog Devices parts and those folk are very active on linux-iio so it should be easy to get them to review a series using it.

I don't think it is going to be particularly hard to generalise this.

We may end up with a variant that takes a 'per channel' callback to fill in more data + a private pointer of some type as often those are putting data in a parallel array of extra info about the channels. Let's see what it looks like.

Jonathan

Quoted 19 lines
Quoted 12 lines

drivers/iio/adc/industrialio-adc.c line 139

Quoted 8 lines
drivers/iio/adc/industrialio-adc.c new file +6 -0
+
+			*chan = *template;
+			chan->channel = ch;
+
+			if (num_chan > 1)
+				chan->indexed = 1;  

I think set this whatever, probably in the template.
I don't want to see the interface change just because a given DT says only one channel is connected.

Ah. I didn't think of that! Good point.

Yours,
-- Matti

reviewer
#9 lore raw

Re: [PATCH v2 2/5] iio: adc: add helpers for parsing ADC nodes

Quoted 65 lines · Jonathan Cameron
Quoted 56 lines · On Tue, 11 Feb 2025 10:52:51 +0200 Matti Vaittinen <[email protected]>…

Hi Jonathan,

Thanks for the review and all the comments!

Just a note that I am currently spending some quality time with rebuilding the floor of my house. Leaking floor drain can cause a bit of a work... :rolleyes: So, my time with upstream work is a bit limited - although writing an occasional bug or two can help one to keep the balance ;)

Anyways, my replies and new versions may be slower than usual..

Quoted 28 lines · Jonathan Cameron
Quoted 18 lines · On Wed, 5 Feb 2025 15:34:51 +0200 Matti Vaittinen <[email protected]>…

There are ADC ICs which may have some of the AIN pins usable for other functions. These ICs may have some of the AIN pins wired so that they should not be used for ADC.

(Preferred?) way for marking pins which can be used as ADC inputs is to add corresponding channels@N nodes in the device tree as described in the ADC binding yaml.

Add couple of helper functions which can be used to retrieve the channel information from the device node.

---
Revision history:
RFC v1 => v2:
- New patch

Quoted 6 lines

I think it might be nice to have helpers for fetching also the other generic (non vendor specific) ADC properties (as listed in the Documentation/devicetree/bindings/iio/adc/adc.yaml) - but as I don't have use for those in BD79124 driver (at least not for now), I don't imnplement them yet. Anyways, this commit creates a place for such helpers.

Quoted 3 lines

There is often a mix of vendor specific and not in channel nodes. Hence I'm not sure how widely this will be and it is driver specific which of the standard things make sense.

I definitely agree. Still, in my experience, no written standard standardizes use as well as written helpers ;) More we support parsing the generic helpers by the (add subsystem here)-core, more the driver writes will use the generic properties (instead of brewing vendor specific ones).

Quoted 5 lines

So before I'd consider a helper like this I'd want to see it alongside a bunch of users including some of the complex ones so that we know it generalizes well enough. It doesn't make sense to introduce it otherwise - just keep the code in the specific drivers instead.

Quoted 6 lines
Quoted 1 line

It's an interesting idea, but not a trivial one :)

I agree it's not trivial. But I believe adding helpers one-by-one to cover 'normal' use-cases guide the use of the properties. Those who need something more exotic can always implement their custom handlers - and then a reviewer of such handler can ask "why" ;)

Quoted 6 lines

I'd be fine with a series taking on the task of converting handling of all the documented properties in adc.yaml

If we do less than that it may never get wide adoption and we end up with a bit of generic looking infrastructure that isn't generic.

Having reviewed quite a few patches recently that make use of these generic channel properties (and writing one driver myself), I don't really see how we could make generic functions for these that would be any simpler than calling the fwnode_property functions directly other than maybe saving a few arguments. The "normal" operation for these properties usually involves poking some registers on the chip (could be immediately or deferred) to configure it. So the only thing we could generalize is reading the property value, but not doing anything with that information.

author maintainer
#10 lore raw

Re: [PATCH v2 2/5] iio: adc: add helpers for parsing ADC nodes

Hi David!

Thanks for the input!

Quoted 69 lines · David Lechner
Quoted 68 lines · Jonathan Cameron
Quoted 56 lines · On Tue, 11 Feb 2025 10:52:51 +0200 Matti Vaittinen <[email protected]>…

Hi Jonathan,

Thanks for the review and all the comments!

Just a note that I am currently spending some quality time with rebuilding the floor of my house. Leaking floor drain can cause a bit of a work... :rolleyes: So, my time with upstream work is a bit limited - although writing an occasional bug or two can help one to keep the balance ;)

Anyways, my replies and new versions may be slower than usual..

Quoted 28 lines · Jonathan Cameron
Quoted 18 lines · On Wed, 5 Feb 2025 15:34:51 +0200 Matti Vaittinen <[email protected]>…

There are ADC ICs which may have some of the AIN pins usable for other functions. These ICs may have some of the AIN pins wired so that they should not be used for ADC.

(Preferred?) way for marking pins which can be used as ADC inputs is to add corresponding channels@N nodes in the device tree as described in the ADC binding yaml.

Add couple of helper functions which can be used to retrieve the channel information from the device node.

---
Revision history:
RFC v1 => v2:
- New patch

Quoted 6 lines

I think it might be nice to have helpers for fetching also the other generic (non vendor specific) ADC properties (as listed in the Documentation/devicetree/bindings/iio/adc/adc.yaml) - but as I don't have use for those in BD79124 driver (at least not for now), I don't imnplement them yet. Anyways, this commit creates a place for such helpers.

Quoted 3 lines

There is often a mix of vendor specific and not in channel nodes. Hence I'm not sure how widely this will be and it is driver specific which of the standard things make sense.

I definitely agree. Still, in my experience, no written standard standardizes use as well as written helpers ;) More we support parsing the generic helpers by the (add subsystem here)-core, more the driver writes will use the generic properties (instead of brewing vendor specific ones).

Quoted 5 lines

So before I'd consider a helper like this I'd want to see it alongside a bunch of users including some of the complex ones so that we know it generalizes well enough. It doesn't make sense to introduce it otherwise - just keep the code in the specific drivers instead.

Quoted 6 lines
Quoted 1 line

It's an interesting idea, but not a trivial one :)

I agree it's not trivial. But I believe adding helpers one-by-one to cover 'normal' use-cases guide the use of the properties. Those who need something more exotic can always implement their custom handlers - and then a reviewer of such handler can ask "why" ;)

I'd be fine with a series taking on the task of converting handling of all the documented properties in adc.yaml

Quoted 8 lines
Quoted 3 lines

If we do less than that it may never get wide adoption and we end up with a bit of generic looking infrastructure that isn't generic.

Having reviewed quite a few patches recently that make use of these generic channel properties (and writing one driver myself), I don't really see how we could make generic functions for these that would be any simpler than calling the fwnode_property functions directly other than maybe saving a few arguments.

I started this for the BD79124 - which is very simple ADC. What it requires from the devicetree is the channel ID. What needs to be done in oreder to get it?

1. Locate the ADC node (device-node)
2. Loop through the sub-nodes and identify subnodes that denote channels.
3. Get the channel number (in this case from the reg-property).

I assume this is very usual need for the simple ADC drivers, and none of this needs to be hardware specific (when we have simple input channels (no need for the differential channel support).

Providing a simple helper doing just this should remove code duplication from drivers. Furthermore, this could help standardize the mechanism for identifying the channel nodes - in my patch this would require channel node to be named channel@<N>. (I see some drivers which don't require this but just assume any sub-node is for channel information. This works badly with chips which may provide multiple functions and thus I think it'd be nice to guide towards naming the nodes to channel@N when possible).

Quoted 5 lines

The "normal" operation for
these properties usually involves poking some registers on the chip (could be immediately or deferred) to configure it. So the only thing we could generalize is reading the property value, but not doing anything with that information.

Absolutely. I had no intention to do anything hardware specific with these helpers. What I'd love to see is IIO-helpers which allowed passing device pointer and a pointer to iio_info - and getting the (known by the helper) device-tree properties parsed and filled in iio_info.

This sure does not help more complex drivers which require some 'not easily parsed' data from the device tree. But for simple drivers like the BD79124 this would allow drivers to omit open-coding the loop locating the channel nodes and getting the channel data.

Maybe a small improvement but one can't deny it still is an improvement, right? :)

Yours,
-- Matti

reviewer
#11 lore raw

Re: [PATCH v2 2/5] iio: adc: add helpers for parsing ADC nodes

Quoted 4 lines · Matti Vaittinen

Hi David!

Thanks for the input!

...

Quoted 9 lines

Absolutely. I had no intention to do anything hardware specific with these helpers. What I'd love to see is IIO-helpers which allowed passing device pointer and a pointer to iio_info - and getting the (known by the helper) device-tree properties parsed and filled in iio_info.

This sure does not help more complex drivers which require some 'not easily parsed' data from the device tree. But for simple drivers like the BD79124 this would allow drivers to omit open-coding the loop locating the channel nodes and getting the channel data.

Maybe a small improvement but one can't deny it still is an improvement, right? :)

Yours,
 -- Matti

If there are a decent number of drivers that can make use of it, then sure. I'm a bit biased because I've been working on some rather complex ADCs lately and not any simple ones. :-)

author maintainer
#12 lore raw

Re: [PATCH v2 2/5] iio: adc: add helpers for parsing ADC nodes

In reply to Jonathan Cameron
Quoted 5 lines · Jonathan Cameron
Quoted 2 lines · On Tue, 11 Feb 2025 10:52:51 +0200 Matti Vaittinen <[email protected]>…

Hi Jonathan,

Quoted 7 lines
Quoted 7 lines

Thanks for the review and all the comments!

Just a note that I am currently spending some quality time with rebuilding the floor of my house. Leaking floor drain can cause a bit of a work... :rolleyes: So, my time with upstream work is a bit limited - although writing an occasional bug or two can help one to keep the balance ;)

The good thing when doing manual labour is that one can run thinking processes at the background - and has no time to reply instantly. XD So, I've been thinking about this quite a bit while installing new floor...

...

Quoted 25 lines
Quoted 25 lines

Right, thanks.

Quoted 9 lines

drivers/iio/adc/industrialio-adc.c line 62

Quoted 5 lines
drivers/iio/adc/industrialio-adc.c new file +5 -0
+			u32 ch;
+
+			ret = fwnode_property_read_u32(child, "reg", &ch);
+			if (ret)
+				return ret;

In general the association between reg and channel is more complex. We need to deal with a reasonable subset of cases (single-channel, diff-channels etc) where it isn't the case that chan == chan->channel.

I guess this is right. I, however, lacked of knowledge on how the differential channels etc. are handled :) Hence I just implemented what I believe is "the most common" approach, while leaving the rest to be implemented by those who need it.

Still, I agree that a generic looking API which silently produces bad results for a few of the use-cases is not nice. By the very minimum we should check if single-channel, diff-channels properties are present, and then error out with a warning print. That should give a good hint for those driver writers who are trying to use API for something unsupported.

Quoted 9 lines
Quoted 4 lines

Also, restrictions should be mentioned in the documentation.

Do you think we should use some more specific function naming, like "_simple" - suffix?

No to _simple. I think this needs to handle those cases before we take it at all They should all have enough docs in adc.yaml to work out what happens.

I am thinking it would be conceptually better to provide small helper (like the *_simpe() ) - which handled only specific properties. We can additionally provide '*_differential()' helper and potentially some '*_full()' helper which covers both cases.

Rationale is that:
- writing _simple() helper is simpler. So is understanding it.
- Devices may not support differential inputs. Drivers for those devices would want to call helper which does not accept the 'differential' channel config. Parsing the differential channel information successfully and filling it in iio_chan_spec would be an error for those drivers.

Quoted 11 lines

The most complex cases are all Analog Devices parts and those folk are very active on linux-iio so it should be easy to get them to review a series using it.

I don't think it is going to be particularly hard to generalise this.

We may end up with a variant that takes a 'per channel' callback to fill in more data + a private pointer of some type as often those are putting data in a parallel array of extra info about the channels. Let's see what it looks like.

I was playing with this thought for a bit. I do love the regulator framework's '.of_parse_cp' which the driver can fill in the regulator desc. It's a pointer to function which is called by a regulator core after it locates the regulator node so the driver can handle regulator specific properties and do relevant configs in the registers.

I some sense the use case is similar - PMICs usually contain multiple regulators, each having own node. Being able to 'offload' locating the node to the regulator-core is often very handy for drivers.

I thought we could maybe add .fwnode_parse_chan_cp field in the iio_info structure. Idea being that it'd be a function which would be called for each channel - and it would probably be useful on other areas in IIO besides the ADCs.

Problem is that the number of channels may not be known prior parsing the device-tree. So, here is kind of a chicken-egg problem with populating the iio_chan_spec. Furthermore, locating the channel node in device-tree may not be 'standard enough' throughout all the different IIO areas.

What comes to a devicetree parsing helper which takes a pointer to a device specific configuration function as argument - my initial feeling is that it might get a bit overly complex to be user-friendly.

Hence, I'd keep this simple and provided small-but-usefull _simple() ;)

Well, if others aren't convinced by the usefulness of these helpers - then I'll probably just squash it into bd79124 driver and be done with this :) Later if I work for another ADC variant I might export it as 'rohm_adc_helper' with an internal to IIO header :)

Thanks for the opinions / discussion!

Yours,
-- Matti

author maintainer
#13 lore raw

Re: [PATCH v2 2/5] iio: adc: add helpers for parsing ADC nodes

In reply to Jonathan Cameron
Quoted 3 lines · Jonathan Cameron

On Tue, 11 Feb 2025 10:52:51 +0200
Matti Vaittinen <[email protected]> wrote:

...

Quoted 32 lines
Quoted 25 lines
Quoted 10 lines

drivers/iio/adc/industrialio-adc.c line 48

Quoted 8 lines
drivers/iio/adc/industrialio-adc.c new file +8 -0
+int iio_adc_device_get_channels(struct device *dev, int *channels,
+				int max_channels)
+{
+	struct fwnode_handle *fwnode, *child;
+	int num_chan = 0, ret;
+
+	fwnode = dev_fwnode(dev);
+	if (!fwnode) {

As before, I'd relax this until we need to do it. We may never do so.

The BD79124 does not require this as I dropped the MFD, so this is ultimately your call :) I, however, would like to humbly suggest adding it now ;) I have changed some APIs in the regulator subsystem and in the clk subsystem to support cases where the device-tree node is in the parent (usual MFD device-case), and it has been somewhat scary... (What if somewhere in some of the existing device-trees the parent happens to have interesting properties - but it actually is not correct node? This becomes a potential source of regression when adding support to an existing API).

Furthermore, when the node is unconditionally taken from the given device-pointer, it is tempting for the caller to just pass the parent device as argument...

Quoted 6 lines

- If you have done this - please raise your hand. /me raises.
- If you have only later realized it can cause life-time issues when devm is used - please raise your hand. /me raises.
- If you have tried to kick your own behind when fixing the issues - please, raise your hand. /me raises. (and if you succeeded - congraz, you aren't as old and clumsy as I am).

Quoted 4 lines

Maybe. I'd be happier if there was a single user included with a patch set doing this. I'm not sure this applies to any of the SoC ADCs which are MFD hosted but maybe it does.

I did a quick "grep powered audit" of the ADC drivers out of the curiosity. It seems to me that most of the platform drivers under ADC do have the of_match table populated. I suppose they have own node for the ADC stuff with ADC specific compatibles, so they're safe from this problem.

(I think we should still also consider cases where the ADC block does not have own compatible/node. This sure is just an opinion but I think it is kind of artificial to have own node for ADC block when it is just a part of a smallish device. Also, having multiple compatibles for one device feels "quirky" to me).

The exception to a rule seems to be the 'sun4i-gpadc-iio.c'

And, if I am not misreading the code, the thermal zone registration may be causing some problems. It seems to me the data of the thermal zone is allocated by the devm_iio_device_alloc() - and bound to the lifetime of the platform device.

The thermal zone in MFD branch is bound to the life time of the parent (MFD) device.

(in MFD case):

	info->sensor_device = pdev->dev.parent;

...

devm_thermal_of_zone_register(info->sensor_device, ...

I believe that releasing the IIO device will free the thermal zone data - but the thermal zone stays there until MFD is released. I haven't checked when the thermal zone uses the data though - but I'd be a bit wary of this design. Furthermore, removing and re-registering the IIO driver may cause some collision with the thermal zone which has stayed there. (Again, I didn't check the thermal zone implementation so I'm not sure this really is a problem).

In any case, it seems to me most of the current IIO ADC MFD devices have dt node "bound" to the platform device and don't use the parent node.

Yours,
-- Matti

author maintainer
#14 lore raw

PATCH v2 3/5 iio: adc: Support ROHM BD79124 ADC

In reply to Matti Vaittinen

The ROHM BD79124 is a 12-bit, 8-channel, SAR ADC. The ADC supports an automatic measurement mode, with an alarm interrupt for out-of-window measurements. The window is configurable for each channel.

The I2C protocol for manual start of the measurement and data reading is somewhat peculiar. It requires the master to do clock stretching after sending the I2C slave-address until the slave has captured the data. Needless to say this is not well suopported by the I2C controllers.

Thus the driver does not support the BD79124's manual measurement mode but implements the measurements using automatic measurement mode relying on the BD79124's ability of storing latest measurements into register.

The driver does also support configuring the threshold events for detecting the out-of-window events.

The BD79124 keeps asserting IRQ for as long as the measured voltage is out of the configured window. Thus the driver masks the received event for a fixed duration (1 second) when an event is handled. This prevents the user-space from choking on the events

The ADC input pins can be also configured as general purpose outputs. Those pins which don't have corresponding ADC channel node in the device-tree will be controllable as GPO.

---
Revision history:
RFC v1 => v2:
- Add event throttling (constant delay of 1 sec)
- rename variable 'd' to 'data'
- Use ADC helpers to detect pins used for ADC
- bd79124 drop MFD and pinmux && handle GPO in this driver
- Drop adc suffix from the IIO file name

---
 drivers/iio/adc/Kconfig        |   12 +
 drivers/iio/adc/Makefile       |    1 +
 drivers/iio/adc/rohm-bd79124.c | 1149 ++++++++++++++++++++++++++++++++
 3 files changed, 1162 insertions(+)
 create mode 100644 drivers/iio/adc/rohm-bd79124.c
drivers/iio/adc/Kconfig +12 -0
index 37b70a65da6f..a2a36a4ec644 100644
@@ -1191,6 +1191,18 @@ config RN5T618_ADC
 	  This driver can also be built as a module. If so, the module
 	  will be called rn5t618-adc.
 
+config ROHM_BD79124
+	tristate "Rohm BD79124 ADC driver"
+	depends on I2C
+	select REGMAP_I2C
+	select IIO_ADC_HELPER
+	help
+	  Say yes here to build support for the ROHM BD79124 ADC. The
+	  ROHM BD79124 is a 12-bit, 8-channel, SAR ADC. The ADC supports
+	  also an automatic measurement mode, with an alarm interrupt for
+	  out-of-window measurements. The window is configurable for each
+	  channel.
+
 config ROCKCHIP_SARADC
 	tristate "Rockchip SARADC driver"
 	depends on ARCH_ROCKCHIP || COMPILE_TEST
drivers/iio/adc/Makefile +1 -0
index 956c121a7544..43f159aba390 100644
@@ -108,6 +108,7 @@ obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o
 obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o
 obj-$(CONFIG_RICHTEK_RTQ6056) += rtq6056.o
 obj-$(CONFIG_RN5T618_ADC) += rn5t618-adc.o
+obj-$(CONFIG_ROHM_BD79124) += rohm-bd79124.o
 obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
 obj-$(CONFIG_RZG2L_ADC) += rzg2l_adc.o
 obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o
drivers/iio/adc/rohm-bd79124.c new file +1149 -0

1,149 lines not on this page · raw message

Signature · 1 line
2.48.1
#15 lore raw

Re: [PATCH v2 3/5] iio: adc: Support ROHM BD79124 ADC

kernel test robot noticed the following build warnings:

Hi Matti,

kernel test robot noticed the following build warnings:

[auto build test WARNING on 5bc55a333a2f7316b58edc7573e8e893f7acb532]

url:    https://github.com/intel-lab-lkp/linux/commits/Matti-Vaittinen/dt-bindings-ROHM-BD79124-ADC-GPO/20250205-214127
base:   5bc55a333a2f7316b58edc7573e8e893f7acb532
patch link:    https://lore.kernel.org/r/4781e1b1f074ca6c84ecc084b152885d08e826cc.1738761899.git.mazziesaccount%40gmail.com
patch subject: [PATCH v2 3/5] iio: adc: Support ROHM BD79124 ADC
config: i386-randconfig-004-20250207 (https://download.01.org/0day-ci/archive/20250207/[email protected]/config)
compiler: clang version 19.1.3 (https://github.com/llvm/llvm-project ab51eccf88f5321e7c60591c5546b254b6afab99)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250207/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All warnings (new ones prefixed by >>):

>> drivers/iio/adc/rohm-bd79124.c:1072:29: warning: variable 'ret' is uninitialized when used here [-Wuninitialized]
    1072 |                 return dev_err_probe(dev, ret, "Failed to get the Vdd\n");
         |                                           ^~~
   drivers/iio/adc/rohm-bd79124.c:1057:9: note: initialize the variable 'ret' to silence this warning
    1057 |         int ret;
         |                ^
         |                 = 0

1 warning generated.

vim +/ret +1072 drivers/iio/adc/rohm-bd79124.c

1049
1050 static int bd79124_probe(struct i2c_client *i2c)
1051 {
1052 struct bd79124_data *data;
1053 struct iio_dev *iio_dev;
1054 const struct iio_chan_spec *template;
1055 struct iio_chan_spec *cs;
1056 struct device *dev = &i2c->dev;
1057 int ret;
1058
1059 iio_dev = devm_iio_device_alloc(dev, sizeof(*data));
1060 if (!iio_dev)
1061 return -ENOMEM;
1062
1063 data = iio_priv(iio_dev);
1064 data->dev = dev;
1065 data->map = devm_regmap_init_i2c(i2c, &bd79124_regmap);
1066 if (IS_ERR(data->map))
1067 return dev_err_probe(dev, PTR_ERR(data->map),
1068 "Failed to initialize Regmap\n");
1069
1070 data->vmax = devm_regulator_get_enable_read_voltage(dev, "vdd");
1071 if (data->vmax < 0)

> 1072			return dev_err_probe(dev, ret, "Failed to get the Vdd\n");

1073
1074 ret = devm_regulator_get_enable(dev, "iovdd");
1075 if (ret < 0)
1076 return dev_err_probe(dev, ret, "Failed to enable I/O voltage\n");
1077
1078 ret = devm_delayed_work_autocancel(dev, &data->alm_enable_work,
1079 bd79124_alm_enable_worker);
1080 if (ret)
1081 return ret;
1082
1083 if (i2c->irq) {
1084 template = &bd79124_chan_template;
1085 } else {
1086 template = &bd79124_chan_template_noirq;
1087 dev_dbg(dev, "No IRQ found, events disabled\n");
1088 }
1089 ret = devm_iio_adc_device_alloc_chaninfo(dev, template, &cs);
1090 if (ret < 0)
1091 return ret;
1092
1093 iio_dev->channels = cs;
1094 iio_dev->num_channels = ret;
1095 iio_dev->info = &bd79124_info;
1096 iio_dev->name = "bd79124";
1097 iio_dev->modes = INDIO_DIRECT_MODE;
1098
1099 data->gc = bd79124gpo_chip;
1100 data->gc.parent = dev;
1101
1102 mutex_init(&data->mutex);
1103
1104 ret = bd79124_hw_init(data);
1105 if (ret)
1106 return ret;
1107
1108 ret = devm_gpiochip_add_data(data->dev, &data->gc, data);
1109 if (ret)
1110 return dev_err_probe(data->dev, ret, "gpio init Failed\n");
1111
1112 if (i2c->irq > 0) {
1113 ret = devm_request_threaded_irq(data->dev, i2c->irq,
1114 bd79124_irq_handler,
1115 &bd79124_event_handler, IRQF_ONESHOT,
1116 "adc-thresh-alert", iio_dev);
1117 if (ret)
1118 return dev_err_probe(data->dev, ret,
1119 "Failed to register IRQ\n");
1120 }
1121
1122 return devm_iio_device_register(data->dev, iio_dev);
1123 }
1124

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

Re: [PATCH v2 3/5] iio: adc: Support ROHM BD79124 ADC

In reply to Matti Vaittinen
Quoted 21 lines · On Wed, 5 Feb 2025 15:38:16 +0200 Matti Vaittinen <[email protected]>…

The ROHM BD79124 is a 12-bit, 8-channel, SAR ADC. The ADC supports an automatic measurement mode, with an alarm interrupt for out-of-window measurements. The window is configurable for each channel.

The I2C protocol for manual start of the measurement and data reading is somewhat peculiar. It requires the master to do clock stretching after sending the I2C slave-address until the slave has captured the data. Needless to say this is not well suopported by the I2C controllers.

Thus the driver does not support the BD79124's manual measurement mode but implements the measurements using automatic measurement mode relying on the BD79124's ability of storing latest measurements into register.

The driver does also support configuring the threshold events for detecting the out-of-window events.

The BD79124 keeps asserting IRQ for as long as the measured voltage is out of the configured window. Thus the driver masks the received event for a fixed duration (1 second) when an event is handled. This prevents the user-space from choking on the events

Quoted 5 lines

The ADC input pins can be also configured as general purpose outputs. Those pins which don't have corresponding ADC channel node in the device-tree will be controllable as GPO.

Hi Matti,

Just a few really trivial comments though this wasn't my most thorough of reviews as ran out of time / energy today!

Jonathan

Quoted 40 lines
drivers/iio/adc/rohm-bd79124.c new file +32 -0
new file mode 100644
index 000000000000..ea93762a24cc
@@ -0,0 +1,1149 @@
 
 
+static int bd79124_write_event_value(struct iio_dev *iio_dev,
+				     const struct iio_chan_spec *chan,
+				     enum iio_event_type type,
+				     enum iio_event_direction dir,
+				     enum iio_event_info info, int val,
+				     int val2)
+{
+	struct bd79124_data *data = iio_priv(iio_dev);
+	int reg;
+
+	if (chan->channel >= BD79124_MAX_NUM_CHANNELS)
+		return -EINVAL;
+
+	switch (info) {
+	case IIO_EV_INFO_VALUE:
+		if (dir == IIO_EV_DIR_RISING) {
+			guard(mutex)(&data->mutex);
+
+			data->alarm_r_limit[chan->channel] = val;
+			reg = BD79124_GET_HIGH_LIMIT_REG(chan->channel);
+		} else if (dir == IIO_EV_DIR_FALLING) {
+			guard(mutex)(&data->mutex);
+
+			data->alarm_f_limit[chan->channel] = val;
+			reg = BD79124_GET_LOW_LIMIT_REG(chan->channel);
+		} else {
+			return -EINVAL;
+		}
+		/*
+		 * We don't want to enable the alarm if it is not enabled or
+		 * if it is suppressed. In that case skip writing to the
+		 * register.

drivers/iio/adc/rohm-bd79124.c line 633

Quoted 10 lines
drivers/iio/adc/rohm-bd79124.c new file +10 -0
+		 */
+		if (!(data->alarm_monitored[chan->channel] & BIT(dir)) ||
+		    data->alarm_suppressed[chan->channel] & BIT(dir))
+			return 0;
+
+		return bd79124_write_int_to_reg(data, reg, val);
+
+	case IIO_EV_INFO_HYSTERESIS:
+			reg = BD79124_GET_HYSTERESIS_REG(chan->channel);
+			val >>= 3;

Odd indent.

Quoted 29 lines
drivers/iio/adc/rohm-bd79124.c new file +27 -0
+
+		return regmap_update_bits(data->map, reg, BD79124_MASK_HYSTERESIS,
+					  val);
+	default:
+		return -EINVAL;
+	}
+}
 
 
+static void bd79124_re_enable_lo(struct bd79124_data *data, unsigned int channel)
+{
+	int ret, evbit = BIT(IIO_EV_DIR_FALLING);
+
+	if (!(data->alarm_suppressed[channel] & evbit))
+		return;
+
+	data->alarm_suppressed[channel] &= (~evbit);
+
+	if (!(data->alarm_monitored[channel] & evbit))
+		return;
+
+	ret = bd79124_write_int_to_reg(data, BD79124_GET_LOW_LIMIT_REG(channel),
+				       data->alarm_f_limit[channel]);
+	if (ret)
+		dev_warn(data->dev, "Low limit enabling failed for channel%d\n",
+			 channel);
+}
+
+static void bd79124_re_enable_hi(struct bd79124_data *data, unsigned int channel)

drivers/iio/adc/rohm-bd79124.c line 785

Quoted 10 lines
drivers/iio/adc/rohm-bd79124.c new file +10 -0
+{
+	int ret, evbit = BIT(IIO_EV_DIR_RISING);
+
+	if (!(data->alarm_suppressed[channel] & evbit))
+		return;
+
+	data->alarm_suppressed[channel] &= (~evbit);
+
+	if (!(data->alarm_monitored[channel] & evbit))
+		return;

This lot is very similar to the lo variant. Can we combine them or use some helper for both?

Quoted 7 lines
drivers/iio/adc/rohm-bd79124.c new file +7 -0
+
+	ret = bd79124_write_int_to_reg(data, BD79124_GET_HIGH_LIMIT_REG(channel),
+				       data->alarm_r_limit[channel]);
+	if (ret)
+		dev_warn(data->dev, "High limit enabling failed for channel%d\n",
+			 channel);
+}
author maintainer
#17 lore raw

Re: [PATCH v2 3/5] iio: adc: Support ROHM BD79124 ADC

Quoted 28 lines · Jonathan Cameron
Quoted 25 lines · On Wed, 5 Feb 2025 15:38:16 +0200 Matti Vaittinen <[email protected]>…

The ROHM BD79124 is a 12-bit, 8-channel, SAR ADC. The ADC supports an automatic measurement mode, with an alarm interrupt for out-of-window measurements. The window is configurable for each channel.

The I2C protocol for manual start of the measurement and data reading is somewhat peculiar. It requires the master to do clock stretching after sending the I2C slave-address until the slave has captured the data. Needless to say this is not well suopported by the I2C controllers.

Thus the driver does not support the BD79124's manual measurement mode but implements the measurements using automatic measurement mode relying on the BD79124's ability of storing latest measurements into register.

The driver does also support configuring the threshold events for detecting the out-of-window events.

The BD79124 keeps asserting IRQ for as long as the measured voltage is out of the configured window. Thus the driver masks the received event for a fixed duration (1 second) when an event is handled. This prevents the user-space from choking on the events

The ADC input pins can be also configured as general purpose outputs. Those pins which don't have corresponding ADC channel node in the device-tree will be controllable as GPO.

Quoted 6 lines
Quoted 1 line

Hi Matti,

Just a few really trivial comments though this wasn't my most thorough of reviews as ran out of time / energy today!

Thanks :) I appreciate the time and energy invested here :)

Quoted 44 lines
Quoted 44 lines
drivers/iio/adc/rohm-bd79124.c new file +35 -0
new file mode 100644
index 000000000000..ea93762a24cc
@@ -0,0 +1,1149 @@
 
 
+static int bd79124_write_event_value(struct iio_dev *iio_dev,
+				     const struct iio_chan_spec *chan,
+				     enum iio_event_type type,
+				     enum iio_event_direction dir,
+				     enum iio_event_info info, int val,
+				     int val2)
+{
+	struct bd79124_data *data = iio_priv(iio_dev);
+	int reg;
+
+	if (chan->channel >= BD79124_MAX_NUM_CHANNELS)
+		return -EINVAL;
+
+	switch (info) {
+	case IIO_EV_INFO_VALUE:
+		if (dir == IIO_EV_DIR_RISING) {
+			guard(mutex)(&data->mutex);
+
+			data->alarm_r_limit[chan->channel] = val;
+			reg = BD79124_GET_HIGH_LIMIT_REG(chan->channel);
+		} else if (dir == IIO_EV_DIR_FALLING) {
+			guard(mutex)(&data->mutex);
+
+			data->alarm_f_limit[chan->channel] = val;
+			reg = BD79124_GET_LOW_LIMIT_REG(chan->channel);
+		} else {
+			return -EINVAL;
+		}
+		/*
+		 * We don't want to enable the alarm if it is not enabled or
+		 * if it is suppressed. In that case skip writing to the
+		 * register.
+		 */
+		if (!(data->alarm_monitored[chan->channel] & BIT(dir)) ||
+		    data->alarm_suppressed[chan->channel] & BIT(dir))
Quoted 8 lines

drivers/iio/adc/rohm-bd79124.c line 633

Quoted 7 lines
drivers/iio/adc/rohm-bd79124.c new file +7 -0
+			return 0;
+
+		return bd79124_write_int_to_reg(data, reg, val);
+
+	case IIO_EV_INFO_HYSTERESIS:
+			reg = BD79124_GET_HYSTERESIS_REG(chan->channel);
+			val >>= 3;

Odd indent.

Oh, indeed. Thanks!

Quoted 33 lines
Quoted 33 lines
drivers/iio/adc/rohm-bd79124.c new file +31 -0
+
+		return regmap_update_bits(data->map, reg, BD79124_MASK_HYSTERESIS,
+					  val);
+	default:
+		return -EINVAL;
+	}
+}
 
 
+static void bd79124_re_enable_lo(struct bd79124_data *data, unsigned int channel)
+{
+	int ret, evbit = BIT(IIO_EV_DIR_FALLING);
+
+	if (!(data->alarm_suppressed[channel] & evbit))
+		return;
+
+	data->alarm_suppressed[channel] &= (~evbit);
+
+	if (!(data->alarm_monitored[channel] & evbit))
+		return;
+
+	ret = bd79124_write_int_to_reg(data, BD79124_GET_LOW_LIMIT_REG(channel),
+				       data->alarm_f_limit[channel]);
+	if (ret)
+		dev_warn(data->dev, "Low limit enabling failed for channel%d\n",
+			 channel);
+}
+
+static void bd79124_re_enable_hi(struct bd79124_data *data, unsigned int channel)
+{
+	int ret, evbit = BIT(IIO_EV_DIR_RISING);
+
+	if (!(data->alarm_suppressed[channel] & evbit))
Quoted 8 lines

drivers/iio/adc/rohm-bd79124.c line 766

Quoted 6 lines
drivers/iio/adc/rohm-bd79124.c new file +6 -0
+		return;
+
+	data->alarm_suppressed[channel] &= (~evbit);
+
+	if (!(data->alarm_monitored[channel] & evbit))
+		return;

This lot is very similar to the lo variant. Can we combine them or use some helper for both?

Initially I did this.

But the code looked a bit dull because the evbitm, alarm-limit array and prints depend on the direction. Furthermore, the caller already knows the direction (as the caller does also handle directions separately), so doing:

foo(dir)

{
	if (dir == bar)
		...
	else
		...
}

...

if (dir == bar)

	foo(dir);
else
	foo(dir);

started to feel just a bit, meh. Hence I separated the stuff to own _lo and _hi functions.

Quoted 8 lines

drivers/iio/adc/rohm-bd79124.c line 792

Quoted 7 lines
drivers/iio/adc/rohm-bd79124.c new file +7 -0
+
+	ret = bd79124_write_int_to_reg(data, BD79124_GET_HIGH_LIMIT_REG(channel),
+				       data->alarm_r_limit[channel]);
+	if (ret)
+		dev_warn(data->dev, "High limit enabling failed for channel%d\n",
+			 channel);
+}

Yours,
-- Matti

maintainer
#18 lore raw

Re: [PATCH v2 3/5] iio: adc: Support ROHM BD79124 ADC

Quoted 33 lines
Quoted 15 lines

drivers/iio/adc/rohm-bd79124.c line 785

Quoted 13 lines
drivers/iio/adc/rohm-bd79124.c new file +12 -0
+
+static void bd79124_re_enable_hi(struct bd79124_data *data, unsigned int channel)
+{
+	int ret, evbit = BIT(IIO_EV_DIR_RISING);
+
+	if (!(data->alarm_suppressed[channel] & evbit))
+		return;
+
+	data->alarm_suppressed[channel] &= (~evbit);
+
+	if (!(data->alarm_monitored[channel] & evbit))
+		return;  

This lot is very similar to the lo variant. Can we combine them or use some helper for both?

Initially I did this.

But the code looked a bit dull because the evbitm, alarm-limit array and prints depend on the direction. Furthermore, the caller already knows the direction (as the caller does also handle directions separately), so doing:

foo(dir)

{
	if (dir == bar)
		...
	else
		...
}

...

Quoted 7 lines

if (dir == bar)

	foo(dir);
else
	foo(dir);

started to feel just a bit, meh. Hence I separated the stuff to own _lo and _hi functions.

Hmm. I was thinking of something like

static void bd79124_re_enable_xx(struct bd79124_data *data, unsigned int channel,
unsigned int reg, u16 limit,
enum iio_event_dir dir)

{
	int ret, evbit = BIT(dir);

	if (!(data->alarm_suppressed[channel] & evbit))
		return;

	data->alarm_suppressed[channel] &= (~evbit);

	if (!(data->alarm_monitored[channel] & evbit))
		return;

	ret = bd79124_write_int_to_reg(data, reg, limit);
	if (ret)
		dev_warn(data->dev, "Low limit enabling failed for channel%d\n",
			 channel);
}

static void bd71924_reenable_hi(struct bd79124_data *data, unsigned int channel)

{
	return bd71924_reenable_x(data, channel, BD79124_GET_HIGH_LIMIT_REG(channel)
				  data->alarm_r_limit[channel], IIO_EV_DIR_RISING);
}

static void bd71924_reenable_lo(struct bd79124_data *data, unsigned int channel)

{
	return bd71924_reenable_x(data, channel, BD79124_GET_LOW_LIMIT_REG(channel)
				  data->alarm_f_limit[channel], IIO_EV_DIR_FALLING);
}

But I guess not really a saving in the end.

Quoted 15 lines
Quoted 10 lines

drivers/iio/adc/rohm-bd79124.c line 792

Quoted 9 lines
drivers/iio/adc/rohm-bd79124.c new file +7 -0
+
+	ret = bd79124_write_int_to_reg(data, BD79124_GET_HIGH_LIMIT_REG(channel),
+				       data->alarm_r_limit[channel]);
+	if (ret)
+		dev_warn(data->dev, "High limit enabling failed for channel%d\n",
+			 channel);
+}  

Yours,
-- Matti

author maintainer
#19 lore raw

PATCH v2 4/5 MAINTAINERS: Add IIO ADC helpers

In reply to Matti Vaittinen

Add undersigned as a maintainer for the IIO ADC helpers.

---
Revision history:
RFC v1 => v2:
- New patch

---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)
MAINTAINERS +7 -0
index a87ddad78e26..bfe2f53fa74d 100644
@@ -11099,6 +11099,13 @@ L:	[email protected]
 S:	Maintained
 F:	drivers/media/rc/iguanair.c
 
+IIO ADC HELPERS
+M:	Matti Vaittinen <[email protected]>
+L:	[email protected]
+S:	Maintained
+F:	drivers/iio/adc/industrialio-adc.c
+F:	include/linux/iio/adc-helpers.h
+
 IIO BACKEND FRAMEWORK
 M:	Nuno Sa <[email protected]>
 R:	Olivier Moysan <[email protected]>
Signature · 1 line
2.48.1
author maintainer
#20 lore raw

PATCH v2 5/5 MAINTAINERS: Add ROHM BD79124 ADC/GPO

In reply to Matti Vaittinen

Add undersigned as a maintainer for the ROHM BD79124 ADC/GPO driver.

---
Revision history:
RFC v1 => v2:
- Drop MFD and pinmux drivers

---
 MAINTAINERS | 5 +++++
 1 file changed, 5 insertions(+)
MAINTAINERS +5 -0
index bfe2f53fa74d..2021327e665e 100644
@@ -20299,6 +20299,11 @@ S:	Supported
 F:	drivers/power/supply/bd99954-charger.c
 F:	drivers/power/supply/bd99954-charger.h
 
+ROHM BD79124 ADC / GPO IC
+M:	Matti Vaittinen <[email protected]>
+S:	Supported
+F:	drivers/iio/adc/rohm-bd79124.c
+
 ROHM BH1745 COLOUR SENSOR
 M:	Mudit Sharma <[email protected]>
 L:	[email protected]
Signature · 1 line
2.48.1