Skip to content

Commit

Permalink
iio: imu: inv_icm42600: add accurate timestamping
Browse files Browse the repository at this point in the history
Add a timestamping mechanism for buffer that provides accurate
event timestamps when using watermark. This mechanism estimates
device internal clock by comparing FIFO interrupts delta time and
device elapsed time computed by parsing FIFO data.

Take interrupt timestamp in hard irq handler and add IIO device
specific timestamp structures in device private allocation.

Signed-off-by: Jean-Baptiste Maneyrol <jmaneyrol@invensense.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
  • Loading branch information
Jean-Baptiste Maneyrol authored and Jonathan Cameron committed Jun 27, 2020
1 parent 7f85e42 commit ec74ae9
Show file tree
Hide file tree
Showing 8 changed files with 396 additions and 15 deletions.
1 change: 1 addition & 0 deletions drivers/iio/imu/inv_icm42600/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ inv-icm42600-y += inv_icm42600_gyro.o
inv-icm42600-y += inv_icm42600_accel.o
inv-icm42600-y += inv_icm42600_temp.o
inv-icm42600-y += inv_icm42600_buffer.o
inv-icm42600-y += inv_icm42600_timestamp.o

obj-$(CONFIG_INV_ICM42600_I2C) += inv-icm42600-i2c.o
inv-icm42600-i2c-y += inv_icm42600_i2c.o
Expand Down
5 changes: 5 additions & 0 deletions drivers/iio/imu/inv_icm42600/inv_icm42600.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ struct inv_icm42600_suspended {
* @indio_accel: accelerometer IIO device.
* @buffer: data transfer buffer aligned for DMA.
* @fifo: FIFO management structure.
* @timestamp: interrupt timestamps.
*/
struct inv_icm42600_state {
struct mutex lock;
Expand All @@ -141,6 +142,10 @@ struct inv_icm42600_state {
struct iio_dev *indio_accel;
uint8_t buffer[2] ____cacheline_aligned;
struct inv_icm42600_fifo fifo;
struct {
int64_t gyro;
int64_t accel;
} timestamp;
};

/* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */
Expand Down
40 changes: 33 additions & 7 deletions drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "inv_icm42600.h"
#include "inv_icm42600_temp.h"
#include "inv_icm42600_buffer.h"
#include "inv_icm42600_timestamp.h"

#define INV_ICM42600_ACCEL_CHAN(_modifier, _index, _ext_info) \
{ \
Expand Down Expand Up @@ -50,6 +51,7 @@ enum inv_icm42600_accel_scan {
INV_ICM42600_ACCEL_SCAN_Y,
INV_ICM42600_ACCEL_SCAN_Z,
INV_ICM42600_ACCEL_SCAN_TEMP,
INV_ICM42600_ACCEL_SCAN_TIMESTAMP,
};

static const struct iio_chan_spec_ext_info inv_icm42600_accel_ext_infos[] = {
Expand All @@ -65,15 +67,17 @@ static const struct iio_chan_spec inv_icm42600_accel_channels[] = {
INV_ICM42600_ACCEL_CHAN(IIO_MOD_Z, INV_ICM42600_ACCEL_SCAN_Z,
inv_icm42600_accel_ext_infos),
INV_ICM42600_TEMP_CHAN(INV_ICM42600_ACCEL_SCAN_TEMP),
IIO_CHAN_SOFT_TIMESTAMP(INV_ICM42600_ACCEL_SCAN_TIMESTAMP),
};

/*
* IIO buffer data: size must be a power of 2
* 8 bytes: 6 bytes acceleration and 2 bytes temperature
* IIO buffer data: size must be a power of 2 and timestamp aligned
* 16 bytes: 6 bytes acceleration, 2 bytes temperature, 8 bytes timestamp
*/
struct inv_icm42600_accel_buffer {
struct inv_icm42600_fifo_sensor_data accel;
int16_t temp;
int64_t timestamp __aligned(8);
};

#define INV_ICM42600_SCAN_MASK_ACCEL_3AXIS \
Expand All @@ -94,6 +98,7 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
unsigned int fifo_en = 0;
unsigned int sleep_temp = 0;
Expand Down Expand Up @@ -121,6 +126,7 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev,
}

/* update data FIFO write */
inv_icm42600_timestamp_apply_odr(ts, 0, 0, 0);
ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
if (ret)
goto out_unlock;
Expand Down Expand Up @@ -301,9 +307,11 @@ static int inv_icm42600_accel_read_odr(struct inv_icm42600_state *st,
return IIO_VAL_INT_PLUS_MICRO;
}

static int inv_icm42600_accel_write_odr(struct inv_icm42600_state *st,
static int inv_icm42600_accel_write_odr(struct iio_dev *indio_dev,
int val, int val2)
{
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
struct device *dev = regmap_get_device(st->map);
unsigned int idx;
struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
Expand All @@ -322,6 +330,11 @@ static int inv_icm42600_accel_write_odr(struct inv_icm42600_state *st,
pm_runtime_get_sync(dev);
mutex_lock(&st->lock);

ret = inv_icm42600_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr),
iio_buffer_enabled(indio_dev));
if (ret)
goto out_unlock;

ret = inv_icm42600_set_accel_conf(st, &conf, NULL);
if (ret)
goto out_unlock;
Expand Down Expand Up @@ -611,7 +624,7 @@ static int inv_icm42600_accel_write_raw(struct iio_dev *indio_dev,
iio_device_release_direct_mode(indio_dev);
return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
return inv_icm42600_accel_write_odr(st, val, val2);
return inv_icm42600_accel_write_odr(indio_dev, val, val2);
case IIO_CHAN_INFO_CALIBBIAS:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
Expand Down Expand Up @@ -694,6 +707,7 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
{
struct device *dev = regmap_get_device(st->map);
const char *name;
struct inv_icm42600_timestamp *ts;
struct iio_dev *indio_dev;
struct iio_buffer *buffer;
int ret;
Expand All @@ -702,14 +716,17 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
if (!name)
return ERR_PTR(-ENOMEM);

indio_dev = devm_iio_device_alloc(dev, 0);
indio_dev = devm_iio_device_alloc(dev, sizeof(*ts));
if (!indio_dev)
return ERR_PTR(-ENOMEM);

buffer = devm_iio_kfifo_allocate(dev);
if (!buffer)
return ERR_PTR(-ENOMEM);

ts = iio_priv(indio_dev);
inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.accel.odr));

iio_device_set_drvdata(indio_dev, st);
indio_dev->name = name;
indio_dev->info = &inv_icm42600_accel_info;
Expand All @@ -731,14 +748,17 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev)
{
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
ssize_t i, size;
unsigned int no;
const void *accel, *gyro, *timestamp;
const int8_t *temp;
unsigned int odr;
int64_t ts_val;
struct inv_icm42600_accel_buffer buffer;

/* parse all fifo packets */
for (i = 0; i < st->fifo.count; i += size) {
for (i = 0, no = 0; i < st->fifo.count; i += size, ++no) {
size = inv_icm42600_fifo_decode_packet(&st->fifo.data[i],
&accel, &gyro, &temp, &timestamp, &odr);
/* quit if error or FIFO is empty */
Expand All @@ -749,12 +769,18 @@ int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev)
if (accel == NULL || !inv_icm42600_fifo_is_data_valid(accel))
continue;

/* update odr */
if (odr & INV_ICM42600_SENSOR_ACCEL)
inv_icm42600_timestamp_apply_odr(ts, st->fifo.period,
st->fifo.nb.total, no);

/* buffer is copied to userspace, zeroing it to avoid any data leak */
memset(&buffer, 0, sizeof(buffer));
memcpy(&buffer.accel, accel, sizeof(buffer.accel));
/* convert 8 bits FIFO temperature in high resolution format */
buffer.temp = temp ? (*temp * 64) : 0;
iio_push_to_buffers(indio_dev, &buffer);
ts_val = inv_icm42600_timestamp_pop(ts);
iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val);
}

return 0;
Expand Down
28 changes: 28 additions & 0 deletions drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <linux/iio/buffer.h>

#include "inv_icm42600.h"
#include "inv_icm42600_timestamp.h"
#include "inv_icm42600_buffer.h"

/* FIFO header: 1 byte */
Expand Down Expand Up @@ -374,6 +375,7 @@ static int inv_icm42600_buffer_postdisable(struct iio_dev *indio_dev)
struct device *dev = regmap_get_device(st->map);
unsigned int sensor;
unsigned int *watermark;
struct inv_icm42600_timestamp *ts;
struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
unsigned int sleep_temp = 0;
unsigned int sleep_sensor = 0;
Expand All @@ -383,9 +385,11 @@ static int inv_icm42600_buffer_postdisable(struct iio_dev *indio_dev)
if (indio_dev == st->indio_gyro) {
sensor = INV_ICM42600_SENSOR_GYRO;
watermark = &st->fifo.watermark.gyro;
ts = iio_priv(st->indio_gyro);
} else if (indio_dev == st->indio_accel) {
sensor = INV_ICM42600_SENSOR_ACCEL;
watermark = &st->fifo.watermark.accel;
ts = iio_priv(st->indio_accel);
} else {
return -EINVAL;
}
Expand Down Expand Up @@ -413,6 +417,8 @@ static int inv_icm42600_buffer_postdisable(struct iio_dev *indio_dev)
if (!st->fifo.on)
ret = inv_icm42600_set_temp_conf(st, false, &sleep_temp);

inv_icm42600_timestamp_reset(ts);

out_unlock:
mutex_unlock(&st->lock);

Expand Down Expand Up @@ -498,17 +504,26 @@ int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st,

int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st)
{
struct inv_icm42600_timestamp *ts;
int ret;

if (st->fifo.nb.total == 0)
return 0;

/* handle gyroscope timestamp and FIFO data parsing */
ts = iio_priv(st->indio_gyro);
inv_icm42600_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
st->fifo.nb.gyro, st->timestamp.gyro);
if (st->fifo.nb.gyro > 0) {
ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);
if (ret)
return ret;
}

/* handle accelerometer timestamp and FIFO data parsing */
ts = iio_priv(st->indio_accel);
inv_icm42600_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
st->fifo.nb.accel, st->timestamp.accel);
if (st->fifo.nb.accel > 0) {
ret = inv_icm42600_accel_parse_fifo(st->indio_accel);
if (ret)
Expand All @@ -521,8 +536,13 @@ int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st)
int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st,
unsigned int count)
{
struct inv_icm42600_timestamp *ts;
int64_t gyro_ts, accel_ts;
int ret;

gyro_ts = iio_get_time_ns(st->indio_gyro);
accel_ts = iio_get_time_ns(st->indio_accel);

ret = inv_icm42600_buffer_fifo_read(st, count);
if (ret)
return ret;
Expand All @@ -531,12 +551,20 @@ int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st,
return 0;

if (st->fifo.nb.gyro > 0) {
ts = iio_priv(st->indio_gyro);
inv_icm42600_timestamp_interrupt(ts, st->fifo.period,
st->fifo.nb.total, st->fifo.nb.gyro,
gyro_ts);
ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);
if (ret)
return ret;
}

if (st->fifo.nb.accel > 0) {
ts = iio_priv(st->indio_accel);
inv_icm42600_timestamp_interrupt(ts, st->fifo.period,
st->fifo.nb.total, st->fifo.nb.accel,
accel_ts);
ret = inv_icm42600_accel_parse_fifo(st->indio_accel);
if (ret)
return ret;
Expand Down
17 changes: 16 additions & 1 deletion drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include "inv_icm42600.h"
#include "inv_icm42600_buffer.h"
#include "inv_icm42600_timestamp.h"

static const struct regmap_range_cfg inv_icm42600_regmap_ranges[] = {
{
Expand Down Expand Up @@ -412,6 +413,16 @@ static int inv_icm42600_setup(struct inv_icm42600_state *st,
return inv_icm42600_set_conf(st, hw->conf);
}

static irqreturn_t inv_icm42600_irq_timestamp(int irq, void *_data)
{
struct inv_icm42600_state *st = _data;

st->timestamp.gyro = iio_get_time_ns(st->indio_gyro);
st->timestamp.accel = iio_get_time_ns(st->indio_accel);

return IRQ_WAKE_THREAD;
}

static irqreturn_t inv_icm42600_irq_handler(int irq, void *_data)
{
struct inv_icm42600_state *st = _data;
Expand Down Expand Up @@ -495,7 +506,7 @@ static int inv_icm42600_irq_init(struct inv_icm42600_state *st, int irq,
if (ret)
return ret;

return devm_request_threaded_irq(dev, irq, NULL,
return devm_request_threaded_irq(dev, irq, inv_icm42600_irq_timestamp,
inv_icm42600_irq_handler, irq_type,
"inv_icm42600", st);
}
Expand Down Expand Up @@ -617,6 +628,10 @@ int inv_icm42600_core_probe(struct regmap *regmap, int chip, int irq,
if (ret)
return ret;

ret = inv_icm42600_timestamp_setup(st);
if (ret)
return ret;

ret = inv_icm42600_buffer_init(st);
if (ret)
return ret;
Expand Down
Loading

0 comments on commit ec74ae9

Please sign in to comment.