* [PATCH 2/5] iio: adc: Add ti-ads1262 driver
2026-06-12 22:46 [PATCH 0/5] iio: adc: Add TI ADS126X ADC family support Kurt Borja
2026-06-12 22:46 ` [PATCH 1/5] dt-bindings: iio: adc: Add TI ADS126x ADC family Kurt Borja
@ 2026-06-12 22:46 ` Kurt Borja
2026-06-12 22:46 ` [PATCH 3/5] iio: adc: ti-ads1262: Add GPIO controller support Kurt Borja
` (3 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Kurt Borja @ 2026-06-12 22:46 UTC (permalink / raw)
To: Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Linus Walleij, Bartosz Golaszewski
Cc: David Lechner, Nuno Sá, Andy Shevchenko, linux-iio,
devicetree, linux-kernel, linux-gpio, Kurt Borja,
Jonathan Cameron
Add ti-ads1262 driver for TI ADS1262 and ADS1263 ADCs with initial
support for the following features:
- Power management
- IIO direct and buffer modes
- Channel hot-reloading
- Internal or external oscillator
- Internal or external voltage reference
- Filter configuration
- Sensor bias configuration
- IDAC configuration
- Level-shift voltage configuration
- Auxiliary ADC interoperability considerations
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
MAINTAINERS | 1 +
drivers/iio/adc/Kconfig | 13 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/ti-ads1262.c | 1816 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1831 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 9379699d99c6..b874add5c924 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -26674,6 +26674,7 @@ L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/adc/ti,ads1262.yaml
F: Documentation/devicetree/bindings/iio/adc/ti,ads1263-adc2.yaml
+F: drivers/iio/adc/ti-ads1262.c
TI ADS7924 ADC DRIVER
M: Hugo Villeneuve <hvilleneuve@dimonoff.com>
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index a3a93a47b43d..b6c35d0c88ed 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -1796,6 +1796,19 @@ 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 && GPIOLIB
+ 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.
+
+ 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
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 707dd708912f..e0653820081e 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -154,6 +154,7 @@ obj-$(CONFIG_TI_ADS1018) += ti-ads1018.o
obj-$(CONFIG_TI_ADS1100) += ti-ads1100.o
obj-$(CONFIG_TI_ADS1119) += ti-ads1119.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
diff --git a/drivers/iio/adc/ti-ads1262.c b/drivers/iio/adc/ti-ads1262.c
new file mode 100644
index 000000000000..fd1911cf65ac
--- /dev/null
+++ b/drivers/iio/adc/ti-ads1262.c
@@ -0,0 +1,1816 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Texas Instruments ADS1262 ADC driver
+ *
+ * Copyright (C) 2025 Kurt Borja <kuurtb@gmail.com>
+ */
+
+#include <linux/array_size.h>
+#include <linux/align.h>
+#include <linux/bitfield.h>
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/compiler_attributes.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/lockdep.h>
+#include <linux/math.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/overflow.h>
+#include <linux/property.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <asm/byteorder.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+/* Commands */
+#define ADS1262_OPCODE_NOP 0x00
+#define ADS1262_OPCODE_RESET 0x06
+#define ADS1262_OPCODE_START1 0x08
+#define ADS1262_OPCODE_STOP1 0x0A
+#define ADS1262_OPCODE_START2 0x0C
+#define ADS1262_OPCODE_STOP2 0x0E
+#define ADS1262_OPCODE_RDATA1 0x12
+#define ADS1262_OPCODE_RDATA2 0x14
+#define ADS1262_OPCODE_SYOCAL1 0x16
+#define ADS1262_OPCODE_SYGCAL1 0x17
+#define ADS1262_OPCODE_SFOCAL1 0x19
+#define ADS1262_OPCODE_SYOCAL2 0x1B
+#define ADS1262_OPCODE_SYGCAL2 0x1C
+#define ADS1262_OPCODE_SFOCAL2 0x1E
+#define ADS1262_OPCODE_RREG 0x20
+#define ADS1262_OPCODE_WREG 0x40
+
+/* Registers */
+#define ADS1262_ID_REG 0x00
+#define ADS1262_POWER_REG 0x01
+#define ADS1262_INTERFACE_REG 0x02
+#define ADS1262_MODE0_REG 0x03
+#define ADS1262_MODE1_REG 0x04
+#define ADS1262_MODE2_REG 0x05
+#define ADS1262_INPMUX_REG 0x06
+#define ADS1262_OFCAL0_REG 0x07
+#define ADS1262_OFCAL1_REG 0x08
+#define ADS1262_OFCAL2_REG 0x09
+#define ADS1262_FSCAL0_REG 0x0A
+#define ADS1262_FSCAL1_REG 0x0B
+#define ADS1262_FSCAL2_REG 0x0C
+#define ADS1262_IDACMUX_REG 0x0D
+#define ADS1262_IDACMAG_REG 0x0E
+#define ADS1262_REFMUX_REG 0x0F
+#define ADS1262_TDACP_REG 0x10
+#define ADS1262_TDACN_REG 0x11
+#define ADS1262_GPIOCON_REG 0x12
+#define ADS1262_GPIODIR_REG 0x13
+#define ADS1262_GPIODAT_REG 0x14
+#define ADS1262_ADC2CFG_REG 0x15
+#define ADS1262_ADC2MUX_REG 0x16
+#define ADS1262_ADC2OFC0_REG 0x17
+#define ADS1262_ADC2OFC1_REG 0x18
+#define ADS1262_ADC2FSC0_REG 0x19
+#define ADS1262_ADC2FSC1_REG 0x1A
+#define ADS1262_REG_COUNT 0x1B
+
+/* ID fields */
+#define ADS1262_DEV_ID_MASK GENMASK(7, 5)
+#define ADS1262_REV_ID_MASK GENMASK(4, 0)
+
+/* POWER fields */
+#define ADS1262_POWER_RESET_MASK BIT(4)
+#define ADS1262_POWER_VBIAS_MASK BIT(1)
+#define ADS1262_POWER_INTREF_MASK BIT(0)
+
+/* INTERFACE fields */
+#define ADS1262_INTERFACE_TIMEOUT_MASK BIT(3)
+#define ADS1262_INTERFACE_STATUS_MASK BIT(2)
+#define ADS1262_INTERFACE_CRC_MASK GENMASK(1, 0)
+
+/* MODE0 fields */
+#define ADS1262_MODE0_REFREV_MASK BIT(7)
+#define ADS1262_MODE0_RUNMODE_MASK BIT(6)
+#define ADS1262_MODE0_IDAC_ROT_MASK BIT(5)
+#define ADS1262_MODE0_CHOP_MASK BIT(4)
+#define ADS1262_MODE0_DELAY_MASK GENMASK(3, 0)
+
+/* MODE1 fields */
+#define ADS1262_MODE1_FILTER_MASK GENMASK(7, 5)
+#define ADS1262_MODE1_SBADC_MASK BIT(4)
+#define ADS1262_MODE1_SBPOL_MASK BIT(3)
+#define ADS1262_MODE1_SBMAG_MASK GENMASK(2, 0)
+
+/* MODE2 fields */
+#define ADS1262_MODE2_BYPASS_MASK BIT(7)
+#define ADS1262_MODE2_GAIN_MASK GENMASK(6, 4)
+#define ADS1262_MODE2_DR_MASK GENMASK(3, 0)
+
+/* INPMUX fields */
+#define ADS1262_INPMUX_MUXP_MASK GENMASK(7, 4)
+#define ADS1262_INPMUX_MUXN_MASK GENMASK(3, 0)
+
+/* IDACMUX fields */
+#define ADS1262_IDACMUX_MUX2_MASK GENMASK(7, 4)
+#define ADS1262_IDACMUX_MUX1_MASK GENMASK(3, 0)
+
+/* IDACMAG fields */
+#define ADS1262_IDACMAG_MAG2_MASK GENMASK(7, 4)
+#define ADS1262_IDACMAG_MAG1_MASK GENMASK(3, 0)
+
+/* REFMUX fields */
+#define ADS1262_REFMUX_RMUXP_MASK GENMASK(5, 3)
+#define ADS1262_REFMUX_RMUXN_MASK GENMASK(2, 0)
+
+/* TDACP fields */
+#define ADS1262_TDACP_OUTP_MASK BIT(7)
+#define ADS1262_TDACP_MAGP_MASK GENMASK(4, 0)
+
+/* TDACN fields */
+#define ADS1262_TDACN_OUTN_MASK BIT(7)
+#define ADS1262_TDACN_MAGN_MASK GENMASK(4, 0)
+
+/* ADC2CFG fields */
+#define ADS1262_ADC2CFG_DR2_MASK GENMASK(7, 6)
+#define ADS1262_ADC2CFG_REF2_MASK GENMASK(5, 3)
+#define ADS1262_ADC2CFG_GAIN2_MASK GENMASK(2, 0)
+
+/* ADC2MUX fields */
+#define ADS1262_ADC2MUX_MUXP2_MASK GENMASK(7, 4)
+#define ADS1262_ADC2MUX_MUXN2_MASK GENMASK(3, 0)
+
+/* ID DEV_ID constants */
+#define ADS1262_DEV_ID_ADS1262 0
+#define ADS1262_DEV_ID_ADS1263 1
+
+/* MODE0 RUNMODE constants */
+#define ADS1262_RUNMODE_CONTINUOUS 0
+#define ADS1262_RUNMODE_PULSE 1
+
+/* MODE1 FILTER constants */
+#define ADS1262_FILTER_SINC1 0
+#define ADS1262_FILTER_SINC2 1
+#define ADS1262_FILTER_SINC3 2
+#define ADS1262_FILTER_SINC4 3
+#define ADS1262_FILTER_FIR 4
+
+/* MODE1 SENSOR BIAS constants */
+#define ADS1262_SBADC_COUNT 2
+#define ADS1262_SBPOL_COUNT 2
+#define ADS1262_SBMAG_COUNT 7
+
+/* MODE2 DATA RATE constants */
+#define ADS1262_DR_2_5_SPS 0
+#define ADS1262_DR_5_SPS 1
+#define ADS1262_DR_10_SPS 2
+#define ADS1262_DR_16_6_SPS 3
+#define ADS1262_DR_20_SPS 4
+#define ADS1262_DR_50_SPS 5
+#define ADS1262_DR_60_SPS 6
+#define ADS1262_DR_100_SPS 7
+#define ADS1262_DR_400_SPS 8
+#define ADS1262_DR_1200_SPS 9
+#define ADS1262_DR_2400_SPS 10
+#define ADS1262_DR_4800_SPS 11
+#define ADS1262_DR_7200_SPS 12
+#define ADS1262_DR_14400_SPS 13
+#define ADS1262_DR_19200_SPS 14
+#define ADS1262_DR_38400_SPS 15
+
+/* INPMUX constants */
+#define ADS1262_INPMUX_AINCOM 10
+#define ADS1262_INPMUX_TEMP 11
+#define ADS1262_INPMUX_AVDD 12
+#define ADS1262_INPMUX_DVDD 13
+#define ADS1262_INPMUX_TDAC 14
+#define ADS1262_INPMUX_FLOAT 15
+
+/* IDACMUX constants */
+#define ADS1262_IDACMUX_NO_CONN 11
+#define ADS1262_IDACMUX_COUNT 12
+
+/* IDACMAG constants */
+#define ADS1262_IDACMAG_OFF 0
+#define ADS1262_IDACMAG_COUNT 11
+
+/* REFMUX constants */
+#define ADS1262_RMUX_INTER 0
+#define ADS1262_RMUX_AIN0_AIN1 1
+#define ADS1262_RMUX_AIN2_AIN3 2
+#define ADS1262_RMUX_AIN4_AIN5 3
+#define ADS1262_RMUX_AVDD_AVSS 4
+#define ADS1262_RMUX_COUNT 5
+
+struct ads1262_channel {
+ /* MODE0 */
+ u8 conv_delay:4;
+ u8 chop_mode:1;
+ u8 idac_rot_mode:1;
+ u8 runmode:1;
+ u8 rev_vref_pol:1;
+
+ /* MODE1 */
+ u8 sbias_magnitude:3;
+ u8 sbias_polarity:1;
+ u8 sbias_connection:1;
+ u8 filter:3;
+
+ /* MODE2 */
+ u8 data_rate:4;
+ u8 gain:3;
+ u8 pga_bypass:1;
+
+ /* INPMUX */
+ u8 negative_input:4;
+ u8 positive_input:4;
+};
+
+struct ads1262 {
+ struct spi_device *spi;
+ struct regmap *regmap;
+ struct iio_dev *indio_dev;
+ struct iio_trigger *trig;
+ struct gpio_desc *reset_gpiod;
+ struct gpio_desc *start_gpiod;
+
+ void *scan_buffer;
+ size_t scan_sz;
+
+ /* Protects channel state */
+ struct mutex chan_lock;
+ u32 vref_uV;
+ unsigned int num_channels;
+ struct ads1262_channel *channels;
+ struct completion drdy;
+ struct spi_message msg;
+ struct spi_transfer xfer;
+
+ /* Protects transfer buffers and concurrent SPI transfers */
+ struct mutex xfer_lock;
+
+ u8 tx[6] __aligned(IIO_DMA_MINALIGN);
+ union {
+ u8 rx[6];
+ struct {
+ __be32 data;
+ } __packed shift_reg;
+ struct {
+ u8 dummy;
+ __be32 data;
+ } __packed holding_reg;
+ };
+};
+
+static const int ads1262_data_rate_avail[][2] = {
+ [ADS1262_DR_2_5_SPS] = { 2, 500000 },
+ [ADS1262_DR_5_SPS] = { 5, 0 },
+ [ADS1262_DR_10_SPS] = { 10, 0 },
+ [ADS1262_DR_16_6_SPS] = { 16, 666667 },
+ [ADS1262_DR_20_SPS] = { 20, 0 },
+ [ADS1262_DR_50_SPS] = { 50, 0 },
+ [ADS1262_DR_60_SPS] = { 60, 0 },
+ [ADS1262_DR_100_SPS] = { 100, 0 },
+ [ADS1262_DR_400_SPS] = { 400, 0 },
+ [ADS1262_DR_1200_SPS] = { 1200, 0 },
+ [ADS1262_DR_2400_SPS] = { 2400, 0 },
+ [ADS1262_DR_4800_SPS] = { 4800, 0 },
+ [ADS1262_DR_7200_SPS] = { 7200, 0 },
+ [ADS1262_DR_14400_SPS] = { 14400, 0 },
+ [ADS1262_DR_19200_SPS] = { 19200, 0 },
+ [ADS1262_DR_38400_SPS] = { 38400, 0 },
+};
+
+static const int ads1262_conv_delay_avail[][2] = {
+ { 0, 0 },
+ { 0, 8700 },
+ { 0, 17000 },
+ { 0, 35000 },
+ { 0, 69000 },
+ { 0, 139000 },
+ { 0, 278000 },
+ { 0, 555000 },
+ { 0, 1100000 },
+ { 0, 2200000 },
+ { 0, 4400000 },
+ { 0, 8800000 },
+};
+
+static const int ads1262_pga_gain_avail[] = {
+ 1, 2, 4, 8, 16, 32
+};
+
+static int ads1262_dev_power_on(struct ads1262 *st)
+{
+ int ret;
+
+ ret = gpiod_set_value_cansleep(st->reset_gpiod, 0);
+ if (ret)
+ return ret;
+
+ fsleep(9 * USEC_PER_MSEC);
+
+ return 0;
+}
+
+static int ads1262_dev_power_off(struct ads1262 *st)
+{
+ int ret;
+
+ ret = gpiod_set_value_cansleep(st->reset_gpiod, 1);
+ if (ret)
+ return ret;
+
+ fsleep(9 * USEC_PER_MSEC);
+
+ return 0;
+}
+
+static int ads1262_dev_cmd(struct ads1262 *st, u8 opcode)
+{
+ guard(mutex)(&st->xfer_lock);
+
+ st->tx[0] = opcode;
+
+ return spi_write(st->spi, &st->tx[0], sizeof(st->tx[0]));
+}
+
+static int ads1262_dev_read_data_command(struct ads1262 *st, u8 cmd,
+ __be32 *val)
+{
+ int ret;
+
+ guard(mutex)(&st->xfer_lock);
+
+ memset(st->tx, 0, sizeof(st->tx));
+ st->tx[0] = cmd;
+
+ ret = spi_sync(st->spi, &st->msg);
+ if (ret)
+ return ret;
+
+ *val = st->holding_reg.data;
+
+ return 0;
+}
+
+static int ads1262_dev_read_data_direct(struct ads1262 *st, __be32 *val)
+{
+ int ret;
+
+ /*
+ * If reading data from the shift register, we should already be holding
+ * the xfer_lock because all SPI activity is forbidden between the START
+ * command and the actual data retrieval.
+ */
+ lockdep_assert_held(&st->xfer_lock);
+
+ memset(st->tx, 0, sizeof(st->tx));
+ ret = spi_sync(st->spi, &st->msg);
+ if (ret)
+ return ret;
+
+ *val = st->shift_reg.data;
+
+ return 0;
+}
+
+static int ads1262_dev_reset(struct ads1262 *st)
+{
+ int ret;
+
+ if (st->reset_gpiod)
+ ret = gpiod_set_value_cansleep(st->reset_gpiod, 1);
+ else
+ ret = ads1262_dev_cmd(st, ADS1262_OPCODE_RESET);
+ if (ret)
+ return ret;
+
+ fsleep(1);
+
+ if (st->reset_gpiod) {
+ ret = gpiod_set_value_cansleep(st->reset_gpiod, 0);
+ fsleep(1);
+ }
+
+ return ret;
+}
+
+static int ads1262_dev_start(struct ads1262 *st)
+{
+ int ret;
+
+ if (st->start_gpiod)
+ ret = gpiod_set_value_cansleep(st->start_gpiod, 1);
+ else
+ ret = ads1262_dev_cmd(st, ADS1262_OPCODE_START1);
+
+ return ret;
+}
+
+static int ads1262_dev_stop(struct ads1262 *st)
+{
+ int ret;
+
+ if (st->start_gpiod)
+ ret = gpiod_set_value_cansleep(st->start_gpiod, 0);
+ else
+ ret = ads1262_dev_cmd(st, ADS1262_OPCODE_STOP1);
+
+ return ret;
+}
+
+static int ads1262_dev_start_one(struct ads1262 *st, u8 runmode)
+{
+ int ret;
+
+ ret = ads1262_dev_start(st);
+ if (ret)
+ return ret;
+
+ if (runmode == ADS1262_RUNMODE_CONTINUOUS)
+ return ads1262_dev_stop(st);
+
+ return 0;
+}
+
+static void ads1262_wait_for_conversion(struct ads1262 *st)
+{
+ reinit_completion(&st->drdy);
+
+ /*
+ * The first conversion latency is affected by the channel's data rate,
+ * filter, the configurable conversion delay and whether chop mode
+ * and/or IDAC rotation mode are enabled.
+ *
+ * The worst possible latency is calculated by taking the lowest data
+ * rate (2.5 SPS) and the sinc4 filter. This gives a latency of 1600 ms
+ * (Table 9-13). Then add the slowest configurable conversion delay
+ * (9 ms) and multiply by 4 to account for chop and IDAC rotation modes
+ * (Equation 20).
+ *
+ * Final result is 4 * (1600 ms + 9 ms) = 6436.
+ */
+ wait_for_completion_timeout(&st->drdy, msecs_to_jiffies(6436));
+}
+
+static void ads1262_channel_set_runmode(struct ads1262 *st,
+ struct ads1262_channel *chan,
+ u8 runmode)
+{
+ guard(mutex)(&st->chan_lock);
+ chan->runmode = runmode;
+}
+
+static int ads1262_channel_enable_and_read(struct ads1262 *st,
+ struct ads1262_channel *chan,
+ __be32 *val)
+{
+ int ret;
+
+ lockdep_assert_held(&st->xfer_lock);
+
+ /*
+ * Prepare the transfer buffer to do bulk register write
+ * (Section 9.5.6).
+ */
+ st->tx[0] = ADS1262_MODE0_REG | ADS1262_OPCODE_WREG;
+ st->tx[1] = sizeof(*chan) - 1;
+
+ /*
+ * The ads1262_channel struct can be written directly to the chip's
+ * configuration registers (MODE0, MODE1, MODE2, INPMUX) in a single
+ * transfer, so it's necessary to assert it's size (4 bytes).
+ */
+ static_assert(sizeof(*chan) == 4);
+ mutex_lock(&st->chan_lock);
+ memcpy(&st->tx[2], chan, sizeof(*chan));
+ mutex_unlock(&st->chan_lock);
+
+ ret = spi_sync(st->spi, &st->msg);
+ if (ret)
+ return ret;
+
+ /*
+ * If new data is ready, it's shifted out on the same transfer.
+ * (Section 9.4.7.1)
+ */
+ if (val)
+ *val = st->shift_reg.data;
+
+ return 0;
+}
+
+static int ads1262_channel_enable(struct ads1262 *st,
+ struct ads1262_channel *chan)
+{
+ guard(mutex)(&st->xfer_lock);
+
+ return ads1262_channel_enable_and_read(st, chan, NULL);
+}
+
+static int ads1262_channel_hot_reload(struct ads1262 *st,
+ const struct iio_chan_spec *chan)
+{
+ unsigned int weight;
+ unsigned long i;
+ int ret;
+
+ /*
+ * Hot reloading is only required on buffer mode and if only one channel
+ * is enabled.
+ */
+ if (!iio_device_try_claim_buffer_mode(st->indio_dev))
+ return 0;
+
+ weight = bitmap_weight(st->indio_dev->active_scan_mask,
+ iio_get_masklength(st->indio_dev));
+ if (weight != 1) {
+ iio_device_release_direct(st->indio_dev);
+ return 0;
+ }
+
+ i = find_first_bit(st->indio_dev->active_scan_mask,
+ iio_get_masklength(st->indio_dev));
+ if (i != chan->scan_index) {
+ iio_device_release_direct(st->indio_dev);
+ return 0;
+ }
+
+ /*
+ * The device automatically hot reloads the channel after writing to
+ * the configuration registers.
+ */
+ ret = ads1262_channel_enable(st, &st->channels[chan->scan_index]);
+
+ iio_device_release_direct(st->indio_dev);
+
+ return ret;
+}
+
+static int ads1262_channel_read(struct ads1262 *st,
+ struct ads1262_channel *chan_data,
+ __be32 *val)
+{
+ struct device *dev = &st->spi->dev;
+ u8 runmode;
+ int ret;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
+
+ IIO_DEV_ACQUIRE_DIRECT_MODE(st->indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ /*
+ * 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_data->chop_mode || chan_data->idac_rot_mode)
+ runmode = ADS1262_RUNMODE_CONTINUOUS;
+ else
+ runmode = ADS1262_RUNMODE_PULSE;
+
+ ads1262_channel_set_runmode(st, chan_data, runmode);
+
+ ret = ads1262_channel_enable(st, chan_data);
+ if (ret)
+ return ret;
+
+ ret = ads1262_dev_start_one(st, runmode);
+ if (ret)
+ return ret;
+
+ ads1262_wait_for_conversion(st);
+
+ return ads1262_dev_read_data_command(st, ADS1262_OPCODE_RDATA1, val);
+}
+
+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;
+ u8 mode, realbits;
+ __be32 raw;
+ u32 cnv;
+ int ret;
+
+ chan_data = &st->channels[chan->scan_index];
+ realbits = chan->scan_type.realbits;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ads1262_channel_read(st, chan_data, &raw);
+ if (ret)
+ return ret;
+
+ cnv = be32_to_cpu(raw);
+ *val = sign_extend32(cnv, realbits - 1);
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ u64 divd, divr, tmp, rem;
+
+ mutex_lock(&st->chan_lock);
+ divd = st->vref_uV;
+ divr = BIT_ULL(chan_data->gain + realbits - 1) * 1000;
+ mutex_unlock(&st->chan_lock);
+
+ tmp = div64_u64(divd * 1000000000ULL, divr);
+ *val = div64_u64_rem(tmp, 1000000000ULL, &rem);
+ *val2 = rem;
+
+ return IIO_VAL_INT_PLUS_NANO;
+
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ mutex_lock(&st->chan_lock);
+ *val = ads1262_pga_gain_avail[chan_data->gain];
+ mutex_unlock(&st->chan_lock);
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ mutex_lock(&st->chan_lock);
+ mode = chan_data->data_rate;
+ mutex_unlock(&st->chan_lock);
+
+ *val = ads1262_data_rate_avail[mode][0];
+ *val2 = ads1262_data_rate_avail[mode][1];
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case IIO_CHAN_INFO_CONVDELAY:
+ mutex_lock(&st->chan_lock);
+ mode = chan_data->conv_delay;
+ mutex_unlock(&st->chan_lock);
+
+ *val = ads1262_conv_delay_avail[mode][0];
+ *val2 = ads1262_conv_delay_avail[mode][1];
+ return IIO_VAL_INT_PLUS_NANO;
+
+ 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)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *vals = (const int *)ads1262_data_rate_avail;
+ *length = ARRAY_SIZE(ads1262_data_rate_avail) * 2;
+ return IIO_AVAIL_LIST;
+
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ *type = IIO_VAL_INT;
+ *vals = ads1262_pga_gain_avail;
+ *length = ARRAY_SIZE(ads1262_pga_gain_avail);
+ return IIO_AVAIL_LIST;
+
+ case IIO_CHAN_INFO_CONVDELAY:
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *vals = (const int *)ads1262_conv_delay_avail;
+ *length = ARRAY_SIZE(ads1262_conv_delay_avail) * 2;
+ return IIO_AVAIL_LIST;
+
+ 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;
+ unsigned int i;
+
+ chan_data = &st->channels[chan->scan_index];
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ for (i = 0; i < ARRAY_SIZE(ads1262_data_rate_avail); i++) {
+ if (val == ads1262_data_rate_avail[i][0] &&
+ val2 == ads1262_data_rate_avail[i][1])
+ break;
+ }
+ if (i == ARRAY_SIZE(ads1262_data_rate_avail))
+ return -EINVAL;
+
+ mutex_lock(&st->chan_lock);
+ chan_data->data_rate = i;
+ mutex_unlock(&st->chan_lock);
+
+ break;
+
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ for (i = 0; i < ARRAY_SIZE(ads1262_pga_gain_avail); i++) {
+ if (val == ads1262_pga_gain_avail[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(ads1262_pga_gain_avail))
+ return -EINVAL;
+
+ mutex_lock(&st->chan_lock);
+ chan_data->gain = i;
+ mutex_unlock(&st->chan_lock);
+
+ break;
+
+ case IIO_CHAN_INFO_CONVDELAY:
+ for (i = 0; i < ARRAY_SIZE(ads1262_conv_delay_avail); i++) {
+ if (val == ads1262_conv_delay_avail[i][0] &&
+ val2 == ads1262_conv_delay_avail[i][1])
+ break;
+ }
+ if (i == ARRAY_SIZE(ads1262_conv_delay_avail))
+ return -EINVAL;
+
+ mutex_lock(&st->chan_lock);
+ chan_data->conv_delay = i;
+ mutex_unlock(&st->chan_lock);
+
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return ads1262_channel_hot_reload(st, chan);
+}
+
+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_CONVDELAY:
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+}
+
+static int ads1262_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int writeval, unsigned int *readval)
+{
+ struct ads1262 *st = iio_priv(indio_dev);
+ struct device *dev = &st->spi->dev;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
+
+ if (readval)
+ return regmap_read_bypassed(st->regmap, reg, readval);
+
+ return regmap_write(st->regmap, reg, writeval);
+}
+
+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,
+};
+
+static int ads1262_get_filter_type(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ads1262 *st = iio_priv(indio_dev);
+ struct ads1262_channel *chan_data;
+
+ guard(mutex)(&st->chan_lock);
+
+ chan_data = &st->channels[chan->scan_index];
+ return chan_data->filter;
+}
+
+static int ads1262_set_filter_type(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int val)
+{
+ struct ads1262 *st = iio_priv(indio_dev);
+ struct ads1262_channel *chan_data;
+
+ /* Can't guard() the lock here to avoid deadlock when hot reloading */
+ mutex_lock(&st->chan_lock);
+ chan_data = &st->channels[chan->scan_index];
+ chan_data->filter = val;
+ mutex_unlock(&st->chan_lock);
+
+ return ads1262_channel_hot_reload(st, chan);
+}
+
+static const char * const ads1262_filter_type_labels[] = {
+ [ADS1262_FILTER_SINC1] = "sinc1",
+ [ADS1262_FILTER_SINC2] = "sinc2",
+ [ADS1262_FILTER_SINC3] = "sinc3",
+ [ADS1262_FILTER_SINC4] = "sinc4",
+ [ADS1262_FILTER_FIR] = "fir",
+};
+
+static const struct iio_enum ads1262_filter_type_enum = {
+ .items = ads1262_filter_type_labels,
+ .num_items = ARRAY_SIZE(ads1262_filter_type_labels),
+ .get = ads1262_get_filter_type,
+ .set = ads1262_set_filter_type,
+};
+
+static const struct iio_chan_spec_ext_info ads1262_ext_info[] = {
+ IIO_ENUM("filter_type", IIO_SEPARATE, &ads1262_filter_type_enum),
+ IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE,
+ &ads1262_filter_type_enum),
+ { }
+};
+
+static const struct iio_chan_spec ads1262_iio_voltage_template = {
+ .type = IIO_VOLTAGE,
+ .indexed = true,
+ .scan_type = {
+ .format = IIO_SCAN_FORMAT_SIGNED_INT,
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_BE,
+ },
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_CONVDELAY),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_HARDWAREGAIN) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_CONVDELAY),
+ .ext_info = ads1262_ext_info,
+};
+
+static irqreturn_t ads1262_irq_handler(int irq, void *dev_id)
+{
+ struct ads1262 *st = dev_id;
+
+ if (iio_buffer_enabled(st->indio_dev))
+ iio_trigger_poll(st->trig);
+
+ complete(&st->drdy);
+
+ return IRQ_HANDLED;
+}
+
+static int ads1262_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct ads1262 *st = iio_priv(indio_dev);
+ struct device *dev = &st->spi->dev;
+ unsigned int weight;
+ unsigned long i;
+ int ret;
+
+ weight = bitmap_weight(indio_dev->active_scan_mask,
+ iio_get_masklength(indio_dev));
+ st->scan_sz = ALIGN(sizeof(__be32) * weight, sizeof(s64));
+ st->scan_sz += sizeof(s64);
+ st->scan_buffer = kzalloc(st->scan_sz, GFP_KERNEL);
+ if (!st->scan_buffer)
+ return -ENOMEM;
+
+ ret = spi_optimize_message(st->spi, &st->msg);
+ if (ret)
+ goto out_state_cleanup;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ goto out_unoptimize_message;
+
+ iio_for_each_active_channel(indio_dev, i)
+ ads1262_channel_set_runmode(st, &st->channels[i],
+ ADS1262_RUNMODE_CONTINUOUS);
+
+ if (weight == 1) {
+ i = find_first_bit(indio_dev->active_scan_mask,
+ iio_get_masklength(indio_dev));
+ ret = ads1262_channel_enable(st, &st->channels[i]);
+ if (ret)
+ goto out_runtime_autosuspend;
+ }
+
+ ret = ads1262_dev_start(st);
+ if (ret)
+ goto out_runtime_autosuspend;
+
+ return 0;
+
+out_runtime_autosuspend:
+ pm_runtime_put_autosuspend(dev);
+
+out_unoptimize_message:
+ spi_unoptimize_message(&st->msg);
+
+out_state_cleanup:
+ kfree(st->scan_buffer);
+ st->scan_buffer = NULL;
+ st->scan_sz = 0;
+
+ return ret;
+}
+
+static int ads1262_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct ads1262 *st = iio_priv(indio_dev);
+ struct device *dev = &st->spi->dev;
+
+ ads1262_dev_stop(st);
+ pm_runtime_put_autosuspend(dev);
+ spi_unoptimize_message(&st->msg);
+ kfree(st->scan_buffer);
+ st->scan_buffer = NULL;
+ st->scan_sz = 0;
+
+ return 0;
+}
+
+static bool ads1262_validate_scan_mask(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct ads1262 *st = iio_priv(indio_dev);
+ struct device *dev = &st->spi->dev;
+
+ if (iio_trigger_using_own(indio_dev)) {
+ dev_err(dev, "The %s trigger only supports one active channel\n",
+ st->trig->name);
+ return iio_validate_scan_mask_onehot(indio_dev, scan_mask);
+ }
+
+ return true;
+}
+
+static const struct iio_buffer_setup_ops ads1262_buffer_ops = {
+ .preenable = ads1262_buffer_preenable,
+ .postdisable = ads1262_buffer_postdisable,
+ .validate_scan_mask = ads1262_validate_scan_mask,
+};
+
+static int ads1262_fill_buffer_one(struct ads1262 *st)
+{
+ __be32 *scan_buffer = st->scan_buffer;
+
+ /*
+ * When only one channel is enabled, we can't really avoid SPI activity
+ * from happening when the auxiliary ADC is in use, thus we have to read
+ * from the data-holding register (Section 9.4.7.2).
+ */
+ return ads1262_dev_read_data_command(st, ADS1262_OPCODE_RDATA1,
+ scan_buffer);
+}
+
+static int ads1262_fill_buffer_mult(struct ads1262 *st)
+{
+ __be32 val, *scan_buffer = st->scan_buffer;
+ unsigned int chan;
+ int i = -1;
+ int ret;
+
+ /*
+ * This routine enables and reads channels in a full-duplex fashion.
+ *
+ * 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 SPI
+ * activity happen 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(st->indio_dev, chan) {
+ ret = ads1262_channel_enable_and_read(st, &st->channels[chan],
+ &val);
+ if (ret)
+ return ret;
+
+ if (i > -1)
+ scan_buffer[i] = val;
+ i++;
+
+ ads1262_wait_for_conversion(st);
+ }
+
+ return ads1262_dev_read_data_direct(st, &scan_buffer[i]);
+}
+
+static irqreturn_t ads1262_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ads1262 *st = iio_priv(indio_dev);
+ s64 ts = pf->timestamp;
+ unsigned int weight;
+ int ret;
+
+ weight = bitmap_weight(indio_dev->active_scan_mask,
+ iio_get_masklength(indio_dev));
+
+ memset(st->scan_buffer, 0, st->scan_sz);
+
+ if (weight == 1)
+ ret = ads1262_fill_buffer_one(st);
+ else
+ ret = ads1262_fill_buffer_mult(st);
+ if (ret)
+ goto out_notify_done;
+
+ iio_push_to_buffers_with_ts(indio_dev, st->scan_buffer,
+ st->scan_sz, ts);
+
+out_notify_done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ads1262_parse_channel_data(struct ads1262 *st,
+ struct ads1262_channel *chan_data,
+ struct fwnode_handle *node)
+{
+ struct device *dev = &st->spi->dev;
+ const char *propname;
+ u32 val;
+ int ret;
+
+ if (fwnode_property_present(node, "ti,pga-bypass"))
+ chan_data->pga_bypass = true;
+
+ if (fwnode_property_present(node, "ti,rev-vref-pol"))
+ chan_data->rev_vref_pol = true;
+
+ if (fwnode_property_present(node, "ti,chop-mode"))
+ chan_data->chop_mode = true;
+
+ if (fwnode_property_present(node, "ti,idac-rotation-mode"))
+ chan_data->idac_rot_mode = true;
+
+ propname = "ti,sbias-connection";
+ if (fwnode_property_present(node, propname)) {
+ ret = fwnode_property_read_u32(node, propname, &val);
+ if (ret)
+ goto err_property_read;
+ if (val >= ADS1262_SBADC_COUNT) {
+ ret = -ERANGE;
+ goto err_property_read;
+ }
+
+ chan_data->sbias_connection = val;
+ }
+
+ propname = "ti,sbias-polarity";
+ if (fwnode_property_present(node, propname)) {
+ ret = fwnode_property_read_u32(node, propname, &val);
+ if (ret)
+ goto err_property_read;
+ if (val >= ADS1262_SBPOL_COUNT) {
+ ret = -ERANGE;
+ goto err_property_read;
+ }
+
+ chan_data->sbias_polarity = val;
+ }
+
+ propname = "ti,sbias-magnitude";
+ if (fwnode_property_present(node, propname)) {
+ ret = fwnode_property_read_u32(node, propname, &val);
+ if (ret)
+ goto err_property_read;
+ if (val >= ADS1262_SBMAG_COUNT) {
+ ret = -ERANGE;
+ goto err_property_read;
+ }
+
+ chan_data->sbias_magnitude = val;
+ }
+
+ return 0;
+
+err_property_read:
+ return dev_err_probe(dev, ret, "%s: Failed to read property %s\n",
+ fwnode_get_name(node), propname);
+}
+
+static int ads1262_parse_channel_node(struct ads1262 *st,
+ struct iio_chan_spec *chan,
+ struct ads1262_channel *chan_data,
+ struct fwnode_handle *node)
+{
+ struct device *dev = &st->spi->dev;
+ const char *propname;
+ u32 pins[2];
+ int ret;
+
+ propname = "diff-channels";
+ ret = fwnode_property_read_u32_array(node, propname, pins,
+ ARRAY_SIZE(pins));
+ if (ret)
+ return dev_err_probe(dev, ret, "%s: Failed to read %s\n",
+ fwnode_get_name(node), propname);
+
+ if (pins[0] > ADS1262_INPMUX_FLOAT)
+ return dev_err_probe(dev, -ENXIO,
+ "%s: positive input %u not in range\n",
+ fwnode_get_name(node), pins[0]);
+
+ if (pins[1] > ADS1262_INPMUX_FLOAT)
+ return dev_err_probe(dev, -ENXIO,
+ "%s: negative input %u not in range\n",
+ fwnode_get_name(node), pins[1]);
+
+ chan->channel = pins[0];
+ chan->channel2 = pins[1];
+ chan->differential = true;
+
+ chan_data->positive_input = pins[0];
+ chan_data->negative_input = pins[1];
+
+ /* Only non-zero default values are data rate and filter */
+ chan_data->data_rate = ADS1262_DR_20_SPS;
+ chan_data->filter = ADS1262_FILTER_FIR;
+
+ return ads1262_parse_channel_data(st, chan_data, node);
+}
+
+static int ads1262_parse_channels(struct iio_dev *indio_dev)
+{
+ struct ads1262 *st = iio_priv(indio_dev);
+ struct iio_chan_spec *channels, *chan;
+ struct device *dev = &st->spi->dev;
+ struct ads1262_channel *chan_data;
+ unsigned int num_channels;
+ u32 ch_reg;
+ int ret;
+
+ num_channels = device_get_named_child_node_count(dev, "channel");
+ if (!num_channels)
+ return dev_err_probe(dev, -ENXIO,
+ "No 'channel' nodes configured\n");
+
+ st->num_channels = num_channels;
+ st->channels = devm_kcalloc(dev, num_channels, sizeof(*st->channels),
+ GFP_KERNEL);
+ if (!st->channels)
+ return -ENOMEM;
+
+ /* Account for the timestamp channel */
+ num_channels++;
+ channels = devm_kcalloc(dev, num_channels, sizeof(*channels),
+ GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ chan = channels;
+ device_for_each_named_child_node_scoped(dev, node, "channel") {
+ ret = fwnode_property_read_u32(node, "reg", &ch_reg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "%s: Failed to read channel reg\n",
+ fwnode_get_name(node));
+
+ /* Last channel is reserved for timestamp */
+ if (ch_reg >= num_channels - 1)
+ return dev_err_probe(dev, -EINVAL,
+ "%s: reg %u out of range\n",
+ fwnode_get_name(node), ch_reg);
+
+ *chan = ads1262_iio_voltage_template;
+ chan->scan_index = ch_reg;
+ chan_data = &st->channels[ch_reg];
+ ret = ads1262_parse_channel_node(st, chan++, chan_data, node);
+ if (ret)
+ return ret;
+ }
+
+ *chan = (struct iio_chan_spec)IIO_CHAN_SOFT_TIMESTAMP(num_channels - 1);
+
+ indio_dev->num_channels = num_channels;
+ indio_dev->channels = channels;
+
+ return 0;
+}
+
+static int ads1262_read_chip_name(struct ads1262 *st, char **name)
+{
+ struct device *dev = &st->spi->dev;
+ u8 dev_id;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(st->regmap, ADS1262_ID_REG, &val);
+ if (ret)
+ return ret;
+
+ dev_id = FIELD_GET(ADS1262_DEV_ID_MASK, val);
+
+ switch (dev_id) {
+ case ADS1262_DEV_ID_ADS1262:
+ *name = "ads1262";
+ break;
+ case ADS1262_DEV_ID_ADS1263:
+ *name = "ads1263";
+ break;
+ default:
+ *name = "ads1262";
+ dev_dbg(dev, "Failed to identify device with ID 0x%x\n", val);
+ }
+
+ return 0;
+}
+
+static int ads1262_parse_idac_pins(struct ads1262 *st, u32 *pins,
+ unsigned int num_pins)
+{
+ struct device *dev = &st->spi->dev;
+ char propname[10];
+ u32 val;
+ int ret;
+
+ for (unsigned int i = 0; i < num_pins; i++) {
+ scnprintf(propname, sizeof(propname), "idac%d-pin", i + 1);
+ if (!device_property_present(dev, propname)) {
+ pins[i] = ADS1262_IDACMUX_NO_CONN;
+ continue;
+ }
+
+ ret = device_property_read_u32(dev, propname, &val);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to read property %s\n",
+ propname);
+ if (val < ADS1262_IDACMUX_COUNT)
+ return dev_err_probe(dev, -EINVAL,
+ "%s: Pin number out of range %d\n",
+ propname, val);
+
+ pins[i] = val;
+ }
+
+ return 0;
+}
+
+static int ads1262_parse_idac_mags(struct ads1262 *st, u32 *mags,
+ unsigned int num_mags)
+{
+ static const u32 idac_microamps[] = {
+ 0, 50, 100, 250, 500, 750, 1000, 1500, 2000, 2500, 3000
+ };
+ struct device *dev = &st->spi->dev;
+ unsigned int mode;
+ char propname[15];
+ u32 val;
+ int ret;
+
+ for (unsigned int i = 0; i < num_mags; i++) {
+ scnprintf(propname, sizeof(propname), "idac%d-microamp", i + 1);
+ if (!device_property_present(dev, propname)) {
+ mags[i] = ADS1262_IDACMAG_OFF;
+ continue;
+ }
+
+ ret = device_property_read_u32(dev, propname, &val);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to read property %s\n",
+ propname);
+
+ for (mode = 0; mode < ARRAY_SIZE(idac_microamps); i++) {
+ if (val == idac_microamps[mode])
+ break;
+ }
+ if (mode == ARRAY_SIZE(idac_microamps))
+ return dev_err_probe(dev, -EINVAL,
+ "%s: Invalid value %d\n",
+ propname, val);
+
+ mags[i] = mode;
+ }
+
+ return 0;
+}
+
+static int ads1262_dev_configure(struct ads1262 *st)
+{
+ struct device *dev = &st->spi->dev;
+ u32 idac_pins[2], idac_mags[2];
+ u8 val;
+ int ret;
+
+ ret = ads1262_dev_reset(st);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to reset device\n");
+
+ ret = regmap_update_bits(st->regmap, ADS1262_INTERFACE_REG,
+ ADS1262_INTERFACE_STATUS_MASK |
+ ADS1262_INTERFACE_CRC_MASK, 0);
+ if (ret)
+ return ret;
+
+ if (device_property_present(dev, "ti,vbias")) {
+ ret = regmap_update_bits(st->regmap, ADS1262_POWER_REG,
+ ADS1262_POWER_VBIAS_MASK,
+ ADS1262_POWER_VBIAS_MASK);
+ if (ret)
+ return ret;
+ }
+
+ ret = ads1262_parse_idac_pins(st, idac_pins, ARRAY_SIZE(idac_pins));
+ if (ret)
+ return ret;
+
+ ret = ads1262_parse_idac_mags(st, idac_mags, ARRAY_SIZE(idac_mags));
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(ADS1262_IDACMUX_MUX1_MASK, idac_pins[0]);
+ val |= FIELD_PREP(ADS1262_IDACMUX_MUX2_MASK, idac_pins[1]);
+
+ ret = regmap_update_bits(st->regmap, ADS1262_IDACMUX_REG,
+ ADS1262_IDACMUX_MUX1_MASK |
+ ADS1262_IDACMUX_MUX2_MASK, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(ADS1262_IDACMAG_MAG1_MASK, idac_mags[0]);
+ val |= FIELD_PREP(ADS1262_IDACMAG_MAG2_MASK, idac_mags[1]);
+
+ return regmap_update_bits(st->regmap, ADS1262_IDACMAG_REG,
+ ADS1262_IDACMAG_MAG1_MASK |
+ ADS1262_IDACMAG_MAG2_MASK, val);
+}
+
+static int ads1262_gpio_setup(struct ads1262 *st)
+{
+ struct device *dev = &st->spi->dev;
+ struct gpio_desc *gpiod;
+ const char *con_id;
+
+ con_id = "start";
+ gpiod = devm_gpiod_get_optional(dev, con_id, GPIOD_OUT_LOW);
+ if (IS_ERR(gpiod))
+ return dev_err_probe(dev, PTR_ERR(gpiod),
+ "Failed to get %s GPIO\n", con_id);
+ st->start_gpiod = gpiod;
+
+ con_id = "reset";
+ gpiod = devm_gpiod_get_optional(dev, con_id, GPIOD_OUT_HIGH);
+ if (IS_ERR(gpiod))
+ return dev_err_probe(dev, PTR_ERR(gpiod),
+ "Failed to get %s GPIO\n", con_id);
+ st->reset_gpiod = gpiod;
+
+ return 0;
+}
+
+static int ads1262_clk_setup(struct ads1262 *st)
+{
+ struct device *dev = &st->spi->dev;
+ struct clk *clk;
+ int ret;
+
+ clk = devm_clk_get_optional_enabled(dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk),
+ "Failed to get external clock\n");
+
+ /*
+ * The nominal clock frequency as indicated by the datasheet is
+ * 7372800.
+ */
+ ret = clk_set_rate(clk, 7372800);
+ if (ret)
+ return dev_err_probe(dev, PTR_ERR(clk),
+ "Failed to set the nominal clock frequency.\n");
+
+ return 0;
+}
+
+static int ads1262_regulator_setup(struct ads1262 *st)
+{
+ struct device *dev = &st->spi->dev;
+ const char *reg_id, *prop;
+ u32 mux[2] = {};
+ int val, ret;
+
+ reg_id = "dvdd";
+ ret = devm_regulator_get_enable(dev, reg_id);
+ if (ret)
+ goto err_regulator_get;
+
+ reg_id = "avdd";
+ ret = devm_regulator_get_enable(dev, reg_id);
+ if (ret)
+ goto err_regulator_get;
+
+ prop = "ti,neg-refmux";
+ device_property_read_u32(dev, prop, &mux[0]);
+ if (mux[0] >= ADS1262_RMUX_COUNT)
+ return dev_err_probe(dev, -ENXIO, " %s out of range\n", prop);
+
+ prop = "ti,pos-refmux";
+ device_property_read_u32(dev, prop, &mux[1]);
+ if (mux[1] >= ADS1262_RMUX_COUNT)
+ return dev_err_probe(dev, -ENXIO, " %s out of range\n", prop);
+
+ if (mux[0] == ADS1262_RMUX_INTER && mux[1] == ADS1262_RMUX_INTER) {
+ /* The internal voltage reference is 2.5 V */
+ st->vref_uV = 2500000;
+ return 0;
+ }
+
+ val = FIELD_PREP(ADS1262_REFMUX_RMUXN_MASK, mux[0]);
+ val |= FIELD_PREP(ADS1262_REFMUX_RMUXP_MASK, mux[1]);
+ ret = regmap_update_bits(st->regmap, ADS1262_REFMUX_REG,
+ ADS1262_REFMUX_RMUXN_MASK |
+ ADS1262_REFMUX_RMUXP_MASK, val);
+ if (ret)
+ return ret;
+
+ reg_id = "vref";
+ st->vref_uV = devm_regulator_get_enable_read_voltage(dev, reg_id);
+ if (st->vref_uV < 0)
+ goto err_regulator_get;
+
+ return 0;
+
+err_regulator_get:
+ return dev_err_probe(dev, ret, "Failed to get regulator %s\n", reg_id);
+}
+
+static int ads1262_spi_message_setup(struct ads1262 *st)
+{
+ st->xfer.tx_buf = st->tx;
+ st->xfer.rx_buf = st->rx;
+ st->xfer.len = sizeof(st->tx);
+ spi_message_init_with_transfers(&st->msg, &st->xfer, 1);
+
+ return 0;
+}
+
+static const struct regmap_range ads1262_read_write_range[] = {
+ regmap_reg_range(ADS1262_ID_REG, ADS1262_ADC2FSC1_REG),
+};
+
+static const struct regmap_range ads1262_read_only_range[] = {
+ regmap_reg_range(ADS1262_ID_REG, ADS1262_ID_REG),
+};
+
+static const struct regmap_range ads1262_volatile_range[] = {
+ /*
+ * The channel configuration registers (MODE0, MODE1, MODE2, INPMUX) are
+ * not actually volatile. However, we bypass the regmap API when
+ * writing to these registers for optimization reasons.
+ */
+ regmap_reg_range(ADS1262_MODE0_REG, ADS1262_INPMUX_REG),
+ regmap_reg_range(ADS1262_GPIODAT_REG, ADS1262_GPIODAT_REG),
+};
+
+static const struct regmap_access_table ads1262_wr_table = {
+ .yes_ranges = ads1262_read_write_range,
+ .n_yes_ranges = ARRAY_SIZE(ads1262_read_write_range),
+ .no_ranges = ads1262_read_only_range,
+ .n_no_ranges = ARRAY_SIZE(ads1262_read_only_range),
+};
+
+static const struct regmap_access_table ads1262_rd_table = {
+ .yes_ranges = ads1262_read_write_range,
+ .n_yes_ranges = ARRAY_SIZE(ads1262_read_write_range),
+};
+
+static const struct regmap_access_table ads1262_volatile_table = {
+ .yes_ranges = ads1262_volatile_range,
+ .n_yes_ranges = ARRAY_SIZE(ads1262_volatile_range),
+};
+
+static const struct reg_default ads1262_reg_defaults[] = {
+ { ADS1262_POWER_REG, 0x11 },
+ { ADS1262_INTERFACE_REG, 0x05 },
+ { ADS1262_MODE0_REG, 0x00 },
+ { ADS1262_MODE1_REG, 0x80 },
+ { ADS1262_MODE2_REG, 0x04 },
+ { ADS1262_INPMUX_REG, 0x01 },
+ { ADS1262_OFCAL0_REG, 0x00 },
+ { ADS1262_OFCAL1_REG, 0x00 },
+ { ADS1262_OFCAL2_REG, 0x00 },
+ { ADS1262_FSCAL0_REG, 0x00 },
+ { ADS1262_FSCAL1_REG, 0x00 },
+ { ADS1262_FSCAL2_REG, 0x40 },
+ { ADS1262_IDACMUX_REG, 0xBB },
+ { ADS1262_IDACMAG_REG, 0x00 },
+ { ADS1262_REFMUX_REG, 0x00 },
+ { ADS1262_TDACP_REG, 0x00 },
+ { ADS1262_TDACN_REG, 0x00 },
+ { ADS1262_GPIOCON_REG, 0x00 },
+ { ADS1262_GPIODIR_REG, 0x00 },
+ { ADS1262_GPIODAT_REG, 0x00 },
+ { ADS1262_ADC2CFG_REG, 0x00 },
+ { ADS1262_ADC2MUX_REG, 0x01 },
+ { ADS1262_ADC2OFC0_REG, 0x00 },
+ { ADS1262_ADC2OFC1_REG, 0x00 },
+ { ADS1262_ADC2FSC0_REG, 0x00 },
+ { ADS1262_ADC2FSC1_REG, 0x40 },
+};
+
+static const struct regmap_config ads1262_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .wr_table = &ads1262_wr_table,
+ .rd_table = &ads1262_rd_table,
+ .volatile_table = &ads1262_volatile_table,
+ .reg_defaults = ads1262_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ads1262_reg_defaults),
+ .max_register = ADS1262_ADC2FSC1_REG,
+ .read_flag_mask = ADS1262_OPCODE_RREG,
+ .write_flag_mask = ADS1262_OPCODE_WREG,
+ .can_sleep = true,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+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;
+ struct spi_transfer xfer[3] = {
+ {
+ .tx_buf = reg_buf,
+ .len = reg_size,
+ },
+ {
+ .tx_buf = &st->tx[0],
+ .len = sizeof(st->tx[0]),
+ },
+ {
+ .rx_buf = val_buf,
+ .len = val_size,
+ },
+ };
+
+ /*
+ * It's necessary to lock the xfer_lock for the entirety of the
+ * operation.
+ *
+ * Not only because the tx buffer is shared with conversion read
+ * transfers, but also because some of these routines require no serial
+ * activity happen between the DRDY signal and the actual read operation
+ * (Section 9.4.7.1).
+ *
+ * The latter is specially important if the chip supports a second
+ * auxiliary ADC (Section 9.3.15).
+ */
+ guard(mutex)(&st->xfer_lock);
+
+ /*
+ * The register read operation (RREG) allows bulk reading registers and
+ * has the following structure (Section 9.5):
+ *
+ * Byte 1: ADS1262_OPCODE_RREG | REG
+ * Byte 2: Number of registers minus 1
+ * Byte 3+: Register data (MISO)
+ */
+ st->tx[0] = val_size - 1;
+
+ return spi_sync_transfer(st->spi, xfer, ARRAY_SIZE(xfer));
+}
+
+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;
+ struct spi_transfer xfer[3] = {
+ {
+ .tx_buf = reg_buf,
+ .len = reg_size,
+ },
+ {
+ .tx_buf = &st->tx[0],
+ .len = sizeof(st->tx[0]),
+ },
+ {
+ .tx_buf = val_buf,
+ .len = val_size,
+ },
+ };
+
+ /*
+ * It's necessary to lock the xfer_lock for the entirety of the
+ * operation.
+ *
+ * Not only because the tx buffer is shared with conversion read
+ * transfers, but also because some of these routines require no serial
+ * activity happen between the DRDY signal and the actual read operation
+ * (Section 9.4.7.1).
+ *
+ * The latter is specially important if the chip supports a second
+ * auxiliary ADC (Section 9.3.15).
+ */
+ guard(mutex)(&st->xfer_lock);
+
+ /*
+ * The register write operation (WREG) allows bulk writing registers
+ * and has the following structure (Section 9.5):
+ *
+ * Byte 1: ADS1262_OPCODE_WREG | REG
+ * Byte 2: Number of registers minus 1
+ * Byte 3+: Register data (MOSI)
+ */
+ st->tx[0] = val_size - 1;
+
+ return spi_sync_transfer(st->spi, xfer, ARRAY_SIZE(xfer));
+}
+
+static int ads1262_regmap_write(void *context, const void *data, size_t count)
+{
+ return ads1262_regmap_gather_write(context, data, 1, data + 1,
+ count - 1);
+}
+
+static const struct regmap_bus ads1262_regmap_bus = {
+ .read = ads1262_regmap_read,
+ .gather_write = ads1262_regmap_gather_write,
+ .write = ads1262_regmap_write,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static int ads1262_spi_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct iio_dev *indio_dev;
+ struct ads1262 *st;
+ char *name;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+ st->indio_dev = indio_dev;
+ init_completion(&st->drdy);
+ dev_set_drvdata(dev, st);
+
+ ret = devm_mutex_init(dev, &st->chan_lock);
+ if (ret)
+ return ret;
+ ret = devm_mutex_init(dev, &st->xfer_lock);
+ if (ret)
+ return ret;
+
+ st->regmap = devm_regmap_init(dev, &ads1262_regmap_bus, st,
+ &ads1262_regmap_config);
+ if (IS_ERR(st->regmap))
+ return PTR_ERR(st->regmap);
+
+ ret = ads1262_spi_message_setup(st);
+ if (ret)
+ return ret;
+
+ ret = ads1262_regulator_setup(st);
+ if (ret)
+ return ret;
+
+ ret = ads1262_clk_setup(st);
+ if (ret)
+ return ret;
+
+ ret = ads1262_gpio_setup(st);
+ if (ret)
+ return ret;
+
+ ret = ads1262_dev_configure(st);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to configure device\n");
+
+ ret = ads1262_read_chip_name(st, &name);
+ if (ret)
+ return ret;
+
+ indio_dev->name = name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ads1262_iio_info;
+ ret = ads1262_parse_channels(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ ads1262_trigger_handler,
+ &ads1262_buffer_ops);
+ if (ret)
+ return ret;
+
+ if (spi->irq > 0) {
+ st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d-drdy", name,
+ iio_device_id(indio_dev));
+ if (!st->trig)
+ return -ENOMEM;
+ iio_trigger_set_drvdata(st->trig, st);
+ ret = devm_iio_trigger_register(dev, st->trig);
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(dev, spi->irq, ads1262_irq_handler,
+ IRQF_NO_THREAD, name, st);
+ if (ret)
+ return ret;
+ }
+
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_autosuspend_delay(dev, 10000);
+ pm_runtime_set_active(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static int ads1262_runtime_suspend(struct device *dev)
+{
+ struct ads1262 *st = dev_get_drvdata(dev);
+
+ if (!st->reset_gpiod)
+ return 0;
+
+ regcache_cache_only(st->regmap, true);
+
+ return ads1262_dev_power_off(st);
+}
+
+static int ads1262_runtime_resume(struct device *dev)
+{
+ struct ads1262 *st = dev_get_drvdata(dev);
+ int ret;
+
+ if (!st->reset_gpiod)
+ return 0;
+
+ ret = ads1262_dev_power_on(st);
+ if (ret)
+ return ret;
+
+ regcache_cache_only(st->regmap, false);
+ regcache_mark_dirty(st->regmap);
+
+ return regcache_sync(st->regmap);
+}
+
+DEFINE_RUNTIME_DEV_PM_OPS(ads1262_runtime_pm, ads1262_runtime_suspend,
+ ads1262_runtime_resume, NULL);
+
+static const struct of_device_id ads1262_of_match[] = {
+ { .compatible = "ti,ads1262" },
+ { .compatible = "ti,ads1263" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ads1262_of_match);
+
+static const struct spi_device_id ads1262_spi_match[] = {
+ { "ads1262" },
+ { "ads1263" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ads1262_spi_match);
+
+static struct spi_driver ads1262_spi_driver = {
+ .driver = {
+ .name = "ads1262",
+ .of_match_table = ads1262_of_match,
+ .pm = pm_ptr(&ads1262_runtime_pm),
+ },
+ .probe = ads1262_spi_probe,
+ .id_table = ads1262_spi_match,
+};
+module_spi_driver(ads1262_spi_driver);
+
+MODULE_DESCRIPTION("Texas Instruments ADS1262 ADC driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kurt Borja <kuurtb@gmail.com>");
--
2.54.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 5/5] iio: adc: Add ti-ads1263-adc2 driver
2026-06-12 22:46 [PATCH 0/5] iio: adc: Add TI ADS126X ADC family support Kurt Borja
` (3 preceding siblings ...)
2026-06-12 22:46 ` [PATCH 4/5] iio: adc: ti-ads1262: Add calibration support Kurt Borja
@ 2026-06-12 22:46 ` Kurt Borja
2026-06-12 23:50 ` [PATCH 0/5] iio: adc: Add TI ADS126X ADC family support David Lechner
5 siblings, 0 replies; 9+ messages in thread
From: Kurt Borja @ 2026-06-12 22:46 UTC (permalink / raw)
To: Jonathan Cameron, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Linus Walleij, Bartosz Golaszewski
Cc: David Lechner, Nuno Sá, Andy Shevchenko, linux-iio,
devicetree, linux-kernel, linux-gpio, Kurt Borja,
Jonathan Cameron
The TI ADS1263 includes an auxiliary, 24-bit, delta-sigma ADC (ADC2).
ADC2 operation is independent of ADC1, with independent selections of
input channel, reference voltage, sample rate, and channel gain
Add support for this ADC as an independent IIO device, through the
auxiliary bus API.
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
MAINTAINERS | 2 +
drivers/iio/adc/Kconfig | 13 ++
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/ti-ads1262.c | 147 ++++++++++++
drivers/iio/adc/ti-ads1262.h | 39 ++++
drivers/iio/adc/ti-ads1263-adc2.c | 470 ++++++++++++++++++++++++++++++++++++++
6 files changed, 672 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index b874add5c924..53ef8fdb5cc9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -26675,6 +26675,8 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/adc/ti,ads1262.yaml
F: Documentation/devicetree/bindings/iio/adc/ti,ads1263-adc2.yaml
F: drivers/iio/adc/ti-ads1262.c
+F: drivers/iio/adc/ti-ads1262.h
+F: drivers/iio/adc/ti-ads1263-adc2.c
TI ADS7924 ADC DRIVER
M: Hugo Villeneuve <hvilleneuve@dimonoff.com>
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index b6c35d0c88ed..bb3a3f7742de 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -1809,6 +1809,19 @@ config TI_ADS1262
This driver can also be built as a module. If so, the module will be
called ti-ads1262.
+config TI_ADS1263_ADC2
+ tristate "Texas Instruments ADS1263 auxiliary ADC (ADC2) driver"
+ depends on TI_ADS1262
+ select AUXILIARY_BUS
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ If you say yes here you get support for Texas Instruments ADS1263
+ auxiliary ADC (ADC2).
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-ads1263-adc2.
+
config TI_ADS1298
tristate "Texas Instruments ADS1298"
depends on SPI
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index e0653820081e..9120c9392176 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -155,6 +155,7 @@ obj-$(CONFIG_TI_ADS1100) += ti-ads1100.o
obj-$(CONFIG_TI_ADS1119) += ti-ads1119.o
obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
obj-$(CONFIG_TI_ADS1262) += ti-ads1262.o
+obj-$(CONFIG_TI_ADS1263_ADC2) += ti-ads1263-adc2.o
obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o
obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o
obj-$(CONFIG_TI_ADS131M02) += ti-ads131m02.o
diff --git a/drivers/iio/adc/ti-ads1262.c b/drivers/iio/adc/ti-ads1262.c
index b33505e7fdc7..1a4b2f934d43 100644
--- a/drivers/iio/adc/ti-ads1262.c
+++ b/drivers/iio/adc/ti-ads1262.c
@@ -14,10 +14,12 @@
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/compiler_attributes.h>
+#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
+#include <linux/idr.h>
#include <linux/interrupt.h>
#include <linux/lockdep.h>
#include <linux/math.h>
@@ -42,6 +44,8 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
+#include "ti-ads1262.h"
+
/* Commands */
#define ADS1262_OPCODE_NOP 0x00
#define ADS1262_OPCODE_RESET 0x06
@@ -221,6 +225,8 @@
#define ADS1262_CALIB_WORD_MAX ((int)(GENMASK(22, 0)))
#define ADS1262_CALIB_WORD_MIN (-ADS1262_CALIB_WORD_MAX - 1)
+static DEFINE_IDA(ads1262_ida);
+
struct ads1262_channel {
/* MODE0 */
u8 conv_delay:4;
@@ -1123,6 +1129,143 @@ static irqreturn_t ads1262_trigger_handler(int irq, void *p)
return IRQ_HANDLED;
}
+static int ads1263_adc2_enable(struct ads1263_adc2_ctx *ctx,
+ const struct ads1263_adc2_channel *chan)
+{
+ struct ads1262 *st = ctx->chip;
+
+ /*
+ * The ads1263_adc2_channel struct can be written directly to the chip's
+ * configuration registers (ADC2CFG, ADC2MUX) in a single transfer, so
+ * it's necessary to assert it's size (2 bytes).
+ */
+ static_assert(sizeof(*chan) == 2);
+
+ guard(mutex)(&ctx->chan_lock);
+
+ return regmap_bulk_write(st->regmap, ADS1262_ADC2CFG_REG, chan,
+ sizeof(*chan));
+}
+
+static int ads1263_adc2_start(struct ads1263_adc2_ctx *ctx)
+{
+ struct ads1262 *st = ctx->chip;
+
+ return ads1262_dev_cmd(st, ADS1262_OPCODE_START2);
+}
+
+static int ads1263_adc2_stop(struct ads1263_adc2_ctx *ctx)
+{
+ struct ads1262 *st = ctx->chip;
+
+ return ads1262_dev_cmd(st, ADS1262_OPCODE_STOP2);
+}
+
+static int ads1263_adc2_read(struct ads1263_adc2_ctx *ctx, __be32 *val)
+{
+ struct ads1262 *st = ctx->chip;
+
+ return ads1262_dev_read_data_command(st, ADS1262_OPCODE_RDATA2, val);
+}
+
+static void ads1262_aux_device_destroy(void *data)
+{
+ struct auxiliary_device *adev = data;
+
+ auxiliary_device_delete(adev);
+ auxiliary_device_uninit(adev);
+}
+
+static void ads1262_aux_device_release(struct device *dev)
+{
+ struct auxiliary_device *adev = to_auxiliary_dev(dev);
+ struct ads1263_adc2_ctx *ctx =
+ container_of(adev, struct ads1263_adc2_ctx, adev);
+ struct fwnode_handle *node = adev->dev.fwnode;
+
+ mutex_destroy(&ctx->chan_lock);
+ kfree(ctx->channels);
+ ida_free(&ads1262_ida, adev->id);
+ kfree(ctx);
+ fwnode_handle_put(node);
+}
+
+static int ads1262_aux_device_setup(struct ads1262 *st)
+{
+ struct device *dev = &st->spi->dev;
+ struct ads1263_adc2_channel *chans;
+ struct auxiliary_device *adev;
+ struct ads1263_adc2_ctx *ctx;
+ struct fwnode_handle *node;
+ int id, ret;
+
+ node = device_get_named_child_node(dev, "adc");
+ if (!node)
+ return 0;
+
+ ctx = kzalloc_obj(*ctx);
+ if (!ctx) {
+ ret = -ENOMEM;
+ goto out_node_put;
+ }
+
+ id = ida_alloc(&ads1262_ida, GFP_KERNEL);
+ if (id < 0) {
+ ret = id;
+ goto out_free_adc2;
+ }
+
+ chans = kcalloc(st->num_channels, sizeof(*chans), GFP_KERNEL);
+ if (!chans) {
+ ret = -ENOMEM;
+ goto out_free_id;
+ }
+
+ for (unsigned int i = 0; i < st->num_channels; i++) {
+ chans[i].negative_input = st->channels[i].negative_input;
+ chans[i].positive_input = st->channels[i].positive_input;
+ }
+
+ ctx->chip = st;
+ ctx->num_channels = st->num_channels;
+ ctx->channels = chans;
+ ctx->enable = ads1263_adc2_enable;
+ ctx->start = ads1263_adc2_start;
+ ctx->stop = ads1263_adc2_stop;
+ ctx->read = ads1263_adc2_read;
+ mutex_init(&ctx->chan_lock);
+
+ adev = &ctx->adev;
+ adev->name = "ads1263_adc2";
+ adev->id = id;
+ adev->dev.release = ads1262_aux_device_release;
+ adev->dev.parent = dev;
+ device_set_node(&adev->dev, no_free_ptr(node));
+
+ ret = auxiliary_device_init(adev);
+ if (ret)
+ goto out_free_channels;
+
+ ret = auxiliary_device_add(adev);
+ if (ret) {
+ auxiliary_device_uninit(adev);
+ return ret;
+ }
+
+ return devm_add_action_or_reset(dev, ads1262_aux_device_destroy, adev);
+
+out_free_channels:
+ kfree(chans);
+out_free_id:
+ ida_free(&ads1262_ida, id);
+out_free_adc2:
+ kfree(ctx);
+out_node_put:
+ fwnode_handle_put(node);
+
+ return ret;
+}
+
static int ads1262_gpiochip_request(struct gpio_chip *gc, unsigned int offset)
{
struct ads1262 *st = gpiochip_get_data(gc);
@@ -1967,6 +2110,10 @@ static int ads1262_spi_probe(struct spi_device *spi)
if (ret)
return ret;
+ ret = ads1262_aux_device_setup(st);
+ if (ret)
+ return ret;
+
return devm_iio_device_register(dev, indio_dev);
}
diff --git a/drivers/iio/adc/ti-ads1262.h b/drivers/iio/adc/ti-ads1262.h
new file mode 100644
index 000000000000..98697d771da3
--- /dev/null
+++ b/drivers/iio/adc/ti-ads1262.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Texas Instruments ADS1262 ADC driver
+ *
+ * Copyright (C) 2025 Kurt Borja <kuurtb@gmail.com>
+ */
+
+#ifndef _ADS1262_H_
+#define _ADS1262_H_
+
+#include <linux/auxiliary_bus.h>
+#include <linux/types.h>
+
+struct ads1263_adc2_channel {
+ /* ADC2CFG */
+ u8 gain:3;
+ u8 refmux:3;
+ u8 data_rate:2;
+
+ /* ADC2MUX */
+ u8 negative_input:4;
+ u8 positive_input:4;
+};
+
+struct ads1263_adc2_ctx {
+ struct auxiliary_device adev;
+ struct ads1262 *chip;
+ /* Protects channel state */
+ struct mutex chan_lock;
+ struct ads1263_adc2_channel *channels;
+ unsigned int num_channels;
+ int (*enable)(struct ads1263_adc2_ctx *ctx,
+ const struct ads1263_adc2_channel *chan);
+ int (*start)(struct ads1263_adc2_ctx *ctx);
+ int (*stop)(struct ads1263_adc2_ctx *ctx);
+ int (*read)(struct ads1263_adc2_ctx *ctx, __be32 *val);
+};
+
+#endif
diff --git a/drivers/iio/adc/ti-ads1263-adc2.c b/drivers/iio/adc/ti-ads1263-adc2.c
new file mode 100644
index 000000000000..d21f08bbd9ee
--- /dev/null
+++ b/drivers/iio/adc/ti-ads1263-adc2.c
@@ -0,0 +1,470 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Texas Instruments ADS1263 auxiliary ADC (ADC2) driver
+ *
+ * Copyright (C) 2025 Kurt Borja <kuurtb@gmail.com>
+ */
+
+#include <linux/align.h>
+#include <linux/array_size.h>
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include "ti-ads1262.h"
+
+/* ADC2CFG REF2 constants */
+#define ADS1263_ADC2_REF2_INTER 0
+#define ADS1263_ADC2_REF2_COUNT 5
+
+struct ads1263_adc2 {
+ struct iio_dev *indio_dev;
+ struct ads1263_adc2_ctx *ctx;
+ u32 vref_uV;
+ u32 refmux;
+};
+
+static const int ads1263_adc2_gain_avail[] = {
+ 1, 2, 4, 8, 16, 32, 64, 128
+};
+
+static const int ads1263_adc2_data_rate_avail[] = {
+ 10, 100, 400, 800
+};
+
+static const unsigned long ads1263_adc2_latency_us[] = {
+ 121000, 31200, 8710, 4970
+};
+
+static const struct iio_chan_spec ads1263_adc2_iio_voltage_template = {
+ .type = IIO_VOLTAGE,
+ .indexed = true,
+ .scan_type = {
+ .format = IIO_SCAN_FORMAT_SIGNED_INT,
+ .realbits = 24,
+ .storagebits = 32,
+ .shift = 8,
+ .endianness = IIO_BE,
+ },
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_HARDWAREGAIN) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+};
+
+static int ads1263_adc2_channel_hot_reload(struct ads1263_adc2 *st,
+ const struct iio_chan_spec *chan)
+{
+ struct ads1263_adc2_ctx *ctx = st->ctx;
+ unsigned long i;
+ int ret;
+
+ /* Hot reloading is only required on buffer mode */
+ if (!iio_device_try_claim_buffer_mode(st->indio_dev))
+ return 0;
+
+ i = find_first_bit(st->indio_dev->active_scan_mask,
+ iio_get_masklength(st->indio_dev));
+ if (i != chan->scan_index) {
+ iio_device_release_direct(st->indio_dev);
+ return 0;
+ }
+
+ ret = ctx->enable(ctx, &ctx->channels[chan->scan_index]);
+
+ iio_device_release_buffer_mode(st->indio_dev);
+
+ return ret;
+}
+
+static int ads1263_adc2_channel_read(struct iio_dev *indio_dev,
+ struct ads1263_adc2_channel *chan_data,
+ __be32 *val)
+{
+ struct ads1263_adc2 *st = iio_priv(indio_dev);
+ struct ads1263_adc2_ctx *ctx = st->ctx;
+ struct device *dev = &ctx->adev.dev;
+ int ret;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev->parent, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
+
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ ret = ctx->enable(ctx, chan_data);
+ if (ret)
+ return ret;
+
+ ret = ctx->start(ctx);
+ if (ret)
+ return ret;
+
+ ret = ctx->stop(ctx);
+ if (ret)
+ return ret;
+
+ fsleep(ads1263_adc2_latency_us[chan_data->data_rate]);
+
+ return ctx->read(ctx, val);
+}
+
+static int ads1263_adc2_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ads1263_adc2 *st = iio_priv(indio_dev);
+ struct ads1263_adc2_ctx *ctx = st->ctx;
+ struct ads1263_adc2_channel *chan_data;
+ u8 realbits;
+ __be32 raw;
+ u32 cnv;
+ int ret;
+
+ chan_data = &st->ctx->channels[chan->scan_index];
+ realbits = chan->scan_type.realbits;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ads1263_adc2_channel_read(indio_dev, chan_data, &raw);
+ if (ret)
+ return ret;
+
+ cnv = be32_to_cpu(raw);
+ cnv >>= chan->scan_type.shift;
+ *val = sign_extend32(cnv, realbits - 1);
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ u64 divd, divr, tmp, rem;
+
+ mutex_lock(&ctx->chan_lock);
+ divd = st->vref_uV;
+ divr = BIT_ULL(chan_data->gain + realbits - 1) * 1000;
+ mutex_unlock(&ctx->chan_lock);
+
+ tmp = div64_u64(divd * 1000000000ULL, divr);
+ *val = div64_u64_rem(tmp, 1000000000ULL, &rem);
+ *val2 = rem;
+
+ return IIO_VAL_INT_PLUS_NANO;
+
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ mutex_lock(&ctx->chan_lock);
+ *val = ads1263_adc2_gain_avail[chan_data->gain];
+ mutex_unlock(&ctx->chan_lock);
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ mutex_lock(&ctx->chan_lock);
+ *val = ads1263_adc2_data_rate_avail[chan_data->data_rate];
+ mutex_unlock(&ctx->chan_lock);
+
+ return IIO_VAL_INT;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int
+ads1263_adc2_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, const int **vals,
+ int *type, int *length, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ *type = IIO_VAL_INT;
+ *vals = ads1263_adc2_gain_avail;
+ *length = ARRAY_SIZE(ads1263_adc2_gain_avail);
+ return IIO_AVAIL_LIST;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *type = IIO_VAL_INT;
+ *vals = ads1263_adc2_data_rate_avail;
+ *length = ARRAY_SIZE(ads1263_adc2_data_rate_avail);
+ return IIO_AVAIL_LIST;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int ads1263_adc2_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ads1263_adc2 *st = iio_priv(indio_dev);
+ struct ads1263_adc2_ctx *ctx = st->ctx;
+ struct ads1263_adc2_channel *chan_data;
+ unsigned int i;
+
+ chan_data = &ctx->channels[chan->scan_index];
+
+ switch (mask) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ for (i = 0; i < ARRAY_SIZE(ads1263_adc2_gain_avail); i++) {
+ if (val == ads1263_adc2_gain_avail[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(ads1263_adc2_gain_avail))
+ return -EINVAL;
+
+ mutex_lock(&ctx->chan_lock);
+ chan_data->gain = i;
+ mutex_unlock(&ctx->chan_lock);
+
+ return 0;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ for (i = 0; i < ARRAY_SIZE(ads1263_adc2_data_rate_avail); i++) {
+ if (val == ads1263_adc2_data_rate_avail[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(ads1263_adc2_data_rate_avail))
+ return -EINVAL;
+
+ mutex_lock(&ctx->chan_lock);
+ chan_data->data_rate = i;
+ mutex_unlock(&ctx->chan_lock);
+
+ return 0;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return ads1263_adc2_channel_hot_reload(st, chan);
+}
+
+static int ads1263_adc2_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_CONVDELAY:
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+}
+
+static const struct iio_info ads1263_adc2_iio_info = {
+ .read_raw = ads1263_adc2_read_raw,
+ .read_avail = ads1263_adc2_read_avail,
+ .write_raw = ads1263_adc2_write_raw,
+ .write_raw_get_fmt = ads1263_adc2_write_raw_get_fmt,
+};
+
+static int ads1263_adc2_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct ads1263_adc2 *st = iio_priv(indio_dev);
+ struct ads1263_adc2_ctx *ctx = st->ctx;
+ struct device *dev = &ctx->adev.dev;
+ unsigned long i;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev->parent);
+ if (ret)
+ return ret;
+
+ i = find_first_bit(indio_dev->active_scan_mask,
+ iio_get_masklength(indio_dev));
+ ret = ctx->enable(ctx, &ctx->channels[i]);
+ if (ret)
+ goto out_runtime_autosuspend;
+
+ ret = ctx->start(ctx);
+ if (ret)
+ goto out_runtime_autosuspend;
+
+ return 0;
+
+out_runtime_autosuspend:
+ pm_runtime_put_autosuspend(dev->parent);
+
+ return ret;
+}
+
+static int ads1263_adc2_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct ads1263_adc2 *st = iio_priv(indio_dev);
+ struct ads1263_adc2_ctx *ctx = st->ctx;
+ struct device *dev = &ctx->adev.dev;
+
+ ctx->stop(ctx);
+ pm_runtime_put_autosuspend(dev->parent);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ads1263_adc2_buffer_ops = {
+ .preenable = ads1263_adc2_buffer_preenable,
+ .postdisable = ads1263_adc2_buffer_postdisable,
+ .validate_scan_mask = iio_validate_scan_mask_onehot,
+};
+
+static irqreturn_t ads1263_adc2_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ads1263_adc2 *st = iio_priv(indio_dev);
+ struct ads1263_adc2_ctx *ctx = st->ctx;
+ struct {
+ __be32 conv;
+ aligned_s64 ts;
+ } scan = {};
+ int ret;
+
+ ret = ctx->read(ctx, &scan.conv);
+ if (ret)
+ goto out_notify_done;
+
+ iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan),
+ pf->timestamp);
+
+out_notify_done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ads1263_adc2_channels_setup(struct iio_dev *indio_dev)
+{
+ struct ads1263_adc2 *st = iio_priv(indio_dev);
+ struct device *dev = &st->ctx->adev.dev;
+ struct ads1263_adc2_ctx *ctx = st->ctx;
+ struct iio_chan_spec *chns;
+ unsigned int i;
+
+ /* Account for the timestamp channel */
+ chns = devm_kcalloc(dev, ctx->num_channels + 1, sizeof(*chns),
+ GFP_KERNEL);
+ if (!chns)
+ return -ENOMEM;
+
+ for (i = 0; i < ctx->num_channels; i++) {
+ guard(mutex)(&ctx->chan_lock);
+
+ ctx->channels[i].refmux = st->refmux;
+
+ chns[i] = ads1263_adc2_iio_voltage_template;
+ chns[i].scan_index = i;
+ chns[i].channel = ctx->channels[i].positive_input;
+ chns[i].channel2 = ctx->channels[i].negative_input;
+ chns[i].differential = true;
+ }
+
+ chns[i] = (struct iio_chan_spec)
+ IIO_CHAN_SOFT_TIMESTAMP(ctx->num_channels - 1);
+ chns[i].scan_index = i;
+
+ indio_dev->num_channels = ctx->num_channels + 1;
+ indio_dev->channels = chns;
+
+ return 0;
+}
+
+static int ads1263_adc2_regulator_setup(struct ads1263_adc2 *st)
+{
+ struct device *dev = &st->ctx->adev.dev;
+ const char *reg_id, *propname;
+ u32 refmux = 0;
+ int ret;
+
+ propname = "ti,refmux";
+ ret = device_property_read_u32(dev, propname, &refmux);
+ if (refmux >= ADS1263_ADC2_REF2_COUNT)
+ return dev_err_probe(dev, ret, "%s out of range\n", propname);
+ st->refmux = refmux;
+
+ if (refmux == ADS1263_ADC2_REF2_INTER) {
+ /* The internal voltage reference is 2.5 V */
+ st->vref_uV = 2500000;
+ return 0;
+ }
+
+ reg_id = "vref";
+ ret = devm_regulator_get_enable_read_voltage(dev, reg_id);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to get regulator %s\n",
+ reg_id);
+ st->vref_uV = ret;
+
+ return 0;
+}
+
+static int ads1263_adc2_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *id)
+{
+ struct ads1263_adc2_ctx *ctx =
+ container_of(auxdev, struct ads1263_adc2_ctx, adev);
+ struct device *dev = &auxdev->dev;
+ struct iio_dev *indio_dev;
+ struct ads1263_adc2 *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->ctx = ctx;
+ st->indio_dev = indio_dev;
+
+ ret = ads1263_adc2_regulator_setup(st);
+ if (ret)
+ return ret;
+
+ indio_dev->name = (char *)id->driver_data;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ads1263_adc2_iio_info;
+ ret = ads1263_adc2_channels_setup(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ ads1263_adc2_trigger_handler,
+ &ads1263_adc2_buffer_ops);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct auxiliary_device_id ads1263_adc2_auxiliary_match[] = {
+ { .name = "ti_ads1262.ads1263_adc2",
+ .driver_data = (kernel_ulong_t)"ads1263_adc2" },
+ { }
+};
+MODULE_DEVICE_TABLE(auxiliary, ads1263_adc2_auxiliary_match);
+
+static struct auxiliary_driver ads1263_adc2_driver = {
+ .name = "ads1263_adc2",
+ .probe = ads1263_adc2_probe,
+ .id_table = ads1263_adc2_auxiliary_match,
+};
+module_auxiliary_driver(ads1263_adc2_driver);
+
+MODULE_DESCRIPTION("Texas Instruments ADS1263 auxiliary ADC (ADC2) driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kurt Borja <kuurtb@gmail.com>");
--
2.54.0
^ permalink raw reply related [flat|nested] 9+ messages in thread