diff options
Diffstat (limited to 'drivers/iio/adc/ad7173.c')
| -rw-r--r-- | drivers/iio/adc/ad7173.c | 1147 |
1 files changed, 882 insertions, 265 deletions
diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index 0702ec71aa29..d36612352b44 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -3,11 +3,12 @@ * AD717x and AD411x family SPI ADC driver * * Supported devices: - * AD4111/AD4112/AD4114/AD4115/AD4116 + * AD4111/AD4112/AD4113/AD4114/AD4115/AD4116 * AD7172-2/AD7172-4/AD7173-8/AD7175-2 * AD7175-8/AD7176-2/AD7177-2 * * Copyright (C) 2015, 2024 Analog Devices, Inc. + * Copyright (C) 2025 BayLibre, SAS */ #include <linux/array_size.h> @@ -35,6 +36,7 @@ #include <linux/units.h> #include <linux/iio/buffer.h> +#include <linux/iio/events.h> #include <linux/iio/iio.h> #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> @@ -76,14 +78,15 @@ (x) == AD7173_AIN_REF_NEG) #define AD7172_2_ID 0x00d0 -#define AD7175_ID 0x0cd0 #define AD7176_ID 0x0c90 +#define AD7175_ID 0x0cd0 #define AD7175_2_ID 0x0cd0 #define AD7172_4_ID 0x2050 #define AD7173_ID 0x30d0 #define AD4111_ID AD7173_ID #define AD4112_ID AD7173_ID #define AD4114_ID AD7173_ID +#define AD4113_ID 0x31d0 #define AD4116_ID 0x34d0 #define AD4115_ID 0x38d0 #define AD7175_8_ID 0x3cd0 @@ -101,6 +104,7 @@ #define AD7173_GPIO_PDSW BIT(14) #define AD7173_GPIO_OP_EN2_3 BIT(13) +#define AD4111_GPIO_GP_OW_EN BIT(12) #define AD7173_GPIO_MUX_IO BIT(12) #define AD7173_GPIO_SYNC_EN BIT(11) #define AD7173_GPIO_ERR_EN BIT(10) @@ -146,8 +150,19 @@ (pin2) < st->info->num_voltage_in && \ (pin2) >= st->info->num_voltage_in_div) -#define AD7173_FILTER_ODR0_MASK GENMASK(5, 0) +#define AD7173_FILTER_SINC3_MAP BIT(15) +#define AD7173_FILTER_SINC3_MAP_DIV GENMASK(14, 0) +#define AD7173_FILTER_ENHFILTEN BIT(11) +#define AD7173_FILTER_ENHFILT_MASK GENMASK(10, 8) +#define AD7173_FILTER_ORDER BIT(6) +#define AD7173_FILTER_ODR_MASK GENMASK(5, 0) #define AD7173_MAX_CONFIGS 8 +#define AD4111_OW_DET_THRSH_MV 300 + +#define AD7173_MODE_CAL_INT_ZERO 0x4 /* Internal Zero-Scale Calibration */ +#define AD7173_MODE_CAL_INT_FULL 0x5 /* Internal Full-Scale Calibration */ +#define AD7173_MODE_CAL_SYS_ZERO 0x6 /* System Zero-Scale Calibration */ +#define AD7173_MODE_CAL_SYS_FULL 0x7 /* System Full-Scale Calibration */ struct ad7173_device_info { const unsigned int *sinc5_data_rates; @@ -165,35 +180,58 @@ struct ad7173_device_info { unsigned int clock; unsigned int id; char *name; + const struct ad_sigma_delta_info *sd_info; bool has_current_inputs; bool has_vincom_input; bool has_temp; /* ((AVDD1 − AVSS)/5) */ bool has_pow_supply_monitoring; + bool data_reg_only_16bit; bool has_input_buf; bool has_int_ref; bool has_ref2; + bool has_internal_fs_calibration; + bool has_openwire_det; bool higher_gpio_bits; u8 num_gpios; }; +enum ad7173_filter_type { + AD7173_FILTER_SINC3, + AD7173_FILTER_SINC5_SINC1, + AD7173_FILTER_SINC5_SINC1_PF1, + AD7173_FILTER_SINC5_SINC1_PF2, + AD7173_FILTER_SINC5_SINC1_PF3, + AD7173_FILTER_SINC5_SINC1_PF4, +}; + struct ad7173_channel_config { + /* Openwire detection threshold */ + unsigned int openwire_thrsh_raw; + int openwire_comp_chan; u8 cfg_slot; bool live; - /* Following fields are used to compare equality. */ + /* + * Following fields are used to compare equality. If you + * make adaptations in it, you most likely also have to adapt + * ad7173_is_setup_equal(), too. + */ struct_group(config_props, bool bipolar; bool input_buf; - u8 odr; + u16 sinc3_odr_div; + u8 sinc5_odr_index; u8 ref_sel; + enum ad7173_filter_type filter_type; ); }; struct ad7173_channel { - unsigned int chan_reg; unsigned int ain; struct ad7173_channel_config cfg; + u8 syscalib_mode; + bool openwire_det_en; }; struct ad7173_state { @@ -207,12 +245,9 @@ struct ad7173_state { struct ida cfg_slots_status; unsigned long long config_usage_counter; unsigned long long *config_cnts; - struct clk *ext_clk; struct clk_hw int_clk_hw; -#if IS_ENABLED(CONFIG_GPIOLIB) struct regmap *reg_gpiocon_regmap; struct gpio_regmap *gpio_regmap; -#endif }; static unsigned int ad4115_sinc5_data_rates[] = { @@ -248,6 +283,24 @@ static const unsigned int ad7175_sinc5_data_rates[] = { 5000, /* 20 */ }; +/** + * ad7173_sinc3_odr_div_from_odr() - Convert ODR to divider value + * @odr_millihz: ODR (sampling_frequency) in milliHz + * Returns: Divider value for SINC3 filter to pass. + */ +static u16 ad7173_sinc3_odr_div_from_odr(u32 odr_millihz) +{ + /* + * Divider is f_MOD (1 MHz) / 32 / ODR. ODR freq is in milliHz, so + * we need to convert f_MOD to the same units. When SING_CYC=1 or + * multiple channels are enabled (currently always the case), there + * is an additional factor of 3. + */ + u32 div = DIV_ROUND_CLOSEST(MEGA * MILLI, odr_millihz * 32 * 3); + /* Avoid divide by 0 and limit to register field size. */ + return clamp(div, 1U, AD7173_FILTER_SINC3_MAP_DIV); +} + static unsigned int ad4111_current_channel_config[] = { /* Ain sel: pos neg */ 0x1E8, /* 15:IIN0+ 8:IIN0− */ @@ -256,235 +309,258 @@ static unsigned int ad4111_current_channel_config[] = { 0x18B, /* 12:IIN3+ 11:IIN3− */ }; -static const struct ad7173_device_info ad4111_device_info = { - .name = "ad4111", - .id = AD4111_ID, - .num_voltage_in_div = 8, - .num_channels = 16, - .num_configs = 8, - .num_voltage_in = 8, - .num_gpios = 2, - .higher_gpio_bits = true, - .has_temp = true, - .has_vincom_input = true, - .has_input_buf = true, - .has_current_inputs = true, - .has_int_ref = true, - .clock = 2 * HZ_PER_MHZ, - .sinc5_data_rates = ad7173_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +static const char *const ad7173_ref_sel_str[] = { + [AD7173_SETUP_REF_SEL_EXT_REF] = "vref", + [AD7173_SETUP_REF_SEL_EXT_REF2] = "vref2", + [AD7173_SETUP_REF_SEL_INT_REF] = "refout-avss", + [AD7173_SETUP_REF_SEL_AVDD1_AVSS] = "avdd", }; -static const struct ad7173_device_info ad4112_device_info = { - .name = "ad4112", - .id = AD4112_ID, - .num_voltage_in_div = 8, - .num_channels = 16, - .num_configs = 8, - .num_voltage_in = 8, - .num_gpios = 2, - .higher_gpio_bits = true, - .has_vincom_input = true, - .has_temp = true, - .has_input_buf = true, - .has_current_inputs = true, - .has_int_ref = true, - .clock = 2 * HZ_PER_MHZ, - .sinc5_data_rates = ad7173_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +static const char *const ad7173_clk_sel[] = { + "ext-clk", "xtal" }; -static const struct ad7173_device_info ad4114_device_info = { - .name = "ad4114", - .id = AD4114_ID, - .num_voltage_in_div = 16, - .num_channels = 16, - .num_configs = 8, - .num_voltage_in = 16, - .num_gpios = 4, - .has_vincom_input = true, - .has_temp = true, - .has_input_buf = true, - .has_int_ref = true, - .clock = 2 * HZ_PER_MHZ, - .sinc5_data_rates = ad7173_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +static const struct regmap_range ad7173_range_gpio[] = { + regmap_reg_range(AD7173_REG_GPIO, AD7173_REG_GPIO), }; -static const struct ad7173_device_info ad4115_device_info = { - .name = "ad4115", - .id = AD4115_ID, - .num_voltage_in_div = 16, - .num_channels = 16, - .num_configs = 8, - .num_voltage_in = 16, - .num_gpios = 4, - .has_vincom_input = true, - .has_temp = true, - .has_input_buf = true, - .has_int_ref = true, - .clock = 8 * HZ_PER_MHZ, - .sinc5_data_rates = ad4115_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad4115_sinc5_data_rates), +static const struct regmap_access_table ad7173_access_table = { + .yes_ranges = ad7173_range_gpio, + .n_yes_ranges = ARRAY_SIZE(ad7173_range_gpio), }; -static const struct ad7173_device_info ad4116_device_info = { - .name = "ad4116", - .id = AD4116_ID, - .num_voltage_in_div = 11, - .num_channels = 16, - .num_configs = 8, - .num_voltage_in = 16, - .num_gpios = 4, - .has_vincom_input = true, - .has_temp = true, - .has_input_buf = true, - .has_int_ref = true, - .clock = 4 * HZ_PER_MHZ, - .sinc5_data_rates = ad4116_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad4116_sinc5_data_rates), +static const struct regmap_config ad7173_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .rd_table = &ad7173_access_table, + .wr_table = &ad7173_access_table, + .read_flag_mask = BIT(6), }; -static const struct ad7173_device_info ad7172_2_device_info = { - .name = "ad7172-2", - .id = AD7172_2_ID, - .num_voltage_in = 5, - .num_channels = 4, - .num_configs = 4, - .num_gpios = 2, - .has_temp = true, - .has_input_buf = true, - .has_int_ref = true, - .has_pow_supply_monitoring = true, - .clock = 2 * HZ_PER_MHZ, - .sinc5_data_rates = ad7173_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +enum { + AD7173_SYSCALIB_ZERO_SCALE, + AD7173_SYSCALIB_FULL_SCALE, }; -static const struct ad7173_device_info ad7172_4_device_info = { - .name = "ad7172-4", - .id = AD7172_4_ID, - .num_voltage_in = 9, - .num_channels = 8, - .num_configs = 8, - .num_gpios = 4, - .has_input_buf = true, - .has_ref2 = true, - .has_pow_supply_monitoring = true, - .clock = 2 * HZ_PER_MHZ, - .sinc5_data_rates = ad7173_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +static const char * const ad7173_syscalib_modes[] = { + [AD7173_SYSCALIB_ZERO_SCALE] = "zero_scale", + [AD7173_SYSCALIB_FULL_SCALE] = "full_scale", }; -static const struct ad7173_device_info ad7173_8_device_info = { - .name = "ad7173-8", - .id = AD7173_ID, - .num_voltage_in = 17, - .num_channels = 16, - .num_configs = 8, - .num_gpios = 4, - .has_temp = true, - .has_input_buf = true, - .has_int_ref = true, - .has_ref2 = true, - .clock = 2 * HZ_PER_MHZ, - .sinc5_data_rates = ad7173_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), -}; +static int ad7173_set_syscalib_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct ad7173_state *st = iio_priv(indio_dev); -static const struct ad7173_device_info ad7175_2_device_info = { - .name = "ad7175-2", - .id = AD7175_2_ID, - .num_voltage_in = 5, - .num_channels = 4, - .num_configs = 4, - .num_gpios = 2, - .has_temp = true, - .has_input_buf = true, - .has_int_ref = true, - .has_pow_supply_monitoring = true, - .clock = 16 * HZ_PER_MHZ, - .sinc5_data_rates = ad7175_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), -}; + st->channels[chan->address].syscalib_mode = mode; -static const struct ad7173_device_info ad7175_8_device_info = { - .name = "ad7175-8", - .id = AD7175_8_ID, - .num_voltage_in = 17, - .num_channels = 16, - .num_configs = 8, - .num_gpios = 4, - .has_temp = true, - .has_input_buf = true, - .has_int_ref = true, - .has_ref2 = true, - .has_pow_supply_monitoring = true, - .clock = 16 * HZ_PER_MHZ, - .sinc5_data_rates = ad7175_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), + return 0; +} + +static int ad7173_get_syscalib_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7173_state *st = iio_priv(indio_dev); + + return st->channels[chan->address].syscalib_mode; +} + +static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad7173_state *st = iio_priv(indio_dev); + bool sys_calib; + int ret, mode; + + ret = kstrtobool(buf, &sys_calib); + if (ret) + return ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + mode = st->channels[chan->address].syscalib_mode; + if (sys_calib) { + if (mode == AD7173_SYSCALIB_ZERO_SCALE) + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_SYS_ZERO, + chan->address); + else + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_SYS_FULL, + chan->address); + } + + iio_device_release_direct(indio_dev); + + return ret ? : len; +} + +static const struct iio_enum ad7173_syscalib_mode_enum = { + .items = ad7173_syscalib_modes, + .num_items = ARRAY_SIZE(ad7173_syscalib_modes), + .set = ad7173_set_syscalib_mode, + .get = ad7173_get_syscalib_mode }; -static const struct ad7173_device_info ad7176_2_device_info = { - .name = "ad7176-2", - .id = AD7176_ID, - .num_voltage_in = 5, - .num_channels = 4, - .num_configs = 4, - .num_gpios = 2, - .has_int_ref = true, - .clock = 16 * HZ_PER_MHZ, - .sinc5_data_rates = ad7175_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), +static const char * const ad7173_filter_types_str[] = { + [AD7173_FILTER_SINC3] = "sinc3", + [AD7173_FILTER_SINC5_SINC1] = "sinc5+sinc1", + [AD7173_FILTER_SINC5_SINC1_PF1] = "sinc5+sinc1+pf1", + [AD7173_FILTER_SINC5_SINC1_PF2] = "sinc5+sinc1+pf2", + [AD7173_FILTER_SINC5_SINC1_PF3] = "sinc5+sinc1+pf3", + [AD7173_FILTER_SINC5_SINC1_PF4] = "sinc5+sinc1+pf4", }; -static const struct ad7173_device_info ad7177_2_device_info = { - .name = "ad7177-2", - .id = AD7177_ID, - .num_voltage_in = 5, - .num_channels = 4, - .num_configs = 4, - .num_gpios = 2, - .has_temp = true, - .has_input_buf = true, - .has_int_ref = true, - .has_pow_supply_monitoring = true, - .clock = 16 * HZ_PER_MHZ, - .odr_start_value = AD7177_ODR_START_VALUE, - .sinc5_data_rates = ad7175_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), +static int ad7173_set_filter_type(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int val) +{ + struct ad7173_state *st = iio_priv(indio_dev); + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + st->channels[chan->address].cfg.filter_type = val; + st->channels[chan->address].cfg.live = false; + + iio_device_release_direct(indio_dev); + + return 0; +} + +static int ad7173_get_filter_type(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7173_state *st = iio_priv(indio_dev); + + return st->channels[chan->address].cfg.filter_type; +} + +static const struct iio_enum ad7173_filter_type_enum = { + .items = ad7173_filter_types_str, + .num_items = ARRAY_SIZE(ad7173_filter_types_str), + .set = ad7173_set_filter_type, + .get = ad7173_get_filter_type, }; -static const char *const ad7173_ref_sel_str[] = { - [AD7173_SETUP_REF_SEL_EXT_REF] = "vref", - [AD7173_SETUP_REF_SEL_EXT_REF2] = "vref2", - [AD7173_SETUP_REF_SEL_INT_REF] = "refout-avss", - [AD7173_SETUP_REF_SEL_AVDD1_AVSS] = "avdd", +static const struct iio_chan_spec_ext_info ad7173_chan_spec_ext_info[] = { + { + .name = "sys_calibration", + .write = ad7173_write_syscalib, + .shared = IIO_SEPARATE, + }, + IIO_ENUM("sys_calibration_mode", IIO_SEPARATE, + &ad7173_syscalib_mode_enum), + IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE, + &ad7173_syscalib_mode_enum), + IIO_ENUM("filter_type", IIO_SEPARATE, &ad7173_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE, + &ad7173_filter_type_enum), + { } }; -static const char *const ad7173_clk_sel[] = { - "ext-clk", "xtal" +static const struct iio_chan_spec_ext_info ad7173_temp_chan_spec_ext_info[] = { + IIO_ENUM("filter_type", IIO_SEPARATE, &ad7173_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE, + &ad7173_filter_type_enum), + { } }; -#if IS_ENABLED(CONFIG_GPIOLIB) +static int ad7173_calibrate_all(struct ad7173_state *st, struct iio_dev *indio_dev) +{ + int ret; + int i; -static const struct regmap_range ad7173_range_gpio[] = { - regmap_reg_range(AD7173_REG_GPIO, AD7173_REG_GPIO), -}; + for (i = 0; i < st->num_channels; i++) { + if (indio_dev->channels[i].type != IIO_VOLTAGE) + continue; -static const struct regmap_access_table ad7173_access_table = { - .yes_ranges = ad7173_range_gpio, - .n_yes_ranges = ARRAY_SIZE(ad7173_range_gpio), -}; + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_ZERO, i); + if (ret < 0) + return ret; -static const struct regmap_config ad7173_regmap_config = { - .reg_bits = 8, - .val_bits = 16, - .rd_table = &ad7173_access_table, - .wr_table = &ad7173_access_table, - .read_flag_mask = BIT(6), + if (st->info->has_internal_fs_calibration) { + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_FULL, i); + if (ret < 0) + return ret; + } + } + + return 0; +} + +/* + * Associative array of channel pairs for open wire detection + * The array is indexed by ain and gives the associated channel pair + * to perform the open wire detection with + * the channel pair [0] is for non differential and pair [1] + * is for differential inputs + */ +static int openwire_ain_to_channel_pair[][2][2] = { +/* AIN Single Differential */ + [0] = { { 0, 15 }, { 1, 2 } }, + [1] = { { 1, 2 }, { 2, 1 } }, + [2] = { { 3, 4 }, { 5, 6 } }, + [3] = { { 5, 6 }, { 6, 5 } }, + [4] = { { 7, 8 }, { 9, 10 } }, + [5] = { { 9, 10 }, { 10, 9 } }, + [6] = { { 11, 12 }, { 13, 14 } }, + [7] = { { 13, 14 }, { 14, 13 } }, }; +/* + * Openwire detection on ad4111 works by running the same input measurement + * on two different channels and compare if the difference between the two + * measurements exceeds a certain value (typical 300mV) + */ +static int ad4111_openwire_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7173_state *st = iio_priv(indio_dev); + struct ad7173_channel *adchan = &st->channels[chan->address]; + struct ad7173_channel_config *cfg = &adchan->cfg; + int ret, val1, val2; + + ret = regmap_set_bits(st->reg_gpiocon_regmap, AD7173_REG_GPIO, + AD4111_GPIO_GP_OW_EN); + if (ret) + return ret; + + adchan->cfg.openwire_comp_chan = + openwire_ain_to_channel_pair[chan->channel][chan->differential][0]; + + ret = ad_sigma_delta_single_conversion(indio_dev, chan, &val1); + if (ret < 0) { + dev_err(&indio_dev->dev, + "Error running ad_sigma_delta single conversion: %d", ret); + goto out; + } + + adchan->cfg.openwire_comp_chan = + openwire_ain_to_channel_pair[chan->channel][chan->differential][1]; + + ret = ad_sigma_delta_single_conversion(indio_dev, chan, &val2); + if (ret < 0) { + dev_err(&indio_dev->dev, + "Error running ad_sigma_delta single conversion: %d", ret); + goto out; + } + + if (abs(val1 - val2) > cfg->openwire_thrsh_raw) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, chan->address, + IIO_EV_TYPE_FAULT, IIO_EV_DIR_FAULT_OPENWIRE), + iio_get_time_ns(indio_dev)); + +out: + adchan->cfg.openwire_comp_chan = -1; + regmap_clear_bits(st->reg_gpiocon_regmap, AD7173_REG_GPIO, + AD4111_GPIO_GP_OW_EN); + return ret; +} + static int ad7173_mask_xlate(struct gpio_regmap *gpio, unsigned int base, unsigned int offset, unsigned int *reg, unsigned int *mask) @@ -547,12 +623,6 @@ static int ad7173_gpio_init(struct ad7173_state *st) return 0; } -#else -static int ad7173_gpio_init(struct ad7173_state *st) -{ - return 0; -} -#endif /* CONFIG_GPIOLIB */ static struct ad7173_state *ad_sigma_delta_to_ad7173(struct ad_sigma_delta *sd) { @@ -577,19 +647,51 @@ static void ad7173_reset_usage_cnts(struct ad7173_state *st) st->config_usage_counter = 0; } +/** + * ad7173_is_setup_equal - Compare two channel setups + * @cfg1: First channel configuration + * @cfg2: Second channel configuration + * + * Compares all configuration options that affect the registers connected to + * SETUP_SEL, namely CONFIGx, FILTERx, GAINx and OFFSETx. + * + * Returns: true if the setups are identical, false otherwise + */ +static bool ad7173_is_setup_equal(const struct ad7173_channel_config *cfg1, + const struct ad7173_channel_config *cfg2) +{ + /* + * This is just to make sure that the comparison is adapted after + * struct ad7173_channel_config was changed. + */ + static_assert(sizeof_field(struct ad7173_channel_config, config_props) == + sizeof(struct { + bool bipolar; + bool input_buf; + u16 sinc3_odr_div; + u8 sinc5_odr_index; + u8 ref_sel; + enum ad7173_filter_type filter_type; + })); + + return cfg1->bipolar == cfg2->bipolar && + cfg1->input_buf == cfg2->input_buf && + cfg1->sinc3_odr_div == cfg2->sinc3_odr_div && + cfg1->sinc5_odr_index == cfg2->sinc5_odr_index && + cfg1->ref_sel == cfg2->ref_sel && + cfg1->filter_type == cfg2->filter_type; +} + static struct ad7173_channel_config * ad7173_find_live_config(struct ad7173_state *st, struct ad7173_channel_config *cfg) { struct ad7173_channel_config *cfg_aux; - ptrdiff_t cmp_size; int i; - cmp_size = sizeof_field(struct ad7173_channel_config, config_props); for (i = 0; i < st->num_channels; i++) { cfg_aux = &st->channels[i].cfg; - if (cfg_aux->live && - !memcmp(&cfg->config_props, &cfg_aux->config_props, cmp_size)) + if (cfg_aux->live && ad7173_is_setup_equal(cfg, cfg_aux)) return cfg_aux; } return NULL; @@ -618,6 +720,7 @@ static int ad7173_load_config(struct ad7173_state *st, { unsigned int config; int free_cfg_slot, ret; + u8 post_filter_enable, post_filter_select; free_cfg_slot = ida_alloc_range(&st->cfg_slots_status, 0, st->info->num_configs - 1, GFP_KERNEL); @@ -637,8 +740,49 @@ static int ad7173_load_config(struct ad7173_state *st, if (ret) return ret; + /* + * When SINC3_MAP flag is enabled, the rest of the register has a + * different meaning. We are using this option to allow the most + * possible sampling frequencies with SINC3 filter. + */ + if (cfg->filter_type == AD7173_FILTER_SINC3) + return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2, + FIELD_PREP(AD7173_FILTER_SINC3_MAP, 1) | + FIELD_PREP(AD7173_FILTER_SINC3_MAP_DIV, + cfg->sinc3_odr_div)); + + switch (cfg->filter_type) { + case AD7173_FILTER_SINC5_SINC1_PF1: + post_filter_enable = 1; + post_filter_select = 2; + break; + case AD7173_FILTER_SINC5_SINC1_PF2: + post_filter_enable = 1; + post_filter_select = 3; + break; + case AD7173_FILTER_SINC5_SINC1_PF3: + post_filter_enable = 1; + post_filter_select = 5; + break; + case AD7173_FILTER_SINC5_SINC1_PF4: + post_filter_enable = 1; + post_filter_select = 6; + break; + default: + post_filter_enable = 0; + post_filter_select = 0; + break; + } + return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2, - AD7173_FILTER_ODR0_MASK & cfg->odr); + FIELD_PREP(AD7173_FILTER_SINC3_MAP, 0) | + FIELD_PREP(AD7173_FILTER_ENHFILT_MASK, + post_filter_enable) | + FIELD_PREP(AD7173_FILTER_ENHFILTEN, + post_filter_select) | + FIELD_PREP(AD7173_FILTER_ORDER, 0) | + FIELD_PREP(AD7173_FILTER_ODR_MASK, + cfg->sinc5_odr_index)); } static int ad7173_config_channel(struct ad7173_state *st, int addr) @@ -682,6 +826,9 @@ static int ad7173_set_channel(struct ad_sigma_delta *sd, unsigned int channel) FIELD_PREP(AD7173_CH_SETUP_SEL_MASK, st->channels[channel].cfg.cfg_slot) | st->channels[channel].ain; + if (st->channels[channel].cfg.openwire_comp_chan >= 0) + channel = st->channels[channel].cfg.openwire_comp_chan; + return ad_sd_write_reg(&st->sd, AD7173_REG_CH(channel), 2, val); } @@ -730,20 +877,299 @@ static int ad7173_disable_all(struct ad_sigma_delta *sd) static int ad7173_disable_one(struct ad_sigma_delta *sd, unsigned int chan) { + struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd); + + if (st->channels[chan].cfg.openwire_comp_chan >= 0) + chan = st->channels[chan].cfg.openwire_comp_chan; + return ad_sd_write_reg(sd, AD7173_REG_CH(chan), 2, 0); } -static struct ad_sigma_delta_info ad7173_sigma_delta_info = { +static const struct ad_sigma_delta_info ad7173_sigma_delta_info_4_slots = { .set_channel = ad7173_set_channel, .append_status = ad7173_append_status, .disable_all = ad7173_disable_all, .disable_one = ad7173_disable_one, .set_mode = ad7173_set_mode, .has_registers = true, + .has_named_irqs = true, + .supports_spi_offload = true, .addr_shift = 0, .read_mask = BIT(6), .status_ch_mask = GENMASK(3, 0), .data_reg = AD7173_REG_DATA, + .num_resetclks = 64, + .num_slots = 4, +}; + +static const struct ad_sigma_delta_info ad7173_sigma_delta_info_8_slots = { + .set_channel = ad7173_set_channel, + .append_status = ad7173_append_status, + .disable_all = ad7173_disable_all, + .disable_one = ad7173_disable_one, + .set_mode = ad7173_set_mode, + .has_registers = true, + .has_named_irqs = true, + .supports_spi_offload = true, + .addr_shift = 0, + .read_mask = BIT(6), + .status_ch_mask = GENMASK(3, 0), + .data_reg = AD7173_REG_DATA, + .num_resetclks = 64, + .num_slots = 8, +}; + +static const struct ad_sigma_delta_info ad7173_sigma_delta_info_16_slots = { + .set_channel = ad7173_set_channel, + .append_status = ad7173_append_status, + .disable_all = ad7173_disable_all, + .disable_one = ad7173_disable_one, + .set_mode = ad7173_set_mode, + .has_registers = true, + .has_named_irqs = true, + .supports_spi_offload = true, + .addr_shift = 0, + .read_mask = BIT(6), + .status_ch_mask = GENMASK(3, 0), + .data_reg = AD7173_REG_DATA, + .num_resetclks = 64, + .num_slots = 16, +}; + +static const struct ad7173_device_info ad4111_device_info = { + .name = "ad4111", + .id = AD4111_ID, + .sd_info = &ad7173_sigma_delta_info_16_slots, + .num_voltage_in_div = 8, + .num_channels = 16, + .num_configs = 8, + .num_voltage_in = 8, + .num_gpios = 2, + .higher_gpio_bits = true, + .has_temp = true, + .has_vincom_input = true, + .has_input_buf = true, + .has_current_inputs = true, + .has_int_ref = true, + .has_internal_fs_calibration = true, + .has_openwire_det = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad4112_device_info = { + .name = "ad4112", + .id = AD4112_ID, + .sd_info = &ad7173_sigma_delta_info_16_slots, + .num_voltage_in_div = 8, + .num_channels = 16, + .num_configs = 8, + .num_voltage_in = 8, + .num_gpios = 2, + .higher_gpio_bits = true, + .has_vincom_input = true, + .has_temp = true, + .has_input_buf = true, + .has_current_inputs = true, + .has_int_ref = true, + .has_internal_fs_calibration = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad4113_device_info = { + .name = "ad4113", + .id = AD4113_ID, + .sd_info = &ad7173_sigma_delta_info_16_slots, + .num_voltage_in_div = 8, + .num_channels = 16, + .num_configs = 8, + .num_voltage_in = 8, + .num_gpios = 2, + .data_reg_only_16bit = true, + .higher_gpio_bits = true, + .has_vincom_input = true, + .has_input_buf = true, + .has_int_ref = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad4114_device_info = { + .name = "ad4114", + .id = AD4114_ID, + .sd_info = &ad7173_sigma_delta_info_16_slots, + .num_voltage_in_div = 16, + .num_channels = 16, + .num_configs = 8, + .num_voltage_in = 16, + .num_gpios = 4, + .has_vincom_input = true, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_internal_fs_calibration = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad4115_device_info = { + .name = "ad4115", + .id = AD4115_ID, + .sd_info = &ad7173_sigma_delta_info_16_slots, + .num_voltage_in_div = 16, + .num_channels = 16, + .num_configs = 8, + .num_voltage_in = 16, + .num_gpios = 4, + .has_vincom_input = true, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_internal_fs_calibration = true, + .clock = 8 * HZ_PER_MHZ, + .sinc5_data_rates = ad4115_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad4115_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad4116_device_info = { + .name = "ad4116", + .id = AD4116_ID, + .sd_info = &ad7173_sigma_delta_info_16_slots, + .num_voltage_in_div = 11, + .num_channels = 16, + .num_configs = 8, + .num_voltage_in = 16, + .num_gpios = 4, + .has_vincom_input = true, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_internal_fs_calibration = true, + .clock = 4 * HZ_PER_MHZ, + .sinc5_data_rates = ad4116_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad4116_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7172_2_device_info = { + .name = "ad7172-2", + .id = AD7172_2_ID, + .sd_info = &ad7173_sigma_delta_info_4_slots, + .num_voltage_in = 5, + .num_channels = 4, + .num_configs = 4, + .num_gpios = 2, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_pow_supply_monitoring = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7172_4_device_info = { + .name = "ad7172-4", + .id = AD7172_4_ID, + .sd_info = &ad7173_sigma_delta_info_8_slots, + .num_voltage_in = 9, + .num_channels = 8, + .num_configs = 8, + .num_gpios = 4, + .has_input_buf = true, + .has_ref2 = true, + .has_pow_supply_monitoring = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7173_8_device_info = { + .name = "ad7173-8", + .id = AD7173_ID, + .sd_info = &ad7173_sigma_delta_info_16_slots, + .num_voltage_in = 17, + .num_channels = 16, + .num_configs = 8, + .num_gpios = 4, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_ref2 = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7175_2_device_info = { + .name = "ad7175-2", + .id = AD7175_2_ID, + .sd_info = &ad7173_sigma_delta_info_4_slots, + .num_voltage_in = 5, + .num_channels = 4, + .num_configs = 4, + .num_gpios = 2, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_pow_supply_monitoring = true, + .clock = 16 * HZ_PER_MHZ, + .sinc5_data_rates = ad7175_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7175_8_device_info = { + .name = "ad7175-8", + .id = AD7175_8_ID, + .sd_info = &ad7173_sigma_delta_info_16_slots, + .num_voltage_in = 17, + .num_channels = 16, + .num_configs = 8, + .num_gpios = 4, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_ref2 = true, + .has_pow_supply_monitoring = true, + .clock = 16 * HZ_PER_MHZ, + .sinc5_data_rates = ad7175_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7176_2_device_info = { + .name = "ad7176-2", + .id = AD7176_ID, + .sd_info = &ad7173_sigma_delta_info_4_slots, + .num_voltage_in = 5, + .num_channels = 4, + .num_configs = 4, + .num_gpios = 2, + .has_int_ref = true, + .clock = 16 * HZ_PER_MHZ, + .sinc5_data_rates = ad7175_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7177_2_device_info = { + .name = "ad7177-2", + .id = AD7177_ID, + .sd_info = &ad7173_sigma_delta_info_4_slots, + .num_voltage_in = 5, + .num_channels = 4, + .num_configs = 4, + .num_gpios = 2, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_pow_supply_monitoring = true, + .clock = 16 * HZ_PER_MHZ, + .odr_start_value = AD7177_ODR_START_VALUE, + .sinc5_data_rates = ad7175_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), }; static int ad7173_setup(struct iio_dev *indio_dev) @@ -781,6 +1207,10 @@ static int ad7173_setup(struct iio_dev *indio_dev) if (!st->config_cnts) return -ENOMEM; + ret = ad7173_calibrate_all(st, indio_dev); + if (ret) + return ret; + /* All channels are enabled by default after a reset */ return ad7173_disable_all(&st->sd); } @@ -833,6 +1263,12 @@ static int ad7173_read_raw(struct iio_dev *indio_dev, if (ret < 0) return ret; + if (ch->openwire_det_en) { + ret = ad4111_openwire_event(indio_dev, chan); + if (ret < 0) + return ret; + } + return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: @@ -879,7 +1315,14 @@ static int ad7173_read_raw(struct iio_dev *indio_dev, return -EINVAL; } case IIO_CHAN_INFO_SAMP_FREQ: - reg = st->channels[chan->address].cfg.odr; + if (st->channels[chan->address].cfg.filter_type == AD7173_FILTER_SINC3) { + /* Inverse operation of ad7173_sinc3_odr_div_from_odr() */ + *val = MEGA; + *val2 = 3 * 32 * st->channels[chan->address].cfg.sinc3_odr_div; + return IIO_VAL_FRACTIONAL; + } + + reg = st->channels[chan->address].cfg.sinc5_odr_index; *val = st->info->sinc5_data_rates[reg] / MILLI; *val2 = (st->info->sinc5_data_rates[reg] % MILLI) * (MICRO / MILLI); @@ -897,11 +1340,10 @@ static int ad7173_write_raw(struct iio_dev *indio_dev, struct ad7173_state *st = iio_priv(indio_dev); struct ad7173_channel_config *cfg; unsigned int freq, i; - int ret; + int ret = 0; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; switch (info) { /* @@ -918,6 +1360,10 @@ static int ad7173_write_raw(struct iio_dev *indio_dev, * * This will cause the reading of CH1 to be actually done once every * 200.16ms, an effective rate of 4.99sps. + * + * Both the sinc5 and sinc3 rates are set here so that if the filter + * type is changed, the requested rate will still be set (aside from + * rounding differences). */ case IIO_CHAN_INFO_SAMP_FREQ: freq = val * MILLI + val2 / MILLI; @@ -926,7 +1372,8 @@ static int ad7173_write_raw(struct iio_dev *indio_dev, break; cfg = &st->channels[chan->address].cfg; - cfg->odr = i; + cfg->sinc5_odr_index = i; + cfg->sinc3_odr_div = ad7173_sinc3_odr_div_from_odr(freq); cfg->live = false; break; @@ -935,7 +1382,7 @@ static int ad7173_write_raw(struct iio_dev *indio_dev, break; } - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; } @@ -943,17 +1390,88 @@ static int ad7173_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *scan_mask) { struct ad7173_state *st = iio_priv(indio_dev); - int i, ret; + u16 sinc3_count = 0; + u16 sinc3_div = 0; + int i, j, k, ret; for (i = 0; i < indio_dev->num_channels; i++) { - if (test_bit(i, scan_mask)) + const struct ad7173_channel_config *cfg = &st->channels[i].cfg; + + if (test_bit(i, scan_mask)) { + if (cfg->filter_type == AD7173_FILTER_SINC3) { + sinc3_count++; + + if (sinc3_div == 0) { + sinc3_div = cfg->sinc3_odr_div; + } else if (sinc3_div != cfg->sinc3_odr_div) { + dev_err(&st->sd.spi->dev, + "All enabled channels must have the same sampling_frequency for sinc3 filter_type\n"); + return -EINVAL; + } + } + ret = ad7173_set_channel(&st->sd, i); - else + } else { ret = ad_sd_write_reg(&st->sd, AD7173_REG_CH(i), 2, 0); + } if (ret < 0) return ret; } + if (sinc3_count && sinc3_count < bitmap_weight(scan_mask, indio_dev->num_channels)) { + dev_err(&st->sd.spi->dev, + "All enabled channels must have sinc3 filter_type\n"); + return -EINVAL; + } + + /* + * On some chips, there are more channels that setups, so if there were + * more unique setups requested than the number of available slots, + * ad7173_set_channel() will have written over some of the slots. We + * can detect this by making sure each assigned cfg_slot matches the + * requested configuration. If it doesn't, we know that the slot was + * overwritten by a different channel. + */ + for_each_set_bit(i, scan_mask, indio_dev->num_channels) { + const struct ad7173_channel_config *cfg1, *cfg2; + + cfg1 = &st->channels[i].cfg; + + for_each_set_bit(j, scan_mask, indio_dev->num_channels) { + cfg2 = &st->channels[j].cfg; + + /* + * Only compare configs that are assigned to the same + * SETUP_SEL slot and don't compare channel to itself. + */ + if (i == j || cfg1->cfg_slot != cfg2->cfg_slot) + continue; + + /* + * If we find two different configs trying to use the + * same SETUP_SEL slot, then we know that the that we + * have too many unique configurations requested for + * the available slots and at least one was overwritten. + */ + if (!ad7173_is_setup_equal(cfg1, cfg2)) { + /* + * At this point, there isn't a way to tell + * which setups are actually programmed in the + * ADC anymore, so we could read them back to + * see, but it is simpler to just turn off all + * of the live flags so that everything gets + * reprogramed on the next attempt read a sample. + */ + for (k = 0; k < st->num_channels; k++) + st->channels[k].cfg.live = false; + + dev_err(&st->sd.spi->dev, + "Too many unique channel configurations requested for scan\n"); + return -EINVAL; + } + } + } + return 0; } @@ -977,12 +1495,64 @@ static int ad7173_debug_reg_access(struct iio_dev *indio_dev, unsigned int reg, return ad_sd_write_reg(&st->sd, reg, reg_size, writeval); } +static int ad7173_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + struct ad7173_state *st = iio_priv(indio_dev); + struct ad7173_channel *adchan = &st->channels[chan->address]; + + switch (type) { + case IIO_EV_TYPE_FAULT: + adchan->openwire_det_en = state; + return 0; + default: + return -EINVAL; + } +} + +static int ad7173_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ad7173_state *st = iio_priv(indio_dev); + struct ad7173_channel *adchan = &st->channels[chan->address]; + + switch (type) { + case IIO_EV_TYPE_FAULT: + return adchan->openwire_det_en; + default: + return -EINVAL; + } +} + +static const struct iio_event_spec ad4111_events[] = { + { + .type = IIO_EV_TYPE_FAULT, + .dir = IIO_EV_DIR_FAULT_OPENWIRE, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + .mask_shared_by_all = BIT(IIO_EV_INFO_ENABLE), + }, +}; + static const struct iio_info ad7173_info = { .read_raw = &ad7173_read_raw, .write_raw = &ad7173_write_raw, .debugfs_reg_access = &ad7173_debug_reg_access, .validate_trigger = ad_sd_validate_trigger, .update_scan_mode = ad7173_update_scan_mode, + .write_event_config = ad7173_write_event_config, + .read_event_config = ad7173_read_event_config, +}; + +static const struct iio_scan_type ad4113_scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, }; static const struct iio_chan_spec ad7173_channel_template = { @@ -996,6 +1566,7 @@ static const struct iio_chan_spec ad7173_channel_template = { .storagebits = 32, .endianness = IIO_BE, }, + .ext_info = ad7173_chan_spec_ext_info, }; static const struct iio_chan_spec ad7173_temp_iio_channel_template = { @@ -1011,6 +1582,7 @@ static const struct iio_chan_spec ad7173_temp_iio_channel_template = { .storagebits = 32, .endianness = IIO_BE, }, + .ext_info = ad7173_temp_chan_spec_ext_info, }; static void ad7173_disable_regulators(void *data) @@ -1020,11 +1592,6 @@ static void ad7173_disable_regulators(void *data) regulator_bulk_disable(ARRAY_SIZE(st->regulators), st->regulators); } -static void ad7173_clk_disable_unprepare(void *clk) -{ - clk_disable_unprepare(clk); -} - static unsigned long ad7173_sel_clk(struct ad7173_state *st, unsigned int clk_sel) { @@ -1178,6 +1745,37 @@ static int ad7173_validate_reference(struct ad7173_state *st, int ref_sel) return 0; } +static int ad7173_validate_openwire_ain_inputs(struct ad7173_state *st, + bool differential, + unsigned int ain0, + unsigned int ain1) +{ + /* + * If the channel is configured as differential, + * the ad4111 requires specific ains to be used together + */ + if (differential) + return (ain0 % 2) ? (ain0 - 1) == ain1 : (ain0 + 1) == ain1; + + return ain1 == AD4111_VINCOM_INPUT; +} + +static unsigned int ad7173_calc_openwire_thrsh_raw(struct ad7173_state *st, + struct iio_chan_spec *chan, + struct ad7173_channel *chan_st_priv, + unsigned int thrsh_mv) { + unsigned int thrsh_raw; + + thrsh_raw = + BIT(chan->scan_type.realbits - !!(chan_st_priv->cfg.bipolar)) + * thrsh_mv + / ad7173_get_ref_voltage_milli(st, chan_st_priv->cfg.ref_sel); + if (chan->channel < st->info->num_voltage_in_div) + thrsh_raw /= AD4111_DIVIDER_RATIO; + + return thrsh_raw; +} + static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) { struct ad7173_channel *chans_st_arr, *chan_st_priv; @@ -1225,7 +1823,20 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) chan_st_priv->cfg.bipolar = false; chan_st_priv->cfg.input_buf = st->info->has_input_buf; chan_st_priv->cfg.ref_sel = AD7173_SETUP_REF_SEL_INT_REF; + chan_st_priv->cfg.sinc3_odr_div = ad7173_sinc3_odr_div_from_odr( + st->info->sinc5_data_rates[st->info->odr_start_value] + ); + chan_st_priv->cfg.sinc5_odr_index = st->info->odr_start_value; + chan_st_priv->cfg.filter_type = AD7173_FILTER_SINC5_SINC1; + chan_st_priv->cfg.openwire_comp_chan = -1; st->adc_mode |= AD7173_ADC_MODE_REF_EN; + if (st->info->data_reg_only_16bit) + chan_arr[chan_index].scan_type = ad4113_scan_type; + + if (ad_sigma_delta_has_spi_offload(&st->sd)) { + chan_arr[chan_index].scan_type.storagebits = 32; + chan_arr[chan_index].scan_type.endianness = IIO_CPU; + } chan_index++; } @@ -1287,9 +1898,13 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) chan->address = chan_index; chan->scan_index = chan_index; chan->channel = ain[0]; - chan_st_priv->chan_reg = chan_index; chan_st_priv->cfg.input_buf = st->info->has_input_buf; - chan_st_priv->cfg.odr = 0; + chan_st_priv->cfg.sinc3_odr_div = ad7173_sinc3_odr_div_from_odr( + st->info->sinc5_data_rates[st->info->odr_start_value] + ); + chan_st_priv->cfg.sinc5_odr_index = st->info->odr_start_value; + chan_st_priv->cfg.filter_type = AD7173_FILTER_SINC5_SINC1; + chan_st_priv->cfg.openwire_comp_chan = -1; chan_st_priv->cfg.bipolar = fwnode_property_read_bool(child, "bipolar"); if (chan_st_priv->cfg.bipolar) @@ -1304,6 +1919,23 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) chan_st_priv->cfg.input_buf = st->info->has_input_buf; chan->channel2 = ain[1]; chan_st_priv->ain = AD7173_CH_ADDRESS(ain[0], ain[1]); + if (st->info->has_openwire_det && + ad7173_validate_openwire_ain_inputs(st, chan->differential, ain[0], ain[1])) { + chan->event_spec = ad4111_events; + chan->num_event_specs = ARRAY_SIZE(ad4111_events); + chan_st_priv->cfg.openwire_thrsh_raw = + ad7173_calc_openwire_thrsh_raw(st, chan, chan_st_priv, + AD4111_OW_DET_THRSH_MV); + } + } + + if (st->info->data_reg_only_16bit) + chan_arr[chan_index].scan_type = ad4113_scan_type; + + /* Assuming SPI offload is ad411x_ad717x HDL project. */ + if (ad_sigma_delta_has_spi_offload(&st->sd)) { + chan_arr[chan_index].scan_type.storagebits = 32; + chan_arr[chan_index].scan_type.endianness = IIO_CPU; } chan_index++; @@ -1338,8 +1970,7 @@ static int ad7173_fw_parse_device_config(struct iio_dev *indio_dev) ret = devm_add_action_or_reset(dev, ad7173_disable_regulators, st); if (ret) - return dev_err_probe(dev, ret, - "Failed to add regulators disable action\n"); + return ret; ret = device_property_match_property_string(dev, "clock-names", ad7173_clk_sel, @@ -1349,30 +1980,16 @@ static int ad7173_fw_parse_device_config(struct iio_dev *indio_dev) AD7173_ADC_MODE_CLOCKSEL_INT); ad7173_register_clk_provider(indio_dev); } else { + struct clk *clk; + st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_CLOCKSEL_MASK, AD7173_ADC_MODE_CLOCKSEL_EXT + ret); - st->ext_clk = devm_clk_get(dev, ad7173_clk_sel[ret]); - if (IS_ERR(st->ext_clk)) - return dev_err_probe(dev, PTR_ERR(st->ext_clk), + clk = devm_clk_get_enabled(dev, ad7173_clk_sel[ret]); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "Failed to get external clock\n"); - - 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, ad7173_clk_disable_unprepare, - st->ext_clk); - if (ret) - return ret; } - ret = fwnode_irq_get_byname(dev_fwnode(dev), "rdy"); - if (ret < 0) - return dev_err_probe(dev, ret, "Interrupt 'rdy' is required\n"); - - ad7173_sigma_delta_info.irq_line = ret; - return ad7173_fw_parse_channel_config(indio_dev); } @@ -1402,10 +2019,11 @@ static int ad7173_probe(struct spi_device *spi) indio_dev->info = &ad7173_info; spi->mode = SPI_MODE_3; - spi_setup(spi); + ret = spi_setup(spi); + if (ret) + return ret; - ad7173_sigma_delta_info.num_slots = st->info->num_configs; - ret = ad_sd_init(&st->sd, indio_dev, spi, &ad7173_sigma_delta_info); + ret = ad_sd_init(&st->sd, indio_dev, spi, st->info->sd_info); if (ret) return ret; @@ -1425,15 +2043,13 @@ static int ad7173_probe(struct spi_device *spi) if (ret) return ret; - if (IS_ENABLED(CONFIG_GPIOLIB)) - return ad7173_gpio_init(st); - - return 0; + return ad7173_gpio_init(st); } static const struct of_device_id ad7173_of_match[] = { { .compatible = "adi,ad4111", .data = &ad4111_device_info }, { .compatible = "adi,ad4112", .data = &ad4112_device_info }, + { .compatible = "adi,ad4113", .data = &ad4113_device_info }, { .compatible = "adi,ad4114", .data = &ad4114_device_info }, { .compatible = "adi,ad4115", .data = &ad4115_device_info }, { .compatible = "adi,ad4116", .data = &ad4116_device_info }, @@ -1451,6 +2067,7 @@ MODULE_DEVICE_TABLE(of, ad7173_of_match); static const struct spi_device_id ad7173_id_table[] = { { "ad4111", (kernel_ulong_t)&ad4111_device_info }, { "ad4112", (kernel_ulong_t)&ad4112_device_info }, + { "ad4113", (kernel_ulong_t)&ad4113_device_info }, { "ad4114", (kernel_ulong_t)&ad4114_device_info }, { "ad4115", (kernel_ulong_t)&ad4115_device_info }, { "ad4116", (kernel_ulong_t)&ad4116_device_info }, @@ -1475,7 +2092,7 @@ static struct spi_driver ad7173_driver = { }; module_spi_driver(ad7173_driver); -MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA); +MODULE_IMPORT_NS("IIO_AD_SIGMA_DELTA"); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafo.de>"); MODULE_AUTHOR("Dumitru Ceclan <dumitru.ceclan@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD7173 and similar ADC driver"); |
