5f60d5f6bb
asm/unaligned.h is always an include of asm-generic/unaligned.h; might as well move that thing to linux/unaligned.h and include that - there's nothing arch-specific in that header. auto-generated by the following: for i in `git grep -l -w asm/unaligned.h`; do sed -i -e "s/asm\/unaligned.h/linux\/unaligned.h/" $i done for i in `git grep -l -w asm-generic/unaligned.h`; do sed -i -e "s/asm-generic\/unaligned.h/linux\/unaligned.h/" $i done git mv include/asm-generic/unaligned.h include/linux/unaligned.h git mv tools/include/asm-generic/unaligned.h tools/include/linux/unaligned.h sed -i -e "/unaligned.h/d" include/asm-generic/Kbuild sed -i -e "s/__ASM_GENERIC/__LINUX/" include/linux/unaligned.h tools/include/linux/unaligned.h
1626 lines
47 KiB
C
1626 lines
47 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* IIO driver for PAC1934 Multi-Channel DC Power/Energy Monitor
|
|
*
|
|
* Copyright (C) 2017-2024 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/device.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/iio/iio.h>
|
|
#include <linux/iio/sysfs.h>
|
|
#include <linux/unaligned.h>
|
|
|
|
/*
|
|
* maximum accumulation time should be (17 * 60 * 1000) around 17 minutes@1024 sps
|
|
* till PAC1934 accumulation registers starts to saturate
|
|
*/
|
|
#define PAC1934_MAX_RFSH_LIMIT_MS 60000
|
|
/* 50msec is the timeout for validity of the cached registers */
|
|
#define PAC1934_MIN_POLLING_TIME_MS 50
|
|
/*
|
|
* 1000usec is the minimum wait time for normal conversions when sample
|
|
* rate doesn't change
|
|
*/
|
|
#define PAC1934_MIN_UPDATE_WAIT_TIME_US 1000
|
|
|
|
/* 32000mV */
|
|
#define PAC1934_VOLTAGE_MILLIVOLTS_MAX 32000
|
|
/* voltage bits resolution when set for unsigned values */
|
|
#define PAC1934_VOLTAGE_U_RES 16
|
|
/* voltage bits resolution when set for signed values */
|
|
#define PAC1934_VOLTAGE_S_RES 15
|
|
|
|
/*
|
|
* 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
|
|
|
|
#define PAC1934_MAX_NUM_CHANNELS 4
|
|
|
|
#define PAC1934_MEAS_REG_LEN 76
|
|
#define PAC1934_CTRL_REG_LEN 12
|
|
|
|
#define PAC1934_DEFAULT_CHIP_SAMP_SPEED_HZ 1024
|
|
|
|
/* I2C address map */
|
|
#define PAC1934_REFRESH_REG_ADDR 0x00
|
|
#define PAC1934_CTRL_REG_ADDR 0x01
|
|
#define PAC1934_ACC_COUNT_REG_ADDR 0x02
|
|
#define PAC1934_VPOWER_ACC_1_ADDR 0x03
|
|
#define PAC1934_VPOWER_ACC_2_ADDR 0x04
|
|
#define PAC1934_VPOWER_ACC_3_ADDR 0x05
|
|
#define PAC1934_VPOWER_ACC_4_ADDR 0x06
|
|
#define PAC1934_VBUS_1_ADDR 0x07
|
|
#define PAC1934_VBUS_2_ADDR 0x08
|
|
#define PAC1934_VBUS_3_ADDR 0x09
|
|
#define PAC1934_VBUS_4_ADDR 0x0A
|
|
#define PAC1934_VSENSE_1_ADDR 0x0B
|
|
#define PAC1934_VSENSE_2_ADDR 0x0C
|
|
#define PAC1934_VSENSE_3_ADDR 0x0D
|
|
#define PAC1934_VSENSE_4_ADDR 0x0E
|
|
#define PAC1934_VBUS_AVG_1_ADDR 0x0F
|
|
#define PAC1934_VBUS_AVG_2_ADDR 0x10
|
|
#define PAC1934_VBUS_AVG_3_ADDR 0x11
|
|
#define PAC1934_VBUS_AVG_4_ADDR 0x12
|
|
#define PAC1934_VSENSE_AVG_1_ADDR 0x13
|
|
#define PAC1934_VSENSE_AVG_2_ADDR 0x14
|
|
#define PAC1934_VSENSE_AVG_3_ADDR 0x15
|
|
#define PAC1934_VSENSE_AVG_4_ADDR 0x16
|
|
#define PAC1934_VPOWER_1_ADDR 0x17
|
|
#define PAC1934_VPOWER_2_ADDR 0x18
|
|
#define PAC1934_VPOWER_3_ADDR 0x19
|
|
#define PAC1934_VPOWER_4_ADDR 0x1A
|
|
#define PAC1934_REFRESH_V_REG_ADDR 0x1F
|
|
#define PAC1934_CTRL_STAT_REGS_ADDR 0x1C
|
|
#define PAC1934_PID_REG_ADDR 0xFD
|
|
#define PAC1934_MID_REG_ADDR 0xFE
|
|
#define PAC1934_RID_REG_ADDR 0xFF
|
|
|
|
/* PRODUCT ID REGISTER + MANUFACTURER ID REGISTER + REVISION ID REGISTER */
|
|
#define PAC1934_ID_REG_LEN 3
|
|
#define PAC1934_PID_IDX 0
|
|
#define PAC1934_MID_IDX 1
|
|
#define PAC1934_RID_IDX 2
|
|
|
|
#define PAC1934_ACPI_GET_NAMES_AND_MOHMS_VALS 1
|
|
#define PAC1934_ACPI_GET_UOHMS_VALS 2
|
|
#define PAC1934_ACPI_GET_BIPOLAR_SETTINGS 4
|
|
#define PAC1934_ACPI_GET_SAMP 5
|
|
|
|
#define PAC1934_SAMPLE_RATE_SHIFT 6
|
|
|
|
#define PAC1934_VBUS_SENSE_REG_LEN 2
|
|
#define PAC1934_ACC_REG_LEN 3
|
|
#define PAC1934_VPOWER_REG_LEN 4
|
|
#define PAC1934_VPOWER_ACC_REG_LEN 6
|
|
#define PAC1934_MAX_REGISTER_LENGTH 6
|
|
|
|
#define PAC1934_CUSTOM_ATTR_FOR_CHANNEL 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 PAC1934_CHANNEL_DIS_REG_OFF 0
|
|
#define PAC1934_NEG_PWR_REG_OFF 1
|
|
|
|
/*
|
|
* when reading/writing multiple bytes from offset PAC1934_CHANNEL_DIS_REG_OFF,
|
|
* the chip jumps over the 0x1E (REFRESH_G) and 0x1F (REFRESH_V) offsets
|
|
*/
|
|
#define PAC1934_SLOW_REG_OFF 2
|
|
#define PAC1934_CTRL_ACT_REG_OFF 3
|
|
#define PAC1934_CHANNEL_DIS_ACT_REG_OFF 4
|
|
#define PAC1934_NEG_PWR_ACT_REG_OFF 5
|
|
#define PAC1934_CTRL_LAT_REG_OFF 6
|
|
#define PAC1934_CHANNEL_DIS_LAT_REG_OFF 7
|
|
#define PAC1934_NEG_PWR_LAT_REG_OFF 8
|
|
#define PAC1934_PID_REG_OFF 9
|
|
#define PAC1934_MID_REG_OFF 10
|
|
#define PAC1934_REV_REG_OFF 11
|
|
#define PAC1934_CTRL_STATUS_INFO_LEN 12
|
|
|
|
#define PAC1934_MID 0x5D
|
|
#define PAC1931_PID 0x58
|
|
#define PAC1932_PID 0x59
|
|
#define PAC1933_PID 0x5A
|
|
#define PAC1934_PID 0x5B
|
|
|
|
/* Scale constant = (10^3 * 3.2 * 10^9 / 2^28) for mili Watt-second */
|
|
#define PAC1934_SCALE_CONSTANT 11921
|
|
|
|
#define PAC1934_MAX_VPOWER_RSHIFTED_BY_28B 11921
|
|
#define PAC1934_MAX_VSENSE_RSHIFTED_BY_16B 1525
|
|
|
|
#define PAC1934_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
|
|
|
|
#define PAC1934_CRTL_SAMPLE_RATE_MASK GENMASK(7, 6)
|
|
#define PAC1934_CHAN_SLEEP_MASK BIT(5)
|
|
#define PAC1934_CHAN_SLEEP_SET BIT(5)
|
|
#define PAC1934_CHAN_SINGLE_MASK BIT(4)
|
|
#define PAC1934_CHAN_SINGLE_SHOT_SET BIT(4)
|
|
#define PAC1934_CHAN_ALERT_MASK BIT(3)
|
|
#define PAC1934_CHAN_ALERT_EN BIT(3)
|
|
#define PAC1934_CHAN_ALERT_CC_MASK BIT(2)
|
|
#define PAC1934_CHAN_ALERT_CC_EN BIT(2)
|
|
#define PAC1934_CHAN_OVF_ALERT_MASK BIT(1)
|
|
#define PAC1934_CHAN_OVF_ALERT_EN BIT(1)
|
|
#define PAC1934_CHAN_OVF_MASK BIT(0)
|
|
|
|
#define PAC1934_CHAN_DIS_CH1_OFF_MASK BIT(7)
|
|
#define PAC1934_CHAN_DIS_CH2_OFF_MASK BIT(6)
|
|
#define PAC1934_CHAN_DIS_CH3_OFF_MASK BIT(5)
|
|
#define PAC1934_CHAN_DIS_CH4_OFF_MASK BIT(4)
|
|
#define PAC1934_SMBUS_TIMEOUT_MASK BIT(3)
|
|
#define PAC1934_SMBUS_BYTECOUNT_MASK BIT(2)
|
|
#define PAC1934_SMBUS_NO_SKIP_MASK BIT(1)
|
|
|
|
#define PAC1934_NEG_PWR_CH1_BIDI_MASK BIT(7)
|
|
#define PAC1934_NEG_PWR_CH2_BIDI_MASK BIT(6)
|
|
#define PAC1934_NEG_PWR_CH3_BIDI_MASK BIT(5)
|
|
#define PAC1934_NEG_PWR_CH4_BIDI_MASK BIT(4)
|
|
#define PAC1934_NEG_PWR_CH1_BIDV_MASK BIT(3)
|
|
#define PAC1934_NEG_PWR_CH2_BIDV_MASK BIT(2)
|
|
#define PAC1934_NEG_PWR_CH3_BIDV_MASK BIT(1)
|
|
#define PAC1934_NEG_PWR_CH4_BIDV_MASK BIT(0)
|
|
|
|
/*
|
|
* Universal Unique Identifier (UUID),
|
|
* 033771E0-1705-47B4-9535-D1BBE14D9A09,
|
|
* is reserved to Microchip for the PAC1934.
|
|
*/
|
|
#define PAC1934_DSM_UUID "033771E0-1705-47B4-9535-D1BBE14D9A09"
|
|
|
|
enum pac1934_ids {
|
|
PAC1931,
|
|
PAC1932,
|
|
PAC1933,
|
|
PAC1934
|
|
};
|
|
|
|
enum pac1934_samps {
|
|
PAC1934_SAMP_1024SPS,
|
|
PAC1934_SAMP_256SPS,
|
|
PAC1934_SAMP_64SPS,
|
|
PAC1934_SAMP_8SPS
|
|
};
|
|
|
|
/*
|
|
* these indexes are exactly describing the element order within a single
|
|
* PAC1934 phys channel IIO channel descriptor; see the static const struct
|
|
* iio_chan_spec pac1934_single_channel[] declaration
|
|
*/
|
|
enum pac1934_ch_idx {
|
|
PAC1934_CH_ENERGY,
|
|
PAC1934_CH_POWER,
|
|
PAC1934_CH_VOLTAGE,
|
|
PAC1934_CH_CURRENT,
|
|
PAC1934_CH_VOLTAGE_AVERAGE,
|
|
PAC1934_CH_CURRENT_AVERAGE
|
|
};
|
|
|
|
/**
|
|
* struct pac1934_features - features of a pac1934 instance
|
|
* @phys_channels: number of physical channels supported by the chip
|
|
* @name: chip's name
|
|
*/
|
|
struct pac1934_features {
|
|
u8 phys_channels;
|
|
const char *name;
|
|
};
|
|
|
|
static const unsigned int samp_rate_map_tbl[] = {
|
|
[PAC1934_SAMP_1024SPS] = 1024,
|
|
[PAC1934_SAMP_256SPS] = 256,
|
|
[PAC1934_SAMP_64SPS] = 64,
|
|
[PAC1934_SAMP_8SPS] = 8,
|
|
};
|
|
|
|
static const struct pac1934_features pac1934_chip_config[] = {
|
|
[PAC1931] = {
|
|
.phys_channels = 1,
|
|
.name = "pac1931",
|
|
},
|
|
[PAC1932] = {
|
|
.phys_channels = 2,
|
|
.name = "pac1932",
|
|
},
|
|
[PAC1933] = {
|
|
.phys_channels = 3,
|
|
.name = "pac1933",
|
|
},
|
|
[PAC1934] = {
|
|
.phys_channels = 4,
|
|
.name = "pac1934",
|
|
},
|
|
};
|
|
|
|
/**
|
|
* 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[PAC1934_MEAS_REG_LEN];
|
|
u8 ctrl_regs[PAC1934_CTRL_REG_LEN];
|
|
s64 energy_sec_acc[PAC1934_MAX_NUM_CHANNELS];
|
|
s64 vpower_acc[PAC1934_MAX_NUM_CHANNELS];
|
|
s32 vpower[PAC1934_MAX_NUM_CHANNELS];
|
|
s32 vbus[PAC1934_MAX_NUM_CHANNELS];
|
|
s32 vbus_avg[PAC1934_MAX_NUM_CHANNELS];
|
|
s32 vsense[PAC1934_MAX_NUM_CHANNELS];
|
|
s32 vsense_avg[PAC1934_MAX_NUM_CHANNELS];
|
|
u8 num_enabled_channels;
|
|
};
|
|
|
|
/**
|
|
* struct pac1934_chip_info - information about the chip
|
|
* @client: the i2c-client attached to the device
|
|
* @lock: synchronize access to driver's state members
|
|
* @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
|
|
* @enable_energy: array of values, true means that channel energy is measured
|
|
* @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
|
|
* @labels: table with channels labels
|
|
* @iio_info: iio_info
|
|
* @tstamp: chip's uptime
|
|
*/
|
|
struct pac1934_chip_info {
|
|
struct i2c_client *client;
|
|
struct mutex lock; /* synchronize access to driver's state members */
|
|
struct delayed_work work_chip_rfsh;
|
|
u8 phys_channels;
|
|
bool active_channels[PAC1934_MAX_NUM_CHANNELS];
|
|
bool enable_energy[PAC1934_MAX_NUM_CHANNELS];
|
|
bool bi_dir[PAC1934_MAX_NUM_CHANNELS];
|
|
u8 chip_variant;
|
|
u8 chip_revision;
|
|
u32 shunts[PAC1934_MAX_NUM_CHANNELS];
|
|
struct reg_data chip_reg_data;
|
|
s32 sample_rate_value;
|
|
char *labels[PAC1934_MAX_NUM_CHANNELS];
|
|
struct iio_info iio_info;
|
|
unsigned long tstamp;
|
|
};
|
|
|
|
#define TO_PAC1934_CHIP_INFO(d) container_of(d, struct pac1934_chip_info, work_chip_rfsh)
|
|
|
|
#define PAC1934_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) | \
|
|
BIT(IIO_CHAN_INFO_ENABLE), \
|
|
.info_mask_shared_by_all = 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 = 48, \
|
|
.storagebits = 64, \
|
|
.endianness = IIO_CPU, \
|
|
} \
|
|
}
|
|
|
|
#define PAC1934_VBUS_CHANNEL(_index, _si, _address) { \
|
|
.type = IIO_VOLTAGE, \
|
|
.address = (_address), \
|
|
.indexed = 1, \
|
|
.channel = (_index), \
|
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
|
BIT(IIO_CHAN_INFO_SCALE), \
|
|
.info_mask_shared_by_all = 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 = 16, \
|
|
.storagebits = 16, \
|
|
.endianness = IIO_CPU, \
|
|
} \
|
|
}
|
|
|
|
#define PAC1934_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_all = 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 = 16, \
|
|
.storagebits = 16, \
|
|
.endianness = IIO_CPU, \
|
|
} \
|
|
}
|
|
|
|
#define PAC1934_VSENSE_CHANNEL(_index, _si, _address) { \
|
|
.type = IIO_CURRENT, \
|
|
.address = (_address), \
|
|
.indexed = 1, \
|
|
.channel = (_index), \
|
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
|
BIT(IIO_CHAN_INFO_SCALE), \
|
|
.info_mask_shared_by_all = 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 = 16, \
|
|
.storagebits = 16, \
|
|
.endianness = IIO_CPU, \
|
|
} \
|
|
}
|
|
|
|
#define PAC1934_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_all = 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 = 16, \
|
|
.storagebits = 16, \
|
|
.endianness = IIO_CPU, \
|
|
} \
|
|
}
|
|
|
|
#define PAC1934_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_all = 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 = 28, \
|
|
.storagebits = 32, \
|
|
.shift = 4, \
|
|
.endianness = IIO_CPU, \
|
|
} \
|
|
}
|
|
|
|
static const struct iio_chan_spec pac1934_single_channel[] = {
|
|
PAC1934_VPOWER_ACC_CHANNEL(0, 0, PAC1934_VPOWER_ACC_1_ADDR),
|
|
PAC1934_VPOWER_CHANNEL(0, 0, PAC1934_VPOWER_1_ADDR),
|
|
PAC1934_VBUS_CHANNEL(0, 0, PAC1934_VBUS_1_ADDR),
|
|
PAC1934_VSENSE_CHANNEL(0, 0, PAC1934_VSENSE_1_ADDR),
|
|
PAC1934_VBUS_AVG_CHANNEL(0, 0, PAC1934_VBUS_AVG_1_ADDR),
|
|
PAC1934_VSENSE_AVG_CHANNEL(0, 0, PAC1934_VSENSE_AVG_1_ADDR),
|
|
};
|
|
|
|
/* Low-level I2c functions used to transfer up to 76 bytes at once */
|
|
static int pac1934_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 *)®_addr,
|
|
},
|
|
{
|
|
.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 pac1934_get_samp_rate_idx(struct pac1934_chip_info *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])
|
|
return cnt;
|
|
|
|
/* not a valid sample rate value */
|
|
return -EINVAL;
|
|
}
|
|
|
|
static ssize_t pac1934_shunt_value_show(struct device *dev,
|
|
struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
|
struct pac1934_chip_info *info = iio_priv(indio_dev);
|
|
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
|
|
|
return sysfs_emit(buf, "%u\n", info->shunts[this_attr->address]);
|
|
}
|
|
|
|
static ssize_t pac1934_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 pac1934_chip_info *info = iio_priv(indio_dev);
|
|
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
|
int sh_val;
|
|
|
|
if (kstrtouint(buf, 10, &sh_val)) {
|
|
dev_err(dev, "Shunt value is not valid\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
scoped_guard(mutex, &info->lock)
|
|
info->shunts[this_attr->address] = sh_val;
|
|
|
|
return count;
|
|
}
|
|
|
|
static int pac1934_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 pac1934_send_refresh(struct pac1934_chip_info *info,
|
|
u8 refresh_cmd, u32 wait_time)
|
|
{
|
|
/* this function only sends REFRESH or REFRESH_V */
|
|
struct i2c_client *client = info->client;
|
|
int ret;
|
|
u8 bidir_reg;
|
|
bool revision_bug = false;
|
|
|
|
if (info->chip_revision == 2 || info->chip_revision == 3) {
|
|
/*
|
|
* chip rev 2 and 3 bug workaround
|
|
* see: PAC1934 Family Data Sheet Errata DS80000836A.pdf
|
|
*/
|
|
revision_bug = true;
|
|
|
|
bidir_reg =
|
|
FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDI_MASK, info->bi_dir[0]) |
|
|
FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDI_MASK, info->bi_dir[1]) |
|
|
FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDI_MASK, info->bi_dir[2]) |
|
|
FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDI_MASK, info->bi_dir[3]) |
|
|
FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDV_MASK, info->bi_dir[0]) |
|
|
FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDV_MASK, info->bi_dir[1]) |
|
|
FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDV_MASK, info->bi_dir[2]) |
|
|
FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDV_MASK, info->bi_dir[3]);
|
|
|
|
ret = i2c_smbus_write_byte_data(client,
|
|
PAC1934_CTRL_STAT_REGS_ADDR +
|
|
PAC1934_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,
|
|
PAC1934_CTRL_STAT_REGS_ADDR +
|
|
PAC1934_NEG_PWR_REG_OFF, bidir_reg);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
/* register data retrieval timestamp */
|
|
info->tstamp = jiffies;
|
|
|
|
/* wait till the data is available */
|
|
usleep_range(wait_time, wait_time + 100);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int pac1934_reg_snapshot(struct pac1934_chip_info *info,
|
|
bool do_refresh, u8 refresh_cmd, u32 wait_time)
|
|
{
|
|
int ret;
|
|
struct i2c_client *client = 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;
|
|
|
|
guard(mutex)(&info->lock);
|
|
|
|
if (do_refresh) {
|
|
ret = pac1934_send_refresh(info, refresh_cmd, wait_time);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev,
|
|
"%s - cannot send refresh\n",
|
|
__func__);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
ret = i2c_smbus_read_i2c_block_data(client, PAC1934_CTRL_STAT_REGS_ADDR,
|
|
PAC1934_CTRL_REG_LEN,
|
|
(u8 *)info->chip_reg_data.ctrl_regs);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev,
|
|
"%s - cannot read ctrl/status registers\n",
|
|
__func__);
|
|
return ret;
|
|
}
|
|
|
|
reg_data = &info->chip_reg_data;
|
|
|
|
/* read the data registers */
|
|
ret = pac1934_i2c_read(client, PAC1934_ACC_COUNT_REG_ADDR,
|
|
(u8 *)reg_data->meas_regs, PAC1934_MEAS_REG_LEN);
|
|
if (ret) {
|
|
dev_err(&client->dev,
|
|
"%s - cannot read ACC_COUNT register: %d:%d\n",
|
|
__func__, ret, PAC1934_MEAS_REG_LEN);
|
|
return ret;
|
|
}
|
|
|
|
/* see how much shift is required by the sample rate */
|
|
samp_rate = samp_rate_map_tbl[((reg_data->ctrl_regs[PAC1934_CTRL_LAT_REG_OFF]) >> 6)];
|
|
samp_shift = get_count_order(samp_rate);
|
|
|
|
ctrl_regs_tmp = reg_data->ctrl_regs[PAC1934_CHANNEL_DIS_LAT_REG_OFF];
|
|
offset_reg_data_p = ®_data->meas_regs[PAC1934_ACC_REG_LEN];
|
|
|
|
/* start with VPOWER_ACC */
|
|
for (cnt = 0; cnt < info->phys_channels; cnt++) {
|
|
/* check if the channel is active, skip all fields if disabled */
|
|
if ((ctrl_regs_tmp << cnt) & 0x80)
|
|
continue;
|
|
|
|
/* skip if the energy accumulation is disabled */
|
|
if (info->enable_energy[cnt]) {
|
|
curr_energy = info->chip_reg_data.energy_sec_acc[cnt];
|
|
|
|
tmp_energy = get_unaligned_be48(offset_reg_data_p);
|
|
|
|
if (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;
|
|
|
|
clamp(curr_energy, PAC_193X_MIN_POWER_ACC, PAC_193X_MAX_POWER_ACC);
|
|
|
|
reg_data->energy_sec_acc[cnt] = curr_energy;
|
|
}
|
|
|
|
offset_reg_data_p += PAC1934_VPOWER_ACC_REG_LEN;
|
|
}
|
|
|
|
/* continue with VBUS */
|
|
for (cnt = 0; cnt < info->phys_channels; cnt++) {
|
|
if ((ctrl_regs_tmp << cnt) & 0x80)
|
|
continue;
|
|
|
|
tmp_value = get_unaligned_be16(offset_reg_data_p);
|
|
|
|
if (info->bi_dir[cnt])
|
|
reg_data->vbus[cnt] = sign_extend32((u32)(tmp_value), 15);
|
|
else
|
|
reg_data->vbus[cnt] = tmp_value;
|
|
|
|
offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN;
|
|
}
|
|
|
|
/* VSENSE */
|
|
for (cnt = 0; cnt < info->phys_channels; cnt++) {
|
|
if ((ctrl_regs_tmp << cnt) & 0x80)
|
|
continue;
|
|
|
|
tmp_value = get_unaligned_be16(offset_reg_data_p);
|
|
|
|
if (info->bi_dir[cnt])
|
|
reg_data->vsense[cnt] = sign_extend32((u32)(tmp_value), 15);
|
|
else
|
|
reg_data->vsense[cnt] = tmp_value;
|
|
|
|
offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN;
|
|
}
|
|
|
|
/* VBUS_AVG */
|
|
for (cnt = 0; cnt < info->phys_channels; cnt++) {
|
|
if ((ctrl_regs_tmp << cnt) & 0x80)
|
|
continue;
|
|
|
|
tmp_value = get_unaligned_be16(offset_reg_data_p);
|
|
|
|
if (info->bi_dir[cnt])
|
|
reg_data->vbus_avg[cnt] = sign_extend32((u32)(tmp_value), 15);
|
|
else
|
|
reg_data->vbus_avg[cnt] = tmp_value;
|
|
|
|
offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN;
|
|
}
|
|
|
|
/* VSENSE_AVG */
|
|
for (cnt = 0; cnt < info->phys_channels; cnt++) {
|
|
if ((ctrl_regs_tmp << cnt) & 0x80)
|
|
continue;
|
|
|
|
tmp_value = get_unaligned_be16(offset_reg_data_p);
|
|
|
|
if (info->bi_dir[cnt])
|
|
reg_data->vsense_avg[cnt] = sign_extend32((u32)(tmp_value), 15);
|
|
else
|
|
reg_data->vsense_avg[cnt] = tmp_value;
|
|
|
|
offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN;
|
|
}
|
|
|
|
/* VPOWER */
|
|
for (cnt = 0; cnt < info->phys_channels; cnt++) {
|
|
if ((ctrl_regs_tmp << cnt) & 0x80)
|
|
continue;
|
|
|
|
tmp = get_unaligned_be32(offset_reg_data_p) >> 4;
|
|
|
|
if (info->bi_dir[cnt])
|
|
reg_data->vpower[cnt] = sign_extend32(tmp, 27);
|
|
else
|
|
reg_data->vpower[cnt] = tmp;
|
|
|
|
offset_reg_data_p += PAC1934_VPOWER_REG_LEN;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pac1934_retrieve_data(struct pac1934_chip_info *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, info->tstamp + msecs_to_jiffies(PAC1934_MIN_POLLING_TIME_MS))) {
|
|
ret = pac1934_reg_snapshot(info, true, PAC1934_REFRESH_REG_ADDR,
|
|
wait_time);
|
|
|
|
/*
|
|
* Re-schedule the work for the read registers on timeout
|
|
* (to prevent chip registers saturation)
|
|
*/
|
|
mod_delayed_work(system_wq, &info->work_chip_rfsh,
|
|
msecs_to_jiffies(PAC1934_MAX_RFSH_LIMIT_MS));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int pac1934_read_raw(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan, int *val,
|
|
int *val2, long mask)
|
|
{
|
|
struct pac1934_chip_info *info = iio_priv(indio_dev);
|
|
s64 curr_energy;
|
|
int ret, channel = chan->channel - 1;
|
|
|
|
/*
|
|
* For AVG the index should be between 5 to 8.
|
|
* To calculate PAC1934_CH_VOLTAGE_AVERAGE,
|
|
* respectively PAC1934_CH_CURRENT real index, we need
|
|
* to remove the added offset (PAC1934_MAX_NUM_CHANNELS).
|
|
*/
|
|
if (channel >= PAC1934_MAX_NUM_CHANNELS)
|
|
channel = channel - PAC1934_MAX_NUM_CHANNELS;
|
|
|
|
ret = pac1934_retrieve_data(info, PAC1934_MIN_UPDATE_WAIT_TIME_US);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_RAW:
|
|
switch (chan->type) {
|
|
case IIO_VOLTAGE:
|
|
*val = info->chip_reg_data.vbus[channel];
|
|
return IIO_VAL_INT;
|
|
case IIO_CURRENT:
|
|
*val = info->chip_reg_data.vsense[channel];
|
|
return IIO_VAL_INT;
|
|
case IIO_POWER:
|
|
*val = info->chip_reg_data.vpower[channel];
|
|
return IIO_VAL_INT;
|
|
case IIO_ENERGY:
|
|
curr_energy = 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;
|
|
}
|
|
case IIO_CHAN_INFO_AVERAGE_RAW:
|
|
switch (chan->type) {
|
|
case IIO_VOLTAGE:
|
|
*val = info->chip_reg_data.vbus_avg[channel];
|
|
return IIO_VAL_INT;
|
|
case IIO_CURRENT:
|
|
*val = info->chip_reg_data.vsense_avg[channel];
|
|
return IIO_VAL_INT;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
case IIO_CHAN_INFO_SCALE:
|
|
switch (chan->address) {
|
|
/* Voltages - scale for millivolts */
|
|
case PAC1934_VBUS_1_ADDR:
|
|
case PAC1934_VBUS_2_ADDR:
|
|
case PAC1934_VBUS_3_ADDR:
|
|
case PAC1934_VBUS_4_ADDR:
|
|
case PAC1934_VBUS_AVG_1_ADDR:
|
|
case PAC1934_VBUS_AVG_2_ADDR:
|
|
case PAC1934_VBUS_AVG_3_ADDR:
|
|
case PAC1934_VBUS_AVG_4_ADDR:
|
|
*val = PAC1934_VOLTAGE_MILLIVOLTS_MAX;
|
|
if (chan->scan_type.sign == 'u')
|
|
*val2 = PAC1934_VOLTAGE_U_RES;
|
|
else
|
|
*val2 = PAC1934_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 PAC1934_VSENSE_1_ADDR:
|
|
case PAC1934_VSENSE_2_ADDR:
|
|
case PAC1934_VSENSE_3_ADDR:
|
|
case PAC1934_VSENSE_4_ADDR:
|
|
case PAC1934_VSENSE_AVG_1_ADDR:
|
|
case PAC1934_VSENSE_AVG_2_ADDR:
|
|
case PAC1934_VSENSE_AVG_3_ADDR:
|
|
case PAC1934_VSENSE_AVG_4_ADDR:
|
|
*val = PAC1934_MAX_VSENSE_RSHIFTED_BY_16B;
|
|
if (chan->scan_type.sign == 'u')
|
|
*val2 = info->shunts[channel];
|
|
else
|
|
*val2 = 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 PAC1934_VPOWER_1_ADDR:
|
|
case PAC1934_VPOWER_2_ADDR:
|
|
case PAC1934_VPOWER_3_ADDR:
|
|
case PAC1934_VPOWER_4_ADDR:
|
|
*val = PAC1934_MAX_VPOWER_RSHIFTED_BY_28B;
|
|
if (chan->scan_type.sign == 'u')
|
|
*val2 = info->shunts[channel];
|
|
else
|
|
*val2 = info->shunts[channel] >> 1;
|
|
return IIO_VAL_FRACTIONAL;
|
|
case PAC1934_VPOWER_ACC_1_ADDR:
|
|
case PAC1934_VPOWER_ACC_2_ADDR:
|
|
case PAC1934_VPOWER_ACC_3_ADDR:
|
|
case PAC1934_VPOWER_ACC_4_ADDR:
|
|
/*
|
|
* expresses the 32 bit scale value here compute
|
|
* the scale for energy (miliWatt-second or miliJoule)
|
|
*/
|
|
*val = PAC1934_SCALE_CONSTANT;
|
|
|
|
if (chan->scan_type.sign == 'u')
|
|
*val2 = info->shunts[channel];
|
|
else
|
|
*val2 = info->shunts[channel] >> 1;
|
|
return IIO_VAL_FRACTIONAL;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
|
*val = info->sample_rate_value;
|
|
return IIO_VAL_INT;
|
|
case IIO_CHAN_INFO_ENABLE:
|
|
*val = info->enable_energy[channel];
|
|
return IIO_VAL_INT;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int pac1934_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
|
|
int val, int val2, long mask)
|
|
{
|
|
struct pac1934_chip_info *info = iio_priv(indio_dev);
|
|
struct i2c_client *client = info->client;
|
|
int ret = -EINVAL;
|
|
s32 old_samp_rate;
|
|
u8 ctrl_reg;
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
|
ret = pac1934_get_samp_rate_idx(info, val);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* write the new sampling value and trigger a snapshot(incl refresh) */
|
|
scoped_guard(mutex, &info->lock) {
|
|
ctrl_reg = FIELD_PREP(PAC1934_CRTL_SAMPLE_RATE_MASK, ret);
|
|
ret = i2c_smbus_write_byte_data(client, PAC1934_CTRL_REG_ADDR, ctrl_reg);
|
|
if (ret) {
|
|
dev_err(&client->dev,
|
|
"%s - can't update sample rate\n",
|
|
__func__);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
old_samp_rate = info->sample_rate_value;
|
|
info->sample_rate_value = val;
|
|
|
|
/*
|
|
* 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
|
|
*/
|
|
info->tstamp -= msecs_to_jiffies(PAC1934_MIN_POLLING_TIME_MS);
|
|
ret = pac1934_retrieve_data(info, (1024 / old_samp_rate) * 1000);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev,
|
|
"%s - cannot snapshot ctrl and measurement regs\n",
|
|
__func__);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
case IIO_CHAN_INFO_ENABLE:
|
|
scoped_guard(mutex, &info->lock) {
|
|
info->enable_energy[chan->channel - 1] = val ? true : false;
|
|
if (!val)
|
|
info->chip_reg_data.energy_sec_acc[chan->channel - 1] = 0;
|
|
}
|
|
|
|
return 0;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int pac1934_read_label(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan, char *label)
|
|
{
|
|
struct pac1934_chip_info *info = iio_priv(indio_dev);
|
|
|
|
switch (chan->address) {
|
|
case PAC1934_VBUS_1_ADDR:
|
|
case PAC1934_VBUS_2_ADDR:
|
|
case PAC1934_VBUS_3_ADDR:
|
|
case PAC1934_VBUS_4_ADDR:
|
|
return sysfs_emit(label, "%s_VBUS_%d\n",
|
|
info->labels[chan->scan_index],
|
|
chan->scan_index + 1);
|
|
case PAC1934_VBUS_AVG_1_ADDR:
|
|
case PAC1934_VBUS_AVG_2_ADDR:
|
|
case PAC1934_VBUS_AVG_3_ADDR:
|
|
case PAC1934_VBUS_AVG_4_ADDR:
|
|
return sysfs_emit(label, "%s_VBUS_AVG_%d\n",
|
|
info->labels[chan->scan_index],
|
|
chan->scan_index + 1);
|
|
case PAC1934_VSENSE_1_ADDR:
|
|
case PAC1934_VSENSE_2_ADDR:
|
|
case PAC1934_VSENSE_3_ADDR:
|
|
case PAC1934_VSENSE_4_ADDR:
|
|
return sysfs_emit(label, "%s_IBUS_%d\n",
|
|
info->labels[chan->scan_index],
|
|
chan->scan_index + 1);
|
|
case PAC1934_VSENSE_AVG_1_ADDR:
|
|
case PAC1934_VSENSE_AVG_2_ADDR:
|
|
case PAC1934_VSENSE_AVG_3_ADDR:
|
|
case PAC1934_VSENSE_AVG_4_ADDR:
|
|
return sysfs_emit(label, "%s_IBUS_AVG_%d\n",
|
|
info->labels[chan->scan_index],
|
|
chan->scan_index + 1);
|
|
case PAC1934_VPOWER_1_ADDR:
|
|
case PAC1934_VPOWER_2_ADDR:
|
|
case PAC1934_VPOWER_3_ADDR:
|
|
case PAC1934_VPOWER_4_ADDR:
|
|
return sysfs_emit(label, "%s_POWER_%d\n",
|
|
info->labels[chan->scan_index],
|
|
chan->scan_index + 1);
|
|
case PAC1934_VPOWER_ACC_1_ADDR:
|
|
case PAC1934_VPOWER_ACC_2_ADDR:
|
|
case PAC1934_VPOWER_ACC_3_ADDR:
|
|
case PAC1934_VPOWER_ACC_4_ADDR:
|
|
return sysfs_emit(label, "%s_ENERGY_%d\n",
|
|
info->labels[chan->scan_index],
|
|
chan->scan_index + 1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void pac1934_work_periodic_rfsh(struct work_struct *work)
|
|
{
|
|
struct pac1934_chip_info *info = TO_PAC1934_CHIP_INFO((struct delayed_work *)work);
|
|
struct device *dev = &info->client->dev;
|
|
|
|
dev_dbg(dev, "%s - Periodic refresh\n", __func__);
|
|
|
|
/* do a REFRESH, then read */
|
|
pac1934_reg_snapshot(info, true, PAC1934_REFRESH_REG_ADDR,
|
|
PAC1934_MIN_UPDATE_WAIT_TIME_US);
|
|
|
|
schedule_delayed_work(&info->work_chip_rfsh,
|
|
msecs_to_jiffies(PAC1934_MAX_RFSH_LIMIT_MS));
|
|
}
|
|
|
|
static int pac1934_read_revision(struct pac1934_chip_info *info, u8 *buf)
|
|
{
|
|
int ret;
|
|
struct i2c_client *client = info->client;
|
|
|
|
ret = i2c_smbus_read_i2c_block_data(client, PAC1934_PID_REG_ADDR,
|
|
PAC1934_ID_REG_LEN,
|
|
buf);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev, "cannot read revision\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pac1934_chip_identify(struct pac1934_chip_info *info)
|
|
{
|
|
u8 rev_info[PAC1934_ID_REG_LEN];
|
|
struct device *dev = &info->client->dev;
|
|
int ret = 0;
|
|
|
|
ret = pac1934_read_revision(info, (u8 *)rev_info);
|
|
if (ret)
|
|
return ret;
|
|
|
|
info->chip_variant = rev_info[PAC1934_PID_IDX];
|
|
info->chip_revision = rev_info[PAC1934_RID_IDX];
|
|
|
|
dev_dbg(dev, "Chip variant: 0x%02X\n", info->chip_variant);
|
|
dev_dbg(dev, "Chip revision: 0x%02X\n", info->chip_revision);
|
|
|
|
switch (info->chip_variant) {
|
|
case PAC1934_PID:
|
|
return PAC1934;
|
|
case PAC1933_PID:
|
|
return PAC1933;
|
|
case PAC1932_PID:
|
|
return PAC1932;
|
|
case PAC1931_PID:
|
|
return PAC1931;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* documentation related to the ACPI device definition
|
|
* https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC1934-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf
|
|
*/
|
|
static int pac1934_acpi_parse_channel_config(struct i2c_client *client,
|
|
struct pac1934_chip_info *info)
|
|
{
|
|
acpi_handle handle;
|
|
union acpi_object *rez;
|
|
struct device *dev = &client->dev;
|
|
unsigned short bi_dir_mask;
|
|
int idx, i;
|
|
guid_t guid;
|
|
|
|
handle = ACPI_HANDLE(dev);
|
|
|
|
guid_parse(PAC1934_DSM_UUID, &guid);
|
|
|
|
rez = acpi_evaluate_dsm(handle, &guid, 0, PAC1934_ACPI_GET_NAMES_AND_MOHMS_VALS, NULL);
|
|
if (!rez)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < rez->package.count; i += 2) {
|
|
idx = i / 2;
|
|
info->labels[idx] =
|
|
devm_kmemdup(dev, rez->package.elements[i].string.pointer,
|
|
(size_t)rez->package.elements[i].string.length + 1,
|
|
GFP_KERNEL);
|
|
info->labels[idx][rez->package.elements[i].string.length] = '\0';
|
|
info->shunts[idx] = rez->package.elements[i + 1].integer.value * 1000;
|
|
info->active_channels[idx] = (info->shunts[idx] != 0);
|
|
}
|
|
|
|
ACPI_FREE(rez);
|
|
|
|
rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_UOHMS_VALS, NULL);
|
|
if (!rez) {
|
|
/*
|
|
* initializing with default values
|
|
* we assume all channels are unidirectional(the mask is zero)
|
|
* and assign the default sampling rate
|
|
*/
|
|
info->sample_rate_value = PAC1934_DEFAULT_CHIP_SAMP_SPEED_HZ;
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < rez->package.count; i++) {
|
|
idx = i;
|
|
info->shunts[idx] = rez->package.elements[i].integer.value;
|
|
info->active_channels[idx] = (info->shunts[idx] != 0);
|
|
}
|
|
|
|
ACPI_FREE(rez);
|
|
|
|
rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_BIPOLAR_SETTINGS, NULL);
|
|
if (!rez)
|
|
return -EINVAL;
|
|
|
|
bi_dir_mask = rez->package.elements[0].integer.value;
|
|
info->bi_dir[0] = ((bi_dir_mask & (1 << 3)) | (bi_dir_mask & (1 << 7))) != 0;
|
|
info->bi_dir[1] = ((bi_dir_mask & (1 << 2)) | (bi_dir_mask & (1 << 6))) != 0;
|
|
info->bi_dir[2] = ((bi_dir_mask & (1 << 1)) | (bi_dir_mask & (1 << 5))) != 0;
|
|
info->bi_dir[3] = ((bi_dir_mask & (1 << 0)) | (bi_dir_mask & (1 << 4))) != 0;
|
|
|
|
ACPI_FREE(rez);
|
|
|
|
rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_SAMP, NULL);
|
|
if (!rez)
|
|
return -EINVAL;
|
|
|
|
info->sample_rate_value = rez->package.elements[0].integer.value;
|
|
|
|
ACPI_FREE(rez);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pac1934_fw_parse_channel_config(struct i2c_client *client,
|
|
struct pac1934_chip_info *info)
|
|
{
|
|
struct device *dev = &client->dev;
|
|
unsigned int current_channel;
|
|
int idx, ret;
|
|
|
|
info->sample_rate_value = 1024;
|
|
current_channel = 1;
|
|
|
|
device_for_each_child_node_scoped(dev, node) {
|
|
ret = fwnode_property_read_u32(node, "reg", &idx);
|
|
if (ret)
|
|
return dev_err_probe(dev, ret,
|
|
"reading invalid channel index\n");
|
|
|
|
/* adjust idx to match channel index (1 to 4) from the datasheet */
|
|
idx--;
|
|
|
|
if (current_channel >= (info->phys_channels + 1) ||
|
|
idx >= info->phys_channels || idx < 0)
|
|
return dev_err_probe(dev, -EINVAL,
|
|
"%s: invalid channel_index %d value\n",
|
|
fwnode_get_name(node), idx);
|
|
|
|
/* enable channel */
|
|
info->active_channels[idx] = true;
|
|
|
|
ret = fwnode_property_read_u32(node, "shunt-resistor-micro-ohms",
|
|
&info->shunts[idx]);
|
|
if (ret)
|
|
return dev_err_probe(dev, ret,
|
|
"%s: invalid shunt-resistor value: %d\n",
|
|
fwnode_get_name(node), info->shunts[idx]);
|
|
|
|
if (fwnode_property_present(node, "label")) {
|
|
ret = fwnode_property_read_string(node, "label",
|
|
(const char **)&info->labels[idx]);
|
|
if (ret)
|
|
return dev_err_probe(dev, ret,
|
|
"%s: invalid rail-name value\n",
|
|
fwnode_get_name(node));
|
|
}
|
|
|
|
info->bi_dir[idx] = fwnode_property_read_bool(node, "bipolar");
|
|
|
|
current_channel++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void pac1934_cancel_delayed_work(void *dwork)
|
|
{
|
|
cancel_delayed_work_sync(dwork);
|
|
}
|
|
|
|
static int pac1934_chip_configure(struct pac1934_chip_info *info)
|
|
{
|
|
int cnt, ret;
|
|
struct i2c_client *client = info->client;
|
|
u8 regs[PAC1934_CTRL_STATUS_INFO_LEN], idx, ctrl_reg;
|
|
u32 wait_time;
|
|
|
|
info->chip_reg_data.num_enabled_channels = 0;
|
|
for (cnt = 0; cnt < info->phys_channels; cnt++) {
|
|
if (info->active_channels[cnt])
|
|
info->chip_reg_data.num_enabled_channels++;
|
|
}
|
|
|
|
/*
|
|
* 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 = i2c_smbus_read_i2c_block_data(client, PAC1934_CTRL_STAT_REGS_ADDR,
|
|
ARRAY_SIZE(regs),
|
|
(u8 *)regs);
|
|
if (ret < 0) {
|
|
dev_err_probe(&client->dev, ret,
|
|
"%s - cannot read regs from 0x%02X\n",
|
|
__func__, PAC1934_CTRL_STAT_REGS_ADDR);
|
|
return ret;
|
|
}
|
|
|
|
/* write the CHANNEL_DIS and the NEG_PWR registers */
|
|
regs[PAC1934_CHANNEL_DIS_REG_OFF] =
|
|
FIELD_PREP(PAC1934_CHAN_DIS_CH1_OFF_MASK, info->active_channels[0] ? 0 : 1) |
|
|
FIELD_PREP(PAC1934_CHAN_DIS_CH2_OFF_MASK, info->active_channels[1] ? 0 : 1) |
|
|
FIELD_PREP(PAC1934_CHAN_DIS_CH3_OFF_MASK, info->active_channels[2] ? 0 : 1) |
|
|
FIELD_PREP(PAC1934_CHAN_DIS_CH4_OFF_MASK, info->active_channels[3] ? 0 : 1) |
|
|
FIELD_PREP(PAC1934_SMBUS_TIMEOUT_MASK, 0) |
|
|
FIELD_PREP(PAC1934_SMBUS_BYTECOUNT_MASK, 0) |
|
|
FIELD_PREP(PAC1934_SMBUS_NO_SKIP_MASK, 0);
|
|
|
|
regs[PAC1934_NEG_PWR_REG_OFF] =
|
|
FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDI_MASK, info->bi_dir[0]) |
|
|
FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDI_MASK, info->bi_dir[1]) |
|
|
FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDI_MASK, info->bi_dir[2]) |
|
|
FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDI_MASK, info->bi_dir[3]) |
|
|
FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDV_MASK, info->bi_dir[0]) |
|
|
FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDV_MASK, info->bi_dir[1]) |
|
|
FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDV_MASK, info->bi_dir[2]) |
|
|
FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDV_MASK, info->bi_dir[3]);
|
|
|
|
/* no SLOW triggered REFRESH, clear POR */
|
|
regs[PAC1934_SLOW_REG_OFF] = 0;
|
|
|
|
ret = i2c_smbus_write_block_data(client, PAC1934_CTRL_STAT_REGS_ADDR,
|
|
ARRAY_SIZE(regs), (u8 *)regs);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Default sampling rate */
|
|
ctrl_reg = FIELD_PREP(PAC1934_CRTL_SAMPLE_RATE_MASK, PAC1934_SAMP_1024SPS);
|
|
|
|
ret = i2c_smbus_write_byte_data(client, PAC1934_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, PAC1934_REFRESH_REG_ADDR);
|
|
if (ret) {
|
|
dev_err(&client->dev,
|
|
"%s - cannot send 0x%02X\n",
|
|
__func__, PAC1934_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[PAC1934_CTRL_ACT_REG_OFF] >> PAC1934_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(&info->work_chip_rfsh, pac1934_work_periodic_rfsh);
|
|
/* Setup the latest moment for reading the regs before saturation */
|
|
schedule_delayed_work(&info->work_chip_rfsh,
|
|
msecs_to_jiffies(PAC1934_MAX_RFSH_LIMIT_MS));
|
|
|
|
return devm_add_action_or_reset(&client->dev, pac1934_cancel_delayed_work,
|
|
&info->work_chip_rfsh);
|
|
}
|
|
|
|
static int pac1934_prep_iio_channels(struct pac1934_chip_info *info, struct iio_dev *indio_dev)
|
|
{
|
|
struct iio_chan_spec *ch_sp;
|
|
int channel_size, attribute_count, cnt;
|
|
void *dyn_ch_struct, *tmp_data;
|
|
struct device *dev = &info->client->dev;
|
|
|
|
/* find out dynamically how many IIO channels we need */
|
|
attribute_count = 0;
|
|
channel_size = 0;
|
|
for (cnt = 0; cnt < info->phys_channels; cnt++) {
|
|
if (!info->active_channels[cnt])
|
|
continue;
|
|
|
|
/* add the size of the properties of one chip physical channel */
|
|
channel_size += sizeof(pac1934_single_channel);
|
|
/* count how many enabled channels we have */
|
|
attribute_count += ARRAY_SIZE(pac1934_single_channel);
|
|
dev_dbg(dev, ":%s: Channel %d active\n", __func__, cnt + 1);
|
|
}
|
|
|
|
dyn_ch_struct = devm_kzalloc(dev, 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 < info->phys_channels; cnt++) {
|
|
if (!info->active_channels[cnt])
|
|
continue;
|
|
|
|
memcpy(tmp_data, pac1934_single_channel, sizeof(pac1934_single_channel));
|
|
ch_sp = (struct iio_chan_spec *)tmp_data;
|
|
ch_sp[PAC1934_CH_ENERGY].channel = cnt + 1;
|
|
ch_sp[PAC1934_CH_ENERGY].scan_index = cnt;
|
|
ch_sp[PAC1934_CH_ENERGY].address = cnt + PAC1934_VPOWER_ACC_1_ADDR;
|
|
ch_sp[PAC1934_CH_POWER].channel = cnt + 1;
|
|
ch_sp[PAC1934_CH_POWER].scan_index = cnt;
|
|
ch_sp[PAC1934_CH_POWER].address = cnt + PAC1934_VPOWER_1_ADDR;
|
|
ch_sp[PAC1934_CH_VOLTAGE].channel = cnt + 1;
|
|
ch_sp[PAC1934_CH_VOLTAGE].scan_index = cnt;
|
|
ch_sp[PAC1934_CH_VOLTAGE].address = cnt + PAC1934_VBUS_1_ADDR;
|
|
ch_sp[PAC1934_CH_CURRENT].channel = cnt + 1;
|
|
ch_sp[PAC1934_CH_CURRENT].scan_index = cnt;
|
|
ch_sp[PAC1934_CH_CURRENT].address = cnt + PAC1934_VSENSE_1_ADDR;
|
|
|
|
/*
|
|
* In order to be able to use labels for PAC1934_CH_VOLTAGE, and
|
|
* PAC1934_CH_VOLTAGE_AVERAGE,respectively PAC1934_CH_CURRENT
|
|
* and PAC1934_CH_CURRENT_AVERAGE we need to use different
|
|
* channel numbers. We will add +5 (+1 to maximum PAC channels).
|
|
*/
|
|
ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].channel = cnt + 5;
|
|
ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].scan_index = cnt;
|
|
ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].address = cnt + PAC1934_VBUS_AVG_1_ADDR;
|
|
ch_sp[PAC1934_CH_CURRENT_AVERAGE].channel = cnt + 5;
|
|
ch_sp[PAC1934_CH_CURRENT_AVERAGE].scan_index = cnt;
|
|
ch_sp[PAC1934_CH_CURRENT_AVERAGE].address = cnt + PAC1934_VSENSE_AVG_1_ADDR;
|
|
|
|
/*
|
|
* now modify the parameters in all channels if the
|
|
* whole chip rail(channel) is bi-directional
|
|
*/
|
|
if (info->bi_dir[cnt]) {
|
|
ch_sp[PAC1934_CH_ENERGY].scan_type.sign = 's';
|
|
ch_sp[PAC1934_CH_ENERGY].scan_type.realbits = 47;
|
|
ch_sp[PAC1934_CH_POWER].scan_type.sign = 's';
|
|
ch_sp[PAC1934_CH_POWER].scan_type.realbits = 27;
|
|
ch_sp[PAC1934_CH_VOLTAGE].scan_type.sign = 's';
|
|
ch_sp[PAC1934_CH_VOLTAGE].scan_type.realbits = 15;
|
|
ch_sp[PAC1934_CH_CURRENT].scan_type.sign = 's';
|
|
ch_sp[PAC1934_CH_CURRENT].scan_type.realbits = 15;
|
|
ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].scan_type.sign = 's';
|
|
ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].scan_type.realbits = 15;
|
|
ch_sp[PAC1934_CH_CURRENT_AVERAGE].scan_type.sign = 's';
|
|
ch_sp[PAC1934_CH_CURRENT_AVERAGE].scan_type.realbits = 15;
|
|
}
|
|
tmp_data += sizeof(pac1934_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 = (const struct iio_chan_spec *)dyn_ch_struct;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static IIO_DEVICE_ATTR(in_shunt_resistor1, 0644,
|
|
pac1934_shunt_value_show, pac1934_shunt_value_store, 0);
|
|
static IIO_DEVICE_ATTR(in_shunt_resistor2, 0644,
|
|
pac1934_shunt_value_show, pac1934_shunt_value_store, 1);
|
|
static IIO_DEVICE_ATTR(in_shunt_resistor3, 0644,
|
|
pac1934_shunt_value_show, pac1934_shunt_value_store, 2);
|
|
static IIO_DEVICE_ATTR(in_shunt_resistor4, 0644,
|
|
pac1934_shunt_value_show, pac1934_shunt_value_store, 3);
|
|
|
|
static int pac1934_prep_custom_attributes(struct pac1934_chip_info *info,
|
|
struct iio_dev *indio_dev)
|
|
{
|
|
int i, active_channels_count = 0;
|
|
struct attribute **pac1934_custom_attr;
|
|
struct attribute_group *pac1934_group;
|
|
struct device *dev = &info->client->dev;
|
|
|
|
for (i = 0 ; i < info->phys_channels; i++)
|
|
if (info->active_channels[i])
|
|
active_channels_count++;
|
|
|
|
pac1934_group = devm_kzalloc(dev, sizeof(*pac1934_group), GFP_KERNEL);
|
|
if (!pac1934_group)
|
|
return -ENOMEM;
|
|
|
|
pac1934_custom_attr = devm_kzalloc(dev,
|
|
(PAC1934_CUSTOM_ATTR_FOR_CHANNEL *
|
|
active_channels_count)
|
|
* sizeof(*pac1934_group) + 1,
|
|
GFP_KERNEL);
|
|
if (!pac1934_custom_attr)
|
|
return -ENOMEM;
|
|
|
|
i = 0;
|
|
if (info->active_channels[0])
|
|
pac1934_custom_attr[i++] = PAC1934_DEV_ATTR(in_shunt_resistor1);
|
|
|
|
if (info->active_channels[1])
|
|
pac1934_custom_attr[i++] = PAC1934_DEV_ATTR(in_shunt_resistor2);
|
|
|
|
if (info->active_channels[2])
|
|
pac1934_custom_attr[i++] = PAC1934_DEV_ATTR(in_shunt_resistor3);
|
|
|
|
if (info->active_channels[3])
|
|
pac1934_custom_attr[i] = PAC1934_DEV_ATTR(in_shunt_resistor4);
|
|
|
|
pac1934_group->attrs = pac1934_custom_attr;
|
|
info->iio_info.attrs = pac1934_group;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void pac1934_mutex_destroy(void *data)
|
|
{
|
|
struct mutex *lock = data;
|
|
|
|
mutex_destroy(lock);
|
|
}
|
|
|
|
static const struct iio_info pac1934_info = {
|
|
.read_raw = pac1934_read_raw,
|
|
.write_raw = pac1934_write_raw,
|
|
.read_avail = pac1934_read_avail,
|
|
.read_label = pac1934_read_label,
|
|
};
|
|
|
|
static int pac1934_probe(struct i2c_client *client)
|
|
{
|
|
struct pac1934_chip_info *info;
|
|
const struct pac1934_features *chip;
|
|
struct iio_dev *indio_dev;
|
|
int cnt, ret;
|
|
struct device *dev = &client->dev;
|
|
|
|
indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
|
|
if (!indio_dev)
|
|
return -ENOMEM;
|
|
|
|
info = iio_priv(indio_dev);
|
|
|
|
info->client = client;
|
|
|
|
/* always start with energy accumulation enabled */
|
|
for (cnt = 0; cnt < PAC1934_MAX_NUM_CHANNELS; cnt++)
|
|
info->enable_energy[cnt] = true;
|
|
|
|
ret = pac1934_chip_identify(info);
|
|
if (ret < 0) {
|
|
/*
|
|
* If failed to identify the hardware based on internal
|
|
* registers, try using fallback compatible in device tree
|
|
* to deal with some newer part number.
|
|
*/
|
|
chip = i2c_get_match_data(client);
|
|
if (!chip)
|
|
return -EINVAL;
|
|
|
|
info->phys_channels = chip->phys_channels;
|
|
indio_dev->name = chip->name;
|
|
} else {
|
|
info->phys_channels = pac1934_chip_config[ret].phys_channels;
|
|
indio_dev->name = pac1934_chip_config[ret].name;
|
|
}
|
|
|
|
if (acpi_match_device(dev->driver->acpi_match_table, dev))
|
|
ret = pac1934_acpi_parse_channel_config(client, info);
|
|
else
|
|
/*
|
|
* This makes it possible to use also ACPI PRP0001 for
|
|
* registering the device using device tree properties.
|
|
*/
|
|
ret = pac1934_fw_parse_channel_config(client, info);
|
|
|
|
if (ret)
|
|
return dev_err_probe(dev, ret,
|
|
"parameter parsing returned an error\n");
|
|
|
|
mutex_init(&info->lock);
|
|
ret = devm_add_action_or_reset(dev, pac1934_mutex_destroy,
|
|
&info->lock);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/*
|
|
* 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 = pac1934_chip_configure(info);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* prepare the channel information */
|
|
ret = pac1934_prep_iio_channels(info, indio_dev);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
info->iio_info = pac1934_info;
|
|
indio_dev->info = &info->iio_info;
|
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
|
|
|
ret = pac1934_prep_custom_attributes(info, indio_dev);
|
|
if (ret < 0)
|
|
return dev_err_probe(dev, ret,
|
|
"Can't configure custom attributes for PAC1934 device\n");
|
|
|
|
/*
|
|
* read whatever has been accumulated in the chip so far
|
|
* and reset the accumulators
|
|
*/
|
|
ret = pac1934_reg_snapshot(info, true, PAC1934_REFRESH_REG_ADDR,
|
|
PAC1934_MIN_UPDATE_WAIT_TIME_US);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = devm_iio_device_register(dev, indio_dev);
|
|
if (ret < 0)
|
|
return dev_err_probe(dev, ret,
|
|
"Can't register IIO device\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct i2c_device_id pac1934_id[] = {
|
|
{ .name = "pac1931", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1931] },
|
|
{ .name = "pac1932", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1932] },
|
|
{ .name = "pac1933", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1933] },
|
|
{ .name = "pac1934", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1934] },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, pac1934_id);
|
|
|
|
static const struct of_device_id pac1934_of_match[] = {
|
|
{
|
|
.compatible = "microchip,pac1931",
|
|
.data = &pac1934_chip_config[PAC1931]
|
|
},
|
|
{
|
|
.compatible = "microchip,pac1932",
|
|
.data = &pac1934_chip_config[PAC1932]
|
|
},
|
|
{
|
|
.compatible = "microchip,pac1933",
|
|
.data = &pac1934_chip_config[PAC1933]
|
|
},
|
|
{
|
|
.compatible = "microchip,pac1934",
|
|
.data = &pac1934_chip_config[PAC1934]
|
|
},
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, pac1934_of_match);
|
|
|
|
/*
|
|
* using MCHP1930 to be compatible with BIOS ACPI. See example:
|
|
* https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC1934-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf
|
|
*/
|
|
static const struct acpi_device_id pac1934_acpi_match[] = {
|
|
{ "MCHP1930", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1934] },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(acpi, pac1934_acpi_match);
|
|
|
|
static struct i2c_driver pac1934_driver = {
|
|
.driver = {
|
|
.name = "pac1934",
|
|
.of_match_table = pac1934_of_match,
|
|
.acpi_match_table = pac1934_acpi_match
|
|
},
|
|
.probe = pac1934_probe,
|
|
.id_table = pac1934_id,
|
|
};
|
|
|
|
module_i2c_driver(pac1934_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 PAC1934 Multi-Channel DC Power/Energy Monitor");
|
|
MODULE_LICENSE("GPL");
|