From: Jonathan Cameron <jic23@kernel.org>
To: Jean-Baptiste Maneyrol <JManeyrol@invensense.com>
Cc: "linux-iio@vger.kernel.org" <linux-iio@vger.kernel.org>
Subject: Re: [PATCH v3 6/7] iio: imu: inv_mpu6050: add MPU925x magnetometer support
Date: Sat, 5 Oct 2019 12:13:18 +0100 [thread overview]
Message-ID: <20191005121318.440cf39c@archlinux> (raw)
In-Reply-To: <20190916094128.30122-7-jmaneyrol@invensense.com>
On Mon, 16 Sep 2019 09:42:07 +0000
Jean-Baptiste Maneyrol <JManeyrol@invensense.com> wrote:
> Add support of driving MPU9250 magnetometer connected on i2c
> auxiliary bus using the MPU i2c master.
>
> Signed-off-by: Jean-Baptiste Maneyrol <jmaneyrol@invensense.com>
Same issue of the c file not including it's own header. Fixed up.
Applied to the togreg branch of iio.git and pushed out as testing
for the autobuilders to play with it.
Thanks,
Jonathan
> ---
> drivers/iio/imu/inv_mpu6050/Makefile | 2 +-
> drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 144 ++++++++-
> drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 4 +
> drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c | 355 +++++++++++++++++++++
> drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h | 36 +++
> 5 files changed, 537 insertions(+), 4 deletions(-)
> create mode 100644 drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c
> create mode 100644 drivers/iio/imu/inv_mpu6050/inv_mpu_magn.h
>
> diff --git a/drivers/iio/imu/inv_mpu6050/Makefile b/drivers/iio/imu/inv_mpu6050/Makefile
> index 2cfbd926522f..c103441a906b 100644
> --- a/drivers/iio/imu/inv_mpu6050/Makefile
> +++ b/drivers/iio/imu/inv_mpu6050/Makefile
> @@ -5,7 +5,7 @@
>
> obj-$(CONFIG_INV_MPU6050_IIO) += inv-mpu6050.o
> inv-mpu6050-y := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o \
> - inv_mpu_aux.o
> + inv_mpu_aux.o inv_mpu_magn.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_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
> index 7b2e4d81bbba..f1c65e0dd1a5 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
> @@ -332,6 +333,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 +417,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;
> @@ -469,6 +478,8 @@ inv_mpu6050_read_raw(struct iio_dev *indio_dev,
> *val2 = INV_MPU6050_TEMP_SCALE;
>
> return IIO_VAL_INT_PLUS_MICRO;
> + case IIO_MAGN:
> + return inv_mpu_magn_get_scale(st, chan, val, val2);
> default:
> return -EINVAL;
> }
> @@ -710,6 +721,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 +811,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[] = {
> @@ -864,6 +886,98 @@ static const unsigned long inv_mpu_scan_masks[] = {
> 0,
> };
>
> +#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, \
This doesn't need to be set as 0 is the 'obvious' default.
> + .endianness = IIO_BE, \
> + }, \
> + .ext_info = inv_ext_info, \
> + }
> +
> +static const struct iio_chan_spec inv_mpu9250_channels[] = {
> + IIO_CHAN_SOFT_TIMESTAMP(INV_MPU9X50_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_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),
> +
> + /* 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),
> +};
> +
> +static const unsigned long inv_mpu9x50_scan_masks[] = {
> + /* 3-axis accel */
> + BIT(INV_MPU6050_SCAN_ACCL_X)
> + | BIT(INV_MPU6050_SCAN_ACCL_Y)
> + | BIT(INV_MPU6050_SCAN_ACCL_Z),
> + /* 3-axis gyro */
> + BIT(INV_MPU6050_SCAN_GYRO_X)
> + | BIT(INV_MPU6050_SCAN_GYRO_Y)
> + | BIT(INV_MPU6050_SCAN_GYRO_Z),
> + /* 3-axis magn */
> + BIT(INV_MPU9X50_SCAN_MAGN_X)
> + | BIT(INV_MPU9X50_SCAN_MAGN_Y)
> + | BIT(INV_MPU9X50_SCAN_MAGN_Z),
> + /* 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),
> + /* 6-axis accel + magn */
> + BIT(INV_MPU6050_SCAN_ACCL_X)
> + | BIT(INV_MPU6050_SCAN_ACCL_Y)
> + | BIT(INV_MPU6050_SCAN_ACCL_Z)
> + | BIT(INV_MPU9X50_SCAN_MAGN_X)
> + | BIT(INV_MPU9X50_SCAN_MAGN_Y)
> + | BIT(INV_MPU9X50_SCAN_MAGN_Z),
> + /* 6-axis gyro + magn */
> + BIT(INV_MPU6050_SCAN_GYRO_X)
> + | BIT(INV_MPU6050_SCAN_GYRO_Y)
> + | BIT(INV_MPU6050_SCAN_GYRO_Z)
> + | BIT(INV_MPU9X50_SCAN_MAGN_X)
> + | BIT(INV_MPU9X50_SCAN_MAGN_Y)
> + | BIT(INV_MPU9X50_SCAN_MAGN_Z),
> + /* 9-axis accel + gyro + magn */
> + 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)
> + | BIT(INV_MPU9X50_SCAN_MAGN_X)
> + | BIT(INV_MPU9X50_SCAN_MAGN_Y)
> + | BIT(INV_MPU9X50_SCAN_MAGN_Z),
> + 0,
> +};
> +
> static const struct iio_chan_spec inv_icm20602_channels[] = {
> IIO_CHAN_SOFT_TIMESTAMP(INV_ICM20602_SCAN_TIMESTAMP),
> {
> @@ -1145,6 +1259,11 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
> 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)
> @@ -1168,14 +1287,33 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
> if (inv_mpu_bus_setup)
> inv_mpu_bus_setup(indio_dev);
>
> - if (chip_type == INV_ICM20602) {
> + switch (chip_type) {
> + case INV_MPU9250:
> + case INV_MPU9255:
> + /*
> + * Use magnetometer inside the chip only if there is no i2c
> + * auxiliary device in use.
> + */
> + if (!st->magn_disabled) {
> + indio_dev->channels = inv_mpu9250_channels;
> + indio_dev->num_channels = ARRAY_SIZE(inv_mpu9250_channels);
> + indio_dev->available_scan_masks = inv_mpu9x50_scan_masks;
> + } else {
> + 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;
> + case INV_ICM20602:
> indio_dev->channels = inv_icm20602_channels;
> indio_dev->num_channels = ARRAY_SIZE(inv_icm20602_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;
> }
>
> indio_dev->info = &mpu_info;
> diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
> index 04215bc6e8ab..953f85618199 100644
> --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
> +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
> @@ -130,6 +130,8 @@ struct inv_mpu6050_hw {
> * @data_timestamp: timestamp for next data sample.
> * @vddio_supply 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;
> @@ -152,6 +154,8 @@ struct inv_mpu6050_state {
> s64 data_timestamp;
> struct regulator *vddio_supply;
> bool magn_disabled;
> + s32 magn_raw_to_gauss[3];
> + struct iio_mount_matrix magn_orient;
> };
>
> /*register and associated bit definition*/
> 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..415d8acece54
> --- /dev/null
> +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c
> @@ -0,0 +1,355 @@
> +// 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_iio.h"
> +#include "inv_mpu_aux.h"
> +
> +/*
> + * MPU9250 magnetometer is an AKM AK8963 chip on I2C aux bus
> + */
> +#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_MPU_MAGN_BIT_OUTPUT_BIT 0x10
> +
> +#define INV_MPU_MAGN_REG_CNTL2 0x0B
> +#define INV_MPU_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_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];
> + 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;
> +
> + /* reset chip */
> + ret = inv_mpu_aux_write(st, INV_MPU_MAGN_I2C_ADDR,
> + INV_MPU_MAGN_REG_CNTL2,
> + INV_MPU_MAGN_BIT_SRST);
> + if (ret)
> + return ret;
> +
> + /* 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;
> +
> + /*
> + * Sensitivity adjustement and scale to Gauss
> + *
> + * Hadj = H * (((ASA - 128) * 0.5 / 128) + 1)
> + * Factor simplification:
> + * Hadj = H * ((ASA + 128) / 256)
> + *
> + * Sensor sentivity
> + * 0.15 uT in 16 bits mode
> + * 1 uT = 0.01 G and value is in micron (1e6)
> + * sensitvity = 0.15 uT * 0.01 * 1e6
> + *
> + * raw_to_gauss = Hadj * 1500
> + */
> + st->magn_raw_to_gauss[0] = (((int32_t)asa[0] + 128) * 1500) / 256;
> + st->magn_raw_to_gauss[1] = (((int32_t)asa[1] + 128) * 1500) / 256;
> + st->magn_raw_to_gauss[2] = (((int32_t)asa[2] + 128) * 1500) / 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)
> +{
> + 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 */
> + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_DO(1),
> + INV_MPU_MAGN_BITS_MODE_SINGLE |
> + INV_MPU_MAGN_BIT_OUTPUT_BIT);
> + 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_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_ */
next prev parent reply other threads:[~2019-10-05 11:13 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-09-16 9:41 [PATCH v3 0/7] add magnetometer support for MPU925x Jean-Baptiste Maneyrol
2019-09-16 9:41 ` [PATCH v3 1/7] iio: imu: inv_mpu6050: disable i2c mux " Jean-Baptiste Maneyrol
2019-10-05 10:57 ` Jonathan Cameron
2019-09-16 9:42 ` [PATCH v3 2/7] iio: imu: inv_mpu6050: add header include protection macro Jean-Baptiste Maneyrol
2019-10-05 10:58 ` Jonathan Cameron
2019-09-16 9:42 ` [PATCH v3 3/7] iio: imu: inv_mpu6050: add defines for supporting 9-axis chips Jean-Baptiste Maneyrol
2019-10-05 10:58 ` Jonathan Cameron
2019-09-16 9:42 ` [PATCH v3 4/7] iio: imu: inv_mpu6050: fix objects syntax in Makefile Jean-Baptiste Maneyrol
2019-10-05 11:00 ` Jonathan Cameron
2019-09-16 9:42 ` [PATCH v3 5/7] iio: imu: inv_mpu6050: helpers for using i2c master on auxiliary bus Jean-Baptiste Maneyrol
2019-10-05 11:06 ` Jonathan Cameron
2019-09-16 9:42 ` [PATCH v3 6/7] iio: imu: inv_mpu6050: add MPU925x magnetometer support Jean-Baptiste Maneyrol
2019-10-05 11:13 ` Jonathan Cameron [this message]
2019-09-16 9:42 ` [PATCH v3 7/7] iio: imu: inv_mpu6050: add fifo support for magnetometer data Jean-Baptiste Maneyrol
2019-10-05 11:15 ` Jonathan Cameron
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20191005121318.440cf39c@archlinux \
--to=jic23@kernel.org \
--cc=JManeyrol@invensense.com \
--cc=linux-iio@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.