Devicetree
 help / color / mirror / Atom feed
From: "Kurt Borja" <kuurtb@gmail.com>
To: "David Lechner" <dlechner@baylibre.com>,
	"Kurt Borja" <kuurtb@gmail.com>,
	"Jonathan Cameron" <jic23@kernel.org>,
	"Rob Herring" <robh@kernel.org>,
	"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
	"Conor Dooley" <conor+dt@kernel.org>,
	"Linus Walleij" <linusw@kernel.org>,
	"Bartosz Golaszewski" <brgl@kernel.org>
Cc: "Nuno Sá" <nuno.sa@analog.com>,
	"Andy Shevchenko" <andy@kernel.org>,
	linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org
Subject: Re: [PATCH v3 7/9] iio: adc: ti-ads1262: support triggered buffer sampling
Date: Sun, 09 Aug 2026 03:28:28 -0500	[thread overview]
Message-ID: <DKK9S36LB543.2T124NE3TNETN@gmail.com> (raw)
In-Reply-To: <1b6b6981-1a08-42a2-a03a-4366b980da13@baylibre.com>

On Sat Aug 8, 2026 at 1:39 PM -05, David Lechner wrote:
> On 8/7/26 10:58 PM, Kurt Borja wrote:
>> Add triggered buffer support and a data-ready (DRDY) hardware trigger.
>> 
>> Signed-off-by: Kurt Borja <kuurtb@gmail.com>
>> ---
>>  drivers/iio/adc/Kconfig      |   2 +
>>  drivers/iio/adc/ti-ads1262.c | 264 +++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 266 insertions(+)
>> 
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index dbf76427912b..b9b561be8347 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig

[...]

>> @@ -241,8 +245,16 @@ struct ads1262 {
>>  	bool need_avss_uV;
>>  	bool bipolar_supply;
>>  
>> +	IIO_DECLARE_BUFFER_WITH_TS(__be32, scan_buffer,
>> +				   ADS1262_MAX_CHANNEL_COUNT);
>> +
>>  	/* Protects transfer buffers and concurrent SPI transfers */
>>  	struct mutex xfer_lock;
>> +	struct spi_message msg;
>> +	struct spi_transfer xfer;
>> +
>
> Where does the 11 come from?

Size needed to hold both transfers for multi channel read. I can use a
macro here.

>
>> +	u8 tx[11] __aligned(IIO_DMA_MINALIGN);
>> +	u8 rx[11] __aligned(IIO_DMA_MINALIGN);
>
> don't need second one to be aligned, they aren't indepedant.

Ah, I forgot this observation in the last version. These are used in a
full-duplex transfer, wouldn't that require for both to be on its own
cache line? I just started learning about DMA.

[...]

>> +static int ads1262_enable_and_read_last(struct ads1262 *st,
>> +					const struct iio_chan_spec *spec,
>> +					__be32 *val)
>> +{
>> +	struct ads1262_channel *chan;
>> +	int ret;
>> +
>> +	lockdep_assert_held(&st->xfer_lock);
>
> What happens if something else (e.g. gpio in the future) decides to do a
> register write here. If it wins the race, will it unintentially read the
> data? So do we also need to read the stored data via command here too?

On each trigger, we are holding the lock before we enable the first
channel, until after we read the final conversion. So we don't really
care if there's concurrent activity in-between triggers. Am I missing
something?

>
>> +
>> +	if (spec) {
>> +		guard(mutex)(&st->chan_lock);
>> +
>> +		chan = &st->channels[spec->scan_index];
>> +
>> +		/* Group 1: MODE0, MODE1, MODE2, INPMUX */
>> +		st->tx[0] = ADS1262_MODE0_REG | ADS1262_OPCODE_WREG;
>> +		st->tx[1] = ADS1262_INPMUX_REG - ADS1262_MODE0_REG;
>> +		st->tx[2] = FIELD_PREP(ADS1262_MODE0_INPUT_CHOP_MASK, chan->input_chop) |
>> +			    FIELD_PREP(ADS1262_MODE0_IDAC_CHOP_MASK, chan->idac_chop) |
>> +			    FIELD_PREP(ADS1262_MODE0_RUNMODE_MASK, ADS1262_RUNMODE_CONTINUOUS) |
>> +			    FIELD_PREP(ADS1262_MODE0_REFREV_MASK, chan->ref_reversal);
>> +		st->tx[3] = FIELD_PREP(ADS1262_MODE1_FILTER_MASK, ADS1262_FILTER_FIR);
>> +		st->tx[4] = FIELD_PREP(ADS1262_MODE2_DR_MASK, chan->data_rate) |
>> +			    FIELD_PREP(ADS1262_MODE2_GAIN_MASK, chan->gain);
>> +		st->tx[5] = FIELD_PREP(ADS1262_INPMUX_MUXP_MASK, spec->channel) |
>> +			    FIELD_PREP(ADS1262_INPMUX_MUXN_MASK, spec->channel2);
>> +
>> +		/* Group 2: IDACMUX, IDACMAG, REFMUX */
>> +		st->tx[6] = ADS1262_IDACMUX_REG | ADS1262_OPCODE_WREG;
>> +		st->tx[7] = ADS1262_REFMUX_REG - ADS1262_IDACMUX_REG;
>> +		st->tx[8] = FIELD_PREP(ADS1262_IDACMUX_MUX1_MASK, chan->idac_mux[0]) |
>> +			    FIELD_PREP(ADS1262_IDACMUX_MUX2_MASK, chan->idac_mux[1]);
>> +		st->tx[9] = FIELD_PREP(ADS1262_IDACMAG_MAG1_MASK, chan->idac_mag[0]) |
>> +			    FIELD_PREP(ADS1262_IDACMAG_MAG2_MASK, chan->idac_mag[1]);
>> +		st->tx[10] = FIELD_PREP(ADS1262_REFMUX_RMUXP_MASK, chan->ref_p) |
>> +			     FIELD_PREP(ADS1262_REFMUX_RMUXN_MASK, chan->ref_n);
>> +	} else {
>> +		memset(st->tx, 0, sizeof(st->tx));
>> +	}
>> +
>> +	ret = spi_sync(st->spi, &st->msg);
>> +	if (ret)
>> +		return ret;
>> +
>> +	memcpy(val, st->rx, sizeof(*val));
>> +
>> +	return 0;
>> +}
>> +
>> +static int ads1262_fill_buffer_mult(struct iio_dev *indio_dev)
>> +{
>> +	struct ads1262 *st = iio_priv(indio_dev);
>> +	unsigned int chan;
>> +	__be32 val;
>> +	int i = -1;
>> +	int ret;
>> +
>> +	/*
>> +	 * This routine enables and reads channels in a full-duplex fashion.
>> +	 *
>> +	 * When a channel is enabled, the previous conversion is clocked out of
>> +	 * the shift data register on the same transfer (Section 9.4.7.1). This
>> +	 * allows for low latency software sequencing but forbids any
>> +	 * communication with the chip in-between or data corruption may occur,
>> +	 * hence the need to take the xfer_lock for the whole operation.
>> +	 */
>> +	guard(mutex)(&st->xfer_lock);
>> +
>> +	iio_for_each_active_channel(indio_dev, chan) {
>> +		ret = ads1262_enable_and_read_last(st, &indio_dev->channels[chan],
>> +						   &val);
>> +		if (ret)
>> +			return ret;
>> +
>> +		/*
>> +		 * After writing to the channel configuration registers, the
>> +		 * conversion-cycle is restarted and the data registers are
>> +		 * cleared. This means we have to reinit the completion after
>> +		 * enabling to avoid reading stale data.
>> +		 */
>> +		reinit_completion(&st->drdy);
>
> This seems racy still as DRDY could have been triggered already, in which case
> we would time out waiting for the interrupt. Or does the DRDY toggle again
> even if we don't read the data to trigger another conversion?

Yep, in continuous mode it just keeps toggling. However, data corruption
may occur if we read just before DRDY is about to toggle again. Which is
why...

>
> Would it be possible to make one big SPI messsage that contains all enabled
> channels and just run that instead? Insted of waiting for drdy, it would have
> to add a delay at the end of the sequence of xfers for setting up each channel
> that was long enough to ensure that the conversion will be done when we read
> it.
>
> Or we could just do similar to the start_one() function and don't leave
> it in continuous conversion mode. Using the delay option of spi xfers, we
> could just tack on two more commands to start and stop the conversion after
> after writing all of the mode stuff so that it still all happens in one SPI
> message.

...this gave me an idea. Instead of relying on delays, which are a bit
of a pain to calculate because the datasheet only gives latency values
for the nominal clock speed (I'll have to reverse engineer the formulas
for settlingtime :]). I can actually read in pulse mode here and stuff
the start commands inside the same transfer. The buffer would look like:

	6 bytes       | 5 bytes       | 1 byte
	--------------+---------------+----------
	channel_cfg_1 | channel_cfg_2 | start cmd

Thankfully we can chain commands without having to lift the CS line so
this is efficient. I didn't know this when I first started developing
the driver.

The buffer of course can be further optimized if say, all channels share
the same IDAC and reference configuration, but that can be done later if
needed.

-- 
Thanks,
 ~ Kurt

  reply	other threads:[~2026-08-09  8:28 UTC|newest]

Thread overview: 36+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-08  3:58 [PATCH v3 0/9] iio: adc: Add TI ADS126X ADC family support Kurt Borja
2026-08-08  3:58 ` [PATCH v3 1/9] dt-bindings: iio: adc: support the TI ADS126x ADC family Kurt Borja
2026-08-08  4:08   ` sashiko-bot
2026-08-08 18:38   ` David Lechner
2026-08-09  8:26     ` Kurt Borja
2026-08-08  3:58 ` [PATCH v3 2/9] iio: adc: add the ti-ads1262 driver Kurt Borja
2026-08-08  4:11   ` sashiko-bot
2026-08-08 18:39   ` David Lechner
2026-08-09  8:26     ` Kurt Borja
2026-08-08 22:28   ` Uwe Kleine-König
2026-08-09 16:24     ` Kurt Borja
2026-08-08  3:58 ` [PATCH v3 3/9] iio: adc: ti-ads1262: support per-channel sampling frequency Kurt Borja
2026-08-08  4:11   ` sashiko-bot
2026-08-08 18:39   ` David Lechner
2026-08-09  8:27     ` Kurt Borja
2026-08-08  3:58 ` [PATCH v3 4/9] iio: adc: ti-ads1262: support per-channel reference and gain Kurt Borja
2026-08-08 18:39   ` David Lechner
2026-08-09  8:28     ` Kurt Borja
2026-08-08  3:58 ` [PATCH v3 5/9] iio: adc: ti-ads1262: support input chopping Kurt Borja
2026-08-08 18:39   ` David Lechner
2026-08-08  3:58 ` [PATCH v3 6/9] iio: adc: ti-ads1262: support excitation currents Kurt Borja
2026-08-08  4:13   ` sashiko-bot
2026-08-08 18:39   ` David Lechner
2026-08-08  3:58 ` [PATCH v3 7/9] iio: adc: ti-ads1262: support triggered buffer sampling Kurt Borja
2026-08-08  4:09   ` sashiko-bot
2026-08-08 18:39   ` David Lechner
2026-08-09  8:28     ` Kurt Borja [this message]
2026-08-08  3:58 ` [PATCH v3 8/9] iio: adc: ti-ads1262: support REFOUT and VBIAS regulators Kurt Borja
2026-08-08  4:11   ` sashiko-bot
2026-08-08 18:40   ` David Lechner
2026-08-09  8:28     ` Kurt Borja
2026-08-08  3:58 ` [PATCH v3 9/9] iio: adc: ti-ads1262: support common mode supplies Kurt Borja
2026-08-08 18:40   ` David Lechner
2026-08-09  8:29     ` Kurt Borja
2026-08-08 18:37 ` [PATCH v3 0/9] iio: adc: Add TI ADS126X ADC family support David Lechner
2026-08-09  8:29   ` Kurt Borja

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=DKK9S36LB543.2T124NE3TNETN@gmail.com \
    --to=kuurtb@gmail.com \
    --cc=andy@kernel.org \
    --cc=brgl@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dlechner@baylibre.com \
    --cc=jic23@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linusw@kernel.org \
    --cc=linux-gpio@vger.kernel.org \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=nuno.sa@analog.com \
    --cc=robh@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox