diff mbox series

[v3,01/17] iio: core: Increase precision of IIO_VAL_FRACTIONAL_LOG2 when possible

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

Commit Message

Jonathan Cameron June 26, 2022, 12:29 p.m. UTC
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>
---
 drivers/iio/industrialio-core.c | 31 +++++++++++++++++++++++--------
 1 file changed, 23 insertions(+), 8 deletions(-)

Comments

Jonathan Cameron Aug. 7, 2022, 1:28 p.m. UTC | #1
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 mbox series

Patch

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;