[v6 00/14] Add support for Bosch BNO055 IMU

36 messages, 6 authors, 2022-07-13 · open the first message on its own page

[v6 00/14] Add support for Bosch BNO055 IMU

From: <hidden>
Date: 2022-06-14 12:35:59

From: Andrea Merello <redacted>

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

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

  7/14: fix binary attributes didn't work with IIO

  8/14 to 11/14: add the core IIO BNO055 driver and documentation for sysfs
                 attributes and DT bindings

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

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

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

Differences wrt v5:
- get rid of few macros
- add almost-empty trace-related file that slipped off prev series
- add newlines to dev_warn() and friends
- some code simplifications
- take advatage of kstrtobool
- style fixes
- comments improvement
- add missing header
- rebase
- fix for some compile errors found by: kernel test robot [off-list ref]

Differences wrt other BNO055 drivers:

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

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

  - This driver supports also serial access (to be reliable, reset pin is
    required to be wired)

  - The above driver tried to support all IMU HW modes by allowing to
    choose one in the DT, and adapting IIO attributes accordingly. This
    driver does not rely on DT for this, instead settings are done via
    sysfs attributes.  All IIO attributes are always exposed; more on this
    later on. This driver however supports only a subset of the
    HW-supported modes.

  - This driver has some support for managing the IMU calibration

Supported operation modes:

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

  - Fusion mode, which still provides AMG measures, while it also provides
    other data calculated by the IMU (e.g. rotation angles, linear
    acceleration, etc). In this mode user has no freedom to set any sensor
    parameter, since the HW locks them. Autocalibration and correction is
    performed by the IMU.

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

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

  IIO attributes for unavailable measurements (e.g. Euler angles in AMG
  mode) can't be read (return -EBUSY, or refuse to enable buffer).

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 (14):
  iio: add modifiers for linear acceleration
  iio: document linear acceleration modifiers
  iio: event_monitor: add linear acceleration modifiers
  iio: add modifers for pitch, yaw, roll
  iio: document pitch, yaw, roll modifiers
  iio: event_monitor: add pitch, yaw and roll modifiers
  iio: add support for binary attributes
  iio: imu: add Bosch Sensortec BNO055 core driver
  iio: document bno055 private sysfs attributes
  iio: document "serialnumber" sysfs attribute
  dt-bindings: iio/imu: Add Bosch BNO055
  iio: imu: add BNO055 serdev driver
  iio: imu: add BNO055 I2C driver
  docs: iio: add documentation for BNO055 driver

 Documentation/ABI/testing/sysfs-bus-iio       |   25 +
 .../ABI/testing/sysfs-bus-iio-bno055          |   81 +
 .../bindings/iio/imu/bosch,bno055.yaml        |   59 +
 Documentation/iio/bno055.rst                  |   50 +
 Documentation/iio/index.rst                   |    2 +
 drivers/iio/imu/Kconfig                       |    1 +
 drivers/iio/imu/Makefile                      |    1 +
 drivers/iio/imu/bno055/Kconfig                |   25 +
 drivers/iio/imu/bno055/Makefile               |   10 +
 drivers/iio/imu/bno055/bno055.c               | 1707 +++++++++++++++++
 drivers/iio/imu/bno055/bno055.h               |   13 +
 drivers/iio/imu/bno055/bno055_i2c.c           |   57 +
 drivers/iio/imu/bno055/bno055_ser_core.c      |  560 ++++++
 drivers/iio/imu/bno055/bno055_ser_trace.c     |   13 +
 drivers/iio/imu/bno055/bno055_ser_trace.h     |  104 +
 drivers/iio/industrialio-core.c               |   10 +-
 include/uapi/linux/iio/types.h                |    7 +-
 tools/iio/iio_event_monitor.c                 |    6 +
 18 files changed, 2729 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-bno055
 create mode 100644 Documentation/devicetree/bindings/iio/imu/bosch,bno055.yaml
 create mode 100644 Documentation/iio/bno055.rst
 create mode 100644 drivers/iio/imu/bno055/Kconfig
 create mode 100644 drivers/iio/imu/bno055/Makefile
 create mode 100644 drivers/iio/imu/bno055/bno055.c
 create mode 100644 drivers/iio/imu/bno055/bno055.h
 create mode 100644 drivers/iio/imu/bno055/bno055_i2c.c
 create mode 100644 drivers/iio/imu/bno055/bno055_ser_core.c
 create mode 100644 drivers/iio/imu/bno055/bno055_ser_trace.c
 create mode 100644 drivers/iio/imu/bno055/bno055_ser_trace.h

--
2.17.1

Re: [v6 08/14] iio: imu: add Bosch Sensortec BNO055 core driver

From: Andy Shevchenko <hidden>
Date: 2022-06-13 19:04:24

On Mon, Jun 13, 2022 at 2:05 PM [off-list ref] wrote:
From: Andrea Merello <redacted>

Add the 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 does 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.
...
+config BOSCH_BNO055_IIO
Does it need _IIO suffix? Any name collision?

...
quoted hunk
+static int bno055_acc_lpf_vals[] = {
+       7, 810000, 15, 630000, 31, 250000, 62, 500000,
+       125, 0, 250, 0, 500, 0, 1000, 0
+ Comma?
+};
...
+                                /* G:   2,    4,    8,    16 */
Indentation of this comment is a bit off.
+static int bno055_acc_range_vals[] = {1962, 3924, 7848, 15696};
Perhaps split this to 4 lines and put the comment on top of the third line?

...
quoted hunk
+static int bno055_gyr_scale_vals[] = {
+       125, 1877467, 250, 1877467, 500, 1877467,
+       1000, 1877467, 2000, 1877467
+ Comma?
+};
...
quoted hunk
+#ifdef CONFIG_DEBUG_FS
+       struct dentry *debugfs;
+#endif
...
+                       /*
+                        * IMU reports sensor offests; IIO wants correction
offsets
+                        * offsets, thus we need the 'minus' here.
+                        */
...
+       if (kstrtobool(buf, &en))
+               return -EINVAL;
Why shadow an actual error code(s)?

...
quoted hunk
+       ret = kstrtoul(buf, 10, &val);
+       if (ret)
+               return ret;
Here it's done properly (see just above).

...
quoted hunk
+static void bno055_debugfs_init(struct iio_dev *iio_dev)
+{
+       struct bno055_priv *priv = iio_priv(iio_dev);
+
+       priv->debugfs = debugfs_create_file("firmware_version", 0400,
+                                           iio_get_debugfs_dentry(iio_dev),
+                                           priv, &bno055_fw_version_ops);
+       devm_add_action_or_reset(priv->dev, bno055_debugfs_remove, priv->debugfs);
Shouldn't we report the potential error here? It's not directly
related to debugfs, but something which is not directly related.
+}
...
quoted hunk
+static IIO_DEVICE_ATTR(fusion_enable, 0644,
+                      bno055_fusion_enable_show,
+                      bno055_fusion_enable_store, 0);
IIO_DEVICE_ATTR_RW()
quoted hunk
+static IIO_DEVICE_ATTR(in_magn_calibration_fast_enable, 0644,
+                      bno055_fmc_enable_show,
+                      bno055_fmc_enable_store, 0);
+
+static IIO_DEVICE_ATTR(in_accel_range_raw, 0644,
+                      bno055_in_accel_range_show,
+                      bno055_in_accel_range_store, 0);
Ditto for above.

...
+       /*
+        * All chans are made up 1 16-bit sample, except for quaternion that is
channels
quoted hunk
+        * 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.
+        */
-- 
With Best Regards,
Andy Shevchenko

Re: [v6 12/14] iio: imu: add BNO055 serdev driver

From: Andy Shevchenko <hidden>
Date: 2022-06-13 19:04:55

On Mon, Jun 13, 2022 at 2:05 PM [off-list ref] wrote:
From: Andrea Merello <redacted>

Add a serdev driver for communicating to a BNO055 IMU via serial bus, and
enable the BNO055 core driver to work in this scenario.
...
+ * This driver is besed on
based
quoted hunk
+ *     Plantower PMS7003 particulate matter sensor driver
+ *     Which is
+ *     Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
...
quoted hunk
+/*
+ * Read operation overhead:
+ *  4 bytes req + 2byte resp hdr.
+ *  6 bytes = 60 bit (considering 1start + 1stop bits).
+ *  60/115200 = ~520uS + about 2500mS dealay -> ~3mS
delay
+ * In 3mS we could read back about 34 bytes that means 17 samples, this means
+ * that in case of scattered read in which the gap is 17 samples or less it is
reads
quoted hunk
+ * 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. Because of this we
+ * round-up to 22, which is the max number of samples, always bursting indeed.
+ */
...
+/*
+ * Sends a read or write command.
Send

...
+/*
+ * Handler for received data; this is called from the reicever callback whenever
receiver
+ * it got some packet from the serial bus. The status tell us whether the
tells
+ * 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
responsibility
+ * expected, of whether we got some unexpected, yet valid, packet.
+ */
...
quoted hunk
+               /*
+                * New packet.
+                * Check for its 1st byte, that identifies the pkt type.
byte that
+                */
-- 
With Best Regards,
Andy Shevchenko

Re: [v6 08/14] iio: imu: add Bosch Sensortec BNO055 core driver

From: Andy Shevchenko <hidden>
Date: 2022-06-14 10:59:46

On Tue, Jun 14, 2022 at 11:11 AM Andrea Merello [off-list ref] wrote:

...
quoted
quoted
+                                /* G:   2,    4,    8,    16 */
Indentation of this comment is a bit off.
quoted
+static int bno055_acc_range_vals[] = {1962, 3924, 7848, 15696};
Perhaps split this to 4 lines and put the comment on top of the third line?
Not sure what you mean here, sorry. May you elaborate or provide an example, please?
static int ... [] = {
    /* Comment goes here */
   value1, value2, ..., valueN,
};

...
quoted
quoted
+static void bno055_debugfs_init(struct iio_dev *iio_dev)
+{
+       struct bno055_priv *priv = iio_priv(iio_dev);
+
+       priv->debugfs = debugfs_create_file("firmware_version", 0400,
+                                           iio_get_debugfs_dentry(iio_dev),
+                                           priv, &bno055_fw_version_ops);
quoted
+       devm_add_action_or_reset(priv->dev, bno055_debugfs_remove, priv->debugfs);
Shouldn't we report the potential error here? It's not directly
related to debugfs, but something which is not directly related.
The error eventually comes out from something that has nothing to do with debugs per se (i.e. the devm stuff), but it will only affect debugfs indeed.

Assuming that we don't want to make the whole driver fail in case debugfs stuff fails (see last part of the comment above debugfs_create_file() implementation), and given that the devm_add_action_or_reset(), should indeed "reset" in case of failure (i.e. we should be in a clean situation anyway), I would say it should be OK not to propagate the error and let things go on.
As I said, it's not directly related to debugfs. Here is the resource
leak possible or bad things happen if you probe the driver, that fails
to add this call for removal, remove it, and try to insert again, in
such case the debugfs will be stale.
However we can add a dev_warn() to report what happened.
Not sure if it would suffice, I leave it to Jonathan.

-- 
With Best Regards,
Andy Shevchenko

[v6 09/14] iio: document bno055 private sysfs attributes

From: <hidden>
Date: 2022-06-14 12:28:01

From: Andrea Merello <redacted>

Add ABI documentation for bno055 driver private sysfs attributes.

Signed-off-by: Andrea Merello <redacted>
Reviewed-by: Andy Shevchenko <redacted>
---
 .../ABI/testing/sysfs-bus-iio-bno055          | 81 +++++++++++++++++++
 1 file changed, 81 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..8d3d0a79d7e4
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-bno055
@@ -0,0 +1,81 @@
+What:		/sys/bus/iio/devices/iio:deviceX/in_accel_raw_range
+KernelVersion:	5.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Raw (unscaled) range for acceleration readings. Unit after
+		application of scale is m/s^2. Note that this doesn't affects
+		the scale (which should be used when changing the maximum and
+		minimum readable value affects also the reading scaling factor).
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_raw_range
+KernelVersion:	5.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Range for angular velocity readings in radians per second. Note
+		that this does not affects the scale (which should be used when
+		changing the maximum and minimum readable value affects also the
+		reading scaling factor).
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_accel_raw_range_available
+KernelVersion:	5.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		List of allowed values for in_accel_raw_range attribute
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_raw_range_available
+KernelVersion:	5.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		List of allowed values for in_anglvel_raw_range attribute
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_magn_calibration_fast_enable
+KernelVersion:	5.20
+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.20
+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/calibration_data
+KernelVersion:	5.20
+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_accel_calibration_auto_status
+KernelVersion:	5.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Reports the autocalibration status for the accelerometer sensor.
+		Can be 0 (calibration non even enabled) or 1 to 5 where the greater
+		the number, the better the calibration status.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_gyro_calibration_auto_status
+KernelVersion:	5.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Reports the autocalibration status for the gyroscope sensor.
+		Can be 0 (calibration non even enabled) or 1 to 5 where the greater
+		the number, the better the calibration status.
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_magn_calibration_auto_status
+KernelVersion:	5.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Reports the autocalibration status for the magnetometer sensor.
+		Can be 0 (calibration non even enabled) or 1 to 5 where the greater
+		the number, the better the calibration status.
+
+What:		/sys/bus/iio/devices/iio:deviceX/sys_calibration_auto_status
+KernelVersion:	5.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Reports the status for the IMU overall autocalibration.
+		Can be 0 (calibration non even enabled) or 1 to 5 where the greater
+		the number, the better the calibration status.
-- 
2.17.1

[v6 05/14] iio: document pitch, yaw, roll modifiers

From: <hidden>
Date: 2022-06-14 12:28:06

From: Andrea Merello <redacted>

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

It looks like we have some unit inconsistency along various IIO modifiers:
it seems that incli is in deg, angl is in radians and rot isn't documented,
but at least the adis16209 driver has rot in deg.

Here we use deg (so angl is the only one using radians).

Signed-off-by: Andrea Merello <redacted>
Reviewed-by: Andy Shevchenko <redacted>
---
 Documentation/ABI/testing/sysfs-bus-iio | 9 +++++++++
 1 file changed, 9 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 88d012bbfe79..2f54f26d9e59 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -2044,3 +2044,12 @@ Description:
 		Available range for the forced calibration value, expressed as:
 
 		- a range specified as "[min step max]"
+
+What:		/sys/bus/iio/devices/iio:deviceX/in_rot_yaw_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_rot_pitch_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_rot_roll_raw
+KernelVersion:	5.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Raw (unscaled) euler angles readings. Units after
+		application of scale are deg.
-- 
2.17.1

[v6 03/14] iio: event_monitor: add linear acceleration modifiers

From: <hidden>
Date: 2022-06-14 12:28:13

From: Andrea Merello <redacted>

Following the introduction of IIO linear acceleration modifiers, update the
event_monitor tool accordingly.

Signed-off-by: Andrea Merello <redacted>
Reviewed-by: Andy Shevchenko <redacted>
---
 tools/iio/iio_event_monitor.c | 3 +++
 1 file changed, 3 insertions(+)
diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c
index 2f4581658859..1fee44abb836 100644
--- a/tools/iio/iio_event_monitor.c
+++ b/tools/iio/iio_event_monitor.c
@@ -122,6 +122,9 @@ static const char * const iio_modifier_names[] = {
 	[IIO_MOD_PM4] = "pm4",
 	[IIO_MOD_PM10] = "pm10",
 	[IIO_MOD_O2] = "o2",
+	[IIO_MOD_LINEAR_X] = "linear_x",
+	[IIO_MOD_LINEAR_Y] = "linear_y",
+	[IIO_MOD_LINEAR_Z] = "linear_z",
 };
 
 static bool event_is_known(struct iio_event_data *event)
-- 
2.17.1

[v6 13/14] iio: imu: add BNO055 I2C driver

From: <hidden>
Date: 2022-06-14 12:28:52

From: Andrea Merello <redacted>

Add an I2C driver for communicating to a BNO055 IMU via I2C bus and enable
the BNO055 core driver to work in this scenario.

Signed-off-by: Andrea Merello <redacted>
Reviewed-by: Andy Shevchenko <redacted>
---
 drivers/iio/imu/bno055/Kconfig      | 11 ++++++
 drivers/iio/imu/bno055/Makefile     |  2 +
 drivers/iio/imu/bno055/bno055_i2c.c | 57 +++++++++++++++++++++++++++++
 3 files changed, 70 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 d014b68cd43d..ccf9ea7c50f8 100644
--- a/drivers/iio/imu/bno055/Kconfig
+++ b/drivers/iio/imu/bno055/Kconfig
@@ -12,3 +12,14 @@ config BOSCH_BNO055_SERIAL
 
 	  This driver can also be built as a module. If so, the module will be
 	  called bno055_sl.
+
+config BOSCH_BNO055_I2C
+	tristate "Bosch BNO055 attached via I2C bus"
+	depends on I2C
+	select REGMAP_I2C
+	select BOSCH_BNO055_IIO
+	help
+	  Enable this to support Bosch BNO055 IMUs attached via I2C bus.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called bno055_i2c.
diff --git a/drivers/iio/imu/bno055/Makefile b/drivers/iio/imu/bno055/Makefile
index 212307ce9c08..f0be80accb5b 100644
--- a/drivers/iio/imu/bno055/Makefile
+++ b/drivers/iio/imu/bno055/Makefile
@@ -6,3 +6,5 @@ bno055_ser-y := bno055_ser_core.o
 # define_trace.h needs to know how to find our header
 CFLAGS_bno055_ser_trace.o := -I$(src)
 bno055_ser-$(CONFIG_TRACING) += bno055_ser_trace.o
+
+obj-$(CONFIG_BOSCH_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..9bb256fdb0d3
--- /dev/null
+++ b/drivers/iio/imu/bno055/bno055_i2c.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support for I2C-interfaced Bosch BNO055 IMU.
+ *
+ * Copyright (C) 2021-2022 Istituto Italiano di Tecnologia
+ * Electronic Design Laboratory
+ * Written by Andrea Merello <andrea.merello@iit.it>
+ */
+
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "bno055.h"
+
+#define BNO055_I2C_XFER_BURST_BREAK_THRESHOLD 3 /* FIXME */
+
+static int bno055_i2c_probe(struct i2c_client *client)
+{
+	struct regmap *regmap;
+
+	regmap = devm_regmap_init_i2c(client, &bno055_regmap_config);
+	if (IS_ERR(regmap))
+		return dev_err_probe(&client->dev, PTR_ERR(regmap),
+				     "Unable to init register map");
+
+	return bno055_probe(&client->dev, regmap,
+			    BNO055_I2C_XFER_BURST_BREAK_THRESHOLD, true);
+}
+
+static const struct i2c_device_id bno055_i2c_id[] = {
+	{"bno055", 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, bno055_i2c_id);
+
+static const struct of_device_id __maybe_unused bno055_i2c_of_match[] = {
+	{ .compatible = "bosch,bno055" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, bno055_i2c_of_match);
+
+static struct i2c_driver bno055_driver = {
+	.driver = {
+		.name = "bno055-i2c",
+		.of_match_table = of_match_ptr(bno055_i2c_of_match),
+	},
+	.probe_new = bno055_i2c_probe,
+	.id_table = bno055_i2c_id,
+};
+module_i2c_driver(bno055_driver);
+
+MODULE_AUTHOR("Andrea Merello");
+MODULE_DESCRIPTION("Bosch BNO055 I2C interface");
+MODULE_IMPORT_NS(IIO_BNO055);
+MODULE_LICENSE("GPL");
-- 
2.17.1

[v6 07/14] iio: add support for binary attributes

From: <hidden>
Date: 2022-06-14 12:29:15

From: Andrea Merello <redacted>

When a IIO device is registered, the IIO core creates an attribute group on
its own, where it puts the channel attributes, and where it copies the
attributes in indio_dev->info->attrs.

Unfortunately it doesn't take care of binary attributes (i.e. it only
consider indio_dev->info->attrs->attrs, and it ignores
indio_dev->info->attrs->bin_attrs).

Fix this by making the IIO layer take care also of the binary attributes.

Note that while it is necessary to copy the non-binary attributes because
the IIO layer needs more room to add the channels attribute, it should be
enough to assign the bin_attrs pointer to the binary attributes pointed by
indio_dev->info->attrs->bin_attrs.

Signed-off-by: Andrea Merello <redacted>
Reviewed-by: Andy Shevchenko <redacted>
---
 drivers/iio/industrialio-core.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 4d95d4df5e91..2ea5e19ea56a 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1581,7 +1581,7 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev)
 		ret = -ENOMEM;
 		goto error_clear_attrs;
 	}
-	/* Copy across original attributes */
+	/* Copy across original attributes, and point to original binary attributes */
 	if (indio_dev->info->attrs) {
 		memcpy(iio_dev_opaque->chan_attr_group.attrs,
 		       indio_dev->info->attrs->attrs,
@@ -1589,6 +1589,8 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev)
 		       *attrcount_orig);
 		iio_dev_opaque->chan_attr_group.is_visible =
 			indio_dev->info->attrs->is_visible;
+		iio_dev_opaque->chan_attr_group.bin_attrs =
+			indio_dev->info->attrs->bin_attrs;
 	}
 	attrn = attrcount_orig;
 	/* Add all elements from the list. */
-- 
2.17.1

[v6 11/14] dt-bindings: iio/imu: Add Bosch BNO055

From: <hidden>
Date: 2022-06-14 12:29:39

From: Andrea Merello <redacted>

Introduce new documentation file for the Bosch BNO055 IMU.

Signed-off-by: Andrea Merello <redacted>
Reviewed-by: Rob Herring <robh@kernel.org>
Reviewed-by: Andy Shevchenko <redacted>
---
 .../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..e0d06db161a9
--- /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.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

[v6 01/14] iio: add modifiers for linear acceleration

From: <hidden>
Date: 2022-06-14 12:29:40

From: Andrea Merello <redacted>

Add IIO_MOD_LINEAR_X, IIO_MOD_LINEAR_Y and IIO_MOD_LINEAR_Z modifiers to te
IIO core, which is preparatory for adding the Bosch BNO055 IMU driver.

Bosch BNO055 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 and for which
those new modifiers are for.

Signed-off-by: Andrea Merello <redacted>
Reviewed-by: Andy Shevchenko <redacted>
---
 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 adf054c7a75e..9c0ee7755884 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_LINEAR_X] = "linear_x",
+	[IIO_MOD_LINEAR_Y] = "linear_y",
+	[IIO_MOD_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 472cead10d8d..0993f6b697fc 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_LINEAR_X,
+	IIO_MOD_LINEAR_Y,
+	IIO_MOD_LINEAR_Z,
 };
 
 enum iio_event_type {
@@ -115,4 +118,3 @@ enum iio_event_direction {
 };
 
 #endif /* _UAPI_IIO_TYPES_H_ */
-
-- 
2.17.1

[v6 08/14] iio: imu: add Bosch Sensortec BNO055 core driver

From: <hidden>
Date: 2022-06-14 12:29:40

From: Andrea Merello <redacted>

Add the 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 does 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 <redacted>
---
 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 | 1712 +++++++++++++++++++++++++++++++
 drivers/iio/imu/bno055/bno055.h |   13 +
 6 files changed, 1734 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..d0ab3221fba5
--- /dev/null
+++ b/drivers/iio/imu/bno055/Kconfig
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config BOSCH_BNO055_IIO
+	tristate
diff --git a/drivers/iio/imu/bno055/Makefile b/drivers/iio/imu/bno055/Makefile
new file mode 100644
index 000000000000..56cc4de60a7e
--- /dev/null
+++ b/drivers/iio/imu/bno055/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_BOSCH_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..5d48a8d8973e
--- /dev/null
+++ b/drivers/iio/imu/bno055/bno055.c
@@ -0,0 +1,1712 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * IIO driver for Bosch BNO055 IMU
+ *
+ * Copyright (C) 2021-2022 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/bitfield.h>
+#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/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include "bno055.h"
+
+#define BNO055_FW_UID_FMT "bno055-caldata-%*phN.dat"
+#define BNO055_FW_GENERIC_NAME "bno055-caldata.dat"
+
+/* common registers */
+#define BNO055_PAGESEL_REG		0x7
+
+/* page 0 registers */
+#define BNO055_CHIP_ID_REG		0x0
+#define BNO055_CHIP_ID_MAGIC 0xA0
+#define BNO055_SW_REV_LSB_REG		0x4
+#define BNO055_SW_REV_MSB_REG		0x5
+#define BNO055_ACC_DATA_X_LSB_REG	0x8
+#define BNO055_ACC_DATA_Y_LSB_REG	0xA
+#define BNO055_ACC_DATA_Z_LSB_REG	0xC
+#define BNO055_MAG_DATA_X_LSB_REG	0xE
+#define BNO055_MAG_DATA_Y_LSB_REG	0x10
+#define BNO055_MAG_DATA_Z_LSB_REG	0x12
+#define BNO055_GYR_DATA_X_LSB_REG	0x14
+#define BNO055_GYR_DATA_Y_LSB_REG	0x16
+#define BNO055_GYR_DATA_Z_LSB_REG	0x18
+#define BNO055_EUL_DATA_X_LSB_REG	0x1A
+#define BNO055_EUL_DATA_Y_LSB_REG	0x1C
+#define BNO055_EUL_DATA_Z_LSB_REG	0x1E
+#define BNO055_QUAT_DATA_W_LSB_REG	0x20
+#define BNO055_LIA_DATA_X_LSB_REG	0x28
+#define BNO055_LIA_DATA_Y_LSB_REG	0x2A
+#define BNO055_LIA_DATA_Z_LSB_REG	0x2C
+#define BNO055_GRAVITY_DATA_X_LSB_REG	0x2E
+#define BNO055_GRAVITY_DATA_Y_LSB_REG	0x30
+#define BNO055_GRAVITY_DATA_Z_LSB_REG	0x32
+#define BNO055_SCAN_CH_COUNT ((BNO055_GRAVITY_DATA_Z_LSB_REG - BNO055_ACC_DATA_X_LSB_REG) / 2)
+#define BNO055_TEMP_REG			0x34
+#define BNO055_CALIB_STAT_REG		0x35
+#define BNO055_CALIB_STAT_MASK GENMASK(1, 0)
+#define BNO055_CALIB_STAT_MAGN_SHIFT 0
+#define BNO055_CALIB_STAT_ACCEL_SHIFT 2
+#define BNO055_CALIB_STAT_GYRO_SHIFT 4
+#define BNO055_CALIB_STAT_SYS_SHIFT 6
+#define BNO055_SYS_ERR_REG		0x3A
+#define BNO055_POWER_MODE_REG		0x3E
+#define BNO055_POWER_MODE_NORMAL 0
+#define BNO055_SYS_TRIGGER_REG		0x3F
+#define BNO055_SYS_TRIGGER_RST_SYS BIT(5)
+#define BNO055_SYS_TRIGGER_CLK_SEL BIT(7)
+#define BNO055_OPR_MODE_REG		0x3D
+#define BNO055_OPR_MODE_CONFIG 0x0
+#define BNO055_OPR_MODE_AMG 0x7
+#define BNO055_OPR_MODE_FUSION_FMC_OFF 0xB
+#define BNO055_OPR_MODE_FUSION 0xC
+#define BNO055_UNIT_SEL_REG		0x3B
+/* Android orientation mode means: pitch value decreases turning clockwise */
+#define BNO055_UNIT_SEL_ANDROID BIT(7)
+#define BNO055_UNIT_SEL_GYR_RPS BIT(1)
+#define BNO055_CALDATA_START		0x55
+#define BNO055_CALDATA_END		0x6A
+#define BNO055_CALDATA_LEN 22
+
+/*
+ * 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 BNO055_PG1(x) ((x) | 0x80)
+#define BNO055_ACC_CONFIG_REG		BNO055_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		BNO055_PG1(0x9)
+#define BNO055_MAG_CONFIG_HIGHACCURACY 0x18
+#define BNO055_MAG_CONFIG_ODR_MASK GENMASK(2, 0)
+#define BNO055_GYR_CONFIG_REG		BNO055_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		BNO055_PG1(0x1F)
+#define BNO055_UID_LOWER_REG		BNO055_PG1(0x50)
+#define BNO055_UID_HIGHER_REG		BNO055_PG1(0x5F)
+#define BNO055_UID_LEN 16
+
+struct bno055_sysfs_attr {
+	int *vals;
+	int len;
+	int *fusion_vals;
+	int *hw_xlate;
+	int type;
+};
+
+static int bno055_acc_lpf_vals[] = {
+	7, 810000, 15, 630000, 31, 250000, 62, 500000,
+	125, 0, 250, 0, 500, 0, 1000, 0
+};
+
+static struct bno055_sysfs_attr bno055_acc_lpf = {
+	.vals = bno055_acc_lpf_vals,
+	.len = ARRAY_SIZE(bno055_acc_lpf_vals),
+	.fusion_vals = (int[]){62, 500000},
+	.type = IIO_VAL_INT_PLUS_MICRO,
+};
+
+				 /* G:   2,    4,    8,    16 */
+static int bno055_acc_range_vals[] = {1962, 3924, 7848, 15696};
+
+static struct bno055_sysfs_attr bno055_acc_range = {
+	.vals = bno055_acc_range_vals,
+	.len = ARRAY_SIZE(bno055_acc_range_vals),
+	.fusion_vals = (int[]){3924}, /* 4G */
+	.type = IIO_VAL_INT,
+};
+
+/*
+ * Theoretically the IMU should return data in a given (i.e. fixed) unit
+ * regardless of the range setting. This happens for the accelerometer, but not
+ * for the gyroscope; the gyroscope range setting affects the scale.
+ * This is probably due to this[0] bug.
+ * For this reason we map the internal range setting onto the standard IIO scale
+ * attribute for gyro.
+ * Since the bug[0] may be fixed in future, we check for the IMU FW version and
+ * eventually warn the user.
+ * Currently we just don't care about "range" attributes for gyro.
+ *
+ * [0]  https://community.bosch-sensortec.com/t5/MEMS-sensors-forum/BNO055-Wrong-sensitivity-resolution-in-datasheet/td-p/10266
+ */
+
+/*
+ * dps = hwval * (dps_range/2^15)
+ * rps = hwval * (rps_range/2^15)
+ *     = hwval * (dps_range/(2^15 * k))
+ * where k is rad-to-deg factor
+ */
+static int bno055_gyr_scale_vals[] = {
+	125, 1877467, 250, 1877467, 500, 1877467,
+	1000, 1877467, 2000, 1877467
+};
+
+static struct bno055_sysfs_attr bno055_gyr_scale = {
+	.vals = bno055_gyr_scale_vals,
+	.len = ARRAY_SIZE(bno055_gyr_scale_vals),
+	.fusion_vals = (int[]){1, 900},
+	.hw_xlate = (int[]){4, 3, 2, 1, 0},
+	.type = IIO_VAL_FRACTIONAL,
+};
+
+static int bno055_gyr_lpf_vals[] = {12, 23, 32, 47, 64, 116, 230, 523};
+static struct bno055_sysfs_attr bno055_gyr_lpf = {
+	.vals = bno055_gyr_lpf_vals,
+	.len = ARRAY_SIZE(bno055_gyr_lpf_vals),
+	.fusion_vals = (int[]){32},
+	.hw_xlate = (int[]){5, 4, 7, 3, 6, 2, 1, 0},
+	.type = IIO_VAL_INT,
+};
+
+static int bno055_mag_odr_vals[] = {2, 6, 8, 10, 15, 20, 25, 30};
+static struct bno055_sysfs_attr bno055_mag_odr = {
+	.vals = bno055_mag_odr_vals,
+	.len =  ARRAY_SIZE(bno055_mag_odr_vals),
+	.fusion_vals = (int[]){20},
+	.type = IIO_VAL_INT,
+};
+
+struct bno055_priv {
+	struct regmap *regmap;
+	struct device *dev;
+	struct clk *clk;
+	int operation_mode;
+	int xfer_burst_break_thr;
+	struct mutex lock;
+	u8 uid[BNO055_UID_LEN];
+	struct gpio_desc *reset_gpio;
+	bool sw_reset;
+	struct {
+		__le16 chans[BNO055_SCAN_CH_COUNT];
+		s64 timestamp __aligned(8);
+	} buf;
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *debugfs;
+#endif
+};
+
+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 < BNO055_PG1(0) && reg > BNO055_CALDATA_END) ||
+	    reg == 0x3C)
+		return false;
+
+	/* unnamed PG1 reserved areas */
+	if (reg > BNO055_PG1(BNO055_UID_HIGHER_REG) ||
+	    (reg < BNO055_PG1(BNO055_UID_LOWER_REG) && reg > BNO055_PG1(BNO055_GYR_AM_SET_REG)) ||
+	    reg == BNO055_PG1(0xE) ||
+	    (reg < BNO055_PG1(BNO055_PAGESEL_REG) && reg >= BNO055_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;
+
+	/* ID 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_NS_GPL(bno055_regmap_config, IIO_BNO055);
+
+/* must be called in configuration mode */
+static int bno055_calibration_load(struct bno055_priv *priv, const u8 *data, int len)
+{
+	if (len != BNO055_CALDATA_LEN) {
+		dev_dbg(priv->dev, "Invalid calibration file size %d (expected %d)",
+			len, BNO055_CALDATA_LEN);
+		return -EINVAL;
+	}
+
+	dev_dbg(priv->dev, "loading cal data: %*ph", BNO055_CALDATA_LEN, data);
+	return regmap_bulk_write(priv->regmap, BNO055_CALDATA_START,
+				 data, BNO055_CALDATA_LEN);
+}
+
+static int bno055_operation_mode_do_set(struct bno055_priv *priv,
+					int operation_mode)
+{
+	int ret;
+
+	ret = regmap_write(priv->regmap, BNO055_OPR_MODE_REG,
+			   operation_mode);
+	if (ret)
+		return ret;
+
+	/* Following datasheet specifications: sensor takes 7mS up to 19 mS to switch mode */
+	msleep(20);
+
+	return 0;
+}
+
+static int bno055_system_reset(struct bno055_priv *priv)
+{
+	int ret;
+
+	if (priv->reset_gpio) {
+		gpiod_set_value_cansleep(priv->reset_gpio, 0);
+		usleep_range(5000, 10000);
+		gpiod_set_value_cansleep(priv->reset_gpio, 1);
+	} else if (priv->sw_reset) {
+		ret = regmap_write(priv->regmap, BNO055_SYS_TRIGGER_REG,
+				   BNO055_SYS_TRIGGER_RST_SYS);
+		if (ret)
+			return ret;
+	} else {
+		return 0;
+	}
+
+	regcache_drop_region(priv->regmap, 0x0, 0xff);
+	usleep_range(650000, 700000);
+
+	return 0;
+}
+
+static int bno055_init(struct bno055_priv *priv, const u8 *caldata, int len)
+{
+	int ret;
+
+	ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(priv->regmap, BNO055_POWER_MODE_REG,
+			   BNO055_POWER_MODE_NORMAL);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(priv->regmap, BNO055_SYS_TRIGGER_REG,
+			   priv->clk ? BNO055_SYS_TRIGGER_CLK_SEL : 0);
+	if (ret)
+		return ret;
+
+	/* use standard SI units */
+	ret = regmap_write(priv->regmap, BNO055_UNIT_SEL_REG,
+			   BNO055_UNIT_SEL_ANDROID | BNO055_UNIT_SEL_GYR_RPS);
+	if (ret)
+		return ret;
+
+	if (caldata) {
+		ret = bno055_calibration_load(priv, caldata, len);
+		if (ret)
+			dev_warn(priv->dev, "failed to load calibration data with error %d\n",
+				 ret);
+	}
+
+	return 0;
+}
+
+static ssize_t bno055_operation_mode_set(struct bno055_priv *priv,
+					 int operation_mode)
+{
+	u8 caldata[BNO055_CALDATA_LEN];
+	int ret;
+
+	mutex_lock(&priv->lock);
+
+	ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+	if (ret)
+		goto exit_unlock;
+
+	if (operation_mode == BNO055_OPR_MODE_FUSION ||
+	    operation_mode == BNO055_OPR_MODE_FUSION_FMC_OFF) {
+		/* for entering fusion mode, reset the chip to clear the algo state */
+		ret = regmap_bulk_read(priv->regmap, BNO055_CALDATA_START, caldata,
+				       BNO055_CALDATA_LEN);
+		if (ret)
+			goto exit_unlock;
+
+		ret = bno055_system_reset(priv);
+		if (ret)
+			goto exit_unlock;
+
+		ret = bno055_init(priv, caldata, BNO055_CALDATA_LEN);
+		if (ret)
+			goto exit_unlock;
+	}
+
+	ret = bno055_operation_mode_do_set(priv, operation_mode);
+	if (ret)
+		goto exit_unlock;
+
+	priv->operation_mode = operation_mode;
+
+exit_unlock:
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static void bno055_uninit(void *arg)
+{
+	struct bno055_priv *priv = arg;
+
+	/* stop the IMU */
+	bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+}
+
+#define BNO055_CHANNEL(_type, _axis, _index, _address, _sep, _sh, _avail) {	\
+	.address = _address,							\
+	.type = _type,								\
+	.modified = 1,								\
+	.channel2 = IIO_MOD_##_axis,						\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | (_sep),			\
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | (_sh),		\
+	.info_mask_shared_by_type_available = _avail,				\
+	.scan_index = _index,							\
+	.scan_type = {								\
+		.sign = 's',							\
+		.realbits = 16,							\
+		.storagebits = 16,						\
+		.endianness = IIO_LE,						\
+		.repeat = IIO_MOD_##_axis == IIO_MOD_QUATERNION ? 4 : 0,        \
+	},									\
+}
+
+/* scan indexes follow DATA register order */
+enum bno055_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,
+	_BNO055_SCAN_MAX
+};
+
+static const struct iio_chan_spec bno055_channels[] = {
+	/* accelerometer */
+	BNO055_CHANNEL(IIO_ACCEL, X, BNO055_SCAN_ACCEL_X,
+		       BNO055_ACC_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)),
+	BNO055_CHANNEL(IIO_ACCEL, Y, BNO055_SCAN_ACCEL_Y,
+		       BNO055_ACC_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)),
+	BNO055_CHANNEL(IIO_ACCEL, Z, BNO055_SCAN_ACCEL_Z,
+		       BNO055_ACC_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)),
+	/* gyroscope */
+	BNO055_CHANNEL(IIO_ANGL_VEL, X, BNO055_SCAN_GYRO_X,
+		       BNO055_GYR_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
+		       BIT(IIO_CHAN_INFO_SCALE)),
+	BNO055_CHANNEL(IIO_ANGL_VEL, Y, BNO055_SCAN_GYRO_Y,
+		       BNO055_GYR_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
+		       BIT(IIO_CHAN_INFO_SCALE)),
+	BNO055_CHANNEL(IIO_ANGL_VEL, Z, BNO055_SCAN_GYRO_Z,
+		       BNO055_GYR_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+		       BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
+		       BIT(IIO_CHAN_INFO_SCALE)),
+	/* magnetometer */
+	BNO055_CHANNEL(IIO_MAGN, X, BNO055_SCAN_MAGN_X,
+		       BNO055_MAG_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)),
+	BNO055_CHANNEL(IIO_MAGN, Y, BNO055_SCAN_MAGN_Y,
+		       BNO055_MAG_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)),
+	BNO055_CHANNEL(IIO_MAGN, Z, BNO055_SCAN_MAGN_Z,
+		       BNO055_MAG_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+		       BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)),
+	/* euler angle */
+	BNO055_CHANNEL(IIO_ROT, YAW, BNO055_SCAN_YAW,
+		       BNO055_EUL_DATA_X_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_ROT, ROLL, BNO055_SCAN_ROLL,
+		       BNO055_EUL_DATA_Y_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_ROT, PITCH, BNO055_SCAN_PITCH,
+		       BNO055_EUL_DATA_Z_LSB_REG, 0, 0, 0),
+	/* quaternion */
+	BNO055_CHANNEL(IIO_ROT, QUATERNION, BNO055_SCAN_QUATERNION,
+		       BNO055_QUAT_DATA_W_LSB_REG, 0, 0, 0),
+
+	/* linear acceleration */
+	BNO055_CHANNEL(IIO_ACCEL, LINEAR_X, BNO055_SCAN_LIA_X,
+		       BNO055_LIA_DATA_X_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_ACCEL, LINEAR_Y, BNO055_SCAN_LIA_Y,
+		       BNO055_LIA_DATA_Y_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_ACCEL, LINEAR_Z, BNO055_SCAN_LIA_Z,
+		       BNO055_LIA_DATA_Z_LSB_REG, 0, 0, 0),
+
+	/* gravity vector */
+	BNO055_CHANNEL(IIO_GRAVITY, X, BNO055_SCAN_GRAVITY_X,
+		       BNO055_GRAVITY_DATA_X_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_GRAVITY, Y, BNO055_SCAN_GRAVITY_Y,
+		       BNO055_GRAVITY_DATA_Y_LSB_REG, 0, 0, 0),
+	BNO055_CHANNEL(IIO_GRAVITY, Z, BNO055_SCAN_GRAVITY_Z,
+		       BNO055_GRAVITY_DATA_Z_LSB_REG, 0, 0, 0),
+
+	{
+		.type = IIO_TEMP,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+		.scan_index = -1,
+	},
+	IIO_CHAN_SOFT_TIMESTAMP(BNO055_SCAN_TIMESTAMP),
+};
+
+static int bno055_get_regmask(struct bno055_priv *priv, int *val, int *val2,
+			      int reg, int mask, struct bno055_sysfs_attr *attr)
+{
+	const int shift = __ffs(mask);
+	int hwval, idx;
+	int ret;
+	int i;
+
+	ret = regmap_read(priv->regmap, reg, &hwval);
+	if (ret)
+		return ret;
+
+	idx = (hwval & mask) >> shift;
+	if (attr->hw_xlate)
+		for (i = 0; i < attr->len; i++)
+			if (attr->hw_xlate[i] == idx) {
+				idx = i;
+				break;
+			}
+	if (attr->type == IIO_VAL_INT) {
+		*val = attr->vals[idx];
+	} else { /* IIO_VAL_INT_PLUS_MICRO or IIO_VAL_FRACTIONAL */
+		*val = attr->vals[idx * 2];
+		*val2 = attr->vals[idx * 2 + 1];
+	}
+
+	return attr->type;
+}
+
+static int bno055_set_regmask(struct bno055_priv *priv, int val, int val2,
+			      int reg, int mask, struct bno055_sysfs_attr *attr)
+{
+	const int shift = __ffs(mask);
+	int best_delta;
+	int req_val;
+	int tbl_val;
+	bool first;
+	int delta;
+	int hwval;
+	int ret;
+	int len;
+	int i;
+
+	/*
+	 * The closest value the HW supports is only one in fusion mode,
+	 * and it is autoselected, so don't do anything, just return OK,
+	 * as the closest possible value has been (virtually) selected
+	 */
+	if (priv->operation_mode != BNO055_OPR_MODE_AMG)
+		return 0;
+
+	len = attr->len;
+
+	/*
+	 * We always get a request in INT_PLUS_MICRO, but we
+	 * take care of the micro part only when we really have
+	 * non-integer tables. This prevents 32-bit overflow with
+	 * larger integers contained in integer tables.
+	 */
+	req_val = val;
+	if (attr->type != IIO_VAL_INT) {
+		len /= 2;
+		req_val = min(val, 2147) * 1000000 + val2;
+	}
+
+	first = true;
+	for (i = 0; i < len; i++) {
+		switch (attr->type) {
+		case IIO_VAL_INT:
+			tbl_val = attr->vals[i];
+			break;
+		case IIO_VAL_INT_PLUS_MICRO:
+			WARN_ON(attr->vals[i * 2] > 2147);
+			tbl_val = attr->vals[i * 2] * 1000000 +
+				attr->vals[i * 2 + 1];
+			break;
+		case IIO_VAL_FRACTIONAL:
+			WARN_ON(attr->vals[i * 2] > 4294);
+			tbl_val = attr->vals[i * 2] * 1000000 /
+				attr->vals[i * 2 + 1];
+			break;
+		default:
+			return -EINVAL;
+		}
+		delta = abs(tbl_val - req_val);
+		if (delta < best_delta || first) {
+			best_delta = delta;
+			hwval = i;
+			first = false;
+		}
+	}
+
+	if (attr->hw_xlate)
+		hwval = attr->hw_xlate[hwval];
+
+	ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(priv->regmap, reg, mask, hwval << shift);
+	if (ret)
+		return ret;
+
+	return bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_AMG);
+}
+
+static int bno055_read_simple_chan(struct iio_dev *indio_dev,
+				   struct iio_chan_spec const *chan,
+				   int *val, int *val2, long mask)
+{
+	struct bno055_priv *priv = iio_priv(indio_dev);
+	__le16 raw_val;
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = regmap_bulk_read(priv->regmap, chan->address,
+				       &raw_val, sizeof(raw_val));
+		if (ret < 0)
+			return ret;
+		*val = sign_extend32(le16_to_cpu(raw_val), 15);
+		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
+			 * offsets, thus we need the 'minus' here.
+			 */
+			*val = -sign_extend32(le16_to_cpu(raw_val), 15);
+		}
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 1;
+		switch (chan->type) {
+		case IIO_GRAVITY:
+			/* Table 3-35: 1 m/s^2 = 100 LSB */
+		case IIO_ACCEL:
+			/* Table 3-17: 1 m/s^2 = 100 LSB */
+			*val2 = 100;
+			break;
+		case IIO_MAGN:
+			/*
+			 * Table 3-19: 1 uT = 16 LSB.  But we need
+			 * Gauss: 1G = 0.1 uT.
+			 */
+			*val2 = 160;
+			break;
+		case IIO_ANGL_VEL:
+			/*
+			 * Table 3-22: 1 Rps = 900 LSB
+			 * .. but this is not exactly true. See comment at the
+			 * beginning of this file.
+			 */
+			if (priv->operation_mode != BNO055_OPR_MODE_AMG) {
+				*val = bno055_gyr_scale.fusion_vals[0];
+				*val2 = bno055_gyr_scale.fusion_vals[1];
+				return IIO_VAL_FRACTIONAL;
+			}
+
+			return bno055_get_regmask(priv, val, val2,
+						  BNO055_GYR_CONFIG_REG,
+						  BNO055_GYR_CONFIG_RANGE_MASK,
+						  &bno055_gyr_scale);
+			break;
+		case IIO_ROT:
+			/* Table 3-28: 1 degree = 16 LSB */
+			*val2 = 16;
+			break;
+		default:
+			return -EINVAL;
+		}
+		return IIO_VAL_FRACTIONAL;
+
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		if (chan->type != IIO_MAGN)
+			return -EINVAL;
+
+		return bno055_get_regmask(priv, val, val2,
+					  BNO055_MAG_CONFIG_REG,
+					  BNO055_MAG_CONFIG_ODR_MASK,
+					  &bno055_mag_odr);
+
+	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			return bno055_get_regmask(priv, val, val2,
+						  BNO055_GYR_CONFIG_REG,
+						  BNO055_GYR_CONFIG_LPF_MASK,
+						  &bno055_gyr_lpf);
+		case IIO_ACCEL:
+			return bno055_get_regmask(priv, val, val2,
+						  BNO055_ACC_CONFIG_REG,
+						  BNO055_ACC_CONFIG_LPF_MASK,
+						  &bno055_acc_lpf);
+		default:
+			return -EINVAL;
+		}
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int bno055_sysfs_attr_avail(struct bno055_priv *priv, struct bno055_sysfs_attr *attr,
+				   const int **vals, int *length)
+{
+	if (priv->operation_mode != BNO055_OPR_MODE_AMG) {
+		/* locked when fusion enabled */
+		*vals = attr->fusion_vals;
+		if (attr->type == IIO_VAL_INT)
+			*length = 1;
+		else
+			*length = 2; /* IIO_VAL_INT_PLUS_MICRO or IIO_VAL_FRACTIONAL*/
+	} else {
+		*vals = attr->vals;
+		*length = attr->len;
+	}
+
+	return attr->type;
+}
+
+static int bno055_read_avail(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     const int **vals, int *type, int *length,
+			     long mask)
+{
+	struct bno055_priv *priv = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			*type = bno055_sysfs_attr_avail(priv, &bno055_gyr_scale,
+							vals, length);
+			return IIO_AVAIL_LIST;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			*type = bno055_sysfs_attr_avail(priv, &bno055_gyr_lpf,
+							vals, length);
+			return IIO_AVAIL_LIST;
+		case IIO_ACCEL:
+			*type = bno055_sysfs_attr_avail(priv, &bno055_acc_lpf,
+							vals, length);
+			return IIO_AVAIL_LIST;
+		default:
+			return -EINVAL;
+		}
+
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		switch (chan->type) {
+		case IIO_MAGN:
+			*type = bno055_sysfs_attr_avail(priv, &bno055_mag_odr,
+							vals, length);
+			return IIO_AVAIL_LIST;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int bno055_read_temp_chan(struct iio_dev *indio_dev, int *val)
+{
+	struct bno055_priv *priv = iio_priv(indio_dev);
+	unsigned int raw_val;
+	int ret;
+
+	ret = regmap_read(priv->regmap, BNO055_TEMP_REG, &raw_val);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Tables 3-36 and 3-37: one byte of priv, signed, 1 LSB = 1C.
+	 * ABI wants milliC.
+	 */
+	*val = raw_val * 1000;
+
+	return IIO_VAL_INT;
+}
+
+static int bno055_read_quaternion(struct iio_dev *indio_dev,
+				  struct iio_chan_spec const *chan,
+				  int size, int *vals, int *val_len,
+				  long mask)
+{
+	struct bno055_priv *priv = iio_priv(indio_dev);
+	__le16 raw_vals[4];
+	int i, ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		if (size < 4)
+			return -EINVAL;
+		ret = regmap_bulk_read(priv->regmap,
+				       BNO055_QUAT_DATA_W_LSB_REG,
+				       raw_vals, sizeof(raw_vals));
+		if (ret < 0)
+			return ret;
+		for (i = 0; i < 4; i++)
+			vals[i] = sign_extend32(le16_to_cpu(raw_vals[i]), 15);
+		*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] = 14;
+		return IIO_VAL_FRACTIONAL_LOG2;
+	default:
+		return -EINVAL;
+	}
+}
+
+static bool bno055_is_chan_readable(struct iio_dev *indio_dev,
+				    struct iio_chan_spec const *chan)
+{
+	struct bno055_priv *priv = iio_priv(indio_dev);
+
+	if (priv->operation_mode != BNO055_OPR_MODE_AMG)
+		return true;
+
+	switch (chan->type) {
+	case IIO_GRAVITY:
+	case IIO_ROT:
+		return false;
+	case IIO_ACCEL:
+		if (chan->channel2 == IIO_MOD_LINEAR_X ||
+		    chan->channel2 == IIO_MOD_LINEAR_Y ||
+		    chan->channel2 == IIO_MOD_LINEAR_Z)
+			return false;
+		return true;
+	default:
+		return true;
+	}
+}
+
+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)
+{
+	if (!bno055_is_chan_readable(indio_dev, chan))
+		return -EBUSY;
+
+	switch (chan->type) {
+	case IIO_MAGN:
+	case IIO_ACCEL:
+	case IIO_ANGL_VEL:
+	case IIO_GRAVITY:
+		if (size < 2)
+			return -EINVAL;
+		*val_len = 2;
+		return bno055_read_simple_chan(indio_dev, chan,
+					       &vals[0], &vals[1],
+					       mask);
+	case IIO_TEMP:
+		*val_len = 1;
+		return bno055_read_temp_chan(indio_dev, &vals[0]);
+	case IIO_ROT:
+		/*
+		 * Rotation is exposed as either a quaternion or three
+		 * Euler angles.
+		 */
+		if (chan->channel2 == IIO_MOD_QUATERNION)
+			return bno055_read_quaternion(indio_dev, chan,
+						      size, vals,
+						      val_len, mask);
+		if (size < 2)
+			return -EINVAL;
+		*val_len = 2;
+		return bno055_read_simple_chan(indio_dev, chan,
+					       &vals[0], &vals[1],
+					       mask);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int bno055_read_raw_multi(struct iio_dev *indio_dev,
+				 struct iio_chan_spec const *chan,
+				 int size, int *vals, int *val_len,
+				 long mask)
+{
+	struct bno055_priv *priv = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&priv->lock);
+	ret = _bno055_read_raw_multi(indio_dev, chan, size,
+				     vals, val_len, mask);
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static int _bno055_write_raw(struct iio_dev *iio_dev,
+			     struct iio_chan_spec const *chan,
+			     int val, int val2, long mask)
+{
+	struct bno055_priv *priv = iio_priv(iio_dev);
+
+	switch (chan->type) {
+	case IIO_MAGN:
+		switch (mask) {
+		case IIO_CHAN_INFO_SAMP_FREQ:
+			return bno055_set_regmask(priv, val, val2,
+						  BNO055_MAG_CONFIG_REG,
+						  BNO055_MAG_CONFIG_ODR_MASK,
+						  &bno055_mag_odr);
+		default:
+			return -EINVAL;
+		}
+	case IIO_ACCEL:
+		switch (mask) {
+		case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+			return bno055_set_regmask(priv, val, val2,
+						  BNO055_ACC_CONFIG_REG,
+						  BNO055_ACC_CONFIG_LPF_MASK,
+						  &bno055_acc_lpf);
+
+		default:
+			return -EINVAL;
+		}
+	case IIO_ANGL_VEL:
+		switch (mask) {
+		case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+			return bno055_set_regmask(priv, val, val2,
+						  BNO055_GYR_CONFIG_REG,
+						  BNO055_GYR_CONFIG_LPF_MASK,
+						  &bno055_gyr_lpf);
+		case IIO_CHAN_INFO_SCALE:
+			return bno055_set_regmask(priv, val, val2,
+						  BNO055_GYR_CONFIG_REG,
+						  BNO055_GYR_CONFIG_RANGE_MASK,
+						  &bno055_gyr_scale);
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int bno055_write_raw(struct iio_dev *iio_dev,
+			    struct iio_chan_spec const *chan,
+			    int val, int val2, long mask)
+{
+	struct bno055_priv *priv = iio_priv(iio_dev);
+	int ret;
+
+	mutex_lock(&priv->lock);
+	ret = _bno055_write_raw(iio_dev, chan, val, val2, mask);
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+static ssize_t in_accel_range_raw_available_show(struct device *dev,
+						 struct device_attribute *attr,
+						 char *buf)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int len = 0;
+	int i;
+
+	if (priv->operation_mode != BNO055_OPR_MODE_AMG)
+		return sysfs_emit(buf, "%d\n", bno055_acc_range.fusion_vals[0]);
+
+	for (i = 0; i < bno055_acc_range.len; i++)
+		len += sysfs_emit_at(buf, len, "%d ", bno055_acc_range.vals[i]);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+
+static ssize_t bno055_fusion_enable_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+
+	return sysfs_emit(buf, "%d\n",
+			  priv->operation_mode != BNO055_OPR_MODE_AMG);
+}
+
+static ssize_t bno055_fusion_enable_store(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct bno055_priv *priv = iio_priv(indio_dev);
+	bool en;
+
+	if (indio_dev->active_scan_mask &&
+	    !bitmap_empty(indio_dev->active_scan_mask, _BNO055_SCAN_MAX))
+		return -EBUSY;
+
+	if (kstrtobool(buf, &en))
+		return -EINVAL;
+
+	if (!en)
+		return bno055_operation_mode_set(priv, BNO055_OPR_MODE_AMG) ?: len;
+
+	/*
+	 * 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)
+		return  bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION_FMC_OFF) ?: len;
+
+	return len;
+}
+
+static ssize_t bno055_fmc_enable_show(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+
+	return sysfs_emit(buf, "%d\n",
+			  priv->operation_mode == BNO055_OPR_MODE_FUSION);
+}
+
+static ssize_t bno055_fmc_enable_store(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct bno055_priv *priv = iio_priv(indio_dev);
+	int ret;
+
+	if (indio_dev->active_scan_mask &&
+	    !bitmap_empty(indio_dev->active_scan_mask, _BNO055_SCAN_MAX))
+		return -EBUSY;
+
+	if (sysfs_streq(buf, "0")) {
+		if (priv->operation_mode == BNO055_OPR_MODE_FUSION) {
+			ret = bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION_FMC_OFF);
+			if (ret)
+				return ret;
+		}
+	} else {
+		if (priv->operation_mode == BNO055_OPR_MODE_AMG)
+			return -EINVAL;
+
+		if (priv->operation_mode != BNO055_OPR_MODE_FUSION) {
+			ret = bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return len;
+}
+
+static ssize_t bno055_in_accel_range_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int val;
+	int ret;
+
+	ret = bno055_get_regmask(priv, &val, NULL,
+				 BNO055_ACC_CONFIG_REG,
+				 BNO055_ACC_CONFIG_RANGE_MASK,
+				 &bno055_acc_range);
+	if (ret < 0)
+		return ret;
+
+	return sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t bno055_in_accel_range_store(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf, size_t len)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	unsigned long val;
+	int ret;
+
+	ret = kstrtoul(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&priv->lock);
+	ret = bno055_set_regmask(priv, val, 0,
+				 BNO055_ACC_CONFIG_REG,
+				 BNO055_ACC_CONFIG_RANGE_MASK,
+				 &bno055_acc_range);
+	mutex_unlock(&priv->lock);
+
+	return ret ?: len;
+}
+
+static ssize_t bno055_get_calib_status(struct device *dev, char *buf, int which)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int calib;
+	int ret;
+	int val;
+
+	if (priv->operation_mode == BNO055_OPR_MODE_AMG ||
+	    (priv->operation_mode == BNO055_OPR_MODE_FUSION_FMC_OFF &&
+	     which == BNO055_CALIB_STAT_MAGN_SHIFT)) {
+		calib = 0;
+	} else {
+		mutex_lock(&priv->lock);
+		ret = regmap_read(priv->regmap, BNO055_CALIB_STAT_REG, &val);
+		mutex_unlock(&priv->lock);
+
+		if (ret)
+			return -EIO;
+
+		calib = ((val >> which) & BNO055_CALIB_STAT_MASK) + 1;
+	}
+
+	return sysfs_emit(buf, "%d\n", calib);
+}
+
+static ssize_t serialnumber_show(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+
+	return sysfs_emit(buf, "%*ph\n", BNO055_UID_LEN, priv->uid);
+}
+
+static ssize_t calibration_data_read(struct file *filp, struct kobject *kobj,
+				     struct bin_attribute *bin_attr, char *buf,
+				     loff_t pos, size_t count)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(kobj_to_dev(kobj)));
+	u8 data[BNO055_CALDATA_LEN];
+	int ret;
+
+	/*
+	 * Calibration data is volatile; reading it in chunks will possibly
+	 * results in inconsistent data. We require the user to read the whole
+	 * blob in a single chunk
+	 */
+	if (count < BNO055_CALDATA_LEN || pos)
+		return -EINVAL;
+
+	mutex_lock(&priv->lock);
+	ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+	if (ret)
+		goto exit_unlock;
+
+	ret = regmap_bulk_read(priv->regmap, BNO055_CALDATA_START, data,
+			       BNO055_CALDATA_LEN);
+	if (ret)
+		goto exit_unlock;
+
+	ret = bno055_operation_mode_do_set(priv, priv->operation_mode);
+	if (ret)
+		goto exit_unlock;
+
+	memcpy(buf, data, BNO055_CALDATA_LEN);
+
+	ret = BNO055_CALDATA_LEN;
+exit_unlock:
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static ssize_t sys_calibration_auto_status_show(struct device *dev,
+						struct device_attribute *a,
+						char *buf)
+{
+	return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_SYS_SHIFT);
+}
+
+static ssize_t in_accel_calibration_auto_status_show(struct device *dev,
+						     struct device_attribute *a,
+						     char *buf)
+{
+	return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_ACCEL_SHIFT);
+}
+
+static ssize_t in_gyro_calibration_auto_status_show(struct device *dev,
+						    struct device_attribute *a,
+						    char *buf)
+{
+	return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_GYRO_SHIFT);
+}
+
+static ssize_t in_magn_calibration_auto_status_show(struct device *dev,
+						    struct device_attribute *a,
+						    char *buf)
+{
+	return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_MAGN_SHIFT);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int bno055_debugfs_reg_access(struct iio_dev *iio_dev, unsigned int reg,
+				     unsigned int writeval, unsigned int *readval)
+{
+	struct bno055_priv *priv = iio_priv(iio_dev);
+
+	if (readval)
+		return regmap_read(priv->regmap, reg, readval);
+	else
+		return regmap_write(priv->regmap, reg, writeval);
+}
+
+static ssize_t bno055_show_fw_version(struct file *file, char __user *userbuf,
+				      size_t count, loff_t *ppos)
+{
+	struct bno055_priv *priv = file->private_data;
+	int rev, ver;
+	char *buf;
+	int ret;
+
+	ret = regmap_read(priv->regmap, BNO055_SW_REV_LSB_REG, &rev);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(priv->regmap, BNO055_SW_REV_MSB_REG, &ver);
+	if (ret)
+		return ret;
+
+	buf = kasprintf(GFP_KERNEL, "ver: 0x%x, rev: 0x%x\n", ver, rev);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf));
+	kfree(buf);
+
+	return ret;
+}
+
+static const struct file_operations bno055_fw_version_ops = {
+	.open = simple_open,
+	.read = bno055_show_fw_version,
+	.llseek = default_llseek,
+	.owner = THIS_MODULE,
+};
+
+static void bno055_debugfs_remove(void *debugfs)
+{
+	debugfs_remove((struct dentry *)debugfs);
+}
+
+static void bno055_debugfs_init(struct iio_dev *iio_dev)
+{
+	struct bno055_priv *priv = iio_priv(iio_dev);
+
+	priv->debugfs = debugfs_create_file("firmware_version", 0400,
+					    iio_get_debugfs_dentry(iio_dev),
+					    priv, &bno055_fw_version_ops);
+
+	devm_add_action_or_reset(priv->dev, bno055_debugfs_remove, priv->debugfs);
+}
+#else
+static void bno055_debugfs_init(struct iio_dev *iio_dev)
+{
+}
+
+static int bno055_debugfs_reg_access(struct iio_dev *iio_dev, unsigned int reg,
+				     unsigned int writeval, unsigned int *readval)
+{
+	return 0;
+}
+#endif
+
+static IIO_DEVICE_ATTR(fusion_enable, 0644,
+		       bno055_fusion_enable_show,
+		       bno055_fusion_enable_store, 0);
+
+static IIO_DEVICE_ATTR(in_magn_calibration_fast_enable, 0644,
+		       bno055_fmc_enable_show,
+		       bno055_fmc_enable_store, 0);
+
+static IIO_DEVICE_ATTR(in_accel_range_raw, 0644,
+		       bno055_in_accel_range_show,
+		       bno055_in_accel_range_store, 0);
+
+static IIO_DEVICE_ATTR_RO(in_accel_range_raw_available, 0);
+
+static IIO_DEVICE_ATTR_RO(sys_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_accel_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_gyro_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_magn_calibration_auto_status, 0);
+
+static IIO_DEVICE_ATTR_RO(serialnumber, 0);
+
+static struct attribute *bno055_attrs[] = {
+	&iio_dev_attr_in_accel_range_raw_available.dev_attr.attr,
+	&iio_dev_attr_in_accel_range_raw.dev_attr.attr,
+	&iio_dev_attr_fusion_enable.dev_attr.attr,
+	&iio_dev_attr_in_magn_calibration_fast_enable.dev_attr.attr,
+	&iio_dev_attr_sys_calibration_auto_status.dev_attr.attr,
+	&iio_dev_attr_in_accel_calibration_auto_status.dev_attr.attr,
+	&iio_dev_attr_in_gyro_calibration_auto_status.dev_attr.attr,
+	&iio_dev_attr_in_magn_calibration_auto_status.dev_attr.attr,
+	&iio_dev_attr_serialnumber.dev_attr.attr,
+	NULL
+};
+
+static BIN_ATTR_RO(calibration_data, BNO055_CALDATA_LEN);
+
+static struct bin_attribute *bno055_bin_attrs[] = {
+	&bin_attr_calibration_data,
+	NULL
+};
+
+static const struct attribute_group bno055_attrs_group = {
+	.attrs = bno055_attrs,
+	.bin_attrs = bno055_bin_attrs,
+};
+
+static const struct iio_info bno055_info = {
+	.read_raw_multi = bno055_read_raw_multi,
+	.read_avail = bno055_read_avail,
+	.write_raw = bno055_write_raw,
+	.attrs = &bno055_attrs_group,
+	.debugfs_reg_access = bno055_debugfs_reg_access,
+};
+
+/*
+ * Reads len samples from the HW, stores them in buf starting from buf_idx,
+ * and applies mask to cull (skip) unneeded samples.
+ * Updates buf_idx incrementing with the number of stored samples.
+ * Samples from HW are transferred into buf, then in-place copy on buf is
+ * performed in order to cull samples that need to be skipped.
+ * This avoids copies of the first samples until we hit the 1st sample to skip,
+ * and also avoids having an extra bounce buffer.
+ * buf must be able to contain len elements in spite of how many samples we are
+ * going to cull.
+ */
+static int bno055_scan_xfer(struct bno055_priv *priv,
+			    int start_ch, int len, unsigned long mask,
+			    __le16 *buf, int *buf_idx)
+{
+	const int base = BNO055_ACC_DATA_X_LSB_REG;
+	bool quat_in_read = false;
+	int buf_base = *buf_idx;
+	__le16 *dst, *src;
+	int offs_fixup = 0;
+	int xfer_len = len;
+	int ret;
+	int i, n;
+
+	if (!mask)
+		return 0;
+
+	/*
+	 * All chans are made up 1 16-bit sample, except for quaternion that is
+	 * made up 4 16-bit values.
+	 * For us the quaternion CH is just like 4 regular CHs.
+	 * If our read starts past the quaternion make sure to adjust the
+	 * starting offset; if the quaternion is contained in our scan then make
+	 * sure to adjust the read len.
+	 */
+	if (start_ch > BNO055_SCAN_QUATERNION) {
+		start_ch += 3;
+	} else if ((start_ch <= BNO055_SCAN_QUATERNION) &&
+		 ((start_ch + len) > BNO055_SCAN_QUATERNION)) {
+		quat_in_read = true;
+		xfer_len += 3;
+	}
+
+	ret = regmap_bulk_read(priv->regmap,
+			       base + start_ch * sizeof(__le16),
+			       buf + buf_base,
+			       xfer_len * sizeof(__le16));
+	if (ret)
+		return ret;
+
+	for_each_set_bit(i, &mask, len) {
+		if (quat_in_read && ((start_ch + i) > BNO055_SCAN_QUATERNION))
+			offs_fixup = 3;
+
+		dst = buf + *buf_idx;
+		src = buf + buf_base + offs_fixup + i;
+
+		n = (start_ch + i == BNO055_SCAN_QUATERNION) ? 4 : 1;
+
+		if (dst != src)
+			memcpy(dst, src, n * sizeof(__le16));
+
+		*buf_idx += n;
+	}
+	return 0;
+}
+
+static irqreturn_t bno055_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *iio_dev = pf->indio_dev;
+	struct bno055_priv *priv = iio_priv(iio_dev);
+	int xfer_start, start, end, prev_end;
+	unsigned long mask;
+	int quat_extra_len;
+	bool first = true;
+	int buf_idx = 0;
+	bool thr_hit;
+	int ret;
+
+	mutex_lock(&priv->lock);
+
+	/*
+	 * Walk the bitmap and eventually perform several transfers.
+	 * Bitmap ones-fields that are separated by gaps <= xfer_burst_break_thr
+	 * will be included in same transfer.
+	 * Every time the bitmap contains a gap wider than xfer_burst_break_thr
+	 * then we split the transfer, skipping the gap.
+	 */
+	for_each_set_bitrange(start, end, iio_dev->active_scan_mask,
+			      iio_dev->masklength) {
+		/*
+		 * First transfer will start from the beginning of the first
+		 * ones-field in the bitmap
+		 */
+		if (first) {
+			xfer_start = start;
+		} else {
+			/*
+			 * We found the next ones-field; check whether to
+			 * include it in * the current transfer or not (i.e.
+			 * let's perform the current * transfer and prepare for
+			 * another one).
+			 */
+
+			/*
+			 * In case the zeros-gap contains the quaternion bit,
+			 * then its length is actually 4 words instead of 1
+			 * (i.e. +3 wrt other channels).
+			 */
+			quat_extra_len = ((start > BNO055_SCAN_QUATERNION) &&
+					  (prev_end <= BNO055_SCAN_QUATERNION)) ? 3 : 0;
+
+			/* If the gap is wider than xfer_burst_break_thr then.. */
+			thr_hit = (start - prev_end + quat_extra_len) >
+				priv->xfer_burst_break_thr;
+
+			/*
+			 * .. transfer all the data up to the gap. Then set the
+			 * next transfer start index at right after the gap
+			 * (i.e. at the start of this ones-field).
+			 */
+			if (thr_hit) {
+				mask = *iio_dev->active_scan_mask >> xfer_start;
+				ret = bno055_scan_xfer(priv, xfer_start,
+						       prev_end - xfer_start,
+						       mask, priv->buf.chans, &buf_idx);
+				if (ret)
+					goto done;
+				xfer_start = start;
+			}
+		}
+		first = false;
+		prev_end = end;
+	}
+
+	/*
+	 * We finished walking the bitmap; no more gaps to check for. Just
+	 * perform the current transfer.
+	 */
+	mask = *iio_dev->active_scan_mask >> xfer_start;
+	ret = bno055_scan_xfer(priv, xfer_start,
+			       prev_end - xfer_start,
+			       mask, priv->buf.chans, &buf_idx);
+
+	iio_push_to_buffers_with_timestamp(iio_dev, &priv->buf, pf->timestamp);
+done:
+	mutex_unlock(&priv->lock);
+	iio_trigger_notify_done(iio_dev->trig);
+	return IRQ_HANDLED;
+}
+
+static int bno055_buffer_preenable(struct iio_dev *indio_dev)
+{
+	struct bno055_priv *priv = iio_priv(indio_dev);
+	const unsigned long fusion_mask =
+		BIT(BNO055_SCAN_YAW) |
+		BIT(BNO055_SCAN_ROLL) |
+		BIT(BNO055_SCAN_PITCH) |
+		BIT(BNO055_SCAN_QUATERNION) |
+		BIT(BNO055_SCAN_LIA_X) |
+		BIT(BNO055_SCAN_LIA_Y) |
+		BIT(BNO055_SCAN_LIA_Z) |
+		BIT(BNO055_SCAN_GRAVITY_X) |
+		BIT(BNO055_SCAN_GRAVITY_Y) |
+		BIT(BNO055_SCAN_GRAVITY_Z);
+
+	if (priv->operation_mode == BNO055_OPR_MODE_AMG &&
+	    bitmap_intersects(indio_dev->active_scan_mask, &fusion_mask,
+			      _BNO055_SCAN_MAX))
+		return -EBUSY;
+	return 0;
+}
+
+static const struct iio_buffer_setup_ops bno055_buffer_setup_ops = {
+	.preenable = bno055_buffer_preenable,
+};
+
+static void bno055_clk_disable(void *arg)
+{
+	clk_disable_unprepare(arg);
+}
+
+int bno055_probe(struct device *dev, struct regmap *regmap,
+		 int xfer_burst_break_thr, bool sw_reset)
+{
+	const struct firmware *caldata = NULL;
+	struct bno055_priv *priv;
+	struct iio_dev *iio_dev;
+	char *fw_name_buf;
+	unsigned int val;
+	int rev, ver;
+	int ret;
+
+	iio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+	if (!iio_dev)
+		return -ENOMEM;
+
+	iio_dev->name = "bno055";
+	priv = iio_priv(iio_dev);
+	mutex_init(&priv->lock);
+	priv->regmap = regmap;
+	priv->dev = dev;
+	priv->xfer_burst_break_thr = xfer_burst_break_thr;
+	priv->sw_reset = sw_reset;
+
+	priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(priv->reset_gpio))
+		return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO\n");
+
+	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\n");
+
+	ret = clk_prepare_enable(priv->clk);
+	if (ret)
+		return ret;
+
+	ret = devm_add_action_or_reset(dev, bno055_clk_disable, priv->clk);
+	if (ret)
+		return ret;
+
+	if (priv->reset_gpio) {
+		usleep_range(5000, 10000);
+		gpiod_set_value_cansleep(priv->reset_gpio, 1);
+		usleep_range(650000, 750000);
+	} else if (!sw_reset) {
+		dev_warn(dev, "No usable reset method; IMU may be unreliable\n");
+	}
+
+	ret = regmap_read(priv->regmap, BNO055_CHIP_ID_REG, &val);
+	if (ret)
+		return ret;
+
+	if (val != BNO055_CHIP_ID_MAGIC)
+		dev_warn(dev, "Unrecognized chip ID 0x%x\n", val);
+
+	/*
+	 * In case we haven't a HW reset pin, we can still reset the chip via
+	 * register write. This is probably nonsense in case we can't even
+	 * communicate with the chip or the chip isn't the one we expect (i.e.
+	 * we don't write to unknown chips), so we perform SW reset only after
+	 * chip magic ID check
+	 */
+	if (!priv->reset_gpio) {
+		ret = bno055_system_reset(priv);
+		if (ret)
+			return ret;
+	}
+
+	ret = regmap_read(priv->regmap, BNO055_SW_REV_LSB_REG, &rev);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(priv->regmap, BNO055_SW_REV_MSB_REG, &ver);
+	if (ret)
+		return ret;
+
+	/*
+	 * The stock FW version contains a bug (see comment at the beginning of
+	 * this file) that causes the anglvel scale to be changed depending on
+	 * the chip range setting. We workaround this, but we don't know what
+	 * other FW versions might do.
+	 */
+	if (ver != 0x3 || rev != 0x11)
+		dev_warn(dev, "Untested firmware version. Anglvel scale may not work as expected\n");
+
+	ret = regmap_bulk_read(priv->regmap, BNO055_UID_LOWER_REG,
+			       priv->uid, BNO055_UID_LEN);
+	if (ret)
+		return ret;
+
+	/* Sensor calibration data */
+	fw_name_buf = kasprintf(GFP_KERNEL,
+				BNO055_FW_UID_FMT,
+				BNO055_UID_LEN, priv->uid);
+	if (!fw_name_buf)
+		return -ENOMEM;
+
+	ret = request_firmware(&caldata, fw_name_buf, dev);
+	kfree(fw_name_buf);
+	if (ret)
+		ret = request_firmware(&caldata, BNO055_FW_GENERIC_NAME, dev);
+	if (ret) {
+		dev_notice(dev, "Calibration file load failed. See instruction in kernel Documentation/iio/bno055.rst\n");
+		ret = bno055_init(priv, NULL, 0);
+	} else {
+		ret = bno055_init(priv, caldata->data, caldata->size);
+		release_firmware(caldata);
+	}
+	if (ret)
+		return ret;
+
+	priv->operation_mode = BNO055_OPR_MODE_FUSION;
+	ret = bno055_operation_mode_do_set(priv, priv->operation_mode);
+	if (ret)
+		return ret;
+
+	ret = devm_add_action_or_reset(dev, bno055_uninit, priv);
+	if (ret)
+		return ret;
+
+	iio_dev->channels = bno055_channels;
+	iio_dev->num_channels = ARRAY_SIZE(bno055_channels);
+	iio_dev->info = &bno055_info;
+	iio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = devm_iio_triggered_buffer_setup(dev, iio_dev,
+					      iio_pollfunc_store_time,
+					      bno055_trigger_handler,
+					      &bno055_buffer_setup_ops);
+	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_NS_GPL(bno055_probe, IIO_BNO055);
+
+MODULE_AUTHOR("Andrea Merello <andrea.merello@iit.it>");
+MODULE_DESCRIPTION("Bosch BNO055 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/bno055/bno055.h b/drivers/iio/imu/bno055/bno055.h
new file mode 100644
index 000000000000..64f9fc95cebc
--- /dev/null
+++ b/drivers/iio/imu/bno055/bno055.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __BNO055_H__
+#define __BNO055_H__
+
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+struct device;
+int bno055_probe(struct device *dev, struct regmap *regmap,
+		 int xfer_burst_break_thr, bool sw_reset);
+extern const struct regmap_config bno055_regmap_config;
+
+#endif
-- 
2.17.1

[v6 02/14] iio: document linear acceleration modifiers

From: <hidden>
Date: 2022-06-14 12:35:30

From: Andrea Merello <redacted>

Introduce ABI documentation for new IIO modifiers used for reporting
"linear acceleration" measures.

Signed-off-by: Andrea Merello <redacted>
Reviewed-by: Andy Shevchenko <redacted>
---
 Documentation/ABI/testing/sysfs-bus-iio | 9 +++++++++
 1 file changed, 9 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 3e00d7f7ee22..88d012bbfe79 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -238,6 +238,15 @@ Description:
 		Has all of the equivalent parameters as per voltageY. Units
 		after application of scale and offset are m/s^2.
 
+What:		/sys/bus/iio/devices/iio:deviceX/in_accel_linear_x_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_accel_linear_y_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_accel_linear_z_raw
+KernelVersion:	5.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		As per in_accel_X_raw attributes, but minus the
+		acceleration due to gravity.
+
 What:		/sys/bus/iio/devices/iio:deviceX/in_gravity_x_raw
 What:		/sys/bus/iio/devices/iio:deviceX/in_gravity_y_raw
 What:		/sys/bus/iio/devices/iio:deviceX/in_gravity_z_raw
-- 
2.17.1

[v6 14/14] docs: iio: add documentation for BNO055 driver

From: <hidden>
Date: 2022-06-14 12:35:37

From: Andrea Merello <redacted>

The bno055 driver is rather complex and have some oddities and not-obvious
things that worth to document (e.g. calibration files).

Signed-off-by: Andrea Merello <redacted>
Reviewed-by: Andy Shevchenko <redacted>
---
 Documentation/iio/bno055.rst | 50 ++++++++++++++++++++++++++++++++++++
 Documentation/iio/index.rst  |  2 ++
 2 files changed, 52 insertions(+)
 create mode 100644 Documentation/iio/bno055.rst
diff --git a/Documentation/iio/bno055.rst b/Documentation/iio/bno055.rst
new file mode 100644
index 000000000000..af21376d7a25
--- /dev/null
+++ b/Documentation/iio/bno055.rst
@@ -0,0 +1,50 @@
+.. SPDX-License-Identifier: GPL-2.0
+==============================
+BNO055 driver
+==============================
+
+1. Overview
+===========
+
+This driver supports Bosch BNO055 IMUs (on both serial and I2C busses).
+
+Accelerometer, magnetometer and gyroscope measures are always provided.
+When "fusion_enable" sysfs attribute is set to 1, orientation (both Euler
+angles and quaternion), linear velocity and gravity vector are also
+provided, but some sensor settings (e.g. low pass filtering and range)
+became locked (the IMU firmware controls them).
+
+This driver supports also IIO buffers.
+
+2. Calibration
+==============
+
+The IMU continuously performs an autocalibration procedure if (and only if)
+operating in fusion mode. The magnetometer autocalibration can however be
+disabled writing 0 in the sysfs in_magn_calibration_fast_enable attribute.
+
+The driver provides access to autocalibration flags (i.e. you can known if
+the IMU has successfully autocalibrated) and to the calibration data blob.
+
+The user can save this blob in a firmware file (i.e. in /lib/firmware) that
+the driver looks for at probe time. If found, then the IMU is initialized
+with this calibration data. This saves the user from performing the
+calibration procedure every time (which consist of moving the IMU in
+various way).
+
+The driver looks for calibration data file using two different names: first
+a file whose name is suffixed with the IMU unique ID (exposed in sysfs as
+serial_number) is searched for; this is useful when there is more than one
+IMU instance. If this file is not found, then a "generic" calibration file
+is searched for (which can be used when only one IMU is present, without
+struggling with fancy names, that change on each device).
+
+Valid calibration file names would be e.g.
+ bno055-caldata-0e7c26a33541515120204a35342b04ff.dat
+ bno055-caldata.dat
+
+In non-fusion mode the IIO 'offset' attributes provide access to the
+offsets from calibration data (if any), so that the user can apply them to
+the accel, angvel and magn IIO attributes. In fusion mode they are not
+needed (the IMU firmware internally applies those corrections) and they
+read as zero.
diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst
index 58b7a4ebac51..1b7292c58cd0 100644
--- a/Documentation/iio/index.rst
+++ b/Documentation/iio/index.rst
@@ -10,3 +10,5 @@ Industrial I/O
    iio_configfs
 
    ep93xx_adc
+
+   bno055
-- 
2.17.1

[v6 06/14] iio: event_monitor: add pitch, yaw and roll modifiers

From: <hidden>
Date: 2022-06-14 12:35:43

From: Andrea Merello <redacted>

Following the introduction of pitch, yaw and roll IIO modifiers, update the
event_monitor tool accordingly.

Signed-off-by: Andrea Merello <redacted>
Reviewed-by: Andy Shevchenko <redacted>
---
 tools/iio/iio_event_monitor.c | 3 +++
 1 file changed, 3 insertions(+)
diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c
index 1fee44abb836..ee3f78c47655 100644
--- a/tools/iio/iio_event_monitor.c
+++ b/tools/iio/iio_event_monitor.c
@@ -125,6 +125,9 @@ static const char * const iio_modifier_names[] = {
 	[IIO_MOD_LINEAR_X] = "linear_x",
 	[IIO_MOD_LINEAR_Y] = "linear_y",
 	[IIO_MOD_LINEAR_Z] = "linear_z",
+	[IIO_MOD_PITCH] = "pitch",
+	[IIO_MOD_YAW] = "yaw",
+	[IIO_MOD_ROLL] = "roll",
 };
 
 static bool event_is_known(struct iio_event_data *event)
-- 
2.17.1

[v6 12/14] iio: imu: add BNO055 serdev driver

From: <hidden>
Date: 2022-06-14 12:35:48

From: Andrea Merello <redacted>

Add a serdev driver for communicating to a BNO055 IMU via serial bus, and
enable the BNO055 core driver to work in this scenario.

Signed-off-by: Andrea Merello <redacted>
---
 drivers/iio/imu/bno055/Kconfig            |  10 +
 drivers/iio/imu/bno055/Makefile           |   5 +
 drivers/iio/imu/bno055/bno055_ser_core.c  | 560 ++++++++++++++++++++++
 drivers/iio/imu/bno055/bno055_ser_trace.c |  13 +
 drivers/iio/imu/bno055/bno055_ser_trace.h | 104 ++++
 5 files changed, 692 insertions(+)
 create mode 100644 drivers/iio/imu/bno055/bno055_ser_core.c
 create mode 100644 drivers/iio/imu/bno055/bno055_ser_trace.c
 create mode 100644 drivers/iio/imu/bno055/bno055_ser_trace.h
diff --git a/drivers/iio/imu/bno055/Kconfig b/drivers/iio/imu/bno055/Kconfig
index d0ab3221fba5..d014b68cd43d 100644
--- a/drivers/iio/imu/bno055/Kconfig
+++ b/drivers/iio/imu/bno055/Kconfig
@@ -2,3 +2,13 @@
 
 config BOSCH_BNO055_IIO
 	tristate
+
+config BOSCH_BNO055_SERIAL
+	tristate "Bosch BNO055 attached via UART"
+	depends on SERIAL_DEV_BUS
+	select BOSCH_BNO055_IIO
+	help
+	  Enable this to support Bosch BNO055 IMUs attached via UART.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called bno055_sl.
diff --git a/drivers/iio/imu/bno055/Makefile b/drivers/iio/imu/bno055/Makefile
index 56cc4de60a7e..212307ce9c08 100644
--- a/drivers/iio/imu/bno055/Makefile
+++ b/drivers/iio/imu/bno055/Makefile
@@ -1,3 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0
 
 obj-$(CONFIG_BOSCH_BNO055_IIO) += bno055.o
+obj-$(CONFIG_BOSCH_BNO055_SERIAL) += bno055_ser.o
+bno055_ser-y := bno055_ser_core.o
+# define_trace.h needs to know how to find our header
+CFLAGS_bno055_ser_trace.o := -I$(src)
+bno055_ser-$(CONFIG_TRACING) += bno055_ser_trace.o
diff --git a/drivers/iio/imu/bno055/bno055_ser_core.c b/drivers/iio/imu/bno055/bno055_ser_core.c
new file mode 100644
index 000000000000..235131900260
--- /dev/null
+++ b/drivers/iio/imu/bno055/bno055_ser_core.c
@@ -0,0 +1,560 @@
+// 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-2022 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_ser_trace.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 |
+ * +------+----------+
+ *
+ * .. except when writing the SYS_RST bit (i.e. triggering a system reset); in
+ * case the IMU accepts the command, then it resets without responding. We don't
+ * handle this (yet) here (so we inform the common bno055 code not to perform
+ * sw resets - bno055 on serial bus basically requires the hw reset pin).
+ *
+ * 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).
+ *
+ * BNO055 has been seen also failing to process commands in case we send them
+ * too close each other (or if it is somehow busy?)
+ *
+ * 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.
+ *
+ * While we must avoid case 3, we could send 4 bytes per time and eventually
+ * retry in case of failure; this seemed convenient for reads (which requires
+ * TXing exactly 4 bytes), however it has been seen that, depending by the IMU
+ * settings (e.g. LPF), failures became less or more frequent; in certain IMU
+ * configurations they are very rare, but in certain others we keeps failing
+ * even after like 30 retries.
+ *
+ * So, we just split TXes in [2-bytes + delay] steps, and still keep an eye on
+ * the IMU response; in case it overflows (which is now unlikely), we retry.
+ */
+
+/*
+ * Read operation overhead:
+ *  4 bytes req + 2byte resp hdr.
+ *  6 bytes = 60 bit (considering 1start + 1stop bits).
+ *  60/115200 = ~520uS + about 2500mS dealay -> ~3mS
+ * In 3mS we could read back about 34 bytes that means 17 samples, this means
+ * that in case of scattered read in which the gap is 17 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. Because of this we
+ * round-up to 22, which is the max number of samples, always bursting indeed.
+ */
+#define BNO055_SER_XFER_BURST_BREAK_THRESHOLD 22
+
+struct bno055_ser_priv {
+	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_CRIT: The command failed: the serial communication failed.
+	 * @STATUS_OK:   The command executed successfully.
+	 * @STATUS_FAIL: The command failed: HW responded with an error.
+	 */
+	enum {
+		STATUS_CRIT = -1,
+		STATUS_OK = 0,
+		STATUS_FAIL = 1,
+	} cmd_status;
+
+	/*
+	 * Protects all the above fields, which are accessed in behalf of both
+	 * the serdev RX callback and the regmap side
+	 */
+	struct mutex lock;
+
+	/* Only accessed in serdev RX callback context*/
+	struct {
+		enum {
+			RX_IDLE,
+			RX_START,
+			RX_DATA,
+		} state;
+		int databuf_count;
+		int expected_len;
+		int type;
+	} rx;
+
+	/* Never accessed in behalf of serdev RX callback context */
+	bool cmd_stale;
+
+	struct completion cmd_complete;
+	struct serdev_device *serdev;
+};
+
+static int bno055_ser_send_chunk(struct bno055_ser_priv *priv, const u8 *data, int len)
+{
+	int ret;
+
+	trace_send_chunk(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_ser_do_send_cmd(struct bno055_ser_priv *priv,
+				  bool read, int addr, int len, const u8 *data)
+{
+	u8 hdr[] = {0xAA, read, addr, len};
+	int chunk_len;
+	int ret;
+
+	ret = bno055_ser_send_chunk(priv, hdr, 2);
+	if (ret)
+		goto fail;
+	usleep_range(2000, 3000);
+	ret = bno055_ser_send_chunk(priv, hdr + 2, 2);
+	if (ret)
+		goto fail;
+
+	if (read)
+		return 0;
+
+	while (len) {
+		chunk_len = min(len, 2);
+		usleep_range(2000, 3000);
+		ret = bno055_ser_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 bno055_ser_send_cmd(struct bno055_ser_priv *priv,
+			       bool read, int addr, int len, const u8 *data)
+{
+	const int retry_max = 5;
+	int retry = retry_max;
+	int ret = 0;
+
+	/*
+	 * In case previous command was interrupted we still need 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)
+			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.
+	 */
+	do {
+		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)
+			trace_cmd_retry(read, addr, retry_max - retry);
+		ret = bno055_ser_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;
+		}
+
+		if (!ret)
+			return -ETIMEDOUT;
+
+		if (priv->cmd_status == STATUS_OK)
+			return 0;
+		if (priv->cmd_status == STATUS_CRIT)
+			return -EIO;
+
+		/* loop in case priv->cmd_status == STATUS_FAIL */
+	} while (--retry);
+
+	if (ret < 0)
+		return ret;
+	if (priv->cmd_status == STATUS_FAIL)
+		return -EINVAL;
+	return 0;
+}
+
+static int bno055_ser_write_reg(void *context, const void *_data, size_t count)
+{
+	const u8 *data = _data;
+	struct bno055_ser_priv *priv = context;
+
+	if (count < 2) {
+		dev_err(&priv->serdev->dev, "Invalid write count %zu", count);
+		return -EINVAL;
+	}
+
+	trace_write_reg(data[0], data[1]);
+	return bno055_ser_send_cmd(priv, 0, data[0], count - 1, data + 1);
+}
+
+static int bno055_ser_read_reg(void *context,
+			       const void *_reg, size_t reg_size,
+			       void *val, size_t val_size)
+{
+	int ret;
+	int reg_addr;
+	const u8 *reg = _reg;
+	struct bno055_ser_priv *priv = context;
+
+	if (val_size > 128) {
+		dev_err(&priv->serdev->dev, "Invalid read valsize %zu", val_size);
+		return -EINVAL;
+	}
+
+	reg_addr = *reg;
+	trace_read_reg(reg_addr, val_size);
+	mutex_lock(&priv->lock);
+	priv->expected_data_len = val_size;
+	priv->response_buf = val;
+	mutex_unlock(&priv->lock);
+
+	ret = bno055_ser_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_ser_handle_rx(struct bno055_ser_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;
+	mutex_unlock(&priv->lock);
+	complete(&priv->cmd_complete);
+}
+
+/*
+ * Serdev receiver FSM. This tracks the serial communication and parse the
+ * header. It pushes packets to bno055_ser_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_ser_receive_buf(struct serdev_device *serdev,
+				  const unsigned char *buf, size_t size)
+{
+	int status;
+	struct bno055_ser_priv *priv = serdev_device_get_drvdata(serdev);
+	int remaining = size;
+
+	if (size == 0)
+		return 0;
+
+	trace_recv(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_ser_handle_rx(priv, STATUS_CRIT);
+			break;
+		}
+		priv->rx.type = buf[0];
+		priv->rx.state = RX_START;
+		remaining--;
+		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 (remaining == 0)
+			break;
+
+		if (priv->rx.type == 0xEE) {
+			if (remaining > 1) {
+				dev_err(&priv->serdev->dev, "EE pkt. Extra data received");
+				status = STATUS_CRIT;
+			} else {
+				status = (buf[0] == 1) ? STATUS_OK : STATUS_FAIL;
+			}
+			bno055_ser_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];
+			remaining--;
+			buf++;
+		}
+		fallthrough;
+
+	case RX_DATA:
+		/* Header parsed; now receiving packet data payload */
+		if (remaining == 0)
+			break;
+
+		if (priv->rx.databuf_count + remaining > priv->rx.expected_len) {
+			/*
+			 * This is an 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_ser_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 + remaining <= priv->expected_data_len))
+			memcpy(priv->response_buf + priv->rx.databuf_count,
+			       buf, remaining);
+		mutex_unlock(&priv->lock);
+
+		priv->rx.databuf_count += remaining;
+
+		/*
+		 * 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_ser_handle_rx(priv, STATUS_OK);
+			priv->rx.state = RX_IDLE;
+		}
+		break;
+	}
+
+	return size;
+}
+
+static const struct serdev_device_ops bno055_ser_serdev_ops = {
+	.receive_buf = bno055_ser_receive_buf,
+	.write_wakeup = serdev_device_write_wakeup,
+};
+
+static struct regmap_bus bno055_ser_regmap_bus = {
+	.write = bno055_ser_write_reg,
+	.read = bno055_ser_read_reg,
+};
+
+static int bno055_ser_probe(struct serdev_device *serdev)
+{
+	struct bno055_ser_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_ser_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_ser_regmap_bus,
+				  priv, &bno055_regmap_config);
+	if (IS_ERR(regmap))
+		return dev_err_probe(&serdev->dev, PTR_ERR(regmap),
+				     "Unable to init register map");
+
+	return bno055_probe(&serdev->dev, regmap,
+			    BNO055_SER_XFER_BURST_BREAK_THRESHOLD, false);
+}
+
+static const struct of_device_id bno055_ser_of_match[] = {
+	{ .compatible = "bosch,bno055" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, bno055_ser_of_match);
+
+static struct serdev_device_driver bno055_ser_driver = {
+	.driver = {
+		.name = "bno055-ser",
+		.of_match_table = bno055_ser_of_match,
+	},
+	.probe = bno055_ser_probe,
+};
+module_serdev_device_driver(bno055_ser_driver);
+
+MODULE_AUTHOR("Andrea Merello <andrea.merello@iit.it>");
+MODULE_DESCRIPTION("Bosch BNO055 serdev interface");
+MODULE_IMPORT_NS(IIO_BNO055);
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/bno055/bno055_ser_trace.c b/drivers/iio/imu/bno055/bno055_ser_trace.c
new file mode 100644
index 000000000000..9e5c65c70fd8
--- /dev/null
+++ b/drivers/iio/imu/bno055/bno055_ser_trace.c
@@ -0,0 +1,13 @@
+//SPDX-License-Identifier: GPL-2.0
+/*
+ * bno055_ser Trace Support
+ * Copyright (C) 2022 Istituto Italiano di Tecnologia
+ * Electronic Design Laboratory
+ *
+ * Based on:
+ *   Device core Trace Support
+ *   Copyright (C) 2021, Intel Corporation
+ */
+
+#define CREATE_TRACE_POINTS
+#include "bno055_ser_trace.h"
diff --git a/drivers/iio/imu/bno055/bno055_ser_trace.h b/drivers/iio/imu/bno055/bno055_ser_trace.h
new file mode 100644
index 000000000000..51d079b91f63
--- /dev/null
+++ b/drivers/iio/imu/bno055/bno055_ser_trace.h
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#if !defined(__BNO055_SERDEV_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ)
+#define __BNO055_SERDEV_TRACE_H__
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM bno055_ser
+
+TRACE_EVENT(send_chunk,
+	    TP_PROTO(int len, const u8 *data),
+	    TP_ARGS(len, data),
+	    TP_STRUCT__entry(
+		    __field(int, len)
+		    __dynamic_array(u8, chunk, len)
+	    ),
+	    TP_fast_assign(
+		    __entry->len = len;
+		    memcpy(__get_dynamic_array(chunk),
+			   data, __entry->len);
+	    ),
+	    TP_printk("len: %d, data: = %*ph",
+		      __entry->len, __entry->len, __get_dynamic_array(chunk)
+	    )
+);
+
+TRACE_EVENT(cmd_retry,
+	    TP_PROTO(bool read, int addr, int retry),
+	    TP_ARGS(read, addr, retry),
+	    TP_STRUCT__entry(
+		    __field(bool, read)
+		    __field(int, addr)
+		    __field(int, retry)
+	    ),
+	    TP_fast_assign(
+		    __entry->read = read;
+		    __entry->addr = addr;
+		    __entry->retry = retry;
+	    ),
+	    TP_printk("%s addr 0x%x retry #%d",
+		      __entry->read ? "read" : "write",
+		      __entry->addr, __entry->retry
+	    )
+);
+
+TRACE_EVENT(write_reg,
+	    TP_PROTO(u8 addr, u8 value),
+	    TP_ARGS(addr, value),
+	    TP_STRUCT__entry(
+		    __field(u8, addr)
+		    __field(u8, value)
+	    ),
+	    TP_fast_assign(
+		    __entry->addr = addr;
+		    __entry->value = value;
+	    ),
+	    TP_printk("reg 0x%x = 0x%x",
+		      __entry->addr, __entry->value
+	    )
+);
+
+TRACE_EVENT(read_reg,
+	    TP_PROTO(int addr, size_t len),
+	    TP_ARGS(addr, len),
+	    TP_STRUCT__entry(
+		    __field(int, addr)
+		    __field(size_t, len)
+	    ),
+	    TP_fast_assign(
+		    __entry->addr = addr;
+		    __entry->len = len;
+	    ),
+	    TP_printk("reg 0x%x (len %zu)",
+		      __entry->addr, __entry->len
+	    )
+);
+
+TRACE_EVENT(recv,
+	    TP_PROTO(size_t len, const unsigned char *buf),
+	    TP_ARGS(len, buf),
+	    TP_STRUCT__entry(
+		    __field(size_t, len)
+		    __dynamic_array(unsigned char, buf, len)
+	    ),
+	    TP_fast_assign(
+		    __entry->len = len;
+		    memcpy(__get_dynamic_array(buf),
+			   buf, __entry->len);
+	    ),
+	    TP_printk("len: %d, data: = %*ph",
+		      __entry->len, __entry->len, __get_dynamic_array(buf)
+	    )
+);
+
+#endif /* __BNO055_SERDEV_TRACE_H__ || TRACE_HEADER_MULTI_READ */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE bno055_ser_trace
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
-- 
2.17.1

Re: [v6 08/14] iio: imu: add Bosch Sensortec BNO055 core driver

From: Andrea Merello <hidden>
Date: 2022-06-14 12:35:51

Few inline comments, OK for the rest.
...
quoted
+                                /* G:   2,    4,    8,    16 */
Indentation of this comment is a bit off.
quoted
+static int bno055_acc_range_vals[] = {1962, 3924, 7848, 15696};
Perhaps split this to 4 lines and put the comment on top of the third line?
Not sure what you mean here, sorry. May you elaborate or provide an example, please?
...
quoted
+static void bno055_debugfs_init(struct iio_dev *iio_dev)
+{
+       struct bno055_priv *priv = iio_priv(iio_dev);
+
+       priv->debugfs = debugfs_create_file("firmware_version", 0400,
+                                           iio_get_debugfs_dentry(iio_dev),
+                                           priv, &bno055_fw_version_ops);
quoted
+       devm_add_action_or_reset(priv->dev, bno055_debugfs_remove, priv->debugfs);
Shouldn't we report the potential error here? It's not directly
related to debugfs, but something which is not directly related.
The error eventually comes out from something that has nothing to do with debugs per se (i.e. the devm stuff), but it will only affect debugfs indeed.

Assuming that we don't want to make the whole driver fail in case debugfs stuff fails (see last part of the comment above debugfs_create_file() implementation), and given that the devm_add_action_or_reset(), should indeed "reset" in case of failure (i.e. we should be in a clean situation anyway), I would say it should be OK not to propagate the error and let things go on.

However we can add a dev_warn() to report what happened.

Re: [v6 08/14] iio: imu: add Bosch Sensortec BNO055 core driver

From: Andrea Merello <hidden>
Date: 2022-06-14 12:36:03

    >
...
quoted
quoted
quoted
+static void bno055_debugfs_init(struct iio_dev *iio_dev)
+{
+       struct bno055_priv *priv = iio_priv(iio_dev);
+
+       priv->debugfs = debugfs_create_file("firmware_version", 0400,
+                                           iio_get_debugfs_dentry(iio_dev),
+                                           priv, &bno055_fw_version_ops);
quoted
+       devm_add_action_or_reset(priv->dev, bno055_debugfs_remove, priv->debugfs);
Shouldn't we report the potential error here? It's not directly
related to debugfs, but something which is not directly related.
The error eventually comes out from something that has nothing to do with debugs per se (i.e. the devm stuff), but it will only affect debugfs indeed.

Assuming that we don't want to make the whole driver fail in case debugfs stuff fails (see last part of the comment above debugfs_create_file() implementation), and given that the devm_add_action_or_reset(), should indeed "reset" in case of failure (i.e.  we should be in a clean situation anyway), I would say it should be OK not to propagate the error and let things go on.
As I said, it's not directly related to debugfs. Here is the resource
leak possible or bad things happen if you probe the driver, that fails
to add this call for removal, remove it, and try to insert again, in
such case the debugfs will be stale.
Hum, I would say this shouldn't ever happen: AFAICS devm_add_action_or_reset() is a wrapper around devm_add_action() and it's purpose is exactly to add a check for failure; devm_add_action_or_reset() immediately invokes the action handler in case devm_add_action() fails. IOW in case of failure to add the devm stuff, the debugfs file is removed immediately and it shouldn't cause any mess with next times probe()s; just the driver will go on without the debugfs file being here.

I think this is the point of using devm_add_action_or_reset() instead of dev_add_action()  indeed, or am I missing something?
quoted
However we can add a dev_warn() to report what happened.
Not sure if it would suffice, I leave it to Jonathan.

-- 
With Best Regards,
Andy Shevchenko

[v6 10/14] iio: document "serialnumber" sysfs attribute

From: <hidden>
Date: 2022-06-14 12:36:10

From: Andrea Merello <redacted>

Add ABI documentation for the new "serialnumber" sysfs attribute. The
first user is the bno055 IIO driver.

Signed-off-by: Andrea Merello <redacted>
Reviewed-by: Andy Shevchenko <redacted>
---
 Documentation/ABI/testing/sysfs-bus-iio | 7 +++++++
 1 file changed, 7 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 2f54f26d9e59..03e29517c1a4 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -2053,3 +2053,10 @@ Contact:	linux-iio@vger.kernel.org
 Description:
 		Raw (unscaled) euler angles readings. Units after
 		application of scale are deg.
+
+What:		/sys/bus/iio/devices/iio:deviceX/serialnumber
+KernelVersion:	5.20
+Contact:	linux-iio@vger.kernel.org
+Description:
+		An example format is 16-bytes, 2-digits-per-byte, HEX-string
+		representing the sensor unique ID number.
-- 
2.17.1

[v6 04/14] iio: add modifers for pitch, yaw, roll

From: <hidden>
Date: 2022-06-14 12:36:14

From: Andrea Merello <redacted>

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

Signed-off-by: Andrea Merello <redacted>
Reviewed-by: Andy Shevchenko <redacted>
---
 drivers/iio/industrialio-core.c | 3 +++
 include/uapi/linux/iio/types.h  | 3 +++
 2 files changed, 6 insertions(+)
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 9c0ee7755884..4d95d4df5e91 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -137,6 +137,9 @@ static const char * const iio_modifier_names[] = {
 	[IIO_MOD_LINEAR_X] = "linear_x",
 	[IIO_MOD_LINEAR_Y] = "linear_y",
 	[IIO_MOD_LINEAR_Z] = "linear_z",
+	[IIO_MOD_PITCH] = "pitch",
+	[IIO_MOD_YAW] = "yaw",
+	[IIO_MOD_ROLL] = "roll",
 };
 
 /* relies on pairs of these shared then separate */
diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h
index 0993f6b697fc..4853701ba70d 100644
--- a/include/uapi/linux/iio/types.h
+++ b/include/uapi/linux/iio/types.h
@@ -98,6 +98,9 @@ enum iio_modifier {
 	IIO_MOD_LINEAR_X,
 	IIO_MOD_LINEAR_Y,
 	IIO_MOD_LINEAR_Z,
+	IIO_MOD_PITCH,
+	IIO_MOD_YAW,
+	IIO_MOD_ROLL,
 };
 
 enum iio_event_type {
-- 
2.17.1

Re: [v6 08/14] iio: imu: add Bosch Sensortec BNO055 core driver

From: Andy Shevchenko <hidden>
Date: 2022-06-14 15:11:29

On Tue, Jun 14, 2022 at 2:15 PM Andrea Merello [off-list ref] wrote:

...
quoted
quoted
quoted
quoted
+       devm_add_action_or_reset(priv->dev, bno055_debugfs_remove, priv->debugfs);
Shouldn't we report the potential error here? It's not directly
related to debugfs, but something which is not directly related.
The error eventually comes out from something that has nothing to do with debugs per se (i.e. the devm stuff), but it will only affect debugfs indeed.

Assuming that we don't want to make the whole driver fail in case debugfs stuff fails (see last part of the comment above debugfs_create_file() implementation), and given that the devm_add_action_or_reset(), should indeed "reset" in case of failure (i.e.  we should be in a clean situation anyway), I would say it should be OK not to propagate the error and let things go on.
As I said, it's not directly related to debugfs. Here is the resource
leak possible or bad things happen if you probe the driver, that fails
to add this call for removal, remove it, and try to insert again, in
such case the debugfs will be stale.
Hum, I would say this shouldn't ever happen: AFAICS devm_add_action_or_reset() is a wrapper around devm_add_action() and it's purpose is exactly to add a check for failure; devm_add_action_or_reset() immediately invokes the action handler in case devm_add_action() fails. IOW in case of failure to add the devm stuff, the debugfs file is removed immediately and it shouldn't cause any mess with next times probe()s; just the driver will go on without the debugfs file being here.

I think this is the point of using devm_add_action_or_reset() instead of dev_add_action()  indeed, or am I missing something?
Reading that code again and I think you are right, so dev_warn() will
be sufficient to show that we fail. OTOH, what is the point of adding
a resource for the failed debugfs call?

-- 
With Best Regards,
Andy Shevchenko

Re: [v6 08/14] iio: imu: add Bosch Sensortec BNO055 core driver

From: Andrea Merello <hidden>
Date: 2022-06-14 15:28:11

Il giorno mar 14 giu 2022 alle ore 17:11 Andy Shevchenko
[off-list ref] ha scritto:
On Tue, Jun 14, 2022 at 2:15 PM Andrea Merello [off-list ref] wrote:

...
quoted
quoted
quoted
quoted
quoted
+       devm_add_action_or_reset(priv->dev, bno055_debugfs_remove, priv->debugfs);
Shouldn't we report the potential error here? It's not directly
related to debugfs, but something which is not directly related.
The error eventually comes out from something that has nothing to do with debugs per se (i.e. the devm stuff), but it will only affect debugfs indeed.

Assuming that we don't want to make the whole driver fail in case debugfs stuff fails (see last part of the comment above debugfs_create_file() implementation), and given that the devm_add_action_or_reset(), should indeed "reset" in case of failure (i.e.  we should be in a clean situation anyway), I would say it should be OK not to propagate the error and let things go on.
As I said, it's not directly related to debugfs. Here is the resource
leak possible or bad things happen if you probe the driver, that fails
to add this call for removal, remove it, and try to insert again, in
such case the debugfs will be stale.
Hum, I would say this shouldn't ever happen: AFAICS devm_add_action_or_reset() is a wrapper around devm_add_action() and it's purpose is exactly to add a check for failure; devm_add_action_or_reset() immediately invokes the action handler in case devm_add_action() fails. IOW in case of failure to add the devm stuff, the debugfs file is removed immediately and it shouldn't cause any mess with next times probe()s; just the driver will go on without the debugfs file being here.

I think this is the point of using devm_add_action_or_reset() instead of dev_add_action()  indeed, or am I missing something?
Reading that code again and I think you are right, so dev_warn() will
be sufficient to show that we fail. OTOH, what is the point of adding
a resource for the failed debugfs call?
Ah, you are right here: I'll make the call to
devm_add_action_or_reset() conditional to success of
debugfs_create_file(). In case any of the two fails we can also warn
the user.
--
With Best Regards,
Andy Shevchenko

Re: [v6 12/14] iio: imu: add BNO055 serdev driver

From: kernel test robot <hidden>
Date: 2022-06-15 20:57:37

Hi,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on jic23-iio/togreg]
[also build test WARNING on linus/master v5.19-rc2 next-20220615]
[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/intel-lab-lkp/linux/commits/andrea-merello-iit-it/Add-support-for-Bosch-BNO055-IMU/20220614-203754
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
config: sparc-allyesconfig (https://download.01.org/0day-ci/archive/20220616/202206160409.GTDk9b3k-lkp@intel.com/config)
compiler: sparc64-linux-gcc (GCC) 11.3.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/intel-lab-lkp/linux/commit/f37504a206ca4b342e184a1fc137f6c47f3960e9
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review andrea-merello-iit-it/Add-support-for-Bosch-BNO055-IMU/20220614-203754
        git checkout f37504a206ca4b342e184a1fc137f6c47f3960e9
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.3.0 make.cross W=1 O=build_dir ARCH=sparc SHELL=/bin/bash drivers/gpu/ drivers/iio/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <redacted>

All warnings (new ones prefixed by >>):

   In file included from include/trace/define_trace.h:102,
                    from drivers/iio/imu/bno055/bno055_ser_trace.h:104,
                    from drivers/iio/imu/bno055/bno055_ser_trace.c:13:
   drivers/iio/imu/bno055/./bno055_ser_trace.h: In function 'trace_raw_output_recv':
quoted
drivers/iio/imu/bno055/./bno055_ser_trace.h:91:23: warning: format '%d' expects argument of type 'int', but argument 3 has type 'size_t' {aka 'long unsigned int'} [-Wformat=]
      91 |             TP_printk("len: %d, data: = %*ph",
         |                       ^~~~~~~~~~~~~~~~~~~~~~~
   include/trace/trace_events.h:203:34: note: in definition of macro 'DECLARE_EVENT_CLASS'
     203 |         trace_event_printf(iter, print);                                \
         |                                  ^~~~~
   include/trace/trace_events.h:45:30: note: in expansion of macro 'PARAMS'
      45 |                              PARAMS(print));                   \
         |                              ^~~~~~
   drivers/iio/imu/bno055/./bno055_ser_trace.h:79:1: note: in expansion of macro 'TRACE_EVENT'
      79 | TRACE_EVENT(recv,
         | ^~~~~~~~~~~
   drivers/iio/imu/bno055/./bno055_ser_trace.h:91:13: note: in expansion of macro 'TP_printk'
      91 |             TP_printk("len: %d, data: = %*ph",
         |             ^~~~~~~~~
   In file included from include/trace/trace_events.h:237,
                    from include/trace/define_trace.h:102,
                    from drivers/iio/imu/bno055/bno055_ser_trace.h:104,
                    from drivers/iio/imu/bno055/bno055_ser_trace.c:13:
   drivers/iio/imu/bno055/./bno055_ser_trace.h:91:30: note: format string is defined here
      91 |             TP_printk("len: %d, data: = %*ph",
         |                             ~^
         |                              |
         |                              int
         |                             %ld
   In file included from include/trace/define_trace.h:102,
                    from drivers/iio/imu/bno055/bno055_ser_trace.h:104,
                    from drivers/iio/imu/bno055/bno055_ser_trace.c:13:
quoted
drivers/iio/imu/bno055/./bno055_ser_trace.h:91:23: warning: field width specifier '*' expects argument of type 'int', but argument 4 has type 'size_t' {aka 'long unsigned int'} [-Wformat=]
      91 |             TP_printk("len: %d, data: = %*ph",
         |                       ^~~~~~~~~~~~~~~~~~~~~~~
   include/trace/trace_events.h:203:34: note: in definition of macro 'DECLARE_EVENT_CLASS'
     203 |         trace_event_printf(iter, print);                                \
         |                                  ^~~~~
   include/trace/trace_events.h:45:30: note: in expansion of macro 'PARAMS'
      45 |                              PARAMS(print));                   \
         |                              ^~~~~~
   drivers/iio/imu/bno055/./bno055_ser_trace.h:79:1: note: in expansion of macro 'TRACE_EVENT'
      79 | TRACE_EVENT(recv,
         | ^~~~~~~~~~~
   drivers/iio/imu/bno055/./bno055_ser_trace.h:91:13: note: in expansion of macro 'TP_printk'
      91 |             TP_printk("len: %d, data: = %*ph",
         |             ^~~~~~~~~
   In file included from include/trace/trace_events.h:237,
                    from include/trace/define_trace.h:102,
                    from drivers/iio/imu/bno055/bno055_ser_trace.h:104,
                    from drivers/iio/imu/bno055/bno055_ser_trace.c:13:
   drivers/iio/imu/bno055/./bno055_ser_trace.h:91:42: note: format string is defined here
      91 |             TP_printk("len: %d, data: = %*ph",
         |                                         ~^~
         |                                          |
         |                                          int


vim +91 drivers/iio/imu/bno055/./bno055_ser_trace.h

    78	
    79	TRACE_EVENT(recv,
    80		    TP_PROTO(size_t len, const unsigned char *buf),
    81		    TP_ARGS(len, buf),
    82		    TP_STRUCT__entry(
    83			    __field(size_t, len)
    84			    __dynamic_array(unsigned char, buf, len)
    85		    ),
    86		    TP_fast_assign(
    87			    __entry->len = len;
    88			    memcpy(__get_dynamic_array(buf),
    89				   buf, __entry->len);
    90		    ),
  > 91		    TP_printk("len: %d, data: = %*ph",
    92			      __entry->len, __entry->len, __get_dynamic_array(buf)
    93		    )
    94	);
    95	

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

Re: [v6 12/14] iio: imu: add BNO055 serdev driver

From: Andy Shevchenko <hidden>
Date: 2022-06-15 21:14:38

On Wed, Jun 15, 2022 at 10:57 PM kernel test robot [off-list ref] wrote:

...
quoted
quoted
drivers/iio/imu/bno055/./bno055_ser_trace.h:91:23: warning: format '%d' expects argument of type 'int', but argument 3 has type 'size_t' {aka 'long unsigned int'} [-Wformat=]
    80              TP_PROTO(size_t len, const unsigned char *buf),
    81              TP_ARGS(len, buf),
    82              TP_STRUCT__entry(
    83                      __field(size_t, len)
    84                      __dynamic_array(unsigned char, buf, len)
    85              ),
    86              TP_fast_assign(
    87                      __entry->len = len;
    88                      memcpy(__get_dynamic_array(buf),
    89                             buf, __entry->len);
    90              ),
  > 91              TP_printk("len: %d, data: = %*ph",
Obviously it must be %zu
    92                        __entry->len, __entry->len, __get_dynamic_array(buf)
    93              )

-- 
With Best Regards,
Andy Shevchenko

Re: [v6 12/14] iio: imu: add BNO055 serdev driver

From: Andy Shevchenko <hidden>
Date: 2022-06-15 21:16:41

On Wed, Jun 15, 2022 at 11:13 PM Andy Shevchenko
[off-list ref] wrote:
On Wed, Jun 15, 2022 at 10:57 PM kernel test robot [off-list ref] wrote:

...
quoted
quoted
quoted
drivers/iio/imu/bno055/./bno055_ser_trace.h:91:23: warning: format '%d' expects argument of type 'int', but argument 3 has type 'size_t' {aka 'long unsigned int'} [-Wformat=]
quoted
    80              TP_PROTO(size_t len, const unsigned char *buf),
    81              TP_ARGS(len, buf),
    82              TP_STRUCT__entry(
    83                      __field(size_t, len)
    84                      __dynamic_array(unsigned char, buf, len)
    85              ),
    86              TP_fast_assign(
    87                      __entry->len = len;
    88                      memcpy(__get_dynamic_array(buf),
    89                             buf, __entry->len);
    90              ),
  > 91              TP_printk("len: %d, data: = %*ph",
Obviously it must be %zu
quoted
    92                        __entry->len, __entry->len, __get_dynamic_array(buf)
...and the second len here should be casted to (int) explicitly, since
* in the printf() format specifier means int argument.
quoted
    93              )
-- 
With Best Regards,
Andy Shevchenko

Re: [v6 08/14] iio: imu: add Bosch Sensortec BNO055 core driver

From: Jonathan Cameron <jic23@kernel.org>
Date: 2022-06-18 17:17:11

On Mon, 13 Jun 2022 14:05:28 +0200
[off-list ref] wrote:
From: Andrea Merello <redacted>

Add the 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 does 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 <redacted>
---
Hi Andrea,

A few trivial things from me this time seeing as you are going to be doing a v7.

Thanks,

Jonathan


quoted hunk
+
+static ssize_t bno055_get_calib_status(struct device *dev, char *buf, int which)
+{
+	struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+	int calib;
+	int ret;
+	int val;
+
+	if (priv->operation_mode == BNO055_OPR_MODE_AMG ||
+	    (priv->operation_mode == BNO055_OPR_MODE_FUSION_FMC_OFF &&
+	     which == BNO055_CALIB_STAT_MAGN_SHIFT)) {
+		calib = 0;
+	} else {
+		mutex_lock(&priv->lock);
+		ret = regmap_read(priv->regmap, BNO055_CALIB_STAT_REG, &val);
+		mutex_unlock(&priv->lock);
+
+		if (ret)
+			return -EIO;
+
+		calib = ((val >> which) & BNO055_CALIB_STAT_MASK) + 1;
Hmm. This is an unsual field get.  I wonder if it is good idea to
have what looks like a field mask in BNO055_CALIB_STAT_MASK but isn't
really because we are applying it to a shifted value.  Maybe we 
are better off just encoding it directly here as  GENMASK(1, 0)
quoted hunk
+	}
+
+	return sysfs_emit(buf, "%d\n", calib);
+}
+
+#ifdef CONFIG_DEBUG_FS
I'm not particularly keen on ifdef fun in c files. How bad is it if we
just don't bother and use __maybe_unused.  debug_fs_createfile is visible to
the compiler in !CONFIG_DEBUG_FS so it should manage to remove everything
except *_debugfs_reg_access() but that is trivial in size anyway.
quoted hunk
+static int bno055_debugfs_reg_access(struct iio_dev *iio_dev, unsigned int reg,
+				     unsigned int writeval, unsigned int *readval)
+{
+	struct bno055_priv *priv = iio_priv(iio_dev);
+
+	if (readval)
+		return regmap_read(priv->regmap, reg, readval);
+	else
+		return regmap_write(priv->regmap, reg, writeval);
+}
+
+static ssize_t bno055_show_fw_version(struct file *file, char __user *userbuf,
+				      size_t count, loff_t *ppos)
+{
+	struct bno055_priv *priv = file->private_data;
+	int rev, ver;
+	char *buf;
+	int ret;
+
+	ret = regmap_read(priv->regmap, BNO055_SW_REV_LSB_REG, &rev);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(priv->regmap, BNO055_SW_REV_MSB_REG, &ver);
+	if (ret)
+		return ret;
+
+	buf = kasprintf(GFP_KERNEL, "ver: 0x%x, rev: 0x%x\n", ver, rev);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf));
+	kfree(buf);
+
+	return ret;
+}
+
+static const struct file_operations bno055_fw_version_ops = {
+	.open = simple_open,
+	.read = bno055_show_fw_version,
+	.llseek = default_llseek,
+	.owner = THIS_MODULE,
+};
+
+static void bno055_debugfs_remove(void *debugfs)
+{
+	debugfs_remove((struct dentry *)debugfs);
+}
+
+static void bno055_debugfs_init(struct iio_dev *iio_dev)
+{
+	struct bno055_priv *priv = iio_priv(iio_dev);
+
+	priv->debugfs = debugfs_create_file("firmware_version", 0400,
+					    iio_get_debugfs_dentry(iio_dev),
+					    priv, &bno055_fw_version_ops);
+
+	devm_add_action_or_reset(priv->dev, bno055_debugfs_remove, priv->debugfs);
+}
+#else
+static void bno055_debugfs_init(struct iio_dev *iio_dev)
+{
+}
+
+static int bno055_debugfs_reg_access(struct iio_dev *iio_dev, unsigned int reg,
+				     unsigned int writeval, unsigned int *readval)
+{
+	return 0;
+}
+#endif
+
...

quoted hunk
+
+static irqreturn_t bno055_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *iio_dev = pf->indio_dev;
+	struct bno055_priv *priv = iio_priv(iio_dev);
+	int xfer_start, start, end, prev_end;
+	unsigned long mask;
+	int quat_extra_len;
+	bool first = true;
+	int buf_idx = 0;
+	bool thr_hit;
+	int ret;
+
+	mutex_lock(&priv->lock);
+
+	/*
+	 * Walk the bitmap and eventually perform several transfers.
+	 * Bitmap ones-fields that are separated by gaps <= xfer_burst_break_thr
+	 * will be included in same transfer.
+	 * Every time the bitmap contains a gap wider than xfer_burst_break_thr
+	 * then we split the transfer, skipping the gap.
+	 */
+	for_each_set_bitrange(start, end, iio_dev->active_scan_mask,
+			      iio_dev->masklength) {
+		/*
+		 * First transfer will start from the beginning of the first
+		 * ones-field in the bitmap
+		 */
+		if (first) {
+			xfer_start = start;
+		} else {
+			/*
+			 * We found the next ones-field; check whether to
+			 * include it in * the current transfer or not (i.e.
+			 * let's perform the current * transfer and prepare for
+			 * another one).
+			 */
+
+			/*
+			 * In case the zeros-gap contains the quaternion bit,
+			 * then its length is actually 4 words instead of 1
+			 * (i.e. +3 wrt other channels).
+			 */
+			quat_extra_len = ((start > BNO055_SCAN_QUATERNION) &&
you could reduce the scope of quat_extra_len and thr_hit, perhaps
to just within the for loop.  Perhaps it's not worth bothering though. Up to you.

quoted hunk
+					  (prev_end <= BNO055_SCAN_QUATERNION)) ? 3 : 0;
+
+			/* If the gap is wider than xfer_burst_break_thr then.. */
+			thr_hit = (start - prev_end + quat_extra_len) >
+				priv->xfer_burst_break_thr;
+
+			/*
+			 * .. transfer all the data up to the gap. Then set the
+			 * next transfer start index at right after the gap
+			 * (i.e. at the start of this ones-field).
+			 */
+			if (thr_hit) {
+				mask = *iio_dev->active_scan_mask >> xfer_start;
+				ret = bno055_scan_xfer(priv, xfer_start,
+						       prev_end - xfer_start,
+						       mask, priv->buf.chans, &buf_idx);
+				if (ret)
+					goto done;
+				xfer_start = start;
+			}
+		}
+		first = false;
+		prev_end = end;
+	}
+
+	/*
+	 * We finished walking the bitmap; no more gaps to check for. Just
+	 * perform the current transfer.
+	 */
+	mask = *iio_dev->active_scan_mask >> xfer_start;
+	ret = bno055_scan_xfer(priv, xfer_start,
+			       prev_end - xfer_start,
+			       mask, priv->buf.chans, &buf_idx);
Check ret and if it's an error don't push the data.
quoted hunk
+
+	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;
+}
+
quoted hunk
+int bno055_probe(struct device *dev, struct regmap *regmap,
+		 int xfer_burst_break_thr, bool sw_reset)
+{
...
quoted hunk
+
+	/*
+	 * The stock FW version contains a bug (see comment at the beginning of
+	 * this file) that causes the anglvel scale to be changed depending on
+	 * the chip range setting. We workaround this, but we don't know what
+	 * other FW versions might do.
+	 */
+	if (ver != 0x3 || rev != 0x11)
+		dev_warn(dev, "Untested firmware version. Anglvel scale may not work as expected\n");
+
+	ret = regmap_bulk_read(priv->regmap, BNO055_UID_LOWER_REG,
+			       priv->uid, BNO055_UID_LEN);
+	if (ret)
+		return ret;
+
+	/* Sensor calibration data */
+	fw_name_buf = kasprintf(GFP_KERNEL,
+				BNO055_FW_UID_FMT,
+				BNO055_UID_LEN, priv->uid);
No need to break this into so many lines.  Can get it on two without
passing 80 chars.

Re: [v6 12/14] iio: imu: add BNO055 serdev driver

From: kernel test robot <hidden>
Date: 2022-07-03 01:53:39

Hi,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on jic23-iio/togreg]
[also build test WARNING on linus/master v5.19-rc4 next-20220701]
[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/intel-lab-lkp/linux/commits/andrea-merello-iit-it/Add-support-for-Bosch-BNO055-IMU/20220614-203754
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
config: x86_64-allyesconfig (https://download.01.org/0day-ci/archive/20220703/202207030922.GDkeqL76-lkp@intel.com/config)
compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project bcd153485ebf07fe79e2b843ed5f1cb74997df1b)
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/intel-lab-lkp/linux/commit/f37504a206ca4b342e184a1fc137f6c47f3960e9
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review andrea-merello-iit-it/Add-support-for-Bosch-BNO055-IMU/20220614-203754
        git checkout f37504a206ca4b342e184a1fc137f6c47f3960e9
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash drivers/iio/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <redacted>

All warnings (new ones prefixed by >>):

   In file included from drivers/iio/imu/bno055/bno055_ser_trace.c:13:
   In file included from drivers/iio/imu/bno055/./bno055_ser_trace.h:104:
   In file included from include/trace/define_trace.h:102:
   In file included from include/trace/trace_events.h:237:
quoted
drivers/iio/imu/bno055/./bno055_ser_trace.h:92:9: warning: format specifies type 'int' but the argument has type 'size_t' (aka 'unsigned long') [-Wformat]
                         __entry->len, __entry->len, __get_dynamic_array(buf)
                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/trace/stages/stage3_trace_output.h:6:17: note: expanded from macro '__entry'
   #define __entry field
                   ^
   include/trace/stages/stage3_trace_output.h:9:43: note: expanded from macro 'TP_printk'
   #define TP_printk(fmt, args...) fmt "\n", args
                                   ~~~       ^
   include/trace/trace_events.h:45:16: note: expanded from macro 'TRACE_EVENT'
                                PARAMS(print));                   \
                                ~~~~~~~^~~~~~~
   include/linux/tracepoint.h:107:25: note: expanded from macro 'PARAMS'
   #define PARAMS(args...) args
                           ^~~~
   include/trace/trace_events.h:203:27: note: expanded from macro 'DECLARE_EVENT_CLASS'
           trace_event_printf(iter, print);                                \
                                    ^~~~~
   In file included from drivers/iio/imu/bno055/bno055_ser_trace.c:13:
   In file included from drivers/iio/imu/bno055/./bno055_ser_trace.h:104:
   In file included from include/trace/define_trace.h:102:
   In file included from include/trace/trace_events.h:237:
quoted
drivers/iio/imu/bno055/./bno055_ser_trace.h:91:36: warning: field width should have type 'int', but argument has type 'size_t' (aka 'unsigned long') [-Wformat]
               TP_printk("len: %d, data: = %*ph",
               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
   include/trace/stages/stage3_trace_output.h:9:33: note: expanded from macro 'TP_printk'
   #define TP_printk(fmt, args...) fmt "\n", args
                                   ^~~
   include/trace/trace_events.h:45:16: note: expanded from macro 'TRACE_EVENT'
                                PARAMS(print));                   \
                                ~~~~~~~^~~~~~~
   include/linux/tracepoint.h:107:25: note: expanded from macro 'PARAMS'
   #define PARAMS(args...) args
                           ^~~~
   include/trace/trace_events.h:203:27: note: expanded from macro 'DECLARE_EVENT_CLASS'
           trace_event_printf(iter, print);                                \
                                    ^~~~~
   2 warnings generated.


vim +92 drivers/iio/imu/bno055/./bno055_ser_trace.h

    78	
    79	TRACE_EVENT(recv,
    80		    TP_PROTO(size_t len, const unsigned char *buf),
    81		    TP_ARGS(len, buf),
    82		    TP_STRUCT__entry(
    83			    __field(size_t, len)
    84			    __dynamic_array(unsigned char, buf, len)
    85		    ),
    86		    TP_fast_assign(
    87			    __entry->len = len;
    88			    memcpy(__get_dynamic_array(buf),
    89				   buf, __entry->len);
    90		    ),
  > 91		    TP_printk("len: %d, data: = %*ph",
  > 92			      __entry->len, __entry->len, __get_dynamic_array(buf)
    93		    )
    94	);
    95	

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

Re: [v6 14/14] docs: iio: add documentation for BNO055 driver

From: kernel test robot <hidden>
Date: 2022-07-03 07:58:49

Hi,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on jic23-iio/togreg]
[also build test WARNING on linus/master v5.19-rc4 next-20220701]
[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/intel-lab-lkp/linux/commits/andrea-merello-iit-it/Add-support-for-Bosch-BNO055-IMU/20220614-203754
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
reproduce: make htmldocs

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <redacted>

All warnings (new ones prefixed by >>):
quoted
Documentation/iio/bno055.rst:2: WARNING: Explicit markup ends without a blank line; unexpected unindent.
vim +2 Documentation/iio/bno055.rst

   > 2	==============================
     3	BNO055 driver
     4	==============================
     5	

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

Re: [v6 14/14] docs: iio: add documentation for BNO055 driver

From: Bagas Sanjaya <hidden>
Date: 2022-07-03 13:11:24

On Sun, Jul 03, 2022 at 03:58:15PM +0800, kernel test robot wrote:
Hi,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on jic23-iio/togreg]
[also build test WARNING on linus/master v5.19-rc4 next-20220701]
[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/intel-lab-lkp/linux/commits/andrea-merello-iit-it/Add-support-for-Bosch-BNO055-IMU/20220614-203754
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
reproduce: make htmldocs

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <redacted>

All warnings (new ones prefixed by >>):
quoted
quoted
Documentation/iio/bno055.rst:2: WARNING: Explicit markup ends without a blank line; unexpected unindent.
vim +2 Documentation/iio/bno055.rst

   > 2	==============================
     3	BNO055 driver
     4	==============================
     5	
Here's the fixup:

---- >8 ----

From bb8524aa4719e54389065548c86155cbee638357 Mon Sep 17 00:00:00 2001
From: Bagas Sanjaya <redacted>
Date: Sun, 3 Jul 2022 18:37:44 +0700
Subject: [PATCH] fixup for "docs: iio: add documentation for BNO055 driver"

kernel test robot reported htmldocs warning:

Documentation/iio/bno055.rst:2: WARNING: Explicit markup ends without a blank line; unexpected unindent.

Add missing blank between SPDX line and the page title to fix the warning.

Link: https://lore.kernel.org/lkml/202207031509.DlBrHyaw-lkp@intel.com/
Reported-by: kernel test robot <redacted>
Cc: lars@metafoo.de
Cc: robh+dt@kernel.org
Cc: andy.shevchenko@gmail.com
Cc: matt.ranostay@konsulko.com
Cc: ardeleanalex@gmail.com
Cc: jacopo@jmondi.org
Cc: Andrea Merello <redacted>
Signed-off-by: Bagas Sanjaya <redacted>
---
 Documentation/iio/bno055.rst | 1 +
 1 file changed, 1 insertion(+)
diff --git a/Documentation/iio/bno055.rst b/Documentation/iio/bno055.rst
index af21376d7a2533..9a489a79d8f5a8 100644
--- a/Documentation/iio/bno055.rst
+++ b/Documentation/iio/bno055.rst
@@ -1,4 +1,5 @@
 .. SPDX-License-Identifier: GPL-2.0
+
 ==============================
 BNO055 driver
 ==============================

---- >8 ----
-- 
An old man doll... just what I always wanted! - Clara

Re: [v6 14/14] docs: iio: add documentation for BNO055 driver

From: Andy Shevchenko <hidden>
Date: 2022-07-03 14:00:45

On Sun, Jul 3, 2022 at 3:11 PM Bagas Sanjaya [off-list ref] wrote:
On Sun, Jul 03, 2022 at 03:58:15PM +0800, kernel test robot wrote:
Please, submit it properly.
You may add my Reviewed-by: Andy Shevchenko [off-list ref]

(I deliberately put it on a wrong line so no tools will catch it up
with improper commit message)
quoted hunk
From bb8524aa4719e54389065548c86155cbee638357 Mon Sep 17 00:00:00 2001
From: Bagas Sanjaya <redacted>
Date: Sun, 3 Jul 2022 18:37:44 +0700
Subject: [PATCH] fixup for "docs: iio: add documentation for BNO055 driver"

kernel test robot reported htmldocs warning:

Documentation/iio/bno055.rst:2: WARNING: Explicit markup ends without a blank line; unexpected unindent.

Add missing blank between SPDX line and the page title to fix the warning.

Link: https://lore.kernel.org/lkml/202207031509.DlBrHyaw-lkp@intel.com/
Reported-by: kernel test robot <redacted>
Cc: lars@metafoo.de
Cc: robh+dt@kernel.org
Cc: andy.shevchenko@gmail.com
Cc: matt.ranostay@konsulko.com
Cc: ardeleanalex@gmail.com
Cc: jacopo@jmondi.org
Cc: Andrea Merello <redacted>
Signed-off-by: Bagas Sanjaya <redacted>
---
 Documentation/iio/bno055.rst | 1 +
 1 file changed, 1 insertion(+)
diff --git a/Documentation/iio/bno055.rst b/Documentation/iio/bno055.rst
index af21376d7a2533..9a489a79d8f5a8 100644
--- a/Documentation/iio/bno055.rst
+++ b/Documentation/iio/bno055.rst
@@ -1,4 +1,5 @@
 .. SPDX-License-Identifier: GPL-2.0
+
 ==============================
 BNO055 driver
 ==============================
-- 
With Best Regards,
Andy Shevchenko

Re: [v6 14/14] docs: iio: add documentation for BNO055 driver

From: Bagas Sanjaya <hidden>
Date: 2022-07-04 01:21:43

On 7/3/22 21:00, Andy Shevchenko wrote:
On Sun, Jul 3, 2022 at 3:11 PM Bagas Sanjaya [off-list ref] wrote:
quoted
On Sun, Jul 03, 2022 at 03:58:15PM +0800, kernel test robot wrote:
Please, submit it properly.
You may add my Reviewed-by: Andy Shevchenko [off-list ref]

(I deliberately put it on a wrong line so no tools will catch it up
with improper commit message)
OK, thanks.

-- 
An old man doll... just what I always wanted! - Clara

[PATCH v2] Documentation: bno055: separate SPDX identifier and page title

From: Bagas Sanjaya <hidden>
Date: 2022-07-04 03:41:29

kernel test robot reported htmldocs warning:

Documentation/iio/bno055.rst:2: WARNING: Explicit markup ends without a blank line; unexpected unindent.

The warning above is caused by missing blank line separator between SPDX
identifier and page title.

Add the blank line to fix the warning.

Link: https://lore.kernel.org/lkml/202207031509.DlBrHyaw-lkp@intel.com/
Fixes: ec0c70cb45507d ("docs: iio: add documentation for BNO055 driver")
Reported-by: kernel test robot <redacted>
Reviewed-by: Andy Shevchenko <redacted>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Andrea Merello <redacted>
Cc: Jonathan Cameron <jic23@kernel.org>
Cc: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Cc: Lars-Peter Clausen <lars@metafoo.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Matt Ranostay <matt.ranostay@konsulko.com>
Cc: Alexandru Ardelean <redacted>
Cc: jacopo@jmondi.org
Cc: linux-iio@vger.kernel.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org (open list)
Signed-off-by: Bagas Sanjaya <redacted>
---

 Changes since v1 [1]:
   - Collect Reviewed-by from Andy Shevchenko
   - Explain why the warning is triggered
   - No code changes 

 This patch is based on "Add support for Bosch BNO055 IMU" series [2]
 as fixup for BNO055 documentation (patch [14/14])

 [1]: https://lore.kernel.org/lkml/YsGVa8KFmdvGY92e@debian.me/
 [2]: https://lore.kernel.org/lkml/20220613120534.36991-1-andrea.merello@iit.it/ 
 Documentation/iio/bno055.rst | 1 +
 1 file changed, 1 insertion(+)
diff --git a/Documentation/iio/bno055.rst b/Documentation/iio/bno055.rst
index af21376d7a2533..9a489a79d8f5a8 100644
--- a/Documentation/iio/bno055.rst
+++ b/Documentation/iio/bno055.rst
@@ -1,4 +1,5 @@
 .. SPDX-License-Identifier: GPL-2.0
+
 ==============================
 BNO055 driver
 ==============================
-- 
An old man doll... just what I always wanted! - Clara

Re: [PATCH v2] Documentation: bno055: separate SPDX identifier and page title

From: Andy Shevchenko <hidden>
Date: 2022-07-04 19:50:39

On Mon, Jul 4, 2022 at 5:41 AM Bagas Sanjaya [off-list ref] wrote:
kernel test robot reported htmldocs warning:

Documentation/iio/bno055.rst:2: WARNING: Explicit markup ends without a blank line; unexpected unindent.

The warning above is caused by missing blank line separator between SPDX
identifier and page title.

Add the blank line to fix the warning.

Link: https://lore.kernel.org/lkml/202207031509.DlBrHyaw-lkp@intel.com/
Fixes: ec0c70cb45507d ("docs: iio: add documentation for BNO055 driver")
Reported-by: kernel test robot <redacted>
Reviewed-by: Andy Shevchenko <redacted>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Andrea Merello <redacted>
Cc: Jonathan Cameron <jic23@kernel.org>
Cc: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Cc: Lars-Peter Clausen <lars@metafoo.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Matt Ranostay <matt.ranostay@konsulko.com>
Cc: Alexandru Ardelean <redacted>
Cc: jacopo@jmondi.org
Cc: linux-iio@vger.kernel.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org (open list)
It's a very noisy Cc list which will go in the git history. Instead,
use --to and --cc parameters of `git format-patch`. Maintainers
usually use `b4` tool that adds a Link tag to the patch itself on the
Lore archive which will keep track on the Cc list anyway.

-- 
With Best Regards,
Andy Shevchenko

Re: [PATCH v2] Documentation: bno055: separate SPDX identifier and page title

From: Bagas Sanjaya <hidden>
Date: 2022-07-05 01:13:42

On 7/5/22 02:49, Andy Shevchenko wrote:
quoted
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Andrea Merello <redacted>
Cc: Jonathan Cameron <jic23@kernel.org>
Cc: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Cc: Lars-Peter Clausen <lars@metafoo.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Matt Ranostay <matt.ranostay@konsulko.com>
Cc: Alexandru Ardelean <redacted>
Cc: jacopo@jmondi.org
Cc: linux-iio@vger.kernel.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org (open list)
It's a very noisy Cc list which will go in the git history. Instead,
use --to and --cc parameters of `git format-patch`. Maintainers
usually use `b4` tool that adds a Link tag to the patch itself on the
Lore archive which will keep track on the Cc list anyway.
Hi Andy,

Thanks for reminding me.

I think something like `b4 am -l`, right?

Anyway, should I resend (reroll)?

-- 
An old man doll... just what I always wanted! - Clara

Re: [PATCH v2] Documentation: bno055: separate SPDX identifier and page title

From: Andy Shevchenko <hidden>
Date: 2022-07-05 09:03:30

On Tue, Jul 5, 2022 at 3:13 AM Bagas Sanjaya [off-list ref] wrote:
On 7/5/22 02:49, Andy Shevchenko wrote:
...
quoted
quoted
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Andrea Merello <redacted>
Cc: Jonathan Cameron <jic23@kernel.org>
Cc: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Cc: Lars-Peter Clausen <lars@metafoo.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Matt Ranostay <matt.ranostay@konsulko.com>
Cc: Alexandru Ardelean <redacted>
Cc: jacopo@jmondi.org
Cc: linux-iio@vger.kernel.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org (open list)
It's a very noisy Cc list which will go in the git history. Instead,
use --to and --cc parameters of `git format-patch`. Maintainers
usually use `b4` tool that adds a Link tag to the patch itself on the
Lore archive which will keep track on the Cc list anyway.
Thanks for reminding me.

I think something like `b4 am -l`, right?

Anyway, should I resend (reroll)?
Depends on the maintainer's wishes. Maybe they can drop them when
applying, I dunno.

-- 
With Best Regards,
Andy Shevchenko

Re: [PATCH v2] Documentation: bno055: separate SPDX identifier and page title

From: Jonathan Cameron <jic23@kernel.org>
Date: 2022-07-13 16:00:15

On Tue, 5 Jul 2022 11:02:32 +0200
Andy Shevchenko [off-list ref] wrote:
On Tue, Jul 5, 2022 at 3:13 AM Bagas Sanjaya [off-list ref] wrote:
quoted
On 7/5/22 02:49, Andy Shevchenko wrote:  
...
quoted
quoted
quoted
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Andrea Merello <redacted>
Cc: Jonathan Cameron <jic23@kernel.org>
Cc: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Cc: Lars-Peter Clausen <lars@metafoo.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Matt Ranostay <matt.ranostay@konsulko.com>
Cc: Alexandru Ardelean <redacted>
Cc: jacopo@jmondi.org
Cc: linux-iio@vger.kernel.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org (open list)  
It's a very noisy Cc list which will go in the git history. Instead,
use --to and --cc parameters of `git format-patch`. Maintainers
usually use `b4` tool that adds a Link tag to the patch itself on the
Lore archive which will keep track on the Cc list anyway.  
quoted
Thanks for reminding me.

I think something like `b4 am -l`, right?

Anyway, should I resend (reroll)?  
Depends on the maintainer's wishes. Maybe they can drop them when
applying, I dunno.
At the moment this is a fix to a series we haven't actually accepted.
Nice to have an all, but up to Andrea on how to handle it.
One reasonable option would be just to squish it into the original
patch for v7 with an appropriate note in the patch description / changelog.

Thanks,

Jonathan
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help