On Mon, 2024-05-27 at 20:02 +0300, Dumitru Ceclan via B4 Relay wrote: > From: Dumitru Ceclan <dumitru.ceclan@xxxxxxxxxx> > > Add support for AD4111/AD4112/AD4114/AD4115/AD4116. > > The AD411X family encompasses a series of low power, low noise, 24-bit, > sigma-delta analog-to-digital converters that offer a versatile range of > specifications. > > This family of ADCs integrates an analog front end suitable for processing > both fully differential and single-ended, bipolar voltage inputs > addressing a wide array of industrial and instrumentation requirements. > > - All ADCs have inputs with a precision voltage divider with a division > ratio of 10. > - AD4116 has 5 low level inputs without a voltage divider. > - AD4111 and AD4112 support current inputs (0 mA to 20 mA) using a 50ohm > shunt resistor. > > Signed-off-by: Dumitru Ceclan <dumitru.ceclan@xxxxxxxxxx> > --- > drivers/iio/adc/ad7173.c | 327 ++++++++++++++++++++++++++++++++++++++++++----- > 1 file changed, 297 insertions(+), 30 deletions(-) > > diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c > index 106a50dbabd4..328685ce25e0 100644 > --- a/drivers/iio/adc/ad7173.c > +++ b/drivers/iio/adc/ad7173.c > @@ -1,8 +1,9 @@ > // SPDX-License-Identifier: GPL-2.0+ > /* > - * AD717x family SPI ADC driver > + * AD717x and AD411x family SPI ADC driver > * > * Supported devices: > + * AD4111/AD4112/AD4114/AD4115/AD4116 > * AD7172-2/AD7172-4/AD7173-8/AD7175-2 > * AD7175-8/AD7176-2/AD7177-2 > * > @@ -75,7 +76,9 @@ > #define AD7176_ID 0x0c90 > #define AD7175_2_ID 0x0cd0 > #define AD7172_4_ID 0x2050 > -#define AD7173_ID 0x30d0 > +#define AD7173_AD4111_AD4112_AD4114_ID 0x30d0 I would definitely rename this :). Would even prefer to have separate defines all defined by AD7173_ID. > +#define AD4116_ID 0x34d0 > +#define AD4115_ID 0x38d0 > #define AD7175_8_ID 0x3cd0 > #define AD7177_ID 0x4fd0 > #define AD7173_ID_MASK GENMASK(15, 4) > @@ -106,6 +109,7 @@ > > #define AD7173_GPO12_DATA(x) BIT((x) + 0) > #define AD7173_GPO23_DATA(x) BIT((x) + 4) > +#define AD4111_GPO01_DATA(x) BIT((x) + 6) > #define AD7173_GPO_DATA(x) ((x) < 2 ? AD7173_GPO12_DATA(x) : > AD7173_GPO23_DATA(x)) > > #define AD7173_INTERFACE_DATA_STAT BIT(6) > @@ -124,11 +128,20 @@ > #define AD7173_VOLTAGE_INT_REF_uV 2500000 > #define AD7173_TEMP_SENSIIVITY_uV_per_C 477 > #define AD7177_ODR_START_VALUE 0x07 > +#define AD4111_SHUNT_RESISTOR_OHM 50 > +#define AD4111_DIVIDER_RATIO 10 > +#define AD411X_VCOM_INPUT 0X10 > +#define AD4111_CURRENT_CHAN_CUTOFF 16 > > #define AD7173_FILTER_ODR0_MASK GENMASK(5, 0) > #define AD7173_MAX_CONFIGS 8 > > enum ad7173_ids { > + ID_AD4111, > + ID_AD4112, > + ID_AD4114, > + ID_AD4115, > + ID_AD4116, > ID_AD7172_2, > ID_AD7172_4, > ID_AD7173_8, > @@ -138,22 +151,43 @@ enum ad7173_ids { > ID_AD7177_2, > }; > > +enum ad4111_current_channels { > + AD4111_CURRENT_IN0P_IN0N, > + AD4111_CURRENT_IN1P_IN1N, > + AD4111_CURRENT_IN2P_IN2N, > + AD4111_CURRENT_IN3P_IN3N, > +}; > + > +enum ad7173_channel_types { > + AD7173_CHAN_SINGLE_ENDED, > + AD7173_CHAN_DIFFERENTIAL, > +}; > + > struct ad7173_device_info { > const unsigned int *sinc5_data_rates; > unsigned int num_sinc5_data_rates; > unsigned int odr_start_value; > + /* > + * AD4116 has both inputs with a volage divider and without. s/volage/voltage > + * These inputs cannot be mixed in the channel configuration. > + * Does not include the VCOM input. > + */ > + unsigned int num_voltage_inputs_with_divider; nit: maybe num_voltage_in_div? > unsigned int num_channels; > unsigned int num_configs; > - unsigned int num_inputs; > + unsigned int num_voltage_inputs; nit: maybe num_voltage_in? > unsigned int clock; > unsigned int id; > char *name; > + bool has_current_inputs; > + bool has_vcom_input; > bool has_temp; > /* ((AVDD1 − AVSS)/5) */ > bool has_common_input; > bool has_input_buf; > bool has_int_ref; > bool has_ref2; > + bool higher_gpio_bits; > u8 num_gpios; > }; > > @@ -195,6 +229,24 @@ struct ad7173_state { > #endif > }; > > +static unsigned int ad4115_sinc5_data_rates[] = { > + 24845000, 24845000, 20725000, 20725000, /* 0-3 */ > + 15564000, 13841000, 10390000, 10390000, /* 4-7 */ > + 4994000, 2499000, 1000000, 500000, /* 8-11 */ > + 395500, 200000, 100000, 59890, /* 12-15 */ > + 49920, 20000, 16660, 10000, /* 16-19 */ > + 5000, 2500, 2500, /* 20-22 */ > +}; > + > +static unsigned int ad4116_sinc5_data_rates[] = { > + 12422360, 12422360, 12422360, 12422360, /* 0-3 */ > + 10362690, 10362690, 7782100, 6290530, /* 4-7 */ > + 5194800, 2496900, 1007600, 499900, /* 8-11 */ > + 390600, 200300, 100000, 59750, /* 12-15 */ > + 49840, 20000, 16650, 10000, /* 16-19 */ > + 5000, 2500, 1250, /* 20-22 */ > +}; > + > static const unsigned int ad7173_sinc5_data_rates[] = { > 6211000, 6211000, 6211000, 6211000, 6211000, 6211000, 5181000, > 4444000, /* 0-7 */ > 3115000, 2597000, 1007000, 503800, 381000, 200300, 100500, > 59520, /* 8-15 */ > @@ -210,14 +262,109 @@ static const unsigned int ad7175_sinc5_data_rates[] = { > 5000, /* 20 */ > }; > > +static unsigned int ad4111_current_channel_config[] = { > + [AD4111_CURRENT_IN0P_IN0N] = 0x1E8, > + [AD4111_CURRENT_IN1P_IN1N] = 0x1C9, > + [AD4111_CURRENT_IN2P_IN2N] = 0x1AA, > + [AD4111_CURRENT_IN3P_IN3N] = 0x18B, > +}; > + > static const struct ad7173_device_info ad7173_device_info[] = { > + [ID_AD4111] = { > + .name = "ad4111", > + .id = AD7173_AD4111_AD4112_AD4114_ID, > + .num_voltage_inputs_with_divider = 8, > + .num_channels = 16, > + .num_configs = 8, > + .num_voltage_inputs = 8, > + .num_gpios = 2, > + .higher_gpio_bits = true, > + .has_temp = true, > + .has_vcom_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), > + }, At some point it would be nice to drop the ad7173_device_info array... ... > > @@ -688,18 +858,33 @@ static int ad7173_read_raw(struct iio_dev *indio_dev, > > return IIO_VAL_INT; > case IIO_CHAN_INFO_SCALE: > - if (chan->type == IIO_TEMP) { > + > + switch (chan->type) { > + case IIO_TEMP: > temp = AD7173_VOLTAGE_INT_REF_uV * MILLI; > temp /= AD7173_TEMP_SENSIIVITY_uV_per_C; > *val = temp; > *val2 = chan->scan_type.realbits; > - } else { > + return IIO_VAL_FRACTIONAL_LOG2; > + case IIO_VOLTAGE: > *val = ad7173_get_ref_voltage_milli(st, ch->cfg.ref_sel); > *val2 = chan->scan_type.realbits - !!(ch->cfg.bipolar); > + > + if (chan->channel < st->info- > >num_voltage_inputs_with_divider) > + *val *= AD4111_DIVIDER_RATIO; > + return IIO_VAL_FRACTIONAL_LOG2; > + case IIO_CURRENT: > + *val = ad7173_get_ref_voltage_milli(st, ch->cfg.ref_sel); > + *val /= AD4111_SHUNT_RESISTOR_OHM; > + *val2 = chan->scan_type.realbits - (ch->cfg.bipolar ? 1 : > 0); Can bipolar have any other value than 0 or 1? Just subtract it directly... > + return IIO_VAL_FRACTIONAL_LOG2; > + default: > + return -EINVAL; > } > - return IIO_VAL_FRACTIONAL_LOG2; > case IIO_CHAN_INFO_OFFSET: > - if (chan->type == IIO_TEMP) { > + > + switch (chan->type) { > + case IIO_TEMP: > /* 0 Kelvin -> raw sample */ > temp = -ABSOLUTE_ZERO_MILLICELSIUS; > temp *= AD7173_TEMP_SENSIIVITY_uV_per_C; > @@ -708,10 +893,14 @@ static int ad7173_read_raw(struct iio_dev *indio_dev, > AD7173_VOLTAGE_INT_REF_uV * > MILLI); > *val = -temp; > - } else { > + return IIO_VAL_INT; > + case IIO_VOLTAGE: > + case IIO_CURRENT: > *val = -BIT(chan->scan_type.realbits - 1); > + return IIO_VAL_INT; > + default: > + return -EINVAL; > } > - return IIO_VAL_INT; > case IIO_CHAN_INFO_SAMP_FREQ: > reg = st->channels[chan->address].cfg.odr; > > @@ -919,13 +1108,34 @@ static int ad7173_register_clk_provider(struct iio_dev > *indio_dev) > &st->int_clk_hw); > } > > +static int ad4111_validate_current_ain(struct ad7173_state *st, > + unsigned int ain[2]) Hmm, pass by reference... Should also be const AFAICT. ... > > @@ -1022,12 +1248,23 @@ static int ad7173_fw_parse_channel_config(struct iio_dev > *indio_dev) > chan_st_priv = &chans_st_arr[chan_index]; > ret = fwnode_property_read_u32_array(child, "diff-channels", > ain, ARRAY_SIZE(ain)); > - if (ret) > - return ret; > + if (ret) { > + ret = fwnode_property_read_u32_array(child, "single- > channel", > + ain, 1); > + if (ret) > + return ret; > > - ret = ad7173_validate_voltage_ain_inputs(st, ain); > - if (ret) > - return ret; > + ret = ad4111_validate_current_ain(st, ain); > + if (ret) > + return ret; > + is_current_chan = true; > + ain[1] = 0; > + } else { > + ret = ad7173_validate_voltage_ain_inputs(st, ain); > + if (ret) > + return ret; > + is_current_chan = false; > + } > > ret = fwnode_property_match_property_string(child, > "adi,reference- > select", > @@ -1051,17 +1288,34 @@ static int ad7173_fw_parse_channel_config(struct iio_dev > *indio_dev) > chan->scan_index = chan_index; > chan->channel = ain[0]; > chan->channel2 = ain[1]; > - chan->differential = true; > - > - chan_st_priv->ain = AD7173_CH_ADDRESS(ain[0], ain[1]); > 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.bipolar = fwnode_property_read_bool(child, > "bipolar"); > + > if (chan_st_priv->cfg.bipolar) > chan->info_mask_separate |= BIT(IIO_CHAN_INFO_OFFSET); > > + ret = fwnode_property_match_property_string(child, > + "adi,channel-type", > + ad7173_channel_types, > + > ARRAY_SIZE(ad7173_channel_types)); > + chan->differential = (ret < 0 || ret == AD7173_CHAN_DIFFERENTIAL) > + ? 1 : 0; I don't think we should treat 'ret < 0' has a differential channel. Any reason for it? For me, it's just an invalid property value given by the user... - Nuno Sá