[PATCH v3 4/5] iio: adc: ad7380: use spi_optimize_message()

From: David Lechner
Date: Thu May 30 2024 - 11:15:41 EST


This changes the AD7380 to use spi_optimize_message() to optimize
buffered reads.

This changes both direct reads and buffered reads to use the same
spi_message. This has some (welcome) side effects. The first is that
in buffered reads, the timestamp will now correspond to the same sample
rather than the previous sample. The second is that direct reads now
use the same SPI bus speed as buffered reads.

This reduces CPU usage of the IRQ thread from around 25% to around 20%
when sampling at 10 kHz on a ZedBoard.

Signed-off-by: David Lechner <dlechner@xxxxxxxxxxxx>
---
v3 changes:
* New patch for this series
* A similar patch was sent at [1].

[1]: https://lore.kernel.org/linux-iio/20240219-mainline-spi-precook-message-v2-5-4a762c6701b9@xxxxxxxxxxxx/
---
drivers/iio/adc/ad7380.c | 70 ++++++++++++++++++++++++++----------------------
1 file changed, 38 insertions(+), 32 deletions(-)

diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c
index 6b0b1b0be363..006496807de9 100644
--- a/drivers/iio/adc/ad7380.c
+++ b/drivers/iio/adc/ad7380.c
@@ -232,6 +232,9 @@ struct ad7380_state {
struct regmap *regmap;
unsigned int vref_mv;
unsigned int vcm_mv[MAX_NUM_CHANNELS];
+ /* xfers, message an buffer for reading sample data */
+ struct spi_transfer xfer[2];
+ struct spi_message msg;
/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
@@ -331,15 +334,9 @@ static irqreturn_t ad7380_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct ad7380_state *st = iio_priv(indio_dev);
- struct spi_transfer xfer = {
- .bits_per_word = st->chip_info->channels[0].scan_type.realbits,
- .len = (st->chip_info->num_channels - 1) *
- BITS_TO_BYTES(st->chip_info->channels->scan_type.storagebits),
- .rx_buf = st->scan_data.raw,
- };
int ret;

- ret = spi_sync_transfer(st->spi, &xfer, 1);
+ ret = spi_sync(st->spi, &st->msg);
if (ret)
goto out;

@@ -355,33 +352,9 @@ static irqreturn_t ad7380_trigger_handler(int irq, void *p)
static int ad7380_read_direct(struct ad7380_state *st,
struct iio_chan_spec const *chan, int *val)
{
- struct spi_transfer xfers[] = {
- /* toggle CS (no data xfer) to trigger a conversion */
- {
- .speed_hz = AD7380_REG_WR_SPEED_HZ,
- .bits_per_word = chan->scan_type.realbits,
- .delay = {
- .value = T_CONVERT_NS,
- .unit = SPI_DELAY_UNIT_NSECS,
- },
- .cs_change = 1,
- .cs_change_delay = {
- .value = st->chip_info->timing_specs->t_csh_ns,
- .unit = SPI_DELAY_UNIT_NSECS,
- },
- },
- /* then read all channels */
- {
- .speed_hz = AD7380_REG_WR_SPEED_HZ,
- .bits_per_word = chan->scan_type.realbits,
- .rx_buf = st->scan_data.raw,
- .len = (st->chip_info->num_channels - 1) *
- ((chan->scan_type.storagebits > 16) ? 4 : 2),
- },
- };
int ret;

- ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
+ ret = spi_sync(st->spi, &st->msg);
if (ret < 0)
return ret;

@@ -464,6 +437,11 @@ static void ad7380_regulator_disable(void *p)
regulator_disable(p);
}

+static void ad7380_unoptimize_msg(void *msg)
+{
+ spi_unoptimize_message(msg);
+}
+
static int ad7380_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
@@ -552,6 +530,34 @@ static int ad7380_probe(struct spi_device *spi)
return dev_err_probe(&spi->dev, PTR_ERR(st->regmap),
"failed to allocate register map\n");

+ /*
+ * Setting up a low latency read for getting sample data. Used for both
+ * direct read an triggered buffer.
+ */
+
+ /* toggle CS (no data xfer) to trigger a conversion */
+ st->xfer[0].delay.value = T_CONVERT_NS;
+ st->xfer[0].delay.unit = SPI_DELAY_UNIT_NSECS;
+ st->xfer[0].cs_change = 1;
+ st->xfer[0].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns;
+ st->xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
+
+ /* then do a second xfer to read the data */
+ st->xfer[1].bits_per_word = st->chip_info->channels[0].scan_type.realbits;
+ st->xfer[1].rx_buf = st->scan_data.raw;
+ st->xfer[1].len = BITS_TO_BYTES(st->chip_info->channels[0].scan_type.storagebits)
+ * (st->chip_info->num_channels - 1);
+
+ spi_message_init_with_transfers(&st->msg, st->xfer, ARRAY_SIZE(st->xfer));
+
+ ret = spi_optimize_message(st->spi, &st->msg);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, ad7380_unoptimize_msg, &st->msg);
+ if (ret)
+ return ret;
+
indio_dev->channels = st->chip_info->channels;
indio_dev->num_channels = st->chip_info->num_channels;
indio_dev->name = st->chip_info->name;

--
2.45.1