]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
iio: adc: ad_sigma_delta: use spi_optimize_message()
authorDavid Lechner <dlechner@baylibre.com>
Tue, 1 Jul 2025 21:37:56 +0000 (16:37 -0500)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Sun, 13 Jul 2025 14:36:25 +0000 (15:36 +0100)
Use spi_optimize_message() to improve the performance of buffered reads.

By setting up the SPI message and pre-optimizing it in the buffer
postenable callback, we can reduce overhead during each sample read.

A rough estimate shows that this reduced the CPU usage of the interrupt
handler thread from 22% to 16% using an EVAL-AD4112ARDZ board on a
DE10-Nano (measuring a single channel at the default 6.2 kHz sample
rate).

Signed-off-by: David Lechner <dlechner@baylibre.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Nuno Sá <nuno.sa@analog.com>
Link: https://patch.msgid.link/20250701-iio-adc-ad7173-add-spi-offload-support-v3-8-42abb83e3dac@baylibre.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/adc/ad_sigma_delta.c
include/linux/iio/adc/ad_sigma_delta.h

index ce549775ac3d68fd11e660b6b993ac661301756a..124c42e19f2e25723b67ea38f7d016f00ff91342 100644 (file)
@@ -466,8 +466,9 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
 {
        struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
        const struct iio_scan_type *scan_type = &indio_dev->channels[0].scan_type;
+       struct spi_transfer *xfer = sigma_delta->sample_xfer;
        unsigned int i, slot, samples_buf_size;
-       unsigned int channel;
+       unsigned int channel, scan_size;
        u8 *samples_buf;
        int ret;
 
@@ -510,6 +511,28 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
                return -ENOMEM;
 
        sigma_delta->samples_buf = samples_buf;
+       scan_size = BITS_TO_BYTES(scan_type->realbits + scan_type->shift);
+       /* For 24-bit data, there is an extra byte of padding. */
+       xfer[1].rx_buf = &sigma_delta->rx_buf[scan_size == 3 ? 1 : 0];
+       xfer[1].len = scan_size + (sigma_delta->status_appended ? 1 : 0);
+       xfer[1].cs_change = 1;
+
+       if (sigma_delta->info->has_registers) {
+               xfer[0].tx_buf = &sigma_delta->sample_addr;
+               xfer[0].len = 1;
+
+               ad_sd_set_read_reg_addr(sigma_delta,
+                                       sigma_delta->info->data_reg ?: AD_SD_REG_DATA,
+                                       &sigma_delta->sample_addr);
+               spi_message_init_with_transfers(&sigma_delta->sample_msg, xfer, 2);
+       } else {
+               spi_message_init_with_transfers(&sigma_delta->sample_msg,
+                                               &xfer[1], 1);
+       }
+
+       ret = spi_optimize_message(sigma_delta->spi, &sigma_delta->sample_msg);
+       if (ret)
+               return ret;
 
        spi_bus_lock(sigma_delta->spi->controller);
        sigma_delta->bus_locked = true;
@@ -529,6 +552,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
 
 err_unlock:
        spi_bus_unlock(sigma_delta->spi->controller);
+       spi_unoptimize_message(&sigma_delta->sample_msg);
 
        return ret;
 }
@@ -550,7 +574,10 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev)
 
        ad_sigma_delta_disable_all(sigma_delta);
        sigma_delta->bus_locked = false;
-       return spi_bus_unlock(sigma_delta->spi->controller);
+       spi_bus_unlock(sigma_delta->spi->controller);
+       spi_unoptimize_message(&sigma_delta->sample_msg);
+
+       return 0;
 }
 
 static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
@@ -560,50 +587,19 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
        const struct iio_scan_type *scan_type = &indio_dev->channels[0].scan_type;
        struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
        u8 *data = sigma_delta->rx_buf;
-       unsigned int transfer_size;
        unsigned int sample_size;
        unsigned int sample_pos;
        unsigned int status_pos;
        unsigned int reg_size;
-       unsigned int data_reg;
+       int ret;
 
        reg_size = BITS_TO_BYTES(scan_type->realbits + scan_type->shift);
+       /* For 24-bit data, there is an extra byte of padding. */
+       status_pos = reg_size + (reg_size == 3 ? 1 : 0);
 
-       if (sigma_delta->info->data_reg != 0)
-               data_reg = sigma_delta->info->data_reg;
-       else
-               data_reg = AD_SD_REG_DATA;
-
-       /* Status word will be appended to the sample during transfer */
-       if (sigma_delta->status_appended)
-               transfer_size = reg_size + 1;
-       else
-               transfer_size = reg_size;
-
-       switch (reg_size) {
-       case 4:
-       case 2:
-       case 1:
-               status_pos = reg_size;
-               ad_sd_read_reg_raw(sigma_delta, data_reg, transfer_size, &data[0]);
-               break;
-       case 3:
-               /*
-                * Data array after transfer will look like (if status is appended):
-                * data[] = { [0][sample][sample][sample][status] }
-                * Keeping the first byte 0 shifts the status position by 1 byte to the right.
-                */
-               status_pos = reg_size + 1;
-
-               /* We store 24 bit samples in a 32 bit word. Keep the upper
-                * byte set to zero. */
-               ad_sd_read_reg_raw(sigma_delta, data_reg, transfer_size, &data[1]);
-               break;
-
-       default:
-               dev_err_ratelimited(&indio_dev->dev, "Unsupported reg_size: %u\n", reg_size);
+       ret = spi_sync_locked(sigma_delta->spi, &sigma_delta->sample_msg);
+       if (ret)
                goto irq_handled;
-       }
 
        /*
         * For devices sampling only one channel at
index 5056677c9941afadc2383febbcafeb02e23a4f44..2037bb68b44115681ff48f66b580b63f50c2ea9e 100644 (file)
@@ -105,6 +105,8 @@ struct ad_sigma_delta {
        bool                    status_appended;
        /* map slots to channels in order to know what to expect from devices */
        unsigned int            *slots;
+       struct spi_message      sample_msg;
+       struct spi_transfer     sample_xfer[2];
        u8                      *samples_buf;
 
        /*
@@ -116,6 +118,7 @@ struct ad_sigma_delta {
         */
        u8                              tx_buf[4] __aligned(IIO_DMA_MINALIGN);
        u8                              rx_buf[16] __aligned(8);
+       u8                              sample_addr;
 };
 
 static inline int ad_sigma_delta_set_channel(struct ad_sigma_delta *sd,