On Thu, 12 Sep 2024 17:07:19 -0400 Alex Lanzano <lanzano.alex@xxxxxxxxx> wrote: > 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@xxxxxxxxx> Hi Alex, A bunch of fairly minor stuff so I've just tweaked it whilst applying rather than wasting time on going around again. I also changed a few line wraps to keep lines a bit shorter. Where it doesn't hurt readability 80 chars is still my preference for IIO. The diff of the tweaks I made is as follows. Shout if I messed anything up! Thanks, Jonathan diff --git a/drivers/iio/imu/bmi270/bmi270.h b/drivers/iio/imu/bmi270/bmi270.h index 4af4098d8e82..608b29ea58a3 100644 --- a/drivers/iio/imu/bmi270/bmi270.h +++ b/drivers/iio/imu/bmi270/bmi270.h @@ -3,53 +3,9 @@ #ifndef BMI270_H_ #define BMI270_H_ -#include <linux/iio/iio.h> - -#define BMI270_CHIP_ID_REG 0x00 -#define BMI270_CHIP_ID_VAL 0x24 -#define BMI270_CHIP_ID_MSK GENMASK(7, 0) - -#define BMI270_ACCEL_X_REG 0x0c -#define BMI270_ANG_VEL_X_REG 0x12 - -#define BMI270_INTERNAL_STATUS_REG 0x21 -#define BMI270_INTERNAL_STATUS_MSG_MSK GENMASK(3, 0) -#define BMI270_INTERNAL_STATUS_MSG_INIT_OK 0x01 - -#define BMI270_INTERNAL_STATUS_AXES_REMAP_ERR_MSK BIT(5) -#define BMI270_INTERNAL_STATUS_ODR_50HZ_ERR_MSK BIT(6) - -#define BMI270_ACC_CONF_REG 0x40 -#define BMI270_ACC_CONF_ODR_MSK GENMASK(3, 0) -#define BMI270_ACC_CONF_ODR_100HZ 0x08 -#define BMI270_ACC_CONF_BWP_MSK GENMASK(6, 4) -#define BMI270_ACC_CONF_BWP_NORMAL_MODE 0x02 -#define BMI270_ACC_CONF_FILTER_PERF_MSK BIT(7) - -#define BMI270_GYR_CONF_REG 0x42 -#define BMI270_GYR_CONF_ODR_MSK GENMASK(3, 0) -#define BMI270_GYR_CONF_ODR_200HZ 0x09 -#define BMI270_GYR_CONF_BWP_MSK GENMASK(5, 4) -#define BMI270_GYR_CONF_BWP_NORMAL_MODE 0x02 -#define BMI270_GYR_CONF_NOISE_PERF_MSK BIT(6) -#define BMI270_GYR_CONF_FILTER_PERF_MSK BIT(7) - -#define BMI270_INIT_CTRL_REG 0x59 -#define BMI270_INIT_CTRL_LOAD_DONE_MSK BIT(0) - -#define BMI270_INIT_DATA_REG 0x5e - -#define BMI270_PWR_CONF_REG 0x7c -#define BMI270_PWR_CONF_ADV_PWR_SAVE_MSK BIT(0) -#define BMI270_PWR_CONF_FIFO_WKUP_MSK BIT(1) -#define BMI270_PWR_CONF_FUP_EN_MSK BIT(2) - -#define BMI270_PWR_CTRL_REG 0x7d -#define BMI270_PWR_CTRL_AUX_EN_MSK BIT(0) -#define BMI270_PWR_CTRL_GYR_EN_MSK BIT(1) -#define BMI270_PWR_CTRL_ACCEL_EN_MSK BIT(2) -#define BMI270_PWR_CTRL_TEMP_EN_MSK BIT(3) +#include <linux/regmap.h> +struct device; struct bmi270_data { struct device *dev; struct regmap *regmap; iff --git a/drivers/iio/imu/bmi270/bmi270_core.c b/drivers/iio/imu/bmi270/bmi270_core.c index 319e5601d9e7..8e45343d6472 100644 --- a/drivers/iio/imu/bmi270/bmi270_core.c +++ b/drivers/iio/imu/bmi270/bmi270_core.c @@ -1,14 +1,60 @@ // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +#include <linux/bitfield.h> #include <linux/firmware.h> #include <linux/i2c.h> -#include <linux/iio/iio.h> #include <linux/module.h> #include <linux/regmap.h> -#include <linux/bitfield.h> + +#include <linux/iio/iio.h> #include "bmi270.h" +#define BMI270_CHIP_ID_REG 0x00 +#define BMI270_CHIP_ID_VAL 0x24 +#define BMI270_CHIP_ID_MSK GENMASK(7, 0) + +#define BMI270_ACCEL_X_REG 0x0c +#define BMI270_ANG_VEL_X_REG 0x12 + +#define BMI270_INTERNAL_STATUS_REG 0x21 +#define BMI270_INTERNAL_STATUS_MSG_MSK GENMASK(3, 0) +#define BMI270_INTERNAL_STATUS_MSG_INIT_OK 0x01 + +#define BMI270_INTERNAL_STATUS_AXES_REMAP_ERR_MSK BIT(5) +#define BMI270_INTERNAL_STATUS_ODR_50HZ_ERR_MSK BIT(6) + +#define BMI270_ACC_CONF_REG 0x40 +#define BMI270_ACC_CONF_ODR_MSK GENMASK(3, 0) +#define BMI270_ACC_CONF_ODR_100HZ 0x08 +#define BMI270_ACC_CONF_BWP_MSK GENMASK(6, 4) +#define BMI270_ACC_CONF_BWP_NORMAL_MODE 0x02 +#define BMI270_ACC_CONF_FILTER_PERF_MSK BIT(7) + +#define BMI270_GYR_CONF_REG 0x42 +#define BMI270_GYR_CONF_ODR_MSK GENMASK(3, 0) +#define BMI270_GYR_CONF_ODR_200HZ 0x09 +#define BMI270_GYR_CONF_BWP_MSK GENMASK(5, 4) +#define BMI270_GYR_CONF_BWP_NORMAL_MODE 0x02 +#define BMI270_GYR_CONF_NOISE_PERF_MSK BIT(6) +#define BMI270_GYR_CONF_FILTER_PERF_MSK BIT(7) + +#define BMI270_INIT_CTRL_REG 0x59 +#define BMI270_INIT_CTRL_LOAD_DONE_MSK BIT(0) + +#define BMI270_INIT_DATA_REG 0x5e + +#define BMI270_PWR_CONF_REG 0x7c +#define BMI270_PWR_CONF_ADV_PWR_SAVE_MSK BIT(0) +#define BMI270_PWR_CONF_FIFO_WKUP_MSK BIT(1) +#define BMI270_PWR_CONF_FUP_EN_MSK BIT(2) + +#define BMI270_PWR_CTRL_REG 0x7d +#define BMI270_PWR_CTRL_AUX_EN_MSK BIT(0) +#define BMI270_PWR_CTRL_GYR_EN_MSK BIT(1) +#define BMI270_PWR_CTRL_ACCEL_EN_MSK BIT(2) +#define BMI270_PWR_CTRL_TEMP_EN_MSK BIT(3) + #define BMI270_INIT_DATA_FILE "bmi270-init-data.fw" enum bmi270_scan { @@ -35,10 +81,10 @@ static int bmi270_get_data(struct bmi270_data *bmi270_device, switch (chan_type) { case IIO_ACCEL: - reg = BMI270_ACCEL_X_REG + (axis - IIO_MOD_X) * sizeof(sample); + reg = BMI270_ACCEL_X_REG + (axis - IIO_MOD_X) * 2; break; case IIO_ANGL_VEL: - reg = BMI270_ANG_VEL_X_REG + (axis - IIO_MOD_X) * sizeof(sample); + reg = BMI270_ANG_VEL_X_REG + (axis - IIO_MOD_X) * 2; break; default: return -EINVAL; @@ -82,7 +128,7 @@ static const struct iio_info bmi270_info = { .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_FREQUENCY), \ + BIT(IIO_CHAN_INFO_FREQUENCY), \ } #define BMI270_ANG_VEL_CHANNEL(_axis) { \ @@ -91,7 +137,7 @@ static const struct iio_info bmi270_info = { .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_FREQUENCY), \ + BIT(IIO_CHAN_INFO_FREQUENCY), \ } static const struct iio_chan_spec bmi270_channels[] = { @@ -100,7 +146,7 @@ static const struct iio_chan_spec bmi270_channels[] = { BMI270_ACCEL_CHANNEL(Z), BMI270_ANG_VEL_CHANNEL(X), BMI270_ANG_VEL_CHANNEL(Y), - BMI270_ANG_VEL_CHANNEL(Z) + BMI270_ANG_VEL_CHANNEL(Z), }; static int bmi270_validate_chip_id(struct bmi270_data *bmi270_device) @@ -115,7 +161,7 @@ static int bmi270_validate_chip_id(struct bmi270_data *bmi270_device) return dev_err_probe(dev, ret, "Failed to read chip id"); if (chip_id != BMI270_CHIP_ID_VAL) - return dev_err_probe(dev, -ENODEV, "Invalid chip id 0x%x", chip_id); + dev_info(dev, "Unknown chip id 0x%x", chip_id); return 0; } @@ -128,18 +174,24 @@ static int bmi270_write_calibration_data(struct bmi270_data *bmi270_device) struct device *dev = bmi270_device->dev; struct regmap *regmap = bmi270_device->regmap; - ret = regmap_clear_bits(regmap, BMI270_PWR_CONF_REG, BMI270_PWR_CONF_ADV_PWR_SAVE_MSK); + ret = regmap_clear_bits(regmap, BMI270_PWR_CONF_REG, + BMI270_PWR_CONF_ADV_PWR_SAVE_MSK); if (ret) - return dev_err_probe(dev, ret, "Failed to write power configuration"); + return dev_err_probe(dev, ret, + "Failed to write power configuration"); - /* After disabling advanced power save, all registers are accessible after a 450us delay - * This delay is specified in table A of the datasheet. + /* + * After disabling advanced power save, all registers are accessible + * after a 450us delay. This delay is specified in table A of the + * datasheet. */ usleep_range(450, 1000); - ret = regmap_clear_bits(regmap, BMI270_INIT_CTRL_REG, BMI270_INIT_CTRL_LOAD_DONE_MSK); + ret = regmap_clear_bits(regmap, BMI270_INIT_CTRL_REG, + BMI270_INIT_CTRL_LOAD_DONE_MSK); if (ret) - return dev_err_probe(dev, ret, "Failed to prepare device to load init data"); + return dev_err_probe(dev, ret, + "Failed to prepare device to load init data"); ret = request_firmware(&init_data, BMI270_INIT_DATA_FILE, dev); if (ret) @@ -151,11 +203,14 @@ static int bmi270_write_calibration_data(struct bmi270_data *bmi270_device) if (ret) return dev_err_probe(dev, ret, "Failed to write init data"); - ret = regmap_set_bits(regmap, BMI270_INIT_CTRL_REG, BMI270_INIT_CTRL_LOAD_DONE_MSK); + ret = regmap_set_bits(regmap, BMI270_INIT_CTRL_REG, + BMI270_INIT_CTRL_LOAD_DONE_MSK); if (ret) - return dev_err_probe(dev, ret, "Failed to stop device initialization"); + return dev_err_probe(dev, ret, + "Failed to stop device initialization"); - /* Wait at least 140ms for the device to complete configuration. + /* + * Wait at least 140ms for the device to complete configuration. * This delay is specified in table C of the datasheet. */ usleep_range(140000, 160000); @@ -231,7 +286,7 @@ int bmi270_core_probe(struct device *dev, struct regmap *regmap) struct bmi270_data *bmi270_device; struct iio_dev *indio_dev; - indio_dev = devm_iio_device_alloc(dev, sizeof(struct bmi270_data *)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*bmi270_device)); if (!indio_dev) return -ENOMEM; > diff --git a/drivers/iio/imu/bmi270/Makefile b/drivers/iio/imu/bmi270/Makefile > new file mode 100644 > index 000000000000..ab4acaaee6d2 > --- /dev/null > +++ b/drivers/iio/imu/bmi270/Makefile > @@ -0,0 +1,6 @@ > +# SPDX-License-Identifier: GPL-2.0 > +# > +# Makefile for Bosch BMI270 IMU > +# > +obj-$(CONFIG_BMI270) += bmi270_core.o > +obj-$(CONFIG_BMI270_I2C) += bmi270_i2c.o > diff --git a/drivers/iio/imu/bmi270/bmi270.h b/drivers/iio/imu/bmi270/bmi270.h > new file mode 100644 > index 000000000000..4af4098d8e82 > --- /dev/null > +++ b/drivers/iio/imu/bmi270/bmi270.h > @@ -0,0 +1,62 @@ > +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ > + > +#ifndef BMI270_H_ > +#define BMI270_H_ > + > +#include <linux/iio/iio.h> no obvious reason for this. Headers should only include things they need. However you do need some forward definitions for struct device; and an include of regmap.h for the regmap_config. > + > +#define BMI270_CHIP_ID_REG 0x00 > +#define BMI270_CHIP_ID_VAL 0x24 > +#define BMI270_CHIP_ID_MSK GENMASK(7, 0) > + > +#define BMI270_ACCEL_X_REG 0x0c > +#define BMI270_ANG_VEL_X_REG 0x12 > + > +#define BMI270_INTERNAL_STATUS_REG 0x21 > +#define BMI270_INTERNAL_STATUS_MSG_MSK GENMASK(3, 0) > +#define BMI270_INTERNAL_STATUS_MSG_INIT_OK 0x01 > + > +#define BMI270_INTERNAL_STATUS_AXES_REMAP_ERR_MSK BIT(5) > +#define BMI270_INTERNAL_STATUS_ODR_50HZ_ERR_MSK BIT(6) > + > +#define BMI270_ACC_CONF_REG 0x40 > +#define BMI270_ACC_CONF_ODR_MSK GENMASK(3, 0) > +#define BMI270_ACC_CONF_ODR_100HZ 0x08 > +#define BMI270_ACC_CONF_BWP_MSK GENMASK(6, 4) > +#define BMI270_ACC_CONF_BWP_NORMAL_MODE 0x02 > +#define BMI270_ACC_CONF_FILTER_PERF_MSK BIT(7) > + > +#define BMI270_GYR_CONF_REG 0x42 > +#define BMI270_GYR_CONF_ODR_MSK GENMASK(3, 0) > +#define BMI270_GYR_CONF_ODR_200HZ 0x09 > +#define BMI270_GYR_CONF_BWP_MSK GENMASK(5, 4) > +#define BMI270_GYR_CONF_BWP_NORMAL_MODE 0x02 > +#define BMI270_GYR_CONF_NOISE_PERF_MSK BIT(6) > +#define BMI270_GYR_CONF_FILTER_PERF_MSK BIT(7) > + > +#define BMI270_INIT_CTRL_REG 0x59 > +#define BMI270_INIT_CTRL_LOAD_DONE_MSK BIT(0) > + > +#define BMI270_INIT_DATA_REG 0x5e > + > +#define BMI270_PWR_CONF_REG 0x7c > +#define BMI270_PWR_CONF_ADV_PWR_SAVE_MSK BIT(0) > +#define BMI270_PWR_CONF_FIFO_WKUP_MSK BIT(1) > +#define BMI270_PWR_CONF_FUP_EN_MSK BIT(2) > + > +#define BMI270_PWR_CTRL_REG 0x7d > +#define BMI270_PWR_CTRL_AUX_EN_MSK BIT(0) > +#define BMI270_PWR_CTRL_GYR_EN_MSK BIT(1) > +#define BMI270_PWR_CTRL_ACCEL_EN_MSK BIT(2) > +#define BMI270_PWR_CTRL_TEMP_EN_MSK BIT(3) Currently all these defines are just used in the core c file. So I'd move them there for now. We can drag them back into the header if the spi bus driver needs them. > + > +struct bmi270_data { > + struct device *dev; > + struct regmap *regmap; > +}; > + > +extern const struct regmap_config bmi270_regmap_config; > + > +int bmi270_core_probe(struct device *dev, struct regmap *regmap); > + > +#endif /* BMI270_H_ */ > diff --git a/drivers/iio/imu/bmi270/bmi270_core.c b/drivers/iio/imu/bmi270/bmi270_core.c > new file mode 100644 > index 000000000000..319e5601d9e7 > --- /dev/null > +++ b/drivers/iio/imu/bmi270/bmi270_core.c > @@ -0,0 +1,258 @@ > +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) > + > +#include <linux/firmware.h> > +#include <linux/i2c.h> > +#include <linux/iio/iio.h> > +#include <linux/module.h> > +#include <linux/regmap.h> > +#include <linux/bitfield.h> > + > +#include "bmi270.h" > + > +#define BMI270_INIT_DATA_FILE "bmi270-init-data.fw" > + > +enum bmi270_scan { > + BMI270_SCAN_ACCEL_X, > + BMI270_SCAN_ACCEL_Y, > + BMI270_SCAN_ACCEL_Z, > + BMI270_SCAN_GYRO_X, > + BMI270_SCAN_GYRO_Y, > + BMI270_SCAN_GYRO_Z, > +}; > + > +const struct regmap_config bmi270_regmap_config = { > + .reg_bits = 8, > + .val_bits = 8, > +}; > +EXPORT_SYMBOL_NS_GPL(bmi270_regmap_config, IIO_BMI270); > + > +static int bmi270_get_data(struct bmi270_data *bmi270_device, > + int chan_type, int axis, int *val) > +{ > + __le16 sample; > + int reg; > + int ret; > + > + switch (chan_type) { > + case IIO_ACCEL: > + reg = BMI270_ACCEL_X_REG + (axis - IIO_MOD_X) * sizeof(sample); > + break; > + case IIO_ANGL_VEL: > + reg = BMI270_ANG_VEL_X_REG + (axis - IIO_MOD_X) * sizeof(sample); This only works because they are 1 byte registers which isn't obvious here So I don't think sizeof(sample) is very useful vs 2. > + break; > + default: > + return -EINVAL; > + } > + > + ret = regmap_bulk_read(bmi270_device->regmap, reg, &sample, sizeof(sample)); > + if (ret) > + return ret; > + > + *val = sign_extend32(le16_to_cpu(sample), 15); > + > + return 0; > +} > + > + > +#define BMI270_ACCEL_CHANNEL(_axis) { \ > + .type = IIO_ACCEL, \ > + .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_FREQUENCY), \ Probably good to indent this last item by one more tab to help with reability. > +} > + > +#define BMI270_ANG_VEL_CHANNEL(_axis) { \ > + .type = IIO_ANGL_VEL, \ > + .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_FREQUENCY), \ > +} > +./v4_20240912_lanzano_alex_add_i2c_driver_for_bosch_bmi270_imu.mbx > +static const struct iio_chan_spec bmi270_channels[] = { > + BMI270_ACCEL_CHANNEL(X), > + BMI270_ACCEL_CHANNEL(Y), > + BMI270_ACCEL_CHANNEL(Z), > + BMI270_ANG_VEL_CHANNEL(X), > + BMI270_ANG_VEL_CHANNEL(Y), > + BMI270_ANG_VEL_CHANNEL(Z) Add a trailing comma as we may well have additional channels in future that come after this. > +}; > + > +static int bmi270_validate_chip_id(struct bmi270_data *bmi270_device) > +{ > + int chip_id; > + int ret; > + struct device *dev = bmi270_device->dev; > + struct regmap *regmap = bmi270_device->regmap; > + > + ret = regmap_read(regmap, BMI270_CHIP_ID_REG, &chip_id); > + if (ret) > + return dev_err_probe(dev, ret, "Failed to read chip id"); > + > + if (chip_id != BMI270_CHIP_ID_VAL) > + return dev_err_probe(dev, -ENODEV, "Invalid chip id 0x%x", chip_id); A failure to match on a ID register should not result in a failure to probe. The reason for this is Device tree fallback compatibles. Those allow for a future device that is compatible (it may have a superset of features) with this part to use a dt-binding that includes the compatible for this one despite having a different ID register value. As such this should print an information message to say it's an unknown device ID but return success. > + > + return 0; > +} > + > +static int bmi270_write_calibration_data(struct bmi270_data *bmi270_device) > +{ > + int ret; > + int status = 0; > + const struct firmware *init_data; > + struct device *dev = bmi270_device->dev; > + struct regmap *regmap = bmi270_device->regmap; > + > + ret = regmap_clear_bits(regmap, BMI270_PWR_CONF_REG, BMI270_PWR_CONF_ADV_PWR_SAVE_MSK); > + if (ret) > + return dev_err_probe(dev, ret, "Failed to write power configuration"); > + > + /* After disabling advanced power save, all registers are accessible after a 450us delay All IIO multiline comments are /* * After... > + * This delay is specified in table A of the datasheet. > + */ > + usleep_range(450, 1000); > + > + ret = regmap_clear_bits(regmap, BMI270_INIT_CTRL_REG, BMI270_INIT_CTRL_LOAD_DONE_MSK); > + if (ret) > + return dev_err_probe(dev, ret, "Failed to prepare device to load init data"); > + > + ret = request_firmware(&init_data, BMI270_INIT_DATA_FILE, dev); > + if (ret) > + return dev_err_probe(dev, ret, "Failed to load init data file"); > + > + ret = regmap_bulk_write(regmap, BMI270_INIT_DATA_REG, > + init_data->data, init_data->size); > + release_firmware(init_data); > + if (ret) > + return dev_err_probe(dev, ret, "Failed to write init data"); > + > + ret = regmap_set_bits(regmap, BMI270_INIT_CTRL_REG, BMI270_INIT_CTRL_LOAD_DONE_MSK); > + if (ret) > + return dev_err_probe(dev, ret, "Failed to stop device initialization"); > + > + /* Wait at least 140ms for the device to complete configuration. > + * This delay is specified in table C of the datasheet. > + */ > + usleep_range(140000, 160000); > + > + ret = regmap_read(regmap, BMI270_INTERNAL_STATUS_REG, &status); > + if (ret) > + return dev_err_probe(dev, ret, "Failed to read internal status"); > + > + if (status != BMI270_INTERNAL_STATUS_MSG_INIT_OK) > + return dev_err_probe(dev, -ENODEV, "Device failed to initialize"); > + > + return 0; > +}