devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [v2 00/10] Add support for Bosch BNO055 IMU
       [not found] <20210715141742.15072-1-andrea.merello@gmail.com>
@ 2021-10-28 10:18 ` Andrea Merello
  2021-10-28 10:18   ` [v2 01/10] utils_macro: introduce find_closest_unsorted() Andrea Merello
                     ` (10 more replies)
  0 siblings, 11 replies; 55+ messages in thread
From: Andrea Merello @ 2021-10-28 10:18 UTC (permalink / raw)
  To: jic23, mchehab+huawei, linux-iio, linux-kernel, devicetree
  Cc: lars, robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex,
	jacopo

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

  1/10: introduces the generic helper find_closest_unsorted()

  2/10 to 5/10: 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.

  6/10 to 8/10: add the core IIO BNO055 driver and its documentation
     	        (including documentation for DT bindings)

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

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


Differences wrt v1:

  - Fixed GPL license version, which was wrong due to bad copy-pastes

  - Make less noise in log and get rid of some dev_dbg()s

  - Fix deferred probe handing and fix devm_add_action_or_reset() usage

  - Get rid of unneeded zeroing for driver data and some IIO "val2"s

  - Get rid of some leftovers of my attempt to support interrupts (which
    don't fully work unless the IMU firmware gets updated)

  - Move IIO buffer off stack and make sure its first zeroed not to leak
    kernel data

  - Hopefully addressed all maintainers and reviewers stylistic advices;
    fixed some typos

  - Take advantage of more kernel helpers. Note: this series depends on
    Yury Norov bitmap series i.e. "[PATCH 14/16] bitmap: unify find_bit
    operations"

  - Make find_closest_unsorted() become an external generic helper

  - Reworked sysfs ABI as per maintainers advices

  - Added ABI documentation where needed

  - Added I2C support

  - Reworked DT documentation as per maintainers advices. Added I2C example

  The serial protocol handling have been criticized because it is not very
  robust, however I couldn't really find any way to improve it; no changes
  here wrt v1. I think the protocol itself is inherently weak and there is
  nothing we can do about this (BTW here it is working fine).

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

  - 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 (10):
  utils_macro: introduce find_closest_unsorted()
  iio: document linear acceleration modifiers
  iio: document euler angles modifiers
  iio: add modifiers for linear acceleration
  iio: add modifers for pitch, yaw, roll
  iio: document bno055 private sysfs attributes
  iio: imu: add Bosch Sensortec BNO055 core driver
  dt-bindings: iio: imu: add documentation for Bosch BNO055 bindings
  iio: imu: add BNO055 serdev driver
  iio: imu: add BNO055 I2C driver

 Documentation/ABI/testing/sysfs-bus-iio       |   16 +
 .../ABI/testing/sysfs-bus-iio-bno055          |   84 +
 .../bindings/iio/imu/bosch,bno055.yaml        |   59 +
 drivers/iio/imu/Kconfig                       |    1 +
 drivers/iio/imu/Makefile                      |    1 +
 drivers/iio/imu/bno055/Kconfig                |   15 +
 drivers/iio/imu/bno055/Makefile               |    5 +
 drivers/iio/imu/bno055/bno055.c               | 1485 +++++++++++++++++
 drivers/iio/imu/bno055/bno055.h               |   12 +
 drivers/iio/imu/bno055/bno055_i2c.c           |   54 +
 drivers/iio/imu/bno055/bno055_sl.c            |  568 +++++++
 drivers/iio/industrialio-core.c               |    6 +
 include/linux/util_macros.h                   |   26 +
 include/uapi/linux/iio/types.h                |    7 +-
 14 files changed, 2338 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 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

^ permalink raw reply	[flat|nested] 55+ messages in thread

* [v2 01/10] utils_macro: introduce find_closest_unsorted()
  2021-10-28 10:18 ` [v2 00/10] Add support for Bosch BNO055 IMU Andrea Merello
@ 2021-10-28 10:18   ` Andrea Merello
  2021-10-28 10:25     ` Andy Shevchenko
  2021-10-28 10:18   ` [v2 02/10] iio: document linear acceleration modifiers Andrea Merello
                     ` (9 subsequent siblings)
  10 siblings, 1 reply; 55+ messages in thread
From: Andrea Merello @ 2021-10-28 10:18 UTC (permalink / raw)
  To: jic23, mchehab+huawei, linux-iio, linux-kernel, devicetree
  Cc: lars, robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex,
	jacopo, Andrea Merello, Andrea Merello

This is similar to find_closest() and find_closest_descending(), but, it
doesn't make any assumption about the array being ordered.

Signed-off-by: Andrea Merello <andrea.merello@iit.it>
---
 include/linux/util_macros.h | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/include/linux/util_macros.h b/include/linux/util_macros.h
index 72299f261b25..b48f80ceb380 100644
--- a/include/linux/util_macros.h
+++ b/include/linux/util_macros.h
@@ -2,6 +2,8 @@
 #ifndef _LINUX_HELPER_MACROS_H_
 #define _LINUX_HELPER_MACROS_H_
 
+#include <linux/math.h>
+
 #define __find_closest(x, a, as, op)					\
 ({									\
 	typeof(as) __fc_i, __fc_as = (as) - 1;				\
@@ -38,4 +40,28 @@
  */
 #define find_closest_descending(x, a, as) __find_closest(x, a, as, >=)
 
+/**
+ * find_closest_unsorted - locate the closest element in a unsorted array
+ * @x: The reference value.
+ * @a: The array in which to look for the closest element.
+ * @as: Size of 'a'.
+ *
+ * Similar to find_closest() but 'a' has no requirement to being sorted
+ */
+#define find_closest_unsorted(x, a, as)					\
+({									\
+	typeof(x) __fc_best_delta, __fc_delta;				\
+	typeof(as) __fc_i, __fc_best_idx;				\
+	bool __fc_first = true;						\
+	for (__fc_i = 0; __fc_i < (as); __fc_i++) {			\
+		__fc_delta = abs(a[__fc_i] - (x));			\
+		if (__fc_first || __fc_delta < __fc_best_delta) {	\
+			__fc_best_delta = __fc_delta;			\
+			__fc_best_idx = __fc_i;				\
+		}							\
+		__fc_first = false;					\
+	}								\
+	(__fc_best_idx);						\
+})
+
 #endif
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [v2 02/10] iio: document linear acceleration modifiers
  2021-10-28 10:18 ` [v2 00/10] Add support for Bosch BNO055 IMU Andrea Merello
  2021-10-28 10:18   ` [v2 01/10] utils_macro: introduce find_closest_unsorted() Andrea Merello
@ 2021-10-28 10:18   ` Andrea Merello
  2021-10-28 10:31     ` Andy Shevchenko
  2021-10-28 10:40     ` Jonathan Cameron
  2021-10-28 10:18   ` [v2 03/10] iio: document euler angles modifiers Andrea Merello
                     ` (8 subsequent siblings)
  10 siblings, 2 replies; 55+ messages in thread
From: Andrea Merello @ 2021-10-28 10:18 UTC (permalink / raw)
  To: jic23, mchehab+huawei, linux-iio, linux-kernel, devicetree
  Cc: lars, robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex,
	jacopo, Andrea Merello, Andrea Merello

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

Signed-off-by: Andrea Merello <andrea.merello@iit.it>
---
 Documentation/ABI/testing/sysfs-bus-iio | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 6ad47a67521c..5147a00bf24a 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -1957,3 +1957,11 @@ Description:
 		Specify the percent for light sensor relative to the channel
 		absolute value that a data field should change before an event
 		is generated. Units are a percentage of the prior reading.
+
+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.15
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Raw (unscaled) linear acceleration readings.
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [v2 03/10] iio: document euler angles modifiers
  2021-10-28 10:18 ` [v2 00/10] Add support for Bosch BNO055 IMU Andrea Merello
  2021-10-28 10:18   ` [v2 01/10] utils_macro: introduce find_closest_unsorted() Andrea Merello
  2021-10-28 10:18   ` [v2 02/10] iio: document linear acceleration modifiers Andrea Merello
@ 2021-10-28 10:18   ` Andrea Merello
  2021-10-28 10:33     ` Andy Shevchenko
  2021-10-28 10:41     ` Jonathan Cameron
  2021-10-28 10:18   ` [v2 04/10] iio: add modifiers for linear acceleration Andrea Merello
                     ` (7 subsequent siblings)
  10 siblings, 2 replies; 55+ messages in thread
From: Andrea Merello @ 2021-10-28 10:18 UTC (permalink / raw)
  To: jic23, mchehab+huawei, linux-iio, linux-kernel, devicetree
  Cc: lars, robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex,
	jacopo, Andrea Merello, Andrea Merello

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

Signed-off-by: Andrea Merello <andrea.merello@iit.it>
---
 Documentation/ABI/testing/sysfs-bus-iio | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 5147a00bf24a..f0adc2c817bd 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -1965,3 +1965,11 @@ KernelVersion:	5.15
 Contact:	linux-iio@vger.kernel.org
 Description:
 		Raw (unscaled) linear acceleration readings.
+
+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.15
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Raw (unscaled) euler angles readings.
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [v2 04/10] iio: add modifiers for linear acceleration
  2021-10-28 10:18 ` [v2 00/10] Add support for Bosch BNO055 IMU Andrea Merello
                     ` (2 preceding siblings ...)
  2021-10-28 10:18   ` [v2 03/10] iio: document euler angles modifiers Andrea Merello
@ 2021-10-28 10:18   ` Andrea Merello
  2021-10-28 10:45     ` Jonathan Cameron
  2021-10-28 10:18   ` [v2 05/10] iio: add modifers for pitch, yaw, roll Andrea Merello
                     ` (6 subsequent siblings)
  10 siblings, 1 reply; 55+ messages in thread
From: Andrea Merello @ 2021-10-28 10:18 UTC (permalink / raw)
  To: jic23, mchehab+huawei, linux-iio, linux-kernel, devicetree
  Cc: lars, robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex,
	jacopo, Andrea Merello, Andrea Merello

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_ACCEL_LINEAR_X, IIO_MOD_ACCEL_LINEAR_Y and
IIO_MOD_ACCEL_LINEAR_Z modifiers to te IIO core.

Signed-off-by: Andrea Merello <andrea.merello@iit.it>
---
 drivers/iio/industrialio-core.c | 3 +++
 include/uapi/linux/iio/types.h  | 4 +++-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 2dbb37e09b8c..a79cb32207e4 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -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_ACCEL_LINEAR_X] = "linear_x",
+	[IIO_MOD_ACCEL_LINEAR_Y] = "linear_y",
+	[IIO_MOD_ACCEL_LINEAR_Z] = "linear_z"
 };
 
 /* relies on pairs of these shared then separate */
diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
index 48c13147c0a8..db00f7c45f48 100644
--- a/include/uapi/linux/iio/types.h
+++ b/include/uapi/linux/iio/types.h
@@ -95,6 +95,9 @@ enum iio_modifier {
 	IIO_MOD_ETHANOL,
 	IIO_MOD_H2,
 	IIO_MOD_O2,
+	IIO_MOD_ACCEL_LINEAR_X,
+	IIO_MOD_ACCEL_LINEAR_Y,
+	IIO_MOD_ACCEL_LINEAR_Z,
 };
 
 enum iio_event_type {
@@ -114,4 +117,3 @@ enum iio_event_direction {
 };
 
 #endif /* _UAPI_IIO_TYPES_H_ */
-
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [v2 05/10] iio: add modifers for pitch, yaw, roll
  2021-10-28 10:18 ` [v2 00/10] Add support for Bosch BNO055 IMU Andrea Merello
                     ` (3 preceding siblings ...)
  2021-10-28 10:18   ` [v2 04/10] iio: add modifiers for linear acceleration Andrea Merello
@ 2021-10-28 10:18   ` Andrea Merello
  2021-10-28 10:47     ` Jonathan Cameron
  2021-10-28 10:18   ` [v2 06/10] iio: document bno055 private sysfs attributes Andrea Merello
                     ` (5 subsequent siblings)
  10 siblings, 1 reply; 55+ messages in thread
From: Andrea Merello @ 2021-10-28 10:18 UTC (permalink / raw)
  To: jic23, mchehab+huawei, linux-iio, linux-kernel, devicetree
  Cc: lars, robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex,
	jacopo, Andrea Merello, Andrea Merello

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

Signed-off-by: Andrea Merello <andrea.merello@iit.it>
---
 drivers/iio/industrialio-core.c | 5 ++++-
 include/uapi/linux/iio/types.h  | 3 +++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index a79cb32207e4..d2ebbfa8b9fc 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -136,7 +136,10 @@ static const char * const iio_modifier_names[] = {
 	[IIO_MOD_O2] = "o2",
 	[IIO_MOD_ACCEL_LINEAR_X] = "linear_x",
 	[IIO_MOD_ACCEL_LINEAR_Y] = "linear_y",
-	[IIO_MOD_ACCEL_LINEAR_Z] = "linear_z"
+	[IIO_MOD_ACCEL_LINEAR_Z] = "linear_z",
+	[IIO_MOD_PITCH] = "pitch",
+	[IIO_MOD_YAW] = "yaw",
+	[IIO_MOD_ROLL] = "roll"
 };
 
 /* relies on pairs of these shared then separate */
diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
index db00f7c45f48..fc9909ca4f95 100644
--- a/include/uapi/linux/iio/types.h
+++ b/include/uapi/linux/iio/types.h
@@ -98,6 +98,9 @@ enum iio_modifier {
 	IIO_MOD_ACCEL_LINEAR_X,
 	IIO_MOD_ACCEL_LINEAR_Y,
 	IIO_MOD_ACCEL_LINEAR_Z,
+	IIO_MOD_PITCH,
+	IIO_MOD_YAW,
+	IIO_MOD_ROLL
 };
 
 enum iio_event_type {
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [v2 06/10] iio: document bno055 private sysfs attributes
  2021-10-28 10:18 ` [v2 00/10] Add support for Bosch BNO055 IMU Andrea Merello
                     ` (4 preceding siblings ...)
  2021-10-28 10:18   ` [v2 05/10] iio: add modifers for pitch, yaw, roll Andrea Merello
@ 2021-10-28 10:18   ` Andrea Merello
  2021-10-28 11:04     ` Jonathan Cameron
  2021-10-28 10:18   ` [v2 07/10] iio: imu: add Bosch Sensortec BNO055 core driver Andrea Merello
                     ` (4 subsequent siblings)
  10 siblings, 1 reply; 55+ messages in thread
From: Andrea Merello @ 2021-10-28 10:18 UTC (permalink / raw)
  To: jic23, mchehab+huawei, linux-iio, linux-kernel, devicetree
  Cc: lars, robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex,
	jacopo, Andrea Merello, Andrea Merello

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

Signed-off-by: Andrea Merello <andrea.merello@iit.it>
---
 .../ABI/testing/sysfs-bus-iio-bno055          | 84 +++++++++++++++++++
 1 file changed, 84 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-bno055

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-bno055 b/Documentation/ABI/testing/sysfs-bus-iio-bno055
new file mode 100644
index 000000000000..930a70c5a858
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-bno055
@@ -0,0 +1,84 @@
+What:		/sys/bus/iio/devices/iio:deviceX/in_accel_range
+KernelVersion:	5.15
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Range for acceleration readings in G. 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_range
+KernelVersion:	5.15
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Range for angular velocity readings in dps. 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_range_available
+KernelVersion:	5.15
+Contact:	linux-iio@vger.kernel.org
+Description:
+		List of allowed values for in_accel_range attribute
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_range_available
+KernelVersion:	5.15
+Contact:	linux-iio@vger.kernel.org
+Description:
+		List of allowed values for in_anglvel_range attribute
+
+What:		/sys/bus/iio/devices/iio:deviceX/fast_magnetometer_calibration_enable
+KernelVersion:	5.15
+Contact:	linux-iio@vger.kernel.org
+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.15
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Can be 1 or 0. Enables/disables the "sensor fusion" (a.k.a.
+		NDOF) HW function.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_calibration_data
+KernelVersion:	5.15
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Reports the binary calibration data blob for the IMU sensors.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_autocalibration_status_accel
+KernelVersion:	5.15
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Can be "Idle", "Bad", "Barely enough", "Fair", or "Good".
+		Report the autocalibration status for the accelerometer sensor.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_autocalibration_status_gyro
+KernelVersion:	5.15
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Can be "Idle", "Bad", "Barely enough", "Fair", or "Good".
+		Reports the autocalibration status for the gyroscope sensor.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_autocalibration_status_magn
+KernelVersion:	5.15
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Can be "Idle", "Bad", "Barely enough", "Fair", or "Good".
+		Reports the autocalibration status for the magnetometer sensor.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_autocalibration_status_sys
+KernelVersion:	5.15
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Can be "Idle", "Bad", "Barely enough", "Fair", or "Good".
+		Reports the status for the IMU overall autocalibration.
+
+What:		/sys/bus/iio/devices/iio:deviceX/unique_id
+KernelVersion:	5.15
+Contact:	linux-iio@vger.kernel.org
+Description:
+		16-bytes, 2-digits-per-byte, HEX-string representing the sensor
+		unique ID number.
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [v2 07/10] iio: imu: add Bosch Sensortec BNO055 core driver
  2021-10-28 10:18 ` [v2 00/10] Add support for Bosch BNO055 IMU Andrea Merello
                     ` (5 preceding siblings ...)
  2021-10-28 10:18   ` [v2 06/10] iio: document bno055 private sysfs attributes Andrea Merello
@ 2021-10-28 10:18   ` Andrea Merello
  2021-10-28 13:31     ` Jonathan Cameron
  2021-10-28 10:18   ` [v2 08/10] dt-bindings: iio: imu: add documentation for Bosch BNO055 bindings Andrea Merello
                     ` (3 subsequent siblings)
  10 siblings, 1 reply; 55+ messages in thread
From: Andrea Merello @ 2021-10-28 10:18 UTC (permalink / raw)
  To: jic23, mchehab+huawei, linux-iio, linux-kernel, devicetree
  Cc: lars, robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex,
	jacopo, Andrea Merello, 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.

Signed-off-by: Andrea Merello <andrea.merello@iit.it>
---
 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 | 1480 +++++++++++++++++++++++++++++++
 drivers/iio/imu/bno055/bno055.h |   12 +
 6 files changed, 1501 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

diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
index 001ca2c3ff95..f1d7d4b5e222 100644
--- a/drivers/iio/imu/Kconfig
+++ b/drivers/iio/imu/Kconfig
@@ -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
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
index c82748096c77..6eb612034722 100644
--- a/drivers/iio/imu/Makefile
+++ b/drivers/iio/imu/Makefile
@@ -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
diff --git a/drivers/iio/imu/bno055/Kconfig b/drivers/iio/imu/bno055/Kconfig
new file mode 100644
index 000000000000..d197310661af
--- /dev/null
+++ b/drivers/iio/imu/bno055/Kconfig
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config BOSH_BNO055_IIO
+	tristate
diff --git a/drivers/iio/imu/bno055/Makefile b/drivers/iio/imu/bno055/Makefile
new file mode 100644
index 000000000000..c55741d0e96f
--- /dev/null
+++ b/drivers/iio/imu/bno055/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_BOSH_BNO055_IIO) += bno055.o
diff --git a/drivers/iio/imu/bno055/bno055.c b/drivers/iio/imu/bno055/bno055.c
new file mode 100644
index 000000000000..c85cb985f0f1
--- /dev/null
+++ b/drivers/iio/imu/bno055/bno055.c
@@ -0,0 +1,1480 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * IIO driver for Bosh BNO055 IMU
+ *
+ * Copyright (C) 2021 Istituto Italiano di Tecnologia
+ * Electronic Design Laboratory
+ * Written by Andrea Merello <andrea.merello@iit.it>
+ *
+ * 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/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_SYS_TRIGGER_REG		0x3F
+#define BNO055_SYS_TRIGGER_RST_INT BIT(6)
+#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_CALDATA_START		0x55
+#define BNO055_CALDATA_END		0x6A
+#define BNO055_CALDATA_LEN 22
+
+/*
+ * 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)
+#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
+
+static const int bno055_mag_odr_vals[] = {2, 6, 8, 10, 15, 20, 25, 30};
+/* the following one is INT_PLUS_MICRO */
+static const int bno055_acc_lpf_vals[] = {7, 810000, 15, 630000,
+					  31, 250000, 62, 500000, 125, 0,
+					  250, 0, 500, 0, 1000, 0};
+static const int bno055_acc_ranges[] = {2, 4, 8, 16};
+static const int bno055_gyr_lpf_vals[] = {523, 230, 116, 47, 23, 12, 64, 32};
+static const int bno055_gyr_ranges[] = {2000, 1000, 500, 250, 125};
+
+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 {
+		__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;
+
+	/* 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;
+	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
+	 * (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 */
+	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,
+	.readable_reg = bno055_regmap_readable,
+	.cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_GPL(bno055_regmap_config);
+
+static int bno055_reg_update_bits(struct bno055_priv *priv, unsigned int reg,
+				  unsigned int mask, unsigned int val)
+{
+	int ret;
+
+	ret = regmap_update_bits(priv->regmap, reg, mask, val);
+	if (ret && ret != -ERESTARTSYS) {
+		dev_err(priv->dev, "Regmap update_bits  error. adr: 0x%x, ret: %d",
+			reg,  ret);
+	}
+
+	return ret;
+}
+
+/* must be called in configuration mode */
+int bno055_calibration_load(struct bno055_priv *priv, const struct firmware *fw)
+{
+	if (fw->size != BNO055_CALDATA_LEN) {
+		dev_dbg(priv->dev, "Invalid calibration file size %d (expected %d)",
+			fw->size, BNO055_CALDATA_LEN);
+		return -EINVAL;
+	}
+
+	dev_dbg(priv->dev, "loading cal data: %*ph", BNO055_CALDATA_LEN, fw->data);
+	return regmap_bulk_write(priv->regmap, BNO055_CALDATA_START,
+				fw->data, BNO055_CALDATA_LEN);
+}
+
+static int bno055_init(struct bno055_priv *priv, const struct firmware *caldata)
+{
+	int ret;
+
+	ret = regmap_write(priv->regmap, BNO055_SYS_TRIGGER_REG,
+			   (priv->clk ? BNO055_SYS_TRIGGER_CLK_SEL : 0) |
+			   BNO055_SYS_TRIGGER_RST_INT);
+	if (ret)
+		return ret;
+
+	msleep(100);
+	ret = regmap_write(priv->regmap, BNO055_OPR_MODE_REG,
+			   BNO055_OPR_MODE_CONFIG);
+	if (ret)
+		return ret;
+
+	/* use standard SI units */
+	ret = regmap_write(priv->regmap, BNO055_UNIT_SEL_REG,
+			   BNO055_UNIT_SEL_ANDROID);
+	if (ret)
+		return ret;
+
+	if (caldata) {
+		ret = bno055_calibration_load(priv, caldata);
+		if (ret)
+			dev_warn(priv->dev, "failed to load calibration data with error %d",
+				 ret);
+	}
+
+	priv->operation_mode = BNO055_OPR_MODE_FUSION;
+	return regmap_write(priv->regmap, BNO055_OPR_MODE_REG,
+			    priv->operation_mode);
+}
+
+static void bno055_uninit(void *arg)
+{
+	struct bno055_priv *priv = arg;
+
+	/* stop the IMU */
+	regmap_write(priv->regmap, BNO055_OPR_MODE_REG, BNO055_OPR_MODE_CONFIG);
+}
+
+static void bno055_clk_disable(void *arg)
+{
+	struct bno055_priv *priv = arg;
+
+	clk_disable_unprepare(priv->clk);
+}
+
+#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)),
+	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)),
+	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)),
+	/* 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, ACCEL_LINEAR_X, BNO055_SCAN_LIA_X,
+		       BNO055_LIA_DATA_X_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_ACCEL, ACCEL_LINEAR_Y, BNO055_SCAN_LIA_Y,
+		       BNO055_LIA_DATA_Y_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_ACCEL, 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_acc_lpf(struct bno055_priv *priv, int *val, int *val2)
+{
+	const int shift = __ffs(BNO055_ACC_CONFIG_LPF_MASK);
+	int hwval, idx;
+	int ret;
+
+	ret = regmap_read(priv->regmap, BNO055_ACC_CONFIG_REG, &hwval);
+	if (ret)
+		return ret;
+
+	idx = (hwval & BNO055_ACC_CONFIG_LPF_MASK) >> shift;
+	*val = bno055_acc_lpf_vals[idx * 2];
+	*val2 = bno055_acc_lpf_vals[idx * 2 + 1];
+
+	return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int bno055_set_acc_lpf(struct bno055_priv *priv, int val, int val2)
+{
+	const int shift = __ffs(BNO055_ACC_CONFIG_LPF_MASK);
+	int req_val = val * 1000 + val2 / 1000;
+	bool first = true;
+	int best_delta;
+	int best_idx;
+	int tbl_val;
+	int delta;
+	int ret;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(bno055_acc_lpf_vals) / 2; i++) {
+		tbl_val = bno055_acc_lpf_vals[i * 2] * 1000 +
+			bno055_acc_lpf_vals[i * 2 + 1] / 1000;
+		delta = abs(tbl_val - req_val);
+		if (first || delta < best_delta) {
+			best_delta = delta;
+			best_idx = i;
+			first = false;
+		}
+	}
+
+	/*
+	 * 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;
+
+	ret = regmap_write(priv->regmap, BNO055_OPR_MODE_REG,
+			   BNO055_OPR_MODE_CONFIG);
+	if (ret)
+		return ret;
+
+	ret = bno055_reg_update_bits(priv, BNO055_ACC_CONFIG_REG,
+				     BNO055_ACC_CONFIG_LPF_MASK,
+				     best_idx << shift);
+
+	if (ret)
+		return ret;
+
+	return regmap_write(priv->regmap, BNO055_OPR_MODE_REG,
+			    BNO055_OPR_MODE_AMG);
+}
+
+static int bno055_get_regmask(struct bno055_priv *priv, int *val, int reg,
+			      int mask,  const int tbl[])
+{
+	const int shift = __ffs(mask);
+	int hwval, idx;
+	int ret;
+
+	ret = regmap_read(priv->regmap, reg, &hwval);
+	if (ret)
+		return ret;
+
+	idx = (hwval & mask) >> shift;
+	*val = tbl[idx];
+
+	return IIO_VAL_INT;
+}
+
+static int bno055_set_regmask(struct bno055_priv *priv, int val, int reg,
+			      int mask, const int table[], int table_len)
+
+{
+	int hwval = find_closest_unsorted(val, table, table_len);
+	const int shift = __ffs(mask);
+	int ret;
+	/*
+	 * 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;
+
+	ret = regmap_write(priv->regmap, BNO055_OPR_MODE_REG,
+			   BNO055_OPR_MODE_CONFIG);
+	if (ret)
+		return ret;
+
+	ret = bno055_reg_update_bits(priv, reg, mask, hwval << shift);
+
+	if (ret)
+		return ret;
+
+	return regmap_write(priv->regmap, BNO055_OPR_MODE_REG,
+			    BNO055_OPR_MODE_AMG);
+}
+
+#define bno055_get_mag_odr(p, v) \
+	bno055_get_regmask(p, v, \
+			   BNO055_MAG_CONFIG_REG, BNO055_MAG_CONFIG_ODR_MASK, \
+			   bno055_mag_odr_vals)
+
+#define bno055_set_mag_odr(p, v) \
+	bno055_set_regmask(p, v, \
+			   BNO055_MAG_CONFIG_REG, BNO055_MAG_CONFIG_ODR_MASK, \
+			   bno055_mag_odr_vals, \
+			   ARRAY_SIZE(bno055_mag_odr_vals))
+
+#define bno055_get_acc_range(p, v) \
+	bno055_get_regmask(priv, v, \
+			   BNO055_ACC_CONFIG_REG, BNO055_ACC_CONFIG_RANGE_MASK, \
+			   bno055_acc_ranges)
+
+#define bno055_set_acc_range(p, v) \
+	bno055_set_regmask(p, v, \
+			   BNO055_ACC_CONFIG_REG, \
+			   BNO055_ACC_CONFIG_RANGE_MASK,  \
+			   bno055_acc_ranges, ARRAY_SIZE(bno055_acc_ranges))
+
+#define bno055_get_gyr_lpf(p, v) \
+	bno055_get_regmask(p, v, \
+			   BNO055_GYR_CONFIG_REG, BNO055_GYR_CONFIG_LPF_MASK, \
+			   bno055_gyr_lpf_vals)
+
+#define bno055_set_gyr_lpf(p, v) \
+	bno055_set_regmask(p, v, \
+			   BNO055_GYR_CONFIG_REG, BNO055_GYR_CONFIG_LPF_MASK, \
+			   bno055_gyr_lpf_vals, \
+			   ARRAY_SIZE(bno055_gyr_lpf_vals))
+
+#define bno055_get_gyr_range(p, v) \
+	bno055_get_regmask(p, v, \
+			   BNO055_GYR_CONFIG_REG, BNO055_GYR_CONFIG_RANGE_MASK, \
+			   bno055_gyr_ranges)
+
+#define bno055_set_gyr_range(p, v) \
+	bno055_set_regmask(p, v, \
+			   BNO055_GYR_CONFIG_REG, \
+			   BNO055_GYR_CONFIG_RANGE_MASK, \
+			   bno055_gyr_ranges, ARRAY_SIZE(bno055_gyr_ranges))
+
+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 */
+			*val2 = 900;
+			break;
+		case IIO_ROT:
+			/* Table 3-28: 1 degree = 16 LSB */
+			*val2 = 16;
+			break;
+		default:
+			return -EINVAL;
+		}
+		return IIO_VAL_FRACTIONAL;
+	default:
+		return -EINVAL;
+
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		if (chan->type != IIO_MAGN)
+			return -EINVAL;
+		else
+			return bno055_get_mag_odr(priv, val);
+
+	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			return bno055_get_gyr_lpf(priv, val);
+		case IIO_ACCEL:
+			return bno055_get_acc_lpf(priv, val, val2);
+		default:
+			return -EINVAL;
+		}
+	}
+}
+
+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_LOW_PASS_FILTER_3DB_FREQUENCY:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			if (priv->operation_mode != BNO055_OPR_MODE_AMG) {
+				/* locked on 32 */
+				*vals = bno055_gyr_lpf_vals + 7;
+				*length = 1;
+			} else {
+				*vals = bno055_gyr_lpf_vals;
+				*length = ARRAY_SIZE(bno055_gyr_lpf_vals);
+			}
+			*type =  IIO_VAL_INT;
+			return IIO_AVAIL_LIST;
+		default:
+			return -EINVAL;
+		case IIO_ACCEL:
+			if (priv->operation_mode != BNO055_OPR_MODE_AMG) {
+				/* locked on 62.5Hz  */
+				*vals = bno055_acc_lpf_vals + 6;
+				*length = 2;
+			} else {
+				*vals = bno055_acc_lpf_vals;
+				*length = ARRAY_SIZE(bno055_acc_lpf_vals);
+			}
+			*type = IIO_VAL_INT_PLUS_MICRO;
+			return IIO_AVAIL_LIST;
+		}
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		switch (chan->type) {
+		case IIO_MAGN:
+			if (priv->operation_mode != BNO055_OPR_MODE_AMG) {
+				/* locked on 20Hz */
+				*vals = bno055_mag_odr_vals + 5;
+				*length = 1;
+			} else {
+				*vals = bno055_mag_odr_vals;
+				*length = ARRAY_SIZE(bno055_mag_odr_vals);
+			}
+			*type =  IIO_VAL_INT;
+			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;
+	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_mag_odr(priv, val);
+
+		default:
+			return -EINVAL;
+		}
+	case IIO_ACCEL:
+		switch (mask) {
+		case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+			return bno055_set_acc_lpf(priv, val, val2);
+
+		default:
+			return -EINVAL;
+		}
+	case IIO_ANGL_VEL:
+		switch (mask) {
+		case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+			return bno055_set_gyr_lpf(priv, val);
+
+		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_available_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, "%s\n",
+			  priv->operation_mode != BNO055_OPR_MODE_AMG ? "4" :
+			 "2 4 8 16");
+}
+
+static ssize_t in_anglvel_range_available_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, "%s\n",
+			 (priv->operation_mode != BNO055_OPR_MODE_AMG) ? "2000" :
+			 "125 250 500 1000 2000");
+}
+
+static ssize_t bno055_operation_mode_set(struct bno055_priv *priv,
+					 int operation_mode)
+{
+	int ret;
+
+	mutex_lock(&priv->lock);
+	priv->operation_mode = operation_mode;
+	ret = regmap_write(priv->regmap, BNO055_OPR_MODE_REG,
+			   BNO055_OPR_MODE_CONFIG);
+	if (ret) {
+		mutex_unlock(&priv->lock);
+		return ret;
+	}
+
+	ret = regmap_write(priv->regmap, BNO055_OPR_MODE_REG, priv->operation_mode);
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+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)
+{
+	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.
+		 * 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);
+	}
+
+	return len ?: 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 = 0;
+
+	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);
+	} else {
+		if (priv->operation_mode == BNO055_OPR_MODE_AMG)
+			return -EINVAL;
+	}
+
+	return len ?: ret;
+}
+
+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_acc_range(priv, &val);
+	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_acc_range(priv, val);
+	mutex_unlock(&priv->lock);
+
+	return ret ?: len;
+}
+
+static ssize_t bno055_in_gyr_range_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	int val;
+	int ret;
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+
+	ret = bno055_get_gyr_range(priv, &val);
+	if (ret < 0)
+		return ret;
+
+	return sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t bno055_in_gyr_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_gyr_range(priv, val);
+	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));
+	static const char * const calib_status[] = {
+		"bad", "barely enough", "fair", "good" };
+	const char *calib_str;
+	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_str = "idle";
+	} else {
+		mutex_lock(&priv->lock);
+		ret = regmap_read(priv->regmap, BNO055_CALIB_STAT_REG, &val);
+		mutex_unlock(&priv->lock);
+
+		if (ret)
+			return -EIO;
+
+		val = (val >> which) & BNO055_CALIB_STAT_MASK;
+		calib_str = calib_status[val];
+	}
+
+	return sysfs_emit(buf, "%s\n", calib_str);
+}
+
+static ssize_t unique_id_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 in_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 = regmap_write(priv->regmap, BNO055_OPR_MODE_REG,
+			   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 = regmap_write(priv->regmap, BNO055_OPR_MODE_REG, priv->operation_mode);
+	mutex_unlock(&priv->lock);
+	if (ret)
+		return ret;
+
+	memcpy(buf, data, BNO055_CALDATA_LEN);
+
+	return BNO055_CALDATA_LEN;
+unlock:
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static ssize_t in_autocalibration_status_sys_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_autocalibration_status_accel_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_autocalibration_status_gyro_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_autocalibration_status_magn_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 = devm_kasprintf(priv->dev, 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));
+	devm_kfree(priv->dev, 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(fast_magnetometer_calibration_enable, 0644,
+		       bno055_fmc_enable_show,
+		       bno055_fmc_enable_store, 0);
+
+static IIO_DEVICE_ATTR(in_accel_range, 0644,
+		       bno055_in_accel_range_show,
+		       bno055_in_accel_range_store, 0);
+
+static IIO_DEVICE_ATTR_RO(in_accel_range_available, 0);
+
+static IIO_DEVICE_ATTR(in_anglvel_range, 0644,
+		       bno055_in_gyr_range_show,
+		       bno055_in_gyr_range_store, 0);
+
+static IIO_DEVICE_ATTR_RO(in_anglvel_range_available, 0);
+
+static IIO_DEVICE_ATTR_RO(in_autocalibration_status_sys, 0);
+static IIO_DEVICE_ATTR_RO(in_autocalibration_status_accel, 0);
+static IIO_DEVICE_ATTR_RO(in_autocalibration_status_gyro, 0);
+static IIO_DEVICE_ATTR_RO(in_autocalibration_status_magn, 0);
+static IIO_DEVICE_ATTR_RO(in_calibration_data, 0);
+
+static IIO_DEVICE_ATTR_RO(unique_id, 0);
+
+static struct attribute *bno055_attrs[] = {
+	&iio_dev_attr_in_accel_range_available.dev_attr.attr,
+	&iio_dev_attr_in_accel_range.dev_attr.attr,
+	&iio_dev_attr_in_anglvel_range_available.dev_attr.attr,
+	&iio_dev_attr_in_anglvel_range.dev_attr.attr,
+	&iio_dev_attr_fusion_enable.dev_attr.attr,
+	&iio_dev_attr_fast_magnetometer_calibration_enable.dev_attr.attr,
+	&iio_dev_attr_in_autocalibration_status_sys.dev_attr.attr,
+	&iio_dev_attr_in_autocalibration_status_accel.dev_attr.attr,
+	&iio_dev_attr_in_autocalibration_status_gyro.dev_attr.attr,
+	&iio_dev_attr_in_autocalibration_status_magn.dev_attr.attr,
+	&iio_dev_attr_in_calibration_data.dev_attr.attr,
+	&iio_dev_attr_unique_id.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;
+
+	/*
+	 * 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;
+	bool xfer_pending = false;
+	bool first = true;
+	unsigned long mask;
+	int buf_idx = 0;
+	bool thr_hit;
+	int quat;
+	int ret;
+
+	mutex_lock(&priv->lock);
+	for_each_set_bitrange(start, end, iio_dev->active_scan_mask,
+			      iio_dev->masklength) {
+		if (!xfer_pending)
+			xfer_start = start;
+		xfer_pending = true;
+
+		if (!first) {
+			quat = ((start > BNO055_SCAN_QUATERNION) &&
+				(prev_end <= BNO055_SCAN_QUATERNION)) ? 3 : 0;
+			thr_hit = (start - prev_end + quat) >
+				priv->xfer_burst_break_thr;
+
+			if (thr_hit) {
+				mask = *iio_dev->active_scan_mask >> xfer_start;
+				ret = bno055_scan_xfer(priv, xfer_start,
+						       prev_end - xfer_start + 1,
+						       mask, priv->buf.chans, &buf_idx);
+				if (ret)
+					goto done;
+				xfer_pending = false;
+			}
+			first = false;
+		}
+		prev_end = end;
+	}
+
+	if (xfer_pending) {
+		mask = *iio_dev->active_scan_mask >> xfer_start;
+		ret = bno055_scan_xfer(priv, xfer_start,
+				       end - xfer_start + 1,
+				       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;
+}
+
+int bno055_probe(struct device *dev, struct regmap *regmap,
+		 int xfer_burst_break_thr)
+{
+	const struct firmware *caldata;
+	struct bno055_priv *priv;
+	struct iio_dev *iio_dev;
+	struct gpio_desc *rst;
+	char *fw_name_buf;
+	unsigned int val;
+	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;
+	rst = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(rst))
+		return dev_err_probe(dev, PTR_ERR(rst), "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);
+	if (ret)
+		return ret;
+
+	if (rst) {
+		usleep_range(5000, 10000);
+		gpiod_set_value_cansleep(rst, 0);
+		usleep_range(650000, 750000);
+	}
+
+	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;
+	}
+
+	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 = devm_kasprintf(dev, 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);
+	devm_kfree(dev, fw_name_buf);
+	if (ret)
+		ret = request_firmware(&caldata, BNO055_FW_GENERIC_NAME, dev);
+
+	if (ret) {
+		dev_notice(dev, "Failed to load calibration data firmware file; this has nothing to do with IMU main firmware.\nYou can calibrate your IMU (look for 'in_autocalibration_status*' files in sysfs) and then copy 'in_calibration_data' to your firmware file");
+		caldata = NULL;
+	}
+
+	ret = bno055_init(priv, caldata);
+	if (caldata)
+		release_firmware(caldata);
+	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 <andrea.merello@iit.it>");
+MODULE_DESCRIPTION("Bosch BNO055 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/imu/bno055/bno055.h b/drivers/iio/imu/bno055/bno055.h
new file mode 100644
index 000000000000..7ad8da1ffbf0
--- /dev/null
+++ b/drivers/iio/imu/bno055/bno055.h
@@ -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);
+extern const struct regmap_config bno055_regmap_config;
+
+#endif
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [v2 08/10] dt-bindings: iio: imu: add documentation for Bosch BNO055 bindings
  2021-10-28 10:18 ` [v2 00/10] Add support for Bosch BNO055 IMU Andrea Merello
                     ` (6 preceding siblings ...)
  2021-10-28 10:18   ` [v2 07/10] iio: imu: add Bosch Sensortec BNO055 core driver Andrea Merello
@ 2021-10-28 10:18   ` Andrea Merello
  2021-10-28 12:25     ` Rob Herring
  2021-10-28 10:18   ` [v2 09/10] iio: imu: add BNO055 serdev driver Andrea Merello
                     ` (2 subsequent siblings)
  10 siblings, 1 reply; 55+ messages in thread
From: Andrea Merello @ 2021-10-28 10:18 UTC (permalink / raw)
  To: jic23, mchehab+huawei, linux-iio, linux-kernel, devicetree
  Cc: lars, robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex,
	jacopo, Andrea Merello, Andrea Merello

Introduce new documentation file for the Bosch BNO055 IMU

Signed-off-by: Andrea Merello <andrea.merello@iit.it>
---
 .../bindings/iio/imu/bosch,bno055.yaml        | 59 +++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml

diff --git a/Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml b/Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml
new file mode 100644
index 000000000000..0c0141162d63
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml
@@ -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-serial.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Bosch BNO055
+
+maintainers:
+  - Andrea Merello <andrea.merello@iit.it>
+
+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>;
+     };
+   };
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [v2 09/10] iio: imu: add BNO055 serdev driver
  2021-10-28 10:18 ` [v2 00/10] Add support for Bosch BNO055 IMU Andrea Merello
                     ` (7 preceding siblings ...)
  2021-10-28 10:18   ` [v2 08/10] dt-bindings: iio: imu: add documentation for Bosch BNO055 bindings Andrea Merello
@ 2021-10-28 10:18   ` Andrea Merello
  2021-10-28 12:31     ` Jonathan Cameron
                       ` (2 more replies)
  2021-10-28 10:18   ` [v2 10/10] iio: imu: add BNO055 I2C driver Andrea Merello
  2021-10-28 10:35   ` [v2 00/10] Add support for Bosch BNO055 IMU Jonathan Cameron
  10 siblings, 3 replies; 55+ messages in thread
From: Andrea Merello @ 2021-10-28 10:18 UTC (permalink / raw)
  To: jic23, mchehab+huawei, linux-iio, linux-kernel, devicetree
  Cc: lars, robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex,
	jacopo, Andrea Merello, 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.

Signed-off-by: Andrea Merello <andrea.merello@iit.it>
---
 drivers/iio/imu/bno055/Kconfig     |   5 +
 drivers/iio/imu/bno055/Makefile    |   1 +
 drivers/iio/imu/bno055/bno055_sl.c | 568 +++++++++++++++++++++++++++++
 3 files changed, 574 insertions(+)
 create mode 100644 drivers/iio/imu/bno055/bno055_sl.c

diff --git a/drivers/iio/imu/bno055/Kconfig b/drivers/iio/imu/bno055/Kconfig
index d197310661af..941e43f0368d 100644
--- a/drivers/iio/imu/bno055/Kconfig
+++ b/drivers/iio/imu/bno055/Kconfig
@@ -2,3 +2,8 @@
 
 config BOSH_BNO055_IIO
 	tristate
+
+config BOSH_BNO055_SERIAL
+	tristate "Bosh BNO055 attached via serial bus"
+	depends on SERIAL_DEV_BUS
+	select BOSH_BNO055_IIO
diff --git a/drivers/iio/imu/bno055/Makefile b/drivers/iio/imu/bno055/Makefile
index c55741d0e96f..7285ade2f4b9 100644
--- a/drivers/iio/imu/bno055/Makefile
+++ b/drivers/iio/imu/bno055/Makefile
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0
 
 obj-$(CONFIG_BOSH_BNO055_IIO) += bno055.o
+obj-$(CONFIG_BOSH_BNO055_SERIAL) += bno055_sl.o
diff --git a/drivers/iio/imu/bno055/bno055_sl.c b/drivers/iio/imu/bno055/bno055_sl.c
new file mode 100644
index 000000000000..1d1410fdaa7c
--- /dev/null
+++ b/drivers/iio/imu/bno055/bno055_sl.c
@@ -0,0 +1,568 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Serial line interface for Bosh BNO055 IMU (via serdev).
+ * This file implements serial communication up to the register read/write
+ * level.
+ *
+ * Copyright (C) 2021 Istituto Italiano di Tecnologia
+ * Electronic Design Laboratory
+ * Written by Andrea Merello <andrea.merello@iit.it>
+ *
+ * This driver is besed on
+ *	Plantower PMS7003 particulate matter sensor driver
+ *	Which is
+ *	Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
+ */
+
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/serdev.h>
+
+#include "bno055.h"
+
+/*
+ * Register writes cmd have the following format
+ * +------+------+-----+-----+----- ... ----+
+ * | 0xAA | 0xOO | REG | LEN | payload[LEN] |
+ * +------+------+-----+-----+----- ... ----+
+ *
+ * Register write responses have the following format
+ * +------+----------+
+ * | 0xEE | ERROCODE |
+ * +------+----------+
+ *
+ * Register read have the following format
+ * +------+------+-----+-----+
+ * | 0xAA | 0xO1 | REG | LEN |
+ * +------+------+-----+-----+
+ *
+ * Successful register read response have the following format
+ * +------+-----+----- ... ----+
+ * | 0xBB | LEN | payload[LEN] |
+ * +------+-----+----- ... ----+
+ *
+ * Failed register read response have the following format
+ * +------+--------+
+ * | 0xEE | ERRCODE|  (ERRCODE always > 1)
+ * +------+--------+
+ *
+ * Error codes are
+ * 01: OK
+ * 02: read/write FAIL
+ * 04: invalid address
+ * 05: write on RO
+ * 06: wrong start byte
+ * 07: bus overrun
+ * 08: len too high
+ * 09: len too low
+ * 10: bus RX byte timeout (timeout is 30mS)
+ *
+ *
+ * **WORKAROUND ALERT**
+ *
+ * Serial communication seems very fragile: the BNO055 buffer seems to overflow
+ * very easy; BNO055 seems able to sink few bytes, then it needs a brief pause.
+ * On the other hand, it is also picky on timeout: if there is a pause > 30mS in
+ * between two bytes then the transaction fails (IMU internal RX FSM resets).
+ *
+ * BMU055 has been seen also failing to process commands in case we send them
+ * too close each other (or if it is somehow busy?)
+ *
+ * One idea would be to split data in chunks, and then wait 1-2mS between
+ * chunks (we hope not to exceed 30mS delay for any reason - which should
+ * be pretty a lot of time for us), and eventually retry in case the BNO055
+ * gets upset for any reason. This seems to work in avoiding the overflow
+ * errors, but indeed it seems slower than just perform a retry when an overflow
+ * error occur.
+ * In particular I saw these scenarios:
+ * 1) If we send 2 bytes per time, then the IMU never(?) overflows.
+ * 2) If we send 4 bytes per time (i.e. the full header), then the IMU could
+ *    overflow, but it seem to sink all 4 bytes, then it returns error.
+ * 3) If we send more than 4 bytes, the IMU could overflow, and I saw it sending
+ *    error after 4 bytes are sent; we have troubles in synchronizing again,
+ *    because we are still sending data, and the IMU interprets it as the 1st
+ *    byte of a new command.
+ *
+ * So, we workaround all this in the following way:
+ * In case of read we don't split the header but we rely on retries; This seems
+ * convenient for data read (where we TX only the hdr).
+ * For TX we split the transmission in 2-bytes chunks so that, we should not
+ * only avoid case 2 (which is still manageable), but we also hopefully avoid
+ * case 3, that would be by far worse.
+ */
+
+/*
+ * Read operation overhead:
+ *  4 bytes req + 2byte resp hdr.
+ *  6 bytes = 60 bit (considering 1start + 1stop bits).
+ *  60/115200 = ~520uS.
+ *
+ * In 520uS we could read back about 34 bytes that means 3 samples, this means
+ * that in case of scattered read in which the gap is 3 samples or less it is
+ * still convenient to go for a burst.
+ * We have to take into account also IMU response time - IMU seems to be often
+ * reasonably quick to respond, but sometimes it seems to be in some "critical
+ * section" in which it delays handling of serial protocol.
+ * By experiment, it seems convenient to burst up to about 5/6-samples-long gap.
+ */
+
+#define BNO055_SL_XFER_BURST_BREAK_THRESHOLD 6
+
+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;
+
+	/**
+	 * 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
+	} cmd_status;
+	struct mutex lock;
+
+	/* Only accessed in behalf of RX callback context. No lock needed. */
+	struct {
+		enum {
+			RX_IDLE,
+			RX_START,
+			RX_DATA
+		} state;
+		int databuf_count;
+		int expected_len;
+		int type;
+	} rx;
+
+	/* Never accessed in behalf of RX callback context. No lock needed */
+	bool cmd_stale;
+};
+
+static int bno055_sl_send_chunk(struct bno055_sl_priv *priv, u8 *data, int len)
+{
+	int ret;
+
+	dev_dbg(&priv->serdev->dev, "send (len: %d): %*ph", len, len, data);
+	ret = serdev_device_write(priv->serdev, data, len,
+				  msecs_to_jiffies(25));
+	if (ret < 0)
+		return ret;
+
+	if (ret < len)
+		return -EIO;
+
+	return 0;
+}
+
+/*
+ * Sends a read or write command.
+ * 'data' can be NULL (used in read case). 'len' parameter is always valid; in
+ * case 'data' is non-NULL then it must match 'data' size.
+ */
+static int bno055_sl_do_send_cmd(struct bno055_sl_priv *priv,
+				 int read, int addr, int len, u8 *data)
+{
+	int ret;
+	int chunk_len;
+	u8 hdr[] = {0xAA, !!read, addr, len};
+
+	if (read) {
+		ret = bno055_sl_send_chunk(priv, hdr, 4);
+	} else {
+		ret = bno055_sl_send_chunk(priv, hdr, 2);
+		if (ret)
+			goto fail;
+
+		usleep_range(2000, 3000);
+		ret = bno055_sl_send_chunk(priv, hdr + 2, 2);
+	}
+	if (ret)
+		goto fail;
+
+	if (data) {
+		while (len) {
+			chunk_len = min(len, 2);
+			usleep_range(2000, 3000);
+			ret = bno055_sl_send_chunk(priv, data, chunk_len);
+			if (ret)
+				goto fail;
+			data += chunk_len;
+			len -= chunk_len;
+		}
+	}
+
+	return 0;
+fail:
+	/* waiting more than 30mS should clear the BNO055 internal state */
+	usleep_range(40000, 50000);
+	return ret;
+}
+
+static int bno_sl_send_cmd(struct bno055_sl_priv *priv,
+			   int read, int addr, int len, u8 *data)
+{
+	const int retry_max = 5;
+	int retry = retry_max;
+	int ret = 0;
+
+	/*
+	 * In case previous command was interrupted we still neet to wait it to
+	 * complete before we can issue new commands
+	 */
+	if (priv->cmd_stale) {
+		ret = wait_for_completion_interruptible_timeout(&priv->cmd_complete,
+								msecs_to_jiffies(100));
+		if (ret == -ERESTARTSYS)
+			return -ERESTARTSYS;
+
+		priv->cmd_stale = false;
+		/* if serial protocol broke, bail out */
+		if (priv->cmd_status == STATUS_CRIT)
+			goto exit;
+	}
+
+	/*
+	 * Try to convince the IMU to cooperate.. as explained in the comments
+	 * at the top of this file, the IMU could also refuse the command (i.e.
+	 * it is not ready yet); retry in this case.
+	 */
+	while (retry--) {
+		mutex_lock(&priv->lock);
+		priv->expect_response = read ? CMD_READ : CMD_WRITE;
+		reinit_completion(&priv->cmd_complete);
+		mutex_unlock(&priv->lock);
+
+		if (retry != (retry_max - 1))
+			dev_dbg(&priv->serdev->dev, "cmd retry: %d",
+				retry_max - retry);
+		ret = bno055_sl_do_send_cmd(priv, read, addr, len, data);
+		if (ret)
+			continue;
+
+		ret = wait_for_completion_interruptible_timeout(&priv->cmd_complete,
+								msecs_to_jiffies(100));
+		if (ret == -ERESTARTSYS) {
+			priv->cmd_stale = true;
+			return -ERESTARTSYS;
+		} else if (!ret) {
+			ret = -ETIMEDOUT;
+			break;
+		}
+		ret = 0;
+
+		/*
+		 * Poll if the IMU returns error (i.e busy), break if the IMU
+		 * returns OK or if the serial communication broke
+		 */
+		if (priv->cmd_status <= 0)
+			break;
+	}
+
+exit:
+	if (ret)
+		return ret;
+	if (priv->cmd_status == STATUS_CRIT)
+		return -EIO;
+	if (priv->cmd_status == STATUS_FAIL)
+		return -EINVAL;
+	return 0;
+}
+
+static int bno055_sl_write_reg(void *context, const void *data, size_t count)
+{
+	int ret;
+	int reg;
+	u8 *write_data = (u8 *)data + 1;
+	struct bno055_sl_priv *priv = context;
+
+	if (count < 2) {
+		dev_err(&priv->serdev->dev, "Invalid write count %zu", count);
+		return -EINVAL;
+	}
+
+	reg = ((u8 *)data)[0];
+	dev_dbg(&priv->serdev->dev, "wr reg 0x%x = 0x%x", reg, ((u8 *)data)[1]);
+	ret = bno_sl_send_cmd(priv, 0, reg, count - 1, write_data);
+
+	return ret;
+}
+
+static int bno055_sl_read_reg(void *context,
+			      const void *reg, size_t reg_size,
+			      void *val, size_t val_size)
+{
+	int ret;
+	int reg_addr;
+	struct bno055_sl_priv *priv = context;
+
+	if (val_size > 128) {
+		dev_err(&priv->serdev->dev, "Invalid read valsize %d",
+			val_size);
+		return -EINVAL;
+	}
+
+	reg_addr = ((u8 *)reg)[0];
+	dev_dbg(&priv->serdev->dev, "rd reg 0x%x (len %d)", reg_addr, val_size);
+	mutex_lock(&priv->lock);
+	priv->expected_data_len = val_size;
+	priv->response_buf = val;
+	mutex_unlock(&priv->lock);
+
+	ret = bno_sl_send_cmd(priv, 1, reg_addr, val_size, NULL);
+
+	mutex_lock(&priv->lock);
+	priv->response_buf = NULL;
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+/*
+ * Handler for received data; this is called from the reicever callback whenever
+ * it got some packet from the serial bus. The status tell us whether the
+ * packet is valid (i.e. header ok && received payload len consistent wrt the
+ * header). It's now our responsability to check whether this is what we
+ * expected, of whether we got some unexpected, yet valid, packet.
+ */
+static void bno055_sl_handle_rx(struct bno055_sl_priv *priv, int status)
+{
+	mutex_lock(&priv->lock);
+	switch (priv->expect_response) {
+	case CMD_NONE:
+		dev_warn(&priv->serdev->dev, "received unexpected, yet valid, data from sensor");
+		mutex_unlock(&priv->lock);
+		return;
+
+	case CMD_READ:
+		priv->cmd_status = status;
+		if (status == STATUS_OK &&
+		    priv->rx.databuf_count != priv->expected_data_len) {
+			/*
+			 * If we got here, then the lower layer serial protocol
+			 * seems consistent with itself; if we got an unexpected
+			 * amount of data then signal it as a non critical error
+			 */
+			priv->cmd_status = STATUS_FAIL;
+			dev_warn(&priv->serdev->dev, "received an unexpected amount of, yet valid, data from sensor");
+		}
+		break;
+
+	case CMD_WRITE:
+		priv->cmd_status = status;
+		break;
+	}
+
+	priv->expect_response = CMD_NONE;
+	complete(&priv->cmd_complete);
+	mutex_unlock(&priv->lock);
+}
+
+/*
+ * Serdev receiver FSM. This tracks the serial communication and parse the
+ * header. It pushes packets to bno055_sl_handle_rx(), eventually communicating
+ * failures (i.e. malformed packets).
+ * Ideally it doesn't know anything about upper layer (i.e. if this is the
+ * packet we were really expecting), but since we copies the payload into the
+ * receiver buffer (that is not valid when i.e. we don't expect data), we
+ * snoop a bit in the upper layer..
+ * Also, we assume to RX one pkt per time (i.e. the HW doesn't send anything
+ * unless we require to AND we don't queue more than one request per time).
+ */
+static int bno055_sl_receive_buf(struct serdev_device *serdev,
+				 const unsigned char *buf, size_t size)
+{
+	int status;
+	struct bno055_sl_priv *priv = serdev_device_get_drvdata(serdev);
+	int _size = size;
+
+	if (size == 0)
+		return 0;
+
+	dev_dbg(&priv->serdev->dev, "recv (len %zu): %*ph ", size, size, buf);
+	switch (priv->rx.state) {
+	case RX_IDLE:
+		/*
+		 * New packet.
+		 * Check for its 1st byte, that identifies the pkt type.
+		 */
+		if (buf[0] != 0xEE && buf[0] != 0xBB) {
+			dev_err(&priv->serdev->dev,
+				"Invalid packet start %x", buf[0]);
+			bno055_sl_handle_rx(priv, STATUS_CRIT);
+			break;
+		}
+		priv->rx.type = buf[0];
+		priv->rx.state = RX_START;
+		size--;
+		buf++;
+		priv->rx.databuf_count = 0;
+		fallthrough;
+
+	case RX_START:
+		/*
+		 * Packet RX in progress, we expect either 1-byte len or 1-byte
+		 * status depending by the packet type.
+		 */
+		if (size == 0)
+			break;
+
+		if (priv->rx.type == 0xEE) {
+			if (size > 1) {
+				dev_err(&priv->serdev->dev, "EE pkt. Extra data received");
+				status = STATUS_CRIT;
+
+			} else {
+				status = (buf[0] == 1) ? STATUS_OK : STATUS_FAIL;
+			}
+			bno055_sl_handle_rx(priv, status);
+			priv->rx.state = RX_IDLE;
+			break;
+
+		} else {
+			/*priv->rx.type == 0xBB */
+			priv->rx.state = RX_DATA;
+			priv->rx.expected_len = buf[0];
+			size--;
+			buf++;
+		}
+		fallthrough;
+
+	case RX_DATA:
+		/* Header parsed; now receiving packet data payload */
+		if (size == 0)
+			break;
+
+		if (priv->rx.databuf_count + size > priv->rx.expected_len) {
+			/*
+			 * This is a inconsistency in serial protocol, we lost
+			 * sync and we don't know how to handle further data
+			 */
+			dev_err(&priv->serdev->dev, "BB pkt. Extra data received");
+			bno055_sl_handle_rx(priv, STATUS_CRIT);
+			priv->rx.state = RX_IDLE;
+			break;
+		}
+
+		mutex_lock(&priv->lock);
+		/*
+		 * NULL e.g. when read cmd is stale or when no read cmd is
+		 * actually pending.
+		 */
+		if (priv->response_buf &&
+		    /*
+		     * Snoop on the upper layer protocol stuff to make sure not
+		     * to write to an invalid memory. Apart for this, let's the
+		     * upper layer manage any inconsistency wrt expected data
+		     * len (as long as the serial protocol is consistent wrt
+		     * itself (i.e. response header is consistent with received
+		     * response len.
+		     */
+		    (priv->rx.databuf_count + size <= priv->expected_data_len))
+			memcpy(priv->response_buf + priv->rx.databuf_count,
+			       buf, size);
+		mutex_unlock(&priv->lock);
+
+		priv->rx.databuf_count += size;
+
+		/*
+		 * Reached expected len advertised by the IMU for the current
+		 * packet. Pass it to the upper layer (for us it is just valid).
+		 */
+		if (priv->rx.databuf_count == priv->rx.expected_len) {
+			bno055_sl_handle_rx(priv, STATUS_OK);
+			priv->rx.state = RX_IDLE;
+		}
+		break;
+	}
+
+	return _size;
+}
+
+static const struct serdev_device_ops bno055_sl_serdev_ops = {
+	.receive_buf = bno055_sl_receive_buf,
+	.write_wakeup = serdev_device_write_wakeup,
+};
+
+static struct regmap_bus bno055_sl_regmap_bus = {
+	.write = bno055_sl_write_reg,
+	.read = bno055_sl_read_reg,
+};
+
+static int bno055_sl_probe(struct serdev_device *serdev)
+{
+	struct bno055_sl_priv *priv;
+	struct regmap *regmap;
+	int ret;
+
+	priv = devm_kzalloc(&serdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	serdev_device_set_drvdata(serdev, priv);
+	priv->serdev = serdev;
+	mutex_init(&priv->lock);
+	init_completion(&priv->cmd_complete);
+
+	serdev_device_set_client_ops(serdev, &bno055_sl_serdev_ops);
+	ret = devm_serdev_device_open(&serdev->dev, serdev);
+	if (ret)
+		return ret;
+
+	if (serdev_device_set_baudrate(serdev, 115200) != 115200) {
+		dev_err(&serdev->dev, "Cannot set required baud rate");
+		return -EIO;
+	}
+
+	ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
+	if (ret) {
+		dev_err(&serdev->dev, "Cannot set required parity setting");
+		return ret;
+	}
+	serdev_device_set_flow_control(serdev, false);
+
+	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 bno055_probe(&serdev->dev, regmap,
+			    BNO055_SL_XFER_BURST_BREAK_THRESHOLD);
+}
+
+static const struct of_device_id bno055_sl_of_match[] = {
+	{ .compatible = "bosch,bno055" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, bno055_sl_of_match);
+
+static struct serdev_device_driver bno055_sl_driver = {
+	.driver = {
+		.name = "bno055-sl",
+		.of_match_table = bno055_sl_of_match,
+	},
+	.probe = bno055_sl_probe,
+};
+module_serdev_device_driver(bno055_sl_driver);
+
+MODULE_AUTHOR("Andrea Merello <andrea.merello@iit.it>");
+MODULE_DESCRIPTION("Bosch BNO055 serdev interface");
+MODULE_LICENSE("GPL v2");
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* [v2 10/10] iio: imu: add BNO055 I2C driver
  2021-10-28 10:18 ` [v2 00/10] Add support for Bosch BNO055 IMU Andrea Merello
                     ` (8 preceding siblings ...)
  2021-10-28 10:18   ` [v2 09/10] iio: imu: add BNO055 serdev driver Andrea Merello
@ 2021-10-28 10:18   ` Andrea Merello
  2021-10-28 11:10     ` Jonathan Cameron
                       ` (2 more replies)
  2021-10-28 10:35   ` [v2 00/10] Add support for Bosch BNO055 IMU Jonathan Cameron
  10 siblings, 3 replies; 55+ messages in thread
From: Andrea Merello @ 2021-10-28 10:18 UTC (permalink / raw)
  To: jic23, mchehab+huawei, linux-iio, linux-kernel, devicetree
  Cc: lars, robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex,
	jacopo, Andrea Merello, 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.

Signed-off-by: Andrea Merello <andrea.merello@iit.it>
---
 drivers/iio/imu/bno055/Kconfig      |  6 ++++
 drivers/iio/imu/bno055/Makefile     |  1 +
 drivers/iio/imu/bno055/bno055_i2c.c | 54 +++++++++++++++++++++++++++++
 3 files changed, 61 insertions(+)
 create mode 100644 drivers/iio/imu/bno055/bno055_i2c.c

diff --git a/drivers/iio/imu/bno055/Kconfig b/drivers/iio/imu/bno055/Kconfig
index 941e43f0368d..87200787d548 100644
--- a/drivers/iio/imu/bno055/Kconfig
+++ b/drivers/iio/imu/bno055/Kconfig
@@ -7,3 +7,9 @@ config BOSH_BNO055_SERIAL
 	tristate "Bosh BNO055 attached via serial bus"
 	depends on SERIAL_DEV_BUS
 	select BOSH_BNO055_IIO
+
+config BOSH_BNO055_I2C
+	tristate "Bosh BNO055 attached via I2C bus"
+	depends on I2C
+	select REGMAP_I2C
+	select BOSH_BNO055_IIO
diff --git a/drivers/iio/imu/bno055/Makefile b/drivers/iio/imu/bno055/Makefile
index 7285ade2f4b9..eaf24018cb28 100644
--- a/drivers/iio/imu/bno055/Makefile
+++ b/drivers/iio/imu/bno055/Makefile
@@ -2,3 +2,4 @@
 
 obj-$(CONFIG_BOSH_BNO055_IIO) += bno055.o
 obj-$(CONFIG_BOSH_BNO055_SERIAL) += bno055_sl.o
+obj-$(CONFIG_BOSH_BNO055_I2C) += bno055_i2c.o
diff --git a/drivers/iio/imu/bno055/bno055_i2c.c b/drivers/iio/imu/bno055/bno055_i2c.c
new file mode 100644
index 000000000000..eea0daa6a61d
--- /dev/null
+++ b/drivers/iio/imu/bno055/bno055_i2c.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * I2C interface for Bosh BNO055 IMU.
+ * This file implements I2C communication up to the register read/write
+ * level.
+ *
+ * Copyright (C) 2021 Istituto Italiano di Tecnologia
+ * Electronic Design Laboratory
+ * Written by Andrea Merello <andrea.merello@iit.it>
+ */
+
+#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);
+	}
+
+	return bno055_probe(&client->dev, regmap,
+			    BNO055_I2C_XFER_BURST_BREAK_THRESHOLD);
+
+	return 0;
+}
+
+static const struct i2c_device_id bno055_i2c_id[] = {
+	{"bno055", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, bno055_i2c_id);
+
+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");
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [v2 01/10] utils_macro: introduce find_closest_unsorted()
  2021-10-28 10:18   ` [v2 01/10] utils_macro: introduce find_closest_unsorted() Andrea Merello
@ 2021-10-28 10:25     ` Andy Shevchenko
  2021-11-08 11:05       ` Andrea Merello
  0 siblings, 1 reply; 55+ messages in thread
From: Andy Shevchenko @ 2021-10-28 10:25 UTC (permalink / raw)
  To: Andrea Merello
  Cc: Jonathan Cameron, Mauro Carvalho Chehab, linux-iio,
	Linux Kernel Mailing List, devicetree, Lars-Peter Clausen,
	Rob Herring, Matt Ranostay, Alexandru Ardelean, jmondi,
	Andrea Merello

On Thu, Oct 28, 2021 at 1:18 PM Andrea Merello <andrea.merello@gmail.com> wrote:
>
> This is similar to find_closest() and find_closest_descending(), but, it
> doesn't make any assumption about the array being ordered.

Macros in general are not so welcoming.
Why do you do it as a macro?

...

> +#include <linux/math.h>

Wondering if the current header misses other inclusions it's a direct user of.

...

> +/**
> + * find_closest_unsorted - locate the closest element in a unsorted array

an

> + * @x: The reference value.
> + * @a: The array in which to look for the closest element.
> + * @as: Size of 'a'.
> + *
> + * Similar to find_closest() but 'a' has no requirement to being sorted
> + */

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 02/10] iio: document linear acceleration modifiers
  2021-10-28 10:18   ` [v2 02/10] iio: document linear acceleration modifiers Andrea Merello
@ 2021-10-28 10:31     ` Andy Shevchenko
  2021-11-09  7:48       ` Andrea Merello
  2021-10-28 10:40     ` Jonathan Cameron
  1 sibling, 1 reply; 55+ messages in thread
From: Andy Shevchenko @ 2021-10-28 10:31 UTC (permalink / raw)
  To: Andrea Merello
  Cc: Jonathan Cameron, Mauro Carvalho Chehab, linux-iio,
	Linux Kernel Mailing List, devicetree, Lars-Peter Clausen,
	Rob Herring, Matt Ranostay, Alexandru Ardelean, jmondi,
	Andrea Merello

On Thu, Oct 28, 2021 at 1:18 PM Andrea Merello <andrea.merello@gmail.com> wrote:
>
> This patch introduces ABI documentation for new iio modifiers used for
> reporting "linear acceleration" measures.

Because of ordering and absence of Fixes tag I haven't clearly got if
this is an existing set of attributes or that that will be added by
the series. If the former, use a Fixes tag and place it first in the
series. If the latter, move it after the actual addition of the
attributes in the code.

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 00/10] Add support for Bosch BNO055 IMU
  2021-10-28 10:35   ` [v2 00/10] Add support for Bosch BNO055 IMU Jonathan Cameron
@ 2021-10-28 10:33     ` Andy Shevchenko
  0 siblings, 0 replies; 55+ messages in thread
From: Andy Shevchenko @ 2021-10-28 10:33 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Andrea Merello, Mauro Carvalho Chehab, linux-iio,
	Linux Kernel Mailing List, devicetree, Lars-Peter Clausen,
	Rob Herring, Matt Ranostay, Alexandru Ardelean, jmondi

On Thu, Oct 28, 2021 at 1:30 PM Jonathan Cameron <jic23@kernel.org> wrote:
>
> Hi Andrea,
>
> I'd advise not sending new versions of a series with the in-reply-to set
> to an older version.  It's a good way to ensure anyone using an email client
> handling threads never sees them...  It also gets very messy if we happen
> to get replies to multiple versions overlapping.
>
> New version, new email thread.

+1 here, but since it will be a new version, let's leave it for a
while so reviewers may have a chance to comment on something.

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 03/10] iio: document euler angles modifiers
  2021-10-28 10:18   ` [v2 03/10] iio: document euler angles modifiers Andrea Merello
@ 2021-10-28 10:33     ` Andy Shevchenko
  2021-10-28 10:41     ` Jonathan Cameron
  1 sibling, 0 replies; 55+ messages in thread
From: Andy Shevchenko @ 2021-10-28 10:33 UTC (permalink / raw)
  To: Andrea Merello
  Cc: Jonathan Cameron, Mauro Carvalho Chehab, linux-iio,
	Linux Kernel Mailing List, devicetree, Lars-Peter Clausen,
	Rob Herring, Matt Ranostay, Alexandru Ardelean, jmondi,
	Andrea Merello

On Thu, Oct 28, 2021 at 1:18 PM Andrea Merello <andrea.merello@gmail.com> wrote:
>
> This patch introduces ABI documentation for new modifiers used for
> reporting rotations expressed as euler angles (i.e. yaw, pitch, roll).

As per previous patch.

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 00/10] Add support for Bosch BNO055 IMU
  2021-10-28 10:18 ` [v2 00/10] Add support for Bosch BNO055 IMU Andrea Merello
                     ` (9 preceding siblings ...)
  2021-10-28 10:18   ` [v2 10/10] iio: imu: add BNO055 I2C driver Andrea Merello
@ 2021-10-28 10:35   ` Jonathan Cameron
  2021-10-28 10:33     ` Andy Shevchenko
  10 siblings, 1 reply; 55+ messages in thread
From: Jonathan Cameron @ 2021-10-28 10:35 UTC (permalink / raw)
  To: Andrea Merello
  Cc: mchehab+huawei, linux-iio, linux-kernel, devicetree, lars,
	robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex, jacopo

Hi Andrea,

I'd advise not sending new versions of a series with the in-reply-to set
to an older version.  It's a good way to ensure anyone using an email client
handling threads never sees them...  It also gets very messy if we happen
to get replies to multiple versions overlapping.

New version, new email thread.

On Thu, 28 Oct 2021 12:18:30 +0200
Andrea Merello <andrea.merello@gmail.com> wrote:

> This series (tries to) add support for Bosch BNO055 IMU to Linux IIO
> subsystem. It is made up several patches:
> 
>   1/10: introduces the generic helper find_closest_unsorted()
> 
>   2/10 to 5/10: 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.
> 
>   6/10 to 8/10: add the core IIO BNO055 driver and its documentation
>      	        (including documentation for DT bindings)
> 
>   9/10: adds serdev BNO055 driver to actually use the IMU via serial line
> 
>   10/10: adds I2C BNO055 driver to actually use the IMU via I2C wiring
> 
> 
> Differences wrt v1:
> 
>   - Fixed GPL license version, which was wrong due to bad copy-pastes
> 
>   - Make less noise in log and get rid of some dev_dbg()s
> 
>   - Fix deferred probe handing and fix devm_add_action_or_reset() usage
> 
>   - Get rid of unneeded zeroing for driver data and some IIO "val2"s
> 
>   - Get rid of some leftovers of my attempt to support interrupts (which
>     don't fully work unless the IMU firmware gets updated)
> 
>   - Move IIO buffer off stack and make sure its first zeroed not to leak
>     kernel data
> 
>   - Hopefully addressed all maintainers and reviewers stylistic advices;
>     fixed some typos
> 
>   - Take advantage of more kernel helpers. Note: this series depends on
>     Yury Norov bitmap series i.e. "[PATCH 14/16] bitmap: unify find_bit
>     operations"
> 
>   - Make find_closest_unsorted() become an external generic helper
> 
>   - Reworked sysfs ABI as per maintainers advices
> 
>   - Added ABI documentation where needed
> 
>   - Added I2C support
> 
>   - Reworked DT documentation as per maintainers advices. Added I2C example
> 
>   The serial protocol handling have been criticized because it is not very
>   robust, however I couldn't really find any way to improve it; no changes
>   here wrt v1. I think the protocol itself is inherently weak and there is
>   nothing we can do about this (BTW here it is working fine).
> 
> 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
> 
>   - 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 (10):
>   utils_macro: introduce find_closest_unsorted()
>   iio: document linear acceleration modifiers
>   iio: document euler angles modifiers
>   iio: add modifiers for linear acceleration
>   iio: add modifers for pitch, yaw, roll
>   iio: document bno055 private sysfs attributes
>   iio: imu: add Bosch Sensortec BNO055 core driver
>   dt-bindings: iio: imu: add documentation for Bosch BNO055 bindings
>   iio: imu: add BNO055 serdev driver
>   iio: imu: add BNO055 I2C driver
> 
>  Documentation/ABI/testing/sysfs-bus-iio       |   16 +
>  .../ABI/testing/sysfs-bus-iio-bno055          |   84 +
>  .../bindings/iio/imu/bosch,bno055.yaml        |   59 +
>  drivers/iio/imu/Kconfig                       |    1 +
>  drivers/iio/imu/Makefile                      |    1 +
>  drivers/iio/imu/bno055/Kconfig                |   15 +
>  drivers/iio/imu/bno055/Makefile               |    5 +
>  drivers/iio/imu/bno055/bno055.c               | 1485 +++++++++++++++++
>  drivers/iio/imu/bno055/bno055.h               |   12 +
>  drivers/iio/imu/bno055/bno055_i2c.c           |   54 +
>  drivers/iio/imu/bno055/bno055_sl.c            |  568 +++++++
>  drivers/iio/industrialio-core.c               |    6 +
>  include/linux/util_macros.h                   |   26 +
>  include/uapi/linux/iio/types.h                |    7 +-
>  14 files changed, 2338 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 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


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 02/10] iio: document linear acceleration modifiers
  2021-10-28 10:18   ` [v2 02/10] iio: document linear acceleration modifiers Andrea Merello
  2021-10-28 10:31     ` Andy Shevchenko
@ 2021-10-28 10:40     ` Jonathan Cameron
  2021-11-09  8:00       ` Andrea Merello
  1 sibling, 1 reply; 55+ messages in thread
From: Jonathan Cameron @ 2021-10-28 10:40 UTC (permalink / raw)
  To: Andrea Merello
  Cc: mchehab+huawei, linux-iio, linux-kernel, devicetree, lars,
	robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex, jacopo,
	Andrea Merello

On Thu, 28 Oct 2021 12:18:32 +0200
Andrea Merello <andrea.merello@gmail.com> wrote:

> This patch introduces ABI documentation for new iio modifiers used for
> reporting "linear acceleration" measures.
> 
> Signed-off-by: Andrea Merello <andrea.merello@iit.it>
> ---
>  Documentation/ABI/testing/sysfs-bus-iio | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index 6ad47a67521c..5147a00bf24a 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -1957,3 +1957,11 @@ Description:
>  		Specify the percent for light sensor relative to the channel
>  		absolute value that a data field should change before an event
>  		is generated. Units are a percentage of the prior reading.
> +
> +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.15
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Raw (unscaled) linear acceleration readings.

Probably need more information that this.   What element is being 'removed' from
a normal acceleration measurement? What are units after application of offset and
scale?  Can cross refer to the in_accel_x_raw for that info if you like.

Also, but them immediately after the block with the in_accel_x_raw etc

The organization fo that file needs a rethink but let us try to avoid making
it worse in the meeantime!

Jonathan



^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 03/10] iio: document euler angles modifiers
  2021-10-28 10:18   ` [v2 03/10] iio: document euler angles modifiers Andrea Merello
  2021-10-28 10:33     ` Andy Shevchenko
@ 2021-10-28 10:41     ` Jonathan Cameron
  2021-11-09  8:15       ` Andrea Merello
  1 sibling, 1 reply; 55+ messages in thread
From: Jonathan Cameron @ 2021-10-28 10:41 UTC (permalink / raw)
  To: Andrea Merello
  Cc: mchehab+huawei, linux-iio, linux-kernel, devicetree, lars,
	robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex, jacopo,
	Andrea Merello

On Thu, 28 Oct 2021 12:18:33 +0200
Andrea Merello <andrea.merello@gmail.com> wrote:

> This patch introduces ABI documentation for new modifiers used for
> reporting rotations expressed as euler angles (i.e. yaw, pitch, roll).
> 
> Signed-off-by: Andrea Merello <andrea.merello@iit.it>
> ---
>  Documentation/ABI/testing/sysfs-bus-iio | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index 5147a00bf24a..f0adc2c817bd 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -1965,3 +1965,11 @@ KernelVersion:	5.15
>  Contact:	linux-iio@vger.kernel.org
>  Description:
>  		Raw (unscaled) linear acceleration readings.
> +
> +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.15
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Raw (unscaled) euler angles readings.
Any _raw entry should also include what the units are after application of
offset and scale.   Or you could just add this as more info to the in_rot_raw
block as an extra sentence explaining that they are euler angles.
That will lose the 'KernelVersion' information but honestly I'm not sure we
care that much about that.

Jonathan



^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 04/10] iio: add modifiers for linear acceleration
  2021-10-28 10:18   ` [v2 04/10] iio: add modifiers for linear acceleration Andrea Merello
@ 2021-10-28 10:45     ` Jonathan Cameron
  2021-11-09  9:58       ` Andrea Merello
  0 siblings, 1 reply; 55+ messages in thread
From: Jonathan Cameron @ 2021-10-28 10:45 UTC (permalink / raw)
  To: Andrea Merello
  Cc: mchehab+huawei, linux-iio, linux-kernel, devicetree, lars,
	robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex, jacopo,
	Andrea Merello

On Thu, 28 Oct 2021 12:18:34 +0200
Andrea Merello <andrea.merello@gmail.com> wrote:

> 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_ACCEL_LINEAR_X, IIO_MOD_ACCEL_LINEAR_Y and
> IIO_MOD_ACCEL_LINEAR_Z modifiers to te IIO core.
> 
> Signed-off-by: Andrea Merello <andrea.merello@iit.it>

They sometimes get forgotten but we should also update
tools/iio/iio_event_montitor.c to handle these new modifiers.

That can be a separate patch, but also fine to do it in this one.

> ---
>  drivers/iio/industrialio-core.c | 3 +++
>  include/uapi/linux/iio/types.h  | 4 +++-
>  2 files changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index 2dbb37e09b8c..a79cb32207e4 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -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_ACCEL_LINEAR_X] = "linear_x",
> +	[IIO_MOD_ACCEL_LINEAR_Y] = "linear_y",
> +	[IIO_MOD_ACCEL_LINEAR_Z] = "linear_z"
>  };
>  
>  /* relies on pairs of these shared then separate */
> diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
> index 48c13147c0a8..db00f7c45f48 100644
> --- a/include/uapi/linux/iio/types.h
> +++ b/include/uapi/linux/iio/types.h
> @@ -95,6 +95,9 @@ enum iio_modifier {
>  	IIO_MOD_ETHANOL,
>  	IIO_MOD_H2,
>  	IIO_MOD_O2,
> +	IIO_MOD_ACCEL_LINEAR_X,
> +	IIO_MOD_ACCEL_LINEAR_Y,
> +	IIO_MOD_ACCEL_LINEAR_Z,

It might be useful for other channel types, so probably drop the ACCEL
part of the name.

I'll admit I can't immediately think of what, but you never know.. :)

>  };
>  
>  enum iio_event_type {
> @@ -114,4 +117,3 @@ enum iio_event_direction {
>  };
>  
>  #endif /* _UAPI_IIO_TYPES_H_ */
> -
?



^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 05/10] iio: add modifers for pitch, yaw, roll
  2021-10-28 10:18   ` [v2 05/10] iio: add modifers for pitch, yaw, roll Andrea Merello
@ 2021-10-28 10:47     ` Jonathan Cameron
  0 siblings, 0 replies; 55+ messages in thread
From: Jonathan Cameron @ 2021-10-28 10:47 UTC (permalink / raw)
  To: Andrea Merello
  Cc: mchehab+huawei, linux-iio, linux-kernel, devicetree, lars,
	robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex, jacopo,
	Andrea Merello

On Thu, 28 Oct 2021 12:18:35 +0200
Andrea Merello <andrea.merello@gmail.com> wrote:

> This patch adds modifiers for reporting rotations as euler angles (i.e.
> yaw, pitch and roll).
> 
> Signed-off-by: Andrea Merello <andrea.merello@iit.it>
Same comment on tools update, and a few editorial things inline.

Jonathan

> ---
>  drivers/iio/industrialio-core.c | 5 ++++-
>  include/uapi/linux/iio/types.h  | 3 +++
>  2 files changed, 7 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index a79cb32207e4..d2ebbfa8b9fc 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -136,7 +136,10 @@ static const char * const iio_modifier_names[] = {
>  	[IIO_MOD_O2] = "o2",
>  	[IIO_MOD_ACCEL_LINEAR_X] = "linear_x",
>  	[IIO_MOD_ACCEL_LINEAR_Y] = "linear_y",
> -	[IIO_MOD_ACCEL_LINEAR_Z] = "linear_z"
> +	[IIO_MOD_ACCEL_LINEAR_Z] = "linear_z",

Move the comman to the previous patch.

> +	[IIO_MOD_PITCH] = "pitch",
> +	[IIO_MOD_YAW] = "yaw",
> +	[IIO_MOD_ROLL] = "roll"
>  };
>  
>  /* relies on pairs of these shared then separate */
> diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
> index db00f7c45f48..fc9909ca4f95 100644
> --- a/include/uapi/linux/iio/types.h
> +++ b/include/uapi/linux/iio/types.h
> @@ -98,6 +98,9 @@ enum iio_modifier {
>  	IIO_MOD_ACCEL_LINEAR_X,
>  	IIO_MOD_ACCEL_LINEAR_Y,
>  	IIO_MOD_ACCEL_LINEAR_Z,
> +	IIO_MOD_PITCH,
> +	IIO_MOD_YAW,
> +	IIO_MOD_ROLL

And add a comma here to make extending this in future easy.

>  };
>  
>  enum iio_event_type {


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 06/10] iio: document bno055 private sysfs attributes
  2021-10-28 10:18   ` [v2 06/10] iio: document bno055 private sysfs attributes Andrea Merello
@ 2021-10-28 11:04     ` Jonathan Cameron
  2021-11-09 10:22       ` Andrea Merello
  0 siblings, 1 reply; 55+ messages in thread
From: Jonathan Cameron @ 2021-10-28 11:04 UTC (permalink / raw)
  To: Andrea Merello
  Cc: mchehab+huawei, linux-iio, linux-kernel, devicetree, lars,
	robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex, jacopo,
	Andrea Merello

On Thu, 28 Oct 2021 12:18:36 +0200
Andrea Merello <andrea.merello@gmail.com> wrote:

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

Hohum. As normal I dislike custom attributes but reality is these
don't map to anything 'standard' and I don't want them getting adopted
in places where the 'standard' approach works.

So thinking a bit more on this, I wonder if we can fit it into standard
ABI.

We can't use the normal range specification method of
_scale because it's internal to the device and the output reading is
unaffected.  The range specification via _raw_available would let us know
the range, but it is not writeable so..

A control that changes the internal scaling of the sensor in a fashion
that is not visible to the outside world maps to calibscale.  Whilst
that was intended for little tweaks to the input signal (often front
end amplifier gain tweak) it works here.  It doesn't map through to
anything userspace is expected to apply.  That combined with
_raw_available to let us know what the result is should work?

What do you think of that approach?  It's obviously a little more complex
to handle in the driver, but it does map to existing ABI and avoids
custom attributes etc.

> 
> Signed-off-by: Andrea Merello <andrea.merello@iit.it>
> ---
>  .../ABI/testing/sysfs-bus-iio-bno055          | 84 +++++++++++++++++++
>  1 file changed, 84 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-bno055
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-bno055 b/Documentation/ABI/testing/sysfs-bus-iio-bno055
> new file mode 100644
> index 000000000000..930a70c5a858
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-bno055
> @@ -0,0 +1,84 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/in_accel_range
> +KernelVersion:	5.15
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Range for acceleration readings in G. 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).

Having this in G but the sensor output in m/s^2 seems inconsistent.

> +
> +What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_range
> +KernelVersion:	5.15
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Range for angular velocity readings in dps. 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).

Again, units need to match or this is going to be really confusing.

> +
> +What:		/sys/bus/iio/devices/iio:deviceX/in_accel_range_available
> +KernelVersion:	5.15
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		List of allowed values for in_accel_range attribute
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_range_available
> +KernelVersion:	5.15
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		List of allowed values for in_anglvel_range attribute
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/fast_magnetometer_calibration_enable
> +KernelVersion:	5.15
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Can be 1 or 0. Enables/disables the "Fast Magnetometer
> +		Calibration" HW function.

Naming needs to be consistent with the ABI.  This is a channel type specific function
and to match existing calibration related ABI naming it would be.

in_magn_calibration_fast_enable

Some of the others need renaming in a similar way.

> +
> +What:		/sys/bus/iio/devices/iio:deviceX/fusion_enable
> +KernelVersion:	5.15
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Can be 1 or 0. Enables/disables the "sensor fusion" (a.k.a.
> +		NDOF) HW function.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/in_calibration_data
> +KernelVersion:	5.15
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Reports the binary calibration data blob for the IMU sensors.

Why in_ ?  What channels does this apply to?

> +
> +What:		/sys/bus/iio/devices/iio:deviceX/in_autocalibration_status_accel
> +KernelVersion:	5.15
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Can be "Idle", "Bad", "Barely enough", "Fair", or "Good".
> +		Report the autocalibration status for the accelerometer sensor.

For interfaces that really don't have any chance of generalising this one is terrible.
Any hope at all of mapping this to something numeric?

in_accel_calibration_auto_status


> +
> +What:		/sys/bus/iio/devices/iio:deviceX/in_autocalibration_status_gyro
> +KernelVersion:	5.15
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Can be "Idle", "Bad", "Barely enough", "Fair", or "Good".
> +		Reports the autocalibration status for the gyroscope sensor.

in_angvel_calibration_auto_status
etc.

> +
> +What:		/sys/bus/iio/devices/iio:deviceX/in_autocalibration_status_magn
> +KernelVersion:	5.15
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Can be "Idle", "Bad", "Barely enough", "Fair", or "Good".
> +		Reports the autocalibration status for the magnetometer sensor.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/in_autocalibration_status_sys
> +KernelVersion:	5.15
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Can be "Idle", "Bad", "Barely enough", "Fair", or "Good".
> +		Reports the status for the IMU overall autocalibration.
> +
> +What:		/sys/bus/iio/devices/iio:deviceX/unique_id

Hmm. So normally we just dump these in the kernel log.  I guess you need it
here to associate a calibration blob with a particular sensor?

We could put it in label, but that would stop us using that for things like
positioning of the sensor.  So perhaps this is something that we should add
to the main ABI doc.  Probably as serial_number rather than unique ID though.

> +KernelVersion:	5.15
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		16-bytes, 2-digits-per-byte, HEX-string representing the sensor
> +		unique ID number.


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 10/10] iio: imu: add BNO055 I2C driver
  2021-10-28 10:18   ` [v2 10/10] iio: imu: add BNO055 I2C driver Andrea Merello
@ 2021-10-28 11:10     ` Jonathan Cameron
  2021-11-11 10:12       ` Andrea Merello
  2021-10-28 22:04     ` Randy Dunlap
  2021-10-29 13:30     ` kernel test robot
  2 siblings, 1 reply; 55+ messages in thread
From: Jonathan Cameron @ 2021-10-28 11:10 UTC (permalink / raw)
  To: Andrea Merello
  Cc: mchehab+huawei, linux-iio, linux-kernel, devicetree, lars,
	robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex, jacopo,
	Andrea Merello

On Thu, 28 Oct 2021 12:18:40 +0200
Andrea Merello <andrea.merello@gmail.com> wrote:

> 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.
> 
> Signed-off-by: Andrea Merello <andrea.merello@iit.it>
Hi Andrea,

A few minor things inline.

Jonathan

> ---
>  drivers/iio/imu/bno055/Kconfig      |  6 ++++
>  drivers/iio/imu/bno055/Makefile     |  1 +
>  drivers/iio/imu/bno055/bno055_i2c.c | 54 +++++++++++++++++++++++++++++
>  3 files changed, 61 insertions(+)
>  create mode 100644 drivers/iio/imu/bno055/bno055_i2c.c
> 
> diff --git a/drivers/iio/imu/bno055/Kconfig b/drivers/iio/imu/bno055/Kconfig
> index 941e43f0368d..87200787d548 100644
> --- a/drivers/iio/imu/bno055/Kconfig
> +++ b/drivers/iio/imu/bno055/Kconfig
> @@ -7,3 +7,9 @@ config BOSH_BNO055_SERIAL
>  	tristate "Bosh BNO055 attached via serial bus"
>  	depends on SERIAL_DEV_BUS
>  	select BOSH_BNO055_IIO
> +
> +config BOSH_BNO055_I2C
> +	tristate "Bosh BNO055 attached via I2C bus"
> +	depends on I2C
> +	select REGMAP_I2C
> +	select BOSH_BNO055_IIO
> diff --git a/drivers/iio/imu/bno055/Makefile b/drivers/iio/imu/bno055/Makefile
> index 7285ade2f4b9..eaf24018cb28 100644
> --- a/drivers/iio/imu/bno055/Makefile
> +++ b/drivers/iio/imu/bno055/Makefile
> @@ -2,3 +2,4 @@
>  
>  obj-$(CONFIG_BOSH_BNO055_IIO) += bno055.o
>  obj-$(CONFIG_BOSH_BNO055_SERIAL) += bno055_sl.o
> +obj-$(CONFIG_BOSH_BNO055_I2C) += bno055_i2c.o
> diff --git a/drivers/iio/imu/bno055/bno055_i2c.c b/drivers/iio/imu/bno055/bno055_i2c.c
> new file mode 100644
> index 000000000000..eea0daa6a61d
> --- /dev/null
> +++ b/drivers/iio/imu/bno055/bno055_i2c.c
> @@ -0,0 +1,54 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * I2C interface for Bosh BNO055 IMU.
> + * This file implements I2C communication up to the register read/write
> + * level.

Not really. It just uses regmap, so I'd drop this comment.

> + *
> + * Copyright (C) 2021 Istituto Italiano di Tecnologia
> + * Electronic Design Laboratory
> + * Written by Andrea Merello <andrea.merello@iit.it>
> + */
> +
> +#include <linux/i2c.h>

Why?  I'm not seeing an i2c specific calls in here.

> +#include <linux/regmap.h>
> +#include <linux/module.h>

mod_devicetable.h for struct i2c_device_id
 
> +
> +#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);
> +	}
> +
> +	return bno055_probe(&client->dev, regmap,
> +			    BNO055_I2C_XFER_BURST_BREAK_THRESHOLD);
> +
> +	return 0;

?

> +}
> +
> +static const struct i2c_device_id bno055_i2c_id[] = {
> +	{"bno055", 0},
> +	{ },

It's at terminator, so don't put a comma as we'll never add entries after this.

> +};
> +MODULE_DEVICE_TABLE(i2c, bno055_i2c_id);
> +
> +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");


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 08/10] dt-bindings: iio: imu: add documentation for Bosch BNO055 bindings
  2021-10-28 10:18   ` [v2 08/10] dt-bindings: iio: imu: add documentation for Bosch BNO055 bindings Andrea Merello
@ 2021-10-28 12:25     ` Rob Herring
  0 siblings, 0 replies; 55+ messages in thread
From: Rob Herring @ 2021-10-28 12:25 UTC (permalink / raw)
  To: Andrea Merello
  Cc: andy.shevchenko, robh+dt, lars, matt.ranostay, Andrea Merello,
	devicetree, jacopo, mchehab+huawei, linux-kernel, linux-iio,
	jic23, ardeleanalex

On Thu, 28 Oct 2021 12:18:38 +0200, Andrea Merello wrote:
> Introduce new documentation file for the Bosch BNO055 IMU
> 
> Signed-off-by: Andrea Merello <andrea.merello@iit.it>
> ---
>  .../bindings/iio/imu/bosch,bno055.yaml        | 59 +++++++++++++++++++
>  1 file changed, 59 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml
> 

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:
./Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml:20:6: [warning] wrong indentation: expected 6 but found 5 (indentation)
./Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml:37:2: [warning] wrong indentation: expected 2 but found 1 (indentation)

dtschema/dtc warnings/errors:
./Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml: $id: relative path/filename doesn't match actual path or filename
	expected: http://devicetree.org/schemas/iio/imu/bosch,bno055.yaml#
Error: Documentation/devicetree/bindings/iio/imu/bosch,bno055.example.dts:53.13-14 syntax error
FATAL ERROR: Unable to parse input tree
make[1]: *** [scripts/Makefile.lib:385: Documentation/devicetree/bindings/iio/imu/bosch,bno055.example.dt.yaml] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:1441: dt_binding_check] Error 2

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/patch/1547408

This check can fail if there are any dependencies. The base for a patch
series is generally the most recent rc1.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit.


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 09/10] iio: imu: add BNO055 serdev driver
  2021-10-28 10:18   ` [v2 09/10] iio: imu: add BNO055 serdev driver Andrea Merello
@ 2021-10-28 12:31     ` Jonathan Cameron
  2021-11-09 15:33       ` Andrea Merello
  2021-10-29  7:09     ` kernel test robot
  2021-10-29 12:59     ` kernel test robot
  2 siblings, 1 reply; 55+ messages in thread
From: Jonathan Cameron @ 2021-10-28 12:31 UTC (permalink / raw)
  To: Andrea Merello
  Cc: mchehab+huawei, linux-iio, linux-kernel, devicetree, lars,
	robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex, jacopo,
	Andrea Merello

On Thu, 28 Oct 2021 12:18:39 +0200
Andrea Merello <andrea.merello@gmail.com> wrote:

> 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.
> 
> Signed-off-by: Andrea Merello <andrea.merello@iit.it>

Hi Andrea,

Some comments inline.  Note I'm not that familiar with the serial_dev interface
so would definitely appreciate input from others on that part.

Jonathan
> ---
>  drivers/iio/imu/bno055/Kconfig     |   5 +
>  drivers/iio/imu/bno055/Makefile    |   1 +
>  drivers/iio/imu/bno055/bno055_sl.c | 568 +++++++++++++++++++++++++++++
>  3 files changed, 574 insertions(+)
>  create mode 100644 drivers/iio/imu/bno055/bno055_sl.c
> 
> diff --git a/drivers/iio/imu/bno055/Kconfig b/drivers/iio/imu/bno055/Kconfig
> index d197310661af..941e43f0368d 100644
> --- a/drivers/iio/imu/bno055/Kconfig
> +++ b/drivers/iio/imu/bno055/Kconfig
> @@ -2,3 +2,8 @@
>  
>  config BOSH_BNO055_IIO
>  	tristate
> +
> +config BOSH_BNO055_SERIAL
> +	tristate "Bosh BNO055 attached via serial bus"
> +	depends on SERIAL_DEV_BUS
> +	select BOSH_BNO055_IIO
> diff --git a/drivers/iio/imu/bno055/Makefile b/drivers/iio/imu/bno055/Makefile
> index c55741d0e96f..7285ade2f4b9 100644
> --- a/drivers/iio/imu/bno055/Makefile
> +++ b/drivers/iio/imu/bno055/Makefile
> @@ -1,3 +1,4 @@
>  # SPDX-License-Identifier: GPL-2.0
>  
>  obj-$(CONFIG_BOSH_BNO055_IIO) += bno055.o
> +obj-$(CONFIG_BOSH_BNO055_SERIAL) += bno055_sl.o
> diff --git a/drivers/iio/imu/bno055/bno055_sl.c b/drivers/iio/imu/bno055/bno055_sl.c
> new file mode 100644
> index 000000000000..1d1410fdaa7c
> --- /dev/null
> +++ b/drivers/iio/imu/bno055/bno055_sl.c
> @@ -0,0 +1,568 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Serial line interface for Bosh BNO055 IMU (via serdev).
> + * This file implements serial communication up to the register read/write
> + * level.
> + *
> + * Copyright (C) 2021 Istituto Italiano di Tecnologia
> + * Electronic Design Laboratory
> + * Written by Andrea Merello <andrea.merello@iit.it>
> + *
> + * This driver is besed on
> + *	Plantower PMS7003 particulate matter sensor driver
> + *	Which is
> + *	Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
> + */
> +
> +#include <linux/completion.h>
> +#include <linux/device.h>
> +#include <linux/errno.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/regmap.h>
> +#include <linux/serdev.h>
> +
> +#include "bno055.h"
> +
> +/*
> + * Register writes cmd have the following format
> + * +------+------+-----+-----+----- ... ----+
> + * | 0xAA | 0xOO | REG | LEN | payload[LEN] |
> + * +------+------+-----+-----+----- ... ----+
> + *
> + * Register write responses have the following format
> + * +------+----------+
> + * | 0xEE | ERROCODE |
> + * +------+----------+
> + *
> + * Register read have the following format
> + * +------+------+-----+-----+
> + * | 0xAA | 0xO1 | REG | LEN |
> + * +------+------+-----+-----+
> + *
> + * Successful register read response have the following format
> + * +------+-----+----- ... ----+
> + * | 0xBB | LEN | payload[LEN] |
> + * +------+-----+----- ... ----+
> + *
> + * Failed register read response have the following format
> + * +------+--------+
> + * | 0xEE | ERRCODE|  (ERRCODE always > 1)
> + * +------+--------+
> + *
> + * Error codes are
> + * 01: OK
> + * 02: read/write FAIL
> + * 04: invalid address
> + * 05: write on RO
> + * 06: wrong start byte
> + * 07: bus overrun
> + * 08: len too high
> + * 09: len too low
> + * 10: bus RX byte timeout (timeout is 30mS)
> + *
> + *
> + * **WORKAROUND ALERT**
> + *
> + * Serial communication seems very fragile: the BNO055 buffer seems to overflow
> + * very easy; BNO055 seems able to sink few bytes, then it needs a brief pause.
> + * On the other hand, it is also picky on timeout: if there is a pause > 30mS in
> + * between two bytes then the transaction fails (IMU internal RX FSM resets).
> + *
> + * BMU055 has been seen also failing to process commands in case we send them
> + * too close each other (or if it is somehow busy?)
> + *
> + * One idea would be to split data in chunks, and then wait 1-2mS between
> + * chunks (we hope not to exceed 30mS delay for any reason - which should
> + * be pretty a lot of time for us), and eventually retry in case the BNO055
> + * gets upset for any reason. This seems to work in avoiding the overflow
> + * errors, but indeed it seems slower than just perform a retry when an overflow
> + * error occur.
> + * In particular I saw these scenarios:
> + * 1) If we send 2 bytes per time, then the IMU never(?) overflows.
> + * 2) If we send 4 bytes per time (i.e. the full header), then the IMU could
> + *    overflow, but it seem to sink all 4 bytes, then it returns error.
> + * 3) If we send more than 4 bytes, the IMU could overflow, and I saw it sending
> + *    error after 4 bytes are sent; we have troubles in synchronizing again,
> + *    because we are still sending data, and the IMU interprets it as the 1st
> + *    byte of a new command.
> + *
> + * So, we workaround all this in the following way:
> + * In case of read we don't split the header but we rely on retries; This seems
> + * convenient for data read (where we TX only the hdr).
> + * For TX we split the transmission in 2-bytes chunks so that, we should not
> + * only avoid case 2 (which is still manageable), but we also hopefully avoid
> + * case 3, that would be by far worse.
> + */
> +
> +/*
> + * Read operation overhead:
> + *  4 bytes req + 2byte resp hdr.
> + *  6 bytes = 60 bit (considering 1start + 1stop bits).
> + *  60/115200 = ~520uS.
> + *
> + * In 520uS we could read back about 34 bytes that means 3 samples, this means
> + * that in case of scattered read in which the gap is 3 samples or less it is
> + * still convenient to go for a burst.
> + * We have to take into account also IMU response time - IMU seems to be often
> + * reasonably quick to respond, but sometimes it seems to be in some "critical
> + * section" in which it delays handling of serial protocol.
> + * By experiment, it seems convenient to burst up to about 5/6-samples-long gap.
> + */
> +
> +#define BNO055_SL_XFER_BURST_BREAK_THRESHOLD 6
> +
> +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;
> +
> +	/**
> +	 * 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
> +	} cmd_status;
> +	struct mutex lock;
> +
> +	/* Only accessed in behalf of RX callback context. No lock needed. */

would "Only accessed in RX callback context. No lock needed." convey the same meaning?

> +	struct {
> +		enum {
> +			RX_IDLE,
> +			RX_START,
> +			RX_DATA
> +		} state;
> +		int databuf_count;
> +		int expected_len;
> +		int type;
> +	} rx;
> +
> +	/* Never accessed in behalf of RX callback context. No lock needed */
> +	bool cmd_stale;
> +};
> +
> +static int bno055_sl_send_chunk(struct bno055_sl_priv *priv, u8 *data, int len)
> +{
> +	int ret;
> +
> +	dev_dbg(&priv->serdev->dev, "send (len: %d): %*ph", len, len, data);
> +	ret = serdev_device_write(priv->serdev, data, len,
> +				  msecs_to_jiffies(25));
> +	if (ret < 0)
> +		return ret;
> +
> +	if (ret < len)
> +		return -EIO;
> +
> +	return 0;
> +}
> +
> +/*
> + * Sends a read or write command.
> + * 'data' can be NULL (used in read case). 'len' parameter is always valid; in
> + * case 'data' is non-NULL then it must match 'data' size.
> + */
> +static int bno055_sl_do_send_cmd(struct bno055_sl_priv *priv,
> +				 int read, int addr, int len, u8 *data)

Read is a bool, so give it that type.

> +{
> +	int ret;
> +	int chunk_len;
> +	u8 hdr[] = {0xAA, !!read, addr, len};
> +
> +	if (read) {
> +		ret = bno055_sl_send_chunk(priv, hdr, 4);
> +	} else {
> +		ret = bno055_sl_send_chunk(priv, hdr, 2);
> +		if (ret)
> +			goto fail;
> +
> +		usleep_range(2000, 3000);
> +		ret = bno055_sl_send_chunk(priv, hdr + 2, 2);
> +	}
> +	if (ret)
> +		goto fail;
> +
> +	if (data) {

I would flip this condition to reduce indent and make it easy to
see we are done in the no 'data' case.  Also, does this correspond in
all cases to read?  If so I would use that as the variable to check.

	if (!data)
		return 0;

	while (len) {
...


> +		while (len) {
> +			chunk_len = min(len, 2);
> +			usleep_range(2000, 3000);
> +			ret = bno055_sl_send_chunk(priv, data, chunk_len);
> +			if (ret)
> +				goto fail;
> +			data += chunk_len;
> +			len -= chunk_len;
> +		}
> +	}
> +
> +	return 0;
> +fail:
> +	/* waiting more than 30mS should clear the BNO055 internal state */
> +	usleep_range(40000, 50000);
> +	return ret;
> +}
> +
> +static int bno_sl_send_cmd(struct bno055_sl_priv *priv,
> +			   int read, int addr, int len, u8 *data)

Read looks to be a bool to me not an integer.

> +{
> +	const int retry_max = 5;
> +	int retry = retry_max;
> +	int ret = 0;
> +
> +	/*
> +	 * In case previous command was interrupted we still neet to wait it to
> +	 * complete before we can issue new commands
> +	 */
> +	if (priv->cmd_stale) {
> +		ret = wait_for_completion_interruptible_timeout(&priv->cmd_complete,
> +								msecs_to_jiffies(100));
> +		if (ret == -ERESTARTSYS)
> +			return -ERESTARTSYS;
> +
> +		priv->cmd_stale = false;
> +		/* if serial protocol broke, bail out */
> +		if (priv->cmd_status == STATUS_CRIT)
> +			goto exit;

			return -EIO;

> +	}
> +
> +	/*
> +	 * Try to convince the IMU to cooperate.. as explained in the comments
> +	 * at the top of this file, the IMU could also refuse the command (i.e.
> +	 * it is not ready yet); retry in this case.
> +	 */
> +	while (retry--) {
> +		mutex_lock(&priv->lock);
> +		priv->expect_response = read ? CMD_READ : CMD_WRITE;
> +		reinit_completion(&priv->cmd_complete);
> +		mutex_unlock(&priv->lock);
> +
> +		if (retry != (retry_max - 1))
> +			dev_dbg(&priv->serdev->dev, "cmd retry: %d",
> +				retry_max - retry);
> +		ret = bno055_sl_do_send_cmd(priv, read, addr, len, data);
> +		if (ret)
> +			continue;
> +
> +		ret = wait_for_completion_interruptible_timeout(&priv->cmd_complete,
> +								msecs_to_jiffies(100));
> +		if (ret == -ERESTARTSYS) {
> +			priv->cmd_stale = true;
> +			return -ERESTARTSYS;
> +		} else if (!ret) {
> +			ret = -ETIMEDOUT;

			return -ETIMEDOUT;

> +			break;
> +		}
> +		ret = 0;
> +
> +		/*
> +		 * Poll if the IMU returns error (i.e busy), break if the IMU
> +		 * returns OK or if the serial communication broke
> +		 */
> +		if (priv->cmd_status <= 0)
I 'think' this is only place we can break out with status set to anything (with
the suggested modifications above) so move the if statements from the error path
here and drop the ret = 0 above.


> +			break;
> +	}
> +
> +exit:
> +	if (ret)
> +		return ret;
> +	if (priv->cmd_status == STATUS_CRIT)
> +		return -EIO;
> +	if (priv->cmd_status == STATUS_FAIL)
> +		return -EINVAL;
> +	return 0;
> +}
> +
> +static int bno055_sl_write_reg(void *context, const void *data, size_t count)
> +{
> +	int ret;
> +	int reg;
> +	u8 *write_data = (u8 *)data + 1;

Given you dereference data as a u8 * in several places, perhaps a local
variable to allow you to do it once.

> +	struct bno055_sl_priv *priv = context;
> +
> +	if (count < 2) {
> +		dev_err(&priv->serdev->dev, "Invalid write count %zu", count);
> +		return -EINVAL;
> +	}
> +
> +	reg = ((u8 *)data)[0];
> +	dev_dbg(&priv->serdev->dev, "wr reg 0x%x = 0x%x", reg, ((u8 *)data)[1]);
> +	ret = bno_sl_send_cmd(priv, 0, reg, count - 1, write_data);
> +
> +	return ret;

	return bno_sl_send_cmd(...)

> +}
> +
> +static int bno055_sl_read_reg(void *context,
> +			      const void *reg, size_t reg_size,
> +			      void *val, size_t val_size)
> +{
> +	int ret;
> +	int reg_addr;
> +	struct bno055_sl_priv *priv = context;
> +
> +	if (val_size > 128) {
> +		dev_err(&priv->serdev->dev, "Invalid read valsize %d",
> +			val_size);
> +		return -EINVAL;
> +	}
> +
> +	reg_addr = ((u8 *)reg)[0];
> +	dev_dbg(&priv->serdev->dev, "rd reg 0x%x (len %d)", reg_addr, val_size);
> +	mutex_lock(&priv->lock);
> +	priv->expected_data_len = val_size;
> +	priv->response_buf = val;
> +	mutex_unlock(&priv->lock);
> +
> +	ret = bno_sl_send_cmd(priv, 1, reg_addr, val_size, NULL);
> +
> +	mutex_lock(&priv->lock);
> +	priv->response_buf = NULL;
> +	mutex_unlock(&priv->lock);
> +
> +	return ret;
> +}
> +
> +/*
> + * Handler for received data; this is called from the reicever callback whenever
> + * it got some packet from the serial bus. The status tell us whether the
> + * packet is valid (i.e. header ok && received payload len consistent wrt the
> + * header). It's now our responsability to check whether this is what we
> + * expected, of whether we got some unexpected, yet valid, packet.
> + */
> +static void bno055_sl_handle_rx(struct bno055_sl_priv *priv, int status)
> +{
> +	mutex_lock(&priv->lock);
> +	switch (priv->expect_response) {
> +	case CMD_NONE:
> +		dev_warn(&priv->serdev->dev, "received unexpected, yet valid, data from sensor");
> +		mutex_unlock(&priv->lock);
> +		return;
> +
> +	case CMD_READ:
> +		priv->cmd_status = status;
> +		if (status == STATUS_OK &&
> +		    priv->rx.databuf_count != priv->expected_data_len) {
> +			/*
> +			 * If we got here, then the lower layer serial protocol
> +			 * seems consistent with itself; if we got an unexpected
> +			 * amount of data then signal it as a non critical error
> +			 */
> +			priv->cmd_status = STATUS_FAIL;
> +			dev_warn(&priv->serdev->dev, "received an unexpected amount of, yet valid, data from sensor");
> +		}
> +		break;
> +
> +	case CMD_WRITE:
> +		priv->cmd_status = status;
> +		break;
> +	}
> +
> +	priv->expect_response = CMD_NONE;
> +	complete(&priv->cmd_complete);
> +	mutex_unlock(&priv->lock);
> +}
> +
> +/*
> + * Serdev receiver FSM. This tracks the serial communication and parse the
> + * header. It pushes packets to bno055_sl_handle_rx(), eventually communicating
> + * failures (i.e. malformed packets).
> + * Ideally it doesn't know anything about upper layer (i.e. if this is the
> + * packet we were really expecting), but since we copies the payload into the
> + * receiver buffer (that is not valid when i.e. we don't expect data), we
> + * snoop a bit in the upper layer..
> + * Also, we assume to RX one pkt per time (i.e. the HW doesn't send anything
> + * unless we require to AND we don't queue more than one request per time).
> + */
> +static int bno055_sl_receive_buf(struct serdev_device *serdev,
> +				 const unsigned char *buf, size_t size)
> +{
> +	int status;
> +	struct bno055_sl_priv *priv = serdev_device_get_drvdata(serdev);
> +	int _size = size;

Why the local variable?

> +
> +	if (size == 0)
> +		return 0;
> +
> +	dev_dbg(&priv->serdev->dev, "recv (len %zu): %*ph ", size, size, buf);
> +	switch (priv->rx.state) {
> +	case RX_IDLE:
> +		/*
> +		 * New packet.
> +		 * Check for its 1st byte, that identifies the pkt type.
> +		 */
> +		if (buf[0] != 0xEE && buf[0] != 0xBB) {
> +			dev_err(&priv->serdev->dev,
> +				"Invalid packet start %x", buf[0]);
> +			bno055_sl_handle_rx(priv, STATUS_CRIT);
> +			break;
> +		}
> +		priv->rx.type = buf[0];
> +		priv->rx.state = RX_START;
> +		size--;
> +		buf++;
> +		priv->rx.databuf_count = 0;
> +		fallthrough;
> +
> +	case RX_START:
> +		/*
> +		 * Packet RX in progress, we expect either 1-byte len or 1-byte
> +		 * status depending by the packet type.
> +		 */
> +		if (size == 0)
> +			break;
> +
> +		if (priv->rx.type == 0xEE) {
> +			if (size > 1) {
> +				dev_err(&priv->serdev->dev, "EE pkt. Extra data received");
> +				status = STATUS_CRIT;
> +
> +			} else {
> +				status = (buf[0] == 1) ? STATUS_OK : STATUS_FAIL;
> +			}
> +			bno055_sl_handle_rx(priv, status);
> +			priv->rx.state = RX_IDLE;
> +			break;
> +
> +		} else {
> +			/*priv->rx.type == 0xBB */
> +			priv->rx.state = RX_DATA;
> +			priv->rx.expected_len = buf[0];
> +			size--;
> +			buf++;
> +		}
> +		fallthrough;
> +
> +	case RX_DATA:
> +		/* Header parsed; now receiving packet data payload */
> +		if (size == 0)
> +			break;
> +
> +		if (priv->rx.databuf_count + size > priv->rx.expected_len) {
> +			/*
> +			 * This is a inconsistency in serial protocol, we lost
> +			 * sync and we don't know how to handle further data
> +			 */
> +			dev_err(&priv->serdev->dev, "BB pkt. Extra data received");
> +			bno055_sl_handle_rx(priv, STATUS_CRIT);
> +			priv->rx.state = RX_IDLE;
> +			break;
> +		}
> +
> +		mutex_lock(&priv->lock);
> +		/*
> +		 * NULL e.g. when read cmd is stale or when no read cmd is
> +		 * actually pending.
> +		 */
> +		if (priv->response_buf &&
> +		    /*
> +		     * Snoop on the upper layer protocol stuff to make sure not
> +		     * to write to an invalid memory. Apart for this, let's the
> +		     * upper layer manage any inconsistency wrt expected data
> +		     * len (as long as the serial protocol is consistent wrt
> +		     * itself (i.e. response header is consistent with received
> +		     * response len.
> +		     */
> +		    (priv->rx.databuf_count + size <= priv->expected_data_len))
> +			memcpy(priv->response_buf + priv->rx.databuf_count,
> +			       buf, size);
> +		mutex_unlock(&priv->lock);
> +
> +		priv->rx.databuf_count += size;
> +
> +		/*
> +		 * Reached expected len advertised by the IMU for the current
> +		 * packet. Pass it to the upper layer (for us it is just valid).
> +		 */
> +		if (priv->rx.databuf_count == priv->rx.expected_len) {
> +			bno055_sl_handle_rx(priv, STATUS_OK);
> +			priv->rx.state = RX_IDLE;
> +		}
> +		break;
> +	}
> +
> +	return _size;
> +}
> +
> +static const struct serdev_device_ops bno055_sl_serdev_ops = {
> +	.receive_buf = bno055_sl_receive_buf,
> +	.write_wakeup = serdev_device_write_wakeup,
> +};
> +
> +static struct regmap_bus bno055_sl_regmap_bus = {
> +	.write = bno055_sl_write_reg,
> +	.read = bno055_sl_read_reg,
> +};
> +
> +static int bno055_sl_probe(struct serdev_device *serdev)
> +{
> +	struct bno055_sl_priv *priv;
> +	struct regmap *regmap;
> +	int ret;
> +
> +	priv = devm_kzalloc(&serdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	serdev_device_set_drvdata(serdev, priv);
> +	priv->serdev = serdev;
> +	mutex_init(&priv->lock);
> +	init_completion(&priv->cmd_complete);
> +
> +	serdev_device_set_client_ops(serdev, &bno055_sl_serdev_ops);
> +	ret = devm_serdev_device_open(&serdev->dev, serdev);
> +	if (ret)
> +		return ret;
> +
> +	if (serdev_device_set_baudrate(serdev, 115200) != 115200) {
> +		dev_err(&serdev->dev, "Cannot set required baud rate");
> +		return -EIO;
> +	}
> +
> +	ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
> +	if (ret) {
> +		dev_err(&serdev->dev, "Cannot set required parity setting");
> +		return ret;
> +	}
> +	serdev_device_set_flow_control(serdev, false);
> +
> +	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 bno055_probe(&serdev->dev, regmap,
> +			    BNO055_SL_XFER_BURST_BREAK_THRESHOLD);
> +}
> +
> +static const struct of_device_id bno055_sl_of_match[] = {
> +	{ .compatible = "bosch,bno055" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, bno055_sl_of_match);
> +
> +static struct serdev_device_driver bno055_sl_driver = {
> +	.driver = {
> +		.name = "bno055-sl",
> +		.of_match_table = bno055_sl_of_match,
> +	},
> +	.probe = bno055_sl_probe,
> +};
> +module_serdev_device_driver(bno055_sl_driver);
> +
> +MODULE_AUTHOR("Andrea Merello <andrea.merello@iit.it>");
> +MODULE_DESCRIPTION("Bosch BNO055 serdev interface");
> +MODULE_LICENSE("GPL v2");


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 07/10] iio: imu: add Bosch Sensortec BNO055 core driver
  2021-10-28 10:18   ` [v2 07/10] iio: imu: add Bosch Sensortec BNO055 core driver Andrea Merello
@ 2021-10-28 13:31     ` Jonathan Cameron
  2021-11-09 11:52       ` Andrea Merello
  0 siblings, 1 reply; 55+ messages in thread
From: Jonathan Cameron @ 2021-10-28 13:31 UTC (permalink / raw)
  To: Andrea Merello
  Cc: mchehab+huawei, linux-iio, linux-kernel, devicetree, lars,
	robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex, jacopo,
	Andrea Merello

On Thu, 28 Oct 2021 12:18:37 +0200
Andrea Merello <andrea.merello@gmail.com> wrote:

> 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.
> 
> Signed-off-by: Andrea Merello <andrea.merello@iit.it>
> ---
>  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 | 1480 +++++++++++++++++++++++++++++++
>  drivers/iio/imu/bno055/bno055.h |   12 +
>  6 files changed, 1501 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
> 
...

> diff --git a/drivers/iio/imu/bno055/bno055.c b/drivers/iio/imu/bno055/bno055.c
> new file mode 100644
> index 000000000000..c85cb985f0f1
> --- /dev/null
> +++ b/drivers/iio/imu/bno055/bno055.c
> @@ -0,0 +1,1480 @@

...

> +
> +static int bno055_reg_update_bits(struct bno055_priv *priv, unsigned int reg,
> +				  unsigned int mask, unsigned int val)
> +{
> +	int ret;
> +
> +	ret = regmap_update_bits(priv->regmap, reg, mask, val);
> +	if (ret && ret != -ERESTARTSYS) {
> +		dev_err(priv->dev, "Regmap update_bits  error. adr: 0x%x, ret: %d",
> +			reg,  ret);

This feels like a wrapper that made sense when developing but probably doesn't
want to still be here now things are 'working'.

> +	}
> +
> +	return ret;
> +}
> +

...

> +
> +static void bno055_clk_disable(void *arg)

Easy to make arg == priv->clk and turn this into a one line function.
I'd expect these cleanup functions to be just above where probe() is defined rather
than all the way up here.

> +{
> +	struct bno055_priv *priv = arg;
> +
> +	clk_disable_unprepare(priv->clk);
> +}
> +

...

> +
> +static int bno055_get_acc_lpf(struct bno055_priv *priv, int *val, int *val2)
> +{
> +	const int shift = __ffs(BNO055_ACC_CONFIG_LPF_MASK);
> +	int hwval, idx;
> +	int ret;
> +
> +	ret = regmap_read(priv->regmap, BNO055_ACC_CONFIG_REG, &hwval);
> +	if (ret)
> +		return ret;
> +
> +	idx = (hwval & BNO055_ACC_CONFIG_LPF_MASK) >> shift;

Use FIELD_GET() and FIELD_PREP where possible rather than reinventing them.

> +	*val = bno055_acc_lpf_vals[idx * 2];
> +	*val2 = bno055_acc_lpf_vals[idx * 2 + 1];
> +
> +	return IIO_VAL_INT_PLUS_MICRO;
> +}
> +
> +static int bno055_set_acc_lpf(struct bno055_priv *priv, int val, int val2)
> +{
> +	const int shift = __ffs(BNO055_ACC_CONFIG_LPF_MASK);
> +	int req_val = val * 1000 + val2 / 1000;
> +	bool first = true;
> +	int best_delta;
> +	int best_idx;
> +	int tbl_val;
> +	int delta;
> +	int ret;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(bno055_acc_lpf_vals) / 2; i++) {
> +		tbl_val = bno055_acc_lpf_vals[i * 2] * 1000 +
> +			bno055_acc_lpf_vals[i * 2 + 1] / 1000;
> +		delta = abs(tbl_val - req_val);
> +		if (first || delta < best_delta) {
> +			best_delta = delta;
> +			best_idx = i;
> +			first = false;
> +		}
> +	}
> +
> +	/*
> +	 * 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;

Can we do this before the big loop above?


> +
> +	ret = regmap_write(priv->regmap, BNO055_OPR_MODE_REG,
> +			   BNO055_OPR_MODE_CONFIG);
> +	if (ret)
> +		return ret;
> +
> +	ret = bno055_reg_update_bits(priv, BNO055_ACC_CONFIG_REG,
> +				     BNO055_ACC_CONFIG_LPF_MASK,
> +				     best_idx << shift);
> +
> +	if (ret)
> +		return ret;
> +
> +	return regmap_write(priv->regmap, BNO055_OPR_MODE_REG,
> +			    BNO055_OPR_MODE_AMG);
> +}
> +

...

> +
> +#define bno055_get_mag_odr(p, v) \
> +	bno055_get_regmask(p, v, \
> +			   BNO055_MAG_CONFIG_REG, BNO055_MAG_CONFIG_ODR_MASK, \
> +			   bno055_mag_odr_vals)

I'm not really convinced this is a worthwhile abstraction as these are typically
only used once.

> +
...

> +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 */
> +			*val2 = 900;
> +			break;
> +		case IIO_ROT:
> +			/* Table 3-28: 1 degree = 16 LSB */
> +			*val2 = 16;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +		return IIO_VAL_FRACTIONAL;
> +	default:
> +		return -EINVAL;

default in the middle is a bit unusual. move it to the end.

> +
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		if (chan->type != IIO_MAGN)
> +			return -EINVAL;
> +		else
> +			return bno055_get_mag_odr(priv, val);
> +
> +	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
> +		switch (chan->type) {
> +		case IIO_ANGL_VEL:
> +			return bno055_get_gyr_lpf(priv, val);
> +		case IIO_ACCEL:
> +			return bno055_get_acc_lpf(priv, val, val2);
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +}
> +


> +
> +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;

IIO_VAL_FRACTIONAL_LOG2?

> +		return IIO_VAL_FRACTIONAL;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +

...

> +
> +static ssize_t bno055_fusion_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 = 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.
> +		 * 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);
> +	}
> +
> +	return len ?: len;

return ret?: len; might make sense, though my inclination would be to use an explicit
if (ret) at the various possible error locations.

> +}

...

> +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 = 0;
> +
> +	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);
> +	} else {
> +		if (priv->operation_mode == BNO055_OPR_MODE_AMG)
> +			return -EINVAL;
> +	}
> +
> +	return len ?: ret;

Don't think that will return ret which is what we want if it's set.

> +}
> +

...

> +static ssize_t in_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 = regmap_write(priv->regmap, BNO055_OPR_MODE_REG,
> +			   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 = regmap_write(priv->regmap, BNO055_OPR_MODE_REG, priv->operation_mode);
> +	mutex_unlock(&priv->lock);
> +	if (ret)
> +		return ret;

This is a case where I'd move the mutex_unlock after the check so that we have
a nice shared error path via the unlock lable.

> +
> +	memcpy(buf, data, BNO055_CALDATA_LEN);
> +
> +	return BNO055_CALDATA_LEN;
> +unlock:
> +	mutex_unlock(&priv->lock);
> +	return ret;
> +}
> +
...

> +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 = devm_kasprintf(priv->dev, 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));
> +	devm_kfree(priv->dev, buf);

Why use devm managed allocations if you are just going to free it immediately?

> +
> +	return ret;
> +}
> +

...

> +/*
> + * 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.

This is rather complex - I take we can't just fall back to letting the IIO core
demux do all the hard work for us?

> + */
> +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;
> +
> +	/*
> +	 * 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;
> +	bool xfer_pending = false;
> +	bool first = true;
> +	unsigned long mask;
> +	int buf_idx = 0;
> +	bool thr_hit;
> +	int quat;
> +	int ret;
> +
> +	mutex_lock(&priv->lock);
> +	for_each_set_bitrange(start, end, iio_dev->active_scan_mask,
> +			      iio_dev->masklength) {

I'm not seeing this function in mainline...  I guess this series has a dependency
I missed?

> +		if (!xfer_pending)
> +			xfer_start = start;
> +		xfer_pending = true;
> +
> +		if (!first) {

first == true and we never get in here to set it to false.

Perhaps we would benefit from a state machine diagram for this function?
In general this function is complex enough to need documentation of what
each major part is doing.

> +			quat = ((start > BNO055_SCAN_QUATERNION) &&
> +				(prev_end <= BNO055_SCAN_QUATERNION)) ? 3 : 0;

Having quat == 3 for a variable named quat doesn't seem intuitive. 

> +			thr_hit = (start - prev_end + quat) >
> +				priv->xfer_burst_break_thr;
> +
> +			if (thr_hit) {
> +				mask = *iio_dev->active_scan_mask >> xfer_start;
> +				ret = bno055_scan_xfer(priv, xfer_start,
> +						       prev_end - xfer_start + 1,
> +						       mask, priv->buf.chans, &buf_idx);
> +				if (ret)
> +					goto done;
> +				xfer_pending = false;
> +			}
> +			first = false;
> +		}
> +		prev_end = end;
> +	}
> +
> +	if (xfer_pending) {
> +		mask = *iio_dev->active_scan_mask >> xfer_start;
> +		ret = bno055_scan_xfer(priv, xfer_start,
> +				       end - xfer_start + 1,
> +				       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;
> +}
> +
> +int bno055_probe(struct device *dev, struct regmap *regmap,
> +		 int xfer_burst_break_thr)
> +{
> +	const struct firmware *caldata;
> +	struct bno055_priv *priv;
> +	struct iio_dev *iio_dev;
> +	struct gpio_desc *rst;
> +	char *fw_name_buf;
> +	unsigned int val;
> +	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;

blank line here would hep readability a little I think.

> +	rst = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
> +	if (IS_ERR(rst))
> +		return dev_err_probe(dev, PTR_ERR(rst), "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);

As mentioned above, pass priv->clk into this as we don't need to see anything
else in the callback.

> +	if (ret)
> +		return ret;
> +
> +	if (rst) {
> +		usleep_range(5000, 10000);
> +		gpiod_set_value_cansleep(rst, 0);
> +		usleep_range(650000, 750000);
> +	}
> +
> +	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;
> +	}
> +
> +	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 = devm_kasprintf(dev, 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);
> +	devm_kfree(dev, fw_name_buf);
> +	if (ret)
> +		ret = request_firmware(&caldata, BNO055_FW_GENERIC_NAME, dev);
> +
> +	if (ret) {
> +		dev_notice(dev, "Failed to load calibration data firmware file; this has nothing to do with IMU main firmware.\nYou can calibrate your IMU (look for 'in_autocalibration_status*' files in sysfs) and then copy 'in_calibration_data' to your firmware file");

As the notice has multiple lines, you can break at the \n points without any loss of greppability.
It would be good to make this shorter though if we can - I wouldn't way what it isn't for example.

		Calibration file load failed.
		Follow instructions in Documentation/ *

+ write some docs on the calibration procedure.  I don't think it is a
good plan to give a guide in a kernel log.

> +		caldata = NULL;

I'd hope that is already the case and it definitely looks like it is from a quick look
at request_firmware(). I'd consider request_firmware buggy if it did anything else
as that would imply it had side effects that weren't cleaned up on error.

> +	}
> +
> +	ret = bno055_init(priv, caldata);
> +	if (caldata)
> +		release_firmware(caldata);
> +	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);
> +
...

Thanks,

Jonathan

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 10/10] iio: imu: add BNO055 I2C driver
  2021-10-28 10:18   ` [v2 10/10] iio: imu: add BNO055 I2C driver Andrea Merello
  2021-10-28 11:10     ` Jonathan Cameron
@ 2021-10-28 22:04     ` Randy Dunlap
  2021-11-09 11:56       ` Andrea Merello
  2021-10-29 13:30     ` kernel test robot
  2 siblings, 1 reply; 55+ messages in thread
From: Randy Dunlap @ 2021-10-28 22:04 UTC (permalink / raw)
  To: Andrea Merello, jic23, mchehab+huawei, linux-iio, linux-kernel,
	devicetree
  Cc: lars, robh+dt, andy.shevchenko, matt.ranostay, ardeleanalex,
	jacopo, Andrea Merello

On 10/28/21 3:18 AM, Andrea Merello wrote:
> 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.
> 
> Signed-off-by: Andrea Merello <andrea.merello@iit.it>
> ---
>   drivers/iio/imu/bno055/Kconfig      |  6 ++++
>   drivers/iio/imu/bno055/Makefile     |  1 +
>   drivers/iio/imu/bno055/bno055_i2c.c | 54 +++++++++++++++++++++++++++++
>   3 files changed, 61 insertions(+)
>   create mode 100644 drivers/iio/imu/bno055/bno055_i2c.c
> 
> diff --git a/drivers/iio/imu/bno055/Kconfig b/drivers/iio/imu/bno055/Kconfig
> index 941e43f0368d..87200787d548 100644
> --- a/drivers/iio/imu/bno055/Kconfig
> +++ b/drivers/iio/imu/bno055/Kconfig
> @@ -7,3 +7,9 @@ config BOSH_BNO055_SERIAL
>   	tristate "Bosh BNO055 attached via serial bus"
>   	depends on SERIAL_DEV_BUS
>   	select BOSH_BNO055_IIO
> +
> +config BOSH_BNO055_I2C
> +	tristate "Bosh BNO055 attached via I2C bus"
> +	depends on I2C
> +	select REGMAP_I2C
> +	select BOSH_BNO055_IIO

Hi,

The config entries that have user prompt strings should also
have help text.  scripts/checkpatch.pl should have told you
about that...

-- 
~Randy

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 09/10] iio: imu: add BNO055 serdev driver
  2021-10-28 10:18   ` [v2 09/10] iio: imu: add BNO055 serdev driver Andrea Merello
  2021-10-28 12:31     ` Jonathan Cameron
@ 2021-10-29  7:09     ` kernel test robot
  2021-10-29 12:59     ` kernel test robot
  2 siblings, 0 replies; 55+ messages in thread
From: kernel test robot @ 2021-10-29  7:09 UTC (permalink / raw)
  To: Andrea Merello, jic23, mchehab+huawei, linux-iio, linux-kernel,
	devicetree
  Cc: kbuild-all, lars, robh+dt, andy.shevchenko, matt.ranostay,
	ardeleanalex

[-- Attachment #1: Type: text/plain, Size: 6416 bytes --]

Hi Andrea,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linux/master]
[also build test ERROR on linus/master v5.15-rc7]
[cannot apply to jic23-iio/togreg next-20211028]
[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/utils_macro-introduce-find_closest_unsorted/20211028-191851
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 2f111a6fd5b5297b4e92f53798ca086f7c7d33a4
config: arc-allyesconfig (attached as .config)
compiler: arceb-elf-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/185166ebe83b933e30af55d4dc7972db6f9a8fb8
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Andrea-Merello/utils_macro-introduce-find_closest_unsorted/20211028-191851
        git checkout 185166ebe83b933e30af55d4dc7972db6f9a8fb8
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=arc 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

>> drivers/iio/imu/bno055/bno055.c:234:5: error: no previous prototype for 'bno055_calibration_load' [-Werror=missing-prototypes]
     234 | int bno055_calibration_load(struct bno055_priv *priv, const struct firmware *fw)
         |     ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/imu/bno055/bno055.c: In function 'bno055_fusion_enable_store':
>> drivers/iio/imu/bno055/bno055.c:917:13: error: variable 'ret' set but not used [-Werror=unused-but-set-variable]
     917 |         int ret = 0;
         |             ^~~
   drivers/iio/imu/bno055/bno055.c: At top level:
>> drivers/iio/imu/bno055/bno055.c:1130:5: error: no previous prototype for 'bno055_debugfs_reg_access' [-Werror=missing-prototypes]
    1130 | int bno055_debugfs_reg_access(struct iio_dev *iio_dev, unsigned int reg,
         |     ^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/imu/bno055/bno055.c: In function 'bno055_trigger_handler':
>> drivers/iio/imu/bno055/bno055.c:1330:9: error: implicit declaration of function 'for_each_set_bitrange'; did you mean 'for_each_set_bit'? [-Werror=implicit-function-declaration]
    1330 |         for_each_set_bitrange(start, end, iio_dev->active_scan_mask,
         |         ^~~~~~~~~~~~~~~~~~~~~
         |         for_each_set_bit
>> drivers/iio/imu/bno055/bno055.c:1331:51: error: expected ';' before '{' token
    1331 |                               iio_dev->masklength) {
         |                                                   ^~
         |                                                   ;
>> drivers/iio/imu/bno055/bno055.c:1364:1: error: label 'done' defined but not used [-Werror=unused-label]
    1364 | done:
         | ^~~~
>> drivers/iio/imu/bno055/bno055.c:1327:13: error: unused variable 'ret' [-Werror=unused-variable]
    1327 |         int ret;
         |             ^~~
>> drivers/iio/imu/bno055/bno055.c:1326:13: error: unused variable 'quat' [-Werror=unused-variable]
    1326 |         int quat;
         |             ^~~~
>> drivers/iio/imu/bno055/bno055.c:1325:14: error: unused variable 'thr_hit' [-Werror=unused-variable]
    1325 |         bool thr_hit;
         |              ^~~~~~~
>> drivers/iio/imu/bno055/bno055.c:1324:13: error: unused variable 'buf_idx' [-Werror=unused-variable]
    1324 |         int buf_idx = 0;
         |             ^~~~~~~
>> drivers/iio/imu/bno055/bno055.c:1323:23: error: unused variable 'mask' [-Werror=unused-variable]
    1323 |         unsigned long mask;
         |                       ^~~~
>> drivers/iio/imu/bno055/bno055.c:1322:14: error: unused variable 'first' [-Werror=unused-variable]
    1322 |         bool first = true;
         |              ^~~~~
>> drivers/iio/imu/bno055/bno055.c:1321:14: error: unused variable 'xfer_pending' [-Werror=unused-variable]
    1321 |         bool xfer_pending = false;
         |              ^~~~~~~~~~~~
>> drivers/iio/imu/bno055/bno055.c:1320:37: error: unused variable 'prev_end' [-Werror=unused-variable]
    1320 |         int xfer_start, start, end, prev_end;
         |                                     ^~~~~~~~
>> drivers/iio/imu/bno055/bno055.c:1320:13: error: unused variable 'xfer_start' [-Werror=unused-variable]
    1320 |         int xfer_start, start, end, prev_end;
         |             ^~~~~~~~~~
   At top level:
>> drivers/iio/imu/bno055/bno055.c:1262:12: error: 'bno055_scan_xfer' defined but not used [-Werror=unused-function]
    1262 | static int bno055_scan_xfer(struct bno055_priv *priv,
         |            ^~~~~~~~~~~~~~~~
   cc1: all warnings being treated as errors


vim +/bno055_calibration_load +234 drivers/iio/imu/bno055/bno055.c

734efd9783b7759 Andrea Merello 2021-10-28  232  
734efd9783b7759 Andrea Merello 2021-10-28  233  /* must be called in configuration mode */
734efd9783b7759 Andrea Merello 2021-10-28 @234  int bno055_calibration_load(struct bno055_priv *priv, const struct firmware *fw)
734efd9783b7759 Andrea Merello 2021-10-28  235  {
734efd9783b7759 Andrea Merello 2021-10-28  236  	if (fw->size != BNO055_CALDATA_LEN) {
734efd9783b7759 Andrea Merello 2021-10-28  237  		dev_dbg(priv->dev, "Invalid calibration file size %d (expected %d)",
734efd9783b7759 Andrea Merello 2021-10-28  238  			fw->size, BNO055_CALDATA_LEN);
734efd9783b7759 Andrea Merello 2021-10-28  239  		return -EINVAL;
734efd9783b7759 Andrea Merello 2021-10-28  240  	}
734efd9783b7759 Andrea Merello 2021-10-28  241  
734efd9783b7759 Andrea Merello 2021-10-28  242  	dev_dbg(priv->dev, "loading cal data: %*ph", BNO055_CALDATA_LEN, fw->data);
734efd9783b7759 Andrea Merello 2021-10-28  243  	return regmap_bulk_write(priv->regmap, BNO055_CALDATA_START,
734efd9783b7759 Andrea Merello 2021-10-28  244  				fw->data, BNO055_CALDATA_LEN);
734efd9783b7759 Andrea Merello 2021-10-28  245  }
734efd9783b7759 Andrea Merello 2021-10-28  246  

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

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 69504 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 09/10] iio: imu: add BNO055 serdev driver
  2021-10-28 10:18   ` [v2 09/10] iio: imu: add BNO055 serdev driver Andrea Merello
  2021-10-28 12:31     ` Jonathan Cameron
  2021-10-29  7:09     ` kernel test robot
@ 2021-10-29 12:59     ` kernel test robot
  2 siblings, 0 replies; 55+ messages in thread
From: kernel test robot @ 2021-10-29 12:59 UTC (permalink / raw)
  To: Andrea Merello, jic23, mchehab+huawei, linux-iio, linux-kernel,
	devicetree
  Cc: kbuild-all, lars, robh+dt, andy.shevchenko, matt.ranostay,
	ardeleanalex

[-- Attachment #1: Type: text/plain, Size: 6386 bytes --]

Hi Andrea,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on linux/master]
[also build test WARNING on linus/master v5.15-rc7]
[cannot apply to jic23-iio/togreg next-20211029]
[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/utils_macro-introduce-find_closest_unsorted/20211028-191851
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 2f111a6fd5b5297b4e92f53798ca086f7c7d33a4
config: m68k-randconfig-r011-20211029 (attached as .config)
compiler: m68k-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/185166ebe83b933e30af55d4dc7972db6f9a8fb8
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Andrea-Merello/utils_macro-introduce-find_closest_unsorted/20211028-191851
        git checkout 185166ebe83b933e30af55d4dc7972db6f9a8fb8
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=m68k 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/iio/imu/bno055/bno055.c:234:5: warning: no previous prototype for 'bno055_calibration_load' [-Wmissing-prototypes]
     234 | int bno055_calibration_load(struct bno055_priv *priv, const struct firmware *fw)
         |     ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/imu/bno055/bno055.c: In function 'bno055_fusion_enable_store':
>> drivers/iio/imu/bno055/bno055.c:917:13: warning: variable 'ret' set but not used [-Wunused-but-set-variable]
     917 |         int ret = 0;
         |             ^~~
   drivers/iio/imu/bno055/bno055.c: At top level:
>> drivers/iio/imu/bno055/bno055.c:1188:5: warning: no previous prototype for 'bno055_debugfs_reg_access' [-Wmissing-prototypes]
    1188 | int bno055_debugfs_reg_access(struct iio_dev *iio_dev, unsigned int reg,
         |     ^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/imu/bno055/bno055.c: In function 'bno055_trigger_handler':
   drivers/iio/imu/bno055/bno055.c:1330:9: error: implicit declaration of function 'for_each_set_bitrange'; did you mean 'for_each_set_bit'? [-Werror=implicit-function-declaration]
    1330 |         for_each_set_bitrange(start, end, iio_dev->active_scan_mask,
         |         ^~~~~~~~~~~~~~~~~~~~~
         |         for_each_set_bit
   drivers/iio/imu/bno055/bno055.c:1331:51: error: expected ';' before '{' token
    1331 |                               iio_dev->masklength) {
         |                                                   ^~
         |                                                   ;
   drivers/iio/imu/bno055/bno055.c:1364:1: warning: label 'done' defined but not used [-Wunused-label]
    1364 | done:
         | ^~~~
   drivers/iio/imu/bno055/bno055.c:1327:13: warning: unused variable 'ret' [-Wunused-variable]
    1327 |         int ret;
         |             ^~~
   drivers/iio/imu/bno055/bno055.c:1326:13: warning: unused variable 'quat' [-Wunused-variable]
    1326 |         int quat;
         |             ^~~~
   drivers/iio/imu/bno055/bno055.c:1325:14: warning: unused variable 'thr_hit' [-Wunused-variable]
    1325 |         bool thr_hit;
         |              ^~~~~~~
   drivers/iio/imu/bno055/bno055.c:1324:13: warning: unused variable 'buf_idx' [-Wunused-variable]
    1324 |         int buf_idx = 0;
         |             ^~~~~~~
   drivers/iio/imu/bno055/bno055.c:1323:23: warning: unused variable 'mask' [-Wunused-variable]
    1323 |         unsigned long mask;
         |                       ^~~~
   drivers/iio/imu/bno055/bno055.c:1322:14: warning: unused variable 'first' [-Wunused-variable]
    1322 |         bool first = true;
         |              ^~~~~
   drivers/iio/imu/bno055/bno055.c:1321:14: warning: unused variable 'xfer_pending' [-Wunused-variable]
    1321 |         bool xfer_pending = false;
         |              ^~~~~~~~~~~~
   drivers/iio/imu/bno055/bno055.c:1320:37: warning: unused variable 'prev_end' [-Wunused-variable]
    1320 |         int xfer_start, start, end, prev_end;
         |                                     ^~~~~~~~
   drivers/iio/imu/bno055/bno055.c:1320:13: warning: unused variable 'xfer_start' [-Wunused-variable]
    1320 |         int xfer_start, start, end, prev_end;
         |             ^~~~~~~~~~
   At top level:
   drivers/iio/imu/bno055/bno055.c:1262:12: warning: 'bno055_scan_xfer' defined but not used [-Wunused-function]
    1262 | static int bno055_scan_xfer(struct bno055_priv *priv,
         |            ^~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors


vim +/bno055_calibration_load +234 drivers/iio/imu/bno055/bno055.c

734efd9783b7759 Andrea Merello 2021-10-28  232  
734efd9783b7759 Andrea Merello 2021-10-28  233  /* must be called in configuration mode */
734efd9783b7759 Andrea Merello 2021-10-28 @234  int bno055_calibration_load(struct bno055_priv *priv, const struct firmware *fw)
734efd9783b7759 Andrea Merello 2021-10-28  235  {
734efd9783b7759 Andrea Merello 2021-10-28  236  	if (fw->size != BNO055_CALDATA_LEN) {
734efd9783b7759 Andrea Merello 2021-10-28  237  		dev_dbg(priv->dev, "Invalid calibration file size %d (expected %d)",
734efd9783b7759 Andrea Merello 2021-10-28  238  			fw->size, BNO055_CALDATA_LEN);
734efd9783b7759 Andrea Merello 2021-10-28  239  		return -EINVAL;
734efd9783b7759 Andrea Merello 2021-10-28  240  	}
734efd9783b7759 Andrea Merello 2021-10-28  241  
734efd9783b7759 Andrea Merello 2021-10-28  242  	dev_dbg(priv->dev, "loading cal data: %*ph", BNO055_CALDATA_LEN, fw->data);
734efd9783b7759 Andrea Merello 2021-10-28  243  	return regmap_bulk_write(priv->regmap, BNO055_CALDATA_START,
734efd9783b7759 Andrea Merello 2021-10-28  244  				fw->data, BNO055_CALDATA_LEN);
734efd9783b7759 Andrea Merello 2021-10-28  245  }
734efd9783b7759 Andrea Merello 2021-10-28  246  

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

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 27609 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 10/10] iio: imu: add BNO055 I2C driver
  2021-10-28 10:18   ` [v2 10/10] iio: imu: add BNO055 I2C driver Andrea Merello
  2021-10-28 11:10     ` Jonathan Cameron
  2021-10-28 22:04     ` Randy Dunlap
@ 2021-10-29 13:30     ` kernel test robot
  2 siblings, 0 replies; 55+ messages in thread
From: kernel test robot @ 2021-10-29 13:30 UTC (permalink / raw)
  To: Andrea Merello, jic23, mchehab+huawei, linux-iio, linux-kernel,
	devicetree
  Cc: kbuild-all, lars, robh+dt, andy.shevchenko, matt.ranostay,
	ardeleanalex

[-- Attachment #1: Type: text/plain, Size: 8517 bytes --]

Hi Andrea,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on linux/master]
[also build test WARNING on linus/master v5.15-rc7]
[cannot apply to jic23-iio/togreg next-20211029]
[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/utils_macro-introduce-find_closest_unsorted/20211028-191851
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 2f111a6fd5b5297b4e92f53798ca086f7c7d33a4
config: alpha-randconfig-r014-20211029 (attached as .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/fab64510c8234ae8acfbc853cc8c433ae831f7b3
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Andrea-Merello/utils_macro-introduce-find_closest_unsorted/20211028-191851
        git checkout fab64510c8234ae8acfbc853cc8c433ae831f7b3
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=alpha 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   drivers/iio/imu/bno055/bno055.c:234:5: warning: no previous prototype for 'bno055_calibration_load' [-Wmissing-prototypes]
     234 | int bno055_calibration_load(struct bno055_priv *priv, const struct firmware *fw)
         |     ^~~~~~~~~~~~~~~~~~~~~~~
   In file included from include/linux/printk.h:555,
                    from include/linux/kernel.h:19,
                    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:237:36: warning: format '%d' expects argument of type 'int', but argument 4 has type 'size_t' {aka 'long unsigned int'} [-Wformat=]
     237 |                 dev_dbg(priv->dev, "Invalid calibration file size %d (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:237:17: note: in expansion of macro 'dev_dbg'
     237 |                 dev_dbg(priv->dev, "Invalid calibration file size %d (expected %d)",
         |                 ^~~~~~~
   drivers/iio/imu/bno055/bno055.c:237:68: note: format string is defined here
     237 |                 dev_dbg(priv->dev, "Invalid calibration file size %d (expected %d)",
         |                                                                   ~^
         |                                                                    |
         |                                                                    int
         |                                                                   %ld
   drivers/iio/imu/bno055/bno055.c: In function 'bno055_fusion_enable_store':
   drivers/iio/imu/bno055/bno055.c:917:13: warning: variable 'ret' set but not used [-Wunused-but-set-variable]
     917 |         int ret = 0;
         |             ^~~
   drivers/iio/imu/bno055/bno055.c: At top level:
   drivers/iio/imu/bno055/bno055.c:1130:5: warning: no previous prototype for 'bno055_debugfs_reg_access' [-Wmissing-prototypes]
    1130 | int bno055_debugfs_reg_access(struct iio_dev *iio_dev, unsigned int reg,
         |     ^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/imu/bno055/bno055.c: In function 'bno055_trigger_handler':
   drivers/iio/imu/bno055/bno055.c:1330:9: error: implicit declaration of function 'for_each_set_bitrange'; did you mean 'for_each_set_bit'? [-Werror=implicit-function-declaration]
    1330 |         for_each_set_bitrange(start, end, iio_dev->active_scan_mask,
         |         ^~~~~~~~~~~~~~~~~~~~~
         |         for_each_set_bit
   drivers/iio/imu/bno055/bno055.c:1331:51: error: expected ';' before '{' token
    1331 |                               iio_dev->masklength) {
         |                                                   ^~
         |                                                   ;
   drivers/iio/imu/bno055/bno055.c:1364:1: warning: label 'done' defined but not used [-Wunused-label]
    1364 | done:
         | ^~~~
   drivers/iio/imu/bno055/bno055.c:1327:13: warning: unused variable 'ret' [-Wunused-variable]
    1327 |         int ret;
         |             ^~~
   drivers/iio/imu/bno055/bno055.c:1326:13: warning: unused variable 'quat' [-Wunused-variable]
    1326 |         int quat;
         |             ^~~~
   drivers/iio/imu/bno055/bno055.c:1325:14: warning: unused variable 'thr_hit' [-Wunused-variable]
    1325 |         bool thr_hit;
         |              ^~~~~~~
   drivers/iio/imu/bno055/bno055.c:1324:13: warning: unused variable 'buf_idx' [-Wunused-variable]
    1324 |         int buf_idx = 0;
         |             ^~~~~~~
   drivers/iio/imu/bno055/bno055.c:1323:23: warning: unused variable 'mask' [-Wunused-variable]
    1323 |         unsigned long mask;
         |                       ^~~~
   drivers/iio/imu/bno055/bno055.c:1322:14: warning: unused variable 'first' [-Wunused-variable]
    1322 |         bool first = true;
         |              ^~~~~
   drivers/iio/imu/bno055/bno055.c:1321:14: warning: unused variable 'xfer_pending' [-Wunused-variable]
    1321 |         bool xfer_pending = false;
         |              ^~~~~~~~~~~~
   drivers/iio/imu/bno055/bno055.c:1320:37: warning: unused variable 'prev_end' [-Wunused-variable]
    1320 |         int xfer_start, start, end, prev_end;
         |                                     ^~~~~~~~
   drivers/iio/imu/bno055/bno055.c:1320:13: warning: unused variable 'xfer_start' [-Wunused-variable]
    1320 |         int xfer_start, start, end, prev_end;
         |             ^~~~~~~~~~
   At top level:
   drivers/iio/imu/bno055/bno055.c:1262:12: warning: 'bno055_scan_xfer' defined but not used [-Wunused-function]
    1262 | static int bno055_scan_xfer(struct bno055_priv *priv,
         |            ^~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors


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

734efd9783b7759 Andrea Merello 2021-10-28  232  
734efd9783b7759 Andrea Merello 2021-10-28  233  /* must be called in configuration mode */
734efd9783b7759 Andrea Merello 2021-10-28 @234  int bno055_calibration_load(struct bno055_priv *priv, const struct firmware *fw)
734efd9783b7759 Andrea Merello 2021-10-28  235  {
734efd9783b7759 Andrea Merello 2021-10-28  236  	if (fw->size != BNO055_CALDATA_LEN) {
734efd9783b7759 Andrea Merello 2021-10-28 @237  		dev_dbg(priv->dev, "Invalid calibration file size %d (expected %d)",
734efd9783b7759 Andrea Merello 2021-10-28  238  			fw->size, BNO055_CALDATA_LEN);
734efd9783b7759 Andrea Merello 2021-10-28  239  		return -EINVAL;
734efd9783b7759 Andrea Merello 2021-10-28  240  	}
734efd9783b7759 Andrea Merello 2021-10-28  241  
734efd9783b7759 Andrea Merello 2021-10-28  242  	dev_dbg(priv->dev, "loading cal data: %*ph", BNO055_CALDATA_LEN, fw->data);
734efd9783b7759 Andrea Merello 2021-10-28  243  	return regmap_bulk_write(priv->regmap, BNO055_CALDATA_START,
734efd9783b7759 Andrea Merello 2021-10-28  244  				fw->data, BNO055_CALDATA_LEN);
734efd9783b7759 Andrea Merello 2021-10-28  245  }
734efd9783b7759 Andrea Merello 2021-10-28  246  

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

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 36323 bytes --]

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 01/10] utils_macro: introduce find_closest_unsorted()
  2021-10-28 10:25     ` Andy Shevchenko
@ 2021-11-08 11:05       ` Andrea Merello
  0 siblings, 0 replies; 55+ messages in thread
From: Andrea Merello @ 2021-11-08 11:05 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Jonathan Cameron, Mauro Carvalho Chehab, linux-iio,
	Linux Kernel Mailing List, devicetree, Lars-Peter Clausen,
	Rob Herring, Matt Ranostay, Alexandru Ardelean, jmondi,
	Andrea Merello

Il giorno gio 28 ott 2021 alle ore 12:26 Andy Shevchenko
<andy.shevchenko@gmail.com> ha scritto:
>
> On Thu, Oct 28, 2021 at 1:18 PM Andrea Merello <andrea.merello@gmail.com> wrote:
> >
> > This is similar to find_closest() and find_closest_descending(), but, it
> > doesn't make any assumption about the array being ordered.
>
> Macros in general are not so welcoming.
> Why do you do it as a macro?

Honestly, I did that just because find_closest() and
find_closest_descending() are macros (i.e. to be consistent wrt them).
I see no drawbacks in making this a regular function indeed; just, do
you have any advice about where should it live?

> ...
>
> > +#include <linux/math.h>
>
> Wondering if the current header misses other inclusions it's a direct user of.

Looking at it, it seems that also __find_closest() actually needs
math.h because it (apparently incorrectly[*]) uses
DIV_ROUND_CLOSEST()..

[*]Indeed it seems there is another issue here about find_closest():
for example it picks the 1st element while searching for "2" in an
array like this: {1,2,..} ..This needs to be reported/fixed..



> ...
>
> > +/**
> > + * find_closest_unsorted - locate the closest element in a unsorted array
>
> an
>
> > + * @x: The reference value.
> > + * @a: The array in which to look for the closest element.
> > + * @as: Size of 'a'.
> > + *
> > + * Similar to find_closest() but 'a' has no requirement to being sorted
> > + */
>
> --
> With Best Regards,
> Andy Shevchenko

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 02/10] iio: document linear acceleration modifiers
  2021-10-28 10:31     ` Andy Shevchenko
@ 2021-11-09  7:48       ` Andrea Merello
  0 siblings, 0 replies; 55+ messages in thread
From: Andrea Merello @ 2021-11-09  7:48 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Jonathan Cameron, Mauro Carvalho Chehab, linux-iio,
	Linux Kernel Mailing List, devicetree, Lars-Peter Clausen,
	Rob Herring, Matt Ranostay, Alexandru Ardelean, jmondi,
	Andrea Merello

Il giorno gio 28 ott 2021 alle ore 12:32 Andy Shevchenko
<andy.shevchenko@gmail.com> ha scritto:
>
> On Thu, Oct 28, 2021 at 1:18 PM Andrea Merello <andrea.merello@gmail.com> wrote:
> >
> > This patch introduces ABI documentation for new iio modifiers used for
> > reporting "linear acceleration" measures.
>
> Because of ordering and absence of Fixes tag I haven't clearly got if
> this is an existing set of attributes or that that will be added by
> the series. If the former, use a Fixes tag and place it first in the
> series. If the latter, move it after the actual addition of the
> attributes in the code.

The latter. Will move in V3.
Thanks

> --
> With Best Regards,
> Andy Shevchenko

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 02/10] iio: document linear acceleration modifiers
  2021-10-28 10:40     ` Jonathan Cameron
@ 2021-11-09  8:00       ` Andrea Merello
  2021-11-09 17:00         ` Jonathan Cameron
  0 siblings, 1 reply; 55+ messages in thread
From: Andrea Merello @ 2021-11-09  8:00 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Mauro Carvalho Chehab, linux-iio, linux-kernel, devicetree,
	Lars-Peter Clausen, Rob Herring, Andy Shevchenko, Matt Ranostay,
	Alexandru Ardelean, Jacopo Mondi, Andrea Merello

Il giorno gio 28 ott 2021 alle ore 12:35 Jonathan Cameron
<jic23@kernel.org> ha scritto:
>
> On Thu, 28 Oct 2021 12:18:32 +0200
> Andrea Merello <andrea.merello@gmail.com> wrote:
>
> > This patch introduces ABI documentation for new iio modifiers used for
> > reporting "linear acceleration" measures.
> >
> > Signed-off-by: Andrea Merello <andrea.merello@iit.it>
> > ---
> >  Documentation/ABI/testing/sysfs-bus-iio | 8 ++++++++
> >  1 file changed, 8 insertions(+)
> >
> > diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> > index 6ad47a67521c..5147a00bf24a 100644
> > --- a/Documentation/ABI/testing/sysfs-bus-iio
> > +++ b/Documentation/ABI/testing/sysfs-bus-iio
> > @@ -1957,3 +1957,11 @@ Description:
> >               Specify the percent for light sensor relative to the channel
> >               absolute value that a data field should change before an event
> >               is generated. Units are a percentage of the prior reading.
> > +
> > +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.15
> > +Contact:     linux-iio@vger.kernel.org
> > +Description:
> > +             Raw (unscaled) linear acceleration readings.
>
> Probably need more information that this.   What element is being 'removed' from
> a normal acceleration measurement? What are units after application of offset and
> scale?  Can cross refer to the in_accel_x_raw for that info if you like.

OK.  So, may I just state something like "As per in_accel_X_raw
attributes, but minus the gravity acceleration"  ?

> Also, but them immediately after the block with the in_accel_x_raw etc

OK

> The organization fo that file needs a rethink but let us try to avoid making
> it worse in the meeantime!
>
> Jonathan
>
>

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 03/10] iio: document euler angles modifiers
  2021-10-28 10:41     ` Jonathan Cameron
@ 2021-11-09  8:15       ` Andrea Merello
  2021-11-09 17:03         ` Jonathan Cameron
  0 siblings, 1 reply; 55+ messages in thread
From: Andrea Merello @ 2021-11-09  8:15 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Mauro Carvalho Chehab, linux-iio, linux-kernel, devicetree,
	Lars-Peter Clausen, Rob Herring, Andy Shevchenko, Matt Ranostay,
	Alexandru Ardelean, Jacopo Mondi, Andrea Merello

Il giorno gio 28 ott 2021 alle ore 12:37 Jonathan Cameron
<jic23@kernel.org> ha scritto:
>
> On Thu, 28 Oct 2021 12:18:33 +0200
> Andrea Merello <andrea.merello@gmail.com> wrote:
>
> > This patch introduces ABI documentation for new modifiers used for
> > reporting rotations expressed as euler angles (i.e. yaw, pitch, roll).
> >
> > Signed-off-by: Andrea Merello <andrea.merello@iit.it>
> > ---
> >  Documentation/ABI/testing/sysfs-bus-iio | 8 ++++++++
> >  1 file changed, 8 insertions(+)
> >
> > diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> > index 5147a00bf24a..f0adc2c817bd 100644
> > --- a/Documentation/ABI/testing/sysfs-bus-iio
> > +++ b/Documentation/ABI/testing/sysfs-bus-iio
> > @@ -1965,3 +1965,11 @@ KernelVersion: 5.15
> >  Contact:     linux-iio@vger.kernel.org
> >  Description:
> >               Raw (unscaled) linear acceleration readings.
> > +
> > +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.15
> > +Contact:     linux-iio@vger.kernel.org
> > +Description:
> > +             Raw (unscaled) euler angles readings.
> Any _raw entry should also include what the units are after application of
> offset and scale.   Or you could just add this as more info to the in_rot_raw
> block as an extra sentence explaining that they are euler angles.
> That will lose the 'KernelVersion' information but honestly I'm not sure we
> care that much about that.

I'm unsure which block you are talking about: I see there are two
blocks that refer to rot things: in_rot_quaternion_raw and
in_rot_from_north_xxx_raw.

Looking at the 1st one description, it looks very specific to
quaternions to me; the 2nd seems very specific to its own thing,
whatever it is.. So I would just add the missing information (unit) in
the new block just being introduced, if this is ok for you. Or am I
missing some other block in which I could  coalesce this new euler
thing?


> Jonathan
>
>

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 04/10] iio: add modifiers for linear acceleration
  2021-10-28 10:45     ` Jonathan Cameron
@ 2021-11-09  9:58       ` Andrea Merello
  2021-11-09 17:05         ` Jonathan Cameron
  0 siblings, 1 reply; 55+ messages in thread
From: Andrea Merello @ 2021-11-09  9:58 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Mauro Carvalho Chehab, linux-iio, linux-kernel, devicetree,
	Lars-Peter Clausen, Rob Herring, Andy Shevchenko, Matt Ranostay,
	Alexandru Ardelean, Jacopo Mondi, Andrea Merello

Il giorno gio 28 ott 2021 alle ore 12:41 Jonathan Cameron
<jic23@kernel.org> ha scritto:
>
> On Thu, 28 Oct 2021 12:18:34 +0200
> Andrea Merello <andrea.merello@gmail.com> wrote:
>
> > 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_ACCEL_LINEAR_X, IIO_MOD_ACCEL_LINEAR_Y and
> > IIO_MOD_ACCEL_LINEAR_Z modifiers to te IIO core.
> >
> > Signed-off-by: Andrea Merello <andrea.merello@iit.it>
>
> They sometimes get forgotten but we should also update
> tools/iio/iio_event_montitor.c to handle these new modifiers.

I'm not so familiar with this tool, but it seems like it has to do
with IIO events, which the bno055 driver doesn't use. On the other
hand the modifiers I would add are not used by any other driver right
now.

So I would say that it would end up in adding things that I couldn't
test.. Or is there any test infrastructure for this? It seems trivial,
just a matter of a few defines, so it shouldn't be an issue indeed..

> That can be a separate patch, but also fine to do it in this one.
>
> > ---
> >  drivers/iio/industrialio-core.c | 3 +++
> >  include/uapi/linux/iio/types.h  | 4 +++-
> >  2 files changed, 6 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> > index 2dbb37e09b8c..a79cb32207e4 100644
> > --- a/drivers/iio/industrialio-core.c
> > +++ b/drivers/iio/industrialio-core.c
> > @@ -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_ACCEL_LINEAR_X] = "linear_x",
> > +     [IIO_MOD_ACCEL_LINEAR_Y] = "linear_y",
> > +     [IIO_MOD_ACCEL_LINEAR_Z] = "linear_z"
> >  };
> >
> >  /* relies on pairs of these shared then separate */
> > diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
> > index 48c13147c0a8..db00f7c45f48 100644
> > --- a/include/uapi/linux/iio/types.h
> > +++ b/include/uapi/linux/iio/types.h
> > @@ -95,6 +95,9 @@ enum iio_modifier {
> >       IIO_MOD_ETHANOL,
> >       IIO_MOD_H2,
> >       IIO_MOD_O2,
> > +     IIO_MOD_ACCEL_LINEAR_X,
> > +     IIO_MOD_ACCEL_LINEAR_Y,
> > +     IIO_MOD_ACCEL_LINEAR_Z,
>
> It might be useful for other channel types, so probably drop the ACCEL
> part of the name.
>
> I'll admit I can't immediately think of what, but you never know.. :)

But in this case what should I write in the ABI documentation? If I
state that this is something that makes the gravity not being included
then isn't it intrinsically tied to be an acceleration?  Or, I do
that, and if someone eventually finds another use, then she/he will
change the ABI doc?

> >  };
> >
> >  enum iio_event_type {
> > @@ -114,4 +117,3 @@ enum iio_event_direction {
> >  };
> >
> >  #endif /* _UAPI_IIO_TYPES_H_ */
> > -
> ?
>
>

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 06/10] iio: document bno055 private sysfs attributes
  2021-10-28 11:04     ` Jonathan Cameron
@ 2021-11-09 10:22       ` Andrea Merello
  2021-11-14 16:20         ` Jonathan Cameron
  0 siblings, 1 reply; 55+ messages in thread
From: Andrea Merello @ 2021-11-09 10:22 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Mauro Carvalho Chehab, linux-iio, linux-kernel, devicetree,
	Lars-Peter Clausen, Rob Herring, Andy Shevchenko, Matt Ranostay,
	Alexandru Ardelean, Jacopo Mondi, Andrea Merello

Few inline  comments; ok for the rest.

Il giorno gio 28 ott 2021 alle ore 12:59 Jonathan Cameron
<jic23@kernel.org> ha scritto:
>
> On Thu, 28 Oct 2021 12:18:36 +0200
> Andrea Merello <andrea.merello@gmail.com> wrote:
>
> > This patch adds ABI documentation for bno055 driver private sysfs
> > attributes.
>
> Hohum. As normal I dislike custom attributes but reality is these
> don't map to anything 'standard' and I don't want them getting adopted
> in places where the 'standard' approach works.
>
> So thinking a bit more on this, I wonder if we can fit it into standard
> ABI.
>
> We can't use the normal range specification method of
> _scale because it's internal to the device and the output reading is
> unaffected.  The range specification via _raw_available would let us know
> the range, but it is not writeable so..
>
> A control that changes the internal scaling of the sensor in a fashion
> that is not visible to the outside world maps to calibscale.  Whilst
> that was intended for little tweaks to the input signal (often front
> end amplifier gain tweak) it works here.  It doesn't map through to
> anything userspace is expected to apply.  That combined with
> _raw_available to let us know what the result is should work?
>
> What do you think of that approach?  It's obviously a little more complex
> to handle in the driver, but it does map to existing ABI and avoids
> custom attributes etc.

If I read the ABI documentation, then I would say that calibscale has
nothing to do with this, but I think you have obviously a better
feeling than me about what calibscale is really for. To be honest I've
probably not a clear idea about what calibscale is indeed...

In general, I would say that is better to stick to standard attributes
when possible, and of course to avoid having the same thing mapped on
random custom attributes in each driver, but IMO only up to the extent
which doesn't  force something that is really something different to
map on a standard thing just because of the sake of having as much
standard things as possible... But all this is probably quite obvious,
and it all depends on the above (i.e. is it calibscale fitting well in
your opinion?) .. Up to you on this one..

BTW I'm missing why this should complicate the driver.. I guess I'll
find out if I'll implement it :)

> >
> > Signed-off-by: Andrea Merello <andrea.merello@iit.it>
> > ---
> >  .../ABI/testing/sysfs-bus-iio-bno055          | 84 +++++++++++++++++++
> >  1 file changed, 84 insertions(+)
> >  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-bno055
> >
> > diff --git a/Documentation/ABI/testing/sysfs-bus-iio-bno055 b/Documentation/ABI/testing/sysfs-bus-iio-bno055
> > new file mode 100644
> > index 000000000000..930a70c5a858
> > --- /dev/null
> > +++ b/Documentation/ABI/testing/sysfs-bus-iio-bno055
> > @@ -0,0 +1,84 @@
> > +What:                /sys/bus/iio/devices/iio:deviceX/in_accel_range
> > +KernelVersion:       5.15
> > +Contact:     linux-iio@vger.kernel.org
> > +Description:
> > +             Range for acceleration readings in G. 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).
>
> Having this in G but the sensor output in m/s^2 seems inconsistent.
>
> > +
> > +What:                /sys/bus/iio/devices/iio:deviceX/in_anglvel_range
> > +KernelVersion:       5.15
> > +Contact:     linux-iio@vger.kernel.org
> > +Description:
> > +             Range for angular velocity readings in dps. 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).
>
> Again, units need to match or this is going to be really confusing.
>
> > +
> > +What:                /sys/bus/iio/devices/iio:deviceX/in_accel_range_available
> > +KernelVersion:       5.15
> > +Contact:     linux-iio@vger.kernel.org
> > +Description:
> > +             List of allowed values for in_accel_range attribute
> > +
> > +What:                /sys/bus/iio/devices/iio:deviceX/in_anglvel_range_available
> > +KernelVersion:       5.15
> > +Contact:     linux-iio@vger.kernel.org
> > +Description:
> > +             List of allowed values for in_anglvel_range attribute
> > +
> > +What:                /sys/bus/iio/devices/iio:deviceX/fast_magnetometer_calibration_enable
> > +KernelVersion:       5.15
> > +Contact:     linux-iio@vger.kernel.org
> > +Description:
> > +             Can be 1 or 0. Enables/disables the "Fast Magnetometer
> > +             Calibration" HW function.
>
> Naming needs to be consistent with the ABI.  This is a channel type specific function
> and to match existing calibration related ABI naming it would be.
>
> in_magn_calibration_fast_enable
>
> Some of the others need renaming in a similar way.
>
> > +
> > +What:                /sys/bus/iio/devices/iio:deviceX/fusion_enable
> > +KernelVersion:       5.15
> > +Contact:     linux-iio@vger.kernel.org
> > +Description:
> > +             Can be 1 or 0. Enables/disables the "sensor fusion" (a.k.a.
> > +             NDOF) HW function.
> > +
> > +What:                /sys/bus/iio/devices/iio:deviceX/in_calibration_data
> > +KernelVersion:       5.15
> > +Contact:     linux-iio@vger.kernel.org
> > +Description:
> > +             Reports the binary calibration data blob for the IMU sensors.
>
> Why in_ ?  What channels does this apply to?
>
> > +
> > +What:                /sys/bus/iio/devices/iio:deviceX/in_autocalibration_status_accel
> > +KernelVersion:       5.15
> > +Contact:     linux-iio@vger.kernel.org
> > +Description:
> > +             Can be "Idle", "Bad", "Barely enough", "Fair", or "Good".
> > +             Report the autocalibration status for the accelerometer sensor.
>
> For interfaces that really don't have any chance of generalising this one is terrible.
> Any hope at all of mapping this to something numeric?
>
> in_accel_calibration_auto_status
>
>
> > +
> > +What:                /sys/bus/iio/devices/iio:deviceX/in_autocalibration_status_gyro
> > +KernelVersion:       5.15
> > +Contact:     linux-iio@vger.kernel.org
> > +Description:
> > +             Can be "Idle", "Bad", "Barely enough", "Fair", or "Good".
> > +             Reports the autocalibration status for the gyroscope sensor.
>
> in_angvel_calibration_auto_status
> etc.
>
> > +
> > +What:                /sys/bus/iio/devices/iio:deviceX/in_autocalibration_status_magn
> > +KernelVersion:       5.15
> > +Contact:     linux-iio@vger.kernel.org
> > +Description:
> > +             Can be "Idle", "Bad", "Barely enough", "Fair", or "Good".
> > +             Reports the autocalibration status for the magnetometer sensor.
> > +
> > +What:                /sys/bus/iio/devices/iio:deviceX/in_autocalibration_status_sys
> > +KernelVersion:       5.15
> > +Contact:     linux-iio@vger.kernel.org
> > +Description:
> > +             Can be "Idle", "Bad", "Barely enough", "Fair", or "Good".
> > +             Reports the status for the IMU overall autocalibration.
> > +
> > +What:                /sys/bus/iio/devices/iio:deviceX/unique_id
>
> Hmm. So normally we just dump these in the kernel log.  I guess you need it
> here to associate a calibration blob with a particular sensor?

Well, it was originally in kernel log, but putting in an attribute was
one of the changes that has been requested for V2.
It is needed by the user who copies the calibration data to the
calibration file, in order for her/him to be able to properly name it
(in case of more than 1 sensor on the same setup).

> We could put it in label, but that would stop us using that for things like
> positioning of the sensor.  So perhaps this is something that we should add
> to the main ABI doc.  Probably as serial_number rather than unique ID though.

OK, for renaming to "serial_number". I'm not sure they are
conceptually the same thing, but I think it works anyway.
Of course I can move its doc to the main file. Do you want a separate
patch for this?

> > +KernelVersion:       5.15
> > +Contact:     linux-iio@vger.kernel.org
> > +Description:
> > +             16-bytes, 2-digits-per-byte, HEX-string representing the sensor
> > +             unique ID number.
>

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 07/10] iio: imu: add Bosch Sensortec BNO055 core driver
  2021-10-28 13:31     ` Jonathan Cameron
@ 2021-11-09 11:52       ` Andrea Merello
  2021-11-14 16:33         ` Jonathan Cameron
  0 siblings, 1 reply; 55+ messages in thread
From: Andrea Merello @ 2021-11-09 11:52 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Mauro Carvalho Chehab, linux-iio, linux-kernel, devicetree,
	Lars-Peter Clausen, Rob Herring, Andy Shevchenko, Matt Ranostay,
	Alexandru Ardelean, Jacopo Mondi, Andrea Merello

Some inline notes. OK for all the rest.

Il giorno gio 28 ott 2021 alle ore 15:27 Jonathan Cameron
<jic23@kernel.org> ha scritto:
>
> On Thu, 28 Oct 2021 12:18:37 +0200
> Andrea Merello <andrea.merello@gmail.com> wrote:
>
> > 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.
> >
> > Signed-off-by: Andrea Merello <andrea.merello@iit.it>
> > ---
> >  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 | 1480 +++++++++++++++++++++++++++++++
> >  drivers/iio/imu/bno055/bno055.h |   12 +
> >  6 files changed, 1501 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
> >
> ...
>
> > diff --git a/drivers/iio/imu/bno055/bno055.c b/drivers/iio/imu/bno055/bno055.c
> > new file mode 100644
> > index 000000000000..c85cb985f0f1
> > --- /dev/null
> > +++ b/drivers/iio/imu/bno055/bno055.c
> > @@ -0,0 +1,1480 @@
>
> ...
>
> > +
> > +static int bno055_reg_update_bits(struct bno055_priv *priv, unsigned int reg,
> > +                               unsigned int mask, unsigned int val)
> > +{
> > +     int ret;
> > +
> > +     ret = regmap_update_bits(priv->regmap, reg, mask, val);
> > +     if (ret && ret != -ERESTARTSYS) {
> > +             dev_err(priv->dev, "Regmap update_bits  error. adr: 0x%x, ret: %d",
> > +                     reg,  ret);
>
> This feels like a wrapper that made sense when developing but probably doesn't
> want to still be here now things are 'working'.
>
> > +     }
> > +
> > +     return ret;
> > +}
> > +
>
> ...
>
> > +
> > +static void bno055_clk_disable(void *arg)
>
> Easy to make arg == priv->clk and turn this into a one line function.
> I'd expect these cleanup functions to be just above where probe() is defined rather
> than all the way up here.
>
> > +{
> > +     struct bno055_priv *priv = arg;
> > +
> > +     clk_disable_unprepare(priv->clk);
> > +}
> > +
>
> ...
>
> > +
> > +static int bno055_get_acc_lpf(struct bno055_priv *priv, int *val, int *val2)
> > +{
> > +     const int shift = __ffs(BNO055_ACC_CONFIG_LPF_MASK);
> > +     int hwval, idx;
> > +     int ret;
> > +
> > +     ret = regmap_read(priv->regmap, BNO055_ACC_CONFIG_REG, &hwval);
> > +     if (ret)
> > +             return ret;
> > +
> > +     idx = (hwval & BNO055_ACC_CONFIG_LPF_MASK) >> shift;
>
> Use FIELD_GET() and FIELD_PREP where possible rather than reinventing them.
>
> > +     *val = bno055_acc_lpf_vals[idx * 2];
> > +     *val2 = bno055_acc_lpf_vals[idx * 2 + 1];
> > +
> > +     return IIO_VAL_INT_PLUS_MICRO;
> > +}
> > +
> > +static int bno055_set_acc_lpf(struct bno055_priv *priv, int val, int val2)
> > +{
> > +     const int shift = __ffs(BNO055_ACC_CONFIG_LPF_MASK);
> > +     int req_val = val * 1000 + val2 / 1000;
> > +     bool first = true;
> > +     int best_delta;
> > +     int best_idx;
> > +     int tbl_val;
> > +     int delta;
> > +     int ret;
> > +     int i;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(bno055_acc_lpf_vals) / 2; i++) {
> > +             tbl_val = bno055_acc_lpf_vals[i * 2] * 1000 +
> > +                     bno055_acc_lpf_vals[i * 2 + 1] / 1000;
> > +             delta = abs(tbl_val - req_val);
> > +             if (first || delta < best_delta) {
> > +                     best_delta = delta;
> > +                     best_idx = i;
> > +                     first = false;
> > +             }
> > +     }
> > +
> > +     /*
> > +      * 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;
>
> Can we do this before the big loop above?
>
>
> > +
> > +     ret = regmap_write(priv->regmap, BNO055_OPR_MODE_REG,
> > +                        BNO055_OPR_MODE_CONFIG);
> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = bno055_reg_update_bits(priv, BNO055_ACC_CONFIG_REG,
> > +                                  BNO055_ACC_CONFIG_LPF_MASK,
> > +                                  best_idx << shift);
> > +
> > +     if (ret)
> > +             return ret;
> > +
> > +     return regmap_write(priv->regmap, BNO055_OPR_MODE_REG,
> > +                         BNO055_OPR_MODE_AMG);
> > +}
> > +
>
> ...
>
> > +
> > +#define bno055_get_mag_odr(p, v) \
> > +     bno055_get_regmask(p, v, \
> > +                        BNO055_MAG_CONFIG_REG, BNO055_MAG_CONFIG_ODR_MASK, \
> > +                        bno055_mag_odr_vals)
>
> I'm not really convinced this is a worthwhile abstraction as these are typically
> only used once.
>
> > +
> ...
>
> > +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 */
> > +                     *val2 = 900;
> > +                     break;
> > +             case IIO_ROT:
> > +                     /* Table 3-28: 1 degree = 16 LSB */
> > +                     *val2 = 16;
> > +                     break;
> > +             default:
> > +                     return -EINVAL;
> > +             }
> > +             return IIO_VAL_FRACTIONAL;
> > +     default:
> > +             return -EINVAL;
>
> default in the middle is a bit unusual. move it to the end.
>
> > +
> > +     case IIO_CHAN_INFO_SAMP_FREQ:
> > +             if (chan->type != IIO_MAGN)
> > +                     return -EINVAL;
> > +             else
> > +                     return bno055_get_mag_odr(priv, val);
> > +
> > +     case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
> > +             switch (chan->type) {
> > +             case IIO_ANGL_VEL:
> > +                     return bno055_get_gyr_lpf(priv, val);
> > +             case IIO_ACCEL:
> > +                     return bno055_get_acc_lpf(priv, val, val2);
> > +             default:
> > +                     return -EINVAL;
> > +             }
> > +     }
> > +}
> > +
>
>
> > +
> > +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;
>
> IIO_VAL_FRACTIONAL_LOG2?
>
> > +             return IIO_VAL_FRACTIONAL;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +}
> > +
>
> ...
>
> > +
> > +static ssize_t bno055_fusion_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 = 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.
> > +              * 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);
> > +     }
> > +
> > +     return len ?: len;
>
> return ret?: len; might make sense, though my inclination would be to use an explicit
> if (ret) at the various possible error locations.
>
> > +}
>
> ...
>
> > +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 = 0;
> > +
> > +     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);
> > +     } else {
> > +             if (priv->operation_mode == BNO055_OPR_MODE_AMG)
> > +                     return -EINVAL;
> > +     }
> > +
> > +     return len ?: ret;
>
> Don't think that will return ret which is what we want if it's set.
>
> > +}
> > +
>
> ...
>
> > +static ssize_t in_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 = regmap_write(priv->regmap, BNO055_OPR_MODE_REG,
> > +                        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 = regmap_write(priv->regmap, BNO055_OPR_MODE_REG, priv->operation_mode);
> > +     mutex_unlock(&priv->lock);
> > +     if (ret)
> > +             return ret;
>
> This is a case where I'd move the mutex_unlock after the check so that we have
> a nice shared error path via the unlock lable.
>
> > +
> > +     memcpy(buf, data, BNO055_CALDATA_LEN);
> > +
> > +     return BNO055_CALDATA_LEN;
> > +unlock:
> > +     mutex_unlock(&priv->lock);
> > +     return ret;
> > +}
> > +
> ...
>
> > +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 = devm_kasprintf(priv->dev, 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));
> > +     devm_kfree(priv->dev, buf);
>
> Why use devm managed allocations if you are just going to free it immediately?
>
> > +
> > +     return ret;
> > +}
> > +
>
> ...
>
> > +/*
> > + * 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.
>
> This is rather complex - I take we can't just fall back to letting the IIO core
> demux do all the hard work for us?

Hum. I'm not sure.. I admit that I'm not familiar with the demux
thing, but as far as I can see it needs to be initialized once with a
list containing all allowed scan masks; IIO core will pick one of them
and eventually cull extra samples it contains. Is this right?

I would say we may precalculate this list at probe time (depending on
the burst break threshold) and populate it with all the possible scan
masks in which there are no gaps < than the bust break threshold. But
this could be a quite high number of combinations..

This way the IIO layer will only request xfers in which gaps are
always > than burst break threshold, which the driver in turn will
always split in several xfers.

Does this make sense to you?

> > + */
> > +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;
> > +
> > +     /*
> > +      * 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;
> > +     bool xfer_pending = false;
> > +     bool first = true;
> > +     unsigned long mask;
> > +     int buf_idx = 0;
> > +     bool thr_hit;
> > +     int quat;
> > +     int ret;
> > +
> > +     mutex_lock(&priv->lock);
> > +     for_each_set_bitrange(start, end, iio_dev->active_scan_mask,
> > +                           iio_dev->masklength) {
>
> I'm not seeing this function in mainline...  I guess this series has a dependency
> I missed?

I've been pointed to Yuri Norov bitmap series (I mentioned this in the
cover letter). Assuming it is close to be merged, I've updated my drv
for its API changes, but if you prefer I can revert to the current
mainline API. It's a trivial change.

> > +             if (!xfer_pending)
> > +                     xfer_start = start;
> > +             xfer_pending = true;
> > +
> > +             if (!first) {
>
> first == true and we never get in here to set it to false.

That's awful. Possibly I've broken this while making changes for V2,
and my test program didn't catch it. Maybe it just impacts
performances, which, now I realize, I probably didn't rechek :(

> Perhaps we would benefit from a state machine diagram for this function?
> In general this function is complex enough to need documentation of what
> each major part is doing.
>
> > +                     quat = ((start > BNO055_SCAN_QUATERNION) &&
> > +                             (prev_end <= BNO055_SCAN_QUATERNION)) ? 3 : 0;
>
> Having quat == 3 for a variable named quat doesn't seem intuitive.
>
> > +                     thr_hit = (start - prev_end + quat) >
> > +                             priv->xfer_burst_break_thr;
> > +
> > +                     if (thr_hit) {
> > +                             mask = *iio_dev->active_scan_mask >> xfer_start;
> > +                             ret = bno055_scan_xfer(priv, xfer_start,
> > +                                                    prev_end - xfer_start + 1,
> > +                                                    mask, priv->buf.chans, &buf_idx);
> > +                             if (ret)
> > +                                     goto done;
> > +                             xfer_pending = false;
> > +                     }
> > +                     first = false;
> > +             }
> > +             prev_end = end;
> > +     }
> > +
> > +     if (xfer_pending) {
> > +             mask = *iio_dev->active_scan_mask >> xfer_start;
> > +             ret = bno055_scan_xfer(priv, xfer_start,
> > +                                    end - xfer_start + 1,
> > +                                    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;
> > +}
> > +
> > +int bno055_probe(struct device *dev, struct regmap *regmap,
> > +              int xfer_burst_break_thr)
> > +{
> > +     const struct firmware *caldata;
> > +     struct bno055_priv *priv;
> > +     struct iio_dev *iio_dev;
> > +     struct gpio_desc *rst;
> > +     char *fw_name_buf;
> > +     unsigned int val;
> > +     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;
>
> blank line here would hep readability a little I think.
>
> > +     rst = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
> > +     if (IS_ERR(rst))
> > +             return dev_err_probe(dev, PTR_ERR(rst), "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);
>
> As mentioned above, pass priv->clk into this as we don't need to see anything
> else in the callback.
>
> > +     if (ret)
> > +             return ret;
> > +
> > +     if (rst) {
> > +             usleep_range(5000, 10000);
> > +             gpiod_set_value_cansleep(rst, 0);
> > +             usleep_range(650000, 750000);
> > +     }
> > +
> > +     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;
> > +     }
> > +
> > +     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 = devm_kasprintf(dev, 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);
> > +     devm_kfree(dev, fw_name_buf);
> > +     if (ret)
> > +             ret = request_firmware(&caldata, BNO055_FW_GENERIC_NAME, dev);
> > +
> > +     if (ret) {
> > +             dev_notice(dev, "Failed to load calibration data firmware file; this has nothing to do with IMU main firmware.\nYou can calibrate your IMU (look for 'in_autocalibration_status*' files in sysfs) and then copy 'in_calibration_data' to your firmware file");
>
> As the notice has multiple lines, you can break at the \n points without any loss of greppability.
> It would be good to make this shorter though if we can - I wouldn't way what it isn't for example.
>
>                 Calibration file load failed.
>                 Follow instructions in Documentation/ *
>
> + write some docs on the calibration procedure.  I don't think it is a
> good plan to give a guide in a kernel log.
>
> > +             caldata = NULL;
>
> I'd hope that is already the case and it definitely looks like it is from a quick look
> at request_firmware(). I'd consider request_firmware buggy if it did anything else
> as that would imply it had side effects that weren't cleaned up on error.
>
> > +     }
> > +
> > +     ret = bno055_init(priv, caldata);
> > +     if (caldata)
> > +             release_firmware(caldata);
> > +     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);
> > +
> ...
>
> Thanks,
>
> Jonathan

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 10/10] iio: imu: add BNO055 I2C driver
  2021-10-28 22:04     ` Randy Dunlap
@ 2021-11-09 11:56       ` Andrea Merello
  2021-11-09 15:47         ` Randy Dunlap
  0 siblings, 1 reply; 55+ messages in thread
From: Andrea Merello @ 2021-11-09 11:56 UTC (permalink / raw)
  To: Randy Dunlap
  Cc: Jonathan Cameron, Mauro Carvalho Chehab, linux-iio, linux-kernel,
	devicetree, Lars-Peter Clausen, Rob Herring, Andy Shevchenko,
	Matt Ranostay, Alexandru Ardelean, Jacopo Mondi, Andrea Merello

Il giorno ven 29 ott 2021 alle ore 00:04 Randy Dunlap
<rdunlap@infradead.org> ha scritto:
>
> On 10/28/21 3:18 AM, Andrea Merello wrote:
> > 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.
> >
> > Signed-off-by: Andrea Merello <andrea.merello@iit.it>
> > ---
> >   drivers/iio/imu/bno055/Kconfig      |  6 ++++
> >   drivers/iio/imu/bno055/Makefile     |  1 +
> >   drivers/iio/imu/bno055/bno055_i2c.c | 54 +++++++++++++++++++++++++++++
> >   3 files changed, 61 insertions(+)
> >   create mode 100644 drivers/iio/imu/bno055/bno055_i2c.c
> >
> > diff --git a/drivers/iio/imu/bno055/Kconfig b/drivers/iio/imu/bno055/Kconfig
> > index 941e43f0368d..87200787d548 100644
> > --- a/drivers/iio/imu/bno055/Kconfig
> > +++ b/drivers/iio/imu/bno055/Kconfig
> > @@ -7,3 +7,9 @@ config BOSH_BNO055_SERIAL
> >       tristate "Bosh BNO055 attached via serial bus"
> >       depends on SERIAL_DEV_BUS
> >       select BOSH_BNO055_IIO
> > +
> > +config BOSH_BNO055_I2C
> > +     tristate "Bosh BNO055 attached via I2C bus"
> > +     depends on I2C
> > +     select REGMAP_I2C
> > +     select BOSH_BNO055_IIO
>
> Hi,
>
> The config entries that have user prompt strings should also
> have help text.  scripts/checkpatch.pl should have told you
> about that...

I'll add it, thanks. But FYI checkpatch doesn't complain about that here.

> --
> ~Randy

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 09/10] iio: imu: add BNO055 serdev driver
  2021-10-28 12:31     ` Jonathan Cameron
@ 2021-11-09 15:33       ` Andrea Merello
  2021-11-14 16:37         ` Jonathan Cameron
  0 siblings, 1 reply; 55+ messages in thread
From: Andrea Merello @ 2021-11-09 15:33 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Mauro Carvalho Chehab, linux-iio, linux-kernel, devicetree,
	Lars-Peter Clausen, Rob Herring, Andy Shevchenko, Matt Ranostay,
	Alexandru Ardelean, Jacopo Mondi, Andrea Merello

Few inline comments. Ok for all the rest.

Il giorno gio 28 ott 2021 alle ore 14:27 Jonathan Cameron
<jic23@kernel.org> ha scritto:
>
> On Thu, 28 Oct 2021 12:18:39 +0200
> Andrea Merello <andrea.merello@gmail.com> wrote:
>
> > 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.
> >
> > Signed-off-by: Andrea Merello <andrea.merello@iit.it>
>
> Hi Andrea,
>
> Some comments inline.  Note I'm not that familiar with the serial_dev interface
> so would definitely appreciate input from others on that part.
>
> Jonathan
> > ---
> >  drivers/iio/imu/bno055/Kconfig     |   5 +
> >  drivers/iio/imu/bno055/Makefile    |   1 +
> >  drivers/iio/imu/bno055/bno055_sl.c | 568 +++++++++++++++++++++++++++++
> >  3 files changed, 574 insertions(+)
> >  create mode 100644 drivers/iio/imu/bno055/bno055_sl.c
> >
> > diff --git a/drivers/iio/imu/bno055/Kconfig b/drivers/iio/imu/bno055/Kconfig
> > index d197310661af..941e43f0368d 100644
> > --- a/drivers/iio/imu/bno055/Kconfig
> > +++ b/drivers/iio/imu/bno055/Kconfig
> > @@ -2,3 +2,8 @@
> >
> >  config BOSH_BNO055_IIO
> >       tristate
> > +
> > +config BOSH_BNO055_SERIAL
> > +     tristate "Bosh BNO055 attached via serial bus"
> > +     depends on SERIAL_DEV_BUS
> > +     select BOSH_BNO055_IIO
> > diff --git a/drivers/iio/imu/bno055/Makefile b/drivers/iio/imu/bno055/Makefile
> > index c55741d0e96f..7285ade2f4b9 100644
> > --- a/drivers/iio/imu/bno055/Makefile
> > +++ b/drivers/iio/imu/bno055/Makefile
> > @@ -1,3 +1,4 @@
> >  # SPDX-License-Identifier: GPL-2.0
> >
> >  obj-$(CONFIG_BOSH_BNO055_IIO) += bno055.o
> > +obj-$(CONFIG_BOSH_BNO055_SERIAL) += bno055_sl.o
> > diff --git a/drivers/iio/imu/bno055/bno055_sl.c b/drivers/iio/imu/bno055/bno055_sl.c
> > new file mode 100644
> > index 000000000000..1d1410fdaa7c
> > --- /dev/null
> > +++ b/drivers/iio/imu/bno055/bno055_sl.c
> > @@ -0,0 +1,568 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Serial line interface for Bosh BNO055 IMU (via serdev).
> > + * This file implements serial communication up to the register read/write
> > + * level.
> > + *
> > + * Copyright (C) 2021 Istituto Italiano di Tecnologia
> > + * Electronic Design Laboratory
> > + * Written by Andrea Merello <andrea.merello@iit.it>
> > + *
> > + * This driver is besed on
> > + *   Plantower PMS7003 particulate matter sensor driver
> > + *   Which is
> > + *   Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
> > + */
> > +
> > +#include <linux/completion.h>
> > +#include <linux/device.h>
> > +#include <linux/errno.h>
> > +#include <linux/jiffies.h>
> > +#include <linux/kernel.h>
> > +#include <linux/mod_devicetable.h>
> > +#include <linux/module.h>
> > +#include <linux/mutex.h>
> > +#include <linux/regmap.h>
> > +#include <linux/serdev.h>
> > +
> > +#include "bno055.h"
> > +
> > +/*
> > + * Register writes cmd have the following format
> > + * +------+------+-----+-----+----- ... ----+
> > + * | 0xAA | 0xOO | REG | LEN | payload[LEN] |
> > + * +------+------+-----+-----+----- ... ----+
> > + *
> > + * Register write responses have the following format
> > + * +------+----------+
> > + * | 0xEE | ERROCODE |
> > + * +------+----------+
> > + *
> > + * Register read have the following format
> > + * +------+------+-----+-----+
> > + * | 0xAA | 0xO1 | REG | LEN |
> > + * +------+------+-----+-----+
> > + *
> > + * Successful register read response have the following format
> > + * +------+-----+----- ... ----+
> > + * | 0xBB | LEN | payload[LEN] |
> > + * +------+-----+----- ... ----+
> > + *
> > + * Failed register read response have the following format
> > + * +------+--------+
> > + * | 0xEE | ERRCODE|  (ERRCODE always > 1)
> > + * +------+--------+
> > + *
> > + * Error codes are
> > + * 01: OK
> > + * 02: read/write FAIL
> > + * 04: invalid address
> > + * 05: write on RO
> > + * 06: wrong start byte
> > + * 07: bus overrun
> > + * 08: len too high
> > + * 09: len too low
> > + * 10: bus RX byte timeout (timeout is 30mS)
> > + *
> > + *
> > + * **WORKAROUND ALERT**
> > + *
> > + * Serial communication seems very fragile: the BNO055 buffer seems to overflow
> > + * very easy; BNO055 seems able to sink few bytes, then it needs a brief pause.
> > + * On the other hand, it is also picky on timeout: if there is a pause > 30mS in
> > + * between two bytes then the transaction fails (IMU internal RX FSM resets).
> > + *
> > + * BMU055 has been seen also failing to process commands in case we send them
> > + * too close each other (or if it is somehow busy?)
> > + *
> > + * One idea would be to split data in chunks, and then wait 1-2mS between
> > + * chunks (we hope not to exceed 30mS delay for any reason - which should
> > + * be pretty a lot of time for us), and eventually retry in case the BNO055
> > + * gets upset for any reason. This seems to work in avoiding the overflow
> > + * errors, but indeed it seems slower than just perform a retry when an overflow
> > + * error occur.
> > + * In particular I saw these scenarios:
> > + * 1) If we send 2 bytes per time, then the IMU never(?) overflows.
> > + * 2) If we send 4 bytes per time (i.e. the full header), then the IMU could
> > + *    overflow, but it seem to sink all 4 bytes, then it returns error.
> > + * 3) If we send more than 4 bytes, the IMU could overflow, and I saw it sending
> > + *    error after 4 bytes are sent; we have troubles in synchronizing again,
> > + *    because we are still sending data, and the IMU interprets it as the 1st
> > + *    byte of a new command.
> > + *
> > + * So, we workaround all this in the following way:
> > + * In case of read we don't split the header but we rely on retries; This seems
> > + * convenient for data read (where we TX only the hdr).
> > + * For TX we split the transmission in 2-bytes chunks so that, we should not
> > + * only avoid case 2 (which is still manageable), but we also hopefully avoid
> > + * case 3, that would be by far worse.
> > + */
> > +
> > +/*
> > + * Read operation overhead:
> > + *  4 bytes req + 2byte resp hdr.
> > + *  6 bytes = 60 bit (considering 1start + 1stop bits).
> > + *  60/115200 = ~520uS.
> > + *
> > + * In 520uS we could read back about 34 bytes that means 3 samples, this means
> > + * that in case of scattered read in which the gap is 3 samples or less it is
> > + * still convenient to go for a burst.
> > + * We have to take into account also IMU response time - IMU seems to be often
> > + * reasonably quick to respond, but sometimes it seems to be in some "critical
> > + * section" in which it delays handling of serial protocol.
> > + * By experiment, it seems convenient to burst up to about 5/6-samples-long gap.
> > + */
> > +
> > +#define BNO055_SL_XFER_BURST_BREAK_THRESHOLD 6
> > +
> > +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;
> > +
> > +     /**
> > +      * 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
> > +     } cmd_status;
> > +     struct mutex lock;
> > +
> > +     /* Only accessed in behalf of RX callback context. No lock needed. */
>
> would "Only accessed in RX callback context. No lock needed." convey the same meaning?
>
> > +     struct {
> > +             enum {
> > +                     RX_IDLE,
> > +                     RX_START,
> > +                     RX_DATA
> > +             } state;
> > +             int databuf_count;
> > +             int expected_len;
> > +             int type;
> > +     } rx;
> > +
> > +     /* Never accessed in behalf of RX callback context. No lock needed */
> > +     bool cmd_stale;
> > +};
> > +
> > +static int bno055_sl_send_chunk(struct bno055_sl_priv *priv, u8 *data, int len)
> > +{
> > +     int ret;
> > +
> > +     dev_dbg(&priv->serdev->dev, "send (len: %d): %*ph", len, len, data);
> > +     ret = serdev_device_write(priv->serdev, data, len,
> > +                               msecs_to_jiffies(25));
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     if (ret < len)
> > +             return -EIO;
> > +
> > +     return 0;
> > +}
> > +
> > +/*
> > + * Sends a read or write command.
> > + * 'data' can be NULL (used in read case). 'len' parameter is always valid; in
> > + * case 'data' is non-NULL then it must match 'data' size.
> > + */
> > +static int bno055_sl_do_send_cmd(struct bno055_sl_priv *priv,
> > +                              int read, int addr, int len, u8 *data)
>
> Read is a bool, so give it that type.
>
> > +{
> > +     int ret;
> > +     int chunk_len;
> > +     u8 hdr[] = {0xAA, !!read, addr, len};
> > +
> > +     if (read) {
> > +             ret = bno055_sl_send_chunk(priv, hdr, 4);
> > +     } else {
> > +             ret = bno055_sl_send_chunk(priv, hdr, 2);
> > +             if (ret)
> > +                     goto fail;
> > +
> > +             usleep_range(2000, 3000);
> > +             ret = bno055_sl_send_chunk(priv, hdr + 2, 2);
> > +     }
> > +     if (ret)
> > +             goto fail;
> > +
> > +     if (data) {
>
> I would flip this condition to reduce indent and make it easy to
> see we are done in the no 'data' case.  Also, does this correspond in
> all cases to read?  If so I would use that as the variable to check.
>
>         if (!data)
>                 return 0;
>
>         while (len) {
> ...
>
>
> > +             while (len) {
> > +                     chunk_len = min(len, 2);
> > +                     usleep_range(2000, 3000);
> > +                     ret = bno055_sl_send_chunk(priv, data, chunk_len);
> > +                     if (ret)
> > +                             goto fail;
> > +                     data += chunk_len;
> > +                     len -= chunk_len;
> > +             }
> > +     }
> > +
> > +     return 0;
> > +fail:
> > +     /* waiting more than 30mS should clear the BNO055 internal state */
> > +     usleep_range(40000, 50000);
> > +     return ret;
> > +}
> > +
> > +static int bno_sl_send_cmd(struct bno055_sl_priv *priv,
> > +                        int read, int addr, int len, u8 *data)
>
> Read looks to be a bool to me not an integer.
>
> > +{
> > +     const int retry_max = 5;
> > +     int retry = retry_max;
> > +     int ret = 0;
> > +
> > +     /*
> > +      * In case previous command was interrupted we still neet to wait it to
> > +      * complete before we can issue new commands
> > +      */
> > +     if (priv->cmd_stale) {
> > +             ret = wait_for_completion_interruptible_timeout(&priv->cmd_complete,
> > +                                                             msecs_to_jiffies(100));
> > +             if (ret == -ERESTARTSYS)
> > +                     return -ERESTARTSYS;
> > +
> > +             priv->cmd_stale = false;
> > +             /* if serial protocol broke, bail out */
> > +             if (priv->cmd_status == STATUS_CRIT)
> > +                     goto exit;
>
>                         return -EIO;
>
> > +     }
> > +
> > +     /*
> > +      * Try to convince the IMU to cooperate.. as explained in the comments
> > +      * at the top of this file, the IMU could also refuse the command (i.e.
> > +      * it is not ready yet); retry in this case.
> > +      */
> > +     while (retry--) {
> > +             mutex_lock(&priv->lock);
> > +             priv->expect_response = read ? CMD_READ : CMD_WRITE;
> > +             reinit_completion(&priv->cmd_complete);
> > +             mutex_unlock(&priv->lock);
> > +
> > +             if (retry != (retry_max - 1))
> > +                     dev_dbg(&priv->serdev->dev, "cmd retry: %d",
> > +                             retry_max - retry);
> > +             ret = bno055_sl_do_send_cmd(priv, read, addr, len, data);
> > +             if (ret)
> > +                     continue;
> > +
> > +             ret = wait_for_completion_interruptible_timeout(&priv->cmd_complete,
> > +                                                             msecs_to_jiffies(100));
> > +             if (ret == -ERESTARTSYS) {
> > +                     priv->cmd_stale = true;
> > +                     return -ERESTARTSYS;
> > +             } else if (!ret) {
> > +                     ret = -ETIMEDOUT;
>
>                         return -ETIMEDOUT;
>
> > +                     break;
> > +             }
> > +             ret = 0;
> > +
> > +             /*
> > +              * Poll if the IMU returns error (i.e busy), break if the IMU
> > +              * returns OK or if the serial communication broke
> > +              */
> > +             if (priv->cmd_status <= 0)
> I 'think' this is only place we can break out with status set to anything (with
> the suggested modifications above) so move the if statements from the error path
> here and drop the ret = 0 above.

Hum. Looks like it always break here. Probably it's correct but the
loop can be reduced just up to the "continue" or something like that.
Have to rework this.

>
> > +                     break;
> > +     }
> > +
> > +exit:
> > +     if (ret)
> > +             return ret;
> > +     if (priv->cmd_status == STATUS_CRIT)
> > +             return -EIO;
> > +     if (priv->cmd_status == STATUS_FAIL)
> > +             return -EINVAL;
> > +     return 0;
> > +}
> > +
> > +static int bno055_sl_write_reg(void *context, const void *data, size_t count)
> > +{
> > +     int ret;
> > +     int reg;
> > +     u8 *write_data = (u8 *)data + 1;
>
> Given you dereference data as a u8 * in several places, perhaps a local
> variable to allow you to do it once.
>
> > +     struct bno055_sl_priv *priv = context;
> > +
> > +     if (count < 2) {
> > +             dev_err(&priv->serdev->dev, "Invalid write count %zu", count);
> > +             return -EINVAL;
> > +     }
> > +
> > +     reg = ((u8 *)data)[0];
> > +     dev_dbg(&priv->serdev->dev, "wr reg 0x%x = 0x%x", reg, ((u8 *)data)[1]);
> > +     ret = bno_sl_send_cmd(priv, 0, reg, count - 1, write_data);
> > +
> > +     return ret;
>
>         return bno_sl_send_cmd(...)
>
> > +}
> > +
> > +static int bno055_sl_read_reg(void *context,
> > +                           const void *reg, size_t reg_size,
> > +                           void *val, size_t val_size)
> > +{
> > +     int ret;
> > +     int reg_addr;
> > +     struct bno055_sl_priv *priv = context;
> > +
> > +     if (val_size > 128) {
> > +             dev_err(&priv->serdev->dev, "Invalid read valsize %d",
> > +                     val_size);
> > +             return -EINVAL;
> > +     }
> > +
> > +     reg_addr = ((u8 *)reg)[0];
> > +     dev_dbg(&priv->serdev->dev, "rd reg 0x%x (len %d)", reg_addr, val_size);
> > +     mutex_lock(&priv->lock);
> > +     priv->expected_data_len = val_size;
> > +     priv->response_buf = val;
> > +     mutex_unlock(&priv->lock);
> > +
> > +     ret = bno_sl_send_cmd(priv, 1, reg_addr, val_size, NULL);
> > +
> > +     mutex_lock(&priv->lock);
> > +     priv->response_buf = NULL;
> > +     mutex_unlock(&priv->lock);
> > +
> > +     return ret;
> > +}
> > +
> > +/*
> > + * Handler for received data; this is called from the reicever callback whenever
> > + * it got some packet from the serial bus. The status tell us whether the
> > + * packet is valid (i.e. header ok && received payload len consistent wrt the
> > + * header). It's now our responsability to check whether this is what we
> > + * expected, of whether we got some unexpected, yet valid, packet.
> > + */
> > +static void bno055_sl_handle_rx(struct bno055_sl_priv *priv, int status)
> > +{
> > +     mutex_lock(&priv->lock);
> > +     switch (priv->expect_response) {
> > +     case CMD_NONE:
> > +             dev_warn(&priv->serdev->dev, "received unexpected, yet valid, data from sensor");
> > +             mutex_unlock(&priv->lock);
> > +             return;
> > +
> > +     case CMD_READ:
> > +             priv->cmd_status = status;
> > +             if (status == STATUS_OK &&
> > +                 priv->rx.databuf_count != priv->expected_data_len) {
> > +                     /*
> > +                      * If we got here, then the lower layer serial protocol
> > +                      * seems consistent with itself; if we got an unexpected
> > +                      * amount of data then signal it as a non critical error
> > +                      */
> > +                     priv->cmd_status = STATUS_FAIL;
> > +                     dev_warn(&priv->serdev->dev, "received an unexpected amount of, yet valid, data from sensor");
> > +             }
> > +             break;
> > +
> > +     case CMD_WRITE:
> > +             priv->cmd_status = status;
> > +             break;
> > +     }
> > +
> > +     priv->expect_response = CMD_NONE;
> > +     complete(&priv->cmd_complete);
> > +     mutex_unlock(&priv->lock);
> > +}
> > +
> > +/*
> > + * Serdev receiver FSM. This tracks the serial communication and parse the
> > + * header. It pushes packets to bno055_sl_handle_rx(), eventually communicating
> > + * failures (i.e. malformed packets).
> > + * Ideally it doesn't know anything about upper layer (i.e. if this is the
> > + * packet we were really expecting), but since we copies the payload into the
> > + * receiver buffer (that is not valid when i.e. we don't expect data), we
> > + * snoop a bit in the upper layer..
> > + * Also, we assume to RX one pkt per time (i.e. the HW doesn't send anything
> > + * unless we require to AND we don't queue more than one request per time).
> > + */
> > +static int bno055_sl_receive_buf(struct serdev_device *serdev,
> > +                              const unsigned char *buf, size_t size)
> > +{
> > +     int status;
> > +     struct bno055_sl_priv *priv = serdev_device_get_drvdata(serdev);
> > +     int _size = size;
>
> Why the local variable?

size variable gets modified, so we cache the value to return in case of success.

> > +
> > +     if (size == 0)
> > +             return 0;
> > +
> > +     dev_dbg(&priv->serdev->dev, "recv (len %zu): %*ph ", size, size, buf);
> > +     switch (priv->rx.state) {
> > +     case RX_IDLE:
> > +             /*
> > +              * New packet.
> > +              * Check for its 1st byte, that identifies the pkt type.
> > +              */
> > +             if (buf[0] != 0xEE && buf[0] != 0xBB) {
> > +                     dev_err(&priv->serdev->dev,
> > +                             "Invalid packet start %x", buf[0]);
> > +                     bno055_sl_handle_rx(priv, STATUS_CRIT);
> > +                     break;
> > +             }
> > +             priv->rx.type = buf[0];
> > +             priv->rx.state = RX_START;
> > +             size--;
> > +             buf++;
> > +             priv->rx.databuf_count = 0;
> > +             fallthrough;
> > +
> > +     case RX_START:
> > +             /*
> > +              * Packet RX in progress, we expect either 1-byte len or 1-byte
> > +              * status depending by the packet type.
> > +              */
> > +             if (size == 0)
> > +                     break;
> > +
> > +             if (priv->rx.type == 0xEE) {
> > +                     if (size > 1) {
> > +                             dev_err(&priv->serdev->dev, "EE pkt. Extra data received");
> > +                             status = STATUS_CRIT;
> > +
> > +                     } else {
> > +                             status = (buf[0] == 1) ? STATUS_OK : STATUS_FAIL;
> > +                     }
> > +                     bno055_sl_handle_rx(priv, status);
> > +                     priv->rx.state = RX_IDLE;
> > +                     break;
> > +
> > +             } else {
> > +                     /*priv->rx.type == 0xBB */
> > +                     priv->rx.state = RX_DATA;
> > +                     priv->rx.expected_len = buf[0];
> > +                     size--;
> > +                     buf++;
> > +             }
> > +             fallthrough;
> > +
> > +     case RX_DATA:
> > +             /* Header parsed; now receiving packet data payload */
> > +             if (size == 0)
> > +                     break;
> > +
> > +             if (priv->rx.databuf_count + size > priv->rx.expected_len) {
> > +                     /*
> > +                      * This is a inconsistency in serial protocol, we lost
> > +                      * sync and we don't know how to handle further data
> > +                      */
> > +                     dev_err(&priv->serdev->dev, "BB pkt. Extra data received");
> > +                     bno055_sl_handle_rx(priv, STATUS_CRIT);
> > +                     priv->rx.state = RX_IDLE;
> > +                     break;
> > +             }
> > +
> > +             mutex_lock(&priv->lock);
> > +             /*
> > +              * NULL e.g. when read cmd is stale or when no read cmd is
> > +              * actually pending.
> > +              */
> > +             if (priv->response_buf &&
> > +                 /*
> > +                  * Snoop on the upper layer protocol stuff to make sure not
> > +                  * to write to an invalid memory. Apart for this, let's the
> > +                  * upper layer manage any inconsistency wrt expected data
> > +                  * len (as long as the serial protocol is consistent wrt
> > +                  * itself (i.e. response header is consistent with received
> > +                  * response len.
> > +                  */
> > +                 (priv->rx.databuf_count + size <= priv->expected_data_len))
> > +                     memcpy(priv->response_buf + priv->rx.databuf_count,
> > +                            buf, size);
> > +             mutex_unlock(&priv->lock);
> > +
> > +             priv->rx.databuf_count += size;
> > +
> > +             /*
> > +              * Reached expected len advertised by the IMU for the current
> > +              * packet. Pass it to the upper layer (for us it is just valid).
> > +              */
> > +             if (priv->rx.databuf_count == priv->rx.expected_len) {
> > +                     bno055_sl_handle_rx(priv, STATUS_OK);
> > +                     priv->rx.state = RX_IDLE;
> > +             }
> > +             break;
> > +     }
> > +
> > +     return _size;
> > +}
> > +
> > +static const struct serdev_device_ops bno055_sl_serdev_ops = {
> > +     .receive_buf = bno055_sl_receive_buf,
> > +     .write_wakeup = serdev_device_write_wakeup,
> > +};
> > +
> > +static struct regmap_bus bno055_sl_regmap_bus = {
> > +     .write = bno055_sl_write_reg,
> > +     .read = bno055_sl_read_reg,
> > +};
> > +
> > +static int bno055_sl_probe(struct serdev_device *serdev)
> > +{
> > +     struct bno055_sl_priv *priv;
> > +     struct regmap *regmap;
> > +     int ret;
> > +
> > +     priv = devm_kzalloc(&serdev->dev, sizeof(*priv), GFP_KERNEL);
> > +     if (!priv)
> > +             return -ENOMEM;
> > +
> > +     serdev_device_set_drvdata(serdev, priv);
> > +     priv->serdev = serdev;
> > +     mutex_init(&priv->lock);
> > +     init_completion(&priv->cmd_complete);
> > +
> > +     serdev_device_set_client_ops(serdev, &bno055_sl_serdev_ops);
> > +     ret = devm_serdev_device_open(&serdev->dev, serdev);
> > +     if (ret)
> > +             return ret;
> > +
> > +     if (serdev_device_set_baudrate(serdev, 115200) != 115200) {
> > +             dev_err(&serdev->dev, "Cannot set required baud rate");
> > +             return -EIO;
> > +     }
> > +
> > +     ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
> > +     if (ret) {
> > +             dev_err(&serdev->dev, "Cannot set required parity setting");
> > +             return ret;
> > +     }
> > +     serdev_device_set_flow_control(serdev, false);
> > +
> > +     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 bno055_probe(&serdev->dev, regmap,
> > +                         BNO055_SL_XFER_BURST_BREAK_THRESHOLD);
> > +}
> > +
> > +static const struct of_device_id bno055_sl_of_match[] = {
> > +     { .compatible = "bosch,bno055" },
> > +     { }
> > +};
> > +MODULE_DEVICE_TABLE(of, bno055_sl_of_match);
> > +
> > +static struct serdev_device_driver bno055_sl_driver = {
> > +     .driver = {
> > +             .name = "bno055-sl",
> > +             .of_match_table = bno055_sl_of_match,
> > +     },
> > +     .probe = bno055_sl_probe,
> > +};
> > +module_serdev_device_driver(bno055_sl_driver);
> > +
> > +MODULE_AUTHOR("Andrea Merello <andrea.merello@iit.it>");
> > +MODULE_DESCRIPTION("Bosch BNO055 serdev interface");
> > +MODULE_LICENSE("GPL v2");
>

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 10/10] iio: imu: add BNO055 I2C driver
  2021-11-09 11:56       ` Andrea Merello
@ 2021-11-09 15:47         ` Randy Dunlap
  2021-11-09 18:21           ` Joe Perches
  0 siblings, 1 reply; 55+ messages in thread
From: Randy Dunlap @ 2021-11-09 15:47 UTC (permalink / raw)
  To: andrea.merello, joe@perches.com
  Cc: Jonathan Cameron, Mauro Carvalho Chehab, linux-iio, linux-kernel,
	devicetree, Lars-Peter Clausen, Rob Herring, Andy Shevchenko,
	Matt Ranostay, Alexandru Ardelean, Jacopo Mondi, Andrea Merello

On 11/9/21 3:56 AM, Andrea Merello wrote:
> Il giorno ven 29 ott 2021 alle ore 00:04 Randy Dunlap
> <rdunlap@infradead.org> ha scritto:
>>
>> On 10/28/21 3:18 AM, Andrea Merello wrote:
>>> 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.
>>>
>>> Signed-off-by: Andrea Merello <andrea.merello@iit.it>
>>> ---
>>>    drivers/iio/imu/bno055/Kconfig      |  6 ++++
>>>    drivers/iio/imu/bno055/Makefile     |  1 +
>>>    drivers/iio/imu/bno055/bno055_i2c.c | 54 +++++++++++++++++++++++++++++
>>>    3 files changed, 61 insertions(+)
>>>    create mode 100644 drivers/iio/imu/bno055/bno055_i2c.c
>>>
>>> diff --git a/drivers/iio/imu/bno055/Kconfig b/drivers/iio/imu/bno055/Kconfig
>>> index 941e43f0368d..87200787d548 100644
>>> --- a/drivers/iio/imu/bno055/Kconfig
>>> +++ b/drivers/iio/imu/bno055/Kconfig
>>> @@ -7,3 +7,9 @@ config BOSH_BNO055_SERIAL
>>>        tristate "Bosh BNO055 attached via serial bus"
>>>        depends on SERIAL_DEV_BUS
>>>        select BOSH_BNO055_IIO
>>> +
>>> +config BOSH_BNO055_I2C
>>> +     tristate "Bosh BNO055 attached via I2C bus"
>>> +     depends on I2C
>>> +     select REGMAP_I2C
>>> +     select BOSH_BNO055_IIO
>>
>> Hi,
>>
>> The config entries that have user prompt strings should also
>> have help text.  scripts/checkpatch.pl should have told you
>> about that...
> 
> I'll add it, thanks. But FYI checkpatch doesn't complain about that here.

Hm, thanks for adding it and telling me about that.

checkpatch.pl does have some code for checking that but I confirmed
that it does not catch this simple case.

Joe, can you identify why checkpatch does not detect missing Kconfig
help text is this simple case?

Thanks.
-- 
~Randy

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 02/10] iio: document linear acceleration modifiers
  2021-11-09  8:00       ` Andrea Merello
@ 2021-11-09 17:00         ` Jonathan Cameron
  0 siblings, 0 replies; 55+ messages in thread
From: Jonathan Cameron @ 2021-11-09 17:00 UTC (permalink / raw)
  To: Andrea Merello
  Cc: Jonathan Cameron, Mauro Carvalho Chehab, linux-iio, linux-kernel,
	devicetree, Lars-Peter Clausen, Rob Herring, Andy Shevchenko,
	Matt Ranostay, Alexandru Ardelean, Jacopo Mondi, Andrea Merello

On Tue, 9 Nov 2021 09:00:09 +0100
Andrea Merello <andrea.merello@gmail.com> wrote:

> Il giorno gio 28 ott 2021 alle ore 12:35 Jonathan Cameron
> <jic23@kernel.org> ha scritto:
> >
> > On Thu, 28 Oct 2021 12:18:32 +0200
> > Andrea Merello <andrea.merello@gmail.com> wrote:
> >  
> > > This patch introduces ABI documentation for new iio modifiers used for
> > > reporting "linear acceleration" measures.
> > >
> > > Signed-off-by: Andrea Merello <andrea.merello@iit.it>
> > > ---
> > >  Documentation/ABI/testing/sysfs-bus-iio | 8 ++++++++
> > >  1 file changed, 8 insertions(+)
> > >
> > > diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> > > index 6ad47a67521c..5147a00bf24a 100644
> > > --- a/Documentation/ABI/testing/sysfs-bus-iio
> > > +++ b/Documentation/ABI/testing/sysfs-bus-iio
> > > @@ -1957,3 +1957,11 @@ Description:
> > >               Specify the percent for light sensor relative to the channel
> > >               absolute value that a data field should change before an event
> > >               is generated. Units are a percentage of the prior reading.
> > > +
> > > +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.15
> > > +Contact:     linux-iio@vger.kernel.org
> > > +Description:
> > > +             Raw (unscaled) linear acceleration readings.  
> >
> > Probably need more information that this.   What element is being 'removed' from
> > a normal acceleration measurement? What are units after application of offset and
> > scale?  Can cross refer to the in_accel_x_raw for that info if you like.  
> 
> OK.  So, may I just state something like "As per in_accel_X_raw
> attributes, but minus the gravity acceleration"  ?

Yup, something along those lines.

Wow, I had a lot of typos in my email. :)

Jonathan


> 
> > Also, but them immediately after the block with the in_accel_x_raw etc  
> 
> OK
> 
> > The organization fo that file needs a rethink but let us try to avoid making
> > it worse in the meeantime!
> >
> > Jonathan
> >
> >  


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 03/10] iio: document euler angles modifiers
  2021-11-09  8:15       ` Andrea Merello
@ 2021-11-09 17:03         ` Jonathan Cameron
  0 siblings, 0 replies; 55+ messages in thread
From: Jonathan Cameron @ 2021-11-09 17:03 UTC (permalink / raw)
  To: Andrea Merello
  Cc: Jonathan Cameron, Mauro Carvalho Chehab, linux-iio, linux-kernel,
	devicetree, Lars-Peter Clausen, Rob Herring, Andy Shevchenko,
	Matt Ranostay, Alexandru Ardelean, Jacopo Mondi, Andrea Merello

On Tue, 9 Nov 2021 09:15:09 +0100
Andrea Merello <andrea.merello@gmail.com> wrote:

> Il giorno gio 28 ott 2021 alle ore 12:37 Jonathan Cameron
> <jic23@kernel.org> ha scritto:
> >
> > On Thu, 28 Oct 2021 12:18:33 +0200
> > Andrea Merello <andrea.merello@gmail.com> wrote:
> >  
> > > This patch introduces ABI documentation for new modifiers used for
> > > reporting rotations expressed as euler angles (i.e. yaw, pitch, roll).
> > >
> > > Signed-off-by: Andrea Merello <andrea.merello@iit.it>
> > > ---
> > >  Documentation/ABI/testing/sysfs-bus-iio | 8 ++++++++
> > >  1 file changed, 8 insertions(+)
> > >
> > > diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> > > index 5147a00bf24a..f0adc2c817bd 100644
> > > --- a/Documentation/ABI/testing/sysfs-bus-iio
> > > +++ b/Documentation/ABI/testing/sysfs-bus-iio
> > > @@ -1965,3 +1965,11 @@ KernelVersion: 5.15
> > >  Contact:     linux-iio@vger.kernel.org
> > >  Description:
> > >               Raw (unscaled) linear acceleration readings.
> > > +
> > > +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.15
> > > +Contact:     linux-iio@vger.kernel.org
> > > +Description:
> > > +             Raw (unscaled) euler angles readings.  
> > Any _raw entry should also include what the units are after application of
> > offset and scale.   Or you could just add this as more info to the in_rot_raw
> > block as an extra sentence explaining that they are euler angles.
> > That will lose the 'KernelVersion' information but honestly I'm not sure we
> > care that much about that.  
> 
> I'm unsure which block you are talking about: I see there are two
> blocks that refer to rot things: in_rot_quaternion_raw and
> in_rot_from_north_xxx_raw.
> 
> Looking at the 1st one description, it looks very specific to
> quaternions to me; the 2nd seems very specific to its own thing,
> whatever it is.. So I would just add the missing information (unit) in
> the new block just being introduced, if this is ok for you. Or am I
> missing some other block in which I could  coalesce this new euler
> thing?

Good point, not sure what I was thinking.  There isn't a sensible block
to add this to.  So just add the info about units.

Jonathan

> 
> 
> > Jonathan
> >
> >  


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 04/10] iio: add modifiers for linear acceleration
  2021-11-09  9:58       ` Andrea Merello
@ 2021-11-09 17:05         ` Jonathan Cameron
  0 siblings, 0 replies; 55+ messages in thread
From: Jonathan Cameron @ 2021-11-09 17:05 UTC (permalink / raw)
  To: Andrea Merello, linux-kernel, devicetree
  Cc: Jonathan Cameron, Mauro Carvalho Chehab, linux-iio,
	Lars-Peter Clausen, Rob Herring, Andy Shevchenko, Matt Ranostay,
	Alexandru Ardelean, Jacopo Mondi, Andrea Merello

On Tue, 9 Nov 2021 10:58:19 +0100
Andrea Merello <andrea.merello@gmail.com> wrote:

> Il giorno gio 28 ott 2021 alle ore 12:41 Jonathan Cameron
> <jic23@kernel.org> ha scritto:
> >
> > On Thu, 28 Oct 2021 12:18:34 +0200
> > Andrea Merello <andrea.merello@gmail.com> wrote:
> >  
> > > 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_ACCEL_LINEAR_X, IIO_MOD_ACCEL_LINEAR_Y and
> > > IIO_MOD_ACCEL_LINEAR_Z modifiers to te IIO core.
> > >
> > > Signed-off-by: Andrea Merello <andrea.merello@iit.it>  
> >
> > They sometimes get forgotten but we should also update
> > tools/iio/iio_event_montitor.c to handle these new modifiers.  
> 
> I'm not so familiar with this tool, but it seems like it has to do
> with IIO events, which the bno055 driver doesn't use. On the other
> hand the modifiers I would add are not used by any other driver right
> now.
> 
> So I would say that it would end up in adding things that I couldn't
> test.. Or is there any test infrastructure for this? It seems trivial,
> just a matter of a few defines, so it shouldn't be an issue indeed..
> 
> > That can be a separate patch, but also fine to do it in this one.
> >  
> > > ---
> > >  drivers/iio/industrialio-core.c | 3 +++
> > >  include/uapi/linux/iio/types.h  | 4 +++-
> > >  2 files changed, 6 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> > > index 2dbb37e09b8c..a79cb32207e4 100644
> > > --- a/drivers/iio/industrialio-core.c
> > > +++ b/drivers/iio/industrialio-core.c
> > > @@ -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_ACCEL_LINEAR_X] = "linear_x",
> > > +     [IIO_MOD_ACCEL_LINEAR_Y] = "linear_y",
> > > +     [IIO_MOD_ACCEL_LINEAR_Z] = "linear_z"
> > >  };
> > >
> > >  /* relies on pairs of these shared then separate */
> > > diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
> > > index 48c13147c0a8..db00f7c45f48 100644
> > > --- a/include/uapi/linux/iio/types.h
> > > +++ b/include/uapi/linux/iio/types.h
> > > @@ -95,6 +95,9 @@ enum iio_modifier {
> > >       IIO_MOD_ETHANOL,
> > >       IIO_MOD_H2,
> > >       IIO_MOD_O2,
> > > +     IIO_MOD_ACCEL_LINEAR_X,
> > > +     IIO_MOD_ACCEL_LINEAR_Y,
> > > +     IIO_MOD_ACCEL_LINEAR_Z,  
> >
> > It might be useful for other channel types, so probably drop the ACCEL
> > part of the name.
> >
> > I'll admit I can't immediately think of what, but you never know.. :)  
> 
> But in this case what should I write in the ABI documentation? If I
> state that this is something that makes the gravity not being included
> then isn't it intrinsically tied to be an acceleration?  Or, I do
> that, and if someone eventually finds another use, then she/he will
> change the ABI doc?

The ABI docs are only documenting the complete ABI, not separately
the modifier so you will be documenting the same thing whatever
we call the modifier inside the code.

I'm just suggesting you call the enum entries the more generic
IIO_MOD_LINEAR_X, etc, not a change to the resulting string.

Jonathan

> 
> > >  };
> > >
> > >  enum iio_event_type {
> > > @@ -114,4 +117,3 @@ enum iio_event_direction {
> > >  };
> > >
> > >  #endif /* _UAPI_IIO_TYPES_H_ */
> > > -  
> > ?
> >
> >  


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 10/10] iio: imu: add BNO055 I2C driver
  2021-11-09 15:47         ` Randy Dunlap
@ 2021-11-09 18:21           ` Joe Perches
  2021-11-09 19:11             ` Randy Dunlap
  0 siblings, 1 reply; 55+ messages in thread
From: Joe Perches @ 2021-11-09 18:21 UTC (permalink / raw)
  To: Randy Dunlap, andrea.merello, Andi Kleen
  Cc: Jonathan Cameron, Mauro Carvalho Chehab, linux-iio, linux-kernel,
	devicetree, Lars-Peter Clausen, Rob Herring, Andy Shevchenko,
	Matt Ranostay, Alexandru Ardelean, Jacopo Mondi, Andrea Merello

(cc'ing Andi Kleen, who wrote this code a decade ago)

On Tue, 2021-11-09 at 07:47 -0800, Randy Dunlap wrote:
> On 11/9/21 3:56 AM, Andrea Merello wrote:
> > Il giorno ven 29 ott 2021 alle ore 00:04 Randy Dunlap <rdunlap@infradead.org> ha scritto:
> > > On 10/28/21 3:18 AM, Andrea Merello wrote:
> > > > 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.
> > > > 
> > > > Signed-off-by: Andrea Merello <andrea.merello@iit.it>
> > > > ---
> > > >    drivers/iio/imu/bno055/Kconfig      |  6 ++++
> > > >    drivers/iio/imu/bno055/Makefile     |  1 +
[]
> > > > diff --git a/drivers/iio/imu/bno055/Kconfig b/drivers/iio/imu/bno055/Kconfig
[]
> > > > @@ -7,3 +7,9 @@ config BOSH_BNO055_SERIAL
> > > >        tristate "Bosh BNO055 attached via serial bus"
> > > >        depends on SERIAL_DEV_BUS
> > > >        select BOSH_BNO055_IIO
> > > > +
> > > > +config BOSH_BNO055_I2C
> > > > +     tristate "Bosh BNO055 attached via I2C bus"
> > > > +     depends on I2C
> > > > +     select REGMAP_I2C
> > > > +     select BOSH_BNO055_IIO
[]
> > > The config entries that have user prompt strings should also
> > > have help text.  scripts/checkpatch.pl should have told you
> > > about that...
> > 
> > I'll add it, thanks. But FYI checkpatch doesn't complain about that here.
> 
> Hm, thanks for adding it and telling me about that.
> 
> checkpatch.pl does have some code for checking that but I confirmed
> that it does not catch this simple case.
> 
> Joe, can you identify why checkpatch does not detect missing Kconfig
> help text is this simple case?

Original patch here: https://lore.kernel.org/all/20211028101840.24632-11-andrea.merello@gmail.com/raw

checkpatch is counting the diff header lines that follow the config entry.
Maybe this is clearer (better?) code:
---
 scripts/checkpatch.pl | 28 +++++++++++++++-------------
 1 file changed, 15 insertions(+), 13 deletions(-)

diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 1784921c645da..b3ce8e04d7df7 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -3483,20 +3483,22 @@ sub process {
 			my $cnt = $realcnt;
 			my $ln = $linenr + 1;
 			my $f;
-			my $is_start = 0;
-			my $is_end = 0;
+			my $needs_help = 0;
+			my $has_help = 0;
 			for (; $cnt > 0 && defined $lines[$ln - 1]; $ln++) {
 				$f = $lines[$ln - 1];
-				$cnt-- if ($lines[$ln - 1] !~ /^-/);
-				$is_end = $lines[$ln - 1] =~ /^\+/;
+				$cnt-- if ($f !~ /^-/);
 
 				next if ($f =~ /^-/);
-				last if (!$file && $f =~ /^\@\@/);
+				last if (!$file && $f =~ /^(?:\@\@|diff )/);
 
-				if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) {
-					$is_start = 1;
-				} elsif ($lines[$ln - 1] =~ /^\+\s*(?:---)?help(?:---)?$/) {
-					$length = -1;
+				if ($f =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) {
+					$needs_help = 1;
+					next;
+				} elsif ($f =~ /^\+\s*help\s*$/) {
+					$length = 0;
+					$has_help = 1;
+					next;
 				}
 
 				$f =~ s/^.//;
@@ -3510,16 +3512,16 @@ sub process {
 				# common words in help texts
 				if ($f =~ /^\s*(?:config|menuconfig|choice|endchoice|
 						  if|endif|menu|endmenu|source)\b/x) {
-					$is_end = 1;
 					last;
 				}
-				$length++;
+				$length++ if ($has_help);
 			}
-			if ($is_start && $is_end && $length < $min_conf_desc_length) {
+			if ($needs_help &&
+			    (!$has_help ||
+			     ($has_help && $length < $min_conf_desc_length))) {
 				WARN("CONFIG_DESCRIPTION",
 				     "please write a paragraph that describes the config symbol fully\n" . $herecurr);
 			}
-			#print "is_start<$is_start> is_end<$is_end> length<$length>\n";
 		}
 
 # check MAINTAINERS entries



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [v2 10/10] iio: imu: add BNO055 I2C driver
  2021-11-09 18:21           ` Joe Perches
@ 2021-11-09 19:11             ` Randy Dunlap
  2021-11-09 20:46               ` Joe Perches
  0 siblings, 1 reply; 55+ messages in thread
From: Randy Dunlap @ 2021-11-09 19:11 UTC (permalink / raw)
  To: Joe Perches, andrea.merello, Andi Kleen
  Cc: Jonathan Cameron, Mauro Carvalho Chehab, linux-iio, linux-kernel,
	devicetree, Lars-Peter Clausen, Rob Herring, Andy Shevchenko,
	Matt Ranostay, Alexandru Ardelean, Jacopo Mondi, Andrea Merello

On 11/9/21 10:21 AM, Joe Perches wrote:
> (cc'ing Andi Kleen, who wrote this code a decade ago)
> 
> On Tue, 2021-11-09 at 07:47 -0800, Randy Dunlap wrote:
>> On 11/9/21 3:56 AM, Andrea Merello wrote:
>>> Il giorno ven 29 ott 2021 alle ore 00:04 Randy Dunlap <rdunlap@infradead.org> ha scritto:
>>>> On 10/28/21 3:18 AM, Andrea Merello wrote:
>>>>> 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.
>>>>>
>>>>> Signed-off-by: Andrea Merello <andrea.merello@iit.it>
>>>>> ---
>>>>>     drivers/iio/imu/bno055/Kconfig      |  6 ++++
>>>>>     drivers/iio/imu/bno055/Makefile     |  1 +
> []
>>>>> diff --git a/drivers/iio/imu/bno055/Kconfig b/drivers/iio/imu/bno055/Kconfig
> []
>>>>> @@ -7,3 +7,9 @@ config BOSH_BNO055_SERIAL
>>>>>         tristate "Bosh BNO055 attached via serial bus"
>>>>>         depends on SERIAL_DEV_BUS
>>>>>         select BOSH_BNO055_IIO
>>>>> +
>>>>> +config BOSH_BNO055_I2C
>>>>> +     tristate "Bosh BNO055 attached via I2C bus"
>>>>> +     depends on I2C
>>>>> +     select REGMAP_I2C
>>>>> +     select BOSH_BNO055_IIO
> []
>>>> The config entries that have user prompt strings should also
>>>> have help text.  scripts/checkpatch.pl should have told you
>>>> about that...
>>>
>>> I'll add it, thanks. But FYI checkpatch doesn't complain about that here.
>>
>> Hm, thanks for adding it and telling me about that.
>>
>> checkpatch.pl does have some code for checking that but I confirmed
>> that it does not catch this simple case.
>>
>> Joe, can you identify why checkpatch does not detect missing Kconfig
>> help text is this simple case?
> 
> Original patch here: https://lore.kernel.org/all/20211028101840.24632-11-andrea.merello@gmail.com/raw
> 
> checkpatch is counting the diff header lines that follow the config entry.
> Maybe this is clearer (better?) code:
> ---
>   scripts/checkpatch.pl | 28 +++++++++++++++-------------
>   1 file changed, 15 insertions(+), 13 deletions(-)
> 
> diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
> index 1784921c645da..b3ce8e04d7df7 100755
> --- a/scripts/checkpatch.pl
> +++ b/scripts/checkpatch.pl
> @@ -3483,20 +3483,22 @@ sub process {
>   			my $cnt = $realcnt;
>   			my $ln = $linenr + 1;
>   			my $f;
> -			my $is_start = 0;
> -			my $is_end = 0;
> +			my $needs_help = 0;
> +			my $has_help = 0;
>   			for (; $cnt > 0 && defined $lines[$ln - 1]; $ln++) {
>   				$f = $lines[$ln - 1];
> -				$cnt-- if ($lines[$ln - 1] !~ /^-/);
> -				$is_end = $lines[$ln - 1] =~ /^\+/;
> +				$cnt-- if ($f !~ /^-/);
>   
>   				next if ($f =~ /^-/);
> -				last if (!$file && $f =~ /^\@\@/);
> +				last if (!$file && $f =~ /^(?:\@\@|diff )/);
>   
> -				if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) {
> -					$is_start = 1;
> -				} elsif ($lines[$ln - 1] =~ /^\+\s*(?:---)?help(?:---)?$/) {
> -					$length = -1;
> +				if ($f =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) {
> +					$needs_help = 1;
> +					next;
> +				} elsif ($f =~ /^\+\s*help\s*$/) {
> +					$length = 0;
> +					$has_help = 1;
> +					next;
>   				}
>   
>   				$f =~ s/^.//;
> @@ -3510,16 +3512,16 @@ sub process {
>   				# common words in help texts
>   				if ($f =~ /^\s*(?:config|menuconfig|choice|endchoice|
>   						  if|endif|menu|endmenu|source)\b/x) {
> -					$is_end = 1;
>   					last;
>   				}
> -				$length++;
> +				$length++ if ($has_help);
>   			}
> -			if ($is_start && $is_end && $length < $min_conf_desc_length) {
> +			if ($needs_help &&
> +			    (!$has_help ||
> +			     ($has_help && $length < $min_conf_desc_length))) {
>   				WARN("CONFIG_DESCRIPTION",
>   				     "please write a paragraph that describes the config symbol fully\n" . $herecurr);
>   			}
> -			#print "is_start<$is_start> is_end<$is_end> length<$length>\n";
>   		}
>   
>   # check MAINTAINERS entries
> 
> 

Which now says:

WARNING: please write a paragraph that describes the config symbol fully
#16: FILE: drivers/iio/Kconfig:101:
+config BOSH_BNO055_I2C

(This is for a dummy entry that I made for testing, not from Andrea's
patch.)

Thanks, Joe.

Tested-by: Randy Dunlap <rdunlap@infradead.org>
Acked-by: Randy Dunlap <rdunlap@infradead.org>

-- 
~Randy

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 10/10] iio: imu: add BNO055 I2C driver
  2021-11-09 19:11             ` Randy Dunlap
@ 2021-11-09 20:46               ` Joe Perches
  2021-11-09 21:20                 ` Randy Dunlap
  0 siblings, 1 reply; 55+ messages in thread
From: Joe Perches @ 2021-11-09 20:46 UTC (permalink / raw)
  To: Randy Dunlap, andrea.merello, Andi Kleen
  Cc: Jonathan Cameron, Mauro Carvalho Chehab, linux-iio, linux-kernel,
	devicetree, Lars-Peter Clausen, Rob Herring, Andy Shevchenko,
	Matt Ranostay, Alexandru Ardelean, Jacopo Mondi, Andrea Merello

On Tue, 2021-11-09 at 11:11 -0800, Randy Dunlap wrote:
> On 11/9/21 10:21 AM, Joe Perches wrote:
> > (cc'ing Andi Kleen, who wrote this code a decade ago)
> > > Joe, can you identify why checkpatch does not detect missing Kconfig
> > > help text is this simple case?
> > 
> > Original patch here: https://lore.kernel.org/all/20211028101840.24632-11-andrea.merello@gmail.com/raw
> > 
> > checkpatch is counting the diff header lines that follow the config entry.
> > Maybe this is clearer (better?) code:
> > ---
> Tested-by: Randy Dunlap <rdunlap@infradead.org>
> Acked-by: Randy Dunlap <rdunlap@infradead.org>

Hey Randy/Andi.

I like this patch below a bit more.

It shows the Kconfig context block in the output message and
documents the code a bit more.

Care to test it again?
---
 scripts/checkpatch.pl | 53 +++++++++++++++++++++++++++------------------------
 1 file changed, 28 insertions(+), 25 deletions(-)

diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 1784921c645da..0b5c0363119ff 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -3480,46 +3480,49 @@ sub process {
 		    # (\b) rather than a whitespace character (\s)
 		    $line =~ /^\+\s*(?:config|menuconfig|choice)\b/) {
 			my $length = 0;
-			my $cnt = $realcnt;
-			my $ln = $linenr + 1;
-			my $f;
-			my $is_start = 0;
-			my $is_end = 0;
-			for (; $cnt > 0 && defined $lines[$ln - 1]; $ln++) {
-				$f = $lines[$ln - 1];
-				$cnt-- if ($lines[$ln - 1] !~ /^-/);
-				$is_end = $lines[$ln - 1] =~ /^\+/;
+			my $ln = $linenr;
+			my $needs_help = 0;
+			my $has_help = 0;
+			my $herecfg = $herecurr;
+			while (defined $lines[$ln]) {
+				my $f = $lines[$ln++];
 
 				next if ($f =~ /^-/);
-				last if (!$file && $f =~ /^\@\@/);
+				last if (!$file && $f =~ /^(?:\@\@|diff )/);
 
-				if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) {
-					$is_start = 1;
-				} elsif ($lines[$ln - 1] =~ /^\+\s*(?:---)?help(?:---)?$/) {
-					$length = -1;
+				$herecfg .= $rawlines[$ln - 1] . "\n";
+				if ($f =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) {
+					$needs_help = 1;
+					next;
+				}
+				if ($f =~ /^\+\s*help\s*$/) {
+					$has_help = 1;
+					next;
 				}
 
-				$f =~ s/^.//;
-				$f =~ s/#.*//;
-				$f =~ s/^\s+//;
-				next if ($f =~ /^$/);
+				$f =~ s/^.//;	# strip patch context [+ ]
+				$f =~ s/#.*//;	# strip # directives
+				$f =~ s/^\s+//;	# strip leading blanks
+				next if ($f =~ /^$/);	# skip blank lines
 
+				# At the end of this Kconfig block:
 				# This only checks context lines in the patch
 				# and so hopefully shouldn't trigger false
 				# positives, even though some of these are
 				# common words in help texts
-				if ($f =~ /^\s*(?:config|menuconfig|choice|endchoice|
-						  if|endif|menu|endmenu|source)\b/x) {
-					$is_end = 1;
+				if ($f =~ /^(?:config|menuconfig|choice|endchoice|
+					       if|endif|menu|endmenu|source)\b/x) {
+					$herecfg =~ s/.*\n\Z//;	# strip last line
 					last;
 				}
-				$length++;
+				$length++ if ($has_help);
 			}
-			if ($is_start && $is_end && $length < $min_conf_desc_length) {
+			if ($needs_help &&
+			    (!$has_help ||
+			     ($has_help && $length < $min_conf_desc_length))) {
 				WARN("CONFIG_DESCRIPTION",
-				     "please write a paragraph that describes the config symbol fully\n" . $herecurr);
+				     "please write a help paragraph that fully describes the config symbol\n" . $herecfg);
 			}
-			#print "is_start<$is_start> is_end<$is_end> length<$length>\n";
 		}
 
 # check MAINTAINERS entries



^ permalink raw reply related	[flat|nested] 55+ messages in thread

* Re: [v2 10/10] iio: imu: add BNO055 I2C driver
  2021-11-09 20:46               ` Joe Perches
@ 2021-11-09 21:20                 ` Randy Dunlap
  0 siblings, 0 replies; 55+ messages in thread
From: Randy Dunlap @ 2021-11-09 21:20 UTC (permalink / raw)
  To: Joe Perches, andrea.merello, Andi Kleen
  Cc: Jonathan Cameron, Mauro Carvalho Chehab, linux-iio, linux-kernel,
	devicetree, Lars-Peter Clausen, Rob Herring, Andy Shevchenko,
	Matt Ranostay, Alexandru Ardelean, Jacopo Mondi, Andrea Merello

On 11/9/21 12:46 PM, Joe Perches wrote:
> On Tue, 2021-11-09 at 11:11 -0800, Randy Dunlap wrote:
>> On 11/9/21 10:21 AM, Joe Perches wrote:
>>> (cc'ing Andi Kleen, who wrote this code a decade ago)
>>>> Joe, can you identify why checkpatch does not detect missing Kconfig
>>>> help text is this simple case?
>>>
>>> Original patch here: https://lore.kernel.org/all/20211028101840.24632-11-andrea.merello@gmail.com/raw
>>>
>>> checkpatch is counting the diff header lines that follow the config entry.
>>> Maybe this is clearer (better?) code:
>>> ---
>> Tested-by: Randy Dunlap <rdunlap@infradead.org>
>> Acked-by: Randy Dunlap <rdunlap@infradead.org>
> 
> Hey Randy/Andi.
> 
> I like this patch below a bit more.
> 
> It shows the Kconfig context block in the output message and
> documents the code a bit more.
> 
> Care to test it again?
> ---
>   scripts/checkpatch.pl | 53 +++++++++++++++++++++++++++------------------------
>   1 file changed, 28 insertions(+), 25 deletions(-)
> 

Same tags from me, better output. Thanks!

Tested-by: Randy Dunlap <rdunlap@infradead.org>
Acked-by: Randy Dunlap <rdunlap@infradead.org>

-- 
~Randy

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 10/10] iio: imu: add BNO055 I2C driver
  2021-10-28 11:10     ` Jonathan Cameron
@ 2021-11-11 10:12       ` Andrea Merello
  2021-11-14 16:38         ` Jonathan Cameron
  0 siblings, 1 reply; 55+ messages in thread
From: Andrea Merello @ 2021-11-11 10:12 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Mauro Carvalho Chehab, linux-iio, linux-kernel, devicetree,
	Lars-Peter Clausen, Rob Herring, Andy Shevchenko, Matt Ranostay,
	Alexandru Ardelean, Jacopo Mondi, Andrea Merello

Just an inline comment; OK for the rest.

Il giorno gio 28 ott 2021 alle ore 13:05 Jonathan Cameron
<jic23@kernel.org> ha scritto:
>
> On Thu, 28 Oct 2021 12:18:40 +0200
> Andrea Merello <andrea.merello@gmail.com> wrote:
>
> > 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.
> >
> > Signed-off-by: Andrea Merello <andrea.merello@iit.it>
> Hi Andrea,
>
> A few minor things inline.
>
> Jonathan
>
> > ---
> >  drivers/iio/imu/bno055/Kconfig      |  6 ++++
> >  drivers/iio/imu/bno055/Makefile     |  1 +
> >  drivers/iio/imu/bno055/bno055_i2c.c | 54 +++++++++++++++++++++++++++++
> >  3 files changed, 61 insertions(+)
> >  create mode 100644 drivers/iio/imu/bno055/bno055_i2c.c
> >
> > diff --git a/drivers/iio/imu/bno055/Kconfig b/drivers/iio/imu/bno055/Kconfig
> > index 941e43f0368d..87200787d548 100644
> > --- a/drivers/iio/imu/bno055/Kconfig
> > +++ b/drivers/iio/imu/bno055/Kconfig
> > @@ -7,3 +7,9 @@ config BOSH_BNO055_SERIAL
> >       tristate "Bosh BNO055 attached via serial bus"
> >       depends on SERIAL_DEV_BUS
> >       select BOSH_BNO055_IIO
> > +
> > +config BOSH_BNO055_I2C
> > +     tristate "Bosh BNO055 attached via I2C bus"
> > +     depends on I2C
> > +     select REGMAP_I2C
> > +     select BOSH_BNO055_IIO
> > diff --git a/drivers/iio/imu/bno055/Makefile b/drivers/iio/imu/bno055/Makefile
> > index 7285ade2f4b9..eaf24018cb28 100644
> > --- a/drivers/iio/imu/bno055/Makefile
> > +++ b/drivers/iio/imu/bno055/Makefile
> > @@ -2,3 +2,4 @@
> >
> >  obj-$(CONFIG_BOSH_BNO055_IIO) += bno055.o
> >  obj-$(CONFIG_BOSH_BNO055_SERIAL) += bno055_sl.o
> > +obj-$(CONFIG_BOSH_BNO055_I2C) += bno055_i2c.o
> > diff --git a/drivers/iio/imu/bno055/bno055_i2c.c b/drivers/iio/imu/bno055/bno055_i2c.c
> > new file mode 100644
> > index 000000000000..eea0daa6a61d
> > --- /dev/null
> > +++ b/drivers/iio/imu/bno055/bno055_i2c.c
> > @@ -0,0 +1,54 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * I2C interface for Bosh BNO055 IMU.
> > + * This file implements I2C communication up to the register read/write
> > + * level.
>
> Not really. It just uses regmap, so I'd drop this comment.
>
> > + *
> > + * Copyright (C) 2021 Istituto Italiano di Tecnologia
> > + * Electronic Design Laboratory
> > + * Written by Andrea Merello <andrea.merello@iit.it>
> > + */
> > +
> > +#include <linux/i2c.h>
>
> Why?  I'm not seeing an i2c specific calls in here.

Because of the definition of struct i2c_client, that is being accessed
in lines like this
dev_err(&client->dev, "Unable to init register map");

> > +#include <linux/regmap.h>
> > +#include <linux/module.h>
>
> mod_devicetable.h for struct i2c_device_id
>
> > +
> > +#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);
> > +     }
> > +
> > +     return bno055_probe(&client->dev, regmap,
> > +                         BNO055_I2C_XFER_BURST_BREAK_THRESHOLD);
> > +
> > +     return 0;
>
> ?
>
> > +}
> > +
> > +static const struct i2c_device_id bno055_i2c_id[] = {
> > +     {"bno055", 0},
> > +     { },
>
> It's at terminator, so don't put a comma as we'll never add entries after this.
>
> > +};
> > +MODULE_DEVICE_TABLE(i2c, bno055_i2c_id);
> > +
> > +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");
>

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 06/10] iio: document bno055 private sysfs attributes
  2021-11-09 10:22       ` Andrea Merello
@ 2021-11-14 16:20         ` Jonathan Cameron
  2022-01-04 11:42           ` Andrea Merello
  0 siblings, 1 reply; 55+ messages in thread
From: Jonathan Cameron @ 2021-11-14 16:20 UTC (permalink / raw)
  To: Andrea Merello
  Cc: Mauro Carvalho Chehab, linux-iio, linux-kernel, devicetree,
	Lars-Peter Clausen, Rob Herring, Andy Shevchenko, Matt Ranostay,
	Alexandru Ardelean, Jacopo Mondi, Andrea Merello

On Tue, 9 Nov 2021 11:22:27 +0100
Andrea Merello <andrea.merello@gmail.com> wrote:

> Few inline  comments; ok for the rest.
> 
> Il giorno gio 28 ott 2021 alle ore 12:59 Jonathan Cameron
> <jic23@kernel.org> ha scritto:
> >
> > On Thu, 28 Oct 2021 12:18:36 +0200
> > Andrea Merello <andrea.merello@gmail.com> wrote:
> >  
> > > This patch adds ABI documentation for bno055 driver private sysfs
> > > attributes.  
> >
> > Hohum. As normal I dislike custom attributes but reality is these
> > don't map to anything 'standard' and I don't want them getting adopted
> > in places where the 'standard' approach works.
> >
> > So thinking a bit more on this, I wonder if we can fit it into standard
> > ABI.
> >
> > We can't use the normal range specification method of
> > _scale because it's internal to the device and the output reading is
> > unaffected.  The range specification via _raw_available would let us know
> > the range, but it is not writeable so..
> >
> > A control that changes the internal scaling of the sensor in a fashion
> > that is not visible to the outside world maps to calibscale.  Whilst
> > that was intended for little tweaks to the input signal (often front
> > end amplifier gain tweak) it works here.  It doesn't map through to
> > anything userspace is expected to apply.  That combined with
> > _raw_available to let us know what the result is should work?
> >
> > What do you think of that approach?  It's obviously a little more complex
> > to handle in the driver, but it does map to existing ABI and avoids
> > custom attributes etc.  
> 
> If I read the ABI documentation, then I would say that calibscale has
> nothing to do with this, but I think you have obviously a better
> feeling than me about what calibscale is really for. To be honest I've
> probably not a clear idea about what calibscale is indeed...

Original intent was that it was a tweak for input amplifiers on some sensor
types that you'd set as part of a calibration process. These days, for
many sensors that have this it's handled at factory anyway and these
tweak values are rarely exposed to software.

> 
> In general, I would say that is better to stick to standard attributes
> when possible, and of course to avoid having the same thing mapped on
> random custom attributes in each driver, but IMO only up to the extent
> which doesn't  force something that is really something different to
> map on a standard thing just because of the sake of having as much
> standard things as possible... But all this is probably quite obvious,
> and it all depends on the above (i.e. is it calibscale fitting well in
> your opinion?) .. Up to you on this one..
> 
> BTW I'm missing why this should complicate the driver.. I guess I'll
> find out if I'll implement it :)
Inverse of the range values which is always a mess without floating point.

Ok, I'm persuaded that we have to go with range here even if it is a bit
painful and might cause confusion if we start getting it in lots of drivers.
*fingers crossed we don't*

There is still a units question though.  Should we express the ranges
in _processed or _raw units?  Or do we make it explicit and call it
rangeprocessed for example?  For some devices the range will naturally
be expressed as the range of ADC raw values, so there is definite room
for confusion if we don't make it clear in the name.

I'm open to other suggestions of how we name this to avoid falling into
any heffalump traps.

> 
> > >
> > > Signed-off-by: Andrea Merello <andrea.merello@iit.it>
> > > ---
> > >  .../ABI/testing/sysfs-bus-iio-bno055          | 84 +++++++++++++++++++
> > >  1 file changed, 84 insertions(+)
> > >  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-bno055
> > >
> > > diff --git a/Documentation/ABI/testing/sysfs-bus-iio-bno055 b/Documentation/ABI/testing/sysfs-bus-iio-bno055
> > > new file mode 100644
> > > index 000000000000..930a70c5a858
> > > --- /dev/null
> > > +++ b/Documentation/ABI/testing/sysfs-bus-iio-bno055
> > > @@ -0,0 +1,84 @@
> > > +What:                /sys/bus/iio/devices/iio:deviceX/in_accel_range
> > > +KernelVersion:       5.15
> > > +Contact:     linux-iio@vger.kernel.org
> > > +Description:
> > > +             Range for acceleration readings in G. 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).  
> >
> > Having this in G but the sensor output in m/s^2 seems inconsistent.
> >  
> > > +
> > > +What:                /sys/bus/iio/devices/iio:deviceX/in_anglvel_range
> > > +KernelVersion:       5.15
> > > +Contact:     linux-iio@vger.kernel.org
> > > +Description:
> > > +             Range for angular velocity readings in dps. 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).  
> >
> > Again, units need to match or this is going to be really confusing.
> >  
> > > +
> > > +What:                /sys/bus/iio/devices/iio:deviceX/in_accel_range_available
> > > +KernelVersion:       5.15
> > > +Contact:     linux-iio@vger.kernel.org
> > > +Description:
> > > +             List of allowed values for in_accel_range attribute
> > > +
> > > +What:                /sys/bus/iio/devices/iio:deviceX/in_anglvel_range_available
> > > +KernelVersion:       5.15
> > > +Contact:     linux-iio@vger.kernel.org
> > > +Description:
> > > +             List of allowed values for in_anglvel_range attribute
> > > +
> > > +What:                /sys/bus/iio/devices/iio:deviceX/fast_magnetometer_calibration_enable
> > > +KernelVersion:       5.15
> > > +Contact:     linux-iio@vger.kernel.org
> > > +Description:
> > > +             Can be 1 or 0. Enables/disables the "Fast Magnetometer
> > > +             Calibration" HW function.  
> >
> > Naming needs to be consistent with the ABI.  This is a channel type specific function
> > and to match existing calibration related ABI naming it would be.
> >
> > in_magn_calibration_fast_enable
> >
> > Some of the others need renaming in a similar way.
> >  
> > > +
> > > +What:                /sys/bus/iio/devices/iio:deviceX/fusion_enable
> > > +KernelVersion:       5.15
> > > +Contact:     linux-iio@vger.kernel.org
> > > +Description:
> > > +             Can be 1 or 0. Enables/disables the "sensor fusion" (a.k.a.
> > > +             NDOF) HW function.
> > > +
> > > +What:                /sys/bus/iio/devices/iio:deviceX/in_calibration_data
> > > +KernelVersion:       5.15
> > > +Contact:     linux-iio@vger.kernel.org
> > > +Description:
> > > +             Reports the binary calibration data blob for the IMU sensors.  
> >
> > Why in_ ?  What channels does this apply to?
> >  
> > > +
> > > +What:                /sys/bus/iio/devices/iio:deviceX/in_autocalibration_status_accel
> > > +KernelVersion:       5.15
> > > +Contact:     linux-iio@vger.kernel.org
> > > +Description:
> > > +             Can be "Idle", "Bad", "Barely enough", "Fair", or "Good".
> > > +             Report the autocalibration status for the accelerometer sensor.  
> >
> > For interfaces that really don't have any chance of generalising this one is terrible.
> > Any hope at all of mapping this to something numeric?
> >
> > in_accel_calibration_auto_status
> >
> >  
> > > +
> > > +What:                /sys/bus/iio/devices/iio:deviceX/in_autocalibration_status_gyro
> > > +KernelVersion:       5.15
> > > +Contact:     linux-iio@vger.kernel.org
> > > +Description:
> > > +             Can be "Idle", "Bad", "Barely enough", "Fair", or "Good".
> > > +             Reports the autocalibration status for the gyroscope sensor.  
> >
> > in_angvel_calibration_auto_status
> > etc.
> >  
> > > +
> > > +What:                /sys/bus/iio/devices/iio:deviceX/in_autocalibration_status_magn
> > > +KernelVersion:       5.15
> > > +Contact:     linux-iio@vger.kernel.org
> > > +Description:
> > > +             Can be "Idle", "Bad", "Barely enough", "Fair", or "Good".
> > > +             Reports the autocalibration status for the magnetometer sensor.
> > > +
> > > +What:                /sys/bus/iio/devices/iio:deviceX/in_autocalibration_status_sys
> > > +KernelVersion:       5.15
> > > +Contact:     linux-iio@vger.kernel.org
> > > +Description:
> > > +             Can be "Idle", "Bad", "Barely enough", "Fair", or "Good".
> > > +             Reports the status for the IMU overall autocalibration.
> > > +
> > > +What:                /sys/bus/iio/devices/iio:deviceX/unique_id  
> >
> > Hmm. So normally we just dump these in the kernel log.  I guess you need it
> > here to associate a calibration blob with a particular sensor?  
> 
> Well, it was originally in kernel log, but putting in an attribute was
> one of the changes that has been requested for V2.

Oops. :)  Inconsistency is my middle name...

> It is needed by the user who copies the calibration data to the
> calibration file, in order for her/him to be able to properly name it
> (in case of more than 1 sensor on the same setup).

Fair enough that makes complete sense.

> 
> > We could put it in label, but that would stop us using that for things like
> > positioning of the sensor.  So perhaps this is something that we should add
> > to the main ABI doc.  Probably as serial_number rather than unique ID though.  
> 
> OK, for renaming to "serial_number". I'm not sure they are
> conceptually the same thing, but I think it works anyway.
> Of course I can move its doc to the main file. Do you want a separate
> patch for this?

Separate patch would be great.  Thanks,

Jonathan

> 
> > > +KernelVersion:       5.15
> > > +Contact:     linux-iio@vger.kernel.org
> > > +Description:
> > > +             16-bytes, 2-digits-per-byte, HEX-string representing the sensor
> > > +             unique ID number.  
> >  


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 07/10] iio: imu: add Bosch Sensortec BNO055 core driver
  2021-11-09 11:52       ` Andrea Merello
@ 2021-11-14 16:33         ` Jonathan Cameron
  0 siblings, 0 replies; 55+ messages in thread
From: Jonathan Cameron @ 2021-11-14 16:33 UTC (permalink / raw)
  To: Andrea Merello
  Cc: Mauro Carvalho Chehab, linux-iio, linux-kernel, devicetree,
	Lars-Peter Clausen, Rob Herring, Andy Shevchenko, Matt Ranostay,
	Alexandru Ardelean, Jacopo Mondi, Andrea Merello

On Tue, 9 Nov 2021 12:52:14 +0100
Andrea Merello <andrea.merello@gmail.com> wrote:

> Some inline notes. OK for all the rest.
> 
> Il giorno gio 28 ott 2021 alle ore 15:27 Jonathan Cameron
> <jic23@kernel.org> ha scritto:
> >
> > On Thu, 28 Oct 2021 12:18:37 +0200
> > Andrea Merello <andrea.merello@gmail.com> wrote:
> >  
> > > 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.
> > >
> > > Signed-off-by: Andrea Merello <andrea.merello@iit.it>
Side not. Please crop out bits of the discussion we aren't continuing.  Makes it easier
to find the relevant parts of the email!  Note this is a do as I say rather than do
as I do as I don't always remember to do this either.

...

> >  
> > > +/*
> > > + * 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.  
> >
> > This is rather complex - I take we can't just fall back to letting the IIO core
> > demux do all the hard work for us?  
> 
> Hum. I'm not sure.. I admit that I'm not familiar with the demux
> thing, but as far as I can see it needs to be initialized once with a
> list containing all allowed scan masks; IIO core will pick one of them
> and eventually cull extra samples it contains. Is this right?

yup - that's pretty much it.

> 
> I would say we may precalculate this list at probe time (depending on
> the burst break threshold) and populate it with all the possible scan
> masks in which there are no gaps < than the bust break threshold. But
> this could be a quite high number of combinations..
> 
> This way the IIO layer will only request xfers in which gaps are
> always > than burst break threshold, which the driver in turn will
> always split in several xfers.
> 
> Does this make sense to you?

If it works and ends up simpler than this I'm all for it, but I'll confess
you've lost me in the explanation.  

Whether the approach you are using here ends up more efficient than the
one in the demux (which IIRC works by doing an expensive copy map building
just once and can then use that to do things like merging copies of neighbouring
elements) will be dependent on exact combinations of enabled channels.

There is also a usecase question for how much effort it is worth putting in
to optimise these paths.  In a lot of cases people have put an IMU in because
their application needs one so will be grabbing almost all channels all the time.

It is unlikely they want a random set of scattered channels.


> 
> > > + */
> > > +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;
> > > +
> > > +     /*
> > > +      * 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;
> > > +     bool xfer_pending = false;
> > > +     bool first = true;
> > > +     unsigned long mask;
> > > +     int buf_idx = 0;
> > > +     bool thr_hit;
> > > +     int quat;
> > > +     int ret;
> > > +
> > > +     mutex_lock(&priv->lock);
> > > +     for_each_set_bitrange(start, end, iio_dev->active_scan_mask,
> > > +                           iio_dev->masklength) {  
> >
> > I'm not seeing this function in mainline...  I guess this series has a dependency
> > I missed?  
> 
> I've been pointed to Yuri Norov bitmap series (I mentioned this in the
> cover letter). Assuming it is close to be merged, I've updated my drv
> for its API changes, but if you prefer I can revert to the current
> mainline API. It's a trivial change.

Ah. Thanks for pointing that out.  I missed the note in the cover letter.

Jonathan


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 09/10] iio: imu: add BNO055 serdev driver
  2021-11-09 15:33       ` Andrea Merello
@ 2021-11-14 16:37         ` Jonathan Cameron
  0 siblings, 0 replies; 55+ messages in thread
From: Jonathan Cameron @ 2021-11-14 16:37 UTC (permalink / raw)
  To: Andrea Merello
  Cc: Mauro Carvalho Chehab, linux-iio, linux-kernel, devicetree,
	Lars-Peter Clausen, Rob Herring, Andy Shevchenko, Matt Ranostay,
	Alexandru Ardelean, Jacopo Mondi, Andrea Merello

On Tue, 9 Nov 2021 16:33:44 +0100
Andrea Merello <andrea.merello@gmail.com> wrote:

...

> > > +static int bno055_sl_receive_buf(struct serdev_device *serdev,
> > > +                              const unsigned char *buf, size_t size)
> > > +{
> > > +     int status;
> > > +     struct bno055_sl_priv *priv = serdev_device_get_drvdata(serdev);
> > > +     int _size = size;  
> >
> > Why the local variable?  
> 
> size variable gets modified, so we cache the value to return in case of success.

Ah - missed that as unusual way around.  I'd modify the local variable instead
and perhaps call it something like remaining to reflect how it is being used?

> 
> > > +
> > > +     if (size == 0)
> > > +             return 0;
> > > +
> > > +     dev_dbg(&priv->serdev->dev, "recv (len %zu): %*ph ", size, size, buf);
> > > +     switch (priv->rx.state) {
> > > +     case RX_IDLE:
> > > +             /*
> > > +              * New packet.
> > > +              * Check for its 1st byte, that identifies the pkt type.
> > > +              */
> > > +             if (buf[0] != 0xEE && buf[0] != 0xBB) {
> > > +                     dev_err(&priv->serdev->dev,
> > > +                             "Invalid packet start %x", buf[0]);
> > > +                     bno055_sl_handle_rx(priv, STATUS_CRIT);
> > > +                     break;
> > > +             }
> > > +             priv->rx.type = buf[0];
> > > +             priv->rx.state = RX_START;
> > > +             size--;
> > > +             buf++;
> > > +             priv->rx.databuf_count = 0;
> > > +             fallthrough;
> > > +
> > > +     case RX_START:
> > > +             /*
> > > +              * Packet RX in progress, we expect either 1-byte len or 1-byte
> > > +              * status depending by the packet type.
> > > +              */
> > > +             if (size == 0)
> > > +                     break;
> > > +
> > > +             if (priv->rx.type == 0xEE) {
> > > +                     if (size > 1) {
> > > +                             dev_err(&priv->serdev->dev, "EE pkt. Extra data received");
> > > +                             status = STATUS_CRIT;
> > > +
> > > +                     } else {
> > > +                             status = (buf[0] == 1) ? STATUS_OK : STATUS_FAIL;
> > > +                     }
> > > +                     bno055_sl_handle_rx(priv, status);
> > > +                     priv->rx.state = RX_IDLE;
> > > +                     break;
> > > +
> > > +             } else {
> > > +                     /*priv->rx.type == 0xBB */
> > > +                     priv->rx.state = RX_DATA;
> > > +                     priv->rx.expected_len = buf[0];
> > > +                     size--;
> > > +                     buf++;
> > > +             }
> > > +             fallthrough;
> > > +
> > > +     case RX_DATA:
> > > +             /* Header parsed; now receiving packet data payload */
> > > +             if (size == 0)
> > > +                     break;
> > > +
> > > +             if (priv->rx.databuf_count + size > priv->rx.expected_len) {
> > > +                     /*
> > > +                      * This is a inconsistency in serial protocol, we lost
> > > +                      * sync and we don't know how to handle further data
> > > +                      */
> > > +                     dev_err(&priv->serdev->dev, "BB pkt. Extra data received");
> > > +                     bno055_sl_handle_rx(priv, STATUS_CRIT);
> > > +                     priv->rx.state = RX_IDLE;
> > > +                     break;
> > > +             }
> > > +
> > > +             mutex_lock(&priv->lock);
> > > +             /*
> > > +              * NULL e.g. when read cmd is stale or when no read cmd is
> > > +              * actually pending.
> > > +              */
> > > +             if (priv->response_buf &&
> > > +                 /*
> > > +                  * Snoop on the upper layer protocol stuff to make sure not
> > > +                  * to write to an invalid memory. Apart for this, let's the
> > > +                  * upper layer manage any inconsistency wrt expected data
> > > +                  * len (as long as the serial protocol is consistent wrt
> > > +                  * itself (i.e. response header is consistent with received
> > > +                  * response len.
> > > +                  */
> > > +                 (priv->rx.databuf_count + size <= priv->expected_data_len))
> > > +                     memcpy(priv->response_buf + priv->rx.databuf_count,
> > > +                            buf, size);
> > > +             mutex_unlock(&priv->lock);
> > > +
> > > +             priv->rx.databuf_count += size;
> > > +
> > > +             /*
> > > +              * Reached expected len advertised by the IMU for the current
> > > +              * packet. Pass it to the upper layer (for us it is just valid).
> > > +              */
> > > +             if (priv->rx.databuf_count == priv->rx.expected_len) {
> > > +                     bno055_sl_handle_rx(priv, STATUS_OK);
> > > +                     priv->rx.state = RX_IDLE;
> > > +             }
> > > +             break;
> > > +     }
> > > +
> > > +     return _size;
> > > +}



^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 10/10] iio: imu: add BNO055 I2C driver
  2021-11-11 10:12       ` Andrea Merello
@ 2021-11-14 16:38         ` Jonathan Cameron
  0 siblings, 0 replies; 55+ messages in thread
From: Jonathan Cameron @ 2021-11-14 16:38 UTC (permalink / raw)
  To: Andrea Merello
  Cc: Mauro Carvalho Chehab, linux-iio, linux-kernel, devicetree,
	Lars-Peter Clausen, Rob Herring, Andy Shevchenko, Matt Ranostay,
	Alexandru Ardelean, Jacopo Mondi, Andrea Merello

On Thu, 11 Nov 2021 11:12:58 +0100
Andrea Merello <andrea.merello@gmail.com> wrote:

> Just an inline comment; OK for the rest.

> > > +#include <linux/i2c.h>  
> >
> > Why?  I'm not seeing an i2c specific calls in here.  
> 
> Because of the definition of struct i2c_client, that is being accessed
> in lines like this
> dev_err(&client->dev, "Unable to init register map");

doh. I was clearly being a bit unobservant that day.


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 06/10] iio: document bno055 private sysfs attributes
  2021-11-14 16:20         ` Jonathan Cameron
@ 2022-01-04 11:42           ` Andrea Merello
  2022-01-15 15:27             ` Jonathan Cameron
  0 siblings, 1 reply; 55+ messages in thread
From: Andrea Merello @ 2022-01-04 11:42 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Mauro Carvalho Chehab, linux-iio, linux-kernel, devicetree,
	Lars-Peter Clausen, Rob Herring, Andy Shevchenko, Matt Ranostay,
	Alexandru Ardelean, Jacopo Mondi, Andrea Merello

Sorry for the huge delay...

> There is still a units question though.  Should we express the ranges
> in _processed or _raw units?  Or do we make it explicit and call it
> rangeprocessed for example?  For some devices the range will naturally
> be expressed as the range of ADC raw values, so there is definite room
> for confusion if we don't make it clear in the name.
>
> I'm open to other suggestions of how we name this to avoid falling into
> any heffalump traps.

You are right: this might lead to confusion.. Making it explicit in
the name seems a good idea.

I've looked at other iio sysfs attributes in the DOC.  It seems  that
"thesh" and "roc" attributes allows for both preprocessed and raw
data: I found e.g. "<type>[Y][_name]_<raw|input>_thresh_value", but
the related "what" entries written above all seem to omit both "_raw"
and "_input"; I don't understand why.

In any case, maybe we can stick to that already-existent naming schema?

Assuming the pattern is correct, then wouldn't it be
"in_accel_raw_range"  (or "in_accel_x_raw_range", in case it could
have different values for each axis) or "in_accel_input_range" in case
range applies to preprocessed vals, etc ?


Andrea

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 06/10] iio: document bno055 private sysfs attributes
  2022-01-04 11:42           ` Andrea Merello
@ 2022-01-15 15:27             ` Jonathan Cameron
  2022-01-17  9:37               ` Andrea Merello
  0 siblings, 1 reply; 55+ messages in thread
From: Jonathan Cameron @ 2022-01-15 15:27 UTC (permalink / raw)
  To: Andrea Merello
  Cc: Mauro Carvalho Chehab, linux-iio, linux-kernel, devicetree,
	Lars-Peter Clausen, Rob Herring, Andy Shevchenko, Matt Ranostay,
	Alexandru Ardelean, Jacopo Mondi, Andrea Merello

On Tue, 4 Jan 2022 12:42:40 +0100
Andrea Merello <andrea.merello@gmail.com> wrote:

> Sorry for the huge delay...

No problem though I may have forgotten some of the discussion!

> 
> > There is still a units question though.  Should we express the ranges
> > in _processed or _raw units?  Or do we make it explicit and call it
> > rangeprocessed for example?  For some devices the range will naturally
> > be expressed as the range of ADC raw values, so there is definite room
> > for confusion if we don't make it clear in the name.
> >
> > I'm open to other suggestions of how we name this to avoid falling into
> > any heffalump traps.  
> 
> You are right: this might lead to confusion.. Making it explicit in
> the name seems a good idea.
> 
> I've looked at other iio sysfs attributes in the DOC.  It seems  that
> "thesh" and "roc" attributes allows for both preprocessed and raw
> data: I found e.g. "<type>[Y][_name]_<raw|input>_thresh_value", but
> the related "what" entries written above all seem to omit both "_raw"
> and "_input"; I don't understand why.

Excellent point.  That documentation is garbage.  Events are meant
to pick it up implicitly from the related channel _raw or _input.
I don't remember them ever having raw or input in their naming but
it's possible they did right at the beginning before the ABI was anywhere
near stable.  Gah. I dread to think how long that that has been wrong.

> 
> In any case, maybe we can stick to that already-existent naming schema?

It doesn't exist really the docs are wrong.  

> 
> Assuming the pattern is correct, then wouldn't it be
> "in_accel_raw_range"  (or "in_accel_x_raw_range", in case it could
> have different values for each axis) or "in_accel_input_range" in case
> range applies to preprocessed vals, etc ?

Tricky corner but I'd go with no, because the pattern is

direction_type_infotype

and in this case the infotype is rangeraw. We've not been totally consistent
on whether we allow spaces in infotype or not.  Intially we always did but then
some of the userspace folks asked us to stop doing so because it requires
all userspace software to have an explicit list rather than just adding
controls to some GUI based on generic parsing.  Hohum. Historical decisions that
lead to messy interfaces... *sigh*

Nearest to what you have here though are peak_raw and mean_raw
though those are odd in of themselves in that they are basically special forms
of _raw rather than something else that is in _raw units...

So I think range_raw postfix is the best bet.

Jonathan





>

> 
> Andrea


^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 06/10] iio: document bno055 private sysfs attributes
  2022-01-15 15:27             ` Jonathan Cameron
@ 2022-01-17  9:37               ` Andrea Merello
  2022-01-22 18:08                 ` Jonathan Cameron
  0 siblings, 1 reply; 55+ messages in thread
From: Andrea Merello @ 2022-01-17  9:37 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Mauro Carvalho Chehab, linux-iio, linux-kernel, devicetree,
	Lars-Peter Clausen, Rob Herring, Andy Shevchenko, Matt Ranostay,
	Alexandru Ardelean, Jacopo Mondi, Andrea Merello

Trivial inline comments below. Beside that, I've found another
pleasing issue with this "range" thing on this device..

One one hand, things seem to always work as we discussed for the
accelerometer (i.e. range doesn't affect the scale; the HW always
provides readings in the same scale, but with different range and
precision) on the other hand, the gyroscope behavior depends by the
internal IMU firmware version.. great..

Stock firmware has a bug[0], so that the "range" gyroscope registers
do change the scale indeed. AFAICT stock firmware is the one you find
in most (all?) breakout boards, which are usually available (and which
I'm using right now for this driver mainlining attempt). Upgrading
firmware looks like a rather obscure process that AFAICT can be done
only in some specific USB-stick demo-board ("shuttle board") or with
maybe with FAE assistance on custom developed boards [1] (i.e. maybe
can be done by some professional user; I would say not for most
people).

So, I'm now wondering how to handle this... I really want to support
the stock FW, which seems the most widespread, and the one I have
right now; I'd say this means: the accelerometer thing will still work
as we discussed (i.e. the range attribute thing), while the gyro will
have writeable scale, and a (ro) scale_available attrib. But what
about the gyro range thing? Should I drop it, or keep it as
informative read-only?

Then I could also support the new firmware (which I cannot test right
now with my actual breakout board, but I might see whether I could get
a board with an updated IMU), keeping also the current driver behavior
(i.e. range stuff).

But the question is: in either cases (new vs old fw) should the
non-necessary attributes disappear or they may just be RO or locked
(i.e. scale_available for new FW and range stuff for the old one)?

Any thoughts and advice on this whole thing would be very welcome :)
my current inclination anyway now tends to be: go on supporting only
the stock FW (i.e. the board I have here now) and eventually add
support for the new fw later on, after merge.

[0] https://community.bosch-sensortec.com/t5/MEMS-sensors-forum/BNO055-Wrong-sensitivity-resolution-in-datasheet/td-p/10266
[1] https://community.bosch-sensortec.com/t5/MEMS-sensors-forum/BNO055-Software-Version/td-p/14001

> > I've looked at other iio sysfs attributes in the DOC.  It seems  that
> > "thesh" and "roc" attributes allows for both preprocessed and raw
> > data: I found e.g. "<type>[Y][_name]_<raw|input>_thresh_value", but
> > the related "what" entries written above all seem to omit both "_raw"
> > and "_input"; I don't understand why.
>
> Excellent point.  That documentation is garbage.  Events are meant
> to pick it up implicitly from the related channel _raw or _input.
> I don't remember them ever having raw or input in their naming but
> it's possible they did right at the beginning before the ABI was anywhere
> near stable.  Gah. I dread to think how long that that has been wrong.

Ok, great :)

> So I think range_raw postfix is the best bet.

Will go with this, thanks.

> Jonathan
>
>
>
>
>
> >
>
> >
> > Andrea
>

^ permalink raw reply	[flat|nested] 55+ messages in thread

* Re: [v2 06/10] iio: document bno055 private sysfs attributes
  2022-01-17  9:37               ` Andrea Merello
@ 2022-01-22 18:08                 ` Jonathan Cameron
  0 siblings, 0 replies; 55+ messages in thread
From: Jonathan Cameron @ 2022-01-22 18:08 UTC (permalink / raw)
  To: Andrea Merello
  Cc: Mauro Carvalho Chehab, linux-iio, linux-kernel, devicetree,
	Lars-Peter Clausen, Rob Herring, Andy Shevchenko, Matt Ranostay,
	Alexandru Ardelean, Jacopo Mondi, Andrea Merello

On Mon, 17 Jan 2022 10:37:33 +0100
Andrea Merello <andrea.merello@gmail.com> wrote:

> Trivial inline comments below. Beside that, I've found another
> pleasing issue with this "range" thing on this device..
> 
> One one hand, things seem to always work as we discussed for the
> accelerometer (i.e. range doesn't affect the scale; the HW always
> provides readings in the same scale, but with different range and
> precision) on the other hand, the gyroscope behavior depends by the
> internal IMU firmware version.. great..

*sigh* :)

> 
> Stock firmware has a bug[0], so that the "range" gyroscope registers
> do change the scale indeed. AFAICT stock firmware is the one you find
> in most (all?) breakout boards, which are usually available (and which
> I'm using right now for this driver mainlining attempt). Upgrading
> firmware looks like a rather obscure process that AFAICT can be done
> only in some specific USB-stick demo-board ("shuttle board") or with
> maybe with FAE assistance on custom developed boards [1] (i.e. maybe
> can be done by some professional user; I would say not for most
> people).
> 
> So, I'm now wondering how to handle this... I really want to support
> the stock FW, which seems the most widespread, and the one I have
> right now; I'd say this means: the accelerometer thing will still work
> as we discussed (i.e. the range attribute thing), while the gyro will
> have writeable scale, and a (ro) scale_available attrib. But what
> about the gyro range thing? Should I drop it, or keep it as
> informative read-only?

I'd be cynical and for initial version at least, just hide it as 'too
complex' with a comment in the driver code on why.

> 
> Then I could also support the new firmware (which I cannot test right
> now with my actual breakout board, but I might see whether I could get
> a board with an updated IMU), keeping also the current driver behavior
> (i.e. range stuff).
> 
> But the question is: in either cases (new vs old fw) should the
> non-necessary attributes disappear or they may just be RO or locked
> (i.e. scale_available for new FW and range stuff for the old one)?

If they don't have meaning then they should disappear, but it would
also be valid to have the 'broken' one be read only if there is
an appropriate value.

> 
> Any thoughts and advice on this whole thing would be very welcome :)
> my current inclination anyway now tends to be: go on supporting only
> the stock FW (i.e. the board I have here now) and eventually add
> support for the new fw later on, after merge.

Sounds sensible - but.... Make sure you check the firmware version
number (I hope it has one) and print a warning at least if you get
one that you have strong reason to believe will handle this differently
from whatever the driver is supporting.

This is definitely going to be a case for detailed comments in
the driver code so that we can 'recall' what on earth was
going on here in N years time!

> 
> [0] https://community.bosch-sensortec.com/t5/MEMS-sensors-forum/BNO055-Wrong-sensitivity-resolution-in-datasheet/td-p/10266
> [1] https://community.bosch-sensortec.com/t5/MEMS-sensors-forum/BNO055-Software-Version/td-p/14001
> 
> > > I've looked at other iio sysfs attributes in the DOC.  It seems  that
> > > "thesh" and "roc" attributes allows for both preprocessed and raw
> > > data: I found e.g. "<type>[Y][_name]_<raw|input>_thresh_value", but
> > > the related "what" entries written above all seem to omit both "_raw"
> > > and "_input"; I don't understand why.  
> >
> > Excellent point.  That documentation is garbage.  Events are meant
> > to pick it up implicitly from the related channel _raw or _input.
> > I don't remember them ever having raw or input in their naming but
> > it's possible they did right at the beginning before the ABI was anywhere
> > near stable.  Gah. I dread to think how long that that has been wrong.  
> 
> Ok, great :)
> 
> > So I think range_raw postfix is the best bet.  
> 
> Will go with this, thanks.
> 
> > Jonathan
> >
> >
> >
> >
> >  
> > >  
> >  
> > >
> > > Andrea  
> >  


^ permalink raw reply	[flat|nested] 55+ messages in thread

end of thread, other threads:[~2022-01-22 18:02 UTC | newest]

Thread overview: 55+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20210715141742.15072-1-andrea.merello@gmail.com>
2021-10-28 10:18 ` [v2 00/10] Add support for Bosch BNO055 IMU Andrea Merello
2021-10-28 10:18   ` [v2 01/10] utils_macro: introduce find_closest_unsorted() Andrea Merello
2021-10-28 10:25     ` Andy Shevchenko
2021-11-08 11:05       ` Andrea Merello
2021-10-28 10:18   ` [v2 02/10] iio: document linear acceleration modifiers Andrea Merello
2021-10-28 10:31     ` Andy Shevchenko
2021-11-09  7:48       ` Andrea Merello
2021-10-28 10:40     ` Jonathan Cameron
2021-11-09  8:00       ` Andrea Merello
2021-11-09 17:00         ` Jonathan Cameron
2021-10-28 10:18   ` [v2 03/10] iio: document euler angles modifiers Andrea Merello
2021-10-28 10:33     ` Andy Shevchenko
2021-10-28 10:41     ` Jonathan Cameron
2021-11-09  8:15       ` Andrea Merello
2021-11-09 17:03         ` Jonathan Cameron
2021-10-28 10:18   ` [v2 04/10] iio: add modifiers for linear acceleration Andrea Merello
2021-10-28 10:45     ` Jonathan Cameron
2021-11-09  9:58       ` Andrea Merello
2021-11-09 17:05         ` Jonathan Cameron
2021-10-28 10:18   ` [v2 05/10] iio: add modifers for pitch, yaw, roll Andrea Merello
2021-10-28 10:47     ` Jonathan Cameron
2021-10-28 10:18   ` [v2 06/10] iio: document bno055 private sysfs attributes Andrea Merello
2021-10-28 11:04     ` Jonathan Cameron
2021-11-09 10:22       ` Andrea Merello
2021-11-14 16:20         ` Jonathan Cameron
2022-01-04 11:42           ` Andrea Merello
2022-01-15 15:27             ` Jonathan Cameron
2022-01-17  9:37               ` Andrea Merello
2022-01-22 18:08                 ` Jonathan Cameron
2021-10-28 10:18   ` [v2 07/10] iio: imu: add Bosch Sensortec BNO055 core driver Andrea Merello
2021-10-28 13:31     ` Jonathan Cameron
2021-11-09 11:52       ` Andrea Merello
2021-11-14 16:33         ` Jonathan Cameron
2021-10-28 10:18   ` [v2 08/10] dt-bindings: iio: imu: add documentation for Bosch BNO055 bindings Andrea Merello
2021-10-28 12:25     ` Rob Herring
2021-10-28 10:18   ` [v2 09/10] iio: imu: add BNO055 serdev driver Andrea Merello
2021-10-28 12:31     ` Jonathan Cameron
2021-11-09 15:33       ` Andrea Merello
2021-11-14 16:37         ` Jonathan Cameron
2021-10-29  7:09     ` kernel test robot
2021-10-29 12:59     ` kernel test robot
2021-10-28 10:18   ` [v2 10/10] iio: imu: add BNO055 I2C driver Andrea Merello
2021-10-28 11:10     ` Jonathan Cameron
2021-11-11 10:12       ` Andrea Merello
2021-11-14 16:38         ` Jonathan Cameron
2021-10-28 22:04     ` Randy Dunlap
2021-11-09 11:56       ` Andrea Merello
2021-11-09 15:47         ` Randy Dunlap
2021-11-09 18:21           ` Joe Perches
2021-11-09 19:11             ` Randy Dunlap
2021-11-09 20:46               ` Joe Perches
2021-11-09 21:20                 ` Randy Dunlap
2021-10-29 13:30     ` kernel test robot
2021-10-28 10:35   ` [v2 00/10] Add support for Bosch BNO055 IMU Jonathan Cameron
2021-10-28 10:33     ` Andy Shevchenko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).