Re: [PATCH] hwmon (ina3221) Add single-shot mode support
From: Guenter Roeck
Date: Mon Nov 12 2018 - 23:33:03 EST
On Mon, Nov 12, 2018 at 08:23:53PM -0800, Nicolin Chen wrote:
> INA3221 supports both continuous and single-shot modes. When
> running in the continuous mode, it keeps measuring the inputs
> and converting them to the data register even if there are no
> users reading the data out. In this use case, this could be a
> power waste.
>
> So this patch adds a single-shot mode support so that ina3221
> could do measurement and conversion only if users trigger it,
> depending on the use case where it only needs to poll data in
> a lower frequency.
>
> The change also exposes "mode" and "available_modes" nodes to
> allow users to switch between two operating modes.
>
Lots and lots of complexity for little gain. Sorry, I don't see
the point of this change.
Guenter
> Signed-off-by: Nicolin Chen <nicoleotsuka@xxxxxxxxx>
> ---
> Documentation/hwmon/ina3221 | 3 +
> drivers/hwmon/ina3221.c | 109 ++++++++++++++++++++++++++++++++++++
> 2 files changed, 112 insertions(+)
>
> diff --git a/Documentation/hwmon/ina3221 b/Documentation/hwmon/ina3221
> index 4b82cbfb551c..b03f4ad901ee 100644
> --- a/Documentation/hwmon/ina3221
> +++ b/Documentation/hwmon/ina3221
> @@ -35,3 +35,6 @@ curr[123]_max Warning alert current(mA) setting, activates the
> average is above this value.
> curr[123]_max_alarm Warning alert current limit exceeded
> in[456]_input Shunt voltage(uV) for channels 1, 2, and 3 respectively
> +available_modes Available operating modes of the chip:
> + continuous mode; single-shot mode
> +mode Current operating mode of the chip
> diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c
> index 17a57dbc0424..8f7da3f75d53 100644
> --- a/drivers/hwmon/ina3221.c
> +++ b/drivers/hwmon/ina3221.c
> @@ -91,6 +91,17 @@ enum ina3221_channels {
> INA3221_NUM_CHANNELS
> };
>
> +enum ina3221_modes {
> + INA3221_MODE_SINGLE_SHOT,
> + INA3221_MODE_CONTINUOUS,
> + INA3221_NUM_MODES,
> +};
> +
> +static const char * const ina3221_mode_names[] = {
> + [INA3221_MODE_SINGLE_SHOT] = "single-shot",
> + [INA3221_MODE_CONTINUOUS] = "continuous",
> +};
> +
> /**
> * struct ina3221_input - channel input source specific information
> * @label: label of channel input source
> @@ -127,6 +138,11 @@ static inline bool ina3221_is_enabled(struct ina3221_data *ina, int channel)
> (ina->reg_config & INA3221_CONFIG_CHx_EN(channel));
> }
>
> +static inline bool ina3221_is_singleshot_mode(struct ina3221_data *ina)
> +{
> + return !(ina->reg_config & INA3221_CONFIG_MODE_CONTINUOUS);
> +}
> +
> /* Lookup table for Bus and Shunt conversion times in usec */
> static const u16 ina3221_conv_time[] = {
> 140, 204, 332, 588, 1100, 2116, 4156, 8244,
> @@ -188,6 +204,11 @@ static int ina3221_read_in(struct device *dev, u32 attr, int channel, long *val)
> if (!ina3221_is_enabled(ina, channel))
> return -ENODATA;
>
> + /* Write CONFIG register to trigger a single-shot measurement */
> + if (ina3221_is_singleshot_mode(ina))
> + regmap_write(ina->regmap, INA3221_CONFIG,
> + ina->reg_config);
> +
> ret = ina3221_wait_for_data(ina);
> if (ret)
> return ret;
> @@ -232,6 +253,11 @@ static int ina3221_read_curr(struct device *dev, u32 attr,
> if (!ina3221_is_enabled(ina, channel))
> return -ENODATA;
>
> + /* Write CONFIG register to trigger a single-shot measurement */
> + if (ina3221_is_singleshot_mode(ina))
> + regmap_write(ina->regmap, INA3221_CONFIG,
> + ina->reg_config);
> +
> ret = ina3221_wait_for_data(ina);
> if (ret)
> return ret;
> @@ -532,6 +558,82 @@ static ssize_t ina3221_set_shunt(struct device *dev,
> return count;
> }
>
> +static ssize_t available_modes_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(ina3221_mode_names); i++)
> + snprintf(buf, PAGE_SIZE, "%s%s ", buf, ina3221_mode_names[i]);
> +
> + return snprintf(buf, PAGE_SIZE, "%s\n", buf);
> +}
> +
> +static ssize_t mode_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct ina3221_data *ina = dev_get_drvdata(dev);
> + int mode;
> +
> + if (ina->reg_config & INA3221_CONFIG_MODE_CONTINUOUS)
> + mode = INA3221_MODE_CONTINUOUS;
> + else
> + mode = INA3221_MODE_SINGLE_SHOT;
> +
> + return snprintf(buf, PAGE_SIZE, "%s\n", ina3221_mode_names[mode]);
> +}
> +
> +static ssize_t mode_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct ina3221_data *ina = dev_get_drvdata(dev);
> + u16 mask = INA3221_CONFIG_MODE_CONTINUOUS;
> + u16 continuous;
> + int mode, ret;
> + char name[32];
> +
> + mutex_lock(&ina->lock);
> +
> + snprintf(name, sizeof(name), "%s", buf);
> + strim(name);
> +
> + for (mode = 0; mode < INA3221_NUM_MODES; mode++) {
> + if (!strcmp(name, ina3221_mode_names[mode]))
> + break;
> + }
> +
> + switch (mode) {
> + case INA3221_MODE_SINGLE_SHOT:
> + continuous = 0;
> + break;
> + case INA3221_MODE_CONTINUOUS:
> + continuous = INA3221_CONFIG_MODE_CONTINUOUS;
> + break;
> + default:
> + mutex_unlock(&ina->lock);
> + return -EINVAL;
> + }
> +
> + /* Set register to configure single-shot or continuous mode */
> + ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, mask, continuous);
> + if (ret) {
> + mutex_unlock(&ina->lock);
> + return ret;
> + }
> +
> + /* Cache the latest config register value */
> + ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config);
> + if (ret) {
> + mutex_unlock(&ina->lock);
> + return ret;
> + }
> +
> + mutex_unlock(&ina->lock);
> +
> + return count;
> +}
> +
> /* shunt resistance */
> static SENSOR_DEVICE_ATTR(shunt1_resistor, S_IRUGO | S_IWUSR,
> ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL1);
> @@ -540,10 +642,17 @@ static SENSOR_DEVICE_ATTR(shunt2_resistor, S_IRUGO | S_IWUSR,
> static SENSOR_DEVICE_ATTR(shunt3_resistor, S_IRUGO | S_IWUSR,
> ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL3);
>
> +/* operating mode */
> +static DEVICE_ATTR_RW(mode);
> +static DEVICE_ATTR_RO(available_modes);
> +
> static struct attribute *ina3221_attrs[] = {
> &sensor_dev_attr_shunt1_resistor.dev_attr.attr,
> &sensor_dev_attr_shunt2_resistor.dev_attr.attr,
> &sensor_dev_attr_shunt3_resistor.dev_attr.attr,
> + /* common attributes */
> + &dev_attr_mode.attr,
> + &dev_attr_available_modes.attr,
> NULL,
> };
> ATTRIBUTE_GROUPS(ina3221);
> --
> 2.17.1
>