Re: [PATCH 3/3] iio: imu: Add scale and sampling frequency to BMI270 IMU
From: Justin Weiss
Date: Sat Oct 12 2024 - 22:45:32 EST
Jonathan Cameron <jic23@xxxxxxxxxx> writes:
> On Fri, 11 Oct 2024 08:37:49 -0700
> Justin Weiss <justin@xxxxxxxxxxxxxxx> wrote:
>
>> Add read and write functions and create _available entries. Use
>> IIO_CHAN_INFO_SAMP_FREQ instead of IIO_CHAN_INFO_FREQUENCY to match
>> the BMI160 / BMI323 drivers.
>
> Ah. Please break dropping _FREQUENCY change out as a separate fix
> with fixes tag etc and drag it to start of the patch. It was never
> wired to anything anyway
>
> That's a straight forward ABI bug so we want that to land ahead
> of the rest of the series.
Thanks, I'll pull that into its own change and make it the first patch.
> Does this device have a data ready interrupt and if so what affect
> do the different ODRs for each type of sensor have on that?
> If there are separate data ready signals, you probably want to
> go with a dual buffer setup from the start as it is hard to unwind
> that later.
It has data ready interrupts for both accelerometer and gyroscope and a
FIFO interrupt. I had held off on interrupts to keep this change
simpler, but if it's a better idea to get it in earlier, I can add it
alongside the triggered buffer change.
>
>>
>> Signed-off-by: Justin Weiss <justin@xxxxxxxxxxxxxxx>
>> ---
>> drivers/iio/imu/bmi270/bmi270_core.c | 293 ++++++++++++++++++++++++++-
>> 1 file changed, 291 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/iio/imu/bmi270/bmi270_core.c b/drivers/iio/imu/bmi270/bmi270_core.c
>> index f49db5d1bffd..ce7873dc3211 100644
>> --- a/drivers/iio/imu/bmi270/bmi270_core.c
>> +++ b/drivers/iio/imu/bmi270/bmi270_core.c
>> @@ -7,6 +7,7 @@
>> #include <linux/regmap.h>
>>
>> #include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> #include <linux/iio/triggered_buffer.h>
>> #include <linux/iio/trigger_consumer.h>
>>
>> @@ -34,6 +35,9 @@
>> #define BMI270_ACC_CONF_BWP_NORMAL_MODE 0x02
>> #define BMI270_ACC_CONF_FILTER_PERF_MSK BIT(7)
>>
>> +#define BMI270_ACC_CONF_RANGE_REG 0x41
>> +#define BMI270_ACC_CONF_RANGE_MSK GENMASK(1, 0)
>> +
>> #define BMI270_GYR_CONF_REG 0x42
>> #define BMI270_GYR_CONF_ODR_MSK GENMASK(3, 0)
>> #define BMI270_GYR_CONF_ODR_200HZ 0x09
>> @@ -42,6 +46,9 @@
>> #define BMI270_GYR_CONF_NOISE_PERF_MSK BIT(6)
>> #define BMI270_GYR_CONF_FILTER_PERF_MSK BIT(7)
>>
>> +#define BMI270_GYR_CONF_RANGE_REG 0x43
>> +#define BMI270_GYR_CONF_RANGE_MSK GENMASK(2, 0)
>> +
>> #define BMI270_INIT_CTRL_REG 0x59
>> #define BMI270_INIT_CTRL_LOAD_DONE_MSK BIT(0)
>>
>> @@ -85,6 +92,236 @@ const struct bmi270_chip_info bmi270_chip_info[] = {
>> };
>> EXPORT_SYMBOL_NS_GPL(bmi270_chip_info, IIO_BMI270);
>>
>> +enum bmi270_sensor_type {
>> + BMI270_ACCEL = 0,
>> + BMI270_GYRO,
>> +};
>> +
>> +struct bmi270_scale {
>> + u8 bits;
>> + int uscale;
>> +};
>> +
>> +struct bmi270_odr {
>> + u8 bits;
>> + int odr;
>> + int uodr;
>> +};
>> +
>> +static const struct bmi270_scale bmi270_accel_scale[] = {
>> + { 0x00, 598},
> { 0x00, 598 },
>
> So space before } for all these.
Will fix in v2.
>> + { 0x01, 1197},
>> + { 0x02, 2394},
>> + { 0x03, 4788},
>> +};
>> +
>> +static const struct bmi270_scale bmi270_gyro_scale[] = {
>> + { 0x00, 1065},
>> + { 0x01, 532},
>> + { 0x02, 266},
>> + { 0x03, 133},
>> + { 0x04, 66},
>> +};
>> +
>> +struct bmi270_scale_item {
>> + const struct bmi270_scale *tbl;
>> + int num;
>> +};
>> +
>> +static const struct bmi270_scale_item bmi270_scale_table[] = {
>> + [BMI270_ACCEL] = {
>> + .tbl = bmi270_accel_scale,
>> + .num = ARRAY_SIZE(bmi270_accel_scale),
>> + },
>> + [BMI270_GYRO] = {
>> + .tbl = bmi270_gyro_scale,
>> + .num = ARRAY_SIZE(bmi270_gyro_scale),
>> + },
>> +};
>> +
>> +static const struct bmi270_odr bmi270_accel_odr[] = {
>> + {0x01, 0, 781250},
>> + {0x02, 1, 562500},
>> + {0x03, 3, 125000},
>> + {0x04, 6, 250000},
>> + {0x05, 12, 500000},
>> + {0x06, 25, 0},
>> + {0x07, 50, 0},
>> + {0x08, 100, 0},
>> + {0x09, 200, 0},
>> + {0x0A, 400, 0},
>> + {0x0B, 800, 0},
>> + {0x0C, 1600, 0},
>> +};
>> +
>> +static const struct bmi270_odr bmi270_gyro_odr[] = {
>> + {0x06, 25, 0},
>> + {0x07, 50, 0},
>> + {0x08, 100, 0},
>> + {0x09, 200, 0},
>> + {0x0A, 400, 0},
>> + {0x0B, 800, 0},
>> + {0x0C, 1600, 0},
>> + {0x0D, 3200, 0},
>> +};
>> +
>> +struct bmi270_odr_item {
>> + const struct bmi270_odr *tbl;
>> + int num;
>> +};
>> +
>> +static const struct bmi270_odr_item bmi270_odr_table[] = {
>> + [BMI270_ACCEL] = {
>> + .tbl = bmi270_accel_odr,
>> + .num = ARRAY_SIZE(bmi270_accel_odr),
>> + },
>> + [BMI270_GYRO] = {
>> + .tbl = bmi270_gyro_odr,
>> + .num = ARRAY_SIZE(bmi270_gyro_odr),
>> + },
>> +};
>> +
>> +static int bmi270_set_scale(struct bmi270_data *data,
>> + int chan_type, int uscale)
>> +{
>> + int i;
>> + int reg;
>> + struct bmi270_scale_item bmi270_scale_item;
>> +
>> + switch (chan_type) {
>> + case IIO_ACCEL:
>> + reg = BMI270_ACC_CONF_RANGE_REG;
>> + bmi270_scale_item = bmi270_scale_table[BMI270_ACCEL];
>> + break;
>> + case IIO_ANGL_VEL:
>> + reg = BMI270_GYR_CONF_RANGE_REG;
>> + bmi270_scale_item = bmi270_scale_table[BMI270_GYRO];
>> + break;
>> + default:
>> + return -EINVAL;
>> + }
>> +
>> + for (i = 0; i < bmi270_scale_item.num; i++)
>> + if (bmi270_scale_item.tbl[i].uscale == uscale)
>> + break;
>> +
>> + if (i == bmi270_scale_item.num)
>> + return -EINVAL;
>> +
>> + return regmap_write(data->regmap, reg,
>> + bmi270_scale_item.tbl[i].bits);
> Maybe do the write in the if above, (or use the continue approach mentioned
> below + do the write in the for loop.
> Then any exit from the loop that isn't a return is a failure and you an save the check.
Thanks for this suggestion, I'll change all of these loops to use continue / return.
>> +}
>> +
>> +static int bmi270_get_scale(struct bmi270_data *bmi270_device,
>> + int chan_type, int *uscale)
>> +{
>> + int i, ret, val;
>> + int reg;
>> + struct bmi270_scale_item bmi270_scale_item;
>> +
>> + switch (chan_type) {
>> + case IIO_ACCEL:
>> + reg = BMI270_ACC_CONF_RANGE_REG;
>> + bmi270_scale_item = bmi270_scale_table[BMI270_ACCEL];
>> + break;
>> + case IIO_ANGL_VEL:
>> + reg = BMI270_GYR_CONF_RANGE_REG;
>> + bmi270_scale_item = bmi270_scale_table[BMI270_GYRO];
>> + break;
>> + default:
>> + return -EINVAL;
>> + }
>> +
>> + ret = regmap_read(bmi270_device->regmap, reg, &val);
>> + if (ret)
>> + return ret;
>
> No masking?
Looks like I missed this. I'll fix it in v2.
>
>> +
>> + for (i = 0; i < bmi270_scale_item.num; i++)
>> + if (bmi270_scale_item.tbl[i].bits == val) {
> Flip the condition to reduce indent
>
> if (bmi270_scale_item.tbl[i].bits != val)
> continue;
>
> *uscale.
>
>> + *uscale = bmi270_scale_item.tbl[i].uscale;
>> + return 0;
>> + }
>> +
>> + return -EINVAL;
>> +}
>> +
>> +static int bmi270_set_odr(struct bmi270_data *data, int chan_type,
>> + int odr, int uodr)
>> +{
>> + int i;
>> + int reg, mask;
>> + struct bmi270_odr_item bmi270_odr_item;
>> +
>> + switch (chan_type) {
>> + case IIO_ACCEL:
>> + reg = BMI270_ACC_CONF_REG;
>> + mask = BMI270_ACC_CONF_ODR_MSK;
>> + bmi270_odr_item = bmi270_odr_table[BMI270_ACCEL];
>> + break;
>> + case IIO_ANGL_VEL:
>> + reg = BMI270_GYR_CONF_REG;
>> + mask = BMI270_GYR_CONF_ODR_MSK;
>> + bmi270_odr_item = bmi270_odr_table[BMI270_GYRO];
>> + break;
>> + default:
>> + return -EINVAL;
>> + }
>> +
>> + for (i = 0; i < bmi270_odr_item.num; i++)
>> + if (bmi270_odr_item.tbl[i].odr == odr &&
>> + bmi270_odr_item.tbl[i].uodr == uodr)
>> + break;
>> +
>> + if (i >= bmi270_odr_item.num)
>> + return -EINVAL;
>> +
>> + return regmap_update_bits(data->regmap,
>> + reg,
>> + mask,
>> + bmi270_odr_item.tbl[i].bits);
>
> combine parameters on each line to get nearer to 80 char limit.
Will fix in v2.
>
>> +}
>> +
>> +static int bmi270_get_odr(struct bmi270_data *data, int chan_type,
>> + int *odr, int *uodr)
>> +{
>> + int i, val, ret;
>> + int reg, mask;
>> + struct bmi270_odr_item bmi270_odr_item;
>> +
>> + switch (chan_type) {
>> + case IIO_ACCEL:
>> + reg = BMI270_ACC_CONF_REG;
>> + mask = BMI270_ACC_CONF_ODR_MSK;
>> + bmi270_odr_item = bmi270_odr_table[BMI270_ACCEL];
>> + break;
>> + case IIO_ANGL_VEL:
>> + reg = BMI270_GYR_CONF_REG;
>> + mask = BMI270_GYR_CONF_ODR_MSK;
>> + bmi270_odr_item = bmi270_odr_table[BMI270_GYRO];
>> + break;
>> + default:
>> + return -EINVAL;
>> + }
>> +
>> + ret = regmap_read(data->regmap, reg, &val);
>> + if (ret)
>> + return ret;
>> +
>> + val &= mask;
>
> I'd just duplicate the regmap_read to allow easy use FIELD_GET etc.
>
>
> switch (chan_type) {
> case IIO_ACCEL:
> ret = regmap_read(data->regmap, BMI270_ACC_CONF_REG, &val);
> if (ret)
> return ret;
> val = FIELD_GET(val, BMI270_ACC_CONF_ODR_MSK);
> bmi270_odr_item = bmi270_odr_table[BMI270_ACCEL];
> break;
> case IIO_ANGL_VEL:
> ret = regmap_read(data->regmap, BMI270_GYR_CONF_REG, &val);
> if (ret)
> return ret;
> val = FIELD_GET(val, BMI270_GYR_CONF_ODR_MSK);
> bmi270_odr_item = bmi270_odr_table[BMI270_GYRO];
> break;
> default:
> return -EINVAL;
> }
Thanks, that's nicer. I'll fix it in v2.
>> +
>> + for (i = 0; i < bmi270_odr_item.num; i++)
>> + if (val == bmi270_odr_item.tbl[i].bits)
>> + break;
>> +
>> + if (i >= bmi270_odr_item.num)
>> + return -EINVAL;
>> +
>> + *odr = bmi270_odr_item.tbl[i].odr;
>> + *uodr = bmi270_odr_item.tbl[i].uodr;
>> +
>> + return 0;
>> +}
>> +
>> +static
>> +IIO_CONST_ATTR(in_accel_sampling_frequency_available,
>> + "0.78125 1.5625 3.125 6.25 12.5 25 50 100 200 400 800 1600");
>> +static
>> +IIO_CONST_ATTR(in_anglvel_sampling_frequency_available,
>> + "25 50 100 200 400 800 1600 3200");
>> +static
>> +IIO_CONST_ATTR(in_accel_scale_available,
>> + "0.000598 0.001197 0.002394 0.004788");
>> +static
>> +IIO_CONST_ATTR(in_anglvel_scale_available,
>> + "0.001065 0.000532 0.000266 0.000133 0.000066");
>> +
>> +static struct attribute *bmi270_attrs[] = {
>> + &iio_const_attr_in_accel_sampling_frequency_available.dev_attr.attr,
>> + &iio_const_attr_in_anglvel_sampling_frequency_available.dev_attr.attr,
>> + &iio_const_attr_in_accel_scale_available.dev_attr.attr,
>> + &iio_const_attr_in_anglvel_scale_available.dev_attr.attr,
> Please use the read_avail callback and info_mask_xxx_avail masks to specify
> these exist for all the channels.
>
> Doing this as custom attrs predates that infrastructure and we are
> slowly converting drivers over. The main advantage beyond enforced
> ABI is that can get to the values from in kernel consumers of the data.
>
Great, I'll remove these and implement read_avail.
>> + NULL,
> No comma here.
Will fix in v2.
>> +};
>> +
>> +static const struct attribute_group bmi270_attrs_group = {
>> + .attrs = bmi270_attrs,
>> +};