iio: adc: Add TI ADS126X ADC family support
Subsystems (2)
v5 cover iio: adc: Add TI ADS126X ADC family support
Hi all,
I apologize for the long delays between versions. This month has been very busy at university plus some personal stuff. I expect to have more time now, so development should be faster. Thanks for your patience!
---
v5:
dt-bindings
-----------
- Change the '#io-channel-cells' layout to <adc inp inn>: the first cell selects ADC1 (1) or ADC2 (2), and the other two cells select the positive and negative inputs, instead of addressing channels by reg. Document the monitor input numbers (11-14).
- Drop the per-compatible '#io-channel-cells' constraints.
- Allow hexadecimal unit addresses in channel node names.
- In the first example, describe the refp3 and refn3 supplies used by the channel's reference-sources.
ti-ads1262
----------
- Get the device name from match data (struct ads1262_chip_info) instead of reading the device ID register at probe.
- Translate io-channel specifiers by the channel's positive and negative inputs.
- Cache the AVDD and AVSS voltage values to avoid locking issues later when the VBIAS regulator is introduced.
- Channels using the internal reference on both sides (internal-p, internal-n) no longer need the AVSS voltage.
- Only treat the supply as bipolar when the AVSS voltage is non-zero.
- Optimize the buffer SPI message in preenable() and release it in postdisable(), so it can't race the trigger handler when a trigger other than DRDY is used.
- Reject multi-channel sampling with the DRDY trigger when the buffer is enabled, instead of in validate_scan_mask(), which missed trigger changes made after the scan mask was set.
- Report REFOUT as AVSS + 2.5 V through a .get_voltage() callback instead of a fixed 2.5 V, so it is correct with bipolar supplies.
- @Andy:
I didn't convert the reset GPIO into a reset controller for two reasons. The most important one is that we need exclusive control over the line because it also acts as PWDN, depending on the timings. The second is that we are currently using it to keep the chip powered-on, so we would need to depend on CONFIG_RESET_GPIO to avoid issues, and that seems a bit unnecesary.
Also I didn't use variables for the single-channel and diff-channels properties because IMO it's easier to read that way.
Let me know what you think about these.
Misc:
- Drop the redundant <linux/compiler_attributes.h> include.
- Annotate st->channels with __counted_by_ptr(num_channels).
- Rename ADS1262_POWER_TRANS_USECS to ADS1262_POWER_TRANS_US.
- Turn ads1262_default_channel into a macro instead of a static const.
- Add unit suffixes to the clock rate constant and field.
- Use ret == 0 for the completion timeout check.
- Join the ads1262_regmap_write() call onto one line.
- Split the initialization of i in ads1262_parse_channels() and move it next to its first use.
- Use the ?: operator for the clock rate fallback.
- Rename ads1262_supply_setup() to ads1262_supplies_setup().
- Drop the NULL check in ads1262_regulator_disable().
v4: https://patch.msgid.link/[email protected]
v3: https://patch.msgid.link/[email protected]
v2: https://patch.msgid.link/[email protected]
v1: https://patch.msgid.link/[email protected]
---
Kurt Borja (10):
dt-bindings: adc: add excitation-current-chopping property
dt-bindings: iio: adc: support the TI ADS126x ADC family
iio: adc: add the ti-ads1262 driver
iio: adc: ti-ads1262: support per-channel sampling frequency
iio: adc: ti-ads1262: support per-channel reference and gain
iio: adc: ti-ads1262: support input chopping
iio: adc: ti-ads1262: support excitation currents
iio: adc: ti-ads1262: support triggered buffer sampling
iio: adc: ti-ads1262: support REFOUT and VBIAS regulators
iio: adc: ti-ads1262: support common mode supplies
Documentation/devicetree/bindings/iio/adc/adc.yaml | 8 + .../devicetree/bindings/iio/adc/ti,ads1262.yaml | 381 ++++ MAINTAINERS | 7 + drivers/iio/adc/Kconfig | 14 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ti-ads1262.c | 2136 ++++++++++++++++++++ 6 files changed, 2547 insertions(+)
---
base-commit: 04434a1d0f311d76b1f15fa987918b471a0b8c6a
change-id: 20251129-ads126x-fb6107505cae
Signature · 2 lines
Thanks, ~ Kurt
PATCH v5 1/10 dt-bindings: adc: add excitation-current-chopping property
Add a generic excitation-current-chopping property. This is a feature seen frequently in ADCs that are designed to ratiometrically measure resistive loads, such as RTDs and strain gauges. Enabling this can reduce any error due to excitation current mismatch by swapping excitation sources of alternate conversions. This can have side-effects in timing and filter response, so it is not always desirable to enable this feature in some applications. Therefore, it is best to make this a property that can be enabled or disabled in the device tree.
--- Documentation/devicetree/bindings/iio/adc/adc.yaml | 8 ++++++++ 1 file changed, 8 insertions(+)
Documentation/devicetree/bindings/iio/adc/adc.yaml +8 -0
@@ -109,6 +109,14 @@ properties: sources. The index in the array corresponds to the same index in the excitation-channels array. + excitation-current-chopping: + type: boolean + description: + If set, the excitation current sources are swapped and averaged on every + other conversion to reduce current mismatch. This generally comes at the + expense of added settling time between conversions, so should be omitted + on systems with high-frequency signals or high data rate requirements. + burn-out-current-nanoamp: description: Burn-out current sources provide current to the channel's input pins for
Signature · 1 line
2.55.0
PATCH v5 2/10 dt-bindings: iio: adc: support the TI ADS126x ADC family
In reply to Kurt BorjaThe ADS1262 and ADS1263 are 32-bit, 38.4-kSPS delta-sigma ADCs with an integrated PGA, internal reference, excitation and burn-out current sources for sensor biasing and diagnostics. The ADS1263 is compatible with ADS1262, but includes a second auxiliary ADC (ADC2) to perform main channel (ADC1) cross-checking measurements, system background measurements, or temperature compensation of the primary sensor.
Both parts can configure per-channel voltage reference source, excitation current sources (IDAC), plus input and IDAC chopping for offset and IDAC mismatch cancellation. This lets the device drive and ratiometrically measure RTDs and other resistive sensors.
--- .../devicetree/bindings/iio/adc/ti,ads1262.yaml | 381 +++++++++++++++++++++ MAINTAINERS | 6 + 2 files changed, 387 insertions(+)
Documentation/devicetree/bindings/iio/adc/ti,ads1262.yaml new file +381 -0
@@ -0,0 +1,381 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/ti,ads1262.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI ADS1262/ADS1263 analog to digital converter + +maintainers: + - Kurt Borja <[email protected]> + +description: | + The ADS1262 and ADS1263 are 38.4-kSPS, delta-sigma (ΔΣ) ADCs with an + integrated PGA, reference, and internal fault monitors. The ADS1263 integrates + an auxiliary, 24-bit, ΔΣ ADC intended for background measurements. + + Datasheets: + - ADS126x: https://www.ti.com/lit/ds/symlink/ads1262.pdf + +properties: + compatible: + oneOf: + - const: ti,ads1262 + - items: + - const: ti,ads1263 + - const: ti,ads1262 + + reg: + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + spi-max-frequency: + maximum: 8000000 + + spi-cpha: true + + interrupts: + minItems: 1 + maxItems: 2 + description: Data ready (DRDY) interrupt lines. + + interrupt-names: + description: + The data ready (DRDY) signal has two independent interrupt lines. A + dedicated DRDY pin and a pin shared with the serial interface MISO pin, + which is called DOUT/DRDY. + minItems: 1 + maxItems: 2 + items: + enum: [drdy, doutdrdy] + + start-gpios: + description: Start conversion control. + maxItems: 1 + + reset-gpios: + maxItems: 1 + + dvdd-supply: + description: Digital power supply. + + avdd-supply: + description: + Analog power supply. In bipolar supply configurations, the reported + voltage should be in reference to DGND. + + avss-supply: + description: + Negative analog power supply for bipolar configurations. AVSS can only be + at or below the ground reference (DGND). If not described, AVSS is assumed + to be connected to ground (0V). + + clocks: + description: + External crystal or clock wired to the XTAL/CLKIN pins. The device + automatically detects and switches to it. If not connected, the internal + oscillator is used. + maxItems: 1 + + '#io-channel-cells': + const: 3 + description: | + The first cell selects between ADC1 (1) and ADC2 (2). The two following + cells select the positive and negative inputs respectively: + 0-9: AIN0-AIN9 + 10: AINCOM + 11: Temperature sensor monitor + 12: Analog power supply monitor + 13: Digital power supply monitor + 14: TDAC test signal + Monitor inputs (11-14) must be selected as both the positive and negative + input. + + '#gpio-cells': + const: 2 + + gpio-controller: true + + regulators: + type: object + description: + List of regulators provided by this chip. + + properties: + vbias: + $ref: /schemas/regulator/regulator.yaml# + type: object + description: + Level-shift voltage output on the AINCOM pin. Its output is the + mid-voltage of the analog supply, (AVDD + AVSS) / 2, and is used to + shift floating sensors to within the ADC input range. + unevaluatedProperties: false + + refout: + $ref: /schemas/regulator/regulator.yaml# + type: object + description: + Buffered internal voltage reference output on the REFOUT pin. + unevaluatedProperties: false + + additionalProperties: false + +patternProperties: + "^ain([0-9]|com)-supply$": + description: + Common-mode voltage supply connected to AIN<N> or AINCOM. + + "^refp[1-3]-supply$": + description: + Positive voltage reference connected to REFP1 (AIN0), REFP2 (AIN2) or + REFP3 (AIN4). If this regulator is not described and a channel selects + it as a reference source, then it's assumed to be connected to ground + (0V). + + "^refn[1-3]-supply$": + description: + Negative voltage reference connected to REFN1 (AIN1), REFN2 (AIN3) or + REFN3 (AIN5). If this regulator is not described and a channel selects + it as a reference source, then it's assumed to be connected to ground + (0V). + + "^ti,refp[1-3]-refn[1-3]-resistor-ohms$": + description: + Magnitude of the external reference resistor connected between REFP<N> + and REFN<M>. In ratiometric configurations, such as RTD measurements, the + IDAC excitation current returns through this resistor, generating the + reference voltage for the conversion. + + "^channel@[0-9a-f]+$": + $ref: /schemas/iio/adc/adc.yaml# + unevaluatedProperties: false + + properties: + reg: + maxItems: 1 + + single-channel: + minimum: 0 + maximum: 10 + + common-mode-channel: + minimum: 0 + maximum: 10 + default: 10 + + diff-channels: + items: + minimum: 0 + maximum: 10 + + input-chopping: true + + reference-sources: + minItems: 2 + description: + Indicates the reference sources for this channel. The first and second + items are the positive (REFP) and negative (REFN) sources of the main + ADC (ADC1). The third item is the reference source of the secondary + ADC (ADC2) and must always have a positive differential voltage. + items: + - enum: [internal-p, refp1, refp2, refp3, avdd] + - enum: [internal-n, refn1, refn2, refn3, avss] + - enum: [internal, refp1-refn1, refp2-refn2, refp3-refn3, avdd-avss] + + ti,reference-reversal: + $ref: /schemas/types.yaml#/definitions/flag + description: + Indicates that the ADC1 (this has no effect on ADC2) reference voltage + for this channel has negative polarity and thus should be internally + reversed. + + excitation-channels: + minItems: 1 + maxItems: 2 + description: + Selects the pins for the IDAC sources from 0 (AIN0) to 10 (AINCOM). + The first value corresponds to IDAC1 and the second to IDAC2. + items: + minimum: 0 + maximum: 10 + + excitation-current-nanoamp: + minItems: 1 + maxItems: 2 + description: + The first value corresponds to IDAC1 and the second to IDAC2. + items: + enum: [50000, 100000, 250000, 500000, 750000, 1000000, 1500000, + 2000000, 2500000, 3000000] + + excitation-current-chopping: true + + burn-out-current-nanoamp: + description: + Selects current magnitude for the sensor bias current source. + enum: [500, 2000, 10000, 50000, 200000] + + ti,burn-out-resistor: + $ref: /schemas/types.yaml#/definitions/flag + description: + Instead of a fixed current, the sensor bias (burn-out) current source + can be pulled using an internal 10 MΩ resistor. + + ti,burn-out-polarity: + $ref: /schemas/types.yaml#/definitions/string + description: + The sensor bias can be configured to either pull-up or pull-down mode. + In pull-up mode, the current flows into the positive input and flows + out of the negative input. In pull-down mode, the polarities are + reversed. + enum: [pull-up, pull-down] + default: pull-up + + dependencies: + excitation-channels: [excitation-current-nanoamp] + excitation-current-nanoamp: [excitation-channels] + burn-out-current-nanoamp: + not: + required: + - ti,burn-out-resistor + + required: + - reg + + allOf: + - if: + properties: + excitation-channels: + maxItems: 1 + required: + - excitation-channels + then: + properties: + excitation-current-nanoamp: + maxItems: 1 + else: + properties: + excitation-current-nanoamp: + minItems: 2 + + oneOf: + - required: [single-channel] + - required: [diff-channels] + +dependencies: + interrupts: [interrupt-names] + interrupts-extended: [interrupt-names] + +required: + - compatible + - reg + - spi-cpha + - avdd-supply + - dvdd-supply + - '#address-cells' + - '#size-cells' + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + - if: + properties: + compatible: + contains: + const: ti,ads1263 + then: + patternProperties: + "^channel@[0-9a-f]+$": + properties: + reference-sources: + minItems: 3 + maxItems: 3 + default: [internal-p, internal-n, internal] + else: + patternProperties: + "^channel@[0-9a-f]+$": + properties: + reference-sources: + minItems: 2 + maxItems: 2 + default: [internal-p, internal-n] + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "ti,ads1262"; + reg = <0>; + spi-max-frequency = <8000000>; + spi-cpha; + avdd-supply = <&avdd>; + dvdd-supply = <&dvdd>; + refp3-supply = <&refp3>; + refn3-supply = <&refn3>; + #address-cells = <1>; + #size-cells = <0>; + + interrupts-extended = <&gpio 0 IRQ_TYPE_EDGE_FALLING>; + interrupt-names = "drdy"; + + /* Typical common mode voltage configuration */ + aincom-supply = <&ads1262_vbias>; + + regulators { + ads1262_vbias: vbias { }; + }; + + channel@0 { + reg = <0>; + single-channel = <0>; + /* The VBIAS is enabled on pin 10 (AINCOM) */ + common-mode-channel = <10>; + reference-sources = "refp3", "refn3"; + }; + }; + }; + + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "ti,ads1263", "ti,ads1262"; + reg = <0>; + spi-max-frequency = <8000000>; + spi-cpha; + avdd-supply = <&avdd>; + dvdd-supply = <&dvdd>; + #address-cells = <1>; + #size-cells = <0>; + + interrupts-extended = <&gpio 0 IRQ_TYPE_EDGE_FALLING>; + interrupt-names = "drdy"; + + ti,refp2-refn2-resistor-ohms = <3520>; + + channel@0 { + reg = <0>; + diff-channels = <4 5>; + reference-sources = "refp2", "refn2", "refp2-refn2"; + excitation-channels = <1 6>; + excitation-current-nanoamp = <500000 500000>; + }; + }; + };
MAINTAINERS +6 -0
@@ -27321,6 +27321,12 @@ S: Maintained F: Documentation/devicetree/bindings/iio/adc/ti,ads1018.yaml F: drivers/iio/adc/ti-ads1018.c +TI ADS1262 ADC DRIVER +M: Kurt Borja <[email protected]> +L: [email protected] +S: Maintained +F: Documentation/devicetree/bindings/iio/adc/ti,ads1262.yaml + TI ADS7924 ADC DRIVER M: Hugo Villeneuve <[email protected]> L: [email protected]
Signature · 1 line
2.55.0
[email protected]
AI reviewRe: [PATCH v5 02/10] dt-bindings: iio: adc: support the TI ADS126x ADC family
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] dt-bindings: missing mandatory required properties for GPIO controller --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] dt-bindings: missing mandatory required properties for GPIO controller --
commit 71f9ec7fa4f648264b425d77eb77849a06771e4a
Author: Kurt Borja <[email protected]>
dt-bindings: iio: adc: support the TI ADS126x ADC family
The ADS1262 and ADS1263 are 32-bit, 38.4-kSPS delta-sigma ADCs with an integrated PGA, internal reference, excitation and burn-out current sources for sensor biasing and diagnostics. The ADS1263 is compatible with ADS1262, but includes a second auxiliary ADC (ADC2).
Quoted 5 lines
Documentation/devicetree/bindings/iio/adc/ti,ads1262.yaml new file +0
-0
[ ... ]
Quoted 7 lines
Documentation/devicetree/bindings/iio/adc/ti,ads1262.yaml new file +7
-0+ '#gpio-cells': + const: 2 + + gpio-controller: true + + regulators: + type: object
[ ... ]
Documentation/devicetree/bindings/iio/adc/ti,ads1262.yaml line 281
Quoted 12 lines
Documentation/devicetree/bindings/iio/adc/ti,ads1262.yaml new file +12
-0+dependencies: + interrupts: [interrupt-names] + interrupts-extended: [interrupt-names] + +required: + - compatible + - reg + - spi-cpha + - avdd-supply + - dvdd-supply + - '#address-cells' + - '#size-cells'
[Severity: Medium]
Since this hardware adds provider capabilities by defining gpio-controller and #gpio-cells, shouldn't these properties be explicitly included in the required list?
When hardware gains new provider capabilities like a GPIO controller, the dt-bindings subsystem guidelines state that the corresponding properties should be mandatory. Omitting them from the required list allows incomplete device tree nodes to silently pass schema validation, which could cause drivers or dependent subsystems to fail at runtime.
Quoted 3 lines
Documentation/devicetree/bindings/iio/adc/ti,ads1262.yaml new file +3
-0+ +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml#
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
Re: [PATCH v5 02/10] dt-bindings: iio: adc: support the TI ADS126x ADC family
In reply to Kurt BorjaQuoted 7 lines · Kurt Borja
The ADS1262 and ADS1263 are 32-bit, 38.4-kSPS delta-sigma ADCs with an integrated PGA, internal reference, excitation and burn-out current sources for sensor biasing and diagnostics. The ADS1263 is compatible with ADS1262, but includes a second auxiliary ADC (ADC2) to perform main channel (ADC1) cross-checking measurements, system background measurements, or temperature compensation of the primary sensor.
Quoted 7 lines
Both parts can configure per-channel voltage reference source, excitation current sources (IDAC), plus input and IDAC chopping for offset and IDAC mismatch cancellation. This lets the device drive and ratiometrically measure RTDs and other resistive sensors.
Reviewed-by David Lechner <[email protected]>Signed-off-by Kurt Borja <[email protected]>
Previously I commented that this was probably too complex for me to do a genuine review of, but David seems happy so that's good enough for me.
Since you changed the stuff I didn't like,
pw-bot: not-applicable
Thanks,
Conor.
PATCH v5 3/10 iio: adc: add the ti-ads1262 driver
In reply to Kurt BorjaAdd the ti-ads1262 driver with initial support for the primary ADC (ADC1). The ADS1263 auxiliary ADC (ADC2) is handled by a separate driver and interoperability considerations were taken into account.
Various features such as accurate timeout delays, per-channel reference sources, scale, offset, settling latency, excitation currents and diagnostics are intentionally left out for future support.
--- MAINTAINERS | 1 + drivers/iio/adc/Kconfig | 11 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ti-ads1262.c | 927 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 940 insertions(+)
MAINTAINERS +1 -0
@@ -27326,6 +27326,7 @@ M: Kurt Borja <[email protected]> L: [email protected] S: Maintained F: Documentation/devicetree/bindings/iio/adc/ti,ads1262.yaml +F: drivers/iio/adc/ti-ads1262.c TI ADS7924 ADC DRIVER M: Hugo Villeneuve <[email protected]>
drivers/iio/adc/Kconfig +11 -0
@@ -1900,6 +1900,17 @@ config TI_ADS124S08 This driver can also be built as a module. If so, the module will be called ti-ads124s08. +config TI_ADS1262 + tristate "Texas Instruments ADS1262" + depends on SPI + select REGMAP + help + If you say yes here you get support for Texas Instruments ADS1262 and + ADS1263 ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-ads1262. + config TI_ADS1298 tristate "Texas Instruments ADS1298" depends on SPI
drivers/iio/adc/Makefile +1 -0
@@ -161,6 +161,7 @@ obj-$(CONFIG_TI_ADS1119) += ti-ads1119.o obj-$(CONFIG_TI_ADS112C04) += ti-ads112c04.o obj-$(CONFIG_TI_ADS112C14) += ti-ads112c14.o obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o +obj-$(CONFIG_TI_ADS1262) += ti-ads1262.o obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o obj-$(CONFIG_TI_ADS131M02) += ti-ads131m02.o
drivers/iio/adc/ti-ads1262.c new file +927 -0
927 lines not on this page · raw message
Signature · 1 line
2.55.0
[email protected]
AI reviewRe: [PATCH v5 03/10] iio: adc: add the ti-ads1262 driver
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Medium] iio: adc: ti-ads1262: lockdep splat on regmap debugfs access - [Medium] iio: adc: ti-ads1262…
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] iio: adc: ti-ads1262: lockdep splat on regmap debugfs access
- [Medium] iio: adc: ti-ads1262: stale DRDY on interrupted channel read causes corrupted data --
commit 5387f009404bbcc26fabb71c5f2ad0b5ba669607
Author: Kurt Borja <[email protected]>
iio: adc: add the ti-ads1262 driver
Add the ti-ads1262 driver with initial support for the primary ADC (ADC1). The ADS1263 auxiliary ADC (ADC2) is handled by a separate driver and interoperability considerations were taken into account.
Quoted 4 lines
drivers/iio/adc/ti-ads1262.c new file +0
-0
[ ... ]
Quoted 17 lines
drivers/iio/adc/ti-ads1262.c new file +17
-0+static int ads1262_channel_read (struct iio_dev *indio_dev, +const struct iio_chan_spec *spec,__be32 *val) +{ +struct ads1262 *st =iio_priv (indio_dev); +int ret; + +IIO_DEV_ACQUIRE_DIRECT_MODE (indio_dev, claim); +if (IIO_DEV_ACQUIRE_FAILED (claim)) +return -EBUSY ; + + ret =ads1262_set_runmode (st,ADS1262_RUNMODE_PULSE ); +if (ret) +return ret; + + ret =ads1262_channel_enable (st, spec); +if (ret) +return ret;
drivers/iio/adc/ti-ads1262.c line 415
Quoted 10 lines
drivers/iio/adc/ti-ads1262.c new file +10
-0+ +reinit_completion (&st->drdy); + + ret =ads1262_dev_start_one (st); +if (ret) +return ret; + + ret =ads1262_wait_for_conversion (st); +if (ret) +return ret;
[Severity: Medium]
If ads1262_wait_for_conversion() is interrupted by a signal and returns -ERESTARTSYS, does this leave the conversion running in the background?
If so, a stale DRDY interrupt from that background conversion might fire on the next read exactly in the window between reinit_completion() and the hardware processing the new START1 command. This would cause the wait to complete prematurely and return corrupted or stale data to user space.
Is there a way to drain the hardware state or cancel the conversion when the wait is interrupted?
[ ... ]
drivers/iio/adc/ti-ads1262.c line 627
Quoted 7 lines
drivers/iio/adc/ti-ads1262.c new file +7
-0+static int ads1262_regmap_read (void *context,const void *reg_buf, +size_t reg_size,void *val_buf,size_t val_size) +{ +struct ads1262 *st = context; +u8 tx[2 ]; + +lockdep_assert_held (&st->xfer_lock);
[Severity: Medium]
Will this lockdep assertion fail when the regmap is accessed via debugfs?
Although the driver manually acquires st->xfer_lock before invoking regmap updates, it does not wire this lock into the regmap core's configuration (via custom lock/unlock callbacks, or by setting disable_locking = true).
As a result, the regmap core manages its own internal mutex and debugfs accesses will bypass st->xfer_lock, calling into the bus callbacks directly and triggering this assertion.
Quoted 13 lines
drivers/iio/adc/ti-ads1262.c new file +13
-0+ +/* +* The register read operation uses a two byte command header followed +* by the register data: +* +* byte 0: RREG opcode | register address +* byte 1: number of registers to transfer, minus one +* byte 2..: register data +*/ + tx[0 ] = *(u8 *)reg_buf |ADS1262_OPCODE_RREG ; + tx[1 ] = val_size -1 ; + +return spi_write_then_read (st->spi, tx,sizeof (tx), val_buf, val_size);
drivers/iio/adc/ti-ads1262.c line 650
Quoted 10 lines
drivers/iio/adc/ti-ads1262.c new file +10
-0+} + +static int ads1262_regmap_gather_write (void *context,const void *reg_buf, +size_t reg_size,const void *val_buf, +size_t val_size) +{ +struct ads1262 *st = context; +u8 tx[ADS1262_REGMAP_WRITE_SZ +2 ]; + +lockdep_assert_held (&st->xfer_lock);
[Severity: Medium]
Similarly, will this assertion fail on regmap debugfs writes for the same reasons?
Quoted 15 lines
drivers/iio/adc/ti-ads1262.c new file +15
-0+ +/* +* The register write operation uses a two byte command header followed +* by the register data: +* +* byte 0: WREG opcode | register address +* byte 1: number of registers to transfer, minus one +* byte 2..: register data +*/ + tx[0 ] = *(u8 *)reg_buf |ADS1262_OPCODE_WREG ; + tx[1 ] = val_size -1 ; +memcpy (&tx[2 ], val_buf, val_size); + +return spi_write_then_read (st->spi, tx,2 + val_size,NULL ,0 ); +}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
PATCH v5 4/10 iio: adc: ti-ads1262: support per-channel sampling frequency
In reply to Kurt BorjaAdd per-channel sampling frequency support. The "available" attribute is assigned per-channel too, in order to eventually support per-filter availability.
--- drivers/iio/adc/ti-ads1262.c | 233 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 228 insertions(+), 5 deletions(-)
drivers/iio/adc/ti-ads1262.c +228 -5
@@ -24,6 +24,7 @@#include <linux/regulator/consumer.h> #include <linux/spi/spi.h> #include <linux/types.h> +#include <linux/units.h> #include <asm/byteorder.h>
@@ -98,6 +99,7 @@#define ADS1262_DR_14400_SPS 13#define ADS1262_DR_19200_SPS 14#define ADS1262_DR_38400_SPS 15 +#define ADS1262_DR_COUNT (ADS1262_DR_38400_SPS + 1)#define ADS1262_INPMUX_REG 0x06#define ADS1262_INPMUX_MUXP_MASK GENMASK(7, 4)
@@ -165,6 +167,7 @@#define ADS1262_POWER_TRANS_US 65536#define ADS1262_NOMINAL_CLK_RATE_HZ 7372800 +#define ADS1262_MODULATOR_DIV 8#define ADS1262_FW_CHANNEL_COUNT 16#define ADS1262_MON_CHANNEL_COUNT 4
@@ -176,16 +179,25 @@ struct ads1262_chip_info {const char *name; }; +struct ads1262_channel { +u8 data_rate; +u8 filter; +}; +struct ads1262 {struct spi_device *spi;struct regmap *regmap;struct gpio_desc *start_gpiod; +size_t num_channels; + struct ads1262_channel *channels __counted_by_ptr(num_channels);/* protects concurrent SPI transfers */ struct mutex xfer_lock;/* protects channel state */ struct mutex chan_lock;struct completion drdy;unsigned long clk_rate_hz; +int sampling_freq_table[ADS1262_DR_COUNT ][2 ]; +int sampling_freq_fir[ADS1262_DR_16_6_SPS +1 ][2 ]; };static const struct iio_chan_spec ads1262_monitor_chan_specs[] = {
@@ -199,7 +211,9 @@ static const struct iio_chan_spec ads1262_monitor_chan_specs[] = { .storagebits = 32, .endianness = IIO_BE, }, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), }, { .type = IIO_VOLTAGE,
@@ -212,7 +226,9 @@ static const struct iio_chan_spec ads1262_monitor_chan_specs[] = { .storagebits = 32, .endianness = IIO_BE, }, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), }, { .type = IIO_VOLTAGE,
@@ -225,7 +241,9 @@ static const struct iio_chan_spec ads1262_monitor_chan_specs[] = { .storagebits = 32, .endianness = IIO_BE, }, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), }, { .type = IIO_VOLTAGE,
@@ -239,10 +257,66 @@ static const struct iio_chan_spec ads1262_monitor_chan_specs[] = { .storagebits = 32, .endianness = IIO_BE, }, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate =BIT (IIO_CHAN_INFO_RAW ) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), }, }; +#define ads1262_default_channel ((struct ads1262_channel) { \ + .data_rate = ADS1262_DR_20_SPS, \ + .filter = ADS1262_FILTER_FIR, \ +}) + +static const u32 ads1262_mod_decimator_a[] = { + [ADS1262_DR_2_5_SPS ] =64 , + [ADS1262_DR_5_SPS ] =64 , + [ADS1262_DR_10_SPS ] =64 , + [ADS1262_DR_16_6_SPS ] =64 , + [ADS1262_DR_20_SPS ] =64 , + [ADS1262_DR_50_SPS ] =64 , + [ADS1262_DR_60_SPS ] =64 , + [ADS1262_DR_100_SPS ] =64 , + [ADS1262_DR_400_SPS ] =64 , + [ADS1262_DR_1200_SPS ] =64 , + [ADS1262_DR_2400_SPS ] =64 , + [ADS1262_DR_4800_SPS ] =64 , + [ADS1262_DR_7200_SPS ] =64 , + [ADS1262_DR_14400_SPS ] =64 , + [ADS1262_DR_19200_SPS ] =48 , + [ADS1262_DR_38400_SPS ] =24 , +}; + +static const u32 ads1262_mod_decimator_b[] = { + [ADS1262_DR_2_5_SPS ] =5760 , + [ADS1262_DR_5_SPS ] =2880 , + [ADS1262_DR_10_SPS ] =1440 , + [ADS1262_DR_16_6_SPS ] =864 , + [ADS1262_DR_20_SPS ] =720 , + [ADS1262_DR_50_SPS ] =288 , + [ADS1262_DR_60_SPS ] =240 , + [ADS1262_DR_100_SPS ] =144 , + [ADS1262_DR_400_SPS ] =36 , + [ADS1262_DR_1200_SPS ] =12 , + [ADS1262_DR_2400_SPS ] =6 , + [ADS1262_DR_4800_SPS ] =3 , + [ADS1262_DR_7200_SPS ] =2 , + [ADS1262_DR_14400_SPS ] =1 , + [ADS1262_DR_19200_SPS ] =1 , + [ADS1262_DR_38400_SPS ] =1 , +}; + +static int ads1262_find_two (const int (*array)[2 ],size_t num_elements,int val, +int val2) +{ +for (unsigned int i =0 ; i < num_elements; i++) { +if (val == array[i][0 ] && val2 == array[i][1 ]) +return i; + } + +return -EINVAL ; +} +static int ads1262_dev_send_cmd (struct ads1262 *st,u8 opcode) {guard (mutex)(&st->xfer_lock);
@@ -367,9 +441,18 @@ static int ads1262_wait_for_conversion(struct ads1262 *st)static int ads1262_channel_enable (struct ads1262 *st,const struct iio_chan_spec *spec) { +struct ads1262_channel *chan = &st->channels[spec->scan_index]; +int ret; +guard (mutex)(&st->xfer_lock);guard (mutex)(&st->chan_lock); + ret =regmap_update_bits (st->regmap,ADS1262_MODE2_REG , +ADS1262_MODE2_DR_MASK , +FIELD_PREP (ADS1262_MODE2_DR_MASK , chan->data_rate)); +if (ret) +return ret; + return regmap_update_bits(st->regmap, ADS1262_INPMUX_REG, ADS1262_INPMUX_MUXN_MASK | ADS1262_INPMUX_MUXP_MASK,
@@ -421,6 +504,8 @@ static int ads1262_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { + struct ads1262 *st = iio_priv(indio_dev); +struct ads1262_channel *chan_data = &st->channels[chan->scan_index];__be32 raw;int ret;
@@ -433,6 +518,83 @@ static int ads1262_read_raw(struct iio_dev *indio_dev,return IIO_VAL_INT ; +case IIO_CHAN_INFO_SAMP_FREQ : { +guard (mutex)(&st->chan_lock); + + *val = st->sampling_freq_table[chan_data->data_rate][0 ]; + *val2 = st->sampling_freq_table[chan_data->data_rate][1 ]; + +return IIO_VAL_INT_PLUS_MICRO ; + } + +default : +return -EOPNOTSUPP ; + } +} + +static int ads1262_read_avail (struct iio_dev *indio_dev, +struct iio_chan_spec const *chan,const int **vals, +int *type,int *length,long mask) +{ +struct ads1262 *st =iio_priv (indio_dev); +struct ads1262_channel *chan_data = &st->channels[chan->scan_index]; + +switch (mask) { +case IIO_CHAN_INFO_SAMP_FREQ : + *type =IIO_VAL_INT_PLUS_MICRO ; + +switch (chan_data->filter) { +case ADS1262_FILTER_FIR : + *vals = (const int *)st->sampling_freq_fir; + *length =ARRAY_SIZE (st->sampling_freq_fir) *2 ; +return IIO_AVAIL_LIST ; +default : +return -EOPNOTSUPP ; + } + +default : +return -EOPNOTSUPP ; + } +} + +static int ads1262_write_raw (struct iio_dev *indio_dev, +struct iio_chan_spec const *chan,int val, +int val2,long mask) +{ +struct ads1262 *st =iio_priv (indio_dev); +struct ads1262_channel *chan_data = &st->channels[chan->scan_index]; +int ret; + +IIO_DEV_ACQUIRE_DIRECT_MODE (indio_dev, claim); +if (IIO_DEV_ACQUIRE_FAILED (claim)) +return -EBUSY ; + +guard (mutex)(&st->chan_lock); + +switch (mask) { +case IIO_CHAN_INFO_SAMP_FREQ : +switch (chan_data->filter) { +case ADS1262_FILTER_FIR : + ret =ads1262_find_two (st->sampling_freq_fir, +ARRAY_SIZE (st->sampling_freq_fir), + val, val2); +if (ret <0 ) +return ret; +break ; +default : +return -EINVAL ; + } + + ret =ads1262_find_two (st->sampling_freq_table, +ARRAY_SIZE (st->sampling_freq_table), + val, val2); +if (ret <0 ) +return ret; + + chan_data->data_rate = ret; + +return 0 ; +default :return -EOPNOTSUPP ; }
@@ -472,6 +634,8 @@ static int ads1262_fwnode_xlate(struct iio_dev *indio_dev,static const struct iio_info ads1262_iio_info = { .read_raw = ads1262_read_raw, + .read_avail = ads1262_read_avail, + .write_raw = ads1262_write_raw, .debugfs_reg_access = ads1262_debugfs_reg_access, .fwnode_xlate = ads1262_fwnode_xlate, };
@@ -678,6 +842,49 @@ static const struct regmap_bus ads1262_regmap_bus = { .max_raw_write = ADS1262_REGMAP_WRITE_SZ, }; +static void ads1262_populate_samp_freqs (struct ads1262 *st) +{ +u64 freq_Hz, freq_uHz; +u32 freq_rem; + +for (unsigned int i =0 ; i <ARRAY_SIZE (st->sampling_freq_table); i++) { + freq_uHz =div_u64 (mul_u32_u32 (st->clk_rate_hz,MICRO ), +ADS1262_MODULATOR_DIV * + ads1262_mod_decimator_a[i] * + ads1262_mod_decimator_b[i]); + freq_Hz =div_u64_rem (freq_uHz,MICRO , &freq_rem); + + st->sampling_freq_table[i][0 ] = (int )freq_Hz; + st->sampling_freq_table[i][1 ] = (int )freq_rem; + } + +for (unsigned int i =0 ; i <ARRAY_SIZE (st->sampling_freq_fir); i++) { +/* +* The FIR filter does not support the 16.6 SPS data rate so it +* gets mapped to 20 SPS instead. +*/ +switch (i) { + case ADS1262_DR_2_5_SPS ... ADS1262_DR_10_SPS: + st->sampling_freq_fir[i][0 ] = st->sampling_freq_table[i][0 ]; + st->sampling_freq_fir[i][1 ] = st->sampling_freq_table[i][1 ]; +break ; +case ADS1262_DR_16_6_SPS : + st->sampling_freq_fir[i][0 ] = st->sampling_freq_table[ADS1262_DR_20_SPS ][0 ]; + st->sampling_freq_fir[i][1 ] = st->sampling_freq_table[ADS1262_DR_20_SPS ][1 ]; +break ; + } + } +} + +static int ads1262_populate_tables (struct iio_dev *indio_dev) +{ +struct ads1262 *st =iio_priv (indio_dev); + +ads1262_populate_samp_freqs (st); + +return 0 ; +} +static int ads1262_parse_channel_node (struct ads1262 *st,struct iio_chan_spec *spec,struct fwnode_handle *node)
@@ -744,9 +951,18 @@ static int ads1262_parse_channels(struct iio_dev *indio_dev)if (!chan_specs)return -ENOMEM ; + st->num_channels = num_fw_channels +ADS1262_MON_CHANNEL_COUNT ; + st->channels =devm_kcalloc (dev, st->num_channels,sizeof (*st->channels), +GFP_KERNEL ); +if (!st->channels) +return -ENOMEM ; + i =0 ;device_for_each_named_child_node_scoped (dev, node,"channel" ) {struct iio_chan_spec *spec = &chan_specs[i]; +struct ads1262_channel *chan = &st->channels[i]; + + *chan = ads1262_default_channel; ret =ads1262_parse_channel_node (st, spec, node);if (ret)
@@ -767,7 +983,9 @@ static int ads1262_parse_channels(struct iio_dev *indio_dev) .storagebits = 32, .endianness = IIO_BE, }; - spec->info_mask_separate =BIT (IIO_CHAN_INFO_RAW ); + spec->info_mask_separate =BIT (IIO_CHAN_INFO_RAW ) | +BIT (IIO_CHAN_INFO_SAMP_FREQ ); + spec->info_mask_separate_available =BIT (IIO_CHAN_INFO_SAMP_FREQ ); i++; }
@@ -776,6 +994,7 @@ static int ads1262_parse_channels(struct iio_dev *indio_dev) sizeof(ads1262_monitor_chan_specs));for (unsigned int mon =0 ; mon <ADS1262_MON_CHANNEL_COUNT ; mon++) { + st->channels[i] = ads1262_default_channel; chan_specs[i].scan_index = i; i++; }
@@ -875,6 +1094,10 @@ static int ads1262_spi_probe(struct spi_device *spi)if (ret)return dev_err_probe (dev, ret,"failed to configure device\n" ); + ret =ads1262_populate_tables (indio_dev); +if (ret) +return ret; +/* * REVISIT: This chip has software polling capabilities, which could be * used to stop depending on the DRDY signal.
Signature · 1 line
2.55.0
[email protected]
AI reviewRe: [PATCH v5 04/10] iio: adc: ti-ads1262: support per-channel sampling frequency
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] iio: adc: ti-ads1262: out-of-bounds access on st->channels for timestamp channel --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] iio: adc: ti-ads1262: out-of-bounds access on st->channels for timestamp channel --
commit 7618e3d1bfd0f75640c3a335d284574aab75861b
Author: Kurt Borja <[email protected]>
iio: adc: ti-ads1262: support per-channel sampling frequency
This commit adds per-channel sampling frequency support to the ti-ads1262 driver. It also assigns the "available" attribute per-channel in preparation for per-filter availability support.
Quoted 4 lines
drivers/iio/adc/ti-ads1262.c +0
-0
[ ... ]
drivers/iio/adc/ti-ads1262.c static int ads1262_wait_for_conversion(struct ads1262 *st) · line 518
Quoted 7 lines
drivers/iio/adc/ti-ads1262.c +3
-0@@ -436,11 +513,19 @@ static int ads1262_wait_for_conversion(struct ads1262 *st)static int ads1262_channel_enable (struct ads1262 *st,const struct iio_chan_spec *spec) { +struct ads1262_channel *chan = &st->channels[spec->scan_index]; +int ret; +
[Severity: High]
Does this cause an out-of-bounds array access if this function is called for the soft timestamp channel?
Because st->channels is allocated without space for the timestamp channel, the timestamp channel is assigned a scan_index equal to st->num_channels, which is one element past the end of the array.
When triggered buffer sampling is introduced in the follow-up patches, iio_for_each_active_channel() iterates over all bits in the active scan mask, including the soft timestamp channel if enabled by userspace.
If the timestamp channel is passed to the preparation functions and reaches here, it looks like this will read out-of-bounds to get chan->data_rate, and then send corrupted configuration commands to the hardware via regmap_update_bits().
Quoted 12 lines
drivers/iio/adc/ti-ads1262.c +6
-0guard (mutex)(&st->xfer_lock);guard (mutex)(&st->chan_lock); + ret =regmap_update_bits (st->regmap,ADS1262_MODE2_REG , +ADS1262_MODE2_DR_MASK , +FIELD_PREP (ADS1262_MODE2_DR_MASK , chan->data_rate)); +if (ret) +return ret; + return regmap_update_bits(st->regmap, ADS1262_INPMUX_REG, ADS1262_INPMUX_MUXN_MASK | ADS1262_INPMUX_MUXP_MASK,
[ ... ]
Quoted 10 lines
drivers/iio/adc/ti-ads1262.c +6
-0@@ -744,9 +951,18 @@ static int ads1262_parse_channels(struct iio_dev *indio_dev)if (!chan_specs)return -ENOMEM ; + st->num_channels = num_fw_channels +ADS1262_MON_CHANNEL_COUNT ; + st->channels =devm_kcalloc (dev, st->num_channels,sizeof (*st->channels), +GFP_KERNEL ); +if (!st->channels) +return -ENOMEM ; +
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4
PATCH v5 5/10 iio: adc: ti-ads1262: support per-channel reference and gain
In reply to Kurt BorjaAllow each channel to select its voltage reference through the "reference-sources" firmware property. Then, use the reference voltage to calculate available scales. The PGA bypass handling is intentionally left out until more information on its usual applications is gathered.
The ADS1262 allows single-ended supply configurations or bipolar supply configurations. In single ended configurations both the analog and digital rails share the same ground, i.e. AVSS = DGND = 0 V. In bipolar supply configurations, AVSS can go below ground, e.g. AVSS = -2.5 V. If AVSS is below ground, the ADC can achieve true bipolar measurements and the external references can also have voltage levels below ground.
This is currently an issue because the regulator subsystem doesn't support reading negative voltages. The ad4170-4 driver faces this problem too and the same workaround is used in this case: assume every regulator reports magnitudes (absolute values). If the chip has a bipolar supply configuration, then assume positive references are above ground (>= 0 V) and negative references are below ground (<= 0 V). This is not a hardware constraint, but it is the most common wiring.
--- drivers/iio/adc/ti-ads1262.c | 479 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 454 insertions(+), 25 deletions(-)
drivers/iio/adc/ti-ads1262.c +454 -25
@@ -8,6 +8,7 @@#include <linux/array_size.h> #include <linux/bitfield.h> #include <linux/bitops.h> +#include <linux/bitmap.h> #include <linux/cleanup.h> #include <linux/clk.h> #include <linux/completion.h>
@@ -17,12 +18,14 @@#include <linux/interrupt.h> #include <linux/lockdep.h> #include <linux/math64.h> +#include <linux/minmax.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/property.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/spi/spi.h> +#include <linux/string.h> #include <linux/types.h> #include <linux/units.h>
@@ -82,6 +85,7 @@#define ADS1262_MODE2_REG 0x05#define ADS1262_MODE2_BYPASS_MASK BIT(7)#define ADS1262_MODE2_GAIN_MASK GENMASK(6, 4) +#define ADS1262_GAIN_COUNT 6#define ADS1262_MODE2_DR_MASK GENMASK(3, 0)#define ADS1262_DR_2_5_SPS 0#define ADS1262_DR_5_SPS 1
@@ -122,7 +126,7 @@#define ADS1262_IDACMUX_REG 0x0D#define ADS1262_IDACMUX_MUX2_MASK GENMASK(7, 4)#define ADS1262_IDACMUX_MUX1_MASK GENMASK(3, 0) -#define ADS1262_IDACMUX_NO_CONN 0b1011 +#define ADS1262_IDACMUX_NO_CONN 11#define ADS1262_IDACMAG_REG 0x0E#define ADS1262_IDACMAG_MAG2_MASK GENMASK(7, 4)
@@ -130,7 +134,17 @@#define ADS1262_REFMUX_REG 0x0F#define ADS1262_REFMUX_RMUXP_MASK GENMASK(5, 3) +#define ADS1262_RMUXP_INTERNAL 0 +#define ADS1262_RMUXP_REFP1 1 +#define ADS1262_RMUXP_REFP2 2 +#define ADS1262_RMUXP_REFP3 3 +#define ADS1262_RMUXP_AVDD 4#define ADS1262_REFMUX_RMUXN_MASK GENMASK(2, 0) +#define ADS1262_RMUXN_INTERNAL 0 +#define ADS1262_RMUXN_REFN1 1 +#define ADS1262_RMUXN_REFN2 2 +#define ADS1262_RMUXN_REFN3 3 +#define ADS1262_RMUXN_AVSS 4#define ADS1262_TDACP_REG 0x10#define ADS1262_TDACP_OUTP_MASK BIT(7)
@@ -168,9 +182,13 @@#define ADS1262_NOMINAL_CLK_RATE_HZ 7372800#define ADS1262_MODULATOR_DIV 8 +#define ADS1262_INTERNAL_REFERENCE_uV 2500000 +#define ADS1262_TEMP_SLOPE_uV_C 420ULL +#define ADS1262_TEMP_ZERO_C 111900ULL#define ADS1262_FW_CHANNEL_COUNT 16#define ADS1262_MON_CHANNEL_COUNT 4 +#define ADS1262_EXT_REF_COUNT 3#define ADS1262_REGMAP_WRITE_SZ 8#define ADS1262_ADC1_RESOLUTION 32
@@ -182,6 +200,13 @@ struct ads1262_chip_info {struct ads1262_channel {u8 data_rate;u8 filter; +u8 gain; +u8 ref_p; +u8 ref_n; +bool ref_reversal; +bool is_resistance; +size_t num_scales; +int scales[ADS1262_GAIN_COUNT ][2 ]; };struct ads1262 {
@@ -196,8 +221,14 @@ struct ads1262 {struct mutex chan_lock;struct completion drdy;unsigned long clk_rate_hz; +bool bipolar_supply; +int avdd_uV; +int avss_uV;int sampling_freq_table[ADS1262_DR_COUNT ][2 ];int sampling_freq_fir[ADS1262_DR_16_6_SPS +1 ][2 ]; +u32 rref_ohms[ADS1262_EXT_REF_COUNT ][ADS1262_EXT_REF_COUNT ]; +int refp_uV[ADS1262_EXT_REF_COUNT ]; +int refn_uV[ADS1262_EXT_REF_COUNT ]; };static const struct iio_chan_spec ads1262_monitor_chan_specs[] = {
@@ -212,8 +243,11 @@ static const struct iio_chan_spec ads1262_monitor_chan_specs[] = { .endianness = IIO_BE, }, .info_mask_separate =BIT (IIO_CHAN_INFO_RAW ) | - BIT(IIO_CHAN_INFO_SAMP_FREQ), - .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), +BIT (IIO_CHAN_INFO_OFFSET ) | +BIT (IIO_CHAN_INFO_SAMP_FREQ ) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available =BIT (IIO_CHAN_INFO_SAMP_FREQ ) | + BIT(IIO_CHAN_INFO_SCALE), }, { .type = IIO_VOLTAGE,
@@ -227,8 +261,10 @@ static const struct iio_chan_spec ads1262_monitor_chan_specs[] = { .endianness = IIO_BE, }, .info_mask_separate =BIT (IIO_CHAN_INFO_RAW ) | - BIT(IIO_CHAN_INFO_SAMP_FREQ), - .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), +BIT (IIO_CHAN_INFO_SAMP_FREQ ) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available =BIT (IIO_CHAN_INFO_SAMP_FREQ ) | + BIT(IIO_CHAN_INFO_SCALE), }, { .type = IIO_VOLTAGE,
@@ -242,8 +278,10 @@ static const struct iio_chan_spec ads1262_monitor_chan_specs[] = { .endianness = IIO_BE, }, .info_mask_separate =BIT (IIO_CHAN_INFO_RAW ) | - BIT(IIO_CHAN_INFO_SAMP_FREQ), - .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), +BIT (IIO_CHAN_INFO_SAMP_FREQ ) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available =BIT (IIO_CHAN_INFO_SAMP_FREQ ) | + BIT(IIO_CHAN_INFO_SCALE), }, { .type = IIO_VOLTAGE,
@@ -258,8 +296,10 @@ static const struct iio_chan_spec ads1262_monitor_chan_specs[] = { .endianness = IIO_BE, }, .info_mask_separate =BIT (IIO_CHAN_INFO_RAW ) | - BIT(IIO_CHAN_INFO_SAMP_FREQ), - .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), +BIT (IIO_CHAN_INFO_SAMP_FREQ ) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available =BIT (IIO_CHAN_INFO_SAMP_FREQ ) | + BIT(IIO_CHAN_INFO_SCALE), }, };
@@ -306,6 +346,24 @@ static const u32 ads1262_mod_decimator_b[] = { [ADS1262_DR_38400_SPS] = 1, }; +static const char *const ads1262_ref_sources_pos[] = { + [ADS1262_RMUXP_INTERNAL ] ="internal-p" , + [ADS1262_RMUXP_REFP1 ] ="refp1" , + [ADS1262_RMUXP_REFP2 ] ="refp2" , + [ADS1262_RMUXP_REFP3 ] ="refp3" , + [ADS1262_RMUXP_AVDD ] ="avdd" , +NULL +}; + +static const char *const ads1262_ref_sources_neg[] = { + [ADS1262_RMUXN_INTERNAL ] ="internal-n" , + [ADS1262_RMUXN_REFN1 ] ="refn1" , + [ADS1262_RMUXN_REFN2 ] ="refn2" , + [ADS1262_RMUXN_REFN3 ] ="refn3" , + [ADS1262_RMUXN_AVSS ] ="avss" , +NULL +}; +static int ads1262_find_two (const int (*array)[2 ],size_t num_elements,int val,int val2) {
@@ -317,6 +375,12 @@ static int ads1262_find_two(const int (*array)[2], size_t num_elements, int val,return -EINVAL ; } +static bool ads1262_reference_is_external (int ref_p,int ref_n) +{ +return in_range (ref_p,ADS1262_RMUXP_REFP1 ,ADS1262_EXT_REF_COUNT ) && +in_range (ref_n,ADS1262_RMUXN_REFN1 ,ADS1262_EXT_REF_COUNT ); +} +static int ads1262_dev_send_cmd (struct ads1262 *st,u8 opcode) {guard (mutex)(&st->xfer_lock);
@@ -447,17 +511,33 @@ static int ads1262_channel_enable(struct ads1262 *st,guard (mutex)(&st->xfer_lock);guard (mutex)(&st->chan_lock); - ret =regmap_update_bits (st->regmap,ADS1262_MODE2_REG , -ADS1262_MODE2_DR_MASK , -FIELD_PREP (ADS1262_MODE2_DR_MASK , chan->data_rate)); + ret =regmap_update_bits (st->regmap,ADS1262_MODE0_REG , +ADS1262_MODE0_REFREV_MASK , +FIELD_PREP (ADS1262_MODE0_REFREV_MASK , chan->ref_reversal));if (ret)return ret; -return regmap_update_bits (st->regmap,ADS1262_INPMUX_REG , -ADS1262_INPMUX_MUXN_MASK | -ADS1262_INPMUX_MUXP_MASK , -FIELD_PREP (ADS1262_INPMUX_MUXN_MASK , spec->channel2) | -FIELD_PREP (ADS1262_INPMUX_MUXP_MASK , spec->channel)); + ret =regmap_update_bits (st->regmap,ADS1262_MODE2_REG , +ADS1262_MODE2_DR_MASK | +ADS1262_MODE2_GAIN_MASK , +FIELD_PREP (ADS1262_MODE2_DR_MASK , chan->data_rate) | +FIELD_PREP (ADS1262_MODE2_GAIN_MASK , chan->gain)); +if (ret) +return ret; + + ret =regmap_update_bits (st->regmap,ADS1262_INPMUX_REG , +ADS1262_INPMUX_MUXN_MASK | +ADS1262_INPMUX_MUXP_MASK , +FIELD_PREP (ADS1262_INPMUX_MUXN_MASK , spec->channel2) | +FIELD_PREP (ADS1262_INPMUX_MUXP_MASK , spec->channel)); +if (ret) +return ret; + +return regmap_update_bits (st->regmap,ADS1262_REFMUX_REG , +ADS1262_REFMUX_RMUXN_MASK | +ADS1262_REFMUX_RMUXP_MASK , +FIELD_PREP (ADS1262_REFMUX_RMUXN_MASK , chan->ref_n) | +FIELD_PREP (ADS1262_REFMUX_RMUXP_MASK , chan->ref_p)); }static int ads1262_set_runmode (struct ads1262 *st,u8 runmode)
@@ -506,6 +586,7 @@ static int ads1262_read_raw(struct iio_dev *indio_dev, {struct ads1262 *st =iio_priv (indio_dev);struct ads1262_channel *chan_data = &st->channels[chan->scan_index]; +u64 scale, offset;__be32 raw;int ret;
@@ -518,6 +599,34 @@ static int ads1262_read_raw(struct iio_dev *indio_dev,return IIO_VAL_INT ; +case IIO_CHAN_INFO_SCALE : { +guard (mutex)(&st->chan_lock); + + *val = chan_data->scales[chan_data->gain][0 ]; + *val2 = chan_data->scales[chan_data->gain][1 ]; + +return IIO_VAL_DECIMAL64_PICO ; + } + +case IIO_CHAN_INFO_OFFSET : { +guard (mutex)(&st->chan_lock); + + scale =iio_val_s64_compose (chan_data->scales[chan_data->gain][0 ], + chan_data->scales[chan_data->gain][1 ]); + +switch (chan->type) { +case IIO_TEMP : + offset = -mul_u64_u64_div_u64 (ADS1262_TEMP_ZERO_C , +PICO *MILLIDEGREE_PER_DEGREE , + scale * ADS1262_TEMP_SLOPE_uV_C); +iio_val_s64_decompose (offset, val, val2); + +return IIO_VAL_INT_64 ; +default : +return -EOPNOTSUPP ; + } + } +case IIO_CHAN_INFO_SAMP_FREQ : {guard (mutex)(&st->chan_lock);
@@ -540,6 +649,12 @@ static int ads1262_read_avail(struct iio_dev *indio_dev,struct ads1262_channel *chan_data = &st->channels[chan->scan_index];switch (mask) { +case IIO_CHAN_INFO_SCALE : + *type =IIO_VAL_DECIMAL64_PICO ; + *vals = (const int *)chan_data->scales; + *length = chan_data->num_scales *2 ; +return IIO_AVAIL_LIST ; +case IIO_CHAN_INFO_SAMP_FREQ : *type =IIO_VAL_INT_PLUS_MICRO ;
@@ -572,6 +687,16 @@ static int ads1262_write_raw(struct iio_dev *indio_dev,guard (mutex)(&st->chan_lock);switch (mask) { +case IIO_CHAN_INFO_SCALE : + ret =ads1262_find_two (chan_data->scales, chan_data->num_scales, + val, val2); +if (ret <0 ) +return ret; + + chan_data->gain = ret; + +return 0 ; +case IIO_CHAN_INFO_SAMP_FREQ :switch (chan_data->filter) {case ADS1262_FILTER_FIR :
@@ -613,6 +738,17 @@ static int ads1262_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int rereturn regmap_write (st->regmap, reg, writeval); } +static int ads1262_write_raw_get_fmt (struct iio_dev *indio_dev, +struct iio_chan_spec const *chan,long mask) +{ +switch (mask) { +case IIO_CHAN_INFO_SCALE : +return IIO_VAL_DECIMAL64_PICO ; +default : +return IIO_VAL_INT_PLUS_MICRO ; + } +} +static int ads1262_fwnode_xlate (struct iio_dev *indio_dev,const struct fwnode_reference_args *iiospec) {
@@ -636,6 +772,7 @@ static const struct iio_info ads1262_iio_info = { .read_raw = ads1262_read_raw, .read_avail = ads1262_read_avail, .write_raw = ads1262_write_raw, + .write_raw_get_fmt = ads1262_write_raw_get_fmt, .debugfs_reg_access = ads1262_debugfs_reg_access, .fwnode_xlate = ads1262_fwnode_xlate, };
@@ -876,20 +1013,241 @@ static void ads1262_populate_samp_freqs(struct ads1262 *st) } } +static void ads1262_calculate_scales (int (*scales)[2 ],size_t num_scales, +u32 full_scale,u64 mult, +u32 resolution) +{ +unsigned int i; +s64 val; + +/* +* Each scale in the table corresponds to a PGA gain configuration, +* which are given in powers of 2. +*/ +for (i =0 ; i < num_scales; i++) { + val =mul_u64_u64_shr (full_scale, mult, resolution -1 + i); +iio_val_s64_decompose (val, &scales[i][0 ], &scales[i][1 ]); + } +} + +static int ads1262_populate_scales_resistance (struct ads1262 *st, +const struct iio_chan_spec *spec) +{ +struct ads1262_channel *chan = &st->channels[spec->scan_index]; +u32 full_scale; + +if (!ads1262_reference_is_external (chan->ref_p, chan->ref_n)) +return -EINVAL ; + + full_scale = st->rref_ohms[chan->ref_p -1 ][chan->ref_n -1 ]; + + chan->num_scales =ARRAY_SIZE (chan->scales); + +ads1262_calculate_scales (chan->scales, chan->num_scales, full_scale, +PICO ,ADS1262_ADC1_RESOLUTION ); + +return 0 ; +} + +static int ads1262_populate_scales_temp (struct ads1262 *st, +const struct iio_chan_spec *spec) +{ +struct ads1262_channel *chan = &st->channels[spec->scan_index]; +u32 full_scale; +u64 mult; + + full_scale = ADS1262_INTERNAL_REFERENCE_uV; + mult =PICO *MILLIDEGREE_PER_DEGREE / ADS1262_TEMP_SLOPE_uV_C; + + chan->num_scales =1 ; + +ads1262_calculate_scales (chan->scales, chan->num_scales, full_scale, + mult,ADS1262_ADC1_RESOLUTION ); + +return 0 ; +} + +static int ads1262_populate_scales_voltage (struct ads1262 *st, +const struct iio_chan_spec *spec) +{ +struct device *dev = &st->spi->dev; +struct ads1262_channel *chan = &st->channels[spec->scan_index]; +int refp_uV, refn_uV; +u32 full_scale_uV; +u64 mult; + +switch (chan->ref_p) { +case ADS1262_RMUXP_INTERNAL : +if (chan->ref_n ==ADS1262_RMUXN_INTERNAL ) { + refp_uV = ADS1262_INTERNAL_REFERENCE_uV; +break ; + } + +/* The negative internal reference is AVSS */ + refp_uV = st->avss_uV + ADS1262_INTERNAL_REFERENCE_uV; +break ; + case ADS1262_RMUXP_REFP1 ... ADS1262_RMUXP_REFP3: + refp_uV = st->refp_uV[chan->ref_p -1 ]; +break ; +case ADS1262_RMUXP_AVDD : +if (!st->avdd_uV) +return dev_err_probe (dev, -EINVAL , +"channel@%lu: avdd voltage unknown\n" , + spec->address); + refp_uV = st->avdd_uV; +break ; +default : +return -EINVAL ; + } + +switch (chan->ref_n) { +case ADS1262_RMUXN_INTERNAL : +if (chan->ref_p ==ADS1262_RMUXP_INTERNAL ) { + refn_uV =0 ; +break ; + } + fallthrough; +case ADS1262_RMUXN_AVSS : + refn_uV = st->avss_uV; +break ; + case ADS1262_RMUXN_REFN1 ... ADS1262_RMUXN_REFN3: + refn_uV = st->refn_uV[chan->ref_n -1 ]; +break ; +default : +return -EINVAL ; + } + + full_scale_uV =abs (refp_uV - refn_uV); +if (full_scale_uV <900000 ) +return dev_err_probe (dev, -EINVAL ,"channel@%lu: reference voltage below 0.9V\n" , + spec->address); + +if (spec->channel >=ADS1262_INPMUX_AVDD && + spec->channel <=ADS1262_INPMUX_DVDD ) { +/* The power supply monitors are scaled down by a factor of 4 */ + mult =4 ; + chan->num_scales =1 ; + }else { + mult =1 ; + chan->num_scales =ARRAY_SIZE (chan->scales); + } + +ads1262_calculate_scales (chan->scales, chan->num_scales, full_scale_uV, +NANO * mult,ADS1262_ADC1_RESOLUTION ); + +return 0 ; +} + +static int ads1262_parse_references (struct ads1262 *st) +{ +struct device *dev = &st->spi->dev; +unsigned int i, j; +char name[sizeof ("ti,refpN-refnM-resistor-ohms" )]; +u32 ohms; +int ret; + +for (i =ADS1262_RMUXP_REFP1 ; i <=ADS1262_RMUXP_REFP3 ; i++) { +scnprintf (name,sizeof (name),"refp%u" , i); + ret =devm_regulator_get_enable_read_voltage (dev, name); +if (ret <0 && ret != -ENODEV ) +return dev_err_probe (dev, ret,"failed to read reference voltage: %s\n" , + name); + + st->refp_uV[i -1 ] = ret == -ENODEV ?0 : ret; + } + +for (i =ADS1262_RMUXN_REFN1 ; i <=ADS1262_RMUXN_REFN3 ; i++) { +scnprintf (name,sizeof (name),"refn%u" , i); + ret =devm_regulator_get_enable_read_voltage (dev, name); +if (ret <0 && ret != -ENODEV ) +return dev_err_probe (dev, ret,"failed to read reference voltage: %s\n" , + name); + +/* +* REVISIT: Currently the regulator subsystem doesn't support +* reading negative voltages. If we have a bipolar supply +* configuration (AVSS < 0), then we are forced to assume that +* negative references are either 0V (no regulator) or below +* ground magnitudes. +*/ +if (st->bipolar_supply) + st->refn_uV[i -1 ] = ret == -ENODEV ?0 : -ret; +else + st->refn_uV[i -1 ] = ret == -ENODEV ?0 : ret; + } + +for (i =ADS1262_RMUXP_REFP1 ; i <=ADS1262_RMUXP_REFP3 ; i++) { +for (j =ADS1262_RMUXN_REFN1 ; j <=ADS1262_RMUXN_REFN3 ; j++) { +scnprintf (name,sizeof (name), +"ti,refp%u-refn%u-resistor-ohms" , i, j); + +if (!device_property_present (dev, name)) +continue ; + + ret =device_property_read_u32 (dev, name, &ohms); +if (ret) +return dev_err_probe (dev, ret, +"failed to read reference resistor: %s\n" , + name); +if (!ohms) +return dev_err_probe (dev, -EINVAL , +"reference resistor can't be 0 ohms: %s\n" , + name); + + st->rref_ohms[i -1 ][j -1 ] = ohms; + } + } + +return 0 ; +} +static int ads1262_populate_tables (struct iio_dev *indio_dev) {struct ads1262 *st =iio_priv (indio_dev); +const struct iio_chan_spec *spec; +int ret;ads1262_populate_samp_freqs (st); + ret =ads1262_parse_references (st); +if (ret) +return ret; + +for (unsigned int i =0 ; i < st->num_channels; i++) { + spec = &indio_dev->channels[i]; + +switch (spec->type) { +case IIO_VOLTAGE : + ret =ads1262_populate_scales_voltage (st, spec); +if (ret) +return ret; +break ; +case IIO_TEMP : + ret =ads1262_populate_scales_temp (st, spec); +if (ret) +return ret; +break ; +case IIO_RESISTANCE : + ret =ads1262_populate_scales_resistance (st, spec); +if (ret) +return ret; +break ; +default : +return -EOPNOTSUPP ; + } + } +return 0 ; }static int ads1262_parse_channel_node (struct ads1262 *st,struct iio_chan_spec *spec, +struct ads1262_channel *chan,struct fwnode_handle *node) {struct device *dev = &st->spi->dev; +const char *sources[2 ]; +char name[sizeof ("ti,refpN-refnM-resistor-ohms" )];u32 pins[2 ];int ret;
@@ -928,6 +1286,38 @@ static int ads1262_parse_channel_node(struct ads1262 *st, spec->channel = pins[0 ]; spec->channel2 = pins[1 ]; +if (fwnode_property_present (node,"reference-sources" )) { + ret =fwnode_property_read_string_array (node,"reference-sources" , + sources,ARRAY_SIZE (sources)); +if (ret <0 ) +return dev_err_probe (dev, ret,"%pfwP: failed to read reference-sources\n" , + node); +if (ret <2 ) +return dev_err_probe (dev, -EINVAL ,"%pfwP: missing reference-sources\n" , + node); + + ret =match_string (ads1262_ref_sources_pos,-1 , sources[0 ]); +if (ret <0 ) +return dev_err_probe (dev, ret,"%pfwP: invalid positive reference source\n" , + node); + chan->ref_p = ret; + + ret =match_string (ads1262_ref_sources_neg,-1 , sources[1 ]); +if (ret <0 ) +return dev_err_probe (dev, ret,"%pfwP: invalid negative reference source\n" , + node); + chan->ref_n = ret; + +if (ads1262_reference_is_external (chan->ref_p, chan->ref_n)) { +scnprintf (name,sizeof (name),"ti,refp%u-refn%u-resistor-ohms" , + chan->ref_p, chan->ref_n); +if (device_property_present (dev, name)) + chan->is_resistance = true; + } + } + + chan->ref_reversal =fwnode_property_read_bool (node,"ti,reference-reversal" ); +return 0 ; }
@@ -964,7 +1354,7 @@ static int ads1262_parse_channels(struct iio_dev *indio_dev) *chan = ads1262_default_channel; - ret =ads1262_parse_channel_node (st, spec, node); + ret =ads1262_parse_channel_node (st, spec, chan, node);if (ret)return ret;
@@ -973,7 +1363,7 @@ static int ads1262_parse_channels(struct iio_dev *indio_dev)return dev_err_probe (dev, ret,"%pfwP: failed to read reg\n" , node); - spec->type =IIO_VOLTAGE ; + spec->type = chan->is_resistance ?IIO_RESISTANCE :IIO_VOLTAGE ; spec->indexed = true; spec->scan_index = i; spec->address = reg;
@@ -984,8 +1374,10 @@ static int ads1262_parse_channels(struct iio_dev *indio_dev) .endianness = IIO_BE, }; spec->info_mask_separate =BIT (IIO_CHAN_INFO_RAW ) | -BIT (IIO_CHAN_INFO_SAMP_FREQ ); - spec->info_mask_separate_available =BIT (IIO_CHAN_INFO_SAMP_FREQ ); +BIT (IIO_CHAN_INFO_SAMP_FREQ ) | +BIT (IIO_CHAN_INFO_SCALE ); + spec->info_mask_separate_available =BIT (IIO_CHAN_INFO_SAMP_FREQ ) | +BIT (IIO_CHAN_INFO_SCALE ); i++; }
@@ -1008,22 +1400,59 @@ static int ads1262_parse_channels(struct iio_dev *indio_dev)return 0 ; } +static void ads1262_regulator_disable (void *data) +{ +struct regulator *supply = data; + +regulator_disable (supply); +} +static int ads1262_supplies_setup (struct ads1262 *st) {struct device *dev = &st->spi->dev; +struct regulator *avdd;int ret; ret =devm_regulator_get_enable (dev,"dvdd" );if (ret)return dev_err_probe (dev, ret,"failed to get dvdd regulator\n" ); - ret =devm_regulator_get_enable (dev,"avdd" ); -if (ret <0 ) + avdd =devm_regulator_get (dev,"avdd" ); + ret =PTR_ERR_OR_ZERO (avdd); +if (ret)return dev_err_probe (dev, ret,"failed to get avdd regulator\n" ); - ret =devm_regulator_get_enable_optional (dev,"avss" ); + ret =regulator_enable (avdd); +if (ret) +return ret; + + ret =devm_add_action_or_reset (dev, ads1262_regulator_disable, avdd); +if (ret) +return ret; + + ret =regulator_get_voltage (avdd); +if (ret >0 ) + st->avdd_uV = ret; + +/* +* REVISIT: The AVSS supply has a minimum of -2.5V and maximum of 0V. +* Currently the regulator subsystem doesn't support negative voltages, +* so we assume the value returned here is actually the magnitude +* (absolute value). +* +* This limitation forces us to assume that, if we have a bipolar supply +* (AVSS < 0V), all negative references are below ground (REFN <= 0V) +* and positive references are above ground (REFP >= 0V), as this is the +* most common configuration. +*/ + ret =devm_regulator_get_enable_read_voltage (dev,"avss" );if (ret <0 && ret != -ENODEV ) -return dev_err_probe (dev, ret,"failed to get avss regulator\n" ); +return dev_err_probe (dev, ret,"failed to read AVSS voltage\n" ); + +if (ret >0 ) { + st->avss_uV = -ret; + st->bipolar_supply = true; + }fsleep (ADS1262_POWER_TRANS_US );
Signature · 1 line
2.55.0
PATCH v5 6/10 iio: adc: ti-ads1262: support input chopping
In reply to Kurt BorjaEnable per-channel input chopping via the "input-chopping" property. As the device withholds the first conversion in chop mode, single reads are taken in CONTINUOUS run mode with a brief start/stop pulse, as recommended by the datasheet (Section 9.4.1.2).
--- drivers/iio/adc/ti-ads1262.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-)
drivers/iio/adc/ti-ads1262.c +21 -4
@@ -205,6 +205,7 @@ struct ads1262_channel {u8 ref_n;bool ref_reversal;bool is_resistance; +bool input_chop;size_t num_scales;int scales[ADS1262_GAIN_COUNT ][2 ]; };
@@ -456,7 +457,7 @@ static int ads1262_dev_stop(struct ads1262 *st)return ret; } -static int ads1262_dev_start_one (struct ads1262 *st) +static int ads1262_dev_start_one (struct ads1262 *st,u8 runmode) {int ret;
@@ -464,7 +465,7 @@ static int ads1262_dev_start_one(struct ads1262 *st)if (ret)return ret; -if (st->start_gpiod) { +if (runmode ==ADS1262_RUNMODE_CONTINUOUS || st->start_gpiod) {/* * The START pulse timing requirement is 4 clock cycles, at the * minimum clock rate this is 4 microseconds.
@@ -512,7 +513,9 @@ static int ads1262_channel_enable(struct ads1262 *st,guard (mutex)(&st->chan_lock); ret =regmap_update_bits (st->regmap,ADS1262_MODE0_REG , +ADS1262_MODE0_INPUT_CHOP_MASK |ADS1262_MODE0_REFREV_MASK , +FIELD_PREP (ADS1262_MODE0_INPUT_CHOP_MASK , chan->input_chop) |FIELD_PREP (ADS1262_MODE0_REFREV_MASK , chan->ref_reversal));if (ret)return ret;
@@ -553,13 +556,26 @@ static int ads1262_channel_read(struct iio_dev *indio_dev, const struct iio_chan_spec *spec, __be32 *val) { struct ads1262 *st = iio_priv(indio_dev); +struct ads1262_channel *chan = &st->channels[spec->scan_index]; +u8 runmode;int ret;IIO_DEV_ACQUIRE_DIRECT_MODE (indio_dev, claim);if (IIO_DEV_ACQUIRE_FAILED (claim))return -EBUSY ; - ret =ads1262_set_runmode (st,ADS1262_RUNMODE_PULSE ); +/* +* When a channel has chop mode or IDAC rotation mode, the first +* conversion is always withheld so the datasheet suggests using the +* CONTINUOUS mode and briefly starting and stopping conversions to +* achieve the same effect (Section 9.4.1.2). +*/ +if (chan->input_chop) + runmode =ADS1262_RUNMODE_CONTINUOUS ; +else + runmode =ADS1262_RUNMODE_PULSE ; + + ret =ads1262_set_runmode (st, runmode);if (ret)return ret;
@@ -569,7 +585,7 @@ static int ads1262_channel_read(struct iio_dev *indio_dev,reinit_completion (&st->drdy); - ret =ads1262_dev_start_one (st); + ret =ads1262_dev_start_one (st, runmode);if (ret)return ret;
@@ -1316,6 +1332,7 @@ static int ads1262_parse_channel_node(struct ads1262 *st, } } + chan->input_chop =fwnode_property_read_bool (node,"input-chopping" ); chan->ref_reversal =fwnode_property_read_bool (node,"ti,reference-reversal" );return 0 ;
Signature · 1 line
2.55.0
PATCH v5 7/10 iio: adc: ti-ads1262: support excitation currents
In reply to Kurt BorjaSupport the two IDAC excitation current sources. Each channel can route its IDAC1/IDAC2 outputs to a pin via the "excitation-channels" property and select a magnitude via "excitation-current-nanoamp".
--- drivers/iio/adc/ti-ads1262.c | 90 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 3 deletions(-)
drivers/iio/adc/ti-ads1262.c +87 -3
@@ -200,12 +200,15 @@ struct ads1262_chip_info {struct ads1262_channel {u8 data_rate;u8 filter; +u8 idac_mux[2 ]; +u8 idac_mag[2 ];u8 gain;u8 ref_p;u8 ref_n;bool ref_reversal;bool is_resistance;bool input_chop; +bool idac_chop;size_t num_scales;int scales[ADS1262_GAIN_COUNT ][2 ]; };
@@ -307,6 +310,10 @@ static const struct iio_chan_spec ads1262_monitor_chan_specs[] = {#define ads1262_default_channel ((struct ads1262_channel) { \ .data_rate = ADS1262_DR_20_SPS, \ .filter = ADS1262_FILTER_FIR, \ + .idac_mux = { \ + ADS1262_IDACMUX_NO_CONN, \ + ADS1262_IDACMUX_NO_CONN \ + }, \ })static const u32 ads1262_mod_decimator_a[] = {
@@ -365,6 +372,21 @@ static const char * const ads1262_ref_sources_neg[] = { NULL }; +static const u32 ads1262_idac_mags_nA[] = { +0 ,50000 ,100000 ,250000 ,500000 ,750000 ,1000000 ,1500000 ,/* 0..7 */ +2000000 ,2500000 ,3000000 /* 8..10 */ +}; + +static int ads1262_find_one (const u32 *array,size_t num_elements,u32 val) +{ +for (unsigned int i =0 ; i < num_elements; i++) { +if (val == array[i]) +return i; + } + +return -EINVAL ; +} +static int ads1262_find_two (const int (*array)[2 ],size_t num_elements,int val,int val2) {
@@ -514,8 +536,10 @@ static int ads1262_channel_enable(struct ads1262 *st, ret =regmap_update_bits (st->regmap,ADS1262_MODE0_REG ,ADS1262_MODE0_INPUT_CHOP_MASK | +ADS1262_MODE0_IDAC_CHOP_MASK |ADS1262_MODE0_REFREV_MASK ,FIELD_PREP (ADS1262_MODE0_INPUT_CHOP_MASK , chan->input_chop) | +FIELD_PREP (ADS1262_MODE0_IDAC_CHOP_MASK , chan->idac_chop) |FIELD_PREP (ADS1262_MODE0_REFREV_MASK , chan->ref_reversal));if (ret)return ret;
@@ -536,6 +560,22 @@ static int ads1262_channel_enable(struct ads1262 *st,if (ret)return ret; + ret =regmap_update_bits (st->regmap,ADS1262_IDACMUX_REG , +ADS1262_IDACMUX_MUX1_MASK | +ADS1262_IDACMUX_MUX2_MASK , +FIELD_PREP (ADS1262_IDACMUX_MUX1_MASK , chan->idac_mux[0 ]) | +FIELD_PREP (ADS1262_IDACMUX_MUX2_MASK , chan->idac_mux[1 ])); +if (ret) +return ret; + + ret =regmap_update_bits (st->regmap,ADS1262_IDACMAG_REG , +ADS1262_IDACMAG_MAG1_MASK | +ADS1262_IDACMAG_MAG2_MASK , +FIELD_PREP (ADS1262_IDACMAG_MAG1_MASK , chan->idac_mag[0 ]) | +FIELD_PREP (ADS1262_IDACMAG_MAG2_MASK , chan->idac_mag[1 ])); +if (ret) +return ret; + return regmap_update_bits(st->regmap, ADS1262_REFMUX_REG, ADS1262_REFMUX_RMUXN_MASK | ADS1262_REFMUX_RMUXP_MASK,
@@ -570,7 +610,7 @@ static int ads1262_channel_read(struct iio_dev *indio_dev,* CONTINUOUS mode and briefly starting and stopping conversions to * achieve the same effect (Section 9.4.1.2). */ -if (chan->input_chop) +if (chan->input_chop || chan->idac_chop) runmode =ADS1262_RUNMODE_CONTINUOUS ;else runmode =ADS1262_RUNMODE_PULSE ;
@@ -1264,8 +1304,8 @@ static int ads1262_parse_channel_node(struct ads1262 *st,struct device *dev = &st->spi->dev;const char *sources[2 ];char name[sizeof ("ti,refpN-refnM-resistor-ohms" )]; -u32 pins[2 ]; -int ret; +u32 pins[2 ], mags[2 ]; +int count, ret;if (fwnode_property_present (node,"single-channel" )) { ret =fwnode_property_read_u32 (node,"single-channel" , &pins[0 ]);
@@ -1332,7 +1372,51 @@ static int ads1262_parse_channel_node(struct ads1262 *st, } } +if (fwnode_property_present (node,"excitation-channels" )) { + count =fwnode_property_count_u32 (node,"excitation-channels" ); +if (count <0 ) +return dev_err_probe (dev, count, +"%pfwP: failed to count excitation-channels\n" , node); + + pins[0 ] =ADS1262_IDACMUX_NO_CONN ; + pins[1 ] =ADS1262_IDACMUX_NO_CONN ; + ret =fwnode_property_read_u32_array (node,"excitation-channels" , + pins,min (count,ARRAY_SIZE (pins))); +if (ret) +return dev_err_probe (dev, ret, +"%pfwP: failed to read excitation-channels\n" , node); +if (pins[0 ] >ADS1262_IDACMUX_NO_CONN || pins[1 ] >ADS1262_IDACMUX_NO_CONN ) +return dev_err_probe (dev, -EINVAL , +"%pfwP: excitation-channels not in range\n" , node); + chan->idac_mux[0 ] = pins[0 ]; + chan->idac_mux[1 ] = pins[1 ]; + + mags[0 ] =0 ; + mags[1 ] =0 ; + ret =fwnode_property_read_u32_array (node,"excitation-current-nanoamp" , + mags,min (count,ARRAY_SIZE (mags))); +if (ret) +return dev_err_probe (dev, ret, +"%pfwP: failed to read excitation-current-nanoamp\n" , + node); + + ret =ads1262_find_one (ads1262_idac_mags_nA, +ARRAY_SIZE (ads1262_idac_mags_nA), mags[0 ]); +if (ret <0 ) +return dev_err_probe (dev, ret, +"%pfwP: invalid excitation-current-nanoamp\n" , node); + chan->idac_mag[0 ] = ret; + + ret =ads1262_find_one (ads1262_idac_mags_nA, +ARRAY_SIZE (ads1262_idac_mags_nA), mags[1 ]); +if (ret <0 ) +return dev_err_probe (dev, ret, +"%pfwP: invalid excitation-current-nanoamp\n" , node); + chan->idac_mag[1 ] = ret; + } + chan->input_chop =fwnode_property_read_bool (node,"input-chopping" ); + chan->idac_chop =fwnode_property_read_bool (node,"excitation-current-chopping" ); chan->ref_reversal =fwnode_property_read_bool (node,"ti,reference-reversal" );return 0 ;
Signature · 1 line
2.55.0
[email protected]
AI reviewRe: [PATCH v5 07/10] iio: adc: ti-ads1262: support excitation currents
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] iio: adc: ti-ads1262: Build failure in min() macro due to mixed signedness --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] iio: adc: ti-ads1262: Build failure in min() macro due to mixed signedness --
commit f745e114f602a4dc9a511ee58a8c5092e66748e8
Author: Kurt Borja <[email protected]>
iio: adc: ti-ads1262: support excitation currents
This commit adds support for the two IDAC excitation current sources on the TI ADS1262 ADC. It allows routing IDAC outputs to pins and configuring their magnitudes via device tree properties.
Quoted 4 lines
drivers/iio/adc/ti-ads1262.c +0
-0
[ ... ]
Quoted 1 line
drivers/iio/adc/ti-ads1262.c +0
-0@@ -1332,7 +1372,51 @@ static int ads1262_parse_channel_node(struct ads1262 *st,
[ ... ]
drivers/iio/adc/ti-ads1262.c static int ads1262_parse_channel_node(struct ads1262 *st, · line 1384
Quoted 10 lines
drivers/iio/adc/ti-ads1262.c +10
-0+if (fwnode_property_present (node,"excitation-channels" )) { + count =fwnode_property_count_u32 (node,"excitation-channels" ); +if (count <0 ) +return dev_err_probe (dev, count, +"%pfwP: failed to count excitation-channels\n" , node); + + pins[0 ] =ADS1262_IDACMUX_NO_CONN ; + pins[1 ] =ADS1262_IDACMUX_NO_CONN ; + ret =fwnode_property_read_u32_array (node,"excitation-channels" , + pins,min (count,ARRAY_SIZE (pins)));
[Severity: Low]
Does this introduce a compile-time regression due to strict signed/unsigned type checking in the min() macro?
The fwnode_property_count_u32() function returns a signed int which is stored in the count variable, while ARRAY_SIZE() evaluates to an unsigned size_t. The kernel's min() macro enforces that both arguments have compatible signedness, which triggers a build-breaking assertion here.
Could min_t(size_t, count, ARRAY_SIZE(pins)) or similar be used here instead?
Quoted 3 lines
drivers/iio/adc/ti-ads1262.c +3
-0+if (ret) +return dev_err_probe (dev, ret, +"%pfwP: failed to read excitation-channels\n" , node);
[ ... ]
drivers/iio/adc/ti-ads1262.c static int ads1262_parse_channel_node(struct ads1262 *st, · line 1397
Quoted 4 lines
drivers/iio/adc/ti-ads1262.c +4
-0+ mags[0 ] =0 ; + mags[1 ] =0 ; + ret =fwnode_property_read_u32_array (node,"excitation-current-nanoamp" , + mags,min (count,ARRAY_SIZE (mags)));
[Severity: Low]
Does this also trigger the same min() macro signedness build regression as above, since count is signed and ARRAY_SIZE(mags) is unsigned?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=7
PATCH v5 8/10 iio: adc: ti-ads1262: support triggered buffer sampling
In reply to Kurt BorjaAdd triggered buffer support and a data-ready (DRDY) hardware trigger.
--- drivers/iio/adc/Kconfig | 2 + drivers/iio/adc/ti-ads1262.c | 299 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 301 insertions(+)
drivers/iio/adc/Kconfig +2 -0
@@ -1904,6 +1904,8 @@ config TI_ADS1262 tristate "Texas Instruments ADS1262" depends on SPI select REGMAP + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help If you say yes here you get support for Texas Instruments ADS1262 and ADS1263 ADC chips.
drivers/iio/adc/ti-ads1262.c +299 -0
351 lines not on this page · raw message
Signature · 1 line
2.55.0
[email protected]
AI reviewRe: [PATCH v5 08/10] iio: adc: ti-ads1262: support triggered buffer sampling
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [High] spi: DMA API violation due to cacheline sharing between tx and rx buffers - [High] iio…
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] spi: DMA API violation due to cacheline sharing between tx and rx buffers
- [High] iio: Out-of-bounds array access on soft timestamp channel in trigger handlers --
commit 498de2a7a709d846f73f3af6fbc4fac307b5368e
Author: Kurt Borja <[email protected]>
iio: adc: ti-ads1262: support triggered buffer sampling
Add triggered buffer support and a data-ready (DRDY) hardware trigger.
Quoted 4 lines
drivers/iio/adc/ti-ads1262.c +0
-0
[ ... ]
drivers/iio/adc/ti-ads1262.c struct ads1262 { · line 247
Quoted 9 lines
drivers/iio/adc/ti-ads1262.c +5
-0@@ -233,6 +240,11 @@ struct ads1262 {u32 rref_ohms[ADS1262_EXT_REF_COUNT ][ADS1262_EXT_REF_COUNT ];int refp_uV[ADS1262_EXT_REF_COUNT ];int refn_uV[ADS1262_EXT_REF_COUNT ]; +IIO_DECLARE_BUFFER_WITH_TS (__be32, scan_buffer, +ADS1262_FW_CHANNEL_COUNT + +ADS1262_MON_CHANNEL_COUNT ); +u8 tx[ADS1262_SPI_XFER_SZ ]__aligned(IIO_DMA_MINALIGN) ; +u8 rx[ADS1262_SPI_XFER_SZ ];
^^^^^^^^^^^^^^^^^^^^^^^^^^
[Severity: High]
Does this code risk a DMA API violation on non-coherent architectures?
The compiler places rx immediately after tx at offset 13. While tx has the __aligned(IIO_DMA_MINALIGN) attribute, its size (13 bytes) is not padded to the cacheline boundary, causing rx to share the same cacheline as tx.
During SPI transfers, both buffers are mapped for DMA concurrently. Unmapping rx (which invalidates the CPU cache for that line) can silently destroy adjacent CPU writes or unflushed data in tx, leading to data corruption and triggering CONFIG_DMA_API_DEBUG warnings.
Should rx also be explicitly marked with __aligned(IIO_DMA_MINALIGN)?
[ ... ]
Quoted 4 lines
drivers/iio/adc/ti-ads1262.c +4
-0+static void ads1262_channel_prep_tx (struct ads1262 *st, +const struct iio_chan_spec *spec) +{ +struct ads1262_channel *chan = &st->channels[spec->scan_index];
[ ... ]
Quoted 10 lines
drivers/iio/adc/ti-ads1262.c +10
-0+static int ads1262_fill_buffer_mult (struct iio_dev *indio_dev) +{ +struct ads1262 *st =iio_priv (indio_dev); +unsigned int chan; +int i =-1 ; +int ret; + +/* +* This routine enables and reads channels in a full-duplex fashion. +*
drivers/iio/adc/ti-ads1262.c static const struct iio_info ads1262_iio_info = { · line 1037
Quoted 10 lines
drivers/iio/adc/ti-ads1262.c +10
-0+* When a channel is enabled, the previous conversion is clocked out of +* the shift data register on the same transfer (Section 9.4.7.1). This +* allows for low latency software sequencing but forbids any +* communication with the chip in-between or data corruption may occur, +* hence the need to take the xfer_lock for the whole operation. +*/ +guard (mutex)(&st->xfer_lock); + +iio_for_each_active_channel (indio_dev, chan) { +ads1262_channel_prep_tx (st, &indio_dev->channels[chan]);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[Severity: High]
Can this code read out-of-bounds when chan is the soft timestamp channel?
When triggered buffer support is set up, the IIO core automatically appends a software timestamp channel to the indio_dev->channels array and reserves a bit for it in the active scan mask.
The loop over iio_for_each_active_channel() does not skip the IIO_TIMESTAMP channel. When ads1262_channel_prep_tx() is called for the timestamp channel, it accesses st->channels[spec->scan_index]. Because the timestamp channel's scan_index is numerically greater than the hardware channels, it indexes out-of-bounds of the driver's allocated st->channels array.
The driver then sends configuration data read from this out-of-bounds memory to the ADC and blocks on wait_for_completion_interruptible_timeout(). Since the ADC was improperly configured, the expected DRDY interrupt may never arrive, causing a timeout.
Does this same issue exist in ads1262_buffer_postenable_one() if only the timestamp channel is enabled?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=8
PATCH v5 9/10 iio: adc: ti-ads1262: support REFOUT and VBIAS regulators
In reply to Kurt BorjaRegister the "refout" and "vbias" regulators to be able to use them as common mode supplies.
--- drivers/iio/adc/Kconfig | 1 + drivers/iio/adc/ti-ads1262.c | 120 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+)
drivers/iio/adc/Kconfig +1 -0
@@ -1904,6 +1904,7 @@ config TI_ADS1262 tristate "Texas Instruments ADS1262" depends on SPI select REGMAP + select REGULATOR select IIO_BUFFER select IIO_TRIGGERED_BUFFER help
drivers/iio/adc/ti-ads1262.c +120 -0
138 lines not on this page · raw message
Signature · 1 line
2.55.0
PATCH v5 10/10 iio: adc: ti-ads1262: support common mode supplies
In reply to Kurt BorjaEnable common mode regulators. The usual configuration is to have our own 'vbias' regulator connected internally as common mode voltage on the AINCOM pin.
--- drivers/iio/adc/ti-ads1262.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+)
drivers/iio/adc/ti-ads1262.c +37 -0
@@ -241,6 +241,7 @@ struct ads1262 {u32 rref_ohms[ADS1262_EXT_REF_COUNT ][ADS1262_EXT_REF_COUNT ];int refp_uV[ADS1262_EXT_REF_COUNT ];int refn_uV[ADS1262_EXT_REF_COUNT ]; +int common_mode_uV[ADS1262_INPMUX_AINCOM +1 ];IIO_DECLARE_BUFFER_WITH_TS (__be32, scan_buffer,ADS1262_FW_CHANNEL_COUNT +ADS1262_MON_CHANNEL_COUNT );
@@ -690,6 +691,13 @@ static int ads1262_read_raw(struct iio_dev *indio_dev, scale * ADS1262_TEMP_SLOPE_uV_C);iio_val_s64_decompose (offset, val, val2); +return IIO_VAL_INT_64 ; + +case IIO_VOLTAGE : + offset =mul_u64_u64_div_u64 (st->common_mode_uV[chan->channel2], +NANO , scale); +iio_val_s64_decompose (offset, val, val2); +return IIO_VAL_INT_64 ;default :return -EOPNOTSUPP ;
@@ -1123,6 +1131,28 @@ static irqreturn_t ads1262_irq_handler(int irq, void *dev_id)return IRQ_HANDLED ; } +static int ads1262_parse_common_mode_supplies (struct ads1262 *st) +{ +struct device *dev = &st->spi->dev; +char name[sizeof ("aincom" )]; +int ret; + +for (unsigned int i =0 ; i <=ADS1262_INPMUX_AINCOM ; i++) { +if (i <ADS1262_INPMUX_AINCOM ) +scnprintf (name,sizeof (name),"ain%u" , i); +else +scnprintf (name,sizeof (name),"aincom" ); + + ret =devm_regulator_get_enable_read_voltage (dev, name); +if (ret <0 && ret != -ENODEV ) +return dev_err_probe (dev, ret, +"failed to get common mode supply: %s\n" , name); + st->common_mode_uV[i] = ret != -ENODEV ? ret :0 ; + } + +return 0 ; +} +static int ads1262_vbias_enable (struct regulator_dev *rdev) {struct ads1262 *st =rdev_get_drvdata (rdev);
@@ -1876,6 +1906,9 @@ static int ads1262_parse_channels(struct iio_dev *indio_dev) spec->info_mask_separate_available =BIT (IIO_CHAN_INFO_SAMP_FREQ ) |BIT (IIO_CHAN_INFO_SCALE ); +if (!chan->is_resistance && !spec->differential) + spec->info_mask_separate |=BIT (IIO_CHAN_INFO_OFFSET ); + i++; }
@@ -2027,6 +2060,10 @@ static int ads1262_spi_probe(struct spi_device *spi)if (ret)return ret; + ret =ads1262_parse_common_mode_supplies (st); +if (ret) +return ret; + ret =ads1262_populate_tables (indio_dev);if (ret)return ret;
Signature · 1 line
2.55.0
Jonathan Cameron
maintainerQuoted 7 lines · On Sat, 10 Oct 2026 03:52:39 -0300 Kurt Borja <[email protected]> wrote:
Hi all,
I apologize for the long delays between versions. This month has been very busy at university plus some personal stuff. I expect to have more time now, so development should be faster. Thanks for your patience!
Signed-off-by Kurt Borja <[email protected]>
I took another look through and it looks good. However, give the sheer scale of the driver I'll leave it a little longer before applying in case anyone else plans to give it another look. As normal, if anyone has it on their list but is snowed under just reply to the thread and I'll let it sit longer.
Thanks,
Jonathan
Jonathan Cameron
maintainerQuoted 9 lines · On Sun, 18 Oct 2026 02:31:29 +0100 Jonathan Cameron <[email protected]> wrote:
Quoted 6 lines · On Sat, 10 Oct 2026 03:52:39 -0300 Kurt Borja <[email protected]> wrote:
Hi all,
I apologize for the long delays between versions. This month has been very busy at university plus some personal stuff. I expect to have more time now, so development should be faster. Thanks for your patience!
Quoted 8 lines
Quoted 1 line
Signed-off-by Kurt Borja <[email protected]>I took another look through and it looks good. However, give the sheer scale of the driver I'll leave it a little longer before applying in case anyone else plans to give it another look. As normal, if anyone has it on their list but is snowed under just reply to the thread and I'll let it sit longer.
Long enough I think. Though given I'll be rebasing on rc1 any feedback people want to give is fine after this.
Applied to the testing branch of iio.git.
Thanks,
Jonathan
Quoted 5 lines
Thanks,
Jonathan
Quoted 14 lines · Jonathan Cameron
Quoted 11 lines · On Sun, 18 Oct 2026 02:31:29 +0100 Jonathan Cameron <[email protected]> wrote:
Quoted 7 lines · On Sat, 10 Oct 2026 03:52:39 -0300 Kurt Borja <[email protected]> wrote:
Hi all,
I apologize for the long delays between versions. This month has been very busy at university plus some personal stuff. I expect to have more time now, so development should be faster. Thanks for your patience!
Signed-off-by Kurt Borja <[email protected]>
Quoted 8 lines
Quoted 6 lines
I took another look through and it looks good. However, give the sheer scale of the driver I'll leave it a little longer before applying in case anyone else plans to give it another look. As normal, if anyone has it on their list but is snowed under just reply to the thread and I'll let it sit longer.
Long enough I think. Though given I'll be rebasing on rc1 any feedback people want to give is fine after this.
Thanks a lot, Jonathan, David and everyone that contributed :) I'll submit the follow up series starting next month.
Quoted 6 lines
Applied to the testing branch of iio.git.
Thanks,
Jonathan
Signature · 2 lines
Thanks, ~ Kurt