From: Ana-Maria Cusco <ana-maria.cusco@xxxxxxxxxx> From: Ana-Maria Cusco <ana-maria.cusco@xxxxxxxxxx> Add support for the AD4170 ADC with the following features: - Single-shot read (read_raw), scale, sampling freq - Multi channel buffer support - Buffered capture in triggered mode - Gain and offset calibration - chop_iexc and chop_adc device configuration - Powerdown switch configuration Co-developed-by: Dragos Bogdan <dragos.bogdan@xxxxxxxxxx> Signed-off-by: Dragos Bogdan <dragos.bogdan@xxxxxxxxxx> Signed-off-by: Ana-Maria Cusco <ana-maria.cusco@xxxxxxxxxx> Co-developed-by: Marcelo Schmitt <marcelo.schmitt@xxxxxxxxxx> Signed-off-by: Marcelo Schmitt <marcelo.schmitt@xxxxxxxxxx> --- drivers/iio/adc/Kconfig | 16 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad4170.c | 2049 ++++++++++++++++++++++++++++++++++++++ drivers/iio/adc/ad4170.h | 316 ++++++ 4 files changed, 2382 insertions(+) create mode 100644 drivers/iio/adc/ad4170.c create mode 100644 drivers/iio/adc/ad4170.h diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 849c90203071..997473ac958f 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -48,6 +48,22 @@ config AD4130 To compile this driver as a module, choose M here: the module will be called ad4130. + +config AD4170 + tristate "Analog Device AD4170 ADC Driver" + depends on SPI + depends on GPIOLIB + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select REGMAP_SPI + depends on COMMON_CLK + help + Say yes here to build support for Analog Devices AD4170 SPI analog + to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad4170. + config AD4695 tristate "Analog Device AD4695 ADC Driver" depends on SPI diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index ee19afba62b7..64c72b595580 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o obj-$(CONFIG_AD4000) += ad4000.o obj-$(CONFIG_AD4130) += ad4130.o +obj-$(CONFIG_AD4170) += ad4170.o obj-$(CONFIG_AD4695) += ad4695.o obj-$(CONFIG_AD7091R) += ad7091r-base.o obj-$(CONFIG_AD7091R5) += ad7091r5.o diff --git a/drivers/iio/adc/ad4170.c b/drivers/iio/adc/ad4170.c new file mode 100644 index 000000000000..20b74575f2cb --- /dev/null +++ b/drivers/iio/adc/ad4170.c @@ -0,0 +1,2049 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2024 Analog Devices, Inc. + * Author: Ana-Maria Cusco <ana-maria.cusco@xxxxxxxxxx> + * Author: Marcelo Schmitt <marcelo.schmitt@xxxxxxxxxx> + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/driver.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/units.h> +#include <linux/util_macros.h> + +#include <asm/div64.h> +#include <linux/unaligned.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/kfifo_buf.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> +#include "ad4170.h" + +#define AD4170_SINC5_AVG_CONF 0x0 +#define AD4170_SINC5_CONF 0x4 +#define AD4170_SINC3_CONF 0x6 + +enum ad4170_regulator { + AD4170_AVDD_SUP, + AD4170_AVSS_SUP, + AD4170_IOVDD_SUP, + AD4170_REFIN1P_SUP, + AD4170_REFIN1N_SUP, + AD4170_REFIN2P_SUP, + AD4170_REFIN2N_SUP, +}; + +static const char *const ad4170_clk_sel[] = { + "ext-clk", "xtal" +}; + +static const char *const ad4170_i_out_pin_dt_props[] = { + "adi,excitation-pin-0", + "adi,excitation-pin-1", + "adi,excitation-pin-2", + "adi,excitation-pin-3", +}; + +static const char *const ad4170_i_out_val_dt_props[] = { + "adi,excitation-current-0-microamp", + "adi,excitation-current-1-microamp", + "adi,excitation-current-2-microamp", + "adi,excitation-current-3-microamp", +}; + +#define AD4170_SINC5_FS_PADDING 8 +#define AD4170_SINC3_FS_OFFSET 2 + +#define AD4170_SINC3_MIN_FS 4 +#define AD4170_SINC3_MAX_FS 65532 +#define AD4170_SINC5_MIN_FS 1 +#define AD4170_SINC5_MAX_FS 256 + +/* Make separate fs tables? One for SINC5 other for SINC5+AVG and SINC3? */ +static const unsigned int ad4170_filt_fs_tbl[] = { + 1, /* SINC5 minimum filter_fs */ + 2, + 4, /* SINC5+AVG and SINC3 minimum filter_fs */ + 8, + 12, + 16, + 20, + 40, + 48, + 80, + 100, + 256, /* SINC5 maximum filter_fs */ + 500, + 1000, + 5000, + 8332, + 10000, + 25000, + 50000, + 65532 /* SINC5+AVG and SINC3 maximum filter_fs */ +}; + +/* + * There are 8 of each MISC, AFE, FILTER, FILTER_FS, OFFSET, and GAIN + * configuration registers. That is, there are 8 miscellaneous registers, MISC0 + * to MISC7. Each MISC register is associated with a setup; MISCN is associated + * with setup number N. The other 5 above mentioned types of registers have + * analogous structure. A setup is a set of those registers. For example, + * setup 1 comprises of MISC1, AFE1, FILTER1, FILTER_FS1, OFFSET1, and GAIN1 + * registers. Also, there are 16 CHANNEL_SETUP registers (CHANNEL_SETUP0 to + * CHANNEL_SETUP15). Each channel setup is associated with one of the 8 possible + * setups. Thus, AD4170 can support up to 16 channels but, since there are only + * 8 available setups, channels must share settings if more than 8 channels are + * configured. + */ +struct ad4170_setup { + u16 misc; + u16 afe; + u16 filter; + u16 filter_fs; + u32 offset; /* For calibration purposes */ + u32 gain; /* For calibration purposes */ +}; + +struct ad4170_chan_info { + int setup_num; + int input_range_uv; + u32 scale_tbl[10][2]; + int offset_tbl[10]; + bool enabled; +}; + +static const char * const ad4170_filt_names[] = { + [AD4170_SINC5_AVG] = "sinc5+avg", + [AD4170_SINC5] = "sinc5", + [AD4170_SINC3] = "sinc3", +}; + +struct ad4170_state { + struct regmap *regmap; + struct spi_device *spi; + struct regulator_bulk_data supplies[7]; + struct mutex lock; /* Protect filter, PGA, GPIO, chan read, chan config */ + struct iio_chan_spec chans[AD4170_MAX_CHANNELS]; + struct ad4170_chan_info chans_info[AD4170_MAX_CHANNELS]; + struct ad4170_setup setups[AD4170_MAX_SETUPS]; + u32 mclk_hz; + enum ad4170_pin_function pins_fn[AD4170_NUM_ANALOG_PINS]; + enum ad4170_gpio_function gpio_fn[AD4170_NUM_GPIO_PINS]; + unsigned int clock_ctrl; + struct clk *ext_clk; + struct clk_hw int_clk_hw; + int sps_tbl[ARRAY_SIZE(ad4170_filt_names)][ARRAY_SIZE(ad4170_filt_fs_tbl)][2]; + struct completion completion; + struct iio_trigger *trig; + u32 data[AD4170_MAX_CHANNELS]; + + struct spi_transfer xfer; + struct spi_message msg; + /* + * DMA (thus cache coherency maintenance) requires the transfer buffers + * to live in their own cache lines. + */ + u8 reg_write_tx_buf[6]; + __be32 reg_read_rx_buf __aligned(IIO_DMA_MINALIGN); + __be16 reg_read_tx_buf; +}; + +static void ad4170_fill_sps_tbl(struct ad4170_state *st) +{ + unsigned int tmp0, tmp1, i; + + /* + * The ODR can be calculated the same way for sinc5+avg, sinc5, and + * sinc3 filter types with the exception that sinc5 filter has a + * narrowed range of allowed FILTER_FS values. + */ + for (i = AD4170_SINC3_FS_OFFSET; i < ARRAY_SIZE(ad4170_filt_fs_tbl); i++) { + tmp0 = div_u64_rem(st->mclk_hz, 32 * ad4170_filt_fs_tbl[i], &tmp1); + tmp1 = mult_frac(tmp1, MICRO, 32 * ad4170_filt_fs_tbl[i]); + // sinc5+avg and sinc3 havethe same SPS table. Drop one of them? + /* Fill sinc5+avg filter SPS table */ + st->sps_tbl[AD4170_SINC5_AVG][i][0] = tmp0; /* Integer part */ + st->sps_tbl[AD4170_SINC5_AVG][i][1] = tmp1; /* Fractional part */ + + /* Fill sinc3 filter SPS table */ + st->sps_tbl[AD4170_SINC3][i][0] = tmp0; /* Integer part */ + st->sps_tbl[AD4170_SINC3][i][1] = tmp1; /* Fractional part */ + } + /* Sinc5 filter ODR doesn't use all FILTER_FS bits */ + for (i = 0; i < ARRAY_SIZE(ad4170_filt_fs_tbl) - AD4170_SINC5_FS_PADDING; i++) { + tmp0 = div_u64_rem(st->mclk_hz, 32 * ad4170_filt_fs_tbl[i], &tmp1); + tmp1 = mult_frac(tmp1, MICRO, 32 * ad4170_filt_fs_tbl[i]); + /* Fill sinc5 filter SPS table */ + st->sps_tbl[AD4170_SINC5][i][0] = tmp0; /* Integer part */ + st->sps_tbl[AD4170_SINC5][i][1] = tmp1; /* Fractional part */ + } +} + +static int ad4170_get_reg_size(struct ad4170_state *st, unsigned int reg, + unsigned int *size) +{ + if (reg >= ARRAY_SIZE(ad4170_reg_size)) + return -EINVAL; + + if (!ad4170_reg_size[reg]) + return -EINVAL; + + *size = ad4170_reg_size[reg]; + + return 0; +} + +static int ad4170_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct ad4170_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static int ad4170_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct ad4170_state *st = context; + unsigned int size, addr; + int ret; + + ret = ad4170_get_reg_size(st, reg, &size); + if (ret) + return ret; + + addr = reg + size - 1; + put_unaligned_be16(addr, &st->reg_write_tx_buf[0]); + + switch (size) { + case 4: + put_unaligned_be32(val, &st->reg_write_tx_buf[2]); + break; + case 3: + put_unaligned_be24(val, &st->reg_write_tx_buf[2]); + break; + case 2: + put_unaligned_be16(val, &st->reg_write_tx_buf[2]); + break; + case 1: + st->reg_write_tx_buf[2] = val; + break; + default: + return -EINVAL; + } + + return spi_write(st->spi, st->reg_write_tx_buf, size + 2); +} + +static int ad4170_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct ad4170_state *st = context; + struct spi_transfer t[] = { + { + .tx_buf = &st->reg_read_tx_buf, + .len = 2, + }, + { + .rx_buf = &st->reg_read_rx_buf, + }, + }; + unsigned int size, addr; + int ret; + + ret = ad4170_get_reg_size(st, reg, &size); + if (ret) + return ret; + + addr = reg + size - 1; + put_unaligned_be16(AD4170_READ_MASK | addr, &st->reg_read_tx_buf); + + t[1].len = size; + + ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t)); + if (ret) + return ret; + + switch (size) { + case 4: + *val = get_unaligned_be32(&st->reg_read_rx_buf); + break; + case 3: + *val = get_unaligned_be24(&st->reg_read_rx_buf); + break; + case 2: + *val = get_unaligned_be16(&st->reg_read_rx_buf); + break; + case 1: + *val = st->reg_read_rx_buf; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct regmap_config ad4170_regmap_config = { + .reg_bits = 14, + .val_bits = 32, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .reg_read = ad4170_reg_read, + .reg_write = ad4170_reg_write, +}; + +static int ad4170_set_mode(struct ad4170_state *st, unsigned int mode) +{ + return regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_REG_CTRL_MODE_MSK, + FIELD_PREP(AD4170_REG_CTRL_MODE_MSK, mode)); +} + +/* 8 possible setups (0-7)*/ +static int ad4170_write_setup(struct ad4170_state *st, unsigned int setup_num, + struct ad4170_setup *setup) +{ + int ret; + + /* + * It is recommended to place the ADC in standby mode or idle mode to + * write to OFFSET and GAIN registers. + */ + ret = ad4170_set_mode(st, AD4170_ADC_CTRL_MODE_IDLE); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4170_MISC_REG(setup_num), setup->misc); + if (ret < 0) + return ret; + + ret = regmap_write(st->regmap, AD4170_AFE_REG(setup_num), setup->afe); + if (ret < 0) + return ret; + + ret = regmap_write(st->regmap, AD4170_FILTER_REG(setup_num), + setup->filter); + if (ret < 0) + return ret; + + ret = regmap_write(st->regmap, AD4170_FILTER_FS_REG(setup_num), + setup->filter_fs); + if (ret < 0) + return ret; + + ret = regmap_write(st->regmap, AD4170_OFFSET_REG(setup_num), + setup->offset); + if (ret < 0) + return ret; + + ret = regmap_write(st->regmap, AD4170_GAIN_REG(setup_num), setup->gain); + if (ret < 0) + return ret; + + return 0; +} + +static int ad4170_filter_to_filter_type(unsigned int filter) +{ + u16 f_conf = FIELD_GET(AD4170_SETUPS_FILTER_TYPE_MSK, filter); + + switch (f_conf) { + case AD4170_SINC5_AVG_CONF: + return AD4170_SINC5_AVG; + case AD4170_SINC5_CONF: + return AD4170_SINC5; + case AD4170_SINC3_CONF: + return AD4170_SINC3; + default: + return -EINVAL; + } +} + +static int ad4170_set_filter_type(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int val) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct ad4170_chan_info *chan_info = &st->chans_info[chan->address]; + struct ad4170_setup *setup = &st->setups[chan_info->setup_num]; + unsigned int old_filter_fs, old_filter, filter_type_conf; + int ret = 0; + + switch (val) { + case AD4170_SINC5_AVG: + filter_type_conf = AD4170_SINC5_AVG_CONF; + break; + case AD4170_SINC5: + filter_type_conf = AD4170_SINC5_CONF; + break; + case AD4170_SINC3: + filter_type_conf = AD4170_SINC3_CONF; + break; + default: + return -EINVAL; + } + + /* + * The filters provide the same ODR for a given filter_fs value but + * there are different minimum and maximum filter_fs limits for each + * filter. The filter_fs value will be adjusted if the current filter_fs + * is out of the limits of the just requested filter. Since the + * filter_fs value affects the ODR (sampling_frequency), changing the + * filter may lead to a change in the sampling frequency. + */ + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { + old_filter = setup->filter; + old_filter_fs = setup->filter_fs; + if (val == AD4170_SINC5_AVG || val == AD4170_SINC3) { + if (setup->filter_fs < AD4170_SINC3_MIN_FS) + setup->filter_fs = AD4170_SINC3_MIN_FS; + if (setup->filter_fs > AD4170_SINC3_MAX_FS) + setup->filter_fs = AD4170_SINC3_MAX_FS; + + } else if (val == AD4170_SINC5) { + if (setup->filter_fs < AD4170_SINC5_MIN_FS) + setup->filter_fs = AD4170_SINC5_MIN_FS; + if (setup->filter_fs > AD4170_SINC5_MAX_FS) + setup->filter_fs = AD4170_SINC5_MAX_FS; + } + + setup->filter &= ~AD4170_SETUPS_FILTER_TYPE_MSK; + setup->filter |= FIELD_PREP(AD4170_SETUPS_FILTER_TYPE_MSK, + filter_type_conf); + + guard(mutex)(&st->lock); + ret = ad4170_write_setup(st, chan_info->setup_num, setup); + if (ret) { + setup->filter = old_filter; + setup->filter_fs = old_filter_fs; + } + return ret; + } + unreachable(); +} + +static int ad4170_get_filter_type(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct ad4170_chan_info *chan_info = &st->chans_info[chan->address]; + struct ad4170_setup *setup = &st->setups[chan_info->setup_num]; + + return ad4170_filter_to_filter_type(setup->filter); +} + +static const struct iio_enum ad4170_filter_type_enum = { + .items = ad4170_filt_names, + .num_items = ARRAY_SIZE(ad4170_filt_names), + .get = ad4170_get_filter_type, + .set = ad4170_set_filter_type, +}; + +static const struct iio_chan_spec_ext_info ad4170_filter_type_ext_info[] = { + IIO_ENUM("filter_type", IIO_SEPARATE, &ad4170_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE, + &ad4170_filter_type_enum), + { } +}; + +static const struct iio_chan_spec ad4170_channel_template = { + .type = IIO_VOLTAGE, + .indexed = 1, + .differential = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info = ad4170_filter_type_ext_info, + .scan_type = { + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, +}; + +static int _ad4170_find_table_index(const unsigned int *tbl, size_t len, + unsigned int val) +{ + unsigned int i; + + for (i = 0; i < len; i++) + if (tbl[i] == val) + return i; + + return -EINVAL; +} + +#define ad4170_find_table_index(table, val) \ + _ad4170_find_table_index(table, ARRAY_SIZE(table), val) + +static int ad4170_set_channel_enable(struct ad4170_state *st, + unsigned int channel, bool status) +{ + struct ad4170_chan_info *chan_info = &st->chans_info[channel]; + int ret; + + if (chan_info->enabled == status) + return 0; + + ret = regmap_update_bits(st->regmap, AD4170_CHANNEL_EN_REG, + AD4170_CHANNEL_EN(channel), + status ? AD4170_CHANNEL_EN(channel) : 0); + if (ret) + return ret; + + chan_info->enabled = status; + return 0; +} + +static int ad4170_read_sample(struct iio_dev *indio_dev, unsigned int channel, + int *val) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct ad4170_chan_info *chan_info = &st->chans_info[channel]; + struct ad4170_setup *setup = &st->setups[chan_info->setup_num]; + int precision_bits = ad4170_channel_template.scan_type.realbits; + int ret; + + guard(mutex)(&st->lock); + ret = ad4170_set_channel_enable(st, channel, true); + if (ret) + return ret; + + reinit_completion(&st->completion); + + ret = ad4170_set_mode(st, AD4170_ADC_CTRL_MODE_SINGLE); + if (ret) + return ret; + + ret = wait_for_completion_timeout(&st->completion, HZ); + if (!ret) + goto out; + + ret = regmap_read(st->regmap, AD4170_DATA_24b_REG, val); + if (ret) + return ret; + + if (FIELD_GET(AD4170_AFE_BIPOLAR_MSK, setup->afe)) + *val = sign_extend32(*val, precision_bits - 1); +out: + ret = ad4170_set_channel_enable(st, channel, false); + if (ret) + return ret; + + return IIO_VAL_INT; +} + +/* + * Receives the device state, the number of a multiplexed input (AINP_N + * or AIM_N), and stores the voltage (in µV) of the specified input into the + * third argument. If the input number is not one of the special multiplexed + * inputs ((AVDD-AVSS)/5, ..., REFOUT), stores zero to the voltage argument. + * If a voltage regulator required by the special input is unavailable, return + * error code. Return 0 on success. + * + * @st: pointer to device state struct + * @ain_n: number of a multiplexed AD4170 input + * @ain_voltage: pointer to a variable where to store ain_n voltage + */ +static int ad4170_get_AINM_voltage(struct ad4170_state *st, int ain_n, + int *ain_voltage) +{ + int ret; + + *ain_voltage = 0; + switch (ain_n) { + case AD4170_MAP_AVDD_AVSS_N: + ret = regulator_get_voltage(st->supplies[AD4170_AVDD_SUP].consumer); + if (ret < 0) + return ret; + + *ain_voltage = ret ? ret / 5 : 0; + return 0; + case AD4170_MAP_IOVDD_DGND_N: + ret = regulator_get_voltage(st->supplies[AD4170_IOVDD_SUP].consumer); + if (ret < 0) + return ret; + + *ain_voltage = ret ? ret / 5 : 0; + return 0; + case AD4170_MAP_AVSS: + ret = regulator_get_voltage(st->supplies[AD4170_AVSS_SUP].consumer); + if (ret < 0) + ret = 0; /* Assume AVSS at 0V if not provided */ + + /* AVSS is never above 0V, i.e., it can only be negative. */ + *ain_voltage = -ret; /* AVSS is a negative voltage */ + return 0; + case AD4170_MAP_DGND: + *ain_voltage = 0; + return 0; + case AD4170_MAP_REFIN1_P: + ret = regulator_get_voltage(st->supplies[AD4170_REFIN1P_SUP].consumer); + if (ret < 0) + return ret; + + *ain_voltage = ret; + return 0; + case AD4170_MAP_REFIN1_N: + ret = regulator_get_voltage(st->supplies[AD4170_REFIN1N_SUP].consumer); + if (ret < 0) + return ret; + + /* + * Making the assumption negative inputs of voltage references + * are either at GND level or negative with respect to GND. + */ + *ain_voltage = -ret; + return 0; + case AD4170_MAP_REFIN2_P: + ret = regulator_get_voltage(st->supplies[AD4170_REFIN2P_SUP].consumer); + if (ret < 0) + return ret; + + *ain_voltage = ret; + return 0; + case AD4170_MAP_REFIN2_N: + ret = regulator_get_voltage(st->supplies[AD4170_REFIN2N_SUP].consumer); + if (ret < 0) + return ret; + + /* + * Making the assumption negative inputs of voltage references + * are either at GND level or negative with respect to GND. + */ + *ain_voltage = -ret; + return 0; + case AD4170_MAP_REFOUT: + /* REFOUT is 2.5V relative to AVSS so take that into account */ + ret = regulator_get_voltage(st->supplies[AD4170_AVSS_SUP].consumer); + if (ret < 0) + ret = 0; /* Assume AVSS at GND (0V) if not provided */ + + *ain_voltage = AD4170_INT_REF_2_5V - ret; + return 0; + } + return -EINVAL; +} + +static int ad4170_validate_analog_input(struct ad4170_state *st, int pin) +{ + if (pin <= AD4170_MAX_ANALOG_PINS) { + if (st->pins_fn[pin] != AD4170_PIN_UNASIGNED) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Pin %d already used with fn %u.\n", + pin, st->pins_fn[pin]); + + st->pins_fn[pin] = AD4170_PIN_ANALOG_IN; + } + return 0; +} + +static int ad4170_validate_channel_input(struct ad4170_state *st, int pin, bool com) +{ + /* Check common-mode input pin is mapped to a special input. */ + if (com && (pin < AD4170_MAP_AVDD_AVSS_P || pin > AD4170_MAP_REFOUT)) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Invalid common-mode input pin number. %d\n", + pin); + + /* Check differential input pin is mapped to a analog input pin. */ + if (!com && pin > AD4170_MAX_ANALOG_PINS) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Invalid analog input pin number. %d\n", + pin); + + return ad4170_validate_analog_input(st, pin); +} + +/* + * Verifies whether the channel input configuration is valid by checking the + * provided input type and input numbers. + * Returns 0 on valid channel input configuration. -EINVAl otherwise. + */ +static int ad4170_validate_channel(struct ad4170_state *st, + struct iio_chan_spec const *chan) +{ + int ret; + + /* Check temperature channel mapping. */ + if (chan->channel == AD4170_MAP_TEMP_SENSOR_P) { + if (chan->channel2 != AD4170_MAP_TEMP_SENSOR_N) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Invalid temperature channel pin. %d\n", + chan->channel2); + + return 0; + } + + ret = ad4170_validate_channel_input(st, chan->channel, false); + if (ret < 0) + return ret; + + ret = ad4170_validate_channel_input(st, chan->channel2, !chan->differential); + if (ret < 0) + return ret; + + return 0; +} + +/* + * Receives the device state, the channel spec, a reference selection, and + * returns the magnitude of the allowed input range in µV. + * Verifies whether the channel configuration is valid by checking the provided + * input type, polarity, and voltage references result in a sane input range. + * Returns negative error code on failure. + */ +static int ad4170_get_input_range(struct ad4170_state *st, + struct iio_chan_spec const *chan, + unsigned int ref_sel) +{ + struct ad4170_chan_info *chan_info = &st->chans_info[chan->address]; + struct ad4170_setup *setup = &st->setups[chan_info->setup_num]; + bool bipolar = FIELD_GET(AD4170_AFE_BIPOLAR_MSK, setup->afe); + int refp, refn, ain_voltage, ret; + + switch (ref_sel) { + case AD4170_AFE_REFIN_REFIN1: + refp = regulator_get_voltage(st->supplies[AD4170_REFIN1P_SUP].consumer); + refn = regulator_get_voltage(st->supplies[AD4170_REFIN1N_SUP].consumer); + break; + case AD4170_AFE_REFIN_REFIN2: + refp = regulator_get_voltage(st->supplies[AD4170_REFIN2P_SUP].consumer); + refn = regulator_get_voltage(st->supplies[AD4170_REFIN2N_SUP].consumer); + break; + case AD4170_AFE_REFIN_AVDD: + refp = regulator_get_voltage(st->supplies[AD4170_AVDD_SUP].consumer); + ret = regulator_get_voltage(st->supplies[AD4170_AVSS_SUP].consumer); + /* + * TODO AVSS is actually optional. + * Should we handle -EPROBE_DEFER here? + */ + if (ret < 0) + ret = 0; /* Assume AVSS at GND if not provided */ + + refn = ret; + break; + case AD4170_AFE_REFIN_REFOUT: + refn = regulator_get_voltage(st->supplies[AD4170_AVSS_SUP].consumer); + if (refn < 0) + refn = 0; + + /* REFOUT is 2.5 V relative to AVSS */ + /* avss-supply is never above 0V. */ + refp = AD4170_INT_REF_2_5V - refn; + break; + default: + return -EINVAL; + } + if (refp < 0) + return refp; + + if (refn < 0) + return refn; + + /* + * Find out the analog input range from the channel type, polarity, and + * voltage reference selection. + * AD4170 channels are either differential or pseudo-differential. + */ + /* Differential Input Voltage Range: −VREF/gain to +VREF/gain (datasheet page 6) */ + /* Single-Ended Input Voltage Range: 0 to VREF/gain (datasheet page 6) */ + if (chan->differential) { + if (!bipolar) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Invalid channel %lu setup.\n", + chan->address); + + /* Differential bipolar channel */ + /* avss-supply is never above 0V. */ + /* Assuming refin1n-supply not above 0V. */ + /* Assuming refin2n-supply not above 0V. */ + return refp + refn; + } + /* + * Some configurations can lead to invalid setups. + * For example, if AVSS = -2.5V, REF_SELECT set to REFOUT (REFOUT/AVSS), + * and single-ended channel configuration set, then the input range + * should go from 0V to +VREF (single-ended - datasheet pg 10), but + * REFOUT/AVSS range would be -2.5V to 0V. + * Check the positive reference is higher than 0V for pseudo-diff + * channels. + */ + if (bipolar) { + /* Pseudo-differential bipolar channel */ + /* Input allowed to swing from GND to +VREF */ + if (refp <= 0) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Invalid setup for channel %lu.\n", + chan->address); + + return refp; + } + + /* Pseudo-differential unipolar channel */ + /* Input allowed to swing from IN- to +VREF */ + if (refp <= 0) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Invalid setup for channel %lu.\n", + chan->address); + + ret = ad4170_get_AINM_voltage(st, chan->channel2, &ain_voltage); + if (ret < 0) + return ret; + + if (refp - ain_voltage <= 0) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Invalid setup for channel %lu.\n", + chan->address); + + return refp - ain_voltage; +} + +static int ad4170_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct ad4170_chan_info *chan_info = &st->chans_info[chan->address]; + struct ad4170_setup *setup = &st->setups[chan_info->setup_num]; + enum ad4170_filter_type f_type; + unsigned int pga, fs_idx; + + switch (info) { + case IIO_CHAN_INFO_RAW: + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) + return ad4170_read_sample(indio_dev, chan->address, val); + unreachable(); + case IIO_CHAN_INFO_SCALE: + pga = FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe); + *val = chan_info->scale_tbl[pga][0]; + *val2 = chan_info->scale_tbl[pga][1]; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + pga = FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe); + *val = chan_info->offset_tbl[pga]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + f_type = ad4170_filter_to_filter_type(setup->filter); + fs_idx = find_closest(setup->filter_fs, ad4170_filt_fs_tbl, + ARRAY_SIZE(ad4170_filt_fs_tbl)); + *val = st->sps_tbl[f_type][fs_idx][0]; + *val2 = st->sps_tbl[f_type][fs_idx][1]; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + *val = setup->offset; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBSCALE: + *val = setup->gain; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad4170_fill_scale_tbl(struct iio_dev *indio_dev, int channel) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct ad4170_chan_info *chan_info = &st->chans_info[channel]; + struct ad4170_setup *setup = &st->setups[chan_info->setup_num]; + const struct iio_chan_spec *chan = &indio_dev->channels[channel]; + bool bipolar = FIELD_GET(AD4170_AFE_BIPOLAR_MSK, setup->afe); + int ch_resolution = chan->scan_type.realbits - bipolar; + int pga, ainm_voltage, ret; + unsigned long long offset; + + ainm_voltage = 0; + if (chan->channel2 > AD4170_MAP_TEMP_SENSOR_N) { + ret = ad4170_get_AINM_voltage(st, chan->channel2, &ainm_voltage); + if (ret < 0) + return dev_err_probe(&st->spi->dev, ret, + "Failed to fill scale table\n"); + } + + for (pga = 0; pga < AD4170_PGA_OPTIONS; pga++) { + u64 nv; + unsigned int lshift, rshift; + + /* + * The scale factor to get ADC output codes to values in mV + * units is given by: + * _scale = (input_range / gain) / 2^precision + * AD4170 gain is a power of 2 so the above can be written as + * _scale = input_range / 2^(precision + gain) + * Keep the input range in µV before right shift to preserve + * scale precision. + */ + nv = (u64)chan_info->input_range_uv * NANO; + lshift = (pga >> 3 & 1); /* handle cases 8 and 9 */ + rshift = ch_resolution + (pga & 0x7) - lshift; + chan_info->scale_tbl[pga][0] = 0; + chan_info->scale_tbl[pga][1] = div_u64(nv >> rshift, MILLI); + + /* + * If the negative input is not at GND, the conversion result + * (which is relative to IN-) will be offset by the level at IN-. + * Use the scale factor the other way around to go from a known + * voltage to the corresponding ADC output code. + * With that, we are able to get to what would be the output + * code for the voltage at the negative input. + * If the negative input is not fixed, there is no offset. + */ + offset = ((unsigned long long)ainm_voltage) * MICRO; + offset = DIV_ROUND_CLOSEST_ULL(offset, chan_info->scale_tbl[pga][1]); + + /* + * After divided by the scale, offset will always fit into 31 + * bits. For _raw + _offset to be relative to GND, the value + * provided as _offset is of opposite sign than the real offset. + */ + chan_info->offset_tbl[pga] = -(int)(offset); + } + return 0; +} + +static int ad4170_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct ad4170_chan_info *chan_info = &st->chans_info[chan->address]; + struct ad4170_setup *setup = &st->setups[chan_info->setup_num]; + enum ad4170_filter_type f_type = ad4170_filter_to_filter_type(setup->filter); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + *vals = (int *)chan_info->scale_tbl; + *length = ARRAY_SIZE(chan_info->scale_tbl) * 2; + *type = IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + switch (f_type) { + case AD4170_SINC5_AVG: + fallthrough; + case AD4170_SINC3: + *vals = (int *)&st->sps_tbl[f_type][AD4170_SINC3_FS_OFFSET]; + *length = (ARRAY_SIZE(ad4170_filt_fs_tbl) + - AD4170_SINC3_FS_OFFSET) * 2; + break; + case AD4170_SINC5: + *vals = (int *)st->sps_tbl[f_type]; + *length = (ARRAY_SIZE(ad4170_filt_fs_tbl) + - AD4170_SINC5_FS_PADDING) * 2; + break; + default: + return -EINVAL; + } + *type = IIO_VAL_INT_PLUS_MICRO; + + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ad4170_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + case IIO_CHAN_INFO_CALIBSCALE: + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad4170_set_pga(struct iio_dev *indio_dev, struct ad4170_state *st, + unsigned int channel_addr, int val, int val2) +{ + struct ad4170_chan_info *chan_info = &st->chans_info[channel_addr]; + struct ad4170_setup *setup = &st->setups[chan_info->setup_num]; + unsigned int old_pga = FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe); + unsigned int pga; + int ret = 0; + + for (pga = 0; pga < AD4170_PGA_OPTIONS; pga++) { + if (val == chan_info->scale_tbl[pga][0] && + val2 == chan_info->scale_tbl[pga][1]) + break; + } + + if (pga == AD4170_PGA_OPTIONS) + return -EINVAL; + + if (pga == old_pga) + return 0; + + setup->afe &= ~AD4170_AFE_PGA_GAIN_MSK; + setup->afe |= FIELD_PREP(AD4170_AFE_PGA_GAIN_MSK, pga); + + guard(mutex)(&st->lock); + ret = ad4170_write_setup(st, chan_info->setup_num, setup); + if (ret) { + setup->afe &= ~AD4170_AFE_PGA_GAIN_MSK; + setup->afe |= FIELD_PREP(AD4170_AFE_PGA_GAIN_MSK, old_pga); + } + + return ret; +} + +static int ad4170_set_channel_freq(struct ad4170_state *st, + unsigned int channel_addr, int val, int val2) +{ + struct ad4170_chan_info *chan_info = &st->chans_info[channel_addr]; + struct ad4170_setup *setup = &st->setups[chan_info->setup_num]; + enum ad4170_filter_type f_type = ad4170_filter_to_filter_type(setup->filter); + int offset, padding, i, ret = 0; + unsigned int old_filter_fs; + + switch (f_type) { + case AD4170_SINC5_AVG: + fallthrough; + case AD4170_SINC3: + offset = AD4170_SINC3_FS_OFFSET; + padding = 0; + break; + case AD4170_SINC5: + offset = 0; + padding = AD4170_SINC5_FS_PADDING; + break; + } + + for (i = offset; i < ARRAY_SIZE(ad4170_filt_fs_tbl) - padding; i++) { + if (st->sps_tbl[f_type][i][0] == val && + st->sps_tbl[f_type][i][1] == val2) + break; + } + if (i >= ARRAY_SIZE(ad4170_filt_fs_tbl) - padding) + return -EINVAL; + + old_filter_fs = setup->filter_fs; + setup->filter_fs = ad4170_filt_fs_tbl[i]; + + guard(mutex)(&st->lock); + ret = ad4170_write_setup(st, chan_info->setup_num, setup); + if (ret) + setup->filter_fs = old_filter_fs; + + return ret; +} + +static int ad4170_set_calib_gain(struct iio_dev *indio_dev, int addr, int val) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct ad4170_chan_info *chan_info = &st->chans_info[addr]; + struct ad4170_setup *setup = &st->setups[chan_info->setup_num]; + u32 old_gain; + int ret; + + old_gain = setup->gain; + setup->gain = val; + + guard(mutex)(&st->lock); + ret = ad4170_write_setup(st, chan_info->setup_num, setup); + if (ret) + setup->gain = old_gain; + + return ret; +} + +static int ad4170_set_calib_offset(struct iio_dev *indio_dev, int addr, int val) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct ad4170_chan_info *chan_info = &st->chans_info[addr]; + struct ad4170_setup *setup = &st->setups[chan_info->setup_num]; + u32 old_offset; + int ret; + + old_offset = setup->offset; + setup->offset = val; + + guard(mutex)(&st->lock); + ret = ad4170_write_setup(st, chan_info->setup_num, setup); + if (ret) + setup->offset = old_offset; + + return ret; +} + +static int ad4170_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad4170_state *st = iio_priv(indio_dev); + + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { + switch (info) { + case IIO_CHAN_INFO_SCALE: + return ad4170_set_pga(indio_dev, st, chan->address, val, + val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return ad4170_set_channel_freq(st, chan->address, val, + val2); + case IIO_CHAN_INFO_CALIBBIAS: + return ad4170_set_calib_offset(indio_dev, chan->address, + val); + case IIO_CHAN_INFO_CALIBSCALE: + return ad4170_set_calib_gain(indio_dev, chan->address, + val); + default: + return -EINVAL; + } + } + unreachable(); +} + +static int ad4170_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *active_scan_mask) +{ + struct ad4170_state *st = iio_priv(indio_dev); + unsigned int channel; + int ret; + + guard(mutex)(&st->lock); + + for_each_set_bit(channel, active_scan_mask, indio_dev->num_channels) { + ret = ad4170_set_channel_enable(st, channel, true); + if (ret) + return ret; + } + return 0; +} + +static const struct iio_info ad4170_info = { + .read_raw = ad4170_read_raw, + .read_avail = ad4170_read_avail, + .write_raw = ad4170_write_raw, + .write_raw_get_fmt = ad4170_write_raw_get_fmt, + .update_scan_mode = ad4170_update_scan_mode, + .debugfs_reg_access = ad4170_reg_access, +}; + +static int ad4170_soft_reset(struct ad4170_state *st) +{ + int ret; + + ret = regmap_write(st->regmap, AD4170_INTERFACE_CONFIG_A_REG, + AD4170_SW_RESET_MSK); + if (ret < 0) + return ret; + + /* + * AD4170-4 requires a minimum of 1 ms between any reset event and a + * register read/write transaction. + */ + fsleep(AD4170_RESET_SLEEP_US); + + return 0; +} + +static void ad4170_clk_disable_unprepare(void *clk) +{ + clk_disable_unprepare(clk); +} + +static int ad4170_parse_pin_muxing(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + u32 pin_muxing; + u8 aux; + int ret; + + /* + * Optional adi,dig-aux1 defaults to 0, DIG_AUX1 pin disabled. + */ + aux = AD4170_DIG_AUX1_DISABLED; + ret = device_property_read_u8(dev, "adi,dig-aux1", &aux); + if (ret) { + if (aux < AD4170_DIG_AUX1_DISABLED || aux > AD4170_DIG_AUX1_SYNC) + return dev_err_probe(dev, -EINVAL, + "Invalid adi,dig-aux1: %u\n", aux); + } + pin_muxing |= FIELD_PREP(AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK, aux); + + /* + * Optional adi,dig-aux2 defaults to 0, DIG_AUX2 pin disabled. + */ + aux = AD4170_DIG_AUX2_DISABLED; + ret = device_property_read_u8(dev, "adi,dig-aux2", &aux); + if (ret) { + if (aux < AD4170_DIG_AUX2_DISABLED || aux > AD4170_DIG_AUX2_SYNC) + return dev_err_probe(dev, -EINVAL, + "Invalid adi,dig-aux2: %u\n", aux); + } + pin_muxing |= FIELD_PREP(AD4170_PIN_MUXING_DIG_AUX2_CTRL_MSK, aux); + + /* + * Optional adi,sync-option defaults to 1, standard sync functionality. + */ + aux = AD4170_SYNC_STANDARD; + ret = device_property_read_u8(dev, "adi,sync-option", &aux); + if (ret) { + if (aux < AD4170_SYNC_DISABLED || aux > AD4170_SYNC_ALTERNATE) + return dev_err_probe(dev, -EINVAL, + "Invalid adi,sync-option: %u\n", aux); + } + pin_muxing |= FIELD_PREP(AD4170_PIN_MUXING_SYNC_CTRL_MSK, aux); + + return regmap_write(st->regmap, AD4170_PIN_MUXING_REG, pin_muxing); +} + +static int ad4170_parse_setup(struct ad4170_state *st, + struct fwnode_handle *child, + struct ad4170_setup *setup) +{ + struct device *dev = &st->spi->dev; + u32 tmp; + u16 aux16; + u8 aux; + int ret; + + /* ADC input chopping setup */ + aux16 = AD4170_MISC_CHOP_ADC_OFF; + ret = fwnode_property_read_u16(child, "adi,chop-adc", &aux16); + if (!ret) { + ret = ad4170_find_table_index(ad4170_chop_adc_tbl, aux16); + if (ret < 0) + return dev_err_probe(dev, ret, + "Invalid ADC chop config: %u\n", + aux16); + } + setup->misc |= FIELD_PREP(AD4170_MISC_CHOP_ADC_MSK, aux16); + + /* Burnout current setup */ + tmp = AD4170_BURNOUT_OFF; + fwnode_property_read_u32(child, "adi,burnout-current-nanoamp", &tmp); + if (!ret) { + ret = ad4170_find_table_index(ad4170_burnout_current_na_tbl, tmp); + return dev_err_probe(dev, ret, + "Invalid burnout current %unA\n", tmp); + } + setup->misc |= FIELD_PREP(AD4170_MISC_BURNOUT_MSK, tmp); + + /* Positive reference buffer setup */ + tmp = AD4170_REF_BUF_PRE; /* Default to have precharge buffer enabled. */ + ret = fwnode_property_read_u32(child, "adi,buffered-positive", &tmp); + if (ret) { + if (tmp < AD4170_REF_BUF_PRE || tmp > AD4170_REF_BUF_BYPASS) + return dev_err_probe(dev, -EINVAL, + "Invalid adi,buffered-positive: %u\n", + tmp); + } + setup->afe |= FIELD_PREP(AD4170_AFE_REF_BUF_P_MSK, tmp); + + /* Negative reference buffer setup */ + tmp = AD4170_REF_BUF_PRE; /* Default to have precharge buffer enabled. */ + ret = fwnode_property_read_u32(child, "adi,buffered-negative", &tmp); + if (ret) { + if (tmp < AD4170_REF_BUF_PRE || tmp > AD4170_REF_BUF_BYPASS) + return dev_err_probe(dev, -EINVAL, + "Invalid adi,buffered-negative: %u\n", + tmp); + } + setup->afe |= FIELD_PREP(AD4170_AFE_REF_BUF_M_MSK, tmp); + + /* Voltage reference selection */ + aux = AD4170_AFE_REFIN_REFOUT; /* Default reference selection. */ + fwnode_property_read_u8(child, "adi,reference-select", &aux); + if (aux >= AD4170_AFE_REFIN_AVDD) + return dev_err_probe(dev, -EINVAL, + "Invalid reference selected %u\n", aux); + setup->afe |= FIELD_PREP(AD4170_AFE_REF_SELECT_MSK, aux); + + return 0; +} + +static int ad4170_parse_channel_type(struct device *dev, + struct fwnode_handle *child, + struct iio_chan_spec *chan) +{ + u32 pins[2]; + int ret; + + ret = fwnode_property_read_u32_array(child, "diff-channels", pins, + ARRAY_SIZE(pins)); + if (!ret) { + chan->differential = true; + chan->channel = pins[0]; + chan->channel2 = pins[1]; + return 0; + } + ret = fwnode_property_read_u32(child, "single-channel", &pins[0]); + if (!ret) { + chan->differential = false; + chan->channel = pins[0]; + + ret = fwnode_property_read_u32(child, "common-mode-channel", + &pins[1]); + if (ret) + return dev_err_probe(dev, ret, + "single-ended channels must define common-mode-channel\n"); + + chan->channel2 = pins[1]; + return 0; + } + return dev_err_probe(dev, ret, + "Channel must define one of diff-channels or single-channel.\n"); +} + +static int ad4170_parse_channel_node(struct iio_dev *indio_dev, + struct fwnode_handle *child, int chan_num) +{ + struct ad4170_state *st = iio_priv(indio_dev); + unsigned int index; + struct device *dev = &st->spi->dev; + struct ad4170_chan_info *chan_info; + struct ad4170_setup *setup; + struct iio_chan_spec *chan; + u8 ref_select; + bool bipolar; + int ret; + + ret = fwnode_property_read_u32(child, "reg", &index); + if (ret) + return ret; + + if (index >= AD4170_MAX_CHANNELS) + return dev_err_probe(dev, -EINVAL, + "Channel idx greater than no of channels\n"); + + chan = &st->chans[chan_num]; + chan_info = &st->chans_info[chan_num]; + + *chan = ad4170_channel_template; + chan->address = chan_num; /* Channel index in ad4170_state chans array */ + chan->scan_index = index; /* Index to use with the sequencer */ + + chan_info->setup_num = AD4170_INVALID_SETUP_SLOT; + ret = fwnode_property_read_u32(child, "adi,config-setup-number", + &chan_info->setup_num); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read adi,config-setup-number\n"); + + if (chan_info->setup_num >= AD4170_MAX_SETUPS) + return dev_err_probe(dev, -EINVAL, + "Invalid adi,config-setup-number: %d\n", + chan_info->setup_num); + + setup = &st->setups[chan_info->setup_num]; + ret = ad4170_parse_setup(st, child, setup); + if (ret) + return ret; + + ret = ad4170_parse_channel_type(dev, child, chan); + if (ret < 0) + return ret; + + bipolar = fwnode_property_read_bool(child, "bipolar"); + setup->afe |= FIELD_PREP(AD4170_AFE_BIPOLAR_MSK, bipolar); + if (bipolar) + chan->scan_type.sign = 's'; + else + chan->scan_type.sign = 'u'; + + ref_select = FIELD_GET(AD4170_AFE_REF_SELECT_MSK, setup->afe); + ret = ad4170_validate_channel(st, chan); + if (ret < 0) + return ret; + + ret = ad4170_get_input_range(st, chan, ref_select); + if (ret < 0) + return dev_err_probe(dev, ret, "Cannot use reference %u\n", + ref_select); + + chan_info->input_range_uv = ret; + return 0; +} + +static int ad4170_parse_channels(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + int chan_num, i, ret; + + chan_num = 0; + device_for_each_child_node_scoped(dev, child) { + ret = ad4170_parse_channel_node(indio_dev, child, chan_num); + if (ret) + return ret; + + chan_num++; + } + for (i = 0; i < AD4170_MAX_CHANNELS; i++) + if (st->chans[i].scan_index == 0) + break; + + /* + * When more than one channel is enabled, channel 0 must always be used. + */ + if (chan_num > 1 && i == AD4170_MAX_CHANNELS) + return dev_err_probe(dev, -EINVAL, + "Channel 0 must be configured\n"); + + indio_dev->channels = st->chans; + indio_dev->num_channels = chan_num; + return 0; +} + +/* + * Parses firmware data describing output current source setup. There are 4 + * excitation currents (IOUT0 to IOUT3) that can be configured independently. + * Excitation currents are added if they are output on the same pin. + */ +static int ad4170_parse_exc_current(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + int gpio_number; + u16 current_src; + u32 pin, val; + int i, ret; + + for (i = 0; i < AD4170_NUM_CURRENT_SRC; i++) { + /* Parse excitation current output pin properties. */ + pin = AD4170_CURRENT_IOUT_AIN0; + ret = fwnode_property_read_u32(dev->fwnode, + ad4170_i_out_pin_dt_props[i], + &pin); + if (!ret) { + ret = ad4170_find_table_index(ad4170_iout_pin_tbl, pin); + if (ret < 0) + return dev_err_probe(dev, ret, + "Invalid %s: %u\n", + ad4170_i_out_pin_dt_props[i], + pin); + } + current_src |= FIELD_PREP(AD4170_CURRENT_SRC_I_OUT_PIN_MSK, pin); + + /* Parse excitation current value properties. */ + val = AD4170_I_OUT_0UA; + ret = fwnode_property_read_u32(dev->fwnode, + ad4170_i_out_val_dt_props[i], + &val); + if (!ret) { + ret = ad4170_find_table_index(ad4170_iout_current_ua_tbl, + val); + if (ret < 0) + return dev_err_probe(dev, ret, + "Invalid %s: %uuA\n", + ad4170_i_out_val_dt_props[i], + val); + } + current_src |= FIELD_PREP(AD4170_CURRENT_SRC_I_OUT_VAL_MSK, val); + + /* + * The excitation currents can be added by outputting them on + * the same pin so don't prevent a pin from being assigned for + * current output multiple times. + */ + if (pin >= AD4170_CURRENT_IOUT_GPIO0) { + gpio_number = pin - AD4170_CURRENT_IOUT_GPIO0; + st->gpio_fn[gpio_number] = AD4170_GPIO_AC_EXCITATION; + } else if (val > AD4170_I_OUT_0UA) { + st->pins_fn[pin] = AD4170_PIN_CURRENT_OUT; + } + + ret = regmap_write(st->regmap, AD4170_CURRENT_SRC_REG(i), + current_src); + if (ret < 0) + return ret; + + current_src = 0; /* Clear temporary variable. */ + } + return 0; +} + +static void ad4170_disable_supplies(void *data) +{ + struct ad4170_state *st = data; + + regulator_bulk_disable(ARRAY_SIZE(st->supplies), st->supplies); +} + +static struct ad4170_state *clk_hw_to_ad4170(struct clk_hw *hw) +{ + return container_of(hw, struct ad4170_state, int_clk_hw); +} + +static unsigned long ad4170_sel_clk(struct ad4170_state *st, + unsigned int clk_sel) +{ + st->clock_ctrl &= ~AD4170_CLOCK_CTRL_CLOCKSEL_MSK; + st->clock_ctrl |= FIELD_PREP(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, clk_sel); + return regmap_write(st->regmap, AD4170_CLOCK_CTRL_REG, st->clock_ctrl); +} + +static unsigned long ad4170_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return AD4170_INT_FREQ_16MHZ; +} + +static int ad4170_clk_output_is_enabled(struct clk_hw *hw) +{ + struct ad4170_state *st = clk_hw_to_ad4170(hw); + u32 clk_sel; + + clk_sel = FIELD_GET(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, st->clock_ctrl); + return clk_sel == AD4170_INTERNAL_OSC_OUTPUT; +} + +static int ad4170_clk_output_prepare(struct clk_hw *hw) +{ + struct ad4170_state *st = clk_hw_to_ad4170(hw); + + return ad4170_sel_clk(st, AD4170_INTERNAL_OSC_OUTPUT); +} + +static void ad4170_clk_output_unprepare(struct clk_hw *hw) +{ + struct ad4170_state *st = clk_hw_to_ad4170(hw); + + ad4170_sel_clk(st, AD4170_INTERNAL_OSC); +} + +static const struct clk_ops ad4170_int_clk_ops = { + .recalc_rate = ad4170_clk_recalc_rate, + .is_enabled = ad4170_clk_output_is_enabled, + .prepare = ad4170_clk_output_prepare, + .unprepare = ad4170_clk_output_unprepare, +}; + +static int ad4170_register_clk_provider(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct clk_init_data init = {}; + int ret; + + if (!IS_ENABLED(CONFIG_COMMON_CLK)) + return 0; + + init.name = fwnode_get_name(fwnode); + init.ops = &ad4170_int_clk_ops; + + st->int_clk_hw.init = &init; + ret = devm_clk_hw_register(dev, &st->int_clk_hw); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &st->int_clk_hw); +} + +static int ad4170_clock_select(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + int ret; + + st->mclk_hz = AD4170_INT_FREQ_16MHZ; + ret = device_property_match_property_string(dev, "clock-names", + ad4170_clk_sel, + ARRAY_SIZE(ad4170_clk_sel)); + if (ret < 0) { + /* Use internal clock reference */ + st->clock_ctrl |= FIELD_PREP(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, + AD4170_INTERNAL_OSC_OUTPUT); + return ad4170_register_clk_provider(indio_dev); + } + + /* Use external clock reference */ + st->ext_clk = devm_clk_get(dev, ad4170_clk_sel[ret]); + if (IS_ERR(st->ext_clk)) { + if (PTR_ERR(st->ext_clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + return dev_err_probe(dev, PTR_ERR(st->ext_clk), + "Failed to get external clock\n"); + } + st->clock_ctrl |= FIELD_PREP(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, + AD4170_EXTERNAL_OSC + ret); + + ret = clk_prepare_enable(st->ext_clk); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable external clock\n"); + + ret = devm_add_action_or_reset(dev, ad4170_clk_disable_unprepare, + st->ext_clk); + if (ret) + return dev_err_probe(dev, ret, + "Failed to add clock unwind action\n"); + + st->mclk_hz = clk_get_rate(st->ext_clk); + if (st->mclk_hz < AD4170_EXT_FREQ_MHZ_MIN || + st->mclk_hz > AD4170_EXT_FREQ_MHZ_MAX) { + return dev_err_probe(dev, -EINVAL, + "Invalid external clock frequency %u\n", + st->mclk_hz); + } + return 0; +} + +static int ad4170_parse_pw_switch(struct ad4170_state *st, struct device *dev) +{ + bool pdsw0, pdsw1; + + pdsw0 = device_property_read_bool(dev, "adi,gpio0-power-down-switch"); + pdsw1 = device_property_read_bool(dev, "adi,gpio1-power-down-switch"); + + /* + * Error if a GPIO is assigned to be both excitation current output and + * power switch. + */ + if (pdsw0) { + if (st->gpio_fn[0] != AD4170_GPIO_UNASIGNED) + return dev_err_probe(&st->spi->dev, -EINVAL, + "GPIO 0 already used with fn %u\n", + st->gpio_fn[0]); + + st->gpio_fn[0] = AD4170_GPIO_PW_DOW_SWITCH; + } + if (pdsw1) { + if (st->gpio_fn[1] != AD4170_GPIO_UNASIGNED) + return dev_err_probe(&st->spi->dev, -EINVAL, + "GPIO 1 already used with fn %u\n", + st->gpio_fn[1]); + + st->gpio_fn[1] = AD4170_GPIO_PW_DOW_SWITCH; + } + return regmap_update_bits(st->regmap, AD4170_POWER_DOWN_SW_REG, + AD4170_POWER_DOWN_SW_PDSW0_MSK | + AD4170_POWER_DOWN_SW_PDSW1_MSK, + pdsw0 | pdsw1); +} + +static int ad4170_parse_vbias(struct ad4170_state *st, struct device *dev) +{ + u32 vbias_pins[AD4170_MAX_ANALOG_PINS]; + unsigned int i, val; + u32 num_vbias_pins; + int ret; + + ret = device_property_count_u32(dev, "adi,vbias-pins"); + if (ret > 0) { + if (ret > AD4170_MAX_ANALOG_PINS) + return dev_err_probe(dev, -EINVAL, + "Too many vbias pins %u\n", ret); + + num_vbias_pins = ret; + ret = device_property_read_u32_array(dev, "adi,vbias-pins", + vbias_pins, + num_vbias_pins); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read vbias pins\n"); + } + for (i = 0; i < num_vbias_pins; i++) { + if (st->pins_fn[vbias_pins[i]] != AD4170_PIN_UNASIGNED) + return dev_err_probe(&st->spi->dev, -EINVAL, + "AIN%d already used with fn %u\n", + vbias_pins[i], st->pins_fn[i]); + + val |= BIT(vbias_pins[i]); + st->pins_fn[vbias_pins[i]] = AD4170_PIN_VBIAS; + } + return regmap_write(st->regmap, AD4170_V_BIAS_REG, val); +} + +static int ad4170_parse_firmware(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + int ret, i; + u8 aux; + + ret = ad4170_clock_select(indio_dev); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to setup device clock\n"); + + ret = regmap_write(st->regmap, AD4170_CLOCK_CTRL_REG, st->clock_ctrl); + if (ret < 0) + return ret; + + ret = ad4170_parse_pin_muxing(indio_dev); + if (ret < 0) + return ret; + + for (i = 0; i < AD4170_NUM_ANALOG_PINS; i++) + st->pins_fn[i] = AD4170_PIN_UNASIGNED; + + for (i = 0; i < AD4170_NUM_GPIO_PINS; i++) + st->gpio_fn[i] = AD4170_GPIO_UNASIGNED; + + ret = ad4170_parse_exc_current(indio_dev); + if (ret) + return ret; +// write to misc reg must be delaied because misc register contains settings +// that are channel specific. + + ret = ad4170_parse_pw_switch(st, dev); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to config power down switches\n"); + + ret = ad4170_parse_vbias(st, dev); + if (ret < 0) + return ret; + + ret = ad4170_parse_channels(indio_dev); + if (ret) + return ret; + + aux = AD4170_MISC_CHOP_IEXC_OFF; + ret = device_property_read_u8(dev, "adi,chop-iexc", &aux); + if (!ret) { + ret = ad4170_find_table_index(ad4170_iexc_chop_tbl, aux); + if (ret < 0) + return dev_err_probe(dev, ret, + "Invalid adi,chop-iexc config: %u\n", + aux); + } + + /* Set excitation current chop config to first channel setup config */ + st->setups[indio_dev->channels[0].address].misc |= + FIELD_PREP(AD4170_MISC_CHOP_IEXC_MSK, aux); + return 0; +} + +static int ad4170_initial_config(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct ad4170_chan_info *chan_info; + struct iio_chan_spec const *chan; + struct ad4170_setup *setup; + unsigned int val; + unsigned int i; + int ret; + + ad4170_fill_sps_tbl(st); + + /* Put ADC in IDLE mode */ + ret = ad4170_set_mode(st, AD4170_ADC_CTRL_MODE_IDLE); + if (ret) + return ret; + + /* Setup channels. */ + for (i = 0; i < indio_dev->num_channels; i++) { + chan = &indio_dev->channels[i]; + chan_info = &st->chans_info[chan->address]; + setup = &st->setups[chan_info->setup_num]; + + ret = regmap_update_bits(st->regmap, + AD4170_CHAN_SETUP_REG(chan->address), + AD4170_CHANNEL_SETUP_SETUP_MSK, + FIELD_PREP(AD4170_CHANNEL_SETUP_SETUP_MSK, + chan_info->setup_num)); + if (ret) + return ret; + + setup->gain = AD4170_GAIN_REG_DEFAULT; + ret = ad4170_write_setup(st, chan_info->setup_num, setup); + if (ret) + return ret; + + val = FIELD_PREP(AD4170_CHANNEL_MAPN_AINP_MSK, chan->channel) | + FIELD_PREP(AD4170_CHANNEL_MAPN_AINM_MSK, chan->channel2); + + ret = regmap_write(st->regmap, AD4170_CHAN_MAP_REG(i), val); + if (ret < 0) + return ret; + + ret = ad4170_set_channel_freq(st, chan->address, + AD4170_MAX_SAMP_RATE, 0); + if (ret) + return ret; + + ret = ad4170_fill_scale_tbl(indio_dev, i); + if (ret) + return ret; + } + + ret = regmap_write(st->regmap, AD4170_CHANNEL_EN_REG, 0); + if (ret < 0) + return ret; + + /* + * Configure channels to share the same data output register, i.e. data + * can be read from the same register address regardless of channel + * number. + */ + ret = regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK, + AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK); + if (ret) + return ret; + + return 0; +} + +static const struct iio_trigger_ops ad4170_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + +static irqreturn_t ad4170_interrupt(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct ad4170_state *st = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev)) + iio_trigger_poll(st->trig); + else + complete(&st->completion); + + return IRQ_HANDLED; +}; + +static void ad4170_prepare_message(struct ad4170_state *st) +{ + /* + * Continuous data register read is enabled on buffer postenable so + * no instruction phase is needed meaning we don't need to send the + * register address to read data. Transfer only needs the read buffer. + */ + st->xfer.rx_buf = &st->reg_read_rx_buf; + st->xfer.bits_per_word = ad4170_channel_template.scan_type.storagebits; + st->xfer.len = BITS_TO_BYTES(ad4170_channel_template.scan_type.storagebits); + + spi_message_init_with_transfers(&st->msg, &st->xfer, 1); +} + +static int ad4170_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + int ret; + + ret = ad4170_set_mode(st, AD4170_ADC_CTRL_MODE_CONT); + if (ret < 0) + return ret; + + ret = regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_REG_CTRL_CONT_READ_MSK, + FIELD_PREP(AD4170_REG_CTRL_CONT_READ_MSK, + AD4170_ADC_CTRL_CONT_READ_ENABLE)); + + if (ret < 0) + return ret; + + return spi_optimize_message(st->spi, &st->msg); +} + +static int ad4170_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + int ret, i; + + spi_unoptimize_message(&st->msg); + + for (i = 0; i < indio_dev->num_channels; i++) { + ret = ad4170_set_channel_enable(st, i, false); + if (ret) + return ret; + } + + ret = regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_REG_CTRL_CONT_READ_MSK, + FIELD_PREP(AD4170_REG_CTRL_CONT_READ_MSK, + AD4170_ADC_CTRL_CONT_READ_DISABLE)); + if (ret < 0) + return ret; + + return ad4170_set_mode(st, AD4170_ADC_CTRL_MODE_IDLE); +} + +static const struct iio_buffer_setup_ops ad4170_buffer_ops = { + .postenable = ad4170_buffer_postenable, + .predisable = ad4170_buffer_predisable, +}; + +static irqreturn_t ad4170_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad4170_state *st = iio_priv(indio_dev); + int ret, i = 0; + int scan_index; + + for_each_set_bit(scan_index, indio_dev->active_scan_mask, + indio_dev->masklength) { + /* Read register data */ + ret = regmap_read(st->regmap, AD4170_DATA_24b_REG, &st->data[i]); + if (ret) + goto out; + i++; + } + + iio_push_to_buffers_with_timestamp(indio_dev, &st->data, + iio_get_time_ns(indio_dev)); +out: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int ad4170_triggered_buffer_setup(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + int ret; + + indio_dev->modes |= INDIO_BUFFER_TRIGGERED; + + st->trig = devm_iio_trigger_alloc(indio_dev->dev.parent, "%s-trig%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ad4170_trigger_ops; + st->trig->dev.parent = indio_dev->dev.parent; + + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = devm_iio_trigger_register(indio_dev->dev.parent, st->trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(st->trig); + + ret = request_irq(st->spi->irq, + &ad4170_interrupt, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + indio_dev->name, indio_dev); + if (ret) + return ret; + + return devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev, + &iio_pollfunc_store_time, + &ad4170_trigger_handler, + &ad4170_buffer_ops); +} + +static int ad4170_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad4170_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + devm_mutex_init(dev, &st->lock); + + indio_dev->name = AD4170_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ad4170_info; + + st->spi = spi; + st->regmap = devm_regmap_init(dev, NULL, st, &ad4170_regmap_config); + + st->supplies[AD4170_AVDD_SUP].supply = "avdd"; + st->supplies[AD4170_AVSS_SUP].supply = "avss"; + st->supplies[AD4170_IOVDD_SUP].supply = "iovdd"; + st->supplies[AD4170_REFIN1P_SUP].supply = "refin1p"; + st->supplies[AD4170_REFIN1N_SUP].supply = "refin1n"; + st->supplies[AD4170_REFIN2P_SUP].supply = "refin2p"; + st->supplies[AD4170_REFIN2N_SUP].supply = "refin2n"; + + /* + * If a regulator is not available, it will be set to a dummy regulator. + * Each channel reference is checked with regulator_get_voltage() before + * setting attributes so if any channel uses a dummy supply the driver + * probe will fail. + */ + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(st->supplies), + st->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to get supplies\n"); + + ret = regulator_bulk_enable(ARRAY_SIZE(st->supplies), st->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable supplies\n"); + + ret = devm_add_action_or_reset(dev, ad4170_disable_supplies, st); + if (ret) + return dev_err_probe(dev, ret, + "Failed to add supplies disable action\n"); + + ret = ad4170_soft_reset(st); + if (ret) + return ret; + + ret = ad4170_parse_firmware(indio_dev); + if (ret) + return dev_err_probe(dev, ret, + "Failed to parse firmware\n"); + + ret = ad4170_initial_config(indio_dev); + if (ret) + return dev_err_probe(dev, ret, + "Failed to setup device\n"); + + init_completion(&st->completion); + + ret = ad4170_triggered_buffer_setup(indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup read buffer\n"); + + ad4170_prepare_message(st); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad4170_of_match[] = { + { + .compatible = "adi,ad4170", + }, + { } +}; +MODULE_DEVICE_TABLE(of, ad4170_of_match); + +static struct spi_driver ad4170_driver = { + .driver = { + .name = AD4170_NAME, + .of_match_table = ad4170_of_match, + }, + .probe = ad4170_probe, +}; +module_spi_driver(ad4170_driver); + +MODULE_AUTHOR("Ana-Maria Cusco <ana-maria.cusco@xxxxxxxxxx>"); +MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt@xxxxxxxxxx>"); +MODULE_DESCRIPTION("Analog Devices AD4170 SPI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ad4170.h b/drivers/iio/adc/ad4170.h new file mode 100644 index 000000000000..5b24788314b1 --- /dev/null +++ b/drivers/iio/adc/ad4170.h @@ -0,0 +1,316 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * AD4170 ADC driver + * + * Copyright 2024 Analog Devices Inc. + */ + +#include <dt-bindings/iio/adc/adi,ad4170.h> + +#define AD4170_NAME "ad4170" + +#define AD4170_READ_MASK BIT(14) +/* AD4170 registers */ +#define AD4170_INTERFACE_CONFIG_A_REG 0x00 +#define AD4170_STATUS_REG 0x14 +#define AD4170_DATA_24b_REG 0x1c +#define AD4170_PIN_MUXING_REG 0x68 +#define AD4170_CLOCK_CTRL_REG 0x6A +#define AD4170_POWER_DOWN_SW_REG 0x6e +#define AD4170_ADC_CTRL_REG 0x70 +#define AD4170_CHANNEL_EN_REG 0x78 +#define AD4170_CHAN_SETUP_REG(x) (0x80 + 4 * (x)) +#define AD4170_CHAN_MAP_REG(x) (0x82 + 4 * (x)) +#define AD4170_MISC_REG(x) (0xc0 + 14 * (x)) +#define AD4170_AFE_REG(x) (0xc2 + 14 * (x)) +#define AD4170_FILTER_REG(x) (0xc4 + 14 * (x)) +#define AD4170_FILTER_FS_REG(x) (0xc6 + 14 * (x)) +#define AD4170_OFFSET_REG(x) (0xc8 + 14 * (x)) +#define AD4170_GAIN_REG(x) (0xcb + 14 * (x)) +#define AD4170_V_BIAS_REG 0x134 +#define AD4170_CURRENT_SRC_REG(x) (0x138 + 2 * (x)) + +/* AD4170_REG_INTERFACE_CONFIG_A */ +#define AD4170_SW_RESET_MSK (BIT(7) | BIT(0)) +#define AD4170_IFACE_CONFIG_ADDR_ASCENSION_MSK BIT(5) +#define AD4170_RESET_SLEEP_US 1000 +#define AD4170_INT_REF_2_5V 2500000 + +/* AD4170_REG_PIN_MUXING */ +#define AD4170_PIN_MUXING_DIG_AUX2_CTRL_MSK GENMASK(7, 6) +#define AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK GENMASK(5, 4) +#define AD4170_PIN_MUXING_SYNC_CTRL_MSK GENMASK(3, 2) + +/* AD4170_REG_CLOCK_CTRL */ +#define AD4170_INT_FREQ_16MHZ 16000000 +#define AD4170_EXT_FREQ_MHZ_MIN 1000000 +#define AD4170_EXT_FREQ_MHZ_MAX 17000000 +#define AD4170_CLOCK_CTRL_CLOCKSEL_MSK GENMASK(1, 0) + +#define AD4170_INTERNAL_OSC 0x0 +#define AD4170_INTERNAL_OSC_OUTPUT 0x1 +#define AD4170_EXTERNAL_OSC 0x2 +#define AD4170_EXTERNAL_XTAL 0x3 + +/* AD4170_REG_POWER_DOWN_SW */ +#define AD4170_POWER_DOWN_SW_PDSW1_MSK BIT(1) +#define AD4170_POWER_DOWN_SW_PDSW0_MSK BIT(0) + +/* AD4170_REG_ADC_CTRL */ +#define AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK BIT(7) +#define AD4170_REG_CTRL_CONT_READ_MSK GENMASK(5, 4) +#define AD4170_REG_CTRL_MODE_MSK GENMASK(3, 0) + +/* AD4170_REG_CHANNEL_EN */ +#define AD4170_CHANNEL_EN(ch) BIT(ch) + +/* AD4170_REG_ADC_CHANNEL_SETUP */ +#define AD4170_CHANNEL_SETUP_SETUP_MSK GENMASK(2, 0) + +/* AD4170_REG_ADC_CHANNEL_MAP */ +#define AD4170_CHANNEL_MAPN_AINP_MSK GENMASK(12, 8) +#define AD4170_CHANNEL_MAPN_AINM_MSK GENMASK(4, 0) + +/* AD4170_REG_ADC_SETUPS_MISC */ +#define AD4170_MISC_CHOP_IEXC_MSK GENMASK(15, 14) +#define AD4170_MISC_CHOP_ADC_MSK GENMASK(9, 8) +#define AD4170_MISC_BURNOUT_MSK GENMASK(1, 0) + +/* AD4170_REG_ADC_SETUPS_AFE */ +#define AD4170_AFE_REF_BUF_M_MSK GENMASK(11, 10) +#define AD4170_AFE_REF_BUF_P_MSK GENMASK(9, 8) +#define AD4170_AFE_REF_SELECT_MSK GENMASK(6, 5) +#define AD4170_AFE_BIPOLAR_MSK BIT(4) +#define AD4170_AFE_PGA_GAIN_MSK GENMASK(3, 0) + +/* AD4170_REG_ADC_SETUPS_FILTER */ +#define AD4170_SETUPS_POST_FILTER_SEL_MSK GENMASK(7, 4) +#define AD4170_SETUPS_FILTER_TYPE_MSK GENMASK(3, 0) + +/* AD4170 REG_OFFSET*/ +#define AD4170_OFFSET_MSK GENMASK(23, 0) + +/* AD4170 REG_GAIN*/ +#define AD4170_GAIN_MSK GENMASK(23, 0) +#define AD4170_GAIN_REG_DEFAULT 0x555555 + +/* AD4170_REG_CURRENT_SOURCE */ +#define AD4170_CURRENT_SRC_I_OUT_PIN_MSK GENMASK(12, 8) +#define AD4170_CURRENT_SRC_I_OUT_VAL_MSK GENMASK(2, 0) + +/* AD4170_REG_FIR_CONTROL */ +#define AD4170_FIR_CONTROL_IIR_MODE_MSK BIT(15) + +#define AD4170_MAX_CHANNELS 16 +#define AD4170_MAX_ANALOG_PINS 8 +#define AD4170_MAX_SETUPS 8 +#define AD4170_INVALID_SETUP_SLOT 9 +#define AD4170_NUM_CURRENT_SRC 4 +#define AD4170_MAX_SAMP_RATE 125000 + +#define AD4170_NUM_ANALOG_PINS 9 +#define AD4170_NUM_GPIO_PINS 4 + +#define AD4170_DIG_AUX1_DISABLED 0 +#define AD4170_DIG_AUX1_RDY 1 +#define AD4170_DIG_AUX1_SYNC 2 + +#define AD4170_DIG_AUX2_DISABLED 0 +#define AD4170_DIG_AUX2_LDAC 1 +#define AD4170_DIG_AUX2_SYNC 2 + +#define AD4170_SYNC_DISABLED 0 +#define AD4170_SYNC_STANDARD 1 +#define AD4170_SYNC_ALTERNATE 2 + +#define AD4170_I_OUT_0UA 0 +#define AD4170_I_OUT_10UA 10 +#define AD4170_I_OUT_50UA 50 +#define AD4170_I_OUT_100UA 100 +#define AD4170_I_OUT_250UA 250 +#define AD4170_I_OUT_500UA 500 +#define AD4170_I_OUT_1000UA 1000 +#define AD4170_I_OUT_1500UA 1500 + +#define AD4170_BURNOUT_OFF 0 +#define AD4170_BURNOUT_100NA 100 +#define AD4170_BURNOUT_2000NA 2000 +#define AD4170_BURNOUT_10000NA 10000 + +#define AD4170_PGA_OPTIONS 10 + +enum ad4170_pin_function { + AD4170_PIN_UNASIGNED, + AD4170_PIN_ANALOG_IN, + AD4170_PIN_CURRENT_OUT, + AD4170_PIN_VBIAS +}; + +enum ad4170_gpio_function { + AD4170_GPIO_UNASIGNED, + AD4170_GPIO_PW_DOW_SWITCH, + AD4170_GPIO_AC_EXCITATION, + AD4170_GPIO_OUTPUT, + AD4170_GPIO_CHANNEL, +}; + +#define AD4170_ADC_CTRL_CONT_READ_DISABLE 0 +#define AD4170_ADC_CTRL_CONT_READ_ENABLE 1 +//#define AD4170_ADC_CTRL_CONT_READ_TRANSMIT 2 + +#define AD4170_ADC_CTRL_MODE_CONT 0 +#define AD4170_ADC_CTRL_MODE_SINGLE 4 +#define AD4170_ADC_CTRL_MODE_IDLE 7 + +/** + * @enum ad4170_ref_buf + * @brief REFIN Buffer Enable. + */ +enum ad4170_ref_buf { + /** Pre-charge Buffer. */ + AD4170_REF_BUF_PRE, + /** Full Buffer.*/ + AD4170_REF_BUF_FULL, + /** Bypass */ + AD4170_REF_BUF_BYPASS +}; + +/** + * @enum ad4170_filter_type + * @brief Filter Mode for Sinc-Based Filters. + */ +enum ad4170_filter_type { + AD4170_SINC5_AVG, + AD4170_SINC5, + AD4170_SINC3, +}; + +/* ADC Register Lengths */ +static const unsigned int ad4170_reg_size[] = { + [AD4170_INTERFACE_CONFIG_A_REG] = 1, + [AD4170_STATUS_REG] = 2, + [AD4170_DATA_24b_REG] = 3, + [AD4170_PIN_MUXING_REG] = 2, + [AD4170_CLOCK_CTRL_REG] = 2, + [AD4170_POWER_DOWN_SW_REG] = 2, + [AD4170_ADC_CTRL_REG] = 2, + [AD4170_CHANNEL_EN_REG] = 2, + /* + * CHANNEL_SETUP and CHANNEL_MAP register are all 2 byte size each and + * their addresses are interleaved such that we have CHANNEL_SETUP0 + * address followed by CHANNEL_MAP0 address, followed by CHANNEL_SETUP1, + * and so on until CHANNEL_MAP15. + * Thus, initialize the register size for them only once. + */ + [AD4170_CHAN_SETUP_REG(0) ... AD4170_CHAN_MAP_REG(AD4170_MAX_CHANNELS - 1)] = 2, + /* + * MISC, AFE, FILTER, FILTER_FS, OFFSET, and GAIN register addresses are + * also interleaved but MISC, AFE, FILTER, FILTER_FS, OFFSET are 16-bit + * while OFFSET, GAIN are 24-bit registers so we can't init them all to + * the same size. + */ + /* Init MISC register size */ + [AD4170_MISC_REG(0)] = 2, + [AD4170_MISC_REG(1)] = 2, + [AD4170_MISC_REG(2)] = 2, + [AD4170_MISC_REG(3)] = 2, + [AD4170_MISC_REG(4)] = 2, + [AD4170_MISC_REG(5)] = 2, + [AD4170_MISC_REG(6)] = 2, + [AD4170_MISC_REG(7)] = 2, + /* Init AFE register size */ + [AD4170_AFE_REG(0)] = 2, + [AD4170_AFE_REG(1)] = 2, + [AD4170_AFE_REG(2)] = 2, + [AD4170_AFE_REG(3)] = 2, + [AD4170_AFE_REG(4)] = 2, + [AD4170_AFE_REG(5)] = 2, + [AD4170_AFE_REG(6)] = 2, + [AD4170_AFE_REG(7)] = 2, + /* Init FILTER register size */ + [AD4170_FILTER_REG(0)] = 2, + [AD4170_FILTER_REG(1)] = 2, + [AD4170_FILTER_REG(2)] = 2, + [AD4170_FILTER_REG(3)] = 2, + [AD4170_FILTER_REG(4)] = 2, + [AD4170_FILTER_REG(5)] = 2, + [AD4170_FILTER_REG(6)] = 2, + [AD4170_FILTER_REG(7)] = 2, + /* Init FILTER_FS register size */ + [AD4170_FILTER_FS_REG(0)] = 2, + [AD4170_FILTER_FS_REG(1)] = 2, + [AD4170_FILTER_FS_REG(2)] = 2, + [AD4170_FILTER_FS_REG(3)] = 2, + [AD4170_FILTER_FS_REG(4)] = 2, + [AD4170_FILTER_FS_REG(5)] = 2, + [AD4170_FILTER_FS_REG(6)] = 2, + [AD4170_FILTER_FS_REG(7)] = 2, + /* Init OFFSET register size */ + [AD4170_OFFSET_REG(0)] = 3, + [AD4170_OFFSET_REG(1)] = 3, + [AD4170_OFFSET_REG(2)] = 3, + [AD4170_OFFSET_REG(3)] = 3, + [AD4170_OFFSET_REG(4)] = 3, + [AD4170_OFFSET_REG(5)] = 3, + [AD4170_OFFSET_REG(6)] = 3, + [AD4170_OFFSET_REG(7)] = 3, + /* Init GAIN register size */ + [AD4170_GAIN_REG(0)] = 3, + [AD4170_GAIN_REG(1)] = 3, + [AD4170_GAIN_REG(2)] = 3, + [AD4170_GAIN_REG(3)] = 3, + [AD4170_GAIN_REG(4)] = 3, + [AD4170_GAIN_REG(5)] = 3, + [AD4170_GAIN_REG(6)] = 3, + [AD4170_GAIN_REG(7)] = 3, + [AD4170_V_BIAS_REG] = 2, + [AD4170_CURRENT_SRC_REG(0) ... AD4170_CURRENT_SRC_REG(AD4170_NUM_CURRENT_SRC - 1)] = 2, +}; + +static const unsigned int ad4170_chop_adc_tbl[] = { + AD4170_MISC_CHOP_ADC_OFF, + AD4170_MISC_CHOP_ADC_MUX, + AD4170_MISC_CHOP_ADC_ACX_4PIN, + AD4170_MISC_CHOP_ADC_ACX_2PIN +}; + +static const unsigned int ad4170_iexc_chop_tbl[] = { + AD4170_MISC_CHOP_IEXC_OFF, + AD4170_MISC_CHOP_IEXC_AB, + AD4170_MISC_CHOP_IEXC_CD, + AD4170_MISC_CHOP_IEXC_ABCD, +}; + +static const unsigned int ad4170_iout_pin_tbl[] = { + AD4170_CURRENT_IOUT_AIN0, + AD4170_CURRENT_IOUT_AIN1, + AD4170_CURRENT_IOUT_AIN2, + AD4170_CURRENT_IOUT_AIN3, + AD4170_CURRENT_IOUT_AIN4, + AD4170_CURRENT_IOUT_AIN5, + AD4170_CURRENT_IOUT_AIN6, + AD4170_CURRENT_IOUT_AIN7, + AD4170_CURRENT_IOUT_AIN8, + AD4170_CURRENT_IOUT_GPIO0, + AD4170_CURRENT_IOUT_GPIO1, + AD4170_CURRENT_IOUT_GPIO2, + AD4170_CURRENT_IOUT_GPIO3, +}; + +static const unsigned int ad4170_iout_current_ua_tbl[] = { + AD4170_I_OUT_0UA, + AD4170_I_OUT_10UA, + AD4170_I_OUT_50UA, + AD4170_I_OUT_100UA, + AD4170_I_OUT_250UA, + AD4170_I_OUT_500UA, + AD4170_I_OUT_1000UA, + AD4170_I_OUT_1500UA, +}; + +static const unsigned int ad4170_burnout_current_na_tbl[] = { + AD4170_BURNOUT_OFF, + AD4170_BURNOUT_100NA, + AD4170_BURNOUT_2000NA, + AD4170_BURNOUT_10000NA, +}; -- 2.45.2