diff options
Diffstat (limited to 'drivers/iio/imu/inv_mpu6050')
-rw-r--r-- | drivers/iio/imu/inv_mpu6050/Kconfig | 13 | ||||
-rw-r--r-- | drivers/iio/imu/inv_mpu6050/Makefile | 7 | ||||
-rw-r--r-- | drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c | 204 | ||||
-rw-r--r-- | drivers/iio/imu/inv_mpu6050/inv_mpu_aux.h | 19 | ||||
-rw-r--r-- | drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 339 | ||||
-rw-r--r-- | drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 62 | ||||
-rw-r--r-- | drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 110 | ||||
-rw-r--r-- | drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c | 390 | ||||
-rw-r--r-- | drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h | 36 | ||||
-rw-r--r-- | drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c | 37 | ||||
-rw-r--r-- | drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c | 1 | ||||
-rw-r--r-- | drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c | 90 |
12 files changed, 1159 insertions, 149 deletions
diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig index 395f3bd7de0a..017bc0fcc365 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -10,12 +10,14 @@ config INV_MPU6050_IIO config INV_MPU6050_I2C tristate "Invensense MPU6050 devices (I2C)" - depends on I2C_MUX + depends on I2C + select I2C_MUX select INV_MPU6050_IIO select REGMAP_I2C help - This driver supports the Invensense MPU6050/6500/9150 and - ICM20608/20602 motion tracking devices over I2C. + This driver supports the Invensense MPU6050/6500/6515, + MPU9150/9250/9255 and ICM20608/20602 motion tracking devices + over I2C. This driver can be built as a module. The module will be called inv-mpu6050-i2c. @@ -25,7 +27,8 @@ config INV_MPU6050_SPI select INV_MPU6050_IIO select REGMAP_SPI help - This driver supports the Invensense MPU6050/6500/9150 and - ICM20608/20602 motion tracking devices over SPI. + This driver supports the Invensense MPU6000/6500/6515, + MPU9250/9255 and ICM20608/20602 motion tracking devices + over SPI. This driver can be built as a module. The module will be called inv-mpu6050-spi. diff --git a/drivers/iio/imu/inv_mpu6050/Makefile b/drivers/iio/imu/inv_mpu6050/Makefile index 70ffe0d13d8c..c103441a906b 100644 --- a/drivers/iio/imu/inv_mpu6050/Makefile +++ b/drivers/iio/imu/inv_mpu6050/Makefile @@ -4,10 +4,11 @@ # obj-$(CONFIG_INV_MPU6050_IIO) += inv-mpu6050.o -inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o +inv-mpu6050-y := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o \ + inv_mpu_aux.o inv_mpu_magn.o obj-$(CONFIG_INV_MPU6050_I2C) += inv-mpu6050-i2c.o -inv-mpu6050-i2c-objs := inv_mpu_i2c.o inv_mpu_acpi.o +inv-mpu6050-i2c-y := inv_mpu_i2c.o inv_mpu_acpi.o obj-$(CONFIG_INV_MPU6050_SPI) += inv-mpu6050-spi.o -inv-mpu6050-spi-objs := inv_mpu_spi.o +inv-mpu6050-spi-y := inv_mpu_spi.o diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c new file mode 100644 index 000000000000..7327e5723f96 --- /dev/null +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 TDK-InvenSense, Inc. + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/regmap.h> +#include <linux/delay.h> + +#include "inv_mpu_aux.h" +#include "inv_mpu_iio.h" + +/* + * i2c master auxiliary bus transfer function. + * Requires the i2c operations to be correctly setup before. + */ +static int inv_mpu_i2c_master_xfer(const struct inv_mpu6050_state *st) +{ + /* use 50hz frequency for xfer */ + const unsigned int freq = 50; + const unsigned int period_ms = 1000 / freq; + uint8_t d; + unsigned int user_ctrl; + int ret; + + /* set sample rate */ + d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(freq); + ret = regmap_write(st->map, st->reg->sample_rate_div, d); + if (ret) + return ret; + + /* start i2c master */ + user_ctrl = st->chip_config.user_ctrl | INV_MPU6050_BIT_I2C_MST_EN; + ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl); + if (ret) + goto error_restore_rate; + + /* wait for xfer: 1 period + half-period margin */ + msleep(period_ms + period_ms / 2); + + /* stop i2c master */ + user_ctrl = st->chip_config.user_ctrl; + ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl); + if (ret) + goto error_stop_i2c; + + /* restore sample rate */ + d = st->chip_config.divider; + ret = regmap_write(st->map, st->reg->sample_rate_div, d); + if (ret) + goto error_restore_rate; + + return 0; + +error_stop_i2c: + regmap_write(st->map, st->reg->user_ctrl, st->chip_config.user_ctrl); +error_restore_rate: + regmap_write(st->map, st->reg->sample_rate_div, st->chip_config.divider); + return ret; +} + +/** + * inv_mpu_aux_init() - init i2c auxiliary bus + * @st: driver internal state + * + * Returns 0 on success, a negative error code otherwise. + */ +int inv_mpu_aux_init(const struct inv_mpu6050_state *st) +{ + unsigned int val; + int ret; + + /* configure i2c master */ + val = INV_MPU6050_BITS_I2C_MST_CLK_400KHZ | + INV_MPU6050_BIT_WAIT_FOR_ES; + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_MST_CTRL, val); + if (ret) + return ret; + + /* configure i2c master delay */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV4_CTRL, 0); + if (ret) + return ret; + + val = INV_MPU6050_BIT_I2C_SLV0_DLY_EN | + INV_MPU6050_BIT_I2C_SLV1_DLY_EN | + INV_MPU6050_BIT_I2C_SLV2_DLY_EN | + INV_MPU6050_BIT_I2C_SLV3_DLY_EN | + INV_MPU6050_BIT_DELAY_ES_SHADOW; + return regmap_write(st->map, INV_MPU6050_REG_I2C_MST_DELAY_CTRL, val); +} + +/** + * inv_mpu_aux_read() - read register function for i2c auxiliary bus + * @st: driver internal state. + * @addr: chip i2c Address + * @reg: chip register address + * @val: buffer for storing read bytes + * @size: number of bytes to read + * + * Returns 0 on success, a negative error code otherwise. + */ +int inv_mpu_aux_read(const struct inv_mpu6050_state *st, uint8_t addr, + uint8_t reg, uint8_t *val, size_t size) +{ + unsigned int status; + int ret; + + if (size > 0x0F) + return -EINVAL; + + /* setup i2c SLV0 control: i2c addr, register, enable + size */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_ADDR(0), + INV_MPU6050_BIT_I2C_SLV_RNW | addr); + if (ret) + return ret; + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_REG(0), reg); + if (ret) + return ret; + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), + INV_MPU6050_BIT_SLV_EN | size); + if (ret) + return ret; + + /* do i2c xfer */ + ret = inv_mpu_i2c_master_xfer(st); + if (ret) + goto error_disable_i2c; + + /* disable i2c slave */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0); + if (ret) + goto error_disable_i2c; + + /* check i2c status */ + ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status); + if (ret) + return ret; + if (status & INV_MPU6050_BIT_I2C_SLV0_NACK) + return -EIO; + + /* read data in registers */ + return regmap_bulk_read(st->map, INV_MPU6050_REG_EXT_SENS_DATA, + val, size); + +error_disable_i2c: + regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0); + return ret; +} + +/** + * inv_mpu_aux_write() - write register function for i2c auxiliary bus + * @st: driver internal state. + * @addr: chip i2c Address + * @reg: chip register address + * @val: 1 byte value to write + * + * Returns 0 on success, a negative error code otherwise. + */ +int inv_mpu_aux_write(const struct inv_mpu6050_state *st, uint8_t addr, + uint8_t reg, uint8_t val) +{ + unsigned int status; + int ret; + + /* setup i2c SLV0 control: i2c addr, register, value, enable + size */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_ADDR(0), addr); + if (ret) + return ret; + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_REG(0), reg); + if (ret) + return ret; + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_DO(0), val); + if (ret) + return ret; + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), + INV_MPU6050_BIT_SLV_EN | 1); + if (ret) + return ret; + + /* do i2c xfer */ + ret = inv_mpu_i2c_master_xfer(st); + if (ret) + goto error_disable_i2c; + + /* disable i2c slave */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0); + if (ret) + goto error_disable_i2c; + + /* check i2c status */ + ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status); + if (ret) + return ret; + if (status & INV_MPU6050_BIT_I2C_SLV0_NACK) + return -EIO; + + return 0; + +error_disable_i2c: + regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0); + return ret; +} diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.h new file mode 100644 index 000000000000..b66997545762 --- /dev/null +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 TDK-InvenSense, Inc. + */ + +#ifndef INV_MPU_AUX_H_ +#define INV_MPU_AUX_H_ + +#include "inv_mpu_iio.h" + +int inv_mpu_aux_init(const struct inv_mpu6050_state *st); + +int inv_mpu_aux_read(const struct inv_mpu6050_state *st, uint8_t addr, + uint8_t reg, uint8_t *val, size_t size); + +int inv_mpu_aux_write(const struct inv_mpu6050_state *st, uint8_t addr, + uint8_t reg, uint8_t val); + +#endif /* INV_MPU_AUX_H_ */ diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 8a704cd5bddb..5096fc49012d 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -17,6 +17,7 @@ #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include "inv_mpu_iio.h" +#include "inv_mpu_magn.h" /* * this is the gyro scale translated from dynamic range plus/minus @@ -103,6 +104,8 @@ static const struct inv_mpu6050_chip_config chip_config_6050 = { .divider = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU6050_INIT_FIFO_RATE), .gyro_fifo_enable = false, .accl_fifo_enable = false, + .temp_fifo_enable = false, + .magn_fifo_enable = false, .accl_fs = INV_MPU6050_FS_02G, .user_ctrl = 0, }; @@ -114,54 +117,72 @@ static const struct inv_mpu6050_hw hw_info[] = { .name = "MPU6050", .reg = ®_set_6050, .config = &chip_config_6050, + .fifo_size = 1024, + .temp = {INV_MPU6050_TEMP_OFFSET, INV_MPU6050_TEMP_SCALE}, }, { .whoami = INV_MPU6500_WHOAMI_VALUE, .name = "MPU6500", .reg = ®_set_6500, .config = &chip_config_6050, + .fifo_size = 512, + .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE}, }, { .whoami = INV_MPU6515_WHOAMI_VALUE, .name = "MPU6515", .reg = ®_set_6500, .config = &chip_config_6050, + .fifo_size = 512, + .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE}, }, { .whoami = INV_MPU6000_WHOAMI_VALUE, .name = "MPU6000", .reg = ®_set_6050, .config = &chip_config_6050, + .fifo_size = 1024, + .temp = {INV_MPU6050_TEMP_OFFSET, INV_MPU6050_TEMP_SCALE}, }, { .whoami = INV_MPU9150_WHOAMI_VALUE, .name = "MPU9150", .reg = ®_set_6050, .config = &chip_config_6050, + .fifo_size = 1024, + .temp = {INV_MPU6050_TEMP_OFFSET, INV_MPU6050_TEMP_SCALE}, }, { .whoami = INV_MPU9250_WHOAMI_VALUE, .name = "MPU9250", .reg = ®_set_6500, .config = &chip_config_6050, + .fifo_size = 512, + .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE}, }, { .whoami = INV_MPU9255_WHOAMI_VALUE, .name = "MPU9255", .reg = ®_set_6500, .config = &chip_config_6050, + .fifo_size = 512, + .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE}, }, { .whoami = INV_ICM20608_WHOAMI_VALUE, .name = "ICM20608", .reg = ®_set_6500, .config = &chip_config_6050, + .fifo_size = 512, + .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, }, { .whoami = INV_ICM20602_WHOAMI_VALUE, .name = "ICM20602", .reg = ®_set_icm20602, .config = &chip_config_6050, + .fifo_size = 1008, + .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, }, }; @@ -332,6 +353,11 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev) */ st->chip_period = NSEC_PER_MSEC; + /* magn chip init, noop if not present in the chip */ + result = inv_mpu_magn_probe(st); + if (result) + goto error_power_off; + return inv_mpu6050_set_power_itg(st, false); error_power_off: @@ -411,6 +437,9 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev, ret = inv_mpu6050_sensor_show(st, st->reg->temperature, IIO_MOD_X, val); break; + case IIO_MAGN: + ret = inv_mpu_magn_read(st, chan->channel2, val); + break; default: ret = -EINVAL; break; @@ -462,24 +491,18 @@ inv_mpu6050_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT_PLUS_MICRO; case IIO_TEMP: - *val = 0; - if (st->chip_type == INV_ICM20602) - *val2 = INV_ICM20602_TEMP_SCALE; - else - *val2 = INV_MPU6050_TEMP_SCALE; - + *val = st->hw->temp.scale / 1000000; + *val2 = st->hw->temp.scale % 1000000; return IIO_VAL_INT_PLUS_MICRO; + case IIO_MAGN: + return inv_mpu_magn_get_scale(st, chan, val, val2); default: return -EINVAL; } case IIO_CHAN_INFO_OFFSET: switch (chan->type) { case IIO_TEMP: - if (st->chip_type == INV_ICM20602) - *val = INV_ICM20602_TEMP_OFFSET; - else - *val = INV_MPU6050_TEMP_OFFSET; - + *val = st->hw->temp.offset; return IIO_VAL_INT; default: return -EINVAL; @@ -710,6 +733,11 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr, if (result) goto fifo_rate_fail_power_off; + /* update rate for magn, noop if not present in chip */ + result = inv_mpu_magn_set_rate(st, fifo_rate); + if (result) + goto fifo_rate_fail_power_off; + fifo_rate_fail_power_off: result |= inv_mpu6050_set_power_itg(st, false); fifo_rate_fail_unlock: @@ -795,8 +823,14 @@ inv_get_mount_matrix(const struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { struct inv_mpu6050_state *data = iio_priv(indio_dev); + const struct iio_mount_matrix *matrix; + + if (chan->type == IIO_MAGN) + matrix = &data->magn_orient; + else + matrix = &data->orientation; - return &data->orientation; + return matrix; } static const struct iio_chan_spec_ext_info inv_ext_info[] = { @@ -823,19 +857,27 @@ static const struct iio_chan_spec_ext_info inv_ext_info[] = { .ext_info = inv_ext_info, \ } +#define INV_MPU6050_TEMP_CHAN(_index) \ + { \ + .type = IIO_TEMP, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ + | BIT(IIO_CHAN_INFO_OFFSET) \ + | BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .shift = 0, \ + .endianness = IIO_BE, \ + }, \ + } + static const struct iio_chan_spec inv_mpu_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP), - /* - * Note that temperature should only be via polled reading only, - * not the final scan elements output. - */ - { - .type = IIO_TEMP, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) - | BIT(IIO_CHAN_INFO_OFFSET) - | BIT(IIO_CHAN_INFO_SCALE), - .scan_index = -1, - }, + + INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP), + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X), INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y), INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z), @@ -845,70 +887,132 @@ static const struct iio_chan_spec inv_mpu_channels[] = { INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z), }; +#define INV_MPU6050_SCAN_MASK_3AXIS_ACCEL \ + (BIT(INV_MPU6050_SCAN_ACCL_X) \ + | BIT(INV_MPU6050_SCAN_ACCL_Y) \ + | BIT(INV_MPU6050_SCAN_ACCL_Z)) + +#define INV_MPU6050_SCAN_MASK_3AXIS_GYRO \ + (BIT(INV_MPU6050_SCAN_GYRO_X) \ + | BIT(INV_MPU6050_SCAN_GYRO_Y) \ + | BIT(INV_MPU6050_SCAN_GYRO_Z)) + +#define INV_MPU6050_SCAN_MASK_TEMP (BIT(INV_MPU6050_SCAN_TEMP)) + static const unsigned long inv_mpu_scan_masks[] = { /* 3-axis accel */ - BIT(INV_MPU6050_SCAN_ACCL_X) - | BIT(INV_MPU6050_SCAN_ACCL_Y) - | BIT(INV_MPU6050_SCAN_ACCL_Z), + INV_MPU6050_SCAN_MASK_3AXIS_ACCEL, + INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_TEMP, /* 3-axis gyro */ - BIT(INV_MPU6050_SCAN_GYRO_X) - | BIT(INV_MPU6050_SCAN_GYRO_Y) - | BIT(INV_MPU6050_SCAN_GYRO_Z), + INV_MPU6050_SCAN_MASK_3AXIS_GYRO, + INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU6050_SCAN_MASK_TEMP, /* 6-axis accel + gyro */ - BIT(INV_MPU6050_SCAN_ACCL_X) - | BIT(INV_MPU6050_SCAN_ACCL_Y) - | BIT(INV_MPU6050_SCAN_ACCL_Z) - | BIT(INV_MPU6050_SCAN_GYRO_X) - | BIT(INV_MPU6050_SCAN_GYRO_Y) - | BIT(INV_MPU6050_SCAN_GYRO_Z), + INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO, + INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO + | INV_MPU6050_SCAN_MASK_TEMP, 0, }; -static const struct iio_chan_spec inv_icm20602_channels[] = { - IIO_CHAN_SOFT_TIMESTAMP(INV_ICM20602_SCAN_TIMESTAMP), - { - .type = IIO_TEMP, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) - | BIT(IIO_CHAN_INFO_OFFSET) - | BIT(IIO_CHAN_INFO_SCALE), - .scan_index = INV_ICM20602_SCAN_TEMP, - .scan_type = { - .sign = 's', - .realbits = 16, - .storagebits = 16, - .shift = 0, - .endianness = IIO_BE, - }, - }, +#define INV_MPU9X50_MAGN_CHAN(_chan2, _bits, _index) \ + { \ + .type = IIO_MAGN, \ + .modified = 1, \ + .channel2 = _chan2, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_RAW), \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = _bits, \ + .storagebits = 16, \ + .shift = 0, \ + .endianness = IIO_BE, \ + }, \ + .ext_info = inv_ext_info, \ + } + +static const struct iio_chan_spec inv_mpu9150_channels[] = { + IIO_CHAN_SOFT_TIMESTAMP(INV_MPU9X50_SCAN_TIMESTAMP), + + INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP), + + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X), + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y), + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z), + + INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X), + INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y), + INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z), - INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_ICM20602_SCAN_GYRO_X), - INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_ICM20602_SCAN_GYRO_Y), - INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_ICM20602_SCAN_GYRO_Z), + /* Magnetometer resolution is 13 bits */ + INV_MPU9X50_MAGN_CHAN(IIO_MOD_X, 13, INV_MPU9X50_SCAN_MAGN_X), + INV_MPU9X50_MAGN_CHAN(IIO_MOD_Y, 13, INV_MPU9X50_SCAN_MAGN_Y), + INV_MPU9X50_MAGN_CHAN(IIO_MOD_Z, 13, INV_MPU9X50_SCAN_MAGN_Z), +}; + +static const struct iio_chan_spec inv_mpu9250_channels[] = { + IIO_CHAN_SOFT_TIMESTAMP(INV_MPU9X50_SCAN_TIMESTAMP), + + INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP), + + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X), + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y), + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z), + + INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X), + INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y), + INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z), - INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_ICM20602_SCAN_ACCL_Y), - INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_ICM20602_SCAN_ACCL_X), - INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_ICM20602_SCAN_ACCL_Z), + /* Magnetometer resolution is 16 bits */ + INV_MPU9X50_MAGN_CHAN(IIO_MOD_X, 16, INV_MPU9X50_SCAN_MAGN_X), + INV_MPU9X50_MAGN_CHAN(IIO_MOD_Y, 16, INV_MPU9X50_SCAN_MAGN_Y), + INV_MPU9X50_MAGN_CHAN(IIO_MOD_Z, 16, INV_MPU9X50_SCAN_MAGN_Z), +}; + +#define INV_MPU9X50_SCAN_MASK_3AXIS_MAGN \ + (BIT(INV_MPU9X50_SCAN_MAGN_X) \ + | BIT(INV_MPU9X50_SCAN_MAGN_Y) \ + | BIT(INV_MPU9X50_SCAN_MAGN_Z)) + +static const unsigned long inv_mpu9x50_scan_masks[] = { + /* 3-axis accel */ + INV_MPU6050_SCAN_MASK_3AXIS_ACCEL, + INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_TEMP, + /* 3-axis gyro */ + INV_MPU6050_SCAN_MASK_3AXIS_GYRO, + INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU6050_SCAN_MASK_TEMP, + /* 3-axis magn */ + INV_MPU9X50_SCAN_MASK_3AXIS_MAGN, + INV_MPU9X50_SCAN_MASK_3AXIS_MAGN | INV_MPU6050_SCAN_MASK_TEMP, + /* 6-axis accel + gyro */ + INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO, + INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO + | INV_MPU6050_SCAN_MASK_TEMP, + /* 6-axis accel + magn */ + INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN, + INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN + | INV_MPU6050_SCAN_MASK_TEMP, + /* 6-axis gyro + magn */ + INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN, + INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN + | INV_MPU6050_SCAN_MASK_TEMP, + /* 9-axis accel + gyro + magn */ + INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO + | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN, + INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO + | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN + | INV_MPU6050_SCAN_MASK_TEMP, + 0, }; static const unsigned long inv_icm20602_scan_masks[] = { /* 3-axis accel + temp (mandatory) */ - BIT(INV_ICM20602_SCAN_ACCL_X) - | BIT(INV_ICM20602_SCAN_ACCL_Y) - | BIT(INV_ICM20602_SCAN_ACCL_Z) - | BIT(INV_ICM20602_SCAN_TEMP), + INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_TEMP, /* 3-axis gyro + temp (mandatory) */ - BIT(INV_ICM20602_SCAN_GYRO_X) - | BIT(INV_ICM20602_SCAN_GYRO_Y) - | BIT(INV_ICM20602_SCAN_GYRO_Z) - | BIT(INV_ICM20602_SCAN_TEMP), + INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU6050_SCAN_MASK_TEMP, /* 6-axis accel + gyro + temp (mandatory) */ - BIT(INV_ICM20602_SCAN_ACCL_X) - | BIT(INV_ICM20602_SCAN_ACCL_Y) - | BIT(INV_ICM20602_SCAN_ACCL_Z) - | BIT(INV_ICM20602_SCAN_GYRO_X) - | BIT(INV_ICM20602_SCAN_GYRO_Y) - | BIT(INV_ICM20602_SCAN_GYRO_Z) - | BIT(INV_ICM20602_SCAN_TEMP), + INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO + | INV_MPU6050_SCAN_MASK_TEMP, 0, }; @@ -1025,14 +1129,14 @@ error_power_off: return result; } -static int inv_mpu_core_enable_regulator(struct inv_mpu6050_state *st) +static int inv_mpu_core_enable_regulator_vddio(struct inv_mpu6050_state *st) { int result; result = regulator_enable(st->vddio_supply); if (result) { dev_err(regmap_get_device(st->map), - "Failed to enable regulator: %d\n", result); + "Failed to enable vddio regulator: %d\n", result); } else { /* Give the device a little bit of time to start up. */ usleep_range(35000, 70000); @@ -1041,21 +1145,29 @@ static int inv_mpu_core_enable_regulator(struct inv_mpu6050_state *st) return result; } -static int inv_mpu_core_disable_regulator(struct inv_mpu6050_state *st) +static int inv_mpu_core_disable_regulator_vddio(struct inv_mpu6050_state *st) { int result; result = regulator_disable(st->vddio_supply); if (result) dev_err(regmap_get_device(st->map), - "Failed to disable regulator: %d\n", result); + "Failed to disable vddio regulator: %d\n", result); return result; } static void inv_mpu_core_disable_regulator_action(void *_data) { - inv_mpu_core_disable_regulator(_data); + struct inv_mpu6050_state *st = _data; + int result; + + result = regulator_disable(st->vdd_supply); + if (result) + dev_err(regmap_get_device(st->map), + "Failed to disable vdd regulator: %d\n", result); + + inv_mpu_core_disable_regulator_vddio(st); } int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, @@ -1108,7 +1220,7 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, irq_type = irqd_get_trigger_type(desc); if (!irq_type) irq_type = IRQF_TRIGGER_RISING; - if (irq_type == IRQF_TRIGGER_RISING) + if (irq_type & IRQF_TRIGGER_RISING) // rising or both-edge st->irq_mask = INV_MPU6050_ACTIVE_HIGH; else if (irq_type == IRQF_TRIGGER_FALLING) st->irq_mask = INV_MPU6050_ACTIVE_LOW; @@ -1124,6 +1236,15 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, return -EINVAL; } + st->vdd_supply = devm_regulator_get(dev, "vdd"); + if (IS_ERR(st->vdd_supply)) { + if (PTR_ERR(st->vdd_supply) != -EPROBE_DEFER) + dev_err(dev, "Failed to get vdd regulator %d\n", + (int)PTR_ERR(st->vdd_supply)); + + return PTR_ERR(st->vdd_supply); + } + st->vddio_supply = devm_regulator_get(dev, "vddio"); if (IS_ERR(st->vddio_supply)) { if (PTR_ERR(st->vddio_supply) != -EPROBE_DEFER) @@ -1133,19 +1254,31 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, return PTR_ERR(st->vddio_supply); } - result = inv_mpu_core_enable_regulator(st); - if (result) + result = regulator_enable(st->vdd_supply); + if (result) { + dev_err(dev, "Failed to enable vdd regulator: %d\n", result); return result; + } - result = devm_add_action(dev, inv_mpu_core_disable_regulator_action, + result = inv_mpu_core_enable_regulator_vddio(st); + if (result) { + regulator_disable(st->vdd_supply); + return result; + } + + result = devm_add_action_or_reset(dev, inv_mpu_core_disable_regulator_action, st); if (result) { - inv_mpu_core_disable_regulator_action(st); dev_err(dev, "Failed to setup regulator cleanup action %d\n", result); return result; } + /* fill magnetometer orientation */ + result = inv_mpu_magn_set_orient(st); + if (result) + return result; + /* power is turned on inside check chip type*/ result = inv_check_and_setup_chip(st); if (result) @@ -1157,9 +1290,6 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, return result; } - if (inv_mpu_bus_setup) - inv_mpu_bus_setup(indio_dev); - dev_set_drvdata(dev, indio_dev); indio_dev->dev.parent = dev; /* name will be NULL when enumerated via ACPI */ @@ -1168,11 +1298,38 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, else indio_dev->name = dev_name(dev); - if (chip_type == INV_ICM20602) { - indio_dev->channels = inv_icm20602_channels; - indio_dev->num_channels = ARRAY_SIZE(inv_icm20602_channels); + /* requires parent device set in indio_dev */ + if (inv_mpu_bus_setup) + inv_mpu_bus_setup(indio_dev); + + switch (chip_type) { + case INV_MPU9150: + indio_dev->channels = inv_mpu9150_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_mpu9150_channels); + indio_dev->available_scan_masks = inv_mpu9x50_scan_masks; + break; + case INV_MPU9250: + case INV_MPU9255: + indio_dev->channels = inv_mpu9250_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_mpu9250_channels); + indio_dev->available_scan_masks = inv_mpu9x50_scan_masks; + break; + case INV_ICM20602: + indio_dev->channels = inv_mpu_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels); indio_dev->available_scan_masks = inv_icm20602_scan_masks; - } else { + break; + default: + indio_dev->channels = inv_mpu_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels); + indio_dev->available_scan_masks = inv_mpu_scan_masks; + break; + } + /* + * Use magnetometer inside the chip only if there is no i2c + * auxiliary device in use. Otherwise Going back to 6-axis only. + */ + if (st->magn_disabled) { indio_dev->channels = inv_mpu_channels; indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels); indio_dev->available_scan_masks = inv_mpu_scan_masks; @@ -1213,7 +1370,7 @@ static int inv_mpu_resume(struct device *dev) int result; mutex_lock(&st->lock); - result = inv_mpu_core_enable_regulator(st); + result = inv_mpu_core_enable_regulator_vddio(st); if (result) goto out_unlock; @@ -1231,7 +1388,7 @@ static int inv_mpu_suspend(struct device *dev) mutex_lock(&st->lock); result = inv_mpu6050_set_power_itg(st, false); - inv_mpu_core_disable_regulator(st); + inv_mpu_core_disable_regulator_vddio(st); mutex_unlock(&st->lock); return result; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index 4b8b5a87398c..f47a28b4be23 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -68,6 +68,58 @@ static const char *inv_mpu_match_acpi_device(struct device *dev, return dev_name(dev); } +static bool inv_mpu_i2c_aux_bus(struct device *dev) +{ + struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev)); + + switch (st->chip_type) { + case INV_ICM20608: + case INV_ICM20602: + /* no i2c auxiliary bus on the chip */ + return false; + case INV_MPU9150: + case INV_MPU9250: + case INV_MPU9255: + if (st->magn_disabled) + return true; + else + return false; + default: + return true; + } +} + +/* + * MPU9xxx magnetometer support requires to disable i2c auxiliary bus support. + * To ensure backward compatibility with existing setups, do not disable + * i2c auxiliary bus if it used. + * Check for i2c-gate node in devicetree and set magnetometer disabled. + * Only MPU6500 is supported by ACPI, no need to check. + */ +static int inv_mpu_magn_disable(struct iio_dev *indio_dev) +{ + struct inv_mpu6050_state *st = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + struct device_node *mux_node; + + switch (st->chip_type) { + case INV_MPU9150: + case INV_MPU9250: + case INV_MPU9255: + mux_node = of_get_child_by_name(dev->of_node, "i2c-gate"); + if (mux_node != NULL) { + st->magn_disabled = true; + dev_warn(dev, "disable internal use of magnetometer\n"); + } + of_node_put(mux_node); + break; + default: + break; + } + + return 0; +} + /** * inv_mpu_probe() - probe function. * @client: i2c client. @@ -112,17 +164,12 @@ static int inv_mpu_probe(struct i2c_client *client, } result = inv_mpu_core_probe(regmap, client->irq, name, - NULL, chip_type); + inv_mpu_magn_disable, chip_type); if (result < 0) return result; st = iio_priv(dev_get_drvdata(&client->dev)); - switch (st->chip_type) { - case INV_ICM20608: - case INV_ICM20602: - /* no i2c auxiliary bus on the chip */ - break; - default: + if (inv_mpu_i2c_aux_bus(&client->dev)) { /* declare i2c auxiliary bus */ st->muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE, @@ -137,7 +184,6 @@ static int inv_mpu_probe(struct i2c_client *client, result = inv_mpu_acpi_create_mux_client(client); if (result) goto out_del_mux; - break; } return 0; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index db1c6904388b..6158fca7f70e 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -2,6 +2,10 @@ /* * Copyright (C) 2012 Invensense, Inc. */ + +#ifndef INV_MPU_IIO_H_ +#define INV_MPU_IIO_H_ + #include <linux/i2c.h> #include <linux/i2c-mux.h> #include <linux/mutex.h> @@ -82,6 +86,8 @@ enum inv_devices { * @accl_fs: accel full scale range. * @accl_fifo_enable: enable accel data output * @gyro_fifo_enable: enable gyro data output + * @temp_fifo_enable: enable temp data output + * @magn_fifo_enable: enable magn data output * @divider: chip sample rate divider (sample rate divider - 1) */ struct inv_mpu6050_chip_config { @@ -90,6 +96,8 @@ struct inv_mpu6050_chip_config { unsigned int accl_fs:2; 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; }; @@ -100,12 +108,19 @@ struct inv_mpu6050_chip_config { * @name: name of the chip. * @reg: register map of the chip. * @config: configuration of the chip. + * @fifo_size: size of the FIFO in bytes. + * @temp: offset and scale to apply to raw temperature. */ struct inv_mpu6050_hw { u8 whoami; u8 *name; const struct inv_mpu6050_reg_map *reg; const struct inv_mpu6050_chip_config *config; + size_t fifo_size; + struct { + int offset; + int scale; + } temp; }; /* @@ -124,7 +139,11 @@ struct inv_mpu6050_hw { * @chip_period: chip internal period estimation (~1kHz). * @it_timestamp: timestamp from previous interrupt. * @data_timestamp: timestamp for next data sample. - * @vddio_supply voltage regulator for the chip. + * @vdd_supply: VDD voltage regulator for the chip. + * @vddio_supply I/O voltage regulator for the chip. + * @magn_disabled: magnetometer disabled for backward compatibility reason. + * @magn_raw_to_gauss: coefficient to convert mag raw value to Gauss. + * @magn_orient: magnetometer sensor chip orientation if available. */ struct inv_mpu6050_state { struct mutex lock; @@ -145,7 +164,11 @@ struct inv_mpu6050_state { s64 chip_period; s64 it_timestamp; s64 data_timestamp; + struct regulator *vdd_supply; struct regulator *vddio_supply; + bool magn_disabled; + s32 magn_raw_to_gauss[3]; + struct iio_mount_matrix magn_orient; }; /*register and associated bit definition*/ @@ -158,8 +181,41 @@ struct inv_mpu6050_state { #define INV_MPU6050_REG_ACCEL_CONFIG 0x1C #define INV_MPU6050_REG_FIFO_EN 0x23 +#define INV_MPU6050_BIT_SLAVE_0 0x01 +#define INV_MPU6050_BIT_SLAVE_1 0x02 +#define INV_MPU6050_BIT_SLAVE_2 0x04 #define INV_MPU6050_BIT_ACCEL_OUT 0x08 #define INV_MPU6050_BITS_GYRO_OUT 0x70 +#define INV_MPU6050_BIT_TEMP_OUT 0x80 + +#define INV_MPU6050_REG_I2C_MST_CTRL 0x24 +#define INV_MPU6050_BITS_I2C_MST_CLK_400KHZ 0x0D +#define INV_MPU6050_BIT_I2C_MST_P_NSR 0x10 +#define INV_MPU6050_BIT_SLV3_FIFO_EN 0x20 +#define INV_MPU6050_BIT_WAIT_FOR_ES 0x40 +#define INV_MPU6050_BIT_MULT_MST_EN 0x80 + +/* control I2C slaves from 0 to 3 */ +#define INV_MPU6050_REG_I2C_SLV_ADDR(_x) (0x25 + 3 * (_x)) +#define INV_MPU6050_BIT_I2C_SLV_RNW 0x80 + +#define INV_MPU6050_REG_I2C_SLV_REG(_x) (0x26 + 3 * (_x)) + +#define INV_MPU6050_REG_I2C_SLV_CTRL(_x) (0x27 + 3 * (_x)) +#define INV_MPU6050_BIT_SLV_GRP 0x10 +#define INV_MPU6050_BIT_SLV_REG_DIS 0x20 +#define INV_MPU6050_BIT_SLV_BYTE_SW 0x40 +#define INV_MPU6050_BIT_SLV_EN 0x80 + +/* I2C master delay register */ +#define INV_MPU6050_REG_I2C_SLV4_CTRL 0x34 +#define INV_MPU6050_BITS_I2C_MST_DLY(_x) ((_x) & 0x1F) + +#define INV_MPU6050_REG_I2C_MST_STATUS 0x36 +#define INV_MPU6050_BIT_I2C_SLV0_NACK 0x01 +#define INV_MPU6050_BIT_I2C_SLV1_NACK 0x02 +#define INV_MPU6050_BIT_I2C_SLV2_NACK 0x04 +#define INV_MPU6050_BIT_I2C_SLV3_NACK 0x08 #define INV_MPU6050_REG_INT_ENABLE 0x38 #define INV_MPU6050_BIT_DATA_RDY_EN 0x01 @@ -173,6 +229,18 @@ struct inv_mpu6050_state { #define INV_MPU6050_BIT_FIFO_OVERFLOW_INT 0x10 #define INV_MPU6050_BIT_RAW_DATA_RDY_INT 0x01 +#define INV_MPU6050_REG_EXT_SENS_DATA 0x49 + +/* I2C slaves data output from 0 to 3 */ +#define INV_MPU6050_REG_I2C_SLV_DO(_x) (0x63 + (_x)) + +#define INV_MPU6050_REG_I2C_MST_DELAY_CTRL 0x67 +#define INV_MPU6050_BIT_I2C_SLV0_DLY_EN 0x01 +#define INV_MPU6050_BIT_I2C_SLV1_DLY_EN 0x02 +#define INV_MPU6050_BIT_I2C_SLV2_DLY_EN 0x04 +#define INV_MPU6050_BIT_I2C_SLV3_DLY_EN 0x08 +#define INV_MPU6050_BIT_DELAY_ES_SHADOW 0x80 + #define INV_MPU6050_REG_USER_CTRL 0x6A #define INV_MPU6050_BIT_FIFO_RST 0x04 #define INV_MPU6050_BIT_DMP_RST 0x08 @@ -200,8 +268,11 @@ struct inv_mpu6050_state { #define INV_MPU6050_BYTES_PER_3AXIS_SENSOR 6 #define INV_MPU6050_FIFO_COUNT_BYTE 2 -/* ICM20602 FIFO samples include temperature readings */ -#define INV_ICM20602_BYTES_PER_TEMP_SENSOR 2 +/* MPU9X50 9-axis magnetometer */ +#define INV_MPU9X50_BYTES_MAGN 7 + +/* FIFO temperature sample size */ +#define INV_MPU6050_BYTES_PER_TEMP_SENSOR 2 /* mpu6500 registers */ #define INV_MPU6500_REG_ACCEL_CONFIG_2 0x1D @@ -216,19 +287,22 @@ struct inv_mpu6050_state { #define INV_MPU6050_REG_UP_TIME_MIN 5000 #define INV_MPU6050_REG_UP_TIME_MAX 10000 -#define INV_MPU6050_TEMP_OFFSET 12421 -#define INV_MPU6050_TEMP_SCALE 2941 +#define INV_MPU6050_TEMP_OFFSET 12420 +#define INV_MPU6050_TEMP_SCALE 2941176 #define INV_MPU6050_MAX_GYRO_FS_PARAM 3 #define INV_MPU6050_MAX_ACCL_FS_PARAM 3 #define INV_MPU6050_THREE_AXIS 3 #define INV_MPU6050_GYRO_CONFIG_FSR_SHIFT 3 #define INV_MPU6050_ACCL_CONFIG_FSR_SHIFT 3 -#define INV_ICM20602_TEMP_OFFSET 8170 -#define INV_ICM20602_TEMP_SCALE 3060 +#define INV_MPU6500_TEMP_OFFSET 7011 +#define INV_MPU6500_TEMP_SCALE 2995178 -/* 6 + 6 round up and plus 8 */ -#define INV_MPU6050_OUTPUT_DATA_SIZE 24 +#define INV_ICM20608_TEMP_OFFSET 8170 +#define INV_ICM20608_TEMP_SCALE 3059976 + +/* 6 + 6 + 2 + 7 (for MPU9x50) = 21 round up to 24 and plus 8 */ +#define INV_MPU6050_OUTPUT_DATA_SIZE 32 #define INV_MPU6050_REG_INT_PIN_CFG 0x37 #define INV_MPU6050_ACTIVE_HIGH 0x00 @@ -273,22 +347,16 @@ enum inv_mpu6050_scan { INV_MPU6050_SCAN_ACCL_X, INV_MPU6050_SCAN_ACCL_Y, INV_MPU6050_SCAN_ACCL_Z, + INV_MPU6050_SCAN_TEMP, INV_MPU6050_SCAN_GYRO_X, INV_MPU6050_SCAN_GYRO_Y, INV_MPU6050_SCAN_GYRO_Z, INV_MPU6050_SCAN_TIMESTAMP, -}; -/* scan element definition for ICM20602, which includes temperature */ -enum inv_icm20602_scan { - INV_ICM20602_SCAN_ACCL_X, - INV_ICM20602_SCAN_ACCL_Y, - INV_ICM20602_SCAN_ACCL_Z, - INV_ICM20602_SCAN_TEMP, - INV_ICM20602_SCAN_GYRO_X, - INV_ICM20602_SCAN_GYRO_Y, - INV_ICM20602_SCAN_GYRO_Z, - INV_ICM20602_SCAN_TIMESTAMP, + INV_MPU9X50_SCAN_MAGN_X = INV_MPU6050_SCAN_GYRO_Z + 1, + INV_MPU9X50_SCAN_MAGN_Y, + INV_MPU9X50_SCAN_MAGN_Z, + INV_MPU9X50_SCAN_TIMESTAMP, }; enum inv_mpu6050_filter_e { @@ -342,3 +410,5 @@ void inv_mpu_acpi_delete_mux_client(struct i2c_client *client); int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type); extern const struct dev_pm_ops inv_mpu_pmops; + +#endif diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c new file mode 100644 index 000000000000..4f192352521e --- /dev/null +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 TDK-InvenSense, Inc. + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/string.h> + +#include "inv_mpu_aux.h" +#include "inv_mpu_iio.h" +#include "inv_mpu_magn.h" + +/* + * MPU9xxx magnetometer are AKM chips on I2C aux bus + * MPU9150 is AK8975 + * MPU9250 is AK8963 + */ +#define INV_MPU_MAGN_I2C_ADDR 0x0C + +#define INV_MPU_MAGN_REG_WIA 0x00 +#define INV_MPU_MAGN_BITS_WIA 0x48 + +#define INV_MPU_MAGN_REG_ST1 0x02 +#define INV_MPU_MAGN_BIT_DRDY 0x01 +#define INV_MPU_MAGN_BIT_DOR 0x02 + +#define INV_MPU_MAGN_REG_DATA 0x03 + +#define INV_MPU_MAGN_REG_ST2 0x09 +#define INV_MPU_MAGN_BIT_HOFL 0x08 +#define INV_MPU_MAGN_BIT_BITM 0x10 + +#define INV_MPU_MAGN_REG_CNTL1 0x0A +#define INV_MPU_MAGN_BITS_MODE_PWDN 0x00 +#define INV_MPU_MAGN_BITS_MODE_SINGLE 0x01 +#define INV_MPU_MAGN_BITS_MODE_FUSE 0x0F +#define INV_MPU9250_MAGN_BIT_OUTPUT_BIT 0x10 + +#define INV_MPU9250_MAGN_REG_CNTL2 0x0B +#define INV_MPU9250_MAGN_BIT_SRST 0x01 + +#define INV_MPU_MAGN_REG_ASAX 0x10 +#define INV_MPU_MAGN_REG_ASAY 0x11 +#define INV_MPU_MAGN_REG_ASAZ 0x12 + +/* Magnetometer maximum frequency */ +#define INV_MPU_MAGN_FREQ_HZ_MAX 50 + +static bool inv_magn_supported(const struct inv_mpu6050_state *st) +{ + switch (st->chip_type) { + case INV_MPU9150: + case INV_MPU9250: + case INV_MPU9255: + return true; + default: + return false; + } +} + +/* init magnetometer chip */ +static int inv_magn_init(struct inv_mpu6050_state *st) +{ + uint8_t val; + uint8_t asa[3]; + int32_t sensitivity; + int ret; + + /* check whoami */ + ret = inv_mpu_aux_read(st, INV_MPU_MAGN_I2C_ADDR, INV_MPU_MAGN_REG_WIA, + &val, sizeof(val)); + if (ret) + return ret; + if (val != INV_MPU_MAGN_BITS_WIA) + return -ENODEV; + + /* software reset for MPU925x only */ + switch (st->chip_type) { + case INV_MPU9250: + case INV_MPU9255: + ret = inv_mpu_aux_write(st, INV_MPU_MAGN_I2C_ADDR, + INV_MPU9250_MAGN_REG_CNTL2, + INV_MPU9250_MAGN_BIT_SRST); + if (ret) + return ret; + break; + default: + break; + } + + /* read fuse ROM data */ + ret = inv_mpu_aux_write(st, INV_MPU_MAGN_I2C_ADDR, + INV_MPU_MAGN_REG_CNTL1, + INV_MPU_MAGN_BITS_MODE_FUSE); + if (ret) + return ret; + + ret = inv_mpu_aux_read(st, INV_MPU_MAGN_I2C_ADDR, INV_MPU_MAGN_REG_ASAX, + asa, sizeof(asa)); + if (ret) + return ret; + + /* switch back to power-down */ + ret = inv_mpu_aux_write(st, INV_MPU_MAGN_I2C_ADDR, + INV_MPU_MAGN_REG_CNTL1, + INV_MPU_MAGN_BITS_MODE_PWDN); + if (ret) + return ret; + + /* + * Sensor sentivity + * 1 uT = 0.01 G and value is in micron (1e6) + * sensitvity = x uT * 0.01 * 1e6 + */ + switch (st->chip_type) { + case INV_MPU9150: + /* sensor sensitivity is 0.3 uT */ + sensitivity = 3000; + break; + case INV_MPU9250: + case INV_MPU9255: + /* sensor sensitivity in 16 bits mode: 0.15 uT */ + sensitivity = 1500; + break; + default: + return -EINVAL; + } + + /* + * Sensitivity adjustement and scale to Gauss + * + * Hadj = H * (((ASA - 128) * 0.5 / 128) + 1) + * Factor simplification: + * Hadj = H * ((ASA + 128) / 256) + * + * raw_to_gauss = Hadj * sensitivity + */ + st->magn_raw_to_gauss[0] = (((int32_t)asa[0] + 128) * sensitivity) / 256; + st->magn_raw_to_gauss[1] = (((int32_t)asa[1] + 128) * sensitivity) / 256; + st->magn_raw_to_gauss[2] = (((int32_t)asa[2] + 128) * sensitivity) / 256; + + return 0; +} + +/** + * inv_mpu_magn_probe() - probe and setup magnetometer chip + * @st: driver internal state + * + * Returns 0 on success, a negative error code otherwise + * + * It is probing the chip and setting up all needed i2c transfers. + * Noop if there is no magnetometer in the chip. + */ +int inv_mpu_magn_probe(struct inv_mpu6050_state *st) +{ + uint8_t val; + int ret; + + /* quit if chip is not supported */ + if (!inv_magn_supported(st)) + return 0; + + /* configure i2c master aux port */ + ret = inv_mpu_aux_init(st); + if (ret) + return ret; + + /* check and init mag chip */ + ret = inv_magn_init(st); + if (ret) + return ret; + + /* + * configure mpu i2c master accesses + * i2c SLV0: read sensor data, 7 bytes data(6)-ST2 + * Byte swap data to store them in big-endian in impair address groups + */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_ADDR(0), + INV_MPU6050_BIT_I2C_SLV_RNW | INV_MPU_MAGN_I2C_ADDR); + if (ret) + return ret; + + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_REG(0), + INV_MPU_MAGN_REG_DATA); + if (ret) + return ret; + + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), + INV_MPU6050_BIT_SLV_EN | + INV_MPU6050_BIT_SLV_BYTE_SW | + INV_MPU6050_BIT_SLV_GRP | + INV_MPU9X50_BYTES_MAGN); + if (ret) + return ret; + + /* i2c SLV1: launch single measurement */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_ADDR(1), + INV_MPU_MAGN_I2C_ADDR); + if (ret) + return ret; + + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_REG(1), + INV_MPU_MAGN_REG_CNTL1); + if (ret) + return ret; + + /* add 16 bits mode for MPU925x */ + val = INV_MPU_MAGN_BITS_MODE_SINGLE; + switch (st->chip_type) { + case INV_MPU9250: + case INV_MPU9255: + val |= INV_MPU9250_MAGN_BIT_OUTPUT_BIT; + break; + default: + break; + } + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_DO(1), val); + if (ret) + return ret; + + return regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(1), + INV_MPU6050_BIT_SLV_EN | 1); +} + +/** + * inv_mpu_magn_set_rate() - set magnetometer sampling rate + * @st: driver internal state + * @fifo_rate: mpu set fifo rate + * + * Returns 0 on success, a negative error code otherwise + * + * Limit sampling frequency to the maximum value supported by the + * magnetometer chip. Resulting in duplicated data for higher frequencies. + * Noop if there is no magnetometer in the chip. + */ +int inv_mpu_magn_set_rate(const struct inv_mpu6050_state *st, int fifo_rate) +{ + uint8_t d; + + /* quit if chip is not supported */ + if (!inv_magn_supported(st)) + return 0; + + /* + * update i2c master delay to limit mag sampling to max frequency + * compute fifo_rate divider d: rate = fifo_rate / (d + 1) + */ + if (fifo_rate > INV_MPU_MAGN_FREQ_HZ_MAX) + d = fifo_rate / INV_MPU_MAGN_FREQ_HZ_MAX - 1; + else + d = 0; + + return regmap_write(st->map, INV_MPU6050_REG_I2C_SLV4_CTRL, d); +} + +/** + * inv_mpu_magn_set_orient() - fill magnetometer mounting matrix + * @st: driver internal state + * + * Returns 0 on success, a negative error code otherwise + * + * Fill magnetometer mounting matrix using the provided chip matrix. + */ +int inv_mpu_magn_set_orient(struct inv_mpu6050_state *st) +{ + const char *orient; + char *str; + int i; + + /* fill magnetometer orientation */ + switch (st->chip_type) { + case INV_MPU9150: + case INV_MPU9250: + case INV_MPU9255: + /* x <- y */ + st->magn_orient.rotation[0] = st->orientation.rotation[3]; + st->magn_orient.rotation[1] = st->orientation.rotation[4]; + st->magn_orient.rotation[2] = st->orientation.rotation[5]; + /* y <- x */ + st->magn_orient.rotation[3] = st->orientation.rotation[0]; + st->magn_orient.rotation[4] = st->orientation.rotation[1]; + st->magn_orient.rotation[5] = st->orientation.rotation[2]; + /* z <- -z */ + for (i = 0; i < 3; ++i) { + orient = st->orientation.rotation[6 + i]; + /* use length + 2 for adding minus sign if needed */ + str = devm_kzalloc(regmap_get_device(st->map), + strlen(orient) + 2, GFP_KERNEL); + if (str == NULL) + return -ENOMEM; + if (strcmp(orient, "0") == 0) { + strcpy(str, orient); + } else if (orient[0] == '-') { + strcpy(str, &orient[1]); + } else { + str[0] = '-'; + strcpy(&str[1], orient); + } + st->magn_orient.rotation[6 + i] = str; + } + break; + default: + st->magn_orient = st->orientation; + break; + } + + return 0; +} + +/** + * inv_mpu_magn_read() - read magnetometer data + * @st: driver internal state + * @axis: IIO modifier axis value + * @val: store corresponding axis value + * + * Returns 0 on success, a negative error code otherwise + */ +int inv_mpu_magn_read(const struct inv_mpu6050_state *st, int axis, int *val) +{ + unsigned int user_ctrl, status; + __be16 data[3]; + uint8_t addr; + uint8_t d; + unsigned int period_ms; + int ret; + + /* quit if chip is not supported */ + if (!inv_magn_supported(st)) + return -ENODEV; + + /* Mag data: X - Y - Z */ + switch (axis) { + case IIO_MOD_X: + addr = 0; + break; + case IIO_MOD_Y: + addr = 1; + break; + case IIO_MOD_Z: + addr = 2; + break; + default: + return -EINVAL; + } + + /* set sample rate to max mag freq */ + d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU_MAGN_FREQ_HZ_MAX); + ret = regmap_write(st->map, st->reg->sample_rate_div, d); + if (ret) + return ret; + + /* start i2c master, wait for xfer, stop */ + user_ctrl = st->chip_config.user_ctrl | INV_MPU6050_BIT_I2C_MST_EN; + ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl); + if (ret) + return ret; + + /* need to wait 2 periods + half-period margin */ + period_ms = 1000 / INV_MPU_MAGN_FREQ_HZ_MAX; + msleep(period_ms * 2 + period_ms / 2); + user_ctrl = st->chip_config.user_ctrl; + ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl); + if (ret) + return ret; + + /* restore sample rate */ + d = st->chip_config.divider; + ret = regmap_write(st->map, st->reg->sample_rate_div, d); + if (ret) + return ret; + + /* check i2c status and read raw data */ + ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status); + if (ret) + return ret; + + if (status & INV_MPU6050_BIT_I2C_SLV0_NACK || + status & INV_MPU6050_BIT_I2C_SLV1_NACK) + return -EIO; + + ret = regmap_bulk_read(st->map, INV_MPU6050_REG_EXT_SENS_DATA, + data, sizeof(data)); + if (ret) + return ret; + + *val = (int16_t)be16_to_cpu(data[addr]); + + return IIO_VAL_INT; +} diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h new file mode 100644 index 000000000000..b41bd0578478 --- /dev/null +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 TDK-InvenSense, Inc. + */ + +#ifndef INV_MPU_MAGN_H_ +#define INV_MPU_MAGN_H_ + +#include <linux/kernel.h> + +#include "inv_mpu_iio.h" + +int inv_mpu_magn_probe(struct inv_mpu6050_state *st); + +/** + * inv_mpu_magn_get_scale() - get magnetometer scale value + * @st: driver internal state + * + * Returns IIO data format. + */ +static inline int inv_mpu_magn_get_scale(const struct inv_mpu6050_state *st, + const struct iio_chan_spec *chan, + int *val, int *val2) +{ + *val = 0; + *val2 = st->magn_raw_to_gauss[chan->address]; + return IIO_VAL_INT_PLUS_MICRO; +} + +int inv_mpu_magn_set_rate(const struct inv_mpu6050_state *st, int fifo_rate); + +int inv_mpu_magn_set_orient(struct inv_mpu6050_state *st); + +int inv_mpu_magn_read(const struct inv_mpu6050_state *st, int axis, int *val); + +#endif /* INV_MPU_MAGN_H_ */ diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c index 5f9a5de0bab4..f9fdf4302a91 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c @@ -124,7 +124,8 @@ int inv_reset_fifo(struct iio_dev *indio_dev) /* enable interrupt */ if (st->chip_config.accl_fifo_enable || - st->chip_config.gyro_fifo_enable) { + st->chip_config.gyro_fifo_enable || + st->chip_config.magn_fifo_enable) { result = regmap_write(st->map, st->reg->int_enable, INV_MPU6050_BIT_DATA_RDY_EN); if (result) @@ -141,6 +142,10 @@ int inv_reset_fifo(struct iio_dev *indio_dev) d |= INV_MPU6050_BITS_GYRO_OUT; if (st->chip_config.accl_fifo_enable) d |= INV_MPU6050_BIT_ACCEL_OUT; + if (st->chip_config.temp_fifo_enable) + d |= INV_MPU6050_BIT_TEMP_OUT; + if (st->chip_config.magn_fifo_enable) + d |= INV_MPU6050_BIT_SLAVE_0; result = regmap_write(st->map, st->reg->fifo_en, d); if (result) goto reset_fifo_fail; @@ -180,17 +185,12 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) "failed to ack interrupt\n"); goto flush_fifo; } - /* handle fifo overflow by reseting fifo */ - if (int_status & INV_MPU6050_BIT_FIFO_OVERFLOW_INT) - goto flush_fifo; - if (!(int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT)) { - dev_warn(regmap_get_device(st->map), - "spurious interrupt with status 0x%x\n", int_status); + if (!(int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT)) goto end_session; - } if (!(st->chip_config.accl_fifo_enable | - st->chip_config.gyro_fifo_enable)) + st->chip_config.gyro_fifo_enable | + st->chip_config.magn_fifo_enable)) goto end_session; bytes_per_datum = 0; if (st->chip_config.accl_fifo_enable) @@ -199,8 +199,11 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) if (st->chip_config.gyro_fifo_enable) bytes_per_datum += INV_MPU6050_BYTES_PER_3AXIS_SENSOR; - if (st->chip_type == INV_ICM20602) - bytes_per_datum += INV_ICM20602_BYTES_PER_TEMP_SENSOR; + if (st->chip_config.temp_fifo_enable) + bytes_per_datum += INV_MPU6050_BYTES_PER_TEMP_SENSOR; + + if (st->chip_config.magn_fifo_enable) + bytes_per_datum += INV_MPU9X50_BYTES_MAGN; /* * read fifo_count register to know how many bytes are inside the FIFO @@ -211,6 +214,18 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) if (result) goto end_session; fifo_count = get_unaligned_be16(&data[0]); + + /* + * Handle fifo overflow by resetting fifo. + * Reset if there is only 3 data set free remaining to mitigate + * possible delay between reading fifo count and fifo data. + */ + nb = 3 * bytes_per_datum; + if (fifo_count >= st->hw->fifo_size - nb) { + dev_warn(regmap_get_device(st->map), "fifo overflow reset\n"); + goto flush_fifo; + } + /* compute and process all complete datum */ nb = fifo_count / bytes_per_datum; inv_mpu6050_update_period(st, pf->timestamp, nb); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index 142692fc0758..ec102d5a5c77 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -74,7 +74,6 @@ static int inv_mpu_probe(struct spi_device *spi) static const struct spi_device_id inv_mpu_id[] = { {"mpu6000", INV_MPU6000}, {"mpu6500", INV_MPU6500}, - {"mpu9150", INV_MPU9150}, {"mpu9250", INV_MPU9250}, {"mpu9255", INV_MPU9255}, {"icm20608", INV_ICM20608}, diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c index dd55e70b6f77..5199fe790c30 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c @@ -5,7 +5,7 @@ #include "inv_mpu_iio.h" -static void inv_scan_query(struct iio_dev *indio_dev) +static void inv_scan_query_mpu6050(struct iio_dev *indio_dev) { struct inv_mpu6050_state *st = iio_priv(indio_dev); @@ -24,6 +24,64 @@ static void inv_scan_query(struct iio_dev *indio_dev) indio_dev->active_scan_mask) || test_bit(INV_MPU6050_SCAN_ACCL_Z, indio_dev->active_scan_mask); + + st->chip_config.temp_fifo_enable = + test_bit(INV_MPU6050_SCAN_TEMP, indio_dev->active_scan_mask); +} + +static void inv_scan_query_mpu9x50(struct iio_dev *indio_dev) +{ + struct inv_mpu6050_state *st = iio_priv(indio_dev); + + inv_scan_query_mpu6050(indio_dev); + + /* no magnetometer if i2c auxiliary bus is used */ + if (st->magn_disabled) + return; + + st->chip_config.magn_fifo_enable = + test_bit(INV_MPU9X50_SCAN_MAGN_X, + indio_dev->active_scan_mask) || + test_bit(INV_MPU9X50_SCAN_MAGN_Y, + indio_dev->active_scan_mask) || + test_bit(INV_MPU9X50_SCAN_MAGN_Z, + indio_dev->active_scan_mask); +} + +static void inv_scan_query(struct iio_dev *indio_dev) +{ + struct inv_mpu6050_state *st = iio_priv(indio_dev); + + switch (st->chip_type) { + case INV_MPU9150: + case INV_MPU9250: + case INV_MPU9255: + return inv_scan_query_mpu9x50(indio_dev); + default: + return inv_scan_query_mpu6050(indio_dev); + } +} + +static unsigned int inv_compute_skip_samples(const struct inv_mpu6050_state *st) +{ + unsigned int gyro_skip = 0; + unsigned int magn_skip = 0; + unsigned int skip_samples; + + /* gyro first sample is out of specs, skip it */ + if (st->chip_config.gyro_fifo_enable) + gyro_skip = 1; + + /* mag first sample is always not ready, skip it */ + if (st->chip_config.magn_fifo_enable) + magn_skip = 1; + + /* compute first samples to skip */ + skip_samples = gyro_skip; + if (magn_skip > skip_samples) + skip_samples = magn_skip; + + return skip_samples; } /** @@ -34,6 +92,7 @@ static void inv_scan_query(struct iio_dev *indio_dev) static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) { struct inv_mpu6050_state *st = iio_priv(indio_dev); + uint8_t d; int result; if (enable) { @@ -41,14 +100,11 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) if (result) return result; inv_scan_query(indio_dev); - st->skip_samples = 0; if (st->chip_config.gyro_fifo_enable) { result = inv_mpu6050_switch_engine(st, true, INV_MPU6050_BIT_PWR_GYRO_STBY); if (result) goto error_power_off; - /* gyro first sample is out of specs, skip it */ - st->skip_samples = 1; } if (st->chip_config.accl_fifo_enable) { result = inv_mpu6050_switch_engine(st, true, @@ -56,22 +112,32 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) if (result) goto error_gyro_off; } + if (st->chip_config.magn_fifo_enable) { + d = st->chip_config.user_ctrl | + INV_MPU6050_BIT_I2C_MST_EN; + result = regmap_write(st->map, st->reg->user_ctrl, d); + if (result) + goto error_accl_off; + st->chip_config.user_ctrl = d; + } + st->skip_samples = inv_compute_skip_samples(st); result = inv_reset_fifo(indio_dev); if (result) - goto error_accl_off; + goto error_magn_off; } else { result = regmap_write(st->map, st->reg->fifo_en, 0); if (result) - goto error_accl_off; + goto error_magn_off; result = regmap_write(st->map, st->reg->int_enable, 0); if (result) - goto error_accl_off; + goto error_magn_off; - result = regmap_write(st->map, st->reg->user_ctrl, - st->chip_config.user_ctrl); + d = st->chip_config.user_ctrl & ~INV_MPU6050_BIT_I2C_MST_EN; + result = regmap_write(st->map, st->reg->user_ctrl, d); if (result) - goto error_accl_off; + goto error_magn_off; + st->chip_config.user_ctrl = d; result = inv_mpu6050_switch_engine(st, false, INV_MPU6050_BIT_PWR_ACCL_STBY); @@ -90,6 +156,10 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) return 0; +error_magn_off: + /* always restore user_ctrl to disable fifo properly */ + st->chip_config.user_ctrl &= ~INV_MPU6050_BIT_I2C_MST_EN; + regmap_write(st->map, st->reg->user_ctrl, st->chip_config.user_ctrl); error_accl_off: if (st->chip_config.accl_fifo_enable) inv_mpu6050_switch_engine(st, false, |