| // 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 (is_acpi_device_node(dev_fwnode(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"); |