* [PATCH V7 00/11] Add Invensense ICM42607
@ 2026-05-15 13:00 Chris Morgan
2026-05-15 13:00 ` [PATCH V7 01/11] dt-bindings: iio: imu: icm42600: Add mount-matrix to icm42600 Chris Morgan
` (10 more replies)
0 siblings, 11 replies; 26+ messages in thread
From: Chris Morgan @ 2026-05-15 13:00 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add support for the ICM42607 IMU. This sensor shares the same
functionality but a different register layout with the existing
ICM42600.
This driver should work with the ICM42607 and ICM42607P over both I2C
and SPI, however only the ICM42607P over I2C could be tested.
Changes Since V1:
- Instead of creating a new driver, merged with the existing inv_icm42600
driver. This necessitated adding some code to the existing driver to
permit using a different register layout for the same functionality.
- Split changes up a bit more to decrease the size of the individual
patches. Note that patch 0004 is still pretty hefty; if I need to split
further I may need to create some temporary stub functions.
- Used guard() and PM_RUNTIME_ACQUIRE_AUTOSUSPEND() on the new functions
per Jonathan's recommendations.
Changes Since V2:
- Went back to using a new driver on advice from Invensense engineer.
- Further split changes up into smaller chunks of functionality. Note
still that the largest patch is approximately 900 lines, and that while
the driver compiles cleanly at each commit it is not able to drive the
hardware until the commit that adds the Interrupt (as it also adds the
Makefile).
- Change the error to a warning when the devicetree binding does not match
the hardware ID.
- Dropped the ack on the devicetree bindings, as I am creating a new file
(for a new driver) instead of modifying the existing one.
Changes Since V3:
- Numerous small fixes (too many to list here). Thank you to everyone who
provided feedback.
- Split power management additions into an additional commit to break
things up further.
- Consolidated devicetree documentation in existing
invensense,icm42600.yaml file.
- Removed most of the FIELD_PREP from header file to c files to make code
easier to read.
- Changed scale values to 2D arrays for Gyro and Accelerometer.
- Removed IIO_CHAN_INFO_CALIBBIAS attribute.
Changes Since V4:
- Additional numerous small fixes, thank you again for all the feedback.
- Dropped power control API and instead run device in low noise mode.
- Split devicetree bindings into two distinct changes.
- Reordered adding of enums and structs to main header file so that they
are only brought in when needed.
- Stopped using enum for driver data and instead am using pointer to
device specific driver data.
Changes Since V5:
- Corrected use of "dev_warn_probe" to just "dev_warn".
- Fixed some return scenarios which would unconditionally return 0
when an error was present.
- Corrected use of max() to min() for bounds checking. max() was
incorrect.
- Fixed using "st->conf.accel.odr" in the gyroscope function. It
should have been "st->conf.gyro.odr" which it now is.
- Additional small fixes suggested by "sashiko.dev".
- Added a regmap cache. I used the datasheet to try and determine
which registers might change without explicit writes.
Changes Since V6:
- Corrected additional errors identified by sashiko.dev, mostly
fixing potential deadlocks, missing calls for pm runtime, and
potential overflow issues.
Chris Morgan (11):
dt-bindings: iio: imu: icm42600: Add mount-matrix to icm42600
dt-bindings: iio: imu: icm42600: Add icm42607 binding
iio: imu: inv_icm42607: Add inv_icm42607 Core Driver
iio: imu: inv_icm42607: Add I2C and SPI For icm42607
iio: imu: inv_icm42607: Add PM support for icm42607
iio: imu: inv_icm42607: Add Buffer support for icm42607
iio: imu: inv_icm42607: Add Temp Support in icm42607
iio: imu: inv_icm42607: Add Accelerometer for icm42607
iio: imu: inv_icm42607: Add Wake on Movement to icm42607
iio: imu: inv_icm42607: Add Gyroscope to icm42607
arm64: dts: rockchip: Add icm42607p IMU for RG-DS
.../bindings/iio/imu/invensense,icm42600.yaml | 4 +
.../dts/rockchip/rk3568-anbernic-rg-ds.dts | 20 +-
drivers/iio/imu/Kconfig | 1 +
drivers/iio/imu/Makefile | 1 +
drivers/iio/imu/inv_icm42607/Kconfig | 30 +
drivers/iio/imu/inv_icm42607/Makefile | 14 +
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 457 +++++++++
.../iio/imu/inv_icm42607/inv_icm42607_accel.c | 890 ++++++++++++++++++
.../imu/inv_icm42607/inv_icm42607_buffer.c | 565 +++++++++++
.../imu/inv_icm42607/inv_icm42607_buffer.h | 95 ++
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 863 +++++++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_gyro.c | 545 +++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_i2c.c | 90 ++
.../iio/imu/inv_icm42607/inv_icm42607_spi.c | 103 ++
.../iio/imu/inv_icm42607/inv_icm42607_temp.c | 80 ++
.../iio/imu/inv_icm42607/inv_icm42607_temp.h | 33 +
16 files changed, 3790 insertions(+), 1 deletion(-)
create mode 100644 drivers/iio/imu/inv_icm42607/Kconfig
create mode 100644 drivers/iio/imu/inv_icm42607/Makefile
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607.h
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h
--
2.43.0
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH V7 01/11] dt-bindings: iio: imu: icm42600: Add mount-matrix to icm42600
2026-05-15 13:00 [PATCH V7 00/11] Add Invensense ICM42607 Chris Morgan
@ 2026-05-15 13:00 ` Chris Morgan
2026-05-15 13:00 ` [PATCH V7 02/11] dt-bindings: iio: imu: icm42600: Add icm42607 binding Chris Morgan
` (9 subsequent siblings)
10 siblings, 0 replies; 26+ messages in thread
From: Chris Morgan @ 2026-05-15 13:00 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan, Krzysztof Kozlowski
From: Chris Morgan <macromorgan@hotmail.com>
Add mount-matrix attribute to schema. This attribute has been supported
since the first revision of this driver, but was not documented.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
.../devicetree/bindings/iio/imu/invensense,icm42600.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
index 119e28a833fd..9b2af104f186 100644
--- a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
+++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
@@ -53,6 +53,8 @@ properties:
drive-open-drain:
type: boolean
+ mount-matrix: true
+
vdd-supply:
description: Regulator that provides power to the sensor
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V7 02/11] dt-bindings: iio: imu: icm42600: Add icm42607 binding
2026-05-15 13:00 [PATCH V7 00/11] Add Invensense ICM42607 Chris Morgan
2026-05-15 13:00 ` [PATCH V7 01/11] dt-bindings: iio: imu: icm42600: Add mount-matrix to icm42600 Chris Morgan
@ 2026-05-15 13:00 ` Chris Morgan
2026-05-15 13:00 ` [PATCH V7 03/11] iio: imu: inv_icm42607: Add inv_icm42607 Core Driver Chris Morgan
` (8 subsequent siblings)
10 siblings, 0 replies; 26+ messages in thread
From: Chris Morgan @ 2026-05-15 13:00 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan, Krzysztof Kozlowski
From: Chris Morgan <macromorgan@hotmail.com>
Add devicetree binding for the Invensense ICM42607 and Invensense
ICM42607P inertial measurement unit. This unit is a combined
accelerometer, gyroscope, and thermometer available via I2C or SPI.
This device is functionally very similar to the icm42600 series with a
very different register layout.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
.../devicetree/bindings/iio/imu/invensense,icm42600.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
index 9b2af104f186..b69c6bbb6f6b 100644
--- a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
+++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
@@ -30,6 +30,8 @@ properties:
- invensense,icm42600
- invensense,icm42602
- invensense,icm42605
+ - invensense,icm42607
+ - invensense,icm42607p
- invensense,icm42622
- invensense,icm42631
- invensense,icm42686
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V7 03/11] iio: imu: inv_icm42607: Add inv_icm42607 Core Driver
2026-05-15 13:00 [PATCH V7 00/11] Add Invensense ICM42607 Chris Morgan
2026-05-15 13:00 ` [PATCH V7 01/11] dt-bindings: iio: imu: icm42600: Add mount-matrix to icm42600 Chris Morgan
2026-05-15 13:00 ` [PATCH V7 02/11] dt-bindings: iio: imu: icm42600: Add icm42607 binding Chris Morgan
@ 2026-05-15 13:00 ` Chris Morgan
2026-05-15 13:23 ` sashiko-bot
2026-05-15 18:31 ` Jonathan Cameron
2026-05-15 13:00 ` [PATCH V7 04/11] iio: imu: inv_icm42607: Add I2C and SPI For icm42607 Chris Morgan
` (7 subsequent siblings)
10 siblings, 2 replies; 26+ messages in thread
From: Chris Morgan @ 2026-05-15 13:00 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add the core component of a new inv_icm42607 driver. This includes
a few setup functions and the full register definition in the
header file.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 334 ++++++++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 207 +++++++++++
2 files changed, 541 insertions(+)
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607.h
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
new file mode 100644
index 000000000000..1916e0b08bca
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -0,0 +1,334 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#ifndef INV_ICM42607_H_
+#define INV_ICM42607_H_
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/iio/iio.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+enum inv_icm42607_sensor_mode {
+ INV_ICM42607_SENSOR_MODE_OFF,
+ INV_ICM42607_SENSOR_MODE_STANDBY,
+ INV_ICM42607_SENSOR_MODE_LOW_POWER,
+ INV_ICM42607_SENSOR_MODE_LOW_NOISE,
+ INV_ICM42607_SENSOR_MODE_NB
+};
+
+/* gyroscope fullscale values */
+enum inv_icm42607_gyro_fs {
+ INV_ICM42607_GYRO_FS_2000DPS,
+ INV_ICM42607_GYRO_FS_1000DPS,
+ INV_ICM42607_GYRO_FS_500DPS,
+ INV_ICM42607_GYRO_FS_250DPS,
+ INV_ICM42607_GYRO_FS_NB
+};
+
+/* accelerometer fullscale values */
+enum inv_icm42607_accel_fs {
+ INV_ICM42607_ACCEL_FS_16G,
+ INV_ICM42607_ACCEL_FS_8G,
+ INV_ICM42607_ACCEL_FS_4G,
+ INV_ICM42607_ACCEL_FS_2G,
+ INV_ICM42607_ACCEL_FS_NB
+};
+
+/* ODR values */
+enum inv_icm42607_odr {
+ INV_ICM42607_ODR_1600HZ = 5,
+ INV_ICM42607_ODR_800HZ,
+ INV_ICM42607_ODR_400HZ,
+ INV_ICM42607_ODR_200HZ,
+ INV_ICM42607_ODR_100HZ,
+ INV_ICM42607_ODR_50HZ,
+ INV_ICM42607_ODR_25HZ,
+ INV_ICM42607_ODR_12_5HZ,
+ INV_ICM42607_ODR_6_25HZ_LP,
+ INV_ICM42607_ODR_3_125HZ_LP,
+ INV_ICM42607_ODR_1_5625HZ_LP,
+ INV_ICM42607_ODR_NB
+};
+
+enum inv_icm42607_filter_bw {
+ /* Low-Noise mode sensor data filter (bandwidth) */
+ INV_ICM42607_FILTER_BYPASS,
+ INV_ICM42607_FILTER_BW_180HZ,
+ INV_ICM42607_FILTER_BW_121HZ,
+ INV_ICM42607_FILTER_BW_73HZ,
+ INV_ICM42607_FILTER_BW_53HZ,
+ INV_ICM42607_FILTER_BW_34HZ,
+ INV_ICM42607_FILTER_BW_25HZ,
+ INV_ICM42607_FILTER_BW_16HZ
+};
+
+struct inv_icm42607_sensor_conf {
+ int mode;
+ int fs;
+ int odr;
+ int filter;
+};
+
+struct inv_icm42607_conf {
+ struct inv_icm42607_sensor_conf gyro;
+ struct inv_icm42607_sensor_conf accel;
+ bool temp_en;
+};
+
+struct inv_icm42607_hw {
+ uint8_t whoami;
+ const char *name;
+ const struct inv_icm42607_conf *conf;
+};
+
+/**
+ * struct inv_icm42607_state - driver state variables
+ * @lock: lock for serializing multiple registers access.
+ * @hw: Hardware specific data.
+ * @map: regmap pointer.
+ * @vddio_supply: I/O voltage regulator for the chip.
+ * @irq: chip irq, required to enable/disable and set wakeup
+ * @orientation: sensor chip orientation relative to main hardware.
+ * @conf: chip sensors configurations.
+ */
+struct inv_icm42607_state {
+ struct mutex lock;
+ const struct inv_icm42607_hw *hw;
+ struct regmap *map;
+ struct regulator *vddio_supply;
+ int irq;
+ struct iio_mount_matrix orientation;
+ struct inv_icm42607_conf conf;
+};
+
+/* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */
+
+/* Register Map for User Bank 0 */
+#define INV_ICM42607_REG_MCLK_RDY 0x00
+
+#define INV_ICM42607_REG_DEVICE_CONFIG 0x01
+#define INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE BIT(2)
+#define INV_ICM42607_DEVICE_CONFIG_SPI_MODE BIT(0)
+
+#define INV_ICM42607_REG_SIGNAL_PATH_RESET 0x02
+#define INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET BIT(4)
+#define INV_ICM42607_SIGNAL_PATH_RESET_FIFO_FLUSH BIT(2)
+
+#define INV_ICM42607_REG_DRIVE_CONFIG1 0x03
+#define INV_ICM42607_DRIVE_CONFIG1_I3C_DDR_MASK GENMASK(5, 3)
+#define INV_ICM42607_DRIVE_CONFIG1_I3C_SDR_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_DRIVE_CONFIG2 0x04
+#define INV_ICM42607_DRIVE_CONFIG2_I2C_MASK GENMASK(5, 3)
+#define INV_ICM42607_DRIVE_CONFIG2_ALL_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_DRIVE_CONFIG3 0x05
+#define INV_ICM42607_DRIVE_CONFIG3_SPI_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_INT_CONFIG 0x06
+#define INV_ICM42607_INT_CONFIG_INT2_LATCHED BIT(5)
+#define INV_ICM42607_INT_CONFIG_INT2_PUSH_PULL BIT(4)
+#define INV_ICM42607_INT_CONFIG_INT2_ACTIVE_HIGH BIT(3)
+#define INV_ICM42607_INT_CONFIG_INT2_ACTIVE_LOW 0x00
+#define INV_ICM42607_INT_CONFIG_INT1_LATCHED BIT(2)
+#define INV_ICM42607_INT_CONFIG_INT1_PUSH_PULL BIT(1)
+#define INV_ICM42607_INT_CONFIG_INT1_ACTIVE_HIGH BIT(0)
+#define INV_ICM42607_INT_CONFIG_INT1_ACTIVE_LOW 0x00
+
+/* all sensor data are 16 bits (2 registers wide) in big-endian */
+#define INV_ICM42607_REG_TEMP_DATA1 0x09
+#define INV_ICM42607_REG_TEMP_DATA0 0x0A
+#define INV_ICM42607_REG_ACCEL_DATA_X1 0x0B
+#define INV_ICM42607_REG_ACCEL_DATA_X0 0x0C
+#define INV_ICM42607_REG_ACCEL_DATA_Y1 0x0D
+#define INV_ICM42607_REG_ACCEL_DATA_Y0 0x0E
+#define INV_ICM42607_REG_ACCEL_DATA_Z1 0x0F
+#define INV_ICM42607_REG_ACCEL_DATA_Z0 0x10
+#define INV_ICM42607_REG_GYRO_DATA_X1 0x11
+#define INV_ICM42607_REG_GYRO_DATA_X0 0x12
+#define INV_ICM42607_REG_GYRO_DATA_Y1 0x13
+#define INV_ICM42607_REG_GYRO_DATA_Y0 0x14
+#define INV_ICM42607_REG_GYRO_DATA_Z1 0x15
+#define INV_ICM42607_REG_GYRO_DATA_Z0 0x16
+#define INV_ICM42607_DATA_INVALID -32768
+
+#define INV_ICM42607_REG_TMST_FSYNCH 0x17
+#define INV_ICM42607_REG_TMST_FSYNCL 0x18
+
+/* APEX Data Registers */
+#define INV_ICM42607_REG_APEX_DATA0 0x31
+#define INV_ICM42607_REG_APEX_DATA1 0x32
+#define INV_ICM42607_REG_APEX_DATA2 0x33
+#define INV_ICM42607_REG_APEX_DATA3 0x34
+#define INV_ICM42607_REG_APEX_DATA4 0x1D
+#define INV_ICM42607_REG_APEX_DATA5 0x1E
+
+#define INV_ICM42607_REG_PWR_MGMT0 0x1F
+#define INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL BIT(7)
+#define INV_ICM42607_PWR_MGMT0_IDLE BIT(4)
+#define INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK GENMASK(3, 2)
+#define INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK GENMASK(1, 0)
+
+#define INV_ICM42607_REG_GYRO_CONFIG0 0x20
+#define INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK GENMASK(6, 5)
+#define INV_ICM42607_GYRO_CONFIG0_ODR_MASK GENMASK(3, 0)
+
+#define INV_ICM42607_REG_ACCEL_CONFIG0 0x21
+#define INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK GENMASK(6, 5)
+#define INV_ICM42607_ACCEL_CONFIG0_ODR_MASK GENMASK(3, 0)
+
+#define INV_ICM42607_REG_TEMP_CONFIG0 0x22
+#define INV_ICM42607_TEMP_CONFIG0_FILTER_MASK GENMASK(6, 4)
+
+#define INV_ICM42607_REG_GYRO_CONFIG1 0x23
+#define INV_ICM42607_GYRO_CONFIG1_FILTER_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_ACCEL_CONFIG1 0x24
+#define INV_ICM42607_ACCEL_CONFIG1_AVG_MASK GENMASK(6, 4)
+#define INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_APEX_CONFIG0 0x25
+#define INV_ICM42607_APEX_CONFIG0_DMP_POWER_SAVE_EN BIT(3)
+#define INV_ICM42607_APEX_CONFIG0_DMP_INIT_EN BIT(2)
+#define INV_ICM42607_APEX_CONFIG0_DMP_MEM_RESET_EN BIT(0)
+
+#define INV_ICM42607_REG_APEX_CONFIG1 0x26
+#define INV_ICM42607_APEX_CONFIG1_SMD_ENABLE BIT(6)
+#define INV_ICM42607_APEX_CONFIG1_FF_ENABLE BIT(5)
+#define INV_ICM42607_APEX_CONFIG1_TILT_ENABLE BIT(4)
+#define INV_ICM42607_APEX_CONFIG1_PED_ENABLE BIT(3)
+#define INV_ICM42607_APEX_CONFIG1_DMP_ODR_MASK GENMASK(1, 0)
+
+#define INV_ICM42607_REG_WOM_CONFIG 0x27
+#define INV_ICM42607_WOM_CONFIG_INT_DUR_MASK GENMASK(4, 3)
+#define INV_ICM42607_WOM_CONFIG_INT_MODE BIT(2)
+#define INV_ICM42607_WOM_CONFIG_MODE BIT(1)
+#define INV_ICM42607_WOM_CONFIG_EN BIT(0)
+
+#define INV_ICM42607_REG_FIFO_CONFIG1 0x28
+#define INV_ICM42607_FIFO_CONFIG1_MODE BIT(1)
+#define INV_ICM42607_FIFO_CONFIG1_BYPASS BIT(0)
+
+#define INV_ICM42607_REG_FIFO_CONFIG2 0x29
+#define INV_ICM42607_REG_FIFO_CONFIG3 0x2A
+#define INV_ICM42607_FIFO_WATERMARK_VAL(_wm) \
+ cpu_to_le16((_wm) & GENMASK(11, 0))
+/* FIFO is 2048 bytes, let 12 samples for reading latency */
+#define INV_ICM42607_FIFO_WATERMARK_MAX (2048 - 12 * 16)
+#define INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE 8
+#define INV_ICM42607_FIFO_2SENSORS_PACKET_SIZE 16
+
+#define INV_ICM42607_REG_INT_SOURCE0 0x2B
+#define INV_ICM42607_INT_SOURCE0_ST_INT1_EN BIT(7)
+#define INV_ICM42607_INT_SOURCE0_FSYNC_INT1_EN BIT(6)
+#define INV_ICM42607_INT_SOURCE0_PLL_RDY_INT1_EN BIT(5)
+#define INV_ICM42607_INT_SOURCE0_RESET_DONE_INT1_EN BIT(4)
+#define INV_ICM42607_INT_SOURCE0_DRDY_INT1_EN BIT(3)
+#define INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN BIT(2)
+#define INV_ICM42607_INT_SOURCE0_FIFO_FULL_INT1_EN BIT(1)
+#define INV_ICM42607_INT_SOURCE0_AGC_RDY_INT1_EN BIT(0)
+
+#define INV_ICM42607_REG_INT_SOURCE1 0x2C
+#define INV_ICM42607_INT_SOURCE1_I3C_ERROR_INT1_EN BIT(6)
+#define INV_ICM42607_INT_SOURCE1_SMD_INT1_EN BIT(3)
+#define INV_ICM42607_INT_SOURCE1_WOM_INT1_EN GENMASK(2, 0)
+
+#define INV_ICM42607_REG_INT_SOURCE3 0x2D
+#define INV_ICM42607_INT_SOURCE3_ST_INT2_EN BIT(7)
+#define INV_ICM42607_INT_SOURCE3_FSYNC_INT2_EN BIT(6)
+#define INV_ICM42607_INT_SOURCE3_PLL_RDY_INT2_EN BIT(5)
+#define INV_ICM42607_INT_SOURCE3_RESET_DONE_INT2_EN BIT(4)
+#define INV_ICM42607_INT_SOURCE3_DRDY_INT2_EN BIT(3)
+#define INV_ICM42607_INT_SOURCE3_FIFO_THS_INT2_EN BIT(2)
+#define INV_ICM42607_INT_SOURCE3_FIFO_FULL_INT2_EN BIT(1)
+#define INV_ICM42607_INT_SOURCE3_AGC_RDY_INT2_EN BIT(0)
+
+#define INV_ICM42607_REG_INT_SOURCE4 0x2E
+#define INV_ICM42607_INT_SOURCE4_I3C_ERROR_INT2_EN BIT(6)
+#define INV_ICM42607_INT_SOURCE4_SMD_INT2_EN BIT(3)
+#define INV_ICM42607_INT_SOURCE4_WOM_Z_INT2_EN BIT(2)
+#define INV_ICM42607_INT_SOURCE4_WOM_Y_INT2_EN BIT(1)
+#define INV_ICM42607_INT_SOURCE4_WOM_X_INT2_EN BIT(0)
+
+#define INV_ICM42607_REG_FIFO_LOST_PKT0 0x2F
+#define INV_ICM42607_REG_FIFO_LOST_PKT1 0x30
+
+#define INV_ICM42607_REG_INTF_CONFIG0 0x35
+#define INV_ICM42607_INTF_CONFIG0_FIFO_COUNT_FORMAT BIT(6)
+#define INV_ICM42607_INTF_CONFIG0_FIFO_COUNT_ENDIAN BIT(5)
+#define INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN BIT(4)
+#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK GENMASK(1, 0)
+#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_SPI_DIS \
+ FIELD_PREP(INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK, 2)
+#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_I2C_DIS \
+ FIELD_PREP(INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK, 3)
+
+#define INV_ICM42607_REG_INTF_CONFIG1 0x36
+#define INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN BIT(3)
+#define INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN BIT(2)
+#define INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK GENMASK(1, 0)
+#define INV_ICM42607_INTF_CONFIG1_CLKSEL_INT 0
+#define INV_ICM42607_INTF_CONFIG1_CLKSEL_PLL 1
+#define INV_ICM42607_INTF_CONFIG1_CLKSEL_OFF 2
+
+#define INV_ICM42607_REG_INT_STATUS_DRDY 0x39
+#define INV_ICM42607_INT_STATUS_DRDY_DATA_RDY BIT(0)
+
+#define INV_ICM42607_REG_INT_STATUS 0x3A
+#define INV_ICM42607_INT_STATUS_ST BIT(7)
+#define INV_ICM42607_INT_STATUS_FSYNC BIT(6)
+#define INV_ICM42607_INT_STATUS_PLL_RDY BIT(5)
+#define INV_ICM42607_INT_STATUS_RESET_DONE BIT(4)
+#define INV_ICM42607_INT_STATUS_FIFO_THS BIT(2)
+#define INV_ICM42607_INT_STATUS_FIFO_FULL BIT(1)
+#define INV_ICM42607_INT_STATUS_AGC_RDY BIT(0)
+
+#define INV_ICM42607_REG_INT_STATUS2 0x3B
+#define INV_ICM42607_INT_STATUS2_SMD BIT(3)
+#define INV_ICM42607_INT_STATUS2_WOM_INT GENMASK(2, 0)
+
+#define INV_ICM42607_REG_INT_STATUS3 0x3C
+#define INV_ICM42607_INT_STATUS3_STEP_DET BIT(5)
+#define INV_ICM42607_INT_STATUS3_STEP_CNT_OVF BIT(4)
+#define INV_ICM42607_INT_STATUS3_TILT_DET BIT(3)
+#define INV_ICM42607_INT_STATUS3_FF_DET BIT(2)
+
+/*
+ * FIFO access registers
+ * FIFO count is 16 bits (2 registers) big-endian
+ * FIFO data is a continuous read register to read FIFO content
+ */
+#define INV_ICM42607_REG_FIFO_COUNTH 0x3D
+#define INV_ICM42607_REG_FIFO_COUNTL 0x3E
+#define INV_ICM42607_REG_FIFO_DATA 0x3F
+
+#define INV_ICM42607_REG_ACCEL_WOM_X_THR 0x4b
+#define INV_ICM42607_REG_ACCEL_WOM_Y_THR 0x4c
+#define INV_ICM42607_REG_ACCEL_WOM_Z_THR 0x4d
+
+#define INV_ICM42607_REG_WHOAMI 0x75
+#define INV_ICM42607P_WHOAMI 0x60
+#define INV_ICM42607_WHOAMI 0x67
+
+/* Sleep times required by the driver */
+#define INV_ICM42607_POWER_UP_TIME_US 100000
+#define INV_ICM42607_RESET_TIME_MS 1
+#define INV_ICM42607_ACCEL_STARTUP_TIME_MS 20
+#define INV_ICM42607_GYRO_STARTUP_TIME_MS 60
+#define INV_ICM42607_GYRO_STOP_TIME_MS 150
+#define INV_ICM42607_TEMP_STARTUP_TIME_MS 14
+#define INV_ICM42607_SUSPEND_DELAY_MS 2000
+
+typedef int (*inv_icm42607_bus_setup)(struct inv_icm42607_state *);
+
+extern const struct regmap_config inv_icm42607_regmap_config;
+
+int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw *hw,
+ inv_icm42607_bus_setup bus_setup);
+
+#endif
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
new file mode 100644
index 000000000000..9784709319b9
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/iio/iio.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include "inv_icm42607.h"
+
+static int inv_icm42607_set_conf(struct inv_icm42607_state *st,
+ const struct inv_icm42607_conf *conf)
+{
+ unsigned int val;
+ int ret;
+
+ val = FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK,
+ conf->gyro.mode);
+ val |= FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK,
+ conf->accel.mode);
+ /*
+ * No temperature enable reg in datasheet, but BSP driver
+ * selected RC oscillator clock in LP mode when temperature
+ * was disabled.
+ */
+ if (!conf->temp_en)
+ val |= INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL;
+ ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK,
+ conf->gyro.fs);
+ val |= FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_ODR_MASK,
+ conf->gyro.odr);
+ ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK, conf->accel.fs);
+ val |= FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_ODR_MASK, conf->accel.odr);
+ ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG1_FILTER_MASK,
+ conf->gyro.filter);
+ ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG1, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK,
+ conf->accel.filter);
+ ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG1, val);
+ if (ret)
+ return ret;
+
+ st->conf = *conf;
+
+ return 0;
+}
+
+/**
+ * inv_icm42607_setup() - check and setup chip
+ * @st: driver internal state
+ * @bus_setup: callback for setting up bus specific registers
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+static int inv_icm42607_setup(struct inv_icm42607_state *st,
+ inv_icm42607_bus_setup bus_setup)
+{
+ const struct device *dev = regmap_get_device(st->map);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(st->map, INV_ICM42607_REG_WHOAMI, &val);
+ if (ret)
+ return ret;
+
+ if (val != st->hw->whoami)
+ dev_warn(dev, "invalid whoami %#02x expected %#02x (%s)\n",
+ val, st->hw->whoami, st->hw->name);
+
+ ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET,
+ INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET);
+ if (ret)
+ return ret;
+
+ fsleep(1000);
+
+ ret = regmap_read_poll_timeout(st->map, INV_ICM42607_REG_INT_STATUS,
+ val, val & INV_ICM42607_INT_STATUS_RESET_DONE,
+ INV_ICM42607_RESET_TIME_MS * 100,
+ INV_ICM42607_RESET_TIME_MS * 1000);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "reset error, reset done bit not set\n");
+
+ ret = bus_setup(st);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
+ INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1,
+ INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK,
+ INV_ICM42607_INTF_CONFIG1_CLKSEL_PLL);
+ if (ret)
+ return ret;
+
+ return inv_icm42607_set_conf(st, st->hw->conf);
+}
+
+static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st)
+{
+ int ret;
+
+ ret = regulator_enable(st->vddio_supply);
+ if (ret)
+ return ret;
+
+ fsleep(INV_ICM42607_POWER_UP_TIME_US);
+
+ return 0;
+}
+
+static void inv_icm42607_disable_vddio_reg(void *_data)
+{
+ struct inv_icm42607_state *st = _data;
+
+ regulator_disable(st->vddio_supply);
+}
+
+int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw *hw,
+ inv_icm42607_bus_setup bus_setup)
+{
+ struct device *dev = regmap_get_device(regmap);
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
+ struct inv_icm42607_state *st;
+ int irq;
+ int ret;
+
+ irq = fwnode_irq_get_byname(fwnode, "INT1");
+ if (!(irq > 0))
+ return dev_err_probe(dev, -EINVAL, "Unable to get INT1 interrupt\n");
+
+ st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, st);
+
+ ret = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
+ st->hw = hw;
+ st->map = regmap;
+ st->irq = irq;
+
+ ret = iio_read_mount_matrix(dev, &st->orientation);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to retrieve mounting matrix %d\n", ret);
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to get vdd regulator\n");
+
+ st->vddio_supply = devm_regulator_get(dev, "vddio");
+ if (IS_ERR(st->vddio_supply))
+ return dev_err_probe(dev, PTR_ERR(st->vddio_supply),
+ "Failed to get vddio regulator\n");
+
+ ret = inv_icm42607_enable_vddio_reg(st);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, inv_icm42607_disable_vddio_reg, st);
+ if (ret)
+ return ret;
+
+ /* Setup chip registers (includes WHOAMI check, reset check, bus setup) */
+ ret = inv_icm42607_setup(st, bus_setup);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607");
+
+MODULE_AUTHOR("InvenSense, Inc.");
+MODULE_DESCRIPTION("InvenSense ICM-42607x device driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_INV_SENSORS_TIMESTAMP");
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V7 04/11] iio: imu: inv_icm42607: Add I2C and SPI For icm42607
2026-05-15 13:00 [PATCH V7 00/11] Add Invensense ICM42607 Chris Morgan
` (2 preceding siblings ...)
2026-05-15 13:00 ` [PATCH V7 03/11] iio: imu: inv_icm42607: Add inv_icm42607 Core Driver Chris Morgan
@ 2026-05-15 13:00 ` Chris Morgan
2026-05-15 13:45 ` sashiko-bot
2026-05-15 18:43 ` Jonathan Cameron
2026-05-15 13:00 ` [PATCH V7 05/11] iio: imu: inv_icm42607: Add PM support for icm42607 Chris Morgan
` (6 subsequent siblings)
10 siblings, 2 replies; 26+ messages in thread
From: Chris Morgan @ 2026-05-15 13:00 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add I2C and SPI driver support for InvenSense ICM-42607 devices.
Add necessary Kconfig and Makefile to allow building of (incomplete)
driver.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/Kconfig | 1 +
drivers/iio/imu/Makefile | 1 +
drivers/iio/imu/inv_icm42607/Kconfig | 30 ++++++
drivers/iio/imu/inv_icm42607/Makefile | 10 ++
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 12 +++
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 88 +++++++++++++--
.../iio/imu/inv_icm42607/inv_icm42607_i2c.c | 89 +++++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_spi.c | 102 ++++++++++++++++++
8 files changed, 325 insertions(+), 8 deletions(-)
create mode 100644 drivers/iio/imu/inv_icm42607/Kconfig
create mode 100644 drivers/iio/imu/inv_icm42607/Makefile
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
index 7e0181c27bb6..8bab4616be20 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_icm42607/Kconfig"
source "drivers/iio/imu/inv_icm45600/Kconfig"
source "drivers/iio/imu/inv_mpu6050/Kconfig"
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
index 13fb7846e9c9..3268dc2371ae 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_icm42607/
obj-y += inv_icm45600/
obj-y += inv_mpu6050/
diff --git a/drivers/iio/imu/inv_icm42607/Kconfig b/drivers/iio/imu/inv_icm42607/Kconfig
new file mode 100644
index 000000000000..7ba64e6e8d80
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/Kconfig
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+config INV_ICM42607
+ tristate
+ select IIO_BUFFER
+ select IIO_INV_SENSORS_TIMESTAMP
+
+config INV_ICM42607_I2C
+ tristate "InvenSense ICM-42607X I2C driver"
+ depends on I2C
+ select INV_ICM42607
+ select REGMAP_I2C
+ help
+ This driver supports the InvenSense ICM-42607 motion tracking
+ device over I2C.
+
+ This driver can be built as a module. The module will be called
+ inv-icm42607-i2c.
+
+config INV_ICM42607_SPI
+ tristate "InvenSense ICM-42607X SPI driver"
+ depends on SPI_MASTER
+ select INV_ICM42607
+ select REGMAP_SPI
+ help
+ This driver supports the InvenSense ICM-42607 motion tracking
+ device over SPI.
+
+ This driver can be built as a module. The module will be called
+ inv-icm42607-spi.
diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile
new file mode 100644
index 000000000000..be109102e203
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o
+inv-icm42607-y += inv_icm42607_core.o
+
+obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o
+inv-icm42607-i2c-y += inv_icm42607_i2c.o
+
+obj-$(CONFIG_INV_ICM42607_SPI) += inv-icm42607-spi.o
+inv-icm42607-spi-y += inv_icm42607_spi.o
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
index 1916e0b08bca..2c20e95b237a 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -13,6 +13,16 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+/* serial bus slew rates */
+enum inv_icm42607_slew_rate {
+ INV_ICM42607_SLEW_RATE_20_60NS,
+ INV_ICM42607_SLEW_RATE_12_36NS,
+ INV_ICM42607_SLEW_RATE_6_18NS,
+ INV_ICM42607_SLEW_RATE_4_12NS,
+ INV_ICM42607_SLEW_RATE_2_6NS,
+ INV_ICM42607_SLEW_RATE_INF_2NS
+};
+
enum inv_icm42607_sensor_mode {
INV_ICM42607_SENSOR_MODE_OFF,
INV_ICM42607_SENSOR_MODE_STANDBY,
@@ -327,6 +337,8 @@ struct inv_icm42607_state {
typedef int (*inv_icm42607_bus_setup)(struct inv_icm42607_state *);
extern const struct regmap_config inv_icm42607_regmap_config;
+extern const struct inv_icm42607_hw inv_icm42607_hw_data;
+extern const struct inv_icm42607_hw inv_icm42607p_hw_data;
int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw *hw,
inv_icm42607_bus_setup bus_setup);
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
index 9784709319b9..1088c5c7076f 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -15,6 +15,63 @@
#include "inv_icm42607.h"
+static bool inv_icm42607_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case INV_ICM42607_REG_MCLK_RDY:
+ case INV_ICM42607_REG_SIGNAL_PATH_RESET:
+ case INV_ICM42607_REG_TEMP_DATA1 ... INV_ICM42607_REG_APEX_DATA5:
+ case INV_ICM42607_REG_APEX_CONFIG0:
+ case INV_ICM42607_REG_FIFO_CONFIG2 ... INV_ICM42607_REG_FIFO_CONFIG3:
+ case INV_ICM42607_REG_FIFO_LOST_PKT0 ... INV_ICM42607_REG_APEX_DATA3:
+ case INV_ICM42607_REG_INT_STATUS_DRDY:
+ case INV_ICM42607_REG_INT_STATUS ... INV_ICM42607_REG_FIFO_DATA:
+ return true;
+ }
+
+ return false;
+}
+
+const struct regmap_config inv_icm42607_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = INV_ICM42607_REG_WHOAMI,
+ .cache_type = REGCACHE_MAPLE,
+ .volatile_reg = inv_icm42607_is_volatile_reg,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm42607_regmap_config, "IIO_ICM42607");
+
+/* chip initial default configuration */
+static const struct inv_icm42607_conf inv_icm42607_default_conf = {
+ .gyro = {
+ .mode = INV_ICM42607_SENSOR_MODE_OFF,
+ .fs = INV_ICM42607_GYRO_FS_1000DPS,
+ .odr = INV_ICM42607_ODR_100HZ,
+ .filter = INV_ICM42607_FILTER_BW_25HZ,
+ },
+ .accel = {
+ .mode = INV_ICM42607_SENSOR_MODE_OFF,
+ .fs = INV_ICM42607_ACCEL_FS_4G,
+ .odr = INV_ICM42607_ODR_100HZ,
+ .filter = INV_ICM42607_FILTER_BW_25HZ,
+ },
+ .temp_en = false,
+};
+
+const struct inv_icm42607_hw inv_icm42607_hw_data = {
+ .whoami = INV_ICM42607_WHOAMI,
+ .name = "icm42607",
+ .conf = &inv_icm42607_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm42607_hw_data, "IIO_ICM42607");
+
+const struct inv_icm42607_hw inv_icm42607p_hw_data = {
+ .whoami = INV_ICM42607P_WHOAMI,
+ .name = "icm42607p",
+ .conf = &inv_icm42607_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607");
+
static int inv_icm42607_set_conf(struct inv_icm42607_state *st,
const struct inv_icm42607_conf *conf)
{
@@ -81,6 +138,14 @@ static int inv_icm42607_setup(struct inv_icm42607_state *st,
unsigned int val;
int ret;
+ /*
+ * Setup the bus first in case we need to set the SPI mode or
+ * change the slew rate in order.
+ */
+ ret = bus_setup(st);
+ if (ret)
+ return ret;
+
ret = regmap_read(st->map, INV_ICM42607_REG_WHOAMI, &val);
if (ret)
return ret;
@@ -94,20 +159,27 @@ static int inv_icm42607_setup(struct inv_icm42607_state *st,
if (ret)
return ret;
- fsleep(1000);
+ /*
+ * In order to confirm a reset is complete, we need to read the reset
+ * bit, but in certain circumstances we need to set the bus up before
+ * we can do a read. So we should wait the required amount of time
+ * per the datasheet first, then set the bus up again, then read to
+ * ensure the reset status is done. Invalidate the regmap cache since
+ * we're doing a hardware reset.
+ */
+ regcache_mark_dirty(st->map);
- ret = regmap_read_poll_timeout(st->map, INV_ICM42607_REG_INT_STATUS,
- val, val & INV_ICM42607_INT_STATUS_RESET_DONE,
- INV_ICM42607_RESET_TIME_MS * 100,
- INV_ICM42607_RESET_TIME_MS * 1000);
- if (ret)
- return dev_err_probe(dev, ret,
- "reset error, reset done bit not set\n");
+ fsleep(INV_ICM42607_RESET_TIME_MS * 1000);
ret = bus_setup(st);
if (ret)
return ret;
+ ret = regmap_read(st->map, INV_ICM42607_REG_INT_STATUS, &val);
+ if (ret || (!(val & INV_ICM42607_INT_STATUS_RESET_DONE)))
+ return dev_err_probe(dev, -EIO,
+ "reset error, reset done bit not set\n");
+
ret = regmap_set_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN);
if (ret)
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
new file mode 100644
index 000000000000..8631579a609a
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+#include "inv_icm42607.h"
+
+static int inv_icm42607_i2c_bus_setup(struct inv_icm42607_state *st)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_clear_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1,
+ INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN |
+ INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_DRIVE_CONFIG2_I2C_MASK,
+ INV_ICM42607_SLEW_RATE_12_36NS);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_DRIVE_CONFIG2,
+ INV_ICM42607_DRIVE_CONFIG2_I2C_MASK, val);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
+ INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK,
+ INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_SPI_DIS);
+}
+
+static int inv_icm42607_probe(struct i2c_client *client)
+{
+ const struct inv_icm42607_hw *hw;
+ struct regmap *regmap;
+
+ hw = i2c_get_match_data(client);
+ if (!hw)
+ return dev_err_probe(&client->dev, -ENODEV,
+ "Failed to get i2c data\n");
+
+ regmap = devm_regmap_init_i2c(client, &inv_icm42607_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(regmap),
+ "Failed to register i2c regmap %ld\n",
+ PTR_ERR(regmap));
+
+ return inv_icm42607_core_probe(regmap, hw, inv_icm42607_i2c_bus_setup);
+}
+
+static const struct i2c_device_id inv_icm42607_id[] = {
+ { "icm42607", (kernel_ulong_t)&inv_icm42607_hw_data },
+ { "icm42607p", (kernel_ulong_t)&inv_icm42607p_hw_data },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, inv_icm42607_id);
+
+static const struct of_device_id inv_icm42607_of_matches[] = {
+ {
+ .compatible = "invensense,icm42607",
+ .data = &inv_icm42607_hw_data,
+ }, {
+ .compatible = "invensense,icm42607p",
+ .data = &inv_icm42607p_hw_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, inv_icm42607_of_matches);
+
+static struct i2c_driver inv_icm42607_driver = {
+ .driver = {
+ .name = "inv-icm42607-i2c",
+ .of_match_table = inv_icm42607_of_matches,
+ },
+ .id_table = inv_icm42607_id,
+ .probe = inv_icm42607_probe,
+};
+module_i2c_driver(inv_icm42607_driver);
+
+MODULE_AUTHOR("InvenSense, Inc.");
+MODULE_DESCRIPTION("InvenSense ICM-42607x I2C driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_ICM42607");
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
new file mode 100644
index 000000000000..49438fa6f867
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "inv_icm42607.h"
+
+static int inv_icm42607_spi_bus_setup(struct inv_icm42607_state *st)
+{
+ const struct device *dev = regmap_get_device(st->map);
+ const struct spi_device *spi = to_spi_device(dev);
+ unsigned int val;
+ int ret;
+
+ if (spi->mode & SPI_3WIRE)
+ ret = regmap_clear_bits(st->map, INV_ICM42607_REG_DEVICE_CONFIG,
+ INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE);
+ else
+ ret = regmap_set_bits(st->map, INV_ICM42607_REG_DEVICE_CONFIG,
+ INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE);
+ if (ret)
+ return ret;
+
+ ret = regmap_clear_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1,
+ INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN |
+ INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_DRIVE_CONFIG3_SPI_MASK,
+ INV_ICM42607_SLEW_RATE_INF_2NS);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_DRIVE_CONFIG3,
+ INV_ICM42607_DRIVE_CONFIG3_SPI_MASK, val);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
+ INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK,
+ INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_I2C_DIS);
+}
+
+static int inv_icm42607_probe(struct spi_device *spi)
+{
+ const struct inv_icm42607_hw *hw;
+ struct regmap *regmap;
+
+ hw = spi_get_device_match_data(spi);
+ if (!hw)
+ return dev_err_probe(&spi->dev, -ENODEV,
+ "Failed to get SPI data\n");
+
+ regmap = devm_regmap_init_spi(spi, &inv_icm42607_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&spi->dev, PTR_ERR(regmap),
+ "Failed to register spi regmap %ld\n",
+ PTR_ERR(regmap));
+
+ return inv_icm42607_core_probe(regmap, hw,
+ inv_icm42607_spi_bus_setup);
+}
+
+static const struct of_device_id inv_icm42607_of_matches[] = {
+ {
+ .compatible = "invensense,icm42607",
+ .data = &inv_icm42607_hw_data,
+ },
+ {
+ .compatible = "invensense,icm42607p",
+ .data = &inv_icm42607p_hw_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, inv_icm42607_of_matches);
+
+static const struct spi_device_id inv_icm42607_spi_id_table[] = {
+ { "icm42607", (kernel_ulong_t)&inv_icm42607_hw_data },
+ { "icm42607p", (kernel_ulong_t)&inv_icm42607p_hw_data },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, inv_icm42607_spi_id_table);
+
+static struct spi_driver inv_icm42607_driver = {
+ .driver = {
+ .name = "inv-icm42607-spi",
+ .of_match_table = inv_icm42607_of_matches,
+ },
+ .id_table = inv_icm42607_spi_id_table,
+ .probe = inv_icm42607_probe,
+};
+module_spi_driver(inv_icm42607_driver);
+
+MODULE_AUTHOR("InvenSense, Inc.");
+MODULE_DESCRIPTION("InvenSense ICM-42607x SPI driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_ICM42607");
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V7 05/11] iio: imu: inv_icm42607: Add PM support for icm42607
2026-05-15 13:00 [PATCH V7 00/11] Add Invensense ICM42607 Chris Morgan
` (3 preceding siblings ...)
2026-05-15 13:00 ` [PATCH V7 04/11] iio: imu: inv_icm42607: Add I2C and SPI For icm42607 Chris Morgan
@ 2026-05-15 13:00 ` Chris Morgan
2026-05-15 13:36 ` sashiko-bot
2026-05-15 18:59 ` Jonathan Cameron
2026-05-15 13:00 ` [PATCH V7 06/11] iio: imu: inv_icm42607: Add Buffer " Chris Morgan
` (5 subsequent siblings)
10 siblings, 2 replies; 26+ messages in thread
From: Chris Morgan @ 2026-05-15 13:00 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add power management support for the ICM42607 device driver.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 12 ++
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 143 ++++++++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_i2c.c | 1 +
.../iio/imu/inv_icm42607/inv_icm42607_spi.c | 1 +
4 files changed, 157 insertions(+)
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
index 2c20e95b237a..5f37999e39a5 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -10,6 +10,7 @@
#include <linux/bitops.h>
#include <linux/iio/iio.h>
#include <linux/mutex.h>
+#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -96,24 +97,34 @@ struct inv_icm42607_hw {
const struct inv_icm42607_conf *conf;
};
+struct inv_icm42607_suspended {
+ enum inv_icm42607_sensor_mode gyro;
+ enum inv_icm42607_sensor_mode accel;
+ bool temp;
+};
+
/**
* struct inv_icm42607_state - driver state variables
* @lock: lock for serializing multiple registers access.
* @hw: Hardware specific data.
* @map: regmap pointer.
* @vddio_supply: I/O voltage regulator for the chip.
+ * @vddio_en: I/O voltage status for runtime PM.
* @irq: chip irq, required to enable/disable and set wakeup
* @orientation: sensor chip orientation relative to main hardware.
* @conf: chip sensors configurations.
+ * @suspended: suspended sensors configuration.
*/
struct inv_icm42607_state {
struct mutex lock;
const struct inv_icm42607_hw *hw;
struct regmap *map;
struct regulator *vddio_supply;
+ bool vddio_en;
int irq;
struct iio_mount_matrix orientation;
struct inv_icm42607_conf conf;
+ struct inv_icm42607_suspended suspended;
};
/* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */
@@ -339,6 +350,7 @@ typedef int (*inv_icm42607_bus_setup)(struct inv_icm42607_state *);
extern const struct regmap_config inv_icm42607_regmap_config;
extern const struct inv_icm42607_hw inv_icm42607_hw_data;
extern const struct inv_icm42607_hw inv_icm42607p_hw_data;
+extern const struct dev_pm_ops inv_icm42607_pm_ops;
int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw *hw,
inv_icm42607_bus_setup bus_setup);
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
index 1088c5c7076f..e27ad0319a12 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -9,6 +9,7 @@
#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>
@@ -72,6 +73,62 @@ const struct inv_icm42607_hw inv_icm42607p_hw_data = {
};
EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607");
+static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st,
+ enum inv_icm42607_sensor_mode gyro,
+ enum inv_icm42607_sensor_mode accel,
+ bool temp, unsigned int *sleep_ms)
+{
+ enum inv_icm42607_sensor_mode oldgyro = st->conf.gyro.mode;
+ enum inv_icm42607_sensor_mode oldaccel = st->conf.accel.mode;
+ bool oldtemp = st->conf.temp_en;
+ unsigned int sleepval;
+ unsigned int val;
+ int ret;
+
+ if (gyro == oldgyro && accel == oldaccel && temp == oldtemp)
+ return 0;
+
+ val = FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, gyro);
+ val |= FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, accel);
+ if (!temp)
+ val |= INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL;
+ ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val);
+ if (ret)
+ return ret;
+
+ st->conf.gyro.mode = gyro;
+ st->conf.accel.mode = accel;
+ st->conf.temp_en = temp;
+
+ sleepval = 0;
+ if (temp && !oldtemp) {
+ if (sleepval < INV_ICM42607_TEMP_STARTUP_TIME_MS)
+ sleepval = INV_ICM42607_TEMP_STARTUP_TIME_MS;
+ }
+ if (accel != oldaccel && oldaccel == INV_ICM42607_SENSOR_MODE_OFF) {
+ usleep_range(200, 300);
+ if (sleepval < INV_ICM42607_ACCEL_STARTUP_TIME_MS)
+ sleepval = INV_ICM42607_ACCEL_STARTUP_TIME_MS;
+ }
+ if (gyro != oldgyro) {
+ if (oldgyro == INV_ICM42607_SENSOR_MODE_OFF) {
+ usleep_range(200, 300);
+ if (sleepval < INV_ICM42607_GYRO_STARTUP_TIME_MS)
+ sleepval = INV_ICM42607_GYRO_STARTUP_TIME_MS;
+ } else if (gyro == INV_ICM42607_SENSOR_MODE_OFF) {
+ if (sleepval < INV_ICM42607_GYRO_STOP_TIME_MS)
+ sleepval = INV_ICM42607_GYRO_STOP_TIME_MS;
+ }
+ }
+
+ if (sleep_ms)
+ *sleep_ms = sleepval;
+ else if (sleepval)
+ msleep(sleepval);
+
+ return 0;
+}
+
static int inv_icm42607_set_conf(struct inv_icm42607_state *st,
const struct inv_icm42607_conf *conf)
{
@@ -198,11 +255,15 @@ static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st)
{
int ret;
+ if (st->vddio_en)
+ return 0;
+
ret = regulator_enable(st->vddio_supply);
if (ret)
return ret;
fsleep(INV_ICM42607_POWER_UP_TIME_US);
+ st->vddio_en = 1;
return 0;
}
@@ -211,7 +272,10 @@ static void inv_icm42607_disable_vddio_reg(void *_data)
{
struct inv_icm42607_state *st = _data;
+ if (!st->vddio_en)
+ return;
regulator_disable(st->vddio_supply);
+ st->vddio_en = 0;
}
int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw *hw,
@@ -269,10 +333,89 @@ int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw
if (ret)
return ret;
+ ret = devm_pm_runtime_set_active_enabled(dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, INV_ICM42607_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+
return 0;
}
EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607");
+static int inv_icm42607_suspend(struct device *dev)
+{
+ struct inv_icm42607_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
+ INV_ICM42607_SENSOR_MODE_OFF,
+ false, NULL);
+ if (ret)
+ return ret;
+ inv_icm42607_disable_vddio_reg(st);
+
+ return 0;
+}
+
+static int inv_icm42607_resume(struct device *dev)
+{
+ struct inv_icm42607_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ ret = inv_icm42607_enable_vddio_reg(st);
+ if (ret)
+ return ret;
+
+ /* Nothing else to restore at this time. */
+
+ return 0;
+}
+
+static int inv_icm42607_runtime_suspend(struct device *dev)
+{
+ struct inv_icm42607_state *st = dev_get_drvdata(dev);
+ int ret = 0;
+
+ guard(mutex)(&st->lock);
+
+ ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
+ INV_ICM42607_SENSOR_MODE_OFF, false,
+ NULL);
+ if (ret)
+ return ret;
+
+ inv_icm42607_disable_vddio_reg(st);
+
+ return 0;
+}
+
+static int inv_icm42607_runtime_resume(struct device *dev)
+{
+ struct inv_icm42607_state *st = dev_get_drvdata(dev);
+
+ guard(mutex)(&st->lock);
+
+ return inv_icm42607_enable_vddio_reg(st);
+}
+
+EXPORT_NS_GPL_DEV_PM_OPS(inv_icm42607_pm_ops, IIO_ICM42607) = {
+ SYSTEM_SLEEP_PM_OPS(inv_icm42607_suspend, inv_icm42607_resume)
+ RUNTIME_PM_OPS(inv_icm42607_runtime_suspend,
+ inv_icm42607_runtime_resume, NULL)
+};
+
MODULE_AUTHOR("InvenSense, Inc.");
MODULE_DESCRIPTION("InvenSense ICM-42607x device driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
index 8631579a609a..a51c764bf1dc 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
@@ -77,6 +77,7 @@ static struct i2c_driver inv_icm42607_driver = {
.driver = {
.name = "inv-icm42607-i2c",
.of_match_table = inv_icm42607_of_matches,
+ .pm = pm_ptr(&inv_icm42607_pm_ops),
},
.id_table = inv_icm42607_id,
.probe = inv_icm42607_probe,
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
index 49438fa6f867..9ba3dd0c689a 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
@@ -90,6 +90,7 @@ static struct spi_driver inv_icm42607_driver = {
.driver = {
.name = "inv-icm42607-spi",
.of_match_table = inv_icm42607_of_matches,
+ .pm = pm_ptr(&inv_icm42607_pm_ops),
},
.id_table = inv_icm42607_spi_id_table,
.probe = inv_icm42607_probe,
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V7 06/11] iio: imu: inv_icm42607: Add Buffer support for icm42607
2026-05-15 13:00 [PATCH V7 00/11] Add Invensense ICM42607 Chris Morgan
` (4 preceding siblings ...)
2026-05-15 13:00 ` [PATCH V7 05/11] iio: imu: inv_icm42607: Add PM support for icm42607 Chris Morgan
@ 2026-05-15 13:00 ` Chris Morgan
2026-05-15 13:35 ` sashiko-bot
2026-05-15 19:20 ` Jonathan Cameron
2026-05-15 13:00 ` [PATCH V7 07/11] iio: imu: inv_icm42607: Add Temp Support in icm42607 Chris Morgan
` (4 subsequent siblings)
10 siblings, 2 replies; 26+ messages in thread
From: Chris Morgan @ 2026-05-15 13:00 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add all FIFO parsing and reading functions to support
inv_icm42607 hardware.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/inv_icm42607/Makefile | 1 +
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 27 +
.../imu/inv_icm42607/inv_icm42607_buffer.c | 483 ++++++++++++++++++
.../imu/inv_icm42607/inv_icm42607_buffer.h | 93 ++++
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 54 +-
5 files changed, 657 insertions(+), 1 deletion(-)
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile
index be109102e203..3c9d08509793 100644
--- a/drivers/iio/imu/inv_icm42607/Makefile
+++ b/drivers/iio/imu/inv_icm42607/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o
inv-icm42607-y += inv_icm42607_core.o
+inv-icm42607-y += inv_icm42607_buffer.o
obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o
inv-icm42607-i2c-y += inv_icm42607_i2c.o
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
index 5f37999e39a5..b00ee6a4d451 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -14,6 +14,10 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+#include <linux/iio/common/inv_sensors_timestamp.h>
+
+#include "inv_icm42607_buffer.h"
+
/* serial bus slew rates */
enum inv_icm42607_slew_rate {
INV_ICM42607_SLEW_RATE_20_60NS,
@@ -84,6 +88,7 @@ struct inv_icm42607_sensor_conf {
int odr;
int filter;
};
+#define INV_ICM42607_SENSOR_CONF_INIT {-1, -1, -1, -1}
struct inv_icm42607_conf {
struct inv_icm42607_sensor_conf gyro;
@@ -114,6 +119,10 @@ struct inv_icm42607_suspended {
* @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.
+ * @fifo: FIFO management structure.
+ * @buffer: data transfer buffer aligned for DMA.
*/
struct inv_icm42607_state {
struct mutex lock;
@@ -125,6 +134,22 @@ struct inv_icm42607_state {
struct iio_mount_matrix orientation;
struct inv_icm42607_conf conf;
struct inv_icm42607_suspended suspended;
+ struct iio_dev *indio_gyro;
+ struct iio_dev *indio_accel;
+ struct inv_icm42607_fifo fifo;
+ __be16 buffer[3] __aligned(IIO_DMA_MINALIGN);
+};
+
+/**
+ * struct inv_icm42607_sensor_state - sensor state variables
+ * @power_mode: sensor requested power mode (for common frequencies)
+ * @filter: sensor filter.
+ * @ts: timestamp module states.
+ */
+struct inv_icm42607_sensor_state {
+ enum inv_icm42607_sensor_mode power_mode;
+ int filter;
+ struct inv_sensors_timestamp ts;
};
/* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */
@@ -352,6 +377,8 @@ extern const struct inv_icm42607_hw inv_icm42607_hw_data;
extern const struct inv_icm42607_hw inv_icm42607p_hw_data;
extern const struct dev_pm_ops inv_icm42607_pm_ops;
+u32 inv_icm42607_odr_to_period(enum inv_icm42607_odr odr);
+
int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw *hw,
inv_icm42607_bus_setup bus_setup);
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
new file mode 100644
index 000000000000..74e5213d9267
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
@@ -0,0 +1,483 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/minmax.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <linux/iio/common/inv_sensors_timestamp.h>
+
+#include "inv_icm42607.h"
+#include "inv_icm42607_buffer.h"
+
+/* FIFO header: 1 byte */
+#define INV_ICM42607_FIFO_HEADER_MSG BIT(7)
+#define INV_ICM42607_FIFO_HEADER_ACCEL BIT(6)
+#define INV_ICM42607_FIFO_HEADER_GYRO BIT(5)
+#define INV_ICM42607_FIFO_HEADER_TMST_FSYNC GENMASK(3, 2)
+#define INV_ICM42607_FIFO_HEADER_ODR_ACCEL BIT(1)
+#define INV_ICM42607_FIFO_HEADER_ODR_GYRO BIT(0)
+
+struct inv_icm42607_fifo_1sensor_packet {
+ u8 header;
+ struct inv_icm42607_fifo_sensor_data data;
+ s8 temp;
+} __packed;
+
+struct inv_icm42607_fifo_2sensors_packet {
+ u8 header;
+ struct inv_icm42607_fifo_sensor_data accel;
+ struct inv_icm42607_fifo_sensor_data gyro;
+ s8 temp;
+ __be16 timestamp;
+} __packed;
+
+ssize_t inv_icm42607_fifo_decode_packet(const void *packet, const void **accel,
+ const void **gyro, const int8_t **temp,
+ const void **timestamp)
+{
+ const struct inv_icm42607_fifo_1sensor_packet *pack1 = packet;
+ const struct inv_icm42607_fifo_2sensors_packet *pack2 = packet;
+ u8 header = *((const u8 *)packet);
+
+ /* FIFO empty */
+ if (header & INV_ICM42607_FIFO_HEADER_MSG) {
+ *accel = NULL;
+ *gyro = NULL;
+ *temp = NULL;
+ *timestamp = NULL;
+ return 0;
+ }
+
+ /* accel + gyro */
+ if ((header & INV_ICM42607_FIFO_HEADER_ACCEL) &&
+ (header & INV_ICM42607_FIFO_HEADER_GYRO)) {
+ *accel = &pack2->accel;
+ *gyro = &pack2->gyro;
+ *temp = &pack2->temp;
+ *timestamp = &pack2->timestamp;
+ return INV_ICM42607_FIFO_2SENSORS_PACKET_SIZE;
+ }
+
+ /* accel only */
+ if (header & INV_ICM42607_FIFO_HEADER_ACCEL) {
+ *accel = &pack1->data;
+ *gyro = NULL;
+ *temp = &pack1->temp;
+ *timestamp = NULL;
+ return INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE;
+ }
+
+ /* gyro only */
+ if (header & INV_ICM42607_FIFO_HEADER_GYRO) {
+ *accel = NULL;
+ *gyro = &pack1->data;
+ *temp = &pack1->temp;
+ *timestamp = NULL;
+ return INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE;
+ }
+
+ /* invalid packet if here */
+ return -EINVAL;
+}
+
+void inv_icm42607_buffer_update_fifo_period(struct inv_icm42607_state *st)
+{
+ u32 period_gyro, period_accel;
+
+ if (st->fifo.en & INV_ICM42607_SENSOR_GYRO)
+ period_gyro = inv_icm42607_odr_to_period(st->conf.gyro.odr);
+ else
+ period_gyro = U32_MAX;
+
+ if (st->fifo.en & INV_ICM42607_SENSOR_ACCEL)
+ period_accel = inv_icm42607_odr_to_period(st->conf.accel.odr);
+ else
+ period_accel = U32_MAX;
+
+ st->fifo.period = min(period_gyro, period_accel);
+}
+
+int inv_icm42607_buffer_set_fifo_en(struct inv_icm42607_state *st,
+ unsigned int fifo_en)
+{
+ unsigned int val;
+ int ret;
+
+ /* update FIFO EN bits for accel and gyro */
+ val = 0;
+ if (fifo_en & INV_ICM42607_SENSOR_GYRO)
+ val |= INV_ICM42607_FIFO_CONFIG1_MODE;
+ if (fifo_en & INV_ICM42607_SENSOR_ACCEL)
+ val |= INV_ICM42607_FIFO_CONFIG1_MODE;
+ if (fifo_en & INV_ICM42607_SENSOR_TEMP)
+ val |= INV_ICM42607_FIFO_CONFIG1_MODE;
+
+ ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1, val);
+ if (ret)
+ return ret;
+
+ st->fifo.en = fifo_en;
+ inv_icm42607_buffer_update_fifo_period(st);
+
+ return 0;
+}
+
+static size_t inv_icm42607_get_packet_size(unsigned int fifo_en)
+{
+ size_t packet_size;
+
+ if ((fifo_en & INV_ICM42607_SENSOR_GYRO) &&
+ (fifo_en & INV_ICM42607_SENSOR_ACCEL))
+ packet_size = INV_ICM42607_FIFO_2SENSORS_PACKET_SIZE;
+ else
+ packet_size = INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE;
+
+ return packet_size;
+}
+
+static unsigned int inv_icm42607_wm_truncate(unsigned int watermark,
+ size_t packet_size)
+{
+ size_t wm_size;
+
+ wm_size = watermark * packet_size;
+ wm_size = min(wm_size, INV_ICM42607_FIFO_WATERMARK_MAX);
+
+ return wm_size / packet_size;
+}
+
+/**
+ * inv_icm42607_buffer_update_watermark - update watermark FIFO threshold
+ * @st: driver internal state
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int inv_icm42607_buffer_update_watermark(struct inv_icm42607_state *st)
+{
+ const struct device *dev = regmap_get_device(st->map);
+ unsigned int wm_gyro, wm_accel, watermark;
+ u32 latency_gyro, latency_accel, latency;
+ u32 period_gyro, period_accel;
+ size_t packet_size, wm_size;
+ __le16 raw_wm;
+ bool restore;
+ int ret;
+
+ packet_size = inv_icm42607_get_packet_size(st->fifo.en);
+
+ /* compute sensors latency, depending on sensor watermark and odr */
+ wm_gyro = inv_icm42607_wm_truncate(st->fifo.watermark.gyro, packet_size);
+ wm_accel = inv_icm42607_wm_truncate(st->fifo.watermark.accel, packet_size);
+ /* use us for odr to avoid overflow using 32 bits values */
+ period_gyro = inv_icm42607_odr_to_period(st->conf.gyro.odr) / 1000UL;
+ period_accel = inv_icm42607_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);
+ /* all this works because periods are multiple of each others */
+ watermark = latency / min(period_gyro, period_accel);
+ watermark = max(watermark, 1);
+ /* update effective watermark */
+ st->fifo.watermark.eff_gyro = max(latency / period_gyro, 1);
+ st->fifo.watermark.eff_accel = max(latency / period_accel, 1);
+ }
+
+ /* changing FIFO watermark requires to turn off watermark interrupt */
+ ret = regmap_update_bits_check(st->map, INV_ICM42607_REG_INT_SOURCE0,
+ INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN,
+ 0, &restore);
+ if (ret)
+ return ret;
+
+ /* compute watermark value in bytes */
+ wm_size = watermark * packet_size;
+ raw_wm = INV_ICM42607_FIFO_WATERMARK_VAL(wm_size);
+ memcpy(st->buffer, &raw_wm, sizeof(raw_wm));
+ ret = regmap_bulk_write(st->map, INV_ICM42607_REG_FIFO_CONFIG2,
+ st->buffer, sizeof(raw_wm));
+ if (ret) {
+ dev_err(dev, "Unable to change watermark value: %d\n", ret);
+ if (restore)
+ regmap_update_bits(st->map, INV_ICM42607_REG_INT_SOURCE0,
+ INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN,
+ INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN);
+ return ret;
+ }
+
+ /* restore watermark interrupt */
+ if (restore) {
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_INT_SOURCE0,
+ INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN,
+ INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int inv_icm42607_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_state *sensor_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &sensor_st->ts;
+ struct device *dev = regmap_get_device(st->map);
+ 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_icm42607_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ /* exit if FIFO is already on */
+ if (st->fifo.on) {
+ st->fifo.on++;
+ return 0;
+ }
+
+ /* set FIFO threshold interrupt */
+ ret = regmap_set_bits(st->map, INV_ICM42607_REG_INT_SOURCE0,
+ INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN);
+ if (ret)
+ return ret;
+
+ /* flush FIFO data */
+ ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET,
+ INV_ICM42607_SIGNAL_PATH_RESET_FIFO_FLUSH);
+ if (ret)
+ return ret;
+
+ /* set FIFO in streaming mode */
+ ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1,
+ INV_ICM42607_FIFO_CONFIG1_MODE);
+ if (ret)
+ return ret;
+
+ /* workaround: first read of FIFO count after reset is always 0 */
+ ret = regmap_bulk_read(st->map, INV_ICM42607_REG_FIFO_COUNTH,
+ st->buffer, sizeof(__be16));
+ if (ret)
+ return ret;
+
+ st->fifo.on++;
+
+ return 0;
+}
+
+static int inv_icm42607_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ if (st->fifo.on > 1) {
+ st->fifo.on--;
+ return 0;
+ }
+
+ /* set FIFO in bypass mode */
+ ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1,
+ INV_ICM42607_FIFO_CONFIG1_BYPASS);
+ if (ret)
+ return ret;
+
+ /* flush FIFO data */
+ ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET,
+ INV_ICM42607_SIGNAL_PATH_RESET_FIFO_FLUSH);
+ if (ret)
+ return ret;
+
+ /* disable FIFO threshold interrupt */
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_INT_SOURCE0,
+ INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN, 0);
+ if (ret)
+ return ret;
+
+ /*
+ * FIFO should only be 1 at this point, so explicitly set it to 0
+ * instead of decrementing it.
+ */
+ st->fifo.on = 0;
+
+ return 0;
+}
+
+static int inv_icm42607_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct inv_icm42607_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_temp = 0;
+ unsigned int sleep_sensor = 0;
+ unsigned int sleep;
+ int ret;
+
+ if (indio_dev == st->indio_gyro) {
+ sensor = INV_ICM42607_SENSOR_GYRO;
+ watermark = &st->fifo.watermark.gyro;
+ } else if (indio_dev == st->indio_accel) {
+ sensor = INV_ICM42607_SENSOR_ACCEL;
+ watermark = &st->fifo.watermark.accel;
+ } else {
+ return -EINVAL;
+ }
+
+ mutex_lock(&st->lock);
+
+ ret = inv_icm42607_buffer_set_fifo_en(st, st->fifo.en & ~sensor);
+ if (ret)
+ goto out_unlock;
+
+ *watermark = 0;
+ ret = inv_icm42607_buffer_update_watermark(st);
+ if (ret)
+ goto out_unlock;
+
+out_unlock:
+ mutex_unlock(&st->lock);
+
+ /* sleep maximum required time */
+ sleep = max(sleep_sensor, sleep_temp);
+ if (sleep)
+ msleep(sleep);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+const struct iio_buffer_setup_ops inv_icm42607_buffer_ops = {
+ .preenable = inv_icm42607_buffer_preenable,
+ .postenable = inv_icm42607_buffer_postenable,
+ .predisable = inv_icm42607_buffer_predisable,
+ .postdisable = inv_icm42607_buffer_postdisable,
+};
+
+int inv_icm42607_buffer_fifo_read(struct inv_icm42607_state *st,
+ unsigned int max)
+{
+ const void *accel, *gyro, *timestamp;
+ size_t i, max_count;
+ const s8 *temp;
+ ssize_t size;
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ /* reset all samples counters */
+ st->fifo.count = 0;
+ st->fifo.nb.gyro = 0;
+ st->fifo.nb.accel = 0;
+ st->fifo.nb.total = 0;
+
+ /* compute maximum FIFO read size */
+ if (max == 0)
+ max_count = sizeof(st->fifo.data);
+ else
+ max_count = min((max * inv_icm42607_get_packet_size(st->fifo.en)),
+ sizeof(st->fifo.data));
+
+ /* read FIFO count value */
+ ret = regmap_bulk_read(st->map, INV_ICM42607_REG_FIFO_COUNTH,
+ st->buffer, sizeof(u8) * 2);
+ if (ret)
+ return ret;
+ st->fifo.count = be16_to_cpup(st->buffer);
+
+ /* check and clamp FIFO count value */
+ if (st->fifo.count == 0)
+ return 0;
+
+ st->fifo.count = min(st->fifo.count, max_count);
+
+ /* read all FIFO data in internal buffer */
+ ret = regmap_noinc_read(st->map, INV_ICM42607_REG_FIFO_DATA,
+ st->fifo.data, st->fifo.count);
+ if (ret)
+ return ret;
+
+ /* compute number of samples for each sensor */
+ for (i = 0; i < st->fifo.count; i += size) {
+ size = inv_icm42607_fifo_decode_packet(&st->fifo.data[i],
+ &accel, &gyro, &temp, ×tamp);
+ /* Make sure the size is at least 1 valid packet. */
+ if (size < INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE)
+ break;
+ if (gyro != NULL && inv_icm42607_fifo_is_data_valid(gyro))
+ st->fifo.nb.gyro++;
+ if (accel != NULL && inv_icm42607_fifo_is_data_valid(accel))
+ st->fifo.nb.accel++;
+ st->fifo.nb.total++;
+ }
+
+ return 0;
+}
+
+int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
+ unsigned int count)
+{
+ int ret;
+
+ ret = inv_icm42607_buffer_fifo_read(st, count);
+
+ return ret;
+}
+
+int inv_icm42607_buffer_init(struct inv_icm42607_state *st)
+{
+ int ret;
+
+ st->fifo.watermark.eff_gyro = 1;
+ st->fifo.watermark.eff_accel = 1;
+
+ /* Configure FIFO_COUNT format in bytes and big endian */
+ ret = regmap_set_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
+ INV_ICM42607_INTF_CONFIG0_FIFO_COUNT_ENDIAN);
+ if (ret)
+ return ret;
+
+ /* Initialize FIFO in bypass mode */
+ return regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1,
+ INV_ICM42607_FIFO_CONFIG1_BYPASS);
+}
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
new file mode 100644
index 000000000000..b77deb66f8bd
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#ifndef INV_ICM42607_BUFFER_H_
+#define INV_ICM42607_BUFFER_H_
+
+#include <linux/bitops.h>
+
+struct inv_icm42607_state;
+
+#define INV_ICM42607_SENSOR_GYRO BIT(0)
+#define INV_ICM42607_SENSOR_ACCEL BIT(1)
+#define INV_ICM42607_SENSOR_TEMP BIT(2)
+
+/**
+ * struct inv_icm42607_fifo - FIFO state variables
+ * @on: reference counter for FIFO on.
+ * @en: bits field of INV_ICM42607_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 (2kB + 32 bytes of read cache).
+ */
+struct inv_icm42607_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[2080] __aligned(IIO_DMA_MINALIGN);
+};
+
+/* FIFO data packet */
+struct inv_icm42607_fifo_sensor_data {
+ __be16 x;
+ __be16 y;
+ __be16 z;
+};
+
+#define INV_ICM42607_FIFO_DATA_INVALID -32768
+
+static inline bool
+inv_icm42607_fifo_is_data_valid(const struct inv_icm42607_fifo_sensor_data *s)
+{
+ s16 x, y, z;
+
+ x = be16_to_cpu(s->x);
+ y = be16_to_cpu(s->y);
+ z = be16_to_cpu(s->z);
+
+ if (x == INV_ICM42607_FIFO_DATA_INVALID &&
+ y == INV_ICM42607_FIFO_DATA_INVALID &&
+ z == INV_ICM42607_FIFO_DATA_INVALID)
+ return false;
+
+ return true;
+}
+
+ssize_t inv_icm42607_fifo_decode_packet(const void *packet, const void **accel,
+ const void **gyro, const s8 **temp,
+ const void **timestamp);
+
+extern const struct iio_buffer_setup_ops inv_icm42607_buffer_ops;
+
+int inv_icm42607_buffer_init(struct inv_icm42607_state *st);
+
+void inv_icm42607_buffer_update_fifo_period(struct inv_icm42607_state *st);
+
+int inv_icm42607_buffer_set_fifo_en(struct inv_icm42607_state *st,
+ unsigned int fifo_en);
+
+int inv_icm42607_buffer_update_watermark(struct inv_icm42607_state *st);
+
+int inv_icm42607_buffer_fifo_read(struct inv_icm42607_state *st,
+ unsigned int max);
+
+int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
+ unsigned int count);
+
+#endif
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
index e27ad0319a12..d5885fc3f7da 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -15,6 +15,7 @@
#include <linux/regulator/consumer.h>
#include "inv_icm42607.h"
+#include "inv_icm42607_buffer.h"
static bool inv_icm42607_is_volatile_reg(struct device *dev, unsigned int reg)
{
@@ -73,6 +74,40 @@ const struct inv_icm42607_hw inv_icm42607p_hw_data = {
};
EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607");
+u32 inv_icm42607_odr_to_period(enum inv_icm42607_odr odr)
+{
+ static const u32 odr_periods[INV_ICM42607_ODR_NB] = {
+ /* Reserved values */
+ 0, 0, 0, 0, 0,
+ /* 1600Hz */
+ 625000,
+ /* 800Hz */
+ 1250000,
+ /* 400Hz */
+ 2500000,
+ /* 200Hz */
+ 5000000,
+ /* 100 Hz */
+ 10000000,
+ /* 50Hz */
+ 20000000,
+ /* 25Hz */
+ 40000000,
+ /* 12.5Hz */
+ 80000000,
+ /* 6.25Hz */
+ 160000000,
+ /* 3.125Hz */
+ 320000000,
+ /* 1.5625Hz */
+ 640000000,
+ };
+
+ odr = clamp(odr, INV_ICM42607_ODR_1600HZ, INV_ICM42607_ODR_1_5625HZ_LP);
+
+ return odr_periods[odr];
+}
+
static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st,
enum inv_icm42607_sensor_mode gyro,
enum inv_icm42607_sensor_mode accel,
@@ -333,6 +368,11 @@ int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw
if (ret)
return ret;
+ /* Initialize buffer/FIFO handling */
+ ret = inv_icm42607_buffer_init(st);
+ if (ret)
+ return ret;
+
ret = devm_pm_runtime_set_active_enabled(dev);
if (ret)
return ret;
@@ -354,6 +394,13 @@ static int inv_icm42607_suspend(struct device *dev)
if (pm_runtime_suspended(dev))
return 0;
+ if (st->fifo.on) {
+ ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1,
+ INV_ICM42607_FIFO_CONFIG1_BYPASS);
+ if (ret)
+ return ret;
+ }
+
ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
INV_ICM42607_SENSOR_MODE_OFF,
false, NULL);
@@ -378,7 +425,12 @@ static int inv_icm42607_resume(struct device *dev)
if (ret)
return ret;
- /* Nothing else to restore at this time. */
+ if (st->fifo.on) {
+ ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1,
+ INV_ICM42607_FIFO_CONFIG1_MODE);
+ if (ret)
+ return ret;
+ }
return 0;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V7 07/11] iio: imu: inv_icm42607: Add Temp Support in icm42607
2026-05-15 13:00 [PATCH V7 00/11] Add Invensense ICM42607 Chris Morgan
` (5 preceding siblings ...)
2026-05-15 13:00 ` [PATCH V7 06/11] iio: imu: inv_icm42607: Add Buffer " Chris Morgan
@ 2026-05-15 13:00 ` Chris Morgan
2026-05-15 13:00 ` [PATCH V7 08/11] iio: imu: inv_icm42607: Add Accelerometer for icm42607 Chris Morgan
` (3 subsequent siblings)
10 siblings, 0 replies; 26+ messages in thread
From: Chris Morgan @ 2026-05-15 13:00 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add functions for reading temperature sensor data.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/inv_icm42607/Makefile | 1 +
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 3 +
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 18 +++++
.../iio/imu/inv_icm42607/inv_icm42607_temp.c | 80 +++++++++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_temp.h | 33 ++++++++
5 files changed, 135 insertions(+)
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h
diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile
index 3c9d08509793..ccb8e007cdeb 100644
--- a/drivers/iio/imu/inv_icm42607/Makefile
+++ b/drivers/iio/imu/inv_icm42607/Makefile
@@ -3,6 +3,7 @@
obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o
inv-icm42607-y += inv_icm42607_core.o
inv-icm42607-y += inv_icm42607_buffer.o
+inv-icm42607-y += inv_icm42607_temp.o
obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o
inv-icm42607-i2c-y += inv_icm42607_i2c.o
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
index b00ee6a4d451..4e053281dc55 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -379,6 +379,9 @@ extern const struct dev_pm_ops inv_icm42607_pm_ops;
u32 inv_icm42607_odr_to_period(enum inv_icm42607_odr odr);
+int inv_icm42607_set_temp_conf(struct inv_icm42607_state *st, bool enable,
+ unsigned int *sleep_ms);
+
int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw *hw,
inv_icm42607_bus_setup bus_setup);
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
index d5885fc3f7da..9d6d7d13c9b9 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -164,6 +164,24 @@ static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st,
return 0;
}
+int inv_icm42607_set_temp_conf(struct inv_icm42607_state *st, bool enable,
+ unsigned int *sleep_ms)
+{
+ unsigned int val;
+ int ret;
+
+ val = FIELD_PREP(INV_ICM42607_TEMP_CONFIG0_FILTER_MASK,
+ INV_ICM42607_FILTER_BW_34HZ);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_TEMP_CONFIG0,
+ INV_ICM42607_TEMP_CONFIG0_FILTER_MASK, val);
+ if (ret)
+ return ret;
+
+ return inv_icm42607_set_pwr_mgmt0(st, st->conf.gyro.mode,
+ st->conf.accel.mode, enable,
+ sleep_ms);
+}
+
static int inv_icm42607_set_conf(struct inv_icm42607_state *st,
const struct inv_icm42607_conf *conf)
{
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c
new file mode 100644
index 000000000000..7e9cb072a011
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/iio/iio.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include "inv_icm42607.h"
+#include "inv_icm42607_temp.h"
+
+static int inv_icm42607_temp_read(struct inv_icm42607_state *st, s16 *temp)
+{
+ struct device *dev = regmap_get_device(st->map);
+ __be16 *raw;
+ int ret;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
+
+ guard(mutex)(&st->lock);
+
+ ret = inv_icm42607_set_temp_conf(st, true, NULL);
+ if (ret)
+ return ret;
+
+ raw = &st->buffer[0];
+ ret = regmap_bulk_read(st->map, INV_ICM42607_REG_TEMP_DATA1, raw, sizeof(*raw));
+ if (ret)
+ return ret;
+
+ *temp = be16_to_cpup(raw);
+ if (*temp == INV_ICM42607_DATA_INVALID)
+ return -EINVAL;
+
+ return 0;
+}
+
+int inv_icm42607_temp_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct inv_icm42607_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:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = inv_icm42607_temp_read(st, &temp);
+ iio_device_release_direct(indio_dev);
+ if (ret)
+ return ret;
+ *val = temp;
+ return IIO_VAL_INT;
+ /*
+ * T°C = (temp / 128) + 25
+ * Tm°C = 1000 * ((temp * 100 / 12800) + 25)
+ * scale: 100000 / 12800 ~= 7.8125
+ * offset: 3200
+ */
+ case IIO_CHAN_INFO_SCALE:
+ *val = 7;
+ *val2 = 812500000;
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 3200;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h
new file mode 100644
index 000000000000..e03924e30866
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#ifndef INV_ICM42607_TEMP_H_
+#define INV_ICM42607_TEMP_H_
+
+#include <linux/bitops.h>
+
+struct iio_dev;
+struct iio_chan_spec;
+
+#define INV_ICM42607_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, \
+ }, \
+}
+
+int inv_icm42607_temp_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask);
+
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V7 08/11] iio: imu: inv_icm42607: Add Accelerometer for icm42607
2026-05-15 13:00 [PATCH V7 00/11] Add Invensense ICM42607 Chris Morgan
` (6 preceding siblings ...)
2026-05-15 13:00 ` [PATCH V7 07/11] iio: imu: inv_icm42607: Add Temp Support in icm42607 Chris Morgan
@ 2026-05-15 13:00 ` Chris Morgan
2026-05-15 13:34 ` sashiko-bot
2026-05-15 19:29 ` Jonathan Cameron
2026-05-15 13:00 ` [PATCH V7 09/11] iio: imu: inv_icm42607: Add Wake on Movement to icm42607 Chris Morgan
` (2 subsequent siblings)
10 siblings, 2 replies; 26+ messages in thread
From: Chris Morgan @ 2026-05-15 13:00 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add icm42607 accelerometer sensor for icm42607.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/inv_icm42607/Makefile | 1 +
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 27 +
.../iio/imu/inv_icm42607/inv_icm42607_accel.c | 586 ++++++++++++++++++
.../imu/inv_icm42607/inv_icm42607_buffer.c | 66 +-
.../imu/inv_icm42607/inv_icm42607_buffer.h | 4 +-
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 72 +++
6 files changed, 752 insertions(+), 4 deletions(-)
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile
index ccb8e007cdeb..e908d77c4219 100644
--- a/drivers/iio/imu/inv_icm42607/Makefile
+++ b/drivers/iio/imu/inv_icm42607/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o
inv-icm42607-y += inv_icm42607_core.o
+inv-icm42607-y += inv_icm42607_accel.o
inv-icm42607-y += inv_icm42607_buffer.o
inv-icm42607-y += inv_icm42607_temp.o
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
index 4e053281dc55..095f4a495e3f 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -82,6 +82,17 @@ enum inv_icm42607_filter_bw {
INV_ICM42607_FILTER_BW_16HZ
};
+enum inv_icm42607_filter_avg {
+ /* Low-Power mode sensor data filter (averaging) */
+ INV_ICM42607_FILTER_AVG_2X = 0,
+ INV_ICM42607_FILTER_AVG_4X,
+ INV_ICM42607_FILTER_AVG_8X,
+ INV_ICM42607_FILTER_AVG_16X,
+ INV_ICM42607_FILTER_AVG_32X,
+ INV_ICM42607_FILTER_AVG_64X
+ /* values 7 and 8 also correspond to 64x. */
+};
+
struct inv_icm42607_sensor_conf {
int mode;
int fs;
@@ -121,6 +132,7 @@ struct inv_icm42607_suspended {
* @suspended: suspended sensors configuration.
* @indio_gyro: gyroscope IIO device.
* @indio_accel: accelerometer IIO device.
+ * @timestamp: interrupt timestamps.
* @fifo: FIFO management structure.
* @buffer: data transfer buffer aligned for DMA.
*/
@@ -136,6 +148,9 @@ struct inv_icm42607_state {
struct inv_icm42607_suspended suspended;
struct iio_dev *indio_gyro;
struct iio_dev *indio_accel;
+ struct {
+ s64 accel;
+ } timestamp;
struct inv_icm42607_fifo fifo;
__be16 buffer[3] __aligned(IIO_DMA_MINALIGN);
};
@@ -377,12 +392,24 @@ extern const struct inv_icm42607_hw inv_icm42607_hw_data;
extern const struct inv_icm42607_hw inv_icm42607p_hw_data;
extern const struct dev_pm_ops inv_icm42607_pm_ops;
+const struct iio_mount_matrix *
+inv_icm42607_get_mount_matrix(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan);
+
u32 inv_icm42607_odr_to_period(enum inv_icm42607_odr odr);
+int inv_icm42607_set_accel_conf(struct inv_icm42607_state *st,
+ struct inv_icm42607_sensor_conf *conf,
+ unsigned int *sleep_ms);
+
int inv_icm42607_set_temp_conf(struct inv_icm42607_state *st, bool enable,
unsigned int *sleep_ms);
int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw *hw,
inv_icm42607_bus_setup bus_setup);
+struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st);
+
+int inv_icm42607_accel_parse_fifo(struct iio_dev *indio_dev);
+
#endif
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
new file mode 100644
index 000000000000..b354a84fd2ed
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
@@ -0,0 +1,586 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/math64.h>
+#include <linux/minmax.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/units.h>
+
+#include <linux/iio/common/inv_sensors_timestamp.h>
+#include "inv_icm42607.h"
+#include "inv_icm42607_temp.h"
+#include "inv_icm42607_buffer.h"
+
+#define INV_ICM42607_ACCEL_CHAN(_modifier, _index, _ext_info) \
+{ \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = _modifier, \
+ .info_mask_separate = \
+ BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .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_BE, \
+ }, \
+ .ext_info = _ext_info, \
+}
+
+#define INV_ICM42607_ACCEL_EVENT_CHAN(_modifier, _events, _events_nb) \
+ { \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = _modifier, \
+ .event_spec = _events, \
+ .num_event_specs = _events_nb, \
+ .scan_index = -1, \
+ }
+
+enum inv_icm42607_accel_scan {
+ INV_ICM42607_ACCEL_SCAN_X,
+ INV_ICM42607_ACCEL_SCAN_Y,
+ INV_ICM42607_ACCEL_SCAN_Z,
+ INV_ICM42607_ACCEL_SCAN_TEMP,
+ INV_ICM42607_ACCEL_SCAN_TIMESTAMP,
+};
+
+static const struct iio_chan_spec_ext_info inv_icm42607_accel_ext_infos[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42607_get_mount_matrix),
+ { }
+};
+
+static const struct iio_chan_spec inv_icm42607_accel_channels[] = {
+ INV_ICM42607_ACCEL_CHAN(IIO_MOD_X, INV_ICM42607_ACCEL_SCAN_X,
+ inv_icm42607_accel_ext_infos),
+ INV_ICM42607_ACCEL_CHAN(IIO_MOD_Y, INV_ICM42607_ACCEL_SCAN_Y,
+ inv_icm42607_accel_ext_infos),
+ INV_ICM42607_ACCEL_CHAN(IIO_MOD_Z, INV_ICM42607_ACCEL_SCAN_Z,
+ inv_icm42607_accel_ext_infos),
+ INV_ICM42607_TEMP_CHAN(INV_ICM42607_ACCEL_SCAN_TEMP),
+ IIO_CHAN_SOFT_TIMESTAMP(INV_ICM42607_ACCEL_SCAN_TIMESTAMP),
+};
+
+static const struct iio_event_spec inv_icm42607_motion_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_VALUE),
+ },
+};
+
+struct inv_icm42607_accel_buffer {
+ struct inv_icm42607_fifo_sensor_data accel;
+ s16 temp;
+ aligned_s64 timestamp;
+};
+
+#define INV_ICM42607_SCAN_MASK_ACCEL_3AXIS \
+ (BIT(INV_ICM42607_ACCEL_SCAN_X) | \
+ BIT(INV_ICM42607_ACCEL_SCAN_Y) | \
+ BIT(INV_ICM42607_ACCEL_SCAN_Z))
+
+#define INV_ICM42607_SCAN_MASK_TEMP BIT(INV_ICM42607_ACCEL_SCAN_TEMP)
+
+static const unsigned long inv_icm42607_accel_scan_masks[] = {
+ INV_ICM42607_SCAN_MASK_ACCEL_3AXIS,
+ INV_ICM42607_SCAN_MASK_TEMP,
+ INV_ICM42607_SCAN_MASK_ACCEL_3AXIS | INV_ICM42607_SCAN_MASK_TEMP,
+ 0
+};
+
+/* enable accelerometer sensor and FIFO write */
+static int inv_icm42607_accel_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_state *accel_st = iio_priv(indio_dev);
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ unsigned int fifo_en = 0;
+ unsigned int sleep_temp = 0;
+ unsigned int sleep_accel = 0;
+ int ret;
+
+ mutex_lock(&st->lock);
+
+ if (*scan_mask & INV_ICM42607_SCAN_MASK_TEMP) {
+ /* enable temp sensor */
+ ret = inv_icm42607_set_temp_conf(st, true, &sleep_temp);
+ if (ret)
+ goto out_unlock;
+ fifo_en |= INV_ICM42607_SENSOR_TEMP;
+ }
+
+ if (*scan_mask & INV_ICM42607_SCAN_MASK_ACCEL_3AXIS) {
+ /* enable accel sensor */
+ conf.mode = accel_st->power_mode;
+ conf.filter = accel_st->filter;
+ ret = inv_icm42607_set_accel_conf(st, &conf, &sleep_accel);
+ if (ret)
+ goto out_unlock;
+ fifo_en |= INV_ICM42607_SENSOR_ACCEL;
+ }
+
+ /* update data FIFO write */
+ ret = inv_icm42607_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
+
+out_unlock:
+ mutex_unlock(&st->lock);
+ /*
+ * Choose the highest enable-delay time of the two sensors being
+ * enabled, and sleep for that amount of time.
+ */
+ msleep(max(sleep_accel, sleep_temp));
+
+ return ret;
+}
+
+static int inv_icm42607_accel_read_sensor(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ s16 *val)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_state *accel_st = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ unsigned int reg;
+ __be16 *data;
+ int ret;
+
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ reg = INV_ICM42607_REG_ACCEL_DATA_X1;
+ break;
+ case IIO_MOD_Y:
+ reg = INV_ICM42607_REG_ACCEL_DATA_Y1;
+ break;
+ case IIO_MOD_Z:
+ reg = INV_ICM42607_REG_ACCEL_DATA_Z1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
+
+ guard(mutex)(&st->lock);
+
+ /* enable accel sensor */
+ conf.mode = accel_st->power_mode;
+ conf.filter = accel_st->filter;
+ ret = inv_icm42607_set_accel_conf(st, &conf, NULL);
+ if (ret)
+ return ret;
+
+ /* read accel register data */
+ data = &st->buffer[0];
+ ret = regmap_bulk_read(st->map, reg, data, sizeof(*data));
+ if (ret)
+ return ret;
+
+ *val = be16_to_cpup(data);
+ if (*val == INV_ICM42607_DATA_INVALID)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const int inv_icm42607_accel_scale_nano[][2] = {
+ [INV_ICM42607_ACCEL_FS_16G] = { 0, 4788403 },
+ [INV_ICM42607_ACCEL_FS_8G] = { 0, 2394202 },
+ [INV_ICM42607_ACCEL_FS_4G] = { 0, 1197101 },
+ [INV_ICM42607_ACCEL_FS_2G] = { 0, 598550 }
+};
+
+static int inv_icm42607_accel_read_scale(struct iio_dev *indio_dev,
+ int *val, int *val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ unsigned int idx;
+
+ idx = st->conf.accel.fs;
+
+ *val = inv_icm42607_accel_scale_nano[idx][0];
+ *val2 = inv_icm42607_accel_scale_nano[idx][1];
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int inv_icm42607_accel_write_scale(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ size_t scales_len = ARRAY_SIZE(inv_icm42607_accel_scale_nano);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+
+ for (idx = 0; idx < scales_len; idx++) {
+ if (val == inv_icm42607_accel_scale_nano[idx][0] &&
+ val2 == inv_icm42607_accel_scale_nano[idx][1])
+ break;
+ }
+ if (idx >= scales_len)
+ return -EINVAL;
+
+ conf.fs = idx;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
+
+ guard(mutex)(&st->lock);
+
+ return inv_icm42607_set_accel_conf(st, &conf, NULL);
+}
+
+/* IIO format int + micro , values 0-5 reserved. */
+static const int inv_icm42607_accel_odr[][2] = {
+ [INV_ICM42607_ODR_1_5625HZ_LP] = { 1, 562500 },
+ [INV_ICM42607_ODR_3_125HZ_LP] = { 3, 125000 },
+ [INV_ICM42607_ODR_6_25HZ_LP] = { 6, 250000 },
+ [INV_ICM42607_ODR_12_5HZ] = { 12, 500000 },
+ [INV_ICM42607_ODR_25HZ] = { 25, 0 },
+ [INV_ICM42607_ODR_50HZ] = { 50, 0 },
+ [INV_ICM42607_ODR_100HZ] = { 100, 0 },
+ [INV_ICM42607_ODR_200HZ] = { 200, 0 },
+ [INV_ICM42607_ODR_400HZ] = { 400, 0 },
+ [INV_ICM42607_ODR_800HZ] = { 800, 0 },
+ [INV_ICM42607_ODR_1600HZ] = { 1600, 0 }
+};
+
+static int inv_icm42607_accel_read_odr(struct inv_icm42607_state *st,
+ int *val, int *val2)
+{
+ unsigned int odr;
+ unsigned int i;
+
+ odr = st->conf.accel.odr;
+
+ for (i = 5; i < ARRAY_SIZE(inv_icm42607_accel_odr); ++i) {
+ if (i == odr)
+ break;
+ }
+ if (i >= ARRAY_SIZE(inv_icm42607_accel_odr))
+ return -EINVAL;
+
+ *val = inv_icm42607_accel_odr[i][0];
+ *val2 = inv_icm42607_accel_odr[i][1];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int inv_icm42607_accel_write_odr(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_state *accel_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &accel_st->ts;
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ int ret;
+
+ for (idx = 5; idx < ARRAY_SIZE(inv_icm42607_accel_odr); ++idx) {
+ if (val == inv_icm42607_accel_odr[idx][0] &&
+ val2 == inv_icm42607_accel_odr[idx][1])
+ break;
+ }
+ if (idx >= ARRAY_SIZE(inv_icm42607_accel_odr))
+ return -EINVAL;
+
+ conf.odr = idx;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
+
+ guard(mutex)(&st->lock);
+
+ ret = inv_sensors_timestamp_update_odr(ts, inv_icm42607_odr_to_period(conf.odr),
+ iio_buffer_enabled(indio_dev));
+ if (ret)
+ return ret;
+
+ ret = inv_icm42607_set_accel_conf(st, &conf, NULL);
+ if (ret)
+ return ret;
+
+ inv_icm42607_buffer_update_fifo_period(st);
+ inv_icm42607_buffer_update_watermark(st);
+
+ return 0;
+}
+
+static int inv_icm42607_accel_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ s16 data;
+ int ret;
+
+ switch (chan->type) {
+ case IIO_ACCEL:
+ break;
+ case IIO_TEMP:
+ return inv_icm42607_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_icm42607_accel_read_sensor(indio_dev, chan, &data);
+ iio_device_release_direct(indio_dev);
+ if (ret)
+ return ret;
+ *val = data;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ return inv_icm42607_accel_read_scale(indio_dev, val, val2);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm42607_accel_read_odr(st, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_accel_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals,
+ int *type, int *length, long mask)
+{
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *vals = (const int *)inv_icm42607_accel_scale_nano;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *length = ARRAY_SIZE(inv_icm42607_accel_scale_nano) * 2;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = (const int *)inv_icm42607_accel_odr;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *length = ARRAY_SIZE(inv_icm42607_accel_odr) * 2;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_accel_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ 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_icm42607_accel_write_scale(indio_dev, val, val2);
+ iio_device_release_direct(indio_dev);
+ return ret;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm42607_accel_write_odr(indio_dev, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_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;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_accel_hwfifo_set_watermark(struct iio_dev *indio_dev,
+ unsigned int val)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
+
+ guard(mutex)(&st->lock);
+
+ st->fifo.watermark.accel = val;
+ return inv_icm42607_buffer_update_watermark(st);
+}
+
+static int inv_icm42607_accel_hwfifo_flush(struct iio_dev *indio_dev,
+ unsigned int count)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ int ret;
+
+ if (count == 0)
+ return 0;
+
+ /* Make sure we don't try to count more than the buffer can hold. */
+ count = min(count, sizeof(st->fifo.data));
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
+
+ ret = inv_icm42607_buffer_hwfifo_flush(st, count);
+ if (ret)
+ return ret;
+
+ return st->fifo.nb.accel;
+}
+
+static const struct iio_info inv_icm42607_accel_info = {
+ .read_raw = inv_icm42607_accel_read_raw,
+ .read_avail = inv_icm42607_accel_read_avail,
+ .write_raw = inv_icm42607_accel_write_raw,
+ .write_raw_get_fmt = inv_icm42607_accel_write_raw_get_fmt,
+ .update_scan_mode = inv_icm42607_accel_update_scan_mode,
+ .hwfifo_set_watermark = inv_icm42607_accel_hwfifo_set_watermark,
+ .hwfifo_flush_to_buffer = inv_icm42607_accel_hwfifo_flush,
+};
+
+struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st)
+{
+ struct device *dev = regmap_get_device(st->map);
+ const char *name;
+ struct inv_icm42607_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->hw->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->power_mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE;
+ accel_st->filter = INV_ICM42607_FILTER_AVG_16X;
+
+ /*
+ * 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_icm42607_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_icm42607_accel_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = inv_icm42607_accel_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_icm42607_accel_channels);
+ indio_dev->available_scan_masks = inv_icm42607_accel_scan_masks;
+
+ ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
+ &inv_icm42607_buffer_ops);
+ if (ret)
+ return ERR_PTR(ret);
+
+ /* accel events are wakeup capable */
+ ret = devm_device_init_wakeup(&indio_dev->dev);
+ 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_icm42607_accel_parse_fifo(struct iio_dev *indio_dev)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_state *accel_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &accel_st->ts;
+ ssize_t i, size;
+ unsigned int no;
+ const void *accel, *gyro, *timestamp;
+ const int8_t *temp;
+ unsigned int odr;
+ int64_t ts_val;
+ struct inv_icm42607_accel_buffer buffer = { };
+
+ /* parse all fifo packets */
+ for (i = 0, no = 0; i < st->fifo.count; i += size, ++no) {
+ size = inv_icm42607_fifo_decode_packet(&st->fifo.data[i],
+ &accel, &gyro, &temp, ×tamp, &odr);
+ /* quit if error or FIFO is empty */
+ if (size <= 0)
+ return size;
+
+ /* If the packet size could cause us to overflow, return. */
+ if (i + size <= st->fifo.count)
+ return -EIO;
+
+ /* skip packet if no accel data or data is invalid */
+ if (accel == NULL || !inv_icm42607_fifo_is_data_valid(accel))
+ continue;
+
+ /* update odr */
+ if (odr & INV_ICM42607_SENSOR_ACCEL)
+ inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
+ st->fifo.nb.total, no);
+
+ 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_ts(indio_dev, &buffer,
+ sizeof(buffer), ts_val);
+ }
+
+ return 0;
+}
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
index 74e5213d9267..4371f428cc3f 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
@@ -40,7 +40,7 @@ struct inv_icm42607_fifo_2sensors_packet {
ssize_t inv_icm42607_fifo_decode_packet(const void *packet, const void **accel,
const void **gyro, const int8_t **temp,
- const void **timestamp)
+ const void **timestamp, unsigned int *odr)
{
const struct inv_icm42607_fifo_1sensor_packet *pack1 = packet;
const struct inv_icm42607_fifo_2sensors_packet *pack2 = packet;
@@ -52,9 +52,17 @@ ssize_t inv_icm42607_fifo_decode_packet(const void *packet, const void **accel,
*gyro = NULL;
*temp = NULL;
*timestamp = NULL;
+ *odr = 0;
return 0;
}
+ /* handle odr flags */
+ *odr = 0;
+ if (header & INV_ICM42607_FIFO_HEADER_ODR_GYRO)
+ *odr |= INV_ICM42607_SENSOR_GYRO;
+ if (header & INV_ICM42607_FIFO_HEADER_ODR_ACCEL)
+ *odr |= INV_ICM42607_SENSOR_ACCEL;
+
/* accel + gyro */
if ((header & INV_ICM42607_FIFO_HEADER_ACCEL) &&
(header & INV_ICM42607_FIFO_HEADER_GYRO)) {
@@ -348,6 +356,7 @@ static int inv_icm42607_buffer_postdisable(struct iio_dev *indio_dev)
struct device *dev = regmap_get_device(st->map);
unsigned int sensor;
unsigned int *watermark;
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
unsigned int sleep_temp = 0;
unsigned int sleep_sensor = 0;
unsigned int sleep;
@@ -374,6 +383,16 @@ static int inv_icm42607_buffer_postdisable(struct iio_dev *indio_dev)
if (ret)
goto out_unlock;
+ conf.mode = INV_ICM42607_SENSOR_MODE_OFF;
+ if (sensor != INV_ICM42607_SENSOR_GYRO)
+ ret = inv_icm42607_set_accel_conf(st, &conf, &sleep_sensor);
+ if (ret)
+ goto out_unlock;
+
+ /* if FIFO is off, turn temperature off */
+ if (!st->fifo.on)
+ ret = inv_icm42607_set_temp_conf(st, false, &sleep_temp);
+
out_unlock:
mutex_unlock(&st->lock);
@@ -399,6 +418,7 @@ int inv_icm42607_buffer_fifo_read(struct inv_icm42607_state *st,
{
const void *accel, *gyro, *timestamp;
size_t i, max_count;
+ unsigned int odr;
const s8 *temp;
ssize_t size;
int ret;
@@ -440,7 +460,7 @@ int inv_icm42607_buffer_fifo_read(struct inv_icm42607_state *st,
/* compute number of samples for each sensor */
for (i = 0; i < st->fifo.count; i += size) {
size = inv_icm42607_fifo_decode_packet(&st->fifo.data[i],
- &accel, &gyro, &temp, ×tamp);
+ &accel, &gyro, &temp, ×tamp, &odr);
/* Make sure the size is at least 1 valid packet. */
if (size < INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE)
break;
@@ -454,14 +474,54 @@ int inv_icm42607_buffer_fifo_read(struct inv_icm42607_state *st,
return 0;
}
+int inv_icm42607_buffer_fifo_parse(struct inv_icm42607_state *st)
+{
+ struct inv_icm42607_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 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_icm42607_accel_parse_fifo(st->indio_accel);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
unsigned int count)
{
+ struct inv_icm42607_sensor_state *accel_st = iio_priv(st->indio_accel);
+ struct inv_sensors_timestamp *ts;
+ s64 accel_ts;
int ret;
+ accel_ts = iio_get_time_ns(st->indio_accel);
+
ret = inv_icm42607_buffer_fifo_read(st, count);
+ if (ret)
+ return ret;
- return ret;
+ if (st->fifo.nb.total == 0)
+ return 0;
+
+ if (st->fifo.nb.accel > 0) {
+ ts = &accel_st->ts;
+ inv_sensors_timestamp_interrupt(ts, st->fifo.nb.accel, accel_ts);
+ ret = inv_icm42607_accel_parse_fifo(st->indio_accel);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
}
int inv_icm42607_buffer_init(struct inv_icm42607_state *st)
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
index b77deb66f8bd..4160076bbf77 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
@@ -71,7 +71,7 @@ inv_icm42607_fifo_is_data_valid(const struct inv_icm42607_fifo_sensor_data *s)
ssize_t inv_icm42607_fifo_decode_packet(const void *packet, const void **accel,
const void **gyro, const s8 **temp,
- const void **timestamp);
+ const void **timestamp, unsigned int *odr);
extern const struct iio_buffer_setup_ops inv_icm42607_buffer_ops;
@@ -87,6 +87,8 @@ int inv_icm42607_buffer_update_watermark(struct inv_icm42607_state *st);
int inv_icm42607_buffer_fifo_read(struct inv_icm42607_state *st,
unsigned int max);
+int inv_icm42607_buffer_fifo_parse(struct inv_icm42607_state *st);
+
int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
unsigned int count);
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
index 9d6d7d13c9b9..1e685451f785 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -74,6 +74,15 @@ const struct inv_icm42607_hw inv_icm42607p_hw_data = {
};
EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607");
+const struct iio_mount_matrix *
+inv_icm42607_get_mount_matrix(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ const struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+
+ return &st->orientation;
+}
+
u32 inv_icm42607_odr_to_period(enum inv_icm42607_odr odr)
{
static const u32 odr_periods[INV_ICM42607_ODR_NB] = {
@@ -164,6 +173,53 @@ static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st,
return 0;
}
+int inv_icm42607_set_accel_conf(struct inv_icm42607_state *st,
+ struct inv_icm42607_sensor_conf *conf,
+ unsigned int *sleep_ms)
+{
+ struct inv_icm42607_sensor_conf *oldconf = &st->conf.accel;
+ unsigned int val;
+ int ret;
+
+ 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;
+
+ if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) {
+ val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK, conf->fs);
+ val |= FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_ODR_MASK, conf->odr);
+ ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val);
+ if (ret)
+ return ret;
+ oldconf->fs = conf->fs;
+ oldconf->odr = conf->odr;
+ }
+
+ if (conf->filter != oldconf->filter) {
+ if (conf->mode == INV_ICM42607_SENSOR_MODE_LOW_POWER) {
+ val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_AVG_MASK, conf->filter);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1,
+ INV_ICM42607_ACCEL_CONFIG1_AVG_MASK, val);
+ } else {
+ val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK,
+ conf->filter);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1,
+ INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, val);
+ }
+ if (ret)
+ return ret;
+ oldconf->filter = conf->filter;
+ }
+
+ return inv_icm42607_set_pwr_mgmt0(st, st->conf.gyro.mode, conf->mode,
+ st->conf.temp_en, sleep_ms);
+}
+
int inv_icm42607_set_temp_conf(struct inv_icm42607_state *st, bool enable,
unsigned int *sleep_ms)
{
@@ -398,6 +454,11 @@ int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw
pm_runtime_set_autosuspend_delay(dev, INV_ICM42607_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
+ /* Initialize IIO device for Accel */
+ st->indio_accel = inv_icm42607_accel_init(st);
+ if (IS_ERR(st->indio_accel))
+ return PTR_ERR(st->indio_accel);
+
return 0;
}
EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607");
@@ -409,6 +470,8 @@ static int inv_icm42607_suspend(struct device *dev)
guard(mutex)(&st->lock);
+ st->suspended.accel = st->conf.accel.mode;
+ st->suspended.temp = st->conf.temp_en;
if (pm_runtime_suspended(dev))
return 0;
@@ -432,6 +495,7 @@ static int inv_icm42607_suspend(struct device *dev)
static int inv_icm42607_resume(struct device *dev)
{
struct inv_icm42607_state *st = dev_get_drvdata(dev);
+ struct inv_icm42607_sensor_state *accel_st = iio_priv(st->indio_accel);
int ret;
guard(mutex)(&st->lock);
@@ -443,7 +507,15 @@ static int inv_icm42607_resume(struct device *dev)
if (ret)
return ret;
+ /* restore sensors state, noting gyro still not yet supported. */
+ ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
+ st->suspended.accel,
+ st->suspended.temp, NULL);
+ if (ret)
+ return ret;
+
if (st->fifo.on) {
+ inv_sensors_timestamp_reset(&accel_st->ts);
ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1,
INV_ICM42607_FIFO_CONFIG1_MODE);
if (ret)
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V7 09/11] iio: imu: inv_icm42607: Add Wake on Movement to icm42607
2026-05-15 13:00 [PATCH V7 00/11] Add Invensense ICM42607 Chris Morgan
` (7 preceding siblings ...)
2026-05-15 13:00 ` [PATCH V7 08/11] iio: imu: inv_icm42607: Add Accelerometer for icm42607 Chris Morgan
@ 2026-05-15 13:00 ` Chris Morgan
2026-05-15 13:42 ` sashiko-bot
2026-05-15 19:36 ` Jonathan Cameron
2026-05-15 13:00 ` [PATCH V7 10/11] iio: imu: inv_icm42607: Add Gyroscope " Chris Morgan
2026-05-15 13:00 ` [PATCH V7 11/11] arm64: dts: rockchip: Add icm42607p IMU for RG-DS Chris Morgan
10 siblings, 2 replies; 26+ messages in thread
From: Chris Morgan @ 2026-05-15 13:00 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add support for wake on movement for the icm42607 driver.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 33 ++
.../iio/imu/inv_icm42607/inv_icm42607_accel.c | 304 ++++++++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 267 ++++++++++++++-
3 files changed, 594 insertions(+), 10 deletions(-)
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
index 095f4a495e3f..16f7929d3d0e 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -119,6 +119,14 @@ struct inv_icm42607_suspended {
bool temp;
};
+struct inv_icm42607_apex {
+ unsigned int on;
+ struct {
+ u64 value;
+ bool enable;
+ } wom;
+};
+
/**
* struct inv_icm42607_state - driver state variables
* @lock: lock for serializing multiple registers access.
@@ -133,6 +141,7 @@ struct inv_icm42607_suspended {
* @indio_gyro: gyroscope IIO device.
* @indio_accel: accelerometer IIO device.
* @timestamp: interrupt timestamps.
+ * @apex: APEX (Advanced Pedometer and Event detection) management
* @fifo: FIFO management structure.
* @buffer: data transfer buffer aligned for DMA.
*/
@@ -151,6 +160,7 @@ struct inv_icm42607_state {
struct {
s64 accel;
} timestamp;
+ struct inv_icm42607_apex apex;
struct inv_icm42607_fifo fifo;
__be16 buffer[3] __aligned(IIO_DMA_MINALIGN);
};
@@ -376,6 +386,18 @@ struct inv_icm42607_sensor_state {
#define INV_ICM42607P_WHOAMI 0x60
#define INV_ICM42607_WHOAMI 0x67
+/* Bank 2/3 register access */
+#define INV_ICM42607_BLK_SEL_W 0x79
+#define INV_ICM42607_MADDR_W 0x7A
+#define INV_ICM42607_M_W 0x7B
+#define INV_ICM42607_BLK_SEL_R 0x7C
+#define INV_ICM42607_MADDR_R 0x7D
+#define INV_ICM42607_M_R 0x7E
+
+#define INV_ICM42607_MREG_1 0x00
+#define INV_ICM42607_MREG_2 0x28
+#define INV_ICM42607_MREG_3 0x50
+
/* Sleep times required by the driver */
#define INV_ICM42607_POWER_UP_TIME_US 100000
#define INV_ICM42607_RESET_TIME_MS 1
@@ -384,6 +406,7 @@ struct inv_icm42607_sensor_state {
#define INV_ICM42607_GYRO_STOP_TIME_MS 150
#define INV_ICM42607_TEMP_STARTUP_TIME_MS 14
#define INV_ICM42607_SUSPEND_DELAY_MS 2000
+#define INV_ICM42607_MREG_DELAY_US 10
typedef int (*inv_icm42607_bus_setup)(struct inv_icm42607_state *);
@@ -392,6 +415,9 @@ extern const struct inv_icm42607_hw inv_icm42607_hw_data;
extern const struct inv_icm42607_hw inv_icm42607p_hw_data;
extern const struct dev_pm_ops inv_icm42607_pm_ops;
+int inv_icm42607_mreg_write(struct regmap *map, unsigned int bank,
+ u8 reg, u8 data);
+
const struct iio_mount_matrix *
inv_icm42607_get_mount_matrix(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan);
@@ -405,6 +431,9 @@ int inv_icm42607_set_accel_conf(struct inv_icm42607_state *st,
int inv_icm42607_set_temp_conf(struct inv_icm42607_state *st, bool enable,
unsigned int *sleep_ms);
+int inv_icm42607_enable_wom(struct inv_icm42607_state *st);
+int inv_icm42607_disable_wom(struct inv_icm42607_state *st);
+
int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw *hw,
inv_icm42607_bus_setup bus_setup);
@@ -412,4 +441,8 @@ struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st);
int inv_icm42607_accel_parse_fifo(struct iio_dev *indio_dev);
+void inv_icm42607_accel_handle_events(struct iio_dev *indio_dev,
+ unsigned int status2, unsigned int status3,
+ s64 timestamp);
+
#endif
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
index b354a84fd2ed..504245b0282b 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
@@ -69,6 +69,16 @@ static const struct iio_chan_spec_ext_info inv_icm42607_accel_ext_infos[] = {
{ }
};
+/* WoM event: rising ROC */
+static const struct iio_event_spec inv_icm42607_wom_events[] = {
+ {
+ .type = IIO_EV_TYPE_ROC,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_VALUE),
+ },
+};
+
static const struct iio_chan_spec inv_icm42607_accel_channels[] = {
INV_ICM42607_ACCEL_CHAN(IIO_MOD_X, INV_ICM42607_ACCEL_SCAN_X,
inv_icm42607_accel_ext_infos),
@@ -78,6 +88,8 @@ static const struct iio_chan_spec inv_icm42607_accel_channels[] = {
inv_icm42607_accel_ext_infos),
INV_ICM42607_TEMP_CHAN(INV_ICM42607_ACCEL_SCAN_TEMP),
IIO_CHAN_SOFT_TIMESTAMP(INV_ICM42607_ACCEL_SCAN_TIMESTAMP),
+ INV_ICM42607_ACCEL_EVENT_CHAN(IIO_MOD_X_OR_Y_OR_Z, inv_icm42607_wom_events,
+ ARRAY_SIZE(inv_icm42607_wom_events)),
};
static const struct iio_event_spec inv_icm42607_motion_events[] = {
@@ -209,6 +221,181 @@ static int inv_icm42607_accel_read_sensor(struct iio_dev *indio_dev,
return 0;
}
+static unsigned int inv_icm42607_accel_convert_roc_to_wom(u64 roc,
+ int accel_hz, int accel_uhz)
+{
+ /* 1000/256mg per LSB converted in µm/s² */
+ const unsigned int convert = (9807U * (MICRO / MILLI)) / 256U;
+ u64 value;
+ u64 freq_uhz;
+
+ /* return 0 only if roc is 0 */
+ if (roc == 0)
+ return 0;
+
+ freq_uhz = (u64)accel_hz * MICRO + (u64)accel_uhz;
+ value = div64_u64(roc, (freq_uhz * (u64)convert) / MICRO);
+
+ /* limit value to 8 bits and prevent 0 */
+ return clamp(value, 1, 255);
+}
+
+static u64 inv_icm42607_accel_convert_wom_to_roc(unsigned int threshold,
+ int accel_hz, int accel_uhz)
+{
+ /* 1000/256mg per LSB converted in µm/s² */
+ const unsigned int convert = (9807U * (MICRO / MILLI)) / 256U;
+ u64 value;
+ u64 freq_uhz;
+
+ value = threshold * convert;
+ freq_uhz = (u64)accel_hz * MICRO + (u64)accel_uhz;
+
+ /* compute the differential by multiplying by the frequency */
+ return div_u64(value * freq_uhz, MICRO);
+}
+
+static int inv_icm42607_accel_set_wom_threshold(struct inv_icm42607_state *st,
+ u64 value,
+ int accel_hz, int accel_uhz)
+{
+ unsigned int threshold;
+ int i, ret;
+
+ /* convert roc to wom threshold and convert back to handle clipping */
+ threshold = inv_icm42607_accel_convert_roc_to_wom(value, accel_hz, accel_uhz);
+ value = inv_icm42607_accel_convert_wom_to_roc(threshold, accel_hz, accel_uhz);
+
+ dev_dbg(regmap_get_device(st->map), "wom_threshold: 0x%x\n", threshold);
+
+ st->apex.wom.value = value;
+
+ /* 42607P cannot set WoM threshold. */
+ if (st->hw->whoami == INV_ICM42607P_WHOAMI)
+ return 0;
+
+ /* set accel WoM threshold for the 3 axes */
+ for (i = 0; i < 3; i++) {
+ ret = inv_icm42607_mreg_write(st->map, INV_ICM42607_MREG_2,
+ INV_ICM42607_REG_ACCEL_WOM_X_THR + i,
+ threshold);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int _inv_icm42607_accel_enable_wom(struct iio_dev *indio_dev)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_state *accel_st = iio_priv(indio_dev);
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ unsigned int sleep_ms = 0;
+ int ret;
+
+ scoped_guard(mutex, &st->lock) {
+ /* turn on accel sensor */
+ conf.mode = accel_st->power_mode;
+ conf.filter = accel_st->filter;
+ ret = inv_icm42607_set_accel_conf(st, &conf, &sleep_ms);
+ if (ret)
+ return ret;
+ }
+
+ if (sleep_ms)
+ msleep(sleep_ms);
+
+ scoped_guard(mutex, &st->lock) {
+ ret = inv_icm42607_enable_wom(st);
+ if (ret)
+ return ret;
+ st->apex.on++;
+ st->apex.wom.enable = true;
+ }
+
+ return 0;
+}
+
+static int inv_icm42607_accel_enable_wom(struct iio_dev *indio_dev)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *pdev = regmap_get_device(st->map);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(pdev);
+ if (ret)
+ return ret;
+
+ ret = _inv_icm42607_accel_enable_wom(indio_dev);
+ if (ret) {
+ pm_runtime_put_autosuspend(pdev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int _inv_icm42607_accel_disable_wom(struct iio_dev *indio_dev)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ unsigned int sleep_ms = 0;
+ int ret;
+
+ scoped_guard(mutex, &st->lock) {
+ /*
+ * Consider that turning off WoM is always working to avoid
+ * blocking the chip in on mode and prevent going back to sleep.
+ * If there is an error, the chip will anyway go back to sleep
+ * and the feature will not work anymore.
+ */
+ st->apex.wom.enable = false;
+ st->apex.on--;
+ inv_icm42607_disable_wom(st);
+ /* turn off accel sensor if not used */
+ if (!st->apex.on && !iio_buffer_enabled(indio_dev)) {
+ conf.mode = INV_ICM42607_SENSOR_MODE_OFF;
+ ret = inv_icm42607_set_accel_conf(st, &conf, &sleep_ms);
+ if (ret)
+ return ret;
+ }
+ }
+
+ if (sleep_ms)
+ msleep(sleep_ms);
+
+ return 0;
+}
+
+static int inv_icm42607_accel_disable_wom(struct iio_dev *indio_dev)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *pdev = regmap_get_device(st->map);
+ int ret;
+
+ ret = _inv_icm42607_accel_disable_wom(indio_dev);
+
+ pm_runtime_put_autosuspend(pdev);
+
+ return ret;
+}
+
+void inv_icm42607_accel_handle_events(struct iio_dev *indio_dev,
+ unsigned int status2, unsigned int status3,
+ s64 timestamp)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ u64 ev_code;
+
+ /* handle WoM event */
+ if (st->apex.wom.enable && (status2 & INV_ICM42607_INT_STATUS2_WOM_INT)) {
+ ev_code = IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z,
+ IIO_EV_TYPE_ROC, IIO_EV_DIR_RISING);
+ iio_push_event(indio_dev, ev_code, timestamp);
+ }
+}
+
static const int inv_icm42607_accel_scale_nano[][2] = {
[INV_ICM42607_ACCEL_FS_16G] = { 0, 4788403 },
[INV_ICM42607_ACCEL_FS_8G] = { 0, 2394202 },
@@ -329,6 +516,10 @@ static int inv_icm42607_accel_write_odr(struct iio_dev *indio_dev,
if (ret)
return ret;
+ ret = inv_icm42607_accel_set_wom_threshold(st, st->apex.wom.value, val, val2);
+ if (ret)
+ return ret;
+
inv_icm42607_buffer_update_fifo_period(st);
inv_icm42607_buffer_update_watermark(st);
@@ -475,6 +666,115 @@ static int inv_icm42607_accel_hwfifo_flush(struct iio_dev *indio_dev,
return st->fifo.nb.accel;
}
+static int inv_icm42607_accel_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+
+ /* handle only WoM (roc rising) event */
+ if (type != IIO_EV_TYPE_ROC || dir != IIO_EV_DIR_RISING)
+ return -EINVAL;
+
+ guard(mutex)(&st->lock);
+
+ return st->apex.wom.enable ? 1 : 0;
+}
+
+static int inv_icm42607_accel_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ bool state)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+
+ /* handle only WoM (roc rising) event */
+ if (type != IIO_EV_TYPE_ROC || dir != IIO_EV_DIR_RISING)
+ return -EINVAL;
+
+ scoped_guard(mutex, &st->lock) {
+ if (st->apex.wom.enable == state)
+ return 0;
+ }
+
+ if (state)
+ return inv_icm42607_accel_enable_wom(indio_dev);
+
+ return inv_icm42607_accel_disable_wom(indio_dev);
+}
+
+static int inv_icm42607_accel_read_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ u32 rem;
+
+ /* handle only WoM (roc rising) event value */
+ if (type != IIO_EV_TYPE_ROC || dir != IIO_EV_DIR_RISING)
+ return -EINVAL;
+
+ guard(mutex)(&st->lock);
+
+ /* return value in micro */
+ *val = div_u64_rem(st->apex.wom.value, MICRO, &rem);
+ *val2 = rem;
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int _inv_icm42607_accel_wom_value(struct inv_icm42607_state *st,
+ int val, int val2)
+{
+ u64 value;
+ int accel_hz, accel_uhz;
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ ret = inv_icm42607_accel_read_odr(st, &accel_hz, &accel_uhz);
+ if (ret < 0)
+ return ret;
+
+ value = (u64)val * MICRO + (u64)val2;
+
+ return inv_icm42607_accel_set_wom_threshold(st, value,
+ accel_hz, accel_uhz);
+}
+
+static int inv_icm42607_accel_write_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ int ret;
+
+ /* handle only WoM (roc rising) event value */
+ if (type != IIO_EV_TYPE_ROC || dir != IIO_EV_DIR_RISING)
+ return -EINVAL;
+
+ if (val < 0 || val2 < 0)
+ return -EINVAL;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ ret = _inv_icm42607_accel_wom_value(st, val, val2);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
static const struct iio_info inv_icm42607_accel_info = {
.read_raw = inv_icm42607_accel_read_raw,
.read_avail = inv_icm42607_accel_read_avail,
@@ -483,6 +783,10 @@ static const struct iio_info inv_icm42607_accel_info = {
.update_scan_mode = inv_icm42607_accel_update_scan_mode,
.hwfifo_set_watermark = inv_icm42607_accel_hwfifo_set_watermark,
.hwfifo_flush_to_buffer = inv_icm42607_accel_hwfifo_flush,
+ .read_event_config = inv_icm42607_accel_read_event_config,
+ .write_event_config = inv_icm42607_accel_write_event_config,
+ .read_event_value = inv_icm42607_accel_read_event_value,
+ .write_event_value = inv_icm42607_accel_write_event_value,
};
struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st)
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
index 1e685451f785..8ab8b5f739f2 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -17,6 +17,57 @@
#include "inv_icm42607.h"
#include "inv_icm42607_buffer.h"
+/**
+ * inv_icm42607_mreg_write() - Write value to user bank
+ * @map: device regmap
+ * @bank: mreg register bank
+ * @reg: register in the mreg bank to write to
+ * @data: data to write to register
+ *
+ * Write a byte of data to bank 2 or 3 as defined in the datasheet.
+ * Writes are performed by first writing which bank to write to the
+ * bank write register, then writing the register address, then
+ * writing the data.
+ * Return 0 on success or error on error.
+ */
+int inv_icm42607_mreg_write(struct regmap *map, unsigned int bank,
+ u8 reg, u8 data)
+{
+ const struct device *dev = regmap_get_device(map);
+ struct inv_icm42607_state *st = dev_get_drvdata(dev);
+ u8 buffer[3];
+ int ret;
+
+ /* The ICM42607P does not have additional banks for registers. */
+ if (st->hw->whoami == INV_ICM42607P_WHOAMI)
+ return -EINVAL;
+
+ /* Bank value can only be bank2 or bank3. */
+ if (bank != INV_ICM42607_MREG_2 && bank != INV_ICM42607_MREG_3)
+ return -EINVAL;
+
+ buffer[0] = bank;
+ buffer[1] = reg;
+ buffer[2] = data;
+
+ /*
+ * Write bank, register, and value in one bulk write. Then pause
+ * the required time according to datasheet.
+ */
+ ret = regmap_bulk_write(map, INV_ICM42607_BLK_SEL_W, buffer, 3);
+ fsleep(INV_ICM42607_MREG_DELAY_US);
+ if (ret)
+ return ret;
+
+ /* Switch back to bank1 and pause the required amount of time. */
+ ret = regmap_write(map, INV_ICM42607_BLK_SEL_W, INV_ICM42607_MREG_1);
+ fsleep(INV_ICM42607_MREG_DELAY_US);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static bool inv_icm42607_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -28,6 +79,7 @@ static bool inv_icm42607_is_volatile_reg(struct device *dev, unsigned int reg)
case INV_ICM42607_REG_FIFO_LOST_PKT0 ... INV_ICM42607_REG_APEX_DATA3:
case INV_ICM42607_REG_INT_STATUS_DRDY:
case INV_ICM42607_REG_INT_STATUS ... INV_ICM42607_REG_FIFO_DATA:
+ case INV_ICM42607_BLK_SEL_W ... INV_ICM42607_M_R:
return true;
}
@@ -238,6 +290,38 @@ int inv_icm42607_set_temp_conf(struct inv_icm42607_state *st, bool enable,
sleep_ms);
}
+int inv_icm42607_enable_wom(struct inv_icm42607_state *st)
+{
+ int ret;
+
+ /* enable WoM hardware */
+ ret = regmap_write(st->map, INV_ICM42607_REG_WOM_CONFIG,
+ FIELD_PREP(INV_ICM42607_WOM_CONFIG_INT_DUR_MASK, 1) |
+ INV_ICM42607_WOM_CONFIG_MODE |
+ INV_ICM42607_WOM_CONFIG_EN);
+ if (ret)
+ return ret;
+
+ /* enable WoM interrupt */
+ return regmap_set_bits(st->map, INV_ICM42607_REG_INT_SOURCE1,
+ INV_ICM42607_INT_SOURCE1_WOM_INT1_EN);
+}
+
+int inv_icm42607_disable_wom(struct inv_icm42607_state *st)
+{
+ int ret;
+
+ /* disable WoM interrupt */
+ ret = regmap_clear_bits(st->map, INV_ICM42607_REG_INT_SOURCE1,
+ INV_ICM42607_INT_SOURCE1_WOM_INT1_EN);
+ if (ret)
+ return ret;
+
+ /* disable WoM hardware */
+ return regmap_clear_bits(st->map, INV_ICM42607_REG_WOM_CONFIG,
+ INV_ICM42607_WOM_CONFIG_EN);
+}
+
static int inv_icm42607_set_conf(struct inv_icm42607_state *st,
const struct inv_icm42607_conf *conf)
{
@@ -360,6 +444,114 @@ static int inv_icm42607_setup(struct inv_icm42607_state *st,
return inv_icm42607_set_conf(st, st->hw->conf);
}
+static irqreturn_t inv_icm42607_irq_timestamp(int irq, void *_data)
+{
+ struct inv_icm42607_state *st = _data;
+
+ st->timestamp.accel = iio_get_time_ns(st->indio_accel);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t inv_icm42607_irq_handler(int irq, void *_data)
+{
+ struct inv_icm42607_state *st = _data;
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int status;
+ int ret;
+
+ mutex_lock(&st->lock);
+
+ if (st->apex.on) {
+ u8 buffer[2];
+
+ /* read INT_STATUS2 and INT_STATUS3 in 1 operation */
+ ret = regmap_bulk_read(st->map, INV_ICM42607_REG_INT_STATUS2, buffer, 2);
+ if (ret) {
+ dev_err(dev, "Interrupt status read error %d\n", ret);
+ goto out_unlock;
+ }
+
+ inv_icm42607_accel_handle_events(st->indio_accel, buffer[0], buffer[1],
+ st->timestamp.accel);
+ }
+
+ ret = regmap_read(st->map, INV_ICM42607_REG_INT_STATUS, &status);
+ if (ret) {
+ dev_err(dev, "Interrut status read error %d\n", ret);
+ goto out_unlock;
+ }
+
+ if (status & INV_ICM42607_INT_STATUS_FIFO_FULL)
+ dev_warn(dev, "FIFO full data lost!\n");
+
+ if (status & INV_ICM42607_INT_STATUS_FIFO_THS) {
+ mutex_unlock(&st->lock);
+ ret = inv_icm42607_buffer_fifo_read(st, 0);
+ if (ret) {
+ dev_err(dev, "FIFO read error %d\n", ret);
+ goto out_unlock;
+ }
+
+ mutex_lock(&st->lock);
+ ret = inv_icm42607_buffer_fifo_parse(st);
+ if (ret)
+ dev_err(dev, "FIFO parsing error %d\n", ret);
+ }
+
+out_unlock:
+ mutex_unlock(&st->lock);
+ return IRQ_HANDLED;
+}
+
+/**
+ * inv_icm42607_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_icm42607_irq_init(struct inv_icm42607_state *st, int irq,
+ int irq_type, bool open_drain)
+{
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int val = 0;
+ int ret;
+
+ switch (irq_type) {
+ case IRQF_TRIGGER_RISING:
+ case IRQF_TRIGGER_HIGH:
+ val = INV_ICM42607_INT_CONFIG_INT1_ACTIVE_HIGH;
+ break;
+ default:
+ val = INV_ICM42607_INT_CONFIG_INT1_ACTIVE_LOW;
+ break;
+ }
+
+ switch (irq_type) {
+ case IRQF_TRIGGER_LOW:
+ case IRQF_TRIGGER_HIGH:
+ val |= INV_ICM42607_INT_CONFIG_INT1_LATCHED;
+ break;
+ default:
+ break;
+ }
+
+ if (!open_drain)
+ val |= INV_ICM42607_INT_CONFIG_INT1_PUSH_PULL;
+
+ ret = regmap_write(st->map, INV_ICM42607_REG_INT_CONFIG, val);
+ if (ret)
+ return ret;
+
+ irq_type |= IRQF_ONESHOT;
+ return devm_request_threaded_irq(dev, irq, inv_icm42607_irq_timestamp,
+ inv_icm42607_irq_handler, irq_type,
+ st->hw->name, st);
+}
+
static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st)
{
int ret;
@@ -393,13 +585,18 @@ int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw
struct device *dev = regmap_get_device(regmap);
struct fwnode_handle *fwnode = dev_fwnode(dev);
struct inv_icm42607_state *st;
- int irq;
+ int irq, irq_type;
+ bool open_drain;
int ret;
irq = fwnode_irq_get_byname(fwnode, "INT1");
if (!(irq > 0))
return dev_err_probe(dev, -EINVAL, "Unable to get INT1 interrupt\n");
+ irq_type = irq_get_trigger_type(irq);
+
+ open_drain = device_property_read_bool(dev, "drive-open-drain");
+
st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
if (!st)
return -ENOMEM;
@@ -459,6 +656,11 @@ int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw
if (IS_ERR(st->indio_accel))
return PTR_ERR(st->indio_accel);
+ /* Initialize interrupt handling */
+ ret = inv_icm42607_irq_init(st, irq, irq_type, open_drain);
+ if (ret)
+ return ret;
+
return 0;
}
EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607");
@@ -466,6 +668,9 @@ EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607");
static int inv_icm42607_suspend(struct device *dev)
{
struct inv_icm42607_state *st = dev_get_drvdata(dev);
+ struct device *accel_dev;
+ bool wakeup;
+ int accel_conf;
int ret;
guard(mutex)(&st->lock);
@@ -482,12 +687,36 @@ static int inv_icm42607_suspend(struct device *dev)
return ret;
}
- ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
- INV_ICM42607_SENSOR_MODE_OFF,
- false, NULL);
- if (ret)
- return ret;
- inv_icm42607_disable_vddio_reg(st);
+ /* keep chip on and wake-up capable if APEX and wakeup on */
+ accel_dev = &st->indio_accel->dev;
+ wakeup = st->apex.on && device_may_wakeup(accel_dev);
+ if (wakeup) {
+ /* keep accel on and setup irq for wakeup */
+ accel_conf = st->conf.accel.mode;
+ enable_irq_wake(st->irq);
+ disable_irq_nosync(st->irq);
+ ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
+ accel_conf, false, NULL);
+ if (ret) {
+ enable_irq(st->irq);
+ disable_irq_wake(st->irq);
+ return ret;
+ }
+ } else {
+ /* disable APEX features and accel if wakeup disabled */
+ if (st->apex.wom.enable) {
+ ret = inv_icm42607_disable_wom(st);
+ if (ret)
+ return ret;
+ }
+ accel_conf = INV_ICM42607_SENSOR_MODE_OFF;
+ ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
+ INV_ICM42607_SENSOR_MODE_OFF,
+ false, NULL);
+ if (ret)
+ return ret;
+ inv_icm42607_disable_vddio_reg(st);
+ }
return 0;
}
@@ -496,6 +725,8 @@ static int inv_icm42607_resume(struct device *dev)
{
struct inv_icm42607_state *st = dev_get_drvdata(dev);
struct inv_icm42607_sensor_state *accel_st = iio_priv(st->indio_accel);
+ struct device *accel_dev;
+ bool wakeup;
int ret;
guard(mutex)(&st->lock);
@@ -503,9 +734,18 @@ static int inv_icm42607_resume(struct device *dev)
if (pm_runtime_suspended(dev))
return 0;
- ret = inv_icm42607_enable_vddio_reg(st);
- if (ret)
- return ret;
+ /* check wakeup capability */
+ accel_dev = &st->indio_accel->dev;
+ wakeup = st->apex.on && device_may_wakeup(accel_dev);
+ /* restore irq state */
+ if (wakeup) {
+ enable_irq(st->irq);
+ disable_irq_wake(st->irq);
+ } else {
+ ret = inv_icm42607_enable_vddio_reg(st);
+ if (ret)
+ return ret;
+ }
/* restore sensors state, noting gyro still not yet supported. */
ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
@@ -514,6 +754,13 @@ static int inv_icm42607_resume(struct device *dev)
if (ret)
return ret;
+ /* restore APEX features if disabled */
+ if (!wakeup && st->apex.wom.enable) {
+ ret = inv_icm42607_enable_wom(st);
+ if (ret)
+ return ret;
+ }
+
if (st->fifo.on) {
inv_sensors_timestamp_reset(&accel_st->ts);
ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1,
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V7 10/11] iio: imu: inv_icm42607: Add Gyroscope to icm42607
2026-05-15 13:00 [PATCH V7 00/11] Add Invensense ICM42607 Chris Morgan
` (8 preceding siblings ...)
2026-05-15 13:00 ` [PATCH V7 09/11] iio: imu: inv_icm42607: Add Wake on Movement to icm42607 Chris Morgan
@ 2026-05-15 13:00 ` Chris Morgan
2026-05-15 13:37 ` sashiko-bot
2026-05-15 19:44 ` Jonathan Cameron
2026-05-15 13:00 ` [PATCH V7 11/11] arm64: dts: rockchip: Add icm42607p IMU for RG-DS Chris Morgan
10 siblings, 2 replies; 26+ messages in thread
From: Chris Morgan @ 2026-05-15 13:00 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add gyroscope functions to the icm42607 driver.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/inv_icm42607/Makefile | 1 +
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 9 +
.../imu/inv_icm42607/inv_icm42607_buffer.c | 26 +-
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 56 +-
.../iio/imu/inv_icm42607/inv_icm42607_gyro.c | 545 ++++++++++++++++++
5 files changed, 633 insertions(+), 4 deletions(-)
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile
index e908d77c4219..fc66e580fe99 100644
--- a/drivers/iio/imu/inv_icm42607/Makefile
+++ b/drivers/iio/imu/inv_icm42607/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o
inv-icm42607-y += inv_icm42607_core.o
+inv-icm42607-y += inv_icm42607_gyro.o
inv-icm42607-y += inv_icm42607_accel.o
inv-icm42607-y += inv_icm42607_buffer.o
inv-icm42607-y += inv_icm42607_temp.o
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
index 16f7929d3d0e..158a0385f0b9 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -158,6 +158,7 @@ struct inv_icm42607_state {
struct iio_dev *indio_gyro;
struct iio_dev *indio_accel;
struct {
+ s64 gyro;
s64 accel;
} timestamp;
struct inv_icm42607_apex apex;
@@ -428,6 +429,10 @@ int inv_icm42607_set_accel_conf(struct inv_icm42607_state *st,
struct inv_icm42607_sensor_conf *conf,
unsigned int *sleep_ms);
+int inv_icm42607_set_gyro_conf(struct inv_icm42607_state *st,
+ struct inv_icm42607_sensor_conf *conf,
+ unsigned int *sleep_ms);
+
int inv_icm42607_set_temp_conf(struct inv_icm42607_state *st, bool enable,
unsigned int *sleep_ms);
@@ -437,6 +442,10 @@ int inv_icm42607_disable_wom(struct inv_icm42607_state *st);
int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw *hw,
inv_icm42607_bus_setup bus_setup);
+struct iio_dev *inv_icm42607_gyro_init(struct inv_icm42607_state *st);
+
+int inv_icm42607_gyro_parse_fifo(struct iio_dev *indio_dev);
+
struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st);
int inv_icm42607_accel_parse_fifo(struct iio_dev *indio_dev);
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
index 4371f428cc3f..771120fa8ebf 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
@@ -384,7 +384,9 @@ static int inv_icm42607_buffer_postdisable(struct iio_dev *indio_dev)
goto out_unlock;
conf.mode = INV_ICM42607_SENSOR_MODE_OFF;
- if (sensor != INV_ICM42607_SENSOR_GYRO)
+ if (sensor == INV_ICM42607_SENSOR_GYRO)
+ ret = inv_icm42607_set_gyro_conf(st, &conf, &sleep_sensor);
+ else
ret = inv_icm42607_set_accel_conf(st, &conf, &sleep_sensor);
if (ret)
goto out_unlock;
@@ -476,6 +478,7 @@ int inv_icm42607_buffer_fifo_read(struct inv_icm42607_state *st,
int inv_icm42607_buffer_fifo_parse(struct inv_icm42607_state *st)
{
+ struct inv_icm42607_sensor_state *gyro_st = iio_priv(st->indio_gyro);
struct inv_icm42607_sensor_state *accel_st = iio_priv(st->indio_accel);
struct inv_sensors_timestamp *ts;
int ret;
@@ -483,6 +486,16 @@ int inv_icm42607_buffer_fifo_parse(struct inv_icm42607_state *st)
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_icm42607_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;
@@ -499,9 +512,10 @@ int inv_icm42607_buffer_fifo_parse(struct inv_icm42607_state *st)
int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
unsigned int count)
{
+ struct inv_icm42607_sensor_state *gyro_st = iio_priv(st->indio_gyro);
struct inv_icm42607_sensor_state *accel_st = iio_priv(st->indio_accel);
struct inv_sensors_timestamp *ts;
- s64 accel_ts;
+ s64 gyro_ts, accel_ts;
int ret;
accel_ts = iio_get_time_ns(st->indio_accel);
@@ -513,6 +527,14 @@ int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
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_icm42607_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);
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
index 8ab8b5f739f2..9a0e1e106b38 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -272,6 +272,49 @@ int inv_icm42607_set_accel_conf(struct inv_icm42607_state *st,
st->conf.temp_en, sleep_ms);
}
+int inv_icm42607_set_gyro_conf(struct inv_icm42607_state *st,
+ struct inv_icm42607_sensor_conf *conf,
+ unsigned int *sleep_ms)
+{
+ struct inv_icm42607_sensor_conf *oldconf = &st->conf.gyro;
+ unsigned int val;
+ int ret;
+
+ 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;
+
+ if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) {
+ val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK,
+ conf->fs);
+ val |= FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_ODR_MASK,
+ conf->odr);
+ ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val);
+ if (ret)
+ return ret;
+ oldconf->fs = conf->fs;
+ oldconf->odr = conf->odr;
+ }
+
+ if (conf->filter != oldconf->filter) {
+ val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG1_FILTER_MASK,
+ conf->filter);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_GYRO_CONFIG1,
+ INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, val);
+ if (ret)
+ return ret;
+ oldconf->filter = conf->filter;
+ }
+
+ return inv_icm42607_set_pwr_mgmt0(st, conf->mode, st->conf.accel.mode,
+ st->conf.temp_en, sleep_ms);
+}
+
int inv_icm42607_set_temp_conf(struct inv_icm42607_state *st, bool enable,
unsigned int *sleep_ms)
{
@@ -448,6 +491,7 @@ static irqreturn_t inv_icm42607_irq_timestamp(int irq, void *_data)
{
struct inv_icm42607_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;
@@ -656,6 +700,11 @@ int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw
if (IS_ERR(st->indio_accel))
return PTR_ERR(st->indio_accel);
+ /* Initialize IIO device for Gyro */
+ st->indio_gyro = inv_icm42607_gyro_init(st);
+ if (IS_ERR(st->indio_gyro))
+ return PTR_ERR(st->indio_gyro);
+
/* Initialize interrupt handling */
ret = inv_icm42607_irq_init(st, irq, irq_type, open_drain);
if (ret)
@@ -675,6 +724,7 @@ static int inv_icm42607_suspend(struct device *dev)
guard(mutex)(&st->lock);
+ st->suspended.gyro = st->conf.gyro.mode;
st->suspended.accel = st->conf.accel.mode;
st->suspended.temp = st->conf.temp_en;
if (pm_runtime_suspended(dev))
@@ -724,6 +774,7 @@ static int inv_icm42607_suspend(struct device *dev)
static int inv_icm42607_resume(struct device *dev)
{
struct inv_icm42607_state *st = dev_get_drvdata(dev);
+ struct inv_icm42607_sensor_state *gyro_st = iio_priv(st->indio_gyro);
struct inv_icm42607_sensor_state *accel_st = iio_priv(st->indio_accel);
struct device *accel_dev;
bool wakeup;
@@ -747,8 +798,8 @@ static int inv_icm42607_resume(struct device *dev)
return ret;
}
- /* restore sensors state, noting gyro still not yet supported. */
- ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
+ /* restore sensors state */
+ ret = inv_icm42607_set_pwr_mgmt0(st, st->suspended.gyro,
st->suspended.accel,
st->suspended.temp, NULL);
if (ret)
@@ -762,6 +813,7 @@ static int inv_icm42607_resume(struct device *dev)
}
if (st->fifo.on) {
+ inv_sensors_timestamp_reset(&gyro_st->ts);
inv_sensors_timestamp_reset(&accel_st->ts);
ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1,
INV_ICM42607_FIFO_CONFIG1_MODE);
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
new file mode 100644
index 000000000000..5a5fd13ba64e
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/iio/buffer.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/iio/common/inv_sensors_timestamp.h>
+#include "inv_icm42607.h"
+#include "inv_icm42607_temp.h"
+#include "inv_icm42607_buffer.h"
+
+#define INV_ICM42607_GYRO_CHAN(_modifier, _index, _ext_info) \
+{ \
+ .type = IIO_ANGL_VEL, \
+ .modified = 1, \
+ .channel2 = _modifier, \
+ .info_mask_separate = \
+ BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+ .ext_info = _ext_info, \
+}
+
+enum inv_icm42607_gyro_scan {
+ INV_ICM42607_GYRO_SCAN_X,
+ INV_ICM42607_GYRO_SCAN_Y,
+ INV_ICM42607_GYRO_SCAN_Z,
+ INV_ICM42607_GYRO_SCAN_TEMP,
+ INV_ICM42607_GYRO_SCAN_TIMESTAMP,
+};
+
+static const struct iio_chan_spec_ext_info inv_icm42607_gyro_ext_infos[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42607_get_mount_matrix),
+ { },
+};
+
+static const struct iio_chan_spec inv_icm42607_gyro_channels[] = {
+ INV_ICM42607_GYRO_CHAN(IIO_MOD_X, INV_ICM42607_GYRO_SCAN_X,
+ inv_icm42607_gyro_ext_infos),
+ INV_ICM42607_GYRO_CHAN(IIO_MOD_Y, INV_ICM42607_GYRO_SCAN_Y,
+ inv_icm42607_gyro_ext_infos),
+ INV_ICM42607_GYRO_CHAN(IIO_MOD_Z, INV_ICM42607_GYRO_SCAN_Z,
+ inv_icm42607_gyro_ext_infos),
+ INV_ICM42607_TEMP_CHAN(INV_ICM42607_GYRO_SCAN_TEMP),
+ IIO_CHAN_SOFT_TIMESTAMP(INV_ICM42607_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_icm42607_gyro_buffer {
+ struct inv_icm42607_fifo_sensor_data gyro;
+ s16 temp;
+ aligned_s64 timestamp;
+};
+
+#define INV_ICM42607_SCAN_MASK_GYRO_3AXIS \
+ (BIT(INV_ICM42607_GYRO_SCAN_X) | \
+ BIT(INV_ICM42607_GYRO_SCAN_Y) | \
+ BIT(INV_ICM42607_GYRO_SCAN_Z))
+
+#define INV_ICM42607_SCAN_MASK_TEMP BIT(INV_ICM42607_GYRO_SCAN_TEMP)
+
+static const unsigned long inv_icm42607_gyro_scan_masks[] = {
+ INV_ICM42607_SCAN_MASK_GYRO_3AXIS,
+ INV_ICM42607_SCAN_MASK_TEMP,
+ INV_ICM42607_SCAN_MASK_GYRO_3AXIS | INV_ICM42607_SCAN_MASK_TEMP,
+ 0
+};
+
+/* enable gyroscope sensor and FIFO write */
+static int inv_icm42607_gyro_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ unsigned int fifo_en = 0;
+ unsigned int sleep_gyro = 0;
+ unsigned int sleep_temp = 0;
+ int ret;
+
+ mutex_lock(&st->lock);
+
+ if (*scan_mask & INV_ICM42607_SCAN_MASK_TEMP) {
+ /* enable temp sensor */
+ ret = inv_icm42607_set_temp_conf(st, true, &sleep_temp);
+ if (ret)
+ goto out_unlock;
+ fifo_en |= INV_ICM42607_SENSOR_TEMP;
+ }
+
+ if (*scan_mask & INV_ICM42607_SCAN_MASK_GYRO_3AXIS) {
+ /* enable gyro sensor */
+ conf.mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE;
+ ret = inv_icm42607_set_gyro_conf(st, &conf, &sleep_gyro);
+ if (ret)
+ goto out_unlock;
+ fifo_en |= INV_ICM42607_SENSOR_GYRO;
+ }
+
+ /* update data FIFO write */
+ ret = inv_icm42607_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
+ if (ret)
+ goto out_unlock;
+
+out_unlock:
+ mutex_unlock(&st->lock);
+ /* sleep maximum required time */
+ msleep(max(sleep_gyro, sleep_temp));
+ return ret;
+}
+
+static int inv_icm42607_gyro_read_sensor(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ s16 *val)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_state *gyro_st = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ unsigned int reg;
+ __be16 *data;
+ int ret;
+
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ reg = INV_ICM42607_REG_GYRO_DATA_X1;
+ break;
+ case IIO_MOD_Y:
+ reg = INV_ICM42607_REG_GYRO_DATA_Y1;
+ break;
+ case IIO_MOD_Z:
+ reg = INV_ICM42607_REG_GYRO_DATA_Z1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
+
+ guard(mutex)(&st->lock);
+
+ /* enable gyro sensor */
+ conf.mode = gyro_st->power_mode;
+ ret = inv_icm42607_set_gyro_conf(st, &conf, NULL);
+ if (ret)
+ return ret;
+
+ /* read gyro register data */
+ data = &st->buffer[0];
+ ret = regmap_bulk_read(st->map, reg, data, sizeof(*data));
+ if (ret)
+ return ret;
+
+ *val = be16_to_cpup(data);
+ if (*val == INV_ICM42607_DATA_INVALID)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const int inv_icm42607_gyro_scale_nano[][2] = {
+ [INV_ICM42607_GYRO_FS_2000DPS] = { 0, 1065264 },
+ [INV_ICM42607_GYRO_FS_1000DPS] = { 0, 532632 },
+ [INV_ICM42607_GYRO_FS_500DPS] = { 0, 266316 },
+ [INV_ICM42607_GYRO_FS_250DPS] = { 0, 133158 },
+};
+
+static int inv_icm42607_gyro_read_scale(struct iio_dev *indio_dev,
+ int *val, int *val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ unsigned int idx;
+
+ idx = st->conf.gyro.fs;
+
+ *val = inv_icm42607_gyro_scale_nano[idx][0];
+ *val2 = inv_icm42607_gyro_scale_nano[idx][1];
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int inv_icm42607_gyro_write_scale(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ size_t scales_len = ARRAY_SIZE(inv_icm42607_gyro_scale_nano);
+
+ for (idx = 0; idx < scales_len; idx++) {
+ if (val == inv_icm42607_gyro_scale_nano[idx][0] &&
+ val2 == inv_icm42607_gyro_scale_nano[idx][1])
+ break;
+ }
+ if (idx >= scales_len)
+ return -EINVAL;
+
+ conf.fs = idx;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
+
+ guard(mutex)(&st->lock);
+
+ return inv_icm42607_set_gyro_conf(st, &conf, NULL);
+}
+
+static const int inv_icm42607_gyro_odr[][2] = {
+ [INV_ICM42607_ODR_12_5HZ] = { 12, 500000 },
+ [INV_ICM42607_ODR_25HZ] = { 25, 0 },
+ [INV_ICM42607_ODR_50HZ] = { 50, 0 },
+ [INV_ICM42607_ODR_100HZ] = { 100, 0 },
+ [INV_ICM42607_ODR_200HZ] = { 200, 0 },
+ [INV_ICM42607_ODR_400HZ] = { 400, 0 },
+ [INV_ICM42607_ODR_800HZ] = { 800, 0 },
+ [INV_ICM42607_ODR_1600HZ] = { 1600, 0 },
+};
+
+static int inv_icm42607_gyro_read_odr(struct inv_icm42607_state *st,
+ int *val, int *val2)
+{
+ unsigned int odr;
+ unsigned int i;
+
+ odr = st->conf.gyro.odr;
+
+ for (i = 5; i < ARRAY_SIZE(inv_icm42607_gyro_odr); ++i) {
+ if (i == odr)
+ break;
+ }
+ if (i >= ARRAY_SIZE(inv_icm42607_gyro_odr))
+ return -EINVAL;
+
+ *val = inv_icm42607_gyro_odr[i][0];
+ *val2 = inv_icm42607_gyro_odr[i][1];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int inv_icm42607_gyro_write_odr(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_state *gyro_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &gyro_st->ts;
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ int ret;
+
+ for (idx = 5; idx < ARRAY_SIZE(inv_icm42607_gyro_odr); ++idx) {
+ if (val == inv_icm42607_gyro_odr[idx][0] &&
+ val2 == inv_icm42607_gyro_odr[idx][1])
+ break;
+ }
+ if (idx >= ARRAY_SIZE(inv_icm42607_gyro_odr))
+ return -EINVAL;
+
+ conf.odr = idx;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
+
+ guard(mutex)(&st->lock);
+
+ ret = inv_sensors_timestamp_update_odr(ts, inv_icm42607_odr_to_period(conf.odr),
+ iio_buffer_enabled(indio_dev));
+ if (ret)
+ return ret;
+
+ ret = inv_icm42607_set_gyro_conf(st, &conf, NULL);
+ if (ret)
+ return ret;
+ inv_icm42607_buffer_update_fifo_period(st);
+ inv_icm42607_buffer_update_watermark(st);
+
+ return 0;
+}
+
+static int inv_icm42607_gyro_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ s16 data;
+ int ret;
+
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ break;
+ case IIO_TEMP:
+ return inv_icm42607_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_icm42607_gyro_read_sensor(indio_dev, chan, &data);
+ iio_device_release_direct(indio_dev);
+ if (ret)
+ return ret;
+ *val = data;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ return inv_icm42607_gyro_read_scale(indio_dev, val, val2);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm42607_gyro_read_odr(st, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_gyro_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals,
+ int *type, int *length, long mask)
+{
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *vals = (const int *)inv_icm42607_gyro_scale_nano;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *length = ARRAY_SIZE(inv_icm42607_gyro_scale_nano) * 2;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = (const int *)inv_icm42607_gyro_odr;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *length = ARRAY_SIZE(inv_icm42607_gyro_odr) * 2;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_gyro_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ 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_icm42607_gyro_write_scale(indio_dev, val, val2);
+ iio_device_release_direct(indio_dev);
+ return ret;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm42607_gyro_write_odr(indio_dev, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_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;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_gyro_hwfifo_set_watermark(struct iio_dev *indio_dev,
+ unsigned int val)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+
+ guard(mutex)(&st->lock);
+
+ st->fifo.watermark.gyro = val;
+ return inv_icm42607_buffer_update_watermark(st);
+}
+
+static int inv_icm42607_gyro_hwfifo_flush(struct iio_dev *indio_dev,
+ unsigned int count)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ int ret;
+
+ if (count == 0)
+ return 0;
+
+ /* Make sure we don't try to count more than the buffer can hold. */
+ count = min(count, sizeof(st->fifo.data));
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
+
+ ret = inv_icm42607_buffer_hwfifo_flush(st, count);
+ if (ret)
+ return ret;
+
+ return st->fifo.nb.gyro;
+}
+
+static const struct iio_info inv_icm42607_gyro_info = {
+ .read_raw = inv_icm42607_gyro_read_raw,
+ .read_avail = inv_icm42607_gyro_read_avail,
+ .write_raw = inv_icm42607_gyro_write_raw,
+ .write_raw_get_fmt = inv_icm42607_gyro_write_raw_get_fmt,
+ .update_scan_mode = inv_icm42607_gyro_update_scan_mode,
+ .hwfifo_set_watermark = inv_icm42607_gyro_hwfifo_set_watermark,
+ .hwfifo_flush_to_buffer = inv_icm42607_gyro_hwfifo_flush,
+};
+
+struct iio_dev *inv_icm42607_gyro_init(struct inv_icm42607_state *st)
+{
+ struct device *dev = regmap_get_device(st->map);
+ const char *name;
+ struct inv_icm42607_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->hw->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->power_mode = INV_ICM42607_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_icm42607_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_icm42607_gyro_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = inv_icm42607_gyro_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_icm42607_gyro_channels);
+ indio_dev->available_scan_masks = inv_icm42607_gyro_scan_masks;
+ indio_dev->setup_ops = &inv_icm42607_buffer_ops;
+
+ ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
+ &inv_icm42607_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_icm42607_gyro_parse_fifo(struct iio_dev *indio_dev)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_state *gyro_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &gyro_st->ts;
+ ssize_t i, size;
+ unsigned int no;
+ const void *accel, *gyro, *timestamp;
+ const s8 *temp;
+ unsigned int odr;
+ s64 ts_val;
+ struct inv_icm42607_gyro_buffer buffer = { };
+
+ /* parse all fifo packets */
+ for (i = 0, no = 0; i < st->fifo.count; i += size, ++no) {
+ size = inv_icm42607_fifo_decode_packet(&st->fifo.data[i],
+ &accel, &gyro, &temp, ×tamp, &odr);
+ /* quit if error or FIFO is empty */
+ if (size <= 0)
+ return size;
+
+ /* If the packet size could cause us to overflow, return. */
+ if (i + size <= st->fifo.count)
+ return -EIO;
+
+ /* skip packet if no gyro data or data is invalid */
+ if (gyro == NULL || !inv_icm42607_fifo_is_data_valid(gyro))
+ continue;
+
+ /* update odr */
+ if (odr & INV_ICM42607_SENSOR_GYRO)
+ inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
+ st->fifo.nb.total, no);
+
+ 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_ts(indio_dev, &buffer,
+ sizeof(buffer), ts_val);
+ }
+
+ return 0;
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH V7 11/11] arm64: dts: rockchip: Add icm42607p IMU for RG-DS
2026-05-15 13:00 [PATCH V7 00/11] Add Invensense ICM42607 Chris Morgan
` (9 preceding siblings ...)
2026-05-15 13:00 ` [PATCH V7 10/11] iio: imu: inv_icm42607: Add Gyroscope " Chris Morgan
@ 2026-05-15 13:00 ` Chris Morgan
10 siblings, 0 replies; 26+ messages in thread
From: Chris Morgan @ 2026-05-15 13:00 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add the Invensense ICM42607P IMU for the Anbernic RG-DS. Mount-matrix
was tested with iio-sensor-proxy and reports correct orientation.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
.../dts/rockchip/rk3568-anbernic-rg-ds.dts | 20 ++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/rockchip/rk3568-anbernic-rg-ds.dts b/arch/arm64/boot/dts/rockchip/rk3568-anbernic-rg-ds.dts
index 8d906ab02c5f..875ca884deca 100644
--- a/arch/arm64/boot/dts/rockchip/rk3568-anbernic-rg-ds.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3568-anbernic-rg-ds.dts
@@ -871,7 +871,18 @@ aw87391_pa_r: audio-codec@5b {
sound-name-prefix = "Right Amp";
};
- /* invensense,icm42607p at 0x68 */
+ icm42607p: imu@68 {
+ compatible = "invensense,icm42607p";
+ reg = <0x68>;
+ interrupt-names = "INT1";
+ interrupt-parent = <&gpio0>;
+ interrupts = <RK_PD6 IRQ_TYPE_EDGE_FALLING>;
+ mount-matrix = "-1", "0", "0",
+ "0", "1", "0",
+ "0", "0", "-1";
+ pinctrl-0 = <&accel_irq>;
+ pinctrl-names = "default";
+ };
};
&i2c3 {
@@ -932,6 +943,13 @@ &i2s1_8ch {
};
&pinctrl {
+ accel {
+ accel_irq: accel-irq {
+ rockchip,pins =
+ <0 RK_PD6 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
gpio-keys {
vol_keys_l: vol-keys_l {
rockchip,pins =
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH V7 03/11] iio: imu: inv_icm42607: Add inv_icm42607 Core Driver
2026-05-15 13:00 ` [PATCH V7 03/11] iio: imu: inv_icm42607: Add inv_icm42607 Core Driver Chris Morgan
@ 2026-05-15 13:23 ` sashiko-bot
2026-05-15 18:31 ` Jonathan Cameron
1 sibling, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-05-15 13:23 UTC (permalink / raw)
To: Chris Morgan; +Cc: conor+dt, krzk+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Overriding `fwnode_irq_get_byname` return value breaks probe deferral.
- [Medium] Permissive WHOAMI check allows successful probe of non-existent devices on floating SPI busses.
--
commit 4cf92e828a7378ea0e258610a5cbe3601aaf01a0
Author: Chris Morgan <macromorgan@hotmail.com>
iio: imu: inv_icm42607: Add inv_icm42607 Core Driver
This commit adds the core component of the new inv_icm42607 driver. It
includes setup functions and the full register definition in the header file.
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> new file mode 100644
> index 0000000000000..9784709319b92
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
[ ... ]
> +static int inv_icm42607_setup(struct inv_icm42607_state *st,
> + inv_icm42607_bus_setup bus_setup)
> +{
> + const struct device *dev = regmap_get_device(st->map);
> + unsigned int val;
> + int ret;
> +
> + ret = regmap_read(st->map, INV_ICM42607_REG_WHOAMI, &val);
> + if (ret)
> + return ret;
> +
> + if (val != st->hw->whoami)
> + dev_warn(dev, "invalid whoami %#02x expected %#02x (%s)\n",
> + val, st->hw->whoami, st->hw->name);
Should the WHOAMI check fail the probe if the device returns completely
invalid values like 0xff or 0x00?
If the device is not present and the SPI MISO line is floating high,
regmap_read() will return 0xff for the WHOAMI register. Because this check
only issues a warning, the driver continues the probe sequence.
> +
> + ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET,
> + INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET);
> + if (ret)
> + return ret;
> +
> + fsleep(1000);
> +
> + ret = regmap_read_poll_timeout(st->map, INV_ICM42607_REG_INT_STATUS,
> + val, val & INV_ICM42607_INT_STATUS_RESET_DONE,
> + INV_ICM42607_RESET_TIME_MS * 100,
> + INV_ICM42607_RESET_TIME_MS * 1000);
Later, when polling for the reset completion here, the condition
(val & INV_ICM42607_INT_STATUS_RESET_DONE) checks for BIT(4). A floating bus
returning 0xff will immediately evaluate to true, allowing the probe to
succeed. This could result in a phantom IIO device being created that only
returns garbage data.
[ ... ]
> +int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw *hw,
> + inv_icm42607_bus_setup bus_setup)
> +{
> + struct device *dev = regmap_get_device(regmap);
> + struct fwnode_handle *fwnode = dev_fwnode(dev);
> + struct inv_icm42607_state *st;
> + int irq;
> + int ret;
> +
> + irq = fwnode_irq_get_byname(fwnode, "INT1");
> + if (!(irq > 0))
> + return dev_err_probe(dev, -EINVAL, "Unable to get INT1 interrupt\n");
Does overriding the error code from fwnode_irq_get_byname() break probe
deferral?
If the interrupt controller is not yet ready, fwnode_irq_get_byname() will
return -EPROBE_DEFER. By unconditionally squashing the return value into
-EINVAL when calling dev_err_probe(), the kernel will permanently fail to
probe the device instead of deferring it. Could we propagate the actual irq
error code instead?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515130018.237378-1-macroalpha82@gmail.com?part=3
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V7 08/11] iio: imu: inv_icm42607: Add Accelerometer for icm42607
2026-05-15 13:00 ` [PATCH V7 08/11] iio: imu: inv_icm42607: Add Accelerometer for icm42607 Chris Morgan
@ 2026-05-15 13:34 ` sashiko-bot
2026-05-15 19:29 ` Jonathan Cameron
1 sibling, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-05-15 13:34 UTC (permalink / raw)
To: Chris Morgan; +Cc: robh, devicetree, krzk+dt, conor+dt
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] Reversed bounds check in FIFO parser causes valid packets to be dropped and out-of-bounds reads
- [High] Unprotected lockless access to shared FIFO state leads to data races and corruption
- [High] Missing packet bounds check in `inv_icm42607_buffer_fifo_read` allows out-of-bounds memory access
- [High] Unsupported TEMP-only scan mask causes silent data drops and userspace deadlock
- [Medium] Reserved array elements exposed as zeroed sampling frequencies in userspace
--
commit 7b72703c452d419fa91a97227176f51c49a6b7cb
Author: Chris Morgan <macromorgan@hotmail.com>
iio: imu: inv_icm42607: Add Accelerometer for icm42607
This commit adds accelerometer sensor support to the icm42607 driver. It
introduces device configuration, power management, timestamp handling, and data
parsing from the hardware FIFO. It also exposes available sampling frequencies
and scales to userspace.
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
> new file mode 100644
> index 0000000000000..b354a84fd2ed4
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
[ ... ]
> +static const unsigned long inv_icm42607_accel_scan_masks[] = {
> + INV_ICM42607_SCAN_MASK_ACCEL_3AXIS,
> + INV_ICM42607_SCAN_MASK_TEMP,
> + INV_ICM42607_SCAN_MASK_ACCEL_3AXIS | INV_ICM42607_SCAN_MASK_TEMP,
> + 0
> +};
Does offering INV_ICM42607_SCAN_MASK_TEMP by itself cause issues? If userspace
requests only the temperature channel, inv_icm42607_accel_update_scan_mode()
enables only the TEMP sensor. If the hardware generates packets without ACCEL
or GYRO headers, inv_icm42607_fifo_decode_packet() returns -EINVAL.
Even if it generates an ACCEL packet, inv_icm42607_accel_parse_fifo() explicitly
discards packets lacking accelerometer data. Would this cause the TEMP data to be
silently dropped, potentially leaving userspace applications deadlocked waiting
for data?
[ ... ]
> +static int inv_icm42607_accel_read_avail(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + const int **vals,
> + int *type, int *length, long mask)
> +{
> + if (chan->type != IIO_ACCEL)
> + return -EINVAL;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_SCALE:
> + *vals = (const int *)inv_icm42607_accel_scale_nano;
> + *type = IIO_VAL_INT_PLUS_NANO;
> + *length = ARRAY_SIZE(inv_icm42607_accel_scale_nano) * 2;
> + return IIO_AVAIL_LIST;
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + *vals = (const int *)inv_icm42607_accel_odr;
> + *type = IIO_VAL_INT_PLUS_MICRO;
> + *length = ARRAY_SIZE(inv_icm42607_accel_odr) * 2;
> + return IIO_AVAIL_LIST;
Can this expose uninitialized array elements to userspace? Because
inv_icm42607_accel_odr has indices 0-5 reserved and implicitly initialized to
zero, passing the full ARRAY_SIZE() could expose those 0.000000 sampling
frequencies as available to userspace.
[ ... ]
> + /* parse all fifo packets */
> + for (i = 0, no = 0; i < st->fifo.count; i += size, ++no) {
> + size = inv_icm42607_fifo_decode_packet(&st->fifo.data[i],
> + &accel, &gyro, &temp, ×tamp, &odr);
> + /* quit if error or FIFO is empty */
> + if (size <= 0)
> + return size;
> +
> + /* If the packet size could cause us to overflow, return. */
> + if (i + size <= st->fifo.count)
> + return -EIO;
Is this condition reversed? If i + size <= st->fifo.count, the packet fits
within the valid buffer limit, but the parser returns -EIO and valid packets
are dropped. Conversely, if the packet exceeds the buffer boundaries
(i + size > st->fifo.count), it evaluates to false, allowing the parser to
read out-of-bounds kernel memory.
> +
> + /* skip packet if no accel data or data is invalid */
> + if (accel == NULL || !inv_icm42607_fifo_is_data_valid(accel))
> + continue;
[ ... ]
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> index 74e5213d92671..4371f428cc3f8 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
[ ... ]
> @@ -440,7 +460,7 @@ int inv_icm42607_buffer_fifo_read(struct inv_icm42607_state *st,
> /* compute number of samples for each sensor */
> for (i = 0; i < st->fifo.count; i += size) {
> size = inv_icm42607_fifo_decode_packet(&st->fifo.data[i],
> - &accel, &gyro, &temp, ×tamp);
> + &accel, &gyro, &temp, ×tamp, &odr);
> /* Make sure the size is at least 1 valid packet. */
> if (size < INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE)
> break;
Could this code access memory out-of-bounds? If the hardware returns a
partial packet at the very end of the st->fifo.data array, the loop does not
verify that i + size <= st->fifo.count before passing the decoded pointers.
inv_icm42607_fifo_decode_packet() could then yield pointers that point
outside the populated bounds, which inv_icm42607_fifo_is_data_valid() would
subsequently dereference.
[ ... ]
> int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
> unsigned int count)
> {
> + struct inv_icm42607_sensor_state *accel_st = iio_priv(st->indio_accel);
> + struct inv_sensors_timestamp *ts;
> + s64 accel_ts;
> int ret;
>
> + accel_ts = iio_get_time_ns(st->indio_accel);
> +
> ret = inv_icm42607_buffer_fifo_read(st, count);
> + if (ret)
> + return ret;
>
> - return ret;
> + if (st->fifo.nb.total == 0)
> + return 0;
> +
> + if (st->fifo.nb.accel > 0) {
> + ts = &accel_st->ts;
> + inv_sensors_timestamp_interrupt(ts, st->fifo.nb.accel, accel_ts);
> + ret = inv_icm42607_accel_parse_fifo(st->indio_accel);
Can a data race happen here? inv_icm42607_buffer_fifo_read() properly
acquires and releases st->lock, but then inv_icm42607_accel_parse_fifo() is
called without holding the lock. A concurrent flush operation could acquire
the lock, overwrite st->fifo.data, and reset st->fifo.count while the
original thread is mid-parse, leading to memory corruption and out-of-bounds
reads.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515130018.237378-1-macroalpha82@gmail.com?part=8
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V7 06/11] iio: imu: inv_icm42607: Add Buffer support for icm42607
2026-05-15 13:00 ` [PATCH V7 06/11] iio: imu: inv_icm42607: Add Buffer " Chris Morgan
@ 2026-05-15 13:35 ` sashiko-bot
2026-05-15 19:20 ` Jonathan Cameron
1 sibling, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-05-15 13:35 UTC (permalink / raw)
To: Chris Morgan; +Cc: krzk+dt, conor+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [High] Missing bounds check in `inv_icm42607_buffer_fifo_read` allows out-of-bounds slab memory read.
- [High] Unaligned memory access in `inv_icm42607_fifo_is_data_valid()` causes undefined behavior and alignment faults.
- [High] Software state desynchronization in `inv_icm42607_buffer_predisable` due to missing teardown on error paths.
- [Medium] Redundant and potentially destructive assignment logic in `inv_icm42607_buffer_set_fifo_en` overwrites FIFO configuration.
- [Medium] Dead code for sleep delay in `inv_icm42607_buffer_postdisable` bypasses required hardware power-down delays.
- [Low] The macro `INV_ICM42607_SENSOR_CONF_INIT` initializes more elements than the struct contains.
--
commit b372db6e3728fbbbe2a3d28ca15a025d7cf101c5
Author: Chris Morgan <macromorgan@hotmail.com>
iio: imu: inv_icm42607: Add Buffer support for icm42607
This commit adds all FIFO parsing and reading functions to support the
inv_icm42607 hardware.
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
[ ... ]
> @@ -84,6 +88,7 @@ struct inv_icm42607_sensor_conf {
> int odr;
> int filter;
> };
> +#define INV_ICM42607_SENSOR_CONF_INIT {-1, -1, -1, -1}
This isn't a bug, but struct inv_icm42607_sensor_conf is defined with exactly
three fields. Will initializing it with four values cause excess elements in
struct initializer compiler warnings?
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
[ ... ]
> +int inv_icm42607_buffer_set_fifo_en(struct inv_icm42607_state *st,
> + unsigned int fifo_en)
> +{
> + unsigned int val;
> + int ret;
> +
> + /* update FIFO EN bits for accel and gyro */
> + val = 0;
> + if (fifo_en & INV_ICM42607_SENSOR_GYRO)
> + val |= INV_ICM42607_FIFO_CONFIG1_MODE;
> + if (fifo_en & INV_ICM42607_SENSOR_ACCEL)
> + val |= INV_ICM42607_FIFO_CONFIG1_MODE;
> + if (fifo_en & INV_ICM42607_SENSOR_TEMP)
> + val |= INV_ICM42607_FIFO_CONFIG1_MODE;
> +
> + ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1, val);
Does writing 0 to the FIFO_CONFIG1 register here when fifo_en is empty
inadvertently overwrite the INV_ICM42607_FIFO_CONFIG1_BYPASS mode set
previously in inv_icm42607_buffer_predisable()?
Also, does setting the exact same mask (INV_ICM42607_FIFO_CONFIG1_MODE) for
all three sensors properly configure the hardware, or are distinct bits
expected here?
[ ... ]
> +static int inv_icm42607_buffer_predisable(struct iio_dev *indio_dev)
> +{
[ ... ]
> + /* set FIFO in bypass mode */
> + ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1,
> + INV_ICM42607_FIFO_CONFIG1_BYPASS);
> + if (ret)
> + return ret;
> +
> + /* flush FIFO data */
> + ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET,
> + INV_ICM42607_SIGNAL_PATH_RESET_FIFO_FLUSH);
> + if (ret)
> + return ret;
> +
> + /* disable FIFO threshold interrupt */
> + ret = regmap_update_bits(st->map, INV_ICM42607_REG_INT_SOURCE0,
> + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN, 0);
> + if (ret)
> + return ret;
> +
> + /*
> + * FIFO should only be 1 at this point, so explicitly set it to 0
> + * instead of decrementing it.
> + */
> + st->fifo.on = 0;
> +
> + return 0;
> +}
If any of these regmap operations fail and return early, st->fifo.on remains
greater than 0. Since the IIO core continues the buffer teardown process even
if predisable returns an error, does this leave the software state permanently
desynchronized?
If so, future calls to inv_icm42607_buffer_postenable() might see st->fifo.on
as non-zero and silently skip initialization, leaving the device unresponsive
until the module is reloaded.
[ ... ]
> +static int inv_icm42607_buffer_postdisable(struct iio_dev *indio_dev)
> +{
> + struct inv_icm42607_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_temp = 0;
> + unsigned int sleep_sensor = 0;
> + unsigned int sleep;
> + int ret;
[ ... ]
> + /* sleep maximum required time */
> + sleep = max(sleep_sensor, sleep_temp);
> + if (sleep)
> + msleep(sleep);
Is there missing logic to update sleep_sensor and sleep_temp?
Since they are initialized to 0 and never changed, this sleep will never
occur. Could bypassing this hardware-specific power-down delay lead to issues
when rapidly toggling power states?
[ ... ]
> +int inv_icm42607_buffer_fifo_read(struct inv_icm42607_state *st,
> + unsigned int max)
> +{
[ ... ]
> + /* compute number of samples for each sensor */
> + for (i = 0; i < st->fifo.count; i += size) {
> + size = inv_icm42607_fifo_decode_packet(&st->fifo.data[i],
> + &accel, &gyro, &temp, ×tamp);
> + /* Make sure the size is at least 1 valid packet. */
> + if (size < INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE)
> + break;
> + if (gyro != NULL && inv_icm42607_fifo_is_data_valid(gyro))
> + st->fifo.nb.gyro++;
> + if (accel != NULL && inv_icm42607_fifo_is_data_valid(accel))
> + st->fifo.nb.accel++;
> + st->fifo.nb.total++;
> + }
Can an incomplete partial packet at the end of the read result in size
extending past the valid populated data?
There is no validation to ensure that i + size <= st->fifo.count. If
st->fifo.count is near the maximum array size, could i + size exceed the
bounds of the st->fifo.data array, leading to an out-of-bounds memory read
in inv_icm42607_fifo_is_data_valid()?
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
[ ... ]
> +/* FIFO data packet */
> +struct inv_icm42607_fifo_sensor_data {
> + __be16 x;
> + __be16 y;
> + __be16 z;
> +};
> +
> +#define INV_ICM42607_FIFO_DATA_INVALID -32768
> +
> +static inline bool
> +inv_icm42607_fifo_is_data_valid(const struct inv_icm42607_fifo_sensor_data *s)
> +{
> + s16 x, y, z;
> +
> + x = be16_to_cpu(s->x);
Because the first field of the packet layout struct
(inv_icm42607_fifo_2sensors_packet) is a 1-byte header, the accel and gyro
fields reside at odd byte offsets.
Since struct inv_icm42607_fifo_sensor_data is not marked as __packed, the
compiler assumes 2-byte alignment for its __be16 fields. Will passing these
unaligned pointers into inv_icm42607_fifo_is_data_valid() trigger undefined
behavior and alignment faults on architectures that enforce strict alignment?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515130018.237378-1-macroalpha82@gmail.com?part=6
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V7 05/11] iio: imu: inv_icm42607: Add PM support for icm42607
2026-05-15 13:00 ` [PATCH V7 05/11] iio: imu: inv_icm42607: Add PM support for icm42607 Chris Morgan
@ 2026-05-15 13:36 ` sashiko-bot
2026-05-15 18:59 ` Jonathan Cameron
1 sibling, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-05-15 13:36 UTC (permalink / raw)
To: Chris Morgan; +Cc: krzk+dt, conor+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Unconditional assignment of runtime PM callbacks using RUNTIME_PM_OPS causes dead code when CONFIG_PM is disabled.
--
commit 2b859f6a3a3693a6dff734a7ea031d9f3a2a527f
Author: Chris Morgan <macromorgan@hotmail.com>
iio: imu: inv_icm42607: Add PM support for icm42607
Add power management support for the ICM42607 device driver.
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> index 1088c5c7076f8..e27ad0319a125 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
[ ... ]
> +static int inv_icm42607_runtime_resume(struct device *dev)
> +{
> + struct inv_icm42607_state *st = dev_get_drvdata(dev);
> +
> + guard(mutex)(&st->lock);
> +
> + return inv_icm42607_enable_vddio_reg(st);
> +}
> +
> +EXPORT_NS_GPL_DEV_PM_OPS(inv_icm42607_pm_ops, IIO_ICM42607) = {
> + SYSTEM_SLEEP_PM_OPS(inv_icm42607_suspend, inv_icm42607_resume)
> + RUNTIME_PM_OPS(inv_icm42607_runtime_suspend,
> + inv_icm42607_runtime_resume, NULL)
Will this result in dead code if CONFIG_PM is disabled?
Unlike SYSTEM_SLEEP_PM_OPS which internally wraps its arguments with
pm_sleep_ptr(), RUNTIME_PM_OPS does not use pm_ptr() for its callbacks.
This leaves the static inv_icm42607_runtime_suspend and
inv_icm42607_runtime_resume functions compiled even when PM is disabled.
Could this be updated to use pm_ptr() for the runtime PM callbacks, or
alternatively use the DEFINE_RUNTIME_DEV_PM_OPS macro?
> +};
> +
> MODULE_AUTHOR("InvenSense, Inc.");
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515130018.237378-1-macroalpha82@gmail.com?part=5
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V7 10/11] iio: imu: inv_icm42607: Add Gyroscope to icm42607
2026-05-15 13:00 ` [PATCH V7 10/11] iio: imu: inv_icm42607: Add Gyroscope " Chris Morgan
@ 2026-05-15 13:37 ` sashiko-bot
2026-05-15 19:44 ` Jonathan Cameron
1 sibling, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-05-15 13:37 UTC (permalink / raw)
To: Chris Morgan; +Cc: robh, krzk+dt, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [Critical] The bounds check in `inv_icm42607_gyro_parse_fifo()` is inverted, dropping valid packets and causing out-of-bounds memory reads.
- [High] The local variable `gyro_ts` is passed uninitialized to `inv_sensors_timestamp_interrupt()` in `inv_icm42607_buffer_hwfifo_flush()`.
- [High] Missing lock acquisition in `inv_icm42607_gyro_hwfifo_flush()` allows concurrent data races over shared FIFO state.
- [High] Missing PM runtime acquisition in `inv_icm42607_gyro_hwfifo_set_watermark()`.
- [Medium] `inv_icm42607_gyro_read_avail()` exposes uninitialized padding as valid sampling frequencies to user-space.
--
commit 207799d1c88894b8f276d2717bc0960a73f7d744
Author: Chris Morgan <macromorgan@hotmail.com>
iio: imu: inv_icm42607: Add Gyroscope to icm42607
This commit adds gyroscope functions to the inv_icm42607 driver.
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> index 4371f428cc3f8..771120fa8ebf8 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
[ ... ]
> @@ -499,9 +512,10 @@ int inv_icm42607_buffer_fifo_parse(struct inv_icm42607_state *st)
> int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
> unsigned int count)
> {
> + struct inv_icm42607_sensor_state *gyro_st = iio_priv(st->indio_gyro);
> struct inv_icm42607_sensor_state *accel_st = iio_priv(st->indio_accel);
> struct inv_sensors_timestamp *ts;
> - s64 accel_ts;
> + s64 gyro_ts, accel_ts;
> int ret;
>
> accel_ts = iio_get_time_ns(st->indio_accel);
[ ... ]
> @@ -513,6 +527,14 @@ int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
> 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);
Is it possible that gyro_ts is used uninitialized here? It appears to be
declared on the stack but never assigned a value, which would feed kernel
stack memory into the timestamp state machine.
> + ret = inv_icm42607_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);
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
> new file mode 100644
> index 0000000000000..5a5fd13ba64ec
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
[ ... ]
> +static int inv_icm42607_gyro_read_avail(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + const int **vals,
> + int *type, int *length, long mask)
> +{
> + if (chan->type != IIO_ANGL_VEL)
> + return -EINVAL;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_SCALE:
> + *vals = (const int *)inv_icm42607_gyro_scale_nano;
> + *type = IIO_VAL_INT_PLUS_NANO;
> + *length = ARRAY_SIZE(inv_icm42607_gyro_scale_nano) * 2;
> + return IIO_AVAIL_LIST;
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + *vals = (const int *)inv_icm42607_gyro_odr;
> + *type = IIO_VAL_INT_PLUS_MICRO;
> + *length = ARRAY_SIZE(inv_icm42607_gyro_odr) * 2;
Could this expose uninitialized padding to user-space?
Because inv_icm42607_gyro_odr is initialized with designated initializers
starting at index 5, the first 5 entries are implicitly zero-initialized.
Returning the entire array length might expose these 0.000000 values as valid
sampling frequencies in the sampling_frequency_available sysfs attribute.
> + return IIO_AVAIL_LIST;
> + default:
> + return -EINVAL;
> + }
> +}
[ ... ]
> +static int inv_icm42607_gyro_hwfifo_set_watermark(struct iio_dev *indio_dev,
> + unsigned int val)
> +{
> + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
> +
> + guard(mutex)(&st->lock);
> +
> + st->fifo.watermark.gyro = val;
> + return inv_icm42607_buffer_update_watermark(st);
Is a PM runtime reference required here before updating the watermark?
inv_icm42607_buffer_update_watermark() modifies hardware registers, but this
function doesn't acquire a PM runtime reference. This might result in writing
to the device while it is suspended.
> +}
> +
> +static int inv_icm42607_gyro_hwfifo_flush(struct iio_dev *indio_dev,
> + unsigned int count)
> +{
> + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
> + struct device *dev = regmap_get_device(st->map);
> + int ret;
> +
> + if (count == 0)
> + return 0;
> +
> + /* Make sure we don't try to count more than the buffer can hold. */
> + count = min(count, sizeof(st->fifo.data));
> +
> + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
> + if (PM_RUNTIME_ACQUIRE_ERR(&pm))
> + return -ENXIO;
> +
> + ret = inv_icm42607_buffer_hwfifo_flush(st, count);
Does this need to acquire st->lock before calling
inv_icm42607_buffer_hwfifo_flush()?
Because inv_icm42607_buffer_hwfifo_flush() calls inv_icm42607_gyro_parse_fifo()
without holding the lock, an interrupt could fire concurrently and overwrite
st->fifo.data and st->fifo.count while they are being parsed locklessly.
> + if (ret)
> + return ret;
> +
> + return st->fifo.nb.gyro;
> +}
[ ... ]
> +int inv_icm42607_gyro_parse_fifo(struct iio_dev *indio_dev)
> +{
> + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
> + struct inv_icm42607_sensor_state *gyro_st = iio_priv(indio_dev);
> + struct inv_sensors_timestamp *ts = &gyro_st->ts;
> + ssize_t i, size;
> + unsigned int no;
> + const void *accel, *gyro, *timestamp;
> + const s8 *temp;
> + unsigned int odr;
> + s64 ts_val;
> + struct inv_icm42607_gyro_buffer buffer = { };
> +
> + /* parse all fifo packets */
> + for (i = 0, no = 0; i < st->fifo.count; i += size, ++no) {
> + size = inv_icm42607_fifo_decode_packet(&st->fifo.data[i],
> + &accel, &gyro, &temp, ×tamp, &odr);
> + /* quit if error or FIFO is empty */
> + if (size <= 0)
> + return size;
> +
> + /* If the packet size could cause us to overflow, return. */
> + if (i + size <= st->fifo.count)
Is this bounds check inverted?
It appears this condition evaluates to true when the packet fits safely within
the buffer, causing it to abort on valid data. Conversely, if a packet size
exceeds the buffer, the condition evaluates to false and allows the subsequent
memcpy() to read out of bounds.
> + return -EIO;
> +
> + /* skip packet if no gyro data or data is invalid */
> + if (gyro == NULL || !inv_icm42607_fifo_is_data_valid(gyro))
> + continue;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515130018.237378-1-macroalpha82@gmail.com?part=10
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V7 09/11] iio: imu: inv_icm42607: Add Wake on Movement to icm42607
2026-05-15 13:00 ` [PATCH V7 09/11] iio: imu: inv_icm42607: Add Wake on Movement to icm42607 Chris Morgan
@ 2026-05-15 13:42 ` sashiko-bot
2026-05-15 19:36 ` Jonathan Cameron
1 sibling, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-05-15 13:42 UTC (permalink / raw)
To: Chris Morgan; +Cc: robh, conor+dt, krzk+dt, devicetree
Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [High] TOCTOU race in event configuration leaks PM references and leaves sensor permanently powered on.
- [High] Disabling the IIO buffer unconditionally powers off the accelerometer, breaking Wake-on-Motion.
- [High] Deadlock avoidance in `inv_icm42607_suspend()` allows the threaded IRQ handler to access a suspended bus.
- [High] I2C failure in `inv_icm42607_mreg_write()` leaves the device trapped in the wrong register bank.
- [Medium] Error path in WoM enablement leaves the accelerometer's software state cached as ON, causing a permanent power leak.
- [Medium] Error path in ODR write desynchronizes the FIFO watermark and timing state from the hardware.
- [Medium] The driver leaks a runtime PM usage count if unbound while the Wake-on-Motion event is active.
- [Low] The driver silently ignores WoM threshold configurations for the ICM42607P variant while returning success to userspace.
--
commit 38592461f791657cf708b555278a27f572c997e7
Author: Chris Morgan <macromorgan@hotmail.com>
Subject: iio: imu: inv_icm42607: Add Wake on Movement to icm42607
Add support for wake on movement for the icm42607 driver.
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
> index b354a84fd2ed4..504245b0282bf 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
[ ... ]
> @@ -209,6 +221,181 @@ static int inv_icm42607_accel_read_sensor(struct iio_dev *indio_dev,
> return 0;
> }
>
> +static unsigned int inv_icm42607_accel_convert_roc_to_wom(u64 roc,
> + int accel_hz, int accel_uhz)
[ ... ]
> +static int inv_icm42607_accel_set_wom_threshold(struct inv_icm42607_state *st,
> + u64 value,
> + int accel_hz, int accel_uhz)
> +{
> + unsigned int threshold;
> + int i, ret;
> +
> + /* convert roc to wom threshold and convert back to handle clipping */
> + threshold = inv_icm42607_accel_convert_roc_to_wom(value, accel_hz, accel_uhz);
> + value = inv_icm42607_accel_convert_wom_to_roc(threshold, accel_hz, accel_uhz);
> +
> + dev_dbg(regmap_get_device(st->map), "wom_threshold: 0x%x\n", threshold);
> +
> + st->apex.wom.value = value;
> +
> + /* 42607P cannot set WoM threshold. */
> + if (st->hw->whoami == INV_ICM42607P_WHOAMI)
> + return 0;
Should this return -EOPNOTSUPP instead of 0? Returning 0 updates the cached
value in st->apex.wom.value and signals success to userspace, even though the
hardware configuration isn't changed for the 42607P variant.
> +
> + /* set accel WoM threshold for the 3 axes */
> + for (i = 0; i < 3; i++) {
> + ret = inv_icm42607_mreg_write(st->map, INV_ICM42607_MREG_2,
> + INV_ICM42607_REG_ACCEL_WOM_X_THR + i,
> + threshold);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int _inv_icm42607_accel_enable_wom(struct iio_dev *indio_dev)
> +{
> + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
> + struct inv_icm42607_sensor_state *accel_st = iio_priv(indio_dev);
> + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
> + unsigned int sleep_ms = 0;
> + int ret;
> +
> + scoped_guard(mutex, &st->lock) {
> + /* turn on accel sensor */
> + conf.mode = accel_st->power_mode;
> + conf.filter = accel_st->filter;
> + ret = inv_icm42607_set_accel_conf(st, &conf, &sleep_ms);
> + if (ret)
> + return ret;
> + }
> +
> + if (sleep_ms)
> + msleep(sleep_ms);
> +
> + scoped_guard(mutex, &st->lock) {
> + ret = inv_icm42607_enable_wom(st);
> + if (ret)
> + return ret;
If inv_icm42607_enable_wom() fails here, does the accelerometer remain powered
on? The earlier call to inv_icm42607_set_accel_conf() enabled it, but an
early return leaves the hardware in the ON state without reverting
st->conf.accel.mode to OFF.
During system or runtime suspend, this ON state would be cached in
st->suspended.accel, and on all future resumes, the accelerometer might be
redundantly powered on.
> + st->apex.on++;
> + st->apex.wom.enable = true;
> + }
> +
> + return 0;
> +}
> +
> +static int inv_icm42607_accel_enable_wom(struct iio_dev *indio_dev)
> +{
> + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
> + struct device *pdev = regmap_get_device(st->map);
> + int ret;
> +
> + ret = pm_runtime_resume_and_get(pdev);
Is a devres action or remove callback needed to disable the event and drop the
PM runtime usage count? If the device is unbound via sysfs or the module is
unloaded while the Wake-on-Motion event is active, the PM usage count might
be leaked, keeping the hardware powered on.
> + if (ret)
> + return ret;
> +
> + ret = _inv_icm42607_accel_enable_wom(indio_dev);
> + if (ret) {
> + pm_runtime_put_autosuspend(pdev);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int _inv_icm42607_accel_disable_wom(struct iio_dev *indio_dev)
> +{
> + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
> + struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
> + unsigned int sleep_ms = 0;
> + int ret;
> +
> + scoped_guard(mutex, &st->lock) {
> + /*
> + * Consider that turning off WoM is always working to avoid
> + * blocking the chip in on mode and prevent going back to sleep.
> + * If there is an error, the chip will anyway go back to sleep
> + * and the feature will not work anymore.
> + */
> + st->apex.wom.enable = false;
> + st->apex.on--;
> + inv_icm42607_disable_wom(st);
> + /* turn off accel sensor if not used */
> + if (!st->apex.on && !iio_buffer_enabled(indio_dev)) {
> + conf.mode = INV_ICM42607_SENSOR_MODE_OFF;
> + ret = inv_icm42607_set_accel_conf(st, &conf, &sleep_ms);
> + if (ret)
> + return ret;
> + }
> + }
Does disabling the IIO buffer unconditionally power off the accelerometer in
inv_icm42607_buffer_postdisable()? If a user enables the Wake-on-Motion
feature (which requires the accelerometer to remain in LOW_NOISE mode) and
subsequently starts then stops the IIO buffer, it appears the sensor could
be physically powered down, preventing the Wake-on-Motion detection from
functioning.
[ ... ]
> @@ -329,6 +516,10 @@ static int inv_icm42607_accel_write_odr(struct iio_dev *indio_dev,
> if (ret)
> return ret;
>
> + ret = inv_icm42607_accel_set_wom_threshold(st, st->apex.wom.value, val, val2);
> + if (ret)
> + return ret;
> +
If this fails, does it leave the hardware ODR out of sync with the driver's
FIFO timing state? The earlier code updated the hardware ODR, but returning
early here skips inv_icm42607_buffer_update_fifo_period() and
inv_icm42607_buffer_update_watermark().
> inv_icm42607_buffer_update_fifo_period(st);
> inv_icm42607_buffer_update_watermark(st);
>
[ ... ]
> +static int inv_icm42607_accel_write_event_config(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan,
> + enum iio_event_type type,
> + enum iio_event_direction dir,
> + bool state)
> +{
> + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
> +
> + /* handle only WoM (roc rising) event */
> + if (type != IIO_EV_TYPE_ROC || dir != IIO_EV_DIR_RISING)
> + return -EINVAL;
> +
> + scoped_guard(mutex, &st->lock) {
> + if (st->apex.wom.enable == state)
> + return 0;
> + }
> +
> + if (state)
> + return inv_icm42607_accel_enable_wom(indio_dev);
> +
> + return inv_icm42607_accel_disable_wom(indio_dev);
> +}
Does this introduce a race condition? Since the lock is dropped before calling
inv_icm42607_accel_enable_wom(), if two threads concurrently enable the event,
both could pass the check and execute the enable function.
This would call pm_runtime_resume_and_get() twice and increment st->apex.on
twice. A subsequent disable operation will only execute once due to the same
check, leaking a runtime PM reference and keeping the device powered on.
[ ... ]
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> index 1e685451f7850..8ab8b5f739f2b 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> @@ -17,6 +17,57 @@
> #include "inv_icm42607.h"
> #include "inv_icm42607_buffer.h"
>
> +/**
> + * inv_icm42607_mreg_write() - Write value to user bank
[ ... ]
> + /*
> + * Write bank, register, and value in one bulk write. Then pause
> + * the required time according to datasheet.
> + */
> + ret = regmap_bulk_write(map, INV_ICM42607_BLK_SEL_W, buffer, 3);
> + fsleep(INV_ICM42607_MREG_DELAY_US);
> + if (ret)
> + return ret;
> +
> + /* Switch back to bank1 and pause the required amount of time. */
> + ret = regmap_write(map, INV_ICM42607_BLK_SEL_W, INV_ICM42607_MREG_1);
If this regmap_bulk_write() fails, will the device be left permanently in bank
2 or 3? Returning early skips the write to switch back to INV_ICM42607_MREG_1.
All subsequent generic register accesses (such as reading INT_STATUS2 in the
IRQ handler) would read from the wrong bank and receive invalid data.
> + fsleep(INV_ICM42607_MREG_DELAY_US);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
[ ... ]
> @@ -466,6 +668,9 @@ EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607");
> static int inv_icm42607_suspend(struct device *dev)
> {
> struct inv_icm42607_state *st = dev_get_drvdata(dev);
> + struct device *accel_dev;
> + bool wakeup;
> + int accel_conf;
> int ret;
>
> guard(mutex)(&st->lock);
> @@ -482,12 +687,36 @@ static int inv_icm42607_suspend(struct device *dev)
> return ret;
> }
>
> - ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
> - INV_ICM42607_SENSOR_MODE_OFF,
> - false, NULL);
> - if (ret)
> - return ret;
> - inv_icm42607_disable_vddio_reg(st);
> + /* keep chip on and wake-up capable if APEX and wakeup on */
> + accel_dev = &st->indio_accel->dev;
> + wakeup = st->apex.on && device_may_wakeup(accel_dev);
> + if (wakeup) {
> + /* keep accel on and setup irq for wakeup */
> + accel_conf = st->conf.accel.mode;
> + enable_irq_wake(st->irq);
> + disable_irq_nosync(st->irq);
Could this lead to an I2C/SPI access on a suspended bus?
If the threaded IRQ handler is currently executing and blocked waiting for
st->lock, disable_irq_nosync() will return without waiting for it. Once this
suspend function drops the lock, the PM core proceeds to suspend the parent
controller.
The unblocked IRQ handler would then acquire the lock and attempt to read via
regmap_bulk_read(), potentially causing a timeout or kernel panic on the
suspended bus.
> + ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
> + accel_conf, false, NULL);
> + if (ret) {
> + enable_irq(st->irq);
> + disable_irq_wake(st->irq);
> + return ret;
> + }
> + } else {
> + /* disable APEX features and accel if wakeup disabled */
> + if (st->apex.wom.enable) {
> + ret = inv_icm42607_disable_wom(st);
> + if (ret)
> + return ret;
> + }
> + accel_conf = INV_ICM42607_SENSOR_MODE_OFF;
> + ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
> + INV_ICM42607_SENSOR_MODE_OFF,
> + false, NULL);
> + if (ret)
> + return ret;
> + inv_icm42607_disable_vddio_reg(st);
> + }
>
> return 0;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515130018.237378-1-macroalpha82@gmail.com?part=9
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V7 04/11] iio: imu: inv_icm42607: Add I2C and SPI For icm42607
2026-05-15 13:00 ` [PATCH V7 04/11] iio: imu: inv_icm42607: Add I2C and SPI For icm42607 Chris Morgan
@ 2026-05-15 13:45 ` sashiko-bot
2026-05-15 18:43 ` Jonathan Cameron
1 sibling, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-05-15 13:45 UTC (permalink / raw)
To: Chris Morgan; +Cc: robh, krzk+dt, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Masking `-EPROBE_DEFER` and strict IRQ naming breaks device probe lifecycle and standard devicetrees.
- [High] `regcache_mark_dirty()` usage causes the hardware state to desync, skipping required bus configuration after soft reset.
- [High] SPI 3-wire mode configuration uses a read-modify-write cycle, reading garbage data and corrupting device registers.
- [Medium] Valid error codes from regmap operations are discarded and replaced with -EIO.
--
commit 7dbec8ad22b33274f815169a6c802cbf63590cea
Author: Chris Morgan <macromorgan@hotmail.com>
iio: imu: inv_icm42607: Add I2C and SPI For icm42607
This commit adds I2C and SPI driver support for the InvenSense
ICM-42607 motion tracking device.
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
> new file mode 100644
> index 0000000000000..49438fa6f8673
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
[ ... ]
> +static int inv_icm42607_spi_bus_setup(struct inv_icm42607_state *st)
> +{
> + const struct device *dev = regmap_get_device(st->map);
> + const struct spi_device *spi = to_spi_device(dev);
> + unsigned int val;
> + int ret;
> +
> + if (spi->mode & SPI_3WIRE)
> + ret = regmap_clear_bits(st->map, INV_ICM42607_REG_DEVICE_CONFIG,
> + INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE);
Since the device powers on in 4-wire mode, won't this initial
read-modify-write fail for a 3-wire configured host?
The host expects data on MOSI but the device drives MISO, which would result
in reading garbage data and permanently corrupting the rest of the
configuration bits in DEVICE_CONFIG when written back.
Should a blind regmap_write() be used here instead to safely establish
3-wire mode?
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> index 9784709319b92..1088c5c7076f8 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
[ ... ]
> +static int inv_icm42607_setup(struct inv_icm42607_state *st,
> + inv_icm42607_bus_setup bus_setup)
> +{
> + const struct device *dev = regmap_get_device(st->map);
> + unsigned int val;
> + int ret;
> +
> + /*
> + * Setup the bus first in case we need to set the SPI mode or
> + * change the slew rate in order.
> + */
> + ret = bus_setup(st);
> + if (ret)
> + return ret;
[ ... ]
> + ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET,
> + INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET);
> + if (ret)
> + return ret;
>
> - fsleep(1000);
> + /*
> + * In order to confirm a reset is complete, we need to read the reset
> + * bit, but in certain circumstances we need to set the bus up before
> + * we can do a read. So we should wait the required amount of time
> + * per the datasheet first, then set the bus up again, then read to
> + * ensure the reset status is done. Invalidate the regmap cache since
> + * we're doing a hardware reset.
> + */
> + regcache_mark_dirty(st->map);
>
> - ret = regmap_read_poll_timeout(st->map, INV_ICM42607_REG_INT_STATUS,
> - val, val & INV_ICM42607_INT_STATUS_RESET_DONE,
> - INV_ICM42607_RESET_TIME_MS * 100,
> - INV_ICM42607_RESET_TIME_MS * 1000);
> - if (ret)
> - return dev_err_probe(dev, ret,
> - "reset error, reset done bit not set\n");
> + fsleep(INV_ICM42607_RESET_TIME_MS * 1000);
>
> ret = bus_setup(st);
Because regcache_mark_dirty() only flags the cache as needing a sync but does
not clear the cached values, will regmap_update_bits() in this second
bus_setup() call silently skip the hardware writes?
The cache would still show the bits as set from the first bus_setup(), leaving
the hardware in its default reset state.
Would regcache_sync() or regcache_drop_region() be more appropriate here to
force the actual hardware writes?
> if (ret)
> return ret;
>
> + ret = regmap_read(st->map, INV_ICM42607_REG_INT_STATUS, &val);
> + if (ret || (!(val & INV_ICM42607_INT_STATUS_RESET_DONE)))
> + return dev_err_probe(dev, -EIO,
> + "reset error, reset done bit not set\n");
If the underlying bus read fails (e.g., returning -ENXIO or -ETIMEDOUT), does
it make sense to mask the actual error code with a hardcoded -EIO?
Could the original error code be propagated to help debug underlying bus
subsystem issues?
[ ... ]
> +int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw *hw,
> + inv_icm42607_bus_setup bus_setup)
> +{
> + struct device *dev = regmap_get_device(regmap);
> + struct fwnode_handle *fwnode = dev_fwnode(dev);
> + struct inv_icm42607_state *st;
> + int irq;
> + int ret;
> +
> + irq = fwnode_irq_get_byname(fwnode, "INT1");
> + if (!(irq > 0))
> + return dev_err_probe(dev, -EINVAL, "Unable to get INT1 interrupt\n");
Does this prevent probe deferral if the interrupt controller isn't ready yet?
Since fwnode_irq_get_byname() would return -EPROBE_DEFER, converting it to
-EINVAL causes a permanent probe failure instead of retrying.
Additionally, if the device tree binding specifies interrupt-names as optional,
will this strict name enforcement cause a hard failure for valid devicetrees
that omit it?
Should the driver fall back to an index-based lookup or use the
framework-provided client->irq / spi->irq if the name is missing?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515130018.237378-1-macroalpha82@gmail.com?part=4
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V7 03/11] iio: imu: inv_icm42607: Add inv_icm42607 Core Driver
2026-05-15 13:00 ` [PATCH V7 03/11] iio: imu: inv_icm42607: Add inv_icm42607 Core Driver Chris Morgan
2026-05-15 13:23 ` sashiko-bot
@ 2026-05-15 18:31 ` Jonathan Cameron
1 sibling, 0 replies; 26+ messages in thread
From: Jonathan Cameron @ 2026-05-15 18:31 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-iio, andy, nuno.sa, dlechner, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
On Fri, 15 May 2026 08:00:08 -0500
Chris Morgan <macroalpha82@gmail.com> wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add the core component of a new inv_icm42607 driver. This includes
> a few setup functions and the full register definition in the
> header file.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Sashiko led you into the weeks with irq request return values.
It was less broken in v6 :(
As to it's other comments on checking for line high/low values (0x00 / 0xFF)
for whoami is something we don't normally bother with but you could if you like.
I'm fairly sure we've had both those values turn up as valid in some devices
in the past.
Otherwise just trivial stuff I noticed whilst having a fresh read through.
All stuff I might have tweaked whilst applying or just let through but
seeing as you are going to be doing a v8, please take a look.
Jonathan
> ---
> drivers/iio/imu/inv_icm42607/inv_icm42607.h | 334 ++++++++++++++++++
> .../iio/imu/inv_icm42607/inv_icm42607_core.c | 207 +++++++++++
> 2 files changed, 541 insertions(+)
> create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607.h
> create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
>
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
> new file mode 100644
> index 000000000000..1916e0b08bca
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
> @@ -0,0 +1,334 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2026 InvenSense, Inc.
> + */
> +
> +#ifndef INV_ICM42607_H_
> +#define INV_ICM42607_H_
> +
> +#include <linux/bitfield.h>
> +#include <linux/bitops.h>
> +#include <linux/iio/iio.h>
> +#include <linux/mutex.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +
> +enum inv_icm42607_sensor_mode {
> + INV_ICM42607_SENSOR_MODE_OFF,
> + INV_ICM42607_SENSOR_MODE_STANDBY,
> + INV_ICM42607_SENSOR_MODE_LOW_POWER,
> + INV_ICM42607_SENSOR_MODE_LOW_NOISE,
> + INV_ICM42607_SENSOR_MODE_NB
> +};
> +
> +/* gyroscope fullscale values */
> +enum inv_icm42607_gyro_fs {
> + INV_ICM42607_GYRO_FS_2000DPS,
> + INV_ICM42607_GYRO_FS_1000DPS,
> + INV_ICM42607_GYRO_FS_500DPS,
> + INV_ICM42607_GYRO_FS_250DPS,
> + INV_ICM42607_GYRO_FS_NB
> +};
> +
> +/* accelerometer fullscale values */
> +enum inv_icm42607_accel_fs {
> + INV_ICM42607_ACCEL_FS_16G,
> + INV_ICM42607_ACCEL_FS_8G,
> + INV_ICM42607_ACCEL_FS_4G,
> + INV_ICM42607_ACCEL_FS_2G,
> + INV_ICM42607_ACCEL_FS_NB
> +};
> +
> +/* ODR values */
> +enum inv_icm42607_odr {
> + INV_ICM42607_ODR_1600HZ = 5,
> + INV_ICM42607_ODR_800HZ,
> + INV_ICM42607_ODR_400HZ,
> + INV_ICM42607_ODR_200HZ,
> + INV_ICM42607_ODR_100HZ,
> + INV_ICM42607_ODR_50HZ,
> + INV_ICM42607_ODR_25HZ,
> + INV_ICM42607_ODR_12_5HZ,
> + INV_ICM42607_ODR_6_25HZ_LP,
> + INV_ICM42607_ODR_3_125HZ_LP,
> + INV_ICM42607_ODR_1_5625HZ_LP,
> + INV_ICM42607_ODR_NB
> +};
> +
> +enum inv_icm42607_filter_bw {
> + /* Low-Noise mode sensor data filter (bandwidth) */
> + INV_ICM42607_FILTER_BYPASS,
> + INV_ICM42607_FILTER_BW_180HZ,
> + INV_ICM42607_FILTER_BW_121HZ,
> + INV_ICM42607_FILTER_BW_73HZ,
> + INV_ICM42607_FILTER_BW_53HZ,
> + INV_ICM42607_FILTER_BW_34HZ,
> + INV_ICM42607_FILTER_BW_25HZ,
> + INV_ICM42607_FILTER_BW_16HZ
That isn't a terminating entry so it should have a ,
Rules on this are a bit obscure - but basically _NB type entries
should never have a , anything else should - even if it's
a register field enum where we know all values are there.
> +};
> +/* Sleep times required by the driver */
> +#define INV_ICM42607_POWER_UP_TIME_US 100000
> +#define INV_ICM42607_RESET_TIME_MS 1
> +#define INV_ICM42607_ACCEL_STARTUP_TIME_MS 20
> +#define INV_ICM42607_GYRO_STARTUP_TIME_MS 60
> +#define INV_ICM42607_GYRO_STOP_TIME_MS 150
> +#define INV_ICM42607_TEMP_STARTUP_TIME_MS 14
> +#define INV_ICM42607_SUSPEND_DELAY_MS 2000
Can we have spec references for these? We often hit problems later
with devices that are a little bit slow and no one is sure if it's because
the sleeps are wrong or device is actually out of spec. Hence
it is useful to be able to quickly check these.
> +
> +typedef int (*inv_icm42607_bus_setup)(struct inv_icm42607_state *);
> +
> +extern const struct regmap_config inv_icm42607_regmap_config;
> +
> +int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw *hw,
> + inv_icm42607_bus_setup bus_setup);
> +
> +#endif
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> new file mode 100644
> index 000000000000..9784709319b9
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> @@ -0,0 +1,207 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2026 InvenSense, Inc.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/iio/iio.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/property.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include "inv_icm42607.h"
> +
> +static int inv_icm42607_set_conf(struct inv_icm42607_state *st,
> + const struct inv_icm42607_conf *conf)
> +{
> + unsigned int val;
> + int ret;
> +
> + val = FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK,
> + conf->gyro.mode);
> + val |= FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK,
> + conf->accel.mode);
As below - I'm fine with these going on one line (first is 80 chars, second just over)
> + /*
> + * No temperature enable reg in datasheet, but BSP driver
> + * selected RC oscillator clock in LP mode when temperature
> + * was disabled.
> + */
/*
* No temperature enable reg in datasheet, but BSP driver selected
* RC oscillator clock in LP mode when temperature was disabled.
*/
Basically go up to 80 chars. Here I think keeping RC oscillator on one line
is a good idea though.
> + if (!conf->temp_en)
> + val |= INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL;
> + ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val);
> + if (ret)
> + return ret;
> +
> + val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK,
> + conf->gyro.fs);
It's under 80 chars on one line so don't wrap.
> + val |= FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_ODR_MASK,
> + conf->gyro.odr);
As is this.
> + ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val);
> + if (ret)
> + return ret;
> +
> + val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK, conf->accel.fs);
> + val |= FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_ODR_MASK, conf->accel.odr);
> + ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val);
> + if (ret)
> + return ret;
> +
> + val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG1_FILTER_MASK,
> + conf->gyro.filter);
Whilst this one and next are a tiny bit over 80 chars, if you want to I don't
mind those being a little long as single lines.
> + ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG1, val);
> + if (ret)
> + return ret;
> +
> + val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK,
> + conf->accel.filter);
> + ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG1, val);
> + if (ret)
> + return ret;
> +
> + st->conf = *conf;
> +
> + return 0;
> +}
> +
> +/**
> + * inv_icm42607_setup() - check and setup chip
> + * @st: driver internal state
> + * @bus_setup: callback for setting up bus specific registers
> + *
> + * Returns 0 on success, a negative error code otherwise.
> + */
> +static int inv_icm42607_setup(struct inv_icm42607_state *st,
> + inv_icm42607_bus_setup bus_setup)
> +{
> + const struct device *dev = regmap_get_device(st->map);
> + unsigned int val;
> + int ret;
> +
> + ret = regmap_read(st->map, INV_ICM42607_REG_WHOAMI, &val);
> + if (ret)
> + return ret;
> +
> + if (val != st->hw->whoami)
> + dev_warn(dev, "invalid whoami %#02x expected %#02x (%s)\n",
> + val, st->hw->whoami, st->hw->name);
> +
> + ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET,
> + INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET);
> + if (ret)
> + return ret;
> +
> + fsleep(1000);
Do we need an explicit sleep here, or is the idea just to save on
a read that can't succeed in the poll that follows? I'd be tempted
to drop this if it's not absolutely needed just to avoid explaining it.
This isn't fast path code. If we need to leave the device alone after
reset for a bit then a datasheet reference is needed.
> +
> + ret = regmap_read_poll_timeout(st->map, INV_ICM42607_REG_INT_STATUS,
> + val, val & INV_ICM42607_INT_STATUS_RESET_DONE,
> + INV_ICM42607_RESET_TIME_MS * 100,
> + INV_ICM42607_RESET_TIME_MS * 1000);
> + if (ret)
> + return dev_err_probe(dev, ret,
> + "reset error, reset done bit not set\n");
> +
> + ret = bus_setup(st);
> + if (ret)
> + return ret;
> +
> + ret = regmap_set_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
> + INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN);
> + if (ret)
> + return ret;
> +
> + ret = regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1,
> + INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK,
> + INV_ICM42607_INTF_CONFIG1_CLKSEL_PLL);
> + if (ret)
> + return ret;
> +
> + return inv_icm42607_set_conf(st, st->hw->conf);
> +}
> +int inv_icm42607_core_probe(struct regmap *regmap, const struct inv_icm42607_hw *hw,
> + inv_icm42607_bus_setup bus_setup)
> +{
> + struct device *dev = regmap_get_device(regmap);
> + struct fwnode_handle *fwnode = dev_fwnode(dev);
> + struct inv_icm42607_state *st;
> + int irq;
> + int ret;
> +
> + irq = fwnode_irq_get_byname(fwnode, "INT1");
Trivial but if fwnode is only used here (I didn't check later patches) then
put the dev_fwnode() inline.
I've never really understood why we don't have device_ variant of that - perhaps
because it's hard to come up with a clear name.
> + if (!(irq > 0))
> + return dev_err_probe(dev, -EINVAL, "Unable to get INT1 interrupt\n");
I assume this is in response to sashiko saying fwnode_irq_get_byname() can in some
corner cases (where I suspect things are broken enough the system probably won't boot)
return 0. If we are actually going to defend against that then you need
to special case it. New sashiko review correctly reports this breaks probe
deferal if say the interrupt chip driver hasn't loaded yet.
if (irq < 0) {
return dev_err_probe(dev, irq, ...);
if (irq == 0)
/* Odd corner case... */
return dev_err_probe(dev, -EINVAL, ...);
However I'm deeply suspicious of whether the code paths that return 0 can happen in
practice. Given you have a suitable DT, can you try seeing if you can actually make
it return 0 by messing around with the interrupt mappings?
> +
> + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
> + if (!st)
> + return -ENOMEM;
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V7 04/11] iio: imu: inv_icm42607: Add I2C and SPI For icm42607
2026-05-15 13:00 ` [PATCH V7 04/11] iio: imu: inv_icm42607: Add I2C and SPI For icm42607 Chris Morgan
2026-05-15 13:45 ` sashiko-bot
@ 2026-05-15 18:43 ` Jonathan Cameron
1 sibling, 0 replies; 26+ messages in thread
From: Jonathan Cameron @ 2026-05-15 18:43 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-iio, andy, nuno.sa, dlechner, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
On Fri, 15 May 2026 08:00:09 -0500
Chris Morgan <macroalpha82@gmail.com> wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add I2C and SPI driver support for InvenSense ICM-42607 devices.
> Add necessary Kconfig and Makefile to allow building of (incomplete)
> driver.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
I'm a little amused that sashiko seems to have halucinated part of the
previous patch as part of this one as so repeated some comments. Oh well.
Some of it's other comments are correct.
The only thing I added was a few formatting things.
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> index 9784709319b9..1088c5c7076f 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
BOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607");
> +
> static int inv_icm42607_set_conf(struct inv_icm42607_state *st,
> const struct inv_icm42607_conf *conf)
> {
> @@ -81,6 +138,14 @@ static int inv_icm42607_setup(struct inv_icm42607_state *st,
> unsigned int val;
> int ret;
>
> + /*
> + * Setup the bus first in case we need to set the SPI mode or
> + * change the slew rate in order.
Short wrap. change at least shold be on the line above.
> + */
> + ret = bus_setup(st);
> + if (ret)
> + return ret;
> +
> ret = regmap_read(st->map, INV_ICM42607_REG_WHOAMI, &val);
> if (ret)
> return ret;
> @@ -94,20 +159,27 @@ static int inv_icm42607_setup(struct inv_icm42607_state *st,
> if (ret)
> return ret;
>
> - fsleep(1000);
> + /*
> + * In order to confirm a reset is complete, we need to read the reset
> + * bit, but in certain circumstances we need to set the bus up before
> + * we can do a read. So we should wait the required amount of time
> + * per the datasheet first, then set the bus up again, then read to
> + * ensure the reset status is done. Invalidate the regmap cache since
> + * we're doing a hardware reset.
> + */
> + regcache_mark_dirty(st->map);
Sashiko has some comments here as well:
"
Because regcache_mark_dirty() only flags the cache as needing a sync but does
not clear the cached values, will regmap_update_bits() in this second
bus_setup() call silently skip the hardware writes?
The cache would still show the bits as set from the first bus_setup(), leaving
the hardware in its default reset state.
Would regcache_sync() or regcache_drop_region() be more appropriate here to
force the actual hardware writes?"
>
> - ret = regmap_read_poll_timeout(st->map, INV_ICM42607_REG_INT_STATUS,
> - val, val & INV_ICM42607_INT_STATUS_RESET_DONE,
> - INV_ICM42607_RESET_TIME_MS * 100,
> - INV_ICM42607_RESET_TIME_MS * 1000);
> - if (ret)
> - return dev_err_probe(dev, ret,
> - "reset error, reset done bit not set\n");
> + fsleep(INV_ICM42607_RESET_TIME_MS * 1000);
>
> ret = bus_setup(st);
> if (ret)
> return ret;
>
> + ret = regmap_read(st->map, INV_ICM42607_REG_INT_STATUS, &val);
> + if (ret || (!(val & INV_ICM42607_INT_STATUS_RESET_DONE)))
I agree with sashiko here. If you get ret you should return it.
if (ret)
return dev_err_probe(dev, ret, "reset error\n");
if (!(val & INV...))
return dev_err_probe(dev, -EIO, "reset done bit not set\n");
or something like that.
> + return dev_err_probe(dev, -EIO,
> + "reset error, reset done bit not set\n");
> +
> ret = regmap_set_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
> INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN);
> if (ret)
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
> new file mode 100644
> index 000000000000..49438fa6f867
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
> @@ -0,0 +1,102 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2026 InvenSense, Inc.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/property.h>
> +#include <linux/regmap.h>
> +#include <linux/spi/spi.h>
> +
> +#include "inv_icm42607.h"
> +
> +static int inv_icm42607_spi_bus_setup(struct inv_icm42607_state *st)
> +{
> + const struct device *dev = regmap_get_device(st->map);
> + const struct spi_device *spi = to_spi_device(dev);
> + unsigned int val;
> + int ret;
> +
> + if (spi->mode & SPI_3WIRE)
> + ret = regmap_clear_bits(st->map, INV_ICM42607_REG_DEVICE_CONFIG,
> + INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE);
> + else
> + ret = regmap_set_bits(st->map, INV_ICM42607_REG_DEVICE_CONFIG,
> + INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE);
I'm inclined to agree with sashiko on these. You need a 'write' not an update
as the read in a read / modify / write cycle will fail if you are in the wrong mode.
https://sashiko.dev/#/patchset/20260515130018.237378-1-macroalpha82%40gmail.com
Given we are near merging this it can be helpful that if you disagree with sashiko's
comments reply to say so and why.
> + if (ret)
> + return ret;
> +
> + ret = regmap_clear_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1,
> + INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN |
> + INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN);
> + if (ret)
> + return ret;
> +
> + val = FIELD_PREP(INV_ICM42607_DRIVE_CONFIG3_SPI_MASK,
> + INV_ICM42607_SLEW_RATE_INF_2NS);
> + ret = regmap_update_bits(st->map, INV_ICM42607_REG_DRIVE_CONFIG3,
> + INV_ICM42607_DRIVE_CONFIG3_SPI_MASK, val);
> + if (ret)
> + return ret;
> +
> + return regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
> + INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK,
> + INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_I2C_DIS);
> +}
> +
> +static int inv_icm42607_probe(struct spi_device *spi)
> +{
> + const struct inv_icm42607_hw *hw;
> + struct regmap *regmap;
> +
> + hw = spi_get_device_match_data(spi);
> + if (!hw)
> + return dev_err_probe(&spi->dev, -ENODEV,
> + "Failed to get SPI data\n");
> +
> + regmap = devm_regmap_init_spi(spi, &inv_icm42607_regmap_config);
> + if (IS_ERR(regmap))
> + return dev_err_probe(&spi->dev, PTR_ERR(regmap),
> + "Failed to register spi regmap %ld\n",
> + PTR_ERR(regmap));
> +
> + return inv_icm42607_core_probe(regmap, hw,
> + inv_icm42607_spi_bus_setup);
Fits on one line under 80 chars. Have another scan through the whole
series for places where edits during revisions might now allow for
less wrapping.
> +}
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V7 05/11] iio: imu: inv_icm42607: Add PM support for icm42607
2026-05-15 13:00 ` [PATCH V7 05/11] iio: imu: inv_icm42607: Add PM support for icm42607 Chris Morgan
2026-05-15 13:36 ` sashiko-bot
@ 2026-05-15 18:59 ` Jonathan Cameron
1 sibling, 0 replies; 26+ messages in thread
From: Jonathan Cameron @ 2026-05-15 18:59 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-iio, andy, nuno.sa, dlechner, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
On Fri, 15 May 2026 08:00:10 -0500
Chris Morgan <macroalpha82@gmail.com> wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add power management support for the ICM42607 device driver.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Sashiko had a comment but I'd ignore it as it's about the rather
complex magic that lets us do various forms of PM configs without
lots of __maybe_unused etc and it works so don't touch it :)
I'm too lazy to check what the dependency ordering is other than
pm_ptr() is true in cases where pm_sleep_ptr() whereas the
opposite is not true and that means the actual dead code removal
occurs at a different point in the code those macros are producing.
Anyhow a few really minor things inline. Otherwise LGTM
> ---
> drivers/iio/imu/inv_icm42607/inv_icm42607.h | 12 ++
> .../iio/imu/inv_icm42607/inv_icm42607_core.c | 143 ++++++++++++++++++
> .../iio/imu/inv_icm42607/inv_icm42607_i2c.c | 1 +
> .../iio/imu/inv_icm42607/inv_icm42607_spi.c | 1 +
> 4 files changed, 157 insertions(+)
>
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
> index 2c20e95b237a..5f37999e39a5 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> index 1088c5c7076f..e27ad0319a12 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> static int inv_icm42607_set_conf(struct inv_icm42607_state *st,
> const struct inv_icm42607_conf *conf)
> {
> @@ -198,11 +255,15 @@ static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st)
> {
> int ret;
>
> + if (st->vddio_en)
> + return 0;
> +
> ret = regulator_enable(st->vddio_supply);
> if (ret)
> return ret;
>
> fsleep(INV_ICM42607_POWER_UP_TIME_US);
> + st->vddio_en = 1;
It's a bool (which is good) so = true
>
> return 0;
> }
> @@ -211,7 +272,10 @@ static void inv_icm42607_disable_vddio_reg(void *_data)
> {
> struct inv_icm42607_state *st = _data;
>
> + if (!st->vddio_en)
> + return;
> regulator_disable(st->vddio_supply);
> + st->vddio_en = 0;
= false
> }
>
> +static int inv_icm42607_suspend(struct device *dev)
> +{
> + struct inv_icm42607_state *st = dev_get_drvdata(dev);
> + int ret;
> +
> + guard(mutex)(&st->lock);
> +
> + if (pm_runtime_suspended(dev))
> + return 0;
> +
> + ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
> + INV_ICM42607_SENSOR_MODE_OFF,
> + false, NULL);
> + if (ret)
> + return ret;
Trivial but blank line here
> + inv_icm42607_disable_vddio_reg(st);
> +
> + return 0;
> +}
> +
> +static int inv_icm42607_runtime_suspend(struct device *dev)
> +{
> + struct inv_icm42607_state *st = dev_get_drvdata(dev);
> + int ret = 0;
Assigned in all paths I think? If that stops being true I'd probably still
introduce the assignment when it becomes the case even though it's a little
more code churn.
> +
> + guard(mutex)(&st->lock);
> +
> + ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
> + INV_ICM42607_SENSOR_MODE_OFF, false,
> + NULL);
> + if (ret)
> + return ret;
> +
> + inv_icm42607_disable_vddio_reg(st);
> +
> + return 0;
> +}
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V7 06/11] iio: imu: inv_icm42607: Add Buffer support for icm42607
2026-05-15 13:00 ` [PATCH V7 06/11] iio: imu: inv_icm42607: Add Buffer " Chris Morgan
2026-05-15 13:35 ` sashiko-bot
@ 2026-05-15 19:20 ` Jonathan Cameron
1 sibling, 0 replies; 26+ messages in thread
From: Jonathan Cameron @ 2026-05-15 19:20 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-iio, andy, nuno.sa, dlechner, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
On Fri, 15 May 2026 08:00:11 -0500
Chris Morgan <macroalpha82@gmail.com> wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add all FIFO parsing and reading functions to support
> inv_icm42607 hardware.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Sashiko didn't do so well on this one so some of what it raised
looks wrong. E.g. the fifo stuff where you are deliberately clearing
bits with the writes rather than 'accidentally' writing over them.
There is one odd misbalance in here for fifo_en() calls
that I'd like a some comments on in the code.
It's not unheard of that we can't deal with the complexity
of update_scan_mode() only occuring in setup path for buffers
but most of the time we can by pushing stuff to posteenable()
/predisable()
The model for how the buffer setup flow works vs tear down doesn't
quite fit everything despite there being a lot of possible callbacks
so what you have may well be the best that we can do. However it
isn't obviously correct so a few comments please!
> ---
> drivers/iio/imu/inv_icm42607/Makefile | 1 +
> drivers/iio/imu/inv_icm42607/inv_icm42607.h | 27 +
> .../imu/inv_icm42607/inv_icm42607_buffer.c | 483 ++++++++++++++++++
> .../imu/inv_icm42607/inv_icm42607_buffer.h | 93 ++++
> .../iio/imu/inv_icm42607/inv_icm42607_core.c | 54 +-
> 5 files changed, 657 insertions(+), 1 deletion(-)
> create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
>
> diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile
> index be109102e203..3c9d08509793 100644
> --- a/drivers/iio/imu/inv_icm42607/Makefile
> +++ b/drivers/iio/imu/inv_icm42607/Makefile
> @@ -2,6 +2,7 @@
>
> obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o
> inv-icm42607-y += inv_icm42607_core.o
> +inv-icm42607-y += inv_icm42607_buffer.o
>
> obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o
> inv-icm42607-i2c-y += inv_icm42607_i2c.o
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
> index 5f37999e39a5..b00ee6a4d451 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
> @@ -14,6 +14,10 @@
> #include <linux/regmap.h>
> #include <linux/regulator/consumer.h>
>
> +#include <linux/iio/common/inv_sensors_timestamp.h>
> +
> +#include "inv_icm42607_buffer.h"
> +
> /* serial bus slew rates */
> enum inv_icm42607_slew_rate {
> INV_ICM42607_SLEW_RATE_20_60NS,
> @@ -84,6 +88,7 @@ struct inv_icm42607_sensor_conf {
> int odr;
> int filter;
> };
> +#define INV_ICM42607_SENSOR_CONF_INIT {-1, -1, -1, -1}
Spaces after { and before } but perhaps more interestingly - why is this
here given I don't see it used in this patch?
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> new file mode 100644
> index 000000000000..74e5213d9267
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> +
> +static int inv_icm42607_buffer_postdisable(struct iio_dev *indio_dev)
> +{
> + struct inv_icm42607_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_temp = 0;
> + unsigned int sleep_sensor = 0;
> + unsigned int sleep;
> + int ret;
> +
> + if (indio_dev == st->indio_gyro) {
> + sensor = INV_ICM42607_SENSOR_GYRO;
> + watermark = &st->fifo.watermark.gyro;
> + } else if (indio_dev == st->indio_accel) {
> + sensor = INV_ICM42607_SENSOR_ACCEL;
> + watermark = &st->fifo.watermark.accel;
> + } else {
> + return -EINVAL;
> + }
> +
> + mutex_lock(&st->lock);
> +
> + ret = inv_icm42607_buffer_set_fifo_en(st, st->fifo.en & ~sensor);
This is a little odd as normally I'd expect to see balance between
preenable and postdisable.
Seems you are unwinding stuff that ends up ultimately in update_scan_mode
If that's absolutely necessary (and you can't shift to the balanced calls
- given it's late in sequence postenable / predisable probably..)
then add a comment here.
> + if (ret)
> + goto out_unlock;
> +
> + *watermark = 0;
> + ret = inv_icm42607_buffer_update_watermark(st);
> + if (ret)
> + goto out_unlock;
> +
> +out_unlock:
> + mutex_unlock(&st->lock);
> +
> + /* sleep maximum required time */
> + sleep = max(sleep_sensor, sleep_temp);
> + if (sleep)
> + msleep(sleep);
Maybe introduce this stuff only when the values are not 0.
> +
> + pm_runtime_put_autosuspend(dev);
> +
> + return ret;
> +}
> +
> +const struct iio_buffer_setup_ops inv_icm42607_buffer_ops = {
> + .preenable = inv_icm42607_buffer_preenable,
> + .postenable = inv_icm42607_buffer_postenable,
> + .predisable = inv_icm42607_buffer_predisable,
> + .postdisable = inv_icm42607_buffer_postdisable,
> +};
> +
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> index e27ad0319a12..d5885fc3f7da 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> @@ -15,6 +15,7 @@
> #include <linux/regulator/consumer.h>
>
> #include "inv_icm42607.h"
> +#include "inv_icm42607_buffer.h"
>
> static bool inv_icm42607_is_volatile_reg(struct device *dev, unsigned int reg)
> {
> @@ -73,6 +74,40 @@ const struct inv_icm42607_hw inv_icm42607p_hw_data = {
> };
> EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607");
>
> +u32 inv_icm42607_odr_to_period(enum inv_icm42607_odr odr)
> +{
> + static const u32 odr_periods[INV_ICM42607_ODR_NB] = {
> + /* Reserved values */
> + 0, 0, 0, 0, 0,
> + /* 1600Hz */
[INV_ICM42607_ODR_1600HZ] = 625000,
etc and drop the comments.
> + 625000,
> + /* 800Hz */
> + 1250000,
> + /* 400Hz */
> + 2500000,
> + /* 200Hz */
> + 5000000,
> + /* 100 Hz */
> + 10000000,
> + /* 50Hz */
> + 20000000,
> + /* 25Hz */
> + 40000000,
> + /* 12.5Hz */
> + 80000000,
> + /* 6.25Hz */
> + 160000000,
> + /* 3.125Hz */
> + 320000000,
> + /* 1.5625Hz */
> + 640000000,
> + };
> +
> + odr = clamp(odr, INV_ICM42607_ODR_1600HZ, INV_ICM42607_ODR_1_5625HZ_LP);
Then this clamp will be more obviously correct.
> +
> + return odr_periods[odr];
> +}
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V7 08/11] iio: imu: inv_icm42607: Add Accelerometer for icm42607
2026-05-15 13:00 ` [PATCH V7 08/11] iio: imu: inv_icm42607: Add Accelerometer for icm42607 Chris Morgan
2026-05-15 13:34 ` sashiko-bot
@ 2026-05-15 19:29 ` Jonathan Cameron
1 sibling, 0 replies; 26+ messages in thread
From: Jonathan Cameron @ 2026-05-15 19:29 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-iio, andy, nuno.sa, dlechner, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
On Fri, 15 May 2026 08:00:13 -0500
Chris Morgan <macroalpha82@gmail.com> wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add icm42607 accelerometer sensor for icm42607.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Sashiko raised a few things. Otherwise I don't have any comments.
https://sashiko.dev/#/patchset/20260515130018.237378-1-macroalpha82%40gmail.com
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V7 09/11] iio: imu: inv_icm42607: Add Wake on Movement to icm42607
2026-05-15 13:00 ` [PATCH V7 09/11] iio: imu: inv_icm42607: Add Wake on Movement to icm42607 Chris Morgan
2026-05-15 13:42 ` sashiko-bot
@ 2026-05-15 19:36 ` Jonathan Cameron
1 sibling, 0 replies; 26+ messages in thread
From: Jonathan Cameron @ 2026-05-15 19:36 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-iio, andy, nuno.sa, dlechner, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
On Fri, 15 May 2026 08:00:14 -0500
Chris Morgan <macroalpha82@gmail.com> wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add support for wake on movement for the icm42607 driver.
Could you add a bit on what is supported on each of the two parts.
Not everything on the 42607P it seems.
There is quite a lot of fairly complex feedback on this in sashiko.
Obviously we are late in the game and I should have suggested
this earlier but I would have dropped this for the initial driver
just to keep it more manageable to review.
It's very much an add on feature.
Jonathan
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V7 10/11] iio: imu: inv_icm42607: Add Gyroscope to icm42607
2026-05-15 13:00 ` [PATCH V7 10/11] iio: imu: inv_icm42607: Add Gyroscope " Chris Morgan
2026-05-15 13:37 ` sashiko-bot
@ 2026-05-15 19:44 ` Jonathan Cameron
1 sibling, 0 replies; 26+ messages in thread
From: Jonathan Cameron @ 2026-05-15 19:44 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-iio, andy, nuno.sa, dlechner, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
On Fri, 15 May 2026 08:00:15 -0500
Chris Morgan <macroalpha82@gmail.com> wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add gyroscope functions to the icm42607 driver.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Hi Chris last one I'm getting to today...
> @@ -499,9 +512,10 @@ int inv_icm42607_buffer_fifo_parse(struct inv_icm42607_state *st)
> int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
> unsigned int count)
> {
> + struct inv_icm42607_sensor_state *gyro_st = iio_priv(st->indio_gyro);
> struct inv_icm42607_sensor_state *accel_st = iio_priv(st->indio_accel);
> struct inv_sensors_timestamp *ts;
> - s64 accel_ts;
> + s64 gyro_ts, accel_ts;
Most of what sashiko got matched the earlier accelerometer driver but this
is a good catch. you never initialize gyro_ts unlike accel_ts:
accel_ts = iio_get_time_ns(st->indio_accel);
> int ret;
>
> accel_ts = iio_get_time_ns(st->indio_accel);
> @@ -513,6 +527,14 @@ int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
> 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_icm42607_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);
^ permalink raw reply [flat|nested] 26+ messages in thread
end of thread, other threads:[~2026-05-15 19:44 UTC | newest]
Thread overview: 26+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-15 13:00 [PATCH V7 00/11] Add Invensense ICM42607 Chris Morgan
2026-05-15 13:00 ` [PATCH V7 01/11] dt-bindings: iio: imu: icm42600: Add mount-matrix to icm42600 Chris Morgan
2026-05-15 13:00 ` [PATCH V7 02/11] dt-bindings: iio: imu: icm42600: Add icm42607 binding Chris Morgan
2026-05-15 13:00 ` [PATCH V7 03/11] iio: imu: inv_icm42607: Add inv_icm42607 Core Driver Chris Morgan
2026-05-15 13:23 ` sashiko-bot
2026-05-15 18:31 ` Jonathan Cameron
2026-05-15 13:00 ` [PATCH V7 04/11] iio: imu: inv_icm42607: Add I2C and SPI For icm42607 Chris Morgan
2026-05-15 13:45 ` sashiko-bot
2026-05-15 18:43 ` Jonathan Cameron
2026-05-15 13:00 ` [PATCH V7 05/11] iio: imu: inv_icm42607: Add PM support for icm42607 Chris Morgan
2026-05-15 13:36 ` sashiko-bot
2026-05-15 18:59 ` Jonathan Cameron
2026-05-15 13:00 ` [PATCH V7 06/11] iio: imu: inv_icm42607: Add Buffer " Chris Morgan
2026-05-15 13:35 ` sashiko-bot
2026-05-15 19:20 ` Jonathan Cameron
2026-05-15 13:00 ` [PATCH V7 07/11] iio: imu: inv_icm42607: Add Temp Support in icm42607 Chris Morgan
2026-05-15 13:00 ` [PATCH V7 08/11] iio: imu: inv_icm42607: Add Accelerometer for icm42607 Chris Morgan
2026-05-15 13:34 ` sashiko-bot
2026-05-15 19:29 ` Jonathan Cameron
2026-05-15 13:00 ` [PATCH V7 09/11] iio: imu: inv_icm42607: Add Wake on Movement to icm42607 Chris Morgan
2026-05-15 13:42 ` sashiko-bot
2026-05-15 19:36 ` Jonathan Cameron
2026-05-15 13:00 ` [PATCH V7 10/11] iio: imu: inv_icm42607: Add Gyroscope " Chris Morgan
2026-05-15 13:37 ` sashiko-bot
2026-05-15 19:44 ` Jonathan Cameron
2026-05-15 13:00 ` [PATCH V7 11/11] arm64: dts: rockchip: Add icm42607p IMU for RG-DS Chris Morgan
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox