On Thu, 12 Sep 2019 10:58:34 +0000 Jean-Baptiste Maneyrol <JManeyrol@xxxxxxxxxxxxxx> wrote: > Add helper functions to use the i2c auxiliary bus with the MPU i2c > master block. > > Support only register based chip, reading and 1 byte writing. These > will be useful for initializing magnetometers inside MPU9x50 chips. > > Signed-off-by: Jean-Baptiste Maneyrol <jmaneyrol@xxxxxxxxxxxxxx> Docs should be by the implementation not in the header. Otherwise, this looks fine I think. I'm not planning to plough through datasheets for this set so mostly assuming you got it right ;) Jonathan > --- > drivers/iio/imu/inv_mpu6050/Makefile | 3 +- > drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c | 184 ++++++++++++++++++++++ > drivers/iio/imu/inv_mpu6050/inv_mpu_aux.h | 46 ++++++ > 3 files changed, 232 insertions(+), 1 deletion(-) > create mode 100644 drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c > create mode 100644 drivers/iio/imu/inv_mpu6050/inv_mpu_aux.h > > diff --git a/drivers/iio/imu/inv_mpu6050/Makefile b/drivers/iio/imu/inv_mpu6050/Makefile > index 33bec09fee9b..2cfbd926522f 100644 > --- a/drivers/iio/imu/inv_mpu6050/Makefile > +++ b/drivers/iio/imu/inv_mpu6050/Makefile > @@ -4,7 +4,8 @@ > # > > obj-$(CONFIG_INV_MPU6050_IIO) += inv-mpu6050.o > -inv-mpu6050-y := 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 > > obj-$(CONFIG_INV_MPU6050_I2C) += inv-mpu6050-i2c.o > inv-mpu6050-i2c-y := inv_mpu_i2c.o inv_mpu_acpi.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..b4907c1a8bb6 > --- /dev/null > +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c > @@ -0,0 +1,184 @@ > +// 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_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; > +} > + > +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); > +} > + > +/* > + * i2c slave reading using SLV0 > + */ > +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; > +} > + > +/* > + * i2c slave writing 1 byte using SLV0 > + */ > +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..bae4eab58e6c > --- /dev/null > +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.h > @@ -0,0 +1,46 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2019 TDK-InvenSense, Inc. > + */ > + > +#ifndef INV_MPU_AUX_H_ > +#define INV_MPU_AUX_H_ > + > +#include <linux/kernel.h> > + > +#include "inv_mpu_iio.h" > + > +/** > + * 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); > + > +/** > + * 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. Documentation at implementation, not in the header. Tends to rot when hidden away from the code. > + */ > +int inv_mpu_aux_read(const struct inv_mpu6050_state *st, uint8_t addr, > + uint8_t reg, uint8_t *val, size_t size); > + > +/** > + * 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); > + > +#endif /* INV_MPU_AUX_H_ */