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>
799 lines
21 KiB
C
799 lines
21 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 2020 Invensense, Inc.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/device.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/math64.h>
|
|
#include <linux/iio/iio.h>
|
|
#include <linux/iio/buffer.h>
|
|
#include <linux/iio/kfifo_buf.h>
|
|
|
|
#include "inv_icm42600.h"
|
|
#include "inv_icm42600_temp.h"
|
|
#include "inv_icm42600_buffer.h"
|
|
#include "inv_icm42600_timestamp.h"
|
|
|
|
#define INV_ICM42600_GYRO_CHAN(_modifier, _index, _ext_info) \
|
|
{ \
|
|
.type = IIO_ANGL_VEL, \
|
|
.modified = 1, \
|
|
.channel2 = _modifier, \
|
|
.info_mask_separate = \
|
|
BIT(IIO_CHAN_INFO_RAW) | \
|
|
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
|
.info_mask_shared_by_type = \
|
|
BIT(IIO_CHAN_INFO_SCALE), \
|
|
.info_mask_shared_by_type_available = \
|
|
BIT(IIO_CHAN_INFO_SCALE) | \
|
|
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
|
.info_mask_shared_by_all = \
|
|
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
|
.info_mask_shared_by_all_available = \
|
|
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
|
.scan_index = _index, \
|
|
.scan_type = { \
|
|
.sign = 's', \
|
|
.realbits = 16, \
|
|
.storagebits = 16, \
|
|
.endianness = IIO_BE, \
|
|
}, \
|
|
.ext_info = _ext_info, \
|
|
}
|
|
|
|
enum inv_icm42600_gyro_scan {
|
|
INV_ICM42600_GYRO_SCAN_X,
|
|
INV_ICM42600_GYRO_SCAN_Y,
|
|
INV_ICM42600_GYRO_SCAN_Z,
|
|
INV_ICM42600_GYRO_SCAN_TEMP,
|
|
INV_ICM42600_GYRO_SCAN_TIMESTAMP,
|
|
};
|
|
|
|
static const struct iio_chan_spec_ext_info inv_icm42600_gyro_ext_infos[] = {
|
|
IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42600_get_mount_matrix),
|
|
{},
|
|
};
|
|
|
|
static const struct iio_chan_spec inv_icm42600_gyro_channels[] = {
|
|
INV_ICM42600_GYRO_CHAN(IIO_MOD_X, INV_ICM42600_GYRO_SCAN_X,
|
|
inv_icm42600_gyro_ext_infos),
|
|
INV_ICM42600_GYRO_CHAN(IIO_MOD_Y, INV_ICM42600_GYRO_SCAN_Y,
|
|
inv_icm42600_gyro_ext_infos),
|
|
INV_ICM42600_GYRO_CHAN(IIO_MOD_Z, INV_ICM42600_GYRO_SCAN_Z,
|
|
inv_icm42600_gyro_ext_infos),
|
|
INV_ICM42600_TEMP_CHAN(INV_ICM42600_GYRO_SCAN_TEMP),
|
|
IIO_CHAN_SOFT_TIMESTAMP(INV_ICM42600_GYRO_SCAN_TIMESTAMP),
|
|
};
|
|
|
|
/*
|
|
* IIO buffer data: size must be a power of 2 and timestamp aligned
|
|
* 16 bytes: 6 bytes angular velocity, 2 bytes temperature, 8 bytes timestamp
|
|
*/
|
|
struct inv_icm42600_gyro_buffer {
|
|
struct inv_icm42600_fifo_sensor_data gyro;
|
|
int16_t temp;
|
|
int64_t timestamp __aligned(8);
|
|
};
|
|
|
|
#define INV_ICM42600_SCAN_MASK_GYRO_3AXIS \
|
|
(BIT(INV_ICM42600_GYRO_SCAN_X) | \
|
|
BIT(INV_ICM42600_GYRO_SCAN_Y) | \
|
|
BIT(INV_ICM42600_GYRO_SCAN_Z))
|
|
|
|
#define INV_ICM42600_SCAN_MASK_TEMP BIT(INV_ICM42600_GYRO_SCAN_TEMP)
|
|
|
|
static const unsigned long inv_icm42600_gyro_scan_masks[] = {
|
|
/* 3-axis gyro + temperature */
|
|
INV_ICM42600_SCAN_MASK_GYRO_3AXIS | INV_ICM42600_SCAN_MASK_TEMP,
|
|
0,
|
|
};
|
|
|
|
/* enable gyroscope sensor and FIFO write */
|
|
static int inv_icm42600_gyro_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_gyro = 0;
|
|
unsigned int sleep_temp = 0;
|
|
unsigned int sleep;
|
|
int ret;
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
if (*scan_mask & INV_ICM42600_SCAN_MASK_TEMP) {
|
|
/* enable temp sensor */
|
|
ret = inv_icm42600_set_temp_conf(st, true, &sleep_temp);
|
|
if (ret)
|
|
goto out_unlock;
|
|
fifo_en |= INV_ICM42600_SENSOR_TEMP;
|
|
}
|
|
|
|
if (*scan_mask & INV_ICM42600_SCAN_MASK_GYRO_3AXIS) {
|
|
/* enable gyro sensor */
|
|
conf.mode = INV_ICM42600_SENSOR_MODE_LOW_NOISE;
|
|
ret = inv_icm42600_set_gyro_conf(st, &conf, &sleep_gyro);
|
|
if (ret)
|
|
goto out_unlock;
|
|
fifo_en |= INV_ICM42600_SENSOR_GYRO;
|
|
}
|
|
|
|
/* 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;
|
|
|
|
ret = inv_icm42600_buffer_update_watermark(st);
|
|
|
|
out_unlock:
|
|
mutex_unlock(&st->lock);
|
|
/* sleep maximum required time */
|
|
if (sleep_gyro > sleep_temp)
|
|
sleep = sleep_gyro;
|
|
else
|
|
sleep = sleep_temp;
|
|
if (sleep)
|
|
msleep(sleep);
|
|
return ret;
|
|
}
|
|
|
|
static int inv_icm42600_gyro_read_sensor(struct inv_icm42600_state *st,
|
|
struct iio_chan_spec const *chan,
|
|
int16_t *val)
|
|
{
|
|
struct device *dev = regmap_get_device(st->map);
|
|
struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
|
|
unsigned int reg;
|
|
__be16 *data;
|
|
int ret;
|
|
|
|
if (chan->type != IIO_ANGL_VEL)
|
|
return -EINVAL;
|
|
|
|
switch (chan->channel2) {
|
|
case IIO_MOD_X:
|
|
reg = INV_ICM42600_REG_GYRO_DATA_X;
|
|
break;
|
|
case IIO_MOD_Y:
|
|
reg = INV_ICM42600_REG_GYRO_DATA_Y;
|
|
break;
|
|
case IIO_MOD_Z:
|
|
reg = INV_ICM42600_REG_GYRO_DATA_Z;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
pm_runtime_get_sync(dev);
|
|
mutex_lock(&st->lock);
|
|
|
|
/* enable gyro sensor */
|
|
conf.mode = INV_ICM42600_SENSOR_MODE_LOW_NOISE;
|
|
ret = inv_icm42600_set_gyro_conf(st, &conf, NULL);
|
|
if (ret)
|
|
goto exit;
|
|
|
|
/* read gyro register data */
|
|
data = (__be16 *)&st->buffer[0];
|
|
ret = regmap_bulk_read(st->map, reg, data, sizeof(*data));
|
|
if (ret)
|
|
goto exit;
|
|
|
|
*val = (int16_t)be16_to_cpup(data);
|
|
if (*val == INV_ICM42600_DATA_INVALID)
|
|
ret = -EINVAL;
|
|
exit:
|
|
mutex_unlock(&st->lock);
|
|
pm_runtime_mark_last_busy(dev);
|
|
pm_runtime_put_autosuspend(dev);
|
|
return ret;
|
|
}
|
|
|
|
/* IIO format int + nano */
|
|
static const int inv_icm42600_gyro_scale[] = {
|
|
/* +/- 2000dps => 0.001065264 rad/s */
|
|
[2 * INV_ICM42600_GYRO_FS_2000DPS] = 0,
|
|
[2 * INV_ICM42600_GYRO_FS_2000DPS + 1] = 1065264,
|
|
/* +/- 1000dps => 0.000532632 rad/s */
|
|
[2 * INV_ICM42600_GYRO_FS_1000DPS] = 0,
|
|
[2 * INV_ICM42600_GYRO_FS_1000DPS + 1] = 532632,
|
|
/* +/- 500dps => 0.000266316 rad/s */
|
|
[2 * INV_ICM42600_GYRO_FS_500DPS] = 0,
|
|
[2 * INV_ICM42600_GYRO_FS_500DPS + 1] = 266316,
|
|
/* +/- 250dps => 0.000133158 rad/s */
|
|
[2 * INV_ICM42600_GYRO_FS_250DPS] = 0,
|
|
[2 * INV_ICM42600_GYRO_FS_250DPS + 1] = 133158,
|
|
/* +/- 125dps => 0.000066579 rad/s */
|
|
[2 * INV_ICM42600_GYRO_FS_125DPS] = 0,
|
|
[2 * INV_ICM42600_GYRO_FS_125DPS + 1] = 66579,
|
|
/* +/- 62.5dps => 0.000033290 rad/s */
|
|
[2 * INV_ICM42600_GYRO_FS_62_5DPS] = 0,
|
|
[2 * INV_ICM42600_GYRO_FS_62_5DPS + 1] = 33290,
|
|
/* +/- 31.25dps => 0.000016645 rad/s */
|
|
[2 * INV_ICM42600_GYRO_FS_31_25DPS] = 0,
|
|
[2 * INV_ICM42600_GYRO_FS_31_25DPS + 1] = 16645,
|
|
/* +/- 15.625dps => 0.000008322 rad/s */
|
|
[2 * INV_ICM42600_GYRO_FS_15_625DPS] = 0,
|
|
[2 * INV_ICM42600_GYRO_FS_15_625DPS + 1] = 8322,
|
|
};
|
|
|
|
static int inv_icm42600_gyro_read_scale(struct inv_icm42600_state *st,
|
|
int *val, int *val2)
|
|
{
|
|
unsigned int idx;
|
|
|
|
idx = st->conf.gyro.fs;
|
|
|
|
*val = inv_icm42600_gyro_scale[2 * idx];
|
|
*val2 = inv_icm42600_gyro_scale[2 * idx + 1];
|
|
return IIO_VAL_INT_PLUS_NANO;
|
|
}
|
|
|
|
static int inv_icm42600_gyro_write_scale(struct inv_icm42600_state *st,
|
|
int val, int val2)
|
|
{
|
|
struct device *dev = regmap_get_device(st->map);
|
|
unsigned int idx;
|
|
struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
|
|
int ret;
|
|
|
|
for (idx = 0; idx < ARRAY_SIZE(inv_icm42600_gyro_scale); idx += 2) {
|
|
if (val == inv_icm42600_gyro_scale[idx] &&
|
|
val2 == inv_icm42600_gyro_scale[idx + 1])
|
|
break;
|
|
}
|
|
if (idx >= ARRAY_SIZE(inv_icm42600_gyro_scale))
|
|
return -EINVAL;
|
|
|
|
conf.fs = idx / 2;
|
|
|
|
pm_runtime_get_sync(dev);
|
|
mutex_lock(&st->lock);
|
|
|
|
ret = inv_icm42600_set_gyro_conf(st, &conf, NULL);
|
|
|
|
mutex_unlock(&st->lock);
|
|
pm_runtime_mark_last_busy(dev);
|
|
pm_runtime_put_autosuspend(dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* IIO format int + micro */
|
|
static const int inv_icm42600_gyro_odr[] = {
|
|
/* 12.5Hz */
|
|
12, 500000,
|
|
/* 25Hz */
|
|
25, 0,
|
|
/* 50Hz */
|
|
50, 0,
|
|
/* 100Hz */
|
|
100, 0,
|
|
/* 200Hz */
|
|
200, 0,
|
|
/* 1kHz */
|
|
1000, 0,
|
|
/* 2kHz */
|
|
2000, 0,
|
|
/* 4kHz */
|
|
4000, 0,
|
|
};
|
|
|
|
static const int inv_icm42600_gyro_odr_conv[] = {
|
|
INV_ICM42600_ODR_12_5HZ,
|
|
INV_ICM42600_ODR_25HZ,
|
|
INV_ICM42600_ODR_50HZ,
|
|
INV_ICM42600_ODR_100HZ,
|
|
INV_ICM42600_ODR_200HZ,
|
|
INV_ICM42600_ODR_1KHZ_LN,
|
|
INV_ICM42600_ODR_2KHZ_LN,
|
|
INV_ICM42600_ODR_4KHZ_LN,
|
|
};
|
|
|
|
static int inv_icm42600_gyro_read_odr(struct inv_icm42600_state *st,
|
|
int *val, int *val2)
|
|
{
|
|
unsigned int odr;
|
|
unsigned int i;
|
|
|
|
odr = st->conf.gyro.odr;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(inv_icm42600_gyro_odr_conv); ++i) {
|
|
if (inv_icm42600_gyro_odr_conv[i] == odr)
|
|
break;
|
|
}
|
|
if (i >= ARRAY_SIZE(inv_icm42600_gyro_odr_conv))
|
|
return -EINVAL;
|
|
|
|
*val = inv_icm42600_gyro_odr[2 * i];
|
|
*val2 = inv_icm42600_gyro_odr[2 * i + 1];
|
|
|
|
return IIO_VAL_INT_PLUS_MICRO;
|
|
}
|
|
|
|
static int inv_icm42600_gyro_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;
|
|
int ret;
|
|
|
|
for (idx = 0; idx < ARRAY_SIZE(inv_icm42600_gyro_odr); idx += 2) {
|
|
if (val == inv_icm42600_gyro_odr[idx] &&
|
|
val2 == inv_icm42600_gyro_odr[idx + 1])
|
|
break;
|
|
}
|
|
if (idx >= ARRAY_SIZE(inv_icm42600_gyro_odr))
|
|
return -EINVAL;
|
|
|
|
conf.odr = inv_icm42600_gyro_odr_conv[idx / 2];
|
|
|
|
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_gyro_conf(st, &conf, NULL);
|
|
if (ret)
|
|
goto out_unlock;
|
|
inv_icm42600_buffer_update_fifo_period(st);
|
|
inv_icm42600_buffer_update_watermark(st);
|
|
|
|
out_unlock:
|
|
mutex_unlock(&st->lock);
|
|
pm_runtime_mark_last_busy(dev);
|
|
pm_runtime_put_autosuspend(dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Calibration bias values, IIO range format int + nano.
|
|
* Value is limited to +/-64dps coded on 12 bits signed. Step is 1/32 dps.
|
|
*/
|
|
static int inv_icm42600_gyro_calibbias[] = {
|
|
-1, 117010721, /* min: -1.117010721 rad/s */
|
|
0, 545415, /* step: 0.000545415 rad/s */
|
|
1, 116465306, /* max: 1.116465306 rad/s */
|
|
};
|
|
|
|
static int inv_icm42600_gyro_read_offset(struct inv_icm42600_state *st,
|
|
struct iio_chan_spec const *chan,
|
|
int *val, int *val2)
|
|
{
|
|
struct device *dev = regmap_get_device(st->map);
|
|
int64_t val64;
|
|
int32_t bias;
|
|
unsigned int reg;
|
|
int16_t offset;
|
|
uint8_t data[2];
|
|
int ret;
|
|
|
|
if (chan->type != IIO_ANGL_VEL)
|
|
return -EINVAL;
|
|
|
|
switch (chan->channel2) {
|
|
case IIO_MOD_X:
|
|
reg = INV_ICM42600_REG_OFFSET_USER0;
|
|
break;
|
|
case IIO_MOD_Y:
|
|
reg = INV_ICM42600_REG_OFFSET_USER1;
|
|
break;
|
|
case IIO_MOD_Z:
|
|
reg = INV_ICM42600_REG_OFFSET_USER3;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
pm_runtime_get_sync(dev);
|
|
mutex_lock(&st->lock);
|
|
|
|
ret = regmap_bulk_read(st->map, reg, st->buffer, sizeof(data));
|
|
memcpy(data, st->buffer, sizeof(data));
|
|
|
|
mutex_unlock(&st->lock);
|
|
pm_runtime_mark_last_busy(dev);
|
|
pm_runtime_put_autosuspend(dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* 12 bits signed value */
|
|
switch (chan->channel2) {
|
|
case IIO_MOD_X:
|
|
offset = sign_extend32(((data[1] & 0x0F) << 8) | data[0], 11);
|
|
break;
|
|
case IIO_MOD_Y:
|
|
offset = sign_extend32(((data[0] & 0xF0) << 4) | data[1], 11);
|
|
break;
|
|
case IIO_MOD_Z:
|
|
offset = sign_extend32(((data[1] & 0x0F) << 8) | data[0], 11);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* convert raw offset to dps then to rad/s
|
|
* 12 bits signed raw max 64 to dps: 64 / 2048
|
|
* dps to rad: Pi / 180
|
|
* result in nano (1000000000)
|
|
* (offset * 64 * Pi * 1000000000) / (2048 * 180)
|
|
*/
|
|
val64 = (int64_t)offset * 64LL * 3141592653LL;
|
|
/* for rounding, add + or - divisor (2048 * 180) divided by 2 */
|
|
if (val64 >= 0)
|
|
val64 += 2048 * 180 / 2;
|
|
else
|
|
val64 -= 2048 * 180 / 2;
|
|
bias = div_s64(val64, 2048 * 180);
|
|
*val = bias / 1000000000L;
|
|
*val2 = bias % 1000000000L;
|
|
|
|
return IIO_VAL_INT_PLUS_NANO;
|
|
}
|
|
|
|
static int inv_icm42600_gyro_write_offset(struct inv_icm42600_state *st,
|
|
struct iio_chan_spec const *chan,
|
|
int val, int val2)
|
|
{
|
|
struct device *dev = regmap_get_device(st->map);
|
|
int64_t val64, min, max;
|
|
unsigned int reg, regval;
|
|
int16_t offset;
|
|
int ret;
|
|
|
|
if (chan->type != IIO_ANGL_VEL)
|
|
return -EINVAL;
|
|
|
|
switch (chan->channel2) {
|
|
case IIO_MOD_X:
|
|
reg = INV_ICM42600_REG_OFFSET_USER0;
|
|
break;
|
|
case IIO_MOD_Y:
|
|
reg = INV_ICM42600_REG_OFFSET_USER1;
|
|
break;
|
|
case IIO_MOD_Z:
|
|
reg = INV_ICM42600_REG_OFFSET_USER3;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* inv_icm42600_gyro_calibbias: min - step - max in nano */
|
|
min = (int64_t)inv_icm42600_gyro_calibbias[0] * 1000000000LL +
|
|
(int64_t)inv_icm42600_gyro_calibbias[1];
|
|
max = (int64_t)inv_icm42600_gyro_calibbias[4] * 1000000000LL +
|
|
(int64_t)inv_icm42600_gyro_calibbias[5];
|
|
val64 = (int64_t)val * 1000000000LL + (int64_t)val2;
|
|
if (val64 < min || val64 > max)
|
|
return -EINVAL;
|
|
|
|
/*
|
|
* convert rad/s to dps then to raw value
|
|
* rad to dps: 180 / Pi
|
|
* dps to raw 12 bits signed, max 64: 2048 / 64
|
|
* val in nano (1000000000)
|
|
* val * 180 * 2048 / (Pi * 1000000000 * 64)
|
|
*/
|
|
val64 = val64 * 180LL * 2048LL;
|
|
/* for rounding, add + or - divisor (3141592653 * 64) divided by 2 */
|
|
if (val64 >= 0)
|
|
val64 += 3141592653LL * 64LL / 2LL;
|
|
else
|
|
val64 -= 3141592653LL * 64LL / 2LL;
|
|
offset = div64_s64(val64, 3141592653LL * 64LL);
|
|
|
|
/* clamp value limited to 12 bits signed */
|
|
if (offset < -2048)
|
|
offset = -2048;
|
|
else if (offset > 2047)
|
|
offset = 2047;
|
|
|
|
pm_runtime_get_sync(dev);
|
|
mutex_lock(&st->lock);
|
|
|
|
switch (chan->channel2) {
|
|
case IIO_MOD_X:
|
|
/* OFFSET_USER1 register is shared */
|
|
ret = regmap_read(st->map, INV_ICM42600_REG_OFFSET_USER1,
|
|
®val);
|
|
if (ret)
|
|
goto out_unlock;
|
|
st->buffer[0] = offset & 0xFF;
|
|
st->buffer[1] = (regval & 0xF0) | ((offset & 0xF00) >> 8);
|
|
break;
|
|
case IIO_MOD_Y:
|
|
/* OFFSET_USER1 register is shared */
|
|
ret = regmap_read(st->map, INV_ICM42600_REG_OFFSET_USER1,
|
|
®val);
|
|
if (ret)
|
|
goto out_unlock;
|
|
st->buffer[0] = ((offset & 0xF00) >> 4) | (regval & 0x0F);
|
|
st->buffer[1] = offset & 0xFF;
|
|
break;
|
|
case IIO_MOD_Z:
|
|
/* OFFSET_USER4 register is shared */
|
|
ret = regmap_read(st->map, INV_ICM42600_REG_OFFSET_USER4,
|
|
®val);
|
|
if (ret)
|
|
goto out_unlock;
|
|
st->buffer[0] = offset & 0xFF;
|
|
st->buffer[1] = (regval & 0xF0) | ((offset & 0xF00) >> 8);
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
goto out_unlock;
|
|
}
|
|
|
|
ret = regmap_bulk_write(st->map, reg, st->buffer, 2);
|
|
|
|
out_unlock:
|
|
mutex_unlock(&st->lock);
|
|
pm_runtime_mark_last_busy(dev);
|
|
pm_runtime_put_autosuspend(dev);
|
|
return ret;
|
|
}
|
|
|
|
static int inv_icm42600_gyro_read_raw(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan,
|
|
int *val, int *val2, long mask)
|
|
{
|
|
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
|
|
int16_t data;
|
|
int ret;
|
|
|
|
switch (chan->type) {
|
|
case IIO_ANGL_VEL:
|
|
break;
|
|
case IIO_TEMP:
|
|
return inv_icm42600_temp_read_raw(indio_dev, chan, val, val2, mask);
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_RAW:
|
|
ret = iio_device_claim_direct_mode(indio_dev);
|
|
if (ret)
|
|
return ret;
|
|
ret = inv_icm42600_gyro_read_sensor(st, chan, &data);
|
|
iio_device_release_direct_mode(indio_dev);
|
|
if (ret)
|
|
return ret;
|
|
*val = data;
|
|
return IIO_VAL_INT;
|
|
case IIO_CHAN_INFO_SCALE:
|
|
return inv_icm42600_gyro_read_scale(st, val, val2);
|
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
|
return inv_icm42600_gyro_read_odr(st, val, val2);
|
|
case IIO_CHAN_INFO_CALIBBIAS:
|
|
return inv_icm42600_gyro_read_offset(st, chan, val, val2);
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int inv_icm42600_gyro_read_avail(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan,
|
|
const int **vals,
|
|
int *type, int *length, long mask)
|
|
{
|
|
if (chan->type != IIO_ANGL_VEL)
|
|
return -EINVAL;
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_SCALE:
|
|
*vals = inv_icm42600_gyro_scale;
|
|
*type = IIO_VAL_INT_PLUS_NANO;
|
|
*length = ARRAY_SIZE(inv_icm42600_gyro_scale);
|
|
return IIO_AVAIL_LIST;
|
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
|
*vals = inv_icm42600_gyro_odr;
|
|
*type = IIO_VAL_INT_PLUS_MICRO;
|
|
*length = ARRAY_SIZE(inv_icm42600_gyro_odr);
|
|
return IIO_AVAIL_LIST;
|
|
case IIO_CHAN_INFO_CALIBBIAS:
|
|
*vals = inv_icm42600_gyro_calibbias;
|
|
*type = IIO_VAL_INT_PLUS_NANO;
|
|
return IIO_AVAIL_RANGE;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int inv_icm42600_gyro_write_raw(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan,
|
|
int val, int val2, long mask)
|
|
{
|
|
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
|
|
int ret;
|
|
|
|
if (chan->type != IIO_ANGL_VEL)
|
|
return -EINVAL;
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_SCALE:
|
|
ret = iio_device_claim_direct_mode(indio_dev);
|
|
if (ret)
|
|
return ret;
|
|
ret = inv_icm42600_gyro_write_scale(st, val, val2);
|
|
iio_device_release_direct_mode(indio_dev);
|
|
return ret;
|
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
|
return inv_icm42600_gyro_write_odr(indio_dev, val, val2);
|
|
case IIO_CHAN_INFO_CALIBBIAS:
|
|
ret = iio_device_claim_direct_mode(indio_dev);
|
|
if (ret)
|
|
return ret;
|
|
ret = inv_icm42600_gyro_write_offset(st, chan, val, val2);
|
|
iio_device_release_direct_mode(indio_dev);
|
|
return ret;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int inv_icm42600_gyro_write_raw_get_fmt(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan,
|
|
long mask)
|
|
{
|
|
if (chan->type != IIO_ANGL_VEL)
|
|
return -EINVAL;
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_SCALE:
|
|
return IIO_VAL_INT_PLUS_NANO;
|
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
|
return IIO_VAL_INT_PLUS_MICRO;
|
|
case IIO_CHAN_INFO_CALIBBIAS:
|
|
return IIO_VAL_INT_PLUS_NANO;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int inv_icm42600_gyro_hwfifo_set_watermark(struct iio_dev *indio_dev,
|
|
unsigned int val)
|
|
{
|
|
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
|
|
int ret;
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
st->fifo.watermark.gyro = val;
|
|
ret = inv_icm42600_buffer_update_watermark(st);
|
|
|
|
mutex_unlock(&st->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int inv_icm42600_gyro_hwfifo_flush(struct iio_dev *indio_dev,
|
|
unsigned int count)
|
|
{
|
|
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
|
|
int ret;
|
|
|
|
if (count == 0)
|
|
return 0;
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
ret = inv_icm42600_buffer_hwfifo_flush(st, count);
|
|
if (!ret)
|
|
ret = st->fifo.nb.gyro;
|
|
|
|
mutex_unlock(&st->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct iio_info inv_icm42600_gyro_info = {
|
|
.read_raw = inv_icm42600_gyro_read_raw,
|
|
.read_avail = inv_icm42600_gyro_read_avail,
|
|
.write_raw = inv_icm42600_gyro_write_raw,
|
|
.write_raw_get_fmt = inv_icm42600_gyro_write_raw_get_fmt,
|
|
.debugfs_reg_access = inv_icm42600_debugfs_reg,
|
|
.update_scan_mode = inv_icm42600_gyro_update_scan_mode,
|
|
.hwfifo_set_watermark = inv_icm42600_gyro_hwfifo_set_watermark,
|
|
.hwfifo_flush_to_buffer = inv_icm42600_gyro_hwfifo_flush,
|
|
};
|
|
|
|
struct iio_dev *inv_icm42600_gyro_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;
|
|
|
|
name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->name);
|
|
if (!name)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
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.gyro.odr));
|
|
|
|
iio_device_set_drvdata(indio_dev, st);
|
|
indio_dev->name = name;
|
|
indio_dev->info = &inv_icm42600_gyro_info;
|
|
indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
|
|
indio_dev->channels = inv_icm42600_gyro_channels;
|
|
indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_gyro_channels);
|
|
indio_dev->available_scan_masks = inv_icm42600_gyro_scan_masks;
|
|
indio_dev->setup_ops = &inv_icm42600_buffer_ops;
|
|
|
|
iio_device_attach_buffer(indio_dev, buffer);
|
|
|
|
ret = devm_iio_device_register(dev, indio_dev);
|
|
if (ret)
|
|
return ERR_PTR(ret);
|
|
|
|
return indio_dev;
|
|
}
|
|
|
|
int inv_icm42600_gyro_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_gyro_buffer buffer;
|
|
|
|
/* parse all fifo packets */
|
|
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, ×tamp, &odr);
|
|
/* quit if error or FIFO is empty */
|
|
if (size <= 0)
|
|
return size;
|
|
|
|
/* skip packet if no gyro data or data is invalid */
|
|
if (gyro == NULL || !inv_icm42600_fifo_is_data_valid(gyro))
|
|
continue;
|
|
|
|
/* update odr */
|
|
if (odr & INV_ICM42600_SENSOR_GYRO)
|
|
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.gyro, gyro, sizeof(buffer.gyro));
|
|
/* convert 8 bits FIFO temperature in high resolution format */
|
|
buffer.temp = temp ? (*temp * 64) : 0;
|
|
ts_val = inv_icm42600_timestamp_pop(ts);
|
|
iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val);
|
|
}
|
|
|
|
return 0;
|
|
}
|