* [PATCH 0/3] iio: adc: add support for ADE9000 Energy Monitoring IC
@ 2025-07-11 13:02 Antoniu Miclaus
2025-07-11 13:02 ` [PATCH 1/3] iio: add power and energy measurement modifiers Antoniu Miclaus
` (3 more replies)
0 siblings, 4 replies; 12+ messages in thread
From: Antoniu Miclaus @ 2025-07-11 13:02 UTC (permalink / raw)
To: jic23, robh, conor+dt, devicetree, linux-iio, linux-kernel
Cc: Antoniu Miclaus
This patch series adds support for the Analog Devices ADE9000, a highly
accurate, fully integrated, multiphase energy and power quality monitoring
device. The ADE9000 is capable of measuring energy consumption and power
quality parameters in industrial and commercial applications.
The series includes:
1. New IIO modifiers for power and energy measurement devices, including
support for active/reactive/apparent power, energy accumulation, RMS
measurements, and power quality indicators (swell/dip detection).
2. Device tree bindings for the ADE9000, supporting waveform buffer
configuration, phase configuration, and trigger settings.
3. Complete driver implementation supporting:
- Multi-phase energy measurement (3-phase support)
- Power quality monitoring (voltage swell/dip detection)
- Waveform buffer capture with configurable triggering
- Energy accumulation with configurable time windows
- IIO buffer interface for continuous data streaming
- Event-based notifications for power quality events
The driver provides a comprehensive interface for energy monitoring
applications through the IIO framework, enabling userspace applications
to monitor power consumption, quality, and waveform data.
The driver will be extended in the future to support multiple parts such as
ade9039.
Antoniu Miclaus (3):
iio: add power and energy measurement modifiers
dt-bindings: iio: adc: add ade9000
iio: adc: add ade9000 support
Documentation/ABI/testing/sysfs-bus-iio | 19 +
.../bindings/iio/adc/adi,ade9000.yaml | 157 ++
drivers/iio/adc/Kconfig | 13 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/ade9000.c | 2187 +++++++++++++++++
drivers/iio/industrialio-core.c | 11 +
include/uapi/linux/iio/types.h | 11 +
7 files changed, 2399 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
create mode 100644 drivers/iio/adc/ade9000.c
--
2.49.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 1/3] iio: add power and energy measurement modifiers
2025-07-11 13:02 [PATCH 0/3] iio: adc: add support for ADE9000 Energy Monitoring IC Antoniu Miclaus
@ 2025-07-11 13:02 ` Antoniu Miclaus
2025-07-11 19:23 ` David Lechner
2025-07-11 13:02 ` [PATCH 2/3] dt-bindings: iio: adc: add ade9000 Antoniu Miclaus
` (2 subsequent siblings)
3 siblings, 1 reply; 12+ messages in thread
From: Antoniu Miclaus @ 2025-07-11 13:02 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)
Energy modifiers:
- IIO_MOD_ACTIVE_ACCUM: Accumulated active energy
- IIO_MOD_APPARENT_ACCUM: Accumulated apparent energy
- IIO_MOD_REACTIVE_ACCUM: Accumulated reactive energy
Signal quality modifiers:
- IIO_MOD_RMS: Root Mean Square value
- IIO_MOD_SWELL: Voltage swell detection
- IIO_MOD_DIP: Voltage dip (sag) detection
These modifiers enable proper representation of power measurement
devices like energy meters and power analyzers.
Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
---
Documentation/ABI/testing/sysfs-bus-iio | 19 +++++++++++++++++++
drivers/iio/industrialio-core.c | 11 +++++++++++
include/uapi/linux/iio/types.h | 11 +++++++++++
3 files changed, 41 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 3bc386995fb6..d5c227c03589 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -143,6 +143,9 @@ What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_raw
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_raw
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_raw
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_raw
+What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_rms_raw
+What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_swell_raw
+What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_dip_raw
KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org
Description:
@@ -158,6 +161,7 @@ Description:
component of the signal while the 'q' channel contains the quadrature
component.
+
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_raw
KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org
@@ -170,6 +174,11 @@ Description:
of scale and offset are millivolts.
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
+What: /sys/bus/iio/devices/iio:deviceX/in_powerY_fund_reactive_raw
+What: /sys/bus/iio/devices/iio:deviceX/in_powerY_factor_raw
KernelVersion: 4.5
Contact: linux-iio@vger.kernel.org
Description:
@@ -178,6 +187,7 @@ 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_capacitanceY_raw
KernelVersion: 3.2
Contact: linux-iio@vger.kernel.org
@@ -1593,6 +1603,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:
@@ -1600,6 +1616,7 @@ Description:
device (e.g.: human activity sensors report energy burnt by the
user). Units after application of scale are Joules.
+
What: /sys/.../iio:deviceX/in_distance_input
What: /sys/.../iio:deviceX/in_distance_raw
KernelVersion: 4.0
@@ -1718,6 +1735,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:
@@ -1733,6 +1751,7 @@ Description:
component of the signal while the 'q' channel contains the quadrature
component.
+
What: /sys/.../iio:deviceX/in_energy_en
What: /sys/.../iio:deviceX/in_distance_en
What: /sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_en
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index f13c3aa470d7..daf486cbe0bd 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -152,6 +152,17 @@ 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",
+ [IIO_MOD_FUND_REACTIVE] = "fund_reactive",
+ [IIO_MOD_FACTOR] = "factor",
+ [IIO_MOD_ACTIVE_ACCUM] = "active_accum",
+ [IIO_MOD_APPARENT_ACCUM] = "apparent_accum",
+ [IIO_MOD_REACTIVE_ACCUM] = "reactive_accum",
+ [IIO_MOD_SWELL] = "swell",
+ [IIO_MOD_DIP] = "dip",
};
/* relies on pairs of these shared then separate */
diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
index 3eb0821af7a4..9e05bbddcbe2 100644
--- a/include/uapi/linux/iio/types.h
+++ b/include/uapi/linux/iio/types.h
@@ -108,6 +108,17 @@ 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,
+ IIO_MOD_FUND_REACTIVE,
+ IIO_MOD_FACTOR,
+ IIO_MOD_ACTIVE_ACCUM,
+ IIO_MOD_APPARENT_ACCUM,
+ IIO_MOD_REACTIVE_ACCUM,
+ IIO_MOD_SWELL,
+ IIO_MOD_DIP,
};
enum iio_event_type {
--
2.49.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 2/3] dt-bindings: iio: adc: add ade9000
2025-07-11 13:02 [PATCH 0/3] iio: adc: add support for ADE9000 Energy Monitoring IC Antoniu Miclaus
2025-07-11 13:02 ` [PATCH 1/3] iio: add power and energy measurement modifiers Antoniu Miclaus
@ 2025-07-11 13:02 ` Antoniu Miclaus
2025-07-11 17:43 ` David Lechner
2025-07-11 13:02 ` [PATCH 3/3] iio: adc: add ade9000 support Antoniu Miclaus
2025-07-11 17:09 ` [PATCH 0/3] iio: adc: add support for ADE9000 Energy Monitoring IC David Lechner
3 siblings, 1 reply; 12+ messages in thread
From: Antoniu Miclaus @ 2025-07-11 13:02 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>
---
.../bindings/iio/adc/adi,ade9000.yaml | 157 ++++++++++++++++++
1 file changed, 157 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..660dca4ea9b5
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
@@ -0,0 +1,157 @@
+# 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
+
+properties:
+ compatible:
+ enum:
+ - adi,ade9000
+
+ reg:
+ maxItems: 1
+
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+ spi-max-frequency:
+ maximum: 20000000
+
+ interrupts:
+ maxItems: 2
+
+ 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
+
+ interrupt-names:
+ items:
+ - const: irq0
+ - const: irq1
+
+ adi,wf-cap-en:
+ description: Enable fixed data rate for waveform buffer instead of resampled data
+ type: boolean
+
+ adi,wf-mode:
+ description: |
+ Waveform buffer filling and trigger mode.
+ 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
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1, 2, 3]
+
+ adi,wf-src:
+ description: |
+ Waveform buffer data source selection.
+ 0 - Sinc4 output, at 16 kSPS
+ 1 - Reserved
+ 2 - Sinc4 + IIR LPF output, at 4 kSPS
+ 3 - Current and voltage channel waveform samples, processed by the DSP at 4 kSPS
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 2, 3]
+
+ adi,wf-in-en:
+ description: Enable IN waveform samples readout from waveform buffer
+ type: boolean
+
+ adi,egy-time:
+ description: Energy accumulation time setting for energy registers
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+required:
+ - compatible
+ - reg
+ - reset-gpios
+ - interrupts
+ - interrupt-names
+ - adi,wf-mode
+ - adi,wf-src
+
+additionalProperties: false
+
+patternProperties:
+ "^phase@[0-2]$":
+ type: object
+ description: |
+ Represents the external phases which are externally connected. Each phase
+ has a current, voltage and power component
+
+ properties:
+ reg:
+ description: |
+ The phase represented by a number
+ 0 - Phase A
+ 1 - Phase B
+ 2 - Phase C
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1, 2]
+
+ required:
+ - reg
+
+ additionalProperties: 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>;
+
+ adi,wf-cap-en;
+ adi,wf-mode = <3>;
+ adi,wf-src = <3>;
+ adi,wf-in-en;
+
+ phase@0 {
+ reg = <0>;
+ };
+ phase@1 {
+ reg = <1>;
+ };
+ phase@2 {
+ reg = <2>;
+ };
+ };
+ };
+...
--
2.49.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 3/3] iio: adc: add ade9000 support
2025-07-11 13:02 [PATCH 0/3] iio: adc: add support for ADE9000 Energy Monitoring IC Antoniu Miclaus
2025-07-11 13:02 ` [PATCH 1/3] iio: add power and energy measurement modifiers Antoniu Miclaus
2025-07-11 13:02 ` [PATCH 2/3] dt-bindings: iio: adc: add ade9000 Antoniu Miclaus
@ 2025-07-11 13:02 ` Antoniu Miclaus
2025-07-12 17:56 ` kernel test robot
2025-07-12 22:16 ` kernel test robot
2025-07-11 17:09 ` [PATCH 0/3] iio: adc: add support for ADE9000 Energy Monitoring IC David Lechner
3 siblings, 2 replies; 12+ messages in thread
From: Antoniu Miclaus @ 2025-07-11 13:02 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>
---
drivers/iio/adc/Kconfig | 12 +
drivers/iio/adc/Makefile | 2 +
drivers/iio/adc/ade9000.c | 2187 +++++++++++++++++++++++++++++++++++++
3 files changed, 2201 insertions(+)
create mode 100644 drivers/iio/adc/ade9000.c
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 7ad58ca5173f..37dd72e4ac66 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -489,6 +489,18 @@ 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
+ Support driver for the ADE9000 Multiphase Energy, and Power Quality Monitoring IC Driver, select y to build the driver.
+
+ 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..4799b0bf7151
--- /dev/null
+++ b/drivers/iio/adc/ade9000.c
@@ -0,0 +1,2187 @@
+// 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/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)
+
+/*
+ * 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 18
+
+#define ADE9000_ADDR_ADJUST(addr, chan) \
+ (((chan) == 0 ? 0 : (chan) == 1 ? 2 : 4) << 4 | (addr))
+
+struct ade9000_state {
+ bool rst_done;
+ u8 wf_mode;
+ u32 wfb_trg;
+ u8 wfb_nr_activ_chan;
+ u32 wfb_nr_samples;
+ struct spi_device *spi;
+ u8 *tx;
+ u8 *rx;
+ u32 egy_active_accum[3];
+ u32 egy_apparent_accum[3];
+ u32 egy_reactive_accum[3];
+ 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),
+ },
+};
+
+#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, \
+ }, \
+}
+
+#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_VOLTAGE_RMS_CHANNEL(num) { \
+ .type = IIO_VOLTAGE, \
+ .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), \
+ .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), \
+ .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), \
+ .scan_index = -1 \
+}
+
+#define ADE9000_POWER_FUND_REACTIVE_CHANNEL(num) { \
+ .type = IIO_POWER, \
+ .channel = num, \
+ .address = ADE9000_ADDR_ADJUST(ADE9000_REG_AFVAR, num), \
+ .channel2 = IIO_MOD_FUND_REACTIVE, \
+ .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_FACTOR_CHANNEL(num) { \
+ .type = IIO_POWER, \
+ .channel = num, \
+ .address = ADE9000_ADDR_ADJUST(ADE9000_REG_APF, num), \
+ .channel2 = IIO_MOD_FACTOR, \
+ .modified = 1, \
+ .indexed = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .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_ENERGY_ACTIVE_ACCUM_CHANNEL(num, addr) { \
+ .type = IIO_ENERGY, \
+ .channel = num, \
+ .address = addr, \
+ .channel2 = IIO_MOD_ACTIVE_ACCUM, \
+ .modified = 1, \
+ .indexed = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .scan_index = -1 \
+}
+
+#define ADE9000_ENERGY_APPARENT_ACCUM_CHANNEL(num, addr) { \
+ .type = IIO_ENERGY, \
+ .channel = num, \
+ .address = addr, \
+ .channel2 = IIO_MOD_APPARENT_ACCUM, \
+ .modified = 1, \
+ .indexed = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .scan_index = -1 \
+}
+
+#define ADE9000_ENERGY_REACTIVE_ACCUM_CHANNEL(num, addr) { \
+ .type = IIO_ENERGY, \
+ .channel = num, \
+ .address = addr, \
+ .channel2 = IIO_MOD_REACTIVE_ACCUM, \
+ .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 \
+}
+
+#define ADE9000_VOLTAGE_SWELL_CHANNEL(num) { \
+ .type = IIO_VOLTAGE, \
+ .channel = num, \
+ .address = (ADE9000_REG_SWELLA + num), \
+ .channel2 = IIO_MOD_SWELL, \
+ .modified = 1, \
+ .indexed = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .scan_index = -1 \
+}
+
+#define ADE9000_VOLTAGE_DIP_CHANNEL(num) { \
+ .type = IIO_VOLTAGE, \
+ .channel = num, \
+ .address = (ADE9000_REG_DIPA + num), \
+ .channel2 = IIO_MOD_DIP, \
+ .modified = 1, \
+ .indexed = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .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_VOLTAGE_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_POWER_FUND_REACTIVE_CHANNEL(ADE9000_PHASE_A_NR),
+ ADE9000_POWER_FACTOR_CHANNEL(ADE9000_PHASE_A_NR),
+ ADE9000_VOLTAGE_SWELL_CHANNEL(ADE9000_PHASE_A_NR),
+ ADE9000_VOLTAGE_DIP_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),
+ ADE9000_ENERGY_ACTIVE_ACCUM_CHANNEL(ADE9000_PHASE_A_NR, ADE9000_REG_AWATTHR_LO),
+ ADE9000_ENERGY_APPARENT_ACCUM_CHANNEL(ADE9000_PHASE_A_NR, ADE9000_REG_AVAHR_LO),
+ ADE9000_ENERGY_REACTIVE_ACCUM_CHANNEL(ADE9000_PHASE_A_NR, ADE9000_REG_AFVARHR_LO),
+ ADE9000_ALTVOLTAGE_ACCMODE_CHANNEL(),
+};
+
+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_VOLTAGE_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_POWER_FUND_REACTIVE_CHANNEL(ADE9000_PHASE_B_NR),
+ ADE9000_POWER_FACTOR_CHANNEL(ADE9000_PHASE_B_NR),
+ ADE9000_VOLTAGE_SWELL_CHANNEL(ADE9000_PHASE_B_NR),
+ ADE9000_VOLTAGE_DIP_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),
+ ADE9000_ENERGY_ACTIVE_ACCUM_CHANNEL(ADE9000_PHASE_B_NR, ADE9000_REG_BWATTHR_LO),
+ ADE9000_ENERGY_APPARENT_ACCUM_CHANNEL(ADE9000_PHASE_B_NR, ADE9000_REG_BVAHR_LO),
+ ADE9000_ENERGY_REACTIVE_ACCUM_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_VOLTAGE_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_POWER_FUND_REACTIVE_CHANNEL(ADE9000_PHASE_C_NR),
+ ADE9000_POWER_FACTOR_CHANNEL(ADE9000_PHASE_C_NR),
+ ADE9000_VOLTAGE_SWELL_CHANNEL(ADE9000_PHASE_C_NR),
+ ADE9000_VOLTAGE_DIP_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),
+ ADE9000_ENERGY_ACTIVE_ACCUM_CHANNEL(ADE9000_PHASE_C_NR, ADE9000_REG_CWATTHR_LO),
+ ADE9000_ENERGY_APPARENT_ACCUM_CHANNEL(ADE9000_PHASE_C_NR, ADE9000_REG_CVAHR_LO),
+ ADE9000_ENERGY_REACTIVE_ACCUM_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, ¤t_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)) {
+ u32 data;
+ /* As per datasheet, reading the _HI registers is enough. */
+
+ ret = regmap_read(st->regmap, (ADE9000_REG_AWATTHR_LO + 1), &data);
+ if (ret)
+ return ret;
+
+ st->egy_active_accum[0] = data;
+
+ ret = regmap_read(st->regmap, (ADE9000_REG_BWATTHR_LO + 1), &data);
+ if (ret)
+ return ret;
+
+ st->egy_active_accum[1] = data;
+
+ ret = regmap_read(st->regmap, (ADE9000_REG_CWATTHR_LO + 1), &data);
+ if (ret)
+ return ret;
+
+ st->egy_active_accum[2] = data;
+
+ ret = regmap_read(st->regmap, (ADE9000_REG_AVAHR_LO + 1), &data);
+ if (ret)
+ return ret;
+
+ st->egy_apparent_accum[0] = data;
+
+ ret = regmap_read(st->regmap, (ADE9000_REG_BVAHR_LO + 1), &data);
+ if (ret)
+ return ret;
+
+ st->egy_apparent_accum[1] = data;
+
+ ret = regmap_read(st->regmap, (ADE9000_REG_CVAHR_LO + 1), &data);
+ if (ret)
+ return ret;
+
+ st->egy_apparent_accum[2] = data;
+
+ ret = regmap_read(st->regmap, (ADE9000_REG_AFVARHR_LO + 1), &data);
+ if (ret)
+ return ret;
+
+ st->egy_reactive_accum[0] = data;
+
+ ret = regmap_read(st->regmap, (ADE9000_REG_BFVARHR_LO + 1), &data);
+ if (ret)
+ return ret;
+
+ st->egy_reactive_accum[1] = data;
+
+ ret = regmap_read(st->regmap, (ADE9000_REG_CFVARHR_LO + 1), &data);
+ if (ret)
+ return ret;
+
+ st->egy_reactive_accum[2] = data;
+
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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, ®);
+ if (ret)
+ return ret;
+ *val = (reg & BIT(8)) ? 60 : 50;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_RAW:
+ if (chan->type == IIO_ENERGY && chan->modified &&
+ chan->channel2 == IIO_MOD_ACTIVE_ACCUM) {
+ *val = st->egy_active_accum[chan->channel];
+ return IIO_VAL_INT;
+ }
+
+ if (chan->type == IIO_ENERGY && chan->modified &&
+ chan->channel2 == IIO_MOD_APPARENT_ACCUM) {
+ *val = st->egy_apparent_accum[chan->channel];
+ return IIO_VAL_INT;
+ }
+
+ if (chan->type == IIO_ENERGY && chan->modified &&
+ chan->channel2 == IIO_MOD_REACTIVE_ACCUM) {
+ *val = st->egy_reactive_accum[chan->channel];
+ return IIO_VAL_INT;
+ }
+
+ 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);
+ if (ret)
+ return ret;
+
+ iio_device_release_direct(indio_dev);
+ *val = measured;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_CURRENT || chan->type == IIO_VOLTAGE) {
+ 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, ®);
+ 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:
+ 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;
+ u32 tmp;
+ int ret;
+
+ 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;
+ }
+
+ if (device_property_read_bool(&st->spi->dev, "adi,wf-cap-en"))
+ wfg_cfg_val |= FIELD_PREP(ADE9000_WF_CAP_SEL_MASK, 1);
+
+ ret = device_property_read_u32(&st->spi->dev, "adi,wf-mode", &tmp);
+ if (ret) {
+ dev_err(&st->spi->dev, "Failed to get wf-mode: %d\n", ret);
+ return ret;
+ }
+ wfg_cfg_val |= FIELD_PREP(ADE9000_WF_MODE_MASK, tmp);
+ st->wf_mode = tmp;
+
+ ret = device_property_read_u32(&st->spi->dev, "adi,wf-src", &tmp);
+ if (ret) {
+ dev_err(&st->spi->dev,
+ "Failed to get wf-src: %d\n",
+ ret);
+ return ret;
+ }
+ wfg_cfg_val |= FIELD_PREP(ADE9000_WF_SRC_MASK, tmp);
+
+ if (device_property_read_bool(&st->spi->dev, "adi,wf-in-en"))
+ wfg_cfg_val |= FIELD_PREP(ADE9000_WF_IN_EN_MASK, 1);
+
+ 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;
+ }
+
+ 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 = 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 ret;
+};
+
+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",
+ },
+ .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
* Re: [PATCH 0/3] iio: adc: add support for ADE9000 Energy Monitoring IC
2025-07-11 13:02 [PATCH 0/3] iio: adc: add support for ADE9000 Energy Monitoring IC Antoniu Miclaus
` (2 preceding siblings ...)
2025-07-11 13:02 ` [PATCH 3/3] iio: adc: add ade9000 support Antoniu Miclaus
@ 2025-07-11 17:09 ` David Lechner
3 siblings, 0 replies; 12+ messages in thread
From: David Lechner @ 2025-07-11 17:09 UTC (permalink / raw)
To: Antoniu Miclaus, jic23, robh, conor+dt, devicetree, linux-iio,
linux-kernel
On 7/11/25 8:02 AM, Antoniu Miclaus wrote:
> This patch series adds support for the Analog Devices ADE9000, a highly
> accurate, fully integrated, multiphase energy and power quality monitoring
> device. The ADE9000 is capable of measuring energy consumption and power
> quality parameters in industrial and commercial applications.
>
> The series includes:
>
> 1. New IIO modifiers for power and energy measurement devices, including
> support for active/reactive/apparent power, energy accumulation, RMS
> measurements, and power quality indicators (swell/dip detection).
>
> 2. Device tree bindings for the ADE9000, supporting waveform buffer
> configuration, phase configuration, and trigger settings.
>
> 3. Complete driver implementation supporting:
You will get much better review if we can spread this out across
multiple series instead of trying to do everything all at once.
At a minimum, each of these should be a separate patch. 2000 lines
in one patch is just way too much to grok at once.
> - Multi-phase energy measurement (3-phase support)
> - Power quality monitoring (voltage swell/dip detection)
> - Waveform buffer capture with configurable triggering
> - Energy accumulation with configurable time windows
> - IIO buffer interface for continuous data streaming
> - Event-based notifications for power quality events
>
> The driver provides a comprehensive interface for energy monitoring
> applications through the IIO framework, enabling userspace applications
> to monitor power consumption, quality, and waveform data.
>
> The driver will be extended in the future to support multiple parts such as
> ade9039.
>
> Antoniu Miclaus (3):
> iio: add power and energy measurement modifiers
It looks like [PATCH 1/3] didn't get sent out.
> dt-bindings: iio: adc: add ade9000
> iio: adc: add ade9000 support
>
> Documentation/ABI/testing/sysfs-bus-iio | 19 +
> .../bindings/iio/adc/adi,ade9000.yaml | 157 ++
> drivers/iio/adc/Kconfig | 13 +
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/ade9000.c | 2187 +++++++++++++++++
> drivers/iio/industrialio-core.c | 11 +
> include/uapi/linux/iio/types.h | 11 +
> 7 files changed, 2399 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
> create mode 100644 drivers/iio/adc/ade9000.c
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 2/3] dt-bindings: iio: adc: add ade9000
2025-07-11 13:02 ` [PATCH 2/3] dt-bindings: iio: adc: add ade9000 Antoniu Miclaus
@ 2025-07-11 17:43 ` David Lechner
0 siblings, 0 replies; 12+ messages in thread
From: David Lechner @ 2025-07-11 17:43 UTC (permalink / raw)
To: Antoniu Miclaus, jic23, robh, conor+dt, devicetree, linux-iio,
linux-kernel
On 7/11/25 8:02 AM, Antoniu Miclaus wrote:
> Add devicetree bindings support for ade9000.
>
> Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
> ---
> .../bindings/iio/adc/adi,ade9000.yaml | 157 ++++++++++++++++++
> 1 file changed, 157 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..660dca4ea9b5
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
> @@ -0,0 +1,157 @@
> +# 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
> +
> +properties:
> + compatible:
> + enum:
> + - adi,ade9000
> +
> + reg:
> + maxItems: 1
> +
> + '#address-cells':
> + const: 1
> +
> + '#size-cells':
> + const: 0
> +
> + spi-max-frequency:
> + maximum: 20000000
> +
> + interrupts:
> + maxItems: 2
Let's keep this together with interrupt-names.
> +
> + 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
> +
> + interrupt-names:
> + items:
> + - const: irq0
> + - const: irq1
The C4/EVENT/DREADY pins can also be an output and would make sense
as a 3rd interrupt.
> +
> + adi,wf-cap-en:
> + description: Enable fixed data rate for waveform buffer instead of resampled data
> + type: boolean
This one sounds like a runtime setting depending on how you want to
configure sampling.
> +
> + adi,wf-mode:
> + description: |
> + Waveform buffer filling and trigger mode.
> + 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
> + $ref: /schemas/types.yaml#/definitions/uint32
> + enum: [0, 1, 2, 3]
This one sounds like something that would be determined by how
a buffered read is setup in the IIO driver, not something that
should be in the devicetree.
> +
> + adi,wf-src:
> + description: |
> + Waveform buffer data source selection.
> + 0 - Sinc4 output, at 16 kSPS
> + 1 - Reserved
> + 2 - Sinc4 + IIR LPF output, at 4 kSPS
> + 3 - Current and voltage channel waveform samples, processed by the DSP at 4 kSPS
> + $ref: /schemas/types.yaml#/definitions/uint32
> + enum: [0, 2, 3]
There is a standard filter_type attribute that should be used
for selecting the filter type rather than hard-coding it in the
devicetree.
> +
> + adi,wf-in-en:
> + description: Enable IN waveform samples readout from waveform buffer
> + type: boolean
This one also sounds like something that would be configured based
on how a scan is setup at runtime.
> +
> + adi,egy-time:
> + description: Energy accumulation time setting for energy registers
> + $ref: /schemas/types.yaml#/definitions/uint32
And another one that sounds like it would be more of a runtime
setting based on what type of data capture you are setting up.
> +
Power and reference voltage supplies are missing.
ref-supply, vdd-supply
And there is a clock input and a clock output that we could
add trivial bindings for.
> +required:
> + - compatible
> + - reg
> + - reset-gpios
> + - interrupts
> + - interrupt-names
> + - adi,wf-mode
> + - adi,wf-src
> +
> +additionalProperties: false
> +
> +patternProperties:
> + "^phase@[0-2]$":
> + type: object
> + description: |
> + Represents the external phases which are externally connected. Each phase
> + has a current, voltage and power component
> +
> + properties:
> + reg:
> + description: |
> + The phase represented by a number
> + 0 - Phase A
> + 1 - Phase B
> + 2 - Phase C
> + $ref: /schemas/types.yaml#/definitions/uint32
> + enum: [0, 1, 2]
If we are calling this an ADC, we should be using adc.yaml and
calling these channel@ rather than phase@. There could be a custom
adi,phase property for each channel if needed. But I'm guessing
IAP/IAN is always going to be phase A, IBP/IBN is always going to
be phase B, etc. so I'm not sure we actually need any special channel
properties at this point.
> +
> + required:
> + - reg
> +
> + additionalProperties: false
> +
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/3] iio: add power and energy measurement modifiers
2025-07-11 13:02 ` [PATCH 1/3] iio: add power and energy measurement modifiers Antoniu Miclaus
@ 2025-07-11 19:23 ` David Lechner
2025-07-24 13:09 ` Jonathan Cameron
0 siblings, 1 reply; 12+ messages in thread
From: David Lechner @ 2025-07-11 19:23 UTC (permalink / raw)
To: Antoniu Miclaus, jic23, robh, conor+dt, devicetree, linux-iio,
linux-kernel
On 7/11/25 8:02 AM, Antoniu Miclaus 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
These make sense a modifiers since they are components of a single
measured value.
> - IIO_MOD_FUND_REACTIVE: Reactive power at fundamental frequency
This one seems like there should just be a separate channel
with IIO_POWER + IIO_MOD_REACTIVE since it is measuring a different
value.
> - IIO_MOD_FACTOR: Power factor (ratio of active to apparent power)
Power factor seems like it should be a IIO_CHAN_INFO_ rather than
IIO_MOD_. It is also unitless, so doesn't make sense to be part
of power_raw which would imply that it shuold be converted to Watts.
>
> Energy modifiers:
> - IIO_MOD_ACTIVE_ACCUM: Accumulated active energy
> - IIO_MOD_APPARENT_ACCUM: Accumulated apparent energy
> - IIO_MOD_REACTIVE_ACCUM: Accumulated reactive energy
As below, this one seems like there should be a separate
energy channel for accumulated energy.
>
> Signal quality modifiers:
> - IIO_MOD_RMS: Root Mean Square value
Suprised we don't have something like this already. altvoltageY isn't
clear about if the value is peak-to-peak or RMS.
> - IIO_MOD_SWELL: Voltage swell detection
> - IIO_MOD_DIP: Voltage dip (sag) detection
These sound like events, not modifiers.
>
> These modifiers enable proper representation of power measurement
> devices like energy meters and power analyzers.
>
> Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
> ---
> Documentation/ABI/testing/sysfs-bus-iio | 19 +++++++++++++++++++
> drivers/iio/industrialio-core.c | 11 +++++++++++
> include/uapi/linux/iio/types.h | 11 +++++++++++
> 3 files changed, 41 insertions(+)
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index 3bc386995fb6..d5c227c03589 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -143,6 +143,9 @@ What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_raw
> What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_raw
> What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_raw
> What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_raw
> +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_rms_raw
This should be on altvoltage, not voltage.
Also, the exisiting i and q are wrong for the same reason.
> +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_swell_raw
> +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_dip_raw
> KernelVersion: 2.6.35
> Contact: linux-iio@vger.kernel.org
> Description:
> @@ -158,6 +161,7 @@ Description:
> component of the signal while the 'q' channel contains the quadrature
> component.
>
> +
> What: /sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_raw
> KernelVersion: 2.6.35
> Contact: linux-iio@vger.kernel.org
> @@ -170,6 +174,11 @@ Description:
> of scale and offset are millivolts.
>
> 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
> +What: /sys/bus/iio/devices/iio:deviceX/in_powerY_fund_reactive_raw
> +What: /sys/bus/iio/devices/iio:deviceX/in_powerY_factor_raw
As above, power factor doesn't have units of watts so doesn't belong here.
> KernelVersion: 4.5
> Contact: linux-iio@vger.kernel.org
> Description:
> @@ -178,6 +187,7 @@ 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_capacitanceY_raw
> KernelVersion: 3.2
> Contact: linux-iio@vger.kernel.org
> @@ -1593,6 +1603,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
I think the accumulated would just be a separate channel, not a modifier.
> KernelVersion: 4.0
> Contact: linux-iio@vger.kernel.org
> Description:
> @@ -1600,6 +1616,7 @@ Description:
> device (e.g.: human activity sensors report energy burnt by the
> user). Units after application of scale are Joules.
>
> +
Stray blank line.
> What: /sys/.../iio:deviceX/in_distance_input
> What: /sys/.../iio:deviceX/in_distance_raw
> KernelVersion: 4.0
> @@ -1718,6 +1735,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
Interesting that we don't have altcurrent like we do altvoltage.
And there don't appeary to be any users of i and q modifiers on current
so that can be dropped.
> KernelVersion: 3.17
> Contact: linux-iio@vger.kernel.org
> Description:
> @@ -1733,6 +1751,7 @@ Description:
> component of the signal while the 'q' channel contains the quadrature
> component.
>
> +
Stray blank line.
> What: /sys/.../iio:deviceX/in_energy_en
> What: /sys/.../iio:deviceX/in_distance_en
> What: /sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_en
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index f13c3aa470d7..daf486cbe0bd 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -152,6 +152,17 @@ 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",
> + [IIO_MOD_FUND_REACTIVE] = "fund_reactive",
> + [IIO_MOD_FACTOR] = "factor",
> + [IIO_MOD_ACTIVE_ACCUM] = "active_accum",
> + [IIO_MOD_APPARENT_ACCUM] = "apparent_accum",
> + [IIO_MOD_REACTIVE_ACCUM] = "reactive_accum",
If we end up keeping any of the two-word modifiers, the actual string
needs to omit the "_". The readability isn't so great, but it makes it
much easier to machine parse if we can assume the modifier is always
"oneword".
> + [IIO_MOD_SWELL] = "swell",
> + [IIO_MOD_DIP] = "dip",
> };
>
> /* relies on pairs of these shared then separate */
> diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
> index 3eb0821af7a4..9e05bbddcbe2 100644
> --- a/include/uapi/linux/iio/types.h
> +++ b/include/uapi/linux/iio/types.h
> @@ -108,6 +108,17 @@ 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,
> + IIO_MOD_FUND_REACTIVE,
> + IIO_MOD_FACTOR,
> + IIO_MOD_ACTIVE_ACCUM,
> + IIO_MOD_APPARENT_ACCUM,
> + IIO_MOD_REACTIVE_ACCUM,
> + IIO_MOD_SWELL,
> + IIO_MOD_DIP,
> };
>
> enum iio_event_type {
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 3/3] iio: adc: add ade9000 support
2025-07-11 13:02 ` [PATCH 3/3] iio: adc: add ade9000 support Antoniu Miclaus
@ 2025-07-12 17:56 ` kernel test robot
2025-07-12 22:16 ` kernel test robot
1 sibling, 0 replies; 12+ messages in thread
From: kernel test robot @ 2025-07-12 17:56 UTC (permalink / raw)
To: Antoniu Miclaus, jic23, robh, conor+dt, devicetree, linux-iio,
linux-kernel
Cc: oe-kbuild-all, Antoniu Miclaus
Hi Antoniu,
kernel test robot noticed the following build warnings:
[auto build test WARNING on jic23-iio/togreg]
[also build test WARNING on robh/for-next linus/master v6.16-rc5 next-20250711]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Antoniu-Miclaus/iio-add-power-and-energy-measurement-modifiers/20250712-022300
base: https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
patch link: https://lore.kernel.org/r/20250711130241.159143-4-antoniu.miclaus%40analog.com
patch subject: [PATCH 3/3] iio: adc: add ade9000 support
config: nios2-randconfig-002-20250713 (https://download.01.org/0day-ci/archive/20250713/202507130110.J1mOxDr1-lkp@intel.com/config)
compiler: nios2-linux-gcc (GCC) 10.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250713/202507130110.J1mOxDr1-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202507130110.J1mOxDr1-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/iio/adc/ade9000.c:2170:34: warning: 'ade9000_of_match' defined but not used [-Wunused-const-variable=]
2170 | static const struct of_device_id ade9000_of_match[] = {
| ^~~~~~~~~~~~~~~~
vim +/ade9000_of_match +2170 drivers/iio/adc/ade9000.c
2169
> 2170 static const struct of_device_id ade9000_of_match[] = {
2171 { .compatible = "adi,ade9000" },
2172 {}
2173 };
2174 MODULE_DEVICE_TABLE(of, ade9000_of_match);
2175
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 3/3] iio: adc: add ade9000 support
2025-07-11 13:02 ` [PATCH 3/3] iio: adc: add ade9000 support Antoniu Miclaus
2025-07-12 17:56 ` kernel test robot
@ 2025-07-12 22:16 ` kernel test robot
1 sibling, 0 replies; 12+ messages in thread
From: kernel test robot @ 2025-07-12 22:16 UTC (permalink / raw)
To: Antoniu Miclaus, jic23, robh, conor+dt, devicetree, linux-iio,
linux-kernel
Cc: oe-kbuild-all, Antoniu Miclaus
Hi Antoniu,
kernel test robot noticed the following build warnings:
[auto build test WARNING on jic23-iio/togreg]
[also build test WARNING on robh/for-next linus/master v6.16-rc5 next-20250711]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Antoniu-Miclaus/iio-add-power-and-energy-measurement-modifiers/20250712-022300
base: https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
patch link: https://lore.kernel.org/r/20250711130241.159143-4-antoniu.miclaus%40analog.com
patch subject: [PATCH 3/3] iio: adc: add ade9000 support
config: arc-randconfig-r133-20250713 (https://download.01.org/0day-ci/archive/20250713/202507130521.iaXBguXP-lkp@intel.com/config)
compiler: arc-linux-gcc (GCC) 10.5.0
reproduce: (https://download.01.org/0day-ci/archive/20250713/202507130521.iaXBguXP-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202507130521.iaXBguXP-lkp@intel.com/
sparse warnings: (new ones prefixed by >>)
>> drivers/iio/adc/ade9000.c:1176:40: sparse: sparse: cast truncates bits from constant value (100000 becomes 0)
drivers/iio/adc/ade9000.c:1185:40: sparse: sparse: cast truncates bits from constant value (200000 becomes 0)
drivers/iio/adc/ade9000.c:1194:40: sparse: sparse: cast truncates bits from constant value (400000 becomes 0)
drivers/iio/adc/ade9000.c:1203:40: sparse: sparse: cast truncates bits from constant value (800000 becomes 0)
drivers/iio/adc/ade9000.c:1212:40: sparse: sparse: cast truncates bits from constant value (1000000 becomes 0)
drivers/iio/adc/ade9000.c:1221:40: sparse: sparse: cast truncates bits from constant value (2000000 becomes 0)
drivers/iio/adc/ade9000.c:1230:40: sparse: sparse: cast truncates bits from constant value (40000 becomes 0)
drivers/iio/adc/ade9000.c:1249:12: sparse: sparse: context imbalance in 'ade9000_read_raw' - different lock contexts for basic block
>> drivers/iio/adc/ade9000.c:1761:9: sparse: sparse: dereference of noderef expression
>> drivers/iio/adc/ade9000.c:1761:9: sparse: sparse: dereference of noderef expression
vim +1176 drivers/iio/adc/ade9000.c
1051
1052 static irqreturn_t ade9000_irq1_thread(int irq, void *data)
1053 {
1054 struct iio_dev *indio_dev = data;
1055 struct ade9000_state *st = iio_priv(indio_dev);
1056 unsigned int bit = ADE9000_ST1_CROSSING_FIRST;
1057 s64 timestamp = iio_get_time_ns(indio_dev);
1058 u32 handled_irq = 0;
1059 u32 interrupts;
1060 u32 result;
1061 u32 status;
1062 u32 tmp;
1063 int ret;
1064
1065 if (!st->rst_done) {
1066 ret = regmap_read(st->regmap, ADE9000_REG_STATUS1, &result);
1067 if (ret)
1068 return ret;
1069
1070 if (result & ADE9000_ST1_RSTDONE_BIT)
1071 st->rst_done = true;
1072 else
1073 dev_err(&st->spi->dev, "Error testing reset done");
1074
1075 return IRQ_HANDLED;
1076 }
1077
1078 ret = regmap_read(st->regmap, ADE9000_REG_STATUS1, &status);
1079 if (ret)
1080 return IRQ_HANDLED;
1081
1082 ret = regmap_read(st->regmap, ADE9000_REG_MASK1, &interrupts);
1083 if (ret) {
1084 dev_err(&st->spi->dev, "IRQ1 read status fail");
1085 return IRQ_HANDLED;
1086 }
1087
1088 for_each_set_bit_from(bit, (unsigned long *)&interrupts,
1089 ADE9000_ST1_CROSSING_DEPTH){
1090 tmp = status & BIT(bit);
1091
1092 switch (tmp) {
1093 case ADE9000_ST1_ZXVA_BIT:
1094 iio_push_event(indio_dev,
1095 IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
1096 ADE9000_ST1_ZXVA_BIT,
1097 IIO_EV_TYPE_THRESH,
1098 IIO_EV_DIR_EITHER),
1099 timestamp);
1100 handled_irq |= ADE9000_ST1_ZXVA_BIT;
1101 break;
1102 case ADE9000_ST1_ZXTOVA_BIT:
1103 iio_push_event(indio_dev,
1104 IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
1105 ADE9000_ST1_ZXTOVA_BIT,
1106 IIO_EV_TYPE_THRESH,
1107 IIO_EV_DIR_EITHER),
1108 timestamp);
1109 handled_irq |= ADE9000_ST1_ZXTOVA_BIT;
1110 break;
1111 case ADE9000_ST1_ZXIA_BIT:
1112 iio_push_event(indio_dev,
1113 IIO_UNMOD_EVENT_CODE(IIO_CURRENT,
1114 ADE9000_ST1_ZXIA_BIT,
1115 IIO_EV_TYPE_THRESH,
1116 IIO_EV_DIR_EITHER),
1117 timestamp);
1118 handled_irq |= ADE9000_ST1_ZXIA_BIT;
1119 break;
1120 case ADE9000_ST1_ZXVB_BIT:
1121 iio_push_event(indio_dev,
1122 IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
1123 ADE9000_ST1_ZXVB_BIT,
1124 IIO_EV_TYPE_THRESH,
1125 IIO_EV_DIR_EITHER),
1126 timestamp);
1127 handled_irq |= ADE9000_ST1_ZXVB_BIT;
1128 break;
1129 case ADE9000_ST1_ZXTOVB_BIT:
1130 iio_push_event(indio_dev,
1131 IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
1132 ADE9000_ST1_ZXTOVB_BIT,
1133 IIO_EV_TYPE_THRESH,
1134 IIO_EV_DIR_EITHER),
1135 timestamp);
1136 handled_irq |= ADE9000_ST1_ZXTOVB_BIT;
1137 break;
1138 case ADE9000_ST1_ZXIB_BIT:
1139 iio_push_event(indio_dev,
1140 IIO_UNMOD_EVENT_CODE(IIO_CURRENT,
1141 ADE9000_ST1_ZXIB_BIT,
1142 IIO_EV_TYPE_THRESH,
1143 IIO_EV_DIR_EITHER),
1144 timestamp);
1145 handled_irq |= ADE9000_ST1_ZXIB_BIT;
1146 break;
1147 case ADE9000_ST1_ZXVC_BIT:
1148 iio_push_event(indio_dev,
1149 IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
1150 ADE9000_ST1_ZXVC_BIT,
1151 IIO_EV_TYPE_THRESH,
1152 IIO_EV_DIR_EITHER),
1153 timestamp);
1154 handled_irq |= ADE9000_ST1_ZXVC_BIT;
1155 break;
1156 case ADE9000_ST1_ZXTOVC_BIT:
1157 iio_push_event(indio_dev,
1158 IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
1159 ADE9000_ST1_ZXTOVC_BIT,
1160 IIO_EV_TYPE_THRESH,
1161 IIO_EV_DIR_EITHER),
1162 timestamp);
1163 handled_irq |= ADE9000_ST1_ZXTOVC_BIT;
1164 break;
1165 case ADE9000_ST1_ZXIC_BIT:
1166 iio_push_event(indio_dev,
1167 IIO_UNMOD_EVENT_CODE(IIO_CURRENT,
1168 ADE9000_ST1_ZXIC_BIT,
1169 IIO_EV_TYPE_THRESH,
1170 IIO_EV_DIR_EITHER),
1171 timestamp);
1172 handled_irq |= ADE9000_ST1_ZXIC_BIT;
1173 break;
1174 case ADE9000_ST1_SWELLA_BIT:
1175 iio_push_event(indio_dev,
> 1176 IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
1177 ADE9000_ST1_SWELLA_BIT,
1178 IIO_EV_TYPE_THRESH,
1179 IIO_EV_DIR_RISING),
1180 timestamp);
1181 handled_irq |= ADE9000_ST1_SWELLA_BIT;
1182 break;
1183 case ADE9000_ST1_SWELLB_BIT:
1184 iio_push_event(indio_dev,
1185 IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
1186 ADE9000_ST1_SWELLB_BIT,
1187 IIO_EV_TYPE_THRESH,
1188 IIO_EV_DIR_RISING),
1189 timestamp);
1190 handled_irq |= ADE9000_ST1_SWELLB_BIT;
1191 break;
1192 case ADE9000_ST1_SWELLC_BIT:
1193 iio_push_event(indio_dev,
1194 IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
1195 ADE9000_ST1_SWELLC_BIT,
1196 IIO_EV_TYPE_THRESH,
1197 IIO_EV_DIR_RISING),
1198 timestamp);
1199 handled_irq |= ADE9000_ST1_SWELLC_BIT;
1200 break;
1201 case ADE9000_ST1_DIPA_BIT:
1202 iio_push_event(indio_dev,
1203 IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
1204 ADE9000_ST1_DIPA_BIT,
1205 IIO_EV_TYPE_THRESH,
1206 IIO_EV_DIR_FALLING),
1207 timestamp);
1208 handled_irq |= ADE9000_ST1_DIPA_BIT;
1209 break;
1210 case ADE9000_ST1_DIPB_BIT:
1211 iio_push_event(indio_dev,
1212 IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
1213 ADE9000_ST1_DIPB_BIT,
1214 IIO_EV_TYPE_THRESH,
1215 IIO_EV_DIR_FALLING),
1216 timestamp);
1217 handled_irq |= ADE9000_ST1_DIPB_BIT;
1218 break;
1219 case ADE9000_ST1_DIPC_BIT:
1220 iio_push_event(indio_dev,
1221 IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
1222 ADE9000_ST1_DIPC_BIT,
1223 IIO_EV_TYPE_THRESH,
1224 IIO_EV_DIR_FALLING),
1225 timestamp);
1226 handled_irq |= ADE9000_ST1_DIPC_BIT;
1227 break;
1228 case ADE9000_ST1_SEQERR_BIT:
1229 iio_push_event(indio_dev,
1230 IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
1231 ADE9000_ST1_SEQERR_BIT,
1232 IIO_EV_TYPE_CHANGE,
1233 IIO_EV_DIR_NONE),
1234 timestamp);
1235 handled_irq |= ADE9000_ST1_SEQERR_BIT;
1236 break;
1237 default:
1238 return IRQ_HANDLED;
1239 }
1240 }
1241
1242 ret = regmap_write(st->regmap, ADE9000_REG_STATUS1, handled_irq);
1243 if (ret)
1244 return ret;
1245
1246 return IRQ_HANDLED;
1247 }
1248
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/3] iio: add power and energy measurement modifiers
2025-07-11 19:23 ` David Lechner
@ 2025-07-24 13:09 ` Jonathan Cameron
2025-07-24 13:58 ` David Lechner
0 siblings, 1 reply; 12+ messages in thread
From: Jonathan Cameron @ 2025-07-24 13:09 UTC (permalink / raw)
To: David Lechner
Cc: Antoniu Miclaus, robh, conor+dt, devicetree, linux-iio,
linux-kernel
On Fri, 11 Jul 2025 14:23:14 -0500
David Lechner <dlechner@baylibre.com> wrote:
> On 7/11/25 8:02 AM, Antoniu Miclaus wrote:
> > Add new IIO modifiers to support power and energy measurement devices:
> >
Sorry I'm late to the game. Busy few weeks :(
> > 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
>
> These make sense a modifiers since they are components of a single
> measured value.
Agreed. Oddly I thought we already had these. Maybe there was a proposal
at some point that never got adopted. Maybe I just had a nightmare
>
> > - IIO_MOD_FUND_REACTIVE: Reactive power at fundamental frequency
>
> This one seems like there should just be a separate channel
> with IIO_POWER + IIO_MOD_REACTIVE since it is measuring a different
> value.
Hmm. This one is new to me. A separate channels sounds fine to me
with a label to provide the info on what it is.
>
> > - IIO_MOD_FACTOR: Power factor (ratio of active to apparent power)
>
> Power factor seems like it should be a IIO_CHAN_INFO_ rather than
> IIO_MOD_. It is also unitless, so doesn't make sense to be part
> of power_raw which would imply that it shuold be converted to Watts.
Agreed.
>
> >
> > Energy modifiers:
> > - IIO_MOD_ACTIVE_ACCUM: Accumulated active energy
> > - IIO_MOD_APPARENT_ACCUM: Accumulated apparent energy
> > - IIO_MOD_REACTIVE_ACCUM: Accumulated reactive energy
>
> As below, this one seems like there should be a separate
> energy channel for accumulated energy.
What sort of energy measurement isn't accumulated? If it's
divided by time then it's power anyway.
>
> >
> > Signal quality modifiers:
> > - IIO_MOD_RMS: Root Mean Square value
>
> Suprised we don't have something like this already. altvoltageY isn't
> clear about if the value is peak-to-peak or RMS.
Hohum.. My vague recollection is peak to peak, but oops we should
have documented that better. Someone want to audit existing drivers?
>
> > - IIO_MOD_SWELL: Voltage swell detection
> > - IIO_MOD_DIP: Voltage dip (sag) detection
>
> These sound like events, not modifiers.
Agreed. Those look fun.
> > What: /sys/bus/iio/devices/iio:deviceX/in_capacitanceY_raw
> > KernelVersion: 3.2
> > Contact: linux-iio@vger.kernel.org
> > @@ -1593,6 +1603,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
>
> I think the accumulated would just be a separate channel, not a modifier.
I'm confused what energy is vs accumulated energy.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/3] iio: add power and energy measurement modifiers
2025-07-24 13:09 ` Jonathan Cameron
@ 2025-07-24 13:58 ` David Lechner
2025-07-27 13:17 ` Jonathan Cameron
0 siblings, 1 reply; 12+ messages in thread
From: David Lechner @ 2025-07-24 13:58 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Antoniu Miclaus, robh, conor+dt, devicetree, linux-iio,
linux-kernel
On 7/24/25 8:09 AM, Jonathan Cameron wrote:
> On Fri, 11 Jul 2025 14:23:14 -0500
> David Lechner <dlechner@baylibre.com> wrote:
>
>> On 7/11/25 8:02 AM, Antoniu Miclaus wrote:
>>> Add new IIO modifiers to support power and energy measurement devices:
>>>
...
>
>>
>>>
>>> Signal quality modifiers:
>>> - IIO_MOD_RMS: Root Mean Square value
>>
>> Suprised we don't have something like this already. altvoltageY isn't
>> clear about if the value is peak-to-peak or RMS.
>
> Hohum.. My vague recollection is peak to peak, but oops we should
> have documented that better. Someone want to audit existing drivers?
>
>>
I did audit them (which is what lead to the Q/I modifier cleanup
in the ABI docs). Most drivers are just using the frequency and/or
phase components. The ads1210 uses peak-to-peak (I should know, I
wrote the driver :-p). The envelope-detector driver mentions high
and low when making an altvoltage_raw measurement so that sounds
like peak-to-peak as well. There are a couple of frequency drivers
that also have altvoltage_raw, but I didn't look as closely at
them.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/3] iio: add power and energy measurement modifiers
2025-07-24 13:58 ` David Lechner
@ 2025-07-27 13:17 ` Jonathan Cameron
0 siblings, 0 replies; 12+ messages in thread
From: Jonathan Cameron @ 2025-07-27 13:17 UTC (permalink / raw)
To: David Lechner
Cc: Antoniu Miclaus, robh, conor+dt, devicetree, linux-iio,
linux-kernel
On Thu, 24 Jul 2025 08:58:48 -0500
David Lechner <dlechner@baylibre.com> wrote:
> On 7/24/25 8:09 AM, Jonathan Cameron wrote:
> > On Fri, 11 Jul 2025 14:23:14 -0500
> > David Lechner <dlechner@baylibre.com> wrote:
> >
> >> On 7/11/25 8:02 AM, Antoniu Miclaus wrote:
> >>> Add new IIO modifiers to support power and energy measurement devices:
> >>>
>
> ...
>
> >
> >>
> >>>
> >>> Signal quality modifiers:
> >>> - IIO_MOD_RMS: Root Mean Square value
> >>
> >> Suprised we don't have something like this already. altvoltageY isn't
> >> clear about if the value is peak-to-peak or RMS.
> >
> > Hohum.. My vague recollection is peak to peak, but oops we should
> > have documented that better. Someone want to audit existing drivers?
> >
> >>
>
> I did audit them (which is what lead to the Q/I modifier cleanup
> in the ABI docs). Most drivers are just using the frequency and/or
> phase components. The ads1210 uses peak-to-peak (I should know, I
> wrote the driver :-p). The envelope-detector driver mentions high
> and low when making an altvoltage_raw measurement so that sounds
> like peak-to-peak as well. There are a couple of frequency drivers
> that also have altvoltage_raw, but I didn't look as closely at
> them.
Cool. If you have time please send a docs update.
Thanks,
Jonathan
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2025-07-27 13:17 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-11 13:02 [PATCH 0/3] iio: adc: add support for ADE9000 Energy Monitoring IC Antoniu Miclaus
2025-07-11 13:02 ` [PATCH 1/3] iio: add power and energy measurement modifiers Antoniu Miclaus
2025-07-11 19:23 ` David Lechner
2025-07-24 13:09 ` Jonathan Cameron
2025-07-24 13:58 ` David Lechner
2025-07-27 13:17 ` Jonathan Cameron
2025-07-11 13:02 ` [PATCH 2/3] dt-bindings: iio: adc: add ade9000 Antoniu Miclaus
2025-07-11 17:43 ` David Lechner
2025-07-11 13:02 ` [PATCH 3/3] iio: adc: add ade9000 support Antoniu Miclaus
2025-07-12 17:56 ` kernel test robot
2025-07-12 22:16 ` kernel test robot
2025-07-11 17:09 ` [PATCH 0/3] iio: adc: add support for ADE9000 Energy Monitoring IC David Lechner
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).