From: sashiko-bot@kernel.org
To: "Ariana Lazar" <ariana.lazar@microchip.com>
Cc: devicetree@vger.kernel.org, linux-hwmon@vger.kernel.org,
robh@kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH v5 5/5] iio: adc: add support for PAC1711
Date: Tue, 03 Nov 2026 14:59:27 +0000 [thread overview]
Message-ID: <sashiko-outbox-195525@kernel.org> (raw)
In-Reply-To: <20261103-pac1711-v5-5-71c20101a52e@microchip.com>
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [High] iio: adc: pac1711: wrong power and energy scale units in pac1711_read_raw()
- [High] iio: adc: pac1711: missing vdd-supply regulator enable in pac1711_probe()
- [Medium] iio: adc: pac1711: in_shunt_resistor uses micro-ohms instead of Ohms
- [Medium] iio: adc: pac1711: signed int overflow of shunt in pac1711_read_raw()
- [Medium] iio: adc: pac1711: premature jiffies_tstamp update in pac1711_send_refresh()
- [Low] iio: adc: pac1711: premature sample_rate_idx update in pac1711_write_raw()
--
commit c4f8dfb4bdee1e8ce1711a47ff2fad6880c4a9a3
Author: Ariana Lazar <ariana.lazar@microchip.com>
iio: adc: add support for PAC1711
This commit adds an IIO ADC driver for the Microchip PAC1711, PAC1721,
PAC1811, and PAC1821 single-channel power and current monitors with hardware
accumulators.
> diff --git a/drivers/iio/adc/pac1711.c b/drivers/iio/adc/pac1711.c
> new file mode 100644
> index 0000000000000..6adc37a7ba0be
> --- /dev/null
> +++ b/drivers/iio/adc/pac1711.c
> @@ -0,0 +1,1233 @@
[ ... ]
> +/* Full FSR power is multiplied by 10^9 to ensure precise scale calculation. */
> +#define PAC1711_PRODUCT_VOLTAGE_PV_FSR (4200ULL * NANO)
> +#define PAC1721_PRODUCT_VOLTAGE_PV_FSR (900ULL * NANO)
[ ... ]
> +static int pac1711_send_refresh(struct pac1711_chip_info *info, u32 wait_time)
> +{
> + struct i2c_client *client = info->client;
> + int ret;
> +
> + /* Writing a REFRESH or a REFRESH_V command */
> + ret = i2c_smbus_write_byte(client, PAC1711_REFRESH_REG_ADDR);
> + if (ret) {
> + dev_err(&client->dev, "%s - cannot send Refresh cmd (0x%02X)\n",
> + __func__, PAC1711_REFRESH_REG_ADDR);
> + return ret;
> + }
> +
> + /* Register data retrieval timestamp */
> + info->chip_reg_data.jiffies_tstamp = jiffies;
> +
> + /* Wait till the data is available */
> + fsleep(wait_time);
> +
> + return 0;
> +}
[ ... ]
> +static int pac1711_reg_snapshot(struct pac1711_chip_info *info, bool *refreshed)
> +{
> + u32 wait_time;
> + int ret;
> +
> + guard(mutex)(&info->lock);
> +
> + /* No hardware refresh is needed */
> + if (refreshed)
> + *refreshed = false;
> +
> + /* Return early if the minimum polling time hasn't elapsed */
> + if (!time_after(jiffies, info->chip_reg_data.jiffies_tstamp +
> + msecs_to_jiffies(PAC1711_MIN_POLLING_TIME_MS)))
> + return 0;
> +
> + wait_time = (1024 * 1000) / pac1711_samp_rate_map_tbl[info->sample_rate_idx];
> +
> + /* A hardware refresh will happen */
> + ret = pac1711_reg_snapshot_locked(info, wait_time);
> + if (!ret && refreshed)
> + *refreshed = true;
> +
> + return ret;
> +}
[ ... ]
> +static ssize_t pac1711_read_shunt_resistor(struct iio_dev *indio_dev, uintptr_t private,
> + const struct iio_chan_spec *ch, char *buf)
> +{
> + struct pac1711_chip_info *info = iio_priv(indio_dev);
> +
> + guard(mutex)(&info->lock);
> +
> + return sysfs_emit(buf, "%u\n", info->shunt);
> +}
> +
> +static int pac1711_set_acc_enable(struct pac1711_chip_info *info, int val)
> +{
> + struct i2c_client *client = info->client;
> + struct device *dev = &client->dev;
> + u32 wait_time, sample_rate;
> + int ret = 0;
> +
> + if (val != 0 && val != 1)
> + return -EINVAL;
> +
> + guard(mutex)(&info->lock);
> +
> + if (val == info->enable_acc)
> + return 0;
> +
> + /*
> + * Force a hardware refresh to zero out the internal accumulators
> + * and start a new measurement window, as the chip accumulates
> + * continuously in the background.
> + */
> + if (val) {
> + sample_rate = pac1711_samp_rate_map_tbl[info->sample_rate_idx];
> + wait_time = (1024 * 1000) / sample_rate;
> + ret = pac1711_send_refresh(info, wait_time);
> + } else {
> + info->chip_reg_data.acc_val = 0;
> + info->chip_reg_data.vacc = 0;
> + info->chip_reg_data.acc_count = 0;
> + }
[Severity: Medium]
Should info->chip_reg_data.jiffies_tstamp be updated only after
pac1711_reg_snapshot_locked() has finished reading the measurement registers,
rather than inside pac1711_send_refresh()?
Currently, pac1711_send_refresh() sets info->chip_reg_data.jiffies_tstamp =
jiffies before sleeping for wait_time and before any registers are read into
info->chip_reg_data.
When pac1711_set_acc_enable() re-enables accumulation, it calls
pac1711_send_refresh() without reading the measurement registers into
info->chip_reg_data. If userspace reads in_voltage0_raw, in_current0_raw, or
in_power0_raw within 50 ms (PAC1711_MIN_POLLING_TIME_MS),
pac1711_reg_snapshot() sees the updated timestamp and returns early with stale
cached values. The same issue occurs if an I2C block read fails in
pac1711_reg_snapshot_locked() after pac1711_send_refresh() succeeds.
Conversely, at 8 SPS where wait_time is 128 ms, recording jiffies_tstamp
before fsleep(wait_time) means the 50 ms cache window has already expired by
the time pac1711_send_refresh() returns.
> +
> + if (!ret)
> + info->enable_acc = val;
> +
> + if (ret)
> + dev_err(dev, "failed to reset accumulator: %d\n", ret);
> +
> + return ret;
> +}
> +
> +/*
> + * The value of the shunt resistor may be known only at runtime and set by a client
> + * application. This attribute allows to set its value in micro-ohms. Y is the channel
> + * number. The value is used to calculate current, power and accumulated energy or
> + * Coulomb counter.
> + */
> +static ssize_t pac1711_write_shunt_resistor(struct iio_dev *indio_dev, uintptr_t private,
> + const struct iio_chan_spec *ch, const char *buf,
> + size_t len)
> +{
> + struct pac1711_chip_info *info = iio_priv(indio_dev);
> + struct device *dev = &info->client->dev;
> + unsigned int sh_val;
> + int ret;
> +
> + ret = kstrtouint(buf, 10, &sh_val);
> + if (ret) {
> + dev_err(dev, "Shunt value is not valid\n");
> + return ret;
> + }
> +
> + if (sh_val == 0)
> + return -EINVAL;
> +
> + guard(mutex)(&info->lock);
> +
> + info->shunt = sh_val;
> +
> + return len;
> +}
> +
> +static const struct iio_chan_spec_ext_info pac1711_ext_info[] = {
> + {
> + .name = "in_shunt_resistor",
> + .read = pac1711_read_shunt_resistor,
> + .write = pac1711_write_shunt_resistor,
> + .shared = IIO_SHARED_BY_ALL,
> + },
> + { }
> +};
[Severity: Medium]
Does exposing in_shunt_resistor in micro-ohms violate the IIO sysfs ABI?
Documentation/ABI/testing/sysfs-bus-iio specifies that in_shunt_resistor is
expressed in Ohms. Here, pac1711_read_shunt_resistor() emits info->shunt
directly in micro-ohms via sysfs_emit(), and pac1711_write_shunt_resistor()
parses the input as an integer in micro-ohms via kstrtouint().
Should this format the value in Ohms using iio_format_value() with
IIO_VAL_FRACTIONAL (or IIO_VAL_INT_PLUS_MICRO) and parse writes with
iio_str_to_fixpoint(), matching other power monitor drivers such as pac1921?
[ ... ]
> + case IIO_CHAN_INFO_SCALE: {
> + u32 shunt;
> +
> + guard(mutex)(&info->lock);
> + shunt = info->shunt;
> +
> + switch (chan->address) {
> + case PAC1711_VBUS_REG_ADDR:
> + *val = info->features->voltage_millivolts_max;
> + *val2 = (info->vbus_mode == PAC1711_FULL_RANGE_BIPOLAR) ? 15 : 16;
> +
> + return IIO_VAL_FRACTIONAL_LOG2;
> + case PAC1711_VSENSE_REG_ADDR:
> + /*
> + * Currents - scale for mA - depends on the channel's shunt value
> + * (100mV * 1000000) / (2^16 * shunt(uohm))
> + */
> + *val = 1526;
> + *val2 = shunt;
> +
> + if (info->vsense_mode == PAC1711_FULL_RANGE_BIPOLAR)
> + *val = *val << 1;
> +
> + return IIO_VAL_FRACTIONAL;
[Severity: Medium]
Can *val2 overflow to a negative value in pac1711_read_raw() when info->shunt
exceeds INT_MAX?
Both pac1711_parse_fw() and pac1711_write_shunt_resistor() accept 32-bit
unsigned values up to U32_MAX into info->shunt, only rejecting zero. When
shunt > INT_MAX, assigning it to the signed int *val2 for IIO_VAL_FRACTIONAL
wraps *val2 negative, causing iio_format_value() to output a negative current
scale. Should shunt values greater than INT_MAX be rejected?
> + case PAC1711_VPOWER_REG_ADDR:
> + case PAC1711_VACC_REG_ADDR:
> + if (chan->type == IIO_CHARGE) {
> + /*
> + * Coulomb counter scale:
> + * 16-bit resolution: (100mV * NANO) / 2^16
> + * 12-bit resolution: (100mV * NANO) / 2^12
> + */
> + u64 ref = info->features->has_16bit_resolution ?
> + 1525878906250ULL : 24414062500000ULL;
> +
> + if (info->vsense_mode == PAC1711_FULL_RANGE_BIPOLAR)
> + ref = ref << 1;
> +
> + ref = div_u64(ref, shunt);
> + *val2 = do_div(ref, 1 * NANO);
> + *val = ref;
> +
> + return IIO_VAL_INT_PLUS_NANO;
> + }
> +
> + /*
> + * Power - uW - it will use the combined scale
> + * for current and voltage
> + * current(mA) * voltage(mV) = power (uW)
> + */
> + tmp = info->features->voltage_pv_fsr;
> + do_div(tmp, shunt);
> +
> + if (chan->type == IIO_ENERGY)
> + *val2 = info->features->has_16bit_resolution ? 32 : 24;
> + else
> + *val2 = 32;
> +
> + if (info->vsense_mode == PAC1711_FULL_RANGE_BIPOLAR)
> + *val2 -= 1;
> +
> + if (info->vbus_mode == PAC1711_FULL_RANGE_BIPOLAR)
> + *val2 -= 1;
> +
> + shift = fls64(tmp) - 31;
> + if (shift > 0) {
> + tmp >>= shift;
> + *val2 -= shift;
> + }
> +
> + *val = (int)tmp;
> +
> + return IIO_VAL_FRACTIONAL_LOG2;
[Severity: High]
Are the scale units for IIO_POWER and IIO_ENERGY in pac1711_read_raw() off by
factors of 1,000 and 1,000,000 respectively?
According to Documentation/ABI/testing/sysfs-bus-iio, power channels
(in_powerY_raw * in_powerY_scale) must be reported in milliwatts (mW), and
energy channels (in_energy_raw * in_energy_scale) must be reported in Joules
(J).
However, PAC1711_PRODUCT_VOLTAGE_PV_FSR (4200ULL * NANO = 4.2 * 10^12) and
PAC1721_PRODUCT_VOLTAGE_PV_FSR (900ULL * NANO = 9 * 10^11) compute the
numerator in microwatts * micro-ohms, as noted in the comment above. Dividing
by shunt (in micro-ohms) and returning IIO_VAL_FRACTIONAL_LOG2 with *val2 = 32
(or 24) yields a scale in microwatts for IIO_POWER (1,000x too large for mW)
and microjoules for IIO_ENERGY (1,000,000x too large for Joules).
[ ... ]
> +static int pac1711_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
> + int val, int val2, long mask)
> +{
> + struct pac1711_chip_info *info = iio_priv(indio_dev);
> + struct i2c_client *client = info->client;
> + struct device *dev = &info->client->dev;
> + int new_idx, ret, refresh_time;
> + s32 old_samp_rate;
> + __be16 val_be16;
> + u32 wait_time;
> + u16 val_u16;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + scoped_guard(mutex, &info->lock) {
> + old_samp_rate = pac1711_samp_rate_map_tbl[info->sample_rate_idx];
> + new_idx = pac1711_get_samp_rate_idx(val);
> + if (new_idx < 0)
> + return new_idx;
> +
> + ret = i2c_smbus_read_i2c_block_data(client, PAC1711_CTRL_ACT_REG_ADDR,
> + sizeof(val_u16), (u8 *)&val_be16);
> + if (ret != sizeof(val_u16)) {
> + dev_err(&client->dev, "cannot read regs from 0x%02X\n",
> + PAC1711_CTRL_ACT_REG_ADDR);
> + return ret < 0 ? ret : -EIO;
> + }
> +
> + val_u16 = be16_to_cpu(val_be16);
> + FIELD_MODIFY(PAC1711_CTRL_SAMPLE_MODE_MASK, &val_u16, new_idx);
> + val_be16 = cpu_to_be16(val_u16);
> + ret = i2c_smbus_write_i2c_block_data(client, PAC1711_CTRL_REG_ADDR,
> + sizeof(val_be16), (u8 *)&val_be16);
> + if (ret) {
> + dev_err(dev, "Failed to configure sampling mode in 0x%02X\n",
> + PAC1711_CTRL_ACT_REG_ADDR);
> + return ret;
> + }
> +
> + info->sample_rate_idx = new_idx;
> + info->chip_reg_data.ctrl_act_reg = val_u16;
> +
> + /* Force register snapshot and timestamp update with a refresh. */
> + refresh_time = msecs_to_jiffies(PAC1711_MIN_POLLING_TIME_MS) + 1;
> + info->chip_reg_data.jiffies_tstamp = jiffies - refresh_time;
> + wait_time = (1024 * 1000) / old_samp_rate;
> +
> + /*
> + * We must wait for cycle completion before updating the
> + * sample rate value.
> + */
> + ret = pac1711_reg_snapshot_locked(info, wait_time);
> + if (ret) {
> + dev_err(dev, "%s - cannot snapshot ctrl and measurement regs\n",
> + __func__);
> + return ret;
> + }
> + }
[Severity: Low]
In pac1711_write_raw(), info->sample_rate_idx is updated to new_idx before
pac1711_reg_snapshot_locked() sends the refresh command that activates the
new control register settings. If pac1711_reg_snapshot_locked() fails to send
the refresh command, won't the chip remain at old_samp_rate while
info->sample_rate_idx reports new_idx?
Also, in the error path of the i2c_smbus_write_i2c_block_data() call above,
dev_err() logs PAC1711_CTRL_ACT_REG_ADDR instead of PAC1711_CTRL_REG_ADDR.
[ ... ]
> +static int pac1711_probe(struct i2c_client *client)
> +{
> + struct device *dev = &client->dev;
> + struct pac1711_chip_info *info;
> + struct iio_dev *indio_dev;
> + u32 wait_time;
> + int ret, err;
> +
> + indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + info = iio_priv(indio_dev);
> + info->client = client;
> +
> + ret = pac1711_chip_identify(indio_dev, info);
[Severity: High]
Does pac1711_probe() need to enable the vdd-supply regulator before accessing
the chip over I2C?
Documentation/devicetree/bindings/iio/adc/microchip,pac1711.yaml requires
vdd-supply, but pac1711_probe() never calls devm_regulator_get_enable(dev,
"vdd") before calling pac1711_chip_identify() and pac1711_chip_configure(). If
the regulator is not already enabled by the bootloader, I2C communication will
fail during probe, or the regulator core may disable the unclaimed supply at
late_initcall while the device is in use.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261103-pac1711-v5-0-71c20101a52e@microchip.com?part=5
next prev parent reply other threads:[~2026-11-03 14:59 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-03 14:37 [PATCH v5 0/5] Add support for Microchip PAC1711 Power Monitor Ariana Lazar
2026-11-03 14:37 ` [PATCH v5 1/5] unaligned: introduce be56 unaligned accessors Ariana Lazar
2026-11-04 7:54 ` Joshua Crofts
2026-11-03 14:37 ` [PATCH v5 2/5] iio: core: add IIO_CHARGE channel type Ariana Lazar
2026-11-03 16:13 ` Andy Shevchenko
2026-11-03 14:37 ` [PATCH v5 3/5] iio: core: add iio_fill_val_int_64() helper Ariana Lazar
2026-11-03 14:42 ` sashiko-bot
2026-11-03 16:14 ` Andy Shevchenko
2026-11-03 14:37 ` [PATCH v5 4/5] dt-bindings: iio: adc: add support for PAC1711 Ariana Lazar
2026-11-03 14:37 ` [PATCH v5 5/5] " Ariana Lazar
2026-11-03 14:59 ` sashiko-bot [this message]
2026-11-03 16:40 ` Andy Shevchenko
2026-11-04 1:36 ` Jonathan Cameron
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-195525@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=ariana.lazar@microchip.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=linux-hwmon@vger.kernel.org \
--cc=robh@kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox