On 27/07/16 01:22, navin patidar wrote: > Oh, I didn't know that Vlad already have submitted a patch for BNO055. > My patch doesn't do any thing new so please drop my patch. > > regards, > --navin-patidar You have my utmost sympathies! Sadly this happens to us all from time to time. Jonathan > > On Tue, Jul 26, 2016 at 11:46 AM, Peter Meerwald-Stadler > <pmeerw@xxxxxxxxxx> wrote: >> Hallo Navin, >> >>> Thanks for reviewing the patch. I will send the updated patch. >> >> I am sorry to have overlooked a previous patch by Vlad Dogaru, see >> http://comments.gmane.org/gmane.linux.kernel.iio/24643, for the BNO055 >> >> there have been a number of issues, it has not been accepted yet >> >> I think our policy is first come, first served; I have compared the two >> patches, maybe you can collaborate on a common proposal? >> >> regards, p. >> >>> regards, >>> --navin-patidar >>> >>> On Tue, Jul 26, 2016 at 1:07 AM, Peter Meerwald-Stadler <pmeerw@xxxxxxxxxx> >>> wrote: >>> >>>> >>>>> BNO055 provides the following motion sensors data: >>>>> >>>>> * Gyroscope >>>>> * Accelerometer >>>>> * Magnetometer >>>>> * Absolute Orientation (Quaternion) >>>> >>>> comments below >>>> >>>>> Signed-off-by: navin patidar <navin.patidar@xxxxxxxxx> >>>>> --- >>>>> drivers/iio/orientation/Kconfig | 10 + >>>>> drivers/iio/orientation/Makefile | 1 + >>>>> drivers/iio/orientation/bno055.c | 422 >>>> +++++++++++++++++++++++++++++++++++++++ >>>>> 3 files changed, 433 insertions(+) >>>>> create mode 100644 drivers/iio/orientation/bno055.c >>>>> >>>>> diff --git a/drivers/iio/orientation/Kconfig >>>> b/drivers/iio/orientation/Kconfig >>>>> index e3aa1e5..9ac21ee 100644 >>>>> --- a/drivers/iio/orientation/Kconfig >>>>> +++ b/drivers/iio/orientation/Kconfig >>>>> @@ -28,4 +28,14 @@ config HID_SENSOR_DEVICE_ROTATION >>>>> device rotation. The output of a device rotation sensor >>>>> is presented using quaternion format. >>>>> >>>>> +config BNO055 >>>>> + tristate "BOSCH BNO055 Absolute Orientation" >>>>> + depends on I2C >>>>> + select REGMAP_I2C >>>>> + help >>>>> + Say yes here to build support for BOSCH BNO55 9-axis absolute >>>> orientation sensor >>>>> + driver connected via I2C. >>>>> + This driver can also be built as a module. If so, the module >>>>> + will be called bno055. >>>>> + >>>>> endmenu >>>>> diff --git a/drivers/iio/orientation/Makefile >>>> b/drivers/iio/orientation/Makefile >>>>> index 4734dab..7d00037 100644 >>>>> --- a/drivers/iio/orientation/Makefile >>>>> +++ b/drivers/iio/orientation/Makefile >>>>> @@ -5,3 +5,4 @@ >>>>> # When adding new entries keep the list in alphabetical order >>>>> obj-$(CONFIG_HID_SENSOR_INCLINOMETER_3D) += hid-sensor-incl-3d.o >>>>> obj-$(CONFIG_HID_SENSOR_DEVICE_ROTATION) += hid-sensor-rotation.o >>>>> +obj-$(CONFIG_BNO055) += bno055.o >>>>> diff --git a/drivers/iio/orientation/bno055.c >>>> b/drivers/iio/orientation/bno055.c >>>>> new file mode 100644 >>>>> index 0000000..48b9e93 >>>>> --- /dev/null >>>>> +++ b/drivers/iio/orientation/bno055.c >>>>> @@ -0,0 +1,422 @@ >>>>> +/* >>>>> + * Copyright (c) 2016 Intel Corporation >>>>> + * >>>>> + * Driver for Bosch Sensortec BNO055 digital motion sensor. >>>>> + * >>>>> + * This program is free software; you can redistribute it and/or modify >>>>> + * it under the terms of the GNU General Public License version 2 as >>>>> + * published by the Free Software Foundation. >>>>> + * >>>> >>>> I find it useful to state the 7-bit i2c chip address here >>>> >>>>> + */ >>>>> + >>>>> +#define pr_fmt(fmt) "bno055: " fmt >>>>> + >>>>> +#include <linux/module.h> >>>>> +#include <linux/i2c.h> >>>>> +#include <linux/acpi.h> >>>>> +#include <linux/regmap.h> >>>>> +#include <linux/iio/iio.h> >>>>> +#include <linux/iio/sysfs.h> >>>>> + >>>> >>>> drop one newline >>>> >>>>> + >>>>> +#define BNO055_CHIP_ID 0xA0 >>>>> +#define REG_MAG_RADIUS_MSB 0x6A >>>> >>>> please consistently prefix with BNO055_ >>>> >>>>> + >>>>> +/* BNO055 configuration registers */ >>>>> +#define REG_PWR_MODE 0x3E >>>>> +#define REG_OPR_MODE 0x3D >>>>> +#define REG_UNIT_SEL 0x3B >>>>> +#define REG_AXIS_MAP_SIGN 0x42 >>>>> +#define REG_AXIS_MAP_CONFIG 0x41 >>>>> +#define REG_UNIT_SEL 0x3B >>>>> +#define REG_SYS_TRIGGER 0x3F >>>>> +#define BNO055_REG_PAGE_ID 0x07 >>>>> +#define BNO055_REG_ID 0x00 >>>>> + >>>>> +/* BNO055 status registers */ >>>>> +#define SYS_STATUS 0x39 >>>>> +#define INT_STATUS 0x37 >>>>> +#define CALIB_STATUS 0x35 >>>>> + >>>>> +/* BNO055 data registers */ >>>> >>>> drop newline >>>> >>>>> + >>>>> +#define GRV_DATA_Z_MSB 0x33 >>>>> +#define GRV_DATA_Z_LSB 0x32 >>>>> +#define GRV_DATA_Y_MSB 0x31 >>>>> +#define GRV_DATA_Y_LSB 0x30 >>>>> +#define GRV_DATA_X_MSB 0x2F >>>>> +#define GRV_DATA_X_LSB 0x2E >>>> >>>> do we really need all those register #defines? >>>> they are not used and noone wants to read just the LSB... >>>> >>>>> + >>>>> +#define LIA_DATA_Z_MSB 0x2D >>>>> +#define LIA_DATA_Z_LSB 0x2C >>>>> +#define LIA_DATA_Y_MSB 0x2B >>>>> +#define LIA_DATA_Y_LSB 0x2A >>>>> +#define LIA_DATA_X_MSB 0x29 >>>>> +#define LIA_DATA_X_LSB 0x28 >>>>> + >>>>> +#define QUA_DATA_Z_MSB 0x27 >>>>> +#define QUA_DATA_Z_LSB 0x26 >>>>> +#define QUA_DATA_Y_MSB 0x25 >>>>> +#define QUA_DATA_Y_LSB 0x24 >>>>> +#define QUA_DATA_X_MSB 0x23 >>>>> +#define QUA_DATA_X_LSB 0x22 >>>>> +#define QUA_DATA_W_MSB 0x21 >>>>> +#define QUA_DATA_W_LSB 0x20 >>>>> + >>>>> +#define EUL_PITCH_MSB 0x1F >>>>> +#define EUL_PITCH_LSB 0x1E >>>>> +#define EUL_ROLL_MSB 0x1D >>>>> +#define EUL_ROLL_LSB 0x1C >>>>> +#define EUL_HEADING_MSB 0x1B >>>>> +#define EUL_HEADING_LSB 0x1A >>>>> + >>>>> +#define GYR_DATA_Z_MSB 0x19 >>>>> +#define GYR_DATA_Z_LSB 0x18 >>>>> +#define GYR_DATA_Y_MSB 0x17 >>>>> +#define GYR_DATA_Y_LSB 0x16 >>>>> +#define GYR_DATA_X_MSB 0x15 >>>>> +#define GYR_DATA_X_LSB 0x14 >>>>> + >>>>> +#define MAG_DATA_Z_MSB 0x13 >>>>> +#define MAG_DATA_Z_LSB 0x12 >>>>> +#define MAG_DATA_Y_MSB 0x11 >>>>> +#define MAG_DATA_Y_LSB 0x10 >>>>> +#define MAG_DATA_X_MSB 0x0F >>>>> +#define MAG_DATA_X_LSB 0x0E >>>>> + >>>>> +#define ACC_DATA_Z_MSB 0x0D >>>>> +#define ACC_DATA_Z_LSB 0x0C >>>>> +#define ACC_DATA_Y_MSB 0x0B >>>>> +#define ACC_DATA_Y_LSB 0x0A >>>>> +#define ACC_DATA_X_MSB 0x09 >>>>> +#define ACC_DATA_X_LSB 0x08 >>>>> + >>>>> +/* operation modes */ >>>>> +#define FUSION_NDOF_MODE 0x0C >>>>> + >>>>> +/* power modes */ >>>>> +#define NORMAL_MODE 0x00 >>>>> +#define LOW_POWER_MODE BIT(0) >>>>> +#define SUSPEND_MODE BIT(1) >>>>> + >>>>> +#define PROPER_CALIBRATION 0xFF >>>>> + >>>>> +#define BASE_REG 0x08 >>>>> +#define SENSOR_AXIS_TO_REG(sensor,axis) (BASE_REG + (sensor * 6) + >>>> (axis * 2)) >>>>> + >>>>> +#define BNO055_CHANNEL(_type, _axis) { >>>> \ >>>>> + .type = IIO_##_type, \ >>>>> + .modified = 1, \ >>>>> + .channel2 = IIO_MOD_##_axis, \ >>>>> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ >>>> >>>> in what unit is the data returned; please check with IIO ABI documentation >>>> I think you will likely need _SCALE for some channels >>>> >>>>> + .scan_index = AXIS_##_axis, \ >>>> >>>> .scan_index is a continuous counter over ALL channels, not just for one >>>> channel type >>>> >>>> the driver does not support buffered reads, so not scan_type/scan_index is >>>> needed anyway >>>> >>>>> + .scan_type = { \ >>>>> + .sign = 's', \ >>>>> + .realbits = 16, \ >>>>> + .storagebits = 16, \ >>>>> + .endianness = IIO_LE, \ >>>>> + }, \ >>>>> +} >>>>> + >>>>> +struct bno055_data { >>>>> + struct i2c_client *client; >>>>> + struct mutex lock; >>>>> + struct regmap *regmap; >>>>> +}; >>>>> + >>>>> +enum bno055_axis { >>>>> + AXIS_X, >>>>> + AXIS_Y, >>>>> + AXIS_Z, >>>>> +}; >>>>> + >>>>> +enum {ACC=0, MAG=1, GYR=2, EUL=3, QUA=4}; >>>> >>>> prefix! >>>> >>>>> + >>>>> +static const struct iio_chan_spec bno055_channels[] = { >>>>> + BNO055_CHANNEL(ANGL_VEL, X), >>>>> + BNO055_CHANNEL(ANGL_VEL, Y), >>>>> + BNO055_CHANNEL(ANGL_VEL, Z), >>>>> + BNO055_CHANNEL(MAGN, X), >>>>> + BNO055_CHANNEL(MAGN, Y), >>>>> + BNO055_CHANNEL(MAGN, Z), >>>>> + BNO055_CHANNEL(ACCEL, X), >>>>> + BNO055_CHANNEL(ACCEL, Y), >>>>> + BNO055_CHANNEL(ACCEL, Z), >>>>> + { >>>>> + .type = IIO_ROT, >>>>> + .modified = 1, >>>>> + .channel2 = IIO_MOD_QUATERNION, >>>>> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), >>>>> + } >>>>> +}; >>>>> + >>>>> +static bool bno055_is_writeable_reg(struct device *dev, unsigned int >>>> reg) >>>>> +{ >>>>> + switch (reg) { >>>>> + case REG_PWR_MODE: >>>>> + case REG_OPR_MODE: >>>>> + case REG_UNIT_SEL: >>>>> + case REG_AXIS_MAP_SIGN: >>>>> + case REG_AXIS_MAP_CONFIG: >>>>> + case REG_SYS_TRIGGER: >>>>> + return true; >>>>> + default: >>>>> + return false; >>>>> + }; >>>>> +} >>>>> + >>>>> +static bool bno055_is_volatile_reg(struct device *dev, unsigned int reg) >>>>> +{ >>>>> + if ((reg >= ACC_DATA_X_LSB) && (reg <= QUA_DATA_Z_MSB)) >>>>> + return true; >>>>> + else >>>>> + return false; >>>>> +} >>>>> + >>>>> +static const struct regmap_config bno055_regmap_config = { >>>>> + .reg_bits = 8, >>>>> + .val_bits = 8, >>>>> + >>>>> + .max_register = REG_MAG_RADIUS_MSB, >>>>> + .cache_type = REGCACHE_RBTREE, >>>>> + >>>>> + .writeable_reg = bno055_is_writeable_reg, >>>>> + .volatile_reg = bno055_is_volatile_reg, >>>>> +}; >>>>> + >>>> >>>> drop newline >>>> >>>>> + >>>>> +static int read_bno055(struct bno055_data *data, int sensor, int axis, >>>> int *val) >>>> >>>> bno055 prefix please >>>> >>>>> +{ >>>>> + int ret; >>>>> + __le16 raw_val; >>>>> + int reg; >>>>> + >>>>> + reg = SENSOR_AXIS_TO_REG(sensor, axis); >>>> >>>> you can use .address in channel spec for that >>>> >>>>> + >>>>> + ret = regmap_bulk_read(data->regmap, reg, &raw_val, >>>>> + sizeof(raw_val)); >>>>> + if (ret < 0) { >>>>> + dev_err(&data->client->dev, "Error reading axis %d\n", >>>> axis); >>>>> + return ret; >>>>> + } >>>>> + >>>>> + *val = sign_extend32(le16_to_cpu(raw_val), 15); >>>>> + return IIO_VAL_INT; >>>>> +} >>>>> + >>>>> +/* Read raw quaternion values W,X,Y and Z. >>>>> + * Raw values needs to be divied by the 16384 to get the exact >>>> quaternion values. >>>>> + */ >>>>> + >>>>> +static int read_rot(struct bno055_data *data, int *vals, int *val_len) >>>>> +{ >>>>> + int ret, i; >>>>> + __le16 raw_val[4]; >>>>> + >>>>> + ret = regmap_bulk_read(data->regmap, QUA_DATA_W_LSB, &raw_val, >>>>> + sizeof(raw_val)); >>>>> + if (ret < 0) { >>>>> + dev_err(&data->client->dev, "Error reading Orientation >>>> \n"); >>>> >>>> lowercase orientation >>>> delete space before \n >>>> >>>>> + return ret; >>>>> + } >>>>> + >>>>> + for (i = 0; i < 4; ++i) >>>>> + vals[i] = sign_extend32(le16_to_cpu(raw_val[i]), 15); >>>>> + >>>>> + *val_len = 4; >>>>> + return IIO_VAL_INT_MULTIPLE; >>>>> +} >>>>> + >>>>> +static int bno055_read_raw_multi(struct iio_dev *indio_dev, >>>>> + struct iio_chan_spec const *chan, int size, >>>>> + int *val, int *val2, long mask) >>>>> +{ >>>>> + int ret = -EINVAL; >>>>> + struct bno055_data *data = iio_priv(indio_dev); >>>>> + >>>>> + mutex_lock(&data->lock); >>>>> + >>>>> + switch (mask) { >>>>> + case IIO_CHAN_INFO_RAW: >>>>> + switch (chan->type) { >>>>> + case IIO_ROT: >>>>> + ret = read_rot(data, val, val2); >>>>> + break; >>>>> + case IIO_ANGL_VEL: >>>>> + /* Gyroscope unit degrees per second */ >>>>> + /* Raw value needs to be divied by the 16 to get >>>> the exact value.*/ >>>> >>>> comment style, not a proper multi-line comment >>>> use _SCALE, it is not about 'exact' but correct scaling/unit >>>> typo: divided >>>> wording: divided by 16 >>>> >>>>> + ret = read_bno055(data, GYR, chan->scan_index, >>>> val); >>>>> + break; >>>>> + case IIO_ACCEL: >>>>> + /* Accelerometer Unit m/s2 */ >>>>> + /* Raw value needs to be divied by the 100 to get >>>> the exact value.*/ >>>>> + ret = read_bno055(data, ACC, chan->scan_index, >>>> val); >>>>> + break; >>>>> + case IIO_MAGN: >>>>> + /* Magnetometer Unit microTesla */ >>>>> + /* Raw value needs to be divied by the 16 to get >>>> the exact value. */ >>>>> + ret = read_bno055(data, MAG, chan->scan_index, >>>> val); >>>>> + break; >>>>> + default: >>>>> + ret = -EINVAL; >>>>> + break; >>>>> + } >>>>> + break; >>>>> + >>>>> + default: >>>>> + ret = -EINVAL; >>>>> + break; >>>>> + } >>>>> + >>>>> + mutex_unlock(&data->lock); >>>>> + >>>>> + return ret; >>>>> +} >>>>> + >>>>> +static const struct iio_info bno055_info = { >>>>> + .driver_module = THIS_MODULE, >>>>> + .read_raw_multi = &bno055_read_raw_multi, >>>>> +}; >>>>> + >>>> >>>> drop newline >>>> >>>>> + >>>>> +static int bno055_chip_init(struct bno055_data *data) >>>>> +{ >>>>> + int ret; >>>>> + >>>>> + ret = regmap_write(data->regmap, REG_PWR_MODE, >>>>> + NORMAL_MODE); >>>>> + if (ret < 0) { >>>>> + dev_err(&data->client->dev, >>>>> + "failed to write power mode register\n"); >>>>> + return ret; >>>>> + } >>>>> + >>>>> + ret = regmap_write(data->regmap, REG_OPR_MODE, >>>>> + FUSION_NDOF_MODE); >>>>> + if (ret < 0) >>>>> + dev_err(&data->client->dev, >>>>> + "failed to write operation mode register\n"); >>>>> + >>>>> + return ret; >>>>> +} >>>>> + >>>>> +static ssize_t show_calibration_status(struct device *dev, struct >>>> device_attribute *attr, >>>>> + char *buf){ >>>> >>>> this is private API which needs to be documented >>>> >>>>> + struct bno055_data *data; >>>>> + int ret; >>>>> + unsigned int calib_stat; >>>>> + >>>>> + data = dev->driver_data; >>>>> + >>>>> + ret = regmap_read(data->regmap, CALIB_STATUS, &calib_stat); >>>>> + >>>>> + if (ret){ >>>> >>>> space before { >>>> >>>>> + dev_err(dev, "Failed to read calibration status.\n"); >>>>> + return ret; >>>>> + } >>>>> + >>>>> + if (calib_stat != PROPER_CALIBRATION) >>>>> + dev_info(dev, "bad calibration. expected %x got %x\n", >>>> >>>> two spaces before 'expected' >>>> >>>>> + PROPER_CALIBRATION, calib_stat); >>>>> + >>>>> + return scnprintf(buf, PAGE_SIZE, "0x%x\n", calib_stat); >>>>> +} >>>>> + >>>>> +static DEVICE_ATTR(calib_status, 0444, show_calibration_status, NULL); >>>>> + >>>>> +static int bno055_probe(struct i2c_client *client, >>>>> + const struct i2c_device_id *id) >>>>> +{ >>>>> + int ret; >>>>> + struct iio_dev *indio_dev; >>>>> + struct bno055_data *data; >>>>> + unsigned int chip_id; >>>>> + >>>>> + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); >>>>> + if (!indio_dev) >>>>> + return -ENOMEM; >>>>> + >>>>> + data = iio_priv(indio_dev); >>>>> + mutex_init(&data->lock); >>>>> + data->client = client; >>>>> + >>>>> + indio_dev->dev.parent = &client->dev; >>>>> + indio_dev->name = id->name; >>>>> + indio_dev->channels = bno055_channels; >>>>> + indio_dev->num_channels = ARRAY_SIZE(bno055_channels); >>>>> + indio_dev->info = &bno055_info; >>>>> + indio_dev->modes = INDIO_DIRECT_MODE; >>>>> + >>>>> + data->regmap = devm_regmap_init_i2c(client, &bno055_regmap_config); >>>>> + if (IS_ERR(data->regmap)) { >>>>> + dev_err(&client->dev, "Failed to allocate register >>>> map.\n"); >>>>> + return PTR_ERR(data->regmap); >>>>> + } >>>>> + >>>>> + indio_dev->dev.driver_data = data; >>>>> + i2c_set_clientdata(client, indio_dev); >>>>> + >>>>> + ret = regmap_read(data->regmap, BNO055_REG_ID, &chip_id); >>>>> + if (ret < 0) >>>>> + return ret; >>>>> + if (chip_id != BNO055_CHIP_ID) { >>>>> + dev_err(&client->dev, "bad chip id. expected %x got %x\n", >>>>> + BNO055_CHIP_ID, chip_id); >>>>> + return -EINVAL; >>>>> + } >>>>> + >>>>> + ret = bno055_chip_init(data); >>>>> + if (ret < 0) >>>>> + return ret; >>>>> + >>>>> + ret = devm_iio_device_register(&client->dev, indio_dev); >>>>> + if (ret){ >>>> >>>> space before { >>>> >>>>> + dev_err(&client->dev, "failed to register IIO device.\n"); >>>>> + return ret; >>>>> + } >>>>> + >>>>> + ret = device_create_file(&indio_dev->dev, &dev_attr_calib_status); >>>> >>>> needs to be done BEFORE devm_iio_device_register() >>>> IIO has specific mechanisms to create custom sysfs entries... >>>> >>>>> + if (ret){ >>>> >>>> space before { >>>> >>>>> + dev_err(&client->dev, "failed to create sysfs entry.\n"); >>>>> + devm_iio_device_unregister(&client->dev, indio_dev); >>>> >>>> no need to call devm_iio_device_unregister >>>> >>>>> + return ret; >>>>> + } >>>>> + >>>>> + return ret; >>>>> +} >>>>> + >>>>> +static int bno055_remove(struct i2c_client *client) >>>>> +{ >>>>> + struct iio_dev *indio_dev = i2c_get_clientdata(client); >>>>> + >>>>> + device_remove_file(&indio_dev->dev, &dev_attr_calib_status); >>>>> + devm_iio_device_unregister(&client->dev, indio_dev); >>>>> + return 0; >>>>> +} >>>>> + >>>>> +static const struct acpi_device_id bno055_acpi_match[] = { >>>>> + {"bno055", 0}, >>>>> + { }, >>>>> +}; >>>>> +MODULE_DEVICE_TABLE(acpi, bno055_acpi_match); >>>>> + >>>>> +static const struct i2c_device_id bno055_id[] = { >>>>> + {"bno055", 0}, >>>>> + { }, >>>>> +}; >>>>> +MODULE_DEVICE_TABLE(i2c, bno055_id); >>>>> + >>>>> +static struct i2c_driver bno055_driver = { >>>>> + .driver = { >>>>> + .name = "bno055", >>>>> + .acpi_match_table = ACPI_PTR(bno055_acpi_match), >>>>> + }, >>>>> + .probe = bno055_probe, >>>>> + .remove = bno055_remove, >>>>> + .id_table = bno055_id, >>>>> +}; >>>>> +module_i2c_driver(bno055_driver); >>>>> + >>>>> +MODULE_AUTHOR("navin patidar <navin.patidar@xxxxxxxxx>"); >>>>> +MODULE_DESCRIPTION("Driver for Bosch Sensortec BNO055 orientation >>>> sensor"); >>>>> +MODULE_LICENSE("GPL v2"); >>>>> >>>> >>>> -- >>>> >>>> Peter Meerwald-Stadler >>>> +43-664-2444418 (mobile) >>>> >>> >> >> -- >> >> Peter Meerwald-Stadler >> +43-664-2444418 (mobile) -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html