* [PATCH 0/1] ti-ads131e08: Driver optimizations
@ 2026-01-20 14:07 Viktor Karamanis
2026-01-22 21:21 ` Jonathan Cameron
0 siblings, 1 reply; 2+ messages in thread
From: Viktor Karamanis @ 2026-01-20 14:07 UTC (permalink / raw)
To: linux-iio; +Cc: jic23, linux-kernel
[-- Attachment #1.1: Type: text/plain, Size: 1056 bytes --]
This series optimizes the TI ADS131E08 ADC driver for better performance
and reliability.
Key improvements in the driver:
- Switch from RDATA polling to RDATAC continuous mode in buffer operations
for reduced SPI overhead during continuous data acquisition
- Add proper timing delays between SPI register writes to meet device
timing requirements
- Consolidate channel configuration functions to minimize unnecessary
SPI transactions
- Add status checking and proper cleanup functions
- Remove redundant code and improve overall code structure
Patch 1 contains the driver optimizations.
As a sidenote, I noticed that the driver is not present in MAINTAINERS file.
This is my first contribution to the Linux kernel,
hopefully I did everything correctly.
Please let me know if any changes are needed.
Viktor Karamanis (1):
iio: adc: ti-ads131e08: Optimize performance and fix timing
drivers/iio/adc/ti-ads131e08.c | 643 ++++++++++++++++++++-------------
1 file changed, 389 insertions(+), 254 deletions(-)
--
2.43.0
[-- Attachment #1.2: Type: text/html, Size: 5899 bytes --]
[-- Attachment #2: 0001-iio-adc-ti-ads131e08-Optimize-performance-and-fix-ti.patch --]
[-- Type: application/octet-stream, Size: 33281 bytes --]
From 37bf00dc880e4ccae8578f6824d462d193cc6778 Mon Sep 17 00:00:00 2001
From: Viktor Karamanis <viktor.karamanis@outlook.com>
Date: Tue, 20 Jan 2026 14:46:28 +0200
Subject: [PATCH 1/1] iio: adc: ti-ads131e08: Optimize performance and fix
timing
Optimize the TI ADS131E08 ADC driver for better performance and
reliability:
1. Switch from RDATA polling to RDATAC continuous mode in buffer
trigger operations. This reduces SPI overhead during continuous
data acquisition by eliminating the need to send RDATA commands
for each sample.
2. Add proper timing delays between SPI register writes. The ADS131E08
requires specific delays after certain commands and register writes,
which were not consistently implemented.
3. Consolidate channel configuration functions. Previously, multiple
functions modified channel registers separately, causing unnecessary
SPI transactions. Now a single function handles all channel
configuration changes.
4. Add ads131e08_check_status() function to check device status bits
for fault conditions, improving error detection.
5. Add ads131e08_stop_read_data_continuous() function for proper
cleanup when stopping continuous mode.
6. Replace ads131e08_trigger_ops with ads131e08_buffer_preenable()
and ads131e08_buffer_postdisable() for better integration
with the IIO buffer framework.
7. Minor code cleanup, formatting fixes, and lint improvements.
The drives has been tested on a RPI 3b+ with 25Mhz spi speed
and sample rates up to 16kSPS without issues.
At sample rates above 16kSPS occasional CRC errors were observed,
which might be related to hardware limitations.
Signed-off-by: Viktor Karamanis <viktor.karamanis@outlook.com>
---
drivers/iio/adc/ti-ads131e08.c | 643 ++++++++++++++++++++-------------
1 file changed, 389 insertions(+), 254 deletions(-)
diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c
index 085f0d6fb39e..5124622cc586 100644
--- a/drivers/iio/adc/ti-ads131e08.c
+++ b/drivers/iio/adc/ti-ads131e08.c
@@ -5,6 +5,9 @@
* Copyright (c) 2020 AVL DiTEST GmbH
* Tomislav Denis <tomislav.denis@avl.com>
*
+ * Copyright (c) 2026 Vrump Industrial IOT Solutions P.C.
+ * Viktor Karamanis <viktor.karamanis@outlook.com>
+ *
* Datasheet: https://www.ti.com/lit/ds/symlink/ads131e08.pdf
*/
@@ -26,50 +29,52 @@
#include <linux/unaligned.h>
/* Commands */
-#define ADS131E08_CMD_RESET 0x06
-#define ADS131E08_CMD_START 0x08
-#define ADS131E08_CMD_STOP 0x0A
-#define ADS131E08_CMD_OFFSETCAL 0x1A
-#define ADS131E08_CMD_SDATAC 0x11
-#define ADS131E08_CMD_RDATA 0x12
-#define ADS131E08_CMD_RREG(r) (BIT(5) | (r & GENMASK(4, 0)))
-#define ADS131E08_CMD_WREG(r) (BIT(6) | (r & GENMASK(4, 0)))
+#define ADS131E08_CMD_RESET 0x06
+#define ADS131E08_CMD_START 0x08
+#define ADS131E08_CMD_STOP 0x0A
+#define ADS131E08_CMD_OFFSETCAL 0x1A
+#define ADS131E08_CMD_RDATAC 0x10
+#define ADS131E08_CMD_SDATAC 0x11
+#define ADS131E08_CMD_RDATA 0x12
+#define ADS131E08_CMD_RREG(r) (BIT(5) | (r & GENMASK(4, 0)))
+#define ADS131E08_CMD_WREG(r) (BIT(6) | (r & GENMASK(4, 0)))
/* Registers */
-#define ADS131E08_ADR_CFG1R 0x01
-#define ADS131E08_ADR_CFG3R 0x03
-#define ADS131E08_ADR_CH0R 0x05
+#define ADS131E08_ADR_CFG1R 0x01
+#define ADS131E08_ADR_CFG3R 0x03
+#define ADS131E08_ADR_CH0R 0x05
/* Configuration register 1 */
-#define ADS131E08_CFG1R_DR_MASK GENMASK(2, 0)
+#define ADS131E08_CFG1R_DR_MASK GENMASK(2, 0)
/* Configuration register 3 */
-#define ADS131E08_CFG3R_PDB_REFBUF_MASK BIT(7)
-#define ADS131E08_CFG3R_VREF_4V_MASK BIT(5)
+#define ADS131E08_CFG3R_PDB_REFBUF_MASK BIT(7)
+#define ADS131E08_CFG3R_VREF_4V_MASK BIT(5)
/* Channel settings register */
-#define ADS131E08_CHR_GAIN_MASK GENMASK(6, 4)
-#define ADS131E08_CHR_MUX_MASK GENMASK(2, 0)
-#define ADS131E08_CHR_PWD_MASK BIT(7)
+#define ADS131E08_CHR_GAIN_MASK GENMASK(6, 4)
+#define ADS131E08_CHR_MUX_MASK GENMASK(2, 0)
+#define ADS131E08_CHR_PWD_MASK BIT(7)
/* ADC misc */
-#define ADS131E08_DEFAULT_DATA_RATE 1
-#define ADS131E08_DEFAULT_PGA_GAIN 1
-#define ADS131E08_DEFAULT_MUX 0
+#define ADS131E08_DEFAULT_DATA_RATE 1
+#define ADS131E08_DEFAULT_PGA_GAIN 1
+#define ADS131E08_DEFAULT_MUX 0
-#define ADS131E08_VREF_2V4_mV 2400
-#define ADS131E08_VREF_4V_mV 4000
+#define ADS131E08_VREF_2V4_mV 2400
+#define ADS131E08_VREF_4V_mV 4000
-#define ADS131E08_WAIT_RESET_CYCLES 18
-#define ADS131E08_WAIT_SDECODE_CYCLES 4
-#define ADS131E08_WAIT_OFFSETCAL_MS 153
-#define ADS131E08_MAX_SETTLING_TIME_MS 6
+#define ADS131E08_WAIT_RESET_CYCLES 20
+#define ADS131E08_WAIT_SDECODE_CYCLES 6
+#define ADS131E08_WAIT_OFFSETCAL_MS 200
+#define ADS131E08_MAX_SETTLING_TIME_MS 6
-#define ADS131E08_NUM_STATUS_BYTES 3
-#define ADS131E08_NUM_DATA_BYTES_MAX 24
-#define ADS131E08_NUM_DATA_BYTES(dr) (((dr) >= 32) ? 2 : 3)
-#define ADS131E08_NUM_DATA_BITS(dr) (ADS131E08_NUM_DATA_BYTES(dr) * 8)
-#define ADS131E08_NUM_STORAGE_BYTES 4
+#define ADS131E08_NUM_OF_CHANNELS_MAX 8
+#define ADS131E08_NUM_STATUS_BYTES 3
+#define ADS131E08_NUM_DATA_BYTES_MAX 24
+#define ADS131E08_NUM_DATA_BYTES(dr) (((dr) >= 32) ? 2 : 3)
+#define ADS131E08_NUM_DATA_BITS(dr) (ADS131E08_NUM_DATA_BYTES(dr) * 8)
+#define ADS131E08_NUM_STORAGE_BYTES 4
enum ads131e08_ids {
ads131e04,
@@ -92,26 +97,25 @@ struct ads131e08_state {
struct spi_device *spi;
struct iio_trigger *trig;
struct clk *adc_clk;
- struct regulator *vref_reg;
- struct ads131e08_channel_config *channel_config;
- unsigned int data_rate;
- unsigned int vref_mv;
+ struct completion completion;
unsigned int sdecode_delay_us;
unsigned int reset_delay_us;
- unsigned int readback_len;
- struct completion completion;
- struct {
- u8 data[ADS131E08_NUM_DATA_BYTES_MAX];
- aligned_s64 ts;
- } tmp_buf;
-
- u8 tx_buf[3] __aligned(IIO_DMA_MINALIGN);
+ struct regulator *vref_reg;
+ unsigned int vref_mv;
+ struct ads131e08_channel_config *channel_config;
+ u8 data_rate;
+ u8 readback_len;
+ bool rdatac_enabled;
+ struct spi_transfer xfer;
+ struct spi_message msg;
/*
* Add extra one padding byte to be able to access the last channel
* value using u32 pointer
*/
- u8 rx_buf[ADS131E08_NUM_STATUS_BYTES +
- ADS131E08_NUM_DATA_BYTES_MAX + 1];
+ u8 rx_buf[ADS131E08_NUM_STATUS_BYTES + ADS131E08_NUM_DATA_BYTES_MAX +
+ 1] __aligned(IIO_DMA_MINALIGN);
+ u8 data[ADS131E08_NUM_OF_CHANNELS_MAX *
+ ADS131E08_NUM_STORAGE_BYTES] __aligned(IIO_DMA_MINALIGN);
};
static const struct ads131e08_info ads131e08_info_tbl[] = {
@@ -130,125 +134,205 @@ static const struct ads131e08_info ads131e08_info_tbl[] = {
};
struct ads131e08_data_rate_desc {
- unsigned int rate; /* data rate in kSPS */
- u8 reg; /* reg value */
+ unsigned int rate; /* data rate in kSPS */
+ u8 reg; /* reg value */
};
static const struct ads131e08_data_rate_desc ads131e08_data_rate_tbl[] = {
- { .rate = 64, .reg = 0x00 },
- { .rate = 32, .reg = 0x01 },
- { .rate = 16, .reg = 0x02 },
- { .rate = 8, .reg = 0x03 },
- { .rate = 4, .reg = 0x04 },
- { .rate = 2, .reg = 0x05 },
- { .rate = 1, .reg = 0x06 },
+ { .rate = 64, .reg = 0x00 }, { .rate = 32, .reg = 0x01 },
+ { .rate = 16, .reg = 0x02 }, { .rate = 8, .reg = 0x03 },
+ { .rate = 4, .reg = 0x04 }, { .rate = 2, .reg = 0x05 },
+ { .rate = 1, .reg = 0x06 },
};
struct ads131e08_pga_gain_desc {
- unsigned int gain; /* PGA gain value */
- u8 reg; /* field value */
+ unsigned int gain; /* PGA gain value */
+ u8 reg; /* field value */
};
static const struct ads131e08_pga_gain_desc ads131e08_pga_gain_tbl[] = {
- { .gain = 1, .reg = 0x01 },
- { .gain = 2, .reg = 0x02 },
- { .gain = 4, .reg = 0x04 },
- { .gain = 8, .reg = 0x05 },
- { .gain = 12, .reg = 0x06 },
+ { .gain = 1, .reg = 0x01 }, { .gain = 2, .reg = 0x02 },
+ { .gain = 4, .reg = 0x04 }, { .gain = 8, .reg = 0x05 },
+ { .gain = 12, .reg = 0x06 },
};
static const u8 ads131e08_valid_channel_mux_values[] = { 0, 1, 3, 4 };
-static int ads131e08_exec_cmd(struct ads131e08_state *st, u8 cmd)
+static int ads131e08_exec_cmd(struct ads131e08_state *st, u8 cmd,
+ unsigned long delay_us)
{
int ret;
+ u8 tx = cmd;
- ret = spi_write_then_read(st->spi, &cmd, 1, NULL, 0);
- if (ret)
- dev_err(&st->spi->dev, "Exec cmd(%02x) failed\n", cmd);
+ struct spi_transfer transfer = {
+ .tx_buf = &tx,
+ .len = 1,
+ .cs_change = 0,
+ .delay = { .value = delay_us, .unit = SPI_DELAY_UNIT_USECS }
+ };
- return ret;
+ ret = spi_sync_transfer(st->spi, &transfer, 1);
+ if (ret) {
+ dev_err(&st->spi->dev, "Exec cmd 0x%02x failed: %d\n", cmd,
+ ret);
+ return ret;
+ }
+
+ dev_info(&st->spi->dev, "Exec cmd 0x%02x\n", cmd);
+ return 0;
}
-static int ads131e08_read_reg(struct ads131e08_state *st, u8 reg)
+static int ads131e08_read_reg(struct ads131e08_state *st, u8 reg, u8 *val)
{
int ret;
+ u8 cmd0 = ADS131E08_CMD_RREG(reg);
+ u8 cmd1 = 0x00;
+ u8 rx;
+
struct spi_transfer transfer[] = {
- {
- .tx_buf = &st->tx_buf,
- .len = 2,
- .delay = {
- .value = st->sdecode_delay_us,
- .unit = SPI_DELAY_UNIT_USECS,
- },
- }, {
- .rx_buf = &st->rx_buf,
- .len = 1,
- },
+ { .tx_buf = &cmd0,
+ .len = 1,
+ .cs_change = 0,
+ .delay = { .value = st->sdecode_delay_us,
+ .unit = SPI_DELAY_UNIT_USECS } },
+ { .tx_buf = &cmd1,
+ .len = 1,
+ .cs_change = 0,
+ .delay = { .value = st->sdecode_delay_us,
+ .unit = SPI_DELAY_UNIT_USECS } },
+ { .rx_buf = &rx, .len = 1, .cs_change = 0 }
};
- st->tx_buf[0] = ADS131E08_CMD_RREG(reg);
- st->tx_buf[1] = 0;
-
- ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer));
+ ret = spi_sync_transfer(st->spi, transfer, 3);
if (ret) {
- dev_err(&st->spi->dev, "Read register failed\n");
+ dev_err(&st->spi->dev, "Read reg 0x%02x failed: %d\n", reg,
+ ret);
return ret;
}
- return st->rx_buf[0];
+ *val = rx;
+
+ return 0;
}
static int ads131e08_write_reg(struct ads131e08_state *st, u8 reg, u8 value)
{
int ret;
+ u8 cmd0 = ADS131E08_CMD_WREG(reg);
+ u8 cmd1 = 0x00;
+ u8 cmd2 = value;
+
struct spi_transfer transfer[] = {
- {
- .tx_buf = &st->tx_buf,
- .len = 3,
- .delay = {
- .value = st->sdecode_delay_us,
- .unit = SPI_DELAY_UNIT_USECS,
- },
- }
+ { .tx_buf = &cmd0,
+ .len = 1,
+ .cs_change = 0,
+ .delay = { .value = st->sdecode_delay_us,
+ .unit = SPI_DELAY_UNIT_USECS } },
+ { .tx_buf = &cmd1,
+ .len = 1,
+ .cs_change = 0,
+ .delay = { .value = st->sdecode_delay_us,
+ .unit = SPI_DELAY_UNIT_USECS } },
+ { .tx_buf = &cmd2,
+ .len = 1,
+ .cs_change = 0,
+ .delay = { .value = st->sdecode_delay_us,
+ .unit = SPI_DELAY_UNIT_USECS } }
};
- st->tx_buf[0] = ADS131E08_CMD_WREG(reg);
- st->tx_buf[1] = 0;
- st->tx_buf[2] = value;
+ ret = spi_sync_transfer(st->spi, transfer, 3);
+ if (ret) {
+ dev_err(&st->spi->dev, "Write reg 0x%02x failed: %d\n", reg,
+ ret);
+ return ret;
+ }
- ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer));
- if (ret)
- dev_err(&st->spi->dev, "Write register failed\n");
+ dev_info(&st->spi->dev, "Written to 0x%02x: value=%02x\n", cmd0, cmd2);
- return ret;
+ return 0;
}
-static int ads131e08_read_data(struct ads131e08_state *st, int rx_len)
+static int ads131e08_read_data(struct ads131e08_state *st)
{
int ret;
+
+ u8 tx = ADS131E08_CMD_RDATA;
+
struct spi_transfer transfer[] = {
- {
- .tx_buf = &st->tx_buf,
- .len = 1,
- }, {
- .rx_buf = &st->rx_buf,
- .len = rx_len,
- },
+ { .tx_buf = &tx, .len = 1, .cs_change = 0 },
+ { .rx_buf = st->rx_buf, .len = st->readback_len, .cs_change = 0 }
};
- st->tx_buf[0] = ADS131E08_CMD_RDATA;
-
- ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer));
+ ret = spi_sync_transfer(st->spi, transfer, 2);
if (ret)
dev_err(&st->spi->dev, "Read data failed\n");
return ret;
}
+static int ads131e08_stop_read_data_continuous(struct ads131e08_state *st)
+{
+ int ret;
+ u8 nop = 0x00;
+
+ ret = ads131e08_exec_cmd(st, ADS131E08_CMD_SDATAC,
+ st->sdecode_delay_us);
+ if (ret)
+ return ret;
+
+ ret = spi_write(st->spi, &nop, 1);
+ if (ret)
+ return ret;
+
+ ret = spi_read(st->spi, st->rx_buf, st->readback_len);
+ if (ret)
+ return ret;
+
+ ret = spi_write(st->spi, &nop, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ads131e08_check_status(struct ads131e08_state *st)
+{
+ u8 *buf = st->rx_buf;
+ int i;
+ int ret = 0;
+
+ u32 status = ((u32)buf[0] << 16) | ((u32)buf[1] << 8) | ((u32)buf[2]);
+
+ /* Header check (bits 23:20) should be 0b1100 */
+ if (((status >> 20) & 0xF) != 0xC) {
+ dev_dbg_ratelimited(&st->spi->dev,
+ "Status word header invalid: 0x%06x\n",
+ status);
+ ret = -EIO;
+ }
+
+ u8 p_fault = (status >> 12) & 0xFF;
+ u8 n_fault = (status >> 4) & 0xFF;
+
+ for (i = 0; i < st->info->max_channels; i++) {
+ if (p_fault & BIT(i))
+ dev_dbg_ratelimited(
+ &st->spi->dev,
+ "Positive fault detected on channel %d\n", i);
+
+ if (n_fault & BIT(i))
+ dev_dbg_ratelimited(
+ &st->spi->dev,
+ "Negative fault detected on channel %d\n", i);
+ }
+
+ return ret;
+}
+
static int ads131e08_set_data_rate(struct ads131e08_state *st, int data_rate)
{
- int i, reg, ret;
+ int i, ret;
+ u8 reg;
for (i = 0; i < ARRAY_SIZE(ads131e08_data_rate_tbl); i++) {
if (ads131e08_data_rate_tbl[i].rate == data_rate)
@@ -260,28 +344,29 @@ static int ads131e08_set_data_rate(struct ads131e08_state *st, int data_rate)
return -EINVAL;
}
- reg = ads131e08_read_reg(st, ADS131E08_ADR_CFG1R);
- if (reg < 0)
- return reg;
+ ret = ads131e08_read_reg(st, ADS131E08_ADR_CFG1R, ®);
+ if (ret)
+ return ret;
reg &= ~ADS131E08_CFG1R_DR_MASK;
reg |= FIELD_PREP(ADS131E08_CFG1R_DR_MASK,
- ads131e08_data_rate_tbl[i].reg);
+ ads131e08_data_rate_tbl[i].reg);
ret = ads131e08_write_reg(st, ADS131E08_ADR_CFG1R, reg);
if (ret)
return ret;
+ /* Update state */
st->data_rate = data_rate;
st->readback_len = ADS131E08_NUM_STATUS_BYTES +
- ADS131E08_NUM_DATA_BYTES(st->data_rate) *
- st->info->max_channels;
+ ADS131E08_NUM_DATA_BYTES(st->data_rate) *
+ st->info->max_channels;
return 0;
}
static int ads131e08_pga_gain_to_field_value(struct ads131e08_state *st,
- unsigned int pga_gain)
+ unsigned int pga_gain)
{
int i;
@@ -298,27 +383,8 @@ static int ads131e08_pga_gain_to_field_value(struct ads131e08_state *st,
return ads131e08_pga_gain_tbl[i].reg;
}
-static int ads131e08_set_pga_gain(struct ads131e08_state *st,
- unsigned int channel, unsigned int pga_gain)
-{
- int field_value, reg;
-
- field_value = ads131e08_pga_gain_to_field_value(st, pga_gain);
- if (field_value < 0)
- return field_value;
-
- reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel);
- if (reg < 0)
- return reg;
-
- reg &= ~ADS131E08_CHR_GAIN_MASK;
- reg |= FIELD_PREP(ADS131E08_CHR_GAIN_MASK, field_value);
-
- return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg);
-}
-
static int ads131e08_validate_channel_mux(struct ads131e08_state *st,
- unsigned int mux)
+ unsigned int mux)
{
int i;
@@ -335,50 +401,54 @@ static int ads131e08_validate_channel_mux(struct ads131e08_state *st,
return 0;
}
-static int ads131e08_set_channel_mux(struct ads131e08_state *st,
- unsigned int channel, unsigned int mux)
+static int ads131e08_set_channel_config(struct ads131e08_state *st,
+ unsigned int channel,
+ unsigned int pga_gain, unsigned int mux,
+ bool power_down)
{
- int reg;
-
- reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel);
- if (reg < 0)
- return reg;
+ int gain, ret;
+ u8 reg;
- reg &= ~ADS131E08_CHR_MUX_MASK;
- reg |= FIELD_PREP(ADS131E08_CHR_MUX_MASK, mux);
+ /* Convert gain to register field */
+ gain = ads131e08_pga_gain_to_field_value(st, pga_gain);
+ if (gain < 0)
+ return gain;
- return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg);
-}
+ /* Read current channel register */
+ ret = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel, ®);
+ if (ret)
+ return ret;
-static int ads131e08_power_down_channel(struct ads131e08_state *st,
- unsigned int channel, bool value)
-{
- int reg;
+ /* Update gain */
+ reg &= ~ADS131E08_CHR_GAIN_MASK;
+ reg |= FIELD_PREP(ADS131E08_CHR_GAIN_MASK, gain);
- reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel);
- if (reg < 0)
- return reg;
+ /* Update mux */
+ reg &= ~ADS131E08_CHR_MUX_MASK;
+ reg |= FIELD_PREP(ADS131E08_CHR_MUX_MASK, mux);
+ /* Update power down */
reg &= ~ADS131E08_CHR_PWD_MASK;
- reg |= FIELD_PREP(ADS131E08_CHR_PWD_MASK, value);
+ reg |= FIELD_PREP(ADS131E08_CHR_PWD_MASK, power_down);
return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg);
}
static int ads131e08_config_reference_voltage(struct ads131e08_state *st)
{
- int reg;
+ int ret;
+ u8 reg;
- reg = ads131e08_read_reg(st, ADS131E08_ADR_CFG3R);
- if (reg < 0)
- return reg;
+ ret = ads131e08_read_reg(st, ADS131E08_ADR_CFG3R, ®);
+ if (ret)
+ return ret;
reg &= ~ADS131E08_CFG3R_PDB_REFBUF_MASK;
if (!st->vref_reg) {
reg |= FIELD_PREP(ADS131E08_CFG3R_PDB_REFBUF_MASK, 1);
reg &= ~ADS131E08_CFG3R_VREF_4V_MASK;
reg |= FIELD_PREP(ADS131E08_CFG3R_VREF_4V_MASK,
- st->vref_mv == ADS131E08_VREF_4V_mV);
+ st->vref_mv == ADS131E08_VREF_4V_mV);
}
return ads131e08_write_reg(st, ADS131E08_ADR_CFG3R, reg);
@@ -391,14 +461,16 @@ static int ads131e08_initial_config(struct iio_dev *indio_dev)
unsigned long active_channels = 0;
int ret, i;
- ret = ads131e08_exec_cmd(st, ADS131E08_CMD_RESET);
+ /* Disable read data in continuous mode (enabled by default) */
+ ret = ads131e08_stop_read_data_continuous(st);
if (ret)
return ret;
- udelay(st->reset_delay_us);
+ ret = ads131e08_exec_cmd(st, ADS131E08_CMD_RESET, st->reset_delay_us);
+ if (ret)
+ return ret;
- /* Disable read data in continuous mode (enabled by default) */
- ret = ads131e08_exec_cmd(st, ADS131E08_CMD_SDATAC);
+ ret = ads131e08_stop_read_data_continuous(st);
if (ret)
return ret;
@@ -410,14 +482,10 @@ static int ads131e08_initial_config(struct iio_dev *indio_dev)
if (ret)
return ret;
- for (i = 0; i < indio_dev->num_channels; i++) {
- ret = ads131e08_set_pga_gain(st, channel->channel,
- st->channel_config[i].pga_gain);
- if (ret)
- return ret;
-
- ret = ads131e08_set_channel_mux(st, channel->channel,
- st->channel_config[i].mux);
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ ret = ads131e08_set_channel_config(
+ st, channel->channel, st->channel_config[i].pga_gain,
+ st->channel_config[i].mux, false);
if (ret)
return ret;
@@ -427,13 +495,16 @@ static int ads131e08_initial_config(struct iio_dev *indio_dev)
/* Power down unused channels */
for_each_clear_bit(i, &active_channels, st->info->max_channels) {
- ret = ads131e08_power_down_channel(st, i, true);
+ ret = ads131e08_set_channel_config(st, i,
+ ADS131E08_DEFAULT_PGA_GAIN,
+ ADS131E08_DEFAULT_MUX, true);
if (ret)
return ret;
}
/* Request channel offset calibration */
- ret = ads131e08_exec_cmd(st, ADS131E08_CMD_OFFSETCAL);
+ ret = ads131e08_exec_cmd(st, ADS131E08_CMD_OFFSETCAL,
+ st->sdecode_delay_us);
if (ret)
return ret;
@@ -444,72 +515,79 @@ static int ads131e08_initial_config(struct iio_dev *indio_dev)
* time (e.g. first call of the ads131e08_read_direct method).
* To avoid this problem offset calibration is triggered here.
*/
- ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START);
+ ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START,
+ st->sdecode_delay_us +
+ ADS131E08_WAIT_OFFSETCAL_MS * 1000);
if (ret)
return ret;
- msleep(ADS131E08_WAIT_OFFSETCAL_MS);
-
- return ads131e08_exec_cmd(st, ADS131E08_CMD_STOP);
+ return ads131e08_exec_cmd(st, ADS131E08_CMD_STOP, st->sdecode_delay_us);
}
-static int ads131e08_pool_data(struct ads131e08_state *st)
+static int ads131e08_poll_data(struct ads131e08_state *st)
{
- unsigned long timeout;
int ret;
reinit_completion(&st->completion);
- ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START);
+ ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START, st->sdecode_delay_us);
if (ret)
return ret;
- timeout = msecs_to_jiffies(ADS131E08_MAX_SETTLING_TIME_MS);
- ret = wait_for_completion_timeout(&st->completion, timeout);
+ ret = wait_for_completion_timeout(
+ &st->completion,
+ msecs_to_jiffies(ADS131E08_MAX_SETTLING_TIME_MS));
if (!ret)
return -ETIMEDOUT;
- ret = ads131e08_read_data(st, st->readback_len);
+ ret = ads131e08_read_data(st);
+ if (ret)
+ return ret;
+
+ ret = ads131e08_check_status(st);
if (ret)
return ret;
- return ads131e08_exec_cmd(st, ADS131E08_CMD_STOP);
+ return ads131e08_exec_cmd(st, ADS131E08_CMD_STOP, st->sdecode_delay_us);
}
static int ads131e08_read_direct(struct iio_dev *indio_dev,
- struct iio_chan_spec const *channel, int *value)
+ struct iio_chan_spec const *channel,
+ int *value)
{
struct ads131e08_state *st = iio_priv(indio_dev);
u8 num_bits, *src;
int ret;
- ret = ads131e08_pool_data(st);
+ ret = ads131e08_poll_data(st);
if (ret)
return ret;
src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES +
- channel->channel * ADS131E08_NUM_DATA_BYTES(st->data_rate);
+ channel->channel * ADS131E08_NUM_DATA_BYTES(st->data_rate);
num_bits = ADS131E08_NUM_DATA_BITS(st->data_rate);
- *value = sign_extend32(get_unaligned_be32(src) >> (32 - num_bits), num_bits - 1);
+ *value = sign_extend32(get_unaligned_be32(src) >> (32 - num_bits),
+ num_bits - 1);
return 0;
}
static int ads131e08_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *channel, int *value,
- int *value2, long mask)
+ struct iio_chan_spec const *channel, int *value,
+ int *value2, long mask)
{
struct ads131e08_state *st = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
- if (!iio_device_claim_direct(indio_dev))
- return -EBUSY;
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
ret = ads131e08_read_direct(indio_dev, channel, value);
- iio_device_release_direct(indio_dev);
+ iio_device_release_direct_mode(indio_dev);
if (ret)
return ret;
@@ -542,19 +620,20 @@ static int ads131e08_read_raw(struct iio_dev *indio_dev,
}
static int ads131e08_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *channel, int value,
- int value2, long mask)
+ struct iio_chan_spec const *channel, int value,
+ int value2, long mask)
{
struct ads131e08_state *st = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
- if (!iio_device_claim_direct(indio_dev))
- return -EBUSY;
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
ret = ads131e08_set_data_rate(st, value);
- iio_device_release_direct(indio_dev);
+ iio_device_release_direct_mode(indio_dev);
return ret;
default:
@@ -565,8 +644,7 @@ static int ads131e08_write_raw(struct iio_dev *indio_dev,
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1 2 4 8 16 32 64");
static struct attribute *ads131e08_attributes[] = {
- &iio_const_attr_sampling_frequency_available.dev_attr.attr,
- NULL
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr, NULL
};
static const struct attribute_group ads131e08_attribute_group = {
@@ -574,17 +652,29 @@ static const struct attribute_group ads131e08_attribute_group = {
};
static int ads131e08_debugfs_reg_access(struct iio_dev *indio_dev,
- unsigned int reg, unsigned int writeval, unsigned int *readval)
+ unsigned int reg, unsigned int writeval,
+ unsigned int *readval)
{
struct ads131e08_state *st = iio_priv(indio_dev);
+ int ret;
- if (readval) {
- int ret = ads131e08_read_reg(st, reg);
- *readval = ret;
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
return ret;
+
+ if (readval) {
+ u8 reg_value;
+
+ ret = ads131e08_read_reg(st, reg, ®_value);
+ *readval = reg_value;
+ goto out;
}
- return ads131e08_write_reg(st, reg, writeval);
+ ret = ads131e08_write_reg(st, reg, writeval);
+
+out:
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
}
static const struct iio_info ads131e08_iio_info = {
@@ -594,18 +684,52 @@ static const struct iio_info ads131e08_iio_info = {
.debugfs_reg_access = &ads131e08_debugfs_reg_access,
};
-static int ads131e08_set_trigger_state(struct iio_trigger *trig, bool state)
+static int ads131e08_buffer_preenable(struct iio_dev *indio_dev)
{
- struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
struct ads131e08_state *st = iio_priv(indio_dev);
- u8 cmd = state ? ADS131E08_CMD_START : ADS131E08_CMD_STOP;
+ int ret;
+
+ memset(&st->xfer, 0, sizeof(st->xfer));
+ st->xfer.rx_buf = st->rx_buf;
+ st->xfer.len = st->readback_len;
+ st->xfer.cs_change = 0;
+ spi_message_init(&st->msg);
+ spi_message_add_tail(&st->xfer, &st->msg);
+
+ ret = ads131e08_exec_cmd(st, ADS131E08_CMD_RDATAC,
+ st->sdecode_delay_us);
+ if (ret)
+ return ret;
+
+ ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START, st->sdecode_delay_us);
+ if (ret)
+ return ret;
+
+ st->rdatac_enabled = true;
+ return 0;
+}
+
+static int ads131e08_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct ads131e08_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = ads131e08_stop_read_data_continuous(st);
+ if (ret)
+ return ret;
+
+ ret = ads131e08_exec_cmd(st, ADS131E08_CMD_STOP, st->sdecode_delay_us);
+ if (ret)
+ return ret;
+
+ st->rdatac_enabled = false;
- return ads131e08_exec_cmd(st, cmd);
+ return 0;
}
-static const struct iio_trigger_ops ads131e08_trigger_ops = {
- .set_trigger_state = &ads131e08_set_trigger_state,
- .validate_device = &iio_trigger_validate_own_device,
+static const struct iio_buffer_setup_ops ads131e08_buffer_ops = {
+ .preenable = ads131e08_buffer_preenable,
+ .postdisable = ads131e08_buffer_postdisable
};
static irqreturn_t ads131e08_trigger_handler(int irq, void *private)
@@ -615,8 +739,6 @@ static irqreturn_t ads131e08_trigger_handler(int irq, void *private)
struct ads131e08_state *st = iio_priv(indio_dev);
unsigned int chn, i = 0;
u8 *src, *dest;
- int ret;
-
/*
* The number of data bits per channel depends on the data rate.
* For 32 and 64 ksps data rates, number of data bits per channel
@@ -625,19 +747,22 @@ static irqreturn_t ads131e08_trigger_handler(int irq, void *private)
* 16 bits of data into the buffer.
*/
unsigned int num_bytes = ADS131E08_NUM_DATA_BYTES(st->data_rate);
- u8 tweek_offset = num_bytes == 2 ? 1 : 0;
+ bool tweek_offset = (num_bytes == 2);
- if (iio_trigger_using_own(indio_dev))
- ret = ads131e08_read_data(st, st->readback_len);
- else
- ret = ads131e08_pool_data(st);
+ if (!st->rdatac_enabled)
+ goto out;
- if (ret)
+ if (spi_sync(st->spi, &st->msg)) {
+ dev_warn(&st->spi->dev, "SPI read in thread failed\n");
+ goto out;
+ }
+
+ if (ads131e08_check_status(st))
goto out;
iio_for_each_active_channel(indio_dev, chn) {
src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES + chn * num_bytes;
- dest = st->tmp_buf.data + i * ADS131E08_NUM_STORAGE_BYTES;
+ dest = st->data + i * ADS131E08_NUM_STORAGE_BYTES;
/*
* Tweek offset is 0:
@@ -664,12 +789,11 @@ static irqreturn_t ads131e08_trigger_handler(int irq, void *private)
i++;
}
- iio_push_to_buffers_with_ts(indio_dev, &st->tmp_buf, sizeof(st->tmp_buf),
- iio_get_time_ns(indio_dev));
+ iio_push_to_buffers_with_timestamp(indio_dev, st->data,
+ iio_get_time_ns(indio_dev));
out:
iio_trigger_notify_done(indio_dev->trig);
-
return IRQ_HANDLED;
}
@@ -678,8 +802,9 @@ static irqreturn_t ads131e08_interrupt(int irq, void *private)
struct iio_dev *indio_dev = private;
struct ads131e08_state *st = iio_priv(indio_dev);
- if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev))
+ if (st->rdatac_enabled)
iio_trigger_poll(st->trig);
+
else
complete(&st->completion);
@@ -718,17 +843,18 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev)
}
if (num_channels > st->info->max_channels) {
- dev_err(&st->spi->dev, "num of channel children out of range\n");
+ dev_err(&st->spi->dev,
+ "num of channel children out of range\n");
return -EINVAL;
}
- channels = devm_kcalloc(&st->spi->dev, num_channels,
- sizeof(*channels), GFP_KERNEL);
+ channels = devm_kcalloc(&st->spi->dev, num_channels, sizeof(*channels),
+ GFP_KERNEL);
if (!channels)
return -ENOMEM;
channel_config = devm_kcalloc(&st->spi->dev, num_channels,
- sizeof(*channel_config), GFP_KERNEL);
+ sizeof(*channel_config), GFP_KERNEL);
if (!channel_config)
return -ENOMEM;
@@ -765,8 +891,9 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev)
channels[i].channel = channel;
channels[i].address = i;
channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
- BIT(IIO_CHAN_INFO_SCALE);
- channels[i].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ);
+ BIT(IIO_CHAN_INFO_SCALE);
+ channels[i].info_mask_shared_by_type =
+ BIT(IIO_CHAN_INFO_SAMP_FREQ);
channels[i].scan_index = channel;
channels[i].scan_type.sign = 's';
channels[i].scan_type.realbits = 24;
@@ -781,7 +908,6 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev)
st->channel_config = channel_config;
return 0;
-
}
static void ads131e08_regulator_disable(void *data)
@@ -815,6 +941,7 @@ static int ads131e08_probe(struct spi_device *spi)
st = iio_priv(indio_dev);
st->info = info;
st->spi = spi;
+ st->rdatac_enabled = false;
ret = ads131e08_alloc_channels(indio_dev);
if (ret)
@@ -822,15 +949,14 @@ static int ads131e08_probe(struct spi_device *spi)
indio_dev->name = st->info->name;
indio_dev->info = &ads131e08_iio_info;
- indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED;
init_completion(&st->completion);
if (spi->irq) {
- ret = devm_request_irq(&spi->dev, spi->irq,
- ads131e08_interrupt,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- spi->dev.driver->name, indio_dev);
+ ret = devm_request_irq(&spi->dev, spi->irq, ads131e08_interrupt,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ dev_name(&spi->dev), indio_dev);
if (ret)
return dev_err_probe(&spi->dev, ret,
"request irq failed\n");
@@ -840,13 +966,13 @@ static int ads131e08_probe(struct spi_device *spi)
}
st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
- indio_dev->name, iio_device_id(indio_dev));
+ indio_dev->name,
+ iio_device_id(indio_dev));
if (!st->trig) {
dev_err(&spi->dev, "failed to allocate IIO trigger\n");
return -ENOMEM;
}
- st->trig->ops = &ads131e08_trigger_ops;
st->trig->dev.parent = &spi->dev;
iio_trigger_set_drvdata(st->trig, indio_dev);
ret = devm_iio_trigger_register(&spi->dev, st->trig);
@@ -857,8 +983,9 @@ static int ads131e08_probe(struct spi_device *spi)
indio_dev->trig = iio_trigger_get(st->trig);
- ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
- NULL, &ads131e08_trigger_handler, NULL);
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
+ &ads131e08_trigger_handler,
+ &ads131e08_buffer_ops);
if (ret) {
dev_err(&spi->dev, "failed to setup IIO buffer\n");
return ret;
@@ -873,7 +1000,8 @@ static int ads131e08_probe(struct spi_device *spi)
return ret;
}
- ret = devm_add_action_or_reset(&spi->dev, ads131e08_regulator_disable, st);
+ ret = devm_add_action_or_reset(&spi->dev,
+ ads131e08_regulator_disable, st);
if (ret)
return ret;
} else {
@@ -891,7 +1019,7 @@ static int ads131e08_probe(struct spi_device *spi)
adc_clk_hz = clk_get_rate(st->adc_clk);
if (!adc_clk_hz) {
dev_err(&spi->dev, "failed to get the ADC clock rate\n");
- return -EINVAL;
+ return -EINVAL;
}
adc_clk_ns = NSEC_PER_SEC / adc_clk_hz;
@@ -910,13 +1038,19 @@ static int ads131e08_probe(struct spi_device *spi)
}
static const struct of_device_id ads131e08_of_match[] = {
- { .compatible = "ti,ads131e04",
- .data = &ads131e08_info_tbl[ads131e04], },
- { .compatible = "ti,ads131e06",
- .data = &ads131e08_info_tbl[ads131e06], },
- { .compatible = "ti,ads131e08",
- .data = &ads131e08_info_tbl[ads131e08], },
- { }
+ {
+ .compatible = "ti,ads131e04",
+ .data = &ads131e08_info_tbl[ads131e04],
+ },
+ {
+ .compatible = "ti,ads131e06",
+ .data = &ads131e08_info_tbl[ads131e06],
+ },
+ {
+ .compatible = "ti,ads131e08",
+ .data = &ads131e08_info_tbl[ads131e08],
+ },
+ {}
};
MODULE_DEVICE_TABLE(of, ads131e08_of_match);
@@ -924,7 +1058,7 @@ static const struct spi_device_id ads131e08_ids[] = {
{ "ads131e04", (kernel_ulong_t)&ads131e08_info_tbl[ads131e04] },
{ "ads131e06", (kernel_ulong_t)&ads131e08_info_tbl[ads131e06] },
{ "ads131e08", (kernel_ulong_t)&ads131e08_info_tbl[ads131e08] },
- { }
+ {}
};
MODULE_DEVICE_TABLE(spi, ads131e08_ids);
@@ -939,5 +1073,6 @@ static struct spi_driver ads131e08_driver = {
module_spi_driver(ads131e08_driver);
MODULE_AUTHOR("Tomislav Denis <tomislav.denis@avl.com>");
+MODULE_AUTHOR("Viktor Karamanis <viktor.karamanis@outlook.com>");
MODULE_DESCRIPTION("Driver for ADS131E0x ADC family");
MODULE_LICENSE("GPL v2");
--
2.43.0
^ permalink raw reply [flat|nested] 2+ messages in thread* Re: [PATCH 0/1] ti-ads131e08: Driver optimizations
2026-01-20 14:07 [PATCH 0/1] ti-ads131e08: Driver optimizations Viktor Karamanis
@ 2026-01-22 21:21 ` Jonathan Cameron
0 siblings, 0 replies; 2+ messages in thread
From: Jonathan Cameron @ 2026-01-22 21:21 UTC (permalink / raw)
To: Viktor Karamanis; +Cc: linux-iio, linux-kernel
On Tue, 20 Jan 2026 14:07:34 +0000
Viktor Karamanis <viktor.karamanis@outlook.com> wrote:
> This series optimizes the TI ADS131E08 ADC driver for better performance
> and reliability.
Hi Viktor and welcome to IIO.
>
> Key improvements in the driver:
> - Switch from RDATA polling to RDATAC continuous mode in buffer operations
> for reduced SPI overhead during continuous data acquisition
> - Add proper timing delays between SPI register writes to meet device
> timing requirements
> - Consolidate channel configuration functions to minimize unnecessary
> SPI transactions
> - Add status checking and proper cleanup functions
> - Remove redundant code and improve overall code structure
>
> Patch 1 contains the driver optimizations.
>
> As a sidenote, I noticed that the driver is not present in MAINTAINERS file.
>
> This is my first contribution to the Linux kernel,
> hopefully I did everything correctly.
> Please let me know if any changes are needed.
Unfortunately not. Please review the documentation on submitting patches
before sending a v2. It should have been a cover letter then patches
as separate emails in reply to that cover letter.
I'm guessing outlook.com isn't going to play well. So I'd suggest you
look at the b4 tool and it's options to use a web gateway to send
patches to the kernel mailing lists.
Anyhow to save time, I'll paste in what should have been the second email
and give a quick review.
> From 37bf00dc880e4ccae8578f6824d462d193cc6778 Mon Sep 17 00:00:00 2001
> From: Viktor Karamanis <viktor.karamanis@outlook.com>
> Date: Tue, 20 Jan 2026 14:46:28 +0200
> Subject: [PATCH 1/1] iio: adc: ti-ads131e08: Optimize performance and fix
> timing
>
> Optimize the TI ADS131E08 ADC driver for better performance and
> reliability:
>
> 1. Switch from RDATA polling to RDATAC continuous mode in buffer
> trigger operations. This reduces SPI overhead during continuous
> data acquisition by eliminating the need to send RDATA commands
> for each sample.
>
> 2. Add proper timing delays between SPI register writes. The ADS131E08
> requires specific delays after certain commands and register writes,
> which were not consistently implemented.
>
> 3. Consolidate channel configuration functions. Previously, multiple
> functions modified channel registers separately, causing unnecessary
> SPI transactions. Now a single function handles all channel
> configuration changes.
>
> 4. Add ads131e08_check_status() function to check device status bits
> for fault conditions, improving error detection.
>
> 5. Add ads131e08_stop_read_data_continuous() function for proper
> cleanup when stopping continuous mode.
>
> 6. Replace ads131e08_trigger_ops with ads131e08_buffer_preenable()
> and ads131e08_buffer_postdisable() for better integration
> with the IIO buffer framework.
>
> 7. Minor code cleanup, formatting fixes, and lint improvements.
First general comment. 1 patch per thing. So at very least this
is a 7 patch series. Not one mega patch.
The biggest feedback is don't make formatting changes in a patch
that does anyting functional. Also look at that sort of formatting
change very carefully and consider it if is a substantial improvement.
These sort of changes are sometimes fine, but there is cost to any code
modification (backporting fixes etc becomes harder).
Anyhow, there is clearly some more interesting code in here, so
I look forward to you sending a version that is easier to review.
thanks,
Jonathan
>
> The drives has been tested on a RPI 3b+ with 25Mhz spi speed
> and sample rates up to 16kSPS without issues.
> At sample rates above 16kSPS occasional CRC errors were observed,
> which might be related to hardware limitations.
>
> Signed-off-by: Viktor Karamanis <viktor.karamanis@outlook.com>
> ---
> drivers/iio/adc/ti-ads131e08.c | 643 ++++++++++++++++++++-------------
> 1 file changed, 389 insertions(+), 254 deletions(-)
>
> diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c
> index 085f0d6fb39e..5124622cc586 100644
> --- a/drivers/iio/adc/ti-ads131e08.c
> +++ b/drivers/iio/adc/ti-ads131e08.c
> -static int ads131e08_read_reg(struct ads131e08_state *st, u8 reg)
> +static int ads131e08_read_reg(struct ads131e08_state *st, u8 reg, u8 *val)
> {
> int ret;
> + u8 cmd0 = ADS131E08_CMD_RREG(reg);
> + u8 cmd1 = 0x00;
> + u8 rx;
> +
> struct spi_transfer transfer[] = {
> - {
> - .tx_buf = &st->tx_buf,
> - .len = 2,
> - .delay = {
> - .value = st->sdecode_delay_us,
> - .unit = SPI_DELAY_UNIT_USECS,
> - },
> - }, {
> - .rx_buf = &st->rx_buf,
> - .len = 1,
> - },
> + { .tx_buf = &cmd0,
This isn not the normal style for these structures. The orignal
code was the preferred style. Note that this sort of
reformat makes it impossible to spot if anything functional
changed in here.
> + .len = 1,
> + .cs_change = 0,
> + .delay = { .value = st->sdecode_delay_us,
> + .unit = SPI_DELAY_UNIT_USECS } },
> + { .tx_buf = &cmd1,
> + .len = 1,
> + .cs_change = 0,
> + .delay = { .value = st->sdecode_delay_us,
> + .unit = SPI_DELAY_UNIT_USECS } },
> + { .rx_buf = &rx, .len = 1, .cs_change = 0 }
> };
...
> static int ads131e08_write_raw(struct iio_dev *indio_dev,
> - struct iio_chan_spec const *channel, int value,
> - int value2, long mask)
> + struct iio_chan_spec const *channel, int value,
> + int value2, long mask)
> {
> struct ads131e08_state *st = iio_priv(indio_dev);
> int ret;
>
> switch (mask) {
> case IIO_CHAN_INFO_SAMP_FREQ:
> - if (!iio_device_claim_direct(indio_dev))
> - return -EBUSY;
> + ret = iio_device_claim_direct_mode(indio_dev);
This shows you are submitting a driver that doesn't build against
the upstream kernel at that interface has being removed.
Please be careful to use the latest kernel release (or the -rc1
after it) as a base.
> + if (ret)
> + return ret;
>
> ret = ads131e08_set_data_rate(st, value);
> - iio_device_release_direct(indio_dev);
> + iio_device_release_direct_mode(indio_dev);
> return ret;
>
> default:
> @@ -565,8 +644,7 @@ static int ads131e08_write_raw(struct iio_dev *indio_dev,
> static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1 2 4 8 16 32 64");
>
> static struct attribute *ads131e08_attributes[] = {
> - &iio_const_attr_sampling_frequency_available.dev_attr.attr,
> - NULL
> + &iio_const_attr_sampling_frequency_available.dev_attr.attr, NULL
> };
The formatting for these follows a standard pattern of one entry
per line so the NULL terminator is obvious, please leave itlike that.
>
> static const struct iio_info ads131e08_iio_info = {
> @@ -594,18 +684,52 @@ static const struct iio_info ads131e08_iio_info = {
> .debugfs_reg_access = &ads131e08_debugfs_reg_access,
> };
>
> -static int ads131e08_set_trigger_state(struct iio_trigger *trig, bool state)
> +static int ads131e08_buffer_preenable(struct iio_dev *indio_dev)
> {
> - struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
> struct ads131e08_state *st = iio_priv(indio_dev);
> - u8 cmd = state ? ADS131E08_CMD_START : ADS131E08_CMD_STOP;
> + int ret;
> +
> + memset(&st->xfer, 0, sizeof(st->xfer));
st->xfer = (spi_xfer) {
rx_buf = st->rx_buf,
etc will zero rest of structure so no need for memset +
make the code a little easier to read.
};
> + st->xfer.rx_buf = st->rx_buf;
> + st->xfer.len = st->readback_len;
> + st->xfer.cs_change = 0;
That's the 'obvious' default and it's zeroed above.
So don't set cs_change.
> + spi_message_init(&st->msg);
> + spi_message_add_tail(&st->xfer, &st->msg);
> +
> + ret = ads131e08_exec_cmd(st, ADS131E08_CMD_RDATAC,
> + st->sdecode_delay_us);
> + if (ret)
> + return ret;
> +
> + ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START, st->sdecode_delay_us);
> + if (ret)
> + return ret;
> +
> + st->rdatac_enabled = true;
> + return 0;
> +}
>
> static irqreturn_t ads131e08_trigger_handler(int irq, void *private)
> @@ -615,8 +739,6 @@ static irqreturn_t ads131e08_trigger_handler(int irq, void *private)
> struct ads131e08_state *st = iio_priv(indio_dev);
> unsigned int chn, i = 0;
> u8 *src, *dest;
> - int ret;
> -
> /*
> * The number of data bits per channel depends on the data rate.
> * For 32 and 64 ksps data rates, number of data bits per channel
> @@ -625,19 +747,22 @@ static irqreturn_t ads131e08_trigger_handler(int irq, void *private)
> * 16 bits of data into the buffer.
> */
> unsigned int num_bytes = ADS131E08_NUM_DATA_BYTES(st->data_rate);
> - u8 tweek_offset = num_bytes == 2 ? 1 : 0;
> + bool tweek_offset = (num_bytes == 2);
>
> - if (iio_trigger_using_own(indio_dev))
> - ret = ads131e08_read_data(st, st->readback_len);
> - else
> - ret = ads131e08_pool_data(st);
> + if (!st->rdatac_enabled)
> + goto out;
>
> - if (ret)
> + if (spi_sync(st->spi, &st->msg)) {
> + dev_warn(&st->spi->dev, "SPI read in thread failed\n");
Sounds like an error so if we are going to report it dev_err() is
appropriate.
> + goto out;
> + }
> +
> + if (ads131e08_check_status(st))
> goto out;
>
> iio_for_each_active_channel(indio_dev, chn) {
> src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES + chn * num_bytes;
> - dest = st->tmp_buf.data + i * ADS131E08_NUM_STORAGE_BYTES;
> + dest = st->data + i * ADS131E08_NUM_STORAGE_BYTES;
>
> /*
> * Tweek offset is 0:
> @@ -664,12 +789,11 @@ static irqreturn_t ads131e08_trigger_handler(int irq, void *private)
> i++;
> }
>
> - iio_push_to_buffers_with_ts(indio_dev, &st->tmp_buf, sizeof(st->tmp_buf),
> - iio_get_time_ns(indio_dev));
> + iio_push_to_buffers_with_timestamp(indio_dev, st->data,
> + iio_get_time_ns(indio_dev));
This switches to a deprecated interface with reduced error checking
which we should not do.
>
> out:
> iio_trigger_notify_done(indio_dev->trig);
> -
Please keep a blank line in places like this that are before simple returns.
> return IRQ_HANDLED;
> }
>
> @@ -678,8 +802,9 @@ static irqreturn_t ads131e08_interrupt(int irq, void *private)
> struct iio_dev *indio_dev = private;
> struct ads131e08_state *st = iio_priv(indio_dev);
>
> - if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev))
> + if (st->rdatac_enabled)
> iio_trigger_poll(st->trig);
> +
Why? This looks unnecessary to me. Makes sure to check for this sort
of white space change. They are easily introduced when working on a driver
but once we get to patch submission stage, they are just adding noise and
making the changes that matter harder to review.
> else
> complete(&st->completion);
> static void ads131e08_regulator_disable(void *data)
> @@ -815,6 +941,7 @@ static int ads131e08_probe(struct spi_device *spi)
> st = iio_priv(indio_dev);
> st->info = info;
> st->spi = spi;
> + st->rdatac_enabled = false;
>
> ret = ads131e08_alloc_channels(indio_dev);
> if (ret)
> @@ -822,15 +949,14 @@ static int ads131e08_probe(struct spi_device *spi)
>
> indio_dev->name = st->info->name;
> indio_dev->info = &ads131e08_iio_info;
> - indio_dev->modes = INDIO_DIRECT_MODE;
> + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED;
This change is a surprise. I'm not immediately seeing why it makes sense
given the extra flag is set internally in devm_iio_triggered_buffer_setup()
an no drivers should be setting it by hand.
> @@ -857,8 +983,9 @@ static int ads131e08_probe(struct spi_device *spi)
>
> indio_dev->trig = iio_trigger_get(st->trig);
>
> - ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
> - NULL, &ads131e08_trigger_handler, NULL);
> + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
> + &ads131e08_trigger_handler,
> + &ads131e08_buffer_ops);
This is fine but wants to be in a patch that only does code formatting
changes.
> if (ret) {
> dev_err(&spi->dev, "failed to setup IIO buffer\n");
> return ret;
> @@ -873,7 +1000,8 @@ static int ads131e08_probe(struct spi_device *spi)
> return ret;
> }
>
> - ret = devm_add_action_or_reset(&spi->dev, ads131e08_regulator_disable, st);
> + ret = devm_add_action_or_reset(&spi->dev,
> + ads131e08_regulator_disable, st);
Why? Arguably I'd have been fine with the shorter line length if the author
had chosen that, but we have some flexibility to go over 80 chars if it helps
readability. Changing it now seems like more trouble than it is worth.
> if (ret)
> return ret;
> } else {
> @@ -891,7 +1019,7 @@ static int ads131e08_probe(struct spi_device *spi)
> adc_clk_hz = clk_get_rate(st->adc_clk);
> if (!adc_clk_hz) {
> dev_err(&spi->dev, "failed to get the ADC clock rate\n");
> - return -EINVAL;
> + return -EINVAL;
This is fine, but white space only changes belong in their own patch that
does nothing else. Otherwise they add noise.
> }
>
> adc_clk_ns = NSEC_PER_SEC / adc_clk_hz;
> @@ -910,13 +1038,19 @@ static int ads131e08_probe(struct spi_device *spi)
> }
>
> static const struct of_device_id ads131e08_of_match[] = {
> - { .compatible = "ti,ads131e04",
> - .data = &ads131e08_info_tbl[ads131e04], },
> - { .compatible = "ti,ads131e06",
> - .data = &ads131e08_info_tbl[ads131e06], },
> - { .compatible = "ti,ads131e08",
> - .data = &ads131e08_info_tbl[ads131e08], },
> - { }
> + {
> + .compatible = "ti,ads131e04",
> + .data = &ads131e08_info_tbl[ads131e04],
> + },
> + {
> + .compatible = "ti,ads131e06",
> + .data = &ads131e08_info_tbl[ads131e06],
> + },
> + {
> + .compatible = "ti,ads131e08",
> + .data = &ads131e08_info_tbl[ads131e08],
> + },
Ok to reorganizing this. However, if you are going to touch
these I would like to see that array ads131e08_info_tbl
go away. It would be more concise as separate structures with
names that make it clear what they are.
ads131e04_info, ads131e06_info etc.
> + {}
As below.
> };
> MODULE_DEVICE_TABLE(of, ads131e08_of_match);
>
> @@ -924,7 +1058,7 @@ static const struct spi_device_id ads131e08_ids[] = {
> { "ads131e04", (kernel_ulong_t)&ads131e08_info_tbl[ads131e04] },
> { "ads131e06", (kernel_ulong_t)&ads131e08_info_tbl[ads131e06] },
> { "ads131e08", (kernel_ulong_t)&ads131e08_info_tbl[ads131e08] },
> - { }
> + {}
What is benefit of this change? It's actually breaking the preferred
style for IIO (which was randomly chosen to be { } for these).
> };
> MODULE_DEVICE_TABLE(spi, ads131e08_ids);
>
> @@ -939,5 +1073,6 @@ static struct spi_driver ads131e08_driver = {
> module_spi_driver(ads131e08_driver);
>
> MODULE_AUTHOR("Tomislav Denis <tomislav.denis@avl.com>");
> +MODULE_AUTHOR("Viktor Karamanis <viktor.karamanis@outlook.com>");
> MODULE_DESCRIPTION("Driver for ADS131E0x ADC family");
> MODULE_LICENSE("GPL v2");
> --
>
> Viktor Karamanis (1):
> iio: adc: ti-ads131e08: Optimize performance and fix timing
>
> drivers/iio/adc/ti-ads131e08.c | 643 ++++++++++++++++++++-------------
> 1 file changed, 389 insertions(+), 254 deletions(-)
>
> --
> 2.43.0
>
>
>
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-01-22 21:21 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-01-20 14:07 [PATCH 0/1] ti-ads131e08: Driver optimizations Viktor Karamanis
2026-01-22 21:21 ` Jonathan Cameron
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®