1 // SPDX-License-Identifier: GPL-2.0
5 * Copyright (C) 2018 Axentia Technologies AB
6 * Copyright (C) 2022 Liam Beguin <liambeguin@gmail.com>
8 * Author: Peter Rosin <peda@axentia.se>
11 #include <linux/err.h>
12 #include <linux/gcd.h>
13 #include <linux/mod_devicetable.h>
14 #include <linux/module.h>
15 #include <linux/platform_device.h>
16 #include <linux/property.h>
18 #include <linux/iio/afe/rescale.h>
19 #include <linux/iio/consumer.h>
20 #include <linux/iio/iio.h>
22 int rescale_process_scale(struct rescale
*rescale
, int scale_type
,
33 *val
*= rescale
->numerator
;
34 if (rescale
->denominator
== 1)
36 *val2
= rescale
->denominator
;
37 return IIO_VAL_FRACTIONAL
;
38 case IIO_VAL_FRACTIONAL
:
40 * When the product of both scales doesn't overflow, avoid
41 * potential accuracy loss (for in kernel consumers) by
42 * keeping a fractional representation.
44 if (!check_mul_overflow(*val
, rescale
->numerator
, &_val
) &&
45 !check_mul_overflow(*val2
, rescale
->denominator
, &_val2
)) {
48 return IIO_VAL_FRACTIONAL
;
51 case IIO_VAL_FRACTIONAL_LOG2
:
52 tmp
= (s64
)*val
* 1000000000LL;
53 tmp
= div_s64(tmp
, rescale
->denominator
);
54 tmp
*= rescale
->numerator
;
56 tmp
= div_s64_rem(tmp
, 1000000000LL, &rem
);
62 if (scale_type
== IIO_VAL_FRACTIONAL
)
65 tmp
= ULL(1) << *val2
;
67 rem2
= *val
% (int)tmp
;
68 *val
= *val
/ (int)tmp
;
70 *val2
= rem
/ (int)tmp
;
72 *val2
+= div_s64((s64
)rem2
* 1000000000LL, tmp
);
74 return IIO_VAL_INT_PLUS_NANO
;
75 case IIO_VAL_INT_PLUS_NANO
:
76 case IIO_VAL_INT_PLUS_MICRO
:
77 mult
= scale_type
== IIO_VAL_INT_PLUS_NANO
? 1000000000L : 1000000L;
80 * For IIO_VAL_INT_PLUS_{MICRO,NANO} scale types if either *val
81 * OR *val2 is negative the schan scale is negative, i.e.
82 * *val = 1 and *val2 = -0.5 yields -1.5 not -0.5.
84 neg
= *val
< 0 || *val2
< 0;
86 tmp
= (s64
)abs(*val
) * abs(rescale
->numerator
);
87 *val
= div_s64_rem(tmp
, abs(rescale
->denominator
), &rem
);
89 tmp
= (s64
)rem
* mult
+ (s64
)abs(*val2
) * abs(rescale
->numerator
);
90 tmp
= div_s64(tmp
, abs(rescale
->denominator
));
92 *val
+= div_s64_rem(tmp
, mult
, val2
);
95 * If only one of the rescaler elements or the schan scale is
96 * negative, the combined scale is negative.
98 if (neg
^ ((rescale
->numerator
< 0) ^ (rescale
->denominator
< 0))) {
110 EXPORT_SYMBOL_NS_GPL(rescale_process_scale
, "IIO_RESCALE");
112 int rescale_process_offset(struct rescale
*rescale
, int scale_type
,
113 int scale
, int scale2
, int schan_off
,
118 switch (scale_type
) {
119 case IIO_VAL_FRACTIONAL
:
120 tmp
= (s64
)rescale
->offset
* scale2
;
121 *val
= div_s64(tmp
, scale
) + schan_off
;
124 *val
= div_s64(rescale
->offset
, scale
) + schan_off
;
126 case IIO_VAL_FRACTIONAL_LOG2
:
127 tmp
= (s64
)rescale
->offset
* (1 << scale2
);
128 *val
= div_s64(tmp
, scale
) + schan_off
;
130 case IIO_VAL_INT_PLUS_NANO
:
131 tmp
= (s64
)rescale
->offset
* 1000000000LL;
132 tmp2
= ((s64
)scale
* 1000000000LL) + scale2
;
133 *val
= div64_s64(tmp
, tmp2
) + schan_off
;
135 case IIO_VAL_INT_PLUS_MICRO
:
136 tmp
= (s64
)rescale
->offset
* 1000000LL;
137 tmp2
= ((s64
)scale
* 1000000LL) + scale2
;
138 *val
= div64_s64(tmp
, tmp2
) + schan_off
;
144 EXPORT_SYMBOL_NS_GPL(rescale_process_offset
, "IIO_RESCALE");
146 static int rescale_read_raw(struct iio_dev
*indio_dev
,
147 struct iio_chan_spec
const *chan
,
148 int *val
, int *val2
, long mask
)
150 struct rescale
*rescale
= iio_priv(indio_dev
);
156 case IIO_CHAN_INFO_RAW
:
157 if (rescale
->chan_processed
)
159 * When only processed channels are supported, we
160 * read the processed data and scale it by 1/1
161 * augmented with whatever the rescaler has calculated.
163 return iio_read_channel_processed(rescale
->source
, val
);
165 return iio_read_channel_raw(rescale
->source
, val
);
167 case IIO_CHAN_INFO_SCALE
:
168 if (rescale
->chan_processed
) {
170 * Processed channels are scaled 1-to-1
174 ret
= IIO_VAL_FRACTIONAL
;
176 ret
= iio_read_channel_scale(rescale
->source
, val
, val2
);
178 return rescale_process_scale(rescale
, ret
, val
, val2
);
179 case IIO_CHAN_INFO_OFFSET
:
181 * Processed channels are scaled 1-to-1 and source offset is
182 * already taken into account.
184 * In other cases, real world measurement are expressed as:
186 * schan_scale * (raw + schan_offset)
188 * Given that the rescaler parameters are applied recursively:
190 * rescaler_scale * (schan_scale * (raw + schan_offset) +
195 * (rescaler_scale * schan_scale) * (raw +
196 * (schan_offset + rescaler_offset / schan_scale)
198 * Thus, reusing the original expression the parameters exposed
201 * scale = schan_scale * rescaler_scale
202 * offset = schan_offset + rescaler_offset / schan_scale
204 if (rescale
->chan_processed
) {
205 *val
= rescale
->offset
;
209 if (iio_channel_has_info(rescale
->source
->channel
,
210 IIO_CHAN_INFO_OFFSET
)) {
211 ret
= iio_read_channel_offset(rescale
->source
,
213 if (ret
!= IIO_VAL_INT
)
214 return ret
< 0 ? ret
: -EOPNOTSUPP
;
217 if (iio_channel_has_info(rescale
->source
->channel
,
218 IIO_CHAN_INFO_SCALE
)) {
219 ret
= iio_read_channel_scale(rescale
->source
, &scale
, &scale2
);
220 return rescale_process_offset(rescale
, ret
, scale
, scale2
,
221 schan_off
, val
, val2
);
225 * If we get here we have no scale so scale 1:1 but apply
226 * rescaler and offset, if any.
228 return rescale_process_offset(rescale
, IIO_VAL_FRACTIONAL
, 1, 1,
229 schan_off
, val
, val2
);
235 static int rescale_read_avail(struct iio_dev
*indio_dev
,
236 struct iio_chan_spec
const *chan
,
237 const int **vals
, int *type
, int *length
,
240 struct rescale
*rescale
= iio_priv(indio_dev
);
243 case IIO_CHAN_INFO_RAW
:
245 return iio_read_avail_channel_raw(rescale
->source
,
252 static const struct iio_info rescale_info
= {
253 .read_raw
= rescale_read_raw
,
254 .read_avail
= rescale_read_avail
,
257 static ssize_t
rescale_read_ext_info(struct iio_dev
*indio_dev
,
259 struct iio_chan_spec
const *chan
,
262 struct rescale
*rescale
= iio_priv(indio_dev
);
264 return iio_read_channel_ext_info(rescale
->source
,
265 rescale
->ext_info
[private].name
,
269 static ssize_t
rescale_write_ext_info(struct iio_dev
*indio_dev
,
271 struct iio_chan_spec
const *chan
,
272 const char *buf
, size_t len
)
274 struct rescale
*rescale
= iio_priv(indio_dev
);
276 return iio_write_channel_ext_info(rescale
->source
,
277 rescale
->ext_info
[private].name
,
281 static int rescale_configure_channel(struct device
*dev
,
282 struct rescale
*rescale
)
284 struct iio_chan_spec
*chan
= &rescale
->chan
;
285 struct iio_chan_spec
const *schan
= rescale
->source
->channel
;
288 chan
->output
= schan
->output
;
289 chan
->ext_info
= rescale
->ext_info
;
290 chan
->type
= rescale
->cfg
->type
;
292 if (iio_channel_has_info(schan
, IIO_CHAN_INFO_RAW
) &&
293 (iio_channel_has_info(schan
, IIO_CHAN_INFO_SCALE
) ||
294 iio_channel_has_info(schan
, IIO_CHAN_INFO_OFFSET
))) {
295 dev_info(dev
, "using raw+scale/offset source channel\n");
296 } else if (iio_channel_has_info(schan
, IIO_CHAN_INFO_PROCESSED
)) {
297 dev_info(dev
, "using processed channel\n");
298 rescale
->chan_processed
= true;
300 dev_err(dev
, "source channel is not supported\n");
304 chan
->info_mask_separate
= BIT(IIO_CHAN_INFO_RAW
) |
305 BIT(IIO_CHAN_INFO_SCALE
);
308 chan
->info_mask_separate
|= BIT(IIO_CHAN_INFO_OFFSET
);
311 * Using .read_avail() is fringe to begin with and makes no sense
312 * whatsoever for processed channels, so we make sure that this cannot
313 * be called on a processed channel.
315 if (iio_channel_has_available(schan
, IIO_CHAN_INFO_RAW
) &&
316 !rescale
->chan_processed
)
317 chan
->info_mask_separate_available
|= BIT(IIO_CHAN_INFO_RAW
);
322 static int rescale_current_sense_amplifier_props(struct device
*dev
,
323 struct rescale
*rescale
)
331 ret
= device_property_read_u32(dev
, "sense-resistor-micro-ohms",
334 dev_err(dev
, "failed to read the sense resistance: %d\n", ret
);
338 device_property_read_u32(dev
, "sense-gain-mult", &gain_mult
);
339 device_property_read_u32(dev
, "sense-gain-div", &gain_div
);
342 * Calculate the scaling factor, 1 / (gain * sense), or
343 * gain_div / (gain_mult * sense), while trying to keep the
344 * numerator/denominator from overflowing.
346 factor
= gcd(sense
, 1000000);
347 rescale
->numerator
= 1000000 / factor
;
348 rescale
->denominator
= sense
/ factor
;
350 factor
= gcd(rescale
->numerator
, gain_mult
);
351 rescale
->numerator
/= factor
;
352 rescale
->denominator
*= gain_mult
/ factor
;
354 factor
= gcd(rescale
->denominator
, gain_div
);
355 rescale
->numerator
*= gain_div
/ factor
;
356 rescale
->denominator
/= factor
;
361 static int rescale_current_sense_shunt_props(struct device
*dev
,
362 struct rescale
*rescale
)
368 ret
= device_property_read_u32(dev
, "shunt-resistor-micro-ohms",
371 dev_err(dev
, "failed to read the shunt resistance: %d\n", ret
);
375 factor
= gcd(shunt
, 1000000);
376 rescale
->numerator
= 1000000 / factor
;
377 rescale
->denominator
= shunt
/ factor
;
382 static int rescale_voltage_divider_props(struct device
*dev
,
383 struct rescale
*rescale
)
388 ret
= device_property_read_u32(dev
, "output-ohms",
389 &rescale
->denominator
);
391 dev_err(dev
, "failed to read output-ohms: %d\n", ret
);
395 ret
= device_property_read_u32(dev
, "full-ohms",
396 &rescale
->numerator
);
398 dev_err(dev
, "failed to read full-ohms: %d\n", ret
);
402 factor
= gcd(rescale
->numerator
, rescale
->denominator
);
403 rescale
->numerator
/= factor
;
404 rescale
->denominator
/= factor
;
409 static int rescale_temp_sense_rtd_props(struct device
*dev
,
410 struct rescale
*rescale
)
419 ret
= device_property_read_u32(dev
, "excitation-current-microamp",
422 dev_err(dev
, "failed to read excitation-current-microamp: %d\n",
427 ret
= device_property_read_u32(dev
, "alpha-ppm-per-celsius", &alpha
);
429 dev_err(dev
, "failed to read alpha-ppm-per-celsius: %d\n",
434 ret
= device_property_read_u32(dev
, "r-naught-ohms", &r0
);
436 dev_err(dev
, "failed to read r-naught-ohms: %d\n", ret
);
440 tmp
= r0
* iexc
* alpha
/ 1000000;
441 factor
= gcd(tmp
, 1000000);
442 rescale
->numerator
= 1000000 / factor
;
443 rescale
->denominator
= tmp
/ factor
;
445 rescale
->offset
= -1 * ((r0
* iexc
) / 1000);
450 static int rescale_temp_transducer_props(struct device
*dev
,
451 struct rescale
*rescale
)
458 device_property_read_u32(dev
, "sense-offset-millicelsius", &offset
);
459 device_property_read_u32(dev
, "sense-resistor-ohms", &sense
);
460 ret
= device_property_read_u32(dev
, "alpha-ppm-per-celsius", &alpha
);
462 dev_err(dev
, "failed to read alpha-ppm-per-celsius: %d\n", ret
);
466 rescale
->numerator
= 1000000;
467 rescale
->denominator
= alpha
* sense
;
469 rescale
->offset
= div_s64((s64
)offset
* rescale
->denominator
,
475 enum rescale_variant
{
476 CURRENT_SENSE_AMPLIFIER
,
483 static const struct rescale_cfg rescale_cfg
[] = {
484 [CURRENT_SENSE_AMPLIFIER
] = {
486 .props
= rescale_current_sense_amplifier_props
,
488 [CURRENT_SENSE_SHUNT
] = {
490 .props
= rescale_current_sense_shunt_props
,
492 [VOLTAGE_DIVIDER
] = {
494 .props
= rescale_voltage_divider_props
,
498 .props
= rescale_temp_sense_rtd_props
,
500 [TEMP_TRANSDUCER
] = {
502 .props
= rescale_temp_transducer_props
,
506 static const struct of_device_id rescale_match
[] = {
507 { .compatible
= "current-sense-amplifier",
508 .data
= &rescale_cfg
[CURRENT_SENSE_AMPLIFIER
], },
509 { .compatible
= "current-sense-shunt",
510 .data
= &rescale_cfg
[CURRENT_SENSE_SHUNT
], },
511 { .compatible
= "voltage-divider",
512 .data
= &rescale_cfg
[VOLTAGE_DIVIDER
], },
513 { .compatible
= "temperature-sense-rtd",
514 .data
= &rescale_cfg
[TEMP_SENSE_RTD
], },
515 { .compatible
= "temperature-transducer",
516 .data
= &rescale_cfg
[TEMP_TRANSDUCER
], },
519 MODULE_DEVICE_TABLE(of
, rescale_match
);
521 static int rescale_probe(struct platform_device
*pdev
)
523 struct device
*dev
= &pdev
->dev
;
524 struct iio_dev
*indio_dev
;
525 struct iio_channel
*source
;
526 struct rescale
*rescale
;
532 source
= devm_iio_channel_get(dev
, NULL
);
534 return dev_err_probe(dev
, PTR_ERR(source
),
535 "failed to get source channel\n");
537 sizeof_ext_info
= iio_get_channel_ext_info_count(source
);
538 if (sizeof_ext_info
) {
539 sizeof_ext_info
+= 1; /* one extra entry for the sentinel */
540 sizeof_ext_info
*= sizeof(*rescale
->ext_info
);
543 sizeof_priv
= sizeof(*rescale
) + sizeof_ext_info
;
545 indio_dev
= devm_iio_device_alloc(dev
, sizeof_priv
);
549 rescale
= iio_priv(indio_dev
);
551 rescale
->cfg
= device_get_match_data(dev
);
552 rescale
->numerator
= 1;
553 rescale
->denominator
= 1;
556 ret
= rescale
->cfg
->props(dev
, rescale
);
560 if (!rescale
->numerator
|| !rescale
->denominator
) {
561 dev_err(dev
, "invalid scaling factor.\n");
565 platform_set_drvdata(pdev
, indio_dev
);
567 rescale
->source
= source
;
569 indio_dev
->name
= dev_name(dev
);
570 indio_dev
->info
= &rescale_info
;
571 indio_dev
->modes
= INDIO_DIRECT_MODE
;
572 indio_dev
->channels
= &rescale
->chan
;
573 indio_dev
->num_channels
= 1;
574 if (sizeof_ext_info
) {
575 rescale
->ext_info
= devm_kmemdup(dev
,
576 source
->channel
->ext_info
,
577 sizeof_ext_info
, GFP_KERNEL
);
578 if (!rescale
->ext_info
)
581 for (i
= 0; rescale
->ext_info
[i
].name
; ++i
) {
582 struct iio_chan_spec_ext_info
*ext_info
=
583 &rescale
->ext_info
[i
];
585 if (source
->channel
->ext_info
[i
].read
)
586 ext_info
->read
= rescale_read_ext_info
;
587 if (source
->channel
->ext_info
[i
].write
)
588 ext_info
->write
= rescale_write_ext_info
;
589 ext_info
->private = i
;
593 ret
= rescale_configure_channel(dev
, rescale
);
597 return devm_iio_device_register(dev
, indio_dev
);
600 static struct platform_driver rescale_driver
= {
601 .probe
= rescale_probe
,
603 .name
= "iio-rescale",
604 .of_match_table
= rescale_match
,
607 module_platform_driver(rescale_driver
);
609 MODULE_DESCRIPTION("IIO rescale driver");
610 MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
611 MODULE_LICENSE("GPL v2");