diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml index d00690a8d3fbd9..cc38617bb8295c 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml @@ -5,19 +5,30 @@ $id: http://devicetree.org/schemas/iio/adc/adi,ad4130.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Analog Devices AD4130 ADC device driver +title: Analog Devices AD4130 family ADCs maintainers: - Cosmin Tanislav description: | - Bindings for the Analog Devices AD4130 ADC. Datasheet can be found here: + Bindings for the Analog Devices AD4130 family ADCs. + Datasheets can be found here: + https://www.analog.com/media/en/technical-documentation/data-sheets/AD4129-4.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/AD4129-8.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/AD4130-4.pdf https://www.analog.com/media/en/technical-documentation/data-sheets/AD4130-8.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/AD4131-4.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/AD4131-8.pdf properties: compatible: enum: + - adi,ad4129-4 + - adi,ad4129-8 + - adi,ad4130-4 - adi,ad4130 + - adi,ad4131-4 + - adi,ad4131-8 reg: maxItems: 1 @@ -32,6 +43,10 @@ properties: interrupts: maxItems: 1 + description: | + Data Ready / FIFO interrupt. For devices with FIFO support, the + interrupt polarity specified here is inverted when the device enters + FIFO mode, and normal for data ready. interrupt-names: description: | diff --git a/arch/arm/boot/dts/xilinx/zynq-coraz7s-ad4129-4.dts b/arch/arm/boot/dts/xilinx/zynq-coraz7s-ad4129-4.dts new file mode 100644 index 00000000000000..fdfe4fc353745d --- /dev/null +++ b/arch/arm/boot/dts/xilinx/zynq-coraz7s-ad4129-4.dts @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices AD4129-4 + * https://www.analog.com/en/products/ad4129-4.html + * + * hdl_project: + * + * Copyright (C) 2026 Analog Devices, Inc. + */ +/dts-v1/; +#include "zynq-coraz7s.dtsi" +#include +#include + +&spi0 { + status = "okay"; + + ad4129_4@0 { + compatible = "adi,ad4129-4"; + reg = <0>; + + #address-cells = <1>; + #size-cells = <0>; + + spi-max-frequency = <5000000>; + interrupts = <85 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio0>; + + channel@0 { + reg = <0>; + + adi,reference-select = <2>; + + /* AIN0, AIN1 */ + diff-channels = <0 1>; + }; + + channel@1 { + reg = <1>; + + adi,reference-select = <2>; + + /* AIN4, AIN5 */ + diff-channels = <4 5>; + }; + + channel@2 { + reg = <2>; + + adi,reference-select = <2>; + + /* Temperature Sensor, DGND */ + diff-channels = <16 19>; + }; + + channel@3 { + reg = <3>; + + adi,reference-select = <2>; + + /* Internal reference, DGND */ + diff-channels = <18 19>; + }; + + channel@4 { + reg = <4>; + + adi,reference-select = <2>; + + /* DGND, DGND */ + diff-channels = <19 19>; + }; + }; +}; diff --git a/arch/arm/boot/dts/xilinx/zynq-coraz7s-ad4129-8.dts b/arch/arm/boot/dts/xilinx/zynq-coraz7s-ad4129-8.dts new file mode 100644 index 00000000000000..4604aa4a17bec7 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/zynq-coraz7s-ad4129-8.dts @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices AD4129-8 + * https://www.analog.com/en/products/ad4129-8.html + * + * hdl_project: + * + * Copyright (C) 2026 Analog Devices, Inc. + */ +/dts-v1/; +#include "zynq-coraz7s.dtsi" +#include +#include + +&spi0 { + status = "okay"; + + ad4129_8@0 { + compatible = "adi,ad4129-8"; + reg = <0>; + + #address-cells = <1>; + #size-cells = <0>; + + spi-max-frequency = <5000000>; + interrupts = <85 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio0>; + + channel@0 { + reg = <0>; + + adi,reference-select = <2>; + + /* AIN8, AIN9 */ + diff-channels = <8 9>; + }; + + channel@1 { + reg = <1>; + + adi,reference-select = <2>; + + /* AIN10, AIN11 */ + diff-channels = <10 11>; + }; + + channel@2 { + reg = <2>; + + adi,reference-select = <2>; + + /* Temperature Sensor, DGND */ + diff-channels = <16 19>; + }; + + channel@3 { + reg = <3>; + + adi,reference-select = <2>; + + /* Internal reference, DGND */ + diff-channels = <18 19>; + }; + + channel@4 { + reg = <4>; + + adi,reference-select = <2>; + + /* DGND, DGND */ + diff-channels = <19 19>; + }; + }; +}; diff --git a/arch/arm/boot/dts/xilinx/zynq-coraz7s-ad4130-4.dts b/arch/arm/boot/dts/xilinx/zynq-coraz7s-ad4130-4.dts new file mode 100644 index 00000000000000..f1879326224988 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/zynq-coraz7s-ad4130-4.dts @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices AD4130-4 + * https://www.analog.com/en/products/ad4130-4.html + * + * hdl_project: + * + * Copyright (C) 2026 Analog Devices, Inc. + */ +/dts-v1/; +#include "zynq-coraz7s.dtsi" +#include +#include + +&spi0 { + status = "okay"; + + ad4130_4@0 { + compatible = "adi,ad4130-4"; + reg = <0>; + + #address-cells = <1>; + #size-cells = <0>; + + spi-max-frequency = <5000000>; + interrupts = <85 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio0>; + + channel@0 { + reg = <0>; + + adi,reference-select = <2>; + + /* AIN0, AIN1 */ + diff-channels = <0 1>; + }; + + channel@1 { + reg = <1>; + + adi,reference-select = <2>; + + /* AIN4, AIN5 */ + diff-channels = <4 5>; + }; + + channel@2 { + reg = <2>; + + adi,reference-select = <2>; + + /* Temperature Sensor, DGND */ + diff-channels = <16 19>; + }; + + channel@3 { + reg = <3>; + + adi,reference-select = <2>; + + /* Internal reference, DGND */ + diff-channels = <18 19>; + }; + + channel@4 { + reg = <4>; + + adi,reference-select = <2>; + + /* DGND, DGND */ + diff-channels = <19 19>; + }; + }; +}; diff --git a/arch/arm/boot/dts/xilinx/zynq-coraz7s-ad4130-8.dts b/arch/arm/boot/dts/xilinx/zynq-coraz7s-ad4130-8.dts new file mode 100644 index 00000000000000..f95380c2429741 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/zynq-coraz7s-ad4130-8.dts @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices AD4130-8 + * https://www.analog.com/en/products/ad4130-8.html + * + * hdl_project: + * + * Copyright (C) 2026 Analog Devices, Inc. + */ +/dts-v1/; +#include "zynq-coraz7s.dtsi" +#include +#include + +&spi0 { + status = "okay"; + + ad4130@0 { + compatible = "adi,ad4130"; + reg = <0>; + + #address-cells = <1>; + #size-cells = <0>; + + spi-max-frequency = <5000000>; + interrupts = <85 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio0>; + + channel@0 { + reg = <0>; + + adi,reference-select = <2>; + + /* AIN8, AIN9 */ + diff-channels = <8 9>; + }; + + channel@1 { + reg = <1>; + + adi,reference-select = <2>; + + /* AIN10, AIN11 */ + diff-channels = <10 11>; + }; + + channel@2 { + reg = <2>; + + adi,reference-select = <2>; + + /* Temperature Sensor, DGND */ + diff-channels = <16 19>; + }; + + channel@3 { + reg = <3>; + + adi,reference-select = <2>; + + /* Internal reference, DGND */ + diff-channels = <18 19>; + }; + + channel@4 { + reg = <4>; + + adi,reference-select = <2>; + + /* DGND, DGND */ + diff-channels = <19 19>; + }; + }; +}; diff --git a/arch/arm/boot/dts/xilinx/zynq-coraz7s-ad4131-4.dts b/arch/arm/boot/dts/xilinx/zynq-coraz7s-ad4131-4.dts new file mode 100644 index 00000000000000..30ef28c705bc99 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/zynq-coraz7s-ad4131-4.dts @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices AD4131-4 + * https://www.analog.com/en/products/ad4131-4.html + * + * hdl_project: + * + * Copyright (C) 2026 Analog Devices, Inc. + */ +/dts-v1/; +#include "zynq-coraz7s.dtsi" +#include +#include + +&spi0 { + status = "okay"; + + ad4131_4@0 { + compatible = "adi,ad4131-4"; + reg = <0>; + + #address-cells = <1>; + #size-cells = <0>; + + spi-max-frequency = <5000000>; + interrupts = <85 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio0>; + + channel@0 { + reg = <0>; + + adi,reference-select = <2>; + + /* AIN0, AIN1 */ + diff-channels = <0 1>; + }; + + channel@1 { + reg = <1>; + + adi,reference-select = <2>; + + /* AIN4, AIN5 */ + diff-channels = <4 5>; + }; + + channel@2 { + reg = <2>; + + adi,reference-select = <2>; + + /* Temperature Sensor, DGND */ + diff-channels = <16 19>; + }; + + channel@3 { + reg = <3>; + + adi,reference-select = <2>; + + /* Internal reference, DGND */ + diff-channels = <18 19>; + }; + + channel@4 { + reg = <4>; + + adi,reference-select = <2>; + + /* DGND, DGND */ + diff-channels = <19 19>; + }; + }; +}; diff --git a/arch/arm/boot/dts/xilinx/zynq-coraz7s-ad4131-8.dts b/arch/arm/boot/dts/xilinx/zynq-coraz7s-ad4131-8.dts new file mode 100644 index 00000000000000..95c506bb5558c9 --- /dev/null +++ b/arch/arm/boot/dts/xilinx/zynq-coraz7s-ad4131-8.dts @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices AD4131-8 + * https://www.analog.com/en/products/ad4131-8.html + * + * hdl_project: + * + * Copyright (C) 2026 Analog Devices, Inc. + */ +/dts-v1/; +#include "zynq-coraz7s.dtsi" +#include +#include + +&spi0 { + status = "okay"; + + ad4131_8@0 { + compatible = "adi,ad4131-8"; + reg = <0>; + + #address-cells = <1>; + #size-cells = <0>; + + spi-max-frequency = <5000000>; + interrupts = <85 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio0>; + + channel@0 { + reg = <0>; + + adi,reference-select = <2>; + + /* AIN8, AIN9 */ + diff-channels = <8 9>; + }; + + channel@1 { + reg = <1>; + + adi,reference-select = <2>; + + /* AIN10, AIN11 */ + diff-channels = <10 11>; + }; + + channel@2 { + reg = <2>; + + adi,reference-select = <2>; + + /* Temperature Sensor, DGND */ + diff-channels = <16 19>; + }; + + channel@3 { + reg = <3>; + + adi,reference-select = <2>; + + /* Internal reference, DGND */ + diff-channels = <18 19>; + }; + + channel@4 { + reg = <4>; + + adi,reference-select = <2>; + + /* DGND, DGND */ + diff-channels = <19 19>; + }; + }; +}; diff --git a/drivers/iio/adc/ad4130.c b/drivers/iio/adc/ad4130.c index 712f95f53c9ecd..3df29ddec7dd46 100644 --- a/drivers/iio/adc/ad4130.c +++ b/drivers/iio/adc/ad4130.c @@ -6,8 +6,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -20,6 +22,7 @@ #include #include #include +#include #include #include @@ -29,6 +32,9 @@ #include #include #include +#include +#include +#include #define AD4130_NAME "ad4130" @@ -39,6 +45,7 @@ #define AD4130_ADC_CONTROL_REG 0x01 #define AD4130_ADC_CONTROL_BIPOLAR_MASK BIT(14) #define AD4130_ADC_CONTROL_INT_REF_VAL_MASK BIT(13) +#define AD4130_ADC_CONTROL_CONT_READ_MASK BIT(11) #define AD4130_INT_REF_2_5V 2500000 #define AD4130_INT_REF_1_25V 1250000 #define AD4130_ADC_CONTROL_CSB_EN_MASK BIT(9) @@ -53,7 +60,9 @@ #define AD4130_IO_CONTROL_REG 0x03 #define AD4130_IO_CONTROL_INT_PIN_SEL_MASK GENMASK(9, 8) #define AD4130_IO_CONTROL_GPIO_DATA_MASK GENMASK(7, 4) +#define AD4130_4_IO_CONTROL_GPIO_DATA_MASK GENMASK(7, 6) #define AD4130_IO_CONTROL_GPIO_CTRL_MASK GENMASK(3, 0) +#define AD4130_4_IO_CONTROL_GPIO_CTRL_MASK GENMASK(3, 2) #define AD4130_VBIAS_REG 0x04 @@ -124,6 +133,28 @@ #define AD4130_INVALID_SLOT -1 +static const unsigned int ad4129_reg_size[] = { + [AD4130_STATUS_REG] = 1, + [AD4130_ADC_CONTROL_REG] = 2, + [AD4130_DATA_REG] = 2, + [AD4130_IO_CONTROL_REG] = 2, + [AD4130_VBIAS_REG] = 2, + [AD4130_ID_REG] = 1, + [AD4130_ERROR_REG] = 2, + [AD4130_ERROR_EN_REG] = 2, + [AD4130_MCLK_COUNT_REG] = 1, + [AD4130_CHANNEL_X_REG(0) ... AD4130_CHANNEL_X_REG(AD4130_MAX_CHANNELS - 1)] = 3, + [AD4130_CONFIG_X_REG(0) ... AD4130_CONFIG_X_REG(AD4130_MAX_SETUPS - 1)] = 2, + [AD4130_FILTER_X_REG(0) ... AD4130_FILTER_X_REG(AD4130_MAX_SETUPS - 1)] = 3, + [AD4130_OFFSET_X_REG(0) ... AD4130_OFFSET_X_REG(AD4130_MAX_SETUPS - 1)] = 2, + [AD4130_GAIN_X_REG(0) ... AD4130_GAIN_X_REG(AD4130_MAX_SETUPS - 1)] = 2, + [AD4130_MISC_REG] = 2, + [AD4130_FIFO_CONTROL_REG] = 3, + [AD4130_FIFO_STATUS_REG] = 1, + [AD4130_FIFO_THRESHOLD_REG] = 3, + [AD4130_FIFO_DATA_REG] = 2, +}; + static const unsigned int ad4130_reg_size[] = { [AD4130_STATUS_REG] = 1, [AD4130_ADC_CONTROL_REG] = 2, @@ -146,6 +177,24 @@ static const unsigned int ad4130_reg_size[] = { [AD4130_FIFO_DATA_REG] = 3, }; +static const unsigned int ad4131_reg_size[] = { + [AD4130_STATUS_REG] = 1, + [AD4130_ADC_CONTROL_REG] = 2, + [AD4130_DATA_REG] = 2, + [AD4130_IO_CONTROL_REG] = 2, + [AD4130_VBIAS_REG] = 2, + [AD4130_ID_REG] = 1, + [AD4130_ERROR_REG] = 2, + [AD4130_ERROR_EN_REG] = 2, + [AD4130_MCLK_COUNT_REG] = 1, + [AD4130_CHANNEL_X_REG(0) ... AD4130_CHANNEL_X_REG(AD4130_MAX_CHANNELS - 1)] = 3, + [AD4130_CONFIG_X_REG(0) ... AD4130_CONFIG_X_REG(AD4130_MAX_SETUPS - 1)] = 2, + [AD4130_FILTER_X_REG(0) ... AD4130_FILTER_X_REG(AD4130_MAX_SETUPS - 1)] = 3, + [AD4130_OFFSET_X_REG(0) ... AD4130_OFFSET_X_REG(AD4130_MAX_SETUPS - 1)] = 2, + [AD4130_GAIN_X_REG(0) ... AD4130_GAIN_X_REG(AD4130_MAX_SETUPS - 1)] = 2, + [AD4130_MISC_REG] = 2, +}; + enum ad4130_int_ref_val { AD4130_INT_REF_VAL_2_5V, AD4130_INT_REF_VAL_1_25V, @@ -223,6 +272,28 @@ enum ad4130_pin_function { AD4130_PIN_FN_VBIAS = BIT(3), }; +/* Pin mapping for AIN0..AIN7, VBIAS_0..VBIAS_7 */ +static const u8 ad4130_4_pin_map[] = { + 0x00, 0x01, 0x04, 0x05, 0x0A, 0x0B, 0x0E, 0x0F, /* 0 - 7 */ +}; + +/* Pin mapping for AIN0..AIN15, VBIAS_0..VBIAS_15 */ +static const u8 ad4130_8_pin_map[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0 - 7 */ + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /* 8 - 15 */ +}; + +struct ad4130_chip_info { + const char *name; + unsigned int max_analog_pins; + unsigned int num_gpios; + const struct iio_info *info; + const unsigned int *reg_size; + const unsigned int reg_size_length; + const u8 *pin_map; + bool has_fifo; +}; + /* * If you make adaptations in this struct, you most likely also have to adapt * ad4130_setup_info_eq(), too. @@ -267,6 +338,7 @@ struct ad4130_state { struct regmap *regmap; struct spi_device *spi; struct clk *mclk; + const struct ad4130_chip_info *chip_info; struct regulator_bulk_data regulators[4]; u32 irq_trigger; u32 inv_irq_trigger; @@ -293,6 +365,7 @@ struct ad4130_state { u32 mclk_sel; bool int_ref_en; bool bipolar; + bool buffer_wait_for_irq; unsigned int num_enabled_channels; unsigned int effective_watermark; @@ -300,6 +373,7 @@ struct ad4130_state { struct spi_message fifo_msg; struct spi_transfer fifo_xfer[2]; + struct iio_trigger *trig; /* * DMA (thus cache coherency maintenance) requires any transfer @@ -311,9 +385,13 @@ struct ad4130_state { u8 reg_write_tx_buf[4]; u8 reg_read_tx_buf[1]; u8 reg_read_rx_buf[3]; - u8 fifo_tx_buf[2]; - u8 fifo_rx_buf[AD4130_FIFO_SIZE * - AD4130_FIFO_MAX_SAMPLE_SIZE]; + union { + struct { + u8 fifo_tx_buf[2]; + u8 fifo_rx_buf[AD4130_FIFO_SIZE * AD4130_FIFO_MAX_SAMPLE_SIZE]; + }; + IIO_DECLARE_BUFFER_WITH_TS(u32, scan_channels, AD4130_MAX_CHANNELS); + }; }; static const char * const ad4130_int_pin_names[] = { @@ -393,10 +471,10 @@ static const char * const ad4130_filter_modes_str[] = { static int ad4130_get_reg_size(struct ad4130_state *st, unsigned int reg, unsigned int *size) { - if (reg >= ARRAY_SIZE(ad4130_reg_size)) + if (reg >= st->chip_info->reg_size_length) return -EINVAL; - *size = ad4130_reg_size[reg]; + *size = st->chip_info->reg_size[reg]; return 0; } @@ -504,7 +582,8 @@ static int ad4130_gpio_init_valid_mask(struct gpio_chip *gc, /* * Output-only GPIO functionality is available on pins AIN2 through - * AIN5. If these pins are used for anything else, do not expose them. + * AIN5 for some parts and AIN2 through AIN3 for others. If these pins + * are used for anything else, do not expose them. */ for (i = 0; i < ngpios; i++) { unsigned int pin = i + AD4130_AIN2_P1; @@ -525,8 +604,14 @@ static void ad4130_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) { struct ad4130_state *st = gpiochip_get_data(gc); - unsigned int mask = FIELD_PREP(AD4130_IO_CONTROL_GPIO_DATA_MASK, - BIT(offset)); + unsigned int mask; + + if (st->chip_info->num_gpios == AD4130_MAX_GPIOS) + mask = FIELD_PREP(AD4130_IO_CONTROL_GPIO_DATA_MASK, + BIT(offset)); + else + mask = FIELD_PREP(AD4130_4_IO_CONTROL_GPIO_DATA_MASK, + BIT(offset)); regmap_update_bits(st->regmap, AD4130_IO_CONTROL_REG, mask, value ? mask : 0); @@ -587,10 +672,43 @@ static irqreturn_t ad4130_irq_handler(int irq, void *private) struct iio_dev *indio_dev = private; struct ad4130_state *st = iio_priv(indio_dev); - if (iio_buffer_enabled(indio_dev)) - ad4130_push_fifo_data(indio_dev); - else + if (iio_buffer_enabled(indio_dev)) { + if (st->chip_info->has_fifo) + ad4130_push_fifo_data(indio_dev); + else if (st->buffer_wait_for_irq) + complete(&st->completion); + else + iio_trigger_poll(st->trig); + } else { complete(&st->completion); + } + + return IRQ_HANDLED; +} + +static irqreturn_t ad4130_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad4130_state *st = iio_priv(indio_dev); + unsigned int data_reg_size = ad4130_data_reg_size(st); + struct spi_transfer xfer = { }; + unsigned int num_en_chn; + int ret; + + num_en_chn = bitmap_weight(indio_dev->active_scan_mask, + iio_get_masklength(indio_dev)); + xfer.rx_buf = st->scan_channels; + xfer.len = data_reg_size * num_en_chn; + ret = spi_sync_transfer(st->spi, &xfer, 1); + if (ret < 0) + goto err_out; + + iio_push_to_buffers_with_timestamp(indio_dev, &st->scan_channels, + iio_get_time_ns(indio_dev)); + +err_out: + iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; } @@ -1279,6 +1397,79 @@ static const struct iio_info ad4130_info = { .debugfs_reg_access = ad4130_reg_access, }; +static const struct iio_info ad4131_info = { + .read_raw = ad4130_read_raw, + .read_avail = ad4130_read_avail, + .write_raw_get_fmt = ad4130_write_raw_get_fmt, + .write_raw = ad4130_write_raw, + .update_scan_mode = ad4130_update_scan_mode, + .debugfs_reg_access = ad4130_reg_access, +}; + +static const struct ad4130_chip_info ad4129_4_chip_info = { + .name = "ad4129-4", + .max_analog_pins = 8, + .num_gpios = 2, + .info = &ad4130_info, + .reg_size = ad4129_reg_size, + .reg_size_length = ARRAY_SIZE(ad4129_reg_size), + .has_fifo = true, + .pin_map = ad4130_4_pin_map, +}; + +static const struct ad4130_chip_info ad4129_8_chip_info = { + .name = "ad4129-8", + .max_analog_pins = 16, + .num_gpios = 4, + .info = &ad4130_info, + .reg_size = ad4129_reg_size, + .reg_size_length = ARRAY_SIZE(ad4129_reg_size), + .has_fifo = true, + .pin_map = ad4130_8_pin_map, +}; + +static const struct ad4130_chip_info ad4130_4_chip_info = { + .name = "ad4130-4", + .max_analog_pins = 16, + .num_gpios = 2, + .info = &ad4130_info, + .reg_size = ad4130_reg_size, + .reg_size_length = ARRAY_SIZE(ad4130_reg_size), + .has_fifo = true, + .pin_map = ad4130_4_pin_map, +}; + +static const struct ad4130_chip_info ad4130_8_chip_info = { + .name = "ad4130-8", + .max_analog_pins = 16, + .num_gpios = 4, + .info = &ad4130_info, + .reg_size = ad4130_reg_size, + .reg_size_length = ARRAY_SIZE(ad4130_reg_size), + .has_fifo = true, + .pin_map = ad4130_8_pin_map, +}; + +static const struct ad4130_chip_info ad4131_4_chip_info = { + .name = "ad4131-4", + .max_analog_pins = 8, + .num_gpios = 2, + .info = &ad4131_info, + .reg_size = ad4131_reg_size, + .reg_size_length = ARRAY_SIZE(ad4131_reg_size), + .pin_map = ad4130_4_pin_map, +}; + +static const struct ad4130_chip_info ad4131_8_chip_info = { + .name = "ad4131-8", + .max_analog_pins = 16, + .num_gpios = 4, + .info = &ad4131_info, + .reg_size = ad4131_reg_size, + .reg_size_length = ARRAY_SIZE(ad4131_reg_size), + .pin_map = ad4130_8_pin_map, +}; + static int ad4130_buffer_postenable(struct iio_dev *indio_dev) { struct ad4130_state *st = iio_priv(indio_dev); @@ -1286,44 +1477,89 @@ static int ad4130_buffer_postenable(struct iio_dev *indio_dev) guard(mutex)(&st->lock); - ret = ad4130_set_watermark_interrupt_en(st, true); - if (ret) - return ret; + if (st->chip_info->has_fifo) { + ret = ad4130_set_watermark_interrupt_en(st, true); + if (ret) + return ret; - ret = irq_set_irq_type(st->spi->irq, st->inv_irq_trigger); - if (ret) - return ret; + ret = irq_set_irq_type(st->spi->irq, st->inv_irq_trigger); + if (ret) + return ret; + + ret = ad4130_set_fifo_mode(st, AD4130_FIFO_MODE_WM); + if (ret) + return ret; + } - ret = ad4130_set_fifo_mode(st, AD4130_FIFO_MODE_WM); + ret = ad4130_set_mode(st, AD4130_MODE_CONTINUOUS); if (ret) return ret; - return ad4130_set_mode(st, AD4130_MODE_CONTINUOUS); + /* + * When using triggered buffer, Entering continuous read mode must + * be the last command sent. No configuration changes are allowed until + * exiting this mode. + */ + if (!st->chip_info->has_fifo) { + ret = regmap_update_bits(st->regmap, AD4130_ADC_CONTROL_REG, + AD4130_ADC_CONTROL_CONT_READ_MASK, + FIELD_PREP(AD4130_ADC_CONTROL_CONT_READ_MASK, 1)); + if (ret) + return ret; + } + + return 0; } static int ad4130_buffer_predisable(struct iio_dev *indio_dev) { struct ad4130_state *st = iio_priv(indio_dev); unsigned int i; + u32 temp; int ret; guard(mutex)(&st->lock); + if (!st->chip_info->has_fifo) { + temp = 0x42; + reinit_completion(&st->completion); + + /* + * In continuous read mode, when all samples are read, the data + * ready signal returns high until the next conversion result is + * ready. To exit this mode, the command must be sent when data + * ready is low. In order to ensure that condition, wait for the + * next interrupt (when the new conversion is finished), allowing + * data ready to return low before sending the exit command. + */ + st->buffer_wait_for_irq = true; + if (!wait_for_completion_timeout(&st->completion, msecs_to_jiffies(1000))) + dev_warn(&st->spi->dev, "Conversion timed out\n"); + st->buffer_wait_for_irq = false; + + /* Perform a read data command to exit continuous read mode (0x42) */ + ret = spi_write(st->spi, &temp, 1); + if (ret) + return ret; + } + ret = ad4130_set_mode(st, AD4130_MODE_IDLE); if (ret) return ret; - ret = irq_set_irq_type(st->spi->irq, st->irq_trigger); - if (ret) - return ret; + if (st->chip_info->has_fifo) { + ret = irq_set_irq_type(st->spi->irq, st->irq_trigger); + if (ret) + return ret; - ret = ad4130_set_fifo_mode(st, AD4130_FIFO_MODE_DISABLED); - if (ret) - return ret; + ret = ad4130_set_fifo_mode(st, AD4130_FIFO_MODE_DISABLED); + if (ret) + return ret; - ret = ad4130_set_watermark_interrupt_en(st, false); - if (ret) - return ret; + ret = ad4130_set_watermark_interrupt_en(st, false); + if (ret) + return ret; + } /* * update_scan_mode() is not called in the disable path, disable all @@ -1398,6 +1634,34 @@ static const struct iio_dev_attr *ad4130_fifo_attributes[] = { NULL }; +static const struct iio_trigger_ops ad4130_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + +static int ad4130_triggered_buffer_setup(struct iio_dev *indio_dev) +{ + struct ad4130_state *st = iio_priv(indio_dev); + int ret; + + st->trig = devm_iio_trigger_alloc(indio_dev->dev.parent, "%s-dev%d", + indio_dev->name, iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ad4130_trigger_ops; + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = devm_iio_trigger_register(indio_dev->dev.parent, st->trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(st->trig); + + return devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev, + &iio_pollfunc_store_time, + &ad4130_trigger_handler, + &ad4130_buffer_ops); +} + static int _ad4130_find_table_index(const unsigned int *tbl, size_t len, unsigned int val) { @@ -1484,6 +1748,17 @@ static int ad4130_parse_fw_setup(struct ad4130_state *st, return 0; } +static unsigned int ad4130_translate_pin(struct ad4130_state *st, + unsigned int logical_pin) +{ + /* For analog input pins, use the chip-specific pin mapping */ + if (logical_pin < st->chip_info->max_analog_pins) + return st->chip_info->pin_map[logical_pin]; + + /* For internal channels, pass through unchanged */ + return logical_pin; +} + static int ad4130_validate_diff_channel(struct ad4130_state *st, u32 pin) { struct device *dev = &st->spi->dev; @@ -1492,7 +1767,7 @@ static int ad4130_validate_diff_channel(struct ad4130_state *st, u32 pin) return dev_err_probe(dev, -EINVAL, "Invalid differential channel %u\n", pin); - if (pin >= AD4130_MAX_ANALOG_PINS) + if (pin >= st->chip_info->max_analog_pins) return 0; if (st->pins_fn[pin] == AD4130_PIN_FN_SPECIAL) @@ -1524,7 +1799,7 @@ static int ad4130_validate_excitation_pin(struct ad4130_state *st, u32 pin) { struct device *dev = &st->spi->dev; - if (pin >= AD4130_MAX_ANALOG_PINS) + if (pin >= st->chip_info->max_analog_pins) return dev_err_probe(dev, -EINVAL, "Invalid excitation pin %u\n", pin); @@ -1542,7 +1817,7 @@ static int ad4130_validate_vbias_pin(struct ad4130_state *st, u32 pin) { struct device *dev = &st->spi->dev; - if (pin >= AD4130_MAX_ANALOG_PINS) + if (pin >= st->chip_info->max_analog_pins) return dev_err_probe(dev, -EINVAL, "Invalid vbias pin %u\n", pin); @@ -1718,7 +1993,7 @@ static int ad4310_parse_fw(struct iio_dev *indio_dev) ret = device_property_count_u32(dev, "adi,vbias-pins"); if (ret > 0) { - if (ret > AD4130_MAX_ANALOG_PINS) + if (ret > st->chip_info->max_analog_pins) return dev_err_probe(dev, -EINVAL, "Too many vbias pins %u\n", ret); @@ -1902,9 +2177,14 @@ static int ad4130_setup(struct iio_dev *indio_dev) * function of P2 takes priority over the GPIO out function. */ val = 0; - for (i = 0; i < AD4130_MAX_GPIOS; i++) - if (st->pins_fn[i + AD4130_AIN2_P1] == AD4130_PIN_FN_NONE) - val |= FIELD_PREP(AD4130_IO_CONTROL_GPIO_CTRL_MASK, BIT(i)); + for (i = 0; i < st->chip_info->num_gpios; i++) { + if (st->pins_fn[i + AD4130_AIN2_P1] == AD4130_PIN_FN_NONE) { + if (st->chip_info->num_gpios == 2) + val |= FIELD_PREP(AD4130_4_IO_CONTROL_GPIO_CTRL_MASK, BIT(i)); + else + val |= FIELD_PREP(AD4130_IO_CONTROL_GPIO_CTRL_MASK, BIT(i)); + } + } val |= FIELD_PREP(AD4130_IO_CONTROL_INT_PIN_SEL_MASK, st->int_pin_sel); @@ -1914,21 +2194,23 @@ static int ad4130_setup(struct iio_dev *indio_dev) val = 0; for (i = 0; i < st->num_vbias_pins; i++) - val |= BIT(st->vbias_pins[i]); + val |= BIT(ad4130_translate_pin(st, st->vbias_pins[i])); ret = regmap_write(st->regmap, AD4130_VBIAS_REG, val); if (ret) return ret; - ret = regmap_clear_bits(st->regmap, AD4130_FIFO_CONTROL_REG, - AD4130_FIFO_CONTROL_HEADER_MASK); - if (ret) - return ret; + if (st->chip_info->has_fifo) { + ret = regmap_clear_bits(st->regmap, AD4130_FIFO_CONTROL_REG, + AD4130_FIFO_CONTROL_HEADER_MASK); + if (ret) + return ret; - /* FIFO watermark interrupt starts out as enabled, disable it. */ - ret = ad4130_set_watermark_interrupt_en(st, false); - if (ret) - return ret; + /* FIFO watermark interrupt starts out as enabled, disable it. */ + ret = ad4130_set_watermark_interrupt_en(st, false); + if (ret) + return ret; + } /* Setup channels. */ for (i = 0; i < indio_dev->num_channels; i++) { @@ -1936,10 +2218,14 @@ static int ad4130_setup(struct iio_dev *indio_dev) struct iio_chan_spec *chan = &st->chans[i]; unsigned int val; - val = FIELD_PREP(AD4130_CHANNEL_AINP_MASK, chan->channel) | - FIELD_PREP(AD4130_CHANNEL_AINM_MASK, chan->channel2) | - FIELD_PREP(AD4130_CHANNEL_IOUT1_MASK, chan_info->iout0) | - FIELD_PREP(AD4130_CHANNEL_IOUT2_MASK, chan_info->iout1); + val = FIELD_PREP(AD4130_CHANNEL_AINP_MASK, + ad4130_translate_pin(st, chan->channel)) | + FIELD_PREP(AD4130_CHANNEL_AINM_MASK, + ad4130_translate_pin(st, chan->channel2)) | + FIELD_PREP(AD4130_CHANNEL_IOUT1_MASK, + ad4130_translate_pin(st, chan_info->iout0)) | + FIELD_PREP(AD4130_CHANNEL_IOUT2_MASK, + ad4130_translate_pin(st, chan_info->iout1)); ret = regmap_write(st->regmap, AD4130_CHANNEL_X_REG(i), val); if (ret) @@ -1982,26 +2268,30 @@ static int ad4130_probe(struct spi_device *spi) st = iio_priv(indio_dev); + st->chip_info = device_get_match_data(dev); + memset(st->reset_buf, 0xff, sizeof(st->reset_buf)); init_completion(&st->completion); mutex_init(&st->lock); st->spi = spi; - /* - * Xfer: [ XFR1 ] [ XFR2 ] - * Master: 0x7D N ...................... - * Slave: ...... DATA1 DATA2 ... DATAN - */ - st->fifo_tx_buf[0] = AD4130_COMMS_READ_MASK | AD4130_FIFO_DATA_REG; - st->fifo_xfer[0].tx_buf = st->fifo_tx_buf; - st->fifo_xfer[0].len = sizeof(st->fifo_tx_buf); - st->fifo_xfer[1].rx_buf = st->fifo_rx_buf; - spi_message_init_with_transfers(&st->fifo_msg, st->fifo_xfer, - ARRAY_SIZE(st->fifo_xfer)); - - indio_dev->name = AD4130_NAME; + if (st->chip_info->has_fifo) { + /* + * Xfer: [ XFR1 ] [ XFR2 ] + * Master: 0x7D N ...................... + * Slave: ...... DATA1 DATA2 ... DATAN + */ + st->fifo_tx_buf[0] = AD4130_COMMS_READ_MASK | AD4130_FIFO_DATA_REG; + st->fifo_xfer[0].tx_buf = st->fifo_tx_buf; + st->fifo_xfer[0].len = sizeof(st->fifo_tx_buf); + st->fifo_xfer[1].rx_buf = st->fifo_rx_buf; + spi_message_init_with_transfers(&st->fifo_msg, st->fifo_xfer, + ARRAY_SIZE(st->fifo_xfer)); + } + + indio_dev->name = st->chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->info = &ad4130_info; + indio_dev->info = st->chip_info->info; st->regmap = devm_regmap_init(dev, NULL, st, &ad4130_regmap_config); if (IS_ERR(st->regmap)) @@ -2045,9 +2335,9 @@ static int ad4130_probe(struct spi_device *spi) ad4130_fill_scale_tbls(st); st->gc.owner = THIS_MODULE; - st->gc.label = AD4130_NAME; + st->gc.label = st->chip_info->name; st->gc.base = -1; - st->gc.ngpio = AD4130_MAX_GPIOS; + st->gc.ngpio = st->chip_info->num_gpios; st->gc.parent = dev; st->gc.can_sleep = true; st->gc.init_valid_mask = ad4130_gpio_init_valid_mask; @@ -2058,9 +2348,12 @@ static int ad4130_probe(struct spi_device *spi) if (ret) return ret; - ret = devm_iio_kfifo_buffer_setup_ext(dev, indio_dev, - &ad4130_buffer_ops, - ad4130_fifo_attributes); + if (st->chip_info->has_fifo) + ret = devm_iio_kfifo_buffer_setup_ext(dev, indio_dev, + &ad4130_buffer_ops, + ad4130_fifo_attributes); + else + ret = ad4130_triggered_buffer_setup(indio_dev); if (ret) return ret; @@ -2070,40 +2363,75 @@ static int ad4130_probe(struct spi_device *spi) if (ret) return dev_err_probe(dev, ret, "Failed to request irq\n"); - /* - * When the chip enters FIFO mode, IRQ polarity is inverted. - * When the chip exits FIFO mode, IRQ polarity returns to normal. - * See datasheet pages: 65, FIFO Watermark Interrupt section, - * and 71, Bit Descriptions for STATUS Register, RDYB. - * Cache the normal and inverted IRQ triggers to set them when - * entering and exiting FIFO mode. - */ - st->irq_trigger = irq_get_trigger_type(spi->irq); - if (st->irq_trigger & IRQF_TRIGGER_RISING) - st->inv_irq_trigger = IRQF_TRIGGER_FALLING; - else if (st->irq_trigger & IRQF_TRIGGER_FALLING) - st->inv_irq_trigger = IRQF_TRIGGER_RISING; - else - return dev_err_probe(dev, -EINVAL, "Invalid irq flags: %u\n", - st->irq_trigger); + if (st->chip_info->has_fifo) { + /* + * When the chip enters FIFO mode, IRQ polarity is inverted. + * When the chip exits FIFO mode, IRQ polarity returns to normal. + * See datasheet pages: 65, FIFO Watermark Interrupt section, + * and 71, Bit Descriptions for STATUS Register, RDYB. + * Cache the normal and inverted IRQ triggers to set them when + * entering and exiting FIFO mode. + */ + st->irq_trigger = irq_get_trigger_type(spi->irq); + if (st->irq_trigger & IRQF_TRIGGER_RISING) + st->inv_irq_trigger = IRQF_TRIGGER_FALLING; + else if (st->irq_trigger & IRQF_TRIGGER_FALLING) + st->inv_irq_trigger = IRQF_TRIGGER_RISING; + else + return dev_err_probe(dev, -EINVAL, "Invalid irq flags: %u\n", + st->irq_trigger); + } return devm_iio_device_register(dev, indio_dev); } static const struct of_device_id ad4130_of_match[] = { + { + .compatible = "adi,ad4129-4", + .data = &ad4129_4_chip_info + }, + { + .compatible = "adi,ad4129-8", + .data = &ad4129_8_chip_info + }, + { + .compatible = "adi,ad4130-4", + .data = &ad4130_4_chip_info + }, { .compatible = "adi,ad4130", + .data = &ad4130_8_chip_info + }, + { + .compatible = "adi,ad4131-4", + .data = &ad4131_4_chip_info + }, + { + .compatible = "adi,ad4131-8", + .data = &ad4131_8_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad4130_of_match); +static const struct spi_device_id ad4130_id_table[] = { + { "ad4129-4", (kernel_ulong_t)&ad4129_4_chip_info }, + { "ad4129-8", (kernel_ulong_t)&ad4129_8_chip_info }, + { "ad4130-4", (kernel_ulong_t)&ad4130_4_chip_info }, + { "ad4130", (kernel_ulong_t)&ad4130_8_chip_info }, + { "ad4131-4", (kernel_ulong_t)&ad4131_4_chip_info }, + { "ad4131-8", (kernel_ulong_t)&ad4131_8_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4130_id_table); + static struct spi_driver ad4130_driver = { .driver = { .name = AD4130_NAME, .of_match_table = ad4130_of_match, }, .probe = ad4130_probe, + .id_table = ad4130_id_table, }; module_spi_driver(ad4130_driver);