iio: afe: rescale: add offset support
authorLiam Beguin <liambeguin@gmail.com>
Sun, 13 Feb 2022 02:57:32 +0000 (21:57 -0500)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Sun, 27 Feb 2022 13:38:10 +0000 (13:38 +0000)
This is a preparatory change required for the addition of temperature
sensing front ends.

Signed-off-by: Liam Beguin <liambeguin@gmail.com>
Reviewed-by: Peter Rosin <peda@axentia.se>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/20220213025739.2561834-4-liambeguin@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/afe/iio-rescale.c
include/linux/iio/afe/rescale.h

index e67d9a9..8eaf766 100644 (file)
@@ -3,6 +3,7 @@
  * IIO rescale driver
  *
  * Copyright (C) 2018 Axentia Technologies AB
+ * Copyright (C) 2022 Liam Beguin <liambeguin@gmail.com>
  *
  * Author: Peter Rosin <peda@axentia.se>
  */
@@ -81,11 +82,46 @@ int rescale_process_scale(struct rescale *rescale, int scale_type,
        }
 }
 
+int rescale_process_offset(struct rescale *rescale, int scale_type,
+                          int scale, int scale2, int schan_off,
+                          int *val, int *val2)
+{
+       s64 tmp, tmp2;
+
+       switch (scale_type) {
+       case IIO_VAL_FRACTIONAL:
+               tmp = (s64)rescale->offset * scale2;
+               *val = div_s64(tmp, scale) + schan_off;
+               return IIO_VAL_INT;
+       case IIO_VAL_INT:
+               *val = div_s64(rescale->offset, scale) + schan_off;
+               return IIO_VAL_INT;
+       case IIO_VAL_FRACTIONAL_LOG2:
+               tmp = (s64)rescale->offset * (1 << scale2);
+               *val = div_s64(tmp, scale) + schan_off;
+               return IIO_VAL_INT;
+       case IIO_VAL_INT_PLUS_NANO:
+               tmp = (s64)rescale->offset * 1000000000LL;
+               tmp2 = ((s64)scale * 1000000000LL) + scale2;
+               *val = div64_s64(tmp, tmp2) + schan_off;
+               return IIO_VAL_INT;
+       case IIO_VAL_INT_PLUS_MICRO:
+               tmp = (s64)rescale->offset * 1000000LL;
+               tmp2 = ((s64)scale * 1000000LL) + scale2;
+               *val = div64_s64(tmp, tmp2) + schan_off;
+               return IIO_VAL_INT;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
 static int rescale_read_raw(struct iio_dev *indio_dev,
                            struct iio_chan_spec const *chan,
                            int *val, int *val2, long mask)
 {
        struct rescale *rescale = iio_priv(indio_dev);
+       int scale, scale2;
+       int schan_off = 0;
        int ret;
 
        switch (mask) {
@@ -112,6 +148,47 @@ static int rescale_read_raw(struct iio_dev *indio_dev,
                        ret = iio_read_channel_scale(rescale->source, val, val2);
                }
                return rescale_process_scale(rescale, ret, val, val2);
+       case IIO_CHAN_INFO_OFFSET:
+               /*
+                * Processed channels are scaled 1-to-1 and source offset is
+                * already taken into account.
+                *
+                * In other cases, real world measurement are expressed as:
+                *
+                *      schan_scale * (raw + schan_offset)
+                *
+                * Given that the rescaler parameters are applied recursively:
+                *
+                *      rescaler_scale * (schan_scale * (raw + schan_offset) +
+                *              rescaler_offset)
+                *
+                * Or,
+                *
+                *      (rescaler_scale * schan_scale) * (raw +
+                *              (schan_offset + rescaler_offset / schan_scale)
+                *
+                * Thus, reusing the original expression the parameters exposed
+                * to userspace are:
+                *
+                *      scale = schan_scale * rescaler_scale
+                *      offset = schan_offset + rescaler_offset / schan_scale
+                */
+               if (rescale->chan_processed) {
+                       *val = rescale->offset;
+                       return IIO_VAL_INT;
+               }
+
+               if (iio_channel_has_info(rescale->source->channel,
+                                        IIO_CHAN_INFO_OFFSET)) {
+                       ret = iio_read_channel_offset(rescale->source,
+                                                     &schan_off, NULL);
+                       if (ret != IIO_VAL_INT)
+                               return ret < 0 ? ret : -EOPNOTSUPP;
+               }
+
+               ret = iio_read_channel_scale(rescale->source, &scale, &scale2);
+               return rescale_process_offset(rescale, ret, scale, scale2,
+                                             schan_off, val, val2);
        default:
                return -EINVAL;
        }
@@ -188,6 +265,9 @@ static int rescale_configure_channel(struct device *dev,
        chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
                BIT(IIO_CHAN_INFO_SCALE);
 
+       if (rescale->offset)
+               chan->info_mask_separate |= BIT(IIO_CHAN_INFO_OFFSET);
+
        /*
         * Using .read_avail() is fringe to begin with and makes no sense
         * whatsoever for processed channels, so we make sure that this cannot
@@ -352,6 +432,7 @@ static int rescale_probe(struct platform_device *pdev)
        rescale->cfg = of_device_get_match_data(dev);
        rescale->numerator = 1;
        rescale->denominator = 1;
+       rescale->offset = 0;
 
        ret = rescale->cfg->props(dev, rescale);
        if (ret)
index 8a2eb34..6eecb43 100644 (file)
@@ -25,8 +25,12 @@ struct rescale {
        bool chan_processed;
        s32 numerator;
        s32 denominator;
+       s32 offset;
 };
 
 int rescale_process_scale(struct rescale *rescale, int scale_type,
                          int *val, int *val2);
+int rescale_process_offset(struct rescale *rescale, int scale_type,
+                          int scale, int scale2, int schan_off,
+                          int *val, int *val2);
 #endif /* __IIO_RESCALE_H__ */