[PATCH v5 9/9] iio: adc: ad7380: add support for multiple SPI lanes

David Lechner
2026-01-12T17:46:42Z

Add support for multiple SPI lanes to increase throughput. The AD7380
family of ADCs have multiple SDO lines on the chip that can be used to
read each channel on a separate SPI lane. If wired up to a SPI
controller that supports it, the driver will now take advantage of this
feature. This allows reaching the maximum sample rate advertised in the
datasheet when combined with SPI offloading.

Reviewed-by: Nuno Sá <nuno.sa@analog.com>
Reviewed-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
Signed-off-by: David Lechner <dlechner@baylibre.com>
---

v5 changes:
* Include the number of SDO lines in the error message.

v4 changes:
* Update for core SPI API changes.

v3 changes:
* Renamed "buses" to "lanes" to reflect devicetree property name change.

v2 changes:
* Move st->seq_xfer[3].multi_lane_mode = SPI_MULTI_BUS_MODE_STRIPE;
to probe().
---
drivers/iio/adc/ad7380.c | 51 ++++++++++++++++++++++++++++++++++++------------
1 file changed, 38 insertions(+), 13 deletions(-)

diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c
index bfd908deefc0..ca411371816f 100644
--- a/drivers/iio/adc/ad7380.c
+++ b/drivers/iio/adc/ad7380.c
@@ -77,8 +77,7 @@
#define AD7380_CONFIG1_REFSEL BIT(1)
#define AD7380_CONFIG1_PMODE BIT(0)

-#define AD7380_CONFIG2_SDO2 GENMASK(9, 8)
-#define AD7380_CONFIG2_SDO BIT(8)
+#define AD7380_CONFIG2_SDO GENMASK(9, 8)
#define AD7380_CONFIG2_RESET GENMASK(7, 0)

#define AD7380_CONFIG2_RESET_SOFT 0x3C
@@ -92,11 +91,6 @@
#define T_CONVERT_X_NS 500 /* xth conversion start time (oversampling) */
#define T_POWERUP_US 5000 /* Power up */

-/*
- * AD738x support several SDO lines to increase throughput, but driver currently
- * supports only 1 SDO line (standard SPI transaction)
- */
-#define AD7380_NUM_SDO_LINES 1
#define AD7380_DEFAULT_GAIN_MILLI 1000

/*
@@ -888,6 +882,8 @@ struct ad7380_state {
bool resolution_boost_enabled;
unsigned int ch;
bool seq;
+ /* How many SDO lines are wired up. */
+ u8 num_sdo_lines;
unsigned int vref_mv;
unsigned int vcm_mv[MAX_NUM_CHANNELS];
unsigned int gain_milli[MAX_NUM_CHANNELS];
@@ -1084,7 +1080,7 @@ static int ad7380_set_ch(struct ad7380_state *st, unsigned int ch)
if (oversampling_ratio > 1)
xfer.delay.value = T_CONVERT_0_NS +
T_CONVERT_X_NS * (oversampling_ratio - 1) *
- st->chip_info->num_simult_channels / AD7380_NUM_SDO_LINES;
+ st->chip_info->num_simult_channels / st->num_sdo_lines;

return spi_sync_transfer(st->spi, &xfer, 1);
}
@@ -1113,7 +1109,7 @@ static int ad7380_update_xfers(struct ad7380_state *st,
if (oversampling_ratio > 1)
t_convert = T_CONVERT_0_NS + T_CONVERT_X_NS *
(oversampling_ratio - 1) *
- st->chip_info->num_simult_channels / AD7380_NUM_SDO_LINES;
+ st->chip_info->num_simult_channels / st->num_sdo_lines;

if (st->seq) {
xfer[0].delay.value = xfer[1].delay.value = t_convert;
@@ -1198,6 +1194,8 @@ static int ad7380_init_offload_msg(struct ad7380_state *st,
xfer->bits_per_word = scan_type->realbits;
xfer->offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
xfer->len = AD7380_SPI_BYTES(scan_type) * st->chip_info->num_simult_channels;
+ if (st->num_sdo_lines > 1)
+ xfer->multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE;

spi_message_init_with_transfers(&st->offload_msg, xfer, 1);
st->offload_msg.offload = st->offload;
@@ -1793,6 +1791,7 @@ static const struct iio_info ad7380_info = {

static int ad7380_init(struct ad7380_state *st, bool external_ref_en)
{
+ u32 sdo;
int ret;

/* perform hard reset */
@@ -1815,11 +1814,24 @@ static int ad7380_init(struct ad7380_state *st, bool external_ref_en)
st->ch = 0;
st->seq = false;

- /* SPI 1-wire mode */
+ /* SDO field has an irregular mapping. */
+ switch (st->num_sdo_lines) {
+ case 1:
+ sdo = 1;
+ break;
+ case 2:
+ sdo = 0;
+ break;
+ case 4:
+ sdo = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
return regmap_update_bits(st->regmap, AD7380_REG_ADDR_CONFIG2,
AD7380_CONFIG2_SDO,
- FIELD_PREP(AD7380_CONFIG2_SDO,
- AD7380_NUM_SDO_LINES));
+ FIELD_PREP(AD7380_CONFIG2_SDO, sdo));
}

static int ad7380_probe_spi_offload(struct iio_dev *indio_dev,
@@ -1842,7 +1854,7 @@ static int ad7380_probe_spi_offload(struct iio_dev *indio_dev,
"failed to get offload trigger\n");

sample_rate = st->chip_info->max_conversion_rate_hz *
- AD7380_NUM_SDO_LINES / st->chip_info->num_simult_channels;
+ st->num_sdo_lines / st->chip_info->num_simult_channels;

st->sample_freq_range[0] = 1; /* min */
st->sample_freq_range[1] = 1; /* step */
@@ -1887,6 +1899,13 @@ static int ad7380_probe(struct spi_device *spi)
if (!st->chip_info)
return dev_err_probe(dev, -EINVAL, "missing match data\n");

+ st->num_sdo_lines = spi->num_rx_lanes;
+
+ if (st->num_sdo_lines < 1 || st->num_sdo_lines > st->chip_info->num_simult_channels)
+ return dev_err_probe(dev, -EINVAL,
+ "invalid number of SDO lines (%d)\n",
+ st->num_sdo_lines);
+
ret = devm_regulator_bulk_get_enable(dev, st->chip_info->num_supplies,
st->chip_info->supplies);

@@ -2010,6 +2029,8 @@ static int ad7380_probe(struct spi_device *spi)
st->normal_xfer[0].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns;
st->normal_xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
st->normal_xfer[1].rx_buf = st->scan_data;
+ if (st->num_sdo_lines > 1)
+ st->normal_xfer[1].multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE;

spi_message_init_with_transfers(&st->normal_msg, st->normal_xfer,
ARRAY_SIZE(st->normal_xfer));
@@ -2031,6 +2052,10 @@ static int ad7380_probe(struct spi_device *spi)
st->seq_xfer[2].cs_change = 1;
st->seq_xfer[2].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns;
st->seq_xfer[2].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
+ if (st->num_sdo_lines > 1) {
+ st->seq_xfer[2].multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE;
+ st->seq_xfer[3].multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE;
+ }

spi_message_init_with_transfers(&st->seq_msg, st->seq_xfer,
ARRAY_SIZE(st->seq_xfer));

--
2.43.0