Skip to contentSkip to conversation

Add Invensense ICM42607

PATCH Superseded Chris Morgan 6 participants 29 messages Mar 30 – Apr 29

Subsystems (3)

v3 of 21 Superseded Latest is v21 →

lore
Share & tools
lore
#0 lore raw

v3 cover Add Invensense ICM42607

From: Chris Morgan <[email protected]>

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.

Chris Morgan (9):
dt-bindings: iio: imu: icm42607: Add devicetree binding
iio: imu: inv_icm42607: Add Core for inv_icm42607 Driver
iio: imu: inv_icm42607: Add I2C and SPI For icm42607
iio: imu: inv_icm42607: Add Buffer support functions to icm42607
iio: imu: inv_icm42607: Add Temperature Support in icm42607
iio: imu: inv_icm42607: Add Accelerometer for icm42607
iio: imu: inv_icm42607: Add Interrupt and Wake on Movement for

    icm42607

iio: imu: inv_icm42607: Add Gyroscope to icm42607
arm64: dts: rockchip: Add icm42607p IMU for RG-DS

 .../bindings/iio/imu/invensense,icm42607.yaml |   95 ++
 .../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   |  472 ++++++++
 .../iio/imu/inv_icm42607/inv_icm42607_accel.c | 1028 +++++++++++++++++
 .../imu/inv_icm42607/inv_icm42607_buffer.c    |  566 +++++++++
 .../imu/inv_icm42607/inv_icm42607_buffer.h    |  100 ++
 .../iio/imu/inv_icm42607/inv_icm42607_core.c  |  806 +++++++++++++
 .../iio/imu/inv_icm42607/inv_icm42607_gyro.c  |  579 ++++++++++
 .../iio/imu/inv_icm42607/inv_icm42607_i2c.c   |   93 ++
 .../iio/imu/inv_icm42607/inv_icm42607_spi.c   |  100 ++
 .../iio/imu/inv_icm42607/inv_icm42607_temp.c  |   81 ++
 .../iio/imu/inv_icm42607/inv_icm42607_temp.h  |   30 +
 16 files changed, 4015 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/iio/imu/invensense,icm42607.yaml
 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
Signature · 1 line
2.43.0
#1 lore raw

PATCH v3 1/9 dt-bindings: iio: imu: icm42607: Add devicetree binding

From: Chris Morgan <[email protected]>

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.

---
 .../bindings/iio/imu/invensense,icm42607.yaml | 95 +++++++++++++++++++
 1 file changed, 95 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/imu/invensense,icm42607.yaml
Documentation/devicetree/bindings/iio/imu/invensense,icm42607.yaml new file +95 -0
new file mode 100644
index 000000000000..bbacdee5b906
@@ -0,0 +1,95 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/imu/invensense,icm42607.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: InvenSense ICM-42607 Inertial Measurement Unit
+
+maintainers:
+  - Chris Morgan <[email protected]>
+
+description: |
+  6-axis MotionTracking device that combines a 3-axis gyroscope and a 3-axis
+  accelerometer.
+
+properties:
+  compatible:
+    enum:
+      - invensense,icm42607
+      - invensense,icm42607p
+  reg:
+    maxItems: 1
+
+  interrupts:
+    minItems: 1
+    maxItems: 2
+
+  interrupt-names:
+    minItems: 1
+    maxItems: 2
+    items:
+      enum:
+        - INT1
+        - INT2
+
+  drive-open-drain:
+    type: boolean
+
+  mount-matrix: true
+
+  spi-cpha: true
+  spi-cpol: true
+
+  vdd-supply:
+    description: Regulator that provides power to the sensor
+
+  vddio-supply:
+    description: Regulator that provides power to the bus
+
+required:
+  - compatible
+  - reg
+  - interrupts
+
+allOf:
+  - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        icm42607p@68 {
+            compatible = "invensense,icm42607p";
+            reg = <0x68>;
+            interrupt-parent = <&gpio2>;
+            interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
+            interrupt-names = "INT1";
+            vdd-supply = <&vdd>;
+            vddio-supply = <&vddio>;
+        };
+    };
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    spi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        icm42607p@0 {
+            compatible = "invensense,icm42607p";
+            reg = <0>;
+            spi-max-frequency = <24000000>;
+            spi-cpha;
+            spi-cpol;
+            interrupt-parent = <&gpio1>;
+            interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
+            interrupt-names = "INT1";
+            vdd-supply = <&vdd>;
+            vddio-supply = <&vddio>;
+        };
+    };
Signature · 1 line
2.43.0
maintainer
#2 lore raw

Re: [PATCH V3 1/9] dt-bindings: iio: imu: icm42607: Add devicetree binding

Quoted 2 lines · Chris Morgan

From: Chris Morgan <[email protected]>

Subject space is limited, so don't say devicetree binding twice:

dt-bindings: iio: imu: Add Invensense ICM42607

Quoted 6 lines

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.

Similar enough to use the same binding schema?

Quoted 14 lines
---
 .../bindings/iio/imu/invensense,icm42607.yaml | 95 +++++++++++++++++++
 1 file changed, 95 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/imu/invensense,icm42607.yaml
Documentation/devicetree/bindings/iio/imu/invensense,icm42607.yaml new file +2 -0
new file mode 100644
index 000000000000..bbacdee5b906
@@ -0,0 +1,95 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2

Documentation/devicetree/bindings/iio/imu/invensense,icm42607.yaml line 12

Quoted 10 lines
Documentation/devicetree/bindings/iio/imu/invensense,icm42607.yaml new file +10 -0
+---
+$id: http://devicetree.org/schemas/iio/imu/invensense,icm42607.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: InvenSense ICM-42607 Inertial Measurement Unit
+
+maintainers:
+  - Chris Morgan <[email protected]>
+
+description: |

Don't need '|'.

Documentation/devicetree/bindings/iio/imu/invensense,icm42607.yaml line 20

Quoted 8 lines
Documentation/devicetree/bindings/iio/imu/invensense,icm42607.yaml new file +8 -0
+  6-axis MotionTracking device that combines a 3-axis gyroscope and a 3-axis
+  accelerometer.
+
+properties:
+  compatible:
+    enum:
+      - invensense,icm42607
+      - invensense,icm42607p

blank line

Quoted 78 lines
Documentation/devicetree/bindings/iio/imu/invensense,icm42607.yaml new file +75 -0
+  reg:
+    maxItems: 1
+
+  interrupts:
+    minItems: 1
+    maxItems: 2
+
+  interrupt-names:
+    minItems: 1
+    maxItems: 2
+    items:
+      enum:
+        - INT1
+        - INT2
+
+  drive-open-drain:
+    type: boolean
+
+  mount-matrix: true
+
+  spi-cpha: true
+  spi-cpol: true
+
+  vdd-supply:
+    description: Regulator that provides power to the sensor
+
+  vddio-supply:
+    description: Regulator that provides power to the bus
+
+required:
+  - compatible
+  - reg
+  - interrupts
+
+allOf:
+  - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        icm42607p@68 {
+            compatible = "invensense,icm42607p";
+            reg = <0x68>;
+            interrupt-parent = <&gpio2>;
+            interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
+            interrupt-names = "INT1";
+            vdd-supply = <&vdd>;
+            vddio-supply = <&vddio>;
+        };
+    };
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    spi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        icm42607p@0 {
+            compatible = "invensense,icm42607p";
+            reg = <0>;
+            spi-max-frequency = <24000000>;
+            spi-cpha;
+            spi-cpol;
+            interrupt-parent = <&gpio1>;
+            interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
+            interrupt-names = "INT1";
+            vdd-supply = <&vdd>;
+            vddio-supply = <&vddio>;
+        };
+    };
Signature · 1 line
2.43.0
#3 lore raw

Re: [PATCH V3 1/9] dt-bindings: iio: imu: icm42607: Add devicetree binding

Quoted 7 lines · Rob Herring
Quoted 2 lines · Chris Morgan

From: Chris Morgan <[email protected]>

Subject space is limited, so don't say devicetree binding twice:

dt-bindings: iio: imu: Add Invensense ICM42607

Quoted 8 lines
Quoted 6 lines

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.

Similar enough to use the same binding schema?

Yes. I was under the (possibly mistaken) impression that different drivers required different binding schemas, but these should be similar enough to use the same schema if it's allowed.

Thank you,
Chris

Quoted 117 lines
Quoted 15 lines
---
 .../bindings/iio/imu/invensense,icm42607.yaml | 95 +++++++++++++++++++
 1 file changed, 95 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/imu/invensense,icm42607.yaml
Documentation/devicetree/bindings/iio/imu/invensense,icm42607.yaml new file +2 -0
new file mode 100644
index 000000000000..bbacdee5b906
@@ -0,0 +1,95 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2

Documentation/devicetree/bindings/iio/imu/invensense,icm42607.yaml line 12

Quoted 10 lines
Documentation/devicetree/bindings/iio/imu/invensense,icm42607.yaml new file +10 -0
+---
+$id: http://devicetree.org/schemas/iio/imu/invensense,icm42607.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: InvenSense ICM-42607 Inertial Measurement Unit
+
+maintainers:
+  - Chris Morgan <[email protected]>
+
+description: |

Don't need '|'.

Documentation/devicetree/bindings/iio/imu/invensense,icm42607.yaml line 20

Quoted 8 lines
Documentation/devicetree/bindings/iio/imu/invensense,icm42607.yaml new file +8 -0
+  6-axis MotionTracking device that combines a 3-axis gyroscope and a 3-axis
+  accelerometer.
+
+properties:
+  compatible:
+    enum:
+      - invensense,icm42607
+      - invensense,icm42607p

blank line

Quoted 78 lines
Documentation/devicetree/bindings/iio/imu/invensense,icm42607.yaml new file +75 -0
+  reg:
+    maxItems: 1
+
+  interrupts:
+    minItems: 1
+    maxItems: 2
+
+  interrupt-names:
+    minItems: 1
+    maxItems: 2
+    items:
+      enum:
+        - INT1
+        - INT2
+
+  drive-open-drain:
+    type: boolean
+
+  mount-matrix: true
+
+  spi-cpha: true
+  spi-cpol: true
+
+  vdd-supply:
+    description: Regulator that provides power to the sensor
+
+  vddio-supply:
+    description: Regulator that provides power to the bus
+
+required:
+  - compatible
+  - reg
+  - interrupts
+
+allOf:
+  - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        icm42607p@68 {
+            compatible = "invensense,icm42607p";
+            reg = <0x68>;
+            interrupt-parent = <&gpio2>;
+            interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
+            interrupt-names = "INT1";
+            vdd-supply = <&vdd>;
+            vddio-supply = <&vddio>;
+        };
+    };
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    spi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        icm42607p@0 {
+            compatible = "invensense,icm42607p";
+            reg = <0>;
+            spi-max-frequency = <24000000>;
+            spi-cpha;
+            spi-cpol;
+            interrupt-parent = <&gpio1>;
+            interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
+            interrupt-names = "INT1";
+            vdd-supply = <&vdd>;
+            vddio-supply = <&vddio>;
+        };
+    };
Signature · 1 line
2.43.0
maintainer
#4 lore raw

Re: [PATCH V3 1/9] dt-bindings: iio: imu: icm42607: Add devicetree binding

Quoted 13 lines · Chris Morgan
Quoted 11 lines · Rob Herring
Quoted 2 lines · Chris Morgan

From: Chris Morgan <[email protected]>

Subject space is limited, so don't say devicetree binding twice:

dt-bindings: iio: imu: Add Invensense ICM42607

Quoted 4 lines

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.

Quoted 8 lines
Quoted 4 lines
Quoted 2 lines

This device is functionally very similar to the icm42600 series with a very different register layout.

Similar enough to use the same binding schema?

Yes. I was under the (possibly mistaken) impression that different drivers required different binding schemas, but these should be similar enough to use the same schema if it's allowed.

Total fine. Bindings and drivers can be 1:1, N:1 or 1:N.

Rob

#5 lore raw

PATCH v3 2/9 iio: imu: inv_icm42607: Add Core for inv_icm42607 Driver

In reply to Chris Morgan

From: Chris Morgan <[email protected]>

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.

---
 drivers/iio/imu/inv_icm42607/inv_icm42607.h   | 424 ++++++++++++++++++
 .../iio/imu/inv_icm42607/inv_icm42607_core.c  | 300 +++++++++++++
 2 files changed, 724 insertions(+)
 create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607.h
 create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
drivers/iio/imu/inv_icm42607/inv_icm42607.h new file +424 -0
new file mode 100644
index 000000000000..609188c40ffc
@@ -0,0 +1,424 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#ifndef INV_ICM42607_H_
+#define INV_ICM42607_H_
+
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
+#include <linux/regmap.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/common/inv_sensors_timestamp.h>
+
+enum inv_icm42607_chip {
+	INV_CHIP_INVALID,
+	INV_CHIP_ICM42607P,
+	INV_CHIP_ICM42607,
+	INV_CHIP_NB,
+};
+
+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 {
+	/* Low-Noise mode sensor data filter */
+	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,
+
+	/* 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,
+};
+
+struct inv_icm42607_sensor_conf {
+	int mode;
+	int fs;
+	int odr;
+	int filter;
+};
+#define INV_ICM42607_SENSOR_CONF_INIT		{-1, -1, -1, -1}
+
+struct inv_icm42607_conf {
+	struct inv_icm42607_sensor_conf gyro;
+	struct inv_icm42607_sensor_conf accel;
+	bool temp_en;
+};
+
+struct inv_icm42607_suspended {
+	enum inv_icm42607_sensor_mode gyro;
+	enum inv_icm42607_sensor_mode accel;
+	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.
+ *  @chip:		chip identifier.
+ *  @name:		chip name.
+ *  @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.
+ *  @suspended:		suspended sensors configuration.
+ *  @indio_gyro:	gyroscope IIO device.
+ *  @indio_accel:	accelerometer IIO device.
+ *  @timestamp:         interrupt timestamps.
+ *  @apex:		APEX (Advanced Pedometer and Event detection) management
+ *  @buffer:		data transfer buffer aligned for DMA.
+ */
+struct inv_icm42607_state {
+	struct mutex lock;
+	enum inv_icm42607_chip chip;
+	const char *name;
+	struct regmap *map;
+	struct regulator *vddio_supply;
+	int irq;
+	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 {
+		s64 gyro;
+		s64 accel;
+	} timestamp;
+	struct inv_icm42607_apex apex;
+	u8 buffer[3] __aligned(IIO_DMA_MINALIGN);
+};
+
+/**
+ * struct inv_icm42607_sensor_state - sensor state variables
+ * @scales:		table of scales.
+ * @scales_len:		length (nb of items) of the scales table.
+ * @power_mode:		sensor requested power mode (for common frequencies)
+ * @filter:		sensor filter.
+ * @ts:			timestamp module states.
+ */
+struct inv_icm42607_sensor_state {
+	const int *scales;
+	size_t scales_len;
+	enum inv_icm42607_sensor_mode power_mode;
+	enum inv_icm42607_filter filter;
+	struct inv_sensors_timestamp ts;
+};
+
+/* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */
+
+/* Register Map for User Bank 0 */
+#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_DDR(_rate)	\
+	FIELD_PREP(INV_ICM42607_DRIVE_CONFIG1_I3C_DDR_MASK, (_rate))
+#define INV_ICM42607_DRIVE_CONFIG1_I3C_SDR_MASK		GENMASK(2, 0)
+#define INV_ICM42607_DRIVE_CONFIG1_I3C_SDR(_rate)	\
+	FIELD_PREP(INV_ICM42607_DRIVE_CONFIG1_I3C_SDR_MASK, (_rate))
+
+#define INV_ICM42607_REG_DRIVE_CONFIG2			0x04
+#define INV_ICM42607_DRIVE_CONFIG2_I2C_MASK		GENMASK(5, 3)
+#define INV_ICM42607_DRIVE_CONFIG2_I2C(_rate)		\
+	FIELD_PREP(INV_ICM42607_DRIVE_CONFIG2_I2C_MASK, (_rate))
+#define INV_ICM42607_DRIVE_CONFIG2_ALL_MASK		GENMASK(2, 0)
+#define INV_ICM42607_DRIVE_CONFIG2_ALL(_rate)		\
+	FIELD_PREP(INV_ICM42607_DRIVE_CONFIG2_ALL_MASK, (_rate))
+
+#define INV_ICM42607_REG_DRIVE_CONFIG3			0x05
+#define INV_ICM42607_DRIVE_CONFIG3_SPI_MASK		GENMASK(2, 0)
+#define INV_ICM42607_DRIVE_CONFIG3_SPI(_rate)		\
+	FIELD_PREP(INV_ICM42607_DRIVE_CONFIG3_SPI_MASK, (_rate))
+
+#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_GYRO(_mode)		\
+	FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, (_mode))
+#define INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK		GENMASK(1, 0)
+#define INV_ICM42607_PWR_MGMT0_ACCEL(_mode)		\
+	FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, (_mode))
+
+#define INV_ICM42607_REG_GYRO_CONFIG0			0x20
+#define INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK		GENMASK(6, 5)
+#define INV_ICM42607_GYRO_CONFIG0_FS_SEL(_fs)		\
+	FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK, (_fs))
+#define INV_ICM42607_GYRO_CONFIG0_ODR_MASK		GENMASK(3, 0)
+#define INV_ICM42607_GYRO_CONFIG0_ODR(_odr)		\
+	FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_ODR_MASK, (_odr))
+
+#define INV_ICM42607_REG_ACCEL_CONFIG0			0x21
+#define INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK		GENMASK(6, 5)
+#define INV_ICM42607_ACCEL_CONFIG0_FS_SEL(_fs)		\
+	FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK, (_fs))
+#define INV_ICM42607_ACCEL_CONFIG0_ODR_MASK		GENMASK(3, 0)
+#define INV_ICM42607_ACCEL_CONFIG0_ODR(_odr)		\
+	FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_ODR_MASK, (_odr))
+
+#define INV_ICM42607_REG_TEMP_CONFIG0			0x22
+#define INV_ICM42607_TEMP_CONFIG0_FILTER_MASK		GENMASK(6, 4)
+#define INV_ICM42607_TEMP_CONFIG0_FILTER(_filter)	\
+	FIELD_PREP(INV_ICM42607_TEMP_CONFIG0_FILTER_MASK, (_filter))
+
+#define INV_ICM42607_REG_GYRO_CONFIG1			0x23
+#define INV_ICM42607_GYRO_CONFIG1_FILTER_MASK		GENMASK(2, 0)
+#define INV_ICM42607_GYRO_CONFIG1_FILTER(_filter)	\
+	FIELD_PREP(INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, (_filter))
+
+#define INV_ICM42607_REG_ACCEL_CONFIG1			0x24
+#define INV_ICM42607_ACCEL_CONFIG1_AVG_MASK		GENMASK(6, 4)
+#define INV_ICM42607_ACCEL_CONFIG1_AVG(_avg)		\
+	FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_AVG_MASK, (_avg))
+#define INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK		GENMASK(2, 0)
+#define INV_ICM42607_ACCEL_CONFIG1_FILTER(_filter)	\
+	FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, (_filter))
+
+#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_APEX_CONFIG1_DMP_ODR(_odr)	\
+	FIELD_PREP(INV_ICM42607_APEX_CONFIG1_DMP_ODR_MASK, (_odr))
+
+#define INV_ICM42607_REG_WOM_CONFIG			0x27
+#define INV_ICM42607_WOM_CONFIG_INT_DUR_MASK		GENMASK(4, 3)
+#define INV_ICM42607_WOM_CONFIG_INT_DUR(_dur)		\
+	FIELD_PREP(INV_ICM42607_WOM_CONFIG_INT_DUR_MASK, (_dur))
+#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_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_PLL		\
+	FIELD_PREP(INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK, 1)
+
+#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_MS			100
+#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 *);
+
+u32 inv_icm42607_odr_to_period(enum inv_icm42607_odr odr);
+
+int inv_icm42607_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
+			     unsigned int writeval, unsigned int *readval);
+
+int inv_icm42607_core_probe(struct regmap *regmap, int chip,
+			    inv_icm42607_bus_setup bus_setup);
+
+#endif
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +300 -0

300 lines not on this page · raw message

Signature · 1 line
2.43.0
reviewer
#6 lore raw

Re: [PATCH V3 2/9] iio: imu: inv_icm42607: Add Core for inv_icm42607 Driver

drivers/iio/imu/inv_icm42607/inv_icm42607.h line 242

Quoted 12 lines · Chris Morgan

From: Chris Morgan <[email protected]>

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.

drivers/iio/imu/inv_icm42607/inv_icm42607.h new file +6 -0
+#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_GYRO(_mode)		\
+	FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, (_mode))

We usually try to avoid macros that hide FIELD_PREP(). IMHO, it makes the code harder to read because you have to jump back and forth to definitions to see if it really is that.

Quoted 4 lines
drivers/iio/imu/inv_icm42607/inv_icm42607.h new file +4 -0
+#define INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK		GENMASK(1, 0)
+#define INV_ICM42607_PWR_MGMT0_ACCEL(_mode)		\
+	FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, (_mode))
+

...

Quoted 18 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +18 -0
+u32 inv_icm42607_odr_to_period(enum inv_icm42607_odr odr)
+{
+	static 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 */

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 84

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +10 -0
+		40000000,
+		/* 12.5Hz */
+		80000000,
+		/* 6.25Hz */
+		160000000,
+		/* 3.125Hz */
+		320000000,
+		/* 1.5625Hz */
+		640000000,
+	};

No range checking to avoid out-of-bouds access?

Quoted 15 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +15 -0
+
+	return odr_periods[odr];
+}
+
+int inv_icm42607_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
+			     unsigned int writeval, unsigned int *readval)
+{
+	struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+
+	guard(mutex)(&st->lock);
+
+	if (readval)
+		return regmap_read(st->map, reg, readval);
+
+	return regmap_write(st->map, reg, writeval);

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 109

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +10 -0
+}
+
+static int inv_icm42607_set_conf(struct inv_icm42607_state *st,
+				 const struct inv_icm42607_conf *conf)
+{
+	unsigned int val;
+	int ret;
+
+	val = INV_ICM42607_PWR_MGMT0_GYRO(conf->gyro.mode) |
+	INV_ICM42607_PWR_MGMT0_ACCEL(conf->accel.mode);

Indent wrapped lines one tab or line up with INV_ after = .

Quoted 58 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +58 -0
+	/*
+	 * 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 = INV_ICM42607_GYRO_CONFIG0_FS_SEL(conf->gyro.fs) |
+	INV_ICM42607_GYRO_CONFIG0_ODR(conf->gyro.odr);
+	ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val);
+	if (ret)
+		return ret;
+
+	val = INV_ICM42607_ACCEL_CONFIG0_FS_SEL(conf->accel.fs) |
+	INV_ICM42607_ACCEL_CONFIG0_ODR(conf->accel.odr);
+	ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val);
+	if (ret)
+		return ret;
+
+	val = INV_ICM42607_GYRO_CONFIG1_FILTER(conf->gyro.filter);
+	ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG1, val);
+	if (ret)
+		return ret;
+
+	val = INV_ICM42607_ACCEL_CONFIG1_FILTER(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 inv_icm42607_hw *hw = &inv_icm42607_hw[st->chip];
+	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 != hw->whoami)

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 177

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +10 -0
+		dev_warn_probe(dev, -ENODEV,
+			       "invalid whoami %#02x expected %#02x (%s)\n",
+			       val, hw->whoami, hw->name);
+
+	st->name = hw->name;
+
+	ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET,
+			   INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET);
+	if (ret)
+		return ret;

nit: Add blank line here.

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 185

Quoted 8 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +8 -0
+	msleep(INV_ICM42607_RESET_TIME_MS);
+
+	ret = regmap_read(st->map, INV_ICM42607_REG_INT_STATUS, &val);
+	if (ret)
+		return ret;
+	if (!(val & INV_ICM42607_INT_STATUS_RESET_DONE))
+		return dev_err_probe(dev, -ENODEV,
+				     "reset error, reset done bit not set\n");

Could also replace this and msleep with regmap_read_poll_timeout().

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 193

Quoted 8 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +8 -0
+
+	ret = bus_setup(st);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
+				 INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN,
+				 INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN);

Simplify with regmap_set_bits().

Quoted 11 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +11 -0
+	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, hw->conf);
+}

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 214

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +10 -0
+
+static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st)
+{
+	int ret;
+
+	ret = regulator_enable(st->vddio_supply);
+	if (ret)
+		return ret;
+
+	usleep_range(3000, 4000);

Use fsleep() and add a comment to explain why the duration was chosen.

Quoted 12 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +12 -0
+
+	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, int chip,

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 236

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +10 -0
+			    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, irq_type;
+	bool open_drain;
+	int ret;
+
+	if (chip < INV_CHIP_INVALID || chip >= INV_CHIP_NB)

Only two chips are defined in regmap_read_poll_timeout, so this range checking seems wrong.

Quoted 32 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +32 -0
+		dev_warn_probe(dev, -ENODEV,
+			       "Invalid chip = %d\n", chip);
+
+	/* get INT1 only supported interrupt or fallback to first interrupt */
+	irq = fwnode_irq_get_byname(fwnode, "INT1");
+	if (irq < 0 && irq != -EPROBE_DEFER) {
+		dev_info(dev, "no INT1 interrupt defined, fallback to first interrupt\n");
+		irq = fwnode_irq_get(fwnode, 0);
+	}
+	if (irq < 0)
+		return dev_err_probe(dev, irq, "error missing INT1 interrupt\n");
+
+	irq_type = irq_get_trigger_type(irq);
+	if (!irq_type)
+		irq_type = IRQF_TRIGGER_FALLING;
+
+	open_drain = device_property_read_bool(dev, "drive-open-drain");
+
+	st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
+	if (!st)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, st);
+	mutex_init(&st->lock);
+	st->chip = chip;
+	st->map = regmap;
+	st->irq = irq;
+
+	ret = iio_read_mount_matrix(dev, &st->orientation);
+	if (ret) {
+		dev_err(dev, "failed to retrieve mounting matrix %d\n", ret);
+		return ret;

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 278

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +10 -0
+	}
+
+	ret = devm_regulator_get_enable(dev, "vdd");
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "Failed to get vdd regulator\n");
+
+	msleep(INV_ICM42607_POWER_UP_TIME_MS);
+
+	st->vddio_supply = devm_regulator_get(dev, "vddio");

If we aren't implementing power managament, we can just use devm_regulator_get_enabled() and avoid the devm_add_action_or_reset().

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 290

Quoted 12 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +12 -0
+	if (IS_ERR(st->vddio_supply))
+		return PTR_ERR(st->vddio_supply);
+
+	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) */

This comment would be better as part of the function doc comment.

Quoted 4 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +4 -0
+	ret = inv_icm42607_setup(st, bus_setup);
+
+	return ret;
+}
maintainer
#7 lore raw

Re: [PATCH V3 2/9] iio: imu: inv_icm42607: Add Core for inv_icm42607 Driver

In reply to Chris Morgan
Quoted 7 lines · On Mon, 30 Mar 2026 14:58:46 -0500 Chris Morgan <[email protected]> wrote:

From: Chris Morgan <[email protected]>

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.

Hi Chris,

I've avoided repeating a few things David raised, but there may well be overlap on some things. I'm not particularly keen on code that doesn't build at the end of a patch or even provide the Kconfig and makefile stuff to do so. Maybe it's too much effort given how you have this structured.

Thanks,

Jonathan

Quoted 16 lines
---
 drivers/iio/imu/inv_icm42607/inv_icm42607.h   | 424 ++++++++++++++++++
 .../iio/imu/inv_icm42607/inv_icm42607_core.c  | 300 +++++++++++++
 2 files changed, 724 insertions(+)
 create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607.h
 create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
drivers/iio/imu/inv_icm42607/inv_icm42607.h new file +3 -0
new file mode 100644
index 000000000000..609188c40ffc
@@ -0,0 +1,424 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2026 InvenSense, Inc.

drivers/iio/imu/inv_icm42607/inv_icm42607.h line 13

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607.h new file +10 -0
+ */
+
+#ifndef INV_ICM42607_H_
+#define INV_ICM42607_H_
+
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
+#include <linux/regmap.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>

Alphabetical order. Though fine to have an IIO specific block at the end.

Quoted 7 lines
drivers/iio/imu/inv_icm42607/inv_icm42607.h new file +6 -0
+#include <linux/iio/iio.h>
+#include <linux/iio/common/inv_sensors_timestamp.h>
 
+/* ODR values */
+enum inv_icm42607_odr {
+	INV_ICM42607_ODR_1600HZ = 5,
+	INV_ICM42607_ODR_800HZ,

drivers/iio/imu/inv_icm42607/inv_icm42607.h line 63

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607.h new file +10 -0
+	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,

If just here as number, then no terminating comma as we don't want to imply something might come after it. Same for all the other enums.

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607.h new file +10 -0
+};
+
+enum inv_icm42607_filter {
+	/* Low-Noise mode sensor data filter */
+	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,

drivers/iio/imu/inv_icm42607/inv_icm42607.h line 83

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607.h new file +10 -0
+	INV_ICM42607_FILTER_BW_25HZ,
+	INV_ICM42607_FILTER_BW_16HZ,
+
+	/* 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,

Bring these in when used. For now these to me look like they should be broken into two separate enums. I can't see how the will be used though so hard to tell.

drivers/iio/imu/inv_icm42607/inv_icm42607.h line 364

Quoted 6 lines
drivers/iio/imu/inv_icm42607/inv_icm42607.h new file +4 -0
+};
 
 
+#define INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK		GENMASK(1, 0)
+#define INV_ICM42607_INTF_CONFIG1_CLKSEL_PLL		\
+	FIELD_PREP(INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK, 1)

Define the value of the field to give that a name and use FIELD_PREP() inline.

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 7

Quoted 13 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +7 -0
new file mode 100644
index 000000000000..6b7078387568
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>

Most likely this should be more specific headers like dev_printk.h etc It is pretty rare it makes sense to include device.h if following IWYU approach (which we do for IIO drivers and is generally accepted across the kernel).

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 8

Quoted 1 line
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +1 -0
+#include <linux/module.h>

Alphabetical order.

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 59

Quoted 13 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +12 -0
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/regulator/consumer.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
 
+u32 inv_icm42607_odr_to_period(enum inv_icm42607_odr odr)
+{
+	static u32 odr_periods[INV_ICM42607_ODR_NB] = {

Hopefully the compiler can figure it out, but better as explicit const.

Quoted 28 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +28 -0
+		/* 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,
+	};
+
+	return odr_periods[odr];
+}

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 109

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +9 -0
 
+
+static int inv_icm42607_set_conf(struct inv_icm42607_state *st,
+				 const struct inv_icm42607_conf *conf)
+{
+	unsigned int val;
+	int ret;
+
+	val = INV_ICM42607_PWR_MGMT0_GYRO(conf->gyro.mode) |
+	INV_ICM42607_PWR_MGMT0_ACCEL(conf->accel.mode);

Align as

	val = INV_ICM42607_PWR_MGMT0_GYRO(conf->gyro.mode) |
	      INV_ICM42607_PWR_MGMT0_ACCEL(conf->accel.mode);

Though as mentioned in David's review, prefer to see the FIELD_PREP() inline.

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 116

Quoted 7 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +7 -0
+	/*
+	 * 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;

Could make this

	val |= FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL,
			  !conf->temp_en);

Not particularly important though if you prefer the if.

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 122

Quoted 6 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +6 -0
+	ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val);
+	if (ret)
+		return ret;
+
+	val = INV_ICM42607_GYRO_CONFIG0_FS_SEL(conf->gyro.fs) |
+	INV_ICM42607_GYRO_CONFIG0_ODR(conf->gyro.odr);

As above. If it's the second line of a statement, it must be indented to avoid readability issues. Fix all examples of this.

Quoted 46 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +46 -0
+	ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val);
+	if (ret)
+		return ret;
+
+	val = INV_ICM42607_ACCEL_CONFIG0_FS_SEL(conf->accel.fs) |
+	INV_ICM42607_ACCEL_CONFIG0_ODR(conf->accel.odr);
+	ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val);
+	if (ret)
+		return ret;
+
+	val = INV_ICM42607_GYRO_CONFIG1_FILTER(conf->gyro.filter);
+	ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG1, val);
+	if (ret)
+		return ret;
+
+	val = INV_ICM42607_ACCEL_CONFIG1_FILTER(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 inv_icm42607_hw *hw = &inv_icm42607_hw[st->chip];
+	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 != hw->whoami)
+		dev_warn_probe(dev, -ENODEV,

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 178

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +10 -0
+			       "invalid whoami %#02x expected %#02x (%s)\n",
+			       val, hw->whoami, hw->name);
+
+	st->name = hw->name;
+
+	ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET,
+			   INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET);
+	if (ret)
+		return ret;
+	msleep(INV_ICM42607_RESET_TIME_MS);

fsleep() here as sleeping longer is probably fine and that function will apply appropriate slack on the times.

Quoted 26 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +26 -0
+
+	ret = regmap_read(st->map, INV_ICM42607_REG_INT_STATUS, &val);
+	if (ret)
+		return ret;
+	if (!(val & INV_ICM42607_INT_STATUS_RESET_DONE))
+		return dev_err_probe(dev, -ENODEV,
+				     "reset error, reset done bit not set\n");
+
+	ret = bus_setup(st);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
+				 INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN,
+				 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, hw->conf);
+}

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 214

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +10 -0
+
+static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st)
+{
+	int ret;
+
+	ret = regulator_enable(st->vddio_supply);
+	if (ret)
+		return ret;
+
+	usleep_range(3000, 4000);

David covered this, fsleep() preferred.

Quoted 8 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +7 -0
+
+	return 0;
+}
 
+
+int inv_icm42607_core_probe(struct regmap *regmap, int chip,
+			    inv_icm42607_bus_setup bus_setup)
+{

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 238

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +10 -0
+	struct device *dev = regmap_get_device(regmap);
+	struct fwnode_handle *fwnode = dev_fwnode(dev);
+	struct inv_icm42607_state *st;
+	int irq, irq_type;
+	bool open_drain;
+	int ret;
+
+	if (chip < INV_CHIP_INVALID || chip >= INV_CHIP_NB)
+		dev_warn_probe(dev, -ENODEV,
+			       "Invalid chip = %d\n", chip);

Easily fits on one line shorter than 80 chars.

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 240

Quoted 2 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +2 -0
+
+	/* get INT1 only supported interrupt or fallback to first interrupt */

Why the fallback? I suspect this is copied from a driver that didn't (bug) support named interrupts from the start. Given you are doing so here, just insist on named one to simplify things. I'd imagine that if INT2 is ever supported, the device configuration will need to be different.

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 251

Quoted 11 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +11 -0
+	irq = fwnode_irq_get_byname(fwnode, "INT1");
+	if (irq < 0 && irq != -EPROBE_DEFER) {
+		dev_info(dev, "no INT1 interrupt defined, fallback to first interrupt\n");
+		irq = fwnode_irq_get(fwnode, 0);
+	}
+	if (irq < 0)
+		return dev_err_probe(dev, irq, "error missing INT1 interrupt\n");
+
+	irq_type = irq_get_trigger_type(irq);
+	if (!irq_type)
+		irq_type = IRQF_TRIGGER_FALLING;

Not used at this point. Bring it in when you use it so we can understand the default setting.

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 260

Quoted 9 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +9 -0
+
+	open_drain = device_property_read_bool(dev, "drive-open-drain");
+
+	st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
+	if (!st)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, st);
+	mutex_init(&st->lock);

For new code

	ret = devm_mutex_init(&st->lock);
	if (ret)
		return ret;

Brings a very small advantage if someone is debugging locks.

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 268

Quoted 8 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +8 -0
+	st->chip = chip;
+	st->map = regmap;
+	st->irq = irq;
+
+	ret = iio_read_mount_matrix(dev, &st->orientation);
+	if (ret) {
+		dev_err(dev, "failed to retrieve mounting matrix %d\n", ret);
+		return ret;

return dev_err_probe();

Quoted 15 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +15 -0
+	}
+
+	ret = devm_regulator_get_enable(dev, "vdd");
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "Failed to get vdd regulator\n");
+
+	msleep(INV_ICM42607_POWER_UP_TIME_MS);
+
+	st->vddio_supply = devm_regulator_get(dev, "vddio");
+	if (IS_ERR(st->vddio_supply))
+		return PTR_ERR(st->vddio_supply);
+
+	ret = inv_icm42607_enable_vddio_reg(st);
+	if (ret)

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 293

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +10 -0
+		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);
+
+	return ret;

Looking forwards I would instead do:

	if (ret)
		return ret;

	return 0;

I think that makes it easier to add the additional pm stuff in later patches.

Quoted 2 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +2 -0
+}
+EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607");
#8 lore raw

Re: [PATCH V3 2/9] iio: imu: inv_icm42607: Add Core for inv_icm42607 Driver

Quoted 2 lines · Jonathan Cameron

On Mon, 30 Mar 2026 14:58:46 -0500
Chris Morgan <[email protected]> wrote:

...

Quoted 7 lines

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 116

Quoted 2 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +2 -0
+	if (!conf->temp_en)
+		val |= INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL;

Could make this

	val |= FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL,
			  !conf->temp_en);

Not particularly important though if you prefer the if.

Isn't this becomes FIELD_MODIFY()?

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

Re: [PATCH V3 2/9] iio: imu: inv_icm42607: Add Core for inv_icm42607 Driver

Quoted 14 lines · On Tue, 14 Apr 2026 10:14:48 +0300 Andy Shevchenko…
Quoted 2 lines · Jonathan Cameron

On Mon, 30 Mar 2026 14:58:46 -0500
Chris Morgan <[email protected]> wrote:

...

Quoted 7 lines

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 116

Quoted 2 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c new file +2 -0
+	if (!conf->temp_en)
+		val |= INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL;  

Could make this

	val |= FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL,
			  !conf->temp_en);

Not particularly important though if you prefer the if.

Quoted 2 lines

Isn't this becomes FIELD_MODIFY()?

Yes, in this particular case as the bit has never been set before this line, they end up the same. If the compiler fails to notice it doesn't need to zero the bit if conf->temp_en is true might make it more efficient to do it the way I had it but given this isn't a fast path, FIELD_MODIFY() is fine.

Jonathan

#10 lore raw

PATCH v3 3/9 iio: imu: inv_icm42607: Add I2C and SPI For icm42607

In reply to Chris Morgan

From: Chris Morgan <[email protected]>

Add I2C and SPI driver support for InvenSense ICM-42607 devices. Include runtime power management on each device.

---
 drivers/iio/imu/inv_icm42607/inv_icm42607.h   |  14 ++
 .../iio/imu/inv_icm42607/inv_icm42607_core.c  | 204 ++++++++++++++++++
 .../iio/imu/inv_icm42607/inv_icm42607_i2c.c   |  93 ++++++++
 .../iio/imu/inv_icm42607/inv_icm42607_spi.c   | 100 +++++++++
 4 files changed, 411 insertions(+)
 create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
 create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
drivers/iio/imu/inv_icm42607/inv_icm42607.h +14 -0
index 609188c40ffc..7d13091aa8df 100644
@@ -11,6 +11,7 @@
 #include <linux/regmap.h>
 #include <linux/mutex.h>
 #include <linux/regulator/consumer.h>
+#include <linux/pm.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/common/inv_sensors_timestamp.h>
 
@@ -21,6 +22,16 @@ enum inv_icm42607_chip {
 	INV_CHIP_NB,
 };
 
+/* 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,
@@ -413,6 +424,9 @@ struct inv_icm42607_sensor_state {
 
 typedef int (*inv_icm42607_bus_setup)(struct inv_icm42607_state *);
 
+extern const struct regmap_config inv_icm42607_regmap_config;
+extern const struct dev_pm_ops inv_icm42607_pm_ops;
+
 u32 inv_icm42607_odr_to_period(enum inv_icm42607_odr odr);
 
 int inv_icm42607_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +204 -0

239 lines not on this page · raw message

drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c new file +93 -0

93 lines not on this page · raw message

drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c new file +100 -0

100 lines not on this page · raw message

Signature · 1 line
2.43.0
reviewer
#11 lore raw

Re: [PATCH V3 3/9] iio: imu: inv_icm42607: Add I2C and SPI For icm42607

Quoted 5 lines · Chris Morgan

From: Chris Morgan <[email protected]>

Add I2C and SPI driver support for InvenSense ICM-42607 devices.

Include runtime power management on each device.

Power management seems unrelated, so likey belongs in a separate patch.

Quoted 74 lines
---
 drivers/iio/imu/inv_icm42607/inv_icm42607.h   |  14 ++
 .../iio/imu/inv_icm42607/inv_icm42607_core.c  | 204 ++++++++++++++++++
 .../iio/imu/inv_icm42607/inv_icm42607_i2c.c   |  93 ++++++++
 .../iio/imu/inv_icm42607/inv_icm42607_spi.c   | 100 +++++++++
 4 files changed, 411 insertions(+)
 create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
 create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
drivers/iio/imu/inv_icm42607/inv_icm42607.h +14 -0
index 609188c40ffc..7d13091aa8df 100644
@@ -11,6 +11,7 @@
 #include <linux/regmap.h>
 #include <linux/mutex.h>
 #include <linux/regulator/consumer.h>
+#include <linux/pm.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/common/inv_sensors_timestamp.h>
 
@@ -21,6 +22,16 @@ enum inv_icm42607_chip {
 	INV_CHIP_NB,
 };
 
+/* 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,
@@ -413,6 +424,9 @@ struct inv_icm42607_sensor_state {
 
 typedef int (*inv_icm42607_bus_setup)(struct inv_icm42607_state *);
 
+extern const struct regmap_config inv_icm42607_regmap_config;
+extern const struct dev_pm_ops inv_icm42607_pm_ops;
+
 u32 inv_icm42607_odr_to_period(enum inv_icm42607_odr odr);
 
 int inv_icm42607_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +10 -0
index 6b7078387568..da04c820dab2 100644
@@ -12,12 +12,33 @@
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
 #include <linux/property.h>
 #include <linux/regmap.h>
 #include <linux/iio/iio.h>
 
 #include "inv_icm42607.h"
 
+static const struct regmap_range_cfg inv_icm42607_regmap_ranges[] = {
+	{
+		.name = "user bank",
+		.range_min = 0x0000,
+		.range_max = 0x00FF,
+		.window_start = 0,
+		.window_len = 0x0100,
+	},
+};

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c line 40

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +10 -0
+
+const struct regmap_config inv_icm42607_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0x00FF,
+	.ranges = inv_icm42607_regmap_ranges,
+	.num_ranges = ARRAY_SIZE(inv_icm42607_regmap_ranges),
+	.cache_type = REGCACHE_NONE,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm42607_regmap_config, "IIO_ICM42607");

It would make more sense to include the regmap config in the first patch since it is shared.

Quoted 79 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +61 -0
+
 struct inv_icm42607_hw {
 	uint8_t whoami;
 	const char *name;
@@ -86,6 +107,62 @@ u32 inv_icm42607_odr_to_period(enum inv_icm42607_odr odr)
 	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,
+				      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 = INV_ICM42607_PWR_MGMT0_GYRO(gyro) |
+	INV_ICM42607_PWR_MGMT0_ACCEL(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;
+}
+
 int inv_icm42607_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
 			     unsigned int writeval, unsigned int *readval)
 {
@@ -219,6 +296,10 @@ static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st)
 static void inv_icm42607_disable_vddio_reg(void *_data)
 {
 	struct inv_icm42607_state *st = _data;
+	struct device *dev = regmap_get_device(st->map);
+
+	if (pm_runtime_status_suspended(dev))
+		return;
 
 	regulator_disable(st->vddio_supply);
 }

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c int inv_icm42607_core_probe(struct regmap *regmap, int chip, · line 376

Quoted 7 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +4 -0
@@ -289,11 +370,134 @@ int inv_icm42607_core_probe(struct regmap *regmap, int chip,
 
 	/* Setup chip registers (includes WHOAMI check, reset check, bus setup) */
 	ret = inv_icm42607_setup(st, bus_setup);
+	if (ret)
+		return ret; /* Return error from setup (e.g., WHOAMI fail) */
+
+	/* Setup runtime power management */

Would add a blank line here if this is meant to apply to more than the following line. Otherwise it doesn't add much.

Quoted 11 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +6 -0
+	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 ret;
 }
 EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607");
 

...

Quoted 19 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c new file +13 -0
new file mode 100644
index 000000000000..eb72973debc5
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/property.h>
+

drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c line 23

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c new file +10 -0
+#include "inv_icm42607.h"
+
+static int inv_icm42607_i2c_bus_setup(struct inv_icm42607_state *st)
+{
+	unsigned int mask, val;
+	int ret;
+
+	ret = regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1,
+				 INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN |
+				 INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN, 0);

regmap_clear_bits()

drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c line 27

Quoted 4 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c new file +4 -0
+	if (ret)
+		return ret;
+
+	mask = INV_ICM42607_DRIVE_CONFIG2_I2C_MASK;

Local mask variable isn't helping much.

Quoted 11 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c new file +11 -0
+	val = INV_ICM42607_DRIVE_CONFIG2_I2C(INV_ICM42607_SLEW_RATE_12_36NS);
+	ret = regmap_update_bits(st->map, INV_ICM42607_REG_DRIVE_CONFIG2,
+				 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);
+}
+

drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c line 48

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c new file +10 -0
+static int inv_icm42607_probe(struct i2c_client *client)
+{
+	const void *match;
+	enum inv_icm42607_chip chip;
+	struct regmap *regmap;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
+		return -EOPNOTSUPP;
+
+	match = device_get_match_data(&client->dev);

Should be i2c_get_match_data(). And we recently decided to standardize on not checking for NULL return.

Quoted 64 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c new file +45 -0
+	if (!match)
+		return -EINVAL;
+	chip = (uintptr_t)match;
+
+	regmap = devm_regmap_init_i2c(client, &inv_icm42607_regmap_config);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	return inv_icm42607_core_probe(regmap, chip, inv_icm42607_i2c_bus_setup);
+}
+
+static const struct i2c_device_id inv_icm42607_id[] = {
+	{ "icm42607", INV_CHIP_ICM42607 },
+	{ "icm42607p", INV_CHIP_ICM42607P },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, inv_icm42607_id);
+
+static const struct of_device_id inv_icm42607_of_matches[] = {
+	{
+		.compatible = "invensense,icm42607",
+		.data = (void *)INV_CHIP_ICM42607,
+	}, {
+		.compatible = "invensense,icm42607p",
+		.data = (void *)INV_CHIP_ICM42607P,
+	},
+	{ }
+};
+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,
+		.pm = pm_ptr(&inv_icm42607_pm_ops),
+	},
+	.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");
drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c new file +13 -0
new file mode 100644
index 000000000000..51ce3deeb706
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/property.h>
+

drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c line 23

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c new file +10 -0
+#include "inv_icm42607.h"
+
+static int inv_icm42607_spi_bus_setup(struct inv_icm42607_state *st)
+{
+	unsigned int mask, val;
+	int ret;
+
+	ret = regmap_update_bits(st->map, INV_ICM42607_REG_DEVICE_CONFIG,
+				 INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE,
+				 INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE);

regmap_set_bits()

drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c line 29

Quoted 6 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c new file +6 -0
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1,
+				 INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN |
+				 INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN, 0);

regmap_clear_bits()

Quoted 14 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c new file +14 -0
+	if (ret)
+		return ret;
+
+	mask = INV_ICM42607_DRIVE_CONFIG3_SPI_MASK;
+	val = INV_ICM42607_DRIVE_CONFIG3_SPI(INV_ICM42607_SLEW_RATE_INF_2NS);
+	ret = regmap_update_bits(st->map, INV_ICM42607_REG_DRIVE_CONFIG3,
+				 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);
+}

drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c line 53

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c new file +10 -0
+
+static int inv_icm42607_probe(struct spi_device *spi)
+{
+	const void *match;
+	enum inv_icm42607_chip chip;
+	struct regmap *regmap;
+
+	match = device_get_match_data(&spi->dev);
+	if (!match)
+		return -EINVAL;

Should be spi_get_device_match_data(). And we recently decided to standardize on not checking for NULL return.

drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c line 54

Quoted 1 line
drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c new file +1 -0
+	chip = (uintptr_t)match;

uintptr_t is frowned upon in the kernel. Stick with kernel_ulong_t.

Quoted 18 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c new file +18 -0
+
+	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, chip,
+				       inv_icm42607_spi_bus_setup);
+}
+
+static const struct of_device_id inv_icm42607_of_matches[] = {
+	{
+		.compatible = "invensense,icm42607",
+		.data = (void *)INV_CHIP_ICM42607,
+	},
+	{
+		.compatible = "invensense,icm42607p",

drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c line 82

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c new file +10 -0
+		.data = (void *)INV_CHIP_ICM42607P,
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, inv_icm42607_of_matches);
+
+static const struct spi_device_id inv_icm42607_spi_id_table[] = {
+	{ "icm42607", INV_CHIP_ICM42607 },
+	{ "icm42607p", INV_CHIP_ICM42607P },
+	{ },

No trailing comma.

Quoted 18 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c new file +18 -0
+};
+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,
+		.pm = &inv_icm42607_pm_ops,
+	},
+	.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");
#12 lore raw

PATCH v3 4/9 iio: imu: inv_icm42607: Add Buffer support functions to icm42607

In reply to Chris Morgan

From: Chris Morgan <[email protected]>

Add all FIFO parsing and reading functions to support inv_icm42607 hardware.

---
 drivers/iio/imu/inv_icm42607/inv_icm42607.h   |   4 +
 .../imu/inv_icm42607/inv_icm42607_buffer.c    | 496 ++++++++++++++++++
 .../imu/inv_icm42607/inv_icm42607_buffer.h    |  98 ++++
 .../iio/imu/inv_icm42607/inv_icm42607_core.c  |  25 +
 4 files changed, 623 insertions(+)
 create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
 create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
drivers/iio/imu/inv_icm42607/inv_icm42607.h +4 -0
index 7d13091aa8df..5530fd3bc03f 100644
@@ -15,6 +15,8 @@
 #include <linux/iio/iio.h>
 #include <linux/iio/common/inv_sensors_timestamp.h>
 
+#include "inv_icm42607_buffer.h"
+
 enum inv_icm42607_chip {
 	INV_CHIP_INVALID,
 	INV_CHIP_ICM42607P,
@@ -137,6 +139,7 @@ struct inv_icm42607_apex {
  *  @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.
  */
 struct inv_icm42607_state {
@@ -156,6 +159,7 @@ struct inv_icm42607_state {
 		s64 accel;
 	} timestamp;
 	struct inv_icm42607_apex apex;
+	struct inv_icm42607_fifo fifo;
 	u8 buffer[3] __aligned(IIO_DMA_MINALIGN);
 };
 
drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c new file +496 -0

496 lines not on this page · raw message

drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h new file +98 -0

98 lines not on this page · raw message

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +25 -0
index da04c820dab2..344071089042 100644
@@ -18,6 +18,7 @@
 #include <linux/iio/iio.h>
 
 #include "inv_icm42607.h"
+#include "inv_icm42607_buffer.h"
 
 static const struct regmap_range_cfg inv_icm42607_regmap_ranges[] = {
 	{
@@ -373,6 +374,11 @@ int inv_icm42607_core_probe(struct regmap *regmap, int chip,
 	if (ret)
 		return ret; /* Return error from setup (e.g., WHOAMI fail) */
 
+	/* Initialize buffer/FIFO handling */
+	ret = inv_icm42607_buffer_init(st);
+	if (ret)
+		return ret;
+
 	/* Setup runtime power management */
 	ret = devm_pm_runtime_set_active_enabled(dev);
 	if (ret)
@@ -405,6 +411,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;
+	}
+
 	/* 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);
@@ -436,6 +449,8 @@ 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;
 	int ret;
@@ -462,6 +477,16 @@ static int inv_icm42607_resume(struct device *dev)
 	ret = inv_icm42607_set_pwr_mgmt0(st, st->suspended.gyro,
 					 st->suspended.accel,
 					 st->suspended.temp, NULL);
+	if (ret)
+		return ret;
+
+	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);
+	}
+
 	return ret;
 }
 
Signature · 1 line
2.43.0
maintainer
#13 lore raw

Re: [PATCH V3 4/9] iio: imu: inv_icm42607: Add Buffer support functions to icm42607

Quoted 6 lines · On Mon, 30 Mar 2026 14:58:48 -0500 Chris Morgan <[email protected]> wrote:

From: Chris Morgan <[email protected]>

Add all FIFO parsing and reading functions to support inv_icm42607 hardware.

A few minor things inline.

Thanks,

Jonathan

Quoted 26 lines
---
 drivers/iio/imu/inv_icm42607/inv_icm42607.h   |   4 +
 .../imu/inv_icm42607/inv_icm42607_buffer.c    | 496 ++++++++++++++++++
 .../imu/inv_icm42607/inv_icm42607_buffer.h    |  98 ++++
 .../iio/imu/inv_icm42607/inv_icm42607_core.c  |  25 +
 4 files changed, 623 insertions(+)
 create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
 create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
drivers/iio/imu/inv_icm42607/inv_icm42607.h +0 -0
index 7d13091aa8df..5530fd3bc03f 100644
drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c new file +6 -0
new file mode 100644
index 000000000000..4f5f199586fc
@@ -0,0 +1,496 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/kernel.h>

drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c line 16

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c new file +10 -0
+#include <linux/device.h>
+#include <linux/minmax.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/delay.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/common/inv_sensors_timestamp.h>
+#include <linux/iio/iio.h>

Alphabetical order (+ a block for IIO) for headers.

Quoted 4 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c new file +4 -0
+
+static unsigned int inv_icm42607_wm_truncate(unsigned int watermark,
+					     size_t packet_size)
+{

drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c line 169

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c new file +10 -0
+	size_t wm_size;
+	unsigned int wm;
+
+	wm_size = watermark * packet_size;
+	if (wm_size > INV_ICM42607_FIFO_WATERMARK_MAX)
+		wm_size = INV_ICM42607_FIFO_WATERMARK_MAX;
+
+	wm = wm_size / packet_size;
+
+	return wm;

Why not

	return wm_size / packet_size;
Quoted 5 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c new file +3 -0
+}
 
 
+int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
+				     unsigned int count)

drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c line 475

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c new file +10 -0
+{
+	s64 gyro_ts, accel_ts;
+	int ret;
+
+	gyro_ts = iio_get_time_ns(st->indio_gyro);
+	accel_ts = iio_get_time_ns(st->indio_accel);
+
+	ret = inv_icm42607_buffer_fifo_read(st, count);
+
+	return ret;

Either

	return inv_...
or
	ret = 
	if (ret)
		return ret;

	return 0;

2nd one only if this is going to get more complex in later patches.

Quoted 4 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c new file +4 -0
+}
+
+int inv_icm42607_buffer_init(struct inv_icm42607_state *st)
+{

drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c line 489

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c new file +10 -0
+	unsigned int val;
+	int ret;
+
+	st->fifo.watermark.eff_gyro = 1;
+	st->fifo.watermark.eff_accel = 1;
+
+	/* Configure FIFO_COUNT format in bytes and big endian */
+	val = INV_ICM42607_INTF_CONFIG0_FIFO_COUNT_ENDIAN;
+	ret = regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
+				 val, val);

regmap_set_bits();

Quoted 13 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c new file +7 -0
+	if (ret)
+		return ret;
+
+	/* Initialize FIFO in bypass mode */
+	return regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1,
+			    INV_ICM42607_FIFO_CONFIG1_BYPASS);
+}
drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h new file +0 -0
new file mode 100644
index 000000000000..64a66c00a861

...

drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h line 52

Quoted 6 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h new file +6 -0
+/* FIFO data packet */
+struct inv_icm42607_fifo_sensor_data {
+	__be16 x;
+	__be16 y;
+	__be16 z;
+} __packed;

Why packed? It will be anyway unless I'm missing something.

Quoted 6 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h new file +1 -0
+#define INV_ICM42607_FIFO_DATA_INVALID		-32768
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +0 -0
index da04c820dab2..344071089042 100644

...

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +2 -0
@@ -436,6 +449,8 @@ 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;
 	int ret;

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c static int inv_icm42607_resume(struct device *dev) · line 483

Quoted 7 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +4 -0
@@ -462,6 +477,16 @@ static int inv_icm42607_resume(struct device *dev)
 	ret = inv_icm42607_set_pwr_mgmt0(st, st->suspended.gyro,
 					 st->suspended.accel,
 					 st->suspended.temp, NULL);
+	if (ret)
+		return ret;
+
+	if (st->fifo.on) {

I've not checked, but if this doesn't get more complex you can do

	if (!st->fifo.on)
		return 0;

	inv_....

	return regmap_write();
Quoted 9 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +6 -0
+		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);
+	}
+
 	return ret;
 }
 
#14 lore raw

PATCH v3 5/9 iio: imu: inv_icm42607: Add Temperature Support in icm42607

In reply to Chris Morgan

From: Chris Morgan <[email protected]>

Add functions for reading temperature sensor data.

---
 drivers/iio/imu/inv_icm42607/inv_icm42607.h   |  3 +
 .../iio/imu/inv_icm42607/inv_icm42607_core.c  | 17 ++++
 .../iio/imu/inv_icm42607/inv_icm42607_temp.c  | 81 +++++++++++++++++++
 .../iio/imu/inv_icm42607/inv_icm42607_temp.h  | 30 +++++++
 4 files changed, 131 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
drivers/iio/imu/inv_icm42607/inv_icm42607.h +3 -0
index 5530fd3bc03f..086848c8fd3b 100644
@@ -433,6 +433,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_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
 			     unsigned int writeval, unsigned int *readval);
 
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +17 -0
index 344071089042..735a262dc103 100644
@@ -164,6 +164,23 @@ 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 = INV_ICM42607_TEMP_CONFIG0_FILTER(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);
+}
+
 int inv_icm42607_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
 			     unsigned int writeval, unsigned int *readval)
 {
drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c new file +81 -0

81 lines not on this page · raw message

drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h new file +30 -0
new file mode 100644
index 000000000000..d0bd6c460ff2
@@ -0,0 +1,30 @@
+/* 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/iio/iio.h>
+
+#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
Signature · 1 line
2.43.0
reviewer
#15 lore raw

Re: [PATCH V3 5/9] iio: imu: inv_icm42607: Add Temperature Support in icm42607

Quoted 84 lines · Chris Morgan

From: Chris Morgan <[email protected]>

Add functions for reading temperature sensor data.

---
 drivers/iio/imu/inv_icm42607/inv_icm42607.h   |  3 +
 .../iio/imu/inv_icm42607/inv_icm42607_core.c  | 17 ++++
 .../iio/imu/inv_icm42607/inv_icm42607_temp.c  | 81 +++++++++++++++++++
 .../iio/imu/inv_icm42607/inv_icm42607_temp.h  | 30 +++++++
 4 files changed, 131 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
drivers/iio/imu/inv_icm42607/inv_icm42607.h +3 -0
index 5530fd3bc03f..086848c8fd3b 100644
@@ -433,6 +433,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_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
 			     unsigned int writeval, unsigned int *readval);
 
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +17 -0
index 344071089042..735a262dc103 100644
@@ -164,6 +164,23 @@ 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 = INV_ICM42607_TEMP_CONFIG0_FILTER(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);
+}
+
 int inv_icm42607_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
 			     unsigned int writeval, unsigned int *readval)
 {
drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c new file +22 -0
new file mode 100644
index 000000000000..b42eb78cd960
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.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);

drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c line 32

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c new file +10 -0
+	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 = (__be16 *)&st->buffer[0];

Can we make buffer __be16 buffer[] to avoid cast and ensure proper alignment?

drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c line 37

Quoted 5 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c new file +5 -0
+	ret = regmap_bulk_read(st->map, INV_ICM42607_REG_TEMP_DATA1, raw, sizeof(*raw));
+	if (ret)
+		return ret;
+
+	*temp = (s16)be16_to_cpup(raw);

cast is not needed. temp is already s16.

Quoted 27 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c new file +27 -0
+	if (*temp == INV_ICM42607_DATA_INVALID)
+		ret = -EINVAL;
+
+	return ret;
+}
+
+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;

drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c line 74

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c new file +10 -0
+	/*
+	 * T°C = (temp / 128) + 25
+	 * Tm°C = 1000 * ((temp * 100 / 12800) + 25)
+	 * scale: 100000 / 12800 ~= 7.8125
+	 * offset: 25000
+	 */
+	case IIO_CHAN_INFO_SCALE:
+		*val = 7;
+		*val2 = 812500;
+		return IIO_VAL_INT_PLUS_MICRO;

Could use IIO_VAL_INT_PLUS_NANO to get more exact value.

Quoted 7 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c new file +7 -0
+	case IIO_CHAN_INFO_OFFSET:
+		*val = 25000;
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+}
#16 lore raw

PATCH v3 6/9 iio: imu: inv_icm42607: Add Accelerometer for icm42607

In reply to Chris Morgan

From: Chris Morgan <[email protected]>

Add icm42607 accelerometer sensor for icm42607.

---
 drivers/iio/imu/inv_icm42607/inv_icm42607.h   |  12 +
 .../iio/imu/inv_icm42607/inv_icm42607_accel.c | 725 ++++++++++++++++++
 .../imu/inv_icm42607/inv_icm42607_buffer.c    |  49 +-
 .../imu/inv_icm42607/inv_icm42607_buffer.h    |   2 +
 .../iio/imu/inv_icm42607/inv_icm42607_core.c  |  60 ++
 5 files changed, 847 insertions(+), 1 deletion(-)
 create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
drivers/iio/imu/inv_icm42607/inv_icm42607.h +12 -0
index 086848c8fd3b..56bb09e2c304 100644
@@ -431,8 +431,16 @@ typedef int (*inv_icm42607_bus_setup)(struct inv_icm42607_state *);
 extern const struct regmap_config inv_icm42607_regmap_config;
 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);
 
@@ -442,4 +450,8 @@ int inv_icm42607_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
 int inv_icm42607_core_probe(struct regmap *regmap, int chip,
 			    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
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +725 -0

725 lines not on this page · raw message

drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c +48 -1

77 lines not on this page · raw message

drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h +2 -0
index 64a66c00a861..6d8d7fa7bd13 100644
@@ -92,6 +92,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);
 
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +60 -0

78 lines not on this page · raw message

Signature · 1 line
2.43.0
reviewer
#17 lore raw

Re: [PATCH V3 6/9] iio: imu: inv_icm42607: Add Accelerometer for icm42607

Quoted 4 lines · Chris Morgan

From: Chris Morgan <[email protected]>

Add icm42607 accelerometer sensor for icm42607.

...

drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c line 215

Quoted 3 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +3 -0
+static const unsigned long inv_icm42607_accel_scan_masks[] = {
+	/* 3-axis accel + temperature */
+	INV_ICM42607_SCAN_MASK_ACCEL_3AXIS | INV_ICM42607_SCAN_MASK_TEMP,

This is going to make it so that the temperature channel is always read even if it isn't enabled and additional work is needed when pushing to buffers to remove it again.

It looks like it is possible to read accel and temp separatly, so there shuold be two more lines here,

	INV_ICM42607_SCAN_MASK_ACCEL_3AXIS,
	INV_ICM42607_SCAN_MASK_TEMP,

I forget what the correct order is though.

Quoted 32 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +32 -0
+	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;
+	unsigned int sleep;
+	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)

drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c line 257

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +10 -0
+			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);
+	/* sleep maximum required time */

Would be better if the comment explain _why_ we need to sleep.

The code is pretty obvious that it does what the comment says, so it doesn't add much.

drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c line 259

Quoted 2 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +2 -0
+	sleep = max(sleep_accel, sleep_temp);
+	if (sleep)

Probably don't need the if here as msleep() should handle 0 without actually sleeping.

Quoted 43 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +43 -0
+		msleep(sleep);
+	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);

drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c line 312

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +10 -0
+	if (ret)
+		return ret;
+
+	/* read accel register data */
+	data = (__be16 *)&st->buffer[0];
+	ret = regmap_bulk_read(st->map, reg, data, sizeof(*data));
+	if (ret)
+		return ret;
+
+	*val = (int16_t)be16_to_cpup(data);

We don't use int16_t in the kernel (ideally). Stick with s16.

Although cast isn't needed here since val is already s16.

drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c line 319

Quoted 7 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +7 -0
+	if (*val == INV_ICM42607_DATA_INVALID)
+		ret = -EINVAL;
+
+	return ret;
+}
+
+/* IIO format int + nano */

Usually we make these 2-D arrays for readability and then cast to int * if needed.

Quoted 15 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +15 -0
+static const int inv_icm42607_accel_scale[] = {
+	/* +/- 16G => 0.004788403 m/s-2 */
+	[2 * INV_ICM42607_ACCEL_FS_16G] = 0,
+	[2 * INV_ICM42607_ACCEL_FS_16G + 1] = 4788403,
+	/* +/- 8G => 0.002394202 m/s-2 */
+	[2 * INV_ICM42607_ACCEL_FS_8G] = 0,
+	[2 * INV_ICM42607_ACCEL_FS_8G + 1] = 2394202,
+	/* +/- 4G => 0.001197101 m/s-2 */
+	[2 * INV_ICM42607_ACCEL_FS_4G] = 0,
+	[2 * INV_ICM42607_ACCEL_FS_4G + 1] = 1197101,
+	/* +/- 2G => 0.000598550 m/s-2 */
+	[2 * INV_ICM42607_ACCEL_FS_2G] = 0,
+	[2 * INV_ICM42607_ACCEL_FS_2G + 1] = 598550,
+};
+

...

drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c line 501

Quoted 7 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +7 -0
+static int inv_icm42607_accel_read_calibbias(struct inv_icm42607_state *st,
+					     struct iio_chan_spec const *chan,
+					     int *val, int *val2)
+{
+	/* Not actually supported in the ICM-42607P registers */
+	return -EOPNOTSUPP;
+}

Can we just not create the attribute instead of returning an error?

Quoted 4 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +4 -0
+static int inv_icm42607_accel_write_raw_get_fmt(struct iio_dev *indio_dev,
+						struct iio_chan_spec const *chan,
+						long mask)
+{

drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c line 584

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +10 -0
+	if (chan->type != IIO_ACCEL)
+		return -EINVAL;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		return IIO_VAL_INT_PLUS_NANO;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		return IIO_VAL_INT_PLUS_MICRO;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		return IIO_VAL_INT_PLUS_MICRO;

Can write this as:

	case IIO_CHAN_INFO_SAMP_FREQ:
	case IIO_CHAN_INFO_CALIBBIAS:
		return IIO_VAL_INT_PLUS_MICRO;
Quoted 5 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +5 -0
+	default:
+		return -EINVAL;
+	}
+}
+

...

Quoted 8 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +8 -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;
+

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st, · line 193

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +10 -0
+	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) {

We could use the regmap cache feature to avoid having to manual keep track of old values. Or just always write the same values anyway. I find that is nice when debugging hardware with a logic analyzer. Unless there is some measureable performance improvlment here?

Quoted 28 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +28 -0
+		val = INV_ICM42607_ACCEL_CONFIG0_FS_SEL(conf->fs) |
+		INV_ICM42607_ACCEL_CONFIG0_ODR(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 = INV_ICM42607_ACCEL_CONFIG1_AVG(conf->filter);
+			ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1,
+						 INV_ICM42607_ACCEL_CONFIG1_AVG_MASK, val);
+		} else {
+			val = INV_ICM42607_ACCEL_CONFIG1_FILTER(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);
+}
+
#18 lore raw

Re: [PATCH V3 6/9] iio: imu: inv_icm42607: Add Accelerometer for icm42607

Quoted 146 lines · David Lechner
Quoted 4 lines · Chris Morgan

From: Chris Morgan <[email protected]>

Add icm42607 accelerometer sensor for icm42607.

...

drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c line 215

Quoted 3 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +3 -0
+static const unsigned long inv_icm42607_accel_scan_masks[] = {
+	/* 3-axis accel + temperature */
+	INV_ICM42607_SCAN_MASK_ACCEL_3AXIS | INV_ICM42607_SCAN_MASK_TEMP,

This is going to make it so that the temperature channel is always read even if it isn't enabled and additional work is needed when pushing to buffers to remove it again.

It looks like it is possible to read accel and temp separatly, so there shuold be two more lines here,

	INV_ICM42607_SCAN_MASK_ACCEL_3AXIS,
	INV_ICM42607_SCAN_MASK_TEMP,

I forget what the correct order is though.

Quoted 32 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +32 -0
+	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;
+	unsigned int sleep;
+	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)

drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c line 257

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +10 -0
+			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);
+	/* sleep maximum required time */

Would be better if the comment explain _why_ we need to sleep.

The code is pretty obvious that it does what the comment says, so it doesn't add much.

drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c line 259

Quoted 2 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +2 -0
+	sleep = max(sleep_accel, sleep_temp);
+	if (sleep)

Probably don't need the if here as msleep() should handle 0 without actually sleeping.

Quoted 43 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +43 -0
+		msleep(sleep);
+	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);

drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c line 312

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +10 -0
+	if (ret)
+		return ret;
+
+	/* read accel register data */
+	data = (__be16 *)&st->buffer[0];
+	ret = regmap_bulk_read(st->map, reg, data, sizeof(*data));
+	if (ret)
+		return ret;
+
+	*val = (int16_t)be16_to_cpup(data);

We don't use int16_t in the kernel (ideally). Stick with s16.

Although cast isn't needed here since val is already s16.

drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c line 319

Quoted 7 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +7 -0
+	if (*val == INV_ICM42607_DATA_INVALID)
+		ret = -EINVAL;
+
+	return ret;
+}
+
+/* IIO format int + nano */

Usually we make these 2-D arrays for readability and then cast to int * if needed.

Quoted 15 lines
Quoted 5 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +5 -0
+static const int inv_icm42607_accel_scale[] = {
+	/* +/- 16G => 0.004788403 m/s-2 */
+	[2 * INV_ICM42607_ACCEL_FS_16G] = 0,
+	[2 * INV_ICM42607_ACCEL_FS_16G + 1] = 4788403,
+	/* +/- 8G => 0.002394202 m/s-2 */

drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c line 334

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +10 -0
+	[2 * INV_ICM42607_ACCEL_FS_8G] = 0,
+	[2 * INV_ICM42607_ACCEL_FS_8G + 1] = 2394202,
+	/* +/- 4G => 0.001197101 m/s-2 */
+	[2 * INV_ICM42607_ACCEL_FS_4G] = 0,
+	[2 * INV_ICM42607_ACCEL_FS_4G + 1] = 1197101,
+	/* +/- 2G => 0.000598550 m/s-2 */
+	[2 * INV_ICM42607_ACCEL_FS_2G] = 0,
+	[2 * INV_ICM42607_ACCEL_FS_2G + 1] = 598550,
+};
+

I've gone through and implemented all of the changes everyone suggested, though this is one of the few on which I had a question. Obviously this driver was cobbled together from 2 different sources and checked to the best of my ability and tested/validated against the data sheet, but there are a few bits I'm not fully clear on such as this.

What's the correct way to represent this data? Since it looks like one of the values is always 0, should I just assume it's always 0 and only represent the values that change in this scale?

Quoted 58 lines

...

drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c line 501

Quoted 7 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +7 -0
+static int inv_icm42607_accel_read_calibbias(struct inv_icm42607_state *st,
+					     struct iio_chan_spec const *chan,
+					     int *val, int *val2)
+{
+	/* Not actually supported in the ICM-42607P registers */
+	return -EOPNOTSUPP;
+}

Can we just not create the attribute instead of returning an error?

Quoted 4 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +4 -0
+static int inv_icm42607_accel_write_raw_get_fmt(struct iio_dev *indio_dev,
+						struct iio_chan_spec const *chan,
+						long mask)
+{

drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c line 584

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +10 -0
+	if (chan->type != IIO_ACCEL)
+		return -EINVAL;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		return IIO_VAL_INT_PLUS_NANO;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		return IIO_VAL_INT_PLUS_MICRO;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		return IIO_VAL_INT_PLUS_MICRO;

Can write this as:

	case IIO_CHAN_INFO_SAMP_FREQ:
	case IIO_CHAN_INFO_CALIBBIAS:
		return IIO_VAL_INT_PLUS_MICRO;
Quoted 5 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +5 -0
+	default:
+		return -EINVAL;
+	}
+}
+

...

Quoted 15 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +15 -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)
Quoted 8 lines

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st, · line 193

Quoted 3 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +3 -0
+		conf->filter = oldconf->filter;
+
+	if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) {

We could use the regmap cache feature to avoid having to manual keep track of old values. Or just always write the same values anyway. I find that is nice when debugging hardware with a logic analyzer. Unless there is some measureable performance improvlment here?

This is another one I had a question on. I'm not entirely clear from the datasheet which reg values are volatile and which ones are safe to cache. Performance wise the 42607 series appears to be the *least* performant in their lineup, so I don't imagine we care much either way. Should I just not worry about the old values and always write? Do you think that would work?

Quoted 21 lines
Quoted 21 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +20 -0
+		val = INV_ICM42607_ACCEL_CONFIG0_FS_SEL(conf->fs) |
+		INV_ICM42607_ACCEL_CONFIG0_ODR(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 = INV_ICM42607_ACCEL_CONFIG1_AVG(conf->filter);
+			ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1,
+						 INV_ICM42607_ACCEL_CONFIG1_AVG_MASK, val);
+		} else {
+			val = INV_ICM42607_ACCEL_CONFIG1_FILTER(conf->filter);
+			ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1,
+						 INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, val);
+		}
+		if (ret)
Quoted 8 lines

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st, · line 221

Quoted 8 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +8 -0
+			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);
+}
+

Thank you otherwise for all your valuable feedback. I've implemented it to the best of my ability and plan on resubmitting this series soon. Thank you.

Chris

reviewer
#19 lore raw

Re: [PATCH V3 6/9] iio: imu: inv_icm42607: Add Accelerometer for icm42607

Quoted 7 lines · Chris Morgan
Quoted 6 lines · David Lechner
Quoted 4 lines · Chris Morgan

From: Chris Morgan <[email protected]>

Add icm42607 accelerometer sensor for icm42607.

...

Quoted 25 lines
Quoted 18 lines

Usually we make these 2-D arrays for readability and then cast to int * if needed.

Quoted 5 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +5 -0
+static const int inv_icm42607_accel_scale[] = {
+	/* +/- 16G => 0.004788403 m/s-2 */
+	[2 * INV_ICM42607_ACCEL_FS_16G] = 0,
+	[2 * INV_ICM42607_ACCEL_FS_16G + 1] = 4788403,
+	/* +/- 8G => 0.002394202 m/s-2 */

drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c line 334

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +10 -0
+	[2 * INV_ICM42607_ACCEL_FS_8G] = 0,
+	[2 * INV_ICM42607_ACCEL_FS_8G + 1] = 2394202,
+	/* +/- 4G => 0.001197101 m/s-2 */
+	[2 * INV_ICM42607_ACCEL_FS_4G] = 0,
+	[2 * INV_ICM42607_ACCEL_FS_4G + 1] = 1197101,
+	/* +/- 2G => 0.000598550 m/s-2 */
+	[2 * INV_ICM42607_ACCEL_FS_2G] = 0,
+	[2 * INV_ICM42607_ACCEL_FS_2G + 1] = 598550,
+};
+

I've gone through and implemented all of the changes everyone suggested, though this is one of the few on which I had a question. Obviously this driver was cobbled together from 2 different sources and checked to the best of my ability and tested/validated against the data sheet, but there are a few bits I'm not fully clear on such as this.

Quoted 4 lines

What's the correct way to represent this data? Since it looks like one of the values is always 0, should I just assume it's always 0 and only represent the values that change in this scale?

Picking a random driver, bma220 has the most usual way of doing it.

static const int bma220_scale_table[][2] = {
	{ 0, 623000 }, { 1, 248000 }, { 2, 491000 }, { 4, 983000 },
};

I do like using the enum to make it self documenting though (no comments needed), so for this driver...

static const int inv_icm42607_accel_scale_nano[][2] = {
	[INV_ICM42607_ACCEL_FS_16G] = { 0, 4788403 },
	...
};

Then the read_avail callback in bma220 does:

	case IIO_CHAN_INFO_SCALE:
		*vals = (int *)bma220_scale_table;
		*type = IIO_VAL_INT_PLUS_MICRO;
		*length = ARRAY_SIZE(bma220_scale_table) * 2;
		return IIO_AVAIL_LIST;

Although the cast would be better as (const int *) and I assume you would want to use NANO instead of MICRO.

Quoted 25 lines
Quoted 16 lines
Quoted 16 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +15 -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)
Quoted 8 lines

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st, · line 193

Quoted 3 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +3 -0
+		conf->filter = oldconf->filter;
+
+	if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) {

We could use the regmap cache feature to avoid having to manual keep track of old values. Or just always write the same values anyway. I find that is nice when debugging hardware with a logic analyzer. Unless there is some measureable performance improvlment here?

Quoted 7 lines

This is another one I had a question on. I'm not entirely clear from the datasheet which reg values are volatile and which ones are safe to cache. Performance wise the 42607 series appears to be the *least* performant in their lineup, so I don't imagine we care much either way. Should I just not worry about the old values and always write? Do you think that would work?

My personal preference is to always do extra SPI writes for anything that is not performance critical. This makes debugging with a logic analyzer much easier.

In cases where using caching makes sense, generally, "status" registers are volatile because the chip can change the value when status changes. "config" registers are not volatile because they don't change unless written to.

#20 lore raw

Re: [PATCH V3 6/9] iio: imu: inv_icm42607: Add Accelerometer for icm42607

In reply to Chris Morgan
Quoted 2 lines · Chris Morgan
Quoted 1 line · David Lechner

On 3/30/26 2:58 PM, Chris Morgan wrote:

...

Quoted 25 lines
Quoted 4 lines
Quoted 1 line

+/* IIO format int + nano */

Usually we make these 2-D arrays for readability and then cast to int * if needed.

Quoted 14 lines
Quoted 4 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +4 -0
+static const int inv_icm42607_accel_scale[] = {
+	/* +/- 16G => 0.004788403 m/s-2 */
+	[2 * INV_ICM42607_ACCEL_FS_16G] = 0,
+	[2 * INV_ICM42607_ACCEL_FS_16G + 1] = 4788403,

drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c line 333

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +10 -0
+	/* +/- 8G => 0.002394202 m/s-2 */
+	[2 * INV_ICM42607_ACCEL_FS_8G] = 0,
+	[2 * INV_ICM42607_ACCEL_FS_8G + 1] = 2394202,
+	/* +/- 4G => 0.001197101 m/s-2 */
+	[2 * INV_ICM42607_ACCEL_FS_4G] = 0,
+	[2 * INV_ICM42607_ACCEL_FS_4G + 1] = 1197101,
+	/* +/- 2G => 0.000598550 m/s-2 */
+	[2 * INV_ICM42607_ACCEL_FS_2G] = 0,
+	[2 * INV_ICM42607_ACCEL_FS_2G + 1] = 598550,
+};

I've gone through and implemented all of the changes everyone suggested, though this is one of the few on which I had a question. Obviously this driver was cobbled together from 2 different sources and checked to the best of my ability and tested/validated against the data sheet, but there are a few bits I'm not fully clear on such as this.

Quoted 3 lines

What's the correct way to represent this data? Since it looks like one of the values is always 0, should I just assume it's always 0 and only represent the values that change in this scale?

Isn't the comment is clear about that? The first one is integer, the second one is fraction. So, no, your assumption is wrong strictly speaking.

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

Re: [PATCH V3 6/9] iio: imu: inv_icm42607: Add Accelerometer for icm42607

In reply to Chris Morgan
Quoted 5 lines · On Mon, 30 Mar 2026 14:58:50 -0500 Chris Morgan <[email protected]> wrote:

From: Chris Morgan <[email protected]>

Add icm42607 accelerometer sensor for icm42607.

A few more things below.

thanks

Jonathan

Quoted 12 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +5 -0
new file mode 100644
index 000000000000..58754af31100
 
 
+
+static const struct iio_enum inv_icm42607_accel_power_mode_enum = {
+	.items = inv_icm42607_accel_power_mode_items,
+	.num_items = ARRAY_SIZE(inv_icm42607_accel_power_mode_items),
+	.set = inv_icm42607_accel_power_mode_set,

drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c line 174

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +10 -0
+	.get = inv_icm42607_accel_power_mode_get,
+};
+
+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),
+	IIO_ENUM_AVAILABLE("power_mode", IIO_SHARED_BY_TYPE,
+			   &inv_icm42607_accel_power_mode_enum),
+	IIO_ENUM("power_mode", IIO_SHARED_BY_TYPE,
+		 &inv_icm42607_accel_power_mode_enum),
+	{ },

No comma.

Quoted 151 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +148 -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 = 0; i < ARRAY_SIZE(inv_icm42607_accel_odr_conv); ++i) {
+		if (inv_icm42607_accel_odr_conv[i] == odr)
+			break;
+	}
+	if (i >= ARRAY_SIZE(inv_icm42607_accel_odr_conv))
+		return -EINVAL;
+
+	*val = inv_icm42607_accel_odr[2 * i];
+	*val2 = inv_icm42607_accel_odr[2 * 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 = 0; idx < ARRAY_SIZE(inv_icm42607_accel_odr); idx += 2) {
+		if (val == inv_icm42607_accel_odr[idx] &&
+			val2 == inv_icm42607_accel_odr[idx + 1])
+			break;
+	}
+	if (idx >= ARRAY_SIZE(inv_icm42607_accel_odr))
+		return -EINVAL;
+
+	conf.odr = inv_icm42607_accel_odr_conv[idx / 2];
+
+	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 ret;
+}
+
+/*
+ * Calibration bias values, IIO range format int + micro.
+ * Not actually supported in the ICM-42607P registers.
+ */
+static int inv_icm42607_accel_write_calibbias(struct inv_icm42607_state *st,
+					      struct iio_chan_spec const *chan,
+					      int val, int val2)
+{
+	/* Not actually supported in the ICM-42607P registers */
+	return -EOPNOTSUPP;
+}
+
+static int inv_icm42607_accel_read_calibbias(struct inv_icm42607_state *st,
+					     struct iio_chan_spec const *chan,
+					     int *val, int *val2)
+{
+	/* Not actually supported in the ICM-42607P registers */
+	return -EOPNOTSUPP;
+}
+
+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);
+	case IIO_CHAN_INFO_CALIBBIAS:
+		return inv_icm42607_accel_read_calibbias(st, chan, val, val2);
+	default:
+		return -EINVAL;
+	}
+}
 
+
+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, &timestamp, &odr);
+		/* quit if error or FIFO is empty */
+		if (size <= 0)
+			return size;
+
+		/* skip packet if no accel data or data is invalid */
+		if (accel == NULL || !inv_icm42607_fifo_is_data_valid(accel))
+			continue;
+
+		/* update odr */

drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +10 -0
+		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_timestamp(indio_dev, &buffer, ts_val);

For new code, always use iio_push_to_buffers_with_ts() This one is going away in near future.

Quoted 20 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c new file +4 -0
+	}
+
+	return 0;
+}
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +10 -0
index 735a262dc103..62a1371b0c4a 100644
 
+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;

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st, · line 195

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +10 -0
+	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 = INV_ICM42607_ACCEL_CONFIG0_FS_SEL(conf->fs) |
+		INV_ICM42607_ACCEL_CONFIG0_ODR(conf->odr);

Another indent case that needs fixing.

Quoted 25 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +25 -0
+		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 = INV_ICM42607_ACCEL_CONFIG1_AVG(conf->filter);
+			ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1,
+						 INV_ICM42607_ACCEL_CONFIG1_AVG_MASK, val);
+		} else {
+			val = INV_ICM42607_ACCEL_CONFIG1_FILTER(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);
+}
#22 lore raw

PATCH v3 7/9 iio: imu: inv_icm42607: Add Interrupt and Wake on Movement for icm42607

In reply to Chris Morgan

From: Chris Morgan <[email protected]>

Add support for wake on movement for the icm42607 driver.

At this point the driver is usable as an accelerometer/temperature driver, so add the necessary Makefile and Kconfig changes as well.

---
 drivers/iio/imu/Kconfig                       |   1 +
 drivers/iio/imu/Makefile                      |   1 +
 drivers/iio/imu/inv_icm42607/Kconfig          |  30 ++
 drivers/iio/imu/inv_icm42607/Makefile         |  13 +
 drivers/iio/imu/inv_icm42607/inv_icm42607.h   |   7 +
 .../iio/imu/inv_icm42607/inv_icm42607_accel.c | 303 ++++++++++++++++++
 .../iio/imu/inv_icm42607/inv_icm42607_core.c  | 155 +++++++++
 7 files changed, 510 insertions(+)
 create mode 100644 drivers/iio/imu/inv_icm42607/Kconfig
 create mode 100644 drivers/iio/imu/inv_icm42607/Makefile
drivers/iio/imu/Kconfig +1 -0
index 7e0181c27bb6..8bab4616be20 100644
@@ -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"
 
drivers/iio/imu/Makefile +1 -0
index 13fb7846e9c9..3268dc2371ae 100644
@@ -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/
 
drivers/iio/imu/inv_icm42607/Kconfig new file +30 -0
new file mode 100644
index 000000000000..b7c533d1a6be
@@ -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.
drivers/iio/imu/inv_icm42607/Makefile new file +13 -0
new file mode 100644
index 000000000000..e908d77c4219
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+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
+
+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
drivers/iio/imu/inv_icm42607/inv_icm42607.h +7 -0
index 56bb09e2c304..f5f1b5fea183 100644
@@ -444,6 +444,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_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
 			     unsigned int writeval, unsigned int *readval);
 
@@ -454,4 +457,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
drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c +303 -0

339 lines not on this page · raw message

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +155 -0

185 lines not on this page · raw message

Signature · 1 line
2.43.0
maintainer
#23 lore raw

Re: [PATCH V3 7/9] iio: imu: inv_icm42607: Add Interrupt and Wake on Movement for icm42607

Quoted 8 lines · On Mon, 30 Mar 2026 14:58:51 -0500 Chris Morgan <[email protected]> wrote:

From: Chris Morgan <[email protected]>

Add support for wake on movement for the icm42607 driver.

At this point the driver is usable as an accelerometer/temperature driver, so add the necessary Makefile and Kconfig changes as well.

A few comments on this one.

Quoted 4 lines
drivers/iio/imu/inv_icm42607/inv_icm42607.h +0 -0
index 56bb09e2c304..f5f1b5fea183 100644

...

Quoted 11 lines

struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st)

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +4 -0
index 62a1371b0c4a..4ac3af52c1b8 100644
 
+
+int inv_icm42607_disable_wom(struct inv_icm42607_state *st)
+{
+	int ret;

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c int inv_icm42607_set_temp_conf(struct inv_icm42607_state *st, bool enable, · line 268

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +10 -0
+
+	/* 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);

regmap_set_bits()

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c

Quoted 3 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +3 -0
+}
+
+

As below.

Quoted 42 lines

int inv_icm42607_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +40 -0
+
+/**
+ * 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);

drivers/iio/imu/inv_icm42607/inv_icm42607_core.c static int inv_icm42607_setup(struct inv_icm42607_state *st, · line 492

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +10 -0
+	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->name, st);
+}
+
+

Trivial but single line is always enough (in IIO anyway)

Quoted 1 line

static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st)

#24 lore raw

PATCH v3 8/9 iio: imu: inv_icm42607: Add Gyroscope to icm42607

In reply to Chris Morgan

From: Chris Morgan <[email protected]>

Add gyroscope functions to the icm42607 driver.

---
 drivers/iio/imu/inv_icm42607/Makefile         |   1 +
 drivers/iio/imu/inv_icm42607/inv_icm42607.h   |   8 +
 .../imu/inv_icm42607/inv_icm42607_buffer.c    |  25 +-
 .../iio/imu/inv_icm42607/inv_icm42607_core.c  |  45 ++
 .../iio/imu/inv_icm42607/inv_icm42607_gyro.c  | 579 ++++++++++++++++++
 5 files changed, 657 insertions(+), 1 deletion(-)
 create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
drivers/iio/imu/inv_icm42607/Makefile +1 -0
index e908d77c4219..fc66e580fe99 100644
@@ -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
drivers/iio/imu/inv_icm42607/inv_icm42607.h +8 -0
index f5f1b5fea183..76388ffb88e3 100644
@@ -441,6 +441,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);
 
@@ -453,6 +457,10 @@ int inv_icm42607_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
 int inv_icm42607_core_probe(struct regmap *regmap, int chip,
 			    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);
drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c +24 -1
index b086068e56ce..92cf7ffe5077 100644
@@ -384,7 +384,10 @@ static int inv_icm42607_buffer_postdisable(struct iio_dev *indio_dev)
 		goto out_unlock;
 
 	conf.mode = INV_ICM42607_SENSOR_MODE_OFF;
-	ret = inv_icm42607_set_accel_conf(st, &conf, &sleep_sensor);
+	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;
 
@@ -473,6 +476,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;
@@ -480,6 +484,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;
@@ -496,6 +510,7 @@ 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 gyro_ts, accel_ts;
@@ -511,6 +526,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);
drivers/iio/imu/inv_icm42607/inv_icm42607_core.c +45 -0

57 lines not on this page · raw message

drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c new file +579 -0

579 lines not on this page · raw message

Signature · 1 line
2.43.0
maintainer
#25 lore raw

Re: [PATCH V3 8/9] iio: imu: inv_icm42607: Add Gyroscope to icm42607

Quoted 5 lines · On Mon, 30 Mar 2026 14:58:52 -0500 Chris Morgan <[email protected]> wrote:

From: Chris Morgan <[email protected]>

Add gyroscope functions to the icm42607 driver.

One very minor comment.

drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c line 55

Quoted 10 lines
drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c new file +4 -0
new file mode 100644
index 000000000000..80473c07c6a4
 
+
+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),
+	{},
	{ }
Quoted 1 line

+};

#26 lore raw

PATCH v3 9/9 arm64: dts: rockchip: Add icm42607p IMU for RG-DS

In reply to Chris Morgan

From: Chris Morgan <[email protected]>

Add the Invensense ICM42607P IMU for the Anbernic RG-DS. Mount-matrix was tested with iio-sensor-proxy and reports correct orientation.

---
 .../dts/rockchip/rk3568-anbernic-rg-ds.dts    | 20 ++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)
arch/arm64/boot/dts/rockchip/rk3568-anbernic-rg-ds.dts +19 -1
index 44de35bcd001..834d2c940810 100644
@@ -872,7 +872,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 {
@@ -933,6 +944,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 =
Signature · 1 line
2.43.0
#27 lore raw
In reply to Chris Morgan
Quoted 17 lines · Chris Morgan

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.

Quoted 2 lines

Changes Since V2:
- Went back to using a new driver on advice from Invensense engineer.

Okay, but this should be elaborated in the cover letter. If I followed previous discussion correctly, the problem is the indirect subset of registers that are absent on the 42600 series. But would be nice to have the summary of what vendor engineers told you.

Quoted 9 lines

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

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#28 lore raw
Quoted 19 lines · Andy Shevchenko
Quoted 17 lines · Chris Morgan

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.

Quoted 7 lines
Quoted 2 lines

Changes Since V2:
- Went back to using a new driver on advice from Invensense engineer.

Okay, but this should be elaborated in the cover letter. If I followed previous discussion correctly, the problem is the indirect subset of registers that are absent on the 42600 series. But would be nice to have the summary of what vendor engineers told you.

Sorry, I should have elaborated you are correct. The information the engineer said was that the 42607 chips use an indirect register access method using IREG specific registers. As a result it did not make sense to have the 42600 and 42607 drivers combined.

That said, the driver in question here does not use any of those specific IREG registers (but it's possible other future 42607 devices could). Specifically, I only have the 42607P (via I2C) to test with, which does not have those registers only ones that can be directly accessed.

I hope this clears that up, I'll try to include that information in the next patch. I also want to make sure with this revision that I'm "in the ballpark" for breaking this down into digestable chunks.

Thank you,
Chris

Quoted 16 lines
Quoted 10 lines

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

Signature · 2 lines
With Best Regards,
Andy Shevchenko