当前位置: 首页 > article >正文

linux 下调试 pac1934 电源监控器

一、环境

imx93 + PAC1934

linux 版本:linux-6.6.52

PAC1934 是 microchip 提供的 4 通道高精度电源监控器

二、驱动下载

由于我使用的 linux-6.6.52 版本,pac1934 的 iio 驱动还没合入该版本,于是去官网自己下载;

https://www.microchip.com/en-us/product/PAC1934

选择下图的 v0.2.1 版本的 linux 驱动;

三、驱动添加

修改 defconfig、Kconfig、Makefile 文件如下:

diff --git a/arch/arm64/configs/imx_v8_defconfig b/arch/arm64/configs/imx_v8_defconfig
index 544046f2aa8a..afd6303d5da0 100644
--- a/arch/arm64/configs/imx_v8_defconfig
+++ b/arch/arm64/configs/imx_v8_defconfig
@@ -995,6 +995,7 @@ CONFIG_IIO_ST_ACCEL_3AXIS=m
 CONFIG_IMX8QXP_ADC=y
 CONFIG_IMX93_ADC=y
 CONFIG_MAX9611=m
+CONFIG_PAC1934=y
 CONFIG_QCOM_SPMI_VADC=m
 CONFIG_QCOM_SPMI_ADC5=m
 CONFIG_IIO_CROS_EC_SENSORS_CORE=m
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index dfb925cfe38e..cca7019a2c4e 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -878,6 +878,17 @@ config NPCM_ADC
          This driver can also be built as a module. If so, the module
          will be called npcm_adc.
 
+config PAC1934
+       tristate "Microchip Technology PAC1934 driver"
+       depends on I2C
+       help
+         Say yes here to build support for Microchip Technology's PAC1931,
+         PAC1932, PAC1933, PAC1934 Single/Multi-Channel Power Monitor with
+         Accumulator.
+
+         This driver can also be built as a module. If so, the module
+         will be called pac1934.
+
 config PALMAS_GPADC
        tristate "TI Palmas General Purpose ADC"
        depends on MFD_PALMAS
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 2facf979327d..08829201b7bd 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_MP2629_ADC) += mp2629_adc.o
 obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
 obj-$(CONFIG_NAU7802) += nau7802.o
 obj-$(CONFIG_NPCM_ADC) += npcm_adc.o
+obj-$(CONFIG_PAC1934) += pac1934.o
 obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
 obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o
 obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o

添加设备树配置如下:

i2c 地址为 7 位地址,ADDRSEL 接电阻到地,不通的阻值地址值不一样;具体见下表;

shunt-resistor-micro-ohms = <20000000>; 单位为 uΩ,与实际的电路设计要对上;

&lpi2c1 {
	#address-cells = <1>;
	#size-cells = <0>;
	clock-frequency = <400000>;
	pinctrl-names = "default", "sleep";
	pinctrl-0 = <&pinctrl_lpi2c1>;
	pinctrl-1 = <&pinctrl_lpi2c1>;
	status = "okay";

	power-monitor@11 {
		compatible = "microchip,pac1934";
		reg = <0x11>;

		#address-cells = <1>;
		#size-cells = <0>;

		channel@1 {
			reg = <0x1>;
			shunt-resistor-micro-ohms = <50000000>;
			label = "VDD2";
		};

		channel@2 {
			reg = <0x2>;
			shunt-resistor-micro-ohms = <50000000>;
			label = "VDDQ";
		};

		channel@3 {
			reg = <0x3>;
			shunt-resistor-micro-ohms = <20000000>;
			label = "VDD3P3";
		};

		channel@4 {
			reg = <0x4>;
			shunt-resistor-micro-ohms = <5000000>;
			label = "SOC";
		};
	};
};

 将驱动文件拷贝到 drivers/iio/adc/ 目录下,pac1934.c 驱动文件内容如下:

// SPDX-License-Identifier: GPL-2.0+
/*
 * IIO driver for PAC1934 Multi-Channel DC Power/Energy Monitor
 *
 * Copyright (C) 2017-2023 Microchip Technology Inc. and its subsidiaries
 *
 * Author: Bogdan Bolocan <bogdan.bolocan@microchip.com>
 * Author: Victor Tudose
 * Author: Marius Cristea <marius.cristea@microchip.com>
 *
 * Datasheet for PAC1931, PAC1932, PAC1933 and PAC1934 can be found here:
 * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/PAC1931-Family-Data-Sheet-DS20005850E.pdf
 *
 */

#include <linux/acpi.h>
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <asm/unaligned.h>

/*
 * maximum accumulation time should be (17 * 60 * 1000) around 17 minutes@1024 sps
 * till PAC193X accumulation registers starts to saturate
 */
#define PAC193X_MAX_RFSH_LIMIT_MS		60000
/* 50msec is the timeout for validity of the cached registers */
#define PAC193X_MIN_POLLING_TIME_MS		50
/*
 * 1000usec is the minimum wait time for normal conversions when sample
 * rate doesn't change
 */
#define PAC193X_MIN_UPDATE_WAIT_TIME_US		1000

#define SHUNT_UOHMS_DEFAULT			100000
/* 32000mV */
#define PAC193X_VOLTAGE_MILLIVOLTS_MAX		32000
/* voltage bits resolution when set for unsigned values */
#define PAC193X_VOLTAGE_U_RES			16
/* voltage bits resolution when set for signed values */
#define PAC193X_VOLTAGE_S_RES			15
/* 100mV maximum for current shunts */
#define PAC193X_VSENSE_MILLIVOLTS_MAX		100
/* voltage bits resolution when set for unsigned values */
#define PAC193X_CURRENT_U_RES			16
/* voltage bits resolution when set for signed values */
#define PAC193X_CURRENT_S_RES			15

/* power resolution is 28 bits when unsigned */
#define PAC193X_POWER_U_RES			28
/* power resolution is 27 bits when signed */
#define PAC193X_POWER_S_RES			27

/* energy accumulation is 48 bits long */
#define PAC193X_ENERGY_U_RES			48

#define PAC193X_ENERGY_S_RES			47

#define BIT_INDEX_31				31

/*
 * max signed value that can be stored on 32 bits and 8 digits fractional value
 * (2^31 - 1) * 10^8 + 99999999
 */
#define PAC_193X_MAX_POWER_ACC			214748364799999999LL
/*
 * min signed value that can be stored on 32 bits and 8 digits fractional value
 * -(2^31) * 10^8 - 99999999
 */
#define PAC_193X_MIN_POWER_ACC			-214748364899999999LL

/* maximum value that device can measure - 32 V * 0.1 V */
#define PAC193X_PRODUCT_VOLTAGE_PV_FSR		3200000000000UL

#define PAC193X_MAX_NUM_CHANNELS		4
#define PAC1931_NUM_CHANNELS			1
#define PAC1932_NUM_CHANNELS			2
#define PAC1933_NUM_CHANNELS			3
#define PAC1934_NUM_CHANNELS			4
#define PAC193X_CH_1				0
#define PAC193X_CH_2				1
#define PAC193X_CH_3				2
#define PAC193X_CH_4				3
#define PAC193X_MEAS_REG_LEN			76
#define PAC193X_CTRL_REG_LEN			12

#define PAC193X_DEFAULT_CHIP_SAMP_SPEED		1024

/* I2C address map */
#define PAC193X_REFRESH_REG_ADDR		0x00
#define PAC193X_CTRL_REG_ADDR			0x01
#define PAC193X_ACC_COUNT_REG_ADDR		0x02
#define PAC193X_VPOWER_ACC_1_ADDR		0x03
#define PAC193X_VPOWER_ACC_2_ADDR		0x04
#define PAC193X_VPOWER_ACC_3_ADDR		0x05
#define PAC193X_VPOWER_ACC_4_ADDR		0x06
#define PAC193X_VBUS_1_ADDR			0x07
#define PAC193X_VBUS_2_ADDR			0x08
#define PAC193X_VBUS_3_ADDR			0x09
#define PAC193X_VBUS_4_ADDR			0x0A
#define PAC193X_VSENSE_1_ADDR			0x0B
#define PAC193X_VSENSE_2_ADDR			0x0C
#define PAC193X_VSENSE_3_ADDR			0x0D
#define PAC193X_VSENSE_4_ADDR			0x0E
#define PAC193X_VBUS_AVG_1_ADDR			0x0F
#define PAC193X_VBUS_AVG_2_ADDR			0x10
#define PAC193X_VBUS_AVG_3_ADDR			0x11
#define PAC193X_VBUS_AVG_4_ADDR			0x12
#define PAC193X_VSENSE_AVG_1_ADDR		0x13
#define PAC193X_VSENSE_AVG_2_ADDR		0x14
#define PAC193X_VSENSE_AVG_3_ADDR		0x15
#define PAC193X_VSENSE_AVG_4_ADDR		0x16
#define PAC193X_VPOWER_1_ADDR			0x17
#define PAC193X_VPOWER_2_ADDR			0x18
#define PAC193X_VPOWER_3_ADDR			0x19
#define PAC193X_VPOWER_4_ADDR			0x1A
#define PAC193X_REFRESH_V_REG_ADDR		0x1F
#define PAC193X_CTRL_STAT_REGS_ADDR		0x1C
#define PAC193X_PID_REG_ADDR			0xFD
#define PAC193X_MID_REG_ADDR			0xFE
#define PAC193X_RID_REG_ADDR			0xFF

/* PRODUCT ID REGISTER + MANUFACTURER ID REGISTER + REVISION ID REGISTER */
#define PAC193X_ID_REG_LEN			0x03
#define PAC193X_PID_IDX				0
#define PAC193X_MID_IDX				1
#define PAC193X_RID_IDX				2

#define PAC193X_ACPI_ARG_COUNT			4
#define PAC193X_ACPI_GET_NAMES_AND_MOHMS_VALS	1
#define PAC193X_ACPI_GET_UOHMS_VALS		2
#define PAC193X_ACPI_GET_BIPOLAR_SETTINGS	4
#define PAC193X_ACPI_GET_SAMP			5

#define PAC193X_SAMPLE_RATE_SHIFT		6

/*
 * these indexes are exactly describing the element order within a single
 * PAC193X phys channel IIO channel descriptor; see the static const struct
 * iio_chan_spec pac193x_single_channel[] declaration
 */
#define IIO_EN					0
#define IIO_POW					1
#define IIO_VOLT				2
#define IIO_CRT					3
#define IIO_VOLTAVG				4
#define IIO_CRTAVG				5

#define PAC193X_VBUS_SENSE_REG_LEN		2
#define PAC193X_ACC_REG_LEN			3
#define PAC193X_VPOWER_REG_LEN			4
#define PAC193X_VPOWER_ACC_REG_LEN		6
#define PAC193X_MAX_REGISTER_LENGTH		6

#define PAC193X_CUSTOM_ATTR_FOR_CHANNEL		2
#define PAC193X_SHARED_DEVATTRS_COUNT		1

/*
 * relative offsets when using multi-byte reads/writes even though these
 * bytes are read one after the other, they are not at adjacent memory
 * locations within the I2C memory map. The chip can skip some addresses
 */
#define PAC193X_CHANNEL_DIS_REG_OFF		0
#define PAC193X_NEG_PWR_REG_OFF			1

/*
 * when reading/writing multiple bytes from offset PAC193X_CHANNEL_DIS_REG_OFF,
 * the chip jumps over the 0x1E (REFRESH_G) and 0x1F (REFRESH_V) offsets
 */
#define PAC193X_SLOW_REG_OFF			2
#define PAC193X_CTRL_ACT_REG_OFF		3
#define PAC193X_CHANNEL_DIS_ACT_REG_OFF		4
#define PAC193X_NEG_PWR_ACT_REG_OFF		5
#define PAC193X_CTRL_LAT_REG_OFF		6
#define PAC193X_CHANNEL_DIS_LAT_REG_OFF		7
#define PAC193X_NEG_PWR_LAT_REG_OFF		8
#define PAC193X_PID_REG_OFF			9
#define PAC193X_MID_REG_OFF			10
#define PAC193X_REV_REG_OFF			11
#define PAC193X_CTRL_STATUS_INFO_LEN		12

#define PAC193X_MID				0x5D
#define PAC1934_PID				0x5B
#define PAC1933_PID				0x5A
#define PAC1932_PID				0x59
#define PAC1931_PID				0x58

/* Scale constant = (10^3 * 3.2 * 10^9 / 2^28) for mili Watt-second */
#define PAC193X_SCALE_CONSTANT			11921

#define PAC193X_MAX_VPOWER_RSHIFTED_BY_28B	11921
#define PAC193X_MAX_VSENSE_RSHIFTED_BY_16B	1525

#define PAC193X_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)

#define PAC193X_CRTL_SAMPLE_RATE_MASK	GENMASK(7, 6)
#define PAC193X_CRTL_SAMPLE_RATE_SET(x)	((u8)FIELD_PREP(PAC193X_CRTL_SAMPLE_RATE_MASK, (x)))
#define PAC193X_CHAN_SLEEP_MASK		BIT(5)
#define PAC193X_CHAN_SLEEP_SET		BIT(5)
#define PAC193X_CHAN_SINLE_MASK		BIT(4)
#define PAC193X_CHAN_SINLE_SHOT_SET	BIT(4)
#define PAC193X_CHAN_ALERT_MASK		BIT(3)
#define PAC193X_CHAN_ALERT_EN		BIT(3)
#define PAC193X_CHAN_ALERT_CC_MASK	BIT(2)
#define PAC193X_CHAN_ALERT_CC_EN	BIT(2)
#define PAC193X_CHAN_OVF_ALERT_MASK	BIT(1)
#define PAC193X_CHAN_OVF_ALERT_EN	BIT(1)
#define PAC193X_CHAN_OVF_MASK		BIT(0)

#define PAC193X_CHAN_DIS_CH1_OFF_MASK	BIT(7)
#define PAC193X_CHAN_DIS_CH1_OFF(x)	((u8)FIELD_PREP(PAC193X_CHAN_DIS_CH1_OFF_MASK, !(x)))
#define PAC193X_CHAN_DIS_CH2_OFF_MASK	BIT(6)
#define PAC193X_CHAN_DIS_CH2_OFF(x)	((u8)FIELD_PREP(PAC193X_CHAN_DIS_CH2_OFF_MASK, !(x)))
#define PAC193X_CHAN_DIS_CH3_OFF_MASK	BIT(5)
#define PAC193X_CHAN_DIS_CH3_OFF(x)	((u8)FIELD_PREP(PAC193X_CHAN_DIS_CH3_OFF_MASK, !(x)))
#define PAC193X_CHAN_DIS_CH4_OFF_MASK	BIT(4)
#define PAC193X_CHAN_DIS_CH4_OFF(x)	((u8)FIELD_PREP(PAC193X_CHAN_DIS_CH4_OFF_MASK, !(x)))
#define PAC193X_SMBUS_TIMEOUT_MASK	BIT(3)
#define PAC193X_SMBUS_TIMEOUT_EN(x)	FIELD_PREP(PAC193X_SMBUS_TIMEOUT_MASK, x)
#define PAC193X_SMBUS_BYTECOUNT_MASK	BIT(2)
#define PAC193X_SMBUS_BYTECOUNT_EN(x)	FIELD_PREP(PAC193X_SMBUS_BYTECOUNT_MASK, x)
#define PAC193X_SMBUS_NO_SKIP_MASK	BIT(1)
#define PAC193X_SMBUS_NO_SKIP_EN(x)	FIELD_PREP(PAC193X_SMBUS_NO_SKIP_MASK, x)

#define PAC193X_NEG_PWR_CH1_BIDI_MASK	BIT(7)
#define PAC193X_NEG_PWR_CH1_BIDI(x)	((u8)FIELD_PREP(PAC193X_NEG_PWR_CH1_BIDI_MASK, (x)))
#define PAC193X_NEG_PWR_CH2_BIDI_MASK	BIT(6)
#define PAC193X_NEG_PWR_CH2_BIDI(x)	((u8)FIELD_PREP(PAC193X_NEG_PWR_CH2_BIDI_MASK, (x)))
#define PAC193X_NEG_PWR_CH3_BIDI_MASK	BIT(5)
#define PAC193X_NEG_PWR_CH3_BIDI(x)	((u8)FIELD_PREP(PAC193X_NEG_PWR_CH3_BIDI_MASK, (x)))
#define PAC193X_NEG_PWR_CH4_BIDI_MASK	BIT(4)
#define PAC193X_NEG_PWR_CH4_BIDI(x)	((u8)FIELD_PREP(PAC193X_NEG_PWR_CH4_BIDI_MASK, (x)))
#define PAC193X_NEG_PWR_CH1_BIDV_MASK	BIT(3)
#define PAC193X_NEG_PWR_CH1_BIDV(x)	((u8)FIELD_PREP(PAC193X_NEG_PWR_CH1_BIDV_MASK, (x)))
#define PAC193X_NEG_PWR_CH2_BIDV_MASK	BIT(2)
#define PAC193X_NEG_PWR_CH2_BIDV(x)	((u8)FIELD_PREP(PAC193X_NEG_PWR_CH2_BIDV_MASK, (x)))
#define PAC193X_NEG_PWR_CH3_BIDV_MASK	BIT(1)
#define PAC193X_NEG_PWR_CH3_BIDV(x)	((u8)FIELD_PREP(PAC193X_NEG_PWR_CH3_BIDV_MASK, (x)))
#define PAC193X_NEG_PWR_CH4_BIDV_MASK	BIT(0)
#define PAC193X_NEG_PWR_CH4_BIDV(x)	((u8)FIELD_PREP(PAC193X_NEG_PWR_CH4_BIDV_MASK, (x)))

/*
 * Universal Unique Identifier (UUID),
 * 033771E0-1705-47B4-9535-D1BBE14D9A09, is
 * reserved to Microchip for the PAC193X and must not be changed
 */
#define PAC193X_DSM_UUID		"033771E0-1705-47B4-9535-D1BBE14D9A09"

enum pac193x_ids {
	PAC1931,
	PAC1932,
	PAC1933,
	PAC1934
};

enum pac193x_samps {
	PAC193X_SAMP_1024SPS,
	PAC193X_SAMP_256SPS,
	PAC193X_SAMP_64SPS,
	PAC193X_SAMP_8SPS
};

/**
 * struct pac193x_features - features of a pac193x instance
 * @phys_channels: number of physical channels supported by the chip
 * @prod_id: product ID
 */
struct pac193x_features {
	u8 phys_channels;
	u8 prod_id;
};

struct samp_rate_mapping {
	u16 samp_rate;
	u8 shift2value;
};

static const unsigned int samp_rate_map_tbl[] = {
	[PAC193X_SAMP_1024SPS] = 1024,
	[PAC193X_SAMP_256SPS] = 256,
	[PAC193X_SAMP_64SPS] = 64,
	[PAC193X_SAMP_8SPS] = 8,
};

static const struct pac193x_features pac193x_chip_config[] = {
	[PAC1931] = {
	    .phys_channels = 1,
	    .prod_id = PAC1931_PID,
	},
	[PAC1932] = {
	    .phys_channels = 2,
	    .prod_id = PAC1932_PID,
	},
	[PAC1933] = {
	    .phys_channels = 3,
	    .prod_id = PAC1933_PID,
	},
	[PAC1934] = {
	    .phys_channels = 4,
	    .prod_id = PAC1934_PID,
	},
};

/**
 * struct reg_data - data from the registers
 * @meas_regs: snapshot of raw measurements registers
 * @ctrl_regs: snapshot of control registers
 * @energy_sec_acc: snapshot of energy values
 * @vpower_acc: accumulated vpower values
 * @vpower: snapshot of vpower registers
 * @vbus: snapshot of vbus registers
 * @vbus_avg: averages of vbus registers
 * @vsense: snapshot of vsense registers
 * @vsense_avg: averages of vsense registers
 * @num_enabled_channels: count of how many chip channels are currently enabled
 */
struct reg_data {
	u8	meas_regs[PAC193X_MEAS_REG_LEN];
	u8	ctrl_regs[PAC193X_CTRL_REG_LEN];
	s64	energy_sec_acc[PAC193X_MAX_NUM_CHANNELS];
	s64	vpower_acc[PAC193X_MAX_NUM_CHANNELS];
	s32	vpower[PAC193X_MAX_NUM_CHANNELS];
	s32	vbus[PAC193X_MAX_NUM_CHANNELS];
	s32	vbus_avg[PAC193X_MAX_NUM_CHANNELS];
	s32	vsense[PAC193X_MAX_NUM_CHANNELS];
	s32	vsense_avg[PAC193X_MAX_NUM_CHANNELS];
	u8	num_enabled_channels;
};

/**
 * struct pac193x_chip_info - information about the chip
 * @client: the i2c-client attached to the device
 * @lock: lock used by the chip's mutex
 * @work_chip_rfsh: work queue used for refresh commands
 * @phys_channels: phys channels count
 * @active_channels: array of values, true means that channel is active
 * @bi_dir: array of bools, true means that channel is bidirectional
 * @chip_variant: chip variant
 * @chip_revision: chip revision
 * @shunts: shunts
 * @chip_reg_data: chip reg data
 * @sample_rate_value: sampling frequency
 * @channel_names: channel names
 * @pac193x_info: pac193x_info
 * @crt_samp_spd_bitfield:the current sampling speed
 * @jiffies_tstamp: chip's uptime
 */
struct pac193x_chip_info {
	struct i2c_client	*client;
	struct mutex		lock; /* lock to prevent concurrent reads/writes */
	struct delayed_work	work_chip_rfsh;
	u8			phys_channels;
	bool			active_channels[PAC193X_MAX_NUM_CHANNELS];
	bool			bi_dir[PAC193X_MAX_NUM_CHANNELS];
	u8			chip_variant;
	u8			chip_revision;
	u32			shunts[PAC193X_MAX_NUM_CHANNELS];
	struct reg_data		chip_reg_data;
	s32			sample_rate_value;
	char			*channel_names[PAC193X_MAX_NUM_CHANNELS];
	struct iio_info		pac193x_info;
	u8			crt_samp_spd_bitfield;
	unsigned long		jiffies_tstamp;
};

#define TO_PAC193X_CHIP_INFO(d) container_of(d, struct pac193x_chip_info, work_chip_rfsh)

/* macros to extract the parameters */
#define PAC193X_MVBUS_SENSES(x)		sign_extend32((u32)(x), 15)

#define PAC193X_VPOWER_ACC_CHANNEL(_index, _si, _address) {			\
	.type = IIO_ENERGY,							\
	.address = (_address),							\
	.indexed = 1,								\
	.channel = (_index),							\
	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)	|			\
				BIT(IIO_CHAN_INFO_SCALE),			\
	.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),		\
	.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
	.scan_index = (_si),							\
	.scan_type = {								\
		.sign = 'u',							\
		.realbits = PAC193X_ENERGY_U_RES,				\
		.storagebits = PAC193X_ENERGY_U_RES,				\
		.endianness = IIO_CPU,						\
	}									\
}

#define PAC193X_VBUS_CHANNEL(_index, _si, _address) {				\
	.type = IIO_VOLTAGE,							\
	.address = (_address),							\
	.indexed = 1,								\
	.channel = (_index),							\
	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),				\
	.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),		\
	.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
	.scan_index = (_si),							\
	.scan_type = {								\
		.sign = 'u',							\
		.realbits = PAC193X_VOLTAGE_U_RES,				\
		.storagebits = PAC193X_VOLTAGE_U_RES,				\
		.endianness = IIO_CPU,						\
	}									\
}

#define PAC193X_VBUS_AVG_CHANNEL(_index, _si, _address) {			\
	.type = IIO_VOLTAGE,							\
	.address = (_address),							\
	.indexed = 1,								\
	.channel = (_index),							\
	.info_mask_separate = BIT(IIO_CHAN_INFO_AVERAGE_RAW)			\
				| BIT(IIO_CHAN_INFO_SCALE),			\
	.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),		\
	.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
	.scan_index = (_si),							\
	.scan_type = {								\
		.sign = 'u',							\
		.realbits = PAC193X_VOLTAGE_U_RES,				\
		.storagebits = PAC193X_VOLTAGE_U_RES,				\
		.endianness = IIO_CPU,						\
	}									\
}

#define PAC193X_VSENSE_CHANNEL(_index, _si, _address) {				\
	.type = IIO_CURRENT,							\
	.address = (_address),							\
	.indexed = 1,								\
	.channel = (_index),							\
	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),				\
	.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),		\
	.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
	.scan_index = (_si),							\
	.scan_type = {								\
		.sign = 'u',							\
		.realbits = PAC193X_CURRENT_U_RES,				\
		.storagebits = PAC193X_CURRENT_U_RES,				\
		.endianness = IIO_CPU,						\
	}									\
}

#define PAC193X_VSENSE_AVG_CHANNEL(_index, _si, _address) {			\
	.type = IIO_CURRENT,							\
	.address = (_address),							\
	.indexed = 1,								\
	.channel = (_index),							\
	.info_mask_separate = BIT(IIO_CHAN_INFO_AVERAGE_RAW) |			\
			BIT(IIO_CHAN_INFO_SCALE),				\
	.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),		\
	.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
	.scan_index = (_si),							\
	.scan_type = {								\
		.sign = 'u',							\
		.realbits = PAC193X_CURRENT_U_RES,				\
		.storagebits = PAC193X_CURRENT_U_RES,				\
		.endianness = IIO_CPU,						\
	}									\
}

#define PAC193X_VPOWER_CHANNEL(_index, _si, _address) {				\
	.type = IIO_POWER,							\
	.address = (_address),							\
	.indexed = 1,								\
	.channel = (_index),							\
	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |				\
			BIT(IIO_CHAN_INFO_SCALE),				\
	.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),		\
	.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
	.scan_index = (_si),							\
	.scan_type = {								\
		.sign = 'u',							\
		.realbits = PAC193X_POWER_U_RES,				\
		.storagebits = 32,						\
		.shift = 4,							\
		.endianness = IIO_CPU,						\
	}									\
}

static const struct iio_chan_spec pac193x_single_channel[] = {
	PAC193X_VPOWER_ACC_CHANNEL(0, 0, PAC193X_VPOWER_ACC_1_ADDR),
	PAC193X_VPOWER_CHANNEL(0, 0, PAC193X_VPOWER_1_ADDR),
	PAC193X_VBUS_CHANNEL(0, 0, PAC193X_VBUS_1_ADDR),
	PAC193X_VSENSE_CHANNEL(0, 0, PAC193X_VSENSE_1_ADDR),
	PAC193X_VBUS_AVG_CHANNEL(0, 0, PAC193X_VBUS_AVG_1_ADDR),
	PAC193X_VSENSE_AVG_CHANNEL(0, 0, PAC193X_VSENSE_AVG_1_ADDR),
};

/* Low-level I2c functions */
static int pac193x_i2c_read(struct i2c_client *client, u8 reg_addr, void *databuf, u8 len)
{
	int ret;
	struct i2c_msg msgs[2] = {
		{ .addr = client->addr,
		 .len = 1,
		 .buf = (u8 *)&reg_addr,
		 .flags = 0
		},
		{ .addr = client->addr,
		 .len = len,
		 .buf = databuf,
		 .flags = I2C_M_RD
		}
	};

	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
	if (ret < 0)
		return ret;

	return 0;
}

static int pac193x_i2c_write(struct i2c_client *client, u8 reg_addr, u8 *data, int len)
{
	int ret;
	u8 send[PAC193X_MAX_REGISTER_LENGTH + 1];

	send[0] = reg_addr;
	memcpy(&send[1], data, len * sizeof(u8));

	ret = i2c_master_send(client, send, len + 1);
	if (ret < 0) {
		dev_err(&client->dev,
			"failed writing data from register 0x%02X\n", reg_addr);
		return ret;
	}

	return 0;
}

static int pac193x_match_samp_rate(struct pac193x_chip_info *chip_info, u32 new_samp_rate)
{
	int cnt;

	for (cnt = 0; cnt < ARRAY_SIZE(samp_rate_map_tbl); cnt++) {
		if (new_samp_rate == samp_rate_map_tbl[cnt]) {
			chip_info->crt_samp_spd_bitfield = cnt;
			return 0;
		}
	}

	/* not a valid sample rate value */
	return -EINVAL;
}

static ssize_t shunt_value_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
	struct pac193x_chip_info *chip_info = iio_priv(indio_dev);
	int len = 0;
	int target = (int)(attr->attr.name[strlen(attr->attr.name) - 1] - '0') - 1;

	len += sprintf(buf, "%u\n", chip_info->shunts[target]);

	return len;
}

static ssize_t channel_name_show(struct device *dev,
				 struct device_attribute *attr,
				 char *buf)
{
	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
	struct pac193x_chip_info *chip_info = iio_priv(indio_dev);
	int len = 0;
	int target = (int)(attr->attr.name[strlen(attr->attr.name) - 1] - '0') - 1;

	len += sprintf(buf, "%s\n", chip_info->channel_names[target]);

	return len;
}

static ssize_t shunt_value_store(struct device *dev,
				 struct device_attribute *attr,
				 const char *buf, size_t count)
{
	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
	struct pac193x_chip_info *chip_info = iio_priv(indio_dev);
	int sh_val, target;

	target = (int)(attr->attr.name[strlen(attr->attr.name) - 1] - '0') - 1;
	if (kstrtouint(buf, 10, &sh_val)) {
		dev_err(dev, "Shunt value is not valid\n");
		return -EINVAL;
	}

	mutex_lock(&chip_info->lock);
	chip_info->shunts[target] = sh_val;
	mutex_unlock(&chip_info->lock);

	return count;
}

static int pac193x_read_avail(struct iio_dev *indio_dev,
			      struct iio_chan_spec const *channel,
			      const int **vals, int *type, int *length, long mask)
{
	switch (mask) {
	case IIO_CHAN_INFO_SAMP_FREQ:
		*type = IIO_VAL_INT;
		*vals = samp_rate_map_tbl;
		*length = ARRAY_SIZE(samp_rate_map_tbl);
		return IIO_AVAIL_LIST;
	}

	return -EINVAL;
}

static int pac193x_send_refresh(struct pac193x_chip_info *chip_info,
				u8 refresh_cmd, u32 wait_time)
{
	/* this function only sends REFRESH or REFRESH_V */
	struct i2c_client *client = chip_info->client;
	int ret;
	u8 bidir_reg;
	bool revision_bug = false;

	if (chip_info->chip_revision == 2 || chip_info->chip_revision == 3) {
		/*
		 * chip rev 2 and 3 bug workaround
		 * see: PAC193X Family Data Sheet Errata DS80000836A.pdf
		 */
		revision_bug = true;

		bidir_reg = PAC193X_NEG_PWR_CH1_BIDI(chip_info->bi_dir[PAC193X_CH_1]) |
			PAC193X_NEG_PWR_CH2_BIDI(chip_info->bi_dir[PAC193X_CH_2]) |
			PAC193X_NEG_PWR_CH3_BIDI(chip_info->bi_dir[PAC193X_CH_3]) |
			PAC193X_NEG_PWR_CH4_BIDI(chip_info->bi_dir[PAC193X_CH_4]) |
			PAC193X_NEG_PWR_CH1_BIDV(chip_info->bi_dir[PAC193X_CH_1]) |
			PAC193X_NEG_PWR_CH2_BIDV(chip_info->bi_dir[PAC193X_CH_2]) |
			PAC193X_NEG_PWR_CH3_BIDV(chip_info->bi_dir[PAC193X_CH_3]) |
			PAC193X_NEG_PWR_CH4_BIDV(chip_info->bi_dir[PAC193X_CH_4]);

		ret = i2c_smbus_write_byte_data(client,
						PAC193X_CTRL_STAT_REGS_ADDR +
						PAC193X_NEG_PWR_REG_OFF,
						bidir_reg);
		if (ret)
			return ret;
	}

	ret = i2c_smbus_write_byte(client, refresh_cmd);
	if (ret) {
		dev_err(&client->dev, "%s - cannot send 0x%02X\n",
			__func__, refresh_cmd);
		return ret;
	}

	if (revision_bug) {
		/*
		 * chip rev 2 and 3 bug workaround - write again the same register
		 * write the updated registers back
		 */
		ret = i2c_smbus_write_byte_data(client,
						PAC193X_CTRL_STAT_REGS_ADDR +
						PAC193X_NEG_PWR_REG_OFF, bidir_reg);
		if (ret)
			return ret;
	}

	/* register data retrieval timestamp */
	chip_info->jiffies_tstamp = jiffies;

	/* wait till the data is available */
	usleep_range(wait_time, wait_time + 100);

	return ret;
}

static int pac193x_reg_snapshot(struct pac193x_chip_info *chip_info,
				bool do_refresh, u8 refresh_cmd, u32 wait_time)
{
	int ret;
	struct i2c_client *client = chip_info->client;
	u8 samp_shift, ctrl_regs_tmp;
	u8 *offset_reg_data_p;
	u16 tmp_value;
	u32 samp_rate, cnt, tmp;
	s64 curr_energy, inc;
	u64 tmp_energy;
	struct reg_data *reg_data;

	mutex_lock(&chip_info->lock);

	if (do_refresh) {
		ret = pac193x_send_refresh(chip_info, refresh_cmd, wait_time);
		if (ret < 0) {
			dev_err(&client->dev,
				"%s - cannot send refresh\n",
				__func__);
			goto reg_snapshot_err;
		}
	}

	/* read the ctrl/status registers for this snapshot */
	ret = pac193x_i2c_read(client, PAC193X_CTRL_STAT_REGS_ADDR,
			       (u8 *)chip_info->chip_reg_data.ctrl_regs,
			       PAC193X_CTRL_REG_LEN);
	if (ret) {
		dev_err(&client->dev,
			"%s - cannot read ctrl/status registers\n",
			__func__);
		goto reg_snapshot_err;
	}

	reg_data = &chip_info->chip_reg_data;

	/* read the data registers */
	ret = pac193x_i2c_read(client, PAC193X_ACC_COUNT_REG_ADDR,
			       (u8 *)reg_data->meas_regs, PAC193X_MEAS_REG_LEN);
	if (ret) {
		dev_err(&client->dev,
			"%s - cannot read ACC_COUNT register: %d:%d\n",
			__func__, ret, PAC193X_MEAS_REG_LEN);
		goto reg_snapshot_err;
	}

	/* see how much shift is required by the sample rate */
	samp_rate = samp_rate_map_tbl[((reg_data->ctrl_regs[PAC193X_CTRL_LAT_REG_OFF]) >> 6)];
	samp_shift = get_count_order(samp_rate);

	ctrl_regs_tmp = reg_data->ctrl_regs[PAC193X_CHANNEL_DIS_LAT_REG_OFF];
	offset_reg_data_p = &reg_data->meas_regs[PAC193X_ACC_REG_LEN];

	/* start with VPOWER_ACC */
	for (cnt = 0; cnt < chip_info->phys_channels; cnt++) {
		/* check if the channel is active, skip all fields if disabled */
		if (((ctrl_regs_tmp << cnt) & 0x80) == 0) {
			curr_energy = chip_info->chip_reg_data.energy_sec_acc[cnt];

			tmp_energy = get_unaligned_be48(offset_reg_data_p);

			if (chip_info->bi_dir[cnt])
				reg_data->vpower_acc[cnt] = sign_extend64(tmp_energy, 47);
			else
				reg_data->vpower_acc[cnt] = tmp_energy;

			/*
			 * compute the scaled to 1 second accumulated energy value;
			 * energy accumulator scaled to 1sec = VPOWER_ACC/2^samp_shift
			 * the chip's sampling rate is 2^samp_shift samples/sec
			 */
			inc = (reg_data->vpower_acc[cnt] >> samp_shift);

			/* add the power_acc field */
			curr_energy += inc;

			/* check if we have reached the upper/lower limit */
			if (curr_energy > (s64)PAC_193X_MAX_POWER_ACC)
				curr_energy = PAC_193X_MAX_POWER_ACC;
			else if (curr_energy < ((s64)PAC_193X_MIN_POWER_ACC))
				curr_energy = PAC_193X_MIN_POWER_ACC;

			reg_data->energy_sec_acc[cnt] = curr_energy;

			offset_reg_data_p += PAC193X_VPOWER_ACC_REG_LEN;
		}
	}

	/* continue with VBUS */
	for (cnt = 0; cnt < chip_info->phys_channels; cnt++) {
		if (((ctrl_regs_tmp << cnt) & 0x80) == 0) {
			tmp_value = get_unaligned_be16(offset_reg_data_p);

			if (chip_info->bi_dir[cnt])
				reg_data->vbus[cnt] = PAC193X_MVBUS_SENSES(tmp_value);
			else
				reg_data->vbus[cnt] = tmp_value;

			offset_reg_data_p += PAC193X_VBUS_SENSE_REG_LEN;
		}
	}

	/* VSENSE */
	for (cnt = 0; cnt < chip_info->phys_channels; cnt++) {
		if (((ctrl_regs_tmp << cnt) & 0x80) == 0) {
			tmp_value = get_unaligned_be16(offset_reg_data_p);

			if (chip_info->bi_dir[cnt])
				reg_data->vsense[cnt] = PAC193X_MVBUS_SENSES(tmp_value);
			else
				reg_data->vsense[cnt] = tmp_value;

			offset_reg_data_p += PAC193X_VBUS_SENSE_REG_LEN;
		}
	}

	/* VBUS_AVG */
	for (cnt = 0; cnt < chip_info->phys_channels; cnt++) {
		if (((ctrl_regs_tmp << cnt) & 0x80) == 0) {
			tmp_value = get_unaligned_be16(offset_reg_data_p);

			if (chip_info->bi_dir[cnt])
				reg_data->vbus_avg[cnt] = PAC193X_MVBUS_SENSES(tmp_value);
			else
				reg_data->vbus_avg[cnt] = tmp_value;

			offset_reg_data_p += PAC193X_VBUS_SENSE_REG_LEN;
		}
	}

	/* VSENSE_AVG */
	for (cnt = 0; cnt < chip_info->phys_channels; cnt++) {
		if (((ctrl_regs_tmp << cnt) & 0x80) == 0) {
			tmp_value = get_unaligned_be16(offset_reg_data_p);

			if (chip_info->bi_dir[cnt])
				reg_data->vsense_avg[cnt] = PAC193X_MVBUS_SENSES(tmp_value);
			else
				reg_data->vsense_avg[cnt] = tmp_value;

			offset_reg_data_p += PAC193X_VBUS_SENSE_REG_LEN;
		}
	}

	/* VPOWER */
	for (cnt = 0; cnt < chip_info->phys_channels; cnt++) {
		if (((ctrl_regs_tmp << cnt) & 0x80) == 0) {
			tmp = get_unaligned_be32(offset_reg_data_p) >> 4;

			if (chip_info->bi_dir[cnt])
				reg_data->vpower[cnt] = sign_extend32(tmp, 27);
			else
				reg_data->vpower[cnt] = tmp;

			offset_reg_data_p += PAC193X_VPOWER_REG_LEN;
		}
	}

reg_snapshot_err:
	mutex_unlock(&chip_info->lock);

	return ret;
}

static int pac193x_retrieve_data(struct pac193x_chip_info *chip_info,
				 u32 wait_time)
{
	int ret = 0;

	/*
	 * check if the minimal elapsed time has passed and if so,
	 * re-read the chip, otherwise the cached info is just fine
	 */
	if (time_after(jiffies, chip_info->jiffies_tstamp +
					 msecs_to_jiffies(PAC193X_MIN_POLLING_TIME_MS))) {
		ret = pac193x_reg_snapshot(chip_info, true, PAC193X_REFRESH_REG_ADDR,
					   wait_time);

		/*
		 * Re-schedule the work for the read registers on timeout
		 * (to prevent chip regs saturation)
		 */
		cancel_delayed_work_sync(&chip_info->work_chip_rfsh);
		schedule_delayed_work(&chip_info->work_chip_rfsh,
				      msecs_to_jiffies(PAC193X_MAX_RFSH_LIMIT_MS));
	}

	return ret;
}

static int pac193x_read_raw(struct iio_dev *indio_dev,
			    struct iio_chan_spec const *chan, int *val,
			    int *val2, long mask)
{
	struct pac193x_chip_info *chip_info = iio_priv(indio_dev);
	s64 curr_energy;
	int ret, channel = chan->channel - 1;

	ret = pac193x_retrieve_data(chip_info, PAC193X_MIN_UPDATE_WAIT_TIME_US);
	if (ret < 0)
		return ret;

	switch (mask) {
	case IIO_CHAN_INFO_RAW:
		switch (chan->type) {
		case IIO_VOLTAGE:
			switch (chan->address) {
			case PAC193X_VBUS_1_ADDR:
			case PAC193X_VBUS_2_ADDR:
			case PAC193X_VBUS_3_ADDR:
			case PAC193X_VBUS_4_ADDR:
				*val = chip_info->chip_reg_data.vbus[channel];
				return IIO_VAL_INT;
			default:
				return -EINVAL;
			}
			break;
		case IIO_CURRENT:
			switch (chan->address) {
			case PAC193X_VSENSE_1_ADDR:
			case PAC193X_VSENSE_2_ADDR:
			case PAC193X_VSENSE_3_ADDR:
			case PAC193X_VSENSE_4_ADDR:
				*val = chip_info->chip_reg_data.vsense[channel];
				return IIO_VAL_INT;
			default:
				return -EINVAL;
			}
			break;
		case IIO_POWER:
			switch (chan->address) {
			case PAC193X_VPOWER_1_ADDR:
			case PAC193X_VPOWER_2_ADDR:
			case PAC193X_VPOWER_3_ADDR:
			case PAC193X_VPOWER_4_ADDR:
				*val = chip_info->chip_reg_data.vpower[channel];
				return IIO_VAL_INT;
			default:
				return -EINVAL;
			}
			break;
		case IIO_ENERGY:
			switch (chan->address) {
			case PAC193X_VPOWER_ACC_1_ADDR:
			case PAC193X_VPOWER_ACC_2_ADDR:
			case PAC193X_VPOWER_ACC_3_ADDR:
			case PAC193X_VPOWER_ACC_4_ADDR:

				curr_energy = chip_info->chip_reg_data.energy_sec_acc[channel];
				*val = (u32)curr_energy;
				*val2 = (u32)(curr_energy >> 32);
				return IIO_VAL_INT_64;
			default:
				return -EINVAL;
			}
			break;
		default:
			return -EINVAL;
		}
		break;
	case IIO_CHAN_INFO_AVERAGE_RAW:
		switch (chan->type) {
		case IIO_VOLTAGE:
			switch (chan->address) {
			case PAC193X_VBUS_AVG_1_ADDR:
			case PAC193X_VBUS_AVG_2_ADDR:
			case PAC193X_VBUS_AVG_3_ADDR:
			case PAC193X_VBUS_AVG_4_ADDR:
				*val = chip_info->chip_reg_data.vbus_avg[channel];
				return IIO_VAL_INT;

			default:
				return -EINVAL;
			}
			break;
		case IIO_CURRENT:
			switch (chan->address) {
			case PAC193X_VSENSE_AVG_1_ADDR:
			case PAC193X_VSENSE_AVG_2_ADDR:
			case PAC193X_VSENSE_AVG_3_ADDR:
			case PAC193X_VSENSE_AVG_4_ADDR:
				*val = chip_info->chip_reg_data.vsense_avg[channel];
				return IIO_VAL_INT;

			default:
				return -EINVAL;
			}
			break;
		default:
			return -EINVAL;
		}
		break;
	case IIO_CHAN_INFO_SCALE:
		switch (chan->address) {
		/* Voltages - scale for millivolts */
		case PAC193X_VBUS_1_ADDR:
		case PAC193X_VBUS_2_ADDR:
		case PAC193X_VBUS_3_ADDR:
		case PAC193X_VBUS_4_ADDR:
		case PAC193X_VBUS_AVG_1_ADDR:
		case PAC193X_VBUS_AVG_2_ADDR:
		case PAC193X_VBUS_AVG_3_ADDR:
		case PAC193X_VBUS_AVG_4_ADDR:
			*val = PAC193X_VOLTAGE_MILLIVOLTS_MAX;
			if (chan->scan_type.sign == 'u')
				*val2 = PAC193X_VOLTAGE_U_RES;
			else
				*val2 = PAC193X_VOLTAGE_S_RES;
			return IIO_VAL_FRACTIONAL_LOG2;

		/*
		 * Currents - scale for mA - depends on the
		 * channel's shunt value
		 * (100mV * 1000000) / (2^16 * shunt(uohm))
		 */
		case PAC193X_VSENSE_1_ADDR:
		case PAC193X_VSENSE_2_ADDR:
		case PAC193X_VSENSE_3_ADDR:
		case PAC193X_VSENSE_4_ADDR:
		case PAC193X_VSENSE_AVG_1_ADDR:
		case PAC193X_VSENSE_AVG_2_ADDR:
		case PAC193X_VSENSE_AVG_3_ADDR:
		case PAC193X_VSENSE_AVG_4_ADDR:
			*val = PAC193X_MAX_VSENSE_RSHIFTED_BY_16B;
			if (chan->scan_type.sign == 'u')
				*val2 = chip_info->shunts[channel];
			else
				*val2 = chip_info->shunts[channel] >> 1;
			return IIO_VAL_FRACTIONAL;

		/*
		 * Power - uW - it will use the combined scale
		 * for current and voltage
		 * current(mA) * voltage(mV) = power (uW)
		 */
		case PAC193X_VPOWER_1_ADDR:
		case PAC193X_VPOWER_2_ADDR:
		case PAC193X_VPOWER_3_ADDR:
		case PAC193X_VPOWER_4_ADDR:
			*val = PAC193X_MAX_VPOWER_RSHIFTED_BY_28B;
			if (chan->scan_type.sign == 'u')
				*val2 = chip_info->shunts[channel];
			else
				*val2 = chip_info->shunts[channel] >> 1;
			return IIO_VAL_FRACTIONAL;
		case PAC193X_VPOWER_ACC_1_ADDR:
		case PAC193X_VPOWER_ACC_2_ADDR:
		case PAC193X_VPOWER_ACC_3_ADDR:
		case PAC193X_VPOWER_ACC_4_ADDR:

			/*
			 * expresses the 32 bit scale value
			 * here compute the scale for energy (mili Watt-second or miliJoule)
			 */
			*val = PAC193X_SCALE_CONSTANT;

			if (chan->scan_type.sign == 'u')
				*val2 = chip_info->shunts[channel];
			else
				*val2 = chip_info->shunts[channel] >> 1;
			return IIO_VAL_FRACTIONAL;
		default:
			return -EINVAL;
		}
		break;
	case IIO_CHAN_INFO_SAMP_FREQ:
		*val = chip_info->sample_rate_value;
		return IIO_VAL_INT;
	default:
		return -EINVAL;
	}

	return -EINVAL;
}

static int pac193x_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
			     int val, int val2, long mask)
{
	struct pac193x_chip_info *chip_info = iio_priv(indio_dev);
	struct i2c_client *client = chip_info->client;
	int ret = -EINVAL;
	s32 old_samp_rate;
	u8 ctrl_reg;

	switch (mask) {
	case IIO_CHAN_INFO_SAMP_FREQ:
		ret = pac193x_match_samp_rate(chip_info, (u16)val);
		if (ret)
			return ret;

		old_samp_rate = chip_info->sample_rate_value;
		chip_info->sample_rate_value = val;

		/*
		 * now lock the access to the chip, write the new
		 * sampling value and trigger a snapshot(incl refresh)
		 */
		mutex_lock(&chip_info->lock);

		ctrl_reg = PAC193X_CRTL_SAMPLE_RATE_SET(chip_info->crt_samp_spd_bitfield);
		ret = i2c_smbus_write_byte_data(client, PAC193X_CTRL_REG_ADDR, ctrl_reg);
		if (ret) {
			dev_err(&client->dev, "%s - can't update sample rate\n", __func__);
			chip_info->sample_rate_value = old_samp_rate;
			mutex_unlock(&chip_info->lock);
			return ret;
		}

		/*
		 * unlock the access towards the chip - register
		 * snapshot includes its own access lock
		 */
		mutex_unlock(&chip_info->lock);

		/*
		 * now, force a snapshot with refresh - call retrieve
		 * data in order to update the refresh timer
		 * alter the timestamp in order to force trigger a
		 * register snapshot and a timestamp update
		 */
		chip_info->jiffies_tstamp -=
			msecs_to_jiffies(PAC193X_MIN_POLLING_TIME_MS);
		ret = pac193x_retrieve_data(chip_info, (1024 / old_samp_rate) * 1000);
		if (ret < 0) {
			dev_err(&client->dev,
				"%s - cannot snapshot ctrl and measurement regs\n",
				__func__);
			return ret;
		}

		ret = 0;
		break;
	default:
		break;
	}

	return ret;
}

static void pac193x_work_periodic_rfsh(struct work_struct *work)
{
	struct pac193x_chip_info *chip_info = TO_PAC193X_CHIP_INFO((struct delayed_work *)work);
	struct i2c_client *client = chip_info->client;

	dev_dbg(&client->dev, "%s - Periodic refresh\n", __func__);

	/* do a REFRESH, then read */
	pac193x_reg_snapshot(chip_info, true, PAC193X_REFRESH_REG_ADDR,
			     PAC193X_MIN_UPDATE_WAIT_TIME_US);

	schedule_delayed_work(&chip_info->work_chip_rfsh,
			      msecs_to_jiffies(PAC193X_MAX_RFSH_LIMIT_MS));
}

static int pac193x_read_revision(struct pac193x_chip_info *chip_info, u8 *buf)
{
	int ret;
	struct i2c_client *client = chip_info->client;

	ret = pac193x_i2c_read(client, PAC193X_PID_REG_ADDR, buf, PAC193X_ID_REG_LEN);
	if (ret) {
		dev_err(&client->dev, "cannot read revision\n");
		return ret;
	}

	return 0;
}

static int pac193x_chip_identify(struct pac193x_chip_info *chip_info)
{
	int ret;
	struct i2c_client *client = chip_info->client;
	u8 rev_info[PAC193X_ID_REG_LEN];

	ret = pac193x_read_revision(chip_info, (u8 *)rev_info);
	if (ret) {
		dev_err_probe(&client->dev, ret, "cannot read revision\n");
		return ret;
	}

	if (rev_info[PAC193X_PID_IDX] != pac193x_chip_config[chip_info->chip_variant].prod_id) {
		dev_err_probe(&client->dev, ret,
			      "chip's product ID doesn't match the exact one for this part %d:%d\n",
			      rev_info[PAC193X_PID_IDX],
			      pac193x_chip_config[chip_info->chip_variant].prod_id);
		return -EINVAL;
	}

	dev_dbg(&client->dev, "Chip revision: 0x%02X\n", rev_info[PAC193X_RID_IDX]);
	chip_info->chip_revision = rev_info[PAC193X_RID_IDX];

	return 0;
}

static int pac193x_get_variant(struct pac193x_chip_info *chip_info)
{
	u8 rev_info[PAC193X_ID_REG_LEN];
	int ret;

	ret = pac193x_read_revision(chip_info, (u8 *)rev_info);
	if (!ret) {
		chip_info->chip_variant = rev_info[PAC193X_PID_IDX];
		chip_info->chip_revision = rev_info[PAC193X_RID_IDX];
		switch (chip_info->chip_variant) {
		case PAC1934_PID:
			chip_info->phys_channels = PAC1934_NUM_CHANNELS;
			break;
		case PAC1933_PID:
			chip_info->phys_channels = PAC1933_NUM_CHANNELS;
			break;
		case PAC1932_PID:
			chip_info->phys_channels = PAC1932_NUM_CHANNELS;
			break;
		case PAC1931_PID:
			chip_info->phys_channels = PAC1931_NUM_CHANNELS;
			break;
		default:
			return -EINVAL;
		}
	}

	return 0;
}

/*
 * documentation related to the ACPI device definition
 * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC193X-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf
 */
static union acpi_object *pac193x_acpi_eval_function(acpi_handle handle,
						     int revision,
						     int function)
{
	acpi_status status;
	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
	union acpi_object args[PAC193X_ACPI_ARG_COUNT];
	struct acpi_object_list args_list;
	uuid_t uuid;

	uuid_parse(PAC193X_DSM_UUID, &uuid);

	args[0].type = ACPI_TYPE_BUFFER;
	args[0].buffer.length = sizeof(uuid_t);
	args[0].buffer.pointer = (u8 *)&uuid;

	args[1].type = ACPI_TYPE_INTEGER;
	args[1].integer.value = revision;

	args[2].type = ACPI_TYPE_INTEGER;
	args[2].integer.value = function;

	args[3].type = ACPI_TYPE_PACKAGE;
	args[3].package.count = 0;

	args_list.count = PAC193X_ACPI_ARG_COUNT;
	args_list.pointer = &args[0];

	status = acpi_evaluate_object(handle, "_DSM", &args_list, &buffer);

	if (ACPI_FAILURE(status)) {
		kfree(buffer.pointer);
		return NULL;
	}

	return buffer.pointer;
}

static char *pac193x_acpi_get_acpi_match_entry(acpi_handle handle)
{
	acpi_status status;
	union acpi_object *name_object;
	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};

	status = acpi_evaluate_object(handle, "_HID", NULL, &buffer);
	name_object = buffer.pointer;

	return name_object->string.pointer;
}

static const char *pac193x_match_acpi_device(struct i2c_client *client,
					     struct pac193x_chip_info *chip_info)
{
	char *name;
	acpi_handle handle;
	union acpi_object *rez;
	unsigned short bi_dir_mask;
	int idx, i;

	handle = ACPI_HANDLE(&client->dev);
	name = pac193x_acpi_get_acpi_match_entry(handle);
	if (!name)
		return NULL;

	rez = pac193x_acpi_eval_function(handle, 0, PAC193X_ACPI_GET_NAMES_AND_MOHMS_VALS);

	if (!rez)
		return NULL;

	for (i = 0; i < rez->package.count; i += 2) {
		idx = i / 2;
		chip_info->channel_names[idx] =
			devm_kmemdup(&client->dev, rez->package.elements[i].string.pointer,
				     (size_t)rez->package.elements[i].string.length + 1,
				     GFP_KERNEL);
		chip_info->channel_names[idx][rez->package.elements[i].string.length] = '\0';
		chip_info->shunts[idx] =
			rez->package.elements[i + 1].integer.value * 1000;
		chip_info->active_channels[idx] = (chip_info->shunts[idx] != 0);
	}

	kfree(rez);

	rez = pac193x_acpi_eval_function(handle, 1, PAC193X_ACPI_GET_UOHMS_VALS);
	if (!rez) {
		/*
		 * initializing with default values
		 * we assume all channels are unidirectional(the mask is zero)
		 * and assign the default sampling rate
		 */
		chip_info->sample_rate_value = PAC193X_DEFAULT_CHIP_SAMP_SPEED;
		return name;
	}

	for (i = 0; i < rez->package.count; i++) {
		idx = i;
		chip_info->shunts[idx] = rez->package.elements[i].integer.value;
		chip_info->active_channels[idx] = (chip_info->shunts[idx] != 0);
	}

	kfree(rez);

	rez = pac193x_acpi_eval_function(handle, 1, PAC193X_ACPI_GET_BIPOLAR_SETTINGS);
	if (!rez)
		return NULL;

	bi_dir_mask = rez->package.elements[0].integer.value;
	chip_info->bi_dir[0] = ((bi_dir_mask & (1 << 3)) | (bi_dir_mask & (1 << 7))) != 0;
	chip_info->bi_dir[1] = ((bi_dir_mask & (1 << 2)) | (bi_dir_mask & (1 << 6))) != 0;
	chip_info->bi_dir[2] = ((bi_dir_mask & (1 << 1)) | (bi_dir_mask & (1 << 5))) != 0;
	chip_info->bi_dir[3] = ((bi_dir_mask & (1 << 0)) | (bi_dir_mask & (1 << 4))) != 0;

	kfree(rez);

	rez = pac193x_acpi_eval_function(handle, 1, PAC193X_ACPI_GET_SAMP);
	if (!rez)
		return NULL;

	chip_info->sample_rate_value = rez->package.elements[0].integer.value;
	kfree(rez);

	return name;
}

static const struct of_device_id pac193x_of_match[] = {
	{
		.compatible = "microchip,pac1931",
		.data = &pac193x_chip_config[PAC1931]
	},
	{
		.compatible = "microchip,pac1932",
		.data = &pac193x_chip_config[PAC1932]
	},
	{
		.compatible = "microchip,pac1933",
		.data = &pac193x_chip_config[PAC1933]
	},
	{
		.compatible = "microchip,pac1934",
		.data = &pac193x_chip_config[PAC1934]
	},
	{}
};

static int pac193x_match_of_device(struct i2c_client *client,
				   struct pac193x_chip_info *chip_info)
{
	struct fwnode_handle *node, *fwnode;
	unsigned int current_channel;
	int idx, ret;

	chip_info->sample_rate_value = 1024;
	current_channel = 1;

	fwnode = dev_fwnode(&client->dev);
	fwnode_for_each_available_child_node(fwnode, node) {
		ret = fwnode_property_read_u32(node, "reg", &idx);
		if (ret) {
			dev_err_probe(&client->dev, ret,
				      "reading invalid channel index\n");
			fwnode_handle_put(node);
			return ret;
		}
		/* adjust idx to match channel index (1 to 4) from the datasheet */
		idx--;
		if (current_channel >= (chip_info->phys_channels + 1) ||
		    idx >= chip_info->phys_channels || idx < 0) {
			dev_err_probe(&client->dev, -EINVAL,
				      "%s: invalid channel_index %d value\n",
				      fwnode_get_name(node), idx);
			fwnode_handle_put(node);
			return -EINVAL;
		}

		/* enable channel */
		chip_info->active_channels[idx] = true;

		ret = fwnode_property_read_u32(node, "shunt-resistor-micro-ohms",
					       &chip_info->shunts[idx]);
		if (ret) {
			dev_err_probe(&client->dev, ret,
				      "%s: invalid shunt-resistor value: %d\n",
				      fwnode_get_name(node), chip_info->shunts[idx]);
			fwnode_handle_put(node);
			return ret;
		}

		ret = fwnode_property_read_string(node, "label",
						  (const char **)&chip_info->channel_names[idx]);
		if (ret) {
			dev_err_probe(&client->dev, ret,
				      "%s: invalid rail-name value\n",
				      fwnode_get_name(node));
			fwnode_handle_put(node);
			return ret;
		}

		chip_info->bi_dir[idx] =
			fwnode_property_read_bool(node, "bipolar");

		current_channel++;
	}

	return 0;
}

static int pac193x_chip_configure(struct pac193x_chip_info *chip_info)
{
	int cnt, ret;
	struct i2c_client *client = chip_info->client;
	u8 regs[PAC193X_CTRL_STATUS_INFO_LEN], idx, ctrl_reg;
	u32 wait_time;

	cnt = 0;
	chip_info->chip_reg_data.num_enabled_channels = 0;
	while (cnt < chip_info->phys_channels) {
		if (chip_info->active_channels[cnt])
			chip_info->chip_reg_data.num_enabled_channels++;
		cnt++;
	}

	/*
	 * read whatever information was gathered before the driver was loaded
	 * establish which channels are enabled/disabled and then establish the
	 * information retrieval mode (using SKIP or no).
	 * Read the chip ID values
	 */
	ret = pac193x_i2c_read(client, PAC193X_CTRL_STAT_REGS_ADDR,
			       (u8 *)regs, sizeof(regs));
	if (ret) {
		dev_err_probe(&client->dev, ret,
			      "%s - cannot read regs from 0x%02X\n",
			      __func__, PAC193X_CTRL_STAT_REGS_ADDR);
		return ret;
	}

	/* write the CHANNEL_DIS and the NEG_PWR registers */
	regs[PAC193X_CHANNEL_DIS_REG_OFF] =
		PAC193X_CHAN_DIS_CH1_OFF(chip_info->active_channels[PAC193X_CH_1]) |
		PAC193X_CHAN_DIS_CH2_OFF(chip_info->active_channels[PAC193X_CH_2]) |
		PAC193X_CHAN_DIS_CH3_OFF(chip_info->active_channels[PAC193X_CH_3]) |
		PAC193X_CHAN_DIS_CH4_OFF(chip_info->active_channels[PAC193X_CH_4]) |
		PAC193X_SMBUS_TIMEOUT_EN(0) |
		PAC193X_SMBUS_BYTECOUNT_EN(0) |
		PAC193X_SMBUS_NO_SKIP_EN(0);

	regs[PAC193X_NEG_PWR_REG_OFF] =
		PAC193X_NEG_PWR_CH1_BIDI(chip_info->bi_dir[PAC193X_CH_1]) |
		PAC193X_NEG_PWR_CH2_BIDI(chip_info->bi_dir[PAC193X_CH_2]) |
		PAC193X_NEG_PWR_CH3_BIDI(chip_info->bi_dir[PAC193X_CH_3]) |
		PAC193X_NEG_PWR_CH4_BIDI(chip_info->bi_dir[PAC193X_CH_4]) |
		PAC193X_NEG_PWR_CH1_BIDV(chip_info->bi_dir[PAC193X_CH_1]) |
		PAC193X_NEG_PWR_CH2_BIDV(chip_info->bi_dir[PAC193X_CH_2]) |
		PAC193X_NEG_PWR_CH3_BIDV(chip_info->bi_dir[PAC193X_CH_3]) |
		PAC193X_NEG_PWR_CH4_BIDV(chip_info->bi_dir[PAC193X_CH_4]);

	/* no SLOW triggered REFRESH, clear POR */
	regs[PAC193X_SLOW_REG_OFF] = 0;

	ret = pac193x_i2c_write(client, PAC193X_CTRL_STAT_REGS_ADDR, (u8 *)regs, 3);
	if (ret)
		return ret;

	ctrl_reg = PAC193X_CRTL_SAMPLE_RATE_SET(chip_info->crt_samp_spd_bitfield);
	ret = i2c_smbus_write_byte_data(client, PAC193X_CTRL_REG_ADDR, ctrl_reg);
	if (ret)
		return ret;

	/*
	 * send a REFRESH to the chip, so the new settings take place
	 * as well as resetting the accumulators
	 */
	ret = i2c_smbus_write_byte(client, PAC193X_REFRESH_REG_ADDR);
	if (ret) {
		dev_err(&client->dev,
			"%s - cannot send 0x%02X\n",
			__func__, PAC193X_REFRESH_REG_ADDR);
		return ret;
	}

	/*
	 * get the current(in the chip) sampling speed and compute the
	 * required timeout based on its value
	 * the timeout is 1/sampling_speed
	 */
	idx = regs[PAC193X_CTRL_ACT_REG_OFF] >> PAC193X_SAMPLE_RATE_SHIFT;
	wait_time = (1024 / samp_rate_map_tbl[idx]) * 1000;

	/*
	 * wait the maximum amount of time to be on the safe side
	 * the maximum wait time is for 8sps
	 */
	usleep_range(wait_time, wait_time + 100);

	INIT_DELAYED_WORK(&chip_info->work_chip_rfsh, pac193x_work_periodic_rfsh);
	/* Setup the latest moment for reading the regs before saturation */
	schedule_delayed_work(&chip_info->work_chip_rfsh,
			      msecs_to_jiffies(PAC193X_MAX_RFSH_LIMIT_MS));

	return 0;
}

static int pac193x_prep_iio_channels(struct pac193x_chip_info *chip_info, struct iio_dev *indio_dev)
{
	struct i2c_client *client;
	struct iio_chan_spec *ch_sp;
	int channel_size, attribute_count, cnt;
	void *dyn_ch_struct, *tmp_data;

	client = chip_info->client;

	/* find out dynamically how many IIO channels we need */
	attribute_count = 0;
	channel_size = 0;
	for (cnt = 0; cnt < chip_info->phys_channels; cnt++) {
		if (chip_info->active_channels[cnt]) {
			/* add the size of the properties of one chip physical channel */
			channel_size += sizeof(pac193x_single_channel);
			/* count how many enabled channels we have */
			attribute_count += ARRAY_SIZE(pac193x_single_channel);
			dev_info(&client->dev, ":%s: Channel %d active\n", __func__, cnt + 1);
		}
	}

	dyn_ch_struct = kzalloc(channel_size, GFP_KERNEL);
	if (!dyn_ch_struct)
		return -EINVAL;

	tmp_data = dyn_ch_struct;

	/* populate the dynamic channels and make all the adjustments */
	for (cnt = 0; cnt < chip_info->phys_channels; cnt++) {
		if (chip_info->active_channels[cnt]) {
			memcpy(tmp_data, pac193x_single_channel, sizeof(pac193x_single_channel));
			ch_sp = (struct iio_chan_spec *)tmp_data;
			ch_sp[IIO_EN].channel = cnt + 1;
			ch_sp[IIO_EN].scan_index = cnt;
			ch_sp[IIO_EN].address = cnt + PAC193X_VPOWER_ACC_1_ADDR;
			ch_sp[IIO_POW].channel = cnt + 1;
			ch_sp[IIO_POW].scan_index = cnt;
			ch_sp[IIO_POW].address = cnt + PAC193X_VPOWER_1_ADDR;
			ch_sp[IIO_VOLT].channel = cnt + 1;
			ch_sp[IIO_VOLT].scan_index = cnt;
			ch_sp[IIO_VOLT].address = cnt + PAC193X_VBUS_1_ADDR;
			ch_sp[IIO_CRT].channel = cnt + 1;
			ch_sp[IIO_CRT].scan_index = cnt;
			ch_sp[IIO_CRT].address = cnt + PAC193X_VSENSE_1_ADDR;
			ch_sp[IIO_VOLTAVG].channel = cnt + 1;
			ch_sp[IIO_VOLTAVG].scan_index = cnt;
			ch_sp[IIO_VOLTAVG].address = cnt + PAC193X_VBUS_AVG_1_ADDR;
			ch_sp[IIO_CRTAVG].channel = cnt + 1;
			ch_sp[IIO_CRTAVG].scan_index = cnt;
			ch_sp[IIO_CRTAVG].address = cnt + PAC193X_VSENSE_AVG_1_ADDR;

			/*
			 * now modify the parameters in all channels if the
			 * whole chip rail(channel) is bi-directional
			 */
			if (chip_info->bi_dir[cnt]) {
				ch_sp[IIO_EN].scan_type.sign = 's';
				ch_sp[IIO_EN].scan_type.realbits = PAC193X_ENERGY_S_RES;
				ch_sp[IIO_POW].scan_type.sign = 's';
				ch_sp[IIO_POW].scan_type.realbits = PAC193X_POWER_S_RES;
				ch_sp[IIO_VOLT].scan_type.sign = 's';
				ch_sp[IIO_VOLT].scan_type.realbits = PAC193X_VOLTAGE_S_RES;
				ch_sp[IIO_CRT].scan_type.sign = 's';
				ch_sp[IIO_CRT].scan_type.realbits = PAC193X_CURRENT_S_RES;
				ch_sp[IIO_VOLTAVG].scan_type.sign = 's';
				ch_sp[IIO_VOLTAVG].scan_type.realbits = PAC193X_VOLTAGE_S_RES;
				ch_sp[IIO_CRTAVG].scan_type.sign = 's';
				ch_sp[IIO_CRTAVG].scan_type.realbits = PAC193X_CURRENT_S_RES;
			}
			tmp_data += sizeof(pac193x_single_channel);
		}
	}

	/*
	 * send the updated dynamic channel structure information towards IIO
	 * prepare the required field for IIO class registration
	 */
	indio_dev->num_channels = attribute_count;
	indio_dev->channels = devm_kmemdup(&client->dev,
					   (const struct iio_chan_spec *)dyn_ch_struct,
					   channel_size, GFP_KERNEL);

	kfree(dyn_ch_struct);

	if (!indio_dev->channels)
		return -EINVAL;

	return 0;
}

static ssize_t reset_accumulators_store(struct device *dev,
					struct device_attribute *attr,
					const char *buf, size_t count)
{
	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
	struct pac193x_chip_info *chip_info = iio_priv(indio_dev);
	int ret, i;
	u8 refresh_cmd = PAC193X_REFRESH_REG_ADDR;

	ret = i2c_smbus_write_byte(chip_info->client, refresh_cmd);
	if (ret) {
		dev_err(&indio_dev->dev,
			"%s - cannot send 0x%02X\n",
			__func__, refresh_cmd);
	}

	for (i = 0 ; i < chip_info->phys_channels; i++)
		chip_info->chip_reg_data.energy_sec_acc[i] = 0;

	return count;
}

static IIO_DEVICE_ATTR(in_shunt_resistor_1, 0644, shunt_value_show, shunt_value_store, 0);
static IIO_DEVICE_ATTR(in_shunt_resistor_2, 0644, shunt_value_show, shunt_value_store, 0);
static IIO_DEVICE_ATTR(in_shunt_resistor_3, 0644, shunt_value_show, shunt_value_store, 0);
static IIO_DEVICE_ATTR(in_shunt_resistor_4, 0644, shunt_value_show, shunt_value_store, 0);

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

static IIO_DEVICE_ATTR(reset_accumulators, 0200, NULL, reset_accumulators_store, 0);

static struct attribute *pac193x_all_attributes[] = {
	PAC193X_DEV_ATTR(in_shunt_resistor_1),
	PAC193X_DEV_ATTR(channel_name_1),
	PAC193X_DEV_ATTR(in_shunt_resistor_2),
	PAC193X_DEV_ATTR(channel_name_2),
	PAC193X_DEV_ATTR(in_shunt_resistor_3),
	PAC193X_DEV_ATTR(channel_name_3),
	PAC193X_DEV_ATTR(in_shunt_resistor_4),
	PAC193X_DEV_ATTR(channel_name_4),
	PAC193X_DEV_ATTR(reset_accumulators),
	NULL
};

static int pac193x_prep_custom_attributes(struct pac193x_chip_info *chip_info,
					  struct iio_dev *indio_dev)
{
	int i, j, active_channels_count = 0;
	struct attribute **pac193x_custom_attributes;
	struct attribute_group *pac193x_group;
	struct i2c_client *client = chip_info->client;

	for (i = 0 ; i < chip_info->phys_channels; i++)
		if (chip_info->active_channels[i])
			active_channels_count++;

	pac193x_group = devm_kzalloc(&client->dev, sizeof(*pac193x_group), GFP_KERNEL);

	pac193x_custom_attributes = devm_kzalloc(&client->dev,
						 (PAC193X_CUSTOM_ATTR_FOR_CHANNEL *
						 active_channels_count +
						 PAC193X_SHARED_DEVATTRS_COUNT)
						 * sizeof(*pac193x_group) + 1,
						 GFP_KERNEL);
	j = 0;

	for (i = 0 ; i < chip_info->phys_channels; i++) {
		if (chip_info->active_channels[i]) {
			pac193x_custom_attributes[PAC193X_CUSTOM_ATTR_FOR_CHANNEL * j] =
			pac193x_all_attributes[PAC193X_CUSTOM_ATTR_FOR_CHANNEL * i];
			pac193x_custom_attributes[PAC193X_CUSTOM_ATTR_FOR_CHANNEL * j + 1] =
			pac193x_all_attributes[PAC193X_CUSTOM_ATTR_FOR_CHANNEL * i + 1];
			j++;
		}
	}

	for (i = 0; i < PAC193X_SHARED_DEVATTRS_COUNT; i++)
		pac193x_custom_attributes[PAC193X_CUSTOM_ATTR_FOR_CHANNEL *
			active_channels_count + i] =
			pac193x_all_attributes[PAC193X_CUSTOM_ATTR_FOR_CHANNEL *
			chip_info->phys_channels + i];

	pac193x_group->attrs = pac193x_custom_attributes;
	chip_info->pac193x_info.attrs = pac193x_group;

	return 0;
}

static void pac193x_remove(struct i2c_client *client)
{
	struct iio_dev *indio_dev = dev_get_drvdata(&client->dev);
	struct pac193x_chip_info *chip_info = iio_priv(indio_dev);

	cancel_delayed_work_sync(&chip_info->work_chip_rfsh);
}

static const struct i2c_device_id pac193x_id[] = {
	{ "pac1931", PAC1931 },
	{ "pac1932", PAC1932 },
	{ "pac1933", PAC1933 },
	{ "pac1934", PAC1934 },
	{}
};

static int pac193x_probe(struct i2c_client *client)
{
	struct pac193x_chip_info *chip_info;
	struct iio_dev *indio_dev;
	const char *name = NULL;
	const struct of_device_id *match;
	int cnt, ret, dev_id = 0;

	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip_info));
	if (!indio_dev) {
		dev_err_probe(&client->dev, PTR_ERR(indio_dev),
			      "Can't allocate iio device\n");
		return -ENOMEM;
	}

	chip_info = iio_priv(indio_dev);

	i2c_set_clientdata(client, indio_dev);
	chip_info->client = client;

	/*
	 * load default settings - all channels disabled,
	 * uni directional flow
	 */
	for (cnt = 0; cnt < PAC193X_MAX_NUM_CHANNELS; cnt++) {
		chip_info->active_channels[cnt] = false;
		chip_info->bi_dir[cnt] = false;
	}

	chip_info->crt_samp_spd_bitfield = PAC193X_SAMP_1024SPS;

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

		switch (chip_info->chip_variant) {
		case PAC1934_PID:
			client->dev.init_name = "pac1934";
			break;
		case PAC1933_PID:
			client->dev.init_name = "pac1933";
			break;
		case PAC1932_PID:
			client->dev.init_name = "pac1932";
			break;
		case PAC1931_PID:
			client->dev.init_name = "pac1931";
			break;
		default:
			return -EINVAL;
		}

		for (cnt = 0; cnt < PAC193X_MAX_NUM_CHANNELS; cnt++)
			chip_info->shunts[cnt] = SHUNT_UOHMS_DEFAULT;

		name = pac193x_match_acpi_device(client, chip_info);
	} else {
		dev_id = i2c_match_id(pac193x_id, client)->driver_data;

		chip_info->chip_variant = dev_id;
		chip_info->phys_channels = pac193x_chip_config[dev_id].phys_channels;

		/* identify the chip we have to deal with */
		ret = pac193x_chip_identify(chip_info);
		if (ret)
			return -EINVAL;

		/* check if we find the device within DT */
		if (!client->dev.of_node || (!of_get_next_child(client->dev.of_node, NULL)))
			return -EINVAL;

		match = of_match_node(pac193x_of_match, client->dev.of_node);
		if (match) {
			ret = pac193x_match_of_device(client, chip_info);
			if (!ret)
				name = match->compatible;
		}
	}

	if (!name) {
		dev_err_probe(&client->dev, PTR_ERR(indio_dev),
			      "parameter parsing returned an error\n");
		return -EINVAL;
	}

	mutex_init(&chip_info->lock);

	/*
	 * do now any chip specific initialization (e.g. read/write
	 * some registers), enable/disable certain channels, change the sampling
	 * rate to the requested value
	 */
	ret = pac193x_chip_configure(chip_info);
	if (ret < 0)
		goto fail;

	/* prepare the channel information */
	ret = pac193x_prep_iio_channels(chip_info, indio_dev);
	if (ret < 0)
		goto fail;

	ret = pac193x_prep_custom_attributes(chip_info, indio_dev);
	if (ret < 0) {
		dev_err_probe(&indio_dev->dev, ret,
			      "Can't configure custom attributes for PAC193X device\n");
		goto fail;
	}

	chip_info->pac193x_info.read_raw = pac193x_read_raw;
	chip_info->pac193x_info.read_avail = pac193x_read_avail;
	chip_info->pac193x_info.write_raw = pac193x_write_raw;

	indio_dev->info = &chip_info->pac193x_info;
	indio_dev->name = name;
	indio_dev->modes = INDIO_DIRECT_MODE;

	/*
	 * read whatever has been accumulated in the chip so far
	 * and reset the accumulators
	 */
	ret = pac193x_reg_snapshot(chip_info, true, PAC193X_REFRESH_REG_ADDR,
				   PAC193X_MIN_UPDATE_WAIT_TIME_US);
	if (ret < 0)
		goto fail;

	ret = devm_iio_device_register(&client->dev, indio_dev);
	if (ret < 0) {
		dev_err_probe(&indio_dev->dev, ret,
			      "Can't register IIO device\n");
		goto fail;
	}

	return 0;

fail:
	cancel_delayed_work_sync(&chip_info->work_chip_rfsh);

	return ret;
}

MODULE_DEVICE_TABLE(i2c, pac193x_id);

MODULE_DEVICE_TABLE(of, pac193x_of_match);

static const struct acpi_device_id pac193x_acpi_match[] = {
	{"MCHP1930", 0},
	{ }
};
MODULE_DEVICE_TABLE(acpi, pac193x_acpi_match);

static struct i2c_driver pac193x_driver = {
	.driver	 = {
		.name = "pac193x",
		.of_match_table = pac193x_of_match,
		.acpi_match_table = ACPI_PTR(pac193x_acpi_match)
		},
	.probe = pac193x_probe,
	.remove = pac193x_remove,
	.id_table = pac193x_id,
};

module_i2c_driver(pac193x_driver);

MODULE_AUTHOR("Bogdan Bolocan <bogdan.bolocan@microchip.com>");
MODULE_AUTHOR("Victor Tudose");
MODULE_AUTHOR("Marius Cristea <marius.cristea@microchip.com>");
MODULE_DESCRIPTION("IIO driver for PAC193X Multi-Channel DC Power/Energy Monitor");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.2.1");

四、应用测试

下载的软件包中有 py 测试代码如下:

import time
import os

pacs= [1]

channels ={
       0: [ 1, 2, 3, 4],
       1: [ 1, 2, 3, 4],
}

def print_label(file_name):
    global ABS_PATH

    if os.path.exists(ABS_PATH+file_name):
        file1 = open(ABS_PATH+file_name,'r')
        string = file1.read()
        file1.close()

        return string.strip()
    else:
        return "-"

    return val1 * val2



def mult(file1_name,file2_name):
    global ABS_PATH

    if os.path.exists(ABS_PATH+file1_name):
        file1 = open(ABS_PATH+file1_name,'r')
        string = file1.read()
        file1.close()

        if bool(string.strip()):
            val1 = float(string)
        else:
            val1 = 0
    else:
        return "-"

    if os.path.exists(ABS_PATH+file2_name):
        file2 = open(ABS_PATH+file2_name,'r')
        string = file2.read()
        file2.close()

        if bool(string.strip()):
            val2 = float(string)
        else:
            val2 = 0

    else:
        return "-"

    return val1 * val2


for pac in pacs:

    ABS_PATH = f'/sys/bus/iio/devices/iio:device{pac}/'
    print('--------------------------------')
    print(f'Pac{pac}')
    print('--------------------------------')
    print('I SENSE')
    for channel in channels[pac]:
        print(f'CH{channel}: -> {mult(f"in_current{channel}_raw",f"in_current{channel}_scale")}')

    print('V BUS')
    for channel in channels[pac]:
        print(f'CH{channel}: -> {mult(f"in_voltage{channel}_mean_raw",f"in_voltage{channel}_scale")}')

    print('Powers')
    for channel in channels[pac]:
        print(f'CH{channel}: -> {mult(f"in_power{channel}_raw",f"in_power{channel}_scale")}')

    print('Energies')
    for channel in channels[pac]:
        val1 = mult(f"in_energy{channel}_raw",f"in_energy{channel}_scale")
        print(f'CH{channel}: -> {val1}')

    print('Label')
    for channel in channels[pac]:
        label = print_label(f"channel_name_{channel}")
        print(f'CH{channel}: -> {label}')

让 ai 写了一个 c 版本如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#define MAX_PATH 256
#define MAX_LINE 128
#define MAX_CHANNELS 4

// 定义通道配置
const int channels[] = {1, 2, 3, 4};
const int num_channels = 4;

// 用于存储文件内容的结构
typedef struct {
    char value[MAX_LINE];
    bool exists;
} FileContent;

// 读取文件内容
FileContent read_file(const char* base_path, const char* file_name) {
    FileContent content = {.exists = false};
    char full_path[MAX_PATH];
    snprintf(full_path, sizeof(full_path), "%s%s", base_path, file_name);
    
    FILE* file = fopen(full_path, "r");
    if (file != NULL) {
        if (fgets(content.value, sizeof(content.value), file) != NULL) {
            // 移除换行符
            content.value[strcspn(content.value, "\n")] = 0;
            content.exists = true;
        }
        fclose(file);
    }
    return content;
}

// 读取并打印标签
void print_label(const char* base_path, int channel) {
    char file_name[MAX_PATH];
    snprintf(file_name, sizeof(file_name), "channel_name_%d", channel);
    
    FileContent content = read_file(base_path, file_name);
    printf("CH%d: -> %s\n", channel, content.exists ? content.value : "-");
}

// 读取并计算两个文件的乘积
void print_mult(const char* base_path, const char* prefix, int channel, const char* suffix1, const char* suffix2) {
    char file1[MAX_PATH], file2[MAX_PATH];
    snprintf(file1, sizeof(file1), "%s%d%s", prefix, channel, suffix1);
    snprintf(file2, sizeof(file2), "%s%d%s", prefix, channel, suffix2);
    
    FileContent content1 = read_file(base_path, file1);
    FileContent content2 = read_file(base_path, file2);
    
    if (content1.exists && content2.exists) {
        float val1 = atof(content1.value);
        float val2 = atof(content2.value);
        printf("CH%d: -> %f\n", channel, val1 * val2);
    } else {
        printf("CH%d: -> -\n", channel);
    }
}

int main() {
    const char* base_path = "/sys/bus/iio/devices/iio:device1/";
    
    printf("--------------------------------\n");
    printf("Pac1\n");
    printf("--------------------------------\n");
    
    // 打印电流信息
    printf("I SENSE\n");
    for (int i = 0; i < num_channels; i++) {
        print_mult(base_path, "in_current", channels[i], "_raw", "_scale");
    }
    
    // 打印电压信息
    printf("\nV BUS\n");
    for (int i = 0; i < num_channels; i++) {
        print_mult(base_path, "in_voltage", channels[i], "_mean_raw", "_scale");
    }
    
    // 打印功率信息
    printf("\nPowers\n");
    for (int i = 0; i < num_channels; i++) {
        print_mult(base_path, "in_power", channels[i], "_raw", "_scale");
    }
    
    // 打印能量信息
    printf("\nEnergies\n");
    for (int i = 0; i < num_channels; i++) {
        print_mult(base_path, "in_energy", channels[i], "_raw", "_scale");
    }
    
    // 打印标签信息
    printf("\nLabel\n");
    for (int i = 0; i < num_channels; i++) {
        print_label(base_path, channels[i]);
    }
    
    return 0;
}

运行结果如下:

第一个是电流值,单位 A;第二个是电压值,单位 mV;第三个是功率,单位 mW;第四个是累计功耗,单位没关注,可能是 mWh

--------------------------------
Pac1
--------------------------------
I SENSE
CH1: -> 0.016745
CH2: -> 0.029951
CH3: -> 0.249490
CH4: -> 0.771040

V BUS
CH1: -> 1099.121094
CH2: -> 597.656250
CH3: -> 3306.152344
CH4: -> 900.878906

Powers
CH1: -> 18.259396
CH2: -> 17.793762
CH3: -> 824.966553
CH4: -> 693.134644

Energies
CH1: -> 22624.001953
CH2: -> 4994.036133
CH3: -> 661066.312500
CH4: -> 460701.906250

Label
CH1: -> VDD2
CH2: -> VDDQ
CH3: -> VDD3P3
CH4: -> SOC


http://www.kler.cn/a/515555.html

相关文章:

  • 开发环境搭建-1:配置 WSL (类 centos 的 oracle linux 官方镜像)
  • HOW - 查看分支创建时间(含 git reflog 和 git log 区别)
  • IP协议格式
  • 靶机复现-pikachu靶机文件包含漏洞
  • Qt —— 控件属性
  • 音频入门(二):音频数据增强
  • AutoPrompt框架和实操:如何用AutoPrompt完成电影评论和聊天审核任务?
  • python内置的调试工具-pdb
  • 解决SpringBoot项目启动错误:找不到或无法加载主类
  • 一文大白话讲清楚webpack基本使用——11——chunkIds和runtimeChunk
  • 【玩转全栈】----基于ModelForm完成用户管理页面
  • 作品显示ip属地与定位哪个是真实的
  • 解决因JDK升级导致的`java.nio.file.NoSuchFileException`问题
  • 【K8S问题系列 |19 】如何解决 Pod 无法挂载 PVC问题
  • Python并发编程 07 事件驱动模型、进程切换、进程阻塞、文件描述符、缓存I/O、selectors模块
  • Vue3+Element Plus 实现 el-table 表格组件滚动是否触底监听判断
  • 父级perspective与子元素transform:perspective的区别
  • 在vue3中使用datav完整引入时卡在加载页面的解决方法
  • 【10.2】队列-设计循环队列
  • FFmpeg音视频采集
  • 数据结构——实验二·栈
  • 2025美赛倒计时,数学建模五类模型40+常用算法及算法手册汇总
  • 【2024年华为OD机试】 (E卷,100分) - 整数编码(JavaScriptJava PythonC/C++)
  • 4.C++中的循环语句
  • 【Mac】Python相关知识经验
  • 什么是网络爬虫?Python爬虫到底怎么学?