From: Liam Beguin <[email protected]>
Add temperature rescaling support to the IIO Analog Front End driver.
This series includes minor bug fixes and adds support for RTD temperature
sensors as well as temperature transducers.
At first I tried to use iio_convert_raw_to_processed() to get more
precision out of processed values but ran into issues when one of my
ADCs didn't provide a scale. I tried to address this in the first two
patches.
When adding offset support to iio-rescale, I also noticed that
iio_read_channel_processed() assumes that the offset is always an
integer which I tried to address in the third patch without breaking
valid implicit truncations.
Related to: https://patchwork.kernel.org/project/linux-iio/list/?series=483087
Changes since v3:
- drop unnecessary fallthrough statements
- drop redundant local variables in some calculations
- fix s64 divisions on 32bit platforms by using do_div
- add comment describing iio-rescaler offset calculation
- drop unnecessary MAINTAINERS entry
Changes since v2:
- don't break implicit offset truncations
- make a best effort to get a valid value for fractional types
- drop return value change in iio_convert_raw_to_processed_unlocked()
- don't rely on processed value for offset calculation
- add INT_PLUS_{MICRO,NANO} support in iio-rescale
- revert generic implementation in favor of temperature-sense-rtd and
temperature-transducer
- add separate section to MAINTAINERS file
Changes since v1:
- rebase on latest iio `testing` branch
- also apply consumer scale on integer channel scale types
- don't break implicit truncation in processed channel offset
calculation
- drop temperature AFE flavors in favor of a simpler generic
implementation
Thanks for your time
Liam Beguin (10):
iio: inkern: apply consumer scale on IIO_VAL_INT cases
iio: inkern: apply consumer scale when no channel scale is available
iio: inkern: make a best effort on offset calculation
iio: afe: rescale: reduce risk of integer overflow
iio: afe: rescale: add INT_PLUS_{MICRO,NANO} support
iio: afe: rescale: add offset support
iio: afe: rescale: add RTD temperature sensor support
iio: afe: rescale: add temperature transducers
dt-bindings: iio: afe: add bindings for temperature-sense-rtd
dt-bindings: iio: afe: add bindings for temperature transducers
.../iio/afe/temperature-sense-rtd.yaml | 101 ++++++++++
.../iio/afe/temperature-transducer.yaml | 111 +++++++++++
drivers/iio/afe/iio-rescale.c | 173 +++++++++++++++++-
drivers/iio/inkern.c | 40 +++-
4 files changed, 413 insertions(+), 12 deletions(-)
create mode 100644 Documentation/devicetree/bindings/iio/afe/temperature-sense-rtd.yaml
create mode 100644 Documentation/devicetree/bindings/iio/afe/temperature-transducer.yaml
Range-diff against v3:
-: ------------ > 1: 42a7a1047edc iio: inkern: apply consumer scale on IIO_VAL_INT cases
-: ------------ > 2: a1cd89fdad11 iio: inkern: apply consumer scale when no channel scale is available
1: e74ff6b2f663 ! 3: ed0721fb6bd1 iio: inkern: make a best effort on offset calculation
@@ drivers/iio/inkern.c: EXPORT_SYMBOL_GPL(iio_read_channel_average_raw);
+ int offset_type, offset_val, offset_val2;
s64 raw64 = raw;
- int ret;
-+ int tmp;
- ret = iio_channel_read(chan, &offset, NULL, IIO_CHAN_INFO_OFFSET);
- if (ret >= 0)
@@ drivers/iio/inkern.c: EXPORT_SYMBOL_GPL(iio_read_channel_average_raw);
+ case IIO_VAL_INT:
+ break;
+ case IIO_VAL_INT_PLUS_MICRO:
-+ fallthrough;
+ case IIO_VAL_INT_PLUS_NANO:
+ /*
+ * Both IIO_VAL_INT_PLUS_MICRO and IIO_VAL_INT_PLUS_NANO
@@ drivers/iio/inkern.c: EXPORT_SYMBOL_GPL(iio_read_channel_average_raw);
+ */
+ break;
+ case IIO_VAL_FRACTIONAL:
-+ tmp = offset_val / offset_val2;
-+ offset_val = tmp;
++ offset_val /= offset_val2;
+ break;
+ case IIO_VAL_FRACTIONAL_LOG2:
-+ tmp = offset_val / (1 << offset_val2);
-+ offset_val = tmp;
++ offset_val /= (1 << offset_val2);
+ break;
+ default:
+ return -EINVAL;
2: a5696ca3c14f ! 4: e23e6cb26b92 iio: afe: rescale: reduce risk of integer overflow
@@ drivers/iio/afe/iio-rescale.c: static int rescale_read_raw(struct iio_dev *indio
+ tmp = (s64)*val * rescale->numerator;
+ tmp2 = (s64)*val2 * rescale->denominator;
+ factor = gcd(tmp, tmp2);
-+ *val = tmp / factor;
-+ *val2 = tmp2 / factor;
++ do_div(tmp, factor);
++ *val = tmp;
++ do_div(tmp2, factor);
++ *val2 = tmp2;
return ret;
case IIO_VAL_INT:
*val *= rescale->numerator;
3: 2b435a2f58e8 ! 5: 28203b672942 iio: afe: rescale: add INT_PLUS_{MICRO,NANO} support
@@ drivers/iio/afe/iio-rescale.c: static int rescale_read_raw(struct iio_dev *indio
*val = tmp;
return ret;
+ case IIO_VAL_INT_PLUS_NANO:
-+ fallthrough;
+ case IIO_VAL_INT_PLUS_MICRO:
+ tmp = (s64)*val * rescale->numerator;
+ *val = div_s64(tmp, rescale->denominator);
4: 577020b8326b ! 6: a6c944ae0f99 iio: afe: rescale: add offset support
@@ drivers/iio/afe/iio-rescale.c: static int rescale_read_raw(struct iio_dev *indio
+ * Processed channels are scaled 1-to-1 and source offset is
+ * already taken into account.
+ *
-+ * In other cases, the final offset value is defined by:
++ * In other cases, real world measurement are expressed as:
++ *
++ * schan_scale * (raw + schan_offset)
++ *
++ * Given that the rescaler parameters are applied recursively:
++ *
++ * rescaler_scale * (schan_scale * (raw + schan_offset) +
++ * rescaler_offset)
++ *
++ * Or,
++ *
++ * (rescaler_scale * schan_scale) * (raw +
++ * (schan_offset + rescaler_offset / schan_scale)
++ *
++ * Thus, reusing the original expression the parameters exposed
++ * to userspace are:
++ *
++ * scale = schan_scale * rescaler_scale
+ * offset = schan_offset + rescaler_offset / schan_scale
+ */
+ if (rescale->chan_processed) {
@@ drivers/iio/afe/iio-rescale.c: static int rescale_read_raw(struct iio_dev *indio
+ IIO_CHAN_INFO_OFFSET)) {
+ ret = iio_read_channel_offset(rescale->source,
+ &schan_off, NULL);
-+ if (ret < 0)
-+ return ret;
-+ else if (ret != IIO_VAL_INT)
-+ return -EOPNOTSUPP;
++ if (ret != IIO_VAL_INT)
++ return ret < 0 ? ret : -EOPNOTSUPP;
+ }
+
+ ret = iio_read_channel_scale(rescale->source, &scale, &scale2);
@@ drivers/iio/afe/iio-rescale.c: static int rescale_read_raw(struct iio_dev *indio
+ case IIO_VAL_INT_PLUS_NANO:
+ tmp = (s64)rescale->offset * 1000000000UL;
+ tmp2 = ((s64)scale * 1000000000UL) + scale2;
-+ factor = gcd(tmp, tmp2);
-+ tmp /= factor;
-+ tmp2 /= factor;
+ *val = div_s64(tmp, tmp2) + schan_off;
+ return IIO_VAL_INT;
+ case IIO_VAL_INT_PLUS_MICRO:
+ tmp = (s64)rescale->offset * 1000000UL;
+ tmp2 = ((s64)scale * 1000000UL) + scale2;
-+ factor = gcd(tmp, tmp2);
-+ tmp /= factor;
-+ tmp2 /= factor;
+ *val = div_s64(tmp, tmp2) + schan_off;
+ return IIO_VAL_INT;
+ default:
5: 0add5863ff00 = 7: cc5eb96512d5 iio: afe: rescale: add RTD temperature sensor support
6: 0306e16020d4 = 8: d8aa257aad35 iio: afe: rescale: add temperature transducers
7: 6906c5a21861 ! 9: f038d6a08ea2 dt-bindings: iio: afe: add bindings for temperature-sense-rtd
@@ Documentation/devicetree/bindings/iio/afe/temperature-sense-rtd.yaml (new)
+ Channel node of a voltage io-channel.
+
+ '#io-channel-cells':
-+ const: 1
++ const: 0
+
+ excitation-current-microamp:
+ description: The current fed through the RTD sensor.
@@ Documentation/devicetree/bindings/iio/afe/temperature-sense-rtd.yaml (new)
+ - |
+ pt1000_1: temperature-sensor0 {
+ compatible = "temperature-sense-rtd";
-+ #io-channel-cells = <1>;
++ #io-channel-cells = <0>;
+ io-channels = <&temp_adc1 0>;
+
+ excitation-current-microamp = <1000>; /* i = U/R = 5 / 5000 */
@@ Documentation/devicetree/bindings/iio/afe/temperature-sense-rtd.yaml (new)
+ r-naught-ohms = <1000>;
+ };
+...
-
- ## MAINTAINERS ##
-@@ MAINTAINERS: F: Documentation/devicetree/bindings/iio/afe/current-sense-shunt.yaml
- F: Documentation/devicetree/bindings/iio/afe/voltage-divider.yaml
- F: drivers/iio/afe/iio-rescale.c
-
-+IIO UNIT CONVERTER (TEMPERATURE)
-+M: Liam Beguin <[email protected]>
-+R: Peter Rosin <[email protected]>
-+L: [email protected]
-+S: Maintained
-+F: Documentation/devicetree/bindings/iio/afe/temperature-sense-rtd.yaml
-+
- IKANOS/ADI EAGLE ADSL USB DRIVER
- M: Matthieu Castet <[email protected]>
- M: Stanislaw Gruszka <[email protected]>
8: ac8d4eef179b ! 10: 1db42cb25254 dt-bindings: iio: afe: add bindings for temperature transducers
@@ Documentation/devicetree/bindings/iio/afe/temperature-transducer.yaml (new)
+ Channel node of a voltage io-channel.
+
+ '#io-channel-cells':
-+ const: 1
++ const: 0
+
+ sense-offset-millicelsius:
+ description: |
@@ Documentation/devicetree/bindings/iio/afe/temperature-transducer.yaml (new)
+ - |
+ ad950: temperature-sensor-0 {
+ compatible = "temperature-transducer";
-+ #io-channel-cells = <1>;
++ #io-channel-cells = <0>;
+ io-channels = <&temp_adc 3>;
+
+ sense-offset-millicelsius = <(-273150)>; /* Kelvin to degrees Celsius */
@@ Documentation/devicetree/bindings/iio/afe/temperature-transducer.yaml (new)
+ - |
+ znq_tmp: temperature-sensor-1 {
+ compatible = "temperature-transducer";
-+ #io-channel-cells = <1>;
++ #io-channel-cells = <0>;
+ io-channels = <&temp_adc 2>;
+
+ sense-offset-millicelsius = <(-273150)>; /* Kelvin to degrees Celsius */
+ alpha-ppm-per-celsius = <4000>; /* 4 mV/K */
+ };
+...
-
- ## MAINTAINERS ##
-@@ MAINTAINERS: R: Peter Rosin <[email protected]>
- L: [email protected]
- S: Maintained
- F: Documentation/devicetree/bindings/iio/afe/temperature-sense-rtd.yaml
-+F: Documentation/devicetree/bindings/iio/afe/temperature-transducer.yaml
-
- IKANOS/ADI EAGLE ADSL USB DRIVER
- M: Matthieu Castet <[email protected]>
base-commit: 6cbb3aa0f9d5d23221df787cf36f74d3866fdb78
--
2.30.1.489.g328c10930387
From: Liam Beguin <[email protected]>
When a consumer calls iio_read_channel_processed() and the channel has
an integer scale, the scale channel scale is applied and the processed
value is returned as expected.
On the other hand, if the consumer calls iio_convert_raw_to_processed()
the scaling factor requested by the consumer is not applied.
This for example causes the consumer to process mV when expecting uV.
Make sure to always apply the scaling factor requested by the consumer.
Fixes: 48e44ce0f881 ("iio:inkern: Add function to read the processed value")
Signed-off-by: Liam Beguin <[email protected]>
---
drivers/iio/inkern.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index 391a3380a1d1..b752fe5818e7 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -599,7 +599,7 @@ static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan,
switch (scale_type) {
case IIO_VAL_INT:
- *processed = raw64 * scale_val;
+ *processed = raw64 * scale_val * scale;
break;
case IIO_VAL_INT_PLUS_MICRO:
if (scale_val2 < 0)
--
2.30.1.489.g328c10930387
From: Liam Beguin <[email protected]>
When a consumer calls iio_read_channel_processed() and no channel scale
is available, it's assumed that the scale is one and the raw value is
returned as expected.
On the other hand, if the consumer calls iio_convert_raw_to_processed()
the scaling factor requested by the consumer is not applied.
This for example causes the consumer to process mV when expecting uV.
Make sure to always apply the scaling factor requested by the consumer.
Fixes: adc8ec5ff183 ("iio: inkern: pass through raw values if no scaling")
Signed-off-by: Liam Beguin <[email protected]>
---
drivers/iio/inkern.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index b752fe5818e7..b69027690ed5 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -590,10 +590,10 @@ static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan,
IIO_CHAN_INFO_SCALE);
if (scale_type < 0) {
/*
- * Just pass raw values as processed if no scaling is
- * available.
+ * If no channel scaling is available apply consumer scale to
+ * raw value and return.
*/
- *processed = raw;
+ *processed = raw * scale;
return 0;
}
--
2.30.1.489.g328c10930387
From: Liam Beguin <[email protected]>
Reduce the risk of integer overflow by doing the scale calculation with
64bit integers and looking for a Greatest Common Divider for both parts
of the fractional value.
Signed-off-by: Liam Beguin <[email protected]>
---
drivers/iio/afe/iio-rescale.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c
index 774eb3044edd..ba3bdcc69b16 100644
--- a/drivers/iio/afe/iio-rescale.c
+++ b/drivers/iio/afe/iio-rescale.c
@@ -39,7 +39,8 @@ static int rescale_read_raw(struct iio_dev *indio_dev,
int *val, int *val2, long mask)
{
struct rescale *rescale = iio_priv(indio_dev);
- unsigned long long tmp;
+ s64 tmp, tmp2;
+ u32 factor;
int ret;
switch (mask) {
@@ -67,8 +68,13 @@ static int rescale_read_raw(struct iio_dev *indio_dev,
}
switch (ret) {
case IIO_VAL_FRACTIONAL:
- *val *= rescale->numerator;
- *val2 *= rescale->denominator;
+ tmp = (s64)*val * rescale->numerator;
+ tmp2 = (s64)*val2 * rescale->denominator;
+ factor = gcd(tmp, tmp2);
+ do_div(tmp, factor);
+ *val = tmp;
+ do_div(tmp2, factor);
+ *val2 = tmp2;
return ret;
case IIO_VAL_INT:
*val *= rescale->numerator;
--
2.30.1.489.g328c10930387
From: Liam Beguin <[email protected]>
An ADC is often used to measure other quantities indirectly. This
binding describe one case, the measurement of a temperature through the
voltage across an RTD resistor such as a PT1000.
Signed-off-by: Liam Beguin <[email protected]>
---
.../iio/afe/temperature-sense-rtd.yaml | 101 ++++++++++++++++++
1 file changed, 101 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/afe/temperature-sense-rtd.yaml
diff --git a/Documentation/devicetree/bindings/iio/afe/temperature-sense-rtd.yaml b/Documentation/devicetree/bindings/iio/afe/temperature-sense-rtd.yaml
new file mode 100644
index 000000000000..11a3d4bd3065
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/afe/temperature-sense-rtd.yaml
@@ -0,0 +1,101 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/afe/temperature-sense-rtd.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Temperature Sense RTD
+
+maintainers:
+ - Liam Beguin <[email protected]>
+
+description: |
+ RTDs (Resistance Temperature Detectors) are a kind of temperature sensors
+ used to get a linear voltage to temperature reading within a give range
+ (usually 0 to 100 degrees Celsius).
+
+ When an io-channel measures the output voltage across an RTD such as a
+ PT1000, the interesting measurement is almost always the corresponding
+ temperature, not the voltage output. This binding describes such a circuit.
+
+ The general transfer function here is (using SI units)
+
+ V = R(T) * iexc
+ R(T) = r0 * (1 + alpha * T)
+ T = 1 / (alpha * r0 * iexc) * (V - r0 * iexc)
+
+ The following circuit matches what's in the examples section.
+
+ 5V0
+ -----
+ |
+ +---+----+
+ | R 5k |
+ +---+----+
+ |
+ V 1mA
+ |
+ +---- Vout
+ |
+ +---+----+
+ | PT1000 |
+ +---+----+
+ |
+ -----
+ GND
+
+properties:
+ compatible:
+ const: temperature-sense-rtd
+
+ io-channels:
+ maxItems: 1
+ description: |
+ Channel node of a voltage io-channel.
+
+ '#io-channel-cells':
+ const: 0
+
+ excitation-current-microamp:
+ description: The current fed through the RTD sensor.
+
+ alpha-ppm-per-celsius:
+ description: |
+ alpha can also be expressed in micro-ohms per ohm Celsius. It's a linear
+ approximation of the resistance versus temperature relationship
+ between 0 and 100 degrees Celsius.
+
+ alpha = (R_100 - R_0) / (100 * R_0)
+
+ Where, R_100 is the resistance of the sensor at 100 degrees Celsius, and
+ R_0 (or r-naught-ohms) is the resistance of the sensor at 0 degrees
+ Celsius.
+
+ Pure platinum has an alpha of 3925. Industry standards such as IEC60751
+ and ASTM E-1137 specify an alpha of 3850.
+
+ r-naught-ohms:
+ description: |
+ Resistance of the sensor at 0 degrees Celsius.
+ Common values are 100 for PT100, 500 for PT500, and 1000 for PT1000
+
+additionalProperties: false
+required:
+ - compatible
+ - io-channels
+ - excitation-current-microamp
+ - alpha-ppm-per-celsius
+ - r-naught-ohms
+
+examples:
+ - |
+ pt1000_1: temperature-sensor0 {
+ compatible = "temperature-sense-rtd";
+ #io-channel-cells = <0>;
+ io-channels = <&temp_adc1 0>;
+
+ excitation-current-microamp = <1000>; /* i = U/R = 5 / 5000 */
+ alpha-ppm-per-celsius = <3908>;
+ r-naught-ohms = <1000>;
+ };
+...
--
2.30.1.489.g328c10930387
From: Liam Beguin <[email protected]>
A temperature transducer is a device that converts a thermal quantity
into any other physical quantity. This patch add support for temperature
to voltage (like the LTC2997) and temperature to current (like the
AD590) linear transducers.
In both cases these are assumed to be connected to a voltage ADC.
Signed-off-by: Liam Beguin <[email protected]>
---
drivers/iio/afe/iio-rescale.c | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c
index 835aa92a1db2..8420bd01f378 100644
--- a/drivers/iio/afe/iio-rescale.c
+++ b/drivers/iio/afe/iio-rescale.c
@@ -387,11 +387,38 @@ static int rescale_temp_sense_rtd_props(struct device *dev,
return 0;
}
+static int rescale_temp_transducer_props(struct device *dev,
+ struct rescale *rescale)
+{
+ s32 offset = 0;
+ s32 sense = 1;
+ s32 alpha;
+ s64 tmp;
+ int ret;
+
+ device_property_read_u32(dev, "sense-offset-millicelsius", &offset);
+ device_property_read_u32(dev, "sense-resistor-ohms", &sense);
+ ret = device_property_read_u32(dev, "alpha-ppm-per-celsius", &alpha);
+ if (ret) {
+ dev_err(dev, "failed to read alpha-ppm-per-celsius: %d\n", ret);
+ return ret;
+ }
+
+ rescale->numerator = 1000000;
+ rescale->denominator = alpha * sense;
+
+ tmp = (s64)offset * (s64)alpha * (s64)sense;
+ rescale->offset = div_s64(tmp, (s32)1000000);
+
+ return 0;
+}
+
enum rescale_variant {
CURRENT_SENSE_AMPLIFIER,
CURRENT_SENSE_SHUNT,
VOLTAGE_DIVIDER,
TEMP_SENSE_RTD,
+ TEMP_TRANSDUCER,
};
static const struct rescale_cfg rescale_cfg[] = {
@@ -411,6 +438,10 @@ static const struct rescale_cfg rescale_cfg[] = {
.type = IIO_TEMP,
.props = rescale_temp_sense_rtd_props,
},
+ [TEMP_TRANSDUCER] = {
+ .type = IIO_TEMP,
+ .props = rescale_temp_transducer_props,
+ },
};
static const struct of_device_id rescale_match[] = {
@@ -422,6 +453,8 @@ static const struct of_device_id rescale_match[] = {
.data = &rescale_cfg[VOLTAGE_DIVIDER], },
{ .compatible = "temperature-sense-rtd",
.data = &rescale_cfg[TEMP_SENSE_RTD], },
+ { .compatible = "temperature-transducer",
+ .data = &rescale_cfg[TEMP_TRANSDUCER], },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rescale_match);
--
2.30.1.489.g328c10930387
From: Liam Beguin <[email protected]>
An ADC is often used to measure other quantities indirectly.
This binding describe one case, the measurement of a temperature
through a temperature transducer (either voltage or current).
Signed-off-by: Liam Beguin <[email protected]>
---
.../iio/afe/temperature-transducer.yaml | 111 ++++++++++++++++++
1 file changed, 111 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/afe/temperature-transducer.yaml
diff --git a/Documentation/devicetree/bindings/iio/afe/temperature-transducer.yaml b/Documentation/devicetree/bindings/iio/afe/temperature-transducer.yaml
new file mode 100644
index 000000000000..b5a4fbfe75e4
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/afe/temperature-transducer.yaml
@@ -0,0 +1,111 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/afe/temperature-transducer.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Temperature Transducer
+
+maintainers:
+ - Liam Beguin <[email protected]>
+
+description: |
+ A temperature transducer is a device that converts a thermal quantity
+ into any other physical quantity. This binding applies to temperature to
+ voltage (like the LTC2997), and temperature to current (like the AD590)
+ linear transducers.
+ In both cases these are assumed to be connected to a voltage ADC.
+
+ When an io-channel measures the output voltage of a temperature analog front
+ end such as a temperature transducer, the interesting measurement is almost
+ always the corresponding temperature, not the voltage output. This binding
+ describes such a circuit.
+
+ The general transfer function here is (using SI units)
+ V(T) = Rsense * Isense(T)
+ T = (Isense(T) / alpha) + offset
+ T = 1 / (Rsense * alpha) * (V + offset * Rsense * alpha)
+
+ When using a temperature to voltage transducer, Rsense is set to 1.
+
+ The following circuits show a temperature to current and a temperature to
+ voltage transducer that can be used with this binding.
+
+ VCC
+ -----
+ |
+ +---+---+
+ | AD590 | VCC
+ +---+---+ -----
+ | |
+ V proportional to T +----+----+
+ | D+ --+ |
+ +---- Vout | LTC2997 +--- Vout
+ | D- --+ |
+ +---+----+ +---------+
+ | Rsense | |
+ +---+----+ -----
+ | GND
+ -----
+ GND
+
+properties:
+ compatible:
+ const: temperature-transducer
+
+ io-channels:
+ maxItems: 1
+ description: |
+ Channel node of a voltage io-channel.
+
+ '#io-channel-cells':
+ const: 0
+
+ sense-offset-millicelsius:
+ description: |
+ Temperature offset. The default is <0>.
+ This offset is commonly used to convert from Kelvins to degrees Celsius.
+ In that case, sense-offset-millicelsius would be set to <(-273150)>.
+
+ sense-resistor-ohms:
+ description: |
+ The sense resistor. Defaults to <1>.
+ Set sense-resistor-ohms to <1> when using a temperature to voltage
+ transducer.
+
+ alpha-ppm-per-celsius:
+ description: |
+ Sometimes referred to as output gain, slope, or temperature coefficient.
+
+ alpha is expressed in parts per million which can be micro-amps per
+ degrees Celsius or micro-volts per degrees Celsius. The is the main
+ characteristic of a temperature transducer and should be stated in the
+ datasheet.
+
+additionalProperties: false
+required:
+ - compatible
+ - io-channels
+ - alpha-ppm-per-celsius
+
+examples:
+ - |
+ ad950: temperature-sensor-0 {
+ compatible = "temperature-transducer";
+ #io-channel-cells = <0>;
+ io-channels = <&temp_adc 3>;
+
+ sense-offset-millicelsius = <(-273150)>; /* Kelvin to degrees Celsius */
+ sense-resistor-ohms = <8060>;
+ alpha-ppm-per-celsius = <1>; /* 1 uA/K */
+ };
+ - |
+ znq_tmp: temperature-sensor-1 {
+ compatible = "temperature-transducer";
+ #io-channel-cells = <0>;
+ io-channels = <&temp_adc 2>;
+
+ sense-offset-millicelsius = <(-273150)>; /* Kelvin to degrees Celsius */
+ alpha-ppm-per-celsius = <4000>; /* 4 mV/K */
+ };
+...
--
2.30.1.489.g328c10930387
From: Liam Beguin <[email protected]>
This is a preparatory change required for the addition of temperature
sensing front ends.
Signed-off-by: Liam Beguin <[email protected]>
---
drivers/iio/afe/iio-rescale.c | 72 +++++++++++++++++++++++++++++++++++
1 file changed, 72 insertions(+)
diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c
index 1d0e24145d87..7b241e0d8638 100644
--- a/drivers/iio/afe/iio-rescale.c
+++ b/drivers/iio/afe/iio-rescale.c
@@ -32,6 +32,7 @@ struct rescale {
bool chan_processed;
s32 numerator;
s32 denominator;
+ s32 offset;
};
static int rescale_read_raw(struct iio_dev *indio_dev,
@@ -39,6 +40,8 @@ static int rescale_read_raw(struct iio_dev *indio_dev,
int *val, int *val2, long mask)
{
struct rescale *rescale = iio_priv(indio_dev);
+ int scale, scale2;
+ int schan_off = 0;
s64 tmp, tmp2;
u32 factor;
int ret;
@@ -100,6 +103,71 @@ static int rescale_read_raw(struct iio_dev *indio_dev,
dev_err(&indio_dev->dev, "unsupported type %d\n", ret);
return -EOPNOTSUPP;
}
+ case IIO_CHAN_INFO_OFFSET:
+ /*
+ * Processed channels are scaled 1-to-1 and source offset is
+ * already taken into account.
+ *
+ * In other cases, real world measurement are expressed as:
+ *
+ * schan_scale * (raw + schan_offset)
+ *
+ * Given that the rescaler parameters are applied recursively:
+ *
+ * rescaler_scale * (schan_scale * (raw + schan_offset) +
+ * rescaler_offset)
+ *
+ * Or,
+ *
+ * (rescaler_scale * schan_scale) * (raw +
+ * (schan_offset + rescaler_offset / schan_scale)
+ *
+ * Thus, reusing the original expression the parameters exposed
+ * to userspace are:
+ *
+ * scale = schan_scale * rescaler_scale
+ * offset = schan_offset + rescaler_offset / schan_scale
+ */
+ if (rescale->chan_processed) {
+ *val = rescale->offset;
+ return IIO_VAL_INT;
+ }
+
+ if (iio_channel_has_info(rescale->source->channel,
+ IIO_CHAN_INFO_OFFSET)) {
+ ret = iio_read_channel_offset(rescale->source,
+ &schan_off, NULL);
+ if (ret != IIO_VAL_INT)
+ return ret < 0 ? ret : -EOPNOTSUPP;
+ }
+
+ ret = iio_read_channel_scale(rescale->source, &scale, &scale2);
+ switch (ret) {
+ case IIO_VAL_FRACTIONAL:
+ tmp = (s64)rescale->offset * scale2;
+ *val = div_s64(tmp, scale) + schan_off;
+ return IIO_VAL_INT;
+ case IIO_VAL_INT:
+ *val = div_s64(rescale->offset, scale) + schan_off;
+ return IIO_VAL_INT;
+ case IIO_VAL_FRACTIONAL_LOG2:
+ tmp = (s64)rescale->offset * (1 << scale2);
+ *val = div_s64(tmp, scale) + schan_off;
+ return IIO_VAL_INT;
+ case IIO_VAL_INT_PLUS_NANO:
+ tmp = (s64)rescale->offset * 1000000000UL;
+ tmp2 = ((s64)scale * 1000000000UL) + scale2;
+ *val = div_s64(tmp, tmp2) + schan_off;
+ return IIO_VAL_INT;
+ case IIO_VAL_INT_PLUS_MICRO:
+ tmp = (s64)rescale->offset * 1000000UL;
+ tmp2 = ((s64)scale * 1000000UL) + scale2;
+ *val = div_s64(tmp, tmp2) + schan_off;
+ return IIO_VAL_INT;
+ default:
+ dev_err(&indio_dev->dev, "unsupported type %d\n", ret);
+ return -EOPNOTSUPP;
+ }
default:
return -EINVAL;
}
@@ -176,6 +244,9 @@ static int rescale_configure_channel(struct device *dev,
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE);
+ if (rescale->offset)
+ chan->info_mask_separate |= BIT(IIO_CHAN_INFO_OFFSET);
+
/*
* Using .read_avail() is fringe to begin with and makes no sense
* whatsoever for processed channels, so we make sure that this cannot
@@ -340,6 +411,7 @@ static int rescale_probe(struct platform_device *pdev)
rescale->cfg = of_device_get_match_data(dev);
rescale->numerator = 1;
rescale->denominator = 1;
+ rescale->offset = 0;
ret = rescale->cfg->props(dev, rescale);
if (ret)
--
2.30.1.489.g328c10930387
From: Liam Beguin <[email protected]>
An RTD (Resistance Temperature Detector) is a kind of temperature
sensor used to get a linear voltage to temperature reading within a
give range (usually 0 to 100 degrees Celsius). Common types of RTDs
include PT100, PT500, and PT1000.
Signed-off-by: Liam Beguin <[email protected]>
---
drivers/iio/afe/iio-rescale.c | 48 +++++++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c
index 7b241e0d8638..835aa92a1db2 100644
--- a/drivers/iio/afe/iio-rescale.c
+++ b/drivers/iio/afe/iio-rescale.c
@@ -346,10 +346,52 @@ static int rescale_voltage_divider_props(struct device *dev,
return 0;
}
+static int rescale_temp_sense_rtd_props(struct device *dev,
+ struct rescale *rescale)
+{
+ u32 factor;
+ u32 alpha;
+ u32 iexc;
+ u32 tmp;
+ int ret;
+ u32 r0;
+
+ ret = device_property_read_u32(dev, "excitation-current-microamp",
+ &iexc);
+ if (ret) {
+ dev_err(dev, "failed to read excitation-current-microamp: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = device_property_read_u32(dev, "alpha-ppm-per-celsius", &alpha);
+ if (ret) {
+ dev_err(dev, "failed to read alpha-ppm-per-celsius: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = device_property_read_u32(dev, "r-naught-ohms", &r0);
+ if (ret) {
+ dev_err(dev, "failed to read r-naught-ohms: %d\n", ret);
+ return ret;
+ }
+
+ tmp = r0 * iexc * alpha / 1000000;
+ factor = gcd(tmp, 1000000);
+ rescale->numerator = 1000000 / factor;
+ rescale->denominator = tmp / factor;
+
+ rescale->offset = -1 * ((r0 * iexc) / 1000);
+
+ return 0;
+}
+
enum rescale_variant {
CURRENT_SENSE_AMPLIFIER,
CURRENT_SENSE_SHUNT,
VOLTAGE_DIVIDER,
+ TEMP_SENSE_RTD,
};
static const struct rescale_cfg rescale_cfg[] = {
@@ -365,6 +407,10 @@ static const struct rescale_cfg rescale_cfg[] = {
.type = IIO_VOLTAGE,
.props = rescale_voltage_divider_props,
},
+ [TEMP_SENSE_RTD] = {
+ .type = IIO_TEMP,
+ .props = rescale_temp_sense_rtd_props,
+ },
};
static const struct of_device_id rescale_match[] = {
@@ -374,6 +420,8 @@ static const struct of_device_id rescale_match[] = {
.data = &rescale_cfg[CURRENT_SENSE_SHUNT], },
{ .compatible = "voltage-divider",
.data = &rescale_cfg[VOLTAGE_DIVIDER], },
+ { .compatible = "temperature-sense-rtd",
+ .data = &rescale_cfg[TEMP_SENSE_RTD], },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rescale_match);
--
2.30.1.489.g328c10930387
From: Liam Beguin <[email protected]>
Add IIO_VAL_INT_PLUS_{NANO,MICRO} scaling support.
Scale the integer part and the decimal parts individually and keep the
original scaling type.
Signed-off-by: Liam Beguin <[email protected]>
---
drivers/iio/afe/iio-rescale.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c
index ba3bdcc69b16..1d0e24145d87 100644
--- a/drivers/iio/afe/iio-rescale.c
+++ b/drivers/iio/afe/iio-rescale.c
@@ -89,7 +89,15 @@ static int rescale_read_raw(struct iio_dev *indio_dev,
do_div(tmp, 1000000000LL);
*val = tmp;
return ret;
+ case IIO_VAL_INT_PLUS_NANO:
+ case IIO_VAL_INT_PLUS_MICRO:
+ tmp = (s64)*val * rescale->numerator;
+ *val = div_s64(tmp, rescale->denominator);
+ tmp = (s64)*val2 * rescale->numerator;
+ *val2 = div_s64(tmp, rescale->denominator);
+ return ret;
default:
+ dev_err(&indio_dev->dev, "unsupported type %d\n", ret);
return -EOPNOTSUPP;
}
default:
--
2.30.1.489.g328c10930387
From: Liam Beguin <[email protected]>
iio_convert_raw_to_processed_unlocked() assumes the offset is an
integer. Make a best effort to get a valid offset value for fractional
cases without breaking implicit truncations.
Fixes: 48e44ce0f881 ("iio:inkern: Add function to read the processed value")
Signed-off-by: Liam Beguin <[email protected]>
---
drivers/iio/inkern.c | 32 +++++++++++++++++++++++++++-----
1 file changed, 27 insertions(+), 5 deletions(-)
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index b69027690ed5..5e74d8983874 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -578,13 +578,35 @@ EXPORT_SYMBOL_GPL(iio_read_channel_average_raw);
static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan,
int raw, int *processed, unsigned int scale)
{
- int scale_type, scale_val, scale_val2, offset;
+ int scale_type, scale_val, scale_val2;
+ int offset_type, offset_val, offset_val2;
s64 raw64 = raw;
- int ret;
- ret = iio_channel_read(chan, &offset, NULL, IIO_CHAN_INFO_OFFSET);
- if (ret >= 0)
- raw64 += offset;
+ 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:
+ break;
+ case IIO_VAL_INT_PLUS_MICRO:
+ case IIO_VAL_INT_PLUS_NANO:
+ /*
+ * Both IIO_VAL_INT_PLUS_MICRO and IIO_VAL_INT_PLUS_NANO
+ * implicitely truncate the offset to it's integer form.
+ */
+ break;
+ case IIO_VAL_FRACTIONAL:
+ offset_val /= offset_val2;
+ break;
+ case IIO_VAL_FRACTIONAL_LOG2:
+ offset_val /= (1 << offset_val2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ raw64 += offset_val;
+ }
scale_type = iio_channel_read(chan, &scale_val, &scale_val2,
IIO_CHAN_INFO_SCALE);
--
2.30.1.489.g328c10930387
On 2021-07-06 18:09, Liam Beguin wrote:
> From: Liam Beguin <[email protected]>
>
> Reduce the risk of integer overflow by doing the scale calculation with
> 64bit integers and looking for a Greatest Common Divider for both parts
> of the fractional value.
>
> Signed-off-by: Liam Beguin <[email protected]>
> ---
> drivers/iio/afe/iio-rescale.c | 12 +++++++++---
> 1 file changed, 9 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c
> index 774eb3044edd..ba3bdcc69b16 100644
> --- a/drivers/iio/afe/iio-rescale.c
> +++ b/drivers/iio/afe/iio-rescale.c
> @@ -39,7 +39,8 @@ static int rescale_read_raw(struct iio_dev *indio_dev,
> int *val, int *val2, long mask)
> {
> struct rescale *rescale = iio_priv(indio_dev);
> - unsigned long long tmp;
> + s64 tmp, tmp2;
> + u32 factor;
> int ret;
>
> switch (mask) {
> @@ -67,8 +68,13 @@ static int rescale_read_raw(struct iio_dev *indio_dev,
> }
> switch (ret) {
> case IIO_VAL_FRACTIONAL:
> - *val *= rescale->numerator;
> - *val2 *= rescale->denominator;
> + tmp = (s64)*val * rescale->numerator;
> + tmp2 = (s64)*val2 * rescale->denominator;
> + factor = gcd(tmp, tmp2);
Hi!
gcd() isn't exactly free. I do not think it is suitable to call it for each
and every value. So, if you really need it, then it should only be used
when there is an actual overflow (or if there is a high risk if that's
somehow easier).
Cheers,
Peter
> + do_div(tmp, factor);
> + *val = tmp;
> + do_div(tmp2, factor);
> + *val2 = tmp2;
> return ret;
> case IIO_VAL_INT:
> *val *= rescale->numerator;
>
On 2021-07-06 18:09, Liam Beguin wrote:
> From: Liam Beguin <[email protected]>
>
> Add IIO_VAL_INT_PLUS_{NANO,MICRO} scaling support.
> Scale the integer part and the decimal parts individually and keep the
> original scaling type.
>
> Signed-off-by: Liam Beguin <[email protected]>
> ---
> drivers/iio/afe/iio-rescale.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c
> index ba3bdcc69b16..1d0e24145d87 100644
> --- a/drivers/iio/afe/iio-rescale.c
> +++ b/drivers/iio/afe/iio-rescale.c
> @@ -89,7 +89,15 @@ static int rescale_read_raw(struct iio_dev *indio_dev,
> do_div(tmp, 1000000000LL);
> *val = tmp;
> return ret;
> + case IIO_VAL_INT_PLUS_NANO:
> + case IIO_VAL_INT_PLUS_MICRO:
> + tmp = (s64)*val * rescale->numerator;
> + *val = div_s64(tmp, rescale->denominator);
> + tmp = (s64)*val2 * rescale->numerator;
> + *val2 = div_s64(tmp, rescale->denominator);
Hi!
You are losing precision, and you are not mormalising after the calculation.
I think it's better to not even attempt this given that the results can be
really poor.
Cheers,
Peter
> + return ret;
> default:
> + dev_err(&indio_dev->dev, "unsupported type %d\n", ret);
> return -EOPNOTSUPP;
> }
> default:
>
On Fri Jul 9, 2021 at 12:24 PM EDT, Peter Rosin wrote:
>
> On 2021-07-06 18:09, Liam Beguin wrote:
> > From: Liam Beguin <[email protected]>
> >
> > Reduce the risk of integer overflow by doing the scale calculation with
> > 64bit integers and looking for a Greatest Common Divider for both parts
> > of the fractional value.
> >
> > Signed-off-by: Liam Beguin <[email protected]>
> > ---
> > drivers/iio/afe/iio-rescale.c | 12 +++++++++---
> > 1 file changed, 9 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c
> > index 774eb3044edd..ba3bdcc69b16 100644
> > --- a/drivers/iio/afe/iio-rescale.c
> > +++ b/drivers/iio/afe/iio-rescale.c
> > @@ -39,7 +39,8 @@ static int rescale_read_raw(struct iio_dev *indio_dev,
> > int *val, int *val2, long mask)
> > {
> > struct rescale *rescale = iio_priv(indio_dev);
> > - unsigned long long tmp;
> > + s64 tmp, tmp2;
> > + u32 factor;
> > int ret;
> >
> > switch (mask) {
> > @@ -67,8 +68,13 @@ static int rescale_read_raw(struct iio_dev *indio_dev,
> > }
> > switch (ret) {
> > case IIO_VAL_FRACTIONAL:
> > - *val *= rescale->numerator;
> > - *val2 *= rescale->denominator;
> > + tmp = (s64)*val * rescale->numerator;
> > + tmp2 = (s64)*val2 * rescale->denominator;
> > + factor = gcd(tmp, tmp2);
Hi Peter,
>
> Hi!
>
> gcd() isn't exactly free. I do not think it is suitable to call it for
> each
> and every value. So, if you really need it, then it should only be used
> when there is an actual overflow (or if there is a high risk if that's
> somehow easier).
Understood, digging into this a little bit, it seems like
check_mul_overflow() could be used here.
I'll give it a try and will look at implementing Jonathan's suggestion
in case we're dealing with a case where gcd() returns 1.
Thanks,
Liam
>
> Cheers,
> Peter
>
> > + do_div(tmp, factor);
> > + *val = tmp;
> > + do_div(tmp2, factor);
> > + *val2 = tmp2;
> > return ret;
> > case IIO_VAL_INT:
> > *val *= rescale->numerator;
> >
On Fri Jul 9, 2021 at 12:29 PM EDT, Peter Rosin wrote:
>
>
> On 2021-07-06 18:09, Liam Beguin wrote:
> > From: Liam Beguin <[email protected]>
> >
> > Add IIO_VAL_INT_PLUS_{NANO,MICRO} scaling support.
> > Scale the integer part and the decimal parts individually and keep the
> > original scaling type.
> >
> > Signed-off-by: Liam Beguin <[email protected]>
> > ---
> > drivers/iio/afe/iio-rescale.c | 8 ++++++++
> > 1 file changed, 8 insertions(+)
> >
> > diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c
> > index ba3bdcc69b16..1d0e24145d87 100644
> > --- a/drivers/iio/afe/iio-rescale.c
> > +++ b/drivers/iio/afe/iio-rescale.c
> > @@ -89,7 +89,15 @@ static int rescale_read_raw(struct iio_dev *indio_dev,
> > do_div(tmp, 1000000000LL);
> > *val = tmp;
> > return ret;
> > + case IIO_VAL_INT_PLUS_NANO:
> > + case IIO_VAL_INT_PLUS_MICRO:
> > + tmp = (s64)*val * rescale->numerator;
> > + *val = div_s64(tmp, rescale->denominator);
> > + tmp = (s64)*val2 * rescale->numerator;
> > + *val2 = div_s64(tmp, rescale->denominator);
>
Hi Peter,
> Hi!
>
> You are losing precision, and you are not mormalising after the
> calculation.
Can you elaborate a little on what you mean here?
Do you mean that I should make sure that *val2, the PLUS_{NANO,MICRO}
part, doesn't contain an integer part? And if so transfer that part back
to *val?
> I think it's better to not even attempt this given that the results can
> be
> really poor.
Unfortunatelly, I'm kinda stuck with this as some of my ADC use these
types.
Thanks,
Liam
>
> Cheers,
> Peter
>
> > + return ret;
> > default:
> > + dev_err(&indio_dev->dev, "unsupported type %d\n", ret);
> > return -EOPNOTSUPP;
> > }
> > default:
> >
On 2021-07-09 21:30, Liam Beguin wrote:
> On Fri Jul 9, 2021 at 12:29 PM EDT, Peter Rosin wrote:
>>
>>
>> On 2021-07-06 18:09, Liam Beguin wrote:
>>> From: Liam Beguin <[email protected]>
>>>
>>> Add IIO_VAL_INT_PLUS_{NANO,MICRO} scaling support.
>>> Scale the integer part and the decimal parts individually and keep the
>>> original scaling type.
>>>
>>> Signed-off-by: Liam Beguin <[email protected]>
>>> ---
>>> drivers/iio/afe/iio-rescale.c | 8 ++++++++
>>> 1 file changed, 8 insertions(+)
>>>
>>> diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c
>>> index ba3bdcc69b16..1d0e24145d87 100644
>>> --- a/drivers/iio/afe/iio-rescale.c
>>> +++ b/drivers/iio/afe/iio-rescale.c
>>> @@ -89,7 +89,15 @@ static int rescale_read_raw(struct iio_dev *indio_dev,
>>> do_div(tmp, 1000000000LL);
>>> *val = tmp;
>>> return ret;
>>> + case IIO_VAL_INT_PLUS_NANO:
>>> + case IIO_VAL_INT_PLUS_MICRO:
>>> + tmp = (s64)*val * rescale->numerator;
>>> + *val = div_s64(tmp, rescale->denominator);
>>> + tmp = (s64)*val2 * rescale->numerator;
>>> + *val2 = div_s64(tmp, rescale->denominator);
>>
>
> Hi Peter,
>
>> Hi!
>>
>> You are losing precision, and you are not mormalising after the
>> calculation.
>
> Can you elaborate a little on what you mean here?
>
> Do you mean that I should make sure that *val2, the PLUS_{NANO,MICRO}
> part, doesn't contain an integer part? And if so transfer that part back
> to *val?
Yes. On 32-bit, you will easily wrap, especially for PLUS_NANO. You'd
only need a scale factor of 10 or so and a fractional part above .5 to
hit the roof (10 * 500000000 > 2^32).
But I also mean that you are losing precision when you are scaling
the integer part and the fractional part separately. That deserves
at least a comment, but ideally it should be handled correctly.
>> I think it's better to not even attempt this given that the results can
>> be
>> really poor.
>
> Unfortunatelly, I'm kinda stuck with this as some of my ADC use these
> types.
Ok. Crap. :-)
Cheers,
Peter
On Sat Jul 10, 2021 at 4:14 AM EDT, Peter Rosin wrote:
>
>
> On 2021-07-09 21:30, Liam Beguin wrote:
> > On Fri Jul 9, 2021 at 12:29 PM EDT, Peter Rosin wrote:
> >>
> >>
> >> On 2021-07-06 18:09, Liam Beguin wrote:
> >>> From: Liam Beguin <[email protected]>
> >>>
> >>> Add IIO_VAL_INT_PLUS_{NANO,MICRO} scaling support.
> >>> Scale the integer part and the decimal parts individually and keep the
> >>> original scaling type.
> >>>
> >>> Signed-off-by: Liam Beguin <[email protected]>
> >>> ---
> >>> drivers/iio/afe/iio-rescale.c | 8 ++++++++
> >>> 1 file changed, 8 insertions(+)
> >>>
> >>> diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c
> >>> index ba3bdcc69b16..1d0e24145d87 100644
> >>> --- a/drivers/iio/afe/iio-rescale.c
> >>> +++ b/drivers/iio/afe/iio-rescale.c
> >>> @@ -89,7 +89,15 @@ static int rescale_read_raw(struct iio_dev *indio_dev,
> >>> do_div(tmp, 1000000000LL);
> >>> *val = tmp;
> >>> return ret;
> >>> + case IIO_VAL_INT_PLUS_NANO:
> >>> + case IIO_VAL_INT_PLUS_MICRO:
> >>> + tmp = (s64)*val * rescale->numerator;
> >>> + *val = div_s64(tmp, rescale->denominator);
> >>> + tmp = (s64)*val2 * rescale->numerator;
> >>> + *val2 = div_s64(tmp, rescale->denominator);
> >>
> >
> > Hi Peter,
> >
> >> Hi!
> >>
> >> You are losing precision, and you are not mormalising after the
> >> calculation.
> >
> > Can you elaborate a little on what you mean here?
> >
> > Do you mean that I should make sure that *val2, the PLUS_{NANO,MICRO}
> > part, doesn't contain an integer part? And if so transfer that part back
> > to *val?
Hi Peter,
>
> Yes. On 32-bit, you will easily wrap, especially for PLUS_NANO. You'd
> only need a scale factor of 10 or so and a fractional part above .5 to
> hit the roof (10 * 500000000 > 2^32).
>
Right, That makes sense!
> But I also mean that you are losing precision when you are scaling
> the integer part and the fractional part separately. That deserves
> at least a comment, but ideally it should be handled correctly.
>
Oh got it! Apologies, How did I miss that...
All things considered, it might make sense to also implement the
test case Jonathan mentioned [1]. I'll look into it.
[1] https://lore.kernel.org/linux-devicetree/20210704173639.622371bf@jic23-huawei/
> >> I think it's better to not even attempt this given that the results can
> >> be
> >> really poor.
> >
> > Unfortunatelly, I'm kinda stuck with this as some of my ADC use these
> > types.
>
> Ok. Crap. :-)
Can't agree more :-)
Thanks,
Liam
>
> Cheers,
> Peter
On Tue, 06 Jul 2021 12:09:41 -0400, Liam Beguin wrote:
> From: Liam Beguin <[email protected]>
>
> An ADC is often used to measure other quantities indirectly. This
> binding describe one case, the measurement of a temperature through the
> voltage across an RTD resistor such as a PT1000.
>
> Signed-off-by: Liam Beguin <[email protected]>
> ---
> .../iio/afe/temperature-sense-rtd.yaml | 101 ++++++++++++++++++
> 1 file changed, 101 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/afe/temperature-sense-rtd.yaml
>
Reviewed-by: Rob Herring <[email protected]>
On Tue, Jul 06, 2021 at 12:09:42PM -0400, Liam Beguin wrote:
> From: Liam Beguin <[email protected]>
>
> An ADC is often used to measure other quantities indirectly.
> This binding describe one case, the measurement of a temperature
> through a temperature transducer (either voltage or current).
>
> Signed-off-by: Liam Beguin <[email protected]>
> ---
> .../iio/afe/temperature-transducer.yaml | 111 ++++++++++++++++++
> 1 file changed, 111 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/afe/temperature-transducer.yaml
>
> diff --git a/Documentation/devicetree/bindings/iio/afe/temperature-transducer.yaml b/Documentation/devicetree/bindings/iio/afe/temperature-transducer.yaml
> new file mode 100644
> index 000000000000..b5a4fbfe75e4
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/afe/temperature-transducer.yaml
> @@ -0,0 +1,111 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/afe/temperature-transducer.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Temperature Transducer
> +
> +maintainers:
> + - Liam Beguin <[email protected]>
> +
> +description: |
> + A temperature transducer is a device that converts a thermal quantity
> + into any other physical quantity. This binding applies to temperature to
> + voltage (like the LTC2997), and temperature to current (like the AD590)
> + linear transducers.
> + In both cases these are assumed to be connected to a voltage ADC.
> +
> + When an io-channel measures the output voltage of a temperature analog front
> + end such as a temperature transducer, the interesting measurement is almost
> + always the corresponding temperature, not the voltage output. This binding
> + describes such a circuit.
> +
> + The general transfer function here is (using SI units)
> + V(T) = Rsense * Isense(T)
> + T = (Isense(T) / alpha) + offset
> + T = 1 / (Rsense * alpha) * (V + offset * Rsense * alpha)
> +
> + When using a temperature to voltage transducer, Rsense is set to 1.
> +
> + The following circuits show a temperature to current and a temperature to
> + voltage transducer that can be used with this binding.
> +
> + VCC
> + -----
> + |
> + +---+---+
> + | AD590 | VCC
> + +---+---+ -----
> + | |
> + V proportional to T +----+----+
> + | D+ --+ |
> + +---- Vout | LTC2997 +--- Vout
> + | D- --+ |
> + +---+----+ +---------+
> + | Rsense | |
> + +---+----+ -----
> + | GND
> + -----
> + GND
> +
> +properties:
> + compatible:
> + const: temperature-transducer
> +
> + io-channels:
> + maxItems: 1
> + description: |
> + Channel node of a voltage io-channel.
> +
> + '#io-channel-cells':
> + const: 0
This is a io-channel consumer and producer?
> +
> + sense-offset-millicelsius:
> + description: |
> + Temperature offset. The default is <0>.
> + This offset is commonly used to convert from Kelvins to degrees Celsius.
> + In that case, sense-offset-millicelsius would be set to <(-273150)>.
default: 0
> +
> + sense-resistor-ohms:
> + description: |
> + The sense resistor. Defaults to <1>.
> + Set sense-resistor-ohms to <1> when using a temperature to voltage
> + transducer.
default: 1
Though why would we set the value to 1 if the default is 1?
> +
> + alpha-ppm-per-celsius:
> + description: |
> + Sometimes referred to as output gain, slope, or temperature coefficient.
> +
> + alpha is expressed in parts per million which can be micro-amps per
> + degrees Celsius or micro-volts per degrees Celsius. The is the main
> + characteristic of a temperature transducer and should be stated in the
> + datasheet.
> +
> +additionalProperties: false
Blank line here.
> +required:
> + - compatible
> + - io-channels
> + - alpha-ppm-per-celsius
> +
> +examples:
> + - |
> + ad950: temperature-sensor-0 {
> + compatible = "temperature-transducer";
> + #io-channel-cells = <0>;
> + io-channels = <&temp_adc 3>;
> +
> + sense-offset-millicelsius = <(-273150)>; /* Kelvin to degrees Celsius */
> + sense-resistor-ohms = <8060>;
> + alpha-ppm-per-celsius = <1>; /* 1 uA/K */
> + };
> + - |
> + znq_tmp: temperature-sensor-1 {
> + compatible = "temperature-transducer";
> + #io-channel-cells = <0>;
> + io-channels = <&temp_adc 2>;
> +
> + sense-offset-millicelsius = <(-273150)>; /* Kelvin to degrees Celsius */
> + alpha-ppm-per-celsius = <4000>; /* 4 mV/K */
> + };
> +...
> --
> 2.30.1.489.g328c10930387
>
>
On Mon Jul 12, 2021 at 12:11 PM EDT, Rob Herring wrote:
> On Tue, Jul 06, 2021 at 12:09:42PM -0400, Liam Beguin wrote:
> > From: Liam Beguin <[email protected]>
> >
> > An ADC is often used to measure other quantities indirectly.
> > This binding describe one case, the measurement of a temperature
> > through a temperature transducer (either voltage or current).
> >
> > Signed-off-by: Liam Beguin <[email protected]>
> > ---
> > .../iio/afe/temperature-transducer.yaml | 111 ++++++++++++++++++
> > 1 file changed, 111 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/iio/afe/temperature-transducer.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/iio/afe/temperature-transducer.yaml b/Documentation/devicetree/bindings/iio/afe/temperature-transducer.yaml
> > new file mode 100644
> > index 000000000000..b5a4fbfe75e4
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/iio/afe/temperature-transducer.yaml
> > @@ -0,0 +1,111 @@
> > +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/iio/afe/temperature-transducer.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Temperature Transducer
> > +
> > +maintainers:
> > + - Liam Beguin <[email protected]>
> > +
> > +description: |
> > + A temperature transducer is a device that converts a thermal quantity
> > + into any other physical quantity. This binding applies to temperature to
> > + voltage (like the LTC2997), and temperature to current (like the AD590)
> > + linear transducers.
> > + In both cases these are assumed to be connected to a voltage ADC.
> > +
> > + When an io-channel measures the output voltage of a temperature analog front
> > + end such as a temperature transducer, the interesting measurement is almost
> > + always the corresponding temperature, not the voltage output. This binding
> > + describes such a circuit.
> > +
> > + The general transfer function here is (using SI units)
> > + V(T) = Rsense * Isense(T)
> > + T = (Isense(T) / alpha) + offset
> > + T = 1 / (Rsense * alpha) * (V + offset * Rsense * alpha)
> > +
> > + When using a temperature to voltage transducer, Rsense is set to 1.
> > +
> > + The following circuits show a temperature to current and a temperature to
> > + voltage transducer that can be used with this binding.
> > +
> > + VCC
> > + -----
> > + |
> > + +---+---+
> > + | AD590 | VCC
> > + +---+---+ -----
> > + | |
> > + V proportional to T +----+----+
> > + | D+ --+ |
> > + +---- Vout | LTC2997 +--- Vout
> > + | D- --+ |
> > + +---+----+ +---------+
> > + | Rsense | |
> > + +---+----+ -----
> > + | GND
> > + -----
> > + GND
> > +
> > +properties:
> > + compatible:
> > + const: temperature-transducer
> > +
> > + io-channels:
> > + maxItems: 1
> > + description: |
> > + Channel node of a voltage io-channel.
> > +
> > + '#io-channel-cells':
> > + const: 0
Hi Rob,
>
> This is a io-channel consumer and producer?
>
Yes, this is a consumer and a producer.
It consumes a single ADC channel and can be fed to something like hwmon.
> > +
> > + sense-offset-millicelsius:
> > + description: |
> > + Temperature offset. The default is <0>.
> > + This offset is commonly used to convert from Kelvins to degrees Celsius.
> > + In that case, sense-offset-millicelsius would be set to <(-273150)>.
>
> default: 0
>
> > +
> > + sense-resistor-ohms:
> > + description: |
> > + The sense resistor. Defaults to <1>.
> > + Set sense-resistor-ohms to <1> when using a temperature to voltage
> > + transducer.
>
> default: 1
>
> Though why would we set the value to 1 if the default is 1?
>
I can rephrase this. I meant to say that the default will make this
behave like a temperature to voltage transducer.
Liam
> > +
> > + alpha-ppm-per-celsius:
> > + description: |
> > + Sometimes referred to as output gain, slope, or temperature coefficient.
> > +
> > + alpha is expressed in parts per million which can be micro-amps per
> > + degrees Celsius or micro-volts per degrees Celsius. The is the main
> > + characteristic of a temperature transducer and should be stated in the
> > + datasheet.
> > +
> > +additionalProperties: false
>
> Blank line here.
>
> > +required:
> > + - compatible
> > + - io-channels
> > + - alpha-ppm-per-celsius
> > +
> > +examples:
> > + - |
> > + ad950: temperature-sensor-0 {
> > + compatible = "temperature-transducer";
> > + #io-channel-cells = <0>;
> > + io-channels = <&temp_adc 3>;
> > +
> > + sense-offset-millicelsius = <(-273150)>; /* Kelvin to degrees Celsius */
> > + sense-resistor-ohms = <8060>;
> > + alpha-ppm-per-celsius = <1>; /* 1 uA/K */
> > + };
> > + - |
> > + znq_tmp: temperature-sensor-1 {
> > + compatible = "temperature-transducer";
> > + #io-channel-cells = <0>;
> > + io-channels = <&temp_adc 2>;
> > +
> > + sense-offset-millicelsius = <(-273150)>; /* Kelvin to degrees Celsius */
> > + alpha-ppm-per-celsius = <4000>; /* 4 mV/K */
> > + };
> > +...
> > --
> > 2.30.1.489.g328c10930387
> >
> >