]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
iio: consumers: Add an iio_multiply_value() helper function
authorHans de Goede <hansg@kernel.org>
Sun, 31 Aug 2025 10:48:22 +0000 (12:48 +0200)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Wed, 10 Sep 2025 18:47:04 +0000 (19:47 +0100)
The channel-scale handling in iio_convert_raw_to_processed() in essence
does the following:

processed  = raw * caller-provided-scale * channel-scale

Which can also be written as:

multiplier = raw * caller-provided-scale
iio-value  = channel-scale
processed  = multiplier * iio-value

Where iio-value is a set of IIO_VAL_* type + val + val2 integers, being
able to handle multiplication of iio-values like this is something
which is useful to have in general and, as previous bugfixes to
iio_convert_raw_to_processed() have shown, also tricky to implement.

Split the iio-value multiplication code from iio_convert_raw_to_processed()
out into a new iio_multiply_value() helper. This serves multiple purposes:

1. Having this split out allows writing a KUnit test for this.
2. Having this split out allows re-use to get better precision
   when scaling values in iio_read_channel_processed_scale().

Reviewed-by: Andy Shevchenko <andy@kernel.org>
Signed-off-by: Hans de Goede <hansg@kernel.org>
Link: https://patch.msgid.link/20250831104825.15097-4-hansg@kernel.org
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/inkern.c
include/linux/iio/consumer.h

index 642beb4b3360d20a4ded1b232cde87f7ccc8b380..158d54de14a799bb52b18af471509c0b5222f3c3 100644 (file)
@@ -599,13 +599,50 @@ int iio_read_channel_average_raw(struct iio_channel *chan, int *val)
 }
 EXPORT_SYMBOL_GPL(iio_read_channel_average_raw);
 
+int iio_multiply_value(int *result, s64 multiplier,
+                      unsigned int type, int val, int val2)
+{
+       s64 denominator;
+
+       switch (type) {
+       case IIO_VAL_INT:
+               *result = multiplier * val;
+               return IIO_VAL_INT;
+       case IIO_VAL_INT_PLUS_MICRO:
+       case IIO_VAL_INT_PLUS_NANO:
+               switch (type) {
+               case IIO_VAL_INT_PLUS_MICRO:
+                       denominator = MICRO;
+                       break;
+               case IIO_VAL_INT_PLUS_NANO:
+                       denominator = NANO;
+                       break;
+               }
+               *result = multiplier * abs(val);
+               *result += div_s64(multiplier * abs(val2), denominator);
+               if (val < 0 || val2 < 0)
+                       *result *= -1;
+               return IIO_VAL_INT;
+       case IIO_VAL_FRACTIONAL:
+               *result = div_s64(multiplier * val, val2);
+               return IIO_VAL_INT;
+       case IIO_VAL_FRACTIONAL_LOG2:
+               *result = (multiplier * val) >> val2;
+               return IIO_VAL_INT;
+       default:
+               return -EINVAL;
+       }
+}
+EXPORT_SYMBOL_NS_GPL(iio_multiply_value, "IIO_UNIT_TEST");
+
 static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan,
                                                 int raw, int *processed,
                                                 unsigned int scale)
 {
        int scale_type, scale_val, scale_val2;
        int offset_type, offset_val, offset_val2;
-       s64 denominator, raw64 = raw;
+       s64 raw64 = raw;
+       int ret;
 
        offset_type = iio_channel_read(chan, &offset_val, &offset_val2,
                                       IIO_CHAN_INFO_OFFSET);
@@ -644,35 +681,10 @@ static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan,
                return 0;
        }
 
-       switch (scale_type) {
-       case IIO_VAL_INT:
-               *processed = raw64 * scale_val * scale;
-               break;
-       case IIO_VAL_INT_PLUS_MICRO:
-       case IIO_VAL_INT_PLUS_NANO:
-               switch (scale_type) {
-               case IIO_VAL_INT_PLUS_MICRO:
-                       denominator = MICRO;
-                       break;
-               case IIO_VAL_INT_PLUS_NANO:
-                       denominator = NANO;
-                       break;
-               }
-               *processed = raw64 * scale * abs(scale_val);
-               *processed += div_s64(raw64 * scale * abs(scale_val2), denominator);
-               if (scale_val < 0 || scale_val2 < 0)
-                       *processed *= -1;
-               break;
-       case IIO_VAL_FRACTIONAL:
-               *processed = div_s64(raw64 * (s64)scale_val * scale,
-                                    scale_val2);
-               break;
-       case IIO_VAL_FRACTIONAL_LOG2:
-               *processed = (raw64 * (s64)scale_val * scale) >> scale_val2;
-               break;
-       default:
-               return -EINVAL;
-       }
+       ret = iio_multiply_value(processed, raw64 * scale,
+                                scale_type, scale_val, scale_val2);
+       if (ret < 0)
+               return ret;
 
        return 0;
 }
index 6a44796164792b2dd930f8168b14de327a80a6f7..a38b277c2c02cb85a766d645a046bdfa534db51a 100644 (file)
@@ -381,6 +381,24 @@ int iio_read_channel_offset(struct iio_channel *chan, int *val,
 int iio_read_channel_scale(struct iio_channel *chan, int *val,
                           int *val2);
 
+/**
+ * iio_multiply_value() - Multiply an IIO value
+ * @result:    Destination pointer for the multiplication result
+ * @multiplier:        Multiplier.
+ * @type:      One of the IIO_VAL_* constants. This decides how the @val and
+ *             @val2 parameters are interpreted.
+ * @val:       Value being multiplied.
+ * @val2:      Value being multiplied. @val2 use depends on type.
+ *
+ * Multiply an IIO value with a s64 multiplier storing the result as
+ * IIO_VAL_INT. This is typically used for scaling.
+ *
+ * Returns:
+ * IIO_VAL_INT on success or a negative error-number on failure.
+ */
+int iio_multiply_value(int *result, s64 multiplier,
+                      unsigned int type, int val, int val2);
+
 /**
  * iio_convert_raw_to_processed() - Converts a raw value to a processed value
  * @chan:              The channel being queried