The Linux Kernel Mailing List
 help / color / mirror / Atom feed
  • * Re: [PATCH v9 3/6] iio: adc: ad4691: add triggered buffer support
           [not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-3-33e439e4fb87@analog.com>
           [not found]   ` <afhReLCsEdaEOT_H@ashevche-desk.local>
    @ 2026-05-05 14:04   ` Jonathan Cameron
      2026-05-05 14:07   ` Jonathan Cameron
      2026-05-07 11:37   ` Sabau, Radu bogdan
      3 siblings, 0 replies; 25+ messages in thread
    From: Jonathan Cameron @ 2026-05-05 14:04 UTC (permalink / raw)
      To: Radu Sabau via B4 Relay
      Cc: radu.sabau, Lars-Peter Clausen, Michael Hennerich, David Lechner,
    	Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
    	Conor Dooley, Uwe Kleine-König, Liam Girdwood, Mark Brown,
    	Linus Walleij, Bartosz Golaszewski, Philipp Zabel,
    	Jonathan Corbet, Shuah Khan, linux-iio, devicetree, linux-kernel,
    	linux-pwm, linux-gpio, linux-doc
    
    On Thu, 30 Apr 2026 13:16:45 +0300
    Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org> wrote:
    
    > From: Radu Sabau <radu.sabau@analog.com>
    > 
    > Add buffered capture support using the IIO triggered buffer framework.
    > 
    > CNV Burst Mode: the GP pin identified by interrupt-names in the device
    > tree is configured as DATA_READY output. The IRQ handler stops
    > conversions and fires the IIO trigger; the trigger handler executes a
    > pre-built SPI message that reads all active channels from the AVG_IN
    > accumulator registers and then resets accumulator state and restarts
    > conversions for the next cycle.
    > 
    > Manual Mode: CNV is tied to SPI CS so each transfer simultaneously
    > reads the previous result and starts the next conversion (pipelined
    > N+1 scheme). At preenable time a pre-built, optimised SPI message of
    > N+1 transfers is constructed (N channel reads plus one NOOP to drain
    > the pipeline). The trigger handler executes the message in a single
    > spi_sync() call and collects the results. An external trigger (e.g.
    > iio-trig-hrtimer) is required to drive the trigger at the desired
    > sample rate.
    > 
    > Both modes share the same trigger handler and push a complete scan —
    > one u16 slot per channel at its scan_index position, followed by a
    > timestamp — to the IIO buffer via iio_push_to_buffers_with_ts().
    > 
    > The CNV Burst Mode sampling frequency (PWM period) is exposed as a
    > buffer-level attribute via IIO_DEVICE_ATTR.
    > 
    > Signed-off-by: Radu Sabau <radu.sabau@analog.com>
    Hi Radu
    
    I haven't chased it through but Sashiko is raising concerns around the
    irq enabling / disable tricks this pulling and they may well be correct.
    Please take a close look at what happens in the remove path. We may
    need some local driver logic to avoid a double disable.
    
    https://sashiko.dev/#/patchset/20260430-ad4692-multichannel-sar-adc-driver-v9-0-33e439e4fb87%40analog.com
    
    > diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c
    > index 05826b762c7f..c1e3406fef18 100644
    > --- a/drivers/iio/adc/ad4691.c
    > +++ b/drivers/iio/adc/ad4691.c
    
    
    > +
    > +static irqreturn_t ad4691_irq(int irq, void *private)
    > +{
    > +	struct iio_dev *indio_dev = private;
    > +	struct ad4691_state *st = iio_priv(indio_dev);
    > +
    > +	iio_trigger_poll(indio_dev->trig);
    > +	/*
    > +	 * Keep the DATA_READY IRQ disabled until the trigger handler has
    > +	 * finished reading the scan, to prevent a new assertion mid-transfer.
    > +	 * The PWM continues running uninterrupted; the IRQ is re-enabled in
    > +	 * ad4691_trigger_handler once spi_sync completes.
    > +	 *
    > +	 * IRQF_ONESHOT already masks the hardware line during this threaded
    > +	 * handler, so disable_irq_nosync here ensures the IRQ stays disabled
    > +	 * even after IRQF_ONESHOT unmasks on return.
    > +	 */
    > +	disable_irq_nosync(st->irq);
    > +
    > +	return IRQ_HANDLED;
    > +}
    
    > +
    > +static irqreturn_t ad4691_trigger_handler(int irq, void *p)
    > +{
    > +	struct iio_poll_func *pf = p;
    > +	struct iio_dev *indio_dev = pf->indio_dev;
    > +	struct ad4691_state *st = iio_priv(indio_dev);
    > +
    > +	ad4691_read_scan(indio_dev, pf->timestamp);
    > +	if (!st->manual_mode)
    > +		enable_irq(st->irq);
    
    I think this should be in the reenable_trigger callback rather than here.
    That's ultimately fired by iio_trigger_notify_done.
    
    Sashiko had quite a bit to say about the problem paths around the buffer
    being disabled between the interrupt and the trigger handler. I don't think
    using the reenable trigger solves that though :(  We might be able
    to fix that centrally by always reenabling the trigger before
    disconnecting it but that's rather ugly. There is a note for the async
    trigger reenabling about a driver possibly needing to be careful
    to not reenable if the whole trigger was disabled in the meantime but
    this particular race isn't covered.
    
    
    Terrible though it is we may need to have some extra infrastructure
    to avoid the double disable (assuming it is real).
    
    
    > +	iio_trigger_notify_done(indio_dev->trig);
    > +	return IRQ_HANDLED;
    > +}
    
    
    
    ^ permalink raw reply	[flat|nested] 25+ messages in thread
  • * Re: [PATCH v9 3/6] iio: adc: ad4691: add triggered buffer support
           [not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-3-33e439e4fb87@analog.com>
           [not found]   ` <afhReLCsEdaEOT_H@ashevche-desk.local>
      2026-05-05 14:04   ` Jonathan Cameron
    @ 2026-05-05 14:07   ` Jonathan Cameron
      2026-05-07 11:37   ` Sabau, Radu bogdan
      3 siblings, 0 replies; 25+ messages in thread
    From: Jonathan Cameron @ 2026-05-05 14:07 UTC (permalink / raw)
      To: Radu Sabau via B4 Relay
      Cc: radu.sabau, Lars-Peter Clausen, Michael Hennerich, David Lechner,
    	Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
    	Conor Dooley, Uwe Kleine-König, Liam Girdwood, Mark Brown,
    	Linus Walleij, Bartosz Golaszewski, Philipp Zabel,
    	Jonathan Corbet, Shuah Khan, linux-iio, devicetree, linux-kernel,
    	linux-pwm, linux-gpio, linux-doc
    
    On Thu, 30 Apr 2026 13:16:45 +0300
    Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org> wrote:
    
    > From: Radu Sabau <radu.sabau@analog.com>
    > 
    > Add buffered capture support using the IIO triggered buffer framework.
    > 
    > CNV Burst Mode: the GP pin identified by interrupt-names in the device
    > tree is configured as DATA_READY output. The IRQ handler stops
    > conversions and fires the IIO trigger; the trigger handler executes a
    > pre-built SPI message that reads all active channels from the AVG_IN
    > accumulator registers and then resets accumulator state and restarts
    > conversions for the next cycle.
    > 
    > Manual Mode: CNV is tied to SPI CS so each transfer simultaneously
    > reads the previous result and starts the next conversion (pipelined
    > N+1 scheme). At preenable time a pre-built, optimised SPI message of
    > N+1 transfers is constructed (N channel reads plus one NOOP to drain
    > the pipeline). The trigger handler executes the message in a single
    > spi_sync() call and collects the results. An external trigger (e.g.
    > iio-trig-hrtimer) is required to drive the trigger at the desired
    > sample rate.
    > 
    > Both modes share the same trigger handler and push a complete scan —
    > one u16 slot per channel at its scan_index position, followed by a
    > timestamp — to the IIO buffer via iio_push_to_buffers_with_ts().
    > 
    > The CNV Burst Mode sampling frequency (PWM period) is exposed as a
    > buffer-level attribute via IIO_DEVICE_ATTR.
    > 
    > Signed-off-by: Radu Sabau <radu.sabau@analog.com>
    Another Sashiko found issue inline.
    
    Also it calls out that you have no validate_trigger which might mean
    another trigger is used.  Moving the irq enable to the trigger_reenable
    should solve that.
    
    > +static ssize_t sampling_frequency_show(struct device *dev,
    > +				       struct device_attribute *attr,
    > +				       char *buf)
    > +{
    > +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
    > +	struct ad4691_state *st = iio_priv(indio_dev);
    > +
    > +	return sysfs_emit(buf, "%lu\n", NSEC_PER_SEC / st->cnv_period_ns);
    > +}
    > +
    > +static ssize_t sampling_frequency_store(struct device *dev,
    > +					struct device_attribute *attr,
    > +					const char *buf, size_t len)
    > +{
    > +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
    > +	struct ad4691_state *st = iio_priv(indio_dev);
    > +	int freq, ret;
    > +
    > +	ret = kstrtoint(buf, 10, &freq);
    I missed this but as Sashiko points out this could read in a negative
    frequency. Given that's clearly silly kstrtouint()
    
    > +	if (ret)
    > +		return ret;
    > +
    > +	IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
    > +	if (IIO_DEV_ACQUIRE_FAILED(claim))
    > +		return -EBUSY;
    > +
    > +
    > +	ret = ad4691_set_pwm_freq(st, freq);
    > +	if (ret)
    > +		return ret;
    > +
    > +	return len;
    > +}
    > +
    > +static IIO_DEVICE_ATTR_RW(sampling_frequency, 0);
    > +
    > +static const struct iio_dev_attr *ad4691_buffer_attrs[] = {
    > +	&iio_dev_attr_sampling_frequency,
    > +	NULL
    > +};
    
    ^ permalink raw reply	[flat|nested] 25+ messages in thread
  • * RE: [PATCH v9 3/6] iio: adc: ad4691: add triggered buffer support
           [not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-3-33e439e4fb87@analog.com>
                         ` (2 preceding siblings ...)
      2026-05-05 14:07   ` Jonathan Cameron
    @ 2026-05-07 11:37   ` Sabau, Radu bogdan
      2026-05-07 14:25     ` Jonathan Cameron
      3 siblings, 1 reply; 25+ messages in thread
    From: Sabau, Radu bogdan @ 2026-05-07 11:37 UTC (permalink / raw)
      To: Sabau, Radu bogdan, Lars-Peter Clausen, Hennerich, Michael,
    	Jonathan Cameron, David Lechner, Sa, Nuno, Andy Shevchenko,
    	Rob Herring, Krzysztof Kozlowski, Conor Dooley,
    	Uwe Kleine-König, Liam Girdwood, Mark Brown, Linus Walleij,
    	Bartosz Golaszewski, Philipp Zabel, Jonathan Corbet, Shuah Khan
      Cc: linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
    	linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
    	linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
    
    Addressing Sashiko's review for triggered buffer patch.
    
    > -----Original Message-----
    > From: Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org>
    > Sent: Thursday, April 30, 2026 1:17 PM
    
    ...
    
    > +static int ad4691_manual_buffer_preenable(struct iio_dev *indio_dev)
    > +{
    > +	struct ad4691_state *st = iio_priv(indio_dev);
    > +	unsigned int prev_i, k, i;
    > +	bool first;
    > +	int ret;
    > +
    > +	memset(st->scan_xfers, 0, sizeof(st->scan_xfers));
    > +	memset(st->scan_tx, 0, sizeof(st->scan_tx));
    > +
    > +	spi_message_init(&st->scan_msg);
    > +
    > +	first = true;
    > +	prev_i = 0;
    > +	k = 0;
    > +	iio_for_each_active_channel(indio_dev, i) {
    > +		st->scan_tx[k] = cpu_to_be16(AD4691_ADC_CHAN(i));
    > +		st->scan_xfers[k].tx_buf = &st->scan_tx[k];
    > +		/*
    > +		 * The pipeline means xfer[0] receives the residual from the
    > +		 * previous sequence, not a valid sample for channel i. Point
    > +		 * it at vals[i] anyway; xfer[1] (or the NOOP when only one
    > +		 * channel is active) will overwrite that slot with the real
    > +		 * result, so no separate dummy buffer is needed.
    > +		 */
    > +		if (first) {
    > +			st->scan_xfers[k].rx_buf = &st->vals[i];
    > +			first = false;
    > +		} else {
    > +			st->scan_xfers[k].rx_buf = &st->vals[prev_i];
    > +		}
    
    
    "The IIO subsystem expects data pushed to the buffer to be densely packed
    according to the active channels in the scan mask.
    If only a subset of channels are enabled, does assigning the rx_buf pointer
    directly to absolute array indices at &st->vals[i] leave holes in the buffer?
    When iio_push_to_buffers_with_ts() is called, this might cause it to read
    uninitialized memory instead of the expected samples."
    
    I would say there is no change needed. Writing to &st->vals[scan_index] and
    passing the full array to iio_push_to_buffers_with_ts() is the standard IIO kfifo
    pattern: the core demultiplexes by reading data[scan_index * storagebits/8]
    for each active channel; holes at inactive indices are silently ignored.
    The same pattern is used in ad4695, ad_sigma_delta, and others. The
    pipeline residual in the first manual-mode transfer is overwritten by the
    subsequent transfer before the scan is pushed, as the comment explains.
    
    > +		st->scan_xfers[k].len = sizeof(__be16);
    > +		st->scan_xfers[k].cs_change = 1;
    > +		spi_message_add_tail(&st->scan_xfers[k], &st->scan_msg);
    > +		prev_i = i;
    > +		k++;
    > +	}
    > +
    
    ...
    
    > +static int ad4691_cnv_burst_buffer_preenable(struct iio_dev *indio_dev)
    > +{
    > +	struct ad4691_state *st = iio_priv(indio_dev);
    > +	unsigned int k, i;
    > +	int ret;
    > +
    > +	memset(st->scan_xfers, 0, sizeof(st->scan_xfers));
    > +	memset(st->scan_tx, 0, sizeof(st->scan_tx));
    > +
    > +	spi_message_init(&st->scan_msg);
    > +
    > +	/*
    > +	 * Each AVG_IN read needs two transfers: a 2-byte address write phase
    > +	 * followed by a 2-byte data read phase. CS toggles between channels
    > +	 * (cs_change=1 on the read phase of all but the last channel).
    > +	 */
    > +	k = 0;
    > +	iio_for_each_active_channel(indio_dev, i) {
    > +		st->scan_tx[k] = cpu_to_be16(0x8000 | AD4691_AVG_IN(i));
    > +		st->scan_xfers[2 * k].tx_buf = &st->scan_tx[k];
    > +		st->scan_xfers[2 * k].len = sizeof(__be16);
    > +		spi_message_add_tail(&st->scan_xfers[2 * k], &st-
    > >scan_msg);
    > +		st->scan_xfers[2 * k + 1].rx_buf = &st->vals[i];
    > +		st->scan_xfers[2 * k + 1].len = sizeof(__be16);
    > +		st->scan_xfers[2 * k + 1].cs_change = 1;
    > +		spi_message_add_tail(&st->scan_xfers[2 * k + 1], &st-
    > >scan_msg);
    > +		k++;
    > +	}
    > +
    > +	st->scan_tx[k] = cpu_to_be16(AD4691_STATE_RESET_REG);
    > +	st->scan_xfers[2 * k].tx_buf = &st->scan_tx[k];
    > +	st->scan_xfers[2 * k].len = sizeof(__be16);
    > +	spi_message_add_tail(&st->scan_xfers[2 * k], &st->scan_msg);
    > +	st->scan_tx[k + 1] = cpu_to_be16(AD4691_STATE_RESET_ALL << 8);
    
    
    "Does the iio_for_each_active_channel() loop iterate over the soft timestamp
    channel if it is enabled?
    If the soft timestamp channel is enabled, k would reach 17. Because
    st->scan_tx is sized for 18 elements, does writing to st->scan_tx[k + 1] and
    st->scan_xfers[2 * k + 1] overflow the bounds of these arrays?"
    
    Valid defensive concern. With all data channels + timestamp enabled,
    the loop iterates scan_index=16 (timestamp), pushing k to 17, and the
    state-reset then accesses scan_xfers[34] which is out-of-bounds (array is
    sized 34). Fixed by adding a guard to break out of the loop when
    i >= num_channels - 1 (the timestamp scan_index), consistent with how
    both ad4691_channels and ad4693_channels are laid out.
    
    "Also, the AD4691 auto-decrements register addresses on multi-byte writes.
    Could writing a 2-byte payload to the 1-byte AD4691_STATE_RESET_REG (0x181)
    inadvertently write 0x00 to the adjacent lower register 0x180
    (AD4691_OSC_EN_REG), disabling the internal oscillator?"
    
    Real bug. The two back-to-back 2-byte transfers ([0x01,0x81] then
    [0x01,0x00]) are sent with CS continuously asserted, so the chip sees a
    4-byte write: 0x01 to STATE_RESET_REG and 0x00 to OSC_EN_REG, disabling
    the oscillator mid-scan. Fixed by using a single 4-byte transfer
    [addr_hi, addr_lo, STATE_RESET_ALL, OSC_EN=1] in a dedicated
    u8 scan_tx_reset[4] field. The fourth byte deliberately re-enables
    OSC_EN_REG (0x180) via ADDR_DESCENDING as a side-write, keeping the
    oscillator enabled. This same buffer is reused by the offload path in
    the following commit (the two paths are mutually exclusive at probe).
    
    > +	st->scan_xfers[2 * k + 1].tx_buf = &st->scan_tx[k + 1];
    > +	st->scan_xfers[2 * k + 1].len = sizeof(__be16);
    > +	st->scan_xfers[2 * k + 1].cs_change = 1;
    > +	spi_message_add_tail(&st->scan_xfers[2 * k + 1], &st->scan_msg);
    > +
    > +	ret = spi_optimize_message(st->spi, &st->scan_msg);
    > +	if (ret)
    > +		return ret;
    > +
    > +	ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,
    > +			   bitmap_read(indio_dev->active_scan_mask, 0,
    > +				       iio_get_masklength(indio_dev)));
    > +	if (ret)
    > +		goto err_unoptimize;
    > +
    > +	ret = regmap_write(st->regmap, AD4691_ACC_MASK_REG,
    > +			   ~bitmap_read(indio_dev->active_scan_mask, 0,
    > +				iio_get_masklength(indio_dev)) &
    > GENMASK(15, 0));
    > +	if (ret)
    > +		goto err_unoptimize;
    > +
    > +	ret = ad4691_enter_conversion_mode(st);
    > +	if (ret)
    > +		goto err_unoptimize;
    > +
    > +	ret = ad4691_sampling_enable(st, true);
    > +	if (ret)
    > +		goto err_exit_conv;
    > +
    > +	enable_irq(st->irq);
    > +	return 0;
    
    "Is there a race condition introduced by enabling the PWM and unmasking the
    IRQ here?
    If a hardware interrupt fires before the IIO core attaches the trigger's poll
    function, iio_trigger_poll() drops the event. Will the IRQ handler then call
    disable_irq_nosync() without ever running the consumer thread to re-enable it?"
    
    Valid. preenable is called before the IIO core attaches the trigger
    poll function; if a DATA_READY IRQ fires in that window, iio_trigger_poll()
    is dropped, disable_irq_nosync() disables the IRQ, and enable_irq() is
    never called, leaving the IRQ stuck. Although the delay would need to be 
    very great for this to happen, I moved sampling_enable(true) and
    enable_irq() to a new postenable callback which the IIO core calls only
    after the trigger poll function is attached.
    
    > +
    > +err_exit_conv:
    > +	ad4691_exit_conversion_mode(st);
    > +err_unoptimize:
    > +	spi_unoptimize_message(&st->scan_msg);
    > +	return ret;
    > +}
    > +
    > +static int ad4691_cnv_burst_buffer_postdisable(struct iio_dev *indio_dev)
    > +{
    > +	struct ad4691_state *st = iio_priv(indio_dev);
    > +	int ret;
    > +
    > +	disable_irq(st->irq);
    
    "During buffer teardown, the IIO core detaches the trigger consumer before
    invoking the postdisable callback.
    If an interrupt fires while the PWM is still running before the call to
    ad4691_sampling_enable(), could ad4691_irq() disable the IRQ without a
    consumer to re-enable it? Does the subsequent disable_irq() call then increase
    the disable depth to 2, permanently disabling the hardware IRQ?"
    
    Valid, fixed as part of the previous concern rework. IRQ disabling and
    sampling_enable(false) are now in a new predisable callback (called before
    the consumer is detached); postdisable only handles SPI cleanup.
    
    > +
    > +	ret = ad4691_sampling_enable(st, false);
    > +	if (ret)
    > +		return ret;
    
    "If ad4691_sampling_enable() fails, does returning early bypass
    spi_unoptimize_message() and leak the DMA mappings allocated by the SPI core
    during preenable?"
    
    Valid. If pwm_apply_might_sleep() inside sampling_enable(false) fails,
    spi_unoptimize_message() was skipped. Always called now before returning
    from postdisable regardless of sampling_enable() result.
    
    > +
    > +	ret = ad4691_exit_conversion_mode(st);
    
    ...
    
    > +static ssize_t sampling_frequency_store(struct device *dev,
    > +					struct device_attribute *attr,
    > +					const char *buf, size_t len)
    > +{
    > +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
    > +	struct ad4691_state *st = iio_priv(indio_dev);
    > +	int freq, ret;
    > +
    > +	ret = kstrtoint(buf, 10, &freq);
    > +	if (ret)
    > +		return ret;
    > +
    > +	IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
    > +	if (IIO_DEV_ACQUIRE_FAILED(claim))
    > +		return -EBUSY;
    > +
    > +
    > +	ret = ad4691_set_pwm_freq(st, freq);
    
    
    "If a negative value is passed to kstrtoint(), does ad4691_set_pwm_freq()
    calculate a cnv_period_ns of 0 due to signed integer arithmetic rules?
    Could this lead to a division by zero regression in sampling_frequency_show()
    when it calculates NSEC_PER_SEC / st->cnv_period_ns?"
    
    Valid. Switching sampling_frequency_store to kstrtouint + unsigned int freq so
    Negative strings are rejected at parse time before reaching set_pwm_freq.
    
    > +	if (ret)
    > +		return ret;
    > +
    > +	return len;
    > +}
    > +
    > +static IIO_DEVICE_ATTR_RW(sampling_frequency, 0);
    > +
    > +static const struct iio_dev_attr *ad4691_buffer_attrs[] = {
    > +	&iio_dev_attr_sampling_frequency,
    > +	NULL
    > +};
    > +
    > +static irqreturn_t ad4691_irq(int irq, void *private)
    > +{
    > +	struct iio_dev *indio_dev = private;
    > +	struct ad4691_state *st = iio_priv(indio_dev);
    > +
    > +	iio_trigger_poll(indio_dev->trig);
    > +	/*
    > +	 * Keep the DATA_READY IRQ disabled until the trigger handler has
    > +	 * finished reading the scan, to prevent a new assertion mid-transfer.
    > +	 * The PWM continues running uninterrupted; the IRQ is re-enabled in
    > +	 * ad4691_trigger_handler once spi_sync completes.
    > +	 *
    > +	 * IRQF_ONESHOT already masks the hardware line during this
    > threaded
    > +	 * handler, so disable_irq_nosync here ensures the IRQ stays disabled
    > +	 * even after IRQF_ONESHOT unmasks on return.
    > +	 */
    > +	disable_irq_nosync(st->irq);
    > +
    > +	return IRQ_HANDLED;
    > +}
    
    "Can a concurrency issue occur here between this threaded IRQ handler and the
    trigger consumer thread?
    Since iio_trigger_poll() wakes up the consumer thread, could the consumer
    thread complete the SPI transfer and call enable_irq() before the provider
    thread reaches disable_irq_nosync()? This might trigger an unbalanced enable
    for IRQ warning and leave the hardware IRQ permanently disabled."
    
    Valid. Moved disable_irq_nosync() before iio_trigger_poll() so the
    IRQ is always disabled before the consumer thread gets a chance to call
    enable_irq().
    
    > +
    > +static const struct iio_trigger_ops ad4691_trigger_ops = {
    > +	.validate_device = iio_trigger_validate_own_device,
    
    ...
    
    > +
    >  static const struct iio_info ad4691_info = {
    >  	.read_raw = &ad4691_read_raw,
    >  	.write_raw = &ad4691_write_raw,
    > @@ -514,6 +910,18 @@ static const struct iio_info ad4691_info = {
    >  	.debugfs_reg_access = &ad4691_reg_access,
    >  };
    
    "Without a .validate_trigger callback here, can external triggers be attached?
    If an external trigger fires, the consumer thread might execute
    enable_irq(st->irq) without a corresponding hardware interrupt calling
    disable_irq_nosync() first, leading to an unbalanced IRQ enable warning."
    
    Valid. Added .validate_trigger = iio_validate_own_trigger to
    ad4691_info to prevent external triggers from causing unbalanced
    enable_irq() calls.
    
    
    ^ permalink raw reply	[flat|nested] 25+ messages in thread
  • [parent not found: <20260430-ad4692-multichannel-sar-adc-driver-v9-5-33e439e4fb87@analog.com>]
  • [parent not found: <20260430-ad4692-multichannel-sar-adc-driver-v9-6-33e439e4fb87@analog.com>]
  • [parent not found: <20260430-ad4692-multichannel-sar-adc-driver-v9-2-33e439e4fb87@analog.com>]
  • [parent not found: <20260430-ad4692-multichannel-sar-adc-driver-v9-4-33e439e4fb87@analog.com>]

  • end of thread, other threads:[~2026-05-08 11:12 UTC | newest]
    
    Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
    -- links below jump to the message on this page --
         [not found] <20260430-ad4692-multichannel-sar-adc-driver-v9-0-33e439e4fb87@analog.com>
         [not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-3-33e439e4fb87@analog.com>
         [not found]   ` <afhReLCsEdaEOT_H@ashevche-desk.local>
         [not found]     ` <LV9PR03MB841441B282275F8F36FD12C1F7312@LV9PR03MB8414.namprd03.prod.outlook.com>
    2026-05-05 13:26       ` [PATCH v9 3/6] iio: adc: ad4691: add triggered buffer support Jonathan Cameron
    2026-05-05 14:58         ` Andy Shevchenko
    2026-05-05 16:17           ` Jonathan Cameron
    2026-05-06  7:25             ` Andy Shevchenko
    2026-05-06  9:01               ` Sabau, Radu bogdan
    2026-05-05 14:04   ` Jonathan Cameron
    2026-05-05 14:07   ` Jonathan Cameron
    2026-05-07 11:37   ` Sabau, Radu bogdan
    2026-05-07 14:25     ` Jonathan Cameron
    2026-05-08 11:08       ` Sabau, Radu bogdan
         [not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-5-33e439e4fb87@analog.com>
    2026-05-05 14:32   ` [PATCH v9 5/6] iio: adc: ad4691: add oversampling support Jonathan Cameron
    2026-05-07 11:56   ` Sabau, Radu bogdan
    2026-05-07 15:26     ` Jonathan Cameron
         [not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-6-33e439e4fb87@analog.com>
    2026-05-05 14:35   ` [PATCH v9 6/6] docs: iio: adc: ad4691: add driver documentation Jonathan Cameron
         [not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-2-33e439e4fb87@analog.com>
    2026-05-05 13:23   ` [PATCH v9 2/6] iio: adc: ad4691: add initial driver for AD4691 family Jonathan Cameron
    2026-05-07  9:26   ` Sabau, Radu bogdan
    2026-05-07 14:15     ` Jonathan Cameron
    2026-05-08  4:44       ` Andy Shevchenko
    2026-05-08  9:53         ` Andy Shevchenko
         [not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-4-33e439e4fb87@analog.com>
    2026-05-05 14:12   ` [PATCH v9 4/6] iio: adc: ad4691: add SPI offload support Jonathan Cameron
    2026-05-05 14:28   ` Jonathan Cameron
    2026-05-06  9:17     ` Sabau, Radu bogdan
    2026-05-07 11:49   ` Sabau, Radu bogdan
    2026-05-07 15:11     ` Jonathan Cameron
    2026-05-08 11:11       ` Sabau, Radu bogdan
    

    This is a public inbox, see mirroring instructions
    for how to clone and mirror all data and code used for this inbox