[PATCH 6/6] iio: adc: ad7173: Add support for AD411x devices

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 | 224 ++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 210 insertions(+), 14 deletions(-)

diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c
index 9526585e6929..ac32bd7dbd1e 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
  *
@@ -72,6 +73,11 @@
 #define AD7175_2_ID			0x0cd0
 #define AD7172_4_ID			0x2050
 #define AD7173_ID			0x30d0
+#define AD4111_ID			0x30d0
+#define AD4112_ID			0x30d0
+#define AD4114_ID			0x30d0
+#define AD4116_ID			0x34d0
+#define AD4115_ID			0x38d0
 #define AD7175_8_ID			0x3cd0
 #define AD7177_ID			0x4fd0
 #define AD7173_ID_MASK			GENMASK(15, 4)
@@ -120,11 +126,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,
@@ -134,16 +149,26 @@ 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,
+};
+
 struct ad7173_device_info {
 	const unsigned int *sinc5_data_rates;
 	unsigned int num_sinc5_data_rates;
 	unsigned int odr_start_value;
+	unsigned int num_inputs_with_divider;
 	unsigned int num_channels;
 	unsigned int num_configs;
 	unsigned int num_inputs;
 	unsigned int clock;
 	unsigned int id;
 	char *name;
+	bool has_current_inputs;
+	bool has_vcom;
 	bool has_temp;
 	bool has_input_buf;
 	bool has_int_ref;
@@ -189,6 +214,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 */
@@ -204,7 +247,91 @@ 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] = {
+		.id = AD4111_ID,
+		.num_inputs_with_divider = 8,
+		.num_channels = 16,
+		.num_configs = 8,
+		.num_inputs = 8,
+		.num_gpios = 2,
+		.has_temp = true,
+		.has_vcom = 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),
+	},
+	[ID_AD4112] = {
+		.id = AD4112_ID,
+		.num_inputs_with_divider = 8,
+		.num_channels = 16,
+		.num_configs = 8,
+		.num_inputs = 8,
+		.num_gpios = 2,
+		.has_vcom = 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),
+	},
+	[ID_AD4114] = {
+		.id = AD4114_ID,
+		.num_inputs_with_divider = 16,
+		.num_channels = 16,
+		.num_configs = 8,
+		.num_inputs = 16,
+		.num_gpios = 4,
+		.has_vcom = 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),
+	},
+	[ID_AD4115] = {
+		.id = AD4115_ID,
+		.num_inputs_with_divider = 16,
+		.num_channels = 16,
+		.num_configs = 8,
+		.num_inputs = 16,
+		.num_gpios = 4,
+		.has_vcom = 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),
+	},
+	[ID_AD4116] = {
+		.id = AD4116_ID,
+		.num_inputs_with_divider = 11,
+		.num_channels = 16,
+		.num_configs = 8,
+		.num_inputs = 16,
+		.num_gpios = 4,
+		.has_vcom = 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),
+	},
 	[ID_AD7172_2] = {
 		.name = "ad7172-2",
 		.id = AD7172_2_ID,
@@ -670,18 +797,34 @@ 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 {
+			break;
+		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_inputs_with_divider)
+				*val *= AD4111_DIVIDER_RATIO;
+			break;
+		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);
+			break;
+		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;
@@ -690,8 +833,13 @@ static int ad7173_read_raw(struct iio_dev *indio_dev,
 						       AD7173_VOLTAGE_INT_REF_uV *
 						       MILLI);
 			*val   = -temp;
-		} else {
+			break;
+		case IIO_VOLTAGE:
+		case IIO_CURRENT:
 			*val = -BIT(chan->scan_type.realbits - 1);
+			break;
+		default:
+			return -EINVAL;
 		}
 		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_SAMP_FREQ:
@@ -909,6 +1057,24 @@ 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])
+{
+	struct device *dev = &st->sd.spi->dev;
+
+	if (!st->info->has_current_inputs)
+		return dev_err_probe(dev, -EINVAL,
+			"Reg values equal to or higher than %d are restricted to models with current channels.\n",
+			AD4111_CURRENT_CHAN_CUTOFF);
+
+	if (ain[1] != 0 && ain[0] >= ARRAY_SIZE(ad4111_current_channel_config))
+		return dev_err_probe(dev, -EINVAL,
+			"For current channel diff-channels must be <[0-%d],0>\n",
+			ARRAY_SIZE(ad4111_current_channel_config) - 1);
+
+	return 0;
+}
+
 static int ad7173_validate_voltage_ain_inputs(struct ad7173_state *st,
 					      unsigned int ain[2])
 {
@@ -951,7 +1117,7 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
 	struct device *dev = indio_dev->dev.parent;
 	struct iio_chan_spec *chan_arr, *chan;
 	unsigned int ain[2], chan_index = 0;
-	int ref_sel, ret, num_channels;
+	int ref_sel, ret, reg, num_channels;
 
 	num_channels = device_get_child_node_count(dev);
 
@@ -1004,10 +1170,20 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
 		if (ret)
 			return ret;
 
-		ret = ad7173_validate_voltage_ain_inputs(st, ain);
+		ret = fwnode_property_read_u32(child, "reg", &reg);
 		if (ret)
 			return ret;
 
+		if (reg >= AD4111_CURRENT_CHAN_CUTOFF) {
+			ret = ad4111_validate_current_ain(st, ain);
+			if (ret)
+				return ret;
+		} else {
+			ret = ad7173_validate_voltage_ain_inputs(st, ain);
+			if (ret)
+				return ret;
+		}
+
 		ret = fwnode_property_match_property_string(child,
 							    "adi,reference-select",
 							    ad7173_ref_sel_str,
@@ -1028,15 +1204,22 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
 		*chan = ad7173_channel_template;
 		chan->address = chan_index;
 		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]);
+		if (reg >= AD4111_CURRENT_CHAN_CUTOFF) {
+			chan->type = IIO_CURRENT;
+			chan->channel = ain[0];
+			chan_st_priv->ain = ad4111_current_channel_config[ain[0]];
+		} else {
+			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->cfg.input_buf = st->info->has_input_buf;
+		}
+
 		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);
@@ -1167,6 +1350,14 @@ static int ad7173_probe(struct spi_device *spi)
 }
 
 static const struct of_device_id ad7173_of_match[] = {
+	{ .compatible = "ad4111",
+	  .data = &ad7173_device_info[ID_AD4111]},
+	{ .compatible = "ad4112",
+	  .data = &ad7173_device_info[ID_AD4112]},
+	{ .compatible = "ad4114",
+	  .data = &ad7173_device_info[ID_AD4114]},
+	{ .compatible = "ad4115",
+	  .data = &ad7173_device_info[ID_AD4115]},
 	{ .compatible = "adi,ad7172-2",
 	  .data = &ad7173_device_info[ID_AD7172_2]},
 	{ .compatible = "adi,ad7172-4",
@@ -1186,6 +1377,11 @@ static const struct of_device_id ad7173_of_match[] = {
 MODULE_DEVICE_TABLE(of, ad7173_of_match);
 
 static const struct spi_device_id ad7173_id_table[] = {
+	{ "ad4111", (kernel_ulong_t)&ad7173_device_info[ID_AD4111]},
+	{ "ad4112", (kernel_ulong_t)&ad7173_device_info[ID_AD4112]},
+	{ "ad4114", (kernel_ulong_t)&ad7173_device_info[ID_AD4114]},
+	{ "ad4115", (kernel_ulong_t)&ad7173_device_info[ID_AD4115]},
+	{ "ad4116", (kernel_ulong_t)&ad7173_device_info[ID_AD4116]},
 	{ "ad7172-2", (kernel_ulong_t)&ad7173_device_info[ID_AD7172_2]},
 	{ "ad7172-4", (kernel_ulong_t)&ad7173_device_info[ID_AD7172_4]},
 	{ "ad7173-8", (kernel_ulong_t)&ad7173_device_info[ID_AD7173_8]},
@@ -1210,5 +1406,5 @@ module_spi_driver(ad7173_driver);
 MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA);
 MODULE_AUTHOR("Lars-Peter Clausen <lars@xxxxxxxxx>");
 MODULE_AUTHOR("Dumitru Ceclan <dumitru.ceclan@xxxxxxxxxx>");
-MODULE_DESCRIPTION("Analog Devices AD7172/AD7173/AD7175/AD7176 ADC driver");
+MODULE_DESCRIPTION("Analog Devices AD717x and AD411x ADC driver");
 MODULE_LICENSE("GPL");

-- 
2.43.0






[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux