]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
hwmon: (ina238) Add update_interval_us attribute
authorFerdinand Schwenk <ferdinand.schwenk@advastore.com>
Tue, 9 Jun 2026 19:43:12 +0000 (21:43 +0200)
committerGuenter Roeck <linux@roeck-us.net>
Tue, 9 Jun 2026 20:48:19 +0000 (13:48 -0700)
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 <ferdinand.schwenk@advastore.com>
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 <linux@roeck-us.net>
Documentation/hwmon/ina238.rst
drivers/hwmon/ina238.c

index 43950d1ec551f7f93a5110a40a4e007b49894cba..a75b79e17d9dfa5dec7210448174cfd587a497d4 100644 (file)
@@ -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)
 ======================= =======================================================
index 1790126800800e68b909f23ecd96f33db02f4213..3632bbca607ee66bc5b498e663df6f07967b5e19 100644 (file)
@@ -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,