Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

iio: imu: Add i2c driver for bmi270 imu

Add initial i2c support for the Bosch BMI270 6-axis IMU.
Provides raw read access to acceleration and angle velocity measurements
via iio channels. Device configuration requires firmware provided by
Bosch and is requested and load from userspace.

Signed-off-by: Alex Lanzano <lanzano.alex@gmail.com>
Link: https://patch.msgid.link/20240912210749.3080157-3-lanzano.alex@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Alex Lanzano and committed by
Jonathan Cameron
3ea51548 242b6890

+414
+7
MAINTAINERS
··· 4035 4035 F: Documentation/devicetree/bindings/iio/accel/bosch,bma400.yaml 4036 4036 F: drivers/iio/accel/bma400* 4037 4037 4038 + BOSCH SENSORTEC BMI270 IMU IIO DRIVER 4039 + M: Alex Lanzano <lanzano.alex@gmail.com> 4040 + L: linux-iio@vger.kernel.org 4041 + S: Maintained 4042 + F: Documentation/devicetree/bindings/iio/imu/bosch,bmi270.yaml 4043 + F: drivers/iio/imu/bmi270/ 4044 + 4038 4045 BOSCH SENSORTEC BMI323 IMU IIO DRIVER 4039 4046 M: Jagath Jog J <jagathjog1996@gmail.com> 4040 4047 L: linux-iio@vger.kernel.org
+1
drivers/iio/imu/Kconfig
··· 53 53 ADIS16485, ADIS16488 inertial sensors. 54 54 55 55 source "drivers/iio/imu/bmi160/Kconfig" 56 + source "drivers/iio/imu/bmi270/Kconfig" 56 57 source "drivers/iio/imu/bmi323/Kconfig" 57 58 source "drivers/iio/imu/bno055/Kconfig" 58 59
+1
drivers/iio/imu/Makefile
··· 15 15 obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o 16 16 17 17 obj-y += bmi160/ 18 + obj-y += bmi270/ 18 19 obj-y += bmi323/ 19 20 obj-y += bno055/ 20 21
+20
drivers/iio/imu/bmi270/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + # 3 + # BMI270 IMU driver 4 + # 5 + 6 + config BMI270 7 + tristate 8 + select IIO_BUFFER 9 + 10 + config BMI270_I2C 11 + tristate "Bosch BMI270 I2C driver" 12 + depends on I2C 13 + select BMI270 14 + select REGMAP_I2C 15 + help 16 + Enable support for the Bosch BMI270 6-Axis IMU connected to I2C 17 + interface. 18 + 19 + This driver can also be built as a module. If so, the module will be 20 + called bmi270_i2c.
+6
drivers/iio/imu/bmi270/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + # 3 + # Makefile for Bosch BMI270 IMU 4 + # 5 + obj-$(CONFIG_BMI270) += bmi270_core.o 6 + obj-$(CONFIG_BMI270_I2C) += bmi270_i2c.o
+18
drivers/iio/imu/bmi270/bmi270.h
··· 1 + /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ 2 + 3 + #ifndef BMI270_H_ 4 + #define BMI270_H_ 5 + 6 + #include <linux/regmap.h> 7 + 8 + struct device; 9 + struct bmi270_data { 10 + struct device *dev; 11 + struct regmap *regmap; 12 + }; 13 + 14 + extern const struct regmap_config bmi270_regmap_config; 15 + 16 + int bmi270_core_probe(struct device *dev, struct regmap *regmap); 17 + 18 + #endif /* BMI270_H_ */
+313
drivers/iio/imu/bmi270/bmi270_core.c
··· 1 + // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + 3 + #include <linux/bitfield.h> 4 + #include <linux/firmware.h> 5 + #include <linux/i2c.h> 6 + #include <linux/module.h> 7 + #include <linux/regmap.h> 8 + 9 + #include <linux/iio/iio.h> 10 + 11 + #include "bmi270.h" 12 + 13 + #define BMI270_CHIP_ID_REG 0x00 14 + #define BMI270_CHIP_ID_VAL 0x24 15 + #define BMI270_CHIP_ID_MSK GENMASK(7, 0) 16 + 17 + #define BMI270_ACCEL_X_REG 0x0c 18 + #define BMI270_ANG_VEL_X_REG 0x12 19 + 20 + #define BMI270_INTERNAL_STATUS_REG 0x21 21 + #define BMI270_INTERNAL_STATUS_MSG_MSK GENMASK(3, 0) 22 + #define BMI270_INTERNAL_STATUS_MSG_INIT_OK 0x01 23 + 24 + #define BMI270_INTERNAL_STATUS_AXES_REMAP_ERR_MSK BIT(5) 25 + #define BMI270_INTERNAL_STATUS_ODR_50HZ_ERR_MSK BIT(6) 26 + 27 + #define BMI270_ACC_CONF_REG 0x40 28 + #define BMI270_ACC_CONF_ODR_MSK GENMASK(3, 0) 29 + #define BMI270_ACC_CONF_ODR_100HZ 0x08 30 + #define BMI270_ACC_CONF_BWP_MSK GENMASK(6, 4) 31 + #define BMI270_ACC_CONF_BWP_NORMAL_MODE 0x02 32 + #define BMI270_ACC_CONF_FILTER_PERF_MSK BIT(7) 33 + 34 + #define BMI270_GYR_CONF_REG 0x42 35 + #define BMI270_GYR_CONF_ODR_MSK GENMASK(3, 0) 36 + #define BMI270_GYR_CONF_ODR_200HZ 0x09 37 + #define BMI270_GYR_CONF_BWP_MSK GENMASK(5, 4) 38 + #define BMI270_GYR_CONF_BWP_NORMAL_MODE 0x02 39 + #define BMI270_GYR_CONF_NOISE_PERF_MSK BIT(6) 40 + #define BMI270_GYR_CONF_FILTER_PERF_MSK BIT(7) 41 + 42 + #define BMI270_INIT_CTRL_REG 0x59 43 + #define BMI270_INIT_CTRL_LOAD_DONE_MSK BIT(0) 44 + 45 + #define BMI270_INIT_DATA_REG 0x5e 46 + 47 + #define BMI270_PWR_CONF_REG 0x7c 48 + #define BMI270_PWR_CONF_ADV_PWR_SAVE_MSK BIT(0) 49 + #define BMI270_PWR_CONF_FIFO_WKUP_MSK BIT(1) 50 + #define BMI270_PWR_CONF_FUP_EN_MSK BIT(2) 51 + 52 + #define BMI270_PWR_CTRL_REG 0x7d 53 + #define BMI270_PWR_CTRL_AUX_EN_MSK BIT(0) 54 + #define BMI270_PWR_CTRL_GYR_EN_MSK BIT(1) 55 + #define BMI270_PWR_CTRL_ACCEL_EN_MSK BIT(2) 56 + #define BMI270_PWR_CTRL_TEMP_EN_MSK BIT(3) 57 + 58 + #define BMI270_INIT_DATA_FILE "bmi270-init-data.fw" 59 + 60 + enum bmi270_scan { 61 + BMI270_SCAN_ACCEL_X, 62 + BMI270_SCAN_ACCEL_Y, 63 + BMI270_SCAN_ACCEL_Z, 64 + BMI270_SCAN_GYRO_X, 65 + BMI270_SCAN_GYRO_Y, 66 + BMI270_SCAN_GYRO_Z, 67 + }; 68 + 69 + const struct regmap_config bmi270_regmap_config = { 70 + .reg_bits = 8, 71 + .val_bits = 8, 72 + }; 73 + EXPORT_SYMBOL_NS_GPL(bmi270_regmap_config, IIO_BMI270); 74 + 75 + static int bmi270_get_data(struct bmi270_data *bmi270_device, 76 + int chan_type, int axis, int *val) 77 + { 78 + __le16 sample; 79 + int reg; 80 + int ret; 81 + 82 + switch (chan_type) { 83 + case IIO_ACCEL: 84 + reg = BMI270_ACCEL_X_REG + (axis - IIO_MOD_X) * 2; 85 + break; 86 + case IIO_ANGL_VEL: 87 + reg = BMI270_ANG_VEL_X_REG + (axis - IIO_MOD_X) * 2; 88 + break; 89 + default: 90 + return -EINVAL; 91 + } 92 + 93 + ret = regmap_bulk_read(bmi270_device->regmap, reg, &sample, sizeof(sample)); 94 + if (ret) 95 + return ret; 96 + 97 + *val = sign_extend32(le16_to_cpu(sample), 15); 98 + 99 + return 0; 100 + } 101 + 102 + static int bmi270_read_raw(struct iio_dev *indio_dev, 103 + struct iio_chan_spec const *chan, 104 + int *val, int *val2, long mask) 105 + { 106 + int ret; 107 + struct bmi270_data *bmi270_device = iio_priv(indio_dev); 108 + 109 + switch (mask) { 110 + case IIO_CHAN_INFO_RAW: 111 + ret = bmi270_get_data(bmi270_device, chan->type, chan->channel2, val); 112 + if (ret) 113 + return ret; 114 + 115 + return IIO_VAL_INT; 116 + default: 117 + return -EINVAL; 118 + } 119 + } 120 + 121 + static const struct iio_info bmi270_info = { 122 + .read_raw = bmi270_read_raw, 123 + }; 124 + 125 + #define BMI270_ACCEL_CHANNEL(_axis) { \ 126 + .type = IIO_ACCEL, \ 127 + .modified = 1, \ 128 + .channel2 = IIO_MOD_##_axis, \ 129 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 130 + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ 131 + BIT(IIO_CHAN_INFO_FREQUENCY), \ 132 + } 133 + 134 + #define BMI270_ANG_VEL_CHANNEL(_axis) { \ 135 + .type = IIO_ANGL_VEL, \ 136 + .modified = 1, \ 137 + .channel2 = IIO_MOD_##_axis, \ 138 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 139 + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ 140 + BIT(IIO_CHAN_INFO_FREQUENCY), \ 141 + } 142 + 143 + static const struct iio_chan_spec bmi270_channels[] = { 144 + BMI270_ACCEL_CHANNEL(X), 145 + BMI270_ACCEL_CHANNEL(Y), 146 + BMI270_ACCEL_CHANNEL(Z), 147 + BMI270_ANG_VEL_CHANNEL(X), 148 + BMI270_ANG_VEL_CHANNEL(Y), 149 + BMI270_ANG_VEL_CHANNEL(Z), 150 + }; 151 + 152 + static int bmi270_validate_chip_id(struct bmi270_data *bmi270_device) 153 + { 154 + int chip_id; 155 + int ret; 156 + struct device *dev = bmi270_device->dev; 157 + struct regmap *regmap = bmi270_device->regmap; 158 + 159 + ret = regmap_read(regmap, BMI270_CHIP_ID_REG, &chip_id); 160 + if (ret) 161 + return dev_err_probe(dev, ret, "Failed to read chip id"); 162 + 163 + if (chip_id != BMI270_CHIP_ID_VAL) 164 + dev_info(dev, "Unknown chip id 0x%x", chip_id); 165 + 166 + return 0; 167 + } 168 + 169 + static int bmi270_write_calibration_data(struct bmi270_data *bmi270_device) 170 + { 171 + int ret; 172 + int status = 0; 173 + const struct firmware *init_data; 174 + struct device *dev = bmi270_device->dev; 175 + struct regmap *regmap = bmi270_device->regmap; 176 + 177 + ret = regmap_clear_bits(regmap, BMI270_PWR_CONF_REG, 178 + BMI270_PWR_CONF_ADV_PWR_SAVE_MSK); 179 + if (ret) 180 + return dev_err_probe(dev, ret, 181 + "Failed to write power configuration"); 182 + 183 + /* 184 + * After disabling advanced power save, all registers are accessible 185 + * after a 450us delay. This delay is specified in table A of the 186 + * datasheet. 187 + */ 188 + usleep_range(450, 1000); 189 + 190 + ret = regmap_clear_bits(regmap, BMI270_INIT_CTRL_REG, 191 + BMI270_INIT_CTRL_LOAD_DONE_MSK); 192 + if (ret) 193 + return dev_err_probe(dev, ret, 194 + "Failed to prepare device to load init data"); 195 + 196 + ret = request_firmware(&init_data, BMI270_INIT_DATA_FILE, dev); 197 + if (ret) 198 + return dev_err_probe(dev, ret, "Failed to load init data file"); 199 + 200 + ret = regmap_bulk_write(regmap, BMI270_INIT_DATA_REG, 201 + init_data->data, init_data->size); 202 + release_firmware(init_data); 203 + if (ret) 204 + return dev_err_probe(dev, ret, "Failed to write init data"); 205 + 206 + ret = regmap_set_bits(regmap, BMI270_INIT_CTRL_REG, 207 + BMI270_INIT_CTRL_LOAD_DONE_MSK); 208 + if (ret) 209 + return dev_err_probe(dev, ret, 210 + "Failed to stop device initialization"); 211 + 212 + /* 213 + * Wait at least 140ms for the device to complete configuration. 214 + * This delay is specified in table C of the datasheet. 215 + */ 216 + usleep_range(140000, 160000); 217 + 218 + ret = regmap_read(regmap, BMI270_INTERNAL_STATUS_REG, &status); 219 + if (ret) 220 + return dev_err_probe(dev, ret, "Failed to read internal status"); 221 + 222 + if (status != BMI270_INTERNAL_STATUS_MSG_INIT_OK) 223 + return dev_err_probe(dev, -ENODEV, "Device failed to initialize"); 224 + 225 + return 0; 226 + } 227 + 228 + static int bmi270_configure_imu(struct bmi270_data *bmi270_device) 229 + { 230 + int ret; 231 + struct device *dev = bmi270_device->dev; 232 + struct regmap *regmap = bmi270_device->regmap; 233 + 234 + ret = regmap_set_bits(regmap, BMI270_PWR_CTRL_REG, 235 + BMI270_PWR_CTRL_AUX_EN_MSK | 236 + BMI270_PWR_CTRL_GYR_EN_MSK | 237 + BMI270_PWR_CTRL_ACCEL_EN_MSK); 238 + if (ret) 239 + return dev_err_probe(dev, ret, "Failed to enable accelerometer and gyroscope"); 240 + 241 + ret = regmap_set_bits(regmap, BMI270_ACC_CONF_REG, 242 + FIELD_PREP(BMI270_ACC_CONF_ODR_MSK, 243 + BMI270_ACC_CONF_ODR_100HZ) | 244 + FIELD_PREP(BMI270_ACC_CONF_BWP_MSK, 245 + BMI270_ACC_CONF_BWP_NORMAL_MODE) | 246 + BMI270_PWR_CONF_ADV_PWR_SAVE_MSK); 247 + if (ret) 248 + return dev_err_probe(dev, ret, "Failed to configure accelerometer"); 249 + 250 + ret = regmap_set_bits(regmap, BMI270_GYR_CONF_REG, 251 + FIELD_PREP(BMI270_GYR_CONF_ODR_MSK, 252 + BMI270_GYR_CONF_ODR_200HZ) | 253 + FIELD_PREP(BMI270_GYR_CONF_BWP_MSK, 254 + BMI270_GYR_CONF_BWP_NORMAL_MODE) | 255 + BMI270_PWR_CONF_ADV_PWR_SAVE_MSK); 256 + if (ret) 257 + return dev_err_probe(dev, ret, "Failed to configure gyroscope"); 258 + 259 + /* Enable FIFO_WKUP, Disable ADV_PWR_SAVE and FUP_EN */ 260 + ret = regmap_write(regmap, BMI270_PWR_CONF_REG, 261 + BMI270_PWR_CONF_FIFO_WKUP_MSK); 262 + if (ret) 263 + return dev_err_probe(dev, ret, "Failed to set power configuration"); 264 + 265 + return 0; 266 + } 267 + 268 + static int bmi270_chip_init(struct bmi270_data *bmi270_device) 269 + { 270 + int ret; 271 + 272 + ret = bmi270_validate_chip_id(bmi270_device); 273 + if (ret) 274 + return ret; 275 + 276 + ret = bmi270_write_calibration_data(bmi270_device); 277 + if (ret) 278 + return ret; 279 + 280 + return bmi270_configure_imu(bmi270_device); 281 + } 282 + 283 + int bmi270_core_probe(struct device *dev, struct regmap *regmap) 284 + { 285 + int ret; 286 + struct bmi270_data *bmi270_device; 287 + struct iio_dev *indio_dev; 288 + 289 + indio_dev = devm_iio_device_alloc(dev, sizeof(*bmi270_device)); 290 + if (!indio_dev) 291 + return -ENOMEM; 292 + 293 + bmi270_device = iio_priv(indio_dev); 294 + bmi270_device->dev = dev; 295 + bmi270_device->regmap = regmap; 296 + 297 + ret = bmi270_chip_init(bmi270_device); 298 + if (ret) 299 + return ret; 300 + 301 + indio_dev->channels = bmi270_channels; 302 + indio_dev->num_channels = ARRAY_SIZE(bmi270_channels); 303 + indio_dev->name = "bmi270"; 304 + indio_dev->modes = INDIO_DIRECT_MODE; 305 + indio_dev->info = &bmi270_info; 306 + 307 + return devm_iio_device_register(dev, indio_dev); 308 + } 309 + EXPORT_SYMBOL_NS_GPL(bmi270_core_probe, IIO_BMI270); 310 + 311 + MODULE_AUTHOR("Alex Lanzano"); 312 + MODULE_DESCRIPTION("BMI270 driver"); 313 + MODULE_LICENSE("GPL");
+48
drivers/iio/imu/bmi270/bmi270_i2c.c
··· 1 + // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + 3 + #include <linux/module.h> 4 + #include <linux/i2c.h> 5 + #include <linux/iio/iio.h> 6 + #include <linux/module.h> 7 + #include <linux/mod_devicetable.h> 8 + #include <linux/regmap.h> 9 + 10 + #include "bmi270.h" 11 + 12 + static int bmi270_i2c_probe(struct i2c_client *client) 13 + { 14 + struct regmap *regmap; 15 + struct device *dev = &client->dev; 16 + 17 + regmap = devm_regmap_init_i2c(client, &bmi270_regmap_config); 18 + if (IS_ERR(regmap)) 19 + return dev_err_probe(dev, PTR_ERR(regmap), 20 + "Failed to init i2c regmap"); 21 + 22 + return bmi270_core_probe(dev, regmap); 23 + } 24 + 25 + static const struct i2c_device_id bmi270_i2c_id[] = { 26 + { "bmi270", 0 }, 27 + { } 28 + }; 29 + 30 + static const struct of_device_id bmi270_of_match[] = { 31 + { .compatible = "bosch,bmi270" }, 32 + { } 33 + }; 34 + 35 + static struct i2c_driver bmi270_i2c_driver = { 36 + .driver = { 37 + .name = "bmi270_i2c", 38 + .of_match_table = bmi270_of_match, 39 + }, 40 + .probe = bmi270_i2c_probe, 41 + .id_table = bmi270_i2c_id, 42 + }; 43 + module_i2c_driver(bmi270_i2c_driver); 44 + 45 + MODULE_AUTHOR("Alex Lanzano"); 46 + MODULE_DESCRIPTION("BMI270 driver"); 47 + MODULE_LICENSE("GPL"); 48 + MODULE_IMPORT_NS(IIO_BMI270);