[PATCH v8 5/5] hwmon: ina2xx: allow to change the averaging rate at run-time
The averaging rate of ina226 is hardcoded to 16 in the driver. Make it modifiable at run-time via a new sysfs attribute. While we're at it - add an additional variable to ina2xx_data, which holds the current configuration settings - this way we'll be able to restore the configuration in case of an unexpected chip reset. Signed-off-by: Bartosz Golaszewski --- Documentation/hwmon/ina2xx | 3 ++ drivers/hwmon/ina2xx.c | 129 +++-- 2 files changed, 128 insertions(+), 4 deletions(-) diff --git a/Documentation/hwmon/ina2xx b/Documentation/hwmon/ina2xx index 320dd69..a11256d 100644 --- a/Documentation/hwmon/ina2xx +++ b/Documentation/hwmon/ina2xx @@ -48,3 +48,6 @@ The shunt value in micro-ohms can be set via platform data or device tree at compile-time or via the shunt_resistor attribute in sysfs at run-time. Please refer to the Documentation/devicetree/bindings/i2c/ina2xx.txt for bindings if the device tree is used. + +The averaging rate of INA226 and INA230 can be changed via the avg_rate sysfs +attribute. The available rates are: 1, 4, 16, 64, 128, 256, 512 and 1024. diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 49537ea..6f6aebd 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -68,6 +68,15 @@ #define INA2XX_RSHUNT_DEFAULT 1 +/* bit masks for the averaging setting in the configuration register */ +#define INA226_AVG_RD_MASK 0x0E00 +#define INA226_AVG_WR_MASK 0xF1FF + +#define INA226_READ_AVG(reg) ((reg & INA226_AVG_RD_MASK) >> 9) + +/* common attrs, ina226 attrs and NULL */ +#define INA2XX_MAX_ATTRIBUTE_GROUPS3 + enum ina2xx_ids { ina219, ina226 }; struct ina2xx_config { @@ -85,12 +94,14 @@ struct ina2xx_data { const struct ina2xx_config *config; long rshunt; + u16 curr_config; struct mutex update_lock; bool valid; unsigned long last_updated; int kind; + const struct attribute_group *groups[INA2XX_MAX_ATTRIBUTE_GROUPS]; u16 regs[INA2XX_MAX_REGISTERS]; }; @@ -115,6 +126,39 @@ static const struct ina2xx_config ina2xx_config[] = { }, }; +/* + * Available averaging rates for ina226. The indices correspond with + * the bit values expected by the chip (according to the ina226 datasheet, + * table 3 AVG bit settings, found at + * http://www.ti.com/lit/ds/symlink/ina226.pdf. + */ +static const int ina226_avg_tab[] = { 1, 4, 16, 64, 128, 256, 512, 1024 }; + +static int ina226_avg_bits(int avg) +{ + int i; + + /* Get the closest average from the tab. */ + for (i = 0; i < ARRAY_SIZE(ina226_avg_tab) - 1; i++) { + if (avg <= (ina226_avg_tab[i] + ina226_avg_tab[i + 1]) / 2) + return i; + } + + return i; /* Return 0b0111 for other values. */ +} + +static int ina226_avg_val(int bits) +{ + /* +* Value read from the config register should be correct, but do check +* the boundary just in case. +*/ + if (bits >= ARRAY_SIZE(ina226_avg_tab)) + return -ENODEV; + + return ina226_avg_tab[bits]; +} + static int ina2xx_calibrate(struct ina2xx_data *data) { return i2c_smbus_write_word_swapped(data->client, INA2XX_CALIBRATION, @@ -131,7 +175,7 @@ static int ina2xx_init(struct ina2xx_data *data) /* device configuration */ ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG, - data->config->config_default); + data->curr_config); if (ret < 0) return ret; @@ -292,6 +336,62 @@ static ssize_t ina2xx_set_shunt(struct device *dev, return count; } +static ssize_t ina226_show_avg(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct ina2xx_data *data = ina2xx_update_device(dev); + int avg, i; + + if (IS_ERR(data)) + return PTR_ERR(data); + + avg = ina226_avg_val(INA226_READ_AVG(data->regs[INA2XX_CONFIG])); + if (avg < 0) + return avg; + + for (i = 0; i < ARRAY_SIZE(ina226_avg_tab); i++) { + if (avg == ina226_avg_tab[i]) + break; + } + + return snprintf(buf, PAGE_SIZE, "%d\n", ina226_avg_tab[i]); +} + +static ssize_t ina226_set_avg(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct ina2xx_data *data = dev_get_drvdata(dev); + unsigned long val; + int status, avg; + + if (IS_ERR(data)) + return PTR_ERR(data); + + status = kstrtoul(buf, 10, ); + if (status < 0) + return status; + + if (val > INT_MAX || val == 0) + return -EINVAL; + + avg = ina226_avg_bits(val); + + mutex_lock(>update_lock); +
[PATCH v8 5/5] hwmon: ina2xx: allow to change the averaging rate at run-time
The averaging rate of ina226 is hardcoded to 16 in the driver. Make it modifiable at run-time via a new sysfs attribute. While we're at it - add an additional variable to ina2xx_data, which holds the current configuration settings - this way we'll be able to restore the configuration in case of an unexpected chip reset. Signed-off-by: Bartosz Golaszewski bgolaszew...@baylibre.com --- Documentation/hwmon/ina2xx | 3 ++ drivers/hwmon/ina2xx.c | 129 +++-- 2 files changed, 128 insertions(+), 4 deletions(-) diff --git a/Documentation/hwmon/ina2xx b/Documentation/hwmon/ina2xx index 320dd69..a11256d 100644 --- a/Documentation/hwmon/ina2xx +++ b/Documentation/hwmon/ina2xx @@ -48,3 +48,6 @@ The shunt value in micro-ohms can be set via platform data or device tree at compile-time or via the shunt_resistor attribute in sysfs at run-time. Please refer to the Documentation/devicetree/bindings/i2c/ina2xx.txt for bindings if the device tree is used. + +The averaging rate of INA226 and INA230 can be changed via the avg_rate sysfs +attribute. The available rates are: 1, 4, 16, 64, 128, 256, 512 and 1024. diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 49537ea..6f6aebd 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -68,6 +68,15 @@ #define INA2XX_RSHUNT_DEFAULT 1 +/* bit masks for the averaging setting in the configuration register */ +#define INA226_AVG_RD_MASK 0x0E00 +#define INA226_AVG_WR_MASK 0xF1FF + +#define INA226_READ_AVG(reg) ((reg INA226_AVG_RD_MASK) 9) + +/* common attrs, ina226 attrs and NULL */ +#define INA2XX_MAX_ATTRIBUTE_GROUPS3 + enum ina2xx_ids { ina219, ina226 }; struct ina2xx_config { @@ -85,12 +94,14 @@ struct ina2xx_data { const struct ina2xx_config *config; long rshunt; + u16 curr_config; struct mutex update_lock; bool valid; unsigned long last_updated; int kind; + const struct attribute_group *groups[INA2XX_MAX_ATTRIBUTE_GROUPS]; u16 regs[INA2XX_MAX_REGISTERS]; }; @@ -115,6 +126,39 @@ static const struct ina2xx_config ina2xx_config[] = { }, }; +/* + * Available averaging rates for ina226. The indices correspond with + * the bit values expected by the chip (according to the ina226 datasheet, + * table 3 AVG bit settings, found at + * http://www.ti.com/lit/ds/symlink/ina226.pdf. + */ +static const int ina226_avg_tab[] = { 1, 4, 16, 64, 128, 256, 512, 1024 }; + +static int ina226_avg_bits(int avg) +{ + int i; + + /* Get the closest average from the tab. */ + for (i = 0; i ARRAY_SIZE(ina226_avg_tab) - 1; i++) { + if (avg = (ina226_avg_tab[i] + ina226_avg_tab[i + 1]) / 2) + return i; + } + + return i; /* Return 0b0111 for other values. */ +} + +static int ina226_avg_val(int bits) +{ + /* +* Value read from the config register should be correct, but do check +* the boundary just in case. +*/ + if (bits = ARRAY_SIZE(ina226_avg_tab)) + return -ENODEV; + + return ina226_avg_tab[bits]; +} + static int ina2xx_calibrate(struct ina2xx_data *data) { return i2c_smbus_write_word_swapped(data-client, INA2XX_CALIBRATION, @@ -131,7 +175,7 @@ static int ina2xx_init(struct ina2xx_data *data) /* device configuration */ ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG, - data-config-config_default); + data-curr_config); if (ret 0) return ret; @@ -292,6 +336,62 @@ static ssize_t ina2xx_set_shunt(struct device *dev, return count; } +static ssize_t ina226_show_avg(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct ina2xx_data *data = ina2xx_update_device(dev); + int avg, i; + + if (IS_ERR(data)) + return PTR_ERR(data); + + avg = ina226_avg_val(INA226_READ_AVG(data-regs[INA2XX_CONFIG])); + if (avg 0) + return avg; + + for (i = 0; i ARRAY_SIZE(ina226_avg_tab); i++) { + if (avg == ina226_avg_tab[i]) + break; + } + + return snprintf(buf, PAGE_SIZE, %d\n, ina226_avg_tab[i]); +} + +static ssize_t ina226_set_avg(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct ina2xx_data *data = dev_get_drvdata(dev); + unsigned long val; + int status, avg; + + if (IS_ERR(data)) + return PTR_ERR(data); + + status = kstrtoul(buf, 10, val); + if (status 0) + return status; + + if (val INT_MAX || val == 0) + return -EINVAL; + + avg = ina226_avg_bits(val); + + mutex_lock(data-update_lock);