Commit a29c3283 authored by Liam Beguin's avatar Liam Beguin Committed by Jonathan Cameron

iio: afe: rescale: add offset support

This is a preparatory change required for the addition of temperature
sensing front ends.
Signed-off-by: default avatarLiam Beguin <liambeguin@gmail.com>
Reviewed-by: default avatarPeter Rosin <peda@axentia.se>
Reviewed-by: default avatarAndy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/20220213025739.2561834-4-liambeguin@gmail.comSigned-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent 701ee14d
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* IIO rescale driver * IIO rescale driver
* *
* Copyright (C) 2018 Axentia Technologies AB * Copyright (C) 2018 Axentia Technologies AB
* Copyright (C) 2022 Liam Beguin <liambeguin@gmail.com>
* *
* Author: Peter Rosin <peda@axentia.se> * Author: Peter Rosin <peda@axentia.se>
*/ */
...@@ -81,11 +82,46 @@ int rescale_process_scale(struct rescale *rescale, int scale_type, ...@@ -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, static int rescale_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, struct iio_chan_spec const *chan,
int *val, int *val2, long mask) int *val, int *val2, long mask)
{ {
struct rescale *rescale = iio_priv(indio_dev); struct rescale *rescale = iio_priv(indio_dev);
int scale, scale2;
int schan_off = 0;
int ret; int ret;
switch (mask) { switch (mask) {
...@@ -112,6 +148,47 @@ static int rescale_read_raw(struct iio_dev *indio_dev, ...@@ -112,6 +148,47 @@ static int rescale_read_raw(struct iio_dev *indio_dev,
ret = iio_read_channel_scale(rescale->source, val, val2); ret = iio_read_channel_scale(rescale->source, val, val2);
} }
return rescale_process_scale(rescale, ret, 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: default:
return -EINVAL; return -EINVAL;
} }
...@@ -188,6 +265,9 @@ static int rescale_configure_channel(struct device *dev, ...@@ -188,6 +265,9 @@ static int rescale_configure_channel(struct device *dev,
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE); 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 * Using .read_avail() is fringe to begin with and makes no sense
* whatsoever for processed channels, so we make sure that this cannot * whatsoever for processed channels, so we make sure that this cannot
...@@ -352,6 +432,7 @@ static int rescale_probe(struct platform_device *pdev) ...@@ -352,6 +432,7 @@ static int rescale_probe(struct platform_device *pdev)
rescale->cfg = of_device_get_match_data(dev); rescale->cfg = of_device_get_match_data(dev);
rescale->numerator = 1; rescale->numerator = 1;
rescale->denominator = 1; rescale->denominator = 1;
rescale->offset = 0;
ret = rescale->cfg->props(dev, rescale); ret = rescale->cfg->props(dev, rescale);
if (ret) if (ret)
......
...@@ -25,8 +25,12 @@ struct rescale { ...@@ -25,8 +25,12 @@ struct rescale {
bool chan_processed; bool chan_processed;
s32 numerator; s32 numerator;
s32 denominator; s32 denominator;
s32 offset;
}; };
int rescale_process_scale(struct rescale *rescale, int scale_type, int rescale_process_scale(struct rescale *rescale, int scale_type,
int *val, int *val2); 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__ */ #endif /* __IIO_RESCALE_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment