diff mbox series

[v2] iio: adis16480: support burst read function

Message ID 20210408075643.70183-1-nuno.sa@analog.com (mailing list archive)
State New, archived
Headers show
Series [v2] iio: adis16480: support burst read function | expand

Commit Message

Nuno Sa April 8, 2021, 7:56 a.m. UTC
Some supported devices support burst read function. This provides a method
for reading a batch of data (status, temperature, gyroscopes,
accelerometers, time stamp/data counter, and CRC code), which does not
require a stall time between each 16-bit segment and only requires one
command on the DIN line to initiate. Devices supporting this mode
are:

  * adis16495-1
  * adis16495-2
  * adis16495-3
  * adis16497-1
  * adis16497-2
  * adis16497-3

Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
Changes in v2:
 * Return right away if offset == 4 (no valid transition found from
brurst_id to sys_flags)

 drivers/iio/imu/adis16480.c | 161 +++++++++++++++++++++++++++++++++---
 1 file changed, 148 insertions(+), 13 deletions(-)

Comments

Lars-Peter Clausen April 9, 2021, 6:52 a.m. UTC | #1
On 4/8/21 9:56 AM, Nuno Sa wrote:
> Some supported devices support burst read function. This provides a method
> for reading a batch of data (status, temperature, gyroscopes,
> accelerometers, time stamp/data counter, and CRC code), which does not
> require a stall time between each 16-bit segment and only requires one
> command on the DIN line to initiate. Devices supporting this mode
> are:
>
>    * adis16495-1
>    * adis16495-2
>    * adis16495-3
>    * adis16497-1
>    * adis16497-2
>    * adis16497-3
>
> Signed-off-by: Nuno Sa <nuno.sa@analog.com>

Looks good to me.

Reviewed-by: Lars-Peter Clausen <lars@metafoo.de>
Jonathan Cameron April 11, 2021, 2:40 p.m. UTC | #2
On Thu, 8 Apr 2021 09:56:43 +0200
Nuno Sa <nuno.sa@analog.com> wrote:

> Some supported devices support burst read function. This provides a method
> for reading a batch of data (status, temperature, gyroscopes,
> accelerometers, time stamp/data counter, and CRC code), which does not
> require a stall time between each 16-bit segment and only requires one
> command on the DIN line to initiate. Devices supporting this mode
> are:
> 
>   * adis16495-1
>   * adis16495-2
>   * adis16495-3
>   * adis16497-1
>   * adis16497-2
>   * adis16497-3
> 
> Signed-off-by: Nuno Sa <nuno.sa@analog.com>
Small issue with return values in the trigger handler and
some stylistic comments.

Otherwise, looks good to me.

> ---
> Changes in v2:
>  * Return right away if offset == 4 (no valid transition found from
> brurst_id to sys_flags)
> 
>  drivers/iio/imu/adis16480.c | 161 +++++++++++++++++++++++++++++++++---
>  1 file changed, 148 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c
> index f81b86690b76..b782070a27f5 100644
> --- a/drivers/iio/imu/adis16480.c
> +++ b/drivers/iio/imu/adis16480.c
> @@ -5,6 +5,7 @@
>   * Copyright 2012 Analog Devices Inc.
>   */
>  
> +#include <linux/crc32.h>

Hmm. The ordering here is a bit odd so I guess this doesn't
really matter, but seems strange to have something so specific as
crc32.h as the first header included when it's not the first one
alphabetically.

>  #include <linux/clk.h>
>  #include <linux/bitfield.h>
>  #include <linux/of_irq.h>
> @@ -19,11 +20,14 @@
>  #include <linux/sysfs.h>
>  #include <linux/module.h>
>  #include <linux/lcm.h>
> +#include <linux/swab.h>
>  
>  #include <linux/iio/iio.h>
>  #include <linux/iio/sysfs.h>
>  #include <linux/iio/buffer.h>
>  #include <linux/iio/imu/adis.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
>  
>  #include <linux/debugfs.h>
>  
> @@ -103,6 +107,12 @@
>   * Available only for ADIS1649x devices
>   */
>  #define ADIS16495_REG_SYNC_SCALE		ADIS16480_REG(0x03, 0x10)
> +#define ADIS16495_REG_BURST_CMD			ADIS16480_REG(0x00, 0x7C)
> +#define ADIS16495_BURST_ID			0xA5A5
> +/* total number of segments in burst */
> +#define ADIS16495_BURST_MAX_DATA		20
> +/* spi max speed in burst mode */
> +#define ADIS16495_BURST_MAX_SPEED              6000000
>  
>  #define ADIS16480_REG_SERIAL_NUM		ADIS16480_REG(0x04, 0x20)
>  
> @@ -163,6 +173,8 @@ struct adis16480 {
>  	struct clk *ext_clk;
>  	enum adis16480_clock_mode clk_mode;
>  	unsigned int clk_freq;
> +	/* Alignment needed for the timestamp */
> +	__be16 data[ADIS16495_BURST_MAX_DATA] __aligned(8);
>  };
>  
>  static const char * const adis16480_int_pin_names[4] = {
> @@ -863,7 +875,7 @@ static const char * const adis16480_status_error_msgs[] = {
>  
>  static int adis16480_enable_irq(struct adis *adis, bool enable);
>  
> -#define ADIS16480_DATA(_prod_id, _timeouts)				\
> +#define ADIS16480_DATA(_prod_id, _timeouts, _burst_len)			\
>  {									\
>  	.diag_stat_reg = ADIS16480_REG_DIAG_STS,			\
>  	.glob_cmd_reg = ADIS16480_REG_GLOB_CMD,				\
> @@ -887,6 +899,8 @@ static int adis16480_enable_irq(struct adis *adis, bool enable);
>  		BIT(ADIS16480_DIAG_STAT_BARO_FAIL),			\
>  	.enable_irq = adis16480_enable_irq,				\
>  	.timeouts = (_timeouts),					\
> +	.burst_reg_cmd = ADIS16495_REG_BURST_CMD,			\
> +	.burst_len = (_burst_len),					\
>  }
>  
>  static const struct adis_timeout adis16485_timeouts = {
> @@ -931,7 +945,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
>  		.int_clk = 2460000,
>  		.max_dec_rate = 2048,
>  		.filter_freqs = adis16480_def_filter_freqs,
> -		.adis_data = ADIS16480_DATA(16375, &adis16485_timeouts),
> +		.adis_data = ADIS16480_DATA(16375, &adis16485_timeouts, 0),
>  	},
>  	[ADIS16480] = {
>  		.channels = adis16480_channels,
> @@ -944,7 +958,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
>  		.int_clk = 2460000,
>  		.max_dec_rate = 2048,
>  		.filter_freqs = adis16480_def_filter_freqs,
> -		.adis_data = ADIS16480_DATA(16480, &adis16480_timeouts),
> +		.adis_data = ADIS16480_DATA(16480, &adis16480_timeouts, 0),
>  	},
>  	[ADIS16485] = {
>  		.channels = adis16485_channels,
> @@ -957,7 +971,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
>  		.int_clk = 2460000,
>  		.max_dec_rate = 2048,
>  		.filter_freqs = adis16480_def_filter_freqs,
> -		.adis_data = ADIS16480_DATA(16485, &adis16485_timeouts),
> +		.adis_data = ADIS16480_DATA(16485, &adis16485_timeouts, 0),
>  	},
>  	[ADIS16488] = {
>  		.channels = adis16480_channels,
> @@ -970,7 +984,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
>  		.int_clk = 2460000,
>  		.max_dec_rate = 2048,
>  		.filter_freqs = adis16480_def_filter_freqs,
> -		.adis_data = ADIS16480_DATA(16488, &adis16485_timeouts),
> +		.adis_data = ADIS16480_DATA(16488, &adis16485_timeouts, 0),
>  	},
>  	[ADIS16490] = {
>  		.channels = adis16485_channels,
> @@ -984,7 +998,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
>  		.max_dec_rate = 4250,
>  		.filter_freqs = adis16495_def_filter_freqs,
>  		.has_pps_clk_mode = true,
> -		.adis_data = ADIS16480_DATA(16490, &adis16495_timeouts),
> +		.adis_data = ADIS16480_DATA(16490, &adis16495_timeouts, 0),
>  	},
>  	[ADIS16495_1] = {
>  		.channels = adis16485_channels,
> @@ -998,7 +1012,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
>  		.max_dec_rate = 4250,
>  		.filter_freqs = adis16495_def_filter_freqs,
>  		.has_pps_clk_mode = true,
> -		.adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts),
> +		/* 20 elements of 16bits */
> +		.adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts,
> +					    ADIS16495_BURST_MAX_DATA * 2),
>  	},
>  	[ADIS16495_2] = {
>  		.channels = adis16485_channels,
> @@ -1012,7 +1028,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
>  		.max_dec_rate = 4250,
>  		.filter_freqs = adis16495_def_filter_freqs,
>  		.has_pps_clk_mode = true,
> -		.adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts),
> +		/* 20 elements of 16bits */
> +		.adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts,
> +					    ADIS16495_BURST_MAX_DATA * 2),
>  	},
>  	[ADIS16495_3] = {
>  		.channels = adis16485_channels,
> @@ -1026,7 +1044,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
>  		.max_dec_rate = 4250,
>  		.filter_freqs = adis16495_def_filter_freqs,
>  		.has_pps_clk_mode = true,
> -		.adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts),
> +		/* 20 elements of 16bits */
> +		.adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts,
> +					    ADIS16495_BURST_MAX_DATA * 2),
>  	},
>  	[ADIS16497_1] = {
>  		.channels = adis16485_channels,
> @@ -1040,7 +1060,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
>  		.max_dec_rate = 4250,
>  		.filter_freqs = adis16495_def_filter_freqs,
>  		.has_pps_clk_mode = true,
> -		.adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts),
> +		/* 20 elements of 16bits */
> +		.adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts,
> +					    ADIS16495_BURST_MAX_DATA * 2),
>  	},
>  	[ADIS16497_2] = {
>  		.channels = adis16485_channels,
> @@ -1054,7 +1076,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
>  		.max_dec_rate = 4250,
>  		.filter_freqs = adis16495_def_filter_freqs,
>  		.has_pps_clk_mode = true,
> -		.adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts),
> +		/* 20 elements of 16bits */
> +		.adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts,
> +					    ADIS16495_BURST_MAX_DATA * 2),
>  	},
>  	[ADIS16497_3] = {
>  		.channels = adis16485_channels,
> @@ -1068,10 +1092,121 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
>  		.max_dec_rate = 4250,
>  		.filter_freqs = adis16495_def_filter_freqs,
>  		.has_pps_clk_mode = true,
> -		.adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts),
> +		/* 20 elements of 16bits */
> +		.adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts,
> +					    ADIS16495_BURST_MAX_DATA * 2),
>  	},
>  };
>  
> +static bool adis16480_validate_crc(const u16 *buf, const u8 n_elem, const u32 crc)
> +{
> +	u32 crc_calc;
> +	u16 crc_buf[15];
> +	int j;
> +
> +	for (j = 0; j < n_elem; j++)
> +		crc_buf[j] = swab16(buf[j]);
> +
> +	crc_calc = crc32(~0, crc_buf, n_elem * 2);
> +	crc_calc ^= ~0;
> +
> +	return (crc == crc_calc);
> +}
> +
> +static irqreturn_t adis16480_trigger_handler(int irq, void *p)
> +{
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct adis16480 *st = iio_priv(indio_dev);
> +	struct adis *adis = &st->adis;
> +	int ret, bit, offset, i = 0;
> +	__be16 *buffer;
> +	u32 crc;
> +	bool valid;
> +	const u32 cached_spi_speed_hz = adis->spi->max_speed_hz;
> +
> +	adis_dev_lock(adis);
> +	if (adis->current_page != 0) {
> +		adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
> +		adis->tx[1] = 0;
> +		ret = spi_write(adis->spi, adis->tx, 2);
> +		if (ret) {
> +			dev_err(&adis->spi->dev, "Failed to change device page: %d\n", ret);
> +			adis_dev_unlock(adis);
> +			return ret;

This is an interrupt handler, you should be careful what you return
as they will be treated as irqreturn_t not ints.

return IRQ_HANDLED even in error paths.

> +		}
> +	}
> +
> +	adis->spi->max_speed_hz = ADIS16495_BURST_MAX_SPEED;
> +
> +	ret = spi_sync(adis->spi, &adis->msg);
> +	if (ret) {
> +		dev_err(&adis->spi->dev, "Failed to read data: %d\n", ret);
> +		adis_dev_unlock(adis);
> +		return ret;
> +	}
> +
> +	adis->spi->max_speed_hz = cached_spi_speed_hz;
> +	adis->current_page = 0;

Does it make more sense to move this to just after we changed the page?
If something more complex is going on, perhaps a comment here?

> +	adis_dev_unlock(adis);
> +
> +	/*
> +	 * After making the burst request, the response can have one or two
> +	 * 16-bit responses containing the BURST_ID depending on the sclk. If
> +	 * clk > 3.6MHz, then we will have two BURST_ID in a row. If clk < 3MHZ,
> +	 * we have only one. To manage that variation, we use the transition from the
> +	 * BURST_ID to the SYS_E_FLAG register, which will not be equal to 0xA5A5. If
> +	 * we not find this variation in the first 4 segments, then the data should
> +	 * not be valid.
> +	 */
> +	buffer = adis->buffer;
> +	for (offset = 0; offset < 4; offset++) {
> +		u16 curr = be16_to_cpu(buffer[offset]);
> +		u16 next = be16_to_cpu(buffer[offset + 1]);
> +
> +		if (curr == ADIS16495_BURST_ID && next != ADIS16495_BURST_ID) {
> +			offset++;
> +			break;
> +		}
> +	}
> +
> +	if (offset == 4) {
> +		dev_err(&adis->spi->dev, "Invalid burst data\n");
> +		goto irq_done;
> +	}
> +
> +	crc = be16_to_cpu(buffer[offset + 16]) << 16 | be16_to_cpu(buffer[offset + 15]);
> +	valid = adis16480_validate_crc((u16 *)&buffer[offset], 15, crc);
> +	if (!valid) {
> +		dev_err(&adis->spi->dev, "Invalid crc\n");
> +		goto irq_done;
> +	}
> +
> +	for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) {
> +		/*
> +		 * When burst mode is used, temperature is the first data
> +		 * channel in the sequence, but the temperature scan index
> +		 * is 10.
> +		 */
> +		switch (bit) {
> +		case ADIS16480_SCAN_TEMP:
> +			st->data[i++] = buffer[offset + 1];
> +			break;
> +		case ADIS16480_SCAN_GYRO_X ... ADIS16480_SCAN_ACCEL_Z:
> +			/* The lower register data is sequenced first */
> +			st->data[i++] = buffer[2 * bit + offset + 3];
> +			st->data[i++] = buffer[2 * bit + offset + 2];
> +			break;
> +		}
> +	}
> +
> +	iio_push_to_buffers_with_timestamp(indio_dev, st->data, pf->timestamp);
> +irq_done:
> +	iio_trigger_notify_done(indio_dev->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +
>  static const struct iio_info adis16480_info = {
>  	.read_raw = &adis16480_read_raw,
>  	.write_raw = &adis16480_write_raw,
> @@ -1341,7 +1476,7 @@ static int adis16480_probe(struct spi_device *spi)
>  		st->clk_freq = st->chip_info->int_clk;
>  	}
>  
> -	ret = devm_adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL);
> +	ret = devm_adis_setup_buffer_and_trigger(&st->adis, indio_dev, adis16480_trigger_handler);

Whilst 100 chars is fine, it's preferred to stay under 80 when it doesn't hurt readability
so I'd like to see this on two lines.

>  	if (ret)
>  		return ret;
>
Nuno Sa April 12, 2021, 7:33 a.m. UTC | #3
> -----Original Message-----
> From: Jonathan Cameron <jic23@kernel.org>
> Sent: Sunday, April 11, 2021 4:40 PM
> To: Sa, Nuno <Nuno.Sa@analog.com>
> Cc: linux-iio@vger.kernel.org; Hennerich, Michael
> <Michael.Hennerich@analog.com>; Lars-Peter Clausen
> <lars@metafoo.de>
> Subject: Re: [PATCH v2] iio: adis16480: support burst read function
> 
> 
> On Thu, 8 Apr 2021 09:56:43 +0200
> Nuno Sa <nuno.sa@analog.com> wrote:
> 
> > Some supported devices support burst read function. This provides a
> method
> > for reading a batch of data (status, temperature, gyroscopes,
> > accelerometers, time stamp/data counter, and CRC code), which
> does not
> > require a stall time between each 16-bit segment and only requires
> one
> > command on the DIN line to initiate. Devices supporting this mode
> > are:
> >
> >   * adis16495-1
> >   * adis16495-2
> >   * adis16495-3
> >   * adis16497-1
> >   * adis16497-2
> >   * adis16497-3
> >
> > Signed-off-by: Nuno Sa <nuno.sa@analog.com>
> Small issue with return values in the trigger handler and
> some stylistic comments.
> 
> Otherwise, looks good to me.
> 
> > ---
> > Changes in v2:
> >  * Return right away if offset == 4 (no valid transition found from
> > brurst_id to sys_flags)
> >
> >  drivers/iio/imu/adis16480.c | 161
> +++++++++++++++++++++++++++++++++---
> >  1 file changed, 148 insertions(+), 13 deletions(-)
> >
> > diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c
> > index f81b86690b76..b782070a27f5 100644
> > --- a/drivers/iio/imu/adis16480.c
> > +++ b/drivers/iio/imu/adis16480.c
> > @@ -5,6 +5,7 @@
> >   * Copyright 2012 Analog Devices Inc.
> >   */
> >
> > +#include <linux/crc32.h>
> 
> Hmm. The ordering here is a bit odd so I guess this doesn't
> really matter, but seems strange to have something so specific as
> crc32.h as the first header included when it's not the first one
> alphabetically.

Yeah, it was not ordered already, so I didn't cared much. I will move more
to the middle...

> >  #include <linux/clk.h>
> >  #include <linux/bitfield.h>
> >  #include <linux/of_irq.h>
> > @@ -19,11 +20,14 @@
> >  #include <linux/sysfs.h>
> >  #include <linux/module.h>
> >  #include <linux/lcm.h>
> > +#include <linux/swab.h>
> >
> >  #include <linux/iio/iio.h>
> >  #include <linux/iio/sysfs.h>
> >  #include <linux/iio/buffer.h>
> >  #include <linux/iio/imu/adis.h>
> > +#include <linux/iio/triggered_buffer.h>
> > +#include <linux/iio/trigger_consumer.h>
> >
> >  #include <linux/debugfs.h>
> >
> > @@ -103,6 +107,12 @@
> >   * Available only for ADIS1649x devices
> >   */
> >  #define ADIS16495_REG_SYNC_SCALE
> 	ADIS16480_REG(0x03, 0x10)
> > +#define ADIS16495_REG_BURST_CMD
> 	ADIS16480_REG(0x00, 0x7C)
> > +#define ADIS16495_BURST_ID			0xA5A5
> > +/* total number of segments in burst */
> > +#define ADIS16495_BURST_MAX_DATA		20
> > +/* spi max speed in burst mode */
> > +#define ADIS16495_BURST_MAX_SPEED              6000000
> >
> >  #define ADIS16480_REG_SERIAL_NUM
> 	ADIS16480_REG(0x04, 0x20)
> >
> > @@ -163,6 +173,8 @@ struct adis16480 {
> >  	struct clk *ext_clk;
> >  	enum adis16480_clock_mode clk_mode;
> >  	unsigned int clk_freq;
> > +	/* Alignment needed for the timestamp */
> > +	__be16 data[ADIS16495_BURST_MAX_DATA] __aligned(8);
> >  };
> >
> >  static const char * const adis16480_int_pin_names[4] = {
> > @@ -863,7 +875,7 @@ static const char * const
> adis16480_status_error_msgs[] = {
> >
> >  static int adis16480_enable_irq(struct adis *adis, bool enable);
> >
> > -#define ADIS16480_DATA(_prod_id, _timeouts)
> 	\
> > +#define ADIS16480_DATA(_prod_id, _timeouts, _burst_len)
> 		\
> >  {									\
> >  	.diag_stat_reg = ADIS16480_REG_DIAG_STS,			\
> >  	.glob_cmd_reg = ADIS16480_REG_GLOB_CMD,
> 		\
> > @@ -887,6 +899,8 @@ static int adis16480_enable_irq(struct adis
> *adis, bool enable);
> >  		BIT(ADIS16480_DIAG_STAT_BARO_FAIL),
> 	\
> >  	.enable_irq = adis16480_enable_irq,				\
> >  	.timeouts = (_timeouts),					\
> > +	.burst_reg_cmd = ADIS16495_REG_BURST_CMD,
> 	\
> > +	.burst_len = (_burst_len),					\
> >  }
> >
> >  static const struct adis_timeout adis16485_timeouts = {
> > @@ -931,7 +945,7 @@ static const struct adis16480_chip_info
> adis16480_chip_info[] = {
> >  		.int_clk = 2460000,
> >  		.max_dec_rate = 2048,
> >  		.filter_freqs = adis16480_def_filter_freqs,
> > -		.adis_data = ADIS16480_DATA(16375,
> &adis16485_timeouts),
> > +		.adis_data = ADIS16480_DATA(16375,
> &adis16485_timeouts, 0),
> >  	},
> >  	[ADIS16480] = {
> >  		.channels = adis16480_channels,
> > @@ -944,7 +958,7 @@ static const struct adis16480_chip_info
> adis16480_chip_info[] = {
> >  		.int_clk = 2460000,
> >  		.max_dec_rate = 2048,
> >  		.filter_freqs = adis16480_def_filter_freqs,
> > -		.adis_data = ADIS16480_DATA(16480,
> &adis16480_timeouts),
> > +		.adis_data = ADIS16480_DATA(16480,
> &adis16480_timeouts, 0),
> >  	},
> >  	[ADIS16485] = {
> >  		.channels = adis16485_channels,
> > @@ -957,7 +971,7 @@ static const struct adis16480_chip_info
> adis16480_chip_info[] = {
> >  		.int_clk = 2460000,
> >  		.max_dec_rate = 2048,
> >  		.filter_freqs = adis16480_def_filter_freqs,
> > -		.adis_data = ADIS16480_DATA(16485,
> &adis16485_timeouts),
> > +		.adis_data = ADIS16480_DATA(16485,
> &adis16485_timeouts, 0),
> >  	},
> >  	[ADIS16488] = {
> >  		.channels = adis16480_channels,
> > @@ -970,7 +984,7 @@ static const struct adis16480_chip_info
> adis16480_chip_info[] = {
> >  		.int_clk = 2460000,
> >  		.max_dec_rate = 2048,
> >  		.filter_freqs = adis16480_def_filter_freqs,
> > -		.adis_data = ADIS16480_DATA(16488,
> &adis16485_timeouts),
> > +		.adis_data = ADIS16480_DATA(16488,
> &adis16485_timeouts, 0),
> >  	},
> >  	[ADIS16490] = {
> >  		.channels = adis16485_channels,
> > @@ -984,7 +998,7 @@ static const struct adis16480_chip_info
> adis16480_chip_info[] = {
> >  		.max_dec_rate = 4250,
> >  		.filter_freqs = adis16495_def_filter_freqs,
> >  		.has_pps_clk_mode = true,
> > -		.adis_data = ADIS16480_DATA(16490,
> &adis16495_timeouts),
> > +		.adis_data = ADIS16480_DATA(16490,
> &adis16495_timeouts, 0),
> >  	},
> >  	[ADIS16495_1] = {
> >  		.channels = adis16485_channels,
> > @@ -998,7 +1012,9 @@ static const struct adis16480_chip_info
> adis16480_chip_info[] = {
> >  		.max_dec_rate = 4250,
> >  		.filter_freqs = adis16495_def_filter_freqs,
> >  		.has_pps_clk_mode = true,
> > -		.adis_data = ADIS16480_DATA(16495,
> &adis16495_1_timeouts),
> > +		/* 20 elements of 16bits */
> > +		.adis_data = ADIS16480_DATA(16495,
> &adis16495_1_timeouts,
> > +
> ADIS16495_BURST_MAX_DATA * 2),
> >  	},
> >  	[ADIS16495_2] = {
> >  		.channels = adis16485_channels,
> > @@ -1012,7 +1028,9 @@ static const struct adis16480_chip_info
> adis16480_chip_info[] = {
> >  		.max_dec_rate = 4250,
> >  		.filter_freqs = adis16495_def_filter_freqs,
> >  		.has_pps_clk_mode = true,
> > -		.adis_data = ADIS16480_DATA(16495,
> &adis16495_1_timeouts),
> > +		/* 20 elements of 16bits */
> > +		.adis_data = ADIS16480_DATA(16495,
> &adis16495_1_timeouts,
> > +
> ADIS16495_BURST_MAX_DATA * 2),
> >  	},
> >  	[ADIS16495_3] = {
> >  		.channels = adis16485_channels,
> > @@ -1026,7 +1044,9 @@ static const struct adis16480_chip_info
> adis16480_chip_info[] = {
> >  		.max_dec_rate = 4250,
> >  		.filter_freqs = adis16495_def_filter_freqs,
> >  		.has_pps_clk_mode = true,
> > -		.adis_data = ADIS16480_DATA(16495,
> &adis16495_1_timeouts),
> > +		/* 20 elements of 16bits */
> > +		.adis_data = ADIS16480_DATA(16495,
> &adis16495_1_timeouts,
> > +
> ADIS16495_BURST_MAX_DATA * 2),
> >  	},
> >  	[ADIS16497_1] = {
> >  		.channels = adis16485_channels,
> > @@ -1040,7 +1060,9 @@ static const struct adis16480_chip_info
> adis16480_chip_info[] = {
> >  		.max_dec_rate = 4250,
> >  		.filter_freqs = adis16495_def_filter_freqs,
> >  		.has_pps_clk_mode = true,
> > -		.adis_data = ADIS16480_DATA(16497,
> &adis16495_1_timeouts),
> > +		/* 20 elements of 16bits */
> > +		.adis_data = ADIS16480_DATA(16497,
> &adis16495_1_timeouts,
> > +
> ADIS16495_BURST_MAX_DATA * 2),
> >  	},
> >  	[ADIS16497_2] = {
> >  		.channels = adis16485_channels,
> > @@ -1054,7 +1076,9 @@ static const struct adis16480_chip_info
> adis16480_chip_info[] = {
> >  		.max_dec_rate = 4250,
> >  		.filter_freqs = adis16495_def_filter_freqs,
> >  		.has_pps_clk_mode = true,
> > -		.adis_data = ADIS16480_DATA(16497,
> &adis16495_1_timeouts),
> > +		/* 20 elements of 16bits */
> > +		.adis_data = ADIS16480_DATA(16497,
> &adis16495_1_timeouts,
> > +
> ADIS16495_BURST_MAX_DATA * 2),
> >  	},
> >  	[ADIS16497_3] = {
> >  		.channels = adis16485_channels,
> > @@ -1068,10 +1092,121 @@ static const struct adis16480_chip_info
> adis16480_chip_info[] = {
> >  		.max_dec_rate = 4250,
> >  		.filter_freqs = adis16495_def_filter_freqs,
> >  		.has_pps_clk_mode = true,
> > -		.adis_data = ADIS16480_DATA(16497,
> &adis16495_1_timeouts),
> > +		/* 20 elements of 16bits */
> > +		.adis_data = ADIS16480_DATA(16497,
> &adis16495_1_timeouts,
> > +
> ADIS16495_BURST_MAX_DATA * 2),
> >  	},
> >  };
> >
> > +static bool adis16480_validate_crc(const u16 *buf, const u8 n_elem,
> const u32 crc)
> > +{
> > +	u32 crc_calc;
> > +	u16 crc_buf[15];
> > +	int j;
> > +
> > +	for (j = 0; j < n_elem; j++)
> > +		crc_buf[j] = swab16(buf[j]);
> > +
> > +	crc_calc = crc32(~0, crc_buf, n_elem * 2);
> > +	crc_calc ^= ~0;
> > +
> > +	return (crc == crc_calc);
> > +}
> > +
> > +static irqreturn_t adis16480_trigger_handler(int irq, void *p)
> > +{
> > +	struct iio_poll_func *pf = p;
> > +	struct iio_dev *indio_dev = pf->indio_dev;
> > +	struct adis16480 *st = iio_priv(indio_dev);
> > +	struct adis *adis = &st->adis;
> > +	int ret, bit, offset, i = 0;
> > +	__be16 *buffer;
> > +	u32 crc;
> > +	bool valid;
> > +	const u32 cached_spi_speed_hz = adis->spi->max_speed_hz;
> > +
> > +	adis_dev_lock(adis);
> > +	if (adis->current_page != 0) {
> > +		adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
> > +		adis->tx[1] = 0;
> > +		ret = spi_write(adis->spi, adis->tx, 2);
> > +		if (ret) {
> > +			dev_err(&adis->spi->dev, "Failed to change
> device page: %d\n", ret);
> > +			adis_dev_unlock(adis);
> > +			return ret;
> 
> This is an interrupt handler, you should be careful what you return
> as they will be treated as irqreturn_t not ints.
> 
> return IRQ_HANDLED even in error paths.

Hmm, yeah, this is definitely not ok. Also imposes the question if we should
call ' iio_trigger_notify_done()' in these error paths? I'm pending to do it as
it might be a big assumption to say the device is 'broken' if some spi transfer
fails...

Not doing it means we will never receive another irq (I think this is also true if
we do not return IRQ_HANDLED)...

Also need to check other places as I'm fairly sure we have this problem (at least)
in the adis16475 driver...
> > +		}
> > +	}
> > +
> > +	adis->spi->max_speed_hz = ADIS16495_BURST_MAX_SPEED;
> > +
> > +	ret = spi_sync(adis->spi, &adis->msg);
> > +	if (ret) {
> > +		dev_err(&adis->spi->dev, "Failed to read data: %d\n",
> ret);
> > +		adis_dev_unlock(adis);
> > +		return ret;
> > +	}
> > +
> > +	adis->spi->max_speed_hz = cached_spi_speed_hz;
> > +	adis->current_page = 0;
> 
> Does it make more sense to move this to just after we changed the
> page?

Yes, it does. If the second spi transfer fails, we already moved to page 0
but did not updated this variable...

- Nuno Sá
Jonathan Cameron April 12, 2021, 10:18 a.m. UTC | #4
...
> > > +static irqreturn_t adis16480_trigger_handler(int irq, void *p)
> > > +{
> > > +	struct iio_poll_func *pf = p;
> > > +	struct iio_dev *indio_dev = pf->indio_dev;
> > > +	struct adis16480 *st = iio_priv(indio_dev);
> > > +	struct adis *adis = &st->adis;
> > > +	int ret, bit, offset, i = 0;
> > > +	__be16 *buffer;
> > > +	u32 crc;
> > > +	bool valid;
> > > +	const u32 cached_spi_speed_hz = adis->spi->max_speed_hz;
> > > +
> > > +	adis_dev_lock(adis);
> > > +	if (adis->current_page != 0) {
> > > +		adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
> > > +		adis->tx[1] = 0;
> > > +		ret = spi_write(adis->spi, adis->tx, 2);
> > > +		if (ret) {
> > > +			dev_err(&adis->spi->dev, "Failed to change  
> > device page: %d\n", ret);  
> > > +			adis_dev_unlock(adis);
> > > +			return ret;  
> > 
> > This is an interrupt handler, you should be careful what you return
> > as they will be treated as irqreturn_t not ints.
> > 
> > return IRQ_HANDLED even in error paths.  
> 
> Hmm, yeah, this is definitely not ok. Also imposes the question if we should
> call ' iio_trigger_notify_done()' in these error paths? I'm pending to do it as
> it might be a big assumption to say the device is 'broken' if some spi transfer
> fails...

Yup, that has always been a bit of an open question in drivers. As likely
as not, any breakage leaves the device in a state from which we can't recover
anyway.  I've mostly left whether to call iio_trigger_notify_done() to the
discretion of the driver writers.

> 
> Not doing it means we will never receive another irq (I think this is also true if
> we do not return IRQ_HANDLED)...
> 
> Also need to check other places as I'm fairly sure we have this problem (at least)
> in the adis16475 driver...
oops. Guess I missed it there ;)
> > > +		}
> > > +	}
> > > +
> > > +	adis->spi->max_speed_hz = ADIS16495_BURST_MAX_SPEED;
> > > +
> > > +	ret = spi_sync(adis->spi, &adis->msg);
> > > +	if (ret) {
> > > +		dev_err(&adis->spi->dev, "Failed to read data: %d\n",  
> > ret);  
> > > +		adis_dev_unlock(adis);
> > > +		return ret;
> > > +	}
> > > +
> > > +	adis->spi->max_speed_hz = cached_spi_speed_hz;
> > > +	adis->current_page = 0;  
> > 
> > Does it make more sense to move this to just after we changed the
> > page?  
> 
> Yes, it does. If the second spi transfer fails, we already moved to page 0
> but did not updated this variable...
> 
> - Nuno Sá
diff mbox series

Patch

diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c
index f81b86690b76..b782070a27f5 100644
--- a/drivers/iio/imu/adis16480.c
+++ b/drivers/iio/imu/adis16480.c
@@ -5,6 +5,7 @@ 
  * Copyright 2012 Analog Devices Inc.
  */
 
+#include <linux/crc32.h>
 #include <linux/clk.h>
 #include <linux/bitfield.h>
 #include <linux/of_irq.h>
@@ -19,11 +20,14 @@ 
 #include <linux/sysfs.h>
 #include <linux/module.h>
 #include <linux/lcm.h>
+#include <linux/swab.h>
 
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
 #include <linux/iio/buffer.h>
 #include <linux/iio/imu/adis.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
 
 #include <linux/debugfs.h>
 
@@ -103,6 +107,12 @@ 
  * Available only for ADIS1649x devices
  */
 #define ADIS16495_REG_SYNC_SCALE		ADIS16480_REG(0x03, 0x10)
+#define ADIS16495_REG_BURST_CMD			ADIS16480_REG(0x00, 0x7C)
+#define ADIS16495_BURST_ID			0xA5A5
+/* total number of segments in burst */
+#define ADIS16495_BURST_MAX_DATA		20
+/* spi max speed in burst mode */
+#define ADIS16495_BURST_MAX_SPEED              6000000
 
 #define ADIS16480_REG_SERIAL_NUM		ADIS16480_REG(0x04, 0x20)
 
@@ -163,6 +173,8 @@  struct adis16480 {
 	struct clk *ext_clk;
 	enum adis16480_clock_mode clk_mode;
 	unsigned int clk_freq;
+	/* Alignment needed for the timestamp */
+	__be16 data[ADIS16495_BURST_MAX_DATA] __aligned(8);
 };
 
 static const char * const adis16480_int_pin_names[4] = {
@@ -863,7 +875,7 @@  static const char * const adis16480_status_error_msgs[] = {
 
 static int adis16480_enable_irq(struct adis *adis, bool enable);
 
-#define ADIS16480_DATA(_prod_id, _timeouts)				\
+#define ADIS16480_DATA(_prod_id, _timeouts, _burst_len)			\
 {									\
 	.diag_stat_reg = ADIS16480_REG_DIAG_STS,			\
 	.glob_cmd_reg = ADIS16480_REG_GLOB_CMD,				\
@@ -887,6 +899,8 @@  static int adis16480_enable_irq(struct adis *adis, bool enable);
 		BIT(ADIS16480_DIAG_STAT_BARO_FAIL),			\
 	.enable_irq = adis16480_enable_irq,				\
 	.timeouts = (_timeouts),					\
+	.burst_reg_cmd = ADIS16495_REG_BURST_CMD,			\
+	.burst_len = (_burst_len),					\
 }
 
 static const struct adis_timeout adis16485_timeouts = {
@@ -931,7 +945,7 @@  static const struct adis16480_chip_info adis16480_chip_info[] = {
 		.int_clk = 2460000,
 		.max_dec_rate = 2048,
 		.filter_freqs = adis16480_def_filter_freqs,
-		.adis_data = ADIS16480_DATA(16375, &adis16485_timeouts),
+		.adis_data = ADIS16480_DATA(16375, &adis16485_timeouts, 0),
 	},
 	[ADIS16480] = {
 		.channels = adis16480_channels,
@@ -944,7 +958,7 @@  static const struct adis16480_chip_info adis16480_chip_info[] = {
 		.int_clk = 2460000,
 		.max_dec_rate = 2048,
 		.filter_freqs = adis16480_def_filter_freqs,
-		.adis_data = ADIS16480_DATA(16480, &adis16480_timeouts),
+		.adis_data = ADIS16480_DATA(16480, &adis16480_timeouts, 0),
 	},
 	[ADIS16485] = {
 		.channels = adis16485_channels,
@@ -957,7 +971,7 @@  static const struct adis16480_chip_info adis16480_chip_info[] = {
 		.int_clk = 2460000,
 		.max_dec_rate = 2048,
 		.filter_freqs = adis16480_def_filter_freqs,
-		.adis_data = ADIS16480_DATA(16485, &adis16485_timeouts),
+		.adis_data = ADIS16480_DATA(16485, &adis16485_timeouts, 0),
 	},
 	[ADIS16488] = {
 		.channels = adis16480_channels,
@@ -970,7 +984,7 @@  static const struct adis16480_chip_info adis16480_chip_info[] = {
 		.int_clk = 2460000,
 		.max_dec_rate = 2048,
 		.filter_freqs = adis16480_def_filter_freqs,
-		.adis_data = ADIS16480_DATA(16488, &adis16485_timeouts),
+		.adis_data = ADIS16480_DATA(16488, &adis16485_timeouts, 0),
 	},
 	[ADIS16490] = {
 		.channels = adis16485_channels,
@@ -984,7 +998,7 @@  static const struct adis16480_chip_info adis16480_chip_info[] = {
 		.max_dec_rate = 4250,
 		.filter_freqs = adis16495_def_filter_freqs,
 		.has_pps_clk_mode = true,
-		.adis_data = ADIS16480_DATA(16490, &adis16495_timeouts),
+		.adis_data = ADIS16480_DATA(16490, &adis16495_timeouts, 0),
 	},
 	[ADIS16495_1] = {
 		.channels = adis16485_channels,
@@ -998,7 +1012,9 @@  static const struct adis16480_chip_info adis16480_chip_info[] = {
 		.max_dec_rate = 4250,
 		.filter_freqs = adis16495_def_filter_freqs,
 		.has_pps_clk_mode = true,
-		.adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts),
+		/* 20 elements of 16bits */
+		.adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts,
+					    ADIS16495_BURST_MAX_DATA * 2),
 	},
 	[ADIS16495_2] = {
 		.channels = adis16485_channels,
@@ -1012,7 +1028,9 @@  static const struct adis16480_chip_info adis16480_chip_info[] = {
 		.max_dec_rate = 4250,
 		.filter_freqs = adis16495_def_filter_freqs,
 		.has_pps_clk_mode = true,
-		.adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts),
+		/* 20 elements of 16bits */
+		.adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts,
+					    ADIS16495_BURST_MAX_DATA * 2),
 	},
 	[ADIS16495_3] = {
 		.channels = adis16485_channels,
@@ -1026,7 +1044,9 @@  static const struct adis16480_chip_info adis16480_chip_info[] = {
 		.max_dec_rate = 4250,
 		.filter_freqs = adis16495_def_filter_freqs,
 		.has_pps_clk_mode = true,
-		.adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts),
+		/* 20 elements of 16bits */
+		.adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts,
+					    ADIS16495_BURST_MAX_DATA * 2),
 	},
 	[ADIS16497_1] = {
 		.channels = adis16485_channels,
@@ -1040,7 +1060,9 @@  static const struct adis16480_chip_info adis16480_chip_info[] = {
 		.max_dec_rate = 4250,
 		.filter_freqs = adis16495_def_filter_freqs,
 		.has_pps_clk_mode = true,
-		.adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts),
+		/* 20 elements of 16bits */
+		.adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts,
+					    ADIS16495_BURST_MAX_DATA * 2),
 	},
 	[ADIS16497_2] = {
 		.channels = adis16485_channels,
@@ -1054,7 +1076,9 @@  static const struct adis16480_chip_info adis16480_chip_info[] = {
 		.max_dec_rate = 4250,
 		.filter_freqs = adis16495_def_filter_freqs,
 		.has_pps_clk_mode = true,
-		.adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts),
+		/* 20 elements of 16bits */
+		.adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts,
+					    ADIS16495_BURST_MAX_DATA * 2),
 	},
 	[ADIS16497_3] = {
 		.channels = adis16485_channels,
@@ -1068,10 +1092,121 @@  static const struct adis16480_chip_info adis16480_chip_info[] = {
 		.max_dec_rate = 4250,
 		.filter_freqs = adis16495_def_filter_freqs,
 		.has_pps_clk_mode = true,
-		.adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts),
+		/* 20 elements of 16bits */
+		.adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts,
+					    ADIS16495_BURST_MAX_DATA * 2),
 	},
 };
 
+static bool adis16480_validate_crc(const u16 *buf, const u8 n_elem, const u32 crc)
+{
+	u32 crc_calc;
+	u16 crc_buf[15];
+	int j;
+
+	for (j = 0; j < n_elem; j++)
+		crc_buf[j] = swab16(buf[j]);
+
+	crc_calc = crc32(~0, crc_buf, n_elem * 2);
+	crc_calc ^= ~0;
+
+	return (crc == crc_calc);
+}
+
+static irqreturn_t adis16480_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct adis16480 *st = iio_priv(indio_dev);
+	struct adis *adis = &st->adis;
+	int ret, bit, offset, i = 0;
+	__be16 *buffer;
+	u32 crc;
+	bool valid;
+	const u32 cached_spi_speed_hz = adis->spi->max_speed_hz;
+
+	adis_dev_lock(adis);
+	if (adis->current_page != 0) {
+		adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
+		adis->tx[1] = 0;
+		ret = spi_write(adis->spi, adis->tx, 2);
+		if (ret) {
+			dev_err(&adis->spi->dev, "Failed to change device page: %d\n", ret);
+			adis_dev_unlock(adis);
+			return ret;
+		}
+	}
+
+	adis->spi->max_speed_hz = ADIS16495_BURST_MAX_SPEED;
+
+	ret = spi_sync(adis->spi, &adis->msg);
+	if (ret) {
+		dev_err(&adis->spi->dev, "Failed to read data: %d\n", ret);
+		adis_dev_unlock(adis);
+		return ret;
+	}
+
+	adis->spi->max_speed_hz = cached_spi_speed_hz;
+	adis->current_page = 0;
+	adis_dev_unlock(adis);
+
+	/*
+	 * After making the burst request, the response can have one or two
+	 * 16-bit responses containing the BURST_ID depending on the sclk. If
+	 * clk > 3.6MHz, then we will have two BURST_ID in a row. If clk < 3MHZ,
+	 * we have only one. To manage that variation, we use the transition from the
+	 * BURST_ID to the SYS_E_FLAG register, which will not be equal to 0xA5A5. If
+	 * we not find this variation in the first 4 segments, then the data should
+	 * not be valid.
+	 */
+	buffer = adis->buffer;
+	for (offset = 0; offset < 4; offset++) {
+		u16 curr = be16_to_cpu(buffer[offset]);
+		u16 next = be16_to_cpu(buffer[offset + 1]);
+
+		if (curr == ADIS16495_BURST_ID && next != ADIS16495_BURST_ID) {
+			offset++;
+			break;
+		}
+	}
+
+	if (offset == 4) {
+		dev_err(&adis->spi->dev, "Invalid burst data\n");
+		goto irq_done;
+	}
+
+	crc = be16_to_cpu(buffer[offset + 16]) << 16 | be16_to_cpu(buffer[offset + 15]);
+	valid = adis16480_validate_crc((u16 *)&buffer[offset], 15, crc);
+	if (!valid) {
+		dev_err(&adis->spi->dev, "Invalid crc\n");
+		goto irq_done;
+	}
+
+	for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) {
+		/*
+		 * When burst mode is used, temperature is the first data
+		 * channel in the sequence, but the temperature scan index
+		 * is 10.
+		 */
+		switch (bit) {
+		case ADIS16480_SCAN_TEMP:
+			st->data[i++] = buffer[offset + 1];
+			break;
+		case ADIS16480_SCAN_GYRO_X ... ADIS16480_SCAN_ACCEL_Z:
+			/* The lower register data is sequenced first */
+			st->data[i++] = buffer[2 * bit + offset + 3];
+			st->data[i++] = buffer[2 * bit + offset + 2];
+			break;
+		}
+	}
+
+	iio_push_to_buffers_with_timestamp(indio_dev, st->data, pf->timestamp);
+irq_done:
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
 static const struct iio_info adis16480_info = {
 	.read_raw = &adis16480_read_raw,
 	.write_raw = &adis16480_write_raw,
@@ -1341,7 +1476,7 @@  static int adis16480_probe(struct spi_device *spi)
 		st->clk_freq = st->chip_info->int_clk;
 	}
 
-	ret = devm_adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL);
+	ret = devm_adis_setup_buffer_and_trigger(&st->adis, indio_dev, adis16480_trigger_handler);
 	if (ret)
 		return ret;