iio: gyro: adxrs290: Add triggered buffer support

Provide a way for continuous data capture by setting up buffer support. The
data ready signal exposed at the SYNC pin of the ADXRS290 is exploited as
a hardware interrupt which triggers to fill the buffer.

Triggered buffer setup was tested with both hardware trigger (DATA_RDY) and
software triggers (sysfs-trig & hrtimer).

Signed-off-by: Nishant Malpani <nish.malpani25@gmail.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/20200910180450.29696-2-nish.malpani25@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
Nishant Malpani 2020-09-10 23:34:48 +05:30 committed by Jonathan Cameron
parent 5ac65da27e
commit 672555807f
2 changed files with 199 additions and 14 deletions

View File

@ -44,6 +44,8 @@ config ADIS16260
config ADXRS290 config ADXRS290
tristate "Analog Devices ADXRS290 Dual-Axis MEMS Gyroscope SPI driver" tristate "Analog Devices ADXRS290 Dual-Axis MEMS Gyroscope SPI driver"
depends on SPI depends on SPI
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help help
Say yes here to build support for Analog Devices ADXRS290 programmable Say yes here to build support for Analog Devices ADXRS290 programmable
digital output gyroscope. digital output gyroscope.

View File

@ -13,8 +13,12 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h> #include <linux/iio/iio.h>
#include <linux/iio/sysfs.h> #include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#define ADXRS290_ADI_ID 0xAD #define ADXRS290_ADI_ID 0xAD
#define ADXRS290_MEMS_ID 0x1D #define ADXRS290_MEMS_ID 0x1D
@ -35,7 +39,9 @@
#define ADXRS290_READ BIT(7) #define ADXRS290_READ BIT(7)
#define ADXRS290_TSM BIT(0) #define ADXRS290_TSM BIT(0)
#define ADXRS290_MEASUREMENT BIT(1) #define ADXRS290_MEASUREMENT BIT(1)
#define ADXRS290_SYNC GENMASK(1, 0) #define ADXRS290_DATA_RDY_OUT BIT(0)
#define ADXRS290_SYNC_MASK GENMASK(1, 0)
#define ADXRS290_SYNC(x) FIELD_PREP(ADXRS290_SYNC_MASK, x)
#define ADXRS290_LPF_MASK GENMASK(2, 0) #define ADXRS290_LPF_MASK GENMASK(2, 0)
#define ADXRS290_LPF(x) FIELD_PREP(ADXRS290_LPF_MASK, x) #define ADXRS290_LPF(x) FIELD_PREP(ADXRS290_LPF_MASK, x)
#define ADXRS290_HPF_MASK GENMASK(7, 4) #define ADXRS290_HPF_MASK GENMASK(7, 4)
@ -50,6 +56,13 @@ enum adxrs290_mode {
ADXRS290_MODE_MEASUREMENT, ADXRS290_MODE_MEASUREMENT,
}; };
enum adxrs290_scan_index {
ADXRS290_IDX_X,
ADXRS290_IDX_Y,
ADXRS290_IDX_TEMP,
ADXRS290_IDX_TS,
};
struct adxrs290_state { struct adxrs290_state {
struct spi_device *spi; struct spi_device *spi;
/* Serialize reads and their subsequent processing */ /* Serialize reads and their subsequent processing */
@ -57,6 +70,12 @@ struct adxrs290_state {
enum adxrs290_mode mode; enum adxrs290_mode mode;
unsigned int lpf_3db_freq_idx; unsigned int lpf_3db_freq_idx;
unsigned int hpf_3db_freq_idx; unsigned int hpf_3db_freq_idx;
struct iio_trigger *dready_trig;
/* Ensure correct alignment of timestamp when present */
struct {
s16 channels[3];
s64 ts __aligned(8);
} buffer;
}; };
/* /*
@ -270,24 +289,34 @@ static int adxrs290_read_raw(struct iio_dev *indio_dev,
switch (mask) { switch (mask) {
case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
switch (chan->type) { switch (chan->type) {
case IIO_ANGL_VEL: case IIO_ANGL_VEL:
ret = adxrs290_get_rate_data(indio_dev, ret = adxrs290_get_rate_data(indio_dev,
ADXRS290_READ_REG(chan->address), ADXRS290_READ_REG(chan->address),
val); val);
if (ret < 0) if (ret < 0)
return ret; break;
return IIO_VAL_INT; ret = IIO_VAL_INT;
break;
case IIO_TEMP: case IIO_TEMP:
ret = adxrs290_get_temp_data(indio_dev, val); ret = adxrs290_get_temp_data(indio_dev, val);
if (ret < 0) if (ret < 0)
return ret; break;
return IIO_VAL_INT; ret = IIO_VAL_INT;
break;
default: default:
return -EINVAL; ret = -EINVAL;
break;
} }
iio_device_release_direct_mode(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE: case IIO_CHAN_INFO_SCALE:
switch (chan->type) { switch (chan->type) {
case IIO_ANGL_VEL: case IIO_ANGL_VEL:
@ -334,34 +363,52 @@ static int adxrs290_write_raw(struct iio_dev *indio_dev,
long mask) long mask)
{ {
struct adxrs290_state *st = iio_priv(indio_dev); struct adxrs290_state *st = iio_priv(indio_dev);
int lpf_idx, hpf_idx; int ret, lpf_idx, hpf_idx;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
switch (mask) { switch (mask) {
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
lpf_idx = adxrs290_find_match(adxrs290_lpf_3db_freq_hz_table, lpf_idx = adxrs290_find_match(adxrs290_lpf_3db_freq_hz_table,
ARRAY_SIZE(adxrs290_lpf_3db_freq_hz_table), ARRAY_SIZE(adxrs290_lpf_3db_freq_hz_table),
val, val2); val, val2);
if (lpf_idx < 0) if (lpf_idx < 0) {
return -EINVAL; ret = -EINVAL;
break;
}
/* caching the updated state of the low-pass filter */ /* caching the updated state of the low-pass filter */
st->lpf_3db_freq_idx = lpf_idx; st->lpf_3db_freq_idx = lpf_idx;
/* retrieving the current state of the high-pass filter */ /* retrieving the current state of the high-pass filter */
hpf_idx = st->hpf_3db_freq_idx; hpf_idx = st->hpf_3db_freq_idx;
return adxrs290_set_filter_freq(indio_dev, lpf_idx, hpf_idx); ret = adxrs290_set_filter_freq(indio_dev, lpf_idx, hpf_idx);
break;
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
hpf_idx = adxrs290_find_match(adxrs290_hpf_3db_freq_hz_table, hpf_idx = adxrs290_find_match(adxrs290_hpf_3db_freq_hz_table,
ARRAY_SIZE(adxrs290_hpf_3db_freq_hz_table), ARRAY_SIZE(adxrs290_hpf_3db_freq_hz_table),
val, val2); val, val2);
if (hpf_idx < 0) if (hpf_idx < 0) {
return -EINVAL; ret = -EINVAL;
break;
}
/* caching the updated state of the high-pass filter */ /* caching the updated state of the high-pass filter */
st->hpf_3db_freq_idx = hpf_idx; st->hpf_3db_freq_idx = hpf_idx;
/* retrieving the current state of the low-pass filter */ /* retrieving the current state of the low-pass filter */
lpf_idx = st->lpf_3db_freq_idx; lpf_idx = st->lpf_3db_freq_idx;
return adxrs290_set_filter_freq(indio_dev, lpf_idx, hpf_idx); ret = adxrs290_set_filter_freq(indio_dev, lpf_idx, hpf_idx);
break;
default:
ret = -EINVAL;
break;
} }
return -EINVAL; iio_device_release_direct_mode(indio_dev);
return ret;
} }
static int adxrs290_read_avail(struct iio_dev *indio_dev, static int adxrs290_read_avail(struct iio_dev *indio_dev,
@ -389,6 +436,72 @@ static int adxrs290_read_avail(struct iio_dev *indio_dev,
} }
} }
static int adxrs290_data_rdy_trigger_set_state(struct iio_trigger *trig,
bool state)
{
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
struct adxrs290_state *st = iio_priv(indio_dev);
int ret;
u8 val;
val = state ? ADXRS290_SYNC(ADXRS290_DATA_RDY_OUT) : 0;
ret = adxrs290_spi_write_reg(st->spi, ADXRS290_REG_DATA_RDY, val);
if (ret < 0)
dev_err(&st->spi->dev, "failed to start data rdy interrupt\n");
return ret;
}
static int adxrs290_reset_trig(struct iio_trigger *trig)
{
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
int val;
/*
* Data ready interrupt is reset after a read of the data registers.
* Here, we only read the 16b DATAY registers as that marks the end of
* a read of the data registers and initiates a reset for the interrupt
* line.
*/
adxrs290_get_rate_data(indio_dev,
ADXRS290_READ_REG(ADXRS290_REG_DATAY0), &val);
return 0;
}
static const struct iio_trigger_ops adxrs290_trigger_ops = {
.set_trigger_state = &adxrs290_data_rdy_trigger_set_state,
.validate_device = &iio_trigger_validate_own_device,
.try_reenable = &adxrs290_reset_trig,
};
static irqreturn_t adxrs290_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct adxrs290_state *st = iio_priv(indio_dev);
u8 tx = ADXRS290_READ_REG(ADXRS290_REG_DATAX0);
int ret;
mutex_lock(&st->lock);
/* exercise a bulk data capture starting from reg DATAX0... */
ret = spi_write_then_read(st->spi, &tx, sizeof(tx), st->buffer.channels,
sizeof(st->buffer.channels));
if (ret < 0)
goto out_unlock_notify;
iio_push_to_buffers_with_timestamp(indio_dev, &st->buffer,
pf->timestamp);
out_unlock_notify:
mutex_unlock(&st->lock);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
#define ADXRS290_ANGL_VEL_CHANNEL(reg, axis) { \ #define ADXRS290_ANGL_VEL_CHANNEL(reg, axis) { \
.type = IIO_ANGL_VEL, \ .type = IIO_ANGL_VEL, \
.address = reg, \ .address = reg, \
@ -401,6 +514,13 @@ static int adxrs290_read_avail(struct iio_dev *indio_dev,
.info_mask_shared_by_type_available = \ .info_mask_shared_by_type_available = \
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \
BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \
.scan_index = ADXRS290_IDX_##axis, \
.scan_type = { \
.sign = 's', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_LE, \
}, \
} }
static const struct iio_chan_spec adxrs290_channels[] = { static const struct iio_chan_spec adxrs290_channels[] = {
@ -411,7 +531,20 @@ static const struct iio_chan_spec adxrs290_channels[] = {
.address = ADXRS290_REG_TEMP0, .address = ADXRS290_REG_TEMP0,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE), BIT(IIO_CHAN_INFO_SCALE),
.scan_index = ADXRS290_IDX_TEMP,
.scan_type = {
.sign = 's',
.realbits = 12,
.storagebits = 16,
.endianness = IIO_LE,
},
}, },
IIO_CHAN_SOFT_TIMESTAMP(ADXRS290_IDX_TS),
};
static const unsigned long adxrs290_avail_scan_masks[] = {
BIT(ADXRS290_IDX_X) | BIT(ADXRS290_IDX_Y) | BIT(ADXRS290_IDX_TEMP),
0
}; };
static const struct iio_info adxrs290_info = { static const struct iio_info adxrs290_info = {
@ -420,6 +553,44 @@ static const struct iio_info adxrs290_info = {
.read_avail = &adxrs290_read_avail, .read_avail = &adxrs290_read_avail,
}; };
static int adxrs290_probe_trigger(struct iio_dev *indio_dev)
{
struct adxrs290_state *st = iio_priv(indio_dev);
int ret;
if (!st->spi->irq) {
dev_info(&st->spi->dev, "no irq, using polling\n");
return 0;
}
st->dready_trig = devm_iio_trigger_alloc(&st->spi->dev, "%s-dev%d",
indio_dev->name,
indio_dev->id);
if (!st->dready_trig)
return -ENOMEM;
st->dready_trig->dev.parent = &st->spi->dev;
st->dready_trig->ops = &adxrs290_trigger_ops;
iio_trigger_set_drvdata(st->dready_trig, indio_dev);
ret = devm_request_irq(&st->spi->dev, st->spi->irq,
&iio_trigger_generic_data_rdy_poll,
IRQF_ONESHOT, "adxrs290_irq", st->dready_trig);
if (ret < 0)
return dev_err_probe(&st->spi->dev, ret,
"request irq %d failed\n", st->spi->irq);
ret = devm_iio_trigger_register(&st->spi->dev, st->dready_trig);
if (ret) {
dev_err(&st->spi->dev, "iio trigger register failed\n");
return ret;
}
indio_dev->trig = iio_trigger_get(st->dready_trig);
return 0;
}
static int adxrs290_probe(struct spi_device *spi) static int adxrs290_probe(struct spi_device *spi)
{ {
struct iio_dev *indio_dev; struct iio_dev *indio_dev;
@ -439,6 +610,7 @@ static int adxrs290_probe(struct spi_device *spi)
indio_dev->channels = adxrs290_channels; indio_dev->channels = adxrs290_channels;
indio_dev->num_channels = ARRAY_SIZE(adxrs290_channels); indio_dev->num_channels = ARRAY_SIZE(adxrs290_channels);
indio_dev->info = &adxrs290_info; indio_dev->info = &adxrs290_info;
indio_dev->available_scan_masks = adxrs290_avail_scan_masks;
mutex_init(&st->lock); mutex_init(&st->lock);
@ -478,6 +650,17 @@ static int adxrs290_probe(struct spi_device *spi)
st->lpf_3db_freq_idx = val; st->lpf_3db_freq_idx = val;
st->hpf_3db_freq_idx = val2; st->hpf_3db_freq_idx = val2;
ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
&iio_pollfunc_store_time,
&adxrs290_trigger_handler, NULL);
if (ret < 0)
return dev_err_probe(&spi->dev, ret,
"iio triggered buffer setup failed\n");
ret = adxrs290_probe_trigger(indio_dev);
if (ret < 0)
return ret;
return devm_iio_device_register(&spi->dev, indio_dev); return devm_iio_device_register(&spi->dev, indio_dev);
} }