Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/iio/adc/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 86 kB image not shown  

Quelle  ad4170-4.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
/*
 * Analog Devices AD4170-4 ADC driver
 *
 * Copyright (C) 2025 Analog Devices, Inc.
 * Author: Ana-Maria Cusco <ana-maria.cusco@analog.com>
 * Author: Marcelo Schmitt <marcelo.schmitt@analog.com>
 */


#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/bits.h>
#include <linux/cleanup.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/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/math64.h>
#include <linux/minmax.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/time.h>
#include <linux/types.h>
#include <linux/unaligned.h>
#include <linux/units.h>
#include <linux/util_macros.h>

/*
 * AD4170 registers
 * Multibyte register addresses point to the most significant byte which is the
 * address to use to get the most significant byte first (address accessed is
 * decremented by one for each data byte)
 *
 * Each register address define follows the AD4170_<REG_NAME>_REG format.
 * Each mask follows the AD4170_<REG_NAME>_<FIELD_NAME> format.
 * E.g. AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK is for accessing DIG_AUX1_CTRL field
 * of PIN_MUXING_REG.
 * Each constant follows the AD4170_<REG_NAME>_<FIELD_NAME>_<FUNCTION> format.
 * E.g. AD4170_PIN_MUXING_DIG_AUX1_DISABLED is the value written to
 * DIG_AUX1_CTRL field of PIN_MUXING register to disable DIG_AUX1 pin.
 * Some register names and register field names are shortened versions of
 * their datasheet counterpart names to provide better code readability.
 */

#define AD4170_CONFIG_A_REG    0x00
#define AD4170_DATA_24B_REG    0x1E
#define AD4170_PIN_MUXING_REG    0x69
#define AD4170_CLOCK_CTRL_REG    0x6B
#define AD4170_ADC_CTRL_REG    0x71
#define AD4170_CHAN_EN_REG    0x79
#define AD4170_CHAN_SETUP_REG(x)   (0x81 + 4 * (x))
#define AD4170_CHAN_MAP_REG(x)    (0x83 + 4 * (x))
#define AD4170_MISC_REG(x)    (0xC1 + 14 * (x))
#define AD4170_AFE_REG(x)    (0xC3 + 14 * (x))
#define AD4170_FILTER_REG(x)    (0xC5 + 14 * (x))
#define AD4170_FILTER_FS_REG(x)    (0xC7 + 14 * (x))
#define AD4170_OFFSET_REG(x)    (0xCA + 14 * (x))
#define AD4170_GAIN_REG(x)    (0xCD + 14 * (x))
#define AD4170_V_BIAS_REG    0x135
#define AD4170_CURRENT_SRC_REG(x)   (0x139 + 2 * (x))
#define AD4170_GPIO_MODE_REG    0x191
#define AD4170_GPIO_OUTPUT_REG    0x193
#define AD4170_GPIO_INPUT_REG    0x195
#define AD4170_ADC_CTRL_CONT_READ_EXIT_REG  0x200 /* virtual reg */

#define AD4170_REG_READ_MASK    BIT(14)

/* AD4170_CONFIG_A_REG - INTERFACE_CONFIG_A REGISTER */
#define AD4170_SW_RESET_MSK    (BIT(7) | BIT(0))

/* AD4170_PIN_MUXING_REG */
#define AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK  GENMASK(5, 4)

/* AD4170_CLOCK_CTRL_REG */
#define AD4170_CLOCK_CTRL_CLOCKSEL_MSK   GENMASK(1, 0)

/* AD4170_ADC_CTRL_REG */
#define AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK  BIT(7)
#define AD4170_ADC_CTRL_CONT_READ_MSK   GENMASK(5, 4)
#define AD4170_ADC_CTRL_MODE_MSK   GENMASK(3, 0)

/* AD4170_CHAN_EN_REG */
#define AD4170_CHAN_EN(ch)    BIT(ch)

/* AD4170_CHAN_SETUP_REG */
#define AD4170_CHAN_SETUP_SETUP_MSK   GENMASK(2, 0)

/* AD4170_CHAN_MAP_REG */
#define AD4170_CHAN_MAP_AINP_MSK   GENMASK(12, 8)
#define AD4170_CHAN_MAP_AINM_MSK   GENMASK(4, 0)

/* AD4170_MISC_REG */
#define AD4170_MISC_CHOP_IEXC_MSK   GENMASK(15, 14)
#define AD4170_MISC_CHOP_ADC_MSK   GENMASK(9, 8)

/* AD4170_AFE_REG */
#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_FILTER_REG */
#define AD4170_FILTER_FILTER_TYPE_MSK   GENMASK(3, 0)

/* AD4170_CURRENT_SRC_REG */
#define AD4170_CURRENT_SRC_I_OUT_PIN_MSK  GENMASK(12, 8)
#define AD4170_CURRENT_SRC_I_OUT_VAL_MSK  GENMASK(2, 0)

/* AD4170_GPIO_MODE_REG */
#define AD4170_GPIO_MODE_GPIO0_MSK   GENMASK(1, 0)
#define AD4170_GPIO_MODE_GPIO1_MSK   GENMASK(3, 2)
#define AD4170_GPIO_MODE_GPIO2_MSK   GENMASK(5, 4)
#define AD4170_GPIO_MODE_GPIO3_MSK   GENMASK(7, 6)

/* AD4170_GPIO_OUTPUT_REG */
#define AD4170_GPIO_OUTPUT_GPIO_MSK(x)   BIT(x)

/* AD4170 register constants */

/* AD4170_CLOCK_CTRL_REG constants */
#define AD4170_CLOCK_CTRL_CLOCKSEL_INT   0x0
#define AD4170_CLOCK_CTRL_CLOCKSEL_INT_OUT  0x1
#define AD4170_CLOCK_CTRL_CLOCKSEL_EXT   0x2
#define AD4170_CLOCK_CTRL_CLOCKSEL_EXT_XTAL  0x3

/* AD4170_CHAN_MAP_REG constants */
#define AD4170_CHAN_MAP_AIN(x)   (x)
#define AD4170_CHAN_MAP_TEMP_SENSOR  17
#define AD4170_CHAN_MAP_AVDD_AVSS_P  18
#define AD4170_CHAN_MAP_AVDD_AVSS_N  18
#define AD4170_CHAN_MAP_IOVDD_DGND_P  19
#define AD4170_CHAN_MAP_IOVDD_DGND_N  19
#define AD4170_CHAN_MAP_AVSS   23
#define AD4170_CHAN_MAP_DGND   24
#define AD4170_CHAN_MAP_REFIN1_P  25
#define AD4170_CHAN_MAP_REFIN1_N  26
#define AD4170_CHAN_MAP_REFIN2_P  27
#define AD4170_CHAN_MAP_REFIN2_N  28
#define AD4170_CHAN_MAP_REFOUT   29

/* AD4170_MISC_REG constants */
#define AD4170_MISC_CHOP_IEXC_PAIR1   0x1
#define AD4170_MISC_CHOP_IEXC_PAIR2   0x2
#define AD4170_MISC_CHOP_IEXC_BOTH   0x3

/* AD4170_PIN_MUXING_REG constants */
#define AD4170_PIN_MUXING_DIG_AUX1_DISABLED  0x0
#define AD4170_PIN_MUXING_DIG_AUX1_RDY   0x1

/* AD4170_ADC_CTRL_REG constants */
#define AD4170_ADC_CTRL_MODE_CONT   0x0
#define AD4170_ADC_CTRL_MODE_SINGLE   0x4
#define AD4170_ADC_CTRL_MODE_IDLE   0x7

#define AD4170_ADC_CTRL_CONT_READ_DISABLE  0x0
#define AD4170_ADC_CTRL_CONT_READ_ENABLE  0x1

/* AD4170_FILTER_REG constants */
#define AD4170_FILTER_FILTER_TYPE_SINC5_AVG  0x0
#define AD4170_FILTER_FILTER_TYPE_SINC5   0x4
#define AD4170_FILTER_FILTER_TYPE_SINC3   0x6

/* AD4170_CURRENT_SRC_REG constants */
#define AD4170_CURRENT_SRC_I_OUT_PIN_AIN(x)  (x)
#define AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(x)  ((x) + 17)

/* AD4170_GPIO_MODE_REG constants */
#define AD4170_GPIO_MODE_GPIO_INPUT   1
#define AD4170_GPIO_MODE_GPIO_OUTPUT   2

/* Device properties and auxiliary constants */

#define AD4170_NUM_ANALOG_PINS    9
#define AD4170_NUM_GPIO_PINS    4
#define AD4170_MAX_ADC_CHANNELS    16
#define AD4170_MAX_IIO_CHANNELS    (AD4170_MAX_ADC_CHANNELS + 1)
#define AD4170_MAX_ANALOG_PINS    8
#define AD4170_MAX_SETUPS    8
#define AD4170_INVALID_SETUP    9
#define AD4170_SPI_INST_PHASE_LEN   2
#define AD4170_SPI_MAX_XFER_LEN    6
#define AD4170_NUM_CURRENT_SRC    4
#define AD4170_DEFAULT_SAMP_RATE   (125 * HZ_PER_KHZ)

#define AD4170_INT_REF_2_5V    2500000

/* Internal and external clock properties */
#define AD4170_INT_CLOCK_16MHZ    (16 * HZ_PER_MHZ)
#define AD4170_EXT_CLOCK_MHZ_MIN   (1 * HZ_PER_MHZ)
#define AD4170_EXT_CLOCK_MHZ_MAX   (17 * HZ_PER_MHZ)

#define AD4170_NUM_PGA_OPTIONS    10

/* Digital filter properties */
#define AD4170_SINC3_MIN_FS    4
#define AD4170_SINC3_MAX_FS    65532
#define AD4170_SINC5_MIN_FS    1
#define AD4170_SINC5_MAX_FS    256

#define AD4170_GAIN_REG_DEFAULT    0x555555

#define AD4170_ADC_CTRL_CONT_READ_EXIT   0xA5

/* Analog pin functions  */
#define AD4170_PIN_UNASSIGNED    0x00
#define AD4170_PIN_ANALOG_IN    0x01
#define AD4170_PIN_CURRENT_OUT    0x02
#define AD4170_PIN_VBIAS    0x04

/* GPIO pin functions  */
#define AD4170_GPIO_UNASSIGNED    0x00
#define AD4170_GPIO_AC_EXCITATION   0x02
#define AD4170_GPIO_OUTPUT    0x04

/* Current source */
#define AD4170_CURRENT_SRC_DISABLED   0xFF

static const unsigned int ad4170_reg_size[] = {
 [AD4170_CONFIG_A_REG] = 1,
 [AD4170_DATA_24B_REG] = 3,
 [AD4170_PIN_MUXING_REG] = 2,
 [AD4170_CLOCK_CTRL_REG] = 2,
 [AD4170_ADC_CTRL_REG] = 2,
 [AD4170_CHAN_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_ADC_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.
 */

 [AD4170_MISC_REG(0) ... AD4170_FILTER_FS_REG(0)] = 2,
 [AD4170_MISC_REG(1) ... AD4170_FILTER_FS_REG(1)] = 2,
 [AD4170_MISC_REG(2) ... AD4170_FILTER_FS_REG(2)] = 2,
 [AD4170_MISC_REG(3) ... AD4170_FILTER_FS_REG(3)] = 2,
 [AD4170_MISC_REG(4) ... AD4170_FILTER_FS_REG(4)] = 2,
 [AD4170_MISC_REG(5) ... AD4170_FILTER_FS_REG(5)] = 2,
 [AD4170_MISC_REG(6) ... AD4170_FILTER_FS_REG(6)] = 2,
 [AD4170_MISC_REG(7) ... AD4170_FILTER_FS_REG(7)] = 2,
 [AD4170_OFFSET_REG(0) ... AD4170_GAIN_REG(0)] = 3,
 [AD4170_OFFSET_REG(1) ... AD4170_GAIN_REG(1)] = 3,
 [AD4170_OFFSET_REG(2) ... AD4170_GAIN_REG(2)] = 3,
 [AD4170_OFFSET_REG(3) ... AD4170_GAIN_REG(3)] = 3,
 [AD4170_OFFSET_REG(4) ... AD4170_GAIN_REG(4)] = 3,
 [AD4170_OFFSET_REG(5) ... AD4170_GAIN_REG(5)] = 3,
 [AD4170_OFFSET_REG(6) ... AD4170_GAIN_REG(6)] = 3,
 [AD4170_OFFSET_REG(7) ... AD4170_GAIN_REG(7)] = 3,
 [AD4170_V_BIAS_REG] = 2,
 [AD4170_CURRENT_SRC_REG(0) ... AD4170_CURRENT_SRC_REG(3)] = 2,
 [AD4170_GPIO_MODE_REG] = 2,
 [AD4170_GPIO_OUTPUT_REG] = 2,
 [AD4170_GPIO_INPUT_REG] = 2,
 [AD4170_ADC_CTRL_CONT_READ_EXIT_REG] = 0,
};

enum ad4170_ref_buf {
 AD4170_REF_BUF_PRE, /* Pre-charge referrence buffer */
 AD4170_REF_BUF_FULL, /* Full referrence buffering */
 AD4170_REF_BUF_BYPASS, /* Bypass referrence buffering */
};

/* maps adi,positive/negative-reference-buffer property values to enum */
static const char * const ad4170_ref_buf_str[] = {
 [AD4170_REF_BUF_PRE] = "precharge",
 [AD4170_REF_BUF_FULL] = "full",
 [AD4170_REF_BUF_BYPASS] = "disabled",
};

enum ad4170_ref_select {
 AD4170_REF_REFIN1,
 AD4170_REF_REFIN2,
 AD4170_REF_REFOUT,
 AD4170_REF_AVDD,
};

enum ad4170_filter_type {
 AD4170_SINC5_AVG,
 AD4170_SINC5,
 AD4170_SINC3,
};

enum ad4170_regulator {
 AD4170_AVDD_SUP,
 AD4170_AVSS_SUP,
 AD4170_IOVDD_SUP,
 AD4170_REFIN1P_SUP,
 AD4170_REFIN1N_SUP,
 AD4170_REFIN2P_SUP,
 AD4170_REFIN2N_SUP,
 AD4170_MAX_SUP,
};

static const char *const ad4170_clk_sel[] = {
 "ext-clk""xtal",
};

enum ad4170_int_pin_sel {
 AD4170_INT_PIN_SDO,
 AD4170_INT_PIN_DIG_AUX1,
};

static const char * const ad4170_int_pin_names[] = {
 [AD4170_INT_PIN_SDO] = "sdo",
 [AD4170_INT_PIN_DIG_AUX1] = "dig_aux1",
};

static const unsigned int ad4170_sinc3_filt_fs_tbl[] = {
 4, 8, 12, 16, 20, 40, 48, 80,   /*  0 -  7 */
 100, 256, 500, 1000, 5000, 8332, 10000, 25000, /*  8 - 15 */
 50000, 65532,     /* 16 - 17 */
};

#define AD4170_MAX_FS_TBL_SIZE  ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl)

static const unsigned int ad4170_sinc5_filt_fs_tbl[] = {
 1, 2, 4, 8, 12, 16, 20, 40, 48, 80, 100, 256,
};

static const unsigned int ad4170_iout_pin_tbl[] = {
 AD4170_CURRENT_SRC_I_OUT_PIN_AIN(0),
 AD4170_CURRENT_SRC_I_OUT_PIN_AIN(1),
 AD4170_CURRENT_SRC_I_OUT_PIN_AIN(2),
 AD4170_CURRENT_SRC_I_OUT_PIN_AIN(3),
 AD4170_CURRENT_SRC_I_OUT_PIN_AIN(4),
 AD4170_CURRENT_SRC_I_OUT_PIN_AIN(5),
 AD4170_CURRENT_SRC_I_OUT_PIN_AIN(6),
 AD4170_CURRENT_SRC_I_OUT_PIN_AIN(7),
 AD4170_CURRENT_SRC_I_OUT_PIN_AIN(8),
 AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(0),
 AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(1),
 AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(2),
 AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(3),
};

static const unsigned int ad4170_iout_current_ua_tbl[] = {
 0, 10, 50, 100, 250, 500, 1000, 1500,
};

enum ad4170_sensor_enum {
 AD4170_ADC_SENSOR = 0,
 AD4170_WEIGH_SCALE_SENSOR = 1,
 AD4170_RTD_SENSOR = 2,
 AD4170_THERMOCOUPLE_SENSOR = 3,
};

/* maps adi,sensor-type property value to enum */
static const char * const ad4170_sensor_type[] = {
 [AD4170_ADC_SENSOR] = "adc",
 [AD4170_WEIGH_SCALE_SENSOR] = "weighscale",
 [AD4170_RTD_SENSOR] = "rtd",
 [AD4170_THERMOCOUPLE_SENSOR] = "thermocouple",
};

struct ad4170_chip_info {
 const char *name;
};

static const struct ad4170_chip_info ad4170_chip_info = {
 .name = "ad4170-4",
};

static const struct ad4170_chip_info ad4190_chip_info = {
 .name = "ad4190-4",
};

static const struct ad4170_chip_info ad4195_chip_info = {
 .name = "ad4195-4",
};

/*
 * 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.
 *
 * If this struct is modified, ad4170_setup_eq() will probably need to be
 * updated too.
 */

struct ad4170_setup {
 u16 misc;
 u16 afe;
 u16 filter;
 u16 filter_fs;
 u32 offset; /* For calibration purposes */
 u32 gain; /* For calibration purposes */
};

struct ad4170_setup_info {
 struct ad4170_setup setup;
 unsigned int enabled_channels;
 unsigned int channels;
};

struct ad4170_chan_info {
 unsigned int input_range_uv;
 unsigned int setup_num; /* Index to access state setup_infos array */
 struct ad4170_setup setup; /* cached setup */
 int offset_tbl[10];
 u32 scale_tbl[10][2];
 bool initialized;
 bool enabled;
};

static const char * const ad4170_filt_names[] = {
 [AD4170_SINC5_AVG] = "sinc5+avg",
 [AD4170_SINC5] = "sinc5",
 [AD4170_SINC3] = "sinc3",
};

struct ad4170_state {
 struct mutex lock; /* Protect read-modify-write and multi write sequences */
 int vrefs_uv[AD4170_MAX_SUP];
 u32 mclk_hz;
 struct ad4170_setup_info setup_infos[AD4170_MAX_SETUPS];
 struct ad4170_chan_info chan_infos[AD4170_MAX_ADC_CHANNELS];
 struct completion completion;
 struct iio_chan_spec chans[AD4170_MAX_IIO_CHANNELS];
 struct spi_device *spi;
 struct regmap *regmap;
 int sps_tbl[ARRAY_SIZE(ad4170_filt_names)][AD4170_MAX_FS_TBL_SIZE][2];
 __be32 bounce_buffer[AD4170_MAX_ADC_CHANNELS];
 struct spi_message msg;
 struct spi_transfer xfer;
 struct iio_trigger *trig;
 struct clk_hw int_clk_hw;
 unsigned int clock_ctrl;
 unsigned int pins_fn[AD4170_NUM_ANALOG_PINS];
 int gpio_fn[AD4170_NUM_GPIO_PINS];
 unsigned int cur_src_pins[AD4170_NUM_CURRENT_SRC];
 struct gpio_chip gpiochip;
 /*
 * DMA (thus cache coherency maintenance) requires the transfer buffers
 * to live in their own cache lines.
 */

 u8 rx_buf[4] __aligned(IIO_DMA_MINALIGN);
};

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 = 0; i < ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl); i++) {
  tmp0 = div_u64_rem(st->mclk_hz, 32 * ad4170_sinc3_filt_fs_tbl[i],
       &tmp1);
  tmp1 = mult_frac(tmp1, MICRO, 32 * ad4170_sinc3_filt_fs_tbl[i]);
  /* 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_sinc5_filt_fs_tbl); i++) {
  tmp0 = div_u64_rem(st->mclk_hz, 32 * ad4170_sinc5_filt_fs_tbl[i],
       &tmp1);
  tmp1 = mult_frac(tmp1, MICRO, 32 * ad4170_sinc5_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_debugfs_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_get_reg_size(struct ad4170_state *st, unsigned int reg,
          unsigned int *size)
{
 if (reg >= ARRAY_SIZE(ad4170_reg_size))
  return -EINVAL;

 *size = ad4170_reg_size[reg];

 return 0;
}

static int ad4170_reg_write(void *context, unsigned int reg, unsigned int val)
{
 struct ad4170_state *st = context;
 u8 tx_buf[AD4170_SPI_MAX_XFER_LEN];
 unsigned int size;
 int ret;

 ret = ad4170_get_reg_size(st, reg, &size);
 if (ret)
  return ret;

 put_unaligned_be16(reg, tx_buf);
 switch (size) {
 case 3:
  put_unaligned_be24(val, &tx_buf[AD4170_SPI_INST_PHASE_LEN]);
  break;
 case 2:
  put_unaligned_be16(val, &tx_buf[AD4170_SPI_INST_PHASE_LEN]);
  break;
 case 1:
  tx_buf[AD4170_SPI_INST_PHASE_LEN] = val;
  break;
 case 0:
  /* Write continuous read exit code */
  tx_buf[0] = AD4170_ADC_CTRL_CONT_READ_EXIT;
  return spi_write_then_read(st->spi, tx_buf, 1, NULL, 0);
 default:
  return -EINVAL;
 }

 return spi_write_then_read(st->spi, tx_buf,
       AD4170_SPI_INST_PHASE_LEN + size, NULL, 0);
}

static int ad4170_reg_read(void *context, unsigned int reg, unsigned int *val)
{
 struct ad4170_state *st = context;
 u8 tx_buf[AD4170_SPI_INST_PHASE_LEN];
 unsigned int size;
 int ret;

 put_unaligned_be16(AD4170_REG_READ_MASK | reg, tx_buf);

 ret = ad4170_get_reg_size(st, reg, &size);
 if (ret)
  return ret;

 ret = spi_write_then_read(st->spi, tx_buf, ARRAY_SIZE(tx_buf),
      st->rx_buf, size);
 if (ret)
  return ret;

 switch (size) {
 case 3:
  *val = get_unaligned_be24(st->rx_buf);
  return 0;
 case 2:
  *val = get_unaligned_be16(st->rx_buf);
  return 0;
 case 1:
  *val = st->rx_buf[0];
  return 0;
 default:
  return -EINVAL;
 }
}

static const struct regmap_config ad4170_regmap_config = {
 .reg_read = ad4170_reg_read,
 .reg_write = ad4170_reg_write,
};

static bool ad4170_setup_eq(struct ad4170_setup *a, struct ad4170_setup *b)
{
 if (a->misc != b->misc ||
     a->afe != b->afe ||
     a->filter != b->filter ||
     a->filter_fs != b->filter_fs ||
     a->offset != b->offset ||
     a->gain != b->gain)
  return false;

 return true;
}

static int ad4170_find_setup(struct ad4170_state *st,
        struct ad4170_setup *target_setup,
        unsigned int *setup_num, bool *overwrite)
{
 unsigned int i;

 *setup_num = AD4170_INVALID_SETUP;
 *overwrite = false;

 for (i = 0; i < AD4170_MAX_SETUPS; i++) {
  struct ad4170_setup_info *setup_info = &st->setup_infos[i];

  /* Immediately accept a matching setup. */
  if (ad4170_setup_eq(target_setup, &setup_info->setup)) {
   *setup_num = i;
   return 0;
  }

  /* Ignore all setups which are used by enabled channels. */
  if (setup_info->enabled_channels)
   continue;

  /* Find the least used slot. */
  if (*setup_num == AD4170_INVALID_SETUP ||
      setup_info->channels < st->setup_infos[*setup_num].channels)
   *setup_num = i;
 }

 if (*setup_num == AD4170_INVALID_SETUP)
  return -EINVAL;

 *overwrite = true;
 return 0;
}

static void ad4170_unlink_channel(struct ad4170_state *st, unsigned int channel)
{
 struct ad4170_chan_info *chan_info = &st->chan_infos[channel];
 struct ad4170_setup_info *setup_info = &st->setup_infos[chan_info->setup_num];

 chan_info->setup_num = AD4170_INVALID_SETUP;
 setup_info->channels--;
}

static int ad4170_unlink_setup(struct ad4170_state *st, unsigned int setup_num)
{
 unsigned int i;

 for (i = 0; i < AD4170_MAX_ADC_CHANNELS; i++) {
  struct ad4170_chan_info *chan_info = &st->chan_infos[i];

  if (!chan_info->initialized || chan_info->setup_num != setup_num)
   continue;

  ad4170_unlink_channel(st, i);
 }
 return 0;
}

static int ad4170_link_channel_setup(struct ad4170_state *st,
         unsigned int chan_addr,
         unsigned int setup_num)
{
 struct ad4170_setup_info *setup_info = &st->setup_infos[setup_num];
 struct ad4170_chan_info *chan_info = &st->chan_infos[chan_addr];
 int ret;

 ret = regmap_update_bits(st->regmap, AD4170_CHAN_SETUP_REG(chan_addr),
     AD4170_CHAN_SETUP_SETUP_MSK,
     FIELD_PREP(AD4170_CHAN_SETUP_SETUP_MSK, setup_num));
 if (ret)
  return ret;

 chan_info->setup_num = setup_num;
 setup_info->channels++;
 return 0;
}

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 = regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG,
     AD4170_ADC_CTRL_MODE_MSK,
     FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK,
         AD4170_ADC_CTRL_MODE_IDLE));
 if (ret)
  return ret;

 ret = regmap_write(st->regmap, AD4170_MISC_REG(setup_num), setup->misc);
 if (ret)
  return ret;

 ret = regmap_write(st->regmap, AD4170_AFE_REG(setup_num), setup->afe);
 if (ret)
  return ret;

 ret = regmap_write(st->regmap, AD4170_FILTER_REG(setup_num),
      setup->filter);
 if (ret)
  return ret;

 ret = regmap_write(st->regmap, AD4170_FILTER_FS_REG(setup_num),
      setup->filter_fs);
 if (ret)
  return ret;

 ret = regmap_write(st->regmap, AD4170_OFFSET_REG(setup_num),
      setup->offset);
 if (ret)
  return ret;

 ret = regmap_write(st->regmap, AD4170_GAIN_REG(setup_num), setup->gain);
 if (ret)
  return ret;

 memcpy(&st->setup_infos[setup_num].setup, setup, sizeof(*setup));
 return 0;
}

static int ad4170_write_channel_setup(struct ad4170_state *st,
          unsigned int chan_addr, bool on_enable)
{
 struct ad4170_chan_info *chan_info = &st->chan_infos[chan_addr];
 bool overwrite;
 int setup_num;
 int ret;

 /*
 * Similar to AD4130 driver, the following cases need to be handled.
 *
 * 1. Enabled and linked channel with setup changes:
 *    - Find a setup. If not possible, return error.
 *    - Unlink channel from current setup.
 *    - If the setup found has only disabled channels linked to it,
 *      unlink all channels, and write the new setup to it.
 *    - Link channel to new setup.
 *
 * 2. Soon to be enabled and unlinked channel:
 *    - Find a setup. If not possible, return error.
 *    - If the setup found has only disabled channels linked to it,
 *      unlink all channels, and write the new setup to it.
 *    - Link channel to the setup.
 *
 * 3. Disabled and linked channel with setup changes:
 *    - Unlink channel from current setup.
 *
 * 4. Soon to be enabled and linked channel:
 * 5. Disabled and unlinked channel with setup changes:
 *    - Do nothing.
 */


 /* Cases 3, 4, and 5 */
 if (chan_info->setup_num != AD4170_INVALID_SETUP) {
  /* Case 4 */
  if (on_enable)
   return 0;

  /* Case 3 */
  if (!chan_info->enabled) {
   ad4170_unlink_channel(st, chan_addr);
   return 0;
  }
 } else if (!on_enable && !chan_info->enabled) {
  /* Case 5 */
  return 0;
 }

 /* Cases 1 & 2 */
 ret = ad4170_find_setup(st, &chan_info->setup, &setup_num, &overwrite);
 if (ret)
  return ret;

 if (chan_info->setup_num != AD4170_INVALID_SETUP)
  /* Case 1 */
  ad4170_unlink_channel(st, chan_addr);

 if (overwrite) {
  ret = ad4170_unlink_setup(st, setup_num);
  if (ret)
   return ret;

  ret = ad4170_write_setup(st, setup_num, &chan_info->setup);
  if (ret)
   return ret;
 }

 return ad4170_link_channel_setup(st, chan_addr, setup_num);
}

static int ad4170_set_channel_enable(struct ad4170_state *st,
         unsigned int chan_addr, bool status)
{
 struct ad4170_chan_info *chan_info = &st->chan_infos[chan_addr];
 struct ad4170_setup_info *setup_info;
 int ret;

 if (chan_info->enabled == status)
  return 0;

 if (status) {
  ret = ad4170_write_channel_setup(st, chan_addr, true);
  if (ret)
   return ret;
 }

 setup_info = &st->setup_infos[chan_info->setup_num];

 ret = regmap_update_bits(st->regmap, AD4170_CHAN_EN_REG,
     AD4170_CHAN_EN(chan_addr),
     status ? AD4170_CHAN_EN(chan_addr) : 0);
 if (ret)
  return ret;

 setup_info->enabled_channels += status ? 1 : -1;
 chan_info->enabled = status;
 return 0;
}

static int __ad4170_get_filter_type(unsigned int filter)
{
 u16 f_conf = FIELD_GET(AD4170_FILTER_FILTER_TYPE_MSK, filter);

 switch (f_conf) {
 case AD4170_FILTER_FILTER_TYPE_SINC5_AVG:
  return AD4170_SINC5_AVG;
 case AD4170_FILTER_FILTER_TYPE_SINC5:
  return AD4170_SINC5;
 case AD4170_FILTER_FILTER_TYPE_SINC3:
  return AD4170_SINC3;
 default:
  return -EINVAL;
 }
}

static int ad4170_set_filter_type(struct iio_dev *indio_dev,
      struct iio_chan_spec const *chan,
      unsigned int val)
{
 struct ad4170_state *st = iio_priv(indio_dev);
 struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address];
 struct ad4170_setup *setup = &chan_info->setup;
 unsigned int filter_type_conf;
 int ret;

 switch (val) {
 case AD4170_SINC5_AVG:
  filter_type_conf = AD4170_FILTER_FILTER_TYPE_SINC5_AVG;
  break;
 case AD4170_SINC5:
  filter_type_conf = AD4170_FILTER_FILTER_TYPE_SINC5;
  break;
 case AD4170_SINC3:
  filter_type_conf = AD4170_FILTER_FILTER_TYPE_SINC3;
  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.
 */

 scoped_guard(mutex, &st->lock) {
  if (!iio_device_claim_direct(indio_dev))
   return -EBUSY;

  if (val == AD4170_SINC5_AVG || val == AD4170_SINC3)
   setup->filter_fs = clamp(val, AD4170_SINC3_MIN_FS,
       AD4170_SINC3_MAX_FS);
  else
   setup->filter_fs = clamp(val, AD4170_SINC5_MIN_FS,
       AD4170_SINC5_MAX_FS);

  setup->filter &= ~AD4170_FILTER_FILTER_TYPE_MSK;
  setup->filter |= FIELD_PREP(AD4170_FILTER_FILTER_TYPE_MSK,
         filter_type_conf);

  ret = ad4170_write_channel_setup(st, chan->address, false);
  iio_device_release_direct(indio_dev);
 }

 return ret;
}

static int ad4170_get_filter_type(struct iio_dev *indio_dev,
      struct iio_chan_spec const *chan)
{
 struct ad4170_state *st = iio_priv(indio_dev);
 struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address];
 struct ad4170_setup *setup = &chan_info->setup;

 return __ad4170_get_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_CALIBBIAS) |
         BIT(IIO_CHAN_INFO_CALIBSCALE) |
         BIT(IIO_CHAN_INFO_SAMP_FREQ) |
         BIT(IIO_CHAN_INFO_OFFSET),
 .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,
  .shift = 8,
  .endianness = IIO_BE,
 },
};

static const struct iio_chan_spec ad4170_temp_channel_template = {
 .type = IIO_TEMP,
 .indexed = 0,
 .channel = 17,
 .channel2 = 17,
 .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_SAMP_FREQ),
 .scan_type = {
  .sign = 's',
  .realbits = 24,
  .storagebits = 32,
  .shift = 8,
  .endianness = IIO_BE,
 },
};

/*
 * Receives the number of a multiplexed AD4170 input (ain_n), and stores the
 * voltage (in µV) of the specified input into ain_voltage. If the input number
 * is a ordinary analog input (AIN0 to AIN8), stores zero into ain_voltage.
 * If a voltage regulator required by a special input is unavailable, return
 * error code. Return 0 on success.
 */

static int ad4170_get_ain_voltage_uv(struct ad4170_state *st, int ain_n,
         int *ain_voltage)
{
 struct device *dev = &st->spi->dev;
 int v_diff;

 *ain_voltage = 0;
 /*
 * The voltage bias (vbias) sets the common-mode voltage of the channel
 * to (AVDD + AVSS)/2. If provided, AVSS supply provides the magnitude
 * (absolute value) of the negative voltage supplied to the AVSS pin.
 * So, we do AVDD - AVSS to compute the DC voltage generated by the bias
 * voltage generator.
 */

 if (st->pins_fn[ain_n] & AD4170_PIN_VBIAS) {
  int v_diff = st->vrefs_uv[AD4170_AVDD_SUP] - st->vrefs_uv[AD4170_AVSS_SUP];
  *ain_voltage = v_diff / 2;
  return 0;
 }

 if (ain_n <= AD4170_CHAN_MAP_TEMP_SENSOR)
  return 0;

 switch (ain_n) {
 case AD4170_CHAN_MAP_AVDD_AVSS_N:
  v_diff = st->vrefs_uv[AD4170_AVDD_SUP] - st->vrefs_uv[AD4170_AVSS_SUP];
  *ain_voltage = v_diff / 5;
  return 0;
 case AD4170_CHAN_MAP_IOVDD_DGND_N:
  *ain_voltage = st->vrefs_uv[AD4170_IOVDD_SUP] / 5;
  return 0;
 case AD4170_CHAN_MAP_AVSS:
  *ain_voltage = st->vrefs_uv[AD4170_AVSS_SUP];
  return 0;
 case AD4170_CHAN_MAP_DGND:
  *ain_voltage = 0;
  return 0;
 case AD4170_CHAN_MAP_REFIN1_P:
  if (st->vrefs_uv[AD4170_REFIN1P_SUP] == -ENODEV)
   return dev_err_probe(dev, -ENODEV,
          "input set to REFIN+ but ref not provided\n");

  *ain_voltage = st->vrefs_uv[AD4170_REFIN1P_SUP];
  return 0;
 case AD4170_CHAN_MAP_REFIN1_N:
  if (st->vrefs_uv[AD4170_REFIN1N_SUP] == -ENODEV)
   return dev_err_probe(dev, -ENODEV,
          "input set to REFIN- but ref not provided\n");

  *ain_voltage = st->vrefs_uv[AD4170_REFIN1N_SUP];
  return 0;
 case AD4170_CHAN_MAP_REFIN2_P:
  if (st->vrefs_uv[AD4170_REFIN2P_SUP] == -ENODEV)
   return dev_err_probe(dev, -ENODEV,
          "input set to REFIN2+ but ref not provided\n");

  *ain_voltage = st->vrefs_uv[AD4170_REFIN2P_SUP];
  return 0;
 case AD4170_CHAN_MAP_REFIN2_N:
  if (st->vrefs_uv[AD4170_REFIN2N_SUP] == -ENODEV)
   return dev_err_probe(dev, -ENODEV,
          "input set to REFIN2- but ref not provided\n");

  *ain_voltage = st->vrefs_uv[AD4170_REFIN2N_SUP];
  return 0;
 case AD4170_CHAN_MAP_REFOUT:
  /* REFOUT is 2.5V relative to AVSS so take that into account */
  *ain_voltage = st->vrefs_uv[AD4170_AVSS_SUP] + AD4170_INT_REF_2_5V;
  return 0;
 default:
  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_CURRENT_OUT)
   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_CHAN_MAP_AVDD_AVSS_P || pin > AD4170_CHAN_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
 * 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;

 ret = ad4170_validate_channel_input(st, chan->channel, false);
 if (ret)
  return ret;

 return ad4170_validate_channel_input(st, chan->channel2,
          !chan->differential);
}

/*
 * 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 ch_reg, unsigned int ref_sel)
{
 bool bipolar = chan->scan_type.sign == 's';
 struct device *dev = &st->spi->dev;
 int refp, refn, ain_voltage, ret;

 switch (ref_sel) {
 case AD4170_REF_REFIN1:
  if (st->vrefs_uv[AD4170_REFIN1P_SUP] == -ENODEV ||
      st->vrefs_uv[AD4170_REFIN1N_SUP] == -ENODEV)
   return dev_err_probe(dev, -ENODEV,
          "REFIN± selected but not provided\n");

  refp = st->vrefs_uv[AD4170_REFIN1P_SUP];
  refn = st->vrefs_uv[AD4170_REFIN1N_SUP];
  break;
 case AD4170_REF_REFIN2:
  if (st->vrefs_uv[AD4170_REFIN2P_SUP] == -ENODEV ||
      st->vrefs_uv[AD4170_REFIN2N_SUP] == -ENODEV)
   return dev_err_probe(dev, -ENODEV,
          "REFIN2± selected but not provided\n");

  refp = st->vrefs_uv[AD4170_REFIN2P_SUP];
  refn = st->vrefs_uv[AD4170_REFIN2N_SUP];
  break;
 case AD4170_REF_AVDD:
  refp = st->vrefs_uv[AD4170_AVDD_SUP];
  refn = st->vrefs_uv[AD4170_AVSS_SUP];
  break;
 case AD4170_REF_REFOUT:
  /* REFOUT is 2.5 V relative to AVSS */
  refp = st->vrefs_uv[AD4170_AVSS_SUP] + AD4170_INT_REF_2_5V;
  refn = st->vrefs_uv[AD4170_AVSS_SUP];
  break;
 default:
  return -EINVAL;
 }

 /*
 * Find out the analog input range from the channel type, polarity, and
 * voltage reference selection.
 * AD4170 channels are either differential or pseudo-differential.
 * Diff input voltage range: −VREF/gain to +VREF/gain (datasheet page 6)
 * Pseudo-diff input voltage range: 0 to VREF/gain (datasheet page 6)
 */

 if (chan->differential) {
  if (!bipolar)
   return dev_err_probe(dev, -EINVAL,
          "Channel %u differential unipolar\n",
          ch_reg);

  /*
 * Differential bipolar channel.
 * avss-supply is never above 0V.
 * Assuming refin1n-supply not above 0V.
 * Assuming refin2n-supply not above 0V.
 */

  return refp + abs(refn);
 }
 /*
 * Some configurations can lead to invalid setups.
 * For example, if AVSS = -2.5V, REF_SELECT set to REFOUT (REFOUT/AVSS),
 * and pseudo-diff 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.
 * Note that at this point in the code, refp can only be >= 0 since all
 * error codes from reading the regulator voltage have been checked
 * either at ad4170_regulator_setup() or above in this function.
 */

 if (refp == 0)
  return dev_err_probe(dev, -EINVAL,
         "REF+ == GND for pseudo-diff chan %u\n",
         ch_reg);

 if (bipolar)
  return refp;

 /*
 * Pseudo-differential unipolar channel.
 * Input expected to swing from IN- to +VREF.
 */

 ret = ad4170_get_ain_voltage_uv(st, chan->channel2, &ain_voltage);
 if (ret)
  return ret;

 if (refp - ain_voltage <= 0)
  return dev_err_probe(dev, -EINVAL,
         "Negative input >= REF+ for pseudo-diff chan %u\n",
         ch_reg);

 return refp - ain_voltage;
}

static int __ad4170_read_sample(struct iio_dev *indio_dev,
    struct iio_chan_spec const *chan, int *val)
{
 struct ad4170_state *st = iio_priv(indio_dev);
 unsigned long settling_time_ms;
 int ret;

 reinit_completion(&st->completion);
 ret = regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG,
     AD4170_ADC_CTRL_MODE_MSK,
     FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK,
         AD4170_ADC_CTRL_MODE_SINGLE));
 if (ret)
  return ret;

 /*
 * When a channel is manually selected by the user, the ADC needs an
 * extra time to provide the first stable conversion. The ADC settling
 * time depends on the filter type, filter frequency, and ADC clock
 * frequency (see datasheet page 53). The maximum settling time among
 * all filter configurations is 6291164 / fCLK. Use that formula to wait
 * for sufficient time whatever the filter configuration may be.
 */

 settling_time_ms = DIV_ROUND_UP(6291164 * MILLI, st->mclk_hz);
 ret = wait_for_completion_timeout(&st->completion,
       msecs_to_jiffies(settling_time_ms));
 if (!ret)
  dev_dbg(&st->spi->dev,
   "No Data Ready signal. Reading after delay.\n");

 ret = regmap_read(st->regmap, AD4170_DATA_24B_REG, val);
 if (ret)
  return ret;

 if (chan->scan_type.sign == 's')
  *val = sign_extend32(*val, chan->scan_type.realbits - 1);

 return 0;
}

static int ad4170_read_sample(struct iio_dev *indio_dev,
         struct iio_chan_spec const *chan, int *val)
{
 struct ad4170_state *st = iio_priv(indio_dev);
 struct device *dev = &st->spi->dev;
 int ret, ret2;

 /*
 * The ADC sequences through all enabled channels. That can lead to
 * incorrect channel being sampled if a previous read would have left a
 * different channel enabled. Thus, always enable and disable the
 * channel on single-shot read.
 */

 ret = ad4170_set_channel_enable(st, chan->address, true);
 if (ret)
  return ret;

 ret = __ad4170_read_sample(indio_dev, chan, val);
 if (ret) {
  dev_err(dev, "failed to read sample: %d\n", ret);

  ret2 = ad4170_set_channel_enable(st, chan->address, false);
  if (ret2)
   dev_err(dev, "failed to disable channel: %d\n", ret2);

  return ret;
 }

 ret = ad4170_set_channel_enable(st, chan->address, false);
 if (ret)
  return ret;

 return IIO_VAL_INT;
}

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->chan_infos[chan->address];
 struct ad4170_setup *setup = &chan_info->setup;
 enum ad4170_filter_type f_type;
 unsigned int pga, fs_idx;
 int ret;

 guard(mutex)(&st->lock);
 switch (info) {
 case IIO_CHAN_INFO_RAW:
  if (!iio_device_claim_direct(indio_dev))
   return -EBUSY;

  ret = ad4170_read_sample(indio_dev, chan, val);
  iio_device_release_direct(indio_dev);
  return ret;
 case IIO_CHAN_INFO_SCALE:
  pga = FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe);
  switch (chan->type) {
  case IIO_VOLTAGE:
   *val = chan_info->scale_tbl[pga][0];
   *val2 = chan_info->scale_tbl[pga][1];
   return IIO_VAL_INT_PLUS_NANO;
  case IIO_TEMP:
   /*
 * The scale_tbl converts output codes to mV units so
 * multiply by MILLI to make the factor convert to µV.
 * Then, apply the temperature sensor change sensitivity
 * of 477 μV/K. Finally, multiply the result by MILLI
 * again to comply with milli degrees Celsius IIO ABI.
 */

   *val = 0;
   *val2 = DIV_ROUND_CLOSEST(chan_info->scale_tbl[pga][1] * MILLI, 477) *
        MILLI;
   return IIO_VAL_INT_PLUS_NANO;
  default:
   return -EINVAL;
  }
 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_get_filter_type(setup->filter);
  switch (f_type) {
  case AD4170_SINC5_AVG:
  case AD4170_SINC3:
   fs_idx = find_closest(setup->filter_fs,
           ad4170_sinc3_filt_fs_tbl,
           ARRAY_SIZE(ad4170_sinc3_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 AD4170_SINC5:
   fs_idx = find_closest(setup->filter_fs,
           ad4170_sinc5_filt_fs_tbl,
           ARRAY_SIZE(ad4170_sinc5_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;
  default:
   return -EINVAL;
  }
 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,
     struct iio_chan_spec const *chan)
{
 struct ad4170_state *st = iio_priv(indio_dev);
 struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address];
 struct device *dev = &st->spi->dev;
 int bipolar = chan->scan_type.sign == 's' ? 1 : 0;
 int precision_bits = chan->scan_type.realbits;
 int pga, ainm_voltage, ret;
 unsigned long long offset;

 ainm_voltage = 0;
 ret = ad4170_get_ain_voltage_uv(st, chan->channel2, &ainm_voltage);
 if (ret < 0)
  return dev_err_probe(dev, ret, "Failed to fill scale table\n");

 for (pga = 0; pga < AD4170_NUM_PGA_OPTIONS; pga++) {
  u64 nv;
  unsigned int lshift, rshift;

  /*
 * The PGA options are numbered from 0 to 9, with option 0 being
 * a gain of 2^0 (no actual gain), and 7 meaning a gain of 2^7.
 * Option 8, though, sets a gain of 0.5, so the input signal can
 * be attenuated by 2 rather than amplified. Option 9, allows
 * the signal to bypass the PGA circuitry (no gain).
 *
 * 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 to avoid truncating the less
 * significant bits when right shifting it so to preserve scale
 * precision.
 */

  nv = (u64)chan_info->input_range_uv * NANO;
  lshift = !!(pga & BIT(3)); /* handle PGA options 8 and 9 */
  rshift = precision_bits - bipolar + (pga & GENMASK(2, 0)) - 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)abs(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.
 */

  if (ainm_voltage > 0)
   chan_info->offset_tbl[pga] = -(int)(offset);
  else
   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->chan_infos[chan->address];
 enum ad4170_filter_type f_type;

 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:
  *type = IIO_VAL_INT_PLUS_MICRO;
  f_type = ad4170_get_filter_type(indio_dev, chan);
  switch (f_type) {
  case AD4170_SINC5_AVG:
  case AD4170_SINC3:
   /* Read sps_tbl here to ensure in bounds array access */
   *vals = (int *)st->sps_tbl[f_type];
   *length = ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl) * 2;
   return IIO_AVAIL_LIST;
  case AD4170_SINC5:
   /* Read sps_tbl here to ensure in bounds array access */
   *vals = (int *)st->sps_tbl[f_type];
   *length = ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl) * 2;
   return IIO_AVAIL_LIST;
  default:
   return -EINVAL;
  }
 default:
  return -EINVAL;
 }
}

static int ad4170_set_pga(struct ad4170_state *st,
     struct iio_chan_spec const *chan, int val, int val2)
{
 struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address];
 struct ad4170_setup *setup = &chan_info->setup;
 unsigned int pga;

 for (pga = 0; pga < AD4170_NUM_PGA_OPTIONS; pga++) {
  if (val == chan_info->scale_tbl[pga][0] &&
      val2 == chan_info->scale_tbl[pga][1])
   break;
 }

 if (pga == AD4170_NUM_PGA_OPTIONS)
  return -EINVAL;

 guard(mutex)(&st->lock);
 setup->afe &= ~AD4170_AFE_PGA_GAIN_MSK;
 setup->afe |= FIELD_PREP(AD4170_AFE_PGA_GAIN_MSK, pga);

 return ad4170_write_channel_setup(st, chan->address, false);
}

static int ad4170_set_channel_freq(struct ad4170_state *st,
       struct iio_chan_spec const *chan, int val,
       int val2)
{
 struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address];
 struct ad4170_setup *setup = &chan_info->setup;
 enum ad4170_filter_type f_type = __ad4170_get_filter_type(setup->filter);
 unsigned int filt_fs_tbl_size, i;

 switch (f_type) {
 case AD4170_SINC5_AVG:
 case AD4170_SINC3:
  filt_fs_tbl_size = ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl);
  break;
 case AD4170_SINC5:
  filt_fs_tbl_size = ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl);
  break;
 }

 for (i = 0; i < filt_fs_tbl_size; i++) {
  if (st->sps_tbl[f_type][i][0] == val &&
      st->sps_tbl[f_type][i][1] == val2)
   break;
 }
 if (i == filt_fs_tbl_size)
  return -EINVAL;

 guard(mutex)(&st->lock);
 if (f_type == AD4170_SINC5)
  setup->filter_fs = ad4170_sinc5_filt_fs_tbl[i];
 else
  setup->filter_fs = ad4170_sinc3_filt_fs_tbl[i];

 return ad4170_write_channel_setup(st, chan->address, false);
}

static int ad4170_set_calib_offset(struct ad4170_state *st,
       struct iio_chan_spec const *chan, int val)
{
 struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address];
 struct ad4170_setup *setup = &chan_info->setup;

 guard(mutex)(&st->lock);
 setup->offset = val;

 return ad4170_write_channel_setup(st, chan->address, false);
}

static int ad4170_set_calib_gain(struct ad4170_state *st,
     struct iio_chan_spec const *chan, int val)
{
 struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address];
 struct ad4170_setup *setup = &chan_info->setup;

 guard(mutex)(&st->lock);
 setup->gain = val;

 return ad4170_write_channel_setup(st, chan->address, false);
}

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);

 switch (info) {
 case IIO_CHAN_INFO_SCALE:
  return ad4170_set_pga(st, chan, val, val2);
 case IIO_CHAN_INFO_SAMP_FREQ:
  return ad4170_set_channel_freq(st, chan, val, val2);
 case IIO_CHAN_INFO_CALIBBIAS:
  return ad4170_set_calib_offset(st, chan, val);
 case IIO_CHAN_INFO_CALIBSCALE:
  return ad4170_set_calib_gain(st, chan, val);
 default:
  return -EINVAL;
 }
}

static int ad4170_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 = __ad4170_write_raw(indio_dev, chan, val, val2, info);
 iio_device_release_direct(indio_dev);
 return ret;
}

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_update_scan_mode(struct iio_dev *indio_dev,
       const unsigned long *active_scan_mask)
{
 struct ad4170_state *st = iio_priv(indio_dev);
 unsigned int chan_index;
 int ret;

 iio_for_each_active_channel(indio_dev, chan_index) {
  ret = ad4170_set_channel_enable(st, chan_index, 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_debugfs_reg_access,
};

static int ad4170_soft_reset(struct ad4170_state *st)
{
 int ret;

 ret = regmap_write(st->regmap, AD4170_CONFIG_A_REG,
      AD4170_SW_RESET_MSK);
 if (ret)
  return ret;

 /* AD4170-4 requires 1 ms between reset and any register access. */
 fsleep(1 * USEC_PER_MSEC);

 return 0;
}

static int ad4170_gpio_get(struct gpio_chip *gc, unsigned int offset)
{
 struct iio_dev *indio_dev = gpiochip_get_data(gc);
 struct ad4170_state *st = iio_priv(indio_dev);
 unsigned int val;
 int ret;

 if (!iio_device_claim_direct(indio_dev))
  return -EBUSY;

 ret = regmap_read(st->regmap, AD4170_GPIO_MODE_REG, &val);
 if (ret)
  goto err_release;

 /*
 * If the GPIO is configured as an input, read the current value from
 * AD4170_GPIO_INPUT_REG. Otherwise, read the input value from
 * AD4170_GPIO_OUTPUT_REG.
 */

 if (val & BIT(offset * 2))
  ret = regmap_read(st->regmap, AD4170_GPIO_INPUT_REG, &val);
 else
  ret = regmap_read(st->regmap, AD4170_GPIO_OUTPUT_REG, &val);
 if (ret)
  goto err_release;

 ret = !!(val & BIT(offset));
err_release:
 iio_device_release_direct(indio_dev);

 return ret;
}

static int ad4170_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
{
 struct iio_dev *indio_dev = gpiochip_get_data(gc);
 struct ad4170_state *st = iio_priv(indio_dev);
 int ret;

 if (!iio_device_claim_direct(indio_dev))
  return -EBUSY;

 ret = regmap_assign_bits(st->regmap, AD4170_GPIO_OUTPUT_REG,
     BIT(offset), !!value);

 iio_device_release_direct(indio_dev);
 return ret;
}

static int ad4170_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
{
 struct iio_dev *indio_dev = gpiochip_get_data(gc);
 struct ad4170_state *st = iio_priv(indio_dev);
 unsigned int val;
 int ret;

 if (!iio_device_claim_direct(indio_dev))
  return -EBUSY;

 ret = regmap_read(st->regmap, AD4170_GPIO_MODE_REG, &val);
 if (ret)
  goto err_release;

 if (val & BIT(offset * 2 + 1))
  ret = GPIO_LINE_DIRECTION_OUT;
 else
  ret = GPIO_LINE_DIRECTION_IN;

err_release:
 iio_device_release_direct(indio_dev);

 return ret;
}

static int ad4170_gpio_direction_input(struct gpio_chip *gc, unsigned int offset)
{
 struct iio_dev *indio_dev = gpiochip_get_data(gc);
 struct ad4170_state *st = iio_priv(indio_dev);
 unsigned long gpio_mask;
 int ret;

 if (!iio_device_claim_direct(indio_dev))
  return -EBUSY;

 switch (offset) {
 case 0:
  gpio_mask = AD4170_GPIO_MODE_GPIO0_MSK;
  break;
 case 1:
  gpio_mask = AD4170_GPIO_MODE_GPIO1_MSK;
  break;
 case 2:
  gpio_mask = AD4170_GPIO_MODE_GPIO2_MSK;
  break;
 case 3:
  gpio_mask = AD4170_GPIO_MODE_GPIO3_MSK;
  break;
 default:
  ret = -EINVAL;
  goto err_release;
 }
 ret = regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, gpio_mask,
     AD4170_GPIO_MODE_GPIO_INPUT << (2 * offset));

err_release:
 iio_device_release_direct(indio_dev);

 return ret;
}

static int ad4170_gpio_direction_output(struct gpio_chip *gc,
     unsigned int offset, int value)
{
 struct iio_dev *indio_dev = gpiochip_get_data(gc);
 struct ad4170_state *st = iio_priv(indio_dev);
 unsigned long gpio_mask;
 int ret;

 ret = ad4170_gpio_set(gc, offset, value);
 if (ret)
  return ret;

 if (!iio_device_claim_direct(indio_dev))
  return -EBUSY;

 switch (offset) {
 case 0:
  gpio_mask = AD4170_GPIO_MODE_GPIO0_MSK;
  break;
 case 1:
  gpio_mask = AD4170_GPIO_MODE_GPIO1_MSK;
  break;
 case 2:
  gpio_mask = AD4170_GPIO_MODE_GPIO2_MSK;
  break;
 case 3:
  gpio_mask = AD4170_GPIO_MODE_GPIO3_MSK;
  break;
 default:
  ret = -EINVAL;
  goto err_release;
 }
 ret = regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, gpio_mask,
     AD4170_GPIO_MODE_GPIO_OUTPUT << (2 * offset));

err_release:
 iio_device_release_direct(indio_dev);

 return ret;
}

static int ad4170_gpio_init_valid_mask(struct gpio_chip *gc,
           unsigned long *valid_mask,
           unsigned int ngpios)
{
 struct ad4170_state *st = gpiochip_get_data(gc);
 unsigned int i;

 /* Only expose GPIOs that were not assigned any other function. */
 for (i = 0; i < ngpios; i++) {
  bool valid = st->gpio_fn[i] == AD4170_GPIO_UNASSIGNED;

  __assign_bit(i, valid_mask, valid);
 }

 return 0;
}

static int ad4170_gpio_init(struct iio_dev *indio_dev)
{
 struct ad4170_state *st = iio_priv(indio_dev);

 st->gpiochip.label = "ad4170_gpios";
 st->gpiochip.base = -1;
 st->gpiochip.ngpio = AD4170_NUM_GPIO_PINS;
 st->gpiochip.parent = &st->spi->dev;
 st->gpiochip.can_sleep = true;
 st->gpiochip.init_valid_mask = ad4170_gpio_init_valid_mask;
 st->gpiochip.get_direction = ad4170_gpio_get_direction;
 st->gpiochip.direction_input = ad4170_gpio_direction_input;
 st->gpiochip.direction_output = ad4170_gpio_direction_output;
 st->gpiochip.get = ad4170_gpio_get;
 st->gpiochip.set = ad4170_gpio_set;
 st->gpiochip.owner = THIS_MODULE;

 return devm_gpiochip_add_data(&st->spi->dev, &st->gpiochip, indio_dev);
}

static int ad4170_validate_excitation_pin(struct ad4170_state *st, u32 pin)
{
 struct device *dev = &st->spi->dev;
 unsigned int i;

 /* Check the pin number is valid */
 for (i = 0; i < ARRAY_SIZE(ad4170_iout_pin_tbl); i++)
  if (ad4170_iout_pin_tbl[i] == pin)
   break;

 if (i == ARRAY_SIZE(ad4170_iout_pin_tbl))
  return dev_err_probe(dev, -EINVAL,
         "Invalid excitation pin: %u\n",
         pin);

 /* Check the pin is available */
 if (pin <= AD4170_MAX_ANALOG_PINS) {
  if (st->pins_fn[pin] != AD4170_PIN_UNASSIGNED)
   return dev_err_probe(dev, -EINVAL,
          "Pin %u already used with fn %u\n",
          pin, st->pins_fn[pin]);

  st->pins_fn[pin] |= AD4170_PIN_CURRENT_OUT;
 } else {
  unsigned int gpio = pin - AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(0);

  if (st->gpio_fn[gpio] != AD4170_GPIO_UNASSIGNED)
   return dev_err_probe(dev, -EINVAL,
          "GPIO %u already used with fn %u\n",
          gpio, st->gpio_fn[gpio]);

  st->gpio_fn[gpio] |= AD4170_GPIO_AC_EXCITATION;
 }

 return 0;
}

static int ad4170_validate_excitation_pins(struct ad4170_state *st,
        u32 *exc_pins, int num_exc_pins)
{
 unsigned int i;
 int ret;

 for (i = 0; i < num_exc_pins; i++) {
  ret = ad4170_validate_excitation_pin(st, exc_pins[i]);
  if (ret)
   return ret;
 }
 return 0;
}

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",
};

/*
 * 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 ad4170_state *st,
        struct fwnode_handle *child,
        unsigned int *exc_pins,
        unsigned int *exc_curs,
        unsigned int *num_exc_pins)
{
 struct device *dev = &st->spi->dev;
 unsigned int num_pins, i, j;
 u32 pin, val;
 int ret;

 num_pins = 0;
 for (i = 0; i < AD4170_NUM_CURRENT_SRC; i++) {
  /* Parse excitation current output pin properties. */
  pin = AD4170_CURRENT_SRC_I_OUT_PIN_AIN(0);
  ret = fwnode_property_read_u32(child, ad4170_i_out_pin_dt_props[i],
            &pin);
  if (ret)
   continue;

  exc_pins[num_pins] = pin;

  /* Parse excitation current value properties. */
  val = ad4170_iout_current_ua_tbl[0];
  fwnode_property_read_u32(child,
      ad4170_i_out_val_dt_props[i], &val);

  for (j = 0; j < ARRAY_SIZE(ad4170_iout_current_ua_tbl); j++)
   if (ad4170_iout_current_ua_tbl[j] == val)
    break;

  if (j == ARRAY_SIZE(ad4170_iout_current_ua_tbl))
   return dev_err_probe(dev, -EINVAL, "Invalid %s: %uuA\n",
          ad4170_i_out_val_dt_props[i], val);

  exc_curs[num_pins] = j;
  num_pins++;
 }
 *num_exc_pins = num_pins;

 return 0;
}

static int ad4170_setup_current_src(struct ad4170_state *st,
        struct fwnode_handle *child,
        struct ad4170_setup *setup, u32 *exc_pins,
        unsigned int *exc_curs, int num_exc_pins,
        bool ac_excited)
{
 unsigned int exc_cur_pair, i, j;
 int ret;

 for (i = 0; i < num_exc_pins; i++) {
  unsigned int exc_cur = exc_curs[i];
  unsigned int pin = exc_pins[i];
  unsigned int current_src = 0;

  for (j = 0; j < AD4170_NUM_CURRENT_SRC; j++)
   if (st->cur_src_pins[j] == AD4170_CURRENT_SRC_DISABLED)
    break;

  if (j == AD4170_NUM_CURRENT_SRC)
   return dev_err_probe(&st->spi->dev, -EINVAL,
          "Too many excitation current sources\n");

  current_src |= FIELD_PREP(AD4170_CURRENT_SRC_I_OUT_PIN_MSK, pin);
  current_src |= FIELD_PREP(AD4170_CURRENT_SRC_I_OUT_VAL_MSK, exc_cur);
  st->cur_src_pins[j] = pin;
  ret = regmap_write(st->regmap, AD4170_CURRENT_SRC_REG(j),
       current_src);
  if (ret)
   return ret;
 }

 if (!ac_excited)
  return 0;

 if (num_exc_pins < 2)
  return dev_err_probe(&st->spi->dev, -EINVAL,
   "Current chopping requested but only one pin provided: %u\n",
   exc_pins[0]);

 /*
 * Two use cases to handle here:
 * - 2 pairs of excitation currents;
 * - 1 pair of excitation currents.
 */

 if (num_exc_pins == 4) {
  for (i = 0; i < AD4170_NUM_CURRENT_SRC; i++)
   if (st->cur_src_pins[i] != exc_pins[i])
    return dev_err_probe(&st->spi->dev, -EINVAL,
           "Unable to use 4 exc pins\n");
 } else {
  /*
 * Excitation current chopping is configured in pairs. Current
 * sources IOUT0 and IOUT1 form pair 1, IOUT2 and IOUT3 make up
 * pair 2. So, if current chopping was requested, check if the
 * first end of the first pair of excitation currents is
 * available. Try the next pair if IOUT0 has already been
 * configured for another channel.
 */

  i = st->cur_src_pins[0] == exc_pins[0] ? 0 : 2;

  if (st->cur_src_pins[i] != exc_pins[0] ||
      st->cur_src_pins[i + 1] != exc_pins[1])
   return dev_err_probe(&st->spi->dev, -EINVAL,
          "Failed to setup current chopping\n");

  st->cur_src_pins[i] = exc_pins[0];
  st->cur_src_pins[i + 1] = exc_pins[1];

  if (i == 0)
   exc_cur_pair = AD4170_MISC_CHOP_IEXC_PAIR1;
  else
   exc_cur_pair = AD4170_MISC_CHOP_IEXC_PAIR2;
 }

 /*
 * Configure excitation current chopping.
 * Chop both pairs if using four excitation pins.
 */

 setup->misc |= FIELD_PREP(AD4170_MISC_CHOP_IEXC_MSK,
      num_exc_pins == 2 ?
      exc_cur_pair :
      AD4170_MISC_CHOP_IEXC_BOTH);

 return 0;
}

static int ad4170_setup_bridge(struct ad4170_state *st,
          struct fwnode_handle *child,
          struct ad4170_setup *setup, u32 *exc_pins,
          unsigned int *exc_curs, int num_exc_pins,
          bool ac_excited)
{
 unsigned long gpio_mask;
 unsigned int i;
 int ret;

 /*
 * If a specific current is provided through
 * adi,excitation-current-n-microamp, set excitation pins provided
 * through adi,excitation-pin-n to excite the bridge circuit.
 */

 for (i = 0; i < num_exc_pins; i++)
  if (exc_curs[i] > 0)
   return ad4170_setup_current_src(st, child, setup, exc_pins,
       exc_curs, num_exc_pins,
       ac_excited);

 /*
 * Else, use predefined ACX1, ACX1 negated, ACX2, ACX2 negated signals
 * to AC excite the bridge. Those signals are output on GPIO2, GPIO0,
 * GPIO3, and GPIO1, respectively. If only two pins are specified for AC
 * excitation, use ACX1 and ACX2 (GPIO2 and GPIO3).
 *
 * Also, to avoid any short-circuit condition when more than one channel
 * is enabled, set GPIO2 and GPIO0 high, and set GPIO1 and GPIO3 low to
 * DC excite the bridge whenever a channel without AC excitation is
 * selected. That is needed because GPIO pins are controlled by the next
 * highest priority GPIO function when a channel doesn't enable AC
 * excitation. See datasheet Figure 113 Weigh Scale (AC Excitation) for
 * the reference circuit diagram.
 */

 if (num_exc_pins == 2) {
  setup->misc |= FIELD_PREP(AD4170_MISC_CHOP_ADC_MSK, 0x3);

  gpio_mask = AD4170_GPIO_MODE_GPIO3_MSK | AD4170_GPIO_MODE_GPIO2_MSK;
  ret = regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, gpio_mask,
      FIELD_PREP(AD4170_GPIO_MODE_GPIO3_MSK,
          AD4170_GPIO_MODE_GPIO_OUTPUT) |
      FIELD_PREP(AD4170_GPIO_MODE_GPIO2_MSK,
          AD4170_GPIO_MODE_GPIO_OUTPUT));
  if (ret)
   return ret;

  /*
 * Set GPIO2 high and GPIO3 low to DC excite the bridge when
 * a different channel is selected.
 */

  gpio_mask = AD4170_GPIO_OUTPUT_GPIO_MSK(3) |
       AD4170_GPIO_OUTPUT_GPIO_MSK(2);
  ret = regmap_update_bits(st->regmap, AD4170_GPIO_OUTPUT_REG, gpio_mask,
      FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(3), 0) |
      FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(2), 1));
  if (ret)
   return ret;

  st->gpio_fn[3] |= AD4170_GPIO_OUTPUT;
  st->gpio_fn[2] |= AD4170_GPIO_OUTPUT;
 } else {
  setup->misc |= FIELD_PREP(AD4170_MISC_CHOP_ADC_MSK, 0x2);

  gpio_mask = AD4170_GPIO_MODE_GPIO3_MSK | AD4170_GPIO_MODE_GPIO2_MSK |
       AD4170_GPIO_MODE_GPIO1_MSK | AD4170_GPIO_MODE_GPIO0_MSK;
  ret = regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, gpio_mask,
      FIELD_PREP(AD4170_GPIO_MODE_GPIO3_MSK,
          AD4170_GPIO_MODE_GPIO_OUTPUT) |
      FIELD_PREP(AD4170_GPIO_MODE_GPIO2_MSK,
          AD4170_GPIO_MODE_GPIO_OUTPUT) |
      FIELD_PREP(AD4170_GPIO_MODE_GPIO1_MSK,
          AD4170_GPIO_MODE_GPIO_OUTPUT) |
      FIELD_PREP(AD4170_GPIO_MODE_GPIO0_MSK,
          AD4170_GPIO_MODE_GPIO_OUTPUT));
  if (ret)
   return ret;

  /*
 * Set GPIO2 and GPIO0 high, and set GPIO1 and GPIO3 low to DC
 * excite the bridge when a different channel is selected.
 */

  gpio_mask = AD4170_GPIO_OUTPUT_GPIO_MSK(3) |
       AD4170_GPIO_OUTPUT_GPIO_MSK(2) |
       AD4170_GPIO_OUTPUT_GPIO_MSK(1) |
       AD4170_GPIO_OUTPUT_GPIO_MSK(0);
  ret = regmap_update_bits(st->regmap, AD4170_GPIO_OUTPUT_REG, gpio_mask,
      FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(3), 0) |
      FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(2), 1) |
      FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(1), 0) |
      FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(0), 1));
  if (ret)
   return ret;

  st->gpio_fn[3] |= AD4170_GPIO_OUTPUT;
  st->gpio_fn[2] |= AD4170_GPIO_OUTPUT;
  st->gpio_fn[1] |= AD4170_GPIO_OUTPUT;
  st->gpio_fn[0] |= AD4170_GPIO_OUTPUT;
 }

 return 0;
}

static int ad4170_setup_rtd(struct ad4170_state *st,
       struct fwnode_handle *child,
       struct ad4170_setup *setup, u32 *exc_pins,
       unsigned int *exc_curs, int num_exc_pins, bool ac_excited)
{
 return ad4170_setup_current_src(st, child, setup, exc_pins,
     exc_curs, num_exc_pins, ac_excited);
}

static int ad4170_parse_external_sensor(struct ad4170_state *st,
     struct fwnode_handle *child,
     struct ad4170_setup *setup,
     struct iio_chan_spec *chan,
     unsigned int s_type)
{
 unsigned int num_exc_pins, reg_val;
 struct device *dev = &st->spi->dev;
 u32 pins[2], exc_pins[4], exc_curs[4];
 bool ac_excited;
 int ret;

 ret = fwnode_property_read_u32_array(child, "diff-channels", pins,
          ARRAY_SIZE(pins));
 if (ret)
  return dev_err_probe(dev, ret,
         "Failed to read sensor diff-channels\n");

 chan->differential = true;
 chan->channel = pins[0];
 chan->channel2 = pins[1];

 ret = ad4170_parse_exc_current(st, child, exc_pins, exc_curs, &num_exc_pins);
 if (ret)
  return ret;

 /* The external sensor may not need excitation from the ADC chip. */
 if (num_exc_pins == 0)
  return 0;

 ret = ad4170_validate_excitation_pins(st, exc_pins, num_exc_pins);
 if (ret)
  return ret;

 ac_excited = fwnode_property_read_bool(child, "adi,excitation-ac");

 if (s_type == AD4170_THERMOCOUPLE_SENSOR) {
  if (st->pins_fn[chan->channel2] & AD4170_PIN_VBIAS) {
   reg_val = BIT(chan->channel2);
   ret = regmap_write(st->regmap, AD4170_V_BIAS_REG, reg_val);
   if (ret)
    dev_err_probe(dev, ret, "Failed to set vbias\n");
  }
 }
 if (s_type == AD4170_WEIGH_SCALE_SENSOR)
  ret = ad4170_setup_bridge(st, child, setup, exc_pins, exc_curs,
       num_exc_pins, ac_excited);
 else
  ret = ad4170_setup_rtd(st, child, setup, exc_pins, exc_curs,
           num_exc_pins, ac_excited);

 return ret;
}

static int ad4170_parse_reference(struct ad4170_state *st,
      struct fwnode_handle *child,
      struct ad4170_setup *setup)
{
 struct device *dev = &st->spi->dev;
 const char *propname;
 u32 aux;
 int ret;

 /* Optional positive reference buffering */
 propname = "adi,positive-reference-buffer";
 ret = device_property_match_property_string(dev, propname,
          ad4170_ref_buf_str,
          ARRAY_SIZE(ad4170_ref_buf_str));

 /* Default to full precharge buffer enabled. */
 setup->afe |= FIELD_PREP(AD4170_AFE_REF_BUF_P_MSK,
     ret >= 0 ? ret : AD4170_REF_BUF_FULL);

 /* Optional negative reference buffering */
 propname = "adi,negative-reference-buffer";
 ret = device_property_match_property_string(dev, propname,
          ad4170_ref_buf_str,
          ARRAY_SIZE(ad4170_ref_buf_str));

 /* Default to full precharge buffer enabled. */
 setup->afe |= FIELD_PREP(AD4170_AFE_REF_BUF_M_MSK,
     ret >= 0 ? ret : AD4170_REF_BUF_FULL);

 /* Optional voltage reference selection */
 propname = "adi,reference-select";
 aux = AD4170_REF_REFOUT; /* Default reference selection. */
 fwnode_property_read_u32(child, propname, &aux);
 if (aux > AD4170_REF_AVDD)
  return dev_err_probe(dev, -EINVAL, "Invalid %s: %u\n",
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=95 G=95

¤ Dauer der Verarbeitung: 0.27 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.