linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 1/5] iio: add power and energy measurement modifiers
@ 2025-07-21 11:24 Antoniu Miclaus
  2025-07-21 11:24 ` [PATCH v2 2/5] dt-bindings: iio: adc: add ade9000 Antoniu Miclaus
                   ` (4 more replies)
  0 siblings, 5 replies; 12+ messages in thread
From: Antoniu Miclaus @ 2025-07-21 11:24 UTC (permalink / raw)
  To: jic23, robh, conor+dt, devicetree, linux-iio, linux-kernel
  Cc: Antoniu Miclaus

Add new IIO modifiers to support power and energy measurement devices:

Power modifiers:
- IIO_MOD_ACTIVE: Real power consumed by the load
- IIO_MOD_REACTIVE: Power that oscillates between source and load
- IIO_MOD_APPARENT: Magnitude of complex power
- IIO_MOD_FUND_REACTIVE: Reactive power at fundamental frequency
- IIO_MOD_FACTOR: Power factor (ratio of active to apparent power)

Signal quality modifiers:
- IIO_MOD_RMS: Root Mean Square value

These modifiers enable proper representation of power measurement
devices like energy meters and power analyzers.

Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
---
changes in v2:
 - drop _accum modifiers
 - drop dip/swell modifiers
 - change power factor to be a chan_info
 - voltage -> altvoltage for rms
 Documentation/ABI/testing/sysfs-bus-iio | 14 ++++++++++++++
 drivers/iio/industrialio-core.c         |  5 +++++
 include/linux/iio/types.h               |  1 +
 include/uapi/linux/iio/types.h          |  4 ++++
 4 files changed, 24 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 3bc386995fb6..0948611227a8 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -169,7 +169,12 @@ Description:
 		is required is a consistent labeling.  Units after application
 		of scale and offset are millivolts.
 
+What:		/sys/bus/iio/devices/iio:deviceX/in_altvoltageY_rms_raw
+
 What:		/sys/bus/iio/devices/iio:deviceX/in_powerY_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_powerY_active_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_powerY_reactive_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_powerY_apparent_raw
 KernelVersion:	4.5
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -178,6 +183,8 @@ Description:
 		unique to allow association with event codes. Units after
 		application of scale and offset are milliwatts.
 
+What:		/sys/bus/iio/devices/iio:deviceX/in_powerY_power_factor
+
 What:		/sys/bus/iio/devices/iio:deviceX/in_capacitanceY_raw
 KernelVersion:	3.2
 Contact:	linux-iio@vger.kernel.org
@@ -1593,6 +1600,12 @@ Description:
 
 What:		/sys/.../iio:deviceX/in_energy_input
 What:		/sys/.../iio:deviceX/in_energy_raw
+What:		/sys/.../iio:deviceX/in_energyY_active_raw
+What:		/sys/.../iio:deviceX/in_energyY_reactive_raw
+What:		/sys/.../iio:deviceX/in_energyY_apparent_raw
+What:		/sys/.../iio:deviceX/in_energyY_active_accum_raw
+What:		/sys/.../iio:deviceX/in_energyY_reactive_accum_raw
+What:		/sys/.../iio:deviceX/in_energyY_apparent_accum_raw
 KernelVersion:	4.0
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -1718,6 +1731,7 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_currentY_raw
 What:		/sys/bus/iio/devices/iio:deviceX/in_currentY_supply_raw
 What:		/sys/bus/iio/devices/iio:deviceX/in_currentY_i_raw
 What:		/sys/bus/iio/devices/iio:deviceX/in_currentY_q_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_currentY_rms_raw
 KernelVersion:	3.17
 Contact:	linux-iio@vger.kernel.org
 Description:
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index f13c3aa470d7..7889f8b62336 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -152,6 +152,10 @@ static const char * const iio_modifier_names[] = {
 	[IIO_MOD_PITCH] = "pitch",
 	[IIO_MOD_YAW] = "yaw",
 	[IIO_MOD_ROLL] = "roll",
+	[IIO_MOD_RMS] = "rms",
+	[IIO_MOD_ACTIVE] = "active",
+	[IIO_MOD_REACTIVE] = "reactive",
+	[IIO_MOD_APPARENT] = "apparent",
 };
 
 /* relies on pairs of these shared then separate */
@@ -189,6 +193,7 @@ static const char * const iio_chan_info_postfix[] = {
 	[IIO_CHAN_INFO_ZEROPOINT] = "zeropoint",
 	[IIO_CHAN_INFO_TROUGH] = "trough_raw",
 	[IIO_CHAN_INFO_CONVDELAY] = "convdelay",
+	[IIO_CHAN_INFO_POWER_FACTOR] = "power_factor",
 };
 /**
  * iio_device_id() - query the unique ID for the device
diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
index ad2761efcc83..f26a0fbd6ab4 100644
--- a/include/linux/iio/types.h
+++ b/include/linux/iio/types.h
@@ -70,6 +70,7 @@ enum iio_chan_info_enum {
 	IIO_CHAN_INFO_ZEROPOINT,
 	IIO_CHAN_INFO_TROUGH,
 	IIO_CHAN_INFO_CONVDELAY,
+	IIO_CHAN_INFO_POWER_FACTOR,
 };
 
 #endif /* _IIO_TYPES_H_ */
diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
index 3eb0821af7a4..0afda9ec2379 100644
--- a/include/uapi/linux/iio/types.h
+++ b/include/uapi/linux/iio/types.h
@@ -108,6 +108,10 @@ enum iio_modifier {
 	IIO_MOD_ROLL,
 	IIO_MOD_LIGHT_UVA,
 	IIO_MOD_LIGHT_UVB,
+	IIO_MOD_RMS,
+	IIO_MOD_ACTIVE,
+	IIO_MOD_REACTIVE,
+	IIO_MOD_APPARENT,
 };
 
 enum iio_event_type {
-- 
2.49.0


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

* [PATCH v2 2/5] dt-bindings: iio: adc: add ade9000
  2025-07-21 11:24 [PATCH v2 1/5] iio: add power and energy measurement modifiers Antoniu Miclaus
@ 2025-07-21 11:24 ` Antoniu Miclaus
  2025-07-21 14:23   ` Rob Herring (Arm)
                     ` (2 more replies)
  2025-07-21 11:24 ` [PATCH v2 3/5] iio: adc: add ade9000 support Antoniu Miclaus
                   ` (3 subsequent siblings)
  4 siblings, 3 replies; 12+ messages in thread
From: Antoniu Miclaus @ 2025-07-21 11:24 UTC (permalink / raw)
  To: jic23, robh, conor+dt, devicetree, linux-iio, linux-kernel
  Cc: Antoniu Miclaus

Add devicetree bindings support for ade9000.

Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
---
changes in v2:
 - move interrup-names near interrupts
 - remove properties related to the waveform buffer and make them runtime attributes
 - remove egy-time property and make it a runtime attribute.
 - remove wf-src and use filter_type in the driver.
 - add vref, vdd support.
 - use adc standard channels instead of phase channels.
 .../bindings/iio/adc/adi,ade9000.yaml         | 122 ++++++++++++++++++
 1 file changed, 122 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml

diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
new file mode 100644
index 000000000000..0176252aae35
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
@@ -0,0 +1,122 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright 2025 Analog Devices Inc.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/bindings/iio/adc/adi,ade9000.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices ADE9000 High Performance, Polyphase Energy Metering driver
+
+maintainers:
+  - Antoniu Miclaus <antoniu.miclaus@analog.com>
+
+description: |
+  The ADE9000 s a highly accurate, fully integrated, multiphase energy and power
+  quality monitoring device. Superior analog performance and a digital signal
+  processing (DSP) core enable accurate energy monitoring over a wide dynamic
+  range. An integrated high end reference ensures low drift over temperature
+  with a combined drift of less than ±25 ppm/°C maximum for the entire channel
+  including a programmable gain amplifier (PGA) and an analog-to- digital
+  converter (ADC).
+
+  https://www.analog.com/media/en/technical-documentation/data-sheets/ADE9000.pdf
+
+$ref: /schemas/spi/spi-peripheral-props.yaml#
+
+properties:
+  compatible:
+    enum:
+      - adi,ade9000
+
+  reg:
+    maxItems: 1
+
+  '#address-cells':
+    const: 1
+
+  '#size-cells':
+    const: 0
+
+  spi-max-frequency:
+    maximum: 20000000
+
+  interrupts:
+    maxItems: 2
+
+  interrupt-names:
+    items:
+      - const: irq0
+      - const: irq1
+
+  reset-gpios:
+    description: |
+      Must be the device tree identifier of the RESET pin. As the line is
+      active low, it should be marked GPIO_ACTIVE_LOW.
+    maxItems: 1
+
+  vdd-supply: true
+
+  vref-supply: true
+
+patternProperties:
+  "^channel@[0-2]$":
+    $ref: /schemas/iio/adc/adc.yaml#
+    type: object
+
+    properties:
+      reg:
+        description: The channel number (0-2 for phases A, B, C)
+        minimum: 0
+        maximum: 2
+
+    required:
+      - reg
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - reset-gpios
+  - interrupts
+  - interrupt-names
+  - vdd-supply
+
+allOf:
+  - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    spi {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      adc@0 {
+          compatible = "adi,ade9000";
+          reg = <0>;
+          spi-max-frequency = <7000000>;
+
+          #address-cells = <1>;
+          #size-cells = <0>;
+          reset-gpios = <&gpio 4 GPIO_ACTIVE_LOW>;
+          interrupts = <2 IRQ_TYPE_EDGE_FALLING>, <3 IRQ_TYPE_EDGE_FALLING>;
+          interrupt-names = "irq0", "irq1";
+          interrupt-parent = <&gpio>;
+          vdd-supply = <&vdd_reg>;
+
+          channel@0 {
+            reg = <0>;
+          };
+          channel@1 {
+            reg = <1>;
+          };
+          channel@2 {
+            reg = <2>;
+          };
+      };
+    };
-- 
2.49.0


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

* [PATCH v2 3/5] iio: adc: add ade9000 support
  2025-07-21 11:24 [PATCH v2 1/5] iio: add power and energy measurement modifiers Antoniu Miclaus
  2025-07-21 11:24 ` [PATCH v2 2/5] dt-bindings: iio: adc: add ade9000 Antoniu Miclaus
@ 2025-07-21 11:24 ` Antoniu Miclaus
  2025-07-27 14:10   ` Jonathan Cameron
  2025-07-21 11:24 ` [PATCH v2 4/5] Documentation: ABI: iio: add sinc4+iir and dsp types Antoniu Miclaus
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: Antoniu Miclaus @ 2025-07-21 11:24 UTC (permalink / raw)
  To: jic23, robh, conor+dt, devicetree, linux-iio, linux-kernel
  Cc: Antoniu Miclaus

Add driver support for the ade9000. highly accurate,
fully integrated, multiphase energy and power quality
monitoring device.

Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
---
changes in v2:
 - fix bot detected issues
 - update driver based on the changes made in the bindings
 drivers/iio/adc/Kconfig   |   19 +
 drivers/iio/adc/Makefile  |    2 +
 drivers/iio/adc/ade9000.c | 2290 +++++++++++++++++++++++++++++++++++++
 3 files changed, 2311 insertions(+)
 create mode 100644 drivers/iio/adc/ade9000.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 7ad58ca5173f..977ad9c839c7 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -489,6 +489,25 @@ config AD9467
 	  To compile this driver as a module, choose M here: the module will be
 	  called ad9467.
 
+config ADE9000
+	tristate "Analog Devices ADE9000 Multiphase Energy, and Power Quality Monitoring IC Driver"
+	depends on SPI
+	select REGMAP_SPI
+	select IIO_BUFFER
+	select IIO_KFIFO_BUF
+	help
+	  Say yes here to build support for the Analog Devices ADE9000,
+	  a highly accurate, multiphase energy and power quality monitoring
+	  integrated circuit.
+
+	  The device features high-precision analog-to-digital converters
+	  and digital signal processing to compute RMS values, power factor,
+	  frequency, and harmonic analysis. It supports SPI communication
+	  and provides buffered data output through the IIO framework.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called ade9000.
+
 config ADI_AXI_ADC
 	tristate "Analog Devices Generic AXI ADC IP core driver"
 	depends on MICROBLAZE || NIOS2 || ARCH_ZYNQ || ARCH_ZYNQMP || ARCH_INTEL_SOCFPGA || COMPILE_TEST
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 15c60544a475..ed25aa838af3 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_AD7091R5) += ad7091r5.o
 obj-$(CONFIG_AD7091R8) += ad7091r8.o
 obj-$(CONFIG_AD7124) += ad7124.o
 obj-$(CONFIG_AD7173) += ad7173.o
+obj-$(CONFIG_ADE9000) += ade9000.o
 obj-$(CONFIG_AD7191) += ad7191.o
 obj-$(CONFIG_AD7192) += ad7192.o
 obj-$(CONFIG_AD7266) += ad7266.o
@@ -45,6 +46,7 @@ obj-$(CONFIG_AD7944) += ad7944.o
 obj-$(CONFIG_AD7949) += ad7949.o
 obj-$(CONFIG_AD799X) += ad799x.o
 obj-$(CONFIG_AD9467) += ad9467.o
+obj-$(CONFIG_ADE9000) += ade9000.o
 obj-$(CONFIG_ADI_AXI_ADC) += adi-axi-adc.o
 obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o
 obj-$(CONFIG_AT91_ADC) += at91_adc.o
diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c
new file mode 100644
index 000000000000..aa4f0f635434
--- /dev/null
+++ b/drivers/iio/adc/ade9000.c
@@ -0,0 +1,2290 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/**
+ * ADE9000 driver
+ *
+ * Copyright 2025 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/events.h>
+#include <linux/iio/sysfs.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/unaligned.h>
+
+/* Address of ADE9000 registers */
+#define	ADE9000_REG_AIGAIN		0x000
+#define	ADE9000_REG_AVGAIN		0x00B
+#define	ADE9000_REG_AIRMSOS		0x00C
+#define	ADE9000_REG_AVRMSOS		0x00D
+#define	ADE9000_REG_APGAIN		0x00E
+#define	ADE9000_REG_AWATTOS		0x00F
+#define	ADE9000_REG_AVAROS		0x010
+#define	ADE9000_REG_AFVAROS		0x012
+#define	ADE9000_REG_CONFIG0		0x060
+#define	ADE9000_REG_DICOEFF		0x072
+#define	ADE9000_REG_AI_PCF		0x20A
+#define	ADE9000_REG_AV_PCF		0x20B
+#define	ADE9000_REG_AIRMS		0x20C
+#define	ADE9000_REG_AVRMS		0x20D
+#define	ADE9000_REG_AWATT		0x210
+#define	ADE9000_REG_AVAR		0x211
+#define	ADE9000_REG_AVA			0x212
+#define ADE9000_REG_AFVAR		0x214
+#define	ADE9000_REG_APF			0x216
+#define	ADE9000_REG_BI_PCF		0x22A
+#define	ADE9000_REG_BV_PCF		0x22B
+#define	ADE9000_REG_BIRMS		0x22C
+#define	ADE9000_REG_BVRMS		0x22D
+#define	ADE9000_REG_CI_PCF		0x24A
+#define	ADE9000_REG_CV_PCF		0x24B
+#define	ADE9000_REG_CIRMS		0x24C
+#define	ADE9000_REG_CVRMS		0x24D
+#define	ADE9000_REG_AWATT_ACC		0x2E5
+#define ADE9000_REG_AWATTHR_LO		0x2E6
+#define ADE9000_REG_AVAHR_LO		0x2FA
+#define ADE9000_REG_AFVARHR_LO		0x30E
+#define ADE9000_REG_BWATTHR_LO		0x322
+#define ADE9000_REG_BVAHR_LO		0x336
+#define ADE9000_REG_BFVARHR_LO		0x34A
+#define ADE9000_REG_CWATTHR_LO		0x35E
+#define ADE9000_REG_CVAHR_LO		0x372
+#define ADE9000_REG_CFVARHR_LO		0x386
+#define	ADE9000_REG_STATUS0		0x402
+#define	ADE9000_REG_STATUS1		0x403
+#define	ADE9000_REG_MASK0		0x405
+#define	ADE9000_REG_MASK1		0x406
+#define	ADE9000_REG_EVENT_MASK		0x407
+#define	ADE9000_REG_VLEVEL		0x40F
+#define	ADE9000_REG_DIP_LVL		0x410
+#define	ADE9000_REG_DIPA		0x411
+#define	ADE9000_REG_DIPB		0x412
+#define	ADE9000_REG_DIPC		0x413
+#define	ADE9000_REG_SWELL_LVL		0x414
+#define	ADE9000_REG_SWELLA		0x415
+#define	ADE9000_REG_SWELLB		0x416
+#define	ADE9000_REG_SWELLC		0x417
+#define ADE9000_REG_APERIOD		0x418
+#define ADE9000_REG_BPERIOD		0x419
+#define ADE9000_REG_CPERIOD		0x41A
+#define	ADE9000_REG_RUN			0x480
+#define ADE9000_REG_CONFIG1		0x481
+#define	ADE9000_REG_ACCMODE		0x492
+#define	ADE9000_REG_CONFIG3		0x493
+#define ADE9000_REG_ZXTOUT		0x498
+#define	ADE9000_REG_ZX_LP_SEL		0x49A
+#define	ADE9000_REG_WFB_CFG		0x4A0
+#define	ADE9000_REG_WFB_PG_IRQEN	0x4A1
+#define	ADE9000_REG_WFB_TRG_CFG		0x4A2
+#define	ADE9000_REG_WFB_TRG_STAT	0x4A3
+#define	ADE9000_REG_CONFIG2		0x4AF
+#define	ADE9000_REG_EP_CFG		0x4B0
+#define	ADE9000_REG_EGY_TIME		0x4B2
+#define	ADE9000_REG_PGA_GAIN		0x4B9
+#define	ADE9000_REG_VERSION		0x4FE
+#define ADE9000_REG_WF_BUFF		0x800
+#define ADE9000_REG_WF_HALF_BUFF	0xC00
+
+#define ADE9000_REG_ADDR_MASK		GENMASK(15, 4)
+#define ADE9000_REG_READ_BIT_MASK	BIT(3)
+#define ADE9000_RX_DEPTH		6
+#define ADE9000_TX_DEPTH		10
+
+#define ADE9000_WF_CAP_EN_MASK		BIT(4)
+#define ADE9000_WF_CAP_SEL_MASK		BIT(5)
+#define ADE9000_WF_MODE_MASK		GENMASK(7, 6)
+#define ADE9000_WF_SRC_MASK		GENMASK(9, 8)
+#define ADE9000_WF_IN_EN_MASK		BIT(12)
+
+/* External reference selection bit in CONFIG1 */
+#define ADE9000_EXT_REF_MASK		BIT(15)
+
+/*
+ * Configuration registers
+ */
+#define ADE9000_PGA_GAIN		0x0000
+
+/* Default configuration */
+
+#define ADE9000_CONFIG0			0x00000000
+
+/* CF3/ZX pin outputs Zero crossing */
+#define ADE9000_CONFIG1			0x0002
+
+//TODO: This looks like it could be better expressed in terms
+//of defines for the fields contained in this register.
+//Same for many of the ones that follow.
+
+/* Default High pass corner frequency of 1.25Hz */
+#define ADE9000_CONFIG2			0x0A00
+
+/* Peak and overcurrent detection disabled */
+#define ADE9000_CONFIG3			0x0000
+
+/*
+ * 50Hz operation, 3P4W Wye configuration, signed accumulation
+ * Clear bit 8 i.e. ACCMODE=0x00xx for 50Hz operation
+ * ACCMODE=0x0x9x for 3Wire delta when phase B is used as reference
+ */
+#define ADE9000_ACCMODE			0x0000
+#define ADE9000_ACCMODE_60HZ		0x0100
+
+/*Line period and zero crossing obtained from VA */
+#define ADE9000_ZX_LP_SEL		0x0000
+
+/* Disable all interrupts except EGYRDY */
+#define ADE9000_MASK0			0x00000001
+
+/* Disable all interrupts */
+#define ADE9000_MASK1			0x00000000
+
+/* Events disabled */
+#define ADE9000_EVENT_MASK		0x00000000
+
+/*
+ * Assuming Vnom=1/2 of full scale.
+ * Refer to Technical reference manual for detailed calculations.
+ */
+#define ADE9000_VLEVEL			0x0022EA28
+
+/* Set DICOEFF= 0xFFFFE000 when integrator is enabled */
+#define ADE9000_DICOEFF			0x00000000
+
+/* DSP ON */
+#define ADE9000_RUN_ON			0xFFFFFFFF
+
+/*
+ * Energy Accumulation Settings
+ * Enable energy accumulation, accumulate samples at 8ksps
+ * latch energy accumulation after EGYRDY
+ * If accumulation is changed to half line cycle mode, change EGY_TIME
+ */
+#define ADE9000_EP_CFG			0x0011
+
+/* Accumulate 4000 samples */
+#define ADE9000_EGY_TIME		7999
+
+/*
+ * Constant Definitions
+ * ADE9000 FDSP: 8000sps, ADE9000 FDSP: 4000sps
+ */
+#define ADE9000_FDSP			4000
+#define ADE9000_WFB_CFG			0x0329
+#define ADE9000_WFB_PAGE_SIZE		128
+#define ADE9000_WFB_NR_OF_PAGES		16
+#define ADE9000_WFB_MAX_CHANNELS	8
+#define ADE9000_WFB_BYTES_IN_SAMPLE	4
+#define ADE9000_WFB_SAMPLES_IN_PAGE	\
+	(ADE9000_WFB_PAGE_SIZE / ADE9000_WFB_MAX_CHANNELS)
+#define ADE9000_WFB_MAX_SAMPLES_CHAN	\
+	(ADE9000_WFB_SAMPLES_IN_PAGE * ADE9000_WFB_NR_OF_PAGES)
+#define ADE9000_WFB_FULL_BUFF_NR_SAMPLES \
+	(ADE9000_WFB_PAGE_SIZE * ADE9000_WFB_NR_OF_PAGES)
+#define ADE9000_WFB_FULL_BUFF_SIZE	\
+	(ADE9000_WFB_FULL_BUFF_NR_SAMPLES * ADE9000_WFB_BYTES_IN_SAMPLE)
+
+#define ADE9000_SWRST_BIT		BIT(0)
+
+/* Status and Mask register bits*/
+#define ADE9000_ST0_WFB_TRIG_BIT	BIT(16)
+#define ADE9000_ST0_PAGE_FULL_BIT	BIT(17)
+#define ADE9000_ST0_EGYRDY		BIT(0)
+
+#define ADE9000_ST1_ZXTOVA_BIT		BIT(6)
+#define ADE9000_ST1_ZXTOVB_BIT		BIT(7)
+#define ADE9000_ST1_ZXTOVC_BIT		BIT(8)
+#define ADE9000_ST1_ZXVA_BIT		BIT(9)
+#define ADE9000_ST1_ZXVB_BIT		BIT(10)
+#define ADE9000_ST1_ZXVC_BIT		BIT(11)
+#define ADE9000_ST1_ZXIA_BIT		BIT(13)
+#define ADE9000_ST1_ZXIB_BIT		BIT(14)
+#define ADE9000_ST1_ZXIC_BIT		BIT(15)
+#define ADE9000_ST1_RSTDONE_BIT		BIT(16)
+#define ADE9000_ST1_SEQERR_BIT		BIT(18)
+#define ADE9000_ST1_SWELLA_BIT		BIT(20)
+#define ADE9000_ST1_SWELLB_BIT		BIT(21)
+#define ADE9000_ST1_SWELLC_BIT		BIT(22)
+#define ADE9000_ST1_DIPA_BIT		BIT(23)
+#define ADE9000_ST1_DIPB_BIT		BIT(24)
+#define ADE9000_ST1_DIPC_BIT		BIT(25)
+#define ADE9000_ST1_ERROR0_BIT		BIT(28)
+#define ADE9000_ST1_ERROR1_BIT		BIT(29)
+#define ADE9000_ST1_ERROR2_BIT		BIT(30)
+#define ADE9000_ST1_ERROR3_BIT		BIT(31)
+#define ADE9000_ST_ERROR \
+	(ADE9000_ST1_ERROR0 | \
+	 ADE9000_ST1_ERROR1 | \
+	 ADE9000_ST1_ERROR2 | \
+	 ADE9000_ST1_ERROR3)
+#define ADE9000_ST1_CROSSING_FIRST	6
+#define ADE9000_ST1_CROSSING_DEPTH	25
+
+#define ADE9000_WFB_TRG_DIP_BIT		BIT(0)
+#define ADE9000_WFB_TRG_SWELL_BIT	BIT(1)
+#define ADE9000_WFB_TRG_ZXIA_BIT	BIT(3)
+#define ADE9000_WFB_TRG_ZXIB_BIT	BIT(4)
+#define ADE9000_WFB_TRG_ZXIC_BIT	BIT(5)
+#define ADE9000_WFB_TRG_ZXVA_BIT	BIT(6)
+#define ADE9000_WFB_TRG_ZXVB_BIT	BIT(7)
+#define ADE9000_WFB_TRG_ZXVC_BIT	BIT(8)
+
+/* Stop when waveform buffer is full */
+#define ADE9000_WFB_FULL_MODE		0x0
+/* Continuous fill—stop only on enabled trigger events */
+#define ADE9000_WFB_EN_TRIG_MODE	0x1
+/* Continuous filling—center capture around enabled trigger events */
+#define ADE9000_WFB_C_EN_TRIG_MODE	0x2
+/* Continuous fill—used as streaming mode for continuous data output */
+#define ADE9000_WFB_STREAMING_MODE	0x3
+
+#define ADE9000_LAST_PAGE_BIT		BIT(15)
+#define ADE9000_MIDDLE_PAGE_BIT		BIT(7)
+
+/*
+ * Full scale Codes referred from Datasheet.Respective digital codes are
+ * produced when ADC inputs are at full scale. Do not Change.
+ */
+#define ADE9000_RMS_FULL_SCALE_CODES	52866837
+#define ADE9000_WATT_FULL_SCALE_CODES	20694066
+#define ADE9000_PCF_FULL_SCALE_CODES	74770000
+
+/* Phase and channel definitions */
+#define ADE9000_PHASE_A_NR		0
+#define ADE9000_PHASE_B_NR		1
+#define ADE9000_PHASE_C_NR		2
+
+#define ADE9000_SCAN_POS_IA		BIT(0)
+#define ADE9000_SCAN_POS_VA		BIT(1)
+#define ADE9000_SCAN_POS_IB		BIT(2)
+#define ADE9000_SCAN_POS_VB		BIT(3)
+#define ADE9000_SCAN_POS_IC		BIT(4)
+#define ADE9000_SCAN_POS_VC		BIT(5)
+#define ADE9000_SCAN_POS_ALL \
+	(ADE9000_SCAN_POS_IA | \
+	 ADE9000_SCAN_POS_VA | \
+	 ADE9000_SCAN_POS_IB | \
+	 ADE9000_SCAN_POS_VB | \
+	 ADE9000_SCAN_POS_IC | \
+	 ADE9000_SCAN_POS_VC)
+
+#define ADE9000_PHASE_B_POS_BIT		BIT(5)
+#define ADE9000_PHASE_C_POS_BIT		BIT(6)
+
+#define ADE9000_MAX_PHASE_NR		3
+#define AD9000_CHANNELS_PER_PHASE	10
+
+#define ADE9000_ADDR_ADJUST(addr, chan)					\
+	(((chan) == 0 ? 0 : (chan) == 1 ? 2 : 4) << 4 | (addr))
+
+struct ade9000_state {
+	bool rst_done;
+	u8 wf_mode;
+	u8 wf_src;
+	u32 wfb_trg;
+	u8 wfb_nr_activ_chan;
+	u32 wfb_nr_samples;
+	struct spi_device *spi;
+	u8 *tx;
+	u8 *rx;
+	struct spi_transfer xfer[2];
+	struct spi_message spi_msg;
+	struct regmap *regmap;
+	union{
+		u8 byte[ADE9000_WFB_FULL_BUFF_SIZE];
+		__be32 word[ADE9000_WFB_FULL_BUFF_NR_SAMPLES];
+	} rx_buff __aligned(IIO_DMA_MINALIGN);
+	u8 tx_buff[2] __aligned(IIO_DMA_MINALIGN);
+};
+
+static const struct iio_event_spec ade9000_events[] = {
+	{
+		.type = IIO_EV_TYPE_MAG,
+		.dir = IIO_EV_DIR_NONE,
+		.mask_shared_by_all = BIT(IIO_EV_INFO_ENABLE),
+	},
+	{
+		.type = IIO_EV_TYPE_CHANGE,
+		.dir = IIO_EV_DIR_NONE,
+		.mask_shared_by_all = BIT(IIO_EV_INFO_ENABLE),
+	},
+	{
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_NONE,
+		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
+		.mask_shared_by_all = BIT(IIO_EV_INFO_VALUE),
+	},
+	{
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_EITHER,
+		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
+	},
+	{
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_RISING, // For swell
+		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
+		.mask_shared_by_all = BIT(IIO_EV_INFO_VALUE),
+	},
+	{
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_FALLING, // For dip
+		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
+		.mask_shared_by_all = BIT(IIO_EV_INFO_VALUE),
+	},
+};
+
+static ssize_t ade9000_wf_cap_en_show(struct iio_dev *indio_dev,
+				      uintptr_t priv,
+				      const struct iio_chan_spec *chan,
+				      char *buf)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	u32 val;
+	int ret;
+
+	ret = regmap_read(st->regmap, ADE9000_REG_WFB_CFG, &val);
+	if (ret)
+		return ret;
+
+	val = FIELD_GET(ADE9000_WF_CAP_SEL_MASK, val);
+	return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t ade9000_wf_cap_en_store(struct iio_dev *indio_dev,
+				       uintptr_t priv,
+				       const struct iio_chan_spec *chan,
+				       const char *buf, size_t len)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	bool val;
+	int ret;
+
+	ret = kstrtobool(buf, &val);
+	if (ret)
+		return ret;
+
+	/* Update the WFB_CFG register with the new CAP_SEL bit */
+	ret = regmap_update_bits(st->regmap, ADE9000_REG_WFB_CFG,
+				 ADE9000_WF_CAP_SEL_MASK,
+				 val ? ADE9000_WF_CAP_SEL_MASK : 0);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+static ssize_t ade9000_wf_mode_show(struct iio_dev *indio_dev,
+				    uintptr_t priv,
+				    const struct iio_chan_spec *chan,
+				    char *buf)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	u32 val;
+	int ret;
+
+	ret = regmap_read(st->regmap, ADE9000_REG_WFB_CFG, &val);
+	if (ret)
+		return ret;
+
+	val = FIELD_GET(ADE9000_WF_MODE_MASK, val);
+	return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t ade9000_wf_mode_store(struct iio_dev *indio_dev,
+				     uintptr_t priv,
+				     const struct iio_chan_spec *chan,
+				     const char *buf, size_t len)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	u8 val;
+	int ret;
+
+	ret = kstrtou8(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	/* Validate wf_mode range (0-3) */
+	if (val > 3)
+		return -EINVAL;
+
+	/* Update the WFB_CFG register with the new mode */
+	ret = regmap_update_bits(st->regmap, ADE9000_REG_WFB_CFG,
+				 ADE9000_WF_MODE_MASK,
+				 FIELD_PREP(ADE9000_WF_MODE_MASK, val));
+	if (ret)
+		return ret;
+
+	/* Update cached value */
+	st->wf_mode = val;
+
+	return len;
+}
+
+static ssize_t ade9000_wf_in_en_show(struct iio_dev *indio_dev,
+				     uintptr_t priv,
+				     const struct iio_chan_spec *chan,
+				     char *buf)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	u32 val;
+	int ret;
+
+	ret = regmap_read(st->regmap, ADE9000_REG_WFB_CFG, &val);
+	if (ret)
+		return ret;
+
+	val = FIELD_GET(ADE9000_WF_IN_EN_MASK, val);
+	return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t ade9000_wf_in_en_store(struct iio_dev *indio_dev,
+				      uintptr_t priv,
+				      const struct iio_chan_spec *chan,
+				      const char *buf, size_t len)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	bool val;
+	int ret;
+
+	ret = kstrtobool(buf, &val);
+	if (ret)
+		return ret;
+
+	/* Update the WFB_CFG register with the new IN_EN bit */
+	ret = regmap_update_bits(st->regmap, ADE9000_REG_WFB_CFG,
+				 ADE9000_WF_IN_EN_MASK,
+				 val ? ADE9000_WF_IN_EN_MASK : 0);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+static ssize_t ade9000_egy_time_show(struct iio_dev *indio_dev,
+				     uintptr_t priv,
+				     const struct iio_chan_spec *chan,
+				     char *buf)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	u32 val;
+	int ret;
+
+	ret = regmap_read(st->regmap, ADE9000_REG_EGY_TIME, &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade9000_egy_time_store(struct iio_dev *indio_dev,
+				      uintptr_t priv,
+				      const struct iio_chan_spec *chan,
+				      const char *buf, size_t len)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	u32 val;
+	int ret;
+
+	ret = kstrtou32(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	/* Update the EGY_TIME register */
+	ret = regmap_write(st->regmap, ADE9000_REG_EGY_TIME, val);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+static const char * const ade9000_filter_type_items[] = {
+	"sinc4", "sinc4+iir", "dsp"
+};
+
+static const int ade9000_filter_type_values[] = {
+	0, 2, 3
+};
+
+static int ade9000_filter_type_get(struct iio_dev *indio_dev,
+				   const struct iio_chan_spec *chan)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	u32 val;
+	int ret, i;
+
+	ret = regmap_read(st->regmap, ADE9000_REG_WFB_CFG, &val);
+	if (ret)
+		return ret;
+
+	val = FIELD_GET(ADE9000_WF_SRC_MASK, val);
+
+	for (i = 0; i < ARRAY_SIZE(ade9000_filter_type_values); i++) {
+		if (ade9000_filter_type_values[i] == val)
+			return i;
+	}
+
+	return -EINVAL;
+}
+
+static int ade9000_filter_type_set(struct iio_dev *indio_dev,
+				   const struct iio_chan_spec *chan,
+				   unsigned int index)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	int ret, val;
+
+	if (index >= ARRAY_SIZE(ade9000_filter_type_values))
+		return -EINVAL;
+
+	val = ade9000_filter_type_values[index];
+
+	/* Update the WFB_CFG register with the new filter type */
+	ret = regmap_update_bits(st->regmap, ADE9000_REG_WFB_CFG,
+				 ADE9000_WF_SRC_MASK,
+				 FIELD_PREP(ADE9000_WF_SRC_MASK, val));
+	if (ret)
+		return ret;
+
+	/* Update cached value */
+	st->wf_src = val;
+
+	return 0;
+}
+
+static const struct iio_enum ade9000_filter_type_enum = {
+	.items = ade9000_filter_type_items,
+	.num_items = ARRAY_SIZE(ade9000_filter_type_items),
+	.get = ade9000_filter_type_get,
+	.set = ade9000_filter_type_set,
+};
+
+static const struct iio_chan_spec_ext_info ade9000_ext_info[] = {
+	{
+		.name = "wf_cap_en",
+		.read = ade9000_wf_cap_en_show,
+		.write = ade9000_wf_cap_en_store,
+		.shared = IIO_SHARED_BY_ALL,
+	},
+	{
+		.name = "wf_mode",
+		.read = ade9000_wf_mode_show,
+		.write = ade9000_wf_mode_store,
+		.shared = IIO_SHARED_BY_ALL,
+	},
+	{
+		.name = "wf_in_en",
+		.read = ade9000_wf_in_en_show,
+		.write = ade9000_wf_in_en_store,
+		.shared = IIO_SHARED_BY_ALL,
+	},
+	{
+		.name = "egy_time",
+		.read = ade9000_egy_time_show,
+		.write = ade9000_egy_time_store,
+		.shared = IIO_SHARED_BY_ALL,
+	},
+	IIO_ENUM("filter_type", IIO_SHARED_BY_ALL, &ade9000_filter_type_enum),
+	IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_ALL, &ade9000_filter_type_enum),
+	{},
+};
+
+#define ADE9000_CURRENT_CHANNEL(num) {				\
+	.type = IIO_CURRENT,						\
+	.channel = num,							\
+	.address = ADE9000_ADDR_ADJUST(ADE9000_REG_AI_PCF, num),	\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
+			      BIT(IIO_CHAN_INFO_SCALE) |		\
+			      BIT(IIO_CHAN_INFO_HARDWAREGAIN),		\
+	.event_spec = &ade9000_events[0],				\
+	.num_event_specs = 1,						\
+	.scan_index = num,						\
+	.indexed = 1,							\
+	.scan_type = {							\
+		.sign = 's',						\
+		.realbits = 32,						\
+		.storagebits = 32,					\
+		.shift = 0,						\
+		.endianness = IIO_BE,					\
+	},								\
+}
+
+#define ADE9000_VOLTAGE_CHANNEL(num) {				\
+	.type = IIO_VOLTAGE,						\
+	.channel = num,							\
+	.address = ADE9000_ADDR_ADJUST(ADE9000_REG_AV_PCF, num),	\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
+			      BIT(IIO_CHAN_INFO_SCALE) |		\
+			      BIT(IIO_CHAN_INFO_CALIBSCALE) |		\
+			      BIT(IIO_CHAN_INFO_HARDWAREGAIN) |	\
+			      BIT(IIO_CHAN_INFO_SAMP_FREQ),		\
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
+	.event_spec = ade9000_events,					\
+	.num_event_specs = ARRAY_SIZE(ade9000_events),			\
+	.scan_index = num + 1,						\
+	.indexed = 1,							\
+	.scan_type = {							\
+		.sign = 's',						\
+		.realbits = 32,						\
+		.storagebits = 32,					\
+		.shift = 0,						\
+		.endianness = IIO_BE,					\
+	},								\
+	.ext_info = ade9000_ext_info,					\
+}
+
+#define ADE9000_CURRENT_RMS_CHANNEL(num) {			\
+	.type = IIO_CURRENT,						\
+	.channel = num,							\
+	.address = ADE9000_ADDR_ADJUST(ADE9000_REG_AIRMS, num),		\
+	.channel2 = IIO_MOD_RMS,					\
+	.modified = 1,							\
+	.indexed = 1,							\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
+			      BIT(IIO_CHAN_INFO_SCALE) |		\
+			      BIT(IIO_CHAN_INFO_OFFSET),		\
+	.scan_index = -1						\
+}
+
+#define ADE9000_ALTVOLTAGE_RMS_CHANNEL(num) {			\
+	.type = IIO_ALTVOLTAGE,						\
+	.channel = num,							\
+	.address = ADE9000_ADDR_ADJUST(ADE9000_REG_AVRMS, num),		\
+	.channel2 = IIO_MOD_RMS,					\
+	.modified = 1,							\
+	.indexed = 1,							\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
+			      BIT(IIO_CHAN_INFO_SCALE) |		\
+			      BIT(IIO_CHAN_INFO_OFFSET),		\
+	.scan_index = -1						\
+}
+
+#define ADE9000_POWER_ACTIVE_CHANNEL(num) {			\
+	.type = IIO_POWER,						\
+	.channel = num,							\
+	.address = ADE9000_ADDR_ADJUST(ADE9000_REG_AWATT, num),		\
+	.channel2 = IIO_MOD_ACTIVE,					\
+	.modified = 1,							\
+	.indexed = 1,							\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
+			      BIT(IIO_CHAN_INFO_SCALE) |		\
+			      BIT(IIO_CHAN_INFO_OFFSET) |		\
+			      BIT(IIO_CHAN_INFO_HARDWAREGAIN) |		\
+			      BIT(IIO_CHAN_INFO_POWER_FACTOR),		\
+	.scan_index = -1						\
+}
+
+#define ADE9000_POWER_REACTIVE_CHANNEL(num) {			\
+	.type = IIO_POWER,						\
+	.channel = num,							\
+	.address = ADE9000_ADDR_ADJUST(ADE9000_REG_AVAR, num),		\
+	.channel2 = IIO_MOD_REACTIVE,					\
+	.modified = 1,							\
+	.indexed = 1,							\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
+			      BIT(IIO_CHAN_INFO_SCALE) |		\
+			      BIT(IIO_CHAN_INFO_OFFSET) |		\
+			      BIT(IIO_CHAN_INFO_POWER_FACTOR),		\
+	.scan_index = -1						\
+}
+
+#define ADE9000_POWER_APPARENT_CHANNEL(num) {			\
+	.type = IIO_POWER,						\
+	.channel = num,							\
+	.address = ADE9000_ADDR_ADJUST(ADE9000_REG_AVA, num),		\
+	.channel2 = IIO_MOD_APPARENT,					\
+	.modified = 1,							\
+	.indexed = 1,							\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
+			      BIT(IIO_CHAN_INFO_SCALE) |		\
+			      BIT(IIO_CHAN_INFO_POWER_FACTOR),		\
+	.scan_index = -1						\
+}
+
+ #define ADE9000_ENERGY_ACTIVE_CHANNEL(num, addr) {     \
+	.type = IIO_ENERGY,                                          \
+	.channel = num,                                              \
+	.address = addr,                                             \
+	.channel2 = IIO_MOD_ACTIVE,				\
+	.modified = 1,						\
+	.indexed = 1,                                                \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),          \
+	.scan_index = -1                                             \
+}
+
+#define ADE9000_ENERGY_APPARENT_CHANNEL(num, addr) {     \
+	.type = IIO_ENERGY,                                          \
+	.channel = num,                                              \
+	.address = addr,                                             \
+	.channel2 = IIO_MOD_APPARENT,				\
+	.modified = 1,						\
+	.indexed = 1,                                                \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),          \
+	.scan_index = -1                                             \
+}
+
+#define ADE9000_ENERGY_REACTIVE_CHANNEL(num, addr) {     \
+	.type = IIO_ENERGY,                                          \
+	.channel = num,                                              \
+	.address = addr,                                             \
+	.channel2 = IIO_MOD_REACTIVE,				\
+	.modified = 1,						\
+	.indexed = 1,                                                \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),          \
+	.scan_index = -1                                             \
+}
+
+#define ADE9000_ALTVOLTAGE_ACCMODE_CHANNEL() {			\
+	.type = IIO_ALTVOLTAGE,						\
+	.channel = 0,							\
+	.address = ADE9000_REG_ACCMODE,					\
+	.indexed = 1,							\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),			\
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
+	.scan_index = -1						\
+}
+
+/* IIO channels of the ade9000 for each phase individually */
+static const struct iio_chan_spec ade9000_a_channels[] = {
+	ADE9000_CURRENT_CHANNEL(ADE9000_PHASE_A_NR),
+	ADE9000_VOLTAGE_CHANNEL(ADE9000_PHASE_A_NR),
+	ADE9000_CURRENT_RMS_CHANNEL(ADE9000_PHASE_A_NR),
+	ADE9000_ALTVOLTAGE_RMS_CHANNEL(ADE9000_PHASE_A_NR),
+	ADE9000_POWER_ACTIVE_CHANNEL(ADE9000_PHASE_A_NR),
+	ADE9000_POWER_REACTIVE_CHANNEL(ADE9000_PHASE_A_NR),
+	ADE9000_POWER_APPARENT_CHANNEL(ADE9000_PHASE_A_NR),
+	ADE9000_ENERGY_ACTIVE_CHANNEL(ADE9000_PHASE_A_NR, ADE9000_REG_AWATTHR_LO),
+	ADE9000_ENERGY_APPARENT_CHANNEL(ADE9000_PHASE_A_NR, ADE9000_REG_AVAHR_LO),
+	ADE9000_ENERGY_REACTIVE_CHANNEL(ADE9000_PHASE_A_NR, ADE9000_REG_AFVARHR_LO),
+};
+
+static const struct iio_chan_spec ade9000_b_channels[] = {
+	ADE9000_CURRENT_CHANNEL(ADE9000_PHASE_B_NR),
+	ADE9000_VOLTAGE_CHANNEL(ADE9000_PHASE_B_NR),
+	ADE9000_CURRENT_RMS_CHANNEL(ADE9000_PHASE_B_NR),
+	ADE9000_ALTVOLTAGE_RMS_CHANNEL(ADE9000_PHASE_B_NR),
+	ADE9000_POWER_ACTIVE_CHANNEL(ADE9000_PHASE_B_NR),
+	ADE9000_POWER_REACTIVE_CHANNEL(ADE9000_PHASE_B_NR),
+	ADE9000_POWER_APPARENT_CHANNEL(ADE9000_PHASE_B_NR),
+	ADE9000_ENERGY_ACTIVE_CHANNEL(ADE9000_PHASE_B_NR, ADE9000_REG_BWATTHR_LO),
+	ADE9000_ENERGY_APPARENT_CHANNEL(ADE9000_PHASE_B_NR, ADE9000_REG_BVAHR_LO),
+	ADE9000_ENERGY_REACTIVE_CHANNEL(ADE9000_PHASE_B_NR, ADE9000_REG_BFVARHR_LO),
+};
+
+static const struct iio_chan_spec ade9000_c_channels[] = {
+	ADE9000_CURRENT_CHANNEL(ADE9000_PHASE_C_NR),
+	ADE9000_VOLTAGE_CHANNEL(ADE9000_PHASE_C_NR),
+	ADE9000_CURRENT_RMS_CHANNEL(ADE9000_PHASE_C_NR),
+	ADE9000_ALTVOLTAGE_RMS_CHANNEL(ADE9000_PHASE_C_NR),
+	ADE9000_POWER_ACTIVE_CHANNEL(ADE9000_PHASE_C_NR),
+	ADE9000_POWER_REACTIVE_CHANNEL(ADE9000_PHASE_C_NR),
+	ADE9000_POWER_APPARENT_CHANNEL(ADE9000_PHASE_C_NR),
+	ADE9000_ENERGY_ACTIVE_CHANNEL(ADE9000_PHASE_C_NR, ADE9000_REG_CWATTHR_LO),
+	ADE9000_ENERGY_APPARENT_CHANNEL(ADE9000_PHASE_C_NR, ADE9000_REG_CVAHR_LO),
+	ADE9000_ENERGY_REACTIVE_CHANNEL(ADE9000_PHASE_C_NR, ADE9000_REG_CFVARHR_LO),
+};
+
+static const struct reg_sequence ade9000_reg_sequence[] = {
+	{ ADE9000_REG_PGA_GAIN, ADE9000_PGA_GAIN },
+	{ ADE9000_REG_CONFIG0, ADE9000_CONFIG0 },
+	{ ADE9000_REG_CONFIG1, ADE9000_CONFIG1 },
+	{ ADE9000_REG_CONFIG2, ADE9000_CONFIG2 },
+	{ ADE9000_REG_CONFIG3, ADE9000_CONFIG3 },
+	{ ADE9000_REG_ACCMODE, ADE9000_ACCMODE },
+	{ ADE9000_REG_ZX_LP_SEL, ADE9000_ZX_LP_SEL },
+	{ ADE9000_REG_MASK0, ADE9000_MASK0 },
+	{ ADE9000_REG_MASK1, ADE9000_MASK1 },
+	{ ADE9000_REG_EVENT_MASK, ADE9000_EVENT_MASK },
+	{ ADE9000_REG_WFB_CFG, ADE9000_WFB_CFG },
+	{ ADE9000_REG_VLEVEL, ADE9000_VLEVEL },
+	{ ADE9000_REG_DICOEFF, ADE9000_DICOEFF },
+	{ ADE9000_REG_EGY_TIME, ADE9000_EGY_TIME },
+	{ ADE9000_REG_EP_CFG, ADE9000_EP_CFG },
+	{ ADE9000_REG_RUN, ADE9000_RUN_ON }
+};
+
+static int ade9000_spi_write_reg(void *context,
+				 unsigned int reg,
+				 unsigned int val)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+	struct ade9000_state *st = spi_get_drvdata(spi);
+
+	u16 addr;
+	int ret = 0;
+	struct spi_transfer xfer[] = {
+		{
+			.tx_buf = st->tx,
+		},
+	};
+
+	addr = FIELD_PREP(ADE9000_REG_ADDR_MASK, reg);
+
+	put_unaligned_be16(addr, st->tx);
+	put_unaligned_be32(val, &st->tx[2]);
+
+	if (reg > ADE9000_REG_RUN && reg < ADE9000_REG_VERSION) {
+		put_unaligned_be16(val, &st->tx[2]);
+		xfer[0].len = 4;
+	} else {
+		xfer[0].len = 6;
+	}
+
+	ret = spi_sync_transfer(st->spi, xfer, ARRAY_SIZE(xfer));
+	if (ret) {
+		dev_err(&st->spi->dev, "problem when writing register 0x%x",
+			reg);
+	}
+
+	return ret;
+}
+
+static int ade9000_spi_read_reg(void *context,
+				unsigned int reg,
+				unsigned int *val)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+	struct ade9000_state *st = spi_get_drvdata(spi);
+
+	u16 addr;
+	int ret = 0;
+	struct spi_transfer xfer[] = {
+		{
+			.tx_buf = st->tx,
+			.len = 2,
+		},
+		{
+			.rx_buf = st->rx,
+		},
+	};
+
+	addr = FIELD_PREP(ADE9000_REG_ADDR_MASK, reg) |
+	       ADE9000_REG_READ_BIT_MASK;
+
+	put_unaligned_be16(addr, st->tx);
+
+	if (reg > ADE9000_REG_RUN && reg < ADE9000_REG_VERSION)
+		xfer[1].len = 4;
+	else
+		xfer[1].len = 6;
+
+	ret = spi_sync_transfer(st->spi, xfer, ARRAY_SIZE(xfer));
+	if (ret) {
+		dev_err(&st->spi->dev, "error reading register 0x%x",
+			reg);
+		goto err_ret;
+	}
+
+	if (reg > ADE9000_REG_RUN && reg < ADE9000_REG_VERSION)
+		*val = get_unaligned_be16(st->rx);
+	else
+		*val = get_unaligned_be32(st->rx);
+
+err_ret:
+	return ret;
+}
+
+static bool ade9000_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case ADE9000_REG_STATUS0:
+	case ADE9000_REG_STATUS1:
+	case ADE9000_REG_MASK0:
+	case ADE9000_REG_MASK1:
+	case ADE9000_REG_WFB_PG_IRQEN:
+		return false;
+	default:
+		return true;
+	}
+}
+
+static int ade9000_configure_scan(struct iio_dev *indio_dev, u32 wfb_addr)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	u16 addr;
+
+	addr = FIELD_PREP(ADE9000_REG_ADDR_MASK, wfb_addr) |
+	       ADE9000_REG_READ_BIT_MASK;
+
+	put_unaligned_be16(addr, st->tx_buff);
+
+	st->xfer[0].tx_buf = &st->tx_buff[0];
+	st->xfer[0].len = 2;
+
+	st->xfer[1].rx_buf = &st->rx_buff.byte[0];
+
+	if (st->wf_mode == ADE9000_WFB_STREAMING_MODE)
+		st->xfer[1].len = (st->wfb_nr_samples / 2) * 4;
+	else
+		st->xfer[1].len = st->wfb_nr_samples * 4;
+
+	spi_message_init_with_transfers(&st->spi_msg, st->xfer, 2);
+	return 0;
+}
+
+static int ade9000_iio_push_streaming(struct iio_dev *indio_dev)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	u32 current_page;
+	int ret;
+	u32 i;
+
+	ret = spi_sync(st->spi, &st->spi_msg);
+	if (ret) {
+		dev_err(&st->spi->dev, "SPI fail in trigger handler");
+		return ret;
+	}
+
+	for (i = 0; i < st->wfb_nr_samples / 2; i += st->wfb_nr_activ_chan)
+		iio_push_to_buffers(indio_dev, &st->rx_buff.word[i]);
+
+	ret = regmap_read(st->regmap, ADE9000_REG_WFB_PG_IRQEN, &current_page);
+	if (ret) {
+		dev_err(&st->spi->dev, "IRQ0 WFB read fail");
+		return ret;
+	}
+
+	if (current_page & ADE9000_MIDDLE_PAGE_BIT) {
+		ret = regmap_write(st->regmap, ADE9000_REG_WFB_PG_IRQEN,
+				   ADE9000_LAST_PAGE_BIT);
+		if (ret) {
+			dev_err(&st->spi->dev, "IRQ0 WFB write fail");
+			return ret;
+		}
+
+		ret = ade9000_configure_scan(indio_dev,
+					     ADE9000_REG_WF_HALF_BUFF);
+		if (ret)
+			return ret;
+	} else {
+		ret = regmap_write(st->regmap, ADE9000_REG_WFB_PG_IRQEN,
+				   ADE9000_MIDDLE_PAGE_BIT);
+		if (ret) {
+			dev_err(&st->spi->dev,
+				"IRQ0 WFB write fail");
+			return IRQ_HANDLED;
+		}
+
+		ret = ade9000_configure_scan(indio_dev,
+					     ADE9000_REG_WF_BUFF);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int ade9000_iio_push_buffer(struct iio_dev *indio_dev)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	int ret;
+	u32 i;
+
+	ret = spi_sync(st->spi, &st->spi_msg);
+	if (ret) {
+		dev_err(&st->spi->dev, "SPI fail in trigger handler");
+		return ret;
+	}
+
+	for (i = 0; i < st->wfb_nr_samples; i += st->wfb_nr_activ_chan)
+		iio_push_to_buffers(indio_dev, &st->rx_buff.word[i]);
+
+	return 0;
+}
+
+static int ade9000_en_wfb(struct ade9000_state *st, bool state)
+{
+	return regmap_update_bits(st->regmap, ADE9000_REG_WFB_CFG, BIT(4),
+				  state ? BIT(4) : 0);
+}
+
+static irqreturn_t ade9000_irq0_thread(int irq, void *data)
+{
+	struct iio_dev *indio_dev = data;
+	struct ade9000_state *st = iio_priv(indio_dev);
+	s64 timestamp = iio_get_time_ns(indio_dev);
+	u32 handled_irq = 0;
+	u32 interrupts;
+	u32 status;
+	int ret;
+
+	ret = regmap_read(st->regmap, ADE9000_REG_STATUS0, &status);
+	if (ret) {
+		dev_err(&st->spi->dev, "IRQ0 read status fail");
+		return IRQ_HANDLED;
+	}
+
+	ret = regmap_read(st->regmap, ADE9000_REG_MASK0, &interrupts);
+	if (ret) {
+		dev_err(&st->spi->dev, "IRQ0 read status fail");
+		return IRQ_HANDLED;
+	}
+
+	if ((status & ADE9000_ST0_PAGE_FULL_BIT) &&
+	    (interrupts & ADE9000_ST0_PAGE_FULL_BIT)) {
+		switch (st->wf_mode) {
+		case ADE9000_WFB_FULL_MODE:
+			ret = ade9000_en_wfb(st, false);
+			if (ret) {
+				dev_err(&st->spi->dev, "IRQ0 WFB stop fail");
+				return IRQ_HANDLED;
+			}
+			ret = ade9000_iio_push_buffer(indio_dev);
+			if (ret) {
+				dev_err(&st->spi->dev, "IRQ0 IIO push fail");
+				return IRQ_HANDLED;
+			}
+
+			interrupts &= ~ADE9000_ST0_PAGE_FULL_BIT;
+
+			ret = regmap_write(st->regmap, ADE9000_REG_MASK0,
+					   interrupts);
+			if (ret) {
+				dev_err(&st->spi->dev, "IRQ0 MAKS0 write fail");
+				return IRQ_HANDLED;
+			}
+			break;
+
+		case ADE9000_WFB_C_EN_TRIG_MODE:
+			ret = regmap_write(st->regmap, ADE9000_REG_WFB_PG_IRQEN,
+					   ADE9000_LAST_PAGE_BIT);
+			if (ret) {
+				dev_err(&st->spi->dev, "IRQ0 WFB write fail");
+				return IRQ_HANDLED;
+			}
+
+			ret = regmap_write(st->regmap, ADE9000_REG_WFB_TRG_CFG,
+					   st->wfb_trg);
+			if (ret) {
+				dev_err(&st->spi->dev, "IRQ0 WFB write fail");
+				return IRQ_HANDLED;
+			}
+
+			interrupts |= ADE9000_ST0_WFB_TRIG_BIT;
+			interrupts &= ~ADE9000_ST0_PAGE_FULL_BIT;
+
+			ret = regmap_write(st->regmap, ADE9000_REG_MASK0,
+					   interrupts);
+			if (ret) {
+				dev_err(&st->spi->dev, "IRQ0 MAKS0 write fail");
+				return IRQ_HANDLED;
+			}
+			break;
+
+		case ADE9000_WFB_EN_TRIG_MODE:
+			ret = regmap_write(st->regmap, ADE9000_REG_WFB_TRG_CFG,
+					   st->wfb_trg);
+			if (ret) {
+				dev_err(&st->spi->dev, "IRQ0 WFB write fail");
+				return IRQ_HANDLED;
+			}
+
+			interrupts |= ADE9000_ST0_WFB_TRIG_BIT;
+
+			interrupts &= ~ADE9000_ST0_PAGE_FULL_BIT;
+
+			ret = regmap_write(st->regmap, ADE9000_REG_MASK0,
+					   interrupts);
+			if (ret) {
+				dev_err(&st->spi->dev, "IRQ0 MAKS0 write fail");
+				return IRQ_HANDLED;
+			}
+			break;
+
+		case ADE9000_WFB_STREAMING_MODE:
+			ret = ade9000_iio_push_streaming(indio_dev);
+			if (ret) {
+				dev_err(&st->spi->dev, "IRQ0 IIO push fail");
+				return IRQ_HANDLED;
+			}
+			break;
+
+		default:
+			return IRQ_HANDLED;
+		}
+
+		handled_irq |= ADE9000_ST0_PAGE_FULL_BIT;
+	}
+
+	if ((status & ADE9000_ST0_WFB_TRIG_BIT) &&
+	    (interrupts & ADE9000_ST0_WFB_TRIG_BIT)) {
+		ret = ade9000_en_wfb(st, false);
+		if (ret) {
+			dev_err(&st->spi->dev, "IRQ0 WFB fail");
+			return IRQ_HANDLED;
+		}
+
+		ret = ade9000_iio_push_buffer(indio_dev);
+		if (ret) {
+			dev_err(&st->spi->dev, "IRQ0 IIO push fail @ WFB TRIG");
+			return IRQ_HANDLED;
+		}
+
+		handled_irq |= ADE9000_ST0_WFB_TRIG_BIT;
+	}
+
+	if ((status & ADE9000_ST0_EGYRDY) &&
+	    (interrupts & ADE9000_ST0_EGYRDY)) {
+		iio_push_event(indio_dev,
+			       IIO_UNMOD_EVENT_CODE(IIO_ENERGY,
+						    ADE9000_ST0_EGYRDY,
+						    IIO_EV_TYPE_MAG,
+						    IIO_EV_DIR_NONE),
+			       timestamp);
+
+		handled_irq |= ADE9000_ST0_EGYRDY;
+	}
+
+	ret = regmap_write(st->regmap, ADE9000_REG_STATUS0, handled_irq);
+	if (ret)
+		dev_err(&st->spi->dev, "IRQ0 write status fail");
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t ade9000_irq1_thread(int irq, void *data)
+{
+	struct iio_dev *indio_dev = data;
+	struct ade9000_state *st = iio_priv(indio_dev);
+	unsigned int bit = ADE9000_ST1_CROSSING_FIRST;
+	s64 timestamp = iio_get_time_ns(indio_dev);
+	u32 handled_irq = 0;
+	u32 interrupts;
+	u32 result;
+	u32 status;
+	u32 tmp;
+	int ret;
+
+	if (!st->rst_done) {
+		ret = regmap_read(st->regmap, ADE9000_REG_STATUS1, &result);
+		if (ret)
+			return ret;
+
+		if (result & ADE9000_ST1_RSTDONE_BIT)
+			st->rst_done = true;
+		else
+			dev_err(&st->spi->dev, "Error testing reset done");
+
+		return IRQ_HANDLED;
+	}
+
+	ret = regmap_read(st->regmap, ADE9000_REG_STATUS1, &status);
+	if (ret)
+		return IRQ_HANDLED;
+
+	ret = regmap_read(st->regmap, ADE9000_REG_MASK1, &interrupts);
+	if (ret) {
+		dev_err(&st->spi->dev, "IRQ1 read status fail");
+		return IRQ_HANDLED;
+	}
+
+	for_each_set_bit_from(bit, (unsigned long *)&interrupts,
+			      ADE9000_ST1_CROSSING_DEPTH){
+		tmp = status & BIT(bit);
+
+		switch (tmp) {
+		case ADE9000_ST1_ZXVA_BIT:
+			iio_push_event(indio_dev,
+				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
+							    ADE9000_ST1_ZXVA_BIT,
+							    IIO_EV_TYPE_THRESH,
+							    IIO_EV_DIR_EITHER),
+				       timestamp);
+			handled_irq |= ADE9000_ST1_ZXVA_BIT;
+			break;
+		case ADE9000_ST1_ZXTOVA_BIT:
+			iio_push_event(indio_dev,
+				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
+							    ADE9000_ST1_ZXTOVA_BIT,
+							    IIO_EV_TYPE_THRESH,
+							    IIO_EV_DIR_EITHER),
+				       timestamp);
+			handled_irq |= ADE9000_ST1_ZXTOVA_BIT;
+			break;
+		case ADE9000_ST1_ZXIA_BIT:
+			iio_push_event(indio_dev,
+				       IIO_UNMOD_EVENT_CODE(IIO_CURRENT,
+							    ADE9000_ST1_ZXIA_BIT,
+							    IIO_EV_TYPE_THRESH,
+							    IIO_EV_DIR_EITHER),
+				       timestamp);
+			handled_irq |= ADE9000_ST1_ZXIA_BIT;
+			break;
+		case ADE9000_ST1_ZXVB_BIT:
+			iio_push_event(indio_dev,
+				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
+							    ADE9000_ST1_ZXVB_BIT,
+							    IIO_EV_TYPE_THRESH,
+							    IIO_EV_DIR_EITHER),
+				       timestamp);
+			handled_irq |= ADE9000_ST1_ZXVB_BIT;
+			break;
+		case ADE9000_ST1_ZXTOVB_BIT:
+			iio_push_event(indio_dev,
+				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
+							    ADE9000_ST1_ZXTOVB_BIT,
+							    IIO_EV_TYPE_THRESH,
+							    IIO_EV_DIR_EITHER),
+				       timestamp);
+			handled_irq |= ADE9000_ST1_ZXTOVB_BIT;
+			break;
+		case ADE9000_ST1_ZXIB_BIT:
+			iio_push_event(indio_dev,
+				       IIO_UNMOD_EVENT_CODE(IIO_CURRENT,
+							    ADE9000_ST1_ZXIB_BIT,
+							    IIO_EV_TYPE_THRESH,
+							    IIO_EV_DIR_EITHER),
+				       timestamp);
+			handled_irq |= ADE9000_ST1_ZXIB_BIT;
+			break;
+		case ADE9000_ST1_ZXVC_BIT:
+			iio_push_event(indio_dev,
+				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
+							    ADE9000_ST1_ZXVC_BIT,
+							    IIO_EV_TYPE_THRESH,
+							    IIO_EV_DIR_EITHER),
+				       timestamp);
+			handled_irq |= ADE9000_ST1_ZXVC_BIT;
+			break;
+		case ADE9000_ST1_ZXTOVC_BIT:
+			iio_push_event(indio_dev,
+				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
+							    ADE9000_ST1_ZXTOVC_BIT,
+							    IIO_EV_TYPE_THRESH,
+							    IIO_EV_DIR_EITHER),
+				       timestamp);
+			handled_irq |= ADE9000_ST1_ZXTOVC_BIT;
+			break;
+		case ADE9000_ST1_ZXIC_BIT:
+			iio_push_event(indio_dev,
+				       IIO_UNMOD_EVENT_CODE(IIO_CURRENT,
+							    ADE9000_ST1_ZXIC_BIT,
+							    IIO_EV_TYPE_THRESH,
+							    IIO_EV_DIR_EITHER),
+				       timestamp);
+			handled_irq |= ADE9000_ST1_ZXIC_BIT;
+			break;
+		case ADE9000_ST1_SWELLA_BIT:
+			iio_push_event(indio_dev,
+				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
+							    ADE9000_ST1_SWELLA_BIT >> 20,
+							    IIO_EV_TYPE_THRESH,
+							    IIO_EV_DIR_RISING),
+				       timestamp);
+			handled_irq |= ADE9000_ST1_SWELLA_BIT;
+			break;
+		case ADE9000_ST1_SWELLB_BIT:
+			iio_push_event(indio_dev,
+				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
+							    ADE9000_ST1_SWELLB_BIT >> 20,
+							    IIO_EV_TYPE_THRESH,
+							    IIO_EV_DIR_RISING),
+				       timestamp);
+			handled_irq |= ADE9000_ST1_SWELLB_BIT;
+			break;
+		case ADE9000_ST1_SWELLC_BIT:
+			iio_push_event(indio_dev,
+				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
+							    ADE9000_ST1_SWELLC_BIT >> 20,
+							    IIO_EV_TYPE_THRESH,
+							    IIO_EV_DIR_RISING),
+				       timestamp);
+			handled_irq |= ADE9000_ST1_SWELLC_BIT;
+			break;
+		case ADE9000_ST1_DIPA_BIT:
+			iio_push_event(indio_dev,
+				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
+							    ADE9000_ST1_DIPA_BIT >> 20,
+							    IIO_EV_TYPE_THRESH,
+							    IIO_EV_DIR_FALLING),
+				       timestamp);
+			handled_irq |= ADE9000_ST1_DIPA_BIT;
+			break;
+		case ADE9000_ST1_DIPB_BIT:
+			iio_push_event(indio_dev,
+				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
+							    ADE9000_ST1_DIPB_BIT >> 20,
+							    IIO_EV_TYPE_THRESH,
+							    IIO_EV_DIR_FALLING),
+				       timestamp);
+			handled_irq |= ADE9000_ST1_DIPB_BIT;
+			break;
+		case ADE9000_ST1_DIPC_BIT:
+			iio_push_event(indio_dev,
+				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
+							    ADE9000_ST1_DIPC_BIT >> 20,
+							    IIO_EV_TYPE_THRESH,
+							    IIO_EV_DIR_FALLING),
+				       timestamp);
+			handled_irq |= ADE9000_ST1_DIPC_BIT;
+			break;
+		case ADE9000_ST1_SEQERR_BIT:
+			iio_push_event(indio_dev,
+				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
+							    ADE9000_ST1_SEQERR_BIT >> 12,
+							    IIO_EV_TYPE_CHANGE,
+							    IIO_EV_DIR_NONE),
+				       timestamp);
+			handled_irq |= ADE9000_ST1_SEQERR_BIT;
+			break;
+		default:
+			return IRQ_HANDLED;
+		}
+	}
+
+	ret = regmap_write(st->regmap, ADE9000_REG_STATUS1, handled_irq);
+	if (ret)
+		return ret;
+
+	return IRQ_HANDLED;
+}
+
+static int ade9000_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int *val,
+			    int *val2,
+			    long mask)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	unsigned int reg;
+	int measured;
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		if (chan->type == IIO_VOLTAGE) {
+			int period_reg;
+			int period;
+
+			switch (chan->channel) {
+			case ADE9000_PHASE_A_NR:
+				period_reg = ADE9000_REG_APERIOD;
+				break;
+			case ADE9000_PHASE_B_NR:
+				period_reg = ADE9000_REG_BPERIOD;
+				break;
+			case ADE9000_PHASE_C_NR:
+				period_reg = ADE9000_REG_CPERIOD;
+				break;
+			default:
+				return -EINVAL;
+			}
+			ret = regmap_read(st->regmap, period_reg, &period);
+			if (ret)
+				return ret;
+			*val = 4000 * 65536;
+			*val2 = period + 1;
+			return IIO_VAL_FRACTIONAL;
+		}
+
+		ret = regmap_read(st->regmap, ADE9000_REG_ACCMODE, &reg);
+		if (ret)
+			return ret;
+		*val = (reg & BIT(8)) ? 60 : 50;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_RAW:
+		if (chan->type == IIO_ENERGY) {
+			s64 val64;
+			u32 data[2];
+			u16 lo_reg = chan->address;
+
+			ret = regmap_bulk_read(st->regmap, lo_reg, data, 2);
+			if (ret)
+				return ret;
+
+			val64 = ((u64)data[1] << 32) | data[0];
+			*(s64 *)val = val64;
+			return IIO_VAL_INT;
+		}
+
+		ret = iio_device_claim_direct(indio_dev);
+		if (ret)
+			return ret;
+
+		ret = regmap_read(st->regmap, chan->address, &measured);
+		iio_device_release_direct(indio_dev);
+		if (ret)
+			return ret;
+
+		*val = measured;
+
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_POWER_FACTOR:
+		ret = iio_device_claim_direct(indio_dev);
+		if (ret)
+			return ret;
+
+		/* Map power channel to corresponding power factor register */
+		reg = ADE9000_ADDR_ADJUST(ADE9000_REG_APF, chan->channel);
+		ret = regmap_read(st->regmap, reg, &measured);
+		iio_device_release_direct(indio_dev);
+		if (ret)
+			return ret;
+
+		*val = measured;
+
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SCALE:
+		if (chan->type == IIO_CURRENT || chan->type == IIO_VOLTAGE ||
+		    chan->type == IIO_ALTVOLTAGE) {
+			switch (chan->address) {
+			case ADE9000_REG_AI_PCF:
+			case ADE9000_REG_AV_PCF:
+			case ADE9000_REG_BI_PCF:
+			case ADE9000_REG_BV_PCF:
+			case ADE9000_REG_CI_PCF:
+			case ADE9000_REG_CV_PCF:
+				*val = 1;
+				*val2 = ADE9000_PCF_FULL_SCALE_CODES;
+				return IIO_VAL_FRACTIONAL;
+			case ADE9000_REG_AIRMS:
+			case ADE9000_REG_AVRMS:
+			case ADE9000_REG_BIRMS:
+			case ADE9000_REG_BVRMS:
+			case ADE9000_REG_CIRMS:
+			case ADE9000_REG_CVRMS:
+				*val = 1;
+				*val2 = ADE9000_RMS_FULL_SCALE_CODES;
+				return IIO_VAL_FRACTIONAL;
+			default:
+				return -EINVAL;
+			}
+		} else if (chan->type == IIO_POWER) {
+			*val = 1;
+			*val2 = ADE9000_WATT_FULL_SCALE_CODES;
+			return IIO_VAL_FRACTIONAL;
+		} else {
+			return -EINVAL;
+		}
+		break;
+	case IIO_CHAN_INFO_CALIBSCALE:
+		ret = regmap_read(st->regmap, ADE9000_REG_PGA_GAIN, &reg);
+		if (ret)
+			return ret;
+		*val = 1 << ((reg >> (8 + chan->channel)) & 0x3);
+		if (*val > 4)
+			*val = 4;
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ade9000_write_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     int val,
+			     int val2,
+			     long mask)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	u32 addr;
+	u32 tmp;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		if (val != 50 && val != 60)
+			return -EINVAL;
+		return regmap_write(st->regmap, ADE9000_REG_ACCMODE,
+				    (val == 60) ? ADE9000_ACCMODE_60HZ : ADE9000_ACCMODE);
+	case IIO_CHAN_INFO_OFFSET:
+		switch (chan->type) {
+		case IIO_CURRENT:
+			addr = ADE9000_ADDR_ADJUST(ADE9000_REG_AIRMSOS,
+						   chan->channel);
+			break;
+		case IIO_VOLTAGE:
+		case IIO_ALTVOLTAGE:
+			addr = ADE9000_ADDR_ADJUST(ADE9000_REG_AVRMSOS,
+						   chan->channel);
+			break;
+		case IIO_POWER:
+			tmp = chan->address;
+			tmp &= ~ADE9000_PHASE_B_POS_BIT;
+			tmp &= ~ADE9000_PHASE_C_POS_BIT;
+
+			switch (tmp) {
+			case ADE9000_REG_AWATTOS:
+				addr = ADE9000_ADDR_ADJUST(ADE9000_REG_AWATTOS,
+							   chan->channel);
+				break;
+			case ADE9000_REG_AVAR:
+				addr = ADE9000_ADDR_ADJUST(ADE9000_REG_AVAROS,
+							   chan->channel);
+				break;
+			case ADE9000_REG_AFVAR:
+				addr = ADE9000_ADDR_ADJUST(ADE9000_REG_AFVAROS,
+							   chan->channel);
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case IIO_CHAN_INFO_HARDWAREGAIN:
+		switch (chan->type) {
+		case IIO_CURRENT:
+			addr = ADE9000_ADDR_ADJUST(ADE9000_REG_AIGAIN,
+						   chan->channel);
+			break;
+		case IIO_VOLTAGE:
+			addr = ADE9000_ADDR_ADJUST(ADE9000_REG_AVGAIN,
+						   chan->channel);
+			break;
+		case IIO_POWER:
+			addr = ADE9000_ADDR_ADJUST(ADE9000_REG_APGAIN,
+						   chan->channel);
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case IIO_CHAN_INFO_CALIBSCALE:
+		if (val > 4 || val < 1 || val == 3)
+			return -EINVAL;
+		addr = ADE9000_REG_PGA_GAIN;
+		val = ilog2(val) << (chan->channel * 2 + 8);
+		tmp = 0x3 << (chan->channel * 2 + 8);
+		return regmap_update_bits(st->regmap, addr, tmp, val);
+	default:
+		return -EINVAL;
+	}
+
+	return regmap_write(st->regmap, addr, val);
+}
+
+static int ade9000_reg_access(struct iio_dev *indio_dev,
+			      unsigned int reg,
+			      unsigned int tx_val,
+			      unsigned int *rx_val)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+
+	if (rx_val)
+		return regmap_read(st->regmap, reg, rx_val);
+
+	return regmap_write(st->regmap, reg, tx_val);
+}
+
+static int ade9000_read_event_config(struct iio_dev *indio_dev,
+				     const struct iio_chan_spec *chan,
+				     enum iio_event_type type,
+				     enum iio_event_direction dir)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	u32 interrupts0, interrupts1, number;
+	int ret;
+
+	ret = regmap_read(st->regmap, ADE9000_REG_MASK0, &interrupts0);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(st->regmap, ADE9000_REG_MASK1, &interrupts1);
+	if (ret)
+		return ret;
+
+	if (type == IIO_EV_TYPE_MAG)
+		return (interrupts0 & ADE9000_ST0_EGYRDY);
+
+	if (type == IIO_EV_TYPE_CHANGE)
+		return (interrupts1 & ADE9000_ST1_SEQERR_BIT);
+
+	number = chan->channel;
+	switch (number) {
+	case ADE9000_PHASE_A_NR:
+		if (chan->type == IIO_VOLTAGE) {
+			if (dir == IIO_EV_DIR_EITHER)
+				return (interrupts1 & ADE9000_ST1_ZXVA_BIT);
+			if (dir == IIO_EV_DIR_NONE)
+				return (interrupts1 & ADE9000_ST1_ZXTOVA_BIT);
+			if (dir == IIO_EV_DIR_RISING)
+				return (interrupts1 & ADE9000_ST1_SWELLA_BIT);
+			if (dir == IIO_EV_DIR_FALLING)
+				return (interrupts1 & ADE9000_ST1_DIPA_BIT);
+		} else if (chan->type == IIO_CURRENT) {
+			return (interrupts1 & ADE9000_ST1_ZXIA_BIT);
+		}
+		break;
+	case ADE9000_PHASE_B_NR:
+		if (chan->type == IIO_VOLTAGE) {
+			if (dir == IIO_EV_DIR_EITHER)
+				return (interrupts1 & ADE9000_ST1_ZXVB_BIT);
+			if (dir == IIO_EV_DIR_NONE)
+				return (interrupts1 & ADE9000_ST1_ZXTOVB_BIT);
+			if (dir == IIO_EV_DIR_RISING)
+				return (interrupts1 & ADE9000_ST1_SWELLB_BIT);
+			if (dir == IIO_EV_DIR_FALLING)
+				return (interrupts1 & ADE9000_ST1_DIPB_BIT);
+		} else if (chan->type == IIO_CURRENT) {
+			return (interrupts1 & ADE9000_ST1_ZXIB_BIT);
+		}
+		break;
+	case ADE9000_PHASE_C_NR:
+		if (chan->type == IIO_VOLTAGE) {
+			if (dir == IIO_EV_DIR_EITHER)
+				return (interrupts1 & ADE9000_ST1_ZXVC_BIT);
+			if (dir == IIO_EV_DIR_NONE)
+				return (interrupts1 & ADE9000_ST1_ZXTOVC_BIT);
+			if (dir == IIO_EV_DIR_RISING)
+				return (interrupts1 & ADE9000_ST1_SWELLC_BIT);
+			if (dir == IIO_EV_DIR_FALLING)
+				return (interrupts1 & ADE9000_ST1_DIPC_BIT);
+		} else if (chan->type == IIO_CURRENT) {
+			return (interrupts1 & ADE9000_ST1_ZXIC_BIT);
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ade9000_write_event_config(struct iio_dev *indio_dev,
+				      const struct iio_chan_spec *chan,
+				      enum iio_event_type type,
+				      enum iio_event_direction dir,
+				      bool state)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	u32 interrupts, tmp;
+	int ret;
+	struct irq_wfb_trig {
+		u32 irq;
+		u32 wfb_trg;
+	};
+
+	ret = regmap_write(st->regmap, ADE9000_REG_STATUS1, GENMASK(31, 0));
+	if (ret)
+		return ret;
+
+	if (type == IIO_EV_TYPE_MAG) {
+		ret = regmap_update_bits(st->regmap, ADE9000_REG_STATUS0,
+					 ADE9000_ST0_EGYRDY, ADE9000_ST0_EGYRDY);
+		if (ret)
+			return ret;
+		return regmap_update_bits(st->regmap, ADE9000_REG_MASK0,
+					 ADE9000_ST0_EGYRDY,
+					 state ? ADE9000_ST1_SEQERR_BIT : 0);
+	}
+
+	if (type == IIO_EV_TYPE_CHANGE)
+		return regmap_update_bits(st->regmap, ADE9000_REG_MASK1,
+					 ADE9000_ST1_SEQERR_BIT,
+					 state ? ADE9000_ST1_SEQERR_BIT : 0);
+
+	struct irq_wfb_trig trig_arr[6] = {
+		{.irq = ADE9000_ST1_ZXVA_BIT,
+		 .wfb_trg = ADE9000_WFB_TRG_ZXVA_BIT
+		},
+		{.irq = ADE9000_ST1_ZXIA_BIT,
+		 .wfb_trg = ADE9000_WFB_TRG_ZXIA_BIT
+		},
+		{.irq = ADE9000_ST1_ZXVB_BIT,
+		 .wfb_trg = ADE9000_WFB_TRG_ZXVB_BIT
+		},
+		{.irq = ADE9000_ST1_ZXIB_BIT,
+		 .wfb_trg = ADE9000_WFB_TRG_ZXIB_BIT
+		},
+		{.irq = ADE9000_ST1_ZXVC_BIT,
+		 .wfb_trg = ADE9000_WFB_TRG_ZXVC_BIT
+		},
+		{.irq = ADE9000_ST1_ZXIC_BIT,
+		 .wfb_trg = ADE9000_WFB_TRG_ZXIC_BIT
+		},
+	};
+
+	if (dir == IIO_EV_DIR_EITHER) {
+		if (state) {
+			interrupts |= trig_arr[chan->channel * 2 + chan->type].irq;
+			st->wfb_trg |= trig_arr[chan->channel * 2 + chan->type].wfb_trg;
+		} else {
+			interrupts &= ~trig_arr[chan->channel * 2 + chan->type].irq;
+			st->wfb_trg &= ~trig_arr[chan->channel * 2 + chan->type].wfb_trg;
+		}
+	if (dir == IIO_EV_DIR_NONE) {
+		switch (chan->channel) {
+		case ADE9000_PHASE_A_NR:
+			interrupts |= ADE9000_ST1_ZXTOVA_BIT;
+			break;
+		case ADE9000_PHASE_B_NR:
+			interrupts |= ADE9000_ST1_ZXTOVB_BIT;
+			break;
+		case ADE9000_PHASE_C_NR:
+			interrupts |= ADE9000_ST1_ZXTOVC_BIT;
+			break;
+		default:
+			break;
+		}
+
+		if (state)
+			interrupts |= tmp;
+		else
+			interrupts &= ~tmp;
+	}
+	} else if (dir == IIO_EV_DIR_RISING) {
+		switch (chan->channel) {
+		case ADE9000_PHASE_A_NR:
+			tmp |= ADE9000_ST1_SWELLA_BIT;
+			break;
+		case ADE9000_PHASE_B_NR:
+			tmp |= ADE9000_ST1_SWELLB_BIT;
+			break;
+		case ADE9000_PHASE_C_NR:
+			tmp |= ADE9000_ST1_SWELLC_BIT;
+			break;
+		default:
+			break;
+		}
+
+		if (state) {
+			interrupts |= tmp;
+			st->wfb_trg |= ADE9000_WFB_TRG_SWELL_BIT;
+		} else {
+			interrupts &= ~tmp;
+			st->wfb_trg &= ~ADE9000_WFB_TRG_SWELL_BIT;
+		}
+
+	} else if (dir == IIO_EV_DIR_FALLING) {
+		switch (chan->channel) {
+		case ADE9000_PHASE_A_NR:
+			tmp |= ADE9000_ST1_DIPA_BIT;
+			break;
+		case ADE9000_PHASE_B_NR:
+			tmp |= ADE9000_ST1_DIPB_BIT;
+			break;
+		case ADE9000_PHASE_C_NR:
+			tmp |= ADE9000_ST1_DIPC_BIT;
+			break;
+		default:
+			break;
+		}
+
+		if (state) {
+			interrupts |= tmp;
+			st->wfb_trg |= ADE9000_WFB_TRG_DIP_BIT;
+		} else {
+			interrupts &= ~tmp;
+			st->wfb_trg &= ~ADE9000_WFB_TRG_DIP_BIT;
+		}
+	}
+	return regmap_update_bits(st->regmap, ADE9000_REG_MASK1, interrupts,
+				  interrupts);
+}
+
+static int ade9000_write_event_value(struct iio_dev *indio_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 ade9000_state *st = iio_priv(indio_dev);
+
+	switch (info) {
+	case IIO_EV_INFO_VALUE:
+		switch (dir) {
+		case IIO_EV_DIR_FALLING:
+			return regmap_write(st->regmap, ADE9000_REG_DIP_LVL, val);
+		case IIO_EV_DIR_RISING:
+			return regmap_write(st->regmap, ADE9000_REG_SWELL_LVL, val);
+		case IIO_EV_DIR_NONE:
+			return regmap_write(st->regmap, ADE9000_REG_ZXTOUT, val);
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ade9000_read_event_value(struct iio_dev *indio_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 ade9000_state *st = iio_priv(indio_dev);
+	unsigned int data;
+	int ret;
+
+	switch (info) {
+	case IIO_EV_INFO_VALUE:
+		switch (dir) {
+		case IIO_EV_DIR_FALLING:
+			ret = regmap_read(st->regmap, ADE9000_REG_DIP_LVL, &data);
+			if (ret)
+				return ret;
+			*val = data;
+			return IIO_VAL_INT;
+		case IIO_EV_DIR_RISING:
+			ret = regmap_read(st->regmap, ADE9000_REG_SWELL_LVL, &data);
+			if (ret)
+				return ret;
+			*val = data;
+			return IIO_VAL_INT;
+		case IIO_EV_DIR_NONE:
+			ret = regmap_read(st->regmap, ADE9000_REG_ZXTOUT, &data);
+			if (ret)
+				return ret;
+			*val = data;
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ade9000_config_wfb(struct iio_dev *indio_dev)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	u32 wfg_cfg_val = 0;
+	u32 active_scans;
+
+	bitmap_to_arr32(&active_scans, indio_dev->active_scan_mask,
+			indio_dev->masklength);
+
+	switch (active_scans) {
+	case ADE9000_SCAN_POS_IA | ADE9000_SCAN_POS_VA:
+		wfg_cfg_val = 0x1;
+		st->wfb_nr_activ_chan = 2;
+		break;
+	case ADE9000_SCAN_POS_IB | ADE9000_SCAN_POS_VB:
+		wfg_cfg_val = 0x2;
+		st->wfb_nr_activ_chan = 2;
+		break;
+	case ADE9000_SCAN_POS_IC | ADE9000_SCAN_POS_VC:
+		wfg_cfg_val = 0x3;
+		st->wfb_nr_activ_chan = 2;
+		break;
+	case ADE9000_SCAN_POS_IA:
+		wfg_cfg_val = 0x8;
+		st->wfb_nr_activ_chan = 1;
+		break;
+	case ADE9000_SCAN_POS_VA:
+		wfg_cfg_val = 0x9;
+		st->wfb_nr_activ_chan = 1;
+		break;
+	case ADE9000_SCAN_POS_IB:
+		wfg_cfg_val = 0xA;
+		st->wfb_nr_activ_chan = 1;
+		break;
+	case ADE9000_SCAN_POS_VB:
+		wfg_cfg_val = 0xB;
+		st->wfb_nr_activ_chan = 1;
+		break;
+	case ADE9000_SCAN_POS_IC:
+		wfg_cfg_val = 0xC;
+		st->wfb_nr_activ_chan = 1;
+		break;
+	case ADE9000_SCAN_POS_VC:
+		wfg_cfg_val = 0xD;
+		st->wfb_nr_activ_chan = 1;
+		break;
+	case ADE9000_SCAN_POS_ALL:
+		wfg_cfg_val = 0x0;
+		st->wfb_nr_activ_chan = 6;
+		break;
+	default:
+		dev_err(&st->spi->dev, "Unsupported combination of scans");
+		return -EINVAL;
+	}
+
+	wfg_cfg_val |= FIELD_PREP(ADE9000_WF_SRC_MASK, st->wf_src);
+
+	return regmap_write(st->regmap, ADE9000_REG_WFB_CFG, wfg_cfg_val);
+}
+
+static int ade9000_wfb_interrupt_setup(struct ade9000_state *st, u8 mode)
+{
+	int ret;
+
+	ret = regmap_write(st->regmap, ADE9000_REG_WFB_TRG_CFG, 0x0);
+	if (ret)
+		return ret;
+
+	switch (mode) {
+	case ADE9000_WFB_FULL_MODE:
+	case ADE9000_WFB_EN_TRIG_MODE:
+		ret = regmap_write(st->regmap, ADE9000_REG_WFB_PG_IRQEN,
+				   ADE9000_LAST_PAGE_BIT);
+		if (ret)
+			return ret;
+		break;
+	case ADE9000_WFB_C_EN_TRIG_MODE:
+		ret = regmap_write(st->regmap, ADE9000_REG_WFB_PG_IRQEN,
+				   ADE9000_MIDDLE_PAGE_BIT);
+		if (ret)
+			return ret;
+		break;
+	case ADE9000_WFB_STREAMING_MODE:
+		ret = regmap_write(st->regmap, ADE9000_REG_WFB_PG_IRQEN,
+				   ADE9000_MIDDLE_PAGE_BIT);
+		if (ret)
+			return ret;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = regmap_write(st->regmap, ADE9000_REG_STATUS0, GENMASK(31, 0));
+	if (ret)
+		return ret;
+
+	return regmap_update_bits(st->regmap, ADE9000_REG_MASK0,
+				  ADE9000_ST0_PAGE_FULL_BIT,
+				  ADE9000_ST0_PAGE_FULL_BIT);
+}
+
+static int ade9000_buffer_preenable(struct iio_dev *indio_dev)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	int ret;
+
+	ret = ade9000_config_wfb(indio_dev);
+	if (ret)
+		return ret;
+
+	st->wfb_nr_samples = ADE9000_WFB_MAX_SAMPLES_CHAN *
+			     st->wfb_nr_activ_chan;
+
+	ret = ade9000_configure_scan(indio_dev, ADE9000_REG_WF_BUFF);
+	if (ret)
+		return ret;
+
+	ret = ade9000_wfb_interrupt_setup(st, st->wf_mode);
+	if (ret)
+		return ret;
+
+	ret = ade9000_en_wfb(st, true);
+	if (ret) {
+		dev_err(&st->spi->dev, "Post-enable wfb enable fail");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ade9000_buffer_postdisable(struct iio_dev *indio_dev)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	u32 interrupts = 0;
+	int ret;
+
+	ret = ade9000_en_wfb(st, false);
+	if (ret) {
+		dev_err(&st->spi->dev, "Post-disable wfb disable fail");
+		return ret;
+	}
+
+	ret = regmap_write(st->regmap, ADE9000_REG_WFB_TRG_CFG, 0x0);
+	if (ret)
+		return ret;
+
+	interrupts |= ADE9000_ST0_WFB_TRIG_BIT;
+	interrupts |= ADE9000_ST0_PAGE_FULL_BIT;
+
+	return regmap_update_bits(st->regmap, ADE9000_REG_MASK0, interrupts, 0);
+	if (ret) {
+		dev_err(&st->spi->dev, "Post-disable update maks0 fail");
+		return ret;
+	}
+
+	return regmap_write(st->regmap, ADE9000_REG_STATUS0, GENMASK(31, 0));
+}
+
+static int ade9000_setup_iio_channels(struct iio_dev *indio_dev)
+{
+	struct ade9000_state *st = iio_priv(indio_dev);
+	struct device *dev = &st->spi->dev;
+	struct fwnode_handle *phase_node = NULL;
+	struct iio_chan_spec *chan;
+	u32 phase_nr;
+	int ret;
+
+	chan = devm_kcalloc(dev,
+			    (ADE9000_MAX_PHASE_NR *
+			     ARRAY_SIZE(ade9000_a_channels)) + 1,
+			    sizeof(*chan), GFP_KERNEL);
+	if (!chan) {
+		dev_err(dev, "Unable to allocate ADE9000 channels");
+		return -ENOMEM;
+	}
+	indio_dev->num_channels = 0;
+	indio_dev->channels = chan;
+
+	struct iio_chan_spec *chan_ptr = chan;
+
+	fwnode_for_each_available_child_node(dev_fwnode(dev), phase_node) {
+		ret = fwnode_property_read_u32(phase_node, "reg", &phase_nr);
+		if (ret) {
+			dev_err(dev, "Could not read channel reg : %d\n", ret);
+			return ret;
+		}
+
+		switch (phase_nr) {
+		case ADE9000_PHASE_A_NR:
+			memcpy(chan_ptr, ade9000_a_channels,
+			       sizeof(ade9000_a_channels));
+			break;
+		case ADE9000_PHASE_B_NR:
+			memcpy(chan_ptr, ade9000_b_channels,
+			       sizeof(ade9000_b_channels));
+			break;
+		case ADE9000_PHASE_C_NR:
+			memcpy(chan_ptr, ade9000_c_channels,
+			       sizeof(ade9000_c_channels));
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		chan_ptr += AD9000_CHANNELS_PER_PHASE;
+		indio_dev->num_channels += AD9000_CHANNELS_PER_PHASE;
+	}
+
+	*chan_ptr = (struct iio_chan_spec)ADE9000_ALTVOLTAGE_ACCMODE_CHANNEL();
+	indio_dev->num_channels++;
+
+	return 0;
+}
+
+static int ade9000_reset(struct ade9000_state *st)
+{
+	struct gpio_desc *gpio_reset;
+	int ret;
+
+	st->rst_done = false;
+
+	gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset",
+					     GPIOD_OUT_HIGH);
+	if (IS_ERR(gpio_reset))
+		return PTR_ERR(gpio_reset);
+
+	if (gpio_reset) {
+		gpiod_set_value_cansleep(gpio_reset, 1);
+		usleep_range(1, 100);
+		gpiod_set_value_cansleep(gpio_reset, 0);
+		msleep_interruptible(50);
+	} else {
+		ret = regmap_update_bits(st->regmap, ADE9000_REG_CONFIG1,
+					 ADE9000_SWRST_BIT, ADE9000_SWRST_BIT);
+		if (ret)
+			return ret;
+		usleep_range(80, 100);
+	}
+
+	if (!st->rst_done)
+		return -EIO;
+
+	return 0;
+}
+
+static int ade9000_setup(struct ade9000_state *st)
+{
+	int ret;
+
+	ret = regmap_multi_reg_write(st->regmap, ade9000_reg_sequence,
+				     ARRAY_SIZE(ade9000_reg_sequence));
+	if (ret)
+		return ret;
+
+	msleep_interruptible(2);
+
+	ret = regmap_write(st->regmap, ADE9000_REG_STATUS0, GENMASK(31, 0));
+	if (ret)
+		return ret;
+
+	return regmap_write(st->regmap, ADE9000_REG_STATUS1, GENMASK(31, 0));
+}
+
+static const struct iio_buffer_setup_ops ade9000_buffer_ops = {
+	.preenable = &ade9000_buffer_preenable,
+	.postdisable = &ade9000_buffer_postdisable,
+};
+
+static const struct iio_info ade9000_info = {
+	.read_raw = ade9000_read_raw,
+	.write_raw = ade9000_write_raw,
+	.debugfs_reg_access = ade9000_reg_access,
+	.write_event_config = ade9000_write_event_config,
+	.read_event_config = ade9000_read_event_config,
+	.write_event_value = ade9000_write_event_value,
+	.read_event_value = ade9000_read_event_value,
+};
+
+static const struct regmap_config ade9000_regmap_config = {
+	.reg_bits = 16,
+	.val_bits = 32,
+	.zero_flag_mask = true,
+	.cache_type = REGCACHE_RBTREE,
+	.reg_read = ade9000_spi_read_reg,
+	.reg_write = ade9000_spi_write_reg,
+	.volatile_reg = ade9000_is_volatile_reg,
+};
+
+static int ade9000_probe(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev;
+	struct ade9000_state *st;
+	struct regmap *regmap;
+	int irq;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+	if (!indio_dev) {
+		dev_err(&spi->dev, "Unable to allocate ADE9000 IIO");
+		return -ENOMEM;
+	}
+	st = iio_priv(indio_dev);
+
+	st->rx = devm_kcalloc(&spi->dev, ADE9000_RX_DEPTH, sizeof(*st->rx),
+			      GFP_KERNEL);
+	if (!st->rx)
+		return -ENOMEM;
+
+	st->tx = devm_kcalloc(&spi->dev, ADE9000_TX_DEPTH, sizeof(*st->tx),
+			      GFP_KERNEL);
+	if (!st->tx)
+		return -ENOMEM;
+
+	regmap = devm_regmap_init(&spi->dev, NULL, spi, &ade9000_regmap_config);
+	if (IS_ERR(regmap))	{
+		dev_err(&spi->dev, "Unable to allocate ADE9000 regmap");
+		return PTR_ERR(regmap);
+	}
+	spi_set_drvdata(spi, st);
+
+	irq = fwnode_irq_get_byname(dev_fwnode(&spi->dev), "irq0");
+	if (irq < 0) {
+		dev_err(&spi->dev, "Unable to find irq0");
+		return -EINVAL;
+	}
+
+	ret = devm_request_threaded_irq(&spi->dev, irq, NULL,
+					ade9000_irq0_thread,
+					IRQF_ONESHOT,
+					KBUILD_MODNAME, indio_dev);
+	if (ret) {
+		dev_err(&spi->dev, "Failed to request threaded irq: %d\n", ret);
+		return ret;
+	}
+
+	irq = fwnode_irq_get_byname(dev_fwnode(&spi->dev), "irq1");
+	if (irq < 0) {
+		dev_err(&spi->dev, "Unable to find irq1");
+		return -EINVAL;
+	}
+
+	ret = devm_request_threaded_irq(&spi->dev, irq, NULL,
+					ade9000_irq1_thread,
+					IRQF_ONESHOT,
+					KBUILD_MODNAME, indio_dev);
+	if (ret) {
+		dev_err(&spi->dev, "Failed to request threaded irq: %d\n", ret);
+		return ret;
+	}
+
+	st->spi = spi;
+
+	indio_dev->name = spi_get_device_id(spi)->name;
+	indio_dev->dev.parent = &st->spi->dev;
+	indio_dev->info = &ade9000_info;
+	indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+	indio_dev->setup_ops = &ade9000_buffer_ops;
+
+	st->regmap = regmap;
+
+	ret = devm_regulator_get_enable(&spi->dev, "vdd");
+	if (ret)
+		return dev_err_probe(&spi->dev, ret,
+				     "Failed to get and enable vdd regulator\n");
+
+	ret = devm_regulator_get_enable_optional(&spi->dev, "vref");
+	if (ret < 0 && ret != -ENODEV)
+		return dev_err_probe(&spi->dev, ret,
+				     "Failed to get and enable vref regulator\n");
+
+	/* Configure reference selection based on vref regulator availability */
+	if (ret != -ENODEV) {
+		ret = regmap_update_bits(st->regmap, ADE9000_REG_CONFIG1,
+					 ADE9000_EXT_REF_MASK,
+					 ADE9000_EXT_REF_MASK);
+		if (ret)
+			return ret;
+	}
+
+	ret = ade9000_setup_iio_channels(indio_dev);
+	if (ret) {
+		dev_err(&spi->dev, "Failed to set up IIO channels");
+		return ret;
+	}
+	ret = devm_iio_kfifo_buffer_setup(&spi->dev, indio_dev,
+					  &ade9000_buffer_ops);
+	if (ret)
+		return ret;
+
+	ret = ade9000_reset(st);
+	if (ret) {
+		dev_err(&spi->dev, "ADE9000 reset failed");
+		return ret;
+	}
+
+	ret = ade9000_setup(st);
+	if (ret) {
+		dev_err(&spi->dev, "Unable to setup ADE9000");
+		return ret;
+	}
+
+	ret = devm_iio_device_register(&spi->dev, indio_dev);
+	if (ret) {
+		dev_err(&spi->dev, "Unable to register IIO device");
+		return ret;
+	}
+
+	return 0;
+};
+
+static const struct spi_device_id ade9000_id[] = {
+		{"ade9000", 0},
+		{}
+};
+MODULE_DEVICE_TABLE(spi, ade9000_id);
+
+static const struct of_device_id ade9000_of_match[] = {
+	{ .compatible = "adi,ade9000" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, ade9000_of_match);
+
+static struct spi_driver ade9000_driver = {
+		.driver = {
+			.name = "ade9000",
+			.of_match_table = ade9000_of_match,
+		},
+		.probe = ade9000_probe,
+		.id_table = ade9000_id,
+};
+module_spi_driver(ade9000_driver);
+
+MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADE9000");
+MODULE_LICENSE("GPL");
-- 
2.49.0


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

* [PATCH v2 4/5] Documentation: ABI: iio: add sinc4+iir and dsp types
  2025-07-21 11:24 [PATCH v2 1/5] iio: add power and energy measurement modifiers Antoniu Miclaus
  2025-07-21 11:24 ` [PATCH v2 2/5] dt-bindings: iio: adc: add ade9000 Antoniu Miclaus
  2025-07-21 11:24 ` [PATCH v2 3/5] iio: adc: add ade9000 support Antoniu Miclaus
@ 2025-07-21 11:24 ` Antoniu Miclaus
  2025-07-27 13:27   ` Jonathan Cameron
  2025-07-21 11:24 ` [PATCH v2 5/5] Documentation: ABI: iio: adc: add ade9000 ABI Antoniu Miclaus
  2025-07-27 13:18 ` [PATCH v2 1/5] iio: add power and energy measurement modifiers Jonathan Cameron
  4 siblings, 1 reply; 12+ messages in thread
From: Antoniu Miclaus @ 2025-07-21 11:24 UTC (permalink / raw)
  To: jic23, robh, conor+dt, devicetree, linux-iio, linux-kernel
  Cc: Antoniu Miclaus

Add two new filter types to the sysfs-bus-iio ABI documentation:
- "sinc4+iir" for Sinc4 + IIR Low Pass Filter
- "dsp" for Digital Signal Processor filtering

Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
---
 new in v2.
 Documentation/ABI/testing/sysfs-bus-iio | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 0948611227a8..9f51aff4c3e8 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -2337,6 +2337,8 @@ Description:
 		* "sinc5+pf1" - Sinc5 + device specific Post Filter 1.
 		* "wideband" - filter with wideband low ripple passband
 		  and sharp transition band.
+		* "sinc4+iir" - Sinc4 + IIR Low Pass Filter.
+		* "dsp" - Digital Signal Processor filtering.
 
 What:		/sys/bus/iio/devices/iio:deviceX/filter_type
 What:		/sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_filter_type
-- 
2.49.0


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

* [PATCH v2 5/5] Documentation: ABI: iio: adc: add ade9000 ABI
  2025-07-21 11:24 [PATCH v2 1/5] iio: add power and energy measurement modifiers Antoniu Miclaus
                   ` (2 preceding siblings ...)
  2025-07-21 11:24 ` [PATCH v2 4/5] Documentation: ABI: iio: add sinc4+iir and dsp types Antoniu Miclaus
@ 2025-07-21 11:24 ` Antoniu Miclaus
  2025-07-27 13:38   ` Jonathan Cameron
  2025-07-27 13:18 ` [PATCH v2 1/5] iio: add power and energy measurement modifiers Jonathan Cameron
  4 siblings, 1 reply; 12+ messages in thread
From: Antoniu Miclaus @ 2025-07-21 11:24 UTC (permalink / raw)
  To: jic23, robh, conor+dt, devicetree, linux-iio, linux-kernel
  Cc: Antoniu Miclaus

Add sysfs ABI documentation for the ADE9000 ADC driver,
documenting the device-specific attributes and interfaces.

Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
---
 new in v2.
 .../ABI/testing/sysfs-bus-iio-adc-ade9000     | 64 +++++++++++++++++++
 1 file changed, 64 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-ade9000

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-ade9000 b/Documentation/ABI/testing/sysfs-bus-iio-adc-ade9000
new file mode 100644
index 000000000000..fa92fd67483f
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-ade9000
@@ -0,0 +1,64 @@
+What:		/sys/bus/iio/devices/iio:deviceX/wf_cap_en
+KernelVersion:	6.13
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Enable fixed data rate for waveform buffer instead of resampled data.
+		When enabled (1), the waveform buffer uses a fixed data rate.
+		When disabled (0), the waveform buffer uses resampled data.
+
+		This attribute is shared by all channels and represents a device-wide
+		setting that affects the entire waveform buffer configuration.
+		Changes immediately update the hardware configuration.
+
+		Reading: Returns current setting (0 or 1)
+		Writing: Accepts 0, 1
+
+What:		/sys/bus/iio/devices/iio:deviceX/wf_mode
+KernelVersion:	6.13
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Waveform buffer filling and trigger mode configuration.
+
+		Valid values:
+		0 - Stop when waveform buffer is full
+		1 - Continuous fill, stop only on enabled trigger events
+		2 - Continuous filling, center capture around enabled trigger events
+		3 - Streaming mode
+
+		This attribute is shared by all channels and represents a device-wide
+		setting that affects the entire waveform buffer configuration.
+		Changes immediately update the hardware configuration.
+
+		Reading: Returns current mode (0-3)
+		Writing: Accepts values 0, 1, 2, or 3
+
+What:		/sys/bus/iio/devices/iio:deviceX/wf_in_en
+KernelVersion:	6.13
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Enable IN waveform samples readout from waveform buffer.
+		When enabled (1), IN waveform samples are included in buffer readout.
+		When disabled (0), IN waveform samples are excluded from buffer readout.
+
+		This attribute is shared by all channels and represents a device-wide
+		setting that affects the entire waveform buffer configuration.
+		Changes immediately update the hardware configuration.
+
+		Reading: Returns current setting (0 or 1)
+		Writing: Accepts 0, 1
+
+What:		/sys/bus/iio/devices/iio:deviceX/egy_time
+KernelVersion:	6.13
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Energy accumulation time setting for energy registers.
+		This value configures the time period over which energy
+		measurements are accumulated in the ADE9000 device.
+
+		This attribute is shared by all channels and represents a device-wide
+		setting that affects energy accumulation across all phases.
+		Changes immediately update the hardware configuration.
+
+		Reading: Returns current energy accumulation time value
+		Writing: Accepts any valid 32-bit unsigned integer value
+
-- 
2.49.0


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

* Re: [PATCH v2 2/5] dt-bindings: iio: adc: add ade9000
  2025-07-21 11:24 ` [PATCH v2 2/5] dt-bindings: iio: adc: add ade9000 Antoniu Miclaus
@ 2025-07-21 14:23   ` Rob Herring (Arm)
  2025-07-26 20:46   ` David Lechner
  2025-07-27 13:24   ` Jonathan Cameron
  2 siblings, 0 replies; 12+ messages in thread
From: Rob Herring (Arm) @ 2025-07-21 14:23 UTC (permalink / raw)
  To: Antoniu Miclaus; +Cc: devicetree, linux-iio, jic23, linux-kernel, conor+dt


On Mon, 21 Jul 2025 14:24:42 +0300, Antoniu Miclaus wrote:
> Add devicetree bindings support for ade9000.
> 
> Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
> ---
> changes in v2:
>  - move interrup-names near interrupts
>  - remove properties related to the waveform buffer and make them runtime attributes
>  - remove egy-time property and make it a runtime attribute.
>  - remove wf-src and use filter_type in the driver.
>  - add vref, vdd support.
>  - use adc standard channels instead of phase channels.
>  .../bindings/iio/adc/adi,ade9000.yaml         | 122 ++++++++++++++++++
>  1 file changed, 122 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
> 

My bot found errors running 'make dt_binding_check' on your patch:

yamllint warnings/errors:

dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml:
	Error in referenced schema matching $id: http://devicetree.org/schemas/spi/spi-peripheral-props.yaml
	Tried these paths (check schema $id if path is wrong):
	/builds/robherring/dt-review-ci/linux/Documentation/devicetree/spi/spi-peripheral-props.yaml
	/usr/local/lib/python3.11/dist-packages/dtschema/schemas/spi/spi-peripheral-props.yaml

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20250721112455.23948-2-antoniu.miclaus@analog.com

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.


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

* Re: [PATCH v2 2/5] dt-bindings: iio: adc: add ade9000
  2025-07-21 11:24 ` [PATCH v2 2/5] dt-bindings: iio: adc: add ade9000 Antoniu Miclaus
  2025-07-21 14:23   ` Rob Herring (Arm)
@ 2025-07-26 20:46   ` David Lechner
  2025-07-27 13:24   ` Jonathan Cameron
  2 siblings, 0 replies; 12+ messages in thread
From: David Lechner @ 2025-07-26 20:46 UTC (permalink / raw)
  To: Antoniu Miclaus, jic23, robh, conor+dt, devicetree, linux-iio,
	linux-kernel

On 7/21/25 6:24 AM, Antoniu Miclaus wrote:
> Add devicetree bindings support for ade9000.
> 
> Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
> ---
> changes in v2:
>  - move interrup-names near interrupts
>  - remove properties related to the waveform buffer and make them runtime attributes
>  - remove egy-time property and make it a runtime attribute.
>  - remove wf-src and use filter_type in the driver.
>  - add vref, vdd support.
>  - use adc standard channels instead of phase channels.

It seems that you ignored some of my v1 comments without saying why.

>  .../bindings/iio/adc/adi,ade9000.yaml         | 122 ++++++++++++++++++
>  1 file changed, 122 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
> new file mode 100644
> index 000000000000..0176252aae35
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
> @@ -0,0 +1,122 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +# Copyright 2025 Analog Devices Inc.
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/bindings/iio/adc/adi,ade9000.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Analog Devices ADE9000 High Performance, Polyphase Energy Metering driver
> +
> +maintainers:
> +  - Antoniu Miclaus <antoniu.miclaus@analog.com>
> +
> +description: |
> +  The ADE9000 s a highly accurate, fully integrated, multiphase energy and power
> +  quality monitoring device. Superior analog performance and a digital signal
> +  processing (DSP) core enable accurate energy monitoring over a wide dynamic
> +  range. An integrated high end reference ensures low drift over temperature
> +  with a combined drift of less than ±25 ppm/°C maximum for the entire channel
> +  including a programmable gain amplifier (PGA) and an analog-to- digital
> +  converter (ADC).
> +
> +  https://www.analog.com/media/en/technical-documentation/data-sheets/ADE9000.pdf
> +
> +$ref: /schemas/spi/spi-peripheral-props.yaml#
> +
> +properties:
> +  compatible:
> +    enum:
> +      - adi,ade9000
> +
> +  reg:
> +    maxItems: 1
> +
> +  '#address-cells':
> +    const: 1
> +
> +  '#size-cells':
> +    const: 0
> +
> +  spi-max-frequency:
> +    maximum: 20000000
> +
> +  interrupts:
> +    maxItems: 2
> +
> +  interrupt-names:
> +    items:
> +      - const: irq0
> +      - const: irq1

I still think that there should be 3 interrupts, the 3rd one being
the C4/EVENT/DREADY pin.

> +
> +  reset-gpios:
> +    description: |
> +      Must be the device tree identifier of the RESET pin. As the line is
> +      active low, it should be marked GPIO_ACTIVE_LOW.
> +    maxItems: 1
> +
> +  vdd-supply: true
> +
> +  vref-supply: true
> +

There is also a clock input and clock output that we can add
clocks and #clock-cells for.

> +patternProperties:
> +  "^channel@[0-2]$":
> +    $ref: /schemas/iio/adc/adc.yaml#
> +    type: object
> +
> +    properties:
> +      reg:
> +        description: The channel number (0-2 for phases A, B, C)
> +        minimum: 0
> +        maximum: 2
> +
> +    required:
> +      - reg

Why do we need channel properties? Do we expect in some cases to
only have 1 or 2 phases wired up? Otherwise, this provides no additional
information.

> +
> +    additionalProperties: false
> +
> +required:
> +  - compatible
> +  - reg
> +  - reset-gpios
> +  - interrupts
> +  - interrupt-names
> +  - vdd-supply
> +

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

* Re: [PATCH v2 1/5] iio: add power and energy measurement modifiers
  2025-07-21 11:24 [PATCH v2 1/5] iio: add power and energy measurement modifiers Antoniu Miclaus
                   ` (3 preceding siblings ...)
  2025-07-21 11:24 ` [PATCH v2 5/5] Documentation: ABI: iio: adc: add ade9000 ABI Antoniu Miclaus
@ 2025-07-27 13:18 ` Jonathan Cameron
  4 siblings, 0 replies; 12+ messages in thread
From: Jonathan Cameron @ 2025-07-27 13:18 UTC (permalink / raw)
  To: Antoniu Miclaus; +Cc: robh, conor+dt, devicetree, linux-iio, linux-kernel

On Mon, 21 Jul 2025 14:24:41 +0300
Antoniu Miclaus <antoniu.miclaus@analog.com> wrote:

> Add new IIO modifiers to support power and energy measurement devices:
> 
> Power modifiers:
> - IIO_MOD_ACTIVE: Real power consumed by the load
> - IIO_MOD_REACTIVE: Power that oscillates between source and load
> - IIO_MOD_APPARENT: Magnitude of complex power
> - IIO_MOD_FUND_REACTIVE: Reactive power at fundamental frequency
> - IIO_MOD_FACTOR: Power factor (ratio of active to apparent power)
> 
> Signal quality modifiers:
> - IIO_MOD_RMS: Root Mean Square value
> 
> These modifiers enable proper representation of power measurement
> devices like energy meters and power analyzers.
> 
> Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>

This series seems to have dropped it's cover letter between v1 and v2.
Make sure it's there for v3.

> ---
> changes in v2:
>  - drop _accum modifiers
>  - drop dip/swell modifiers
>  - change power factor to be a chan_info

Do we ever need to getting for an IIO buffer? 

>  - voltage -> altvoltage for rms
>  Documentation/ABI/testing/sysfs-bus-iio | 14 ++++++++++++++
>  drivers/iio/industrialio-core.c         |  5 +++++
>  include/linux/iio/types.h               |  1 +
>  include/uapi/linux/iio/types.h          |  4 ++++
>  4 files changed, 24 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index 3bc386995fb6..0948611227a8 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -169,7 +169,12 @@ Description:
>  		is required is a consistent labeling.  Units after application
>  		of scale and offset are millivolts.
>  
> +What:		/sys/bus/iio/devices/iio:deviceX/in_altvoltageY_rms_raw
> +
>  What:		/sys/bus/iio/devices/iio:deviceX/in_powerY_raw
> +What:		/sys/bus/iio/devices/iio:deviceX/in_powerY_active_raw
> +What:		/sys/bus/iio/devices/iio:deviceX/in_powerY_reactive_raw
> +What:		/sys/bus/iio/devices/iio:deviceX/in_powerY_apparent_raw
>  KernelVersion:	4.5
>  Contact:	linux-iio@vger.kernel.org
>  Description:
> @@ -178,6 +183,8 @@ Description:
>  		unique to allow association with event codes. Units after
>  		application of scale and offset are milliwatts.
>  
> +What:		/sys/bus/iio/devices/iio:deviceX/in_powerY_power_factor
> +
>  What:		/sys/bus/iio/devices/iio:deviceX/in_capacitanceY_raw
>  KernelVersion:	3.2
>  Contact:	linux-iio@vger.kernel.org
> @@ -1593,6 +1600,12 @@ Description:
>  
>  What:		/sys/.../iio:deviceX/in_energy_input
>  What:		/sys/.../iio:deviceX/in_energy_raw
> +What:		/sys/.../iio:deviceX/in_energyY_active_raw
> +What:		/sys/.../iio:deviceX/in_energyY_reactive_raw
> +What:		/sys/.../iio:deviceX/in_energyY_apparent_raw
> +What:		/sys/.../iio:deviceX/in_energyY_active_accum_raw
> +What:		/sys/.../iio:deviceX/in_energyY_reactive_accum_raw
> +What:		/sys/.../iio:deviceX/in_energyY_apparent_accum_raw

Seems the docs are still here from v1 for accum versions.



>  /* relies on pairs of these shared then separate */
> @@ -189,6 +193,7 @@ static const char * const iio_chan_info_postfix[] = {
>  	[IIO_CHAN_INFO_ZEROPOINT] = "zeropoint",
>  	[IIO_CHAN_INFO_TROUGH] = "trough_raw",
>  	[IIO_CHAN_INFO_CONVDELAY] = "convdelay",
> +	[IIO_CHAN_INFO_POWER_FACTOR] = "power_factor",

I thought David comment on this in v1.  No _ in new info elements because
of the complexity it makes for userspace. We sometimes do accept them still
if they are related to an existing element - hence trough_raw a few lines up.

>  };
>  /**
>   * iio_device_id() - query the unique ID for the device
> diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
> index ad2761efcc83..f26a0fbd6ab4 100644
> --- a/include/linux/iio/types.h
> +++ b/include/linux/iio/types.h
> @@ -70,6 +70,7 @@ enum iio_chan_info_enum {
>  	IIO_CHAN_INFO_ZEROPOINT,
>  	IIO_CHAN_INFO_TROUGH,
>  	IIO_CHAN_INFO_CONVDELAY,
> +	IIO_CHAN_INFO_POWER_FACTOR,
>  };

>  enum iio_event_type {


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

* Re: [PATCH v2 2/5] dt-bindings: iio: adc: add ade9000
  2025-07-21 11:24 ` [PATCH v2 2/5] dt-bindings: iio: adc: add ade9000 Antoniu Miclaus
  2025-07-21 14:23   ` Rob Herring (Arm)
  2025-07-26 20:46   ` David Lechner
@ 2025-07-27 13:24   ` Jonathan Cameron
  2 siblings, 0 replies; 12+ messages in thread
From: Jonathan Cameron @ 2025-07-27 13:24 UTC (permalink / raw)
  To: Antoniu Miclaus; +Cc: robh, conor+dt, devicetree, linux-iio, linux-kernel

On Mon, 21 Jul 2025 14:24:42 +0300
Antoniu Miclaus <antoniu.miclaus@analog.com> wrote:

> Add devicetree bindings support for ade9000.
> 
> Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
> ---
> changes in v2:
>  - move interrup-names near interrupts
>  - remove properties related to the waveform buffer and make them runtime attributes
>  - remove egy-time property and make it a runtime attribute.
>  - remove wf-src and use filter_type in the driver.
>  - add vref, vdd support.
>  - use adc standard channels instead of phase channels.
>  .../bindings/iio/adc/adi,ade9000.yaml         | 122 ++++++++++++++++++
>  1 file changed, 122 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
> new file mode 100644
> index 000000000000..0176252aae35
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
> @@ -0,0 +1,122 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +# Copyright 2025 Analog Devices Inc.
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/bindings/iio/adc/adi,ade9000.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Analog Devices ADE9000 High Performance, Polyphase Energy Metering driver
> +
> +maintainers:
> +  - Antoniu Miclaus <antoniu.miclaus@analog.com>
> +
> +description: |
> +  The ADE9000 s a highly accurate, fully integrated, multiphase energy and power
> +  quality monitoring device. Superior analog performance and a digital signal
> +  processing (DSP) core enable accurate energy monitoring over a wide dynamic

Getting a bit marketing heavy for my liking!  Make just drop this sentence.

> +  range. An integrated high end reference ensures low drift over temperature
> +  with a combined drift of less than ±25 ppm/°C maximum for the entire channel
> +  including a programmable gain amplifier (PGA) and an analog-to- digital
> +  converter (ADC).
> +
> +  https://www.analog.com/media/en/technical-documentation/data-sheets/ADE9000.pdf
> +
> +$ref: /schemas/spi/spi-peripheral-props.yaml#
> +
> +properties:
> +  compatible:
> +    enum:
> +      - adi,ade9000
> +
> +  reg:
> +    maxItems: 1
> +
> +  '#address-cells':
> +    const: 1
> +
> +  '#size-cells':
> +    const: 0
> +
> +  spi-max-frequency:
> +    maximum: 20000000
> +
> +  interrupts:
> +    maxItems: 2
> +
> +  interrupt-names:
> +    items:
> +      - const: irq0
> +      - const: irq1
> +
> +  reset-gpios:
> +    description: |
> +      Must be the device tree identifier of the RESET pin. As the line is
> +      active low, it should be marked GPIO_ACTIVE_LOW.
> +    maxItems: 1
> +
> +  vdd-supply: true
> +
> +  vref-supply: true
> +
> +patternProperties:
> +  "^channel@[0-2]$":
> +    $ref: /schemas/iio/adc/adc.yaml#
> +    type: object
> +
> +    properties:
> +      reg:
> +        description: The channel number (0-2 for phases A, B, C)
> +        minimum: 0
> +        maximum: 2
> +

What useful info is this providing?  Seems relatively unlikely anyone
will use a 3 phase power monitor for less than 3 phases.  It's not
required to have per channel nodes and many drivers don't because there
isn't anything channels specific in DT and we just leave them all on.

> +    required:
> +      - reg
> +
> +    additionalProperties: false
> +
> +required:
> +  - compatible
> +  - reg
> +  - reset-gpios
> +  - interrupts
> +  - interrupt-names
> +  - vdd-supply
> +
> +allOf:
> +  - $ref: /schemas/spi/spi-peripheral-props.yaml#
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/gpio/gpio.h>
> +    #include <dt-bindings/interrupt-controller/irq.h>
> +
> +    spi {
> +      #address-cells = <1>;
> +      #size-cells = <0>;
> +
> +      adc@0 {
> +          compatible = "adi,ade9000";
> +          reg = <0>;
> +          spi-max-frequency = <7000000>;
> +
> +          #address-cells = <1>;
> +          #size-cells = <0>;
> +          reset-gpios = <&gpio 4 GPIO_ACTIVE_LOW>;
> +          interrupts = <2 IRQ_TYPE_EDGE_FALLING>, <3 IRQ_TYPE_EDGE_FALLING>;
> +          interrupt-names = "irq0", "irq1";
> +          interrupt-parent = <&gpio>;
> +          vdd-supply = <&vdd_reg>;
> +
> +          channel@0 {
> +            reg = <0>;
> +          };
> +          channel@1 {
> +            reg = <1>;
> +          };
> +          channel@2 {
> +            reg = <2>;
> +          };
> +      };
> +    };


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

* Re: [PATCH v2 4/5] Documentation: ABI: iio: add sinc4+iir and dsp types
  2025-07-21 11:24 ` [PATCH v2 4/5] Documentation: ABI: iio: add sinc4+iir and dsp types Antoniu Miclaus
@ 2025-07-27 13:27   ` Jonathan Cameron
  0 siblings, 0 replies; 12+ messages in thread
From: Jonathan Cameron @ 2025-07-27 13:27 UTC (permalink / raw)
  To: Antoniu Miclaus; +Cc: robh, conor+dt, devicetree, linux-iio, linux-kernel

On Mon, 21 Jul 2025 14:24:44 +0300
Antoniu Miclaus <antoniu.miclaus@analog.com> wrote:

> Add two new filter types to the sysfs-bus-iio ABI documentation:
> - "sinc4+iir" for Sinc4 + IIR Low Pass Filter
> - "dsp" for Digital Signal Processor filtering
> 
> Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
> ---
>  new in v2.
>  Documentation/ABI/testing/sysfs-bus-iio | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index 0948611227a8..9f51aff4c3e8 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -2337,6 +2337,8 @@ Description:
>  		* "sinc5+pf1" - Sinc5 + device specific Post Filter 1.
>  		* "wideband" - filter with wideband low ripple passband
>  		  and sharp transition band.
> +		* "sinc4+iir" - Sinc4 + IIR Low Pass Filter.

The fact it's IIR doesn't really tell us much other than the particular design methodology.
Maybe sinc4+lp is more generic?  I'd like others to give inputs on this though before
we choose!

J


> +		* "dsp" - Digital Signal Processor filtering.
>  
>  What:		/sys/bus/iio/devices/iio:deviceX/filter_type
>  What:		/sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_filter_type


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

* Re: [PATCH v2 5/5] Documentation: ABI: iio: adc: add ade9000 ABI
  2025-07-21 11:24 ` [PATCH v2 5/5] Documentation: ABI: iio: adc: add ade9000 ABI Antoniu Miclaus
@ 2025-07-27 13:38   ` Jonathan Cameron
  0 siblings, 0 replies; 12+ messages in thread
From: Jonathan Cameron @ 2025-07-27 13:38 UTC (permalink / raw)
  To: Antoniu Miclaus; +Cc: robh, conor+dt, devicetree, linux-iio, linux-kernel

On Mon, 21 Jul 2025 14:24:45 +0300
Antoniu Miclaus <antoniu.miclaus@analog.com> wrote:

> Add sysfs ABI documentation for the ADE9000 ADC driver,
> documenting the device-specific attributes and interfaces.
> 
> Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
> ---
>  new in v2.
>  .../ABI/testing/sysfs-bus-iio-adc-ade9000     | 64 +++++++++++++++++++
>  1 file changed, 64 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-ade9000
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-ade9000 b/Documentation/ABI/testing/sysfs-bus-iio-adc-ade9000
> new file mode 100644
> index 000000000000..fa92fd67483f
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-ade9000
> @@ -0,0 +1,64 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/wf_cap_en
> +KernelVersion:	6.13
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Enable fixed data rate for waveform buffer instead of resampled data.
> +		When enabled (1), the waveform buffer uses a fixed data rate.
> +		When disabled (0), the waveform buffer uses resampled data.

I had to go read the datasheet section on this to find out what this means.
It is changing the sampling frequency to the wave form frequency / 128.
We need to figure out how to map this to something related to sampling frequency.
Given the fixes sample rates are 8k or larger, maybe we just use anything below 8K to mean
use this mode?  Bit hacky but mostly that's the right thing to do as line frequencies
tend to be lower than that anyway.

> +
> +		This attribute is shared by all channels and represents a device-wide
> +		setting that affects the entire waveform buffer configuration.
> +		Changes immediately update the hardware configuration.
> +
> +		Reading: Returns current setting (0 or 1)
> +		Writing: Accepts 0, 1
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/wf_mode
> +KernelVersion:	6.13
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Waveform buffer filling and trigger mode configuration.
> +
> +		Valid values:
> +		0 - Stop when waveform buffer is full
> +		1 - Continuous fill, stop only on enabled trigger events
> +		2 - Continuous filling, center capture around enabled trigger events
> +		3 - Streaming mode
> +
> +		This attribute is shared by all channels and represents a device-wide
> +		setting that affects the entire waveform buffer configuration.
> +		Changes immediately update the hardware configuration.
> +
> +		Reading: Returns current mode (0-3)
> +		Writing: Accepts values 0, 1, 2, or 3
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/wf_in_en
> +KernelVersion:	6.13
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Enable IN waveform samples readout from waveform buffer.
> +		When enabled (1), IN waveform samples are included in buffer readout.

What does buffer readout mean here? Is this the IIO buffer?  In which case why isn't it
just a channel?

> +		When disabled (0), IN waveform samples are excluded from buffer readout.

Hmm. This waveform buffer stuff needs some more thought.  We should really be mapping this
to a buffer with control over the triggering.   Smells a bit like the old impact sensors
but we never actually got those out of staging ;(


I'd be tempted to drop this support from the initial driver so that we can revisit
it and consider it carefully after the main part of the driver is upstream.

Gut feeling is this needs to be using a separate buffer from main channels with
separate trigger controls etc.  The multibuffer stuff is not yet much used so
there may be some core features missing.

> +
> +		This attribute is shared by all channels and represents a device-wide
> +		setting that affects the entire waveform buffer configuration.
> +		Changes immediately update the hardware configuration.
> +
> +		Reading: Returns current setting (0 or 1)
> +		Writing: Accepts 0, 1
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/egy_time
If we keep this, the name definitely needs some work. Probably needs to be standard
ABI as well.

> +KernelVersion:	6.13
That was a while back!

> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Energy accumulation time setting for energy registers.
> +		This value configures the time period over which energy
> +		measurements are accumulated in the ADE9000 device.

So this is interesting.  Feels a bit like it corresponds to a low pass filter
on a power measurement? Or kind of scaling on the power measurement but in terms
of timing.  I'd like inputs from others on how to handle this but I don't think
a custom ABI is the way to go

> +
> +		This attribute is shared by all channels and represents a device-wide
> +		setting that affects energy accumulation across all phases.
> +		Changes immediately update the hardware configuration.
> +
> +		Reading: Returns current energy accumulation time value
> +		Writing: Accepts any valid 32-bit unsigned integer value
> +


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

* Re: [PATCH v2 3/5] iio: adc: add ade9000 support
  2025-07-21 11:24 ` [PATCH v2 3/5] iio: adc: add ade9000 support Antoniu Miclaus
@ 2025-07-27 14:10   ` Jonathan Cameron
  0 siblings, 0 replies; 12+ messages in thread
From: Jonathan Cameron @ 2025-07-27 14:10 UTC (permalink / raw)
  To: Antoniu Miclaus; +Cc: robh, conor+dt, devicetree, linux-iio, linux-kernel

On Mon, 21 Jul 2025 14:24:43 +0300
Antoniu Miclaus <antoniu.miclaus@analog.com> wrote:

Hi Antoniu,

> Add driver support for the ade9000. highly accurate,
> fully integrated, multiphase energy and power quality
> monitoring device.
wrap at 75 chars.
> 
> Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>

This remains too large to be properly reviewed. Split out some features
from initial version. Based on the docs (that I reviewed first) the
waveform buffer support would be a good thing to drop for now.
Similar for events.

I'll take a quick look only for now (for reference this took me 40 mins and
was not at all thorough).

Jonathan

> diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c
> new file mode 100644
> index 000000000000..aa4f0f635434
> --- /dev/null
> +++ b/drivers/iio/adc/ade9000.c
> @@ -0,0 +1,2290 @@

> +struct ade9000_state {
> +	bool rst_done;
> +	u8 wf_mode;
> +	u8 wf_src;
> +	u32 wfb_trg;
> +	u8 wfb_nr_activ_chan;
> +	u32 wfb_nr_samples;
> +	struct spi_device *spi;
> +	u8 *tx;
> +	u8 *rx;
As later, add these to the buffers you used force alignment on
and don't bother with separate allocations.

> +	struct spi_transfer xfer[2];
> +	struct spi_message spi_msg;
> +	struct regmap *regmap;
> +	union{
> +		u8 byte[ADE9000_WFB_FULL_BUFF_SIZE];
> +		__be32 word[ADE9000_WFB_FULL_BUFF_NR_SAMPLES];
> +	} rx_buff __aligned(IIO_DMA_MINALIGN);
> +	u8 tx_buff[2] __aligned(IIO_DMA_MINALIGN);
Do you access tx_buff whilst rx_buff is being used for DMA?
If not, having them all in one __aligned(IIO_DMA_MINALIGN) should
be fine.  That is, just mark the first one.

> +};
> +
> +static const struct iio_event_spec ade9000_events[] = {
> +	{
> +		.type = IIO_EV_TYPE_MAG,
> +		.dir = IIO_EV_DIR_NONE,
> +		.mask_shared_by_all = BIT(IIO_EV_INFO_ENABLE),
> +	},
> +	{
> +		.type = IIO_EV_TYPE_CHANGE,
> +		.dir = IIO_EV_DIR_NONE,
> +		.mask_shared_by_all = BIT(IIO_EV_INFO_ENABLE),
> +	},
> +	{
> +		.type = IIO_EV_TYPE_THRESH,
> +		.dir = IIO_EV_DIR_NONE,
> +		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
> +		.mask_shared_by_all = BIT(IIO_EV_INFO_VALUE),
> +	},
> +	{
> +		.type = IIO_EV_TYPE_THRESH,
> +		.dir = IIO_EV_DIR_EITHER,
> +		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
> +	},
> +	{
> +		.type = IIO_EV_TYPE_THRESH,
> +		.dir = IIO_EV_DIR_RISING, // For swell
> +		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
> +		.mask_shared_by_all = BIT(IIO_EV_INFO_VALUE),
> +	},
> +	{
> +		.type = IIO_EV_TYPE_THRESH,
> +		.dir = IIO_EV_DIR_FALLING, // For dip
/* for dip */

No C++ style comments (except for SPDX related ones)

> +		.mask_separate = BIT(IIO_EV_INFO_ENABLE),
> +		.mask_shared_by_all = BIT(IIO_EV_INFO_VALUE),
> +	},
> +};
>
> +
> +static const char * const ade9000_filter_type_items[] = {
> +	"sinc4", "sinc4+iir", "dsp"

Is dsp a documented type?

> +};
> +
> +static const int ade9000_filter_type_values[] = {
> +	0, 2, 3
Trailing comma. It's not 'terminated' so we always treat these as potentially
extended in future.

> +};

> +static const struct iio_chan_spec_ext_info ade9000_ext_info[] = {
> +	{
> +		.name = "wf_cap_en",
> +		.read = ade9000_wf_cap_en_show,
> +		.write = ade9000_wf_cap_en_store,
> +		.shared = IIO_SHARED_BY_ALL,
> +	},
> +	{
> +		.name = "wf_mode",
> +		.read = ade9000_wf_mode_show,
> +		.write = ade9000_wf_mode_store,
> +		.shared = IIO_SHARED_BY_ALL,
> +	},
> +	{
> +		.name = "wf_in_en",
> +		.read = ade9000_wf_in_en_show,
> +		.write = ade9000_wf_in_en_store,
> +		.shared = IIO_SHARED_BY_ALL,
> +	},
> +	{
> +		.name = "egy_time",
> +		.read = ade9000_egy_time_show,
> +		.write = ade9000_egy_time_store,
> +		.shared = IIO_SHARED_BY_ALL,
> +	},
> +	IIO_ENUM("filter_type", IIO_SHARED_BY_ALL, &ade9000_filter_type_enum),
> +	IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_ALL, &ade9000_filter_type_enum),
> +	{},
No comma.

Some discussion of these in the docs patch, but in general I'd not try to merge
new custom ABI in an initial driver series.  Get the basic standard stuff in first
and we can have a better discussion of these.

> +};
> +
> +#define ADE9000_CURRENT_CHANNEL(num) {				\
> +	.type = IIO_CURRENT,						\
> +	.channel = num,							\
> +	.address = ADE9000_ADDR_ADJUST(ADE9000_REG_AI_PCF, num),	\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
> +			      BIT(IIO_CHAN_INFO_SCALE) |		\
> +			      BIT(IIO_CHAN_INFO_HARDWAREGAIN),		\
> +	.event_spec = &ade9000_events[0],				\
> +	.num_event_specs = 1,						\
> +	.scan_index = num,						\
> +	.indexed = 1,							\
> +	.scan_type = {							\
> +		.sign = 's',						\
> +		.realbits = 32,						\
> +		.storagebits = 32,					\
> +		.shift = 0,						\
> +		.endianness = IIO_BE,					\
> +	},								\
> +}
> +
> +#define ADE9000_VOLTAGE_CHANNEL(num) {				\
> +	.type = IIO_VOLTAGE,						\
> +	.channel = num,							\
> +	.address = ADE9000_ADDR_ADJUST(ADE9000_REG_AV_PCF, num),	\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
> +			      BIT(IIO_CHAN_INFO_SCALE) |		\
> +			      BIT(IIO_CHAN_INFO_CALIBSCALE) |		\
> +			      BIT(IIO_CHAN_INFO_HARDWAREGAIN) |	\
> +			      BIT(IIO_CHAN_INFO_SAMP_FREQ),		\
> +	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
> +	.event_spec = ade9000_events,					\
> +	.num_event_specs = ARRAY_SIZE(ade9000_events),			\
> +	.scan_index = num + 1,						\
> +	.indexed = 1,							\
> +	.scan_type = {							\
> +		.sign = 's',						\
> +		.realbits = 32,						\
> +		.storagebits = 32,					\
> +		.shift = 0,						\
Shift of 0 is default, so don't set it explicitly.

> +		.endianness = IIO_BE,					\
> +	},								\
> +	.ext_info = ade9000_ext_info,					\
> +}
> +
> +#define ADE9000_CURRENT_RMS_CHANNEL(num) {			\
> +	.type = IIO_CURRENT,						\
> +	.channel = num,							\
> +	.address = ADE9000_ADDR_ADJUST(ADE9000_REG_AIRMS, num),		\
> +	.channel2 = IIO_MOD_RMS,					\
> +	.modified = 1,							\
> +	.indexed = 1,							\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
> +			      BIT(IIO_CHAN_INFO_SCALE) |		\
> +			      BIT(IIO_CHAN_INFO_OFFSET),		\
> +	.scan_index = -1						\
> +}
> +
> +#define ADE9000_ALTVOLTAGE_RMS_CHANNEL(num) {			\
> +	.type = IIO_ALTVOLTAGE,						\
> +	.channel = num,							\
> +	.address = ADE9000_ADDR_ADJUST(ADE9000_REG_AVRMS, num),		\
> +	.channel2 = IIO_MOD_RMS,					\
> +	.modified = 1,							\
> +	.indexed = 1,							\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
> +			      BIT(IIO_CHAN_INFO_SCALE) |		\
> +			      BIT(IIO_CHAN_INFO_OFFSET),		\
> +	.scan_index = -1						\
> +}
> +
> +#define ADE9000_POWER_ACTIVE_CHANNEL(num) {			\
> +	.type = IIO_POWER,						\
> +	.channel = num,							\
> +	.address = ADE9000_ADDR_ADJUST(ADE9000_REG_AWATT, num),		\
> +	.channel2 = IIO_MOD_ACTIVE,					\
> +	.modified = 1,							\
> +	.indexed = 1,							\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
> +			      BIT(IIO_CHAN_INFO_SCALE) |		\
> +			      BIT(IIO_CHAN_INFO_OFFSET) |		\
> +			      BIT(IIO_CHAN_INFO_HARDWAREGAIN) |		\
> +			      BIT(IIO_CHAN_INFO_POWER_FACTOR),		\
> +	.scan_index = -1						\
> +}
> +
> +#define ADE9000_POWER_REACTIVE_CHANNEL(num) {			\
> +	.type = IIO_POWER,						\
> +	.channel = num,							\
> +	.address = ADE9000_ADDR_ADJUST(ADE9000_REG_AVAR, num),		\
> +	.channel2 = IIO_MOD_REACTIVE,					\
> +	.modified = 1,							\
> +	.indexed = 1,							\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
> +			      BIT(IIO_CHAN_INFO_SCALE) |		\
> +			      BIT(IIO_CHAN_INFO_OFFSET) |		\
> +			      BIT(IIO_CHAN_INFO_POWER_FACTOR),		\
> +	.scan_index = -1						\
> +}
> +
> +#define ADE9000_POWER_APPARENT_CHANNEL(num) {			\
> +	.type = IIO_POWER,						\
> +	.channel = num,							\
> +	.address = ADE9000_ADDR_ADJUST(ADE9000_REG_AVA, num),		\
> +	.channel2 = IIO_MOD_APPARENT,					\
> +	.modified = 1,							\
> +	.indexed = 1,							\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
> +			      BIT(IIO_CHAN_INFO_SCALE) |		\
> +			      BIT(IIO_CHAN_INFO_POWER_FACTOR),		\
> +	.scan_index = -1						\
> +}
> +
> + #define ADE9000_ENERGY_ACTIVE_CHANNEL(num, addr) {     \
> +	.type = IIO_ENERGY,                                          \
> +	.channel = num,                                              \
> +	.address = addr,                                             \
Tidyup the tabs and space here.  Tabs is the way to go.
> +	.channel2 = IIO_MOD_ACTIVE,				\
> +	.modified = 1,						\
> +	.indexed = 1,                                                \
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),          \
> +	.scan_index = -1                                             \
> +}
> +
> +#define ADE9000_ENERGY_APPARENT_CHANNEL(num, addr) {     \
> +	.type = IIO_ENERGY,                                          \
> +	.channel = num,                                              \
> +	.address = addr,                                             \
> +	.channel2 = IIO_MOD_APPARENT,				\
> +	.modified = 1,						\
> +	.indexed = 1,                                                \
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),          \
> +	.scan_index = -1                                             \
> +}
> +
> +#define ADE9000_ENERGY_REACTIVE_CHANNEL(num, addr) {     \
> +	.type = IIO_ENERGY,                                          \
> +	.channel = num,                                              \
> +	.address = addr,                                             \
> +	.channel2 = IIO_MOD_REACTIVE,				\
> +	.modified = 1,						\
> +	.indexed = 1,                                                \
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),          \
> +	.scan_index = -1                                             \
> +}
> +
> +#define ADE9000_ALTVOLTAGE_ACCMODE_CHANNEL() {			\
> +	.type = IIO_ALTVOLTAGE,						\
> +	.channel = 0,							\
> +	.address = ADE9000_REG_ACCMODE,					\
> +	.indexed = 1,							\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),			\
> +	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
> +	.scan_index = -1						\
> +}
> +
> +/* IIO channels of the ade9000 for each phase individually */
> +static const struct iio_chan_spec ade9000_a_channels[] = {

Eyeballing the A B C sets here, can you use a macro that just takes, A etc
as as parameter to generate the whole lot. It would be useful to see that
they are effectively the same other than that.

Having done that, consider if it makes sense have have the macros used
here now, or whether it would be clearer to have the chan spec for one phase
clearly laid out.

> +	ADE9000_CURRENT_CHANNEL(ADE9000_PHASE_A_NR),
> +	ADE9000_VOLTAGE_CHANNEL(ADE9000_PHASE_A_NR),
> +	ADE9000_CURRENT_RMS_CHANNEL(ADE9000_PHASE_A_NR),
> +	ADE9000_ALTVOLTAGE_RMS_CHANNEL(ADE9000_PHASE_A_NR),
> +	ADE9000_POWER_ACTIVE_CHANNEL(ADE9000_PHASE_A_NR),
> +	ADE9000_POWER_REACTIVE_CHANNEL(ADE9000_PHASE_A_NR),
> +	ADE9000_POWER_APPARENT_CHANNEL(ADE9000_PHASE_A_NR),
> +	ADE9000_ENERGY_ACTIVE_CHANNEL(ADE9000_PHASE_A_NR, ADE9000_REG_AWATTHR_LO),
> +	ADE9000_ENERGY_APPARENT_CHANNEL(ADE9000_PHASE_A_NR, ADE9000_REG_AVAHR_LO),
> +	ADE9000_ENERGY_REACTIVE_CHANNEL(ADE9000_PHASE_A_NR, ADE9000_REG_AFVARHR_LO),
> +};

> +static int ade9000_spi_write_reg(void *context,
> +				 unsigned int reg,
> +				 unsigned int val)
Same as next function.

> +{
> +	struct device *dev = context;
> +	struct spi_device *spi = to_spi_device(dev);
> +	struct ade9000_state *st = spi_get_drvdata(spi);
> +
> +	u16 addr;
> +	int ret = 0;
> +	struct spi_transfer xfer[] = {
> +		{
> +			.tx_buf = st->tx,
> +		},
> +	};
> +
> +	addr = FIELD_PREP(ADE9000_REG_ADDR_MASK, reg);
> +
> +	put_unaligned_be16(addr, st->tx);
> +	put_unaligned_be32(val, &st->tx[2]);
> +
> +	if (reg > ADE9000_REG_RUN && reg < ADE9000_REG_VERSION) {
> +		put_unaligned_be16(val, &st->tx[2]);
> +		xfer[0].len = 4;
> +	} else {
> +		xfer[0].len = 6;
> +	}
> +
> +	ret = spi_sync_transfer(st->spi, xfer, ARRAY_SIZE(xfer));
> +	if (ret) {
> +		dev_err(&st->spi->dev, "problem when writing register 0x%x",
> +			reg);
> +	}
> +
> +	return ret;
> +}
> +
> +static int ade9000_spi_read_reg(void *context,
> +				unsigned int reg,
> +				unsigned int *val)
wrap closer to 80 chars.

> +{
> +	struct device *dev = context;
> +	struct spi_device *spi = to_spi_device(dev);
> +	struct ade9000_state *st = spi_get_drvdata(spi);
> +
> +	u16 addr;
> +	int ret = 0;
> +	struct spi_transfer xfer[] = {
> +		{
> +			.tx_buf = st->tx,
> +			.len = 2,
> +		},
> +		{
> +			.rx_buf = st->rx,
> +		},
> +	};
> +
> +	addr = FIELD_PREP(ADE9000_REG_ADDR_MASK, reg) |
> +	       ADE9000_REG_READ_BIT_MASK;
> +
> +	put_unaligned_be16(addr, st->tx);
> +
> +	if (reg > ADE9000_REG_RUN && reg < ADE9000_REG_VERSION)
> +		xfer[1].len = 4;
> +	else
> +		xfer[1].len = 6;
> +
> +	ret = spi_sync_transfer(st->spi, xfer, ARRAY_SIZE(xfer));
> +	if (ret) {
> +		dev_err(&st->spi->dev, "error reading register 0x%x",
> +			reg);
> +		goto err_ret;
> +	}
> +
> +	if (reg > ADE9000_REG_RUN && reg < ADE9000_REG_VERSION)
> +		*val = get_unaligned_be16(st->rx);
> +	else
> +		*val = get_unaligned_be32(st->rx);
> +
> +err_ret:

Just return in error paths.

> +	return ret;
> +}
>

> +static irqreturn_t ade9000_irq0_thread(int irq, void *data)

> +static irqreturn_t ade9000_irq1_thread(int irq, void *data)
> +{
> +	struct iio_dev *indio_dev = data;
> +	struct ade9000_state *st = iio_priv(indio_dev);
> +	unsigned int bit = ADE9000_ST1_CROSSING_FIRST;
> +	s64 timestamp = iio_get_time_ns(indio_dev);
> +	u32 handled_irq = 0;
> +	u32 interrupts;
> +	u32 result;
> +	u32 status;
> +	u32 tmp;
> +	int ret;
> +
> +	if (!st->rst_done) {
> +		ret = regmap_read(st->regmap, ADE9000_REG_STATUS1, &result);
> +		if (ret)
> +			return ret;
> +
> +		if (result & ADE9000_ST1_RSTDONE_BIT)
> +			st->rst_done = true;
> +		else
> +			dev_err(&st->spi->dev, "Error testing reset done");
> +
> +		return IRQ_HANDLED;
> +	}
> +
> +	ret = regmap_read(st->regmap, ADE9000_REG_STATUS1, &status);
> +	if (ret)
> +		return IRQ_HANDLED;
> +
> +	ret = regmap_read(st->regmap, ADE9000_REG_MASK1, &interrupts);
> +	if (ret) {
> +		dev_err(&st->spi->dev, "IRQ1 read status fail");
> +		return IRQ_HANDLED;
> +	}
> +
> +	for_each_set_bit_from(bit, (unsigned long *)&interrupts,

Store interrupts into an unsigned long before using this helper.

> +			      ADE9000_ST1_CROSSING_DEPTH){
> +		tmp = status & BIT(bit);
> +
> +		switch (tmp) {
> +		case ADE9000_ST1_ZXVA_BIT:
> +			iio_push_event(indio_dev,
> +				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
> +							    ADE9000_ST1_ZXVA_BIT,
> +							    IIO_EV_TYPE_THRESH,
> +							    IIO_EV_DIR_EITHER),
> +				       timestamp);
> +			handled_irq |= ADE9000_ST1_ZXVA_BIT;
> +			break;
> +		case ADE9000_ST1_ZXTOVA_BIT:
> +			iio_push_event(indio_dev,
> +				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
> +							    ADE9000_ST1_ZXTOVA_BIT,
> +							    IIO_EV_TYPE_THRESH,
> +							    IIO_EV_DIR_EITHER),
> +				       timestamp);
> +			handled_irq |= ADE9000_ST1_ZXTOVA_BIT;
> +			break;
> +		case ADE9000_ST1_ZXIA_BIT:
> +			iio_push_event(indio_dev,
> +				       IIO_UNMOD_EVENT_CODE(IIO_CURRENT,
> +							    ADE9000_ST1_ZXIA_BIT,
> +							    IIO_EV_TYPE_THRESH,
> +							    IIO_EV_DIR_EITHER),
> +				       timestamp);
> +			handled_irq |= ADE9000_ST1_ZXIA_BIT;
> +			break;
> +		case ADE9000_ST1_ZXVB_BIT:
> +			iio_push_event(indio_dev,
> +				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
> +							    ADE9000_ST1_ZXVB_BIT,
> +							    IIO_EV_TYPE_THRESH,
> +							    IIO_EV_DIR_EITHER),
> +				       timestamp);
> +			handled_irq |= ADE9000_ST1_ZXVB_BIT;
> +			break;
> +		case ADE9000_ST1_ZXTOVB_BIT:
> +			iio_push_event(indio_dev,
> +				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
> +							    ADE9000_ST1_ZXTOVB_BIT,
> +							    IIO_EV_TYPE_THRESH,
> +							    IIO_EV_DIR_EITHER),
> +				       timestamp);
> +			handled_irq |= ADE9000_ST1_ZXTOVB_BIT;
> +			break;
> +		case ADE9000_ST1_ZXIB_BIT:
> +			iio_push_event(indio_dev,
> +				       IIO_UNMOD_EVENT_CODE(IIO_CURRENT,
> +							    ADE9000_ST1_ZXIB_BIT,
> +							    IIO_EV_TYPE_THRESH,
> +							    IIO_EV_DIR_EITHER),
> +				       timestamp);
> +			handled_irq |= ADE9000_ST1_ZXIB_BIT;
> +			break;
> +		case ADE9000_ST1_ZXVC_BIT:
> +			iio_push_event(indio_dev,
> +				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
> +							    ADE9000_ST1_ZXVC_BIT,
> +							    IIO_EV_TYPE_THRESH,
> +							    IIO_EV_DIR_EITHER),
> +				       timestamp);
> +			handled_irq |= ADE9000_ST1_ZXVC_BIT;
> +			break;
> +		case ADE9000_ST1_ZXTOVC_BIT:
> +			iio_push_event(indio_dev,
> +				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
> +							    ADE9000_ST1_ZXTOVC_BIT,
> +							    IIO_EV_TYPE_THRESH,
> +							    IIO_EV_DIR_EITHER),
> +				       timestamp);
> +			handled_irq |= ADE9000_ST1_ZXTOVC_BIT;
> +			break;
> +		case ADE9000_ST1_ZXIC_BIT:
> +			iio_push_event(indio_dev,
> +				       IIO_UNMOD_EVENT_CODE(IIO_CURRENT,
> +							    ADE9000_ST1_ZXIC_BIT,
> +							    IIO_EV_TYPE_THRESH,
> +							    IIO_EV_DIR_EITHER),
> +				       timestamp);
> +			handled_irq |= ADE9000_ST1_ZXIC_BIT;
> +			break;
> +		case ADE9000_ST1_SWELLA_BIT:
> +			iio_push_event(indio_dev,
> +				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
> +							    ADE9000_ST1_SWELLA_BIT >> 20,
> +							    IIO_EV_TYPE_THRESH,
> +							    IIO_EV_DIR_RISING),
> +				       timestamp);
> +			handled_irq |= ADE9000_ST1_SWELLA_BIT;
> +			break;
> +		case ADE9000_ST1_SWELLB_BIT:
> +			iio_push_event(indio_dev,
> +				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
> +							    ADE9000_ST1_SWELLB_BIT >> 20,
> +							    IIO_EV_TYPE_THRESH,
> +							    IIO_EV_DIR_RISING),
> +				       timestamp);
> +			handled_irq |= ADE9000_ST1_SWELLB_BIT;
> +			break;
> +		case ADE9000_ST1_SWELLC_BIT:
> +			iio_push_event(indio_dev,
> +				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
> +							    ADE9000_ST1_SWELLC_BIT >> 20,
> +							    IIO_EV_TYPE_THRESH,
> +							    IIO_EV_DIR_RISING),
> +				       timestamp);
> +			handled_irq |= ADE9000_ST1_SWELLC_BIT;
> +			break;
> +		case ADE9000_ST1_DIPA_BIT:
> +			iio_push_event(indio_dev,
> +				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
> +							    ADE9000_ST1_DIPA_BIT >> 20,
> +							    IIO_EV_TYPE_THRESH,
> +							    IIO_EV_DIR_FALLING),
> +				       timestamp);
> +			handled_irq |= ADE9000_ST1_DIPA_BIT;
> +			break;
> +		case ADE9000_ST1_DIPB_BIT:
> +			iio_push_event(indio_dev,
> +				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
> +							    ADE9000_ST1_DIPB_BIT >> 20,
> +							    IIO_EV_TYPE_THRESH,
> +							    IIO_EV_DIR_FALLING),
> +				       timestamp);
> +			handled_irq |= ADE9000_ST1_DIPB_BIT;
> +			break;
> +		case ADE9000_ST1_DIPC_BIT:
> +			iio_push_event(indio_dev,
> +				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
> +							    ADE9000_ST1_DIPC_BIT >> 20,
> +							    IIO_EV_TYPE_THRESH,
> +							    IIO_EV_DIR_FALLING),
> +				       timestamp);
> +			handled_irq |= ADE9000_ST1_DIPC_BIT;
> +			break;
> +		case ADE9000_ST1_SEQERR_BIT:
> +			iio_push_event(indio_dev,
> +				       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
> +							    ADE9000_ST1_SEQERR_BIT >> 12,
> +							    IIO_EV_TYPE_CHANGE,
> +							    IIO_EV_DIR_NONE),
> +				       timestamp);
> +			handled_irq |= ADE9000_ST1_SEQERR_BIT;
> +			break;
> +		default:
> +			return IRQ_HANDLED;
> +		}
> +	}
> +
> +	ret = regmap_write(st->regmap, ADE9000_REG_STATUS1, handled_irq);
> +	if (ret)
> +		return ret;
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int ade9000_read_raw(struct iio_dev *indio_dev,
> +			    struct iio_chan_spec const *chan,
> +			    int *val,
> +			    int *val2,
> +			    long mask)
> +{
...

> +	case IIO_CHAN_INFO_RAW:
> +		if (chan->type == IIO_ENERGY) {
> +			s64 val64;
> +			u32 data[2];
> +			u16 lo_reg = chan->address;
> +
> +			ret = regmap_bulk_read(st->regmap, lo_reg, data, 2);
> +			if (ret)
> +				return ret;
> +
> +			val64 = ((u64)data[1] << 32) | data[0];
> +			*(s64 *)val = val64;

That won't work got 64 bit values and better still will corrupt
a random bit of the stack.

> +			return IIO_VAL_INT;
> +		}
> +
> +		ret = iio_device_claim_direct(indio_dev);
> +		if (ret)
> +			return ret;
> +
> +		ret = regmap_read(st->regmap, chan->address, &measured);
> +		iio_device_release_direct(indio_dev);
> +		if (ret)
> +			return ret;
> +
> +		*val = measured;
> +
> +		return IIO_VAL_INT;
...

> +	case IIO_CHAN_INFO_CALIBSCALE:
> +		ret = regmap_read(st->regmap, ADE9000_REG_PGA_GAIN, &reg);
> +		if (ret)
> +			return ret;
> +		*val = 1 << ((reg >> (8 + chan->channel)) & 0x3);
> +		if (*val > 4)
> +			*val = 4;
		*val = min(val, 4);

> +		return IIO_VAL_INT;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int ade9000_write_raw(struct iio_dev *indio_dev,
> +			     struct iio_chan_spec const *chan,
> +			     int val,
> +			     int val2,
> +			     long mask)
> +{
> +	struct ade9000_state *st = iio_priv(indio_dev);
> +	u32 addr;
> +	u32 tmp;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		if (val != 50 && val != 60)
> +			return -EINVAL;
> +		return regmap_write(st->regmap, ADE9000_REG_ACCMODE,
> +				    (val == 60) ? ADE9000_ACCMODE_60HZ : ADE9000_ACCMODE);
> +	case IIO_CHAN_INFO_OFFSET:
> +		switch (chan->type) {
> +		case IIO_CURRENT:
> +			addr = ADE9000_ADDR_ADJUST(ADE9000_REG_AIRMSOS,
> +						   chan->channel);

Do the regmap writes here and return.  A little more duplication for more
readable code.  Same for all the other cases.


> +			break;
> +		case IIO_VOLTAGE:
> +		case IIO_ALTVOLTAGE:
> +			addr = ADE9000_ADDR_ADJUST(ADE9000_REG_AVRMSOS,
> +						   chan->channel);
> +			break;
> +		case IIO_POWER:
> +			tmp = chan->address;
> +			tmp &= ~ADE9000_PHASE_B_POS_BIT;
> +			tmp &= ~ADE9000_PHASE_C_POS_BIT;
> +
> +			switch (tmp) {
> +			case ADE9000_REG_AWATTOS:
> +				addr = ADE9000_ADDR_ADJUST(ADE9000_REG_AWATTOS,
> +							   chan->channel);
> +				break;
> +			case ADE9000_REG_AVAR:
> +				addr = ADE9000_ADDR_ADJUST(ADE9000_REG_AVAROS,
> +							   chan->channel);
> +				break;
> +			case ADE9000_REG_AFVAR:
> +				addr = ADE9000_ADDR_ADJUST(ADE9000_REG_AFVAROS,
> +							   chan->channel);
> +				break;
> +			default:
> +				return -EINVAL;
> +			}
> +
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +		break;
> +	case IIO_CHAN_INFO_HARDWAREGAIN:
> +		switch (chan->type) {
> +		case IIO_CURRENT:
> +			addr = ADE9000_ADDR_ADJUST(ADE9000_REG_AIGAIN,
> +						   chan->channel);
> +			break;
> +		case IIO_VOLTAGE:
> +			addr = ADE9000_ADDR_ADJUST(ADE9000_REG_AVGAIN,
> +						   chan->channel);
> +			break;
> +		case IIO_POWER:
> +			addr = ADE9000_ADDR_ADJUST(ADE9000_REG_APGAIN,
> +						   chan->channel);
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +		break;
> +	case IIO_CHAN_INFO_CALIBSCALE:
> +		if (val > 4 || val < 1 || val == 3)
> +			return -EINVAL;
> +		addr = ADE9000_REG_PGA_GAIN;
> +		val = ilog2(val) << (chan->channel * 2 + 8);
> +		tmp = 0x3 << (chan->channel * 2 + 8);
> +		return regmap_update_bits(st->regmap, addr, tmp, val);
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return regmap_write(st->regmap, addr, val);

> +}

> +
> +static int ade9000_write_event_config(struct iio_dev *indio_dev,
> +				      const struct iio_chan_spec *chan,
> +				      enum iio_event_type type,
> +				      enum iio_event_direction dir,
> +				      bool state)
> +{
> +	struct ade9000_state *st = iio_priv(indio_dev);
> +	u32 interrupts, tmp;
> +	int ret;
> +	struct irq_wfb_trig {
> +		u32 irq;
> +		u32 wfb_trg;
> +	};
> +
> +	ret = regmap_write(st->regmap, ADE9000_REG_STATUS1, GENMASK(31, 0));
> +	if (ret)
> +		return ret;
> +
> +	if (type == IIO_EV_TYPE_MAG) {
> +		ret = regmap_update_bits(st->regmap, ADE9000_REG_STATUS0,
> +					 ADE9000_ST0_EGYRDY, ADE9000_ST0_EGYRDY);
> +		if (ret)
> +			return ret;
> +		return regmap_update_bits(st->regmap, ADE9000_REG_MASK0,
> +					 ADE9000_ST0_EGYRDY,
> +					 state ? ADE9000_ST1_SEQERR_BIT : 0);
> +	}
> +
> +	if (type == IIO_EV_TYPE_CHANGE)
> +		return regmap_update_bits(st->regmap, ADE9000_REG_MASK1,
> +					 ADE9000_ST1_SEQERR_BIT,
> +					 state ? ADE9000_ST1_SEQERR_BIT : 0);
> +
> +	struct irq_wfb_trig trig_arr[6] = {
> +		{.irq = ADE9000_ST1_ZXVA_BIT,

		{
			.irq = ...
		}, {


> +		 .wfb_trg = ADE9000_WFB_TRG_ZXVA_BIT
> +		},
> +		{.irq = ADE9000_ST1_ZXIA_BIT,
> +		 .wfb_trg = ADE9000_WFB_TRG_ZXIA_BIT
> +		},
> +		{.irq = ADE9000_ST1_ZXVB_BIT,
> +		 .wfb_trg = ADE9000_WFB_TRG_ZXVB_BIT
> +		},
> +		{.irq = ADE9000_ST1_ZXIB_BIT,
> +		 .wfb_trg = ADE9000_WFB_TRG_ZXIB_BIT
> +		},
> +		{.irq = ADE9000_ST1_ZXVC_BIT,
> +		 .wfb_trg = ADE9000_WFB_TRG_ZXVC_BIT
> +		},
> +		{.irq = ADE9000_ST1_ZXIC_BIT,
> +		 .wfb_trg = ADE9000_WFB_TRG_ZXIC_BIT
> +		},
> +	};
> +
> +	if (dir == IIO_EV_DIR_EITHER) {
> +		if (state) {
> +			interrupts |= trig_arr[chan->channel * 2 + chan->type].irq;
> +			st->wfb_trg |= trig_arr[chan->channel * 2 + chan->type].wfb_trg;
> +		} else {
> +			interrupts &= ~trig_arr[chan->channel * 2 + chan->type].irq;
> +			st->wfb_trg &= ~trig_arr[chan->channel * 2 + chan->type].wfb_trg;
> +		}
> +	if (dir == IIO_EV_DIR_NONE) {
> +		switch (chan->channel) {
> +		case ADE9000_PHASE_A_NR:
> +			interrupts |= ADE9000_ST1_ZXTOVA_BIT;
> +			break;
> +		case ADE9000_PHASE_B_NR:
> +			interrupts |= ADE9000_ST1_ZXTOVB_BIT;
> +			break;
> +		case ADE9000_PHASE_C_NR:
> +			interrupts |= ADE9000_ST1_ZXTOVC_BIT;
> +			break;
> +		default:
> +			break;
> +		}
> +
> +		if (state)
> +			interrupts |= tmp;
> +		else
> +			interrupts &= ~tmp;
> +	}
> +	} else if (dir == IIO_EV_DIR_RISING) {
> +		switch (chan->channel) {
> +		case ADE9000_PHASE_A_NR:
> +			tmp |= ADE9000_ST1_SWELLA_BIT;
> +			break;
> +		case ADE9000_PHASE_B_NR:
> +			tmp |= ADE9000_ST1_SWELLB_BIT;
> +			break;
> +		case ADE9000_PHASE_C_NR:
> +			tmp |= ADE9000_ST1_SWELLC_BIT;
> +			break;
> +		default:
> +			break;
> +		}
> +
> +		if (state) {
> +			interrupts |= tmp;
> +			st->wfb_trg |= ADE9000_WFB_TRG_SWELL_BIT;
> +		} else {
> +			interrupts &= ~tmp;
> +			st->wfb_trg &= ~ADE9000_WFB_TRG_SWELL_BIT;
> +		}
> +
> +	} else if (dir == IIO_EV_DIR_FALLING) {
> +		switch (chan->channel) {
> +		case ADE9000_PHASE_A_NR:
> +			tmp |= ADE9000_ST1_DIPA_BIT;
> +			break;
> +		case ADE9000_PHASE_B_NR:
> +			tmp |= ADE9000_ST1_DIPB_BIT;
> +			break;
> +		case ADE9000_PHASE_C_NR:
> +			tmp |= ADE9000_ST1_DIPC_BIT;
> +			break;
> +		default:
> +			break;
> +		}
> +
> +		if (state) {
> +			interrupts |= tmp;
> +			st->wfb_trg |= ADE9000_WFB_TRG_DIP_BIT;
> +		} else {
> +			interrupts &= ~tmp;
> +			st->wfb_trg &= ~ADE9000_WFB_TRG_DIP_BIT;
> +		}
> +	}
> +	return regmap_update_bits(st->regmap, ADE9000_REG_MASK1, interrupts,
> +				  interrupts);
> +}

> +static int ade9000_config_wfb(struct iio_dev *indio_dev)

Maybe expand wfb. It's not a particularly common acronym.

> +{
> +	struct ade9000_state *st = iio_priv(indio_dev);
> +	u32 wfg_cfg_val = 0;
> +	u32 active_scans;
> +
> +	bitmap_to_arr32(&active_scans, indio_dev->active_scan_mask,
> +			indio_dev->masklength);
> +
> +	switch (active_scans) {
> +	case ADE9000_SCAN_POS_IA | ADE9000_SCAN_POS_VA:
> +		wfg_cfg_val = 0x1;
> +		st->wfb_nr_activ_chan = 2;
> +		break;
> +	case ADE9000_SCAN_POS_IB | ADE9000_SCAN_POS_VB:
> +		wfg_cfg_val = 0x2;
> +		st->wfb_nr_activ_chan = 2;
> +		break;
> +	case ADE9000_SCAN_POS_IC | ADE9000_SCAN_POS_VC:
> +		wfg_cfg_val = 0x3;
> +		st->wfb_nr_activ_chan = 2;
> +		break;
> +	case ADE9000_SCAN_POS_IA:
> +		wfg_cfg_val = 0x8;
> +		st->wfb_nr_activ_chan = 1;
> +		break;
> +	case ADE9000_SCAN_POS_VA:
> +		wfg_cfg_val = 0x9;
> +		st->wfb_nr_activ_chan = 1;
> +		break;
> +	case ADE9000_SCAN_POS_IB:
> +		wfg_cfg_val = 0xA;
> +		st->wfb_nr_activ_chan = 1;
> +		break;
> +	case ADE9000_SCAN_POS_VB:
> +		wfg_cfg_val = 0xB;
> +		st->wfb_nr_activ_chan = 1;
> +		break;
> +	case ADE9000_SCAN_POS_IC:
> +		wfg_cfg_val = 0xC;
> +		st->wfb_nr_activ_chan = 1;
> +		break;
> +	case ADE9000_SCAN_POS_VC:
> +		wfg_cfg_val = 0xD;
Feels like an enum is probably appropriate for these settings.

> +		st->wfb_nr_activ_chan = 1;
> +		break;
> +	case ADE9000_SCAN_POS_ALL:
> +		wfg_cfg_val = 0x0;
> +		st->wfb_nr_activ_chan = 6;
> +		break;
> +	default:
> +		dev_err(&st->spi->dev, "Unsupported combination of scans");
> +		return -EINVAL;
> +	}
> +
> +	wfg_cfg_val |= FIELD_PREP(ADE9000_WF_SRC_MASK, st->wf_src);
> +
> +	return regmap_write(st->regmap, ADE9000_REG_WFB_CFG, wfg_cfg_val);
> +}

> +
> +static int ade9000_buffer_postdisable(struct iio_dev *indio_dev)
> +{
> +	struct ade9000_state *st = iio_priv(indio_dev);
> +	u32 interrupts = 0;
> +	int ret;
> +
> +	ret = ade9000_en_wfb(st, false);
> +	if (ret) {
> +		dev_err(&st->spi->dev, "Post-disable wfb disable fail");
> +		return ret;
> +	}
> +
> +	ret = regmap_write(st->regmap, ADE9000_REG_WFB_TRG_CFG, 0x0);
> +	if (ret)
> +		return ret;
> +
> +	interrupts |= ADE9000_ST0_WFB_TRIG_BIT;

interrupts = ADE9000_ST0_WFB_TRIG_BIT | ADE9000_ST0_PAGE_FULL_BIT;
and drop the initialization to zero above.

> +	interrupts |= ADE9000_ST0_PAGE_FULL_BIT;
> +
> +	return regmap_update_bits(st->regmap, ADE9000_REG_MASK0, interrupts, 0);
> +	if (ret) {
> +		dev_err(&st->spi->dev, "Post-disable update maks0 fail");
> +		return ret;
> +	}
> +
> +	return regmap_write(st->regmap, ADE9000_REG_STATUS0, GENMASK(31, 0));
> +}
> +
> +static int ade9000_setup_iio_channels(struct iio_dev *indio_dev)
> +{
> +	struct ade9000_state *st = iio_priv(indio_dev);
> +	struct device *dev = &st->spi->dev;
> +	struct fwnode_handle *phase_node = NULL;
> +	struct iio_chan_spec *chan;
> +	u32 phase_nr;
> +	int ret;
> +
> +	chan = devm_kcalloc(dev,
> +			    (ADE9000_MAX_PHASE_NR *
> +			     ARRAY_SIZE(ade9000_a_channels)) + 1,
> +			    sizeof(*chan), GFP_KERNEL);
> +	if (!chan) {
> +		dev_err(dev, "Unable to allocate ADE9000 channels");
> +		return -ENOMEM;
> +	}
> +	indio_dev->num_channels = 0;
> +	indio_dev->channels = chan;
> +
> +	struct iio_chan_spec *chan_ptr = chan;

only declare local variables inline if doing cleanup.h stuff.

> +
> +	fwnode_for_each_available_child_node(dev_fwnode(dev), phase_node) {
> +		ret = fwnode_property_read_u32(phase_node, "reg", &phase_nr);
As per binding review, i'm doubtful anyone buys a 3 phase chip and wants
only some phases.  Simpler if we just assume everything on and make all this
stuff const.

> +		if (ret) {
> +			dev_err(dev, "Could not read channel reg : %d\n", ret);
> +			return ret;
> +		}
> +
> +		switch (phase_nr) {
> +		case ADE9000_PHASE_A_NR:
> +			memcpy(chan_ptr, ade9000_a_channels,
> +			       sizeof(ade9000_a_channels));
> +			break;
> +		case ADE9000_PHASE_B_NR:
> +			memcpy(chan_ptr, ade9000_b_channels,
> +			       sizeof(ade9000_b_channels));
> +			break;
> +		case ADE9000_PHASE_C_NR:
> +			memcpy(chan_ptr, ade9000_c_channels,
> +			       sizeof(ade9000_c_channels));
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +
> +		chan_ptr += AD9000_CHANNELS_PER_PHASE;
> +		indio_dev->num_channels += AD9000_CHANNELS_PER_PHASE;
> +	}
> +
> +	*chan_ptr = (struct iio_chan_spec)ADE9000_ALTVOLTAGE_ACCMODE_CHANNEL();
> +	indio_dev->num_channels++;
> +
> +	return 0;
> +}
> +
> +static int ade9000_reset(struct ade9000_state *st)
> +{
> +	struct gpio_desc *gpio_reset;
> +	int ret;
> +
> +	st->rst_done = false;
> +
> +	gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset",
> +					     GPIOD_OUT_HIGH);
> +	if (IS_ERR(gpio_reset))
> +		return PTR_ERR(gpio_reset);
> +
> +	if (gpio_reset) {
> +		gpiod_set_value_cansleep(gpio_reset, 1);

You got it high - so I think this isn't needed.

> +		usleep_range(1, 100);

That's an odd range.  fsleep() for expected necessary value perhaps more appropriate.


> +		gpiod_set_value_cansleep(gpio_reset, 0);
> +		msleep_interruptible(50);
flseep for this too.

> +	} else {
> +		ret = regmap_update_bits(st->regmap, ADE9000_REG_CONFIG1,
> +					 ADE9000_SWRST_BIT, ADE9000_SWRST_BIT);
> +		if (ret)
> +			return ret;
> +		usleep_range(80, 100);
fsleep() for all these.

> +	}
> +
> +	if (!st->rst_done)
> +		return -EIO;
> +
> +	return 0;
> +}
> +
> +static int ade9000_setup(struct ade9000_state *st)
> +{
> +	int ret;
> +
> +	ret = regmap_multi_reg_write(st->regmap, ade9000_reg_sequence,
> +				     ARRAY_SIZE(ade9000_reg_sequence));
> +	if (ret)
> +		return ret;
> +
> +	msleep_interruptible(2);
> +
> +	ret = regmap_write(st->regmap, ADE9000_REG_STATUS0, GENMASK(31, 0));

Not obvious why you'd write all 1s to status registers on setup(). Maybe worth
adding a comment.

> +	if (ret)
> +		return ret;
> +
> +	return regmap_write(st->regmap, ADE9000_REG_STATUS1, GENMASK(31, 0));
> +}

> +
> +static int ade9000_probe(struct spi_device *spi)
> +{
> +	struct iio_dev *indio_dev;
> +	struct ade9000_state *st;
> +	struct regmap *regmap;
> +	int irq;
> +	int ret;
> +
> +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
> +	if (!indio_dev) {
> +		dev_err(&spi->dev, "Unable to allocate ADE9000 IIO");
as below. dev_err_probe() for all errors in probe.
Also, lot of spi->dev, so use a local
	struct device *dev = &spi->dev;
in the interests of slightly shorter lines.

> +		return -ENOMEM;
> +	}
> +	st = iio_priv(indio_dev);
> +
> +	st->rx = devm_kcalloc(&spi->dev, ADE9000_RX_DEPTH, sizeof(*st->rx),

These are small.  So Embed them in st with __aligned(IIO_DMA_MINALIGN)

> +			      GFP_KERNEL);
> +	if (!st->rx)
> +		return -ENOMEM;
> +
> +	st->tx = devm_kcalloc(&spi->dev, ADE9000_TX_DEPTH, sizeof(*st->tx),
> +			      GFP_KERNEL);
> +	if (!st->tx)
> +		return -ENOMEM;
> +
> +	regmap = devm_regmap_init(&spi->dev, NULL, spi, &ade9000_regmap_config);
> +	if (IS_ERR(regmap))	{

Odd spacing. Check for stuff like this throughout.

> +		dev_err(&spi->dev, "Unable to allocate ADE9000 regmap");
> +		return PTR_ERR(regmap);
> +	}
> +	spi_set_drvdata(spi, st);
> +
> +	irq = fwnode_irq_get_byname(dev_fwnode(&spi->dev), "irq0");
> +	if (irq < 0) {
> +		dev_err(&spi->dev, "Unable to find irq0");
> +		return -EINVAL;
> +	}
> +
> +	ret = devm_request_threaded_irq(&spi->dev, irq, NULL,
> +					ade9000_irq0_thread,
> +					IRQF_ONESHOT,
> +					KBUILD_MODNAME, indio_dev);
> +	if (ret) {
> +		dev_err(&spi->dev, "Failed to request threaded irq: %d\n", ret);
> +		return ret;
> +	}
> +
> +	irq = fwnode_irq_get_byname(dev_fwnode(&spi->dev), "irq1");
> +	if (irq < 0) {
> +		dev_err(&spi->dev, "Unable to find irq1");
> +		return -EINVAL;
> +	}
> +
> +	ret = devm_request_threaded_irq(&spi->dev, irq, NULL,
> +					ade9000_irq1_thread,
> +					IRQF_ONESHOT,
> +					KBUILD_MODNAME, indio_dev);
> +	if (ret) {
> +		dev_err(&spi->dev, "Failed to request threaded irq: %d\n", ret);
> +		return ret;
> +	}
> +
> +	st->spi = spi;
> +
> +	indio_dev->name = spi_get_device_id(spi)->name;
> +	indio_dev->dev.parent = &st->spi->dev;
> +	indio_dev->info = &ade9000_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> +	indio_dev->setup_ops = &ade9000_buffer_ops;
> +
> +	st->regmap = regmap;
> +
> +	ret = devm_regulator_get_enable(&spi->dev, "vdd");
> +	if (ret)
> +		return dev_err_probe(&spi->dev, ret,
> +				     "Failed to get and enable vdd regulator\n");
> +
> +	ret = devm_regulator_get_enable_optional(&spi->dev, "vref");
> +	if (ret < 0 && ret != -ENODEV)
> +		return dev_err_probe(&spi->dev, ret,
> +				     "Failed to get and enable vref regulator\n");
> +
> +	/* Configure reference selection based on vref regulator availability */
> +	if (ret != -ENODEV) {
> +		ret = regmap_update_bits(st->regmap, ADE9000_REG_CONFIG1,
> +					 ADE9000_EXT_REF_MASK,
> +					 ADE9000_EXT_REF_MASK);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = ade9000_setup_iio_channels(indio_dev);
> +	if (ret) {
> +		dev_err(&spi->dev, "Failed to set up IIO channels");

return dev_err_probe() for all errors in probe() and things only called from probe.

> +		return ret;
> +	}
> +	ret = devm_iio_kfifo_buffer_setup(&spi->dev, indio_dev,
> +					  &ade9000_buffer_ops);
> +	if (ret)
> +		return ret;
> +
> +	ret = ade9000_reset(st);
> +	if (ret) {
> +		dev_err(&spi->dev, "ADE9000 reset failed");
> +		return ret;
> +	}
> +
> +	ret = ade9000_setup(st);
> +	if (ret) {
> +		dev_err(&spi->dev, "Unable to setup ADE9000");
> +		return ret;
> +	}
> +
> +	ret = devm_iio_device_register(&spi->dev, indio_dev);
> +	if (ret) {
> +		dev_err(&spi->dev, "Unable to register IIO device");
> +		return ret;
> +	}
> +
> +	return 0;
> +};
> +
> +static const struct spi_device_id ade9000_id[] = {
> +		{"ade9000", 0},
> +		{}
	{ }
> +};
> +MODULE_DEVICE_TABLE(spi, ade9000_id);
> +
> +static const struct of_device_id ade9000_of_match[] = {
> +	{ .compatible = "adi,ade9000" },
> +	{}
	{ }
is the convention we've fixed on for IIO.  It was a random choice, but
better to be consistent that not

> +};
> +MODULE_DEVICE_TABLE(of, ade9000_of_match);
> +
> +static struct spi_driver ade9000_driver = {
> +		.driver = {
1 tab only.

> +			.name = "ade9000",
> +			.of_match_table = ade9000_of_match,
> +		},
> +		.probe = ade9000_probe,
> +		.id_table = ade9000_id,
> +};
> +module_spi_driver(ade9000_driver);
> +
> +MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>");
> +MODULE_DESCRIPTION("Analog Devices ADE9000");
> +MODULE_LICENSE("GPL");


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

end of thread, other threads:[~2025-07-27 14:10 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-21 11:24 [PATCH v2 1/5] iio: add power and energy measurement modifiers Antoniu Miclaus
2025-07-21 11:24 ` [PATCH v2 2/5] dt-bindings: iio: adc: add ade9000 Antoniu Miclaus
2025-07-21 14:23   ` Rob Herring (Arm)
2025-07-26 20:46   ` David Lechner
2025-07-27 13:24   ` Jonathan Cameron
2025-07-21 11:24 ` [PATCH v2 3/5] iio: adc: add ade9000 support Antoniu Miclaus
2025-07-27 14:10   ` Jonathan Cameron
2025-07-21 11:24 ` [PATCH v2 4/5] Documentation: ABI: iio: add sinc4+iir and dsp types Antoniu Miclaus
2025-07-27 13:27   ` Jonathan Cameron
2025-07-21 11:24 ` [PATCH v2 5/5] Documentation: ABI: iio: adc: add ade9000 ABI Antoniu Miclaus
2025-07-27 13:38   ` Jonathan Cameron
2025-07-27 13:18 ` [PATCH v2 1/5] iio: add power and energy measurement modifiers Jonathan Cameron

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).