iio: imu: inv_icm42600: add support of accel low-power mode
Add ODRs accessible only in low-power mode. Switch automatically to low-power or low-noise depending on the ODR set. Add channel attributes "power_mode" and "power_mode_available" for setting the power mode to use (low-noise or low-power) for ODRs supporting both mode. Reading "power_mode" when the sensor is on will return the actual mode and not the requested one. It will be different when using ODRs not supported by the requested mode. Use low-power mode by default. Signed-off-by: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com> Link: https://lore.kernel.org/r/20240605195949.766677-3-inv.git-commit@tdk.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
committed by
Jonathan Cameron
parent
3d4d033a8d
commit
07d4d0bb4a
@@ -177,11 +177,15 @@ struct inv_icm42600_state {
|
||||
* struct inv_icm42600_sensor_state - sensor state variables
|
||||
* @scales: table of scales.
|
||||
* @scales_len: length (nb of items) of the scales table.
|
||||
* @power_mode: sensor requested power mode (for common frequencies)
|
||||
* @filter: sensor filter.
|
||||
* @ts: timestamp module states.
|
||||
*/
|
||||
struct inv_icm42600_sensor_state {
|
||||
const int *scales;
|
||||
size_t scales_len;
|
||||
enum inv_icm42600_sensor_mode power_mode;
|
||||
enum inv_icm42600_filter filter;
|
||||
struct inv_sensors_timestamp ts;
|
||||
};
|
||||
|
||||
|
||||
@@ -55,8 +55,108 @@ enum inv_icm42600_accel_scan {
|
||||
INV_ICM42600_ACCEL_SCAN_TIMESTAMP,
|
||||
};
|
||||
|
||||
static const char * const inv_icm42600_accel_power_mode_items[] = {
|
||||
"low-noise",
|
||||
"low-power",
|
||||
};
|
||||
static const int inv_icm42600_accel_power_mode_values[] = {
|
||||
INV_ICM42600_SENSOR_MODE_LOW_NOISE,
|
||||
INV_ICM42600_SENSOR_MODE_LOW_POWER,
|
||||
};
|
||||
static const int inv_icm42600_accel_filter_values[] = {
|
||||
INV_ICM42600_FILTER_BW_ODR_DIV_2,
|
||||
INV_ICM42600_FILTER_AVG_16X,
|
||||
};
|
||||
|
||||
static int inv_icm42600_accel_power_mode_set(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int idx)
|
||||
{
|
||||
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
|
||||
struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev);
|
||||
int power_mode, filter;
|
||||
|
||||
if (chan->type != IIO_ACCEL)
|
||||
return -EINVAL;
|
||||
|
||||
if (idx >= ARRAY_SIZE(inv_icm42600_accel_power_mode_values))
|
||||
return -EINVAL;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
power_mode = inv_icm42600_accel_power_mode_values[idx];
|
||||
filter = inv_icm42600_accel_filter_values[idx];
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
|
||||
/* prevent change if power mode is not supported by the ODR */
|
||||
switch (power_mode) {
|
||||
case INV_ICM42600_SENSOR_MODE_LOW_NOISE:
|
||||
if (st->conf.accel.odr >= INV_ICM42600_ODR_6_25HZ_LP &&
|
||||
st->conf.accel.odr <= INV_ICM42600_ODR_1_5625HZ_LP)
|
||||
return -EPERM;
|
||||
break;
|
||||
case INV_ICM42600_SENSOR_MODE_LOW_POWER:
|
||||
default:
|
||||
if (st->conf.accel.odr <= INV_ICM42600_ODR_1KHZ_LN)
|
||||
return -EPERM;
|
||||
break;
|
||||
}
|
||||
|
||||
accel_st->power_mode = power_mode;
|
||||
accel_st->filter = filter;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int inv_icm42600_accel_power_mode_get(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
|
||||
struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev);
|
||||
unsigned int idx;
|
||||
int power_mode;
|
||||
|
||||
if (chan->type != IIO_ACCEL)
|
||||
return -EINVAL;
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
|
||||
/* if sensor is on, returns actual power mode and not configured one */
|
||||
switch (st->conf.accel.mode) {
|
||||
case INV_ICM42600_SENSOR_MODE_LOW_POWER:
|
||||
case INV_ICM42600_SENSOR_MODE_LOW_NOISE:
|
||||
power_mode = st->conf.accel.mode;
|
||||
break;
|
||||
default:
|
||||
power_mode = accel_st->power_mode;
|
||||
break;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(inv_icm42600_accel_power_mode_values); ++idx) {
|
||||
if (power_mode == inv_icm42600_accel_power_mode_values[idx])
|
||||
break;
|
||||
}
|
||||
if (idx >= ARRAY_SIZE(inv_icm42600_accel_power_mode_values))
|
||||
return -EINVAL;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
static const struct iio_enum inv_icm42600_accel_power_mode_enum = {
|
||||
.items = inv_icm42600_accel_power_mode_items,
|
||||
.num_items = ARRAY_SIZE(inv_icm42600_accel_power_mode_items),
|
||||
.set = inv_icm42600_accel_power_mode_set,
|
||||
.get = inv_icm42600_accel_power_mode_get,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info inv_icm42600_accel_ext_infos[] = {
|
||||
IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42600_get_mount_matrix),
|
||||
IIO_ENUM_AVAILABLE("power_mode", IIO_SHARED_BY_TYPE,
|
||||
&inv_icm42600_accel_power_mode_enum),
|
||||
IIO_ENUM("power_mode", IIO_SHARED_BY_TYPE,
|
||||
&inv_icm42600_accel_power_mode_enum),
|
||||
{},
|
||||
};
|
||||
|
||||
@@ -120,7 +220,8 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev,
|
||||
|
||||
if (*scan_mask & INV_ICM42600_SCAN_MASK_ACCEL_3AXIS) {
|
||||
/* enable accel sensor */
|
||||
conf.mode = INV_ICM42600_SENSOR_MODE_LOW_NOISE;
|
||||
conf.mode = accel_st->power_mode;
|
||||
conf.filter = accel_st->filter;
|
||||
ret = inv_icm42600_set_accel_conf(st, &conf, &sleep_accel);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
@@ -144,10 +245,12 @@ out_unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int inv_icm42600_accel_read_sensor(struct inv_icm42600_state *st,
|
||||
static int inv_icm42600_accel_read_sensor(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int16_t *val)
|
||||
{
|
||||
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
|
||||
struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev);
|
||||
struct device *dev = regmap_get_device(st->map);
|
||||
struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
|
||||
unsigned int reg;
|
||||
@@ -175,7 +278,8 @@ static int inv_icm42600_accel_read_sensor(struct inv_icm42600_state *st,
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
/* enable accel sensor */
|
||||
conf.mode = INV_ICM42600_SENSOR_MODE_LOW_NOISE;
|
||||
conf.mode = accel_st->power_mode;
|
||||
conf.filter = accel_st->filter;
|
||||
ret = inv_icm42600_set_accel_conf(st, &conf, NULL);
|
||||
if (ret)
|
||||
goto exit;
|
||||
@@ -277,6 +381,12 @@ static int inv_icm42600_accel_write_scale(struct iio_dev *indio_dev,
|
||||
|
||||
/* IIO format int + micro */
|
||||
static const int inv_icm42600_accel_odr[] = {
|
||||
/* 1.5625Hz */
|
||||
1, 562500,
|
||||
/* 3.125Hz */
|
||||
3, 125000,
|
||||
/* 6.25Hz */
|
||||
6, 250000,
|
||||
/* 12.5Hz */
|
||||
12, 500000,
|
||||
/* 25Hz */
|
||||
@@ -296,6 +406,9 @@ static const int inv_icm42600_accel_odr[] = {
|
||||
};
|
||||
|
||||
static const int inv_icm42600_accel_odr_conv[] = {
|
||||
INV_ICM42600_ODR_1_5625HZ_LP,
|
||||
INV_ICM42600_ODR_3_125HZ_LP,
|
||||
INV_ICM42600_ODR_6_25HZ_LP,
|
||||
INV_ICM42600_ODR_12_5HZ,
|
||||
INV_ICM42600_ODR_25HZ,
|
||||
INV_ICM42600_ODR_50HZ,
|
||||
@@ -581,7 +694,7 @@ static int inv_icm42600_accel_read_raw(struct iio_dev *indio_dev,
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = inv_icm42600_accel_read_sensor(st, chan, &data);
|
||||
ret = inv_icm42600_accel_read_sensor(indio_dev, chan, &data);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -754,6 +867,9 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
|
||||
accel_st->scales_len = ARRAY_SIZE(inv_icm42600_accel_scale);
|
||||
break;
|
||||
}
|
||||
/* low-power by default at init */
|
||||
accel_st->power_mode = INV_ICM42600_SENSOR_MODE_LOW_POWER;
|
||||
accel_st->filter = INV_ICM42600_FILTER_AVG_16X;
|
||||
|
||||
/*
|
||||
* clock period is 32kHz (31250ns)
|
||||
|
||||
@@ -292,6 +292,23 @@ int inv_icm42600_set_accel_conf(struct inv_icm42600_state *st,
|
||||
if (conf->filter < 0)
|
||||
conf->filter = oldconf->filter;
|
||||
|
||||
/* force power mode against ODR when sensor is on */
|
||||
switch (conf->mode) {
|
||||
case INV_ICM42600_SENSOR_MODE_LOW_POWER:
|
||||
case INV_ICM42600_SENSOR_MODE_LOW_NOISE:
|
||||
if (conf->odr <= INV_ICM42600_ODR_1KHZ_LN) {
|
||||
conf->mode = INV_ICM42600_SENSOR_MODE_LOW_NOISE;
|
||||
conf->filter = INV_ICM42600_FILTER_BW_ODR_DIV_2;
|
||||
} else if (conf->odr >= INV_ICM42600_ODR_6_25HZ_LP &&
|
||||
conf->odr <= INV_ICM42600_ODR_1_5625HZ_LP) {
|
||||
conf->mode = INV_ICM42600_SENSOR_MODE_LOW_POWER;
|
||||
conf->filter = INV_ICM42600_FILTER_AVG_16X;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* set ACCEL_CONFIG0 register (accel fullscale & odr) */
|
||||
if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) {
|
||||
val = INV_ICM42600_ACCEL_CONFIG0_FS(conf->fs) |
|
||||
@@ -485,6 +502,16 @@ static int inv_icm42600_setup(struct inv_icm42600_state *st,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Use RC clock for accel low-power to fix glitches when switching
|
||||
* gyro on/off while accel low-power is on.
|
||||
*/
|
||||
ret = regmap_update_bits(st->map, INV_ICM42600_REG_INTF_CONFIG1,
|
||||
INV_ICM42600_INTF_CONFIG1_ACCEL_LP_CLK_RC,
|
||||
INV_ICM42600_INTF_CONFIG1_ACCEL_LP_CLK_RC);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return inv_icm42600_set_conf(st, hw->conf);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user