lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Thu, 17 Feb 2022 15:51:40 +0200
From:   chegbeli <ciprian.hegbeli@...log.com>
To:     <jic23@...nel.org>, <robh+dt@...nel.org>,
        <linux-iio@...r.kernel.org>, <devicetree@...r.kernel.org>,
        <linux-kernel@...r.kernel.org>
CC:     chegbeli <ciprian.hegbeli@...log.com>
Subject: [PATCH 3/3] iio: meter: add ADE9078 driver

The ADE9078 is a highly accurate, fully integrated energy metering
device. It allows the monitoring of three independent phases
simultaneously, by using seven high performances ADCs and a flexible
DSP core for a fixed fundamental signal frequency of either 50Hz or
60Hz.

Datasheet:
www.analog.com/media/en/technical-documentation/data-sheets/ADE9078.pdf

Signed-off-by: chegbeli <ciprian.hegbeli@...log.com>
---
 MAINTAINERS                 |    8 +
 drivers/iio/meter/Kconfig   |   13 +
 drivers/iio/meter/Makefile  |    2 +
 drivers/iio/meter/ade9078.c | 1666 +++++++++++++++++++++++++++++++++++
 4 files changed, 1689 insertions(+)
 create mode 100644 drivers/iio/meter/ade9078.c

diff --git a/MAINTAINERS b/MAINTAINERS
index a2c8699e9e41..72fac9f4f837 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1101,6 +1101,14 @@ L:	linux-media@...r.kernel.org
 S:	Maintained
 F:	drivers/media/i2c/ad9389b*
 
+ANALOG DEVICES INC ADE9078 DRIVER
+M:	Ciprian Hegbeli <ciprian.hegbeli@...log.com>
+L:	linux-iio@...r.kernel.org
+S:	Supported
+W:	https://ez.analog.com/linux-software-drivers
+F:	Documentation/devicetree/bindings/iio/meter/adi,ade9078.yaml
+F:	drivers/iio/meter/ade9078.c
+
 ANALOG DEVICES INC ADGS1408 DRIVER
 M:	Mircea Caprioru <mircea.caprioru@...log.com>
 S:	Supported
diff --git a/drivers/iio/meter/Kconfig b/drivers/iio/meter/Kconfig
index d1c91eed5283..b46e93af1052 100644
--- a/drivers/iio/meter/Kconfig
+++ b/drivers/iio/meter/Kconfig
@@ -6,4 +6,17 @@
 
 menu "Active energy metering"
 
+config ADE9078
+	tristate "Analog Devices ADE9078 Poly Phase Multifunction Energy Metering IC Driver"
+	depends on SPI
+	select REGMAP_SPI
+	select IIO_BUFFER
+	select IIO_KFIFO_BUF
+	help
+	  Support driver for the ADE9078Poly Phase Multifunction Energy Meter,
+	  select y to build the driver.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called ade9078
+
 endmenu
diff --git a/drivers/iio/meter/Makefile b/drivers/iio/meter/Makefile
index c27cba44fc0b..866b7f7dc703 100644
--- a/drivers/iio/meter/Makefile
+++ b/drivers/iio/meter/Makefile
@@ -3,3 +3,5 @@
 # Makefile for enrgy metering drivers
 #
 # When adding new entries keep the list in alphabetical order
+
+obj-$(CONFIG_ADE9078) += ade9078.o
diff --git a/drivers/iio/meter/ade9078.c b/drivers/iio/meter/ade9078.c
new file mode 100644
index 000000000000..cfcb093e4c27
--- /dev/null
+++ b/drivers/iio/meter/ade9078.c
@@ -0,0 +1,1666 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ADE9078 driver
+ *
+ * Copyright 2021 Analog Devices Inc.
+ */
+
+#include <asm/unaligned.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.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/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+/* Address of ADE90XX registers */
+#define	ADE9078_REG_AIGAIN		0x000
+#define	ADE9078_REG_AVGAIN		0x00B
+#define	ADE9078_REG_AIRMSOS		0x00C
+#define	ADE9078_REG_AVRMSOS		0x00D
+#define	ADE9078_REG_APGAIN		0x00E
+#define	ADE9078_REG_AWATTOS		0x00F
+#define	ADE9078_REG_AVAROS		0x010
+#define	ADE9078_REG_AFVAROS		0x012
+#define	ADE9078_REG_CONFIG0		0x060
+#define	ADE9078_REG_DICOEFF		0x072
+#define	ADE9078_REG_AI_PCF		0x20A
+#define	ADE9078_REG_AV_PCF		0x20B
+#define	ADE9078_REG_AIRMS		0x20C
+#define	ADE9078_REG_AVRMS		0x20D
+#define	ADE9078_REG_AWATT		0x210
+#define	ADE9078_REG_AVAR		0x211
+#define	ADE9078_REG_AVA			0x212
+#define ADE9078_REG_AFVAR		0x214
+#define	ADE9078_REG_APF			0x216
+#define	ADE9078_REG_BI_PCF		0x22A
+#define	ADE9078_REG_BV_PCF		0x22B
+#define	ADE9078_REG_BIRMS		0x22C
+#define	ADE9078_REG_BVRMS		0x22D
+#define	ADE9078_REG_CI_PCF		0x24A
+#define	ADE9078_REG_CV_PCF		0x24B
+#define	ADE9078_REG_CIRMS		0x24C
+#define	ADE9078_REG_CVRMS		0x24D
+#define	ADE9078_REG_AWATT_ACC		0x2E5
+#define	ADE9078_REG_STATUS0		0x402
+#define	ADE9078_REG_STATUS1		0x403
+#define	ADE9078_REG_MASK0		0x405
+#define	ADE9078_REG_MASK1		0x406
+#define	ADE9078_REG_EVENT_MASK		0x407
+#define	ADE9078_REG_VLEVEL		0x40F
+#define	ADE9078_REG_RUN			0x480
+#define ADE9078_REG_CONFIG1		0x481
+#define	ADE9078_REG_ACCMODE		0x492
+#define	ADE9078_REG_CONFIG3		0x493
+#define	ADE9078_REG_ZX_LP_SEL		0x49A
+#define	ADE9078_REG_WFB_CFG		0x4A0
+#define	ADE9078_REG_WFB_PG_IRQEN	0x4A1
+#define	ADE9078_REG_WFB_TRG_CFG		0x4A2
+#define	ADE9078_REG_WFB_TRG_STAT	0x4A3
+#define	ADE9078_REG_CONFIG2		0x4AF
+#define	ADE9078_REG_EP_CFG		0x4B0
+#define	ADE9078_REG_EGY_TIME		0x4B2
+#define	ADE9078_REG_PGA_GAIN		0x4B9
+#define	ADE9078_REG_VERSION		0x4FE
+#define ADE9078_REG_WF_BUFF		0x800
+
+#define ADE9078_REG_ADDR_MASK		GENMASK(15, 4)
+#define ADE9078_REG_READ_BIT_MASK	BIT(3)
+#define ADE9078_RX_DEPTH		6
+#define ADE9078_TX_DEPTH		10
+
+#define ADE9078_WF_CAP_EN_MASK		BIT(4)
+#define ADE9078_WF_CAP_SEL_MASK		BIT(5)
+#define ADE9078_WF_MODE_MASK		GENMASK(7, 6)
+#define ADE9078_WF_SRC_MASK		GENMASK(9, 8)
+#define ADE9078_WF_IN_EN_MASK		BIT(12)
+
+/*
+ * Configuration registers
+ * PGA@...000. Gain of all channels=1
+ */
+#define ADE9078_PGA_GAIN		0x0000
+
+/* Default configuration */
+#define ADE9078_CONFIG0			0x00000000
+
+/* CF3/ZX pin outputs Zero crossing */
+#define ADE9078_CONFIG1			0x0002
+
+/* Default High pass corner frequency of 1.25Hz */
+#define ADE9078_CONFIG2			0x0A00
+
+/* Peak and overcurrent detection disabled */
+#define ADE9078_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 ADE9078_ACCMODE			0x0000
+
+/*Line period and zero crossing obtained from VA */
+#define ADE9078_ZX_LP_SEL		0x0000
+
+/* Disable all interrupts */
+#define ADE9078_MASK0			0x00000000
+
+/* Disable all interrupts */
+#define ADE9078_MASK1			0x00000000
+
+/* Events disabled */
+#define ADE9078_EVENT_MASK		0x00000000
+
+/*
+ * Assuming Vnom=1/2 of full scale.
+ * Refer to Technical reference manual for detailed calculations.
+ */
+#define ADE9078_VLEVEL			0x0022EA28
+
+/* Set DICOEFF= 0xFFFFE000 when integrator is enabled */
+#define ADE9078_DICOEFF			0x00000000
+
+/* DSP ON */
+#define ADE9078_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 ADE9078_EP_CFG			0x0011
+
+/* Accumulate 4000 samples */
+#define ADE9078_EGY_TIME		0x0FA0
+
+/*
+ * Constant Definitions
+ * ADE9000 FDSP: 8000sps, ADE9078 FDSP: 4000sps
+ */
+#define ADE9078_FDSP			4000
+#define ADE9078_WFB_CFG			0x0329
+#define ADE9078_WFB_PAGE_SIZE		128
+#define ADE9078_WFB_BYTES_IN_PAGE	4
+#define ADE9078_WFB_PAGE_ARRAY_SIZE	\
+	(ADE9078_WFB_PAGE_SIZE * ADE9078_WFB_BYTES_IN_PAGE)
+#define ADE9078_WFB_FULL_BUFF_SIZE	\
+	(ADE9078_WFB_PAGE_ARRAY_SIZE * 16)
+#define ADE9078_WFB_FULL_BUFF_NR_SAMPLES \
+	(ADE9078_WFB_PAGE_SIZE * 16)
+
+#define ADE9078_SWRST_BIT		BIT(0)
+
+/* Status and Mask register bits*/
+#define ADE9078_ST0_WFB_TRIG_BIT	BIT(16)
+#define ADE9078_ST0_PAGE_FULL_BIT	BIT(17)
+
+#define ADE9078_ST1_ZXTOVA_BIT		BIT(6)
+#define ADE9078_ST1_ZXTOVB_BIT		BIT(7)
+#define ADE9078_ST1_ZXTOVC_BIT		BIT(8)
+#define ADE9078_ST1_ZXVA_BIT		BIT(9)
+#define ADE9078_ST1_ZXVB_BIT		BIT(10)
+#define ADE9078_ST1_ZXVC_BIT		BIT(11)
+#define ADE9078_ST1_ZXIA_BIT		BIT(13)
+#define ADE9078_ST1_ZXIB_BIT		BIT(14)
+#define ADE9078_ST1_ZXIC_BIT		BIT(15)
+#define ADE9078_ST1_RSTDONE_BIT		BIT(16)
+#define ADE9078_ST1_ERROR0_BIT		BIT(28)
+#define ADE9078_ST1_ERROR1_BIT		BIT(29)
+#define ADE9078_ST1_ERROR2_BIT		BIT(30)
+#define ADE9078_ST1_ERROR3_BIT		BIT(31)
+#define ADE9078_ST_ERROR \
+	(ADE9078_ST1_ERROR0 | \
+	 ADE9078_ST1_ERROR1 | \
+	 ADE9078_ST1_ERROR2 | \
+	 ADE9078_ST1_ERROR3)
+#define ADE9078_ST1_CROSSING_FIRST	6
+#define ADE9078_ST1_CROSSING_DEPTH	16
+
+#define ADE9078_WFB_TRG_ZXIA_BIT	BIT(3)
+#define ADE9078_WFB_TRG_ZXIB_BIT	BIT(4)
+#define ADE9078_WFB_TRG_ZXIC_BIT	BIT(5)
+#define ADE9078_WFB_TRG_ZXVA_BIT	BIT(6)
+#define ADE9078_WFB_TRG_ZXVB_BIT	BIT(7)
+#define ADE9078_WFB_TRG_ZXVC_BIT	BIT(8)
+
+/* Stop when waveform buffer is full */
+#define ADE9078_WFB_FULL_MODE		0x0
+/* Continuous fill—stop only on enabled trigger events */
+#define ADE9078_WFB_EN_TRIG_MODE	0x1
+/* Continuous filling—center capture around enabled trigger events */
+#define ADE9078_WFB_C_EN_TRIG_MODE	0x2
+/* Continuous fill—save event address of enabled trigger events */
+#define ADE9078_WFB_SAVE_EN_TRIG_MODE	0x3
+
+#define ADE9078_MODE_0_1_PAGE_BIT	BIT(15)
+#define ADE9078_MODE_2_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 ADE9078_RMS_FULL_SCALE_CODES	52866837
+#define ADE9000_WATT_FULL_SCALE_CODES	20694066
+#define ADE9078_PCF_FULL_SCALE_CODES	74770000
+
+/* Phase and channel definitions */
+#define ADE9078_PHASE_A_NR		0
+#define ADE9078_PHASE_B_NR		2
+#define ADE9078_PHASE_C_NR		4
+#define ADE9078_PHASE_A_NAME		"A"
+#define ADE9078_PHASE_B_NAME		"B"
+#define ADE9078_PHASE_C_NAME		"C"
+
+#define ADE9078_SCAN_POS_IA		BIT(0)
+#define ADE9078_SCAN_POS_VA		BIT(1)
+#define ADE9078_SCAN_POS_IB		BIT(2)
+#define ADE9078_SCAN_POS_VB		BIT(3)
+#define ADE9078_SCAN_POS_IC		BIT(4)
+#define ADE9078_SCAN_POS_VC		BIT(5)
+
+#define ADE9078_PHASE_B_POS_BIT		BIT(5)
+#define ADE9078_PHASE_C_POS_BIT		BIT(6)
+
+#define ADE9078_MAX_PHASE_NR		3
+#define AD9078_CHANNELS_PER_PHASE	9
+
+#define ADE9078_ADDR_ADJUST(addr, chan)					\
+	(((chan) << 4) | (addr))
+
+#define ADE9078_CURRENT_CHANNEL(num, name) {				\
+	.type = IIO_CURRENT,						\
+	.channel = num,							\
+	.extend_name = name,						\
+	.address = ADE9078_ADDR_ADJUST(ADE9078_REG_AI_PCF, num),	\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
+			      BIT(IIO_CHAN_INFO_SCALE) |		\
+			      BIT(IIO_CHAN_INFO_HARDWAREGAIN),		\
+	.event_spec = ade9078_events,					\
+	.num_event_specs = ARRAY_SIZE(ade9078_events),			\
+	.scan_index = num,						\
+	.scan_type = {							\
+		.sign = 's',						\
+		.realbits = 32,						\
+		.storagebits = 32,					\
+		.shift = 0,						\
+		.endianness = IIO_BE,					\
+	},								\
+}
+
+#define ADE9078_VOLTAGE_CHANNEL(num, name) {				\
+	.type = IIO_VOLTAGE,						\
+	.channel = num,							\
+	.extend_name = name,						\
+	.address = ADE9078_ADDR_ADJUST(ADE9078_REG_AV_PCF, num),	\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
+			      BIT(IIO_CHAN_INFO_SCALE) |		\
+			      BIT(IIO_CHAN_INFO_HARDWAREGAIN),		\
+	.event_spec = ade9078_events,					\
+	.num_event_specs = ARRAY_SIZE(ade9078_events),			\
+	.scan_index = num + 1,						\
+	.scan_type = {							\
+		.sign = 's',						\
+		.realbits = 32,						\
+		.storagebits = 32,					\
+		.shift = 0,						\
+		.endianness = IIO_BE,					\
+	},								\
+}
+
+#define ADE9078_CURRENT_RMS_CHANNEL(num, name) {			\
+	.type = IIO_CURRENT,						\
+	.channel = num,							\
+	.address = ADE9078_ADDR_ADJUST(ADE9078_REG_AIRMS, num),		\
+	.extend_name = name "_rms",					\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
+			      BIT(IIO_CHAN_INFO_SCALE) |		\
+			      BIT(IIO_CHAN_INFO_OFFSET),		\
+	.scan_index = -1						\
+}
+
+#define ADE9078_VOLTAGE_RMS_CHANNEL(num, name) {			\
+	.type = IIO_VOLTAGE,						\
+	.channel = num,							\
+	.address = ADE9078_ADDR_ADJUST(ADE9078_REG_AVRMS, num),		\
+	.extend_name = name "_rms",					\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
+			      BIT(IIO_CHAN_INFO_SCALE) |		\
+			      BIT(IIO_CHAN_INFO_OFFSET),		\
+	.scan_index = -1						\
+}
+
+#define ADE9078_POWER_ACTIV_CHANNEL(num, name) {			\
+	.type = IIO_POWER,						\
+	.channel = num,							\
+	.address = ADE9078_ADDR_ADJUST(ADE9078_REG_AWATT, num),		\
+	.extend_name = name "_activ",					\
+	.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 ADE9078_POWER_REACTIV_CHANNEL(num, name) {			\
+	.type = IIO_POWER,						\
+	.channel = num,							\
+	.address = ADE9078_ADDR_ADJUST(ADE9078_REG_AVAR, num),		\
+	.extend_name = name "_reactiv",					\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
+			      BIT(IIO_CHAN_INFO_SCALE) |		\
+			      BIT(IIO_CHAN_INFO_OFFSET),		\
+	.scan_index = -1						\
+}
+
+#define ADE9078_POWER_APPARENT_CHANNEL(num, name) {			\
+	.type = IIO_POWER,						\
+	.channel = num,							\
+	.address = ADE9078_ADDR_ADJUST(ADE9078_REG_AVA, num),		\
+	.extend_name = name "_apparent",				\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
+			      BIT(IIO_CHAN_INFO_SCALE),			\
+	.scan_index = -1						\
+}
+
+#define ADE9078_POWER_FUND_REACTIV_CHANNEL(num, name) {			\
+	.type = IIO_POWER,						\
+	.channel = num,							\
+	.address = ADE9078_ADDR_ADJUST(ADE9078_REG_AFVAR, num),		\
+	.extend_name = name "_fund_reactiv",				\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
+			      BIT(IIO_CHAN_INFO_SCALE) |		\
+			      BIT(IIO_CHAN_INFO_OFFSET),		\
+	.scan_index = -1						\
+}
+
+#define ADE9078_POWER_FACTOR_CHANNEL(num, name) {			\
+	.type = IIO_POWER,						\
+	.channel = num,							\
+	.address = ADE9078_ADDR_ADJUST(ADE9078_REG_APF, num),		\
+	.extend_name = name "_factor",					\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
+			      BIT(IIO_CHAN_INFO_SCALE),			\
+	.scan_index = -1						\
+}
+
+/*
+ * struct ade9078_state - ade9078 specific data
+ * @rst_done	flag for when reset sequence irq has been received
+ * @wf_mode	wave form buffer mode, read datasheet for more details,
+ *		retrieved from DT
+ * @wfb_trg	wave form buffer triger configuration, read datasheet for more
+ *		details, retrieved from DT
+ * @spi		spi device associated to the ade9078
+ * @tx		transmit buffer for the spi
+ * @rx		receive buffer for the spi
+ * @xfer	transfer setup used in iio buffer configuration
+ * @spi_msg	message transfer trough spi, used in iio buffer
+ *		configuration
+ * @regmap	register map pointer
+ * @indio_dev:	the IIO device
+ * @trig	iio trigger pointer, is connected to IRQ0 and IRQ1
+ * @rx_buff	receive buffer for the iio buffer trough spi, will
+ *		contain the samples from the IC wave form buffer
+ * @tx_buff	transmit buffer for the iio buffer trough spi, used
+ *		in iio	buffer configuration
+ */
+
+struct ade9078_state {
+	bool rst_done;
+	u8 wf_mode;
+	u32 wfb_trg;
+	struct spi_device *spi;
+	u8 *tx;
+	u8 *rx;
+	struct spi_transfer xfer[2];
+	struct spi_message spi_msg;
+	struct regmap *regmap;
+	struct iio_dev *indio_dev;
+	union{
+		u8 byte[ADE9078_WFB_FULL_BUFF_SIZE];
+		__be32 word[ADE9078_WFB_FULL_BUFF_NR_SAMPLES];
+	} rx_buff ____cacheline_aligned;
+	u8 tx_buff[2];
+};
+
+static const struct iio_event_spec ade9078_events[] = {
+	{
+		.type = IIO_EV_TYPE_THRESH,
+		.dir = IIO_EV_DIR_EITHER,
+		.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+				 BIT(IIO_EV_INFO_VALUE),
+	},
+};
+
+/* IIO channels of the ade9078 for each phase individually */
+static const struct iio_chan_spec ade9078_a_channels[] = {
+	ADE9078_CURRENT_CHANNEL(ADE9078_PHASE_A_NR, ADE9078_PHASE_A_NAME),
+	ADE9078_VOLTAGE_CHANNEL(ADE9078_PHASE_A_NR, ADE9078_PHASE_A_NAME),
+	ADE9078_CURRENT_RMS_CHANNEL(ADE9078_PHASE_A_NR, ADE9078_PHASE_A_NAME),
+	ADE9078_VOLTAGE_RMS_CHANNEL(ADE9078_PHASE_A_NR, ADE9078_PHASE_A_NAME),
+	ADE9078_POWER_ACTIV_CHANNEL(ADE9078_PHASE_A_NR, ADE9078_PHASE_A_NAME),
+	ADE9078_POWER_REACTIV_CHANNEL(ADE9078_PHASE_A_NR, ADE9078_PHASE_A_NAME),
+	ADE9078_POWER_APPARENT_CHANNEL(ADE9078_PHASE_A_NR,
+				       ADE9078_PHASE_A_NAME),
+	ADE9078_POWER_FUND_REACTIV_CHANNEL(ADE9078_PHASE_A_NR,
+					   ADE9078_PHASE_A_NAME),
+	ADE9078_POWER_FACTOR_CHANNEL(ADE9078_PHASE_A_NR, ADE9078_PHASE_A_NAME),
+};
+
+static const struct iio_chan_spec ade9078_b_channels[] = {
+	ADE9078_CURRENT_CHANNEL(ADE9078_PHASE_B_NR, ADE9078_PHASE_B_NAME),
+	ADE9078_VOLTAGE_CHANNEL(ADE9078_PHASE_B_NR, ADE9078_PHASE_B_NAME),
+	ADE9078_CURRENT_RMS_CHANNEL(ADE9078_PHASE_B_NR, ADE9078_PHASE_B_NAME),
+	ADE9078_VOLTAGE_RMS_CHANNEL(ADE9078_PHASE_B_NR, ADE9078_PHASE_B_NAME),
+	ADE9078_POWER_ACTIV_CHANNEL(ADE9078_PHASE_B_NR, ADE9078_PHASE_B_NAME),
+	ADE9078_POWER_REACTIV_CHANNEL(ADE9078_PHASE_B_NR, ADE9078_PHASE_B_NAME),
+	ADE9078_POWER_APPARENT_CHANNEL(ADE9078_PHASE_B_NR,
+				       ADE9078_PHASE_B_NAME),
+	ADE9078_POWER_FUND_REACTIV_CHANNEL(ADE9078_PHASE_B_NR,
+					   ADE9078_PHASE_B_NAME),
+	ADE9078_POWER_FACTOR_CHANNEL(ADE9078_PHASE_B_NR, ADE9078_PHASE_B_NAME),
+};
+
+static const struct iio_chan_spec ade9078_c_channels[] = {
+	ADE9078_CURRENT_CHANNEL(ADE9078_PHASE_C_NR, ADE9078_PHASE_C_NAME),
+	ADE9078_VOLTAGE_CHANNEL(ADE9078_PHASE_C_NR, ADE9078_PHASE_C_NAME),
+	ADE9078_CURRENT_RMS_CHANNEL(ADE9078_PHASE_C_NR, ADE9078_PHASE_C_NAME),
+	ADE9078_VOLTAGE_RMS_CHANNEL(ADE9078_PHASE_C_NR, ADE9078_PHASE_C_NAME),
+	ADE9078_POWER_ACTIV_CHANNEL(ADE9078_PHASE_C_NR, ADE9078_PHASE_C_NAME),
+	ADE9078_POWER_REACTIV_CHANNEL(ADE9078_PHASE_C_NR, ADE9078_PHASE_C_NAME),
+	ADE9078_POWER_APPARENT_CHANNEL(ADE9078_PHASE_C_NR,
+				       ADE9078_PHASE_C_NAME),
+	ADE9078_POWER_FUND_REACTIV_CHANNEL(ADE9078_PHASE_C_NR,
+					   ADE9078_PHASE_C_NAME),
+	ADE9078_POWER_FACTOR_CHANNEL(ADE9078_PHASE_C_NR, ADE9078_PHASE_C_NAME),
+};
+
+static const struct reg_sequence ade9078_reg_sequence[] = {
+	{ ADE9078_REG_PGA_GAIN, ADE9078_PGA_GAIN },
+	{ ADE9078_REG_CONFIG0, ADE9078_CONFIG0 },
+	{ ADE9078_REG_CONFIG1, ADE9078_CONFIG1 },
+	{ ADE9078_REG_CONFIG2, ADE9078_CONFIG2 },
+	{ ADE9078_REG_CONFIG3, ADE9078_CONFIG3 },
+	{ ADE9078_REG_ACCMODE, ADE9078_ACCMODE },
+	{ ADE9078_REG_ZX_LP_SEL, ADE9078_ZX_LP_SEL },
+	{ ADE9078_REG_MASK0, ADE9078_MASK0 },
+	{ ADE9078_REG_MASK1, ADE9078_MASK1 },
+	{ ADE9078_REG_EVENT_MASK, ADE9078_EVENT_MASK },
+	{ ADE9078_REG_WFB_CFG, ADE9078_WFB_CFG },
+	{ ADE9078_REG_VLEVEL, ADE9078_VLEVEL },
+	{ ADE9078_REG_DICOEFF, ADE9078_DICOEFF },
+	{ ADE9078_REG_EGY_TIME, ADE9078_EGY_TIME },
+	{ ADE9078_REG_EP_CFG, ADE9078_EP_CFG },
+	{ ADE9078_REG_RUN, ADE9078_RUN_ON }
+};
+
+/*
+ * ade9078_spi_write_reg() - ade9078 write register over SPI
+ * the data format for communicating with the ade9078 over SPI
+ * is very specific and can access both 32bit and 16bit registers
+ * @context:	void pointer to the SPI device
+ * @reg:	address of the of desired register
+ * @val:	value to be written to the ade9078
+ */
+static int ade9078_spi_write_reg(void *context,
+				 unsigned int reg,
+				 unsigned int val)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+	struct ade9078_state *st = spi_get_drvdata(spi);
+
+	u16 addr;
+	int ret = 0;
+	struct spi_transfer xfer[] = {
+		{
+			.tx_buf = st->tx,
+		},
+	};
+
+	addr = FIELD_PREP(ADE9078_REG_ADDR_MASK, reg);
+
+	put_unaligned_be16(addr, st->tx);
+	put_unaligned_be32(val, &st->tx[2]);
+
+	if (reg > ADE9078_REG_RUN && reg < ADE9078_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;
+}
+
+/*
+ * ade9078_spi_write_reg() - ade9078 read register over SPI
+ * the data format for communicating with the ade9078 over SPI
+ * is very specific and can access both 32bit and 16bit registers
+ * @context:	void pointer to the SPI device
+ * @reg:	address of the of desired register
+ * @val:	value to be read to the ade9078
+ */
+static int ade9078_spi_read_reg(void *context,
+				unsigned int reg,
+				unsigned int *val)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+	struct ade9078_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(ADE9078_REG_ADDR_MASK, reg) |
+	       ADE9078_REG_READ_BIT_MASK;
+
+	put_unaligned_be16(addr, st->tx);
+
+	if (reg > ADE9078_REG_RUN && reg < ADE9078_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, "problem when reading register 0x%x",
+			reg);
+		goto err_ret;
+	}
+
+	//registers which are 16 bits
+	if (reg > 0x480 && reg < 0x4FE)
+		*val = get_unaligned_be16(st->rx);
+	else
+		*val = get_unaligned_be32(st->rx);
+
+err_ret:
+	return ret;
+}
+
+/*
+ * ade9078_is_volatile_reg() - list of ade9078 registers which should use
+ * caching
+ * @dev:	device data
+ * @reg:	address of the of desired register
+ */
+static bool ade9078_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case ADE9078_REG_STATUS0:
+	case ADE9078_REG_STATUS1:
+	case ADE9078_REG_MASK0:
+	case ADE9078_REG_MASK1:
+		return true;
+	default:
+		return false;
+	}
+}
+
+/*
+ * ade9078_en_wfb() - enables or disables the WFBuffer in the ADE9078
+ * @st:		ade9078 device data
+ * @state:	true for enabled; false for disabled
+ */
+static int ade9078_en_wfb(struct ade9078_state *st, bool state)
+{
+	return regmap_update_bits(st->regmap, ADE9078_REG_WFB_CFG, BIT(4),
+				  state ? BIT(4) : 0);
+}
+
+/*
+ * ade9078_iio_push_buffer() - reads out the content of the waveform buffer and
+ * pushes it to the IIO buffer.
+ * @st:		ade9078 device data
+ */
+static int ade9078_iio_push_buffer(struct ade9078_state *st)
+{
+	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 < ADE9078_WFB_FULL_BUFF_NR_SAMPLES; i++)
+		iio_push_to_buffers(st->indio_dev, &st->rx_buff.word[i]);
+
+	return 0;
+}
+
+/*
+ * ade9078_irq0_thread() - Thread for IRQ0. It reads Status register 0 and
+ * checks for the IRQ activation. This is configured to acquire samples in to
+ * the IC buffer and dump it in to the iio_buffer according to Stop When Buffer
+ * Is Full Mode, Stop Filling on Trigger and Capture Around Trigger from the
+ * ADE9078 Datasheet
+ */
+static irqreturn_t ade9078_irq0_thread(int irq, void *data)
+{
+	struct ade9078_state *st = data;
+	u32 handled_irq = 0;
+	u32 interrupts;
+	u32 status;
+	int ret;
+
+	ret = regmap_read(st->regmap, ADE9078_REG_STATUS0, &status);
+	if (ret) {
+		dev_err(&st->spi->dev, "IRQ0 read status fail");
+		goto irq0_done;
+	}
+
+	ret = regmap_read(st->regmap, ADE9078_REG_MASK0, &interrupts);
+	if (ret) {
+		dev_err(&st->spi->dev, "IRQ0 read status fail");
+		goto irq0_done;
+	}
+
+	if ((status & ADE9078_ST0_PAGE_FULL_BIT) &&
+	    (interrupts & ADE9078_ST0_PAGE_FULL_BIT)) {
+		//Stop Filling on Trigger and Center Capture Around Trigger
+		if (st->wf_mode) {
+			ret = regmap_write(st->regmap, ADE9078_REG_WFB_TRG_CFG,
+					   st->wfb_trg);
+			if (ret) {
+				dev_err(&st->spi->dev, "IRQ0 WFB write fail");
+				goto irq0_done;
+			}
+
+			interrupts |= ADE9078_ST0_WFB_TRIG_BIT;
+
+		} else {
+			//Stop When Buffer Is Full Mode
+			ret = ade9078_en_wfb(st, false);
+			if (ret) {
+				dev_err(&st->spi->dev, "IRQ0 WFB stop fail");
+				goto irq0_done;
+			}
+			ret = ade9078_iio_push_buffer(st);
+			if (ret) {
+				dev_err(&st->spi->dev, "IRQ0 IIO push fail");
+				goto irq0_done;
+			}
+		}
+
+		//disable Page full interrupt
+		interrupts &= ~ADE9078_ST0_PAGE_FULL_BIT;
+
+		ret = regmap_write(st->regmap, ADE9078_REG_MASK0, interrupts);
+		if (ret) {
+			dev_err(&st->spi->dev, "IRQ0 MAKS0 write fail");
+			goto irq0_done;
+		}
+
+		handled_irq |= ADE9078_ST0_PAGE_FULL_BIT;
+	}
+
+	if ((status & ADE9078_ST0_WFB_TRIG_BIT) &&
+	    (interrupts & ADE9078_ST0_WFB_TRIG_BIT)) {
+		//Stop Filling on Trigger and Center Capture Around Trigger
+		ret = ade9078_en_wfb(st, false);
+		if (ret) {
+			dev_err(&st->spi->dev, "IRQ0 WFB fail");
+			goto irq0_done;
+		}
+
+		ret = ade9078_iio_push_buffer(st);
+		if (ret) {
+			dev_err(&st->spi->dev, "IRQ0 IIO push fail @ WFB TRIG");
+			goto irq0_done;
+		}
+
+		handled_irq |= ADE9078_ST0_WFB_TRIG_BIT;
+	}
+
+	ret = regmap_write(st->regmap, ADE9078_REG_STATUS0, handled_irq);
+	if (ret)
+		dev_err(&st->spi->dev, "IRQ0 write status fail");
+
+irq0_done:
+	return IRQ_HANDLED;
+}
+
+/*
+ * ade9078_irq1_thread() - Thread for IRQ1. It reads Status register 1 and
+ * checks for the IRQ activation. This thread handles the reset condition and
+ * the zero-crossing conditions for all 3 phases on Voltage and Current
+ */
+static irqreturn_t ade9078_irq1_thread(int irq, void *data)
+{
+	struct ade9078_state *st = data;
+	struct iio_dev *indio_dev = st->indio_dev;
+	struct iio_chan_spec const *chan = indio_dev->channels;
+	unsigned int bit = ADE9078_ST1_CROSSING_FIRST;
+	s64 timestamp = iio_get_time_ns(indio_dev);
+	u32 interrupts;
+	u32 result;
+	u32 status;
+	u32 tmp;
+	int ret;
+
+	//reset
+	if (!st->rst_done) {
+		ret = regmap_read(st->regmap, ADE9078_REG_STATUS1, &result);
+		if (ret)
+			return ret;
+
+		if (result & ADE9078_ST1_RSTDONE_BIT)
+			st->rst_done = true;
+		else
+			dev_err(&st->spi->dev, "Error testing reset done");
+
+		goto irq1_done;
+	}
+
+	ret = regmap_read(st->regmap, ADE9078_REG_STATUS1, &status);
+	if (ret)
+		goto irq1_done;
+
+	ret = regmap_read(st->regmap, ADE9078_REG_MASK1, &interrupts);
+	if (ret) {
+		dev_err(&st->spi->dev, "IRQ1 read status fail");
+		goto irq1_done;
+	}
+
+	//crossings
+	for_each_set_bit_from(bit, (unsigned long *)&interrupts,
+			      ADE9078_ST1_CROSSING_DEPTH) {
+		tmp = status & BIT(bit);
+
+		switch (tmp) {
+		case ADE9078_ST1_ZXVA_BIT:
+		case ADE9078_ST1_ZXTOVA_BIT:
+		case ADE9078_ST1_ZXIA_BIT:
+			iio_push_event(indio_dev,
+				       IIO_UNMOD_EVENT_CODE(chan->type,
+							    ADE9078_PHASE_A_NR,
+							    IIO_EV_TYPE_THRESH,
+							    IIO_EV_DIR_EITHER),
+				       timestamp);
+			break;
+		case ADE9078_ST1_ZXVB_BIT:
+		case ADE9078_ST1_ZXTOVB_BIT:
+		case ADE9078_ST1_ZXIB_BIT:
+			iio_push_event(indio_dev,
+				       IIO_UNMOD_EVENT_CODE(chan->type,
+							    ADE9078_PHASE_B_NR,
+							    IIO_EV_TYPE_THRESH,
+							    IIO_EV_DIR_EITHER),
+				       timestamp);
+			break;
+		case ADE9078_ST1_ZXVC_BIT:
+		case ADE9078_ST1_ZXTOVC_BIT:
+		case ADE9078_ST1_ZXIC_BIT:
+			iio_push_event(indio_dev,
+				       IIO_UNMOD_EVENT_CODE(chan->type,
+							    ADE9078_PHASE_C_NR,
+							    IIO_EV_TYPE_THRESH,
+							    IIO_EV_DIR_EITHER),
+				       timestamp);
+			break;
+		default:
+			goto irq1_done;
+		}
+	}
+
+irq1_done:
+	return IRQ_HANDLED;
+}
+
+/*
+ * ade9078_configure_scan() - sets up the transfer parameters
+ * as well as the tx and rx buffers
+ * @indio_dev:	the IIO device
+ */
+static int ade9078_configure_scan(struct iio_dev *indio_dev)
+{
+	struct ade9078_state *st = iio_priv(indio_dev);
+	u16 addr;
+
+	addr = FIELD_PREP(ADE9078_REG_ADDR_MASK, ADE9078_REG_WF_BUFF) |
+	       ADE9078_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];
+	st->xfer[1].len = ADE9078_WFB_FULL_BUFF_SIZE;
+
+	spi_message_init_with_transfers(&st->spi_msg, st->xfer, 2);
+	return 0;
+}
+
+/*
+ * ade9078_read_raw() - IIO read function
+ * @indio_dev:	the IIO device
+ * @chan:	channel specs of the ade9078
+ * @val:	first half of the read value
+ * @val2:	second half of the read value
+ * @mask:	info mask of the channel
+ */
+static int ade9078_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int *val,
+			    int *val2,
+			    long mask)
+{
+	struct ade9078_state *st = iio_priv(indio_dev);
+	int measured;
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = iio_device_claim_direct_mode(indio_dev);
+		if (ret)
+			return ret;
+
+		ret = regmap_read(st->regmap, chan->address, &measured);
+
+		iio_device_release_direct_mode(indio_dev);
+		*val = measured;
+
+		return ret ?: IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SCALE:
+		if (chan->type == IIO_CURRENT || chan->type == IIO_VOLTAGE) {
+			switch (chan->address) {
+			case ADE9078_REG_AI_PCF:
+			case ADE9078_REG_AV_PCF:
+			case ADE9078_REG_BI_PCF:
+			case ADE9078_REG_BV_PCF:
+			case ADE9078_REG_CI_PCF:
+			case ADE9078_REG_CV_PCF:
+				*val = 1;
+				*val2 = ADE9078_PCF_FULL_SCALE_CODES;
+				return IIO_VAL_FRACTIONAL;
+			case ADE9078_REG_AIRMS:
+			case ADE9078_REG_AVRMS:
+			case ADE9078_REG_BIRMS:
+			case ADE9078_REG_BVRMS:
+			case ADE9078_REG_CIRMS:
+			case ADE9078_REG_CVRMS:
+				*val = 1;
+				*val2 = ADE9078_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;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * ade9078_write_raw() - IIO write function
+ * @indio_dev:	the IIO device
+ * @chan:	channel specs of the ade9078
+ * @val:	first half of the read value
+ * @val2:	second half of the read value
+ * @mask:	info mask of the channel
+ */
+static int ade9078_write_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     int val,
+			     int val2,
+			     long mask)
+{
+	struct ade9078_state *st = iio_priv(indio_dev);
+	u32 addr = 0xFFFFF;
+	u32 tmp;
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_OFFSET:
+		switch (chan->type) {
+		case IIO_CURRENT:
+			addr = ADE9078_ADDR_ADJUST(ADE9078_REG_AIRMSOS,
+						   chan->channel);
+			break;
+		case IIO_VOLTAGE:
+			addr = ADE9078_ADDR_ADJUST(ADE9078_REG_AVRMSOS,
+						   chan->channel);
+			break;
+		case IIO_POWER:
+			tmp = chan->address;
+			tmp &= ~ADE9078_PHASE_B_POS_BIT;
+			tmp &= ~ADE9078_PHASE_C_POS_BIT;
+
+			switch (tmp) {
+			case ADE9078_REG_AWATTOS:
+				addr = ADE9078_ADDR_ADJUST(ADE9078_REG_AWATTOS,
+							   chan->channel);
+				break;
+			case ADE9078_REG_AVAR:
+				addr = ADE9078_ADDR_ADJUST(ADE9078_REG_AVAROS,
+							   chan->channel);
+				break;
+			case ADE9078_REG_AFVAR:
+				addr = ADE9078_ADDR_ADJUST(ADE9078_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 = ADE9078_ADDR_ADJUST(ADE9078_REG_AIGAIN,
+						   chan->channel);
+			break;
+		case IIO_VOLTAGE:
+			addr = ADE9078_ADDR_ADJUST(ADE9078_REG_AVGAIN,
+						   chan->channel);
+			break;
+		case IIO_POWER:
+			addr = ADE9078_ADDR_ADJUST(ADE9078_REG_APGAIN,
+						   chan->channel);
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = regmap_write(st->regmap, addr, val);
+	return ret;
+}
+
+/*
+ * ade9078_reg_access() - IIO debug register access
+ * @indio_dev:	the IIO device
+ * @reg:	register to be accessed
+ * @tx_val:	value to be transmitted
+ * @rx_val:	value to be received
+ */
+static int ade9078_reg_access(struct iio_dev *indio_dev,
+			      unsigned int reg,
+			      unsigned int tx_val,
+			      unsigned int *rx_val)
+{
+	struct ade9078_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);
+}
+
+/*
+ * ade9078_write_event_config() - IIO event configure to enable zero-crossing
+ * and zero-crossing timeout on voltage and current for each phases. These
+ * events will also influence the trigger conditions for the buffer capture.
+ */
+static int ade9078_write_event_config(struct iio_dev *indio_dev,
+				      const struct iio_chan_spec *chan,
+				      enum iio_event_type type,
+				      enum iio_event_direction dir,
+				      int state)
+{
+	struct ade9078_state *st = iio_priv(indio_dev);
+	u32 interrupts;
+	u32 number;
+	int ret;
+
+	number = chan->channel;
+
+	switch (number) {
+	case ADE9078_PHASE_A_NR:
+		if (chan->type == IIO_VOLTAGE) {
+			if (state) {
+				interrupts |= ADE9078_ST1_ZXVA_BIT;
+				interrupts |= ADE9078_ST1_ZXTOVA_BIT;
+				st->wfb_trg |= ADE9078_WFB_TRG_ZXVA_BIT;
+			} else {
+				interrupts &= ~ADE9078_ST1_ZXVA_BIT;
+				interrupts &= ~ADE9078_ST1_ZXTOVA_BIT;
+				st->wfb_trg &= ~ADE9078_WFB_TRG_ZXVA_BIT;
+			}
+		} else if (chan->type == IIO_CURRENT) {
+			if (state) {
+				interrupts |= ADE9078_ST1_ZXIA_BIT;
+				st->wfb_trg |= ADE9078_WFB_TRG_ZXIA_BIT;
+			} else {
+				interrupts &= ~ADE9078_ST1_ZXIA_BIT;
+				st->wfb_trg &= ~ADE9078_WFB_TRG_ZXIA_BIT;
+			}
+		}
+		break;
+	case ADE9078_PHASE_B_NR:
+		if (chan->type == IIO_VOLTAGE) {
+			if (state) {
+				interrupts |= ADE9078_ST1_ZXVB_BIT;
+				interrupts |= ADE9078_ST1_ZXTOVB_BIT;
+				st->wfb_trg |= ADE9078_WFB_TRG_ZXVB_BIT;
+			} else {
+				interrupts &= ~ADE9078_ST1_ZXVB_BIT;
+				interrupts &= ~ADE9078_ST1_ZXTOVB_BIT;
+				st->wfb_trg &= ~ADE9078_WFB_TRG_ZXVB_BIT;
+			}
+		} else if (chan->type == IIO_CURRENT) {
+			if (state) {
+				interrupts |= ADE9078_ST1_ZXIB_BIT;
+				st->wfb_trg |= ADE9078_WFB_TRG_ZXIB_BIT;
+			} else {
+				interrupts &= ~ADE9078_ST1_ZXIB_BIT;
+				st->wfb_trg &= ~ADE9078_WFB_TRG_ZXIB_BIT;
+			}
+		}
+		break;
+	case ADE9078_PHASE_C_NR:
+		if (chan->type == IIO_VOLTAGE) {
+			if (state) {
+				interrupts |= ADE9078_ST1_ZXVC_BIT;
+				interrupts |= ADE9078_ST1_ZXTOVC_BIT;
+				st->wfb_trg |= ADE9078_WFB_TRG_ZXVC_BIT;
+			} else {
+				interrupts &= ~ADE9078_ST1_ZXVC_BIT;
+				interrupts &= ~ADE9078_ST1_ZXTOVC_BIT;
+				st->wfb_trg &= ~ADE9078_WFB_TRG_ZXVC_BIT;
+			}
+		} else if (chan->type == IIO_CURRENT) {
+			if (state) {
+				interrupts |= ADE9078_ST1_ZXIC_BIT;
+				st->wfb_trg |= ADE9078_WFB_TRG_ZXIC_BIT;
+			} else {
+				interrupts &= ~ADE9078_ST1_ZXIC_BIT;
+				st->wfb_trg &= ~ADE9078_WFB_TRG_ZXIC_BIT;
+			}
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = regmap_write(st->regmap, ADE9078_REG_STATUS1, GENMASK(31, 0));
+	if (ret)
+		return ret;
+
+	return regmap_update_bits(st->regmap, ADE9078_REG_MASK1, interrupts,
+				  interrupts);
+}
+
+/*
+ * ade9078_read_event_vlaue() - Outputs the result of the zero-crossing for
+ * voltage and current for each phase.
+ * Result:
+ * 0 - if crossing event not set
+ * 1 - if crossing event occurred
+ * -1 - if crossing timeout (only for Voltages)
+ */
+static int ade9078_read_event_vlaue(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 ade9078_state *st = iio_priv(indio_dev);
+	u32 handeled_irq = 0;
+	u32 interrupts;
+	u32 number;
+	u32 status;
+	int ret;
+
+	ret = regmap_read(st->regmap, ADE9078_REG_STATUS1, &status);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(st->regmap, ADE9078_REG_MASK1, &interrupts);
+	if (ret)
+		return ret;
+
+	*val = 0;
+	number = chan->channel;
+	switch (number) {
+	case ADE9078_PHASE_A_NR:
+		if (chan->type == IIO_VOLTAGE) {
+			if (status & ADE9078_ST1_ZXVA_BIT) {
+				*val = 1;
+				handeled_irq |= ADE9078_ST1_ZXVA_BIT;
+			} else if (status & ADE9078_ST1_ZXTOVA_BIT) {
+				*val = -1;
+				handeled_irq |= ADE9078_ST1_ZXTOVA_BIT;
+			}
+			if (!(interrupts & ADE9078_ST1_ZXTOVA_BIT))
+				*val = 0;
+		} else if (chan->type == IIO_CURRENT) {
+			if (status & ADE9078_ST1_ZXIA_BIT) {
+				*val = 1;
+				handeled_irq |= ADE9078_ST1_ZXIA_BIT;
+			}
+		}
+		break;
+	case ADE9078_PHASE_B_NR:
+		if (chan->type == IIO_VOLTAGE) {
+			if (status & ADE9078_ST1_ZXVB_BIT) {
+				*val = 1;
+				handeled_irq |= ADE9078_ST1_ZXVB_BIT;
+			} else if (status & ADE9078_ST1_ZXTOVB_BIT) {
+				*val = -1;
+				handeled_irq |= ADE9078_ST1_ZXTOVB_BIT;
+			}
+			if (!(interrupts & ADE9078_ST1_ZXTOVB_BIT))
+				*val = 0;
+		} else if (chan->type == IIO_CURRENT) {
+			if (status & ADE9078_ST1_ZXIB_BIT) {
+				*val = 1;
+				handeled_irq |= ADE9078_ST1_ZXIB_BIT;
+			}
+		}
+		break;
+	case ADE9078_PHASE_C_NR:
+		if (chan->type == IIO_VOLTAGE) {
+			if (status & ADE9078_ST1_ZXVC_BIT) {
+				*val = 1;
+				handeled_irq |= ADE9078_ST1_ZXVC_BIT;
+			} else if (status & ADE9078_ST1_ZXTOVC_BIT) {
+				*val = -1;
+				handeled_irq |= ADE9078_ST1_ZXTOVC_BIT;
+			}
+			if (!(interrupts & ADE9078_ST1_ZXTOVC_BIT))
+				*val = 0;
+		} else if (chan->type == IIO_CURRENT) {
+			if (status & ADE9078_ST1_ZXIC_BIT) {
+				*val = 1;
+				handeled_irq |= ADE9078_ST1_ZXIC_BIT;
+			}
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = regmap_write(st->regmap, ADE9078_REG_STATUS1, handeled_irq);
+	if (ret)
+		return ret;
+
+	return IIO_VAL_INT;
+}
+
+/*
+ * ade9078_config_wfb() - reads the ade9078 node and configures the wave form
+ * buffer based on the options set. Additionally is reads the active scan mask
+ * in order to set the input data of the buffer. There are only a few available
+ * input configurations permitted by the IC, any unpermitted configuration will
+ * result in all channels being active.
+ * @indio_dev:	the IIO device
+ */
+static int ade9078_config_wfb(struct iio_dev *indio_dev)
+{
+	struct ade9078_state *st = iio_priv(indio_dev);
+	u32 wfg_cfg_val = 0;
+	u32 tmp;
+	int ret;
+
+	bitmap_to_arr32(&wfg_cfg_val, indio_dev->active_scan_mask,
+			indio_dev->masklength);
+
+	switch (wfg_cfg_val) {
+	case ADE9078_SCAN_POS_IA | ADE9078_SCAN_POS_VA:
+		wfg_cfg_val = 0x1;
+		break;
+	case ADE9078_SCAN_POS_IB | ADE9078_SCAN_POS_VB:
+		wfg_cfg_val = 0x2;
+		break;
+	case ADE9078_SCAN_POS_IC | ADE9078_SCAN_POS_VC:
+		wfg_cfg_val = 0x3;
+		break;
+	case ADE9078_SCAN_POS_IA:
+		wfg_cfg_val = 0x8;
+		break;
+	case ADE9078_SCAN_POS_VA:
+		wfg_cfg_val = 0x9;
+		break;
+	case ADE9078_SCAN_POS_IB:
+		wfg_cfg_val = 0xA;
+		break;
+	case ADE9078_SCAN_POS_VB:
+		wfg_cfg_val = 0xB;
+		break;
+	case ADE9078_SCAN_POS_IC:
+		wfg_cfg_val = 0xC;
+		break;
+	case ADE9078_SCAN_POS_VC:
+		wfg_cfg_val = 0xD;
+		break;
+	default:
+		wfg_cfg_val = 0x0;
+		break;
+	}
+
+	ret = of_property_read_u32((&st->spi->dev)->of_node, "adi,wf-cap-sel",
+				   &tmp);
+	if (ret) {
+		dev_err(&st->spi->dev, "Failed to get wf-cap-sel: %d\n", ret);
+		return ret;
+	}
+	wfg_cfg_val |= FIELD_PREP(ADE9078_WF_CAP_SEL_MASK, tmp);
+
+	ret = of_property_read_u32((&st->spi->dev)->of_node, "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(ADE9078_WF_MODE_MASK, tmp);
+	st->wf_mode = tmp;
+
+	ret = of_property_read_u32((&st->spi->dev)->of_node, "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(ADE9078_WF_SRC_MASK, tmp);
+
+	ret = of_property_read_u32((&st->spi->dev)->of_node, "adi,wf-in-en",
+				   &tmp);
+	if (ret) {
+		dev_err(&st->spi->dev, "Failed to get wf-in-en: %d\n", ret);
+		return ret;
+	}
+	wfg_cfg_val |= FIELD_PREP(ADE9078_WF_IN_EN_MASK, tmp);
+
+	return regmap_write(st->regmap, ADE9078_REG_WFB_CFG, wfg_cfg_val);
+}
+
+/*
+ * ade9078_wfb_interrupt_setup() - Configures the wave form buffer interrupt
+ * according to modes
+ * @st:		ade9078 device data
+ * @mode:	modes according to datasheet; values [0-2]
+ *
+ * This sets the interrupt register and other registers related to the
+ * interrupts according to mode [0-2] from the datasheet
+ */
+static int ade9078_wfb_interrupt_setup(struct ade9078_state *st, u8 mode)
+{
+	int ret;
+
+	ret = regmap_write(st->regmap, ADE9078_REG_WFB_TRG_CFG, 0x0);
+	if (ret)
+		return ret;
+
+	if (mode == ADE9078_WFB_FULL_MODE || mode == ADE9078_WFB_EN_TRIG_MODE) {
+		ret = regmap_write(st->regmap, ADE9078_REG_WFB_PG_IRQEN,
+				   ADE9078_MODE_0_1_PAGE_BIT);
+		if (ret)
+			return ret;
+	} else if (mode == ADE9078_WFB_C_EN_TRIG_MODE) {
+		ret = regmap_write(st->regmap, ADE9078_REG_WFB_PG_IRQEN,
+				   ADE9078_MODE_2_PAGE_BIT);
+		if (ret)
+			return ret;
+	}
+
+	ret = regmap_write(st->regmap, ADE9078_REG_STATUS0, GENMASK(31, 0));
+	if (ret)
+		return ret;
+
+	return regmap_update_bits(st->regmap, ADE9078_REG_MASK0,
+				  ADE9078_ST0_PAGE_FULL_BIT,
+				  ADE9078_ST0_PAGE_FULL_BIT);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+/*
+ * ade9078_buffer_preenable() - configures the waveform buffer, sets the
+ * interrupts and enables the buffer
+ * @indio_dev:	the IIO device
+ */
+static int ade9078_buffer_preenable(struct iio_dev *indio_dev)
+{
+	struct ade9078_state *st = iio_priv(indio_dev);
+	int ret;
+
+	ret = ade9078_config_wfb(indio_dev);
+	if (ret)
+		return ret;
+
+	ret = ade9078_wfb_interrupt_setup(st, st->wf_mode);
+	if (ret)
+		return ret;
+
+	ret = ade9078_en_wfb(st, true);
+	if (ret) {
+		dev_err(&st->spi->dev, "Post-enable wfb enable fail");
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * ade9078_buffer_postdisable() - after the iio is disable
+ * this will disable the ade9078 internal buffer for acquisition
+ * @indio_dev:	the IIO device
+ */
+static int ade9078_buffer_postdisable(struct iio_dev *indio_dev)
+{
+	struct ade9078_state *st = iio_priv(indio_dev);
+	u32 interrupts = 0;
+	int ret;
+
+	ret = ade9078_en_wfb(st, false);
+	if (ret) {
+		dev_err(&st->spi->dev, "Post-disable wfb disable fail");
+		return ret;
+	}
+
+	ret = regmap_write(st->regmap, ADE9078_REG_WFB_TRG_CFG, 0x0);
+	if (ret)
+		return ret;
+
+	interrupts |= ADE9078_ST0_WFB_TRIG_BIT;
+	interrupts |= ADE9078_ST0_PAGE_FULL_BIT;
+
+	return regmap_update_bits(st->regmap, ADE9078_REG_MASK0, interrupts, 0);
+	if (ret) {
+		dev_err(&st->spi->dev, "Post-disable update maks0 fail");
+		return ret;
+	}
+
+	ret = regmap_write(st->regmap, ADE9078_REG_STATUS0, GENMASK(31, 0));
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+/*
+ * ade9078_setup_iio_channels() - parses the phase nodes of the device-tree and
+ * creates the iio channels based on the active phases in the DT.
+ * @st:		ade9078 device data
+ */
+static int ade9078_setup_iio_channels(struct ade9078_state *st)
+{
+	struct fwnode_handle *phase_node = NULL;
+	struct device *dev = &st->spi->dev;
+	struct iio_chan_spec *chan;
+	u32 phase_nr;
+	int ret;
+
+	chan = devm_kcalloc(dev,
+			    ADE9078_MAX_PHASE_NR *
+			    ARRAY_SIZE(ade9078_a_channels),
+			    sizeof(*ade9078_a_channels), GFP_KERNEL);
+	if (!chan) {
+		dev_err(dev, "Unable to allocate ADE9078 channels");
+		return -ENOMEM;
+	}
+	st->indio_dev->num_channels = 0;
+	st->indio_dev->channels = 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 ADE9078_PHASE_A_NR:
+			memcpy(chan, ade9078_a_channels,
+			       sizeof(ade9078_a_channels));
+			break;
+		case ADE9078_PHASE_B_NR:
+			memcpy(chan, ade9078_b_channels,
+			       sizeof(ade9078_b_channels));
+			break;
+		case ADE9078_PHASE_C_NR:
+			memcpy(chan, ade9078_c_channels,
+			       sizeof(ade9078_c_channels));
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		chan += AD9078_CHANNELS_PER_PHASE;
+		st->indio_dev->num_channels += AD9078_CHANNELS_PER_PHASE;
+	}
+
+	return 0;
+}
+
+/*
+ * ade9078_reset() - Reset sequence for the ADE9078, the hardware reset is
+ * optional in the DT. When no hardware reset has been declared a software
+ * reset is executed
+ * @st:		ade9078 device data
+ */
+static int ade9078_reset(struct ade9078_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, ADE9078_REG_CONFIG1,
+					 ADE9078_SWRST_BIT, ADE9078_SWRST_BIT);
+		if (ret)
+			return ret;
+		usleep_range(80, 100);
+	}
+
+	if (!st->rst_done)
+		return -EPERM;
+
+	return 0;
+}
+
+/*
+ * ade9078_setup() - initial register setup of the ade9078
+ * @st:		ade9078 device data
+ */
+static int ade9078_setup(struct ade9078_state *st)
+{
+	int ret = 0;
+
+	ret = regmap_multi_reg_write(st->regmap, ade9078_reg_sequence,
+				     ARRAY_SIZE(ade9078_reg_sequence));
+	if (ret)
+		return ret;
+
+	msleep_interruptible(2);
+
+	ret = regmap_write(st->regmap, ADE9078_REG_STATUS0, GENMASK(31, 0));
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, ADE9078_REG_STATUS1, GENMASK(31, 0));
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+static const struct iio_buffer_setup_ops ade9078_buffer_ops = {
+	.preenable = &ade9078_buffer_preenable,
+	.postdisable = &ade9078_buffer_postdisable,
+};
+
+static const struct iio_info ade9078_info = {
+	.read_raw = &ade9078_read_raw,
+	.write_raw = &ade9078_write_raw,
+	.debugfs_reg_access = &ade9078_reg_access,
+	.write_event_config = &ade9078_write_event_config,
+	.read_event_value = &ade9078_read_event_vlaue,
+};
+
+/*
+ * Regmap configuration
+ * The register access of the ade9078 requires a 16 bit address
+ * with the read flag on bit 3. This is not supported by default
+ * regmap functionality, thus reg_read and reg_write have been
+ * replaced with custom functions
+ */
+static const struct regmap_config ade9078_regmap_config = {
+	.reg_bits = 16,
+	.val_bits = 32,
+	.zero_flag_mask = true,
+	.cache_type = REGCACHE_RBTREE,
+	.reg_read = ade9078_spi_read_reg,
+	.reg_write = ade9078_spi_write_reg,
+	.volatile_reg = ade9078_is_volatile_reg,
+};
+
+static int ade9078_probe(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev;
+	struct ade9078_state *st;
+	unsigned long irqflags;
+	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 ADE9078 IIO");
+		return -ENOMEM;
+	}
+	st = iio_priv(indio_dev);
+	if (!st) {
+		dev_err(&spi->dev,
+			"Unable to allocate ADE9078 device structure");
+		return -ENOMEM;
+	}
+
+	st->rx = devm_kcalloc(&spi->dev, ADE9078_RX_DEPTH, sizeof(*st->rx),
+			      GFP_KERNEL);
+	if (!st->rx)
+		return -ENOMEM;
+
+	st->tx = devm_kcalloc(&spi->dev, ADE9078_TX_DEPTH, sizeof(*st->tx),
+			      GFP_KERNEL);
+	if (!st->tx)
+		return -ENOMEM;
+
+	regmap = devm_regmap_init(&spi->dev, NULL, spi, &ade9078_regmap_config);
+	if (IS_ERR(regmap))	{
+		dev_err(&spi->dev, "Unable to allocate ADE9078 regmap");
+		return PTR_ERR(regmap);
+	}
+	spi_set_drvdata(spi, st);
+
+	irq = of_irq_get_byname((&spi->dev)->of_node, "irq0");
+	if (irq < 0) {
+		dev_err(&spi->dev, "Unable to find irq0");
+		return -EINVAL;
+	}
+	irqflags = irq_get_trigger_type(irq);
+	ret = devm_request_threaded_irq(&spi->dev, irq, NULL,
+					ade9078_irq0_thread,
+					irqflags | IRQF_ONESHOT,
+					KBUILD_MODNAME, st);
+	if (ret) {
+		dev_err(&spi->dev, "Failed to request threaded irq: %d\n", ret);
+		return ret;
+	}
+
+	irq = of_irq_get_byname((&spi->dev)->of_node, "irq1");
+	if (irq < 0) {
+		dev_err(&spi->dev, "Unable to find irq1");
+		return -EINVAL;
+	}
+	irqflags = irq_get_trigger_type(irq);
+	ret = devm_request_threaded_irq(&spi->dev, irq, NULL,
+					ade9078_irq1_thread,
+					irqflags | IRQF_ONESHOT,
+					KBUILD_MODNAME, st);
+	if (ret) {
+		dev_err(&spi->dev, "Failed to request threaded irq: %d\n", ret);
+		return ret;
+	}
+
+	st->spi = spi;
+
+	indio_dev->dev.parent = &st->spi->dev;
+	indio_dev->info = &ade9078_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	st->regmap = regmap;
+	st->indio_dev = indio_dev;
+	ade9078_setup_iio_channels(st);
+	if (ret) {
+		dev_err(&spi->dev, "Failed to set up IIO channels");
+		return ret;
+	}
+
+	ret = ade9078_configure_scan(indio_dev);
+	if (ret)
+		return ret;
+
+	ret = devm_iio_kfifo_buffer_setup(&spi->dev, indio_dev,
+					  INDIO_BUFFER_SOFTWARE,
+					  &ade9078_buffer_ops);
+	if (ret)
+		return ret;
+
+	ret = devm_iio_device_register(&spi->dev, indio_dev);
+	if (ret) {
+		dev_err(&spi->dev, "Unable to register IIO device");
+		return ret;
+	}
+
+	ret = ade9078_reset(st);
+	if (ret) {
+		dev_err(&spi->dev, "ADE9078 reset failed");
+		return ret;
+	}
+
+	ret = ade9078_setup(st);
+	if (ret) {
+		dev_err(&spi->dev, "Unable to setup ADE9078");
+		return ret;
+	}
+
+	return ret;
+};
+
+static const struct spi_device_id ade9078_id[] = {
+		{"ade9078", 0},
+		{}
+};
+MODULE_DEVICE_TABLE(spi, ade9078_id);
+
+static const struct of_device_id ade9078_of_match[] = {
+	{ .compatible = "adi,ade9078" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, ade9078_of_match);
+
+static struct spi_driver ade9078_driver = {
+		.driver = {
+			.name = "ade9078",
+		},
+		.probe = ade9078_probe,
+		.id_table = ade9078_id,
+};
+module_spi_driver(ade9078_driver);
+
+MODULE_AUTHOR("Ciprian Hegbeli <ciprian.hegbeli@...log.com>");
+MODULE_DESCRIPTION("Analog Devices ADE9078 High Performance,Polyphase Energy Metering IC Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.34.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ