Skip to contentSkip to conversation

Add support for Bosch BNO055 IMU

PATCH Superseded Andrea Merello 8 participants 36 messages Feb 17, 2022 – Apr 4, 2022

Subsystems (3)

v3 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

v3 cover Add support for Bosch BNO055 IMU

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

1/13 to 6/13: add some IIO modifiers, and their documentation, to the IIO
core layer, in order to being able to expose the linear acceleration and Euler angles among standard attributes. Also update the IIO event monitor tool

6/13 to 10/13: add the core IIO BNO055 driver documentation for sysfs
attributes and DT bindings

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

12/13: adds I2C BNO055 driver to actually use the IMU via I2C wiring

13/13: add a documentation file that describe the bno055 driver and
specifically the calibration

Differences wrt v2:

FIXES:
- workaround: switching off and on fusion mode could cause IMU to misbehave (i.e. wrong anglvel data)
- FIX sometimes driver failed to write attributes / manual settings were not honored
- workaround: for IMU gyroscope unit bug (unit change by changing range)
- Fixed some typos bosh->bosch
- Fixed warning caught by kernel robot
- Fix bursting was broken
- Fix missing mutex unlock in exit path

API:
- rework sysfs attribute names
- change linear acceleration modifiers names (e.g. ACCEL_LINEAR_MOD -> ACCEL_MOD)
- 'unique_id' sysfs attribute become 'serial_number' and become

    non-bno055-specific

- fix range units and sysfs names
- fix gyro lpf available values were returned in twisted order
- gyroscope no longer has the range attribute, the scale can now be changed instead

Misc:
- Dropped previous patch 01 (add find_closest_unsorted) as it is not needed anymore
- Some trivial stuff (commas, newlines etc)
- Added help section in Kconfig
- reworked sysfs attribute code, to easier implement required API changes and above fixes
- hopefully implement all reviewers advice (e.g. rid of macro helpers, kill useless devm primitive, simplify bno055_clk_disable, rework some bail-out code, conditions check, remove odd usage of ternary operator, move default clause at end of switch block, etc ..)
- reorder patches so that DOC for a new thing come after patch that introduced that thing
- linear acceleration DOC moved and cross-referenced to acceleration doc

Differences wrt other BNO055 drivers:

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 also serial access (to be reliable, reset pin is required to be wired)

- 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 settings are done via sysfs attributes. All IIO attributes are always exposed; more on this later on. This driver however supports only a subset of the HW-supported modes.

- This driver has some support for managing the IMU calibration

Supported operation modes:

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

- Fusion mode, which still provides AMG measures, while it also provides 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. Autocalibration and correction is performed by the IMU.

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).

IMU calibration:

The IMU supports for two sets of calibration parameters:

- SIC matrix. user-provided; this driver doesn't currently support it

- Offset and radius parameters. The IMU automatically finds out them when it is running in fusion mode; supported by this driver.

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 changes on each device).

In AMG mode the IIO 'offset' attributes provide access to the offsets from calibration data (if any), so that the user can apply them to the accel, angvel and magn IIO attributes. In fusion mode they are not needed and read as zero.

Access protocols 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 (either serdev or I2C) at probe time.

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

Andrea Merello (13):
iio: add modifiers for linear acceleration
iio: document linear acceleration modifiers
iio: event_monitor: add linear acceleration modifiers
iio: add modifers for pitch, yaw, roll
iio: document pitch, yaw, roll modifiers
iio: event_monitor: add pitch, yaw and roll modifiers
iio: imu: add Bosch Sensortec BNO055 core driver
iio: document bno055 private sysfs attributes
iio: document "serial_number" sysfs attribute
dt-bindings: iio: imu: add documentation for Bosch BNO055 bindings
iio: imu: add BNO055 serdev driver
iio: imu: add BNO055 I2C driver
docs: iio: add documentation for BNO055 driver

 Documentation/ABI/testing/sysfs-bus-iio       |   25 +
 .../ABI/testing/sysfs-bus-iio-bno055          |   81 +
 .../bindings/iio/imu/bosch,bno055.yaml        |   59 +
 Documentation/iio/bno055.rst                  |   53 +
 drivers/iio/imu/Kconfig                       |    1 +
 drivers/iio/imu/Makefile                      |    1 +
 drivers/iio/imu/bno055/Kconfig                |   25 +
 drivers/iio/imu/bno055/Makefile               |    5 +
 drivers/iio/imu/bno055/bno055.c               | 1612 +++++++++++++++++
 drivers/iio/imu/bno055/bno055.h               |   12 +
 drivers/iio/imu/bno055/bno055_i2c.c           |   50 +
 drivers/iio/imu/bno055/bno055_sl.c            |  557 ++++++
 drivers/iio/industrialio-core.c               |    6 +
 include/uapi/linux/iio/types.h                |    7 +-
 tools/iio/iio_event_monitor.c                 |    6 +
 15 files changed, 2499 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-bno055
 create mode 100644 Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml
 create mode 100644 Documentation/iio/bno055.rst
 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_i2c.c
 create mode 100644 drivers/iio/imu/bno055/bno055_sl.c

--
2.17.1

#1 lore raw

PATCH v3 1/13 iio: add modifiers for linear acceleration

This patch is preparatory for adding the Bosh BNO055 IMU driver. The said IMU can report raw accelerations (among x, y and z axis) as well as the so called "linear accelerations" (again, among x, y and z axis) which is basically the acceleration after subtracting gravity.

This patch adds IIO_MOD_LINEAR_X, IIO_MOD_LINEAR_Y and IIO_MOD_LINEAR_Z modifiers to te IIO core.

---
 drivers/iio/industrialio-core.c | 3 +++
 include/uapi/linux/iio/types.h  | 4 +++-
 2 files changed, 6 insertions(+), 1 deletion(-)
drivers/iio/industrialio-core.c +3 -0
index e1ed44dec2ab..252dbe3444cc 100644
@@ -134,6 +134,9 @@ static const char * const iio_modifier_names[] = {
 	[IIO_MOD_ETHANOL] = "ethanol",
 	[IIO_MOD_H2] = "h2",
 	[IIO_MOD_O2] = "o2",
+	[IIO_MOD_LINEAR_X] = "linear_x",
+	[IIO_MOD_LINEAR_Y] = "linear_y",
+	[IIO_MOD_LINEAR_Z] = "linear_z",
 };
 
 /* relies on pairs of these shared then separate */
include/uapi/linux/iio/types.h +3 -1
index 48c13147c0a8..387a1b5cd1a9 100644
@@ -95,6 +95,9 @@ enum iio_modifier {
 	IIO_MOD_ETHANOL,
 	IIO_MOD_H2,
 	IIO_MOD_O2,
+	IIO_MOD_LINEAR_X,
+	IIO_MOD_LINEAR_Y,
+	IIO_MOD_LINEAR_Z,
 };
 
 enum iio_event_type {
@@ -114,4 +117,3 @@ enum iio_event_direction {
 };
 
 #endif /* _UAPI_IIO_TYPES_H_ */
-
Signature · 1 line
2.17.1
#2 lore raw

PATCH v3 2/13 iio: document linear acceleration modifiers

In reply to Andrea Merello

This patch introduces ABI documentation for new iio modifiers used for reporting "linear acceleration" measures.

---
 Documentation/ABI/testing/sysfs-bus-iio | 9 +++++++++
 1 file changed, 9 insertions(+)
Documentation/ABI/testing/sysfs-bus-iio +9 -0
index c551301b33f1..b2fb4d9abcd1 100644
@@ -233,6 +233,15 @@ Description:
 		Has all of the equivalent parameters as per voltageY. Units
 		after application of scale and offset are m/s^2.
 
+What:		/sys/bus/iio/devices/iio:deviceX/in_accel_linear_x_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_accel_linear_y_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_accel_linear_z_raw
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		As per in_accel_X_raw attributes, but minus the gravity
+		acceleration.
+
 What:		/sys/bus/iio/devices/iio:deviceX/in_gravity_x_raw
 What:		/sys/bus/iio/devices/iio:deviceX/in_gravity_y_raw
 What:		/sys/bus/iio/devices/iio:deviceX/in_gravity_z_raw
Signature · 1 line
2.17.1
maintainer
#3 lore raw

Re: [v3 02/13] iio: document linear acceleration modifiers

Quoted 14 lines · On Thu, 17 Feb 2022 17:26:59 +0100 Andrea Merello <[email protected]>…

This patch introduces ABI documentation for new iio modifiers used for reporting "linear acceleration" measures.

---
 Documentation/ABI/testing/sysfs-bus-iio | 9 +++++++++
 1 file changed, 9 insertions(+)
Documentation/ABI/testing/sysfs-bus-iio +0 -0
index c551301b33f1..b2fb4d9abcd1 100644
@@ -233,6 +233,15 @@ Description:
 		Has all of the equivalent parameters as per voltageY. Units

Documentation/ABI/testing/sysfs-bus-iio Description: · line 243

Quoted 10 lines
Documentation/ABI/testing/sysfs-bus-iio +8 -0
 		after application of scale and offset are m/s^2.
 
+What:		/sys/bus/iio/devices/iio:deviceX/in_accel_linear_x_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_accel_linear_y_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_accel_linear_z_raw
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		As per in_accel_X_raw attributes, but minus the gravity
+		acceleration.

As it looks like you are going to be doing a v4 given the stuff 0-day found I'd prefer this as "minus the acceleration due to gravity" which from what I recall tends to be what it is called in physics text books etc.

Quoted 4 lines
Documentation/ABI/testing/sysfs-bus-iio +1 -0
+
 What:		/sys/bus/iio/devices/iio:deviceX/in_gravity_x_raw
 What:		/sys/bus/iio/devices/iio:deviceX/in_gravity_y_raw
 What:		/sys/bus/iio/devices/iio:deviceX/in_gravity_z_raw
#4 lore raw

PATCH v3 3/13 iio: event_monitor: add linear acceleration modifiers

In reply to Andrea Merello
---
 tools/iio/iio_event_monitor.c | 3 +++
 1 file changed, 3 insertions(+)
tools/iio/iio_event_monitor.c +3 -0
index b94a16ba5c6c..84c90b8fdd91 100644
@@ -121,6 +121,9 @@ static const char * const iio_modifier_names[] = {
 	[IIO_MOD_PM4] = "pm4",
 	[IIO_MOD_PM10] = "pm10",
 	[IIO_MOD_O2] = "o2",
+	[IIO_MOD_LINEAR_X] = "linear_x",
+	[IIO_MOD_LINEAR_Y] = "linear_y",
+	[IIO_MOD_LINEAR_Z] = "linear_z",
 };
 
 static bool event_is_known(struct iio_event_data *event)
Signature · 1 line
2.17.1
#5 lore raw

PATCH v3 4/13 iio: add modifers for pitch, yaw, roll

In reply to Andrea Merello

This patch adds modifiers for reporting rotations as euler angles (i.e. yaw, pitch and roll).

---
 drivers/iio/industrialio-core.c | 3 +++
 include/uapi/linux/iio/types.h  | 3 +++
 2 files changed, 6 insertions(+)
drivers/iio/industrialio-core.c +3 -0
index 252dbe3444cc..8a5ba32d3963 100644
@@ -137,6 +137,9 @@ static const char * const iio_modifier_names[] = {
 	[IIO_MOD_LINEAR_X] = "linear_x",
 	[IIO_MOD_LINEAR_Y] = "linear_y",
 	[IIO_MOD_LINEAR_Z] = "linear_z",
+	[IIO_MOD_PITCH] = "pitch",
+	[IIO_MOD_YAW] = "yaw",
+	[IIO_MOD_ROLL] = "roll",
 };
 
 /* relies on pairs of these shared then separate */
include/uapi/linux/iio/types.h +3 -0
index 387a1b5cd1a9..4a7481cdaf7a 100644
@@ -98,6 +98,9 @@ enum iio_modifier {
 	IIO_MOD_LINEAR_X,
 	IIO_MOD_LINEAR_Y,
 	IIO_MOD_LINEAR_Z,
+	IIO_MOD_PITCH,
+	IIO_MOD_YAW,
+	IIO_MOD_ROLL,
 };
 
 enum iio_event_type {
Signature · 1 line
2.17.1
#6 lore raw

PATCH v3 5/13 iio: document pitch, yaw, roll modifiers

In reply to Andrea Merello

This patch introduces ABI documentation for new modifiers used for reporting rotations expressed as euler angles (i.e. yaw, pitch, roll).

---
 Documentation/ABI/testing/sysfs-bus-iio | 9 +++++++++
 1 file changed, 9 insertions(+)
Documentation/ABI/testing/sysfs-bus-iio +9 -0
index b2fb4d9abcd1..1b8d77577608 100644
@@ -2008,3 +2008,12 @@ Description:
 		Available range for the forced calibration value, expressed as:
 
 		- a range specified as "[min step max]"
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_rot_yaw_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_rot_pitch_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_rot_roll_raw
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		Raw (unscaled) euler angles readings. Units after
+		application of scale are deg.
Signature · 1 line
2.17.1
maintainer
#7 lore raw

Re: [v3 05/13] iio: document pitch, yaw, roll modifiers

Quoted 4 lines · On Thu, 17 Feb 2022 17:27:02 +0100 Andrea Merello <[email protected]>…

This patch introduces ABI documentation for new modifiers used for reporting rotations expressed as euler angles (i.e. yaw, pitch, roll).

I've lost track of what we already discussed about this, but why have these as measured in degrees?

Gah. I thought we'd maintained consistency on this, but seems incli is in degrees, angl is in radians and rot isn't documented at all.

Checking drivers the adis16209 has rot in degrees and I can't immediately figure out what the hid sensors driver is using. The underlying spec supports the hardware returning in either radians or degrees I think.

Ah well, at least these are new so given the existing mess means at least some units are already in degrees we aren't making things worse.

Perhaps it's worth a comment on the unit inconsistency in this patch description so we can at least look back at the history if it comes up for a future driver.

Thanks,

Jonathan

Quoted 21 lines
---
 Documentation/ABI/testing/sysfs-bus-iio | 9 +++++++++
 1 file changed, 9 insertions(+)
Documentation/ABI/testing/sysfs-bus-iio +9 -0
index b2fb4d9abcd1..1b8d77577608 100644
@@ -2008,3 +2008,12 @@ Description:
 		Available range for the forced calibration value, expressed as:
 
 		- a range specified as "[min step max]"
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_rot_yaw_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_rot_pitch_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_rot_roll_raw
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		Raw (unscaled) euler angles readings. Units after
+		application of scale are deg.
#8 lore raw

PATCH v3 6/13 iio: event_monitor: add pitch, yaw and roll modifiers

In reply to Andrea Merello
---
 tools/iio/iio_event_monitor.c | 3 +++
 1 file changed, 3 insertions(+)
tools/iio/iio_event_monitor.c +3 -0
index 84c90b8fdd91..436ac2f6b907 100644
@@ -124,6 +124,9 @@ static const char * const iio_modifier_names[] = {
 	[IIO_MOD_LINEAR_X] = "linear_x",
 	[IIO_MOD_LINEAR_Y] = "linear_y",
 	[IIO_MOD_LINEAR_Z] = "linear_z",
+	[IIO_MOD_PITCH] = "pitch",
+	[IIO_MOD_YAW] = "yaw",
+	[IIO_MOD_ROLL] = "roll",
 };
 
 static bool event_is_known(struct iio_event_data *event)
Signature · 1 line
2.17.1
#9 lore raw

PATCH v3 7/13 iio: imu: add Bosch Sensortec BNO055 core driver

In reply to Andrea Merello

This patch adds a core driver for the BNO055 IMU from Bosch. This IMU can be connected via both serial and I2C busses; separate patches will add support for them.

The driver supports "AMG" (Accelerometer, Magnetometer, Gyroscope) mode, that provides raw data from the said internal sensors, and a couple of "fusion" modes (i.e. the IMU also do calculations in order to provide euler angles, quaternions, linear acceleration and gravity measurements).

In fusion modes the AMG data is still available (with some calibration refinements done by the IMU), but certain settings such as low pass filters cut-off frequency and sensors ranges are fixed, while in AMG mode they can be customized; this is why AMG mode can still be interesting.

---
 drivers/iio/imu/Kconfig         |    1 +
 drivers/iio/imu/Makefile        |    1 +
 drivers/iio/imu/bno055/Kconfig  |    4 +
 drivers/iio/imu/bno055/Makefile |    3 +
 drivers/iio/imu/bno055/bno055.c | 1612 +++++++++++++++++++++++++++++++
 drivers/iio/imu/bno055/bno055.h |   12 +
 6 files changed, 1633 insertions(+)
 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
drivers/iio/imu/Kconfig +1 -0
index 001ca2c3ff95..f1d7d4b5e222 100644
@@ -52,6 +52,7 @@ config ADIS16480
 	  ADIS16485, ADIS16488 inertial sensors.
 
 source "drivers/iio/imu/bmi160/Kconfig"
+source "drivers/iio/imu/bno055/Kconfig"
 
 config FXOS8700
 	tristate
drivers/iio/imu/Makefile +1 -0
index c82748096c77..6eb612034722 100644
@@ -15,6 +15,7 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o
 obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o
 
 obj-y += bmi160/
+obj-y += bno055/
 
 obj-$(CONFIG_FXOS8700) += fxos8700_core.o
 obj-$(CONFIG_FXOS8700_I2C) += fxos8700_i2c.o
drivers/iio/imu/bno055/Kconfig new file +4 -0
new file mode 100644
index 000000000000..d0ab3221fba5
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config BOSCH_BNO055_IIO
+	tristate
drivers/iio/imu/bno055/Makefile new file +3 -0
new file mode 100644
index 000000000000..56cc4de60a7e
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_BOSCH_BNO055_IIO) += bno055.o
drivers/iio/imu/bno055/bno055.c new file +1612 -0

1,612 lines not on this page · raw message

drivers/iio/imu/bno055/bno055.h new file +12 -0
new file mode 100644
index 000000000000..2ccb373c61cd
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __BNO055_H__
+#define __BNO055_H__
+
+#include <linux/regmap.h>
+
+struct device;
+int bno055_probe(struct device *dev, struct regmap *regmap,
+		 int xfer_burst_break_thr, bool sw_reset);
+extern const struct regmap_config bno055_regmap_config;
+
+#endif
Signature · 1 line
2.17.1
#10 lore raw

Re: [v3 07/13] iio: imu: add Bosch Sensortec BNO055 core driver

On Thu, 17 Feb 2022, Andrea Merello wrote:

nice work, minor comments below

Quoted 171 lines

This patch adds a core driver for the BNO055 IMU from Bosch. This IMU can be connected via both serial and I2C busses; separate patches will add support for them.

The driver supports "AMG" (Accelerometer, Magnetometer, Gyroscope) mode, that provides raw data from the said internal sensors, and a couple of "fusion" modes (i.e. the IMU also do calculations in order to provide euler angles, quaternions, linear acceleration and gravity measurements).

In fusion modes the AMG data is still available (with some calibration refinements done by the IMU), but certain settings such as low pass filters cut-off frequency and sensors ranges are fixed, while in AMG mode they can be customized; this is why AMG mode can still be interesting.

---
 drivers/iio/imu/Kconfig         |    1 +
 drivers/iio/imu/Makefile        |    1 +
 drivers/iio/imu/bno055/Kconfig  |    4 +
 drivers/iio/imu/bno055/Makefile |    3 +
 drivers/iio/imu/bno055/bno055.c | 1612 +++++++++++++++++++++++++++++++
 drivers/iio/imu/bno055/bno055.h |   12 +
 6 files changed, 1633 insertions(+)
 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
drivers/iio/imu/Kconfig +1 -0
index 001ca2c3ff95..f1d7d4b5e222 100644
@@ -52,6 +52,7 @@ config ADIS16480
 	  ADIS16485, ADIS16488 inertial sensors.
 
 source "drivers/iio/imu/bmi160/Kconfig"
+source "drivers/iio/imu/bno055/Kconfig"
 
 config FXOS8700
 	tristate
drivers/iio/imu/Makefile +1 -0
index c82748096c77..6eb612034722 100644
@@ -15,6 +15,7 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o
 obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o
 
 obj-y += bmi160/
+obj-y += bno055/
 
 obj-$(CONFIG_FXOS8700) += fxos8700_core.o
 obj-$(CONFIG_FXOS8700_I2C) += fxos8700_i2c.o
drivers/iio/imu/bno055/Kconfig new file +4 -0
new file mode 100644
index 000000000000..d0ab3221fba5
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config BOSCH_BNO055_IIO
+	tristate
drivers/iio/imu/bno055/Makefile new file +3 -0
new file mode 100644
index 000000000000..56cc4de60a7e
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_BOSCH_BNO055_IIO) += bno055.o
drivers/iio/imu/bno055/bno055.c new file +94 -0
new file mode 100644
index 000000000000..881d08277356
@@ -0,0 +1,1612 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * IIO driver for Bosch BNO055 IMU
+ *
+ * Copyright (C) 2021 Istituto Italiano di Tecnologia
+ * Electronic Design Laboratory
+ * Written by Andrea Merello <[email protected]>
+ *
+ * Portions of this driver are taken from the BNO055 driver patch
+ * from Vlad Dogaru which is Copyright (c) 2016, Intel Corporation.
+ *
+ * This driver is also based on BMI160 driver, which is:
+ *	Copyright (c) 2016, Intel Corporation.
+ *	Copyright (c) 2019, Martin Kelly.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/clk.h>
+#include <linux/bitfield.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/util_macros.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/sysfs.h>
+
+#include "bno055.h"
+
+#define BNO055_FW_NAME "bno055-caldata"
+#define BNO055_FW_EXT ".dat"
+#define BNO055_FW_UID_NAME BNO055_FW_NAME "-%*phN" BNO055_FW_EXT
+#define BNO055_FW_GENERIC_NAME (BNO055_FW_NAME BNO055_FW_EXT)
+
+/* common registers */
+#define BNO055_PAGESEL_REG		0x7
+
+/* page 0 registers */
+#define BNO055_CHIP_ID_REG		0x0
+#define BNO055_CHIP_ID_MAGIC 0xA0
+#define BNO055_SW_REV_LSB_REG		0x4
+#define BNO055_SW_REV_MSB_REG		0x5
+#define BNO055_ACC_DATA_X_LSB_REG	0x8
+#define BNO055_ACC_DATA_Y_LSB_REG	0xA
+#define BNO055_ACC_DATA_Z_LSB_REG	0xC
+#define BNO055_MAG_DATA_X_LSB_REG	0xE
+#define BNO055_MAG_DATA_Y_LSB_REG	0x10
+#define BNO055_MAG_DATA_Z_LSB_REG	0x12
+#define BNO055_GYR_DATA_X_LSB_REG	0x14
+#define BNO055_GYR_DATA_Y_LSB_REG	0x16
+#define BNO055_GYR_DATA_Z_LSB_REG	0x18
+#define BNO055_EUL_DATA_X_LSB_REG	0x1A
+#define BNO055_EUL_DATA_Y_LSB_REG	0x1C
+#define BNO055_EUL_DATA_Z_LSB_REG	0x1E
+#define BNO055_QUAT_DATA_W_LSB_REG	0x20
+#define BNO055_LIA_DATA_X_LSB_REG	0x28
+#define BNO055_LIA_DATA_Y_LSB_REG	0x2A
+#define BNO055_LIA_DATA_Z_LSB_REG	0x2C
+#define BNO055_GRAVITY_DATA_X_LSB_REG	0x2E
+#define BNO055_GRAVITY_DATA_Y_LSB_REG	0x30
+#define BNO055_GRAVITY_DATA_Z_LSB_REG	0x32
+#define BNO055_SCAN_CH_COUNT ((BNO055_GRAVITY_DATA_Z_LSB_REG - BNO055_ACC_DATA_X_LSB_REG) / 2)
+#define BNO055_TEMP_REG			0x34
+#define BNO055_CALIB_STAT_REG		0x35
+#define BNO055_CALIB_STAT_MASK GENMASK(1, 0)
+#define BNO055_CALIB_STAT_MAGN_SHIFT 0
+#define BNO055_CALIB_STAT_ACCEL_SHIFT 2
+#define BNO055_CALIB_STAT_GYRO_SHIFT 4
+#define BNO055_CALIB_STAT_SYS_SHIFT 6
+#define BNO055_SYS_ERR_REG		0x3A
+#define BNO055_POWER_MODE_REG		0x3E
+#define BNO055_POWER_MODE_NORMAL 0
+#define BNO055_SYS_TRIGGER_REG		0x3F
+#define BNO055_SYS_TRIGGER_RST_SYS BIT(5)
+#define BNO055_SYS_TRIGGER_CLK_SEL BIT(7)
+#define BNO055_OPR_MODE_REG		0x3D
+#define BNO055_OPR_MODE_CONFIG 0x0
+#define BNO055_OPR_MODE_AMG 0x7
+#define BNO055_OPR_MODE_FUSION_FMC_OFF 0xB
+#define BNO055_OPR_MODE_FUSION 0xC
+#define BNO055_UNIT_SEL_REG		0x3B
+/* Android orientation mode means: pitch value decreases turning clockwise */
+#define BNO055_UNIT_SEL_ANDROID BIT(7)
+#define BNO055_UNIT_SEL_GYR_RPS BIT(1)
+#define BNO055_CALDATA_START		0x55
+#define BNO055_CALDATA_END		0x6A
+#define BNO055_CALDATA_LEN 22

drivers/iio/imu/bno055/bno055.c line 104

Quoted 10 lines
drivers/iio/imu/bno055/bno055.c new file +10 -0
+
+/*
+ * The difference in address between the register that contains the
+ * value and the register that contains the offset.  This applies for
+ * accel, gyro and magn channels.
+ */
+#define BNO055_REG_OFFSET_ADDR		0x4D
+
+/* page 1 registers */
+#define PG1(x) ((x) | 0x80)

BNO055_ prefix missing

Quoted 41 lines
drivers/iio/imu/bno055/bno055.c new file +41 -0
+#define BNO055_ACC_CONFIG_REG		PG1(0x8)
+#define BNO055_ACC_CONFIG_LPF_MASK GENMASK(4, 2)
+#define BNO055_ACC_CONFIG_RANGE_MASK GENMASK(1, 0)
+#define BNO055_MAG_CONFIG_REG		PG1(0x9)
+#define BNO055_MAG_CONFIG_HIGHACCURACY 0x18
+#define BNO055_MAG_CONFIG_ODR_MASK GENMASK(2, 0)
+#define BNO055_GYR_CONFIG_REG		PG1(0xA)
+#define BNO055_GYR_CONFIG_RANGE_MASK GENMASK(2, 0)
+#define BNO055_GYR_CONFIG_LPF_MASK GENMASK(5, 3)
+#define BNO055_GYR_AM_SET_REG		PG1(0x1F)
+#define BNO055_UID_LOWER_REG		PG1(0x50)
+#define BNO055_UID_HIGHER_REG		PG1(0x5F)
+#define BNO055_UID_LEN 16
+
+struct bno055_sysfs_attr {
+	int *vals;
+	int len;
+	int *fusion_vals;
+	int *hw_xlate;
+	int type;
+};
+
+#define BNO055_ATTR_VALS(...)		\
+	.vals = (int[]){ __VA_ARGS__},	\
+	.len = ARRAY_SIZE(((int[]){__VA_ARGS__}))
+
+static struct bno055_sysfs_attr bno055_acc_lpf = {
+	BNO055_ATTR_VALS(7, 810000, 15, 630000,
+			 31, 250000, 62, 500000, 125, 0,
+			 250, 0, 500, 0, 1000, 0),
+	.fusion_vals = (int[]){62, 500000},
+	.type = IIO_VAL_INT_PLUS_MICRO
+};
+
+static struct bno055_sysfs_attr bno055_acc_range = {
+		     /* G:   2,    4,    8,    16 */
+	BNO055_ATTR_VALS(1962, 3924, 7848, 15696),
+	.fusion_vals = (int[]){3924}, /* 4G */
+	.type = IIO_VAL_INT
+};
+

drivers/iio/imu/bno055/bno055.c line 155

Quoted 10 lines
drivers/iio/imu/bno055/bno055.c new file +10 -0
+/*
+ * Theoretically the IMU should return data in a given (i.e. fixed) unit
+ * regardless the range setting. This happens for the accelerometer, but not for
+ * the gyroscope; the gyroscope range setting affects the scale.
+ * This is probably due to this[0] bug.
+ * For this reason we map the internal range setting onto the standard IIO scale
+ * attribute for gyro.
+ * Since the bug[0] may be fixed in future, we check for the IMU FW version and
+ * eventually warn the user.
+ * Currently we just't don't care about "range" attributes for gyro.

typo: just't

Quoted 52 lines
drivers/iio/imu/bno055/bno055.c new file +52 -0
+ *
+ * [0]  https://community.bosch-sensortec.com/t5/MEMS-sensors-forum/BNO055-Wrong-sensitivity-resolution-in-datasheet/td-p/10266
+ */
+static struct bno055_sysfs_attr bno055_gyr_scale = {
+	/*
+	 * dps = hwval * (dps_range/2^15)
+	 * rps = hwval * (rps_range/2^15)
+	 *     = hwval * (dps_range/(2^15 * k))
+	 * where k is rad-to-deg factor
+	 */
+	BNO055_ATTR_VALS(125, 1877467, 250, 1877467,
+			 500, 1877467, 1000, 1877467,
+			 2000, 1877467),
+	.fusion_vals = (int[]){1, 900},
+	.hw_xlate = (int[]){4, 3, 2, 1, 0},
+	.type = IIO_VAL_FRACTIONAL
+};
+
+static struct bno055_sysfs_attr bno055_gyr_lpf = {
+	BNO055_ATTR_VALS(12, 23, 32, 47, 64, 116, 230, 523),
+	.fusion_vals = (int[]){32},
+	.hw_xlate = (int[]){5, 4, 7, 3, 6, 2, 1, 0},
+	.type = IIO_VAL_INT
+};
+
+static struct bno055_sysfs_attr bno055_mag_odr = {
+	BNO055_ATTR_VALS(2, 6, 8, 10, 15, 20, 25, 30),
+	.fusion_vals = (int[]){20},
+	.type = IIO_VAL_INT
+};
+
+struct bno055_priv {
+	struct regmap *regmap;
+	struct device *dev;
+	struct clk *clk;
+	int operation_mode;
+	int xfer_burst_break_thr;
+	struct mutex lock;
+	u8 uid[BNO055_UID_LEN];
+	struct gpio_desc *reset_gpio;
+	bool sw_reset;
+	struct {
+		__le16 chans[BNO055_SCAN_CH_COUNT];
+		s64 timestamp __aligned(8);
+	} buf;
+};
+
+static bool bno055_regmap_volatile(struct device *dev, unsigned int reg)
+{
+	/* data and status registers */
+	if (reg >= BNO055_ACC_DATA_X_LSB_REG && reg <= BNO055_SYS_ERR_REG)
+		return true;

drivers/iio/imu/bno055/bno055.c line 217

Quoted 10 lines
drivers/iio/imu/bno055/bno055.c new file +10 -0
+
+	/* when in fusion mode, config is updated by chip */
+	if (reg == BNO055_MAG_CONFIG_REG ||
+	    reg == BNO055_ACC_CONFIG_REG ||
+	    reg == BNO055_GYR_CONFIG_REG)
+		return true;
+
+	/* calibration data may be updated by the IMU */
+	if (reg >= BNO055_CALDATA_START && reg <= BNO055_CALDATA_END)
+		return true;

I'd suggest a newline here (similarly elsewhere)

Quoted 23 lines
drivers/iio/imu/bno055/bno055.c new file +23 -0
+	return false;
+}
+
+static bool bno055_regmap_readable(struct device *dev, unsigned int reg)
+{
+	/* unnamed PG0 reserved areas */
+	if ((reg < PG1(0) && reg > BNO055_CALDATA_END) ||
+	    reg == 0x3C)
+		return false;
+
+	/* unnamed PG1 reserved areas */
+	if (reg > PG1(BNO055_UID_HIGHER_REG) ||
+	    (reg < PG1(BNO055_UID_LOWER_REG) && reg > PG1(BNO055_GYR_AM_SET_REG)) ||
+	    reg == PG1(0xE) ||
+	    (reg < PG1(BNO055_PAGESEL_REG) && reg >= PG1(0x0)))
+		return false;
+	return true;
+}
+
+static bool bno055_regmap_writeable(struct device *dev, unsigned int reg)
+{
+	/*
+	 * Unreadable registers are indeed reserved; there are no WO regs

drivers/iio/imu/bno055/bno055.c line 250

Quoted 10 lines
drivers/iio/imu/bno055/bno055.c new file +10 -0
+	 * (except for a single bit in SYS_TRIGGER register)
+	 */
+	if (!bno055_regmap_readable(dev, reg))
+		return false;
+
+	/* data and status registers */
+	if (reg >= BNO055_ACC_DATA_X_LSB_REG && reg <= BNO055_SYS_ERR_REG)
+		return false;
+
+	/* IDs areas */

probably 'ID area'?

Quoted 28 lines
drivers/iio/imu/bno055/bno055.c new file +28 -0
+	if (reg < BNO055_PAGESEL_REG ||
+	    (reg <= BNO055_UID_HIGHER_REG && reg >= BNO055_UID_LOWER_REG))
+		return false;
+
+	return true;
+}
+
+static const struct regmap_range_cfg bno055_regmap_ranges[] = {
+	{
+		.range_min = 0,
+		.range_max = 0x7f * 2,
+		.selector_reg = BNO055_PAGESEL_REG,
+		.selector_mask = GENMASK(7, 0),
+		.selector_shift = 0,
+		.window_start = 0,
+		.window_len = 0x80
+	},
+};
+
+const struct regmap_config bno055_regmap_config = {
+	.name = "bno055",
+	.reg_bits = 8,
+	.val_bits = 8,
+	.ranges = bno055_regmap_ranges,
+	.num_ranges = 1,
+	.volatile_reg = bno055_regmap_volatile,
+	.max_register = 0x80 * 2,
+	.writeable_reg = bno055_regmap_writeable,

drivers/iio/imu/bno055/bno055.c line 288

Quoted 10 lines
drivers/iio/imu/bno055/bno055.c new file +10 -0
+	.readable_reg = bno055_regmap_readable,
+	.cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(bno055_regmap_config);
+
+/* must be called in configuration mode */
+int bno055_calibration_load(struct bno055_priv *priv, const u8 *data, int len)
+{
+	if (len != BNO055_CALDATA_LEN) {
+		dev_dbg(priv->dev, "Invalid calibration file size %zu (expected %d)",

why %zu? len is int

Quoted 87 lines
drivers/iio/imu/bno055/bno055.c new file +87 -0
+			len, BNO055_CALDATA_LEN);
+		return -EINVAL;
+	}
+
+	dev_dbg(priv->dev, "loading cal data: %*ph", BNO055_CALDATA_LEN, data);
+	return regmap_bulk_write(priv->regmap, BNO055_CALDATA_START,
+				 data, BNO055_CALDATA_LEN);
+}
+
+static int bno055_operation_mode_do_set(struct bno055_priv *priv,
+					int operation_mode)
+{
+	int ret;
+
+	ret = regmap_write(priv->regmap, BNO055_OPR_MODE_REG,
+			   operation_mode);
+	if (ret)
+		return ret;
+
+	msleep(20);
+
+	return 0;
+}
+
+static int bno055_system_reset(struct bno055_priv *priv)
+{
+	int ret;
+
+	if (priv->reset_gpio) {
+		gpiod_set_value_cansleep(priv->reset_gpio, 0);
+		usleep_range(5000, 10000);
+		gpiod_set_value_cansleep(priv->reset_gpio, 1);
+	} else {
+		if (!priv->sw_reset)
+			return 0;
+
+		ret = regmap_write(priv->regmap, BNO055_SYS_TRIGGER_REG,
+				   BNO055_SYS_TRIGGER_RST_SYS);
+		if (ret)
+			return ret;
+	}
+
+	regcache_drop_region(priv->regmap, 0x0, 0xff);
+	usleep_range(650000, 700000);
+
+	return 0;
+}
+
+static int bno055_init(struct bno055_priv *priv, const u8 *caldata, int len)
+{
+	int ret;
+
+	ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(priv->regmap, BNO055_POWER_MODE_REG,
+			   BNO055_POWER_MODE_NORMAL);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(priv->regmap, BNO055_SYS_TRIGGER_REG,
+			   priv->clk ? BNO055_SYS_TRIGGER_CLK_SEL : 0);
+	if (ret)
+		return ret;
+
+	/* use standard SI units */
+	ret = regmap_write(priv->regmap, BNO055_UNIT_SEL_REG,
+			   BNO055_UNIT_SEL_ANDROID | BNO055_UNIT_SEL_GYR_RPS);
+	if (ret)
+		return ret;
+
+	if (caldata) {
+		ret = bno055_calibration_load(priv, caldata, len);
+		if (ret)
+			dev_warn(priv->dev, "failed to load calibration data with error %d",
+				 ret);
+	}
+
+	return 0;
+}
+
+static ssize_t bno055_operation_mode_set(struct bno055_priv *priv,
+					 int operation_mode)
+{
+	u8 caldata[BNO055_CALDATA_LEN];
+	int ret;

drivers/iio/imu/bno055/bno055.c line 385

Quoted 10 lines
drivers/iio/imu/bno055/bno055.c new file +10 -0
+
+	mutex_lock(&priv->lock);
+
+	ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+	if (ret)
+		goto exit;
+
+	if (operation_mode == BNO055_OPR_MODE_FUSION ||
+	    operation_mode == BNO055_OPR_MODE_FUSION_FMC_OFF) {
+		/* for entering fusiod mode, reset the chip to clear the algo state */

typo: fusiod

drivers/iio/imu/bno055/bno055.c line 393

Quoted 8 lines
drivers/iio/imu/bno055/bno055.c new file +8 -0
+		ret = regmap_bulk_read(priv->regmap, BNO055_CALDATA_START, caldata,
+				       BNO055_CALDATA_LEN);
+		if (ret)
+			goto exit;
+
+		ret = bno055_system_reset(priv);
+		if (ret)
+			return ret;

goto exit;
here as well?

drivers/iio/imu/bno055/bno055.c line 404

Quoted 11 lines
drivers/iio/imu/bno055/bno055.c new file +11 -0
+
+		ret = bno055_init(priv, caldata, BNO055_CALDATA_LEN);
+		if (ret)
+			goto exit;
+	}
+
+	ret = bno055_operation_mode_do_set(priv, operation_mode);
+	if (ret)
+		goto exit;
+
+	priv->operation_mode = operation_mode;

suggest a newline

Quoted 606 lines
drivers/iio/imu/bno055/bno055.c new file +606 -0
+exit:
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static void bno055_uninit(void *arg)
+{
+	struct bno055_priv *priv = arg;
+
+	/* stop the IMU */
+	bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+}
+
+#define BNO055_CHANNEL(_type, _axis, _index, _address, _sep, _sh, _avail) {	\
+	.address = _address,							\
+	.type = _type,								\
+	.modified = 1,								\
+	.channel2 = IIO_MOD_##_axis,						\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | (_sep),			\
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | (_sh),		\
+	.info_mask_shared_by_type_available = _avail,				\
+	.scan_index = _index,							\
+	.scan_type = {								\
+		.sign = 's',							\
+		.realbits = 16,							\
+		.storagebits = 16,						\
+		.endianness = IIO_LE,						\
+		.repeat = IIO_MOD_##_axis == IIO_MOD_QUATERNION ? 4 : 0		\
+	},									\
+}
+
+/* scan indexes follow DATA register order */
+enum bmi160_scan_axis {
+	BNO055_SCAN_ACCEL_X,
+	BNO055_SCAN_ACCEL_Y,
+	BNO055_SCAN_ACCEL_Z,
+	BNO055_SCAN_MAGN_X,
+	BNO055_SCAN_MAGN_Y,
+	BNO055_SCAN_MAGN_Z,
+	BNO055_SCAN_GYRO_X,
+	BNO055_SCAN_GYRO_Y,
+	BNO055_SCAN_GYRO_Z,
+	BNO055_SCAN_YAW,
+	BNO055_SCAN_ROLL,
+	BNO055_SCAN_PITCH,
+	BNO055_SCAN_QUATERNION,
+	BNO055_SCAN_LIA_X,
+	BNO055_SCAN_LIA_Y,
+	BNO055_SCAN_LIA_Z,
+	BNO055_SCAN_GRAVITY_X,
+	BNO055_SCAN_GRAVITY_Y,
+	BNO055_SCAN_GRAVITY_Z,
+	BNO055_SCAN_TIMESTAMP,
+};
+
+static const struct iio_chan_spec bno055_channels[] = {
+	/* accelerometer */
+	BNO055_CHANNEL(IIO_ACCEL, X, BNO055_SCAN_ACCEL_X,
+		       BNO055_ACC_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)),
+	BNO055_CHANNEL(IIO_ACCEL, Y, BNO055_SCAN_ACCEL_Y,
+		       BNO055_ACC_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)),
+	BNO055_CHANNEL(IIO_ACCEL, Z, BNO055_SCAN_ACCEL_Z,
+		       BNO055_ACC_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)),
+	/* gyroscope */
+	BNO055_CHANNEL(IIO_ANGL_VEL, X, BNO055_SCAN_GYRO_X,
+		       BNO055_GYR_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
+		       BIT(IIO_CHAN_INFO_SCALE)),
+	BNO055_CHANNEL(IIO_ANGL_VEL, Y, BNO055_SCAN_GYRO_Y,
+		       BNO055_GYR_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
+		       BIT(IIO_CHAN_INFO_SCALE)),
+	BNO055_CHANNEL(IIO_ANGL_VEL, Z, BNO055_SCAN_GYRO_Z,
+		       BNO055_GYR_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
+		       BIT(IIO_CHAN_INFO_SCALE)),
+	/* magnetometer */
+	BNO055_CHANNEL(IIO_MAGN, X, BNO055_SCAN_MAGN_X,
+		       BNO055_MAG_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)),
+	BNO055_CHANNEL(IIO_MAGN, Y, BNO055_SCAN_MAGN_Y,
+		       BNO055_MAG_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)),
+	BNO055_CHANNEL(IIO_MAGN, Z, BNO055_SCAN_MAGN_Z,
+		       BNO055_MAG_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)),
+	/* euler angle */
+	BNO055_CHANNEL(IIO_ROT, YAW, BNO055_SCAN_YAW,
+		       BNO055_EUL_DATA_X_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_ROT, ROLL, BNO055_SCAN_ROLL,
+		       BNO055_EUL_DATA_Y_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_ROT, PITCH, BNO055_SCAN_PITCH,
+		       BNO055_EUL_DATA_Z_LSB_REG, 0, 0, 0),
+	/* quaternion */
+	BNO055_CHANNEL(IIO_ROT, QUATERNION, BNO055_SCAN_QUATERNION,
+		       BNO055_QUAT_DATA_W_LSB_REG, 0, 0, 0),
+
+	/* linear acceleration */
+	BNO055_CHANNEL(IIO_ACCEL, LINEAR_X, BNO055_SCAN_LIA_X,
+		       BNO055_LIA_DATA_X_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_ACCEL, LINEAR_Y, BNO055_SCAN_LIA_Y,
+		       BNO055_LIA_DATA_Y_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_ACCEL, LINEAR_Z, BNO055_SCAN_LIA_Z,
+		       BNO055_LIA_DATA_Z_LSB_REG, 0, 0, 0),
+
+	/* gravity vector */
+	BNO055_CHANNEL(IIO_GRAVITY, X, BNO055_SCAN_GRAVITY_X,
+		       BNO055_GRAVITY_DATA_X_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_GRAVITY, Y, BNO055_SCAN_GRAVITY_Y,
+		       BNO055_GRAVITY_DATA_Y_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_GRAVITY, Z, BNO055_SCAN_GRAVITY_Z,
+		       BNO055_GRAVITY_DATA_Z_LSB_REG, 0, 0, 0),
+
+	{
+		.type = IIO_TEMP,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+		.scan_index = -1
+	},
+	IIO_CHAN_SOFT_TIMESTAMP(BNO055_SCAN_TIMESTAMP),
+};
+
+static int bno055_get_regmask(struct bno055_priv *priv, int *val, int *val2,
+			      int reg, int mask, struct bno055_sysfs_attr *attr)
+{
+	const int shift = __ffs(mask);
+	int hwval, idx;
+	int ret;
+	int i;
+
+	ret = regmap_read(priv->regmap, reg, &hwval);
+	if (ret)
+		return ret;
+
+	idx = (hwval & mask) >> shift;
+	if (attr->hw_xlate)
+		for (i = 0; i < attr->len; i++)
+			if (attr->hw_xlate[i] == idx) {
+				idx = i;
+				break;
+			}
+	if (attr->type == IIO_VAL_INT) {
+		*val = attr->vals[idx];
+	} else { /* IIO_VAL_INT_PLUS_MICRO or IIO_VAL_FRACTIONAL */
+		*val = attr->vals[idx * 2];
+		*val2 = attr->vals[idx * 2 + 1];
+	}
+
+	return attr->type;
+}
+
+static int bno055_set_regmask(struct bno055_priv *priv, int val, int val2,
+			      int reg, int mask, struct bno055_sysfs_attr *attr)
+{
+	const int shift = __ffs(mask);
+	int best_delta;
+	int req_val;
+	int tbl_val;
+	bool first;
+	int delta;
+	int hwval;
+	int ret;
+	int len;
+	int i;
+
+	/*
+	 * The closest value the HW supports is only one in fusion mode,
+	 * and it is autoselected, so don't do anything, just return OK,
+	 * as the closest possible value has been (virtually) selected
+	 */
+	if (priv->operation_mode != BNO055_OPR_MODE_AMG)
+		return 0;
+
+	len = attr->len;
+
+	/*
+	 * We always get a request in INT_PLUS_MICRO, but we
+	 * take care of the micro part only when we really have
+	 * non-integer tables. This prevents 32-bit overflow with
+	 * larger integers contained in integer tables.
+	 */
+	req_val = val;
+	if (attr->type != IIO_VAL_INT) {
+		if (val > 2147)
+			val = 2147;
+		len /= 2;
+		req_val = val * 1000000 + val2;
+	}
+
+	first = true;
+	for (i = 0; i < len; i++) {
+		switch (attr->type) {
+		case IIO_VAL_INT:
+			tbl_val = attr->vals[i];
+			break;
+		case IIO_VAL_INT_PLUS_MICRO:
+			WARN_ON(attr->vals[i * 2] > 2147);
+			tbl_val = attr->vals[i * 2] * 1000000 +
+				attr->vals[i * 2 + 1];
+			break;
+		case IIO_VAL_FRACTIONAL:
+			WARN_ON(attr->vals[i * 2] > 4294);
+			tbl_val = attr->vals[i * 2] * 1000000 /
+				attr->vals[i * 2 + 1];
+			break;
+		default:
+			return -EINVAL;
+		}
+		delta = abs(tbl_val - req_val);
+		if (first || delta < best_delta) {
+			best_delta = delta;
+			hwval = i;
+			first = false;
+		}
+	}
+
+	if (attr->hw_xlate)
+		hwval = attr->hw_xlate[hwval];
+
+	ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(priv->regmap, reg, mask, hwval << shift);
+	if (ret)
+		return ret;
+
+	return bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_AMG);
+}
+
+static int bno055_read_simple_chan(struct iio_dev *indio_dev,
+				   struct iio_chan_spec const *chan,
+				   int *val, int *val2, long mask)
+{
+	struct bno055_priv *priv = iio_priv(indio_dev);
+	__le16 raw_val;
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = regmap_bulk_read(priv->regmap, chan->address,
+				       &raw_val, sizeof(raw_val));
+		if (ret < 0)
+			return ret;
+		*val = (s16)le16_to_cpu(raw_val);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_OFFSET:
+		if (priv->operation_mode != BNO055_OPR_MODE_AMG) {
+			*val = 0;
+		} else {
+			ret = regmap_bulk_read(priv->regmap,
+					       chan->address +
+					       BNO055_REG_OFFSET_ADDR,
+					       &raw_val, sizeof(raw_val));
+			if (ret < 0)
+				return ret;
+			/*
+			 * IMU reports sensor offests; IIO wants correction
+			 * offset, thus we need the 'minus' here.
+			 */
+			*val = -(s16)le16_to_cpu(raw_val);
+		}
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 1;
+		switch (chan->type) {
+		case IIO_GRAVITY:
+			/* Table 3-35: 1 m/s^2 = 100 LSB */
+		case IIO_ACCEL:
+			/* Table 3-17: 1 m/s^2 = 100 LSB */
+			*val2 = 100;
+			break;
+		case IIO_MAGN:
+			/*
+			 * Table 3-19: 1 uT = 16 LSB.  But we need
+			 * Gauss: 1G = 0.1 uT.
+			 */
+			*val2 = 160;
+			break;
+		case IIO_ANGL_VEL:
+			/*
+			 * Table 3-22: 1 Rps = 900 LSB
+			 * .. but this is not exactly true. See comment at the
+			 * beginning of this file.
+			 */
+			if (priv->operation_mode != BNO055_OPR_MODE_AMG) {
+				*val = bno055_gyr_scale.fusion_vals[0];
+				*val2 = bno055_gyr_scale.fusion_vals[1];
+				return IIO_VAL_FRACTIONAL;
+			}
+
+			return bno055_get_regmask(priv, val, val2,
+						  BNO055_GYR_CONFIG_REG,
+						  BNO055_GYR_CONFIG_RANGE_MASK,
+						  &bno055_gyr_scale);
+			break;
+		case IIO_ROT:
+			/* Table 3-28: 1 degree = 16 LSB */
+			*val2 = 16;
+			break;
+		default:
+			return -EINVAL;
+		}
+		return IIO_VAL_FRACTIONAL;
+
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		if (chan->type != IIO_MAGN)
+			return -EINVAL;
+		else
+			return bno055_get_regmask(priv, val, val2,
+						  BNO055_MAG_CONFIG_REG,
+						  BNO055_MAG_CONFIG_ODR_MASK,
+						  &bno055_mag_odr);
+
+	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			return bno055_get_regmask(priv, val, val2,
+						  BNO055_GYR_CONFIG_REG,
+						  BNO055_GYR_CONFIG_LPF_MASK,
+						  &bno055_gyr_lpf);
+		case IIO_ACCEL:
+			return bno055_get_regmask(priv, val, val2,
+						  BNO055_ACC_CONFIG_REG,
+						  BNO055_ACC_CONFIG_LPF_MASK,
+						  &bno055_acc_lpf);
+		default:
+			return -EINVAL;
+		}
+
+	default:
+		return -EINVAL;
+	}
+}
+
+int bno055_sysfs_attr_avail(struct bno055_priv *priv, struct bno055_sysfs_attr *attr,
+			    const int **vals, int *length)
+{
+	if (priv->operation_mode != BNO055_OPR_MODE_AMG) {
+		/* locked when fusion enabled */
+		*vals = attr->fusion_vals;
+		if (attr->type == IIO_VAL_INT)
+			*length = 1;
+		else
+			*length = 2; /* IIO_VAL_INT_PLUS_MICRO or IIO_VAL_FRACTIONAL*/
+	} else {
+		*vals = attr->vals;
+		*length = attr->len;
+	}
+
+	return attr->type;
+}
+
+static int bno055_read_avail(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     const int **vals, int *type, int *length,
+			     long mask)
+{
+	struct bno055_priv *priv = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			*type = bno055_sysfs_attr_avail(priv, &bno055_gyr_scale,
+							vals, length);
+			return IIO_AVAIL_LIST;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			*type = bno055_sysfs_attr_avail(priv, &bno055_gyr_lpf,
+							vals, length);
+			return IIO_AVAIL_LIST;
+		case IIO_ACCEL:
+			*type = bno055_sysfs_attr_avail(priv, &bno055_acc_lpf,
+							vals, length);
+			return IIO_AVAIL_LIST;
+		default:
+			return -EINVAL;
+		}
+
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		switch (chan->type) {
+		case IIO_MAGN:
+			*type = bno055_sysfs_attr_avail(priv, &bno055_mag_odr,
+							vals, length);
+			return IIO_AVAIL_LIST;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int bno055_read_temp_chan(struct iio_dev *indio_dev, int *val)
+{
+	struct bno055_priv *priv = iio_priv(indio_dev);
+	unsigned int raw_val;
+	int ret;
+
+	ret = regmap_read(priv->regmap, BNO055_TEMP_REG, &raw_val);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Tables 3-36 and 3-37: one byte of priv, signed, 1 LSB = 1C.
+	 * ABI wants milliC.
+	 */
+	*val = raw_val * 1000;
+
+	return IIO_VAL_INT;
+}
+
+static int bno055_read_quaternion(struct iio_dev *indio_dev,
+				  struct iio_chan_spec const *chan,
+				  int size, int *vals, int *val_len,
+				  long mask)
+{
+	struct bno055_priv *priv = iio_priv(indio_dev);
+	__le16 raw_vals[4];
+	int i, ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		if (size < 4)
+			return -EINVAL;
+		ret = regmap_bulk_read(priv->regmap,
+				       BNO055_QUAT_DATA_W_LSB_REG,
+				       raw_vals, sizeof(raw_vals));
+		if (ret < 0)
+			return ret;
+		for (i = 0; i < 4; i++)
+			vals[i] = (s16)le16_to_cpu(raw_vals[i]);
+		*val_len = 4;
+		return IIO_VAL_INT_MULTIPLE;
+	case IIO_CHAN_INFO_SCALE:
+		/* Table 3-31: 1 quaternion = 2^14 LSB */
+		if (size < 2)
+			return -EINVAL;
+		vals[0] = 1;
+		vals[1] = 1 << 14;
+		return IIO_VAL_FRACTIONAL_LOG2;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int _bno055_read_raw_multi(struct iio_dev *indio_dev,
+				  struct iio_chan_spec const *chan,
+				  int size, int *vals, int *val_len,
+				  long mask)
+{
+	switch (chan->type) {
+	case IIO_MAGN:
+	case IIO_ACCEL:
+	case IIO_ANGL_VEL:
+	case IIO_GRAVITY:
+		if (size < 2)
+			return -EINVAL;
+		*val_len = 2;
+		return bno055_read_simple_chan(indio_dev, chan,
+					       &vals[0], &vals[1],
+					       mask);
+	case IIO_TEMP:
+		*val_len = 1;
+		return bno055_read_temp_chan(indio_dev, &vals[0]);
+	case IIO_ROT:
+		/*
+		 * Rotation is exposed as either a quaternion or three
+		 * Euler angles.
+		 */
+		if (chan->channel2 == IIO_MOD_QUATERNION)
+			return bno055_read_quaternion(indio_dev, chan,
+						      size, vals,
+						      val_len, mask);
+		if (size < 2)
+			return -EINVAL;
+		*val_len = 2;
+		return bno055_read_simple_chan(indio_dev, chan,
+					       &vals[0], &vals[1],
+					       mask);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int bno055_read_raw_multi(struct iio_dev *indio_dev,
+				 struct iio_chan_spec const *chan,
+				 int size, int *vals, int *val_len,
+				 long mask)
+{
+	struct bno055_priv *priv = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&priv->lock);
+	ret = _bno055_read_raw_multi(indio_dev, chan, size,
+				     vals, val_len, mask);
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static int _bno055_write_raw(struct iio_dev *iio_dev,
+			     struct iio_chan_spec const *chan,
+			     int val, int val2, long mask)
+{
+	struct bno055_priv *priv = iio_priv(iio_dev);
+
+	switch (chan->type) {
+	case IIO_MAGN:
+		switch (mask) {
+		case IIO_CHAN_INFO_SAMP_FREQ:
+			return bno055_set_regmask(priv, val, val2,
+						  BNO055_MAG_CONFIG_REG,
+						  BNO055_MAG_CONFIG_ODR_MASK,
+						  &bno055_mag_odr);
+		default:
+			return -EINVAL;
+		}
+	case IIO_ACCEL:
+		switch (mask) {
+		case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+			return bno055_set_regmask(priv, val, val2,
+						  BNO055_ACC_CONFIG_REG,
+						  BNO055_ACC_CONFIG_LPF_MASK,
+						  &bno055_acc_lpf);
+
+		default:
+			return -EINVAL;
+		}
+	case IIO_ANGL_VEL:
+		switch (mask) {
+		case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+			return bno055_set_regmask(priv, val, val2,
+						  BNO055_GYR_CONFIG_REG,
+						  BNO055_GYR_CONFIG_LPF_MASK,
+						  &bno055_gyr_lpf);
+		case IIO_CHAN_INFO_SCALE:
+			return bno055_set_regmask(priv, val, val2,
+						  BNO055_GYR_CONFIG_REG,
+						  BNO055_GYR_CONFIG_RANGE_MASK,
+						  &bno055_gyr_scale);
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int bno055_write_raw(struct iio_dev *iio_dev,
+			    struct iio_chan_spec const *chan,
+			    int val, int val2, long mask)
+{
+	struct bno055_priv *priv = iio_priv(iio_dev);
+	int ret;
+
+	mutex_lock(&priv->lock);
+	ret = _bno055_write_raw(iio_dev, chan, val, val2, mask);
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+static ssize_t in_accel_range_raw_available_show(struct device *dev,
+						 struct device_attribute *attr,
+						 char *buf)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int len = 0;
+	int i;
+
+	if (priv->operation_mode != BNO055_OPR_MODE_AMG)
+		return sysfs_emit(buf, "%d\n", bno055_acc_range.fusion_vals[0]);
+
+	for (i = 0; i < bno055_acc_range.len; i++)
+		len += sysfs_emit_at(buf, len, "%d%c", bno055_acc_range.vals[i],
+				     (i == bno055_acc_range.len - 1) ? '\n' : ' ');
+	return len;
+}
+
+static ssize_t bno055_fusion_enable_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+
+	return sysfs_emit(buf, "%d\n",
+			  priv->operation_mode != BNO055_OPR_MODE_AMG);
+}
+
+static ssize_t bno055_fusion_enable_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t len)

drivers/iio/imu/bno055/bno055.c line 1020

Quoted 10 lines
drivers/iio/imu/bno055/bno055.c new file +10 -0
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int ret = 0;
+
+	if (sysfs_streq(buf, "0")) {
+		ret = bno055_operation_mode_set(priv, BNO055_OPR_MODE_AMG);
+	} else {
+		/*
+		 * Coming from AMG means the FMC was off, just switch to fusion
+		 * but don't change anything that doesn't belong to us (i.e let.

typo: no . at the end

Quoted 488 lines
drivers/iio/imu/bno055/bno055.c new file +488 -0
+		 * FMC stay off.
+		 * Coming from any other fusion mode means we don't need to do
+		 * anything.
+		 */
+		if (priv->operation_mode == BNO055_OPR_MODE_AMG)
+			ret = bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION_FMC_OFF);
+	}
+	if (ret)
+		return ret;
+	return len;
+}
+
+static ssize_t bno055_fmc_enable_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+
+	return sysfs_emit(buf, "%d\n",
+			  priv->operation_mode == BNO055_OPR_MODE_FUSION);
+}
+
+static ssize_t bno055_fmc_enable_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t len)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int ret;
+
+	if (sysfs_streq(buf, "0")) {
+		if (priv->operation_mode == BNO055_OPR_MODE_FUSION) {
+			ret = bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION_FMC_OFF);
+			if (ret)
+				return ret;
+		}
+	} else {
+		if (priv->operation_mode == BNO055_OPR_MODE_AMG)
+			return -EINVAL;
+
+		if (priv->operation_mode != BNO055_OPR_MODE_FUSION) {
+			ret = bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return len;
+}
+
+static ssize_t bno055_in_accel_range_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int val;
+	int ret;
+
+	ret = bno055_get_regmask(priv, &val, NULL,
+				 BNO055_ACC_CONFIG_REG,
+				 BNO055_ACC_CONFIG_RANGE_MASK,
+				 &bno055_acc_range);
+	if (ret < 0)
+		return ret;
+
+	return sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t bno055_in_accel_range_store(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf, size_t len)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	unsigned long val;
+	int ret;
+
+	ret = kstrtoul(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&priv->lock);
+	ret = bno055_set_regmask(priv, val, 0,
+				 BNO055_ACC_CONFIG_REG,
+				 BNO055_ACC_CONFIG_RANGE_MASK,
+				 &bno055_acc_range);
+	mutex_unlock(&priv->lock);
+
+	return ret ?: len;
+}
+
+static ssize_t bno055_get_calib_status(struct device *dev, char *buf, int which)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int calib;
+	int ret;
+	int val;
+
+	if (priv->operation_mode == BNO055_OPR_MODE_AMG ||
+	    (priv->operation_mode == BNO055_OPR_MODE_FUSION_FMC_OFF &&
+	     which == BNO055_CALIB_STAT_MAGN_SHIFT)) {
+		calib = 0;
+	} else {
+		mutex_lock(&priv->lock);
+		ret = regmap_read(priv->regmap, BNO055_CALIB_STAT_REG, &val);
+		mutex_unlock(&priv->lock);
+
+		if (ret)
+			return -EIO;
+
+		calib = ((val >> which) & BNO055_CALIB_STAT_MASK) + 1;
+	}
+
+	return sysfs_emit(buf, "%d\n", calib);
+}
+
+static ssize_t serial_number_show(struct device *dev,
+				  struct device_attribute *attr,
+				  char *buf)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+
+	return sysfs_emit(buf, "%*ph\n", BNO055_UID_LEN, priv->uid);
+}
+
+static ssize_t calibration_data_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	u8 data[BNO055_CALDATA_LEN];
+	int ret;
+
+	mutex_lock(&priv->lock);
+	ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+	if (ret)
+		goto unlock;
+
+	ret = regmap_bulk_read(priv->regmap, BNO055_CALDATA_START, data,
+			       BNO055_CALDATA_LEN);
+	if (ret)
+		goto unlock;
+
+	ret = bno055_operation_mode_do_set(priv, priv->operation_mode);
+	if (ret)
+		goto unlock;
+
+	memcpy(buf, data, BNO055_CALDATA_LEN);
+
+	ret = BNO055_CALDATA_LEN;
+unlock:
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static ssize_t sys_calibration_auto_status_show(struct device *dev,
+						struct device_attribute *a,
+						char *buf)
+{
+	return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_SYS_SHIFT);
+}
+
+static ssize_t in_accel_calibration_auto_status_show(struct device *dev,
+						     struct device_attribute *a,
+						     char *buf)
+{
+	return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_ACCEL_SHIFT);
+}
+
+static ssize_t in_gyro_calibration_auto_status_show(struct device *dev,
+						    struct device_attribute *a,
+						    char *buf)
+{
+	return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_GYRO_SHIFT);
+}
+
+static ssize_t in_magn_calibration_auto_status_show(struct device *dev,
+						    struct device_attribute *a,
+						    char *buf)
+{
+	return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_MAGN_SHIFT);
+}
+
+#ifdef CONFIG_DEBUG_FS
+int bno055_debugfs_reg_access(struct iio_dev *iio_dev, unsigned int reg,
+			      unsigned int writeval, unsigned int *readval)
+{
+	struct bno055_priv *priv = iio_priv(iio_dev);
+
+	if (readval)
+		return regmap_read(priv->regmap, reg, readval);
+	else
+		return regmap_write(priv->regmap, reg, writeval);
+}
+
+static ssize_t bno055_show_fw_version(struct file *file, char __user *userbuf,
+				      size_t count, loff_t *ppos)
+{
+	struct bno055_priv *priv = file->private_data;
+	int rev, ver;
+	char *buf;
+	int ret;
+
+	ret = regmap_read(priv->regmap, BNO055_SW_REV_LSB_REG, &rev);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(priv->regmap, BNO055_SW_REV_MSB_REG, &ver);
+	if (ret)
+		return ret;
+
+	buf = kasprintf(GFP_KERNEL, "ver: 0x%x, rev: 0x%x\n", ver, rev);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf));
+	kfree(buf);
+
+	return ret;
+}
+
+static const struct file_operations bno055_fw_version_ops = {
+	.open = simple_open,
+	.read = bno055_show_fw_version,
+	.llseek = default_llseek,
+	.owner = THIS_MODULE,
+};
+
+static void bno055_debugfs_init(struct iio_dev *iio_dev)
+{
+	struct bno055_priv *priv = iio_priv(iio_dev);
+
+	debugfs_create_file("firmware_version", 0400,
+			    iio_get_debugfs_dentry(iio_dev), priv,
+			    &bno055_fw_version_ops);
+}
+#else
+static void bno055_debugfs_init(struct iio_dev *iio_dev)
+{
+}
+
+int bno055_debugfs_reg_access(struct iio_dev *iio_dev, unsigned int reg,
+			      unsigned int writeval, unsigned int *readval)
+{
+	return 0;
+}
+#endif
+
+static IIO_DEVICE_ATTR(fusion_enable, 0644,
+		       bno055_fusion_enable_show,
+		       bno055_fusion_enable_store, 0);
+
+static IIO_DEVICE_ATTR(in_magn_calibration_fast_enable, 0644,
+		       bno055_fmc_enable_show,
+		       bno055_fmc_enable_store, 0);
+
+static IIO_DEVICE_ATTR(in_accel_range_raw, 0644,
+		       bno055_in_accel_range_show,
+		       bno055_in_accel_range_store, 0);
+
+static IIO_DEVICE_ATTR_RO(in_accel_range_raw_available, 0);
+
+static IIO_DEVICE_ATTR_RO(sys_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_accel_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_gyro_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_magn_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(calibration_data, 0);
+
+static IIO_DEVICE_ATTR_RO(serial_number, 0);
+
+static struct attribute *bno055_attrs[] = {
+	&iio_dev_attr_in_accel_range_raw_available.dev_attr.attr,
+	&iio_dev_attr_in_accel_range_raw.dev_attr.attr,
+	&iio_dev_attr_fusion_enable.dev_attr.attr,
+	&iio_dev_attr_in_magn_calibration_fast_enable.dev_attr.attr,
+	&iio_dev_attr_sys_calibration_auto_status.dev_attr.attr,
+	&iio_dev_attr_in_accel_calibration_auto_status.dev_attr.attr,
+	&iio_dev_attr_in_gyro_calibration_auto_status.dev_attr.attr,
+	&iio_dev_attr_in_magn_calibration_auto_status.dev_attr.attr,
+	&iio_dev_attr_calibration_data.dev_attr.attr,
+	&iio_dev_attr_serial_number.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group bno055_attrs_group = {
+	.attrs = bno055_attrs,
+};
+
+static const struct iio_info bno055_info = {
+	.read_raw_multi = bno055_read_raw_multi,
+	.read_avail = bno055_read_avail,
+	.write_raw = bno055_write_raw,
+	.attrs = &bno055_attrs_group,
+	.debugfs_reg_access = bno055_debugfs_reg_access,
+};
+
+/*
+ * Reads len samples from the HW, stores them in buf starting from buf_idx,
+ * and applies mask to cull (skip) unneeded samples.
+ * Updates buf_idx incrementing with the number of stored samples.
+ * Samples from HW are transferred into buf, then in-place copy on buf is
+ * performed in order to cull samples that need to be skipped.
+ * This avoids copies of the first samples until we hit the 1st sample to skip,
+ * and also avoids having an extra bounce buffer.
+ * buf must be able to contain len elements in spite of how many samples we are
+ * going to cull.
+ */
+static int bno055_scan_xfer(struct bno055_priv *priv,
+			    int start_ch, int len, unsigned long mask,
+			    __le16 *buf, int *buf_idx)
+{
+	const int base = BNO055_ACC_DATA_X_LSB_REG;
+	bool quat_in_read = false;
+	int buf_base = *buf_idx;
+	__le16 *dst, *src;
+	int offs_fixup = 0;
+	int xfer_len = len;
+	int ret;
+	int i, n;
+
+	if (!mask)
+		return 0;
+
+	/*
+	 * All chans are made up 1 16-bit sample, except for quaternion that is
+	 * made up 4 16-bit values.
+	 * For us the quaternion CH is just like 4 regular CHs.
+	 * If our read starts past the quaternion make sure to adjust the
+	 * starting offset; if the quaternion is contained in our scan then make
+	 * sure to adjust the read len.
+	 */
+	if (start_ch > BNO055_SCAN_QUATERNION) {
+		start_ch += 3;
+	} else if ((start_ch <= BNO055_SCAN_QUATERNION) &&
+		 ((start_ch + len) > BNO055_SCAN_QUATERNION)) {
+		quat_in_read = true;
+		xfer_len += 3;
+	}
+
+	ret = regmap_bulk_read(priv->regmap,
+			       base + start_ch * sizeof(__le16),
+			       buf + buf_base,
+			       xfer_len * sizeof(__le16));
+	if (ret)
+		return ret;
+
+	for_each_set_bit(i, &mask, len) {
+		if (quat_in_read && ((start_ch + i) > BNO055_SCAN_QUATERNION))
+			offs_fixup = 3;
+
+		dst = buf + *buf_idx;
+		src = buf + buf_base + offs_fixup + i;
+
+		n = (start_ch + i == BNO055_SCAN_QUATERNION) ? 4 : 1;
+
+		if (dst != src)
+			memcpy(dst, src, n * sizeof(__le16));
+
+		*buf_idx += n;
+	}
+	return 0;
+}
+
+static irqreturn_t bno055_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *iio_dev = pf->indio_dev;
+	struct bno055_priv *priv = iio_priv(iio_dev);
+	int xfer_start, start, end, prev_end;
+	unsigned long mask;
+	int quat_extra_len;
+	bool first = true;
+	int buf_idx = 0;
+	bool thr_hit;
+	int ret;
+
+	mutex_lock(&priv->lock);
+
+	/*
+	 * Walk the bitmap and eventually perform several transfers.
+	 * Bitmap ones-fileds that are separated by gaps <= xfer_burst_break_thr
+	 * will be included in same transfer.
+	 * Every time the bitmap contains a gap wider than xfer_burst_break_thr
+	 * then we split the transfer, skipping the gap.
+	 */
+	for_each_set_bitrange(start, end, iio_dev->active_scan_mask,
+			      iio_dev->masklength) {
+		/*
+		 * First transfer will start from the beginnig of the first
+		 * ones-field in the bitmap
+		 */
+		if (first)
+			xfer_start = start;
+
+		/*
+		 * We found the next ones-field; check whether to include it in
+		 * the current transfer or not (i.e. let's perform the current
+		 * transfer and prepare for another one).
+		 */
+		if (!first) {
+			/*
+			 * In case the zeros-gap contains the quaternion bit,
+			 * then its length is actually 4 words instead of 1
+			 * (i.e. +3 wrt other channels).
+			 */
+			quat_extra_len = ((start > BNO055_SCAN_QUATERNION) &&
+					  (prev_end <= BNO055_SCAN_QUATERNION)) ? 3 : 0;
+
+			/* If the gap is wider than xfer_burst_break_thr then.. */
+			thr_hit = (start - prev_end + quat_extra_len) >
+				priv->xfer_burst_break_thr;
+
+			/*
+			 * .. transfer all the data up to the gap. Then set the
+			 * next transfer start index at right after the gap
+			 * (i.e. at the start of this ones-field).
+			 */
+			if (thr_hit) {
+				mask = *iio_dev->active_scan_mask >> xfer_start;
+				ret = bno055_scan_xfer(priv, xfer_start,
+						       prev_end - xfer_start,
+						       mask, priv->buf.chans, &buf_idx);
+				if (ret)
+					goto done;
+				xfer_start = start;
+			}
+		}
+		first = false;
+		prev_end = end;
+	}
+
+	/*
+	 * We finished walking the bitmap; no more gaps to check for. Just
+	 * perform the current transfer.
+	 */
+	mask = *iio_dev->active_scan_mask >> xfer_start;
+	ret = bno055_scan_xfer(priv, xfer_start,
+			       prev_end - xfer_start,
+			       mask, priv->buf.chans, &buf_idx);
+
+	iio_push_to_buffers_with_timestamp(iio_dev, &priv->buf, pf->timestamp);
+done:
+	mutex_unlock(&priv->lock);
+	iio_trigger_notify_done(iio_dev->trig);
+	return IRQ_HANDLED;
+}
+
+static void bno055_clk_disable(void *arg)
+{
+	clk_disable_unprepare((struct clk *)arg);
+}
+
+int bno055_probe(struct device *dev, struct regmap *regmap,
+		 int xfer_burst_break_thr, bool sw_reset)
+{
+	const struct firmware *caldata;
+	struct bno055_priv *priv;
+	const u8 *caldata_data = NULL;
+	struct iio_dev *iio_dev;
+	int caldata_size = 0;
+	char *fw_name_buf;
+	unsigned int val;
+	int rev, ver;
+	int ret;
+
+	iio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+	if (!iio_dev)
+		return -ENOMEM;
+
+	iio_dev->name = "bno055";
+	priv = iio_priv(iio_dev);
+	mutex_init(&priv->lock);
+	priv->regmap = regmap;
+	priv->dev = dev;
+	priv->xfer_burst_break_thr = xfer_burst_break_thr;
+	priv->sw_reset = sw_reset;
+
+	priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(priv->reset_gpio))
+		return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO");
+
+	priv->clk = devm_clk_get_optional(dev, "clk");
+	if (IS_ERR(priv->clk))
+		return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to get CLK");
+
+	ret = clk_prepare_enable(priv->clk);
+	if (ret)
+		return ret;
+
+	ret = devm_add_action_or_reset(dev, bno055_clk_disable, priv->clk);

drivers/iio/imu/bno055/bno055.c line 1518

Quoted 10 lines
drivers/iio/imu/bno055/bno055.c new file +10 -0
+	if (ret)
+		return ret;
+
+	if (priv->reset_gpio) {
+		usleep_range(5000, 10000);
+		gpiod_set_value_cansleep(priv->reset_gpio, 1);
+		usleep_range(650000, 750000);
+	} else {
+		if (!sw_reset)
+			dev_warn(dev, "No any usable reset metod; IMU may be unreliable");

typo: metod
maybe: "No usable reset method; ..."

Quoted 113 lines
drivers/iio/imu/bno055/bno055.c new file +94 -0
+	}
+
+	ret = regmap_read(priv->regmap, BNO055_CHIP_ID_REG, &val);
+	if (ret)
+		return ret;
+
+	if (val != BNO055_CHIP_ID_MAGIC) {
+		dev_err(dev, "Unrecognized chip ID 0x%x", val);
+		return -ENODEV;
+	}
+
+	if (!priv->reset_gpio) {
+		ret = bno055_system_reset(priv);
+		if (ret)
+			return ret;
+	}
+
+	ret = regmap_read(priv->regmap, BNO055_SW_REV_LSB_REG, &rev);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(priv->regmap, BNO055_SW_REV_MSB_REG, &ver);
+	if (ret)
+		return ret;
+
+	if (ver != 0x3 || rev != 0x11)
+		dev_warn(dev, "Untested firmware version. Anglvel scale may not work as expected");
+
+	ret = regmap_bulk_read(priv->regmap, BNO055_UID_LOWER_REG,
+			       priv->uid, BNO055_UID_LEN);
+	if (ret)
+		return ret;
+
+	/*
+	 * This has nothing to do with the IMU firmware, this is for sensor
+	 * calibration data.
+	 */
+	fw_name_buf = kasprintf(GFP_KERNEL,
+				BNO055_FW_UID_NAME,
+				BNO055_UID_LEN, priv->uid);
+	if (!fw_name_buf)
+		return -ENOMEM;
+
+	ret = request_firmware(&caldata, fw_name_buf, dev);
+	kfree(fw_name_buf);
+	if (ret)
+		ret = request_firmware(&caldata, BNO055_FW_GENERIC_NAME, dev);
+
+	if (ret)
+		dev_notice(dev, "Calibration file load failed. See instruction in kernel Documentation");
+
+	if (caldata) {
+		caldata_data = caldata->data;
+		caldata_size = caldata->size;
+	}
+	ret = bno055_init(priv, caldata_data, caldata_size);
+	if (caldata)
+		release_firmware(caldata);
+	if (ret)
+		return ret;
+
+	priv->operation_mode = BNO055_OPR_MODE_FUSION;
+	ret = bno055_operation_mode_do_set(priv, priv->operation_mode);
+	if (ret)
+		return ret;
+
+	ret = devm_add_action_or_reset(dev, bno055_uninit, priv);
+	if (ret)
+		return ret;
+
+	iio_dev->channels = bno055_channels;
+	iio_dev->num_channels = ARRAY_SIZE(bno055_channels);
+	iio_dev->info = &bno055_info;
+	iio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = devm_iio_triggered_buffer_setup(dev, iio_dev,
+					      iio_pollfunc_store_time,
+					      bno055_trigger_handler, NULL);
+	if (ret)
+		return ret;
+
+	ret = devm_iio_device_register(dev, iio_dev);
+	if (ret)
+		return ret;
+
+	bno055_debugfs_init(iio_dev);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(bno055_probe);
+
+MODULE_AUTHOR("Andrea Merello <[email protected]>");
+MODULE_DESCRIPTION("Bosch BNO055 driver");
+MODULE_LICENSE("GPL v2");
drivers/iio/imu/bno055/bno055.h new file +12 -0
new file mode 100644
index 000000000000..2ccb373c61cd
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __BNO055_H__
+#define __BNO055_H__
+
+#include <linux/regmap.h>
+
+struct device;
+int bno055_probe(struct device *dev, struct regmap *regmap,
+		 int xfer_burst_break_thr, bool sw_reset);
+extern const struct regmap_config bno055_regmap_config;
+
+#endif
Signature · 2 lines
Peter Meerwald-Stadler
Mobile: +43 664 24 44 418
maintainer
#11 lore raw

Re: [v3 07/13] iio: imu: add Bosch Sensortec BNO055 core driver

Quoted 3 lines · On Thu, 17 Feb 2022 22:58:14 +0100 (CET) Peter Meerwald-Stadler…

On Thu, 17 Feb 2022, Andrea Merello wrote:

nice work, minor comments below

I'll review on top of Peter to save on duplication.

Mostly really minor stuff.

Given this has crossed with the introduction of namespaces to quite a few IIO drivers (I have another series to do on that once I get caught up with reviews) I'd prefer it if you would move this into a symbol namespace (EXPORT_SYMBOL_NS_GPL() and appropriate namespace statements in the two bus modules.

Save it being done as a follow up series. If you prefer not to then that's fine too as it'll be a trivial follow up patch.

The binary attribute question is one that might take a bit of investigation. I'd like to have pointed you in a particular direction for that but too many reviews outstanding to dig into it at the moment :(

Thanks,

Jonathan

Quoted 364 lines
Quoted 172 lines

This patch adds a core driver for the BNO055 IMU from Bosch. This IMU can be connected via both serial and I2C busses; separate patches will add support for them.

The driver supports "AMG" (Accelerometer, Magnetometer, Gyroscope) mode, that provides raw data from the said internal sensors, and a couple of "fusion" modes (i.e. the IMU also do calculations in order to provide euler angles, quaternions, linear acceleration and gravity measurements).

In fusion modes the AMG data is still available (with some calibration refinements done by the IMU), but certain settings such as low pass filters cut-off frequency and sensors ranges are fixed, while in AMG mode they can be customized; this is why AMG mode can still be interesting.

---
 drivers/iio/imu/Kconfig         |    1 +
 drivers/iio/imu/Makefile        |    1 +
 drivers/iio/imu/bno055/Kconfig  |    4 +
 drivers/iio/imu/bno055/Makefile |    3 +
 drivers/iio/imu/bno055/bno055.c | 1612 +++++++++++++++++++++++++++++++
 drivers/iio/imu/bno055/bno055.h |   12 +
 6 files changed, 1633 insertions(+)
 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
drivers/iio/imu/Kconfig +1 -0
index 001ca2c3ff95..f1d7d4b5e222 100644
@@ -52,6 +52,7 @@ config ADIS16480
 	  ADIS16485, ADIS16488 inertial sensors.
 
 source "drivers/iio/imu/bmi160/Kconfig"
+source "drivers/iio/imu/bno055/Kconfig"
 
 config FXOS8700
 	tristate
drivers/iio/imu/Makefile +1 -0
index c82748096c77..6eb612034722 100644
@@ -15,6 +15,7 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o
 obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o
 
 obj-y += bmi160/
+obj-y += bno055/
 
 obj-$(CONFIG_FXOS8700) += fxos8700_core.o
 obj-$(CONFIG_FXOS8700_I2C) += fxos8700_i2c.o
drivers/iio/imu/bno055/Kconfig new file +4 -0
new file mode 100644
index 000000000000..d0ab3221fba5
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config BOSCH_BNO055_IIO
+	tristate
drivers/iio/imu/bno055/Makefile new file +3 -0
new file mode 100644
index 000000000000..56cc4de60a7e
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_BOSCH_BNO055_IIO) += bno055.o
drivers/iio/imu/bno055/bno055.c new file +94 -0
new file mode 100644
index 000000000000..881d08277356
@@ -0,0 +1,1612 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * IIO driver for Bosch BNO055 IMU
+ *
+ * Copyright (C) 2021 Istituto Italiano di Tecnologia
+ * Electronic Design Laboratory
+ * Written by Andrea Merello <[email protected]>
+ *
+ * Portions of this driver are taken from the BNO055 driver patch
+ * from Vlad Dogaru which is Copyright (c) 2016, Intel Corporation.
+ *
+ * This driver is also based on BMI160 driver, which is:
+ *	Copyright (c) 2016, Intel Corporation.
+ *	Copyright (c) 2019, Martin Kelly.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/clk.h>
+#include <linux/bitfield.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/util_macros.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/sysfs.h>
+
+#include "bno055.h"
+
+#define BNO055_FW_NAME "bno055-caldata"
+#define BNO055_FW_EXT ".dat"
+#define BNO055_FW_UID_NAME BNO055_FW_NAME "-%*phN" BNO055_FW_EXT
+#define BNO055_FW_GENERIC_NAME (BNO055_FW_NAME BNO055_FW_EXT)
+
+/* common registers */
+#define BNO055_PAGESEL_REG		0x7
+
+/* page 0 registers */
+#define BNO055_CHIP_ID_REG		0x0
+#define BNO055_CHIP_ID_MAGIC 0xA0
+#define BNO055_SW_REV_LSB_REG		0x4
+#define BNO055_SW_REV_MSB_REG		0x5
+#define BNO055_ACC_DATA_X_LSB_REG	0x8
+#define BNO055_ACC_DATA_Y_LSB_REG	0xA
+#define BNO055_ACC_DATA_Z_LSB_REG	0xC
+#define BNO055_MAG_DATA_X_LSB_REG	0xE
+#define BNO055_MAG_DATA_Y_LSB_REG	0x10
+#define BNO055_MAG_DATA_Z_LSB_REG	0x12
+#define BNO055_GYR_DATA_X_LSB_REG	0x14
+#define BNO055_GYR_DATA_Y_LSB_REG	0x16
+#define BNO055_GYR_DATA_Z_LSB_REG	0x18
+#define BNO055_EUL_DATA_X_LSB_REG	0x1A
+#define BNO055_EUL_DATA_Y_LSB_REG	0x1C
+#define BNO055_EUL_DATA_Z_LSB_REG	0x1E
+#define BNO055_QUAT_DATA_W_LSB_REG	0x20
+#define BNO055_LIA_DATA_X_LSB_REG	0x28
+#define BNO055_LIA_DATA_Y_LSB_REG	0x2A
+#define BNO055_LIA_DATA_Z_LSB_REG	0x2C
+#define BNO055_GRAVITY_DATA_X_LSB_REG	0x2E
+#define BNO055_GRAVITY_DATA_Y_LSB_REG	0x30
+#define BNO055_GRAVITY_DATA_Z_LSB_REG	0x32
+#define BNO055_SCAN_CH_COUNT ((BNO055_GRAVITY_DATA_Z_LSB_REG - BNO055_ACC_DATA_X_LSB_REG) / 2)
+#define BNO055_TEMP_REG			0x34
+#define BNO055_CALIB_STAT_REG		0x35
+#define BNO055_CALIB_STAT_MASK GENMASK(1, 0)
+#define BNO055_CALIB_STAT_MAGN_SHIFT 0
+#define BNO055_CALIB_STAT_ACCEL_SHIFT 2
+#define BNO055_CALIB_STAT_GYRO_SHIFT 4
+#define BNO055_CALIB_STAT_SYS_SHIFT 6
+#define BNO055_SYS_ERR_REG		0x3A
+#define BNO055_POWER_MODE_REG		0x3E
+#define BNO055_POWER_MODE_NORMAL 0
+#define BNO055_SYS_TRIGGER_REG		0x3F
+#define BNO055_SYS_TRIGGER_RST_SYS BIT(5)
+#define BNO055_SYS_TRIGGER_CLK_SEL BIT(7)
+#define BNO055_OPR_MODE_REG		0x3D
+#define BNO055_OPR_MODE_CONFIG 0x0
+#define BNO055_OPR_MODE_AMG 0x7
+#define BNO055_OPR_MODE_FUSION_FMC_OFF 0xB
+#define BNO055_OPR_MODE_FUSION 0xC
+#define BNO055_UNIT_SEL_REG		0x3B
+/* Android orientation mode means: pitch value decreases turning clockwise */
+#define BNO055_UNIT_SEL_ANDROID BIT(7)
+#define BNO055_UNIT_SEL_GYR_RPS BIT(1)
+#define BNO055_CALDATA_START		0x55
+#define BNO055_CALDATA_END		0x6A
+#define BNO055_CALDATA_LEN 22

drivers/iio/imu/bno055/bno055.c line 104

Quoted 10 lines
drivers/iio/imu/bno055/bno055.c new file +10 -0
+
+/*
+ * The difference in address between the register that contains the
+ * value and the register that contains the offset.  This applies for
+ * accel, gyro and magn channels.
+ */
+#define BNO055_REG_OFFSET_ADDR		0x4D
+
+/* page 1 registers */
+#define PG1(x) ((x) | 0x80)  

BNO055_ prefix missing

Quoted 41 lines
drivers/iio/imu/bno055/bno055.c new file +41 -0
+#define BNO055_ACC_CONFIG_REG		PG1(0x8)
+#define BNO055_ACC_CONFIG_LPF_MASK GENMASK(4, 2)
+#define BNO055_ACC_CONFIG_RANGE_MASK GENMASK(1, 0)
+#define BNO055_MAG_CONFIG_REG		PG1(0x9)
+#define BNO055_MAG_CONFIG_HIGHACCURACY 0x18
+#define BNO055_MAG_CONFIG_ODR_MASK GENMASK(2, 0)
+#define BNO055_GYR_CONFIG_REG		PG1(0xA)
+#define BNO055_GYR_CONFIG_RANGE_MASK GENMASK(2, 0)
+#define BNO055_GYR_CONFIG_LPF_MASK GENMASK(5, 3)
+#define BNO055_GYR_AM_SET_REG		PG1(0x1F)
+#define BNO055_UID_LOWER_REG		PG1(0x50)
+#define BNO055_UID_HIGHER_REG		PG1(0x5F)
+#define BNO055_UID_LEN 16
+
+struct bno055_sysfs_attr {
+	int *vals;
+	int len;
+	int *fusion_vals;
+	int *hw_xlate;
+	int type;
+};
+
+#define BNO055_ATTR_VALS(...)		\
+	.vals = (int[]){ __VA_ARGS__},	\
+	.len = ARRAY_SIZE(((int[]){__VA_ARGS__}))
+
+static struct bno055_sysfs_attr bno055_acc_lpf = {
+	BNO055_ATTR_VALS(7, 810000, 15, 630000,
+			 31, 250000, 62, 500000, 125, 0,
+			 250, 0, 500, 0, 1000, 0),
+	.fusion_vals = (int[]){62, 500000},
+	.type = IIO_VAL_INT_PLUS_MICRO
+};
+
+static struct bno055_sysfs_attr bno055_acc_range = {
+		     /* G:   2,    4,    8,    16 */
+	BNO055_ATTR_VALS(1962, 3924, 7848, 15696),
+	.fusion_vals = (int[]){3924}, /* 4G */
+	.type = IIO_VAL_INT
+};
+

drivers/iio/imu/bno055/bno055.c line 155

Quoted 10 lines
drivers/iio/imu/bno055/bno055.c new file +10 -0
+/*
+ * Theoretically the IMU should return data in a given (i.e. fixed) unit
+ * regardless the range setting. This happens for the accelerometer, but not for
+ * the gyroscope; the gyroscope range setting affects the scale.
+ * This is probably due to this[0] bug.
+ * For this reason we map the internal range setting onto the standard IIO scale
+ * attribute for gyro.
+ * Since the bug[0] may be fixed in future, we check for the IMU FW version and
+ * eventually warn the user.
+ * Currently we just't don't care about "range" attributes for gyro.  

typo: just't

Quoted 52 lines
drivers/iio/imu/bno055/bno055.c new file +52 -0
+ *
+ * [0]  https://community.bosch-sensortec.com/t5/MEMS-sensors-forum/BNO055-Wrong-sensitivity-resolution-in-datasheet/td-p/10266
+ */
+static struct bno055_sysfs_attr bno055_gyr_scale = {
+	/*
+	 * dps = hwval * (dps_range/2^15)
+	 * rps = hwval * (rps_range/2^15)
+	 *     = hwval * (dps_range/(2^15 * k))
+	 * where k is rad-to-deg factor
+	 */
+	BNO055_ATTR_VALS(125, 1877467, 250, 1877467,
+			 500, 1877467, 1000, 1877467,
+			 2000, 1877467),
+	.fusion_vals = (int[]){1, 900},
+	.hw_xlate = (int[]){4, 3, 2, 1, 0},
+	.type = IIO_VAL_FRACTIONAL
+};
+
+static struct bno055_sysfs_attr bno055_gyr_lpf = {
+	BNO055_ATTR_VALS(12, 23, 32, 47, 64, 116, 230, 523),
+	.fusion_vals = (int[]){32},
+	.hw_xlate = (int[]){5, 4, 7, 3, 6, 2, 1, 0},
+	.type = IIO_VAL_INT
+};
+
+static struct bno055_sysfs_attr bno055_mag_odr = {
+	BNO055_ATTR_VALS(2, 6, 8, 10, 15, 20, 25, 30),
+	.fusion_vals = (int[]){20},
+	.type = IIO_VAL_INT
+};
+
+struct bno055_priv {
+	struct regmap *regmap;
+	struct device *dev;
+	struct clk *clk;
+	int operation_mode;
+	int xfer_burst_break_thr;
+	struct mutex lock;
+	u8 uid[BNO055_UID_LEN];
+	struct gpio_desc *reset_gpio;
+	bool sw_reset;
+	struct {
+		__le16 chans[BNO055_SCAN_CH_COUNT];
+		s64 timestamp __aligned(8);
+	} buf;
+};
+
+static bool bno055_regmap_volatile(struct device *dev, unsigned int reg)
+{
+	/* data and status registers */
+	if (reg >= BNO055_ACC_DATA_X_LSB_REG && reg <= BNO055_SYS_ERR_REG)
+		return true;

drivers/iio/imu/bno055/bno055.c line 217

Quoted 10 lines
drivers/iio/imu/bno055/bno055.c new file +10 -0
+
+	/* when in fusion mode, config is updated by chip */
+	if (reg == BNO055_MAG_CONFIG_REG ||
+	    reg == BNO055_ACC_CONFIG_REG ||
+	    reg == BNO055_GYR_CONFIG_REG)
+		return true;
+
+	/* calibration data may be updated by the IMU */
+	if (reg >= BNO055_CALDATA_START && reg <= BNO055_CALDATA_END)
+		return true;  

I'd suggest a newline here (similarly elsewhere)

Quoted 23 lines
drivers/iio/imu/bno055/bno055.c new file +23 -0
+	return false;
+}
+
+static bool bno055_regmap_readable(struct device *dev, unsigned int reg)
+{
+	/* unnamed PG0 reserved areas */
+	if ((reg < PG1(0) && reg > BNO055_CALDATA_END) ||
+	    reg == 0x3C)
+		return false;
+
+	/* unnamed PG1 reserved areas */
+	if (reg > PG1(BNO055_UID_HIGHER_REG) ||
+	    (reg < PG1(BNO055_UID_LOWER_REG) && reg > PG1(BNO055_GYR_AM_SET_REG)) ||
+	    reg == PG1(0xE) ||
+	    (reg < PG1(BNO055_PAGESEL_REG) && reg >= PG1(0x0)))
+		return false;
+	return true;
+}
+
+static bool bno055_regmap_writeable(struct device *dev, unsigned int reg)
+{
+	/*
+	 * Unreadable registers are indeed reserved; there are no WO regs

drivers/iio/imu/bno055/bno055.c line 250

Quoted 10 lines
drivers/iio/imu/bno055/bno055.c new file +10 -0
+	 * (except for a single bit in SYS_TRIGGER register)
+	 */
+	if (!bno055_regmap_readable(dev, reg))
+		return false;
+
+	/* data and status registers */
+	if (reg >= BNO055_ACC_DATA_X_LSB_REG && reg <= BNO055_SYS_ERR_REG)
+		return false;
+
+	/* IDs areas */  

probably 'ID area'?

Quoted 24 lines
drivers/iio/imu/bno055/bno055.c new file +24 -0
+	if (reg < BNO055_PAGESEL_REG ||
+	    (reg <= BNO055_UID_HIGHER_REG && reg >= BNO055_UID_LOWER_REG))
+		return false;
+
+	return true;
+}
+
+static const struct regmap_range_cfg bno055_regmap_ranges[] = {
+	{
+		.range_min = 0,
+		.range_max = 0x7f * 2,
+		.selector_reg = BNO055_PAGESEL_REG,
+		.selector_mask = GENMASK(7, 0),
+		.selector_shift = 0,
+		.window_start = 0,
+		.window_len = 0x80
+	},
+};
+
+const struct regmap_config bno055_regmap_config = {
+	.name = "bno055",
+	.reg_bits = 8,
+	.val_bits = 8,
+	.ranges = bno055_regmap_ranges,
Quoted 8 lines

drivers/iio/imu/bno055/bno055.c line 282

Quoted 8 lines
drivers/iio/imu/bno055/bno055.c new file +8 -0
+	.num_ranges = 1,
+	.volatile_reg = bno055_regmap_volatile,
+	.max_register = 0x80 * 2,
+	.writeable_reg = bno055_regmap_writeable,
+	.readable_reg = bno055_regmap_readable,
+	.cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(bno055_regmap_config);

Given I'll otherwise end up moving this to a namespace as a follow up patch, would you mind doing so now. So use EXPORT_SYMBOL_NS_GPL() etc and use IIO_BNO055 as the namespace.

Quoted 582 lines

drivers/iio/imu/bno055/bno055.c line 288

Quoted 6 lines
drivers/iio/imu/bno055/bno055.c new file +6 -0
+
+/* must be called in configuration mode */
+int bno055_calibration_load(struct bno055_priv *priv, const u8 *data, int len)
+{
+	if (len != BNO055_CALDATA_LEN) {
+		dev_dbg(priv->dev, "Invalid calibration file size %zu (expected %d)",  

why %zu? len is int

Quoted 87 lines
drivers/iio/imu/bno055/bno055.c new file +87 -0
+			len, BNO055_CALDATA_LEN);
+		return -EINVAL;
+	}
+
+	dev_dbg(priv->dev, "loading cal data: %*ph", BNO055_CALDATA_LEN, data);
+	return regmap_bulk_write(priv->regmap, BNO055_CALDATA_START,
+				 data, BNO055_CALDATA_LEN);
+}
+
+static int bno055_operation_mode_do_set(struct bno055_priv *priv,
+					int operation_mode)
+{
+	int ret;
+
+	ret = regmap_write(priv->regmap, BNO055_OPR_MODE_REG,
+			   operation_mode);
+	if (ret)
+		return ret;
+
+	msleep(20);
+
+	return 0;
+}
+
+static int bno055_system_reset(struct bno055_priv *priv)
+{
+	int ret;
+
+	if (priv->reset_gpio) {
+		gpiod_set_value_cansleep(priv->reset_gpio, 0);
+		usleep_range(5000, 10000);
+		gpiod_set_value_cansleep(priv->reset_gpio, 1);
+	} else {
+		if (!priv->sw_reset)
+			return 0;
+
+		ret = regmap_write(priv->regmap, BNO055_SYS_TRIGGER_REG,
+				   BNO055_SYS_TRIGGER_RST_SYS);
+		if (ret)
+			return ret;
+	}
+
+	regcache_drop_region(priv->regmap, 0x0, 0xff);
+	usleep_range(650000, 700000);
+
+	return 0;
+}
+
+static int bno055_init(struct bno055_priv *priv, const u8 *caldata, int len)
+{
+	int ret;
+
+	ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(priv->regmap, BNO055_POWER_MODE_REG,
+			   BNO055_POWER_MODE_NORMAL);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(priv->regmap, BNO055_SYS_TRIGGER_REG,
+			   priv->clk ? BNO055_SYS_TRIGGER_CLK_SEL : 0);
+	if (ret)
+		return ret;
+
+	/* use standard SI units */
+	ret = regmap_write(priv->regmap, BNO055_UNIT_SEL_REG,
+			   BNO055_UNIT_SEL_ANDROID | BNO055_UNIT_SEL_GYR_RPS);
+	if (ret)
+		return ret;
+
+	if (caldata) {
+		ret = bno055_calibration_load(priv, caldata, len);
+		if (ret)
+			dev_warn(priv->dev, "failed to load calibration data with error %d",
+				 ret);
+	}
+
+	return 0;
+}
+
+static ssize_t bno055_operation_mode_set(struct bno055_priv *priv,
+					 int operation_mode)
+{
+	u8 caldata[BNO055_CALDATA_LEN];
+	int ret;

drivers/iio/imu/bno055/bno055.c line 385

Quoted 10 lines
drivers/iio/imu/bno055/bno055.c new file +10 -0
+
+	mutex_lock(&priv->lock);
+
+	ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+	if (ret)
+		goto exit;
+
+	if (operation_mode == BNO055_OPR_MODE_FUSION ||
+	    operation_mode == BNO055_OPR_MODE_FUSION_FMC_OFF) {
+		/* for entering fusiod mode, reset the chip to clear the algo state */  

typo: fusiod

drivers/iio/imu/bno055/bno055.c line 393

Quoted 8 lines
drivers/iio/imu/bno055/bno055.c new file +8 -0
+		ret = regmap_bulk_read(priv->regmap, BNO055_CALDATA_START, caldata,
+				       BNO055_CALDATA_LEN);
+		if (ret)
+			goto exit;
+
+		ret = bno055_system_reset(priv);
+		if (ret)
+			return ret;  

goto exit;
here as well?

drivers/iio/imu/bno055/bno055.c line 404

Quoted 11 lines
drivers/iio/imu/bno055/bno055.c new file +11 -0
+
+		ret = bno055_init(priv, caldata, BNO055_CALDATA_LEN);
+		if (ret)
+			goto exit;
+	}
+
+	ret = bno055_operation_mode_do_set(priv, operation_mode);
+	if (ret)
+		goto exit;
+
+	priv->operation_mode = operation_mode;  

suggest a newline

Quoted 447 lines
drivers/iio/imu/bno055/bno055.c new file +447 -0
+exit:
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static void bno055_uninit(void *arg)
+{
+	struct bno055_priv *priv = arg;
+
+	/* stop the IMU */
+	bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+}
+
+#define BNO055_CHANNEL(_type, _axis, _index, _address, _sep, _sh, _avail) {	\
+	.address = _address,							\
+	.type = _type,								\
+	.modified = 1,								\
+	.channel2 = IIO_MOD_##_axis,						\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | (_sep),			\
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | (_sh),		\
+	.info_mask_shared_by_type_available = _avail,				\
+	.scan_index = _index,							\
+	.scan_type = {								\
+		.sign = 's',							\
+		.realbits = 16,							\
+		.storagebits = 16,						\
+		.endianness = IIO_LE,						\
+		.repeat = IIO_MOD_##_axis == IIO_MOD_QUATERNION ? 4 : 0		\
+	},									\
+}
+
+/* scan indexes follow DATA register order */
+enum bmi160_scan_axis {
+	BNO055_SCAN_ACCEL_X,
+	BNO055_SCAN_ACCEL_Y,
+	BNO055_SCAN_ACCEL_Z,
+	BNO055_SCAN_MAGN_X,
+	BNO055_SCAN_MAGN_Y,
+	BNO055_SCAN_MAGN_Z,
+	BNO055_SCAN_GYRO_X,
+	BNO055_SCAN_GYRO_Y,
+	BNO055_SCAN_GYRO_Z,
+	BNO055_SCAN_YAW,
+	BNO055_SCAN_ROLL,
+	BNO055_SCAN_PITCH,
+	BNO055_SCAN_QUATERNION,
+	BNO055_SCAN_LIA_X,
+	BNO055_SCAN_LIA_Y,
+	BNO055_SCAN_LIA_Z,
+	BNO055_SCAN_GRAVITY_X,
+	BNO055_SCAN_GRAVITY_Y,
+	BNO055_SCAN_GRAVITY_Z,
+	BNO055_SCAN_TIMESTAMP,
+};
+
+static const struct iio_chan_spec bno055_channels[] = {
+	/* accelerometer */
+	BNO055_CHANNEL(IIO_ACCEL, X, BNO055_SCAN_ACCEL_X,
+		       BNO055_ACC_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)),
+	BNO055_CHANNEL(IIO_ACCEL, Y, BNO055_SCAN_ACCEL_Y,
+		       BNO055_ACC_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)),
+	BNO055_CHANNEL(IIO_ACCEL, Z, BNO055_SCAN_ACCEL_Z,
+		       BNO055_ACC_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)),
+	/* gyroscope */
+	BNO055_CHANNEL(IIO_ANGL_VEL, X, BNO055_SCAN_GYRO_X,
+		       BNO055_GYR_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
+		       BIT(IIO_CHAN_INFO_SCALE)),
+	BNO055_CHANNEL(IIO_ANGL_VEL, Y, BNO055_SCAN_GYRO_Y,
+		       BNO055_GYR_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
+		       BIT(IIO_CHAN_INFO_SCALE)),
+	BNO055_CHANNEL(IIO_ANGL_VEL, Z, BNO055_SCAN_GYRO_Z,
+		       BNO055_GYR_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
+		       BIT(IIO_CHAN_INFO_SCALE)),
+	/* magnetometer */
+	BNO055_CHANNEL(IIO_MAGN, X, BNO055_SCAN_MAGN_X,
+		       BNO055_MAG_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)),
+	BNO055_CHANNEL(IIO_MAGN, Y, BNO055_SCAN_MAGN_Y,
+		       BNO055_MAG_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)),
+	BNO055_CHANNEL(IIO_MAGN, Z, BNO055_SCAN_MAGN_Z,
+		       BNO055_MAG_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)),
+	/* euler angle */
+	BNO055_CHANNEL(IIO_ROT, YAW, BNO055_SCAN_YAW,
+		       BNO055_EUL_DATA_X_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_ROT, ROLL, BNO055_SCAN_ROLL,
+		       BNO055_EUL_DATA_Y_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_ROT, PITCH, BNO055_SCAN_PITCH,
+		       BNO055_EUL_DATA_Z_LSB_REG, 0, 0, 0),
+	/* quaternion */
+	BNO055_CHANNEL(IIO_ROT, QUATERNION, BNO055_SCAN_QUATERNION,
+		       BNO055_QUAT_DATA_W_LSB_REG, 0, 0, 0),
+
+	/* linear acceleration */
+	BNO055_CHANNEL(IIO_ACCEL, LINEAR_X, BNO055_SCAN_LIA_X,
+		       BNO055_LIA_DATA_X_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_ACCEL, LINEAR_Y, BNO055_SCAN_LIA_Y,
+		       BNO055_LIA_DATA_Y_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_ACCEL, LINEAR_Z, BNO055_SCAN_LIA_Z,
+		       BNO055_LIA_DATA_Z_LSB_REG, 0, 0, 0),
+
+	/* gravity vector */
+	BNO055_CHANNEL(IIO_GRAVITY, X, BNO055_SCAN_GRAVITY_X,
+		       BNO055_GRAVITY_DATA_X_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_GRAVITY, Y, BNO055_SCAN_GRAVITY_Y,
+		       BNO055_GRAVITY_DATA_Y_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_GRAVITY, Z, BNO055_SCAN_GRAVITY_Z,
+		       BNO055_GRAVITY_DATA_Z_LSB_REG, 0, 0, 0),
+
+	{
+		.type = IIO_TEMP,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+		.scan_index = -1
+	},
+	IIO_CHAN_SOFT_TIMESTAMP(BNO055_SCAN_TIMESTAMP),
+};
+
+static int bno055_get_regmask(struct bno055_priv *priv, int *val, int *val2,
+			      int reg, int mask, struct bno055_sysfs_attr *attr)
+{
+	const int shift = __ffs(mask);
+	int hwval, idx;
+	int ret;
+	int i;
+
+	ret = regmap_read(priv->regmap, reg, &hwval);
+	if (ret)
+		return ret;
+
+	idx = (hwval & mask) >> shift;
+	if (attr->hw_xlate)
+		for (i = 0; i < attr->len; i++)
+			if (attr->hw_xlate[i] == idx) {
+				idx = i;
+				break;
+			}
+	if (attr->type == IIO_VAL_INT) {
+		*val = attr->vals[idx];
+	} else { /* IIO_VAL_INT_PLUS_MICRO or IIO_VAL_FRACTIONAL */
+		*val = attr->vals[idx * 2];
+		*val2 = attr->vals[idx * 2 + 1];
+	}
+
+	return attr->type;
+}
+
+static int bno055_set_regmask(struct bno055_priv *priv, int val, int val2,
+			      int reg, int mask, struct bno055_sysfs_attr *attr)
+{
+	const int shift = __ffs(mask);
+	int best_delta;
+	int req_val;
+	int tbl_val;
+	bool first;
+	int delta;
+	int hwval;
+	int ret;
+	int len;
+	int i;
+
+	/*
+	 * The closest value the HW supports is only one in fusion mode,
+	 * and it is autoselected, so don't do anything, just return OK,
+	 * as the closest possible value has been (virtually) selected
+	 */
+	if (priv->operation_mode != BNO055_OPR_MODE_AMG)
+		return 0;
+
+	len = attr->len;
+
+	/*
+	 * We always get a request in INT_PLUS_MICRO, but we
+	 * take care of the micro part only when we really have
+	 * non-integer tables. This prevents 32-bit overflow with
+	 * larger integers contained in integer tables.
+	 */
+	req_val = val;
+	if (attr->type != IIO_VAL_INT) {
+		if (val > 2147)
+			val = 2147;
+		len /= 2;
+		req_val = val * 1000000 + val2;
+	}
+
+	first = true;
+	for (i = 0; i < len; i++) {
+		switch (attr->type) {
+		case IIO_VAL_INT:
+			tbl_val = attr->vals[i];
+			break;
+		case IIO_VAL_INT_PLUS_MICRO:
+			WARN_ON(attr->vals[i * 2] > 2147);
+			tbl_val = attr->vals[i * 2] * 1000000 +
+				attr->vals[i * 2 + 1];
+			break;
+		case IIO_VAL_FRACTIONAL:
+			WARN_ON(attr->vals[i * 2] > 4294);
+			tbl_val = attr->vals[i * 2] * 1000000 /
+				attr->vals[i * 2 + 1];
+			break;
+		default:
+			return -EINVAL;
+		}
+		delta = abs(tbl_val - req_val);
+		if (first || delta < best_delta) {
+			best_delta = delta;
+			hwval = i;
+			first = false;
+		}
+	}
+
+	if (attr->hw_xlate)
+		hwval = attr->hw_xlate[hwval];
+
+	ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(priv->regmap, reg, mask, hwval << shift);
+	if (ret)
+		return ret;
+
+	return bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_AMG);
+}
+
+static int bno055_read_simple_chan(struct iio_dev *indio_dev,
+				   struct iio_chan_spec const *chan,
+				   int *val, int *val2, long mask)
+{
+	struct bno055_priv *priv = iio_priv(indio_dev);
+	__le16 raw_val;
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = regmap_bulk_read(priv->regmap, chan->address,
+				       &raw_val, sizeof(raw_val));
+		if (ret < 0)
+			return ret;
+		*val = (s16)le16_to_cpu(raw_val);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_OFFSET:
+		if (priv->operation_mode != BNO055_OPR_MODE_AMG) {
+			*val = 0;
+		} else {
+			ret = regmap_bulk_read(priv->regmap,
+					       chan->address +
+					       BNO055_REG_OFFSET_ADDR,
+					       &raw_val, sizeof(raw_val));
+			if (ret < 0)
+				return ret;
+			/*
+			 * IMU reports sensor offests; IIO wants correction
+			 * offset, thus we need the 'minus' here.
+			 */
+			*val = -(s16)le16_to_cpu(raw_val);
+		}
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 1;
+		switch (chan->type) {
+		case IIO_GRAVITY:
+			/* Table 3-35: 1 m/s^2 = 100 LSB */
+		case IIO_ACCEL:
+			/* Table 3-17: 1 m/s^2 = 100 LSB */
+			*val2 = 100;
+			break;
+		case IIO_MAGN:
+			/*
+			 * Table 3-19: 1 uT = 16 LSB.  But we need
+			 * Gauss: 1G = 0.1 uT.
+			 */
+			*val2 = 160;
+			break;
+		case IIO_ANGL_VEL:
+			/*
+			 * Table 3-22: 1 Rps = 900 LSB
+			 * .. but this is not exactly true. See comment at the
+			 * beginning of this file.
+			 */
+			if (priv->operation_mode != BNO055_OPR_MODE_AMG) {
+				*val = bno055_gyr_scale.fusion_vals[0];
+				*val2 = bno055_gyr_scale.fusion_vals[1];
+				return IIO_VAL_FRACTIONAL;
+			}
+
+			return bno055_get_regmask(priv, val, val2,
+						  BNO055_GYR_CONFIG_REG,
+						  BNO055_GYR_CONFIG_RANGE_MASK,
+						  &bno055_gyr_scale);
+			break;
+		case IIO_ROT:
+			/* Table 3-28: 1 degree = 16 LSB */
+			*val2 = 16;
+			break;
+		default:
+			return -EINVAL;
+		}
+		return IIO_VAL_FRACTIONAL;
+
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		if (chan->type != IIO_MAGN)
+			return -EINVAL;
+		else
+			return bno055_get_regmask(priv, val, val2,
+						  BNO055_MAG_CONFIG_REG,
+						  BNO055_MAG_CONFIG_ODR_MASK,
+						  &bno055_mag_odr);
+
+	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			return bno055_get_regmask(priv, val, val2,
+						  BNO055_GYR_CONFIG_REG,
+						  BNO055_GYR_CONFIG_LPF_MASK,
+						  &bno055_gyr_lpf);
+		case IIO_ACCEL:
+			return bno055_get_regmask(priv, val, val2,
+						  BNO055_ACC_CONFIG_REG,
+						  BNO055_ACC_CONFIG_LPF_MASK,
+						  &bno055_acc_lpf);
+		default:
+			return -EINVAL;
+		}
+
+	default:
+		return -EINVAL;
+	}
+}
+
+int bno055_sysfs_attr_avail(struct bno055_priv *priv, struct bno055_sysfs_attr *attr,
+			    const int **vals, int *length)
+{
+	if (priv->operation_mode != BNO055_OPR_MODE_AMG) {
+		/* locked when fusion enabled */
+		*vals = attr->fusion_vals;
+		if (attr->type == IIO_VAL_INT)
+			*length = 1;
+		else
+			*length = 2; /* IIO_VAL_INT_PLUS_MICRO or IIO_VAL_FRACTIONAL*/
+	} else {
+		*vals = attr->vals;
+		*length = attr->len;
+	}
+
+	return attr->type;
+}
+
+static int bno055_read_avail(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     const int **vals, int *type, int *length,
+			     long mask)
+{
+	struct bno055_priv *priv = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			*type = bno055_sysfs_attr_avail(priv, &bno055_gyr_scale,
+							vals, length);
+			return IIO_AVAIL_LIST;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			*type = bno055_sysfs_attr_avail(priv, &bno055_gyr_lpf,
+							vals, length);
+			return IIO_AVAIL_LIST;
+		case IIO_ACCEL:
+			*type = bno055_sysfs_attr_avail(priv, &bno055_acc_lpf,
+							vals, length);
+			return IIO_AVAIL_LIST;
+		default:
+			return -EINVAL;
+		}
+
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		switch (chan->type) {
+		case IIO_MAGN:
+			*type = bno055_sysfs_attr_avail(priv, &bno055_mag_odr,
+							vals, length);
+			return IIO_AVAIL_LIST;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int bno055_read_temp_chan(struct iio_dev *indio_dev, int *val)
+{
+	struct bno055_priv *priv = iio_priv(indio_dev);
+	unsigned int raw_val;
+	int ret;
+
+	ret = regmap_read(priv->regmap, BNO055_TEMP_REG, &raw_val);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Tables 3-36 and 3-37: one byte of priv, signed, 1 LSB = 1C.
+	 * ABI wants milliC.
+	 */
+	*val = raw_val * 1000;
+
+	return IIO_VAL_INT;
+}
+
+static int bno055_read_quaternion(struct iio_dev *indio_dev,
+				  struct iio_chan_spec const *chan,
+				  int size, int *vals, int *val_len,
+				  long mask)
+{
+	struct bno055_priv *priv = iio_priv(indio_dev);
+	__le16 raw_vals[4];
+	int i, ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		if (size < 4)
+			return -EINVAL;
+		ret = regmap_bulk_read(priv->regmap,
+				       BNO055_QUAT_DATA_W_LSB_REG,
+				       raw_vals, sizeof(raw_vals));
+		if (ret < 0)
+			return ret;
+		for (i = 0; i < 4; i++)
+			vals[i] = (s16)le16_to_cpu(raw_vals[i]);
+		*val_len = 4;
Quoted 8 lines

drivers/iio/imu/bno055/bno055.c line 859

Quoted 8 lines
drivers/iio/imu/bno055/bno055.c new file +8 -0
+		return IIO_VAL_INT_MULTIPLE;
+	case IIO_CHAN_INFO_SCALE:
+		/* Table 3-31: 1 quaternion = 2^14 LSB */
+		if (size < 2)
+			return -EINVAL;
+		vals[0] = 1;
+		vals[1] = 1 << 14;
+		return IIO_VAL_FRACTIONAL_LOG2;

This doesn't look right. Not vals[1] = 14 given FRACTIONAL_LOG2?

Quoted 421 lines
Quoted 151 lines
drivers/iio/imu/bno055/bno055.c new file +151 -0
+	default:
+		return -EINVAL;
+	}
+}
+
+static int _bno055_read_raw_multi(struct iio_dev *indio_dev,
+				  struct iio_chan_spec const *chan,
+				  int size, int *vals, int *val_len,
+				  long mask)
+{
+	switch (chan->type) {
+	case IIO_MAGN:
+	case IIO_ACCEL:
+	case IIO_ANGL_VEL:
+	case IIO_GRAVITY:
+		if (size < 2)
+			return -EINVAL;
+		*val_len = 2;
+		return bno055_read_simple_chan(indio_dev, chan,
+					       &vals[0], &vals[1],
+					       mask);
+	case IIO_TEMP:
+		*val_len = 1;
+		return bno055_read_temp_chan(indio_dev, &vals[0]);
+	case IIO_ROT:
+		/*
+		 * Rotation is exposed as either a quaternion or three
+		 * Euler angles.
+		 */
+		if (chan->channel2 == IIO_MOD_QUATERNION)
+			return bno055_read_quaternion(indio_dev, chan,
+						      size, vals,
+						      val_len, mask);
+		if (size < 2)
+			return -EINVAL;
+		*val_len = 2;
+		return bno055_read_simple_chan(indio_dev, chan,
+					       &vals[0], &vals[1],
+					       mask);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int bno055_read_raw_multi(struct iio_dev *indio_dev,
+				 struct iio_chan_spec const *chan,
+				 int size, int *vals, int *val_len,
+				 long mask)
+{
+	struct bno055_priv *priv = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&priv->lock);
+	ret = _bno055_read_raw_multi(indio_dev, chan, size,
+				     vals, val_len, mask);
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static int _bno055_write_raw(struct iio_dev *iio_dev,
+			     struct iio_chan_spec const *chan,
+			     int val, int val2, long mask)
+{
+	struct bno055_priv *priv = iio_priv(iio_dev);
+
+	switch (chan->type) {
+	case IIO_MAGN:
+		switch (mask) {
+		case IIO_CHAN_INFO_SAMP_FREQ:
+			return bno055_set_regmask(priv, val, val2,
+						  BNO055_MAG_CONFIG_REG,
+						  BNO055_MAG_CONFIG_ODR_MASK,
+						  &bno055_mag_odr);
+		default:
+			return -EINVAL;
+		}
+	case IIO_ACCEL:
+		switch (mask) {
+		case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+			return bno055_set_regmask(priv, val, val2,
+						  BNO055_ACC_CONFIG_REG,
+						  BNO055_ACC_CONFIG_LPF_MASK,
+						  &bno055_acc_lpf);
+
+		default:
+			return -EINVAL;
+		}
+	case IIO_ANGL_VEL:
+		switch (mask) {
+		case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+			return bno055_set_regmask(priv, val, val2,
+						  BNO055_GYR_CONFIG_REG,
+						  BNO055_GYR_CONFIG_LPF_MASK,
+						  &bno055_gyr_lpf);
+		case IIO_CHAN_INFO_SCALE:
+			return bno055_set_regmask(priv, val, val2,
+						  BNO055_GYR_CONFIG_REG,
+						  BNO055_GYR_CONFIG_RANGE_MASK,
+						  &bno055_gyr_scale);
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int bno055_write_raw(struct iio_dev *iio_dev,
+			    struct iio_chan_spec const *chan,
+			    int val, int val2, long mask)
+{
+	struct bno055_priv *priv = iio_priv(iio_dev);
+	int ret;
+
+	mutex_lock(&priv->lock);
+	ret = _bno055_write_raw(iio_dev, chan, val, val2, mask);
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+static ssize_t in_accel_range_raw_available_show(struct device *dev,
+						 struct device_attribute *attr,
+						 char *buf)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int len = 0;
+	int i;
+
+	if (priv->operation_mode != BNO055_OPR_MODE_AMG)
+		return sysfs_emit(buf, "%d\n", bno055_acc_range.fusion_vals[0]);
+
+	for (i = 0; i < bno055_acc_range.len; i++)
+		len += sysfs_emit_at(buf, len, "%d%c", bno055_acc_range.vals[i],
+				     (i == bno055_acc_range.len - 1) ? '\n' : ' ');
+	return len;
+}
+
+static ssize_t bno055_fusion_enable_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+
+	return sysfs_emit(buf, "%d\n",
+			  priv->operation_mode != BNO055_OPR_MODE_AMG);
+}
+
+static ssize_t bno055_fusion_enable_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t len)

drivers/iio/imu/bno055/bno055.c line 1020

Quoted 10 lines
drivers/iio/imu/bno055/bno055.c new file +10 -0
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int ret = 0;
+
+	if (sysfs_streq(buf, "0")) {
+		ret = bno055_operation_mode_set(priv, BNO055_OPR_MODE_AMG);
+	} else {
+		/*
+		 * Coming from AMG means the FMC was off, just switch to fusion
+		 * but don't change anything that doesn't belong to us (i.e let.  

typo: no . at the end

Quoted 257 lines
drivers/iio/imu/bno055/bno055.c new file +257 -0
+		 * FMC stay off.
+		 * Coming from any other fusion mode means we don't need to do
+		 * anything.
+		 */
+		if (priv->operation_mode == BNO055_OPR_MODE_AMG)
+			ret = bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION_FMC_OFF);
+	}
+	if (ret)
+		return ret;
+	return len;
+}
+
+static ssize_t bno055_fmc_enable_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+
+	return sysfs_emit(buf, "%d\n",
+			  priv->operation_mode == BNO055_OPR_MODE_FUSION);
+}
+
+static ssize_t bno055_fmc_enable_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t len)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int ret;
+
+	if (sysfs_streq(buf, "0")) {
+		if (priv->operation_mode == BNO055_OPR_MODE_FUSION) {
+			ret = bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION_FMC_OFF);
+			if (ret)
+				return ret;
+		}
+	} else {
+		if (priv->operation_mode == BNO055_OPR_MODE_AMG)
+			return -EINVAL;
+
+		if (priv->operation_mode != BNO055_OPR_MODE_FUSION) {
+			ret = bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return len;
+}
+
+static ssize_t bno055_in_accel_range_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int val;
+	int ret;
+
+	ret = bno055_get_regmask(priv, &val, NULL,
+				 BNO055_ACC_CONFIG_REG,
+				 BNO055_ACC_CONFIG_RANGE_MASK,
+				 &bno055_acc_range);
+	if (ret < 0)
+		return ret;
+
+	return sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t bno055_in_accel_range_store(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf, size_t len)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	unsigned long val;
+	int ret;
+
+	ret = kstrtoul(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&priv->lock);
+	ret = bno055_set_regmask(priv, val, 0,
+				 BNO055_ACC_CONFIG_REG,
+				 BNO055_ACC_CONFIG_RANGE_MASK,
+				 &bno055_acc_range);
+	mutex_unlock(&priv->lock);
+
+	return ret ?: len;
+}
+
+static ssize_t bno055_get_calib_status(struct device *dev, char *buf, int which)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int calib;
+	int ret;
+	int val;
+
+	if (priv->operation_mode == BNO055_OPR_MODE_AMG ||
+	    (priv->operation_mode == BNO055_OPR_MODE_FUSION_FMC_OFF &&
+	     which == BNO055_CALIB_STAT_MAGN_SHIFT)) {
+		calib = 0;
+	} else {
+		mutex_lock(&priv->lock);
+		ret = regmap_read(priv->regmap, BNO055_CALIB_STAT_REG, &val);
+		mutex_unlock(&priv->lock);
+
+		if (ret)
+			return -EIO;
+
+		calib = ((val >> which) & BNO055_CALIB_STAT_MASK) + 1;
+	}
+
+	return sysfs_emit(buf, "%d\n", calib);
+}
+
+static ssize_t serial_number_show(struct device *dev,
+				  struct device_attribute *attr,
+				  char *buf)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+
+	return sysfs_emit(buf, "%*ph\n", BNO055_UID_LEN, priv->uid);
+}
+
+static ssize_t calibration_data_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	u8 data[BNO055_CALDATA_LEN];
+	int ret;
+
+	mutex_lock(&priv->lock);
+	ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+	if (ret)
+		goto unlock;
+
+	ret = regmap_bulk_read(priv->regmap, BNO055_CALDATA_START, data,
+			       BNO055_CALDATA_LEN);
+	if (ret)
+		goto unlock;
+
+	ret = bno055_operation_mode_do_set(priv, priv->operation_mode);
+	if (ret)
+		goto unlock;
+
+	memcpy(buf, data, BNO055_CALDATA_LEN);
+
+	ret = BNO055_CALDATA_LEN;
+unlock:
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static ssize_t sys_calibration_auto_status_show(struct device *dev,
+						struct device_attribute *a,
+						char *buf)
+{
+	return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_SYS_SHIFT);
+}
+
+static ssize_t in_accel_calibration_auto_status_show(struct device *dev,
+						     struct device_attribute *a,
+						     char *buf)
+{
+	return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_ACCEL_SHIFT);
+}
+
+static ssize_t in_gyro_calibration_auto_status_show(struct device *dev,
+						    struct device_attribute *a,
+						    char *buf)
+{
+	return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_GYRO_SHIFT);
+}
+
+static ssize_t in_magn_calibration_auto_status_show(struct device *dev,
+						    struct device_attribute *a,
+						    char *buf)
+{
+	return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_MAGN_SHIFT);
+}
+
+#ifdef CONFIG_DEBUG_FS
+int bno055_debugfs_reg_access(struct iio_dev *iio_dev, unsigned int reg,
+			      unsigned int writeval, unsigned int *readval)
+{
+	struct bno055_priv *priv = iio_priv(iio_dev);
+
+	if (readval)
+		return regmap_read(priv->regmap, reg, readval);
+	else
+		return regmap_write(priv->regmap, reg, writeval);
+}
+
+static ssize_t bno055_show_fw_version(struct file *file, char __user *userbuf,
+				      size_t count, loff_t *ppos)
+{
+	struct bno055_priv *priv = file->private_data;
+	int rev, ver;
+	char *buf;
+	int ret;
+
+	ret = regmap_read(priv->regmap, BNO055_SW_REV_LSB_REG, &rev);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(priv->regmap, BNO055_SW_REV_MSB_REG, &ver);
+	if (ret)
+		return ret;
+
+	buf = kasprintf(GFP_KERNEL, "ver: 0x%x, rev: 0x%x\n", ver, rev);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf));
+	kfree(buf);
+
+	return ret;
+}
+
+static const struct file_operations bno055_fw_version_ops = {
+	.open = simple_open,
+	.read = bno055_show_fw_version,
+	.llseek = default_llseek,
+	.owner = THIS_MODULE,
+};
+
+static void bno055_debugfs_init(struct iio_dev *iio_dev)
+{
+	struct bno055_priv *priv = iio_priv(iio_dev);
+
+	debugfs_create_file("firmware_version", 0400,
+			    iio_get_debugfs_dentry(iio_dev), priv,
+			    &bno055_fw_version_ops);
+}
+#else
+static void bno055_debugfs_init(struct iio_dev *iio_dev)
+{
+}
+
+int bno055_debugfs_reg_access(struct iio_dev *iio_dev, unsigned int reg,
+			      unsigned int writeval, unsigned int *readval)
+{
+	return 0;
+}
+#endif
+
+static IIO_DEVICE_ATTR(fusion_enable, 0644,
+		       bno055_fusion_enable_show,
+		       bno055_fusion_enable_store, 0);
+
+static IIO_DEVICE_ATTR(in_magn_calibration_fast_enable, 0644,
+		       bno055_fmc_enable_show,
+		       bno055_fmc_enable_store, 0);
+
+static IIO_DEVICE_ATTR(in_accel_range_raw, 0644,
+		       bno055_in_accel_range_show,
+		       bno055_in_accel_range_store, 0);
Quoted 8 lines

drivers/iio/imu/bno055/bno055.c line 1285

Quoted 8 lines
drivers/iio/imu/bno055/bno055.c new file +8 -0
+
+static IIO_DEVICE_ATTR_RO(in_accel_range_raw_available, 0);
+
+static IIO_DEVICE_ATTR_RO(sys_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_accel_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_gyro_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_magn_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(calibration_data, 0);

This is documented as providing binary data but it's not using a binary attribute and that rather surprised me.

Off the top of my head I can't recall why it matters though, so please take a look at whether a bin_attribute makes more sense for this.

Quoted 5 lines

drivers/iio/imu/bno055/bno055.c line 1290

Quoted 5 lines
drivers/iio/imu/bno055/bno055.c new file +5 -0
+
+static IIO_DEVICE_ATTR_RO(serial_number, 0);
+
+static struct attribute *bno055_attrs[] = {
+	&iio_dev_attr_in_accel_range_raw_available.dev_attr.attr,

discussed in ABI documentation review.
I think these should be range_input to avoid implication they are in _raw units (i.e. need _scale to be applied)

Quoted 217 lines
Quoted 217 lines
drivers/iio/imu/bno055/bno055.c new file +217 -0
+	&iio_dev_attr_in_accel_range_raw.dev_attr.attr,
+	&iio_dev_attr_fusion_enable.dev_attr.attr,
+	&iio_dev_attr_in_magn_calibration_fast_enable.dev_attr.attr,
+	&iio_dev_attr_sys_calibration_auto_status.dev_attr.attr,
+	&iio_dev_attr_in_accel_calibration_auto_status.dev_attr.attr,
+	&iio_dev_attr_in_gyro_calibration_auto_status.dev_attr.attr,
+	&iio_dev_attr_in_magn_calibration_auto_status.dev_attr.attr,
+	&iio_dev_attr_calibration_data.dev_attr.attr,
+	&iio_dev_attr_serial_number.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group bno055_attrs_group = {
+	.attrs = bno055_attrs,
+};
+
+static const struct iio_info bno055_info = {
+	.read_raw_multi = bno055_read_raw_multi,
+	.read_avail = bno055_read_avail,
+	.write_raw = bno055_write_raw,
+	.attrs = &bno055_attrs_group,
+	.debugfs_reg_access = bno055_debugfs_reg_access,
+};
+
+/*
+ * Reads len samples from the HW, stores them in buf starting from buf_idx,
+ * and applies mask to cull (skip) unneeded samples.
+ * Updates buf_idx incrementing with the number of stored samples.
+ * Samples from HW are transferred into buf, then in-place copy on buf is
+ * performed in order to cull samples that need to be skipped.
+ * This avoids copies of the first samples until we hit the 1st sample to skip,
+ * and also avoids having an extra bounce buffer.
+ * buf must be able to contain len elements in spite of how many samples we are
+ * going to cull.
+ */
+static int bno055_scan_xfer(struct bno055_priv *priv,
+			    int start_ch, int len, unsigned long mask,
+			    __le16 *buf, int *buf_idx)
+{
+	const int base = BNO055_ACC_DATA_X_LSB_REG;
+	bool quat_in_read = false;
+	int buf_base = *buf_idx;
+	__le16 *dst, *src;
+	int offs_fixup = 0;
+	int xfer_len = len;
+	int ret;
+	int i, n;
+
+	if (!mask)
+		return 0;
+
+	/*
+	 * All chans are made up 1 16-bit sample, except for quaternion that is
+	 * made up 4 16-bit values.
+	 * For us the quaternion CH is just like 4 regular CHs.
+	 * If our read starts past the quaternion make sure to adjust the
+	 * starting offset; if the quaternion is contained in our scan then make
+	 * sure to adjust the read len.
+	 */
+	if (start_ch > BNO055_SCAN_QUATERNION) {
+		start_ch += 3;
+	} else if ((start_ch <= BNO055_SCAN_QUATERNION) &&
+		 ((start_ch + len) > BNO055_SCAN_QUATERNION)) {
+		quat_in_read = true;
+		xfer_len += 3;
+	}
+
+	ret = regmap_bulk_read(priv->regmap,
+			       base + start_ch * sizeof(__le16),
+			       buf + buf_base,
+			       xfer_len * sizeof(__le16));
+	if (ret)
+		return ret;
+
+	for_each_set_bit(i, &mask, len) {
+		if (quat_in_read && ((start_ch + i) > BNO055_SCAN_QUATERNION))
+			offs_fixup = 3;
+
+		dst = buf + *buf_idx;
+		src = buf + buf_base + offs_fixup + i;
+
+		n = (start_ch + i == BNO055_SCAN_QUATERNION) ? 4 : 1;
+
+		if (dst != src)
+			memcpy(dst, src, n * sizeof(__le16));
+
+		*buf_idx += n;
+	}
+	return 0;
+}
+
+static irqreturn_t bno055_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *iio_dev = pf->indio_dev;
+	struct bno055_priv *priv = iio_priv(iio_dev);
+	int xfer_start, start, end, prev_end;
+	unsigned long mask;
+	int quat_extra_len;
+	bool first = true;
+	int buf_idx = 0;
+	bool thr_hit;
+	int ret;
+
+	mutex_lock(&priv->lock);
+
+	/*
+	 * Walk the bitmap and eventually perform several transfers.
+	 * Bitmap ones-fileds that are separated by gaps <= xfer_burst_break_thr
+	 * will be included in same transfer.
+	 * Every time the bitmap contains a gap wider than xfer_burst_break_thr
+	 * then we split the transfer, skipping the gap.
+	 */
+	for_each_set_bitrange(start, end, iio_dev->active_scan_mask,
+			      iio_dev->masklength) {
+		/*
+		 * First transfer will start from the beginnig of the first
+		 * ones-field in the bitmap
+		 */
+		if (first)
+			xfer_start = start;
+
+		/*
+		 * We found the next ones-field; check whether to include it in
+		 * the current transfer or not (i.e. let's perform the current
+		 * transfer and prepare for another one).
+		 */
+		if (!first) {
+			/*
+			 * In case the zeros-gap contains the quaternion bit,
+			 * then its length is actually 4 words instead of 1
+			 * (i.e. +3 wrt other channels).
+			 */
+			quat_extra_len = ((start > BNO055_SCAN_QUATERNION) &&
+					  (prev_end <= BNO055_SCAN_QUATERNION)) ? 3 : 0;
+
+			/* If the gap is wider than xfer_burst_break_thr then.. */
+			thr_hit = (start - prev_end + quat_extra_len) >
+				priv->xfer_burst_break_thr;
+
+			/*
+			 * .. transfer all the data up to the gap. Then set the
+			 * next transfer start index at right after the gap
+			 * (i.e. at the start of this ones-field).
+			 */
+			if (thr_hit) {
+				mask = *iio_dev->active_scan_mask >> xfer_start;
+				ret = bno055_scan_xfer(priv, xfer_start,
+						       prev_end - xfer_start,
+						       mask, priv->buf.chans, &buf_idx);
+				if (ret)
+					goto done;
+				xfer_start = start;
+			}
+		}
+		first = false;
+		prev_end = end;
+	}
+
+	/*
+	 * We finished walking the bitmap; no more gaps to check for. Just
+	 * perform the current transfer.
+	 */
+	mask = *iio_dev->active_scan_mask >> xfer_start;
+	ret = bno055_scan_xfer(priv, xfer_start,
+			       prev_end - xfer_start,
+			       mask, priv->buf.chans, &buf_idx);
+
+	iio_push_to_buffers_with_timestamp(iio_dev, &priv->buf, pf->timestamp);
+done:
+	mutex_unlock(&priv->lock);
+	iio_trigger_notify_done(iio_dev->trig);
+	return IRQ_HANDLED;
+}
+
+static void bno055_clk_disable(void *arg)
+{
+	clk_disable_unprepare((struct clk *)arg);
+}
+
+int bno055_probe(struct device *dev, struct regmap *regmap,
+		 int xfer_burst_break_thr, bool sw_reset)
+{
+	const struct firmware *caldata;
+	struct bno055_priv *priv;
+	const u8 *caldata_data = NULL;
+	struct iio_dev *iio_dev;
+	int caldata_size = 0;
+	char *fw_name_buf;
+	unsigned int val;
+	int rev, ver;
+	int ret;
+
+	iio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+	if (!iio_dev)
+		return -ENOMEM;
+
+	iio_dev->name = "bno055";
+	priv = iio_priv(iio_dev);
+	mutex_init(&priv->lock);
+	priv->regmap = regmap;
+	priv->dev = dev;
+	priv->xfer_burst_break_thr = xfer_burst_break_thr;
+	priv->sw_reset = sw_reset;
+
+	priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(priv->reset_gpio))
+		return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO");
+
+	priv->clk = devm_clk_get_optional(dev, "clk");
+	if (IS_ERR(priv->clk))
+		return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to get CLK");
+
+	ret = clk_prepare_enable(priv->clk);
+	if (ret)
+		return ret;
+
Quoted 8 lines

drivers/iio/imu/bno055/bno055.c line 1515

Quoted 8 lines
drivers/iio/imu/bno055/bno055.c new file +8 -0
+	ret = devm_add_action_or_reset(dev, bno055_clk_disable, priv->clk);
+	if (ret)
+		return ret;
+
+	if (priv->reset_gpio) {
+		usleep_range(5000, 10000);
+		gpiod_set_value_cansleep(priv->reset_gpio, 1);
+		usleep_range(650000, 750000);

Not a toggle on the reset? I'd expect it to be set and then unset after a pulse.

Quoted 7 lines

drivers/iio/imu/bno055/bno055.c line 1518

Quoted 3 lines
drivers/iio/imu/bno055/bno055.c new file +3 -0
+	} else {
+		if (!sw_reset)
+			dev_warn(dev, "No any usable reset metod; IMU may be unreliable");  

typo: metod
maybe: "No usable reset method; ..."

Quoted 12 lines

drivers/iio/imu/bno055/bno055.c line 1530

Quoted 12 lines
drivers/iio/imu/bno055/bno055.c new file +12 -0
+	}
+
+	ret = regmap_read(priv->regmap, BNO055_CHIP_ID_REG, &val);
+	if (ret)
+		return ret;
+
+	if (val != BNO055_CHIP_ID_MAGIC) {
+		dev_err(dev, "Unrecognized chip ID 0x%x", val);
+		return -ENODEV;
+	}
+
+	if (!priv->reset_gpio) {

Good to have a comment here on why we wait for non gpio based reset to here rather than doing it before reading the chip ID.

Quoted 7 lines
Quoted 7 lines
drivers/iio/imu/bno055/bno055.c new file +7 -0
+		ret = bno055_system_reset(priv);
+		if (ret)
+			return ret;
+	}
+
+	ret = regmap_read(priv->regmap, BNO055_SW_REV_LSB_REG, &rev);
+	if (ret)
Quoted 8 lines

drivers/iio/imu/bno055/bno055.c line 1545

Quoted 8 lines
drivers/iio/imu/bno055/bno055.c new file +8 -0
+		return ret;
+
+	ret = regmap_read(priv->regmap, BNO055_SW_REV_MSB_REG, &ver);
+	if (ret)
+		return ret;
+
+	if (ver != 0x3 || rev != 0x11)
+		dev_warn(dev, "Untested firmware version. Anglvel scale may not work as expected");

Leave a comment here as well on the oddities you have discovered about this particular firmware version. Let's not loose that useful work you did to figure out what was going on.

Quoted 15 lines
Quoted 15 lines
drivers/iio/imu/bno055/bno055.c new file +15 -0
+
+	ret = regmap_bulk_read(priv->regmap, BNO055_UID_LOWER_REG,
+			       priv->uid, BNO055_UID_LEN);
+	if (ret)
+		return ret;
+
+	/*
+	 * This has nothing to do with the IMU firmware, this is for sensor
+	 * calibration data.
+	 */
+	fw_name_buf = kasprintf(GFP_KERNEL,
+				BNO055_FW_UID_NAME,
+				BNO055_UID_LEN, priv->uid);
+	if (!fw_name_buf)
+		return -ENOMEM;
Quoted 8 lines

drivers/iio/imu/bno055/bno055.c line 1568

Quoted 8 lines
drivers/iio/imu/bno055/bno055.c new file +8 -0
+
+	ret = request_firmware(&caldata, fw_name_buf, dev);
+	kfree(fw_name_buf);
+	if (ret)
+		ret = request_firmware(&caldata, BNO055_FW_GENERIC_NAME, dev);
+
+	if (ret)
+		dev_notice(dev, "Calibration file load failed. See instruction in kernel Documentation");

Better to give a file location perhaps to make it easy to find.

Quoted 28 lines
Quoted 28 lines
drivers/iio/imu/bno055/bno055.c new file +28 -0
+
+	if (caldata) {
+		caldata_data = caldata->data;
+		caldata_size = caldata->size;
+	}
+	ret = bno055_init(priv, caldata_data, caldata_size);
+	if (caldata)
+		release_firmware(caldata);
+	if (ret)
+		return ret;
+
+	priv->operation_mode = BNO055_OPR_MODE_FUSION;
+	ret = bno055_operation_mode_do_set(priv, priv->operation_mode);
+	if (ret)
+		return ret;
+
+	ret = devm_add_action_or_reset(dev, bno055_uninit, priv);
+	if (ret)
+		return ret;
+
+	iio_dev->channels = bno055_channels;
+	iio_dev->num_channels = ARRAY_SIZE(bno055_channels);
+	iio_dev->info = &bno055_info;
+	iio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = devm_iio_triggered_buffer_setup(dev, iio_dev,
+					      iio_pollfunc_store_time,
+					      bno055_trigger_handler, NULL);
Quoted 8 lines

drivers/iio/imu/bno055/bno055.c line 1604

Quoted 8 lines
drivers/iio/imu/bno055/bno055.c new file +8 -0
+	if (ret)
+		return ret;
+
+	ret = devm_iio_device_register(dev, iio_dev);
+	if (ret)
+		return ret;
+
+	bno055_debugfs_init(iio_dev);

No cleanup of this? Perhaps for consistency should have a devm_add_action_or_reset() to remove the file again at the equivalent place in the remove() ordering.

Quoted 28 lines
Quoted 27 lines
drivers/iio/imu/bno055/bno055.c new file +8 -0
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(bno055_probe);
+
+MODULE_AUTHOR("Andrea Merello <[email protected]>");
+MODULE_DESCRIPTION("Bosch BNO055 driver");
+MODULE_LICENSE("GPL v2");
drivers/iio/imu/bno055/bno055.h new file +12 -0
new file mode 100644
index 000000000000..2ccb373c61cd
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __BNO055_H__
+#define __BNO055_H__
+
+#include <linux/regmap.h>
+
+struct device;
+int bno055_probe(struct device *dev, struct regmap *regmap,
+		 int xfer_burst_break_thr, bool sw_reset);
+extern const struct regmap_config bno055_regmap_config;
+
+#endif
#12 lore raw

Re: [v3 07/13] iio: imu: add Bosch Sensortec BNO055 core driver

Il giorno sab 19 feb 2022 alle ore 18:34 Jonathan Cameron <[email protected]> ha scritto:

Quoted 11 lines
Quoted 3 lines · On Thu, 17 Feb 2022 22:58:14 +0100 (CET) Peter Meerwald-Stadler…

On Thu, 17 Feb 2022, Andrea Merello wrote:

nice work, minor comments below

I'll review on top of Peter to save on duplication.

Mostly really minor stuff.

:)

As usual, comments inline; OK for all the rest.

Quoted 8 lines

Given this has crossed with the introduction of namespaces to quite a few IIO drivers (I have another series to do on that once I get caught up with reviews) I'd prefer it if you would move this into a symbol namespace (EXPORT_SYMBOL_NS_GPL() and appropriate namespace statements in the two bus modules.

Save it being done as a follow up series. If you prefer not to then that's fine too as it'll be a trivial follow up patch.

I'll include it in V4 directly.

[...]

Quoted 9 lines
Quoted 7 lines

drivers/iio/imu/bno055/bno055.c line 859

Quoted 7 lines
drivers/iio/imu/bno055/bno055.c new file +7 -0
+   case IIO_CHAN_INFO_SCALE:
+           /* Table 3-31: 1 quaternion = 2^14 LSB */
+           if (size < 2)
+                   return -EINVAL;
+           vals[0] = 1;
+           vals[1] = 1 << 14;
+           return IIO_VAL_FRACTIONAL_LOG2;

This doesn't look right. Not vals[1] = 14 given FRACTIONAL_LOG2?

Hum.. maybe just IIO_VAL_FRACTIONAL ?

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
drivers/iio/imu/bno055/bno055.c new file +5 -0
+   default:
+           return -EINVAL;
+   }
+}
+

[...]

Quoted 11 lines
Quoted 5 lines

drivers/iio/imu/bno055/bno055.c line 1285

Quoted 5 lines
drivers/iio/imu/bno055/bno055.c new file +5 -0
+static IIO_DEVICE_ATTR_RO(sys_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_accel_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_gyro_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_magn_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(calibration_data, 0);

This is documented as providing binary data but it's not using a binary attribute and that rather surprised me.

Off the top of my head I can't recall why it matters though, so please take a look at whether a bin_attribute makes more sense for this.

As far as I can see, it seems that a non-binary attributes only support to be read at once while the binary attributes read() operation supports random access i.e. it has the file position parameter.

The calibration data is "dynamic", it's read from the HW every time, and I'm not sure it makes any sense to read it in several chunks (what if we read a chunk and the calibration data is updated by the HW before reading the second chunk?). So, despide the fitting "binary" name I'm tempted to stick with regular attribute. However I'm not sure this is the only difference related to binary attributes.

Quoted 9 lines
Quoted 5 lines

drivers/iio/imu/bno055/bno055.c line 1290

Quoted 5 lines
drivers/iio/imu/bno055/bno055.c new file +5 -0
+
+static IIO_DEVICE_ATTR_RO(serial_number, 0);
+
+static struct attribute *bno055_attrs[] = {
+   &iio_dev_attr_in_accel_range_raw_available.dev_attr.attr,

discussed in ABI documentation review.
I think these should be range_input to avoid implication they are in _raw units (i.e. need _scale to be applied)

They are raw indeed; they need scale to be applied, then they become m/s^2.

I'll fix the doc to clarify this.

[...]

Quoted 16 lines
Quoted 16 lines
Quoted 16 lines
drivers/iio/imu/bno055/bno055.c new file +16 -0
+
+   priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+   if (IS_ERR(priv->reset_gpio))
+           return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO");
+
+   priv->clk = devm_clk_get_optional(dev, "clk");
+   if (IS_ERR(priv->clk))
+           return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to get CLK");
+
+   ret = clk_prepare_enable(priv->clk);
+   if (ret)
+           return ret;
+
+   ret = devm_add_action_or_reset(dev, bno055_clk_disable, priv->clk);
+   if (ret)
+           return ret;
Quoted 8 lines
Quoted 5 lines

drivers/iio/imu/bno055/bno055.c line 1515

Quoted 5 lines
drivers/iio/imu/bno055/bno055.c new file +5 -0
+
+   if (priv->reset_gpio) {
+           usleep_range(5000, 10000);
+           gpiod_set_value_cansleep(priv->reset_gpio, 1);
+           usleep_range(650000, 750000);

Not a toggle on the reset? I'd expect it to be set and then unset after a pulse.

Isn't the above devm_gpiod_get_optional() call that also initialize the initial GPIO value (then just wait and flip here) ?

[...]

maintainer
#13 lore raw

Re: [v3 07/13] iio: imu: add Bosch Sensortec BNO055 core driver

Quoted 13 lines · On Tue, 22 Mar 2022 11:27:14 +0100 Andrea Merello <[email protected]>…

Il giorno sab 19 feb 2022 alle ore 18:34 Jonathan Cameron <[email protected]> ha scritto:

Quoted 11 lines
Quoted 3 lines · On Thu, 17 Feb 2022 22:58:14 +0100 (CET) Peter Meerwald-Stadler…

On Thu, 17 Feb 2022, Andrea Merello wrote:

nice work, minor comments below

I'll review on top of Peter to save on duplication.

Mostly really minor stuff.

+CC Greg for binary attribute questions.

Quoted 26 lines

:)

As usual, comments inline; OK for all the rest.

Quoted 8 lines

Given this has crossed with the introduction of namespaces to quite a few IIO drivers (I have another series to do on that once I get caught up with reviews) I'd prefer it if you would move this into a symbol namespace (EXPORT_SYMBOL_NS_GPL() and appropriate namespace statements in the two bus modules.

Save it being done as a follow up series. If you prefer not to then that's fine too as it'll be a trivial follow up patch.

I'll include it in V4 directly.

[...]

Quoted 8 lines
Quoted 7 lines
Quoted 7 lines
drivers/iio/imu/bno055/bno055.c new file +7 -0
+   case IIO_CHAN_INFO_SCALE:
+           /* Table 3-31: 1 quaternion = 2^14 LSB */
+           if (size < 2)
+                   return -EINVAL;
+           vals[0] = 1;
+           vals[1] = 1 << 14;
+           return IIO_VAL_FRACTIONAL_LOG2;  
Quoted 3 lines
Quoted 1 line

This doesn't look right. Not vals[1] = 14 given FRACTIONAL_LOG2?

Hum.. maybe just IIO_VAL_FRACTIONAL ?

That works as well, though I'd argue FRACTIONAL_LOG2 is the better option as it makes it clear the divisor is a power of 2 and the precision might potentially be better as a result (I've not checked!)

Quoted 26 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
drivers/iio/imu/bno055/bno055.c new file +5 -0
+   default:
+           return -EINVAL;
+   }
+}
+  

[...]

Quoted 11 lines
Quoted 5 lines

drivers/iio/imu/bno055/bno055.c line 1285

Quoted 5 lines
drivers/iio/imu/bno055/bno055.c new file +5 -0
+static IIO_DEVICE_ATTR_RO(sys_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_accel_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_gyro_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_magn_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(calibration_data, 0);  

This is documented as providing binary data but it's not using a binary attribute and that rather surprised me.

Off the top of my head I can't recall why it matters though, so please take a look at whether a bin_attribute makes more sense for this.

As far as I can see, it seems that a non-binary attributes only support to be read at once while the binary attributes read() operation supports random access i.e. it has the file position parameter.

Quoted 6 lines

The calibration data is "dynamic", it's read from the HW every time, and I'm not sure it makes any sense to read it in several chunks (what if we read a chunk and the calibration data is updated by the HW before reading the second chunk?). So, despide the fitting "binary" name I'm tempted to stick with regular attribute. However I'm not sure this is the only difference related to binary attributes.

+Cc Greg. Valid choice to use a normal attribute for this?

Quoted 11 lines
Quoted 10 lines
Quoted 6 lines

drivers/iio/imu/bno055/bno055.c line 1290

Quoted 6 lines
drivers/iio/imu/bno055/bno055.c new file +5 -0
+
+static IIO_DEVICE_ATTR_RO(serial_number, 0);
+
+static struct attribute *bno055_attrs[] = {
+   &iio_dev_attr_in_accel_range_raw_available.dev_attr.attr,  

discussed in ABI documentation review.
I think these should be range_input to avoid implication they are in _raw units (i.e. need _scale to be applied)

Quoted 3 lines

They are raw indeed; they need scale to be applied, then they become m/s^2.

I'll fix the doc to clarify this.

Ah. Ok.

Quoted 28 lines

[...]

Quoted 16 lines
Quoted 16 lines
Quoted 16 lines
drivers/iio/imu/bno055/bno055.c new file +16 -0
+
+   priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+   if (IS_ERR(priv->reset_gpio))
+           return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO");
+
+   priv->clk = devm_clk_get_optional(dev, "clk");
+   if (IS_ERR(priv->clk))
+           return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to get CLK");
+
+   ret = clk_prepare_enable(priv->clk);
+   if (ret)
+           return ret;
+
+   ret = devm_add_action_or_reset(dev, bno055_clk_disable, priv->clk);
+   if (ret)
+           return ret;
Quoted 8 lines
Quoted 5 lines

drivers/iio/imu/bno055/bno055.c line 1515

Quoted 5 lines
drivers/iio/imu/bno055/bno055.c new file +5 -0
+
+   if (priv->reset_gpio) {
+           usleep_range(5000, 10000);
+           gpiod_set_value_cansleep(priv->reset_gpio, 1);
+           usleep_range(650000, 750000);  

Not a toggle on the reset? I'd expect it to be set and then unset after a pulse.

Quoted 2 lines

Isn't the above devm_gpiod_get_optional() call that also initialize the initial GPIO value (then just wait and flip here) ?

good point. Missed that.

Jonathan

Quoted 2 lines

[...]

#14 lore raw

Re: [v3 07/13] iio: imu: add Bosch Sensortec BNO055 core driver

Quoted 80 lines · Jonathan Cameron
Quoted 13 lines · On Tue, 22 Mar 2022 11:27:14 +0100 Andrea Merello <[email protected]>…

Il giorno sab 19 feb 2022 alle ore 18:34 Jonathan Cameron <[email protected]> ha scritto:

Quoted 11 lines
Quoted 3 lines · On Thu, 17 Feb 2022 22:58:14 +0100 (CET) Peter Meerwald-Stadler…

On Thu, 17 Feb 2022, Andrea Merello wrote:

nice work, minor comments below

I'll review on top of Peter to save on duplication.

Mostly really minor stuff.

+CC Greg for binary attribute questions.

Quoted 26 lines

:)

As usual, comments inline; OK for all the rest.

Quoted 8 lines

Given this has crossed with the introduction of namespaces to quite a few IIO drivers (I have another series to do on that once I get caught up with reviews) I'd prefer it if you would move this into a symbol namespace (EXPORT_SYMBOL_NS_GPL() and appropriate namespace statements in the two bus modules.

Save it being done as a follow up series. If you prefer not to then that's fine too as it'll be a trivial follow up patch.

I'll include it in V4 directly.

[...]

Quoted 8 lines
Quoted 7 lines
Quoted 7 lines
drivers/iio/imu/bno055/bno055.c new file +7 -0
+   case IIO_CHAN_INFO_SCALE:
+           /* Table 3-31: 1 quaternion = 2^14 LSB */
+           if (size < 2)
+                   return -EINVAL;
+           vals[0] = 1;
+           vals[1] = 1 << 14;
+           return IIO_VAL_FRACTIONAL_LOG2;  
Quoted 3 lines
Quoted 1 line

This doesn't look right. Not vals[1] = 14 given FRACTIONAL_LOG2?

Hum.. maybe just IIO_VAL_FRACTIONAL ?

That works as well, though I'd argue FRACTIONAL_LOG2 is the better option as it makes it clear the divisor is a power of 2 and the precision might potentially be better as a result (I've not checked!)

Quoted 26 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
drivers/iio/imu/bno055/bno055.c new file +5 -0
+   default:
+           return -EINVAL;
+   }
+}
+  

[...]

Quoted 11 lines
Quoted 5 lines

drivers/iio/imu/bno055/bno055.c line 1285

Quoted 5 lines
drivers/iio/imu/bno055/bno055.c new file +5 -0
+static IIO_DEVICE_ATTR_RO(sys_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_accel_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_gyro_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_magn_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(calibration_data, 0);  

This is documented as providing binary data but it's not using a binary attribute and that rather surprised me.

Off the top of my head I can't recall why it matters though, so please take a look at whether a bin_attribute makes more sense for this.

As far as I can see, it seems that a non-binary attributes only support to be read at once while the binary attributes read() operation supports random access i.e. it has the file position parameter.

Quoted 8 lines
Quoted 6 lines

The calibration data is "dynamic", it's read from the HW every time, and I'm not sure it makes any sense to read it in several chunks (what if we read a chunk and the calibration data is updated by the HW before reading the second chunk?). So, despide the fitting "binary" name I'm tempted to stick with regular attribute. However I'm not sure this is the only difference related to binary attributes.

+Cc Greg. Valid choice to use a normal attribute for this?

binary attributes are to ONLY be used for data that flows to/from a device without the kernel ever modifying the data at all. The kerneln is just a pass-through here.

There are a few minor exceptions, but they were exceptions, please don't use them as a valid reason to use a binary attribute.

does that help?

greg k-h

#15 lore raw

Re: [v3 07/13] iio: imu: add Bosch Sensortec BNO055 core driver

Il giorno lun 28 mar 2022 alle ore 15:02 Greg Kroah-Hartman <[email protected]> ha scritto:

Quoted 91 lines · Jonathan Cameron
Quoted 80 lines
Quoted 13 lines · On Tue, 22 Mar 2022 11:27:14 +0100 Andrea Merello <[email protected]>…

Il giorno sab 19 feb 2022 alle ore 18:34 Jonathan Cameron <[email protected]> ha scritto:

Quoted 11 lines
Quoted 3 lines · On Thu, 17 Feb 2022 22:58:14 +0100 (CET) Peter Meerwald-Stadler…

On Thu, 17 Feb 2022, Andrea Merello wrote:

nice work, minor comments below

I'll review on top of Peter to save on duplication.

Mostly really minor stuff.

+CC Greg for binary attribute questions.

Quoted 26 lines

:)

As usual, comments inline; OK for all the rest.

Quoted 8 lines

Given this has crossed with the introduction of namespaces to quite a few IIO drivers (I have another series to do on that once I get caught up with reviews) I'd prefer it if you would move this into a symbol namespace (EXPORT_SYMBOL_NS_GPL() and appropriate namespace statements in the two bus modules.

Save it being done as a follow up series. If you prefer not to then that's fine too as it'll be a trivial follow up patch.

I'll include it in V4 directly.

[...]

Quoted 8 lines
Quoted 7 lines
Quoted 7 lines
drivers/iio/imu/bno055/bno055.c new file +7 -0
+   case IIO_CHAN_INFO_SCALE:
+           /* Table 3-31: 1 quaternion = 2^14 LSB */
+           if (size < 2)
+                   return -EINVAL;
+           vals[0] = 1;
+           vals[1] = 1 << 14;
+           return IIO_VAL_FRACTIONAL_LOG2;
Quoted 3 lines
Quoted 1 line

This doesn't look right. Not vals[1] = 14 given FRACTIONAL_LOG2?

Hum.. maybe just IIO_VAL_FRACTIONAL ?

That works as well, though I'd argue FRACTIONAL_LOG2 is the better option as it makes it clear the divisor is a power of 2 and the precision might potentially be better as a result (I've not checked!)

Quoted 26 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
drivers/iio/imu/bno055/bno055.c new file +5 -0
+   default:
+           return -EINVAL;
+   }
+}
+

[...]

Quoted 11 lines
Quoted 5 lines

drivers/iio/imu/bno055/bno055.c line 1285

Quoted 5 lines
drivers/iio/imu/bno055/bno055.c new file +5 -0
+static IIO_DEVICE_ATTR_RO(sys_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_accel_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_gyro_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_magn_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(calibration_data, 0);

This is documented as providing binary data but it's not using a binary attribute and that rather surprised me.

Off the top of my head I can't recall why it matters though, so please take a look at whether a bin_attribute makes more sense for this.

As far as I can see, it seems that a non-binary attributes only support to be read at once while the binary attributes read() operation supports random access i.e. it has the file position parameter.

Quoted 8 lines
Quoted 6 lines

The calibration data is "dynamic", it's read from the HW every time, and I'm not sure it makes any sense to read it in several chunks (what if we read a chunk and the calibration data is updated by the HW before reading the second chunk?). So, despide the fitting "binary" name I'm tempted to stick with regular attribute. However I'm not sure this is the only difference related to binary attributes.

+Cc Greg. Valid choice to use a normal attribute for this?

Quoted 8 lines

binary attributes are to ONLY be used for data that flows to/from a device without the kernel ever modifying the data at all. The kerneln is just a pass-through here.

There are a few minor exceptions, but they were exceptions, please don't use them as a valid reason to use a binary attribute.

does that help?

Thanks. Here the driver doesn't modify the data, so no probl here about this.

Would it be valid to restrict usage to only complete reads from the start to the end on calibration data i.e. returning -EINVAL when read() function is called with pos != 0 or count < actual data size ?

Quoted 2 lines

greg k-h

#16 lore raw

Re: [v3 07/13] iio: imu: add Bosch Sensortec BNO055 core driver

Quoted 100 lines · Andrea Merello

Il giorno lun 28 mar 2022 alle ore 15:02 Greg Kroah-Hartman <[email protected]> ha scritto:

Quoted 98 lines · Jonathan Cameron
Quoted 80 lines
Quoted 13 lines · On Tue, 22 Mar 2022 11:27:14 +0100 Andrea Merello <[email protected]>…

Il giorno sab 19 feb 2022 alle ore 18:34 Jonathan Cameron <[email protected]> ha scritto:

Quoted 11 lines
Quoted 3 lines · On Thu, 17 Feb 2022 22:58:14 +0100 (CET) Peter Meerwald-Stadler…

On Thu, 17 Feb 2022, Andrea Merello wrote:

nice work, minor comments below

I'll review on top of Peter to save on duplication.

Mostly really minor stuff.

+CC Greg for binary attribute questions.

Quoted 26 lines

:)

As usual, comments inline; OK for all the rest.

Quoted 8 lines

Given this has crossed with the introduction of namespaces to quite a few IIO drivers (I have another series to do on that once I get caught up with reviews) I'd prefer it if you would move this into a symbol namespace (EXPORT_SYMBOL_NS_GPL() and appropriate namespace statements in the two bus modules.

Save it being done as a follow up series. If you prefer not to then that's fine too as it'll be a trivial follow up patch.

I'll include it in V4 directly.

[...]

Quoted 8 lines
Quoted 7 lines
Quoted 7 lines
(quoted diff) +7 -0
+   case IIO_CHAN_INFO_SCALE:
+           /* Table 3-31: 1 quaternion = 2^14 LSB */
+           if (size < 2)
+                   return -EINVAL;
+           vals[0] = 1;
+           vals[1] = 1 << 14;
+           return IIO_VAL_FRACTIONAL_LOG2;
Quoted 3 lines
Quoted 1 line

This doesn't look right. Not vals[1] = 14 given FRACTIONAL_LOG2?

Hum.. maybe just IIO_VAL_FRACTIONAL ?

That works as well, though I'd argue FRACTIONAL_LOG2 is the better option as it makes it clear the divisor is a power of 2 and the precision might potentially be better as a result (I've not checked!)

Quoted 26 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
(quoted diff) +5 -0
+   default:
+           return -EINVAL;
+   }
+}
+

[...]

Quoted 11 lines
Quoted 5 lines

Review

Quoted 5 lines
(quoted diff) +5 -0
+static IIO_DEVICE_ATTR_RO(sys_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_accel_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_gyro_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_magn_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(calibration_data, 0);

This is documented as providing binary data but it's not using a binary attribute and that rather surprised me.

Off the top of my head I can't recall why it matters though, so please take a look at whether a bin_attribute makes more sense for this.

As far as I can see, it seems that a non-binary attributes only support to be read at once while the binary attributes read() operation supports random access i.e. it has the file position parameter.

Quoted 8 lines
Quoted 6 lines

The calibration data is "dynamic", it's read from the HW every time, and I'm not sure it makes any sense to read it in several chunks (what if we read a chunk and the calibration data is updated by the HW before reading the second chunk?). So, despide the fitting "binary" name I'm tempted to stick with regular attribute. However I'm not sure this is the only difference related to binary attributes.

+Cc Greg. Valid choice to use a normal attribute for this?

binary attributes are to ONLY be used for data that flows to/from a device without the kernel ever modifying the data at all. The kerneln is just a pass-through here.

There are a few minor exceptions, but they were exceptions, please don't use them as a valid reason to use a binary attribute.

Quoted 7 lines
Quoted 1 line

does that help?

Thanks. Here the driver doesn't modify the data, so no probl here about this.

Would it be valid to restrict usage to only complete reads from the start to the end on calibration data i.e. returning -EINVAL when read() function is called with pos != 0 or count < actual data size ?

That's up to you.

#17 lore raw

Re: [v3 07/13] iio: imu: add Bosch Sensortec BNO055 core driver

In reply to Peter Meerwald-Stadler

Il giorno gio 17 feb 2022 alle ore 22:58 Peter Meerwald-Stadler <[email protected]> ha scritto:

Quoted 4 lines · Andrea Merello

nice work, minor comments below

Thanks.
Just an inline comment; OK for all the rest.

[...]

Quoted 8 lines
Quoted 8 lines
drivers/iio/imu/bno055/bno055.c new file +8 -0
+static bool bno055_regmap_writeable(struct device *dev, unsigned int reg)
+{
+     /*
+      * Unreadable registers are indeed reserved; there are no WO regs
+      * (except for a single bit in SYS_TRIGGER register)
+      */
+     if (!bno055_regmap_readable(dev, reg))
+             return false;
Quoted 9 lines

drivers/iio/imu/bno055/bno055.c line 250

Quoted 6 lines
drivers/iio/imu/bno055/bno055.c new file +6 -0
+
+     /* data and status registers */
+     if (reg >= BNO055_ACC_DATA_X_LSB_REG && reg <= BNO055_SYS_ERR_REG)
+             return false;
+
+     /* IDs areas */

probably 'ID area'?

I'd say 'ID areas' then, because there are two zones in which different IDs lay (one below the PAGESEL register that contains e.g. SW ID, internal gyro ID, accell ID, and the other that contains the chip UID)

Quoted 7 lines
Quoted 7 lines
drivers/iio/imu/bno055/bno055.c new file +7 -0
+     if (reg < BNO055_PAGESEL_REG ||
+         (reg <= BNO055_UID_HIGHER_REG && reg >= BNO055_UID_LOWER_REG))
+             return false;
+
+     return true;
+}
+
#18 lore raw

PATCH v3 8/13 iio: document bno055 private sysfs attributes

In reply to Andrea Merello

This patch adds ABI documentation for bno055 driver private sysfs attributes.

---
 .../ABI/testing/sysfs-bus-iio-bno055          | 81 +++++++++++++++++++
 1 file changed, 81 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-bno055
Documentation/ABI/testing/sysfs-bus-iio-bno055 new file +81 -0
new file mode 100644
index 000000000000..c239276f2b0f
@@ -0,0 +1,81 @@
+What:		/sys/bus/iio/devices/iio:deviceX/in_accel_raw_range
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		Range for acceleration readings in m/s^2. Note that this does
+		not affects the scale (which should be used when changing the
+		maximum and minimum readable value affects also the reading
+		scaling factor).
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_raw_range
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		Range for angular velocity readings in radians per second. Note
+		that this does not affects the scale (which should be used when
+		changing the maximum and minimum readable value affects also the
+		reading scaling factor).
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_accel_raw_range_available
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		List of allowed values for in_accel_raw_range attribute
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_raw_range_available
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		List of allowed values for in_anglvel_raw_range attribute
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_magn_calibration_fast_enable
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		Can be 1 or 0. Enables/disables the "Fast Magnetometer
+		Calibration" HW function.
+
+What:		/sys/bus/iio/devices/iio:deviceX/fusion_enable
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		Can be 1 or 0. Enables/disables the "sensor fusion" (a.k.a.
+		NDOF) HW function.
+
+What:		/sys/bus/iio/devices/iio:deviceX/calibration_data
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		Reports the binary calibration data blob for the IMU sensors.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_accel_calibration_auto_status
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		Reports the autocalibration status for the accelerometer sensor.
+		Can be 0 (calibration non even enabled) or 1 to 5 where the greater
+		the number, the better the calibration status.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_gyro_calibration_auto_status
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		Reports the autocalibration status for the gyroscope sensor.
+		Can be 0 (calibration non even enabled) or 1 to 5 where the greater
+		the number, the better the calibration status.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_magn_calibration_auto_status
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		Reports the autocalibration status for the magnetometer sensor.
+		Can be 0 (calibration non even enabled) or 1 to 5 where the greater
+		the number, the better the calibration status.
+
+What:		/sys/bus/iio/devices/iio:deviceX/sys_calibration_auto_status
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		Reports the status for the IMU overall autocalibration.
+		Can be 0 (calibration non even enabled) or 1 to 5 where the greater
+		the number, the better the calibration status.
Signature · 1 line
2.17.1
maintainer
#19 lore raw

Re: [v3 08/13] iio: document bno055 private sysfs attributes

Quoted 15 lines · On Thu, 17 Feb 2022 17:27:05 +0100 Andrea Merello <[email protected]>…

This patch adds ABI documentation for bno055 driver private sysfs attributes.

---
 .../ABI/testing/sysfs-bus-iio-bno055          | 81 +++++++++++++++++++
 1 file changed, 81 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-bno055
Documentation/ABI/testing/sysfs-bus-iio-bno055 new file +0 -0
new file mode 100644
index 000000000000..c239276f2b0f

Documentation/ABI/testing/sysfs-bus-iio-bno055 line 2

Quoted 2 lines
Documentation/ABI/testing/sysfs-bus-iio-bno055 new file +2 -0
@@ -0,0 +1,81 @@
+What:		/sys/bus/iio/devices/iio:deviceX/in_accel_raw_range
+KernelVersion:	5.17

Update this to 5.18 as hopefully we'll resolve remaining questions in time to make the merge window.

Documentation/ABI/testing/sysfs-bus-iio-bno055

Quoted 3 lines
Documentation/ABI/testing/sysfs-bus-iio-bno055 new file +3 -0
+Contact:	[email protected]
+Description:
+		Range for acceleration readings in m/s^2. 

If it is in m/s^2 then we need to not call it _raw_ as that implies application of offset and scale. I didn't think this through in earlier discussion...

If it's in m/s^2 then in_accel_input_range would be appropriate. Same for the other similar cases that follow.

Quoted 77 lines

Note that this does

Documentation/ABI/testing/sysfs-bus-iio-bno055 new file +76 -0
+		not affects the scale (which should be used when changing the
+		maximum and minimum readable value affects also the reading
+		scaling factor).
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_raw_range
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		Range for angular velocity readings in radians per second. Note
+		that this does not affects the scale (which should be used when
+		changing the maximum and minimum readable value affects also the
+		reading scaling factor).
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_accel_raw_range_available
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		List of allowed values for in_accel_raw_range attribute
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_raw_range_available
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		List of allowed values for in_anglvel_raw_range attribute
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_magn_calibration_fast_enable
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		Can be 1 or 0. Enables/disables the "Fast Magnetometer
+		Calibration" HW function.
+
+What:		/sys/bus/iio/devices/iio:deviceX/fusion_enable
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		Can be 1 or 0. Enables/disables the "sensor fusion" (a.k.a.
+		NDOF) HW function.
+
+What:		/sys/bus/iio/devices/iio:deviceX/calibration_data
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		Reports the binary calibration data blob for the IMU sensors.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_accel_calibration_auto_status
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		Reports the autocalibration status for the accelerometer sensor.
+		Can be 0 (calibration non even enabled) or 1 to 5 where the greater
+		the number, the better the calibration status.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_gyro_calibration_auto_status
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		Reports the autocalibration status for the gyroscope sensor.
+		Can be 0 (calibration non even enabled) or 1 to 5 where the greater
+		the number, the better the calibration status.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_magn_calibration_auto_status
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		Reports the autocalibration status for the magnetometer sensor.
+		Can be 0 (calibration non even enabled) or 1 to 5 where the greater
+		the number, the better the calibration status.
+
+What:		/sys/bus/iio/devices/iio:deviceX/sys_calibration_auto_status
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		Reports the status for the IMU overall autocalibration.
+		Can be 0 (calibration non even enabled) or 1 to 5 where the greater
+		the number, the better the calibration status.
#20 lore raw

PATCH v3 9/13 iio: document "serial_number" sysfs attribute

In reply to Andrea Merello

This patch adds ABI documentation for the new "serial_number" sysfs attribute. The first user is the bno055 IIO driver.

---
 Documentation/ABI/testing/sysfs-bus-iio | 7 +++++++
 1 file changed, 7 insertions(+)
Documentation/ABI/testing/sysfs-bus-iio +7 -0
index 1b8d77577608..224585f00147 100644
@@ -2017,3 +2017,10 @@ Contact:	[email protected]
 Description:
 		Raw (unscaled) euler angles readings. Units after
 		application of scale are deg.
+
+What:		/sys/bus/iio/devices/iio:deviceX/serial_number
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		16-bytes, 2-digits-per-byte, HEX-string representing the sensor
+		unique ID number.
Signature · 1 line
2.17.1
maintainer
#21 lore raw

Re: [v3 09/13] iio: document "serial_number" sysfs attribute

Quoted 13 lines · On Thu, 17 Feb 2022 17:27:06 +0100 Andrea Merello <[email protected]>…

This patch adds ABI documentation for the new "serial_number" sysfs attribute. The first user is the bno055 IIO driver.

---
 Documentation/ABI/testing/sysfs-bus-iio | 7 +++++++
 1 file changed, 7 insertions(+)
Documentation/ABI/testing/sysfs-bus-iio +0 -0
index 1b8d77577608..224585f00147 100644

Documentation/ABI/testing/sysfs-bus-iio Contact: [email protected] · line 2026

Quoted 10 lines
Documentation/ABI/testing/sysfs-bus-iio +7 -0
@@ -2017,3 +2017,10 @@ Contact:	[email protected]
 Description:
 		Raw (unscaled) euler angles readings. Units after
 		application of scale are deg.
+
+What:		/sys/bus/iio/devices/iio:deviceX/serial_number
+KernelVersion:	5.17
+Contact:	[email protected]
+Description:
+		16-bytes, 2-digits-per-byte, HEX-string representing the sensor
+		unique ID number.

This might prove too restrictive for other devices but I guess we can update the help text when they come along. Maybe relax this to

An example format is 16-bytes, 2-digits-per-byte ....

then we avoid having to update it later when a different format comes along.

#22 lore raw

PATCH v3 10/13 dt-bindings: iio: imu: add documentation for Bosch BNO055 bindings

In reply to Andrea Merello

Introduce new documentation file for the Bosch BNO055 IMU

---
 .../bindings/iio/imu/bosch,bno055.yaml        | 59 +++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml
Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml new file +59 -0
new file mode 100644
index 000000000000..e0d06db161a9
@@ -0,0 +1,59 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/imu/bosch,bno055.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Bosch BNO055
+
+maintainers:
+  - Andrea Merello <[email protected]>
+
+description: |
+  Inertial Measurement Unit with Accelerometer, Gyroscope, Magnetometer and
+  internal MCU for sensor fusion
+  https://www.bosch-sensortec.com/products/smart-sensors/bno055/
+
+properties:
+  compatible:
+    enum:
+      - bosch,bno055
+
+  reg:
+    maxItems: 1
+
+  reset-gpios:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+required:
+  - compatible
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    serial {
+      imu {
+        compatible = "bosch,bno055";
+        reset-gpios = <&gpio0 54 GPIO_ACTIVE_LOW>;
+        clocks = <&imu_clk>;
+      };
+    };
+
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      imu@28 {
+        compatible = "bosch,bno055";
+        reg = <0x28>;
+        reset-gpios = <&gpio0 54 GPIO_ACTIVE_LOW>;
+        clocks = <&imu_clk>;
+      };
+    };
Signature · 1 line
2.17.1
maintainer
#23 lore raw

Re: [v3 10/13] dt-bindings: iio: imu: add documentation for Bosch BNO055 bindings

Quoted 3 lines · Andrea Merello

Introduce new documentation file for the Bosch BNO055 IMU

Your author and Sob emails need to match.

Quoted 74 lines
---
 .../bindings/iio/imu/bosch,bno055.yaml        | 59 +++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml
Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml new file +59 -0
new file mode 100644
index 000000000000..e0d06db161a9
@@ -0,0 +1,59 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/imu/bosch,bno055.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Bosch BNO055
+
+maintainers:
+  - Andrea Merello <[email protected]>
+
+description: |
+  Inertial Measurement Unit with Accelerometer, Gyroscope, Magnetometer and
+  internal MCU for sensor fusion
+  https://www.bosch-sensortec.com/products/smart-sensors/bno055/
+
+properties:
+  compatible:
+    enum:
+      - bosch,bno055
+
+  reg:
+    maxItems: 1
+
+  reset-gpios:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+required:
+  - compatible
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    serial {
+      imu {
+        compatible = "bosch,bno055";
+        reset-gpios = <&gpio0 54 GPIO_ACTIVE_LOW>;
+        clocks = <&imu_clk>;
+      };
+    };
+
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      imu@28 {
+        compatible = "bosch,bno055";
+        reg = <0x28>;
+        reset-gpios = <&gpio0 54 GPIO_ACTIVE_LOW>;
+        clocks = <&imu_clk>;
+      };
+    };
Signature · 1 line
2.17.1
#24 lore raw

PATCH v3 11/13 iio: imu: add BNO055 serdev driver

In reply to Andrea Merello

This path adds a serdev driver for communicating to a BNO055 IMU via serial bus, and it enables the BNO055 core driver to work in this scenario.

---
 drivers/iio/imu/bno055/Kconfig     |  10 +
 drivers/iio/imu/bno055/Makefile    |   1 +
 drivers/iio/imu/bno055/bno055_sl.c | 557 +++++++++++++++++++++++++++++
 3 files changed, 568 insertions(+)
 create mode 100644 drivers/iio/imu/bno055/bno055_sl.c
drivers/iio/imu/bno055/Kconfig +10 -0
index d0ab3221fba5..8a83639ad2a9 100644
@@ -2,3 +2,13 @@
 
 config BOSCH_BNO055_IIO
 	tristate
+
+config BOSCH_BNO055_SERIAL
+	tristate "Bosch BNO055 attached via serial bus"
+	depends on SERIAL_DEV_BUS
+	select BOSCH_BNO055_IIO
+	help
+	  Enable this to support Bosch BNO055 IMUs attached via serial bus.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called bno055_sl.
drivers/iio/imu/bno055/Makefile +1 -0
index 56cc4de60a7e..416f0ff96de5 100644
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0
 
 obj-$(CONFIG_BOSCH_BNO055_IIO) += bno055.o
+obj-$(CONFIG_BOSCH_BNO055_SERIAL) += bno055_sl.o
drivers/iio/imu/bno055/bno055_sl.c new file +557 -0

557 lines not on this page · raw message

Signature · 1 line
2.17.1
#25 lore raw

Re: [v3 11/13] iio: imu: add BNO055 serdev driver

Thank you for the patch! Perhaps something to improve:

Hi Andrea,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on jic23-iio/togreg]
[also build test WARNING on linux/master linus/master v5.17-rc4 next-20220217] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Andrea-Merello/Add-support-for-Bosch-BNO055-IMU/20220218-002932
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
config: alpha-allyesconfig (https://download.01.org/0day-ci/archive/20220218/202202180353.lRcys4tc-lkp(a)intel.com/config)
compiler: alpha-linux-gcc (GCC) 11.2.0

reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/98d7db4486f0404718da9e85ae13da54d757104b
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Andrea-Merello/Add-support-for-Bosch-BNO055-IMU/20220218-002932
git checkout 98d7db4486f0404718da9e85ae13da54d757104b
# save the config file to linux build tree
mkdir build_dir
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=alpha SHELL=/bin/bash drivers/iio/imu/bno055/

If you fix the issue, kindly add following tag as appropriate

All warnings (new ones prefixed by >>):

>> drivers/iio/imu/bno055/bno055.c:285:5: warning: no previous prototype for 'bno055_calibration_load' [-Wmissing-prototypes]
     285 | int bno055_calibration_load(struct bno055_priv *priv, const u8 *data, int len)
         |     ^~~~~~~~~~~~~~~~~~~~~~~
   In file included from include/linux/printk.h:555,
                    from include/linux/kernel.h:29,
                    from include/linux/clk.h:13,
                    from drivers/iio/imu/bno055/bno055.c:18:
   drivers/iio/imu/bno055/bno055.c: In function 'bno055_calibration_load':
>> drivers/iio/imu/bno055/bno055.c:288:36: warning: format '%zu' expects argument of type 'size_t', but argument 4 has type 'int' [-Wformat=]
     288 |                 dev_dbg(priv->dev, "Invalid calibration file size %zu (expected %d)",
         |                                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/dynamic_debug.h:134:29: note: in definition of macro '__dynamic_func_call'
     134 |                 func(&id, ##__VA_ARGS__);               \
         |                             ^~~~~~~~~~~
   include/linux/dynamic_debug.h:166:9: note: in expansion of macro '_dynamic_func_call'
     166 |         _dynamic_func_call(fmt,__dynamic_dev_dbg,               \
         |         ^~~~~~~~~~~~~~~~~~
   include/linux/dev_printk.h:155:9: note: in expansion of macro 'dynamic_dev_dbg'
     155 |         dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |         ^~~~~~~~~~~~~~~
   include/linux/dev_printk.h:155:30: note: in expansion of macro 'dev_fmt'
     155 |         dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |                              ^~~~~~~
   drivers/iio/imu/bno055/bno055.c:288:17: note: in expansion of macro 'dev_dbg'
     288 |                 dev_dbg(priv->dev, "Invalid calibration file size %zu (expected %d)",
         |                 ^~~~~~~
   drivers/iio/imu/bno055/bno055.c:288:69: note: format string is defined here
     288 |                 dev_dbg(priv->dev, "Invalid calibration file size %zu (expected %d)",
         |                                                                   ~~^
         |                                                                     |
         |                                                                     long unsigned int
         |                                                                   %u
   drivers/iio/imu/bno055/bno055.c: At top level:
>> drivers/iio/imu/bno055/bno055.c:748:5: warning: no previous prototype for 'bno055_sysfs_attr_avail' [-Wmissing-prototypes]
     748 | int bno055_sysfs_attr_avail(struct bno055_priv *priv, struct bno055_sysfs_attr *attr,
         |     ^~~~~~~~~~~~~~~~~~~~~~~
>> drivers/iio/imu/bno055/bno055.c:1203:5: warning: no previous prototype for 'bno055_debugfs_reg_access' [-Wmissing-prototypes]

1203 | int bno055_debugfs_reg_access(struct iio_dev *iio_dev, unsigned int reg,
| ^~~~~~~~~~~~~~~~~~~~~~~~~
--
In file included from include/linux/device.h:15,

                    from drivers/iio/imu/bno055/bno055_sl.c:18:
   drivers/iio/imu/bno055/bno055_sl.c: In function 'bno055_sl_read_reg':
>> drivers/iio/imu/bno055/bno055_sl.c:305:45: warning: format '%d' expects argument of type 'int', but argument 3 has type 'size_t' {aka 'long unsigned int'} [-Wformat=]
     305 |                 dev_err(&priv->serdev->dev, "Invalid read valsize %d",
         |                                             ^~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap'
     110 |                 _p_func(dev, fmt, ##__VA_ARGS__);                       \
         |                              ^~~
   include/linux/dev_printk.h:144:56: note: in expansion of macro 'dev_fmt'
     144 |         dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
         |                                                        ^~~~~~~
   drivers/iio/imu/bno055/bno055_sl.c:305:17: note: in expansion of macro 'dev_err'
     305 |                 dev_err(&priv->serdev->dev, "Invalid read valsize %d",
         |                 ^~~~~~~
   drivers/iio/imu/bno055/bno055_sl.c:305:68: note: format string is defined here
     305 |                 dev_err(&priv->serdev->dev, "Invalid read valsize %d",
         |                                                                   ~^
         |                                                                    |
         |                                                                    int
         |                                                                   %ld
   In file included from include/linux/printk.h:555,
                    from include/asm-generic/bug.h:22,
                    from arch/alpha/include/asm/bug.h:23,
                    from include/linux/bug.h:5,
                    from include/linux/thread_info.h:13,
                    from include/asm-generic/preempt.h:5,
                    from ./arch/alpha/include/generated/asm/preempt.h:1,
                    from include/linux/preempt.h:78,
                    from include/linux/spinlock.h:55,
                    from include/linux/swait.h:7,
                    from include/linux/completion.h:12,
                    from drivers/iio/imu/bno055/bno055_sl.c:17:
   drivers/iio/imu/bno055/bno055_sl.c:311:37: warning: format '%d' expects argument of type 'int', but argument 5 has type 'size_t' {aka 'long unsigned int'} [-Wformat=]
     311 |         dev_dbg(&priv->serdev->dev, "rd reg 0x%x (len %d)", reg_addr, val_size);
         |                                     ^~~~~~~~~~~~~~~~~~~~~~
   include/linux/dynamic_debug.h:134:29: note: in definition of macro '__dynamic_func_call'
     134 |                 func(&id, ##__VA_ARGS__);               \
         |                             ^~~~~~~~~~~
   include/linux/dynamic_debug.h:166:9: note: in expansion of macro '_dynamic_func_call'
     166 |         _dynamic_func_call(fmt,__dynamic_dev_dbg,               \
         |         ^~~~~~~~~~~~~~~~~~
   include/linux/dev_printk.h:155:9: note: in expansion of macro 'dynamic_dev_dbg'
     155 |         dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |         ^~~~~~~~~~~~~~~
   include/linux/dev_printk.h:155:30: note: in expansion of macro 'dev_fmt'
     155 |         dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |                              ^~~~~~~
   drivers/iio/imu/bno055/bno055_sl.c:311:9: note: in expansion of macro 'dev_dbg'
     311 |         dev_dbg(&priv->serdev->dev, "rd reg 0x%x (len %d)", reg_addr, val_size);
         |         ^~~~~~~
   drivers/iio/imu/bno055/bno055_sl.c:311:56: note: format string is defined here
     311 |         dev_dbg(&priv->serdev->dev, "rd reg 0x%x (len %d)", reg_addr, val_size);
         |                                                       ~^
         |                                                        |
         |                                                        int
         |                                                       %ld
   In file included from include/linux/printk.h:555,
                    from include/asm-generic/bug.h:22,
                    from arch/alpha/include/asm/bug.h:23,
                    from include/linux/bug.h:5,
                    from include/linux/thread_info.h:13,
                    from include/asm-generic/preempt.h:5,
                    from ./arch/alpha/include/generated/asm/preempt.h:1,
                    from include/linux/preempt.h:78,
                    from include/linux/spinlock.h:55,
                    from include/linux/swait.h:7,
                    from include/linux/completion.h:12,
                    from drivers/iio/imu/bno055/bno055_sl.c:17:
   drivers/iio/imu/bno055/bno055_sl.c: In function 'bno055_sl_receive_buf':
>> drivers/iio/imu/bno055/bno055_sl.c:387:37: warning: field width specifier '*' expects argument of type 'int', but argument 5 has type 'size_t' {aka 'long unsigned int'} [-Wformat=]
     387 |         dev_dbg(&priv->serdev->dev, "recv (len %zu): %*ph ", size, size, buf);
         |                                     ^~~~~~~~~~~~~~~~~~~~~~~
   include/linux/dynamic_debug.h:134:29: note: in definition of macro '__dynamic_func_call'
     134 |                 func(&id, ##__VA_ARGS__);               \
         |                             ^~~~~~~~~~~
   include/linux/dynamic_debug.h:166:9: note: in expansion of macro '_dynamic_func_call'
     166 |         _dynamic_func_call(fmt,__dynamic_dev_dbg,               \
         |         ^~~~~~~~~~~~~~~~~~
   include/linux/dev_printk.h:155:9: note: in expansion of macro 'dynamic_dev_dbg'
     155 |         dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |         ^~~~~~~~~~~~~~~
   include/linux/dev_printk.h:155:30: note: in expansion of macro 'dev_fmt'
     155 |         dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |                              ^~~~~~~
   drivers/iio/imu/bno055/bno055_sl.c:387:9: note: in expansion of macro 'dev_dbg'
     387 |         dev_dbg(&priv->serdev->dev, "recv (len %zu): %*ph ", size, size, buf);
         |         ^~~~~~~
   drivers/iio/imu/bno055/bno055_sl.c:387:55: note: format string is defined here
     387 |         dev_dbg(&priv->serdev->dev, "recv (len %zu): %*ph ", size, size, buf);
         |                                                      ~^~
         |                                                       |
         |                                                       int

vim +305 drivers/iio/imu/bno055/bno055_sl.c

295
296 static int bno055_sl_read_reg(void *context,
297 const void *reg, size_t reg_size,
298 void *val, size_t val_size)
299 {
300 int ret;
301 int reg_addr;
302 struct bno055_sl_priv *priv = context;
303
304 if (val_size > 128) {

 > 305			dev_err(&priv->serdev->dev, "Invalid read valsize %d",
   306				val_size);
   307			return -EINVAL;
   308		}
   309	
   310		reg_addr = ((u8 *)reg)[0];
   311		dev_dbg(&priv->serdev->dev, "rd reg 0x%x (len %d)", reg_addr, val_size);
   312		mutex_lock(&priv->lock);
   313		priv->expected_data_len = val_size;
   314		priv->response_buf = val;
   315		mutex_unlock(&priv->lock);
   316	
   317		ret = bno_sl_send_cmd(priv, 1, reg_addr, val_size, NULL);
   318	
   319		mutex_lock(&priv->lock);
   320		priv->response_buf = NULL;
   321		mutex_unlock(&priv->lock);
   322	
   323		return ret;
   324	}
   325	
   326	/*
   327	 * Handler for received data; this is called from the reicever callback whenever
   328	 * it got some packet from the serial bus. The status tell us whether the
   329	 * packet is valid (i.e. header ok && received payload len consistent wrt the
   330	 * header). It's now our responsability to check whether this is what we
   331	 * expected, of whether we got some unexpected, yet valid, packet.
   332	 */
   333	static void bno055_sl_handle_rx(struct bno055_sl_priv *priv, int status)
   334	{
   335		mutex_lock(&priv->lock);
   336		switch (priv->expect_response) {
   337		case CMD_NONE:
   338			dev_warn(&priv->serdev->dev, "received unexpected, yet valid, data from sensor");
   339			mutex_unlock(&priv->lock);
   340			return;
   341	
   342		case CMD_READ:
   343			priv->cmd_status = status;
   344			if (status == STATUS_OK &&
   345			    priv->rx.databuf_count != priv->expected_data_len) {
   346				/*
   347				 * If we got here, then the lower layer serial protocol
   348				 * seems consistent with itself; if we got an unexpected
   349				 * amount of data then signal it as a non critical error
   350				 */
   351				priv->cmd_status = STATUS_FAIL;
   352				dev_warn(&priv->serdev->dev, "received an unexpected amount of, yet valid, data from sensor");
   353			}
   354			break;
   355	
   356		case CMD_WRITE:
   357			priv->cmd_status = status;
   358			break;
   359		}
   360	
   361		priv->expect_response = CMD_NONE;
   362		complete(&priv->cmd_complete);
   363		mutex_unlock(&priv->lock);
   364	}
   365	
   366	/*
   367	 * Serdev receiver FSM. This tracks the serial communication and parse the
   368	 * header. It pushes packets to bno055_sl_handle_rx(), eventually communicating
   369	 * failures (i.e. malformed packets).
   370	 * Ideally it doesn't know anything about upper layer (i.e. if this is the
   371	 * packet we were really expecting), but since we copies the payload into the
   372	 * receiver buffer (that is not valid when i.e. we don't expect data), we
   373	 * snoop a bit in the upper layer..
   374	 * Also, we assume to RX one pkt per time (i.e. the HW doesn't send anything
   375	 * unless we require to AND we don't queue more than one request per time).
   376	 */
   377	static int bno055_sl_receive_buf(struct serdev_device *serdev,
   378					 const unsigned char *buf, size_t size)
   379	{
   380		int status;
   381		struct bno055_sl_priv *priv = serdev_device_get_drvdata(serdev);
   382		int remaining = size;
   383	
   384		if (size == 0)
   385			return 0;
   386	
 > 387		dev_dbg(&priv->serdev->dev, "recv (len %zu): %*ph ", size, size, buf);
   388		switch (priv->rx.state) {
   389		case RX_IDLE:
   390			/*
   391			 * New packet.
   392			 * Check for its 1st byte, that identifies the pkt type.
   393			 */
   394			if (buf[0] != 0xEE && buf[0] != 0xBB) {
   395				dev_err(&priv->serdev->dev,
   396					"Invalid packet start %x", buf[0]);
   397				bno055_sl_handle_rx(priv, STATUS_CRIT);
   398				break;
   399			}
   400			priv->rx.type = buf[0];
   401			priv->rx.state = RX_START;
   402			remaining--;
   403			buf++;
   404			priv->rx.databuf_count = 0;
   405			fallthrough;
   406	
   407		case RX_START:
   408			/*
   409			 * Packet RX in progress, we expect either 1-byte len or 1-byte
   410			 * status depending by the packet type.
   411			 */
   412			if (remaining == 0)
   413				break;
   414	
   415			if (priv->rx.type == 0xEE) {
   416				if (remaining > 1) {
   417					dev_err(&priv->serdev->dev, "EE pkt. Extra data received");
   418					status = STATUS_CRIT;
   419	
   420				} else {
   421					status = (buf[0] == 1) ? STATUS_OK : STATUS_FAIL;
   422				}
   423				bno055_sl_handle_rx(priv, status);
   424				priv->rx.state = RX_IDLE;
   425				break;
   426	
   427			} else {
   428				/*priv->rx.type == 0xBB */
   429				priv->rx.state = RX_DATA;
   430				priv->rx.expected_len = buf[0];
   431				remaining--;
   432				buf++;
   433			}
   434			fallthrough;
   435	
   436		case RX_DATA:
   437			/* Header parsed; now receiving packet data payload */
   438			if (remaining == 0)
   439				break;
   440	
   441			if (priv->rx.databuf_count + remaining > priv->rx.expected_len) {
   442				/*
   443				 * This is a inconsistency in serial protocol, we lost
   444				 * sync and we don't know how to handle further data
   445				 */
   446				dev_err(&priv->serdev->dev, "BB pkt. Extra data received");
   447				bno055_sl_handle_rx(priv, STATUS_CRIT);
   448				priv->rx.state = RX_IDLE;
   449				break;
   450			}
   451	
   452			mutex_lock(&priv->lock);
   453			/*
   454			 * NULL e.g. when read cmd is stale or when no read cmd is
   455			 * actually pending.
   456			 */
   457			if (priv->response_buf &&
   458			    /*
   459			     * Snoop on the upper layer protocol stuff to make sure not
   460			     * to write to an invalid memory. Apart for this, let's the
   461			     * upper layer manage any inconsistency wrt expected data
   462			     * len (as long as the serial protocol is consistent wrt
   463			     * itself (i.e. response header is consistent with received
   464			     * response len.
   465			     */

466 (priv->rx.databuf_count + remaining <= priv->expected_data_len))
467 memcpy(priv->response_buf + priv->rx.databuf_count,
468 buf, remaining);
469 mutex_unlock(&priv->lock);
470
471 priv->rx.databuf_count += remaining;
472
473 /*
474 * Reached expected len advertised by the IMU for the current
475 * packet. Pass it to the upper layer (for us it is just valid).
476 */
477 if (priv->rx.databuf_count == priv->rx.expected_len) {
478 bno055_sl_handle_rx(priv, STATUS_OK);
479 priv->rx.state = RX_IDLE;
480 }
481 break;
482 }
483
484 return size;
485 }
486

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

#26 lore raw

Re: [v3 11/13] iio: imu: add BNO055 serdev driver

In reply to Andrea Merello
Thank you for the patch! Yet something to improve:

Hi Andrea,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on jic23-iio/togreg]
[also build test ERROR on linux/master linus/master v5.17-rc4 next-20220217] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Andrea-Merello/Add-support-for-Bosch-BNO055-IMU/20220218-002932
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20220218/202202181307.Lb4f99qg-lkp(a)intel.com/config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0

reproduce (this is a W=1 build):
# https://github.com/0day-ci/linux/commit/98d7db4486f0404718da9e85ae13da54d757104b
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Andrea-Merello/Add-support-for-Bosch-BNO055-IMU/20220218-002932 git checkout 98d7db4486f0404718da9e85ae13da54d757104b
# save the config file to linux build tree
mkdir build_dir
make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash

If you fix the issue, kindly add following tag as appropriate

All errors (new ones prefixed by >>):

drivers/iio/imu/bno055/bno055.c:285:5: warning: no previous prototype for 'bno055_calibration_load' [-Wmissing-prototypes]
285 | int bno055_calibration_load(struct bno055_priv *priv, const u8 *data, int len)
| ^~~~~~~~~~~~~~~~~~~~~~~
drivers/iio/imu/bno055/bno055.c:748:5: warning: no previous prototype for 'bno055_sysfs_attr_avail' [-Wmissing-prototypes]

     748 | int bno055_sysfs_attr_avail(struct bno055_priv *priv, struct bno055_sysfs_attr *attr,
         |     ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/imu/bno055/bno055.c:1203:5: warning: no previous prototype for 'bno055_debugfs_reg_access' [-Wmissing-prototypes]
    1203 | int bno055_debugfs_reg_access(struct iio_dev *iio_dev, unsigned int reg,
         |     ^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/imu/bno055/bno055.c: In function 'bno055_show_fw_version':
>> drivers/iio/imu/bno055/bno055.c:1235:2: error: implicit declaration of function 'kfree'; did you mean 'vfree'? [-Werror=implicit-function-declaration]
    1235 |  kfree(buf);
         |  ^~~~~
         |  vfree

cc1: some warnings being treated as errors

vim +1235 drivers/iio/imu/bno055/bno055.c

16cbcd24402827b Andrea Merello 2022-02-17  1213  
16cbcd24402827b Andrea Merello 2022-02-17  1214  static ssize_t bno055_show_fw_version(struct file *file, char __user *userbuf,
16cbcd24402827b Andrea Merello 2022-02-17  1215  				      size_t count, loff_t *ppos)
16cbcd24402827b Andrea Merello 2022-02-17  1216  {
16cbcd24402827b Andrea Merello 2022-02-17  1217  	struct bno055_priv *priv = file->private_data;
16cbcd24402827b Andrea Merello 2022-02-17  1218  	int rev, ver;
16cbcd24402827b Andrea Merello 2022-02-17  1219  	char *buf;
16cbcd24402827b Andrea Merello 2022-02-17  1220  	int ret;
16cbcd24402827b Andrea Merello 2022-02-17  1221  
16cbcd24402827b Andrea Merello 2022-02-17  1222  	ret = regmap_read(priv->regmap, BNO055_SW_REV_LSB_REG, &rev);
16cbcd24402827b Andrea Merello 2022-02-17  1223  	if (ret)
16cbcd24402827b Andrea Merello 2022-02-17  1224  		return ret;
16cbcd24402827b Andrea Merello 2022-02-17  1225  
16cbcd24402827b Andrea Merello 2022-02-17  1226  	ret = regmap_read(priv->regmap, BNO055_SW_REV_MSB_REG, &ver);
16cbcd24402827b Andrea Merello 2022-02-17  1227  	if (ret)
16cbcd24402827b Andrea Merello 2022-02-17  1228  		return ret;
16cbcd24402827b Andrea Merello 2022-02-17  1229  
16cbcd24402827b Andrea Merello 2022-02-17  1230  	buf = kasprintf(GFP_KERNEL, "ver: 0x%x, rev: 0x%x\n", ver, rev);
16cbcd24402827b Andrea Merello 2022-02-17  1231  	if (!buf)
16cbcd24402827b Andrea Merello 2022-02-17  1232  		return -ENOMEM;
16cbcd24402827b Andrea Merello 2022-02-17  1233  
16cbcd24402827b Andrea Merello 2022-02-17  1234  	ret = simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf));
16cbcd24402827b Andrea Merello 2022-02-17 @1235  	kfree(buf);
16cbcd24402827b Andrea Merello 2022-02-17  1236  
16cbcd24402827b Andrea Merello 2022-02-17  1237  	return ret;
16cbcd24402827b Andrea Merello 2022-02-17  1238  }
16cbcd24402827b Andrea Merello 2022-02-17  1239  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

#27 lore raw

Re: [v3 11/13] iio: imu: add BNO055 serdev driver

In reply to Andrea Merello
Quoted 6 lines · Andrea Merello

This path adds a serdev driver for communicating to a BNO055 IMU via serial bus, and it enables the BNO055 core driver to work in this scenario.

 drivers/iio/imu/bno055/bno055_sl.c | 557 +++++++++++++++++++++++++++++

Can we use the suffix _ser instead of _sl?

...

drivers/iio/imu/bno055/Kconfig line 7

Quoted 2 lines
drivers/iio/imu/bno055/Kconfig +2 -0
+config BOSCH_BNO055_SERIAL
+       tristate "Bosch BNO055 attached via serial bus"

Serial is too broad, it can cover a lot of buses, can we be more specific?

...

drivers/iio/imu/bno055/Kconfig line 11

Quoted 2 lines
drivers/iio/imu/bno055/Kconfig +2 -0
+       help
+         Enable this to support Bosch BNO055 IMUs attached via serial bus.

Ditto.

...

Quoted 11 lines
drivers/iio/imu/bno055/bno055_sl.c new file +11 -0
+struct bno055_sl_priv {
+       struct serdev_device *serdev;
+       struct completion cmd_complete;
+       enum {
+               CMD_NONE,
+               CMD_READ,
+               CMD_WRITE,
+       } expect_response;
+       int expected_data_len;
+       u8 *response_buf;
+

drivers/iio/imu/bno055/bno055_sl.c line 138

Quoted 10 lines
drivers/iio/imu/bno055/bno055_sl.c new file +10 -0
+       /**
+        * enum cmd_status - represent the status of a command sent to the HW.
+        * @STATUS_OK:   The command executed successfully.
+        * @STATUS_FAIL: The command failed: HW responded with an error.
+        * @STATUS_CRIT: The command failed: the serial communication failed.
+        */
+       enum {
+               STATUS_OK = 0,
+               STATUS_FAIL = 1,
+               STATUS_CRIT = -1

+ Comma and sort them by value?
For the second part is an additional question, why negative?

drivers/iio/imu/bno055/bno055_sl.c line 147

Quoted 9 lines
drivers/iio/imu/bno055/bno055_sl.c new file +9 -0
+       } cmd_status;
+       struct mutex lock;
+
+       /* Only accessed in RX callback context. No lock needed. */
+       struct {
+               enum {
+                       RX_IDLE,
+                       RX_START,
+                       RX_DATA

+ Comma.

Quoted 9 lines
drivers/iio/imu/bno055/bno055_sl.c new file +9 -0
+               } state;
+               int databuf_count;
+               int expected_len;
+               int type;
+       } rx;
+
+       /* Never accessed in behalf of RX callback context. No lock needed */
+       bool cmd_stale;
+};

...

Quoted 1 line

+ dev_dbg(&priv->serdev->dev, "send (len: %d): %*ph", len, len, data);

Not a fan of this and similar. Can't you introduce a trace events for this driver?

...

drivers/iio/imu/bno055/bno055_sl.c line 164

Quoted 2 lines
drivers/iio/imu/bno055/bno055_sl.c new file +2 -0
+       ret = serdev_device_write(priv->serdev, data, len,
+                                 msecs_to_jiffies(25));

One line?

...

drivers/iio/imu/bno055/bno055_sl.c line 195

Quoted 11 lines
drivers/iio/imu/bno055/bno055_sl.c new file +10 -0
+       int i = 0;
 
+       while (1) {
+               ret = bno055_sl_send_chunk(priv, hdr + i * 2, 2);
+               if (ret)
+                       goto fail;
+
+               if (i++ == 1)
+                       break;
+               usleep_range(2000, 3000);
+       }

The infinite loops are hard to read and understand. Can you convert it to the regular while or for one?

Also, this looks like a repetition of something (however it seems that it's two sequencial packets to send).

...

drivers/iio/imu/bno055/bno055_sl.c line 245

Quoted 4 lines
drivers/iio/imu/bno055/bno055_sl.c new file +3 -0
+       const int retry_max = 5;
+       int retry = retry_max;
 
+       while (retry--) {

Instead simply use

unsigned int retries = 5;

do {
...
} while (--retries);

which is much better to understand.

...

drivers/iio/imu/bno055/bno055_sl.c line 253

Quoted 3 lines
drivers/iio/imu/bno055/bno055_sl.c new file +3 -0
+               if (retry != (retry_max - 1))
+                       dev_dbg(&priv->serdev->dev, "cmd retry: %d",
+                               retry_max - retry);

This is an invariant to the loop.

Quoted 3 lines
drivers/iio/imu/bno055/bno055_sl.c new file +3 -0
+               ret = bno055_sl_do_send_cmd(priv, read, addr, len, data);
+               if (ret)
+                       continue;

...

drivers/iio/imu/bno055/bno055_sl.c line 263

Quoted 5 lines
drivers/iio/imu/bno055/bno055_sl.c new file +4 -0
+               if (ret == -ERESTARTSYS) {
+                       priv->cmd_stale = true;
+                       return -ERESTARTSYS;
 
+               } else if (!ret) {

Redundant 'else'

Review

Quoted 2 lines
(quoted diff) +2 -0
+                       return -ETIMEDOUT;
+               }

Also {} can be dropped.

...

drivers/iio/imu/bno055/bno055_sl.c line 269

Quoted 4 lines
drivers/iio/imu/bno055/bno055_sl.c new file +3 -0
+               if (priv->cmd_status == STATUS_OK)
+                       return 0;
 
+               else if (priv->cmd_status == STATUS_CRIT)

Redundant 'else'

Quoted 1 line

+ return -EIO;

...

drivers/iio/imu/bno055/bno055_sl.c line 284

Quoted 3 lines
drivers/iio/imu/bno055/bno055_sl.c new file +3 -0
+static int bno055_sl_write_reg(void *context, const void *_data, size_t count)
+{
+       u8 *data = (u8 *)_data;

Why casting?

...

drivers/iio/imu/bno055/bno055_sl.c line 306

Quoted 3 lines
drivers/iio/imu/bno055/bno055_sl.c new file +3 -0
+       if (val_size > 128) {
+               dev_err(&priv->serdev->dev, "Invalid read valsize %d",
+                       val_size);

One line?

Quoted 2 lines
(quoted diff) +2 -0
+               return -EINVAL;
+       }

...

Quoted 1 line

+ reg_addr = ((u8 *)reg)[0];

This looks ugly.
Can't you supply the data struct pointer instead of void pointer?

...

Quoted 1 line

+ if (serdev_device_set_baudrate(serdev, 115200) != 115200) {

Is it limitation / requirement by the hardware? Otherwise it should come from DT / ACPI.

...

Quoted 1 line

+ ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);

Ditto.

...

drivers/iio/imu/bno055/bno055_sl.c line 534

Quoted 7 lines
drivers/iio/imu/bno055/bno055_sl.c new file +6 -0
+       regmap = devm_regmap_init(&serdev->dev, &bno055_sl_regmap_bus,
+                                 priv, &bno055_regmap_config);
 
+       if (IS_ERR(regmap)) {
+               dev_err(&serdev->dev, "Unable to init register map");
+               return PTR_ERR(regmap);
+       }

return dev_err_probe();

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#28 lore raw

Re: [v3 11/13] iio: imu: add BNO055 serdev driver

Some inline comments, OK for the rest.

Il giorno lun 21 feb 2022 alle ore 21:28 Andy Shevchenko <[email protected]> ha scritto:

Quoted 6 lines · Andrea Merello
Quoted 4 lines

This path adds a serdev driver for communicating to a BNO055 IMU via serial bus, and it enables the BNO055 core driver to work in this scenario.

[...]

Quoted 11 lines

drivers/iio/imu/bno055/bno055_sl.c line 138

Quoted 10 lines
drivers/iio/imu/bno055/bno055_sl.c new file +10 -0
+       /**
+        * enum cmd_status - represent the status of a command sent to the HW.
+        * @STATUS_OK:   The command executed successfully.
+        * @STATUS_FAIL: The command failed: HW responded with an error.
+        * @STATUS_CRIT: The command failed: the serial communication failed.
+        */
+       enum {
+               STATUS_OK = 0,
+               STATUS_FAIL = 1,
+               STATUS_CRIT = -1
Quoted 2 lines

+ Comma and sort them by value?
For the second part is an additional question, why negative?

For STATUS_CRIT, being a (bad) error, a negative value seemed appropriate to me. STATUS_OK is zero as usual, but maybe STATUS_FAIL should be negative also? It is some legal protocol status (unlike the STATUS_CRIT mess), in this sense I may consider it not an error, but still our command failed (because the IMU politely didn't accept it), so it's an error in this sense...

I may just let all of them implicit if you prefer.

[...]

Quoted 10 lines

drivers/iio/imu/bno055/bno055_sl.c line 195

Quoted 9 lines
drivers/iio/imu/bno055/bno055_sl.c new file +9 -0
+       while (1) {
+               ret = bno055_sl_send_chunk(priv, hdr + i * 2, 2);
+               if (ret)
+                       goto fail;
+
+               if (i++ == 1)
+                       break;
+               usleep_range(2000, 3000);
+       }
Quoted 5 lines

The infinite loops are hard to read and understand. Can you convert it to the regular while or for one?

Also, this looks like a repetition of something (however it seems that it's two sequencial packets to send).

Maybe it's worth to unroll then?

Quoted 9 lines

...

drivers/iio/imu/bno055/bno055_sl.c line 245

Quoted 4 lines
drivers/iio/imu/bno055/bno055_sl.c new file +3 -0
+       const int retry_max = 5;
+       int retry = retry_max;
 
+       while (retry--) {

Instead simply use

Quoted 7 lines

unsigned int retries = 5;

do {
...
} while (--retries);

which is much better to understand.

OK, but still have the const var for the max (see below)

Quoted 7 lines

...

drivers/iio/imu/bno055/bno055_sl.c line 253

Quoted 3 lines
drivers/iio/imu/bno055/bno055_sl.c new file +3 -0
+               if (retry != (retry_max - 1))
+                       dev_dbg(&priv->serdev->dev, "cmd retry: %d",
+                               retry_max - retry);

This is an invariant to the loop.

why? This triggers at all retries, not at the first attempt i.e. it prints only if this doesn't succeed at the first time. Indeed what seems wrong to me is that you need -1 also in the dev_dbg() argument to produce correct text.

[...]

Quoted 5 lines
Quoted 2 lines

+ reg_addr = ((u8 *)reg)[0];

This looks ugly.
Can't you supply the data struct pointer instead of void pointer?

I confirm that it's ugly :)

Not sure about what you exactly meant, sorry; what I can do is to introduce a local and split this ugly loc, as done in bno055_sl_write_reg(). Is this what you are suggesting?

Quoted 6 lines

...

Quoted 1 line

+ if (serdev_device_set_baudrate(serdev, 115200) != 115200) {

Is it limitation / requirement by the hardware? Otherwise it should come from DT / ACPI.

It's a requirement. Not sure it's really by the HW; possibly it's
statically set in device firmware.

Quoted 5 lines

...

Quoted 1 line

+ ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);

Ditto.

Ditto :)

[...]

#29 lore raw

PATCH v3 12/13 iio: imu: add BNO055 I2C driver

In reply to Andrea Merello

This path adds an I2C driver for communicating to a BNO055 IMU via I2C bus and it enables the BNO055 core driver to work in this scenario.

---
 drivers/iio/imu/bno055/Kconfig      | 11 +++++++
 drivers/iio/imu/bno055/Makefile     |  1 +
 drivers/iio/imu/bno055/bno055_i2c.c | 50 +++++++++++++++++++++++++++++
 3 files changed, 62 insertions(+)
 create mode 100644 drivers/iio/imu/bno055/bno055_i2c.c
drivers/iio/imu/bno055/Kconfig +11 -0
index 8a83639ad2a9..3693a408e270 100644
@@ -12,3 +12,14 @@ config BOSCH_BNO055_SERIAL
 
 	  This driver can also be built as a module. If so, the module will be
 	  called bno055_sl.
+
+config BOSCH_BNO055_I2C
+	tristate "Bosch BNO055 attached via I2C bus"
+	depends on I2C
+	select REGMAP_I2C
+	select BOSCH_BNO055_IIO
+	help
+	  Enable this to support Bosch BNO055 IMUs attached via I2C bus.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called bno055_i2c.
drivers/iio/imu/bno055/Makefile +1 -0
index 416f0ff96de5..2fe2b0857903 100644
@@ -2,3 +2,4 @@
 
 obj-$(CONFIG_BOSCH_BNO055_IIO) += bno055.o
 obj-$(CONFIG_BOSCH_BNO055_SERIAL) += bno055_sl.o
+obj-$(CONFIG_BOSCH_BNO055_I2C) += bno055_i2c.o
drivers/iio/imu/bno055/bno055_i2c.c new file +50 -0

50 lines not on this page · raw message

Signature · 1 line
2.17.1
maintainer
#30 lore raw

Re: [v3 12/13] iio: imu: add BNO055 I2C driver

Quoted 4 lines · On Thu, 17 Feb 2022 17:27:09 +0100 Andrea Merello <[email protected]>…

This path adds an I2C driver for communicating to a BNO055 IMU via I2C bus and it enables the BNO055 core driver to work in this scenario.

Minor comment inline to add an of_device_id table. Isn't i2c so much nicer than serial? :)

Thanks,

Jonathan

Quoted 68 lines
---
 drivers/iio/imu/bno055/Kconfig      | 11 +++++++
 drivers/iio/imu/bno055/Makefile     |  1 +
 drivers/iio/imu/bno055/bno055_i2c.c | 50 +++++++++++++++++++++++++++++
 3 files changed, 62 insertions(+)
 create mode 100644 drivers/iio/imu/bno055/bno055_i2c.c
drivers/iio/imu/bno055/Kconfig +11 -0
index 8a83639ad2a9..3693a408e270 100644
@@ -12,3 +12,14 @@ config BOSCH_BNO055_SERIAL
 
 	  This driver can also be built as a module. If so, the module will be
 	  called bno055_sl.
+
+config BOSCH_BNO055_I2C
+	tristate "Bosch BNO055 attached via I2C bus"
+	depends on I2C
+	select REGMAP_I2C
+	select BOSCH_BNO055_IIO
+	help
+	  Enable this to support Bosch BNO055 IMUs attached via I2C bus.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called bno055_i2c.
drivers/iio/imu/bno055/Makefile +1 -0
index 416f0ff96de5..2fe2b0857903 100644
@@ -2,3 +2,4 @@
 
 obj-$(CONFIG_BOSCH_BNO055_IIO) += bno055.o
 obj-$(CONFIG_BOSCH_BNO055_SERIAL) += bno055_sl.o
+obj-$(CONFIG_BOSCH_BNO055_I2C) += bno055_i2c.o
drivers/iio/imu/bno055/bno055_i2c.c new file +27 -0
new file mode 100644
index 000000000000..9900e2a4d905
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support for I2C-interfaced Bosch BNO055 IMU.
+ *
+ * Copyright (C) 2021 Istituto Italiano di Tecnologia
+ * Electronic Design Laboratory
+ * Written by Andrea Merello <[email protected]>
+ */
+
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/module.h>
+
+#include "bno055.h"
+
+#define BNO055_I2C_XFER_BURST_BREAK_THRESHOLD 3 /* FIXME */
+
+static int bno055_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	struct regmap *regmap =
+		devm_regmap_init_i2c(client, &bno055_regmap_config);
+
+	if (IS_ERR(regmap)) {
+		dev_err(&client->dev, "Unable to init register map");
+		return PTR_ERR(regmap);
+	}

drivers/iio/imu/bno055/bno055_i2c.c line 37

Quoted 10 lines
drivers/iio/imu/bno055/bno055_i2c.c new file +10 -0
+
+	return bno055_probe(&client->dev, regmap,
+			    BNO055_I2C_XFER_BURST_BREAK_THRESHOLD, true);
+}
+
+static const struct i2c_device_id bno055_i2c_id[] = {
+	{"bno055", 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, bno055_i2c_id);

No of_device_id table? Whilst the fallback path that usese the i2c_device_id will mostly work, it leaves us in theory more susceptible to naming clashes and is generally not as nice. Hence definitely prefer it if we have an of_device_id table as well as the i2c_device_id one.

Quoted 13 lines
drivers/iio/imu/bno055/bno055_i2c.c new file +13 -0
+
+static struct i2c_driver bno055_driver = {
+	.driver = {
+		.name = "bno055-i2c",
+	},
+	.probe = bno055_i2c_probe,
+	.id_table = bno055_i2c_id
+};
+module_i2c_driver(bno055_driver);
+
+MODULE_AUTHOR("Andrea Merello");
+MODULE_DESCRIPTION("Bosch BNO055 I2C interface");
+MODULE_LICENSE("GPL v2");
#31 lore raw

Re: [v3 12/13] iio: imu: add BNO055 I2C driver

In reply to Andrea Merello
Quoted 3 lines · Andrea Merello

This path adds an I2C driver for communicating to a BNO055 IMU via I2C bus and it enables the BNO055 core driver to work in this scenario.

...

drivers/iio/imu/bno055/bno055_i2c.c line 12

Quoted 3 lines
drivers/iio/imu/bno055/bno055_i2c.c new file +3 -0
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/module.h>

Keep it sorted?

...

drivers/iio/imu/bno055/bno055_i2c.c line 19

Quoted 2 lines
drivers/iio/imu/bno055/bno055_i2c.c new file +2 -0
+static int bno055_i2c_probe(struct i2c_client *client,
+                           const struct i2c_device_id *id)

You may switch it to use ->probe_new() (you may leave I2C ID table for possibility to instantiate this via user space).

...

drivers/iio/imu/bno055/bno055_i2c.c line 22

Quoted 2 lines
drivers/iio/imu/bno055/bno055_i2c.c new file +2 -0
+       struct regmap *regmap =
+               devm_regmap_init_i2c(client, &bno055_regmap_config);

Please, split the definition and assignment.

drivers/iio/imu/bno055/bno055_i2c.c line 26

Quoted 3 lines
drivers/iio/imu/bno055/bno055_i2c.c new file +3 -0
+       if (IS_ERR(regmap)) {
+               dev_err(&client->dev, "Unable to init register map");
+               return PTR_ERR(regmap);

return dev_err_probe(...);

...

drivers/iio/imu/bno055/bno055_i2c.c line 44

Quoted 2 lines
drivers/iio/imu/bno055/bno055_i2c.c new file +2 -0
+       .probe = bno055_i2c_probe,
+       .id_table = bno055_i2c_id

Missed comma.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#32 lore raw

PATCH v3 13/13 docs: iio: add documentation for BNO055 driver

In reply to Andrea Merello
---
 Documentation/iio/bno055.rst | 53 ++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)
 create mode 100644 Documentation/iio/bno055.rst
Documentation/iio/bno055.rst new file +53 -0
new file mode 100644
index 000000000000..4bb185075325
@@ -0,0 +1,53 @@
+.. SPDX-License-Identifier: GPL-2.0
+==============================
+BNO055 driver
+==============================
+
+1. Overview
+===========
+
+This driver supports Bosch BNO055 IMUs (on both serial and I2C busses).
+
+Accelerometer, magnetometer and gyroscope measures are always provided.
+When "fusion_enable" sysfs attribute is set to 1, orientation (both Euler
+angles and quaternion), linear velocity and gravity vector are also
+provided, but some sensor settings (e.g. low pass filtering and range)
+became locked (the IMU firmware controls them).
+
+IIO attributes for unavailable measurements (e.g. Euler angles when fusion
+mode is disabled) just read zero.
+
+This driver supports also IIO buffers.
+
+2. Calibration
+==============
+
+The IMU continuously performs an autocalibration procedure if (and only if)
+operating in fusion mode. The magnetometer autocalibration can however be
+disabled writing 0 in the sysfs in_magn_calibration_fast_enable attribute.
+
+The driver provides access to autocalibration flags (i.e. you can known if
+the IMU has successfully autocalibrated) and to the 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 (exposed in sysfs as
+serial_number) 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).
+
+Valid calibration file names would be e.g.
+ bno055-caldata-0e7c26a33541515120204a35342b04ff.dat
+ bno055-caldata.dat
+
+In non-fusion mode the IIO 'offset' attributes provide access to the
+offsets from calibration data (if any), so that the user can apply them to
+the accel, angvel and magn IIO attributes. In fusion mode they are not
+needed (the IMU firmware internally applies those corrections) and they
+read as zero.
Signature · 1 line
2.17.1
maintainer
#33 lore raw

Re: [v3 13/13] docs: iio: add documentation for BNO055 driver

On Thu, 17 Feb 2022 17:27:10 +0100
Andrea Merello <[email protected]> wrote:

Perhaps a comment on why it's worth having documentation for this device when we rarely bother? (complex device etc...)

Quoted 3 lines
---
 Documentation/iio/bno055.rst | 53 ++++++++++++++++++++++++++++++++++++

Please add it to an appropriate index such as Docuemntation/iio/index.rst so that it actually gets linked to in the documentation built.

One comment inline.

Quoted 17 lines
 1 file changed, 53 insertions(+)
 create mode 100644 Documentation/iio/bno055.rst
Documentation/iio/bno055.rst new file +8 -0
new file mode 100644
index 000000000000..4bb185075325
@@ -0,0 +1,53 @@
+.. SPDX-License-Identifier: GPL-2.0
+==============================
+BNO055 driver
+==============================
+
+1. Overview
+===========
+

Documentation/iio/bno055.rst line 18

Quoted 10 lines
Documentation/iio/bno055.rst new file +10 -0
+This driver supports Bosch BNO055 IMUs (on both serial and I2C busses).
+
+Accelerometer, magnetometer and gyroscope measures are always provided.
+When "fusion_enable" sysfs attribute is set to 1, orientation (both Euler
+angles and quaternion), linear velocity and gravity vector are also
+provided, but some sensor settings (e.g. low pass filtering and range)
+became locked (the IMU firmware controls them).
+
+IIO attributes for unavailable measurements (e.g. Euler angles when fusion
+mode is disabled) just read zero.

Hmm. Should probably return -EBUSY as 0 could be a valid value.

Quoted 35 lines
Documentation/iio/bno055.rst new file +35 -0
+
+This driver supports also IIO buffers.
+
+2. Calibration
+==============
+
+The IMU continuously performs an autocalibration procedure if (and only if)
+operating in fusion mode. The magnetometer autocalibration can however be
+disabled writing 0 in the sysfs in_magn_calibration_fast_enable attribute.
+
+The driver provides access to autocalibration flags (i.e. you can known if
+the IMU has successfully autocalibrated) and to the 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 (exposed in sysfs as
+serial_number) 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).
+
+Valid calibration file names would be e.g.
+ bno055-caldata-0e7c26a33541515120204a35342b04ff.dat
+ bno055-caldata.dat
+
+In non-fusion mode the IIO 'offset' attributes provide access to the
+offsets from calibration data (if any), so that the user can apply them to
+the accel, angvel and magn IIO attributes. In fusion mode they are not
+needed (the IMU firmware internally applies those corrections) and they
+read as zero.
#34 lore raw

Re: [v3 13/13] docs: iio: add documentation for BNO055 driver

[...]

Quoted 5 lines

Documentation/iio/bno055.rst line 18

Quoted 2 lines
Documentation/iio/bno055.rst new file +2 -0
+IIO attributes for unavailable measurements (e.g. Euler angles when fusion
+mode is disabled) just read zero.

Hmm. Should probably return -EBUSY as 0 could be a valid value.

While it seems reasonable, that can be easily done while reading from sysfs, but how do we handle this when reading from buffer? Right now both sysfs and buffer read zero from unused chans, which it is at least consistent..

#35 lore raw

Re: [v3 13/13] docs: iio: add documentation for BNO055 driver

Quoted 8 lines · On Tue, 22 Mar 2022 11:30:12 +0100 Andrea Merello <[email protected]>…

[...]

Quoted 5 lines

Documentation/iio/bno055.rst line 18

Quoted 2 lines
Documentation/iio/bno055.rst new file +2 -0
+IIO attributes for unavailable measurements (e.g. Euler angles when fusion
+mode is disabled) just read zero.  

Hmm. Should probably return -EBUSY as 0 could be a valid value.

Quoted 4 lines

While it seems reasonable, that can be easily done while reading from sysfs, but how do we handle this when reading from buffer? Right now both sysfs and buffer read zero from unused chans, which it is at least consistent..

Can't we refuse to start the buffer if we have a channel enabled that makes no sense in the current mode?

If seems very odd to allow capture of 'nothing'.

Jonathan