[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <570A64B1.7080303@kernel.org>
Date: Sun, 10 Apr 2016 15:35:29 +0100
From: Jonathan Cameron <jic23@...nel.org>
To: Daniel Baluta <daniel.baluta@...el.com>
Cc: knaack.h@....de, lars@...afoo.de, pmeerw@...erw.net,
linux-iio@...r.kernel.org, linux-kernel@...r.kernel.org,
lucas.demarchi@...el.com
Subject: Re: [PATCH v3] iio: imu: Add initial support for Bosch BMI160
On 06/04/16 15:58, Daniel Baluta wrote:
> BMI160 is an Inertial Measurement Unit (IMU) which provides acceleration
> and angular rate measurement. It also offers a secondary I2C interface
> for connecting a magnetometer sensor (usually BMM160).
>
> Current driver offers support for accelerometer and gyroscope readings
> via sysfs or via buffer interface using an external trigger (e.g.
> hrtimer). Data is retrieved from IMU via I2C or SPI interface.
>
> Datasheet is at:
> http://www.mouser.com/ds/2/783/BST-BMI160-DS000-07-786474.pdf
>
> Signed-off-by: Daniel Baluta <daniel.baluta@...el.com>
I'm still a little unconvinced abou the data handling in the trigger_handler.
Sample should be __le16 I think which would make it all rather more
obvious.
Jonathan
> ---
> Changes since v2:
> * include <linux/module.h> to fix compile errors reported by
> Kbuild test robot
>
> Changes since v1:
> * addressed comments from Jonathan and Peter
> * fixed Kconfig spelling
> * shared bmi160_regmap_config between i2c and spi modules
> * clarify timeouts unit (useconds)
> * use sizeof(_le16) consistently
> * 1 -> true
> * clarify documentation references to datasheet
>
> drivers/iio/imu/Kconfig | 2 +
> drivers/iio/imu/Makefile | 1 +
> drivers/iio/imu/bmi160/Kconfig | 32 ++
> drivers/iio/imu/bmi160/Makefile | 6 +
> drivers/iio/imu/bmi160/bmi160.h | 10 +
> drivers/iio/imu/bmi160/bmi160_core.c | 594 +++++++++++++++++++++++++++++++++++
> drivers/iio/imu/bmi160/bmi160_i2c.c | 72 +++++
> drivers/iio/imu/bmi160/bmi160_spi.c | 63 ++++
> 8 files changed, 780 insertions(+)
> create mode 100644 drivers/iio/imu/bmi160/Kconfig
> create mode 100644 drivers/iio/imu/bmi160/Makefile
> create mode 100644 drivers/iio/imu/bmi160/bmi160.h
> create mode 100644 drivers/iio/imu/bmi160/bmi160_core.c
> create mode 100644 drivers/iio/imu/bmi160/bmi160_i2c.c
> create mode 100644 drivers/iio/imu/bmi160/bmi160_spi.c
>
> diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
> index 5e610f7..1f1ad41 100644
> --- a/drivers/iio/imu/Kconfig
> +++ b/drivers/iio/imu/Kconfig
> @@ -25,6 +25,8 @@ config ADIS16480
> Say yes here to build support for Analog Devices ADIS16375, ADIS16480,
> ADIS16485, ADIS16488 inertial sensors.
>
> +source "drivers/iio/imu/bmi160/Kconfig"
> +
> config KMX61
> tristate "Kionix KMX61 6-axis accelerometer and magnetometer"
> depends on I2C
> diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
> index e1e6e3d..c71bcd3 100644
> --- a/drivers/iio/imu/Makefile
> +++ b/drivers/iio/imu/Makefile
> @@ -13,6 +13,7 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_trigger.o
> adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o
> obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o
>
> +obj-y += bmi160/
> obj-y += inv_mpu6050/
>
> obj-$(CONFIG_KMX61) += kmx61.o
> diff --git a/drivers/iio/imu/bmi160/Kconfig b/drivers/iio/imu/bmi160/Kconfig
> new file mode 100644
> index 0000000..005c17c
> --- /dev/null
> +++ b/drivers/iio/imu/bmi160/Kconfig
> @@ -0,0 +1,32 @@
> +#
> +# BMI160 IMU driver
> +#
> +
> +config BMI160
> + tristate
> + select IIO_BUFFER
> + select IIO_TRIGGERED_BUFFER
> +
> +config BMI160_I2C
> + tristate "Bosch BMI160 I2C driver"
> + depends on I2C
> + select BMI160
> + select REGMAP_I2C
> + help
> + If you say yes here you get support for BMI160 IMU on I2C with
> + accelerometer, gyroscope and external BMG160 magnetometer.
> +
> + This driver can also be built as a module. If so, the module will be
> + called bmi160_i2c.
> +
> +config BMI160_SPI
> + tristate "Bosch BMI160 SPI driver"
> + depends on SPI
> + select BMI160
> + select REGMAP_SPI
> + help
> + If you say yes here you get support for BMI160 IMU on SPI with
> + accelerometer, gyroscope and external BMG160 magnetometer.
> +
> + This driver can also be built as a module. If so, the module will be
> + called bmi160_spi.
> diff --git a/drivers/iio/imu/bmi160/Makefile b/drivers/iio/imu/bmi160/Makefile
> new file mode 100644
> index 0000000..10365e4
> --- /dev/null
> +++ b/drivers/iio/imu/bmi160/Makefile
> @@ -0,0 +1,6 @@
> +#
> +# Makefile for Bosch BMI160 IMU
> +#
> +obj-$(CONFIG_BMI160) += bmi160_core.o
> +obj-$(CONFIG_BMI160_I2C) += bmi160_i2c.o
> +obj-$(CONFIG_BMI160_SPI) += bmi160_spi.o
> diff --git a/drivers/iio/imu/bmi160/bmi160.h b/drivers/iio/imu/bmi160/bmi160.h
> new file mode 100644
> index 0000000..d2ae6ed
> --- /dev/null
> +++ b/drivers/iio/imu/bmi160/bmi160.h
> @@ -0,0 +1,10 @@
> +#ifndef BMI160_H_
> +#define BMI160_H_
> +
> +extern const struct regmap_config bmi160_regmap_config;
> +
> +int bmi160_core_probe(struct device *dev, struct regmap *regmap,
> + const char *name, bool use_spi);
> +void bmi160_core_remove(struct device *dev);
> +
> +#endif /* BMI160_H_ */
> diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c
> new file mode 100644
> index 0000000..eef04a3
> --- /dev/null
> +++ b/drivers/iio/imu/bmi160/bmi160_core.c
> @@ -0,0 +1,594 @@
> +/*
> + * BMI160 - Bosch IMU (accel, gyro plus external magnetometer)
> + *
> + * Copyright (c) 2016, Intel Corporation.
> + *
> + * This file is subject to the terms and conditions of version 2 of
> + * the GNU General Public License. See the file COPYING in the main
> + * directory of this archive for more details.
> + *
> + * IIO core driver for BMI160, with support for I2C/SPI busses
> + *
> + * TODO: magnetometer, interrupts, hardware FIFO
> + */
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/acpi.h>
> +#include <linux/delay.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/buffer.h>
> +
> +#include "bmi160.h"
> +
> +#define BMI160_REG_CHIP_ID 0x00
> +#define BMI160_CHIP_ID_VAL 0xD1
> +
> +#define BMI160_REG_PMU_STATUS 0x03
> +
> +/* X axis data low byte address, the rest can be obtained using axis offset */
> +#define BMI160_REG_DATA_MAGN_XOUT_L 0x04
> +#define BMI160_REG_DATA_GYRO_XOUT_L 0x0C
> +#define BMI160_REG_DATA_ACCEL_XOUT_L 0x12
> +
> +#define BMI160_REG_ACCEL_CONFIG 0x40
> +#define BMI160_ACCEL_CONFIG_ODR_MASK GENMASK(3, 0)
> +#define BMI160_ACCEL_CONFIG_BWP_MASK GENMASK(6, 4)
> +
> +#define BMI160_REG_ACCEL_RANGE 0x41
> +#define BMI160_ACCEL_RANGE_2G 0x03
> +#define BMI160_ACCEL_RANGE_4G 0x05
> +#define BMI160_ACCEL_RANGE_8G 0x08
> +#define BMI160_ACCEL_RANGE_16G 0x0C
> +
> +#define BMI160_REG_GYRO_CONFIG 0x42
> +#define BMI160_GYRO_CONFIG_ODR_MASK GENMASK(3, 0)
> +#define BMI160_GYRO_CONFIG_BWP_MASK GENMASK(5, 4)
> +
> +#define BMI160_REG_GYRO_RANGE 0x43
> +#define BMI160_GYRO_RANGE_2000DPS 0x00
> +#define BMI160_GYRO_RANGE_1000DPS 0x01
> +#define BMI160_GYRO_RANGE_500DPS 0x02
> +#define BMI160_GYRO_RANGE_250DPS 0x03
> +#define BMI160_GYRO_RANGE_125DPS 0x04
> +
> +#define BMI160_REG_CMD 0x7E
> +#define BMI160_CMD_ACCEL_PM_SUSPEND 0x10
> +#define BMI160_CMD_ACCEL_PM_NORMAL 0x11
> +#define BMI160_CMD_ACCEL_PM_LOW_POWER 0x12
> +#define BMI160_CMD_GYRO_PM_SUSPEND 0x14
> +#define BMI160_CMD_GYRO_PM_NORMAL 0x15
> +#define BMI160_CMD_GYRO_PM_FAST_STARTUP 0x17
> +#define BMI160_CMD_SOFTRESET 0xB6
> +
> +#define BMI160_REG_DUMMY 0x7F
> +
> +#define BMI160_ACCEL_PMU_MIN_USLEEP 3200
> +#define BMI160_ACCEL_PMU_MAX_USLEEP 3800
> +#define BMI160_GYRO_PMU_MIN_USLEEP 55000
> +#define BMI160_GYRO_PMU_MAX_USLEEP 80000
> +#define BMI160_SOFTRESET_USLEEP 1000
> +
> +#define BMI160_CHANNEL(_type, _axis, _index) { \
> + .type = _type, \
> + .modified = 1, \
> + .channel2 = IIO_MOD_##_axis, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
> + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> + .scan_index = _index, \
> + .scan_type = { \
> + .sign = 's', \
> + .realbits = 16, \
> + .storagebits = 16, \
> + .endianness = IIO_LE, \
> + }, \
> +}
> +
> +/* scan indexes follow DATA register order */
> +enum bmi160_scan_axis {
> + BMI160_SCAN_EXT_MAGN_X = 0,
> + BMI160_SCAN_EXT_MAGN_Y,
> + BMI160_SCAN_EXT_MAGN_Z,
> + BMI160_SCAN_RHALL,
> + BMI160_SCAN_GYRO_X,
> + BMI160_SCAN_GYRO_Y,
> + BMI160_SCAN_GYRO_Z,
> + BMI160_SCAN_ACCEL_X,
> + BMI160_SCAN_ACCEL_Y,
> + BMI160_SCAN_ACCEL_Z,
> + BMI160_SCAN_TIMESTAMP,
> +};
> +
> +enum bmi160_sensor_type {
> + BMI160_ACCEL = 0,
> + BMI160_GYRO,
> + BMI160_EXT_MAGN,
> + BMI160_NUM_SENSORS /* must be last */
> +};
> +
> +struct bmi160_data {
> + struct regmap *regmap;
> +};
> +
> +const struct regmap_config bmi160_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> +};
> +EXPORT_SYMBOL(bmi160_regmap_config);
> +
> +struct bmi160_regs {
> + u8 data; /* LSB byte register for X-axis */
> + u8 config;
> + u8 config_odr_mask;
> + u8 config_bwp_mask;
> + u8 range;
> + u8 pmu_cmd_normal;
> + u8 pmu_cmd_suspend;
> +};
> +
> +struct bmi160_regs bmi160_regs[] = {
> + [BMI160_ACCEL] = {
> + .data = BMI160_REG_DATA_ACCEL_XOUT_L,
> + .config = BMI160_REG_ACCEL_CONFIG,
> + .config_odr_mask = BMI160_ACCEL_CONFIG_ODR_MASK,
> + .config_bwp_mask = BMI160_ACCEL_CONFIG_BWP_MASK,
> + .range = BMI160_REG_ACCEL_RANGE,
> + .pmu_cmd_normal = BMI160_CMD_ACCEL_PM_NORMAL,
> + .pmu_cmd_suspend = BMI160_CMD_ACCEL_PM_SUSPEND,
> + },
> + [BMI160_GYRO] = {
> + .data = BMI160_REG_DATA_GYRO_XOUT_L,
> + .config = BMI160_REG_GYRO_CONFIG,
> + .config_odr_mask = BMI160_GYRO_CONFIG_ODR_MASK,
> + .config_bwp_mask = BMI160_GYRO_CONFIG_BWP_MASK,
> + .range = BMI160_REG_GYRO_RANGE,
> + .pmu_cmd_normal = BMI160_CMD_GYRO_PM_NORMAL,
> + .pmu_cmd_suspend = BMI160_CMD_GYRO_PM_SUSPEND,
> + },
> +};
> +
> +struct bmi160_pmu_time {
> + unsigned long min;
> + unsigned long max;
> +};
> +
> +struct bmi160_pmu_time bmi160_pmu_time[] = {
> + [BMI160_ACCEL] = {
> + .min = BMI160_ACCEL_PMU_MIN_USLEEP,
> + .max = BMI160_ACCEL_PMU_MAX_USLEEP
> + },
> + [BMI160_GYRO] = {
> + .min = BMI160_GYRO_PMU_MIN_USLEEP,
> + .max = BMI160_GYRO_PMU_MIN_USLEEP,
> + },
> +};
> +
> +struct bmi160_scale {
> + u8 bits;
> + int uscale;
> +};
> +
> +struct bmi160_odr {
> + u8 bits;
> + int odr;
> + int uodr;
> +};
> +
> +static const struct bmi160_scale bmi160_accel_scale[] = {
> + { BMI160_ACCEL_RANGE_2G, 598},
> + { BMI160_ACCEL_RANGE_4G, 1197},
> + { BMI160_ACCEL_RANGE_8G, 2394},
> + { BMI160_ACCEL_RANGE_16G, 4788},
> +};
> +
> +static const struct bmi160_scale bmi160_gyro_scale[] = {
> + { BMI160_GYRO_RANGE_2000DPS, 1065},
> + { BMI160_GYRO_RANGE_1000DPS, 532},
> + { BMI160_GYRO_RANGE_500DPS, 266},
> + { BMI160_GYRO_RANGE_250DPS, 133},
> + { BMI160_GYRO_RANGE_125DPS, 66},
> +};
> +
> +struct bmi160_scale_item {
> + const struct bmi160_scale *tbl;
> + int num;
> +};
> +
> +static const struct bmi160_scale_item bmi160_scale_table[] = {
> + [BMI160_ACCEL] = {
> + .tbl = bmi160_accel_scale,
> + .num = ARRAY_SIZE(bmi160_accel_scale),
> + },
> + [BMI160_GYRO] = {
> + .tbl = bmi160_gyro_scale,
> + .num = ARRAY_SIZE(bmi160_gyro_scale),
> + },
> +};
> +
> +static const struct bmi160_odr bmi160_accel_odr[] = {
> + {0x01, 0, 78125},
> + {0x02, 1, 5625},
> + {0x03, 3, 125},
> + {0x04, 6, 25},
> + {0x05, 12, 5},
> + {0x06, 25, 0},
> + {0x07, 50, 0},
> + {0x08, 100, 0},
> + {0x09, 200, 0},
> + {0x0A, 400, 0},
> + {0x0B, 800, 0},
> + {0x0C, 1600, 0},
> +};
> +
> +static const struct bmi160_odr bmi160_gyro_odr[] = {
> + {0x06, 25, 0},
> + {0x07, 50, 0},
> + {0x08, 100, 0},
> + {0x09, 200, 0},
> + {0x0A, 400, 0},
> + {0x0B, 8000, 0},
> + {0x0C, 1600, 0},
> + {0x0D, 3200, 0},
> +};
> +
> +struct bmi160_odr_item {
> + const struct bmi160_odr *tbl;
> + int num;
> +};
> +
> +static const struct bmi160_odr_item bmi160_odr_table[] = {
> + [BMI160_ACCEL] = {
> + .tbl = bmi160_accel_odr,
> + .num = ARRAY_SIZE(bmi160_accel_odr),
> + },
> + [BMI160_GYRO] = {
> + .tbl = bmi160_gyro_odr,
> + .num = ARRAY_SIZE(bmi160_gyro_odr),
> + },
> +};
> +
> +static const struct iio_chan_spec bmi160_channels[] = {
> + BMI160_CHANNEL(IIO_ACCEL, X, BMI160_SCAN_ACCEL_X),
> + BMI160_CHANNEL(IIO_ACCEL, Y, BMI160_SCAN_ACCEL_Y),
> + BMI160_CHANNEL(IIO_ACCEL, Z, BMI160_SCAN_ACCEL_Z),
> + BMI160_CHANNEL(IIO_ANGL_VEL, X, BMI160_SCAN_GYRO_X),
> + BMI160_CHANNEL(IIO_ANGL_VEL, Y, BMI160_SCAN_GYRO_Y),
> + BMI160_CHANNEL(IIO_ANGL_VEL, Z, BMI160_SCAN_GYRO_Z),
> + IIO_CHAN_SOFT_TIMESTAMP(BMI160_SCAN_TIMESTAMP),
> +};
> +
> +static enum bmi160_sensor_type bmi160_to_sensor(enum iio_chan_type iio_type)
> +{
> + switch (iio_type) {
> + case IIO_ACCEL:
> + return BMI160_ACCEL;
> + case IIO_ANGL_VEL:
> + return BMI160_GYRO;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static
> +int bmi160_set_mode(struct bmi160_data *data, enum bmi160_sensor_type t,
> + bool mode)
> +{
> + int ret;
> + u8 cmd;
> +
> + if (mode)
> + cmd = bmi160_regs[t].pmu_cmd_normal;
> + else
> + cmd = bmi160_regs[t].pmu_cmd_suspend;
> +
> + ret = regmap_write(data->regmap, BMI160_REG_CMD, cmd);
> + if (ret < 0)
> + return ret;
> +
> + usleep_range(bmi160_pmu_time[t].min, bmi160_pmu_time[t].max);
> +
> + return 0;
> +}
> +
> +static
> +int bmi160_set_scale(struct bmi160_data *data, enum bmi160_sensor_type t,
> + int uscale)
> +{
> + int i;
> +
> + for (i = 0; i < bmi160_scale_table[t].num; i++)
> + if (bmi160_scale_table[t].tbl[i].uscale == uscale)
> + break;
> +
> + if (i == bmi160_scale_table[t].num)
> + return -EINVAL;
> +
> + return regmap_write(data->regmap, bmi160_regs[t].range,
> + bmi160_scale_table[t].tbl[i].bits);
> +}
> +
> +static
> +int bmi160_get_scale(struct bmi160_data *data, enum bmi160_sensor_type t,
> + int *uscale)
> +{
> + int i, ret, val;
> +
> + ret = regmap_read(data->regmap, bmi160_regs[t].range, &val);
> + if (ret < 0)
> + return ret;
> +
> + for (i = 0; i < bmi160_scale_table[t].num; i++)
> + if (bmi160_scale_table[t].tbl[i].bits == val) {
> + *uscale = bmi160_scale_table[t].tbl[i].uscale;
> + return 0;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int bmi160_get_data(struct bmi160_data *data, int chan_type,
> + int axis, int *val)
> +{
> + u8 reg;
> + int ret;
> + __le16 sample;
> + enum bmi160_sensor_type t = bmi160_to_sensor(chan_type);
> +
> + reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(__le16);
> +
> + ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(__le16));
> + if (ret < 0)
> + return ret;
> +
> + *val = sign_extend32(le16_to_cpu(sample), 15);
> +
> + return 0;
> +}
> +
> +static
> +int bmi160_set_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
> + int odr, int uodr)
> +{
> + int i;
> +
> + for (i = 0; i < bmi160_odr_table[t].num; i++)
> + if (bmi160_odr_table[t].tbl[i].odr == odr &&
> + bmi160_odr_table[t].tbl[i].uodr == uodr)
> + break;
> +
> + if (i >= bmi160_odr_table[t].num)
> + return -EINVAL;
> +
> + return regmap_update_bits(data->regmap,
> + bmi160_regs[t].config,
> + bmi160_odr_table[t].tbl[i].bits,
> + bmi160_regs[t].config_odr_mask);
> +}
> +
> +static int bmi160_get_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
> + int *odr, int *uodr)
> +{
> + int i, val, ret;
> +
> + ret = regmap_read(data->regmap, bmi160_regs[t].config, &val);
> + if (ret < 0)
> + return ret;
> +
> + val &= bmi160_regs[t].config_odr_mask;
> +
> + for (i = 0; i < bmi160_odr_table[t].num; i++)
> + if (val == bmi160_odr_table[t].tbl[i].bits)
> + break;
> +
> + if (i >= bmi160_odr_table[t].num)
> + return -EINVAL;
> +
> + *odr = bmi160_odr_table[t].tbl[i].odr;
> + *uodr = bmi160_odr_table[t].tbl[i].uodr;
> +
> + return 0;
> +}
> +
> +static irqreturn_t bmi160_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + struct bmi160_data *data = iio_priv(indio_dev);
> + s16 buf[16]; /* 3 sens x 3 axis x s16 + 3 x s16 pad + 4 x s16 tstamp */
> + int i, ret, sample, j = 0, base = BMI160_REG_DATA_MAGN_XOUT_L;
> +
> + for_each_set_bit(i, indio_dev->active_scan_mask,
> + indio_dev->masklength) {
> + ret = regmap_bulk_read(data->regmap, base + i * sizeof(__le16),
> + &sample, sizeof(__le16));
Shouldn't sample be an __le16 ideally? Then you could use sizeof(sample)
as well which would be more obviously right...
> + if (ret < 0)
> + goto done;
> + buf[j++] = sample;
Probably makes sense to make buf __le16 buf[16] as well though that one isn't
as important (and perhaps missleading in some ways)
> + }
> +
> + iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns());
> +done:
> + iio_trigger_notify_done(indio_dev->trig);
> + return IRQ_HANDLED;
> +}
> +
> +static int bmi160_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int *val, int *val2, long mask)
> +{
> + int ret;
> + struct bmi160_data *data = iio_priv(indio_dev);
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + ret = bmi160_get_data(data, chan->type, chan->channel2, val);
> + if (ret < 0)
> + return ret;
> + return IIO_VAL_INT;
> + case IIO_CHAN_INFO_SCALE:
> + *val = 0;
> + ret = bmi160_get_scale(data,
> + bmi160_to_sensor(chan->type), val2);
> + return ret < 0 ? ret : IIO_VAL_INT_PLUS_MICRO;
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + ret = bmi160_get_odr(data, bmi160_to_sensor(chan->type),
> + val, val2);
> + return ret < 0 ? ret : IIO_VAL_INT_PLUS_MICRO;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int bmi160_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int val, int val2, long mask)
> +{
> + struct bmi160_data *data = iio_priv(indio_dev);
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_SCALE:
> + return bmi160_set_scale(data,
> + bmi160_to_sensor(chan->type), val2);
> + break;
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + return bmi160_set_odr(data, bmi160_to_sensor(chan->type),
> + val, val2);
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static const struct iio_info bmi160_info = {
> + .driver_module = THIS_MODULE,
> + .read_raw = bmi160_read_raw,
> + .write_raw = bmi160_write_raw,
> +};
> +
> +static const char *bmi160_match_acpi_device(struct device *dev)
> +{
> + const struct acpi_device_id *id;
> +
> + id = acpi_match_device(dev->driver->acpi_match_table, dev);
> + if (!id)
> + return NULL;
> +
> + return dev_name(dev);
> +}
> +
> +static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
> +{
> + int ret;
> + unsigned int val;
> + struct device *dev = regmap_get_device(data->regmap);
> +
> + ret = regmap_write(data->regmap, BMI160_REG_CMD, BMI160_CMD_SOFTRESET);
> + if (ret < 0)
> + return ret;
> +
> + usleep_range(BMI160_SOFTRESET_USLEEP, BMI160_SOFTRESET_USLEEP + 1);
> +
> + /* CS rising edge is needed before starting SPI, so do a dummy read
> + * See Section 3.2.1, page 86 of the datasheet
> + */
> + if (use_spi) {
> + ret = regmap_read(data->regmap, BMI160_REG_DUMMY, &val);
> + if (ret < 0)
> + return ret;
> + }
> +
> + ret = regmap_read(data->regmap, BMI160_REG_CHIP_ID, &val);
> + if (ret < 0) {
> + dev_err(dev, "Error reading chip id\n");
> + return ret;
> + }
> + if (val != BMI160_CHIP_ID_VAL) {
> + dev_err(dev, "Wrong chip id, got %x expected %x\n",
> + val, BMI160_CHIP_ID_VAL);
> + return -ENODEV;
> + }
> +
> + ret = bmi160_set_mode(data, BMI160_ACCEL, true);
> + if (ret < 0)
> + return ret;
> +
> + ret = bmi160_set_mode(data, BMI160_GYRO, true);
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +}
> +
> +static void bmi160_chip_uninit(struct bmi160_data *data)
> +{
> + bmi160_set_mode(data, BMI160_GYRO, false);
> + bmi160_set_mode(data, BMI160_ACCEL, false);
> +}
> +
> +int bmi160_core_probe(struct device *dev, struct regmap *regmap,
> + const char *name, bool use_spi)
> +{
> + struct iio_dev *indio_dev;
> + struct bmi160_data *data;
> + int ret;
> +
> + indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + data = iio_priv(indio_dev);
> + dev_set_drvdata(dev, indio_dev);
> + data->regmap = regmap;
> +
> + ret = bmi160_chip_init(data, use_spi);
> + if (ret < 0)
> + return ret;
> +
> + if (!name && ACPI_HANDLE(dev))
> + name = bmi160_match_acpi_device(dev);
> +
> + indio_dev->dev.parent = dev;
> + indio_dev->channels = bmi160_channels;
> + indio_dev->num_channels = ARRAY_SIZE(bmi160_channels);
> + indio_dev->name = name;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + indio_dev->info = &bmi160_info;
> +
> + ret = iio_triggered_buffer_setup(indio_dev, NULL,
> + bmi160_trigger_handler, NULL);
> + if (ret < 0)
> + goto uninit;
> +
> + ret = iio_device_register(indio_dev);
> + if (ret < 0)
> + goto buffer_cleanup;
> +
> + return 0;
> +uninit:
> + bmi160_chip_uninit(data);
> +buffer_cleanup:
> + iio_triggered_buffer_cleanup(indio_dev);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(bmi160_core_probe);
> +
> +void bmi160_core_remove(struct device *dev)
> +{
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
> + struct bmi160_data *data = iio_priv(indio_dev);
> +
> + iio_device_unregister(indio_dev);
> + iio_triggered_buffer_cleanup(indio_dev);
> + bmi160_chip_uninit(data);
> +}
> +EXPORT_SYMBOL_GPL(bmi160_core_remove);
> +
> +MODULE_AUTHOR("Daniel Baluta <daniel.baluta@...el.com");
> +MODULE_DESCRIPTION("Bosch BMI160 driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/imu/bmi160/bmi160_i2c.c b/drivers/iio/imu/bmi160/bmi160_i2c.c
> new file mode 100644
> index 0000000..07a179d
> --- /dev/null
> +++ b/drivers/iio/imu/bmi160/bmi160_i2c.c
> @@ -0,0 +1,72 @@
> +/*
> + * BMI160 - Bosch IMU, I2C bits
> + *
> + * Copyright (c) 2016, Intel Corporation.
> + *
> + * This file is subject to the terms and conditions of version 2 of
> + * the GNU General Public License. See the file COPYING in the main
> + * directory of this archive for more details.
> + *
> + * 7-bit I2C slave address is:
> + * - 0x68 if SDO is pulled to GND
> + * - 0x69 if SDO is pulled to VDDIO
> + */
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/regmap.h>
> +#include <linux/acpi.h>
> +
> +#include "bmi160.h"
> +
> +static int bmi160_i2c_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct regmap *regmap;
> + const char *name = NULL;
> +
> + regmap = devm_regmap_init_i2c(client, &bmi160_regmap_config);
> + if (IS_ERR(regmap)) {
> + dev_err(&client->dev, "Failed to register i2c regmap %d\n",
> + (int)PTR_ERR(regmap));
> + return PTR_ERR(regmap);
> + }
> +
> + if (id)
> + name = id->name;
> +
> + return bmi160_core_probe(&client->dev, regmap, name, false);
> +}
> +
> +static int bmi160_i2c_remove(struct i2c_client *client)
> +{
> + bmi160_core_remove(&client->dev);
> +
> + return 0;
> +}
> +
> +static const struct i2c_device_id bmi160_i2c_id[] = {
> + {"bmi160", 0},
> + {}
> +};
> +MODULE_DEVICE_TABLE(i2c, bmi160_i2c_id);
> +
> +static const struct acpi_device_id bmi160_acpi_match[] = {
> + {"BMI0160", 0},
> + { },
> +};
> +MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match);
> +
> +static struct i2c_driver bmi160_i2c_driver = {
> + .driver = {
> + .name = "bmi160_i2c",
> + .acpi_match_table = ACPI_PTR(bmi160_acpi_match),
> + },
> + .probe = bmi160_i2c_probe,
> + .remove = bmi160_i2c_remove,
> + .id_table = bmi160_i2c_id,
> +};
> +module_i2c_driver(bmi160_i2c_driver);
> +
> +MODULE_AUTHOR("Daniel Baluta <daniel.baluta@...el.com>");
> +MODULE_DESCRIPTION("BMI160 I2C driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/imu/bmi160/bmi160_spi.c b/drivers/iio/imu/bmi160/bmi160_spi.c
> new file mode 100644
> index 0000000..1ec8b12
> --- /dev/null
> +++ b/drivers/iio/imu/bmi160/bmi160_spi.c
> @@ -0,0 +1,63 @@
> +/*
> + * BMI160 - Bosch IMU, SPI bits
> + *
> + * Copyright (c) 2016, Intel Corporation.
> + *
> + * This file is subject to the terms and conditions of version 2 of
> + * the GNU General Public License. See the file COPYING in the main
> + * directory of this archive for more details.
> + */
> +#include <linux/module.h>
> +#include <linux/spi/spi.h>
> +#include <linux/regmap.h>
> +#include <linux/acpi.h>
> +
> +#include "bmi160.h"
> +
> +static int bmi160_spi_probe(struct spi_device *spi)
> +{
> + struct regmap *regmap;
> + const struct spi_device_id *id = spi_get_device_id(spi);
> +
> + regmap = devm_regmap_init_spi(spi, &bmi160_regmap_config);
> + if (IS_ERR(regmap)) {
> + dev_err(&spi->dev, "Failed to register spi regmap %d\n",
> + (int)PTR_ERR(regmap));
> + return PTR_ERR(regmap);
> + }
> + return bmi160_core_probe(&spi->dev, regmap, id->name, true);
> +}
> +
> +static int bmi160_spi_remove(struct spi_device *spi)
> +{
> + bmi160_core_remove(&spi->dev);
> +
> + return 0;
> +}
> +
> +static const struct spi_device_id bmi160_spi_id[] = {
> + {"bmi160", 0},
> + {}
> +};
> +MODULE_DEVICE_TABLE(spi, bmi160_spi_id);
> +
> +static const struct acpi_device_id bmi160_acpi_match[] = {
> + {"BMI0160", 0},
> + { },
> +};
> +MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match);
> +
> +static struct spi_driver bmi160_spi_driver = {
> + .probe = bmi160_spi_probe,
> + .remove = bmi160_spi_remove,
> + .id_table = bmi160_spi_id,
> + .driver = {
> + .acpi_match_table = ACPI_PTR(bmi160_acpi_match),
> + .name = "bmi160_spi",
> + },
> +};
> +module_spi_driver(bmi160_spi_driver);
> +
> +MODULE_AUTHOR("Daniel Baluta <daniel.baluta@...el.com");
> +MODULE_DESCRIPTION("Bosch BMI160 SPI driver");
> +MODULE_LICENSE("GPL v2");
>
Powered by blists - more mailing lists