From: Ferdinand Schwenk Date: Tue, 9 Jun 2026 19:43:12 +0000 (+0200) Subject: hwmon: (ina238) Add update_interval_us attribute X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=b47f4a72c62f2db5995ee4c7adfcd876faadf328;p=thirdparty%2Flinux.git hwmon: (ina238) Add update_interval_us attribute The INA238 family supports eight conversion time steps from 50 us to 4120 us (SQ52206: 66 us to 8230 us). At the millisecond granularity of update_interval, the four shortest steps (50, 84, 150, 280 us) all round to the same value and cannot be individually selected. Add support for the generic update_interval_us attribute, which reports and programs the same ADC cycle time as update_interval but in microseconds, giving userspace full access to all conversion time steps. Both attributes reflect the total cycle time including the active averaging count: the reported value is the raw conversion time multiplied by the number of averaged samples, and writes apply the inverse mapping. Signed-off-by: Ferdinand Schwenk Link: https://lore.kernel.org/r/20260609-hwmon-ina238-update-interval-us-v2-v3-3-016b55567950@advastore.com [groeck: Fixed some multi-line alignment issues] Signed-off-by: Guenter Roeck --- diff --git a/Documentation/hwmon/ina238.rst b/Documentation/hwmon/ina238.rst index 43950d1ec551f..a75b79e17d9df 100644 --- a/Documentation/hwmon/ina238.rst +++ b/Documentation/hwmon/ina238.rst @@ -106,4 +106,8 @@ energy1_input Energy measurement (uJ) temp1_input Die temperature measurement (mC) temp1_max Maximum die temperature threshold (mC) temp1_max_alarm Maximum die temperature alarm + +samples ADC averaging count (1, 4, 16, 64, 128, 256, 512, 1024) +update_interval Total ADC conversion cycle time including averaging (ms) +update_interval_us Total ADC conversion cycle time including averaging (us) ======================= ======================================================= diff --git a/drivers/hwmon/ina238.c b/drivers/hwmon/ina238.c index 1790126800800..3632bbca607ee 100644 --- a/drivers/hwmon/ina238.c +++ b/drivers/hwmon/ina238.c @@ -316,19 +316,36 @@ static inline u32 ina238_samples(struct ina238_data *data) INA238_ADC_CONFIG_AVG_SHIFT]; } -/* Converting update_interval in msec to a single conversion time in usec */ -static inline u32 ina238_interval_ms_to_conv_time(long interval, u32 samples) +/* Converting update_interval(_us) to a per-field conversion time in usec. + * interval_us is the total ADC cycle time including averaging in microseconds. + * All three conversion fields (VBUSCT, VSHCT, VTCT) are set equal, so the + * per-field time is interval_us / (samples * 3). + */ +static inline u32 ina238_interval_us_to_conv_time(u32 interval_us, u32 samples) { - u64 interval_us; + return DIV_ROUND_CLOSEST_ULL(interval_us, samples * 3); +} - interval = clamp_val(interval, 0, INT_MAX / 1000); - interval_us = (u64)interval * 1000; +/* Write a per-field conversion time (in usec) to the ADC_CONFIG register */ +static int ina238_write_conv_time(struct ina238_data *data, u32 conv_time_us) +{ + u16 adc_config; + int idx, ret; - /* - * update_interval reports the ADC cycle time including averaging. - * The target per-field conversion time is interval_us / (samples * 3). - */ - return DIV_ROUND_CLOSEST_ULL(interval_us, samples * 3); + idx = find_closest(conv_time_us, data->config->conv_time, + ARRAY_SIZE(ina238_conv_time)); + adc_config = (data->adc_config & + ~(INA238_ADC_CONFIG_VBUSCT_MASK | + INA238_ADC_CONFIG_VSHCT_MASK | + INA238_ADC_CONFIG_VTCT_MASK)) | + ((u16)idx << INA238_ADC_CONFIG_VBUSCT_SHIFT) | + ((u16)idx << INA238_ADC_CONFIG_VSHCT_SHIFT) | + ((u16)idx << INA238_ADC_CONFIG_VTCT_SHIFT); + ret = regmap_write(data->regmap, INA238_ADC_CONFIG, adc_config); + if (ret) + return ret; + data->adc_config = adc_config; + return 0; } static int ina238_read_chip(struct device *dev, u32 attr, long *val) @@ -344,6 +361,10 @@ static int ina238_read_chip(struct device *dev, u32 attr, long *val) *val = DIV_ROUND_CLOSEST(ina238_reg_to_interval_us(data) * ina238_samples(data), 1000); return 0; + case hwmon_chip_update_interval_us: + /* Return in usec */ + *val = ina238_reg_to_interval_us(data) * ina238_samples(data); + return 0; default: return -EOPNOTSUPP; } @@ -367,21 +388,14 @@ static int ina238_write_chip(struct device *dev, u32 attr, long val) data->adc_config = adc_config; return 0; case hwmon_chip_update_interval: - val = ina238_interval_ms_to_conv_time(val, ina238_samples(data)); - idx = find_closest(val, data->config->conv_time, - ARRAY_SIZE(ina238_conv_time)); - adc_config = (data->adc_config & - ~(INA238_ADC_CONFIG_VBUSCT_MASK | - INA238_ADC_CONFIG_VSHCT_MASK | - INA238_ADC_CONFIG_VTCT_MASK)) | - ((u16)idx << INA238_ADC_CONFIG_VBUSCT_SHIFT) | - ((u16)idx << INA238_ADC_CONFIG_VSHCT_SHIFT) | - ((u16)idx << INA238_ADC_CONFIG_VTCT_SHIFT); - ret = regmap_write(data->regmap, INA238_ADC_CONFIG, adc_config); - if (ret) - return ret; - data->adc_config = adc_config; - return 0; + /* Convert ms to us before passing to the shared helper */ + val = clamp_val(val, 0, INT_MAX / 1000) * 1000; + return ina238_write_conv_time(data, + ina238_interval_us_to_conv_time((u32)val, ina238_samples(data))); + case hwmon_chip_update_interval_us: + val = clamp_val(val, 0, INT_MAX); + return ina238_write_conv_time(data, + ina238_interval_us_to_conv_time((u32)val, ina238_samples(data))); default: return -EOPNOTSUPP; } @@ -763,6 +777,7 @@ static umode_t ina238_is_visible(const void *drvdata, switch (attr) { case hwmon_chip_samples: case hwmon_chip_update_interval: + case hwmon_chip_update_interval_us: return 0644; default: return 0; @@ -831,7 +846,8 @@ static umode_t ina238_is_visible(const void *drvdata, static const struct hwmon_channel_info * const ina238_info[] = { HWMON_CHANNEL_INFO(chip, - HWMON_C_SAMPLES | HWMON_C_UPDATE_INTERVAL), + HWMON_C_SAMPLES | HWMON_C_UPDATE_INTERVAL | + HWMON_C_UPDATE_INTERVAL_US), HWMON_CHANNEL_INFO(in, /* 0: shunt voltage */ INA238_HWMON_IN_CONFIG,