Add a function to allow IIO consumers to write a processed value to a
channel.
Signed-off-by: Romain Gantois <romain.gantois@bootlin.com>
---
drivers/iio/inkern.c | 99 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/iio/consumer.h | 17 ++++++++
2 files changed, 116 insertions(+)
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index c174ebb7d5e6d183674b7ffb15c4ce0f65fa3aed..6486fdb7c66a4c84312541f0f42cc24469972a9c 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -598,6 +598,85 @@ int iio_read_channel_average_raw(struct iio_channel *chan, int *val)
}
EXPORT_SYMBOL_GPL(iio_read_channel_average_raw);
+static int iio_convert_processed_to_raw_unlocked(struct iio_channel *chan,
+ int processed, int *raw,
+ unsigned int scale)
+{
+ int scale_type, scale_val, scale_val2;
+ int offset_type, offset_val, offset_val2;
+ s64 tmp_num, tmp_den;
+
+ scale_type = iio_channel_read(chan, &scale_val, &scale_val2,
+ IIO_CHAN_INFO_SCALE);
+ if (scale_type >= 0) {
+ switch (scale_type) {
+ case IIO_VAL_INT:
+ tmp_num = processed;
+ tmp_den = scale_val;
+ break;
+ case IIO_VAL_INT_PLUS_MICRO:
+ tmp_num = (s64)processed * 1000000LL;
+
+ if (scale_val2 < 0) {
+ tmp_den = (s64)scale_val * 1000000LL - (s64)scale_val2;
+ tmp_den *= -1;
+ } else {
+ tmp_den = (s64)scale_val * 1000000LL + (s64)scale_val2;
+ }
+
+ break;
+ case IIO_VAL_INT_PLUS_NANO:
+ tmp_num = (s64)processed * 1000000000LL;
+
+ if (scale_val2 < 0) {
+ tmp_den = (s64)scale_val * 1000000000LL - (s64)scale_val2;
+ tmp_den *= -1;
+ } else {
+ tmp_den = (s64)scale_val * 1000000000LL + (s64)scale_val2;
+ }
+
+ break;
+ case IIO_VAL_FRACTIONAL:
+ tmp_num = (s64)processed * (s64)scale_val2;
+ tmp_den = scale_val;
+ break;
+ case IIO_VAL_FRACTIONAL_LOG2:
+ tmp_num = (s64)processed << scale_val2;
+ tmp_den = scale_val;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tmp_den *= scale;
+
+ *raw = div64_s64(tmp_num, tmp_den);
+ }
+
+ offset_type = iio_channel_read(chan, &offset_val, &offset_val2,
+ IIO_CHAN_INFO_OFFSET);
+ if (offset_type >= 0) {
+ switch (offset_type) {
+ case IIO_VAL_INT:
+ case IIO_VAL_INT_PLUS_MICRO:
+ case IIO_VAL_INT_PLUS_NANO:
+ break;
+ case IIO_VAL_FRACTIONAL:
+ offset_val /= offset_val2;
+ break;
+ case IIO_VAL_FRACTIONAL_LOG2:
+ offset_val >>= offset_val2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *raw -= offset_val;
+ }
+
+ return 0;
+}
+
static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan,
int raw, int *processed,
unsigned int scale)
@@ -1028,3 +1107,23 @@ ssize_t iio_read_channel_label(struct iio_channel *chan, char *buf)
return do_iio_read_channel_label(chan->indio_dev, chan->channel, buf);
}
EXPORT_SYMBOL_GPL(iio_read_channel_label);
+
+int iio_write_channel_processed_scale(struct iio_channel *chan, int val,
+ unsigned int scale)
+{
+ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
+ int ret, processed;
+
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+
+ if (!chan->indio_dev->info)
+ return -ENODEV;
+
+ ret = iio_convert_processed_to_raw_unlocked(chan, val, &processed, scale);
+ if (ret)
+ return ret;
+
+ return iio_channel_write(chan, processed, 0, IIO_CHAN_INFO_RAW);
+}
+EXPORT_SYMBOL_GPL(iio_write_channel_processed_scale);
+
diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
index 6a44796164792b2dd930f8168b14de327a80a6f7..79c4804a73b0652d4c16ee5ad07c4543bccd6c92 100644
--- a/include/linux/iio/consumer.h
+++ b/include/linux/iio/consumer.h
@@ -451,4 +451,21 @@ ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr,
*/
ssize_t iio_read_channel_label(struct iio_channel *chan, char *buf);
+/**
+ * iio_write_channel_processed_scale() - scale and write processed value to a given channel
+ * @chan: The channel being queried.
+ * @val: Value to write.
+ * @scale: Scale factor to apply during the conversion
+ *
+ * Returns an error code or 0.
+ *
+ * This function writes a processed value to a channel. A processed value means
+ * that this value will have the correct unit and not some device internal
+ * representation. If the device does not support writing a processed value, the
+ * function will query the channel's scale and offset and write an appropriately
+ * transformed raw value.
+ */
+int iio_write_channel_processed_scale(struct iio_channel *chan, int val,
+ unsigned int scale);
+
#endif
--
2.51.0
On 9/16/25 5:24 AM, Romain Gantois wrote: > Add a function to allow IIO consumers to write a processed value to a > channel. > > Signed-off-by: Romain Gantois <romain.gantois@bootlin.com> > --- > drivers/iio/inkern.c | 99 ++++++++++++++++++++++++++++++++++++++++++++ > include/linux/iio/consumer.h | 17 ++++++++ > 2 files changed, 116 insertions(+) > > diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c > index c174ebb7d5e6d183674b7ffb15c4ce0f65fa3aed..6486fdb7c66a4c84312541f0f42cc24469972a9c 100644 > --- a/drivers/iio/inkern.c > +++ b/drivers/iio/inkern.c > @@ -598,6 +598,85 @@ int iio_read_channel_average_raw(struct iio_channel *chan, int *val) > } > EXPORT_SYMBOL_GPL(iio_read_channel_average_raw); > > +static int iio_convert_processed_to_raw_unlocked(struct iio_channel *chan, > + int processed, int *raw, > + unsigned int scale) > +{ > + int scale_type, scale_val, scale_val2; > + int offset_type, offset_val, offset_val2; > + s64 tmp_num, tmp_den; > + > + scale_type = iio_channel_read(chan, &scale_val, &scale_val2, > + IIO_CHAN_INFO_SCALE); > + if (scale_type >= 0) { > + switch (scale_type) { > + case IIO_VAL_INT: > + tmp_num = processed; > + tmp_den = scale_val; > + break; > + case IIO_VAL_INT_PLUS_MICRO: > + tmp_num = (s64)processed * 1000000LL; > + > + if (scale_val2 < 0) { > + tmp_den = (s64)scale_val * 1000000LL - (s64)scale_val2; > + tmp_den *= -1; > + } else { > + tmp_den = (s64)scale_val * 1000000LL + (s64)scale_val2; > + } > + > + break; > + case IIO_VAL_INT_PLUS_NANO: > + tmp_num = (s64)processed * 1000000000LL; > + > + if (scale_val2 < 0) { > + tmp_den = (s64)scale_val * 1000000000LL - (s64)scale_val2; > + tmp_den *= -1; > + } else { > + tmp_den = (s64)scale_val * 1000000000LL + (s64)scale_val2; > + } > + > + break; > + case IIO_VAL_FRACTIONAL: > + tmp_num = (s64)processed * (s64)scale_val2; > + tmp_den = scale_val; > + break; > + case IIO_VAL_FRACTIONAL_LOG2: > + tmp_num = (s64)processed << scale_val2; > + tmp_den = scale_val; > + break; > + default: > + return -EINVAL; > + } > + > + tmp_den *= scale; > + > + *raw = div64_s64(tmp_num, tmp_den); > + } It can be quite tricky to get all of these combinations right. I would prefer if added some unit tests like we did in [1]. [1]: https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/commit/?h=testing&id=c732e60ee10ed0611a59513cbf9c8d35fbe7cf65 > + > + offset_type = iio_channel_read(chan, &offset_val, &offset_val2, > + IIO_CHAN_INFO_OFFSET); > + if (offset_type >= 0) { > + switch (offset_type) { > + case IIO_VAL_INT: > + case IIO_VAL_INT_PLUS_MICRO: > + case IIO_VAL_INT_PLUS_NANO: > + break; > + case IIO_VAL_FRACTIONAL: > + offset_val /= offset_val2; > + break; > + case IIO_VAL_FRACTIONAL_LOG2: > + offset_val >>= offset_val2; > + break; > + default: > + return -EINVAL; > + } > + > + *raw -= offset_val; > + } > + > + return 0; > +}
Hello David, On Tuesday, 16 September 2025 21:23:04 CEST David Lechner wrote: > On 9/16/25 5:24 AM, Romain Gantois wrote: > > Add a function to allow IIO consumers to write a processed value to a ... > > + case IIO_VAL_FRACTIONAL: > > + tmp_num = (s64)processed * (s64)scale_val2; > > + tmp_den = scale_val; > > + break; > > + case IIO_VAL_FRACTIONAL_LOG2: > > + tmp_num = (s64)processed << scale_val2; > > + tmp_den = scale_val; > > + break; > > + default: > > + return -EINVAL; > > + } > > + > > + tmp_den *= scale; > > + > > + *raw = div64_s64(tmp_num, tmp_den); > > + } > > It can be quite tricky to get all of these combinations right. I would > prefer if added some unit tests like we did in [1]. > > [1]: > https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/commit/?h=tes > ting&id=c732e60ee10ed0611a59513cbf9c8d35fbe7cf65 Agreed, that would be nice, I'll look into it. Thanks, -- Romain Gantois, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
On Tue, Sep 16, 2025 at 12:24:07PM +0200, Romain Gantois wrote: > Add a function to allow IIO consumers to write a processed value to a > channel. ... > +static int iio_convert_processed_to_raw_unlocked(struct iio_channel *chan, > + int processed, int *raw, > + unsigned int scale) > +{ > + int scale_type, scale_val, scale_val2; > + int offset_type, offset_val, offset_val2; > + s64 tmp_num, tmp_den; > + > + scale_type = iio_channel_read(chan, &scale_val, &scale_val2, > + IIO_CHAN_INFO_SCALE); > + if (scale_type >= 0) { > + switch (scale_type) { > + case IIO_VAL_INT: > + tmp_num = processed; > + tmp_den = scale_val; > + break; > + case IIO_VAL_INT_PLUS_MICRO: > + tmp_num = (s64)processed * 1000000LL; If you go with this, in IIO we heavily use units.h and here something like (s64)MICRO would be appropriate. Similar to the rest of the code where one or another constant may be used. > + if (scale_val2 < 0) { > + tmp_den = (s64)scale_val * 1000000LL - (s64)scale_val2; > + tmp_den *= -1; > + } else { > + tmp_den = (s64)scale_val * 1000000LL + (s64)scale_val2; > + } > + > + break; > + case IIO_VAL_INT_PLUS_NANO: > + tmp_num = (s64)processed * 1000000000LL; > + > + if (scale_val2 < 0) { > + tmp_den = (s64)scale_val * 1000000000LL - (s64)scale_val2; > + tmp_den *= -1; > + } else { > + tmp_den = (s64)scale_val * 1000000000LL + (s64)scale_val2; > + } > + > + break; > + case IIO_VAL_FRACTIONAL: > + tmp_num = (s64)processed * (s64)scale_val2; > + tmp_den = scale_val; > + break; > + case IIO_VAL_FRACTIONAL_LOG2: > + tmp_num = (s64)processed << scale_val2; > + tmp_den = scale_val; > + break; > + default: > + return -EINVAL; > + } > + > + tmp_den *= scale; > + > + *raw = div64_s64(tmp_num, tmp_den); > + } > + > + offset_type = iio_channel_read(chan, &offset_val, &offset_val2, > + IIO_CHAN_INFO_OFFSET); > + if (offset_type >= 0) { > + switch (offset_type) { > + case IIO_VAL_INT: > + case IIO_VAL_INT_PLUS_MICRO: > + case IIO_VAL_INT_PLUS_NANO: > + break; > + case IIO_VAL_FRACTIONAL: > + offset_val /= offset_val2; > + break; > + case IIO_VAL_FRACTIONAL_LOG2: > + offset_val >>= offset_val2; > + break; > + default: > + return -EINVAL; > + } > + > + *raw -= offset_val; > + } > + > + return 0; > +} ... > +/** > + * iio_write_channel_processed_scale() - scale and write processed value to a given channel > + * @chan: The channel being queried. > + * @val: Value to write. > + * @scale: Scale factor to apply during the conversion > + * > + * Returns an error code or 0. > + * > + * This function writes a processed value to a channel. A processed value means > + * that this value will have the correct unit and not some device internal > + * representation. If the device does not support writing a processed value, the > + * function will query the channel's scale and offset and write an appropriately > + * transformed raw value. > + */ This needs a run via kernel-doc validator (warning: Missing Return section). Also note, in accordance with kernel-doc documentation the Return section must be last in the big description. -- With Best Regards, Andy Shevchenko
On Tue, Sep 16, 2025 at 12:24:07PM +0200, Romain Gantois wrote: > Add a function to allow IIO consumers to write a processed value to a > channel. This seems unrelated to the rest of the series?
Hello Mark, On Tuesday, 16 September 2025 13:19:10 CEST Mark Brown wrote: > On Tue, Sep 16, 2025 at 12:24:07PM +0200, Romain Gantois wrote: > > Add a function to allow IIO consumers to write a processed value to a > > channel. > > This seems unrelated to the rest of the series? This adds the iio_write_channel_processed_scale() function which is used in patch 4/4 of this series in the set_current_limit() callback. Thanks, -- Romain Gantois, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
© 2016 - 2025 Red Hat, Inc.