From nobody Sat Sep 13 22:31:04 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A3FF726FDA9; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755699885; cv=none; b=lEKbtT8mJBI6auxvp7n91T5axnlXMabv/0EbCI9jOkeOmn7IG+3fIzDSU3UBVfXOW4YoLNJR9Qv2WsppD1PYEzxc6aoZt0m8Q7TIps9x34mWBf3dyZTqM8SAZu47TasoCR8/qh3sGceMeVw/UaMZz8+3gaWobtrNhoXy2kvrKzo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755699885; c=relaxed/simple; bh=43Hd4T+6Nuk347DonpknX8Z7snBYArgegOMZAKI7kCI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=MjpPSFNGH43rHybn9p6lf/1D4DXSqg0JA6wgu8/9wNJGhJ/j5VxFvff/exXWn1/oqZwoNQsylqR1hIhKnHTuipm4Sjm4A1F2MJHScDCbWClVWLCG6l6+le3tm8FrONYFxVsQ0zPpSp+y7LMv7HIaI11b/ieH2jPxPJLoM5LJVoI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=QrA2NEm3; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="QrA2NEm3" Received: by smtp.kernel.org (Postfix) with ESMTPS id 54A88C4CEE7; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1755699885; bh=43Hd4T+6Nuk347DonpknX8Z7snBYArgegOMZAKI7kCI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=QrA2NEm3cKXUeEFZzGCxuf1j8LAplxp/5xln9YyUIHjL8zUEYEeLUJQCBRELLUbRS L3zDHx3JQlQfKjuKI5R4tBBZ6D5SmucUzbsSFwsh8ZtG+QWhBdtW4rfFmwG1DiTwir CU4YnWW1EBuscyNfSmpahPct6IP4EmvpSgdLuE4oRke/VqPW8ag6tcFnF4uIo1Yg/2 S9XoHoKO+0Le1ScDqQKGmW8gbKpoZEfTT+5ZvM6tceZWvlAsc+rbgPmUyVL+/DixN/ PJjH64UQadQnwzxOJRsbmO0B9gPYSd1i3OavMn53Mcc/9E4U3V7IU8fzInQluzavq5 Hj9tn23vl64Ug== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 43DF9CA0EE4; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Wed, 20 Aug 2025 14:24:19 +0000 Subject: [PATCH v5 1/9] dt-bindings: iio: imu: Add inv_icm45600 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250820-add_newport_driver-v5-1-2fc9f13dddee@tdk.com> References: <20250820-add_newport_driver-v5-0-2fc9f13dddee@tdk.com> In-Reply-To: <20250820-add_newport_driver-v5-0-2fc9f13dddee@tdk.com> To: Jonathan Cameron , David Lechner , =?utf-8?q?Nuno_S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, Remi Buisson X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1755699883; l=3083; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=CvG751gXpIrPtBZ/B5Zk3M5Cc7OHvJOTN+ywayo73q0=; b=LJMAASNBD7UiBd02spINrvRU+tqypx/DPEmEftnwHW88HzTWgwMxsmF+ftL78VxO66hDp9/GG Y7C1MEfd2gCALN/YeSW/PgnblGd+Ehsi4fSx3mTP/VWdpxqRSg29zUj X-Developer-Key: i=remi.buisson@tdk.com; a=ed25519; pk=yDVMi4C7RpXN4dififo42A7fDDt3THYzoZoNq9lUZuo= X-Endpoint-Received: by B4 Relay for remi.buisson@tdk.com/20250411 with auth_id=372 X-Original-From: Remi Buisson Reply-To: remi.buisson@tdk.com From: Remi Buisson Document the ICM-45600 devices devicetree bindings. Signed-off-by: Remi Buisson Reviewed-by: Conor Dooley --- .../bindings/iio/imu/invensense,icm45600.yaml | 90 ++++++++++++++++++= ++++ 1 file changed, 90 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm45600.= yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm45600.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e0b78d14420febee405750ef5cb= 6ce14bfa07447 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm45600.yaml @@ -0,0 +1,90 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/imu/invensense,icm45600.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: InvenSense ICM-45600 Inertial Measurement Unit + +maintainers: + - Remi Buisson + +description: | + 6-axis MotionTracking device that combines a 3-axis gyroscope and a 3-ax= is + accelerometer. + + It has a configurable host interface that supports I3C, I2C and SPI seri= al + communication, features up to 8kB FIFO and 2 programmable interrupts with + ultra-low-power wake-on-motion support to minimize system power consumpt= ion. + + Other industry-leading features include InvenSense on-chip APEX Motion + Processing engine for gesture recognition, activity classification, and + pedometer, along with programmable digital filters, and an embedded + temperature sensor. + + https://invensense.tdk.com/wp-content/uploads/documentation/DS-000576_IC= M-45605.pdf + +properties: + compatible: + enum: + - invensense,icm45605 + - invensense,icm45606 + - invensense,icm45608 + - invensense,icm45634 + - invensense,icm45686 + - invensense,icm45687 + - invensense,icm45688p + - invensense,icm45689 + + reg: + maxItems: 1 + + interrupts: + minItems: 1 + maxItems: 2 + + interrupt-names: + minItems: 1 + items: + - enum: [int1, int2] + - const: int2 + description: Choose chip interrupt pin to be used as interrupt input. + + drive-open-drain: + type: boolean + + vdd-supply: true + + vddio-supply: true + + mount-matrix: true + +required: + - compatible + - reg + - vdd-supply + - vddio-supply + +unevaluatedProperties: false + +examples: + - | + #include + #include + i2c { + #address-cells =3D <1>; + #size-cells =3D <0>; + + imu@68 { + compatible =3D "invensense,icm45605"; + reg =3D <0x68>; + interrupt-parent =3D <&gpio2>; + interrupt-names =3D "int1"; + interrupts =3D <7 IRQ_TYPE_EDGE_RISING>; + vdd-supply =3D <&vdd>; + vddio-supply =3D <&vddio>; + mount-matrix =3D "0", "-1", "0", + "1", "0", "0", + "0", "0", "1"; + }; + }; --=20 2.34.1 From nobody Sat Sep 13 22:31:04 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CBDDE27280B; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755699885; cv=none; b=hhdrEgUi81tRu0VAzA2IeDTxuJ+6wV9fIVhSaiaTuGmJ69QnwLk4NGE2u7E8hokRPHcIaTqFerwi2L9AEO5eSnaNNOoDxok7cYKW3J7GtwzUce01/k78nvhWBJxv28mCmcuudZvMtn/+M6+4ASaMm4wTkg8nbLxZbHnW+4v2b7A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755699885; c=relaxed/simple; bh=GoFe02y/IsKi7KF6k7Lxc1sbG1Q+p7uk19zJb8SK7a0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=bdxpzc2FhVZ6RNPMVC+IFyLE8nDCPLgK0wtw9YSAuN6WJek2lX0/oVQuCP4yEAeUPf/SgUJO/3Dgk5NO9sYT8kaTT7Jm8fODvrgPKp1JVhcfIYgrvBhAXdcO2PV7Igmrhg72taRhoOADS8XwlCR0wsRfZHP5T+AX8XZ9l7ZJIm8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=U+PPrJwQ; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="U+PPrJwQ" Received: by smtp.kernel.org (Postfix) with ESMTPS id 67C5FC116D0; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1755699885; bh=GoFe02y/IsKi7KF6k7Lxc1sbG1Q+p7uk19zJb8SK7a0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=U+PPrJwQ9vLrHd1Als4v4zqvXRlR1fPE8XBGvV0q6l3FKKESO++YxQDHW3r+bzaXM k05bIY1ySRmj8SXOsucOp8oV/dUnFM3BIgll9YtYqqAvEAuvNjh8Wm5S7kX2rKa3u4 xdaXriLMKe7AzHgUu9UgQOfWXrPth1FRJEUbeyl+3sA9fkRUfmW4e1uL72yABn4DHf YLp2NqlG7iRTvhqv81M+l6CJ8JBcW9+HV3zK/qQ3NgO7qcbAoO9wGyNPxCSiPwVh+X PgzXfTPWugIAcYaPSfzMyFXiku04QUnXtWe4FRT6Y0/pgjqeuBwEbNvJ4dcqmFsp8a rYvdTQFCRF/wA== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6043FCA0EFC; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Wed, 20 Aug 2025 14:24:20 +0000 Subject: [PATCH v5 2/9] iio: imu: inv_icm45600: add new inv_icm45600 driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250820-add_newport_driver-v5-2-2fc9f13dddee@tdk.com> References: <20250820-add_newport_driver-v5-0-2fc9f13dddee@tdk.com> In-Reply-To: <20250820-add_newport_driver-v5-0-2fc9f13dddee@tdk.com> To: Jonathan Cameron , David Lechner , =?utf-8?q?Nuno_S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, Remi Buisson X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1755699883; l=36264; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=Z8uZqzZh4P9UG+ImLwRpbwGNc19OUW8gOJCsTVcfNR8=; b=4pcYqOcYDrQDx7HNy+UJPseCEGr6AzBmQxhtpk1cybx7hEZdsKZJbCht29DmnUZM0KeKacdtD ZUibtrgdblXAGfnALkJ1yyCXVjKHOYaSKqw76tKjOtMPeUbugGiHIcp X-Developer-Key: i=remi.buisson@tdk.com; a=ed25519; pk=yDVMi4C7RpXN4dififo42A7fDDt3THYzoZoNq9lUZuo= X-Endpoint-Received: by B4 Relay for remi.buisson@tdk.com/20250411 with auth_id=372 X-Original-From: Remi Buisson Reply-To: remi.buisson@tdk.com From: Remi Buisson Core component of a new driver for InvenSense ICM-45600 devices. It includes registers definition, main probe/setup, and device utility functions. ICM-456xx devices are latest generation of 6-axis IMU, gyroscope+accelerometer and temperature sensor. This device includes a 8K FIFO, supports I2C/I3C/SPI, and provides intelligent motion features like pedometer, tilt detection, and tap detection. Signed-off-by: Remi Buisson --- drivers/iio/imu/Kconfig | 1 + drivers/iio/imu/Makefile | 1 + drivers/iio/imu/inv_icm45600/Kconfig | 5 + drivers/iio/imu/inv_icm45600/Makefile | 4 + drivers/iio/imu/inv_icm45600/inv_icm45600.h | 336 +++++++++++ drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 693 +++++++++++++++++++= ++++ 6 files changed, 1040 insertions(+) diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig index 15612f0f189b5114deb414ef840339678abdc562..9d732bed9fcdac12a13713dba34= 55c1fdf9f4a53 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig @@ -109,6 +109,7 @@ config KMX61 be called kmx61. =20 source "drivers/iio/imu/inv_icm42600/Kconfig" +source "drivers/iio/imu/inv_icm45600/Kconfig" source "drivers/iio/imu/inv_mpu6050/Kconfig" =20 config SMI240 diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile index e901aea498d37e5897e8b71268356a19eac2cb59..2ae6344f84699b2f85fff1c8077= cb412f6ae2658 100644 --- a/drivers/iio/imu/Makefile +++ b/drivers/iio/imu/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_FXOS8700_I2C) +=3D fxos8700_i2c.o obj-$(CONFIG_FXOS8700_SPI) +=3D fxos8700_spi.o =20 obj-y +=3D inv_icm42600/ +obj-y +=3D inv_icm45600/ obj-y +=3D inv_mpu6050/ =20 obj-$(CONFIG_KMX61) +=3D kmx61.o diff --git a/drivers/iio/imu/inv_icm45600/Kconfig b/drivers/iio/imu/inv_icm= 45600/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..8cb5543e0a5817323ab7b2d520d= d3430ac5dbc99 --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/Kconfig @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +config INV_ICM45600 + tristate + select IIO_INV_SENSORS_TIMESTAMP diff --git a/drivers/iio/imu/inv_icm45600/Makefile b/drivers/iio/imu/inv_ic= m45600/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..4f442b61896e91647c7947a0449= 49792bae06a30 --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +obj-$(CONFIG_INV_ICM45600) +=3D inv-icm45600.o +inv-icm45600-y +=3D inv_icm45600_core.o diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600.h b/drivers/iio/imu/= inv_icm45600/inv_icm45600.h new file mode 100644 index 0000000000000000000000000000000000000000..94ef0ff3ccda85583101f2eaca3= bc3771141505a --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h @@ -0,0 +1,336 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2025 Invensense, Inc. */ + +#ifndef INV_ICM45600_H_ +#define INV_ICM45600_H_ + +#include +#include +#include +#include +#include + +#define INV_ICM45600_REG_BANK_MASK GENMASK(15, 8) +#define INV_ICM45600_REG_ADDR_MASK GENMASK(7, 0) + +enum inv_icm45600_sensor_mode { + INV_ICM45600_SENSOR_MODE_OFF, + INV_ICM45600_SENSOR_MODE_STANDBY, + INV_ICM45600_SENSOR_MODE_LOW_POWER, + INV_ICM45600_SENSOR_MODE_LOW_NOISE, + INV_ICM45600_SENSOR_MODE_MAX +}; + +/* gyroscope fullscale values */ +enum inv_icm45600_gyro_fs { + INV_ICM45600_GYRO_FS_2000DPS, + INV_ICM45600_GYRO_FS_1000DPS, + INV_ICM45600_GYRO_FS_500DPS, + INV_ICM45600_GYRO_FS_250DPS, + INV_ICM45600_GYRO_FS_125DPS, + INV_ICM45600_GYRO_FS_62_5DPS, + INV_ICM45600_GYRO_FS_31_25DPS, + INV_ICM45600_GYRO_FS_15_625DPS, + INV_ICM45600_GYRO_FS_MAX +}; + +enum inv_icm45686_gyro_fs { + INV_ICM45686_GYRO_FS_4000DPS, + INV_ICM45686_GYRO_FS_2000DPS, + INV_ICM45686_GYRO_FS_1000DPS, + INV_ICM45686_GYRO_FS_500DPS, + INV_ICM45686_GYRO_FS_250DPS, + INV_ICM45686_GYRO_FS_125DPS, + INV_ICM45686_GYRO_FS_62_5DPS, + INV_ICM45686_GYRO_FS_31_25DPS, + INV_ICM45686_GYRO_FS_15_625DPS, + INV_ICM45686_GYRO_FS_MAX +}; + +/* accelerometer fullscale values */ +enum inv_icm45600_accel_fs { + INV_ICM45600_ACCEL_FS_16G, + INV_ICM45600_ACCEL_FS_8G, + INV_ICM45600_ACCEL_FS_4G, + INV_ICM45600_ACCEL_FS_2G, + INV_ICM45600_ACCEL_FS_MAX +}; + +enum inv_icm45686_accel_fs { + INV_ICM45686_ACCEL_FS_32G, + INV_ICM45686_ACCEL_FS_16G, + INV_ICM45686_ACCEL_FS_8G, + INV_ICM45686_ACCEL_FS_4G, + INV_ICM45686_ACCEL_FS_2G, + INV_ICM45686_ACCEL_FS_MAX +}; + +/* ODR suffixed by LN or LP are Low-Noise or Low-Power mode only */ +enum inv_icm45600_odr { + INV_ICM45600_ODR_6400HZ_LN =3D 0x03, + INV_ICM45600_ODR_3200HZ_LN, + INV_ICM45600_ODR_1600HZ_LN, + INV_ICM45600_ODR_800HZ_LN, + INV_ICM45600_ODR_400HZ, + INV_ICM45600_ODR_200HZ, + INV_ICM45600_ODR_100HZ, + INV_ICM45600_ODR_50HZ, + INV_ICM45600_ODR_25HZ, + INV_ICM45600_ODR_12_5HZ, + INV_ICM45600_ODR_6_25HZ_LP, + INV_ICM45600_ODR_3_125HZ_LP, + INV_ICM45600_ODR_1_5625HZ_LP, + INV_ICM45600_ODR_MAX +}; + +struct inv_icm45600_sensor_conf { + u8 mode; + u8 fs; + u8 odr; + u8 filter; +}; + +struct inv_icm45600_conf { + struct inv_icm45600_sensor_conf gyro; + struct inv_icm45600_sensor_conf accel; +}; + +struct inv_icm45600_suspended { + enum inv_icm45600_sensor_mode gyro; + enum inv_icm45600_sensor_mode accel; +}; + +struct inv_icm45600_chip_info { + u8 whoami; + const char *name; + const struct inv_icm45600_conf *conf; +}; + +extern const struct inv_icm45600_chip_info inv_icm45605_chip_info; +extern const struct inv_icm45600_chip_info inv_icm45606_chip_info; +extern const struct inv_icm45600_chip_info inv_icm45608_chip_info; +extern const struct inv_icm45600_chip_info inv_icm45634_chip_info; +extern const struct inv_icm45600_chip_info inv_icm45686_chip_info; +extern const struct inv_icm45600_chip_info inv_icm45687_chip_info; +extern const struct inv_icm45600_chip_info inv_icm45688p_chip_info; +extern const struct inv_icm45600_chip_info inv_icm45689_chip_info; + +/** + * struct inv_icm45600_state - driver state variables + * @lock: lock for serializing multiple registers access. + * @chip: chip identifier. + * @map: regmap pointer. + * @vddio_supply: I/O voltage regulator for the chip. + * @orientation: sensor chip orientation relative to main hardware. + * @conf: chip sensors configurations. + * @suspended: suspended sensors configuration. + * @indio_gyro: gyroscope IIO device. + * @indio_accel: accelerometer IIO device. + * @timestamp: interrupt timestamps. + * @buffer: data transfer buffer aligned for DMA. + */ +struct inv_icm45600_state { + struct mutex lock; + struct regmap *map; + struct regulator *vddio_supply; + struct iio_mount_matrix orientation; + struct inv_icm45600_conf conf; + struct inv_icm45600_suspended suspended; + struct iio_dev *indio_gyro; + struct iio_dev *indio_accel; + const struct inv_icm45600_chip_info *chip_info; + struct { + s64 gyro; + s64 accel; + } timestamp; + union { + u8 buff[2]; + __le16 u16; + u8 ireg[3]; + } buffer __aligned(IIO_DMA_MINALIGN); +}; + +/** + * struct inv_icm45600_sensor_state - sensor state variables + * @scales: table of scales. + * @scales_len: length (nb of items) of the scales table. + * @power_mode: sensor requested power mode (for common frequencies) + * @ts: timestamp module states. + */ +struct inv_icm45600_sensor_state { + const int *scales; + size_t scales_len; + enum inv_icm45600_sensor_mode power_mode; + struct inv_sensors_timestamp ts; +}; + +/* Virtual register addresses: @bank on MSB (16 bits), @address on LSB */ + +/* Indirect register access */ +#define INV_ICM45600_REG_IREG_ADDR 0x7C +#define INV_ICM45600_REG_IREG_DATA 0x7E + +/* Direct acces registers */ +#define INV_ICM45600_REG_MISC2 0x007F +#define INV_ICM45600_MISC2_SOFT_RESET BIT(1) + +#define INV_ICM45600_REG_DRIVE_CONFIG0 0x0032 +#define INV_ICM45600_DRIVE_CONFIG0_SPI_MASK GENMASK(3, 1) +#define INV_ICM45600_SPI_SLEW_RATE_0_5NS 6 +#define INV_ICM45600_SPI_SLEW_RATE_4NS 5 +#define INV_ICM45600_SPI_SLEW_RATE_5NS 4 +#define INV_ICM45600_SPI_SLEW_RATE_7NS 3 +#define INV_ICM45600_SPI_SLEW_RATE_10NS 2 +#define INV_ICM45600_SPI_SLEW_RATE_14NS 1 +#define INV_ICM45600_SPI_SLEW_RATE_38NS 0 + +#define INV_ICM45600_REG_INT1_CONFIG2 0x0018 +#define INV_ICM45600_INT1_CONFIG2_PUSH_PULL BIT(2) +#define INV_ICM45600_INT1_CONFIG2_LATCHED BIT(1) +#define INV_ICM45600_INT1_CONFIG2_ACTIVE_HIGH BIT(0) +#define INV_ICM45600_INT1_CONFIG2_ACTIVE_LOW 0x00 + +#define INV_ICM45600_REG_FIFO_CONFIG0 0x001D +#define INV_ICM45600_FIFO_CONFIG0_MODE_MASK GENMASK(7, 6) +#define INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS 0 +#define INV_ICM45600_FIFO_CONFIG0_MODE_STREAM 1 +#define INV_ICM45600_FIFO_CONFIG0_MODE_STOP_ON_FULL 2 +#define INV_ICM45600_FIFO_CONFIG0_FIFO_DEPTH_MAX 0x1F + +#define INV_ICM45600_REG_FIFO_CONFIG2 0x0020 +#define INV_ICM45600_REG_FIFO_CONFIG2_FIFO_FLUSH BIT(7) +#define INV_ICM45600_REG_FIFO_CONFIG2_WM_GT_TH BIT(3) + +#define INV_ICM45600_REG_FIFO_CONFIG3 0x0021 +#define INV_ICM45600_FIFO_CONFIG3_ES1_EN BIT(5) +#define INV_ICM45600_FIFO_CONFIG3_ES0_EN BIT(4) +#define INV_ICM45600_FIFO_CONFIG3_HIRES_EN BIT(3) +#define INV_ICM45600_FIFO_CONFIG3_GYRO_EN BIT(2) +#define INV_ICM45600_FIFO_CONFIG3_ACCEL_EN BIT(1) +#define INV_ICM45600_FIFO_CONFIG3_IF_EN BIT(0) + +#define INV_ICM45600_REG_FIFO_CONFIG4 0x0022 +#define INV_ICM45600_FIFO_CONFIG4_COMP_EN BIT(2) +#define INV_ICM45600_FIFO_CONFIG4_TMST_FSYNC_EN BIT(1) +#define INV_ICM45600_FIFO_CONFIG4_ES0_9B BIT(0) + +/* all sensor data are 16 bits (2 registers wide) in big-endian */ +#define INV_ICM45600_REG_TEMP_DATA 0x000C +#define INV_ICM45600_REG_ACCEL_DATA_X 0x0000 +#define INV_ICM45600_REG_ACCEL_DATA_Y 0x0002 +#define INV_ICM45600_REG_ACCEL_DATA_Z 0x0004 +#define INV_ICM45600_REG_GYRO_DATA_X 0x0006 +#define INV_ICM45600_REG_GYRO_DATA_Y 0x0008 +#define INV_ICM45600_REG_GYRO_DATA_Z 0x000A + +#define INV_ICM45600_REG_INT_STATUS 0x0019 +#define INV_ICM45600_INT_STATUS_RESET_DONE BIT(7) +#define INV_ICM45600_INT_STATUS_AUX1_AGC_RDY BIT(6) +#define INV_ICM45600_INT_STATUS_AP_AGC_RDY BIT(5) +#define INV_ICM45600_INT_STATUS_AP_FSYNC BIT(4) +#define INV_ICM45600_INT_STATUS_AUX1_DRDY BIT(3) +#define INV_ICM45600_INT_STATUS_DATA_RDY BIT(2) +#define INV_ICM45600_INT_STATUS_FIFO_THS BIT(1) +#define INV_ICM45600_INT_STATUS_FIFO_FULL BIT(0) + +/* + * FIFO access registers + * FIFO count is 16 bits (2 registers) + * FIFO data is a continuous read register to read FIFO content + */ +#define INV_ICM45600_REG_FIFO_COUNT 0x0012 +#define INV_ICM45600_REG_FIFO_DATA 0x0014 + +#define INV_ICM45600_REG_PWR_MGMT0 0x0010 +#define INV_ICM45600_PWR_MGMT0_GYRO_MODE_MASK GENMASK(3, 2) +#define INV_ICM45600_PWR_MGMT0_ACCEL_MODE_MASK GENMASK(1, 0) + +#define INV_ICM45600_REG_ACCEL_CONFIG0 0x001B +#define INV_ICM45600_ACCEL_CONFIG0_FS_MASK GENMASK(6, 4) +#define INV_ICM45600_ACCEL_CONFIG0_ODR_MASK GENMASK(3, 0) +#define INV_ICM45600_REG_GYRO_CONFIG0 0x001C +#define INV_ICM45600_GYRO_CONFIG0_FS_MASK GENMASK(7, 4) +#define INV_ICM45600_GYRO_CONFIG0_ODR_MASK GENMASK(3, 0) + +#define INV_ICM45600_REG_SMC_CONTROL_0 0xA258 +#define INV_ICM45600_SMC_CONTROL_0_ACCEL_LP_CLK_SEL BIT(4) +#define INV_ICM45600_SMC_CONTROL_0_TMST_EN BIT(0) + +/* FIFO watermark is 16 bits (2 registers wide) in little-endian */ +#define INV_ICM45600_REG_FIFO_WATERMARK 0x001E + +/* FIFO is configured for 8kb */ +#define INV_ICM45600_FIFO_SIZE_MAX (8 * 1024) + +#define INV_ICM45600_REG_INT1_CONFIG0 0x0016 +#define INV_ICM45600_INT1_CONFIG0_RESET_DONE_EN BIT(7) +#define INV_ICM45600_INT1_CONFIG0_AUX1_AGC_RDY_EN BIT(6) +#define INV_ICM45600_INT1_CONFIG0_AP_AGC_RDY_EN BIT(5) +#define INV_ICM45600_INT1_CONFIG0_AP_FSYNC_EN BIT(4) +#define INV_ICM45600_INT1_CONFIG0_AUX1_DRDY_EN BIT(3) +#define INV_ICM45600_INT1_CONFIG0_DRDY_EN BIT(2) +#define INV_ICM45600_INT1_CONFIG0_FIFO_THS_EN BIT(1) +#define INV_ICM45600_INT1_CONFIG0_FIFO_FULL_EN BIT(0) + +#define INV_ICM45600_REG_WHOAMI 0x0072 +#define INV_ICM45600_WHOAMI_ICM45605 0xE5 +#define INV_ICM45600_WHOAMI_ICM45686 0xE9 +#define INV_ICM45600_WHOAMI_ICM45688P 0xE7 +#define INV_ICM45600_WHOAMI_ICM45608 0x81 +#define INV_ICM45600_WHOAMI_ICM45634 0x82 +#define INV_ICM45600_WHOAMI_ICM45689 0x83 +#define INV_ICM45600_WHOAMI_ICM45606 0x84 +#define INV_ICM45600_WHOAMI_ICM45687 0x85 + +/* Gyro USER offset */ +#define INV_ICM45600_IPREG_SYS1_REG_42 0xA42A +#define INV_ICM45600_IPREG_SYS1_REG_56 0xA438 +#define INV_ICM45600_IPREG_SYS1_REG_70 0xA446 +#define INV_ICM45600_GYRO_OFFUSER_MASK GENMASK(13, 0) +/* Gyro Averaging filter */ +#define INV_ICM45600_IPREG_SYS1_REG_170 0xA4AA +#define INV_ICM45600_IPREG_SYS1_170_GYRO_LP_AVG_MASK GENMASK(4, 1) +#define INV_ICM45600_GYRO_LP_AVG_SEL_8X 5 +#define INV_ICM45600_GYRO_LP_AVG_SEL_2X 1 +/* Accel USER offset */ +#define INV_ICM45600_IPREG_SYS2_REG_24 0xA518 +#define INV_ICM45600_IPREG_SYS2_REG_32 0xA520 +#define INV_ICM45600_IPREG_SYS2_REG_40 0xA528 +#define INV_ICM45600_ACCEL_OFFUSER_MASK GENMASK(13, 0) +/* Accel averaging filter */ +#define INV_ICM45600_IPREG_SYS2_REG_129 0xA581 +#define INV_ICM45600_ACCEL_LP_AVG_SEL_1X 0x0000 +#define INV_ICM45600_ACCEL_LP_AVG_SEL_4X 0x0002 + +/* Sleep times required by the driver */ +#define INV_ICM45600_ACCEL_STARTUP_TIME_MS 60 +#define INV_ICM45600_GYRO_STARTUP_TIME_MS 60 +#define INV_ICM45600_GYRO_STOP_TIME_MS 150 +#define INV_ICM45600_IREG_DELAY_US 4 + +typedef int (*inv_icm45600_bus_setup)(struct inv_icm45600_state *); + +extern const struct dev_pm_ops inv_icm45600_pm_ops; + +const struct iio_mount_matrix * +inv_icm45600_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan); + +u32 inv_icm45600_odr_to_period(enum inv_icm45600_odr odr); + +int inv_icm45600_set_accel_conf(struct inv_icm45600_state *st, + struct inv_icm45600_sensor_conf *conf, + unsigned int *sleep_ms); + +int inv_icm45600_set_gyro_conf(struct inv_icm45600_state *st, + struct inv_icm45600_sensor_conf *conf, + unsigned int *sleep_ms); + +int inv_icm45600_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval); + +int inv_icm45600_core_probe(struct regmap *regmap, + const struct inv_icm45600_chip_info *chip_info, + bool reset, inv_icm45600_bus_setup bus_setup); + +#endif diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio= /imu/inv_icm45600/inv_icm45600_core.c new file mode 100644 index 0000000000000000000000000000000000000000..4b8f3dad8984cfa6642b1b4c94a= cbb0674084f3f --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c @@ -0,0 +1,693 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Copyright (C) 2025 Invensense, Inc. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inv_icm45600.h" + +static int inv_icm45600_ireg_read(struct regmap *map, unsigned int reg, + u8 *data, size_t count) +{ + const struct device *dev =3D regmap_get_device(map); + struct inv_icm45600_state *st =3D dev_get_drvdata(dev); + unsigned int d; + ssize_t i; + int ret; + + st->buffer.ireg[0] =3D FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg); + st->buffer.ireg[1] =3D FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg); + + /* Burst write address. */ + ret =3D regmap_bulk_write(map, INV_ICM45600_REG_IREG_ADDR, st->buffer.ire= g, 2); + /* Wait while the device is busy processing the address. */ + fsleep(INV_ICM45600_IREG_DELAY_US); + if (ret) + return ret; + + /* Read the data. */ + for (i =3D 0; i < count; i++) { + ret =3D regmap_read(map, INV_ICM45600_REG_IREG_DATA, &d); + /* Wait while the device is busy processing the data. */ + fsleep(INV_ICM45600_IREG_DELAY_US); + if (ret) + return ret; + data[i] =3D d; + } + + return 0; +} + +static int inv_icm45600_ireg_write(struct regmap *map, unsigned int reg, + const u8 *data, size_t count) +{ + const struct device *dev =3D regmap_get_device(map); + struct inv_icm45600_state *st =3D dev_get_drvdata(dev); + ssize_t i; + int ret; + + st->buffer.ireg[0] =3D FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg); + st->buffer.ireg[1] =3D FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg); + st->buffer.ireg[2] =3D data[0]; + + /* Burst write address and first byte. */ + ret =3D regmap_bulk_write(map, INV_ICM45600_REG_IREG_ADDR, st->buffer.ire= g, 3); + /* Wait while the device is busy processing the address and data. */ + fsleep(INV_ICM45600_IREG_DELAY_US); + if (ret) + return ret; + + /* Write the remaining bytes. */ + for (i =3D 1; i < count; i++) { + ret =3D regmap_write(map, INV_ICM45600_REG_IREG_DATA, data[i]); + /* Wait while the device is busy processing the data. */ + fsleep(INV_ICM45600_IREG_DELAY_US); + if (ret) + return ret; + } + + return 0; +} + +static int inv_icm45600_read(void *context, const void *reg_buf, size_t re= g_size, + void *val_buf, size_t val_size) +{ + unsigned int reg =3D be16_to_cpup(reg_buf); + struct regmap *map =3D context; + + if (FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg) =3D=3D 0) + return regmap_bulk_read(map, FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg), + val_buf, val_size); + + return inv_icm45600_ireg_read(map, reg, val_buf, val_size); +} + +static int inv_icm45600_write(void *context, const void *data, + size_t count) +{ + const u8 *d =3D data; + unsigned int reg =3D be16_to_cpup(data); + struct regmap *map =3D context; + + if (FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg) =3D=3D 0) + return regmap_bulk_write(map, FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg), + d + 2, count - 2); + + return inv_icm45600_ireg_write(map, reg, d + 2, count - 2); +} + +static const struct regmap_bus inv_icm45600_regmap_bus =3D { + .read =3D inv_icm45600_read, + .write =3D inv_icm45600_write, +}; + +static const struct regmap_config inv_icm45600_regmap_config =3D { + .reg_bits =3D 16, + .val_bits =3D 8, +}; + +/* These are the chip initial default configurations (default FS value is = based on icm45686) */ +static const struct inv_icm45600_conf inv_icm45600_default_conf =3D { + .gyro =3D { + .mode =3D INV_ICM45600_SENSOR_MODE_OFF, + .fs =3D INV_ICM45686_GYRO_FS_2000DPS, + .odr =3D INV_ICM45600_ODR_800HZ_LN, + .filter =3D INV_ICM45600_GYRO_LP_AVG_SEL_8X, + }, + .accel =3D { + .mode =3D INV_ICM45600_SENSOR_MODE_OFF, + .fs =3D INV_ICM45686_ACCEL_FS_16G, + .odr =3D INV_ICM45600_ODR_800HZ_LN, + .filter =3D INV_ICM45600_ACCEL_LP_AVG_SEL_4X, + }, +}; + +static const struct inv_icm45600_conf inv_icm45686_default_conf =3D { + .gyro =3D { + .mode =3D INV_ICM45600_SENSOR_MODE_OFF, + .fs =3D INV_ICM45686_GYRO_FS_4000DPS, + .odr =3D INV_ICM45600_ODR_800HZ_LN, + .filter =3D INV_ICM45600_GYRO_LP_AVG_SEL_8X, + }, + .accel =3D { + .mode =3D INV_ICM45600_SENSOR_MODE_OFF, + .fs =3D INV_ICM45686_ACCEL_FS_32G, + .odr =3D INV_ICM45600_ODR_800HZ_LN, + .filter =3D INV_ICM45600_ACCEL_LP_AVG_SEL_4X, + }, +}; + +const struct inv_icm45600_chip_info inv_icm45605_chip_info =3D { + .whoami =3D INV_ICM45600_WHOAMI_ICM45605, + .name =3D "icm45605", + .conf =3D &inv_icm45600_default_conf, +}; +EXPORT_SYMBOL_NS_GPL(inv_icm45605_chip_info, "IIO_ICM45600"); + +const struct inv_icm45600_chip_info inv_icm45606_chip_info =3D { + .whoami =3D INV_ICM45600_WHOAMI_ICM45606, + .name =3D "icm45606", + .conf =3D &inv_icm45600_default_conf, +}; +EXPORT_SYMBOL_NS_GPL(inv_icm45606_chip_info, "IIO_ICM45600"); + +const struct inv_icm45600_chip_info inv_icm45608_chip_info =3D { + .whoami =3D INV_ICM45600_WHOAMI_ICM45608, + .name =3D "icm45608", + .conf =3D &inv_icm45600_default_conf, +}; +EXPORT_SYMBOL_NS_GPL(inv_icm45608_chip_info, "IIO_ICM45600"); + +const struct inv_icm45600_chip_info inv_icm45634_chip_info =3D { + .whoami =3D INV_ICM45600_WHOAMI_ICM45634, + .name =3D "icm45634", + .conf =3D &inv_icm45600_default_conf, +}; +EXPORT_SYMBOL_NS_GPL(inv_icm45634_chip_info, "IIO_ICM45600"); + +const struct inv_icm45600_chip_info inv_icm45686_chip_info =3D { + .whoami =3D INV_ICM45600_WHOAMI_ICM45686, + .name =3D "icm45686", + .conf =3D &inv_icm45686_default_conf, +}; +EXPORT_SYMBOL_NS_GPL(inv_icm45686_chip_info, "IIO_ICM45600"); + +const struct inv_icm45600_chip_info inv_icm45687_chip_info =3D { + .whoami =3D INV_ICM45600_WHOAMI_ICM45687, + .name =3D "icm45687", + .conf =3D &inv_icm45686_default_conf, +}; +EXPORT_SYMBOL_NS_GPL(inv_icm45687_chip_info, "IIO_ICM45600"); + +const struct inv_icm45600_chip_info inv_icm45688p_chip_info =3D { + .whoami =3D INV_ICM45600_WHOAMI_ICM45688P, + .name =3D "icm45688p", + .conf =3D &inv_icm45686_default_conf, +}; +EXPORT_SYMBOL_NS_GPL(inv_icm45688p_chip_info, "IIO_ICM45600"); + +const struct inv_icm45600_chip_info inv_icm45689_chip_info =3D { + .whoami =3D INV_ICM45600_WHOAMI_ICM45689, + .name =3D "icm45689", + .conf =3D &inv_icm45686_default_conf, +}; +EXPORT_SYMBOL_NS_GPL(inv_icm45689_chip_info, "IIO_ICM45600"); + +const struct iio_mount_matrix * +inv_icm45600_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + const struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + + return &st->orientation; +} + +u32 inv_icm45600_odr_to_period(enum inv_icm45600_odr odr) +{ + static const u32 odr_periods[INV_ICM45600_ODR_MAX] =3D { + 0, 0, 0, /* reserved values */ + 156250, /* 6.4kHz */ + 312500, /* 3.2kHz */ + 625000, /* 1.6kHz */ + 1250000, /* 800kHz */ + 2500000, /* 400Hz */ + 5000000, /* 200Hz */ + 10000000, /* 100Hz */ + 20000000, /* 50Hz */ + 40000000, /* 25Hz */ + 80000000, /* 12.5Hz */ + 160000000, /* 6.25Hz */ + 320000000, /* 3.125Hz */ + 640000000, /* 1.5625Hz */ + }; + + return odr_periods[odr]; +} + +static int inv_icm45600_set_pwr_mgmt0(struct inv_icm45600_state *st, + enum inv_icm45600_sensor_mode gyro, + enum inv_icm45600_sensor_mode accel, + unsigned int *sleep_ms) +{ + enum inv_icm45600_sensor_mode oldgyro =3D st->conf.gyro.mode; + enum inv_icm45600_sensor_mode oldaccel =3D st->conf.accel.mode; + unsigned int sleepval; + unsigned int val; + int ret; + + /* if nothing changed, exit */ + if (gyro =3D=3D oldgyro && accel =3D=3D oldaccel) + return 0; + + val =3D FIELD_PREP(INV_ICM45600_PWR_MGMT0_GYRO_MODE_MASK, gyro) | + FIELD_PREP(INV_ICM45600_PWR_MGMT0_ACCEL_MODE_MASK, accel); + ret =3D regmap_write(st->map, INV_ICM45600_REG_PWR_MGMT0, val); + if (ret) + return ret; + + st->conf.gyro.mode =3D gyro; + st->conf.accel.mode =3D accel; + + /* Compute the required wait time for sensors to stabilize. */ + sleepval =3D 0; + + /* Accel startup time. */ + if (accel !=3D oldaccel && oldaccel =3D=3D INV_ICM45600_SENSOR_MODE_OFF) + sleepval =3D max(sleepval, INV_ICM45600_ACCEL_STARTUP_TIME_MS); + + if (gyro !=3D oldgyro) { + /* Gyro startup time. */ + if (oldgyro =3D=3D INV_ICM45600_SENSOR_MODE_OFF) + sleepval =3D max(sleepval, INV_ICM45600_GYRO_STARTUP_TIME_MS); + /* Gyro stop time. */ + else if (gyro =3D=3D INV_ICM45600_SENSOR_MODE_OFF) + sleepval =3D max(sleepval, INV_ICM45600_GYRO_STOP_TIME_MS); + } + + /* Deferred sleep value if sleep pointer is provided or direct sleep */ + if (sleep_ms) + *sleep_ms =3D sleepval; + else if (sleepval) + msleep(sleepval); + + return 0; +} + +static void inv_icm45600_set_default_conf(struct inv_icm45600_sensor_conf = *conf, + struct inv_icm45600_sensor_conf *oldconf) +{ + /* Sanitize missing values with current values. */ + if (conf->mode =3D=3D U8_MAX) + conf->mode =3D oldconf->mode; + if (conf->fs =3D=3D U8_MAX) + conf->fs =3D oldconf->fs; + if (conf->odr =3D=3D U8_MAX) + conf->odr =3D oldconf->odr; + if (conf->filter =3D=3D U8_MAX) + conf->filter =3D oldconf->filter; +} + +int inv_icm45600_set_accel_conf(struct inv_icm45600_state *st, + struct inv_icm45600_sensor_conf *conf, + unsigned int *sleep_ms) +{ + struct inv_icm45600_sensor_conf *oldconf =3D &st->conf.accel; + unsigned int val; + int ret; + + inv_icm45600_set_default_conf(conf, oldconf); + + /* Force the power mode against the ODR when sensor is on. */ + if (conf->mode > INV_ICM45600_SENSOR_MODE_STANDBY) { + if (conf->odr <=3D INV_ICM45600_ODR_800HZ_LN) { + conf->mode =3D INV_ICM45600_SENSOR_MODE_LOW_NOISE; + } else { + conf->mode =3D INV_ICM45600_SENSOR_MODE_LOW_POWER; + /* sanitize averaging value depending on ODR for low-power mode */ + /* maximum 1x @400Hz */ + if (conf->odr =3D=3D INV_ICM45600_ODR_400HZ) + conf->filter =3D INV_ICM45600_ACCEL_LP_AVG_SEL_1X; + else + conf->filter =3D INV_ICM45600_ACCEL_LP_AVG_SEL_4X; + } + } + + /* Set ACCEL_CONFIG0 register (accel fullscale & odr). */ + if (conf->fs !=3D oldconf->fs || conf->odr !=3D oldconf->odr) { + val =3D FIELD_PREP(INV_ICM45600_ACCEL_CONFIG0_FS_MASK, conf->fs) | + FIELD_PREP(INV_ICM45600_ACCEL_CONFIG0_ODR_MASK, conf->odr); + ret =3D regmap_write(st->map, INV_ICM45600_REG_ACCEL_CONFIG0, val); + if (ret) + return ret; + oldconf->fs =3D conf->fs; + oldconf->odr =3D conf->odr; + } + + /* Set ACCEL_LP_AVG_SEL register (accel low-power average filter). */ + if (conf->filter !=3D oldconf->filter) { + ret =3D regmap_write(st->map, INV_ICM45600_IPREG_SYS2_REG_129, + conf->filter); + if (ret) + return ret; + oldconf->filter =3D conf->filter; + } + + /* Set PWR_MGMT0 register (accel sensor mode). */ + return inv_icm45600_set_pwr_mgmt0(st, st->conf.gyro.mode, conf->mode, + sleep_ms); +} + +int inv_icm45600_set_gyro_conf(struct inv_icm45600_state *st, + struct inv_icm45600_sensor_conf *conf, + unsigned int *sleep_ms) +{ + struct inv_icm45600_sensor_conf *oldconf =3D &st->conf.gyro; + unsigned int val; + int ret; + + inv_icm45600_set_default_conf(conf, oldconf); + + /* Force the power mode against ODR when sensor is on. */ + if (conf->mode > INV_ICM45600_SENSOR_MODE_STANDBY) { + if (conf->odr >=3D INV_ICM45600_ODR_6_25HZ_LP) { + conf->mode =3D INV_ICM45600_SENSOR_MODE_LOW_POWER; + conf->filter =3D INV_ICM45600_GYRO_LP_AVG_SEL_8X; + } else { + conf->mode =3D INV_ICM45600_SENSOR_MODE_LOW_NOISE; + } + } + + /* Set GYRO_CONFIG0 register (gyro fullscale & odr). */ + if (conf->fs !=3D oldconf->fs || conf->odr !=3D oldconf->odr) { + val =3D FIELD_PREP(INV_ICM45600_GYRO_CONFIG0_FS_MASK, conf->fs) | + FIELD_PREP(INV_ICM45600_GYRO_CONFIG0_ODR_MASK, conf->odr); + ret =3D regmap_write(st->map, INV_ICM45600_REG_GYRO_CONFIG0, val); + if (ret) + return ret; + oldconf->fs =3D conf->fs; + oldconf->odr =3D conf->odr; + } + + /* Set GYRO_LP_AVG_SEL register (gyro low-power average filter). */ + if (conf->filter !=3D oldconf->filter) { + val =3D FIELD_PREP(INV_ICM45600_IPREG_SYS1_170_GYRO_LP_AVG_MASK, conf->f= ilter); + ret =3D regmap_update_bits(st->map, INV_ICM45600_IPREG_SYS1_REG_170, + INV_ICM45600_IPREG_SYS1_170_GYRO_LP_AVG_MASK, val); + if (ret) + return ret; + oldconf->filter =3D conf->filter; + } + + /* Set PWR_MGMT0 register (gyro sensor mode). */ + return inv_icm45600_set_pwr_mgmt0(st, conf->mode, st->conf.accel.mode, + sleep_ms); +} + +int inv_icm45600_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + int ret; + + guard(mutex)(&st->lock); + + if (readval) + ret =3D regmap_read(st->map, reg, readval); + else + ret =3D regmap_write(st->map, reg, writeval); + + return ret; +} + +static int inv_icm45600_set_conf(struct inv_icm45600_state *st, + const struct inv_icm45600_conf *conf) +{ + unsigned int val; + int ret; + + /* Set PWR_MGMT0 register (gyro & accel sensor mode, temp enabled). */ + val =3D FIELD_PREP(INV_ICM45600_PWR_MGMT0_GYRO_MODE_MASK, conf->gyro.mode= ) | + FIELD_PREP(INV_ICM45600_PWR_MGMT0_ACCEL_MODE_MASK, conf->accel.mode= ); + ret =3D regmap_write(st->map, INV_ICM45600_REG_PWR_MGMT0, val); + if (ret) + return ret; + + /* Set GYRO_CONFIG0 register (gyro fullscale & odr). */ + val =3D FIELD_PREP(INV_ICM45600_GYRO_CONFIG0_FS_MASK, conf->gyro.fs) | + FIELD_PREP(INV_ICM45600_GYRO_CONFIG0_ODR_MASK, conf->gyro.odr); + ret =3D regmap_write(st->map, INV_ICM45600_REG_GYRO_CONFIG0, val); + if (ret) + return ret; + + /* Set ACCEL_CONFIG0 register (accel fullscale & odr). */ + val =3D FIELD_PREP(INV_ICM45600_ACCEL_CONFIG0_FS_MASK, conf->accel.fs) | + FIELD_PREP(INV_ICM45600_ACCEL_CONFIG0_ODR_MASK, conf->accel.odr); + ret =3D regmap_write(st->map, INV_ICM45600_REG_ACCEL_CONFIG0, val); + if (ret) + return ret; + + /* Update the internal configuration. */ + st->conf =3D *conf; + + return 0; +} + +/** + * inv_icm45600_setup() - check and setup chip + * @st: driver internal state + * @chip_info: detected chip description + * @reset: define whether a reset is required or not + * @bus_setup: callback for setting up bus specific registers + * + * Returns 0 on success, a negative error code otherwise. + */ +static int inv_icm45600_setup(struct inv_icm45600_state *st, + const struct inv_icm45600_chip_info *chip_info, + bool reset, inv_icm45600_bus_setup bus_setup) +{ + const struct device *dev =3D regmap_get_device(st->map); + unsigned int val; + int ret; + + /* Set chip bus configuration if specified. */ + if (bus_setup) { + ret =3D bus_setup(st); + if (ret) + return ret; + } + + /* Check chip self-identification value. */ + ret =3D regmap_read(st->map, INV_ICM45600_REG_WHOAMI, &val); + if (ret) + return ret; + if (val !=3D chip_info->whoami) { + if (val =3D=3D U8_MAX || val =3D=3D 0) + return dev_err_probe(dev, -ENODEV, + "Invalid whoami %#02x expected %#02x (%s)\n", + val, chip_info->whoami, chip_info->name); + else + dev_warn(dev, "Unexpected whoami %#02x expected %#02x (%s)\n", + val, chip_info->whoami, chip_info->name); + } + + st->chip_info =3D chip_info; + + if (reset) { + /* Reset to make sure previous state are not there. */ + ret =3D regmap_write(st->map, INV_ICM45600_REG_MISC2, + INV_ICM45600_MISC2_SOFT_RESET); + if (ret) + return ret; + /* IMU reset time: 1ms. */ + fsleep(1000); + + if (bus_setup) { + ret =3D bus_setup(st); + if (ret) + return ret; + } + + ret =3D regmap_read(st->map, INV_ICM45600_REG_INT_STATUS, &val); + if (ret) + return ret; + if (!(val & INV_ICM45600_INT_STATUS_RESET_DONE)) { + dev_err(dev, "reset error, reset done bit not set\n"); + return -ENODEV; + } + } + + return inv_icm45600_set_conf(st, chip_info->conf); +} + +static int inv_icm45600_timestamp_setup(struct inv_icm45600_state *st) +{ + /* Enable timestamps. */ + return regmap_set_bits(st->map, INV_ICM45600_REG_SMC_CONTROL_0, + INV_ICM45600_SMC_CONTROL_0_TMST_EN); +} + +static int inv_icm45600_enable_regulator_vddio(struct inv_icm45600_state *= st) +{ + int ret; + + ret =3D regulator_enable(st->vddio_supply); + if (ret) + return ret; + + /* Wait a little for supply ramp. */ + fsleep(3000); + + return 0; +} + +static void inv_icm45600_disable_vddio_reg(void *_data) +{ + struct inv_icm45600_state *st =3D _data; + struct device *dev =3D regmap_get_device(st->map); + + if (pm_runtime_status_suspended(dev)) + return; + + regulator_disable(st->vddio_supply); +} + +int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm456= 00_chip_info *chip_info, + bool reset, inv_icm45600_bus_setup bus_setup) +{ + struct device *dev =3D regmap_get_device(regmap); + struct fwnode_handle *fwnode; + struct inv_icm45600_state *st; + struct regmap *regmap_custom; + int ret; + + regmap_custom =3D devm_regmap_init(dev, &inv_icm45600_regmap_bus, + regmap, &inv_icm45600_regmap_config); + if (IS_ERR(regmap_custom)) + return dev_err_probe(dev, PTR_ERR(regmap_custom), "Failed to register re= gmap\n"); + + st =3D devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (!st) + return dev_err_probe(dev, -ENOMEM, "Cannot allocate memory\n"); + + dev_set_drvdata(dev, st); + + ret =3D devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + st->map =3D regmap_custom; + + ret =3D iio_read_mount_matrix(dev, &st->orientation); + if (ret) + return dev_err_probe(dev, ret, "Failed to retrieve mounting matrix\n"); + + st->vddio_supply =3D devm_regulator_get(dev, "vddio"); + if (IS_ERR(st->vddio_supply)) + return PTR_ERR(st->vddio_supply); + + ret =3D devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to get vdd regulator\n"); + + /* IMU start-up time. */ + fsleep(100000); + + ret =3D inv_icm45600_enable_regulator_vddio(st); + if (ret) + return ret; + + ret =3D devm_add_action_or_reset(dev, inv_icm45600_disable_vddio_reg, st); + if (ret) + return ret; + + ret =3D inv_icm45600_setup(st, chip_info, reset, bus_setup); + if (ret) + return ret; + + ret =3D inv_icm45600_timestamp_setup(st); + if (ret) + return ret; + + /* Setup runtime power management. */ + ret =3D devm_pm_runtime_set_active_enabled(dev); + if (ret) + return ret; + + pm_runtime_get_noresume(dev); + /* Suspend after 2 seconds. */ + pm_runtime_set_autosuspend_delay(dev, 2000); + pm_runtime_use_autosuspend(dev); + pm_runtime_put(dev); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(inv_icm45600_core_probe, "IIO_ICM45600"); + +/* + * Suspend saves sensors state and turns everything off. + */ +static int inv_icm45600_suspend(struct device *dev) +{ + struct inv_icm45600_state *st =3D dev_get_drvdata(dev); + int ret; + + scoped_guard(mutex, &st->lock) { + /* Save sensors states */ + st->suspended.gyro =3D st->conf.gyro.mode; + st->suspended.accel =3D st->conf.accel.mode; + } + + return pm_runtime_force_suspend(dev); +} + +/* + * System resume gets the system back on and restores the sensors state. + * Manually put runtime power management in system active state. + */ +static int inv_icm45600_resume(struct device *dev) +{ + struct inv_icm45600_state *st =3D dev_get_drvdata(dev); + int ret =3D 0; + + ret =3D pm_runtime_force_resume(dev); + if (ret) + return ret; + + scoped_guard(mutex, &st->lock) + /* Restore sensors state. */ + ret =3D inv_icm45600_set_pwr_mgmt0(st, st->suspended.gyro, + st->suspended.accel, NULL); + + return ret; +} + +/* Runtime suspend will turn off sensors that are enabled by iio devices. = */ +static int inv_icm45600_runtime_suspend(struct device *dev) +{ + struct inv_icm45600_state *st =3D dev_get_drvdata(dev); + int ret; + + guard(mutex)(&st->lock); + + /* disable all sensors */ + ret =3D inv_icm45600_set_pwr_mgmt0(st, INV_ICM45600_SENSOR_MODE_OFF, + INV_ICM45600_SENSOR_MODE_OFF, NULL); + if (ret) + return ret; + + regulator_disable(st->vddio_supply); + + return 0; +} + +/* Sensors are enabled by iio devices, no need to turn them back on here. = */ +static int inv_icm45600_runtime_resume(struct device *dev) +{ + struct inv_icm45600_state *st =3D dev_get_drvdata(dev); + + guard(mutex)(&st->lock); + + return inv_icm45600_enable_regulator_vddio(st); +} + +EXPORT_NS_GPL_DEV_PM_OPS(inv_icm45600_pm_ops, IIO_ICM45600) =3D { + SYSTEM_SLEEP_PM_OPS(inv_icm45600_suspend, inv_icm45600_resume) + RUNTIME_PM_OPS(inv_icm45600_runtime_suspend, + inv_icm45600_runtime_resume, NULL) +}; + +MODULE_AUTHOR("InvenSense, Inc."); +MODULE_DESCRIPTION("InvenSense ICM-456xx device driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_INV_SENSORS_TIMESTAMP"); --=20 2.34.1 From nobody Sat Sep 13 22:31:04 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A6A2126FDB2; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755699885; cv=none; b=Ya4DmbvcojhEqWWTNBmwBJwep5HQ8iq/cRLXo41WqSyegYKQhwhkA2TRNMQZt15oWKyALTMMRQV40xCI792z5K5dvpP6JWKqDdQdHw/KvRh6LNRKRlLvR/2RYF8E1BDajWg2C4AynaE190eSmjnUuHqMnNYg/FCbbe8ekzRiYuc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755699885; c=relaxed/simple; bh=iWuO/NUBOxPQ8PyB/hzHjmuwTU69Lpj2W6tXIe4ygQc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=trSSY8XlS8r9oytCsivmxwpVXCVa1yt6OeSQbkgWGvWsqkxdP6czYBIijM+g//WUg7WXsOX6AmgDdnx5k1wYZkMkYrDR7yu7gTgoz3eAepkA62LRZzmXkSbfEQw0c/Y6UQIPRqL72711hMrcQFoEDRB8A+6SEHcKaX3B5kdZELA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=atnPApvX; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="atnPApvX" Received: by smtp.kernel.org (Postfix) with ESMTPS id 7A135C113CF; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1755699885; bh=iWuO/NUBOxPQ8PyB/hzHjmuwTU69Lpj2W6tXIe4ygQc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=atnPApvXjKiMsLspWkoElX/U8MSKMYX33HO/IGQjRdct3Y6MBgFvlq+o1cJfTHQWt S9lfHTDGdApoczMX5KnudH4so5I9+/49NEbi1NwKCN7H6czGTIzwNuOPYdqa3TP16Z y+ZbcqWp3C6r6/lM3TpmO52eBdmSYlBYUUYzao0clpVdCGN7cNW3TcSNyTrOXKIbQy UHIxKP0Z5xeyRqgqKlj9M0fpUolvsKSTUZUy1TTSaZiS33eGMdNxOVzcI0K/XlBdhG U8J8lW0/iUe/shtIrJv9QQ9wK7Oe/9i+QNo9mUUeCXRAvH/4F6KiXq2nTwn9Y+4Qcn rlZsbEifF3b7Q== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 71F8CCA0EE4; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Wed, 20 Aug 2025 14:24:21 +0000 Subject: [PATCH v5 3/9] iio: imu: inv_icm45600: add buffer support in iio devices Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250820-add_newport_driver-v5-3-2fc9f13dddee@tdk.com> References: <20250820-add_newport_driver-v5-0-2fc9f13dddee@tdk.com> In-Reply-To: <20250820-add_newport_driver-v5-0-2fc9f13dddee@tdk.com> To: Jonathan Cameron , David Lechner , =?utf-8?q?Nuno_S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, Remi Buisson X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1755699883; l=28197; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=7sReBcTlSe5SKVNWkBxn46Owj7RIZjXaQG5ARzMBkII=; b=seZgtXloV0sofx8MlMdSykTICXYQgetKF1KmCd0ZzWyr0i7DXptMnxJfL9yIHxzCCaoqSAuFM O6xT89U7bscB2T7RqGBJvShrrhqpT4OJMpFL4MVVzOUh+LJrzBKtUXK X-Developer-Key: i=remi.buisson@tdk.com; a=ed25519; pk=yDVMi4C7RpXN4dififo42A7fDDt3THYzoZoNq9lUZuo= X-Endpoint-Received: by B4 Relay for remi.buisson@tdk.com/20250411 with auth_id=372 X-Original-From: Remi Buisson Reply-To: remi.buisson@tdk.com From: Remi Buisson Add FIFO control functions. Support hwfifo watermark by multiplexing gyro and accel settings. Support hwfifo flush. Signed-off-by: Remi Buisson --- drivers/iio/imu/inv_icm45600/Makefile | 1 + drivers/iio/imu/inv_icm45600/inv_icm45600.h | 7 + drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c | 492 +++++++++++++++++= ++++ drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h | 98 ++++ drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 158 ++++++- 5 files changed, 754 insertions(+), 2 deletions(-) diff --git a/drivers/iio/imu/inv_icm45600/Makefile b/drivers/iio/imu/inv_ic= m45600/Makefile index 4f442b61896e91647c7947a044949792bae06a30..72f95bc30d993e0ea16b97622f4= a041a09ec6559 100644 --- a/drivers/iio/imu/inv_icm45600/Makefile +++ b/drivers/iio/imu/inv_icm45600/Makefile @@ -2,3 +2,4 @@ =20 obj-$(CONFIG_INV_ICM45600) +=3D inv-icm45600.o inv-icm45600-y +=3D inv_icm45600_core.o +inv-icm45600-y +=3D inv_icm45600_buffer.o diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600.h b/drivers/iio/imu/= inv_icm45600/inv_icm45600.h index 94ef0ff3ccda85583101f2eaca3bc3771141505a..a8f5769a09c9f734a15d02d2ee9= 53fac649dc04f 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600.h +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h @@ -8,8 +8,11 @@ #include #include #include +#include #include =20 +#include "inv_icm45600_buffer.h" + #define INV_ICM45600_REG_BANK_MASK GENMASK(15, 8) #define INV_ICM45600_REG_ADDR_MASK GENMASK(7, 0) =20 @@ -90,6 +93,8 @@ struct inv_icm45600_sensor_conf { u8 filter; }; =20 +#define INV_ICM45600_SENSOR_CONF_KEEP_VALUES {U8_MAX, U8_MAX, U8_MAX, U8_M= AX, } + struct inv_icm45600_conf { struct inv_icm45600_sensor_conf gyro; struct inv_icm45600_sensor_conf accel; @@ -127,6 +132,7 @@ extern const struct inv_icm45600_chip_info inv_icm45689= _chip_info; * @indio_gyro: gyroscope IIO device. * @indio_accel: accelerometer IIO device. * @timestamp: interrupt timestamps. + * @fifo: FIFO management structure. * @buffer: data transfer buffer aligned for DMA. */ struct inv_icm45600_state { @@ -143,6 +149,7 @@ struct inv_icm45600_state { s64 gyro; s64 accel; } timestamp; + struct inv_icm45600_fifo fifo; union { u8 buff[2]; __le16 u16; diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c b/drivers/i= io/imu/inv_icm45600/inv_icm45600_buffer.c new file mode 100644 index 0000000000000000000000000000000000000000..50fd21a24e34decfbe10426946a= 51c61353eb6a9 --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c @@ -0,0 +1,492 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Copyright (C) 2025 Invensense, Inc. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inv_icm45600_buffer.h" +#include "inv_icm45600.h" + +/* FIFO header: 1 byte */ +#define INV_ICM45600_FIFO_EXT_HEADER BIT(7) +#define INV_ICM45600_FIFO_HEADER_ACCEL BIT(6) +#define INV_ICM45600_FIFO_HEADER_GYRO BIT(5) +#define INV_ICM45600_FIFO_HEADER_HIGH_RES BIT(4) +#define INV_ICM45600_FIFO_HEADER_TMST_FSYNC GENMASK(3, 2) +#define INV_ICM45600_FIFO_HEADER_ODR_ACCEL BIT(1) +#define INV_ICM45600_FIFO_HEADER_ODR_GYRO BIT(0) + +struct inv_icm45600_fifo_1sensor_packet { + u8 header; + struct inv_icm45600_fifo_sensor_data data; + s8 temp; +} __packed; + +struct inv_icm45600_fifo_2sensors_packet { + u8 header; + struct inv_icm45600_fifo_sensor_data accel; + struct inv_icm45600_fifo_sensor_data gyro; + s8 temp; + __le16 timestamp; +} __packed; + +ssize_t inv_icm45600_fifo_decode_packet(const void *packet, + const struct inv_icm45600_fifo_sensor_data **accel, + const struct inv_icm45600_fifo_sensor_data **gyro, + const s8 **temp, + const __le16 **timestamp, unsigned int *odr) +{ + const struct inv_icm45600_fifo_1sensor_packet *pack1 =3D packet; + const struct inv_icm45600_fifo_2sensors_packet *pack2 =3D packet; + u8 header =3D *((const u8 *)packet); + + /* FIFO extended header */ + if (header & INV_ICM45600_FIFO_EXT_HEADER) { + /* Not yet supported */ + return 0; + } + + /* handle odr flags. */ + *odr =3D 0; + if (header & INV_ICM45600_FIFO_HEADER_ODR_GYRO) + *odr |=3D INV_ICM45600_SENSOR_GYRO; + if (header & INV_ICM45600_FIFO_HEADER_ODR_ACCEL) + *odr |=3D INV_ICM45600_SENSOR_ACCEL; + + /* Accel + Gyro data are present. */ + if ((header & INV_ICM45600_FIFO_HEADER_ACCEL) && + (header & INV_ICM45600_FIFO_HEADER_GYRO)) { + *accel =3D &pack2->accel; + *gyro =3D &pack2->gyro; + *temp =3D &pack2->temp; + *timestamp =3D &pack2->timestamp; + return sizeof(*pack2); + } + + /* Accel data only. */ + if (header & INV_ICM45600_FIFO_HEADER_ACCEL) { + *accel =3D &pack1->data; + *gyro =3D NULL; + *temp =3D &pack1->temp; + *timestamp =3D NULL; + return sizeof(*pack1); + } + + /* Gyro data only. */ + if (header & INV_ICM45600_FIFO_HEADER_GYRO) { + *accel =3D NULL; + *gyro =3D &pack1->data; + *temp =3D &pack1->temp; + *timestamp =3D NULL; + return sizeof(*pack1); + } + + /* Invalid packet if here. */ + return -EINVAL; +} + +void inv_icm45600_buffer_update_fifo_period(struct inv_icm45600_state *st) +{ + u32 period_gyro, period_accel; + + if (st->fifo.en & INV_ICM45600_SENSOR_GYRO) + period_gyro =3D inv_icm45600_odr_to_period(st->conf.gyro.odr); + else + period_gyro =3D U32_MAX; + + if (st->fifo.en & INV_ICM45600_SENSOR_ACCEL) + period_accel =3D inv_icm45600_odr_to_period(st->conf.accel.odr); + else + period_accel =3D U32_MAX; + + st->fifo.period =3D min(period_gyro, period_accel); +} + +int inv_icm45600_buffer_set_fifo_en(struct inv_icm45600_state *st, + unsigned int fifo_en) +{ + unsigned int mask, val; + int ret; + + /* Update only FIFO EN bits. */ + mask =3D INV_ICM45600_FIFO_CONFIG3_GYRO_EN | + INV_ICM45600_FIFO_CONFIG3_ACCEL_EN; + + val =3D 0; + if ((fifo_en & INV_ICM45600_SENSOR_GYRO) || (fifo_en & INV_ICM45600_SENSO= R_ACCEL)) + val =3D (INV_ICM45600_FIFO_CONFIG3_GYRO_EN | INV_ICM45600_FIFO_CONFIG3_A= CCEL_EN); + + ret =3D regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3, mask, = val); + if (ret) + return ret; + + st->fifo.en =3D fifo_en; + inv_icm45600_buffer_update_fifo_period(st); + + return 0; +} + +static unsigned int inv_icm45600_wm_truncate(unsigned int watermark, size_= t packet_size, + unsigned int fifo_period) +{ + size_t watermark_max, grace_samples; + + /* Keep 20ms for processing FIFO. fifo_period is in ns */ + grace_samples =3D (20U * 1000000U) / fifo_period; + if (grace_samples < 1) + grace_samples =3D 1; + + watermark_max =3D INV_ICM45600_FIFO_SIZE_MAX / packet_size; + watermark_max -=3D grace_samples; + + return min(watermark, watermark_max); +} + +/** + * inv_icm45600_buffer_update_watermark - update watermark FIFO threshold + * @st: driver internal state + * + * Returns 0 on success, a negative error code otherwise. + * + * FIFO watermark threshold is computed based on the required watermark va= lues + * set for gyro and accel sensors. Since watermark is all about acceptable= data + * latency, use the smallest setting between the 2. It means choosing the + * smallest latency but this is not as simple as choosing the smallest wat= ermark + * value. Latency depends on watermark and ODR. It requires several steps: + * 1) compute gyro and accel latencies and choose the smallest value. + * 2) adapt the chosen latency so that it is a multiple of both gyro and a= ccel + * ones. Otherwise it is possible that you don't meet a requirement. (f= or + * example with gyro @100Hz wm 4 and accel @100Hz with wm 6, choosing t= he + * value of 4 will not meet accel latency requirement because 6 is not a + * multiple of 4. You need to use the value 2.) + * 3) Since all periods are multiple of each others, watermark is computed= by + * dividing this computed latency by the smallest period, which corresp= onds + * to the FIFO frequency. + */ +int inv_icm45600_buffer_update_watermark(struct inv_icm45600_state *st) +{ + const size_t packet_size =3D sizeof(struct inv_icm45600_fifo_2sensors_pac= ket); + unsigned int wm_gyro, wm_accel, watermark; + u32 period_gyro, period_accel, period; + u32 latency_gyro, latency_accel, latency; + + /* Compute sensors latency, depending on sensor watermark and odr. */ + wm_gyro =3D inv_icm45600_wm_truncate(st->fifo.watermark.gyro, packet_size, + st->fifo.period); + wm_accel =3D inv_icm45600_wm_truncate(st->fifo.watermark.accel, packet_si= ze, + st->fifo.period); + /* Use us for odr to avoid overflow using 32 bits values. */ + period_gyro =3D inv_icm45600_odr_to_period(st->conf.gyro.odr) / 1000UL; + period_accel =3D inv_icm45600_odr_to_period(st->conf.accel.odr) / 1000UL; + latency_gyro =3D period_gyro * wm_gyro; + latency_accel =3D period_accel * wm_accel; + + /* 0 value for watermark means that the sensor is turned off. */ + if (wm_gyro =3D=3D 0 && wm_accel =3D=3D 0) + return 0; + + if (latency_gyro =3D=3D 0) { + watermark =3D wm_accel; + st->fifo.watermark.eff_accel =3D wm_accel; + } else if (latency_accel =3D=3D 0) { + watermark =3D wm_gyro; + st->fifo.watermark.eff_gyro =3D wm_gyro; + } else { + /* Compute the smallest latency that is a multiple of both. */ + if (latency_gyro <=3D latency_accel) + latency =3D latency_gyro - (latency_accel % latency_gyro); + else + latency =3D latency_accel - (latency_gyro % latency_accel); + /* Use the shortest period. */ + period =3D min(period_gyro, period_accel); + /* All this works because periods are multiple of each others. */ + watermark =3D max(latency / period, 1); + /* Update effective watermark. */ + st->fifo.watermark.eff_gyro =3D max(latency / period_gyro, 1); + st->fifo.watermark.eff_accel =3D max(latency / period_accel, 1); + } + + + st->buffer.u16 =3D cpu_to_le16(watermark); + return regmap_bulk_write(st->map, INV_ICM45600_REG_FIFO_WATERMARK, + &st->buffer.u16, sizeof(st->buffer.u16)); +} + +static int inv_icm45600_buffer_preenable(struct iio_dev *indio_dev) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + struct device *dev =3D regmap_get_device(st->map); + struct inv_icm45600_sensor_state *sensor_st =3D iio_priv(indio_dev); + struct inv_sensors_timestamp *ts =3D &sensor_st->ts; + int ret; + + ret =3D 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 e= nable + * bits. + */ +static int inv_icm45600_buffer_postenable(struct iio_dev *indio_dev) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + unsigned int val; + int ret; + + guard(mutex)(&st->lock); + + /* Exit if FIFO is already on. */ + if (st->fifo.on) { + /* Increase FIFO on counter. */ + st->fifo.on++; + return 0; + } + + /* Flush all FIFO data. */ + ret =3D regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG2, + INV_ICM45600_REG_FIFO_CONFIG2_FIFO_FLUSH); + if (ret) + return ret; + + /* Set FIFO threshold and full interrupt. */ + ret =3D regmap_set_bits(st->map, INV_ICM45600_REG_INT1_CONFIG0, + INV_ICM45600_INT1_CONFIG0_FIFO_THS_EN | + INV_ICM45600_INT1_CONFIG0_FIFO_FULL_EN); + if (ret) + return ret; + + /* Set FIFO in streaming mode. */ + val =3D FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK, + INV_ICM45600_FIFO_CONFIG0_MODE_STREAM); + ret =3D regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0, + INV_ICM45600_FIFO_CONFIG0_MODE_MASK, val); + if (ret) + return ret; + + /* Enable writing sensor data to FIFO. */ + ret =3D regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3, + INV_ICM45600_FIFO_CONFIG3_IF_EN); + if (ret) + return ret; + + /* Increase FIFO on counter. */ + st->fifo.on++; + return 0; +} + +static int inv_icm45600_buffer_predisable(struct iio_dev *indio_dev) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + unsigned int val; + int ret; + + guard(mutex)(&st->lock); + + /* Exit if there are several sensors using the FIFO. */ + if (st->fifo.on > 1) { + /* decrease FIFO on counter */ + st->fifo.on--; + return 0; + } + + /* Disable writing sensor data to FIFO. */ + ret =3D regmap_clear_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3, + INV_ICM45600_FIFO_CONFIG3_IF_EN); + if (ret) + return ret; + + /* Set FIFO in bypass mode. */ + val =3D FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK, + INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS); + ret =3D regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0, + INV_ICM45600_FIFO_CONFIG0_MODE_MASK, val); + if (ret) + return ret; + + /* Disable FIFO threshold and full interrupt. */ + ret =3D regmap_clear_bits(st->map, INV_ICM45600_REG_INT1_CONFIG0, + INV_ICM45600_INT1_CONFIG0_FIFO_THS_EN | + INV_ICM45600_INT1_CONFIG0_FIFO_FULL_EN); + if (ret) + return ret; + + /* Flush all FIFO data. */ + ret =3D regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG2, + INV_ICM45600_REG_FIFO_CONFIG2_FIFO_FLUSH); + if (ret) + return ret; + + /* Decrease FIFO on counter. */ + st->fifo.on--; + return 0; +} + +static int _inv_icm45600_buffer_postdisable(struct inv_icm45600_state *st, + unsigned int sensor, unsigned int *watermark, + unsigned int *sleep) +{ + struct inv_icm45600_sensor_conf conf =3D INV_ICM45600_SENSOR_CONF_KEEP_VA= LUES; + int ret; + + ret =3D inv_icm45600_buffer_set_fifo_en(st, st->fifo.en & ~sensor); + if (ret) + return ret; + + *watermark =3D 0; + ret =3D inv_icm45600_buffer_update_watermark(st); + if (ret) + return ret; + + conf.mode =3D INV_ICM45600_SENSOR_MODE_OFF; + if (sensor =3D=3D INV_ICM45600_SENSOR_GYRO) + ret =3D inv_icm45600_set_gyro_conf(st, &conf, sleep); + else + ret =3D inv_icm45600_set_accel_conf(st, &conf, sleep); + + return ret; +} + +static int inv_icm45600_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + struct device *dev =3D regmap_get_device(st->map); + unsigned int sensor; + unsigned int *watermark; + unsigned int sleep; + int ret; + + if (indio_dev =3D=3D st->indio_gyro) { + sensor =3D INV_ICM45600_SENSOR_GYRO; + watermark =3D &st->fifo.watermark.gyro; + } else if (indio_dev =3D=3D st->indio_accel) { + sensor =3D INV_ICM45600_SENSOR_ACCEL; + watermark =3D &st->fifo.watermark.accel; + } else { + return -EINVAL; + } + + scoped_guard(mutex, &st->lock) + ret =3D _inv_icm45600_buffer_postdisable(st, sensor, watermark, &sleep); + + /* Sleep required time. */ + if (sleep) + msleep(sleep); + + pm_runtime_put_autosuspend(dev); + + return ret; +} + +const struct iio_buffer_setup_ops inv_icm45600_buffer_ops =3D { + .preenable =3D inv_icm45600_buffer_preenable, + .postenable =3D inv_icm45600_buffer_postenable, + .predisable =3D inv_icm45600_buffer_predisable, + .postdisable =3D inv_icm45600_buffer_postdisable, +}; + +int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st) +{ + const ssize_t packet_size =3D sizeof(struct inv_icm45600_fifo_2sensors_pa= cket); + __le16 *raw_fifo_count; + size_t fifo_nb, i; + ssize_t size; + const struct inv_icm45600_fifo_sensor_data *accel, *gyro; + const __le16 *timestamp; + const s8 *temp; + unsigned int odr; + int ret; + + /* Reset all samples counters. */ + st->fifo.count =3D 0; + st->fifo.nb.gyro =3D 0; + st->fifo.nb.accel =3D 0; + st->fifo.nb.total =3D 0; + + /* Read FIFO count value. */ + raw_fifo_count =3D &st->buffer.u16; + ret =3D regmap_bulk_read(st->map, INV_ICM45600_REG_FIFO_COUNT, + raw_fifo_count, sizeof(*raw_fifo_count)); + if (ret) + return ret; + fifo_nb =3D le16_to_cpup(raw_fifo_count); + + /* Check and limit number of samples if requested. */ + if (fifo_nb =3D=3D 0) + return 0; + + /* Try to read all FIFO data in internal buffer. */ + st->fifo.count =3D fifo_nb * packet_size; + ret =3D regmap_noinc_read(st->map, INV_ICM45600_REG_FIFO_DATA, + st->fifo.data, st->fifo.count); + if (ret =3D=3D -ENOTSUPP || ret =3D=3D -EFBIG) { + /* Read full fifo is not supported, read samples one by one. */ + ret =3D 0; + for (i =3D 0; i < st->fifo.count && ret =3D=3D 0; i +=3D packet_size) + ret =3D regmap_noinc_read(st->map, INV_ICM45600_REG_FIFO_DATA, + &st->fifo.data[i], packet_size); + } + if (ret) + return ret; + + for (i =3D 0; i < st->fifo.count; i +=3D size) { + size =3D inv_icm45600_fifo_decode_packet(&st->fifo.data[i], &accel, &gyr= o, + &temp, ×tamp, &odr); + if (size <=3D 0) + break; + if (gyro !=3D NULL && inv_icm45600_fifo_is_data_valid(gyro)) + st->fifo.nb.gyro++; + if (accel !=3D NULL && inv_icm45600_fifo_is_data_valid(accel)) + st->fifo.nb.accel++; + st->fifo.nb.total++; + } + + return 0; +} + +int inv_icm45600_buffer_init(struct inv_icm45600_state *st) +{ + int ret; + unsigned int val; + + st->fifo.watermark.eff_gyro =3D 1; + st->fifo.watermark.eff_accel =3D 1; + + /* Disable all FIFO EN bits. */ + ret =3D regmap_write(st->map, INV_ICM45600_REG_FIFO_CONFIG3, 0); + if (ret) + return ret; + + /* Disable FIFO and set depth. */ + val =3D FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK, + INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS); + val |=3D INV_ICM45600_FIFO_CONFIG0_FIFO_DEPTH_MAX; + ret =3D regmap_write(st->map, INV_ICM45600_REG_FIFO_CONFIG0, val); + if (ret) + return ret; + + /* Enable only timestamp in fifo, disable compression. */ + ret =3D regmap_write(st->map, INV_ICM45600_REG_FIFO_CONFIG4, + INV_ICM45600_FIFO_CONFIG4_TMST_FSYNC_EN); + if (ret) + return ret; + + /* Enable FIFO continuous watermark interrupt. */ + return regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG2, + INV_ICM45600_REG_FIFO_CONFIG2_WM_GT_TH); +} diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h b/drivers/i= io/imu/inv_icm45600/inv_icm45600_buffer.h new file mode 100644 index 0000000000000000000000000000000000000000..da463461b5f2708014126f868fa= 6008db0520a4e --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2025 Invensense, Inc. */ + +#ifndef INV_ICM45600_BUFFER_H_ +#define INV_ICM45600_BUFFER_H_ + +#include +#include +#include + +struct inv_icm45600_state; + +#define INV_ICM45600_SENSOR_GYRO BIT(0) +#define INV_ICM45600_SENSOR_ACCEL BIT(1) +#define INV_ICM45600_SENSOR_TEMP BIT(2) + +/** + * struct inv_icm45600_fifo - FIFO state variables + * @on: reference counter for FIFO on. + * @en: bits field of INV_ICM45600_SENSOR_* for FIFO EN bits. + * @period: FIFO internal period. + * @watermark: watermark configuration values for accel and gyro. + * @count: number of bytes in the FIFO data buffer. + * @nb: gyro, accel and total samples in the FIFO data buffer. + * @data: FIFO data buffer aligned for DMA (8kB) + */ +struct inv_icm45600_fifo { + unsigned int on; + unsigned int en; + u32 period; + struct { + unsigned int gyro; + unsigned int accel; + unsigned int eff_gyro; + unsigned int eff_accel; + } watermark; + size_t count; + struct { + size_t gyro; + size_t accel; + size_t total; + } nb; + u8 *data; +}; + +/* FIFO data packet */ +struct inv_icm45600_fifo_sensor_data { + __le16 x; + __le16 y; + __le16 z; +} __packed; +#define INV_ICM45600_DATA_INVALID S16_MIN + +static inline s16 inv_icm45600_fifo_get_sensor_data(__le16 d) +{ + return le16_to_cpu(d); +} + +static inline bool +inv_icm45600_fifo_is_data_valid(const struct inv_icm45600_fifo_sensor_data= *s) +{ + s16 x, y, z; + + x =3D inv_icm45600_fifo_get_sensor_data(s->x); + y =3D inv_icm45600_fifo_get_sensor_data(s->y); + z =3D inv_icm45600_fifo_get_sensor_data(s->z); + + if (x =3D=3D INV_ICM45600_DATA_INVALID && + y =3D=3D INV_ICM45600_DATA_INVALID && + z =3D=3D INV_ICM45600_DATA_INVALID) + return false; + + return true; +} + +ssize_t inv_icm45600_fifo_decode_packet(const void *packet, + const struct inv_icm45600_fifo_sensor_data **accel, + const struct inv_icm45600_fifo_sensor_data **gyro, + const s8 **temp, + const __le16 **timestamp, unsigned int *odr); + +extern const struct iio_buffer_setup_ops inv_icm45600_buffer_ops; + +int inv_icm45600_buffer_init(struct inv_icm45600_state *st); + +void inv_icm45600_buffer_update_fifo_period(struct inv_icm45600_state *st); + +int inv_icm45600_buffer_set_fifo_en(struct inv_icm45600_state *st, + unsigned int fifo_en); + +int inv_icm45600_buffer_update_watermark(struct inv_icm45600_state *st); + +int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st); + +int inv_icm45600_buffer_hwfifo_flush(struct inv_icm45600_state *st, + unsigned int count); + +#endif diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio= /imu/inv_icm45600/inv_icm45600_core.c index 4b8f3dad8984cfa6642b1b4c94acbb0674084f3f..1657daf7c2d0445d6d10658f7f5= b0f16f1623d69 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include #include @@ -15,6 +17,7 @@ #include #include =20 +#include "inv_icm45600_buffer.h" #include "inv_icm45600.h" =20 static int inv_icm45600_ireg_read(struct regmap *map, unsigned int reg, @@ -510,6 +513,94 @@ static int inv_icm45600_setup(struct inv_icm45600_stat= e *st, return inv_icm45600_set_conf(st, chip_info->conf); } =20 +static irqreturn_t inv_icm45600_irq_timestamp(int irq, void *_data) +{ + struct inv_icm45600_state *st =3D _data; + + st->timestamp.gyro =3D iio_get_time_ns(st->indio_gyro); + st->timestamp.accel =3D iio_get_time_ns(st->indio_accel); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t inv_icm45600_irq_handler(int irq, void *_data) +{ + struct inv_icm45600_state *st =3D _data; + struct device *dev =3D regmap_get_device(st->map); + unsigned int mask, status; + int ret; + + guard(mutex)(&st->lock); + + ret =3D regmap_read(st->map, INV_ICM45600_REG_INT_STATUS, &status); + if (ret) + return IRQ_HANDLED; + + /* Read the FIFO data. */ + mask =3D INV_ICM45600_INT_STATUS_FIFO_THS | INV_ICM45600_INT_STATUS_FIFO_= FULL; + if (status & mask) { + ret =3D inv_icm45600_buffer_fifo_read(st); + if (ret) { + dev_err(dev, "FIFO read error %d\n", ret); + return IRQ_HANDLED; + } + } + + /* FIFO full warning. */ + if (status & INV_ICM45600_INT_STATUS_FIFO_FULL) + dev_warn(dev, "FIFO full possible data lost!\n"); + + return IRQ_HANDLED; +} + +/** + * inv_icm45600_irq_init() - initialize int pin and interrupt handler + * @st: driver internal state + * @irq: irq number + * @irq_type: irq trigger type + * @open_drain: true if irq is open drain, false for push-pull + * + * Returns 0 on success, a negative error code otherwise. + */ +static int inv_icm45600_irq_init(struct inv_icm45600_state *st, int irq, + int irq_type, bool open_drain) +{ + struct device *dev =3D regmap_get_device(st->map); + unsigned int val; + int ret; + + /* Configure INT1 interrupt: default is active low on edge. */ + switch (irq_type) { + case IRQF_TRIGGER_RISING: + case IRQF_TRIGGER_HIGH: + val =3D INV_ICM45600_INT1_CONFIG2_ACTIVE_HIGH; + break; + default: + val =3D INV_ICM45600_INT1_CONFIG2_ACTIVE_LOW; + break; + } + + switch (irq_type) { + case IRQF_TRIGGER_LOW: + case IRQF_TRIGGER_HIGH: + val |=3D INV_ICM45600_INT1_CONFIG2_LATCHED; + break; + default: + break; + } + + if (!open_drain) + val |=3D INV_ICM45600_INT1_CONFIG2_PUSH_PULL; + + ret =3D regmap_write(st->map, INV_ICM45600_REG_INT1_CONFIG2, val); + if (ret) + return ret; + + return devm_request_threaded_irq(dev, irq, inv_icm45600_irq_timestamp, + inv_icm45600_irq_handler, irq_type | IRQF_ONESHOT, + "inv_icm45600", st); +} + static int inv_icm45600_timestamp_setup(struct inv_icm45600_state *st) { /* Enable timestamps. */ @@ -549,8 +640,20 @@ int inv_icm45600_core_probe(struct regmap *regmap, con= st struct inv_icm45600_chi struct fwnode_handle *fwnode; struct inv_icm45600_state *st; struct regmap *regmap_custom; + int irq, irq_type; + bool open_drain; int ret; =20 + /* Get INT1 only supported interrupt. */ + fwnode =3D dev_fwnode(dev); + irq =3D fwnode_irq_get_byname(fwnode, "int1"); + if (irq < 0) + return dev_err_probe(dev, irq, "Missing int1 interrupt\n"); + + irq_type =3D irq_get_trigger_type(irq); + + open_drain =3D device_property_read_bool(dev, "drive-open-drain"); + regmap_custom =3D devm_regmap_init(dev, &inv_icm45600_regmap_bus, regmap, &inv_icm45600_regmap_config); if (IS_ERR(regmap_custom)) @@ -562,6 +665,10 @@ int inv_icm45600_core_probe(struct regmap *regmap, con= st struct inv_icm45600_chi =20 dev_set_drvdata(dev, st); =20 + st->fifo.data =3D devm_kzalloc(dev, 8192, GFP_KERNEL); + if (!st->fifo.data) + return dev_err_probe(dev, -ENOMEM, "Cannot allocate fifo memory\n"); + ret =3D devm_mutex_init(dev, &st->lock); if (ret) return ret; @@ -599,6 +706,14 @@ int inv_icm45600_core_probe(struct regmap *regmap, con= st struct inv_icm45600_chi if (ret) return ret; =20 + ret =3D inv_icm45600_buffer_init(st); + if (ret) + return ret; + + ret =3D inv_icm45600_irq_init(st, irq, irq_type, open_drain); + if (ret) + return ret; + /* Setup runtime power management. */ ret =3D devm_pm_runtime_set_active_enabled(dev); if (ret) @@ -623,6 +738,23 @@ static int inv_icm45600_suspend(struct device *dev) int ret; =20 scoped_guard(mutex, &st->lock) { + /* Disable FIFO data streaming. */ + if (st->fifo.on) { + unsigned int val; + + /* Clear FIFO_CONFIG3_IF_EN before changing the FIFO configuration */ + ret =3D regmap_clear_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3, + INV_ICM45600_FIFO_CONFIG3_IF_EN); + if (ret) + return ret; + val =3D FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK, + INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS); + ret =3D regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0, + INV_ICM45600_FIFO_CONFIG0_MODE_MASK, val); + if (ret) + return ret; + } + /* Save sensors states */ st->suspended.gyro =3D st->conf.gyro.mode; st->suspended.accel =3D st->conf.accel.mode; @@ -644,10 +776,32 @@ static int inv_icm45600_resume(struct device *dev) if (ret) return ret; =20 - scoped_guard(mutex, &st->lock) + scoped_guard(mutex, &st->lock) { /* Restore sensors state. */ ret =3D inv_icm45600_set_pwr_mgmt0(st, st->suspended.gyro, - st->suspended.accel, NULL); + st->suspended.accel, NULL); + if (ret) + return ret; + + /* Restore FIFO data streaming. */ + if (st->fifo.on) { + struct inv_icm45600_sensor_state *gyro_st =3D iio_priv(st->indio_gyro); + struct inv_icm45600_sensor_state *accel_st =3D iio_priv(st->indio_accel= ); + unsigned int val; + + inv_sensors_timestamp_reset(&gyro_st->ts); + inv_sensors_timestamp_reset(&accel_st->ts); + val =3D FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK, + INV_ICM45600_FIFO_CONFIG0_MODE_STREAM); + ret =3D regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0, + INV_ICM45600_FIFO_CONFIG0_MODE_MASK, val); + if (ret) + return ret; + /* FIFO_CONFIG3_IF_EN must only be set at end of FIFO the configuration= */ + ret =3D regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3, + INV_ICM45600_FIFO_CONFIG3_IF_EN); + } + } =20 return ret; } --=20 2.34.1 From nobody Sat Sep 13 22:31:04 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EE90F275B0A; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755699886; cv=none; b=pdsophViFSJURWYjI0lN1OMubrvIu3P1DF8hIwNLIEvTDtKnOUnLCkgfD0JXTv8DRQ9VDj8TG9wQWvU+EIKFGPY9PzbFh/+tgb1aNYZGtc52zR3kYOnvMg2r3LGgJrq35McC17AjrgqqPiosrqLubRl9mbEY8u0BHtGv2RBt5JM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755699886; c=relaxed/simple; bh=iV1cHVTZOpKmWsKgE6I1SJMRols0ul8ljgR2JlaAEB0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=PjP1cHHrCEmyJihtlSOhG+FkCySlNdSFlu0oh1SBtKazPl7WYRlXq1avBHLUzP2roBEhzWOFCECPl4FgjZLCUvBi8h9rGAcw9c2Q/RtbZxXLBzk3U7mqRgJbsEEc8FhsvdeBh47ICtm5mKgMeu28OxPK1JaLn1z8Vk70NdG5BQA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=fO2bZgQl; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="fO2bZgQl" Received: by smtp.kernel.org (Postfix) with ESMTPS id 9033DC19421; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1755699885; bh=iV1cHVTZOpKmWsKgE6I1SJMRols0ul8ljgR2JlaAEB0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=fO2bZgQlHpsomwjAP2bKklfGnHhDBSW7ZP6RwfUGM01t2SXYlX/9vc1koiiAdMnoK JNwTtdPNAf85sWRL2mqMwoHmC3g+TL9swGfEEPnrH87gIHEI4ZeuT8KZ/zDLWtnmgv eyIdw7Tot9s9A/Pfk4XIokb1krg9YIx1XFi8ySIxI9NBN3fuf6jiNhogHcJcjfmuVo iiO3czvL8IzI9Y4/aJ0e4bFSd11PpeAj15uLRo5/1aCkUuvT4f5FNEakw8bEcAdlAg iB6MJJLmW7SdL+NlkkpLFsYuVWWAwS82Pf+TIyBhy88dael0exHYoVMUe7q/0Wj4UR C9349FEh5439g== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 87D2CCA0EED; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Wed, 20 Aug 2025 14:24:22 +0000 Subject: [PATCH v5 4/9] iio: imu: inv_icm45600: add IMU IIO gyroscope device Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250820-add_newport_driver-v5-4-2fc9f13dddee@tdk.com> References: <20250820-add_newport_driver-v5-0-2fc9f13dddee@tdk.com> In-Reply-To: <20250820-add_newport_driver-v5-0-2fc9f13dddee@tdk.com> To: Jonathan Cameron , David Lechner , =?utf-8?q?Nuno_S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, Remi Buisson X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1755699883; l=38002; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=wi2VuFFuUCGamBruMs5lxhen9GfdBAn3HvcjNggFwtY=; b=GdLK58aPrPreCtcZ7JB/0PPrQSgcCvp8/E5h+7b1DXRzIMVta2GFpeWBPThc4dOC+zu/06EVN W8x8BGyBxKUDVWc6TM3naPdr7pGs7U3u96/Na1YHwS9Qzbz8rH7DeBP X-Developer-Key: i=remi.buisson@tdk.com; a=ed25519; pk=yDVMi4C7RpXN4dififo42A7fDDt3THYzoZoNq9lUZuo= X-Endpoint-Received: by B4 Relay for remi.buisson@tdk.com/20250411 with auth_id=372 X-Original-From: Remi Buisson Reply-To: remi.buisson@tdk.com From: Remi Buisson Add IIO device for gyroscope sensor with data polling interface and FIFO parsing. Attributes: raw, scale, sampling_frequency, calibbias. Temperature is available as a processed channel. Signed-off-by: Remi Buisson --- drivers/iio/imu/inv_icm45600/Kconfig | 2 + drivers/iio/imu/inv_icm45600/Makefile | 1 + drivers/iio/imu/inv_icm45600/inv_icm45600.h | 29 + drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c | 76 +- drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h | 5 +- drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 104 ++- drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c | 792 +++++++++++++++++= ++++ 7 files changed, 1006 insertions(+), 3 deletions(-) diff --git a/drivers/iio/imu/inv_icm45600/Kconfig b/drivers/iio/imu/inv_icm= 45600/Kconfig index 8cb5543e0a5817323ab7b2d520dd3430ac5dbc99..ea0a8d20cba26549b74105fa6fd= bca1ddb222633 100644 --- a/drivers/iio/imu/inv_icm45600/Kconfig +++ b/drivers/iio/imu/inv_icm45600/Kconfig @@ -2,4 +2,6 @@ =20 config INV_ICM45600 tristate + select IIO_BUFFER + select IIO_KFIFO_BUF select IIO_INV_SENSORS_TIMESTAMP diff --git a/drivers/iio/imu/inv_icm45600/Makefile b/drivers/iio/imu/inv_ic= m45600/Makefile index 72f95bc30d993e0ea16b97622f4a041a09ec6559..b5954b4053c25259957faf4fc9d= 1979c9ff74608 100644 --- a/drivers/iio/imu/inv_icm45600/Makefile +++ b/drivers/iio/imu/inv_icm45600/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_INV_ICM45600) +=3D inv-icm45600.o inv-icm45600-y +=3D inv_icm45600_core.o inv-icm45600-y +=3D inv_icm45600_buffer.o +inv-icm45600-y +=3D inv_icm45600_gyro.o diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600.h b/drivers/iio/imu/= inv_icm45600/inv_icm45600.h index a8f5769a09c9f734a15d02d2ee953fac649dc04f..a243cf5ad7e05039a8b1d4bed07= e30e89e8f6ee4 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600.h +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h @@ -109,6 +109,8 @@ struct inv_icm45600_chip_info { u8 whoami; const char *name; const struct inv_icm45600_conf *conf; + const int *gyro_scales; + const int gyro_scales_len; }; =20 extern const struct inv_icm45600_chip_info inv_icm45605_chip_info; @@ -120,6 +122,9 @@ extern const struct inv_icm45600_chip_info inv_icm45687= _chip_info; extern const struct inv_icm45600_chip_info inv_icm45688p_chip_info; extern const struct inv_icm45600_chip_info inv_icm45689_chip_info; =20 +extern const int inv_icm45600_gyro_scale[][2]; +extern const int inv_icm45686_gyro_scale[][2]; + /** * struct inv_icm45600_state - driver state variables * @lock: lock for serializing multiple registers access. @@ -323,6 +328,26 @@ const struct iio_mount_matrix * inv_icm45600_get_mount_matrix(const struct iio_dev *indio_dev, const struct iio_chan_spec *chan); =20 +#define INV_ICM45600_TEMP_CHAN(_index) \ + { \ + .type =3D IIO_TEMP, \ + .info_mask_separate =3D \ + BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index =3D _index, \ + .scan_type =3D { \ + .sign =3D 's', \ + .realbits =3D 16, \ + .storagebits =3D 16, \ + .endianness =3D IIO_LE, \ + }, \ + } + +int inv_icm45600_temp_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask); + u32 inv_icm45600_odr_to_period(enum inv_icm45600_odr odr); =20 int inv_icm45600_set_accel_conf(struct inv_icm45600_state *st, @@ -340,4 +365,8 @@ int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm45600_chip_info *chip_info, bool reset, inv_icm45600_bus_setup bus_setup); =20 +struct iio_dev *inv_icm45600_gyro_init(struct inv_icm45600_state *st); + +int inv_icm45600_gyro_parse_fifo(struct iio_dev *indio_dev); + #endif diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c b/drivers/i= io/imu/inv_icm45600/inv_icm45600_buffer.c index 50fd21a24e34decfbe10426946a51c61353eb6a9..5bf9535e27e8942312fe9749ce0= c733149de0a9d 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c @@ -400,7 +400,8 @@ const struct iio_buffer_setup_ops inv_icm45600_buffer_o= ps =3D { .postdisable =3D inv_icm45600_buffer_postdisable, }; =20 -int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st) +int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st, + unsigned int max) { const ssize_t packet_size =3D sizeof(struct inv_icm45600_fifo_2sensors_pa= cket); __le16 *raw_fifo_count; @@ -429,6 +430,8 @@ int inv_icm45600_buffer_fifo_read(struct inv_icm45600_s= tate *st) /* Check and limit number of samples if requested. */ if (fifo_nb =3D=3D 0) return 0; + if (max > 0 && fifo_nb > max) + fifo_nb =3D max; =20 /* Try to read all FIFO data in internal buffer. */ st->fifo.count =3D fifo_nb * packet_size; @@ -459,6 +462,77 @@ int inv_icm45600_buffer_fifo_read(struct inv_icm45600_= state *st) return 0; } =20 +int inv_icm45600_buffer_fifo_parse(struct inv_icm45600_state *st) +{ + struct inv_icm45600_sensor_state *gyro_st =3D iio_priv(st->indio_gyro); + struct inv_icm45600_sensor_state *accel_st =3D iio_priv(st->indio_accel); + struct inv_sensors_timestamp *ts; + int ret; + + if (st->fifo.nb.total =3D=3D 0) + return 0; + + /* Handle gyroscope timestamp and FIFO data parsing. */ + if (st->fifo.nb.gyro > 0) { + ts =3D &gyro_st->ts; + inv_sensors_timestamp_interrupt(ts, st->fifo.watermark.eff_gyro, + st->timestamp.gyro); + ret =3D inv_icm45600_gyro_parse_fifo(st->indio_gyro); + if (ret) + return ret; + } + + /* Handle accelerometer timestamp and FIFO data parsing. */ + if (st->fifo.nb.accel > 0) { + ts =3D &accel_st->ts; + inv_sensors_timestamp_interrupt(ts, st->fifo.watermark.eff_accel, + st->timestamp.accel); + ret =3D inv_icm45600_accel_parse_fifo(st->indio_accel); + if (ret) + return ret; + } + + return 0; +} + +int inv_icm45600_buffer_hwfifo_flush(struct inv_icm45600_state *st, + unsigned int count) +{ + struct inv_icm45600_sensor_state *gyro_st =3D iio_priv(st->indio_gyro); + struct inv_icm45600_sensor_state *accel_st =3D iio_priv(st->indio_accel); + struct inv_sensors_timestamp *ts; + s64 gyro_ts, accel_ts; + int ret; + + gyro_ts =3D iio_get_time_ns(st->indio_gyro); + accel_ts =3D iio_get_time_ns(st->indio_accel); + + ret =3D inv_icm45600_buffer_fifo_read(st, count); + if (ret) + return ret; + + if (st->fifo.nb.total =3D=3D 0) + return 0; + + if (st->fifo.nb.gyro > 0) { + ts =3D &gyro_st->ts; + inv_sensors_timestamp_interrupt(ts, st->fifo.nb.gyro, gyro_ts); + ret =3D inv_icm45600_gyro_parse_fifo(st->indio_gyro); + if (ret) + return ret; + } + + if (st->fifo.nb.accel > 0) { + ts =3D &accel_st->ts; + inv_sensors_timestamp_interrupt(ts, st->fifo.nb.accel, accel_ts); + ret =3D inv_icm45600_accel_parse_fifo(st->indio_accel); + if (ret) + return ret; + } + + return 0; +} + int inv_icm45600_buffer_init(struct inv_icm45600_state *st) { int ret; diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h b/drivers/i= io/imu/inv_icm45600/inv_icm45600_buffer.h index da463461b5f2708014126f868fa6008db0520a4e..cf4495a1d53921ad9ee35fbe3dc= 8862af46a9b65 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h @@ -90,7 +90,10 @@ int inv_icm45600_buffer_set_fifo_en(struct inv_icm45600_= state *st, =20 int inv_icm45600_buffer_update_watermark(struct inv_icm45600_state *st); =20 -int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st); +int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st, + unsigned int max); + +int inv_icm45600_buffer_fifo_parse(struct inv_icm45600_state *st); =20 int inv_icm45600_buffer_hwfifo_flush(struct inv_icm45600_state *st, unsigned int count); diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio= /imu/inv_icm45600/inv_icm45600_core.c index 1657daf7c2d0445d6d10658f7f5b0f16f1623d69..706fda073060f18e3627b1de66c= 96ad95a74b8e1 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c @@ -155,6 +155,8 @@ const struct inv_icm45600_chip_info inv_icm45605_chip_i= nfo =3D { .whoami =3D INV_ICM45600_WHOAMI_ICM45605, .name =3D "icm45605", .conf =3D &inv_icm45600_default_conf, + .gyro_scales =3D (const int *)inv_icm45600_gyro_scale, + .gyro_scales_len =3D INV_ICM45600_GYRO_FS_MAX, }; EXPORT_SYMBOL_NS_GPL(inv_icm45605_chip_info, "IIO_ICM45600"); =20 @@ -162,6 +164,8 @@ const struct inv_icm45600_chip_info inv_icm45606_chip_i= nfo =3D { .whoami =3D INV_ICM45600_WHOAMI_ICM45606, .name =3D "icm45606", .conf =3D &inv_icm45600_default_conf, + .gyro_scales =3D (const int *)inv_icm45600_gyro_scale, + .gyro_scales_len =3D INV_ICM45600_GYRO_FS_MAX, }; EXPORT_SYMBOL_NS_GPL(inv_icm45606_chip_info, "IIO_ICM45600"); =20 @@ -169,6 +173,8 @@ const struct inv_icm45600_chip_info inv_icm45608_chip_i= nfo =3D { .whoami =3D INV_ICM45600_WHOAMI_ICM45608, .name =3D "icm45608", .conf =3D &inv_icm45600_default_conf, + .gyro_scales =3D (const int *)inv_icm45600_gyro_scale, + .gyro_scales_len =3D INV_ICM45600_GYRO_FS_MAX, }; EXPORT_SYMBOL_NS_GPL(inv_icm45608_chip_info, "IIO_ICM45600"); =20 @@ -176,6 +182,8 @@ const struct inv_icm45600_chip_info inv_icm45634_chip_i= nfo =3D { .whoami =3D INV_ICM45600_WHOAMI_ICM45634, .name =3D "icm45634", .conf =3D &inv_icm45600_default_conf, + .gyro_scales =3D (const int *)inv_icm45600_gyro_scale, + .gyro_scales_len =3D INV_ICM45600_GYRO_FS_MAX, }; EXPORT_SYMBOL_NS_GPL(inv_icm45634_chip_info, "IIO_ICM45600"); =20 @@ -183,6 +191,8 @@ const struct inv_icm45600_chip_info inv_icm45686_chip_i= nfo =3D { .whoami =3D INV_ICM45600_WHOAMI_ICM45686, .name =3D "icm45686", .conf =3D &inv_icm45686_default_conf, + .gyro_scales =3D (const int *)inv_icm45686_gyro_scale, + .gyro_scales_len =3D INV_ICM45686_GYRO_FS_MAX, }; EXPORT_SYMBOL_NS_GPL(inv_icm45686_chip_info, "IIO_ICM45600"); =20 @@ -190,6 +200,8 @@ const struct inv_icm45600_chip_info inv_icm45687_chip_i= nfo =3D { .whoami =3D INV_ICM45600_WHOAMI_ICM45687, .name =3D "icm45687", .conf =3D &inv_icm45686_default_conf, + .gyro_scales =3D (const int *)inv_icm45686_gyro_scale, + .gyro_scales_len =3D INV_ICM45686_GYRO_FS_MAX, }; EXPORT_SYMBOL_NS_GPL(inv_icm45687_chip_info, "IIO_ICM45600"); =20 @@ -197,6 +209,8 @@ const struct inv_icm45600_chip_info inv_icm45688p_chip_= info =3D { .whoami =3D INV_ICM45600_WHOAMI_ICM45688P, .name =3D "icm45688p", .conf =3D &inv_icm45686_default_conf, + .gyro_scales =3D (const int *)inv_icm45686_gyro_scale, + .gyro_scales_len =3D INV_ICM45686_GYRO_FS_MAX, }; EXPORT_SYMBOL_NS_GPL(inv_icm45688p_chip_info, "IIO_ICM45600"); =20 @@ -204,6 +218,8 @@ const struct inv_icm45600_chip_info inv_icm45689_chip_i= nfo =3D { .whoami =3D INV_ICM45600_WHOAMI_ICM45689, .name =3D "icm45689", .conf =3D &inv_icm45686_default_conf, + .gyro_scales =3D (const int *)inv_icm45686_gyro_scale, + .gyro_scales_len =3D INV_ICM45686_GYRO_FS_MAX, }; EXPORT_SYMBOL_NS_GPL(inv_icm45689_chip_info, "IIO_ICM45600"); =20 @@ -539,11 +555,14 @@ static irqreturn_t inv_icm45600_irq_handler(int irq, = void *_data) /* Read the FIFO data. */ mask =3D INV_ICM45600_INT_STATUS_FIFO_THS | INV_ICM45600_INT_STATUS_FIFO_= FULL; if (status & mask) { - ret =3D inv_icm45600_buffer_fifo_read(st); + ret =3D inv_icm45600_buffer_fifo_read(st, 0); if (ret) { dev_err(dev, "FIFO read error %d\n", ret); return IRQ_HANDLED; } + ret =3D inv_icm45600_buffer_fifo_parse(st); + if (ret) + dev_err(dev, "FIFO parsing error %d\n", ret); } =20 /* FIFO full warning. */ @@ -710,6 +729,10 @@ int inv_icm45600_core_probe(struct regmap *regmap, con= st struct inv_icm45600_chi if (ret) return ret; =20 + st->indio_gyro =3D inv_icm45600_gyro_init(st); + if (IS_ERR(st->indio_gyro)) + return PTR_ERR(st->indio_gyro); + ret =3D inv_icm45600_irq_init(st, irq, irq_type, open_drain); if (ret) return ret; @@ -835,6 +858,85 @@ static int inv_icm45600_runtime_resume(struct device *= dev) return inv_icm45600_enable_regulator_vddio(st); } =20 +static int _inv_icm45600_temp_read(struct inv_icm45600_state *st, s16 *tem= p) +{ + struct inv_icm45600_sensor_conf conf =3D INV_ICM45600_SENSOR_CONF_KEEP_VA= LUES; + int ret; + + /* Make sure a sensor is on. */ + if (st->conf.gyro.mode =3D=3D INV_ICM45600_SENSOR_MODE_OFF && + st->conf.accel.mode =3D=3D INV_ICM45600_SENSOR_MODE_OFF) { + conf.mode =3D INV_ICM45600_SENSOR_MODE_LOW_POWER; + ret =3D inv_icm45600_set_accel_conf(st, &conf, NULL); + if (ret) + return ret; + } + + ret =3D regmap_bulk_read(st->map, INV_ICM45600_REG_TEMP_DATA, + &st->buffer.u16, sizeof(st->buffer.u16)); + if (ret) + return ret; + + *temp =3D (s16)le16_to_cpup(&st->buffer.u16); + if (*temp =3D=3D INV_ICM45600_DATA_INVALID) + return -EINVAL; + + return 0; +} + +static int inv_icm45600_temp_read(struct inv_icm45600_state *st, s16 *temp) +{ + struct device *dev =3D regmap_get_device(st->map); + int ret; + + ret =3D pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + scoped_guard(mutex, &st->lock) + ret =3D _inv_icm45600_temp_read(st, temp); + + pm_runtime_put_autosuspend(dev); + + return ret; +} + +int inv_icm45600_temp_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + s16 temp; + int ret; + + if (chan->type !=3D IIO_TEMP) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret =3D inv_icm45600_temp_read(st, &temp); + if (ret) + return ret; + *val =3D temp; + return IIO_VAL_INT; + /* + * T=C2=B0C =3D (temp / 128) + 25 + * Tm=C2=B0C =3D 1000 * ((temp * 100 / 12800) + 25) + * scale: 100000 / 13248 =3D 7.8125 + * offset: 25000 + */ + case IIO_CHAN_INFO_SCALE: + *val =3D 7; + *val2 =3D 812500; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + *val =3D 25000; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + EXPORT_NS_GPL_DEV_PM_OPS(inv_icm45600_pm_ops, IIO_ICM45600) =3D { SYSTEM_SLEEP_PM_OPS(inv_icm45600_suspend, inv_icm45600_resume) RUNTIME_PM_OPS(inv_icm45600_runtime_suspend, diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c b/drivers/iio= /imu/inv_icm45600/inv_icm45600_gyro.c new file mode 100644 index 0000000000000000000000000000000000000000..e4300b28436130a529914333578= cca9896ac62a3 --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c @@ -0,0 +1,792 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 Invensense, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inv_icm45600_buffer.h" +#include "inv_icm45600.h" + +enum inv_icm45600_gyro_scan { + INV_ICM45600_GYRO_SCAN_X, + INV_ICM45600_GYRO_SCAN_Y, + INV_ICM45600_GYRO_SCAN_Z, + INV_ICM45600_GYRO_SCAN_TEMP, + INV_ICM45600_GYRO_SCAN_TIMESTAMP, +}; + +static const struct iio_chan_spec_ext_info inv_icm45600_gyro_ext_infos[] = =3D { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm45600_get_mount_matrix), + { } +}; + +#define INV_ICM45600_GYRO_CHAN(_modifier, _index, _ext_info) \ + { \ + .type =3D IIO_ANGL_VEL, \ + .modified =3D 1, \ + .channel2 =3D _modifier, \ + .info_mask_separate =3D \ + BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .info_mask_shared_by_type =3D \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available =3D \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .info_mask_shared_by_all =3D \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available =3D \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index =3D _index, \ + .scan_type =3D { \ + .sign =3D 's', \ + .realbits =3D 16, \ + .storagebits =3D 16, \ + .endianness =3D IIO_LE, \ + }, \ + .ext_info =3D _ext_info, \ + } + +static const struct iio_chan_spec inv_icm45600_gyro_channels[] =3D { + INV_ICM45600_GYRO_CHAN(IIO_MOD_X, INV_ICM45600_GYRO_SCAN_X, + inv_icm45600_gyro_ext_infos), + INV_ICM45600_GYRO_CHAN(IIO_MOD_Y, INV_ICM45600_GYRO_SCAN_Y, + inv_icm45600_gyro_ext_infos), + INV_ICM45600_GYRO_CHAN(IIO_MOD_Z, INV_ICM45600_GYRO_SCAN_Z, + inv_icm45600_gyro_ext_infos), + INV_ICM45600_TEMP_CHAN(INV_ICM45600_GYRO_SCAN_TEMP), + IIO_CHAN_SOFT_TIMESTAMP(INV_ICM45600_GYRO_SCAN_TIMESTAMP), +}; + +/* + * IIO buffer data: size must be a power of 2 and timestamp aligned + * 16 bytes: 6 bytes angular velocity, 2 bytes temperature, 8 bytes timest= amp + */ +struct inv_icm45600_gyro_buffer { + struct inv_icm45600_fifo_sensor_data gyro; + s16 temp; + aligned_s64 timestamp; +}; + +static const unsigned long inv_icm45600_gyro_scan_masks[] =3D { + /* 3-axis gyro + temperature */ + BIT(INV_ICM45600_GYRO_SCAN_X) | + BIT(INV_ICM45600_GYRO_SCAN_Y) | + BIT(INV_ICM45600_GYRO_SCAN_Z) | + BIT(INV_ICM45600_GYRO_SCAN_TEMP), + 0 +}; + +/* enable gyroscope sensor and FIFO write */ +static int inv_icm45600_gyro_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + struct inv_icm45600_sensor_state *gyro_st =3D iio_priv(indio_dev); + struct inv_icm45600_sensor_conf conf =3D INV_ICM45600_SENSOR_CONF_KEEP_VA= LUES; + unsigned int fifo_en =3D 0; + unsigned int sleep =3D 0; + int ret; + + scoped_guard(mutex, &st->lock) { + if (*scan_mask & BIT(INV_ICM45600_GYRO_SCAN_TEMP)) + fifo_en |=3D INV_ICM45600_SENSOR_TEMP; + + if (*scan_mask & (BIT(INV_ICM45600_GYRO_SCAN_X) | + BIT(INV_ICM45600_GYRO_SCAN_Y) | + BIT(INV_ICM45600_GYRO_SCAN_Z))) { + /* enable gyro sensor */ + conf.mode =3D gyro_st->power_mode; + ret =3D inv_icm45600_set_gyro_conf(st, &conf, &sleep); + if (ret) + return ret; + fifo_en |=3D INV_ICM45600_SENSOR_GYRO; + } + /* update data FIFO write */ + ret =3D inv_icm45600_buffer_set_fifo_en(st, fifo_en | st->fifo.en); + } + /* sleep required time */ + if (sleep) + msleep(sleep); + + return ret; +} + +static int _inv_icm45600_gyro_read_sensor(struct inv_icm45600_state *st, + struct inv_icm45600_sensor_state *gyro_st, + unsigned int reg, int *val) +{ + struct inv_icm45600_sensor_conf conf =3D INV_ICM45600_SENSOR_CONF_KEEP_VA= LUES; + int ret; + + /* enable gyro sensor */ + conf.mode =3D gyro_st->power_mode; + ret =3D inv_icm45600_set_gyro_conf(st, &conf, NULL); + if (ret) + return ret; + + /* read gyro register data */ + ret =3D regmap_bulk_read(st->map, reg, &st->buffer.u16, sizeof(st->buffer= .u16)); + if (ret) + return ret; + + *val =3D sign_extend32(le16_to_cpup(&st->buffer.u16), 15); + if (*val =3D=3D INV_ICM45600_DATA_INVALID) + return -ENODATA; + + return 0; +} + +static int inv_icm45600_gyro_read_sensor(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + struct inv_icm45600_sensor_state *gyro_st =3D iio_priv(indio_dev); + struct device *dev =3D regmap_get_device(st->map); + unsigned int reg; + int ret; + + if (chan->type !=3D IIO_ANGL_VEL) + return -EINVAL; + + switch (chan->channel2) { + case IIO_MOD_X: + reg =3D INV_ICM45600_REG_GYRO_DATA_X; + break; + case IIO_MOD_Y: + reg =3D INV_ICM45600_REG_GYRO_DATA_Y; + break; + case IIO_MOD_Z: + reg =3D INV_ICM45600_REG_GYRO_DATA_Z; + break; + default: + return -EINVAL; + } + + ret =3D pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + scoped_guard(mutex, &st->lock) + ret =3D _inv_icm45600_gyro_read_sensor(st, gyro_st, reg, val); + + pm_runtime_put_autosuspend(dev); + + return ret; +} + +/* IIO format int + nano */ +const int inv_icm45600_gyro_scale[][2] =3D { + /* +/- 2000dps =3D> 0.001065264 rad/s */ + [INV_ICM45600_GYRO_FS_2000DPS] =3D { 0, 1065264 }, + /* +/- 1000dps =3D> 0.000532632 rad/s */ + [INV_ICM45600_GYRO_FS_1000DPS] =3D { 0, 532632 }, + /* +/- 500dps =3D> 0.000266316 rad/s */ + [INV_ICM45600_GYRO_FS_500DPS] =3D { 0, 266316 }, + /* +/- 250dps =3D> 0.000133158 rad/s */ + [INV_ICM45600_GYRO_FS_250DPS] =3D { 0, 133158 }, + /* +/- 125dps =3D> 0.000066579 rad/s */ + [INV_ICM45600_GYRO_FS_125DPS] =3D { 0, 66579 }, + /* +/- 62.5dps =3D> 0.000033290 rad/s */ + [INV_ICM45600_GYRO_FS_62_5DPS] =3D { 0, 33290 }, + /* +/- 31.25dps =3D> 0.000016645 rad/s */ + [INV_ICM45600_GYRO_FS_31_25DPS] =3D { 0, 16645 }, + /* +/- 15.625dps =3D> 0.000008322 rad/s */ + [INV_ICM45600_GYRO_FS_15_625DPS] =3D { 0, 8322 }, +}; + +/* IIO format int + nano */ +const int inv_icm45686_gyro_scale[][2] =3D { + /* +/- 4000dps =3D> 0.002130529 rad/s */ + [INV_ICM45686_GYRO_FS_4000DPS] =3D { 0, 2130529 }, + /* +/- 2000dps =3D> 0.001065264 rad/s */ + [INV_ICM45686_GYRO_FS_2000DPS] =3D { 0, 1065264 }, + /* +/- 1000dps =3D> 0.000532632 rad/s */ + [INV_ICM45686_GYRO_FS_1000DPS] =3D { 0, 532632 }, + /* +/- 500dps =3D> 0.000266316 rad/s */ + [INV_ICM45686_GYRO_FS_500DPS] =3D { 0, 266316 }, + /* +/- 250dps =3D> 0.000133158 rad/s */ + [INV_ICM45686_GYRO_FS_250DPS] =3D { 0, 133158 }, + /* +/- 125dps =3D> 0.000066579 rad/s */ + [INV_ICM45686_GYRO_FS_125DPS] =3D { 0, 66579 }, + /* +/- 62.5dps =3D> 0.000033290 rad/s */ + [INV_ICM45686_GYRO_FS_62_5DPS] =3D { 0, 33290 }, + /* +/- 31.25dps =3D> 0.000016645 rad/s */ + [INV_ICM45686_GYRO_FS_31_25DPS] =3D { 0, 16645 }, + /* +/- 15.625dps =3D> 0.000008322 rad/s */ + [INV_ICM45686_GYRO_FS_15_625DPS] =3D { 0, 8322 }, +}; + +static int inv_icm45600_gyro_read_scale(struct iio_dev *indio_dev, + int *val, int *val2) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + struct inv_icm45600_sensor_state *gyro_st =3D iio_priv(indio_dev); + unsigned int idx; + + idx =3D st->conf.gyro.fs; + + /* Full scale register starts at 1 for not High FSR parts */ + if (gyro_st->scales =3D=3D (const int *)&inv_icm45600_gyro_scale) + idx--; + + *val =3D gyro_st->scales[2 * idx]; + *val2 =3D gyro_st->scales[2 * idx + 1]; + return IIO_VAL_INT_PLUS_NANO; +} + +static int inv_icm45600_gyro_write_scale(struct iio_dev *indio_dev, + int val, int val2) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + struct inv_icm45600_sensor_state *gyro_st =3D iio_priv(indio_dev); + struct device *dev =3D regmap_get_device(st->map); + unsigned int idx; + struct inv_icm45600_sensor_conf conf =3D INV_ICM45600_SENSOR_CONF_KEEP_VA= LUES; + int ret; + + for (idx =3D 0; idx < gyro_st->scales_len; idx +=3D 2) { + if (val =3D=3D gyro_st->scales[idx] && + val2 =3D=3D gyro_st->scales[idx + 1]) + break; + } + if (idx =3D=3D gyro_st->scales_len) + return -EINVAL; + + conf.fs =3D idx / 2; + + /* Full scale register starts at 1 for not High FSR parts */ + if (gyro_st->scales =3D=3D (const int *)&inv_icm45600_gyro_scale) + conf.fs++; + + ret =3D pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + scoped_guard(mutex, &st->lock) + ret =3D inv_icm45600_set_gyro_conf(st, &conf, NULL); + + pm_runtime_put_autosuspend(dev); + + return ret; +} + +/* IIO format int + micro */ +static const int inv_icm45600_gyro_odr[] =3D { + 1, 562500, /* 1.5625Hz */ + 3, 125000, /* 3.125Hz */ + 6, 250000, /* 6.25Hz */ + 12, 500000, /* 12.5Hz */ + 25, 0, /* 25Hz */ + 50, 0, /* 50Hz */ + 100, 0, /* 100Hz */ + 200, 0, /* 200Hz */ + 400, 0, /* 400Hz */ + 800, 0, /* 800Hz */ + 1600, 0, /* 1.6kHz */ + 3200, 0, /* 3.2kHz */ + 6400, 0, /* 6.4kHz */ +}; + +static const int inv_icm45600_gyro_odr_conv[] =3D { + INV_ICM45600_ODR_1_5625HZ_LP, + INV_ICM45600_ODR_3_125HZ_LP, + INV_ICM45600_ODR_6_25HZ_LP, + INV_ICM45600_ODR_12_5HZ, + INV_ICM45600_ODR_25HZ, + INV_ICM45600_ODR_50HZ, + INV_ICM45600_ODR_100HZ, + INV_ICM45600_ODR_200HZ, + INV_ICM45600_ODR_400HZ, + INV_ICM45600_ODR_800HZ_LN, + INV_ICM45600_ODR_1600HZ_LN, + INV_ICM45600_ODR_3200HZ_LN, + INV_ICM45600_ODR_6400HZ_LN, +}; + +static int inv_icm45600_gyro_read_odr(struct inv_icm45600_state *st, + int *val, int *val2) +{ + unsigned int odr; + unsigned int i; + + odr =3D st->conf.gyro.odr; + + for (i =3D 0; i < ARRAY_SIZE(inv_icm45600_gyro_odr_conv); ++i) { + if (inv_icm45600_gyro_odr_conv[i] =3D=3D odr) + break; + } + if (i >=3D ARRAY_SIZE(inv_icm45600_gyro_odr_conv)) + return -EINVAL; + + *val =3D inv_icm45600_gyro_odr[2 * i]; + *val2 =3D inv_icm45600_gyro_odr[2 * i + 1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int _inv_icm45600_gyro_write_odr(struct iio_dev *indio_dev, int odr) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + struct inv_icm45600_sensor_state *gyro_st =3D iio_priv(indio_dev); + struct inv_sensors_timestamp *ts =3D &gyro_st->ts; + struct inv_icm45600_sensor_conf conf =3D INV_ICM45600_SENSOR_CONF_KEEP_VA= LUES; + int ret; + + conf.odr =3D odr; + ret =3D inv_sensors_timestamp_update_odr(ts, inv_icm45600_odr_to_period(c= onf.odr), + iio_buffer_enabled(indio_dev)); + if (ret) + return ret; + + if (st->conf.gyro.mode !=3D INV_ICM45600_SENSOR_MODE_OFF) + conf.mode =3D gyro_st->power_mode; + + ret =3D inv_icm45600_set_gyro_conf(st, &conf, NULL); + if (ret) + return ret; + + inv_icm45600_buffer_update_fifo_period(st); + inv_icm45600_buffer_update_watermark(st); + + return 0; +} + +static int inv_icm45600_gyro_write_odr(struct iio_dev *indio_dev, + int val, int val2) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + struct device *dev =3D regmap_get_device(st->map); + unsigned int idx; + int odr; + int ret; + + for (idx =3D 0; idx < ARRAY_SIZE(inv_icm45600_gyro_odr); idx +=3D 2) { + if (val =3D=3D inv_icm45600_gyro_odr[idx] && + val2 =3D=3D inv_icm45600_gyro_odr[idx + 1]) + break; + } + if (idx >=3D ARRAY_SIZE(inv_icm45600_gyro_odr)) + return -EINVAL; + + odr =3D inv_icm45600_gyro_odr_conv[idx / 2]; + + ret =3D pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + scoped_guard(mutex, &st->lock) + ret =3D _inv_icm45600_gyro_write_odr(indio_dev, odr); + + pm_runtime_put_autosuspend(dev); + + return ret; +} + +/* + * Calibration bias values, IIO range format int + nano. + * Value is limited to +/-62.5dps coded on 14 bits signed. Step is 7.5mdps. + */ +static int inv_icm45600_gyro_calibbias[] =3D { + -1, 90830336, /* min: -1.090830336 rad/s */ + 0, 133158, /* step: 0.000133158 rad/s */ + 1, 90697178, /* max: 1.090697178 rad/s */ +}; + +static int inv_icm45600_gyro_read_offset(struct inv_icm45600_state *st, + struct iio_chan_spec const *chan, + int *val, int *val2) +{ + struct device *dev =3D regmap_get_device(st->map); + s64 val64; + s32 bias; + unsigned int reg; + s16 offset; + int ret; + + if (chan->type !=3D IIO_ANGL_VEL) + return -EINVAL; + + switch (chan->channel2) { + case IIO_MOD_X: + reg =3D INV_ICM45600_IPREG_SYS1_REG_42; + break; + case IIO_MOD_Y: + reg =3D INV_ICM45600_IPREG_SYS1_REG_56; + break; + case IIO_MOD_Z: + reg =3D INV_ICM45600_IPREG_SYS1_REG_70; + break; + default: + return -EINVAL; + } + + ret =3D pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + scoped_guard(mutex, &st->lock) + ret =3D regmap_bulk_read(st->map, reg, &st->buffer.u16, sizeof(st->buffe= r.u16)); + + pm_runtime_put_autosuspend(dev); + if (ret) + return ret; + + offset =3D le16_to_cpup(&st->buffer.u16) & INV_ICM45600_GYRO_OFFUSER_MASK; + /* 14 bits signed value */ + offset =3D sign_extend32(offset, 13); + + /* + * convert raw offset to dps then to rad/s + * 14 bits signed raw max 62.5 to dps: 625 / 81920 + * dps to rad: Pi / 180 + * result in nano (1000000000) + * (offset * 625 * Pi * 1000000000) / (81920 * 180) + */ + val64 =3D (s64)offset * 625LL * 3141592653LL; + /* for rounding, add + or - divisor (81920 * 180) divided by 2 */ + if (val64 >=3D 0) + val64 +=3D 81920 * 180 / 2; + else + val64 -=3D 81920 * 180 / 2; + bias =3D div_s64(val64, 81920 * 180); + *val =3D bias / 1000000000L; + *val2 =3D bias % 1000000000L; + + return IIO_VAL_INT_PLUS_NANO; +} + +static int inv_icm45600_gyro_write_offset(struct inv_icm45600_state *st, + struct iio_chan_spec const *chan, + int val, int val2) +{ + struct device *dev =3D regmap_get_device(st->map); + s64 val64, min, max; + unsigned int reg; + s16 offset; + int ret; + + if (chan->type !=3D IIO_ANGL_VEL) + return -EINVAL; + + switch (chan->channel2) { + case IIO_MOD_X: + reg =3D INV_ICM45600_IPREG_SYS1_REG_42; + break; + case IIO_MOD_Y: + reg =3D INV_ICM45600_IPREG_SYS1_REG_56; + break; + case IIO_MOD_Z: + reg =3D INV_ICM45600_IPREG_SYS1_REG_70; + break; + default: + return -EINVAL; + } + + /* inv_icm45600_gyro_calibbias: min - step - max in nano */ + min =3D (s64)inv_icm45600_gyro_calibbias[0] * 1000000000LL - + (s64)inv_icm45600_gyro_calibbias[1]; + max =3D (s64)inv_icm45600_gyro_calibbias[4] * 1000000000LL + + (s64)inv_icm45600_gyro_calibbias[5]; + val64 =3D (s64)val * 1000000000LL; + if (val >=3D 0) + val64 +=3D (s64)val2; + else + val64 -=3D (s64)val2; + if (val64 < min || val64 > max) + return -EINVAL; + + /* + * convert rad/s to dps then to raw value + * rad to dps: 180 / Pi + * dps to raw 14 bits signed, max 62.5: 8192 / 62.5 + * val in nano (1000000000) + * val * 180 * 8192 / (Pi * 1000000000 * 62.5) + */ + val64 =3D val64 * 180LL * 8192; + /* for rounding, add + or - divisor (314159265 * 625) divided by 2 */ + if (val64 >=3D 0) + val64 +=3D 314159265LL * 625LL / 2LL; + else + val64 -=3D 314159265LL * 625LL / 2LL; + offset =3D div64_s64(val64, 314159265LL * 625LL); + + /* clamp value limited to 14 bits signed */ + offset =3D clamp(offset, -8192, 8191); + + st->buffer.u16 =3D cpu_to_le16(offset & INV_ICM45600_GYRO_OFFUSER_MASK); + + ret =3D pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + scoped_guard(mutex, &st->lock) + ret =3D regmap_bulk_write(st->map, reg, &st->buffer.u16, sizeof(st->buff= er.u16)); + + pm_runtime_put_autosuspend(dev); + return ret; +} + +static int inv_icm45600_gyro_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + int ret; + + switch (chan->type) { + case IIO_ANGL_VEL: + break; + case IIO_TEMP: + return inv_icm45600_temp_read_raw(indio_dev, chan, val, val2, mask); + default: + return -EINVAL; + } + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret =3D inv_icm45600_gyro_read_sensor(indio_dev, chan, val); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return inv_icm45600_gyro_read_scale(indio_dev, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm45600_gyro_read_odr(st, val, val2); + case IIO_CHAN_INFO_CALIBBIAS: + return inv_icm45600_gyro_read_offset(st, chan, val, val2); + default: + return -EINVAL; + } +} + +static int inv_icm45600_gyro_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, + int *type, int *length, long mask) +{ + struct inv_icm45600_sensor_state *gyro_st =3D iio_priv(indio_dev); + + if (chan->type !=3D IIO_ANGL_VEL) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *vals =3D gyro_st->scales; + *type =3D IIO_VAL_INT_PLUS_NANO; + *length =3D gyro_st->scales_len; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals =3D inv_icm45600_gyro_odr; + *type =3D IIO_VAL_INT_PLUS_MICRO; + *length =3D ARRAY_SIZE(inv_icm45600_gyro_odr); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_CALIBBIAS: + *vals =3D inv_icm45600_gyro_calibbias; + *type =3D IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +static int inv_icm45600_gyro_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + int ret; + + if (chan->type !=3D IIO_ANGL_VEL) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret =3D inv_icm45600_gyro_write_scale(indio_dev, val, val2); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm45600_gyro_write_odr(indio_dev, val, val2); + case IIO_CHAN_INFO_CALIBBIAS: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret =3D inv_icm45600_gyro_write_offset(st, chan, val, val2); + iio_device_release_direct(indio_dev); + return ret; + default: + return -EINVAL; + } +} + +static int inv_icm45600_gyro_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + if (chan->type !=3D IIO_ANGL_VEL) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static int inv_icm45600_gyro_hwfifo_set_watermark(struct iio_dev *indio_de= v, + unsigned int val) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + + guard(mutex)(&st->lock); + + st->fifo.watermark.gyro =3D val; + return inv_icm45600_buffer_update_watermark(st); +} + +static int inv_icm45600_gyro_hwfifo_flush(struct iio_dev *indio_dev, + unsigned int count) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + int ret; + + if (count =3D=3D 0) + return 0; + + guard(mutex)(&st->lock); + + ret =3D inv_icm45600_buffer_hwfifo_flush(st, count); + if (ret) + return ret; + + return st->fifo.nb.gyro; +} + +static const struct iio_info inv_icm45600_gyro_info =3D { + .read_raw =3D inv_icm45600_gyro_read_raw, + .read_avail =3D inv_icm45600_gyro_read_avail, + .write_raw =3D inv_icm45600_gyro_write_raw, + .write_raw_get_fmt =3D inv_icm45600_gyro_write_raw_get_fmt, + .debugfs_reg_access =3D inv_icm45600_debugfs_reg, + .update_scan_mode =3D inv_icm45600_gyro_update_scan_mode, + .hwfifo_set_watermark =3D inv_icm45600_gyro_hwfifo_set_watermark, + .hwfifo_flush_to_buffer =3D inv_icm45600_gyro_hwfifo_flush, +}; + +struct iio_dev *inv_icm45600_gyro_init(struct inv_icm45600_state *st) +{ + struct device *dev =3D regmap_get_device(st->map); + struct inv_icm45600_sensor_state *gyro_st; + struct inv_sensors_timestamp_chip ts_chip; + struct iio_dev *indio_dev; + const char *name; + int ret; + + name =3D devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->chip_info->name); + if (!name) + return ERR_PTR(-ENOMEM); + + indio_dev =3D devm_iio_device_alloc(dev, sizeof(*gyro_st)); + if (!indio_dev) + return ERR_PTR(-ENOMEM); + gyro_st =3D iio_priv(indio_dev); + + gyro_st->scales =3D st->chip_info->gyro_scales; + gyro_st->scales_len =3D st->chip_info->gyro_scales_len * 2; + + /* low-noise by default at init */ + gyro_st->power_mode =3D INV_ICM45600_SENSOR_MODE_LOW_NOISE; + + /* + * clock period is 32kHz (31250ns) + * jitter is +/- 2% (20 per mille) + */ + ts_chip.clock_period =3D 31250; + ts_chip.jitter =3D 20; + ts_chip.init_period =3D inv_icm45600_odr_to_period(st->conf.gyro.odr); + inv_sensors_timestamp_init(&gyro_st->ts, &ts_chip); + + iio_device_set_drvdata(indio_dev, st); + indio_dev->name =3D name; + indio_dev->info =3D &inv_icm45600_gyro_info; + indio_dev->modes =3D INDIO_DIRECT_MODE; + indio_dev->channels =3D inv_icm45600_gyro_channels; + indio_dev->num_channels =3D ARRAY_SIZE(inv_icm45600_gyro_channels); + indio_dev->available_scan_masks =3D inv_icm45600_gyro_scan_masks; + indio_dev->setup_ops =3D &inv_icm45600_buffer_ops; + + ret =3D devm_iio_kfifo_buffer_setup(dev, indio_dev, + &inv_icm45600_buffer_ops); + if (ret) + return ERR_PTR(ret); + + ret =3D devm_iio_device_register(dev, indio_dev); + if (ret) + return ERR_PTR(ret); + + return indio_dev; +} + +int inv_icm45600_gyro_parse_fifo(struct iio_dev *indio_dev) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + struct inv_icm45600_sensor_state *gyro_st =3D iio_priv(indio_dev); + struct inv_sensors_timestamp *ts =3D &gyro_st->ts; + ssize_t i, size; + unsigned int no; + + /* parse all fifo packets */ + for (i =3D 0, no =3D 0; i < st->fifo.count; i +=3D size, ++no) { + struct inv_icm45600_gyro_buffer buffer =3D { }; + const struct inv_icm45600_fifo_sensor_data *accel, *gyro; + const __le16 *timestamp; + const s8 *temp; + unsigned int odr; + s64 ts_val; + + size =3D inv_icm45600_fifo_decode_packet(&st->fifo.data[i], + &accel, &gyro, &temp, ×tamp, &odr); + /* quit if error or FIFO is empty */ + if (size <=3D 0) + return size; + + /* skip packet if no gyro data or data is invalid */ + if (gyro =3D=3D NULL || !inv_icm45600_fifo_is_data_valid(gyro)) + continue; + + /* update odr */ + if (odr & INV_ICM45600_SENSOR_GYRO) + inv_sensors_timestamp_apply_odr(ts, st->fifo.period, + st->fifo.nb.total, no); + + memcpy(&buffer.gyro, gyro, sizeof(buffer.gyro)); + /* convert 8 bits FIFO temperature in high resolution format */ + buffer.temp =3D temp ? (*temp * 64) : 0; + ts_val =3D inv_sensors_timestamp_pop(ts); + iio_push_to_buffers_with_ts(indio_dev, &buffer, sizeof(buffer), ts_val); + } + + return 0; +} --=20 2.34.1 From nobody Sat Sep 13 22:31:04 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D6658275AE6; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755699885; cv=none; b=H8mYD5ED78PKfzaKQN5rEn807xwFeILT7wvlIIHw8Osnx5TUkJUZ4RdFwMl/gCLGY8Q5O7I+iA2zRZpmR96lCxAXfKfnr91efchlDPZrViOvrWFY+962uPO0bzBCmQr+XSoWJgh3cEQWu2S5Kx8hMbxDtJ6Tofi9xBTg3OldBiM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755699885; c=relaxed/simple; bh=qGQNmR6Ef9Fvdsx1u2ipFQDpVaaSEL1igCMxn57ax3o=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=TFr6cmVdJTqPgAhr8VL//xT6YWWHwuukeIQLhwj0ewRNOyMeHXqM8rj8ZvqywSjyfCOxlUyTlHBdbio1pl+tbrIZ1BClhNcPEs8bDCGDNQS/abBKKjQqUuhpifcoJJqVQzihGBzbUgR3XISwBO5DOL03hFg8cWeryYmJOwMYkLw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=DpbbzWP2; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="DpbbzWP2" Received: by smtp.kernel.org (Postfix) with ESMTPS id A18FEC19423; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1755699885; bh=qGQNmR6Ef9Fvdsx1u2ipFQDpVaaSEL1igCMxn57ax3o=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=DpbbzWP2+Lezl8UMUTDSZOtspaIsoDHfpZxeHdSS7MfTHfeFp/NsiIyKZ7kXmlt6W 3H9RVCmaUXTR9wIrM40rycq+JRLi93OhRchILxLIDQa5gCMUSFutJvOqjDnvRa+DC+ Q1u4wQiA9GnKlSeRuPYhIfkKdgCquRsWaxPp9PFBrsZVtuldacYxQb3o+zVraRvBML KdTGSyTFPLU5Ug0tcuwaRkYAXXccRVGgbJk+NPv6o/9ToUJKFtPaxwL7ar1Q/3V3Ve veG5EEFtgQUtxDltc1e6MfuW1U480IUT2pybASCY8yIi2LaDQkMKA2Bb72Y0/XYwa4 kerFlMnaNWbXA== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 97C3CCA0EDC; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Wed, 20 Aug 2025 14:24:23 +0000 Subject: [PATCH v5 5/9] iio: imu: inv_icm45600: add IMU IIO accelerometer device Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250820-add_newport_driver-v5-5-2fc9f13dddee@tdk.com> References: <20250820-add_newport_driver-v5-0-2fc9f13dddee@tdk.com> In-Reply-To: <20250820-add_newport_driver-v5-0-2fc9f13dddee@tdk.com> To: Jonathan Cameron , David Lechner , =?utf-8?q?Nuno_S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, Remi Buisson X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1755699883; l=29191; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=zaRW4xSw0Lcfr7Ls5a5a/FyE790qk6UB/wfkVhe484I=; b=bGVMfU+matpMvOBucqCZ6jTbQNLfkCveZBqZK7nqt3gVPxGfhSnM1GYSmtbulHSLHDkGM4JA4 fCCQE1TAPz9ARvAfZ5aMPQ30os9N/4xz1hZ+GjOEwZGYfXcR1Lpb1al X-Developer-Key: i=remi.buisson@tdk.com; a=ed25519; pk=yDVMi4C7RpXN4dififo42A7fDDt3THYzoZoNq9lUZuo= X-Endpoint-Received: by B4 Relay for remi.buisson@tdk.com/20250411 with auth_id=372 X-Original-From: Remi Buisson Reply-To: remi.buisson@tdk.com From: Remi Buisson Add IIO device for accelerometer sensor with data polling interface and FIFO parsing. Attributes: raw, scale, sampling_frequency, calibbias. Temperature is available as a processed channel. Signed-off-by: Remi Buisson --- drivers/iio/imu/inv_icm45600/Makefile | 1 + drivers/iio/imu/inv_icm45600/inv_icm45600.h | 8 + drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c | 781 ++++++++++++++++++= ++++ drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 20 + 4 files changed, 810 insertions(+) diff --git a/drivers/iio/imu/inv_icm45600/Makefile b/drivers/iio/imu/inv_ic= m45600/Makefile index b5954b4053c25259957faf4fc9d1979c9ff74608..e34553d2b74dc46bb0f533d2bd0= 875655f91c781 100644 --- a/drivers/iio/imu/inv_icm45600/Makefile +++ b/drivers/iio/imu/inv_icm45600/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_INV_ICM45600) +=3D inv-icm45600.o inv-icm45600-y +=3D inv_icm45600_core.o inv-icm45600-y +=3D inv_icm45600_buffer.o inv-icm45600-y +=3D inv_icm45600_gyro.o +inv-icm45600-y +=3D inv_icm45600_accel.o diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600.h b/drivers/iio/imu/= inv_icm45600/inv_icm45600.h index a243cf5ad7e05039a8b1d4bed07e30e89e8f6ee4..2e40edebcc241ae47793e104103= 7fd6e86a1317a 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600.h +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h @@ -109,6 +109,8 @@ struct inv_icm45600_chip_info { u8 whoami; const char *name; const struct inv_icm45600_conf *conf; + const int *accel_scales; + const int accel_scales_len; const int *gyro_scales; const int gyro_scales_len; }; @@ -122,6 +124,8 @@ extern const struct inv_icm45600_chip_info inv_icm45687= _chip_info; extern const struct inv_icm45600_chip_info inv_icm45688p_chip_info; extern const struct inv_icm45600_chip_info inv_icm45689_chip_info; =20 +extern const int inv_icm45600_accel_scale[][2]; +extern const int inv_icm45686_accel_scale[][2]; extern const int inv_icm45600_gyro_scale[][2]; extern const int inv_icm45686_gyro_scale[][2]; =20 @@ -369,4 +373,8 @@ struct iio_dev *inv_icm45600_gyro_init(struct inv_icm45= 600_state *st); =20 int inv_icm45600_gyro_parse_fifo(struct iio_dev *indio_dev); =20 +struct iio_dev *inv_icm45600_accel_init(struct inv_icm45600_state *st); + +int inv_icm45600_accel_parse_fifo(struct iio_dev *indio_dev); + #endif diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c b/drivers/ii= o/imu/inv_icm45600/inv_icm45600_accel.c new file mode 100644 index 0000000000000000000000000000000000000000..0ed535c3d460178930445165b37= 713ecd40d5c7c --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c @@ -0,0 +1,781 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 Invensense, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inv_icm45600_buffer.h" +#include "inv_icm45600.h" + +enum inv_icm45600_accel_scan { + INV_ICM45600_ACCEL_SCAN_X, + INV_ICM45600_ACCEL_SCAN_Y, + INV_ICM45600_ACCEL_SCAN_Z, + INV_ICM45600_ACCEL_SCAN_TEMP, + INV_ICM45600_ACCEL_SCAN_TIMESTAMP, +}; + +static const struct iio_chan_spec_ext_info inv_icm45600_accel_ext_infos[] = =3D { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm45600_get_mount_matrix), + { } +}; + +#define INV_ICM45600_ACCEL_CHAN(_modifier, _index, _ext_info) \ + { \ + .type =3D IIO_ACCEL, \ + .modified =3D 1, \ + .channel2 =3D _modifier, \ + .info_mask_separate =3D \ + BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .info_mask_shared_by_type =3D \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available =3D \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .info_mask_shared_by_all =3D \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available =3D \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index =3D _index, \ + .scan_type =3D { \ + .sign =3D 's', \ + .realbits =3D 16, \ + .storagebits =3D 16, \ + .endianness =3D IIO_LE, \ + }, \ + .ext_info =3D _ext_info, \ + } + +static const struct iio_chan_spec inv_icm45600_accel_channels[] =3D { + INV_ICM45600_ACCEL_CHAN(IIO_MOD_X, INV_ICM45600_ACCEL_SCAN_X, + inv_icm45600_accel_ext_infos), + INV_ICM45600_ACCEL_CHAN(IIO_MOD_Y, INV_ICM45600_ACCEL_SCAN_Y, + inv_icm45600_accel_ext_infos), + INV_ICM45600_ACCEL_CHAN(IIO_MOD_Z, INV_ICM45600_ACCEL_SCAN_Z, + inv_icm45600_accel_ext_infos), + INV_ICM45600_TEMP_CHAN(INV_ICM45600_ACCEL_SCAN_TEMP), + IIO_CHAN_SOFT_TIMESTAMP(INV_ICM45600_ACCEL_SCAN_TIMESTAMP), +}; + +/* + * IIO buffer data: size must be a power of 2 and timestamp aligned + * 16 bytes: 6 bytes acceleration, 2 bytes temperature, 8 bytes timestamp + */ +struct inv_icm45600_accel_buffer { + struct inv_icm45600_fifo_sensor_data accel; + s16 temp; + aligned_s64 timestamp; +}; + +static const unsigned long inv_icm45600_accel_scan_masks[] =3D { + /* 3-axis accel + temperature */ + BIT(INV_ICM45600_ACCEL_SCAN_X) | + BIT(INV_ICM45600_ACCEL_SCAN_Y) | + BIT(INV_ICM45600_ACCEL_SCAN_Z) | + BIT(INV_ICM45600_ACCEL_SCAN_TEMP), + 0 +}; + +/* enable accelerometer sensor and FIFO write */ +static int inv_icm45600_accel_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + struct inv_icm45600_sensor_state *accel_st =3D iio_priv(indio_dev); + struct inv_icm45600_sensor_conf conf =3D INV_ICM45600_SENSOR_CONF_KEEP_VA= LUES; + unsigned int fifo_en =3D 0; + unsigned int sleep =3D 0; + int ret; + + scoped_guard(mutex, &st->lock) { + if (*scan_mask & BIT(INV_ICM45600_ACCEL_SCAN_TEMP)) + fifo_en |=3D INV_ICM45600_SENSOR_TEMP; + + if (*scan_mask & (BIT(INV_ICM45600_ACCEL_SCAN_X) | + BIT(INV_ICM45600_ACCEL_SCAN_Y) | + BIT(INV_ICM45600_ACCEL_SCAN_Z))) { + /* enable accel sensor */ + conf.mode =3D accel_st->power_mode; + ret =3D inv_icm45600_set_accel_conf(st, &conf, &sleep); + if (ret) + return ret; + fifo_en |=3D INV_ICM45600_SENSOR_ACCEL; + } + + /* Update data FIFO write. */ + ret =3D inv_icm45600_buffer_set_fifo_en(st, fifo_en | st->fifo.en); + } + + /* Sleep required time. */ + if (sleep) + msleep(sleep); + + return ret; +} + +static int _inv_icm45600_accel_read_sensor(struct inv_icm45600_state *st, + struct inv_icm45600_sensor_state *accel_st, + unsigned int reg, int *val) +{ + struct inv_icm45600_sensor_conf conf =3D INV_ICM45600_SENSOR_CONF_KEEP_VA= LUES; + int ret; + + /* enable accel sensor */ + conf.mode =3D accel_st->power_mode; + ret =3D inv_icm45600_set_accel_conf(st, &conf, NULL); + if (ret) + return ret; + + /* read accel register data */ + ret =3D regmap_bulk_read(st->map, reg, &st->buffer.u16, sizeof(st->buffer= .u16)); + if (ret) + return ret; + + *val =3D sign_extend32(le16_to_cpup(&st->buffer.u16), 15); + if (*val =3D=3D INV_ICM45600_DATA_INVALID) + return -ENODATA; + + return 0; +} + +static int inv_icm45600_accel_read_sensor(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + struct inv_icm45600_sensor_state *accel_st =3D iio_priv(indio_dev); + struct device *dev =3D regmap_get_device(st->map); + unsigned int reg; + int ret; + + if (chan->type !=3D IIO_ACCEL) + return -EINVAL; + + switch (chan->channel2) { + case IIO_MOD_X: + reg =3D INV_ICM45600_REG_ACCEL_DATA_X; + break; + case IIO_MOD_Y: + reg =3D INV_ICM45600_REG_ACCEL_DATA_Y; + break; + case IIO_MOD_Z: + reg =3D INV_ICM45600_REG_ACCEL_DATA_Z; + break; + default: + return -EINVAL; + } + + ret =3D pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + scoped_guard(mutex, &st->lock) + ret =3D _inv_icm45600_accel_read_sensor(st, accel_st, reg, val); + + pm_runtime_put_autosuspend(dev); + + return ret; +} + +/* IIO format int + nano */ +const int inv_icm45600_accel_scale[][2] =3D { + /* +/- 16G =3D> 0.004788403 m/s-2 */ + [INV_ICM45600_ACCEL_FS_16G] =3D { 0, 4788403 }, + /* +/- 8G =3D> 0.002394202 m/s-2 */ + [INV_ICM45600_ACCEL_FS_8G] =3D { 0, 2394202 }, + /* +/- 4G =3D> 0.001197101 m/s-2 */ + [INV_ICM45600_ACCEL_FS_4G] =3D { 0, 1197101 }, + /* +/- 2G =3D> 0.000598550 m/s-2 */ + [INV_ICM45600_ACCEL_FS_2G] =3D { 0, 598550 }, +}; + +const int inv_icm45686_accel_scale[][2] =3D { + /* +/- 32G =3D> 0.009576806 m/s-2 */ + [INV_ICM45686_ACCEL_FS_32G] =3D { 0, 9576806 }, + /* +/- 16G =3D> 0.004788403 m/s-2 */ + [INV_ICM45686_ACCEL_FS_16G] =3D { 0, 4788403 }, + /* +/- 8G =3D> 0.002394202 m/s-2 */ + [INV_ICM45686_ACCEL_FS_8G] =3D { 0, 2394202 }, + /* +/- 4G =3D> 0.001197101 m/s-2 */ + [INV_ICM45686_ACCEL_FS_4G] =3D { 0, 1197101 }, + /* +/- 2G =3D> 0.000598550 m/s-2 */ + [INV_ICM45686_ACCEL_FS_2G] =3D { 0, 598550 }, +}; + +static int inv_icm45600_accel_read_scale(struct iio_dev *indio_dev, + int *val, int *val2) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + struct inv_icm45600_sensor_state *accel_st =3D iio_priv(indio_dev); + unsigned int idx; + + idx =3D st->conf.accel.fs; + + /* Full scale register starts at 1 for not High FSR parts */ + if (accel_st->scales =3D=3D (const int *)&inv_icm45600_accel_scale) + idx--; + + *val =3D accel_st->scales[2 * idx]; + *val2 =3D accel_st->scales[2 * idx + 1]; + return IIO_VAL_INT_PLUS_NANO; +} + +static int inv_icm45600_accel_write_scale(struct iio_dev *indio_dev, + int val, int val2) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + struct inv_icm45600_sensor_state *accel_st =3D iio_priv(indio_dev); + struct device *dev =3D regmap_get_device(st->map); + unsigned int idx; + struct inv_icm45600_sensor_conf conf =3D INV_ICM45600_SENSOR_CONF_KEEP_VA= LUES; + int ret; + + for (idx =3D 0; idx < accel_st->scales_len; idx +=3D 2) { + if (val =3D=3D accel_st->scales[idx] && + val2 =3D=3D accel_st->scales[idx + 1]) + break; + } + if (idx =3D=3D accel_st->scales_len) + return -EINVAL; + + conf.fs =3D idx / 2; + + /* Full scale register starts at 1 for not High FSR parts */ + if (accel_st->scales =3D=3D (const int *)&inv_icm45600_accel_scale) + conf.fs++; + + ret =3D pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + scoped_guard(mutex, &st->lock) + ret =3D inv_icm45600_set_accel_conf(st, &conf, NULL); + + pm_runtime_put_autosuspend(dev); + + return ret; +} + +/* IIO format int + micro */ +static const int inv_icm45600_accel_odr[] =3D { + 1, 562500, /* 1.5625Hz */ + 3, 125000, /* 3.125Hz */ + 6, 250000, /* 6.25Hz */ + 12, 500000, /* 12.5Hz */ + 25, 0, /* 25Hz */ + 50, 0, /* 50Hz */ + 100, 0, /* 100Hz */ + 200, 0, /* 200Hz */ + 400, 0, /* 400Hz */ + 800, 0, /* 800Hz */ + 1600, 0, /* 1.6kHz */ + 3200, 0, /* 3.2kHz */ + 6400, 0, /* 6.4kHz */ +}; + +static const int inv_icm45600_accel_odr_conv[] =3D { + INV_ICM45600_ODR_1_5625HZ_LP, + INV_ICM45600_ODR_3_125HZ_LP, + INV_ICM45600_ODR_6_25HZ_LP, + INV_ICM45600_ODR_12_5HZ, + INV_ICM45600_ODR_25HZ, + INV_ICM45600_ODR_50HZ, + INV_ICM45600_ODR_100HZ, + INV_ICM45600_ODR_200HZ, + INV_ICM45600_ODR_400HZ, + INV_ICM45600_ODR_800HZ_LN, + INV_ICM45600_ODR_1600HZ_LN, + INV_ICM45600_ODR_3200HZ_LN, + INV_ICM45600_ODR_6400HZ_LN, +}; + +static int inv_icm45600_accel_read_odr(struct inv_icm45600_state *st, + int *val, int *val2) +{ + unsigned int odr; + unsigned int i; + + odr =3D st->conf.accel.odr; + + for (i =3D 0; i < ARRAY_SIZE(inv_icm45600_accel_odr_conv); ++i) { + if (inv_icm45600_accel_odr_conv[i] =3D=3D odr) + break; + } + if (i >=3D ARRAY_SIZE(inv_icm45600_accel_odr_conv)) + return -EINVAL; + + *val =3D inv_icm45600_accel_odr[2 * i]; + *val2 =3D inv_icm45600_accel_odr[2 * i + 1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int _inv_icm45600_accel_write_odr(struct iio_dev *indio_dev, int od= r) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + struct inv_icm45600_sensor_state *accel_st =3D iio_priv(indio_dev); + struct inv_sensors_timestamp *ts =3D &accel_st->ts; + struct inv_icm45600_sensor_conf conf =3D INV_ICM45600_SENSOR_CONF_KEEP_VA= LUES; + int ret; + + conf.odr =3D odr; + ret =3D inv_sensors_timestamp_update_odr(ts, inv_icm45600_odr_to_period(c= onf.odr), + iio_buffer_enabled(indio_dev)); + if (ret) + return ret; + + if (st->conf.accel.mode !=3D INV_ICM45600_SENSOR_MODE_OFF) + conf.mode =3D accel_st->power_mode; + + ret =3D inv_icm45600_set_accel_conf(st, &conf, NULL); + if (ret) + return ret; + + inv_icm45600_buffer_update_fifo_period(st); + inv_icm45600_buffer_update_watermark(st); + + return 0; +} + +static int inv_icm45600_accel_write_odr(struct iio_dev *indio_dev, + int val, int val2) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + struct device *dev =3D regmap_get_device(st->map); + unsigned int idx; + int odr; + int ret; + + for (idx =3D 0; idx < ARRAY_SIZE(inv_icm45600_accel_odr); idx +=3D 2) { + if (val =3D=3D inv_icm45600_accel_odr[idx] && + val2 =3D=3D inv_icm45600_accel_odr[idx + 1]) + break; + } + if (idx >=3D ARRAY_SIZE(inv_icm45600_accel_odr)) + return -EINVAL; + + odr =3D inv_icm45600_accel_odr_conv[idx / 2]; + + ret =3D pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + scoped_guard(mutex, &st->lock) + ret =3D _inv_icm45600_accel_write_odr(indio_dev, odr); + + pm_runtime_put_autosuspend(dev); + + return ret; +} + +/* + * Calibration bias values, IIO range format int + micro. + * Value is limited to +/-1g coded on 14 bits signed. Step is 0.125mg. + */ +static int inv_icm45600_accel_calibbias[] =3D { + -9, 806650, /* min: -9.806650 m/s=C2=B2 */ + 0, 1197, /* step: 0.001197 m/s=C2=B2 */ + 9, 805453, /* max: 9.805453 m/s=C2=B2 */ +}; + +static int inv_icm45600_accel_read_offset(struct inv_icm45600_state *st, + struct iio_chan_spec const *chan, + int *val, int *val2) +{ + struct device *dev =3D regmap_get_device(st->map); + s64 val64; + s32 bias; + unsigned int reg; + s16 offset; + int ret; + + if (chan->type !=3D IIO_ACCEL) + return -EINVAL; + + switch (chan->channel2) { + case IIO_MOD_X: + reg =3D INV_ICM45600_IPREG_SYS2_REG_24; + break; + case IIO_MOD_Y: + reg =3D INV_ICM45600_IPREG_SYS2_REG_32; + break; + case IIO_MOD_Z: + reg =3D INV_ICM45600_IPREG_SYS2_REG_40; + break; + default: + return -EINVAL; + } + + ret =3D pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + scoped_guard(mutex, &st->lock) + ret =3D regmap_bulk_read(st->map, reg, &st->buffer.u16, sizeof(st->buffe= r.u16)); + + pm_runtime_put_autosuspend(dev); + if (ret) + return ret; + + offset =3D le16_to_cpup(&st->buffer.u16) & INV_ICM45600_ACCEL_OFFUSER_MAS= K; + /* 14 bits signed value */ + offset =3D sign_extend32(offset, 13); + + /* + * convert raw offset to g then to m/s=C2=B2 + * 14 bits signed raw step 1/8192g + * g to m/s=C2=B2: 9.806650 + * result in micro (* 1000000) + * (offset * 9806650) / 8192 + */ + val64 =3D (s64)offset * 9806650LL; + /* for rounding, add + or - divisor (8192) divided by 2 */ + if (val64 >=3D 0) + val64 +=3D 8192LL / 2LL; + else + val64 -=3D 8192LL / 2LL; + bias =3D div_s64(val64, 8192L); + *val =3D bias / 1000000L; + *val2 =3D bias % 1000000L; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int inv_icm45600_accel_write_offset(struct inv_icm45600_state *st, + struct iio_chan_spec const *chan, + int val, int val2) +{ + struct device *dev =3D regmap_get_device(st->map); + s64 val64; + s32 min, max; + unsigned int reg; + s16 offset; + int ret; + + if (chan->type !=3D IIO_ACCEL) + return -EINVAL; + + switch (chan->channel2) { + case IIO_MOD_X: + reg =3D INV_ICM45600_IPREG_SYS2_REG_24; + break; + case IIO_MOD_Y: + reg =3D INV_ICM45600_IPREG_SYS2_REG_32; + break; + case IIO_MOD_Z: + reg =3D INV_ICM45600_IPREG_SYS2_REG_40; + break; + default: + return -EINVAL; + } + + /* inv_icm45600_accel_calibbias: min - step - max in micro */ + min =3D inv_icm45600_accel_calibbias[0] * 1000000L - + inv_icm45600_accel_calibbias[1]; + max =3D inv_icm45600_accel_calibbias[4] * 1000000L + + inv_icm45600_accel_calibbias[5]; + val64 =3D (s64)val * 1000000LL; + if (val >=3D 0) + val64 +=3D (s64)val2; + else + val64 -=3D (s64)val2; + if (val64 < min || val64 > max) + return -EINVAL; + + /* + * convert m/s=C2=B2 to g then to raw value + * m/s=C2=B2 to g: 1 / 9.806650 + * g to raw 14 bits signed, step 1/8192g: * 8192 + * val in micro (1000000) + * val * 8192 / (9.806650 * 1000000) + */ + val64 =3D val64 * 8192LL; + /* for rounding, add + or - divisor (9806650) divided by 2 */ + if (val64 >=3D 0) + val64 +=3D 9806650 / 2; + else + val64 -=3D 9806650 / 2; + offset =3D div_s64(val64, 9806650); + + /* clamp value limited to 14 bits signed */ + offset =3D clamp(offset, -8192, 8191); + + st->buffer.u16 =3D cpu_to_le16(offset & INV_ICM45600_ACCEL_OFFUSER_MASK); + + ret =3D pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + scoped_guard(mutex, &st->lock) + ret =3D regmap_bulk_write(st->map, reg, &st->buffer.u16, sizeof(st->buff= er.u16)); + + pm_runtime_put_autosuspend(dev); + return ret; +} + +static int inv_icm45600_accel_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + int ret; + + switch (chan->type) { + case IIO_ACCEL: + break; + case IIO_TEMP: + return inv_icm45600_temp_read_raw(indio_dev, chan, val, val2, mask); + default: + return -EINVAL; + } + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret =3D inv_icm45600_accel_read_sensor(indio_dev, chan, val); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return inv_icm45600_accel_read_scale(indio_dev, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm45600_accel_read_odr(st, val, val2); + case IIO_CHAN_INFO_CALIBBIAS: + return inv_icm45600_accel_read_offset(st, chan, val, val2); + default: + return -EINVAL; + } +} + +static int inv_icm45600_accel_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, + int *type, int *length, long mask) +{ + struct inv_icm45600_sensor_state *accel_st =3D iio_priv(indio_dev); + + if (chan->type !=3D IIO_ACCEL) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *vals =3D accel_st->scales; + *type =3D IIO_VAL_INT_PLUS_NANO; + *length =3D accel_st->scales_len; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals =3D inv_icm45600_accel_odr; + *type =3D IIO_VAL_INT_PLUS_MICRO; + *length =3D ARRAY_SIZE(inv_icm45600_accel_odr); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_CALIBBIAS: + *vals =3D inv_icm45600_accel_calibbias; + *type =3D IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +static int inv_icm45600_accel_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + int ret; + + if (chan->type !=3D IIO_ACCEL) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret =3D inv_icm45600_accel_write_scale(indio_dev, val, val2); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm45600_accel_write_odr(indio_dev, val, val2); + case IIO_CHAN_INFO_CALIBBIAS: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret =3D inv_icm45600_accel_write_offset(st, chan, val, val2); + iio_device_release_direct(indio_dev); + return ret; + default: + return -EINVAL; + } +} + +static int inv_icm45600_accel_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + if (chan->type !=3D IIO_ACCEL) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int inv_icm45600_accel_hwfifo_set_watermark(struct iio_dev *indio_d= ev, + unsigned int val) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + + guard(mutex)(&st->lock); + + st->fifo.watermark.accel =3D val; + return inv_icm45600_buffer_update_watermark(st); +} + +static int inv_icm45600_accel_hwfifo_flush(struct iio_dev *indio_dev, + unsigned int count) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + int ret; + + if (count =3D=3D 0) + return 0; + + guard(mutex)(&st->lock); + + ret =3D inv_icm45600_buffer_hwfifo_flush(st, count); + if (ret) + return ret; + + return st->fifo.nb.accel; +} + +static const struct iio_info inv_icm45600_accel_info =3D { + .read_raw =3D inv_icm45600_accel_read_raw, + .read_avail =3D inv_icm45600_accel_read_avail, + .write_raw =3D inv_icm45600_accel_write_raw, + .write_raw_get_fmt =3D inv_icm45600_accel_write_raw_get_fmt, + .debugfs_reg_access =3D inv_icm45600_debugfs_reg, + .update_scan_mode =3D inv_icm45600_accel_update_scan_mode, + .hwfifo_set_watermark =3D inv_icm45600_accel_hwfifo_set_watermark, + .hwfifo_flush_to_buffer =3D inv_icm45600_accel_hwfifo_flush, +}; + +struct iio_dev *inv_icm45600_accel_init(struct inv_icm45600_state *st) +{ + struct device *dev =3D regmap_get_device(st->map); + struct inv_icm45600_sensor_state *accel_st; + struct inv_sensors_timestamp_chip ts_chip; + struct iio_dev *indio_dev; + const char *name; + int ret; + + name =3D devm_kasprintf(dev, GFP_KERNEL, "%s-accel", st->chip_info->name); + if (!name) + return ERR_PTR(-ENOMEM); + + indio_dev =3D devm_iio_device_alloc(dev, sizeof(*accel_st)); + if (!indio_dev) + return ERR_PTR(-ENOMEM); + accel_st =3D iio_priv(indio_dev); + + accel_st->scales =3D st->chip_info->accel_scales; + accel_st->scales_len =3D st->chip_info->accel_scales_len * 2; + + /* low-power (LP) mode by default at init, no ULP mode */ + accel_st->power_mode =3D INV_ICM45600_SENSOR_MODE_LOW_POWER; + ret =3D regmap_set_bits(st->map, INV_ICM45600_REG_SMC_CONTROL_0, + INV_ICM45600_SMC_CONTROL_0_ACCEL_LP_CLK_SEL); + if (ret) + return ERR_PTR(ret); + + /* + * clock period is 32kHz (31250ns) + * jitter is +/- 2% (20 per mille) + */ + ts_chip.clock_period =3D 31250; + ts_chip.jitter =3D 20; + ts_chip.init_period =3D inv_icm45600_odr_to_period(st->conf.accel.odr); + inv_sensors_timestamp_init(&accel_st->ts, &ts_chip); + + iio_device_set_drvdata(indio_dev, st); + indio_dev->name =3D name; + indio_dev->info =3D &inv_icm45600_accel_info; + indio_dev->modes =3D INDIO_DIRECT_MODE; + indio_dev->channels =3D inv_icm45600_accel_channels; + indio_dev->num_channels =3D ARRAY_SIZE(inv_icm45600_accel_channels); + indio_dev->available_scan_masks =3D inv_icm45600_accel_scan_masks; + + ret =3D devm_iio_kfifo_buffer_setup(dev, indio_dev, + &inv_icm45600_buffer_ops); + if (ret) + return ERR_PTR(ret); + + ret =3D devm_iio_device_register(dev, indio_dev); + if (ret) + return ERR_PTR(ret); + + return indio_dev; +} + +int inv_icm45600_accel_parse_fifo(struct iio_dev *indio_dev) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + struct inv_icm45600_sensor_state *accel_st =3D iio_priv(indio_dev); + struct inv_sensors_timestamp *ts =3D &accel_st->ts; + ssize_t i, size; + unsigned int no; + + /* parse all fifo packets */ + for (i =3D 0, no =3D 0; i < st->fifo.count; i +=3D size, ++no) { + struct inv_icm45600_accel_buffer buffer =3D { }; + const struct inv_icm45600_fifo_sensor_data *accel, *gyro; + const __le16 *timestamp; + const s8 *temp; + unsigned int odr; + s64 ts_val; + + size =3D inv_icm45600_fifo_decode_packet(&st->fifo.data[i], + &accel, &gyro, &temp, ×tamp, &odr); + /* quit if error or FIFO is empty */ + if (size <=3D 0) + return size; + + /* skip packet if no accel data or data is invalid */ + if (accel =3D=3D NULL || !inv_icm45600_fifo_is_data_valid(accel)) + continue; + + /* update odr */ + if (odr & INV_ICM45600_SENSOR_ACCEL) + inv_sensors_timestamp_apply_odr(ts, st->fifo.period, + st->fifo.nb.total, no); + + memcpy(&buffer.accel, accel, sizeof(buffer.accel)); + /* convert 8 bits FIFO temperature in high resolution format */ + buffer.temp =3D temp ? (*temp * 64) : 0; + ts_val =3D 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_icm45600/inv_icm45600_core.c b/drivers/iio= /imu/inv_icm45600/inv_icm45600_core.c index 706fda073060f18e3627b1de66c96ad95a74b8e1..e8bf7e0ee1bede0c2c343e3abcf= 36b9c2a2b0954 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c @@ -155,6 +155,8 @@ const struct inv_icm45600_chip_info inv_icm45605_chip_i= nfo =3D { .whoami =3D INV_ICM45600_WHOAMI_ICM45605, .name =3D "icm45605", .conf =3D &inv_icm45600_default_conf, + .accel_scales =3D (const int *)inv_icm45600_accel_scale, + .accel_scales_len =3D INV_ICM45600_ACCEL_FS_MAX, .gyro_scales =3D (const int *)inv_icm45600_gyro_scale, .gyro_scales_len =3D INV_ICM45600_GYRO_FS_MAX, }; @@ -164,6 +166,8 @@ const struct inv_icm45600_chip_info inv_icm45606_chip_i= nfo =3D { .whoami =3D INV_ICM45600_WHOAMI_ICM45606, .name =3D "icm45606", .conf =3D &inv_icm45600_default_conf, + .accel_scales =3D (const int *)inv_icm45600_accel_scale, + .accel_scales_len =3D INV_ICM45600_ACCEL_FS_MAX, .gyro_scales =3D (const int *)inv_icm45600_gyro_scale, .gyro_scales_len =3D INV_ICM45600_GYRO_FS_MAX, }; @@ -173,6 +177,8 @@ const struct inv_icm45600_chip_info inv_icm45608_chip_i= nfo =3D { .whoami =3D INV_ICM45600_WHOAMI_ICM45608, .name =3D "icm45608", .conf =3D &inv_icm45600_default_conf, + .accel_scales =3D (const int *)inv_icm45600_accel_scale, + .accel_scales_len =3D INV_ICM45600_ACCEL_FS_MAX, .gyro_scales =3D (const int *)inv_icm45600_gyro_scale, .gyro_scales_len =3D INV_ICM45600_GYRO_FS_MAX, }; @@ -182,6 +188,8 @@ const struct inv_icm45600_chip_info inv_icm45634_chip_i= nfo =3D { .whoami =3D INV_ICM45600_WHOAMI_ICM45634, .name =3D "icm45634", .conf =3D &inv_icm45600_default_conf, + .accel_scales =3D (const int *)inv_icm45600_accel_scale, + .accel_scales_len =3D INV_ICM45600_ACCEL_FS_MAX, .gyro_scales =3D (const int *)inv_icm45600_gyro_scale, .gyro_scales_len =3D INV_ICM45600_GYRO_FS_MAX, }; @@ -191,6 +199,8 @@ const struct inv_icm45600_chip_info inv_icm45686_chip_i= nfo =3D { .whoami =3D INV_ICM45600_WHOAMI_ICM45686, .name =3D "icm45686", .conf =3D &inv_icm45686_default_conf, + .accel_scales =3D (const int *)inv_icm45686_accel_scale, + .accel_scales_len =3D INV_ICM45686_ACCEL_FS_MAX, .gyro_scales =3D (const int *)inv_icm45686_gyro_scale, .gyro_scales_len =3D INV_ICM45686_GYRO_FS_MAX, }; @@ -200,6 +210,8 @@ const struct inv_icm45600_chip_info inv_icm45687_chip_i= nfo =3D { .whoami =3D INV_ICM45600_WHOAMI_ICM45687, .name =3D "icm45687", .conf =3D &inv_icm45686_default_conf, + .accel_scales =3D (const int *)inv_icm45686_accel_scale, + .accel_scales_len =3D INV_ICM45686_ACCEL_FS_MAX, .gyro_scales =3D (const int *)inv_icm45686_gyro_scale, .gyro_scales_len =3D INV_ICM45686_GYRO_FS_MAX, }; @@ -209,6 +221,8 @@ const struct inv_icm45600_chip_info inv_icm45688p_chip_= info =3D { .whoami =3D INV_ICM45600_WHOAMI_ICM45688P, .name =3D "icm45688p", .conf =3D &inv_icm45686_default_conf, + .accel_scales =3D (const int *)inv_icm45686_accel_scale, + .accel_scales_len =3D INV_ICM45686_ACCEL_FS_MAX, .gyro_scales =3D (const int *)inv_icm45686_gyro_scale, .gyro_scales_len =3D INV_ICM45686_GYRO_FS_MAX, }; @@ -218,6 +232,8 @@ const struct inv_icm45600_chip_info inv_icm45689_chip_i= nfo =3D { .whoami =3D INV_ICM45600_WHOAMI_ICM45689, .name =3D "icm45689", .conf =3D &inv_icm45686_default_conf, + .accel_scales =3D (const int *)inv_icm45686_accel_scale, + .accel_scales_len =3D INV_ICM45686_ACCEL_FS_MAX, .gyro_scales =3D (const int *)inv_icm45686_gyro_scale, .gyro_scales_len =3D INV_ICM45686_GYRO_FS_MAX, }; @@ -733,6 +749,10 @@ int inv_icm45600_core_probe(struct regmap *regmap, con= st struct inv_icm45600_chi if (IS_ERR(st->indio_gyro)) return PTR_ERR(st->indio_gyro); =20 + st->indio_accel =3D inv_icm45600_accel_init(st); + if (IS_ERR(st->indio_accel)) + return PTR_ERR(st->indio_accel); + ret =3D inv_icm45600_irq_init(st, irq, irq_type, open_drain); if (ret) return ret; --=20 2.34.1 From nobody Sat Sep 13 22:31:04 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4088127A134; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755699886; cv=none; b=PIAsuv+zh/cjAlNiZntnnBJf1HJOWE1YRKNlpJtZMGpIJoh8UxDJLyxLeLVPjaqWIsOYgs2SBdrpe1+9STfmZdqHfM6gYJrOYDLJ7UVflOhqHwEv4ssOriZtuSFJbRnHK6mS2dyszimYOsv3plg37cNlV6eInfSR1rDU1CX5hrI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755699886; c=relaxed/simple; bh=iad621vV49d+wv02UCcQ9so9zAeVhfhnBdchvCJXOp4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=BuGAfvYnb1bj3tN84Oog5QohdiS4NEgzQEclsZRQ/v7YBsGBR4x9dhqS8Nf6oMOjLsilpgVsAy2f/Wb+SwrYOTWJ7boQsOdGx1gMCqsmmG1HTomarmMpc/rAuVCiUgVLhWdg1ow2PasNo4Jbu4NcDewlxkcIGa5tfB+98BM6wDI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=IJyDdefw; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="IJyDdefw" Received: by smtp.kernel.org (Postfix) with ESMTPS id AE0C3C2BC9E; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1755699885; bh=iad621vV49d+wv02UCcQ9so9zAeVhfhnBdchvCJXOp4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=IJyDdefwon57mZvbxGZoLDfDazTSsy5cXs3u7E64zaZzxwiixUYYPkKZ44Wm2n7BA 3oKzlqo8SoFxur8DlSDjcHaRJQ48BD83cues7uSfTV6+5HZK5yVHjBeXjISR8EdOIQ 37A3/GQb9vBgZYmksYuEJ3F3W6uSbd+q+ynt5uXe8h1f9Gs0PbyPaWm4UbwivQuM4J 2j8IKvNakrbwPR7TSRbN/bZN3204v9zUlzIk81P/9w5LGNY7VovFvR/3F17zhtn+Ih FsJjTXnx1ui4VPHdQmfl6PZ2HMfA+kiQ4Jqyhhx/mw8z1eATjziaGvi7k1/yLW5UD1 UZvulXaLiimoQ== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id A565ECA0EFE; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Wed, 20 Aug 2025 14:24:24 +0000 Subject: [PATCH v5 6/9] iio: imu: inv_icm45600: add I2C driver for inv_icm45600 driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250820-add_newport_driver-v5-6-2fc9f13dddee@tdk.com> References: <20250820-add_newport_driver-v5-0-2fc9f13dddee@tdk.com> In-Reply-To: <20250820-add_newport_driver-v5-0-2fc9f13dddee@tdk.com> To: Jonathan Cameron , David Lechner , =?utf-8?q?Nuno_S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, Remi Buisson X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1755699883; l=5052; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=sBMP9Nob/m9bf7U1Inc9JV4s0JfRRB/2JECgb0ifR6Q=; b=UWdfpEKOEjnzjiOaSTVNpDRGH9W3klFpS/3LnJFAIoQCkTK8YRA/JNpN2MmFxLH1caXRyZB4z nfrFPXIGHUlD8crYgeBkchllWsJWj3YdjCaJjtXbwtSwGhVUoSALjyi X-Developer-Key: i=remi.buisson@tdk.com; a=ed25519; pk=yDVMi4C7RpXN4dififo42A7fDDt3THYzoZoNq9lUZuo= X-Endpoint-Received: by B4 Relay for remi.buisson@tdk.com/20250411 with auth_id=372 X-Original-From: Remi Buisson Reply-To: remi.buisson@tdk.com From: Remi Buisson Add I2C driver for InvenSense ICM-456000 devices. Signed-off-by: Remi Buisson --- drivers/iio/imu/inv_icm45600/Kconfig | 21 ++++++ drivers/iio/imu/inv_icm45600/Makefile | 3 + drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c | 98 +++++++++++++++++++++= ++++ 3 files changed, 122 insertions(+) diff --git a/drivers/iio/imu/inv_icm45600/Kconfig b/drivers/iio/imu/inv_icm= 45600/Kconfig index ea0a8d20cba26549b74105fa6fdbca1ddb222633..5b044a954e952ffa8e44507eea4= 2872e1f3161bc 100644 --- a/drivers/iio/imu/inv_icm45600/Kconfig +++ b/drivers/iio/imu/inv_icm45600/Kconfig @@ -5,3 +5,24 @@ config INV_ICM45600 select IIO_BUFFER select IIO_KFIFO_BUF select IIO_INV_SENSORS_TIMESTAMP + +config INV_ICM45600_I2C + tristate "InvenSense ICM-456xx I2C driver" + depends on I2C + select INV_ICM45600 + select REGMAP_I2C + help + This driver supports the InvenSense ICM-456xx motion tracking + devices over I2C. + Supported devices: + - ICM-45605 + - ICM-45606 + - ICM-45608 + - ICM-45634 + - ICM-45686 + - ICM-45687 + - ICM-45688-P + - ICM-45689 + + This driver can be built as a module. The module will be called + inv-icm45600-i2c. diff --git a/drivers/iio/imu/inv_icm45600/Makefile b/drivers/iio/imu/inv_ic= m45600/Makefile index e34553d2b74dc46bb0f533d2bd0875655f91c781..c43e5d6ad3a2ddbd666d7763001= 5c440e740d969 100644 --- a/drivers/iio/imu/inv_icm45600/Makefile +++ b/drivers/iio/imu/inv_icm45600/Makefile @@ -5,3 +5,6 @@ inv-icm45600-y +=3D inv_icm45600_core.o inv-icm45600-y +=3D inv_icm45600_buffer.o inv-icm45600-y +=3D inv_icm45600_gyro.o inv-icm45600-y +=3D inv_icm45600_accel.o + +obj-$(CONFIG_INV_ICM45600_I2C) +=3D inv-icm45600-i2c.o +inv-icm45600-i2c-y +=3D inv_icm45600_i2c.o diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c b/drivers/iio/= imu/inv_icm45600/inv_icm45600_i2c.c new file mode 100644 index 0000000000000000000000000000000000000000..5ebc18121a11f8ad576efb4d4cf= 80091c13af31d --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Copyright (C) 2025 InvenSense, Inc. */ + +#include +#include +#include +#include +#include +#include + +#include "inv_icm45600.h" + +static const struct regmap_config inv_icm45600_regmap_config =3D { + .reg_bits =3D 8, + .val_bits =3D 8, +}; + +static int inv_icm45600_probe(struct i2c_client *client) +{ + const struct inv_icm45600_chip_info *chip_info; + struct regmap *regmap; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) + return -ENODEV; + + chip_info =3D device_get_match_data(&client->dev); + if (!chip_info) + return -ENODEV; + + regmap =3D devm_regmap_init_i2c(client, &inv_icm45600_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return inv_icm45600_core_probe(regmap, chip_info, true, NULL); +} + +/* + * The device id table is used to identify which device is + * supported by this driver. + */ +static const struct i2c_device_id inv_icm45600_id[] =3D { + { "icm45605", (kernel_ulong_t)&inv_icm45605_chip_info }, + { "icm45606", (kernel_ulong_t)&inv_icm45606_chip_info }, + { "icm45608", (kernel_ulong_t)&inv_icm45608_chip_info }, + { "icm45634", (kernel_ulong_t)&inv_icm45634_chip_info }, + { "icm45686", (kernel_ulong_t)&inv_icm45686_chip_info }, + { "icm45687", (kernel_ulong_t)&inv_icm45687_chip_info }, + { "icm45688p", (kernel_ulong_t)&inv_icm45688p_chip_info }, + { "icm45689", (kernel_ulong_t)&inv_icm45689_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(i2c, inv_icm45600_id); + +static const struct of_device_id inv_icm45600_of_matches[] =3D { + { + .compatible =3D "invensense,icm45605", + .data =3D &inv_icm45605_chip_info, + }, { + .compatible =3D "invensense,icm45606", + .data =3D &inv_icm45606_chip_info, + }, { + .compatible =3D "invensense,icm45608", + .data =3D &inv_icm45608_chip_info, + }, { + .compatible =3D "invensense,icm45634", + .data =3D &inv_icm45634_chip_info, + }, { + .compatible =3D "invensense,icm45686", + .data =3D &inv_icm45686_chip_info, + }, { + .compatible =3D "invensense,icm45687", + .data =3D &inv_icm45687_chip_info, + }, { + .compatible =3D "invensense,icm45688p", + .data =3D &inv_icm45688p_chip_info, + }, { + .compatible =3D "invensense,icm45689", + .data =3D &inv_icm45689_chip_info, + }, + { } +}; +MODULE_DEVICE_TABLE(of, inv_icm45600_of_matches); + +static struct i2c_driver inv_icm45600_driver =3D { + .driver =3D { + .name =3D "inv-icm45600-i2c", + .of_match_table =3D inv_icm45600_of_matches, + .pm =3D pm_ptr(&inv_icm45600_pm_ops), + }, + .id_table =3D inv_icm45600_id, + .probe =3D inv_icm45600_probe, +}; +module_i2c_driver(inv_icm45600_driver); + +MODULE_AUTHOR("InvenSense, Inc."); +MODULE_DESCRIPTION("InvenSense ICM-456xx I2C driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_ICM45600"); --=20 2.34.1 From nobody Sat Sep 13 22:31:04 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E29D4275B06; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755699886; cv=none; b=i55Y0929IDRAAApUipNsWsoOWXRqjDIhsBNWwE/aUEX1ZHcAQ53BfpptQzFepfquHvQpbJPu9fHNt9TYA0RB5Te01DvGd+AmMHGiT3Q8bJB+7Y57Dd/RH4fuCEsO2h2aI8czgNB9zviu/NWnSXIwIQmIUYEr78WSkmnB1Rs3e+0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755699886; c=relaxed/simple; bh=1mvaLJYXcpBg3jHRWA6Z73vIYEHei+5ci7/poVvYBIQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=QgCsJ+yfXCENa52LGPxPSinY+lA+zg/EIWwtgAvVymC+G2TJz4z2UWwCb5M8Txdx0qm+xwyvphF5L2Rq8mX7Tw0eJvelW0T9f501/x758Rhp/VDQCdIUMkVz9T/rt+IRwIbxd+rsTvZHbF7NNvz7Y8cmP9l0a4r7Ld11Wtk/oq0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=fBm79lPP; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="fBm79lPP" Received: by smtp.kernel.org (Postfix) with ESMTPS id BEFB9C2BCAF; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1755699885; bh=1mvaLJYXcpBg3jHRWA6Z73vIYEHei+5ci7/poVvYBIQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=fBm79lPPf+X8tXIEtkjytbwOn4FNdO9eIq0izbdvexgx0X5dZxSg54DiIg6bqWRdc 7UIXM1EToGZB+pwK80oNqtL/tZ3VdcvSqwLFNB1yQ1kIVbzldoLMo0lG7H490oTlpl AqompVesFiwBuBWgXRne2b7fT740mHrhlFu9IwCbEWNhfHFSwfv4lUn9ke8Mzm4dn0 OIky+z5N8og4taP0vTdAAt3gDcfgvDo09zSiYXw7cZ2pQ6r+LbeOdY3FLA82N6NKR6 KZUGRlLflpplyF0CDnmkRBKgfRaHubcTGJ+vFkfZ8cmkwSLl4mdbiYZ1DjRmXt3ri7 3hMCm4xqnFhiQ== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id B2242CA0EE4; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Wed, 20 Aug 2025 14:24:25 +0000 Subject: [PATCH v5 7/9] iio: imu: inv_icm45600: add SPI driver for inv_icm45600 driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250820-add_newport_driver-v5-7-2fc9f13dddee@tdk.com> References: <20250820-add_newport_driver-v5-0-2fc9f13dddee@tdk.com> In-Reply-To: <20250820-add_newport_driver-v5-0-2fc9f13dddee@tdk.com> To: Jonathan Cameron , David Lechner , =?utf-8?q?Nuno_S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, Remi Buisson X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1755699883; l=5346; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=PWO5+4GHdjNXSXh2Gtxk2P1lIY3h8sVtAxOPR7SWAVA=; b=jcjCiMsUthtT6i/ShhMMs87ckaa9jXdbd4Pt4yVpqarULT/HLZPwppo7CJdSv1H627QvyjTtV BIRrQqtWK/SCB4I9akk/hPKPb/5Y6Tqu7tYdcPA/vcIs3PlHpXm5fZv X-Developer-Key: i=remi.buisson@tdk.com; a=ed25519; pk=yDVMi4C7RpXN4dififo42A7fDDt3THYzoZoNq9lUZuo= X-Endpoint-Received: by B4 Relay for remi.buisson@tdk.com/20250411 with auth_id=372 X-Original-From: Remi Buisson Reply-To: remi.buisson@tdk.com From: Remi Buisson Add SPI driver for InvenSense ICM-456000 devices. Signed-off-by: Remi Buisson --- drivers/iio/imu/inv_icm45600/Kconfig | 21 +++++ drivers/iio/imu/inv_icm45600/Makefile | 3 + drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c | 106 ++++++++++++++++++++= ++++ 3 files changed, 130 insertions(+) diff --git a/drivers/iio/imu/inv_icm45600/Kconfig b/drivers/iio/imu/inv_icm= 45600/Kconfig index 5b044a954e952ffa8e44507eea42872e1f3161bc..01399d136a7ea3aa92a3a18ea45= 5c95c0a6578b3 100644 --- a/drivers/iio/imu/inv_icm45600/Kconfig +++ b/drivers/iio/imu/inv_icm45600/Kconfig @@ -26,3 +26,24 @@ config INV_ICM45600_I2C =20 This driver can be built as a module. The module will be called inv-icm45600-i2c. + +config INV_ICM45600_SPI + tristate "InvenSense ICM-456xx SPI driver" + depends on SPI_MASTER + select INV_ICM45600 + select REGMAP_SPI + help + This driver supports the InvenSense ICM-456xx motion tracking + devices over SPI. + Supported devices: + - ICM-45605 + - ICM-45606 + - ICM-45608 + - ICM-45634 + - ICM-45686 + - ICM-45687 + - ICM-45688-P + - ICM-45689 + + This driver can be built as a module. The module will be called + inv-icm45600-spi. diff --git a/drivers/iio/imu/inv_icm45600/Makefile b/drivers/iio/imu/inv_ic= m45600/Makefile index c43e5d6ad3a2ddbd666d77630015c440e740d969..3692636d393a109a0ad68e955e1= cad59005e9128 100644 --- a/drivers/iio/imu/inv_icm45600/Makefile +++ b/drivers/iio/imu/inv_icm45600/Makefile @@ -8,3 +8,6 @@ inv-icm45600-y +=3D inv_icm45600_accel.o =20 obj-$(CONFIG_INV_ICM45600_I2C) +=3D inv-icm45600-i2c.o inv-icm45600-i2c-y +=3D inv_icm45600_i2c.o + +obj-$(CONFIG_INV_ICM45600_SPI) +=3D inv-icm45600-spi.o +inv-icm45600-spi-y +=3D inv_icm45600_spi.o diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c b/drivers/iio/= imu/inv_icm45600/inv_icm45600_spi.c new file mode 100644 index 0000000000000000000000000000000000000000..2fd4ecce24bd60aa2112e1bcae3= f7196504df637 --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Copyright (C) 2025 InvenSense, Inc. */ + +#include +#include +#include +#include +#include +#include + +#include "inv_icm45600.h" + +static const struct regmap_config inv_icm45600_regmap_config =3D { + .reg_bits =3D 8, + .val_bits =3D 8, +}; + +static int inv_icm45600_spi_bus_setup(struct inv_icm45600_state *st) +{ + /* Set slew rates for SPI. */ + return regmap_update_bits(st->map, INV_ICM45600_REG_DRIVE_CONFIG0, + INV_ICM45600_DRIVE_CONFIG0_SPI_MASK, + FIELD_PREP(INV_ICM45600_DRIVE_CONFIG0_SPI_MASK, + INV_ICM45600_SPI_SLEW_RATE_5NS)); +} + +static int inv_icm45600_probe(struct spi_device *spi) +{ + const struct inv_icm45600_chip_info *chip_info; + struct regmap *regmap; + + chip_info =3D spi_get_device_match_data(spi); + if (!chip_info) + return -ENODEV; + + /* Use SPI specific regmap. */ + regmap =3D devm_regmap_init_spi(spi, &inv_icm45600_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return inv_icm45600_core_probe(regmap, chip_info, true, + inv_icm45600_spi_bus_setup); +} + +/* + * The device id table is used to identify which device is + * supported by this driver. + */ +static const struct spi_device_id inv_icm45600_id[] =3D { + { "icm45605", (kernel_ulong_t)&inv_icm45605_chip_info }, + { "icm45606", (kernel_ulong_t)&inv_icm45606_chip_info }, + { "icm45608", (kernel_ulong_t)&inv_icm45608_chip_info }, + { "icm45634", (kernel_ulong_t)&inv_icm45634_chip_info }, + { "icm45686", (kernel_ulong_t)&inv_icm45686_chip_info }, + { "icm45687", (kernel_ulong_t)&inv_icm45687_chip_info }, + { "icm45688p", (kernel_ulong_t)&inv_icm45688p_chip_info }, + { "icm45689", (kernel_ulong_t)&inv_icm45689_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, inv_icm45600_id); + +static const struct of_device_id inv_icm45600_of_matches[] =3D { + { + .compatible =3D "invensense,icm45605", + .data =3D &inv_icm45605_chip_info, + }, { + .compatible =3D "invensense,icm45606", + .data =3D &inv_icm45606_chip_info, + }, { + .compatible =3D "invensense,icm45608", + .data =3D &inv_icm45608_chip_info, + }, { + .compatible =3D "invensense,icm45634", + .data =3D &inv_icm45634_chip_info, + }, { + .compatible =3D "invensense,icm45686", + .data =3D &inv_icm45686_chip_info, + }, { + .compatible =3D "invensense,icm45687", + .data =3D &inv_icm45687_chip_info, + }, { + .compatible =3D "invensense,icm45688p", + .data =3D &inv_icm45688p_chip_info, + }, { + .compatible =3D "invensense,icm45689", + .data =3D &inv_icm45689_chip_info, + }, + { } +}; +MODULE_DEVICE_TABLE(of, inv_icm45600_of_matches); + +static struct spi_driver inv_icm45600_driver =3D { + .driver =3D { + .name =3D "inv-icm45600-spi", + .of_match_table =3D inv_icm45600_of_matches, + .pm =3D pm_ptr(&inv_icm45600_pm_ops), + }, + .id_table =3D inv_icm45600_id, + .probe =3D inv_icm45600_probe, +}; +module_spi_driver(inv_icm45600_driver); + +MODULE_AUTHOR("InvenSense, Inc."); +MODULE_DESCRIPTION("InvenSense ICM-456xx SPI driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_ICM45600"); --=20 2.34.1 From nobody Sat Sep 13 22:31:04 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 409EB27A45C; Wed, 20 Aug 2025 14:24:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755699886; cv=none; b=K0Nk/uxd9YK1ro6rw8vbnjSpIKtRfizDTRXvi8pkwJ6Gg1jwibV42V06zh307m9/HHuTu2Z/ClqBwTVV3YKGP/EsvtrSIqWNT8h6M99LETpt5Kys0n/9SUr53geS6DvlE9FehHrEB/eRypiI23ousijNtkhatrGoSMuL95VjifQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755699886; c=relaxed/simple; bh=qLy7Mfk1fO5OlX68MtQ114jWdx58MPhXCI22IADztoA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=DfyUbrim96kCiDsEyNDUvEnmxCHorw3/7ZjrzIqEEnCCCnYDTwP3TZFDrK132gQLkn2V8GccsNl42G0lbl9Wufd66l+SB+eQ8LSWJg6+t0WKoDmxwnGRAdHNBrGsb0OuoAj20WGHuun6E5LMzxf+kxGjoknvXuFYV4ARrSirsAk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=tcaeBTVp; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="tcaeBTVp" Received: by smtp.kernel.org (Postfix) with ESMTPS id C7A8EC116B1; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1755699885; bh=qLy7Mfk1fO5OlX68MtQ114jWdx58MPhXCI22IADztoA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=tcaeBTVp5C98gOu0P3njLmdAeJrejGHqMPFDXq1hpQML0wTcRjym3zgxL+8p42piY tahDyF4Vqw5ai1FeBu48tM3hNRHNaAqt0BpcXa8Ic90khYh9sUzc1ceeKzmSTxW9Ma Ue1/CGvuCvgXDzfGo+K2Ju1so7vstOG+fE3BAqE9Lk4OYi9KW5wOcG/YhKk7sUm0qL tWKaqRar92LpNgptFpZGHsq+lNE4IDsaT5hNBbYUO2MAjhbkysD/HwHy8GQw5K7u+X O1XPHGl606mF2BBYFtNMz+gdudlygRjRi6znz6msm0nrVVzVWAiOQQ+OpqO30Q606h +F5moe+PuYexA== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id BFDB5CA0EFC; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Wed, 20 Aug 2025 14:24:26 +0000 Subject: [PATCH v5 8/9] iio: imu: inv_icm45600: add I3C driver for inv_icm45600 driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250820-add_newport_driver-v5-8-2fc9f13dddee@tdk.com> References: <20250820-add_newport_driver-v5-0-2fc9f13dddee@tdk.com> In-Reply-To: <20250820-add_newport_driver-v5-0-2fc9f13dddee@tdk.com> To: Jonathan Cameron , David Lechner , =?utf-8?q?Nuno_S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, Remi Buisson X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1755699883; l=4440; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=buF0oQQr9kLsJS6bFDnI6ITH4jX267a+LRmeoFoV7CA=; b=eZSt+bc7HdLTTtf361vUV4of9OxS4Vp8je+yW0lqvlN6Q8Hoh7cFskPicaZiLjmp1t4aBTO+B xIKhFcDk7moD9KNhitUNCKwYv3UcAHLDd/eeABKwCsOoJa+lVsf/TTz X-Developer-Key: i=remi.buisson@tdk.com; a=ed25519; pk=yDVMi4C7RpXN4dififo42A7fDDt3THYzoZoNq9lUZuo= X-Endpoint-Received: by B4 Relay for remi.buisson@tdk.com/20250411 with auth_id=372 X-Original-From: Remi Buisson Reply-To: remi.buisson@tdk.com From: Remi Buisson Add I3C driver for InvenSense ICM-45600 devices. Signed-off-by: Remi Buisson --- drivers/iio/imu/inv_icm45600/Kconfig | 21 +++++++ drivers/iio/imu/inv_icm45600/Makefile | 3 + drivers/iio/imu/inv_icm45600/inv_icm45600_i3c.c | 77 +++++++++++++++++++++= ++++ 3 files changed, 101 insertions(+) diff --git a/drivers/iio/imu/inv_icm45600/Kconfig b/drivers/iio/imu/inv_icm= 45600/Kconfig index 01399d136a7ea3aa92a3a18ea455c95c0a6578b3..dc133402f6d75f8c050100e8475= 404e00993818b 100644 --- a/drivers/iio/imu/inv_icm45600/Kconfig +++ b/drivers/iio/imu/inv_icm45600/Kconfig @@ -47,3 +47,24 @@ config INV_ICM45600_SPI =20 This driver can be built as a module. The module will be called inv-icm45600-spi. + +config INV_ICM45600_I3C + tristate "InvenSense ICM-456xx I3C driver" + depends on I3C + select INV_ICM45600 + select REGMAP_I3C + help + This driver supports the InvenSense ICM-456xx motion tracking + devices over I3C. + Supported devices: + - ICM-45605 + - ICM-45606 + - ICM-45608 + - ICM-45634 + - ICM-45686 + - ICM-45687 + - ICM-45688-P + - ICM-45689 + + This driver can be built as a module. The module will be called + inv-icm45600-i3c. diff --git a/drivers/iio/imu/inv_icm45600/Makefile b/drivers/iio/imu/inv_ic= m45600/Makefile index 3692636d393a109a0ad68e955e1cad59005e9128..c98b8365b467de6abe9873e7ba3= c0aca77f464e3 100644 --- a/drivers/iio/imu/inv_icm45600/Makefile +++ b/drivers/iio/imu/inv_icm45600/Makefile @@ -11,3 +11,6 @@ inv-icm45600-i2c-y +=3D inv_icm45600_i2c.o =20 obj-$(CONFIG_INV_ICM45600_SPI) +=3D inv-icm45600-spi.o inv-icm45600-spi-y +=3D inv_icm45600_spi.o + +obj-$(CONFIG_INV_ICM45600_I3C) +=3D inv-icm45600-i3c.o +inv-icm45600-i3c-y +=3D inv_icm45600_i3c.o diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_i3c.c b/drivers/iio/= imu/inv_icm45600/inv_icm45600_i3c.c new file mode 100644 index 0000000000000000000000000000000000000000..e58de9c8a8b59682bec30d6de29= 3a1adda8618e6 --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_i3c.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Copyright (C) 2025 InvenSense, Inc. */ + +#include +#include +#include +#include +#include +#include + +#include "inv_icm45600.h" + +static const struct regmap_config inv_icm45600_regmap_config =3D { + .reg_bits =3D 8, + .val_bits =3D 8, +}; + +static const struct i3c_device_id inv_icm45600_i3c_ids[] =3D { + I3C_DEVICE_EXTRA_INFO(0x0235, 0x0000, 0x0011, (void *)NULL), + I3C_DEVICE_EXTRA_INFO(0x0235, 0x0000, 0x0084, (void *)NULL), + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i3c, inv_icm45600_i3c_ids); + +static const struct inv_icm45600_chip_info *i3c_chip_info[] =3D { + &inv_icm45605_chip_info, + &inv_icm45606_chip_info, + &inv_icm45608_chip_info, + &inv_icm45634_chip_info, + &inv_icm45686_chip_info, + &inv_icm45687_chip_info, + &inv_icm45688p_chip_info, + &inv_icm45689_chip_info, +}; + +static int inv_icm45600_i3c_probe(struct i3c_device *i3cdev) +{ + int ret; + unsigned int whoami; + struct regmap *regmap; + const int nb_chip =3D ARRAY_SIZE(i3c_chip_info); + int chip; + + regmap =3D devm_regmap_init_i3c(i3cdev, &inv_icm45600_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(&i3cdev->dev, PTR_ERR(regmap), + "Failed to register i3c regmap %ld\n", PTR_ERR(regmap)); + + ret =3D regmap_read(regmap, INV_ICM45600_REG_WHOAMI, &whoami); + if (ret) + return dev_err_probe(&i3cdev->dev, ret, "Failed to read part id %d\n", w= hoami); + + for (chip =3D 0; chip < nb_chip; chip++) { + if (whoami =3D=3D i3c_chip_info[chip]->whoami) + break; + } + + if (chip =3D=3D nb_chip) + dev_err_probe(&i3cdev->dev, -ENODEV, "Failed to match part id %d\n", who= ami); + + return inv_icm45600_core_probe(regmap, i3c_chip_info[chip], false, NULL); +} + +static struct i3c_driver inv_icm45600_driver =3D { + .driver =3D { + .name =3D "inv_icm45600_i3c", + .pm =3D pm_sleep_ptr(&inv_icm45600_pm_ops), + }, + .probe =3D inv_icm45600_i3c_probe, + .id_table =3D inv_icm45600_i3c_ids, +}; +module_i3c_driver(inv_icm45600_driver); + +MODULE_AUTHOR("Remi Buisson "); +MODULE_DESCRIPTION("InvenSense ICM-456xx i3c driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_ICM45600"); --=20 2.34.1 From nobody Sat Sep 13 22:31:04 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0B855276056; Wed, 20 Aug 2025 14:24:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755699886; cv=none; b=oBJKstOkjLnliY5yFEXkcM2IjaJz1z3BDX7bLW04JGq9G6PGCZR1p85mvD3WNxVJRgy0M7BpeG/2lw5A2wABsoC9SKbgGqGyXbom/3OzCQmoML8sI1a5aYwUoCSIRCs8HtBMyyUOy1aC/UUJoBo4ZMJOiHqqG1+wRBCwwCBjCn4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755699886; c=relaxed/simple; bh=DSLa9aPeM077q6gvw6PURI/pH8PombVAOR3qpaCktpg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=XbJDGJC/PSDy7pnclYBWxjSHnMqYjcW3mHbgfH9kF/Jw/uXUykELvyas1IrR5R92PEsM0V6bfaTflqWPefSuf6Ugz92IS8AIxMXAzJ5UX1ykiVXOgWGmYmzWsr1nkXfQGsc3N5SYkGsW9Fhqb52Q9ixLo9NpWms5MavkGuUUzgM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=e92CilXH; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="e92CilXH" Received: by smtp.kernel.org (Postfix) with ESMTPS id D795AC4CEE7; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1755699885; bh=DSLa9aPeM077q6gvw6PURI/pH8PombVAOR3qpaCktpg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=e92CilXH6toh7fbFiggE1hj8Fdl4chdqL5yNYouWGxX7RA/aiqrf24Us5SkBvTiBr 0rKYE87R5CYUbe9zcKp5G2vF0noeTQEi6vlz3cCzqd8FPAGwJ1y+a9GF7D/9QLkuzD MbeVb3T37vhuINd+q2/HF9PNLJKN2AHHPDeGNYXyyd7kRwBm0S4WXhBMVWa6RrMDKa 3vrr5YYdWie0wuMKn1iTKXL/7WQHjHw4JRgVqxMXlqBPdTZEKdtDidk1N9pnO/sA4i LbB5snMyCyd24oksQteNXfzI3sKAH7HSrbhy9qyW/Ejd3pxg9qOXnBhOLdwIThkfq7 rDseQs6DIK/aw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id CC3A5CA0EED; Wed, 20 Aug 2025 14:24:45 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Wed, 20 Aug 2025 14:24:27 +0000 Subject: [PATCH v5 9/9] MAINTAINERS: add entry for inv_icm45600 6-axis imu sensor Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250820-add_newport_driver-v5-9-2fc9f13dddee@tdk.com> References: <20250820-add_newport_driver-v5-0-2fc9f13dddee@tdk.com> In-Reply-To: <20250820-add_newport_driver-v5-0-2fc9f13dddee@tdk.com> To: Jonathan Cameron , David Lechner , =?utf-8?q?Nuno_S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, Remi Buisson X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1755699883; l=945; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=8YxNmTOrgJXwqprkXECUw3/UJ3Lll4HOZd/QYkpH/Pw=; b=HJaXGKCqmI2IHecPQQBsr53fRhNMexXrqMf5baE5x2/kZUh5ZtPdSaUAMHY2AmZBW/4HnFnCf lUaHT2EoktUD1G5vNW8L4bQs43LajAA6ppEhcdCu36tduZ2Jc5d1n+/ X-Developer-Key: i=remi.buisson@tdk.com; a=ed25519; pk=yDVMi4C7RpXN4dififo42A7fDDt3THYzoZoNq9lUZuo= X-Endpoint-Received: by B4 Relay for remi.buisson@tdk.com/20250411 with auth_id=372 X-Original-From: Remi Buisson Reply-To: remi.buisson@tdk.com From: Remi Buisson Add MAINTAINERS entry for InvenSense ICM-45600 IMU device. Signed-off-by: Remi Buisson --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index e3b0109a23045926d6a7e9659afdab0a6dbf7bed..c4aa2102ef398130074d20dd5b9= 367ce3fa51968 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12621,6 +12621,14 @@ F: Documentation/ABI/testing/sysfs-bus-iio-inv_icm= 42600 F: Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml F: drivers/iio/imu/inv_icm42600/ =20 +INVENSENSE ICM-456xx IMU DRIVER +M: Remi Buisson +L: linux-iio@vger.kernel.org +S: Maintained +W: https://invensense.tdk.com/ +F: Documentation/devicetree/bindings/iio/imu/invensense,icm45600.yaml +F: drivers/iio/imu/inv_icm45600/ + INVENSENSE MPU-3050 GYROSCOPE DRIVER M: Linus Walleij L: linux-iio@vger.kernel.org --=20 2.34.1