[PATCH 0/4] Add support for Bosch BNO055 IMU

From: Andrea Merello
Date: Thu Jul 15 2021 - 10:17:53 EST


This series (tries to) add support for Bosch BNO055 IMU to Linux IIO
subsystem. It is made up four patches:

1/4: adds some IIO modifiers to the IIO core layer, in order to being
able to expose the linear acceleration among standard attributes.

2/4: adds the core IIO BNO055 driver

3/4: adds DT bindings for the serdev BNO055 driver - Note: I've put
as "maintainer" the one that ./scripts/get_maintainer.pl suggested
to me, but I wasn't sure about this.

4/4: adds serdev BNO055 driver to actually use the IMU via serial line

Previously at least another driver for the very same chip has been
posted to the Linux ML [0], but it has been never merged, and it seems
no one cared of it since quite a long time.

This driver differs from the above driver on the following aspects:

- This driver supports serial access, instead of I2C access (however it
is designed in a modular way that should make it easy to eventually add
I2C support later on).

- The above driver tried to support all IMU HW modes by allowing to choose
one in the DT, and adapting IIO attributes accordingly. This driver does
not rely on DT for this, instead an attribute "operation_mode" is
exposed. All IIO attributes used in all modes are exposed; more on this
later on. This driver BTW supports only a subset of the HW-supported
modes.

- This driver has some support for managing the IMU calibration

This driver supports three operation modes:

- AMG (accelerometer, magnetometer and gyroscope) mode, which provides
raw measurements from the said sensors, and allows for setting some
parameters about them (e.g. filter cut-off frequency, max sensor ranges,
etc).

- Two flavors of fusion mode, which still provide AMG measures, while
they also provide other data calculated by the IMU (e.g. rotation
angles, linear acceleration, etc). In this mode user has no freedom to
set any sensor parameter, since the HW locks them.

IIO attributes exposing sensors parameters are always present, but in
fusion modes the available values are constrained to just the one used
by the HW. This is reflected in the '*_available' IIO attributes.

Trying to set a not-supported value always falls back to the closest
supported one, which in this case is just the one in use by the HW.

IIO attributes for unavailable measurements (e.g. Euler angles in AMG
mode) just read zero (which is consistent WRT what you get when reading
from a buffer with those attributes enabled).
Another option could be make them return -EINVAL or to make those
attributes reading as "N/A" or something like that, but in those cases I
wouldn't know what to do with buffers..

About the IMU calibration:

The IMU supports for two sets of calibration parameters:
- SIC matrix, which has to be provided by the user, and that this driver
doesn't currently care about of (yet)
- offset and radius parameters, which the IMU can automatically find
out, and for which this driver has support

The driver provides access to autocalibration flags (i.e. you can known if
the IMU has successfully autocalibrated) and to calibration data blob.
The user can save this blob in a "firmware" file (i.e. in /lib/firmware)
that the driver looks for at probe time. If found, then the IMU is
initialized with this calibration data. This saves the user from performing
the calibration procedure every time (which consist of moving the IMU in
various way).

The driver looks for calibration data file using two different names:
first a file whose name is suffixed with the IMU unique ID is searched
for; this is useful when there is more than one IMU instance. If this
file is not found, then a "generic" calibration file is searched for
(which can be used when only one IMU is present, without struggling with
fancy names, that change on each device).

In fusion modes the HW applies calibration data to all measurements, while
in AMG mode the calibration data is not used by the HW.

The IIO 'offset' attributes provide access to the offsets from calibration
data, so that the user can apply them to the accel, angvel and magn IIO
attributes.

There is an hack here: since the HW does already apply the offsets when in
fusion modes, then we report 'zero' for all offsets when we are in fusion
modes. In this way offsets can be always applied by the user and this leads
to correct results in all operation modes.

Alternatively we could try to un-correct the measures when in fusion mode,
and let the offset attributes to report actual calibration offset, but this
seems awkward to me, and I can see some problems about races WRT HW
autocalibration (which always run in background anyway).

The last alternative could be to expose two sets of IIO attributes for
accel, anglvel and magn measures, which would refer to the same registers
indeed; one attribute set has associated offsets, while the other hasn't.
Then we make only one set "readable", depending by the current operation
mode. Unfortunately they would all still be "raw" anyway, since they still
need to get the 'scale' attribute applied. Furthermore this may complicate
things with buffer and bursts handling.

I've implemented the 1st solution because IMHO it's simpler and still
reasonably coherent.

About access protocol and serdev module:

The serial protocol is quite simple, but there are tricks to make it
really works. Those tricks and workarounds are documented in the driver
source file.

The core BNO055 driver tries to group readings in burst when appropriate,
in order to optimize triggered buffer operation. The threshold for
splitting a burst (i.e. max number of unused bytes in the middle of a
burst that will be throw away) is provided to the core driver by the
lowlevel access driver (which is the bno055_sl serdev driver indeed) at
probe time.

If an I2C access driver will be written, then it can provide its own
threshold value (which depends by the protocol overhead, etc..) to the
core bno055 driver.

Finally, I wrote and tested this driver on a Zynq-based custom board that
runs a kernel v5.4 and I've rebased on latest mainline tree on which I've
just compile-tested.

[0] https://www.spinics.net/lists/linux-iio/msg25508.html

Andrea Merello (4):
iio: add modifiers for linear acceleration
iio: imu: add Bosch Sensortec BNO055 core driver
dt-bindings: iio: imu: add bosch BNO055 serdev driver bindings
iio: imu: add BNO055 serdev driver

.../bindings/iio/imu/bosch,bno055-serial.yaml | 40 +
drivers/iio/imu/Kconfig | 1 +
drivers/iio/imu/Makefile | 1 +
drivers/iio/imu/bno055/Kconfig | 12 +
drivers/iio/imu/bno055/Makefile | 7 +
drivers/iio/imu/bno055/bno055.c | 1361 +++++++++++++++++
drivers/iio/imu/bno055/bno055.h | 12 +
drivers/iio/imu/bno055/bno055_sl.c | 576 +++++++
drivers/iio/industrialio-core.c | 3 +
include/uapi/linux/iio/types.h | 4 +-
10 files changed, 2016 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/iio/imu/bosch,bno055-serial.yaml
create mode 100644 drivers/iio/imu/bno055/Kconfig
create mode 100644 drivers/iio/imu/bno055/Makefile
create mode 100644 drivers/iio/imu/bno055/bno055.c
create mode 100644 drivers/iio/imu/bno055/bno055.h
create mode 100644 drivers/iio/imu/bno055/bno055_sl.c

--
2.17.1