Commit 305914d0 authored by Jean-Baptiste Maneyrol's avatar Jean-Baptiste Maneyrol Committed by Jonathan Cameron

iio: imu: inv_mpu6050: add WoM suspend wakeup with low-power mode

Add wakeup from suspend for WoM when enabled and put accel in low-power
mode when suspended. Requires rewriting pwr_mgmt_1 register handling and
factorize out accel LPF settings. Use a low-power rate similar to the chip
sampling rate but always lower for a best match of the sampling rate while
saving power and adjust threshold to follow the required roc value.
Signed-off-by: default avatarJean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>
Link: https://lore.kernel.org/r/20240311160557.437337-5-inv.git-commit@tdk.comSigned-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent 5537f653
......@@ -289,7 +289,7 @@ static const struct inv_mpu6050_hw hw_info[] = {
};
static int inv_mpu6050_pwr_mgmt_1_write(struct inv_mpu6050_state *st, bool sleep,
int clock, int temp_dis)
bool cycle, int clock, int temp_dis)
{
u8 val;
......@@ -303,6 +303,8 @@ static int inv_mpu6050_pwr_mgmt_1_write(struct inv_mpu6050_state *st, bool sleep
val |= INV_MPU6050_BIT_TEMP_DIS;
if (sleep)
val |= INV_MPU6050_BIT_SLEEP;
if (cycle)
val |= INV_MPU6050_BIT_CYCLE;
dev_dbg(regmap_get_device(st->map), "pwr_mgmt_1: 0x%x\n", val);
return regmap_write(st->map, st->reg->pwr_mgmt_1, val);
......@@ -318,7 +320,7 @@ static int inv_mpu6050_clock_switch(struct inv_mpu6050_state *st,
case INV_MPU6000:
case INV_MPU9150:
/* old chips: switch clock manually */
ret = inv_mpu6050_pwr_mgmt_1_write(st, false, clock, -1);
ret = inv_mpu6050_pwr_mgmt_1_write(st, false, false, clock, -1);
if (ret)
return ret;
st->chip_config.clk = clock;
......@@ -360,7 +362,7 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en,
/* turn on/off temperature sensor */
if (mask & INV_MPU6050_SENSOR_TEMP) {
ret = inv_mpu6050_pwr_mgmt_1_write(st, false, -1, !en);
ret = inv_mpu6050_pwr_mgmt_1_write(st, false, false, -1, !en);
if (ret)
return ret;
st->chip_config.temp_en = en;
......@@ -467,7 +469,7 @@ static int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st,
{
int result;
result = inv_mpu6050_pwr_mgmt_1_write(st, !power_on, -1, -1);
result = inv_mpu6050_pwr_mgmt_1_write(st, !power_on, false, -1, -1);
if (result)
return result;
......@@ -497,22 +499,9 @@ static int inv_mpu6050_set_gyro_fsr(struct inv_mpu6050_state *st,
return regmap_write(st->map, st->reg->gyro_config, data);
}
/*
* inv_mpu6050_set_lpf_regs() - set low pass filter registers, chip dependent
*
* MPU60xx/MPU9150 use only 1 register for accelerometer + gyroscope
* MPU6500 and above have a dedicated register for accelerometer
*/
static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st,
enum inv_mpu6050_filter_e val)
static int inv_mpu6050_set_accel_lpf_regs(struct inv_mpu6050_state *st,
enum inv_mpu6050_filter_e val)
{
int result;
result = regmap_write(st->map, st->reg->lpf, val);
if (result)
return result;
/* set accel lpf */
switch (st->chip_type) {
case INV_MPU6050:
case INV_MPU6000:
......@@ -531,6 +520,25 @@ static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st,
return regmap_write(st->map, st->reg->accel_lpf, val);
}
/*
* inv_mpu6050_set_lpf_regs() - set low pass filter registers, chip dependent
*
* MPU60xx/MPU9150 use only 1 register for accelerometer + gyroscope
* MPU6500 and above have a dedicated register for accelerometer
*/
static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st,
enum inv_mpu6050_filter_e val)
{
int result;
result = regmap_write(st->map, st->reg->lpf, val);
if (result)
return result;
/* set accel lpf */
return inv_mpu6050_set_accel_lpf_regs(st, val);
}
/*
* inv_mpu6050_init_config() - Initialize hardware, disable FIFO.
*
......@@ -1002,6 +1010,84 @@ static int inv_mpu6050_set_wom_threshold(struct inv_mpu6050_state *st, u64 value
return 0;
}
static int inv_mpu6050_set_lp_odr(struct inv_mpu6050_state *st, unsigned int freq_div,
unsigned int *lp_div)
{
static const unsigned int freq_dividers[] = {2, 4, 8, 16, 32, 64, 128, 256};
static const unsigned int reg_values[] = {
INV_MPU6050_LPOSC_500HZ, INV_MPU6050_LPOSC_250HZ,
INV_MPU6050_LPOSC_125HZ, INV_MPU6050_LPOSC_62HZ,
INV_MPU6050_LPOSC_31HZ, INV_MPU6050_LPOSC_16HZ,
INV_MPU6050_LPOSC_8HZ, INV_MPU6050_LPOSC_4HZ,
};
unsigned int val, i;
switch (st->chip_type) {
case INV_ICM20609:
case INV_ICM20689:
case INV_ICM20600:
case INV_ICM20602:
case INV_ICM20690:
/* nothing to do */
*lp_div = INV_MPU6050_FREQ_DIVIDER(st);
return 0;
default:
break;
}
/* found the nearest superior frequency divider */
i = ARRAY_SIZE(reg_values) - 1;
val = reg_values[i];
*lp_div = freq_dividers[i];
for (i = 0; i < ARRAY_SIZE(freq_dividers); ++i) {
if (freq_div <= freq_dividers[i]) {
val = reg_values[i];
*lp_div = freq_dividers[i];
break;
}
}
dev_dbg(regmap_get_device(st->map), "lp_odr: 0x%x\n", val);
return regmap_write(st->map, INV_MPU6500_REG_LP_ODR, val);
}
static int inv_mpu6050_set_wom_lp(struct inv_mpu6050_state *st, bool on)
{
unsigned int lp_div;
int result;
if (on) {
/* set low power ODR */
result = inv_mpu6050_set_lp_odr(st, INV_MPU6050_FREQ_DIVIDER(st), &lp_div);
if (result)
return result;
/* disable accel low pass filter */
result = inv_mpu6050_set_accel_lpf_regs(st, INV_MPU6050_FILTER_NOLPF);
if (result)
return result;
/* update wom threshold with new low-power frequency divider */
result = inv_mpu6050_set_wom_threshold(st, st->chip_config.roc_threshold, lp_div);
if (result)
return result;
/* set cycle mode */
result = inv_mpu6050_pwr_mgmt_1_write(st, false, true, -1, -1);
} else {
/* disable cycle mode */
result = inv_mpu6050_pwr_mgmt_1_write(st, false, false, -1, -1);
if (result)
return result;
/* restore wom threshold */
result = inv_mpu6050_set_wom_threshold(st, st->chip_config.roc_threshold,
INV_MPU6050_FREQ_DIVIDER(st));
if (result)
return result;
/* restore accel low pass filter */
result = inv_mpu6050_set_accel_lpf_regs(st, st->chip_config.lpf);
}
return result;
}
static int inv_mpu6050_enable_wom(struct inv_mpu6050_state *st, bool en)
{
struct device *pdev = regmap_get_device(st->map);
......@@ -1836,6 +1922,7 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
irq_type);
return -EINVAL;
}
device_set_wakeup_capable(dev, true);
st->vdd_supply = devm_regulator_get(dev, "vdd");
if (IS_ERR(st->vdd_supply))
......@@ -2001,16 +2088,27 @@ static int inv_mpu_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct inv_mpu6050_state *st = iio_priv(indio_dev);
bool wakeup;
int result;
mutex_lock(&st->lock);
result = inv_mpu_core_enable_regulator_vddio(st);
if (result)
goto out_unlock;
guard(mutex)(&st->lock);
result = inv_mpu6050_set_power_itg(st, true);
if (result)
goto out_unlock;
wakeup = device_may_wakeup(dev) && st->chip_config.wom_en;
if (wakeup) {
enable_irq(st->irq);
disable_irq_wake(st->irq);
result = inv_mpu6050_set_wom_lp(st, false);
if (result)
return result;
} else {
result = inv_mpu_core_enable_regulator_vddio(st);
if (result)
return result;
result = inv_mpu6050_set_power_itg(st, true);
if (result)
return result;
}
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
......@@ -2018,20 +2116,17 @@ static int inv_mpu_resume(struct device *dev)
result = inv_mpu6050_switch_engine(st, true, st->suspended_sensors);
if (result)
goto out_unlock;
return result;
if (st->chip_config.wom_en) {
if (st->chip_config.wom_en && !wakeup) {
result = inv_mpu6050_set_wom_int(st, true);
if (result)
goto out_unlock;
return result;
}
if (iio_buffer_enabled(indio_dev))
result = inv_mpu6050_prepare_fifo(st, true);
out_unlock:
mutex_unlock(&st->lock);
return result;
}
......@@ -2039,29 +2134,30 @@ static int inv_mpu_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct inv_mpu6050_state *st = iio_priv(indio_dev);
bool wakeup;
int result;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
st->suspended_sensors = 0;
if (pm_runtime_suspended(dev)) {
result = 0;
goto out_unlock;
}
if (pm_runtime_suspended(dev))
return 0;
if (iio_buffer_enabled(indio_dev)) {
result = inv_mpu6050_prepare_fifo(st, false);
if (result)
goto out_unlock;
return result;
}
if (st->chip_config.wom_en) {
wakeup = device_may_wakeup(dev) && st->chip_config.wom_en;
if (st->chip_config.wom_en && !wakeup) {
result = inv_mpu6050_set_wom_int(st, false);
if (result)
goto out_unlock;
return result;
}
if (st->chip_config.accl_en)
if (st->chip_config.accl_en && !wakeup)
st->suspended_sensors |= INV_MPU6050_SENSOR_ACCL;
if (st->chip_config.gyro_en)
st->suspended_sensors |= INV_MPU6050_SENSOR_GYRO;
......@@ -2069,21 +2165,26 @@ static int inv_mpu_suspend(struct device *dev)
st->suspended_sensors |= INV_MPU6050_SENSOR_TEMP;
if (st->chip_config.magn_en)
st->suspended_sensors |= INV_MPU6050_SENSOR_MAGN;
if (st->chip_config.wom_en)
if (st->chip_config.wom_en && !wakeup)
st->suspended_sensors |= INV_MPU6050_SENSOR_WOM;
result = inv_mpu6050_switch_engine(st, false, st->suspended_sensors);
if (result)
goto out_unlock;
result = inv_mpu6050_set_power_itg(st, false);
if (result)
goto out_unlock;
return result;
inv_mpu_core_disable_regulator_vddio(st);
out_unlock:
mutex_unlock(&st->lock);
if (wakeup) {
result = inv_mpu6050_set_wom_lp(st, true);
if (result)
return result;
enable_irq_wake(st->irq);
disable_irq(st->irq);
} else {
result = inv_mpu6050_set_power_itg(st, false);
if (result)
return result;
inv_mpu_core_disable_regulator_vddio(st);
}
return result;
return 0;
}
static int inv_mpu_runtime_suspend(struct device *dev)
......
......@@ -305,6 +305,7 @@ struct inv_mpu6050_state {
#define INV_MPU6050_REG_PWR_MGMT_1 0x6B
#define INV_MPU6050_BIT_H_RESET 0x80
#define INV_MPU6050_BIT_SLEEP 0x40
#define INV_MPU6050_BIT_CYCLE 0x20
#define INV_MPU6050_BIT_TEMP_DIS 0x08
#define INV_MPU6050_BIT_CLK_MASK 0x7
......@@ -336,6 +337,7 @@ struct inv_mpu6050_state {
/* mpu6500 registers */
#define INV_MPU6500_REG_ACCEL_CONFIG_2 0x1D
#define INV_ICM20689_BITS_FIFO_SIZE_MAX 0xC0
#define INV_MPU6500_REG_LP_ODR 0x1E
#define INV_MPU6500_REG_WOM_THRESHOLD 0x1F
#define INV_MPU6500_REG_ACCEL_INTEL_CTRL 0x69
#define INV_MPU6500_BIT_ACCEL_INTEL_EN BIT(7)
......@@ -452,6 +454,18 @@ enum inv_mpu6050_filter_e {
NUM_MPU6050_FILTER
};
enum inv_mpu6050_lposc_e {
INV_MPU6050_LPOSC_4HZ = 4,
INV_MPU6050_LPOSC_8HZ,
INV_MPU6050_LPOSC_16HZ,
INV_MPU6050_LPOSC_31HZ,
INV_MPU6050_LPOSC_62HZ,
INV_MPU6050_LPOSC_125HZ,
INV_MPU6050_LPOSC_250HZ,
INV_MPU6050_LPOSC_500HZ,
NUM_MPU6050_LPOSC,
};
/* IIO attribute address */
enum INV_MPU6050_IIO_ATTR_ADDR {
ATTR_GYRO_MATRIX,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment