* [PATCH V9 01/11] dt-bindings: iio: imu: icm42600: Add mount-matrix to icm42600
2026-05-30 3:17 [PATCH V9 00/11] Add Invensense ICM42607 Chris Morgan
@ 2026-05-30 3:17 ` Chris Morgan
2026-05-30 3:17 ` [PATCH V9 02/11] dt-bindings: iio: imu: icm42600: Add icm42607 binding Chris Morgan
` (9 subsequent siblings)
10 siblings, 0 replies; 26+ messages in thread
From: Chris Morgan @ 2026-05-30 3:17 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan, Krzysztof Kozlowski
From: Chris Morgan <macromorgan@hotmail.com>
Add mount-matrix attribute to schema. This attribute has been supported
since the first revision of this driver, but was not documented.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
.../devicetree/bindings/iio/imu/invensense,icm42600.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
index 119e28a833fd..9b2af104f186 100644
--- a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
+++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
@@ -53,6 +53,8 @@ properties:
drive-open-drain:
type: boolean
+ mount-matrix: true
+
vdd-supply:
description: Regulator that provides power to the sensor
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH V9 02/11] dt-bindings: iio: imu: icm42600: Add icm42607 binding
2026-05-30 3:17 [PATCH V9 00/11] Add Invensense ICM42607 Chris Morgan
2026-05-30 3:17 ` [PATCH V9 01/11] dt-bindings: iio: imu: icm42600: Add mount-matrix to icm42600 Chris Morgan
@ 2026-05-30 3:17 ` Chris Morgan
2026-05-30 7:26 ` Krzysztof Kozlowski
2026-05-30 3:17 ` [PATCH V9 03/11] iio: imu: inv_icm42607: Add inv_icm42607 Core Driver Chris Morgan
` (8 subsequent siblings)
10 siblings, 1 reply; 26+ messages in thread
From: Chris Morgan @ 2026-05-30 3:17 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add 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, however unlike the icm42600 driver we
require a interrupt-names property be present.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
.../bindings/iio/imu/invensense,icm42600.yaml | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
index 9b2af104f186..475a498207f9 100644
--- a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
+++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
@@ -30,6 +30,8 @@ properties:
- invensense,icm42600
- invensense,icm42602
- invensense,icm42605
+ - invensense,icm42607
+ - invensense,icm42607p
- invensense,icm42622
- invensense,icm42631
- invensense,icm42686
@@ -71,6 +73,16 @@ required:
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - invensense,icm42607
+ - invensense,icm42607p
+ then:
+ required:
+ - interrupt-names
unevaluatedProperties: false
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH V9 02/11] dt-bindings: iio: imu: icm42600: Add icm42607 binding
2026-05-30 3:17 ` [PATCH V9 02/11] dt-bindings: iio: imu: icm42600: Add icm42607 binding Chris Morgan
@ 2026-05-30 7:26 ` Krzysztof Kozlowski
2026-06-01 1:44 ` Chris Morgan
0 siblings, 1 reply; 26+ messages in thread
From: Krzysztof Kozlowski @ 2026-05-30 7:26 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-iio, andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
On Fri, May 29, 2026 at 10:17:29PM -0500, Chris Morgan wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add devicetree binding for the Invensense ICM42607 and Invensense
> ICM42607P inertial measurement unit. This unit is a combined
> accelerometer, gyroscope, and thermometer available via I2C or SPI.
Implement feedback from v3. Subject is still incorrect.
>
> This device is functionally very similar to the icm42600 series with a
> very different register layout, however unlike the icm42600 driver we
> require a interrupt-names property be present.
Why do we require it? Who needs it?
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V9 02/11] dt-bindings: iio: imu: icm42600: Add icm42607 binding
2026-05-30 7:26 ` Krzysztof Kozlowski
@ 2026-06-01 1:44 ` Chris Morgan
2026-06-01 8:42 ` Krzysztof Kozlowski
2026-06-01 9:15 ` Jonathan Cameron
0 siblings, 2 replies; 26+ messages in thread
From: Chris Morgan @ 2026-06-01 1:44 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Chris Morgan, linux-iio, andy, nuno.sa, dlechner, jic23,
jean-baptiste.maneyrol, linux-rockchip, devicetree, heiko,
conor+dt, krzk+dt, robh, andriy.shevchenko
On Sat, May 30, 2026 at 09:26:31AM +0200, Krzysztof Kozlowski wrote:
> On Fri, May 29, 2026 at 10:17:29PM -0500, Chris Morgan wrote:
> > From: Chris Morgan <macromorgan@hotmail.com>
> >
> > Add devicetree binding for the Invensense ICM42607 and Invensense
> > ICM42607P inertial measurement unit. This unit is a combined
> > accelerometer, gyroscope, and thermometer available via I2C or SPI.
>
> Implement feedback from v3. Subject is still incorrect.
>
I'm sorry, that was dropped due to an oversight. I'll correct it.
> >
> > This device is functionally very similar to the icm42600 series with a
> > very different register layout, however unlike the icm42600 driver we
> > require a interrupt-names property be present.
>
> Why do we require it? Who needs it?
>
The driver this was based off of, the icm42600 driver, has a fallback
to select the 1st available interrupt in the event of the named
interrupt not existing. I was told not to do that here so the named
interrupt is a requirement for the icm42607.
> Best regards,
> Krzysztof
>
Thank you,
Chris
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V9 02/11] dt-bindings: iio: imu: icm42600: Add icm42607 binding
2026-06-01 1:44 ` Chris Morgan
@ 2026-06-01 8:42 ` Krzysztof Kozlowski
2026-06-01 9:15 ` Jonathan Cameron
1 sibling, 0 replies; 26+ messages in thread
From: Krzysztof Kozlowski @ 2026-06-01 8:42 UTC (permalink / raw)
To: Chris Morgan
Cc: Chris Morgan, linux-iio, andy, nuno.sa, dlechner, jic23,
jean-baptiste.maneyrol, linux-rockchip, devicetree, heiko,
conor+dt, krzk+dt, robh, andriy.shevchenko
On 01/06/2026 03:44, Chris Morgan wrote:
> On Sat, May 30, 2026 at 09:26:31AM +0200, Krzysztof Kozlowski wrote:
>> On Fri, May 29, 2026 at 10:17:29PM -0500, Chris Morgan wrote:
>>> From: Chris Morgan <macromorgan@hotmail.com>
>>>
>>> Add devicetree binding for the Invensense ICM42607 and Invensense
>>> ICM42607P inertial measurement unit. This unit is a combined
>>> accelerometer, gyroscope, and thermometer available via I2C or SPI.
>>
>> Implement feedback from v3. Subject is still incorrect.
>>
>
> I'm sorry, that was dropped due to an oversight. I'll correct it.
>
>>>
>>> This device is functionally very similar to the icm42600 series with a
>>> very different register layout, however unlike the icm42600 driver we
>>> require a interrupt-names property be present.
>>
>> Why do we require it? Who needs it?
>>
>
> The driver this was based off of, the icm42600 driver, has a fallback
> to select the 1st available interrupt in the event of the named
So some other driver? That's not relevant here if you do not use that.
> interrupt not existing. I was told not to do that here so the named
> interrupt is a requirement for the icm42607.
So icm42607 driver needs it? Then write it explicitly.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH V9 02/11] dt-bindings: iio: imu: icm42600: Add icm42607 binding
2026-06-01 1:44 ` Chris Morgan
2026-06-01 8:42 ` Krzysztof Kozlowski
@ 2026-06-01 9:15 ` Jonathan Cameron
1 sibling, 0 replies; 26+ messages in thread
From: Jonathan Cameron @ 2026-06-01 9:15 UTC (permalink / raw)
To: Chris Morgan
Cc: Krzysztof Kozlowski, Chris Morgan, linux-iio, andy, nuno.sa,
dlechner, jean-baptiste.maneyrol, linux-rockchip, devicetree,
heiko, conor+dt, krzk+dt, robh, andriy.shevchenko
On Sun, 31 May 2026 20:44:03 -0500
Chris Morgan <macromorgan@hotmail.com> wrote:
> On Sat, May 30, 2026 at 09:26:31AM +0200, Krzysztof Kozlowski wrote:
> > On Fri, May 29, 2026 at 10:17:29PM -0500, Chris Morgan wrote:
> > > From: Chris Morgan <macromorgan@hotmail.com>
> > >
> > > Add devicetree binding for the Invensense ICM42607 and Invensense
> > > ICM42607P inertial measurement unit. This unit is a combined
> > > accelerometer, gyroscope, and thermometer available via I2C or SPI.
> >
> > Implement feedback from v3. Subject is still incorrect.
> >
>
> I'm sorry, that was dropped due to an oversight. I'll correct it.
>
> > >
> > > This device is functionally very similar to the icm42600 series with a
> > > very different register layout, however unlike the icm42600 driver we
> > > require a interrupt-names property be present.
> >
> > Why do we require it? Who needs it?
> >
>
> The driver this was based off of, the icm42600 driver, has a fallback
> to select the 1st available interrupt in the event of the named
> interrupt not existing. I was told not to do that here so the named
> interrupt is a requirement for the icm42607.
A bit more background on this. I think it went as follows.
When that other driver originally went in we didn't pick up on their
being multiple interrupt lines. Hence once that came up because
someone had a board that only connected the other pin we had to
'guess' which one was the default (I'm sure we asked people :)
I'll noted that we had at least one case in the past (can't remember
if it was this part) where the assumed default was not the first one :(
We've even had package variants (which we don't normally use different
compatibles for) where only INT2 actually had a pin.
Hence, since then we've been more careful with these devices.
Given there is nothing special with one interrupt rather than the other
with a mux on each that lets us route all interrupts signals to each
pin, my strong preference is we don't make assumptions and always require
the firmeware to say which one it is.
Jonathan
>
> > Best regards,
> > Krzysztof
> >
>
> Thank you,
> Chris
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH V9 03/11] iio: imu: inv_icm42607: Add inv_icm42607 Core Driver
2026-05-30 3:17 [PATCH V9 00/11] Add Invensense ICM42607 Chris Morgan
2026-05-30 3:17 ` [PATCH V9 01/11] dt-bindings: iio: imu: icm42600: Add mount-matrix to icm42600 Chris Morgan
2026-05-30 3:17 ` [PATCH V9 02/11] dt-bindings: iio: imu: icm42600: Add icm42607 binding Chris Morgan
@ 2026-05-30 3:17 ` Chris Morgan
2026-05-31 12:11 ` Jonathan Cameron
2026-05-30 3:17 ` [PATCH V9 04/11] iio: imu: inv_icm42607: Add I2C and SPI For icm42607 Chris Morgan
` (7 subsequent siblings)
10 siblings, 1 reply; 26+ messages in thread
From: Chris Morgan @ 2026-05-30 3:17 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add the core component of a new inv_icm42607 driver. This includes
a few setup functions and the full register definition in the
header file.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 336 ++++++++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 206 +++++++++++
2 files changed, 542 insertions(+)
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607.h
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
new file mode 100644
index 000000000000..fe565c423c98
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -0,0 +1,336 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#ifndef INV_ICM42607_H_
+#define INV_ICM42607_H_
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/iio/iio.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+enum inv_icm42607_sensor_mode {
+ INV_ICM42607_SENSOR_MODE_OFF,
+ INV_ICM42607_SENSOR_MODE_STANDBY,
+ INV_ICM42607_SENSOR_MODE_LOW_POWER,
+ INV_ICM42607_SENSOR_MODE_LOW_NOISE,
+ INV_ICM42607_SENSOR_MODE_NB
+};
+
+/* gyroscope fullscale values */
+enum inv_icm42607_gyro_fs {
+ INV_ICM42607_GYRO_FS_2000DPS,
+ INV_ICM42607_GYRO_FS_1000DPS,
+ INV_ICM42607_GYRO_FS_500DPS,
+ INV_ICM42607_GYRO_FS_250DPS,
+ INV_ICM42607_GYRO_FS_NB
+};
+
+/* accelerometer fullscale values */
+enum inv_icm42607_accel_fs {
+ INV_ICM42607_ACCEL_FS_16G,
+ INV_ICM42607_ACCEL_FS_8G,
+ INV_ICM42607_ACCEL_FS_4G,
+ INV_ICM42607_ACCEL_FS_2G,
+ INV_ICM42607_ACCEL_FS_NB
+};
+
+/* ODR values */
+enum inv_icm42607_odr {
+ INV_ICM42607_ODR_1600HZ = 5,
+ INV_ICM42607_ODR_800HZ,
+ INV_ICM42607_ODR_400HZ,
+ INV_ICM42607_ODR_200HZ,
+ INV_ICM42607_ODR_100HZ,
+ INV_ICM42607_ODR_50HZ,
+ INV_ICM42607_ODR_25HZ,
+ INV_ICM42607_ODR_12_5HZ,
+ INV_ICM42607_ODR_6_25HZ_LP,
+ INV_ICM42607_ODR_3_125HZ_LP,
+ INV_ICM42607_ODR_1_5625HZ_LP,
+ INV_ICM42607_ODR_NB
+};
+
+enum inv_icm42607_filter_bw {
+ /* Low-Noise mode sensor data filter (bandwidth) */
+ INV_ICM42607_FILTER_BYPASS,
+ INV_ICM42607_FILTER_BW_180HZ,
+ INV_ICM42607_FILTER_BW_121HZ,
+ INV_ICM42607_FILTER_BW_73HZ,
+ INV_ICM42607_FILTER_BW_53HZ,
+ INV_ICM42607_FILTER_BW_34HZ,
+ INV_ICM42607_FILTER_BW_25HZ,
+ INV_ICM42607_FILTER_BW_16HZ,
+ INV_ICM42607_FILTER_BW_NB
+};
+
+struct inv_icm42607_sensor_conf {
+ int mode;
+ int fs;
+ int odr;
+ int filter;
+};
+
+struct inv_icm42607_conf {
+ struct inv_icm42607_sensor_conf gyro;
+ struct inv_icm42607_sensor_conf accel;
+ bool temp_en;
+};
+
+struct inv_icm42607_hw {
+ uint8_t whoami;
+ const char *name;
+ const struct inv_icm42607_conf *conf;
+};
+
+/**
+ * struct inv_icm42607_state - driver state variables
+ * @lock: lock for serializing multiple registers access.
+ * @hw: Hardware specific data.
+ * @map: regmap pointer.
+ * @vddio_supply: I/O voltage regulator for the chip.
+ * @irq: chip irq, required to enable/disable and set wakeup
+ * @orientation: sensor chip orientation relative to main hardware.
+ * @conf: chip sensors configurations.
+ */
+struct inv_icm42607_state {
+ struct mutex lock;
+ const struct inv_icm42607_hw *hw;
+ struct regmap *map;
+ struct regulator *vddio_supply;
+ int irq;
+ struct iio_mount_matrix orientation;
+ struct inv_icm42607_conf conf;
+};
+
+/* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */
+
+/* Register Map for User Bank 0 */
+#define INV_ICM42607_REG_MCLK_RDY 0x00
+
+#define INV_ICM42607_REG_DEVICE_CONFIG 0x01
+#define INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE BIT(2)
+#define INV_ICM42607_DEVICE_CONFIG_SPI_MODE BIT(0)
+
+#define INV_ICM42607_REG_SIGNAL_PATH_RESET 0x02
+#define INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET BIT(4)
+#define INV_ICM42607_SIGNAL_PATH_RESET_FIFO_FLUSH BIT(2)
+
+#define INV_ICM42607_REG_DRIVE_CONFIG1 0x03
+#define INV_ICM42607_DRIVE_CONFIG1_I3C_DDR_MASK GENMASK(5, 3)
+#define INV_ICM42607_DRIVE_CONFIG1_I3C_SDR_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_DRIVE_CONFIG2 0x04
+#define INV_ICM42607_DRIVE_CONFIG2_I2C_MASK GENMASK(5, 3)
+#define INV_ICM42607_DRIVE_CONFIG2_ALL_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_DRIVE_CONFIG3 0x05
+#define INV_ICM42607_DRIVE_CONFIG3_SPI_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_INT_CONFIG 0x06
+#define INV_ICM42607_INT_CONFIG_INT2_LATCHED BIT(5)
+#define INV_ICM42607_INT_CONFIG_INT2_PUSH_PULL BIT(4)
+#define INV_ICM42607_INT_CONFIG_INT2_ACTIVE_HIGH BIT(3)
+#define INV_ICM42607_INT_CONFIG_INT2_ACTIVE_LOW 0x00
+#define INV_ICM42607_INT_CONFIG_INT1_LATCHED BIT(2)
+#define INV_ICM42607_INT_CONFIG_INT1_PUSH_PULL BIT(1)
+#define INV_ICM42607_INT_CONFIG_INT1_ACTIVE_HIGH BIT(0)
+#define INV_ICM42607_INT_CONFIG_INT1_ACTIVE_LOW 0x00
+
+/* all sensor data are 16 bits (2 registers wide) in big-endian */
+#define INV_ICM42607_REG_TEMP_DATA1 0x09
+#define INV_ICM42607_REG_TEMP_DATA0 0x0A
+#define INV_ICM42607_REG_ACCEL_DATA_X1 0x0B
+#define INV_ICM42607_REG_ACCEL_DATA_X0 0x0C
+#define INV_ICM42607_REG_ACCEL_DATA_Y1 0x0D
+#define INV_ICM42607_REG_ACCEL_DATA_Y0 0x0E
+#define INV_ICM42607_REG_ACCEL_DATA_Z1 0x0F
+#define INV_ICM42607_REG_ACCEL_DATA_Z0 0x10
+#define INV_ICM42607_REG_GYRO_DATA_X1 0x11
+#define INV_ICM42607_REG_GYRO_DATA_X0 0x12
+#define INV_ICM42607_REG_GYRO_DATA_Y1 0x13
+#define INV_ICM42607_REG_GYRO_DATA_Y0 0x14
+#define INV_ICM42607_REG_GYRO_DATA_Z1 0x15
+#define INV_ICM42607_REG_GYRO_DATA_Z0 0x16
+#define INV_ICM42607_DATA_INVALID -32768
+
+#define INV_ICM42607_REG_TMST_FSYNCH 0x17
+#define INV_ICM42607_REG_TMST_FSYNCL 0x18
+
+/* APEX Data Registers */
+#define INV_ICM42607_REG_APEX_DATA0 0x31
+#define INV_ICM42607_REG_APEX_DATA1 0x32
+#define INV_ICM42607_REG_APEX_DATA2 0x33
+#define INV_ICM42607_REG_APEX_DATA3 0x34
+#define INV_ICM42607_REG_APEX_DATA4 0x1D
+#define INV_ICM42607_REG_APEX_DATA5 0x1E
+
+#define INV_ICM42607_REG_PWR_MGMT0 0x1F
+#define INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL BIT(7)
+#define INV_ICM42607_PWR_MGMT0_IDLE BIT(4)
+#define INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK GENMASK(3, 2)
+#define INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK GENMASK(1, 0)
+
+#define INV_ICM42607_REG_GYRO_CONFIG0 0x20
+#define INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK GENMASK(6, 5)
+#define INV_ICM42607_GYRO_CONFIG0_ODR_MASK GENMASK(3, 0)
+
+#define INV_ICM42607_REG_ACCEL_CONFIG0 0x21
+#define INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK GENMASK(6, 5)
+#define INV_ICM42607_ACCEL_CONFIG0_ODR_MASK GENMASK(3, 0)
+
+#define INV_ICM42607_REG_TEMP_CONFIG0 0x22
+#define INV_ICM42607_TEMP_CONFIG0_FILTER_MASK GENMASK(6, 4)
+
+#define INV_ICM42607_REG_GYRO_CONFIG1 0x23
+#define INV_ICM42607_GYRO_CONFIG1_FILTER_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_ACCEL_CONFIG1 0x24
+#define INV_ICM42607_ACCEL_CONFIG1_AVG_MASK GENMASK(6, 4)
+#define INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_APEX_CONFIG0 0x25
+#define INV_ICM42607_APEX_CONFIG0_DMP_POWER_SAVE_EN BIT(3)
+#define INV_ICM42607_APEX_CONFIG0_DMP_INIT_EN BIT(2)
+#define INV_ICM42607_APEX_CONFIG0_DMP_MEM_RESET_EN BIT(0)
+
+#define INV_ICM42607_REG_APEX_CONFIG1 0x26
+#define INV_ICM42607_APEX_CONFIG1_SMD_ENABLE BIT(6)
+#define INV_ICM42607_APEX_CONFIG1_FF_ENABLE BIT(5)
+#define INV_ICM42607_APEX_CONFIG1_TILT_ENABLE BIT(4)
+#define INV_ICM42607_APEX_CONFIG1_PED_ENABLE BIT(3)
+#define INV_ICM42607_APEX_CONFIG1_DMP_ODR_MASK GENMASK(1, 0)
+
+#define INV_ICM42607_REG_WOM_CONFIG 0x27
+#define INV_ICM42607_WOM_CONFIG_INT_DUR_MASK GENMASK(4, 3)
+#define INV_ICM42607_WOM_CONFIG_INT_MODE BIT(2)
+#define INV_ICM42607_WOM_CONFIG_MODE BIT(1)
+#define INV_ICM42607_WOM_CONFIG_EN BIT(0)
+
+#define INV_ICM42607_REG_FIFO_CONFIG1 0x28
+#define INV_ICM42607_FIFO_CONFIG1_MODE BIT(1)
+#define INV_ICM42607_FIFO_CONFIG1_BYPASS BIT(0)
+
+#define INV_ICM42607_REG_FIFO_CONFIG2 0x29
+#define INV_ICM42607_REG_FIFO_CONFIG3 0x2A
+#define INV_ICM42607_FIFO_WATERMARK_VAL(_wm) \
+ cpu_to_le16((_wm) & GENMASK(11, 0))
+/* FIFO is 2048 bytes, let 12 samples for reading latency */
+#define INV_ICM42607_FIFO_WATERMARK_MAX (2048 - 12 * 16)
+#define INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE 8
+#define INV_ICM42607_FIFO_2SENSORS_PACKET_SIZE 16
+
+#define INV_ICM42607_REG_INT_SOURCE0 0x2B
+#define INV_ICM42607_INT_SOURCE0_ST_INT1_EN BIT(7)
+#define INV_ICM42607_INT_SOURCE0_FSYNC_INT1_EN BIT(6)
+#define INV_ICM42607_INT_SOURCE0_PLL_RDY_INT1_EN BIT(5)
+#define INV_ICM42607_INT_SOURCE0_RESET_DONE_INT1_EN BIT(4)
+#define INV_ICM42607_INT_SOURCE0_DRDY_INT1_EN BIT(3)
+#define INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN BIT(2)
+#define INV_ICM42607_INT_SOURCE0_FIFO_FULL_INT1_EN BIT(1)
+#define INV_ICM42607_INT_SOURCE0_AGC_RDY_INT1_EN BIT(0)
+
+#define INV_ICM42607_REG_INT_SOURCE1 0x2C
+#define INV_ICM42607_INT_SOURCE1_I3C_ERROR_INT1_EN BIT(6)
+#define INV_ICM42607_INT_SOURCE1_SMD_INT1_EN BIT(3)
+#define INV_ICM42607_INT_SOURCE1_WOM_INT1_EN GENMASK(2, 0)
+
+#define INV_ICM42607_REG_INT_SOURCE3 0x2D
+#define INV_ICM42607_INT_SOURCE3_ST_INT2_EN BIT(7)
+#define INV_ICM42607_INT_SOURCE3_FSYNC_INT2_EN BIT(6)
+#define INV_ICM42607_INT_SOURCE3_PLL_RDY_INT2_EN BIT(5)
+#define INV_ICM42607_INT_SOURCE3_RESET_DONE_INT2_EN BIT(4)
+#define INV_ICM42607_INT_SOURCE3_DRDY_INT2_EN BIT(3)
+#define INV_ICM42607_INT_SOURCE3_FIFO_THS_INT2_EN BIT(2)
+#define INV_ICM42607_INT_SOURCE3_FIFO_FULL_INT2_EN BIT(1)
+#define INV_ICM42607_INT_SOURCE3_AGC_RDY_INT2_EN BIT(0)
+
+#define INV_ICM42607_REG_INT_SOURCE4 0x2E
+#define INV_ICM42607_INT_SOURCE4_I3C_ERROR_INT2_EN BIT(6)
+#define INV_ICM42607_INT_SOURCE4_SMD_INT2_EN BIT(3)
+#define INV_ICM42607_INT_SOURCE4_WOM_Z_INT2_EN BIT(2)
+#define INV_ICM42607_INT_SOURCE4_WOM_Y_INT2_EN BIT(1)
+#define INV_ICM42607_INT_SOURCE4_WOM_X_INT2_EN BIT(0)
+
+#define INV_ICM42607_REG_FIFO_LOST_PKT0 0x2F
+#define INV_ICM42607_REG_FIFO_LOST_PKT1 0x30
+
+#define INV_ICM42607_REG_INTF_CONFIG0 0x35
+#define INV_ICM42607_INTF_CONFIG0_FIFO_COUNT_FORMAT BIT(6)
+#define INV_ICM42607_INTF_CONFIG0_FIFO_COUNT_ENDIAN BIT(5)
+#define INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN BIT(4)
+#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK GENMASK(1, 0)
+#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_SPI_DIS \
+ FIELD_PREP(INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK, 2)
+#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_I2C_DIS \
+ FIELD_PREP(INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK, 3)
+
+#define INV_ICM42607_REG_INTF_CONFIG1 0x36
+#define INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN BIT(3)
+#define INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN BIT(2)
+#define INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK GENMASK(1, 0)
+#define INV_ICM42607_INTF_CONFIG1_CLKSEL_INT 0
+#define INV_ICM42607_INTF_CONFIG1_CLKSEL_PLL 1
+#define INV_ICM42607_INTF_CONFIG1_CLKSEL_OFF 2
+
+#define INV_ICM42607_REG_INT_STATUS_DRDY 0x39
+#define INV_ICM42607_INT_STATUS_DRDY_DATA_RDY BIT(0)
+
+#define INV_ICM42607_REG_INT_STATUS 0x3A
+#define INV_ICM42607_INT_STATUS_ST BIT(7)
+#define INV_ICM42607_INT_STATUS_FSYNC BIT(6)
+#define INV_ICM42607_INT_STATUS_PLL_RDY BIT(5)
+#define INV_ICM42607_INT_STATUS_RESET_DONE BIT(4)
+#define INV_ICM42607_INT_STATUS_FIFO_THS BIT(2)
+#define INV_ICM42607_INT_STATUS_FIFO_FULL BIT(1)
+#define INV_ICM42607_INT_STATUS_AGC_RDY BIT(0)
+
+#define INV_ICM42607_REG_INT_STATUS2 0x3B
+#define INV_ICM42607_INT_STATUS2_SMD BIT(3)
+#define INV_ICM42607_INT_STATUS2_WOM_INT GENMASK(2, 0)
+
+#define INV_ICM42607_REG_INT_STATUS3 0x3C
+#define INV_ICM42607_INT_STATUS3_STEP_DET BIT(5)
+#define INV_ICM42607_INT_STATUS3_STEP_CNT_OVF BIT(4)
+#define INV_ICM42607_INT_STATUS3_TILT_DET BIT(3)
+#define INV_ICM42607_INT_STATUS3_FF_DET BIT(2)
+
+/*
+ * FIFO access registers
+ * FIFO count is 16 bits (2 registers) big-endian
+ * FIFO data is a continuous read register to read FIFO content
+ */
+#define INV_ICM42607_REG_FIFO_COUNTH 0x3D
+#define INV_ICM42607_REG_FIFO_COUNTL 0x3E
+#define INV_ICM42607_REG_FIFO_DATA 0x3F
+
+#define INV_ICM42607_REG_ACCEL_WOM_X_THR 0x4b
+#define INV_ICM42607_REG_ACCEL_WOM_Y_THR 0x4c
+#define INV_ICM42607_REG_ACCEL_WOM_Z_THR 0x4d
+
+#define INV_ICM42607_REG_WHOAMI 0x75
+#define INV_ICM42607P_WHOAMI 0x60
+#define INV_ICM42607_WHOAMI 0x67
+
+/* Timings as listed in section 3 of datasheet */
+#define INV_ICM42607_POWER_UP_TIME_US 100000
+#define INV_ICM42607_RESET_TIME_MS 1
+#define INV_ICM42607_ACCEL_STARTUP_TIME_MS 10
+#define INV_ICM42607_GYRO_STARTUP_TIME_MS 30
+#define INV_ICM42607_TEMP_STARTUP_TIME_MS 77
+
+typedef int (*inv_icm42607_bus_setup)(struct inv_icm42607_state *);
+
+extern const struct regmap_config inv_icm42607_regmap_config;
+extern const struct inv_icm42607_hw inv_icm42607_hw_data;
+extern const struct inv_icm42607_hw inv_icm42607p_hw_data;
+
+int inv_icm42607_core_probe(struct regmap *regmap,
+ const struct inv_icm42607_hw *hw,
+ inv_icm42607_bus_setup bus_setup);
+
+#endif
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
new file mode 100644
index 000000000000..4801ece28058
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/interrupt.h>
+#include <linux/iio/iio.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include "inv_icm42607.h"
+
+static int inv_icm42607_set_conf(struct inv_icm42607_state *st,
+ const struct inv_icm42607_conf *conf)
+{
+ unsigned int val;
+ int ret;
+
+ val = FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, conf->gyro.mode);
+ val |= FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, conf->accel.mode);
+ /*
+ * No temperature enable reg in datasheet, but BSP driver selected RC
+ * oscillator clock in LP mode when temperature was disabled.
+ */
+ if (!conf->temp_en)
+ val |= INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL;
+ ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK, conf->gyro.fs);
+ val |= FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_ODR_MASK, conf->gyro.odr);
+ ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK, conf->accel.fs);
+ val |= FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_ODR_MASK, conf->accel.odr);
+ ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, conf->gyro.filter);
+ ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG1, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, conf->accel.filter);
+ ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG1, val);
+ if (ret)
+ return ret;
+
+ st->conf = *conf;
+
+ return 0;
+}
+
+/**
+ * inv_icm42607_setup() - check and setup chip
+ * @st: driver internal state
+ * @bus_setup: callback for setting up bus specific registers
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+static int inv_icm42607_setup(struct inv_icm42607_state *st,
+ inv_icm42607_bus_setup bus_setup)
+{
+ const struct device *dev = regmap_get_device(st->map);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(st->map, INV_ICM42607_REG_WHOAMI, &val);
+ if (ret)
+ return ret;
+
+ if (val != st->hw->whoami)
+ dev_warn(dev, "Unknown whoami %#02x expected %#02x (%s)\n",
+ val, st->hw->whoami, st->hw->name);
+
+ ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET,
+ INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET);
+ if (ret)
+ return ret;
+
+ fsleep(INV_ICM42607_RESET_TIME_MS * 1000);
+
+ ret = regmap_read_poll_timeout(st->map, INV_ICM42607_REG_INT_STATUS,
+ val, val & INV_ICM42607_INT_STATUS_RESET_DONE,
+ INV_ICM42607_RESET_TIME_MS * 100,
+ INV_ICM42607_RESET_TIME_MS * 10000);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "reset error, reset done bit not set\n");
+
+ /* Sync the regcache again after a reset. */
+ ret = regcache_sync(st->map);
+ if (ret)
+ return ret;
+
+ ret = bus_setup(st);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
+ INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1,
+ INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK,
+ INV_ICM42607_INTF_CONFIG1_CLKSEL_PLL);
+ if (ret)
+ return ret;
+
+ return inv_icm42607_set_conf(st, st->hw->conf);
+}
+
+static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st)
+{
+ int ret;
+
+ ret = regulator_enable(st->vddio_supply);
+ if (ret)
+ return ret;
+
+ fsleep(INV_ICM42607_POWER_UP_TIME_US);
+
+ return 0;
+}
+
+static void inv_icm42607_disable_vddio_reg(void *_data)
+{
+ struct inv_icm42607_state *st = _data;
+
+ regulator_disable(st->vddio_supply);
+}
+
+int inv_icm42607_core_probe(struct regmap *regmap,
+ const struct inv_icm42607_hw *hw,
+ inv_icm42607_bus_setup bus_setup)
+{
+ struct device *dev = regmap_get_device(regmap);
+ struct inv_icm42607_state *st;
+ int irq;
+ int ret;
+
+ irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT1");
+ if (irq < 0)
+ return dev_err_probe(dev, irq, "Unable to get INT1 interrupt\n");
+
+ st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, st);
+
+ ret = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
+ st->hw = hw;
+ st->map = regmap;
+ st->irq = irq;
+
+ ret = iio_read_mount_matrix(dev, &st->orientation);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to retrieve mounting matrix %d\n", ret);
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to get vdd regulator\n");
+
+ st->vddio_supply = devm_regulator_get(dev, "vddio");
+ if (IS_ERR(st->vddio_supply))
+ return dev_err_probe(dev, PTR_ERR(st->vddio_supply),
+ "Failed to get vddio regulator\n");
+
+ ret = inv_icm42607_enable_vddio_reg(st);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, inv_icm42607_disable_vddio_reg, st);
+ if (ret)
+ return ret;
+
+ /* Setup chip registers (includes WHOAMI check, reset check, bus setup) */
+ ret = inv_icm42607_setup(st, bus_setup);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607");
+
+MODULE_AUTHOR("InvenSense, Inc.");
+MODULE_DESCRIPTION("InvenSense ICM-42607x device driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_INV_SENSORS_TIMESTAMP");
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH V9 03/11] iio: imu: inv_icm42607: Add inv_icm42607 Core Driver
2026-05-30 3:17 ` [PATCH V9 03/11] iio: imu: inv_icm42607: Add inv_icm42607 Core Driver Chris Morgan
@ 2026-05-31 12:11 ` Jonathan Cameron
0 siblings, 0 replies; 26+ messages in thread
From: Jonathan Cameron @ 2026-05-31 12:11 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-iio, andy, nuno.sa, dlechner, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
On Fri, 29 May 2026 22:17:30 -0500
Chris Morgan <macroalpha82@gmail.com> wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add the core component of a new inv_icm42607 driver. This includes
> a few setup functions and the full register definition in the
> header file.
https://sashiko.dev/#/patchset/20260530031739.109063-1-macroalpha82%40gmail.com
Again has some feedback. I've commented on them below to say whether I agree
or not. Also a few other minor things inline.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
> ---
> drivers/iio/imu/inv_icm42607/inv_icm42607.h | 336 ++++++++++++++++++
> .../iio/imu/inv_icm42607/inv_icm42607_core.c | 206 +++++++++++
> 2 files changed, 542 insertions(+)
> create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607.h
> create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
>
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> new file mode 100644
> index 000000000000..4801ece28058
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> +static int inv_icm42607_set_conf(struct inv_icm42607_state *st,
> + const struct inv_icm42607_conf *conf)
> +{
> + unsigned int val;
> + int ret;
> +
> + val = FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, conf->gyro.mode);
> + val |= FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, conf->accel.mode);
> + /*
> + * No temperature enable reg in datasheet, but BSP driver selected RC
> + * oscillator clock in LP mode when temperature was disabled.
> + */
> + if (!conf->temp_en)
> + val |= INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL;
> + ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val);
> + if (ret)
> + return ret;
> +
> + val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK, conf->gyro.fs);
> + val |= FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_ODR_MASK, conf->gyro.odr);
> + ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val);
> + if (ret)
> + return ret;
> +
> + val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK, conf->accel.fs);
> + val |= FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_ODR_MASK, conf->accel.odr);
> + ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val);
> + if (ret)
> + return ret;
> +
> + val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, conf->gyro.filter);
> + ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG1, val);
> + if (ret)
> + return ret;
> +
> + val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, conf->accel.filter);
> + ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG1, val);
> + if (ret)
Sashiko points out that this is overwriting whatever is the
INV_ICM42607_ACCEL_CONFIG1_AVG_MASK field. That isn't set at all yet, which is fine
but I'd be tempted to make it clear what intent is here. Either explicitly set it
to 0 as
val |= FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_AVG_MASK, 0);
or use regmap_update_bits as sashiko suggests.
> + return ret;
> +
> + st->conf = *conf;
> +
> + return 0;
> +}
> +
> +/**
> + * inv_icm42607_setup() - check and setup chip
> + * @st: driver internal state
> + * @bus_setup: callback for setting up bus specific registers
> + *
> + * Returns 0 on success, a negative error code otherwise.
> + */
> +static int inv_icm42607_setup(struct inv_icm42607_state *st,
> + inv_icm42607_bus_setup bus_setup)
> +{
> + const struct device *dev = regmap_get_device(st->map);
> + unsigned int val;
> + int ret;
> +
> + ret = regmap_read(st->map, INV_ICM42607_REG_WHOAMI, &val);
> + if (ret)
> + return ret;
> +
> + if (val != st->hw->whoami)
> + dev_warn(dev, "Unknown whoami %#02x expected %#02x (%s)\n",
> + val, st->hw->whoami, st->hw->name);
> +
> + ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET,
> + INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET);
> + if (ret)
> + return ret;
> +
> + fsleep(INV_ICM42607_RESET_TIME_MS * 1000);
> +
> + ret = regmap_read_poll_timeout(st->map, INV_ICM42607_REG_INT_STATUS,
> + val, val & INV_ICM42607_INT_STATUS_RESET_DONE,
> + INV_ICM42607_RESET_TIME_MS * 100,
> + INV_ICM42607_RESET_TIME_MS * 10000);
> + if (ret)
> + return dev_err_probe(dev, ret,
> + "reset error, reset done bit not set\n");
> +
> + /* Sync the regcache again after a reset. */
> + ret = regcache_sync(st->map);
I haven't checked sashiko's feedback on this but it seems plausible as
there is a check in regcache_sync on whether the cache is dirty and no obvious
path for it to get to that state here.
> + if (ret)
> + return ret;
> +
> + ret = bus_setup(st);
> + if (ret)
> + return ret;
> +
> + ret = regmap_set_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
> + INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN);
> + if (ret)
> + return ret;
> +
> + ret = regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1,
> + INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK,
> + INV_ICM42607_INTF_CONFIG1_CLKSEL_PLL);
> + if (ret)
> + return ret;
> +
> + return inv_icm42607_set_conf(st, st->hw->conf);
> +}
> +int inv_icm42607_core_probe(struct regmap *regmap,
> + const struct inv_icm42607_hw *hw,
> + inv_icm42607_bus_setup bus_setup)
> +{
> + struct device *dev = regmap_get_device(regmap);
> + struct inv_icm42607_state *st;
> + int irq;
> + int ret;
> +
> + irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT1");
> + if (irq < 0)
> + return dev_err_probe(dev, irq, "Unable to get INT1 interrupt\n");
Sashiko comments on this and is correct that this only works on
device tree with interrupt-names. Which is intentional!
> +
> + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
> + if (!st)
> + return -ENOMEM;
> +
> + dev_set_drvdata(dev, st);
Really small thing. Is this used yet? I think this should be in a later
patch. Probably patch 5.
> +
> + ret = devm_mutex_init(dev, &st->lock);
> + if (ret)
> + return ret;
> +
> + st->hw = hw;
> + st->map = regmap;
> + st->irq = irq;
> +
> + ret = iio_read_mount_matrix(dev, &st->orientation);
> + if (ret)
> + return dev_err_probe(dev, ret,
> + "failed to retrieve mounting matrix %d\n", ret);
> +
> + ret = devm_regulator_get_enable(dev, "vdd");
> + if (ret)
> + return dev_err_probe(dev, ret,
> + "Failed to get vdd regulator\n");
> +
> + st->vddio_supply = devm_regulator_get(dev, "vddio");
> + if (IS_ERR(st->vddio_supply))
> + return dev_err_probe(dev, PTR_ERR(st->vddio_supply),
> + "Failed to get vddio regulator\n");
> +
> + ret = inv_icm42607_enable_vddio_reg(st);
> + if (ret)
> + return ret;
> +
> + ret = devm_add_action_or_reset(dev, inv_icm42607_disable_vddio_reg, st);
> + if (ret)
> + return ret;
> +
> + /* Setup chip registers (includes WHOAMI check, reset check, bus setup) */
> + ret = inv_icm42607_setup(st, bus_setup);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607");
> +
> +MODULE_AUTHOR("InvenSense, Inc.");
> +MODULE_DESCRIPTION("InvenSense ICM-42607x device driver");
> +MODULE_LICENSE("GPL");
> +MODULE_IMPORT_NS("IIO_INV_SENSORS_TIMESTAMP");
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH V9 04/11] iio: imu: inv_icm42607: Add I2C and SPI For icm42607
2026-05-30 3:17 [PATCH V9 00/11] Add Invensense ICM42607 Chris Morgan
` (2 preceding siblings ...)
2026-05-30 3:17 ` [PATCH V9 03/11] iio: imu: inv_icm42607: Add inv_icm42607 Core Driver Chris Morgan
@ 2026-05-30 3:17 ` Chris Morgan
2026-05-31 12:15 ` Jonathan Cameron
2026-05-30 3:17 ` [PATCH V9 05/11] iio: imu: inv_icm42607: Add PM support for icm42607 Chris Morgan
` (6 subsequent siblings)
10 siblings, 1 reply; 26+ messages in thread
From: Chris Morgan @ 2026-05-30 3:17 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add I2C and SPI driver support for InvenSense ICM-42607 devices.
Add necessary Kconfig and Makefile to allow building of (incomplete)
driver.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/Kconfig | 1 +
drivers/iio/imu/Makefile | 1 +
drivers/iio/imu/inv_icm42607/Kconfig | 30 +++++
drivers/iio/imu/inv_icm42607/Makefile | 10 ++
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 16 +++
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 57 ++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_i2c.c | 94 ++++++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_spi.c | 106 ++++++++++++++++++
8 files changed, 315 insertions(+)
create mode 100644 drivers/iio/imu/inv_icm42607/Kconfig
create mode 100644 drivers/iio/imu/inv_icm42607/Makefile
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
index 7e0181c27bb6..8bab4616be20 100644
--- a/drivers/iio/imu/Kconfig
+++ b/drivers/iio/imu/Kconfig
@@ -109,6 +109,7 @@ config KMX61
be called kmx61.
source "drivers/iio/imu/inv_icm42600/Kconfig"
+source "drivers/iio/imu/inv_icm42607/Kconfig"
source "drivers/iio/imu/inv_icm45600/Kconfig"
source "drivers/iio/imu/inv_mpu6050/Kconfig"
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
index 13fb7846e9c9..3268dc2371ae 100644
--- a/drivers/iio/imu/Makefile
+++ b/drivers/iio/imu/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_FXOS8700_I2C) += fxos8700_i2c.o
obj-$(CONFIG_FXOS8700_SPI) += fxos8700_spi.o
obj-y += inv_icm42600/
+obj-y += inv_icm42607/
obj-y += inv_icm45600/
obj-y += inv_mpu6050/
diff --git a/drivers/iio/imu/inv_icm42607/Kconfig b/drivers/iio/imu/inv_icm42607/Kconfig
new file mode 100644
index 000000000000..7ba64e6e8d80
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/Kconfig
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+config INV_ICM42607
+ tristate
+ select IIO_BUFFER
+ select IIO_INV_SENSORS_TIMESTAMP
+
+config INV_ICM42607_I2C
+ tristate "InvenSense ICM-42607X I2C driver"
+ depends on I2C
+ select INV_ICM42607
+ select REGMAP_I2C
+ help
+ This driver supports the InvenSense ICM-42607 motion tracking
+ device over I2C.
+
+ This driver can be built as a module. The module will be called
+ inv-icm42607-i2c.
+
+config INV_ICM42607_SPI
+ tristate "InvenSense ICM-42607X SPI driver"
+ depends on SPI_MASTER
+ select INV_ICM42607
+ select REGMAP_SPI
+ help
+ This driver supports the InvenSense ICM-42607 motion tracking
+ device over SPI.
+
+ This driver can be built as a module. The module will be called
+ inv-icm42607-spi.
diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile
new file mode 100644
index 000000000000..be109102e203
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o
+inv-icm42607-y += inv_icm42607_core.o
+
+obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o
+inv-icm42607-i2c-y += inv_icm42607_i2c.o
+
+obj-$(CONFIG_INV_ICM42607_SPI) += inv-icm42607-spi.o
+inv-icm42607-spi-y += inv_icm42607_spi.o
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
index fe565c423c98..7a73c917fe8a 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -13,6 +13,22 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+/*
+ * Serial bus slew rates. Rates are expressed as range between the two
+ * values with the midpoint as the typical rate. For the final value of
+ * 2ns, 2ns is considered the max value with no expressed minimum or
+ * typical value.
+ */
+enum inv_icm42607_slew_rate {
+ INV_ICM42607_SLEW_RATE_20_60NS,
+ INV_ICM42607_SLEW_RATE_12_36NS,
+ INV_ICM42607_SLEW_RATE_6_19NS,
+ INV_ICM42607_SLEW_RATE_4_14NS,
+ INV_ICM42607_SLEW_RATE_2_8NS,
+ INV_ICM42607_SLEW_RATE_2NS,
+ INV_ICM42607_SLEW_RATE_NB
+};
+
enum inv_icm42607_sensor_mode {
INV_ICM42607_SENSOR_MODE_OFF,
INV_ICM42607_SENSOR_MODE_STANDBY,
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
index 4801ece28058..3dd4cc8c6b1d 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -16,6 +16,63 @@
#include "inv_icm42607.h"
+static bool inv_icm42607_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case INV_ICM42607_REG_MCLK_RDY:
+ case INV_ICM42607_REG_SIGNAL_PATH_RESET:
+ case INV_ICM42607_REG_TEMP_DATA1 ... INV_ICM42607_REG_APEX_DATA5:
+ case INV_ICM42607_REG_APEX_CONFIG0:
+ case INV_ICM42607_REG_FIFO_CONFIG2 ... INV_ICM42607_REG_FIFO_CONFIG3:
+ case INV_ICM42607_REG_FIFO_LOST_PKT0 ... INV_ICM42607_REG_APEX_DATA3:
+ case INV_ICM42607_REG_INT_STATUS_DRDY:
+ case INV_ICM42607_REG_INT_STATUS ... INV_ICM42607_REG_FIFO_DATA:
+ return true;
+ }
+
+ return false;
+}
+
+const struct regmap_config inv_icm42607_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = INV_ICM42607_REG_WHOAMI,
+ .cache_type = REGCACHE_MAPLE,
+ .volatile_reg = inv_icm42607_is_volatile_reg,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm42607_regmap_config, "IIO_ICM42607");
+
+/* chip initial default configuration */
+static const struct inv_icm42607_conf inv_icm42607_default_conf = {
+ .gyro = {
+ .mode = INV_ICM42607_SENSOR_MODE_OFF,
+ .fs = INV_ICM42607_GYRO_FS_1000DPS,
+ .odr = INV_ICM42607_ODR_100HZ,
+ .filter = INV_ICM42607_FILTER_BW_25HZ,
+ },
+ .accel = {
+ .mode = INV_ICM42607_SENSOR_MODE_OFF,
+ .fs = INV_ICM42607_ACCEL_FS_4G,
+ .odr = INV_ICM42607_ODR_100HZ,
+ .filter = INV_ICM42607_FILTER_BW_25HZ,
+ },
+ .temp_en = false,
+};
+
+const struct inv_icm42607_hw inv_icm42607_hw_data = {
+ .whoami = INV_ICM42607_WHOAMI,
+ .name = "icm42607",
+ .conf = &inv_icm42607_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm42607_hw_data, "IIO_ICM42607");
+
+const struct inv_icm42607_hw inv_icm42607p_hw_data = {
+ .whoami = INV_ICM42607P_WHOAMI,
+ .name = "icm42607p",
+ .conf = &inv_icm42607_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607");
+
static int inv_icm42607_set_conf(struct inv_icm42607_state *st,
const struct inv_icm42607_conf *conf)
{
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
new file mode 100644
index 000000000000..22ac51108cd3
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+#include "inv_icm42607.h"
+
+static int inv_icm42607_i2c_bus_setup(struct inv_icm42607_state *st)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_clear_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1,
+ INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN |
+ INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_DRIVE_CONFIG2_I2C_MASK,
+ INV_ICM42607_SLEW_RATE_12_36NS);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_DRIVE_CONFIG2,
+ INV_ICM42607_DRIVE_CONFIG2_I2C_MASK, val);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
+ INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK,
+ INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_SPI_DIS);
+}
+
+static int inv_icm42607_probe(struct i2c_client *client)
+{
+ const struct inv_icm42607_hw *hw;
+ struct regmap *regmap;
+
+ hw = i2c_get_match_data(client);
+ if (!hw)
+ return dev_err_probe(&client->dev, -ENODEV,
+ "Failed to get i2c data\n");
+
+ regmap = devm_regmap_init_i2c(client, &inv_icm42607_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(regmap),
+ "Failed to register i2c regmap %ld\n",
+ PTR_ERR(regmap));
+
+ return inv_icm42607_core_probe(regmap, hw, inv_icm42607_i2c_bus_setup);
+}
+
+static const struct i2c_device_id inv_icm42607_id[] = {
+ {
+ .name = "icm42607",
+ .driver_data = (kernel_ulong_t)&inv_icm42607_hw_data
+ }, {
+ .name = "icm42607p",
+ .driver_data = (kernel_ulong_t)&inv_icm42607p_hw_data
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, inv_icm42607_id);
+
+static const struct of_device_id inv_icm42607_of_matches[] = {
+ {
+ .compatible = "invensense,icm42607",
+ .data = &inv_icm42607_hw_data,
+ }, {
+ .compatible = "invensense,icm42607p",
+ .data = &inv_icm42607p_hw_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, inv_icm42607_of_matches);
+
+static struct i2c_driver inv_icm42607_driver = {
+ .driver = {
+ .name = "inv-icm42607-i2c",
+ .of_match_table = inv_icm42607_of_matches,
+ },
+ .id_table = inv_icm42607_id,
+ .probe = inv_icm42607_probe,
+};
+module_i2c_driver(inv_icm42607_driver);
+
+MODULE_AUTHOR("InvenSense, Inc.");
+MODULE_DESCRIPTION("InvenSense ICM-42607x I2C driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_ICM42607");
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
new file mode 100644
index 000000000000..421d3ec1cded
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "inv_icm42607.h"
+
+static int inv_icm42607_spi_bus_setup(struct inv_icm42607_state *st)
+{
+ unsigned int val;
+ int ret;
+
+ /* Only support 4-wire mode for now. */
+ ret = regmap_set_bits(st->map, INV_ICM42607_REG_DEVICE_CONFIG,
+ INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE);
+ if (ret)
+ return ret;
+
+ ret = regmap_clear_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1,
+ INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN |
+ INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_DRIVE_CONFIG3_SPI_MASK,
+ INV_ICM42607_SLEW_RATE_2NS);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_DRIVE_CONFIG3,
+ INV_ICM42607_DRIVE_CONFIG3_SPI_MASK, val);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
+ INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK,
+ INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_I2C_DIS);
+}
+
+static int inv_icm42607_probe(struct spi_device *spi)
+{
+ const struct inv_icm42607_hw *hw;
+ struct regmap *regmap;
+
+ hw = spi_get_device_match_data(spi);
+ if (!hw)
+ return dev_err_probe(&spi->dev, -ENODEV,
+ "Failed to get SPI data\n");
+
+ if (spi->mode & SPI_3WIRE)
+ return dev_err_probe(&spi->dev, -ENODEV,
+ "SPI 3-wire mode not supported\n");
+
+ regmap = devm_regmap_init_spi(spi, &inv_icm42607_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&spi->dev, PTR_ERR(regmap),
+ "Failed to register spi regmap %ld\n",
+ PTR_ERR(regmap));
+
+ return inv_icm42607_core_probe(regmap, hw,
+ inv_icm42607_spi_bus_setup);
+}
+
+static const struct spi_device_id inv_icm42607_spi_id_table[] = {
+ {
+ .name = "icm42607",
+ .driver_data = (kernel_ulong_t)&inv_icm42607_hw_data
+ }, {
+ .name = "icm42607p",
+ .driver_data = (kernel_ulong_t)&inv_icm42607p_hw_data
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, inv_icm42607_spi_id_table);
+
+static const struct of_device_id inv_icm42607_of_matches[] = {
+ {
+ .compatible = "invensense,icm42607",
+ .data = &inv_icm42607_hw_data,
+ },
+ {
+ .compatible = "invensense,icm42607p",
+ .data = &inv_icm42607p_hw_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, inv_icm42607_of_matches);
+
+static struct spi_driver inv_icm42607_driver = {
+ .driver = {
+ .name = "inv-icm42607-spi",
+ .of_match_table = inv_icm42607_of_matches,
+ },
+ .id_table = inv_icm42607_spi_id_table,
+ .probe = inv_icm42607_probe,
+};
+module_spi_driver(inv_icm42607_driver);
+
+MODULE_AUTHOR("InvenSense, Inc.");
+MODULE_DESCRIPTION("InvenSense ICM-42607x SPI driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_ICM42607");
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH V9 04/11] iio: imu: inv_icm42607: Add I2C and SPI For icm42607
2026-05-30 3:17 ` [PATCH V9 04/11] iio: imu: inv_icm42607: Add I2C and SPI For icm42607 Chris Morgan
@ 2026-05-31 12:15 ` Jonathan Cameron
0 siblings, 0 replies; 26+ messages in thread
From: Jonathan Cameron @ 2026-05-31 12:15 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-iio, andy, nuno.sa, dlechner, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
On Fri, 29 May 2026 22:17:31 -0500
Chris Morgan <macroalpha82@gmail.com> wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add I2C and SPI driver support for InvenSense ICM-42607 devices.
> Add necessary Kconfig and Makefile to allow building of (incomplete)
> driver.
>
Sashiko suggested power sequencing timing might apply between vdd and vddio.
I took a quick look and didn't spot it on the datasheet. Perhaps they've
figured out how not to have that burden on the platforms using their chips!
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
LGTM
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH V9 05/11] iio: imu: inv_icm42607: Add PM support for icm42607
2026-05-30 3:17 [PATCH V9 00/11] Add Invensense ICM42607 Chris Morgan
` (3 preceding siblings ...)
2026-05-30 3:17 ` [PATCH V9 04/11] iio: imu: inv_icm42607: Add I2C and SPI For icm42607 Chris Morgan
@ 2026-05-30 3:17 ` Chris Morgan
2026-05-31 12:21 ` Jonathan Cameron
2026-05-30 3:17 ` [PATCH V9 06/11] iio: imu: inv_icm42607: Add Buffer " Chris Morgan
` (5 subsequent siblings)
10 siblings, 1 reply; 26+ messages in thread
From: Chris Morgan @ 2026-05-30 3:17 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add power management support for the ICM42607 device driver.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 10 ++
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 144 ++++++++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_i2c.c | 1 +
.../iio/imu/inv_icm42607/inv_icm42607_spi.c | 1 +
4 files changed, 156 insertions(+)
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
index 7a73c917fe8a..00c8dfe20717 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -10,6 +10,7 @@
#include <linux/bitops.h>
#include <linux/iio/iio.h>
#include <linux/mutex.h>
+#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -109,6 +110,7 @@ struct inv_icm42607_hw {
* @hw: Hardware specific data.
* @map: regmap pointer.
* @vddio_supply: I/O voltage regulator for the chip.
+ * @vddio_en: I/O voltage status for runtime PM.
* @irq: chip irq, required to enable/disable and set wakeup
* @orientation: sensor chip orientation relative to main hardware.
* @conf: chip sensors configurations.
@@ -118,6 +120,7 @@ struct inv_icm42607_state {
const struct inv_icm42607_hw *hw;
struct regmap *map;
struct regulator *vddio_supply;
+ bool vddio_en;
int irq;
struct iio_mount_matrix orientation;
struct inv_icm42607_conf conf;
@@ -339,11 +342,18 @@ struct inv_icm42607_state {
#define INV_ICM42607_GYRO_STARTUP_TIME_MS 30
#define INV_ICM42607_TEMP_STARTUP_TIME_MS 77
+/*
+ * Suspend delay assumed from other icm42600 series device, not
+ * documented in datasheet.
+ */
+#define INV_ICM42607_SUSPEND_DELAY_MS 2000
+
typedef int (*inv_icm42607_bus_setup)(struct inv_icm42607_state *);
extern const struct regmap_config inv_icm42607_regmap_config;
extern const struct inv_icm42607_hw inv_icm42607_hw_data;
extern const struct inv_icm42607_hw inv_icm42607p_hw_data;
+extern const struct dev_pm_ops inv_icm42607_pm_ops;
int inv_icm42607_core_probe(struct regmap *regmap,
const struct inv_icm42607_hw *hw,
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
index 3dd4cc8c6b1d..cfdce70ce25d 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -10,6 +10,7 @@
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -73,6 +74,51 @@ const struct inv_icm42607_hw inv_icm42607p_hw_data = {
};
EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607");
+static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st,
+ enum inv_icm42607_sensor_mode gyro,
+ enum inv_icm42607_sensor_mode accel,
+ bool temp, unsigned int *sleep_ms)
+{
+ enum inv_icm42607_sensor_mode oldgyro = st->conf.gyro.mode;
+ enum inv_icm42607_sensor_mode oldaccel = st->conf.accel.mode;
+ bool oldtemp = st->conf.temp_en;
+ unsigned int sleepval;
+ unsigned int val;
+ int ret;
+
+ if (gyro == oldgyro && accel == oldaccel && temp == oldtemp)
+ return 0;
+
+ val = FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, gyro);
+ val |= FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, accel);
+ if (!temp)
+ val |= INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL;
+ ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val);
+ if (ret)
+ return ret;
+
+ st->conf.gyro.mode = gyro;
+ st->conf.accel.mode = accel;
+ st->conf.temp_en = temp;
+
+ sleepval = 0;
+ if (temp && !oldtemp)
+ sleepval = max(sleepval, INV_ICM42607_TEMP_STARTUP_TIME_MS);
+
+ if (accel != oldaccel)
+ sleepval = max(sleepval, INV_ICM42607_ACCEL_STARTUP_TIME_MS);
+
+ if (gyro != oldgyro)
+ sleepval = max(sleepval, INV_ICM42607_GYRO_STARTUP_TIME_MS);
+
+ if (sleep_ms)
+ *sleep_ms = sleepval;
+ else if (sleepval)
+ fsleep(sleepval * 1000);
+
+ return 0;
+}
+
static int inv_icm42607_set_conf(struct inv_icm42607_state *st,
const struct inv_icm42607_conf *conf)
{
@@ -182,12 +228,17 @@ static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st)
{
int ret;
+ if (st->vddio_en)
+ return 0;
+
ret = regulator_enable(st->vddio_supply);
if (ret)
return ret;
fsleep(INV_ICM42607_POWER_UP_TIME_US);
+ st->vddio_en = true;
+
return 0;
}
@@ -195,7 +246,12 @@ static void inv_icm42607_disable_vddio_reg(void *_data)
{
struct inv_icm42607_state *st = _data;
+ if (!st->vddio_en)
+ return;
+
regulator_disable(st->vddio_supply);
+
+ st->vddio_en = false;
}
int inv_icm42607_core_probe(struct regmap *regmap,
@@ -253,10 +309,98 @@ int inv_icm42607_core_probe(struct regmap *regmap,
if (ret)
return ret;
+ ret = devm_pm_runtime_set_active_enabled(dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, INV_ICM42607_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+
return 0;
}
EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607");
+static int inv_icm42607_suspend(struct device *dev)
+{
+ struct inv_icm42607_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret)
+ return ret;
+
+ ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
+ INV_ICM42607_SENSOR_MODE_OFF,
+ false, NULL);
+ if (ret)
+ return ret;
+
+ inv_icm42607_disable_vddio_reg(st);
+
+ return 0;
+}
+
+static int inv_icm42607_resume(struct device *dev)
+{
+ struct inv_icm42607_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ return ret;
+
+ ret = inv_icm42607_enable_vddio_reg(st);
+ if (ret)
+ return ret;
+
+ /* Nothing else to restore at this time. */
+
+ return 0;
+}
+
+static int inv_icm42607_runtime_suspend(struct device *dev)
+{
+ struct inv_icm42607_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
+ INV_ICM42607_SENSOR_MODE_OFF, false,
+ NULL);
+ if (ret)
+ return ret;
+
+ inv_icm42607_disable_vddio_reg(st);
+
+ return 0;
+}
+
+static int inv_icm42607_runtime_resume(struct device *dev)
+{
+ struct inv_icm42607_state *st = dev_get_drvdata(dev);
+
+ guard(mutex)(&st->lock);
+
+ return inv_icm42607_enable_vddio_reg(st);
+}
+
+EXPORT_NS_GPL_DEV_PM_OPS(inv_icm42607_pm_ops, IIO_ICM42607) = {
+ SYSTEM_SLEEP_PM_OPS(inv_icm42607_suspend, inv_icm42607_resume)
+ RUNTIME_PM_OPS(inv_icm42607_runtime_suspend,
+ inv_icm42607_runtime_resume, NULL)
+};
+
MODULE_AUTHOR("InvenSense, Inc.");
MODULE_DESCRIPTION("InvenSense ICM-42607x device driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
index 22ac51108cd3..cd08bb9514f2 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
@@ -82,6 +82,7 @@ static struct i2c_driver inv_icm42607_driver = {
.driver = {
.name = "inv-icm42607-i2c",
.of_match_table = inv_icm42607_of_matches,
+ .pm = pm_ptr(&inv_icm42607_pm_ops),
},
.id_table = inv_icm42607_id,
.probe = inv_icm42607_probe,
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
index 421d3ec1cded..8a82e1610531 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
@@ -94,6 +94,7 @@ static struct spi_driver inv_icm42607_driver = {
.driver = {
.name = "inv-icm42607-spi",
.of_match_table = inv_icm42607_of_matches,
+ .pm = pm_ptr(&inv_icm42607_pm_ops),
},
.id_table = inv_icm42607_spi_id_table,
.probe = inv_icm42607_probe,
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH V9 05/11] iio: imu: inv_icm42607: Add PM support for icm42607
2026-05-30 3:17 ` [PATCH V9 05/11] iio: imu: inv_icm42607: Add PM support for icm42607 Chris Morgan
@ 2026-05-31 12:21 ` Jonathan Cameron
0 siblings, 0 replies; 26+ messages in thread
From: Jonathan Cameron @ 2026-05-31 12:21 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-iio, andy, nuno.sa, dlechner, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
On Fri, 29 May 2026 22:17:32 -0500
Chris Morgan <macroalpha82@gmail.com> wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add power management support for the ICM42607 device driver.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Hi Chris,
https://sashiko.dev/#/patchset/20260530031739.109063-1-macroalpha82%40gmail.com
Points out a deadlock. Suggests maybe you need a few more test cases!
More generally I think you've misunderstood how force_suspend is used.
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> index 3dd4cc8c6b1d..cfdce70ce25d 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
>
> +static int inv_icm42607_suspend(struct device *dev)
> +{
> + struct inv_icm42607_state *st = dev_get_drvdata(dev);
> + int ret;
> +
> + guard(mutex)(&st->lock);
> +
> + if (pm_runtime_suspended(dev))
> + return 0;
Force suspend will deal with this for everything in there.
I'm a little confused on why runtime suspend effects disabing
vddio_reg.
> +
> + ret = pm_runtime_force_suspend(dev);
You'll need to do this without holding the lock.
Also take a much closer look at what it does.
(sashiko points out why what you have here doesn't make sense).
> + if (ret)
> + return ret;
> +
> + ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
> + INV_ICM42607_SENSOR_MODE_OFF,
> + false, NULL);
> + if (ret)
> + return ret;
> +
> + inv_icm42607_disable_vddio_reg(st);
> +
> + return 0;
> +}
> +
> +static int inv_icm42607_runtime_suspend(struct device *dev)
> +{
> + struct inv_icm42607_state *st = dev_get_drvdata(dev);
> + int ret;
> +
> + guard(mutex)(&st->lock);
> +
> + ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
> + INV_ICM42607_SENSOR_MODE_OFF, false,
> + NULL);
> + if (ret)
> + return ret;
> +
> + inv_icm42607_disable_vddio_reg(st);
> +
> + return 0;
> +}
I cropped the resume cases but they have the same problems..
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH V9 06/11] iio: imu: inv_icm42607: Add Buffer support for icm42607
2026-05-30 3:17 [PATCH V9 00/11] Add Invensense ICM42607 Chris Morgan
` (4 preceding siblings ...)
2026-05-30 3:17 ` [PATCH V9 05/11] iio: imu: inv_icm42607: Add PM support for icm42607 Chris Morgan
@ 2026-05-30 3:17 ` Chris Morgan
2026-05-31 12:38 ` Jonathan Cameron
2026-05-30 3:17 ` [PATCH V9 07/11] iio: imu: inv_icm42607: Add Temp Support in icm42607 Chris Morgan
` (4 subsequent siblings)
10 siblings, 1 reply; 26+ messages in thread
From: Chris Morgan @ 2026-05-30 3:17 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add all FIFO parsing and reading functions to support
inv_icm42607 hardware.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/inv_icm42607/Makefile | 1 +
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 26 +
.../imu/inv_icm42607/inv_icm42607_buffer.c | 483 ++++++++++++++++++
.../imu/inv_icm42607/inv_icm42607_buffer.h | 93 ++++
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 52 +-
5 files changed, 654 insertions(+), 1 deletion(-)
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile
index be109102e203..3c9d08509793 100644
--- a/drivers/iio/imu/inv_icm42607/Makefile
+++ b/drivers/iio/imu/inv_icm42607/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o
inv-icm42607-y += inv_icm42607_core.o
+inv-icm42607-y += inv_icm42607_buffer.o
obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o
inv-icm42607-i2c-y += inv_icm42607_i2c.o
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
index 00c8dfe20717..cb878de970d4 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -14,6 +14,10 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+#include <linux/iio/common/inv_sensors_timestamp.h>
+
+#include "inv_icm42607_buffer.h"
+
/*
* Serial bus slew rates. Rates are expressed as range between the two
* values with the midpoint as the typical rate. For the final value of
@@ -114,6 +118,10 @@ struct inv_icm42607_hw {
* @irq: chip irq, required to enable/disable and set wakeup
* @orientation: sensor chip orientation relative to main hardware.
* @conf: chip sensors configurations.
+ * @indio_gyro: gyroscope IIO device.
+ * @indio_accel: accelerometer IIO device.
+ * @fifo: FIFO management structure.
+ * @buffer: data transfer buffer aligned for DMA.
*/
struct inv_icm42607_state {
struct mutex lock;
@@ -124,6 +132,22 @@ struct inv_icm42607_state {
int irq;
struct iio_mount_matrix orientation;
struct inv_icm42607_conf conf;
+ struct iio_dev *indio_gyro;
+ struct iio_dev *indio_accel;
+ struct inv_icm42607_fifo fifo;
+ __be16 buffer[3] __aligned(IIO_DMA_MINALIGN);
+};
+
+/**
+ * struct inv_icm42607_sensor_state - sensor state variables
+ * @power_mode: sensor requested power mode (for common frequencies)
+ * @filter: sensor filter.
+ * @ts: timestamp module states.
+ */
+struct inv_icm42607_sensor_state {
+ enum inv_icm42607_sensor_mode power_mode;
+ int filter;
+ struct inv_sensors_timestamp ts;
};
/* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */
@@ -355,6 +379,8 @@ extern const struct inv_icm42607_hw inv_icm42607_hw_data;
extern const struct inv_icm42607_hw inv_icm42607p_hw_data;
extern const struct dev_pm_ops inv_icm42607_pm_ops;
+u32 inv_icm42607_odr_to_period(enum inv_icm42607_odr odr);
+
int inv_icm42607_core_probe(struct regmap *regmap,
const struct inv_icm42607_hw *hw,
inv_icm42607_bus_setup bus_setup);
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
new file mode 100644
index 000000000000..e065d60ac119
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
@@ -0,0 +1,483 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/minmax.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/unaligned.h>
+
+#include <linux/iio/common/inv_sensors_timestamp.h>
+
+#include "inv_icm42607.h"
+#include "inv_icm42607_buffer.h"
+
+/* FIFO header: 1 byte */
+#define INV_ICM42607_FIFO_HEADER_MSG BIT(7)
+#define INV_ICM42607_FIFO_HEADER_ACCEL BIT(6)
+#define INV_ICM42607_FIFO_HEADER_GYRO BIT(5)
+#define INV_ICM42607_FIFO_HEADER_TMST_FSYNC GENMASK(3, 2)
+#define INV_ICM42607_FIFO_HEADER_ODR_ACCEL BIT(1)
+#define INV_ICM42607_FIFO_HEADER_ODR_GYRO BIT(0)
+
+struct inv_icm42607_fifo_1sensor_packet {
+ u8 header;
+ struct inv_icm42607_fifo_sensor_data data;
+ s8 temp;
+} __packed;
+
+struct inv_icm42607_fifo_2sensors_packet {
+ u8 header;
+ struct inv_icm42607_fifo_sensor_data accel;
+ struct inv_icm42607_fifo_sensor_data gyro;
+ s8 temp;
+ __be16 timestamp;
+} __packed;
+
+ssize_t inv_icm42607_fifo_decode_packet(const void *packet, const void **accel,
+ const void **gyro, const int8_t **temp,
+ const void **timestamp)
+{
+ const struct inv_icm42607_fifo_1sensor_packet *pack1 = get_unaligned(&packet);
+ const struct inv_icm42607_fifo_2sensors_packet *pack2 = get_unaligned(&packet);
+ u8 header = *((const u8 *)packet);
+
+ /* FIFO empty */
+ if (header & INV_ICM42607_FIFO_HEADER_MSG) {
+ *accel = NULL;
+ *gyro = NULL;
+ *temp = NULL;
+ *timestamp = NULL;
+ return 0;
+ }
+
+ /* accel + gyro */
+ if ((header & INV_ICM42607_FIFO_HEADER_ACCEL) &&
+ (header & INV_ICM42607_FIFO_HEADER_GYRO)) {
+ *accel = &pack2->accel;
+ *gyro = &pack2->gyro;
+ *temp = &pack2->temp;
+ *timestamp = &pack2->timestamp;
+ return INV_ICM42607_FIFO_2SENSORS_PACKET_SIZE;
+ }
+
+ /* accel only */
+ if (header & INV_ICM42607_FIFO_HEADER_ACCEL) {
+ *accel = &pack1->data;
+ *gyro = NULL;
+ *temp = &pack1->temp;
+ *timestamp = NULL;
+ return INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE;
+ }
+
+ /* gyro only */
+ if (header & INV_ICM42607_FIFO_HEADER_GYRO) {
+ *accel = NULL;
+ *gyro = &pack1->data;
+ *temp = &pack1->temp;
+ *timestamp = NULL;
+ return INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE;
+ }
+
+ /* invalid packet if here */
+ return -EINVAL;
+}
+
+void inv_icm42607_buffer_update_fifo_period(struct inv_icm42607_state *st)
+{
+ u32 period_gyro, period_accel;
+
+ if (st->fifo.en & INV_ICM42607_SENSOR_GYRO)
+ period_gyro = inv_icm42607_odr_to_period(st->conf.gyro.odr);
+ else
+ period_gyro = U32_MAX;
+
+ if (st->fifo.en & INV_ICM42607_SENSOR_ACCEL)
+ period_accel = inv_icm42607_odr_to_period(st->conf.accel.odr);
+ else
+ period_accel = U32_MAX;
+
+ st->fifo.period = min(period_gyro, period_accel);
+}
+
+int inv_icm42607_buffer_set_fifo_en(struct inv_icm42607_state *st,
+ unsigned int fifo_en)
+{
+ unsigned int val;
+ int ret;
+
+ if ((fifo_en & INV_ICM42607_SENSOR_GYRO) ||
+ (fifo_en & INV_ICM42607_SENSOR_ACCEL) ||
+ (fifo_en & INV_ICM42607_SENSOR_TEMP)) {
+ /* Enable FIFO mode and disable bypass mode if any sensor on */
+ val = FIELD_PREP(INV_ICM42607_FIFO_CONFIG1_MODE, 1);
+ val |= FIELD_PREP(INV_ICM42607_FIFO_CONFIG1_BYPASS, 0);
+ } else {
+ /* Disable FIFO mode and enable bypass mode if all sensor off */
+ val = FIELD_PREP(INV_ICM42607_FIFO_CONFIG1_MODE, 0);
+ val |= FIELD_PREP(INV_ICM42607_FIFO_CONFIG1_BYPASS, 1);
+ }
+
+ ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1, val);
+ if (ret)
+ return ret;
+
+ st->fifo.en = fifo_en;
+ inv_icm42607_buffer_update_fifo_period(st);
+
+ return 0;
+}
+
+static size_t inv_icm42607_get_packet_size(unsigned int fifo_en)
+{
+ size_t packet_size;
+
+ if ((fifo_en & INV_ICM42607_SENSOR_GYRO) &&
+ (fifo_en & INV_ICM42607_SENSOR_ACCEL))
+ packet_size = INV_ICM42607_FIFO_2SENSORS_PACKET_SIZE;
+ else
+ packet_size = INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE;
+
+ return packet_size;
+}
+
+static unsigned int inv_icm42607_wm_truncate(unsigned int watermark,
+ size_t packet_size)
+{
+ size_t wm_size;
+
+ wm_size = watermark * packet_size;
+ wm_size = min(wm_size, INV_ICM42607_FIFO_WATERMARK_MAX);
+
+ return wm_size / packet_size;
+}
+
+/**
+ * inv_icm42607_buffer_update_watermark - update watermark FIFO threshold
+ * @st: driver internal state
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int inv_icm42607_buffer_update_watermark(struct inv_icm42607_state *st)
+{
+ const struct device *dev = regmap_get_device(st->map);
+ unsigned int wm_gyro, wm_accel, watermark;
+ u32 latency_gyro, latency_accel, latency;
+ u32 period_gyro, period_accel;
+ size_t packet_size, wm_size;
+ __le16 raw_wm;
+ bool restore;
+ int ret;
+
+ packet_size = inv_icm42607_get_packet_size(st->fifo.en);
+
+ /* compute sensors latency, depending on sensor watermark and odr */
+ wm_gyro = inv_icm42607_wm_truncate(st->fifo.watermark.gyro, packet_size);
+ wm_accel = inv_icm42607_wm_truncate(st->fifo.watermark.accel, packet_size);
+ /* use us for odr to avoid overflow using 32 bits values */
+ period_gyro = inv_icm42607_odr_to_period(st->conf.gyro.odr) / 1000UL;
+ period_accel = inv_icm42607_odr_to_period(st->conf.accel.odr) / 1000UL;
+ latency_gyro = period_gyro * wm_gyro;
+ latency_accel = period_accel * wm_accel;
+
+ /* 0 value for watermark means that the sensor is turned off */
+ if (wm_gyro == 0 && wm_accel == 0)
+ return 0;
+
+ if (latency_gyro == 0) {
+ watermark = wm_accel;
+ st->fifo.watermark.eff_accel = wm_accel;
+ } else if (latency_accel == 0) {
+ watermark = wm_gyro;
+ st->fifo.watermark.eff_gyro = wm_gyro;
+ } else {
+ /* compute the smallest latency that is a multiple of both */
+ if (latency_gyro <= latency_accel)
+ latency = latency_gyro - (latency_accel % latency_gyro);
+ else
+ latency = latency_accel - (latency_gyro % latency_accel);
+ /* all this works because periods are multiple of each others */
+ watermark = latency / min(period_gyro, period_accel);
+ watermark = max(watermark, 1);
+ /* update effective watermark */
+ st->fifo.watermark.eff_gyro = max(latency / period_gyro, 1);
+ st->fifo.watermark.eff_accel = max(latency / period_accel, 1);
+ }
+
+ /* changing FIFO watermark requires to turn off watermark interrupt */
+ ret = regmap_update_bits_check(st->map, INV_ICM42607_REG_INT_SOURCE0,
+ INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN,
+ 0, &restore);
+ if (ret)
+ return ret;
+
+ /* compute watermark value in bytes */
+ wm_size = watermark * packet_size;
+ raw_wm = INV_ICM42607_FIFO_WATERMARK_VAL(wm_size);
+ memcpy(st->buffer, &raw_wm, sizeof(raw_wm));
+ ret = regmap_bulk_write(st->map, INV_ICM42607_REG_FIFO_CONFIG2,
+ st->buffer, sizeof(raw_wm));
+ if (ret) {
+ dev_err(dev, "Unable to change watermark value: %d\n", ret);
+ if (restore)
+ regmap_update_bits(st->map, INV_ICM42607_REG_INT_SOURCE0,
+ INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN,
+ INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN);
+ return ret;
+ }
+
+ /* restore watermark interrupt */
+ if (restore) {
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_INT_SOURCE0,
+ INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN,
+ INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int inv_icm42607_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_state *sensor_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &sensor_st->ts;
+ struct device *dev = regmap_get_device(st->map);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+ inv_sensors_timestamp_reset(ts);
+
+ return 0;
+}
+
+/*
+ * update_scan_mode callback is turning sensors on and setting data FIFO enable
+ * bits.
+ */
+static int inv_icm42607_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ /* exit if FIFO is already on */
+ if (st->fifo.on) {
+ st->fifo.on++;
+ return 0;
+ }
+
+ /* set FIFO threshold interrupt */
+ ret = regmap_set_bits(st->map, INV_ICM42607_REG_INT_SOURCE0,
+ INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN);
+ if (ret)
+ return ret;
+
+ /* flush FIFO data */
+ ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET,
+ INV_ICM42607_SIGNAL_PATH_RESET_FIFO_FLUSH);
+ if (ret)
+ return ret;
+
+ /* set FIFO in streaming mode */
+ ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1,
+ INV_ICM42607_FIFO_CONFIG1_MODE);
+ if (ret)
+ return ret;
+
+ /* workaround: first read of FIFO count after reset is always 0 */
+ ret = regmap_bulk_read(st->map, INV_ICM42607_REG_FIFO_COUNTH,
+ st->buffer, sizeof(__be16));
+ if (ret)
+ return ret;
+
+ st->fifo.on++;
+
+ return 0;
+}
+
+static int inv_icm42607_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ if (st->fifo.on > 1) {
+ st->fifo.on--;
+ return 0;
+ }
+
+ /* Set FIFO to 0 since iio core ignores teardown errors. */
+ st->fifo.on = 0;
+
+ /* set FIFO in bypass mode */
+ ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1,
+ INV_ICM42607_FIFO_CONFIG1_BYPASS);
+ if (ret)
+ return ret;
+
+ /* flush FIFO data */
+ ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET,
+ INV_ICM42607_SIGNAL_PATH_RESET_FIFO_FLUSH);
+ if (ret)
+ return ret;
+
+ /* disable FIFO threshold interrupt */
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_INT_SOURCE0,
+ INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int inv_icm42607_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int sensor;
+ unsigned int *watermark;
+ int ret;
+
+ if (indio_dev == st->indio_gyro) {
+ sensor = INV_ICM42607_SENSOR_GYRO;
+ watermark = &st->fifo.watermark.gyro;
+ } else if (indio_dev == st->indio_accel) {
+ sensor = INV_ICM42607_SENSOR_ACCEL;
+ watermark = &st->fifo.watermark.accel;
+ } else {
+ return -EINVAL;
+ }
+
+ mutex_lock(&st->lock);
+
+ /*
+ * FIFO enabled at update scan mode for accel or gyro, and
+ * disabled here.
+ */
+ ret = inv_icm42607_buffer_set_fifo_en(st, st->fifo.en & ~sensor);
+ if (ret)
+ goto out_unlock;
+
+ *watermark = 0;
+ ret = inv_icm42607_buffer_update_watermark(st);
+ if (ret)
+ goto out_unlock;
+
+out_unlock:
+ mutex_unlock(&st->lock);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+const struct iio_buffer_setup_ops inv_icm42607_buffer_ops = {
+ .preenable = inv_icm42607_buffer_preenable,
+ .postenable = inv_icm42607_buffer_postenable,
+ .predisable = inv_icm42607_buffer_predisable,
+ .postdisable = inv_icm42607_buffer_postdisable,
+};
+
+int inv_icm42607_buffer_fifo_read(struct inv_icm42607_state *st,
+ unsigned int max)
+{
+ const void *accel, *gyro, *timestamp;
+ size_t i, max_count;
+ const s8 *temp;
+ ssize_t size;
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ /* reset all samples counters */
+ st->fifo.count = 0;
+ st->fifo.nb.gyro = 0;
+ st->fifo.nb.accel = 0;
+ st->fifo.nb.total = 0;
+
+ /* compute maximum FIFO read size */
+ if (max == 0)
+ max_count = sizeof(st->fifo.data);
+ else
+ max_count = min((max * inv_icm42607_get_packet_size(st->fifo.en)),
+ sizeof(st->fifo.data));
+
+ /* read FIFO count value */
+ ret = regmap_bulk_read(st->map, INV_ICM42607_REG_FIFO_COUNTH,
+ st->buffer, sizeof(u8) * 2);
+ if (ret)
+ return ret;
+ st->fifo.count = be16_to_cpup(st->buffer);
+
+ /* check and clamp FIFO count value */
+ if (st->fifo.count == 0)
+ return 0;
+
+ st->fifo.count = min(st->fifo.count, max_count);
+
+ /* read all FIFO data in internal buffer */
+ ret = regmap_noinc_read(st->map, INV_ICM42607_REG_FIFO_DATA,
+ st->fifo.data, st->fifo.count);
+ if (ret)
+ return ret;
+
+ /* compute number of samples for each sensor */
+ for (i = 0; i < st->fifo.count; i += size) {
+ size = inv_icm42607_fifo_decode_packet(&st->fifo.data[i],
+ &accel, &gyro, &temp, ×tamp);
+ /* Make sure the size is at least 1 valid packet. */
+ if (size < INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE)
+ break;
+ /* Error if we are going to overflow the buffer. */
+ if (i + size > st->fifo.count)
+ return -EIO;
+ if (gyro != NULL && inv_icm42607_fifo_is_data_valid(gyro))
+ st->fifo.nb.gyro++;
+ if (accel != NULL && inv_icm42607_fifo_is_data_valid(accel))
+ st->fifo.nb.accel++;
+ st->fifo.nb.total++;
+ }
+
+ return 0;
+}
+
+int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
+ unsigned int count)
+{
+ int ret;
+
+ ret = inv_icm42607_buffer_fifo_read(st, count);
+
+ return ret;
+}
+
+int inv_icm42607_buffer_init(struct inv_icm42607_state *st)
+{
+ int ret;
+
+ st->fifo.watermark.eff_gyro = 1;
+ st->fifo.watermark.eff_accel = 1;
+
+ /* Configure FIFO_COUNT format in bytes and big endian */
+ ret = regmap_set_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
+ INV_ICM42607_INTF_CONFIG0_FIFO_COUNT_ENDIAN);
+ if (ret)
+ return ret;
+
+ /* Initialize FIFO in bypass mode */
+ return regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1,
+ INV_ICM42607_FIFO_CONFIG1_BYPASS);
+}
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
new file mode 100644
index 000000000000..b77deb66f8bd
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#ifndef INV_ICM42607_BUFFER_H_
+#define INV_ICM42607_BUFFER_H_
+
+#include <linux/bitops.h>
+
+struct inv_icm42607_state;
+
+#define INV_ICM42607_SENSOR_GYRO BIT(0)
+#define INV_ICM42607_SENSOR_ACCEL BIT(1)
+#define INV_ICM42607_SENSOR_TEMP BIT(2)
+
+/**
+ * struct inv_icm42607_fifo - FIFO state variables
+ * @on: reference counter for FIFO on.
+ * @en: bits field of INV_ICM42607_SENSOR_* for FIFO EN bits.
+ * @period: FIFO internal period.
+ * @watermark: watermark configuration values for accel and gyro.
+ * @count: number of bytes in the FIFO data buffer.
+ * @nb: gyro, accel and total samples in the FIFO data buffer.
+ * @data: FIFO data buffer aligned for DMA (2kB + 32 bytes of read cache).
+ */
+struct inv_icm42607_fifo {
+ unsigned int on;
+ unsigned int en;
+ u32 period;
+ struct {
+ unsigned int gyro;
+ unsigned int accel;
+ unsigned int eff_gyro;
+ unsigned int eff_accel;
+ } watermark;
+ size_t count;
+ struct {
+ size_t gyro;
+ size_t accel;
+ size_t total;
+ } nb;
+ u8 data[2080] __aligned(IIO_DMA_MINALIGN);
+};
+
+/* FIFO data packet */
+struct inv_icm42607_fifo_sensor_data {
+ __be16 x;
+ __be16 y;
+ __be16 z;
+};
+
+#define INV_ICM42607_FIFO_DATA_INVALID -32768
+
+static inline bool
+inv_icm42607_fifo_is_data_valid(const struct inv_icm42607_fifo_sensor_data *s)
+{
+ s16 x, y, z;
+
+ x = be16_to_cpu(s->x);
+ y = be16_to_cpu(s->y);
+ z = be16_to_cpu(s->z);
+
+ if (x == INV_ICM42607_FIFO_DATA_INVALID &&
+ y == INV_ICM42607_FIFO_DATA_INVALID &&
+ z == INV_ICM42607_FIFO_DATA_INVALID)
+ return false;
+
+ return true;
+}
+
+ssize_t inv_icm42607_fifo_decode_packet(const void *packet, const void **accel,
+ const void **gyro, const s8 **temp,
+ const void **timestamp);
+
+extern const struct iio_buffer_setup_ops inv_icm42607_buffer_ops;
+
+int inv_icm42607_buffer_init(struct inv_icm42607_state *st);
+
+void inv_icm42607_buffer_update_fifo_period(struct inv_icm42607_state *st);
+
+int inv_icm42607_buffer_set_fifo_en(struct inv_icm42607_state *st,
+ unsigned int fifo_en);
+
+int inv_icm42607_buffer_update_watermark(struct inv_icm42607_state *st);
+
+int inv_icm42607_buffer_fifo_read(struct inv_icm42607_state *st,
+ unsigned int max);
+
+int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
+ unsigned int count);
+
+#endif
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
index cfdce70ce25d..16ead03a59f9 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -16,6 +16,7 @@
#include <linux/regulator/consumer.h>
#include "inv_icm42607.h"
+#include "inv_icm42607_buffer.h"
static bool inv_icm42607_is_volatile_reg(struct device *dev, unsigned int reg)
{
@@ -74,6 +75,38 @@ const struct inv_icm42607_hw inv_icm42607p_hw_data = {
};
EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607");
+u32 inv_icm42607_odr_to_period(enum inv_icm42607_odr odr)
+{
+ static const u32 odr_periods[INV_ICM42607_ODR_NB] = {
+ /* 1600Hz */
+ [INV_ICM42607_ODR_1600HZ] = 625000,
+ /* 800Hz */
+ 1250000,
+ /* 400Hz */
+ 2500000,
+ /* 200Hz */
+ 5000000,
+ /* 100 Hz */
+ 10000000,
+ /* 50Hz */
+ 20000000,
+ /* 25Hz */
+ 40000000,
+ /* 12.5Hz */
+ 80000000,
+ /* 6.25Hz */
+ 160000000,
+ /* 3.125Hz */
+ 320000000,
+ /* 1.5625Hz */
+ 640000000,
+ };
+
+ odr = clamp(odr, INV_ICM42607_ODR_1600HZ, INV_ICM42607_ODR_1_5625HZ_LP);
+
+ return odr_periods[odr];
+}
+
static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st,
enum inv_icm42607_sensor_mode gyro,
enum inv_icm42607_sensor_mode accel,
@@ -309,6 +342,11 @@ int inv_icm42607_core_probe(struct regmap *regmap,
if (ret)
return ret;
+ /* Initialize buffer/FIFO handling */
+ ret = inv_icm42607_buffer_init(st);
+ if (ret)
+ return ret;
+
ret = devm_pm_runtime_set_active_enabled(dev);
if (ret)
return ret;
@@ -334,6 +372,13 @@ static int inv_icm42607_suspend(struct device *dev)
if (ret)
return ret;
+ if (st->fifo.on) {
+ ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1,
+ INV_ICM42607_FIFO_CONFIG1_BYPASS);
+ if (ret)
+ return ret;
+ }
+
ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
INV_ICM42607_SENSOR_MODE_OFF,
false, NULL);
@@ -363,7 +408,12 @@ static int inv_icm42607_resume(struct device *dev)
if (ret)
return ret;
- /* Nothing else to restore at this time. */
+ if (st->fifo.on) {
+ ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1,
+ INV_ICM42607_FIFO_CONFIG1_MODE);
+ if (ret)
+ return ret;
+ }
return 0;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH V9 06/11] iio: imu: inv_icm42607: Add Buffer support for icm42607
2026-05-30 3:17 ` [PATCH V9 06/11] iio: imu: inv_icm42607: Add Buffer " Chris Morgan
@ 2026-05-31 12:38 ` Jonathan Cameron
2026-06-01 13:50 ` Chris Morgan
0 siblings, 1 reply; 26+ messages in thread
From: Jonathan Cameron @ 2026-05-31 12:38 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-iio, andy, nuno.sa, dlechner, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
On Fri, 29 May 2026 22:17:33 -0500
Chris Morgan <macroalpha82@gmail.com> wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add all FIFO parsing and reading functions to support
> inv_icm42607 hardware.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
A few things inline.
J
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> new file mode 100644
> index 000000000000..e065d60ac119
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> @@ -0,0 +1,483 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2026 InvenSense, Inc.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/minmax.h>
> +#include <linux/mutex.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/unaligned.h>
> +
> +#include <linux/iio/common/inv_sensors_timestamp.h>
> +
> +#include "inv_icm42607.h"
> +#include "inv_icm42607_buffer.h"
> +
> +/* FIFO header: 1 byte */
> +#define INV_ICM42607_FIFO_HEADER_MSG BIT(7)
> +#define INV_ICM42607_FIFO_HEADER_ACCEL BIT(6)
> +#define INV_ICM42607_FIFO_HEADER_GYRO BIT(5)
> +#define INV_ICM42607_FIFO_HEADER_TMST_FSYNC GENMASK(3, 2)
> +#define INV_ICM42607_FIFO_HEADER_ODR_ACCEL BIT(1)
> +#define INV_ICM42607_FIFO_HEADER_ODR_GYRO BIT(0)
> +
> +struct inv_icm42607_fifo_1sensor_packet {
> + u8 header;
> + struct inv_icm42607_fifo_sensor_data data;
> + s8 temp;
> +} __packed;
> +
> +struct inv_icm42607_fifo_2sensors_packet {
> + u8 header;
> + struct inv_icm42607_fifo_sensor_data accel;
Good example of the need for the inner structures to be packed
> + struct inv_icm42607_fifo_sensor_data gyro;
> + s8 temp;
> + __be16 timestamp;
> +} __packed;
> +
> +ssize_t inv_icm42607_fifo_decode_packet(const void *packet, const void **accel,
> + const void **gyro, const int8_t **temp,
> + const void **timestamp)
> +{
> + const struct inv_icm42607_fifo_1sensor_packet *pack1 = get_unaligned(&packet);
> + const struct inv_icm42607_fifo_2sensors_packet *pack2 = get_unaligned(&packet);
Hmm. Sashiko points out you are messing around with pointers here and it's not those but
what the point to that we need to worry about alignment for.
You could memcpy that data into local structures.
> + u8 header = *((const u8 *)packet);
> +
> + /* FIFO empty */
> + if (header & INV_ICM42607_FIFO_HEADER_MSG) {
> + *accel = NULL;
> + *gyro = NULL;
> + *temp = NULL;
> + *timestamp = NULL;
> + return 0;
> + }
> +
> + /* accel + gyro */
> + if ((header & INV_ICM42607_FIFO_HEADER_ACCEL) &&
> + (header & INV_ICM42607_FIFO_HEADER_GYRO)) {
> + *accel = &pack2->accel;
> + *gyro = &pack2->gyro;
> + *temp = &pack2->temp;
> + *timestamp = &pack2->timestamp;
> + return INV_ICM42607_FIFO_2SENSORS_PACKET_SIZE;
> + }
> +
> + /* accel only */
> + if (header & INV_ICM42607_FIFO_HEADER_ACCEL) {
> + *accel = &pack1->data;
> + *gyro = NULL;
> + *temp = &pack1->temp;
> + *timestamp = NULL;
> + return INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE;
> + }
> +
> + /* gyro only */
> + if (header & INV_ICM42607_FIFO_HEADER_GYRO) {
> + *accel = NULL;
> + *gyro = &pack1->data;
> + *temp = &pack1->temp;
> + *timestamp = NULL;
> + return INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE;
> + }
> +
> + /* invalid packet if here */
> + return -EINVAL;
> +}
> +/**
> + * inv_icm42607_buffer_update_watermark - update watermark FIFO threshold
> + * @st: driver internal state
> + *
> + * Returns 0 on success, a negative error code otherwise.
> + */
> +int inv_icm42607_buffer_update_watermark(struct inv_icm42607_state *st)
> +{
> + const struct device *dev = regmap_get_device(st->map);
> + unsigned int wm_gyro, wm_accel, watermark;
> + u32 latency_gyro, latency_accel, latency;
> + u32 period_gyro, period_accel;
> + size_t packet_size, wm_size;
> + __le16 raw_wm;
> + bool restore;
> + int ret;
> +
> + packet_size = inv_icm42607_get_packet_size(st->fifo.en);
> +
> + /* compute sensors latency, depending on sensor watermark and odr */
> + wm_gyro = inv_icm42607_wm_truncate(st->fifo.watermark.gyro, packet_size);
> + wm_accel = inv_icm42607_wm_truncate(st->fifo.watermark.accel, packet_size);
> + /* use us for odr to avoid overflow using 32 bits values */
> + period_gyro = inv_icm42607_odr_to_period(st->conf.gyro.odr) / 1000UL;
> + period_accel = inv_icm42607_odr_to_period(st->conf.accel.odr) / 1000UL;
> + latency_gyro = period_gyro * wm_gyro;
> + latency_accel = period_accel * wm_accel;
> +
> + /* 0 value for watermark means that the sensor is turned off */
> + if (wm_gyro == 0 && wm_accel == 0)
> + return 0;
> +
> + if (latency_gyro == 0) {
> + watermark = wm_accel;
> + st->fifo.watermark.eff_accel = wm_accel;
> + } else if (latency_accel == 0) {
> + watermark = wm_gyro;
> + st->fifo.watermark.eff_gyro = wm_gyro;
> + } else {
> + /* compute the smallest latency that is a multiple of both */
> + if (latency_gyro <= latency_accel)
> + latency = latency_gyro - (latency_accel % latency_gyro);
> + else
> + latency = latency_accel - (latency_gyro % latency_accel);
> + /* all this works because periods are multiple of each others */
> + watermark = latency / min(period_gyro, period_accel);
> + watermark = max(watermark, 1);
> + /* update effective watermark */
> + st->fifo.watermark.eff_gyro = max(latency / period_gyro, 1);
> + st->fifo.watermark.eff_accel = max(latency / period_accel, 1);
> + }
> +
> + /* changing FIFO watermark requires to turn off watermark interrupt */
> + ret = regmap_update_bits_check(st->map, INV_ICM42607_REG_INT_SOURCE0,
> + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN,
> + 0, &restore);
> + if (ret)
> + return ret;
> +
> + /* compute watermark value in bytes */
> + wm_size = watermark * packet_size;
> + raw_wm = INV_ICM42607_FIFO_WATERMARK_VAL(wm_size);
> + memcpy(st->buffer, &raw_wm, sizeof(raw_wm));
> + ret = regmap_bulk_write(st->map, INV_ICM42607_REG_FIFO_CONFIG2,
> + st->buffer, sizeof(raw_wm));
> + if (ret) {
> + dev_err(dev, "Unable to change watermark value: %d\n", ret);
> + if (restore)
> + regmap_update_bits(st->map, INV_ICM42607_REG_INT_SOURCE0,
> + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN,
> + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN);
set_bits.
> + return ret;
> + }
> +
> + /* restore watermark interrupt */
> + if (restore) {
> + ret = regmap_update_bits(st->map, INV_ICM42607_REG_INT_SOURCE0,
> + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN,
> + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN);
set_bits
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int inv_icm42607_buffer_predisable(struct iio_dev *indio_dev)
> +{
> + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
> + int ret;
> +
> + guard(mutex)(&st->lock);
> +
> + if (st->fifo.on > 1) {
> + st->fifo.on--;
> + return 0;
> + }
> +
> + /* Set FIFO to 0 since iio core ignores teardown errors. */
> + st->fifo.on = 0;
> +
> + /* set FIFO in bypass mode */
> + ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1,
> + INV_ICM42607_FIFO_CONFIG1_BYPASS);
> + if (ret)
> + return ret;
> +
> + /* flush FIFO data */
> + ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET,
> + INV_ICM42607_SIGNAL_PATH_RESET_FIFO_FLUSH);
> + if (ret)
> + return ret;
> +
> + /* disable FIFO threshold interrupt */
> + ret = regmap_update_bits(st->map, INV_ICM42607_REG_INT_SOURCE0,
> + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN, 0);
regmap_clear_bits()
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int inv_icm42607_buffer_postdisable(struct iio_dev *indio_dev)
> +{
> + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
> + struct device *dev = regmap_get_device(st->map);
> + unsigned int sensor;
> + unsigned int *watermark;
> + int ret;
> +
> + if (indio_dev == st->indio_gyro) {
> + sensor = INV_ICM42607_SENSOR_GYRO;
> + watermark = &st->fifo.watermark.gyro;
> + } else if (indio_dev == st->indio_accel) {
> + sensor = INV_ICM42607_SENSOR_ACCEL;
> + watermark = &st->fifo.watermark.accel;
> + } else {
> + return -EINVAL;
> + }
> +
> + mutex_lock(&st->lock);
> +
> + /*
> + * FIFO enabled at update scan mode for accel or gyro, and
> + * disabled here.
> + */
> + ret = inv_icm42607_buffer_set_fifo_en(st, st->fifo.en & ~sensor);
> + if (ret)
> + goto out_unlock;
> +
> + *watermark = 0;
Add a comment on why this needs to be set to 0. Normally that only
matters at all if the fifo is on, so I guess something unusual here?
> + ret = inv_icm42607_buffer_update_watermark(st);
> + if (ret)
> + goto out_unlock;
> +
> +out_unlock:
> + mutex_unlock(&st->lock);
> +
> + pm_runtime_put_autosuspend(dev);
> +
> + return ret;
> +}
> +int inv_icm42607_buffer_fifo_read(struct inv_icm42607_state *st,
> + unsigned int max)
> +{
> + const void *accel, *gyro, *timestamp;
> + size_t i, max_count;
> + const s8 *temp;
> + ssize_t size;
> + int ret;
> +
> + guard(mutex)(&st->lock);
> +
> + /* reset all samples counters */
> + st->fifo.count = 0;
> + st->fifo.nb.gyro = 0;
> + st->fifo.nb.accel = 0;
> + st->fifo.nb.total = 0;
> +
> + /* compute maximum FIFO read size */
> + if (max == 0)
> + max_count = sizeof(st->fifo.data);
> + else
> + max_count = min((max * inv_icm42607_get_packet_size(st->fifo.en)),
> + sizeof(st->fifo.data));
> +
> + /* read FIFO count value */
> + ret = regmap_bulk_read(st->map, INV_ICM42607_REG_FIFO_COUNTH,
> + st->buffer, sizeof(u8) * 2);
> + if (ret)
> + return ret;
> + st->fifo.count = be16_to_cpup(st->buffer);
Might be ok to assume this is always a multiple of the scan size, but
maybe sanity check it to keep sashiko happy and remove that assumption
of atomic update.
> +
> + /* check and clamp FIFO count value */
> + if (st->fifo.count == 0)
> + return 0;
> +
> + st->fifo.count = min(st->fifo.count, max_count);
> +
> + /* read all FIFO data in internal buffer */
> + ret = regmap_noinc_read(st->map, INV_ICM42607_REG_FIFO_DATA,
> + st->fifo.data, st->fifo.count);
> + if (ret)
> + return ret;
> +
> + /* compute number of samples for each sensor */
> + for (i = 0; i < st->fifo.count; i += size) {
> + size = inv_icm42607_fifo_decode_packet(&st->fifo.data[i],
> + &accel, &gyro, &temp, ×tamp);
> + /* Make sure the size is at least 1 valid packet. */
> + if (size < INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE)
> + break;
> + /* Error if we are going to overflow the buffer. */
> + if (i + size > st->fifo.count)
> + return -EIO;
> + if (gyro != NULL && inv_icm42607_fifo_is_data_valid(gyro))
> + st->fifo.nb.gyro++;
> + if (accel != NULL && inv_icm42607_fifo_is_data_valid(accel))
> + st->fifo.nb.accel++;
> + st->fifo.nb.total++;
> + }
> +
> + return 0;
> +}
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
> new file mode 100644
> index 000000000000..b77deb66f8bd
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
> +
> +/* FIFO data packet */
> +struct inv_icm42607_fifo_sensor_data {
> + __be16 x;
> + __be16 y;
> + __be16 z;
> +};
Sashiko is probably correct that this should be packed.
Makes not difference here but it's not aligned in the places it's embedded
in other structs and the compiler seeing this will assume it is aligned.
> +
> +#define INV_ICM42607_FIFO_DATA_INVALID -32768
> +
> +static inline bool
> +inv_icm42607_fifo_is_data_valid(const struct inv_icm42607_fifo_sensor_data *s)
> +{
> + s16 x, y, z;
> +
> + x = be16_to_cpu(s->x);
> + y = be16_to_cpu(s->y);
> + z = be16_to_cpu(s->z);
> +
> + if (x == INV_ICM42607_FIFO_DATA_INVALID &&
> + y == INV_ICM42607_FIFO_DATA_INVALID &&
> + z == INV_ICM42607_FIFO_DATA_INVALID)
> + return false;
> +
> + return true;
> +}
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH V9 06/11] iio: imu: inv_icm42607: Add Buffer support for icm42607
2026-05-31 12:38 ` Jonathan Cameron
@ 2026-06-01 13:50 ` Chris Morgan
2026-06-01 14:36 ` Jonathan Cameron
0 siblings, 1 reply; 26+ messages in thread
From: Chris Morgan @ 2026-06-01 13:50 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Chris Morgan, linux-iio, andy, nuno.sa, dlechner,
jean-baptiste.maneyrol, linux-rockchip, devicetree, heiko,
conor+dt, krzk+dt, robh, andriy.shevchenko
On Sun, May 31, 2026 at 01:38:01PM +0100, Jonathan Cameron wrote:
> On Fri, 29 May 2026 22:17:33 -0500
> Chris Morgan <macroalpha82@gmail.com> wrote:
>
> > From: Chris Morgan <macromorgan@hotmail.com>
> >
> > Add all FIFO parsing and reading functions to support
> > inv_icm42607 hardware.
> >
> > Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
> A few things inline.
>
> J
>
> > diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> > new file mode 100644
> > index 000000000000..e065d60ac119
> > --- /dev/null
> > +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> > @@ -0,0 +1,483 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * Copyright (C) 2026 InvenSense, Inc.
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/iio/buffer.h>
> > +#include <linux/iio/iio.h>
> > +#include <linux/minmax.h>
> > +#include <linux/mutex.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <linux/unaligned.h>
> > +
> > +#include <linux/iio/common/inv_sensors_timestamp.h>
> > +
> > +#include "inv_icm42607.h"
> > +#include "inv_icm42607_buffer.h"
> > +
> > +/* FIFO header: 1 byte */
> > +#define INV_ICM42607_FIFO_HEADER_MSG BIT(7)
> > +#define INV_ICM42607_FIFO_HEADER_ACCEL BIT(6)
> > +#define INV_ICM42607_FIFO_HEADER_GYRO BIT(5)
> > +#define INV_ICM42607_FIFO_HEADER_TMST_FSYNC GENMASK(3, 2)
> > +#define INV_ICM42607_FIFO_HEADER_ODR_ACCEL BIT(1)
> > +#define INV_ICM42607_FIFO_HEADER_ODR_GYRO BIT(0)
> > +
> > +struct inv_icm42607_fifo_1sensor_packet {
> > + u8 header;
> > + struct inv_icm42607_fifo_sensor_data data;
> > + s8 temp;
> > +} __packed;
> > +
> > +struct inv_icm42607_fifo_2sensors_packet {
> > + u8 header;
> > + struct inv_icm42607_fifo_sensor_data accel;
> Good example of the need for the inner structures to be packed
Are you saying I should set the inv_icm42607_fifo_sensor_data to be
__packed? I think I was told in a previous patch not to do that, but
I can add it back.
>
> > + struct inv_icm42607_fifo_sensor_data gyro;
> > + s8 temp;
> > + __be16 timestamp;
> > +} __packed;
> > +
> > +ssize_t inv_icm42607_fifo_decode_packet(const void *packet, const void **accel,
> > + const void **gyro, const int8_t **temp,
> > + const void **timestamp)
> > +{
> > + const struct inv_icm42607_fifo_1sensor_packet *pack1 = get_unaligned(&packet);
> > + const struct inv_icm42607_fifo_2sensors_packet *pack2 = get_unaligned(&packet);
>
> Hmm. Sashiko points out you are messing around with pointers here and it's not those but
> what the point to that we need to worry about alignment for.
> You could memcpy that data into local structures.
I still suck at pointers, sorry. What exactly would you recommend here?
memcpy the packet value into an allocated
inv_icm42607_fifo_1sensor_packet or inv_icm42607_fifo_2sensors_packet?
>
>
> > + u8 header = *((const u8 *)packet);
> > +
> > + /* FIFO empty */
> > + if (header & INV_ICM42607_FIFO_HEADER_MSG) {
> > + *accel = NULL;
> > + *gyro = NULL;
> > + *temp = NULL;
> > + *timestamp = NULL;
> > + return 0;
> > + }
> > +
> > + /* accel + gyro */
> > + if ((header & INV_ICM42607_FIFO_HEADER_ACCEL) &&
> > + (header & INV_ICM42607_FIFO_HEADER_GYRO)) {
> > + *accel = &pack2->accel;
> > + *gyro = &pack2->gyro;
> > + *temp = &pack2->temp;
> > + *timestamp = &pack2->timestamp;
> > + return INV_ICM42607_FIFO_2SENSORS_PACKET_SIZE;
> > + }
> > +
> > + /* accel only */
> > + if (header & INV_ICM42607_FIFO_HEADER_ACCEL) {
> > + *accel = &pack1->data;
> > + *gyro = NULL;
> > + *temp = &pack1->temp;
> > + *timestamp = NULL;
> > + return INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE;
> > + }
> > +
> > + /* gyro only */
> > + if (header & INV_ICM42607_FIFO_HEADER_GYRO) {
> > + *accel = NULL;
> > + *gyro = &pack1->data;
> > + *temp = &pack1->temp;
> > + *timestamp = NULL;
> > + return INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE;
> > + }
> > +
> > + /* invalid packet if here */
> > + return -EINVAL;
> > +}
>
> > +/**
> > + * inv_icm42607_buffer_update_watermark - update watermark FIFO threshold
> > + * @st: driver internal state
> > + *
> > + * Returns 0 on success, a negative error code otherwise.
> > + */
> > +int inv_icm42607_buffer_update_watermark(struct inv_icm42607_state *st)
> > +{
> > + const struct device *dev = regmap_get_device(st->map);
> > + unsigned int wm_gyro, wm_accel, watermark;
> > + u32 latency_gyro, latency_accel, latency;
> > + u32 period_gyro, period_accel;
> > + size_t packet_size, wm_size;
> > + __le16 raw_wm;
> > + bool restore;
> > + int ret;
> > +
> > + packet_size = inv_icm42607_get_packet_size(st->fifo.en);
> > +
> > + /* compute sensors latency, depending on sensor watermark and odr */
> > + wm_gyro = inv_icm42607_wm_truncate(st->fifo.watermark.gyro, packet_size);
> > + wm_accel = inv_icm42607_wm_truncate(st->fifo.watermark.accel, packet_size);
> > + /* use us for odr to avoid overflow using 32 bits values */
> > + period_gyro = inv_icm42607_odr_to_period(st->conf.gyro.odr) / 1000UL;
> > + period_accel = inv_icm42607_odr_to_period(st->conf.accel.odr) / 1000UL;
> > + latency_gyro = period_gyro * wm_gyro;
> > + latency_accel = period_accel * wm_accel;
> > +
> > + /* 0 value for watermark means that the sensor is turned off */
> > + if (wm_gyro == 0 && wm_accel == 0)
> > + return 0;
> > +
> > + if (latency_gyro == 0) {
> > + watermark = wm_accel;
> > + st->fifo.watermark.eff_accel = wm_accel;
> > + } else if (latency_accel == 0) {
> > + watermark = wm_gyro;
> > + st->fifo.watermark.eff_gyro = wm_gyro;
> > + } else {
> > + /* compute the smallest latency that is a multiple of both */
> > + if (latency_gyro <= latency_accel)
> > + latency = latency_gyro - (latency_accel % latency_gyro);
> > + else
> > + latency = latency_accel - (latency_gyro % latency_accel);
> > + /* all this works because periods are multiple of each others */
> > + watermark = latency / min(period_gyro, period_accel);
> > + watermark = max(watermark, 1);
> > + /* update effective watermark */
> > + st->fifo.watermark.eff_gyro = max(latency / period_gyro, 1);
> > + st->fifo.watermark.eff_accel = max(latency / period_accel, 1);
> > + }
> > +
> > + /* changing FIFO watermark requires to turn off watermark interrupt */
> > + ret = regmap_update_bits_check(st->map, INV_ICM42607_REG_INT_SOURCE0,
> > + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN,
> > + 0, &restore);
> > + if (ret)
> > + return ret;
> > +
> > + /* compute watermark value in bytes */
> > + wm_size = watermark * packet_size;
> > + raw_wm = INV_ICM42607_FIFO_WATERMARK_VAL(wm_size);
> > + memcpy(st->buffer, &raw_wm, sizeof(raw_wm));
> > + ret = regmap_bulk_write(st->map, INV_ICM42607_REG_FIFO_CONFIG2,
> > + st->buffer, sizeof(raw_wm));
> > + if (ret) {
> > + dev_err(dev, "Unable to change watermark value: %d\n", ret);
> > + if (restore)
> > + regmap_update_bits(st->map, INV_ICM42607_REG_INT_SOURCE0,
> > + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN,
> > + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN);
>
> set_bits.
>
> > + return ret;
> > + }
> > +
> > + /* restore watermark interrupt */
> > + if (restore) {
> > + ret = regmap_update_bits(st->map, INV_ICM42607_REG_INT_SOURCE0,
> > + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN,
> > + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN);
>
> set_bits
>
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
>
> > +
> > +static int inv_icm42607_buffer_predisable(struct iio_dev *indio_dev)
> > +{
> > + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
> > + int ret;
> > +
> > + guard(mutex)(&st->lock);
> > +
> > + if (st->fifo.on > 1) {
> > + st->fifo.on--;
> > + return 0;
> > + }
> > +
> > + /* Set FIFO to 0 since iio core ignores teardown errors. */
> > + st->fifo.on = 0;
> > +
> > + /* set FIFO in bypass mode */
> > + ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1,
> > + INV_ICM42607_FIFO_CONFIG1_BYPASS);
> > + if (ret)
> > + return ret;
> > +
> > + /* flush FIFO data */
> > + ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET,
> > + INV_ICM42607_SIGNAL_PATH_RESET_FIFO_FLUSH);
> > + if (ret)
> > + return ret;
> > +
> > + /* disable FIFO threshold interrupt */
> > + ret = regmap_update_bits(st->map, INV_ICM42607_REG_INT_SOURCE0,
> > + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN, 0);
>
> regmap_clear_bits()
>
> > + if (ret)
> > + return ret;
> > +
> > + return 0;
> > +}
> > +
> > +static int inv_icm42607_buffer_postdisable(struct iio_dev *indio_dev)
> > +{
> > + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
> > + struct device *dev = regmap_get_device(st->map);
> > + unsigned int sensor;
> > + unsigned int *watermark;
> > + int ret;
> > +
> > + if (indio_dev == st->indio_gyro) {
> > + sensor = INV_ICM42607_SENSOR_GYRO;
> > + watermark = &st->fifo.watermark.gyro;
> > + } else if (indio_dev == st->indio_accel) {
> > + sensor = INV_ICM42607_SENSOR_ACCEL;
> > + watermark = &st->fifo.watermark.accel;
> > + } else {
> > + return -EINVAL;
> > + }
> > +
> > + mutex_lock(&st->lock);
> > +
> > + /*
> > + * FIFO enabled at update scan mode for accel or gyro, and
> > + * disabled here.
> > + */
> > + ret = inv_icm42607_buffer_set_fifo_en(st, st->fifo.en & ~sensor);
> > + if (ret)
> > + goto out_unlock;
> > +
> > + *watermark = 0;
>
> Add a comment on why this needs to be set to 0. Normally that only
> matters at all if the fifo is on, so I guess something unusual here?
I think it's just resetting the watermark to 0 when shutting down,
not 100% sure though.
>
>
> > + ret = inv_icm42607_buffer_update_watermark(st);
> > + if (ret)
> > + goto out_unlock;
> > +
> > +out_unlock:
> > + mutex_unlock(&st->lock);
> > +
> > + pm_runtime_put_autosuspend(dev);
> > +
> > + return ret;
> > +}
>
> > +int inv_icm42607_buffer_fifo_read(struct inv_icm42607_state *st,
> > + unsigned int max)
> > +{
> > + const void *accel, *gyro, *timestamp;
> > + size_t i, max_count;
> > + const s8 *temp;
> > + ssize_t size;
> > + int ret;
> > +
> > + guard(mutex)(&st->lock);
> > +
> > + /* reset all samples counters */
> > + st->fifo.count = 0;
> > + st->fifo.nb.gyro = 0;
> > + st->fifo.nb.accel = 0;
> > + st->fifo.nb.total = 0;
> > +
> > + /* compute maximum FIFO read size */
> > + if (max == 0)
> > + max_count = sizeof(st->fifo.data);
> > + else
> > + max_count = min((max * inv_icm42607_get_packet_size(st->fifo.en)),
> > + sizeof(st->fifo.data));
> > +
> > + /* read FIFO count value */
> > + ret = regmap_bulk_read(st->map, INV_ICM42607_REG_FIFO_COUNTH,
> > + st->buffer, sizeof(u8) * 2);
> > + if (ret)
> > + return ret;
> > + st->fifo.count = be16_to_cpup(st->buffer);
>
> Might be ok to assume this is always a multiple of the scan size, but
> maybe sanity check it to keep sashiko happy and remove that assumption
> of atomic update.
>
> > +
> > + /* check and clamp FIFO count value */
> > + if (st->fifo.count == 0)
> > + return 0;
> > +
> > + st->fifo.count = min(st->fifo.count, max_count);
> > +
> > + /* read all FIFO data in internal buffer */
> > + ret = regmap_noinc_read(st->map, INV_ICM42607_REG_FIFO_DATA,
> > + st->fifo.data, st->fifo.count);
> > + if (ret)
> > + return ret;
> > +
> > + /* compute number of samples for each sensor */
> > + for (i = 0; i < st->fifo.count; i += size) {
> > + size = inv_icm42607_fifo_decode_packet(&st->fifo.data[i],
> > + &accel, &gyro, &temp, ×tamp);
> > + /* Make sure the size is at least 1 valid packet. */
> > + if (size < INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE)
> > + break;
> > + /* Error if we are going to overflow the buffer. */
> > + if (i + size > st->fifo.count)
> > + return -EIO;
> > + if (gyro != NULL && inv_icm42607_fifo_is_data_valid(gyro))
> > + st->fifo.nb.gyro++;
> > + if (accel != NULL && inv_icm42607_fifo_is_data_valid(accel))
> > + st->fifo.nb.accel++;
> > + st->fifo.nb.total++;
> > + }
> > +
> > + return 0;
> > +}
>
> > diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
> > new file mode 100644
> > index 000000000000..b77deb66f8bd
> > --- /dev/null
> > +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
>
> > +
> > +/* FIFO data packet */
> > +struct inv_icm42607_fifo_sensor_data {
> > + __be16 x;
> > + __be16 y;
> > + __be16 z;
> > +};
>
> Sashiko is probably correct that this should be packed.
> Makes not difference here but it's not aligned in the places it's embedded
> in other structs and the compiler seeing this will assume it is aligned.
>
So I should pack this instead of doing the "get_unaligned()" call?
Thank you.
> > +
> > +#define INV_ICM42607_FIFO_DATA_INVALID -32768
> > +
> > +static inline bool
> > +inv_icm42607_fifo_is_data_valid(const struct inv_icm42607_fifo_sensor_data *s)
> > +{
> > + s16 x, y, z;
> > +
> > + x = be16_to_cpu(s->x);
> > + y = be16_to_cpu(s->y);
> > + z = be16_to_cpu(s->z);
> > +
> > + if (x == INV_ICM42607_FIFO_DATA_INVALID &&
> > + y == INV_ICM42607_FIFO_DATA_INVALID &&
> > + z == INV_ICM42607_FIFO_DATA_INVALID)
> > + return false;
> > +
> > + return true;
> > +}
>
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH V9 06/11] iio: imu: inv_icm42607: Add Buffer support for icm42607
2026-06-01 13:50 ` Chris Morgan
@ 2026-06-01 14:36 ` Jonathan Cameron
0 siblings, 0 replies; 26+ messages in thread
From: Jonathan Cameron @ 2026-06-01 14:36 UTC (permalink / raw)
To: Chris Morgan
Cc: Chris Morgan, linux-iio, andy, nuno.sa, dlechner,
jean-baptiste.maneyrol, linux-rockchip, devicetree, heiko,
conor+dt, krzk+dt, robh, andriy.shevchenko
On Mon, 1 Jun 2026 08:50:02 -0500
Chris Morgan <macromorgan@hotmail.com> wrote:
> On Sun, May 31, 2026 at 01:38:01PM +0100, Jonathan Cameron wrote:
> > On Fri, 29 May 2026 22:17:33 -0500
> > Chris Morgan <macroalpha82@gmail.com> wrote:
> >
> > > From: Chris Morgan <macromorgan@hotmail.com>
> > >
> > > Add all FIFO parsing and reading functions to support
> > > inv_icm42607 hardware.
> > >
> > > Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
> > A few things inline.
> >
> > J
> >
> > > diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> > > new file mode 100644
> > > index 000000000000..e065d60ac119
> > > --- /dev/null
> > > +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> > > @@ -0,0 +1,483 @@
> > > +// SPDX-License-Identifier: GPL-2.0-or-later
> > > +/*
> > > + * Copyright (C) 2026 InvenSense, Inc.
> > > + */
> > > +
> > > +#include <linux/delay.h>
> > > +#include <linux/iio/buffer.h>
> > > +#include <linux/iio/iio.h>
> > > +#include <linux/minmax.h>
> > > +#include <linux/mutex.h>
> > > +#include <linux/pm_runtime.h>
> > > +#include <linux/regmap.h>
> > > +#include <linux/unaligned.h>
> > > +
> > > +#include <linux/iio/common/inv_sensors_timestamp.h>
> > > +
> > > +#include "inv_icm42607.h"
> > > +#include "inv_icm42607_buffer.h"
> > > +
> > > +/* FIFO header: 1 byte */
> > > +#define INV_ICM42607_FIFO_HEADER_MSG BIT(7)
> > > +#define INV_ICM42607_FIFO_HEADER_ACCEL BIT(6)
> > > +#define INV_ICM42607_FIFO_HEADER_GYRO BIT(5)
> > > +#define INV_ICM42607_FIFO_HEADER_TMST_FSYNC GENMASK(3, 2)
> > > +#define INV_ICM42607_FIFO_HEADER_ODR_ACCEL BIT(1)
> > > +#define INV_ICM42607_FIFO_HEADER_ODR_GYRO BIT(0)
> > > +
> > > +struct inv_icm42607_fifo_1sensor_packet {
> > > + u8 header;
> > > + struct inv_icm42607_fifo_sensor_data data;
> > > + s8 temp;
> > > +} __packed;
> > > +
> > > +struct inv_icm42607_fifo_2sensors_packet {
> > > + u8 header;
> > > + struct inv_icm42607_fifo_sensor_data accel;
> > Good example of the need for the inner structures to be packed
>
> Are you saying I should set the inv_icm42607_fifo_sensor_data to be
> __packed? I think I was told in a previous patch not to do that, but
> I can add it back.
My understanding is that it is necessary. Just add a comment on why
next to the structure. Otherwise typically the compiler assumes
they are aligned and doesn't work around that. Alternatively just
use an array of u8 and get_unaligned() to extract the values.
(read on - on I suspect this is the better path forwards)
>
> >
> > > + struct inv_icm42607_fifo_sensor_data gyro;
> > > + s8 temp;
> > > + __be16 timestamp;
> > > +} __packed;
> > > +
> > > +ssize_t inv_icm42607_fifo_decode_packet(const void *packet, const void **accel,
> > > + const void **gyro, const int8_t **temp,
> > > + const void **timestamp)
> > > +{
> > > + const struct inv_icm42607_fifo_1sensor_packet *pack1 = get_unaligned(&packet);
> > > + const struct inv_icm42607_fifo_2sensors_packet *pack2 = get_unaligned(&packet);
> >
> > Hmm. Sashiko points out you are messing around with pointers here and it's not those but
> > what the point to that we need to worry about alignment for.
> > You could memcpy that data into local structures.
>
> I still suck at pointers, sorry. What exactly would you recommend here?
> memcpy the packet value into an allocated
> inv_icm42607_fifo_1sensor_packet or inv_icm42607_fifo_2sensors_packet?
yes. Copy it. though given the result has unaligned internal elements so it
doesn't really help much. I'm thinking simpler to just treat it as a byte stream
and access each element directly.
>
> >
> >
> > > + u8 header = *((const u8 *)packet);
> > > +
> > > + /* FIFO empty */
> > > + if (header & INV_ICM42607_FIFO_HEADER_MSG) {
> > > + *accel = NULL;
> > > + *gyro = NULL;
> > > + *temp = NULL;
> > > + *timestamp = NULL;
> > > + return 0;
> > > + }
> > > +
> > > + /* accel + gyro */
> > > + if ((header & INV_ICM42607_FIFO_HEADER_ACCEL) &&
> > > + (header & INV_ICM42607_FIFO_HEADER_GYRO)) {
> > > + *accel = &pack2->accel;
> > > + *gyro = &pack2->gyro;
> > > + *temp = &pack2->temp;
> > > + *timestamp = &pack2->timestamp;
> > > + return INV_ICM42607_FIFO_2SENSORS_PACKET_SIZE;
> > > + }
> > > +
> > > + /* accel only */
> > > + if (header & INV_ICM42607_FIFO_HEADER_ACCEL) {
> > > + *accel = &pack1->data;
> > > + *gyro = NULL;
> > > + *temp = &pack1->temp;
> > > + *timestamp = NULL;
> > > + return INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE;
> > > + }
> > > +
> > > + /* gyro only */
> > > + if (header & INV_ICM42607_FIFO_HEADER_GYRO) {
> > > + *accel = NULL;
> > > + *gyro = &pack1->data;
> > > + *temp = &pack1->temp;
> > > + *timestamp = NULL;
> > > + return INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE;
> > > + }
> > > +
> > > + /* invalid packet if here */
> > > + return -EINVAL;
> > > +}
> >
> > > +/**
> > > + * inv_icm42607_buffer_update_watermark - update watermark FIFO threshold
> > > + * @st: driver internal state
> > > + *
> > > + * Returns 0 on success, a negative error code otherwise.
> > > + */
> > > +int inv_icm42607_buffer_update_watermark(struct inv_icm42607_state *st)
> > > +{
> > > + const struct device *dev = regmap_get_device(st->map);
> > > + unsigned int wm_gyro, wm_accel, watermark;
> > > + u32 latency_gyro, latency_accel, latency;
> > > + u32 period_gyro, period_accel;
> > > + size_t packet_size, wm_size;
> > > + __le16 raw_wm;
> > > + bool restore;
> > > + int ret;
> > > +
> > > + packet_size = inv_icm42607_get_packet_size(st->fifo.en);
> > > +
> > > + /* compute sensors latency, depending on sensor watermark and odr */
> > > + wm_gyro = inv_icm42607_wm_truncate(st->fifo.watermark.gyro, packet_size);
> > > + wm_accel = inv_icm42607_wm_truncate(st->fifo.watermark.accel, packet_size);
> > > + /* use us for odr to avoid overflow using 32 bits values */
> > > + period_gyro = inv_icm42607_odr_to_period(st->conf.gyro.odr) / 1000UL;
> > > + period_accel = inv_icm42607_odr_to_period(st->conf.accel.odr) / 1000UL;
> > > + latency_gyro = period_gyro * wm_gyro;
> > > + latency_accel = period_accel * wm_accel;
> > > +
> > > + /* 0 value for watermark means that the sensor is turned off */
> > > + if (wm_gyro == 0 && wm_accel == 0)
> > > + return 0;
> > > +
> > > + if (latency_gyro == 0) {
> > > + watermark = wm_accel;
> > > + st->fifo.watermark.eff_accel = wm_accel;
> > > + } else if (latency_accel == 0) {
> > > + watermark = wm_gyro;
> > > + st->fifo.watermark.eff_gyro = wm_gyro;
> > > + } else {
> > > + /* compute the smallest latency that is a multiple of both */
> > > + if (latency_gyro <= latency_accel)
> > > + latency = latency_gyro - (latency_accel % latency_gyro);
> > > + else
> > > + latency = latency_accel - (latency_gyro % latency_accel);
> > > + /* all this works because periods are multiple of each others */
> > > + watermark = latency / min(period_gyro, period_accel);
> > > + watermark = max(watermark, 1);
> > > + /* update effective watermark */
> > > + st->fifo.watermark.eff_gyro = max(latency / period_gyro, 1);
> > > + st->fifo.watermark.eff_accel = max(latency / period_accel, 1);
> > > + }
> > > +
> > > + /* changing FIFO watermark requires to turn off watermark interrupt */
> > > + ret = regmap_update_bits_check(st->map, INV_ICM42607_REG_INT_SOURCE0,
> > > + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN,
> > > + 0, &restore);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + /* compute watermark value in bytes */
> > > + wm_size = watermark * packet_size;
> > > + raw_wm = INV_ICM42607_FIFO_WATERMARK_VAL(wm_size);
> > > + memcpy(st->buffer, &raw_wm, sizeof(raw_wm));
> > > + ret = regmap_bulk_write(st->map, INV_ICM42607_REG_FIFO_CONFIG2,
> > > + st->buffer, sizeof(raw_wm));
> > > + if (ret) {
> > > + dev_err(dev, "Unable to change watermark value: %d\n", ret);
> > > + if (restore)
> > > + regmap_update_bits(st->map, INV_ICM42607_REG_INT_SOURCE0,
> > > + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN,
> > > + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN);
> >
> > set_bits.
> >
> > > + return ret;
> > > + }
> > > +
> > > + /* restore watermark interrupt */
> > > + if (restore) {
> > > + ret = regmap_update_bits(st->map, INV_ICM42607_REG_INT_SOURCE0,
> > > + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN,
> > > + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN);
> >
> > set_bits
> >
> > > + if (ret)
> > > + return ret;
> > > + }
> > > +
> > > + return 0;
> > > +}
> >
> > > +
> > > +static int inv_icm42607_buffer_predisable(struct iio_dev *indio_dev)
> > > +{
> > > + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
> > > + int ret;
> > > +
> > > + guard(mutex)(&st->lock);
> > > +
> > > + if (st->fifo.on > 1) {
> > > + st->fifo.on--;
> > > + return 0;
> > > + }
> > > +
> > > + /* Set FIFO to 0 since iio core ignores teardown errors. */
> > > + st->fifo.on = 0;
> > > +
> > > + /* set FIFO in bypass mode */
> > > + ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1,
> > > + INV_ICM42607_FIFO_CONFIG1_BYPASS);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + /* flush FIFO data */
> > > + ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET,
> > > + INV_ICM42607_SIGNAL_PATH_RESET_FIFO_FLUSH);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + /* disable FIFO threshold interrupt */
> > > + ret = regmap_update_bits(st->map, INV_ICM42607_REG_INT_SOURCE0,
> > > + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN, 0);
> >
> > regmap_clear_bits()
> >
> > > + if (ret)
> > > + return ret;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int inv_icm42607_buffer_postdisable(struct iio_dev *indio_dev)
> > > +{
> > > + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
> > > + struct device *dev = regmap_get_device(st->map);
> > > + unsigned int sensor;
> > > + unsigned int *watermark;
> > > + int ret;
> > > +
> > > + if (indio_dev == st->indio_gyro) {
> > > + sensor = INV_ICM42607_SENSOR_GYRO;
> > > + watermark = &st->fifo.watermark.gyro;
> > > + } else if (indio_dev == st->indio_accel) {
> > > + sensor = INV_ICM42607_SENSOR_ACCEL;
> > > + watermark = &st->fifo.watermark.accel;
> > > + } else {
> > > + return -EINVAL;
> > > + }
> > > +
> > > + mutex_lock(&st->lock);
> > > +
> > > + /*
> > > + * FIFO enabled at update scan mode for accel or gyro, and
> > > + * disabled here.
> > > + */
> > > + ret = inv_icm42607_buffer_set_fifo_en(st, st->fifo.en & ~sensor);
> > > + if (ret)
> > > + goto out_unlock;
> > > +
> > > + *watermark = 0;
> >
> > Add a comment on why this needs to be set to 0. Normally that only
> > matters at all if the fifo is on, so I guess something unusual here?
>
> I think it's just resetting the watermark to 0 when shutting down,
> not 100% sure though.
>
> >
> >
> > > + ret = inv_icm42607_buffer_update_watermark(st);
> > > + if (ret)
> > > + goto out_unlock;
> > > +
> > > +out_unlock:
> > > + mutex_unlock(&st->lock);
> > > +
> > > + pm_runtime_put_autosuspend(dev);
> > > +
> > > + return ret;
> > > +}
> >
> > > +int inv_icm42607_buffer_fifo_read(struct inv_icm42607_state *st,
> > > + unsigned int max)
> > > +{
> > > + const void *accel, *gyro, *timestamp;
> > > + size_t i, max_count;
> > > + const s8 *temp;
> > > + ssize_t size;
> > > + int ret;
> > > +
> > > + guard(mutex)(&st->lock);
> > > +
> > > + /* reset all samples counters */
> > > + st->fifo.count = 0;
> > > + st->fifo.nb.gyro = 0;
> > > + st->fifo.nb.accel = 0;
> > > + st->fifo.nb.total = 0;
> > > +
> > > + /* compute maximum FIFO read size */
> > > + if (max == 0)
> > > + max_count = sizeof(st->fifo.data);
> > > + else
> > > + max_count = min((max * inv_icm42607_get_packet_size(st->fifo.en)),
> > > + sizeof(st->fifo.data));
> > > +
> > > + /* read FIFO count value */
> > > + ret = regmap_bulk_read(st->map, INV_ICM42607_REG_FIFO_COUNTH,
> > > + st->buffer, sizeof(u8) * 2);
> > > + if (ret)
> > > + return ret;
> > > + st->fifo.count = be16_to_cpup(st->buffer);
> >
> > Might be ok to assume this is always a multiple of the scan size, but
> > maybe sanity check it to keep sashiko happy and remove that assumption
> > of atomic update.
> >
> > > +
> > > + /* check and clamp FIFO count value */
> > > + if (st->fifo.count == 0)
> > > + return 0;
> > > +
> > > + st->fifo.count = min(st->fifo.count, max_count);
> > > +
> > > + /* read all FIFO data in internal buffer */
> > > + ret = regmap_noinc_read(st->map, INV_ICM42607_REG_FIFO_DATA,
> > > + st->fifo.data, st->fifo.count);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + /* compute number of samples for each sensor */
> > > + for (i = 0; i < st->fifo.count; i += size) {
> > > + size = inv_icm42607_fifo_decode_packet(&st->fifo.data[i],
> > > + &accel, &gyro, &temp, ×tamp);
> > > + /* Make sure the size is at least 1 valid packet. */
> > > + if (size < INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE)
> > > + break;
> > > + /* Error if we are going to overflow the buffer. */
> > > + if (i + size > st->fifo.count)
> > > + return -EIO;
> > > + if (gyro != NULL && inv_icm42607_fifo_is_data_valid(gyro))
> > > + st->fifo.nb.gyro++;
> > > + if (accel != NULL && inv_icm42607_fifo_is_data_valid(accel))
> > > + st->fifo.nb.accel++;
> > > + st->fifo.nb.total++;
> > > + }
> > > +
> > > + return 0;
> > > +}
> >
> > > diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
> > > new file mode 100644
> > > index 000000000000..b77deb66f8bd
> > > --- /dev/null
> > > +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
> >
> > > +
> > > +/* FIFO data packet */
> > > +struct inv_icm42607_fifo_sensor_data {
> > > + __be16 x;
> > > + __be16 y;
> > > + __be16 z;
> > > +};
> >
> > Sashiko is probably correct that this should be packed.
> > Makes not difference here but it's not aligned in the places it's embedded
> > in other structs and the compiler seeing this will assume it is aligned.
> >
>
> So I should pack this instead of doing the "get_unaligned()" call?
This whole thing feels like a loosing battle. Just rip the structures out and
use defines for the offsets.
> Thank you.
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH V9 07/11] iio: imu: inv_icm42607: Add Temp Support in icm42607
2026-05-30 3:17 [PATCH V9 00/11] Add Invensense ICM42607 Chris Morgan
` (5 preceding siblings ...)
2026-05-30 3:17 ` [PATCH V9 06/11] iio: imu: inv_icm42607: Add Buffer " Chris Morgan
@ 2026-05-30 3:17 ` Chris Morgan
2026-05-30 3:17 ` [PATCH V9 08/11] iio: imu: inv_icm42607: Add Accelerometer for icm42607 Chris Morgan
` (3 subsequent siblings)
10 siblings, 0 replies; 26+ messages in thread
From: Chris Morgan @ 2026-05-30 3:17 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add functions for reading temperature sensor data.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/inv_icm42607/Makefile | 1 +
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 3 +
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 18 +++++
.../iio/imu/inv_icm42607/inv_icm42607_temp.c | 81 +++++++++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_temp.h | 33 ++++++++
5 files changed, 136 insertions(+)
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h
diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile
index 3c9d08509793..ccb8e007cdeb 100644
--- a/drivers/iio/imu/inv_icm42607/Makefile
+++ b/drivers/iio/imu/inv_icm42607/Makefile
@@ -3,6 +3,7 @@
obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o
inv-icm42607-y += inv_icm42607_core.o
inv-icm42607-y += inv_icm42607_buffer.o
+inv-icm42607-y += inv_icm42607_temp.o
obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o
inv-icm42607-i2c-y += inv_icm42607_i2c.o
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
index cb878de970d4..c651f0b66d43 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -381,6 +381,9 @@ extern const struct dev_pm_ops inv_icm42607_pm_ops;
u32 inv_icm42607_odr_to_period(enum inv_icm42607_odr odr);
+int inv_icm42607_set_temp_conf(struct inv_icm42607_state *st, bool enable,
+ unsigned int *sleep_ms);
+
int inv_icm42607_core_probe(struct regmap *regmap,
const struct inv_icm42607_hw *hw,
inv_icm42607_bus_setup bus_setup);
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
index 16ead03a59f9..cf9d0c6f3f4b 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -152,6 +152,24 @@ static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st,
return 0;
}
+int inv_icm42607_set_temp_conf(struct inv_icm42607_state *st, bool enable,
+ unsigned int *sleep_ms)
+{
+ unsigned int val;
+ int ret;
+
+ val = FIELD_PREP(INV_ICM42607_TEMP_CONFIG0_FILTER_MASK,
+ INV_ICM42607_FILTER_BW_34HZ);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_TEMP_CONFIG0,
+ INV_ICM42607_TEMP_CONFIG0_FILTER_MASK, val);
+ if (ret)
+ return ret;
+
+ return inv_icm42607_set_pwr_mgmt0(st, st->conf.gyro.mode,
+ st->conf.accel.mode, enable,
+ sleep_ms);
+}
+
static int inv_icm42607_set_conf(struct inv_icm42607_state *st,
const struct inv_icm42607_conf *conf)
{
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c
new file mode 100644
index 000000000000..418098450592
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/iio/iio.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include "inv_icm42607.h"
+#include "inv_icm42607_temp.h"
+
+static int inv_icm42607_temp_read(struct inv_icm42607_state *st, s16 *temp)
+{
+ struct device *dev = regmap_get_device(st->map);
+ __be16 *raw;
+ int ret;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ ret = inv_icm42607_set_temp_conf(st, true, NULL);
+ if (ret)
+ return ret;
+
+ raw = &st->buffer[0];
+ ret = regmap_bulk_read(st->map, INV_ICM42607_REG_TEMP_DATA1, raw, sizeof(*raw));
+ if (ret)
+ return ret;
+
+ *temp = be16_to_cpup(raw);
+ if (*temp == INV_ICM42607_DATA_INVALID)
+ return -EINVAL;
+
+ return 0;
+}
+
+int inv_icm42607_temp_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ s16 temp;
+ int ret;
+
+ if (chan->type != IIO_TEMP)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = inv_icm42607_temp_read(st, &temp);
+ iio_device_release_direct(indio_dev);
+ if (ret)
+ return ret;
+ *val = temp;
+ return IIO_VAL_INT;
+ /*
+ * T°C = (temp / 128) + 25
+ * Tm°C = 1000 * ((temp * 100 / 12800) + 25)
+ * scale: 100000 / 12800 ~= 7.8125
+ * offset: 3200
+ */
+ case IIO_CHAN_INFO_SCALE:
+ *val = 7;
+ *val2 = 812500000;
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 3200;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h
new file mode 100644
index 000000000000..e03924e30866
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#ifndef INV_ICM42607_TEMP_H_
+#define INV_ICM42607_TEMP_H_
+
+#include <linux/bitops.h>
+
+struct iio_dev;
+struct iio_chan_spec;
+
+#define INV_ICM42607_TEMP_CHAN(_index) \
+{ \
+ .type = IIO_TEMP, \
+ .info_mask_separate = \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ }, \
+}
+
+int inv_icm42607_temp_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask);
+
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH V9 08/11] iio: imu: inv_icm42607: Add Accelerometer for icm42607
2026-05-30 3:17 [PATCH V9 00/11] Add Invensense ICM42607 Chris Morgan
` (6 preceding siblings ...)
2026-05-30 3:17 ` [PATCH V9 07/11] iio: imu: inv_icm42607: Add Temp Support in icm42607 Chris Morgan
@ 2026-05-30 3:17 ` Chris Morgan
2026-05-30 3:17 ` [PATCH V9 09/11] iio: imu: inv_icm42607: Add IRQ " Chris Morgan
` (2 subsequent siblings)
10 siblings, 0 replies; 26+ messages in thread
From: Chris Morgan @ 2026-05-30 3:17 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add icm42607 accelerometer sensor for icm42607.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/inv_icm42607/Makefile | 1 +
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 36 ++
.../iio/imu/inv_icm42607/inv_icm42607_accel.c | 589 ++++++++++++++++++
.../imu/inv_icm42607/inv_icm42607_buffer.c | 74 ++-
.../imu/inv_icm42607/inv_icm42607_buffer.h | 4 +-
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 72 +++
6 files changed, 772 insertions(+), 4 deletions(-)
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile
index ccb8e007cdeb..e908d77c4219 100644
--- a/drivers/iio/imu/inv_icm42607/Makefile
+++ b/drivers/iio/imu/inv_icm42607/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o
inv-icm42607-y += inv_icm42607_core.o
+inv-icm42607-y += inv_icm42607_accel.o
inv-icm42607-y += inv_icm42607_buffer.o
inv-icm42607-y += inv_icm42607_temp.o
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
index c651f0b66d43..5a649567ca62 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -89,12 +89,24 @@ enum inv_icm42607_filter_bw {
INV_ICM42607_FILTER_BW_NB
};
+enum inv_icm42607_filter_avg {
+ /* Low-Power mode sensor data filter (averaging) */
+ INV_ICM42607_FILTER_AVG_2X = 0,
+ INV_ICM42607_FILTER_AVG_4X,
+ INV_ICM42607_FILTER_AVG_8X,
+ INV_ICM42607_FILTER_AVG_16X,
+ INV_ICM42607_FILTER_AVG_32X,
+ INV_ICM42607_FILTER_AVG_64X,
+ /* values 7 and 8 also correspond to 64x. */
+};
+
struct inv_icm42607_sensor_conf {
int mode;
int fs;
int odr;
int filter;
};
+#define INV_ICM42607_SENSOR_CONF_INIT { -1, -1, -1, -1 }
struct inv_icm42607_conf {
struct inv_icm42607_sensor_conf gyro;
@@ -108,6 +120,12 @@ struct inv_icm42607_hw {
const struct inv_icm42607_conf *conf;
};
+struct inv_icm42607_suspended {
+ enum inv_icm42607_sensor_mode gyro;
+ enum inv_icm42607_sensor_mode accel;
+ bool temp;
+};
+
/**
* struct inv_icm42607_state - driver state variables
* @lock: lock for serializing multiple registers access.
@@ -118,8 +136,10 @@ struct inv_icm42607_hw {
* @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.
* @fifo: FIFO management structure.
* @buffer: data transfer buffer aligned for DMA.
*/
@@ -132,8 +152,12 @@ struct inv_icm42607_state {
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 accel;
+ } timestamp;
struct inv_icm42607_fifo fifo;
__be16 buffer[3] __aligned(IIO_DMA_MINALIGN);
};
@@ -379,8 +403,16 @@ extern const struct inv_icm42607_hw inv_icm42607_hw_data;
extern const struct inv_icm42607_hw inv_icm42607p_hw_data;
extern const struct dev_pm_ops inv_icm42607_pm_ops;
+const struct iio_mount_matrix *
+inv_icm42607_get_mount_matrix(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan);
+
u32 inv_icm42607_odr_to_period(enum inv_icm42607_odr odr);
+int inv_icm42607_set_accel_conf(struct inv_icm42607_state *st,
+ struct inv_icm42607_sensor_conf *conf,
+ unsigned int *sleep_ms);
+
int inv_icm42607_set_temp_conf(struct inv_icm42607_state *st, bool enable,
unsigned int *sleep_ms);
@@ -388,4 +420,8 @@ int inv_icm42607_core_probe(struct regmap *regmap,
const struct inv_icm42607_hw *hw,
inv_icm42607_bus_setup bus_setup);
+struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st);
+
+int inv_icm42607_accel_parse_fifo(struct iio_dev *indio_dev);
+
#endif
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
new file mode 100644
index 000000000000..43edc2923d04
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/math64.h>
+#include <linux/minmax.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/units.h>
+
+#include <linux/iio/common/inv_sensors_timestamp.h>
+#include "inv_icm42607.h"
+#include "inv_icm42607_temp.h"
+#include "inv_icm42607_buffer.h"
+
+#define INV_ICM42607_ACCEL_CHAN(_modifier, _index, _ext_info) \
+{ \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = _modifier, \
+ .info_mask_separate = \
+ BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+ .ext_info = _ext_info, \
+}
+
+#define INV_ICM42607_ACCEL_EVENT_CHAN(_modifier, _events, _events_nb) \
+ { \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = _modifier, \
+ .event_spec = _events, \
+ .num_event_specs = _events_nb, \
+ .scan_index = -1, \
+ }
+
+enum inv_icm42607_accel_scan {
+ INV_ICM42607_ACCEL_SCAN_X,
+ INV_ICM42607_ACCEL_SCAN_Y,
+ INV_ICM42607_ACCEL_SCAN_Z,
+ INV_ICM42607_ACCEL_SCAN_TEMP,
+ INV_ICM42607_ACCEL_SCAN_TIMESTAMP,
+};
+
+static const struct iio_chan_spec_ext_info inv_icm42607_accel_ext_infos[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42607_get_mount_matrix),
+ { }
+};
+
+static const struct iio_chan_spec inv_icm42607_accel_channels[] = {
+ INV_ICM42607_ACCEL_CHAN(IIO_MOD_X, INV_ICM42607_ACCEL_SCAN_X,
+ inv_icm42607_accel_ext_infos),
+ INV_ICM42607_ACCEL_CHAN(IIO_MOD_Y, INV_ICM42607_ACCEL_SCAN_Y,
+ inv_icm42607_accel_ext_infos),
+ INV_ICM42607_ACCEL_CHAN(IIO_MOD_Z, INV_ICM42607_ACCEL_SCAN_Z,
+ inv_icm42607_accel_ext_infos),
+ INV_ICM42607_TEMP_CHAN(INV_ICM42607_ACCEL_SCAN_TEMP),
+ IIO_CHAN_SOFT_TIMESTAMP(INV_ICM42607_ACCEL_SCAN_TIMESTAMP),
+};
+
+static const struct iio_event_spec inv_icm42607_motion_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_VALUE),
+ },
+};
+
+struct inv_icm42607_accel_buffer {
+ struct inv_icm42607_fifo_sensor_data accel;
+ s16 temp;
+ aligned_s64 timestamp;
+};
+
+#define INV_ICM42607_SCAN_MASK_ACCEL_3AXIS \
+ (BIT(INV_ICM42607_ACCEL_SCAN_X) | \
+ BIT(INV_ICM42607_ACCEL_SCAN_Y) | \
+ BIT(INV_ICM42607_ACCEL_SCAN_Z))
+
+#define INV_ICM42607_SCAN_MASK_TEMP BIT(INV_ICM42607_ACCEL_SCAN_TEMP)
+
+static const unsigned long inv_icm42607_accel_scan_masks[] = {
+ INV_ICM42607_SCAN_MASK_ACCEL_3AXIS,
+ INV_ICM42607_SCAN_MASK_ACCEL_3AXIS | INV_ICM42607_SCAN_MASK_TEMP,
+ 0
+};
+
+/* enable accelerometer sensor and FIFO write */
+static int inv_icm42607_accel_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_state *accel_st = iio_priv(indio_dev);
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ unsigned int fifo_en = 0;
+ unsigned int sleep_temp = 0;
+ unsigned int sleep_accel = 0;
+ int ret;
+
+ mutex_lock(&st->lock);
+
+ if (*scan_mask & INV_ICM42607_SCAN_MASK_TEMP) {
+ /* enable temp sensor */
+ ret = inv_icm42607_set_temp_conf(st, true, &sleep_temp);
+ if (ret)
+ goto out_unlock;
+ fifo_en |= INV_ICM42607_SENSOR_TEMP;
+ }
+
+ if (*scan_mask & INV_ICM42607_SCAN_MASK_ACCEL_3AXIS) {
+ /* enable accel sensor */
+ conf.mode = accel_st->power_mode;
+ conf.filter = accel_st->filter;
+ ret = inv_icm42607_set_accel_conf(st, &conf, &sleep_accel);
+ if (ret)
+ goto out_unlock;
+ fifo_en |= INV_ICM42607_SENSOR_ACCEL;
+ }
+
+ /* update data FIFO write */
+ ret = inv_icm42607_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
+
+out_unlock:
+ mutex_unlock(&st->lock);
+ /*
+ * Choose the highest enable-delay time of the two sensors being
+ * enabled, and sleep for that amount of time.
+ */
+ msleep(max(sleep_accel, sleep_temp));
+
+ return ret;
+}
+
+static int inv_icm42607_accel_read_sensor(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ s16 *val)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_state *accel_st = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ unsigned int reg;
+ __be16 *data;
+ int ret;
+
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ reg = INV_ICM42607_REG_ACCEL_DATA_X1;
+ break;
+ case IIO_MOD_Y:
+ reg = INV_ICM42607_REG_ACCEL_DATA_Y1;
+ break;
+ case IIO_MOD_Z:
+ reg = INV_ICM42607_REG_ACCEL_DATA_Z1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ /* enable accel sensor */
+ conf.mode = accel_st->power_mode;
+ conf.filter = accel_st->filter;
+ ret = inv_icm42607_set_accel_conf(st, &conf, NULL);
+ if (ret)
+ return ret;
+
+ /* read accel register data */
+ data = &st->buffer[0];
+ ret = regmap_bulk_read(st->map, reg, data, sizeof(*data));
+ if (ret)
+ return ret;
+
+ *val = be16_to_cpup(data);
+ if (*val == INV_ICM42607_DATA_INVALID)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const int inv_icm42607_accel_scale_nano[][2] = {
+ [INV_ICM42607_ACCEL_FS_16G] = { 0, 4788403 },
+ [INV_ICM42607_ACCEL_FS_8G] = { 0, 2394202 },
+ [INV_ICM42607_ACCEL_FS_4G] = { 0, 1197101 },
+ [INV_ICM42607_ACCEL_FS_2G] = { 0, 598550 }
+};
+
+static int inv_icm42607_accel_read_scale(struct iio_dev *indio_dev,
+ int *val, int *val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ unsigned int idx;
+
+ idx = st->conf.accel.fs;
+
+ *val = inv_icm42607_accel_scale_nano[idx][0];
+ *val2 = inv_icm42607_accel_scale_nano[idx][1];
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int inv_icm42607_accel_write_scale(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ size_t scales_len = ARRAY_SIZE(inv_icm42607_accel_scale_nano);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+ int ret;
+
+ for (idx = 0; idx < scales_len; idx++) {
+ if (val == inv_icm42607_accel_scale_nano[idx][0] &&
+ val2 == inv_icm42607_accel_scale_nano[idx][1])
+ break;
+ }
+ if (idx >= scales_len)
+ return -EINVAL;
+
+ conf.fs = idx;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ return inv_icm42607_set_accel_conf(st, &conf, NULL);
+}
+
+/* IIO format int + micro , values 0-5 reserved. */
+static const int inv_icm42607_accel_odr[][2] = {
+ [INV_ICM42607_ODR_1_5625HZ_LP] = { 1, 562500 },
+ [INV_ICM42607_ODR_3_125HZ_LP] = { 3, 125000 },
+ [INV_ICM42607_ODR_6_25HZ_LP] = { 6, 250000 },
+ [INV_ICM42607_ODR_12_5HZ] = { 12, 500000 },
+ [INV_ICM42607_ODR_25HZ] = { 25, 0 },
+ [INV_ICM42607_ODR_50HZ] = { 50, 0 },
+ [INV_ICM42607_ODR_100HZ] = { 100, 0 },
+ [INV_ICM42607_ODR_200HZ] = { 200, 0 },
+ [INV_ICM42607_ODR_400HZ] = { 400, 0 },
+ [INV_ICM42607_ODR_800HZ] = { 800, 0 },
+ [INV_ICM42607_ODR_1600HZ] = { 1600, 0 }
+};
+
+static int inv_icm42607_accel_read_odr(struct inv_icm42607_state *st,
+ int *val, int *val2)
+{
+ unsigned int odr;
+ unsigned int i;
+
+ odr = st->conf.accel.odr;
+
+ for (i = 5; i < ARRAY_SIZE(inv_icm42607_accel_odr); ++i) {
+ if (i == odr)
+ break;
+ }
+ if (i >= ARRAY_SIZE(inv_icm42607_accel_odr))
+ return -EINVAL;
+
+ *val = inv_icm42607_accel_odr[i][0];
+ *val2 = inv_icm42607_accel_odr[i][1];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int inv_icm42607_accel_write_odr(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_state *accel_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &accel_st->ts;
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ int ret;
+
+ for (idx = 5; idx < ARRAY_SIZE(inv_icm42607_accel_odr); ++idx) {
+ if (val == inv_icm42607_accel_odr[idx][0] &&
+ val2 == inv_icm42607_accel_odr[idx][1])
+ break;
+ }
+ if (idx >= ARRAY_SIZE(inv_icm42607_accel_odr))
+ return -EINVAL;
+
+ conf.odr = idx;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ ret = inv_sensors_timestamp_update_odr(ts, inv_icm42607_odr_to_period(conf.odr),
+ iio_buffer_enabled(indio_dev));
+ if (ret)
+ return ret;
+
+ ret = inv_icm42607_set_accel_conf(st, &conf, NULL);
+ if (ret)
+ return ret;
+
+ inv_icm42607_buffer_update_fifo_period(st);
+ inv_icm42607_buffer_update_watermark(st);
+
+ return 0;
+}
+
+static int inv_icm42607_accel_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ s16 data;
+ int ret;
+
+ switch (chan->type) {
+ case IIO_ACCEL:
+ break;
+ case IIO_TEMP:
+ return inv_icm42607_temp_read_raw(indio_dev, chan, val, val2, mask);
+ default:
+ return -EINVAL;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = inv_icm42607_accel_read_sensor(indio_dev, chan, &data);
+ iio_device_release_direct(indio_dev);
+ if (ret)
+ return ret;
+ *val = data;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ return inv_icm42607_accel_read_scale(indio_dev, val, val2);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm42607_accel_read_odr(st, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_accel_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals,
+ int *type, int *length, long mask)
+{
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *vals = (const int *)inv_icm42607_accel_scale_nano;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *length = ARRAY_SIZE(inv_icm42607_accel_scale_nano) * 2;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = (const int *)inv_icm42607_accel_odr[5];
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *length = (ARRAY_SIZE(inv_icm42607_accel_odr) - 5) * 2;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_accel_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret;
+
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = inv_icm42607_accel_write_scale(indio_dev, val, val2);
+ iio_device_release_direct(indio_dev);
+ return ret;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm42607_accel_write_odr(indio_dev, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_accel_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_accel_hwfifo_set_watermark(struct iio_dev *indio_dev,
+ unsigned int val)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ int ret;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ st->fifo.watermark.accel = val;
+ return inv_icm42607_buffer_update_watermark(st);
+}
+
+static int inv_icm42607_accel_hwfifo_flush(struct iio_dev *indio_dev,
+ unsigned int count)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ int ret;
+
+ if (count == 0)
+ return 0;
+
+ /* Make sure we don't try to count more than the buffer can hold. */
+ count = min(count, sizeof(st->fifo.data));
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ ret = inv_icm42607_buffer_hwfifo_flush(st, count);
+ if (ret)
+ return ret;
+
+ return st->fifo.nb.accel;
+}
+
+static const struct iio_info inv_icm42607_accel_info = {
+ .read_raw = inv_icm42607_accel_read_raw,
+ .read_avail = inv_icm42607_accel_read_avail,
+ .write_raw = inv_icm42607_accel_write_raw,
+ .write_raw_get_fmt = inv_icm42607_accel_write_raw_get_fmt,
+ .update_scan_mode = inv_icm42607_accel_update_scan_mode,
+ .hwfifo_set_watermark = inv_icm42607_accel_hwfifo_set_watermark,
+ .hwfifo_flush_to_buffer = inv_icm42607_accel_hwfifo_flush,
+};
+
+struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st)
+{
+ struct device *dev = regmap_get_device(st->map);
+ const char *name;
+ struct inv_icm42607_sensor_state *accel_st;
+ struct inv_sensors_timestamp_chip ts_chip;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "%s-accel", st->hw->name);
+ if (!name)
+ return ERR_PTR(-ENOMEM);
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*accel_st));
+ if (!indio_dev)
+ return ERR_PTR(-ENOMEM);
+ accel_st = iio_priv(indio_dev);
+
+ accel_st->power_mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE;
+ accel_st->filter = INV_ICM42607_FILTER_AVG_16X;
+
+ /*
+ * clock period is 32kHz (31250ns)
+ * jitter is +/- 2% (20 per mille)
+ */
+ ts_chip.clock_period = 31250;
+ ts_chip.jitter = 20;
+ ts_chip.init_period = inv_icm42607_odr_to_period(st->conf.accel.odr);
+ inv_sensors_timestamp_init(&accel_st->ts, &ts_chip);
+
+ iio_device_set_drvdata(indio_dev, st);
+ indio_dev->name = name;
+ indio_dev->info = &inv_icm42607_accel_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = inv_icm42607_accel_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_icm42607_accel_channels);
+ indio_dev->available_scan_masks = inv_icm42607_accel_scan_masks;
+
+ ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
+ &inv_icm42607_buffer_ops);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return indio_dev;
+}
+
+int inv_icm42607_accel_parse_fifo(struct iio_dev *indio_dev)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_state *accel_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &accel_st->ts;
+ ssize_t i, size;
+ unsigned int no;
+ const void *accel, *gyro, *timestamp;
+ const int8_t *temp;
+ unsigned int odr;
+ int64_t ts_val;
+ struct inv_icm42607_accel_buffer buffer = { };
+
+ guard(mutex)(&st->lock);
+
+ /* parse all fifo packets */
+ for (i = 0, no = 0; i < st->fifo.count; i += size, ++no) {
+ size = inv_icm42607_fifo_decode_packet(&st->fifo.data[i],
+ &accel, &gyro, &temp, ×tamp, &odr);
+ /* quit if error or FIFO is empty */
+ if (size <= 0)
+ return size;
+
+ /* If the packet size could cause us to overflow, return. */
+ if (i + size > st->fifo.count)
+ return -EIO;
+
+ /* skip packet if no accel data or data is invalid */
+ if (accel == NULL || !inv_icm42607_fifo_is_data_valid(accel))
+ continue;
+
+ /* update odr */
+ if (odr & INV_ICM42607_SENSOR_ACCEL)
+ inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
+ st->fifo.nb.total, no);
+
+ memcpy(&buffer.accel, accel, sizeof(buffer.accel));
+ /* convert 8 bits FIFO temperature in high resolution format */
+ buffer.temp = temp ? (*temp * 64) : 0;
+ ts_val = inv_sensors_timestamp_pop(ts);
+ iio_push_to_buffers_with_ts(indio_dev, &buffer,
+ sizeof(buffer), ts_val);
+ }
+
+ return 0;
+}
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
index e065d60ac119..5b69bf895b35 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
@@ -41,7 +41,7 @@ struct inv_icm42607_fifo_2sensors_packet {
ssize_t inv_icm42607_fifo_decode_packet(const void *packet, const void **accel,
const void **gyro, const int8_t **temp,
- const void **timestamp)
+ const void **timestamp, unsigned int *odr)
{
const struct inv_icm42607_fifo_1sensor_packet *pack1 = get_unaligned(&packet);
const struct inv_icm42607_fifo_2sensors_packet *pack2 = get_unaligned(&packet);
@@ -53,9 +53,17 @@ ssize_t inv_icm42607_fifo_decode_packet(const void *packet, const void **accel,
*gyro = NULL;
*temp = NULL;
*timestamp = NULL;
+ *odr = 0;
return 0;
}
+ /* handle odr flags */
+ *odr = 0;
+ if (header & INV_ICM42607_FIFO_HEADER_ODR_GYRO)
+ *odr |= INV_ICM42607_SENSOR_GYRO;
+ if (header & INV_ICM42607_FIFO_HEADER_ODR_ACCEL)
+ *odr |= INV_ICM42607_SENSOR_ACCEL;
+
/* accel + gyro */
if ((header & INV_ICM42607_FIFO_HEADER_ACCEL) &&
(header & INV_ICM42607_FIFO_HEADER_GYRO)) {
@@ -349,6 +357,10 @@ static int inv_icm42607_buffer_postdisable(struct iio_dev *indio_dev)
struct device *dev = regmap_get_device(st->map);
unsigned int sensor;
unsigned int *watermark;
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ unsigned int sleep_temp = 0;
+ unsigned int sleep_sensor = 0;
+ unsigned int sleep;
int ret;
if (indio_dev == st->indio_gyro) {
@@ -376,9 +388,24 @@ static int inv_icm42607_buffer_postdisable(struct iio_dev *indio_dev)
if (ret)
goto out_unlock;
+ conf.mode = INV_ICM42607_SENSOR_MODE_OFF;
+ if (sensor != INV_ICM42607_SENSOR_GYRO)
+ ret = inv_icm42607_set_accel_conf(st, &conf, &sleep_sensor);
+ if (ret)
+ goto out_unlock;
+
+ /* if FIFO is off, turn temperature off */
+ if (!st->fifo.on)
+ ret = inv_icm42607_set_temp_conf(st, false, &sleep_temp);
+
out_unlock:
mutex_unlock(&st->lock);
+ /* sleep maximum required time */
+ sleep = max(sleep_sensor, sleep_temp);
+ if (sleep)
+ msleep(sleep);
+
pm_runtime_put_autosuspend(dev);
return ret;
@@ -396,6 +423,7 @@ int inv_icm42607_buffer_fifo_read(struct inv_icm42607_state *st,
{
const void *accel, *gyro, *timestamp;
size_t i, max_count;
+ unsigned int odr;
const s8 *temp;
ssize_t size;
int ret;
@@ -437,7 +465,7 @@ int inv_icm42607_buffer_fifo_read(struct inv_icm42607_state *st,
/* compute number of samples for each sensor */
for (i = 0; i < st->fifo.count; i += size) {
size = inv_icm42607_fifo_decode_packet(&st->fifo.data[i],
- &accel, &gyro, &temp, ×tamp);
+ &accel, &gyro, &temp, ×tamp, &odr);
/* Make sure the size is at least 1 valid packet. */
if (size < INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE)
break;
@@ -454,14 +482,54 @@ int inv_icm42607_buffer_fifo_read(struct inv_icm42607_state *st,
return 0;
}
+int inv_icm42607_buffer_fifo_parse(struct inv_icm42607_state *st)
+{
+ struct inv_icm42607_sensor_state *accel_st = iio_priv(st->indio_accel);
+ struct inv_sensors_timestamp *ts;
+ int ret;
+
+ if (st->fifo.nb.total == 0)
+ return 0;
+
+ /* handle accelerometer timestamp and FIFO data parsing */
+ if (st->fifo.nb.accel > 0) {
+ ts = &accel_st->ts;
+ inv_sensors_timestamp_interrupt(ts, st->fifo.watermark.eff_accel,
+ st->timestamp.accel);
+ ret = inv_icm42607_accel_parse_fifo(st->indio_accel);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
unsigned int count)
{
+ struct inv_icm42607_sensor_state *accel_st = iio_priv(st->indio_accel);
+ struct inv_sensors_timestamp *ts;
+ s64 accel_ts;
int ret;
+ accel_ts = iio_get_time_ns(st->indio_accel);
+
ret = inv_icm42607_buffer_fifo_read(st, count);
+ if (ret)
+ return ret;
- return ret;
+ if (st->fifo.nb.total == 0)
+ return 0;
+
+ if (st->fifo.nb.accel > 0) {
+ ts = &accel_st->ts;
+ inv_sensors_timestamp_interrupt(ts, st->fifo.nb.accel, accel_ts);
+ ret = inv_icm42607_accel_parse_fifo(st->indio_accel);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
}
int inv_icm42607_buffer_init(struct inv_icm42607_state *st)
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
index b77deb66f8bd..4160076bbf77 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.h
@@ -71,7 +71,7 @@ inv_icm42607_fifo_is_data_valid(const struct inv_icm42607_fifo_sensor_data *s)
ssize_t inv_icm42607_fifo_decode_packet(const void *packet, const void **accel,
const void **gyro, const s8 **temp,
- const void **timestamp);
+ const void **timestamp, unsigned int *odr);
extern const struct iio_buffer_setup_ops inv_icm42607_buffer_ops;
@@ -87,6 +87,8 @@ int inv_icm42607_buffer_update_watermark(struct inv_icm42607_state *st);
int inv_icm42607_buffer_fifo_read(struct inv_icm42607_state *st,
unsigned int max);
+int inv_icm42607_buffer_fifo_parse(struct inv_icm42607_state *st);
+
int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
unsigned int count);
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
index cf9d0c6f3f4b..6b623fb679f3 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -75,6 +75,15 @@ const struct inv_icm42607_hw inv_icm42607p_hw_data = {
};
EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607");
+const struct iio_mount_matrix *
+inv_icm42607_get_mount_matrix(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ const struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+
+ return &st->orientation;
+}
+
u32 inv_icm42607_odr_to_period(enum inv_icm42607_odr odr)
{
static const u32 odr_periods[INV_ICM42607_ODR_NB] = {
@@ -152,6 +161,53 @@ static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st,
return 0;
}
+int inv_icm42607_set_accel_conf(struct inv_icm42607_state *st,
+ struct inv_icm42607_sensor_conf *conf,
+ unsigned int *sleep_ms)
+{
+ struct inv_icm42607_sensor_conf *oldconf = &st->conf.accel;
+ unsigned int val;
+ int ret;
+
+ if (conf->mode < 0)
+ conf->mode = oldconf->mode;
+ if (conf->fs < 0)
+ conf->fs = oldconf->fs;
+ if (conf->odr < 0)
+ conf->odr = oldconf->odr;
+ if (conf->filter < 0)
+ conf->filter = oldconf->filter;
+
+ if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) {
+ val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK, conf->fs);
+ val |= FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_ODR_MASK, conf->odr);
+ ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val);
+ if (ret)
+ return ret;
+ oldconf->fs = conf->fs;
+ oldconf->odr = conf->odr;
+ }
+
+ if (conf->filter != oldconf->filter) {
+ if (conf->mode == INV_ICM42607_SENSOR_MODE_LOW_POWER) {
+ val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_AVG_MASK, conf->filter);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1,
+ INV_ICM42607_ACCEL_CONFIG1_AVG_MASK, val);
+ } else {
+ val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK,
+ conf->filter);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1,
+ INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, val);
+ }
+ if (ret)
+ return ret;
+ oldconf->filter = conf->filter;
+ }
+
+ return inv_icm42607_set_pwr_mgmt0(st, st->conf.gyro.mode, conf->mode,
+ st->conf.temp_en, sleep_ms);
+}
+
int inv_icm42607_set_temp_conf(struct inv_icm42607_state *st, bool enable,
unsigned int *sleep_ms)
{
@@ -372,6 +428,11 @@ int inv_icm42607_core_probe(struct regmap *regmap,
pm_runtime_set_autosuspend_delay(dev, INV_ICM42607_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
+ /* Initialize IIO device for Accel */
+ st->indio_accel = inv_icm42607_accel_init(st);
+ if (IS_ERR(st->indio_accel))
+ return PTR_ERR(st->indio_accel);
+
return 0;
}
EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607");
@@ -383,6 +444,8 @@ static int inv_icm42607_suspend(struct device *dev)
guard(mutex)(&st->lock);
+ st->suspended.accel = st->conf.accel.mode;
+ st->suspended.temp = st->conf.temp_en;
if (pm_runtime_suspended(dev))
return 0;
@@ -411,6 +474,7 @@ static int inv_icm42607_suspend(struct device *dev)
static int inv_icm42607_resume(struct device *dev)
{
struct inv_icm42607_state *st = dev_get_drvdata(dev);
+ struct inv_icm42607_sensor_state *accel_st = iio_priv(st->indio_accel);
int ret;
guard(mutex)(&st->lock);
@@ -426,7 +490,15 @@ static int inv_icm42607_resume(struct device *dev)
if (ret)
return ret;
+ /* restore sensors state, noting gyro still not yet supported. */
+ ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
+ st->suspended.accel,
+ st->suspended.temp, NULL);
+ if (ret)
+ return ret;
+
if (st->fifo.on) {
+ inv_sensors_timestamp_reset(&accel_st->ts);
ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1,
INV_ICM42607_FIFO_CONFIG1_MODE);
if (ret)
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH V9 09/11] iio: imu: inv_icm42607: Add IRQ for icm42607
2026-05-30 3:17 [PATCH V9 00/11] Add Invensense ICM42607 Chris Morgan
` (7 preceding siblings ...)
2026-05-30 3:17 ` [PATCH V9 08/11] iio: imu: inv_icm42607: Add Accelerometer for icm42607 Chris Morgan
@ 2026-05-30 3:17 ` Chris Morgan
2026-05-31 12:49 ` Jonathan Cameron
2026-05-30 3:17 ` [PATCH V9 10/11] iio: imu: inv_icm42607: Add Gyroscope to icm42607 Chris Morgan
2026-05-30 3:17 ` [PATCH V9 11/11] arm64: dts: rockchip: Add icm42607p IMU for RG-DS Chris Morgan
10 siblings, 1 reply; 26+ messages in thread
From: Chris Morgan @ 2026-05-30 3:17 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add IRQ support for the icm42607 driver.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 97 ++++++++++++++++++-
1 file changed, 96 insertions(+), 1 deletion(-)
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
index 6b623fb679f3..3c91623dffb2 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -331,6 +331,91 @@ static int inv_icm42607_setup(struct inv_icm42607_state *st,
return inv_icm42607_set_conf(st, st->hw->conf);
}
+static irqreturn_t inv_icm42607_irq_timestamp(int irq, void *_data)
+{
+ struct inv_icm42607_state *st = _data;
+
+ st->timestamp.accel = iio_get_time_ns(st->indio_accel);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t inv_icm42607_irq_handler(int irq, void *_data)
+{
+ struct inv_icm42607_state *st = _data;
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int status;
+ int ret;
+
+ mutex_lock(&st->lock);
+
+ ret = regmap_read(st->map, INV_ICM42607_REG_INT_STATUS, &status);
+ if (ret) {
+ dev_err(dev, "Interrut status read error %d\n", ret);
+ goto out_unlock;
+ }
+
+ if (status & INV_ICM42607_INT_STATUS_FIFO_FULL)
+ dev_warn(dev, "FIFO full data lost!\n");
+
+ if (status & INV_ICM42607_INT_STATUS_FIFO_THS) {
+ mutex_unlock(&st->lock);
+ ret = inv_icm42607_buffer_fifo_read(st, 0);
+ if (ret) {
+ dev_err(dev, "FIFO read error %d\n", ret);
+ goto out_unlock;
+ }
+
+ mutex_lock(&st->lock);
+ ret = inv_icm42607_buffer_fifo_parse(st);
+ if (ret)
+ dev_err(dev, "FIFO parsing error %d\n", ret);
+ }
+
+out_unlock:
+ mutex_unlock(&st->lock);
+ return IRQ_HANDLED;
+}
+
+static int inv_icm42607_irq_init(struct inv_icm42607_state *st, int irq,
+ int irq_type, bool open_drain)
+{
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int val = 0;
+ int ret;
+
+ switch (irq_type) {
+ case IRQF_TRIGGER_RISING:
+ case IRQF_TRIGGER_HIGH:
+ val = INV_ICM42607_INT_CONFIG_INT1_ACTIVE_HIGH;
+ break;
+ default:
+ val = INV_ICM42607_INT_CONFIG_INT1_ACTIVE_LOW;
+ break;
+ }
+
+ switch (irq_type) {
+ case IRQF_TRIGGER_LOW:
+ case IRQF_TRIGGER_HIGH:
+ val |= INV_ICM42607_INT_CONFIG_INT1_LATCHED;
+ break;
+ default:
+ break;
+ }
+
+ if (!open_drain)
+ val |= INV_ICM42607_INT_CONFIG_INT1_PUSH_PULL;
+
+ ret = regmap_write(st->map, INV_ICM42607_REG_INT_CONFIG, val);
+ if (ret)
+ return ret;
+
+ irq_type |= IRQF_ONESHOT;
+ return devm_request_threaded_irq(dev, irq, inv_icm42607_irq_timestamp,
+ inv_icm42607_irq_handler, irq_type,
+ st->hw->name, st);
+}
+
static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st)
{
int ret;
@@ -367,13 +452,18 @@ int inv_icm42607_core_probe(struct regmap *regmap,
{
struct device *dev = regmap_get_device(regmap);
struct inv_icm42607_state *st;
- int irq;
+ int irq, irq_type;
+ bool open_drain;
int ret;
irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT1");
if (irq < 0)
return dev_err_probe(dev, irq, "Unable to get INT1 interrupt\n");
+ irq_type = irq_get_trigger_type(irq);
+
+ open_drain = device_property_read_bool(dev, "drive-open-drain");
+
st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
if (!st)
return -ENOMEM;
@@ -433,6 +523,11 @@ int inv_icm42607_core_probe(struct regmap *regmap,
if (IS_ERR(st->indio_accel))
return PTR_ERR(st->indio_accel);
+ /* Initialize interrupt handling */
+ ret = inv_icm42607_irq_init(st, irq, irq_type, open_drain);
+ if (ret)
+ return ret;
+
return 0;
}
EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607");
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH V9 09/11] iio: imu: inv_icm42607: Add IRQ for icm42607
2026-05-30 3:17 ` [PATCH V9 09/11] iio: imu: inv_icm42607: Add IRQ " Chris Morgan
@ 2026-05-31 12:49 ` Jonathan Cameron
0 siblings, 0 replies; 26+ messages in thread
From: Jonathan Cameron @ 2026-05-31 12:49 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-iio, andy, nuno.sa, dlechner, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
On Fri, 29 May 2026 22:17:36 -0500
Chris Morgan <macroalpha82@gmail.com> wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add IRQ support for the icm42607 driver.
Note I'm only calling out a few things sashiko commented on. Make sure
you verify any others are fixed or false positives.
https://sashiko.dev/#/patchset/20260530031739.109063-1-macroalpha82%40gmail.com
I didn't have anything non sashiko related to add to this patch.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
> ---
> .../iio/imu/inv_icm42607/inv_icm42607_core.c | 97 ++++++++++++++++++-
> 1 file changed, 96 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> index 6b623fb679f3..3c91623dffb2 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> +
> +static irqreturn_t inv_icm42607_irq_handler(int irq, void *_data)
> +{
> + struct inv_icm42607_state *st = _data;
> + struct device *dev = regmap_get_device(st->map);
> + unsigned int status;
> + int ret;
> +
> + mutex_lock(&st->lock);
> +
> + ret = regmap_read(st->map, INV_ICM42607_REG_INT_STATUS, &status);
> + if (ret) {
> + dev_err(dev, "Interrut status read error %d\n", ret);
> + goto out_unlock;
> + }
> +
> + if (status & INV_ICM42607_INT_STATUS_FIFO_FULL)
> + dev_warn(dev, "FIFO full data lost!\n");
> +
> + if (status & INV_ICM42607_INT_STATUS_FIFO_THS) {
> + mutex_unlock(&st->lock);
> + ret = inv_icm42607_buffer_fifo_read(st, 0);
> + if (ret) {
> + dev_err(dev, "FIFO read error %d\n", ret);
> + goto out_unlock;
Sashiko caught this one.
Lock isn't held. This dance is horrible though. Normally we avoid this
by having an unlocked variant of the inner function
__inv_icm42607_buffer_fifo_read() and a locked wrapper without the underscores.
Or push the lock out of there in general and add a __must_hold() marking so
we can detect any paths that don't have the lock.
> + }
> +
> + mutex_lock(&st->lock);
> + ret = inv_icm42607_buffer_fifo_parse(st);
> + if (ret)
> + dev_err(dev, "FIFO parsing error %d\n", ret);
> + }
> +
> +out_unlock:
> + mutex_unlock(&st->lock);
> + return IRQ_HANDLED;
> +}
> +
> static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st)
> {
> int ret;
> @@ -367,13 +452,18 @@ int inv_icm42607_core_probe(struct regmap *regmap,
> {
> struct device *dev = regmap_get_device(regmap);
> struct inv_icm42607_state *st;
> - int irq;
> + int irq, irq_type;
> + bool open_drain;
> int ret;
>
> irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT1");
> if (irq < 0)
> return dev_err_probe(dev, irq, "Unable to get INT1 interrupt\n");
>
> + irq_type = irq_get_trigger_type(irq);
> +
> + open_drain = device_property_read_bool(dev, "drive-open-drain");
> +
> st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
> if (!st)
> return -ENOMEM;
> @@ -433,6 +523,11 @@ int inv_icm42607_core_probe(struct regmap *regmap,
> if (IS_ERR(st->indio_accel))
> return PTR_ERR(st->indio_accel);
>
> + /* Initialize interrupt handling */
> + ret = inv_icm42607_irq_init(st, irq, irq_type, open_drain);
Sashiko asks some stuff about ordering wrt to this call. It is relatively
unusual to register an irq after the driver is exposed to userspace. Tends to
lead to potentially silly races. Can we move it before device registration
and rely on presence checks to handle any interrupts that we see before those
iio devices are registered?
> + if (ret)
> + return ret;
> +
> return 0;
> }
> EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607");
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH V9 10/11] iio: imu: inv_icm42607: Add Gyroscope to icm42607
2026-05-30 3:17 [PATCH V9 00/11] Add Invensense ICM42607 Chris Morgan
` (8 preceding siblings ...)
2026-05-30 3:17 ` [PATCH V9 09/11] iio: imu: inv_icm42607: Add IRQ " Chris Morgan
@ 2026-05-30 3:17 ` Chris Morgan
2026-05-31 12:58 ` Jonathan Cameron
2026-05-30 3:17 ` [PATCH V9 11/11] arm64: dts: rockchip: Add icm42607p IMU for RG-DS Chris Morgan
10 siblings, 1 reply; 26+ messages in thread
From: Chris Morgan @ 2026-05-30 3:17 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add gyroscope functions to the icm42607 driver.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/inv_icm42607/Makefile | 1 +
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 9 +
.../imu/inv_icm42607/inv_icm42607_buffer.c | 27 +-
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 55 +-
.../iio/imu/inv_icm42607/inv_icm42607_gyro.c | 559 ++++++++++++++++++
5 files changed, 647 insertions(+), 4 deletions(-)
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile
index e908d77c4219..fc66e580fe99 100644
--- a/drivers/iio/imu/inv_icm42607/Makefile
+++ b/drivers/iio/imu/inv_icm42607/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o
inv-icm42607-y += inv_icm42607_core.o
+inv-icm42607-y += inv_icm42607_gyro.o
inv-icm42607-y += inv_icm42607_accel.o
inv-icm42607-y += inv_icm42607_buffer.o
inv-icm42607-y += inv_icm42607_temp.o
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
index 5a649567ca62..b1c7bc98758b 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -156,6 +156,7 @@ struct inv_icm42607_state {
struct iio_dev *indio_gyro;
struct iio_dev *indio_accel;
struct {
+ s64 gyro;
s64 accel;
} timestamp;
struct inv_icm42607_fifo fifo;
@@ -413,6 +414,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);
@@ -420,6 +425,10 @@ int inv_icm42607_core_probe(struct regmap *regmap,
const struct inv_icm42607_hw *hw,
inv_icm42607_bus_setup bus_setup);
+struct iio_dev *inv_icm42607_gyro_init(struct inv_icm42607_state *st);
+
+int inv_icm42607_gyro_parse_fifo(struct iio_dev *indio_dev);
+
struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st);
int inv_icm42607_accel_parse_fifo(struct iio_dev *indio_dev);
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
index 5b69bf895b35..c45239613344 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
@@ -389,7 +389,9 @@ static int inv_icm42607_buffer_postdisable(struct iio_dev *indio_dev)
goto out_unlock;
conf.mode = INV_ICM42607_SENSOR_MODE_OFF;
- if (sensor != INV_ICM42607_SENSOR_GYRO)
+ if (sensor == INV_ICM42607_SENSOR_GYRO)
+ ret = inv_icm42607_set_gyro_conf(st, &conf, &sleep_sensor);
+ else
ret = inv_icm42607_set_accel_conf(st, &conf, &sleep_sensor);
if (ret)
goto out_unlock;
@@ -484,6 +486,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;
@@ -491,6 +494,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;
@@ -507,12 +520,14 @@ int inv_icm42607_buffer_fifo_parse(struct inv_icm42607_state *st)
int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
unsigned int count)
{
+ struct inv_icm42607_sensor_state *gyro_st = iio_priv(st->indio_gyro);
struct inv_icm42607_sensor_state *accel_st = iio_priv(st->indio_accel);
struct inv_sensors_timestamp *ts;
- s64 accel_ts;
+ s64 gyro_ts, accel_ts;
int ret;
accel_ts = iio_get_time_ns(st->indio_accel);
+ gyro_ts = iio_get_time_ns(st->indio_gyro);
ret = inv_icm42607_buffer_fifo_read(st, count);
if (ret)
@@ -521,6 +536,14 @@ int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
if (st->fifo.nb.total == 0)
return 0;
+ if (st->fifo.nb.gyro > 0) {
+ ts = &gyro_st->ts;
+ inv_sensors_timestamp_interrupt(ts, st->fifo.nb.gyro, gyro_ts);
+ ret = inv_icm42607_gyro_parse_fifo(st->indio_gyro);
+ if (ret)
+ return ret;
+ }
+
if (st->fifo.nb.accel > 0) {
ts = &accel_st->ts;
inv_sensors_timestamp_interrupt(ts, st->fifo.nb.accel, accel_ts);
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
index 3c91623dffb2..cc564fb94089 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -208,6 +208,49 @@ int inv_icm42607_set_accel_conf(struct inv_icm42607_state *st,
st->conf.temp_en, sleep_ms);
}
+int inv_icm42607_set_gyro_conf(struct inv_icm42607_state *st,
+ struct inv_icm42607_sensor_conf *conf,
+ unsigned int *sleep_ms)
+{
+ struct inv_icm42607_sensor_conf *oldconf = &st->conf.gyro;
+ unsigned int val;
+ int ret;
+
+ if (conf->mode < 0)
+ conf->mode = oldconf->mode;
+ if (conf->fs < 0)
+ conf->fs = oldconf->fs;
+ if (conf->odr < 0)
+ conf->odr = oldconf->odr;
+ if (conf->filter < 0)
+ conf->filter = oldconf->filter;
+
+ if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) {
+ val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK,
+ conf->fs);
+ val |= FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_ODR_MASK,
+ conf->odr);
+ ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val);
+ if (ret)
+ return ret;
+ oldconf->fs = conf->fs;
+ oldconf->odr = conf->odr;
+ }
+
+ if (conf->filter != oldconf->filter) {
+ val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG1_FILTER_MASK,
+ conf->filter);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_GYRO_CONFIG1,
+ INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, val);
+ if (ret)
+ return ret;
+ oldconf->filter = conf->filter;
+ }
+
+ return inv_icm42607_set_pwr_mgmt0(st, conf->mode, st->conf.accel.mode,
+ st->conf.temp_en, sleep_ms);
+}
+
int inv_icm42607_set_temp_conf(struct inv_icm42607_state *st, bool enable,
unsigned int *sleep_ms)
{
@@ -523,6 +566,11 @@ int inv_icm42607_core_probe(struct regmap *regmap,
if (IS_ERR(st->indio_accel))
return PTR_ERR(st->indio_accel);
+ /* Initialize IIO device for Gyro */
+ st->indio_gyro = inv_icm42607_gyro_init(st);
+ if (IS_ERR(st->indio_gyro))
+ return PTR_ERR(st->indio_gyro);
+
/* Initialize interrupt handling */
ret = inv_icm42607_irq_init(st, irq, irq_type, open_drain);
if (ret)
@@ -539,6 +587,7 @@ static int inv_icm42607_suspend(struct device *dev)
guard(mutex)(&st->lock);
+ st->suspended.gyro = st->conf.gyro.mode;
st->suspended.accel = st->conf.accel.mode;
st->suspended.temp = st->conf.temp_en;
if (pm_runtime_suspended(dev))
@@ -569,6 +618,7 @@ static int inv_icm42607_suspend(struct device *dev)
static int inv_icm42607_resume(struct device *dev)
{
struct inv_icm42607_state *st = dev_get_drvdata(dev);
+ struct inv_icm42607_sensor_state *gyro_st = iio_priv(st->indio_gyro);
struct inv_icm42607_sensor_state *accel_st = iio_priv(st->indio_accel);
int ret;
@@ -585,14 +635,15 @@ static int inv_icm42607_resume(struct device *dev)
if (ret)
return ret;
- /* restore sensors state, noting gyro still not yet supported. */
- ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
+ /* restore sensors state */
+ 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);
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
new file mode 100644
index 000000000000..8d59156086b1
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
@@ -0,0 +1,559 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/math64.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <linux/iio/common/inv_sensors_timestamp.h>
+#include "inv_icm42607.h"
+#include "inv_icm42607_temp.h"
+#include "inv_icm42607_buffer.h"
+
+#define INV_ICM42607_GYRO_CHAN(_modifier, _index, _ext_info) \
+{ \
+ .type = IIO_ANGL_VEL, \
+ .modified = 1, \
+ .channel2 = _modifier, \
+ .info_mask_separate = \
+ BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+ .ext_info = _ext_info, \
+}
+
+enum inv_icm42607_gyro_scan {
+ INV_ICM42607_GYRO_SCAN_X,
+ INV_ICM42607_GYRO_SCAN_Y,
+ INV_ICM42607_GYRO_SCAN_Z,
+ INV_ICM42607_GYRO_SCAN_TEMP,
+ INV_ICM42607_GYRO_SCAN_TIMESTAMP,
+};
+
+static const struct iio_chan_spec_ext_info inv_icm42607_gyro_ext_infos[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42607_get_mount_matrix),
+ { },
+};
+
+static const struct iio_chan_spec inv_icm42607_gyro_channels[] = {
+ INV_ICM42607_GYRO_CHAN(IIO_MOD_X, INV_ICM42607_GYRO_SCAN_X,
+ inv_icm42607_gyro_ext_infos),
+ INV_ICM42607_GYRO_CHAN(IIO_MOD_Y, INV_ICM42607_GYRO_SCAN_Y,
+ inv_icm42607_gyro_ext_infos),
+ INV_ICM42607_GYRO_CHAN(IIO_MOD_Z, INV_ICM42607_GYRO_SCAN_Z,
+ inv_icm42607_gyro_ext_infos),
+ INV_ICM42607_TEMP_CHAN(INV_ICM42607_GYRO_SCAN_TEMP),
+ IIO_CHAN_SOFT_TIMESTAMP(INV_ICM42607_GYRO_SCAN_TIMESTAMP),
+};
+
+/*
+ * IIO buffer data: size must be a power of 2 and timestamp aligned
+ * 16 bytes: 6 bytes angular velocity, 2 bytes temperature, 8 bytes timestamp
+ */
+struct inv_icm42607_gyro_buffer {
+ struct inv_icm42607_fifo_sensor_data gyro;
+ s16 temp;
+ aligned_s64 timestamp;
+};
+
+#define INV_ICM42607_SCAN_MASK_GYRO_3AXIS \
+ (BIT(INV_ICM42607_GYRO_SCAN_X) | \
+ BIT(INV_ICM42607_GYRO_SCAN_Y) | \
+ BIT(INV_ICM42607_GYRO_SCAN_Z))
+
+#define INV_ICM42607_SCAN_MASK_TEMP BIT(INV_ICM42607_GYRO_SCAN_TEMP)
+
+static const unsigned long inv_icm42607_gyro_scan_masks[] = {
+ INV_ICM42607_SCAN_MASK_GYRO_3AXIS,
+ INV_ICM42607_SCAN_MASK_GYRO_3AXIS | INV_ICM42607_SCAN_MASK_TEMP,
+ 0
+};
+
+/* enable gyroscope sensor and FIFO write */
+static int inv_icm42607_gyro_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ unsigned int fifo_en = 0;
+ unsigned int sleep_gyro = 0;
+ unsigned int sleep_temp = 0;
+ int ret;
+
+ mutex_lock(&st->lock);
+
+ if (*scan_mask & INV_ICM42607_SCAN_MASK_TEMP) {
+ /* enable temp sensor */
+ ret = inv_icm42607_set_temp_conf(st, true, &sleep_temp);
+ if (ret)
+ goto out_unlock;
+ fifo_en |= INV_ICM42607_SENSOR_TEMP;
+ }
+
+ if (*scan_mask & INV_ICM42607_SCAN_MASK_GYRO_3AXIS) {
+ /* enable gyro sensor */
+ conf.mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE;
+ ret = inv_icm42607_set_gyro_conf(st, &conf, &sleep_gyro);
+ if (ret)
+ goto out_unlock;
+ fifo_en |= INV_ICM42607_SENSOR_GYRO;
+ }
+
+ /* update data FIFO write */
+ ret = inv_icm42607_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
+ if (ret)
+ goto out_unlock;
+
+out_unlock:
+ mutex_unlock(&st->lock);
+ /* sleep maximum required time */
+ msleep(max(sleep_gyro, sleep_temp));
+ return ret;
+}
+
+static int inv_icm42607_gyro_read_sensor(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ s16 *val)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_state *gyro_st = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ unsigned int reg;
+ __be16 *data;
+ int ret;
+
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ reg = INV_ICM42607_REG_GYRO_DATA_X1;
+ break;
+ case IIO_MOD_Y:
+ reg = INV_ICM42607_REG_GYRO_DATA_Y1;
+ break;
+ case IIO_MOD_Z:
+ reg = INV_ICM42607_REG_GYRO_DATA_Z1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ /* enable gyro sensor */
+ conf.mode = gyro_st->power_mode;
+ ret = inv_icm42607_set_gyro_conf(st, &conf, NULL);
+ if (ret)
+ return ret;
+
+ /* read gyro register data */
+ data = &st->buffer[0];
+ ret = regmap_bulk_read(st->map, reg, data, sizeof(*data));
+ if (ret)
+ return ret;
+
+ *val = be16_to_cpup(data);
+ if (*val == INV_ICM42607_DATA_INVALID)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const int inv_icm42607_gyro_scale_nano[][2] = {
+ [INV_ICM42607_GYRO_FS_2000DPS] = { 0, 1065264 },
+ [INV_ICM42607_GYRO_FS_1000DPS] = { 0, 532632 },
+ [INV_ICM42607_GYRO_FS_500DPS] = { 0, 266316 },
+ [INV_ICM42607_GYRO_FS_250DPS] = { 0, 133158 },
+};
+
+static int inv_icm42607_gyro_read_scale(struct iio_dev *indio_dev,
+ int *val, int *val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ unsigned int idx;
+
+ idx = st->conf.gyro.fs;
+
+ *val = inv_icm42607_gyro_scale_nano[idx][0];
+ *val2 = inv_icm42607_gyro_scale_nano[idx][1];
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int inv_icm42607_gyro_write_scale(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ size_t scales_len = ARRAY_SIZE(inv_icm42607_gyro_scale_nano);
+ int ret;
+
+ for (idx = 0; idx < scales_len; idx++) {
+ if (val == inv_icm42607_gyro_scale_nano[idx][0] &&
+ val2 == inv_icm42607_gyro_scale_nano[idx][1])
+ break;
+ }
+ if (idx >= scales_len)
+ return -EINVAL;
+
+ conf.fs = idx;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ return inv_icm42607_set_gyro_conf(st, &conf, NULL);
+}
+
+static const int inv_icm42607_gyro_odr[][2] = {
+ [INV_ICM42607_ODR_12_5HZ] = { 12, 500000 },
+ [INV_ICM42607_ODR_25HZ] = { 25, 0 },
+ [INV_ICM42607_ODR_50HZ] = { 50, 0 },
+ [INV_ICM42607_ODR_100HZ] = { 100, 0 },
+ [INV_ICM42607_ODR_200HZ] = { 200, 0 },
+ [INV_ICM42607_ODR_400HZ] = { 400, 0 },
+ [INV_ICM42607_ODR_800HZ] = { 800, 0 },
+ [INV_ICM42607_ODR_1600HZ] = { 1600, 0 },
+};
+
+static int inv_icm42607_gyro_read_odr(struct inv_icm42607_state *st,
+ int *val, int *val2)
+{
+ unsigned int odr;
+ unsigned int i;
+
+ odr = st->conf.gyro.odr;
+
+ for (i = 5; i < ARRAY_SIZE(inv_icm42607_gyro_odr); ++i) {
+ if (i == odr)
+ break;
+ }
+ if (i >= ARRAY_SIZE(inv_icm42607_gyro_odr))
+ return -EINVAL;
+
+ *val = inv_icm42607_gyro_odr[i][0];
+ *val2 = inv_icm42607_gyro_odr[i][1];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int inv_icm42607_gyro_write_odr(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_state *gyro_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &gyro_st->ts;
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ int ret;
+
+ for (idx = 5; idx < ARRAY_SIZE(inv_icm42607_gyro_odr); ++idx) {
+ if (val == inv_icm42607_gyro_odr[idx][0] &&
+ val2 == inv_icm42607_gyro_odr[idx][1])
+ break;
+ }
+ if (idx >= ARRAY_SIZE(inv_icm42607_gyro_odr))
+ return -EINVAL;
+
+ conf.odr = idx;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ ret = inv_sensors_timestamp_update_odr(ts, inv_icm42607_odr_to_period(conf.odr),
+ iio_buffer_enabled(indio_dev));
+ if (ret)
+ return ret;
+
+ ret = inv_icm42607_set_gyro_conf(st, &conf, NULL);
+ if (ret)
+ return ret;
+
+ inv_icm42607_buffer_update_fifo_period(st);
+ inv_icm42607_buffer_update_watermark(st);
+
+ return 0;
+}
+
+static int inv_icm42607_gyro_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ s16 data;
+ int ret;
+
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ break;
+ case IIO_TEMP:
+ return inv_icm42607_temp_read_raw(indio_dev, chan, val, val2, mask);
+ default:
+ return -EINVAL;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = inv_icm42607_gyro_read_sensor(indio_dev, chan, &data);
+ iio_device_release_direct(indio_dev);
+ if (ret)
+ return ret;
+ *val = data;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ return inv_icm42607_gyro_read_scale(indio_dev, val, val2);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm42607_gyro_read_odr(st, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_gyro_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals,
+ int *type, int *length, long mask)
+{
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *vals = (const int *)inv_icm42607_gyro_scale_nano;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *length = ARRAY_SIZE(inv_icm42607_gyro_scale_nano) * 2;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = (const int *)inv_icm42607_gyro_odr[5];
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *length = (ARRAY_SIZE(inv_icm42607_gyro_odr) - 5) * 2;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_gyro_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret;
+
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = inv_icm42607_gyro_write_scale(indio_dev, val, val2);
+ iio_device_release_direct(indio_dev);
+ return ret;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm42607_gyro_write_odr(indio_dev, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_gyro_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_gyro_hwfifo_set_watermark(struct iio_dev *indio_dev,
+ unsigned int val)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ int ret;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ st->fifo.watermark.gyro = val;
+ return inv_icm42607_buffer_update_watermark(st);
+}
+
+static int inv_icm42607_gyro_hwfifo_flush(struct iio_dev *indio_dev,
+ unsigned int count)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ int ret;
+
+ if (count == 0)
+ return 0;
+
+ /* Make sure we don't try to count more than the buffer can hold. */
+ count = min(count, sizeof(st->fifo.data));
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ ret = inv_icm42607_buffer_hwfifo_flush(st, count);
+ if (ret)
+ return ret;
+
+ return st->fifo.nb.gyro;
+}
+
+static const struct iio_info inv_icm42607_gyro_info = {
+ .read_raw = inv_icm42607_gyro_read_raw,
+ .read_avail = inv_icm42607_gyro_read_avail,
+ .write_raw = inv_icm42607_gyro_write_raw,
+ .write_raw_get_fmt = inv_icm42607_gyro_write_raw_get_fmt,
+ .update_scan_mode = inv_icm42607_gyro_update_scan_mode,
+ .hwfifo_set_watermark = inv_icm42607_gyro_hwfifo_set_watermark,
+ .hwfifo_flush_to_buffer = inv_icm42607_gyro_hwfifo_flush,
+};
+
+struct iio_dev *inv_icm42607_gyro_init(struct inv_icm42607_state *st)
+{
+ struct device *dev = regmap_get_device(st->map);
+ const char *name;
+ struct inv_icm42607_sensor_state *gyro_st;
+ struct inv_sensors_timestamp_chip ts_chip;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->hw->name);
+ if (!name)
+ return ERR_PTR(-ENOMEM);
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*gyro_st));
+ if (!indio_dev)
+ return ERR_PTR(-ENOMEM);
+ gyro_st = iio_priv(indio_dev);
+
+ gyro_st->power_mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE;
+
+ /*
+ * clock period is 32kHz (31250ns)
+ * jitter is +/- 2% (20 per mille)
+ */
+ ts_chip.clock_period = 31250;
+ ts_chip.jitter = 20;
+ ts_chip.init_period = inv_icm42607_odr_to_period(st->conf.gyro.odr);
+ inv_sensors_timestamp_init(&gyro_st->ts, &ts_chip);
+
+ iio_device_set_drvdata(indio_dev, st);
+ indio_dev->name = name;
+ indio_dev->info = &inv_icm42607_gyro_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = inv_icm42607_gyro_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_icm42607_gyro_channels);
+ indio_dev->available_scan_masks = inv_icm42607_gyro_scan_masks;
+ indio_dev->setup_ops = &inv_icm42607_buffer_ops;
+
+ ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
+ &inv_icm42607_buffer_ops);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return indio_dev;
+}
+
+int inv_icm42607_gyro_parse_fifo(struct iio_dev *indio_dev)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_state *gyro_st = iio_priv(indio_dev);
+ struct inv_sensors_timestamp *ts = &gyro_st->ts;
+ ssize_t i, size;
+ unsigned int no;
+ const void *accel, *gyro, *timestamp;
+ const s8 *temp;
+ unsigned int odr;
+ s64 ts_val;
+ struct inv_icm42607_gyro_buffer buffer = { };
+
+ guard(mutex)(&st->lock);
+
+ /* parse all fifo packets */
+ for (i = 0, no = 0; i < st->fifo.count; i += size, ++no) {
+ size = inv_icm42607_fifo_decode_packet(&st->fifo.data[i],
+ &accel, &gyro, &temp, ×tamp, &odr);
+ /* quit if error or FIFO is empty */
+ if (size <= 0)
+ return size;
+
+ /* If the packet size could cause us to overflow, return. */
+ if (i + size > st->fifo.count)
+ return -EIO;
+
+ /* skip packet if no gyro data or data is invalid */
+ if (gyro == NULL || !inv_icm42607_fifo_is_data_valid(gyro))
+ continue;
+
+ /* update odr */
+ if (odr & INV_ICM42607_SENSOR_GYRO)
+ inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
+ st->fifo.nb.total, no);
+
+ memcpy(&buffer.gyro, gyro, sizeof(buffer.gyro));
+ /* convert 8 bits FIFO temperature in high resolution format */
+ buffer.temp = temp ? (*temp * 64) : 0;
+ ts_val = inv_sensors_timestamp_pop(ts);
+ iio_push_to_buffers_with_ts(indio_dev, &buffer,
+ sizeof(buffer), ts_val);
+ }
+
+ return 0;
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH V9 10/11] iio: imu: inv_icm42607: Add Gyroscope to icm42607
2026-05-30 3:17 ` [PATCH V9 10/11] iio: imu: inv_icm42607: Add Gyroscope to icm42607 Chris Morgan
@ 2026-05-31 12:58 ` Jonathan Cameron
2026-06-01 14:37 ` Chris Morgan
0 siblings, 1 reply; 26+ messages in thread
From: Jonathan Cameron @ 2026-05-31 12:58 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-iio, andy, nuno.sa, dlechner, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
On Fri, 29 May 2026 22:17:37 -0500
Chris Morgan <macroalpha82@gmail.com> wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add gyroscope functions to the icm42607 driver.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Hi Chris,
Various things inline.
Thanks,
Jonathan
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> index 5b69bf895b35..c45239613344 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> 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;
> @@ -491,6 +494,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;
> @@ -507,12 +520,14 @@ int inv_icm42607_buffer_fifo_parse(struct inv_icm42607_state *st)
> int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
> unsigned int count)
> {
> + struct inv_icm42607_sensor_state *gyro_st = iio_priv(st->indio_gyro);
> struct inv_icm42607_sensor_state *accel_st = iio_priv(st->indio_accel);
> struct inv_sensors_timestamp *ts;
> - s64 accel_ts;
> + s64 gyro_ts, accel_ts;
> int ret;
>
> accel_ts = iio_get_time_ns(st->indio_accel);
> + gyro_ts = iio_get_time_ns(st->indio_gyro);
Sashiko calls out correctly that there is a race with devices finishing
coming up that might be hit here. Probably need some sort of presence
check and locking to be sure those are both valid.
>
> ret = inv_icm42607_buffer_fifo_read(st, count);
> if (ret)
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
> new file mode 100644
> index 000000000000..8d59156086b1
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
> +
> +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),
> + { },
No comma. Check for any other commas after terminating entries like this one.
> +};
> +
> +int inv_icm42607_gyro_parse_fifo(struct iio_dev *indio_dev)
> +{
> + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
> + struct inv_icm42607_sensor_state *gyro_st = iio_priv(indio_dev);
> + struct inv_sensors_timestamp *ts = &gyro_st->ts;
> + ssize_t i, size;
> + unsigned int no;
> + const void *accel, *gyro, *timestamp;
> + const s8 *temp;
> + unsigned int odr;
> + s64 ts_val;
> + struct inv_icm42607_gyro_buffer buffer = { };
> +
> + guard(mutex)(&st->lock);
Sashiko thinks there is a deadlock here as this lock will already
be held. Report looks correct to me + same bug in the accelerometer case.
I'm surprised you didn't see them in testing as deadlocks tend to be obvious!
> +
> + /* parse all fifo packets */
> + for (i = 0, no = 0; i < st->fifo.count; i += size, ++no) {
> + size = inv_icm42607_fifo_decode_packet(&st->fifo.data[i],
> + &accel, &gyro, &temp, ×tamp, &odr);
> + /* quit if error or FIFO is empty */
> + if (size <= 0)
> + return size;
> +
> + /* If the packet size could cause us to overflow, return. */
> + if (i + size > st->fifo.count)
> + return -EIO;
> +
> + /* skip packet if no gyro data or data is invalid */
> + if (gyro == NULL || !inv_icm42607_fifo_is_data_valid(gyro))
> + continue;
> +
> + /* update odr */
> + if (odr & INV_ICM42607_SENSOR_GYRO)
> + inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
> + st->fifo.nb.total, no);
> +
> + memcpy(&buffer.gyro, gyro, sizeof(buffer.gyro));
> + /* convert 8 bits FIFO temperature in high resolution format */
> + buffer.temp = temp ? (*temp * 64) : 0;
> + ts_val = inv_sensors_timestamp_pop(ts);
> + iio_push_to_buffers_with_ts(indio_dev, &buffer,
> + sizeof(buffer), ts_val);
> + }
> +
> + return 0;
> +}
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH V9 10/11] iio: imu: inv_icm42607: Add Gyroscope to icm42607
2026-05-31 12:58 ` Jonathan Cameron
@ 2026-06-01 14:37 ` Chris Morgan
2026-06-01 16:39 ` Jonathan Cameron
0 siblings, 1 reply; 26+ messages in thread
From: Chris Morgan @ 2026-06-01 14:37 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Chris Morgan, linux-iio, andy, nuno.sa, dlechner,
jean-baptiste.maneyrol, linux-rockchip, devicetree, heiko,
conor+dt, krzk+dt, robh, andriy.shevchenko
On Sun, May 31, 2026 at 01:58:23PM +0100, Jonathan Cameron wrote:
> On Fri, 29 May 2026 22:17:37 -0500
> Chris Morgan <macroalpha82@gmail.com> wrote:
>
> > From: Chris Morgan <macromorgan@hotmail.com>
> >
> > Add gyroscope functions to the icm42607 driver.
> >
> > Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
>
> Hi Chris,
> Various things inline.
>
> Thanks,
>
> Jonathan
>
> > diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> > index 5b69bf895b35..c45239613344 100644
> > --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> > +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
>
> > 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;
> > @@ -491,6 +494,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;
> > @@ -507,12 +520,14 @@ int inv_icm42607_buffer_fifo_parse(struct inv_icm42607_state *st)
> > int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
> > unsigned int count)
> > {
> > + struct inv_icm42607_sensor_state *gyro_st = iio_priv(st->indio_gyro);
> > struct inv_icm42607_sensor_state *accel_st = iio_priv(st->indio_accel);
> > struct inv_sensors_timestamp *ts;
> > - s64 accel_ts;
> > + s64 gyro_ts, accel_ts;
> > int ret;
> >
> > accel_ts = iio_get_time_ns(st->indio_accel);
> > + gyro_ts = iio_get_time_ns(st->indio_gyro);
>
> Sashiko calls out correctly that there is a race with devices finishing
> coming up that might be hit here. Probably need some sort of presence
> check and locking to be sure those are both valid.
>
While I'm not sure how to fix this exactly, I think (attempting to)
test deadlocks I found a bigger issue... I don't think my interrupt
line is hooked up at all...
Assuming I should be getting interrupts when enabling the buffer
(and also assuming I can do that with sysfs) I'm not getting any calls
to the IRQ routine.
Would you know a better way to test the hardware buffers? Thus far
my tests for data correctness involved either `monitor-sensor -a`
or reading the values directly from sysfs.
Assuming I am in fact working with a device with no interrupt line, I
can just modify this to remove the hardware buffer stuff and the IRQ
stuff and work with an even more simplified driver. Probably going to
miss the 7.2 merge window at this time, but still...
>
>
> >
> > ret = inv_icm42607_buffer_fifo_read(st, count);
> > if (ret)
>
>
> > diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
> > new file mode 100644
> > index 000000000000..8d59156086b1
> > --- /dev/null
> > +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
>
> > +
> > +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),
> > + { },
> No comma. Check for any other commas after terminating entries like this one.
> > +};
>
>
> > +
> > +int inv_icm42607_gyro_parse_fifo(struct iio_dev *indio_dev)
> > +{
> > + struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
> > + struct inv_icm42607_sensor_state *gyro_st = iio_priv(indio_dev);
> > + struct inv_sensors_timestamp *ts = &gyro_st->ts;
> > + ssize_t i, size;
> > + unsigned int no;
> > + const void *accel, *gyro, *timestamp;
> > + const s8 *temp;
> > + unsigned int odr;
> > + s64 ts_val;
> > + struct inv_icm42607_gyro_buffer buffer = { };
> > +
> > + guard(mutex)(&st->lock);
> Sashiko thinks there is a deadlock here as this lock will already
> be held. Report looks correct to me + same bug in the accelerometer case.
>
> I'm surprised you didn't see them in testing as deadlocks tend to be obvious!
>
Yeah... about that... turns out my IRQ wasn't firing correctly so I was never
hitting this path. I suspect the line isn't even hooked up!
>
> > +
> > + /* parse all fifo packets */
> > + for (i = 0, no = 0; i < st->fifo.count; i += size, ++no) {
> > + size = inv_icm42607_fifo_decode_packet(&st->fifo.data[i],
> > + &accel, &gyro, &temp, ×tamp, &odr);
> > + /* quit if error or FIFO is empty */
> > + if (size <= 0)
> > + return size;
> > +
> > + /* If the packet size could cause us to overflow, return. */
> > + if (i + size > st->fifo.count)
> > + return -EIO;
> > +
> > + /* skip packet if no gyro data or data is invalid */
> > + if (gyro == NULL || !inv_icm42607_fifo_is_data_valid(gyro))
> > + continue;
> > +
> > + /* update odr */
> > + if (odr & INV_ICM42607_SENSOR_GYRO)
> > + inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
> > + st->fifo.nb.total, no);
> > +
> > + memcpy(&buffer.gyro, gyro, sizeof(buffer.gyro));
> > + /* convert 8 bits FIFO temperature in high resolution format */
> > + buffer.temp = temp ? (*temp * 64) : 0;
> > + ts_val = inv_sensors_timestamp_pop(ts);
> > + iio_push_to_buffers_with_ts(indio_dev, &buffer,
> > + sizeof(buffer), ts_val);
> > + }
> > +
> > + return 0;
> > +}
>
Thank you again for all your help.
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH V9 10/11] iio: imu: inv_icm42607: Add Gyroscope to icm42607
2026-06-01 14:37 ` Chris Morgan
@ 2026-06-01 16:39 ` Jonathan Cameron
0 siblings, 0 replies; 26+ messages in thread
From: Jonathan Cameron @ 2026-06-01 16:39 UTC (permalink / raw)
To: Chris Morgan
Cc: Chris Morgan, linux-iio, andy, nuno.sa, dlechner,
jean-baptiste.maneyrol, linux-rockchip, devicetree, heiko,
conor+dt, krzk+dt, robh, andriy.shevchenko
On Mon, 1 Jun 2026 09:37:56 -0500
Chris Morgan <macromorgan@hotmail.com> wrote:
> On Sun, May 31, 2026 at 01:58:23PM +0100, Jonathan Cameron wrote:
> > On Fri, 29 May 2026 22:17:37 -0500
> > Chris Morgan <macroalpha82@gmail.com> wrote:
> >
> > > From: Chris Morgan <macromorgan@hotmail.com>
> > >
> > > Add gyroscope functions to the icm42607 driver.
> > >
> > > Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
> >
> > Hi Chris,
> > Various things inline.
> >
> > Thanks,
> >
> > Jonathan
> >
> > > diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> > > index 5b69bf895b35..c45239613344 100644
> > > --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> > > +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_buffer.c
> >
> > > 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;
> > > @@ -491,6 +494,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;
> > > @@ -507,12 +520,14 @@ int inv_icm42607_buffer_fifo_parse(struct inv_icm42607_state *st)
> > > int inv_icm42607_buffer_hwfifo_flush(struct inv_icm42607_state *st,
> > > unsigned int count)
> > > {
> > > + struct inv_icm42607_sensor_state *gyro_st = iio_priv(st->indio_gyro);
> > > struct inv_icm42607_sensor_state *accel_st = iio_priv(st->indio_accel);
> > > struct inv_sensors_timestamp *ts;
> > > - s64 accel_ts;
> > > + s64 gyro_ts, accel_ts;
> > > int ret;
> > >
> > > accel_ts = iio_get_time_ns(st->indio_accel);
> > > + gyro_ts = iio_get_time_ns(st->indio_gyro);
> >
> > Sashiko calls out correctly that there is a race with devices finishing
> > coming up that might be hit here. Probably need some sort of presence
> > check and locking to be sure those are both valid.
> >
>
> While I'm not sure how to fix this exactly, I think (attempting to)
> test deadlocks I found a bigger issue... I don't think my interrupt
> line is hooked up at all...
ouch. Wouldn't be the first time a given device has saved on pins
to the extent of not wiring any interrupts.
>
> Assuming I should be getting interrupts when enabling the buffer
> (and also assuming I can do that with sysfs) I'm not getting any calls
> to the IRQ routine.
>
> Would you know a better way to test the hardware buffers? Thus far
> my tests for data correctness involved either `monitor-sensor -a`
> or reading the values directly from sysfs.
There is a basic buffer example in the kernel tree under tools/iio
that should work for this. iio_generic_buffer
>
> Assuming I am in fact working with a device with no interrupt line, I
> can just modify this to remove the hardware buffer stuff and the IRQ
> stuff and work with an even more simplified driver. Probably going to
> miss the 7.2 merge window at this time, but still...
Indeed an option to do this as a temporary step. If we get
other users hopefully someone has the interrupt line wired!
Jonathan
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH V9 11/11] arm64: dts: rockchip: Add icm42607p IMU for RG-DS
2026-05-30 3:17 [PATCH V9 00/11] Add Invensense ICM42607 Chris Morgan
` (9 preceding siblings ...)
2026-05-30 3:17 ` [PATCH V9 10/11] iio: imu: inv_icm42607: Add Gyroscope to icm42607 Chris Morgan
@ 2026-05-30 3:17 ` Chris Morgan
10 siblings, 0 replies; 26+ messages in thread
From: Chris Morgan @ 2026-05-30 3:17 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add the Invensense ICM42607P IMU for the Anbernic RG-DS. Mount-matrix
was tested with iio-sensor-proxy and reports correct orientation.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
.../dts/rockchip/rk3568-anbernic-rg-ds.dts | 20 ++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/rockchip/rk3568-anbernic-rg-ds.dts b/arch/arm64/boot/dts/rockchip/rk3568-anbernic-rg-ds.dts
index 8d906ab02c5f..875ca884deca 100644
--- a/arch/arm64/boot/dts/rockchip/rk3568-anbernic-rg-ds.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3568-anbernic-rg-ds.dts
@@ -871,7 +871,18 @@ aw87391_pa_r: audio-codec@5b {
sound-name-prefix = "Right Amp";
};
- /* invensense,icm42607p at 0x68 */
+ icm42607p: imu@68 {
+ compatible = "invensense,icm42607p";
+ reg = <0x68>;
+ interrupt-names = "INT1";
+ interrupt-parent = <&gpio0>;
+ interrupts = <RK_PD6 IRQ_TYPE_EDGE_FALLING>;
+ mount-matrix = "-1", "0", "0",
+ "0", "1", "0",
+ "0", "0", "-1";
+ pinctrl-0 = <&accel_irq>;
+ pinctrl-names = "default";
+ };
};
&i2c3 {
@@ -932,6 +943,13 @@ &i2s1_8ch {
};
&pinctrl {
+ accel {
+ accel_irq: accel-irq {
+ rockchip,pins =
+ <0 RK_PD6 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
gpio-keys {
vol_keys_l: vol-keys_l {
rockchip,pins =
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread