drivers: adc: Implement the current sources of ADS114S08

Implement support for the current sources of ADS114S08.

Signed-off-by: Benedikt Schmidt <benedikt.schmidt@embedded-solutions.at>
diff --git a/drivers/adc/Kconfig.ads114s0x b/drivers/adc/Kconfig.ads114s0x
index 39ed5c5..31f1939 100644
--- a/drivers/adc/Kconfig.ads114s0x
+++ b/drivers/adc/Kconfig.ads114s0x
@@ -8,6 +8,7 @@
 	depends on DT_HAS_TI_ADS114S08_ENABLED
 	select SPI
 	select ADC_CONFIGURABLE_INPUTS
+	select ADC_CONFIGURABLE_EXCITATION_CURRENT_SOURCE_PIN
 	help
 	  Enable the driver implementation for the ADS114S0X family
 
diff --git a/drivers/adc/adc_ads114s0x.c b/drivers/adc/adc_ads114s0x.c
index 8885c00..eeafd0d 100644
--- a/drivers/adc/adc_ads114s0x.c
+++ b/drivers/adc/adc_ads114s0x.c
@@ -381,6 +381,22 @@
 	ADS114S0X_REGISTER_PGA_DELAY_SET(target, 0b000);                                           \
 	ADS114S0X_REGISTER_PGA_PGA_EN_SET(target, 0b00);                                           \
 	ADS114S0X_REGISTER_PGA_GAIN_SET(target, 0b000)
+/*
+ * - disable PGA output rail flag
+ * - low-side power switch
+ * - IDAC off
+ */
+#define ADS114S0X_REGISTER_IDACMAG_SET_DEFAULTS(target)                                            \
+	ADS114S0X_REGISTER_IDACMAG_FL_RAIL_EN_SET(target, 0b0);                                    \
+	ADS114S0X_REGISTER_IDACMAG_PSW_SET(target, 0b0);                                           \
+	ADS114S0X_REGISTER_IDACMAG_IMAG_SET(target, 0b0000)
+/*
+ * - disconnect IDAC1
+ * - disconnect IDAC2
+ */
+#define ADS114S0X_REGISTER_IDACMUX_SET_DEFAULTS(target)                                            \
+	ADS114S0X_REGISTER_IDACMUX_I1MUX_SET(target, 0b1111);                                      \
+	ADS114S0X_REGISTER_IDACMUX_I2MUX_SET(target, 0b1111)
 
 struct ads114s0x_config {
 	struct spi_dt_spec bus;
@@ -390,6 +406,7 @@
 	const struct gpio_dt_spec gpio_reset;
 	const struct gpio_dt_spec gpio_data_ready;
 	const struct gpio_dt_spec gpio_start_sync;
+	int idac_current;
 };
 
 struct ads114s0x_data {
@@ -570,14 +587,20 @@
 	uint8_t reference_control = 0;
 	uint8_t data_rate = 0;
 	uint8_t gain = 0;
+	uint8_t idac_magnitude = 0;
+	uint8_t idac_mux = 0;
+	uint8_t pin_selections[4];
+	size_t pin_selections_size;
 	int result;
-	enum ads114s0x_register register_addresses[4];
+	enum ads114s0x_register register_addresses[6];
 	uint8_t values[ARRAY_SIZE(register_addresses)];
 
 	ADS114S0X_REGISTER_INPMUX_SET_DEFAULTS(gain);
 	ADS114S0X_REGISTER_REF_SET_DEFAULTS(reference_control);
 	ADS114S0X_REGISTER_DATARATE_SET_DEFAULTS(data_rate);
 	ADS114S0X_REGISTER_PGA_SET_DEFAULTS(gain);
+	ADS114S0X_REGISTER_IDACMAG_SET_DEFAULTS(idac_magnitude);
+	ADS114S0X_REGISTER_IDACMUX_SET_DEFAULTS(idac_mux);
 
 	if (channel_cfg->channel_id != 0) {
 		LOG_ERR("only one channel is supported");
@@ -634,6 +657,8 @@
 
 		ADS114S0X_REGISTER_INPMUX_MUXP_SET(input_mux, channel_cfg->input_positive);
 		ADS114S0X_REGISTER_INPMUX_MUXN_SET(input_mux, channel_cfg->input_negative);
+		pin_selections[0] = channel_cfg->input_positive;
+		pin_selections[1] = channel_cfg->input_negative;
 	} else {
 		if (channel_cfg->input_positive >= ADS114S0X_INPUT_SELECTION_AINCOM) {
 			LOG_ERR("channel input %i is invalid", channel_cfg->input_positive);
@@ -642,6 +667,8 @@
 
 		ADS114S0X_REGISTER_INPMUX_MUXP_SET(input_mux, channel_cfg->input_positive);
 		ADS114S0X_REGISTER_INPMUX_MUXN_SET(input_mux, ADS114S0X_INPUT_SELECTION_AINCOM);
+		pin_selections[0] = channel_cfg->input_positive;
+		pin_selections[1] = ADS114S0X_INPUT_SELECTION_AINCOM;
 	}
 
 	switch (channel_cfg->gain) {
@@ -680,16 +707,95 @@
 		ADS114S0X_REGISTER_PGA_PGA_EN_SET(gain, 0b01);
 	}
 
+	switch (config->idac_current) {
+	case 0:
+		ADS114S0X_REGISTER_IDACMAG_IMAG_SET(idac_magnitude, 0b0000);
+		break;
+	case 10:
+		ADS114S0X_REGISTER_IDACMAG_IMAG_SET(idac_magnitude, 0b0001);
+		break;
+	case 50:
+		ADS114S0X_REGISTER_IDACMAG_IMAG_SET(idac_magnitude, 0b0010);
+		break;
+	case 100:
+		ADS114S0X_REGISTER_IDACMAG_IMAG_SET(idac_magnitude, 0b0011);
+		break;
+	case 250:
+		ADS114S0X_REGISTER_IDACMAG_IMAG_SET(idac_magnitude, 0b0100);
+		break;
+	case 500:
+		ADS114S0X_REGISTER_IDACMAG_IMAG_SET(idac_magnitude, 0b0101);
+		break;
+	case 750:
+		ADS114S0X_REGISTER_IDACMAG_IMAG_SET(idac_magnitude, 0b0110);
+		break;
+	case 1000:
+		ADS114S0X_REGISTER_IDACMAG_IMAG_SET(idac_magnitude, 0b0111);
+		break;
+	case 1500:
+		ADS114S0X_REGISTER_IDACMAG_IMAG_SET(idac_magnitude, 0b1000);
+		break;
+	case 2000:
+		ADS114S0X_REGISTER_IDACMAG_IMAG_SET(idac_magnitude, 0b1001);
+		break;
+	default:
+		LOG_ERR("IDAC magnitude %i not supported", config->idac_current);
+		return -EINVAL;
+	}
+
+	if (channel_cfg->current_source_pin_set) {
+		if (channel_cfg->current_source_pin[0] > 0b1111) {
+			LOG_ERR("invalid selection %i for I1MUX",
+				channel_cfg->current_source_pin[0]);
+			return -EINVAL;
+		}
+
+		if (channel_cfg->current_source_pin[1] > 0b1111) {
+			LOG_ERR("invalid selection %i for I2MUX",
+				channel_cfg->current_source_pin[1]);
+			return -EINVAL;
+		}
+
+		ADS114S0X_REGISTER_IDACMUX_I1MUX_SET(idac_mux, channel_cfg->current_source_pin[0]);
+		ADS114S0X_REGISTER_IDACMUX_I2MUX_SET(idac_mux, channel_cfg->current_source_pin[1]);
+		pin_selections[2] = channel_cfg->current_source_pin[0];
+		pin_selections[3] = channel_cfg->current_source_pin[1];
+		pin_selections_size = 4;
+	} else {
+		pin_selections_size = 2;
+	}
+
+	for (size_t i = 0; i < pin_selections_size; ++i) {
+		if (pin_selections[i] > ADS114S0X_INPUT_SELECTION_AINCOM) {
+			continue;
+		}
+
+		for (size_t j = i + 1; j < pin_selections_size; ++j) {
+			if (pin_selections[j] > ADS114S0X_INPUT_SELECTION_AINCOM) {
+				continue;
+			}
+
+			if (pin_selections[i] == pin_selections[j]) {
+				LOG_ERR("pins for inputs and current sources must be different");
+				return -EINVAL;
+			}
+		}
+	}
+
 	register_addresses[0] = ADS114S0X_REGISTER_INPMUX;
 	register_addresses[1] = ADS114S0X_REGISTER_PGA;
 	register_addresses[2] = ADS114S0X_REGISTER_DATARATE;
 	register_addresses[3] = ADS114S0X_REGISTER_REF;
-	BUILD_ASSERT(ARRAY_SIZE(register_addresses) == 4);
+	register_addresses[4] = ADS114S0X_REGISTER_IDACMAG;
+	register_addresses[5] = ADS114S0X_REGISTER_IDACMUX;
+	BUILD_ASSERT(ARRAY_SIZE(register_addresses) == 6);
 	values[0] = input_mux;
 	values[1] = gain;
 	values[2] = data_rate;
 	values[3] = reference_control;
-	BUILD_ASSERT(ARRAY_SIZE(values) == 4);
+	values[4] = idac_magnitude;
+	values[5] = idac_mux;
+	BUILD_ASSERT(ARRAY_SIZE(values) == 6);
 
 	result = ads114s0x_write_multiple_registers(&config->bus, register_addresses, values,
 						    ARRAY_SIZE(values));
@@ -1328,6 +1434,7 @@
 		.gpio_reset = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {0}),                       \
 		.gpio_data_ready = GPIO_DT_SPEC_INST_GET(n, drdy_gpios),                           \
 		.gpio_start_sync = GPIO_DT_SPEC_INST_GET_OR(n, start_sync_gpios, {0}),             \
+		.idac_current = DT_INST_PROP(n,	idac_current),                                     \
 	};                                                                                         \
 	static struct ads114s0x_data data_##n;                                                     \
 	DEVICE_DT_INST_DEFINE(n, ads114s0x_init, NULL, &data_##n, &config_##n, POST_KERNEL,        \