drivers/iio/adc/ad4030.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
The read path returns calibscale in IIO_VAL_INT_PLUS_NANO but the write
path treats it as MICRO. Since no write_raw_get_fmt is provided, the
IIO core defaults to MICRO when parsing userspace input.
This means reading calibscale and writing it back results in a ~1000x
gain error.
Change the read path and available range to use MICRO to match the
write path.
Fixes: 0cb8b324852f ("iio: adc: ad4030: add driver for ad4030-24")
Signed-off-by: Giorgi Tchankvetadze <giorgitchankvetadze1997@gmail.com>
---
drivers/iio/adc/ad4030.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c
index def3e1d01ceb..3d823648371d 100644
--- a/drivers/iio/adc/ad4030.c
+++ b/drivers/iio/adc/ad4030.c
@@ -423,10 +423,10 @@ static int ad4030_get_chan_calibscale(struct iio_dev *indio_dev,
/* From datasheet: multiplied output = input × gain word/0x8000 */
*val = gain / AD4030_GAIN_MIDLE_POINT;
- *val2 = mul_u64_u32_div(gain % AD4030_GAIN_MIDLE_POINT, NANO,
+ *val2 = mul_u64_u32_div(gain % AD4030_GAIN_MIDLE_POINT, MICRO,
AD4030_GAIN_MIDLE_POINT);
- return IIO_VAL_INT_PLUS_NANO;
+ return IIO_VAL_INT_PLUS_MICRO;
}
/* Returns the offset where 1 LSB = (VREF/2^precision_bits - 1)/gain */
@@ -720,8 +720,8 @@ static irqreturn_t ad4030_trigger_handler(int irq, void *p)
static const int ad4030_gain_avail[3][2] = {
{ 0, 0 },
- { 0, 30518 },
- { 1, 999969482 },
+ { 0, 30 },
+ { 1, 999969 },
};
static int ad4030_read_avail(struct iio_dev *indio_dev,
@@ -739,7 +739,7 @@ static int ad4030_read_avail(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_CALIBSCALE:
*vals = (void *)ad4030_gain_avail;
- *type = IIO_VAL_INT_PLUS_NANO;
+ *type = IIO_VAL_INT_PLUS_MICRO;
return IIO_AVAIL_RANGE;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
--
2.52.0
On Wed, 25 Feb 2026 15:30:39 +0400
Giorgi Tchankvetadze <giorgitchankvetadze1997@gmail.com> wrote:
> The read path returns calibscale in IIO_VAL_INT_PLUS_NANO but the write
> path treats it as MICRO. Since no write_raw_get_fmt is provided, the
> IIO core defaults to MICRO when parsing userspace input.
>
> This means reading calibscale and writing it back results in a ~1000x
> gain error.
I'm not following this part. What I'd expect to see something like
reading avail gets you
[0.0 0.0000030518 1.99996982]
Reading the channel gets you maybe 1.1333333333 and then when you write
back because of the lack of write_fmt it gets truncated and you
are effectively reading 1.133333
So I'd not expect to see a gain error, just a loss of precision.
What am I missing? Please provide a numeric example to motivate the
patch.
Thanks,
Jonathan
>
> Change the read path and available range to use MICRO to match the
> write path.
>
> Fixes: 0cb8b324852f ("iio: adc: ad4030: add driver for ad4030-24")
> Signed-off-by: Giorgi Tchankvetadze <giorgitchankvetadze1997@gmail.com>
> ---
> drivers/iio/adc/ad4030.c | 10 +++++-----
> 1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c
> index def3e1d01ceb..3d823648371d 100644
> --- a/drivers/iio/adc/ad4030.c
> +++ b/drivers/iio/adc/ad4030.c
> @@ -423,10 +423,10 @@ static int ad4030_get_chan_calibscale(struct iio_dev *indio_dev,
>
> /* From datasheet: multiplied output = input × gain word/0x8000 */
> *val = gain / AD4030_GAIN_MIDLE_POINT;
> - *val2 = mul_u64_u32_div(gain % AD4030_GAIN_MIDLE_POINT, NANO,
> + *val2 = mul_u64_u32_div(gain % AD4030_GAIN_MIDLE_POINT, MICRO,
> AD4030_GAIN_MIDLE_POINT);
>
> - return IIO_VAL_INT_PLUS_NANO;
> + return IIO_VAL_INT_PLUS_MICRO;
> }
>
> /* Returns the offset where 1 LSB = (VREF/2^precision_bits - 1)/gain */
> @@ -720,8 +720,8 @@ static irqreturn_t ad4030_trigger_handler(int irq, void *p)
>
> static const int ad4030_gain_avail[3][2] = {
> { 0, 0 },
> - { 0, 30518 },
> - { 1, 999969482 },
> + { 0, 30 },
> + { 1, 999969 },
> };
>
> static int ad4030_read_avail(struct iio_dev *indio_dev,
> @@ -739,7 +739,7 @@ static int ad4030_read_avail(struct iio_dev *indio_dev,
>
> case IIO_CHAN_INFO_CALIBSCALE:
> *vals = (void *)ad4030_gain_avail;
> - *type = IIO_VAL_INT_PLUS_NANO;
> + *type = IIO_VAL_INT_PLUS_MICRO;
> return IIO_AVAIL_RANGE;
>
> case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
On 02/25, Giorgi Tchankvetadze wrote:
> The read path returns calibscale in IIO_VAL_INT_PLUS_NANO but the write
> path treats it as MICRO. Since no write_raw_get_fmt is provided, the
> IIO core defaults to MICRO when parsing userspace input.
>
> This means reading calibscale and writing it back results in a ~1000x
> gain error.
>
> Change the read path and available range to use MICRO to match the
> write path.
The updates to ad4030 driver will add write_raw_get_fmt() [1].
[1]: https://lore.kernel.org/linux-iio/516cccc47e917bd26be29b016907f50a244a68b9.1771865684.git.marcelo.schmitt@analog.com/
At first glance, I think this could instead add a case to write_raw_get_fmt(),
keeping calibscale nano precision (unless nano precision doesn't make sense
for ad4030 calibscale (don't recall from top of my mind)).
>
> Fixes: 0cb8b324852f ("iio: adc: ad4030: add driver for ad4030-24")
> Signed-off-by: Giorgi Tchankvetadze <giorgitchankvetadze1997@gmail.com>
> ---
> drivers/iio/adc/ad4030.c | 10 +++++-----
> 1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c
> index def3e1d01ceb..3d823648371d 100644
> --- a/drivers/iio/adc/ad4030.c
> +++ b/drivers/iio/adc/ad4030.c
> @@ -423,10 +423,10 @@ static int ad4030_get_chan_calibscale(struct iio_dev *indio_dev,
>
> /* From datasheet: multiplied output = input × gain word/0x8000 */
> *val = gain / AD4030_GAIN_MIDLE_POINT;
> - *val2 = mul_u64_u32_div(gain % AD4030_GAIN_MIDLE_POINT, NANO,
> + *val2 = mul_u64_u32_div(gain % AD4030_GAIN_MIDLE_POINT, MICRO,
> AD4030_GAIN_MIDLE_POINT);
>
> - return IIO_VAL_INT_PLUS_NANO;
> + return IIO_VAL_INT_PLUS_MICRO;
> }
>
> /* Returns the offset where 1 LSB = (VREF/2^precision_bits - 1)/gain */
> @@ -720,8 +720,8 @@ static irqreturn_t ad4030_trigger_handler(int irq, void *p)
>
> static const int ad4030_gain_avail[3][2] = {
> { 0, 0 },
> - { 0, 30518 },
> - { 1, 999969482 },
> + { 0, 30 },
> + { 1, 999969 },
> };
>
> static int ad4030_read_avail(struct iio_dev *indio_dev,
> @@ -739,7 +739,7 @@ static int ad4030_read_avail(struct iio_dev *indio_dev,
>
> case IIO_CHAN_INFO_CALIBSCALE:
> *vals = (void *)ad4030_gain_avail;
> - *type = IIO_VAL_INT_PLUS_NANO;
> + *type = IIO_VAL_INT_PLUS_MICRO;
> return IIO_AVAIL_RANGE;
>
> case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> --
> 2.52.0
>
>
On 2/25/26 5:53 AM, Marcelo Schmitt wrote:
> On 02/25, Giorgi Tchankvetadze wrote:
>> The read path returns calibscale in IIO_VAL_INT_PLUS_NANO but the write
>> path treats it as MICRO. Since no write_raw_get_fmt is provided, the
>> IIO core defaults to MICRO when parsing userspace input.
>>
>> This means reading calibscale and writing it back results in a ~1000x
>> gain error.
>>
>> Change the read path and available range to use MICRO to match the
>> write path.
>
> The updates to ad4030 driver will add write_raw_get_fmt() [1].
> [1]: https://lore.kernel.org/linux-iio/516cccc47e917bd26be29b016907f50a244a68b9.1771865684.git.marcelo.schmitt@analog.com/
>
> At first glance, I think this could instead add a case to write_raw_get_fmt(),
> keeping calibscale nano precision (unless nano precision doesn't make sense
> for ad4030 calibscale (don't recall from top of my mind)).
Since this is a fix and needs to be backported, it probably make sense
to not depend on a patch that is adding new features.
I'm sure Jonathan will have an opinion about how he would like to handle
a conflict between the fixes and testing branches though.
>
>>
>> Fixes: 0cb8b324852f ("iio: adc: ad4030: add driver for ad4030-24")
>> Signed-off-by: Giorgi Tchankvetadze <giorgitchankvetadze1997@gmail.com>
>> ---
>> drivers/iio/adc/ad4030.c | 10 +++++-----
>> 1 file changed, 5 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c
>> index def3e1d01ceb..3d823648371d 100644
>> --- a/drivers/iio/adc/ad4030.c
>> +++ b/drivers/iio/adc/ad4030.c
>> @@ -423,10 +423,10 @@ static int ad4030_get_chan_calibscale(struct iio_dev *indio_dev,
>>
>> /* From datasheet: multiplied output = input × gain word/0x8000 */
>> *val = gain / AD4030_GAIN_MIDLE_POINT;
>> - *val2 = mul_u64_u32_div(gain % AD4030_GAIN_MIDLE_POINT, NANO,
>> + *val2 = mul_u64_u32_div(gain % AD4030_GAIN_MIDLE_POINT, MICRO,
>> AD4030_GAIN_MIDLE_POINT);
>>
>> - return IIO_VAL_INT_PLUS_NANO;
>> + return IIO_VAL_INT_PLUS_MICRO;
>> }
>>
>> /* Returns the offset where 1 LSB = (VREF/2^precision_bits - 1)/gain */
>> @@ -720,8 +720,8 @@ static irqreturn_t ad4030_trigger_handler(int irq, void *p)
>>
>> static const int ad4030_gain_avail[3][2] = {
>> { 0, 0 },
>> - { 0, 30518 },
>> - { 1, 999969482 },
>> + { 0, 30 },
>> + { 1, 999969 },
This loses precision. Instead I would change the write to IIO_VAL_INT_PLUS_NANO
to match the read.
>> };
>>
>> static int ad4030_read_avail(struct iio_dev *indio_dev,
>> @@ -739,7 +739,7 @@ static int ad4030_read_avail(struct iio_dev *indio_dev,
>>
>> case IIO_CHAN_INFO_CALIBSCALE:
>> *vals = (void *)ad4030_gain_avail;
>> - *type = IIO_VAL_INT_PLUS_NANO;
>> + *type = IIO_VAL_INT_PLUS_MICRO;
>> return IIO_AVAIL_RANGE;
>>
>> case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>> --
>> 2.52.0
>>
>>
On Wed, 2026-02-25 at 09:20 -0600, David Lechner wrote:
> On 2/25/26 5:53 AM, Marcelo Schmitt wrote:
> > On 02/25, Giorgi Tchankvetadze wrote:
> > > The read path returns calibscale in IIO_VAL_INT_PLUS_NANO but the write
> > > path treats it as MICRO. Since no write_raw_get_fmt is provided, the
> > > IIO core defaults to MICRO when parsing userspace input.
> > >
> > > This means reading calibscale and writing it back results in a ~1000x
> > > gain error.
> > >
> > > Change the read path and available range to use MICRO to match the
> > > write path.
> >
> > The updates to ad4030 driver will add write_raw_get_fmt() [1].
> > [1]:
> > https://lore.kernel.org/linux-iio/516cccc47e917bd26be29b016907f50a244a68b9.1771865684.git.marcelo.schmitt@analog.com/
> >
> > At first glance, I think this could instead add a case to write_raw_get_fmt(),
> > keeping calibscale nano precision (unless nano precision doesn't make sense
> > for ad4030 calibscale (don't recall from top of my mind)).
>
> Since this is a fix and needs to be backported, it probably make sense
> to not depend on a patch that is adding new features.
>
+1
- Nuno Sá
> I'm sure Jonathan will have an opinion about how he would like to handle
> a conflict between the fixes and testing branches though.
>
> >
> > >
> > > Fixes: 0cb8b324852f ("iio: adc: ad4030: add driver for ad4030-24")
> > > Signed-off-by: Giorgi Tchankvetadze <giorgitchankvetadze1997@gmail.com>
> > > ---
> > > drivers/iio/adc/ad4030.c | 10 +++++-----
> > > 1 file changed, 5 insertions(+), 5 deletions(-)
> > >
> > > diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c
> > > index def3e1d01ceb..3d823648371d 100644
> > > --- a/drivers/iio/adc/ad4030.c
> > > +++ b/drivers/iio/adc/ad4030.c
> > > @@ -423,10 +423,10 @@ static int ad4030_get_chan_calibscale(struct iio_dev *indio_dev,
> > >
> > > /* From datasheet: multiplied output = input × gain word/0x8000 */
> > > *val = gain / AD4030_GAIN_MIDLE_POINT;
> > > - *val2 = mul_u64_u32_div(gain % AD4030_GAIN_MIDLE_POINT, NANO,
> > > + *val2 = mul_u64_u32_div(gain % AD4030_GAIN_MIDLE_POINT, MICRO,
> > > AD4030_GAIN_MIDLE_POINT);
> > >
> > > - return IIO_VAL_INT_PLUS_NANO;
> > > + return IIO_VAL_INT_PLUS_MICRO;
> > > }
> > >
> > > /* Returns the offset where 1 LSB = (VREF/2^precision_bits - 1)/gain */
> > > @@ -720,8 +720,8 @@ static irqreturn_t ad4030_trigger_handler(int irq, void *p)
> > >
> > > static const int ad4030_gain_avail[3][2] = {
> > > { 0, 0 },
> > > - { 0, 30518 },
> > > - { 1, 999969482 },
> > > + { 0, 30 },
> > > + { 1, 999969 },
>
> This loses precision. Instead I would change the write to IIO_VAL_INT_PLUS_NANO
> to match the read.
>
> > > };
> > >
> > > static int ad4030_read_avail(struct iio_dev *indio_dev,
> > > @@ -739,7 +739,7 @@ static int ad4030_read_avail(struct iio_dev *indio_dev,
> > >
> > > case IIO_CHAN_INFO_CALIBSCALE:
> > > *vals = (void *)ad4030_gain_avail;
> > > - *type = IIO_VAL_INT_PLUS_NANO;
> > > + *type = IIO_VAL_INT_PLUS_MICRO;
> > > return IIO_AVAIL_RANGE;
> > >
> > > case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> > > --
> > > 2.52.0
> > >
> > >
On Thu, 26 Feb 2026 09:32:45 +0000 Nuno Sá <noname.nuno@gmail.com> wrote: > On Wed, 2026-02-25 at 09:20 -0600, David Lechner wrote: > > On 2/25/26 5:53 AM, Marcelo Schmitt wrote: > > > On 02/25, Giorgi Tchankvetadze wrote: > > > > The read path returns calibscale in IIO_VAL_INT_PLUS_NANO but the write > > > > path treats it as MICRO. Since no write_raw_get_fmt is provided, the > > > > IIO core defaults to MICRO when parsing userspace input. > > > > > > > > This means reading calibscale and writing it back results in a ~1000x > > > > gain error. > > > > > > > > Change the read path and available range to use MICRO to match the > > > > write path. > > > > > > The updates to ad4030 driver will add write_raw_get_fmt() [1]. > > > [1]: > > > https://lore.kernel.org/linux-iio/516cccc47e917bd26be29b016907f50a244a68b9.1771865684.git.marcelo.schmitt@analog.com/ > > > > > > At first glance, I think this could instead add a case to write_raw_get_fmt(), > > > keeping calibscale nano precision (unless nano precision doesn't make sense > > > for ad4030 calibscale (don't recall from top of my mind)). > > > > Since this is a fix and needs to be backported, it probably make sense > > to not depend on a patch that is adding new features. > > > > +1 > > - Nuno Sá > > > I'm sure Jonathan will have an opinion about how he would like to handle > > a conflict between the fixes and testing branches though. Fixes in theory always go first. That'll mean I end up holding the other series back if this one is ready in the near future. I'm not sure I fully understand the bug though! Jonathan
Hi Marcelo, Thanks for the pointer to your pending series. As for
keeping NANO precision, the hardware register is 16-bit with a step of
1/0x8000 ≈ 30 µ, so NANO doesn't provide any additional precision that
the hardware can actually represent. MICRO is sufficient.
Btw, I checked and found that no driver in the entire IIO tree uses
NANO for calibscale.
Drivers use either IIO_VAL_INT (raw register value) or IIO_VAL_INT_PLUS_MICRO.
That said, if your series is already adding write_raw_get_fmt(), I'm
happy to drop this patch and let yours handle it - as long as the
mismatch gets fixed either way.
Regards, Giorgi
On Wed, Feb 25, 2026 at 3:53 PM Marcelo Schmitt
<marcelo.schmitt1@gmail.com> wrote:
>
> On 02/25, Giorgi Tchankvetadze wrote:
> > The read path returns calibscale in IIO_VAL_INT_PLUS_NANO but the write
> > path treats it as MICRO. Since no write_raw_get_fmt is provided, the
> > IIO core defaults to MICRO when parsing userspace input.
> >
> > This means reading calibscale and writing it back results in a ~1000x
> > gain error.
> >
> > Change the read path and available range to use MICRO to match the
> > write path.
>
> The updates to ad4030 driver will add write_raw_get_fmt() [1].
> [1]: https://lore.kernel.org/linux-iio/516cccc47e917bd26be29b016907f50a244a68b9.1771865684.git.marcelo.schmitt@analog.com/
>
> At first glance, I think this could instead add a case to write_raw_get_fmt(),
> keeping calibscale nano precision (unless nano precision doesn't make sense
> for ad4030 calibscale (don't recall from top of my mind)).
>
> >
> > Fixes: 0cb8b324852f ("iio: adc: ad4030: add driver for ad4030-24")
> > Signed-off-by: Giorgi Tchankvetadze <giorgitchankvetadze1997@gmail.com>
> > ---
> > drivers/iio/adc/ad4030.c | 10 +++++-----
> > 1 file changed, 5 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c
> > index def3e1d01ceb..3d823648371d 100644
> > --- a/drivers/iio/adc/ad4030.c
> > +++ b/drivers/iio/adc/ad4030.c
> > @@ -423,10 +423,10 @@ static int ad4030_get_chan_calibscale(struct iio_dev *indio_dev,
> >
> > /* From datasheet: multiplied output = input в gain word/0x8000 */
> > *val = gain / AD4030_GAIN_MIDLE_POINT;
> > - *val2 = mul_u64_u32_div(gain % AD4030_GAIN_MIDLE_POINT, NANO,
> > + *val2 = mul_u64_u32_div(gain % AD4030_GAIN_MIDLE_POINT, MICRO,
> > AD4030_GAIN_MIDLE_POINT);
> >
> > - return IIO_VAL_INT_PLUS_NANO;
> > + return IIO_VAL_INT_PLUS_MICRO;
> > }
> >
> > /* Returns the offset where 1 LSB = (VREF/2^precision_bits - 1)/gain */
> > @@ -720,8 +720,8 @@ static irqreturn_t ad4030_trigger_handler(int irq, void *p)
> >
> > static const int ad4030_gain_avail[3][2] = {
> > { 0, 0 },
> > - { 0, 30518 },
> > - { 1, 999969482 },
> > + { 0, 30 },
> > + { 1, 999969 },
> > };
> >
> > static int ad4030_read_avail(struct iio_dev *indio_dev,
> > @@ -739,7 +739,7 @@ static int ad4030_read_avail(struct iio_dev *indio_dev,
> >
> > case IIO_CHAN_INFO_CALIBSCALE:
> > *vals = (void *)ad4030_gain_avail;
> > - *type = IIO_VAL_INT_PLUS_NANO;
> > + *type = IIO_VAL_INT_PLUS_MICRO;
> > return IIO_AVAIL_RANGE;
> >
> > case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> > --
> > 2.52.0
> >
> >
On Wed, Feb 25, 2026 at 04:21:53PM +0400, Giorgi Tchankvetadze wrote: > Hi Marcelo, Thanks for the pointer to your pending series. As for > keeping NANO precision, the hardware register is 16-bit with a step of > 1/0x8000 ≈ 30 µ, so NANO doesn't provide any additional precision that > the hardware can actually represent. MICRO is sufficient. > > Btw, I checked and found that no driver in the entire IIO tree uses > NANO for calibscale. > Drivers use either IIO_VAL_INT (raw register value) or IIO_VAL_INT_PLUS_MICRO. > > That said, if your series is already adding write_raw_get_fmt(), I'm > happy to drop this patch and let yours handle it - as long as the > mismatch gets fixed either way. - Please, do not top-post! - Remove context which is unrelated to the replies. -- With Best Regards, Andy Shevchenko
© 2016 - 2026 Red Hat, Inc.