Re: [PATCH RFC] iio: adc: ad7380: add alert support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Tue, Oct 29, 2024 at 04:02:46PM +0100, Julien Stephan kirjoitti:
> The alert functionality is an out of range indicator and can be used as an
> early indicator of an out of bounds conversion result.
> 
> ALERT_LOW_THRESHOLD and ALERT_HIGH_THRESHOLD registers are common to all
> channels.
> 
> When using 1 SDO line (only mode supported by the driver right now), i.e
> data outputs only on SDOA, SDOB (or SDOD for 4 channels variants) is
> used as an alert pin. The alert pin is updated at the end of the
> conversion (set to low if an alert occurs) and is cleared on a falling
> edge of CS.
> 
> The ALERT register contains information about the exact alert status:
> channel and direction. Unfortunately we can't read this register because
> in buffered read we cannot claim for direct mode.
> 
> User can set high/low thresholds and enable event detection using the
> regular iio events:
> 
>   events/in_thresh_falling_value
>   events/in_thresh_rising_value
>   events/thresh_either_en
> 
> Because we cannot read ALERT register, we can't determine the exact
> channel that triggers the alert, neither the direction (hight/low
> threshold violation), so we send and IIO_EV_DIR_EITHER event for all
> channels. This is ok, because the primary use case for this chip is to
> hardwire the alert line to shutdown the device.
> 
> When reading a channel raw data, we have to trigger 2 spi transactions: a
> first transaction that will trigger a conversion and a second
> transaction to read the conversion. By design a new conversion is
> initiated on each falling edge of CS. This will trigger a second
> interrupt. To avoid that we disable irq in the hard irq handler and
> re-enable them in thread handler.

...

>  #include <linux/iio/buffer.h>
>  #include <linux/iio/iio.h>
> +#include <linux/iio/events.h>

Perhaps keep it ordered?

>  #include <linux/iio/trigger_consumer.h>
>  #include <linux/iio/triggered_buffer.h>

...

> +	iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
> +		struct ad7380_state *st = iio_priv(indio_dev);
> +		int ret;
> +
> +		if (state == st->alert_en)
> +			return 0;
> +
> +		/*
> +		 * According to the datasheet, high threshold must always be
> +		 * greater than low threshold

Missed period at the end.

> +		 */
> +		if (state && st->high_th < st->low_th)
> +			return -EINVAL;
> +
> +		ret = regmap_update_bits(st->regmap,
> +					 AD7380_REG_ADDR_CONFIG1,
> +					 AD7380_CONFIG1_ALERTEN,
> +					 FIELD_PREP(AD7380_CONFIG1_ALERTEN, state));
> +
> +		if (ret)
> +			return ret;
> +
> +		st->alert_en = state;
> +
> +		return 0;
> +	}
> +	unreachable();
> +}

...

> +static int ad7380_write_event_value(struct iio_dev *indio_dev,
> +				    const struct iio_chan_spec *chan,
> +				    enum iio_event_type type,
> +				    enum iio_event_direction dir,
> +				    enum iio_event_info info,
> +				    int val, int val2)
> +{
> +	iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
> +		struct ad7380_state *st = iio_priv(indio_dev);
> +		int ret;
> +
> +		/*
> +		 * According to the datasheet,
> +		 * AD7380_REG_ADDR_ALERT_HIGH_TH[11:0] are the MSB of the 16-bit
> +		 * internal alert high register. LSB are set to 0x0.
> +		 * AD7380_REG_ADDR_ALERT_LOW_TH[11:0] are the MSB of the 16 bit
> +		 * internal alert low register. LSB are set to 0xf.
> +		 *
> +		 * When alert is enabled the conversion from the adc is compared
> +		 * immediately to the alert high/low thresholds, before any
> +		 * oversampling. This means that the thresholds are the same for
> +		 * normal mode and oversampling mode.
> +		 * For 12 and 14 bits, the thresholds are still on 16 bits.
> +		 */
> +		if (val < 0 || val > 2047)

What about having val >= BIT(11) here?

> +			return -EINVAL;
> +
> +		switch (dir) {
> +		case IIO_EV_DIR_RISING:
> +			ret = regmap_write(st->regmap,
> +					   AD7380_REG_ADDR_ALERT_HIGH_TH,
> +					   val);
> +			if (!ret)
> +				st->high_th = val << 4 | 0xf;

GENMASK() ? Predefined constant?

> +			return ret;


			if (ret)
				return ret;
			...
			return 0;

(yes, more verbose, but better for reading and maintenance)

> +		case IIO_EV_DIR_FALLING:
> +			ret = regmap_write(st->regmap,
> +					   AD7380_REG_ADDR_ALERT_LOW_TH,
> +					   val);
> +			if (!ret)
> +				st->low_th = val << 4;
> +			return ret;
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +	unreachable();
> +}

...

> +	st->high_th = 0x7ff;
> +	st->low_th = 0x800;

I would go with BIT(11) - 1 and BIT(11) as it seems related to the amount of
bits in the hardware.

...

> +static irqreturn_t ad7380_event_handler(int irq, void *private)
> +{
> +	struct iio_dev *indio_dev = private;
> +	s64 timestamp = iio_get_time_ns(indio_dev);
> +	struct ad7380_state *st = iio_priv(indio_dev);
> +	const struct iio_chan_spec *chan = &indio_dev->channels[0];
> +	int i = 0, j = 0;

Why signed? And for 'i' the assignment is redundant.

> +
> +	for (i = 0; i < st->chip_info->num_channels - 1; i++) {
> +		iio_push_event(indio_dev,
> +			       chan->differential ?
> +			       IIO_DIFF_EVENT_CODE(IIO_VOLTAGE,
> +						   j,
> +						   j + 1,
> +						   IIO_EV_TYPE_THRESH,
> +						   IIO_EV_DIR_EITHER) :
> +			       IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
> +						    i,
> +						    IIO_EV_TYPE_THRESH,
> +						    IIO_EV_DIR_EITHER),
> +			       timestamp);
> +		j += 2;
> +	}
> +
> +	enable_irq(irq);
> +
> +	return IRQ_HANDLED;
> +}

-- 
With Best Regards,
Andy Shevchenko






[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux