Message ID | 20240225160027.200092-2-inv.git-commit@tdk.com (mailing list archive) |
---|---|
State | Changes Requested |
Headers | show |
Series | Add WoM feature as an IIO event | expand |
On Sun, 25 Feb 2024 16:00:24 +0000 inv.git-commit@tdk.com wrote: > From: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com> > > WoM is a threshold test on accel value comparing actual sample > with previous one. It maps best to magnitude adaptive rising > event. > Add support of a new WOM sensor and functions for handling the > corresponding mag_adaptive_rising event. The event value is in > SI units. Ensure WOM is stopped and restarted at suspend-resume > and handle usage with buffer data ready interrupt. > > Signed-off-by: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com> A few questions inline. Things don't seem to align perfectly with the few datasheets I opened. Jonathan > diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h > index 5950e2419ebb..519c1eee96ad 100644 > --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h > +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h > @@ -88,11 +88,12 @@ enum inv_devices { > INV_NUM_PARTS > }; > > -/* chip sensors mask: accelerometer, gyroscope, temperature, magnetometer */ > +/* chip sensors mask: accelerometer, gyroscope, temperature, magnetometer, WoM */ > #define INV_MPU6050_SENSOR_ACCL BIT(0) > #define INV_MPU6050_SENSOR_GYRO BIT(1) > #define INV_MPU6050_SENSOR_TEMP BIT(2) > #define INV_MPU6050_SENSOR_MAGN BIT(3) > +#define INV_MPU6050_SENSOR_WOM BIT(4) > > /** > * struct inv_mpu6050_chip_config - Cached chip configuration data. > @@ -104,6 +105,7 @@ enum inv_devices { > * @gyro_en: gyro engine enabled > * @temp_en: temperature sensor enabled > * @magn_en: magn engine (i2c master) enabled > + * @wom_en: Wake-on-Motion enabled > * @accl_fifo_enable: enable accel data output > * @gyro_fifo_enable: enable gyro data output > * @temp_fifo_enable: enable temp data output > @@ -119,12 +121,14 @@ struct inv_mpu6050_chip_config { > unsigned int gyro_en:1; > unsigned int temp_en:1; > unsigned int magn_en:1; > + unsigned int wom_en:1; > unsigned int accl_fifo_enable:1; > unsigned int gyro_fifo_enable:1; > unsigned int temp_fifo_enable:1; > unsigned int magn_fifo_enable:1; > u8 divider; > u8 user_ctrl; > + u8 wom_threshold; > }; > > /* > @@ -256,12 +260,14 @@ struct inv_mpu6050_state { > #define INV_MPU6050_REG_INT_ENABLE 0x38 > #define INV_MPU6050_BIT_DATA_RDY_EN 0x01 > #define INV_MPU6050_BIT_DMP_INT_EN 0x02 > +#define INV_MPU6500_BIT_WOM_INT_EN (BIT(7) | BIT(6) | BIT(5)) GENMASK? or build it up as I'm guessing those are the 3 axis? However I opened the datasheet from the tdk website and only bit(6) is mentioned. > > #define INV_MPU6050_REG_RAW_ACCEL 0x3B > #define INV_MPU6050_REG_TEMPERATURE 0x41 > #define INV_MPU6050_REG_RAW_GYRO 0x43 > > #define INV_MPU6050_REG_INT_STATUS 0x3A > +#define INV_MPU6500_BIT_WOM_INT (BIT(7) | BIT(6) | BIT(5)) Likewise on this, the mpu6500 datasheet only mentions bit(6) > #define INV_MPU6050_BIT_FIFO_OVERFLOW_INT 0x10 > #define INV_MPU6050_BIT_RAW_DATA_RDY_INT 0x01 > > @@ -301,6 +307,11 @@ struct inv_mpu6050_state { > #define INV_MPU6050_BIT_PWR_ACCL_STBY 0x38 > #define INV_MPU6050_BIT_PWR_GYRO_STBY 0x07 > > +/* ICM20609 registers */ > +#define INV_ICM20609_REG_ACCEL_WOM_X_THR 0x20 > +#define INV_ICM20609_REG_ACCEL_WOM_Y_THR 0x21 > +#define INV_ICM20609_REG_ACCEL_WOM_Z_THR 0x22 This one does mention all 3 bits for the enable and status registers. Perhaps there is more inter chip variation than currently covered? I don't like writing reserved bits unless we have a clear statement (and a comment here) that it is correct thing to do. > + > /* ICM20602 register */ > #define INV_ICM20602_REG_I2C_IF 0x70 > #define INV_ICM20602_BIT_I2C_IF_DIS 0x40 > @@ -320,6 +331,10 @@ 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_WOM_THRESHOLD 0x1F > +#define INV_MPU6500_REG_ACCEL_INTEL_CTRL 0x69 > +#define INV_MPU6500_BIT_ACCEL_INTEL_EN BIT(7) > +#define INV_MPU6500_BIT_ACCEL_INTEL_MODE BIT(6) > #define INV_MPU6500_REG_ACCEL_OFFSET 0x77 > > /* delay time in milliseconds */ > diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c > index 66d4ba088e70..13da6f523ca2 100644 > --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c > +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c > @@ -33,10 +33,8 @@ static int inv_reset_fifo(struct iio_dev *indio_dev) > > reset_fifo_fail: > dev_err(regmap_get_device(st->map), "reset fifo failed %d\n", result); > - result = regmap_write(st->map, st->reg->int_enable, > - INV_MPU6050_BIT_DATA_RDY_EN); > - > - return result; > + return regmap_update_bits(st->map, st->reg->int_enable, > + INV_MPU6050_BIT_DATA_RDY_EN, INV_MPU6050_BIT_DATA_RDY_EN); > } > > /* > diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c > index 676704f9151f..ec2398a87f45 100644 > --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c > +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c > @@ -134,11 +134,13 @@ int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable) > ret = regmap_write(st->map, st->reg->user_ctrl, d); > if (ret) > return ret; > - /* enable interrupt */ > - ret = regmap_write(st->map, st->reg->int_enable, > - INV_MPU6050_BIT_DATA_RDY_EN); > + /* enable data interrupt */ > + ret = regmap_update_bits(st->map, st->reg->int_enable, > + INV_MPU6050_BIT_DATA_RDY_EN, INV_MPU6050_BIT_DATA_RDY_EN); > } else { > - ret = regmap_write(st->map, st->reg->int_enable, 0); > + /* disable data interrupt */ > + ret = regmap_update_bits(st->map, st->reg->int_enable, > + INV_MPU6050_BIT_DATA_RDY_EN, 0); > if (ret) > return ret; > ret = regmap_write(st->map, st->reg->fifo_en, 0); > @@ -171,9 +173,9 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) > return result; > /* > * In case autosuspend didn't trigger, turn off first not > - * required sensors. > + * required sensors excepted WoM > */ > - result = inv_mpu6050_switch_engine(st, false, ~scan); > + result = inv_mpu6050_switch_engine(st, false, ~scan & ~INV_MPU6050_SENSOR_WOM); > if (result) > goto error_power_off; > result = inv_mpu6050_switch_engine(st, true, scan);
Hi Jonathan, you're right that for MPU-6500 this is not documented. In fact if I remember well, all chips were supposed to support 3-axes but it never works as expected until ICM-20609. For MPU-6500, only 1 bit is advertised, on ICM-20608 you have to use the 3 bits at the same time, and starting with ICM-20609 you can use the 3 separate bits. Using the 3 bits is working for all chips when testing, but certainly it would be better to comply with the datasheet and use only 1 bit for MPU-6500. Thanks for spotting this. Best regards, JB From: Jonathan Cameron <jic23@kernel.org> Sent: Sunday, March 3, 2024 17:56 To: INV Git Commit <INV.git-commit@tdk.com> Cc: lars@metafoo.de <lars@metafoo.de>; linux-iio@vger.kernel.org <linux-iio@vger.kernel.org>; Jean-Baptiste Maneyrol <Jean-Baptiste.Maneyrol@tdk.com> Subject: Re: [PATCH 1/4] iio: imu: inv_mpu6050: add WoM (Wake-on-Motion) sensor On Sun, 25 Feb 2024 16: 00: 24 +0000 inv. git-commit@ tdk. com wrote: > From: Jean-Baptiste Maneyrol <jean-baptiste. maneyrol@ tdk. com> > > WoM is a threshold test on accel value comparing actual sample > with previous one. It maps ZjQcmQRYFpfptBannerStart This Message Is From an External Sender This message came from outside your organization. ZjQcmQRYFpfptBannerEnd On Sun, 25 Feb 2024 16:00:24 +0000 inv.git-commit@tdk.com wrote: > From: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com> > > WoM is a threshold test on accel value comparing actual sample > with previous one. It maps best to magnitude adaptive rising > event. > Add support of a new WOM sensor and functions for handling the > corresponding mag_adaptive_rising event. The event value is in > SI units. Ensure WOM is stopped and restarted at suspend-resume > and handle usage with buffer data ready interrupt. > > Signed-off-by: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com> A few questions inline. Things don't seem to align perfectly with the few datasheets I opened. Jonathan > diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h > index 5950e2419ebb..519c1eee96ad 100644 > --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h > +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h > @@ -88,11 +88,12 @@ enum inv_devices { > INV_NUM_PARTS > }; > > -/* chip sensors mask: accelerometer, gyroscope, temperature, magnetometer */ > +/* chip sensors mask: accelerometer, gyroscope, temperature, magnetometer, WoM */ > #define INV_MPU6050_SENSOR_ACCL BIT(0) > #define INV_MPU6050_SENSOR_GYRO BIT(1) > #define INV_MPU6050_SENSOR_TEMP BIT(2) > #define INV_MPU6050_SENSOR_MAGN BIT(3) > +#define INV_MPU6050_SENSOR_WOM BIT(4) > > /** > * struct inv_mpu6050_chip_config - Cached chip configuration data. > @@ -104,6 +105,7 @@ enum inv_devices { > * @gyro_en: gyro engine enabled > * @temp_en: temperature sensor enabled > * @magn_en: magn engine (i2c master) enabled > + * @wom_en: Wake-on-Motion enabled > * @accl_fifo_enable: enable accel data output > * @gyro_fifo_enable: enable gyro data output > * @temp_fifo_enable: enable temp data output > @@ -119,12 +121,14 @@ struct inv_mpu6050_chip_config { > unsigned int gyro_en:1; > unsigned int temp_en:1; > unsigned int magn_en:1; > + unsigned int wom_en:1; > unsigned int accl_fifo_enable:1; > unsigned int gyro_fifo_enable:1; > unsigned int temp_fifo_enable:1; > unsigned int magn_fifo_enable:1; > u8 divider; > u8 user_ctrl; > + u8 wom_threshold; > }; > > /* > @@ -256,12 +260,14 @@ struct inv_mpu6050_state { > #define INV_MPU6050_REG_INT_ENABLE 0x38 > #define INV_MPU6050_BIT_DATA_RDY_EN 0x01 > #define INV_MPU6050_BIT_DMP_INT_EN 0x02 > +#define INV_MPU6500_BIT_WOM_INT_EN (BIT(7) | BIT(6) | BIT(5)) GENMASK? or build it up as I'm guessing those are the 3 axis? However I opened the datasheet from the tdk website and only bit(6) is mentioned. > > #define INV_MPU6050_REG_RAW_ACCEL 0x3B > #define INV_MPU6050_REG_TEMPERATURE 0x41 > #define INV_MPU6050_REG_RAW_GYRO 0x43 > > #define INV_MPU6050_REG_INT_STATUS 0x3A > +#define INV_MPU6500_BIT_WOM_INT (BIT(7) | BIT(6) | BIT(5)) Likewise on this, the mpu6500 datasheet only mentions bit(6) > #define INV_MPU6050_BIT_FIFO_OVERFLOW_INT 0x10 > #define INV_MPU6050_BIT_RAW_DATA_RDY_INT 0x01 > > @@ -301,6 +307,11 @@ struct inv_mpu6050_state { > #define INV_MPU6050_BIT_PWR_ACCL_STBY 0x38 > #define INV_MPU6050_BIT_PWR_GYRO_STBY 0x07 > > +/* ICM20609 registers */ > +#define INV_ICM20609_REG_ACCEL_WOM_X_THR 0x20 > +#define INV_ICM20609_REG_ACCEL_WOM_Y_THR 0x21 > +#define INV_ICM20609_REG_ACCEL_WOM_Z_THR 0x22 This one does mention all 3 bits for the enable and status registers. Perhaps there is more inter chip variation than currently covered? I don't like writing reserved bits unless we have a clear statement (and a comment here) that it is correct thing to do. > + > /* ICM20602 register */ > #define INV_ICM20602_REG_I2C_IF 0x70 > #define INV_ICM20602_BIT_I2C_IF_DIS 0x40 > @@ -320,6 +331,10 @@ 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_WOM_THRESHOLD 0x1F > +#define INV_MPU6500_REG_ACCEL_INTEL_CTRL 0x69 > +#define INV_MPU6500_BIT_ACCEL_INTEL_EN BIT(7) > +#define INV_MPU6500_BIT_ACCEL_INTEL_MODE BIT(6) > #define INV_MPU6500_REG_ACCEL_OFFSET 0x77 > > /* delay time in milliseconds */ > diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c > index 66d4ba088e70..13da6f523ca2 100644 > --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c > +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c > @@ -33,10 +33,8 @@ static int inv_reset_fifo(struct iio_dev *indio_dev) > > reset_fifo_fail: > dev_err(regmap_get_device(st->map), "reset fifo failed %d\n", result); > - result = regmap_write(st->map, st->reg->int_enable, > - INV_MPU6050_BIT_DATA_RDY_EN); > - > - return result; > + return regmap_update_bits(st->map, st->reg->int_enable, > + INV_MPU6050_BIT_DATA_RDY_EN, INV_MPU6050_BIT_DATA_RDY_EN); > } > > /* > diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c > index 676704f9151f..ec2398a87f45 100644 > --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c > +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c > @@ -134,11 +134,13 @@ int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable) > ret = regmap_write(st->map, st->reg->user_ctrl, d); > if (ret) > return ret; > - /* enable interrupt */ > - ret = regmap_write(st->map, st->reg->int_enable, > - INV_MPU6050_BIT_DATA_RDY_EN); > + /* enable data interrupt */ > + ret = regmap_update_bits(st->map, st->reg->int_enable, > + INV_MPU6050_BIT_DATA_RDY_EN, INV_MPU6050_BIT_DATA_RDY_EN); > } else { > - ret = regmap_write(st->map, st->reg->int_enable, 0); > + /* disable data interrupt */ > + ret = regmap_update_bits(st->map, st->reg->int_enable, > + INV_MPU6050_BIT_DATA_RDY_EN, 0); > if (ret) > return ret; > ret = regmap_write(st->map, st->reg->fifo_en, 0); > @@ -171,9 +173,9 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) > return result; > /* > * In case autosuspend didn't trigger, turn off first not > - * required sensors. > + * required sensors excepted WoM > */ > - result = inv_mpu6050_switch_engine(st, false, ~scan); > + result = inv_mpu6050_switch_engine(st, false, ~scan & ~INV_MPU6050_SENSOR_WOM); > if (result) > goto error_power_off; > result = inv_mpu6050_switch_engine(st, true, scan);
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 0e94e5335e93..fca7fc1ba4e2 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -332,7 +332,7 @@ static int inv_mpu6050_clock_switch(struct inv_mpu6050_state *st, int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, unsigned int mask) { - unsigned int sleep; + unsigned int sleep, val; u8 pwr_mgmt2, user_ctrl; int ret; @@ -345,6 +345,14 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, mask &= ~INV_MPU6050_SENSOR_TEMP; if (mask & INV_MPU6050_SENSOR_MAGN && en == st->chip_config.magn_en) mask &= ~INV_MPU6050_SENSOR_MAGN; + if (mask & INV_MPU6050_SENSOR_WOM && en == st->chip_config.wom_en) + mask &= ~INV_MPU6050_SENSOR_WOM; + + /* force accel on if WoM is on and not going off */ + if (!en && (mask & INV_MPU6050_SENSOR_ACCL) && st->chip_config.wom_en && + !(mask & INV_MPU6050_SENSOR_WOM)) + mask &= ~INV_MPU6050_SENSOR_ACCL; + if (mask == 0) return 0; @@ -439,6 +447,16 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, } } + /* enable/disable accel intelligence control */ + if (mask & INV_MPU6050_SENSOR_WOM) { + val = en ? INV_MPU6500_BIT_ACCEL_INTEL_EN | + INV_MPU6500_BIT_ACCEL_INTEL_MODE : 0; + ret = regmap_write(st->map, INV_MPU6500_REG_ACCEL_INTEL_CTRL, val); + if (ret) + return ret; + st->chip_config.wom_en = en; + } + return 0; } @@ -893,6 +911,202 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev, return result; } +static int inv_mpu6050_set_wom_int(struct inv_mpu6050_state *st, bool on) +{ + unsigned int val; + + val = on ? INV_MPU6500_BIT_WOM_INT_EN : 0; + + return regmap_update_bits(st->map, st->reg->int_enable, + INV_MPU6500_BIT_WOM_INT_EN, val); +} + +static int inv_mpu6050_set_wom_threshold(struct inv_mpu6050_state *st, unsigned int value) +{ + struct device *pdev = regmap_get_device(st->map); + int result; + + mutex_lock(&st->lock); + + result = pm_runtime_resume_and_get(pdev); + if (result) + goto exit_unlock; + + switch (st->chip_type) { + case INV_ICM20609: + case INV_ICM20689: + case INV_ICM20600: + case INV_ICM20602: + case INV_ICM20690: + st->data[0] = value; + st->data[1] = value; + st->data[2] = value; + result = regmap_bulk_write(st->map, INV_ICM20609_REG_ACCEL_WOM_X_THR, + st->data, 3); + break; + default: + result = regmap_write(st->map, INV_MPU6500_REG_WOM_THRESHOLD, value); + break; + } + if (result) + goto exit_suspend; + + st->chip_config.wom_threshold = value; + +exit_suspend: + pm_runtime_mark_last_busy(pdev); + pm_runtime_put_autosuspend(pdev); +exit_unlock: + mutex_unlock(&st->lock); + return result; +} + +static int inv_mpu6050_enable_wom(struct inv_mpu6050_state *st, bool en) +{ + struct device *pdev = regmap_get_device(st->map); + unsigned int mask; + int result; + + if (en) { + result = pm_runtime_resume_and_get(pdev); + if (result) + return result; + + mask = INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_WOM; + result = inv_mpu6050_switch_engine(st, true, mask); + if (result) + goto error_suspend; + + result = inv_mpu6050_set_wom_int(st, true); + if (result) + goto error_suspend; + } else { + result = inv_mpu6050_set_wom_int(st, false); + if (result) + dev_err(pdev, "error %d disabling WoM interrupt bit", result); + + /* disable only WoM and let accel be disabled by autosuspend */ + result = inv_mpu6050_switch_engine(st, false, INV_MPU6050_SENSOR_WOM); + if (result) { + dev_err(pdev, "error %d disabling WoM force off", result); + /* force WoM off */ + st->chip_config.wom_en = false; + } + + pm_runtime_mark_last_busy(pdev); + pm_runtime_put_autosuspend(pdev); + } + + return result; + +error_suspend: + pm_runtime_mark_last_busy(pdev); + pm_runtime_put_autosuspend(pdev); + return result; +} + +static int inv_mpu6050_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct inv_mpu6050_state *st = iio_priv(indio_dev); + int result; + + /* support only WoM (accel mag_adaptive rising) event */ + if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_MAG_ADAPTIVE || + dir != IIO_EV_DIR_RISING) + return -EINVAL; + + mutex_lock(&st->lock); + result = st->chip_config.wom_en ? 1 : 0; + mutex_unlock(&st->lock); + + return result; +} + +static int inv_mpu6050_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct inv_mpu6050_state *st = iio_priv(indio_dev); + int enable; + int result; + + /* support only WoM (accel mag_adaptive rising) event */ + if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_MAG_ADAPTIVE || + dir != IIO_EV_DIR_RISING) + return -EINVAL; + + enable = !!state; + + mutex_lock(&st->lock); + + if (st->chip_config.wom_en == enable) { + result = 0; + goto exit_unlock; + } + + result = inv_mpu6050_enable_wom(st, enable); + +exit_unlock: + mutex_unlock(&st->lock); + return result; +} + +static int inv_mpu6050_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, int *val2) +{ + struct inv_mpu6050_state *st = iio_priv(indio_dev); + unsigned int value; + + /* support only WoM (accel mag_adaptive rising) event threshold value */ + if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_MAG_ADAPTIVE || + dir != IIO_EV_DIR_RISING || info != IIO_EV_INFO_VALUE) + return -EINVAL; + + /* 4mg per LSB converted in m/s² in micro (1000000) */ + value = (unsigned int)st->chip_config.wom_threshold * 4U * 9807U; + *val = value / 1000000; + *val2 = value % 1000000; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int inv_mpu6050_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, int val2) +{ + struct inv_mpu6050_state *st = iio_priv(indio_dev); + const int max = 4 * 255 * 9807; + const int max_val = max / 1000000; + const int max_val2 = max % 1000000; + unsigned int value; + + /* support only WoM (accel mag_adaptive rising) event threshold value */ + if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_MAG_ADAPTIVE || + dir != IIO_EV_DIR_RISING || info != IIO_EV_INFO_VALUE) + return -EINVAL; + + if (val < 0 || val2 < 0) + return -EINVAL; + if (val > max_val || (val == max_val && val2 > max_val2)) { + val = max_val; + val2 = max_val2; + } + value = val * 1000000U + val2; + value /= (4U * 9807U); + + return inv_mpu6050_set_wom_threshold(st, value); +} + /* * inv_mpu6050_set_lpf() - set low pass filer based on fifo rate. * @@ -1326,6 +1540,10 @@ static const struct iio_info mpu_info = { .write_raw = &inv_mpu6050_write_raw, .write_raw_get_fmt = &inv_write_raw_get_fmt, .attrs = &inv_attribute_group, + .read_event_config = inv_mpu6050_read_event_config, + .write_event_config = inv_mpu6050_write_event_config, + .read_event_value = inv_mpu6050_read_event_value, + .write_event_value = inv_mpu6050_write_event_value, .validate_trigger = inv_mpu6050_validate_trigger, .debugfs_reg_access = &inv_mpu6050_reg_access, }; @@ -1706,6 +1924,12 @@ static int inv_mpu_resume(struct device *dev) if (result) goto out_unlock; + if (st->chip_config.wom_en) { + result = inv_mpu6050_set_wom_int(st, true); + if (result) + goto out_unlock; + } + if (iio_buffer_enabled(indio_dev)) result = inv_mpu6050_prepare_fifo(st, true); @@ -1735,6 +1959,12 @@ static int inv_mpu_suspend(struct device *dev) goto out_unlock; } + if (st->chip_config.wom_en) { + result = inv_mpu6050_set_wom_int(st, false); + if (result) + goto out_unlock; + } + if (st->chip_config.accl_en) st->suspended_sensors |= INV_MPU6050_SENSOR_ACCL; if (st->chip_config.gyro_en) @@ -1743,6 +1973,8 @@ 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) + st->suspended_sensors |= INV_MPU6050_SENSOR_WOM; result = inv_mpu6050_switch_engine(st, false, st->suspended_sensors); if (result) goto out_unlock; @@ -1767,7 +1999,8 @@ static int inv_mpu_runtime_suspend(struct device *dev) mutex_lock(&st->lock); sensors = INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_GYRO | - INV_MPU6050_SENSOR_TEMP | INV_MPU6050_SENSOR_MAGN; + INV_MPU6050_SENSOR_TEMP | INV_MPU6050_SENSOR_MAGN | + INV_MPU6050_SENSOR_WOM; ret = inv_mpu6050_switch_engine(st, false, sensors); if (ret) goto out_unlock; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index 5950e2419ebb..519c1eee96ad 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -88,11 +88,12 @@ enum inv_devices { INV_NUM_PARTS }; -/* chip sensors mask: accelerometer, gyroscope, temperature, magnetometer */ +/* chip sensors mask: accelerometer, gyroscope, temperature, magnetometer, WoM */ #define INV_MPU6050_SENSOR_ACCL BIT(0) #define INV_MPU6050_SENSOR_GYRO BIT(1) #define INV_MPU6050_SENSOR_TEMP BIT(2) #define INV_MPU6050_SENSOR_MAGN BIT(3) +#define INV_MPU6050_SENSOR_WOM BIT(4) /** * struct inv_mpu6050_chip_config - Cached chip configuration data. @@ -104,6 +105,7 @@ enum inv_devices { * @gyro_en: gyro engine enabled * @temp_en: temperature sensor enabled * @magn_en: magn engine (i2c master) enabled + * @wom_en: Wake-on-Motion enabled * @accl_fifo_enable: enable accel data output * @gyro_fifo_enable: enable gyro data output * @temp_fifo_enable: enable temp data output @@ -119,12 +121,14 @@ struct inv_mpu6050_chip_config { unsigned int gyro_en:1; unsigned int temp_en:1; unsigned int magn_en:1; + unsigned int wom_en:1; unsigned int accl_fifo_enable:1; unsigned int gyro_fifo_enable:1; unsigned int temp_fifo_enable:1; unsigned int magn_fifo_enable:1; u8 divider; u8 user_ctrl; + u8 wom_threshold; }; /* @@ -256,12 +260,14 @@ struct inv_mpu6050_state { #define INV_MPU6050_REG_INT_ENABLE 0x38 #define INV_MPU6050_BIT_DATA_RDY_EN 0x01 #define INV_MPU6050_BIT_DMP_INT_EN 0x02 +#define INV_MPU6500_BIT_WOM_INT_EN (BIT(7) | BIT(6) | BIT(5)) #define INV_MPU6050_REG_RAW_ACCEL 0x3B #define INV_MPU6050_REG_TEMPERATURE 0x41 #define INV_MPU6050_REG_RAW_GYRO 0x43 #define INV_MPU6050_REG_INT_STATUS 0x3A +#define INV_MPU6500_BIT_WOM_INT (BIT(7) | BIT(6) | BIT(5)) #define INV_MPU6050_BIT_FIFO_OVERFLOW_INT 0x10 #define INV_MPU6050_BIT_RAW_DATA_RDY_INT 0x01 @@ -301,6 +307,11 @@ struct inv_mpu6050_state { #define INV_MPU6050_BIT_PWR_ACCL_STBY 0x38 #define INV_MPU6050_BIT_PWR_GYRO_STBY 0x07 +/* ICM20609 registers */ +#define INV_ICM20609_REG_ACCEL_WOM_X_THR 0x20 +#define INV_ICM20609_REG_ACCEL_WOM_Y_THR 0x21 +#define INV_ICM20609_REG_ACCEL_WOM_Z_THR 0x22 + /* ICM20602 register */ #define INV_ICM20602_REG_I2C_IF 0x70 #define INV_ICM20602_BIT_I2C_IF_DIS 0x40 @@ -320,6 +331,10 @@ 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_WOM_THRESHOLD 0x1F +#define INV_MPU6500_REG_ACCEL_INTEL_CTRL 0x69 +#define INV_MPU6500_BIT_ACCEL_INTEL_EN BIT(7) +#define INV_MPU6500_BIT_ACCEL_INTEL_MODE BIT(6) #define INV_MPU6500_REG_ACCEL_OFFSET 0x77 /* delay time in milliseconds */ diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c index 66d4ba088e70..13da6f523ca2 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c @@ -33,10 +33,8 @@ static int inv_reset_fifo(struct iio_dev *indio_dev) reset_fifo_fail: dev_err(regmap_get_device(st->map), "reset fifo failed %d\n", result); - result = regmap_write(st->map, st->reg->int_enable, - INV_MPU6050_BIT_DATA_RDY_EN); - - return result; + return regmap_update_bits(st->map, st->reg->int_enable, + INV_MPU6050_BIT_DATA_RDY_EN, INV_MPU6050_BIT_DATA_RDY_EN); } /* diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c index 676704f9151f..ec2398a87f45 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c @@ -134,11 +134,13 @@ int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable) ret = regmap_write(st->map, st->reg->user_ctrl, d); if (ret) return ret; - /* enable interrupt */ - ret = regmap_write(st->map, st->reg->int_enable, - INV_MPU6050_BIT_DATA_RDY_EN); + /* enable data interrupt */ + ret = regmap_update_bits(st->map, st->reg->int_enable, + INV_MPU6050_BIT_DATA_RDY_EN, INV_MPU6050_BIT_DATA_RDY_EN); } else { - ret = regmap_write(st->map, st->reg->int_enable, 0); + /* disable data interrupt */ + ret = regmap_update_bits(st->map, st->reg->int_enable, + INV_MPU6050_BIT_DATA_RDY_EN, 0); if (ret) return ret; ret = regmap_write(st->map, st->reg->fifo_en, 0); @@ -171,9 +173,9 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) return result; /* * In case autosuspend didn't trigger, turn off first not - * required sensors. + * required sensors excepted WoM */ - result = inv_mpu6050_switch_engine(st, false, ~scan); + result = inv_mpu6050_switch_engine(st, false, ~scan & ~INV_MPU6050_SENSOR_WOM); if (result) goto error_power_off; result = inv_mpu6050_switch_engine(st, true, scan);