summaryrefslogtreecommitdiffstats
path: root/drivers/iio/adc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio/adc')
-rw-r--r--drivers/iio/adc/Kconfig56
-rw-r--r--drivers/iio/adc/Makefile4
-rw-r--r--drivers/iio/adc/ad4000.c60
-rw-r--r--drivers/iio/adc/ad4030.c1230
-rw-r--r--drivers/iio/adc/ad4130.c139
-rw-r--r--drivers/iio/adc/ad4695.c1094
-rw-r--r--drivers/iio/adc/ad4851.c1315
-rw-r--r--drivers/iio/adc/ad7091r-base.c1
-rw-r--r--drivers/iio/adc/ad7124.c343
-rw-r--r--drivers/iio/adc/ad7173.c707
-rw-r--r--drivers/iio/adc/ad7191.c554
-rw-r--r--drivers/iio/adc/ad7192.c124
-rw-r--r--drivers/iio/adc/ad7266.c7
-rw-r--r--drivers/iio/adc/ad7298.c7
-rw-r--r--drivers/iio/adc/ad7380.c917
-rw-r--r--drivers/iio/adc/ad7476.c7
-rw-r--r--drivers/iio/adc/ad7606.c174
-rw-r--r--drivers/iio/adc/ad7606.h103
-rw-r--r--drivers/iio/adc/ad7606_bus_iface.h16
-rw-r--r--drivers/iio/adc/ad7606_par.c52
-rw-r--r--drivers/iio/adc/ad7606_spi.c137
-rw-r--r--drivers/iio/adc/ad7625.c13
-rw-r--r--drivers/iio/adc/ad7768-1.c47
-rw-r--r--drivers/iio/adc/ad7779.c101
-rw-r--r--drivers/iio/adc/ad7791.c31
-rw-r--r--drivers/iio/adc/ad7793.c80
-rw-r--r--drivers/iio/adc/ad7887.c7
-rw-r--r--drivers/iio/adc/ad7923.c7
-rw-r--r--drivers/iio/adc/ad7944.c314
-rw-r--r--drivers/iio/adc/ad799x.c14
-rw-r--r--drivers/iio/adc/ad9467.c23
-rw-r--r--drivers/iio/adc/ad_sigma_delta.c24
-rw-r--r--drivers/iio/adc/adi-axi-adc.c305
-rw-r--r--drivers/iio/adc/at91-sama5d2_adc.c54
-rw-r--r--drivers/iio/adc/dln2-adc.c7
-rw-r--r--drivers/iio/adc/max1027.c37
-rw-r--r--drivers/iio/adc/max11410.c72
-rw-r--r--drivers/iio/adc/max1363.c165
-rw-r--r--drivers/iio/adc/max34408.c1
-rw-r--r--drivers/iio/adc/pac1921.c1
-rw-r--r--drivers/iio/adc/rockchip_saradc.c42
-rw-r--r--drivers/iio/adc/rtq6056.c46
-rw-r--r--drivers/iio/adc/stm32-adc-core.c6
-rw-r--r--drivers/iio/adc/stm32-adc.c7
-rw-r--r--drivers/iio/adc/stm32-dfsdm-adc.c76
-rw-r--r--drivers/iio/adc/ti-adc084s021.c9
-rw-r--r--drivers/iio/adc/ti-adc108s102.c7
-rw-r--r--drivers/iio/adc/ti-adc161s626.c14
-rw-r--r--drivers/iio/adc/ti-ads1119.c17
-rw-r--r--drivers/iio/adc/ti-ads124s08.c2
-rw-r--r--drivers/iio/adc/ti-ads1298.c7
-rw-r--r--drivers/iio/adc/ti-ads131e08.c14
-rw-r--r--drivers/iio/adc/ti-ads7138.c749
-rw-r--r--drivers/iio/adc/ti-ads7924.c7
-rw-r--r--drivers/iio/adc/ti-tlc4541.c7
-rw-r--r--drivers/iio/adc/ti-tsc2046.c4
-rw-r--r--drivers/iio/adc/xilinx-xadc-core.c4
57 files changed, 8053 insertions, 1315 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 849c90203071..6529df1a498c 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -33,6 +33,20 @@ config AD4000
To compile this driver as a module, choose M here: the module will be
called ad4000.
+config AD4030
+ tristate "Analog Devices AD4030 ADC Driver"
+ depends on SPI
+ depends on GPIOLIB
+ select REGMAP
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices AD4030 and AD4630 high speed
+ SPI analog to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad4030.
+
config AD4130
tristate "Analog Device AD4130 ADC Driver"
depends on SPI
@@ -51,9 +65,11 @@ config AD4130
config AD4695
tristate "Analog Device AD4695 ADC Driver"
depends on SPI
- select REGMAP_SPI
select IIO_BUFFER
+ select IIO_BUFFER_DMAENGINE
select IIO_TRIGGERED_BUFFER
+ select REGMAP
+ select SPI_OFFLOAD
help
Say yes here to build support for Analog Devices AD4695 and similar
analog to digital converters (ADC).
@@ -61,6 +77,20 @@ config AD4695
To compile this driver as a module, choose M here: the module will be
called ad4695.
+config AD4851
+ tristate "Analog Device AD4851 DAS Driver"
+ depends on SPI
+ depends on PWM
+ select REGMAP_SPI
+ select IIO_BACKEND
+ help
+ Say yes here to build support for Analog Devices AD4851, AD4852,
+ AD4853, AD4854, AD4855, AD4856, AD4857, AD4858, AD4858I high speed
+ data acquisition system (DAS).
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad4851.
+
config AD7091R
tristate
@@ -112,6 +142,16 @@ config AD7173
To compile this driver as a module, choose M here: the module will be
called ad7173.
+config AD7191
+ tristate "Analog Devices AD7191 ADC driver"
+ depends on SPI
+ select AD_SIGMA_DELTA
+ help
+ Say yes here to build support for Analog Devices AD7191.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7191.
+
config AD7192
tristate "Analog Devices AD7192 and similar ADC driver"
depends on SPI
@@ -188,7 +228,9 @@ config AD7298
config AD7380
tristate "Analog Devices AD7380 ADC driver"
depends on SPI_MASTER
+ select SPI_OFFLOAD
select IIO_BUFFER
+ select IIO_BUFFER_DMAENGINE
select IIO_TRIGGER
select IIO_TRIGGERED_BUFFER
help
@@ -360,7 +402,9 @@ config AD7923
config AD7944
tristate "Analog Devices AD7944 and similar ADCs driver"
depends on SPI
+ select SPI_OFFLOAD
select IIO_BUFFER
+ select IIO_BUFFER_DMAENGINE
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Analog Devices
@@ -1467,6 +1511,16 @@ config TI_ADS1119
This driver can also be built as a module. If so, the module will be
called ti-ads1119.
+config TI_ADS7138
+ tristate "Texas Instruments ADS7128 and ADS7138 ADC driver"
+ depends on I2C
+ help
+ If you say yes here you get support for Texas Instruments ADS7128 and
+ ADS7138 8-channel A/D converters with 12-bit resolution.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-ads7138.
+
config TI_ADS7924
tristate "Texas Instruments ADS7924 ADC"
depends on I2C
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index ee19afba62b7..3e918c3eec69 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -7,13 +7,16 @@
obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
obj-$(CONFIG_AD4000) += ad4000.o
+obj-$(CONFIG_AD4030) += ad4030.o
obj-$(CONFIG_AD4130) += ad4130.o
obj-$(CONFIG_AD4695) += ad4695.o
+obj-$(CONFIG_AD4851) += ad4851.o
obj-$(CONFIG_AD7091R) += ad7091r-base.o
obj-$(CONFIG_AD7091R5) += ad7091r5.o
obj-$(CONFIG_AD7091R8) += ad7091r8.o
obj-$(CONFIG_AD7124) += ad7124.o
obj-$(CONFIG_AD7173) += ad7173.o
+obj-$(CONFIG_AD7191) += ad7191.o
obj-$(CONFIG_AD7192) += ad7192.o
obj-$(CONFIG_AD7266) += ad7266.o
obj-$(CONFIG_AD7280) += ad7280a.o
@@ -133,6 +136,7 @@ obj-$(CONFIG_TI_ADS1119) += ti-ads1119.o
obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o
obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o
+obj-$(CONFIG_TI_ADS7138) += ti-ads7138.o
obj-$(CONFIG_TI_ADS7924) += ti-ads7924.o
obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o
diff --git a/drivers/iio/adc/ad4000.c b/drivers/iio/adc/ad4000.c
index 1d556a842a68..4fe8dee48da9 100644
--- a/drivers/iio/adc/ad4000.c
+++ b/drivers/iio/adc/ad4000.c
@@ -535,12 +535,16 @@ static int ad4000_read_raw(struct iio_dev *indio_dev,
int *val2, long info)
{
struct ad4000_state *st = iio_priv(indio_dev);
+ int ret;
switch (info) {
case IIO_CHAN_INFO_RAW:
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev)
- return ad4000_single_conversion(indio_dev, chan, val);
- unreachable();
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad4000_single_conversion(indio_dev, chan, val);
+ iio_device_release_direct(indio_dev);
+ return ret;
case IIO_CHAN_INFO_SCALE:
*val = st->scale_tbl[st->span_comp][0];
*val2 = st->scale_tbl[st->span_comp][1];
@@ -585,36 +589,46 @@ static int ad4000_write_raw_get_fmt(struct iio_dev *indio_dev,
}
}
-static int ad4000_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan, int val, int val2,
- long mask)
+static int __ad4000_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val2)
{
struct ad4000_state *st = iio_priv(indio_dev);
unsigned int reg_val;
bool span_comp_en;
int ret;
- switch (mask) {
- case IIO_CHAN_INFO_SCALE:
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- guard(mutex)(&st->lock);
+ guard(mutex)(&st->lock);
+
+ ret = ad4000_read_reg(st, &reg_val);
+ if (ret < 0)
+ return ret;
+
+ span_comp_en = val2 == st->scale_tbl[1][1];
+ reg_val &= ~AD4000_CFG_SPAN_COMP;
+ reg_val |= FIELD_PREP(AD4000_CFG_SPAN_COMP, span_comp_en);
- ret = ad4000_read_reg(st, &reg_val);
- if (ret < 0)
- return ret;
+ ret = ad4000_write_reg(st, reg_val);
+ if (ret < 0)
+ return ret;
- span_comp_en = val2 == st->scale_tbl[1][1];
- reg_val &= ~AD4000_CFG_SPAN_COMP;
- reg_val |= FIELD_PREP(AD4000_CFG_SPAN_COMP, span_comp_en);
+ st->span_comp = span_comp_en;
+ return 0;
+}
- ret = ad4000_write_reg(st, reg_val);
- if (ret < 0)
- return ret;
+static int ad4000_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret;
- st->span_comp = span_comp_en;
- return 0;
- }
- unreachable();
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = __ad4000_write_raw(indio_dev, chan, val2);
+ iio_device_release_direct(indio_dev);
+ return ret;
default:
return -EINVAL;
}
diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c
new file mode 100644
index 000000000000..9a020680885d
--- /dev/null
+++ b/drivers/iio/adc/ad4030.c
@@ -0,0 +1,1230 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices AD4030 and AD4630 ADC family driver.
+ *
+ * Copyright 2024 Analog Devices, Inc.
+ * Copyright 2024 BayLibre, SAS
+ *
+ * based on code from:
+ * Analog Devices, Inc.
+ * Sergiu Cuciurean <sergiu.cuciurean@analog.com>
+ * Nuno Sa <nuno.sa@analog.com>
+ * Marcelo Schmitt <marcelo.schmitt@analog.com>
+ * Liviu Adace <liviu.adace@analog.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/unaligned.h>
+#include <linux/units.h>
+
+#define AD4030_REG_INTERFACE_CONFIG_A 0x00
+#define AD4030_REG_INTERFACE_CONFIG_A_SW_RESET (BIT(0) | BIT(7))
+#define AD4030_REG_INTERFACE_CONFIG_B 0x01
+#define AD4030_REG_DEVICE_CONFIG 0x02
+#define AD4030_REG_CHIP_TYPE 0x03
+#define AD4030_REG_PRODUCT_ID_L 0x04
+#define AD4030_REG_PRODUCT_ID_H 0x05
+#define AD4030_REG_CHIP_GRADE 0x06
+#define AD4030_REG_CHIP_GRADE_AD4030_24_GRADE 0x10
+#define AD4030_REG_CHIP_GRADE_AD4630_16_GRADE 0x03
+#define AD4030_REG_CHIP_GRADE_AD4630_24_GRADE 0x00
+#define AD4030_REG_CHIP_GRADE_AD4632_16_GRADE 0x05
+#define AD4030_REG_CHIP_GRADE_AD4632_24_GRADE 0x02
+#define AD4030_REG_CHIP_GRADE_MASK_CHIP_GRADE GENMASK(7, 3)
+#define AD4030_REG_SCRATCH_PAD 0x0A
+#define AD4030_REG_SPI_REVISION 0x0B
+#define AD4030_REG_VENDOR_L 0x0C
+#define AD4030_REG_VENDOR_H 0x0D
+#define AD4030_REG_STREAM_MODE 0x0E
+#define AD4030_REG_INTERFACE_CONFIG_C 0x10
+#define AD4030_REG_INTERFACE_STATUS_A 0x11
+#define AD4030_REG_EXIT_CFG_MODE 0x14
+#define AD4030_REG_EXIT_CFG_MODE_EXIT_MSK BIT(0)
+#define AD4030_REG_AVG 0x15
+#define AD4030_REG_AVG_MASK_AVG_SYNC BIT(7)
+#define AD4030_REG_AVG_MASK_AVG_VAL GENMASK(4, 0)
+#define AD4030_REG_OFFSET_X0_0 0x16
+#define AD4030_REG_OFFSET_X0_1 0x17
+#define AD4030_REG_OFFSET_X0_2 0x18
+#define AD4030_REG_OFFSET_X1_0 0x19
+#define AD4030_REG_OFFSET_X1_1 0x1A
+#define AD4030_REG_OFFSET_X1_2 0x1B
+#define AD4030_REG_OFFSET_BYTES_NB 3
+#define AD4030_REG_OFFSET_CHAN(ch) \
+ (AD4030_REG_OFFSET_X0_2 + (AD4030_REG_OFFSET_BYTES_NB * (ch)))
+#define AD4030_REG_GAIN_X0_LSB 0x1C
+#define AD4030_REG_GAIN_X0_MSB 0x1D
+#define AD4030_REG_GAIN_X1_LSB 0x1E
+#define AD4030_REG_GAIN_X1_MSB 0x1F
+#define AD4030_REG_GAIN_MAX_GAIN 1999970
+#define AD4030_REG_GAIN_BYTES_NB 2
+#define AD4030_REG_GAIN_CHAN(ch) \
+ (AD4030_REG_GAIN_X0_MSB + (AD4030_REG_GAIN_BYTES_NB * (ch)))
+#define AD4030_REG_MODES 0x20
+#define AD4030_REG_MODES_MASK_OUT_DATA_MODE GENMASK(2, 0)
+#define AD4030_REG_MODES_MASK_LANE_MODE GENMASK(7, 6)
+#define AD4030_REG_OSCILATOR 0x21
+#define AD4030_REG_IO 0x22
+#define AD4030_REG_IO_MASK_IO2X BIT(1)
+#define AD4030_REG_PAT0 0x23
+#define AD4030_REG_PAT1 0x24
+#define AD4030_REG_PAT2 0x25
+#define AD4030_REG_PAT3 0x26
+#define AD4030_REG_DIG_DIAG 0x34
+#define AD4030_REG_DIG_ERR 0x35
+
+/* Sequence starting with "1 0 1" to enable reg access */
+#define AD4030_REG_ACCESS 0xA0
+
+#define AD4030_MAX_IIO_SAMPLE_SIZE_BUFFERED BITS_TO_BYTES(64)
+#define AD4030_MAX_HARDWARE_CHANNEL_NB 2
+#define AD4030_MAX_IIO_CHANNEL_NB 5
+#define AD4030_SINGLE_COMMON_BYTE_CHANNELS_MASK 0b10
+#define AD4030_DUAL_COMMON_BYTE_CHANNELS_MASK 0b1100
+#define AD4030_GAIN_MIDLE_POINT 0x8000
+/*
+ * This accounts for 1 sample per channel plus one s64 for the timestamp,
+ * aligned on a s64 boundary
+ */
+#define AD4030_MAXIMUM_RX_BUFFER_SIZE \
+ (ALIGN(AD4030_MAX_IIO_SAMPLE_SIZE_BUFFERED * \
+ AD4030_MAX_HARDWARE_CHANNEL_NB, \
+ sizeof(s64)) + sizeof(s64))
+
+#define AD4030_VREF_MIN_UV (4096 * MILLI)
+#define AD4030_VREF_MAX_UV (5000 * MILLI)
+#define AD4030_VIO_THRESHOLD_UV (1400 * MILLI)
+#define AD4030_SPI_MAX_XFER_LEN 8
+#define AD4030_SPI_MAX_REG_XFER_SPEED (80 * MEGA)
+#define AD4030_TCNVH_NS 10
+#define AD4030_TCNVL_NS 20
+#define AD4030_TCYC_NS 500
+#define AD4030_TCYC_ADJUSTED_NS (AD4030_TCYC_NS - AD4030_TCNVL_NS)
+#define AD4030_TRESET_PW_NS 50
+#define AD4632_TCYC_NS 2000
+#define AD4632_TCYC_ADJUSTED_NS (AD4632_TCYC_NS - AD4030_TCNVL_NS)
+#define AD4030_TRESET_COM_DELAY_MS 750
+
+enum ad4030_out_mode {
+ AD4030_OUT_DATA_MD_DIFF,
+ AD4030_OUT_DATA_MD_16_DIFF_8_COM,
+ AD4030_OUT_DATA_MD_24_DIFF_8_COM,
+ AD4030_OUT_DATA_MD_30_AVERAGED_DIFF,
+ AD4030_OUT_DATA_MD_32_PATTERN,
+};
+
+enum {
+ AD4030_LANE_MD_1_PER_CH,
+ AD4030_LANE_MD_2_PER_CH,
+ AD4030_LANE_MD_4_PER_CH,
+ AD4030_LANE_MD_INTERLEAVED,
+};
+
+enum {
+ AD4030_SCAN_TYPE_NORMAL,
+ AD4030_SCAN_TYPE_AVG,
+};
+
+struct ad4030_chip_info {
+ const char *name;
+ const unsigned long *available_masks;
+ const struct iio_chan_spec channels[AD4030_MAX_IIO_CHANNEL_NB];
+ u8 grade;
+ u8 precision_bits;
+ /* Number of hardware channels */
+ int num_voltage_inputs;
+ unsigned int tcyc_ns;
+};
+
+struct ad4030_state {
+ struct spi_device *spi;
+ struct regmap *regmap;
+ const struct ad4030_chip_info *chip;
+ const struct iio_scan_type *current_scan_type;
+ struct gpio_desc *cnv_gpio;
+ int vref_uv;
+ int vio_uv;
+ int offset_avail[3];
+ unsigned int avg_log2;
+ enum ad4030_out_mode mode;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the transfer buffers
+ * to live in their own cache lines.
+ */
+ u8 tx_data[AD4030_SPI_MAX_XFER_LEN] __aligned(IIO_DMA_MINALIGN);
+ union {
+ u8 raw[AD4030_MAXIMUM_RX_BUFFER_SIZE];
+ struct {
+ s32 diff;
+ u8 common;
+ } single;
+ struct {
+ s32 diff[2];
+ u8 common[2];
+ } dual;
+ } rx_data;
+};
+
+/*
+ * For a chip with 2 hardware channel this will be used to create 2 common-mode
+ * channels:
+ * - voltage4
+ * - voltage5
+ * As the common-mode channels are after the differential ones, we compute the
+ * channel number like this:
+ * - _idx is the scan_index (the order in the output buffer)
+ * - _ch is the hardware channel number this common-mode channel is related
+ * - _idx - _ch gives us the number of channel in the chip
+ * - _idx - _ch * 2 is the starting number of the common-mode channels, since
+ * for each differential channel there is a common-mode channel
+ * - _idx - _ch * 2 + _ch gives the channel number for this specific common-mode
+ * channel
+ */
+#define AD4030_CHAN_CMO(_idx, _ch) { \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .address = (_ch), \
+ .channel = ((_idx) - (_ch)) * 2 + (_ch), \
+ .scan_index = (_idx), \
+ .scan_type = { \
+ .sign = 'u', \
+ .storagebits = 8, \
+ .realbits = 8, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+/*
+ * For a chip with 2 hardware channel this will be used to create 2 differential
+ * channels:
+ * - voltage0-voltage1
+ * - voltage2-voltage3
+ */
+#define AD4030_CHAN_DIFF(_idx, _scan_type) { \
+ .info_mask_shared_by_all = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS) | \
+ BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBBIAS) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE), \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .address = (_idx), \
+ .channel = (_idx) * 2, \
+ .channel2 = (_idx) * 2 + 1, \
+ .scan_index = (_idx), \
+ .differential = true, \
+ .has_ext_scan_type = 1, \
+ .ext_scan_type = _scan_type, \
+ .num_ext_scan_type = ARRAY_SIZE(_scan_type), \
+}
+
+static const int ad4030_average_modes[] = {
+ 1, 2, 4, 8, 16, 32, 64, 128,
+ 256, 512, 1024, 2048, 4096, 8192, 16384, 32768,
+ 65536,
+};
+
+static int ad4030_enter_config_mode(struct ad4030_state *st)
+{
+ st->tx_data[0] = AD4030_REG_ACCESS;
+
+ struct spi_transfer xfer = {
+ .tx_buf = st->tx_data,
+ .bits_per_word = 8,
+ .len = 1,
+ .speed_hz = AD4030_SPI_MAX_REG_XFER_SPEED,
+ };
+
+ return spi_sync_transfer(st->spi, &xfer, 1);
+}
+
+static int ad4030_exit_config_mode(struct ad4030_state *st)
+{
+ st->tx_data[0] = 0;
+ st->tx_data[1] = AD4030_REG_EXIT_CFG_MODE;
+ st->tx_data[2] = AD4030_REG_EXIT_CFG_MODE_EXIT_MSK;
+
+ struct spi_transfer xfer = {
+ .tx_buf = st->tx_data,
+ .bits_per_word = 8,
+ .len = 3,
+ .speed_hz = AD4030_SPI_MAX_REG_XFER_SPEED,
+ };
+
+ return spi_sync_transfer(st->spi, &xfer, 1);
+}
+
+static int ad4030_spi_read(void *context, const void *reg, size_t reg_size,
+ void *val, size_t val_size)
+{
+ int ret;
+ struct ad4030_state *st = context;
+ struct spi_transfer xfer = {
+ .tx_buf = st->tx_data,
+ .rx_buf = st->rx_data.raw,
+ .bits_per_word = 8,
+ .len = reg_size + val_size,
+ .speed_hz = AD4030_SPI_MAX_REG_XFER_SPEED,
+ };
+
+ if (xfer.len > sizeof(st->tx_data) ||
+ xfer.len > sizeof(st->rx_data.raw))
+ return -EINVAL;
+
+ ret = ad4030_enter_config_mode(st);
+ if (ret)
+ return ret;
+
+ memset(st->tx_data, 0, sizeof(st->tx_data));
+ memcpy(st->tx_data, reg, reg_size);
+
+ ret = spi_sync_transfer(st->spi, &xfer, 1);
+ if (ret)
+ return ret;
+
+ memcpy(val, &st->rx_data.raw[reg_size], val_size);
+
+ return ad4030_exit_config_mode(st);
+}
+
+static int ad4030_spi_write(void *context, const void *data, size_t count)
+{
+ int ret;
+ struct ad4030_state *st = context;
+ bool is_reset = count >= 3 &&
+ ((u8 *)data)[0] == 0 &&
+ ((u8 *)data)[1] == 0 &&
+ ((u8 *)data)[2] == 0x81;
+ struct spi_transfer xfer = {
+ .tx_buf = st->tx_data,
+ .bits_per_word = 8,
+ .len = count,
+ .speed_hz = AD4030_SPI_MAX_REG_XFER_SPEED,
+ };
+
+ if (count > sizeof(st->tx_data))
+ return -EINVAL;
+
+ ret = ad4030_enter_config_mode(st);
+ if (ret)
+ return ret;
+
+ memcpy(st->tx_data, data, count);
+
+ ret = spi_sync_transfer(st->spi, &xfer, 1);
+ if (ret)
+ return ret;
+
+ /*
+ * From datasheet: "After a [...] reset, no SPI commands or conversions
+ * can be started for 750us"
+ * After a reset we are in conversion mode, no need to exit config mode
+ */
+ if (is_reset) {
+ fsleep(750);
+ return 0;
+ }
+
+ return ad4030_exit_config_mode(st);
+}
+
+static const struct regmap_bus ad4030_regmap_bus = {
+ .read = ad4030_spi_read,
+ .write = ad4030_spi_write,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static const struct regmap_range ad4030_regmap_rd_range[] = {
+ regmap_reg_range(AD4030_REG_INTERFACE_CONFIG_A, AD4030_REG_CHIP_GRADE),
+ regmap_reg_range(AD4030_REG_SCRATCH_PAD, AD4030_REG_STREAM_MODE),
+ regmap_reg_range(AD4030_REG_INTERFACE_CONFIG_C,
+ AD4030_REG_INTERFACE_STATUS_A),
+ regmap_reg_range(AD4030_REG_EXIT_CFG_MODE, AD4030_REG_PAT3),
+ regmap_reg_range(AD4030_REG_DIG_DIAG, AD4030_REG_DIG_ERR),
+};
+
+static const struct regmap_range ad4030_regmap_wr_range[] = {
+ regmap_reg_range(AD4030_REG_CHIP_TYPE, AD4030_REG_CHIP_GRADE),
+ regmap_reg_range(AD4030_REG_SPI_REVISION, AD4030_REG_VENDOR_H),
+};
+
+static const struct regmap_access_table ad4030_regmap_rd_table = {
+ .yes_ranges = ad4030_regmap_rd_range,
+ .n_yes_ranges = ARRAY_SIZE(ad4030_regmap_rd_range),
+};
+
+static const struct regmap_access_table ad4030_regmap_wr_table = {
+ .no_ranges = ad4030_regmap_wr_range,
+ .n_no_ranges = ARRAY_SIZE(ad4030_regmap_wr_range),
+};
+
+static const struct regmap_config ad4030_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .read_flag_mask = 0x80,
+ .rd_table = &ad4030_regmap_rd_table,
+ .wr_table = &ad4030_regmap_wr_table,
+ .max_register = AD4030_REG_DIG_ERR,
+};
+
+static int ad4030_get_chan_scale(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2)
+{
+ struct ad4030_state *st = iio_priv(indio_dev);
+ const struct iio_scan_type *scan_type;
+
+ if (chan->differential) {
+ scan_type = iio_get_current_scan_type(indio_dev,
+ st->chip->channels);
+ *val = (st->vref_uv * 2) / MILLI;
+ *val2 = scan_type->realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+
+ *val = st->vref_uv / MILLI;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+}
+
+static int ad4030_get_chan_calibscale(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2)
+{
+ struct ad4030_state *st = iio_priv(indio_dev);
+ u16 gain;
+ int ret;
+
+ ret = regmap_bulk_read(st->regmap, AD4030_REG_GAIN_CHAN(chan->address),
+ st->rx_data.raw, AD4030_REG_GAIN_BYTES_NB);
+ if (ret)
+ return ret;
+
+ gain = get_unaligned_be16(st->rx_data.raw);
+
+ /* From datasheet: multiplied output = input × gain word/0x8000 */
+ *val = gain / AD4030_GAIN_MIDLE_POINT;
+ *val2 = mul_u64_u32_div(gain % AD4030_GAIN_MIDLE_POINT, NANO,
+ AD4030_GAIN_MIDLE_POINT);
+
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+/* Returns the offset where 1 LSB = (VREF/2^precision_bits - 1)/gain */
+static int ad4030_get_chan_calibbias(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val)
+{
+ struct ad4030_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = regmap_bulk_read(st->regmap,
+ AD4030_REG_OFFSET_CHAN(chan->address),
+ st->rx_data.raw, AD4030_REG_OFFSET_BYTES_NB);
+ if (ret)
+ return ret;
+
+ switch (st->chip->precision_bits) {
+ case 16:
+ *val = sign_extend32(get_unaligned_be16(st->rx_data.raw), 15);
+ return IIO_VAL_INT;
+
+ case 24:
+ *val = sign_extend32(get_unaligned_be24(st->rx_data.raw), 23);
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4030_set_chan_calibscale(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int gain_int,
+ int gain_frac)
+{
+ struct ad4030_state *st = iio_priv(indio_dev);
+ u64 gain;
+
+ if (gain_int < 0 || gain_frac < 0)
+ return -EINVAL;
+
+ gain = mul_u32_u32(gain_int, MICRO) + gain_frac;
+
+ if (gain > AD4030_REG_GAIN_MAX_GAIN)
+ return -EINVAL;
+
+ put_unaligned_be16(DIV_ROUND_CLOSEST_ULL(gain * AD4030_GAIN_MIDLE_POINT,
+ MICRO),
+ st->tx_data);
+
+ return regmap_bulk_write(st->regmap,
+ AD4030_REG_GAIN_CHAN(chan->address),
+ st->tx_data, AD4030_REG_GAIN_BYTES_NB);
+}
+
+static int ad4030_set_chan_calibbias(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int offset)
+{
+ struct ad4030_state *st = iio_priv(indio_dev);
+
+ if (offset < st->offset_avail[0] || offset > st->offset_avail[2])
+ return -EINVAL;
+
+ st->tx_data[2] = 0;
+
+ switch (st->chip->precision_bits) {
+ case 16:
+ put_unaligned_be16(offset, st->tx_data);
+ break;
+
+ case 24:
+ put_unaligned_be24(offset, st->tx_data);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_bulk_write(st->regmap,
+ AD4030_REG_OFFSET_CHAN(chan->address),
+ st->tx_data, AD4030_REG_OFFSET_BYTES_NB);
+}
+
+static int ad4030_set_avg_frame_len(struct iio_dev *dev, int avg_val)
+{
+ struct ad4030_state *st = iio_priv(dev);
+ unsigned int avg_log2 = ilog2(avg_val);
+ unsigned int last_avg_idx = ARRAY_SIZE(ad4030_average_modes) - 1;
+ int ret;
+
+ if (avg_val < 0 || avg_val > ad4030_average_modes[last_avg_idx])
+ return -EINVAL;
+
+ ret = regmap_write(st->regmap, AD4030_REG_AVG,
+ AD4030_REG_AVG_MASK_AVG_SYNC |
+ FIELD_PREP(AD4030_REG_AVG_MASK_AVG_VAL, avg_log2));
+ if (ret)
+ return ret;
+
+ st->avg_log2 = avg_log2;
+
+ return 0;
+}
+
+static bool ad4030_is_common_byte_asked(struct ad4030_state *st,
+ unsigned int mask)
+{
+ return mask & (st->chip->num_voltage_inputs == 1 ?
+ AD4030_SINGLE_COMMON_BYTE_CHANNELS_MASK :
+ AD4030_DUAL_COMMON_BYTE_CHANNELS_MASK);
+}
+
+static int ad4030_set_mode(struct iio_dev *indio_dev, unsigned long mask)
+{
+ struct ad4030_state *st = iio_priv(indio_dev);
+
+ if (st->avg_log2 > 0) {
+ st->mode = AD4030_OUT_DATA_MD_30_AVERAGED_DIFF;
+ } else if (ad4030_is_common_byte_asked(st, mask)) {
+ switch (st->chip->precision_bits) {
+ case 16:
+ st->mode = AD4030_OUT_DATA_MD_16_DIFF_8_COM;
+ break;
+
+ case 24:
+ st->mode = AD4030_OUT_DATA_MD_24_DIFF_8_COM;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ } else {
+ st->mode = AD4030_OUT_DATA_MD_DIFF;
+ }
+
+ st->current_scan_type = iio_get_current_scan_type(indio_dev,
+ st->chip->channels);
+ if (IS_ERR(st->current_scan_type))
+ return PTR_ERR(st->current_scan_type);
+
+ return regmap_update_bits(st->regmap, AD4030_REG_MODES,
+ AD4030_REG_MODES_MASK_OUT_DATA_MODE,
+ st->mode);
+}
+
+/*
+ * Descramble 2 32bits numbers out of a 64bits. The bits are interleaved:
+ * 1 bit for first number, 1 bit for the second, and so on...
+ */
+static void ad4030_extract_interleaved(u8 *src, u32 *ch0, u32 *ch1)
+{
+ u8 h0, h1, l0, l1;
+ u32 out0, out1;
+ u8 *out0_raw = (u8 *)&out0;
+ u8 *out1_raw = (u8 *)&out1;
+
+ for (int i = 0; i < 4; i++) {
+ h0 = src[i * 2];
+ l1 = src[i * 2 + 1];
+ h1 = h0 << 1;
+ l0 = l1 >> 1;
+
+ h0 &= 0xAA;
+ l0 &= 0x55;
+ h1 &= 0xAA;
+ l1 &= 0x55;
+
+ h0 = (h0 | h0 << 001) & 0xCC;
+ h1 = (h1 | h1 << 001) & 0xCC;
+ l0 = (l0 | l0 >> 001) & 0x33;
+ l1 = (l1 | l1 >> 001) & 0x33;
+ h0 = (h0 | h0 << 002) & 0xF0;
+ h1 = (h1 | h1 << 002) & 0xF0;
+ l0 = (l0 | l0 >> 002) & 0x0F;
+ l1 = (l1 | l1 >> 002) & 0x0F;
+
+ out0_raw[i] = h0 | l0;
+ out1_raw[i] = h1 | l1;
+ }
+
+ *ch0 = out0;
+ *ch1 = out1;
+}
+
+static int ad4030_conversion(struct iio_dev *indio_dev)
+{
+ struct ad4030_state *st = iio_priv(indio_dev);
+ unsigned char diff_realbytes =
+ BITS_TO_BYTES(st->current_scan_type->realbits);
+ unsigned char diff_storagebytes =
+ BITS_TO_BYTES(st->current_scan_type->storagebits);
+ unsigned int bytes_to_read;
+ unsigned long cnv_nb = BIT(st->avg_log2);
+ unsigned int i;
+ int ret;
+
+ /* Number of bytes for one differential channel */
+ bytes_to_read = diff_realbytes;
+ /* Add one byte if we are using a differential + common byte mode */
+ bytes_to_read += (st->mode == AD4030_OUT_DATA_MD_24_DIFF_8_COM ||
+ st->mode == AD4030_OUT_DATA_MD_16_DIFF_8_COM) ? 1 : 0;
+ /* Mulitiply by the number of hardware channels */
+ bytes_to_read *= st->chip->num_voltage_inputs;
+
+ for (i = 0; i < cnv_nb; i++) {
+ gpiod_set_value_cansleep(st->cnv_gpio, 1);
+ ndelay(AD4030_TCNVH_NS);
+ gpiod_set_value_cansleep(st->cnv_gpio, 0);
+ ndelay(st->chip->tcyc_ns);
+ }
+
+ ret = spi_read(st->spi, st->rx_data.raw, bytes_to_read);
+ if (ret)
+ return ret;
+
+ if (st->chip->num_voltage_inputs == 2)
+ ad4030_extract_interleaved(st->rx_data.raw,
+ &st->rx_data.dual.diff[0],
+ &st->rx_data.dual.diff[1]);
+
+ if (st->mode != AD4030_OUT_DATA_MD_16_DIFF_8_COM &&
+ st->mode != AD4030_OUT_DATA_MD_24_DIFF_8_COM)
+ return 0;
+
+ if (st->chip->num_voltage_inputs == 1) {
+ st->rx_data.single.common = st->rx_data.raw[diff_realbytes];
+ return 0;
+ }
+
+ for (i = 0; i < st->chip->num_voltage_inputs; i++)
+ st->rx_data.dual.common[i] =
+ st->rx_data.raw[diff_storagebytes * i + diff_realbytes];
+
+ return 0;
+}
+
+static int ad4030_single_conversion(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *val)
+{
+ struct ad4030_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = ad4030_set_mode(indio_dev, BIT(chan->scan_index));
+ if (ret)
+ return ret;
+
+ st->current_scan_type = iio_get_current_scan_type(indio_dev,
+ st->chip->channels);
+ if (IS_ERR(st->current_scan_type))
+ return PTR_ERR(st->current_scan_type);
+
+ ret = ad4030_conversion(indio_dev);
+ if (ret)
+ return ret;
+
+ if (chan->differential)
+ if (st->chip->num_voltage_inputs == 1)
+ *val = st->rx_data.single.diff;
+ else
+ *val = st->rx_data.dual.diff[chan->address];
+ else
+ if (st->chip->num_voltage_inputs == 1)
+ *val = st->rx_data.single.common;
+ else
+ *val = st->rx_data.dual.common[chan->address];
+
+ return IIO_VAL_INT;
+}
+
+static irqreturn_t ad4030_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad4030_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = ad4030_conversion(indio_dev);
+ if (ret)
+ goto out;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, st->rx_data.raw,
+ pf->timestamp);
+
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const int ad4030_gain_avail[3][2] = {
+ { 0, 0 },
+ { 0, 30518 },
+ { 1, 999969482 },
+};
+
+static int ad4030_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel,
+ const int **vals, int *type,
+ int *length, long mask)
+{
+ struct ad4030_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ *vals = st->offset_avail;
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_RANGE;
+
+ case IIO_CHAN_INFO_CALIBSCALE:
+ *vals = (void *)ad4030_gain_avail;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ return IIO_AVAIL_RANGE;
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *vals = ad4030_average_modes;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(ad4030_average_modes);
+ return IIO_AVAIL_LIST;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4030_read_raw_dispatch(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long info)
+{
+ struct ad4030_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ return ad4030_single_conversion(indio_dev, chan, val);
+
+ case IIO_CHAN_INFO_CALIBSCALE:
+ return ad4030_get_chan_calibscale(indio_dev, chan, val, val2);
+
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return ad4030_get_chan_calibbias(indio_dev, chan, val);
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *val = BIT(st->avg_log2);
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4030_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long info)
+{
+ int ret;
+
+ if (info == IIO_CHAN_INFO_SCALE)
+ return ad4030_get_chan_scale(indio_dev, chan, val, val2);
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad4030_read_raw_dispatch(indio_dev, chan, val, val2, info);
+
+ iio_device_release_direct(indio_dev);
+
+ return ret;
+}
+
+static int ad4030_write_raw_dispatch(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long info)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_CALIBSCALE:
+ return ad4030_set_chan_calibscale(indio_dev, chan, val, val2);
+
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (val2 != 0)
+ return -EINVAL;
+ return ad4030_set_chan_calibbias(indio_dev, chan, val);
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ return ad4030_set_avg_frame_len(indio_dev, val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4030_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long info)
+{
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad4030_write_raw_dispatch(indio_dev, chan, val, val2, info);
+
+ iio_device_release_direct(indio_dev);
+
+ return ret;
+}
+
+static int ad4030_reg_access(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int writeval, unsigned int *readval)
+{
+ const struct ad4030_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ if (readval)
+ ret = regmap_read(st->regmap, reg, readval);
+ else
+ ret = regmap_write(st->regmap, reg, writeval);
+
+ iio_device_release_direct(indio_dev);
+
+ return ret;
+}
+
+static int ad4030_read_label(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ char *label)
+{
+ if (chan->differential)
+ return sprintf(label, "differential%lu\n", chan->address);
+ return sprintf(label, "common-mode%lu\n", chan->address);
+}
+
+static int ad4030_get_current_scan_type(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad4030_state *st = iio_priv(indio_dev);
+
+ return st->avg_log2 ? AD4030_SCAN_TYPE_AVG : AD4030_SCAN_TYPE_NORMAL;
+}
+
+static const struct iio_info ad4030_iio_info = {
+ .read_avail = ad4030_read_avail,
+ .read_raw = ad4030_read_raw,
+ .write_raw = ad4030_write_raw,
+ .debugfs_reg_access = ad4030_reg_access,
+ .read_label = ad4030_read_label,
+ .get_current_scan_type = ad4030_get_current_scan_type,
+};
+
+static int ad4030_buffer_preenable(struct iio_dev *indio_dev)
+{
+ return ad4030_set_mode(indio_dev, *indio_dev->active_scan_mask);
+}
+
+static bool ad4030_validate_scan_mask(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct ad4030_state *st = iio_priv(indio_dev);
+
+ /* Asking for both common channels and averaging */
+ if (st->avg_log2 && ad4030_is_common_byte_asked(st, *scan_mask))
+ return false;
+
+ return true;
+}
+
+static const struct iio_buffer_setup_ops ad4030_buffer_setup_ops = {
+ .preenable = ad4030_buffer_preenable,
+ .validate_scan_mask = ad4030_validate_scan_mask,
+};
+
+static int ad4030_regulators_get(struct ad4030_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ static const char * const ids[] = { "vdd-5v", "vdd-1v8" };
+ int ret;
+
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ids), ids);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable regulators\n");
+
+ st->vio_uv = devm_regulator_get_enable_read_voltage(dev, "vio");
+ if (st->vio_uv < 0)
+ return dev_err_probe(dev, st->vio_uv,
+ "Failed to enable and read vio voltage\n");
+
+ st->vref_uv = devm_regulator_get_enable_read_voltage(dev, "ref");
+ if (st->vref_uv < 0) {
+ if (st->vref_uv != -ENODEV)
+ return dev_err_probe(dev, st->vref_uv,
+ "Failed to read ref voltage\n");
+
+ /* if not using optional REF, the REFIN must be used */
+ st->vref_uv = devm_regulator_get_enable_read_voltage(dev,
+ "refin");
+ if (st->vref_uv < 0)
+ return dev_err_probe(dev, st->vref_uv,
+ "Failed to read refin voltage\n");
+ }
+
+ return 0;
+}
+
+static int ad4030_reset(struct ad4030_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ struct gpio_desc *reset;
+
+ reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(reset))
+ return dev_err_probe(dev, PTR_ERR(reset),
+ "Failed to get reset GPIO\n");
+
+ if (reset) {
+ ndelay(50);
+ gpiod_set_value_cansleep(reset, 0);
+ return 0;
+ }
+
+ return regmap_write(st->regmap, AD4030_REG_INTERFACE_CONFIG_A,
+ AD4030_REG_INTERFACE_CONFIG_A_SW_RESET);
+}
+
+static int ad4030_detect_chip_info(const struct ad4030_state *st)
+{
+ unsigned int grade;
+ int ret;
+
+ ret = regmap_read(st->regmap, AD4030_REG_CHIP_GRADE, &grade);
+ if (ret)
+ return ret;
+
+ grade = FIELD_GET(AD4030_REG_CHIP_GRADE_MASK_CHIP_GRADE, grade);
+ if (grade != st->chip->grade)
+ dev_warn(&st->spi->dev, "Unknown grade(0x%x) for %s\n", grade,
+ st->chip->name);
+
+ return 0;
+}
+
+static int ad4030_config(struct ad4030_state *st)
+{
+ int ret;
+ u8 reg_modes;
+
+ st->offset_avail[0] = (int)BIT(st->chip->precision_bits - 1) * -1;
+ st->offset_avail[1] = 1;
+ st->offset_avail[2] = BIT(st->chip->precision_bits - 1) - 1;
+
+ if (st->chip->num_voltage_inputs > 1)
+ reg_modes = FIELD_PREP(AD4030_REG_MODES_MASK_LANE_MODE,
+ AD4030_LANE_MD_INTERLEAVED);
+ else
+ reg_modes = FIELD_PREP(AD4030_REG_MODES_MASK_LANE_MODE,
+ AD4030_LANE_MD_1_PER_CH);
+
+ ret = regmap_write(st->regmap, AD4030_REG_MODES, reg_modes);
+ if (ret)
+ return ret;
+
+ if (st->vio_uv < AD4030_VIO_THRESHOLD_UV)
+ return regmap_write(st->regmap, AD4030_REG_IO,
+ AD4030_REG_IO_MASK_IO2X);
+
+ return 0;
+}
+
+static int ad4030_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct iio_dev *indio_dev;
+ struct ad4030_state *st;
+ 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->regmap = devm_regmap_init(dev, &ad4030_regmap_bus, st,
+ &ad4030_regmap_config);
+ if (IS_ERR(st->regmap))
+ return dev_err_probe(dev, PTR_ERR(st->regmap),
+ "Failed to initialize regmap\n");
+
+ st->chip = spi_get_device_match_data(spi);
+ if (!st->chip)
+ return -EINVAL;
+
+ ret = ad4030_regulators_get(st);
+ if (ret)
+ return ret;
+
+ /*
+ * From datasheet: "Perform a reset no sooner than 3ms after the power
+ * supplies are valid and stable"
+ */
+ fsleep(3000);
+
+ ret = ad4030_reset(st);
+ if (ret)
+ return ret;
+
+ ret = ad4030_detect_chip_info(st);
+ if (ret)
+ return ret;
+
+ ret = ad4030_config(st);
+ if (ret)
+ return ret;
+
+ st->cnv_gpio = devm_gpiod_get(dev, "cnv", GPIOD_OUT_LOW);
+ if (IS_ERR(st->cnv_gpio))
+ return dev_err_probe(dev, PTR_ERR(st->cnv_gpio),
+ "Failed to get cnv gpio\n");
+
+ /*
+ * One hardware channel is split in two software channels when using
+ * common byte mode. Add one more channel for the timestamp.
+ */
+ indio_dev->num_channels = 2 * st->chip->num_voltage_inputs + 1;
+ indio_dev->name = st->chip->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ad4030_iio_info;
+ indio_dev->channels = st->chip->channels;
+ indio_dev->available_scan_masks = st->chip->available_masks;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ ad4030_trigger_handler,
+ &ad4030_buffer_setup_ops);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to setup triggered buffer\n");
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const unsigned long ad4030_channel_masks[] = {
+ /* Differential only */
+ BIT(0),
+ /* Differential and common-mode voltage */
+ GENMASK(1, 0),
+ 0,
+};
+
+static const unsigned long ad4630_channel_masks[] = {
+ /* Differential only */
+ BIT(1) | BIT(0),
+ /* Differential with common byte */
+ GENMASK(3, 0),
+ 0,
+};
+
+static const struct iio_scan_type ad4030_24_scan_types[] = {
+ [AD4030_SCAN_TYPE_NORMAL] = {
+ .sign = 's',
+ .storagebits = 32,
+ .realbits = 24,
+ .shift = 8,
+ .endianness = IIO_BE,
+ },
+ [AD4030_SCAN_TYPE_AVG] = {
+ .sign = 's',
+ .storagebits = 32,
+ .realbits = 30,
+ .shift = 2,
+ .endianness = IIO_BE,
+ },
+};
+
+static const struct iio_scan_type ad4030_16_scan_types[] = {
+ [AD4030_SCAN_TYPE_NORMAL] = {
+ .sign = 's',
+ .storagebits = 32,
+ .realbits = 16,
+ .shift = 16,
+ .endianness = IIO_BE,
+ },
+ [AD4030_SCAN_TYPE_AVG] = {
+ .sign = 's',
+ .storagebits = 32,
+ .realbits = 30,
+ .shift = 2,
+ .endianness = IIO_BE,
+ }
+};
+
+static const struct ad4030_chip_info ad4030_24_chip_info = {
+ .name = "ad4030-24",
+ .available_masks = ad4030_channel_masks,
+ .channels = {
+ AD4030_CHAN_DIFF(0, ad4030_24_scan_types),
+ AD4030_CHAN_CMO(1, 0),
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+ },
+ .grade = AD4030_REG_CHIP_GRADE_AD4030_24_GRADE,
+ .precision_bits = 24,
+ .num_voltage_inputs = 1,
+ .tcyc_ns = AD4030_TCYC_ADJUSTED_NS,
+};
+
+static const struct ad4030_chip_info ad4630_16_chip_info = {
+ .name = "ad4630-16",
+ .available_masks = ad4630_channel_masks,
+ .channels = {
+ AD4030_CHAN_DIFF(0, ad4030_16_scan_types),
+ AD4030_CHAN_DIFF(1, ad4030_16_scan_types),
+ AD4030_CHAN_CMO(2, 0),
+ AD4030_CHAN_CMO(3, 1),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+ },
+ .grade = AD4030_REG_CHIP_GRADE_AD4630_16_GRADE,
+ .precision_bits = 16,
+ .num_voltage_inputs = 2,
+ .tcyc_ns = AD4030_TCYC_ADJUSTED_NS,
+};
+
+static const struct ad4030_chip_info ad4630_24_chip_info = {
+ .name = "ad4630-24",
+ .available_masks = ad4630_channel_masks,
+ .channels = {
+ AD4030_CHAN_DIFF(0, ad4030_24_scan_types),
+ AD4030_CHAN_DIFF(1, ad4030_24_scan_types),
+ AD4030_CHAN_CMO(2, 0),
+ AD4030_CHAN_CMO(3, 1),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+ },
+ .grade = AD4030_REG_CHIP_GRADE_AD4630_24_GRADE,
+ .precision_bits = 24,
+ .num_voltage_inputs = 2,
+ .tcyc_ns = AD4030_TCYC_ADJUSTED_NS,
+};
+
+static const struct ad4030_chip_info ad4632_16_chip_info = {
+ .name = "ad4632-16",
+ .available_masks = ad4630_channel_masks,
+ .channels = {
+ AD4030_CHAN_DIFF(0, ad4030_16_scan_types),
+ AD4030_CHAN_DIFF(1, ad4030_16_scan_types),
+ AD4030_CHAN_CMO(2, 0),
+ AD4030_CHAN_CMO(3, 1),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+ },
+ .grade = AD4030_REG_CHIP_GRADE_AD4632_16_GRADE,
+ .precision_bits = 16,
+ .num_voltage_inputs = 2,
+ .tcyc_ns = AD4632_TCYC_ADJUSTED_NS,
+};
+
+static const struct ad4030_chip_info ad4632_24_chip_info = {
+ .name = "ad4632-24",
+ .available_masks = ad4630_channel_masks,
+ .channels = {
+ AD4030_CHAN_DIFF(0, ad4030_24_scan_types),
+ AD4030_CHAN_DIFF(1, ad4030_24_scan_types),
+ AD4030_CHAN_CMO(2, 0),
+ AD4030_CHAN_CMO(3, 1),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+ },
+ .grade = AD4030_REG_CHIP_GRADE_AD4632_24_GRADE,
+ .precision_bits = 24,
+ .num_voltage_inputs = 2,
+ .tcyc_ns = AD4632_TCYC_ADJUSTED_NS,
+};
+
+static const struct spi_device_id ad4030_id_table[] = {
+ { "ad4030-24", (kernel_ulong_t)&ad4030_24_chip_info },
+ { "ad4630-16", (kernel_ulong_t)&ad4630_16_chip_info },
+ { "ad4630-24", (kernel_ulong_t)&ad4630_24_chip_info },
+ { "ad4632-16", (kernel_ulong_t)&ad4632_16_chip_info },
+ { "ad4632-24", (kernel_ulong_t)&ad4632_24_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad4030_id_table);
+
+static const struct of_device_id ad4030_of_match[] = {
+ { .compatible = "adi,ad4030-24", .data = &ad4030_24_chip_info },
+ { .compatible = "adi,ad4630-16", .data = &ad4630_16_chip_info },
+ { .compatible = "adi,ad4630-24", .data = &ad4630_24_chip_info },
+ { .compatible = "adi,ad4632-16", .data = &ad4632_16_chip_info },
+ { .compatible = "adi,ad4632-24", .data = &ad4632_24_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad4030_of_match);
+
+static struct spi_driver ad4030_driver = {
+ .driver = {
+ .name = "ad4030",
+ .of_match_table = ad4030_of_match,
+ },
+ .probe = ad4030_probe,
+ .id_table = ad4030_id_table,
+};
+module_spi_driver(ad4030_driver);
+
+MODULE_AUTHOR("Esteban Blanc <eblanc@baylibre.com>");
+MODULE_DESCRIPTION("Analog Devices AD4630 ADC family driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ad4130.c b/drivers/iio/adc/ad4130.c
index de32cc9d18c5..0f4c9cd6c102 100644
--- a/drivers/iio/adc/ad4130.c
+++ b/drivers/iio/adc/ad4130.c
@@ -6,6 +6,7 @@
#include <linux/bitfield.h>
#include <linux/bitops.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
@@ -203,7 +204,7 @@ enum ad4130_mode {
AD4130_MODE_IDLE = 0b0100,
};
-enum ad4130_filter_mode {
+enum ad4130_filter_type {
AD4130_FILTER_SINC4,
AD4130_FILTER_SINC4_SINC1,
AD4130_FILTER_SINC3,
@@ -223,6 +224,10 @@ enum ad4130_pin_function {
AD4130_PIN_FN_VBIAS = BIT(3),
};
+/*
+ * If you make adaptations in this struct, you most likely also have to adapt
+ * ad4130_setup_info_eq(), too.
+ */
struct ad4130_setup_info {
unsigned int iout0_val;
unsigned int iout1_val;
@@ -230,7 +235,7 @@ struct ad4130_setup_info {
unsigned int pga;
unsigned int fs;
u32 ref_sel;
- enum ad4130_filter_mode filter_mode;
+ enum ad4130_filter_type filter_type;
bool ref_bufp;
bool ref_bufm;
};
@@ -251,7 +256,7 @@ struct ad4130_chan_info {
};
struct ad4130_filter_config {
- enum ad4130_filter_mode filter_mode;
+ enum ad4130_filter_type filter_type;
unsigned int odr_div;
unsigned int fs_max;
enum iio_available_type samp_freq_avail_type;
@@ -337,9 +342,9 @@ static const unsigned int ad4130_burnout_current_na_tbl[AD4130_BURNOUT_MAX] = {
[AD4130_BURNOUT_4000NA] = 4000,
};
-#define AD4130_VARIABLE_ODR_CONFIG(_filter_mode, _odr_div, _fs_max) \
+#define AD4130_VARIABLE_ODR_CONFIG(_filter_type, _odr_div, _fs_max) \
{ \
- .filter_mode = (_filter_mode), \
+ .filter_type = (_filter_type), \
.odr_div = (_odr_div), \
.fs_max = (_fs_max), \
.samp_freq_avail_type = IIO_AVAIL_RANGE, \
@@ -350,9 +355,9 @@ static const unsigned int ad4130_burnout_current_na_tbl[AD4130_BURNOUT_MAX] = {
}, \
}
-#define AD4130_FIXED_ODR_CONFIG(_filter_mode, _odr_div) \
+#define AD4130_FIXED_ODR_CONFIG(_filter_type, _odr_div) \
{ \
- .filter_mode = (_filter_mode), \
+ .filter_type = (_filter_type), \
.odr_div = (_odr_div), \
.fs_max = AD4130_FILTER_SELECT_MIN, \
.samp_freq_avail_type = IIO_AVAIL_LIST, \
@@ -374,7 +379,7 @@ static const struct ad4130_filter_config ad4130_filter_configs[] = {
AD4130_FIXED_ODR_CONFIG(AD4130_FILTER_SINC3_PF4, 148),
};
-static const char * const ad4130_filter_modes_str[] = {
+static const char * const ad4130_filter_types_str[] = {
[AD4130_FILTER_SINC4] = "sinc4",
[AD4130_FILTER_SINC4_SINC1] = "sinc4+sinc1",
[AD4130_FILTER_SINC3] = "sinc3",
@@ -591,6 +596,40 @@ static irqreturn_t ad4130_irq_handler(int irq, void *private)
return IRQ_HANDLED;
}
+static bool ad4130_setup_info_eq(struct ad4130_setup_info *a,
+ struct ad4130_setup_info *b)
+{
+ /*
+ * This is just to make sure that the comparison is adapted after
+ * struct ad4130_setup_info was changed.
+ */
+ static_assert(sizeof(*a) ==
+ sizeof(struct {
+ unsigned int iout0_val;
+ unsigned int iout1_val;
+ unsigned int burnout;
+ unsigned int pga;
+ unsigned int fs;
+ u32 ref_sel;
+ enum ad4130_filter_type filter_type;
+ bool ref_bufp;
+ bool ref_bufm;
+ }));
+
+ if (a->iout0_val != b->iout0_val ||
+ a->iout1_val != b->iout1_val ||
+ a->burnout != b->burnout ||
+ a->pga != b->pga ||
+ a->fs != b->fs ||
+ a->ref_sel != b->ref_sel ||
+ a->filter_type != b->filter_type ||
+ a->ref_bufp != b->ref_bufp ||
+ a->ref_bufm != b->ref_bufm)
+ return false;
+
+ return true;
+}
+
static int ad4130_find_slot(struct ad4130_state *st,
struct ad4130_setup_info *target_setup_info,
unsigned int *slot, bool *overwrite)
@@ -604,8 +643,7 @@ static int ad4130_find_slot(struct ad4130_state *st,
struct ad4130_slot_info *slot_info = &st->slots_info[i];
/* Immediately accept a matching setup info. */
- if (!memcmp(target_setup_info, &slot_info->setup,
- sizeof(*target_setup_info))) {
+ if (ad4130_setup_info_eq(target_setup_info, &slot_info->setup)) {
*slot = i;
return 0;
}
@@ -691,7 +729,7 @@ static int ad4130_write_slot_setup(struct ad4130_state *st,
if (ret)
return ret;
- val = FIELD_PREP(AD4130_FILTER_MODE_MASK, setup_info->filter_mode) |
+ val = FIELD_PREP(AD4130_FILTER_MODE_MASK, setup_info->filter_type) |
FIELD_PREP(AD4130_FILTER_SELECT_MASK, setup_info->fs);
ret = regmap_write(st->regmap, AD4130_FILTER_X_REG(slot), val);
@@ -835,11 +873,11 @@ static int ad4130_set_channel_enable(struct ad4130_state *st,
* (used in ad4130_fs_to_freq)
*/
-static void ad4130_freq_to_fs(enum ad4130_filter_mode filter_mode,
+static void ad4130_freq_to_fs(enum ad4130_filter_type filter_type,
int val, int val2, unsigned int *fs)
{
const struct ad4130_filter_config *filter_config =
- &ad4130_filter_configs[filter_mode];
+ &ad4130_filter_configs[filter_type];
u64 dividend, divisor;
int temp;
@@ -858,11 +896,11 @@ static void ad4130_freq_to_fs(enum ad4130_filter_mode filter_mode,
*fs = temp;
}
-static void ad4130_fs_to_freq(enum ad4130_filter_mode filter_mode,
+static void ad4130_fs_to_freq(enum ad4130_filter_type filter_type,
unsigned int fs, int *val, int *val2)
{
const struct ad4130_filter_config *filter_config =
- &ad4130_filter_configs[filter_mode];
+ &ad4130_filter_configs[filter_type];
unsigned int dividend, divisor;
u64 temp;
@@ -874,7 +912,7 @@ static void ad4130_fs_to_freq(enum ad4130_filter_mode filter_mode,
*val = div_u64_rem(temp, NANO, val2);
}
-static int ad4130_set_filter_mode(struct iio_dev *indio_dev,
+static int ad4130_set_filter_type(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
unsigned int val)
{
@@ -882,17 +920,17 @@ static int ad4130_set_filter_mode(struct iio_dev *indio_dev,
unsigned int channel = chan->scan_index;
struct ad4130_chan_info *chan_info = &st->chans_info[channel];
struct ad4130_setup_info *setup_info = &chan_info->setup;
- enum ad4130_filter_mode old_filter_mode;
+ enum ad4130_filter_type old_filter_type;
int freq_val, freq_val2;
unsigned int old_fs;
int ret = 0;
guard(mutex)(&st->lock);
- if (setup_info->filter_mode == val)
+ if (setup_info->filter_type == val)
return 0;
old_fs = setup_info->fs;
- old_filter_mode = setup_info->filter_mode;
+ old_filter_type = setup_info->filter_type;
/*
* When switching between filter modes, try to match the ODR as
@@ -900,48 +938,55 @@ static int ad4130_set_filter_mode(struct iio_dev *indio_dev,
* using the old filter mode, then convert it back into FS using
* the new filter mode.
*/
- ad4130_fs_to_freq(setup_info->filter_mode, setup_info->fs,
+ ad4130_fs_to_freq(setup_info->filter_type, setup_info->fs,
&freq_val, &freq_val2);
ad4130_freq_to_fs(val, freq_val, freq_val2, &setup_info->fs);
- setup_info->filter_mode = val;
+ setup_info->filter_type = val;
ret = ad4130_write_channel_setup(st, channel, false);
if (ret) {
setup_info->fs = old_fs;
- setup_info->filter_mode = old_filter_mode;
+ setup_info->filter_type = old_filter_type;
return ret;
}
return 0;
}
-static int ad4130_get_filter_mode(struct iio_dev *indio_dev,
+static int ad4130_get_filter_type(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct ad4130_state *st = iio_priv(indio_dev);
unsigned int channel = chan->scan_index;
struct ad4130_setup_info *setup_info = &st->chans_info[channel].setup;
- enum ad4130_filter_mode filter_mode;
+ enum ad4130_filter_type filter_type;
guard(mutex)(&st->lock);
- filter_mode = setup_info->filter_mode;
+ filter_type = setup_info->filter_type;
- return filter_mode;
+ return filter_type;
}
-static const struct iio_enum ad4130_filter_mode_enum = {
- .items = ad4130_filter_modes_str,
- .num_items = ARRAY_SIZE(ad4130_filter_modes_str),
- .set = ad4130_set_filter_mode,
- .get = ad4130_get_filter_mode,
+static const struct iio_enum ad4130_filter_type_enum = {
+ .items = ad4130_filter_types_str,
+ .num_items = ARRAY_SIZE(ad4130_filter_types_str),
+ .set = ad4130_set_filter_type,
+ .get = ad4130_get_filter_type,
};
-static const struct iio_chan_spec_ext_info ad4130_filter_mode_ext_info[] = {
- IIO_ENUM("filter_mode", IIO_SEPARATE, &ad4130_filter_mode_enum),
+static const struct iio_chan_spec_ext_info ad4130_ext_info[] = {
+ /*
+ * `filter_type` is the standardized IIO ABI for digital filtering.
+ * `filter_mode` is just kept for backwards compatibility.
+ */
+ IIO_ENUM("filter_mode", IIO_SEPARATE, &ad4130_filter_type_enum),
IIO_ENUM_AVAILABLE("filter_mode", IIO_SHARED_BY_TYPE,
- &ad4130_filter_mode_enum),
+ &ad4130_filter_type_enum),
+ IIO_ENUM("filter_type", IIO_SEPARATE, &ad4130_filter_type_enum),
+ IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE,
+ &ad4130_filter_type_enum),
{ }
};
@@ -955,7 +1000,7 @@ static const struct iio_chan_spec ad4130_channel_template = {
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
- .ext_info = ad4130_filter_mode_ext_info,
+ .ext_info = ad4130_ext_info,
.scan_type = {
.sign = 'u',
.endianness = IIO_BE,
@@ -1005,7 +1050,7 @@ static int ad4130_set_channel_freq(struct ad4130_state *st,
guard(mutex)(&st->lock);
old_fs = setup_info->fs;
- ad4130_freq_to_fs(setup_info->filter_mode, val, val2, &fs);
+ ad4130_freq_to_fs(setup_info->filter_type, val, val2, &fs);
if (fs == setup_info->fs)
return 0;
@@ -1060,13 +1105,11 @@ static int _ad4130_read_sample(struct iio_dev *indio_dev, unsigned int channel,
static int ad4130_read_sample(struct iio_dev *indio_dev, unsigned int channel,
int *val)
{
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- struct ad4130_state *st = iio_priv(indio_dev);
+ struct ad4130_state *st = iio_priv(indio_dev);
- guard(mutex)(&st->lock);
- return _ad4130_read_sample(indio_dev, channel, val);
- }
- unreachable();
+ guard(mutex)(&st->lock);
+
+ return _ad4130_read_sample(indio_dev, channel, val);
}
static int ad4130_read_raw(struct iio_dev *indio_dev,
@@ -1076,10 +1119,16 @@ static int ad4130_read_raw(struct iio_dev *indio_dev,
struct ad4130_state *st = iio_priv(indio_dev);
unsigned int channel = chan->scan_index;
struct ad4130_setup_info *setup_info = &st->chans_info[channel].setup;
+ int ret;
switch (info) {
case IIO_CHAN_INFO_RAW:
- return ad4130_read_sample(indio_dev, channel, val);
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad4130_read_sample(indio_dev, channel, val);
+ iio_device_release_direct(indio_dev);
+ return ret;
case IIO_CHAN_INFO_SCALE: {
guard(mutex)(&st->lock);
*val = st->scale_tbls[setup_info->ref_sel][setup_info->pga][0];
@@ -1093,7 +1142,7 @@ static int ad4130_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ: {
guard(mutex)(&st->lock);
- ad4130_fs_to_freq(setup_info->filter_mode, setup_info->fs,
+ ad4130_fs_to_freq(setup_info->filter_type, setup_info->fs,
val, val2);
return IIO_VAL_INT_PLUS_NANO;
@@ -1123,7 +1172,7 @@ static int ad4130_read_avail(struct iio_dev *indio_dev,
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_SAMP_FREQ:
scoped_guard(mutex, &st->lock) {
- filter_config = &ad4130_filter_configs[setup_info->filter_mode];
+ filter_config = &ad4130_filter_configs[setup_info->filter_type];
}
*vals = (int *)filter_config->samp_freq_avail;
diff --git a/drivers/iio/adc/ad4695.c b/drivers/iio/adc/ad4695.c
index b79d135a5471..8222c8ab2940 100644
--- a/drivers/iio/adc/ad4695.c
+++ b/drivers/iio/adc/ad4695.c
@@ -19,14 +19,19 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
+#include <linux/iio/buffer-dmaengine.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/minmax.h>
+#include <linux/mutex.h>
#include <linux/property.h>
+#include <linux/pwm.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+#include <linux/spi/offload/consumer.h>
+#include <linux/spi/offload/provider.h>
#include <linux/spi/spi.h>
#include <linux/units.h>
@@ -66,12 +71,15 @@
#define AD4695_REG_STD_SEQ_CONFIG 0x0024
#define AD4695_REG_GPIO_CTRL 0x0026
#define AD4695_REG_GP_MODE 0x0027
+#define AD4695_REG_GP_MODE_BUSY_GP_SEL BIT(5)
+#define AD4695_REG_GP_MODE_BUSY_GP_EN BIT(1)
#define AD4695_REG_TEMP_CTRL 0x0029
#define AD4695_REG_TEMP_CTRL_TEMP_EN BIT(0)
#define AD4695_REG_CONFIG_IN(n) (0x0030 | (n))
#define AD4695_REG_CONFIG_IN_MODE BIT(6)
#define AD4695_REG_CONFIG_IN_PAIR GENMASK(5, 4)
#define AD4695_REG_CONFIG_IN_AINHIGHZ_EN BIT(3)
+#define AD4695_REG_CONFIG_IN_OSR_SET GENMASK(1, 0)
#define AD4695_REG_UPPER_IN(n) (0x0040 | (2 * (n)))
#define AD4695_REG_LOWER_IN(n) (0x0060 | (2 * (n)))
#define AD4695_REG_HYST_IN(n) (0x0080 | (2 * (n)))
@@ -92,6 +100,8 @@
#define AD4695_T_REFBUF_MS 100
#define AD4695_T_REGCONFIG_NS 20
#define AD4695_T_SCK_CNV_DELAY_NS 80
+#define AD4695_T_CNVL_NS 80
+#define AD4695_T_CNVH_NS 10
#define AD4695_REG_ACCESS_SCLK_HZ (10 * MEGA)
/* Max number of voltage input channels. */
@@ -118,17 +128,27 @@ struct ad4695_channel_config {
bool bipolar;
enum ad4695_in_pair pin_pairing;
unsigned int common_mode_mv;
+ unsigned int oversampling_ratio;
};
struct ad4695_state {
struct spi_device *spi;
+ struct spi_offload *offload;
+ struct spi_offload_trigger *offload_trigger;
struct regmap *regmap;
struct regmap *regmap16;
struct gpio_desc *reset_gpio;
+ /* currently PWM CNV only supported with SPI offload use */
+ struct pwm_device *cnv_pwm;
+ /* protects against concurrent use of cnv_pwm */
+ struct mutex cnv_pwm_lock;
+ /* offload also requires separate gpio to manually control CNV */
+ struct gpio_desc *cnv_gpio;
/* voltages channels plus temperature and timestamp */
struct iio_chan_spec iio_chan[AD4695_MAX_CHANNELS + 2];
struct ad4695_channel_config channels_cfg[AD4695_MAX_CHANNELS];
const struct ad4695_chip_info *chip_info;
+ int sample_freq_range[3];
/* Reference voltage. */
unsigned int vref_mv;
/* Common mode input pin voltage. */
@@ -148,6 +168,8 @@ struct ad4695_state {
/* Commands to send for single conversion. */
u16 cnv_cmd;
u8 cnv_cmd2;
+ /* Buffer for storing data from regmap bus reads/writes */
+ u8 regmap_bus_data[4];
};
static const struct regmap_range ad4695_regmap_rd_ranges[] = {
@@ -192,7 +214,6 @@ static const struct regmap_config ad4695_regmap_config = {
.max_register = AD4695_REG_AS_SLOT(127),
.rd_table = &ad4695_regmap_rd_table,
.wr_table = &ad4695_regmap_wr_table,
- .can_multi_write = true,
};
static const struct regmap_range ad4695_regmap16_rd_ranges[] = {
@@ -224,7 +245,126 @@ static const struct regmap_config ad4695_regmap16_config = {
.max_register = AD4695_REG_GAIN_IN(15),
.rd_table = &ad4695_regmap16_rd_table,
.wr_table = &ad4695_regmap16_wr_table,
- .can_multi_write = true,
+};
+
+static int ad4695_regmap_bus_reg_write(void *context, const void *data,
+ size_t count)
+{
+ struct ad4695_state *st = context;
+ struct spi_transfer xfer = {
+ .speed_hz = AD4695_REG_ACCESS_SCLK_HZ,
+ .len = count,
+ .tx_buf = st->regmap_bus_data,
+ };
+
+ if (count > ARRAY_SIZE(st->regmap_bus_data))
+ return -EINVAL;
+
+ memcpy(st->regmap_bus_data, data, count);
+
+ return spi_sync_transfer(st->spi, &xfer, 1);
+}
+
+static int ad4695_regmap_bus_reg_read(void *context, const void *reg,
+ size_t reg_size, void *val,
+ size_t val_size)
+{
+ struct ad4695_state *st = context;
+ struct spi_transfer xfers[] = {
+ {
+ .speed_hz = AD4695_REG_ACCESS_SCLK_HZ,
+ .len = reg_size,
+ .tx_buf = &st->regmap_bus_data[0],
+ }, {
+ .speed_hz = AD4695_REG_ACCESS_SCLK_HZ,
+ .len = val_size,
+ .rx_buf = &st->regmap_bus_data[2],
+ },
+ };
+ int ret;
+
+ if (reg_size > 2)
+ return -EINVAL;
+
+ if (val_size > 2)
+ return -EINVAL;
+
+ memcpy(&st->regmap_bus_data[0], reg, reg_size);
+
+ ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
+ if (ret)
+ return ret;
+
+ memcpy(val, &st->regmap_bus_data[2], val_size);
+
+ return 0;
+}
+
+static const struct regmap_bus ad4695_regmap_bus = {
+ .write = ad4695_regmap_bus_reg_write,
+ .read = ad4695_regmap_bus_reg_read,
+ .read_flag_mask = 0x80,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+enum {
+ AD4695_SCAN_TYPE_OSR_1,
+ AD4695_SCAN_TYPE_OSR_4,
+ AD4695_SCAN_TYPE_OSR_16,
+ AD4695_SCAN_TYPE_OSR_64,
+};
+
+static const struct iio_scan_type ad4695_scan_type_offload_u[] = {
+ [AD4695_SCAN_TYPE_OSR_1] = {
+ .sign = 'u',
+ .realbits = 16,
+ .shift = 3,
+ .storagebits = 32,
+ },
+ [AD4695_SCAN_TYPE_OSR_4] = {
+ .sign = 'u',
+ .realbits = 17,
+ .shift = 2,
+ .storagebits = 32,
+ },
+ [AD4695_SCAN_TYPE_OSR_16] = {
+ .sign = 'u',
+ .realbits = 18,
+ .shift = 1,
+ .storagebits = 32,
+ },
+ [AD4695_SCAN_TYPE_OSR_64] = {
+ .sign = 'u',
+ .realbits = 19,
+ .storagebits = 32,
+ },
+};
+
+static const struct iio_scan_type ad4695_scan_type_offload_s[] = {
+ [AD4695_SCAN_TYPE_OSR_1] = {
+ .sign = 's',
+ .realbits = 16,
+ .shift = 3,
+ .storagebits = 32,
+ },
+ [AD4695_SCAN_TYPE_OSR_4] = {
+ .sign = 's',
+ .realbits = 17,
+ .shift = 2,
+ .storagebits = 32,
+ },
+ [AD4695_SCAN_TYPE_OSR_16] = {
+ .sign = 's',
+ .realbits = 18,
+ .shift = 1,
+ .storagebits = 32,
+ },
+ [AD4695_SCAN_TYPE_OSR_64] = {
+ .sign = 's',
+ .realbits = 19,
+ .storagebits = 32,
+ },
};
static const struct iio_chan_spec ad4695_channel_template = {
@@ -264,6 +404,10 @@ static const char * const ad4695_power_supplies[] = {
"avdd", "vio"
};
+static const int ad4695_oversampling_ratios[] = {
+ 1, 4, 16, 64,
+};
+
static const struct ad4695_chip_info ad4695_chip_info = {
.name = "ad4695",
.max_sample_rate = 500 * KILO,
@@ -292,6 +436,13 @@ static const struct ad4695_chip_info ad4698_chip_info = {
.num_voltage_inputs = 8,
};
+static void ad4695_cnv_manual_trigger(struct ad4695_state *st)
+{
+ gpiod_set_value_cansleep(st->cnv_gpio, 1);
+ ndelay(10);
+ gpiod_set_value_cansleep(st->cnv_gpio, 0);
+}
+
/**
* ad4695_set_single_cycle_mode - Set the device in single cycle mode
* @st: The AD4695 state
@@ -364,11 +515,31 @@ static int ad4695_enter_advanced_sequencer_mode(struct ad4695_state *st, u32 n)
*/
static int ad4695_exit_conversion_mode(struct ad4695_state *st)
{
- struct spi_transfer xfer = {
- .tx_buf = &st->cnv_cmd2,
- .len = 1,
- .delay.value = AD4695_T_REGCONFIG_NS,
- .delay.unit = SPI_DELAY_UNIT_NSECS,
+ /*
+ * An extra transfer is needed to trigger a conversion here so
+ * that we can be 100% sure the command will be processed by the
+ * ADC, rather than relying on it to be in the correct state
+ * when this function is called (this chip has a quirk where the
+ * command only works when reading a conversion, and if the
+ * previous conversion was already read then it won't work). The
+ * actual conversion command is then run at the slower
+ * AD4695_REG_ACCESS_SCLK_HZ speed to guarantee this works.
+ */
+ struct spi_transfer xfers[] = {
+ {
+ .delay.value = AD4695_T_CNVL_NS,
+ .delay.unit = SPI_DELAY_UNIT_NSECS,
+ .cs_change = 1,
+ .cs_change_delay.value = AD4695_T_CNVH_NS,
+ .cs_change_delay.unit = SPI_DELAY_UNIT_NSECS,
+ },
+ {
+ .speed_hz = AD4695_REG_ACCESS_SCLK_HZ,
+ .tx_buf = &st->cnv_cmd2,
+ .len = 1,
+ .delay.value = AD4695_T_REGCONFIG_NS,
+ .delay.unit = SPI_DELAY_UNIT_NSECS,
+ },
};
/*
@@ -377,7 +548,18 @@ static int ad4695_exit_conversion_mode(struct ad4695_state *st)
*/
st->cnv_cmd2 = AD4695_CMD_EXIT_CNV_MODE << 3;
- return spi_sync_transfer(st->spi, &xfer, 1);
+ if (st->cnv_gpio) {
+ ad4695_cnv_manual_trigger(st);
+
+ /*
+ * In this case, CNV is not connected to CS, so we don't need
+ * the extra CS toggle to trigger the conversion and toggling
+ * CS would have no effect.
+ */
+ return spi_sync_transfer(st->spi, &xfers[1], 1);
+ }
+
+ return spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
}
static int ad4695_set_ref_voltage(struct ad4695_state *st, int vref_mv)
@@ -402,6 +584,29 @@ static int ad4695_set_ref_voltage(struct ad4695_state *st, int vref_mv)
FIELD_PREP(AD4695_REG_REF_CTRL_VREF_SET, val));
}
+/**
+ * ad4695_osr_to_regval - convert ratio to OSR register value
+ * @ratio: ratio to check
+ *
+ * Check if ratio is present in the list of available ratios and return
+ * the corresponding value that needs to be written to the register to
+ * select that ratio.
+ *
+ * Returns: register value (0 to 3) or -EINVAL if there is not an exact
+ * match
+ */
+static int ad4695_osr_to_regval(int ratio)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ad4695_oversampling_ratios); i++) {
+ if (ratio == ad4695_oversampling_ratios[i])
+ return i;
+ }
+
+ return -EINVAL;
+}
+
static int ad4695_write_chn_cfg(struct ad4695_state *st,
struct ad4695_channel_config *cfg)
{
@@ -604,6 +809,161 @@ out:
return IRQ_HANDLED;
}
+static int ad4695_offload_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ad4695_state *st = iio_priv(indio_dev);
+ struct spi_offload_trigger_config config = {
+ .type = SPI_OFFLOAD_TRIGGER_DATA_READY,
+ };
+ struct spi_transfer *xfer = &st->buf_read_xfer[0];
+ struct pwm_state state;
+ u8 temp_chan_bit = st->chip_info->num_voltage_inputs;
+ u8 num_slots = 0;
+ u8 temp_en = 0;
+ unsigned int bit;
+ int ret;
+
+ iio_for_each_active_channel(indio_dev, bit) {
+ if (bit == temp_chan_bit) {
+ temp_en = 1;
+ continue;
+ }
+
+ ret = regmap_write(st->regmap, AD4695_REG_AS_SLOT(num_slots),
+ FIELD_PREP(AD4695_REG_AS_SLOT_INX, bit));
+ if (ret)
+ return ret;
+
+ num_slots++;
+ }
+
+ /*
+ * For non-offload, we could discard data to work around this
+ * restriction, but with offload, that is not possible.
+ */
+ if (num_slots < 2) {
+ dev_err(&st->spi->dev,
+ "At least two voltage channels must be enabled.\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(st->regmap, AD4695_REG_TEMP_CTRL,
+ AD4695_REG_TEMP_CTRL_TEMP_EN,
+ FIELD_PREP(AD4695_REG_TEMP_CTRL_TEMP_EN,
+ temp_en));
+ if (ret)
+ return ret;
+
+ /* Each BUSY event means just one sample for one channel is ready. */
+ memset(xfer, 0, sizeof(*xfer));
+ xfer->offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
+ /* Using 19 bits per word to allow for possible oversampling */
+ xfer->bits_per_word = 19;
+ xfer->len = 4;
+
+ spi_message_init_with_transfers(&st->buf_read_msg, xfer, 1);
+ st->buf_read_msg.offload = st->offload;
+
+ ret = spi_optimize_message(st->spi, &st->buf_read_msg);
+ if (ret)
+ return ret;
+
+ /*
+ * NB: technically, this is part the SPI offload trigger enable, but it
+ * doesn't work to call it from the offload trigger enable callback
+ * because it requires accessing the SPI bus. Calling it from the
+ * trigger enable callback could cause a deadlock.
+ */
+ ret = regmap_set_bits(st->regmap, AD4695_REG_GP_MODE,
+ AD4695_REG_GP_MODE_BUSY_GP_EN);
+ if (ret)
+ goto err_unoptimize_message;
+
+ ret = spi_offload_trigger_enable(st->offload, st->offload_trigger,
+ &config);
+ if (ret)
+ goto err_disable_busy_output;
+
+ ret = ad4695_enter_advanced_sequencer_mode(st, num_slots);
+ if (ret)
+ goto err_offload_trigger_disable;
+
+ mutex_lock(&st->cnv_pwm_lock);
+ pwm_get_state(st->cnv_pwm, &state);
+ /*
+ * PWM subsystem generally rounds down, so requesting 2x minimum high
+ * time ensures that we meet the minimum high time in any case.
+ */
+ state.duty_cycle = AD4695_T_CNVH_NS * 2;
+ ret = pwm_apply_might_sleep(st->cnv_pwm, &state);
+ mutex_unlock(&st->cnv_pwm_lock);
+ if (ret)
+ goto err_offload_exit_conversion_mode;
+
+ return 0;
+
+err_offload_exit_conversion_mode:
+ /*
+ * We have to unwind in a different order to avoid triggering offload.
+ * ad4695_exit_conversion_mode() triggers a conversion, so it has to be
+ * done after spi_offload_trigger_disable().
+ */
+ spi_offload_trigger_disable(st->offload, st->offload_trigger);
+ ad4695_exit_conversion_mode(st);
+ goto err_disable_busy_output;
+
+err_offload_trigger_disable:
+ spi_offload_trigger_disable(st->offload, st->offload_trigger);
+
+err_disable_busy_output:
+ regmap_clear_bits(st->regmap, AD4695_REG_GP_MODE,
+ AD4695_REG_GP_MODE_BUSY_GP_EN);
+
+err_unoptimize_message:
+ spi_unoptimize_message(&st->buf_read_msg);
+
+ return ret;
+}
+
+static int ad4695_offload_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct ad4695_state *st = iio_priv(indio_dev);
+ struct pwm_state state;
+ int ret;
+
+ scoped_guard(mutex, &st->cnv_pwm_lock) {
+ pwm_get_state(st->cnv_pwm, &state);
+ state.duty_cycle = 0;
+ ret = pwm_apply_might_sleep(st->cnv_pwm, &state);
+ if (ret)
+ return ret;
+ }
+
+ spi_offload_trigger_disable(st->offload, st->offload_trigger);
+
+ /*
+ * ad4695_exit_conversion_mode() triggers a conversion, so it has to be
+ * done after spi_offload_trigger_disable().
+ */
+ ret = ad4695_exit_conversion_mode(st);
+ if (ret)
+ return ret;
+
+ ret = regmap_clear_bits(st->regmap, AD4695_REG_GP_MODE,
+ AD4695_REG_GP_MODE_BUSY_GP_EN);
+ if (ret)
+ return ret;
+
+ spi_unoptimize_message(&st->buf_read_msg);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ad4695_offload_buffer_setup_ops = {
+ .postenable = ad4695_offload_buffer_postenable,
+ .predisable = ad4695_offload_buffer_predisable,
+};
+
/**
* ad4695_read_one_sample - Read a single sample using single-cycle mode
* @st: The AD4695 state
@@ -636,6 +996,13 @@ static int ad4695_read_one_sample(struct ad4695_state *st, unsigned int address)
return ret;
/*
+ * If CNV is connected to CS, the previous function will have triggered
+ * the conversion, otherwise, we do it manually.
+ */
+ if (st->cnv_gpio)
+ ad4695_cnv_manual_trigger(st);
+
+ /*
* Setting the first channel to the temperature channel isn't supported
* in single-cycle mode, so we have to do an extra conversion to read
* the temperature.
@@ -646,6 +1013,13 @@ static int ad4695_read_one_sample(struct ad4695_state *st, unsigned int address)
ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
if (ret)
return ret;
+
+ /*
+ * If CNV is connected to CS, the previous function will have
+ * triggered the conversion, otherwise, we do it manually.
+ */
+ if (st->cnv_gpio)
+ ad4695_cnv_manual_trigger(st);
}
/* Then read the result and exit conversion mode. */
@@ -655,36 +1029,58 @@ static int ad4695_read_one_sample(struct ad4695_state *st, unsigned int address)
return spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
}
+static int __ad4695_read_info_raw(struct ad4695_state *st,
+ struct iio_chan_spec const *chan,
+ int *val)
+{
+ u8 realbits = chan->scan_type.realbits;
+ int ret;
+
+ ret = ad4695_read_one_sample(st, chan->address);
+ if (ret)
+ return ret;
+
+ if (chan->scan_type.sign == 's')
+ *val = sign_extend32(st->raw_data, realbits - 1);
+ else
+ *val = st->raw_data;
+
+ return IIO_VAL_INT;
+}
+
static int ad4695_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct ad4695_state *st = iio_priv(indio_dev);
- struct ad4695_channel_config *cfg = &st->channels_cfg[chan->scan_index];
- u8 realbits = chan->scan_type.realbits;
+ const struct iio_scan_type *scan_type;
+ struct ad4695_channel_config *cfg;
unsigned int reg_val;
int ret, tmp;
+ u8 realbits;
+
+ if (chan->type == IIO_VOLTAGE)
+ cfg = &st->channels_cfg[chan->scan_index];
+
+ scan_type = iio_get_current_scan_type(indio_dev, chan);
+ if (IS_ERR(scan_type))
+ return PTR_ERR(scan_type);
+
+ realbits = scan_type->realbits;
switch (mask) {
case IIO_CHAN_INFO_RAW:
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- ret = ad4695_read_one_sample(st, chan->address);
- if (ret)
- return ret;
-
- if (chan->scan_type.sign == 's')
- *val = sign_extend32(st->raw_data, realbits - 1);
- else
- *val = st->raw_data;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
- return IIO_VAL_INT;
- }
- unreachable();
+ ret = __ad4695_read_info_raw(st, chan, val);
+ iio_device_release_direct(indio_dev);
+ return ret;
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_VOLTAGE:
*val = st->vref_mv;
- *val2 = chan->scan_type.realbits;
+ *val2 = realbits;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_TEMP:
/* T_scale (°C) = raw * V_REF (mV) / (-1.8 mV/°C * 2^16) */
@@ -717,111 +1113,245 @@ static int ad4695_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_CALIBSCALE:
switch (chan->type) {
case IIO_VOLTAGE:
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- ret = regmap_read(st->regmap16,
- AD4695_REG_GAIN_IN(chan->scan_index),
- &reg_val);
- if (ret)
- return ret;
-
- *val = reg_val;
- *val2 = 15;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = regmap_read(st->regmap16,
+ AD4695_REG_GAIN_IN(chan->scan_index),
+ &reg_val);
+ iio_device_release_direct(indio_dev);
+ if (ret)
+ return ret;
+ *val = reg_val;
+ *val2 = 15;
- return IIO_VAL_FRACTIONAL_LOG2;
- }
- unreachable();
+ return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_CALIBBIAS:
- switch (chan->type) {
- case IIO_VOLTAGE:
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- ret = regmap_read(st->regmap16,
- AD4695_REG_OFFSET_IN(chan->scan_index),
- &reg_val);
- if (ret)
- return ret;
+ switch (chan->type)
+ case IIO_VOLTAGE: {
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = regmap_read(st->regmap16,
+ AD4695_REG_OFFSET_IN(chan->scan_index),
+ &reg_val);
+ iio_device_release_direct(indio_dev);
+ if (ret)
+ return ret;
- tmp = sign_extend32(reg_val, 15);
+ tmp = sign_extend32(reg_val, 15);
+ switch (cfg->oversampling_ratio) {
+ case 1:
*val = tmp / 4;
*val2 = abs(tmp) % 4 * MICRO / 4;
+ break;
+ case 4:
+ *val = tmp / 2;
+ *val2 = abs(tmp) % 2 * MICRO / 2;
+ break;
+ case 16:
+ *val = tmp;
+ *val2 = 0;
+ break;
+ case 64:
+ *val = tmp * 2;
+ *val2 = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
- if (tmp < 0 && *val2) {
- *val *= -1;
- *val2 *= -1;
- }
-
- return IIO_VAL_INT_PLUS_MICRO;
+ if (tmp < 0 && *val2) {
+ *val *= -1;
+ *val2 *= -1;
}
- unreachable();
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ *val = cfg->oversampling_ratio;
+ return IIO_VAL_INT;
default:
return -EINVAL;
}
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ struct pwm_state state;
+ unsigned int osr = 1;
+
+ if (chan->type == IIO_VOLTAGE)
+ osr = cfg->oversampling_ratio;
+
+ ret = pwm_get_state_hw(st->cnv_pwm, &state);
+ if (ret)
+ return ret;
+
+ /*
+ * The effective sampling frequency for a channel is the input
+ * frequency divided by the channel's OSR value.
+ */
+ *val = DIV_ROUND_UP_ULL(NSEC_PER_SEC, state.period * osr);
+
+ return IIO_VAL_INT;
+ }
default:
return -EINVAL;
}
}
-static int ad4695_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val, int val2, long mask)
+static int ad4695_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return IIO_VAL_INT;
+ default:
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+}
+
+static int ad4695_set_osr_val(struct ad4695_state *st,
+ struct iio_chan_spec const *chan,
+ int val)
+{
+ int osr = ad4695_osr_to_regval(val);
+
+ if (osr < 0)
+ return osr;
+
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ st->channels_cfg[chan->scan_index].oversampling_ratio = val;
+ return regmap_update_bits(st->regmap,
+ AD4695_REG_CONFIG_IN(chan->scan_index),
+ AD4695_REG_CONFIG_IN_OSR_SET,
+ FIELD_PREP(AD4695_REG_CONFIG_IN_OSR_SET, osr));
+ default:
+ return -EINVAL;
+ }
+}
+
+static unsigned int ad4695_get_calibbias(int val, int val2, int osr)
+{
+ int val_calc, scale;
+
+ switch (osr) {
+ case 4:
+ scale = 4;
+ break;
+ case 16:
+ scale = 2;
+ break;
+ case 64:
+ scale = 1;
+ break;
+ default:
+ scale = 8;
+ break;
+ }
+
+ val = clamp_t(int, val, S32_MIN / 8, S32_MAX / 8);
+
+ /* val2 range is (-MICRO, MICRO) if val == 0, otherwise [0, MICRO) */
+ if (val < 0)
+ val_calc = val * scale - val2 * scale / MICRO;
+ else if (val2 < 0)
+ /* if val2 < 0 then val == 0 */
+ val_calc = val2 * scale / (int)MICRO;
+ else
+ val_calc = val * scale + val2 * scale / MICRO;
+
+ val_calc /= 2;
+
+ return clamp_t(int, val_calc, S16_MIN, S16_MAX);
+}
+
+static int __ad4695_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
{
struct ad4695_state *st = iio_priv(indio_dev);
unsigned int reg_val;
+ unsigned int osr = 1;
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- switch (mask) {
- case IIO_CHAN_INFO_CALIBSCALE:
- switch (chan->type) {
- case IIO_VOLTAGE:
- if (val < 0 || val2 < 0)
- reg_val = 0;
- else if (val > 1)
- reg_val = U16_MAX;
- else
- reg_val = (val * (1 << 16) +
- mul_u64_u32_div(val2, 1 << 16,
- MICRO)) / 2;
-
- return regmap_write(st->regmap16,
- AD4695_REG_GAIN_IN(chan->scan_index),
- reg_val);
- default:
- return -EINVAL;
- }
- case IIO_CHAN_INFO_CALIBBIAS:
- switch (chan->type) {
- case IIO_VOLTAGE:
- if (val2 >= 0 && val > S16_MAX / 4)
- reg_val = S16_MAX;
- else if ((val2 < 0 ? -val : val) < S16_MIN / 4)
- reg_val = S16_MIN;
- else if (val2 < 0)
- reg_val = clamp_t(int,
- -(val * 4 + -val2 * 4 / MICRO),
- S16_MIN, S16_MAX);
- else if (val < 0)
- reg_val = clamp_t(int,
- val * 4 - val2 * 4 / MICRO,
- S16_MIN, S16_MAX);
- else
- reg_val = clamp_t(int,
- val * 4 + val2 * 4 / MICRO,
- S16_MIN, S16_MAX);
-
- return regmap_write(st->regmap16,
- AD4695_REG_OFFSET_IN(chan->scan_index),
- reg_val);
- default:
- return -EINVAL;
- }
+ if (chan->type == IIO_VOLTAGE)
+ osr = st->channels_cfg[chan->scan_index].oversampling_ratio;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBSCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ if (val < 0 || val2 < 0)
+ reg_val = 0;
+ else if (val > 1)
+ reg_val = U16_MAX;
+ else
+ reg_val = (val * (1 << 16) +
+ mul_u64_u32_div(val2, 1 << 16,
+ MICRO)) / 2;
+
+ return regmap_write(st->regmap16,
+ AD4695_REG_GAIN_IN(chan->scan_index),
+ reg_val);
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_CALIBBIAS:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ reg_val = ad4695_get_calibbias(val, val2, osr);
+ return regmap_write(st->regmap16,
+ AD4695_REG_OFFSET_IN(chan->scan_index),
+ reg_val);
default:
return -EINVAL;
}
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ struct pwm_state state;
+ /*
+ * Limit the maximum acceptable sample rate according to
+ * the channel's oversampling ratio.
+ */
+ u64 max_osr_rate = DIV_ROUND_UP_ULL(st->chip_info->max_sample_rate,
+ osr);
+
+ if (val <= 0 || val > max_osr_rate)
+ return -EINVAL;
+
+ guard(mutex)(&st->cnv_pwm_lock);
+ pwm_get_state(st->cnv_pwm, &state);
+ /*
+ * The required sample frequency for a given OSR is the
+ * input frequency multiplied by it.
+ */
+ state.period = DIV_ROUND_UP_ULL(NSEC_PER_SEC, val * osr);
+ return pwm_apply_might_sleep(st->cnv_pwm, &state);
+ }
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ return ad4695_set_osr_val(st, chan, val);
+ default:
+ return -EINVAL;
}
- unreachable();
+}
+
+static int ad4695_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = __ad4695_write_raw(indio_dev, chan, val, val2, mask);
+ iio_device_release_direct(indio_dev);
+
+ return ret;
}
static int ad4695_read_avail(struct iio_dev *indio_dev,
@@ -829,17 +1359,43 @@ static int ad4695_read_avail(struct iio_dev *indio_dev,
const int **vals, int *type, int *length,
long mask)
{
+ int ret;
static const int ad4695_calibscale_available[6] = {
/* Range of 0 (inclusive) to 2 (exclusive) */
0, 15, 1, 15, U16_MAX, 15
};
- static const int ad4695_calibbias_available[6] = {
+ static const int ad4695_calibbias_available[4][6] = {
/*
* Datasheet says FSR/8 which translates to signed/4. The step
- * depends on oversampling ratio which is always 1 for now.
+ * depends on oversampling ratio, so we need four different
+ * ranges to select from.
*/
- S16_MIN / 4, 0, 0, MICRO / 4, S16_MAX / 4, S16_MAX % 4 * MICRO / 4
+ {
+ S16_MIN / 4, 0,
+ 0, MICRO / 4,
+ S16_MAX / 4, S16_MAX % 4 * MICRO / 4
+ },
+ {
+ S16_MIN / 2, 0,
+ 0, MICRO / 2,
+ S16_MAX / 2, S16_MAX % 2 * MICRO / 2,
+ },
+ {
+ S16_MIN, 0,
+ 1, 0,
+ S16_MAX, 0,
+ },
+ {
+ S16_MIN * 2, 0,
+ 2, 0,
+ S16_MAX * 2, 0,
+ },
};
+ struct ad4695_state *st = iio_priv(indio_dev);
+ unsigned int osr = 1;
+
+ if (chan->type == IIO_VOLTAGE)
+ osr = st->channels_cfg[chan->scan_index].oversampling_ratio;
switch (mask) {
case IIO_CHAN_INFO_CALIBSCALE:
@@ -854,12 +1410,36 @@ static int ad4695_read_avail(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_CALIBBIAS:
switch (chan->type) {
case IIO_VOLTAGE:
- *vals = ad4695_calibbias_available;
+ ret = ad4695_osr_to_regval(osr);
+ if (ret < 0)
+ return ret;
+ /*
+ * Select the appropriate calibbias array based on the
+ * OSR value in the register.
+ */
+ *vals = ad4695_calibbias_available[ret];
*type = IIO_VAL_INT_PLUS_MICRO;
return IIO_AVAIL_RANGE;
default:
return -EINVAL;
}
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ /* Max sample rate for the channel depends on OSR */
+ st->sample_freq_range[2] =
+ DIV_ROUND_UP_ULL(st->chip_info->max_sample_rate, osr);
+ *vals = st->sample_freq_range;
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_RANGE;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ *vals = ad4695_oversampling_ratios;
+ *length = ARRAY_SIZE(ad4695_oversampling_ratios);
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
default:
return -EINVAL;
}
@@ -871,31 +1451,64 @@ static int ad4695_debugfs_reg_access(struct iio_dev *indio_dev,
unsigned int *readval)
{
struct ad4695_state *st = iio_priv(indio_dev);
-
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- if (readval) {
- if (regmap_check_range_table(st->regmap, reg,
- &ad4695_regmap_rd_table))
- return regmap_read(st->regmap, reg, readval);
- if (regmap_check_range_table(st->regmap16, reg,
- &ad4695_regmap16_rd_table))
- return regmap_read(st->regmap16, reg, readval);
- } else {
- if (regmap_check_range_table(st->regmap, reg,
- &ad4695_regmap_wr_table))
- return regmap_write(st->regmap, reg, writeval);
- if (regmap_check_range_table(st->regmap16, reg,
- &ad4695_regmap16_wr_table))
- return regmap_write(st->regmap16, reg, writeval);
- }
+ int ret = -EINVAL;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ if (readval) {
+ if (regmap_check_range_table(st->regmap, reg,
+ &ad4695_regmap_rd_table))
+ ret = regmap_read(st->regmap, reg, readval);
+ if (regmap_check_range_table(st->regmap16, reg,
+ &ad4695_regmap16_rd_table))
+ ret = regmap_read(st->regmap16, reg, readval);
+ } else {
+ if (regmap_check_range_table(st->regmap, reg,
+ &ad4695_regmap_wr_table))
+ ret = regmap_write(st->regmap, reg, writeval);
+ if (regmap_check_range_table(st->regmap16, reg,
+ &ad4695_regmap16_wr_table))
+ ret = regmap_write(st->regmap16, reg, writeval);
}
+ iio_device_release_direct(indio_dev);
- return -EINVAL;
+ return ret;
+}
+
+static int ad4695_get_current_scan_type(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad4695_state *st = iio_priv(indio_dev);
+ unsigned int osr = st->channels_cfg[chan->scan_index].oversampling_ratio;
+
+ switch (osr) {
+ case 1:
+ return AD4695_SCAN_TYPE_OSR_1;
+ case 4:
+ return AD4695_SCAN_TYPE_OSR_4;
+ case 16:
+ return AD4695_SCAN_TYPE_OSR_16;
+ case 64:
+ return AD4695_SCAN_TYPE_OSR_64;
+ default:
+ return -EINVAL;
+ }
}
static const struct iio_info ad4695_info = {
.read_raw = &ad4695_read_raw,
+ .write_raw_get_fmt = &ad4695_write_raw_get_fmt,
+ .write_raw = &ad4695_write_raw,
+ .read_avail = &ad4695_read_avail,
+ .debugfs_reg_access = &ad4695_debugfs_reg_access,
+};
+
+static const struct iio_info ad4695_offload_info = {
+ .read_raw = &ad4695_read_raw,
+ .write_raw_get_fmt = &ad4695_write_raw_get_fmt,
.write_raw = &ad4695_write_raw,
+ .get_current_scan_type = &ad4695_get_current_scan_type,
.read_avail = &ad4695_read_avail,
.debugfs_reg_access = &ad4695_debugfs_reg_access,
};
@@ -915,6 +1528,9 @@ static int ad4695_parse_channel_cfg(struct ad4695_state *st)
chan_cfg->highz_en = true;
chan_cfg->channel = i;
+ /* This is the default OSR after reset */
+ chan_cfg->oversampling_ratio = 1;
+
*iio_chan = ad4695_channel_template;
iio_chan->channel = i;
iio_chan->scan_index = i;
@@ -1008,26 +1624,188 @@ static int ad4695_parse_channel_cfg(struct ad4695_state *st)
return 0;
}
+static bool ad4695_offload_trigger_match(struct spi_offload_trigger *trigger,
+ enum spi_offload_trigger_type type,
+ u64 *args, u32 nargs)
+{
+ if (type != SPI_OFFLOAD_TRIGGER_DATA_READY)
+ return false;
+
+ /*
+ * Requires 2 args:
+ * args[0] is the trigger event.
+ * args[1] is the GPIO pin number.
+ */
+ if (nargs != 2 || args[0] != AD4695_TRIGGER_EVENT_BUSY)
+ return false;
+
+ return true;
+}
+
+static int ad4695_offload_trigger_request(struct spi_offload_trigger *trigger,
+ enum spi_offload_trigger_type type,
+ u64 *args, u32 nargs)
+{
+ struct ad4695_state *st = spi_offload_trigger_get_priv(trigger);
+
+ /* Should already be validated by match, but just in case. */
+ if (nargs != 2)
+ return -EINVAL;
+
+ /* DT tells us if BUSY event uses GP0 or GP3. */
+ if (args[1] == AD4695_TRIGGER_PIN_GP3)
+ return regmap_set_bits(st->regmap, AD4695_REG_GP_MODE,
+ AD4695_REG_GP_MODE_BUSY_GP_SEL);
+
+ return regmap_clear_bits(st->regmap, AD4695_REG_GP_MODE,
+ AD4695_REG_GP_MODE_BUSY_GP_SEL);
+}
+
+static int
+ad4695_offload_trigger_validate(struct spi_offload_trigger *trigger,
+ struct spi_offload_trigger_config *config)
+{
+ if (config->type != SPI_OFFLOAD_TRIGGER_DATA_READY)
+ return -EINVAL;
+
+ return 0;
+}
+
+/*
+ * NB: There are no enable/disable callbacks here due to requiring a SPI
+ * message to enable or disable the BUSY output on the ADC.
+ */
+static const struct spi_offload_trigger_ops ad4695_offload_trigger_ops = {
+ .match = ad4695_offload_trigger_match,
+ .request = ad4695_offload_trigger_request,
+ .validate = ad4695_offload_trigger_validate,
+};
+
+static void ad4695_pwm_disable(void *pwm)
+{
+ pwm_disable(pwm);
+}
+
+static int ad4695_probe_spi_offload(struct iio_dev *indio_dev,
+ struct ad4695_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ struct spi_offload_trigger_info trigger_info = {
+ .fwnode = dev_fwnode(dev),
+ .ops = &ad4695_offload_trigger_ops,
+ .priv = st,
+ };
+ struct pwm_state pwm_state;
+ struct dma_chan *rx_dma;
+ int ret, i;
+
+ indio_dev->info = &ad4695_offload_info;
+ indio_dev->num_channels = st->chip_info->num_voltage_inputs + 1;
+ indio_dev->setup_ops = &ad4695_offload_buffer_setup_ops;
+
+ if (!st->cnv_gpio)
+ return dev_err_probe(dev, -ENODEV,
+ "CNV GPIO is required for SPI offload\n");
+
+ ret = devm_spi_offload_trigger_register(dev, &trigger_info);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to register offload trigger\n");
+
+ st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload,
+ SPI_OFFLOAD_TRIGGER_DATA_READY);
+ if (IS_ERR(st->offload_trigger))
+ return dev_err_probe(dev, PTR_ERR(st->offload_trigger),
+ "failed to get offload trigger\n");
+
+ ret = devm_mutex_init(dev, &st->cnv_pwm_lock);
+ if (ret)
+ return ret;
+
+ st->cnv_pwm = devm_pwm_get(dev, NULL);
+ if (IS_ERR(st->cnv_pwm))
+ return dev_err_probe(dev, PTR_ERR(st->cnv_pwm),
+ "failed to get CNV PWM\n");
+
+ pwm_init_state(st->cnv_pwm, &pwm_state);
+
+ /* If firmware didn't provide default rate, use 10kHz (arbitrary). */
+ if (pwm_state.period == 0)
+ pwm_state.period = 100 * MILLI;
+
+ pwm_state.enabled = true;
+
+ ret = pwm_apply_might_sleep(st->cnv_pwm, &pwm_state);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to apply CNV PWM\n");
+
+ ret = devm_add_action_or_reset(dev, ad4695_pwm_disable, st->cnv_pwm);
+ if (ret)
+ return ret;
+
+ rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload);
+ if (IS_ERR(rx_dma))
+ return dev_err_probe(dev, PTR_ERR(rx_dma),
+ "failed to get offload RX DMA\n");
+
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ struct iio_chan_spec *chan = &st->iio_chan[i];
+ struct ad4695_channel_config *cfg;
+
+ /*
+ * NB: When using offload support, all channels need to have the
+ * same bits_per_word because they all use the same SPI message
+ * for reading one sample. In order to prevent breaking
+ * userspace in the future when oversampling support is added,
+ * all channels are set read 19 bits with a shift of 3 to mask
+ * out the extra bits even though we currently only support 16
+ * bit samples (oversampling ratio == 1).
+ */
+ chan->scan_type.shift = 3;
+ chan->scan_type.storagebits = 32;
+ /* add sample frequency for PWM CNV trigger */
+ chan->info_mask_separate |= BIT(IIO_CHAN_INFO_SAMP_FREQ);
+ chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_SAMP_FREQ);
+
+ /* Add the oversampling properties only for voltage channels */
+ if (chan->type != IIO_VOLTAGE)
+ continue;
+
+ cfg = &st->channels_cfg[i];
+
+ chan->info_mask_separate |= BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
+ chan->info_mask_separate_available |=
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
+ chan->has_ext_scan_type = 1;
+ if (cfg->bipolar) {
+ chan->ext_scan_type = ad4695_scan_type_offload_s;
+ chan->num_ext_scan_type =
+ ARRAY_SIZE(ad4695_scan_type_offload_s);
+ } else {
+ chan->ext_scan_type = ad4695_scan_type_offload_u;
+ chan->num_ext_scan_type =
+ ARRAY_SIZE(ad4695_scan_type_offload_u);
+ }
+ }
+
+ return devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev,
+ rx_dma, IIO_BUFFER_DIRECTION_IN);
+}
+
+static const struct spi_offload_config ad4695_spi_offload_config = {
+ .capability_flags = SPI_OFFLOAD_CAP_TRIGGER |
+ SPI_OFFLOAD_CAP_RX_STREAM_DMA,
+};
+
static int ad4695_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
struct ad4695_state *st;
struct iio_dev *indio_dev;
- struct gpio_desc *cnv_gpio;
bool use_internal_ldo_supply;
bool use_internal_ref_buffer;
int ret;
- cnv_gpio = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_LOW);
- if (IS_ERR(cnv_gpio))
- return dev_err_probe(dev, PTR_ERR(cnv_gpio),
- "Failed to get CNV GPIO\n");
-
- /* Driver currently requires CNV pin to be connected to SPI CS */
- if (cnv_gpio)
- return dev_err_probe(dev, -ENODEV,
- "CNV GPIO is not supported\n");
-
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
@@ -1039,19 +1817,27 @@ static int ad4695_probe(struct spi_device *spi)
if (!st->chip_info)
return -EINVAL;
- /* Registers cannot be read at the max allowable speed */
- spi->max_speed_hz = AD4695_REG_ACCESS_SCLK_HZ;
+ st->sample_freq_range[0] = 1; /* min */
+ st->sample_freq_range[1] = 1; /* step */
+ st->sample_freq_range[2] = st->chip_info->max_sample_rate; /* max */
- st->regmap = devm_regmap_init_spi(spi, &ad4695_regmap_config);
+ st->regmap = devm_regmap_init(dev, &ad4695_regmap_bus, st,
+ &ad4695_regmap_config);
if (IS_ERR(st->regmap))
return dev_err_probe(dev, PTR_ERR(st->regmap),
"Failed to initialize regmap\n");
- st->regmap16 = devm_regmap_init_spi(spi, &ad4695_regmap16_config);
+ st->regmap16 = devm_regmap_init(dev, &ad4695_regmap_bus, st,
+ &ad4695_regmap16_config);
if (IS_ERR(st->regmap16))
return dev_err_probe(dev, PTR_ERR(st->regmap16),
"Failed to initialize regmap16\n");
+ st->cnv_gpio = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_LOW);
+ if (IS_ERR(st->cnv_gpio))
+ return dev_err_probe(dev, PTR_ERR(st->cnv_gpio),
+ "Failed to get CNV GPIO\n");
+
ret = devm_regulator_bulk_get_enable(dev,
ARRAY_SIZE(ad4695_power_supplies),
ad4695_power_supplies);
@@ -1179,12 +1965,31 @@ static int ad4695_probe(struct spi_device *spi)
indio_dev->channels = st->iio_chan;
indio_dev->num_channels = st->chip_info->num_voltage_inputs + 2;
- ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
- iio_pollfunc_store_time,
- ad4695_trigger_handler,
- &ad4695_buffer_setup_ops);
- if (ret)
- return ret;
+ st->offload = devm_spi_offload_get(dev, spi, &ad4695_spi_offload_config);
+ ret = PTR_ERR_OR_ZERO(st->offload);
+ if (ret && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "failed to get SPI offload\n");
+
+ /* If no SPI offload, fall back to low speed usage. */
+ if (ret == -ENODEV) {
+ /* Driver currently requires CNV pin to be connected to SPI CS */
+ if (st->cnv_gpio)
+ return dev_err_probe(dev, -EINVAL,
+ "CNV GPIO is not supported\n");
+
+ indio_dev->num_channels = st->chip_info->num_voltage_inputs + 2;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ ad4695_trigger_handler,
+ &ad4695_buffer_setup_ops);
+ if (ret)
+ return ret;
+ } else {
+ ret = ad4695_probe_spi_offload(indio_dev, st);
+ if (ret)
+ return ret;
+ }
return devm_iio_device_register(dev, indio_dev);
}
@@ -1221,3 +2026,4 @@ MODULE_AUTHOR("Ramona Gradinariu <ramona.gradinariu@analog.com>");
MODULE_AUTHOR("David Lechner <dlechner@baylibre.com>");
MODULE_DESCRIPTION("Analog Devices AD4695 ADC driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER");
diff --git a/drivers/iio/adc/ad4851.c b/drivers/iio/adc/ad4851.c
new file mode 100644
index 000000000000..98ebc853db79
--- /dev/null
+++ b/drivers/iio/adc/ad4851.c
@@ -0,0 +1,1315 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices AD4851 DAS driver
+ *
+ * Copyright 2024 Analog Devices Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/minmax.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+#include <linux/units.h>
+
+#include <linux/iio/backend.h>
+#include <linux/iio/iio.h>
+
+#define AD4851_REG_INTERFACE_CONFIG_A 0x00
+#define AD4851_REG_INTERFACE_CONFIG_B 0x01
+#define AD4851_REG_PRODUCT_ID_L 0x04
+#define AD4851_REG_PRODUCT_ID_H 0x05
+#define AD4851_REG_DEVICE_CTRL 0x25
+#define AD4851_REG_PACKET 0x26
+#define AD4851_REG_OVERSAMPLE 0x27
+
+#define AD4851_REG_CH_CONFIG_BASE 0x2A
+#define AD4851_REG_CHX_SOFTSPAN(ch) ((0x12 * (ch)) + AD4851_REG_CH_CONFIG_BASE)
+#define AD4851_REG_CHX_OFFSET(ch) (AD4851_REG_CHX_SOFTSPAN(ch) + 0x01)
+#define AD4851_REG_CHX_OFFSET_LSB(ch) AD4851_REG_CHX_OFFSET(ch)
+#define AD4851_REG_CHX_OFFSET_MID(ch) (AD4851_REG_CHX_OFFSET_LSB(ch) + 0x01)
+#define AD4851_REG_CHX_OFFSET_MSB(ch) (AD4851_REG_CHX_OFFSET_MID(ch) + 0x01)
+#define AD4851_REG_CHX_GAIN(ch) (AD4851_REG_CHX_OFFSET(ch) + 0x03)
+#define AD4851_REG_CHX_GAIN_LSB(ch) AD4851_REG_CHX_GAIN(ch)
+#define AD4851_REG_CHX_GAIN_MSB(ch) (AD4851_REG_CHX_GAIN(ch) + 0x01)
+#define AD4851_REG_CHX_PHASE(ch) (AD4851_REG_CHX_GAIN(ch) + 0x02)
+#define AD4851_REG_CHX_PHASE_LSB(ch) AD4851_REG_CHX_PHASE(ch)
+#define AD4851_REG_CHX_PHASE_MSB(ch) (AD4851_REG_CHX_PHASE_LSB(ch) + 0x01)
+
+#define AD4851_REG_TESTPAT_0(c) (0x38 + (c) * 0x12)
+#define AD4851_REG_TESTPAT_1(c) (0x39 + (c) * 0x12)
+#define AD4851_REG_TESTPAT_2(c) (0x3A + (c) * 0x12)
+#define AD4851_REG_TESTPAT_3(c) (0x3B + (c) * 0x12)
+
+#define AD4851_SW_RESET (BIT(7) | BIT(0))
+#define AD4851_SDO_ENABLE BIT(4)
+#define AD4851_SINGLE_INSTRUCTION BIT(7)
+#define AD4851_REFBUF BIT(2)
+#define AD4851_REFSEL BIT(1)
+#define AD4851_ECHO_CLOCK_MODE BIT(0)
+
+#define AD4851_PACKET_FORMAT_0 0
+#define AD4851_PACKET_FORMAT_1 1
+#define AD4851_PACKET_FORMAT_MASK GENMASK(1, 0)
+
+#define AD4851_OS_EN_MSK BIT(7)
+#define AD4851_OS_RATIO_MSK GENMASK(3, 0)
+
+#define AD4851_TEST_PAT BIT(2)
+
+#define AD4858_PACKET_SIZE_20 0
+#define AD4858_PACKET_SIZE_24 1
+#define AD4858_PACKET_SIZE_32 2
+
+#define AD4857_PACKET_SIZE_16 0
+#define AD4857_PACKET_SIZE_24 1
+
+#define AD4851_TESTPAT_0_DEFAULT 0x2A
+#define AD4851_TESTPAT_1_DEFAULT 0x3C
+#define AD4851_TESTPAT_2_DEFAULT 0xCE
+#define AD4851_TESTPAT_3_DEFAULT(c) (0x0A + (0x10 * (c)))
+
+#define AD4851_SOFTSPAN_0V_2V5 0
+#define AD4851_SOFTSPAN_N2V5_2V5 1
+#define AD4851_SOFTSPAN_0V_5V 2
+#define AD4851_SOFTSPAN_N5V_5V 3
+#define AD4851_SOFTSPAN_0V_6V25 4
+#define AD4851_SOFTSPAN_N6V25_6V25 5
+#define AD4851_SOFTSPAN_0V_10V 6
+#define AD4851_SOFTSPAN_N10V_10V 7
+#define AD4851_SOFTSPAN_0V_12V5 8
+#define AD4851_SOFTSPAN_N12V5_12V5 9
+#define AD4851_SOFTSPAN_0V_20V 10
+#define AD4851_SOFTSPAN_N20V_20V 11
+#define AD4851_SOFTSPAN_0V_25V 12
+#define AD4851_SOFTSPAN_N25V_25V 13
+#define AD4851_SOFTSPAN_0V_40V 14
+#define AD4851_SOFTSPAN_N40V_40V 15
+
+#define AD4851_MAX_LANES 8
+#define AD4851_MAX_IODELAY 32
+
+#define AD4851_T_CNVH_NS 40
+#define AD4851_T_CNVH_NS_MARGIN 10
+
+#define AD4841_MAX_SCALE_AVAIL 8
+
+#define AD4851_MAX_CH_NR 8
+#define AD4851_CH_START 0
+
+struct ad4851_scale {
+ unsigned int scale_val;
+ u8 reg_val;
+};
+
+static const struct ad4851_scale ad4851_scale_table_unipolar[] = {
+ { 2500, 0x0 },
+ { 5000, 0x2 },
+ { 6250, 0x4 },
+ { 10000, 0x6 },
+ { 12500, 0x8 },
+ { 20000, 0xA },
+ { 25000, 0xC },
+ { 40000, 0xE },
+};
+
+static const struct ad4851_scale ad4851_scale_table_bipolar[] = {
+ { 5000, 0x1 },
+ { 10000, 0x3 },
+ { 12500, 0x5 },
+ { 20000, 0x7 },
+ { 25000, 0x9 },
+ { 40000, 0xB },
+ { 50000, 0xD },
+ { 80000, 0xF },
+};
+
+static const unsigned int ad4851_scale_avail_unipolar[] = {
+ 2500,
+ 5000,
+ 6250,
+ 10000,
+ 12500,
+ 20000,
+ 25000,
+ 40000,
+};
+
+static const unsigned int ad4851_scale_avail_bipolar[] = {
+ 5000,
+ 10000,
+ 12500,
+ 20000,
+ 25000,
+ 40000,
+ 50000,
+ 80000,
+};
+
+struct ad4851_chip_info {
+ const char *name;
+ unsigned int product_id;
+ int num_scales;
+ unsigned long max_sample_rate_hz;
+ unsigned int resolution;
+ unsigned int max_channels;
+ int (*parse_channels)(struct iio_dev *indio_dev);
+};
+
+enum {
+ AD4851_SCAN_TYPE_NORMAL,
+ AD4851_SCAN_TYPE_RESOLUTION_BOOST,
+};
+
+struct ad4851_state {
+ struct spi_device *spi;
+ struct pwm_device *cnv;
+ struct iio_backend *back;
+ /*
+ * Synchronize access to members the of driver state, and ensure
+ * atomicity of consecutive regmap operations.
+ */
+ struct mutex lock;
+ struct regmap *regmap;
+ const struct ad4851_chip_info *info;
+ struct gpio_desc *pd_gpio;
+ bool resolution_boost_enabled;
+ unsigned long cnv_trigger_rate_hz;
+ unsigned int osr;
+ bool vrefbuf_en;
+ bool vrefio_en;
+ bool bipolar_ch[AD4851_MAX_CH_NR];
+ unsigned int scales_unipolar[AD4841_MAX_SCALE_AVAIL][2];
+ unsigned int scales_bipolar[AD4841_MAX_SCALE_AVAIL][2];
+};
+
+static int ad4851_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ad4851_state *st = iio_priv(indio_dev);
+
+ if (readval)
+ return regmap_read(st->regmap, reg, readval);
+
+ return regmap_write(st->regmap, reg, writeval);
+}
+
+static int ad4851_set_sampling_freq(struct ad4851_state *st, unsigned int freq)
+{
+ struct pwm_state cnv_state = {
+ .duty_cycle = AD4851_T_CNVH_NS + AD4851_T_CNVH_NS_MARGIN,
+ .enabled = true,
+ };
+ int ret;
+
+ freq = clamp(freq, 1, st->info->max_sample_rate_hz);
+
+ cnv_state.period = DIV_ROUND_UP_ULL(NSEC_PER_SEC, freq);
+
+ ret = pwm_apply_might_sleep(st->cnv, &cnv_state);
+ if (ret)
+ return ret;
+
+ st->cnv_trigger_rate_hz = freq;
+
+ return 0;
+}
+
+static const int ad4851_oversampling_ratios[] = {
+ 1, 2, 4, 8, 16, 32, 64, 128,
+ 256, 512, 1024, 2048, 4096, 8192, 16384, 32768,
+ 65536,
+};
+
+static int ad4851_osr_to_regval(unsigned int ratio)
+{
+ int i;
+
+ for (i = 1; i < ARRAY_SIZE(ad4851_oversampling_ratios); i++)
+ if (ratio == ad4851_oversampling_ratios[i])
+ return i - 1;
+
+ return -EINVAL;
+}
+
+static int __ad4851_get_scale(struct iio_dev *indio_dev, int scale_tbl,
+ unsigned int *val, unsigned int *val2)
+{
+ const struct iio_scan_type *scan_type;
+ unsigned int tmp;
+
+ scan_type = iio_get_current_scan_type(indio_dev, &indio_dev->channels[0]);
+ if (IS_ERR(scan_type))
+ return PTR_ERR(scan_type);
+
+ tmp = ((u64)scale_tbl * MICRO) >> scan_type->realbits;
+ *val = tmp / MICRO;
+ *val2 = tmp % MICRO;
+
+ return 0;
+}
+
+static int ad4851_scale_fill(struct iio_dev *indio_dev)
+{
+ struct ad4851_state *st = iio_priv(indio_dev);
+ unsigned int i, val1, val2;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(ad4851_scale_avail_unipolar); i++) {
+ ret = __ad4851_get_scale(indio_dev,
+ ad4851_scale_avail_unipolar[i],
+ &val1, &val2);
+ if (ret)
+ return ret;
+
+ st->scales_unipolar[i][0] = val1;
+ st->scales_unipolar[i][1] = val2;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ad4851_scale_avail_bipolar); i++) {
+ ret = __ad4851_get_scale(indio_dev,
+ ad4851_scale_avail_bipolar[i],
+ &val1, &val2);
+ if (ret)
+ return ret;
+
+ st->scales_bipolar[i][0] = val1;
+ st->scales_bipolar[i][1] = val2;
+ }
+
+ return 0;
+}
+
+static int ad4851_set_oversampling_ratio(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int osr)
+{
+ struct ad4851_state *st = iio_priv(indio_dev);
+ int val, ret;
+
+ guard(mutex)(&st->lock);
+
+ if (osr == 1) {
+ ret = regmap_clear_bits(st->regmap, AD4851_REG_OVERSAMPLE,
+ AD4851_OS_EN_MSK);
+ if (ret)
+ return ret;
+ } else {
+ val = ad4851_osr_to_regval(osr);
+ if (val < 0)
+ return -EINVAL;
+
+ ret = regmap_update_bits(st->regmap, AD4851_REG_OVERSAMPLE,
+ AD4851_OS_EN_MSK |
+ AD4851_OS_RATIO_MSK,
+ FIELD_PREP(AD4851_OS_EN_MSK, 1) |
+ FIELD_PREP(AD4851_OS_RATIO_MSK, val));
+ if (ret)
+ return ret;
+ }
+
+ ret = iio_backend_oversampling_ratio_set(st->back, osr);
+ if (ret)
+ return ret;
+
+ switch (st->info->resolution) {
+ case 20:
+ switch (osr) {
+ case 0:
+ return -EINVAL;
+ case 1:
+ val = 20;
+ break;
+ default:
+ val = 24;
+ break;
+ }
+ break;
+ case 16:
+ val = 16;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = iio_backend_data_size_set(st->back, val);
+ if (ret)
+ return ret;
+
+ if (osr == 1 || st->info->resolution == 16) {
+ ret = regmap_clear_bits(st->regmap, AD4851_REG_PACKET,
+ AD4851_PACKET_FORMAT_MASK);
+ if (ret)
+ return ret;
+
+ st->resolution_boost_enabled = false;
+ } else {
+ ret = regmap_update_bits(st->regmap, AD4851_REG_PACKET,
+ AD4851_PACKET_FORMAT_MASK,
+ FIELD_PREP(AD4851_PACKET_FORMAT_MASK, 1));
+ if (ret)
+ return ret;
+
+ st->resolution_boost_enabled = true;
+ }
+
+ if (st->osr != osr) {
+ ret = ad4851_scale_fill(indio_dev);
+ if (ret)
+ return ret;
+
+ st->osr = osr;
+ }
+
+ return 0;
+}
+
+static int ad4851_get_oversampling_ratio(struct ad4851_state *st, unsigned int *val)
+{
+ unsigned int osr;
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ ret = regmap_read(st->regmap, AD4851_REG_OVERSAMPLE, &osr);
+ if (ret)
+ return ret;
+
+ if (!FIELD_GET(AD4851_OS_EN_MSK, osr))
+ *val = 1;
+ else
+ *val = ad4851_oversampling_ratios[FIELD_GET(AD4851_OS_RATIO_MSK, osr) + 1];
+
+ st->osr = *val;
+
+ return IIO_VAL_INT;
+}
+
+static void ad4851_pwm_disable(void *data)
+{
+ pwm_disable(data);
+}
+
+static int ad4851_setup(struct ad4851_state *st)
+{
+ unsigned int product_id;
+ int ret;
+
+ if (st->pd_gpio) {
+ /* To initiate a global reset, bring the PD pin high twice */
+ gpiod_set_value(st->pd_gpio, 1);
+ fsleep(1);
+ gpiod_set_value(st->pd_gpio, 0);
+ fsleep(1);
+ gpiod_set_value(st->pd_gpio, 1);
+ fsleep(1);
+ gpiod_set_value(st->pd_gpio, 0);
+ fsleep(1000);
+ } else {
+ ret = regmap_set_bits(st->regmap, AD4851_REG_INTERFACE_CONFIG_A,
+ AD4851_SW_RESET);
+ if (ret)
+ return ret;
+ }
+
+ if (st->vrefbuf_en) {
+ ret = regmap_set_bits(st->regmap, AD4851_REG_DEVICE_CTRL,
+ AD4851_REFBUF);
+ if (ret)
+ return ret;
+ }
+
+ if (st->vrefio_en) {
+ ret = regmap_set_bits(st->regmap, AD4851_REG_DEVICE_CTRL,
+ AD4851_REFSEL);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_write(st->regmap, AD4851_REG_INTERFACE_CONFIG_B,
+ AD4851_SINGLE_INSTRUCTION);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD4851_REG_INTERFACE_CONFIG_A,
+ AD4851_SDO_ENABLE);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(st->regmap, AD4851_REG_PRODUCT_ID_L, &product_id);
+ if (ret)
+ return ret;
+
+ if (product_id != st->info->product_id)
+ dev_info(&st->spi->dev, "Unknown product ID: 0x%02X\n",
+ product_id);
+
+ ret = regmap_set_bits(st->regmap, AD4851_REG_DEVICE_CTRL,
+ AD4851_ECHO_CLOCK_MODE);
+ if (ret)
+ return ret;
+
+ return regmap_write(st->regmap, AD4851_REG_PACKET, 0);
+}
+
+/*
+ * Find the longest consecutive sequence of false values from field
+ * and return starting index.
+ */
+static int ad4851_find_opt(const unsigned long *field, unsigned int start,
+ unsigned int nbits, unsigned int *val)
+{
+ unsigned int bit = start, end, start_cnt, cnt = 0;
+
+ for_each_clear_bitrange_from(bit, end, field, start + nbits) {
+ if (end - bit > cnt) {
+ cnt = end - bit;
+ start_cnt = bit - start;
+ }
+ }
+
+ if (!cnt)
+ return -ENOENT;
+
+ *val = start_cnt;
+
+ return cnt;
+}
+
+static int ad4851_calibrate(struct iio_dev *indio_dev)
+{
+ struct ad4851_state *st = iio_priv(indio_dev);
+ unsigned int opt_delay, num_lanes, delay, i, s;
+ enum iio_backend_interface_type interface_type;
+ DECLARE_BITMAP(pn_status, AD4851_MAX_LANES * AD4851_MAX_IODELAY);
+ bool status;
+ int c, ret;
+
+ ret = iio_backend_interface_type_get(st->back, &interface_type);
+ if (ret)
+ return ret;
+
+ switch (interface_type) {
+ case IIO_BACKEND_INTERFACE_SERIAL_CMOS:
+ num_lanes = indio_dev->num_channels;
+ break;
+ case IIO_BACKEND_INTERFACE_SERIAL_LVDS:
+ num_lanes = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (st->info->resolution == 16) {
+ ret = iio_backend_data_size_set(st->back, 24);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD4851_REG_PACKET,
+ AD4851_TEST_PAT | AD4857_PACKET_SIZE_24);
+ if (ret)
+ return ret;
+ } else {
+ ret = iio_backend_data_size_set(st->back, 32);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD4851_REG_PACKET,
+ AD4851_TEST_PAT | AD4858_PACKET_SIZE_32);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_0(i),
+ AD4851_TESTPAT_0_DEFAULT);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_1(i),
+ AD4851_TESTPAT_1_DEFAULT);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_2(i),
+ AD4851_TESTPAT_2_DEFAULT);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_3(i),
+ AD4851_TESTPAT_3_DEFAULT(i));
+ if (ret)
+ return ret;
+
+ ret = iio_backend_chan_enable(st->back,
+ indio_dev->channels[i].channel);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < num_lanes; i++) {
+ for (delay = 0; delay < AD4851_MAX_IODELAY; delay++) {
+ ret = iio_backend_iodelay_set(st->back, i, delay);
+ if (ret)
+ return ret;
+
+ ret = iio_backend_chan_status(st->back, i, &status);
+ if (ret)
+ return ret;
+
+ __assign_bit(i * AD4851_MAX_IODELAY + delay, pn_status,
+ status);
+ }
+ }
+
+ for (i = 0; i < num_lanes; i++) {
+ c = ad4851_find_opt(pn_status, i * AD4851_MAX_IODELAY,
+ AD4851_MAX_IODELAY, &s);
+ if (c < 0)
+ return c;
+
+ opt_delay = s + c / 2;
+ ret = iio_backend_iodelay_set(st->back, i, opt_delay);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ ret = iio_backend_chan_disable(st->back, i);
+ if (ret)
+ return ret;
+ }
+
+ ret = iio_backend_data_size_set(st->back, 20);
+ if (ret)
+ return ret;
+
+ return regmap_write(st->regmap, AD4851_REG_PACKET, 0);
+}
+
+static int ad4851_get_calibscale(struct ad4851_state *st, int ch, int *val, int *val2)
+{
+ unsigned int reg_val;
+ int gain;
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ ret = regmap_read(st->regmap, AD4851_REG_CHX_GAIN_MSB(ch), &reg_val);
+ if (ret)
+ return ret;
+
+ gain = reg_val << 8;
+
+ ret = regmap_read(st->regmap, AD4851_REG_CHX_GAIN_LSB(ch), &reg_val);
+ if (ret)
+ return ret;
+
+ gain |= reg_val;
+
+ *val = gain;
+ *val2 = 15;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+}
+
+static int ad4851_set_calibscale(struct ad4851_state *st, int ch, int val,
+ int val2)
+{
+ u64 gain;
+ u8 buf[2];
+ int ret;
+
+ if (val < 0 || val2 < 0)
+ return -EINVAL;
+
+ gain = val * MICRO + val2;
+ gain = DIV_U64_ROUND_CLOSEST(gain * 32768, MICRO);
+
+ put_unaligned_be16(gain, buf);
+
+ guard(mutex)(&st->lock);
+
+ ret = regmap_write(st->regmap, AD4851_REG_CHX_GAIN_MSB(ch), buf[0]);
+ if (ret)
+ return ret;
+
+ return regmap_write(st->regmap, AD4851_REG_CHX_GAIN_LSB(ch), buf[1]);
+}
+
+static int ad4851_get_calibbias(struct ad4851_state *st, int ch, int *val)
+{
+ unsigned int lsb, mid, msb;
+ int ret;
+
+ guard(mutex)(&st->lock);
+ /*
+ * After testing, the bulk_write operations doesn't work as expected
+ * here since the cs needs to be raised after each byte transaction.
+ */
+ ret = regmap_read(st->regmap, AD4851_REG_CHX_OFFSET_MSB(ch), &msb);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(st->regmap, AD4851_REG_CHX_OFFSET_MID(ch), &mid);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(st->regmap, AD4851_REG_CHX_OFFSET_LSB(ch), &lsb);
+ if (ret)
+ return ret;
+
+ if (st->info->resolution == 16) {
+ *val = msb << 8;
+ *val |= mid;
+ *val = sign_extend32(*val, 15);
+ } else {
+ *val = msb << 12;
+ *val |= mid << 4;
+ *val |= lsb >> 4;
+ *val = sign_extend32(*val, 19);
+ }
+
+ return IIO_VAL_INT;
+}
+
+static int ad4851_set_calibbias(struct ad4851_state *st, int ch, int val)
+{
+ u8 buf[3];
+ int ret;
+
+ if (val < 0)
+ return -EINVAL;
+
+ if (st->info->resolution == 16)
+ put_unaligned_be16(val, buf);
+ else
+ put_unaligned_be24(val << 4, buf);
+
+ guard(mutex)(&st->lock);
+ /*
+ * After testing, the bulk_write operations doesn't work as expected
+ * here since the cs needs to be raised after each byte transaction.
+ */
+ ret = regmap_write(st->regmap, AD4851_REG_CHX_OFFSET_LSB(ch), buf[2]);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD4851_REG_CHX_OFFSET_MID(ch), buf[1]);
+ if (ret)
+ return ret;
+
+ return regmap_write(st->regmap, AD4851_REG_CHX_OFFSET_MSB(ch), buf[0]);
+}
+
+static int ad4851_set_scale(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int val, int val2)
+{
+ struct ad4851_state *st = iio_priv(indio_dev);
+ unsigned int scale_val[2];
+ unsigned int i;
+ const struct ad4851_scale *scale_table;
+ size_t table_size;
+ int ret;
+
+ if (st->bipolar_ch[chan->channel]) {
+ scale_table = ad4851_scale_table_bipolar;
+ table_size = ARRAY_SIZE(ad4851_scale_table_bipolar);
+ } else {
+ scale_table = ad4851_scale_table_unipolar;
+ table_size = ARRAY_SIZE(ad4851_scale_table_unipolar);
+ }
+
+ for (i = 0; i < table_size; i++) {
+ ret = __ad4851_get_scale(indio_dev, scale_table[i].scale_val,
+ &scale_val[0], &scale_val[1]);
+ if (ret)
+ return ret;
+
+ if (scale_val[0] != val || scale_val[1] != val2)
+ continue;
+
+ return regmap_write(st->regmap,
+ AD4851_REG_CHX_SOFTSPAN(chan->channel),
+ scale_table[i].reg_val);
+ }
+
+ return -EINVAL;
+}
+
+static int ad4851_get_scale(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *val,
+ int *val2)
+{
+ struct ad4851_state *st = iio_priv(indio_dev);
+ const struct ad4851_scale *scale_table;
+ size_t table_size;
+ u32 softspan_val;
+ int i, ret;
+
+ if (st->bipolar_ch[chan->channel]) {
+ scale_table = ad4851_scale_table_bipolar;
+ table_size = ARRAY_SIZE(ad4851_scale_table_bipolar);
+ } else {
+ scale_table = ad4851_scale_table_unipolar;
+ table_size = ARRAY_SIZE(ad4851_scale_table_unipolar);
+ }
+
+ ret = regmap_read(st->regmap, AD4851_REG_CHX_SOFTSPAN(chan->channel),
+ &softspan_val);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < table_size; i++) {
+ if (softspan_val == scale_table[i].reg_val)
+ break;
+ }
+
+ if (i == table_size)
+ return -EIO;
+
+ ret = __ad4851_get_scale(indio_dev, scale_table[i].scale_val, val,
+ val2);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int ad4851_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long info)
+{
+ struct ad4851_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->cnv_trigger_rate_hz;
+ *val2 = st->osr;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ return ad4851_get_calibscale(st, chan->channel, val, val2);
+ case IIO_CHAN_INFO_SCALE:
+ return ad4851_get_scale(indio_dev, chan, val, val2);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return ad4851_get_calibbias(st, chan->channel, val);
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ return ad4851_get_oversampling_ratio(st, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4851_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long info)
+{
+ struct ad4851_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (val < 0 || val2 < 0)
+ return -EINVAL;
+ return ad4851_set_sampling_freq(st, val * st->osr + val2 * st->osr / MICRO);
+ case IIO_CHAN_INFO_SCALE:
+ return ad4851_set_scale(indio_dev, chan, val, val2);
+ case IIO_CHAN_INFO_CALIBSCALE:
+ return ad4851_set_calibscale(st, chan->channel, val, val2);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return ad4851_set_calibbias(st, chan->channel, val);
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ return ad4851_set_oversampling_ratio(indio_dev, chan, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4851_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct ad4851_state *st = iio_priv(indio_dev);
+ unsigned int c;
+ int ret;
+
+ for (c = 0; c < indio_dev->num_channels; c++) {
+ if (test_bit(c, scan_mask))
+ ret = iio_backend_chan_enable(st->back, c);
+ else
+ ret = iio_backend_chan_disable(st->back, c);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ad4851_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct ad4851_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (st->bipolar_ch[chan->channel]) {
+ *vals = (const int *)st->scales_bipolar;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ /* Values are stored in a 2D matrix */
+ *length = ARRAY_SIZE(ad4851_scale_avail_bipolar) * 2;
+ } else {
+ *vals = (const int *)st->scales_unipolar;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ /* Values are stored in a 2D matrix */
+ *length = ARRAY_SIZE(ad4851_scale_avail_unipolar) * 2;
+ }
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *vals = ad4851_oversampling_ratios;
+ *length = ARRAY_SIZE(ad4851_oversampling_ratios);
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_scan_type ad4851_scan_type_20_u[] = {
+ [AD4851_SCAN_TYPE_NORMAL] = {
+ .sign = 'u',
+ .realbits = 20,
+ .storagebits = 32,
+ },
+ [AD4851_SCAN_TYPE_RESOLUTION_BOOST] = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ },
+};
+
+static const struct iio_scan_type ad4851_scan_type_20_b[] = {
+ [AD4851_SCAN_TYPE_NORMAL] = {
+ .sign = 's',
+ .realbits = 20,
+ .storagebits = 32,
+ },
+ [AD4851_SCAN_TYPE_RESOLUTION_BOOST] = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ },
+};
+
+static int ad4851_get_current_scan_type(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad4851_state *st = iio_priv(indio_dev);
+
+ return st->resolution_boost_enabled ? AD4851_SCAN_TYPE_RESOLUTION_BOOST
+ : AD4851_SCAN_TYPE_NORMAL;
+}
+
+#define AD4851_IIO_CHANNEL \
+ .type = IIO_VOLTAGE, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBSCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .indexed = 1
+
+/*
+ * In case of AD4858_IIO_CHANNEL the scan_type is handled dynamically during the
+ * parse_channels function.
+ */
+#define AD4858_IIO_CHANNEL \
+{ \
+ AD4851_IIO_CHANNEL \
+}
+
+#define AD4857_IIO_CHANNEL \
+{ \
+ AD4851_IIO_CHANNEL, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ }, \
+}
+
+static int ad4851_parse_channels_common(struct iio_dev *indio_dev,
+ struct iio_chan_spec **chans,
+ const struct iio_chan_spec ad4851_chan)
+{
+ struct ad4851_state *st = iio_priv(indio_dev);
+ struct device *dev = &st->spi->dev;
+ struct iio_chan_spec *channels, *chan_start;
+ unsigned int num_channels, reg;
+ unsigned int index = 0;
+ int ret;
+
+ num_channels = device_get_child_node_count(dev);
+ if (num_channels > AD4851_MAX_CH_NR)
+ return dev_err_probe(dev, -EINVAL, "Too many channels: %u\n",
+ num_channels);
+
+ channels = devm_kcalloc(dev, num_channels, sizeof(*channels), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ chan_start = channels;
+
+ device_for_each_child_node_scoped(dev, child) {
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Missing channel number\n");
+ if (reg >= AD4851_MAX_CH_NR)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid channel number\n");
+ *channels = ad4851_chan;
+ channels->scan_index = index++;
+ channels->channel = reg;
+
+ if (fwnode_property_present(child, "diff-channels")) {
+ channels->channel2 = reg + st->info->max_channels;
+ channels->differential = 1;
+ }
+
+ st->bipolar_ch[reg] = fwnode_property_read_bool(child, "bipolar");
+
+ if (st->bipolar_ch[reg]) {
+ channels->scan_type.sign = 's';
+ } else {
+ ret = regmap_write(st->regmap, AD4851_REG_CHX_SOFTSPAN(reg),
+ AD4851_SOFTSPAN_0V_40V);
+ if (ret)
+ return ret;
+ }
+
+ channels++;
+ }
+
+ *chans = chan_start;
+
+ return num_channels;
+}
+
+static int ad4857_parse_channels(struct iio_dev *indio_dev)
+{
+ struct iio_chan_spec *ad4851_channels;
+ const struct iio_chan_spec ad4851_chan = AD4857_IIO_CHANNEL;
+ int ret;
+
+ ret = ad4851_parse_channels_common(indio_dev, &ad4851_channels,
+ ad4851_chan);
+ if (ret < 0)
+ return ret;
+
+ indio_dev->channels = ad4851_channels;
+ indio_dev->num_channels = ret;
+
+ return 0;
+}
+
+static int ad4858_parse_channels(struct iio_dev *indio_dev)
+{
+ struct ad4851_state *st = iio_priv(indio_dev);
+ struct device *dev = &st->spi->dev;
+ struct iio_chan_spec *ad4851_channels;
+ const struct iio_chan_spec ad4851_chan = AD4858_IIO_CHANNEL;
+ int ret;
+
+ ret = ad4851_parse_channels_common(indio_dev, &ad4851_channels,
+ ad4851_chan);
+ if (ret < 0)
+ return ret;
+
+ device_for_each_child_node_scoped(dev, child) {
+ ad4851_channels->has_ext_scan_type = 1;
+ if (fwnode_property_read_bool(child, "bipolar")) {
+ ad4851_channels->ext_scan_type = ad4851_scan_type_20_b;
+ ad4851_channels->num_ext_scan_type = ARRAY_SIZE(ad4851_scan_type_20_b);
+ } else {
+ ad4851_channels->ext_scan_type = ad4851_scan_type_20_u;
+ ad4851_channels->num_ext_scan_type = ARRAY_SIZE(ad4851_scan_type_20_u);
+ }
+ ad4851_channels++;
+ }
+
+ indio_dev->channels = ad4851_channels;
+ indio_dev->num_channels = ret;
+
+ return 0;
+}
+
+/*
+ * parse_channels() function handles the rest of the channel related attributes
+ * that are usually are stored in the chip info structure.
+ */
+static const struct ad4851_chip_info ad4851_info = {
+ .name = "ad4851",
+ .product_id = 0x67,
+ .max_sample_rate_hz = 250 * KILO,
+ .resolution = 16,
+ .max_channels = AD4851_MAX_CH_NR,
+ .parse_channels = ad4857_parse_channels,
+};
+
+static const struct ad4851_chip_info ad4852_info = {
+ .name = "ad4852",
+ .product_id = 0x66,
+ .max_sample_rate_hz = 250 * KILO,
+ .resolution = 20,
+ .max_channels = AD4851_MAX_CH_NR,
+ .parse_channels = ad4858_parse_channels,
+};
+
+static const struct ad4851_chip_info ad4853_info = {
+ .name = "ad4853",
+ .product_id = 0x65,
+ .max_sample_rate_hz = 1 * MEGA,
+ .resolution = 16,
+ .max_channels = AD4851_MAX_CH_NR,
+ .parse_channels = ad4857_parse_channels,
+};
+
+static const struct ad4851_chip_info ad4854_info = {
+ .name = "ad4854",
+ .product_id = 0x64,
+ .max_sample_rate_hz = 1 * MEGA,
+ .resolution = 20,
+ .max_channels = AD4851_MAX_CH_NR,
+ .parse_channels = ad4858_parse_channels,
+};
+
+static const struct ad4851_chip_info ad4855_info = {
+ .name = "ad4855",
+ .product_id = 0x63,
+ .max_sample_rate_hz = 250 * KILO,
+ .resolution = 16,
+ .max_channels = AD4851_MAX_CH_NR,
+ .parse_channels = ad4857_parse_channels,
+};
+
+static const struct ad4851_chip_info ad4856_info = {
+ .name = "ad4856",
+ .product_id = 0x62,
+ .max_sample_rate_hz = 250 * KILO,
+ .resolution = 20,
+ .max_channels = AD4851_MAX_CH_NR,
+ .parse_channels = ad4858_parse_channels,
+};
+
+static const struct ad4851_chip_info ad4857_info = {
+ .name = "ad4857",
+ .product_id = 0x61,
+ .max_sample_rate_hz = 1 * MEGA,
+ .resolution = 16,
+ .max_channels = AD4851_MAX_CH_NR,
+ .parse_channels = ad4857_parse_channels,
+};
+
+static const struct ad4851_chip_info ad4858_info = {
+ .name = "ad4858",
+ .product_id = 0x60,
+ .max_sample_rate_hz = 1 * MEGA,
+ .resolution = 20,
+ .max_channels = AD4851_MAX_CH_NR,
+ .parse_channels = ad4858_parse_channels,
+};
+
+static const struct ad4851_chip_info ad4858i_info = {
+ .name = "ad4858i",
+ .product_id = 0x6F,
+ .max_sample_rate_hz = 1 * MEGA,
+ .resolution = 20,
+ .max_channels = AD4851_MAX_CH_NR,
+ .parse_channels = ad4858_parse_channels,
+};
+
+static const struct iio_info ad4851_iio_info = {
+ .debugfs_reg_access = ad4851_reg_access,
+ .read_raw = ad4851_read_raw,
+ .write_raw = ad4851_write_raw,
+ .update_scan_mode = ad4851_update_scan_mode,
+ .get_current_scan_type = ad4851_get_current_scan_type,
+ .read_avail = ad4851_read_avail,
+};
+
+static const struct regmap_config regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .read_flag_mask = BIT(7),
+};
+
+static const char * const ad4851_power_supplies[] = {
+ "vcc", "vdd", "vee", "vio",
+};
+
+static int ad4851_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct device *dev = &spi->dev;
+ struct ad4851_state *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+
+ ret = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
+ ret = devm_regulator_bulk_get_enable(dev,
+ ARRAY_SIZE(ad4851_power_supplies),
+ ad4851_power_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get and enable supplies\n");
+
+ ret = devm_regulator_get_enable_optional(dev, "vddh");
+ if (ret < 0 && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "failed to enable vddh voltage\n");
+
+ ret = devm_regulator_get_enable_optional(dev, "vddl");
+ if (ret < 0 && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "failed to enable vddl voltage\n");
+
+ ret = devm_regulator_get_enable_optional(dev, "vrefbuf");
+ if (ret < 0 && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "failed to enable vrefbuf voltage\n");
+
+ st->vrefbuf_en = ret != -ENODEV;
+
+ ret = devm_regulator_get_enable_optional(dev, "vrefio");
+ if (ret < 0 && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "failed to enable vrefio voltage\n");
+
+ st->vrefio_en = ret != -ENODEV;
+
+ st->pd_gpio = devm_gpiod_get_optional(dev, "pd", GPIOD_OUT_LOW);
+ if (IS_ERR(st->pd_gpio))
+ return dev_err_probe(dev, PTR_ERR(st->pd_gpio),
+ "Error on requesting pd GPIO\n");
+
+ st->cnv = devm_pwm_get(dev, NULL);
+ if (IS_ERR(st->cnv))
+ return dev_err_probe(dev, PTR_ERR(st->cnv),
+ "Error on requesting pwm\n");
+
+ st->info = spi_get_device_match_data(spi);
+ if (!st->info)
+ return -ENODEV;
+
+ st->regmap = devm_regmap_init_spi(spi, &regmap_config);
+ if (IS_ERR(st->regmap))
+ return PTR_ERR(st->regmap);
+
+ ret = ad4851_set_sampling_freq(st, HZ_PER_MHZ);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&st->spi->dev, ad4851_pwm_disable,
+ st->cnv);
+ if (ret)
+ return ret;
+
+ ret = ad4851_setup(st);
+ if (ret)
+ return ret;
+
+ indio_dev->name = st->info->name;
+ indio_dev->info = &ad4851_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = st->info->parse_channels(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ad4851_scale_fill(indio_dev);
+ if (ret)
+ return ret;
+
+ st->back = devm_iio_backend_get(dev, NULL);
+ if (IS_ERR(st->back))
+ return PTR_ERR(st->back);
+
+ ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_backend_enable(dev, st->back);
+ if (ret)
+ return ret;
+
+ ret = ad4851_calibrate(indio_dev);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id ad4851_of_match[] = {
+ { .compatible = "adi,ad4851", .data = &ad4851_info, },
+ { .compatible = "adi,ad4852", .data = &ad4852_info, },
+ { .compatible = "adi,ad4853", .data = &ad4853_info, },
+ { .compatible = "adi,ad4854", .data = &ad4854_info, },
+ { .compatible = "adi,ad4855", .data = &ad4855_info, },
+ { .compatible = "adi,ad4856", .data = &ad4856_info, },
+ { .compatible = "adi,ad4857", .data = &ad4857_info, },
+ { .compatible = "adi,ad4858", .data = &ad4858_info, },
+ { .compatible = "adi,ad4858i", .data = &ad4858i_info, },
+ { }
+};
+
+static const struct spi_device_id ad4851_spi_id[] = {
+ { "ad4851", (kernel_ulong_t)&ad4851_info },
+ { "ad4852", (kernel_ulong_t)&ad4852_info },
+ { "ad4853", (kernel_ulong_t)&ad4853_info },
+ { "ad4854", (kernel_ulong_t)&ad4854_info },
+ { "ad4855", (kernel_ulong_t)&ad4855_info },
+ { "ad4856", (kernel_ulong_t)&ad4856_info },
+ { "ad4857", (kernel_ulong_t)&ad4857_info },
+ { "ad4858", (kernel_ulong_t)&ad4858_info },
+ { "ad4858i", (kernel_ulong_t)&ad4858i_info },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad4851_spi_id);
+
+static struct spi_driver ad4851_driver = {
+ .probe = ad4851_probe,
+ .driver = {
+ .name = "ad4851",
+ .of_match_table = ad4851_of_match,
+ },
+ .id_table = ad4851_spi_id,
+};
+module_spi_driver(ad4851_driver);
+
+MODULE_AUTHOR("Sergiu Cuciurean <sergiu.cuciurean@analog.com>");
+MODULE_AUTHOR("Dragos Bogdan <dragos.bogdan@analog.com>");
+MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD4851 DAS driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_BACKEND");
diff --git a/drivers/iio/adc/ad7091r-base.c b/drivers/iio/adc/ad7091r-base.c
index 606486c4dfe8..931ff71b2888 100644
--- a/drivers/iio/adc/ad7091r-base.c
+++ b/drivers/iio/adc/ad7091r-base.c
@@ -7,6 +7,7 @@
#include <linux/bitops.h>
#include <linux/bitfield.h>
+#include <linux/cleanup.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/interrupt.h>
diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c
index 6ae27cdd3250..3ea81a98e455 100644
--- a/drivers/iio/adc/ad7124.c
+++ b/drivers/iio/adc/ad7124.c
@@ -53,6 +53,11 @@
#define AD7124_ADC_CTRL_MODE_MSK GENMASK(5, 2)
#define AD7124_ADC_CTRL_MODE(x) FIELD_PREP(AD7124_ADC_CTRL_MODE_MSK, x)
+#define AD7124_MODE_CAL_INT_ZERO 0x5 /* Internal Zero-Scale Calibration */
+#define AD7124_MODE_CAL_INT_FULL 0x6 /* Internal Full-Scale Calibration */
+#define AD7124_MODE_CAL_SYS_ZERO 0x7 /* System Zero-Scale Calibration */
+#define AD7124_MODE_CAL_SYS_FULL 0x8 /* System Full-Scale Calibration */
+
/* AD7124 ID */
#define AD7124_DEVICE_ID_MSK GENMASK(7, 4)
#define AD7124_DEVICE_ID_GET(x) FIELD_GET(AD7124_DEVICE_ID_MSK, x)
@@ -151,7 +156,11 @@ struct ad7124_chip_info {
struct ad7124_channel_config {
bool live;
unsigned int cfg_slot;
- /* Following fields are used to compare equality. */
+ /*
+ * Following fields are used to compare for equality. If you
+ * make adaptations in it, you most likely also have to adapt
+ * ad7124_find_similar_live_cfg(), too.
+ */
struct_group(config_props,
enum ad7124_ref_sel refsel;
bool bipolar;
@@ -162,6 +171,8 @@ struct ad7124_channel_config {
unsigned int odr;
unsigned int odr_sel_bits;
unsigned int filter_type;
+ unsigned int calibration_offset;
+ unsigned int calibration_gain;
);
};
@@ -170,6 +181,7 @@ struct ad7124_channel {
struct ad7124_channel_config cfg;
unsigned int ain;
unsigned int slot;
+ u8 syscalib_mode;
};
struct ad7124_state {
@@ -182,24 +194,13 @@ struct ad7124_state {
unsigned int num_channels;
struct mutex cfgs_lock; /* lock for configs access */
unsigned long cfg_slots_status; /* bitmap with slot status (1 means it is used) */
- DECLARE_KFIFO(live_cfgs_fifo, struct ad7124_channel_config *, AD7124_MAX_CONFIGS);
-};
-static const struct iio_chan_spec ad7124_channel_template = {
- .type = IIO_VOLTAGE,
- .indexed = 1,
- .differential = 1,
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
- BIT(IIO_CHAN_INFO_SCALE) |
- BIT(IIO_CHAN_INFO_OFFSET) |
- BIT(IIO_CHAN_INFO_SAMP_FREQ) |
- BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
- .scan_type = {
- .sign = 'u',
- .realbits = 24,
- .storagebits = 32,
- .endianness = IIO_BE,
- },
+ /*
+ * Stores the power-on reset value for the GAIN(x) registers which are
+ * needed for measurements at gain 1 (i.e. CONFIG(x).PGA == 0)
+ */
+ unsigned int gain_default;
+ DECLARE_KFIFO(live_cfgs_fifo, struct ad7124_channel_config *, AD7124_MAX_CONFIGS);
};
static struct ad7124_chip_info ad7124_chip_info_tbl[] = {
@@ -338,15 +339,42 @@ static struct ad7124_channel_config *ad7124_find_similar_live_cfg(struct ad7124_
struct ad7124_channel_config *cfg)
{
struct ad7124_channel_config *cfg_aux;
- ptrdiff_t cmp_size;
int i;
- cmp_size = sizeof_field(struct ad7124_channel_config, config_props);
+ /*
+ * This is just to make sure that the comparison is adapted after
+ * struct ad7124_channel_config was changed.
+ */
+ static_assert(sizeof_field(struct ad7124_channel_config, config_props) ==
+ sizeof(struct {
+ enum ad7124_ref_sel refsel;
+ bool bipolar;
+ bool buf_positive;
+ bool buf_negative;
+ unsigned int vref_mv;
+ unsigned int pga_bits;
+ unsigned int odr;
+ unsigned int odr_sel_bits;
+ unsigned int filter_type;
+ unsigned int calibration_offset;
+ unsigned int calibration_gain;
+ }));
+
for (i = 0; i < st->num_channels; i++) {
cfg_aux = &st->channels[i].cfg;
if (cfg_aux->live &&
- !memcmp(&cfg->config_props, &cfg_aux->config_props, cmp_size))
+ cfg->refsel == cfg_aux->refsel &&
+ cfg->bipolar == cfg_aux->bipolar &&
+ cfg->buf_positive == cfg_aux->buf_positive &&
+ cfg->buf_negative == cfg_aux->buf_negative &&
+ cfg->vref_mv == cfg_aux->vref_mv &&
+ cfg->pga_bits == cfg_aux->pga_bits &&
+ cfg->odr == cfg_aux->odr &&
+ cfg->odr_sel_bits == cfg_aux->odr_sel_bits &&
+ cfg->filter_type == cfg_aux->filter_type &&
+ cfg->calibration_offset == cfg_aux->calibration_offset &&
+ cfg->calibration_gain == cfg_aux->calibration_gain)
return cfg_aux;
}
@@ -402,6 +430,14 @@ static int ad7124_write_config(struct ad7124_state *st, struct ad7124_channel_co
cfg->cfg_slot = cfg_slot;
+ ret = ad_sd_write_reg(&st->sd, AD7124_OFFSET(cfg->cfg_slot), 3, cfg->calibration_offset);
+ if (ret)
+ return ret;
+
+ ret = ad_sd_write_reg(&st->sd, AD7124_GAIN(cfg->cfg_slot), 3, cfg->calibration_gain);
+ if (ret)
+ return ret;
+
tmp = (cfg->buf_positive << 1) + cfg->buf_negative;
val = AD7124_CONFIG_BIPOLAR(cfg->bipolar) | AD7124_CONFIG_REF_SEL(cfg->refsel) |
AD7124_CONFIG_IN_BUFF(tmp) | AD7124_CONFIG_PGA(cfg->pga_bits);
@@ -540,14 +576,21 @@ static int ad7124_append_status(struct ad_sigma_delta *sd, bool append)
return 0;
}
-static int ad7124_disable_all(struct ad_sigma_delta *sd)
+static int ad7124_disable_one(struct ad_sigma_delta *sd, unsigned int chan)
{
struct ad7124_state *st = container_of(sd, struct ad7124_state, sd);
+
+ /* The relevant thing here is that AD7124_CHANNEL_EN_MSK is cleared. */
+ return ad_sd_write_reg(&st->sd, AD7124_CHANNEL(chan), 2, 0);
+}
+
+static int ad7124_disable_all(struct ad_sigma_delta *sd)
+{
int ret;
int i;
- for (i = 0; i < st->num_channels; i++) {
- ret = ad7124_spi_write_mask(st, AD7124_CHANNEL(i), AD7124_CHANNEL_EN_MSK, 0, 2);
+ for (i = 0; i < 16; i++) {
+ ret = ad7124_disable_one(sd, i);
if (ret < 0)
return ret;
}
@@ -555,13 +598,6 @@ static int ad7124_disable_all(struct ad_sigma_delta *sd)
return 0;
}
-static int ad7124_disable_one(struct ad_sigma_delta *sd, unsigned int chan)
-{
- struct ad7124_state *st = container_of(sd, struct ad7124_state, sd);
-
- return ad7124_spi_write_mask(st, AD7124_CHANNEL(chan), AD7124_CHANNEL_EN_MSK, 0, 2);
-}
-
static const struct ad_sigma_delta_info ad7124_sigma_delta_info = {
.set_channel = ad7124_set_channel,
.append_status = ad7124_append_status,
@@ -808,13 +844,22 @@ static int ad7124_soft_reset(struct ad7124_state *st)
return dev_err_probe(dev, ret, "Error reading status register\n");
if (!(readval & AD7124_STATUS_POR_FLAG_MSK))
- return 0;
+ break;
/* The AD7124 requires typically 2ms to power up and settle */
usleep_range(100, 2000);
} while (--timeout);
- return dev_err_probe(dev, -EIO, "Soft reset failed\n");
+ if (readval & AD7124_STATUS_POR_FLAG_MSK)
+ return dev_err_probe(dev, -EIO, "Soft reset failed\n");
+
+ ret = ad_sd_read_reg(&st->sd, AD7124_GAIN(0), 3, &st->gain_default);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Error reading gain register\n");
+
+ dev_dbg(dev, "Reset value of GAIN register is 0x%x\n", st->gain_default);
+
+ return 0;
}
static int ad7124_check_chip_id(struct ad7124_state *st)
@@ -842,6 +887,140 @@ static int ad7124_check_chip_id(struct ad7124_state *st)
return 0;
}
+enum {
+ AD7124_SYSCALIB_ZERO_SCALE,
+ AD7124_SYSCALIB_FULL_SCALE,
+};
+
+static int ad7124_syscalib_locked(struct ad7124_state *st, const struct iio_chan_spec *chan)
+{
+ struct device *dev = &st->sd.spi->dev;
+ struct ad7124_channel *ch = &st->channels[chan->channel];
+ int ret;
+
+ if (ch->syscalib_mode == AD7124_SYSCALIB_ZERO_SCALE) {
+ ch->cfg.calibration_offset = 0x800000;
+
+ ret = ad_sd_calibrate(&st->sd, AD7124_MODE_CAL_SYS_ZERO,
+ chan->address);
+ if (ret < 0)
+ return ret;
+
+ ret = ad_sd_read_reg(&st->sd, AD7124_OFFSET(ch->cfg.cfg_slot), 3,
+ &ch->cfg.calibration_offset);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "offset for channel %d after zero-scale calibration: 0x%x\n",
+ chan->channel, ch->cfg.calibration_offset);
+ } else {
+ ch->cfg.calibration_gain = st->gain_default;
+
+ ret = ad_sd_calibrate(&st->sd, AD7124_MODE_CAL_SYS_FULL,
+ chan->address);
+ if (ret < 0)
+ return ret;
+
+ ret = ad_sd_read_reg(&st->sd, AD7124_GAIN(ch->cfg.cfg_slot), 3,
+ &ch->cfg.calibration_gain);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "gain for channel %d after full-scale calibration: 0x%x\n",
+ chan->channel, ch->cfg.calibration_gain);
+ }
+
+ return 0;
+}
+
+static ssize_t ad7124_write_syscalib(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct ad7124_state *st = iio_priv(indio_dev);
+ bool sys_calib;
+ int ret;
+
+ ret = kstrtobool(buf, &sys_calib);
+ if (ret)
+ return ret;
+
+ if (!sys_calib)
+ return len;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad7124_syscalib_locked(st, chan);
+
+ iio_device_release_direct(indio_dev);
+
+ return ret ?: len;
+}
+
+static const char * const ad7124_syscalib_modes[] = {
+ [AD7124_SYSCALIB_ZERO_SCALE] = "zero_scale",
+ [AD7124_SYSCALIB_FULL_SCALE] = "full_scale",
+};
+
+static int ad7124_set_syscalib_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int mode)
+{
+ struct ad7124_state *st = iio_priv(indio_dev);
+
+ st->channels[chan->channel].syscalib_mode = mode;
+
+ return 0;
+}
+
+static int ad7124_get_syscalib_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad7124_state *st = iio_priv(indio_dev);
+
+ return st->channels[chan->channel].syscalib_mode;
+}
+
+static const struct iio_enum ad7124_syscalib_mode_enum = {
+ .items = ad7124_syscalib_modes,
+ .num_items = ARRAY_SIZE(ad7124_syscalib_modes),
+ .set = ad7124_set_syscalib_mode,
+ .get = ad7124_get_syscalib_mode
+};
+
+static const struct iio_chan_spec_ext_info ad7124_calibsys_ext_info[] = {
+ {
+ .name = "sys_calibration",
+ .write = ad7124_write_syscalib,
+ .shared = IIO_SEPARATE,
+ },
+ IIO_ENUM("sys_calibration_mode", IIO_SEPARATE,
+ &ad7124_syscalib_mode_enum),
+ IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE,
+ &ad7124_syscalib_mode_enum),
+ { }
+};
+
+static const struct iio_chan_spec ad7124_channel_template = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .differential = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_BE,
+ },
+ .ext_info = ad7124_calibsys_ext_info,
+};
+
/*
* Input specifiers 8 - 15 are explicitly reserved for ad7124-4
* while they are fine for ad7124-8. Values above 31 don't fit
@@ -881,12 +1060,12 @@ static int ad7124_parse_channel_config(struct iio_dev *indio_dev,
/* Add one for temperature */
st->num_channels = min(num_channels + 1, AD7124_MAX_CHANNELS);
- chan = devm_kcalloc(indio_dev->dev.parent, st->num_channels,
+ chan = devm_kcalloc(dev, st->num_channels,
sizeof(*chan), GFP_KERNEL);
if (!chan)
return -ENOMEM;
- channels = devm_kcalloc(indio_dev->dev.parent, st->num_channels, sizeof(*channels),
+ channels = devm_kcalloc(dev, st->num_channels, sizeof(*channels),
GFP_KERNEL);
if (!channels)
return -ENOMEM;
@@ -1016,11 +1195,10 @@ static int ad7124_setup(struct ad7124_state *st)
* set all channels to this default value.
*/
ad7124_set_channel_odr(st, i, 10);
-
- /* Disable all channels to prevent unintended conversions. */
- ad_sd_write_reg(&st->sd, AD7124_CHANNEL(i), 2, 0);
}
+ ad7124_disable_all(&st->sd);
+
ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to setup CONTROL register\n");
@@ -1028,6 +1206,91 @@ static int ad7124_setup(struct ad7124_state *st)
return ret;
}
+static int __ad7124_calibrate_all(struct ad7124_state *st, struct iio_dev *indio_dev)
+{
+ struct device *dev = &st->sd.spi->dev;
+ int ret, i;
+
+ for (i = 0; i < st->num_channels; i++) {
+
+ if (indio_dev->channels[i].type != IIO_VOLTAGE)
+ continue;
+
+ /*
+ * For calibration the OFFSET register should hold its reset default
+ * value. For the GAIN register there is no such requirement but
+ * for gain 1 it should hold the reset default value, too. So to
+ * simplify matters use the reset default value for both.
+ */
+ st->channels[i].cfg.calibration_offset = 0x800000;
+ st->channels[i].cfg.calibration_gain = st->gain_default;
+
+ /*
+ * Full-scale calibration isn't supported at gain 1, so skip in
+ * that case. Note that untypically full-scale calibration has
+ * to happen before zero-scale calibration. This only applies to
+ * the internal calibration. For system calibration it's as
+ * usual: first zero-scale then full-scale calibration.
+ */
+ if (st->channels[i].cfg.pga_bits > 0) {
+ ret = ad_sd_calibrate(&st->sd, AD7124_MODE_CAL_INT_FULL, i);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * read out the resulting value of GAIN
+ * after full-scale calibration because the next
+ * ad_sd_calibrate() call overwrites this via
+ * ad_sigma_delta_set_channel() -> ad7124_set_channel()
+ * ... -> ad7124_enable_channel().
+ */
+ ret = ad_sd_read_reg(&st->sd, AD7124_GAIN(st->channels[i].cfg.cfg_slot), 3,
+ &st->channels[i].cfg.calibration_gain);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = ad_sd_calibrate(&st->sd, AD7124_MODE_CAL_INT_ZERO, i);
+ if (ret < 0)
+ return ret;
+
+ ret = ad_sd_read_reg(&st->sd, AD7124_OFFSET(st->channels[i].cfg.cfg_slot), 3,
+ &st->channels[i].cfg.calibration_offset);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "offset and gain for channel %d = 0x%x + 0x%x\n", i,
+ st->channels[i].cfg.calibration_offset,
+ st->channels[i].cfg.calibration_gain);
+ }
+
+ return 0;
+}
+
+static int ad7124_calibrate_all(struct ad7124_state *st, struct iio_dev *indio_dev)
+{
+ int ret;
+ unsigned int adc_control = st->adc_control;
+
+ /*
+ * Calibration isn't supported at full power, so speed down a bit.
+ * Setting .adc_control is enough here because the control register is
+ * written as part of ad_sd_calibrate() -> ad_sigma_delta_set_mode().
+ * The resulting calibration is then also valid for high-speed, so just
+ * restore adc_control afterwards.
+ */
+ if (FIELD_GET(AD7124_ADC_CTRL_PWR_MSK, adc_control) >= AD7124_FULL_POWER) {
+ st->adc_control &= ~AD7124_ADC_CTRL_PWR_MSK;
+ st->adc_control |= AD7124_ADC_CTRL_PWR(AD7124_MID_POWER);
+ }
+
+ ret = __ad7124_calibrate_all(st, indio_dev);
+
+ st->adc_control = adc_control;
+
+ return ret;
+}
+
static void ad7124_reg_disable(void *r)
{
regulator_disable(r);
@@ -1106,6 +1369,10 @@ static int ad7124_probe(struct spi_device *spi)
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to setup triggers\n");
+ ret = ad7124_calibrate_all(st, indio_dev);
+ if (ret)
+ return ret;
+
ret = devm_iio_device_register(&spi->dev, indio_dev);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to register iio device\n");
diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c
index 6c4ed10ae580..69de5886474c 100644
--- a/drivers/iio/adc/ad7173.c
+++ b/drivers/iio/adc/ad7173.c
@@ -35,6 +35,7 @@
#include <linux/units.h>
#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
@@ -102,6 +103,7 @@
#define AD7173_GPIO_PDSW BIT(14)
#define AD7173_GPIO_OP_EN2_3 BIT(13)
+#define AD4111_GPIO_GP_OW_EN BIT(12)
#define AD7173_GPIO_MUX_IO BIT(12)
#define AD7173_GPIO_SYNC_EN BIT(11)
#define AD7173_GPIO_ERR_EN BIT(10)
@@ -149,6 +151,7 @@
#define AD7173_FILTER_ODR0_MASK GENMASK(5, 0)
#define AD7173_MAX_CONFIGS 8
+#define AD4111_OW_DET_THRSH_MV 300
#define AD7173_MODE_CAL_INT_ZERO 0x4 /* Internal Zero-Scale Calibration */
#define AD7173_MODE_CAL_INT_FULL 0x5 /* Internal Full-Scale Calibration */
@@ -171,6 +174,7 @@ struct ad7173_device_info {
unsigned int clock;
unsigned int id;
char *name;
+ const struct ad_sigma_delta_info *sd_info;
bool has_current_inputs;
bool has_vincom_input;
bool has_temp;
@@ -181,15 +185,23 @@ struct ad7173_device_info {
bool has_int_ref;
bool has_ref2;
bool has_internal_fs_calibration;
+ bool has_openwire_det;
bool higher_gpio_bits;
u8 num_gpios;
};
struct ad7173_channel_config {
+ /* Openwire detection threshold */
+ unsigned int openwire_thrsh_raw;
+ int openwire_comp_chan;
u8 cfg_slot;
bool live;
- /* Following fields are used to compare equality. */
+ /*
+ * Following fields are used to compare equality. If you
+ * make adaptations in it, you most likely also have to adapt
+ * ad7173_find_live_config(), too.
+ */
struct_group(config_props,
bool bipolar;
bool input_buf;
@@ -202,11 +214,11 @@ struct ad7173_channel {
unsigned int ain;
struct ad7173_channel_config cfg;
u8 syscalib_mode;
+ bool openwire_det_en;
};
struct ad7173_state {
struct ad_sigma_delta sd;
- struct ad_sigma_delta_info sigma_delta_info;
const struct ad7173_device_info *info;
struct ad7173_channel *channels;
struct regulator_bulk_data regulators[3];
@@ -265,228 +277,6 @@ static unsigned int ad4111_current_channel_config[] = {
0x18B, /* 12:IIN3+ 11:IIN3− */
};
-static const struct ad7173_device_info ad4111_device_info = {
- .name = "ad4111",
- .id = AD4111_ID,
- .num_voltage_in_div = 8,
- .num_channels = 16,
- .num_configs = 8,
- .num_voltage_in = 8,
- .num_gpios = 2,
- .higher_gpio_bits = true,
- .has_temp = true,
- .has_vincom_input = true,
- .has_input_buf = true,
- .has_current_inputs = true,
- .has_int_ref = true,
- .has_internal_fs_calibration = true,
- .clock = 2 * HZ_PER_MHZ,
- .sinc5_data_rates = ad7173_sinc5_data_rates,
- .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
-};
-
-static const struct ad7173_device_info ad4112_device_info = {
- .name = "ad4112",
- .id = AD4112_ID,
- .num_voltage_in_div = 8,
- .num_channels = 16,
- .num_configs = 8,
- .num_voltage_in = 8,
- .num_gpios = 2,
- .higher_gpio_bits = true,
- .has_vincom_input = true,
- .has_temp = true,
- .has_input_buf = true,
- .has_current_inputs = true,
- .has_int_ref = true,
- .has_internal_fs_calibration = true,
- .clock = 2 * HZ_PER_MHZ,
- .sinc5_data_rates = ad7173_sinc5_data_rates,
- .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
-};
-
-static const struct ad7173_device_info ad4113_device_info = {
- .name = "ad4113",
- .id = AD4113_ID,
- .num_voltage_in_div = 8,
- .num_channels = 16,
- .num_configs = 8,
- .num_voltage_in = 8,
- .num_gpios = 2,
- .data_reg_only_16bit = true,
- .higher_gpio_bits = true,
- .has_vincom_input = true,
- .has_input_buf = true,
- .has_int_ref = true,
- .clock = 2 * HZ_PER_MHZ,
- .sinc5_data_rates = ad7173_sinc5_data_rates,
- .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
-};
-
-static const struct ad7173_device_info ad4114_device_info = {
- .name = "ad4114",
- .id = AD4114_ID,
- .num_voltage_in_div = 16,
- .num_channels = 16,
- .num_configs = 8,
- .num_voltage_in = 16,
- .num_gpios = 4,
- .has_vincom_input = true,
- .has_temp = true,
- .has_input_buf = true,
- .has_int_ref = true,
- .has_internal_fs_calibration = true,
- .clock = 2 * HZ_PER_MHZ,
- .sinc5_data_rates = ad7173_sinc5_data_rates,
- .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
-};
-
-static const struct ad7173_device_info ad4115_device_info = {
- .name = "ad4115",
- .id = AD4115_ID,
- .num_voltage_in_div = 16,
- .num_channels = 16,
- .num_configs = 8,
- .num_voltage_in = 16,
- .num_gpios = 4,
- .has_vincom_input = true,
- .has_temp = true,
- .has_input_buf = true,
- .has_int_ref = true,
- .has_internal_fs_calibration = true,
- .clock = 8 * HZ_PER_MHZ,
- .sinc5_data_rates = ad4115_sinc5_data_rates,
- .num_sinc5_data_rates = ARRAY_SIZE(ad4115_sinc5_data_rates),
-};
-
-static const struct ad7173_device_info ad4116_device_info = {
- .name = "ad4116",
- .id = AD4116_ID,
- .num_voltage_in_div = 11,
- .num_channels = 16,
- .num_configs = 8,
- .num_voltage_in = 16,
- .num_gpios = 4,
- .has_vincom_input = true,
- .has_temp = true,
- .has_input_buf = true,
- .has_int_ref = true,
- .has_internal_fs_calibration = true,
- .clock = 4 * HZ_PER_MHZ,
- .sinc5_data_rates = ad4116_sinc5_data_rates,
- .num_sinc5_data_rates = ARRAY_SIZE(ad4116_sinc5_data_rates),
-};
-
-static const struct ad7173_device_info ad7172_2_device_info = {
- .name = "ad7172-2",
- .id = AD7172_2_ID,
- .num_voltage_in = 5,
- .num_channels = 4,
- .num_configs = 4,
- .num_gpios = 2,
- .has_temp = true,
- .has_input_buf = true,
- .has_int_ref = true,
- .has_pow_supply_monitoring = true,
- .clock = 2 * HZ_PER_MHZ,
- .sinc5_data_rates = ad7173_sinc5_data_rates,
- .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
-};
-
-static const struct ad7173_device_info ad7172_4_device_info = {
- .name = "ad7172-4",
- .id = AD7172_4_ID,
- .num_voltage_in = 9,
- .num_channels = 8,
- .num_configs = 8,
- .num_gpios = 4,
- .has_input_buf = true,
- .has_ref2 = true,
- .has_pow_supply_monitoring = true,
- .clock = 2 * HZ_PER_MHZ,
- .sinc5_data_rates = ad7173_sinc5_data_rates,
- .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
-};
-
-static const struct ad7173_device_info ad7173_8_device_info = {
- .name = "ad7173-8",
- .id = AD7173_ID,
- .num_voltage_in = 17,
- .num_channels = 16,
- .num_configs = 8,
- .num_gpios = 4,
- .has_temp = true,
- .has_input_buf = true,
- .has_int_ref = true,
- .has_ref2 = true,
- .clock = 2 * HZ_PER_MHZ,
- .sinc5_data_rates = ad7173_sinc5_data_rates,
- .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
-};
-
-static const struct ad7173_device_info ad7175_2_device_info = {
- .name = "ad7175-2",
- .id = AD7175_2_ID,
- .num_voltage_in = 5,
- .num_channels = 4,
- .num_configs = 4,
- .num_gpios = 2,
- .has_temp = true,
- .has_input_buf = true,
- .has_int_ref = true,
- .has_pow_supply_monitoring = true,
- .clock = 16 * HZ_PER_MHZ,
- .sinc5_data_rates = ad7175_sinc5_data_rates,
- .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
-};
-
-static const struct ad7173_device_info ad7175_8_device_info = {
- .name = "ad7175-8",
- .id = AD7175_8_ID,
- .num_voltage_in = 17,
- .num_channels = 16,
- .num_configs = 8,
- .num_gpios = 4,
- .has_temp = true,
- .has_input_buf = true,
- .has_int_ref = true,
- .has_ref2 = true,
- .has_pow_supply_monitoring = true,
- .clock = 16 * HZ_PER_MHZ,
- .sinc5_data_rates = ad7175_sinc5_data_rates,
- .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
-};
-
-static const struct ad7173_device_info ad7176_2_device_info = {
- .name = "ad7176-2",
- .id = AD7176_ID,
- .num_voltage_in = 5,
- .num_channels = 4,
- .num_configs = 4,
- .num_gpios = 2,
- .has_int_ref = true,
- .clock = 16 * HZ_PER_MHZ,
- .sinc5_data_rates = ad7175_sinc5_data_rates,
- .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
-};
-
-static const struct ad7173_device_info ad7177_2_device_info = {
- .name = "ad7177-2",
- .id = AD7177_ID,
- .num_voltage_in = 5,
- .num_channels = 4,
- .num_configs = 4,
- .num_gpios = 2,
- .has_temp = true,
- .has_input_buf = true,
- .has_int_ref = true,
- .has_pow_supply_monitoring = true,
- .clock = 16 * HZ_PER_MHZ,
- .odr_start_value = AD7177_ODR_START_VALUE,
- .sinc5_data_rates = ad7175_sinc5_data_rates,
- .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
-};
-
static const char *const ad7173_ref_sel_str[] = {
[AD7173_SETUP_REF_SEL_EXT_REF] = "vref",
[AD7173_SETUP_REF_SEL_EXT_REF2] = "vref2",
@@ -559,6 +349,9 @@ static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev,
if (ret)
return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
mode = st->channels[chan->channel].syscalib_mode;
if (sys_calib) {
if (mode == AD7173_SYSCALIB_ZERO_SCALE)
@@ -569,6 +362,8 @@ static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev,
chan->address);
}
+ iio_device_release_direct(indio_dev);
+
return ret ? : len;
}
@@ -616,6 +411,76 @@ static int ad7173_calibrate_all(struct ad7173_state *st, struct iio_dev *indio_d
return 0;
}
+/*
+ * Associative array of channel pairs for open wire detection
+ * The array is indexed by ain and gives the associated channel pair
+ * to perform the open wire detection with
+ * the channel pair [0] is for non differential and pair [1]
+ * is for differential inputs
+ */
+static int openwire_ain_to_channel_pair[][2][2] = {
+/* AIN Single Differential */
+ [0] = { { 0, 15 }, { 1, 2 } },
+ [1] = { { 1, 2 }, { 2, 1 } },
+ [2] = { { 3, 4 }, { 5, 6 } },
+ [3] = { { 5, 6 }, { 6, 5 } },
+ [4] = { { 7, 8 }, { 9, 10 } },
+ [5] = { { 9, 10 }, { 10, 9 } },
+ [6] = { { 11, 12 }, { 13, 14 } },
+ [7] = { { 13, 14 }, { 14, 13 } },
+};
+
+/*
+ * Openwire detection on ad4111 works by running the same input measurement
+ * on two different channels and compare if the difference between the two
+ * measurements exceeds a certain value (typical 300mV)
+ */
+static int ad4111_openwire_event(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad7173_state *st = iio_priv(indio_dev);
+ struct ad7173_channel *adchan = &st->channels[chan->address];
+ struct ad7173_channel_config *cfg = &adchan->cfg;
+ int ret, val1, val2;
+
+ ret = regmap_set_bits(st->reg_gpiocon_regmap, AD7173_REG_GPIO,
+ AD4111_GPIO_GP_OW_EN);
+ if (ret)
+ return ret;
+
+ adchan->cfg.openwire_comp_chan =
+ openwire_ain_to_channel_pair[chan->channel][chan->differential][0];
+
+ ret = ad_sigma_delta_single_conversion(indio_dev, chan, &val1);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev,
+ "Error running ad_sigma_delta single conversion: %d", ret);
+ goto out;
+ }
+
+ adchan->cfg.openwire_comp_chan =
+ openwire_ain_to_channel_pair[chan->channel][chan->differential][1];
+
+ ret = ad_sigma_delta_single_conversion(indio_dev, chan, &val2);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev,
+ "Error running ad_sigma_delta single conversion: %d", ret);
+ goto out;
+ }
+
+ if (abs(val1 - val2) > cfg->openwire_thrsh_raw)
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, chan->address,
+ IIO_EV_TYPE_FAULT, IIO_EV_DIR_FAULT_OPENWIRE),
+ iio_get_time_ns(indio_dev));
+
+out:
+ adchan->cfg.openwire_comp_chan = -1;
+ regmap_clear_bits(st->reg_gpiocon_regmap, AD7173_REG_GPIO,
+ AD4111_GPIO_GP_OW_EN);
+ return ret;
+}
+
static int ad7173_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
unsigned int offset, unsigned int *reg,
unsigned int *mask)
@@ -712,15 +577,28 @@ static struct ad7173_channel_config *
ad7173_find_live_config(struct ad7173_state *st, struct ad7173_channel_config *cfg)
{
struct ad7173_channel_config *cfg_aux;
- ptrdiff_t cmp_size;
int i;
- cmp_size = sizeof_field(struct ad7173_channel_config, config_props);
+ /*
+ * This is just to make sure that the comparison is adapted after
+ * struct ad7173_channel_config was changed.
+ */
+ static_assert(sizeof_field(struct ad7173_channel_config, config_props) ==
+ sizeof(struct {
+ bool bipolar;
+ bool input_buf;
+ u8 odr;
+ u8 ref_sel;
+ }));
+
for (i = 0; i < st->num_channels; i++) {
cfg_aux = &st->channels[i].cfg;
if (cfg_aux->live &&
- !memcmp(&cfg->config_props, &cfg_aux->config_props, cmp_size))
+ cfg->bipolar == cfg_aux->bipolar &&
+ cfg->input_buf == cfg_aux->input_buf &&
+ cfg->odr == cfg_aux->odr &&
+ cfg->ref_sel == cfg_aux->ref_sel)
return cfg_aux;
}
return NULL;
@@ -813,6 +691,9 @@ static int ad7173_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
FIELD_PREP(AD7173_CH_SETUP_SEL_MASK, st->channels[channel].cfg.cfg_slot) |
st->channels[channel].ain;
+ if (st->channels[channel].cfg.openwire_comp_chan >= 0)
+ channel = st->channels[channel].cfg.openwire_comp_chan;
+
return ad_sd_write_reg(&st->sd, AD7173_REG_CH(channel), 2, val);
}
@@ -861,21 +742,280 @@ static int ad7173_disable_all(struct ad_sigma_delta *sd)
static int ad7173_disable_one(struct ad_sigma_delta *sd, unsigned int chan)
{
+ struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd);
+
+ if (st->channels[chan].cfg.openwire_comp_chan >= 0)
+ chan = st->channels[chan].cfg.openwire_comp_chan;
+
return ad_sd_write_reg(sd, AD7173_REG_CH(chan), 2, 0);
}
-static const struct ad_sigma_delta_info ad7173_sigma_delta_info = {
+static const struct ad_sigma_delta_info ad7173_sigma_delta_info_4_slots = {
+ .set_channel = ad7173_set_channel,
+ .append_status = ad7173_append_status,
+ .disable_all = ad7173_disable_all,
+ .disable_one = ad7173_disable_one,
+ .set_mode = ad7173_set_mode,
+ .has_registers = true,
+ .has_named_irqs = true,
+ .addr_shift = 0,
+ .read_mask = BIT(6),
+ .status_ch_mask = GENMASK(3, 0),
+ .data_reg = AD7173_REG_DATA,
+ .num_resetclks = 64,
+ .num_slots = 4,
+};
+
+static const struct ad_sigma_delta_info ad7173_sigma_delta_info_8_slots = {
.set_channel = ad7173_set_channel,
.append_status = ad7173_append_status,
.disable_all = ad7173_disable_all,
.disable_one = ad7173_disable_one,
.set_mode = ad7173_set_mode,
.has_registers = true,
+ .has_named_irqs = true,
.addr_shift = 0,
.read_mask = BIT(6),
.status_ch_mask = GENMASK(3, 0),
.data_reg = AD7173_REG_DATA,
.num_resetclks = 64,
+ .num_slots = 8,
+};
+
+static const struct ad7173_device_info ad4111_device_info = {
+ .name = "ad4111",
+ .id = AD4111_ID,
+ .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .num_voltage_in_div = 8,
+ .num_channels = 16,
+ .num_configs = 8,
+ .num_voltage_in = 8,
+ .num_gpios = 2,
+ .higher_gpio_bits = true,
+ .has_temp = true,
+ .has_vincom_input = true,
+ .has_input_buf = true,
+ .has_current_inputs = true,
+ .has_int_ref = true,
+ .has_internal_fs_calibration = true,
+ .has_openwire_det = true,
+ .clock = 2 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7173_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
+};
+
+static const struct ad7173_device_info ad4112_device_info = {
+ .name = "ad4112",
+ .id = AD4112_ID,
+ .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .num_voltage_in_div = 8,
+ .num_channels = 16,
+ .num_configs = 8,
+ .num_voltage_in = 8,
+ .num_gpios = 2,
+ .higher_gpio_bits = true,
+ .has_vincom_input = true,
+ .has_temp = true,
+ .has_input_buf = true,
+ .has_current_inputs = true,
+ .has_int_ref = true,
+ .has_internal_fs_calibration = true,
+ .clock = 2 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7173_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
+};
+
+static const struct ad7173_device_info ad4113_device_info = {
+ .name = "ad4113",
+ .id = AD4113_ID,
+ .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .num_voltage_in_div = 8,
+ .num_channels = 16,
+ .num_configs = 8,
+ .num_voltage_in = 8,
+ .num_gpios = 2,
+ .data_reg_only_16bit = true,
+ .higher_gpio_bits = true,
+ .has_vincom_input = true,
+ .has_input_buf = true,
+ .has_int_ref = true,
+ .clock = 2 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7173_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
+};
+
+static const struct ad7173_device_info ad4114_device_info = {
+ .name = "ad4114",
+ .id = AD4114_ID,
+ .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .num_voltage_in_div = 16,
+ .num_channels = 16,
+ .num_configs = 8,
+ .num_voltage_in = 16,
+ .num_gpios = 4,
+ .has_vincom_input = true,
+ .has_temp = true,
+ .has_input_buf = true,
+ .has_int_ref = true,
+ .has_internal_fs_calibration = true,
+ .clock = 2 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7173_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
+};
+
+static const struct ad7173_device_info ad4115_device_info = {
+ .name = "ad4115",
+ .id = AD4115_ID,
+ .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .num_voltage_in_div = 16,
+ .num_channels = 16,
+ .num_configs = 8,
+ .num_voltage_in = 16,
+ .num_gpios = 4,
+ .has_vincom_input = true,
+ .has_temp = true,
+ .has_input_buf = true,
+ .has_int_ref = true,
+ .has_internal_fs_calibration = true,
+ .clock = 8 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad4115_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad4115_sinc5_data_rates),
+};
+
+static const struct ad7173_device_info ad4116_device_info = {
+ .name = "ad4116",
+ .id = AD4116_ID,
+ .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .num_voltage_in_div = 11,
+ .num_channels = 16,
+ .num_configs = 8,
+ .num_voltage_in = 16,
+ .num_gpios = 4,
+ .has_vincom_input = true,
+ .has_temp = true,
+ .has_input_buf = true,
+ .has_int_ref = true,
+ .has_internal_fs_calibration = true,
+ .clock = 4 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad4116_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad4116_sinc5_data_rates),
+};
+
+static const struct ad7173_device_info ad7172_2_device_info = {
+ .name = "ad7172-2",
+ .id = AD7172_2_ID,
+ .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .num_voltage_in = 5,
+ .num_channels = 4,
+ .num_configs = 4,
+ .num_gpios = 2,
+ .has_temp = true,
+ .has_input_buf = true,
+ .has_int_ref = true,
+ .has_pow_supply_monitoring = true,
+ .clock = 2 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7173_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
+};
+
+static const struct ad7173_device_info ad7172_4_device_info = {
+ .name = "ad7172-4",
+ .id = AD7172_4_ID,
+ .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .num_voltage_in = 9,
+ .num_channels = 8,
+ .num_configs = 8,
+ .num_gpios = 4,
+ .has_input_buf = true,
+ .has_ref2 = true,
+ .has_pow_supply_monitoring = true,
+ .clock = 2 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7173_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
+};
+
+static const struct ad7173_device_info ad7173_8_device_info = {
+ .name = "ad7173-8",
+ .id = AD7173_ID,
+ .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .num_voltage_in = 17,
+ .num_channels = 16,
+ .num_configs = 8,
+ .num_gpios = 4,
+ .has_temp = true,
+ .has_input_buf = true,
+ .has_int_ref = true,
+ .has_ref2 = true,
+ .clock = 2 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7173_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
+};
+
+static const struct ad7173_device_info ad7175_2_device_info = {
+ .name = "ad7175-2",
+ .id = AD7175_2_ID,
+ .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .num_voltage_in = 5,
+ .num_channels = 4,
+ .num_configs = 4,
+ .num_gpios = 2,
+ .has_temp = true,
+ .has_input_buf = true,
+ .has_int_ref = true,
+ .has_pow_supply_monitoring = true,
+ .clock = 16 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7175_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
+};
+
+static const struct ad7173_device_info ad7175_8_device_info = {
+ .name = "ad7175-8",
+ .id = AD7175_8_ID,
+ .sd_info = &ad7173_sigma_delta_info_8_slots,
+ .num_voltage_in = 17,
+ .num_channels = 16,
+ .num_configs = 8,
+ .num_gpios = 4,
+ .has_temp = true,
+ .has_input_buf = true,
+ .has_int_ref = true,
+ .has_ref2 = true,
+ .has_pow_supply_monitoring = true,
+ .clock = 16 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7175_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
+};
+
+static const struct ad7173_device_info ad7176_2_device_info = {
+ .name = "ad7176-2",
+ .id = AD7176_ID,
+ .sd_info = &ad7173_sigma_delta_info_4_slots,
+ .num_voltage_in = 5,
+ .num_channels = 4,
+ .num_configs = 4,
+ .num_gpios = 2,
+ .has_int_ref = true,
+ .clock = 16 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7175_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
+};
+
+static const struct ad7173_device_info ad7177_2_device_info = {
+ .name = "ad7177-2",
+ .id = AD7177_ID,
+ .sd_info = &ad7173_sigma_delta_info_4_slots,
+ .num_voltage_in = 5,
+ .num_channels = 4,
+ .num_configs = 4,
+ .num_gpios = 2,
+ .has_temp = true,
+ .has_input_buf = true,
+ .has_int_ref = true,
+ .has_pow_supply_monitoring = true,
+ .clock = 16 * HZ_PER_MHZ,
+ .odr_start_value = AD7177_ODR_START_VALUE,
+ .sinc5_data_rates = ad7175_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
};
static int ad7173_setup(struct iio_dev *indio_dev)
@@ -969,6 +1109,12 @@ static int ad7173_read_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
+ if (ch->openwire_det_en) {
+ ret = ad4111_openwire_event(indio_dev, chan);
+ if (ret < 0)
+ return ret;
+ }
+
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
@@ -1033,11 +1179,10 @@ static int ad7173_write_raw(struct iio_dev *indio_dev,
struct ad7173_state *st = iio_priv(indio_dev);
struct ad7173_channel_config *cfg;
unsigned int freq, i;
- int ret;
+ int ret = 0;
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
switch (info) {
/*
@@ -1071,7 +1216,7 @@ static int ad7173_write_raw(struct iio_dev *indio_dev,
break;
}
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
}
@@ -1113,12 +1258,57 @@ static int ad7173_debug_reg_access(struct iio_dev *indio_dev, unsigned int reg,
return ad_sd_write_reg(&st->sd, reg, reg_size, writeval);
}
+static int ad7173_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ bool state)
+{
+ struct ad7173_state *st = iio_priv(indio_dev);
+ struct ad7173_channel *adchan = &st->channels[chan->address];
+
+ switch (type) {
+ case IIO_EV_TYPE_FAULT:
+ adchan->openwire_det_en = state;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7173_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct ad7173_state *st = iio_priv(indio_dev);
+ struct ad7173_channel *adchan = &st->channels[chan->address];
+
+ switch (type) {
+ case IIO_EV_TYPE_FAULT:
+ return adchan->openwire_det_en;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_event_spec ad4111_events[] = {
+ {
+ .type = IIO_EV_TYPE_FAULT,
+ .dir = IIO_EV_DIR_FAULT_OPENWIRE,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ .mask_shared_by_all = BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
static const struct iio_info ad7173_info = {
.read_raw = &ad7173_read_raw,
.write_raw = &ad7173_write_raw,
.debugfs_reg_access = &ad7173_debug_reg_access,
.validate_trigger = ad_sd_validate_trigger,
.update_scan_mode = ad7173_update_scan_mode,
+ .write_event_config = ad7173_write_event_config,
+ .read_event_config = ad7173_read_event_config,
};
static const struct iio_scan_type ad4113_scan_type = {
@@ -1322,6 +1512,37 @@ static int ad7173_validate_reference(struct ad7173_state *st, int ref_sel)
return 0;
}
+static int ad7173_validate_openwire_ain_inputs(struct ad7173_state *st,
+ bool differential,
+ unsigned int ain0,
+ unsigned int ain1)
+{
+ /*
+ * If the channel is configured as differential,
+ * the ad4111 requires specific ains to be used together
+ */
+ if (differential)
+ return (ain0 % 2) ? (ain0 - 1) == ain1 : (ain0 + 1) == ain1;
+
+ return ain1 == AD4111_VINCOM_INPUT;
+}
+
+static unsigned int ad7173_calc_openwire_thrsh_raw(struct ad7173_state *st,
+ struct iio_chan_spec *chan,
+ struct ad7173_channel *chan_st_priv,
+ unsigned int thrsh_mv) {
+ unsigned int thrsh_raw;
+
+ thrsh_raw =
+ BIT(chan->scan_type.realbits - !!(chan_st_priv->cfg.bipolar))
+ * thrsh_mv
+ / ad7173_get_ref_voltage_milli(st, chan_st_priv->cfg.ref_sel);
+ if (chan->channel < st->info->num_voltage_in_div)
+ thrsh_raw /= AD4111_DIVIDER_RATIO;
+
+ return thrsh_raw;
+}
+
static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
{
struct ad7173_channel *chans_st_arr, *chan_st_priv;
@@ -1369,6 +1590,7 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
chan_st_priv->cfg.bipolar = false;
chan_st_priv->cfg.input_buf = st->info->has_input_buf;
chan_st_priv->cfg.ref_sel = AD7173_SETUP_REF_SEL_INT_REF;
+ chan_st_priv->cfg.openwire_comp_chan = -1;
st->adc_mode |= AD7173_ADC_MODE_REF_EN;
if (st->info->data_reg_only_16bit)
chan_arr[chan_index].scan_type = ad4113_scan_type;
@@ -1435,6 +1657,7 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
chan->channel = ain[0];
chan_st_priv->cfg.input_buf = st->info->has_input_buf;
chan_st_priv->cfg.odr = 0;
+ chan_st_priv->cfg.openwire_comp_chan = -1;
chan_st_priv->cfg.bipolar = fwnode_property_read_bool(child, "bipolar");
if (chan_st_priv->cfg.bipolar)
@@ -1449,6 +1672,14 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
chan_st_priv->cfg.input_buf = st->info->has_input_buf;
chan->channel2 = ain[1];
chan_st_priv->ain = AD7173_CH_ADDRESS(ain[0], ain[1]);
+ if (st->info->has_openwire_det &&
+ ad7173_validate_openwire_ain_inputs(st, chan->differential, ain[0], ain[1])) {
+ chan->event_spec = ad4111_events;
+ chan->num_event_specs = ARRAY_SIZE(ad4111_events);
+ chan_st_priv->cfg.openwire_thrsh_raw =
+ ad7173_calc_openwire_thrsh_raw(st, chan, chan_st_priv,
+ AD4111_OW_DET_THRSH_MV);
+ }
}
if (st->info->data_reg_only_16bit)
@@ -1515,12 +1746,6 @@ static int ad7173_fw_parse_device_config(struct iio_dev *indio_dev)
return ret;
}
- ret = fwnode_irq_get_byname(dev_fwnode(dev), "rdy");
- if (ret < 0)
- return dev_err_probe(dev, ret, "Interrupt 'rdy' is required\n");
-
- st->sigma_delta_info.irq_line = ret;
-
return ad7173_fw_parse_channel_config(indio_dev);
}
@@ -1552,9 +1777,7 @@ static int ad7173_probe(struct spi_device *spi)
spi->mode = SPI_MODE_3;
spi_setup(spi);
- st->sigma_delta_info = ad7173_sigma_delta_info;
- st->sigma_delta_info.num_slots = st->info->num_configs;
- ret = ad_sd_init(&st->sd, indio_dev, spi, &st->sigma_delta_info);
+ ret = ad_sd_init(&st->sd, indio_dev, spi, st->info->sd_info);
if (ret)
return ret;
diff --git a/drivers/iio/adc/ad7191.c b/drivers/iio/adc/ad7191.c
new file mode 100644
index 000000000000..d9cd903ffdd2
--- /dev/null
+++ b/drivers/iio/adc/ad7191.c
@@ -0,0 +1,554 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD7191 ADC driver
+ *
+ * Copyright 2025 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <linux/iio/adc/ad_sigma_delta.h>
+#include <linux/iio/iio.h>
+
+#define ad_sigma_delta_to_ad7191(sigmad) \
+ container_of((sigmad), struct ad7191_state, sd)
+
+#define AD7191_TEMP_CODES_PER_DEGREE 2815
+
+#define AD7191_CHAN_MASK BIT(0)
+#define AD7191_TEMP_MASK BIT(1)
+
+enum ad7191_channel {
+ AD7191_CH_AIN1_AIN2,
+ AD7191_CH_AIN3_AIN4,
+ AD7191_CH_TEMP,
+};
+
+/*
+ * NOTE:
+ * The AD7191 features a dual-use data out ready DOUT/RDY output.
+ * In order to avoid contentions on the SPI bus, it's therefore necessary
+ * to use SPI bus locking.
+ *
+ * The DOUT/RDY output must also be wired to an interrupt-capable GPIO.
+ *
+ * The SPI controller's chip select must be connected to the PDOWN pin
+ * of the ADC. When CS (PDOWN) is high, it powers down the device and
+ * resets the internal circuitry.
+ */
+
+struct ad7191_state {
+ struct ad_sigma_delta sd;
+ struct mutex lock; /* Protect device state */
+
+ struct gpio_descs *odr_gpios;
+ struct gpio_descs *pga_gpios;
+ struct gpio_desc *temp_gpio;
+ struct gpio_desc *chan_gpio;
+
+ u16 int_vref_mv;
+ const u32 (*scale_avail)[2];
+ size_t scale_avail_size;
+ u32 scale_index;
+ const u32 *samp_freq_avail;
+ size_t samp_freq_avail_size;
+ u32 samp_freq_index;
+
+ struct clk *mclk;
+};
+
+static int ad7191_set_channel(struct ad_sigma_delta *sd, unsigned int address)
+{
+ struct ad7191_state *st = ad_sigma_delta_to_ad7191(sd);
+ u8 temp_gpio_val, chan_gpio_val;
+
+ if (!FIELD_FIT(AD7191_CHAN_MASK | AD7191_TEMP_MASK, address))
+ return -EINVAL;
+
+ chan_gpio_val = FIELD_GET(AD7191_CHAN_MASK, address);
+ temp_gpio_val = FIELD_GET(AD7191_TEMP_MASK, address);
+
+ gpiod_set_value(st->chan_gpio, chan_gpio_val);
+ gpiod_set_value(st->temp_gpio, temp_gpio_val);
+
+ return 0;
+}
+
+static int ad7191_set_cs(struct ad_sigma_delta *sigma_delta, int assert)
+{
+ struct spi_transfer t = {
+ .len = 0,
+ .cs_change = assert,
+ };
+ struct spi_message m;
+
+ spi_message_init_with_transfers(&m, &t, 1);
+
+ return spi_sync_locked(sigma_delta->spi, &m);
+}
+
+static int ad7191_set_mode(struct ad_sigma_delta *sd,
+ enum ad_sigma_delta_mode mode)
+{
+ struct ad7191_state *st = ad_sigma_delta_to_ad7191(sd);
+
+ switch (mode) {
+ case AD_SD_MODE_CONTINUOUS:
+ case AD_SD_MODE_SINGLE:
+ return ad7191_set_cs(&st->sd, 1);
+ case AD_SD_MODE_IDLE:
+ return ad7191_set_cs(&st->sd, 0);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct ad_sigma_delta_info ad7191_sigma_delta_info = {
+ .set_channel = ad7191_set_channel,
+ .set_mode = ad7191_set_mode,
+ .has_registers = false,
+};
+
+static int ad7191_init_regulators(struct iio_dev *indio_dev)
+{
+ struct ad7191_state *st = iio_priv(indio_dev);
+ struct device *dev = &st->sd.spi->dev;
+ int ret;
+
+ ret = devm_regulator_get_enable(dev, "avdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable specified AVdd supply\n");
+
+ ret = devm_regulator_get_enable(dev, "dvdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable specified DVdd supply\n");
+
+ ret = devm_regulator_get_enable_read_voltage(dev, "vref");
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to get Vref voltage\n");
+
+ st->int_vref_mv = ret / 1000;
+
+ return 0;
+}
+
+static int ad7191_config_setup(struct iio_dev *indio_dev)
+{
+ struct ad7191_state *st = iio_priv(indio_dev);
+ struct device *dev = &st->sd.spi->dev;
+ /* Sampling frequencies in Hz, see Table 5 */
+ static const u32 samp_freq[4] = { 120, 60, 50, 10 };
+ /* Gain options, see Table 7 */
+ const u32 gain[4] = { 1, 8, 64, 128 };
+ static u32 scale_buffer[4][2];
+ int odr_value, odr_index = 0, pga_value, pga_index = 0, i, ret;
+ u64 scale_uv;
+
+ st->samp_freq_index = 0;
+ st->scale_index = 0;
+
+ ret = device_property_read_u32(dev, "adi,odr-value", &odr_value);
+ if (ret && ret != -EINVAL)
+ return dev_err_probe(dev, ret, "Failed to get odr value.\n");
+
+ if (ret == -EINVAL) {
+ st->odr_gpios = devm_gpiod_get_array(dev, "odr", GPIOD_OUT_LOW);
+ if (IS_ERR(st->odr_gpios))
+ return dev_err_probe(dev, PTR_ERR(st->odr_gpios),
+ "Failed to get odr gpios.\n");
+
+ if (st->odr_gpios->ndescs != 2)
+ return dev_err_probe(dev, -EINVAL, "Expected 2 odr gpio pins.\n");
+
+ st->samp_freq_avail = samp_freq;
+ st->samp_freq_avail_size = ARRAY_SIZE(samp_freq);
+ } else {
+ for (i = 0; i < ARRAY_SIZE(samp_freq); i++) {
+ if (odr_value != samp_freq[i])
+ continue;
+ odr_index = i;
+ break;
+ }
+
+ st->samp_freq_avail = &samp_freq[odr_index];
+ st->samp_freq_avail_size = 1;
+
+ st->odr_gpios = NULL;
+ }
+
+ mutex_lock(&st->lock);
+
+ for (i = 0; i < ARRAY_SIZE(scale_buffer); i++) {
+ scale_uv = ((u64)st->int_vref_mv * NANO) >>
+ (indio_dev->channels[0].scan_type.realbits - 1);
+ do_div(scale_uv, gain[i]);
+ scale_buffer[i][1] = do_div(scale_uv, NANO);
+ scale_buffer[i][0] = scale_uv;
+ }
+
+ mutex_unlock(&st->lock);
+
+ ret = device_property_read_u32(dev, "adi,pga-value", &pga_value);
+ if (ret && ret != -EINVAL)
+ return dev_err_probe(dev, ret, "Failed to get pga value.\n");
+
+ if (ret == -EINVAL) {
+ st->pga_gpios = devm_gpiod_get_array(dev, "pga", GPIOD_OUT_LOW);
+ if (IS_ERR(st->pga_gpios))
+ return dev_err_probe(dev, PTR_ERR(st->pga_gpios),
+ "Failed to get pga gpios.\n");
+
+ if (st->pga_gpios->ndescs != 2)
+ return dev_err_probe(dev, -EINVAL, "Expected 2 pga gpio pins.\n");
+
+ st->scale_avail = scale_buffer;
+ st->scale_avail_size = ARRAY_SIZE(scale_buffer);
+ } else {
+ for (i = 0; i < ARRAY_SIZE(gain); i++) {
+ if (pga_value != gain[i])
+ continue;
+ pga_index = i;
+ break;
+ }
+
+ st->scale_avail = &scale_buffer[pga_index];
+ st->scale_avail_size = 1;
+
+ st->pga_gpios = NULL;
+ }
+
+ st->temp_gpio = devm_gpiod_get(dev, "temp", GPIOD_OUT_LOW);
+ if (IS_ERR(st->temp_gpio))
+ return dev_err_probe(dev, PTR_ERR(st->temp_gpio),
+ "Failed to get temp gpio.\n");
+
+ st->chan_gpio = devm_gpiod_get(dev, "chan", GPIOD_OUT_LOW);
+ if (IS_ERR(st->chan_gpio))
+ return dev_err_probe(dev, PTR_ERR(st->chan_gpio),
+ "Failed to get chan gpio.\n");
+
+ return 0;
+}
+
+static int ad7191_clock_setup(struct ad7191_state *st)
+{
+ struct device *dev = &st->sd.spi->dev;
+
+ st->mclk = devm_clk_get_optional_enabled(dev, "mclk");
+ if (IS_ERR(st->mclk))
+ return dev_err_probe(dev, PTR_ERR(st->mclk),
+ "Failed to get mclk.\n");
+
+ return 0;
+}
+
+static int ad7191_setup(struct iio_dev *indio_dev)
+{
+ struct ad7191_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = ad7191_init_regulators(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ad7191_config_setup(indio_dev);
+ if (ret)
+ return ret;
+
+ return ad7191_clock_setup(st);
+}
+
+static int ad7191_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long m)
+{
+ struct ad7191_state *st = iio_priv(indio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ return ad_sigma_delta_single_conversion(indio_dev, chan, val);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE: {
+ guard(mutex)(&st->lock);
+ *val = st->scale_avail[st->scale_index][0];
+ *val2 = st->scale_avail[st->scale_index][1];
+ return IIO_VAL_INT_PLUS_NANO;
+ }
+ case IIO_TEMP:
+ *val = 0;
+ *val2 = NANO / AD7191_TEMP_CODES_PER_DEGREE;
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ *val = -(1 << (chan->scan_type.realbits - 1));
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ return IIO_VAL_INT;
+ case IIO_TEMP:
+ *val -= 273 * AD7191_TEMP_CODES_PER_DEGREE;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->samp_freq_avail[st->samp_freq_index];
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7191_set_gain(struct ad7191_state *st, int gain_index)
+{
+ DECLARE_BITMAP(bitmap, 2) = { };
+
+ st->scale_index = gain_index;
+
+ bitmap_write(bitmap, gain_index, 0, 2);
+
+ return gpiod_multi_set_value_cansleep(st->pga_gpios, bitmap);
+}
+
+static int ad7191_set_samp_freq(struct ad7191_state *st, int samp_freq_index)
+{
+ DECLARE_BITMAP(bitmap, 2) = {};
+
+ st->samp_freq_index = samp_freq_index;
+
+ bitmap_write(bitmap, samp_freq_index, 0, 2);
+
+ return gpiod_multi_set_value_cansleep(st->odr_gpios, bitmap);
+}
+
+static int __ad7191_write_raw(struct ad7191_state *st,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int i;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE: {
+ if (!st->pga_gpios)
+ return -EPERM;
+ guard(mutex)(&st->lock);
+ for (i = 0; i < st->scale_avail_size; i++) {
+ if (val2 == st->scale_avail[i][1])
+ return ad7191_set_gain(st, i);
+ }
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ if (!st->odr_gpios)
+ return -EPERM;
+ guard(mutex)(&st->lock);
+ for (i = 0; i < st->samp_freq_avail_size; i++) {
+ if (val == st->samp_freq_avail[i])
+ return ad7191_set_samp_freq(st, i);
+ }
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7191_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2,
+ long mask)
+{
+ struct ad7191_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = __ad7191_write_raw(st, chan, val, val2, mask);
+
+ iio_device_release_direct(indio_dev);
+
+ return ret;
+}
+
+static int ad7191_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7191_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, const int **vals,
+ int *type, int *length, long mask)
+{
+ struct ad7191_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *vals = (int *)st->scale_avail;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *length = st->scale_avail_size * 2;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = (int *)st->samp_freq_avail;
+ *type = IIO_VAL_INT;
+ *length = st->samp_freq_avail_size;
+ return IIO_AVAIL_LIST;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info ad7191_info = {
+ .read_raw = ad7191_read_raw,
+ .write_raw = ad7191_write_raw,
+ .write_raw_get_fmt = ad7191_write_raw_get_fmt,
+ .read_avail = ad7191_read_avail,
+ .validate_trigger = ad_sd_validate_trigger,
+};
+
+static const struct iio_chan_spec ad7191_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .address = AD7191_CH_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_BE,
+ },
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .differential = 1,
+ .indexed = 1,
+ .channel = 1,
+ .channel2 = 2,
+ .address = AD7191_CH_AIN1_AIN2,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_BE,
+ },
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .differential = 1,
+ .indexed = 1,
+ .channel = 3,
+ .channel2 = 4,
+ .address = AD7191_CH_AIN3_AIN4,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = 2,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_BE,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static int ad7191_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct ad7191_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ ret = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
+ indio_dev->name = "ad7191";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad7191_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad7191_channels);
+ indio_dev->info = &ad7191_info;
+
+ ret = ad_sd_init(&st->sd, indio_dev, spi, &ad7191_sigma_delta_info);
+ if (ret)
+ return ret;
+
+ ret = devm_ad_sd_setup_buffer_and_trigger(dev, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ad7191_setup(indio_dev);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id ad7191_of_match[] = {
+ { .compatible = "adi,ad7191", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad7191_of_match);
+
+static const struct spi_device_id ad7191_id_table[] = {
+ { "ad7191" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad7191_id_table);
+
+static struct spi_driver ad7191_driver = {
+ .driver = {
+ .name = "ad7191",
+ .of_match_table = ad7191_of_match,
+ },
+ .probe = ad7191_probe,
+ .id_table = ad7191_id_table,
+};
+module_spi_driver(ad7191_driver);
+
+MODULE_AUTHOR("Alisa-Dariana Roman <alisa.roman@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7191 ADC");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_AD_SIGMA_DELTA");
diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c
index cfaf8f7e0a07..530e1d307860 100644
--- a/drivers/iio/adc/ad7192.c
+++ b/drivers/iio/adc/ad7192.c
@@ -7,6 +7,7 @@
#include <linux/interrupt.h>
#include <linux/bitfield.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/device.h>
@@ -256,6 +257,9 @@ static ssize_t ad7192_write_syscalib(struct iio_dev *indio_dev,
if (ret)
return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
temp = st->syscalib_mode[chan->channel];
if (sys_calib) {
if (temp == AD7192_SYSCALIB_ZERO_SCALE)
@@ -266,6 +270,8 @@ static ssize_t ad7192_write_syscalib(struct iio_dev *indio_dev,
chan->address);
}
+ iio_device_release_direct(indio_dev);
+
return ret ? ret : len;
}
@@ -693,9 +699,8 @@ static ssize_t ad7192_set(struct device *dev,
if (ret < 0)
return ret;
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
switch ((u32)this_attr->address) {
case AD7192_REG_GPOCON:
@@ -718,7 +723,7 @@ static ssize_t ad7192_set(struct device *dev,
ret = -EINVAL;
}
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret ? ret : len;
}
@@ -945,82 +950,83 @@ static int ad7192_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
-static int ad7192_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val,
- int val2,
- long mask)
+static int __ad7192_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
{
struct ad7192_state *st = iio_priv(indio_dev);
- int ret, i, div;
+ int i, div;
unsigned int tmp;
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
-
- mutex_lock(&st->lock);
+ guard(mutex)(&st->lock);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
- ret = -EINVAL;
- for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
- if (val2 == st->scale_avail[i][1]) {
- ret = 0;
- tmp = st->conf;
- st->conf &= ~AD7192_CONF_GAIN_MASK;
- st->conf |= FIELD_PREP(AD7192_CONF_GAIN_MASK, i);
- if (tmp == st->conf)
- break;
- ad_sd_write_reg(&st->sd, AD7192_REG_CONF,
- 3, st->conf);
- ad7192_calibrate_all(st);
- break;
- }
- break;
- case IIO_CHAN_INFO_SAMP_FREQ:
- if (!val) {
- ret = -EINVAL;
- break;
+ for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) {
+ if (val2 != st->scale_avail[i][1])
+ continue;
+
+ tmp = st->conf;
+ st->conf &= ~AD7192_CONF_GAIN_MASK;
+ st->conf |= FIELD_PREP(AD7192_CONF_GAIN_MASK, i);
+ if (tmp == st->conf)
+ return 0;
+ ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf);
+ ad7192_calibrate_all(st);
+ return 0;
}
+ return -EINVAL;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (!val)
+ return -EINVAL;
div = st->fclk / (val * ad7192_get_f_order(st) * 1024);
- if (div < 1 || div > 1023) {
- ret = -EINVAL;
- break;
- }
+ if (div < 1 || div > 1023)
+ return -EINVAL;
st->mode &= ~AD7192_MODE_RATE_MASK;
st->mode |= FIELD_PREP(AD7192_MODE_RATE_MASK, div);
ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
ad7192_update_filter_freq_avail(st);
- break;
+ return 0;
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
- ret = ad7192_set_3db_filter_freq(st, val, val2 / 1000);
- break;
+ return ad7192_set_3db_filter_freq(st, val, val2 / 1000);
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
- ret = -EINVAL;
- for (i = 0; i < ARRAY_SIZE(st->oversampling_ratio_avail); i++)
- if (val == st->oversampling_ratio_avail[i]) {
- ret = 0;
- tmp = st->mode;
- st->mode &= ~AD7192_MODE_AVG_MASK;
- st->mode |= FIELD_PREP(AD7192_MODE_AVG_MASK, i);
- if (tmp == st->mode)
- break;
- ad_sd_write_reg(&st->sd, AD7192_REG_MODE,
- 3, st->mode);
- break;
- }
- ad7192_update_filter_freq_avail(st);
- break;
+ for (i = 0; i < ARRAY_SIZE(st->oversampling_ratio_avail); i++) {
+ if (val != st->oversampling_ratio_avail[i])
+ continue;
+
+ tmp = st->mode;
+ st->mode &= ~AD7192_MODE_AVG_MASK;
+ st->mode |= FIELD_PREP(AD7192_MODE_AVG_MASK, i);
+ if (tmp == st->mode)
+ return 0;
+ ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
+ ad7192_update_filter_freq_avail(st);
+ return 0;
+ }
+ return -EINVAL;
default:
- ret = -EINVAL;
+ return -EINVAL;
}
+}
+
+static int ad7192_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
- mutex_unlock(&st->lock);
+ ret = __ad7192_write_raw(indio_dev, chan, val, val2, mask);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
}
diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c
index 858c8be2ff1a..18559757f908 100644
--- a/drivers/iio/adc/ad7266.c
+++ b/drivers/iio/adc/ad7266.c
@@ -153,11 +153,10 @@ static int ad7266_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = ad7266_read_single(st, val, chan->address);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c
index b35bd4d9ef81..28b88092b4aa 100644
--- a/drivers/iio/adc/ad7298.c
+++ b/drivers/iio/adc/ad7298.c
@@ -232,16 +232,15 @@ static int ad7298_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
if (chan->address == AD7298_CH_TEMP)
ret = ad7298_scan_temp(st, val);
else
ret = ad7298_scan_direct(st, chan->address);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c
index 4f32cb22f140..4fcb49fdf566 100644
--- a/drivers/iio/adc/ad7380.c
+++ b/drivers/iio/adc/ad7380.c
@@ -15,6 +15,10 @@
* ad7386/7/8-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7386-4-7387-4-7388-4.pdf
* adaq4370-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4370-4.pdf
* adaq4380-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4380-4.pdf
+ * adaq4381-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4381-4.pdf
+ *
+ * HDL ad738x_fmc: https://analogdevicesinc.github.io/hdl/projects/ad738x_fmc/index.html
+ *
*/
#include <linux/align.h>
@@ -29,11 +33,14 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/spi/offload/consumer.h>
#include <linux/spi/spi.h>
#include <linux/units.h>
#include <linux/util_macros.h>
#include <linux/iio/buffer.h>
+#include <linux/iio/buffer-dmaengine.h>
+#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
@@ -91,6 +98,12 @@
#define AD7380_NUM_SDO_LINES 1
#define AD7380_DEFAULT_GAIN_MILLI 1000
+/*
+ * Using SPI offload, storagebits is always 32, so can't be used to compute struct
+ * spi_transfer.len. Using realbits instead.
+ */
+#define AD7380_SPI_BYTES(scan_type) ((scan_type)->realbits > 16 ? 4 : 2)
+
struct ad7380_timing_specs {
const unsigned int t_csh_ns; /* CS minimum high time */
};
@@ -98,6 +111,7 @@ struct ad7380_timing_specs {
struct ad7380_chip_info {
const char *name;
const struct iio_chan_spec *channels;
+ const struct iio_chan_spec *offload_channels;
unsigned int num_channels;
unsigned int num_simult_channels;
bool has_hardware_gain;
@@ -110,6 +124,25 @@ struct ad7380_chip_info {
unsigned int num_vcm_supplies;
const unsigned long *available_scan_masks;
const struct ad7380_timing_specs *timing_specs;
+ u32 max_conversion_rate_hz;
+};
+
+static const struct iio_event_spec ad7380_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_shared_by_dir = BIT(IIO_EV_INFO_VALUE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_shared_by_dir = BIT(IIO_EV_INFO_VALUE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_ENABLE),
+ },
};
enum {
@@ -197,6 +230,91 @@ static const struct iio_scan_type ad7380_scan_type_16_u[] = {
},
};
+/*
+ * Defining here scan types for offload mode, since with current available HDL
+ * only a value of 32 for storagebits is supported.
+ */
+
+/* Extended scan types for 12-bit unsigned chips, offload support. */
+static const struct iio_scan_type ad7380_scan_type_12_u_offload[] = {
+ [AD7380_SCAN_TYPE_NORMAL] = {
+ .sign = 'u',
+ .realbits = 12,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
+ .sign = 'u',
+ .realbits = 14,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+};
+
+/* Extended scan types for 14-bit signed chips, offload support. */
+static const struct iio_scan_type ad7380_scan_type_14_s_offload[] = {
+ [AD7380_SCAN_TYPE_NORMAL] = {
+ .sign = 's',
+ .realbits = 14,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+};
+
+/* Extended scan types for 14-bit unsigned chips, offload support. */
+static const struct iio_scan_type ad7380_scan_type_14_u_offload[] = {
+ [AD7380_SCAN_TYPE_NORMAL] = {
+ .sign = 'u',
+ .realbits = 14,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+};
+
+/* Extended scan types for 16-bit signed_chips, offload support. */
+static const struct iio_scan_type ad7380_scan_type_16_s_offload[] = {
+ [AD7380_SCAN_TYPE_NORMAL] = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
+ .sign = 's',
+ .realbits = 18,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+};
+
+/* Extended scan types for 16-bit unsigned chips, offload support. */
+static const struct iio_scan_type ad7380_scan_type_16_u_offload[] = {
+ [AD7380_SCAN_TYPE_NORMAL] = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
+ .sign = 'u',
+ .realbits = 18,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+};
+
#define _AD7380_CHANNEL(index, bits, diff, sign, gain) { \
.type = IIO_VOLTAGE, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
@@ -214,50 +332,127 @@ static const struct iio_scan_type ad7380_scan_type_16_u[] = {
.has_ext_scan_type = 1, \
.ext_scan_type = ad7380_scan_type_##bits##_##sign, \
.num_ext_scan_type = ARRAY_SIZE(ad7380_scan_type_##bits##_##sign), \
+ .event_spec = ad7380_events, \
+ .num_event_specs = ARRAY_SIZE(ad7380_events), \
+}
+
+#define _AD7380_OFFLOAD_CHANNEL(index, bits, diff, sign, gain) { \
+ .type = IIO_VOLTAGE, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ ((gain) ? BIT(IIO_CHAN_INFO_SCALE) : 0) | \
+ ((diff) ? 0 : BIT(IIO_CHAN_INFO_OFFSET)), \
+ .info_mask_shared_by_type = ((gain) ? 0 : BIT(IIO_CHAN_INFO_SCALE)) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .indexed = 1, \
+ .differential = (diff), \
+ .channel = (diff) ? (2 * (index)) : (index), \
+ .channel2 = (diff) ? (2 * (index) + 1) : 0, \
+ .scan_index = (index), \
+ .has_ext_scan_type = 1, \
+ .ext_scan_type = ad7380_scan_type_##bits##_##sign##_offload, \
+ .num_ext_scan_type = \
+ ARRAY_SIZE(ad7380_scan_type_##bits##_##sign##_offload), \
+ .event_spec = ad7380_events, \
+ .num_event_specs = ARRAY_SIZE(ad7380_events), \
}
+/*
+ * Notes on the offload channels:
+ * - There is no soft timestamp since everything is done in hardware.
+ * - There is a sampling frequency attribute added. This controls the SPI
+ * offload trigger.
+ * - The storagebits value depends on the SPI offload provider. Currently there
+ * is only one supported provider, namely the ADI PULSAR ADC HDL project,
+ * which always uses 32-bit words for data values, even for <= 16-bit ADCs.
+ * So the value is just hardcoded to 32 for now.
+ */
+
#define AD7380_CHANNEL(index, bits, diff, sign) \
_AD7380_CHANNEL(index, bits, diff, sign, false)
#define ADAQ4380_CHANNEL(index, bits, diff, sign) \
_AD7380_CHANNEL(index, bits, diff, sign, true)
-#define DEFINE_AD7380_2_CHANNEL(name, bits, diff, sign) \
+#define DEFINE_AD7380_2_CHANNEL(name, bits, diff, sign) \
+static const struct iio_chan_spec name[] = { \
+ AD7380_CHANNEL(0, bits, diff, sign), \
+ AD7380_CHANNEL(1, bits, diff, sign), \
+ IIO_CHAN_SOFT_TIMESTAMP(2), \
+}
+
+#define DEFINE_AD7380_4_CHANNEL(name, bits, diff, sign) \
+static const struct iio_chan_spec name[] = { \
+ AD7380_CHANNEL(0, bits, diff, sign), \
+ AD7380_CHANNEL(1, bits, diff, sign), \
+ AD7380_CHANNEL(2, bits, diff, sign), \
+ AD7380_CHANNEL(3, bits, diff, sign), \
+ IIO_CHAN_SOFT_TIMESTAMP(4), \
+}
+
+#define DEFINE_ADAQ4380_4_CHANNEL(name, bits, diff, sign) \
+static const struct iio_chan_spec name[] = { \
+ ADAQ4380_CHANNEL(0, bits, diff, sign), \
+ ADAQ4380_CHANNEL(1, bits, diff, sign), \
+ ADAQ4380_CHANNEL(2, bits, diff, sign), \
+ ADAQ4380_CHANNEL(3, bits, diff, sign), \
+ IIO_CHAN_SOFT_TIMESTAMP(4), \
+}
+
+#define DEFINE_AD7380_8_CHANNEL(name, bits, diff, sign) \
+static const struct iio_chan_spec name[] = { \
+ AD7380_CHANNEL(0, bits, diff, sign), \
+ AD7380_CHANNEL(1, bits, diff, sign), \
+ AD7380_CHANNEL(2, bits, diff, sign), \
+ AD7380_CHANNEL(3, bits, diff, sign), \
+ AD7380_CHANNEL(4, bits, diff, sign), \
+ AD7380_CHANNEL(5, bits, diff, sign), \
+ AD7380_CHANNEL(6, bits, diff, sign), \
+ AD7380_CHANNEL(7, bits, diff, sign), \
+ IIO_CHAN_SOFT_TIMESTAMP(8), \
+}
+
+#define AD7380_OFFLOAD_CHANNEL(index, bits, diff, sign) \
+_AD7380_OFFLOAD_CHANNEL(index, bits, diff, sign, false)
+
+#define ADAQ4380_OFFLOAD_CHANNEL(index, bits, diff, sign) \
+_AD7380_OFFLOAD_CHANNEL(index, bits, diff, sign, true)
+
+#define DEFINE_AD7380_2_OFFLOAD_CHANNEL(name, bits, diff, sign) \
static const struct iio_chan_spec name[] = { \
- AD7380_CHANNEL(0, bits, diff, sign), \
- AD7380_CHANNEL(1, bits, diff, sign), \
- IIO_CHAN_SOFT_TIMESTAMP(2), \
+ AD7380_OFFLOAD_CHANNEL(0, bits, diff, sign), \
+ AD7380_OFFLOAD_CHANNEL(1, bits, diff, sign), \
}
-#define DEFINE_AD7380_4_CHANNEL(name, bits, diff, sign) \
+#define DEFINE_AD7380_4_OFFLOAD_CHANNEL(name, bits, diff, sign) \
static const struct iio_chan_spec name[] = { \
- AD7380_CHANNEL(0, bits, diff, sign), \
- AD7380_CHANNEL(1, bits, diff, sign), \
- AD7380_CHANNEL(2, bits, diff, sign), \
- AD7380_CHANNEL(3, bits, diff, sign), \
- IIO_CHAN_SOFT_TIMESTAMP(4), \
+ AD7380_OFFLOAD_CHANNEL(0, bits, diff, sign), \
+ AD7380_OFFLOAD_CHANNEL(1, bits, diff, sign), \
+ AD7380_OFFLOAD_CHANNEL(2, bits, diff, sign), \
+ AD7380_OFFLOAD_CHANNEL(3, bits, diff, sign), \
}
-#define DEFINE_ADAQ4380_4_CHANNEL(name, bits, diff, sign) \
-static const struct iio_chan_spec name[] = { \
- ADAQ4380_CHANNEL(0, bits, diff, sign), \
- ADAQ4380_CHANNEL(1, bits, diff, sign), \
- ADAQ4380_CHANNEL(2, bits, diff, sign), \
- ADAQ4380_CHANNEL(3, bits, diff, sign), \
- IIO_CHAN_SOFT_TIMESTAMP(4), \
+#define DEFINE_ADAQ4380_4_OFFLOAD_CHANNEL(name, bits, diff, sign) \
+static const struct iio_chan_spec name[] = { \
+ AD7380_OFFLOAD_CHANNEL(0, bits, diff, sign), \
+ AD7380_OFFLOAD_CHANNEL(1, bits, diff, sign), \
+ AD7380_OFFLOAD_CHANNEL(2, bits, diff, sign), \
+ AD7380_OFFLOAD_CHANNEL(3, bits, diff, sign), \
}
-#define DEFINE_AD7380_8_CHANNEL(name, bits, diff, sign) \
+#define DEFINE_AD7380_8_OFFLOAD_CHANNEL(name, bits, diff, sign) \
static const struct iio_chan_spec name[] = { \
- AD7380_CHANNEL(0, bits, diff, sign), \
- AD7380_CHANNEL(1, bits, diff, sign), \
- AD7380_CHANNEL(2, bits, diff, sign), \
- AD7380_CHANNEL(3, bits, diff, sign), \
- AD7380_CHANNEL(4, bits, diff, sign), \
- AD7380_CHANNEL(5, bits, diff, sign), \
- AD7380_CHANNEL(6, bits, diff, sign), \
- AD7380_CHANNEL(7, bits, diff, sign), \
- IIO_CHAN_SOFT_TIMESTAMP(8), \
+ AD7380_OFFLOAD_CHANNEL(0, bits, diff, sign), \
+ AD7380_OFFLOAD_CHANNEL(1, bits, diff, sign), \
+ AD7380_OFFLOAD_CHANNEL(2, bits, diff, sign), \
+ AD7380_OFFLOAD_CHANNEL(3, bits, diff, sign), \
+ AD7380_OFFLOAD_CHANNEL(4, bits, diff, sign), \
+ AD7380_OFFLOAD_CHANNEL(5, bits, diff, sign), \
+ AD7380_OFFLOAD_CHANNEL(6, bits, diff, sign), \
+ AD7380_OFFLOAD_CHANNEL(7, bits, diff, sign), \
}
/* fully differential */
@@ -266,6 +461,7 @@ DEFINE_AD7380_2_CHANNEL(ad7381_channels, 14, 1, s);
DEFINE_AD7380_4_CHANNEL(ad7380_4_channels, 16, 1, s);
DEFINE_AD7380_4_CHANNEL(ad7381_4_channels, 14, 1, s);
DEFINE_ADAQ4380_4_CHANNEL(adaq4380_4_channels, 16, 1, s);
+DEFINE_ADAQ4380_4_CHANNEL(adaq4381_4_channels, 14, 1, s);
/* pseudo differential */
DEFINE_AD7380_2_CHANNEL(ad7383_channels, 16, 0, s);
DEFINE_AD7380_2_CHANNEL(ad7384_channels, 14, 0, s);
@@ -280,6 +476,28 @@ DEFINE_AD7380_8_CHANNEL(ad7386_4_channels, 16, 0, u);
DEFINE_AD7380_8_CHANNEL(ad7387_4_channels, 14, 0, u);
DEFINE_AD7380_8_CHANNEL(ad7388_4_channels, 12, 0, u);
+/* offload channels */
+DEFINE_AD7380_2_OFFLOAD_CHANNEL(ad7380_offload_channels, 16, 1, s);
+DEFINE_AD7380_2_OFFLOAD_CHANNEL(ad7381_offload_channels, 14, 1, s);
+DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7380_4_offload_channels, 16, 1, s);
+DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7381_4_offload_channels, 14, 1, s);
+DEFINE_ADAQ4380_4_OFFLOAD_CHANNEL(adaq4380_4_offload_channels, 16, 1, s);
+DEFINE_ADAQ4380_4_OFFLOAD_CHANNEL(adaq4381_4_offload_channels, 14, 1, s);
+
+/* pseudo differential */
+DEFINE_AD7380_2_OFFLOAD_CHANNEL(ad7383_offload_channels, 16, 0, s);
+DEFINE_AD7380_2_OFFLOAD_CHANNEL(ad7384_offload_channels, 14, 0, s);
+DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7383_4_offload_channels, 16, 0, s);
+DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7384_4_offload_channels, 14, 0, s);
+
+/* Single ended */
+DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7386_offload_channels, 16, 0, u);
+DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7387_offload_channels, 14, 0, u);
+DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7388_offload_channels, 12, 0, u);
+DEFINE_AD7380_8_OFFLOAD_CHANNEL(ad7386_4_offload_channels, 16, 0, u);
+DEFINE_AD7380_8_OFFLOAD_CHANNEL(ad7387_4_offload_channels, 14, 0, u);
+DEFINE_AD7380_8_OFFLOAD_CHANNEL(ad7388_4_offload_channels, 12, 0, u);
+
static const char * const ad7380_supplies[] = {
"vcc", "vlogic",
};
@@ -386,28 +604,33 @@ static const int ad7380_gains[] = {
static const struct ad7380_chip_info ad7380_chip_info = {
.name = "ad7380",
.channels = ad7380_channels,
+ .offload_channels = ad7380_offload_channels,
.num_channels = ARRAY_SIZE(ad7380_channels),
.num_simult_channels = 2,
.supplies = ad7380_supplies,
.num_supplies = ARRAY_SIZE(ad7380_supplies),
.available_scan_masks = ad7380_2_channel_scan_masks,
.timing_specs = &ad7380_timing,
+ .max_conversion_rate_hz = 4 * MEGA,
};
static const struct ad7380_chip_info ad7381_chip_info = {
.name = "ad7381",
.channels = ad7381_channels,
+ .offload_channels = ad7381_offload_channels,
.num_channels = ARRAY_SIZE(ad7381_channels),
.num_simult_channels = 2,
.supplies = ad7380_supplies,
.num_supplies = ARRAY_SIZE(ad7380_supplies),
.available_scan_masks = ad7380_2_channel_scan_masks,
.timing_specs = &ad7380_timing,
+ .max_conversion_rate_hz = 4 * MEGA,
};
static const struct ad7380_chip_info ad7383_chip_info = {
.name = "ad7383",
.channels = ad7383_channels,
+ .offload_channels = ad7383_offload_channels,
.num_channels = ARRAY_SIZE(ad7383_channels),
.num_simult_channels = 2,
.supplies = ad7380_supplies,
@@ -416,11 +639,13 @@ static const struct ad7380_chip_info ad7383_chip_info = {
.num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies),
.available_scan_masks = ad7380_2_channel_scan_masks,
.timing_specs = &ad7380_timing,
+ .max_conversion_rate_hz = 4 * MEGA,
};
static const struct ad7380_chip_info ad7384_chip_info = {
.name = "ad7384",
.channels = ad7384_channels,
+ .offload_channels = ad7384_offload_channels,
.num_channels = ARRAY_SIZE(ad7384_channels),
.num_simult_channels = 2,
.supplies = ad7380_supplies,
@@ -429,11 +654,13 @@ static const struct ad7380_chip_info ad7384_chip_info = {
.num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies),
.available_scan_masks = ad7380_2_channel_scan_masks,
.timing_specs = &ad7380_timing,
+ .max_conversion_rate_hz = 4 * MEGA,
};
static const struct ad7380_chip_info ad7386_chip_info = {
.name = "ad7386",
.channels = ad7386_channels,
+ .offload_channels = ad7386_offload_channels,
.num_channels = ARRAY_SIZE(ad7386_channels),
.num_simult_channels = 2,
.supplies = ad7380_supplies,
@@ -441,11 +668,13 @@ static const struct ad7380_chip_info ad7386_chip_info = {
.has_mux = true,
.available_scan_masks = ad7380_2x2_channel_scan_masks,
.timing_specs = &ad7380_timing,
+ .max_conversion_rate_hz = 4 * MEGA,
};
static const struct ad7380_chip_info ad7387_chip_info = {
.name = "ad7387",
.channels = ad7387_channels,
+ .offload_channels = ad7387_offload_channels,
.num_channels = ARRAY_SIZE(ad7387_channels),
.num_simult_channels = 2,
.supplies = ad7380_supplies,
@@ -453,11 +682,13 @@ static const struct ad7380_chip_info ad7387_chip_info = {
.has_mux = true,
.available_scan_masks = ad7380_2x2_channel_scan_masks,
.timing_specs = &ad7380_timing,
+ .max_conversion_rate_hz = 4 * MEGA,
};
static const struct ad7380_chip_info ad7388_chip_info = {
.name = "ad7388",
.channels = ad7388_channels,
+ .offload_channels = ad7388_offload_channels,
.num_channels = ARRAY_SIZE(ad7388_channels),
.num_simult_channels = 2,
.supplies = ad7380_supplies,
@@ -465,11 +696,13 @@ static const struct ad7380_chip_info ad7388_chip_info = {
.has_mux = true,
.available_scan_masks = ad7380_2x2_channel_scan_masks,
.timing_specs = &ad7380_timing,
+ .max_conversion_rate_hz = 4 * MEGA,
};
static const struct ad7380_chip_info ad7380_4_chip_info = {
.name = "ad7380-4",
.channels = ad7380_4_channels,
+ .offload_channels = ad7380_4_offload_channels,
.num_channels = ARRAY_SIZE(ad7380_4_channels),
.num_simult_channels = 4,
.supplies = ad7380_supplies,
@@ -477,22 +710,26 @@ static const struct ad7380_chip_info ad7380_4_chip_info = {
.external_ref_only = true,
.available_scan_masks = ad7380_4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
+ .max_conversion_rate_hz = 4 * MEGA,
};
static const struct ad7380_chip_info ad7381_4_chip_info = {
.name = "ad7381-4",
.channels = ad7381_4_channels,
+ .offload_channels = ad7381_4_offload_channels,
.num_channels = ARRAY_SIZE(ad7381_4_channels),
.num_simult_channels = 4,
.supplies = ad7380_supplies,
.num_supplies = ARRAY_SIZE(ad7380_supplies),
.available_scan_masks = ad7380_4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
+ .max_conversion_rate_hz = 4 * MEGA,
};
static const struct ad7380_chip_info ad7383_4_chip_info = {
.name = "ad7383-4",
.channels = ad7383_4_channels,
+ .offload_channels = ad7383_4_offload_channels,
.num_channels = ARRAY_SIZE(ad7383_4_channels),
.num_simult_channels = 4,
.supplies = ad7380_supplies,
@@ -501,11 +738,13 @@ static const struct ad7380_chip_info ad7383_4_chip_info = {
.num_vcm_supplies = ARRAY_SIZE(ad7380_4_channel_vcm_supplies),
.available_scan_masks = ad7380_4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
+ .max_conversion_rate_hz = 4 * MEGA,
};
static const struct ad7380_chip_info ad7384_4_chip_info = {
.name = "ad7384-4",
.channels = ad7384_4_channels,
+ .offload_channels = ad7384_4_offload_channels,
.num_channels = ARRAY_SIZE(ad7384_4_channels),
.num_simult_channels = 4,
.supplies = ad7380_supplies,
@@ -514,11 +753,13 @@ static const struct ad7380_chip_info ad7384_4_chip_info = {
.num_vcm_supplies = ARRAY_SIZE(ad7380_4_channel_vcm_supplies),
.available_scan_masks = ad7380_4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
+ .max_conversion_rate_hz = 4 * MEGA,
};
static const struct ad7380_chip_info ad7386_4_chip_info = {
.name = "ad7386-4",
.channels = ad7386_4_channels,
+ .offload_channels = ad7386_4_offload_channels,
.num_channels = ARRAY_SIZE(ad7386_4_channels),
.num_simult_channels = 4,
.supplies = ad7380_supplies,
@@ -526,11 +767,13 @@ static const struct ad7380_chip_info ad7386_4_chip_info = {
.has_mux = true,
.available_scan_masks = ad7380_2x4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
+ .max_conversion_rate_hz = 4 * MEGA,
};
static const struct ad7380_chip_info ad7387_4_chip_info = {
.name = "ad7387-4",
.channels = ad7387_4_channels,
+ .offload_channels = ad7387_4_offload_channels,
.num_channels = ARRAY_SIZE(ad7387_4_channels),
.num_simult_channels = 4,
.supplies = ad7380_supplies,
@@ -538,11 +781,13 @@ static const struct ad7380_chip_info ad7387_4_chip_info = {
.has_mux = true,
.available_scan_masks = ad7380_2x4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
+ .max_conversion_rate_hz = 4 * MEGA,
};
static const struct ad7380_chip_info ad7388_4_chip_info = {
.name = "ad7388-4",
.channels = ad7388_4_channels,
+ .offload_channels = ad7388_4_offload_channels,
.num_channels = ARRAY_SIZE(ad7388_4_channels),
.num_simult_channels = 4,
.supplies = ad7380_supplies,
@@ -550,11 +795,13 @@ static const struct ad7380_chip_info ad7388_4_chip_info = {
.has_mux = true,
.available_scan_masks = ad7380_2x4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
+ .max_conversion_rate_hz = 4 * MEGA,
};
static const struct ad7380_chip_info adaq4370_4_chip_info = {
.name = "adaq4370-4",
.channels = adaq4380_4_channels,
+ .offload_channels = adaq4380_4_offload_channels,
.num_channels = ARRAY_SIZE(adaq4380_4_channels),
.num_simult_channels = 4,
.supplies = adaq4380_supplies,
@@ -563,11 +810,13 @@ static const struct ad7380_chip_info adaq4370_4_chip_info = {
.has_hardware_gain = true,
.available_scan_masks = ad7380_4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
+ .max_conversion_rate_hz = 2 * MEGA,
};
static const struct ad7380_chip_info adaq4380_4_chip_info = {
.name = "adaq4380-4",
.channels = adaq4380_4_channels,
+ .offload_channels = adaq4380_4_offload_channels,
.num_channels = ARRAY_SIZE(adaq4380_4_channels),
.num_simult_channels = 4,
.supplies = adaq4380_supplies,
@@ -576,13 +825,32 @@ static const struct ad7380_chip_info adaq4380_4_chip_info = {
.has_hardware_gain = true,
.available_scan_masks = ad7380_4_channel_scan_masks,
.timing_specs = &ad7380_4_timing,
+ .max_conversion_rate_hz = 4 * MEGA,
+};
+
+static const struct ad7380_chip_info adaq4381_4_chip_info = {
+ .name = "adaq4381-4",
+ .channels = adaq4381_4_channels,
+ .offload_channels = adaq4381_4_offload_channels,
+ .num_channels = ARRAY_SIZE(adaq4381_4_channels),
+ .num_simult_channels = 4,
+ .supplies = adaq4380_supplies,
+ .num_supplies = ARRAY_SIZE(adaq4380_supplies),
+ .adaq_internal_ref_only = true,
+ .has_hardware_gain = true,
+ .available_scan_masks = ad7380_4_channel_scan_masks,
+ .timing_specs = &ad7380_4_timing,
+};
+
+static const struct spi_offload_config ad7380_offload_config = {
+ .capability_flags = SPI_OFFLOAD_CAP_TRIGGER |
+ SPI_OFFLOAD_CAP_RX_STREAM_DMA,
};
struct ad7380_state {
const struct ad7380_chip_info *chip_info;
struct spi_device *spi;
struct regmap *regmap;
- unsigned int oversampling_ratio;
bool resolution_boost_enabled;
unsigned int ch;
bool seq;
@@ -594,6 +862,13 @@ struct ad7380_state {
struct spi_message normal_msg;
struct spi_transfer seq_xfer[4];
struct spi_message seq_msg;
+ struct spi_transfer offload_xfer;
+ struct spi_message offload_msg;
+ struct spi_offload *offload;
+ struct spi_offload_trigger *offload_trigger;
+ unsigned long offload_trigger_hz;
+
+ int sample_freq_range[3];
/*
* DMA (thus cache coherency maintenance) requires the transfer buffers
* to live in their own cache lines.
@@ -663,6 +938,20 @@ static int ad7380_regmap_reg_read(void *context, unsigned int reg,
return 0;
}
+static const struct reg_default ad7380_reg_defaults[] = {
+ { AD7380_REG_ADDR_ALERT_LOW_TH, 0x800 },
+ { AD7380_REG_ADDR_ALERT_HIGH_TH, 0x7FF },
+};
+
+static const struct regmap_range ad7380_volatile_reg_ranges[] = {
+ regmap_reg_range(AD7380_REG_ADDR_CONFIG2, AD7380_REG_ADDR_ALERT),
+};
+
+static const struct regmap_access_table ad7380_volatile_regs = {
+ .yes_ranges = ad7380_volatile_reg_ranges,
+ .n_yes_ranges = ARRAY_SIZE(ad7380_volatile_reg_ranges),
+};
+
static const struct regmap_config ad7380_regmap_config = {
.reg_bits = 3,
.val_bits = 12,
@@ -670,20 +959,59 @@ static const struct regmap_config ad7380_regmap_config = {
.reg_write = ad7380_regmap_reg_write,
.max_register = AD7380_REG_ADDR_ALERT_HIGH_TH,
.can_sleep = true,
+ .reg_defaults = ad7380_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ad7380_reg_defaults),
+ .volatile_table = &ad7380_volatile_regs,
+ .cache_type = REGCACHE_MAPLE,
};
static int ad7380_debugfs_reg_access(struct iio_dev *indio_dev, u32 reg,
u32 writeval, u32 *readval)
{
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- struct ad7380_state *st = iio_priv(indio_dev);
+ struct ad7380_state *st = iio_priv(indio_dev);
+ int ret;
- if (readval)
- return regmap_read(st->regmap, reg, readval);
- else
- return regmap_write(st->regmap, reg, writeval);
- }
- unreachable();
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ if (readval)
+ ret = regmap_read(st->regmap, reg, readval);
+ else
+ ret = regmap_write(st->regmap, reg, writeval);
+
+ iio_device_release_direct(indio_dev);
+
+ return ret;
+}
+
+/**
+ * ad7380_regval_to_osr - convert OSR register value to ratio
+ * @regval: register value to check
+ *
+ * Returns: the ratio corresponding to the OSR register. If regval is not in
+ * bound, return 1 (oversampling disabled)
+ *
+ */
+static int ad7380_regval_to_osr(unsigned int regval)
+{
+ if (regval >= ARRAY_SIZE(ad7380_oversampling_ratios))
+ return 1;
+
+ return ad7380_oversampling_ratios[regval];
+}
+
+static int ad7380_get_osr(struct ad7380_state *st, int *val)
+{
+ u32 tmp;
+ int ret;
+
+ ret = regmap_read(st->regmap, AD7380_REG_ADDR_CONFIG1, &tmp);
+ if (ret)
+ return ret;
+
+ *val = ad7380_regval_to_osr(FIELD_GET(AD7380_CONFIG1_OSR, tmp));
+
+ return 0;
}
/*
@@ -701,11 +1029,15 @@ static int ad7380_set_ch(struct ad7380_state *st, unsigned int ch)
.unit = SPI_DELAY_UNIT_NSECS,
}
};
- int ret;
+ int oversampling_ratio, ret;
if (st->ch == ch)
return 0;
+ ret = ad7380_get_osr(st, &oversampling_ratio);
+ if (ret)
+ return ret;
+
ret = regmap_update_bits(st->regmap,
AD7380_REG_ADDR_CONFIG1,
AD7380_CONFIG1_CH,
@@ -716,9 +1048,9 @@ static int ad7380_set_ch(struct ad7380_state *st, unsigned int ch)
st->ch = ch;
- if (st->oversampling_ratio > 1)
+ if (oversampling_ratio > 1)
xfer.delay.value = T_CONVERT_0_NS +
- T_CONVERT_X_NS * (st->oversampling_ratio - 1) *
+ T_CONVERT_X_NS * (oversampling_ratio - 1) *
st->chip_info->num_simult_channels / AD7380_NUM_SDO_LINES;
return spi_sync_transfer(st->spi, &xfer, 1);
@@ -729,20 +1061,25 @@ static int ad7380_set_ch(struct ad7380_state *st, unsigned int ch)
* @st: device instance specific state
* @scan_type: current scan type
*/
-static void ad7380_update_xfers(struct ad7380_state *st,
+static int ad7380_update_xfers(struct ad7380_state *st,
const struct iio_scan_type *scan_type)
{
struct spi_transfer *xfer = st->seq ? st->seq_xfer : st->normal_xfer;
unsigned int t_convert = T_CONVERT_NS;
+ int oversampling_ratio, ret;
/*
* In the case of oversampling, conversion time is higher than in normal
* mode. Technically T_CONVERT_X_NS is lower for some chips, but we use
* the maximum value for simplicity for now.
*/
- if (st->oversampling_ratio > 1)
+ ret = ad7380_get_osr(st, &oversampling_ratio);
+ if (ret)
+ return ret;
+
+ if (oversampling_ratio > 1)
t_convert = T_CONVERT_0_NS + T_CONVERT_X_NS *
- (st->oversampling_ratio - 1) *
+ (oversampling_ratio - 1) *
st->chip_info->num_simult_channels / AD7380_NUM_SDO_LINES;
if (st->seq) {
@@ -751,11 +1088,11 @@ static void ad7380_update_xfers(struct ad7380_state *st,
xfer[2].bits_per_word = xfer[3].bits_per_word =
scan_type->realbits;
xfer[2].len = xfer[3].len =
- BITS_TO_BYTES(scan_type->storagebits) *
+ AD7380_SPI_BYTES(scan_type) *
st->chip_info->num_simult_channels;
xfer[3].rx_buf = xfer[2].rx_buf + xfer[2].len;
/* Additional delay required here when oversampling is enabled */
- if (st->oversampling_ratio > 1)
+ if (oversampling_ratio > 1)
xfer[2].delay.value = t_convert;
else
xfer[2].delay.value = 0;
@@ -764,16 +1101,145 @@ static void ad7380_update_xfers(struct ad7380_state *st,
xfer[0].delay.value = t_convert;
xfer[0].delay.unit = SPI_DELAY_UNIT_NSECS;
xfer[1].bits_per_word = scan_type->realbits;
- xfer[1].len = BITS_TO_BYTES(scan_type->storagebits) *
+ xfer[1].len = AD7380_SPI_BYTES(scan_type) *
st->chip_info->num_simult_channels;
}
+
+ return 0;
}
+static int ad7380_set_sample_freq(struct ad7380_state *st, int val)
+{
+ struct spi_offload_trigger_config config = {
+ .type = SPI_OFFLOAD_TRIGGER_PERIODIC,
+ .periodic = {
+ .frequency_hz = val,
+ },
+ };
+ int ret;
+
+ ret = spi_offload_trigger_validate(st->offload_trigger, &config);
+ if (ret)
+ return ret;
+
+ st->offload_trigger_hz = config.periodic.frequency_hz;
+
+ return 0;
+}
+
+static int ad7380_init_offload_msg(struct ad7380_state *st,
+ struct iio_dev *indio_dev)
+{
+ struct spi_transfer *xfer = &st->offload_xfer;
+ struct device *dev = &st->spi->dev;
+ const struct iio_scan_type *scan_type;
+ int oversampling_ratio;
+ int ret;
+
+ scan_type = iio_get_current_scan_type(indio_dev,
+ &indio_dev->channels[0]);
+ if (IS_ERR(scan_type))
+ return PTR_ERR(scan_type);
+
+ if (st->chip_info->has_mux) {
+ int index;
+
+ ret = iio_active_scan_mask_index(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ index = ret;
+ if (index == AD7380_SCAN_MASK_SEQ) {
+ ret = regmap_set_bits(st->regmap, AD7380_REG_ADDR_CONFIG1,
+ AD7380_CONFIG1_SEQ);
+ if (ret)
+ return ret;
+
+ st->seq = true;
+ } else {
+ ret = ad7380_set_ch(st, index);
+ if (ret)
+ return ret;
+ }
+ }
+
+ ret = ad7380_get_osr(st, &oversampling_ratio);
+ if (ret)
+ return ret;
+
+ xfer->bits_per_word = scan_type->realbits;
+ xfer->offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
+ xfer->len = AD7380_SPI_BYTES(scan_type) * st->chip_info->num_simult_channels;
+
+ spi_message_init_with_transfers(&st->offload_msg, xfer, 1);
+ st->offload_msg.offload = st->offload;
+
+ ret = spi_optimize_message(st->spi, &st->offload_msg);
+ if (ret) {
+ dev_err(dev, "failed to prepare offload msg, err: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ad7380_offload_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ad7380_state *st = iio_priv(indio_dev);
+ struct spi_offload_trigger_config config = {
+ .type = SPI_OFFLOAD_TRIGGER_PERIODIC,
+ .periodic = {
+ .frequency_hz = st->offload_trigger_hz,
+ },
+ };
+ int ret;
+
+ ret = ad7380_init_offload_msg(st, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, &config);
+ if (ret)
+ spi_unoptimize_message(&st->offload_msg);
+
+ return ret;
+}
+
+static int ad7380_offload_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct ad7380_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (st->seq) {
+ ret = regmap_update_bits(st->regmap,
+ AD7380_REG_ADDR_CONFIG1,
+ AD7380_CONFIG1_SEQ,
+ FIELD_PREP(AD7380_CONFIG1_SEQ, 0));
+ if (ret)
+ return ret;
+
+ st->seq = false;
+ }
+
+ spi_offload_trigger_disable(st->offload, st->offload_trigger);
+
+ spi_unoptimize_message(&st->offload_msg);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ad7380_offload_buffer_setup_ops = {
+ .postenable = ad7380_offload_buffer_postenable,
+ .predisable = ad7380_offload_buffer_predisable,
+};
+
static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev)
{
struct ad7380_state *st = iio_priv(indio_dev);
const struct iio_scan_type *scan_type;
struct spi_message *msg = &st->normal_msg;
+ int ret;
/*
* Currently, we always read all channels at the same time. The scan_type
@@ -785,7 +1251,6 @@ static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev)
if (st->chip_info->has_mux) {
unsigned int index;
- int ret;
/*
* Depending on the requested scan_mask and current state,
@@ -816,7 +1281,9 @@ static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev)
}
- ad7380_update_xfers(st, scan_type);
+ ret = ad7380_update_xfers(st, scan_type);
+ if (ret)
+ return ret;
return spi_optimize_message(st->spi, msg);
}
@@ -889,13 +1356,15 @@ static int ad7380_read_direct(struct ad7380_state *st, unsigned int scan_index,
return ret;
}
- ad7380_update_xfers(st, scan_type);
+ ret = ad7380_update_xfers(st, scan_type);
+ if (ret)
+ return ret;
ret = spi_sync(st->spi, &st->normal_msg);
if (ret < 0)
return ret;
- if (scan_type->storagebits > 16) {
+ if (scan_type->realbits > 16) {
if (scan_type->sign == 's')
*val = sign_extend32(*(u32 *)(st->scan_data + 4 * index),
scan_type->realbits - 1);
@@ -920,6 +1389,7 @@ static int ad7380_read_raw(struct iio_dev *indio_dev,
{
struct ad7380_state *st = iio_priv(indio_dev);
const struct iio_scan_type *scan_type;
+ int ret;
scan_type = iio_get_current_scan_type(indio_dev, chan);
@@ -928,11 +1398,15 @@ static int ad7380_read_raw(struct iio_dev *indio_dev,
switch (info) {
case IIO_CHAN_INFO_RAW:
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- return ad7380_read_direct(st, chan->scan_index,
- scan_type, val);
- }
- unreachable();
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad7380_read_direct(st, chan->scan_index,
+ scan_type, val);
+
+ iio_device_release_direct(indio_dev);
+
+ return ret;
case IIO_CHAN_INFO_SCALE:
/*
* According to the datasheet, the LSB size is:
@@ -961,8 +1435,19 @@ static int ad7380_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
- *val = st->oversampling_ratio;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = ad7380_get_osr(st, val);
+
+ iio_device_release_direct(indio_dev);
+
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->offload_trigger_hz;
return IIO_VAL_INT;
default:
return -EINVAL;
@@ -974,6 +1459,8 @@ static int ad7380_read_avail(struct iio_dev *indio_dev,
const int **vals, int *type, int *length,
long mask)
{
+ struct ad7380_state *st = iio_priv(indio_dev);
+
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*vals = ad7380_oversampling_ratios;
@@ -981,6 +1468,10 @@ static int ad7380_read_avail(struct iio_dev *indio_dev,
*type = IIO_VAL_INT;
return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = st->sample_freq_range;
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_RANGE;
default:
return -EINVAL;
}
@@ -1008,47 +1499,61 @@ static int ad7380_osr_to_regval(int ratio)
return -EINVAL;
}
+static int ad7380_set_oversampling_ratio(struct ad7380_state *st, int val)
+{
+ int ret, osr, boost;
+
+ osr = ad7380_osr_to_regval(val);
+ if (osr < 0)
+ return osr;
+
+ /* always enable resolution boost when oversampling is enabled */
+ boost = osr > 0 ? 1 : 0;
+
+ ret = regmap_update_bits(st->regmap,
+ AD7380_REG_ADDR_CONFIG1,
+ AD7380_CONFIG1_OSR | AD7380_CONFIG1_RES,
+ FIELD_PREP(AD7380_CONFIG1_OSR, osr) |
+ FIELD_PREP(AD7380_CONFIG1_RES, boost));
+
+ if (ret)
+ return ret;
+
+ st->resolution_boost_enabled = boost;
+
+ /*
+ * Perform a soft reset. This will flush the oversampling
+ * block and FIFO but will maintain the content of the
+ * configurable registers.
+ */
+ ret = regmap_update_bits(st->regmap,
+ AD7380_REG_ADDR_CONFIG2,
+ AD7380_CONFIG2_RESET,
+ FIELD_PREP(AD7380_CONFIG2_RESET,
+ AD7380_CONFIG2_RESET_SOFT));
+ return ret;
+}
static int ad7380_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
struct ad7380_state *st = iio_priv(indio_dev);
- int ret, osr, boost;
+ int ret;
switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (val < 1)
+ return -EINVAL;
+ return ad7380_set_sample_freq(st, val);
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
- osr = ad7380_osr_to_regval(val);
- if (osr < 0)
- return osr;
-
- /* always enable resolution boost when oversampling is enabled */
- boost = osr > 0 ? 1 : 0;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- ret = regmap_update_bits(st->regmap,
- AD7380_REG_ADDR_CONFIG1,
- AD7380_CONFIG1_OSR | AD7380_CONFIG1_RES,
- FIELD_PREP(AD7380_CONFIG1_OSR, osr) |
- FIELD_PREP(AD7380_CONFIG1_RES, boost));
+ ret = ad7380_set_oversampling_ratio(st, val);
- if (ret)
- return ret;
+ iio_device_release_direct(indio_dev);
- st->oversampling_ratio = val;
- st->resolution_boost_enabled = boost;
-
- /*
- * Perform a soft reset. This will flush the oversampling
- * block and FIFO but will maintain the content of the
- * configurable registers.
- */
- return regmap_update_bits(st->regmap,
- AD7380_REG_ADDR_CONFIG2,
- AD7380_CONFIG2_RESET,
- FIELD_PREP(AD7380_CONFIG2_RESET,
- AD7380_CONFIG2_RESET_SOFT));
- }
- unreachable();
+ return ret;
default:
return -EINVAL;
}
@@ -1063,12 +1568,179 @@ static int ad7380_get_current_scan_type(const struct iio_dev *indio_dev,
: AD7380_SCAN_TYPE_NORMAL;
}
+static int ad7380_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct ad7380_state *st = iio_priv(indio_dev);
+ int tmp, ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = regmap_read(st->regmap, AD7380_REG_ADDR_CONFIG1, &tmp);
+
+ iio_device_release_direct(indio_dev);
+
+ if (ret)
+ return ret;
+
+ return FIELD_GET(AD7380_CONFIG1_ALERTEN, tmp);
+}
+
+static int ad7380_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ bool state)
+{
+ struct ad7380_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = regmap_update_bits(st->regmap,
+ AD7380_REG_ADDR_CONFIG1,
+ AD7380_CONFIG1_ALERTEN,
+ FIELD_PREP(AD7380_CONFIG1_ALERTEN, state));
+
+ iio_device_release_direct(indio_dev);
+
+ return ret;
+}
+
+static int ad7380_get_alert_th(struct ad7380_state *st,
+ enum iio_event_direction dir,
+ int *val)
+{
+ int ret, tmp;
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ ret = regmap_read(st->regmap,
+ AD7380_REG_ADDR_ALERT_HIGH_TH,
+ &tmp);
+ if (ret)
+ return ret;
+
+ *val = FIELD_GET(AD7380_ALERT_HIGH_TH, tmp);
+ return IIO_VAL_INT;
+ case IIO_EV_DIR_FALLING:
+ ret = regmap_read(st->regmap,
+ AD7380_REG_ADDR_ALERT_LOW_TH,
+ &tmp);
+ if (ret)
+ return ret;
+
+ *val = FIELD_GET(AD7380_ALERT_LOW_TH, tmp);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7380_read_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ struct ad7380_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad7380_get_alert_th(st, dir, val);
+
+ iio_device_release_direct(indio_dev);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7380_set_alert_th(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_direction dir,
+ int val)
+{
+ struct ad7380_state *st = iio_priv(indio_dev);
+ const struct iio_scan_type *scan_type;
+ u16 th;
+
+ /*
+ * According to the datasheet,
+ * AD7380_REG_ADDR_ALERT_HIGH_TH[11:0] are the 12 MSB of the
+ * 16-bits internal alert high register. LSB are set to 0xf.
+ * AD7380_REG_ADDR_ALERT_LOW_TH[11:0] are the 12 MSB of the
+ * 16 bits internal alert low register. LSB are set to 0x0.
+ *
+ * When alert is enabled the conversion from the adc is compared
+ * immediately to the alert high/low thresholds, before any
+ * oversampling. This means that the thresholds are the same for
+ * normal mode and oversampling mode.
+ */
+
+ /* Extract the 12 MSB of val */
+ scan_type = iio_get_current_scan_type(indio_dev, chan);
+ if (IS_ERR(scan_type))
+ return PTR_ERR(scan_type);
+
+ th = val >> (scan_type->realbits - 12);
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ return regmap_write(st->regmap,
+ AD7380_REG_ADDR_ALERT_HIGH_TH,
+ th);
+ case IIO_EV_DIR_FALLING:
+ return regmap_write(st->regmap,
+ AD7380_REG_ADDR_ALERT_LOW_TH,
+ th);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7380_write_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ int ret;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad7380_set_alert_th(indio_dev, chan, dir, val);
+
+ iio_device_release_direct(indio_dev);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
static const struct iio_info ad7380_info = {
.read_raw = &ad7380_read_raw,
.read_avail = &ad7380_read_avail,
.write_raw = &ad7380_write_raw,
.get_current_scan_type = &ad7380_get_current_scan_type,
.debugfs_reg_access = &ad7380_debugfs_reg_access,
+ .read_event_config = &ad7380_read_event_config,
+ .write_event_config = &ad7380_write_event_config,
+ .read_event_value = &ad7380_read_event_value,
+ .write_event_value = &ad7380_write_event_value,
};
static int ad7380_init(struct ad7380_state *st, bool external_ref_en)
@@ -1092,7 +1764,6 @@ static int ad7380_init(struct ad7380_state *st, bool external_ref_en)
}
/* This is the default value after reset. */
- st->oversampling_ratio = 1;
st->ch = 0;
st->seq = false;
@@ -1103,6 +1774,53 @@ static int ad7380_init(struct ad7380_state *st, bool external_ref_en)
AD7380_NUM_SDO_LINES));
}
+static int ad7380_probe_spi_offload(struct iio_dev *indio_dev,
+ struct ad7380_state *st)
+{
+ struct spi_device *spi = st->spi;
+ struct device *dev = &spi->dev;
+ struct dma_chan *rx_dma;
+ int sample_rate, ret;
+
+ indio_dev->setup_ops = &ad7380_offload_buffer_setup_ops;
+ indio_dev->channels = st->chip_info->offload_channels;
+ /* Just removing the timestamp channel. */
+ indio_dev->num_channels--;
+
+ st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload,
+ SPI_OFFLOAD_TRIGGER_PERIODIC);
+ if (IS_ERR(st->offload_trigger))
+ return dev_err_probe(dev, PTR_ERR(st->offload_trigger),
+ "failed to get offload trigger\n");
+
+ sample_rate = st->chip_info->max_conversion_rate_hz *
+ AD7380_NUM_SDO_LINES / st->chip_info->num_simult_channels;
+
+ st->sample_freq_range[0] = 1; /* min */
+ st->sample_freq_range[1] = 1; /* step */
+ st->sample_freq_range[2] = sample_rate; /* max */
+
+ /*
+ * Starting with a quite low frequency, to allow oversampling x32,
+ * user is then reponsible to adjust the frequency for the specific case.
+ */
+ ret = ad7380_set_sample_freq(st, sample_rate / 32);
+ if (ret)
+ return ret;
+
+ rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload);
+ if (IS_ERR(rx_dma))
+ return dev_err_probe(dev, PTR_ERR(rx_dma),
+ "failed to get offload RX DMA\n");
+
+ ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev,
+ rx_dma, IIO_BUFFER_DIRECTION_IN);
+ if (ret)
+ return dev_err_probe(dev, ret, "cannot setup dma buffer\n");
+
+ return 0;
+}
+
static int ad7380_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
@@ -1274,12 +1992,24 @@ static int ad7380_probe(struct spi_device *spi)
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->available_scan_masks = st->chip_info->available_scan_masks;
- ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
- iio_pollfunc_store_time,
- ad7380_trigger_handler,
- &ad7380_buffer_setup_ops);
- if (ret)
- return ret;
+ st->offload = devm_spi_offload_get(dev, spi, &ad7380_offload_config);
+ ret = PTR_ERR_OR_ZERO(st->offload);
+ if (ret && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "failed to get offload\n");
+
+ /* If no SPI offload, fall back to low speed usage. */
+ if (ret == -ENODEV) {
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ ad7380_trigger_handler,
+ &ad7380_buffer_setup_ops);
+ if (ret)
+ return ret;
+ } else {
+ ret = ad7380_probe_spi_offload(indio_dev, st);
+ if (ret)
+ return ret;
+ }
ret = ad7380_init(st, external_ref_en);
if (ret)
@@ -1305,6 +2035,7 @@ static const struct of_device_id ad7380_of_match_table[] = {
{ .compatible = "adi,ad7388-4", .data = &ad7388_4_chip_info },
{ .compatible = "adi,adaq4370-4", .data = &adaq4370_4_chip_info },
{ .compatible = "adi,adaq4380-4", .data = &adaq4380_4_chip_info },
+ { .compatible = "adi,adaq4381-4", .data = &adaq4381_4_chip_info },
{ }
};
@@ -1325,6 +2056,7 @@ static const struct spi_device_id ad7380_id_table[] = {
{ "ad7388-4", (kernel_ulong_t)&ad7388_4_chip_info },
{ "adaq4370-4", (kernel_ulong_t)&adaq4370_4_chip_info },
{ "adaq4380-4", (kernel_ulong_t)&adaq4380_4_chip_info },
+ { "adaq4381-4", (kernel_ulong_t)&adaq4381_4_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7380_id_table);
@@ -1342,3 +2074,4 @@ module_spi_driver(ad7380_driver);
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD738x ADC driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER");
diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c
index aeb8e383fe71..37b0515cf4fc 100644
--- a/drivers/iio/adc/ad7476.c
+++ b/drivers/iio/adc/ad7476.c
@@ -138,11 +138,10 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = ad7476_scan_direct(st);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
index d39354afd539..1a314fddd7eb 100644
--- a/drivers/iio/adc/ad7606.c
+++ b/drivers/iio/adc/ad7606.c
@@ -5,6 +5,7 @@
* Copyright 2011 Analog Devices Inc.
*/
+#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -85,6 +86,10 @@ static const unsigned int ad7606_oversampling_avail[7] = {
1, 2, 4, 8, 16, 32, 64,
};
+static const unsigned int ad7606b_oversampling_avail[9] = {
+ 1, 2, 4, 8, 16, 32, 64, 128, 256,
+};
+
static const unsigned int ad7616_oversampling_avail[8] = {
1, 2, 4, 8, 16, 32, 64, 128,
};
@@ -187,6 +192,8 @@ static int ad7608_chan_scale_setup(struct iio_dev *indio_dev,
struct iio_chan_spec *chan, int ch);
static int ad7609_chan_scale_setup(struct iio_dev *indio_dev,
struct iio_chan_spec *chan, int ch);
+static int ad7616_sw_mode_setup(struct iio_dev *indio_dev);
+static int ad7606b_sw_mode_setup(struct iio_dev *indio_dev);
const struct ad7606_chip_info ad7605_4_info = {
.channels = ad7605_channels,
@@ -239,6 +246,7 @@ const struct ad7606_chip_info ad7606b_info = {
.oversampling_avail = ad7606_oversampling_avail,
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
.scale_setup_cb = ad7606_16bit_chan_scale_setup,
+ .sw_setup_cb = ad7606b_sw_mode_setup,
};
EXPORT_SYMBOL_NS_GPL(ad7606b_info, "IIO_AD7606");
@@ -250,6 +258,7 @@ const struct ad7606_chip_info ad7606c_16_info = {
.oversampling_avail = ad7606_oversampling_avail,
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
.scale_setup_cb = ad7606c_16bit_chan_scale_setup,
+ .sw_setup_cb = ad7606b_sw_mode_setup,
};
EXPORT_SYMBOL_NS_GPL(ad7606c_16_info, "IIO_AD7606");
@@ -294,6 +303,7 @@ const struct ad7606_chip_info ad7606c_18_info = {
.oversampling_avail = ad7606_oversampling_avail,
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
.scale_setup_cb = ad7606c_18bit_chan_scale_setup,
+ .sw_setup_cb = ad7606b_sw_mode_setup,
};
EXPORT_SYMBOL_NS_GPL(ad7606c_18_info, "IIO_AD7606");
@@ -307,6 +317,7 @@ const struct ad7606_chip_info ad7616_info = {
.oversampling_num = ARRAY_SIZE(ad7616_oversampling_avail),
.os_req_reset = true,
.scale_setup_cb = ad7606_16bit_chan_scale_setup,
+ .sw_setup_cb = ad7616_sw_mode_setup,
};
EXPORT_SYMBOL_NS_GPL(ad7616_info, "IIO_AD7606");
@@ -752,13 +763,13 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- ret = ad7606_scan_direct(indio_dev, chan->address, val);
- if (ret < 0)
- return ret;
- return IIO_VAL_INT;
- }
- unreachable();
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = ad7606_scan_direct(indio_dev, chan->address, val);
+ iio_device_release_direct(indio_dev);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
if (st->sw_mode_en)
ch = chan->address;
@@ -818,8 +829,7 @@ static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val)
values[0] = val & GENMASK(2, 0);
- gpiod_set_array_value(st->gpio_os->ndescs, st->gpio_os->desc,
- st->gpio_os->info, values);
+ gpiod_multi_set_value_cansleep(st->gpio_os, values);
/* AD7616 requires a reset to update value */
if (st->chip_info->os_req_reset)
@@ -852,7 +862,11 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
}
val = (val * MICRO) + val2;
i = find_closest(val, scale_avail_uv, cs->num_scales);
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = st->write_scale(indio_dev, ch, i + cs->reg_offset);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
cs->range = i;
@@ -863,7 +877,11 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
i = find_closest(val, st->oversampling_avail,
st->num_os_ratios);
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = st->write_os(indio_dev, i);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
st->oversampling = st->oversampling_avail[i];
@@ -1138,16 +1156,117 @@ static const struct iio_trigger_ops ad7606_trigger_ops = {
.validate_device = iio_trigger_validate_own_device,
};
-static int ad7606_sw_mode_setup(struct iio_dev *indio_dev)
+static int ad7606_write_mask(struct ad7606_state *st, unsigned int addr,
+ unsigned long mask, unsigned int val)
+{
+ int readval;
+
+ readval = st->bops->reg_read(st, addr);
+ if (readval < 0)
+ return readval;
+
+ readval &= ~mask;
+ readval |= val;
+
+ return st->bops->reg_write(st, addr, readval);
+}
+
+static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
{
struct ad7606_state *st = iio_priv(indio_dev);
+ unsigned int ch_addr, mode, ch_index;
- st->sw_mode_en = st->bops->sw_mode_config &&
- device_property_present(st->dev, "adi,sw-mode");
- if (!st->sw_mode_en)
- return 0;
+ /*
+ * Ad7616 has 16 channels divided in group A and group B.
+ * The range of channels from A are stored in registers with address 4
+ * while channels from B are stored in register with address 6.
+ * The last bit from channels determines if it is from group A or B
+ * because the order of channels in iio is 0A, 0B, 1A, 1B...
+ */
+ ch_index = ch >> 1;
+
+ ch_addr = AD7616_RANGE_CH_ADDR(ch_index);
+
+ if ((ch & 0x1) == 0) /* channel A */
+ ch_addr += AD7616_RANGE_CH_A_ADDR_OFF;
+ else /* channel B */
+ ch_addr += AD7616_RANGE_CH_B_ADDR_OFF;
+
+ /* 0b01 for 2.5v, 0b10 for 5v and 0b11 for 10v */
+ mode = AD7616_RANGE_CH_MODE(ch_index, ((val + 1) & 0b11));
+
+ return ad7606_write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch_index),
+ mode);
+}
+
+static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_write_mask(st, AD7616_CONFIGURATION_REGISTER,
+ AD7616_OS_MASK, val << 2);
+}
+
+static int ad7606_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_write_mask(st, AD7606_RANGE_CH_ADDR(ch),
+ AD7606_RANGE_CH_MSK(ch),
+ AD7606_RANGE_CH_MODE(ch, val));
+}
+
+static int ad7606_write_os_sw(struct iio_dev *indio_dev, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return st->bops->reg_write(st, AD7606_OS_MODE, val);
+}
+
+static int ad7616_sw_mode_setup(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ int ret;
+
+ /*
+ * Scale can be configured individually for each channel
+ * in software mode.
+ */
+
+ st->write_scale = ad7616_write_scale_sw;
+ st->write_os = &ad7616_write_os_sw;
+
+ ret = st->bops->sw_mode_config(indio_dev);
+ if (ret)
+ return ret;
- indio_dev->info = &ad7606_info_sw_mode;
+ /* Activate Burst mode and SEQEN MODE */
+ return ad7606_write_mask(st, AD7616_CONFIGURATION_REGISTER,
+ AD7616_BURST_MODE | AD7616_SEQEN_MODE,
+ AD7616_BURST_MODE | AD7616_SEQEN_MODE);
+}
+
+static int ad7606b_sw_mode_setup(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ DECLARE_BITMAP(os, 3);
+
+ bitmap_fill(os, 3);
+ /*
+ * Software mode is enabled when all three oversampling
+ * pins are set to high. If oversampling gpios are defined
+ * in the device tree, then they need to be set to high,
+ * otherwise, they must be hardwired to VDD
+ */
+ if (st->gpio_os)
+ gpiod_multi_set_value_cansleep(st->gpio_os, os);
+
+ /* OS of 128 and 256 are available only in software mode */
+ st->oversampling_avail = ad7606b_oversampling_avail;
+ st->num_os_ratios = ARRAY_SIZE(ad7606b_oversampling_avail);
+
+ st->write_scale = ad7606_write_scale_sw;
+ st->write_os = &ad7606_write_os_sw;
return st->bops->sw_mode_config(indio_dev);
}
@@ -1246,17 +1365,6 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
return -ERESTARTSYS;
}
- st->write_scale = ad7606_write_scale_hw;
- st->write_os = ad7606_write_os_hw;
-
- ret = ad7606_sw_mode_setup(indio_dev);
- if (ret)
- return ret;
-
- ret = ad7606_chan_scales_setup(indio_dev);
- if (ret)
- return ret;
-
/* If convst pin is not defined, setup PWM. */
if (!st->gpio_convst) {
st->cnvst_pwm = devm_pwm_get(dev, NULL);
@@ -1334,6 +1442,20 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
return ret;
}
+ st->write_scale = ad7606_write_scale_hw;
+ st->write_os = ad7606_write_os_hw;
+
+ st->sw_mode_en = st->chip_info->sw_setup_cb &&
+ device_property_present(st->dev, "adi,sw-mode");
+ if (st->sw_mode_en) {
+ indio_dev->info = &ad7606_info_sw_mode;
+ st->chip_info->sw_setup_cb(indio_dev);
+ }
+
+ ret = ad7606_chan_scales_setup(indio_dev);
+ if (ret)
+ return ret;
+
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_NS_GPL(ad7606_probe, "IIO_AD7606");
diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h
index 8778ffe515b3..71a30525eaab 100644
--- a/drivers/iio/adc/ad7606.h
+++ b/drivers/iio/adc/ad7606.h
@@ -10,37 +10,49 @@
#define AD760X_MAX_CHANNELS 16
-#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all, bits) { \
+#define AD7616_CONFIGURATION_REGISTER 0x02
+#define AD7616_OS_MASK GENMASK(4, 2)
+#define AD7616_BURST_MODE BIT(6)
+#define AD7616_SEQEN_MODE BIT(5)
+#define AD7616_RANGE_CH_A_ADDR_OFF 0x04
+#define AD7616_RANGE_CH_B_ADDR_OFF 0x06
+/*
+ * Range of channels from a group are stored in 2 registers.
+ * 0, 1, 2, 3 in a register followed by 4, 5, 6, 7 in second register.
+ * For channels from second group(8-15) the order is the same, only with
+ * an offset of 2 for register address.
+ */
+#define AD7616_RANGE_CH_ADDR(ch) ((ch) >> 2)
+/* The range of the channel is stored in 2 bits */
+#define AD7616_RANGE_CH_MSK(ch) (0b11 << (((ch) & 0b11) * 2))
+#define AD7616_RANGE_CH_MODE(ch, mode) ((mode) << ((((ch) & 0b11)) * 2))
+
+#define AD7606_CONFIGURATION_REGISTER 0x02
+#define AD7606_SINGLE_DOUT 0x00
+
+/*
+ * Range for AD7606B channels are stored in registers starting with address 0x3.
+ * Each register stores range for 2 channels(4 bits per channel).
+ */
+#define AD7606_RANGE_CH_MSK(ch) (GENMASK(3, 0) << (4 * ((ch) & 0x1)))
+#define AD7606_RANGE_CH_MODE(ch, mode) \
+ ((GENMASK(3, 0) & (mode)) << (4 * ((ch) & 0x1)))
+#define AD7606_RANGE_CH_ADDR(ch) (0x03 + ((ch) >> 1))
+#define AD7606_OS_MODE 0x08
+
+#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all, \
+ mask_sep_avail, mask_all_avail, bits) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = num, \
.address = num, \
.info_mask_separate = mask_sep, \
+ .info_mask_separate_available = \
+ mask_sep_avail, \
.info_mask_shared_by_type = mask_type, \
.info_mask_shared_by_all = mask_all, \
- .scan_index = num, \
- .scan_type = { \
- .sign = 's', \
- .realbits = (bits), \
- .storagebits = (bits) > 16 ? 32 : 16, \
- .endianness = IIO_CPU, \
- }, \
-}
-
-#define AD7606_SW_CHANNEL(num, bits) { \
- .type = IIO_VOLTAGE, \
- .indexed = 1, \
- .channel = num, \
- .address = num, \
- .info_mask_separate = \
- BIT(IIO_CHAN_INFO_RAW) | \
- BIT(IIO_CHAN_INFO_SCALE), \
- .info_mask_separate_available = \
- BIT(IIO_CHAN_INFO_SCALE), \
- .info_mask_shared_by_all = \
- BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.info_mask_shared_by_all_available = \
- BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ mask_all_avail, \
.scan_index = num, \
.scan_type = { \
.sign = 's', \
@@ -50,14 +62,30 @@
}, \
}
+#define AD7606_SW_CHANNEL(num, bits) \
+ AD760X_CHANNEL(num, \
+ /* mask separate */ \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ /* mask type */ \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ /* mask all */ \
+ 0, \
+ /* mask separate available */ \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ /* mask all available */ \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ bits)
+
#define AD7605_CHANNEL(num) \
AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW), \
- BIT(IIO_CHAN_INFO_SCALE), 0, 16)
+ BIT(IIO_CHAN_INFO_SCALE), 0, 0, 0, 16)
#define AD7606_CHANNEL(num, bits) \
AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW), \
BIT(IIO_CHAN_INFO_SCALE), \
- BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), bits)
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ 0, 0, bits)
#define AD7616_CHANNEL(num) AD7606_SW_CHANNEL(num, 16)
@@ -65,12 +93,29 @@
AD760X_CHANNEL(num, 0, \
BIT(IIO_CHAN_INFO_SCALE), \
BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
- BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), 16)
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ 0, 0, 16)
+
+#define AD7606_BI_SW_CHANNEL(num) \
+ AD760X_CHANNEL(num, \
+ /* mask separate */ \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ /* mask type */ \
+ 0, \
+ /* mask all */ \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ /* mask separate available */ \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ /* mask all available */ \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ 16)
struct ad7606_state;
typedef int (*ad7606_scale_setup_cb_t)(struct iio_dev *indio_dev,
struct iio_chan_spec *chan, int ch);
+typedef int (*ad7606_sw_setup_cb_t)(struct iio_dev *indio_dev);
/**
* struct ad7606_chip_info - chip specific information
@@ -80,6 +125,7 @@ typedef int (*ad7606_scale_setup_cb_t)(struct iio_dev *indio_dev,
* @num_channels: number of channels
* @num_adc_channels the number of channels the ADC actually inputs.
* @scale_setup_cb: callback to setup the scales for each channel
+ * @sw_setup_cb: callback to setup the software mode if available.
* @oversampling_avail pointer to the array which stores the available
* oversampling ratios.
* @oversampling_num number of elements stored in oversampling_avail array
@@ -94,6 +140,7 @@ struct ad7606_chip_info {
unsigned int num_adc_channels;
unsigned int num_channels;
ad7606_scale_setup_cb_t scale_setup_cb;
+ ad7606_sw_setup_cb_t sw_setup_cb;
const unsigned int *oversampling_avail;
unsigned int oversampling_num;
bool os_req_reset;
@@ -206,10 +253,6 @@ struct ad7606_bus_ops {
int (*reg_write)(struct ad7606_state *st,
unsigned int addr,
unsigned int val);
- int (*write_mask)(struct ad7606_state *st,
- unsigned int addr,
- unsigned long mask,
- unsigned int val);
int (*update_scan_mode)(struct iio_dev *indio_dev, const unsigned long *scan_mask);
u16 (*rd_wr_cmd)(int addr, char isWriteOp);
};
diff --git a/drivers/iio/adc/ad7606_bus_iface.h b/drivers/iio/adc/ad7606_bus_iface.h
new file mode 100644
index 000000000000..f2c979a9b7f3
--- /dev/null
+++ b/drivers/iio/adc/ad7606_bus_iface.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2010-2024 Analog Devices Inc.
+ * Copyright (c) 2025 Baylibre, SAS
+ */
+#ifndef __LINUX_PLATFORM_DATA_AD7606_H__
+#define __LINUX_PLATFORM_DATA_AD7606_H__
+
+struct iio_backend;
+
+struct ad7606_platform_data {
+ int (*bus_reg_read)(struct iio_backend *back, u32 reg, u32 *val);
+ int (*bus_reg_write)(struct iio_backend *back, u32 reg, u32 val);
+};
+
+#endif /* __LINUX_PLATFORM_DATA_AD7606_H__ */
diff --git a/drivers/iio/adc/ad7606_par.c b/drivers/iio/adc/ad7606_par.c
index 64733b607aa8..335fb481bfde 100644
--- a/drivers/iio/adc/ad7606_par.c
+++ b/drivers/iio/adc/ad7606_par.c
@@ -19,6 +19,7 @@
#include <linux/iio/iio.h>
#include "ad7606.h"
+#include "ad7606_bus_iface.h"
static const struct iio_chan_spec ad7606b_bi_channels[] = {
AD7606_BI_CHANNEL(0),
@@ -31,7 +32,19 @@ static const struct iio_chan_spec ad7606b_bi_channels[] = {
AD7606_BI_CHANNEL(7),
};
-static int ad7606_bi_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *scan_mask)
+static const struct iio_chan_spec ad7606b_bi_sw_channels[] = {
+ AD7606_BI_SW_CHANNEL(0),
+ AD7606_BI_SW_CHANNEL(1),
+ AD7606_BI_SW_CHANNEL(2),
+ AD7606_BI_SW_CHANNEL(3),
+ AD7606_BI_SW_CHANNEL(4),
+ AD7606_BI_SW_CHANNEL(5),
+ AD7606_BI_SW_CHANNEL(6),
+ AD7606_BI_SW_CHANNEL(7),
+};
+
+static int ad7606_par_bus_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
{
struct ad7606_state *st = iio_priv(indio_dev);
unsigned int c, ret;
@@ -48,7 +61,8 @@ static int ad7606_bi_update_scan_mode(struct iio_dev *indio_dev, const unsigned
return 0;
}
-static int ad7606_bi_setup_iio_backend(struct device *dev, struct iio_dev *indio_dev)
+static int ad7606_par_bus_setup_iio_backend(struct device *dev,
+ struct iio_dev *indio_dev)
{
struct ad7606_state *st = iio_priv(indio_dev);
unsigned int ret, c;
@@ -86,9 +100,39 @@ static int ad7606_bi_setup_iio_backend(struct device *dev, struct iio_dev *indio
return 0;
}
+static int ad7606_par_bus_reg_read(struct ad7606_state *st, unsigned int addr)
+{
+ struct ad7606_platform_data *pdata = st->dev->platform_data;
+ int val, ret;
+
+ ret = pdata->bus_reg_read(st->back, addr, &val);
+ if (ret)
+ return ret;
+
+ return val;
+}
+
+static int ad7606_par_bus_reg_write(struct ad7606_state *st, unsigned int addr,
+ unsigned int val)
+{
+ struct ad7606_platform_data *pdata = st->dev->platform_data;
+
+ return pdata->bus_reg_write(st->back, addr, val);
+}
+
+static int ad7606_par_bus_sw_mode_config(struct iio_dev *indio_dev)
+{
+ indio_dev->channels = ad7606b_bi_sw_channels;
+
+ return 0;
+}
+
static const struct ad7606_bus_ops ad7606_bi_bops = {
- .iio_backend_config = ad7606_bi_setup_iio_backend,
- .update_scan_mode = ad7606_bi_update_scan_mode,
+ .iio_backend_config = ad7606_par_bus_setup_iio_backend,
+ .update_scan_mode = ad7606_par_bus_update_scan_mode,
+ .reg_read = ad7606_par_bus_reg_read,
+ .reg_write = ad7606_par_bus_reg_write,
+ .sw_mode_config = ad7606_par_bus_sw_mode_config,
};
static int ad7606_par16_read_block(struct device *dev,
diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c
index e2c147525706..885bf0b68e77 100644
--- a/drivers/iio/adc/ad7606_spi.c
+++ b/drivers/iio/adc/ad7606_spi.c
@@ -15,36 +15,6 @@
#define MAX_SPI_FREQ_HZ 23500000 /* VDRIVE above 4.75 V */
-#define AD7616_CONFIGURATION_REGISTER 0x02
-#define AD7616_OS_MASK GENMASK(4, 2)
-#define AD7616_BURST_MODE BIT(6)
-#define AD7616_SEQEN_MODE BIT(5)
-#define AD7616_RANGE_CH_A_ADDR_OFF 0x04
-#define AD7616_RANGE_CH_B_ADDR_OFF 0x06
-/*
- * Range of channels from a group are stored in 2 registers.
- * 0, 1, 2, 3 in a register followed by 4, 5, 6, 7 in second register.
- * For channels from second group(8-15) the order is the same, only with
- * an offset of 2 for register address.
- */
-#define AD7616_RANGE_CH_ADDR(ch) ((ch) >> 2)
-/* The range of the channel is stored in 2 bits */
-#define AD7616_RANGE_CH_MSK(ch) (0b11 << (((ch) & 0b11) * 2))
-#define AD7616_RANGE_CH_MODE(ch, mode) ((mode) << ((((ch) & 0b11)) * 2))
-
-#define AD7606_CONFIGURATION_REGISTER 0x02
-#define AD7606_SINGLE_DOUT 0x00
-
-/*
- * Range for AD7606B channels are stored in registers starting with address 0x3.
- * Each register stores range for 2 channels(4 bits per channel).
- */
-#define AD7606_RANGE_CH_MSK(ch) (GENMASK(3, 0) << (4 * ((ch) & 0x1)))
-#define AD7606_RANGE_CH_MODE(ch, mode) \
- ((GENMASK(3, 0) & mode) << (4 * ((ch) & 0x1)))
-#define AD7606_RANGE_CH_ADDR(ch) (0x03 + ((ch) >> 1))
-#define AD7606_OS_MODE 0x08
-
static const struct iio_chan_spec ad7616_sw_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(16),
AD7616_CHANNEL(0),
@@ -89,10 +59,6 @@ static const struct iio_chan_spec ad7606c_18_sw_channels[] = {
AD7606_SW_CHANNEL(7, 18),
};
-static const unsigned int ad7606B_oversampling_avail[9] = {
- 1, 2, 4, 8, 16, 32, 64, 128, 256
-};
-
static u16 ad7616_spi_rd_wr_cmd(int addr, char isWriteOp)
{
/*
@@ -194,118 +160,20 @@ static int ad7606_spi_reg_write(struct ad7606_state *st,
return spi_write(spi, &st->d16[0], sizeof(st->d16[0]));
}
-static int ad7606_spi_write_mask(struct ad7606_state *st,
- unsigned int addr,
- unsigned long mask,
- unsigned int val)
-{
- int readval;
-
- readval = st->bops->reg_read(st, addr);
- if (readval < 0)
- return readval;
-
- readval &= ~mask;
- readval |= val;
-
- return st->bops->reg_write(st, addr, readval);
-}
-
-static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
-{
- struct ad7606_state *st = iio_priv(indio_dev);
- unsigned int ch_addr, mode, ch_index;
-
-
- /*
- * Ad7616 has 16 channels divided in group A and group B.
- * The range of channels from A are stored in registers with address 4
- * while channels from B are stored in register with address 6.
- * The last bit from channels determines if it is from group A or B
- * because the order of channels in iio is 0A, 0B, 1A, 1B...
- */
- ch_index = ch >> 1;
-
- ch_addr = AD7616_RANGE_CH_ADDR(ch_index);
-
- if ((ch & 0x1) == 0) /* channel A */
- ch_addr += AD7616_RANGE_CH_A_ADDR_OFF;
- else /* channel B */
- ch_addr += AD7616_RANGE_CH_B_ADDR_OFF;
-
- /* 0b01 for 2.5v, 0b10 for 5v and 0b11 for 10v */
- mode = AD7616_RANGE_CH_MODE(ch_index, ((val + 1) & 0b11));
- return st->bops->write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch_index),
- mode);
-}
-
-static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val)
-{
- struct ad7606_state *st = iio_priv(indio_dev);
-
- return st->bops->write_mask(st, AD7616_CONFIGURATION_REGISTER,
- AD7616_OS_MASK, val << 2);
-}
-
-static int ad7606_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
-{
- struct ad7606_state *st = iio_priv(indio_dev);
-
- return ad7606_spi_write_mask(st,
- AD7606_RANGE_CH_ADDR(ch),
- AD7606_RANGE_CH_MSK(ch),
- AD7606_RANGE_CH_MODE(ch, val));
-}
-
-static int ad7606_write_os_sw(struct iio_dev *indio_dev, int val)
-{
- struct ad7606_state *st = iio_priv(indio_dev);
-
- return ad7606_spi_reg_write(st, AD7606_OS_MODE, val);
-}
-
static int ad7616_sw_mode_config(struct iio_dev *indio_dev)
{
- struct ad7606_state *st = iio_priv(indio_dev);
-
/*
* Scale can be configured individually for each channel
* in software mode.
*/
indio_dev->channels = ad7616_sw_channels;
- st->write_scale = ad7616_write_scale_sw;
- st->write_os = &ad7616_write_os_sw;
-
- /* Activate Burst mode and SEQEN MODE */
- return st->bops->write_mask(st,
- AD7616_CONFIGURATION_REGISTER,
- AD7616_BURST_MODE | AD7616_SEQEN_MODE,
- AD7616_BURST_MODE | AD7616_SEQEN_MODE);
+ return 0;
}
static int ad7606B_sw_mode_config(struct iio_dev *indio_dev)
{
struct ad7606_state *st = iio_priv(indio_dev);
- DECLARE_BITMAP(os, 3);
-
- bitmap_fill(os, 3);
- /*
- * Software mode is enabled when all three oversampling
- * pins are set to high. If oversampling gpios are defined
- * in the device tree, then they need to be set to high,
- * otherwise, they must be hardwired to VDD
- */
- if (st->gpio_os) {
- gpiod_set_array_value(st->gpio_os->ndescs,
- st->gpio_os->desc, st->gpio_os->info, os);
- }
- /* OS of 128 and 256 are available only in software mode */
- st->oversampling_avail = ad7606B_oversampling_avail;
- st->num_os_ratios = ARRAY_SIZE(ad7606B_oversampling_avail);
-
- st->write_scale = ad7606_write_scale_sw;
- st->write_os = &ad7606_write_os_sw;
/* Configure device spi to output on a single channel */
st->bops->reg_write(st,
@@ -350,7 +218,6 @@ static const struct ad7606_bus_ops ad7616_spi_bops = {
.read_block = ad7606_spi_read_block,
.reg_read = ad7606_spi_reg_read,
.reg_write = ad7606_spi_reg_write,
- .write_mask = ad7606_spi_write_mask,
.rd_wr_cmd = ad7616_spi_rd_wr_cmd,
.sw_mode_config = ad7616_sw_mode_config,
};
@@ -359,7 +226,6 @@ static const struct ad7606_bus_ops ad7606b_spi_bops = {
.read_block = ad7606_spi_read_block,
.reg_read = ad7606_spi_reg_read,
.reg_write = ad7606_spi_reg_write,
- .write_mask = ad7606_spi_write_mask,
.rd_wr_cmd = ad7606B_spi_rd_wr_cmd,
.sw_mode_config = ad7606B_sw_mode_config,
};
@@ -368,7 +234,6 @@ static const struct ad7606_bus_ops ad7606c_18_spi_bops = {
.read_block = ad7606_spi_read_block18to32,
.reg_read = ad7606_spi_reg_read,
.reg_write = ad7606_spi_reg_write,
- .write_mask = ad7606_spi_write_mask,
.rd_wr_cmd = ad7606B_spi_rd_wr_cmd,
.sw_mode_config = ad7606c_18_sw_mode_config,
};
diff --git a/drivers/iio/adc/ad7625.c b/drivers/iio/adc/ad7625.c
index afa9bf4ddf3c..0466c0c7eae4 100644
--- a/drivers/iio/adc/ad7625.c
+++ b/drivers/iio/adc/ad7625.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+// SPDX-License-Identifier: (GPL-2.0-only)
/*
* Analog Devices Inc. AD7625 ADC driver
*
@@ -248,12 +248,15 @@ static int ad7625_write_raw(struct iio_dev *indio_dev,
int val, int val2, long info)
{
struct ad7625_state *st = iio_priv(indio_dev);
+ int ret;
switch (info) {
case IIO_CHAN_INFO_SAMP_FREQ:
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev)
- return ad7625_set_sampling_freq(st, val);
- unreachable();
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = ad7625_set_sampling_freq(st, val);
+ iio_device_release_direct(indio_dev);
+ return ret;
default:
return -EINVAL;
}
@@ -680,5 +683,5 @@ module_platform_driver(ad7625_driver);
MODULE_AUTHOR("Trevor Gamblin <tgamblin@baylibre.com>");
MODULE_DESCRIPTION("Analog Devices AD7625 ADC");
-MODULE_LICENSE("Dual BSD/GPL");
+MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("IIO_BACKEND");
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index 113703fb7245..5a863005aca6 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -142,7 +142,7 @@ static const struct iio_chan_spec ad7768_channels[] = {
.channel = 0,
.scan_index = 0,
.scan_type = {
- .sign = 'u',
+ .sign = 's',
.realbits = 24,
.storagebits = 32,
.shift = 8,
@@ -154,7 +154,6 @@ static const struct iio_chan_spec ad7768_channels[] = {
struct ad7768_state {
struct spi_device *spi;
struct regulator *vref;
- struct mutex lock;
struct clk *mclk;
unsigned int mclk_freq;
unsigned int samp_freq;
@@ -256,18 +255,20 @@ static int ad7768_reg_access(struct iio_dev *indio_dev,
struct ad7768_state *st = iio_priv(indio_dev);
int ret;
- mutex_lock(&st->lock);
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
if (readval) {
ret = ad7768_spi_reg_read(st, reg, 1);
if (ret < 0)
- goto err_unlock;
+ goto err_release;
*readval = ret;
ret = 0;
} else {
ret = ad7768_spi_reg_write(st, reg, writeval);
}
-err_unlock:
- mutex_unlock(&st->lock);
+err_release:
+ iio_device_release_direct(indio_dev);
return ret;
}
@@ -365,17 +366,15 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
switch (info) {
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = ad7768_scan_direct(indio_dev);
- if (ret >= 0)
- *val = ret;
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
+ *val = sign_extend32(ret, chan->scan_type.realbits - 1);
return IIO_VAL_INT;
@@ -471,18 +470,15 @@ static irqreturn_t ad7768_trigger_handler(int irq, void *p)
struct ad7768_state *st = iio_priv(indio_dev);
int ret;
- mutex_lock(&st->lock);
-
ret = spi_read(st->spi, &st->data.scan.chan, 3);
if (ret < 0)
- goto err_unlock;
+ goto out;
iio_push_to_buffers_with_timestamp(indio_dev, &st->data.scan,
iio_get_time_ns(indio_dev));
-err_unlock:
+out:
iio_trigger_notify_done(indio_dev->trig);
- mutex_unlock(&st->lock);
return IRQ_HANDLED;
}
@@ -574,6 +570,21 @@ static int ad7768_probe(struct spi_device *spi)
return -ENOMEM;
st = iio_priv(indio_dev);
+ /*
+ * Datasheet recommends SDI line to be kept high when data is not being
+ * clocked out of the controller and the spi clock is free running,
+ * to prevent accidental reset.
+ * Since many controllers do not support the SPI_MOSI_IDLE_HIGH flag
+ * yet, only request the MOSI idle state to enable if the controller
+ * supports it.
+ */
+ if (spi->controller->mode_bits & SPI_MOSI_IDLE_HIGH) {
+ spi->mode |= SPI_MOSI_IDLE_HIGH;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+ }
+
st->spi = spi;
st->vref = devm_regulator_get(&spi->dev, "vref");
@@ -596,8 +607,6 @@ static int ad7768_probe(struct spi_device *spi)
st->mclk_freq = clk_get_rate(st->mclk);
- mutex_init(&st->lock);
-
indio_dev->channels = ad7768_channels;
indio_dev->num_channels = ARRAY_SIZE(ad7768_channels);
indio_dev->name = spi_get_device_id(spi)->name;
diff --git a/drivers/iio/adc/ad7779.c b/drivers/iio/adc/ad7779.c
index 2537dab69a35..a5d87faa5e12 100644
--- a/drivers/iio/adc/ad7779.c
+++ b/drivers/iio/adc/ad7779.c
@@ -467,59 +467,82 @@ static int ad7779_set_calibbias(struct ad7779_state *st, int channel, int val)
calibbias[2]);
}
+static int __ad7779_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct ad7779_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBSCALE:
+ ret = ad7779_get_calibscale(st, chan->channel);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ *val2 = GAIN_REL;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ ret = ad7779_get_calibbias(st, chan->channel);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->sampling_freq;
+ if (*val < 0)
+ return -EINVAL;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
static int ad7779_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
- struct ad7779_state *st = iio_priv(indio_dev);
int ret;
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- switch (mask) {
- case IIO_CHAN_INFO_CALIBSCALE:
- ret = ad7779_get_calibscale(st, chan->channel);
- if (ret < 0)
- return ret;
- *val = ret;
- *val2 = GAIN_REL;
- return IIO_VAL_FRACTIONAL;
- case IIO_CHAN_INFO_CALIBBIAS:
- ret = ad7779_get_calibbias(st, chan->channel);
- if (ret < 0)
- return ret;
- *val = ret;
- return IIO_VAL_INT;
- case IIO_CHAN_INFO_SAMP_FREQ:
- *val = st->sampling_freq;
- if (*val < 0)
- return -EINVAL;
- return IIO_VAL_INT;
- default:
- return -EINVAL;
- }
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = __ad7779_read_raw(indio_dev, chan, val, val2, mask);
+ iio_device_release_direct(indio_dev);
+ return ret;
+}
+
+static int __ad7779_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2,
+ long mask)
+{
+ struct ad7779_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBSCALE:
+ return ad7779_set_calibscale(st, chan->channel, val2);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return ad7779_set_calibbias(st, chan->channel, val);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return ad7779_set_sampling_frequency(st, val);
+ default:
+ return -EINVAL;
}
- unreachable();
}
static int ad7779_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2,
long mask)
{
- struct ad7779_state *st = iio_priv(indio_dev);
+ int ret;
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- switch (mask) {
- case IIO_CHAN_INFO_CALIBSCALE:
- return ad7779_set_calibscale(st, chan->channel, val2);
- case IIO_CHAN_INFO_CALIBBIAS:
- return ad7779_set_calibbias(st, chan->channel, val);
- case IIO_CHAN_INFO_SAMP_FREQ:
- return ad7779_set_sampling_frequency(st, val);
- default:
- return -EINVAL;
- }
- }
- unreachable();
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = __ad7779_write_raw(indio_dev, chan, val, val2, mask);
+ iio_device_release_direct(indio_dev);
+ return ret;
}
static int ad7779_buffer_preenable(struct iio_dev *indio_dev)
diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c
index 76118fe22db8..597c2686ffa4 100644
--- a/drivers/iio/adc/ad7791.c
+++ b/drivers/iio/adc/ad7791.c
@@ -310,15 +310,11 @@ static int ad7791_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
-static int ad7791_write_raw(struct iio_dev *indio_dev,
+static int __ad7791_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2, long mask)
{
struct ad7791_state *st = iio_priv(indio_dev);
- int ret, i;
-
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ int i;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
@@ -328,22 +324,31 @@ static int ad7791_write_raw(struct iio_dev *indio_dev,
break;
}
- if (i == ARRAY_SIZE(ad7791_sample_freq_avail)) {
- ret = -EINVAL;
- break;
- }
+ if (i == ARRAY_SIZE(ad7791_sample_freq_avail))
+ return -EINVAL;
st->filter &= ~AD7791_FILTER_RATE_MASK;
st->filter |= i;
ad_sd_write_reg(&st->sd, AD7791_REG_FILTER,
sizeof(st->filter),
st->filter);
- break;
+ return 0;
default:
- ret = -EINVAL;
+ return -EINVAL;
}
+}
+
+static int ad7791_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = __ad7791_write_raw(indio_dev, chan, val, val2, mask);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
}
diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c
index 1b50d9643a63..ccf18ce48e34 100644
--- a/drivers/iio/adc/ad7793.c
+++ b/drivers/iio/adc/ad7793.c
@@ -462,64 +462,68 @@ static int ad7793_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
-static int ad7793_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val,
- int val2,
- long mask)
+static int __ad7793_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
{
struct ad7793_state *st = iio_priv(indio_dev);
- int ret, i;
+ int i;
unsigned int tmp;
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
-
switch (mask) {
case IIO_CHAN_INFO_SCALE:
- ret = -EINVAL;
- for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
- if (val2 == st->scale_avail[i][1]) {
- ret = 0;
- tmp = st->conf;
- st->conf &= ~AD7793_CONF_GAIN(-1);
- st->conf |= AD7793_CONF_GAIN(i);
-
- if (tmp == st->conf)
- break;
-
- ad_sd_write_reg(&st->sd, AD7793_REG_CONF,
- sizeof(st->conf), st->conf);
- ad7793_calibrate_all(st);
- break;
- }
- break;
- case IIO_CHAN_INFO_SAMP_FREQ:
- if (!val) {
- ret = -EINVAL;
- break;
+ for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) {
+ if (val2 != st->scale_avail[i][1])
+ continue;
+
+ tmp = st->conf;
+ st->conf &= ~AD7793_CONF_GAIN(-1);
+ st->conf |= AD7793_CONF_GAIN(i);
+
+ if (tmp == st->conf)
+ return 0;
+
+ ad_sd_write_reg(&st->sd, AD7793_REG_CONF,
+ sizeof(st->conf), st->conf);
+ ad7793_calibrate_all(st);
+
+ return 0;
}
+ return -EINVAL;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (!val)
+ return -EINVAL;
for (i = 0; i < 16; i++)
if (val == st->chip_info->sample_freq_avail[i])
break;
- if (i == 16) {
- ret = -EINVAL;
- break;
- }
+ if (i == 16)
+ return -EINVAL;
st->mode &= ~AD7793_MODE_RATE(-1);
st->mode |= AD7793_MODE_RATE(i);
ad_sd_write_reg(&st->sd, AD7793_REG_MODE, sizeof(st->mode),
st->mode);
- break;
+ return 0;
default:
- ret = -EINVAL;
+ return -EINVAL;
}
+}
+
+static int ad7793_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = __ad7793_write_raw(indio_dev, chan, val, val2, mask);
+
+ iio_device_release_direct(indio_dev);
- iio_device_release_direct_mode(indio_dev);
return ret;
}
diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c
index 69add1dc4b53..87ff95643794 100644
--- a/drivers/iio/adc/ad7887.c
+++ b/drivers/iio/adc/ad7887.c
@@ -152,11 +152,10 @@ static int ad7887_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = ad7887_scan_direct(st, chan->address);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c
index acc44cb34f82..87945efb940b 100644
--- a/drivers/iio/adc/ad7923.c
+++ b/drivers/iio/adc/ad7923.c
@@ -260,11 +260,10 @@ static int ad7923_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = ad7923_scan_direct(st, chan->address);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
diff --git a/drivers/iio/adc/ad7944.c b/drivers/iio/adc/ad7944.c
index 0ec9cda10f5f..2f949fe55873 100644
--- a/drivers/iio/adc/ad7944.c
+++ b/drivers/iio/adc/ad7944.c
@@ -16,11 +16,14 @@
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
+#include <linux/spi/offload/consumer.h>
#include <linux/spi/spi.h>
#include <linux/string_helpers.h>
+#include <linux/units.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer-dmaengine.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
@@ -54,6 +57,12 @@ struct ad7944_adc {
enum ad7944_spi_mode spi_mode;
struct spi_transfer xfers[3];
struct spi_message msg;
+ struct spi_transfer offload_xfers[2];
+ struct spi_message offload_msg;
+ struct spi_offload *offload;
+ struct spi_offload_trigger *offload_trigger;
+ unsigned long offload_trigger_hz;
+ int sample_freq_range[3];
void *chain_mode_buf;
/* Chip-specific timing specifications. */
const struct ad7944_timing_spec *timing_spec;
@@ -81,6 +90,8 @@ struct ad7944_adc {
/* quite time before CNV rising edge */
#define AD7944_T_QUIET_NS 20
+/* minimum CNV high time to trigger conversion */
+#define AD7944_T_CNVH_NS 10
static const struct ad7944_timing_spec ad7944_timing_spec = {
.conv_ns = 420,
@@ -95,20 +106,27 @@ static const struct ad7944_timing_spec ad7986_timing_spec = {
struct ad7944_chip_info {
const char *name;
const struct ad7944_timing_spec *timing_spec;
+ u32 max_sample_rate_hz;
const struct iio_chan_spec channels[2];
+ const struct iio_chan_spec offload_channels[1];
};
+/* get number of bytes for SPI xfer */
+#define AD7944_SPI_BYTES(scan_type) ((scan_type).realbits > 16 ? 4 : 2)
+
/*
* AD7944_DEFINE_CHIP_INFO - Define a chip info structure for a specific chip
* @_name: The name of the chip
* @_ts: The timing specification for the chip
+ * @_max: The maximum sample rate in Hz
* @_bits: The number of bits in the conversion result
* @_diff: Whether the chip is true differential or not
*/
-#define AD7944_DEFINE_CHIP_INFO(_name, _ts, _bits, _diff) \
+#define AD7944_DEFINE_CHIP_INFO(_name, _ts, _max, _bits, _diff) \
static const struct ad7944_chip_info _name##_chip_info = { \
.name = #_name, \
.timing_spec = &_ts##_timing_spec, \
+ .max_sample_rate_hz = _max, \
.channels = { \
{ \
.type = IIO_VOLTAGE, \
@@ -126,13 +144,43 @@ static const struct ad7944_chip_info _name##_chip_info = { \
}, \
IIO_CHAN_SOFT_TIMESTAMP(1), \
}, \
+ .offload_channels = { \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .differential = _diff, \
+ .channel = 0, \
+ .channel2 = _diff ? 1 : 0, \
+ .scan_index = 0, \
+ .scan_type.sign = _diff ? 's' : 'u', \
+ .scan_type.realbits = _bits, \
+ .scan_type.storagebits = 32, \
+ .scan_type.endianness = IIO_CPU, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
+ | BIT(IIO_CHAN_INFO_SCALE) \
+ | BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_separate_available = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ }, \
+ }, \
}
+/*
+ * Notes on the offload channels:
+ * - There is no soft timestamp since everything is done in hardware.
+ * - There is a sampling frequency attribute added. This controls the SPI
+ * offload trigger.
+ * - The storagebits value depends on the SPI offload provider. Currently there
+ * is only one supported provider, namely the ADI PULSAR ADC HDL project,
+ * which always uses 32-bit words for data values, even for <= 16-bit ADCs.
+ * So the value is just hardcoded to 32 for now.
+ */
+
/* pseudo-differential with ground sense */
-AD7944_DEFINE_CHIP_INFO(ad7944, ad7944, 14, 0);
-AD7944_DEFINE_CHIP_INFO(ad7985, ad7944, 16, 0);
+AD7944_DEFINE_CHIP_INFO(ad7944, ad7944, 2.5 * MEGA, 14, 0);
+AD7944_DEFINE_CHIP_INFO(ad7985, ad7944, 2.5 * MEGA, 16, 0);
/* fully differential */
-AD7944_DEFINE_CHIP_INFO(ad7986, ad7986, 18, 1);
+AD7944_DEFINE_CHIP_INFO(ad7986, ad7986, 2 * MEGA, 18, 1);
static int ad7944_3wire_cs_mode_init_msg(struct device *dev, struct ad7944_adc *adc,
const struct iio_chan_spec *chan)
@@ -164,7 +212,7 @@ static int ad7944_3wire_cs_mode_init_msg(struct device *dev, struct ad7944_adc *
/* Then we can read the data during the acquisition phase */
xfers[2].rx_buf = &adc->sample.raw;
- xfers[2].len = BITS_TO_BYTES(chan->scan_type.storagebits);
+ xfers[2].len = AD7944_SPI_BYTES(chan->scan_type);
xfers[2].bits_per_word = chan->scan_type.realbits;
spi_message_init_with_transfers(&adc->msg, xfers, 3);
@@ -193,7 +241,7 @@ static int ad7944_4wire_mode_init_msg(struct device *dev, struct ad7944_adc *adc
xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
xfers[1].rx_buf = &adc->sample.raw;
- xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits);
+ xfers[1].len = AD7944_SPI_BYTES(chan->scan_type);
xfers[1].bits_per_word = chan->scan_type.realbits;
spi_message_init_with_transfers(&adc->msg, xfers, 2);
@@ -228,7 +276,7 @@ static int ad7944_chain_mode_init_msg(struct device *dev, struct ad7944_adc *adc
xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
xfers[1].rx_buf = adc->chain_mode_buf;
- xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits) * n_chain_dev;
+ xfers[1].len = AD7944_SPI_BYTES(chan->scan_type) * n_chain_dev;
xfers[1].bits_per_word = chan->scan_type.realbits;
spi_message_init_with_transfers(&adc->msg, xfers, 2);
@@ -236,6 +284,48 @@ static int ad7944_chain_mode_init_msg(struct device *dev, struct ad7944_adc *adc
return devm_spi_optimize_message(dev, adc->spi, &adc->msg);
}
+/*
+ * Unlike ad7944_3wire_cs_mode_init_msg(), this creates a message that reads
+ * during the conversion phase instead of the acquisition phase when reading
+ * a sample from the ADC. This is needed to be able to read at the maximum
+ * sample rate. It requires the SPI controller to have offload support and a
+ * high enough SCLK rate to read the sample during the conversion phase.
+ */
+static int ad7944_3wire_cs_mode_init_offload_msg(struct device *dev,
+ struct ad7944_adc *adc,
+ const struct iio_chan_spec *chan)
+{
+ struct spi_transfer *xfers = adc->offload_xfers;
+ int ret;
+
+ /*
+ * CS is tied to CNV and we need a low to high transition to start the
+ * conversion, so place CNV low for t_QUIET to prepare for this.
+ */
+ xfers[0].delay.value = AD7944_T_QUIET_NS;
+ xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
+ /* CNV has to be high for a minimum time to trigger conversion. */
+ xfers[0].cs_change = 1;
+ xfers[0].cs_change_delay.value = AD7944_T_CNVH_NS;
+ xfers[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
+
+ /* Then we can read the previous sample during the conversion phase */
+ xfers[1].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
+ xfers[1].len = AD7944_SPI_BYTES(chan->scan_type);
+ xfers[1].bits_per_word = chan->scan_type.realbits;
+
+ spi_message_init_with_transfers(&adc->offload_msg, xfers,
+ ARRAY_SIZE(adc->offload_xfers));
+
+ adc->offload_msg.offload = adc->offload;
+
+ ret = devm_spi_optimize_message(dev, adc->spi, &adc->offload_msg);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to prepare offload msg\n");
+
+ return 0;
+}
+
/**
* ad7944_convert_and_acquire - Perform a single conversion and acquisition
* @adc: The ADC device structure
@@ -274,12 +364,12 @@ static int ad7944_single_conversion(struct ad7944_adc *adc,
return ret;
if (adc->spi_mode == AD7944_SPI_MODE_CHAIN) {
- if (chan->scan_type.storagebits > 16)
+ if (chan->scan_type.realbits > 16)
*val = ((u32 *)adc->chain_mode_buf)[chan->scan_index];
else
*val = ((u16 *)adc->chain_mode_buf)[chan->scan_index];
} else {
- if (chan->scan_type.storagebits > 16)
+ if (chan->scan_type.realbits > 16)
*val = adc->sample.raw.u32;
else
*val = adc->sample.raw.u16;
@@ -291,6 +381,23 @@ static int ad7944_single_conversion(struct ad7944_adc *adc,
return IIO_VAL_INT;
}
+static int ad7944_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct ad7944_adc *adc = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = adc->sample_freq_range;
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_RANGE;
+ default:
+ return -EINVAL;
+ }
+}
+
static int ad7944_read_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
int *val, int *val2, long info)
@@ -300,12 +407,11 @@ static int ad7944_read_raw(struct iio_dev *indio_dev,
switch (info) {
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = ad7944_single_conversion(adc, chan, val);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE:
@@ -323,13 +429,104 @@ static int ad7944_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = adc->offload_trigger_hz;
+ return IIO_VAL_INT;
+
default:
return -EINVAL;
}
}
+static int ad7944_set_sample_freq(struct ad7944_adc *adc, int val)
+{
+ struct spi_offload_trigger_config config = {
+ .type = SPI_OFFLOAD_TRIGGER_PERIODIC,
+ .periodic = {
+ .frequency_hz = val,
+ },
+ };
+ int ret;
+
+ ret = spi_offload_trigger_validate(adc->offload_trigger, &config);
+ if (ret)
+ return ret;
+
+ adc->offload_trigger_hz = config.periodic.frequency_hz;
+
+ return 0;
+}
+
+static int ad7944_write_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int val, int val2, long info)
+{
+ struct ad7944_adc *adc = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (val < 1 || val > adc->sample_freq_range[2])
+ return -EINVAL;
+
+ return ad7944_set_sample_freq(adc, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7944_write_raw_get_fmt(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return IIO_VAL_INT;
+ default:
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+}
+
static const struct iio_info ad7944_iio_info = {
+ .read_avail = &ad7944_read_avail,
.read_raw = &ad7944_read_raw,
+ .write_raw = &ad7944_write_raw,
+ .write_raw_get_fmt = &ad7944_write_raw_get_fmt,
+};
+
+static int ad7944_offload_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ad7944_adc *adc = iio_priv(indio_dev);
+ struct spi_offload_trigger_config config = {
+ .type = SPI_OFFLOAD_TRIGGER_PERIODIC,
+ .periodic = {
+ .frequency_hz = adc->offload_trigger_hz,
+ },
+ };
+ int ret;
+
+ gpiod_set_value_cansleep(adc->turbo, 1);
+
+ ret = spi_offload_trigger_enable(adc->offload, adc->offload_trigger,
+ &config);
+ if (ret)
+ gpiod_set_value_cansleep(adc->turbo, 0);
+
+ return ret;
+}
+
+static int ad7944_offload_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct ad7944_adc *adc = iio_priv(indio_dev);
+
+ spi_offload_trigger_disable(adc->offload, adc->offload_trigger);
+ gpiod_set_value_cansleep(adc->turbo, 0);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ad7944_offload_buffer_setup_ops = {
+ .postenable = &ad7944_offload_buffer_postenable,
+ .predisable = &ad7944_offload_buffer_predisable,
};
static irqreturn_t ad7944_trigger_handler(int irq, void *p)
@@ -409,8 +606,7 @@ static int ad7944_chain_mode_alloc(struct device *dev,
/* 1 word for each voltage channel + aligned u64 for timestamp */
chain_mode_buf_size = ALIGN(n_chain_dev *
- BITS_TO_BYTES(chan[0].scan_type.storagebits), sizeof(u64))
- + sizeof(u64);
+ AD7944_SPI_BYTES(chan[0].scan_type), sizeof(u64)) + sizeof(u64);
buf = devm_kzalloc(dev, chain_mode_buf_size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -444,6 +640,11 @@ static const char * const ad7944_power_supplies[] = {
"avdd", "dvdd", "bvdd", "vio"
};
+static const struct spi_offload_config ad7944_offload_config = {
+ .capability_flags = SPI_OFFLOAD_CAP_TRIGGER |
+ SPI_OFFLOAD_CAP_RX_STREAM_DMA,
+};
+
static int ad7944_probe(struct spi_device *spi)
{
const struct ad7944_chip_info *chip_info;
@@ -469,6 +670,10 @@ static int ad7944_probe(struct spi_device *spi)
adc->timing_spec = chip_info->timing_spec;
+ adc->sample_freq_range[0] = 1; /* min */
+ adc->sample_freq_range[1] = 1; /* step */
+ adc->sample_freq_range[2] = chip_info->max_sample_rate_hz; /* max */
+
ret = device_property_match_property_string(dev, "adi,spi-mode",
ad7944_spi_modes,
ARRAY_SIZE(ad7944_spi_modes));
@@ -588,20 +793,74 @@ static int ad7944_probe(struct spi_device *spi)
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &ad7944_iio_info;
- if (adc->spi_mode == AD7944_SPI_MODE_CHAIN) {
- indio_dev->available_scan_masks = chain_scan_masks;
- indio_dev->channels = chain_chan;
- indio_dev->num_channels = n_chain_dev + 1;
+ adc->offload = devm_spi_offload_get(dev, spi, &ad7944_offload_config);
+ ret = PTR_ERR_OR_ZERO(adc->offload);
+ if (ret && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "failed to get offload\n");
+
+ /* Fall back to low speed usage when no SPI offload available. */
+ if (ret == -ENODEV) {
+ if (adc->spi_mode == AD7944_SPI_MODE_CHAIN) {
+ indio_dev->available_scan_masks = chain_scan_masks;
+ indio_dev->channels = chain_chan;
+ indio_dev->num_channels = n_chain_dev + 1;
+ } else {
+ indio_dev->channels = chip_info->channels;
+ indio_dev->num_channels = ARRAY_SIZE(chip_info->channels);
+ }
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ ad7944_trigger_handler,
+ NULL);
+ if (ret)
+ return ret;
} else {
- indio_dev->channels = chip_info->channels;
- indio_dev->num_channels = ARRAY_SIZE(chip_info->channels);
- }
+ struct dma_chan *rx_dma;
- ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
- iio_pollfunc_store_time,
- ad7944_trigger_handler, NULL);
- if (ret)
- return ret;
+ if (adc->spi_mode != AD7944_SPI_MODE_SINGLE)
+ return dev_err_probe(dev, -EINVAL,
+ "offload only supported in single mode\n");
+
+ indio_dev->setup_ops = &ad7944_offload_buffer_setup_ops;
+ indio_dev->channels = chip_info->offload_channels;
+ indio_dev->num_channels = ARRAY_SIZE(chip_info->offload_channels);
+
+ adc->offload_trigger = devm_spi_offload_trigger_get(dev,
+ adc->offload, SPI_OFFLOAD_TRIGGER_PERIODIC);
+ if (IS_ERR(adc->offload_trigger))
+ return dev_err_probe(dev, PTR_ERR(adc->offload_trigger),
+ "failed to get offload trigger\n");
+
+ ret = ad7944_set_sample_freq(adc, 2 * MEGA);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to init sample rate\n");
+
+ rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev,
+ adc->offload);
+ if (IS_ERR(rx_dma))
+ return dev_err_probe(dev, PTR_ERR(rx_dma),
+ "failed to get offload RX DMA\n");
+
+ /*
+ * REVISIT: ideally, we would confirm that the offload RX DMA
+ * buffer layout is the same as what is hard-coded in
+ * offload_channels. Right now, the only supported offload
+ * is the pulsar_adc project which always uses 32-bit word
+ * size for data values, regardless of the SPI bits per word.
+ */
+
+ ret = devm_iio_dmaengine_buffer_setup_with_handle(dev,
+ indio_dev, rx_dma, IIO_BUFFER_DIRECTION_IN);
+ if (ret)
+ return ret;
+
+ ret = ad7944_3wire_cs_mode_init_offload_msg(dev, adc,
+ &chip_info->offload_channels[0]);
+ if (ret)
+ return ret;
+ }
return devm_iio_device_register(dev, indio_dev);
}
@@ -636,3 +895,4 @@ module_spi_driver(ad7944_driver);
MODULE_AUTHOR("David Lechner <dlechner@baylibre.com>");
MODULE_DESCRIPTION("Analog Devices AD7944 PulSAR ADC family driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER");
diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c
index aa44b4e2542b..993f4651b73a 100644
--- a/drivers/iio/adc/ad799x.c
+++ b/drivers/iio/adc/ad799x.c
@@ -291,13 +291,12 @@ static int ad799x_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
mutex_lock(&st->lock);
ret = ad799x_scan_direct(st, chan->scan_index);
mutex_unlock(&st->lock);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
@@ -411,9 +410,8 @@ static int ad799x_write_event_config(struct iio_dev *indio_dev,
struct ad799x_state *st = iio_priv(indio_dev);
int ret;
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
mutex_lock(&st->lock);
@@ -429,7 +427,7 @@ static int ad799x_write_event_config(struct iio_dev *indio_dev,
ret = ad799x_write_config(st, st->config);
mutex_unlock(&st->lock);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
}
diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c
index f30119b42ba0..f7a9f46ea0dc 100644
--- a/drivers/iio/adc/ad9467.c
+++ b/drivers/iio/adc/ad9467.c
@@ -813,6 +813,18 @@ static int ad9467_read_raw(struct iio_dev *indio_dev,
}
}
+static int __ad9467_update_clock(struct ad9467_state *st, long r_clk)
+{
+ int ret;
+
+ ret = clk_set_rate(st->clk, r_clk);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+ return ad9467_calibrate(st);
+}
+
static int ad9467_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
@@ -842,14 +854,11 @@ static int ad9467_write_raw(struct iio_dev *indio_dev,
if (sample_rate == r_clk)
return 0;
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- ret = clk_set_rate(st->clk, r_clk);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
- guard(mutex)(&st->lock);
- ret = ad9467_calibrate(st);
- }
+ ret = __ad9467_update_clock(st, r_clk);
+ iio_device_release_direct(indio_dev);
return ret;
default:
return -EINVAL;
diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c
index d5d81581ab34..6c37f8e21120 100644
--- a/drivers/iio/adc/ad_sigma_delta.c
+++ b/drivers/iio/adc/ad_sigma_delta.c
@@ -339,6 +339,7 @@ int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
out:
sigma_delta->keep_cs_asserted = false;
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
+ ad_sigma_delta_disable_one(sigma_delta, channel);
sigma_delta->bus_locked = false;
spi_bus_unlock(sigma_delta->spi->controller);
@@ -386,11 +387,12 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
unsigned int data_reg;
int ret = 0;
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
- ad_sigma_delta_set_channel(sigma_delta, chan->address);
+ ret = ad_sigma_delta_set_channel(sigma_delta, chan->address);
+ if (ret)
+ goto out_release;
spi_bus_lock(sigma_delta->spi->controller);
sigma_delta->bus_locked = true;
@@ -431,7 +433,8 @@ out_unlock:
sigma_delta->keep_cs_asserted = false;
sigma_delta->bus_locked = false;
spi_bus_unlock(sigma_delta->spi->controller);
- iio_device_release_direct_mode(indio_dev);
+out_release:
+ iio_device_release_direct(indio_dev);
if (ret)
return ret;
@@ -801,10 +804,15 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev,
spin_lock_init(&sigma_delta->irq_lock);
- if (info->irq_line)
- sigma_delta->irq_line = info->irq_line;
- else
+ if (info->has_named_irqs) {
+ sigma_delta->irq_line = fwnode_irq_get_byname(dev_fwnode(&spi->dev),
+ "rdy");
+ if (sigma_delta->irq_line < 0)
+ return dev_err_probe(&spi->dev, sigma_delta->irq_line,
+ "Interrupt 'rdy' is required\n");
+ } else {
sigma_delta->irq_line = spi->irq;
+ }
sigma_delta->rdy_gpiod = devm_gpiod_get_optional(&spi->dev, "rdy", GPIOD_IN);
if (IS_ERR(sigma_delta->rdy_gpiod))
diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c
index c7357601f0f8..cf942c043457 100644
--- a/drivers/iio/adc/adi-axi-adc.c
+++ b/drivers/iio/adc/adi-axi-adc.c
@@ -12,9 +12,9 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
@@ -27,6 +27,7 @@
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
+#include "ad7606_bus_iface.h"
/*
* Register definitions:
* https://wiki.analog.com/resources/fpga/docs/axi_adc_ip#register_map
@@ -39,9 +40,19 @@
#define ADI_AXI_REG_RSTN_MMCM_RSTN BIT(1)
#define ADI_AXI_REG_RSTN_RSTN BIT(0)
+#define ADI_AXI_ADC_REG_CONFIG 0x000c
+#define ADI_AXI_ADC_REG_CONFIG_CMOS_OR_LVDS_N BIT(7)
+
#define ADI_AXI_ADC_REG_CTRL 0x0044
#define ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK BIT(1)
+#define ADI_AXI_ADC_REG_CNTRL_3 0x004c
+#define AXI_AD485X_CNTRL_3_OS_EN_MSK BIT(2)
+#define AXI_AD485X_CNTRL_3_PACKET_FORMAT_MSK GENMASK(1, 0)
+#define AXI_AD485X_PACKET_FORMAT_20BIT 0x0
+#define AXI_AD485X_PACKET_FORMAT_24BIT 0x1
+#define AXI_AD485X_PACKET_FORMAT_32BIT 0x2
+
#define ADI_AXI_ADC_REG_DRP_STATUS 0x0074
#define ADI_AXI_ADC_DRP_LOCKED BIT(17)
@@ -73,6 +84,12 @@
#define ADI_AXI_ADC_REG_DELAY(l) (0x0800 + (l) * 0x4)
#define AXI_ADC_DELAY_CTRL_MASK GENMASK(4, 0)
+#define ADI_AXI_REG_CONFIG_WR 0x0080
+#define ADI_AXI_REG_CONFIG_RD 0x0084
+#define ADI_AXI_REG_CONFIG_CTRL 0x008c
+#define ADI_AXI_REG_CONFIG_CTRL_READ 0x03
+#define ADI_AXI_REG_CONFIG_CTRL_WRITE 0x01
+
#define ADI_AXI_ADC_MAX_IO_NUM_LANES 15
#define ADI_AXI_REG_CHAN_CTRL_DEFAULTS \
@@ -80,7 +97,20 @@
ADI_AXI_REG_CHAN_CTRL_FMT_EN | \
ADI_AXI_REG_CHAN_CTRL_ENABLE)
+#define ADI_AXI_REG_READ_BIT 0x8000
+#define ADI_AXI_REG_ADDRESS_MASK 0xff00
+#define ADI_AXI_REG_VALUE_MASK 0x00ff
+
+struct axi_adc_info {
+ unsigned int version;
+ const struct iio_backend_info *backend_info;
+ bool has_child_nodes;
+ const void *pdata;
+ unsigned int pdata_sz;
+};
+
struct adi_axi_adc_state {
+ const struct axi_adc_info *info;
struct regmap *regmap;
struct device *dev;
/* lock to protect multiple accesses to the device registers */
@@ -290,6 +320,88 @@ static int axi_adc_chan_disable(struct iio_backend *back, unsigned int chan)
ADI_AXI_REG_CHAN_CTRL_ENABLE);
}
+static int axi_adc_interface_type_get(struct iio_backend *back,
+ enum iio_backend_interface_type *type)
+{
+ struct adi_axi_adc_state *st = iio_backend_get_priv(back);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(st->regmap, ADI_AXI_ADC_REG_CONFIG, &val);
+ if (ret)
+ return ret;
+
+ if (val & ADI_AXI_ADC_REG_CONFIG_CMOS_OR_LVDS_N)
+ *type = IIO_BACKEND_INTERFACE_SERIAL_CMOS;
+ else
+ *type = IIO_BACKEND_INTERFACE_SERIAL_LVDS;
+
+ return 0;
+}
+
+static int axi_adc_ad485x_data_size_set(struct iio_backend *back,
+ unsigned int size)
+{
+ struct adi_axi_adc_state *st = iio_backend_get_priv(back);
+ unsigned int val;
+
+ switch (size) {
+ /*
+ * There are two different variants of the AXI AXI_AD485X IP block, a
+ * 16-bit and a 20-bit variant.
+ * The 0x0 value (AXI_AD485X_PACKET_FORMAT_20BIT) is corresponding also
+ * to the 16-bit variant of the IP block.
+ */
+ case 16:
+ case 20:
+ val = AXI_AD485X_PACKET_FORMAT_20BIT;
+ break;
+ case 24:
+ val = AXI_AD485X_PACKET_FORMAT_24BIT;
+ break;
+ /*
+ * The 0x2 (AXI_AD485X_PACKET_FORMAT_32BIT) corresponds only to the
+ * 20-bit variant of the IP block. Setting this value properly is
+ * ensured by the upper layers of the drivers calling the axi-adc
+ * functions.
+ * Also, for 16-bit IP block, the 0x2 (AXI_AD485X_PACKET_FORMAT_32BIT)
+ * value is handled as maximum size available which is 24-bit for this
+ * configuration.
+ */
+ case 32:
+ val = AXI_AD485X_PACKET_FORMAT_32BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3,
+ AXI_AD485X_CNTRL_3_PACKET_FORMAT_MSK,
+ FIELD_PREP(AXI_AD485X_CNTRL_3_PACKET_FORMAT_MSK, val));
+}
+
+static int axi_adc_ad485x_oversampling_ratio_set(struct iio_backend *back,
+ unsigned int ratio)
+{
+ struct adi_axi_adc_state *st = iio_backend_get_priv(back);
+
+ /* The current state of the function enables or disables the
+ * oversampling in REG_CNTRL_3 register. A ratio equal to 1 implies no
+ * oversampling, while a value greater than 1 implies oversampling being
+ * enabled.
+ */
+ switch (ratio) {
+ case 0:
+ return -EINVAL;
+ case 1:
+ return regmap_clear_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3,
+ AXI_AD485X_CNTRL_3_OS_EN_MSK);
+ default:
+ return regmap_set_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3,
+ AXI_AD485X_CNTRL_3_OS_EN_MSK);
+ }
+}
+
static struct iio_buffer *axi_adc_request_buffer(struct iio_backend *back,
struct iio_dev *indio_dev)
{
@@ -302,10 +414,79 @@ static struct iio_buffer *axi_adc_request_buffer(struct iio_backend *back,
return iio_dmaengine_buffer_setup(st->dev, indio_dev, dma_name);
}
+static int axi_adc_raw_write(struct iio_backend *back, u32 val)
+{
+ struct adi_axi_adc_state *st = iio_backend_get_priv(back);
+
+ regmap_write(st->regmap, ADI_AXI_REG_CONFIG_WR, val);
+ regmap_write(st->regmap, ADI_AXI_REG_CONFIG_CTRL,
+ ADI_AXI_REG_CONFIG_CTRL_WRITE);
+ fsleep(100);
+ regmap_write(st->regmap, ADI_AXI_REG_CONFIG_CTRL, 0x00);
+ fsleep(100);
+
+ return 0;
+}
+
+static int axi_adc_raw_read(struct iio_backend *back, u32 *val)
+{
+ struct adi_axi_adc_state *st = iio_backend_get_priv(back);
+
+ regmap_write(st->regmap, ADI_AXI_REG_CONFIG_CTRL,
+ ADI_AXI_REG_CONFIG_CTRL_READ);
+ fsleep(100);
+ regmap_read(st->regmap, ADI_AXI_REG_CONFIG_RD, val);
+ regmap_write(st->regmap, ADI_AXI_REG_CONFIG_CTRL, 0x00);
+ fsleep(100);
+
+ return 0;
+}
+
+static int ad7606_bus_reg_read(struct iio_backend *back, u32 reg, u32 *val)
+{
+ struct adi_axi_adc_state *st = iio_backend_get_priv(back);
+ int addr;
+
+ guard(mutex)(&st->lock);
+
+ /*
+ * The address is written on the highest weight byte, and the MSB set
+ * at 1 indicates a read operation.
+ */
+ addr = FIELD_PREP(ADI_AXI_REG_ADDRESS_MASK, reg) | ADI_AXI_REG_READ_BIT;
+ axi_adc_raw_write(back, addr);
+ axi_adc_raw_read(back, val);
+
+ /* Write 0x0 on the bus to get back to ADC mode */
+ axi_adc_raw_write(back, 0);
+
+ return 0;
+}
+
+static int ad7606_bus_reg_write(struct iio_backend *back, u32 reg, u32 val)
+{
+ struct adi_axi_adc_state *st = iio_backend_get_priv(back);
+ u32 buf;
+
+ guard(mutex)(&st->lock);
+
+ /* Write any register to switch to register mode */
+ axi_adc_raw_write(back, 0xaf00);
+
+ buf = FIELD_PREP(ADI_AXI_REG_ADDRESS_MASK, reg) |
+ FIELD_PREP(ADI_AXI_REG_VALUE_MASK, val);
+ axi_adc_raw_write(back, buf);
+
+ /* Write 0x0 on the bus to get back to ADC mode */
+ axi_adc_raw_write(back, 0);
+
+ return 0;
+}
+
static void axi_adc_free_buffer(struct iio_backend *back,
struct iio_buffer *buffer)
{
- iio_dmaengine_buffer_free(buffer);
+ iio_dmaengine_buffer_teardown(buffer);
}
static int axi_adc_reg_access(struct iio_backend *back, unsigned int reg,
@@ -325,6 +506,36 @@ static const struct regmap_config axi_adc_regmap_config = {
.reg_stride = 4,
};
+static void axi_adc_child_remove(void *data)
+{
+ platform_device_unregister(data);
+}
+
+static int axi_adc_create_platform_device(struct adi_axi_adc_state *st,
+ struct fwnode_handle *child)
+{
+ struct platform_device_info pi = {
+ .parent = st->dev,
+ .name = fwnode_get_name(child),
+ .id = PLATFORM_DEVID_AUTO,
+ .fwnode = child,
+ .data = st->info->pdata,
+ .size_data = st->info->pdata_sz,
+ };
+ struct platform_device *pdev;
+ int ret;
+
+ pdev = platform_device_register_full(&pi);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ ret = devm_add_action_or_reset(st->dev, axi_adc_child_remove, pdev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static const struct iio_backend_ops adi_axi_adc_ops = {
.enable = axi_adc_enable,
.disable = axi_adc_disable,
@@ -337,6 +548,7 @@ static const struct iio_backend_ops adi_axi_adc_ops = {
.iodelay_set = axi_adc_iodelays_set,
.test_pattern_set = axi_adc_test_pattern_set,
.chan_status = axi_adc_chan_status,
+ .interface_type_get = axi_adc_interface_type_get,
.debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access),
.debugfs_print_chan_status = iio_backend_debugfs_ptr(axi_adc_debugfs_print_chan_status),
};
@@ -346,9 +558,32 @@ static const struct iio_backend_info adi_axi_adc_generic = {
.ops = &adi_axi_adc_ops,
};
+static const struct iio_backend_ops adi_ad485x_ops = {
+ .enable = axi_adc_enable,
+ .disable = axi_adc_disable,
+ .data_format_set = axi_adc_data_format_set,
+ .chan_enable = axi_adc_chan_enable,
+ .chan_disable = axi_adc_chan_disable,
+ .request_buffer = axi_adc_request_buffer,
+ .free_buffer = axi_adc_free_buffer,
+ .data_sample_trigger = axi_adc_data_sample_trigger,
+ .iodelay_set = axi_adc_iodelays_set,
+ .chan_status = axi_adc_chan_status,
+ .interface_type_get = axi_adc_interface_type_get,
+ .data_size_set = axi_adc_ad485x_data_size_set,
+ .oversampling_ratio_set = axi_adc_ad485x_oversampling_ratio_set,
+ .debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access),
+ .debugfs_print_chan_status =
+ iio_backend_debugfs_ptr(axi_adc_debugfs_print_chan_status),
+};
+
+static const struct iio_backend_info axi_ad485x = {
+ .name = "axi-ad485x",
+ .ops = &adi_ad485x_ops,
+};
+
static int adi_axi_adc_probe(struct platform_device *pdev)
{
- const unsigned int *expected_ver;
struct adi_axi_adc_state *st;
void __iomem *base;
unsigned int ver;
@@ -370,8 +605,8 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, PTR_ERR(st->regmap),
"failed to init register map\n");
- expected_ver = device_get_match_data(&pdev->dev);
- if (!expected_ver)
+ st->info = device_get_match_data(&pdev->dev);
+ if (!st->info)
return -ENODEV;
clk = devm_clk_get_enabled(&pdev->dev, NULL);
@@ -391,23 +626,46 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
if (ret)
return ret;
- if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(*expected_ver)) {
+ if (ADI_AXI_PCORE_VER_MAJOR(ver) !=
+ ADI_AXI_PCORE_VER_MAJOR(st->info->version)) {
dev_err(&pdev->dev,
"Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
- ADI_AXI_PCORE_VER_MAJOR(*expected_ver),
- ADI_AXI_PCORE_VER_MINOR(*expected_ver),
- ADI_AXI_PCORE_VER_PATCH(*expected_ver),
+ ADI_AXI_PCORE_VER_MAJOR(st->info->version),
+ ADI_AXI_PCORE_VER_MINOR(st->info->version),
+ ADI_AXI_PCORE_VER_PATCH(st->info->version),
ADI_AXI_PCORE_VER_MAJOR(ver),
ADI_AXI_PCORE_VER_MINOR(ver),
ADI_AXI_PCORE_VER_PATCH(ver));
return -ENODEV;
}
- ret = devm_iio_backend_register(&pdev->dev, &adi_axi_adc_generic, st);
+ ret = devm_iio_backend_register(&pdev->dev, st->info->backend_info, st);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"failed to register iio backend\n");
+ device_for_each_child_node_scoped(&pdev->dev, child) {
+ int val;
+
+ if (!st->info->has_child_nodes)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "invalid fdt axi-dac compatible.");
+
+ /* Processing only reg 0 node */
+ ret = fwnode_property_read_u32(child, "reg", &val);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "invalid reg property.");
+ if (val != 0)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "invalid node address.");
+
+ ret = axi_adc_create_platform_device(st, child);
+ if (ret)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "cannot create device.");
+ }
+
dev_info(&pdev->dev, "AXI ADC IP core (%d.%.2d.%c) probed\n",
ADI_AXI_PCORE_VER_MAJOR(ver),
ADI_AXI_PCORE_VER_MINOR(ver),
@@ -416,11 +674,34 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
return 0;
}
-static unsigned int adi_axi_adc_10_0_a_info = ADI_AXI_PCORE_VER(10, 0, 'a');
+static const struct axi_adc_info adc_generic = {
+ .version = ADI_AXI_PCORE_VER(10, 0, 'a'),
+ .backend_info = &adi_axi_adc_generic,
+};
+
+static const struct axi_adc_info adi_axi_ad485x = {
+ .version = ADI_AXI_PCORE_VER(10, 0, 'a'),
+ .backend_info = &axi_ad485x,
+};
+
+static const struct ad7606_platform_data ad7606_pdata = {
+ .bus_reg_read = ad7606_bus_reg_read,
+ .bus_reg_write = ad7606_bus_reg_write,
+};
+
+static const struct axi_adc_info adc_ad7606 = {
+ .version = ADI_AXI_PCORE_VER(10, 0, 'a'),
+ .backend_info = &adi_axi_adc_generic,
+ .pdata = &ad7606_pdata,
+ .pdata_sz = sizeof(ad7606_pdata),
+ .has_child_nodes = true,
+};
/* Match table for of_platform binding */
static const struct of_device_id adi_axi_adc_of_match[] = {
- { .compatible = "adi,axi-adc-10.0.a", .data = &adi_axi_adc_10_0_a_info },
+ { .compatible = "adi,axi-adc-10.0.a", .data = &adc_generic },
+ { .compatible = "adi,axi-ad485x", .data = &adi_axi_ad485x },
+ { .compatible = "adi,axi-ad7606x", .data = &adc_ad7606 },
{ /* end of list */ }
};
MODULE_DEVICE_TABLE(of, adi_axi_adc_of_match);
diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
index c3a1dea2aa82..414610afcb2c 100644
--- a/drivers/iio/adc/at91-sama5d2_adc.c
+++ b/drivers/iio/adc/at91-sama5d2_adc.c
@@ -9,6 +9,7 @@
*/
#include <linux/bitops.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
@@ -1826,19 +1827,10 @@ static int at91_adc_read_info_locked(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val)
{
struct at91_adc_state *st = iio_priv(indio_dev);
- int ret;
-
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
-
- mutex_lock(&st->lock);
- ret = at91_adc_read_info_raw(indio_dev, chan, val);
- mutex_unlock(&st->lock);
- iio_device_release_direct_mode(indio_dev);
+ guard(mutex)(&st->lock);
- return ret;
+ return at91_adc_read_info_raw(indio_dev, chan, val);
}
static void at91_adc_temp_sensor_configure(struct at91_adc_state *st,
@@ -1883,14 +1875,11 @@ static int at91_adc_read_temp(struct iio_dev *indio_dev,
u32 tmp;
int ret, vbg, vtemp;
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
- mutex_lock(&st->lock);
+ guard(mutex)(&st->lock);
ret = pm_runtime_resume_and_get(st->dev);
if (ret < 0)
- goto unlock;
+ return ret;
at91_adc_temp_sensor_configure(st, true);
@@ -1912,9 +1901,6 @@ restore_config:
at91_adc_temp_sensor_configure(st, false);
pm_runtime_mark_last_busy(st->dev);
pm_runtime_put_autosuspend(st->dev);
-unlock:
- mutex_unlock(&st->lock);
- iio_device_release_direct_mode(indio_dev);
if (ret < 0)
return ret;
@@ -1936,10 +1922,16 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev,
int *val, int *val2, long mask)
{
struct at91_adc_state *st = iio_priv(indio_dev);
+ int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
- return at91_adc_read_info_locked(indio_dev, chan, val);
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = at91_adc_read_info_locked(indio_dev, chan, val);
+ iio_device_release_direct(indio_dev);
+ return ret;
case IIO_CHAN_INFO_SCALE:
*val = st->vref_uv / 1000;
@@ -1951,7 +1943,13 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_PROCESSED:
if (chan->type != IIO_TEMP)
return -EINVAL;
- return at91_adc_read_temp(indio_dev, chan, val);
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = at91_adc_read_temp(indio_dev, chan, val);
+ iio_device_release_direct(indio_dev);
+
+ return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = at91_adc_get_sample_freq(st);
@@ -1979,28 +1977,26 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev,
if (val == st->oversampling_ratio)
return 0;
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
mutex_lock(&st->lock);
/* update ratio */
ret = at91_adc_config_emr(st, val, 0);
mutex_unlock(&st->lock);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
if (val < st->soc_info.min_sample_rate ||
val > st->soc_info.max_sample_rate)
return -EINVAL;
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
mutex_lock(&st->lock);
at91_adc_setup_samp_freq(indio_dev, val,
st->soc_info.startup_time, 0);
mutex_unlock(&st->lock);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return 0;
default:
return -EINVAL;
diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
index 221a5fdc1eaa..a1e48a756a7b 100644
--- a/drivers/iio/adc/dln2-adc.c
+++ b/drivers/iio/adc/dln2-adc.c
@@ -314,15 +314,14 @@ static int dln2_adc_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret < 0)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
mutex_lock(&dln2->mutex);
ret = dln2_adc_read(dln2, chan->channel);
mutex_unlock(&dln2->mutex);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c
index f5ba4a1b5a7d..7e736e77d8bb 100644
--- a/drivers/iio/adc/max1027.c
+++ b/drivers/iio/adc/max1027.c
@@ -336,10 +336,6 @@ static int max1027_read_single_value(struct iio_dev *indio_dev,
int ret;
struct max1027_state *st = iio_priv(indio_dev);
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
-
/* Configure conversion register with the requested chan */
st->reg = MAX1027_CONV_REG | MAX1027_CHAN(chan->channel) |
MAX1027_NOSCAN;
@@ -349,7 +345,7 @@ static int max1027_read_single_value(struct iio_dev *indio_dev,
if (ret < 0) {
dev_err(&indio_dev->dev,
"Failed to configure conversion register\n");
- goto release;
+ return ret;
}
/*
@@ -359,14 +355,10 @@ static int max1027_read_single_value(struct iio_dev *indio_dev,
*/
ret = max1027_wait_eoc(indio_dev);
if (ret)
- goto release;
+ return ret;
/* Read result */
ret = spi_read(st->spi, st->buffer, (chan->type == IIO_TEMP) ? 4 : 2);
-
-release:
- iio_device_release_direct_mode(indio_dev);
-
if (ret < 0)
return ret;
@@ -382,37 +374,32 @@ static int max1027_read_raw(struct iio_dev *indio_dev,
int ret = 0;
struct max1027_state *st = iio_priv(indio_dev);
- mutex_lock(&st->lock);
+ guard(mutex)(&st->lock);
switch (mask) {
case IIO_CHAN_INFO_RAW:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
ret = max1027_read_single_value(indio_dev, chan, val);
- break;
+ iio_device_release_direct(indio_dev);
+ return ret;
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_TEMP:
*val = 1;
*val2 = 8;
- ret = IIO_VAL_FRACTIONAL;
- break;
+ return IIO_VAL_FRACTIONAL;
case IIO_VOLTAGE:
*val = 2500;
*val2 = chan->scan_type.realbits;
- ret = IIO_VAL_FRACTIONAL_LOG2;
- break;
+ return IIO_VAL_FRACTIONAL_LOG2;
default:
- ret = -EINVAL;
- break;
+ return -EINVAL;
}
- break;
default:
- ret = -EINVAL;
- break;
+ return -EINVAL;
}
-
- mutex_unlock(&st->lock);
-
- return ret;
}
static int max1027_debugfs_reg_access(struct iio_dev *indio_dev,
diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c
index 76abafd47404..437d9f24b5a1 100644
--- a/drivers/iio/adc/max11410.c
+++ b/drivers/iio/adc/max11410.c
@@ -471,9 +471,8 @@ static int max11410_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
mutex_lock(&state->lock);
@@ -481,7 +480,7 @@ static int max11410_read_raw(struct iio_dev *indio_dev,
mutex_unlock(&state->lock);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
if (ret)
return ret;
@@ -507,12 +506,37 @@ static int max11410_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
+static int __max11410_write_samp_freq(struct max11410_state *st,
+ int val, int val2)
+{
+ int ret, i, reg_val, filter;
+
+ guard(mutex)(&st->lock);
+
+ ret = regmap_read(st->regmap, MAX11410_REG_FILTER, &reg_val);
+ if (ret)
+ return ret;
+
+ filter = FIELD_GET(MAX11410_FILTER_LINEF_MASK, reg_val);
+
+ for (i = 0; i < max11410_sampling_len[filter]; ++i) {
+ if (val == max11410_sampling_rates[filter][i][0] &&
+ val2 == max11410_sampling_rates[filter][i][1])
+ break;
+ }
+ if (i == max11410_sampling_len[filter])
+ return -EINVAL;
+
+ return regmap_write_bits(st->regmap, MAX11410_REG_FILTER,
+ MAX11410_FILTER_RATE_MASK, i);
+}
+
static int max11410_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct max11410_state *st = iio_priv(indio_dev);
- int i, ret, reg_val, filter, gain;
+ int ret, gain;
u32 *scale_avail;
switch (mask) {
@@ -525,9 +549,8 @@ static int max11410_write_raw(struct iio_dev *indio_dev,
if (val != 0 || val2 == 0)
return -EINVAL;
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
/* Convert from INT_PLUS_MICRO to FRACTIONAL_LOG2 */
val2 = val2 * DIV_ROUND_CLOSEST(BIT(24), 1000000);
@@ -536,38 +559,15 @@ static int max11410_write_raw(struct iio_dev *indio_dev,
st->channels[chan->address].gain = clamp_val(gain, 0, 7);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return 0;
case IIO_CHAN_INFO_SAMP_FREQ:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
-
- mutex_lock(&st->lock);
-
- ret = regmap_read(st->regmap, MAX11410_REG_FILTER, &reg_val);
- if (ret)
- goto out;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
- filter = FIELD_GET(MAX11410_FILTER_LINEF_MASK, reg_val);
-
- for (i = 0; i < max11410_sampling_len[filter]; ++i) {
- if (val == max11410_sampling_rates[filter][i][0] &&
- val2 == max11410_sampling_rates[filter][i][1])
- break;
- }
- if (i == max11410_sampling_len[filter]) {
- ret = -EINVAL;
- goto out;
- }
-
- ret = regmap_write_bits(st->regmap, MAX11410_REG_FILTER,
- MAX11410_FILTER_RATE_MASK, i);
-
-out:
- mutex_unlock(&st->lock);
- iio_device_release_direct_mode(indio_dev);
+ ret = __max11410_write_samp_freq(st, val, val2);
+ iio_device_release_direct(indio_dev);
return ret;
default:
diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c
index e8d731bc34e0..35717ec082ce 100644
--- a/drivers/iio/adc/max1363.c
+++ b/drivers/iio/adc/max1363.c
@@ -364,55 +364,52 @@ static int max1363_read_single_chan(struct iio_dev *indio_dev,
int *val,
long m)
{
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- s32 data;
- u8 rxbuf[2];
- struct max1363_state *st = iio_priv(indio_dev);
- struct i2c_client *client = st->client;
-
- guard(mutex)(&st->lock);
-
- /*
- * If monitor mode is enabled, the method for reading a single
- * channel will have to be rather different and has not yet
- * been implemented.
- *
- * Also, cannot read directly if buffered capture enabled.
- */
- if (st->monitor_on)
- return -EBUSY;
+ s32 data;
+ u8 rxbuf[2];
+ struct max1363_state *st = iio_priv(indio_dev);
+ struct i2c_client *client = st->client;
- /* Check to see if current scan mode is correct */
- if (st->current_mode != &max1363_mode_table[chan->address]) {
- int ret;
+ guard(mutex)(&st->lock);
- /* Update scan mode if needed */
- st->current_mode = &max1363_mode_table[chan->address];
- ret = max1363_set_scan_mode(st);
- if (ret < 0)
- return ret;
- }
- if (st->chip_info->bits != 8) {
- /* Get reading */
- data = st->recv(client, rxbuf, 2);
- if (data < 0)
- return data;
-
- data = get_unaligned_be16(rxbuf) &
- ((1 << st->chip_info->bits) - 1);
- } else {
- /* Get reading */
- data = st->recv(client, rxbuf, 1);
- if (data < 0)
- return data;
-
- data = rxbuf[0];
- }
- *val = data;
+ /*
+ * If monitor mode is enabled, the method for reading a single
+ * channel will have to be rather different and has not yet
+ * been implemented.
+ *
+ * Also, cannot read directly if buffered capture enabled.
+ */
+ if (st->monitor_on)
+ return -EBUSY;
+
+ /* Check to see if current scan mode is correct */
+ if (st->current_mode != &max1363_mode_table[chan->address]) {
+ int ret;
+
+ /* Update scan mode if needed */
+ st->current_mode = &max1363_mode_table[chan->address];
+ ret = max1363_set_scan_mode(st);
+ if (ret < 0)
+ return ret;
+ }
+ if (st->chip_info->bits != 8) {
+ /* Get reading */
+ data = st->recv(client, rxbuf, 2);
+ if (data < 0)
+ return data;
+
+ data = get_unaligned_be16(rxbuf) &
+ ((1 << st->chip_info->bits) - 1);
+ } else {
+ /* Get reading */
+ data = st->recv(client, rxbuf, 1);
+ if (data < 0)
+ return data;
- return 0;
+ data = rxbuf[0];
}
- unreachable();
+ *val = data;
+
+ return 0;
}
static int max1363_read_raw(struct iio_dev *indio_dev,
@@ -426,7 +423,11 @@ static int max1363_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
ret = max1363_read_single_chan(indio_dev, chan, val, m);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
return IIO_VAL_INT;
@@ -947,46 +948,58 @@ error_ret:
return ret;
}
-static int max1363_write_event_config(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan, enum iio_event_type type,
+static int __max1363_write_event_config(struct max1363_state *st,
+ const struct iio_chan_spec *chan,
enum iio_event_direction dir, bool state)
{
- struct max1363_state *st = iio_priv(indio_dev);
-
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- int number = chan->channel;
- u16 unifiedmask;
- int ret;
+ int number = chan->channel;
+ u16 unifiedmask;
+ int ret;
- guard(mutex)(&st->lock);
+ guard(mutex)(&st->lock);
- unifiedmask = st->mask_low | st->mask_high;
- if (dir == IIO_EV_DIR_FALLING) {
+ unifiedmask = st->mask_low | st->mask_high;
+ if (dir == IIO_EV_DIR_FALLING) {
- if (state == 0)
- st->mask_low &= ~(1 << number);
- else {
- ret = __max1363_check_event_mask((1 << number),
- unifiedmask);
- if (ret)
- return ret;
- st->mask_low |= (1 << number);
- }
- } else {
- if (state == 0)
- st->mask_high &= ~(1 << number);
- else {
- ret = __max1363_check_event_mask((1 << number),
- unifiedmask);
- if (ret)
- return ret;
- st->mask_high |= (1 << number);
- }
+ if (state == 0)
+ st->mask_low &= ~(1 << number);
+ else {
+ ret = __max1363_check_event_mask((1 << number),
+ unifiedmask);
+ if (ret)
+ return ret;
+ st->mask_low |= (1 << number);
+ }
+ } else {
+ if (state == 0)
+ st->mask_high &= ~(1 << number);
+ else {
+ ret = __max1363_check_event_mask((1 << number),
+ unifiedmask);
+ if (ret)
+ return ret;
+ st->mask_high |= (1 << number);
}
}
- max1363_monitor_mode_update(st, !!(st->mask_high | st->mask_low));
return 0;
+
+}
+static int max1363_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir, bool state)
+{
+ struct max1363_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = __max1363_write_event_config(st, chan, dir, state);
+ iio_device_release_direct(indio_dev);
+ max1363_monitor_mode_update(st, !!(st->mask_high | st->mask_low));
+
+ return ret;
}
/*
diff --git a/drivers/iio/adc/max34408.c b/drivers/iio/adc/max34408.c
index 971e6e5dee9b..4f45fd22a90c 100644
--- a/drivers/iio/adc/max34408.c
+++ b/drivers/iio/adc/max34408.c
@@ -8,6 +8,7 @@
*/
#include <linux/bitfield.h>
+#include <linux/cleanup.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/module.h>
diff --git a/drivers/iio/adc/pac1921.c b/drivers/iio/adc/pac1921.c
index 63f518215156..beb5511c4504 100644
--- a/drivers/iio/adc/pac1921.c
+++ b/drivers/iio/adc/pac1921.c
@@ -7,6 +7,7 @@
#include <linux/unaligned.h>
#include <linux/bitfield.h>
+#include <linux/cleanup.h>
#include <linux/i2c.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
index a29e54754c8f..9a099df79518 100644
--- a/drivers/iio/adc/rockchip_saradc.c
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Rockchip Successive Approximation Register (SAR) A/D Converter
- * Copyright (C) 2014 ROCKCHIP, Inc.
+ * Copyright (C) 2014 Rockchip Electronics Co., Ltd.
*/
#include <linux/bitfield.h>
@@ -275,6 +275,40 @@ static const struct rockchip_saradc_data rk3399_saradc_data = {
.power_down = rockchip_saradc_power_down_v1,
};
+static const struct iio_chan_spec rockchip_rk3528_saradc_iio_channels[] = {
+ SARADC_CHANNEL(0, "adc0", 10),
+ SARADC_CHANNEL(1, "adc1", 10),
+ SARADC_CHANNEL(2, "adc2", 10),
+ SARADC_CHANNEL(3, "adc3", 10),
+};
+
+static const struct rockchip_saradc_data rk3528_saradc_data = {
+ .channels = rockchip_rk3528_saradc_iio_channels,
+ .num_channels = ARRAY_SIZE(rockchip_rk3528_saradc_iio_channels),
+ .clk_rate = 1000000,
+ .start = rockchip_saradc_start_v2,
+ .read = rockchip_saradc_read_v2,
+};
+
+static const struct iio_chan_spec rockchip_rk3562_saradc_iio_channels[] = {
+ SARADC_CHANNEL(0, "adc0", 10),
+ SARADC_CHANNEL(1, "adc1", 10),
+ SARADC_CHANNEL(2, "adc2", 10),
+ SARADC_CHANNEL(3, "adc3", 10),
+ SARADC_CHANNEL(4, "adc4", 10),
+ SARADC_CHANNEL(5, "adc5", 10),
+ SARADC_CHANNEL(6, "adc6", 10),
+ SARADC_CHANNEL(7, "adc7", 10),
+};
+
+static const struct rockchip_saradc_data rk3562_saradc_data = {
+ .channels = rockchip_rk3562_saradc_iio_channels,
+ .num_channels = ARRAY_SIZE(rockchip_rk3562_saradc_iio_channels),
+ .clk_rate = 1000000,
+ .start = rockchip_saradc_start_v2,
+ .read = rockchip_saradc_read_v2,
+};
+
static const struct iio_chan_spec rockchip_rk3568_saradc_iio_channels[] = {
SARADC_CHANNEL(0, "adc0", 10),
SARADC_CHANNEL(1, "adc1", 10),
@@ -325,6 +359,12 @@ static const struct of_device_id rockchip_saradc_match[] = {
.compatible = "rockchip,rk3399-saradc",
.data = &rk3399_saradc_data,
}, {
+ .compatible = "rockchip,rk3528-saradc",
+ .data = &rk3528_saradc_data,
+ }, {
+ .compatible = "rockchip,rk3562-saradc",
+ .data = &rk3562_saradc_data,
+ }, {
.compatible = "rockchip,rk3568-saradc",
.data = &rk3568_saradc_data,
}, {
diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c
index 337bc8b31b2c..54239df61d86 100644
--- a/drivers/iio/adc/rtq6056.c
+++ b/drivers/iio/adc/rtq6056.c
@@ -514,26 +514,37 @@ static int rtq6056_adc_read_avail(struct iio_dev *indio_dev,
}
}
-static int rtq6056_adc_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan, int val,
- int val2, long mask)
+static int __rtq6056_adc_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ long mask)
{
struct rtq6056_priv *priv = iio_priv(indio_dev);
const struct richtek_dev_data *devdata = priv->devdata;
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- switch (mask) {
- case IIO_CHAN_INFO_SAMP_FREQ:
- if (devdata->fixed_samp_freq)
- return -EINVAL;
- return rtq6056_adc_set_samp_freq(priv, chan, val);
- case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
- return devdata->set_average(priv, val);
- default:
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (devdata->fixed_samp_freq)
return -EINVAL;
- }
+ return rtq6056_adc_set_samp_freq(priv, chan, val);
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ return devdata->set_average(priv, val);
+ default:
+ return -EINVAL;
}
- unreachable();
+}
+
+static int rtq6056_adc_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = __rtq6056_adc_write_raw(indio_dev, chan, val, mask);
+ iio_device_release_direct(indio_dev);
+ return ret;
}
static const char *rtq6056_channel_labels[RTQ6056_MAX_CHANNEL] = {
@@ -590,9 +601,8 @@ static ssize_t shunt_resistor_store(struct device *dev,
struct rtq6056_priv *priv = iio_priv(indio_dev);
int val, val_fract, ret;
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = iio_str_to_fixpoint(buf, 100000, &val, &val_fract);
if (ret)
@@ -601,7 +611,7 @@ static ssize_t shunt_resistor_store(struct device *dev,
ret = rtq6056_set_shunt_resistor(priv, val * 1000000 + val_fract);
out_store:
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret ?: len;
}
diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
index 2201ee9987ae..0914148d1a22 100644
--- a/drivers/iio/adc/stm32-adc-core.c
+++ b/drivers/iio/adc/stm32-adc-core.c
@@ -615,8 +615,7 @@ static int stm32_adc_core_switches_probe(struct device *dev,
}
/* Booster can be used to supply analog switches (optional) */
- if (priv->cfg->has_syscfg & HAS_VBOOSTER &&
- of_property_read_bool(np, "booster-supply")) {
+ if (priv->cfg->has_syscfg & HAS_VBOOSTER) {
priv->booster = devm_regulator_get_optional(dev, "booster");
if (IS_ERR(priv->booster)) {
ret = PTR_ERR(priv->booster);
@@ -628,8 +627,7 @@ static int stm32_adc_core_switches_probe(struct device *dev,
}
/* Vdd can be used to supply analog switches (optional) */
- if (priv->cfg->has_syscfg & HAS_ANASWVDD &&
- of_property_read_bool(np, "vdd-supply")) {
+ if (priv->cfg->has_syscfg & HAS_ANASWVDD) {
priv->vdd = devm_regulator_get_optional(dev, "vdd");
if (IS_ERR(priv->vdd)) {
ret = PTR_ERR(priv->vdd);
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 9d3b23efcc06..5dbf5f136768 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -1471,9 +1471,8 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
case IIO_CHAN_INFO_PROCESSED:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
if (chan->type == IIO_VOLTAGE)
ret = stm32_adc_single_conv(indio_dev, chan, val);
else
@@ -1482,7 +1481,7 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
if (mask == IIO_CHAN_INFO_PROCESSED)
*val = STM32_ADC_VREFINT_VOLTAGE * adc->vrefint.vrefint_cal / *val;
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE:
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index fe11b0d8eab3..726ddafc9f6d 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -1275,9 +1275,8 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = stm32_dfsdm_compute_all_osrs(indio_dev, val);
if (!ret) {
@@ -1287,25 +1286,56 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
adc->oversamp = val;
adc->sample_freq = spi_freq / val;
}
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
if (!val)
return -EINVAL;
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = dfsdm_adc_set_samp_freq(indio_dev, val, spi_freq);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
}
return -EINVAL;
}
+static int __stm32_dfsdm_read_info_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ int ret = 0;
+
+ if (adc->hwc)
+ ret = iio_hw_consumer_enable(adc->hwc);
+ if (adc->backend)
+ ret = iio_backend_enable(adc->backend[chan->scan_index]);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev,
+ "%s: IIO enable failed (channel %d)\n",
+ __func__, chan->channel);
+ return ret;
+ }
+ ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
+ if (adc->hwc)
+ iio_hw_consumer_disable(adc->hwc);
+ if (adc->backend)
+ iio_backend_disable(adc->backend[chan->scan_index]);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev,
+ "%s: Conversion failed (channel %d)\n",
+ __func__, chan->channel);
+ return ret;
+ }
+
+ return 0;
+}
+
static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
@@ -1323,33 +1353,13 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = __stm32_dfsdm_read_info_raw(indio_dev, chan, val);
+ iio_device_release_direct(indio_dev);
if (ret)
return ret;
- if (adc->hwc)
- ret = iio_hw_consumer_enable(adc->hwc);
- if (adc->backend)
- ret = iio_backend_enable(adc->backend[idx]);
- if (ret < 0) {
- dev_err(&indio_dev->dev,
- "%s: IIO enable failed (channel %d)\n",
- __func__, chan->channel);
- iio_device_release_direct_mode(indio_dev);
- return ret;
- }
- ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
- if (adc->hwc)
- iio_hw_consumer_disable(adc->hwc);
- if (adc->backend)
- iio_backend_disable(adc->backend[idx]);
- if (ret < 0) {
- dev_err(&indio_dev->dev,
- "%s: Conversion failed (channel %d)\n",
- __func__, chan->channel);
- iio_device_release_direct_mode(indio_dev);
- return ret;
- }
- iio_device_release_direct_mode(indio_dev);
return IIO_VAL_INT;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c
index da16876c32ae..9c845ee01697 100644
--- a/drivers/iio/adc/ti-adc084s021.c
+++ b/drivers/iio/adc/ti-adc084s021.c
@@ -96,19 +96,18 @@ static int adc084s021_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret < 0)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = regulator_enable(adc->reg);
if (ret) {
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
}
adc->tx_buf[0] = channel->channel << 3;
ret = adc084s021_adc_conversion(adc, &be_val);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
regulator_disable(adc->reg);
if (ret < 0)
return ret;
diff --git a/drivers/iio/adc/ti-adc108s102.c b/drivers/iio/adc/ti-adc108s102.c
index 9758ac801310..7d615e2bbf39 100644
--- a/drivers/iio/adc/ti-adc108s102.c
+++ b/drivers/iio/adc/ti-adc108s102.c
@@ -181,13 +181,12 @@ static int adc108s102_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = adc108s102_scan_direct(st, chan->address);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c
index 474e733fb8e0..28aa6b80160c 100644
--- a/drivers/iio/adc/ti-adc161s626.c
+++ b/drivers/iio/adc/ti-adc161s626.c
@@ -137,13 +137,13 @@ static int ti_adc_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- ret = ti_adc_read_measurement(data, chan, val);
- if (ret)
- return ret;
- return IIO_VAL_INT;
- }
- unreachable();
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = ti_adc_read_measurement(data, chan, val);
+ iio_device_release_direct(indio_dev);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
ret = regulator_get_voltage(data->ref);
if (ret < 0)
diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c
index de019b3faa48..f120e7e21cff 100644
--- a/drivers/iio/adc/ti-ads1119.c
+++ b/drivers/iio/adc/ti-ads1119.c
@@ -336,19 +336,24 @@ static int ads1119_read_raw(struct iio_dev *indio_dev,
{
struct ads1119_state *st = iio_priv(indio_dev);
unsigned int index = chan->address;
+ int ret;
if (index >= st->num_channels_cfg)
return -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_RAW:
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev)
- return ads1119_single_conversion(st, chan, val, false);
- unreachable();
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = ads1119_single_conversion(st, chan, val, false);
+ iio_device_release_direct(indio_dev);
+ return ret;
case IIO_CHAN_INFO_OFFSET:
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev)
- return ads1119_single_conversion(st, chan, val, true);
- unreachable();
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = ads1119_single_conversion(st, chan, val, true);
+ iio_device_release_direct(indio_dev);
+ return ret;
case IIO_CHAN_INFO_SCALE:
*val = st->vref_uV / 1000;
*val /= st->channels_cfg[index].gain;
diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c
index f452f57f11c9..77c299bb4ebc 100644
--- a/drivers/iio/adc/ti-ads124s08.c
+++ b/drivers/iio/adc/ti-ads124s08.c
@@ -184,7 +184,7 @@ static int ads124s_reset(struct iio_dev *indio_dev)
if (priv->reset_gpio) {
gpiod_set_value_cansleep(priv->reset_gpio, 0);
- udelay(200);
+ fsleep(200);
gpiod_set_value_cansleep(priv->reset_gpio, 1);
} else {
return ads124s_write_cmd(indio_dev, ADS124S08_CMD_RESET);
diff --git a/drivers/iio/adc/ti-ads1298.c b/drivers/iio/adc/ti-ads1298.c
index 03f762415fa5..ae30b47e4514 100644
--- a/drivers/iio/adc/ti-ads1298.c
+++ b/drivers/iio/adc/ti-ads1298.c
@@ -319,13 +319,12 @@ static int ads1298_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = ads1298_read_one(priv, chan->scan_index);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
if (ret)
return ret;
diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c
index 91a79ebc4bde..c6096b64664e 100644
--- a/drivers/iio/adc/ti-ads131e08.c
+++ b/drivers/iio/adc/ti-ads131e08.c
@@ -505,12 +505,11 @@ static int ads131e08_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = ads131e08_read_direct(indio_dev, channel, value);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
if (ret)
return ret;
@@ -551,12 +550,11 @@ static int ads131e08_write_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = ads131e08_set_data_rate(st, value);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
return ret;
default:
diff --git a/drivers/iio/adc/ti-ads7138.c b/drivers/iio/adc/ti-ads7138.c
new file mode 100644
index 000000000000..ee5c1b8e3a8e
--- /dev/null
+++ b/drivers/iio/adc/ti-ads7138.c
@@ -0,0 +1,749 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ADS7138 - Texas Instruments Analog-to-Digital Converter
+ */
+
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/unaligned.h>
+
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+
+/*
+ * Always assume 16 bits resolution as HW registers are aligned like that and
+ * with enabled oversampling/averaging it actually corresponds to 16 bits.
+ */
+#define ADS7138_RES_BITS 16
+
+/* ADS7138 operation codes */
+#define ADS7138_OPCODE_SINGLE_WRITE 0x08
+#define ADS7138_OPCODE_SET_BIT 0x18
+#define ADS7138_OPCODE_CLEAR_BIT 0x20
+#define ADS7138_OPCODE_BLOCK_WRITE 0x28
+#define ADS7138_OPCODE_BLOCK_READ 0x30
+
+/* ADS7138 registers */
+#define ADS7138_REG_GENERAL_CFG 0x01
+#define ADS7138_REG_OSR_CFG 0x03
+#define ADS7138_REG_OPMODE_CFG 0x04
+#define ADS7138_REG_SEQUENCE_CFG 0x10
+#define ADS7138_REG_AUTO_SEQ_CH_SEL 0x12
+#define ADS7138_REG_ALERT_CH_SEL 0x14
+#define ADS7138_REG_EVENT_FLAG 0x18
+#define ADS7138_REG_EVENT_HIGH_FLAG 0x1A
+#define ADS7138_REG_EVENT_LOW_FLAG 0x1C
+#define ADS7138_REG_HIGH_TH_HYS_CH(x) ((x) * 4 + 0x20)
+#define ADS7138_REG_LOW_TH_CNT_CH(x) ((x) * 4 + 0x22)
+#define ADS7138_REG_MAX_LSB_CH(x) ((x) * 2 + 0x60)
+#define ADS7138_REG_MIN_LSB_CH(x) ((x) * 2 + 0x80)
+#define ADS7138_REG_RECENT_LSB_CH(x) ((x) * 2 + 0xA0)
+
+#define ADS7138_GENERAL_CFG_RST BIT(0)
+#define ADS7138_GENERAL_CFG_DWC_EN BIT(4)
+#define ADS7138_GENERAL_CFG_STATS_EN BIT(5)
+#define ADS7138_OSR_CFG_MASK GENMASK(2, 0)
+#define ADS7138_OPMODE_CFG_CONV_MODE BIT(5)
+#define ADS7138_OPMODE_CFG_FREQ_MASK GENMASK(4, 0)
+#define ADS7138_SEQUENCE_CFG_SEQ_MODE BIT(0)
+#define ADS7138_SEQUENCE_CFG_SEQ_START BIT(4)
+#define ADS7138_THRESHOLD_LSB_MASK GENMASK(7, 4)
+
+enum ads7138_modes {
+ ADS7138_MODE_MANUAL,
+ ADS7138_MODE_AUTO,
+};
+
+struct ads7138_chip_data {
+ const char *name;
+ const int channel_num;
+};
+
+struct ads7138_data {
+ /* Protects RMW access to the I2C interface */
+ struct mutex lock;
+ struct i2c_client *client;
+ struct regulator *vref_regu;
+ const struct ads7138_chip_data *chip_data;
+};
+
+/*
+ * 2D array of available sampling frequencies and the corresponding register
+ * values. Structured like this to be easily usable in read_avail function.
+ */
+static const int ads7138_samp_freqs_bits[2][26] = {
+ {
+ 163, 244, 326, 488, 651, 977, 1302, 1953,
+ 2604, 3906, 5208, 7813, 10417, 15625, 20833, 31250,
+ 41667, 62500, 83333, 125000, 166667, 250000, 333333, 500000,
+ 666667, 1000000
+ }, {
+ 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
+ 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
+ /* Here is a hole, due to duplicate frequencies */
+ 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02,
+ 0x01, 0x00
+ }
+};
+
+static const int ads7138_oversampling_ratios[] = {
+ 1, 2, 4, 8, 16, 32, 64, 128
+};
+
+static int ads7138_i2c_write_block(const struct i2c_client *client, u8 reg,
+ u8 *values, u8 length)
+{
+ int ret;
+ int len = length + 2; /* "+ 2" for OPCODE and reg */
+
+ u8 *buf __free(kfree) = kmalloc(len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = ADS7138_OPCODE_BLOCK_WRITE;
+ buf[1] = reg;
+ memcpy(&buf[2], values, length);
+
+ ret = i2c_master_send(client, buf, len);
+ if (ret < 0)
+ return ret;
+ if (ret != len)
+ return -EIO;
+
+ return 0;
+}
+
+static int ads7138_i2c_write_with_opcode(const struct i2c_client *client,
+ u8 reg, u8 regval, u8 opcode)
+{
+ u8 buf[3] = { opcode, reg, regval };
+ int ret;
+
+ ret = i2c_master_send(client, buf, ARRAY_SIZE(buf));
+ if (ret < 0)
+ return ret;
+ if (ret != ARRAY_SIZE(buf))
+ return -EIO;
+
+ return 0;
+}
+
+static int ads7138_i2c_write(const struct i2c_client *client, u8 reg, u8 value)
+{
+ return ads7138_i2c_write_with_opcode(client, reg, value,
+ ADS7138_OPCODE_SINGLE_WRITE);
+}
+
+static int ads7138_i2c_set_bit(const struct i2c_client *client, u8 reg, u8 bits)
+{
+ return ads7138_i2c_write_with_opcode(client, reg, bits,
+ ADS7138_OPCODE_SET_BIT);
+}
+
+static int ads7138_i2c_clear_bit(const struct i2c_client *client, u8 reg, u8 bits)
+{
+ return ads7138_i2c_write_with_opcode(client, reg, bits,
+ ADS7138_OPCODE_CLEAR_BIT);
+}
+
+static int ads7138_i2c_read_block(const struct i2c_client *client, u8 reg,
+ u8 *out_values, u8 length)
+{
+ u8 buf[2] = { ADS7138_OPCODE_BLOCK_READ, reg };
+ int ret;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .len = ARRAY_SIZE(buf),
+ .buf = buf,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = out_values,
+ },
+ };
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
+ return ret;
+ if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ return 0;
+}
+
+static int ads7138_i2c_read(const struct i2c_client *client, u8 reg)
+{
+ u8 value;
+ int ret;
+
+ ret = ads7138_i2c_read_block(client, reg, &value, sizeof(value));
+ if (ret)
+ return ret;
+ return value;
+}
+
+static int ads7138_freq_to_bits(int freq)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ads7138_samp_freqs_bits[0]); i++)
+ if (freq == ads7138_samp_freqs_bits[0][i])
+ return ads7138_samp_freqs_bits[1][i];
+
+ return -EINVAL;
+}
+
+static int ads7138_bits_to_freq(int bits)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ads7138_samp_freqs_bits[1]); i++)
+ if (bits == ads7138_samp_freqs_bits[1][i])
+ return ads7138_samp_freqs_bits[0][i];
+
+ return -EINVAL;
+}
+
+static int ads7138_osr_to_bits(int osr)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ads7138_oversampling_ratios); i++)
+ if (osr == ads7138_oversampling_ratios[i])
+ return i;
+
+ return -EINVAL;
+}
+
+static int ads7138_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct ads7138_data *data = iio_priv(indio_dev);
+ int ret, vref, bits;
+ u8 values[2];
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ads7138_i2c_read_block(data->client,
+ ADS7138_REG_RECENT_LSB_CH(chan->channel),
+ values, ARRAY_SIZE(values));
+ if (ret)
+ return ret;
+
+ *val = get_unaligned_le16(values);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_PEAK:
+ ret = ads7138_i2c_read_block(data->client,
+ ADS7138_REG_MAX_LSB_CH(chan->channel),
+ values, ARRAY_SIZE(values));
+ if (ret)
+ return ret;
+
+ *val = get_unaligned_le16(values);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_TROUGH:
+ ret = ads7138_i2c_read_block(data->client,
+ ADS7138_REG_MIN_LSB_CH(chan->channel),
+ values, ARRAY_SIZE(values));
+ if (ret)
+ return ret;
+
+ *val = get_unaligned_le16(values);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = ads7138_i2c_read(data->client, ADS7138_REG_OPMODE_CFG);
+ if (ret < 0)
+ return ret;
+
+ bits = FIELD_GET(ADS7138_OPMODE_CFG_FREQ_MASK, ret);
+ *val = ads7138_bits_to_freq(bits);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ vref = regulator_get_voltage(data->vref_regu);
+ if (vref < 0)
+ return vref;
+ *val = vref / 1000;
+ *val2 = ADS7138_RES_BITS;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ ret = ads7138_i2c_read(data->client, ADS7138_REG_OSR_CFG);
+ if (ret < 0)
+ return ret;
+
+ bits = FIELD_GET(ADS7138_OSR_CFG_MASK, ret);
+ *val = ads7138_oversampling_ratios[bits];
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ads7138_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct ads7138_data *data = iio_priv(indio_dev);
+ int bits, ret;
+ u8 value;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ bits = ads7138_freq_to_bits(val);
+ if (bits < 0)
+ return bits;
+
+ guard(mutex)(&data->lock);
+ ret = ads7138_i2c_read(data->client, ADS7138_REG_OPMODE_CFG);
+ if (ret < 0)
+ return ret;
+
+ value = ret & ~ADS7138_OPMODE_CFG_FREQ_MASK;
+ value |= FIELD_PREP(ADS7138_OPMODE_CFG_FREQ_MASK, bits);
+ return ads7138_i2c_write(data->client, ADS7138_REG_OPMODE_CFG,
+ value);
+ }
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ bits = ads7138_osr_to_bits(val);
+ if (bits < 0)
+ return bits;
+
+ return ads7138_i2c_write(data->client, ADS7138_REG_OSR_CFG,
+ bits);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ads7138_read_event(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info, int *val, int *val2)
+{
+ struct ads7138_data *data = iio_priv(indio_dev);
+ u8 reg, values[2];
+ int ret;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ reg = (dir == IIO_EV_DIR_RISING) ?
+ ADS7138_REG_HIGH_TH_HYS_CH(chan->channel) :
+ ADS7138_REG_LOW_TH_CNT_CH(chan->channel);
+ ret = ads7138_i2c_read_block(data->client, reg, values,
+ ARRAY_SIZE(values));
+ if (ret)
+ return ret;
+
+ *val = ((values[1] << 4) | (values[0] >> 4));
+ return IIO_VAL_INT;
+ case IIO_EV_INFO_HYSTERESIS:
+ ret = ads7138_i2c_read(data->client,
+ ADS7138_REG_HIGH_TH_HYS_CH(chan->channel));
+ if (ret < 0)
+ return ret;
+
+ *val = ret & ~ADS7138_THRESHOLD_LSB_MASK;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ads7138_write_event(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info, int val, int val2)
+{
+ struct ads7138_data *data = iio_priv(indio_dev);
+ u8 reg, values[2];
+ int ret;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE: {
+ if (val >= BIT(12) || val < 0)
+ return -EINVAL;
+
+ reg = (dir == IIO_EV_DIR_RISING) ?
+ ADS7138_REG_HIGH_TH_HYS_CH(chan->channel) :
+ ADS7138_REG_LOW_TH_CNT_CH(chan->channel);
+
+ guard(mutex)(&data->lock);
+ ret = ads7138_i2c_read(data->client, reg);
+ if (ret < 0)
+ return ret;
+
+ values[0] = ret & ~ADS7138_THRESHOLD_LSB_MASK;
+ values[0] |= FIELD_PREP(ADS7138_THRESHOLD_LSB_MASK, val);
+ values[1] = (val >> 4);
+ return ads7138_i2c_write_block(data->client, reg, values,
+ ARRAY_SIZE(values));
+ }
+ case IIO_EV_INFO_HYSTERESIS: {
+ if (val >= BIT(4) || val < 0)
+ return -EINVAL;
+
+ reg = ADS7138_REG_HIGH_TH_HYS_CH(chan->channel);
+
+ guard(mutex)(&data->lock);
+ ret = ads7138_i2c_read(data->client, reg);
+ if (ret < 0)
+ return ret;
+
+ values[0] = val & ~ADS7138_THRESHOLD_LSB_MASK;
+ values[0] |= FIELD_PREP(ADS7138_THRESHOLD_LSB_MASK, ret >> 4);
+ return ads7138_i2c_write(data->client, reg, values[0]);
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ads7138_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct ads7138_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (dir != IIO_EV_DIR_EITHER)
+ return -EINVAL;
+
+ ret = ads7138_i2c_read(data->client, ADS7138_REG_ALERT_CH_SEL);
+ if (ret < 0)
+ return ret;
+
+ return (ret & BIT(chan->channel)) ? 1 : 0;
+}
+
+static int ads7138_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir, bool state)
+{
+ struct ads7138_data *data = iio_priv(indio_dev);
+
+ if (dir != IIO_EV_DIR_EITHER)
+ return -EINVAL;
+
+ if (state)
+ return ads7138_i2c_set_bit(data->client,
+ ADS7138_REG_ALERT_CH_SEL,
+ BIT(chan->channel));
+ else
+ return ads7138_i2c_clear_bit(data->client,
+ ADS7138_REG_ALERT_CH_SEL,
+ BIT(chan->channel));
+}
+
+static int ads7138_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:
+ *vals = ads7138_samp_freqs_bits[0];
+ *length = ARRAY_SIZE(ads7138_samp_freqs_bits[0]);
+ *type = IIO_VAL_INT;
+
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *vals = ads7138_oversampling_ratios;
+ *length = ARRAY_SIZE(ads7138_oversampling_ratios);
+ *type = IIO_VAL_INT;
+
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ti_ads7138_info = {
+ .read_raw = &ads7138_read_raw,
+ .read_avail = &ads7138_read_avail,
+ .write_raw = &ads7138_write_raw,
+ .read_event_value = &ads7138_read_event,
+ .write_event_value = &ads7138_write_event,
+ .read_event_config = &ads7138_read_event_config,
+ .write_event_config = &ads7138_write_event_config,
+};
+
+static const struct iio_event_spec ads7138_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE)
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_HYSTERESIS) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+#define ADS7138_V_CHAN(_chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_PEAK) | \
+ BIT(IIO_CHAN_INFO_TROUGH), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .datasheet_name = "AIN"#_chan, \
+ .event_spec = ads7138_events, \
+ .num_event_specs = ARRAY_SIZE(ads7138_events), \
+}
+
+static const struct iio_chan_spec ads7138_channels[] = {
+ ADS7138_V_CHAN(0),
+ ADS7138_V_CHAN(1),
+ ADS7138_V_CHAN(2),
+ ADS7138_V_CHAN(3),
+ ADS7138_V_CHAN(4),
+ ADS7138_V_CHAN(5),
+ ADS7138_V_CHAN(6),
+ ADS7138_V_CHAN(7),
+};
+
+static irqreturn_t ads7138_event_handler(int irq, void *priv)
+{
+ struct iio_dev *indio_dev = priv;
+ struct ads7138_data *data = iio_priv(indio_dev);
+ struct device *dev = &data->client->dev;
+ u8 i, events_high, events_low;
+ u64 code;
+ int ret;
+
+ /* Check if interrupt was trigger by us */
+ ret = ads7138_i2c_read(data->client, ADS7138_REG_EVENT_FLAG);
+ if (ret <= 0)
+ return IRQ_NONE;
+
+ ret = ads7138_i2c_read(data->client, ADS7138_REG_EVENT_HIGH_FLAG);
+ if (ret < 0) {
+ dev_warn(dev, "Failed to read event high flags: %d\n", ret);
+ return IRQ_HANDLED;
+ }
+ events_high = ret;
+
+ ret = ads7138_i2c_read(data->client, ADS7138_REG_EVENT_LOW_FLAG);
+ if (ret < 0) {
+ dev_warn(dev, "Failed to read event low flags: %d\n", ret);
+ return IRQ_HANDLED;
+ }
+ events_low = ret;
+
+ for (i = 0; i < data->chip_data->channel_num; i++) {
+ if (events_high & BIT(i)) {
+ code = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING);
+ iio_push_event(indio_dev, code,
+ iio_get_time_ns(indio_dev));
+ }
+ if (events_low & BIT(i)) {
+ code = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING);
+ iio_push_event(indio_dev, code,
+ iio_get_time_ns(indio_dev));
+ }
+ }
+
+ /* Try to clear all interrupt flags */
+ ret = ads7138_i2c_write(data->client, ADS7138_REG_EVENT_HIGH_FLAG, 0xFF);
+ if (ret)
+ dev_warn(dev, "Failed to clear event high flags: %d\n", ret);
+
+ ret = ads7138_i2c_write(data->client, ADS7138_REG_EVENT_LOW_FLAG, 0xFF);
+ if (ret)
+ dev_warn(dev, "Failed to clear event low flags: %d\n", ret);
+
+ return IRQ_HANDLED;
+}
+
+static int ads7138_set_conv_mode(struct ads7138_data *data,
+ enum ads7138_modes mode)
+{
+ if (mode == ADS7138_MODE_AUTO)
+ return ads7138_i2c_set_bit(data->client, ADS7138_REG_OPMODE_CFG,
+ ADS7138_OPMODE_CFG_CONV_MODE);
+ return ads7138_i2c_clear_bit(data->client, ADS7138_REG_OPMODE_CFG,
+ ADS7138_OPMODE_CFG_CONV_MODE);
+}
+
+static int ads7138_init_hw(struct ads7138_data *data)
+{
+ struct device *dev = &data->client->dev;
+ int ret;
+
+ data->vref_regu = devm_regulator_get(dev, "avdd");
+ if (IS_ERR(data->vref_regu))
+ return dev_err_probe(dev, PTR_ERR(data->vref_regu),
+ "Failed to get avdd regulator\n");
+
+ ret = regulator_get_voltage(data->vref_regu);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to get avdd voltage\n");
+
+ /* Reset the chip to get a defined starting configuration */
+ ret = ads7138_i2c_set_bit(data->client, ADS7138_REG_GENERAL_CFG,
+ ADS7138_GENERAL_CFG_RST);
+ if (ret)
+ return ret;
+
+ ret = ads7138_set_conv_mode(data, ADS7138_MODE_AUTO);
+ if (ret)
+ return ret;
+
+ /* Enable statistics and digital window comparator */
+ ret = ads7138_i2c_set_bit(data->client, ADS7138_REG_GENERAL_CFG,
+ ADS7138_GENERAL_CFG_STATS_EN |
+ ADS7138_GENERAL_CFG_DWC_EN);
+ if (ret)
+ return ret;
+
+ /* Enable all channels for auto sequencing */
+ ret = ads7138_i2c_set_bit(data->client, ADS7138_REG_AUTO_SEQ_CH_SEL, 0xFF);
+ if (ret)
+ return ret;
+
+ /* Set auto sequence mode and start sequencing */
+ return ads7138_i2c_set_bit(data->client, ADS7138_REG_SEQUENCE_CFG,
+ ADS7138_SEQUENCE_CFG_SEQ_START |
+ ADS7138_SEQUENCE_CFG_SEQ_MODE);
+}
+
+static int ads7138_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct iio_dev *indio_dev;
+ struct ads7138_data *data;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->client = client;
+ data->chip_data = i2c_get_match_data(client);
+ if (!data->chip_data)
+ return -ENODEV;
+
+ ret = devm_mutex_init(dev, &data->lock);
+ if (ret)
+ return ret;
+
+ indio_dev->name = data->chip_data->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ads7138_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ads7138_channels);
+ indio_dev->info = &ti_ads7138_info;
+
+ i2c_set_clientdata(client, indio_dev);
+
+ if (client->irq > 0) {
+ ret = devm_request_threaded_irq(dev, client->irq,
+ NULL, ads7138_event_handler,
+ IRQF_TRIGGER_LOW |
+ IRQF_ONESHOT | IRQF_SHARED,
+ client->name, indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ ret = ads7138_init_hw(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to initialize device\n");
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register iio device\n");
+
+ return 0;
+}
+
+static int ads7138_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ads7138_data *data = iio_priv(indio_dev);
+
+ return ads7138_set_conv_mode(data, ADS7138_MODE_MANUAL);
+}
+
+static int ads7138_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ads7138_data *data = iio_priv(indio_dev);
+
+ return ads7138_set_conv_mode(data, ADS7138_MODE_AUTO);
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(ads7138_pm_ops,
+ ads7138_runtime_suspend,
+ ads7138_runtime_resume,
+ NULL);
+
+static const struct ads7138_chip_data ads7128_data = {
+ .name = "ads7128",
+ .channel_num = 8,
+};
+
+static const struct ads7138_chip_data ads7138_data = {
+ .name = "ads7138",
+ .channel_num = 8,
+};
+
+static const struct of_device_id ads7138_of_match[] = {
+ { .compatible = "ti,ads7128", .data = &ads7128_data },
+ { .compatible = "ti,ads7138", .data = &ads7138_data },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ads7138_of_match);
+
+static const struct i2c_device_id ads7138_device_ids[] = {
+ { "ads7128", (kernel_ulong_t)&ads7128_data },
+ { "ads7138", (kernel_ulong_t)&ads7138_data },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ads7138_device_ids);
+
+static struct i2c_driver ads7138_driver = {
+ .driver = {
+ .name = "ads7138",
+ .of_match_table = ads7138_of_match,
+ .pm = pm_ptr(&ads7138_pm_ops),
+ },
+ .id_table = ads7138_device_ids,
+ .probe = ads7138_probe,
+};
+module_i2c_driver(ads7138_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tobias Sperling <tobias.sperling@softing.com>");
+MODULE_DESCRIPTION("Driver for TI ADS7138 ADCs");
diff --git a/drivers/iio/adc/ti-ads7924.c b/drivers/iio/adc/ti-ads7924.c
index 66b54c0d75aa..b1f745f75dbe 100644
--- a/drivers/iio/adc/ti-ads7924.c
+++ b/drivers/iio/adc/ti-ads7924.c
@@ -251,11 +251,8 @@ static const struct iio_info ads7924_info = {
.read_raw = ads7924_read_raw,
};
-static int ads7924_get_channels_config(struct i2c_client *client,
- struct iio_dev *indio_dev)
+static int ads7924_get_channels_config(struct device *dev)
{
- struct ads7924_data *priv = iio_priv(indio_dev);
- struct device *dev = priv->dev;
struct fwnode_handle *node;
int num_channels = 0;
@@ -380,7 +377,7 @@ static int ads7924_probe(struct i2c_client *client)
indio_dev->num_channels = ARRAY_SIZE(ads7924_channels);
indio_dev->info = &ads7924_info;
- ret = ads7924_get_channels_config(client, indio_dev);
+ ret = ads7924_get_channels_config(dev);
if (ret < 0)
return dev_err_probe(dev, ret,
"failed to get channels configuration\n");
diff --git a/drivers/iio/adc/ti-tlc4541.c b/drivers/iio/adc/ti-tlc4541.c
index 08de997584fd..5a138be983ed 100644
--- a/drivers/iio/adc/ti-tlc4541.c
+++ b/drivers/iio/adc/ti-tlc4541.c
@@ -131,11 +131,10 @@ static int tlc4541_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = spi_sync(st->spi, &st->scan_single_msg);
- iio_device_release_direct_mode(indio_dev);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
*val = be16_to_cpu(st->rx_buf[0]);
diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c
index 7dde5713973f..49560059f4b7 100644
--- a/drivers/iio/adc/ti-tsc2046.c
+++ b/drivers/iio/adc/ti-tsc2046.c
@@ -812,9 +812,7 @@ static int tsc2046_adc_probe(struct spi_device *spi)
spin_lock_init(&priv->state_lock);
priv->state = TSC2046_STATE_SHUTDOWN;
- hrtimer_init(&priv->trig_timer, CLOCK_MONOTONIC,
- HRTIMER_MODE_REL_SOFT);
- priv->trig_timer.function = tsc2046_adc_timer;
+ hrtimer_setup(&priv->trig_timer, tsc2046_adc_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
ret = devm_iio_trigger_register(dev, trig);
if (ret) {
diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c
index cfbfcaefec0f..e1f8740ae688 100644
--- a/drivers/iio/adc/xilinx-xadc-core.c
+++ b/drivers/iio/adc/xilinx-xadc-core.c
@@ -1245,8 +1245,8 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, unsigned int *conf, int irq)
channel_templates = xadc_us_channels;
max_channels = ARRAY_SIZE(xadc_us_channels);
}
- channels = devm_kmemdup(dev, channel_templates,
- sizeof(channels[0]) * max_channels, GFP_KERNEL);
+ channels = devm_kmemdup_array(dev, channel_templates, max_channels,
+ sizeof(*channel_templates), GFP_KERNEL);
if (!channels)
return -ENOMEM;