Message ID | 20220626122938.582107-2-jic23@kernel.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [v3,01/17] iio: core: Increase precision of IIO_VAL_FRACTIONAL_LOG2 when possible | expand |
On Sun, 26 Jun 2022 13:29:22 +0100 Jonathan Cameron <jic23@kernel.org> wrote: > From: Jonathan Cameron <Jonathan.Cameron@huawei.com> > > With some high resolution sensors such as the ad7746 the build up of error > when multiplying the _raw and _scale values together can be significant. > Reduce this affect by providing additional resolution in both calculation > and formatting of result. If overflow would occur with a 1e12 multiplier, > fall back to the 1e9 used before this patch and 9 decimal places. > > Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> I want to leave this patch on the list a little longer on basis it may have side effects in other drivers (hopefully not bad ones!) However, the only patch that needs this in the series is the roadtest tests in the final patch. Those are obviously dependent on roadtest going upstream so no rush their. As such I'm going to see if I can pick up the reset of the series (2-16 - minor mods as per Andy's comments). Thanks, Jonathan > --- > drivers/iio/industrialio-core.c | 31 +++++++++++++++++++++++-------- > 1 file changed, 23 insertions(+), 8 deletions(-) > > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c > index dc3e1cb9bfbd..8225d0c43010 100644 > --- a/drivers/iio/industrialio-core.c > +++ b/drivers/iio/industrialio-core.c > @@ -18,6 +18,7 @@ > #include <linux/poll.h> > #include <linux/property.h> > #include <linux/sched.h> > +#include <linux/units.h> > #include <linux/wait.h> > #include <linux/cdev.h> > #include <linux/slab.h> > @@ -674,14 +675,28 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, > else > return sysfs_emit_at(buf, offset, "%d.%09u", tmp0, > abs(tmp1)); > - case IIO_VAL_FRACTIONAL_LOG2: > - tmp2 = shift_right((s64)vals[0] * 1000000000LL, vals[1]); > - tmp0 = (int)div_s64_rem(tmp2, 1000000000LL, &tmp1); > - if (tmp0 == 0 && tmp2 < 0) > - return sysfs_emit_at(buf, offset, "-0.%09u", abs(tmp1)); > - else > - return sysfs_emit_at(buf, offset, "%d.%09u", tmp0, > - abs(tmp1)); > + case IIO_VAL_FRACTIONAL_LOG2: { > + u64 t1, t2, mult; > + int integer, precision; > + bool neg = vals[0] < 0; > + > + if (vals[0] > ULLONG_MAX / PICO) { > + mult = NANO; > + precision = 9; > + } else { > + mult = PICO; > + precision = 12; > + } > + t1 = shift_right((u64)abs(vals[0]) * mult, vals[1]); > + integer = (int)div64_u64_rem(t1, mult, &t2); > + if (integer == 0 && neg) > + return sysfs_emit_at(buf, offset, "-0.%0*llu", > + precision, abs(t2)); > + if (neg) > + integer *= -1; > + return sysfs_emit_at(buf, offset, "%d.%0*llu", integer, > + precision, abs(t2)); > + } > case IIO_VAL_INT_MULTIPLE: > { > int i;
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index dc3e1cb9bfbd..8225d0c43010 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -18,6 +18,7 @@ #include <linux/poll.h> #include <linux/property.h> #include <linux/sched.h> +#include <linux/units.h> #include <linux/wait.h> #include <linux/cdev.h> #include <linux/slab.h> @@ -674,14 +675,28 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, else return sysfs_emit_at(buf, offset, "%d.%09u", tmp0, abs(tmp1)); - case IIO_VAL_FRACTIONAL_LOG2: - tmp2 = shift_right((s64)vals[0] * 1000000000LL, vals[1]); - tmp0 = (int)div_s64_rem(tmp2, 1000000000LL, &tmp1); - if (tmp0 == 0 && tmp2 < 0) - return sysfs_emit_at(buf, offset, "-0.%09u", abs(tmp1)); - else - return sysfs_emit_at(buf, offset, "%d.%09u", tmp0, - abs(tmp1)); + case IIO_VAL_FRACTIONAL_LOG2: { + u64 t1, t2, mult; + int integer, precision; + bool neg = vals[0] < 0; + + if (vals[0] > ULLONG_MAX / PICO) { + mult = NANO; + precision = 9; + } else { + mult = PICO; + precision = 12; + } + t1 = shift_right((u64)abs(vals[0]) * mult, vals[1]); + integer = (int)div64_u64_rem(t1, mult, &t2); + if (integer == 0 && neg) + return sysfs_emit_at(buf, offset, "-0.%0*llu", + precision, abs(t2)); + if (neg) + integer *= -1; + return sysfs_emit_at(buf, offset, "%d.%0*llu", integer, + precision, abs(t2)); + } case IIO_VAL_INT_MULTIPLE: { int i;