diff options
-rw-r--r-- | drivers/iio/afe/iio-rescale.c | 23 |
1 files changed, 18 insertions, 5 deletions
diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c index 5d78f0cf47d2..46947c68d3a9 100644 --- a/drivers/iio/afe/iio-rescale.c +++ b/drivers/iio/afe/iio-rescale.c @@ -24,21 +24,31 @@ int rescale_process_scale(struct rescale *rescale, int scale_type, int *val, int *val2) { s64 tmp; + int _val, _val2; s32 rem, rem2; u32 mult; u32 neg; switch (scale_type) { - case IIO_VAL_FRACTIONAL: - *val *= rescale->numerator; - *val2 *= rescale->denominator; - return scale_type; case IIO_VAL_INT: *val *= rescale->numerator; if (rescale->denominator == 1) return scale_type; *val2 = rescale->denominator; return IIO_VAL_FRACTIONAL; + case IIO_VAL_FRACTIONAL: + /* + * When the product of both scales doesn't overflow, avoid + * potential accuracy loss (for in kernel consumers) by + * keeping a fractional representation. + */ + if (!check_mul_overflow(*val, rescale->numerator, &_val) && + !check_mul_overflow(*val2, rescale->denominator, &_val2)) { + *val = _val; + *val2 = _val2; + return IIO_VAL_FRACTIONAL; + } + fallthrough; case IIO_VAL_FRACTIONAL_LOG2: tmp = (s64)*val * 1000000000LL; tmp = div_s64(tmp, rescale->denominator); @@ -50,7 +60,10 @@ int rescale_process_scale(struct rescale *rescale, int scale_type, if (!rem) return scale_type; - tmp = 1 << *val2; + if (scale_type == IIO_VAL_FRACTIONAL) + tmp = *val2; + else + tmp = ULL(1) << *val2; rem2 = *val % (int)tmp; *val = *val / (int)tmp; |