* [PATCH v2 0/8] iio: imu: new inv_icm45600 driver
@ 2025-07-10 8:57 Remi Buisson via B4 Relay
2025-07-10 8:57 ` [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600 Remi Buisson via B4 Relay
` (8 more replies)
0 siblings, 9 replies; 46+ messages in thread
From: Remi Buisson via B4 Relay @ 2025-07-10 8:57 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-kernel, linux-iio, devicetree, Remi Buisson
This series add a new driver for managing InvenSense ICM-456xx 6-axis IMUs.
This next generation of chips includes new generations of 3-axis gyroscope
and 3-axis accelerometer, support of I3C in addition to I2C and SPI, and
intelligent MotionTracking features like pedometer, tilt detection, and
tap detection.
This series is delivering a driver supporting gyroscope, accelerometer and
temperature data, with polling and buffering using hwfifo and watermark,
on I2C, SPI and I3C busses.
Gyroscope and accelerometer sensors are completely independent and can have
different ODRs. Since there is only a single FIFO a specific value is used to
mark invalid data. For keeping the device standard we are de-multiplexing data
from the FIFO to 2 IIO devices with 2 buffers, 1 for the accelerometer and 1
for the gyroscope. This architecture also enables to easily turn each sensor
on/off without impacting the other. The device interrupt is used to read the
FIFO and launch parsing of accelerometer and gyroscope data. This driver
relies on the common Invensense timestamping mechanism to handle correctly
FIFO watermark and dynamic changes of settings.
The structure of the driver is quite similar to the inv_icm42600 driver,
however there are significant reasons for adding a different driver for
inv_icm45600, such as:
- A completely different register map.
- Different FIFO management, based on number of samples instead of bytes.
- Different indirect register access mechanism.
Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
---
Changes in v2:
- Reworked patches order and content to ease review and make sure everything compiles
- Reworked gyro and accel FSR as 2D arrays
- Moved temperature processed sensor to core module
- Use latest API to claim/release device
- Implemented chip_info structure instead of relying on an enum
- Removed power-mode ABI, only relying on ODR to switch power_mode
- Reworked regulator control to use devm_ API where relevant
- Reworked inv_icm45600_state.buffer as a union to avoid casts, using getter/setter instead of memcpy
- Fixed dt-binding error and moved patch at the beginning of the patch-set
- Reworked macros to use FIELD_PREP inline instead of inside the header
- Fixed comment's grammar
- Removed extra blank lines
- Reordered part numbers alphanumerically
- Removed useless default/error fallbacks
- Typed accel, gyro and timestamp data when parsing FIFO
- Fixed I2C module return code
- Use Linux types instead of C standard
- Reviewed headers inclusion to remove useless #include and to add missing ones
- Link to v1: https://lore.kernel.org/r/20250411-add_newport_driver-v1-0-15082160b019@tdk.com
---
Remi Buisson (8):
dt-bindings: iio: imu: Add inv_icm45600
iio: imu: inv_icm45600: add new inv_icm45600 driver
iio: imu: inv_icm45600: add buffer support in iio devices
iio: imu: inv_icm45600: add IMU IIO devices
iio: imu: inv_icm45600: add I2C driver for inv_icm45600 driver
iio: imu: inv_icm45600: add SPI driver for inv_icm45600 driver
iio: imu: inv_icm45600: add I3C driver for inv_icm45600 driver
MAINTAINERS: add entry for inv_icm45600 6-axis imu sensor
.../bindings/iio/imu/invensense,icm45600.yaml | 138 +++
MAINTAINERS | 8 +
drivers/iio/imu/Kconfig | 1 +
drivers/iio/imu/Makefile | 1 +
drivers/iio/imu/inv_icm45600/Kconfig | 70 ++
drivers/iio/imu/inv_icm45600/Makefile | 16 +
drivers/iio/imu/inv_icm45600/inv_icm45600.h | 377 ++++++++
drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c | 798 +++++++++++++++++
drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c | 585 ++++++++++++
drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h | 101 +++
drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 991 +++++++++++++++++++++
drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c | 809 +++++++++++++++++
drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c | 98 ++
drivers/iio/imu/inv_icm45600/inv_icm45600_i3c.c | 82 ++
drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c | 106 +++
15 files changed, 4181 insertions(+)
---
base-commit: f8f559752d573a051a984adda8d2d1464f92f954
change-id: 20250411-add_newport_driver-529cf5b71ea8
Best regards,
--
Remi Buisson <remi.buisson@tdk.com>
^ permalink raw reply [flat|nested] 46+ messages in thread
* [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600
2025-07-10 8:57 [PATCH v2 0/8] iio: imu: new inv_icm45600 driver Remi Buisson via B4 Relay
@ 2025-07-10 8:57 ` Remi Buisson via B4 Relay
2025-07-10 22:41 ` Rob Herring
2025-07-14 5:38 ` Krzysztof Kozlowski
2025-07-10 8:57 ` [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver Remi Buisson via B4 Relay
` (7 subsequent siblings)
8 siblings, 2 replies; 46+ messages in thread
From: Remi Buisson via B4 Relay @ 2025-07-10 8:57 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-kernel, linux-iio, devicetree, Remi Buisson
From: Remi Buisson <remi.buisson@tdk.com>
Document the ICM-45600 devices devicetree bindings.
Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
---
.../bindings/iio/imu/invensense,icm45600.yaml | 138 +++++++++++++++++++++
1 file changed, 138 insertions(+)
diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm45600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm45600.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..a651878791ffae8d1c8d6c8ff1e4becfc56af79f
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm45600.yaml
@@ -0,0 +1,138 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/imu/invensense,icm45600.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: InvenSense ICM-45600 Inertial Measurement Unit
+
+maintainers:
+ - Remi Buisson <remi.buisson@tdk.com>
+
+description: |
+ 6-axis MotionTracking device that combines a 3-axis gyroscope and a 3-axis
+ accelerometer.
+
+ It has a configurable host interface that supports I3C, I2C and SPI serial
+ communication, features up to 8kB FIFO and 2 programmable interrupts with
+ ultra-low-power wake-on-motion support to minimize system power consumption.
+
+ Other industry-leading features include InvenSense on-chip APEX Motion
+ Processing engine for gesture recognition, activity classification, and
+ pedometer, along with programmable digital filters, and an embedded
+ temperature sensor.
+
+ https://invensense.tdk.com/wp-content/uploads/documentation/DS-000576_ICM-45605.pdf
+
+properties:
+ compatible:
+ enum:
+ - invensense,icm45605
+ - invensense,icm45606
+ - invensense,icm45608
+ - invensense,icm45634
+ - invensense,icm45686
+ - invensense,icm45687
+ - invensense,icm45688p
+ - invensense,icm45689
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ minItems: 1
+ maxItems: 2
+
+ interrupt-names:
+ minItems: 1
+ maxItems: 2
+ items:
+ enum:
+ - INT1
+ - INT2
+ description: Choose chip interrupt pin to be used as interrupt input.
+
+ drive-open-drain:
+ type: boolean
+
+ vdd-supply:
+ description: Regulator that provides power to the sensor
+
+ vddio-supply:
+ description: Regulator that provides power to the bus
+
+ mount-matrix:
+ description: an optional 3x3 mounting rotation matrix
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - interrupt-names
+
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ icm45605@68 {
+ compatible = "invensense,icm45605";
+ reg = <0x68>;
+ interrupt-parent = <&gpio2>;
+ interrupt-names = "INT1";
+ interrupts = <7 IRQ_TYPE_EDGE_RISING>;
+ vdd-supply = <&vdd>;
+ vddio-supply = <&vddio>;
+ mount-matrix = "0", "-1", "0",
+ "1", "0", "0",
+ "0", "0", "1";
+ };
+ };
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ icm45605@0 {
+ compatible = "invensense,icm45605";
+ reg = <0>;
+ spi-max-frequency = <24000000>;
+ interrupt-parent = <&gpio1>;
+ interrupt-names = "INT1";
+ interrupts = <6 IRQ_TYPE_EDGE_RISING>;
+ vdd-supply = <&vdd>;
+ vddio-supply = <&vddio>;
+ mount-matrix = "0", "-1", "0",
+ "1", "0", "0",
+ "0", "0", "1";
+ };
+ };
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ i3c {
+ #address-cells = <3>;
+ #size-cells = <0>;
+
+ icm45606@68,46A00000084 {
+ reg = <0x68 0x46A 0x84>;
+ interrupt-parent = <&gpio1>;
+ interrupt-names = "INT1";
+ interrupts = <5 IRQ_TYPE_EDGE_RISING>;
+ vdd-supply = <&vdd>;
+ vddio-supply = <&vddio>;
+ mount-matrix = "0", "-1", "0",
+ "1", "0", "0",
+ "0", "0", "1";
+ };
+ };
--
2.34.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver
2025-07-10 8:57 [PATCH v2 0/8] iio: imu: new inv_icm45600 driver Remi Buisson via B4 Relay
2025-07-10 8:57 ` [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600 Remi Buisson via B4 Relay
@ 2025-07-10 8:57 ` Remi Buisson via B4 Relay
2025-07-10 9:29 ` Andy Shevchenko
` (2 more replies)
2025-07-10 8:57 ` [PATCH v2 3/8] iio: imu: inv_icm45600: add buffer support in iio devices Remi Buisson via B4 Relay
` (6 subsequent siblings)
8 siblings, 3 replies; 46+ messages in thread
From: Remi Buisson via B4 Relay @ 2025-07-10 8:57 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-kernel, linux-iio, devicetree, Remi Buisson
From: Remi Buisson <remi.buisson@tdk.com>
Core component of a new driver for InvenSense ICM-45600 devices.
It includes registers definition, main probe/setup, and device
utility functions.
ICM-456xx devices are latest generation of 6-axis IMU,
gyroscope+accelerometer and temperature sensor. This device
includes a 8K FIFO, supports I2C/I3C/SPI, and provides
intelligent motion features like pedometer, tilt detection,
and tap detection.
Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
---
drivers/iio/imu/Kconfig | 1 +
drivers/iio/imu/Makefile | 1 +
drivers/iio/imu/inv_icm45600/Kconfig | 5 +
drivers/iio/imu/inv_icm45600/Makefile | 4 +
drivers/iio/imu/inv_icm45600/inv_icm45600.h | 364 +++++++++++
drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 734 +++++++++++++++++++++++
6 files changed, 1109 insertions(+)
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
index 15612f0f189b5114deb414ef840339678abdc562..9d732bed9fcdac12a13713dba3455c1fdf9f4a53 100644
--- a/drivers/iio/imu/Kconfig
+++ b/drivers/iio/imu/Kconfig
@@ -109,6 +109,7 @@ config KMX61
be called kmx61.
source "drivers/iio/imu/inv_icm42600/Kconfig"
+source "drivers/iio/imu/inv_icm45600/Kconfig"
source "drivers/iio/imu/inv_mpu6050/Kconfig"
config SMI240
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
index e901aea498d37e5897e8b71268356a19eac2cb59..2ae6344f84699b2f85fff1c8077cb412f6ae2658 100644
--- a/drivers/iio/imu/Makefile
+++ b/drivers/iio/imu/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_FXOS8700_I2C) += fxos8700_i2c.o
obj-$(CONFIG_FXOS8700_SPI) += fxos8700_spi.o
obj-y += inv_icm42600/
+obj-y += inv_icm45600/
obj-y += inv_mpu6050/
obj-$(CONFIG_KMX61) += kmx61.o
diff --git a/drivers/iio/imu/inv_icm45600/Kconfig b/drivers/iio/imu/inv_icm45600/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..8cb5543e0a5817323ab7b2d520dd3430ac5dbc99
--- /dev/null
+++ b/drivers/iio/imu/inv_icm45600/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+config INV_ICM45600
+ tristate
+ select IIO_INV_SENSORS_TIMESTAMP
diff --git a/drivers/iio/imu/inv_icm45600/Makefile b/drivers/iio/imu/inv_icm45600/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..4f442b61896e91647c7947a044949792bae06a30
--- /dev/null
+++ b/drivers/iio/imu/inv_icm45600/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+obj-$(CONFIG_INV_ICM45600) += inv-icm45600.o
+inv-icm45600-y += inv_icm45600_core.o
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600.h b/drivers/iio/imu/inv_icm45600/inv_icm45600.h
new file mode 100644
index 0000000000000000000000000000000000000000..d56de75ab7f2168f22e25b5816cb361bef457c0d
--- /dev/null
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h
@@ -0,0 +1,364 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2025 Invensense, Inc. */
+
+#ifndef INV_ICM45600_H_
+#define INV_ICM45600_H_
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/iio/common/inv_sensors_timestamp.h>
+#include <linux/iio/iio.h>
+#include <linux/types.h>
+
+#define INV_ICM45600_REG_BANK_MASK GENMASK(15, 8)
+#define INV_ICM45600_REG_ADDR_MASK GENMASK(7, 0)
+
+enum inv_icm45600_sensor_mode {
+ INV_ICM45600_SENSOR_MODE_OFF,
+ INV_ICM45600_SENSOR_MODE_STANDBY,
+ INV_ICM45600_SENSOR_MODE_LOW_POWER,
+ INV_ICM45600_SENSOR_MODE_LOW_NOISE,
+ INV_ICM45600_SENSOR_MODE_NB
+};
+
+/* gyroscope fullscale values */
+enum inv_icm45600_gyro_fs {
+ INV_ICM45600_GYRO_FS_2000DPS,
+ INV_ICM45600_GYRO_FS_1000DPS,
+ INV_ICM45600_GYRO_FS_500DPS,
+ INV_ICM45600_GYRO_FS_250DPS,
+ INV_ICM45600_GYRO_FS_125DPS,
+ INV_ICM45600_GYRO_FS_62_5DPS,
+ INV_ICM45600_GYRO_FS_31_25DPS,
+ INV_ICM45600_GYRO_FS_15_625DPS,
+ INV_ICM45600_GYRO_FS_NB
+};
+
+enum inv_icm45686_gyro_fs {
+ INV_ICM45686_GYRO_FS_4000DPS,
+ INV_ICM45686_GYRO_FS_2000DPS,
+ INV_ICM45686_GYRO_FS_1000DPS,
+ INV_ICM45686_GYRO_FS_500DPS,
+ INV_ICM45686_GYRO_FS_250DPS,
+ INV_ICM45686_GYRO_FS_125DPS,
+ INV_ICM45686_GYRO_FS_62_5DPS,
+ INV_ICM45686_GYRO_FS_31_25DPS,
+ INV_ICM45686_GYRO_FS_15_625DPS,
+ INV_ICM45686_GYRO_FS_NB
+};
+
+/* accelerometer fullscale values */
+enum inv_icm45600_accel_fs {
+ INV_ICM45600_ACCEL_FS_16G,
+ INV_ICM45600_ACCEL_FS_8G,
+ INV_ICM45600_ACCEL_FS_4G,
+ INV_ICM45600_ACCEL_FS_2G,
+ INV_ICM45600_ACCEL_FS_NB
+};
+
+enum inv_icm45686_accel_fs {
+ INV_ICM45686_ACCEL_FS_32G,
+ INV_ICM45686_ACCEL_FS_16G,
+ INV_ICM45686_ACCEL_FS_8G,
+ INV_ICM45686_ACCEL_FS_4G,
+ INV_ICM45686_ACCEL_FS_2G,
+ INV_ICM45686_ACCEL_FS_NB
+};
+
+/* ODR suffixed by LN or LP are Low-Noise or Low-Power mode only */
+enum inv_icm45600_odr {
+ INV_ICM45600_ODR_6400HZ_LN = 0x03,
+ INV_ICM45600_ODR_3200HZ_LN,
+ INV_ICM45600_ODR_1600HZ_LN,
+ INV_ICM45600_ODR_800HZ_LN,
+ INV_ICM45600_ODR_400HZ,
+ INV_ICM45600_ODR_200HZ,
+ INV_ICM45600_ODR_100HZ,
+ INV_ICM45600_ODR_50HZ,
+ INV_ICM45600_ODR_25HZ,
+ INV_ICM45600_ODR_12_5HZ,
+ INV_ICM45600_ODR_6_25HZ_LP,
+ INV_ICM45600_ODR_3_125HZ_LP,
+ INV_ICM45600_ODR_1_5625HZ_LP,
+ INV_ICM45600_ODR_NB
+};
+
+struct inv_icm45600_sensor_conf {
+ int mode;
+ int fs;
+ int odr;
+ int filter;
+};
+#define INV_ICM45600_SENSOR_CONF_INIT {-1, -1, -1, -1}
+
+struct inv_icm45600_conf {
+ struct inv_icm45600_sensor_conf gyro;
+ struct inv_icm45600_sensor_conf accel;
+};
+
+struct inv_icm45600_suspended {
+ enum inv_icm45600_sensor_mode gyro;
+ enum inv_icm45600_sensor_mode accel;
+};
+
+struct inv_icm45600_chip_info {
+ u8 whoami;
+ const char *name;
+ const struct inv_icm45600_conf *conf;
+};
+
+extern const struct inv_icm45600_chip_info inv_icm45605_chip_info;
+extern const struct inv_icm45600_chip_info inv_icm45606_chip_info;
+extern const struct inv_icm45600_chip_info inv_icm45608_chip_info;
+extern const struct inv_icm45600_chip_info inv_icm45634_chip_info;
+extern const struct inv_icm45600_chip_info inv_icm45686_chip_info;
+extern const struct inv_icm45600_chip_info inv_icm45687_chip_info;
+extern const struct inv_icm45600_chip_info inv_icm45688p_chip_info;
+extern const struct inv_icm45600_chip_info inv_icm45689_chip_info;
+
+/**
+ * struct inv_icm45600_state - driver state variables
+ * @lock: lock for serializing multiple registers access.
+ * @chip: chip identifier.
+ * @map: regmap pointer.
+ * @vddio_supply: I/O voltage regulator for the chip.
+ * @orientation: sensor chip orientation relative to main hardware.
+ * @conf: chip sensors configurations.
+ * @suspended: suspended sensors configuration.
+ * @indio_gyro: gyroscope IIO device.
+ * @indio_accel: accelerometer IIO device.
+ * @timestamp: interrupt timestamps.
+ * @buffer: data transfer buffer aligned for DMA.
+ */
+struct inv_icm45600_state {
+ struct mutex lock;
+ struct regmap *map;
+ struct regulator *vddio_supply;
+ struct iio_mount_matrix orientation;
+ struct inv_icm45600_conf conf;
+ struct inv_icm45600_suspended suspended;
+ struct iio_dev *indio_gyro;
+ struct iio_dev *indio_accel;
+ const struct inv_icm45600_chip_info *chip_info;
+ struct {
+ s64 gyro;
+ s64 accel;
+ } timestamp;
+ union {
+ u8 buff[2];
+ __le16 u16;
+ } buffer __aligned(IIO_DMA_MINALIGN);
+};
+
+/**
+ * struct inv_icm45600_sensor_state - sensor state variables
+ * @scales: table of scales.
+ * @scales_len: length (nb of items) of the scales table.
+ * @power_mode: sensor requested power mode (for common frequencies)
+ * @ts: timestamp module states.
+ */
+struct inv_icm45600_sensor_state {
+ const int *scales;
+ size_t scales_len;
+ enum inv_icm45600_sensor_mode power_mode;
+ struct inv_sensors_timestamp ts;
+};
+
+/* Virtual register addresses: @bank on MSB (16 bits), @address on LSB */
+
+/* Indirect register access */
+#define INV_ICM45600_REG_IREG_ADDR 0x7C
+#define INV_ICM45600_REG_IREG_DATA 0x7E
+
+/* Direct acces registers */
+#define INV_ICM45600_REG_MISC2 0x007F
+#define INV_ICM45600_MISC2_SOFT_RESET BIT(1)
+
+#define INV_ICM45600_REG_DRIVE_CONFIG0 0x0032
+#define INV_ICM45600_DRIVE_CONFIG0_SPI_MASK GENMASK(3, 1)
+#define INV_ICM45600_SPI_SLEW_RATE_0_5NS 6
+#define INV_ICM45600_SPI_SLEW_RATE_4NS 5
+#define INV_ICM45600_SPI_SLEW_RATE_5NS 4
+#define INV_ICM45600_SPI_SLEW_RATE_7NS 3
+#define INV_ICM45600_SPI_SLEW_RATE_10NS 2
+#define INV_ICM45600_SPI_SLEW_RATE_14NS 1
+#define INV_ICM45600_SPI_SLEW_RATE_38NS 0
+
+#define INV_ICM45600_REG_INT1_CONFIG2 0x0018
+#define INV_ICM45600_INT1_CONFIG2_PUSH_PULL BIT(2)
+#define INV_ICM45600_INT1_CONFIG2_LATCHED BIT(1)
+#define INV_ICM45600_INT1_CONFIG2_ACTIVE_HIGH BIT(0)
+#define INV_ICM45600_INT1_CONFIG2_ACTIVE_LOW 0x00
+
+#define INV_ICM45600_REG_FIFO_CONFIG0 0x001D
+#define INV_ICM45600_FIFO_CONFIG0_MODE_MASK GENMASK(7, 6)
+#define INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS 0
+#define INV_ICM45600_FIFO_CONFIG0_MODE_STREAM 1
+#define INV_ICM45600_FIFO_CONFIG0_MODE_STOP_ON_FULL 2
+#define INV_ICM45600_FIFO_CONFIG0_FIFO_DEPTH_MAX 0x1F
+
+#define INV_ICM45600_REG_FIFO_CONFIG2 0x0020
+#define INV_ICM45600_REG_FIFO_CONFIG2_FIFO_FLUSH BIT(7)
+#define INV_ICM45600_REG_FIFO_CONFIG2_WM_GT_TH BIT(3)
+
+#define INV_ICM45600_REG_FIFO_CONFIG3 0x0021
+#define INV_ICM45600_FIFO_CONFIG3_ES1_EN BIT(5)
+#define INV_ICM45600_FIFO_CONFIG3_ES0_EN BIT(4)
+#define INV_ICM45600_FIFO_CONFIG3_HIRES_EN BIT(3)
+#define INV_ICM45600_FIFO_CONFIG3_GYRO_EN BIT(2)
+#define INV_ICM45600_FIFO_CONFIG3_ACCEL_EN BIT(1)
+#define INV_ICM45600_FIFO_CONFIG3_IF_EN BIT(0)
+
+#define INV_ICM45600_REG_FIFO_CONFIG4 0x0022
+#define INV_ICM45600_FIFO_CONFIG4_COMP_EN BIT(2)
+#define INV_ICM45600_FIFO_CONFIG4_TMST_FSYNC_EN BIT(1)
+#define INV_ICM45600_FIFO_CONFIG4_ES0_9B BIT(0)
+
+/* all sensor data are 16 bits (2 registers wide) in big-endian */
+#define INV_ICM45600_REG_TEMP_DATA 0x000C
+#define INV_ICM45600_REG_ACCEL_DATA_X 0x0000
+#define INV_ICM45600_REG_ACCEL_DATA_Y 0x0002
+#define INV_ICM45600_REG_ACCEL_DATA_Z 0x0004
+#define INV_ICM45600_REG_GYRO_DATA_X 0x0006
+#define INV_ICM45600_REG_GYRO_DATA_Y 0x0008
+#define INV_ICM45600_REG_GYRO_DATA_Z 0x000A
+#define INV_ICM45600_DATA_INVALID -32768
+
+#define INV_ICM45600_REG_INT_STATUS 0x0019
+#define INV_ICM45600_INT_STATUS_RESET_DONE BIT(7)
+#define INV_ICM45600_INT_STATUS_AUX1_AGC_RDY BIT(6)
+#define INV_ICM45600_INT_STATUS_AP_AGC_RDY BIT(5)
+#define INV_ICM45600_INT_STATUS_AP_FSYNC BIT(4)
+#define INV_ICM45600_INT_STATUS_AUX1_DRDY BIT(3)
+#define INV_ICM45600_INT_STATUS_DATA_RDY BIT(2)
+#define INV_ICM45600_INT_STATUS_FIFO_THS BIT(1)
+#define INV_ICM45600_INT_STATUS_FIFO_FULL BIT(0)
+
+/*
+ * FIFO access registers
+ * FIFO count is 16 bits (2 registers)
+ * FIFO data is a continuous read register to read FIFO content
+ */
+#define INV_ICM45600_REG_FIFO_COUNT 0x0012
+#define INV_ICM45600_REG_FIFO_DATA 0x0014
+
+#define INV_ICM45600_REG_PWR_MGMT0 0x0010
+#define INV_ICM45600_PWR_MGMT0_GYRO_MODE_MASK GENMASK(3, 2)
+#define INV_ICM45600_PWR_MGMT0_ACCEL_MODE_MASK GENMASK(1, 0)
+
+#define INV_ICM45600_REG_ACCEL_CONFIG0 0x001B
+#define INV_ICM45600_ACCEL_CONFIG0_FS_MASK GENMASK(6, 4)
+#define INV_ICM45600_ACCEL_CONFIG0_ODR_MASK GENMASK(3, 0)
+#define INV_ICM45600_REG_GYRO_CONFIG0 0x001C
+#define INV_ICM45600_GYRO_CONFIG0_FS_MASK GENMASK(7, 4)
+#define INV_ICM45600_GYRO_CONFIG0_ODR_MASK GENMASK(3, 0)
+
+#define INV_ICM45600_REG_SMC_CONTROL_0 0xA258
+#define INV_ICM45600_SMC_CONTROL_0_ACCEL_LP_CLK_SEL BIT(4)
+#define INV_ICM45600_SMC_CONTROL_0_TMST_EN BIT(0)
+
+/* FIFO watermark is 16 bits (2 registers wide) in little-endian */
+#define INV_ICM45600_REG_FIFO_WATERMARK 0x001E
+
+/* FIFO is configured for 8kb */
+#define INV_ICM45600_FIFO_SIZE_MAX (8 * 1024)
+
+#define INV_ICM45600_REG_INT1_CONFIG0 0x0016
+#define INV_ICM45600_INT1_CONFIG0_RESET_DONE_EN BIT(7)
+#define INV_ICM45600_INT1_CONFIG0_AUX1_AGC_RDY_EN BIT(6)
+#define INV_ICM45600_INT1_CONFIG0_AP_AGC_RDY_EN BIT(5)
+#define INV_ICM45600_INT1_CONFIG0_AP_FSYNC_EN BIT(4)
+#define INV_ICM45600_INT1_CONFIG0_AUX1_DRDY_EN BIT(3)
+#define INV_ICM45600_INT1_CONFIG0_DRDY_EN BIT(2)
+#define INV_ICM45600_INT1_CONFIG0_FIFO_THS_EN BIT(1)
+#define INV_ICM45600_INT1_CONFIG0_FIFO_FULL_EN BIT(0)
+
+#define INV_ICM45600_REG_WHOAMI 0x0072
+#define INV_ICM45600_WHOAMI_ICM45605 0xE5
+#define INV_ICM45600_WHOAMI_ICM45686 0xE9
+#define INV_ICM45600_WHOAMI_ICM45688P 0xE7
+#define INV_ICM45600_WHOAMI_ICM45608 0x81
+#define INV_ICM45600_WHOAMI_ICM45634 0x82
+#define INV_ICM45600_WHOAMI_ICM45689 0x83
+#define INV_ICM45600_WHOAMI_ICM45606 0x84
+#define INV_ICM45600_WHOAMI_ICM45687 0x85
+
+/* Gyro USER offset */
+#define INV_ICM45600_IPREG_SYS1_REG_42 0xA42A
+#define INV_ICM45600_IPREG_SYS1_REG_56 0xA438
+#define INV_ICM45600_IPREG_SYS1_REG_70 0xA446
+#define INV_ICM45600_GYRO_OFFUSER_MASK GENMASK(13, 0)
+/* Gyro Averaging filter */
+#define INV_ICM45600_IPREG_SYS1_REG_170 0xA4AA
+#define INV_ICM45600_IPREG_SYS1_170_GYRO_LP_AVG_MASK GENMASK(4, 1)
+#define INV_ICM45600_GYRO_LP_AVG_SEL_8X 5
+#define INV_ICM45600_GYRO_LP_AVG_SEL_2X 1
+/* Accel USER offset */
+#define INV_ICM45600_IPREG_SYS2_REG_24 0xA518
+#define INV_ICM45600_IPREG_SYS2_REG_32 0xA520
+#define INV_ICM45600_IPREG_SYS2_REG_40 0xA528
+#define INV_ICM45600_ACCEL_OFFUSER_MASK GENMASK(13, 0)
+/* Accel averaging filter */
+#define INV_ICM45600_IPREG_SYS2_REG_129 0xA581
+#define INV_ICM45600_ACCEL_LP_AVG_SEL_1X 0x0000
+#define INV_ICM45600_ACCEL_LP_AVG_SEL_4X 0x0002
+
+/* Sleep times required by the driver */
+#define INV_ICM45600_ACCEL_STARTUP_TIME_MS 60
+#define INV_ICM45600_GYRO_STARTUP_TIME_MS 60
+#define INV_ICM45600_GYRO_STOP_TIME_MS 150
+#define INV_ICM45600_IREG_DELAY_US 4
+
+typedef int (*inv_icm45600_bus_setup)(struct inv_icm45600_state *);
+
+extern const struct dev_pm_ops inv_icm45600_pm_ops;
+
+const struct iio_mount_matrix *
+inv_icm45600_get_mount_matrix(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan);
+
+#define INV_ICM45600_TEMP_CHAN(_index) \
+ { \
+ .type = IIO_TEMP, \
+ .info_mask_separate = \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+ }, \
+ }
+
+int inv_icm45600_temp_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask);
+
+u32 inv_icm45600_odr_to_period(enum inv_icm45600_odr odr);
+
+int inv_icm45600_set_accel_conf(struct inv_icm45600_state *st,
+ struct inv_icm45600_sensor_conf *conf,
+ unsigned int *sleep_ms);
+
+int inv_icm45600_set_gyro_conf(struct inv_icm45600_state *st,
+ struct inv_icm45600_sensor_conf *conf,
+ unsigned int *sleep_ms);
+
+int inv_icm45600_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int writeval, unsigned int *readval);
+
+int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm45600_chip_info *chip_info,
+ bool reset, inv_icm45600_bus_setup bus_setup);
+
+struct iio_dev *inv_icm45600_gyro_init(struct inv_icm45600_state *st);
+
+int inv_icm45600_gyro_parse_fifo(struct iio_dev *indio_dev);
+
+struct iio_dev *inv_icm45600_accel_init(struct inv_icm45600_state *st);
+
+int inv_icm45600_accel_parse_fifo(struct iio_dev *indio_dev);
+
+#endif
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
new file mode 100644
index 0000000000000000000000000000000000000000..022eb9180b5a750e8fba4a1cd873041c27f59880
--- /dev/null
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
@@ -0,0 +1,734 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (C) 2025 Invensense, Inc. */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+
+#include "inv_icm45600.h"
+
+static int inv_icm45600_ireg_read(struct regmap *map, unsigned int reg,
+ u8 *data, size_t count)
+{
+ int ret;
+ u8 addr[2];
+ ssize_t i;
+ unsigned int d;
+
+ addr[0] = FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg);
+ addr[1] = FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg);
+
+ /* Burst write address. */
+ ret = regmap_bulk_write(map, INV_ICM45600_REG_IREG_ADDR, addr, 2);
+ udelay(INV_ICM45600_IREG_DELAY_US);
+ if (ret)
+ return ret;
+
+ /* Read the data. */
+ for (i = 0; i < count; i++) {
+ ret = regmap_read(map, INV_ICM45600_REG_IREG_DATA, &d);
+ data[i] = d;
+ udelay(INV_ICM45600_IREG_DELAY_US);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int inv_icm45600_ireg_write(struct regmap *map, unsigned int reg,
+ const u8 *data, size_t count)
+{
+ int ret;
+ u8 addr_data0[3];
+ ssize_t i;
+
+ addr_data0[0] = FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg);
+ addr_data0[1] = FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg);
+ addr_data0[2] = data[0];
+
+ /* Burst write address and first byte. */
+ ret = regmap_bulk_write(map, INV_ICM45600_REG_IREG_ADDR, addr_data0, 3);
+ udelay(INV_ICM45600_IREG_DELAY_US);
+ if (ret)
+ return ret;
+
+ /* Write the remaining bytes. */
+ for (i = 1; i < count; i++) {
+ ret = regmap_write(map, INV_ICM45600_REG_IREG_DATA, data[i]);
+ udelay(INV_ICM45600_IREG_DELAY_US);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int inv_icm45600_read(void *context, const void *reg_buf, size_t reg_size,
+ void *val_buf, size_t val_size)
+{
+ unsigned int reg = (unsigned int) be16_to_cpup(reg_buf);
+ struct regmap *map = context;
+
+ if (FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg) == 0)
+ return regmap_bulk_read(map, FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg),
+ val_buf, val_size);
+
+ return inv_icm45600_ireg_read(map, reg, val_buf, val_size);
+}
+
+static int inv_icm45600_write(void *context, const void *data,
+ size_t count)
+{
+ u8 *d = (u8 *)data;
+ unsigned int reg = (unsigned int) be16_to_cpup(data);
+ struct regmap *map = context;
+
+ if (FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg) == 0)
+ return regmap_bulk_write(map, FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg),
+ d + 2, count - 2);
+
+ return inv_icm45600_ireg_write(map, reg, d + 2, count - 2);
+}
+
+static const struct regmap_bus inv_icm45600_regmap_bus = {
+ .read = inv_icm45600_read,
+ .write = inv_icm45600_write,
+};
+
+static const struct regmap_config inv_icm45600_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+};
+
+/* These are the chip initial default configurations (default FS value is based on icm45686) */
+static const struct inv_icm45600_conf inv_icm45600_default_conf = {
+ .gyro = {
+ .mode = INV_ICM45600_SENSOR_MODE_OFF,
+ .fs = INV_ICM45686_GYRO_FS_2000DPS,
+ .odr = INV_ICM45600_ODR_800HZ_LN,
+ .filter = INV_ICM45600_GYRO_LP_AVG_SEL_8X,
+ },
+ .accel = {
+ .mode = INV_ICM45600_SENSOR_MODE_OFF,
+ .fs = INV_ICM45686_ACCEL_FS_16G,
+ .odr = INV_ICM45600_ODR_800HZ_LN,
+ .filter = INV_ICM45600_ACCEL_LP_AVG_SEL_4X,
+ },
+};
+
+static const struct inv_icm45600_conf inv_icm45686_default_conf = {
+ .gyro = {
+ .mode = INV_ICM45600_SENSOR_MODE_OFF,
+ .fs = INV_ICM45686_GYRO_FS_4000DPS,
+ .odr = INV_ICM45600_ODR_800HZ_LN,
+ .filter = INV_ICM45600_GYRO_LP_AVG_SEL_8X,
+ },
+ .accel = {
+ .mode = INV_ICM45600_SENSOR_MODE_OFF,
+ .fs = INV_ICM45686_ACCEL_FS_32G,
+ .odr = INV_ICM45600_ODR_800HZ_LN,
+ .filter = INV_ICM45600_ACCEL_LP_AVG_SEL_4X,
+ },
+};
+
+const struct inv_icm45600_chip_info inv_icm45605_chip_info = {
+ .whoami = INV_ICM45600_WHOAMI_ICM45605,
+ .name = "icm45605",
+ .conf = &inv_icm45600_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm45605_chip_info, "IIO_ICM45600");
+
+const struct inv_icm45600_chip_info inv_icm45606_chip_info = {
+ .whoami = INV_ICM45600_WHOAMI_ICM45606,
+ .name = "icm45606",
+ .conf = &inv_icm45600_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm45606_chip_info, "IIO_ICM45600");
+
+const struct inv_icm45600_chip_info inv_icm45608_chip_info = {
+ .whoami = INV_ICM45600_WHOAMI_ICM45608,
+ .name = "icm45608",
+ .conf = &inv_icm45600_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm45608_chip_info, "IIO_ICM45600");
+
+const struct inv_icm45600_chip_info inv_icm45634_chip_info = {
+ .whoami = INV_ICM45600_WHOAMI_ICM45634,
+ .name = "icm45634",
+ .conf = &inv_icm45600_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm45634_chip_info, "IIO_ICM45600");
+
+const struct inv_icm45600_chip_info inv_icm45686_chip_info = {
+ .whoami = INV_ICM45600_WHOAMI_ICM45686,
+ .name = "icm45686",
+ .conf = &inv_icm45686_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm45686_chip_info, "IIO_ICM45600");
+
+const struct inv_icm45600_chip_info inv_icm45687_chip_info = {
+ .whoami = INV_ICM45600_WHOAMI_ICM45687,
+ .name = "icm45687",
+ .conf = &inv_icm45686_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm45687_chip_info, "IIO_ICM45600");
+
+const struct inv_icm45600_chip_info inv_icm45688p_chip_info = {
+ .whoami = INV_ICM45600_WHOAMI_ICM45688P,
+ .name = "icm45688p",
+ .conf = &inv_icm45686_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm45688p_chip_info, "IIO_ICM45600");
+
+const struct inv_icm45600_chip_info inv_icm45689_chip_info = {
+ .whoami = INV_ICM45600_WHOAMI_ICM45689,
+ .name = "icm45689",
+ .conf = &inv_icm45686_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm45689_chip_info, "IIO_ICM45600");
+
+const struct iio_mount_matrix *
+inv_icm45600_get_mount_matrix(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ const struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+
+ return &st->orientation;
+}
+
+u32 inv_icm45600_odr_to_period(enum inv_icm45600_odr odr)
+{
+ static u32 odr_periods[INV_ICM45600_ODR_NB] = {
+ /* reserved values */
+ 0, 0, 0,
+ /* 6.4kHz */
+ 156250,
+ /* 3.2kHz */
+ 312500,
+ /* 1.6kHz */
+ 625000,
+ /* 800kHz */
+ 1250000,
+ /* 400Hz */
+ 2500000,
+ /* 200Hz */
+ 5000000,
+ /* 100Hz */
+ 10000000,
+ /* 50Hz */
+ 20000000,
+ /* 25Hz */
+ 40000000,
+ /* 12.5Hz */
+ 80000000,
+ /* 6.25Hz */
+ 160000000,
+ /* 3.125Hz */
+ 320000000,
+ /* 1.5625Hz */
+ 640000000,
+ };
+
+ return odr_periods[odr];
+}
+
+static int inv_icm45600_set_pwr_mgmt0(struct inv_icm45600_state *st,
+ enum inv_icm45600_sensor_mode gyro,
+ enum inv_icm45600_sensor_mode accel,
+ unsigned int *sleep_ms)
+{
+ enum inv_icm45600_sensor_mode oldgyro = st->conf.gyro.mode;
+ enum inv_icm45600_sensor_mode oldaccel = st->conf.accel.mode;
+ unsigned int sleepval;
+ unsigned int val;
+ int ret;
+
+ /* if nothing changed, exit */
+ if (gyro == oldgyro && accel == oldaccel)
+ return 0;
+
+ val = FIELD_PREP(INV_ICM45600_PWR_MGMT0_GYRO_MODE_MASK, gyro) |
+ FIELD_PREP(INV_ICM45600_PWR_MGMT0_ACCEL_MODE_MASK, accel);
+ ret = regmap_write(st->map, INV_ICM45600_REG_PWR_MGMT0, val);
+ if (ret)
+ return ret;
+
+ st->conf.gyro.mode = gyro;
+ st->conf.accel.mode = accel;
+
+ /* Compute the required wait time for sensors to stabilize. */
+ sleepval = 0;
+
+ /* Accel startup time. */
+ if (accel != oldaccel && oldaccel == INV_ICM45600_SENSOR_MODE_OFF) {
+ if (sleepval < INV_ICM45600_ACCEL_STARTUP_TIME_MS)
+ sleepval = INV_ICM45600_ACCEL_STARTUP_TIME_MS;
+ }
+ if (gyro != oldgyro) {
+ /* Gyro startup time. */
+ if (oldgyro == INV_ICM45600_SENSOR_MODE_OFF) {
+ if (sleepval < INV_ICM45600_GYRO_STARTUP_TIME_MS)
+ sleepval = INV_ICM45600_GYRO_STARTUP_TIME_MS;
+ /* Gyro stop time. */
+ } else if (gyro == INV_ICM45600_SENSOR_MODE_OFF) {
+ if (sleepval < INV_ICM45600_GYRO_STOP_TIME_MS)
+ sleepval = INV_ICM45600_GYRO_STOP_TIME_MS;
+ }
+ }
+
+ /* Deferred sleep value if sleep pointer is provided or direct sleep */
+ if (sleep_ms)
+ *sleep_ms = sleepval;
+ else if (sleepval)
+ msleep(sleepval);
+
+ return 0;
+}
+
+int inv_icm45600_set_accel_conf(struct inv_icm45600_state *st,
+ struct inv_icm45600_sensor_conf *conf,
+ unsigned int *sleep_ms)
+{
+ struct inv_icm45600_sensor_conf *oldconf = &st->conf.accel;
+ unsigned int val;
+ int ret;
+
+ /* Sanitize missing values with current values. */
+ if (conf->mode < 0)
+ conf->mode = oldconf->mode;
+ if (conf->fs < 0)
+ conf->fs = oldconf->fs;
+ if (conf->odr < 0)
+ conf->odr = oldconf->odr;
+ if (conf->filter < 0)
+ conf->filter = oldconf->filter;
+
+ /* Force the power mode against the ODR when sensor is on. */
+ if (conf->mode > INV_ICM45600_SENSOR_MODE_STANDBY) {
+ if (conf->odr <= INV_ICM45600_ODR_800HZ_LN) {
+ conf->mode = INV_ICM45600_SENSOR_MODE_LOW_NOISE;
+ } else {
+ conf->mode = INV_ICM45600_SENSOR_MODE_LOW_POWER;
+ /* sanitize averaging value depending on ODR for low-power mode */
+ /* maximum 1x @400Hz */
+ if (conf->odr == INV_ICM45600_ODR_400HZ)
+ conf->filter = INV_ICM45600_ACCEL_LP_AVG_SEL_1X;
+ else
+ conf->filter = INV_ICM45600_ACCEL_LP_AVG_SEL_4X;
+ }
+ }
+
+ /* Set ACCEL_CONFIG0 register (accel fullscale & odr). */
+ if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) {
+ val = FIELD_PREP(INV_ICM45600_ACCEL_CONFIG0_FS_MASK, conf->fs) |
+ FIELD_PREP(INV_ICM45600_ACCEL_CONFIG0_ODR_MASK, conf->odr);
+ ret = regmap_write(st->map, INV_ICM45600_REG_ACCEL_CONFIG0, val);
+ if (ret)
+ return ret;
+ oldconf->fs = conf->fs;
+ oldconf->odr = conf->odr;
+ }
+
+ /* Set ACCEL_LP_AVG_SEL register (accel low-power average filter). */
+ if (conf->filter != oldconf->filter) {
+ ret = regmap_write(st->map, INV_ICM45600_IPREG_SYS2_REG_129,
+ conf->filter);
+ if (ret)
+ return ret;
+ oldconf->filter = conf->filter;
+ }
+
+ /* Set PWR_MGMT0 register (accel sensor mode). */
+ return inv_icm45600_set_pwr_mgmt0(st, st->conf.gyro.mode, conf->mode,
+ sleep_ms);
+}
+
+int inv_icm45600_set_gyro_conf(struct inv_icm45600_state *st,
+ struct inv_icm45600_sensor_conf *conf,
+ unsigned int *sleep_ms)
+{
+ struct inv_icm45600_sensor_conf *oldconf = &st->conf.gyro;
+ unsigned int val;
+ int ret;
+
+ /* Sanitize missing values with current values. */
+ if (conf->mode < 0)
+ conf->mode = oldconf->mode;
+ if (conf->fs < 0)
+ conf->fs = oldconf->fs;
+ if (conf->odr < 0)
+ conf->odr = oldconf->odr;
+ if (conf->filter < 0)
+ conf->filter = oldconf->filter;
+
+ /* Force the power mode against ODR when sensor is on. */
+ if (conf->mode > INV_ICM45600_SENSOR_MODE_STANDBY) {
+ if (conf->odr >= INV_ICM45600_ODR_6_25HZ_LP) {
+ conf->mode = INV_ICM45600_SENSOR_MODE_LOW_POWER;
+ conf->filter = INV_ICM45600_GYRO_LP_AVG_SEL_8X;
+ } else {
+ conf->mode = INV_ICM45600_SENSOR_MODE_LOW_NOISE;
+ }
+ }
+
+ /* Set GYRO_CONFIG0 register (gyro fullscale & odr). */
+ if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) {
+ val = FIELD_PREP(INV_ICM45600_GYRO_CONFIG0_FS_MASK, conf->fs) |
+ FIELD_PREP(INV_ICM45600_GYRO_CONFIG0_ODR_MASK, conf->odr);
+ ret = regmap_write(st->map, INV_ICM45600_REG_GYRO_CONFIG0, val);
+ if (ret)
+ return ret;
+ oldconf->fs = conf->fs;
+ oldconf->odr = conf->odr;
+ }
+
+ /* Set GYRO_LP_AVG_SEL register (gyro low-power average filter). */
+ if (conf->filter != oldconf->filter) {
+ val = FIELD_PREP(INV_ICM45600_IPREG_SYS1_170_GYRO_LP_AVG_MASK, conf->filter);
+ ret = regmap_update_bits(st->map, INV_ICM45600_IPREG_SYS1_REG_170,
+ INV_ICM45600_IPREG_SYS1_170_GYRO_LP_AVG_MASK, val);
+ if (ret)
+ return ret;
+ oldconf->filter = conf->filter;
+ }
+
+ /* Set PWR_MGMT0 register (gyro sensor mode). */
+ return inv_icm45600_set_pwr_mgmt0(st, conf->mode, st->conf.accel.mode,
+ sleep_ms);
+}
+
+int inv_icm45600_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int writeval, unsigned int *readval)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ if (readval)
+ ret = regmap_read(st->map, reg, readval);
+ else
+ ret = regmap_write(st->map, reg, writeval);
+
+ return ret;
+}
+
+static int inv_icm45600_set_conf(struct inv_icm45600_state *st,
+ const struct inv_icm45600_conf *conf)
+{
+ unsigned int val;
+ int ret;
+
+ /* Set PWR_MGMT0 register (gyro & accel sensor mode, temp enabled). */
+ val = FIELD_PREP(INV_ICM45600_PWR_MGMT0_GYRO_MODE_MASK, conf->gyro.mode) |
+ FIELD_PREP(INV_ICM45600_PWR_MGMT0_ACCEL_MODE_MASK, conf->accel.mode);
+ ret = regmap_write(st->map, INV_ICM45600_REG_PWR_MGMT0, val);
+ if (ret)
+ return ret;
+
+ /* Set GYRO_CONFIG0 register (gyro fullscale & odr). */
+ val = FIELD_PREP(INV_ICM45600_GYRO_CONFIG0_FS_MASK, conf->gyro.fs) |
+ FIELD_PREP(INV_ICM45600_GYRO_CONFIG0_ODR_MASK, conf->gyro.odr);
+ ret = regmap_write(st->map, INV_ICM45600_REG_GYRO_CONFIG0, val);
+ if (ret)
+ return ret;
+
+ /* Set ACCEL_CONFIG0 register (accel fullscale & odr). */
+ val = FIELD_PREP(INV_ICM45600_ACCEL_CONFIG0_FS_MASK, conf->accel.fs) |
+ FIELD_PREP(INV_ICM45600_ACCEL_CONFIG0_ODR_MASK, conf->accel.odr);
+ ret = regmap_write(st->map, INV_ICM45600_REG_ACCEL_CONFIG0, val);
+ if (ret)
+ return ret;
+
+ /* Update the internal configuration. */
+ st->conf = *conf;
+
+ return 0;
+}
+
+/**
+ * inv_icm45600_setup() - check and setup chip
+ * @st: driver internal state
+ * @chip_info: detected chip description
+ * @reset: define whether a reset is required or not
+ * @bus_setup: callback for setting up bus specific registers
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+static int inv_icm45600_setup(struct inv_icm45600_state *st,
+ const struct inv_icm45600_chip_info *chip_info,
+ bool reset, inv_icm45600_bus_setup bus_setup)
+{
+ const struct device *dev = regmap_get_device(st->map);
+ unsigned int val;
+ int ret;
+
+ /* Set chip bus configuration if specified. */
+ if (bus_setup) {
+ ret = bus_setup(st);
+ if (ret)
+ return ret;
+ }
+
+ /* Check chip self-identification value. */
+ ret = regmap_read(st->map, INV_ICM45600_REG_WHOAMI, &val);
+ if (ret)
+ return ret;
+ if (val != chip_info->whoami) {
+ dev_err(dev, "invalid whoami %#02x expected %#02x (%s)\n",
+ val, chip_info->whoami, chip_info->name);
+ return -ENODEV;
+ }
+
+ st->chip_info = chip_info;
+
+ if (reset) {
+ /* Reset to make sure previous state are not there. */
+ ret = regmap_write(st->map, INV_ICM45600_REG_MISC2,
+ INV_ICM45600_MISC2_SOFT_RESET);
+ if (ret)
+ return ret;
+ /* IMU reset time: 1ms. */
+ fsleep(1000);
+
+ if (bus_setup) {
+ ret = bus_setup(st);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_read(st->map, INV_ICM45600_REG_INT_STATUS, &val);
+ if (ret)
+ return ret;
+ if (!(val & INV_ICM45600_INT_STATUS_RESET_DONE)) {
+ dev_err(dev, "reset error, reset done bit not set\n");
+ return -ENODEV;
+ }
+ }
+
+ return inv_icm45600_set_conf(st, chip_info->conf);
+}
+
+static int inv_icm45600_timestamp_setup(struct inv_icm45600_state *st)
+{
+ /* Enable timestamps. */
+ return regmap_set_bits(st->map, INV_ICM45600_REG_SMC_CONTROL_0,
+ INV_ICM45600_SMC_CONTROL_0_TMST_EN);
+}
+
+static int inv_icm45600_enable_regulator_vddio(struct inv_icm45600_state *st)
+{
+ int ret;
+
+ ret = regulator_enable(st->vddio_supply);
+ if (ret)
+ return ret;
+
+ /* Wait a little for supply ramp. */
+ usleep_range(3000, 4000);
+
+ return 0;
+}
+
+static void inv_icm45600_disable_vddio_reg(void *_data)
+{
+ struct inv_icm45600_state *st = _data;
+ const struct device *dev = regmap_get_device(st->map);
+ int ret;
+
+ ret = regulator_disable(st->vddio_supply);
+ if (ret)
+ dev_err(dev, "failed to disable vddio error %d\n", ret);
+}
+
+int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm45600_chip_info *chip_info,
+ bool reset, inv_icm45600_bus_setup bus_setup)
+{
+ struct device *dev = regmap_get_device(regmap);
+ struct fwnode_handle *fwnode;
+ struct inv_icm45600_state *st;
+ struct regmap *regmap_custom;
+ int irq, irq_type;
+ bool open_drain;
+ int ret;
+
+ /* Get INT1 only supported interrupt. */
+ fwnode = dev_fwnode(dev);
+ if (!fwnode)
+ return dev_err_probe(dev, -ENODEV, "Missing FW node\n");
+
+ irq = fwnode_irq_get_byname(fwnode, "INT1");
+ if (irq < 0) {
+ if (irq != -EPROBE_DEFER)
+ dev_err_probe(dev, irq, "Missing INT1 interrupt\n");
+ return irq;
+ }
+
+ irq_type = irq_get_trigger_type(irq);
+
+ open_drain = device_property_read_bool(dev, "drive-open-drain");
+
+ regmap_custom = devm_regmap_init(dev, &inv_icm45600_regmap_bus,
+ regmap, &inv_icm45600_regmap_config);
+ if (IS_ERR(regmap_custom))
+ return dev_err_probe(dev, PTR_ERR(regmap_custom), "Failed to register regmap\n");
+
+ st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return dev_err_probe(dev, -ENOMEM, "Cannot allocate memory\n");
+
+ dev_set_drvdata(dev, st);
+ ret = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
+ st->map = regmap_custom;
+
+ ret = iio_read_mount_matrix(dev, &st->orientation);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to retrieve mounting matrix\n");
+
+ st->vddio_supply = devm_regulator_get(dev, "vddio");
+ if (IS_ERR(st->vddio_supply))
+ return PTR_ERR(st->vddio_supply);
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return ret;
+ /* IMU start-up time. */
+ msleep(100);
+
+ ret = inv_icm45600_enable_regulator_vddio(st);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, inv_icm45600_disable_vddio_reg, st);
+ if (ret)
+ return ret;
+
+ /* Setup chip registers. */
+ ret = inv_icm45600_setup(st, chip_info, reset, bus_setup);
+ if (ret)
+ return ret;
+
+ ret = inv_icm45600_timestamp_setup(st);
+ if (ret)
+ return ret;
+
+ /* Setup runtime power management. */
+ ret = devm_pm_runtime_set_active_enabled(dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_get_noresume(dev);
+ /* Suspend after 2 seconds. */
+ pm_runtime_set_autosuspend_delay(dev, 2000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_put(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(inv_icm45600_core_probe, "IIO_ICM45600");
+
+/*
+ * Suspend saves sensors state and turns everything off.
+ * Check first if runtime suspend has not already done the job.
+ */
+static int inv_icm45600_suspend(struct device *dev)
+{
+ struct inv_icm45600_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ st->suspended.gyro = st->conf.gyro.mode;
+ st->suspended.accel = st->conf.accel.mode;
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ ret = inv_icm45600_set_pwr_mgmt0(st, INV_ICM45600_SENSOR_MODE_OFF,
+ INV_ICM45600_SENSOR_MODE_OFF, NULL);
+ if (ret)
+ return ret;
+
+ regulator_disable(st->vddio_supply);
+
+ return 0;
+}
+
+/*
+ * System resume gets the system back on and restores the sensors state.
+ * Manually put runtime power management in system active state.
+ */
+static int inv_icm45600_resume(struct device *dev)
+{
+ struct inv_icm45600_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ ret = inv_icm45600_enable_regulator_vddio(st);
+ if (ret)
+ return ret;
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ /* Restore sensors state. */
+ return inv_icm45600_set_pwr_mgmt0(st, st->suspended.gyro,
+ st->suspended.accel, NULL);
+
+}
+
+/* Runtime suspend will turn off sensors that are enabled by iio devices. */
+static int inv_icm45600_runtime_suspend(struct device *dev)
+{
+ struct inv_icm45600_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ /* disable all sensors */
+ ret = inv_icm45600_set_pwr_mgmt0(st, INV_ICM45600_SENSOR_MODE_OFF,
+ INV_ICM45600_SENSOR_MODE_OFF, NULL);
+ if (ret)
+ return ret;
+
+ regulator_disable(st->vddio_supply);
+
+ return 0;
+}
+
+/* Sensors are enabled by iio devices, no need to turn them back on here. */
+static int inv_icm45600_runtime_resume(struct device *dev)
+{
+ struct inv_icm45600_state *st = dev_get_drvdata(dev);
+
+ guard(mutex)(&st->lock);
+
+ return inv_icm45600_enable_regulator_vddio(st);
+}
+
+EXPORT_NS_GPL_DEV_PM_OPS(inv_icm45600_pm_ops, IIO_ICM45600) = {
+ SET_SYSTEM_SLEEP_PM_OPS(inv_icm45600_suspend, inv_icm45600_resume)
+ SET_RUNTIME_PM_OPS(inv_icm45600_runtime_suspend,
+ inv_icm45600_runtime_resume, NULL)
+};
+
+MODULE_AUTHOR("InvenSense, Inc.");
+MODULE_DESCRIPTION("InvenSense ICM-456xx device driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_INV_SENSORS_TIMESTAMP");
--
2.34.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v2 3/8] iio: imu: inv_icm45600: add buffer support in iio devices
2025-07-10 8:57 [PATCH v2 0/8] iio: imu: new inv_icm45600 driver Remi Buisson via B4 Relay
2025-07-10 8:57 ` [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600 Remi Buisson via B4 Relay
2025-07-10 8:57 ` [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver Remi Buisson via B4 Relay
@ 2025-07-10 8:57 ` Remi Buisson via B4 Relay
2025-07-17 14:33 ` Jonathan Cameron
2025-07-10 8:57 ` [PATCH v2 4/8] iio: imu: inv_icm45600: add IMU IIO devices Remi Buisson via B4 Relay
` (5 subsequent siblings)
8 siblings, 1 reply; 46+ messages in thread
From: Remi Buisson via B4 Relay @ 2025-07-10 8:57 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-kernel, linux-iio, devicetree, Remi Buisson
From: Remi Buisson <remi.buisson@tdk.com>
Add FIFO control functions.
Support hwfifo watermark by multiplexing gyro and accel settings.
Support hwfifo flush.
Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
---
drivers/iio/imu/inv_icm45600/Makefile | 1 +
drivers/iio/imu/inv_icm45600/inv_icm45600.h | 4 +
drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c | 514 +++++++++++++++++++++
drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h | 99 ++++
drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 137 +++++-
5 files changed, 754 insertions(+), 1 deletion(-)
diff --git a/drivers/iio/imu/inv_icm45600/Makefile b/drivers/iio/imu/inv_icm45600/Makefile
index 4f442b61896e91647c7947a044949792bae06a30..19c521ffba17b0d108a8ecb45ecdea35dff6fd18 100644
--- a/drivers/iio/imu/inv_icm45600/Makefile
+++ b/drivers/iio/imu/inv_icm45600/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_INV_ICM45600) += inv-icm45600.o
inv-icm45600-y += inv_icm45600_core.o
+inv-icm45600-y += inv_icm45600_buffer.o
\ No newline at end of file
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600.h b/drivers/iio/imu/inv_icm45600/inv_icm45600.h
index d56de75ab7f2168f22e25b5816cb361bef457c0d..0b97c54f74b30aef3842e34da098c2443347de00 100644
--- a/drivers/iio/imu/inv_icm45600/inv_icm45600.h
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h
@@ -10,6 +10,8 @@
#include <linux/iio/iio.h>
#include <linux/types.h>
+#include "inv_icm45600_buffer.h"
+
#define INV_ICM45600_REG_BANK_MASK GENMASK(15, 8)
#define INV_ICM45600_REG_ADDR_MASK GENMASK(7, 0)
@@ -128,6 +130,7 @@ extern const struct inv_icm45600_chip_info inv_icm45689_chip_info;
* @indio_gyro: gyroscope IIO device.
* @indio_accel: accelerometer IIO device.
* @timestamp: interrupt timestamps.
+ * @fifo: FIFO management structure.
* @buffer: data transfer buffer aligned for DMA.
*/
struct inv_icm45600_state {
@@ -144,6 +147,7 @@ struct inv_icm45600_state {
s64 gyro;
s64 accel;
} timestamp;
+ struct inv_icm45600_fifo fifo;
union {
u8 buff[2];
__le16 u16;
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c
new file mode 100644
index 0000000000000000000000000000000000000000..a07affa0e3c64f84330071c92018438882cdf686
--- /dev/null
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c
@@ -0,0 +1,514 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (C) 2025 Invensense, Inc. */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/common/inv_sensors_timestamp.h>
+#include <linux/iio/iio.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include "inv_icm45600_buffer.h"
+#include "inv_icm45600.h"
+
+/* FIFO header: 1 byte */
+#define INV_ICM45600_FIFO_EXT_HEADER BIT(7)
+#define INV_ICM45600_FIFO_HEADER_ACCEL BIT(6)
+#define INV_ICM45600_FIFO_HEADER_GYRO BIT(5)
+#define INV_ICM45600_FIFO_HEADER_HIGH_RES BIT(4)
+#define INV_ICM45600_FIFO_HEADER_TMST_FSYNC GENMASK(3, 2)
+#define INV_ICM45600_FIFO_HEADER_ODR_ACCEL BIT(1)
+#define INV_ICM45600_FIFO_HEADER_ODR_GYRO BIT(0)
+
+struct inv_icm45600_fifo_1sensor_packet {
+ u8 header;
+ struct inv_icm45600_fifo_sensor_data data;
+ s8 temp;
+} __packed;
+#define INV_ICM45600_FIFO_1SENSOR_PACKET_SIZE 8
+
+struct inv_icm45600_fifo_2sensors_packet {
+ u8 header;
+ struct inv_icm45600_fifo_sensor_data accel;
+ struct inv_icm45600_fifo_sensor_data gyro;
+ s8 temp;
+ __le16 timestamp;
+} __packed;
+#define INV_ICM45600_FIFO_2SENSORS_PACKET_SIZE 16
+
+ssize_t inv_icm45600_fifo_decode_packet(const void *packet,
+ const struct inv_icm45600_fifo_sensor_data **accel,
+ const struct inv_icm45600_fifo_sensor_data **gyro,
+ const s8 **temp,
+ const __le16 **timestamp, unsigned int *odr)
+{
+ const struct inv_icm45600_fifo_1sensor_packet *pack1 = packet;
+ const struct inv_icm45600_fifo_2sensors_packet *pack2 = packet;
+ u8 header = *((const u8 *)packet);
+
+ /* FIFO extended header */
+ if (header & INV_ICM45600_FIFO_EXT_HEADER) {
+ /* Not yet supported */
+ return 0;
+ }
+
+ /* handle odr flags. */
+ *odr = 0;
+ if (header & INV_ICM45600_FIFO_HEADER_ODR_GYRO)
+ *odr |= INV_ICM45600_SENSOR_GYRO;
+ if (header & INV_ICM45600_FIFO_HEADER_ODR_ACCEL)
+ *odr |= INV_ICM45600_SENSOR_ACCEL;
+
+ /* Accel + Gyro data are present. */
+ if ((header & INV_ICM45600_FIFO_HEADER_ACCEL) &&
+ (header & INV_ICM45600_FIFO_HEADER_GYRO)) {
+ *accel = &pack2->accel;
+ *gyro = &pack2->gyro;
+ *temp = &pack2->temp;
+ *timestamp = &pack2->timestamp;
+ return INV_ICM45600_FIFO_2SENSORS_PACKET_SIZE;
+ }
+
+ /* Accel data only. */
+ if (header & INV_ICM45600_FIFO_HEADER_ACCEL) {
+ *accel = &pack1->data;
+ *gyro = NULL;
+ *temp = &pack1->temp;
+ *timestamp = NULL;
+ return INV_ICM45600_FIFO_1SENSOR_PACKET_SIZE;
+ }
+
+ /* Gyro data only. */
+ if (header & INV_ICM45600_FIFO_HEADER_GYRO) {
+ *accel = NULL;
+ *gyro = &pack1->data;
+ *temp = &pack1->temp;
+ *timestamp = NULL;
+ return INV_ICM45600_FIFO_1SENSOR_PACKET_SIZE;
+ }
+
+ /* Invalid packet if here. */
+ return -EINVAL;
+}
+
+void inv_icm45600_buffer_update_fifo_period(struct inv_icm45600_state *st)
+{
+ u32 period_gyro, period_accel, period;
+
+ if (st->fifo.en & INV_ICM45600_SENSOR_GYRO)
+ period_gyro = inv_icm45600_odr_to_period(st->conf.gyro.odr);
+ else
+ period_gyro = U32_MAX;
+
+ if (st->fifo.en & INV_ICM45600_SENSOR_ACCEL)
+ period_accel = inv_icm45600_odr_to_period(st->conf.accel.odr);
+ else
+ period_accel = U32_MAX;
+
+ if (period_gyro <= period_accel)
+ period = period_gyro;
+ else
+ period = period_accel;
+
+ st->fifo.period = period;
+}
+
+int inv_icm45600_buffer_set_fifo_en(struct inv_icm45600_state *st,
+ unsigned int fifo_en)
+{
+ unsigned int mask, val;
+ int ret;
+
+ /* Update only FIFO EN bits. */
+ mask = INV_ICM45600_FIFO_CONFIG3_GYRO_EN |
+ INV_ICM45600_FIFO_CONFIG3_ACCEL_EN;
+
+ val = 0;
+ if ((fifo_en & INV_ICM45600_SENSOR_GYRO) || (fifo_en & INV_ICM45600_SENSOR_ACCEL))
+ val = (INV_ICM45600_FIFO_CONFIG3_GYRO_EN | INV_ICM45600_FIFO_CONFIG3_ACCEL_EN);
+
+ ret = regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3, mask, val);
+ if (ret)
+ return ret;
+
+ st->fifo.en = fifo_en;
+ inv_icm45600_buffer_update_fifo_period(st);
+
+ return 0;
+}
+
+static unsigned int inv_icm45600_wm_truncate(unsigned int watermark, size_t packet_size,
+ unsigned int fifo_period)
+{
+ size_t watermark_max, grace_samples;
+
+ /* Keep 20ms for processing FIFO. */
+ grace_samples = (20U * 1000000U) / fifo_period;
+ if (grace_samples < 1)
+ grace_samples = 1;
+
+ watermark_max = INV_ICM45600_FIFO_SIZE_MAX / packet_size;
+ watermark_max -= grace_samples;
+
+ if (watermark > watermark_max)
+ watermark = watermark_max;
+
+ return watermark;
+}
+
+/**
+ * inv_icm45600_buffer_update_watermark - update watermark FIFO threshold
+ * @st: driver internal state
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ *
+ * FIFO watermark threshold is computed based on the required watermark values
+ * set for gyro and accel sensors. Since watermark is all about acceptable data
+ * latency, use the smallest setting between the 2. It means choosing the
+ * smallest latency but this is not as simple as choosing the smallest watermark
+ * value. Latency depends on watermark and ODR. It requires several steps:
+ * 1) compute gyro and accel latencies and choose the smallest value.
+ * 2) adapt the chosen latency so that it is a multiple of both gyro and accel
+ * ones. Otherwise it is possible that you don't meet a requirement. (for
+ * example with gyro @100Hz wm 4 and accel @100Hz with wm 6, choosing the
+ * value of 4 will not meet accel latency requirement because 6 is not a
+ * multiple of 4. You need to use the value 2.)
+ * 3) Since all periods are multiple of each others, watermark is computed by
+ * dividing this computed latency by the smallest period, which corresponds
+ * to the FIFO frequency.
+ */
+int inv_icm45600_buffer_update_watermark(struct inv_icm45600_state *st)
+{
+ const size_t packet_size = INV_ICM45600_FIFO_2SENSORS_PACKET_SIZE;
+ unsigned int wm_gyro, wm_accel, watermark;
+ u32 period_gyro, period_accel, period;
+ u32 latency_gyro, latency_accel, latency;
+
+ /* Compute sensors latency, depending on sensor watermark and odr. */
+ wm_gyro = inv_icm45600_wm_truncate(st->fifo.watermark.gyro, packet_size,
+ st->fifo.period);
+ wm_accel = inv_icm45600_wm_truncate(st->fifo.watermark.accel, packet_size,
+ st->fifo.period);
+ /* Use us for odr to avoid overflow using 32 bits values. */
+ period_gyro = inv_icm45600_odr_to_period(st->conf.gyro.odr) / 1000UL;
+ period_accel = inv_icm45600_odr_to_period(st->conf.accel.odr) / 1000UL;
+ latency_gyro = period_gyro * wm_gyro;
+ latency_accel = period_accel * wm_accel;
+
+ /* 0 value for watermark means that the sensor is turned off. */
+ if (wm_gyro == 0 && wm_accel == 0)
+ return 0;
+
+ if (latency_gyro == 0) {
+ watermark = wm_accel;
+ st->fifo.watermark.eff_accel = wm_accel;
+ } else if (latency_accel == 0) {
+ watermark = wm_gyro;
+ st->fifo.watermark.eff_gyro = wm_gyro;
+ } else {
+ /* Compute the smallest latency that is a multiple of both. */
+ if (latency_gyro <= latency_accel)
+ latency = latency_gyro - (latency_accel % latency_gyro);
+ else
+ latency = latency_accel - (latency_gyro % latency_accel);
+ /* Use the shortest period. */
+ if (period_gyro <= period_accel)
+ period = period_gyro;
+ else
+ period = period_accel;
+ /* All this works because periods are multiple of each others. */
+ watermark = latency / period;
+ if (watermark < 1)
+ watermark = 1;
+ /* Update effective watermark. */
+ st->fifo.watermark.eff_gyro = latency / period_gyro;
+ if (st->fifo.watermark.eff_gyro < 1)
+ st->fifo.watermark.eff_gyro = 1;
+ st->fifo.watermark.eff_accel = latency / period_accel;
+ if (st->fifo.watermark.eff_accel < 1)
+ st->fifo.watermark.eff_accel = 1;
+ }
+
+
+ st->buffer.u16 = cpu_to_le16(watermark);
+ return regmap_bulk_write(st->map, INV_ICM45600_REG_FIFO_WATERMARK,
+ &st->buffer.u16, sizeof(st->buffer.u16));
+}
+
+static int inv_icm45600_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ struct inv_icm45600_sensor_state *sensor_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &sensor_st->ts;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+ inv_sensors_timestamp_reset(ts);
+
+ return 0;
+}
+
+/*
+ * Update_scan_mode callback is turning sensors on and setting data FIFO enable
+ * bits.
+ */
+static int inv_icm45600_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ unsigned int val;
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ /* Exit if FIFO is already on. */
+ if (st->fifo.on) {
+ /* Increase FIFO on counter. */
+ st->fifo.on++;
+ return 0;
+ }
+
+ /* Flush all FIFO data. */
+ ret = regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG2,
+ INV_ICM45600_REG_FIFO_CONFIG2_FIFO_FLUSH);
+ if (ret)
+ return ret;
+
+ /* Set FIFO threshold and full interrupt. */
+ ret = regmap_set_bits(st->map, INV_ICM45600_REG_INT1_CONFIG0,
+ INV_ICM45600_INT1_CONFIG0_FIFO_THS_EN |
+ INV_ICM45600_INT1_CONFIG0_FIFO_FULL_EN);
+ if (ret)
+ return ret;
+
+ /* Set FIFO in streaming mode. */
+ val = FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK,
+ INV_ICM45600_FIFO_CONFIG0_MODE_STREAM);
+ ret = regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0,
+ INV_ICM45600_FIFO_CONFIG0_MODE_MASK, val);
+ if (ret)
+ return ret;
+
+ /* Enable writing sensor data to FIFO. */
+ ret = regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3,
+ INV_ICM45600_FIFO_CONFIG3_IF_EN);
+ if (ret)
+ return ret;
+
+ /* Increase FIFO on counter. */
+ st->fifo.on++;
+ return 0;
+}
+
+static int inv_icm45600_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ unsigned int val;
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ /* Exit if there are several sensors using the FIFO. */
+ if (st->fifo.on > 1) {
+ /* decrease FIFO on counter */
+ st->fifo.on--;
+ return 0;
+ }
+
+ /* Disable writing sensor data to FIFO. */
+ ret = regmap_clear_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3,
+ INV_ICM45600_FIFO_CONFIG3_IF_EN);
+ if (ret)
+ return ret;
+
+ /* Set FIFO in bypass mode. */
+ val = FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK,
+ INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS);
+ ret = regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0,
+ INV_ICM45600_FIFO_CONFIG0_MODE_MASK, val);
+ if (ret)
+ return ret;
+
+ /* Disable FIFO threshold and full interrupt. */
+ ret = regmap_clear_bits(st->map, INV_ICM45600_REG_INT1_CONFIG0,
+ INV_ICM45600_INT1_CONFIG0_FIFO_THS_EN |
+ INV_ICM45600_INT1_CONFIG0_FIFO_FULL_EN);
+ if (ret)
+ return ret;
+
+ /* Flush all FIFO data. */
+ ret = regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG2,
+ INV_ICM45600_REG_FIFO_CONFIG2_FIFO_FLUSH);
+ if (ret)
+ return ret;
+
+ /* Decrease FIFO on counter. */
+ st->fifo.on--;
+ return 0;
+}
+
+static int _inv_icm45600_buffer_postdisable(struct inv_icm45600_state *st,
+ unsigned int sensor, unsigned int *watermark,
+ unsigned int *sleep)
+{
+ struct inv_icm45600_sensor_conf conf = INV_ICM45600_SENSOR_CONF_INIT;
+ int ret;
+
+ ret = inv_icm45600_buffer_set_fifo_en(st, st->fifo.en & ~sensor);
+ if (ret)
+ return ret;
+
+ *watermark = 0;
+ ret = inv_icm45600_buffer_update_watermark(st);
+ if (ret)
+ return ret;
+
+ conf.mode = INV_ICM45600_SENSOR_MODE_OFF;
+ if (sensor == INV_ICM45600_SENSOR_GYRO)
+ ret = inv_icm45600_set_gyro_conf(st, &conf, sleep);
+ else
+ ret = inv_icm45600_set_accel_conf(st, &conf, sleep);
+
+ return ret;
+}
+
+static int inv_icm45600_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int sensor;
+ unsigned int *watermark;
+ unsigned int sleep;
+ int ret;
+
+ if (indio_dev == st->indio_gyro) {
+ sensor = INV_ICM45600_SENSOR_GYRO;
+ watermark = &st->fifo.watermark.gyro;
+ } else if (indio_dev == st->indio_accel) {
+ sensor = INV_ICM45600_SENSOR_ACCEL;
+ watermark = &st->fifo.watermark.accel;
+ } else {
+ return -EINVAL;
+ }
+
+ scoped_guard(mutex, &st->lock)
+ ret = _inv_icm45600_buffer_postdisable(st, sensor, watermark, &sleep);
+
+ /* Sleep required time. */
+ if (sleep)
+ msleep(sleep);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+const struct iio_buffer_setup_ops inv_icm45600_buffer_ops = {
+ .preenable = inv_icm45600_buffer_preenable,
+ .postenable = inv_icm45600_buffer_postenable,
+ .predisable = inv_icm45600_buffer_predisable,
+ .postdisable = inv_icm45600_buffer_postdisable,
+};
+
+int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st,
+ unsigned int max)
+{
+ const ssize_t packet_size = INV_ICM45600_FIFO_2SENSORS_PACKET_SIZE;
+ __le16 *raw_fifo_count;
+ size_t fifo_nb, i;
+ ssize_t size;
+ const struct inv_icm45600_fifo_sensor_data *accel, *gyro;
+ const __le16 *timestamp;
+ const s8 *temp;
+ unsigned int odr;
+ int ret;
+
+ /* Reset all samples counters. */
+ st->fifo.count = 0;
+ st->fifo.nb.gyro = 0;
+ st->fifo.nb.accel = 0;
+ st->fifo.nb.total = 0;
+
+ /* Read FIFO count value. */
+ raw_fifo_count = &st->buffer.u16;
+ ret = regmap_bulk_read(st->map, INV_ICM45600_REG_FIFO_COUNT,
+ raw_fifo_count, sizeof(*raw_fifo_count));
+ if (ret)
+ return ret;
+ fifo_nb = le16_to_cpup(raw_fifo_count);
+
+ /* Check and limit number of samples if requested. */
+ if (fifo_nb == 0)
+ return 0;
+ if (max > 0 && fifo_nb > max)
+ fifo_nb = max;
+
+ /* Try to read all FIFO data in internal buffer. */
+ st->fifo.count = fifo_nb * packet_size;
+ ret = regmap_noinc_read(st->map, INV_ICM45600_REG_FIFO_DATA,
+ st->fifo.data, st->fifo.count);
+ if (ret == -ENOTSUPP || ret == -EFBIG) {
+ /* Read full fifo is not supported, read samples one by one. */
+ ret = 0;
+ for (i = 0; i < st->fifo.count && ret == 0; i += packet_size)
+ ret = regmap_noinc_read(st->map, INV_ICM45600_REG_FIFO_DATA,
+ &st->fifo.data[i], packet_size);
+ }
+ if (ret)
+ return ret;
+
+ for (i = 0; i < st->fifo.count; i += size) {
+ size = inv_icm45600_fifo_decode_packet(&st->fifo.data[i],
+ &accel, &gyro, &temp, ×tamp, &odr);
+ if (size <= 0)
+ break;
+ if (gyro != NULL && inv_icm45600_fifo_is_data_valid(gyro))
+ st->fifo.nb.gyro++;
+ if (accel != NULL && inv_icm45600_fifo_is_data_valid(accel))
+ st->fifo.nb.accel++;
+ st->fifo.nb.total++;
+ }
+
+ return 0;
+}
+
+int inv_icm45600_buffer_init(struct inv_icm45600_state *st)
+{
+ int ret;
+ unsigned int val;
+
+ st->fifo.watermark.eff_gyro = 1;
+ st->fifo.watermark.eff_accel = 1;
+
+ /* Disable all FIFO EN bits. */
+ ret = regmap_write(st->map, INV_ICM45600_REG_FIFO_CONFIG3, 0);
+ if (ret)
+ return ret;
+
+ /* Disable FIFO and set depth. */
+ val = FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK,
+ INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS);
+ val |= INV_ICM45600_FIFO_CONFIG0_FIFO_DEPTH_MAX;
+ ret = regmap_write(st->map, INV_ICM45600_REG_FIFO_CONFIG0, val);
+ if (ret)
+ return ret;
+
+ /* Enable only timestamp in fifo, disable compression. */
+ ret = regmap_write(st->map, INV_ICM45600_REG_FIFO_CONFIG4,
+ INV_ICM45600_FIFO_CONFIG4_TMST_FSYNC_EN);
+ if (ret)
+ return ret;
+
+ /* Enable FIFO continuous watermark interrupt. */
+ return regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG2,
+ INV_ICM45600_REG_FIFO_CONFIG2_WM_GT_TH);
+}
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h
new file mode 100644
index 0000000000000000000000000000000000000000..09595a41cf637a3ba9bc44e4df53a9d0aa11f485
--- /dev/null
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2025 Invensense, Inc. */
+
+#ifndef INV_ICM45600_BUFFER_H_
+#define INV_ICM45600_BUFFER_H_
+
+#include <linux/bits.h>
+#include <linux/iio/iio.h>
+#include <linux/types.h>
+
+struct inv_icm45600_state;
+
+#define INV_ICM45600_SENSOR_GYRO BIT(0)
+#define INV_ICM45600_SENSOR_ACCEL BIT(1)
+#define INV_ICM45600_SENSOR_TEMP BIT(2)
+
+/**
+ * struct inv_icm45600_fifo - FIFO state variables
+ * @on: reference counter for FIFO on.
+ * @en: bits field of INV_ICM45600_SENSOR_* for FIFO EN bits.
+ * @period: FIFO internal period.
+ * @watermark: watermark configuration values for accel and gyro.
+ * @count: number of bytes in the FIFO data buffer.
+ * @nb: gyro, accel and total samples in the FIFO data buffer.
+ * @data: FIFO data buffer aligned for DMA (8kB)
+ */
+struct inv_icm45600_fifo {
+ unsigned int on;
+ unsigned int en;
+ u32 period;
+ struct {
+ unsigned int gyro;
+ unsigned int accel;
+ unsigned int eff_gyro;
+ unsigned int eff_accel;
+ } watermark;
+ size_t count;
+ struct {
+ size_t gyro;
+ size_t accel;
+ size_t total;
+ } nb;
+ u8 data[8192] __aligned(IIO_DMA_MINALIGN);
+};
+
+/* FIFO data packet */
+struct inv_icm45600_fifo_sensor_data {
+ __le16 x;
+ __le16 y;
+ __le16 z;
+} __packed;
+#define INV_ICM45600_FIFO_DATA_INVALID -32768
+
+static inline s16 inv_icm45600_fifo_get_sensor_data(__le16 d)
+{
+ return le16_to_cpu(d);
+}
+
+static inline bool
+inv_icm45600_fifo_is_data_valid(const struct inv_icm45600_fifo_sensor_data *s)
+{
+ s16 x, y, z;
+
+ x = inv_icm45600_fifo_get_sensor_data(s->x);
+ y = inv_icm45600_fifo_get_sensor_data(s->y);
+ z = inv_icm45600_fifo_get_sensor_data(s->z);
+
+ if (x == INV_ICM45600_FIFO_DATA_INVALID &&
+ y == INV_ICM45600_FIFO_DATA_INVALID &&
+ z == INV_ICM45600_FIFO_DATA_INVALID)
+ return false;
+
+ return true;
+}
+
+ssize_t inv_icm45600_fifo_decode_packet(const void *packet,
+ const struct inv_icm45600_fifo_sensor_data **accel,
+ const struct inv_icm45600_fifo_sensor_data **gyro,
+ const s8 **temp,
+ const __le16 **timestamp, unsigned int *odr);
+
+extern const struct iio_buffer_setup_ops inv_icm45600_buffer_ops;
+
+int inv_icm45600_buffer_init(struct inv_icm45600_state *st);
+
+void inv_icm45600_buffer_update_fifo_period(struct inv_icm45600_state *st);
+
+int inv_icm45600_buffer_set_fifo_en(struct inv_icm45600_state *st,
+ unsigned int fifo_en);
+
+int inv_icm45600_buffer_update_watermark(struct inv_icm45600_state *st);
+
+int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st,
+ unsigned int max);
+
+int inv_icm45600_buffer_hwfifo_flush(struct inv_icm45600_state *st,
+ unsigned int count);
+
+#endif
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
index 022eb9180b5a750e8fba4a1cd873041c27f59880..37487e9174bf53f2fc6583f37ac7542ea31b0643 100644
--- a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
@@ -16,6 +16,7 @@
#include <linux/regulator/consumer.h>
#include <linux/types.h>
+#include "inv_icm45600_buffer.h"
#include "inv_icm45600.h"
static int inv_icm45600_ireg_read(struct regmap *map, unsigned int reg,
@@ -521,6 +522,95 @@ static int inv_icm45600_setup(struct inv_icm45600_state *st,
return inv_icm45600_set_conf(st, chip_info->conf);
}
+static irqreturn_t inv_icm45600_irq_timestamp(int irq, void *_data)
+{
+ struct inv_icm45600_state *st = _data;
+
+ st->timestamp.gyro = iio_get_time_ns(st->indio_gyro);
+ st->timestamp.accel = iio_get_time_ns(st->indio_accel);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t inv_icm45600_irq_handler(int irq, void *_data)
+{
+ struct inv_icm45600_state *st = _data;
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int mask, status;
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ ret = regmap_read(st->map, INV_ICM45600_REG_INT_STATUS, &status);
+ if (ret)
+ return IRQ_HANDLED;
+
+ /* Read the FIFO data. */
+ mask = INV_ICM45600_INT_STATUS_FIFO_THS | INV_ICM45600_INT_STATUS_FIFO_FULL;
+ if (status & mask) {
+ ret = inv_icm45600_buffer_fifo_read(st, 0);
+ if (ret) {
+ dev_err(dev, "FIFO read error %d\n", ret);
+ return IRQ_HANDLED;
+ }
+ }
+
+ /* FIFO full warning. */
+ if (status & INV_ICM45600_INT_STATUS_FIFO_FULL)
+ dev_warn(dev, "FIFO full possible data lost!\n");
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * inv_icm45600_irq_init() - initialize int pin and interrupt handler
+ * @st: driver internal state
+ * @irq: irq number
+ * @irq_type: irq trigger type
+ * @open_drain: true if irq is open drain, false for push-pull
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+static int inv_icm45600_irq_init(struct inv_icm45600_state *st, int irq,
+ int irq_type, bool open_drain)
+{
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int val;
+ int ret;
+
+ /* Configure INT1 interrupt: default is active low on edge. */
+ switch (irq_type) {
+ case IRQF_TRIGGER_RISING:
+ case IRQF_TRIGGER_HIGH:
+ val = INV_ICM45600_INT1_CONFIG2_ACTIVE_HIGH;
+ break;
+ default:
+ val = INV_ICM45600_INT1_CONFIG2_ACTIVE_LOW;
+ break;
+ }
+
+ switch (irq_type) {
+ case IRQF_TRIGGER_LOW:
+ case IRQF_TRIGGER_HIGH:
+ val |= INV_ICM45600_INT1_CONFIG2_LATCHED;
+ break;
+ default:
+ break;
+ }
+
+ if (!open_drain)
+ val |= INV_ICM45600_INT1_CONFIG2_PUSH_PULL;
+
+ ret = regmap_write(st->map, INV_ICM45600_REG_INT1_CONFIG2, val);
+ if (ret)
+ return ret;
+
+ irq_type |= IRQF_ONESHOT;
+ return devm_request_threaded_irq(dev, irq, inv_icm45600_irq_timestamp,
+ inv_icm45600_irq_handler, irq_type,
+ "inv_icm45600", st);
+}
+
static int inv_icm45600_timestamp_setup(struct inv_icm45600_state *st)
{
/* Enable timestamps. */
@@ -627,6 +717,14 @@ int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm45600_chi
if (ret)
return ret;
+ ret = inv_icm45600_buffer_init(st);
+ if (ret)
+ return ret;
+
+ ret = inv_icm45600_irq_init(st, irq, irq_type, open_drain);
+ if (ret)
+ return ret;
+
/* Setup runtime power management. */
ret = devm_pm_runtime_set_active_enabled(dev);
if (ret)
@@ -658,6 +756,22 @@ static int inv_icm45600_suspend(struct device *dev)
if (pm_runtime_suspended(dev))
return 0;
+ /* Disable FIFO data streaming. */
+ if (st->fifo.on) {
+ unsigned int val;
+
+ ret = regmap_clear_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3,
+ INV_ICM45600_FIFO_CONFIG3_IF_EN);
+ if (ret)
+ return ret;
+ val = FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK,
+ INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS);
+ ret = regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0,
+ INV_ICM45600_FIFO_CONFIG0_MODE_MASK, val);
+ if (ret)
+ return ret;
+ }
+
ret = inv_icm45600_set_pwr_mgmt0(st, INV_ICM45600_SENSOR_MODE_OFF,
INV_ICM45600_SENSOR_MODE_OFF, NULL);
if (ret)
@@ -675,6 +789,8 @@ static int inv_icm45600_suspend(struct device *dev)
static int inv_icm45600_resume(struct device *dev)
{
struct inv_icm45600_state *st = dev_get_drvdata(dev);
+ struct inv_icm45600_sensor_state *gyro_st = iio_priv(st->indio_gyro);
+ struct inv_icm45600_sensor_state *accel_st = iio_priv(st->indio_accel);
int ret;
guard(mutex)(&st->lock);
@@ -688,9 +804,28 @@ static int inv_icm45600_resume(struct device *dev)
pm_runtime_enable(dev);
/* Restore sensors state. */
- return inv_icm45600_set_pwr_mgmt0(st, st->suspended.gyro,
+ ret = inv_icm45600_set_pwr_mgmt0(st, st->suspended.gyro,
st->suspended.accel, NULL);
+ if (ret)
+ return ret;
+
+ /* Restore FIFO data streaming. */
+ if (st->fifo.on) {
+ unsigned int val;
+ inv_sensors_timestamp_reset(&gyro_st->ts);
+ inv_sensors_timestamp_reset(&accel_st->ts);
+ val = FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK,
+ INV_ICM45600_FIFO_CONFIG0_MODE_STREAM);
+ ret = regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0,
+ INV_ICM45600_FIFO_CONFIG0_MODE_MASK, val);
+ if (ret)
+ return ret;
+ ret = regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3,
+ INV_ICM45600_FIFO_CONFIG3_IF_EN);
+ }
+
+ return ret;
}
/* Runtime suspend will turn off sensors that are enabled by iio devices. */
--
2.34.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v2 4/8] iio: imu: inv_icm45600: add IMU IIO devices
2025-07-10 8:57 [PATCH v2 0/8] iio: imu: new inv_icm45600 driver Remi Buisson via B4 Relay
` (2 preceding siblings ...)
2025-07-10 8:57 ` [PATCH v2 3/8] iio: imu: inv_icm45600: add buffer support in iio devices Remi Buisson via B4 Relay
@ 2025-07-10 8:57 ` Remi Buisson via B4 Relay
2025-07-17 14:47 ` Jonathan Cameron
2025-07-10 8:58 ` [PATCH v2 5/8] iio: imu: inv_icm45600: add I2C driver for inv_icm45600 driver Remi Buisson via B4 Relay
` (4 subsequent siblings)
8 siblings, 1 reply; 46+ messages in thread
From: Remi Buisson via B4 Relay @ 2025-07-10 8:57 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-kernel, linux-iio, devicetree, Remi Buisson
From: Remi Buisson <remi.buisson@tdk.com>
Add IIO devices for accelerometer and gyroscope sensors
with data polling interface and FIFO parsing.
Attributes: raw, scale, sampling_frequency, calibbias.
Temperature is available as a processed channel.
Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
---
drivers/iio/imu/inv_icm45600/Kconfig | 2 +
drivers/iio/imu/inv_icm45600/Makefile | 4 +-
drivers/iio/imu/inv_icm45600/inv_icm45600.h | 9 +
drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c | 798 ++++++++++++++++++++
drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c | 71 ++
drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h | 2 +
drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 122 ++++
drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c | 809 +++++++++++++++++++++
8 files changed, 1816 insertions(+), 1 deletion(-)
diff --git a/drivers/iio/imu/inv_icm45600/Kconfig b/drivers/iio/imu/inv_icm45600/Kconfig
index 8cb5543e0a5817323ab7b2d520dd3430ac5dbc99..ea0a8d20cba26549b74105fa6fdbca1ddb222633 100644
--- a/drivers/iio/imu/inv_icm45600/Kconfig
+++ b/drivers/iio/imu/inv_icm45600/Kconfig
@@ -2,4 +2,6 @@
config INV_ICM45600
tristate
+ select IIO_BUFFER
+ select IIO_KFIFO_BUF
select IIO_INV_SENSORS_TIMESTAMP
diff --git a/drivers/iio/imu/inv_icm45600/Makefile b/drivers/iio/imu/inv_icm45600/Makefile
index 19c521ffba17b0d108a8ecb45ecdea35dff6fd18..ced5e1d9f2c10400cfa4878146672d41f815c22a 100644
--- a/drivers/iio/imu/inv_icm45600/Makefile
+++ b/drivers/iio/imu/inv_icm45600/Makefile
@@ -2,4 +2,6 @@
obj-$(CONFIG_INV_ICM45600) += inv-icm45600.o
inv-icm45600-y += inv_icm45600_core.o
-inv-icm45600-y += inv_icm45600_buffer.o
\ No newline at end of file
+inv-icm45600-y += inv_icm45600_buffer.o
+inv-icm45600-y += inv_icm45600_accel.o
+inv-icm45600-y += inv_icm45600_gyro.o
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600.h b/drivers/iio/imu/inv_icm45600/inv_icm45600.h
index 0b97c54f74b30aef3842e34da098c2443347de00..dad53abf1b867da7ad2bc88c222031de791fed16 100644
--- a/drivers/iio/imu/inv_icm45600/inv_icm45600.h
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h
@@ -107,6 +107,10 @@ struct inv_icm45600_chip_info {
u8 whoami;
const char *name;
const struct inv_icm45600_conf *conf;
+ const int *accel_scales;
+ const int accel_scales_len;
+ const int *gyro_scales;
+ const int gyro_scales_len;
};
extern const struct inv_icm45600_chip_info inv_icm45605_chip_info;
@@ -118,6 +122,11 @@ extern const struct inv_icm45600_chip_info inv_icm45687_chip_info;
extern const struct inv_icm45600_chip_info inv_icm45688p_chip_info;
extern const struct inv_icm45600_chip_info inv_icm45689_chip_info;
+extern const int inv_icm45600_accel_scale[][2];
+extern const int inv_icm45686_accel_scale[][2];
+extern const int inv_icm45600_gyro_scale[][2];
+extern const int inv_icm45686_gyro_scale[][2];
+
/**
* struct inv_icm45600_state - driver state variables
* @lock: lock for serializing multiple registers access.
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c
new file mode 100644
index 0000000000000000000000000000000000000000..f0a5e58c95fa3f6c05524771dd03a5b3026b7a48
--- /dev/null
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c
@@ -0,0 +1,798 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2025 Invensense, Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/common/inv_sensors_timestamp.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/math64.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include "inv_icm45600_buffer.h"
+#include "inv_icm45600.h"
+
+enum inv_icm45600_accel_scan {
+ INV_ICM45600_ACCEL_SCAN_X,
+ INV_ICM45600_ACCEL_SCAN_Y,
+ INV_ICM45600_ACCEL_SCAN_Z,
+ INV_ICM45600_ACCEL_SCAN_TEMP,
+ INV_ICM45600_ACCEL_SCAN_TIMESTAMP,
+};
+
+static const struct iio_chan_spec_ext_info inv_icm45600_accel_ext_infos[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm45600_get_mount_matrix),
+ { },
+};
+
+#define INV_ICM45600_ACCEL_CHAN(_modifier, _index, _ext_info) \
+ { \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = _modifier, \
+ .info_mask_separate = \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS), \
+ .info_mask_shared_by_type = \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS), \
+ .info_mask_shared_by_all = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+ }, \
+ .ext_info = _ext_info, \
+ }
+
+static const struct iio_chan_spec inv_icm45600_accel_channels[] = {
+ INV_ICM45600_ACCEL_CHAN(IIO_MOD_X, INV_ICM45600_ACCEL_SCAN_X,
+ inv_icm45600_accel_ext_infos),
+ INV_ICM45600_ACCEL_CHAN(IIO_MOD_Y, INV_ICM45600_ACCEL_SCAN_Y,
+ inv_icm45600_accel_ext_infos),
+ INV_ICM45600_ACCEL_CHAN(IIO_MOD_Z, INV_ICM45600_ACCEL_SCAN_Z,
+ inv_icm45600_accel_ext_infos),
+ INV_ICM45600_TEMP_CHAN(INV_ICM45600_ACCEL_SCAN_TEMP),
+ IIO_CHAN_SOFT_TIMESTAMP(INV_ICM45600_ACCEL_SCAN_TIMESTAMP),
+};
+
+/*
+ * IIO buffer data: size must be a power of 2 and timestamp aligned
+ * 16 bytes: 6 bytes acceleration, 2 bytes temperature, 8 bytes timestamp
+ */
+struct inv_icm45600_accel_buffer {
+ struct inv_icm45600_fifo_sensor_data accel;
+ s16 temp;
+ aligned_s64 timestamp;
+};
+
+static const unsigned long inv_icm45600_accel_scan_masks[] = {
+ /* 3-axis accel + temperature */
+ BIT(INV_ICM45600_ACCEL_SCAN_X) |
+ BIT(INV_ICM45600_ACCEL_SCAN_Y) |
+ BIT(INV_ICM45600_ACCEL_SCAN_Z) |
+ BIT(INV_ICM45600_ACCEL_SCAN_TEMP),
+ 0,
+};
+
+/* enable accelerometer sensor and FIFO write */
+static int inv_icm45600_accel_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm45600_sensor_state *accel_st = iio_priv(indio_dev);
+ struct inv_icm45600_sensor_conf conf = INV_ICM45600_SENSOR_CONF_INIT;
+ unsigned int fifo_en = 0;
+ unsigned int sleep;
+ int ret;
+
+ scoped_guard(mutex, &st->lock) {
+ if (*scan_mask & BIT(INV_ICM45600_ACCEL_SCAN_TEMP))
+ fifo_en |= INV_ICM45600_SENSOR_TEMP;
+
+ if (*scan_mask & (BIT(INV_ICM45600_ACCEL_SCAN_X) |
+ BIT(INV_ICM45600_ACCEL_SCAN_Y) |
+ BIT(INV_ICM45600_ACCEL_SCAN_Z))) {
+ /* enable accel sensor */
+ conf.mode = accel_st->power_mode;
+ ret = inv_icm45600_set_accel_conf(st, &conf, &sleep);
+ if (ret)
+ return ret;
+ fifo_en |= INV_ICM45600_SENSOR_ACCEL;
+ }
+
+ /* Update data FIFO write. */
+ ret = inv_icm45600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
+ }
+
+ /* Sleep required time. */
+ if (sleep)
+ msleep(sleep);
+
+ return ret;
+}
+
+static int _inv_icm45600_accel_read_sensor(struct inv_icm45600_state *st,
+ struct inv_icm45600_sensor_state *accel_st,
+ unsigned int reg, int *val)
+{
+ struct inv_icm45600_sensor_conf conf = INV_ICM45600_SENSOR_CONF_INIT;
+ int ret;
+
+ /* enable accel sensor */
+ conf.mode = accel_st->power_mode;
+ ret = inv_icm45600_set_accel_conf(st, &conf, NULL);
+ if (ret)
+ return ret;
+
+ /* read accel register data */
+ ret = regmap_bulk_read(st->map, reg, &st->buffer.u16, sizeof(st->buffer.u16));
+ if (ret)
+ return ret;
+
+ *val = sign_extend32(le16_to_cpup(&st->buffer.u16), 15);
+ if (*val == INV_ICM45600_DATA_INVALID)
+ return -ENODATA;
+
+ return 0;
+}
+
+static int inv_icm45600_accel_read_sensor(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm45600_sensor_state *accel_st = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int reg;
+ int ret;
+
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ reg = INV_ICM45600_REG_ACCEL_DATA_X;
+ break;
+ case IIO_MOD_Y:
+ reg = INV_ICM45600_REG_ACCEL_DATA_Y;
+ break;
+ case IIO_MOD_Z:
+ reg = INV_ICM45600_REG_ACCEL_DATA_Z;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ scoped_guard(mutex, &st->lock)
+ ret = _inv_icm45600_accel_read_sensor(st, accel_st, reg, val);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+/* IIO format int + nano */
+const int inv_icm45600_accel_scale[][2] = {
+ /* +/- 16G => 0.004788403 m/s-2 */
+ [INV_ICM45600_ACCEL_FS_16G] = {0, 4788403},
+ /* +/- 8G => 0.002394202 m/s-2 */
+ [INV_ICM45600_ACCEL_FS_8G] = {0, 2394202},
+ /* +/- 4G => 0.001197101 m/s-2 */
+ [INV_ICM45600_ACCEL_FS_4G] = {0, 1197101},
+ /* +/- 2G => 0.000598550 m/s-2 */
+ [INV_ICM45600_ACCEL_FS_2G] = {0, 598550},
+};
+
+const int inv_icm45686_accel_scale[][2] = {
+ /* +/- 32G => 0.009576806 m/s-2 */
+ [INV_ICM45686_ACCEL_FS_32G] = {0, 9576806},
+ /* +/- 16G => 0.004788403 m/s-2 */
+ [INV_ICM45686_ACCEL_FS_16G] = {0, 4788403},
+ /* +/- 8G => 0.002394202 m/s-2 */
+ [INV_ICM45686_ACCEL_FS_8G] = {0, 2394202},
+ /* +/- 4G => 0.001197101 m/s-2 */
+ [INV_ICM45686_ACCEL_FS_4G] = {0, 1197101},
+ /* +/- 2G => 0.000598550 m/s-2 */
+ [INV_ICM45686_ACCEL_FS_2G] = {0, 598550},
+};
+
+static int inv_icm45600_accel_read_scale(struct iio_dev *indio_dev,
+ int *val, int *val2)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm45600_sensor_state *accel_st = iio_priv(indio_dev);
+ unsigned int idx;
+
+ idx = st->conf.accel.fs;
+
+ /* Full scale register starts at 1 for not High FSR parts */
+ if (accel_st->scales == (const int *)&inv_icm45600_accel_scale)
+ idx--;
+
+ *val = accel_st->scales[2 * idx];
+ *val2 = accel_st->scales[2 * idx + 1];
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int inv_icm45600_accel_write_scale(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm45600_sensor_state *accel_st = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+ struct inv_icm45600_sensor_conf conf = INV_ICM45600_SENSOR_CONF_INIT;
+ int ret;
+
+ for (idx = 0; idx < accel_st->scales_len; idx += 2) {
+ if (val == accel_st->scales[idx] &&
+ val2 == accel_st->scales[idx + 1])
+ break;
+ }
+ if (idx == accel_st->scales_len)
+ return -EINVAL;
+
+ conf.fs = idx / 2;
+
+ /* Full scale register starts at 1 for not High FSR parts */
+ if (accel_st->scales == (const int *)&inv_icm45600_accel_scale)
+ conf.fs++;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ scoped_guard(mutex, &st->lock)
+ ret = inv_icm45600_set_accel_conf(st, &conf, NULL);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+/* IIO format int + micro */
+static const int inv_icm45600_accel_odr[] = {
+ /* 1.5625Hz */
+ 1, 562500,
+ /* 3.125Hz */
+ 3, 125000,
+ /* 6.25Hz */
+ 6, 250000,
+ /* 12.5Hz */
+ 12, 500000,
+ /* 25Hz */
+ 25, 0,
+ /* 50Hz */
+ 50, 0,
+ /* 100Hz */
+ 100, 0,
+ /* 200Hz */
+ 200, 0,
+ /* 400Hz */
+ 400, 0,
+ /* 800Hz */
+ 800, 0,
+ /* 1.6kHz */
+ 1600, 0,
+ /* 3.2kHz */
+ 3200, 0,
+ /* 6.4kHz */
+ 6400, 0,
+};
+
+static const int inv_icm45600_accel_odr_conv[] = {
+ INV_ICM45600_ODR_1_5625HZ_LP,
+ INV_ICM45600_ODR_3_125HZ_LP,
+ INV_ICM45600_ODR_6_25HZ_LP,
+ INV_ICM45600_ODR_12_5HZ,
+ INV_ICM45600_ODR_25HZ,
+ INV_ICM45600_ODR_50HZ,
+ INV_ICM45600_ODR_100HZ,
+ INV_ICM45600_ODR_200HZ,
+ INV_ICM45600_ODR_400HZ,
+ INV_ICM45600_ODR_800HZ_LN,
+ INV_ICM45600_ODR_1600HZ_LN,
+ INV_ICM45600_ODR_3200HZ_LN,
+ INV_ICM45600_ODR_6400HZ_LN,
+};
+
+static int inv_icm45600_accel_read_odr(struct inv_icm45600_state *st,
+ int *val, int *val2)
+{
+ unsigned int odr;
+ unsigned int i;
+
+ odr = st->conf.accel.odr;
+
+ for (i = 0; i < ARRAY_SIZE(inv_icm45600_accel_odr_conv); ++i) {
+ if (inv_icm45600_accel_odr_conv[i] == odr)
+ break;
+ }
+ if (i >= ARRAY_SIZE(inv_icm45600_accel_odr_conv))
+ return -EINVAL;
+
+ *val = inv_icm45600_accel_odr[2 * i];
+ *val2 = inv_icm45600_accel_odr[2 * i + 1];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int _inv_icm45600_accel_write_odr(struct iio_dev *indio_dev, int odr)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm45600_sensor_state *accel_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &accel_st->ts;
+ struct inv_icm45600_sensor_conf conf = INV_ICM45600_SENSOR_CONF_INIT;
+ int ret;
+
+ conf.odr = odr;
+ ret = inv_sensors_timestamp_update_odr(ts, inv_icm45600_odr_to_period(conf.odr),
+ iio_buffer_enabled(indio_dev));
+ if (ret)
+ return ret;
+
+ if (st->conf.accel.mode != INV_ICM45600_SENSOR_MODE_OFF)
+ conf.mode = accel_st->power_mode;
+
+ ret = inv_icm45600_set_accel_conf(st, &conf, NULL);
+ if (ret)
+ return ret;
+
+ inv_icm45600_buffer_update_fifo_period(st);
+ inv_icm45600_buffer_update_watermark(st);
+
+ return 0;
+}
+
+static int inv_icm45600_accel_write_odr(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+ int odr;
+ int ret;
+
+ for (idx = 0; idx < ARRAY_SIZE(inv_icm45600_accel_odr); idx += 2) {
+ if (val == inv_icm45600_accel_odr[idx] &&
+ val2 == inv_icm45600_accel_odr[idx + 1])
+ break;
+ }
+ if (idx >= ARRAY_SIZE(inv_icm45600_accel_odr))
+ return -EINVAL;
+
+ odr = inv_icm45600_accel_odr_conv[idx / 2];
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ scoped_guard(mutex, &st->lock)
+ ret = _inv_icm45600_accel_write_odr(indio_dev, odr);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+/*
+ * Calibration bias values, IIO range format int + micro.
+ * Value is limited to +/-1g coded on 14 bits signed. Step is 0.125mg.
+ */
+static int inv_icm45600_accel_calibbias[] = {
+ -9, 806650, /* min: -9.806650 m/s² */
+ 0, 1197, /* step: 0.001197 m/s² */
+ 9, 805453, /* max: 9.805453 m/s² */
+};
+
+static int inv_icm45600_accel_read_offset(struct inv_icm45600_state *st,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2)
+{
+ struct device *dev = regmap_get_device(st->map);
+ s64 val64;
+ s32 bias;
+ unsigned int reg;
+ s16 offset;
+ int ret;
+
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ reg = INV_ICM45600_IPREG_SYS2_REG_24;
+ break;
+ case IIO_MOD_Y:
+ reg = INV_ICM45600_IPREG_SYS2_REG_32;
+ break;
+ case IIO_MOD_Z:
+ reg = INV_ICM45600_IPREG_SYS2_REG_40;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ scoped_guard(mutex, &st->lock)
+ ret = regmap_bulk_read(st->map, reg, &st->buffer.u16, sizeof(st->buffer.u16));
+
+ pm_runtime_put_autosuspend(dev);
+ if (ret)
+ return ret;
+
+ offset = le16_to_cpup(&st->buffer.u16) & INV_ICM45600_ACCEL_OFFUSER_MASK;
+ /* 14 bits signed value */
+ offset = sign_extend32(offset, 13);
+
+ /*
+ * convert raw offset to g then to m/s²
+ * 14 bits signed raw step 1/8192g
+ * g to m/s²: 9.806650
+ * result in micro (* 1000000)
+ * (offset * 9806650) / 8192
+ */
+ val64 = (s64)offset * 9806650LL;
+ /* for rounding, add + or - divisor (8192) divided by 2 */
+ if (val64 >= 0)
+ val64 += 8192LL / 2LL;
+ else
+ val64 -= 8192LL / 2LL;
+ bias = div_s64(val64, 8192L);
+ *val = bias / 1000000L;
+ *val2 = bias % 1000000L;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int inv_icm45600_accel_write_offset(struct inv_icm45600_state *st,
+ struct iio_chan_spec const *chan,
+ int val, int val2)
+{
+ struct device *dev = regmap_get_device(st->map);
+ s64 val64;
+ s32 min, max;
+ unsigned int reg;
+ s16 offset;
+ int ret;
+
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ reg = INV_ICM45600_IPREG_SYS2_REG_24;
+ break;
+ case IIO_MOD_Y:
+ reg = INV_ICM45600_IPREG_SYS2_REG_32;
+ break;
+ case IIO_MOD_Z:
+ reg = INV_ICM45600_IPREG_SYS2_REG_40;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* inv_icm45600_accel_calibbias: min - step - max in micro */
+ min = inv_icm45600_accel_calibbias[0] * 1000000L -
+ inv_icm45600_accel_calibbias[1];
+ max = inv_icm45600_accel_calibbias[4] * 1000000L +
+ inv_icm45600_accel_calibbias[5];
+ val64 = (s64)val * 1000000LL;
+ if (val > 0)
+ val64 += (s64)val2;
+ else
+ val64 -= (s64)val2;
+ if (val64 < min || val64 > max)
+ return -EINVAL;
+
+ /*
+ * convert m/s² to g then to raw value
+ * m/s² to g: 1 / 9.806650
+ * g to raw 14 bits signed, step 1/8192g: * 8192
+ * val in micro (1000000)
+ * val * 8192 / (9.806650 * 1000000)
+ */
+ val64 = val64 * 8192LL;
+ /* for rounding, add + or - divisor (9806650) divided by 2 */
+ if (val64 >= 0)
+ val64 += 9806650 / 2;
+ else
+ val64 -= 9806650 / 2;
+ offset = div_s64(val64, 9806650);
+
+ /* clamp value limited to 14 bits signed */
+ if (offset < -8192)
+ offset = -8192;
+ else if (offset > 8191)
+ offset = 8191;
+
+ st->buffer.u16 = cpu_to_le16(offset & INV_ICM45600_ACCEL_OFFUSER_MASK);
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ scoped_guard(mutex, &st->lock)
+ ret = regmap_bulk_write(st->map, reg, &st->buffer.u16, sizeof(st->buffer.u16));
+
+ pm_runtime_put_autosuspend(dev);
+ return ret;
+}
+
+static int inv_icm45600_accel_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ int ret;
+
+ switch (chan->type) {
+ case IIO_ACCEL:
+ break;
+ case IIO_TEMP:
+ return inv_icm45600_temp_read_raw(indio_dev, chan, val, val2, mask);
+ default:
+ return -EINVAL;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = inv_icm45600_accel_read_sensor(indio_dev, chan, val);
+ iio_device_release_direct(indio_dev);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ return inv_icm45600_accel_read_scale(indio_dev, val, val2);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm45600_accel_read_odr(st, val, val2);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return inv_icm45600_accel_read_offset(st, chan, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm45600_accel_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals,
+ int *type, int *length, long mask)
+{
+ struct inv_icm45600_sensor_state *accel_st = iio_priv(indio_dev);
+
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *vals = accel_st->scales;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *length = accel_st->scales_len;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = inv_icm45600_accel_odr;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *length = ARRAY_SIZE(inv_icm45600_accel_odr);
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ *vals = inv_icm45600_accel_calibbias;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_RANGE;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm45600_accel_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ int ret;
+
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = inv_icm45600_accel_write_scale(indio_dev, val, val2);
+ iio_device_release_direct(indio_dev);
+ return ret;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm45600_accel_write_odr(indio_dev, val, val2);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = inv_icm45600_accel_write_offset(st, chan, val, val2);
+ iio_device_release_direct(indio_dev);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm45600_accel_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm45600_accel_hwfifo_set_watermark(struct iio_dev *indio_dev,
+ unsigned int val)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+
+ guard(mutex)(&st->lock);
+
+ st->fifo.watermark.accel = val;
+ return inv_icm45600_buffer_update_watermark(st);
+}
+
+static int inv_icm45600_accel_hwfifo_flush(struct iio_dev *indio_dev,
+ unsigned int count)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ int ret;
+
+ if (count == 0)
+ return 0;
+
+ guard(mutex)(&st->lock);
+
+ ret = inv_icm45600_buffer_hwfifo_flush(st, count);
+ if (!ret)
+ ret = st->fifo.nb.accel;
+
+ return ret;
+}
+
+static const struct iio_info inv_icm45600_accel_info = {
+ .read_raw = inv_icm45600_accel_read_raw,
+ .read_avail = inv_icm45600_accel_read_avail,
+ .write_raw = inv_icm45600_accel_write_raw,
+ .write_raw_get_fmt = inv_icm45600_accel_write_raw_get_fmt,
+ .debugfs_reg_access = inv_icm45600_debugfs_reg,
+ .update_scan_mode = inv_icm45600_accel_update_scan_mode,
+ .hwfifo_set_watermark = inv_icm45600_accel_hwfifo_set_watermark,
+ .hwfifo_flush_to_buffer = inv_icm45600_accel_hwfifo_flush,
+};
+
+struct iio_dev *inv_icm45600_accel_init(struct inv_icm45600_state *st)
+{
+ struct device *dev = regmap_get_device(st->map);
+ const char *name;
+ struct inv_icm45600_sensor_state *accel_st;
+ struct inv_sensors_timestamp_chip ts_chip;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "%s-accel", st->chip_info->name);
+ if (!name)
+ return ERR_PTR(-ENOMEM);
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*accel_st));
+ if (!indio_dev)
+ return ERR_PTR(-ENOMEM);
+ accel_st = iio_priv(indio_dev);
+
+ accel_st->scales = st->chip_info->accel_scales;
+ accel_st->scales_len = st->chip_info->accel_scales_len * 2;
+
+ /* low-power (LP) mode by default at init, no ULP mode */
+ accel_st->power_mode = INV_ICM45600_SENSOR_MODE_LOW_POWER;
+ ret = regmap_set_bits(st->map, INV_ICM45600_REG_SMC_CONTROL_0,
+ INV_ICM45600_SMC_CONTROL_0_ACCEL_LP_CLK_SEL);
+ if (ret)
+ return ERR_PTR(ret);
+
+ /*
+ * clock period is 32kHz (31250ns)
+ * jitter is +/- 2% (20 per mille)
+ */
+ ts_chip.clock_period = 31250;
+ ts_chip.jitter = 20;
+ ts_chip.init_period = inv_icm45600_odr_to_period(st->conf.accel.odr);
+ inv_sensors_timestamp_init(&accel_st->ts, &ts_chip);
+
+ iio_device_set_drvdata(indio_dev, st);
+ indio_dev->name = name;
+ indio_dev->info = &inv_icm45600_accel_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = inv_icm45600_accel_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_icm45600_accel_channels);
+ indio_dev->available_scan_masks = inv_icm45600_accel_scan_masks;
+
+ ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
+ &inv_icm45600_buffer_ops);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return indio_dev;
+}
+
+int inv_icm45600_accel_parse_fifo(struct iio_dev *indio_dev)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm45600_sensor_state *accel_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &accel_st->ts;
+ ssize_t i, size;
+ unsigned int no;
+ const struct inv_icm45600_fifo_sensor_data *accel, *gyro;
+ const __le16 *timestamp;
+ const s8 *temp;
+ unsigned int odr;
+ s64 ts_val;
+ struct inv_icm45600_accel_buffer buffer;
+
+ /* parse all fifo packets */
+ for (i = 0, no = 0; i < st->fifo.count; i += size, ++no) {
+ size = inv_icm45600_fifo_decode_packet(&st->fifo.data[i],
+ &accel, &gyro, &temp, ×tamp, &odr);
+ /* quit if error or FIFO is empty */
+ if (size <= 0)
+ return size;
+
+ /* skip packet if no accel data or data is invalid */
+ if (accel == NULL || !inv_icm45600_fifo_is_data_valid(accel))
+ continue;
+
+ /* update odr */
+ if (odr & INV_ICM45600_SENSOR_ACCEL)
+ inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
+ st->fifo.nb.total, no);
+
+ /* buffer is copied to userspace, zeroing it to avoid any data leak */
+ memset(&buffer, 0, sizeof(buffer));
+ memcpy(&buffer.accel, accel, sizeof(buffer.accel));
+ /* convert 8 bits FIFO temperature in high resolution format */
+ buffer.temp = temp ? (*temp * 64) : 0;
+ ts_val = inv_sensors_timestamp_pop(ts);
+ iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val);
+ }
+
+ return 0;
+}
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c
index a07affa0e3c64f84330071c92018438882cdf686..ba6d76e71abf5835813bbbb4faeff4971029865c 100644
--- a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c
@@ -481,6 +481,77 @@ int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st,
return 0;
}
+int inv_icm45600_buffer_fifo_parse(struct inv_icm45600_state *st)
+{
+ struct inv_icm45600_sensor_state *gyro_st = iio_priv(st->indio_gyro);
+ struct inv_icm45600_sensor_state *accel_st = iio_priv(st->indio_accel);
+ struct inv_sensors_timestamp *ts;
+ int ret;
+
+ if (st->fifo.nb.total == 0)
+ return 0;
+
+ /* Handle gyroscope timestamp and FIFO data parsing. */
+ if (st->fifo.nb.gyro > 0) {
+ ts = &gyro_st->ts;
+ inv_sensors_timestamp_interrupt(ts, st->fifo.watermark.eff_gyro,
+ st->timestamp.gyro);
+ ret = inv_icm45600_gyro_parse_fifo(st->indio_gyro);
+ if (ret)
+ return ret;
+ }
+
+ /* Handle accelerometer timestamp and FIFO data parsing. */
+ if (st->fifo.nb.accel > 0) {
+ ts = &accel_st->ts;
+ inv_sensors_timestamp_interrupt(ts, st->fifo.watermark.eff_accel,
+ st->timestamp.accel);
+ ret = inv_icm45600_accel_parse_fifo(st->indio_accel);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int inv_icm45600_buffer_hwfifo_flush(struct inv_icm45600_state *st,
+ unsigned int count)
+{
+ struct inv_icm45600_sensor_state *gyro_st = iio_priv(st->indio_gyro);
+ struct inv_icm45600_sensor_state *accel_st = iio_priv(st->indio_accel);
+ struct inv_sensors_timestamp *ts;
+ s64 gyro_ts, accel_ts;
+ int ret;
+
+ gyro_ts = iio_get_time_ns(st->indio_gyro);
+ accel_ts = iio_get_time_ns(st->indio_accel);
+
+ ret = inv_icm45600_buffer_fifo_read(st, count);
+ if (ret)
+ return ret;
+
+ if (st->fifo.nb.total == 0)
+ return 0;
+
+ if (st->fifo.nb.gyro > 0) {
+ ts = &gyro_st->ts;
+ inv_sensors_timestamp_interrupt(ts, st->fifo.nb.gyro, gyro_ts);
+ ret = inv_icm45600_gyro_parse_fifo(st->indio_gyro);
+ if (ret)
+ return ret;
+ }
+
+ if (st->fifo.nb.accel > 0) {
+ ts = &accel_st->ts;
+ inv_sensors_timestamp_interrupt(ts, st->fifo.nb.accel, accel_ts);
+ ret = inv_icm45600_accel_parse_fifo(st->indio_accel);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
int inv_icm45600_buffer_init(struct inv_icm45600_state *st)
{
int ret;
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h
index 09595a41cf637a3ba9bc44e4df53a9d0aa11f485..23a75fe19c483428f92cad1c7ef95cbcaa3c4d37 100644
--- a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h
@@ -93,6 +93,8 @@ int inv_icm45600_buffer_update_watermark(struct inv_icm45600_state *st);
int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st,
unsigned int max);
+int inv_icm45600_buffer_fifo_parse(struct inv_icm45600_state *st);
+
int inv_icm45600_buffer_hwfifo_flush(struct inv_icm45600_state *st,
unsigned int count);
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
index 37487e9174bf53f2fc6583f37ac7542ea31b0643..9319cd5c067d42b5f4e93c9a52187be17a71599c 100644
--- a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
@@ -148,6 +148,10 @@ const struct inv_icm45600_chip_info inv_icm45605_chip_info = {
.whoami = INV_ICM45600_WHOAMI_ICM45605,
.name = "icm45605",
.conf = &inv_icm45600_default_conf,
+ .accel_scales = (const int *)inv_icm45600_accel_scale,
+ .accel_scales_len = INV_ICM45600_ACCEL_FS_NB,
+ .gyro_scales = (const int *)inv_icm45600_gyro_scale,
+ .gyro_scales_len = INV_ICM45600_GYRO_FS_NB,
};
EXPORT_SYMBOL_NS_GPL(inv_icm45605_chip_info, "IIO_ICM45600");
@@ -155,6 +159,10 @@ const struct inv_icm45600_chip_info inv_icm45606_chip_info = {
.whoami = INV_ICM45600_WHOAMI_ICM45606,
.name = "icm45606",
.conf = &inv_icm45600_default_conf,
+ .accel_scales = (const int *)inv_icm45600_accel_scale,
+ .accel_scales_len = INV_ICM45600_ACCEL_FS_NB,
+ .gyro_scales = (const int *)inv_icm45600_gyro_scale,
+ .gyro_scales_len = INV_ICM45600_GYRO_FS_NB,
};
EXPORT_SYMBOL_NS_GPL(inv_icm45606_chip_info, "IIO_ICM45600");
@@ -162,6 +170,10 @@ const struct inv_icm45600_chip_info inv_icm45608_chip_info = {
.whoami = INV_ICM45600_WHOAMI_ICM45608,
.name = "icm45608",
.conf = &inv_icm45600_default_conf,
+ .accel_scales = (const int *)inv_icm45600_accel_scale,
+ .accel_scales_len = INV_ICM45600_ACCEL_FS_NB,
+ .gyro_scales = (const int *)inv_icm45600_gyro_scale,
+ .gyro_scales_len = INV_ICM45600_GYRO_FS_NB,
};
EXPORT_SYMBOL_NS_GPL(inv_icm45608_chip_info, "IIO_ICM45600");
@@ -169,6 +181,10 @@ const struct inv_icm45600_chip_info inv_icm45634_chip_info = {
.whoami = INV_ICM45600_WHOAMI_ICM45634,
.name = "icm45634",
.conf = &inv_icm45600_default_conf,
+ .accel_scales = (const int *)inv_icm45600_accel_scale,
+ .accel_scales_len = INV_ICM45600_ACCEL_FS_NB,
+ .gyro_scales = (const int *)inv_icm45600_gyro_scale,
+ .gyro_scales_len = INV_ICM45600_GYRO_FS_NB,
};
EXPORT_SYMBOL_NS_GPL(inv_icm45634_chip_info, "IIO_ICM45600");
@@ -176,6 +192,10 @@ const struct inv_icm45600_chip_info inv_icm45686_chip_info = {
.whoami = INV_ICM45600_WHOAMI_ICM45686,
.name = "icm45686",
.conf = &inv_icm45686_default_conf,
+ .accel_scales = (const int *)inv_icm45686_accel_scale,
+ .accel_scales_len = INV_ICM45686_ACCEL_FS_NB,
+ .gyro_scales = (const int *)inv_icm45686_gyro_scale,
+ .gyro_scales_len = INV_ICM45686_GYRO_FS_NB,
};
EXPORT_SYMBOL_NS_GPL(inv_icm45686_chip_info, "IIO_ICM45600");
@@ -183,6 +203,10 @@ const struct inv_icm45600_chip_info inv_icm45687_chip_info = {
.whoami = INV_ICM45600_WHOAMI_ICM45687,
.name = "icm45687",
.conf = &inv_icm45686_default_conf,
+ .accel_scales = (const int *)inv_icm45686_accel_scale,
+ .accel_scales_len = INV_ICM45686_ACCEL_FS_NB,
+ .gyro_scales = (const int *)inv_icm45686_gyro_scale,
+ .gyro_scales_len = INV_ICM45686_GYRO_FS_NB,
};
EXPORT_SYMBOL_NS_GPL(inv_icm45687_chip_info, "IIO_ICM45600");
@@ -190,6 +214,10 @@ const struct inv_icm45600_chip_info inv_icm45688p_chip_info = {
.whoami = INV_ICM45600_WHOAMI_ICM45688P,
.name = "icm45688p",
.conf = &inv_icm45686_default_conf,
+ .accel_scales = (const int *)inv_icm45686_accel_scale,
+ .accel_scales_len = INV_ICM45686_ACCEL_FS_NB,
+ .gyro_scales = (const int *)inv_icm45686_gyro_scale,
+ .gyro_scales_len = INV_ICM45686_GYRO_FS_NB,
};
EXPORT_SYMBOL_NS_GPL(inv_icm45688p_chip_info, "IIO_ICM45600");
@@ -197,6 +225,10 @@ const struct inv_icm45600_chip_info inv_icm45689_chip_info = {
.whoami = INV_ICM45600_WHOAMI_ICM45689,
.name = "icm45689",
.conf = &inv_icm45686_default_conf,
+ .accel_scales = (const int *)inv_icm45686_accel_scale,
+ .accel_scales_len = INV_ICM45686_ACCEL_FS_NB,
+ .gyro_scales = (const int *)inv_icm45686_gyro_scale,
+ .gyro_scales_len = INV_ICM45686_GYRO_FS_NB,
};
EXPORT_SYMBOL_NS_GPL(inv_icm45689_chip_info, "IIO_ICM45600");
@@ -553,6 +585,9 @@ static irqreturn_t inv_icm45600_irq_handler(int irq, void *_data)
dev_err(dev, "FIFO read error %d\n", ret);
return IRQ_HANDLED;
}
+ ret = inv_icm45600_buffer_fifo_parse(st);
+ if (ret)
+ dev_err(dev, "FIFO parsing error %d\n", ret);
}
/* FIFO full warning. */
@@ -721,6 +756,14 @@ int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm45600_chi
if (ret)
return ret;
+ st->indio_gyro = inv_icm45600_gyro_init(st);
+ if (IS_ERR(st->indio_gyro))
+ return PTR_ERR(st->indio_gyro);
+
+ st->indio_accel = inv_icm45600_accel_init(st);
+ if (IS_ERR(st->indio_accel))
+ return PTR_ERR(st->indio_accel);
+
ret = inv_icm45600_irq_init(st, irq, irq_type, open_drain);
if (ret)
return ret;
@@ -857,6 +900,85 @@ static int inv_icm45600_runtime_resume(struct device *dev)
return inv_icm45600_enable_regulator_vddio(st);
}
+static int _inv_icm45600_temp_read(struct inv_icm45600_state *st, s16 *temp)
+{
+ struct inv_icm45600_sensor_conf conf = INV_ICM45600_SENSOR_CONF_INIT;
+ int ret;
+
+ /* Make sure a sensor is on. */
+ if (st->conf.gyro.mode == INV_ICM45600_SENSOR_MODE_OFF &&
+ st->conf.accel.mode == INV_ICM45600_SENSOR_MODE_OFF) {
+ conf.mode = INV_ICM45600_SENSOR_MODE_LOW_POWER;
+ ret = inv_icm45600_set_accel_conf(st, &conf, NULL);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_bulk_read(st->map, INV_ICM45600_REG_TEMP_DATA,
+ &st->buffer.u16, sizeof(st->buffer.u16));
+ if (ret)
+ return ret;
+
+ *temp = (s16)le16_to_cpup(&st->buffer.u16);
+ if (*temp == INV_ICM45600_DATA_INVALID)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int inv_icm45600_temp_read(struct inv_icm45600_state *st, s16 *temp)
+{
+ struct device *dev = regmap_get_device(st->map);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ scoped_guard(mutex, &st->lock)
+ ret = _inv_icm45600_temp_read(st, temp);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+int inv_icm45600_temp_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ s16 temp;
+ int ret;
+
+ if (chan->type != IIO_TEMP)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = inv_icm45600_temp_read(st, &temp);
+ if (ret)
+ return ret;
+ *val = temp;
+ return IIO_VAL_INT;
+ /*
+ * T°C = (temp / 128) + 25
+ * Tm°C = 1000 * ((temp * 100 / 12800) + 25)
+ * scale: 100000 / 13248 = 7.8125
+ * offset: 25000
+ */
+ case IIO_CHAN_INFO_SCALE:
+ *val = 7;
+ *val2 = 812500;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 25000;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
EXPORT_NS_GPL_DEV_PM_OPS(inv_icm45600_pm_ops, IIO_ICM45600) = {
SET_SYSTEM_SLEEP_PM_OPS(inv_icm45600_suspend, inv_icm45600_resume)
SET_RUNTIME_PM_OPS(inv_icm45600_runtime_suspend,
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c
new file mode 100644
index 0000000000000000000000000000000000000000..8ba2e8e54c738a3289726d6f23a46f5307ebdffe
--- /dev/null
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c
@@ -0,0 +1,809 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2025 Invensense, Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/common/inv_sensors_timestamp.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/math64.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include "inv_icm45600_buffer.h"
+#include "inv_icm45600.h"
+
+enum inv_icm45600_gyro_scan {
+ INV_ICM45600_GYRO_SCAN_X,
+ INV_ICM45600_GYRO_SCAN_Y,
+ INV_ICM45600_GYRO_SCAN_Z,
+ INV_ICM45600_GYRO_SCAN_TEMP,
+ INV_ICM45600_GYRO_SCAN_TIMESTAMP,
+};
+
+static const struct iio_chan_spec_ext_info inv_icm45600_gyro_ext_infos[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm45600_get_mount_matrix),
+ { },
+};
+
+#define INV_ICM45600_GYRO_CHAN(_modifier, _index, _ext_info) \
+ { \
+ .type = IIO_ANGL_VEL, \
+ .modified = 1, \
+ .channel2 = _modifier, \
+ .info_mask_separate = \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS), \
+ .info_mask_shared_by_type = \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS), \
+ .info_mask_shared_by_all = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+ }, \
+ .ext_info = _ext_info, \
+ }
+
+static const struct iio_chan_spec inv_icm45600_gyro_channels[] = {
+ INV_ICM45600_GYRO_CHAN(IIO_MOD_X, INV_ICM45600_GYRO_SCAN_X,
+ inv_icm45600_gyro_ext_infos),
+ INV_ICM45600_GYRO_CHAN(IIO_MOD_Y, INV_ICM45600_GYRO_SCAN_Y,
+ inv_icm45600_gyro_ext_infos),
+ INV_ICM45600_GYRO_CHAN(IIO_MOD_Z, INV_ICM45600_GYRO_SCAN_Z,
+ inv_icm45600_gyro_ext_infos),
+ INV_ICM45600_TEMP_CHAN(INV_ICM45600_GYRO_SCAN_TEMP),
+ IIO_CHAN_SOFT_TIMESTAMP(INV_ICM45600_GYRO_SCAN_TIMESTAMP),
+};
+
+/*
+ * IIO buffer data: size must be a power of 2 and timestamp aligned
+ * 16 bytes: 6 bytes angular velocity, 2 bytes temperature, 8 bytes timestamp
+ */
+struct inv_icm45600_gyro_buffer {
+ struct inv_icm45600_fifo_sensor_data gyro;
+ s16 temp;
+ aligned_s64 timestamp;
+};
+
+static const unsigned long inv_icm45600_gyro_scan_masks[] = {
+ /* 3-axis gyro + temperature */
+ BIT(INV_ICM45600_GYRO_SCAN_X) |
+ BIT(INV_ICM45600_GYRO_SCAN_Y) |
+ BIT(INV_ICM45600_GYRO_SCAN_Z) |
+ BIT(INV_ICM45600_GYRO_SCAN_TEMP),
+ 0,
+};
+
+/* enable gyroscope sensor and FIFO write */
+static int inv_icm45600_gyro_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm45600_sensor_state *gyro_st = iio_priv(indio_dev);
+ struct inv_icm45600_sensor_conf conf = INV_ICM45600_SENSOR_CONF_INIT;
+ unsigned int fifo_en = 0;
+ unsigned int sleep;
+ int ret;
+
+ scoped_guard(mutex, &st->lock) {
+ if (*scan_mask & BIT(INV_ICM45600_GYRO_SCAN_TEMP))
+ fifo_en |= INV_ICM45600_SENSOR_TEMP;
+
+ if (*scan_mask & (BIT(INV_ICM45600_GYRO_SCAN_X) |
+ BIT(INV_ICM45600_GYRO_SCAN_Y) |
+ BIT(INV_ICM45600_GYRO_SCAN_Z))) {
+ /* enable gyro sensor */
+ conf.mode = gyro_st->power_mode;
+ ret = inv_icm45600_set_gyro_conf(st, &conf, &sleep);
+ if (ret)
+ return ret;
+ fifo_en |= INV_ICM45600_SENSOR_GYRO;
+ }
+ /* update data FIFO write */
+ ret = inv_icm45600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
+ }
+ /* sleep required time */
+ if (sleep)
+ msleep(sleep);
+
+ return ret;
+}
+
+static int _inv_icm45600_gyro_read_sensor(struct inv_icm45600_state *st,
+ struct inv_icm45600_sensor_state *gyro_st,
+ unsigned int reg, int *val)
+{
+ struct inv_icm45600_sensor_conf conf = INV_ICM45600_SENSOR_CONF_INIT;
+ int ret;
+
+ /* enable gyro sensor */
+ conf.mode = gyro_st->power_mode;
+ ret = inv_icm45600_set_gyro_conf(st, &conf, NULL);
+ if (ret)
+ return ret;
+
+ /* read gyro register data */
+ ret = regmap_bulk_read(st->map, reg, &st->buffer.u16, sizeof(st->buffer.u16));
+ if (ret)
+ return ret;
+
+ *val = sign_extend32(le16_to_cpup(&st->buffer.u16), 15);
+ if (*val == INV_ICM45600_DATA_INVALID)
+ return -ENODATA;
+
+ return 0;
+}
+
+static int inv_icm45600_gyro_read_sensor(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm45600_sensor_state *gyro_st = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int reg;
+ int ret;
+
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ reg = INV_ICM45600_REG_GYRO_DATA_X;
+ break;
+ case IIO_MOD_Y:
+ reg = INV_ICM45600_REG_GYRO_DATA_Y;
+ break;
+ case IIO_MOD_Z:
+ reg = INV_ICM45600_REG_GYRO_DATA_Z;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ scoped_guard(mutex, &st->lock)
+ ret = _inv_icm45600_gyro_read_sensor(st, gyro_st, reg, val);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+/* IIO format int + nano */
+const int inv_icm45600_gyro_scale[][2] = {
+ /* +/- 2000dps => 0.001065264 rad/s */
+ [INV_ICM45600_GYRO_FS_2000DPS] = {0, 1065264},
+ /* +/- 1000dps => 0.000532632 rad/s */
+ [INV_ICM45600_GYRO_FS_1000DPS] = {0, 532632},
+ /* +/- 500dps => 0.000266316 rad/s */
+ [INV_ICM45600_GYRO_FS_500DPS] = {0, 266316},
+ /* +/- 250dps => 0.000133158 rad/s */
+ [INV_ICM45600_GYRO_FS_250DPS] = {0, 133158},
+ /* +/- 125dps => 0.000066579 rad/s */
+ [INV_ICM45600_GYRO_FS_125DPS] = {0, 66579},
+ /* +/- 62.5dps => 0.000033290 rad/s */
+ [INV_ICM45600_GYRO_FS_62_5DPS] = {0, 33290},
+ /* +/- 31.25dps => 0.000016645 rad/s */
+ [INV_ICM45600_GYRO_FS_31_25DPS] = {0, 16645},
+ /* +/- 15.625dps => 0.000008322 rad/s */
+ [INV_ICM45600_GYRO_FS_15_625DPS] = {0, 8322},
+};
+
+/* IIO format int + nano */
+const int inv_icm45686_gyro_scale[][2] = {
+ /* +/- 4000dps => 0.002130529 rad/s */
+ [INV_ICM45686_GYRO_FS_4000DPS] = {0, 2130529},
+ /* +/- 2000dps => 0.001065264 rad/s */
+ [INV_ICM45686_GYRO_FS_2000DPS] = {0, 1065264},
+ /* +/- 1000dps => 0.000532632 rad/s */
+ [INV_ICM45686_GYRO_FS_1000DPS] = {0, 532632},
+ /* +/- 500dps => 0.000266316 rad/s */
+ [INV_ICM45686_GYRO_FS_500DPS] = {0, 266316},
+ /* +/- 250dps => 0.000133158 rad/s */
+ [INV_ICM45686_GYRO_FS_250DPS] = {0, 133158},
+ /* +/- 125dps => 0.000066579 rad/s */
+ [INV_ICM45686_GYRO_FS_125DPS] = {0, 66579},
+ /* +/- 62.5dps => 0.000033290 rad/s */
+ [INV_ICM45686_GYRO_FS_62_5DPS] = {0, 33290},
+ /* +/- 31.25dps => 0.000016645 rad/s */
+ [INV_ICM45686_GYRO_FS_31_25DPS] = {0, 16645},
+ /* +/- 15.625dps => 0.000008322 rad/s */
+ [INV_ICM45686_GYRO_FS_15_625DPS] = {0, 8322},
+};
+
+static int inv_icm45600_gyro_read_scale(struct iio_dev *indio_dev,
+ int *val, int *val2)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm45600_sensor_state *gyro_st = iio_priv(indio_dev);
+ unsigned int idx;
+
+ idx = st->conf.gyro.fs;
+
+ /* Full scale register starts at 1 for not High FSR parts */
+ if (gyro_st->scales == (const int *)&inv_icm45600_gyro_scale)
+ idx--;
+
+ *val = gyro_st->scales[2 * idx];
+ *val2 = gyro_st->scales[2 * idx + 1];
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int inv_icm45600_gyro_write_scale(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm45600_sensor_state *gyro_st = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+ struct inv_icm45600_sensor_conf conf = INV_ICM45600_SENSOR_CONF_INIT;
+ int ret;
+
+ for (idx = 0; idx < gyro_st->scales_len; idx += 2) {
+ if (val == gyro_st->scales[idx] &&
+ val2 == gyro_st->scales[idx + 1])
+ break;
+ }
+ if (idx == gyro_st->scales_len)
+ return -EINVAL;
+
+ conf.fs = idx / 2;
+
+ /* Full scale register starts at 1 for not High FSR parts */
+ if (gyro_st->scales == (const int *)&inv_icm45600_gyro_scale)
+ conf.fs++;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ scoped_guard(mutex, &st->lock)
+ ret = inv_icm45600_set_gyro_conf(st, &conf, NULL);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+/* IIO format int + micro */
+static const int inv_icm45600_gyro_odr[] = {
+ /* 1.5625Hz */
+ 1, 562500,
+ /* 3.125Hz */
+ 3, 125000,
+ /* 6.25Hz */
+ 6, 250000,
+ /* 12.5Hz */
+ 12, 500000,
+ /* 25Hz */
+ 25, 0,
+ /* 50Hz */
+ 50, 0,
+ /* 100Hz */
+ 100, 0,
+ /* 200Hz */
+ 200, 0,
+ /* 400Hz */
+ 400, 0,
+ /* 800Hz */
+ 800, 0,
+ /* 1.6kHz */
+ 1600, 0,
+ /* 3.2kHz */
+ 3200, 0,
+ /* 6.4kHz */
+ 6400, 0,
+};
+
+static const int inv_icm45600_gyro_odr_conv[] = {
+ INV_ICM45600_ODR_1_5625HZ_LP,
+ INV_ICM45600_ODR_3_125HZ_LP,
+ INV_ICM45600_ODR_6_25HZ_LP,
+ INV_ICM45600_ODR_12_5HZ,
+ INV_ICM45600_ODR_25HZ,
+ INV_ICM45600_ODR_50HZ,
+ INV_ICM45600_ODR_100HZ,
+ INV_ICM45600_ODR_200HZ,
+ INV_ICM45600_ODR_400HZ,
+ INV_ICM45600_ODR_800HZ_LN,
+ INV_ICM45600_ODR_1600HZ_LN,
+ INV_ICM45600_ODR_3200HZ_LN,
+ INV_ICM45600_ODR_6400HZ_LN,
+};
+
+static int inv_icm45600_gyro_read_odr(struct inv_icm45600_state *st,
+ int *val, int *val2)
+{
+ unsigned int odr;
+ unsigned int i;
+
+ odr = st->conf.gyro.odr;
+
+ for (i = 0; i < ARRAY_SIZE(inv_icm45600_gyro_odr_conv); ++i) {
+ if (inv_icm45600_gyro_odr_conv[i] == odr)
+ break;
+ }
+ if (i >= ARRAY_SIZE(inv_icm45600_gyro_odr_conv))
+ return -EINVAL;
+
+ *val = inv_icm45600_gyro_odr[2 * i];
+ *val2 = inv_icm45600_gyro_odr[2 * i + 1];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int _inv_icm45600_gyro_write_odr(struct iio_dev *indio_dev, int odr)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm45600_sensor_state *gyro_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &gyro_st->ts;
+ struct inv_icm45600_sensor_conf conf = INV_ICM45600_SENSOR_CONF_INIT;
+ int ret;
+
+ conf.odr = odr;
+ ret = inv_sensors_timestamp_update_odr(ts, inv_icm45600_odr_to_period(conf.odr),
+ iio_buffer_enabled(indio_dev));
+ if (ret)
+ return ret;
+
+ if (st->conf.gyro.mode != INV_ICM45600_SENSOR_MODE_OFF)
+ conf.mode = gyro_st->power_mode;
+
+ ret = inv_icm45600_set_gyro_conf(st, &conf, NULL);
+ if (ret)
+ return ret;
+
+ inv_icm45600_buffer_update_fifo_period(st);
+ inv_icm45600_buffer_update_watermark(st);
+
+ return 0;
+}
+
+static int inv_icm45600_gyro_write_odr(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+ int odr;
+ int ret;
+
+ for (idx = 0; idx < ARRAY_SIZE(inv_icm45600_gyro_odr); idx += 2) {
+ if (val == inv_icm45600_gyro_odr[idx] &&
+ val2 == inv_icm45600_gyro_odr[idx + 1])
+ break;
+ }
+ if (idx >= ARRAY_SIZE(inv_icm45600_gyro_odr))
+ return -EINVAL;
+
+ odr = inv_icm45600_gyro_odr_conv[idx / 2];
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ scoped_guard(mutex, &st->lock)
+ ret = _inv_icm45600_gyro_write_odr(indio_dev, odr);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+/*
+ * Calibration bias values, IIO range format int + nano.
+ * Value is limited to +/-62.5dps coded on 14 bits signed. Step is 7.5mdps.
+ */
+static int inv_icm45600_gyro_calibbias[] = {
+ -1, 90830336, /* min: -1.090830336 rad/s */
+ 0, 133158, /* step: 0.000133158 rad/s */
+ 1, 90697178, /* max: 1.090697178 rad/s */
+};
+
+static int inv_icm45600_gyro_read_offset(struct inv_icm45600_state *st,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2)
+{
+ struct device *dev = regmap_get_device(st->map);
+ s64 val64;
+ s32 bias;
+ unsigned int reg;
+ s16 offset;
+ int ret;
+
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ reg = INV_ICM45600_IPREG_SYS1_REG_42;
+ break;
+ case IIO_MOD_Y:
+ reg = INV_ICM45600_IPREG_SYS1_REG_56;
+ break;
+ case IIO_MOD_Z:
+ reg = INV_ICM45600_IPREG_SYS1_REG_70;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ scoped_guard(mutex, &st->lock)
+ ret = regmap_bulk_read(st->map, reg, &st->buffer.u16, sizeof(st->buffer.u16));
+
+ pm_runtime_put_autosuspend(dev);
+ if (ret)
+ return ret;
+
+ offset = le16_to_cpup(&st->buffer.u16) & INV_ICM45600_GYRO_OFFUSER_MASK;
+ /* 14 bits signed value */
+ offset = sign_extend32(offset, 13);
+
+ /*
+ * convert raw offset to dps then to rad/s
+ * 14 bits signed raw max 62.5 to dps: 625 / 81920
+ * dps to rad: Pi / 180
+ * result in nano (1000000000)
+ * (offset * 625 * Pi * 1000000000) / (81920 * 180)
+ */
+ val64 = (s64)offset * 625LL * 3141592653LL;
+ /* for rounding, add + or - divisor (81920 * 180) divided by 2 */
+ if (val64 >= 0)
+ val64 += 81920 * 180 / 2;
+ else
+ val64 -= 81920 * 180 / 2;
+ bias = div_s64(val64, 81920 * 180);
+ *val = bias / 1000000000L;
+ *val2 = bias % 1000000000L;
+
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int inv_icm45600_gyro_write_offset(struct inv_icm45600_state *st,
+ struct iio_chan_spec const *chan,
+ int val, int val2)
+{
+ struct device *dev = regmap_get_device(st->map);
+ s64 val64, min, max;
+ unsigned int reg;
+ s16 offset;
+ int ret;
+
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ reg = INV_ICM45600_IPREG_SYS1_REG_42;
+ break;
+ case IIO_MOD_Y:
+ reg = INV_ICM45600_IPREG_SYS1_REG_56;
+ break;
+ case IIO_MOD_Z:
+ reg = INV_ICM45600_IPREG_SYS1_REG_70;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* inv_icm45600_gyro_calibbias: min - step - max in nano */
+ min = (s64)inv_icm45600_gyro_calibbias[0] * 1000000000LL -
+ (s64)inv_icm45600_gyro_calibbias[1];
+ max = (s64)inv_icm45600_gyro_calibbias[4] * 1000000000LL +
+ (s64)inv_icm45600_gyro_calibbias[5];
+ val64 = (s64)val * 1000000000LL;
+ if (val > 0)
+ val64 += (s64)val2;
+ else
+ val64 -= (s64)val2;
+ if (val64 < min || val64 > max)
+ return -EINVAL;
+
+ /*
+ * convert rad/s to dps then to raw value
+ * rad to dps: 180 / Pi
+ * dps to raw 14 bits signed, max 62.5: 8192 / 62.5
+ * val in nano (1000000000)
+ * val * 180 * 8192 / (Pi * 1000000000 * 62.5)
+ */
+ val64 = val64 * 180LL * 8192;
+ /* for rounding, add + or - divisor (314159265 * 625) divided by 2 */
+ if (val64 >= 0)
+ val64 += 314159265LL * 625LL / 2LL;
+ else
+ val64 -= 314159265LL * 625LL / 2LL;
+ offset = div64_s64(val64, 314159265LL * 625LL);
+
+ /* clamp value limited to 14 bits signed */
+ if (offset < -8192)
+ offset = -8192;
+ else if (offset > 8191)
+ offset = 8191;
+
+ st->buffer.u16 = cpu_to_le16(offset & INV_ICM45600_GYRO_OFFUSER_MASK);
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ scoped_guard(mutex, &st->lock)
+ ret = regmap_bulk_write(st->map, reg, &st->buffer.u16, sizeof(st->buffer.u16));
+
+ pm_runtime_put_autosuspend(dev);
+ return ret;
+}
+
+static int inv_icm45600_gyro_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ int ret;
+
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ break;
+ case IIO_TEMP:
+ return inv_icm45600_temp_read_raw(indio_dev, chan, val, val2, mask);
+ default:
+ return -EINVAL;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = inv_icm45600_gyro_read_sensor(indio_dev, chan, val);
+ iio_device_release_direct(indio_dev);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ return inv_icm45600_gyro_read_scale(indio_dev, val, val2);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm45600_gyro_read_odr(st, val, val2);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return inv_icm45600_gyro_read_offset(st, chan, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm45600_gyro_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals,
+ int *type, int *length, long mask)
+{
+ struct inv_icm45600_sensor_state *gyro_st = iio_priv(indio_dev);
+
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *vals = gyro_st->scales;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *length = gyro_st->scales_len;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = inv_icm45600_gyro_odr;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *length = ARRAY_SIZE(inv_icm45600_gyro_odr);
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ *vals = inv_icm45600_gyro_calibbias;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ return IIO_AVAIL_RANGE;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm45600_gyro_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ int ret;
+
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = inv_icm45600_gyro_write_scale(indio_dev, val, val2);
+ iio_device_release_direct(indio_dev);
+ return ret;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm45600_gyro_write_odr(indio_dev, val, val2);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = inv_icm45600_gyro_write_offset(st, chan, val, val2);
+ iio_device_release_direct(indio_dev);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm45600_gyro_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm45600_gyro_hwfifo_set_watermark(struct iio_dev *indio_dev,
+ unsigned int val)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+
+ guard(mutex)(&st->lock);
+
+ st->fifo.watermark.gyro = val;
+ return inv_icm45600_buffer_update_watermark(st);
+}
+
+static int inv_icm45600_gyro_hwfifo_flush(struct iio_dev *indio_dev,
+ unsigned int count)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ int ret;
+
+ if (count == 0)
+ return 0;
+
+ guard(mutex)(&st->lock);
+
+ ret = inv_icm45600_buffer_hwfifo_flush(st, count);
+ if (!ret)
+ ret = st->fifo.nb.gyro;
+
+ return ret;
+}
+
+static const struct iio_info inv_icm45600_gyro_info = {
+ .read_raw = inv_icm45600_gyro_read_raw,
+ .read_avail = inv_icm45600_gyro_read_avail,
+ .write_raw = inv_icm45600_gyro_write_raw,
+ .write_raw_get_fmt = inv_icm45600_gyro_write_raw_get_fmt,
+ .debugfs_reg_access = inv_icm45600_debugfs_reg,
+ .update_scan_mode = inv_icm45600_gyro_update_scan_mode,
+ .hwfifo_set_watermark = inv_icm45600_gyro_hwfifo_set_watermark,
+ .hwfifo_flush_to_buffer = inv_icm45600_gyro_hwfifo_flush,
+};
+
+struct iio_dev *inv_icm45600_gyro_init(struct inv_icm45600_state *st)
+{
+ struct device *dev = regmap_get_device(st->map);
+ const char *name;
+ struct inv_icm45600_sensor_state *gyro_st;
+ struct inv_sensors_timestamp_chip ts_chip;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->chip_info->name);
+ if (!name)
+ return ERR_PTR(-ENOMEM);
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*gyro_st));
+ if (!indio_dev)
+ return ERR_PTR(-ENOMEM);
+ gyro_st = iio_priv(indio_dev);
+
+ gyro_st->scales = st->chip_info->gyro_scales;
+ gyro_st->scales_len = st->chip_info->gyro_scales_len * 2;
+
+ /* low-noise by default at init */
+ gyro_st->power_mode = INV_ICM45600_SENSOR_MODE_LOW_NOISE;
+
+ /*
+ * clock period is 32kHz (31250ns)
+ * jitter is +/- 2% (20 per mille)
+ */
+ ts_chip.clock_period = 31250;
+ ts_chip.jitter = 20;
+ ts_chip.init_period = inv_icm45600_odr_to_period(st->conf.gyro.odr);
+ inv_sensors_timestamp_init(&gyro_st->ts, &ts_chip);
+
+ iio_device_set_drvdata(indio_dev, st);
+ indio_dev->name = name;
+ indio_dev->info = &inv_icm45600_gyro_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = inv_icm45600_gyro_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_icm45600_gyro_channels);
+ indio_dev->available_scan_masks = inv_icm45600_gyro_scan_masks;
+ indio_dev->setup_ops = &inv_icm45600_buffer_ops;
+
+ ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
+ &inv_icm45600_buffer_ops);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return indio_dev;
+}
+
+int inv_icm45600_gyro_parse_fifo(struct iio_dev *indio_dev)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm45600_sensor_state *gyro_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &gyro_st->ts;
+ ssize_t i, size;
+ unsigned int no;
+ const struct inv_icm45600_fifo_sensor_data *accel, *gyro;
+ const __le16 *timestamp;
+ const s8 *temp;
+ unsigned int odr;
+ s64 ts_val;
+ struct inv_icm45600_gyro_buffer buffer;
+
+ /* parse all fifo packets */
+ for (i = 0, no = 0; i < st->fifo.count; i += size, ++no) {
+ size = inv_icm45600_fifo_decode_packet(&st->fifo.data[i],
+ &accel, &gyro, &temp, ×tamp, &odr);
+ /* quit if error or FIFO is empty */
+ if (size <= 0)
+ return size;
+
+ /* skip packet if no gyro data or data is invalid */
+ if (gyro == NULL || !inv_icm45600_fifo_is_data_valid(gyro))
+ continue;
+
+ /* update odr */
+ if (odr & INV_ICM45600_SENSOR_GYRO)
+ inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
+ st->fifo.nb.total, no);
+
+ /* buffer is copied to userspace, zeroing it to avoid any data leak */
+ memset(&buffer, 0, sizeof(buffer));
+ memcpy(&buffer.gyro, gyro, sizeof(buffer.gyro));
+ /* convert 8 bits FIFO temperature in high resolution format */
+ buffer.temp = temp ? (*temp * 64) : 0;
+ ts_val = inv_sensors_timestamp_pop(ts);
+ iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val);
+ }
+
+ return 0;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v2 5/8] iio: imu: inv_icm45600: add I2C driver for inv_icm45600 driver
2025-07-10 8:57 [PATCH v2 0/8] iio: imu: new inv_icm45600 driver Remi Buisson via B4 Relay
` (3 preceding siblings ...)
2025-07-10 8:57 ` [PATCH v2 4/8] iio: imu: inv_icm45600: add IMU IIO devices Remi Buisson via B4 Relay
@ 2025-07-10 8:58 ` Remi Buisson via B4 Relay
2025-07-11 7:24 ` kernel test robot
2025-07-14 20:21 ` Dan Carpenter
2025-07-10 8:58 ` [PATCH v2 6/8] iio: imu: inv_icm45600: add SPI " Remi Buisson via B4 Relay
` (3 subsequent siblings)
8 siblings, 2 replies; 46+ messages in thread
From: Remi Buisson via B4 Relay @ 2025-07-10 8:58 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-kernel, linux-iio, devicetree, Remi Buisson
From: Remi Buisson <remi.buisson@tdk.com>
Add I2C driver for InvenSense ICM-456000 devices.
Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
---
drivers/iio/imu/inv_icm45600/Kconfig | 21 ++++++
drivers/iio/imu/inv_icm45600/Makefile | 3 +
drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c | 98 +++++++++++++++++++++++++
3 files changed, 122 insertions(+)
diff --git a/drivers/iio/imu/inv_icm45600/Kconfig b/drivers/iio/imu/inv_icm45600/Kconfig
index ea0a8d20cba26549b74105fa6fdbca1ddb222633..5b044a954e952ffa8e44507eea42872e1f3161bc 100644
--- a/drivers/iio/imu/inv_icm45600/Kconfig
+++ b/drivers/iio/imu/inv_icm45600/Kconfig
@@ -5,3 +5,24 @@ config INV_ICM45600
select IIO_BUFFER
select IIO_KFIFO_BUF
select IIO_INV_SENSORS_TIMESTAMP
+
+config INV_ICM45600_I2C
+ tristate "InvenSense ICM-456xx I2C driver"
+ depends on I2C
+ select INV_ICM45600
+ select REGMAP_I2C
+ help
+ This driver supports the InvenSense ICM-456xx motion tracking
+ devices over I2C.
+ Supported devices:
+ - ICM-45605
+ - ICM-45606
+ - ICM-45608
+ - ICM-45634
+ - ICM-45686
+ - ICM-45687
+ - ICM-45688-P
+ - ICM-45689
+
+ This driver can be built as a module. The module will be called
+ inv-icm45600-i2c.
diff --git a/drivers/iio/imu/inv_icm45600/Makefile b/drivers/iio/imu/inv_icm45600/Makefile
index ced5e1d9f2c10400cfa4878146672d41f815c22a..371585e29e92e78a30c7d40c1389548fa22a999a 100644
--- a/drivers/iio/imu/inv_icm45600/Makefile
+++ b/drivers/iio/imu/inv_icm45600/Makefile
@@ -5,3 +5,6 @@ inv-icm45600-y += inv_icm45600_core.o
inv-icm45600-y += inv_icm45600_buffer.o
inv-icm45600-y += inv_icm45600_accel.o
inv-icm45600-y += inv_icm45600_gyro.o
+
+obj-$(CONFIG_INV_ICM45600_I2C) += inv-icm45600-i2c.o
+inv-icm45600-i2c-y += inv_icm45600_i2c.o
\ No newline at end of file
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c
new file mode 100644
index 0000000000000000000000000000000000000000..5ebc18121a11f8ad576efb4d4cf80091c13af31d
--- /dev/null
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (C) 2025 InvenSense, Inc. */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regmap.h>
+
+#include "inv_icm45600.h"
+
+static const struct regmap_config inv_icm45600_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int inv_icm45600_probe(struct i2c_client *client)
+{
+ const struct inv_icm45600_chip_info *chip_info;
+ struct regmap *regmap;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
+ return -ENODEV;
+
+ chip_info = device_get_match_data(&client->dev);
+ if (!chip_info)
+ return -ENODEV;
+
+ regmap = devm_regmap_init_i2c(client, &inv_icm45600_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return inv_icm45600_core_probe(regmap, chip_info, true, NULL);
+}
+
+/*
+ * The device id table is used to identify which device is
+ * supported by this driver.
+ */
+static const struct i2c_device_id inv_icm45600_id[] = {
+ { "icm45605", (kernel_ulong_t)&inv_icm45605_chip_info },
+ { "icm45606", (kernel_ulong_t)&inv_icm45606_chip_info },
+ { "icm45608", (kernel_ulong_t)&inv_icm45608_chip_info },
+ { "icm45634", (kernel_ulong_t)&inv_icm45634_chip_info },
+ { "icm45686", (kernel_ulong_t)&inv_icm45686_chip_info },
+ { "icm45687", (kernel_ulong_t)&inv_icm45687_chip_info },
+ { "icm45688p", (kernel_ulong_t)&inv_icm45688p_chip_info },
+ { "icm45689", (kernel_ulong_t)&inv_icm45689_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, inv_icm45600_id);
+
+static const struct of_device_id inv_icm45600_of_matches[] = {
+ {
+ .compatible = "invensense,icm45605",
+ .data = &inv_icm45605_chip_info,
+ }, {
+ .compatible = "invensense,icm45606",
+ .data = &inv_icm45606_chip_info,
+ }, {
+ .compatible = "invensense,icm45608",
+ .data = &inv_icm45608_chip_info,
+ }, {
+ .compatible = "invensense,icm45634",
+ .data = &inv_icm45634_chip_info,
+ }, {
+ .compatible = "invensense,icm45686",
+ .data = &inv_icm45686_chip_info,
+ }, {
+ .compatible = "invensense,icm45687",
+ .data = &inv_icm45687_chip_info,
+ }, {
+ .compatible = "invensense,icm45688p",
+ .data = &inv_icm45688p_chip_info,
+ }, {
+ .compatible = "invensense,icm45689",
+ .data = &inv_icm45689_chip_info,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, inv_icm45600_of_matches);
+
+static struct i2c_driver inv_icm45600_driver = {
+ .driver = {
+ .name = "inv-icm45600-i2c",
+ .of_match_table = inv_icm45600_of_matches,
+ .pm = pm_ptr(&inv_icm45600_pm_ops),
+ },
+ .id_table = inv_icm45600_id,
+ .probe = inv_icm45600_probe,
+};
+module_i2c_driver(inv_icm45600_driver);
+
+MODULE_AUTHOR("InvenSense, Inc.");
+MODULE_DESCRIPTION("InvenSense ICM-456xx I2C driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_ICM45600");
--
2.34.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v2 6/8] iio: imu: inv_icm45600: add SPI driver for inv_icm45600 driver
2025-07-10 8:57 [PATCH v2 0/8] iio: imu: new inv_icm45600 driver Remi Buisson via B4 Relay
` (4 preceding siblings ...)
2025-07-10 8:58 ` [PATCH v2 5/8] iio: imu: inv_icm45600: add I2C driver for inv_icm45600 driver Remi Buisson via B4 Relay
@ 2025-07-10 8:58 ` Remi Buisson via B4 Relay
2025-07-11 4:55 ` kernel test robot
2025-07-10 8:58 ` [PATCH v2 7/8] iio: imu: inv_icm45600: add I3C " Remi Buisson via B4 Relay
` (2 subsequent siblings)
8 siblings, 1 reply; 46+ messages in thread
From: Remi Buisson via B4 Relay @ 2025-07-10 8:58 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-kernel, linux-iio, devicetree, Remi Buisson
From: Remi Buisson <remi.buisson@tdk.com>
Add SPI driver for InvenSense ICM-456000 devices.
Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
---
drivers/iio/imu/inv_icm45600/Kconfig | 21 +++++
drivers/iio/imu/inv_icm45600/Makefile | 5 +-
drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c | 106 ++++++++++++++++++++++++
3 files changed, 131 insertions(+), 1 deletion(-)
diff --git a/drivers/iio/imu/inv_icm45600/Kconfig b/drivers/iio/imu/inv_icm45600/Kconfig
index 5b044a954e952ffa8e44507eea42872e1f3161bc..01399d136a7ea3aa92a3a18ea455c95c0a6578b3 100644
--- a/drivers/iio/imu/inv_icm45600/Kconfig
+++ b/drivers/iio/imu/inv_icm45600/Kconfig
@@ -26,3 +26,24 @@ config INV_ICM45600_I2C
This driver can be built as a module. The module will be called
inv-icm45600-i2c.
+
+config INV_ICM45600_SPI
+ tristate "InvenSense ICM-456xx SPI driver"
+ depends on SPI_MASTER
+ select INV_ICM45600
+ select REGMAP_SPI
+ help
+ This driver supports the InvenSense ICM-456xx motion tracking
+ devices over SPI.
+ Supported devices:
+ - ICM-45605
+ - ICM-45606
+ - ICM-45608
+ - ICM-45634
+ - ICM-45686
+ - ICM-45687
+ - ICM-45688-P
+ - ICM-45689
+
+ This driver can be built as a module. The module will be called
+ inv-icm45600-spi.
diff --git a/drivers/iio/imu/inv_icm45600/Makefile b/drivers/iio/imu/inv_icm45600/Makefile
index 371585e29e92e78a30c7d40c1389548fa22a999a..244cc8aa1f79e3367ac6925504cfd9d5b918a0e6 100644
--- a/drivers/iio/imu/inv_icm45600/Makefile
+++ b/drivers/iio/imu/inv_icm45600/Makefile
@@ -7,4 +7,7 @@ inv-icm45600-y += inv_icm45600_accel.o
inv-icm45600-y += inv_icm45600_gyro.o
obj-$(CONFIG_INV_ICM45600_I2C) += inv-icm45600-i2c.o
-inv-icm45600-i2c-y += inv_icm45600_i2c.o
\ No newline at end of file
+inv-icm45600-i2c-y += inv_icm45600_i2c.o
+
+obj-$(CONFIG_INV_ICM45600_SPI) += inv-icm45600-spi.o
+inv-icm45600-spi-y += inv_icm45600_spi.o
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c
new file mode 100644
index 0000000000000000000000000000000000000000..2fd4ecce24bd60aa2112e1bcae3f7196504df637
--- /dev/null
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (C) 2025 InvenSense, Inc. */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "inv_icm45600.h"
+
+static const struct regmap_config inv_icm45600_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int inv_icm45600_spi_bus_setup(struct inv_icm45600_state *st)
+{
+ /* Set slew rates for SPI. */
+ return regmap_update_bits(st->map, INV_ICM45600_REG_DRIVE_CONFIG0,
+ INV_ICM45600_DRIVE_CONFIG0_SPI_MASK,
+ FIELD_PREP(INV_ICM45600_DRIVE_CONFIG0_SPI_MASK,
+ INV_ICM45600_SPI_SLEW_RATE_5NS));
+}
+
+static int inv_icm45600_probe(struct spi_device *spi)
+{
+ const struct inv_icm45600_chip_info *chip_info;
+ struct regmap *regmap;
+
+ chip_info = spi_get_device_match_data(spi);
+ if (!chip_info)
+ return -ENODEV;
+
+ /* Use SPI specific regmap. */
+ regmap = devm_regmap_init_spi(spi, &inv_icm45600_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return inv_icm45600_core_probe(regmap, chip_info, true,
+ inv_icm45600_spi_bus_setup);
+}
+
+/*
+ * The device id table is used to identify which device is
+ * supported by this driver.
+ */
+static const struct spi_device_id inv_icm45600_id[] = {
+ { "icm45605", (kernel_ulong_t)&inv_icm45605_chip_info },
+ { "icm45606", (kernel_ulong_t)&inv_icm45606_chip_info },
+ { "icm45608", (kernel_ulong_t)&inv_icm45608_chip_info },
+ { "icm45634", (kernel_ulong_t)&inv_icm45634_chip_info },
+ { "icm45686", (kernel_ulong_t)&inv_icm45686_chip_info },
+ { "icm45687", (kernel_ulong_t)&inv_icm45687_chip_info },
+ { "icm45688p", (kernel_ulong_t)&inv_icm45688p_chip_info },
+ { "icm45689", (kernel_ulong_t)&inv_icm45689_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, inv_icm45600_id);
+
+static const struct of_device_id inv_icm45600_of_matches[] = {
+ {
+ .compatible = "invensense,icm45605",
+ .data = &inv_icm45605_chip_info,
+ }, {
+ .compatible = "invensense,icm45606",
+ .data = &inv_icm45606_chip_info,
+ }, {
+ .compatible = "invensense,icm45608",
+ .data = &inv_icm45608_chip_info,
+ }, {
+ .compatible = "invensense,icm45634",
+ .data = &inv_icm45634_chip_info,
+ }, {
+ .compatible = "invensense,icm45686",
+ .data = &inv_icm45686_chip_info,
+ }, {
+ .compatible = "invensense,icm45687",
+ .data = &inv_icm45687_chip_info,
+ }, {
+ .compatible = "invensense,icm45688p",
+ .data = &inv_icm45688p_chip_info,
+ }, {
+ .compatible = "invensense,icm45689",
+ .data = &inv_icm45689_chip_info,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, inv_icm45600_of_matches);
+
+static struct spi_driver inv_icm45600_driver = {
+ .driver = {
+ .name = "inv-icm45600-spi",
+ .of_match_table = inv_icm45600_of_matches,
+ .pm = pm_ptr(&inv_icm45600_pm_ops),
+ },
+ .id_table = inv_icm45600_id,
+ .probe = inv_icm45600_probe,
+};
+module_spi_driver(inv_icm45600_driver);
+
+MODULE_AUTHOR("InvenSense, Inc.");
+MODULE_DESCRIPTION("InvenSense ICM-456xx SPI driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_ICM45600");
--
2.34.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v2 7/8] iio: imu: inv_icm45600: add I3C driver for inv_icm45600 driver
2025-07-10 8:57 [PATCH v2 0/8] iio: imu: new inv_icm45600 driver Remi Buisson via B4 Relay
` (5 preceding siblings ...)
2025-07-10 8:58 ` [PATCH v2 6/8] iio: imu: inv_icm45600: add SPI " Remi Buisson via B4 Relay
@ 2025-07-10 8:58 ` Remi Buisson via B4 Relay
2025-07-14 12:56 ` Sean Nyekjaer
2025-07-10 8:58 ` [PATCH v2 8/8] MAINTAINERS: add entry for inv_icm45600 6-axis imu sensor Remi Buisson via B4 Relay
2025-07-14 12:06 ` [PATCH v2 0/8] iio: imu: new inv_icm45600 driver Sean Nyekjaer
8 siblings, 1 reply; 46+ messages in thread
From: Remi Buisson via B4 Relay @ 2025-07-10 8:58 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-kernel, linux-iio, devicetree, Remi Buisson
From: Remi Buisson <remi.buisson@tdk.com>
Add I3C driver for InvenSense ICM-45600 devices.
Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
---
drivers/iio/imu/inv_icm45600/Kconfig | 21 +++++++
drivers/iio/imu/inv_icm45600/Makefile | 3 +
drivers/iio/imu/inv_icm45600/inv_icm45600_i3c.c | 82 +++++++++++++++++++++++++
3 files changed, 106 insertions(+)
diff --git a/drivers/iio/imu/inv_icm45600/Kconfig b/drivers/iio/imu/inv_icm45600/Kconfig
index 01399d136a7ea3aa92a3a18ea455c95c0a6578b3..dc133402f6d75f8c050100e8475404e00993818b 100644
--- a/drivers/iio/imu/inv_icm45600/Kconfig
+++ b/drivers/iio/imu/inv_icm45600/Kconfig
@@ -47,3 +47,24 @@ config INV_ICM45600_SPI
This driver can be built as a module. The module will be called
inv-icm45600-spi.
+
+config INV_ICM45600_I3C
+ tristate "InvenSense ICM-456xx I3C driver"
+ depends on I3C
+ select INV_ICM45600
+ select REGMAP_I3C
+ help
+ This driver supports the InvenSense ICM-456xx motion tracking
+ devices over I3C.
+ Supported devices:
+ - ICM-45605
+ - ICM-45606
+ - ICM-45608
+ - ICM-45634
+ - ICM-45686
+ - ICM-45687
+ - ICM-45688-P
+ - ICM-45689
+
+ This driver can be built as a module. The module will be called
+ inv-icm45600-i3c.
diff --git a/drivers/iio/imu/inv_icm45600/Makefile b/drivers/iio/imu/inv_icm45600/Makefile
index 244cc8aa1f79e3367ac6925504cfd9d5b918a0e6..fa646ea8339da3c41eab8aa16e14b484fd884843 100644
--- a/drivers/iio/imu/inv_icm45600/Makefile
+++ b/drivers/iio/imu/inv_icm45600/Makefile
@@ -11,3 +11,6 @@ inv-icm45600-i2c-y += inv_icm45600_i2c.o
obj-$(CONFIG_INV_ICM45600_SPI) += inv-icm45600-spi.o
inv-icm45600-spi-y += inv_icm45600_spi.o
+
+obj-$(CONFIG_INV_ICM45600_I3C) += inv-icm45600-i3c.o
+inv-icm45600-i3c-y += inv_icm45600_i3c.o
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_i3c.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_i3c.c
new file mode 100644
index 0000000000000000000000000000000000000000..9db249ca53ec3fecb0f85792a353d05463f52acb
--- /dev/null
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_i3c.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (C) 2025 InvenSense, Inc. */
+
+#include <linux/err.h>
+#include <linux/i3c/device.h>
+#include <linux/i3c/master.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "inv_icm45600.h"
+
+static const struct regmap_config inv_icm45600_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static const struct i3c_device_id inv_icm45600_i3c_ids[] = {
+ I3C_DEVICE_EXTRA_INFO(0x0235, 0x0000, 0x0011, (void *)NULL),
+ I3C_DEVICE_EXTRA_INFO(0x0235, 0x0000, 0x0084, (void *)NULL),
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(i3c, inv_icm45600_i3c_ids);
+
+static const struct inv_icm45600_chip_info *i3c_chip_info[] = {
+ &inv_icm45605_chip_info,
+ &inv_icm45606_chip_info,
+ &inv_icm45608_chip_info,
+ &inv_icm45634_chip_info,
+ &inv_icm45686_chip_info,
+ &inv_icm45687_chip_info,
+ &inv_icm45688p_chip_info,
+ &inv_icm45689_chip_info,
+};
+
+static int inv_icm45600_i3c_probe(struct i3c_device *i3cdev)
+{
+ int ret;
+ unsigned int whoami;
+ struct regmap *regmap;
+ const int nb_chip = ARRAY_SIZE(i3c_chip_info);
+ int chip;
+
+ regmap = devm_regmap_init_i3c(i3cdev, &inv_icm45600_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&i3cdev->dev, "Failed to register i3c regmap %ld\n", PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
+ ret = regmap_read(regmap, INV_ICM45600_REG_WHOAMI, &whoami);
+ if (ret) {
+ dev_err(&i3cdev->dev, "Failed to read part id %d\n", whoami);
+ return ret;
+ }
+
+ for (chip = 0; chip < nb_chip; chip++) {
+ if (whoami == i3c_chip_info[chip]->whoami)
+ break;
+ }
+
+ if (chip == nb_chip) {
+ dev_err(&i3cdev->dev, "Failed to match part id %d\n", whoami);
+ return -ENODEV;
+ }
+
+ return inv_icm45600_core_probe(regmap, i3c_chip_info[chip], false, NULL);
+}
+
+static struct i3c_driver inv_icm45600_driver = {
+ .driver = {
+ .name = "inv_icm45600_i3c",
+ .pm = pm_sleep_ptr(&inv_icm45600_pm_ops),
+ },
+ .probe = inv_icm45600_i3c_probe,
+ .id_table = inv_icm45600_i3c_ids,
+};
+module_i3c_driver(inv_icm45600_driver);
+
+MODULE_AUTHOR("Remi Buisson <remi.buisson@tdk.com>");
+MODULE_DESCRIPTION("InvenSense ICM-456xx i3c driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_ICM45600");
--
2.34.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH v2 8/8] MAINTAINERS: add entry for inv_icm45600 6-axis imu sensor
2025-07-10 8:57 [PATCH v2 0/8] iio: imu: new inv_icm45600 driver Remi Buisson via B4 Relay
` (6 preceding siblings ...)
2025-07-10 8:58 ` [PATCH v2 7/8] iio: imu: inv_icm45600: add I3C " Remi Buisson via B4 Relay
@ 2025-07-10 8:58 ` Remi Buisson via B4 Relay
2025-07-14 12:06 ` [PATCH v2 0/8] iio: imu: new inv_icm45600 driver Sean Nyekjaer
8 siblings, 0 replies; 46+ messages in thread
From: Remi Buisson via B4 Relay @ 2025-07-10 8:58 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-kernel, linux-iio, devicetree, Remi Buisson
From: Remi Buisson <remi.buisson@tdk.com>
Add MAINTAINERS entry for InvenSense ICM-45600 IMU device.
Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
---
MAINTAINERS | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index e3b0109a23045926d6a7e9659afdab0a6dbf7bed..c4aa2102ef398130074d20dd5b9367ce3fa51968 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12621,6 +12621,14 @@ F: Documentation/ABI/testing/sysfs-bus-iio-inv_icm42600
F: Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
F: drivers/iio/imu/inv_icm42600/
+INVENSENSE ICM-456xx IMU DRIVER
+M: Remi Buisson <remi.buisson@tdk.com>
+L: linux-iio@vger.kernel.org
+S: Maintained
+W: https://invensense.tdk.com/
+F: Documentation/devicetree/bindings/iio/imu/invensense,icm45600.yaml
+F: drivers/iio/imu/inv_icm45600/
+
INVENSENSE MPU-3050 GYROSCOPE DRIVER
M: Linus Walleij <linus.walleij@linaro.org>
L: linux-iio@vger.kernel.org
--
2.34.1
^ permalink raw reply related [flat|nested] 46+ messages in thread
* Re: [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver
2025-07-10 8:57 ` [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver Remi Buisson via B4 Relay
@ 2025-07-10 9:29 ` Andy Shevchenko
2025-07-11 11:39 ` Remi Buisson
[not found] ` <FR2PPF4571F02BC5366477EC02E9C44041A8C4BA@FR2PPF4571F02BC.DEUP281.PROD.OUTLOOK.COM>
2025-07-11 2:58 ` kernel test robot
2025-07-13 16:00 ` Jonathan Cameron
2 siblings, 2 replies; 46+ messages in thread
From: Andy Shevchenko @ 2025-07-10 9:29 UTC (permalink / raw)
To: remi.buisson
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, linux-kernel,
linux-iio, devicetree
On Thu, Jul 10, 2025 at 08:57:57AM +0000, Remi Buisson via B4 Relay wrote:
> From: Remi Buisson <remi.buisson@tdk.com>
>
> Core component of a new driver for InvenSense ICM-45600 devices.
> It includes registers definition, main probe/setup, and device
> utility functions.
>
> ICM-456xx devices are latest generation of 6-axis IMU,
> gyroscope+accelerometer and temperature sensor. This device
> includes a 8K FIFO, supports I2C/I3C/SPI, and provides
> intelligent motion features like pedometer, tilt detection,
> and tap detection.
...
> + INV_ICM45600_SENSOR_MODE_NB
What does the _NB stand for? Number of Bullets?
...
> +struct inv_icm45600_sensor_conf {
> + int mode;
> + int fs;
> + int odr;
> + int filter;
Any of them can hold negative value?
> +};
...
> +#define INV_ICM45600_SENSOR_CONF_INIT {-1, -1, -1, -1}
Unused.
> +#include <linux/bitfield.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/iio/iio.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/property.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/types.h>
...
> +static int inv_icm45600_ireg_read(struct regmap *map, unsigned int reg,
> + u8 *data, size_t count)
> +{
> + int ret;
> + u8 addr[2];
> + ssize_t i;
> + unsigned int d;
> +
> + addr[0] = FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg);
> + addr[1] = FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg);
> +
> + /* Burst write address. */
> + ret = regmap_bulk_write(map, INV_ICM45600_REG_IREG_ADDR, addr, 2);
sizeof()?
> + udelay(INV_ICM45600_IREG_DELAY_US);
See below. This is also weird.
> + if (ret)
> + return ret;
> +
> + /* Read the data. */
> + for (i = 0; i < count; i++) {
> + ret = regmap_read(map, INV_ICM45600_REG_IREG_DATA, &d);
> + data[i] = d;
> + udelay(INV_ICM45600_IREG_DELAY_US);
Can fsleep() be used here?
> + if (ret)
> + return ret;
This is weird. First you assign a garbage to the output, delay and return
an error. It seems entire code is broken...
Please, fix all these and try again, I stop my review here.
> + }
> +
> + return 0;
> +}
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600
2025-07-10 8:57 ` [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600 Remi Buisson via B4 Relay
@ 2025-07-10 22:41 ` Rob Herring
2025-07-11 11:40 ` Remi Buisson
2025-07-14 5:38 ` Krzysztof Kozlowski
1 sibling, 1 reply; 46+ messages in thread
From: Rob Herring @ 2025-07-10 22:41 UTC (permalink / raw)
To: Remi Buisson
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Krzysztof Kozlowski, Conor Dooley, linux-kernel, linux-iio,
devicetree
On Thu, Jul 10, 2025 at 08:57:56AM +0000, Remi Buisson wrote:
> Document the ICM-45600 devices devicetree bindings.
>
> Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
> ---
> .../bindings/iio/imu/invensense,icm45600.yaml | 138 +++++++++++++++++++++
> 1 file changed, 138 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm45600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm45600.yaml
> new file mode 100644
> index 0000000000000000000000000000000000000000..a651878791ffae8d1c8d6c8ff1e4becfc56af79f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm45600.yaml
> @@ -0,0 +1,138 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/imu/invensense,icm45600.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: InvenSense ICM-45600 Inertial Measurement Unit
> +
> +maintainers:
> + - Remi Buisson <remi.buisson@tdk.com>
> +
> +description: |
> + 6-axis MotionTracking device that combines a 3-axis gyroscope and a 3-axis
> + accelerometer.
> +
> + It has a configurable host interface that supports I3C, I2C and SPI serial
> + communication, features up to 8kB FIFO and 2 programmable interrupts with
> + ultra-low-power wake-on-motion support to minimize system power consumption.
> +
> + Other industry-leading features include InvenSense on-chip APEX Motion
> + Processing engine for gesture recognition, activity classification, and
> + pedometer, along with programmable digital filters, and an embedded
> + temperature sensor.
> +
> + https://invensense.tdk.com/wp-content/uploads/documentation/DS-000576_ICM-45605.pdf
> +
> +properties:
> + compatible:
> + enum:
> + - invensense,icm45605
> + - invensense,icm45606
> + - invensense,icm45608
> + - invensense,icm45634
> + - invensense,icm45686
> + - invensense,icm45687
> + - invensense,icm45688p
> + - invensense,icm45689
> +
> + reg:
> + maxItems: 1
> +
> + interrupts:
> + minItems: 1
> + maxItems: 2
> +
> + interrupt-names:
> + minItems: 1
> + maxItems: 2
> + items:
> + enum:
> + - INT1
> + - INT2
> + description: Choose chip interrupt pin to be used as interrupt input.
> +
> + drive-open-drain:
> + type: boolean
> +
> + vdd-supply:
> + description: Regulator that provides power to the sensor
> +
> + vddio-supply:
> + description: Regulator that provides power to the bus
> +
> + mount-matrix:
> + description: an optional 3x3 mounting rotation matrix
> +
> +required:
> + - compatible
> + - reg
> + - interrupts
> + - interrupt-names
> +
> +allOf:
> + - $ref: /schemas/spi/spi-peripheral-props.yaml#
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/gpio/gpio.h>
> + #include <dt-bindings/interrupt-controller/irq.h>
> + i2c {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + icm45605@68 {
> + compatible = "invensense,icm45605";
> + reg = <0x68>;
> + interrupt-parent = <&gpio2>;
> + interrupt-names = "INT1";
> + interrupts = <7 IRQ_TYPE_EDGE_RISING>;
> + vdd-supply = <&vdd>;
> + vddio-supply = <&vddio>;
> + mount-matrix = "0", "-1", "0",
> + "1", "0", "0",
> + "0", "0", "1";
> + };
> + };
> + - |
> + #include <dt-bindings/gpio/gpio.h>
> + #include <dt-bindings/interrupt-controller/irq.h>
> + spi {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + icm45605@0 {
> + compatible = "invensense,icm45605";
> + reg = <0>;
> + spi-max-frequency = <24000000>;
> + interrupt-parent = <&gpio1>;
> + interrupt-names = "INT1";
> + interrupts = <6 IRQ_TYPE_EDGE_RISING>;
> + vdd-supply = <&vdd>;
> + vddio-supply = <&vddio>;
> + mount-matrix = "0", "-1", "0",
> + "1", "0", "0",
> + "0", "0", "1";
> + };
> + };
> + - |
> + #include <dt-bindings/gpio/gpio.h>
> + #include <dt-bindings/interrupt-controller/irq.h>
> + i3c {
> + #address-cells = <3>;
> + #size-cells = <0>;
> +
> + icm45606@68,46A00000084 {
> + reg = <0x68 0x46A 0x84>;
> + interrupt-parent = <&gpio1>;
> + interrupt-names = "INT1";
> + interrupts = <5 IRQ_TYPE_EDGE_RISING>;
> + vdd-supply = <&vdd>;
> + vddio-supply = <&vddio>;
> + mount-matrix = "0", "-1", "0",
> + "1", "0", "0",
> + "0", "0", "1";
> + };
> + };
I don't think we need 3 examples just for different buses being the only
diff.
Rob
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver
2025-07-10 8:57 ` [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver Remi Buisson via B4 Relay
2025-07-10 9:29 ` Andy Shevchenko
@ 2025-07-11 2:58 ` kernel test robot
2025-07-13 16:00 ` Jonathan Cameron
2 siblings, 0 replies; 46+ messages in thread
From: kernel test robot @ 2025-07-11 2:58 UTC (permalink / raw)
To: Remi Buisson via B4 Relay, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: oe-kbuild-all, linux-kernel, linux-iio, devicetree, Remi Buisson
Hi Remi,
kernel test robot noticed the following build warnings:
[auto build test WARNING on f8f559752d573a051a984adda8d2d1464f92f954]
url: https://github.com/intel-lab-lkp/linux/commits/Remi-Buisson-via-B4-Relay/dt-bindings-iio-imu-Add-inv_icm45600/20250710-170143
base: f8f559752d573a051a984adda8d2d1464f92f954
patch link: https://lore.kernel.org/r/20250710-add_newport_driver-v2-2-bf76d8142ef2%40tdk.com
patch subject: [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver
config: i386-buildonly-randconfig-001-20250711 (https://download.01.org/0day-ci/archive/20250711/202507111042.NdeRL3wM-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14+deb12u1) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250711/202507111042.NdeRL3wM-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202507111042.NdeRL3wM-lkp@intel.com/
All warnings (new ones prefixed by >>):
drivers/hsi/hsi_boardinfo.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hsi/hsi_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hte/hte.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hv/channel.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hv/channel_mgmt.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hv/hv_proc.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hv/mshv_common.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hv/mshv_root_hv_call.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hv/ring_buffer.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hv/vmbus_drv.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hwmon/adt7x10.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hwmon/hwmon-vid.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hwmon/hwmon.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hwmon/ltc2947-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hwmon/max1111.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hwmon/nct6775-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hwmon/pmbus/pmbus_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hwmon/sch56xx-common.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hwspinlock/hwspinlock_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hwtracing/coresight/coresight-etm-perf.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hwtracing/coresight/coresight-platform.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hwtracing/coresight/coresight-syscfg.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hwtracing/coresight/coresight-sysfs.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hwtracing/coresight/coresight-tmc-etr.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hwtracing/coresight/coresight-trace-id.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hwtracing/intel_th/core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hwtracing/intel_th/msu.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hwtracing/stm/core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/hwtracing/stm/policy.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/i2c/algos/i2c-algo-bit.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/i2c/algos/i2c-algo-pca.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/i2c/algos/i2c-algo-pcf.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/i2c/busses/i2c-amd-mp2-pci.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/i2c/busses/i2c-designware-slave.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/i2c/busses/i2c-pasemi-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/i2c/busses/i2c-piix4.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/i2c/busses/i2c-viai2c-common.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/i2c/i2c-atr.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/i2c/i2c-core-acpi.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/i2c/i2c-core-base.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/i2c/i2c-core-of-prober.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/i2c/i2c-core-of.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/i2c/i2c-core-slave.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/i2c/i2c-core-smbus.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/i2c/i2c-mux.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/i2c/i2c-smbus.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/i3c/device.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/i3c/master/dw-i3c-master.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/accel/adxl313_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/accel/adxl345_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/accel/adxl355_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/accel/adxl367.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/accel/adxl372.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/accel/adxl380.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/accel/bma400_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/accel/bmc150-accel-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/accel/bmi088-accel-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/accel/fxls8962af-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/accel/kxsd9.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/accel/mma7455_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/accel/mma9551_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/accel/st_accel_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/adc/ad7091r-base.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/adc/ad7606.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/adc/ltc2497-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/adc/qcom-vadc-common.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/afe/iio-rescale.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/buffer/industrialio-buffer-dma.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/buffer/industrialio-buffer-dmaengine.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/buffer/kfifo_buf.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/chemical/bme680_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/chemical/ens160_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/chemical/sps30.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/common/hid-sensors/hid-sensor-attributes.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/common/hid-sensors/hid-sensor-trigger.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/common/inv_sensors/inv_sensors_timestamp.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/common/ms_sensors/ms_sensors_i2c.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/common/ssp_sensors/ssp_dev.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/common/ssp_sensors/ssp_iio.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/common/st_sensors/st_sensors_buffer.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/common/st_sensors/st_sensors_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/common/st_sensors/st_sensors_i2c.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/common/st_sensors/st_sensors_spi.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/common/st_sensors/st_sensors_trigger.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/dac/ad3552r-common.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/dac/ad5592r-base.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/dac/ad5686.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/dummy/iio_dummy_evgen.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/gyro/bmg160_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/gyro/fxas21002c_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/gyro/st_gyro_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/humidity/hts221_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/imu/adis.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/imu/bmi160/bmi160_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/imu/bmi270/bmi270_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/imu/bmi323/bmi323_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/imu/bno055/bno055.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/imu/fxos8700_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/imu/inv_icm42600/inv_icm42600_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
>> drivers/iio/imu/inv_icm45600/inv_icm45600_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/imu/inv_mpu6050/inv_mpu_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/industrialio-backend.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/industrialio-configfs.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/industrialio-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/industrialio-event.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/industrialio-sw-device.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/industrialio-sw-trigger.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/industrialio-trigger.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/light/st_uvis25_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/magnetometer/bmc150_magn.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/magnetometer/hmc5843_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/magnetometer/rm3100-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/magnetometer/st_magn_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/pressure/bmp280-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/pressure/bmp280-regmap.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/pressure/hsc030pa.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/pressure/mpl115.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/pressure/mprls0025pa.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/pressure/ms5611_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/pressure/st_pressure_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iio/pressure/zpa2326.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/addr.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/cache.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/cgroup.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/cm.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/cma.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/cq.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/device.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/ib_core_uverbs.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/iwcm.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/mad.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/mr_pool.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/nldev.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/rdma_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/restrack.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/roce_gid_mgmt.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/rw.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/sa_query.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/security.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/sysfs.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/ucaps.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/umem_dmabuf.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/uverbs_cmd.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/uverbs_ioctl.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/uverbs_main.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/core/uverbs_std_types.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/sw/rdmavt/ah.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/sw/rdmavt/cq.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/sw/rdmavt/mcast.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/sw/rdmavt/mr.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/sw/rdmavt/qp.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/sw/rdmavt/rc.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/sw/rdmavt/vt.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/ulp/rtrs/rtrs-clt.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/ulp/rtrs/rtrs-srv.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/infiniband/ulp/rtrs/rtrs.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/ff-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/ff-memless.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/gameport/gameport.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/input-poller.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/input.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/joystick/iforce/iforce-main.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/joystick/iforce/iforce-packets.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/misc/ad714x.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/misc/adxl34x.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/misc/cma3000_d0x.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/rmi4/rmi_2d_sensor.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/rmi4/rmi_bus.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/rmi4/rmi_driver.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/serio/hil_mlc.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/serio/hp_sdc.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/serio/i8042.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/serio/libps2.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/serio/serio.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/sparse-keymap.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/touchscreen.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/touchscreen/ad7879.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/touchscreen/cyttsp_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/touchscreen/goodix_berlin_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/touchscreen/tsc200x-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/touchscreen/wm9705.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/touchscreen/wm9712.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/touchscreen/wm9713.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/input/touchscreen/wm97xx-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/interconnect/core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/interconnect/icc-clk.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/interconnect/imx/imx.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/interconnect/mediatek/icc-emi.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/interconnect/qcom/bcm-voter.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/interconnect/qcom/icc-common.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/interconnect/qcom/icc-rpm-clocks.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/interconnect/qcom/icc-rpm.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/interconnect/qcom/icc-rpmh.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/interconnect/qcom/smd-rpm.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iommu/dma-iommu.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iommu/intel/dmar.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iommu/intel/iommu.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/iommu/io-pgfault.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 6/8] iio: imu: inv_icm45600: add SPI driver for inv_icm45600 driver
2025-07-10 8:58 ` [PATCH v2 6/8] iio: imu: inv_icm45600: add SPI " Remi Buisson via B4 Relay
@ 2025-07-11 4:55 ` kernel test robot
2025-07-11 6:09 ` Andy Shevchenko
0 siblings, 1 reply; 46+ messages in thread
From: kernel test robot @ 2025-07-11 4:55 UTC (permalink / raw)
To: Remi Buisson via B4 Relay, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: oe-kbuild-all, linux-kernel, linux-iio, devicetree, Remi Buisson
Hi Remi,
kernel test robot noticed the following build warnings:
[auto build test WARNING on f8f559752d573a051a984adda8d2d1464f92f954]
url: https://github.com/intel-lab-lkp/linux/commits/Remi-Buisson-via-B4-Relay/dt-bindings-iio-imu-Add-inv_icm45600/20250710-170143
base: f8f559752d573a051a984adda8d2d1464f92f954
patch link: https://lore.kernel.org/r/20250710-add_newport_driver-v2-6-bf76d8142ef2%40tdk.com
patch subject: [PATCH v2 6/8] iio: imu: inv_icm45600: add SPI driver for inv_icm45600 driver
config: mips-randconfig-r072-20250711 (https://download.01.org/0day-ci/archive/20250711/202507111201.r62j5rb6-lkp@intel.com/config)
compiler: clang version 21.0.0git (https://github.com/llvm/llvm-project 01c97b4953e87ae455bd4c41e3de3f0f0f29c61c)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250711/202507111201.r62j5rb6-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202507111201.r62j5rb6-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c:107:7: warning: variable 'sleep' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
107 | if (*scan_mask & (BIT(INV_ICM45600_ACCEL_SCAN_X) |
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
108 | BIT(INV_ICM45600_ACCEL_SCAN_Y) |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
109 | BIT(INV_ICM45600_ACCEL_SCAN_Z))) {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c:123:6: note: uninitialized use occurs here
123 | if (sleep)
| ^~~~~
drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c:107:3: note: remove the 'if' if its condition is always true
107 | if (*scan_mask & (BIT(INV_ICM45600_ACCEL_SCAN_X) |
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
108 | BIT(INV_ICM45600_ACCEL_SCAN_Y) |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
109 | BIT(INV_ICM45600_ACCEL_SCAN_Z))) {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c:100:20: note: initialize the variable 'sleep' to silence this warning
100 | unsigned int sleep;
| ^
| = 0
1 warning generated.
--
>> drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c:107:7: warning: variable 'sleep' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
107 | if (*scan_mask & (BIT(INV_ICM45600_GYRO_SCAN_X) |
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
108 | BIT(INV_ICM45600_GYRO_SCAN_Y) |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
109 | BIT(INV_ICM45600_GYRO_SCAN_Z))) {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c:121:6: note: uninitialized use occurs here
121 | if (sleep)
| ^~~~~
drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c:107:3: note: remove the 'if' if its condition is always true
107 | if (*scan_mask & (BIT(INV_ICM45600_GYRO_SCAN_X) |
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
108 | BIT(INV_ICM45600_GYRO_SCAN_Y) |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
109 | BIT(INV_ICM45600_GYRO_SCAN_Z))) {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c:100:20: note: initialize the variable 'sleep' to silence this warning
100 | unsigned int sleep;
| ^
| = 0
1 warning generated.
vim +107 drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c
1fb5c2bf7348d06 Remi Buisson 2025-07-10 91
1fb5c2bf7348d06 Remi Buisson 2025-07-10 92 /* enable accelerometer sensor and FIFO write */
1fb5c2bf7348d06 Remi Buisson 2025-07-10 93 static int inv_icm45600_accel_update_scan_mode(struct iio_dev *indio_dev,
1fb5c2bf7348d06 Remi Buisson 2025-07-10 94 const unsigned long *scan_mask)
1fb5c2bf7348d06 Remi Buisson 2025-07-10 95 {
1fb5c2bf7348d06 Remi Buisson 2025-07-10 96 struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
1fb5c2bf7348d06 Remi Buisson 2025-07-10 97 struct inv_icm45600_sensor_state *accel_st = iio_priv(indio_dev);
1fb5c2bf7348d06 Remi Buisson 2025-07-10 98 struct inv_icm45600_sensor_conf conf = INV_ICM45600_SENSOR_CONF_INIT;
1fb5c2bf7348d06 Remi Buisson 2025-07-10 99 unsigned int fifo_en = 0;
1fb5c2bf7348d06 Remi Buisson 2025-07-10 100 unsigned int sleep;
1fb5c2bf7348d06 Remi Buisson 2025-07-10 101 int ret;
1fb5c2bf7348d06 Remi Buisson 2025-07-10 102
1fb5c2bf7348d06 Remi Buisson 2025-07-10 103 scoped_guard(mutex, &st->lock) {
1fb5c2bf7348d06 Remi Buisson 2025-07-10 104 if (*scan_mask & BIT(INV_ICM45600_ACCEL_SCAN_TEMP))
1fb5c2bf7348d06 Remi Buisson 2025-07-10 105 fifo_en |= INV_ICM45600_SENSOR_TEMP;
1fb5c2bf7348d06 Remi Buisson 2025-07-10 106
1fb5c2bf7348d06 Remi Buisson 2025-07-10 @107 if (*scan_mask & (BIT(INV_ICM45600_ACCEL_SCAN_X) |
1fb5c2bf7348d06 Remi Buisson 2025-07-10 108 BIT(INV_ICM45600_ACCEL_SCAN_Y) |
1fb5c2bf7348d06 Remi Buisson 2025-07-10 109 BIT(INV_ICM45600_ACCEL_SCAN_Z))) {
1fb5c2bf7348d06 Remi Buisson 2025-07-10 110 /* enable accel sensor */
1fb5c2bf7348d06 Remi Buisson 2025-07-10 111 conf.mode = accel_st->power_mode;
1fb5c2bf7348d06 Remi Buisson 2025-07-10 112 ret = inv_icm45600_set_accel_conf(st, &conf, &sleep);
1fb5c2bf7348d06 Remi Buisson 2025-07-10 113 if (ret)
1fb5c2bf7348d06 Remi Buisson 2025-07-10 114 return ret;
1fb5c2bf7348d06 Remi Buisson 2025-07-10 115 fifo_en |= INV_ICM45600_SENSOR_ACCEL;
1fb5c2bf7348d06 Remi Buisson 2025-07-10 116 }
1fb5c2bf7348d06 Remi Buisson 2025-07-10 117
1fb5c2bf7348d06 Remi Buisson 2025-07-10 118 /* Update data FIFO write. */
1fb5c2bf7348d06 Remi Buisson 2025-07-10 119 ret = inv_icm45600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
1fb5c2bf7348d06 Remi Buisson 2025-07-10 120 }
1fb5c2bf7348d06 Remi Buisson 2025-07-10 121
1fb5c2bf7348d06 Remi Buisson 2025-07-10 122 /* Sleep required time. */
1fb5c2bf7348d06 Remi Buisson 2025-07-10 123 if (sleep)
1fb5c2bf7348d06 Remi Buisson 2025-07-10 124 msleep(sleep);
1fb5c2bf7348d06 Remi Buisson 2025-07-10 125
1fb5c2bf7348d06 Remi Buisson 2025-07-10 126 return ret;
1fb5c2bf7348d06 Remi Buisson 2025-07-10 127 }
1fb5c2bf7348d06 Remi Buisson 2025-07-10 128
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 6/8] iio: imu: inv_icm45600: add SPI driver for inv_icm45600 driver
2025-07-11 4:55 ` kernel test robot
@ 2025-07-11 6:09 ` Andy Shevchenko
2025-07-15 9:18 ` Remi Buisson
0 siblings, 1 reply; 46+ messages in thread
From: Andy Shevchenko @ 2025-07-11 6:09 UTC (permalink / raw)
To: kernel test robot
Cc: Remi Buisson via B4 Relay, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, oe-kbuild-all, linux-kernel, linux-iio, devicetree,
Remi Buisson
On Fri, Jul 11, 2025 at 12:55:25PM +0800, kernel test robot wrote:
> Hi Remi,
>
> drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c:100:20: note: initialize the variable 'sleep' to silence this warning
> 100 | unsigned int sleep;
> | ^
> | = 0
For the record, this is usually bad advice by the compiler. You need to either
check for errors, or do something else.
> drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c:100:20: note: initialize the variable 'sleep' to silence this warning
> 100 | unsigned int sleep;
> | ^
> | = 0
Ditto.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 5/8] iio: imu: inv_icm45600: add I2C driver for inv_icm45600 driver
2025-07-10 8:58 ` [PATCH v2 5/8] iio: imu: inv_icm45600: add I2C driver for inv_icm45600 driver Remi Buisson via B4 Relay
@ 2025-07-11 7:24 ` kernel test robot
2025-07-14 20:21 ` Dan Carpenter
1 sibling, 0 replies; 46+ messages in thread
From: kernel test robot @ 2025-07-11 7:24 UTC (permalink / raw)
To: Remi Buisson via B4 Relay, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: oe-kbuild-all, linux-kernel, linux-iio, devicetree, Remi Buisson
Hi Remi,
kernel test robot noticed the following build warnings:
[auto build test WARNING on f8f559752d573a051a984adda8d2d1464f92f954]
url: https://github.com/intel-lab-lkp/linux/commits/Remi-Buisson-via-B4-Relay/dt-bindings-iio-imu-Add-inv_icm45600/20250710-170143
base: f8f559752d573a051a984adda8d2d1464f92f954
patch link: https://lore.kernel.org/r/20250710-add_newport_driver-v2-5-bf76d8142ef2%40tdk.com
patch subject: [PATCH v2 5/8] iio: imu: inv_icm45600: add I2C driver for inv_icm45600 driver
config: m68k-allmodconfig (https://download.01.org/0day-ci/archive/20250711/202507111539.F4FrQltx-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 15.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250711/202507111539.F4FrQltx-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202507111539.F4FrQltx-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/iio/imu/inv_icm45600/inv_icm45600_core.c:894:12: warning: 'inv_icm45600_runtime_resume' defined but not used [-Wunused-function]
894 | static int inv_icm45600_runtime_resume(struct device *dev)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/iio/imu/inv_icm45600/inv_icm45600_core.c:875:12: warning: 'inv_icm45600_runtime_suspend' defined but not used [-Wunused-function]
875 | static int inv_icm45600_runtime_suspend(struct device *dev)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/iio/imu/inv_icm45600/inv_icm45600_core.c:832:12: warning: 'inv_icm45600_resume' defined but not used [-Wunused-function]
832 | static int inv_icm45600_resume(struct device *dev)
| ^~~~~~~~~~~~~~~~~~~
>> drivers/iio/imu/inv_icm45600/inv_icm45600_core.c:790:12: warning: 'inv_icm45600_suspend' defined but not used [-Wunused-function]
790 | static int inv_icm45600_suspend(struct device *dev)
| ^~~~~~~~~~~~~~~~~~~~
vim +/inv_icm45600_runtime_resume +894 drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
01fd98fed267cd Remi Buisson 2025-07-10 785
01fd98fed267cd Remi Buisson 2025-07-10 786 /*
01fd98fed267cd Remi Buisson 2025-07-10 787 * Suspend saves sensors state and turns everything off.
01fd98fed267cd Remi Buisson 2025-07-10 788 * Check first if runtime suspend has not already done the job.
01fd98fed267cd Remi Buisson 2025-07-10 789 */
01fd98fed267cd Remi Buisson 2025-07-10 @790 static int inv_icm45600_suspend(struct device *dev)
01fd98fed267cd Remi Buisson 2025-07-10 791 {
01fd98fed267cd Remi Buisson 2025-07-10 792 struct inv_icm45600_state *st = dev_get_drvdata(dev);
01fd98fed267cd Remi Buisson 2025-07-10 793 int ret;
01fd98fed267cd Remi Buisson 2025-07-10 794
01fd98fed267cd Remi Buisson 2025-07-10 795 guard(mutex)(&st->lock);
01fd98fed267cd Remi Buisson 2025-07-10 796
01fd98fed267cd Remi Buisson 2025-07-10 797 st->suspended.gyro = st->conf.gyro.mode;
01fd98fed267cd Remi Buisson 2025-07-10 798 st->suspended.accel = st->conf.accel.mode;
01fd98fed267cd Remi Buisson 2025-07-10 799 if (pm_runtime_suspended(dev))
01fd98fed267cd Remi Buisson 2025-07-10 800 return 0;
01fd98fed267cd Remi Buisson 2025-07-10 801
eaedaea6990c59 Remi Buisson 2025-07-10 802 /* Disable FIFO data streaming. */
eaedaea6990c59 Remi Buisson 2025-07-10 803 if (st->fifo.on) {
eaedaea6990c59 Remi Buisson 2025-07-10 804 unsigned int val;
eaedaea6990c59 Remi Buisson 2025-07-10 805
eaedaea6990c59 Remi Buisson 2025-07-10 806 ret = regmap_clear_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3,
eaedaea6990c59 Remi Buisson 2025-07-10 807 INV_ICM45600_FIFO_CONFIG3_IF_EN);
eaedaea6990c59 Remi Buisson 2025-07-10 808 if (ret)
eaedaea6990c59 Remi Buisson 2025-07-10 809 return ret;
eaedaea6990c59 Remi Buisson 2025-07-10 810 val = FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK,
eaedaea6990c59 Remi Buisson 2025-07-10 811 INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS);
eaedaea6990c59 Remi Buisson 2025-07-10 812 ret = regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0,
eaedaea6990c59 Remi Buisson 2025-07-10 813 INV_ICM45600_FIFO_CONFIG0_MODE_MASK, val);
eaedaea6990c59 Remi Buisson 2025-07-10 814 if (ret)
eaedaea6990c59 Remi Buisson 2025-07-10 815 return ret;
eaedaea6990c59 Remi Buisson 2025-07-10 816 }
eaedaea6990c59 Remi Buisson 2025-07-10 817
01fd98fed267cd Remi Buisson 2025-07-10 818 ret = inv_icm45600_set_pwr_mgmt0(st, INV_ICM45600_SENSOR_MODE_OFF,
01fd98fed267cd Remi Buisson 2025-07-10 819 INV_ICM45600_SENSOR_MODE_OFF, NULL);
01fd98fed267cd Remi Buisson 2025-07-10 820 if (ret)
01fd98fed267cd Remi Buisson 2025-07-10 821 return ret;
01fd98fed267cd Remi Buisson 2025-07-10 822
01fd98fed267cd Remi Buisson 2025-07-10 823 regulator_disable(st->vddio_supply);
01fd98fed267cd Remi Buisson 2025-07-10 824
01fd98fed267cd Remi Buisson 2025-07-10 825 return 0;
01fd98fed267cd Remi Buisson 2025-07-10 826 }
01fd98fed267cd Remi Buisson 2025-07-10 827
01fd98fed267cd Remi Buisson 2025-07-10 828 /*
01fd98fed267cd Remi Buisson 2025-07-10 829 * System resume gets the system back on and restores the sensors state.
01fd98fed267cd Remi Buisson 2025-07-10 830 * Manually put runtime power management in system active state.
01fd98fed267cd Remi Buisson 2025-07-10 831 */
01fd98fed267cd Remi Buisson 2025-07-10 @832 static int inv_icm45600_resume(struct device *dev)
01fd98fed267cd Remi Buisson 2025-07-10 833 {
01fd98fed267cd Remi Buisson 2025-07-10 834 struct inv_icm45600_state *st = dev_get_drvdata(dev);
eaedaea6990c59 Remi Buisson 2025-07-10 835 struct inv_icm45600_sensor_state *gyro_st = iio_priv(st->indio_gyro);
eaedaea6990c59 Remi Buisson 2025-07-10 836 struct inv_icm45600_sensor_state *accel_st = iio_priv(st->indio_accel);
01fd98fed267cd Remi Buisson 2025-07-10 837 int ret;
01fd98fed267cd Remi Buisson 2025-07-10 838
01fd98fed267cd Remi Buisson 2025-07-10 839 guard(mutex)(&st->lock);
01fd98fed267cd Remi Buisson 2025-07-10 840
01fd98fed267cd Remi Buisson 2025-07-10 841 ret = inv_icm45600_enable_regulator_vddio(st);
01fd98fed267cd Remi Buisson 2025-07-10 842 if (ret)
01fd98fed267cd Remi Buisson 2025-07-10 843 return ret;
01fd98fed267cd Remi Buisson 2025-07-10 844
01fd98fed267cd Remi Buisson 2025-07-10 845 pm_runtime_disable(dev);
01fd98fed267cd Remi Buisson 2025-07-10 846 pm_runtime_set_active(dev);
01fd98fed267cd Remi Buisson 2025-07-10 847 pm_runtime_enable(dev);
01fd98fed267cd Remi Buisson 2025-07-10 848
01fd98fed267cd Remi Buisson 2025-07-10 849 /* Restore sensors state. */
eaedaea6990c59 Remi Buisson 2025-07-10 850 ret = inv_icm45600_set_pwr_mgmt0(st, st->suspended.gyro,
01fd98fed267cd Remi Buisson 2025-07-10 851 st->suspended.accel, NULL);
eaedaea6990c59 Remi Buisson 2025-07-10 852 if (ret)
eaedaea6990c59 Remi Buisson 2025-07-10 853 return ret;
01fd98fed267cd Remi Buisson 2025-07-10 854
eaedaea6990c59 Remi Buisson 2025-07-10 855 /* Restore FIFO data streaming. */
eaedaea6990c59 Remi Buisson 2025-07-10 856 if (st->fifo.on) {
eaedaea6990c59 Remi Buisson 2025-07-10 857 unsigned int val;
eaedaea6990c59 Remi Buisson 2025-07-10 858
eaedaea6990c59 Remi Buisson 2025-07-10 859 inv_sensors_timestamp_reset(&gyro_st->ts);
eaedaea6990c59 Remi Buisson 2025-07-10 860 inv_sensors_timestamp_reset(&accel_st->ts);
eaedaea6990c59 Remi Buisson 2025-07-10 861 val = FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK,
eaedaea6990c59 Remi Buisson 2025-07-10 862 INV_ICM45600_FIFO_CONFIG0_MODE_STREAM);
eaedaea6990c59 Remi Buisson 2025-07-10 863 ret = regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0,
eaedaea6990c59 Remi Buisson 2025-07-10 864 INV_ICM45600_FIFO_CONFIG0_MODE_MASK, val);
eaedaea6990c59 Remi Buisson 2025-07-10 865 if (ret)
eaedaea6990c59 Remi Buisson 2025-07-10 866 return ret;
eaedaea6990c59 Remi Buisson 2025-07-10 867 ret = regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3,
eaedaea6990c59 Remi Buisson 2025-07-10 868 INV_ICM45600_FIFO_CONFIG3_IF_EN);
eaedaea6990c59 Remi Buisson 2025-07-10 869 }
eaedaea6990c59 Remi Buisson 2025-07-10 870
eaedaea6990c59 Remi Buisson 2025-07-10 871 return ret;
01fd98fed267cd Remi Buisson 2025-07-10 872 }
01fd98fed267cd Remi Buisson 2025-07-10 873
01fd98fed267cd Remi Buisson 2025-07-10 874 /* Runtime suspend will turn off sensors that are enabled by iio devices. */
01fd98fed267cd Remi Buisson 2025-07-10 @875 static int inv_icm45600_runtime_suspend(struct device *dev)
01fd98fed267cd Remi Buisson 2025-07-10 876 {
01fd98fed267cd Remi Buisson 2025-07-10 877 struct inv_icm45600_state *st = dev_get_drvdata(dev);
01fd98fed267cd Remi Buisson 2025-07-10 878 int ret;
01fd98fed267cd Remi Buisson 2025-07-10 879
01fd98fed267cd Remi Buisson 2025-07-10 880 guard(mutex)(&st->lock);
01fd98fed267cd Remi Buisson 2025-07-10 881
01fd98fed267cd Remi Buisson 2025-07-10 882 /* disable all sensors */
01fd98fed267cd Remi Buisson 2025-07-10 883 ret = inv_icm45600_set_pwr_mgmt0(st, INV_ICM45600_SENSOR_MODE_OFF,
01fd98fed267cd Remi Buisson 2025-07-10 884 INV_ICM45600_SENSOR_MODE_OFF, NULL);
01fd98fed267cd Remi Buisson 2025-07-10 885 if (ret)
01fd98fed267cd Remi Buisson 2025-07-10 886 return ret;
01fd98fed267cd Remi Buisson 2025-07-10 887
01fd98fed267cd Remi Buisson 2025-07-10 888 regulator_disable(st->vddio_supply);
01fd98fed267cd Remi Buisson 2025-07-10 889
01fd98fed267cd Remi Buisson 2025-07-10 890 return 0;
01fd98fed267cd Remi Buisson 2025-07-10 891 }
01fd98fed267cd Remi Buisson 2025-07-10 892
01fd98fed267cd Remi Buisson 2025-07-10 893 /* Sensors are enabled by iio devices, no need to turn them back on here. */
01fd98fed267cd Remi Buisson 2025-07-10 @894 static int inv_icm45600_runtime_resume(struct device *dev)
01fd98fed267cd Remi Buisson 2025-07-10 895 {
01fd98fed267cd Remi Buisson 2025-07-10 896 struct inv_icm45600_state *st = dev_get_drvdata(dev);
01fd98fed267cd Remi Buisson 2025-07-10 897
01fd98fed267cd Remi Buisson 2025-07-10 898 guard(mutex)(&st->lock);
01fd98fed267cd Remi Buisson 2025-07-10 899
01fd98fed267cd Remi Buisson 2025-07-10 900 return inv_icm45600_enable_regulator_vddio(st);
01fd98fed267cd Remi Buisson 2025-07-10 901 }
01fd98fed267cd Remi Buisson 2025-07-10 902
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 46+ messages in thread
* RE: [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver
2025-07-10 9:29 ` Andy Shevchenko
@ 2025-07-11 11:39 ` Remi Buisson
[not found] ` <FR2PPF4571F02BC5366477EC02E9C44041A8C4BA@FR2PPF4571F02BC.DEUP281.PROD.OUTLOOK.COM>
1 sibling, 0 replies; 46+ messages in thread
From: Remi Buisson @ 2025-07-11 11:39 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org
>
>
>From: Andy Shevchenko andriy.shevchenko@intel.com
>Sent: Thursday, July 10, 2025 11:30 AM
>To: Remi Buisson Remi.Buisson@tdk.com
>Cc: Jonathan Cameron jic23@kernel.org; David Lechner dlechner@baylibre.com; Nuno Sá nuno.sa@analog.com; Andy Shevchenko andy@kernel.org; Rob Herring robh@kernel.org; Krzysztof Kozlowski krzk+dt@kernel.org; Conor Dooley conor+dt@kernel.org; linux-kernel@vger.kernel.org; linux-iio@vger.kernel.org; devicetree@vger.kernel.org
>Subject: Re: [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver
>
>On Thu, Jul 10, 2025 at 08:57:57AM +0000, Remi Buisson via B4 Relay wrote:
>> From: Remi Buisson remi.buisson@tdk.com
>>
>> Core component of a new driver for InvenSense ICM-45600 devices.
>> It includes registers definition, main probe/setup, and device
>> utility functions.
>>
>> ICM-456xx devices are latest generation of 6-axis IMU,
>> gyroscope+accelerometer and temperature sensor. This device
>> includes a 8K FIFO, supports I2C/I3C/SPI, and provides
>> intelligent motion features like pedometer, tilt detection,
>> and tap detection.
>
>...
>
>> + INV_ICM45600_SENSOR_MODE_NB
>
>What does the _NB stand for? Number of Bullets?
Hi Andy,
Thanks for your review !
I will rename it to something more explicit like INV_ICM45600_SENSOR_MODE_NUMBER
>
>...
>
>> +struct inv_icm45600_sensor_conf {
>> + int mode;
>> + int fs;
>> + int odr;
>> + int filter;
>
>Any of them can hold negative value?
Yes, when setting the configuration, a negative value means "keep previous configured value".
Like in the INV_ICM45600_SENSOR_CONF_INIT macro below.
>
>> +};
>
>...
>
>> +#define INV_ICM45600_SENSOR_CONF_INIT {-1, -1, -1, -1}
>
>Unused.
This is used in later patch of the serie.
I will move this definition to the patch using it.
>
>
>> +#include <linux/bitfield.h>
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <linux/err.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/irq.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/property.h>
>> +#include <linux/regmap.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/types.h>
>
>...
>
>> +static int inv_icm45600_ireg_read(struct regmap *map, unsigned int reg,
>> + u8 *data, size_t count)
>> +{
>> + int ret;
>> + u8 addr[2];
>> + ssize_t i;
>> + unsigned int d;
>> +
>> + addr[0] = FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg);
>> + addr[1] = FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg);
>> +
>> + /* Burst write address. */
>> + ret = regmap_bulk_write(map, INV_ICM45600_REG_IREG_ADDR, addr, 2);
>
>sizeof()?
Sure, will be better.
>
>> + udelay(INV_ICM45600_IREG_DELAY_US);
>
>See below. This is also weird.
This function implement indirect register access, requiring following sequence to be applied:
1. write indirect register address
2. wait for the data register to be updated
3. read register address
I will add a comment to explain what's is going on here.
>
>> + if (ret)
>> + return ret;
>> +
>> + /* Read the data. */
>> + for (i = 0; i < count; i++) {
>> + ret = regmap_read(map, INV_ICM45600_REG_IREG_DATA, &d);
>> + data[i] = d;
>> + udelay(INV_ICM45600_IREG_DELAY_US);
>
>Can fsleep() be used here?
I will use fsleep there.
Is it recommended to use fsleep everywhere else in place of other sleep APIs?
>
>> + if (ret)
>> + return ret;
>
>This is weird. First you assign a garbage to the output, delay and return
>an error. It seems entire code is broken...
>Please, fix all these and try again, I stop my review here.
Agreed, data assignment must be avoided in case of error.
It's probably safer to keep the delay even in case of failure to make sure the device is ready before next operation.
>
>> + }
>> +
>> + return 0;
>> +}
>
>--
>With Best Regards,
>Andy Shevchenko
>
>
>
^ permalink raw reply [flat|nested] 46+ messages in thread
* RE: [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600
2025-07-10 22:41 ` Rob Herring
@ 2025-07-11 11:40 ` Remi Buisson
0 siblings, 0 replies; 46+ messages in thread
From: Remi Buisson @ 2025-07-11 11:40 UTC (permalink / raw)
To: Rob Herring
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Krzysztof Kozlowski, Conor Dooley, linux-kernel@vger.kernel.org,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org
>
>
>From: Rob Herring robh@kernel.org
>Sent: Friday, July 11, 2025 12:41 AM
>To: Remi Buisson Remi.Buisson@tdk.com
>Cc: Jonathan Cameron jic23@kernel.org; David Lechner dlechner@baylibre.com; Nuno Sá nuno.sa@analog.com; Andy Shevchenko andy@kernel.org; Krzysztof Kozlowski krzk+dt@kernel.org; Conor Dooley conor+dt@kernel.org; linux-kernel@vger.kernel.org; linux-iio@vger.kernel.org; devicetree@vger.kernel.org
>Subject: Re: [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600
>
>On Thu, Jul 10, 2025 at 08:57:56AM +0000, Remi Buisson wrote:
>> Document the ICM-45600 devices devicetree bindings.
>>
>> Signed-off-by: Remi Buisson remi.buisson@tdk.com
>> ---
>> .../bindings/iio/imu/invensense,icm45600.yaml | 138 +++++++++++++++++++++
>> 1 file changed, 138 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm45600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm45600.yaml
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..a651878791ffae8d1c8d6c8ff1e4becfc56af79f
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm45600.yaml
>> @@ -0,0 +1,138 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: https://urldefense.com/v3/__http://devicetree.org/schemas/iio/imu/invensense,icm45600.yaml*__;Iw!!FtrhtPsWDhZ6tw!EJ6o-tnOtuJYQRU8150mnYpf0uCMnYOSvfBK-DKo5kDvwheDRaLvPeQpbZ03mKXaPTGv5T8JO5sT$[devicetree[.]org]
>> +$schema: https://urldefense.com/v3/__http://devicetree.org/meta-schemas/core.yaml*__;Iw!!FtrhtPsWDhZ6tw!EJ6o-tnOtuJYQRU8150mnYpf0uCMnYOSvfBK-DKo5kDvwheDRaLvPeQpbZ03mKXaPTGv5SV7Z3My$[devicetree[.]org]
>> +
>> +title: InvenSense ICM-45600 Inertial Measurement Unit
>> +
>> +maintainers:
>> + - Remi Buisson remi.buisson@tdk.com
>> +
>> +description: |
>> + 6-axis MotionTracking device that combines a 3-axis gyroscope and a 3-axis
>> + accelerometer.
>> +
>> + It has a configurable host interface that supports I3C, I2C and SPI serial
>> + communication, features up to 8kB FIFO and 2 programmable interrupts with
>> + ultra-low-power wake-on-motion support to minimize system power consumption.
>> +
>> + Other industry-leading features include InvenSense on-chip APEX Motion
>> + Processing engine for gesture recognition, activity classification, and
>> + pedometer, along with programmable digital filters, and an embedded
>> + temperature sensor.
>> +
>> + https://invensense.tdk.com/wp-content/uploads/documentation/DS-000576_ICM-45605.pdf
>> +
>> +properties:
>> + compatible:
>> + enum:
>> + - invensense,icm45605
>> + - invensense,icm45606
>> + - invensense,icm45608
>> + - invensense,icm45634
>> + - invensense,icm45686
>> + - invensense,icm45687
>> + - invensense,icm45688p
>> + - invensense,icm45689
>> +
>> + reg:
>> + maxItems: 1
>> +
>> + interrupts:
>> + minItems: 1
>> + maxItems: 2
>> +
>> + interrupt-names:
>> + minItems: 1
>> + maxItems: 2
>> + items:
>> + enum:
>> + - INT1
>> + - INT2
>> + description: Choose chip interrupt pin to be used as interrupt input.
>> +
>> + drive-open-drain:
>> + type: boolean
>> +
>> + vdd-supply:
>> + description: Regulator that provides power to the sensor
>> +
>> + vddio-supply:
>> + description: Regulator that provides power to the bus
>> +
>> + mount-matrix:
>> + description: an optional 3x3 mounting rotation matrix
>> +
>> +required:
>> + - compatible
>> + - reg
>> + - interrupts
>> + - interrupt-names
>> +
>> +allOf:
>> + - $ref: /schemas/spi/spi-peripheral-props.yaml#
>> +
>> +unevaluatedProperties: false
>> +
>> +examples:
>> + - |
>> + #include <dt-bindings/gpio/gpio.h>
>> + #include <dt-bindings/interrupt-controller/irq.h>
>> + i2c {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + icm45605@68 {
>> + compatible = "invensense,icm45605";
>> + reg = <0x68>;
>> + interrupt-parent = <&gpio2>;
>> + interrupt-names = "INT1";
>> + interrupts = <7 IRQ_TYPE_EDGE_RISING>;
>> + vdd-supply = <&vdd>;
>> + vddio-supply = <&vddio>;
>> + mount-matrix = "0", "-1", "0",
>> + "1", "0", "0",
>> + "0", "0", "1";
>> + };
>> + };
>> + - |
>> + #include <dt-bindings/gpio/gpio.h>
>> + #include <dt-bindings/interrupt-controller/irq.h>
>> + spi {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + icm45605@0 {
>> + compatible = "invensense,icm45605";
>> + reg = <0>;
>> + spi-max-frequency = <24000000>;
>> + interrupt-parent = <&gpio1>;
>> + interrupt-names = "INT1";
>> + interrupts = <6 IRQ_TYPE_EDGE_RISING>;
>> + vdd-supply = <&vdd>;
>> + vddio-supply = <&vddio>;
>> + mount-matrix = "0", "-1", "0",
>> + "1", "0", "0",
>> + "0", "0", "1";
>> + };
>> + };
>> + - |
>> + #include <dt-bindings/gpio/gpio.h>
>> + #include <dt-bindings/interrupt-controller/irq.h>
>> + i3c {
>> + #address-cells = <3>;
>> + #size-cells = <0>;
>> +
>> + icm45606@68,46A00000084 {
>> + reg = <0x68 0x46A 0x84>;
>> + interrupt-parent = <&gpio1>;
>> + interrupt-names = "INT1";
>> + interrupts = <5 IRQ_TYPE_EDGE_RISING>;
>> + vdd-supply = <&vdd>;
>> + vddio-supply = <&vddio>;
>> + mount-matrix = "0", "-1", "0",
>> + "1", "0", "0",
>> + "0", "0", "1";
>> + };
>> + };
>
>I don't think we need 3 examples just for different buses being the only
>diff.
>
>Rob
>
Hello Rob,
Thanks for your feedback, I will simplify it then.
Remi
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver
[not found] ` <FR2PPF4571F02BC5366477EC02E9C44041A8C4BA@FR2PPF4571F02BC.DEUP281.PROD.OUTLOOK.COM>
@ 2025-07-11 11:55 ` Andy Shevchenko
2025-07-15 9:11 ` Remi Buisson
0 siblings, 1 reply; 46+ messages in thread
From: Andy Shevchenko @ 2025-07-11 11:55 UTC (permalink / raw)
To: Remi Buisson
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org
On Fri, Jul 11, 2025 at 11:32:48AM +0000, Remi Buisson wrote:
> >From: Andy Shevchenko andriy.shevchenko@intel.com<mailto:andriy.shevchenko@intel.com>
> >Sent: Thursday, July 10, 2025 11:30 AM
> >To: Remi Buisson Remi.Buisson@tdk.com<mailto:Remi.Buisson@tdk.com>
> >On Thu, Jul 10, 2025 at 08:57:57AM +0000, Remi Buisson via B4 Relay wrote:
...
> >> +struct inv_icm45600_sensor_conf {
> >> + int mode;
> >> + int fs;
> >> + int odr;
> >> + int filter;
> >
> >Any of them can hold negative value?
>
> Yes, when setting the configuration, a negative value means "keep previous configured value".
> Like in the INV_ICM45600_SENSOR_CONF_INIT macro below.
I see, but can't it be as simple as "big number" with the proper type instead?
#define ..._KEEP_VALUE U8_MAX // or whatever type
> >> +};
...
> >> +#define INV_ICM45600_SENSOR_CONF_INIT {-1, -1, -1, -1}
> >
> >Unused.
> This is used in later patch of the serie.
> I will move this definition to the patch using it.
Yes, unused in this code. You should compile the series incrementally,
so each patch will get a compilation test. This is called compile-time
bisectability. Also run the system each time to confirm no regressions
(this is called run-time bisectability).
...
> >> + udelay(INV_ICM45600_IREG_DELAY_US);
> >
> >Can fsleep() be used here?
> I will use fsleep there.
> Is it recommended to use fsleep everywhere else in place of other sleep APIs?
Not everywhere, but in general yes, if in doubt, use it (but the author is
the one who knows their the code and answer to this question).
...
> It's probably safer to keep the delay even in case of failure to make sure
> the device is ready before next operation.
I am not sure about it. Why? This has to be well justified as it's quite
unusual pattern.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver
2025-07-10 8:57 ` [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver Remi Buisson via B4 Relay
2025-07-10 9:29 ` Andy Shevchenko
2025-07-11 2:58 ` kernel test robot
@ 2025-07-13 16:00 ` Jonathan Cameron
2025-07-15 9:33 ` Remi Buisson
2 siblings, 1 reply; 46+ messages in thread
From: Jonathan Cameron @ 2025-07-13 16:00 UTC (permalink / raw)
To: Remi Buisson via B4 Relay
Cc: remi.buisson, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, linux-kernel,
linux-iio, devicetree, Sean Nyekjaer
On Thu, 10 Jul 2025 08:57:57 +0000
Remi Buisson via B4 Relay <devnull+remi.buisson.tdk.com@kernel.org> wrote:
> From: Remi Buisson <remi.buisson@tdk.com>
>
> Core component of a new driver for InvenSense ICM-45600 devices.
> It includes registers definition, main probe/setup, and device
> utility functions.
>
> ICM-456xx devices are latest generation of 6-axis IMU,
> gyroscope+accelerometer and temperature sensor. This device
> includes a 8K FIFO, supports I2C/I3C/SPI, and provides
> intelligent motion features like pedometer, tilt detection,
> and tap detection.
>
> Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
Hi Remi,
A few minor things inline. Given Sean has been looking at a closely
related driver and you have some of the things he's been fixing up
I +CC Sean on off chance he has time to take a look at this.
> ---
> drivers/iio/imu/Kconfig | 1 +
> drivers/iio/imu/Makefile | 1 +
> drivers/iio/imu/inv_icm45600/Kconfig | 5 +
> drivers/iio/imu/inv_icm45600/Makefile | 4 +
> drivers/iio/imu/inv_icm45600/inv_icm45600.h | 364 +++++++++++
> drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 734 +++++++++++++++++++++++
> 6 files changed, 1109 insertions(+)
>
> diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600.h b/drivers/iio/imu/inv_icm45600/inv_icm45600.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..d56de75ab7f2168f22e25b5816cb361bef457c0d
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h
> @@ -0,0 +1,364 @@
> +struct inv_icm45600_sensor_conf {
> + int mode;
> + int fs;
> + int odr;
> + int filter;
> +};
> +#define INV_ICM45600_SENSOR_CONF_INIT {-1, -1, -1, -1}
+#define INV_ICM45600_SENSOR_CONF_INIT { -1, -1, -1, -1, }
Though I'm not sure the define is that useful vs pushing this down into the code.
> diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..022eb9180b5a750e8fba4a1cd873041c27f59880
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
> +static int inv_icm45600_read(void *context, const void *reg_buf, size_t reg_size,
> + void *val_buf, size_t val_size)
> +{
> + unsigned int reg = (unsigned int) be16_to_cpup(reg_buf);
Why is the cast needed? It's a u16 so why not keep it like that?
> + struct regmap *map = context;
> +
> + if (FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg) == 0)
> + return regmap_bulk_read(map, FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg),
> + val_buf, val_size);
> +
> + return inv_icm45600_ireg_read(map, reg, val_buf, val_size);
> +}
> +
> +static int inv_icm45600_write(void *context, const void *data,
> + size_t count)
> +{
> + u8 *d = (u8 *)data;
const u8 *d = data;
should be fine.
> + unsigned int reg = (unsigned int) be16_to_cpup(data);
u16
> + struct regmap *map = context;
> +
> + if (FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg) == 0)
> + return regmap_bulk_write(map, FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg),
> + d + 2, count - 2);
> +
> + return inv_icm45600_ireg_write(map, reg, d + 2, count - 2);
> +}
> + * inv_icm45600_setup() - check and setup chip
> + * @st: driver internal state
> + * @chip_info: detected chip description
> + * @reset: define whether a reset is required or not
> + * @bus_setup: callback for setting up bus specific registers
> + *
> + * Returns 0 on success, a negative error code otherwise.
> + */
> +static int inv_icm45600_setup(struct inv_icm45600_state *st,
> + const struct inv_icm45600_chip_info *chip_info,
> + bool reset, inv_icm45600_bus_setup bus_setup)
> +{
> + const struct device *dev = regmap_get_device(st->map);
> + unsigned int val;
> + int ret;
> +
> + /* Set chip bus configuration if specified. */
> + if (bus_setup) {
> + ret = bus_setup(st);
> + if (ret)
> + return ret;
> + }
> +
> + /* Check chip self-identification value. */
> + ret = regmap_read(st->map, INV_ICM45600_REG_WHOAMI, &val);
> + if (ret)
> + return ret;
> + if (val != chip_info->whoami) {
> + dev_err(dev, "invalid whoami %#02x expected %#02x (%s)\n",
This should be relaxed to a dev_info and carry on anyway. The reasoning
for this is fallback compatibles in DT. If a new compatible chip
with a different whoami value comes along we still want it to run
with older kernels so we use fallback compatibles. Unfortunately the
ID won't match.
> + val, chip_info->whoami, chip_info->name);
> + return -ENODEV;
> + }
> +
> + st->chip_info = chip_info;
> +
> + if (reset) {
> + /* Reset to make sure previous state are not there. */
> + ret = regmap_write(st->map, INV_ICM45600_REG_MISC2,
> + INV_ICM45600_MISC2_SOFT_RESET);
> + if (ret)
> + return ret;
> + /* IMU reset time: 1ms. */
> + fsleep(1000);
> +
> + if (bus_setup) {
> + ret = bus_setup(st);
> + if (ret)
> + return ret;
> + }
> +
> + ret = regmap_read(st->map, INV_ICM45600_REG_INT_STATUS, &val);
> + if (ret)
> + return ret;
> + if (!(val & INV_ICM45600_INT_STATUS_RESET_DONE)) {
> + dev_err(dev, "reset error, reset done bit not set\n");
> + return -ENODEV;
> + }
> + }
> +
> + return inv_icm45600_set_conf(st, chip_info->conf);
> +}
> +
> +static void inv_icm45600_disable_vddio_reg(void *_data)
> +{
> + struct inv_icm45600_state *st = _data;
> + const struct device *dev = regmap_get_device(st->map);
> + int ret;
> +
> + ret = regulator_disable(st->vddio_supply);
> + if (ret)
> + dev_err(dev, "failed to disable vddio error %d\n", ret);
As per Sean's set - is this useful given such failures are already noisy.
> +}
> +
> +int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm45600_chip_info *chip_info,
> + bool reset, inv_icm45600_bus_setup bus_setup)
> +{
> + struct device *dev = regmap_get_device(regmap);
> + struct fwnode_handle *fwnode;
> + struct inv_icm45600_state *st;
> + struct regmap *regmap_custom;
> + int irq, irq_type;
> + bool open_drain;
> + int ret;
> +
> + /* Get INT1 only supported interrupt. */
> + fwnode = dev_fwnode(dev);
> + if (!fwnode)
> + return dev_err_probe(dev, -ENODEV, "Missing FW node\n");
> +
> + irq = fwnode_irq_get_byname(fwnode, "INT1");
> + if (irq < 0) {
> + if (irq != -EPROBE_DEFER)
> + dev_err_probe(dev, irq, "Missing INT1 interrupt\n");
> + return irq;
> + }
> +
> + irq_type = irq_get_trigger_type(irq);
> +
> + open_drain = device_property_read_bool(dev, "drive-open-drain");
> +
> + regmap_custom = devm_regmap_init(dev, &inv_icm45600_regmap_bus,
> + regmap, &inv_icm45600_regmap_config);
> + if (IS_ERR(regmap_custom))
> + return dev_err_probe(dev, PTR_ERR(regmap_custom), "Failed to register regmap\n");
> +
> + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
> + if (!st)
> + return dev_err_probe(dev, -ENOMEM, "Cannot allocate memory\n");
> +
> + dev_set_drvdata(dev, st);
> + ret = devm_mutex_init(dev, &st->lock);
> + if (ret)
> + return ret;
> +
> + st->map = regmap_custom;
> +
> + ret = iio_read_mount_matrix(dev, &st->orientation);
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to retrieve mounting matrix\n");
> +
> + st->vddio_supply = devm_regulator_get(dev, "vddio");
> + if (IS_ERR(st->vddio_supply))
> + return PTR_ERR(st->vddio_supply);
> +
> + ret = devm_regulator_get_enable(dev, "vdd");
> + if (ret)
> + return ret;
> + /* IMU start-up time. */
> + msleep(100);
> +
> + ret = inv_icm45600_enable_regulator_vddio(st);
> + if (ret)
> + return ret;
> +
> + ret = devm_add_action_or_reset(dev, inv_icm45600_disable_vddio_reg, st);
> + if (ret)
> + return ret;
> +
> + /* Setup chip registers. */
> + ret = inv_icm45600_setup(st, chip_info, reset, bus_setup);
> + if (ret)
> + return ret;
> +
> + ret = inv_icm45600_timestamp_setup(st);
> + if (ret)
> + return ret;
> +
> + /* Setup runtime power management. */
> + ret = devm_pm_runtime_set_active_enabled(dev);
> + if (ret)
> + return ret;
> +
> + pm_runtime_get_noresume(dev);
> + /* Suspend after 2 seconds. */
> + pm_runtime_set_autosuspend_delay(dev, 2000);
> + pm_runtime_use_autosuspend(dev);
> + pm_runtime_put(dev);
See below. Sean also reworks this. + I suggested he look at
devm_pm_runtime_enable but the set active version seems even better.
I'm not setting the gets and puts I'd expect for working runtime PM though.
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(inv_icm45600_core_probe, "IIO_ICM45600");
> +
> +/*
> + * System resume gets the system back on and restores the sensors state.
> + * Manually put runtime power management in system active state.
> + */
> +static int inv_icm45600_resume(struct device *dev)
> +{
> + struct inv_icm45600_state *st = dev_get_drvdata(dev);
> + int ret;
> +
> + guard(mutex)(&st->lock);
> +
> + ret = inv_icm45600_enable_regulator_vddio(st);
> + if (ret)
> + return ret;
> +
> + pm_runtime_disable(dev);
> + pm_runtime_set_active(dev);
> + pm_runtime_enable(dev);
Take a look at
https://lore.kernel.org/all/20250709-icm42pmreg-v1-0-3d0e793c99b2@geanix.com/
It would be good if you an review Sean's series as well as applying anything
you agree with here. For example he got rid of the 3 lines above.
> +
> + /* Restore sensors state. */
> + return inv_icm45600_set_pwr_mgmt0(st, st->suspended.gyro,
> + st->suspended.accel, NULL);
> +
> +}
> +
> +/* Runtime suspend will turn off sensors that are enabled by iio devices. */
> +static int inv_icm45600_runtime_suspend(struct device *dev)
> +{
> + struct inv_icm45600_state *st = dev_get_drvdata(dev);
> + int ret;
> +
> + guard(mutex)(&st->lock);
> +
> + /* disable all sensors */
> + ret = inv_icm45600_set_pwr_mgmt0(st, INV_ICM45600_SENSOR_MODE_OFF,
> + INV_ICM45600_SENSOR_MODE_OFF, NULL);
> + if (ret)
> + return ret;
> +
> + regulator_disable(st->vddio_supply);
> +
> + return 0;
> +}
> +
> +/* Sensors are enabled by iio devices, no need to turn them back on here. */
> +static int inv_icm45600_runtime_resume(struct device *dev)
> +{
> + struct inv_icm45600_state *st = dev_get_drvdata(dev);
> +
> + guard(mutex)(&st->lock);
> +
> + return inv_icm45600_enable_regulator_vddio(st);
> +}
> +
> +EXPORT_NS_GPL_DEV_PM_OPS(inv_icm45600_pm_ops, IIO_ICM45600) = {
> + SET_SYSTEM_SLEEP_PM_OPS(inv_icm45600_suspend, inv_icm45600_resume)
> + SET_RUNTIME_PM_OPS(inv_icm45600_runtime_suspend,
> + inv_icm45600_runtime_resume, NULL)
> +};
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600
2025-07-10 8:57 ` [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600 Remi Buisson via B4 Relay
2025-07-10 22:41 ` Rob Herring
@ 2025-07-14 5:38 ` Krzysztof Kozlowski
2025-07-15 8:35 ` Remi Buisson
` (2 more replies)
1 sibling, 3 replies; 46+ messages in thread
From: Krzysztof Kozlowski @ 2025-07-14 5:38 UTC (permalink / raw)
To: remi.buisson, Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-kernel, linux-iio, devicetree
On 10/07/2025 10:57, Remi Buisson via B4 Relay wrote:
> +
> + reg:
> + maxItems: 1
> +
> + interrupts:
> + minItems: 1
> + maxItems: 2
> +
> + interrupt-names:
> + minItems: 1
> + maxItems: 2
> + items:
> + enum:
> + - INT1
> + - INT2
This can be simpler
minItems: 1
items:
- enum: [ int1, int2 ]
- const: int 2
and use lowercase anyway.
> + description: Choose chip interrupt pin to be used as interrupt input.
> +
> + drive-open-drain:
> + type: boolean
> +
> + vdd-supply:
> + description: Regulator that provides power to the sensor
> +
> + vddio-supply:
> + description: Regulator that provides power to the bus
> +
> + mount-matrix:
> + description: an optional 3x3 mounting rotation matrix
> +
> +required:
> + - compatible
> + - reg
> + - interrupts
> + - interrupt-names
Missing supplies
> +
> +allOf:
> + - $ref: /schemas/spi/spi-peripheral-props.yaml#
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/gpio/gpio.h>
> + #include <dt-bindings/interrupt-controller/irq.h>
> + i2c {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + icm45605@68 {
It does not look like you tested the DTS against bindings. Please run
`make dtbs_check W=1` (see
Documentation/devicetree/bindings/writing-schema.rst or
https://www.linaro.org/blog/tips-and-tricks-for-validating-devicetree-sources-with-the-devicetree-schema/
for instructions).
Maybe you need to update your dtschema and yamllint. Don't rely on
distro packages for dtschema and be sure you are using the latest
released dtschema.
(see how other bindings or DTS call this type of device)
> + compatible = "invensense,icm45605";
> + reg = <0x68>;
> + interrupt-parent = <&gpio2>;
> + interrupt-names = "INT1";
> + interrupts = <7 IRQ_TYPE_EDGE_RISING>;
> + vdd-supply = <&vdd>;
> + vddio-supply = <&vddio>;
> + mount-matrix = "0", "-1", "0",
> + "1", "0", "0",
> + "0", "0", "1";
> + };
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 0/8] iio: imu: new inv_icm45600 driver
2025-07-10 8:57 [PATCH v2 0/8] iio: imu: new inv_icm45600 driver Remi Buisson via B4 Relay
` (7 preceding siblings ...)
2025-07-10 8:58 ` [PATCH v2 8/8] MAINTAINERS: add entry for inv_icm45600 6-axis imu sensor Remi Buisson via B4 Relay
@ 2025-07-14 12:06 ` Sean Nyekjaer
2025-07-15 9:03 ` Remi Buisson
8 siblings, 1 reply; 46+ messages in thread
From: Sean Nyekjaer @ 2025-07-14 12:06 UTC (permalink / raw)
To: remi.buisson
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, linux-kernel,
linux-iio, devicetree
On Thu, Jul 10, 2025 at 08:57:55AM +0100, Remi Buisson via B4 Relay wrote:
> This series add a new driver for managing InvenSense ICM-456xx 6-axis IMUs.
> This next generation of chips includes new generations of 3-axis gyroscope
> and 3-axis accelerometer, support of I3C in addition to I2C and SPI, and
> intelligent MotionTracking features like pedometer, tilt detection, and
> tap detection.
>
> This series is delivering a driver supporting gyroscope, accelerometer and
> temperature data, with polling and buffering using hwfifo and watermark,
> on I2C, SPI and I3C busses.
>
> Gyroscope and accelerometer sensors are completely independent and can have
> different ODRs. Since there is only a single FIFO a specific value is used to
> mark invalid data. For keeping the device standard we are de-multiplexing data
> from the FIFO to 2 IIO devices with 2 buffers, 1 for the accelerometer and 1
> for the gyroscope. This architecture also enables to easily turn each sensor
> on/off without impacting the other. The device interrupt is used to read the
> FIFO and launch parsing of accelerometer and gyroscope data. This driver
> relies on the common Invensense timestamping mechanism to handle correctly
> FIFO watermark and dynamic changes of settings.
>
> The structure of the driver is quite similar to the inv_icm42600 driver,
> however there are significant reasons for adding a different driver for
> inv_icm45600, such as:
> - A completely different register map.
At one point we asked TDK/Invense for a driver for icm42670. It also
have a completely different register map... Grrr :S
Anyhow, should we combine these drivers in inv_icm42600? Like
st_lsm6dsx?
/Sean
> - Different FIFO management, based on number of samples instead of bytes.
> - Different indirect register access mechanism.
>
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 7/8] iio: imu: inv_icm45600: add I3C driver for inv_icm45600 driver
2025-07-10 8:58 ` [PATCH v2 7/8] iio: imu: inv_icm45600: add I3C " Remi Buisson via B4 Relay
@ 2025-07-14 12:56 ` Sean Nyekjaer
2025-07-15 8:48 ` Remi Buisson
0 siblings, 1 reply; 46+ messages in thread
From: Sean Nyekjaer @ 2025-07-14 12:56 UTC (permalink / raw)
To: remi.buisson
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, linux-kernel,
linux-iio, devicetree
On Thu, Jul 10, 2025 at 08:58:02AM +0100, Remi Buisson via B4 Relay wrote:
> From: Remi Buisson <remi.buisson@tdk.com>
>
> Add I3C driver for InvenSense ICM-45600 devices.
>
> Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
Is this tested on real hardware?
/Sean
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 5/8] iio: imu: inv_icm45600: add I2C driver for inv_icm45600 driver
2025-07-10 8:58 ` [PATCH v2 5/8] iio: imu: inv_icm45600: add I2C driver for inv_icm45600 driver Remi Buisson via B4 Relay
2025-07-11 7:24 ` kernel test robot
@ 2025-07-14 20:21 ` Dan Carpenter
2025-07-15 8:09 ` Andy Shevchenko
2025-07-15 9:17 ` Remi Buisson
1 sibling, 2 replies; 46+ messages in thread
From: Dan Carpenter @ 2025-07-14 20:21 UTC (permalink / raw)
To: oe-kbuild, Remi Buisson via B4 Relay, Jonathan Cameron,
David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: lkp, oe-kbuild-all, linux-kernel, linux-iio, devicetree,
Remi Buisson
Hi Remi,
kernel test robot noticed the following build warnings:
url: https://github.com/intel-lab-lkp/linux/commits/Remi-Buisson-via-B4-Relay/dt-bindings-iio-imu-Add-inv_icm45600/20250710-170143
base: f8f559752d573a051a984adda8d2d1464f92f954
patch link: https://lore.kernel.org/r/20250710-add_newport_driver-v2-5-bf76d8142ef2%40tdk.com
patch subject: [PATCH v2 5/8] iio: imu: inv_icm45600: add I2C driver for inv_icm45600 driver
config: s390-randconfig-r073-20250712 (https://download.01.org/0day-ci/archive/20250713/202507130115.7g0XWB2E-lkp@intel.com/config)
compiler: clang version 21.0.0git (https://github.com/llvm/llvm-project 01c97b4953e87ae455bd4c41e3de3f0f0f29c61c)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
| Closes: https://lore.kernel.org/r/202507130115.7g0XWB2E-lkp@intel.com/
smatch warnings:
drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c:121 inv_icm45600_gyro_update_scan_mode() error: uninitialized symbol 'sleep'.
drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c:123 inv_icm45600_accel_update_scan_mode() error: uninitialized symbol 'sleep'.
vim +/sleep +121 drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c
1fb5c2bf7348d0 Remi Buisson 2025-07-10 93 static int inv_icm45600_gyro_update_scan_mode(struct iio_dev *indio_dev,
1fb5c2bf7348d0 Remi Buisson 2025-07-10 94 const unsigned long *scan_mask)
1fb5c2bf7348d0 Remi Buisson 2025-07-10 95 {
1fb5c2bf7348d0 Remi Buisson 2025-07-10 96 struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
1fb5c2bf7348d0 Remi Buisson 2025-07-10 97 struct inv_icm45600_sensor_state *gyro_st = iio_priv(indio_dev);
1fb5c2bf7348d0 Remi Buisson 2025-07-10 98 struct inv_icm45600_sensor_conf conf = INV_ICM45600_SENSOR_CONF_INIT;
1fb5c2bf7348d0 Remi Buisson 2025-07-10 99 unsigned int fifo_en = 0;
1fb5c2bf7348d0 Remi Buisson 2025-07-10 100 unsigned int sleep;
1fb5c2bf7348d0 Remi Buisson 2025-07-10 101 int ret;
1fb5c2bf7348d0 Remi Buisson 2025-07-10 102
1fb5c2bf7348d0 Remi Buisson 2025-07-10 103 scoped_guard(mutex, &st->lock) {
1fb5c2bf7348d0 Remi Buisson 2025-07-10 104 if (*scan_mask & BIT(INV_ICM45600_GYRO_SCAN_TEMP))
1fb5c2bf7348d0 Remi Buisson 2025-07-10 105 fifo_en |= INV_ICM45600_SENSOR_TEMP;
1fb5c2bf7348d0 Remi Buisson 2025-07-10 106
1fb5c2bf7348d0 Remi Buisson 2025-07-10 107 if (*scan_mask & (BIT(INV_ICM45600_GYRO_SCAN_X) |
1fb5c2bf7348d0 Remi Buisson 2025-07-10 108 BIT(INV_ICM45600_GYRO_SCAN_Y) |
1fb5c2bf7348d0 Remi Buisson 2025-07-10 109 BIT(INV_ICM45600_GYRO_SCAN_Z))) {
1fb5c2bf7348d0 Remi Buisson 2025-07-10 110 /* enable gyro sensor */
1fb5c2bf7348d0 Remi Buisson 2025-07-10 111 conf.mode = gyro_st->power_mode;
1fb5c2bf7348d0 Remi Buisson 2025-07-10 112 ret = inv_icm45600_set_gyro_conf(st, &conf, &sleep);
sleep isn't necessarily set if nothing changed.
1fb5c2bf7348d0 Remi Buisson 2025-07-10 113 if (ret)
1fb5c2bf7348d0 Remi Buisson 2025-07-10 114 return ret;
1fb5c2bf7348d0 Remi Buisson 2025-07-10 115 fifo_en |= INV_ICM45600_SENSOR_GYRO;
1fb5c2bf7348d0 Remi Buisson 2025-07-10 116 }
1fb5c2bf7348d0 Remi Buisson 2025-07-10 117 /* update data FIFO write */
1fb5c2bf7348d0 Remi Buisson 2025-07-10 118 ret = inv_icm45600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
1fb5c2bf7348d0 Remi Buisson 2025-07-10 119 }
1fb5c2bf7348d0 Remi Buisson 2025-07-10 120 /* sleep required time */
1fb5c2bf7348d0 Remi Buisson 2025-07-10 @121 if (sleep)
1fb5c2bf7348d0 Remi Buisson 2025-07-10 122 msleep(sleep);
1fb5c2bf7348d0 Remi Buisson 2025-07-10 123
1fb5c2bf7348d0 Remi Buisson 2025-07-10 124 return ret;
1fb5c2bf7348d0 Remi Buisson 2025-07-10 125 }
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 5/8] iio: imu: inv_icm45600: add I2C driver for inv_icm45600 driver
2025-07-14 20:21 ` Dan Carpenter
@ 2025-07-15 8:09 ` Andy Shevchenko
2025-07-15 9:17 ` Remi Buisson
1 sibling, 0 replies; 46+ messages in thread
From: Andy Shevchenko @ 2025-07-15 8:09 UTC (permalink / raw)
To: Dan Carpenter
Cc: oe-kbuild, Remi Buisson via B4 Relay, Jonathan Cameron,
David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, lkp, oe-kbuild-all,
linux-kernel, linux-iio, devicetree, Remi Buisson
On Mon, Jul 14, 2025 at 11:21:17PM +0300, Dan Carpenter wrote:
> Hi Remi,
>
> kernel test robot noticed the following build warnings:
It looks like a false positive, but the code is fragile.
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 99 unsigned int fifo_en = 0;
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 100 unsigned int sleep;
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 101 int ret;
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 102
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 103 scoped_guard(mutex, &st->lock) {
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 104 if (*scan_mask & BIT(INV_ICM45600_GYRO_SCAN_TEMP))
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 105 fifo_en |= INV_ICM45600_SENSOR_TEMP;
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 106
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 107 if (*scan_mask & (BIT(INV_ICM45600_GYRO_SCAN_X) |
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 108 BIT(INV_ICM45600_GYRO_SCAN_Y) |
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 109 BIT(INV_ICM45600_GYRO_SCAN_Z))) {
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 110 /* enable gyro sensor */
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 111 conf.mode = gyro_st->power_mode;
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 112 ret = inv_icm45600_set_gyro_conf(st, &conf, &sleep);
>
> sleep isn't necessarily set if nothing changed.
>
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 113 if (ret)
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 114 return ret;
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 115 fifo_en |= INV_ICM45600_SENSOR_GYRO;
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 116 }
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 117 /* update data FIFO write */
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 118 ret = inv_icm45600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 119 }
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 120 /* sleep required time */
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 @121 if (sleep)
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 122 msleep(sleep);
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 123
> 1fb5c2bf7348d0 Remi Buisson 2025-07-10 124 return ret;
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 46+ messages in thread
* RE: [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600
2025-07-14 5:38 ` Krzysztof Kozlowski
@ 2025-07-15 8:35 ` Remi Buisson
2025-07-15 12:16 ` Krzysztof Kozlowski
2025-08-19 10:19 ` Remi Buisson
[not found] ` <FR2PPF4571F02BC2C08BFD80F57AC4F45AA8C30A@FR2PPF4571F02BC.DEUP281.PROD.OUTLOOK.COM>
2 siblings, 1 reply; 46+ messages in thread
From: Remi Buisson @ 2025-07-15 8:35 UTC (permalink / raw)
To: Krzysztof Kozlowski, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org
>
>
>From: Krzysztof Kozlowski <krzk@kernel.org>
>Sent: Monday, July 14, 2025 7:39 AM
>To: Remi Buisson <Remi.Buisson@tdk.com>; Jonathan Cameron <jic23@kernel.org>; David Lechner <dlechner@baylibre.com>; Nuno Sá <nuno.sa@analog.com>; Andy Shevchenko <andy@kernel.org>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>
>Cc: linux-kernel@vger.kernel.org; linux-iio@vger.kernel.org; devicetree@vger.kernel.org
>Subject: Re: [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600
>
>On 10/07/2025 10:57, Remi Buisson via B4 Relay wrote:
>> +
>> + reg:
>> + maxItems: 1
>> +
>> + interrupts:
>> + minItems: 1
>> + maxItems: 2
>> +
>> + interrupt-names:
>> + minItems: 1
>> + maxItems: 2
>> + items:
>> + enum:
>> + - INT1
>> + - INT2
>
>This can be simpler
>
>minItems: 1
>items:
> - enum: [ int1, int2 ]
> - const: int 2
>
>and use lowercase anyway.
Hello Krzysztof, thanks for your review.
Ok, got it!
>
>> + description: Choose chip interrupt pin to be used as interrupt input.
>> +
>> + drive-open-drain:
>> + type: boolean
>> +
>> + vdd-supply:
>> + description: Regulator that provides power to the sensor
>> +
>> + vddio-supply:
>> + description: Regulator that provides power to the bus
>> +
>> + mount-matrix:
>> + description: an optional 3x3 mounting rotation matrix
>> +
>> +required:
>> + - compatible
>> + - reg
>> + - interrupts
>> + - interrupt-names
>
>Missing supplies
Are supplies always required ?
They are not present for invensense,icm426000 nor for st,lsm6dsx.
>
>> +
>> +allOf:
>> + - $ref: /schemas/spi/spi-peripheral-props.yaml#
>> +
>> +unevaluatedProperties: false
>> +
>> +examples:
>> + - |
>> + #include <dt-bindings/gpio/gpio.h>
>> + #include <dt-bindings/interrupt-controller/irq.h>
>> + i2c {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + icm45605@68 {
>
>It does not look like you tested the DTS against bindings. Please run
>`make dtbs_check W=1` (see
>Documentation/devicetree/bindings/writing-schema.rst or
>https://urldefense.com/v3/__https://www.linaro.org/blog/tips-and-tricks-for-validating-devicetree-sources-with-the-devicetree-schema/__;!!FtrhtPsWDhZ6tw!Dse6zIqgRmQ8nxzdmpC7Uu96WBVwNWbql_4WnfPwjuISbauv7GGsDO2zSDTq4TEzSeb0s6fcbmFR$[linaro[.]org]
>for instructions).
>Maybe you need to update your dtschema and yamllint. Don't rely on
>distro packages for dtschema and be sure you are using the latest
>released dtschema.
>
>(see how other bindings or DTS call this type of device)
>
I successfully ran "make dt_binding_check" with up-to-date testing branch.
I'll try dtbs_check as suggested, thanks!
>> + compatible = "invensense,icm45605";
>> + reg = <0x68>;
>> + interrupt-parent = <&gpio2>;
>> + interrupt-names = "INT1";
>> + interrupts = <7 IRQ_TYPE_EDGE_RISING>;
>> + vdd-supply = <&vdd>;
>> + vddio-supply = <&vddio>;
>> + mount-matrix = "0", "-1", "0",
>> + "1", "0", "0",
>> + "0", "0", "1";
>> + };
>Best regards,
>Krzysztof
>
^ permalink raw reply [flat|nested] 46+ messages in thread
* RE: [PATCH v2 7/8] iio: imu: inv_icm45600: add I3C driver for inv_icm45600 driver
2025-07-14 12:56 ` Sean Nyekjaer
@ 2025-07-15 8:48 ` Remi Buisson
0 siblings, 0 replies; 46+ messages in thread
From: Remi Buisson @ 2025-07-15 8:48 UTC (permalink / raw)
To: Sean Nyekjaer
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org
>
>
>From: Sean Nyekjaer mailto:sean@geanix.com
>Sent: Monday, July 14, 2025 2:56 PM
>To: Remi Buisson mailto:Remi.Buisson@tdk.com
>Cc: Jonathan Cameron mailto:jic23@kernel.org; David Lechner mailto:dlechner@baylibre.com; Nuno Sá mailto:nuno.sa@analog.com; Andy Shevchenko mailto:andy@kernel.org; Rob Herring mailto:robh@kernel.org; Krzysztof Kozlowski mailto:krzk+dt@kernel.org; Conor Dooley mailto:conor+dt@kernel.org; mailto:linux-kernel@vger.kernel.org; mailto:linux-iio@vger.kernel.org; mailto:devicetree@vger.kernel.org
>Subject: Re: [PATCH v2 7/8] iio: imu: inv_icm45600: add I3C driver for inv_icm45600 driver
>
> > Signed-off-by: Remi Buisson <remi. buisson@ tdk. com>
>On Thu, Jul 10, 2025 at 08:58:02AM +0100, Remi Buisson via B4 Relay wrote:
>> From: Remi Buisson mailto:remi.buisson@tdk.com
>>
>> Add I3C driver for InvenSense ICM-45600 devices.
>>
>> Signed-off-by: Remi Buisson mailto:remi.buisson@tdk.com
>
>Is this tested on real hardware?
Yes, on MCIMX93-QSB A3 (agreed that only few platforms support that interface as of today)
>
>/Sean
>
>
^ permalink raw reply [flat|nested] 46+ messages in thread
* RE: [PATCH v2 0/8] iio: imu: new inv_icm45600 driver
2025-07-14 12:06 ` [PATCH v2 0/8] iio: imu: new inv_icm45600 driver Sean Nyekjaer
@ 2025-07-15 9:03 ` Remi Buisson
2025-07-15 9:18 ` Sean Nyekjaer
0 siblings, 1 reply; 46+ messages in thread
From: Remi Buisson @ 2025-07-15 9:03 UTC (permalink / raw)
To: Sean Nyekjaer
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org
>>This Message Is From an External Sender
>>This message came from outside your organization.
>>
>>
>>On Thu, Jul 10, 2025 at 08:57:55AM +0100, Remi Buisson via B4 Relay wrote:
>>> This series add a new driver for managing InvenSense ICM-456xx 6-axis IMUs.
>>> This next generation of chips includes new generations of 3-axis gyroscope
>>> and 3-axis accelerometer, support of I3C in addition to I2C and SPI, and
>>> intelligent MotionTracking features like pedometer, tilt detection, and
>>> tap detection.
>>>
>>> This series is delivering a driver supporting gyroscope, accelerometer and
>>> temperature data, with polling and buffering using hwfifo and watermark,
>>> on I2C, SPI and I3C busses.
>>>
>>> Gyroscope and accelerometer sensors are completely independent and can have
>>> different ODRs. Since there is only a single FIFO a specific value is used to
>>> mark invalid data. For keeping the device standard we are de-multiplexing data
>
>
>From: Sean Nyekjaer <sean@geanix.com>
>Sent: Monday, July 14, 2025 2:07 PM
>To: Remi Buisson <Remi.Buisson@tdk.com>
>Cc: Jonathan Cameron <jic23@kernel.org>; David Lechner <dlechner@baylibre.com>; Nuno Sá <nuno.sa@analog.com>; Andy Shevchenko <andy@kernel.org>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; linux-kernel@vger.kernel.org; linux-iio@vger.kernel.org; devicetree@vger.kernel.org
>Subject: Re: [PATCH v2 0/8] iio: imu: new inv_icm45600 driver
>
>On Thu, Jul 10, 2025 at 08:57:55AM +0100, Remi Buisson via B4 Relay wrote:
>> This series add a new driver for managing InvenSense ICM-456xx 6-axis IMUs.
>> This next generation of chips includes new generations of 3-axis gyroscope
>> and 3-axis accelerometer, support of I3C in addition to I2C and SPI, and
>> intelligent MotionTracking features like pedometer, tilt detection, and
>> tap detection.
>>
>> This series is delivering a driver supporting gyroscope, accelerometer and
>> temperature data, with polling and buffering using hwfifo and watermark,
>> on I2C, SPI and I3C busses.
>>
>> Gyroscope and accelerometer sensors are completely independent and can have
>> different ODRs. Since there is only a single FIFO a specific value is used to
>> mark invalid data. For keeping the device standard we are de-multiplexing data
>> from the FIFO to 2 IIO devices with 2 buffers, 1 for the accelerometer and 1
>> for the gyroscope. This architecture also enables to easily turn each sensor
>> on/off without impacting the other. The device interrupt is used to read the
>> FIFO and launch parsing of accelerometer and gyroscope data. This driver
>> relies on the common Invensense timestamping mechanism to handle correctly
>> FIFO watermark and dynamic changes of settings.
>>
>> The structure of the driver is quite similar to the inv_icm42600 driver,
>> however there are significant reasons for adding a different driver for
>> inv_icm45600, such as:
>> - A completely different register map.
True, but icm45600 and icm42670 are more similar regarding indirect access
and FIFO management.
>At one point we asked TDK/Invense for a driver for icm42670. It also
>have a completely different register map... Grrr :S
>
>Anyhow, should we combine these drivers in inv_icm42600? Like
>st_lsm6dsx?
I don't believe so for icm42600 and icm45600, because of the exposed reasons,
they won't share much in the end.
However, new parts like icm56600 might share the same driver than icm45600.
(even with different memory maps :( )
>
>/Sean
>
>> - Different FIFO management, based on number of samples instead of bytes.
>> - Different indirect register access mechanism.
>>
^ permalink raw reply [flat|nested] 46+ messages in thread
* RE: [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver
2025-07-11 11:55 ` Andy Shevchenko
@ 2025-07-15 9:11 ` Remi Buisson
2025-07-15 10:42 ` Andy Shevchenko
0 siblings, 1 reply; 46+ messages in thread
From: Remi Buisson @ 2025-07-15 9:11 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org
>
>
>From: Andy Shevchenko <andriy.shevchenko@intel.com>
>Sent: Friday, July 11, 2025 1:56 PM
>To: Remi Buisson <Remi.Buisson@tdk.com>
>Cc: Jonathan Cameron <jic23@kernel.org>; David Lechner <dlechner@baylibre.com>; Nuno Sá <nuno.sa@analog.com>; Andy Shevchenko <andy@kernel.org>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; linux-kernel@vger.kernel.org; linux-iio@vger.kernel.org; devicetree@vger.kernel.org
>Subject: Re: [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver
>On Fri, Jul 11, 2025 at 11:32:48AM +0000, Remi Buisson wrote:
>> >From: Andy Shevchenko andriy.shevchenko@intel.com<mailto:andriy.shevchenko@intel.com>
>> >Sent: Thursday, July 10, 2025 11:30 AM
>> >To: Remi Buisson Remi.Buisson@tdk.com<mailto:Remi.Buisson@tdk.com>
>> >On Thu, Jul 10, 2025 at 08:57:57AM +0000, Remi Buisson via B4 Relay wrote:
>
>...
>
>> >> +struct inv_icm45600_sensor_conf {
>> >> + int mode;
>> >> + int fs;
>> >> + int odr;
>> >> + int filter;
>> >
>> >Any of them can hold negative value?
>>
>> Yes, when setting the configuration, a negative value means "keep previous configured value".
>> Like in the INV_ICM45600_SENSOR_CONF_INIT macro below.
>
>I see, but can't it be as simple as "big number" with the proper type instead?
>
>#define ..._KEEP_VALUE U8_MAX // or whatever type
Yes, I can surely fix it this way.
I agree it will be clearer.
>
>> >> +};
>
>...
>
>> >> +#define INV_ICM45600_SENSOR_CONF_INIT {-1, -1, -1, -1}
>> >
>> >Unused.
>> This is used in later patch of the serie.
>> I will move this definition to the patch using it.
>
>Yes, unused in this code. You should compile the series incrementally,
>so each patch will get a compilation test. This is called compile-time
>bisectability. Also run the system each time to confirm no regressions
>(this is called run-time bisectability).
Yes I did that for each patch, everything build successfully.
In that case, nothing is broken due to this early definition of the macro.
But I'll definitely move it to later patch for clarity.
>
>...
>
>> >> + udelay(INV_ICM45600_IREG_DELAY_US);
>> >
>> >Can fsleep() be used here?
>> I will use fsleep there.
>> Is it recommended to use fsleep everywhere else in place of other sleep APIs?
>
>Not everywhere, but in general yes, if in doubt, use it (but the author is
>the one who knows their the code and answer to this question).
Ok thanks!
>
>...
>
>> It's probably safer to keep the delay even in case of failure to make sure
>> the device is ready before next operation.
>
>I am not sure about it. Why? This has to be well justified as it's quite
>unusual pattern.
Ok I understand, the hardware needs that delay if the access was actually done on the bus (to not jeopardize next access).
If a regmap error means that no real access occured then the delay is avoidable.
>
>--
>With Best Regards,
>Andy Shevchenko
>
>
>
^ permalink raw reply [flat|nested] 46+ messages in thread
* RE: [PATCH v2 5/8] iio: imu: inv_icm45600: add I2C driver for inv_icm45600 driver
2025-07-14 20:21 ` Dan Carpenter
2025-07-15 8:09 ` Andy Shevchenko
@ 2025-07-15 9:17 ` Remi Buisson
1 sibling, 0 replies; 46+ messages in thread
From: Remi Buisson @ 2025-07-15 9:17 UTC (permalink / raw)
To: Dan Carpenter, oe-kbuild@lists.linux.dev,
Remi Buisson via B4 Relay, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: lkp@intel.com, oe-kbuild-all@lists.linux.dev,
linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org
>
>
>From: Dan Carpenter <dan.carpenter@linaro.org>
>Sent: Monday, July 14, 2025 10:21 PM
>To: oe-kbuild@lists.linux.dev; Remi Buisson via B4 Relay <devnull+remi.buisson.tdk.com@kernel.org>; Jonathan Cameron <jic23@kernel.org>; David Lechner <dlechner@baylibre.com>; Nuno Sá <nuno.sa@analog.com>; Andy Shevchenko <andy@kernel.org>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski <krzk@kernel.org>; Conor Dooley <conor+dt@kernel.org>
>Cc: lkp@intel.com; oe-kbuild-all@lists.linux.dev; linux-kernel@vger.kernel.org; linux-iio@vger.kernel.org; devicetree@vger.kernel.org; Remi Buisson <Remi.Buisson@tdk.com>
>Subject: Re: [PATCH v2 5/8] iio: imu: inv_icm45600: add I2C driver for inv_icm45600 driver
>
>Hi Remi,
>
>kernel test robot noticed the following build warnings:
>
>url: https://urldefense.com/v3/__https://github.com/intel-lab-lkp/linux/commits/Remi-Buisson-via-B4-Relay/dt-bindings-iio-imu-Add-inv_icm45600/20250710-170143__;!!FtrhtPsWDhZ6tw!GO8R3ZuhZ1VFpLzHNMxNgLQh_MnTqOf0QfhvgAVIMg15J6EFXJwPIClI9Xn3AWrYrSg0AGwenDxAngczRVPS8ZEa$[github[.]com]
>base: f8f559752d573a051a984adda8d2d1464f92f954
>patch link: https://urldefense.com/v3/__https://lore.kernel.org/r/20250710-add_newport_driver-v2-5-bf76d8142ef2*40tdk.com__;JQ!!FtrhtPsWDhZ6tw!GO8R3ZuhZ1VFpLzHNMxNgLQh_MnTqOf0QfhvgAVIMg15J6EFXJwPIClI9Xn3AWrYrSg0AGwenDxAngczRXeV1o-2$[lore[.]kernel[.]org]
>patch subject: [PATCH v2 5/8] iio: imu: inv_icm45600: add I2C driver for inv_icm45600 driver
>config: s390-randconfig-r073-20250712 (https://urldefense.com/v3/__https://download.01.org/0day-ci/archive/20250713/202507130115.7g0XWB2E-lkp@intel.com/config__;!!FtrhtPsWDhZ6tw!GO8R3ZuhZ1VFpLzHNMxNgLQh_MnTqOf0QfhvgAVIMg15J6EFXJwPIClI9Xn3AWrYrSg0AGwenDxAngczRdpu-U1X$[download[.]01[.]org])
>compiler: clang version 21.0.0git (https://urldefense.com/v3/__https://github.com/llvm/llvm-project__;!!FtrhtPsWDhZ6tw!GO8R3ZuhZ1VFpLzHNMxNgLQh_MnTqOf0QfhvgAVIMg15J6EFXJwPIClI9Xn3AWrYrSg0AGwenDxAngczRVrN-XjA$[github[.]com] 01c97b4953e87ae455bd4c41e3de3f0f0f29c61c)
>
>If you fix the issue in a separate patch/commit (i.e. not just a new version of
>the same patch/commit), kindly add following tags
>| Reported-by: kernel test robot <lkp@intel.com>
>| Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
>| Closes: https://urldefense.com/v3/__https://lore.kernel.org/r/202507130115.7g0XWB2E-lkp@intel.com/__;!!FtrhtPsWDhZ6tw!GO8R3ZuhZ1VFpLzHNMxNgLQh_MnTqOf0QfhvgAVIMg15J6EFXJwPIClI9Xn3AWrYrSg0AGwenDxAngczRW3ZbDKk$[lore[.]kernel[.]org]
>
>smatch warnings:
>drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c:121 inv_icm45600_gyro_update_scan_mode() error: uninitialized symbol 'sleep'.
>drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c:123 inv_icm45600_accel_update_scan_mode() error: uninitialized symbol 'sleep'.
>
>vim +/sleep +121 drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c
>
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 93 static int inv_icm45600_gyro_update_scan_mode(struct iio_dev *indio_dev,
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 94 const unsigned long *scan_mask)
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 95 {
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 96 struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 97 struct inv_icm45600_sensor_state *gyro_st = iio_priv(indio_dev);
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 98 struct inv_icm45600_sensor_conf conf = INV_ICM45600_SENSOR_CONF_INIT;
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 99 unsigned int fifo_en = 0;
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 100 unsigned int sleep;
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 101 int ret;
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 102
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 103 scoped_guard(mutex, &st->lock) {
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 104 if (*scan_mask & BIT(INV_ICM45600_GYRO_SCAN_TEMP))
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 105 fifo_en |= INV_ICM45600_SENSOR_TEMP;
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 106
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 107 if (*scan_mask & (BIT(INV_ICM45600_GYRO_SCAN_X) |
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 108 BIT(INV_ICM45600_GYRO_SCAN_Y) |
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 109 BIT(INV_ICM45600_GYRO_SCAN_Z))) {
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 110 /* enable gyro sensor */
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 111 conf.mode = gyro_st->power_mode;
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 112 ret = inv_icm45600_set_gyro_conf(st, &conf, &sleep);
>
>sleep isn't necessarily set if nothing changed.
Hello Dan,
Thanks for the tip,
I'll fix this following Andy's recommandation.
>
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 113 if (ret)
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 114 return ret;
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 115 fifo_en |= INV_ICM45600_SENSOR_GYRO;
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 116 }
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 117 /* update data FIFO write */
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 118 ret = inv_icm45600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 119 }
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 120 /* sleep required time */
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 @121 if (sleep)
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 122 msleep(sleep);
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 123
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 124 return ret;
>1fb5c2bf7348d0 Remi Buisson 2025-07-10 125 }
>
>--
>0-DAY CI Kernel Test Service
>https://urldefense.com/v3/__https://github.com/intel/lkp-tests/wiki__;!!FtrhtPsWDhZ6tw!GO8R3ZuhZ1VFpLzHNMxNgLQh_MnTqOf0QfhvgAVIMg15J6EFXJwPIClI9Xn3AWrYrSg0AGwenDxAngczRadkLqZM$[github[.]com]
>
>
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 0/8] iio: imu: new inv_icm45600 driver
2025-07-15 9:03 ` Remi Buisson
@ 2025-07-15 9:18 ` Sean Nyekjaer
0 siblings, 0 replies; 46+ messages in thread
From: Sean Nyekjaer @ 2025-07-15 9:18 UTC (permalink / raw)
To: Remi Buisson
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org
On Tue, Jul 15, 2025 at 09:03:57AM +0100, Remi Buisson wrote:
> >>This Message Is From an External Sender
> >>This message came from outside your organization.
> >>
> >>
[...]
> >>
> >> The structure of the driver is quite similar to the inv_icm42600 driver,
> >> however there are significant reasons for adding a different driver for
> >> inv_icm45600, such as:
> >> - A completely different register map.
> True, but icm45600 and icm42670 are more similar regarding indirect access
> and FIFO management.
> >At one point we asked TDK/Invense for a driver for icm42670. It also
> >have a completely different register map... Grrr :S
> >
> >Anyhow, should we combine these drivers in inv_icm42600? Like
> >st_lsm6dsx?
> I don't believe so for icm42600 and icm45600, because of the exposed reasons,
> they won't share much in the end.
> However, new parts like icm56600 might share the same driver than icm45600.
> (even with different memory maps :( )
Thanks for the clarification.
/Sean
^ permalink raw reply [flat|nested] 46+ messages in thread
* RE: [PATCH v2 6/8] iio: imu: inv_icm45600: add SPI driver for inv_icm45600 driver
2025-07-11 6:09 ` Andy Shevchenko
@ 2025-07-15 9:18 ` Remi Buisson
0 siblings, 0 replies; 46+ messages in thread
From: Remi Buisson @ 2025-07-15 9:18 UTC (permalink / raw)
To: Andy Shevchenko, kernel test robot
Cc: Remi Buisson via B4 Relay, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, oe-kbuild-all@lists.linux.dev,
linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org
>
>
>From: Andy Shevchenko mailto:andriy.shevchenko@intel.com
>Sent: Friday, July 11, 2025 8:10 AM
>To: kernel test robot mailto:lkp@intel.com
>Cc: Remi Buisson via B4 Relay mailto:devnull+remi.buisson.tdk.com@kernel.org; Jonathan Cameron mailto:jic23@kernel.org; David Lechner mailto:dlechner@baylibre.com; Nuno Sá mailto:nuno.sa@analog.com; Andy Shevchenko mailto:andy@kernel.org; Rob Herring mailto:robh@kernel.org; Krzysztof Kozlowski mailto:krzk@kernel.org; Conor Dooley mailto:conor+dt@kernel.org; mailto:oe-kbuild-all@lists.linux.dev; mailto:linux-kernel@vger.kernel.org; mailto:linux-iio@vger.kernel.org; mailto:devicetree@vger.kernel.org; Remi Buisson mailto:Remi.Buisson@tdk.com
>Subject: Re: [PATCH v2 6/8] iio: imu: inv_icm45600: add SPI driver for inv_icm45600 driver
>On Fri, Jul 11, 2025 at 12:55:25PM +0800, kernel test robot wrote:
>> Hi Remi,
>>
>
>> drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c:100:20: note: initialize the variable 'sleep' to silence this warning
>> 100 | unsigned int sleep;
>> | ^
>> | = 0
>
>For the record, this is usually bad advice by the compiler. You need to either
>check for errors, or do something else.
Thanks, I'll follow your recommendation.
>
>> drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c:100:20: note: initialize the variable 'sleep' to silence this warning
>> 100 | unsigned int sleep;
>> | ^
>> | = 0
>
>Ditto.
>
>--
>With Best Regards,
>Andy Shevchenko
>
>
>
^ permalink raw reply [flat|nested] 46+ messages in thread
* RE: [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver
2025-07-13 16:00 ` Jonathan Cameron
@ 2025-07-15 9:33 ` Remi Buisson
0 siblings, 0 replies; 46+ messages in thread
From: Remi Buisson @ 2025-07-15 9:33 UTC (permalink / raw)
To: Jonathan Cameron, Remi Buisson via B4 Relay
Cc: David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-kernel@vger.kernel.org,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
Sean Nyekjaer
>
>
>From: Jonathan Cameron <jic23@kernel.org>
>Sent: Sunday, July 13, 2025 6:00 PM
>To: Remi Buisson via B4 Relay <devnull+remi.buisson.tdk.com@kernel.org>
>Cc: Remi Buisson <Remi.Buisson@tdk.com>; David Lechner <dlechner@baylibre.com>; Nuno Sá <nuno.sa@analog.com>; Andy Shevchenko <andy@kernel.org>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; linux-kernel@vger.kernel.org; linux-iio@vger.kernel.org; devicetree@vger.kernel.org; Sean Nyekjaer <sean@geanix.com>
>Subject: Re: [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver
>On Thu, 10 Jul 2025 08:57:57 +0000
>Remi Buisson via B4 Relay <devnull+remi.buisson.tdk.com@kernel.org> wrote:
>
>> From: Remi Buisson <remi.buisson@tdk.com>
>>
>> Core component of a new driver for InvenSense ICM-45600 devices.
>> It includes registers definition, main probe/setup, and device
>> utility functions.
>>
>> ICM-456xx devices are latest generation of 6-axis IMU,
>> gyroscope+accelerometer and temperature sensor. This device
>> includes a 8K FIFO, supports I2C/I3C/SPI, and provides
>> intelligent motion features like pedometer, tilt detection,
>> and tap detection.
>>
>> Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
>
>Hi Remi,
>
>A few minor things inline. Given Sean has been looking at a closely
>related driver and you have some of the things he's been fixing up
>I +CC Sean on off chance he has time to take a look at this.
Hi Jonathan,
Thanks for your review and advises!
>
>> ---
>> drivers/iio/imu/Kconfig | 1 +
>> drivers/iio/imu/Makefile | 1 +
>> drivers/iio/imu/inv_icm45600/Kconfig | 5 +
>> drivers/iio/imu/inv_icm45600/Makefile | 4 +
>> drivers/iio/imu/inv_icm45600/inv_icm45600.h | 364 +++++++++++
>> drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 734 +++++++++++++++++++++++
>> 6 files changed, 1109 insertions(+)
>>
>> diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600.h b/drivers/iio/imu/inv_icm45600/inv_icm45600.h
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..d56de75ab7f2168f22e25b5816cb361bef457c0d
>> --- /dev/null
>> +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h
>> @@ -0,0 +1,364 @@
>
>> +struct inv_icm45600_sensor_conf {
>> + int mode;
>> + int fs;
>> + int odr;
>> + int filter;
>> +};
>> +#define INV_ICM45600_SENSOR_CONF_INIT {-1, -1, -1, -1}
>
>+#define INV_ICM45600_SENSOR_CONF_INIT { -1, -1, -1, -1, }
>
>Though I'm not sure the define is that useful vs pushing this down into the code.
It's used in ten places (but in later patch).
I'll add the last comma and move the macro to the patch actually using it.
>
>
>> diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..022eb9180b5a750e8fba4a1cd873041c27f59880
>> --- /dev/null
>> +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
>
>> +static int inv_icm45600_read(void *context, const void *reg_buf, size_t reg_size,
>> + void *val_buf, size_t val_size)
>> +{
>> + unsigned int reg = (unsigned int) be16_to_cpup(reg_buf);
>
>Why is the cast needed? It's a u16 so why not keep it like that?
The regmap API is expecting an unsigned int, but the cast is probably useless here.
>
>> + struct regmap *map = context;
>> +
>> + if (FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg) == 0)
>> + return regmap_bulk_read(map, FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg),
>> + val_buf, val_size);
>> +
>> + return inv_icm45600_ireg_read(map, reg, val_buf, val_size);
>> +}
>> +
>> +static int inv_icm45600_write(void *context, const void *data,
>> + size_t count)
>> +{
>> + u8 *d = (u8 *)data;
> const u8 *d = data;
>should be fine.
>
>> + unsigned int reg = (unsigned int) be16_to_cpup(data);
>
>u16
Same !
>
>> + struct regmap *map = context;
>> +
>> + if (FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg) == 0)
>> + return regmap_bulk_write(map, FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg),
>> + d + 2, count - 2);
>> +
>> + return inv_icm45600_ireg_write(map, reg, d + 2, count - 2);
>> +}
>
>> + * inv_icm45600_setup() - check and setup chip
>> + * @st: driver internal state
>> + * @chip_info: detected chip description
>> + * @reset: define whether a reset is required or not
>> + * @bus_setup: callback for setting up bus specific registers
>> + *
>> + * Returns 0 on success, a negative error code otherwise.
>> + */
>> +static int inv_icm45600_setup(struct inv_icm45600_state *st,
>> + const struct inv_icm45600_chip_info *chip_info,
>> + bool reset, inv_icm45600_bus_setup bus_setup)
>> +{
>> + const struct device *dev = regmap_get_device(st->map);
>> + unsigned int val;
>> + int ret;
>> +
>> + /* Set chip bus configuration if specified. */
>> + if (bus_setup) {
>> + ret = bus_setup(st);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + /* Check chip self-identification value. */
>> + ret = regmap_read(st->map, INV_ICM45600_REG_WHOAMI, &val);
>> + if (ret)
>> + return ret;
>> + if (val != chip_info->whoami) {
>> + dev_err(dev, "invalid whoami %#02x expected %#02x (%s)\n",
>
>This should be relaxed to a dev_info and carry on anyway. The reasoning
>for this is fallback compatibles in DT. If a new compatible chip
>with a different whoami value comes along we still want it to run
>with older kernels so we use fallback compatibles. Unfortunately the
>ID won't match.
Agreed, it can totally be relaxed.
>
>> + val, chip_info->whoami, chip_info->name);
>> + return -ENODEV;
>> + }
>> +
>> + st->chip_info = chip_info;
>> +
>> + if (reset) {
>> + /* Reset to make sure previous state are not there. */
>> + ret = regmap_write(st->map, INV_ICM45600_REG_MISC2,
>> + INV_ICM45600_MISC2_SOFT_RESET);
>> + if (ret)
>> + return ret;
>> + /* IMU reset time: 1ms. */
>> + fsleep(1000);
>> +
>> + if (bus_setup) {
>> + ret = bus_setup(st);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + ret = regmap_read(st->map, INV_ICM45600_REG_INT_STATUS, &val);
>> + if (ret)
>> + return ret;
>> + if (!(val & INV_ICM45600_INT_STATUS_RESET_DONE)) {
>> + dev_err(dev, "reset error, reset done bit not set\n");
>> + return -ENODEV;
>> + }
>> + }
>> +
>> + return inv_icm45600_set_conf(st, chip_info->conf);
>> +}
>
>> +
>> +static void inv_icm45600_disable_vddio_reg(void *_data)
>> +{
>> + struct inv_icm45600_state *st = _data;
>> + const struct device *dev = regmap_get_device(st->map);
>> + int ret;
>> +
>> + ret = regulator_disable(st->vddio_supply);
>> + if (ret)
>> + dev_err(dev, "failed to disable vddio error %d\n", ret);
>
>As per Sean's set - is this useful given such failures are already noisy.
Ok !
>
>
>> +}
>> +
>> +int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm45600_chip_info *chip_info,
>> + bool reset, inv_icm45600_bus_setup bus_setup)
>> +{
>> + struct device *dev = regmap_get_device(regmap);
>> + struct fwnode_handle *fwnode;
>> + struct inv_icm45600_state *st;
>> + struct regmap *regmap_custom;
>> + int irq, irq_type;
>> + bool open_drain;
>> + int ret;
>> +
>> + /* Get INT1 only supported interrupt. */
>> + fwnode = dev_fwnode(dev);
>> + if (!fwnode)
>> + return dev_err_probe(dev, -ENODEV, "Missing FW node\n");
>> +
>> + irq = fwnode_irq_get_byname(fwnode, "INT1");
>> + if (irq < 0) {
>> + if (irq != -EPROBE_DEFER)
>> + dev_err_probe(dev, irq, "Missing INT1 interrupt\n");
>> + return irq;
>> + }
>> +
>> + irq_type = irq_get_trigger_type(irq);
>> +
>> + open_drain = device_property_read_bool(dev, "drive-open-drain");
>> +
>> + regmap_custom = devm_regmap_init(dev, &inv_icm45600_regmap_bus,
>> + regmap, &inv_icm45600_regmap_config);
>> + if (IS_ERR(regmap_custom))
>> + return dev_err_probe(dev, PTR_ERR(regmap_custom), "Failed to register regmap\n");
>> +
>> + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
>> + if (!st)
>> + return dev_err_probe(dev, -ENOMEM, "Cannot allocate memory\n");
>> +
>> + dev_set_drvdata(dev, st);
>> + ret = devm_mutex_init(dev, &st->lock);
>> + if (ret)
>> + return ret;
>> +
>> + st->map = regmap_custom;
>> +
>> + ret = iio_read_mount_matrix(dev, &st->orientation);
>> + if (ret)
>> + return dev_err_probe(dev, ret, "Failed to retrieve mounting matrix\n");
>> +
>> + st->vddio_supply = devm_regulator_get(dev, "vddio");
>> + if (IS_ERR(st->vddio_supply))
>> + return PTR_ERR(st->vddio_supply);
>> +
>> + ret = devm_regulator_get_enable(dev, "vdd");
>> + if (ret)
>> + return ret;
>> + /* IMU start-up time. */
>> + msleep(100);
>> +
>> + ret = inv_icm45600_enable_regulator_vddio(st);
>> + if (ret)
>> + return ret;
>> +
>> + ret = devm_add_action_or_reset(dev, inv_icm45600_disable_vddio_reg, st);
>> + if (ret)
>> + return ret;
>> +
>> + /* Setup chip registers. */
>> + ret = inv_icm45600_setup(st, chip_info, reset, bus_setup);
>> + if (ret)
>> + return ret;
>> +
>> + ret = inv_icm45600_timestamp_setup(st);
>> + if (ret)
>> + return ret;
>> +
>> + /* Setup runtime power management. */
>> + ret = devm_pm_runtime_set_active_enabled(dev);
>> + if (ret)
>> + return ret;
>> +
>> + pm_runtime_get_noresume(dev);
>> + /* Suspend after 2 seconds. */
>> + pm_runtime_set_autosuspend_delay(dev, 2000);
>> + pm_runtime_use_autosuspend(dev);
>> + pm_runtime_put(dev);
>
>See below. Sean also reworks this. + I suggested he look at
>devm_pm_runtime_enable but the set active version seems even better.
>
>I'm not setting the gets and puts I'd expect for working runtime PM though.
>
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_NS_GPL(inv_icm45600_core_probe, "IIO_ICM45600");
>
>> +
>> +/*
>> + * System resume gets the system back on and restores the sensors state.
>> + * Manually put runtime power management in system active state.
>> + */
>> +static int inv_icm45600_resume(struct device *dev)
>> +{
>> + struct inv_icm45600_state *st = dev_get_drvdata(dev);
>> + int ret;
>> +
>> + guard(mutex)(&st->lock);
>> +
>> + ret = inv_icm45600_enable_regulator_vddio(st);
>> + if (ret)
>> + return ret;
>> +
>> + pm_runtime_disable(dev);
>> + pm_runtime_set_active(dev);
>> + pm_runtime_enable(dev);
>Take a look at
>https://urldefense.com/v3/__https://lore.kernel.org/all/20250709-icm42pmreg-v1-0-3d0e793c99b2@geanix.com/__;!!FtrhtPsWDhZ6tw!BSHVk18DJp39qlR3TRcUmneIt6kRtkdNe5pr_Wm__NSC6DzqqxfGOOZCqPUHnuG6pnx30oM5bVNjig$[lore[.]kernel[.]org]
>
>It would be good if you an review Sean's series as well as applying anything
>you agree with here. For example he got rid of the 3 lines above.
Thanks for pointing this patch, I'll import good practices into this one.
>
>> +
>> + /* Restore sensors state. */
>> + return inv_icm45600_set_pwr_mgmt0(st, st->suspended.gyro,
>> + st->suspended.accel, NULL);
>> +
>> +}
>> +
>> +/* Runtime suspend will turn off sensors that are enabled by iio devices. */
>> +static int inv_icm45600_runtime_suspend(struct device *dev)
>> +{
>> + struct inv_icm45600_state *st = dev_get_drvdata(dev);
>> + int ret;
>> +
>> + guard(mutex)(&st->lock);
>> +
>> + /* disable all sensors */
>> + ret = inv_icm45600_set_pwr_mgmt0(st, INV_ICM45600_SENSOR_MODE_OFF,
>> + INV_ICM45600_SENSOR_MODE_OFF, NULL);
>> + if (ret)
>> + return ret;
>> +
>> + regulator_disable(st->vddio_supply);
>> +
>> + return 0;
>> +}
>> +
>> +/* Sensors are enabled by iio devices, no need to turn them back on here. */
>> +static int inv_icm45600_runtime_resume(struct device *dev)
>> +{
>> + struct inv_icm45600_state *st = dev_get_drvdata(dev);
>> +
>> + guard(mutex)(&st->lock);
>> +
>> + return inv_icm45600_enable_regulator_vddio(st);
>> +}
>> +
>> +EXPORT_NS_GPL_DEV_PM_OPS(inv_icm45600_pm_ops, IIO_ICM45600) = {
>> + SET_SYSTEM_SLEEP_PM_OPS(inv_icm45600_suspend, inv_icm45600_resume)
>> + SET_RUNTIME_PM_OPS(inv_icm45600_runtime_suspend,
>> + inv_icm45600_runtime_resume, NULL)
>> +};
>
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver
2025-07-15 9:11 ` Remi Buisson
@ 2025-07-15 10:42 ` Andy Shevchenko
2025-07-15 15:26 ` Remi Buisson
0 siblings, 1 reply; 46+ messages in thread
From: Andy Shevchenko @ 2025-07-15 10:42 UTC (permalink / raw)
To: Remi Buisson
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org
On Tue, Jul 15, 2025 at 09:11:47AM +0000, Remi Buisson wrote:
> >From: Andy Shevchenko <andriy.shevchenko@intel.com>
> >Sent: Friday, July 11, 2025 1:56 PM
> >On Fri, Jul 11, 2025 at 11:32:48AM +0000, Remi Buisson wrote:
> >> >From: Andy Shevchenko andriy.shevchenko@intel.com<mailto:andriy.shevchenko@intel.com>
> >> >Sent: Thursday, July 10, 2025 11:30 AM
> >> >On Thu, Jul 10, 2025 at 08:57:57AM +0000, Remi Buisson via B4 Relay wrote:
...
> >> >> +#define INV_ICM45600_SENSOR_CONF_INIT {-1, -1, -1, -1}
> >> >
> >> >Unused.
> >> This is used in later patch of the serie.
> >> I will move this definition to the patch using it.
> >
> >Yes, unused in this code. You should compile the series incrementally,
> >so each patch will get a compilation test. This is called compile-time
> >bisectability. Also run the system each time to confirm no regressions
> >(this is called run-time bisectability).
> Yes I did that for each patch, everything build successfully.
> In that case, nothing is broken due to this early definition of the macro.
> But I'll definitely move it to later patch for clarity.
Yeah, the problem is that the (unused) definitions are not warned even when
`make W=1`. And I guess I understand why. We have tons of unused definitions
in the drivers that usually substitute (on whatever reasons) the actual
documentation. It's hard to catch for the definitions like this without reading
the code.
...
> >> It's probably safer to keep the delay even in case of failure to make sure
> >> the device is ready before next operation.
> >
> >I am not sure about it. Why? This has to be well justified as it's quite
> >unusual pattern.
> Ok I understand, the hardware needs that delay if the access was actually
> done on the bus (to not jeopardize next access). If a regmap error means
> that no real access occured then the delay is avoidable.
Perhaps you need to have this delay embedded in the IO accessors? Also do
read _and_ write need this or only one of them?
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600
2025-07-15 8:35 ` Remi Buisson
@ 2025-07-15 12:16 ` Krzysztof Kozlowski
2025-07-16 14:31 ` Remi Buisson
0 siblings, 1 reply; 46+ messages in thread
From: Krzysztof Kozlowski @ 2025-07-15 12:16 UTC (permalink / raw)
To: Remi Buisson, Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org
On 15/07/2025 10:35, Remi Buisson wrote:
>>> + drive-open-drain:
>>> + type: boolean
>>> +
>>> + vdd-supply:
>>> + description: Regulator that provides power to the sensor
>>> +
>>> + vddio-supply:
>>> + description: Regulator that provides power to the bus
>>> +
>>> + mount-matrix:
>>> + description: an optional 3x3 mounting rotation matrix
>>> +
>>> +required:
>>> + - compatible
>>> + - reg
>>> + - interrupts
>>> + - interrupt-names
>>
>> Missing supplies
> Are supplies always required ?
> They are not present for invensense,icm426000 nor for st,lsm6dsx.
Depends on the hardware... What does datasheet say? Which ones are optional?
Other bindings are not good examples in this topic, because people tend
to skip the supplies, reviewers and maintainers tend not to bother with
nitpicking this.
>>
>>> +
>>> +allOf:
>>> + - $ref: /schemas/spi/spi-peripheral-props.yaml#
>>> +
>>> +unevaluatedProperties: false
>>> +
>>> +examples:
>>> + - |
>>> + #include <dt-bindings/gpio/gpio.h>
>>> + #include <dt-bindings/interrupt-controller/irq.h>
>>> + i2c {
>>> + #address-cells = <1>;
>>> + #size-cells = <0>;
>>> +
>>> + icm45605@68 {
>>
>> It does not look like you tested the DTS against bindings. Please run
>> `make dtbs_check W=1` (see
>> Documentation/devicetree/bindings/writing-schema.rst or
>> https://urldefense.com/v3/__https://www.linaro.org/blog/tips-and-tricks-for-validating-devicetree-sources-with-the-devicetree-schema/__;!!FtrhtPsWDhZ6tw!Dse6zIqgRmQ8nxzdmpC7Uu96WBVwNWbql_4WnfPwjuISbauv7GGsDO2zSDTq4TEzSeb0s6fcbmFR$[linaro[.]org]
>> for instructions).
>> Maybe you need to update your dtschema and yamllint. Don't rely on
>> distro packages for dtschema and be sure you are using the latest
>> released dtschema.
>>
>> (see how other bindings or DTS call this type of device)
>>
> I successfully ran "make dt_binding_check" with up-to-date testing branch.
> I'll try dtbs_check as suggested, thanks!
I am sorry. I think I pasted wrong template for a common issue.
I wanted to say: you need generic name, imu etc. That's the template
response should be there:
Node names should be generic. See also an explanation and list of
examples (not exhaustive) in DT specification:
https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#generic-names-recommendation
(and I use templates to streamline my review. Even though they might
feel a bit robotic, without personal touch or patronizing, that's not
their intention, I just optimize my process)
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 46+ messages in thread
* RE: [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver
2025-07-15 10:42 ` Andy Shevchenko
@ 2025-07-15 15:26 ` Remi Buisson
2025-07-16 9:25 ` Andy Shevchenko
0 siblings, 1 reply; 46+ messages in thread
From: Remi Buisson @ 2025-07-15 15:26 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org
>
>
>From: Andy Shevchenko <andriy.shevchenko@intel.com>
>Sent: Tuesday, July 15, 2025 12:43 PM
>To: Remi Buisson <Remi.Buisson@tdk.com>
>Cc: Jonathan Cameron <jic23@kernel.org>; David Lechner <dlechner@baylibre.com>; Nuno Sá <nuno.sa@analog.com>; Andy Shevchenko <andy@kernel.org>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; linux-kernel@vger.kernel.org; linux-iio@vger.kernel.org; devicetree@vger.kernel.org
>Subject: Re: [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver
>
>On Tue, Jul 15, 2025 at 09:11:47AM +0000, Remi Buisson wrote:
>> >From: Andy Shevchenko <andriy.shevchenko@intel.com>
>> >Sent: Friday, July 11, 2025 1:56 PM
>> >On Fri, Jul 11, 2025 at 11:32:48AM +0000, Remi Buisson wrote:
>> >> >From: Andy Shevchenko andriy.shevchenko@intel.com<mailto:andriy.shevchenko@intel.com>
>> >> >Sent: Thursday, July 10, 2025 11:30 AM
>> >> >On Thu, Jul 10, 2025 at 08:57:57AM +0000, Remi Buisson via B4 Relay wrote:
>
>...
>
>> >> >> +#define INV_ICM45600_SENSOR_CONF_INIT {-1, -1, -1, -1}
>> >> >
>> >> >Unused.
>> >> This is used in later patch of the serie.
>> >> I will move this definition to the patch using it.
>> >
>> >Yes, unused in this code. You should compile the series incrementally,
>> >so each patch will get a compilation test. This is called compile-time
>> >bisectability. Also run the system each time to confirm no regressions
>> >(this is called run-time bisectability).
>
>> Yes I did that for each patch, everything build successfully.
>> In that case, nothing is broken due to this early definition of the macro.
>> But I'll definitely move it to later patch for clarity.
>
>Yeah, the problem is that the (unused) definitions are not warned even when
>`make W=1`. And I guess I understand why. We have tons of unused definitions
>in the drivers that usually substitute (on whatever reasons) the actual
>documentation. It's hard to catch for the definitions like this without reading
>the code.
>
>...
>
>> >> It's probably safer to keep the delay even in case of failure to make sure
>> >> the device is ready before next operation.
>> >
>> >I am not sure about it. Why? This has to be well justified as it's quite
>> >unusual pattern.
>
>> Ok I understand, the hardware needs that delay if the access was actually
>> done on the bus (to not jeopardize next access). If a regmap error means
>> that no real access occured then the delay is avoidable.
>
>Perhaps you need to have this delay embedded in the IO accessors? Also do
>read _and_ write need this or only one of them?
It's required for both indirect read and write BUT not when writing the first data
which need to be done in a single burst.
Could you please be more specific on how to add delays to IO accessors?
>
>--
>With Best Regards,
>Andy Shevchenko
>
>
>
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver
2025-07-15 15:26 ` Remi Buisson
@ 2025-07-16 9:25 ` Andy Shevchenko
0 siblings, 0 replies; 46+ messages in thread
From: Andy Shevchenko @ 2025-07-16 9:25 UTC (permalink / raw)
To: Remi Buisson
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org
On Tue, Jul 15, 2025 at 03:26:48PM +0000, Remi Buisson wrote:
> >From: Andy Shevchenko <andriy.shevchenko@intel.com>
> >Sent: Tuesday, July 15, 2025 12:43 PM
> >On Tue, Jul 15, 2025 at 09:11:47AM +0000, Remi Buisson wrote:
> >> >From: Andy Shevchenko <andriy.shevchenko@intel.com>
> >> >Sent: Friday, July 11, 2025 1:56 PM
> >> >On Fri, Jul 11, 2025 at 11:32:48AM +0000, Remi Buisson wrote:
> >> >> >From: Andy Shevchenko andriy.shevchenko@intel.com<mailto:andriy.shevchenko@intel.com>
> >> >> >Sent: Thursday, July 10, 2025 11:30 AM
> >> >> >On Thu, Jul 10, 2025 at 08:57:57AM +0000, Remi Buisson via B4 Relay wrote:
...
> >> >> It's probably safer to keep the delay even in case of failure to make sure
> >> >> the device is ready before next operation.
> >> >
> >> >I am not sure about it. Why? This has to be well justified as it's quite
> >> >unusual pattern.
> >
> >> Ok I understand, the hardware needs that delay if the access was actually
> >> done on the bus (to not jeopardize next access). If a regmap error means
> >> that no real access occured then the delay is avoidable.
> >
> >Perhaps you need to have this delay embedded in the IO accessors? Also do
> >read _and_ write need this or only one of them?
> It's required for both indirect read and write BUT not when writing the first data
> which need to be done in a single burst.
> Could you please be more specific on how to add delays to IO accessors?
I don't remember if regmap core supports already such a delay, but always
working case is to redefine your own regmap_read()/regmap_write() callbacks.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 46+ messages in thread
* RE: [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600
2025-07-15 12:16 ` Krzysztof Kozlowski
@ 2025-07-16 14:31 ` Remi Buisson
0 siblings, 0 replies; 46+ messages in thread
From: Remi Buisson @ 2025-07-16 14:31 UTC (permalink / raw)
To: Krzysztof Kozlowski, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org
>
>
>From: Krzysztof Kozlowski <krzk@kernel.org>
>Sent: Tuesday, July 15, 2025 2:16 PM
>To: Remi Buisson <Remi.Buisson@tdk.com>; Jonathan Cameron <jic23@kernel.org>; David Lechner <dlechner@baylibre.com>; Nuno Sá <nuno.sa@analog.com>; Andy Shevchenko <andy@kernel.org>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>
>Cc: linux-kernel@vger.kernel.org; linux-iio@vger.kernel.org; devicetree@vger.kernel.org
>Subject: Re: [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600
>
>On 15/07/2025 10:35, Remi Buisson wrote:
>>>> + drive-open-drain:
>>>> + type: boolean
>>>> +
>>>> + vdd-supply:
>>>> + description: Regulator that provides power to the sensor
>>>> +
>>>> + vddio-supply:
>>>> + description: Regulator that provides power to the bus
>>>> +
>>>> + mount-matrix:
>>>> + description: an optional 3x3 mounting rotation matrix
>>>> +
>>>> +required:
>>>> + - compatible
>>>> + - reg
>>>> + - interrupts
>>>> + - interrupt-names
>>>
>>> Missing supplies
>> Are supplies always required ?
>> They are not present for invensense,icm426000 nor for st,lsm6dsx.
>
>Depends on the hardware... What does datasheet say? Which ones are optional?
>
>Other bindings are not good examples in this topic, because people tend
>to skip the supplies, reviewers and maintainers tend not to bother with
>nitpicking this.
Understood, VDD and VDDIO are actually mandatory.
I'll add them.
>
>
>>>
>>>> +
>>>> +allOf:
>>>> + - $ref: /schemas/spi/spi-peripheral-props.yaml#
>>>> +
>>>> +unevaluatedProperties: false
>>>> +
>>>> +examples:
>>>> + - |
>>>> + #include <dt-bindings/gpio/gpio.h>
>>>> + #include <dt-bindings/interrupt-controller/irq.h>
>>>> + i2c {
>>>> + #address-cells = <1>;
>>>> + #size-cells = <0>;
>>>> +
>>>> + icm45605@68 {
>>>
>>> It does not look like you tested the DTS against bindings. Please run
>>> `make dtbs_check W=1` (see
>>> Documentation/devicetree/bindings/writing-schema.rst or
>>> https://urldefense.com/v3/__https://www.linaro.org/blog/tips-and-tricks-for-validating-devicetree-sources-with-the-devicetree-schema/__;!!FtrhtPsWDhZ6tw!Dse6zIqgRmQ8nxzdmpC7Uu96WBVwNWbql_4WnfPwjuISbauv7GGsDO2zSDTq4TEzSeb0s6fcbmFR$%5Blinaro%5B.%5Dorg%5D
>>>for instructions).
>>> Maybe you need to update your dtschema and yamllint. Don't rely on
>>> distro packages for dtschema and be sure you are using the latest
>>> released dtschema.
>>>
>>> (see how other bindings or DTS call this type of device)
>>>
>> I successfully ran "make dt_binding_check" with up-to-date testing branch.
>> I'll try dtbs_check as suggested, thanks!
>
>I am sorry. I think I pasted wrong template for a common issue.
>
>I wanted to say: you need generic name, imu etc. That's the template
>response should be there:
>
>Node names should be generic. See also an explanation and list of
>examples (not exhaustive) in DT specification:
>https://urldefense.com/v3/__https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html*generic-names-recommendation__;Iw!!FtrhtPsWDhZ6tw!DNZeP0a7fEex8t6jRU9hHDfJL3hXbiodPsB40bjewu_3nuUgfx6KyW65oDRKJDf9i7WfsEz6LwNa$[devicetree-specification[.]readthedocs[.]io]
>
>(and I use templates to streamline my review. Even though they might
>feel a bit robotic, without personal touch or patronizing, that's not
>their intention, I just optimize my process)
Thanks for the clarification, I'll use "imu" then.
Templates are totally ok :)
>
>
>Best regards,
>Krzysztof
>
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 3/8] iio: imu: inv_icm45600: add buffer support in iio devices
2025-07-10 8:57 ` [PATCH v2 3/8] iio: imu: inv_icm45600: add buffer support in iio devices Remi Buisson via B4 Relay
@ 2025-07-17 14:33 ` Jonathan Cameron
2025-08-11 14:13 ` Remi Buisson
0 siblings, 1 reply; 46+ messages in thread
From: Jonathan Cameron @ 2025-07-17 14:33 UTC (permalink / raw)
To: Remi Buisson via B4 Relay
Cc: remi.buisson, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, linux-kernel,
linux-iio, devicetree
On Thu, 10 Jul 2025 08:57:58 +0000
Remi Buisson via B4 Relay <devnull+remi.buisson.tdk.com@kernel.org> wrote:
> From: Remi Buisson <remi.buisson@tdk.com>
>
> Add FIFO control functions.
> Support hwfifo watermark by multiplexing gyro and accel settings.
> Support hwfifo flush.
>
> Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
Hi Remi,
Sorry for delay - hectic week.
Jonathan
> ---
> drivers/iio/imu/inv_icm45600/Makefile | 1 +
> drivers/iio/imu/inv_icm45600/inv_icm45600.h | 4 +
> drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c | 514 +++++++++++++++++++++
> drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h | 99 ++++
> drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 137 +++++-
We used to do the buffer / core split a lot but it often ends up more trouble
that it is worth and we no longer make buffer support a build time option (which was
what motivated the separate files) Consider how much simplification you'd get by squashing them into
one file.
> 5 files changed, 754 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/iio/imu/inv_icm45600/Makefile b/drivers/iio/imu/inv_icm45600/Makefile
> index 4f442b61896e91647c7947a044949792bae06a30..19c521ffba17b0d108a8ecb45ecdea35dff6fd18 100644
> --- a/drivers/iio/imu/inv_icm45600/Makefile
> +++ b/drivers/iio/imu/inv_icm45600/Makefile
> @@ -2,3 +2,4 @@
>
> obj-$(CONFIG_INV_ICM45600) += inv-icm45600.o
> inv-icm45600-y += inv_icm45600_core.o
> +inv-icm45600-y += inv_icm45600_buffer.o
> \ No newline at end of file
Fix that.
> diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600.h b/drivers/iio/imu/inv_icm45600/inv_icm45600.h
> index d56de75ab7f2168f22e25b5816cb361bef457c0d..0b97c54f74b30aef3842e34da098c2443347de00 100644
> --- a/drivers/iio/imu/inv_icm45600/inv_icm45600.h
> +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h
> @@ -10,6 +10,8 @@
> #include <linux/iio/iio.h>
> #include <linux/types.h>
>
> +#include "inv_icm45600_buffer.h"
> +
> #define INV_ICM45600_REG_BANK_MASK GENMASK(15, 8)
> #define INV_ICM45600_REG_ADDR_MASK GENMASK(7, 0)
>
> @@ -128,6 +130,7 @@ extern const struct inv_icm45600_chip_info inv_icm45689_chip_info;
> * @indio_gyro: gyroscope IIO device.
> * @indio_accel: accelerometer IIO device.
> * @timestamp: interrupt timestamps.
> + * @fifo: FIFO management structure.
> * @buffer: data transfer buffer aligned for DMA.
> */
> struct inv_icm45600_state {
> @@ -144,6 +147,7 @@ struct inv_icm45600_state {
> s64 gyro;
> s64 accel;
> } timestamp;
> + struct inv_icm45600_fifo fifo;
This has a forced alignment at the end which is going to ensure the next bit
is pushed out a long way. It it is possible to squash the definitions here
so we can see what is going on that would be good.
> union {
> u8 buff[2];
> __le16 u16;
> diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..a07affa0e3c64f84330071c92018438882cdf686
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c
> +
> +struct inv_icm45600_fifo_1sensor_packet {
> + u8 header;
> + struct inv_icm45600_fifo_sensor_data data;
> + s8 temp;
> +} __packed;
> +#define INV_ICM45600_FIFO_1SENSOR_PACKET_SIZE 8
> +
> +struct inv_icm45600_fifo_2sensors_packet {
> + u8 header;
> + struct inv_icm45600_fifo_sensor_data accel;
> + struct inv_icm45600_fifo_sensor_data gyro;
> + s8 temp;
> + __le16 timestamp;
> +} __packed;
> +#define INV_ICM45600_FIFO_2SENSORS_PACKET_SIZE 16
This definition seems unnecessary given you have
sizeof(struct inv_icm45600_fifo_2sensors_packet)
(or better yet sizeof(instance)) to use for it.
> +void inv_icm45600_buffer_update_fifo_period(struct inv_icm45600_state *st)
> +{
> + u32 period_gyro, period_accel, period;
> +
> + if (st->fifo.en & INV_ICM45600_SENSOR_GYRO)
> + period_gyro = inv_icm45600_odr_to_period(st->conf.gyro.odr);
> + else
> + period_gyro = U32_MAX;
> +
> + if (st->fifo.en & INV_ICM45600_SENSOR_ACCEL)
> + period_accel = inv_icm45600_odr_to_period(st->conf.accel.odr);
> + else
> + period_accel = U32_MAX;
> +
> + if (period_gyro <= period_accel)
> + period = period_gyro;
> + else
> + period = period_accel;
st->fifo.period = min(period_gyro, period_accel);
saves a few lines.
> +
> + st->fifo.period = period;
> +}
> +static unsigned int inv_icm45600_wm_truncate(unsigned int watermark, size_t packet_size,
> + unsigned int fifo_period)
> +{
> + size_t watermark_max, grace_samples;
> +
> + /* Keep 20ms for processing FIFO. */
> + grace_samples = (20U * 1000000U) / fifo_period;
> + if (grace_samples < 1)
> + grace_samples = 1;
> +
> + watermark_max = INV_ICM45600_FIFO_SIZE_MAX / packet_size;
> + watermark_max -= grace_samples;
> +
> + if (watermark > watermark_max)
> + watermark = watermark_max;
return min(watermark, watermark_max);
> +
> + return watermark;
> +}
> +
> +/**
> + * inv_icm45600_buffer_update_watermark - update watermark FIFO threshold
> + * @st: driver internal state
> + *
> + * Returns 0 on success, a negative error code otherwise.
> + *
> + * FIFO watermark threshold is computed based on the required watermark values
> + * set for gyro and accel sensors. Since watermark is all about acceptable data
> + * latency, use the smallest setting between the 2. It means choosing the
> + * smallest latency but this is not as simple as choosing the smallest watermark
> + * value. Latency depends on watermark and ODR. It requires several steps:
> + * 1) compute gyro and accel latencies and choose the smallest value.
> + * 2) adapt the chosen latency so that it is a multiple of both gyro and accel
> + * ones. Otherwise it is possible that you don't meet a requirement. (for
> + * example with gyro @100Hz wm 4 and accel @100Hz with wm 6, choosing the
> + * value of 4 will not meet accel latency requirement because 6 is not a
> + * multiple of 4. You need to use the value 2.)
> + * 3) Since all periods are multiple of each others, watermark is computed by
> + * dividing this computed latency by the smallest period, which corresponds
> + * to the FIFO frequency.
Nice explanation and clear code. Thanks!
> + */
> +int inv_icm45600_buffer_update_watermark(struct inv_icm45600_state *st)
> +{
> + const size_t packet_size = INV_ICM45600_FIFO_2SENSORS_PACKET_SIZE;
> + unsigned int wm_gyro, wm_accel, watermark;
> + u32 period_gyro, period_accel, period;
> + u32 latency_gyro, latency_accel, latency;
> +
> + /* Compute sensors latency, depending on sensor watermark and odr. */
> + wm_gyro = inv_icm45600_wm_truncate(st->fifo.watermark.gyro, packet_size,
> + st->fifo.period);
> + wm_accel = inv_icm45600_wm_truncate(st->fifo.watermark.accel, packet_size,
> + st->fifo.period);
> + /* Use us for odr to avoid overflow using 32 bits values. */
> + period_gyro = inv_icm45600_odr_to_period(st->conf.gyro.odr) / 1000UL;
> + period_accel = inv_icm45600_odr_to_period(st->conf.accel.odr) / 1000UL;
> + latency_gyro = period_gyro * wm_gyro;
> + latency_accel = period_accel * wm_accel;
> +
> + /* 0 value for watermark means that the sensor is turned off. */
> + if (wm_gyro == 0 && wm_accel == 0)
> + return 0;
> +
> + if (latency_gyro == 0) {
> + watermark = wm_accel;
> + st->fifo.watermark.eff_accel = wm_accel;
> + } else if (latency_accel == 0) {
> + watermark = wm_gyro;
> + st->fifo.watermark.eff_gyro = wm_gyro;
> + } else {
> + /* Compute the smallest latency that is a multiple of both. */
> + if (latency_gyro <= latency_accel)
> + latency = latency_gyro - (latency_accel % latency_gyro);
> + else
> + latency = latency_accel - (latency_gyro % latency_accel);
> + /* Use the shortest period. */
> + if (period_gyro <= period_accel)
> + period = period_gyro;
> + else
> + period = period_accel;
> + /* All this works because periods are multiple of each others. */
> + watermark = latency / period;
> + if (watermark < 1)
> + watermark = 1;
> + /* Update effective watermark. */
> + st->fifo.watermark.eff_gyro = latency / period_gyro;
> + if (st->fifo.watermark.eff_gyro < 1)
> + st->fifo.watermark.eff_gyro = 1;
> + st->fifo.watermark.eff_accel = latency / period_accel;
> + if (st->fifo.watermark.eff_accel < 1)
> + st->fifo.watermark.eff_accel = 1;
> + }
> +
> +
> + st->buffer.u16 = cpu_to_le16(watermark);
> + return regmap_bulk_write(st->map, INV_ICM45600_REG_FIFO_WATERMARK,
> + &st->buffer.u16, sizeof(st->buffer.u16));
> +}
> +
> +static int inv_icm45600_buffer_predisable(struct iio_dev *indio_dev)
> +{
> + struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
> + unsigned int val;
> + int ret;
> +
> + guard(mutex)(&st->lock);
> +
> + /* Exit if there are several sensors using the FIFO. */
> + if (st->fifo.on > 1) {
> + /* decrease FIFO on counter */
> + st->fifo.on--;
> + return 0;
> + }
> +
> + /* Disable writing sensor data to FIFO. */
> + ret = regmap_clear_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3,
> + INV_ICM45600_FIFO_CONFIG3_IF_EN);
> + if (ret)
> + return ret;
> +
> + /* Set FIFO in bypass mode. */
> + val = FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK,
> + INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS);
One more space. Check for similar.
> + ret = regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0,
> + INV_ICM45600_FIFO_CONFIG0_MODE_MASK, val);
> + if (ret)
> + return ret;
> +
> + /* Disable FIFO threshold and full interrupt. */
> + ret = regmap_clear_bits(st->map, INV_ICM45600_REG_INT1_CONFIG0,
> + INV_ICM45600_INT1_CONFIG0_FIFO_THS_EN |
> + INV_ICM45600_INT1_CONFIG0_FIFO_FULL_EN);
> + if (ret)
> + return ret;
> +
> + /* Flush all FIFO data. */
> + ret = regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG2,
> + INV_ICM45600_REG_FIFO_CONFIG2_FIFO_FLUSH);
> + if (ret)
> + return ret;
> +
> + /* Decrease FIFO on counter. */
> + st->fifo.on--;
> + return 0;
> +}
> +
> +static int inv_icm45600_buffer_postdisable(struct iio_dev *indio_dev)
> +{
> + struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
> + struct device *dev = regmap_get_device(st->map);
> + unsigned int sensor;
> + unsigned int *watermark;
> + unsigned int sleep;
> + int ret;
> +
> + if (indio_dev == st->indio_gyro) {
> + sensor = INV_ICM45600_SENSOR_GYRO;
> + watermark = &st->fifo.watermark.gyro;
> + } else if (indio_dev == st->indio_accel) {
> + sensor = INV_ICM45600_SENSOR_ACCEL;
> + watermark = &st->fifo.watermark.accel;
> + } else {
> + return -EINVAL;
> + }
> +
> + scoped_guard(mutex, &st->lock)
> + ret = _inv_icm45600_buffer_postdisable(st, sensor, watermark, &sleep);
> +
> + /* Sleep required time. */
> + if (sleep)
Is there a case where sleep is 0? If not just do it unconditionally perhaps.
> + msleep(sleep);
> +
> + pm_runtime_put_autosuspend(dev);
> +
> + return ret;
> +}
> +
> +const struct iio_buffer_setup_ops inv_icm45600_buffer_ops = {
> + .preenable = inv_icm45600_buffer_preenable,
> + .postenable = inv_icm45600_buffer_postenable,
> + .predisable = inv_icm45600_buffer_predisable,
> + .postdisable = inv_icm45600_buffer_postdisable,
> +};
> +
> +int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st,
> + unsigned int max)
What is max here? Seems to be passed 0 in the only caller.
> +{
> + const ssize_t packet_size = INV_ICM45600_FIFO_2SENSORS_PACKET_SIZE;
> + __le16 *raw_fifo_count;
> + size_t fifo_nb, i;
> + ssize_t size;
> + const struct inv_icm45600_fifo_sensor_data *accel, *gyro;
> + const __le16 *timestamp;
> + const s8 *temp;
> + unsigned int odr;
> + int ret;
> +
> + /* Reset all samples counters. */
> + st->fifo.count = 0;
> + st->fifo.nb.gyro = 0;
> + st->fifo.nb.accel = 0;
> + st->fifo.nb.total = 0;
> +
> + /* Read FIFO count value. */
> + raw_fifo_count = &st->buffer.u16;
> + ret = regmap_bulk_read(st->map, INV_ICM45600_REG_FIFO_COUNT,
> + raw_fifo_count, sizeof(*raw_fifo_count));
For IIO drivers at least we still operated under some guidance the regmap maintainer
gave years ago to never assume regmap (for busses that otherwise require DMA safe
buffers) will always bounce the data. So bulk reads with SPI buffers need
DMA safe buffers. Easiest is usually an __aligned(IIO_DMA_MINALIGN) buffer
(or set of buffers) at the end of st.
In practice last time I checked regmap doesn't bother with the zero copy
optimization that would need this safety so you won't actually hit this
issue.
> + if (ret)
> + return ret;
> + fifo_nb = le16_to_cpup(raw_fifo_count);
> +
> + /* Check and limit number of samples if requested. */
> + if (fifo_nb == 0)
> + return 0;
> + if (max > 0 && fifo_nb > max)
> + fifo_nb = max;
> +
> + /* Try to read all FIFO data in internal buffer. */
> + st->fifo.count = fifo_nb * packet_size;
> + ret = regmap_noinc_read(st->map, INV_ICM45600_REG_FIFO_DATA,
> + st->fifo.data, st->fifo.count);
> + if (ret == -ENOTSUPP || ret == -EFBIG) {
> + /* Read full fifo is not supported, read samples one by one. */
Interesting. Maybe we could push the fallback to regmap or further given it's
emulating the big read version.
> + ret = 0;
> + for (i = 0; i < st->fifo.count && ret == 0; i += packet_size)
> + ret = regmap_noinc_read(st->map, INV_ICM45600_REG_FIFO_DATA,
> + &st->fifo.data[i], packet_size);
> + }
> + if (ret)
> + return ret;
> +
> + for (i = 0; i < st->fifo.count; i += size) {
> + size = inv_icm45600_fifo_decode_packet(&st->fifo.data[i],
> + &accel, &gyro, &temp, ×tamp, &odr);
> + if (size <= 0)
> + break;
> + if (gyro != NULL && inv_icm45600_fifo_is_data_valid(gyro))
> + st->fifo.nb.gyro++;
> + if (accel != NULL && inv_icm45600_fifo_is_data_valid(accel))
> + st->fifo.nb.accel++;
> + st->fifo.nb.total++;
> + }
> +
> + return 0;
> +}
> diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..09595a41cf637a3ba9bc44e4df53a9d0aa11f485
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h
> @@ -0,0 +1,99 @@
> +/**
> + * struct inv_icm45600_fifo - FIFO state variables
> + * @on: reference counter for FIFO on.
> + * @en: bits field of INV_ICM45600_SENSOR_* for FIFO EN bits.
> + * @period: FIFO internal period.
> + * @watermark: watermark configuration values for accel and gyro.
> + * @count: number of bytes in the FIFO data buffer.
> + * @nb: gyro, accel and total samples in the FIFO data buffer.
> + * @data: FIFO data buffer aligned for DMA (8kB)
> + */
> +struct inv_icm45600_fifo {
> + unsigned int on;
> + unsigned int en;
> + u32 period;
> + struct {
> + unsigned int gyro;
> + unsigned int accel;
> + unsigned int eff_gyro;
> + unsigned int eff_accel;
> + } watermark;
> + size_t count;
> + struct {
> + size_t gyro;
> + size_t accel;
> + size_t total;
> + } nb;
> + u8 data[8192] __aligned(IIO_DMA_MINALIGN);
Doing this inside an embedded structure isn't great for readability.
See comment above on putting them next to each other. Also worth thinking
about structure padding that is going on here - maybe point pahole at the
result.
> +};
> +
> +/* FIFO data packet */
> +struct inv_icm45600_fifo_sensor_data {
> + __le16 x;
> + __le16 y;
> + __le16 z;
> +} __packed;
> +#define INV_ICM45600_FIFO_DATA_INVALID -32768
> +
> +static inline s16 inv_icm45600_fifo_get_sensor_data(__le16 d)
> +{
> + return le16_to_cpu(d);
> +}
> +
> +static inline bool
> +inv_icm45600_fifo_is_data_valid(const struct inv_icm45600_fifo_sensor_data *s)
> +{
> + s16 x, y, z;
> +
> + x = inv_icm45600_fifo_get_sensor_data(s->x);
> + y = inv_icm45600_fifo_get_sensor_data(s->y);
> + z = inv_icm45600_fifo_get_sensor_data(s->z);
> +
> + if (x == INV_ICM45600_FIFO_DATA_INVALID &&
> + y == INV_ICM45600_FIFO_DATA_INVALID &&
> + z == INV_ICM45600_FIFO_DATA_INVALID)
> + return false;
> +
> + return true;
> +}
> +
> +ssize_t inv_icm45600_fifo_decode_packet(const void *packet,
> + const struct inv_icm45600_fifo_sensor_data **accel,
> + const struct inv_icm45600_fifo_sensor_data **gyro,
> + const s8 **temp,
> + const __le16 **timestamp, unsigned int *odr);
> +
> +extern const struct iio_buffer_setup_ops inv_icm45600_buffer_ops;
> +
> +int inv_icm45600_buffer_init(struct inv_icm45600_state *st);
> +
> +void inv_icm45600_buffer_update_fifo_period(struct inv_icm45600_state *st);
> +
> +int inv_icm45600_buffer_set_fifo_en(struct inv_icm45600_state *st,
> + unsigned int fifo_en);
> +
> +int inv_icm45600_buffer_update_watermark(struct inv_icm45600_state *st);
> +
> +int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st,
> + unsigned int max);
> +
> +int inv_icm45600_buffer_hwfifo_flush(struct inv_icm45600_state *st,
> + unsigned int count);
The comment above about considering a single file was because there is a lot in this header
rather implying maybe there isn't a good separation of code.
> +
> +#endif
> diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
> index 022eb9180b5a750e8fba4a1cd873041c27f59880..37487e9174bf53f2fc6583f37ac7542ea31b0643 100644
> --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
> +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
> static int inv_icm45600_timestamp_setup(struct inv_icm45600_state *st)
> {
> /* Enable timestamps. */
> @@ -627,6 +717,14 @@ int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm45600_chi
> if (ret)
> return ret;
>
> + ret = inv_icm45600_buffer_init(st);
> + if (ret)
> + return ret;
> +
> + ret = inv_icm45600_irq_init(st, irq, irq_type, open_drain);
> + if (ret)
> + return ret;
> +
> /* Setup runtime power management. */
> ret = devm_pm_runtime_set_active_enabled(dev);
> if (ret)
> @@ -658,6 +756,22 @@ static int inv_icm45600_suspend(struct device *dev)
> if (pm_runtime_suspended(dev))
> return 0;
>
> + /* Disable FIFO data streaming. */
> + if (st->fifo.on) {
> + unsigned int val;
> +
> + ret = regmap_clear_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3,
> + INV_ICM45600_FIFO_CONFIG3_IF_EN);
> + if (ret)
> + return ret;
> + val = FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK,
> + INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS);
Align second line after (
> + ret = regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0,
> + INV_ICM45600_FIFO_CONFIG0_MODE_MASK, val);
> + if (ret)
> + return ret;
> + }
> +
> ret = inv_icm45600_set_pwr_mgmt0(st, INV_ICM45600_SENSOR_MODE_OFF,
> INV_ICM45600_SENSOR_MODE_OFF, NULL);
> if (ret)
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 4/8] iio: imu: inv_icm45600: add IMU IIO devices
2025-07-10 8:57 ` [PATCH v2 4/8] iio: imu: inv_icm45600: add IMU IIO devices Remi Buisson via B4 Relay
@ 2025-07-17 14:47 ` Jonathan Cameron
2025-08-11 14:56 ` Remi Buisson
0 siblings, 1 reply; 46+ messages in thread
From: Jonathan Cameron @ 2025-07-17 14:47 UTC (permalink / raw)
To: Remi Buisson via B4 Relay
Cc: remi.buisson, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, linux-kernel,
linux-iio, devicetree
On Thu, 10 Jul 2025 08:57:59 +0000
Remi Buisson via B4 Relay <devnull+remi.buisson.tdk.com@kernel.org> wrote:
> From: Remi Buisson <remi.buisson@tdk.com>
>
> Add IIO devices for accelerometer and gyroscope sensors
> with data polling interface and FIFO parsing.
> Attributes: raw, scale, sampling_frequency, calibbias.
> Temperature is available as a processed channel.
>
> Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
Hi Remi,
A few comments inline.
Jonathan
> diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..8ba2e8e54c738a3289726d6f23a46f5307ebdffe
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c
> @@ -0,0 +1,809 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2025 Invensense, Inc.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/common/inv_sensors_timestamp.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/kfifo_buf.h>
> +#include <linux/math64.h>
> +#include <linux/mutex.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/types.h>
> +
> +#include "inv_icm45600_buffer.h"
> +#include "inv_icm45600.h"
> +
> +enum inv_icm45600_gyro_scan {
> + INV_ICM45600_GYRO_SCAN_X,
> + INV_ICM45600_GYRO_SCAN_Y,
> + INV_ICM45600_GYRO_SCAN_Z,
> + INV_ICM45600_GYRO_SCAN_TEMP,
> + INV_ICM45600_GYRO_SCAN_TIMESTAMP,
> +};
> +
> +static const struct iio_chan_spec_ext_info inv_icm45600_gyro_ext_infos[] = {
> + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm45600_get_mount_matrix),
> + { },
No trailing comma on a terminating entry.
> +};
> +
> +#define INV_ICM45600_GYRO_CHAN(_modifier, _index, _ext_info) \
> + { \
> + .type = IIO_ANGL_VEL, \
> + .modified = 1, \
> + .channel2 = _modifier, \
> + .info_mask_separate = \
> + BIT(IIO_CHAN_INFO_RAW) | \
> + BIT(IIO_CHAN_INFO_CALIBBIAS), \
> + .info_mask_shared_by_type = \
> + BIT(IIO_CHAN_INFO_SCALE), \
> + .info_mask_shared_by_type_available = \
> + BIT(IIO_CHAN_INFO_SCALE) | \
> + BIT(IIO_CHAN_INFO_CALIBBIAS), \
> + .info_mask_shared_by_all = \
> + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> + .info_mask_shared_by_all_available = \
> + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> + .scan_index = _index, \
> + .scan_type = { \
> + .sign = 's', \
> + .realbits = 16, \
> + .storagebits = 16, \
> + .endianness = IIO_LE, \
> + }, \
> + .ext_info = _ext_info, \
> + }
> +static const unsigned long inv_icm45600_gyro_scan_masks[] = {
> + /* 3-axis gyro + temperature */
> + BIT(INV_ICM45600_GYRO_SCAN_X) |
> + BIT(INV_ICM45600_GYRO_SCAN_Y) |
> + BIT(INV_ICM45600_GYRO_SCAN_Z) |
> + BIT(INV_ICM45600_GYRO_SCAN_TEMP),
> + 0,
No comma as that's a terminating entry so nothing should come after it.
> +};
> +
> +/* enable gyroscope sensor and FIFO write */
> +static int inv_icm45600_gyro_update_scan_mode(struct iio_dev *indio_dev,
> + const unsigned long *scan_mask)
> +{
> + struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
> + struct inv_icm45600_sensor_state *gyro_st = iio_priv(indio_dev);
> + struct inv_icm45600_sensor_conf conf = INV_ICM45600_SENSOR_CONF_INIT;
> + unsigned int fifo_en = 0;
> + unsigned int sleep;
> + int ret;
> +
> + scoped_guard(mutex, &st->lock) {
> + if (*scan_mask & BIT(INV_ICM45600_GYRO_SCAN_TEMP))
> + fifo_en |= INV_ICM45600_SENSOR_TEMP;
> +
> + if (*scan_mask & (BIT(INV_ICM45600_GYRO_SCAN_X) |
> + BIT(INV_ICM45600_GYRO_SCAN_Y) |
> + BIT(INV_ICM45600_GYRO_SCAN_Z))) {
> + /* enable gyro sensor */
> + conf.mode = gyro_st->power_mode;
> + ret = inv_icm45600_set_gyro_conf(st, &conf, &sleep);
> + if (ret)
> + return ret;
> + fifo_en |= INV_ICM45600_SENSOR_GYRO;
> + }
> + /* update data FIFO write */
> + ret = inv_icm45600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
> + }
> + /* sleep required time */
Can we just always sleep?
> + if (sleep)
> + msleep(sleep);
> +
> + return ret;
> +}
> +
> +/* IIO format int + nano */
> +const int inv_icm45600_gyro_scale[][2] = {
> + /* +/- 2000dps => 0.001065264 rad/s */
> + [INV_ICM45600_GYRO_FS_2000DPS] = {0, 1065264},
> + /* +/- 1000dps => 0.000532632 rad/s */
> + [INV_ICM45600_GYRO_FS_1000DPS] = {0, 532632},
> + /* +/- 500dps => 0.000266316 rad/s */
> + [INV_ICM45600_GYRO_FS_500DPS] = {0, 266316},
> + /* +/- 250dps => 0.000133158 rad/s */
> + [INV_ICM45600_GYRO_FS_250DPS] = {0, 133158},
> + /* +/- 125dps => 0.000066579 rad/s */
> + [INV_ICM45600_GYRO_FS_125DPS] = {0, 66579},
> + /* +/- 62.5dps => 0.000033290 rad/s */
> + [INV_ICM45600_GYRO_FS_62_5DPS] = {0, 33290},
> + /* +/- 31.25dps => 0.000016645 rad/s */
> + [INV_ICM45600_GYRO_FS_31_25DPS] = {0, 16645},
> + /* +/- 15.625dps => 0.000008322 rad/s */
> + [INV_ICM45600_GYRO_FS_15_625DPS] = {0, 8322},
> +};
> +
> +/* IIO format int + nano */
> +const int inv_icm45686_gyro_scale[][2] = {
> + /* +/- 4000dps => 0.002130529 rad/s */
> + [INV_ICM45686_GYRO_FS_4000DPS] = {0, 2130529},
{ 0, 2130529 },
preferred for IIO code. Arbitrary choice I made a while back
in the interests of homogeneous looking code.
> + /* +/- 2000dps => 0.001065264 rad/s */
> + [INV_ICM45686_GYRO_FS_2000DPS] = {0, 1065264},
> + /* +/- 1000dps => 0.000532632 rad/s */
> + [INV_ICM45686_GYRO_FS_1000DPS] = {0, 532632},
> + /* +/- 500dps => 0.000266316 rad/s */
> + [INV_ICM45686_GYRO_FS_500DPS] = {0, 266316},
> + /* +/- 250dps => 0.000133158 rad/s */
> + [INV_ICM45686_GYRO_FS_250DPS] = {0, 133158},
> + /* +/- 125dps => 0.000066579 rad/s */
> + [INV_ICM45686_GYRO_FS_125DPS] = {0, 66579},
> + /* +/- 62.5dps => 0.000033290 rad/s */
> + [INV_ICM45686_GYRO_FS_62_5DPS] = {0, 33290},
> + /* +/- 31.25dps => 0.000016645 rad/s */
> + [INV_ICM45686_GYRO_FS_31_25DPS] = {0, 16645},
> + /* +/- 15.625dps => 0.000008322 rad/s */
> + [INV_ICM45686_GYRO_FS_15_625DPS] = {0, 8322},
> +};
> +
> +/* IIO format int + micro */
> +static const int inv_icm45600_gyro_odr[] = {
> + /* 1.5625Hz */
> + 1, 562500,
1, 562500, /* 1.5625Hz */
3, 125000, /* 3.125Hz */
etc if you think the comments are needed.
> + /* 3.125Hz */
> + 3, 125000,
> + /* 6.25Hz */
> + 6, 250000,
> + /* 12.5Hz */
> + 12, 500000,
> + /* 25Hz */
> + 25, 0,
> + /* 50Hz */
> + 50, 0,
> + /* 100Hz */
> + 100, 0,
> + /* 200Hz */
> + 200, 0,
> + /* 400Hz */
> + 400, 0,
> + /* 800Hz */
> + 800, 0,
> + /* 1.6kHz */
> + 1600, 0,
> + /* 3.2kHz */
> + 3200, 0,
> + /* 6.4kHz */
> + 6400, 0,
> +};
> +/*
> + * Calibration bias values, IIO range format int + nano.
> + * Value is limited to +/-62.5dps coded on 14 bits signed. Step is 7.5mdps.
> + */
> +static int inv_icm45600_gyro_calibbias[] = {
> + -1, 90830336, /* min: -1.090830336 rad/s */
> + 0, 133158, /* step: 0.000133158 rad/s */
> + 1, 90697178, /* max: 1.090697178 rad/s */
> +};
> +static int inv_icm45600_gyro_write_offset(struct inv_icm45600_state *st,
> + struct iio_chan_spec const *chan,
> + int val, int val2)
As this is calibbias, it's not really required to be in any particular
units. Often these are analog front end tweaks where that doesn't matter.
So you could just have this take the integer value you compute instead
of doing all this maths. Also update the available ranges if you do that.
I don't mind if you want something specific though (like radians) though
as that's also compliant with the ABI.
> +{
> + struct device *dev = regmap_get_device(st->map);
> + s64 val64, min, max;
> + unsigned int reg;
> + s16 offset;
> + int ret;
> +
> + if (chan->type != IIO_ANGL_VEL)
> + return -EINVAL;
> +
> + switch (chan->channel2) {
> + case IIO_MOD_X:
> + reg = INV_ICM45600_IPREG_SYS1_REG_42;
> + break;
> + case IIO_MOD_Y:
> + reg = INV_ICM45600_IPREG_SYS1_REG_56;
> + break;
> + case IIO_MOD_Z:
> + reg = INV_ICM45600_IPREG_SYS1_REG_70;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + /* inv_icm45600_gyro_calibbias: min - step - max in nano */
> + min = (s64)inv_icm45600_gyro_calibbias[0] * 1000000000LL -
> + (s64)inv_icm45600_gyro_calibbias[1];
> + max = (s64)inv_icm45600_gyro_calibbias[4] * 1000000000LL +
> + (s64)inv_icm45600_gyro_calibbias[5];
> + val64 = (s64)val * 1000000000LL;
> + if (val > 0)
> + val64 += (s64)val2;
> + else
> + val64 -= (s64)val2;
> + if (val64 < min || val64 > max)
> + return -EINVAL;
> +
> + /*
> + * convert rad/s to dps then to raw value
> + * rad to dps: 180 / Pi
> + * dps to raw 14 bits signed, max 62.5: 8192 / 62.5
> + * val in nano (1000000000)
> + * val * 180 * 8192 / (Pi * 1000000000 * 62.5)
> + */
> + val64 = val64 * 180LL * 8192;
> + /* for rounding, add + or - divisor (314159265 * 625) divided by 2 */
> + if (val64 >= 0)
> + val64 += 314159265LL * 625LL / 2LL;
> + else
> + val64 -= 314159265LL * 625LL / 2LL;
> + offset = div64_s64(val64, 314159265LL * 625LL);
> +
> + /* clamp value limited to 14 bits signed */
> + if (offset < -8192)
> + offset = -8192;
> + else if (offset > 8191)
> + offset = 8191;
> +
> + st->buffer.u16 = cpu_to_le16(offset & INV_ICM45600_GYRO_OFFUSER_MASK);
> +
> + ret = pm_runtime_resume_and_get(dev);
> + if (ret)
> + return ret;
> +
> + scoped_guard(mutex, &st->lock)
> + ret = regmap_bulk_write(st->map, reg, &st->buffer.u16, sizeof(st->buffer.u16));
> +
> + pm_runtime_put_autosuspend(dev);
> + return ret;
> +}
> +static int inv_icm45600_gyro_hwfifo_flush(struct iio_dev *indio_dev,
> + unsigned int count)
> +{
> + struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
> + int ret;
> +
> + if (count == 0)
> + return 0;
> +
> + guard(mutex)(&st->lock);
> +
> + ret = inv_icm45600_buffer_hwfifo_flush(st, count);
> + if (!ret)
> + ret = st->fifo.nb.gyro;
if (ret)
return ret;
return s->fifo.nb.gyro;
preferred slightly because it keeps the error path out of line.
When reading a lot of code conventions like that an help.
> +
> + return ret;
> +}
> +
> +struct iio_dev *inv_icm45600_gyro_init(struct inv_icm45600_state *st)
> +{
> + struct device *dev = regmap_get_device(st->map);
> + const char *name;
Pick an ordering. If nothing else makes sense, reverse xmas tree
isn't too ugly.
> + struct inv_icm45600_sensor_state *gyro_st;
> + struct inv_sensors_timestamp_chip ts_chip;
> + struct iio_dev *indio_dev;
> + int ret;
> +
> + name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->chip_info->name);
> + if (!name)
> + return ERR_PTR(-ENOMEM);
> +
> + indio_dev = devm_iio_device_alloc(dev, sizeof(*gyro_st));
> + if (!indio_dev)
> + return ERR_PTR(-ENOMEM);
> + gyro_st = iio_priv(indio_dev);
> +
> + gyro_st->scales = st->chip_info->gyro_scales;
> + gyro_st->scales_len = st->chip_info->gyro_scales_len * 2;
> +
> + /* low-noise by default at init */
> + gyro_st->power_mode = INV_ICM45600_SENSOR_MODE_LOW_NOISE;
> +
> + /*
> + * clock period is 32kHz (31250ns)
> + * jitter is +/- 2% (20 per mille)
> + */
> + ts_chip.clock_period = 31250;
> + ts_chip.jitter = 20;
> + ts_chip.init_period = inv_icm45600_odr_to_period(st->conf.gyro.odr);
> + inv_sensors_timestamp_init(&gyro_st->ts, &ts_chip);
> +
> + iio_device_set_drvdata(indio_dev, st);
> + indio_dev->name = name;
> + indio_dev->info = &inv_icm45600_gyro_info;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + indio_dev->channels = inv_icm45600_gyro_channels;
> + indio_dev->num_channels = ARRAY_SIZE(inv_icm45600_gyro_channels);
> + indio_dev->available_scan_masks = inv_icm45600_gyro_scan_masks;
> + indio_dev->setup_ops = &inv_icm45600_buffer_ops;
> +
> + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
> + &inv_icm45600_buffer_ops);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + ret = devm_iio_device_register(dev, indio_dev);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + return indio_dev;
> +}
> +
> +int inv_icm45600_gyro_parse_fifo(struct iio_dev *indio_dev)
> +{
> + struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
> + struct inv_icm45600_sensor_state *gyro_st = iio_priv(indio_dev);
> + struct inv_sensors_timestamp *ts = &gyro_st->ts;
> + ssize_t i, size;
> + unsigned int no;
> + const struct inv_icm45600_fifo_sensor_data *accel, *gyro;
> + const __le16 *timestamp;
> + const s8 *temp;
> + unsigned int odr;
> + s64 ts_val;
> + struct inv_icm45600_gyro_buffer buffer;
I'd push scope of many of these into the for loop.
> +
> + /* parse all fifo packets */
> + for (i = 0, no = 0; i < st->fifo.count; i += size, ++no) {
> + size = inv_icm45600_fifo_decode_packet(&st->fifo.data[i],
> + &accel, &gyro, &temp, ×tamp, &odr);
> + /* quit if error or FIFO is empty */
> + if (size <= 0)
> + return size;
> +
> + /* skip packet if no gyro data or data is invalid */
> + if (gyro == NULL || !inv_icm45600_fifo_is_data_valid(gyro))
> + continue;
> +
> + /* update odr */
> + if (odr & INV_ICM45600_SENSOR_GYRO)
> + inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
> + st->fifo.nb.total, no);
> +
> + /* buffer is copied to userspace, zeroing it to avoid any data leak */
> + memset(&buffer, 0, sizeof(buffer));
> + memcpy(&buffer.gyro, gyro, sizeof(buffer.gyro));
> + /* convert 8 bits FIFO temperature in high resolution format */
> + buffer.temp = temp ? (*temp * 64) : 0;
> + ts_val = inv_sensors_timestamp_pop(ts);
> + iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val);
> + }
> +
> + return 0;
> +}
>
^ permalink raw reply [flat|nested] 46+ messages in thread
* RE: [PATCH v2 3/8] iio: imu: inv_icm45600: add buffer support in iio devices
2025-07-17 14:33 ` Jonathan Cameron
@ 2025-08-11 14:13 ` Remi Buisson
2025-08-16 11:17 ` Jonathan Cameron
0 siblings, 1 reply; 46+ messages in thread
From: Remi Buisson @ 2025-08-11 14:13 UTC (permalink / raw)
To: Jonathan Cameron, Remi Buisson via B4 Relay
Cc: David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-kernel@vger.kernel.org,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org
>
>
>From: Jonathan Cameron <jic23@kernel.org>
>Sent: Thursday, July 17, 2025 4:34 PM
>To: Remi Buisson via B4 Relay <devnull+remi.buisson.tdk.com@kernel.org>
>Cc: Remi Buisson <Remi.Buisson@tdk.com>; David Lechner <dlechner@baylibre.com>; Nuno Sá <nuno.sa@analog.com>; Andy Shevchenko <andy@kernel.org>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; linux-kernel@vger.kernel.org; linux-iio@vger.kernel.org; devicetree@vger.kernel.org
>Subject: Re: [PATCH v2 3/8] iio: imu: inv_icm45600: add buffer support in iio devices
>On Thu, 10 Jul 2025 08:57:58 +0000
>Remi Buisson via B4 Relay <devnull+remi.buisson.tdk.com@kernel.org> wrote:
>
>> From: Remi Buisson <remi.buisson@tdk.com>
>>
>> Add FIFO control functions.
>> Support hwfifo watermark by multiplexing gyro and accel settings.
>> Support hwfifo flush.
>>
>> Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
>Hi Remi,
>
>Sorry for delay - hectic week.
>
>Jonathan
No problem, thanks for the review ! (and sorry for my late reply)
>
>> ---
>> drivers/iio/imu/inv_icm45600/Makefile | 1 +
>> drivers/iio/imu/inv_icm45600/inv_icm45600.h | 4 +
>> drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c | 514 +++++++++++++++++++++
>> drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h | 99 ++++
>> drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 137 +++++-
>We used to do the buffer / core split a lot but it often ends up more trouble
>that it is worth and we no longer make buffer support a build time option (which was
>what motivated the separate files) Consider how much simplification you'd get by squashing them into
>one file.
I understand the point.
However merging files will allow to remove 5 lines at most,
while the length of the core file will increase a lot.
I'm not sure of the benefit in the end, but
please let me know if you really want me to proceed with the merge.
>
>> 5 files changed, 754 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/iio/imu/inv_icm45600/Makefile b/drivers/iio/imu/inv_icm45600/Makefile
>> index 4f442b61896e91647c7947a044949792bae06a30..19c521ffba17b0d108a8ecb45ecdea35dff6fd18 100644
>> --- a/drivers/iio/imu/inv_icm45600/Makefile
>> +++ b/drivers/iio/imu/inv_icm45600/Makefile
>> @@ -2,3 +2,4 @@
>>
>> obj-$(CONFIG_INV_ICM45600) += inv-icm45600.o
>> inv-icm45600-y += inv_icm45600_core.o
>> +inv-icm45600-y += inv_icm45600_buffer.o
>> \ No newline at end of file
>Fix that.
I'll fix.
>
>> diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600.h b/drivers/iio/imu/inv_icm45600/inv_icm45600.h
>> index d56de75ab7f2168f22e25b5816cb361bef457c0d..0b97c54f74b30aef3842e34da098c2443347de00 100644
>> --- a/drivers/iio/imu/inv_icm45600/inv_icm45600.h
>> +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h
>> @@ -10,6 +10,8 @@
>> #include <linux/iio/iio.h>
>> #include <linux/types.h>
>>
>> +#include "inv_icm45600_buffer.h"
>> +
>> #define INV_ICM45600_REG_BANK_MASK GENMASK(15, 8)
>> #define INV_ICM45600_REG_ADDR_MASK GENMASK(7, 0)
>>
>> @@ -128,6 +130,7 @@ extern const struct inv_icm45600_chip_info inv_icm45689_chip_info;
>> * @indio_gyro: gyroscope IIO device.
>> * @indio_accel: accelerometer IIO device.
>> * @timestamp: interrupt timestamps.
>> + * @fifo: FIFO management structure.
>> * @buffer: data transfer buffer aligned for DMA.
>> */
>> struct inv_icm45600_state {
>> @@ -144,6 +147,7 @@ struct inv_icm45600_state {
>> s64 gyro;
>> s64 accel;
>> } timestamp;
>> + struct inv_icm45600_fifo fifo;
>
>This has a forced alignment at the end which is going to ensure the next bit
>is pushed out a long way. It it is possible to squash the definitions here
>so we can see what is going on that would be good.
>
In your review of v3, you suggested to dynamically allocate this buffer to optimize
memory usage, I will rather implement the suggested allocation (i.e. data will be a pointer).
And thus, there will be a single IIO_DMA_MINALIGN at the end of the struct.
>> union {
>> u8 buff[2];
>> __le16 u16;
>> diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..a07affa0e3c64f84330071c92018438882cdf686
>> --- /dev/null
>> +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c
>
>> +
>> +struct inv_icm45600_fifo_1sensor_packet {
>> + u8 header;
>> + struct inv_icm45600_fifo_sensor_data data;
>> + s8 temp;
>> +} __packed;
>> +#define INV_ICM45600_FIFO_1SENSOR_PACKET_SIZE 8
>> +
>> +struct inv_icm45600_fifo_2sensors_packet {
>> + u8 header;
>> + struct inv_icm45600_fifo_sensor_data accel;
>> + struct inv_icm45600_fifo_sensor_data gyro;
>> + s8 temp;
>> + __le16 timestamp;
>> +} __packed;
>> +#define INV_ICM45600_FIFO_2SENSORS_PACKET_SIZE 16
>This definition seems unnecessary given you have
> sizeof(struct inv_icm45600_fifo_2sensors_packet)
>(or better yet sizeof(instance)) to use for it.
Ok to use sizeof instead.
>
>
>
>
>> +void inv_icm45600_buffer_update_fifo_period(struct inv_icm45600_state *st)
>> +{
>> + u32 period_gyro, period_accel, period;
>> +
>> + if (st->fifo.en & INV_ICM45600_SENSOR_GYRO)
>> + period_gyro = inv_icm45600_odr_to_period(st->conf.gyro.odr);
>> + else
>> + period_gyro = U32_MAX;
>> +
>> + if (st->fifo.en & INV_ICM45600_SENSOR_ACCEL)
>> + period_accel = inv_icm45600_odr_to_period(st->conf.accel.odr);
>> + else
>> + period_accel = U32_MAX;
>> +
>> + if (period_gyro <= period_accel)
>> + period = period_gyro;
>> + else
>> + period = period_accel;
>
> st->fifo.period = min(period_gyro, period_accel);
>saves a few lines.
Ok, thanks for the advice.
>
>> +
>> + st->fifo.period = period;
>> +}
>
>> +static unsigned int inv_icm45600_wm_truncate(unsigned int watermark, size_t packet_size,
>> + unsigned int fifo_period)
>> +{
>> + size_t watermark_max, grace_samples;
>> +
>> + /* Keep 20ms for processing FIFO. */
>> + grace_samples = (20U * 1000000U) / fifo_period;
>> + if (grace_samples < 1)
>> + grace_samples = 1;
>> +
>> + watermark_max = INV_ICM45600_FIFO_SIZE_MAX / packet_size;
>> + watermark_max -= grace_samples;
>> +
>> + if (watermark > watermark_max)
>> + watermark = watermark_max;
>
> return min(watermark, watermark_max);
Good point !
>> +
>> + return watermark;
>> +}
>> +
>> +/**
>> + * inv_icm45600_buffer_update_watermark - update watermark FIFO threshold
>> + * @st: driver internal state
>> + *
>> + * Returns 0 on success, a negative error code otherwise.
>> + *
>> + * FIFO watermark threshold is computed based on the required watermark values
>> + * set for gyro and accel sensors. Since watermark is all about acceptable data
>> + * latency, use the smallest setting between the 2. It means choosing the
>> + * smallest latency but this is not as simple as choosing the smallest watermark
>> + * value. Latency depends on watermark and ODR. It requires several steps:
>> + * 1) compute gyro and accel latencies and choose the smallest value.
>> + * 2) adapt the chosen latency so that it is a multiple of both gyro and accel
>> + * ones. Otherwise it is possible that you don't meet a requirement. (for
>> + * example with gyro @100Hz wm 4 and accel @100Hz with wm 6, choosing the
>> + * value of 4 will not meet accel latency requirement because 6 is not a
>> + * multiple of 4. You need to use the value 2.)
>> + * 3) Since all periods are multiple of each others, watermark is computed by
>> + * dividing this computed latency by the smallest period, which corresponds
>> + * to the FIFO frequency.
>
>Nice explanation and clear code. Thanks!
>
>> + */
>> +int inv_icm45600_buffer_update_watermark(struct inv_icm45600_state *st)
>> +{
>> + const size_t packet_size = INV_ICM45600_FIFO_2SENSORS_PACKET_SIZE;
>> + unsigned int wm_gyro, wm_accel, watermark;
>> + u32 period_gyro, period_accel, period;
>> + u32 latency_gyro, latency_accel, latency;
>> +
>> + /* Compute sensors latency, depending on sensor watermark and odr. */
>> + wm_gyro = inv_icm45600_wm_truncate(st->fifo.watermark.gyro, packet_size,
>> + st->fifo.period);
>> + wm_accel = inv_icm45600_wm_truncate(st->fifo.watermark.accel, packet_size,
>> + st->fifo.period);
>> + /* Use us for odr to avoid overflow using 32 bits values. */
>> + period_gyro = inv_icm45600_odr_to_period(st->conf.gyro.odr) / 1000UL;
>> + period_accel = inv_icm45600_odr_to_period(st->conf.accel.odr) / 1000UL;
>> + latency_gyro = period_gyro * wm_gyro;
>> + latency_accel = period_accel * wm_accel;
>> +
>> + /* 0 value for watermark means that the sensor is turned off. */
>> + if (wm_gyro == 0 && wm_accel == 0)
>> + return 0;
>> +
>> + if (latency_gyro == 0) {
>> + watermark = wm_accel;
>> + st->fifo.watermark.eff_accel = wm_accel;
>> + } else if (latency_accel == 0) {
>> + watermark = wm_gyro;
>> + st->fifo.watermark.eff_gyro = wm_gyro;
>> + } else {
>> + /* Compute the smallest latency that is a multiple of both. */
>> + if (latency_gyro <= latency_accel)
>> + latency = latency_gyro - (latency_accel % latency_gyro);
>> + else
>> + latency = latency_accel - (latency_gyro % latency_accel);
>> + /* Use the shortest period. */
>> + if (period_gyro <= period_accel)
>> + period = period_gyro;
>> + else
>> + period = period_accel;
>> + /* All this works because periods are multiple of each others. */
>> + watermark = latency / period;
>> + if (watermark < 1)
>> + watermark = 1;
>> + /* Update effective watermark. */
>> + st->fifo.watermark.eff_gyro = latency / period_gyro;
>> + if (st->fifo.watermark.eff_gyro < 1)
>> + st->fifo.watermark.eff_gyro = 1;
>> + st->fifo.watermark.eff_accel = latency / period_accel;
>> + if (st->fifo.watermark.eff_accel < 1)
>> + st->fifo.watermark.eff_accel = 1;
>> + }
>> +
>> +
>> + st->buffer.u16 = cpu_to_le16(watermark);
>> + return regmap_bulk_write(st->map, INV_ICM45600_REG_FIFO_WATERMARK,
>> + &st->buffer.u16, sizeof(st->buffer.u16));
>> +}
>
>> +
>> +static int inv_icm45600_buffer_predisable(struct iio_dev *indio_dev)
>> +{
>> + struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
>> + unsigned int val;
>> + int ret;
>> +
>> + guard(mutex)(&st->lock);
>> +
>> + /* Exit if there are several sensors using the FIFO. */
>> + if (st->fifo.on > 1) {
>> + /* decrease FIFO on counter */
>> + st->fifo.on--;
>> + return 0;
>> + }
>> +
>> + /* Disable writing sensor data to FIFO. */
>> + ret = regmap_clear_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3,
>> + INV_ICM45600_FIFO_CONFIG3_IF_EN);
>> + if (ret)
>> + return ret;
>> +
>> + /* Set FIFO in bypass mode. */
>> + val = FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK,
>> + INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS);
>
>One more space. Check for similar.
I'll fix, among others.
>
>> + ret = regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0,
>> + INV_ICM45600_FIFO_CONFIG0_MODE_MASK, val);
>> + if (ret)
>> + return ret;
>> +
>> + /* Disable FIFO threshold and full interrupt. */
>> + ret = regmap_clear_bits(st->map, INV_ICM45600_REG_INT1_CONFIG0,
>> + INV_ICM45600_INT1_CONFIG0_FIFO_THS_EN |
>> + INV_ICM45600_INT1_CONFIG0_FIFO_FULL_EN);
>> + if (ret)
>> + return ret;
>> +
>> + /* Flush all FIFO data. */
>> + ret = regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG2,
>> + INV_ICM45600_REG_FIFO_CONFIG2_FIFO_FLUSH);
>> + if (ret)
>> + return ret;
>> +
>> + /* Decrease FIFO on counter. */
>> + st->fifo.on--;
>> + return 0;
>> +}
>
>> +
>> +static int inv_icm45600_buffer_postdisable(struct iio_dev *indio_dev)
>> +{
>> + struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
>> + struct device *dev = regmap_get_device(st->map);
>> + unsigned int sensor;
>> + unsigned int *watermark;
>> + unsigned int sleep;
>> + int ret;
>> +
>> + if (indio_dev == st->indio_gyro) {
>> + sensor = INV_ICM45600_SENSOR_GYRO;
>> + watermark = &st->fifo.watermark.gyro;
>> + } else if (indio_dev == st->indio_accel) {
>> + sensor = INV_ICM45600_SENSOR_ACCEL;
>> + watermark = &st->fifo.watermark.accel;
>> + } else {
>> + return -EINVAL;
>> + }
>> +
>> + scoped_guard(mutex, &st->lock)
>> + ret = _inv_icm45600_buffer_postdisable(st, sensor, watermark, &sleep);
>> +
>> + /* Sleep required time. */
>> + if (sleep)
>
>Is there a case where sleep is 0? If not just do it unconditionally perhaps.
Yes, sleep is not 0, when a sensor has been stopped in the process.
>
>
>> + msleep(sleep);
>> +
>> + pm_runtime_put_autosuspend(dev);
>> +
>> + return ret;
>> +}
>> +
>> +const struct iio_buffer_setup_ops inv_icm45600_buffer_ops = {
>> + .preenable = inv_icm45600_buffer_preenable,
>> + .postenable = inv_icm45600_buffer_postenable,
>> + .predisable = inv_icm45600_buffer_predisable,
>> + .postdisable = inv_icm45600_buffer_postdisable,
>> +};
>> +
>> +int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st,
>> + unsigned int max)
>What is max here? Seems to be passed 0 in the only caller.
Function call with max > 0 is implemented later in the same patch
(in 4/8, from inv_icm45600_buffer_hwfifo_flush).
>> +{
>> + const ssize_t packet_size = INV_ICM45600_FIFO_2SENSORS_PACKET_SIZE;
>> + __le16 *raw_fifo_count;
>> + size_t fifo_nb, i;
>> + ssize_t size;
>> + const struct inv_icm45600_fifo_sensor_data *accel, *gyro;
>> + const __le16 *timestamp;
>> + const s8 *temp;
>> + unsigned int odr;
>> + int ret;
>> +
>> + /* Reset all samples counters. */
>> + st->fifo.count = 0;
>> + st->fifo.nb.gyro = 0;
>> + st->fifo.nb.accel = 0;
>> + st->fifo.nb.total = 0;
>> +
>> + /* Read FIFO count value. */
>> + raw_fifo_count = &st->buffer.u16;
>> + ret = regmap_bulk_read(st->map, INV_ICM45600_REG_FIFO_COUNT,
>> + raw_fifo_count, sizeof(*raw_fifo_count));
>
>For IIO drivers at least we still operated under some guidance the regmap maintainer
>gave years ago to never assume regmap (for busses that otherwise require DMA safe
>buffers) will always bounce the data. So bulk reads with SPI buffers need
>DMA safe buffers. Easiest is usually an __aligned(IIO_DMA_MINALIGN) buffer
>(or set of buffers) at the end of st.
>
>In practice last time I checked regmap doesn't bother with the zero copy
>optimization that would need this safety so you won't actually hit this
>issue.
From my understanding, alignment of &st->buffer.u16 is correct because the union is aligned,
union {
u8 buff[2];
__le16 u16;
} buffer __aligned(IIO_DMA_MINALIGN);
Please let me know if my answer is not correct.
>
>> + if (ret)
>> + return ret;
>> + fifo_nb = le16_to_cpup(raw_fifo_count);
>> +
>> + /* Check and limit number of samples if requested. */
>> + if (fifo_nb == 0)
>> + return 0;
>> + if (max > 0 && fifo_nb > max)
>> + fifo_nb = max;
>> +
>> + /* Try to read all FIFO data in internal buffer. */
>> + st->fifo.count = fifo_nb * packet_size;
>> + ret = regmap_noinc_read(st->map, INV_ICM45600_REG_FIFO_DATA,
>> + st->fifo.data, st->fifo.count);
>> + if (ret == -ENOTSUPP || ret == -EFBIG) {
>> + /* Read full fifo is not supported, read samples one by one. */
>
>Interesting. Maybe we could push the fallback to regmap or further given it's
>emulating the big read version.
That would be great indeed.
>
>> + ret = 0;
>> + for (i = 0; i < st->fifo.count && ret == 0; i += packet_size)
>> + ret = regmap_noinc_read(st->map, INV_ICM45600_REG_FIFO_DATA,
>> + &st->fifo.data[i], packet_size);
>> + }
>> + if (ret)
>> + return ret;
>> +
>> + for (i = 0; i < st->fifo.count; i += size) {
>> + size = inv_icm45600_fifo_decode_packet(&st->fifo.data[i],
>> + &accel, &gyro, &temp, ×tamp, &odr);
>> + if (size <= 0)
>> + break;
>> + if (gyro != NULL && inv_icm45600_fifo_is_data_valid(gyro))
>> + st->fifo.nb.gyro++;
>> + if (accel != NULL && inv_icm45600_fifo_is_data_valid(accel))
>> + st->fifo.nb.accel++;
>> + st->fifo.nb.total++;
>> + }
>> +
>> + return 0;
>> +}
>
>> diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..09595a41cf637a3ba9bc44e4df53a9d0aa11f485
>> --- /dev/null
>> +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h
>> @@ -0,0 +1,99 @@
>
>
>> +/**
>> + * struct inv_icm45600_fifo - FIFO state variables
>> + * @on: reference counter for FIFO on.
>> + * @en: bits field of INV_ICM45600_SENSOR_* for FIFO EN bits.
>> + * @period: FIFO internal period.
>> + * @watermark: watermark configuration values for accel and gyro.
>> + * @count: number of bytes in the FIFO data buffer.
>> + * @nb: gyro, accel and total samples in the FIFO data buffer.
>> + * @data: FIFO data buffer aligned for DMA (8kB)
>> + */
>> +struct inv_icm45600_fifo {
>> + unsigned int on;
>> + unsigned int en;
>> + u32 period;
>> + struct {
>> + unsigned int gyro;
>> + unsigned int accel;
>> + unsigned int eff_gyro;
>> + unsigned int eff_accel;
>> + } watermark;
>> + size_t count;
>> + struct {
>> + size_t gyro;
>> + size_t accel;
>> + size_t total;
>> + } nb;
>> + u8 data[8192] __aligned(IIO_DMA_MINALIGN);
>
>Doing this inside an embedded structure isn't great for readability.
>See comment above on putting them next to each other. Also worth thinking
>about structure padding that is going on here - maybe point pahole at the
>result.
Same answer than previous comment: I will replace this static buffer by a dynamic allocation.
As suggested in v3 patch review.
>
>> +};
>> +
>> +/* FIFO data packet */
>> +struct inv_icm45600_fifo_sensor_data {
>> + __le16 x;
>> + __le16 y;
>> + __le16 z;
>> +} __packed;
>> +#define INV_ICM45600_FIFO_DATA_INVALID -32768
>> +
>> +static inline s16 inv_icm45600_fifo_get_sensor_data(__le16 d)
>> +{
>> + return le16_to_cpu(d);
>> +}
>> +
>> +static inline bool
>> +inv_icm45600_fifo_is_data_valid(const struct inv_icm45600_fifo_sensor_data *s)
>> +{
>> + s16 x, y, z;
>> +
>> + x = inv_icm45600_fifo_get_sensor_data(s->x);
>> + y = inv_icm45600_fifo_get_sensor_data(s->y);
>> + z = inv_icm45600_fifo_get_sensor_data(s->z);
>> +
>> + if (x == INV_ICM45600_FIFO_DATA_INVALID &&
>> + y == INV_ICM45600_FIFO_DATA_INVALID &&
>> + z == INV_ICM45600_FIFO_DATA_INVALID)
>> + return false;
>> +
>> + return true;
>> +}
>> +
>> +ssize_t inv_icm45600_fifo_decode_packet(const void *packet,
>> + const struct inv_icm45600_fifo_sensor_data **accel,
>> + const struct inv_icm45600_fifo_sensor_data **gyro,
>> + const s8 **temp,
>> + const __le16 **timestamp, unsigned int *odr);
>> +
>> +extern const struct iio_buffer_setup_ops inv_icm45600_buffer_ops;
>> +
>> +int inv_icm45600_buffer_init(struct inv_icm45600_state *st);
>> +
>> +void inv_icm45600_buffer_update_fifo_period(struct inv_icm45600_state *st);
>> +
>> +int inv_icm45600_buffer_set_fifo_en(struct inv_icm45600_state *st,
>> + unsigned int fifo_en);
>> +
>> +int inv_icm45600_buffer_update_watermark(struct inv_icm45600_state *st);
>> +
>> +int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st,
>> + unsigned int max);
>> +
>> +int inv_icm45600_buffer_hwfifo_flush(struct inv_icm45600_state *st,
>> + unsigned int count);
>
>The comment above about considering a single file was because there is a lot in this header
>rather implying maybe there isn't a good separation of code.
Understood, however IMO merging core and buffer files will only save few lines,
while increasing a lot the length of core file.
I fear it would decrease readability in the end.
Nevertheless, if you still think a merge must occur between core and buffer, I'll do it.
>
>> +
>> +#endif
>> diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
>> index 022eb9180b5a750e8fba4a1cd873041c27f59880..37487e9174bf53f2fc6583f37ac7542ea31b0643 100644
>> --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
>> +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
>
>
>> static int inv_icm45600_timestamp_setup(struct inv_icm45600_state *st)
>> {
>> /* Enable timestamps. */
>> @@ -627,6 +717,14 @@ int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm45600_chi
>> if (ret)
>> return ret;
>>
>> + ret = inv_icm45600_buffer_init(st);
>> + if (ret)
>> + return ret;
>> +
>> + ret = inv_icm45600_irq_init(st, irq, irq_type, open_drain);
>> + if (ret)
>> + return ret;
>> +
>> /* Setup runtime power management. */
>> ret = devm_pm_runtime_set_active_enabled(dev);
>> if (ret)
>> @@ -658,6 +756,22 @@ static int inv_icm45600_suspend(struct device *dev)
>> if (pm_runtime_suspended(dev))
>> return 0;
>>
>> + /* Disable FIFO data streaming. */
>> + if (st->fifo.on) {
>> + unsigned int val;
>> +
>> + ret = regmap_clear_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3,
>> + INV_ICM45600_FIFO_CONFIG3_IF_EN);
>> + if (ret)
>> + return ret;
>> + val = FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK,
>> + INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS);
>
>Align second line after (
I'll fix (there are some other occurences)
>
>> + ret = regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0,
>> + INV_ICM45600_FIFO_CONFIG0_MODE_MASK, val);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> ret = inv_icm45600_set_pwr_mgmt0(st, INV_ICM45600_SENSOR_MODE_OFF,
>> INV_ICM45600_SENSOR_MODE_OFF, NULL);
>> if (ret)
>
^ permalink raw reply [flat|nested] 46+ messages in thread
* RE: [PATCH v2 4/8] iio: imu: inv_icm45600: add IMU IIO devices
2025-07-17 14:47 ` Jonathan Cameron
@ 2025-08-11 14:56 ` Remi Buisson
0 siblings, 0 replies; 46+ messages in thread
From: Remi Buisson @ 2025-08-11 14:56 UTC (permalink / raw)
To: Jonathan Cameron, Remi Buisson via B4 Relay
Cc: David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-kernel@vger.kernel.org,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org
>
>
>From: Jonathan Cameron <jic23@kernel.org>
>Sent: Thursday, July 17, 2025 4:48 PM
>To: Remi Buisson via B4 Relay <devnull+remi.buisson.tdk.com@kernel.org>
>Cc: Remi Buisson <Remi.Buisson@tdk.com>; David Lechner <dlechner@baylibre.com>; Nuno Sá <nuno.sa@analog.com>; Andy Shevchenko <andy@kernel.org>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; linux-kernel@vger.kernel.org; linux-iio@vger.kernel.org; devicetree@vger.kernel.org
>Subject: Re: [PATCH v2 4/8] iio: imu: inv_icm45600: add IMU IIO devices
>
>On Thu, 10 Jul 2025 08:57:59 +0000
>Remi Buisson via B4 Relay <devnull+remi.buisson.tdk.com@kernel.org> wrote:
>
>> From: Remi Buisson <remi.buisson@tdk.com>
>>
>> Add IIO devices for accelerometer and gyroscope sensors
>> with data polling interface and FIFO parsing.
>> Attributes: raw, scale, sampling_frequency, calibbias.
>> Temperature is available as a processed channel.
>>
>> Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
>
>Hi Remi,
>
>A few comments inline.
>
>Jonathan
>
Thanks for the review, I sent a v3 of the patch before seeing this.
I'll include those fixes in v4 (with the fixes linked with your review of v3).
Remi
>
>> diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..8ba2e8e54c738a3289726d6f23a46f5307ebdffe
>> --- /dev/null
>> +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c
>> @@ -0,0 +1,809 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/*
>> + * Copyright (C) 2025 Invensense, Inc.
>> + */
>> +
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <linux/err.h>
>> +#include <linux/iio/buffer.h>
>> +#include <linux/iio/common/inv_sensors_timestamp.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/kfifo_buf.h>
>> +#include <linux/math64.h>
>> +#include <linux/mutex.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/regmap.h>
>> +#include <linux/types.h>
>> +
>> +#include "inv_icm45600_buffer.h"
>> +#include "inv_icm45600.h"
>> +
>> +enum inv_icm45600_gyro_scan {
>> + INV_ICM45600_GYRO_SCAN_X,
>> + INV_ICM45600_GYRO_SCAN_Y,
>> + INV_ICM45600_GYRO_SCAN_Z,
>> + INV_ICM45600_GYRO_SCAN_TEMP,
>> + INV_ICM45600_GYRO_SCAN_TIMESTAMP,
>> +};
>> +
>> +static const struct iio_chan_spec_ext_info inv_icm45600_gyro_ext_infos[] = {
>> + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm45600_get_mount_matrix),
>> + { },
>
>No trailing comma on a terminating entry.
Ok.
>
>> +};
>> +
>> +#define INV_ICM45600_GYRO_CHAN(_modifier, _index, _ext_info) \
>> + { \
>> + .type = IIO_ANGL_VEL, \
>> + .modified = 1, \
>> + .channel2 = _modifier, \
>> + .info_mask_separate = \
>> + BIT(IIO_CHAN_INFO_RAW) | \
>> + BIT(IIO_CHAN_INFO_CALIBBIAS), \
>> + .info_mask_shared_by_type = \
>> + BIT(IIO_CHAN_INFO_SCALE), \
>> + .info_mask_shared_by_type_available = \
>> + BIT(IIO_CHAN_INFO_SCALE) | \
>> + BIT(IIO_CHAN_INFO_CALIBBIAS), \
>> + .info_mask_shared_by_all = \
>> + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
>> + .info_mask_shared_by_all_available = \
>> + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
>> + .scan_index = _index, \
>> + .scan_type = { \
>> + .sign = 's', \
>> + .realbits = 16, \
>> + .storagebits = 16, \
>> + .endianness = IIO_LE, \
>> + }, \
>> + .ext_info = _ext_info, \
>> + }
>
>> +static const unsigned long inv_icm45600_gyro_scan_masks[] = {
>> + /* 3-axis gyro + temperature */
>> + BIT(INV_ICM45600_GYRO_SCAN_X) |
>> + BIT(INV_ICM45600_GYRO_SCAN_Y) |
>> + BIT(INV_ICM45600_GYRO_SCAN_Z) |
>> + BIT(INV_ICM45600_GYRO_SCAN_TEMP),
>> + 0,
>
>No comma as that's a terminating entry so nothing should come after it.
Ok.
>
>> +};
>> +
>> +/* enable gyroscope sensor and FIFO write */
>> +static int inv_icm45600_gyro_update_scan_mode(struct iio_dev *indio_dev,
>> + const unsigned long *scan_mask)
>> +{
>> + struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
>> + struct inv_icm45600_sensor_state *gyro_st = iio_priv(indio_dev);
>> + struct inv_icm45600_sensor_conf conf = INV_ICM45600_SENSOR_CONF_INIT;
>> + unsigned int fifo_en = 0;
>> + unsigned int sleep;
>> + int ret;
>> +
>> + scoped_guard(mutex, &st->lock) {
>> + if (*scan_mask & BIT(INV_ICM45600_GYRO_SCAN_TEMP))
>> + fifo_en |= INV_ICM45600_SENSOR_TEMP;
>> +
>> + if (*scan_mask & (BIT(INV_ICM45600_GYRO_SCAN_X) |
>> + BIT(INV_ICM45600_GYRO_SCAN_Y) |
>> + BIT(INV_ICM45600_GYRO_SCAN_Z))) {
>> + /* enable gyro sensor */
>> + conf.mode = gyro_st->power_mode;
>> + ret = inv_icm45600_set_gyro_conf(st, &conf, &sleep);
>> + if (ret)
>> + return ret;
>> + fifo_en |= INV_ICM45600_SENSOR_GYRO;
>> + }
>> + /* update data FIFO write */
>> + ret = inv_icm45600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
>> + }
>> + /* sleep required time */
>Can we just always sleep?
That will be a big loss of time when sensor is already switched on.
It would add a 60ms delay to each individual access.
>
>> + if (sleep)
>> + msleep(sleep);
>> +
>> + return ret;
>> +}
>
>> +
>> +/* IIO format int + nano */
>> +const int inv_icm45600_gyro_scale[][2] = {
>> + /* +/- 2000dps => 0.001065264 rad/s */
>> + [INV_ICM45600_GYRO_FS_2000DPS] = {0, 1065264},
>> + /* +/- 1000dps => 0.000532632 rad/s */
>> + [INV_ICM45600_GYRO_FS_1000DPS] = {0, 532632},
>> + /* +/- 500dps => 0.000266316 rad/s */
>> + [INV_ICM45600_GYRO_FS_500DPS] = {0, 266316},
>> + /* +/- 250dps => 0.000133158 rad/s */
>> + [INV_ICM45600_GYRO_FS_250DPS] = {0, 133158},
>> + /* +/- 125dps => 0.000066579 rad/s */
>> + [INV_ICM45600_GYRO_FS_125DPS] = {0, 66579},
>> + /* +/- 62.5dps => 0.000033290 rad/s */
>> + [INV_ICM45600_GYRO_FS_62_5DPS] = {0, 33290},
>> + /* +/- 31.25dps => 0.000016645 rad/s */
>> + [INV_ICM45600_GYRO_FS_31_25DPS] = {0, 16645},
>> + /* +/- 15.625dps => 0.000008322 rad/s */
>> + [INV_ICM45600_GYRO_FS_15_625DPS] = {0, 8322},
>> +};
>> +
>> +/* IIO format int + nano */
>> +const int inv_icm45686_gyro_scale[][2] = {
>> + /* +/- 4000dps => 0.002130529 rad/s */
>> + [INV_ICM45686_GYRO_FS_4000DPS] = {0, 2130529},
>{ 0, 2130529 },
>
>preferred for IIO code. Arbitrary choice I made a while back
>in the interests of homogeneous looking code.
Ok, I'll align the code on this.
>
>
>> + /* +/- 2000dps => 0.001065264 rad/s */
>> + [INV_ICM45686_GYRO_FS_2000DPS] = {0, 1065264},
>> + /* +/- 1000dps => 0.000532632 rad/s */
>> + [INV_ICM45686_GYRO_FS_1000DPS] = {0, 532632},
>> + /* +/- 500dps => 0.000266316 rad/s */
>> + [INV_ICM45686_GYRO_FS_500DPS] = {0, 266316},
>> + /* +/- 250dps => 0.000133158 rad/s */
>> + [INV_ICM45686_GYRO_FS_250DPS] = {0, 133158},
>> + /* +/- 125dps => 0.000066579 rad/s */
>> + [INV_ICM45686_GYRO_FS_125DPS] = {0, 66579},
>> + /* +/- 62.5dps => 0.000033290 rad/s */
>> + [INV_ICM45686_GYRO_FS_62_5DPS] = {0, 33290},
>> + /* +/- 31.25dps => 0.000016645 rad/s */
>> + [INV_ICM45686_GYRO_FS_31_25DPS] = {0, 16645},
>> + /* +/- 15.625dps => 0.000008322 rad/s */
>> + [INV_ICM45686_GYRO_FS_15_625DPS] = {0, 8322},
>> +};
>
>> +
>> +/* IIO format int + micro */
>> +static const int inv_icm45600_gyro_odr[] = {
>> + /* 1.5625Hz */
>> + 1, 562500,
> 1, 562500, /* 1.5625Hz */
> 3, 125000, /* 3.125Hz */
>
>etc if you think the comments are needed.
Ok I'll update.
>
>> + /* 3.125Hz */
>> + 3, 125000,
>> + /* 6.25Hz */
>> + 6, 250000,
>> + /* 12.5Hz */
>> + 12, 500000,
>> + /* 25Hz */
>> + 25, 0,
>> + /* 50Hz */
>> + 50, 0,
>> + /* 100Hz */
>> + 100, 0,
>> + /* 200Hz */
>> + 200, 0,
>> + /* 400Hz */
>> + 400, 0,
>> + /* 800Hz */
>> + 800, 0,
>> + /* 1.6kHz */
>> + 1600, 0,
>> + /* 3.2kHz */
>> + 3200, 0,
>> + /* 6.4kHz */
>> + 6400, 0,
>> +};
>
>> +/*
>> + * Calibration bias values, IIO range format int + nano.
>> + * Value is limited to +/-62.5dps coded on 14 bits signed. Step is 7.5mdps.
>> + */
>> +static int inv_icm45600_gyro_calibbias[] = {
>> + -1, 90830336, /* min: -1.090830336 rad/s */
>> + 0, 133158, /* step: 0.000133158 rad/s */
>> + 1, 90697178, /* max: 1.090697178 rad/s */
>> +};
>
>> +static int inv_icm45600_gyro_write_offset(struct inv_icm45600_state *st,
>> + struct iio_chan_spec const *chan,
>> + int val, int val2)
>As this is calibbias, it's not really required to be in any particular
>units. Often these are analog front end tweaks where that doesn't matter.
>So you could just have this take the integer value you compute instead
>of doing all this maths. Also update the available ranges if you do that.
>
>
>I don't mind if you want something specific though (like radians) though
>as that's also compliant with the ABI.
I'd prefer to keep rad/s, to align with other invensense IMU.
>
>> +{
>> + struct device *dev = regmap_get_device(st->map);
>> + s64 val64, min, max;
>> + unsigned int reg;
>> + s16 offset;
>> + int ret;
>> +
>> + if (chan->type != IIO_ANGL_VEL)
>> + return -EINVAL;
>> +
>> + switch (chan->channel2) {
>> + case IIO_MOD_X:
>> + reg = INV_ICM45600_IPREG_SYS1_REG_42;
>> + break;
>> + case IIO_MOD_Y:
>> + reg = INV_ICM45600_IPREG_SYS1_REG_56;
>> + break;
>> + case IIO_MOD_Z:
>> + reg = INV_ICM45600_IPREG_SYS1_REG_70;
>> + break;
>> + default:
>> + return -EINVAL;
>> + }
>> +
>> + /* inv_icm45600_gyro_calibbias: min - step - max in nano */
>> + min = (s64)inv_icm45600_gyro_calibbias[0] * 1000000000LL -
>> + (s64)inv_icm45600_gyro_calibbias[1];
>> + max = (s64)inv_icm45600_gyro_calibbias[4] * 1000000000LL +
>> + (s64)inv_icm45600_gyro_calibbias[5];
>> + val64 = (s64)val * 1000000000LL;
>> + if (val > 0)
>> + val64 += (s64)val2;
>> + else
>> + val64 -= (s64)val2;
>> + if (val64 < min || val64 > max)
>> + return -EINVAL;
>> +
>> + /*
>> + * convert rad/s to dps then to raw value
>> + * rad to dps: 180 / Pi
>> + * dps to raw 14 bits signed, max 62.5: 8192 / 62.5
>> + * val in nano (1000000000)
>> + * val * 180 * 8192 / (Pi * 1000000000 * 62.5)
>> + */
>> + val64 = val64 * 180LL * 8192;
>> + /* for rounding, add + or - divisor (314159265 * 625) divided by 2 */
>> + if (val64 >= 0)
>> + val64 += 314159265LL * 625LL / 2LL;
>> + else
>> + val64 -= 314159265LL * 625LL / 2LL;
>> + offset = div64_s64(val64, 314159265LL * 625LL);
>> +
>> + /* clamp value limited to 14 bits signed */
>> + if (offset < -8192)
>> + offset = -8192;
>> + else if (offset > 8191)
>> + offset = 8191;
>> +
>> + st->buffer.u16 = cpu_to_le16(offset & INV_ICM45600_GYRO_OFFUSER_MASK);
>> +
>> + ret = pm_runtime_resume_and_get(dev);
>> + if (ret)
>> + return ret;
>> +
>> + scoped_guard(mutex, &st->lock)
>> + ret = regmap_bulk_write(st->map, reg, &st->buffer.u16, sizeof(st->buffer.u16));
>> +
>> + pm_runtime_put_autosuspend(dev);
>> + return ret;
>> +}
>
>
>
>> +static int inv_icm45600_gyro_hwfifo_flush(struct iio_dev *indio_dev,
>> + unsigned int count)
>> +{
>> + struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
>> + int ret;
>> +
>> + if (count == 0)
>> + return 0;
>> +
>> + guard(mutex)(&st->lock);
>> +
>> + ret = inv_icm45600_buffer_hwfifo_flush(st, count);
>> + if (!ret)
>> + ret = st->fifo.nb.gyro;
> if (ret)
> return ret;
>
> return s->fifo.nb.gyro;
>
>preferred slightly because it keeps the error path out of line.
>When reading a lot of code conventions like that an help.
>
Agreed, your suggestion is far clearer.
>> +
>> + return ret;
>> +}
>
>> +
>> +struct iio_dev *inv_icm45600_gyro_init(struct inv_icm45600_state *st)
>> +{
>> + struct device *dev = regmap_get_device(st->map);
>> + const char *name;
>
>Pick an ordering. If nothing else makes sense, reverse xmas tree
>isn't too ugly.
I'll fix.
>
>> + struct inv_icm45600_sensor_state *gyro_st;
>> + struct inv_sensors_timestamp_chip ts_chip;
>> + struct iio_dev *indio_dev;
>> + int ret;
>> +
>> + name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->chip_info->name);
>> + if (!name)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + indio_dev = devm_iio_device_alloc(dev, sizeof(*gyro_st));
>> + if (!indio_dev)
>> + return ERR_PTR(-ENOMEM);
>> + gyro_st = iio_priv(indio_dev);
>> +
>> + gyro_st->scales = st->chip_info->gyro_scales;
>> + gyro_st->scales_len = st->chip_info->gyro_scales_len * 2;
>> +
>> + /* low-noise by default at init */
>> + gyro_st->power_mode = INV_ICM45600_SENSOR_MODE_LOW_NOISE;
>> +
>> + /*
>> + * clock period is 32kHz (31250ns)
>> + * jitter is +/- 2% (20 per mille)
>> + */
>> + ts_chip.clock_period = 31250;
>> + ts_chip.jitter = 20;
>> + ts_chip.init_period = inv_icm45600_odr_to_period(st->conf.gyro.odr);
>> + inv_sensors_timestamp_init(&gyro_st->ts, &ts_chip);
>> +
>> + iio_device_set_drvdata(indio_dev, st);
>> + indio_dev->name = name;
>> + indio_dev->info = &inv_icm45600_gyro_info;
>> + indio_dev->modes = INDIO_DIRECT_MODE;
>> + indio_dev->channels = inv_icm45600_gyro_channels;
>> + indio_dev->num_channels = ARRAY_SIZE(inv_icm45600_gyro_channels);
>> + indio_dev->available_scan_masks = inv_icm45600_gyro_scan_masks;
>> + indio_dev->setup_ops = &inv_icm45600_buffer_ops;
>> +
>> + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
>> + &inv_icm45600_buffer_ops);
>> + if (ret)
>> + return ERR_PTR(ret);
>> +
>> + ret = devm_iio_device_register(dev, indio_dev);
>> + if (ret)
>> + return ERR_PTR(ret);
>> +
>> + return indio_dev;
>> +}
>> +
>> +int inv_icm45600_gyro_parse_fifo(struct iio_dev *indio_dev)
>> +{
>> + struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
>> + struct inv_icm45600_sensor_state *gyro_st = iio_priv(indio_dev);
>> + struct inv_sensors_timestamp *ts = &gyro_st->ts;
>> + ssize_t i, size;
>> + unsigned int no;
>> + const struct inv_icm45600_fifo_sensor_data *accel, *gyro;
>> + const __le16 *timestamp;
>> + const s8 *temp;
>> + unsigned int odr;
>> + s64 ts_val;
>> + struct inv_icm45600_gyro_buffer buffer;
>
>I'd push scope of many of these into the for loop.
Sure, good point!
>
>> +
>> + /* parse all fifo packets */
>> + for (i = 0, no = 0; i < st->fifo.count; i += size, ++no) {
>> + size = inv_icm45600_fifo_decode_packet(&st->fifo.data[i],
>> + &accel, &gyro, &temp, ×tamp, &odr);
>> + /* quit if error or FIFO is empty */
>> + if (size <= 0)
>> + return size;
>> +
>> + /* skip packet if no gyro data or data is invalid */
>> + if (gyro == NULL || !inv_icm45600_fifo_is_data_valid(gyro))
>> + continue;
>> +
>> + /* update odr */
>> + if (odr & INV_ICM45600_SENSOR_GYRO)
>> + inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
>> + st->fifo.nb.total, no);
>> +
>> + /* buffer is copied to userspace, zeroing it to avoid any data leak */
>> + memset(&buffer, 0, sizeof(buffer));
>> + memcpy(&buffer.gyro, gyro, sizeof(buffer.gyro));
>> + /* convert 8 bits FIFO temperature in high resolution format */
>> + buffer.temp = temp ? (*temp * 64) : 0;
>> + ts_val = inv_sensors_timestamp_pop(ts);
>> + iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val);
>> + }
>> +
>> + return 0;
>> +}
>>
>
>
>
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 3/8] iio: imu: inv_icm45600: add buffer support in iio devices
2025-08-11 14:13 ` Remi Buisson
@ 2025-08-16 11:17 ` Jonathan Cameron
2025-08-20 13:34 ` Remi Buisson
0 siblings, 1 reply; 46+ messages in thread
From: Jonathan Cameron @ 2025-08-16 11:17 UTC (permalink / raw)
To: Remi Buisson
Cc: Remi Buisson via B4 Relay, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org
On Mon, 11 Aug 2025 14:13:54 +0000
Remi Buisson <Remi.Buisson@tdk.com> wrote:
> >
> >
> >From: Jonathan Cameron <jic23@kernel.org>
> >Sent: Thursday, July 17, 2025 4:34 PM
> >To: Remi Buisson via B4 Relay <devnull+remi.buisson.tdk.com@kernel.org>
> >Cc: Remi Buisson <Remi.Buisson@tdk.com>; David Lechner <dlechner@baylibre.com>; Nuno Sá <nuno.sa@analog.com>; Andy Shevchenko <andy@kernel.org>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; linux-kernel@vger.kernel.org; linux-iio@vger.kernel.org; devicetree@vger.kernel.org
> >Subject: Re: [PATCH v2 3/8] iio: imu: inv_icm45600: add buffer support in iio devices
> >On Thu, 10 Jul 2025 08:57:58 +0000
> >Remi Buisson via B4 Relay <devnull+remi.buisson.tdk.com@kernel.org> wrote:
> >
> >> From: Remi Buisson <remi.buisson@tdk.com>
> >>
> >> Add FIFO control functions.
> >> Support hwfifo watermark by multiplexing gyro and accel settings.
> >> Support hwfifo flush.
> >>
> >> Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
> >Hi Remi,
> >
> >Sorry for delay - hectic week.
> >
> >Jonathan
> No problem, thanks for the review ! (and sorry for my late reply)
> >
> >> ---
> >> drivers/iio/imu/inv_icm45600/Makefile | 1 +
> >> drivers/iio/imu/inv_icm45600/inv_icm45600.h | 4 +
> >> drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c | 514 +++++++++++++++++++++
> >> drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h | 99 ++++
> >> drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 137 +++++-
> >We used to do the buffer / core split a lot but it often ends up more trouble
> >that it is worth and we no longer make buffer support a build time option (which was
> >what motivated the separate files) Consider how much simplification you'd get by squashing them into
> >one file.
> I understand the point.
> However merging files will allow to remove 5 lines at most,
> while the length of the core file will increase a lot.
> I'm not sure of the benefit in the end, but
> please let me know if you really want me to proceed with the merge.
It's only a combined 1.5k. That would be fine even if the savings are fairly small.
It's not something I care that much about though.
> >> +const struct iio_buffer_setup_ops inv_icm45600_buffer_ops = {
> >> + .preenable = inv_icm45600_buffer_preenable,
> >> + .postenable = inv_icm45600_buffer_postenable,
> >> + .predisable = inv_icm45600_buffer_predisable,
> >> + .postdisable = inv_icm45600_buffer_postdisable,
> >> +};
> >> +
> >> +int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st,
> >> + unsigned int max)
> >What is max here? Seems to be passed 0 in the only caller.
> Function call with max > 0 is implemented later in the same patch
> (in 4/8, from inv_icm45600_buffer_hwfifo_flush).
Maybe push the parameter being introduced to ther.
> >> +{
> >> + const ssize_t packet_size = INV_ICM45600_FIFO_2SENSORS_PACKET_SIZE;
> >> + __le16 *raw_fifo_count;
> >> + size_t fifo_nb, i;
> >> + ssize_t size;
> >> + const struct inv_icm45600_fifo_sensor_data *accel, *gyro;
> >> + const __le16 *timestamp;
> >> + const s8 *temp;
> >> + unsigned int odr;
> >> + int ret;
> >> +
> >> + /* Reset all samples counters. */
> >> + st->fifo.count = 0;
> >> + st->fifo.nb.gyro = 0;
> >> + st->fifo.nb.accel = 0;
> >> + st->fifo.nb.total = 0;
> >> +
> >> + /* Read FIFO count value. */
> >> + raw_fifo_count = &st->buffer.u16;
> >> + ret = regmap_bulk_read(st->map, INV_ICM45600_REG_FIFO_COUNT,
> >> + raw_fifo_count, sizeof(*raw_fifo_count));
> >
> >For IIO drivers at least we still operated under some guidance the regmap maintainer
> >gave years ago to never assume regmap (for busses that otherwise require DMA safe
> >buffers) will always bounce the data. So bulk reads with SPI buffers need
> >DMA safe buffers. Easiest is usually an __aligned(IIO_DMA_MINALIGN) buffer
> >(or set of buffers) at the end of st.
> >
> >In practice last time I checked regmap doesn't bother with the zero copy
> >optimization that would need this safety so you won't actually hit this
> >issue.
> From my understanding, alignment of &st->buffer.u16 is correct because the union is aligned,
>
> union {
> u8 buff[2];
> __le16 u16;
> } buffer __aligned(IIO_DMA_MINALIGN);
>
> Please let me know if my answer is not correct.
Ah. I probably just missed that. can't remember!
> >
^ permalink raw reply [flat|nested] 46+ messages in thread
* RE: [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600
2025-07-14 5:38 ` Krzysztof Kozlowski
2025-07-15 8:35 ` Remi Buisson
@ 2025-08-19 10:19 ` Remi Buisson
[not found] ` <FR2PPF4571F02BC2C08BFD80F57AC4F45AA8C30A@FR2PPF4571F02BC.DEUP281.PROD.OUTLOOK.COM>
2 siblings, 0 replies; 46+ messages in thread
From: Remi Buisson @ 2025-08-19 10:19 UTC (permalink / raw)
To: Krzysztof Kozlowski, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org
>
>
>From: Krzysztof Kozlowski <krzk@kernel.org>
>Sent: Monday, July 14, 2025 7:39 AM
>To: Remi Buisson <Remi.Buisson@tdk.com>; Jonathan Cameron <jic23@kernel.org>; David Lechner <dlechner@baylibre.com>; Nuno Sá <nuno.sa@analog.com>; Andy Shevchenko <andy@kernel.org>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>
>Cc: linux-kernel@vger.kernel.org; linux-iio@vger.kernel.org; devicetree@vger.kernel.org
>Subject: Re: [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600
>
>On 10/07/2025 10:57, Remi Buisson via B4 Relay wrote:
>> +
>> + reg:
>> + maxItems: 1
>> +
>> + interrupts:
>> + minItems: 1
>> + maxItems: 2
>> +
>> + interrupt-names:
>> + minItems: 1
>> + maxItems: 2
>> + items:
>> + enum:
>> + - INT1
>> + - INT2
>
Hello Krzysztof,
Could you please help me understand the proposed simplification?
>This can be simpler
>
>minItems: 1
>items:
> - enum: [ int1, int2 ]
This should be the one-lined "original" list.
> - const: int 2
I don't understand what this "int 2" does.
Why do we need a const here ?
>
>and use lowercase anyway.
Sure.
>
>> + description: Choose chip interrupt pin to be used as interrupt input.
>> +
>> + drive-open-drain:
>> + type: boolean
>> +
>> + vdd-supply:
>> + description: Regulator that provides power to the sensor
>> +
>> + vddio-supply:
>> + description: Regulator that provides power to the bus
>> +
>> + mount-matrix:
>> + description: an optional 3x3 mounting rotation matrix
>> +
>> +required:
>> + - compatible
>> + - reg
>> + - interrupts
>> + - interrupt-names
>
>Missing supplies
>
>> +
>> +allOf:
>> + - $ref: /schemas/spi/spi-peripheral-props.yaml#
>> +
>> +unevaluatedProperties: false
>> +
>> +examples:
>> + - |
>> + #include <dt-bindings/gpio/gpio.h>
>> + #include <dt-bindings/interrupt-controller/irq.h>
>> + i2c {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + icm45605@68 {
>
>It does not look like you tested the DTS against bindings. Please run
>`make dtbs_check W=1` (see
>Documentation/devicetree/bindings/writing-schema.rst or
>https://urldefense.com/v3/__https://www.linaro.org/blog/tips-and-tricks-for-validating-devicetree-sources-with-the-devicetree-schema/__;!!FtrhtPsWDhZ6tw!Dse6zIqgRmQ8nxzdmpC7Uu96WBVwNWbql_4WnfPwjuISbauv7GGsDO2zSDTq4TEzSeb0s6fcbmFR$[linaro[.]org]
>for instructions).
>Maybe you need to update your dtschema and yamllint. Don't rely on
>distro packages for dtschema and be sure you are using the latest
>released dtschema.
>
>(see how other bindings or DTS call this type of device)
>
>> + compatible = "invensense,icm45605";
>> + reg = <0x68>;
>> + interrupt-parent = <&gpio2>;
>> + interrupt-names = "INT1";
>> + interrupts = <7 IRQ_TYPE_EDGE_RISING>;
>> + vdd-supply = <&vdd>;
>> + vddio-supply = <&vddio>;
>> + mount-matrix = "0", "-1", "0",
>> + "1", "0", "0",
>> + "0", "0", "1";
>> + };
>Best regards,
>Krzysztof
>
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600
[not found] ` <FR2PPF4571F02BC2C08BFD80F57AC4F45AA8C30A@FR2PPF4571F02BC.DEUP281.PROD.OUTLOOK.COM>
@ 2025-08-19 14:28 ` Krzysztof Kozlowski
2025-08-19 14:53 ` Remi Buisson
0 siblings, 1 reply; 46+ messages in thread
From: Krzysztof Kozlowski @ 2025-08-19 14:28 UTC (permalink / raw)
To: Remi Buisson, Jonathan Cameron, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org
On 19/08/2025 12:18, Remi Buisson wrote:
>>> + interrupt-names:
>>> + minItems: 1
>>> + maxItems: 2
>>> + items:
>>> + enum:
>>> + - INT1
>>> + - INT2
>>
> Hello Krzysztof,
> Could you please help me understand the proposed simplification?
>> This can be simpler
>>
>> minItems: 1
>> items:
>> - enum: [ int1, int2 ]
> This should be the one-lined "original" list.
>> - const: int 2
> I don't understand what this "int 2" does.
space was a typo, obviously
> Why do we need a const here ?
Because you cannot have here int1. Order of items is fixed.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 46+ messages in thread
* RE: [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600
2025-08-19 14:28 ` Krzysztof Kozlowski
@ 2025-08-19 14:53 ` Remi Buisson
0 siblings, 0 replies; 46+ messages in thread
From: Remi Buisson @ 2025-08-19 14:53 UTC (permalink / raw)
To: Krzysztof Kozlowski, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org
>
>
>From: Krzysztof Kozlowski <krzk@kernel.org>
>Sent: Tuesday, August 19, 2025 4:29 PM
>To: Remi Buisson <Remi.Buisson@tdk.com>; Jonathan Cameron <jic23@kernel.org>; David Lechner <dlechner@baylibre.com>; Nuno Sá <nuno.sa@analog.com>; Andy Shevchenko <andy@kernel.org>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>
>Cc: linux-kernel@vger.kernel.org; linux-iio@vger.kernel.org; devicetree@vger.kernel.org
>Subject: Re: [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600
>On 19/08/2025 12:18, Remi Buisson wrote:
>>>> + interrupt-names:
>>>> + minItems: 1
>>>> + maxItems: 2
>>>> + items:
>>>> + enum:
>>>> + - INT1
>>>> + - INT2
>>>
>> Hello Krzysztof,
>> Could you please help me understand the proposed simplification?
>>> This can be simpler
>>>
>>> minItems: 1
>>> items:
>>> - enum: [ int1, int2 ]
>> This should be the one-lined "original" list.
>>> - const: int 2
>> I don't understand what this "int 2" does.
>
>space was a typo, obviously
>
>> Why do we need a const here ?
>
>Because you cannot have here int1. Order of items is fixed.
Thanks for your prompt reply.
I'll fix it in next patch version.
Remi
>
>
>
>Best regards,
>Krzysztof
>
^ permalink raw reply [flat|nested] 46+ messages in thread
* RE: [PATCH v2 3/8] iio: imu: inv_icm45600: add buffer support in iio devices
2025-08-16 11:17 ` Jonathan Cameron
@ 2025-08-20 13:34 ` Remi Buisson
0 siblings, 0 replies; 46+ messages in thread
From: Remi Buisson @ 2025-08-20 13:34 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Remi Buisson via B4 Relay, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org
>
>
>From: Jonathan Cameron <jic23@kernel.org>
>Sent: Saturday, August 16, 2025 1:17 PM
>To: Remi Buisson <Remi.Buisson@tdk.com>
>Cc: Remi Buisson via B4 Relay <devnull+remi.buisson.tdk.com@kernel.org>; David Lechner <dlechner@baylibre.com>; Nuno Sá <nuno.sa@analog.com>; Andy Shevchenko <andy@kernel.org>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; linux-kernel@vger.kernel.org; linux-iio@vger.kernel.org; devicetree@vger.kernel.org
>Subject: Re: [PATCH v2 3/8] iio: imu: inv_icm45600: add buffer support in iio devices
>
>On Mon, 11 Aug 2025 14: 13: 54 +0000 Remi Buisson <Remi. Buisson@ tdk. com> wrote: > > > > > >From: Jonathan Cameron <jic23@ kernel. org> > >Sent: Thursday, July 17, 2025 4: 34 PM > >To: Remi Buisson via B4
>On Mon, 11 Aug 2025 14:13:54 +0000
>Remi Buisson <Remi.Buisson@tdk.com> wrote:
>
>> >
>> >
>> >From: Jonathan Cameron <jic23@kernel.org>
>> >Sent: Thursday, July 17, 2025 4:34 PM
>> >To: Remi Buisson via B4 Relay <devnull+remi.buisson.tdk.com@kernel.org>
>> >Cc: Remi Buisson <Remi.Buisson@tdk.com>; David Lechner <dlechner@baylibre.com>; Nuno Sá <nuno.sa@analog.com>; Andy Shevchenko <andy@kernel.org>; Rob Herring <robh@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; linux-kernel@vger.kernel.org; linux-iio@vger.kernel.org; devicetree@vger.kernel.org
>> >Subject: Re: [PATCH v2 3/8] iio: imu: inv_icm45600: add buffer support in iio devices
>> >On Thu, 10 Jul 2025 08:57:58 +0000
>> >Remi Buisson via B4 Relay <devnull+remi.buisson.tdk.com@kernel.org> wrote:
>> >
>> >> From: Remi Buisson <remi.buisson@tdk.com>
>> >>
>> >> Add FIFO control functions.
>> >> Support hwfifo watermark by multiplexing gyro and accel settings.
>> >> Support hwfifo flush.
>> >>
>> >> Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
>> >Hi Remi,
>> >
>> >Sorry for delay - hectic week.
>> >
>> >Jonathan
>> No problem, thanks for the review ! (and sorry for my late reply)
>> >
>> >> ---
>> >> drivers/iio/imu/inv_icm45600/Makefile | 1 +
>> >> drivers/iio/imu/inv_icm45600/inv_icm45600.h | 4 +
>> >> drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c | 514 +++++++++++++++++++++
>> >> drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h | 99 ++++
>> >> drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 137 +++++-
>> >We used to do the buffer / core split a lot but it often ends up more trouble
>> >that it is worth and we no longer make buffer support a build time option (which was
>> >what motivated the separate files) Consider how much simplification you'd get by squashing them into
>> >one file.
>> I understand the point.
>> However merging files will allow to remove 5 lines at most,
>> while the length of the core file will increase a lot.
>> I'm not sure of the benefit in the end, but
>> please let me know if you really want me to proceed with the merge.
>
>It's only a combined 1.5k. That would be fine even if the savings are fairly small.
>
>It's not something I care that much about though.
>
>
I'd rather keep it this way then.
>
>
>
>> >> +const struct iio_buffer_setup_ops inv_icm45600_buffer_ops = {
>> >> + .preenable = inv_icm45600_buffer_preenable,
>> >> + .postenable = inv_icm45600_buffer_postenable,
>> >> + .predisable = inv_icm45600_buffer_predisable,
>> >> + .postdisable = inv_icm45600_buffer_postdisable,
>> >> +};
>> >> +
>> >> +int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st,
>> >> + unsigned int max)
>> >What is max here? Seems to be passed 0 in the only caller.
>> Function call with max > 0 is implemented later in the same patch
>> (in 4/8, from inv_icm45600_buffer_hwfifo_flush).
>
>Maybe push the parameter being introduced to ther.
I will do that.
>
>> >> +{
>> >> + const ssize_t packet_size = INV_ICM45600_FIFO_2SENSORS_PACKET_SIZE;
>> >> + __le16 *raw_fifo_count;
>> >> + size_t fifo_nb, i;
>> >> + ssize_t size;
>> >> + const struct inv_icm45600_fifo_sensor_data *accel, *gyro;
>> >> + const __le16 *timestamp;
>> >> + const s8 *temp;
>> >> + unsigned int odr;
>> >> + int ret;
>> >> +
>> >> + /* Reset all samples counters. */
>> >> + st->fifo.count = 0;
>> >> + st->fifo.nb.gyro = 0;
>> >> + st->fifo.nb.accel = 0;
>> >> + st->fifo.nb.total = 0;
>> >> +
>> >> + /* Read FIFO count value. */
>> >> + raw_fifo_count = &st->buffer.u16;
>> >> + ret = regmap_bulk_read(st->map, INV_ICM45600_REG_FIFO_COUNT,
>> >> + raw_fifo_count, sizeof(*raw_fifo_count));
>> >
>> >For IIO drivers at least we still operated under some guidance the regmap maintainer
>> >gave years ago to never assume regmap (for busses that otherwise require DMA safe
>> >buffers) will always bounce the data. So bulk reads with SPI buffers need
>> >DMA safe buffers. Easiest is usually an __aligned(IIO_DMA_MINALIGN) buffer
>> >(or set of buffers) at the end of st.
>> >
>> >In practice last time I checked regmap doesn't bother with the zero copy
>> >optimization that would need this safety so you won't actually hit this
>> >issue.
>> From my understanding, alignment of &st->buffer.u16 is correct because the union is aligned,
>>
>> union {
>> u8 buff[2];
>> __le16 u16;
>> } buffer __aligned(IIO_DMA_MINALIGN);
>>
>> Please let me know if my answer is not correct.
>Ah. I probably just missed that. can't remember!
No prob.
>
>> >
>
^ permalink raw reply [flat|nested] 46+ messages in thread
end of thread, other threads:[~2025-08-20 13:34 UTC | newest]
Thread overview: 46+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-10 8:57 [PATCH v2 0/8] iio: imu: new inv_icm45600 driver Remi Buisson via B4 Relay
2025-07-10 8:57 ` [PATCH v2 1/8] dt-bindings: iio: imu: Add inv_icm45600 Remi Buisson via B4 Relay
2025-07-10 22:41 ` Rob Herring
2025-07-11 11:40 ` Remi Buisson
2025-07-14 5:38 ` Krzysztof Kozlowski
2025-07-15 8:35 ` Remi Buisson
2025-07-15 12:16 ` Krzysztof Kozlowski
2025-07-16 14:31 ` Remi Buisson
2025-08-19 10:19 ` Remi Buisson
[not found] ` <FR2PPF4571F02BC2C08BFD80F57AC4F45AA8C30A@FR2PPF4571F02BC.DEUP281.PROD.OUTLOOK.COM>
2025-08-19 14:28 ` Krzysztof Kozlowski
2025-08-19 14:53 ` Remi Buisson
2025-07-10 8:57 ` [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver Remi Buisson via B4 Relay
2025-07-10 9:29 ` Andy Shevchenko
2025-07-11 11:39 ` Remi Buisson
[not found] ` <FR2PPF4571F02BC5366477EC02E9C44041A8C4BA@FR2PPF4571F02BC.DEUP281.PROD.OUTLOOK.COM>
2025-07-11 11:55 ` Andy Shevchenko
2025-07-15 9:11 ` Remi Buisson
2025-07-15 10:42 ` Andy Shevchenko
2025-07-15 15:26 ` Remi Buisson
2025-07-16 9:25 ` Andy Shevchenko
2025-07-11 2:58 ` kernel test robot
2025-07-13 16:00 ` Jonathan Cameron
2025-07-15 9:33 ` Remi Buisson
2025-07-10 8:57 ` [PATCH v2 3/8] iio: imu: inv_icm45600: add buffer support in iio devices Remi Buisson via B4 Relay
2025-07-17 14:33 ` Jonathan Cameron
2025-08-11 14:13 ` Remi Buisson
2025-08-16 11:17 ` Jonathan Cameron
2025-08-20 13:34 ` Remi Buisson
2025-07-10 8:57 ` [PATCH v2 4/8] iio: imu: inv_icm45600: add IMU IIO devices Remi Buisson via B4 Relay
2025-07-17 14:47 ` Jonathan Cameron
2025-08-11 14:56 ` Remi Buisson
2025-07-10 8:58 ` [PATCH v2 5/8] iio: imu: inv_icm45600: add I2C driver for inv_icm45600 driver Remi Buisson via B4 Relay
2025-07-11 7:24 ` kernel test robot
2025-07-14 20:21 ` Dan Carpenter
2025-07-15 8:09 ` Andy Shevchenko
2025-07-15 9:17 ` Remi Buisson
2025-07-10 8:58 ` [PATCH v2 6/8] iio: imu: inv_icm45600: add SPI " Remi Buisson via B4 Relay
2025-07-11 4:55 ` kernel test robot
2025-07-11 6:09 ` Andy Shevchenko
2025-07-15 9:18 ` Remi Buisson
2025-07-10 8:58 ` [PATCH v2 7/8] iio: imu: inv_icm45600: add I3C " Remi Buisson via B4 Relay
2025-07-14 12:56 ` Sean Nyekjaer
2025-07-15 8:48 ` Remi Buisson
2025-07-10 8:58 ` [PATCH v2 8/8] MAINTAINERS: add entry for inv_icm45600 6-axis imu sensor Remi Buisson via B4 Relay
2025-07-14 12:06 ` [PATCH v2 0/8] iio: imu: new inv_icm45600 driver Sean Nyekjaer
2025-07-15 9:03 ` Remi Buisson
2025-07-15 9:18 ` Sean Nyekjaer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).