iio: adc: Add support for AD4170 series of ADCs
Subsystems (4)
v4 cover iio: adc: Add support for AD4170 series of ADCs
Hi,
As always, thank you to all reviewers of previous versions of this set. v4 comes with a few changes to comply with suggestions provided to v3, being most of them related to the adaptation of dt-binding properties.
This patch set adds support for Analog Devices AD4170 and similar sigma-delta ADCs.
Patch 1 adds device tree documentation for the parts.
Patch 2 adds basic device support.
Patch 3 adds support for calibration scale.
Patch 4 adds support for calibration bias.
Patch 5 adds support for sample frequency along with filter type configuration. Patch 6 adds support for buffered ADC reading.
Patch 7 adds clock provider support
Patch 8 adds GPIO controller support.
Patch 9 adds internal temperature sensor support.
Patch 10 adds support for external RTD and bridge circuit sensors. Patch 11 adds timestamp channel [new patch]
Change log v3 -> v4
[Device tree changes]
- Dropped sensor-node and most of defs.
- Updated external sensor props to have similar name and type of adi,ad4130 ones.
- Added constraints to properties related to external bridge sensor excitation.
[General IIO driver changes]
- Locked device mutex to ensure attribute read correctness on all archs
- Fixed typo unasigned -> unassigned
[Basic driver patch]
- Added previously missing #include <linux/cleanup.h>.
- Moved struct completion declaration to reduce commit diff.
[Calibration scale/gain patch]
[Calibration bias/offset patch]
- No longer restoring calib gain or calib offset on reg write fail.
[Digital filter and sample frequency config patch]
- Use scoped_guard to ensure correct lock release order in ad4170_set_filter_type().
[Buffer support patch]
- Fixed a bug in the filling of the IIO device buffer.
[CLOCK provider patch]
- Explicitly stated that clock divider (CLKDIV) control support is not provided.
- Skipped clock provider register if "#clock-cells" is not present.
[GPIO controller patch]
- Made AD4170 depend on GPIOLIB.
[External sensor patch]
- Update to string adi,sensor-type dt property.
- Adapted external sensor dt prop parsing to work with the updated version of those props.
- Improvements to readability.
[New patch - Add timestamp channel]
Link to v3: https://lore.kernel.org/linux-iio/[email protected]/
Link to v2: https://lore.kernel.org/linux-iio/[email protected]/
Link to v1: https://lore.kernel.org/linux-iio/[email protected]/
Ana-Maria Cusco (1):
iio: adc: Add basic support for AD4170
Marcelo Schmitt (10):
dt-bindings: iio: adc: Add AD4170
iio: adc: ad4170: Add support for calibration gain
iio: adc: ad4170: Add support for calibration bias
iio: adc: ad4170: Add digital filter and sample frequency config
support
iio: adc: ad4170: Add support for buffered data capture
iio: adc: ad4170: Add clock provider support
iio: adc: ad4170: Add GPIO controller support
iio: adc: ad4170: Add support for internal temperature sensor
iio: adc: ad4170: Add support for weigh scale and RTD sensors
iio: adc: ad4170: Add timestamp channel
.../bindings/iio/adc/adi,ad4170.yaml | 543 +++ MAINTAINERS | 8 + drivers/iio/adc/Kconfig | 16 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad4170.c | 2973 +++++++++++++++++ 5 files changed, 3541 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml create mode 100644 drivers/iio/adc/ad4170.c
base-commit: c06335516e8c14f501a479a4d9de0e6c09c52ef2
Signature · 1 line
2.47.2
PATCH v4 1/11 dt-bindings: iio: adc: Add AD4170
Add device tree documentation for AD4170 and similar sigma-delta ADCs. The AD4170 is a 24-bit, multichannel, sigma-delta ADC.
---
Change log v3 -> v4
- Dropped sensor-node and most of defs.
- Updated external sensor props to have similar name and type of adi,ad4130 ones.
- Added constraints to properties related to external bridge sensor excitation.
Some explanation about the constraints to weigh scale sensor types.
The predefined ACX1, ACX1 negated, ACX2, and ACX2 negated signals are used to AC excite external bridge circuits and are output on GPIO2, GPIO0, GPIO3, and GPIO1, respectively. If only two pins are specified for AC excitation, only ACX1 and ACX2 (GPIO2 and GPIO3) are used. Because of that, if AC excitation is specified/requested, then those specific GPIO pins must be used with the bridge. Otherwise, the bridge won't get properly excited and we also cannot guarantee to avoid short-circuit conditions since the level set to GPIOs to DC excite the bridge depends on the GPIO number. See AD4170 datasheet Figure 113 Weigh Scale (AC Excitation) for the reference circuit diagram.
.../bindings/iio/adc/adi,ad4170.yaml | 543 ++++++++++++++++++ MAINTAINERS | 7 + 2 files changed, 550 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml new file +543 -0
@@ -0,0 +1,543 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ad4170.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD4170 and similar Analog to Digital Converters + +maintainers: + - Marcelo Schmitt <[email protected]> + +description: | + Analog Devices AD4170 series of Sigma-delta Analog to Digital Converters. + Specifications can be found at: + https://www.analog.com/media/en/technical-documentation/data-sheets/ad4170-4.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad4190-4.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad4195-4.pdf + +$ref: /schemas/spi/spi-peripheral-props.yaml# + +$defs: + reference-buffer: + description: | + Enable precharge buffer, full buffer, or skip reference buffering of + the positive/negative voltage reference. Because the output impedance + of the source driving the voltage reference inputs may be dynamic, RC + combinations of those inputs can cause DC gain errors if the reference + inputs go unbuffered into the ADC. Enable reference buffering if the + provided reference source has dynamic high impedance output. Note the + absolute voltage allowed on REFINn+ and REFINn- inputs is from + AVSS - 50 mV to AVDD + 50 mV when the reference buffers are disabled + but narrows to AVSS to AVDD when reference buffering is enabled or in + precharge mode. The valid options for this property are: + 0: Reference precharge buffer. + 1: Full reference buffering. + 2: Bypass reference buffers (buffering disabled). + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2] + default: 1 + + excitation-pin: + description: | + Analog input to apply excitation current to while the channel + is active. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 20 + +properties: + compatible: + enum: + - adi,ad4170 + - adi,ad4190 + - adi,ad4195 + + avss-supply: + description: + Reference voltage supply for AVSS. If provided, describes the magnitude + (absolute value) of the negative voltage supplied to the AVSS pin. Since + AVSS must be −2.625V minimum and 0V maximum, the declared supply voltage + must be between 0 and 2.65V. If not provided, AVSS is assumed to be at + system ground (0V). + + avdd-supply: + description: + A supply of 4.75V to 5.25V relative to AVSS that powers the chip (AVDD). + + iovdd-supply: + description: 1.7V to 5.25V reference supply to the serial interface (IOVDD). + + refin1p-supply: + description: REFIN+ supply that can be used as reference for conversion. + + refin1n-supply: + description: REFIN- supply that can be used as reference for conversion. If + provided, describes the magnitude (absolute value) of the negative voltage + supplied to the REFIN- pin. + + refin2p-supply: + description: REFIN2+ supply that can be used as reference for conversion. + + refin2n-supply: + description: REFIN2- supply that can be used as reference for conversion. If + provided, describes the magnitude (absolute value) of the negative voltage + supplied to the REFIN2- pin. + + spi-cpol: true + + spi-cpha: true + + interrupts: + description: + Interrupt for signaling the completion of conversion results. The data + ready signal (RDY) used as interrupt is by default provided on the SDO + pin. Alternatively, it can be provided on the DIG_AUX1 pin in which case + the chip disables the RDY function on SDO. Thus, there can be only one + data ready interrupt enabled at a time. + maxItems: 1 + + interrupt-names: + description: + Specify which pin should be configured as Data Ready interrupt. + enum: + - sdo + - dig_aux1 + maxItems: 1 + default: sdo + + clocks: + maxItems: 1 + description: + Optional external clock source. Can specify either an external clock or + external crystal. + + clock-names: + enum: + - ext-clk + - xtal + default: ext-clk + + '#clock-cells': + const: 0 + + clock-output-names: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + description: | + The first cell is for the GPIO number: 0 to 3. + The second cell takes standard GPIO flags. + + ldac-gpios: + description: + GPIO connected to DIG_AUX2 pin to be used as LDAC toggle to control the + transfer of data from the DAC_INPUT_A register to the DAC. + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + adi,vbias-pins: + description: Analog inputs to apply a voltage bias of (AVDD − AVSS) / 2 to. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 9 + items: + minimum: 0 + maximum: 8 + +allOf: + # Some devices don't have integrated DAC + - if: + properties: + compatible: + contains: + enum: + - adi,ad4190 + - adi,ad4195 + then: + properties: + ldac-gpios: false + + # If an external clock is set, the internal clock cannot go out and vice versa + - oneOf: + - required: [clocks] + properties: + '#clock-cells': false + - required: ['#clock-cells'] + properties: + clocks: false + +patternProperties: + "^channel@[0-9a-f]$": + $ref: /schemas/iio/adc/adc.yaml# + unevaluatedProperties: false + description: + Represents the external channels which are connected to the ADC. + + properties: + reg: + description: + The channel number. + minimum: 0 + maximum: 15 + + diff-channels: + description: | + This property is used for defining the inputs of a differential + voltage channel. The first value is the positive input and the second + value is the negative input of the channel. + + Besides the analog input pins AIN0 to AIN8, there are special inputs + that can be selected with the following values: + 17: Internal temperature sensor + 18: (AVDD-AVSS)/5 + 19: (IOVDD-DGND)/5 + 20: DAC output + 21: ALDO + 22: DLDO + 23: AVSS + 24: DGND + 25: REFIN+ + 26: REFIN- + 27: REFIN2+ + 28: REFIN2- + 29: REFOUT + For the internal temperature sensor, use the input number for both + inputs (i.e. diff-channels = <17 17>). + items: + enum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29] + + adi,reference-select: + description: | + Select the reference source to use when converting on the + specific channel. Valid values are: + 0: REFIN+/REFIN- + 1: REFIN2+/REFIN2− + 2: REFOUT/AVSS (internal reference) + 3: AVDD/AVSS + If not specified, REFOUT/AVSS is used. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 3] + default: 1 + + adi,positive-reference-buffer: + $ref: '#/$defs/reference-buffer' + + adi,negative-reference-buffer: + $ref: '#/$defs/reference-buffer' + + adi,sensor-type: + description: + The AD4170 and similar designs have features to aid interfacing with + load cell weigh scale, RTD, and thermocouple sensors. Each of those + sensor types requires either distinct wiring configuration or + external circuitry for proper sensor operation and can use different + ADC chip functionality on their setups. A key characteristic of those + external sensors is that they must be excited either by voltage supply + or by ADC chip excitation signals. The sensor can then be read through + a pair of analog inputs. This property specifies which particular + sensor type is connected to the ADC so it can be properly setup and + handled. Omit this property for conventional (not weigh scale, RTD, or + thermocouple) ADC channel setups. + $ref: /schemas/types.yaml#/definitions/string + enum: [ weighscale, rtd, thermocouple ] + + adi,excitation-pin-0: + $ref: '#/$defs/excitation-pin' + + adi,excitation-pin-1: + $ref: '#/$defs/excitation-pin' + + adi,excitation-pin-2: + $ref: '#/$defs/excitation-pin' + + adi,excitation-pin-3: + $ref: '#/$defs/excitation-pin' + + adi,excitation-current-0-microamp: + description: + Excitation current in microamperes to be applied to pin specified in + adi,excitation-pin-0 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500] + default: 0 + + adi,excitation-current-1-microamp: + description: + Excitation current in microamperes to be applied to pin specified in + adi,excitation-pin-1 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500] + default: 0 + + adi,excitation-current-2-microamp: + description: + Excitation current in microamperes to be applied to pin specified in + adi,excitation-pin-2 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500] + default: 0 + + adi,excitation-current-3-microamp: + description: + Excitation current in microamperes to be applied to pin specified in + adi,excitation-pin-3 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500] + default: 0 + + adi,excitation-ac: + type: boolean + description: + Whether the external sensor has to be AC or DC excited. When omitted, + it is DC excited. + + allOf: + - oneOf: + - required: [single-channel, common-mode-channel] + properties: + diff-channels: false + - required: [diff-channels] + properties: + single-channel: false + common-mode-channel: false + # Usual ADC channels don't need external circuitry excitation. + - if: + not: + required: + - adi,sensor-type + then: + properties: + adi,excitation-pin-0: false + adi,excitation-pin-1: false + adi,excitation-pin-2: false + adi,excitation-pin-3: false + adi,excitation-current-0-microamp: false + adi,excitation-current-1-microamp: false + adi,excitation-current-2-microamp: false + adi,excitation-current-3-microamp: false + adi,excitation-ac: false + # Weigh scale bridge AC excited with one pair of predefined signals. + - if: + allOf: + - properties: + adi,sensor-type: + contains: + const: weighscale + - required: + - adi,excitation-ac + - adi,excitation-pin-2 + - adi,excitation-pin-3 + - not: + required: + - adi,excitation-current-2-microamp + - adi,excitation-current-3-microamp + then: + properties: + adi,excitation-pin-2: + const: 19 + adi,excitation-pin-3: + const: 20 + # Weigh scale bridge AC excited with two pairs of predefined signals. + - if: + allOf: + - properties: + adi,sensor-type: + contains: + const: weighscale + - required: + - adi,excitation-ac + - adi,excitation-pin-0 + - adi,excitation-pin-1 + - adi,excitation-pin-2 + - adi,excitation-pin-3 + - not: + required: + - adi,excitation-current-0-microamp + - adi,excitation-current-1-microamp + - adi,excitation-current-2-microamp + - adi,excitation-current-3-microamp + then: + properties: + adi,excitation-pin-0: + const: 17 + adi,excitation-pin-1: + const: 18 + adi,excitation-pin-2: + const: 19 + adi,excitation-pin-3: + const: 20 + +required: + - compatible + - reg + - avdd-supply + - iovdd-supply + - spi-cpol + - spi-cpha + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad4170"; + reg = <0>; + spi-max-frequency = <20000000>; + spi-cpol; + spi-cpha; + avdd-supply = <&avdd>; + iovdd-supply = <&iovdd>; + clocks = <&clk>; + clock-names = "xtal"; + interrupt-parent = <&gpio_in>; + interrupts = <0 IRQ_TYPE_EDGE_FALLING>; + adi,vbias-pins = <7>; + #address-cells = <1>; + #size-cells = <0>; + + // Sample AIN0 with respect to DGND throughout AVDD/DGND input range + // Pseudo-differential unipolar + channel@0 { + reg = <0>; + single-channel = <0>; + common-mode-channel = <24>; + adi,reference-select = <3>; + }; + // Weigh scale sensor + channel@1 { + reg = <1>; + bipolar; + diff-channels = <1 2>; + adi,reference-select = <0>; + adi,positive-reference-buffer = <0>; + adi,negative-reference-buffer = <0>; + adi,sensor-type = "weighscale"; + adi,excitation-pin-2 = <19>; + adi,excitation-pin-3 = <20>; + adi,excitation-ac; + }; + // RTD sensor + channel@2 { + reg = <2>; + bipolar; + diff-channels = <3 4>; + adi,reference-select = <0>; + adi,sensor-type = "rtd"; + adi,excitation-pin-0 = <5>; + adi,excitation-pin-1 = <6>; + adi,excitation-current-0-microamp = <500>; + adi,excitation-current-1-microamp = <500>; + adi,excitation-ac; + }; + // Thermocouple sensor + channel@3 { + reg = <3>; + bipolar; + diff-channels = <7 8>; + adi,reference-select = <0>; + adi,sensor-type = "thermocouple"; + adi,excitation-pin-0 = <18>; + adi,excitation-current-0-microamp = <500>; + }; + }; + }; + - | + #include <dt-bindings/interrupt-controller/irq.h> + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad4170"; + reg = <0>; + spi-max-frequency = <20000000>; + spi-cpol; + spi-cpha; + avdd-supply = <&avdd>; + iovdd-supply = <&iovdd>; + #clock-cells = <0>; + clock-output-names = "ad4170-clk16mhz"; + interrupt-parent = <&gpio_in>; + interrupts = <0 IRQ_TYPE_EDGE_FALLING>; + #address-cells = <1>; + #size-cells = <0>; + + // Sample AIN0 with respect to AIN1 throughout AVDD/AVSS input range + // Differential bipolar. If AVSS < 0V, differential true bipolar + channel@0 { + reg = <0>; + bipolar; + diff-channels = <0 1>; + adi,reference-select = <3>; + }; + // Sample AIN2 with respect to DGND throughout AVDD/DGND input range + // Pseudo-differential unipolar + channel@1 { + reg = <1>; + single-channel = <2>; + common-mode-channel = <24>; + adi,reference-select = <3>; + }; + // Sample AIN3 with respect to 2.5V throughout AVDD/AVSS input range + // Pseudo-differential bipolar + channel@2 { + reg = <2>; + bipolar; + single-channel = <3>; + common-mode-channel = <29>; + adi,reference-select = <3>; + }; + // Sample AIN4 with respect to DGND throughout AVDD/AVSS input range + // Pseudo-differential bipolar + channel@3 { + reg = <3>; + bipolar; + single-channel = <4>; + common-mode-channel = <24>; + adi,reference-select = <3>; + }; + // Sample AIN5 with respect to 2.5V throughout AVDD/AVSS input range + // Pseudo-differential unipolar (AD4170 datasheet page 46 example) + channel@4 { + reg = <4>; + single-channel = <5>; + common-mode-channel = <29>; + adi,reference-select = <3>; + }; + // Sample AIN6 with respect to 2.5V throughout REFIN+/REFIN- input range + // Pseudo-differential bipolar + channel@5 { + reg = <5>; + bipolar; + single-channel = <6>; + common-mode-channel = <29>; + adi,reference-select = <0>; + }; + // Weigh scale sensor + channel@6 { + reg = <6>; + bipolar; + diff-channels = <7 8>; + adi,reference-select = <0>; + adi,sensor-type = "weighscale"; + adi,excitation-pin-0 = <17>; + adi,excitation-pin-1 = <18>; + adi,excitation-pin-2 = <19>; + adi,excitation-pin-3 = <20>; + adi,excitation-ac; + }; + }; + }; +... +
MAINTAINERS +7 -0
@@ -1354,6 +1354,13 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-ad4130 F: Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml F: drivers/iio/adc/ad4130.c +ANALOG DEVICES INC AD4170 DRIVER +M: Marcelo Schmitt <[email protected]> +L: [email protected] +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml + ANALOG DEVICES INC AD4695 DRIVER M: Michael Hennerich <[email protected]> M: Nuno Sá <[email protected]>
Signature · 1 line
2.47.2
Rob Herring (Arm)
maintainerRe: [PATCH v4 01/11] dt-bindings: iio: adc: Add AD4170
Quoted 21 lines · Marcelo Schmitt
Add device tree documentation for AD4170 and similar sigma-delta ADCs. The AD4170 is a 24-bit, multichannel, sigma-delta ADC.
Signed-off-by Marcelo Schmitt <[email protected]>---
Change log v3 -> v4
- Dropped sensor-node and most of defs.
- Updated external sensor props to have similar name and type of adi,ad4130 ones.
- Added constraints to properties related to external bridge sensor excitation.Some explanation about the constraints to weigh scale sensor types.
The predefined ACX1, ACX1 negated, ACX2, and ACX2 negated signals are used to AC excite external bridge circuits and are output on GPIO2, GPIO0, GPIO3, and GPIO1, respectively. If only two pins are specified for AC excitation, only ACX1 and ACX2 (GPIO2 and GPIO3) are used. Because of that, if AC excitation is specified/requested, then those specific GPIO pins must be used with the bridge. Otherwise, the bridge won't get properly excited and we also cannot guarantee to avoid short-circuit conditions since the level set to GPIOs to DC excite the bridge depends on the GPIO number. See AD4170 datasheet Figure 113 Weigh Scale (AC Excitation) for the reference circuit diagram.
Quoted 7 lines
.../bindings/iio/adc/adi,ad4170.yaml | 543 ++++++++++++++++++ MAINTAINERS | 7 + 2 files changed, 550 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml: properties:interrupt-names: 'enum' should not be valid under {'enum': ['const', 'enum', 'exclusiveMaximum', 'exclusiveMinimum', 'minimum', 'maximum', 'multipleOf', 'pattern']}
hint: Scalar and array keywords cannot be mixed
from schema $id: http://devicetree.org/meta-schemas/keywords.yaml#
doc reference errors (make refcheckdocs):
The base for the series is generally the latest rc1. A different dependency should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note that DT_SCHEMA_FILES can be set to your schema file to speed up checking your schema. However, it must be unset to test all examples with your schema.
Re: [PATCH v4 01/11] dt-bindings: iio: adc: Add AD4170
Quoted 7 lines · Rob Herring (Arm)
Quoted 4 lines · Marcelo Schmitt
Add device tree documentation for AD4170 and similar sigma-delta ADCs. The AD4170 is a 24-bit, multichannel, sigma-delta ADC.
Signed-off-by Marcelo Schmitt <[email protected]>
...
Quoted 8 lines
My bot found errors running 'make
dt_binding_check' on your patch:yamllint warnings/errors:
dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml: properties:interrupt-names: 'enum' should not be valid under {'enum': ['const', 'enum', 'exclusiveMaximum', 'exclusiveMinimum', 'minimum', 'maximum', 'multipleOf', 'pattern']}
hint: Scalar and array keywords cannot be mixed
from schema $id: http://devicetree.org/meta-schemas/keywords.yaml#
Removing maxItems from under interrupt-names seems to solve the issue.
Thanks
Jonathan Cameron
maintainerRe: [PATCH v4 01/11] dt-bindings: iio: adc: Add AD4170
Quoted 11 lines · On Mon, 2 Jun 2025 17:52:31 -0300 Marcelo Schmitt <[email protected]>…
Quoted 7 lines · Rob Herring (Arm)
Quoted 4 lines · Marcelo Schmitt
Add device tree documentation for AD4170 and similar sigma-delta ADCs. The AD4170 is a 24-bit, multichannel, sigma-delta ADC.
Signed-off-by Marcelo Schmitt <[email protected]>...
Quoted 2 lines
My bot found errors running 'make
dt_binding_check' on your patch:
Quoted 8 lines
Quoted 6 lines
yamllint warnings/errors:
dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml: properties:interrupt-names: 'enum' should not be valid under {'enum': ['const', 'enum', 'exclusiveMaximum', 'exclusiveMinimum', 'minimum', 'maximum', 'multipleOf', 'pattern']}
hint: Scalar and array keywords cannot be mixed
from schema $id: http://devicetree.org/meta-schemas/keywords.yaml#Removing maxItems from under interrupt-names seems to solve the issue.
As per feedback on v3. I don't think we should be limiting the max of interrupt-names anyway. We should allow two interrupts if they are both wired - it's a driver choice on which one to use.
Quoted 3 lines
Thanks
Jonathan Cameron
maintainerRe: [PATCH v4 01/11] dt-bindings: iio: adc: Add AD4170
In reply to Marcelo SchmittQuoted 34 lines · On Mon, 2 Jun 2025 08:36:24 -0300 Marcelo Schmitt <[email protected]>…
Add device tree documentation for AD4170 and similar sigma-delta ADCs. The AD4170 is a 24-bit, multichannel, sigma-delta ADC.
Signed-off-by Marcelo Schmitt <[email protected]>---
Change log v3 -> v4
- Dropped sensor-node and most of defs.
- Updated external sensor props to have similar name and type of adi,ad4130 ones.
- Added constraints to properties related to external bridge sensor excitation.Some explanation about the constraints to weigh scale sensor types.
The predefined ACX1, ACX1 negated, ACX2, and ACX2 negated signals are used to AC excite external bridge circuits and are output on GPIO2, GPIO0, GPIO3, and GPIO1, respectively. If only two pins are specified for AC excitation, only ACX1 and ACX2 (GPIO2 and GPIO3) are used. Because of that, if AC excitation is specified/requested, then those specific GPIO pins must be used with the bridge. Otherwise, the bridge won't get properly excited and we also cannot guarantee to avoid short-circuit conditions since the level set to GPIOs to DC excite the bridge depends on the GPIO number. See AD4170 datasheet Figure 113 Weigh Scale (AC Excitation) for the reference circuit diagram.
.../bindings/iio/adc/adi,ad4170.yaml | 543 ++++++++++++++++++ MAINTAINERS | 7 + 2 files changed, 550 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ad4170.yamlDocumentation/devicetree/bindings/iio/adc/adi,ad4170.yaml new file +0
-0
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml line 26
Quoted 8 lines
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml new file +7
-0@@ -0,0 +1,543 @@ + +$defs: + reference-buffer: + description: | + Enable precharge buffer, full buffer, or skip reference buffering of + the positive/negative voltage reference. Because the output impedance + of the source driving the voltage reference inputs may be dynamic, RC
RC?
Quoted 22 lines
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml new file +21
-0+ combinations of those inputs can cause DC gain errors if the reference + inputs go unbuffered into the ADC. Enable reference buffering if the + provided reference source has dynamic high impedance output. Note the + absolute voltage allowed on REFINn+ and REFINn- inputs is from + AVSS - 50 mV to AVDD + 50 mV when the reference buffers are disabled + but narrows to AVSS to AVDD when reference buffering is enabled or in + precharge mode. The valid options for this property are: + 0: Reference precharge buffer. + 1: Full reference buffering. + 2: Bypass reference buffers (buffering disabled). + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2] + default: 1 + + interrupts: + description: + Interrupt for signaling the completion of conversion results. The data + ready signal (RDY) used as interrupt is by default provided on the SDO + pin. Alternatively, it can be provided on the DIG_AUX1 pin in which case + the chip disables the RDY function on SDO. Thus, there can be only one + data ready interrupt enabled at a time.
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml line 107
Quoted 10 lines
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml new file +10
-0+ maxItems: 1 + + interrupt-names: + description: + Specify which pin should be configured as Data Ready interrupt. + enum: + - sdo + - dig_aux1 + maxItems: 1 + default: sdo
I think I'd prefer to see it always provided as there is no strong reason to prefer one as the default.
Quoted 8 lines
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml new file +7
-0+ adi,excitation-pin-0: + $ref: '#/$defs/excitation-pin' + + adi,excitation-pin-1: + $ref: '#/$defs/excitation-pin' + + adi,excitation-pin-2:
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml line 270
Quoted 10 lines
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml new file +10
-0+ $ref: '#/$defs/excitation-pin' + + adi,excitation-pin-3: + $ref: '#/$defs/excitation-pin' + + adi,excitation-current-0-microamp: + description: + Excitation current in microamperes to be applied to pin specified in + adi,excitation-pin-0 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500]
What motivated mix of using $ref and here where there is a lot of repetition? I don't mind which approach is used, but a mix seems the worst option.
Quoted 47 lines
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml new file +46
-0+ default: 0 + + adi,excitation-current-1-microamp: + description: + Excitation current in microamperes to be applied to pin specified in + adi,excitation-pin-1 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500] + default: 0 + + adi,excitation-current-2-microamp: + description: + Excitation current in microamperes to be applied to pin specified in + adi,excitation-pin-2 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500] + default: 0 + + adi,excitation-current-3-microamp: + description: + Excitation current in microamperes to be applied to pin specified in + adi,excitation-pin-3 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500] + default: 0 + + adi,excitation-ac: + type: boolean + description: + Whether the external sensor has to be AC or DC excited. When omitted, + it is DC excited. +required: + - compatible + - reg + - avdd-supply + - iovdd-supply + - spi-cpol + - spi-cpha + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 {
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml line 403
Quoted 10 lines
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml new file +10
-0+ compatible = "adi,ad4170"; + reg = <0>; + spi-max-frequency = <20000000>; + spi-cpol; + spi-cpha; + avdd-supply = <&avdd>; + iovdd-supply = <&iovdd>; + clocks = <&clk>; + clock-names = "xtal"; + interrupt-parent = <&gpio_in>;
In examples I don't think we tend to specify interrupt-parent. That's covered fine by the interrupt bindings.
As above, I'd prefer to always see interrupt-names as well so we have it really visible which one this is.
Quoted 97 lines
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml new file +95
-0+ interrupts = <0 IRQ_TYPE_EDGE_FALLING>; + adi,vbias-pins = <7>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + - | + #include <dt-bindings/interrupt-controller/irq.h> + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad4170"; + reg = <0>; + spi-max-frequency = <20000000>; + spi-cpol; + spi-cpha; + avdd-supply = <&avdd>; + iovdd-supply = <&iovdd>; + #clock-cells = <0>; + clock-output-names = "ad4170-clk16mhz"; + interrupt-parent = <&gpio_in>; + interrupts = <0 IRQ_TYPE_EDGE_FALLING>; + #address-cells = <1>; + #size-cells = <0>; + + // Sample AIN0 with respect to AIN1 throughout AVDD/AVSS input range + // Differential bipolar. If AVSS < 0V, differential true bipolar + channel@0 { + reg = <0>; + bipolar; + diff-channels = <0 1>; + adi,reference-select = <3>; + }; + // Sample AIN2 with respect to DGND throughout AVDD/DGND input range + // Pseudo-differential unipolar + channel@1 { + reg = <1>; + single-channel = <2>; + common-mode-channel = <24>; + adi,reference-select = <3>; + }; + // Sample AIN3 with respect to 2.5V throughout AVDD/AVSS input range + // Pseudo-differential bipolar + channel@2 { + reg = <2>; + bipolar; + single-channel = <3>; + common-mode-channel = <29>; + adi,reference-select = <3>; + }; + // Sample AIN4 with respect to DGND throughout AVDD/AVSS input range + // Pseudo-differential bipolar + channel@3 { + reg = <3>; + bipolar; + single-channel = <4>; + common-mode-channel = <24>; + adi,reference-select = <3>; + }; + // Sample AIN5 with respect to 2.5V throughout AVDD/AVSS input range + // Pseudo-differential unipolar (AD4170 datasheet page 46 example) + channel@4 { + reg = <4>; + single-channel = <5>; + common-mode-channel = <29>; + adi,reference-select = <3>; + }; + // Sample AIN6 with respect to 2.5V throughout REFIN+/REFIN- input range + // Pseudo-differential bipolar + channel@5 { + reg = <5>; + bipolar; + single-channel = <6>; + common-mode-channel = <29>; + adi,reference-select = <0>; + }; + // Weigh scale sensor + channel@6 { + reg = <6>; + bipolar; + diff-channels = <7 8>; + adi,reference-select = <0>; + adi,sensor-type = "weighscale"; + adi,excitation-pin-0 = <17>; + adi,excitation-pin-1 = <18>; + adi,excitation-pin-2 = <19>; + adi,excitation-pin-3 = <20>; + adi,excitation-ac; + }; + }; + }; +... +
Re: [PATCH v4 01/11] dt-bindings: iio: adc: Add AD4170
Quoted 6 lines · Jonathan Cameron
Quoted 3 lines · On Mon, 2 Jun 2025 08:36:24 -0300 Marcelo Schmitt <[email protected]>…
Add device tree documentation for AD4170 and similar sigma-delta ADCs. The AD4170 is a 24-bit, multichannel, sigma-delta ADC.
...
Quoted 9 lines
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml line 26
Quoted 7 lines
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml new file +7
-0+ +$defs: + reference-buffer: + description: | + Enable precharge buffer, full buffer, or skip reference buffering of + the positive/negative voltage reference. Because the output impedance + of the source driving the voltage reference inputs may be dynamic, RCRC?
Stands for the combination of resistive and capacitive elements in the path between the reference supply output and AD4170 REFINn± inputs.
Datasheet Figure 76 shows an example with only capacitive elements, but it could have resistive elements too.
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4170-4.pdf#unique_75_Connect_42_ID8013
I will rephrase to make that clearer. This is probably too long and detailed description for a dt property. I can't figure out how to put that in a more concise and meaningful way, though.
Quoted 15 lines
Quoted 14 lines
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml new file +13
-0+ combinations of those inputs can cause DC gain errors if the reference + inputs go unbuffered into the ADC. Enable reference buffering if the + provided reference source has dynamic high impedance output. Note the + absolute voltage allowed on REFINn+ and REFINn- inputs is from + AVSS - 50 mV to AVDD + 50 mV when the reference buffers are disabled + but narrows to AVSS to AVDD when reference buffering is enabled or in + precharge mode. The valid options for this property are: + 0: Reference precharge buffer. + 1: Full reference buffering. + 2: Bypass reference buffers (buffering disabled). + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2] + default: 1
...
Quoted 10 lines
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml line 270
Quoted 6 lines
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml new file +6
-0+ + adi,excitation-current-0-microamp: + description: + Excitation current in microamperes to be applied to pin specified in + adi,excitation-pin-0 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500]What motivated mix of using $ref and here where there is a lot of repetition? I don't mind which approach is used, but a mix seems the worst option.
Because
$defs:
...
adi,excitation-current-n-microamp:
description:
Excitation current in microamperes to be applied to pin specified in adi,excitation-pin-0 while this channel is active.
enum: [0, 10, 50, 100, 250, 500, 1000, 1500]
default: 0
patternProperties:
...
adi,excitation-current-0-microamp:
$ref: '#/$defs/adi,excitation-current-n-microamp'
triggers dt_binding_check warn:
patternProperties:^channel@[0-9a-f]$:properties:adi,excitation-current-0-microamp: '$ref' should not be valid under {'const': '$ref'}
hint: Standard unit suffix properties don't need a type $refThanks,
Marcelo
Re: [PATCH v4 01/11] dt-bindings: iio: adc: Add AD4170
Quoted 16 lines · On Mon, 9 Jun 2025 12:28:57 -0300 Marcelo Schmitt <[email protected]>…
Quoted 6 lines · Jonathan Cameron
Quoted 3 lines · On Mon, 2 Jun 2025 08:36:24 -0300 Marcelo Schmitt <[email protected]>…
Add device tree documentation for AD4170 and similar sigma-delta ADCs. The AD4170 is a 24-bit, multichannel, sigma-delta ADC.
...
Quoted 8 lines
Quoted 7 lines
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml new file +7
-0+ +$defs: + reference-buffer: + description: | + Enable precharge buffer, full buffer, or skip reference buffering of + the positive/negative voltage reference. Because the output impedance + of the source driving the voltage reference inputs may be dynamic, RC
Quoted 4 lines
Quoted 1 line
RC?
Stands for the combination of resistive and capacitive elements in the path between the reference supply output and AD4170 REFINn± inputs.
Ah. My head wasn't in the right space at all. RC is common enough term but I'd still spell it out here as DC is very near as is ADC and the C is different in all 3 cases :)
resistance/capacitance (RC)
or something along those lines should do the job.
Quoted 52 lines
Datasheet Figure 76 shows an example with only capacitive elements, but it could have resistive elements too.
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4170-4.pdf#unique_75_Connect_42_ID8013I will rephrase to make that clearer. This is probably too long and detailed description for a dt property. I can't figure out how to put that in a more concise and meaningful way, though.
Quoted 15 lines
Quoted 14 lines
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml new file +13
-0+ combinations of those inputs can cause DC gain errors if the reference + inputs go unbuffered into the ADC. Enable reference buffering if the + provided reference source has dynamic high impedance output. Note the + absolute voltage allowed on REFINn+ and REFINn- inputs is from + AVSS - 50 mV to AVDD + 50 mV when the reference buffers are disabled + but narrows to AVSS to AVDD when reference buffering is enabled or in + precharge mode. The valid options for this property are: + 0: Reference precharge buffer. + 1: Full reference buffering. + 2: Bypass reference buffers (buffering disabled). + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2] + default: 1...
Quoted 10 lines
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml line 270
Quoted 6 lines
Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml new file +6
-0+ + adi,excitation-current-0-microamp: + description: + Excitation current in microamperes to be applied to pin specified in + adi,excitation-pin-0 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500]What motivated mix of using $ref and here where there is a lot of repetition? I don't mind which approach is used, but a mix seems the worst option.
Because
$defs:
...
adi,excitation-current-n-microamp:
description:
Excitation current in microamperes to be applied to pin specified in adi,excitation-pin-0 while this channel is active.
enum: [0, 10, 50, 100, 250, 500, 1000, 1500]
default: 0patternProperties:
...
adi,excitation-current-0-microamp:
$ref: '#/$defs/adi,excitation-current-n-microamp'
Quoted 3 lines
triggers
dt_binding_checkwarn:patternProperties:^channel@[0-9a-f]$:properties:adi,excitation-current-0-microamp: '$ref' should not be valid under {'const': '$ref'} hint: Standard unit suffix properties don't need a type $ref
Fair enough!
J
Quoted 5 lines
Thanks,
Marcelo
PATCH v4 2/11 iio: adc: Add basic support for AD4170
In reply to Marcelo SchmittFrom: Ana-Maria Cusco <[email protected]>
The AD4170 is a multichannel, low noise, 24-bit precision sigma-delta analog to digital converter. The AD4170 design offers a flexible data acquisition solution with crosspoint multiplexed analog inputs, configurable ADC voltage reference inputs, ultra-low noise integrated PGA, digital filtering, wide range of configurable output data rates, internal oscillator and temperature sensor, four GPIOs, and integrated features for interfacing with load cell weigh scales, RTD, and thermocouple sensors.
Add basic support for the AD4170 ADC with the following features:
- Single-shot read.
- Analog front end PGA configuration.
- Differential and pseudo-differential input configuration.
---
Change log v3 -> v4
- Added previously missing #include <linux/cleanup.h>.
- Moved struct completion declaration to reduce commit diff.
MAINTAINERS | 1 + drivers/iio/adc/Kconfig | 12 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad4170.c | 1531 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 1545 insertions(+) create mode 100644 drivers/iio/adc/ad4170.c
MAINTAINERS +1 -0
@@ -1360,6 +1360,7 @@ L: [email protected] S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml +F: drivers/iio/adc/ad4170.c ANALOG DEVICES INC AD4695 DRIVER M: Michael Hennerich <[email protected]>
drivers/iio/adc/Kconfig +12 -0
@@ -84,6 +84,18 @@ config AD4130 To compile this driver as a module, choose M here: the module will be called ad4130. + +config AD4170 + tristate "Analog Device AD4170 ADC Driver" + depends on SPI + select REGMAP_SPI + help + Say yes here to build support for Analog Devices AD4170 SPI analog + to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad4170. + config AD4695 tristate "Analog Device AD4695 ADC Driver" depends on SPI
drivers/iio/adc/Makefile +1 -0
@@ -12,6 +12,7 @@ obj-$(CONFIG_AD4000) += ad4000.o obj-$(CONFIG_AD4030) += ad4030.o obj-$(CONFIG_AD4080) += ad4080.o obj-$(CONFIG_AD4130) += ad4130.o +obj-$(CONFIG_AD4170) += ad4170.o obj-$(CONFIG_AD4695) += ad4695.o obj-$(CONFIG_AD4851) += ad4851.o obj-$(CONFIG_AD7091R) += ad7091r-base.o
drivers/iio/adc/ad4170.c new file +1531 -0
1,531 lines not on this page · raw message
Signature · 1 line
2.47.2
Andy Shevchenko
reviewerRe: [PATCH v4 02/11] iio: adc: Add basic support for AD4170
Quoted 10 lines · Marcelo Schmitt
From: Ana-Maria Cusco <[email protected]>
The AD4170 is a multichannel, low noise, 24-bit precision sigma-delta analog to digital converter. The AD4170 design offers a flexible data acquisition solution with crosspoint multiplexed analog inputs, configurable ADC voltage reference inputs, ultra-low noise integrated PGA, digital filtering, wide range of configurable output data rates, internal oscillator and temperature sensor, four GPIOs, and integrated features for interfacing with load cell weigh scales, RTD, and thermocouple sensors.
Quoted 4 lines
Add basic support for the AD4170 ADC with the following features:
- Single-shot read.
- Analog front end PGA configuration.
- Differential and pseudo-differential input configuration.
...
+ array_size.h
drivers/iio/adc/ad4170.c line 11
Quoted 5 lines
drivers/iio/adc/ad4170.c new file +4
-0+#include <linux/bitfield.h> +#include <linux/bitmap.h> +#include <linux/bitops.h> +#include <linux/bits.h>
bitmap.h guarantees to include the two above, but it;s not a big deal to leave them.
drivers/iio/adc/ad4170.c line 24
Quoted 13 lines
drivers/iio/adc/ad4170.c new file +13
-0+#include <linux/cleanup.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/iio/iio.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h>
+ types.h
Quoted 3 lines
drivers/iio/adc/ad4170.c new file +3
-0+#include <linux/unaligned.h> +#include <linux/units.h> +#include <linux/util_macros.h>
...
Quoted 5 lines
drivers/iio/adc/ad4170.c new file +5
-0+static bool ad4170_setup_eq (struct ad4170_setup *a,struct ad4170_setup *b) +{ +/* +* The use of static_assert() here is to make sure that the comparison +* is adapted whenever struct ad4170_setup is changed.
drivers/iio/adc/ad4170.c line 391
Quoted 10 lines
drivers/iio/adc/ad4170.c new file +10
-0+*/ +static_assert (sizeof (*a) == +sizeof (struct { +u16 misc; +u16 afe; +u16 filter; +u16 filter_fs; +u32 offset; +u32 gain; + }));
I think it doesn't make much sense unless one uses memcpy().
Quoted 10 lines
drivers/iio/adc/ad4170.c new file +10
-0+if (a->misc != b->misc || + a->afe != b->afe || + a->filter != b->filter || + a->filter_fs != b->filter_fs || + a->offset != b->offset || + a->gain != b->gain) +return false; + +return true; +}
...
drivers/iio/adc/ad4170.c line 1120
Quoted 11 lines
drivers/iio/adc/ad4170.c new file +11
-0+static int ad4170_soft_reset (struct ad4170_state *st) +{ +int ret; + + ret =regmap_write (st->regmap,AD4170_CONFIG_A_REG , +AD4170_SW_RESET_MSK ); +if (ret) +return ret; + +/* AD4170-4 requires 1 ms between reset and any register access. */ +fsleep (MILLI );
1 * USEC_PER_MSEC
(need time.h)
...
drivers/iio/adc/ad4170.c line 1455
Quoted 5 lines
drivers/iio/adc/ad4170.c new file +5
-0+ indio_dev =devm_iio_device_alloc (&spi->dev,sizeof (*st)); +if (!indio_dev) +return -ENOMEM ; + + st =iio_priv (indio_dev);
+ blank line here.
drivers/iio/adc/ad4170.c line 1467
Quoted 12 lines
drivers/iio/adc/ad4170.c new file +10
-0+ ret =devm_mutex_init (dev, &st->lock); +if (ret) +return ret; + chip =spi_get_device_match_data (spi); +if (!chip) +return -EINVAL ; + + indio_dev->name = chip->name; + indio_dev->info = &ad4170_info; + st->spi = spi;
Can be grouped with above st = ...
Quoted 4 lines
drivers/iio/adc/ad4170.c new file +4
-0+ st->regmap =devm_regmap_init (dev,NULL , st, &ad4170_regmap_config); +if (IS_ERR (st->regmap)) +return dev_err_probe (dev,PTR_ERR (st->regmap), +"Failed to initialize regmap\n" );
Signature · 2 lines
With Best Regards, Andy Shevchenko
Re: [PATCH v4 02/11] iio: adc: Add basic support for AD4170
...
Quoted 9 lines
Quoted 9 lines
drivers/iio/adc/ad4170.c new file +9
-0+static bool ad4170_setup_eq (struct ad4170_setup *a,struct ad4170_setup *b) +{ +/* +* The use of static_assert() here is to make sure that the comparison +* is adapted whenever struct ad4170_setup is changed. +*/ + static_assert(sizeof(*a) == + sizeof(struct { + u16 misc;
Quoted 8 lines
drivers/iio/adc/ad4170.c line 391
Quoted 6 lines
drivers/iio/adc/ad4170.c new file +6
-0+u16 afe; +u16 filter; +u16 filter_fs; +u32 offset; +u32 gain; + }));I think it doesn't make much sense unless one uses
memcpy().
memcpy() is used to update the setups after reg write succeeds. Also, previously, memcmp() was used to compare setups.
Since struct ad4170_setup has only unsigned integers (no floating point fields like ad7124 had [1]), ad4170 works properly when comparing setups with memcmp(). Though, it was asked to do explicit field matching on previous reviews [2] so that's how it had been since then. Well, both ways work for ad4170. We can compare setup with memcmp(), or do the comparison field by field. I don't mind changing it again if requested. I guess we only need to reach an agreement about what to go with.
[1]: https://lore.kernel.org/all/[email protected]/
[2]: https://lore.kernel.org/linux-iio/20250504192117.5e19f44b@jic23-huawei/
Quoted 14 lines
Quoted 11 lines
drivers/iio/adc/ad4170.c new file +10
-0+if (a->misc != b->misc || + a->afe != b->afe || + a->filter != b->filter || + a->filter_fs != b->filter_fs || + a->offset != b->offset || + a->gain != b->gain) +return false; + +return true; +}...
Thanks,
Marcelo
Andy Shevchenko
reviewerRe: [PATCH v4 02/11] iio: adc: Add basic support for AD4170
On Mon, Jun 02, 2025 at 01:54:25PM -0300, Marcelo Schmitt wrote:
...
Quoted 18 lines
Quoted 9 lines
Quoted 9 lines
drivers/iio/adc/ad4170.c new file +9
-0+static bool ad4170_setup_eq (struct ad4170_setup *a,struct ad4170_setup *b) +{ +/* +* The use of static_assert() here is to make sure that the comparison +* is adapted whenever struct ad4170_setup is changed. +*/ + static_assert(sizeof(*a) == + sizeof(struct { + u16 misc;Quoted 8 lines
drivers/iio/adc/ad4170.c line 391
Quoted 6 lines
drivers/iio/adc/ad4170.c new file +6
-0+u16 afe; +u16 filter; +u16 filter_fs; +u32 offset; +u32 gain; + }));I think it doesn't make much sense unless one uses
memcpy().
Quoted 9 lines
memcpy()is used to update the setups after reg write succeeds. Also, previously,memcmp()was used to compare setups.
Since structad4170_setuphas only unsigned integers (no floating point fields like ad7124 had [1]), ad4170 works properly when comparing setups withmemcmp(). Though, it was asked to do explicit field matching on previous reviews [2] so that's how it had been since then. Well, both ways work for ad4170. We can compare setup withmemcmp(), or do the comparison field by field. I don't mind changing it again if requested. I guess we only need to reach an agreement about what to go with.
The question was "why do you need the static_assert() now?"
Quoted 13 lines
[1]: https://lore.kernel.org/all/[email protected]/
[2]: https://lore.kernel.org/linux-iio/20250504192117.5e19f44b@jic23-huawei/Quoted 10 lines
Quoted 10 lines
drivers/iio/adc/ad4170.c new file +10
-0+if (a->misc != b->misc || + a->afe != b->afe || + a->filter != b->filter || + a->filter_fs != b->filter_fs || + a->offset != b->offset || + a->gain != b->gain) +return false; + +return true; +}
Signature · 2 lines
With Best Regards, Andy Shevchenko
Re: [PATCH v4 02/11] iio: adc: Add basic support for AD4170
Quoted 10 lines · Andy Shevchenko
On Mon, Jun 02, 2025 at 01:54:25PM -0300, Marcelo Schmitt wrote:
...
Quoted 6 lines
Quoted 6 lines
drivers/iio/adc/ad4170.c line 382
Quoted 6 lines
drivers/iio/adc/ad4170.c new file +6
-0+static bool ad4170_setup_eq (struct ad4170_setup *a,struct ad4170_setup *b) +{ +/* +* The use of static_assert() here is to make sure that the comparison +* is adapted whenever struct ad4170_setup is changed. +*/
Does the reason given in the comment justify the use of static_assert?
Quoted 22 lines
Quoted 12 lines
Quoted 11 lines
drivers/iio/adc/ad4170.c line 391
Quoted 9 lines
drivers/iio/adc/ad4170.c new file +9
-0+static_assert (sizeof (*a) == +sizeof (struct { +u16 misc; +u16 afe; +u16 filter; +u16 filter_fs; +u32 offset; +u32 gain; + }));I think it doesn't make much sense unless one uses
memcpy().Quoted 9 lines
memcpy()is used to update the setups after reg write succeeds. Also, previously,memcmp()was used to compare setups.
Since structad4170_setuphas only unsigned integers (no floating point fields like ad7124 had [1]), ad4170 works properly when comparing setups withmemcmp(). Though, it was asked to do explicit field matching on previous reviews [2] so that's how it had been since then. Well, both ways work for ad4170. We can compare setup withmemcmp(), or do the comparison field by field. I don't mind changing it again if requested. I guess we only need to reach an agreement about what to go with.
Quoted 1 line
The question was "why do you need the
static_assert()now?"
To ensure that the comparison function gets updated if struct ad4170_setup is ever modified? This intends to be similar to what was implemented in ad7124 driver as the chips have similar channel configuration mechanisms. We also have ad7173 and ad4130 using static_assert for analogous purpose. There was also a comment about static_assert above.
Best regards,
Marcelo
Quoted 18 lines
Quoted 14 lines
[1]: https://lore.kernel.org/all/[email protected]/
[2]: https://lore.kernel.org/linux-iio/20250504192117.5e19f44b@jic23-huawei/Quoted 10 lines
Quoted 10 lines
drivers/iio/adc/ad4170.c new file +10
-0+if (a->misc != b->misc || + a->afe != b->afe || + a->filter != b->filter || + a->filter_fs != b->filter_fs || + a->offset != b->offset || + a->gain != b->gain) +return false; + +return true; +}Signature · 2 lines
With Best Regards, Andy Shevchenko
Andy Shevchenko
reviewerRe: [PATCH v4 02/11] iio: adc: Add basic support for AD4170
Quoted 2 lines · Marcelo Schmitt
Quoted 1 line · Andy Shevchenko
On Mon, Jun 02, 2025 at 01:54:25PM -0300, Marcelo Schmitt wrote:
...
Quoted 7 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
drivers/iio/adc/ad4170.c line 382
Quoted 6 lines
drivers/iio/adc/ad4170.c new file +6
-0+static bool ad4170_setup_eq (struct ad4170_setup *a,struct ad4170_setup *b) +{ +/* +* The use of static_assert() here is to make sure that the comparison +* is adapted whenever struct ad4170_setup is changed. +*/ Does the reason given in the comment justify the use of
static_assert?
Should I repeat myself? It makes a little sense when no memcmp() is involved.
Quoted 22 lines
Quoted 22 lines
Quoted 21 lines
Quoted 11 lines
drivers/iio/adc/ad4170.c line 391
Quoted 9 lines
drivers/iio/adc/ad4170.c new file +9
-0+static_assert (sizeof (*a) == +sizeof (struct { +u16 misc; +u16 afe; +u16 filter; +u16 filter_fs; +u32 offset; +u32 gain; + }));I think it doesn't make much sense unless one uses
memcpy().
memcpy()is used to update the setups after reg write succeeds. Also, previously,memcmp()was used to compare setups.
Since structad4170_setuphas only unsigned integers (no floating point fields like ad7124 had [1]), ad4170 works properly when comparing setups withmemcmp(). Though, it was asked to do explicit field matching on previous reviews [2] so that's how it had been since then. Well, both ways work for ad4170. We can compare setup withmemcmp(), or do the comparison field by field. I don't mind changing it again if requested. I guess we only need to reach an agreement about what to go with.
Quoted 7 lines
Quoted 1 line
The question was "why do you need the
static_assert()now?"To ensure that the comparison function gets updated if struct
ad4170_setupis ever modified? This intends to be similar to what was implemented in ad7124 driver as the chips have similar channel configuration mechanisms. We also have ad7173 and ad4130 usingstatic_assertfor analogous purpose. There was also a comment aboutstatic_assertabove.
Does this won;t work if you changes field types? (Assuming only integers to integers) I believe it doesn't affect the field-by-field comparison.
The other drivers may have different approach, have you studied them? Do they use memcmp()
Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
[1]: https://lore.kernel.org/all/[email protected]/
[2]: https://lore.kernel.org/linux-iio/20250504192117.5e19f44b@jic23-huawei/Quoted 10 lines
Quoted 10 lines
drivers/iio/adc/ad4170.c new file +10
-0+if (a->misc != b->misc || + a->afe != b->afe || + a->filter != b->filter || + a->filter_fs != b->filter_fs || + a->offset != b->offset || + a->gain != b->gain) +return false; + +return true; +}
Signature · 2 lines
With Best Regards, Andy Shevchenko
Jonathan Cameron
maintainerRe: [PATCH v4 02/11] iio: adc: Add basic support for AD4170
Quoted 14 lines · On Tue, 3 Jun 2025 16:43:42 +0300 Andy Shevchenko <[email protected]> wrote:
Quoted 2 lines · Marcelo Schmitt
Quoted 1 line · Andy Shevchenko
On Mon, Jun 02, 2025 at 01:54:25PM -0300, Marcelo Schmitt wrote:
...
Quoted 7 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
drivers/iio/adc/ad4170.c line 382
Quoted 6 lines
drivers/iio/adc/ad4170.c new file +6
-0+static bool ad4170_setup_eq (struct ad4170_setup *a,struct ad4170_setup *b) +{ +/* +* The use of static_assert() here is to make sure that the comparison +* is adapted whenever struct ad4170_setup is changed. +*/ Does the reason given in the comment justify the use of
static_assert?
Quoted 1 line
Should I repeat myself? It makes a little sense when no
memcmp()is involved.
The intent I think is to reduce the chance of a field being added without
this match function being updated. Not a very strong test but maybe
better than nothing...
Not sure how memcmp() is relevant.
Quoted 51 lines
Quoted 23 lines
Quoted 23 lines
Quoted 22 lines
Quoted 12 lines
drivers/iio/adc/ad4170.c line 391
Quoted 10 lines
drivers/iio/adc/ad4170.c new file +9
-0+static_assert (sizeof (*a) == +sizeof (struct { +u16 misc; +u16 afe; +u16 filter; +u16 filter_fs; +u32 offset; +u32 gain; + }));I think it doesn't make much sense unless one uses
memcpy().
memcpy()is used to update the setups after reg write succeeds. Also, previously,memcmp()was used to compare setups.
Since structad4170_setuphas only unsigned integers (no floating point fields like ad7124 had [1]), ad4170 works properly when comparing setups withmemcmp(). Though, it was asked to do explicit field matching on previous reviews [2] so that's how it had been since then. Well, both ways work for ad4170. We can compare setup withmemcmp(), or do the comparison field by field. I don't mind changing it again if requested. I guess we only need to reach an agreement about what to go with.Quoted 7 lines
Quoted 1 line
The question was "why do you need the
static_assert()now?"To ensure that the comparison function gets updated if struct
ad4170_setupis ever modified? This intends to be similar to what was implemented in ad7124 driver as the chips have similar channel configuration mechanisms. We also have ad7173 and ad4130 usingstatic_assertfor analogous purpose. There was also a comment aboutstatic_assertabove.Does this won;t work if you changes field types? (Assuming only integers to integers) I believe it doesn't affect the field-by-field comparison.
The other drivers may have different approach, have you studied them? Do they use
memcmp()Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
[1]: https://lore.kernel.org/all/[email protected]/
[2]: https://lore.kernel.org/linux-iio/20250504192117.5e19f44b@jic23-huawei/Quoted 10 lines
Quoted 10 lines
drivers/iio/adc/ad4170.c new file +10
-0+if (a->misc != b->misc || + a->afe != b->afe || + a->filter != b->filter || + a->filter_fs != b->filter_fs || + a->offset != b->offset || + a->gain != b->gain) +return false; + +return true; +}
kernel test robot
build botRe: [PATCH v4 02/11] iio: adc: Add basic support for AD4170
In reply to Marcelo Schmittkernel test robot noticed the following build warnings:
Hi Marcelo,
kernel test robot noticed the following build warnings:
[auto build test WARNING on c06335516e8c14f501a479a4d9de0e6c09c52ef2]
url: https://github.com/intel-lab-lkp/linux/commits/Marcelo-Schmitt/dt-bindings-iio-adc-Add-AD4170/20250603-105744 base: c06335516e8c14f501a479a4d9de0e6c09c52ef2 patch link: https://lore.kernel.org/r/e79f9a126672b33b8a7c01f650fee43a68c74029.1748829860.git.marcelo.schmitt%40analog.com patch subject: [PATCH v4 02/11] iio: adc: Add basic support for AD4170 config: m68k-allmodconfig (https://download.01.org/0day-ci/archive/20250603/[email protected]/config) compiler: m68k-linux-gcc (GCC) 15.1.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250603/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All warnings (new ones prefixed by >>):
drivers/iio/adc/ad4170.c: In function 'ad4170_parse_reference':
>> drivers/iio/adc/ad4170.c:1130:13: warning: variable 'ret' set but not used [-Wunused-but-set-variable]
1130 | int ret;
| ^~~vim +/ret +1130 drivers/iio/adc/ad4170.c
1124
1125 static int ad4170_parse_reference(struct ad4170_state *st,
1126 struct fwnode_handle *child,
1127 struct ad4170_setup *setup)
1128 {
1129 struct device *dev = &st->spi->dev;
> 1130 int ret;
1131 u32 aux;
1132
1133 /* Optional positive reference buffering */
1134 aux = AD4170_REF_BUF_FULL; /* Default to full precharge buffer enabled. */
1135 fwnode_property_read_u32(child, "adi,positive-reference-buffer", &aux);
1136 if (aux < AD4170_REF_BUF_PRE || aux > AD4170_REF_BUF_BYPASS)
1137 return dev_err_probe(dev, -EINVAL,
1138 "Invalid adi,positive-reference-buffer: %u\n",
1139 aux);
1140
1141 setup->afe |= FIELD_PREP(AD4170_AFE_REF_BUF_P_MSK, aux);
1142
1143 /* Optional negative reference buffering */
1144 aux = AD4170_REF_BUF_FULL; /* Default to full precharge buffer enabled. */
1145 fwnode_property_read_u32(child, "adi,negative-reference-buffer", &aux);
1146 if (aux < AD4170_REF_BUF_PRE || aux > AD4170_REF_BUF_BYPASS)
1147 return dev_err_probe(dev, -EINVAL,
1148 "Invalid adi,negative-reference-buffer: %u\n",
1149 aux);
1150
1151 setup->afe |= FIELD_PREP(AD4170_AFE_REF_BUF_M_MSK, aux);
1152
1153 /* Optional voltage reference selection */
1154 aux = AD4170_REF_REFOUT; /* Default reference selection. */
1155 ret = fwnode_property_read_u32(child, "adi,reference-select", &aux);
1156 if (aux > AD4170_REF_AVDD)
1157 return dev_err_probe(dev, -EINVAL,
1158 "Invalid reference selected %u\n",
1159 aux);
1160
1161 setup->afe |= FIELD_PREP(AD4170_AFE_REF_SELECT_MSK, aux);
1162
1163 return 0;
1164 }
1165
Signature · 2 lines
0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
PATCH v4 3/11 iio: adc: ad4170: Add support for calibration gain
In reply to Marcelo SchmittAdd support for ADC calibration gain configuration.
---
Change log v3 -> v4
- No longer restoring calib gain or calib offset on reg write fail.
drivers/iio/adc/ad4170.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-)
drivers/iio/adc/ad4170.c +21 -1
@@ -631,7 +631,8 @@ static const struct iio_chan_spec ad4170_channel_template = { .differential = 1, .info_mask_separate =BIT (IIO_CHAN_INFO_RAW ) |BIT (IIO_CHAN_INFO_SCALE ) | - BIT(IIO_CHAN_INFO_OFFSET), +BIT (IIO_CHAN_INFO_OFFSET ) | + BIT(IIO_CHAN_INFO_CALIBSCALE), .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), .scan_type = { .realbits = 24,
@@ -945,6 +946,9 @@ static int ad4170_read_raw(struct iio_dev *indio_dev, pga =FIELD_GET (AD4170_AFE_PGA_GAIN_MSK , setup->afe); *val = chan_info->offset_tbl[pga];return IIO_VAL_INT ; +case IIO_CHAN_INFO_CALIBSCALE : + *val = setup->gain; +return IIO_VAL_INT ;default :return -EINVAL ; }
@@ -1059,6 +1063,18 @@ static int ad4170_set_pga(struct ad4170_state *st,return ad4170_write_channel_setup (st, chan->address, false); } +static int ad4170_set_calib_gain (struct ad4170_state *st, +struct iio_chan_spec const *chan,int val) +{ +struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; +struct ad4170_setup *setup = &chan_info->setup; + +guard (mutex)(&st->lock); + setup->gain = val; + +return ad4170_write_channel_setup (st, chan->address, false); +} +static int __ad4170_write_raw (struct iio_dev *indio_dev,struct iio_chan_spec const *chan,int val,int val2,long info)
@@ -1068,6 +1084,8 @@ static int __ad4170_write_raw(struct iio_dev *indio_dev,switch (info) {case IIO_CHAN_INFO_SCALE :return ad4170_set_pga (st, chan, val, val2); +case IIO_CHAN_INFO_CALIBSCALE : +return ad4170_set_calib_gain (st, chan, val);default :return -EINVAL ; }
@@ -1094,6 +1112,8 @@ static int ad4170_write_raw_get_fmt(struct iio_dev *indio_dev,switch (info) {case IIO_CHAN_INFO_SCALE :return IIO_VAL_INT_PLUS_NANO ; +case IIO_CHAN_INFO_CALIBSCALE : +return IIO_VAL_INT ;default :return -EINVAL ; }
Signature · 1 line
2.47.2
Andy Shevchenko
reviewerRe: [PATCH v4 03/11] iio: adc: ad4170: Add support for calibration gain
Quoted 1 line · Marcelo Schmitt
Add support for ADC calibration gain configuration.
...
drivers/iio/adc/ad4170.c static const struct iio_chan_spec ad4170_channel_template = { · line 635
Quoted 5 lines
drivers/iio/adc/ad4170.c +2
-1.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |BIT (IIO_CHAN_INFO_SCALE ) | - BIT(IIO_CHAN_INFO_OFFSET), +BIT (IIO_CHAN_INFO_OFFSET ) | + BIT(IIO_CHAN_INFO_CALIBSCALE),
If you squeeze the added bit in between the upper lines, the diff will be much clearer.
Signature · 2 lines
With Best Regards, Andy Shevchenko
PATCH v4 4/11 iio: adc: ad4170: Add support for calibration bias
In reply to Marcelo SchmittAdd support for ADC calibration bias/offset configuration.
---
Change log v3 -> v4
- No longer restoring calib gain or calib offset on reg write fail.
drivers/iio/adc/ad4170.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
drivers/iio/adc/ad4170.c +19 -0
@@ -632,6 +632,7 @@ static const struct iio_chan_spec ad4170_channel_template = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_CALIBSCALE), .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), .scan_type = {
@@ -946,6 +947,9 @@ static int ad4170_read_raw(struct iio_dev *indio_dev, pga =FIELD_GET (AD4170_AFE_PGA_GAIN_MSK , setup->afe); *val = chan_info->offset_tbl[pga];return IIO_VAL_INT ; +case IIO_CHAN_INFO_CALIBBIAS : + *val = setup->offset; +return IIO_VAL_INT ;case IIO_CHAN_INFO_CALIBSCALE : *val = setup->gain;return IIO_VAL_INT ;
@@ -1063,6 +1067,18 @@ static int ad4170_set_pga(struct ad4170_state *st,return ad4170_write_channel_setup (st, chan->address, false); } +static int ad4170_set_calib_offset (struct ad4170_state *st, +struct iio_chan_spec const *chan,int val) +{ +struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; +struct ad4170_setup *setup = &chan_info->setup; + +guard (mutex)(&st->lock); + setup->offset = val; + +return ad4170_write_channel_setup (st, chan->address, false); +} +static int ad4170_set_calib_gain (struct ad4170_state *st,struct iio_chan_spec const *chan,int val) {
@@ -1084,6 +1100,8 @@ static int __ad4170_write_raw(struct iio_dev *indio_dev,switch (info) {case IIO_CHAN_INFO_SCALE :return ad4170_set_pga (st, chan, val, val2); +case IIO_CHAN_INFO_CALIBBIAS : +return ad4170_set_calib_offset (st, chan, val);case IIO_CHAN_INFO_CALIBSCALE :return ad4170_set_calib_gain (st, chan, val);default :
@@ -1112,6 +1130,7 @@ static int ad4170_write_raw_get_fmt(struct iio_dev *indio_dev,switch (info) {case IIO_CHAN_INFO_SCALE :return IIO_VAL_INT_PLUS_NANO ; +case IIO_CHAN_INFO_CALIBBIAS :case IIO_CHAN_INFO_CALIBSCALE :return IIO_VAL_INT ;default :
Signature · 1 line
2.47.2
PATCH v4 5/11 iio: adc: ad4170: Add digital filter and sample frequency config support
In reply to Marcelo SchmittAdd support for sinc3, sinc5, and averaged sinc5 digital filters along with sample frequency configuration.
---
Change log v3 -> v4
- Use scoped_guard to ensure correct lock release order in ad4170_set_filter_type().
drivers/iio/adc/ad4170.c | 262 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 259 insertions(+), 3 deletions(-)
drivers/iio/adc/ad4170.c +259 -3
@@ -17,6 +17,7 @@#include <linux/interrupt.h> #include <linux/irq.h> #include <linux/math64.h> +#include <linux/minmax.h> #include <linux/module.h> #include <linux/property.h> #include <linux/regmap.h>
@@ -85,6 +86,9 @@#define AD4170_AFE_BIPOLAR_MSK BIT(4)#define AD4170_AFE_PGA_GAIN_MSK GENMASK(3, 0) +/* AD4170_FILTER_REG */ +#define AD4170_FILTER_FILTER_TYPE_MSK GENMASK(3, 0) +/* AD4170 register constants */ /* AD4170_CHAN_MAP_REG constants */
@@ -110,6 +114,11 @@#define AD4170_ADC_CTRL_MODE_SINGLE 0x4#define AD4170_ADC_CTRL_MODE_IDLE 0x7 +/* AD4170_FILTER_REG constants */ +#define AD4170_FILTER_FILTER_TYPE_SINC5_AVG 0x0 +#define AD4170_FILTER_FILTER_TYPE_SINC5 0x4 +#define AD4170_FILTER_FILTER_TYPE_SINC3 0x6 +/* Device properties and auxiliary constants */ #define AD4170_NUM_ANALOG_PINS 9
@@ -118,6 +127,7 @@#define AD4170_MAX_SETUPS 8#define AD4170_INVALID_SETUP 9#define AD4170_SPI_MAX_XFER_LEN 6 +#define AD4170_DEFAULT_SAMP_RATE (125 * HZ_PER_KHZ)#define AD4170_INT_REF_2_5V 2500000
@@ -126,6 +136,12 @@#define AD4170_NUM_PGA_OPTIONS 10 +/* Digital filter properties */ +#define AD4170_SINC3_MIN_FS 4 +#define AD4170_SINC3_MAX_FS 65532 +#define AD4170_SINC5_MIN_FS 1 +#define AD4170_SINC5_MAX_FS 256 +#define AD4170_GAIN_REG_DEFAULT 0x555555static const unsigned int ad4170_reg_size[] = {
@@ -179,6 +195,12 @@ enum ad4170_ref_select {AD4170_REF_AVDD }; +enum ad4170_filter_type { +AD4170_SINC5_AVG , +AD4170_SINC5 , +AD4170_SINC3 , +}; +enum ad4170_regulator {AD4170_AVDD_SUP ,AD4170_AVSS_SUP ,
@@ -200,6 +222,18 @@ static const char * const ad4170_int_pin_names[] = { [AD4170_INT_PIN_DIG_AUX1] = "dig_aux1", }; +static const unsigned int ad4170_sinc3_filt_fs_tbl[] = { +4 ,8 ,12 ,16 ,20 ,40 ,48 ,80 ,/* 0 - 7 */ +100 ,256 ,500 ,1000 ,5000 ,8332 ,10000 ,25000 ,/* 8 - 15 */ +50000 ,65532 ,/* 16 - 17 */ +}; + +#define AD4170_MAX_FS_TBL_SIZE ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl) + +static const unsigned int ad4170_sinc5_filt_fs_tbl[] = { +1 ,2 ,4 ,8 ,12 ,16 ,20 ,40 ,48 ,80 ,100 ,256 , +}; +struct ad4170_chip_info {const char *name; };
@@ -257,6 +291,12 @@ struct ad4170_chan_info {bool enabled; }; +static const char *const ad4170_filt_names[] = { + [AD4170_SINC5_AVG ] ="sinc5+avg" , + [AD4170_SINC5 ] ="sinc5" , + [AD4170_SINC3 ] ="sinc3" , +}; +struct ad4170_state {struct mutex lock;/* Protect read-modify-write and multi write sequences */ int vrefs_uv[AD4170_MAX_SUP ];
@@ -266,6 +306,7 @@ struct ad4170_state {struct ad4170_chan_info chan_infos[AD4170_MAX_CHANNELS ];struct spi_device *spi;struct regmap *regmap; +int sps_tbl[ARRAY_SIZE (ad4170_filt_names)][AD4170_MAX_FS_TBL_SIZE ][2 ];struct completion completion;int pins_fn[AD4170_NUM_ANALOG_PINS ];u32 int_pin_sel;
@@ -277,6 +318,38 @@ struct ad4170_state {u8 rx_buf[4 ]; }; +static void ad4170_fill_sps_tbl (struct ad4170_state *st) +{ +unsigned int tmp0, tmp1, i; + +/* +* The ODR can be calculated the same way for sinc5+avg, sinc5, and +* sinc3 filter types with the exception that sinc5 filter has a +* narrowed range of allowed FILTER_FS values. +*/ +for (i =0 ; i <ARRAY_SIZE (ad4170_sinc3_filt_fs_tbl); i++) { + tmp0 =div_u64_rem (st->mclk_hz,32 * ad4170_sinc3_filt_fs_tbl[i], + &tmp1); + tmp1 =mult_frac (tmp1,MICRO ,32 * ad4170_sinc3_filt_fs_tbl[i]); +/* Fill sinc5+avg filter SPS table */ + st->sps_tbl[AD4170_SINC5_AVG ][i][0 ] = tmp0;/* Integer part */ + st->sps_tbl[AD4170_SINC5_AVG ][i][1 ] = tmp1;/* Fractional part */ + +/* Fill sinc3 filter SPS table */ + st->sps_tbl[AD4170_SINC3 ][i][0 ] = tmp0;/* Integer part */ + st->sps_tbl[AD4170_SINC3 ][i][1 ] = tmp1;/* Fractional part */ + } +/* Sinc5 filter ODR doesn't use all FILTER_FS bits */ +for (i =0 ; i <ARRAY_SIZE (ad4170_sinc5_filt_fs_tbl); i++) { + tmp0 =div_u64_rem (st->mclk_hz,32 * ad4170_sinc5_filt_fs_tbl[i], + &tmp1); + tmp1 =mult_frac (tmp1,MICRO ,32 * ad4170_sinc5_filt_fs_tbl[i]); +/* Fill sinc5 filter SPS table */ + st->sps_tbl[AD4170_SINC5 ][i][0 ] = tmp0;/* Integer part */ + st->sps_tbl[AD4170_SINC5 ][i][1 ] = tmp1;/* Fractional part */ + } +} +static int ad4170_debugfs_reg_access (struct iio_dev *indio_dev,unsigned int reg,unsigned int writeval,unsigned int *readval)
@@ -625,6 +698,100 @@ static int ad4170_set_channel_enable(struct ad4170_state *st,return 0 ; } +static int __ad4170_get_filter_type (unsigned int filter) +{ +u16 f_conf =FIELD_GET (AD4170_FILTER_FILTER_TYPE_MSK , filter); + +switch (f_conf) { +case AD4170_FILTER_FILTER_TYPE_SINC5_AVG : +return AD4170_SINC5_AVG ; +case AD4170_FILTER_FILTER_TYPE_SINC5 : +return AD4170_SINC5 ; +case AD4170_FILTER_FILTER_TYPE_SINC3 : +return AD4170_SINC3 ; +default : +return -EINVAL ; + } +} + +static int ad4170_set_filter_type (struct iio_dev *indio_dev, +struct iio_chan_spec const *chan, +unsigned int val) +{ +struct ad4170_state *st =iio_priv (indio_dev); +struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; +struct ad4170_setup *setup = &chan_info->setup; +unsigned int filter_type_conf; +int ret; + +switch (val) { +case AD4170_SINC5_AVG : + filter_type_conf =AD4170_FILTER_FILTER_TYPE_SINC5_AVG ; +break ; +case AD4170_SINC5 : + filter_type_conf =AD4170_FILTER_FILTER_TYPE_SINC5 ; +break ; +case AD4170_SINC3 : + filter_type_conf =AD4170_FILTER_FILTER_TYPE_SINC3 ; +break ; +default : +return -EINVAL ; + } + +/* +* The filters provide the same ODR for a given filter_fs value but +* there are different minimum and maximum filter_fs limits for each +* filter. The filter_fs value will be adjusted if the current filter_fs +* is out of the limits of the just requested filter. Since the +* filter_fs value affects the ODR (sampling_frequency), changing the +* filter may lead to a change in the sampling frequency. +*/ + scoped_guard(mutex, &st->lock) { +if (!iio_device_claim_direct (indio_dev)) +return -EBUSY ; + +if (val ==AD4170_SINC5_AVG || val ==AD4170_SINC3 ) + setup->filter_fs =clamp (val,AD4170_SINC3_MIN_FS , +AD4170_SINC3_MAX_FS ); +else + setup->filter_fs =clamp (val,AD4170_SINC5_MIN_FS , +AD4170_SINC5_MAX_FS ); + + setup->filter &= ~AD4170_FILTER_FILTER_TYPE_MSK ; + setup->filter |=FIELD_PREP (AD4170_FILTER_FILTER_TYPE_MSK , + filter_type_conf); + + ret =ad4170_write_channel_setup (st, chan->address, false); +iio_device_release_direct (indio_dev); + } + +return ret; +} + +static int ad4170_get_filter_type (struct iio_dev *indio_dev, +struct iio_chan_spec const *chan) +{ +struct ad4170_state *st =iio_priv (indio_dev); +struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; +struct ad4170_setup *setup = &chan_info->setup; + +return __ad4170_get_filter_type (setup->filter); +} + +static const struct iio_enum ad4170_filter_type_enum = { + .items = ad4170_filt_names, + .num_items =ARRAY_SIZE (ad4170_filt_names), + .get = ad4170_get_filter_type, + .set = ad4170_set_filter_type, +}; + +static const struct iio_chan_spec_ext_info ad4170_filter_type_ext_info[] = { +IIO_ENUM ("filter_type" ,IIO_SEPARATE , &ad4170_filter_type_enum), +IIO_ENUM_AVAILABLE ("filter_type" ,IIO_SHARED_BY_TYPE , + &ad4170_filter_type_enum), + { } +}; +static const struct iio_chan_spec ad4170_channel_template = { .type =IIO_VOLTAGE , .indexed =1 ,
@@ -633,8 +800,11 @@ static const struct iio_chan_spec ad4170_channel_template = {BIT (IIO_CHAN_INFO_SCALE ) |BIT (IIO_CHAN_INFO_OFFSET ) |BIT (IIO_CHAN_INFO_CALIBBIAS ) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), +BIT (IIO_CHAN_INFO_CALIBSCALE ) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available =BIT (IIO_CHAN_INFO_SCALE ) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info = ad4170_filter_type_ext_info, .scan_type = { .realbits = 24, .storagebits = 32,
@@ -926,7 +1096,8 @@ static int ad4170_read_raw(struct iio_dev *indio_dev,struct ad4170_state *st =iio_priv (indio_dev);struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address];struct ad4170_setup *setup = &chan_info->setup; -unsigned int pga; +enum ad4170_filter_type f_type; +unsigned int pga, fs_idx;int ret;guard (mutex)(&st->lock);
@@ -947,6 +1118,27 @@ static int ad4170_read_raw(struct iio_dev *indio_dev, pga =FIELD_GET (AD4170_AFE_PGA_GAIN_MSK , setup->afe); *val = chan_info->offset_tbl[pga];return IIO_VAL_INT ; +case IIO_CHAN_INFO_SAMP_FREQ : + f_type =__ad4170_get_filter_type (setup->filter); +switch (f_type) { +case AD4170_SINC5_AVG : +case AD4170_SINC3 : + fs_idx =find_closest (setup->filter_fs, + ad4170_sinc3_filt_fs_tbl, +ARRAY_SIZE (ad4170_sinc3_filt_fs_tbl)); + *val = st->sps_tbl[f_type][fs_idx][0 ]; + *val2 = st->sps_tbl[f_type][fs_idx][1 ]; +return IIO_VAL_INT_PLUS_MICRO ; +case AD4170_SINC5 : + fs_idx =find_closest (setup->filter_fs, + ad4170_sinc5_filt_fs_tbl, +ARRAY_SIZE (ad4170_sinc5_filt_fs_tbl)); + *val = st->sps_tbl[f_type][fs_idx][0 ]; + *val2 = st->sps_tbl[f_type][fs_idx][1 ]; +return IIO_VAL_INT_PLUS_MICRO ; +default : +return -EINVAL ; + }case IIO_CHAN_INFO_CALIBBIAS : *val = setup->offset;return IIO_VAL_INT ;
@@ -1032,6 +1224,7 @@ static int ad4170_read_avail(struct iio_dev *indio_dev, {struct ad4170_state *st =iio_priv (indio_dev);struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; +enum ad4170_filter_type f_type;switch (info) {case IIO_CHAN_INFO_SCALE :
@@ -1039,6 +1232,21 @@ static int ad4170_read_avail(struct iio_dev *indio_dev, *length =ARRAY_SIZE (chan_info->scale_tbl) *2 ; *type =IIO_VAL_INT_PLUS_NANO ;return IIO_AVAIL_LIST ; +case IIO_CHAN_INFO_SAMP_FREQ : + f_type =ad4170_get_filter_type (indio_dev, chan); + *vals = (int *)st->sps_tbl[f_type]; + *type =IIO_VAL_INT_PLUS_MICRO ; +switch (f_type) { +case AD4170_SINC5_AVG : +case AD4170_SINC3 : + *length =ARRAY_SIZE (ad4170_sinc3_filt_fs_tbl) *2 ; +return IIO_AVAIL_LIST ; +case AD4170_SINC5 : + *length =ARRAY_SIZE (ad4170_sinc5_filt_fs_tbl) *2 ; +return IIO_AVAIL_LIST ; +default : +return -EINVAL ; + }default :return -EINVAL ; }
@@ -1067,6 +1275,42 @@ static int ad4170_set_pga(struct ad4170_state *st,return ad4170_write_channel_setup (st, chan->address, false); } +static int ad4170_set_channel_freq (struct ad4170_state *st, +struct iio_chan_spec const *chan,int val, +int val2) +{ +struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; +struct ad4170_setup *setup = &chan_info->setup; +enum ad4170_filter_type f_type =__ad4170_get_filter_type (setup->filter); +unsigned int filt_fs_tbl_size, i; + +switch (f_type) { +case AD4170_SINC5_AVG : +case AD4170_SINC3 : + filt_fs_tbl_size =ARRAY_SIZE (ad4170_sinc3_filt_fs_tbl); +break ; +case AD4170_SINC5 : + filt_fs_tbl_size =ARRAY_SIZE (ad4170_sinc5_filt_fs_tbl); +break ; + } + +for (i =0 ; i < filt_fs_tbl_size; i++) { +if (st->sps_tbl[f_type][i][0 ] == val && + st->sps_tbl[f_type][i][1 ] == val2) +break ; + } +if (i == filt_fs_tbl_size) +return -EINVAL ; + +guard (mutex)(&st->lock); +if (f_type ==AD4170_SINC5 ) + setup->filter_fs = ad4170_sinc5_filt_fs_tbl[i]; +else + setup->filter_fs = ad4170_sinc3_filt_fs_tbl[i]; + +return ad4170_write_channel_setup (st, chan->address, false); +} +static int ad4170_set_calib_offset (struct ad4170_state *st,struct iio_chan_spec const *chan,int val) {
@@ -1100,6 +1344,8 @@ static int __ad4170_write_raw(struct iio_dev *indio_dev,switch (info) {case IIO_CHAN_INFO_SCALE :return ad4170_set_pga (st, chan, val, val2); +case IIO_CHAN_INFO_SAMP_FREQ : +return ad4170_set_channel_freq (st, chan, val, val2);case IIO_CHAN_INFO_CALIBBIAS :return ad4170_set_calib_offset (st, chan, val);case IIO_CHAN_INFO_CALIBSCALE :
@@ -1130,6 +1376,8 @@ static int ad4170_write_raw_get_fmt(struct iio_dev *indio_dev,switch (info) {case IIO_CHAN_INFO_SCALE :return IIO_VAL_INT_PLUS_NANO ; +case IIO_CHAN_INFO_SAMP_FREQ : +return IIO_VAL_INT_PLUS_MICRO ;case IIO_CHAN_INFO_CALIBBIAS :case IIO_CHAN_INFO_CALIBSCALE :return IIO_VAL_INT ;
@@ -1360,6 +1608,8 @@ static int ad4170_initial_config(struct iio_dev *indio_dev)unsigned int i;int ret; +ad4170_fill_sps_tbl (st); + ret = regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, AD4170_ADC_CTRL_MODE_MSK, FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK,
@@ -1392,6 +1642,12 @@ static int ad4170_initial_config(struct iio_dev *indio_dev)return dev_err_probe (dev, ret,"Failed to write CHAN_MAP_REG\n" ); + ret =ad4170_set_channel_freq (st, chan, +AD4170_DEFAULT_SAMP_RATE ,0 ); +if (ret) +return dev_err_probe (dev, ret, +"Failed to set channel freq\n" ); + ret =ad4170_fill_scale_tbl (indio_dev, chan);if (ret) return dev_err_probe(dev, ret,
Signature · 1 line
2.47.2
PATCH v4 6/11 iio: adc: ad4170: Add support for buffered data capture
In reply to Marcelo SchmittExtend the AD4170 driver to allow buffered data capture in continuous read mode. In continuous read mode, the chip skips the instruction phase and outputs just ADC sample data, enabling faster sample rates to be reached. The internal channel sequencer always starts sampling from channel 0 and channel 0 must be enabled if more than one channel is selected for data capture. The scan mask validation callback checks if the aforementioned condition is met.
---
Change log v3 -> v4
- Fixed a bug in the filling of the IIO device buffer.
drivers/iio/adc/Kconfig | 2 + drivers/iio/adc/ad4170.c | 215 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 216 insertions(+), 1 deletion(-)
drivers/iio/adc/Kconfig +2 -0
@@ -89,6 +89,8 @@ config AD4170 tristate "Analog Device AD4170 ADC Driver" depends on SPI select REGMAP_SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help Say yes here to build support for Analog Devices AD4170 SPI analog to digital converters (ADC).
drivers/iio/adc/ad4170.c +214 -1
307 lines not on this page · raw message
Signature · 1 line
2.47.2
Jonathan Cameron
maintainerRe: [PATCH v4 06/11] iio: adc: ad4170: Add support for buffered data capture
Quoted 9 lines · On Mon, 2 Jun 2025 08:38:46 -0300 Marcelo Schmitt <[email protected]>…
Extend the AD4170 driver to allow buffered data capture in continuous read mode. In continuous read mode, the chip skips the instruction phase and outputs just ADC sample data, enabling faster sample rates to be reached. The internal channel sequencer always starts sampling from channel 0 and channel 0 must be enabled if more than one channel is selected for data capture. The scan mask validation callback checks if the aforementioned condition is met.
Signed-off-by Marcelo Schmitt <[email protected]>
A comment inline about possibly relaxing the channel 0 constraint as far as userspace is concerned anyway. Not necessary for now though.
drivers/iio/adc/ad4170.c line 17
Quoted 10 lines
drivers/iio/adc/ad4170.c +4
-0@@ -13,7 +13,11 @@ +static bool ad4170_validate_scan_mask (struct iio_dev *indio_dev, +const unsigned long *scan_mask) +{ +unsigned int masklength =iio_get_masklength (indio_dev);
If this becomes a useability issue we could probably generative appropriate available_scan_masks entries and let the demuxer in the IIO core deal with dropping the unwanted first channel. That might be preferable to just failing if the channel isn't enabled.
As a general rule, I think we've always used validate_scan_mask to prevent too many channels being turned on, or incompatible sets of channels rather than to prevent too few being enabled.
Anyhow that would be a relaxation of constraints so can be done if it turns out to be needed later.
Quoted 13 lines
drivers/iio/adc/ad4170.c +13
-0+ +/* +* The channel sequencer cycles through the enabled channels in +* sequential order, from channel 0 to channel 15, bypassing disabled +* channels. When more than one channel is enabled, channel 0 must +* always be enabled. See datasheet channel_en register description at +* page 95. +*/ +if (bitmap_weight (scan_mask, masklength) >1 ) +return test_bit (0 , scan_mask); + +return bitmap_weight (scan_mask, masklength) ==1 ; +}
Re: [PATCH v4 06/11] iio: adc: ad4170: Add support for buffered data capture
...
Quoted 6 lines
Quoted 5 lines
drivers/iio/adc/ad4170.c +0
-0@@ -13,7 +13,11 @@
Quoted 9 lines
drivers/iio/adc/ad4170.c static int ad4170_initial_config(struct iio_dev *indio_dev) · line 1796
Quoted 4 lines
drivers/iio/adc/ad4170.c +4
-0+static bool ad4170_validate_scan_mask (struct iio_dev *indio_dev, +const unsigned long *scan_mask) +{ +unsigned int masklength =iio_get_masklength (indio_dev);If this becomes a useability issue we could probably generative appropriate
available_scan_masksentries and let the demuxer in the IIO core deal with dropping the unwanted first channel. That might be preferable to just failing if the channel isn't enabled.
Yes, we can do that. I also think that would probably be the preferred way from the user's perspective. Though, that tinkering may result in a few more tens of lines of code, which I'm reluctant to add, given the size of this set.
Quoted 8 lines
As a general rule, I think we've always used
validate_scan_maskto prevent too many channels being turned on, or incompatible sets of channels rather than to prevent too few being enabled.Anyhow that would be a relaxation of constraints so can be done if it turns out to be needed later.
Added to my list of patch ideas :)
Quoted 13 lines
Quoted 13 lines
drivers/iio/adc/ad4170.c +13
-0+ +/* +* The channel sequencer cycles through the enabled channels in +* sequential order, from channel 0 to channel 15, bypassing disabled +* channels. When more than one channel is enabled, channel 0 must +* always be enabled. See datasheet channel_en register description at +* page 95. +*/ +if (bitmap_weight (scan_mask, masklength) >1 ) +return test_bit (0 , scan_mask); + +return bitmap_weight (scan_mask, masklength) ==1 ; +}
PATCH v4 7/11 iio: adc: ad4170: Add clock provider support
In reply to Marcelo SchmittThe AD4170 chip can use an externally supplied clock at the XTAL2 pin, or an external crystal connected to the XTAL1 and XTAL2 pins. Alternatively, the AD4170 can provide its 16 MHz internal clock at the XTAL2 pin. In addition, the chip has a programmable clock divider that allows dividing the external or internal clock frequency, however, control for that is not provided in this patch. Extend the AD4170 driver so it effectively uses the provided external clock, if any, or supplies its own clock as a clock provider.
---
Change log v3 -> v4
- Explicitly stated that clock divider (CLKDIV) control support is not provided.
- Skipped clock provider register if "#clock-cells" is not present.
Suggestions/comments not followed/applied:
Quoted 2 lines
Shouldn't there be a dependency that if clocks is given, then clock-names is required.
It could be, but we have a default for clock-names so I think it's not needed.
Quoted 2 lines
Do we need to claim direct mode here to avoid poking registers during a buffered read?
In this case, I don't think so. All of clock configuration is done during probe. The IIO device will not be visible to user space at that moment. There should be no need to protect register access during probe (unless another thread tries to concurrently probe the device?).
Quoted 2 lines
Why not just do the
regmap_write()inad4170_clock_select()to keep it all together?
ad4170_clock_select() has two return paths (one for internal and one for external clock). We would have to duplicate the regmap_write() to have that inside ad4170_clock_select().
Quoted 1 line
Seems like we would want to start in the
AD4170_CLOCK_CTRL_CLOCKSEL_INTstate.
I don't think so. We'll match clock-names either with 0 or 1 and that indicates if the external clock is an input clock or a crystal. If it's ext-clk, we write 0x2 (2 + 0) to CLOCKSEL field. If it's xtal we write 0x3 (2 + 1).
drivers/iio/adc/Kconfig | 1 + drivers/iio/adc/ad4170.c | 147 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 147 insertions(+), 1 deletion(-)
drivers/iio/adc/Kconfig +1 -0
@@ -91,6 +91,7 @@ config AD4170 select REGMAP_SPI select IIO_BUFFER select IIO_TRIGGERED_BUFFER + depends on COMMON_CLK help Say yes here to build support for Analog Devices AD4170 SPI analog to digital converters (ADC).
drivers/iio/adc/ad4170.c +146 -1
207 lines not on this page · raw message
Signature · 1 line
2.47.2
PATCH v4 8/11 iio: adc: ad4170: Add GPIO controller support
In reply to Marcelo SchmittThe AD4170 has four multifunctional pins that can be used as GPIOs. The GPIO functionality can be accessed when the AD4170 chip is not busy performing continuous data capture or handling any other register read/write request. Also, the AD4170 does not provide any interrupt based on GPIO pin states so AD4170 GPIOs can't be used as interrupt sources.
Implement gpio_chip callbacks to make AD4170 GPIO pins controllable through the gpiochip interface.
---
Change log v3 -> v4
- Made AD4170 depend on GPIOLIB.
drivers/iio/adc/Kconfig | 1 + drivers/iio/adc/ad4170.c | 224 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 224 insertions(+), 1 deletion(-)
drivers/iio/adc/Kconfig +1 -0
@@ -92,6 +92,7 @@ config AD4170 select IIO_BUFFER select IIO_TRIGGERED_BUFFER depends on COMMON_CLK + depends on GPIOLIB help Say yes here to build support for Analog Devices AD4170 SPI analog to digital converters (ADC).
drivers/iio/adc/ad4170.c +223 -1
282 lines not on this page · raw message
Signature · 1 line
2.47.2
PATCH v4 9/11 iio: adc: ad4170: Add support for internal temperature sensor
In reply to Marcelo SchmittThe AD4170 has an internal temperature sensor that can be read using the ADC. Whenever possible, configure an IIO channel to provide the chip's temperature.
---
No changes since v3.
drivers/iio/adc/ad4170.c | 72 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 3 deletions(-)
drivers/iio/adc/ad4170.c +69 -3
@@ -878,6 +878,27 @@ static const struct iio_chan_spec ad4170_channel_template = { }, }; +static const struct iio_chan_spec ad4170_temp_channel_template = { + .type =IIO_TEMP , + .indexed =0 , + .channel =17 , + .channel2 =17 , + .info_mask_separate =BIT (IIO_CHAN_INFO_RAW ) | +BIT (IIO_CHAN_INFO_SCALE ) | +BIT (IIO_CHAN_INFO_OFFSET ) | +BIT (IIO_CHAN_INFO_CALIBSCALE ) | +BIT (IIO_CHAN_INFO_CALIBBIAS ) | +BIT (IIO_CHAN_INFO_SAMP_FREQ ), + .info_mask_separate_available =BIT (IIO_CHAN_INFO_SAMP_FREQ ), + .scan_type = { + .sign ='s' , + .realbits =24 , + .storagebits =32 , + .shift =8 , + .endianness =IIO_BE , + }, +}; +/* * Receives the number of a multiplexed AD4170 input (ain_n), and stores the * voltage (in µV) of the specified input into ain_voltage. If the input number
@@ -1177,9 +1198,27 @@ static int ad4170_read_raw(struct iio_dev *indio_dev,return ret;case IIO_CHAN_INFO_SCALE : pga =FIELD_GET (AD4170_AFE_PGA_GAIN_MSK , setup->afe); - *val = chan_info->scale_tbl[pga][0 ]; - *val2 = chan_info->scale_tbl[pga][1 ]; -return IIO_VAL_INT_PLUS_NANO ; +switch (chan->type) { +case IIO_VOLTAGE : + *val = chan_info->scale_tbl[pga][0 ]; + *val2 = chan_info->scale_tbl[pga][1 ]; +return IIO_VAL_INT_PLUS_NANO ; + +case IIO_TEMP : +/* +* The scale_tbl converts output codes to mV units so +* multiply by MILLI to make the factor convert to µV. +* Then, apply the temperature sensor change sensitivity +* of 477 μV/K. Finally, multiply the result by MILLI +* again to comply with milli degrees Celsius IIO ABI. +*/ + *val =0 ; + *val2 =DIV_ROUND_CLOSEST (chan_info->scale_tbl[pga][1 ] *MILLI ,477 ) * +MILLI ; +return IIO_VAL_INT_PLUS_NANO ; +default : +return -EINVAL ; + }case IIO_CHAN_INFO_OFFSET : pga =FIELD_GET (AD4170_AFE_PGA_GAIN_MSK , setup->afe); *val = chan_info->offset_tbl[pga];
@@ -1830,12 +1869,39 @@ static int ad4170_parse_channels(struct iio_dev *indio_dev)if (num_channels >AD4170_MAX_CHANNELS )return dev_err_probe (dev, -EINVAL ,"Too many channels\n" ); +/* Add one for temperature */ + num_channels =min (num_channels +1 ,AD4170_MAX_CHANNELS ); +device_for_each_child_node_scoped (dev, child) { ret =ad4170_parse_channel_node (indio_dev, child, chan_num++);if (ret)return ret; } +/* +* Add internal temperature sensor channel if the maximum number of +* channels has not been reached. +*/ +if (num_channels <AD4170_MAX_CHANNELS ) { +struct ad4170_setup *setup = &st->chan_infos[chan_num].setup; + + st->chans[chan_num] = ad4170_temp_channel_template; + st->chans[chan_num].address = chan_num; + st->chans[chan_num].scan_index = chan_num; + + st->chan_infos[chan_num].setup_num =AD4170_INVALID_SETUP ; + st->chan_infos[chan_num].initialized = true; + + setup->afe |=FIELD_PREP (AD4170_AFE_REF_SELECT_MSK , +AD4170_REF_AVDD ); + + ret =ad4170_get_input_range (st, &st->chans[chan_num], chan_num, +AD4170_REF_AVDD ); +if (ret <0 ) +return dev_err_probe (dev, ret,"Invalid input config\n" ); + + st->chan_infos[chan_num].input_range_uv = ret; + } indio_dev->num_channels = num_channels; indio_dev->channels = st->chans;
Signature · 1 line
2.47.2
PATCH v4 10/11 iio: adc: ad4170: Add support for weigh scale and RTD sensors
In reply to Marcelo SchmittThe AD4170 design has features to aid interfacing with weigh scale and RTD sensors that are expected to be setup with external circuitry for proper sensor operation. A key characteristic of those sensors is that the circuit they are in must be excited with a pair of signals. The external circuit can be excited either by voltage supply or by AD4170 excitation signals. The sensor can then be read through a different pair of lines that are connected to AD4170 ADC.
Configure AD4170 to handle external circuit sensors.
---
Change log v3 -> v4
- Update to string adi,sensor-type dt property.
- Adapted external sensor dt prop parsing to work with the updated version of those props.
drivers/iio/adc/ad4170.c | 500 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 494 insertions(+), 6 deletions(-)
drivers/iio/adc/ad4170.c +494 -6
@@ -64,6 +64,8 @@#define AD4170_FILTER_FS_REG (x) (0xC7 + 14 * (x))#define AD4170_OFFSET_REG (x) (0xCA + 14 * (x))#define AD4170_GAIN_REG (x) (0xCD + 14 * (x)) +#define AD4170_V_BIAS_REG 0x135 +#define AD4170_CURRENT_SRC_REG (x) (0x139 + 2 * (x))#define AD4170_GPIO_MODE_REG 0x191#define AD4170_GPIO_OUTPUT_REG 0x193#define AD4170_GPIO_INPUT_REG 0x195
@@ -95,6 +97,10 @@#define AD4170_CHAN_MAP_AINP_MSK GENMASK(12, 8)#define AD4170_CHAN_MAP_AINM_MSK GENMASK(4, 0) +/* AD4170_MISC_REG */ +#define AD4170_MISC_CHOP_IEXC_MSK GENMASK(15, 14) +#define AD4170_MISC_CHOP_ADC_MSK GENMASK(9, 8) +/* AD4170_AFE_REG */ #define AD4170_AFE_REF_BUF_M_MSK GENMASK(11, 10)#define AD4170_AFE_REF_BUF_P_MSK GENMASK(9, 8)
@@ -105,12 +111,19 @@/* AD4170_FILTER_REG */ #define AD4170_FILTER_FILTER_TYPE_MSK GENMASK(3, 0) +/* AD4170_CURRENT_SRC_REG */ +#define AD4170_CURRENT_SRC_I_OUT_PIN_MSK GENMASK(12, 8) +#define AD4170_CURRENT_SRC_I_OUT_VAL_MSK GENMASK(2, 0) +/* AD4170_GPIO_MODE_REG */ #define AD4170_GPIO_MODE_GPIO0_MSK GENMASK(1, 0)#define AD4170_GPIO_MODE_GPIO1_MSK GENMASK(3, 2)#define AD4170_GPIO_MODE_GPIO2_MSK GENMASK(5, 4)#define AD4170_GPIO_MODE_GPIO3_MSK GENMASK(7, 6) +/* AD4170_GPIO_OUTPUT_REG */ +#define AD4170_GPIO_OUTPUT_GPIO_MSK (x) BIT(x) +/* AD4170 register constants */ /* AD4170_CLOCK_CTRL_REG constants */
@@ -134,6 +147,11 @@#define AD4170_CHAN_MAP_REFIN2_N 28#define AD4170_CHAN_MAP_REFOUT 29 +/* AD4170_MISC_REG constants */ +#define AD4170_MISC_CHOP_IEXC_PAIR1 0x1 +#define AD4170_MISC_CHOP_IEXC_PAIR2 0x2 +#define AD4170_MISC_CHOP_IEXC_BOTH 0x3 +/* AD4170_PIN_MUXING_REG constants */ #define AD4170_PIN_MUXING_DIG_AUX1_DISABLED 0x0#define AD4170_PIN_MUXING_DIG_AUX1_RDY 0x1
@@ -151,6 +169,10 @@#define AD4170_FILTER_FILTER_TYPE_SINC5 0x4#define AD4170_FILTER_FILTER_TYPE_SINC3 0x6 +/* AD4170_CURRENT_SRC_REG constants */ +#define AD4170_CURRENT_SRC_I_OUT_PIN_AIN (x) (x) +#define AD4170_CURRENT_SRC_I_OUT_PIN_GPIO (x) ((x) + 17) +/* AD4170_GPIO_MODE_REG constants */ #define AD4170_GPIO_MODE_GPIO_INPUT 1#define AD4170_GPIO_MODE_GPIO_OUTPUT 2
@@ -164,6 +186,7 @@#define AD4170_MAX_SETUPS 8#define AD4170_INVALID_SETUP 9#define AD4170_SPI_MAX_XFER_LEN 6 +#define AD4170_NUM_CURRENT_SRC 4#define AD4170_DEFAULT_SAMP_RATE (125 * HZ_PER_KHZ)#define AD4170_INT_REF_2_5V 2500000
@@ -185,8 +208,19 @@#define AD4170_ADC_CTRL_CONT_READ_EXIT 0xA5 +/* Analog pin functions */ +#define AD4170_PIN_UNASSIGNED 0x00 +#define AD4170_PIN_ANALOG_IN 0x01 +#define AD4170_PIN_CURRENT_OUT 0x02 +#define AD4170_PIN_VBIAS 0x04 +/* GPIO pin functions */ #define AD4170_GPIO_UNASSIGNED 0x00 +#define AD4170_GPIO_AC_EXCITATION 0x02 +#define AD4170_GPIO_OUTPUT 0x04 + +/* Current source */ +#define AD4170_CURRENT_SRC_DISABLED 0xFFstatic const unsigned int ad4170_reg_size[] = { [AD4170_CONFIG_A_REG ] =1 ,
@@ -225,6 +259,8 @@ static const unsigned int ad4170_reg_size[] = { [AD4170_OFFSET_REG(5) ... AD4170_GAIN_REG(5)] = 3, [AD4170_OFFSET_REG(6) ... AD4170_GAIN_REG(6)] = 3, [AD4170_OFFSET_REG(7) ... AD4170_GAIN_REG(7)] = 3, + [AD4170_V_BIAS_REG ] =2 , + [AD4170_CURRENT_SRC_REG(0) ... AD4170_CURRENT_SRC_REG(3)] = 2, [AD4170_GPIO_MODE_REG] = 2, [AD4170_GPIO_OUTPUT_REG] = 2, [AD4170_GPIO_INPUT_REG] = 2,
@@ -287,6 +323,41 @@ static const unsigned int ad4170_sinc5_filt_fs_tbl[] = { 1, 2, 4, 8, 12, 16, 20, 40, 48, 80, 100, 256, }; +static const unsigned int ad4170_iout_pin_tbl[] = { +AD4170_CURRENT_SRC_I_OUT_PIN_AIN (0 ), +AD4170_CURRENT_SRC_I_OUT_PIN_AIN (1 ), +AD4170_CURRENT_SRC_I_OUT_PIN_AIN (2 ), +AD4170_CURRENT_SRC_I_OUT_PIN_AIN (3 ), +AD4170_CURRENT_SRC_I_OUT_PIN_AIN (4 ), +AD4170_CURRENT_SRC_I_OUT_PIN_AIN (5 ), +AD4170_CURRENT_SRC_I_OUT_PIN_AIN (6 ), +AD4170_CURRENT_SRC_I_OUT_PIN_AIN (7 ), +AD4170_CURRENT_SRC_I_OUT_PIN_AIN (8 ), +AD4170_CURRENT_SRC_I_OUT_PIN_GPIO (0 ), +AD4170_CURRENT_SRC_I_OUT_PIN_GPIO (1 ), +AD4170_CURRENT_SRC_I_OUT_PIN_GPIO (2 ), +AD4170_CURRENT_SRC_I_OUT_PIN_GPIO (3 ), +}; + +static const unsigned int ad4170_iout_current_ua_tbl[] = { +0 ,10 ,50 ,100 ,250 ,500 ,1000 ,1500 , +}; + +enum ad4170_sensor_enum { +AD4170_ADC_SENSOR =0 , +AD4170_WEIGH_SCALE_SENSOR =1 , +AD4170_RTD_SENSOR =2 , +AD4170_THERMOCOUPLE_SENSOR =3 , +}; + +/* maps adi,sensor-type property value to enum */ +static const char *const ad4170_sensor_type[] = { + [AD4170_ADC_SENSOR ] ="adc" , + [AD4170_WEIGH_SCALE_SENSOR ] ="weighscale" , + [AD4170_RTD_SENSOR ] ="rtd" , + [AD4170_THERMOCOUPLE_SENSOR ] ="thermocouple" , +}; +struct ad4170_chip_info {const char *name; };
@@ -371,6 +442,7 @@ struct ad4170_state {struct gpio_chip gpiochip;unsigned int clock_ctrl;int gpio_fn[AD4170_NUM_GPIO_PINS ]; +unsigned int cur_src_pins[AD4170_NUM_CURRENT_SRC ];/* * DMA (thus cache coherency maintenance) requires the transfer buffers * to live in their own cache lines.
@@ -913,6 +985,19 @@ static int ad4170_get_ain_voltage_uv(struct ad4170_state *st, int ain_n,int v_diff; *ain_voltage =0 ; +/* +* The voltage bias (vbias) sets the common-mode voltage of the channel +* to (AVDD + AVSS)/2. If provided, AVSS supply provides the magnitude +* (absolute value) of the negative voltage supplied to the AVSS pin. +* So, we do AVDD - AVSS to compute the DC voltage generated by the bias +* voltage generator. +*/ +if (st->pins_fn[ain_n] &AD4170_PIN_VBIAS ) { +int v_diff = st->vrefs_uv[AD4170_AVDD_SUP ] - st->vrefs_uv[AD4170_AVSS_SUP ]; + *ain_voltage = v_diff /2 ; +return 0 ; + } +if (ain_n <=AD4170_CHAN_MAP_TEMP_SENSOR )return 0 ;
@@ -967,6 +1052,19 @@ static int ad4170_get_ain_voltage_uv(struct ad4170_state *st, int ain_n, } } +static int ad4170_validate_analog_input (struct ad4170_state *st,int pin) +{ +if (pin <=AD4170_MAX_ANALOG_PINS ) { +if (st->pins_fn[pin] &AD4170_PIN_CURRENT_OUT ) +return dev_err_probe (&st->spi->dev, -EINVAL , +"Pin %d already used with fn %u.\n" , + pin, st->pins_fn[pin]); + + st->pins_fn[pin] |=AD4170_PIN_ANALOG_IN ; + } +return 0 ; +} +static int ad4170_validate_channel_input (struct ad4170_state *st,int pin,bool com) {/* Check common-mode input pin is mapped to a special input. */
@@ -981,7 +1079,7 @@ static int ad4170_validate_channel_input(struct ad4170_state *st, int pin, bool"Invalid analog input pin number. %d\n" , pin); -return 0 ; +return ad4170_validate_analog_input (st, pin); }/*
@@ -1718,6 +1816,373 @@ static int ad4170_gpio_init(struct iio_dev *indio_dev)return devm_gpiochip_add_data (&st->spi->dev, &st->gpiochip, indio_dev); } +static int ad4170_validate_excitation_pin (struct ad4170_state *st,u32 pin) +{ +struct device *dev = &st->spi->dev; +unsigned int i; + +/* Check the pin number is valid */ +for (i =0 ; i <ARRAY_SIZE (ad4170_iout_pin_tbl); i++) +if (ad4170_iout_pin_tbl[i] == pin) +break ; + +if (i ==ARRAY_SIZE (ad4170_iout_pin_tbl)) +return dev_err_probe (dev, -EINVAL , +"Invalid excitation pin: %u\n" , + pin); + +/* Check the pin is available */ +if (pin <=AD4170_MAX_ANALOG_PINS ) { +if (st->pins_fn[pin] !=AD4170_PIN_UNASSIGNED ) +return dev_err_probe (dev, -EINVAL , +"Pin %u already used with fn %u\n" , + pin, st->pins_fn[pin]); + + st->pins_fn[pin] |=AD4170_PIN_CURRENT_OUT ; + }else { +unsigned int gpio = pin -AD4170_CURRENT_SRC_I_OUT_PIN_GPIO (0 ); + +if (st->gpio_fn[gpio] !=AD4170_GPIO_UNASSIGNED ) +return dev_err_probe (dev, -EINVAL , +"GPIO %u already used with fn %u\n" , + gpio, st->gpio_fn[gpio]); + + st->gpio_fn[gpio] |=AD4170_GPIO_AC_EXCITATION ; + } + +return 0 ; +} + +static int ad4170_validate_excitation_pins (struct ad4170_state *st, +u32 *exc_pins,int num_exc_pins) +{ +unsigned int i; +int ret; + +for (i =0 ; i < num_exc_pins; i++) { +unsigned int pin = exc_pins[i]; + + ret =ad4170_validate_excitation_pin (st, pin); +if (ret) +return ret; + } +return 0 ; +} + +static const char *const ad4170_i_out_pin_dt_props[] = { +"adi,excitation-pin-0" , +"adi,excitation-pin-1" , +"adi,excitation-pin-2" , +"adi,excitation-pin-3" , +}; + +static const char *const ad4170_i_out_val_dt_props[] = { +"adi,excitation-current-0-microamp" , +"adi,excitation-current-1-microamp" , +"adi,excitation-current-2-microamp" , +"adi,excitation-current-3-microamp" , +}; + +/* +* Parses firmware data describing output current source setup. There are 4 +* excitation currents (IOUT0 to IOUT3) that can be configured independently. +* Excitation currents are added if they are output on the same pin. +*/ +static int ad4170_parse_exc_current (struct ad4170_state *st, +struct fwnode_handle *child, +unsigned int *exc_pins, +unsigned int *exc_curs, +unsigned int *num_exc_pins) +{ +struct device *dev = &st->spi->dev; +unsigned int num_pins, i, j; +u32 pin, val; +int ret; + + num_pins =0 ; +for (i =0 ; i <AD4170_NUM_CURRENT_SRC ; i++) { +/* Parse excitation current output pin properties. */ + pin =AD4170_CURRENT_SRC_I_OUT_PIN_AIN (0 ); + ret =fwnode_property_read_u32 (child, ad4170_i_out_pin_dt_props[i], + &pin); +if (ret) +continue ; + + exc_pins[num_pins] = pin; + +/* Parse excitation current value properties. */ + val = ad4170_iout_current_ua_tbl[0 ]; +fwnode_property_read_u32 (child, + ad4170_i_out_val_dt_props[i], &val); + +for (j =0 ; j <ARRAY_SIZE (ad4170_iout_current_ua_tbl); j++) +if (ad4170_iout_current_ua_tbl[j] == val) +break ; + +if (j ==ARRAY_SIZE (ad4170_iout_current_ua_tbl)) +return dev_err_probe (dev, -EINVAL ,"Invalid %s: %uuA\n" , + ad4170_i_out_val_dt_props[i], val); + + exc_curs[num_pins] = j; + num_pins++; + } + *num_exc_pins = num_pins; + +return 0 ; +} + +static int ad4170_setup_current_src (struct ad4170_state *st, +struct fwnode_handle *child, +struct ad4170_setup *setup,u32 *exc_pins, +unsigned int *exc_curs,int num_exc_pins, +bool ac_excited) +{ +unsigned int exc_cur_pair, i, j; +int ret; + +for (i =0 ; i < num_exc_pins; i++) { +unsigned int exc_cur = exc_curs[i]; +unsigned int pin = exc_pins[i]; +unsigned int current_src =0 ; + +for (j =0 ; j <AD4170_NUM_CURRENT_SRC ; j++) +if (st->cur_src_pins[j] ==AD4170_CURRENT_SRC_DISABLED ) +break ; + +if (j ==AD4170_NUM_CURRENT_SRC ) +return dev_err_probe (&st->spi->dev, -EINVAL , +"Too many excitation current sources\n" ); + + current_src |=FIELD_PREP (AD4170_CURRENT_SRC_I_OUT_PIN_MSK , pin); + current_src |=FIELD_PREP (AD4170_CURRENT_SRC_I_OUT_VAL_MSK , exc_cur); + st->cur_src_pins[j] = pin; + ret =regmap_write (st->regmap,AD4170_CURRENT_SRC_REG (j), + current_src); +if (ret) +return ret; + } + +if (!ac_excited) +return 0 ; + +if (num_exc_pins <2 ) +return dev_err_probe (&st->spi->dev, -EINVAL , +"Current chopping requested but only one pin provided: %u\n" , + exc_pins[0 ]); + +/* +* Two use cases to handle here: +* - 2 pairs of excitation currents; +* - 1 pair of excitation currents. +*/ +if (num_exc_pins ==4 ) { +for (i =0 ; i <AD4170_NUM_CURRENT_SRC ; i++) +if (st->cur_src_pins[i] != exc_pins[i]) +return dev_err_probe (&st->spi->dev, -EINVAL , +"Unable to use 4 exc pins\n" ); + }else { +/* +* Excitation current chopping is configured in pairs. Current +* sources IOUT0 and IOUT1 form pair 1, IOUT2 and IOUT3 make up +* pair 2. So, if current chopping was requested, check if the +* first end of the first pair of excitation currents is +* available. Try the next pair if IOUT0 has already been +* configured for another channel. +*/ + i = st->cur_src_pins[0 ] == exc_pins[0 ] ?0 :2 ; + +if (st->cur_src_pins[i] != exc_pins[0 ] || + st->cur_src_pins[i +1 ] != exc_pins[1 ]) +return dev_err_probe (&st->spi->dev, -EINVAL , +"Failed to setup current chopping\n" ); + + st->cur_src_pins[i] = exc_pins[0 ]; + st->cur_src_pins[i +1 ] = exc_pins[1 ]; + +if (i ==0 ) + exc_cur_pair =AD4170_MISC_CHOP_IEXC_PAIR1 ; +else + exc_cur_pair =AD4170_MISC_CHOP_IEXC_PAIR2 ; + } + +/* +* Configure excitation current chopping. +* Chop both pairs if using four excitation pins. +*/ + setup->misc |=FIELD_PREP (AD4170_MISC_CHOP_IEXC_MSK , + num_exc_pins ==2 ? + exc_cur_pair : +AD4170_MISC_CHOP_IEXC_BOTH ); + +return 0 ; +} + +static int ad4170_setup_bridge (struct ad4170_state *st, +struct fwnode_handle *child, +struct ad4170_setup *setup,u32 *exc_pins, +unsigned int *exc_curs,int num_exc_pins, +bool ac_excited) +{ +unsigned long gpio_mask; +unsigned int i; +int ret; + +/* +* If a specific current is provided through +* adi,excitation-current-n-microamp, set excitation pins provided +* through adi,excitation-pin-n to excite the bridge circuit. +*/ +for (i =0 ; i < num_exc_pins; i++) +if (exc_curs[i] >0 ) +return ad4170_setup_current_src (st, child, setup, exc_pins, + exc_curs, num_exc_pins, + ac_excited); + +/* +* Else, use predefined ACX1, ACX1 negated, ACX2, ACX2 negated signals +* to AC excite the bridge. Those signals are output on GPIO2, GPIO0, +* GPIO3, and GPIO1, respectively. If only two pins are specified for AC +* excitation, use ACX1 and ACX2 (GPIO2 and GPIO3). +* +* Also, to avoid any short-circuit condition when more than one channel +* is enabled, set GPIO2 and GPIO0 high, and set GPIO1 and GPIO3 low to +* DC excite the bridge whenever a channel without AC excitation is +* selected. That is needed because GPIO pins are controlled by the next +* highest priority GPIO function when a channel doesn't enable AC +* excitation. See datasheet Figure 113 Weigh Scale (AC Excitation) for +* the reference circuit diagram. +*/ +if (num_exc_pins ==2 ) { + setup->misc |=FIELD_PREP (AD4170_MISC_CHOP_ADC_MSK ,0x3 ); + + gpio_mask =AD4170_GPIO_MODE_GPIO3_MSK |AD4170_GPIO_MODE_GPIO2_MSK ; + ret =regmap_update_bits (st->regmap,AD4170_GPIO_MODE_REG , gpio_mask, +FIELD_PREP (AD4170_GPIO_MODE_GPIO3_MSK , +AD4170_GPIO_MODE_GPIO_OUTPUT ) | +FIELD_PREP (AD4170_GPIO_MODE_GPIO2_MSK , +AD4170_GPIO_MODE_GPIO_OUTPUT )); +if (ret) +return ret; + +/* +* Set GPIO2 high and GPIO3 low to DC excite the bridge when +* a different channel is selected. +*/ + gpio_mask =AD4170_GPIO_OUTPUT_GPIO_MSK (3 ) | +AD4170_GPIO_OUTPUT_GPIO_MSK (2 ); + ret =regmap_update_bits (st->regmap,AD4170_GPIO_OUTPUT_REG , gpio_mask, +FIELD_PREP (AD4170_GPIO_OUTPUT_GPIO_MSK (3 ),0 ) | +FIELD_PREP (AD4170_GPIO_OUTPUT_GPIO_MSK (2 ),1 )); +if (ret) +return ret; + + st->gpio_fn[3 ] |=AD4170_GPIO_OUTPUT ; + st->gpio_fn[2 ] |=AD4170_GPIO_OUTPUT ; + }else { + setup->misc |=FIELD_PREP (AD4170_MISC_CHOP_ADC_MSK ,0x2 ); + + gpio_mask =AD4170_GPIO_MODE_GPIO3_MSK |AD4170_GPIO_MODE_GPIO2_MSK | +AD4170_GPIO_MODE_GPIO1_MSK |AD4170_GPIO_MODE_GPIO0_MSK ; + ret =regmap_update_bits (st->regmap,AD4170_GPIO_MODE_REG , gpio_mask, +FIELD_PREP (AD4170_GPIO_MODE_GPIO3_MSK , +AD4170_GPIO_MODE_GPIO_OUTPUT ) | +FIELD_PREP (AD4170_GPIO_MODE_GPIO2_MSK , +AD4170_GPIO_MODE_GPIO_OUTPUT ) | +FIELD_PREP (AD4170_GPIO_MODE_GPIO1_MSK , +AD4170_GPIO_MODE_GPIO_OUTPUT ) | +FIELD_PREP (AD4170_GPIO_MODE_GPIO0_MSK , +AD4170_GPIO_MODE_GPIO_OUTPUT )); +if (ret) +return ret; + +/* +* Set GPIO2 and GPIO0 high, and set GPIO1 and GPIO3 low to DC +* excite the bridge when a different channel is selected. +*/ + gpio_mask =AD4170_GPIO_OUTPUT_GPIO_MSK (3 ) | +AD4170_GPIO_OUTPUT_GPIO_MSK (2 ) | +AD4170_GPIO_OUTPUT_GPIO_MSK (1 ) | +AD4170_GPIO_OUTPUT_GPIO_MSK (0 ); + ret =regmap_update_bits (st->regmap,AD4170_GPIO_OUTPUT_REG , gpio_mask, +FIELD_PREP (AD4170_GPIO_OUTPUT_GPIO_MSK (3 ),0 ) | +FIELD_PREP (AD4170_GPIO_OUTPUT_GPIO_MSK (2 ),1 ) | +FIELD_PREP (AD4170_GPIO_OUTPUT_GPIO_MSK (1 ),0 ) | +FIELD_PREP (AD4170_GPIO_OUTPUT_GPIO_MSK (0 ),1 )); +if (ret) +return ret; + + st->gpio_fn[3 ] |=AD4170_GPIO_OUTPUT ; + st->gpio_fn[2 ] |=AD4170_GPIO_OUTPUT ; + st->gpio_fn[1 ] |=AD4170_GPIO_OUTPUT ; + st->gpio_fn[0 ] |=AD4170_GPIO_OUTPUT ; + } + +return 0 ; +} + +static int ad4170_setup_rtd (struct ad4170_state *st, +struct fwnode_handle *child, +struct ad4170_setup *setup,u32 *exc_pins, +unsigned int *exc_curs,int num_exc_pins,bool ac_excited) +{ +return ad4170_setup_current_src (st, child, setup, exc_pins, + exc_curs, num_exc_pins, ac_excited); +} + +static int ad4170_parse_external_sensor (struct ad4170_state *st, +struct fwnode_handle *child, +struct ad4170_setup *setup, +struct iio_chan_spec *chan,u8 s_type) +{ +unsigned int num_exc_pins, reg_val; +struct device *dev = &st->spi->dev; +u32 pins[2 ], exc_pins[4 ], exc_curs[4 ]; +bool ac_excited, vbias; +int ret; + + ret =fwnode_property_read_u32_array (child,"diff-channels" , pins, +ARRAY_SIZE (pins)); +if (ret) +return dev_err_probe (dev, ret, +"Failed to read sensor diff-channels\n" ); + + chan->differential = true; + chan->channel = pins[0 ]; + chan->channel2 = pins[1 ]; + + ret =ad4170_parse_exc_current (st, child, exc_pins, exc_curs, &num_exc_pins); +if (ret) +return ret; + +/* The external sensor may not need excitation from the ADC chip. */ +if (num_exc_pins ==0 ) +return 0 ; + + ret =ad4170_validate_excitation_pins (st, exc_pins, num_exc_pins); +if (ret) +return ret; + + ac_excited =fwnode_property_read_bool (child,"adi,excitation-ac" ); + +if (s_type ==AD4170_THERMOCOUPLE_SENSOR ) { + vbias =fwnode_property_read_bool (child,"adi,vbias" ); +if (vbias) { + st->pins_fn[chan->channel2] |=AD4170_PIN_VBIAS ; + reg_val =BIT (chan->channel2); +return regmap_write (st->regmap,AD4170_V_BIAS_REG , + reg_val); + } + } +if (s_type ==AD4170_WEIGH_SCALE_SENSOR ) + ret =ad4170_setup_bridge (st, child, setup, exc_pins, exc_curs, + num_exc_pins, ac_excited); +else + ret =ad4170_setup_rtd (st, child, setup, exc_pins, exc_curs, + num_exc_pins, ac_excited); + +return ret; +} +static int ad4170_parse_reference (struct ad4170_state *st,struct fwnode_handle *child,struct ad4170_setup *setup)
@@ -1803,8 +2268,9 @@ static int ad4170_parse_channel_node(struct iio_dev *indio_dev,struct ad4170_chan_info *chan_info;struct ad4170_setup *setup;struct iio_chan_spec *chan; +unsigned int ref_select; +unsigned int s_type;unsigned int ch_reg; -u8 ref_select;bool bipolar;int ret;
@@ -1832,10 +2298,28 @@ static int ad4170_parse_channel_node(struct iio_dev *indio_dev,if (ret)return ret; - ret =ad4170_parse_adc_channel_type (dev, child, chan); -if (ret <0 ) -return ret; + ret =fwnode_property_match_property_string (child,"adi,sensor-type" , + ad4170_sensor_type, +ARRAY_SIZE (ad4170_sensor_type)); +/* Default to conventional ADC channel if sensor type not present */ + s_type = ret <0 ?AD4170_ADC_SENSOR : ret; +switch (s_type) { +case AD4170_ADC_SENSOR : + ret =ad4170_parse_adc_channel_type (dev, child, chan); +if (ret) +return ret; + +break ; +case AD4170_WEIGH_SCALE_SENSOR : +case AD4170_THERMOCOUPLE_SENSOR : +case AD4170_RTD_SENSOR : + ret =ad4170_parse_external_sensor (st, child, setup, chan, s_type); +if (ret) +return ret; + +break ; + } bipolar =fwnode_property_read_bool (child,"bipolar" ); setup->afe |=FIELD_PREP (AD4170_AFE_BIPOLAR_MSK , bipolar);if (bipolar)
@@ -1843,11 +2327,11 @@ static int ad4170_parse_channel_node(struct iio_dev *indio_dev,else chan->scan_type.sign ='u' ; - ref_select =FIELD_GET (AD4170_AFE_REF_SELECT_MSK , setup->afe); ret =ad4170_validate_channel (st, chan);if (ret <0 )return ret; + ref_select =FIELD_GET (AD4170_AFE_REF_SELECT_MSK , setup->afe); ret =ad4170_get_input_range (st, chan, ch_reg, ref_select);if (ret <0 )return dev_err_probe (dev, ret,"Invalid input config\n" );
@@ -2031,6 +2515,7 @@ static int ad4170_parse_firmware(struct iio_dev *indio_dev)struct ad4170_state *st =iio_priv (indio_dev);struct device *dev = &st->spi->dev;int reg_data, ret; +unsigned int i; ret =ad4170_clock_select (indio_dev);if (ret)
@@ -2040,6 +2525,9 @@ static int ad4170_parse_firmware(struct iio_dev *indio_dev)if (ret)return ret; +for (i =0 ; i <AD4170_NUM_CURRENT_SRC ; i++) + st->cur_src_pins[i] =AD4170_CURRENT_SRC_DISABLED ; +/* On power on, device defaults to using SDO pin for data ready signal */ st->int_pin_sel =AD4170_INT_PIN_SDO ; ret = device_property_match_property_string(dev, "interrupt-names",
Signature · 1 line
2.47.2
Jonathan Cameron
maintainerRe: [PATCH v4 10/11] iio: adc: ad4170: Add support for weigh scale and RTD sensors
Quoted 11 lines · On Mon, 2 Jun 2025 08:40:01 -0300 Marcelo Schmitt <[email protected]>…
The AD4170 design has features to aid interfacing with weigh scale and RTD sensors that are expected to be setup with external circuitry for proper sensor operation. A key characteristic of those sensors is that the circuit they are in must be excited with a pair of signals. The external circuit can be excited either by voltage supply or by AD4170 excitation signals. The sensor can then be read through a different pair of lines that are connected to AD4170 ADC.
Configure AD4170 to handle external circuit sensors.
Signed-off-by Marcelo Schmitt <[email protected]>
Trivial comments inline.
drivers/iio/adc/ad4170.c static int ad4170_gpio_init(struct iio_dev *indio_dev) · line 1863
Quoted 8 lines
drivers/iio/adc/ad4170.c +8
-0+static int ad4170_validate_excitation_pins (struct ad4170_state *st, +u32 *exc_pins,int num_exc_pins) +{ +unsigned int i; +int ret; + +for (i =0 ; i < num_exc_pins; i++) { +unsigned int pin = exc_pins[i];
Is the local variable useful?
ret = ad4170_validate_excitation_pin(st, exc_pins[i]);
seems equally readable.
Quoted 14 lines
drivers/iio/adc/ad4170.c +7
-0+ + ret =ad4170_validate_excitation_pin (st, pin); +if (ret) +return ret; + } +return 0 ; +}static int ad4170_parse_reference (struct ad4170_state *st,struct fwnode_handle *child,struct ad4170_setup *setup)
drivers/iio/adc/ad4170.c static int ad4170_parse_channel_node(struct iio_dev *indio_dev, · line 1807
Quoted 7 lines
drivers/iio/adc/ad4170.c +2
-1@@ -1803,8 +2268,9 @@ static int ad4170_parse_channel_node(struct iio_dev *indio_dev,struct ad4170_chan_info *chan_info;struct ad4170_setup *setup;struct iio_chan_spec *chan; +unsigned int ref_select; +unsigned int s_type;unsigned int ch_reg; -u8 ref_select;
Why the change of type?
drivers/iio/adc/ad4170.c static int ad4170_parse_channel_node(struct iio_dev *indio_dev, · line 2334
Quoted 13 lines
drivers/iio/adc/ad4170.c +1
-1bipolar =fwnode_property_read_bool (child,"bipolar" ); setup->afe |=FIELD_PREP (AD4170_AFE_BIPOLAR_MSK , bipolar);if (bipolar)@@ -1843,11 +2327,11 @@ static int ad4170_parse_channel_node(struct iio_dev *indio_dev,else chan->scan_type.sign ='u' ; - ref_select =FIELD_GET (AD4170_AFE_REF_SELECT_MSK , setup->afe); ret =ad4170_validate_channel (st, chan);if (ret <0 )return ret; + ref_select =FIELD_GET (AD4170_AFE_REF_SELECT_MSK , setup->afe);
Can we do it in the patch that introduced the code rather than moving it here?
Quoted 3 lines
drivers/iio/adc/ad4170.c +0
-0ret =ad4170_get_input_range (st, chan, ch_reg, ref_select);if (ret <0 )return dev_err_probe (dev, ret,"Invalid input config\n" );
PATCH v4 11/11 iio: adc: ad4170: Add timestamp channel
In reply to Marcelo SchmittAdd timestamp channel allowing to record the moment at which ADC samples are captured in buffered read mode.
--- drivers/iio/adc/ad4170.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-)
drivers/iio/adc/ad4170.c +17 -4
@@ -182,6 +182,7 @@#define AD4170_NUM_ANALOG_PINS 9#define AD4170_NUM_GPIO_PINS 4#define AD4170_MAX_CHANNELS 16 +#define AD4170_MAX_IIO_CHANNELS (AD4170_MAX_CHANNELS + 1)#define AD4170_MAX_ANALOG_PINS 8#define AD4170_MAX_SETUPS 8#define AD4170_INVALID_SETUP 9
@@ -426,7 +427,7 @@ struct ad4170_state {int vrefs_uv[AD4170_MAX_SUP ];u32 mclk_hz;struct ad4170_setup_info setup_infos[AD4170_MAX_SETUPS ]; -struct iio_chan_spec chans[AD4170_MAX_CHANNELS ]; +struct iio_chan_spec chans[AD4170_MAX_IIO_CHANNELS ];struct ad4170_chan_info chan_infos[AD4170_MAX_CHANNELS ];struct spi_device *spi;struct regmap *regmap;
@@ -443,6 +444,7 @@ struct ad4170_state {unsigned int clock_ctrl;int gpio_fn[AD4170_NUM_GPIO_PINS ];unsigned int cur_src_pins[AD4170_NUM_CURRENT_SRC ]; +unsigned int num_adc_chans;/* * DMA (thus cache coherency maintenance) requires the transfer buffers * to live in their own cache lines.
@@ -2385,7 +2387,16 @@ static int ad4170_parse_channels(struct iio_dev *indio_dev)return dev_err_probe (dev, ret,"Invalid input config\n" ); st->chan_infos[chan_num].input_range_uv = ret; + chan_num++; } + st->num_adc_chans = chan_num; + +/* Add timestamp channel */ +struct iio_chan_spec ts_chan =IIO_CHAN_SOFT_TIMESTAMP (chan_num); + + st->chans[chan_num] = ts_chan; + num_channels = num_channels +1 ; + indio_dev->num_channels = num_channels; indio_dev->channels = st->chans;
@@ -2577,7 +2588,7 @@ static int ad4170_initial_config(struct iio_dev *indio_dev)return dev_err_probe (dev, ret,"Failed to set ADC mode to idle\n" ); -for (i =0 ; i < indio_dev->num_channels; i++) { +for (i =0 ; i < st->num_adc_chans; i++) {struct ad4170_chan_info *chan_info;struct iio_chan_spec const *chan;struct ad4170_setup *setup;
@@ -2702,7 +2713,7 @@ static int ad4170_buffer_predisable(struct iio_dev *indio_dev)* is done after buffer disable. Disable all channels so only requested * channels will be read. */ -for (i =0 ; i < indio_dev->num_channels; i++) { +for (i =0 ; i < st->num_adc_chans; i++) { ret =ad4170_set_channel_enable (st, i, false);if (ret)return ret;
@@ -2752,7 +2763,9 @@ static irqreturn_t ad4170_trigger_handler(int irq, void *p)memcpy (&st->bounce_buffer[i++], st->rx_buf,ARRAY_SIZE (st->rx_buf)); } -iio_push_to_buffers (indio_dev, st->bounce_buffer); +iio_push_to_buffers_with_ts (indio_dev, st->bounce_buffer, +sizeof (st->bounce_buffer), +iio_get_time_ns (indio_dev)); err_out:iio_trigger_notify_done (indio_dev->trig);return IRQ_HANDLED ;
Signature · 1 line
2.47.2