From nobody Sat Sep 13 22:29:47 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 D96BE2063E7; Thu, 17 Jul 2025 13:26:04 +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=1752758765; cv=none; b=qWXpE7tpawbupXotMXy2VSjXOCO1NNt5xsTbR4YFFTGLA7fXGwgTrEHvFibUMjGJNUHfwQJp+RCkJGiLSWG7HI0EQhoGW2m4/HeKcC5tQEYt6wmAhM8Do2R7Oh6CES2Wofu6I4oF1dhnZgZnBgW4W7lUFQlrpYryACKl2M3Cc8M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752758765; c=relaxed/simple; bh=Fl8H+oj0osT+K1fBWkJc81TgNpphdRz2K2nB4HUVi1M=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=j21snAmJaQNVZMd8LpIANLKkhEuUwiVN6OIt5k33+TyHl3Vj5dQ4DOl/tZ69EVAj4Raqs0o3ZuzWi1+wT25MWON8JkOoOKcfypGLLpjTRVBR0aM6UGH02iXi6lEDCraRBA8a9BK4TE0jtLNpt1UgxyUGHj8HjVMUrE5/MDaHprM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=IOOd0ljf; 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="IOOd0ljf" Received: by smtp.kernel.org (Postfix) with ESMTPS id 7BA3BC4CEED; Thu, 17 Jul 2025 13:26:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1752758764; bh=Fl8H+oj0osT+K1fBWkJc81TgNpphdRz2K2nB4HUVi1M=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=IOOd0ljfha87s3DlJpQWOad+rbw/nnPZxqIYJfzbzLh4oWaGuwJbx4IwOikZ25R08 AWLJ+wtGNMmoj0hPszG6SKZflIvWwl660r0+f7LG7WgQrwcjTKjuurFsjPeixb4efV DRsFHt1vEkHMVlpxWW2Q9Xb/jGrhBDHRBKVNQNtnWsoykinPIceQwum+L2wahLNzO1 lwDXahpMWKwZCiUtsbEBr6S8FmlQZ8aeo2tZElfwCaYifVSRI2vk6nM1ScOByCI901 CkHQfPR71Lbdx/uv5Wzo6bIlW4wS5J7mDGlGGdNCtCONX75BYcRPh5UitAWH7FL0rC fg90X5jbqHH3A== 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 6AA78C83F22; Thu, 17 Jul 2025 13:26:04 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Thu, 17 Jul 2025 13:25:53 +0000 Subject: [PATCH v3 1/8] 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: <20250717-add_newport_driver-v3-1-c6099e02c562@tdk.com> References: <20250717-add_newport_driver-v3-0-c6099e02c562@tdk.com> In-Reply-To: <20250717-add_newport_driver-v3-0-c6099e02c562@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=1752758762; l=3305; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=h1J88JN1g/YReQtGVeLTKGjC/sJtA2uL0J0HESlzSgg=; b=Y39qiNyNZToqLF5HZW+Y4m8BW6+vy6Z0GvTqUO3Kj7hyNY0y8cGGh12BltwrlOTfgciRzZaBB HOVA8hkUAZ9BrbRmmgCMPBMvZhfHdNJh9xp25cQ2iAwttfUttX4xmjf 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 --- .../bindings/iio/imu/invensense,icm45600.yaml | 97 ++++++++++++++++++= ++++ 1 file changed, 97 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..f43258124c32ebf850fc29b2e97= 643885e6f8480 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm45600.yaml @@ -0,0 +1,97 @@ +# 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 + maxItems: 2 + items: + enum: + - INT1 + - INT2 + description: Choose chip interrupt pin to be used as interrupt input. + + drive-open-drain: + type: boolean + + vdd-supply: + description: Regulator that provides power to the sensor + + vddio-supply: + description: Regulator that provides power to the bus + + mount-matrix: + description: an optional 3x3 mounting rotation matrix + +required: + - compatible + - reg + - interrupts + - interrupt-names + - 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:29:47 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 D97BA2E4995; Thu, 17 Jul 2025 13:26:04 +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=1752758765; cv=none; b=mswz6mBD6MiwklxgHlbi76WOjkURBgIb/d3GLeFgPVL8DDco9IzPgPM9zPN6JUM7Yw4j6IVJzkf665ISl0KXqo0+XIXRflUwEPfzudIizY/MmRbXCG2vhoePrd9E6qRCHceGJcC6jWnmVQI+PY0io/N+OC8/6dTmBigH+fNMApw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752758765; c=relaxed/simple; bh=INPS6Nkj94dIzoVVQ3Ct2XWtAkNojdhcJ7UYnniPmNY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ZtuqSYz7HmOnmN7OjOFqPCI2rpe6i0FNwESvBn2X0cvZonfXvyetD9TPxA4Ng5eh5Ex4E6kZpjiVz0ddDHzX24IuiEY4JC4D65aSI93SmJ3bjuwSKCCfFNf7/rzfhdjkeYYC+rgvbGwpZE+oGHzh78tEMYqHJ/56qjEl1GHYKqI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=e5YkCFRV; 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="e5YkCFRV" Received: by smtp.kernel.org (Postfix) with ESMTPS id 8BF3AC4CEEB; Thu, 17 Jul 2025 13:26:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1752758764; bh=INPS6Nkj94dIzoVVQ3Ct2XWtAkNojdhcJ7UYnniPmNY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=e5YkCFRVohulsghOBcEu3zo3EU1KAdECqI8c9SGZFN10Z1K+BDq97oPGX6yrWm9/0 usCu2TxfyN5SrFW8tIXWoI1hWz8n33Nn7iuUl+XAbSvvpUa+t30yY9CwbGsAkE4uGc nsr7R/sYW4kXyUSKikf9L+4eNjktP0rDb/qi8lFqS6grtqz4kNWRfi33u06dk19KpC QQ25XqhgNIMsXMysU/0VNQ+5p0hbUigheV54LvHdTYU+6mQjr/hxXDU/cPciiXNf6P XMblnWMZR5lKbv0URGvdA3m+cLLJjq8Kks52mabOwVl4ZRPJH/4LE97inSIla4W+cn gppsGUyS1G6IA== 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 7B8F9C83F1A; Thu, 17 Jul 2025 13:26:04 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Thu, 17 Jul 2025 13:25:54 +0000 Subject: [PATCH v3 2/8] 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: <20250717-add_newport_driver-v3-2-c6099e02c562@tdk.com> References: <20250717-add_newport_driver-v3-0-c6099e02c562@tdk.com> In-Reply-To: <20250717-add_newport_driver-v3-0-c6099e02c562@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=1752758762; l=37883; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=Q1W70p7ZqWe7NPMFFA6VbkM7UNn1W8EPiZ5p8llsnTw=; b=Yj3OCmuxDrm/xYdaod1Awvj3qvRfo5qTgZ4AVlZ47JgsKWT/uhqJuP/pPx/noRJTmh4aE/a1n 2yqvVxc31YJACh4wdcZ+kDBZpBn5KK/4gMDMo/XgIhZi9g/oxHMh7IS 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 | 363 +++++++++++ drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 737 +++++++++++++++++++= ++++ 6 files changed, 1111 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..59aed59b94ca2d4709b0c986dde= da80b33064f90 --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h @@ -0,0 +1,363 @@ +/* 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; + } buffer __aligned(IIO_DMA_MINALIGN); +}; + +/** + * struct inv_icm45600_sensor_state - sensor state variables + * @scales: table of scales. + * @scales_len: length (nb of items) of the scales table. + * @power_mode: sensor requested power mode (for common frequencies) + * @ts: timestamp module states. + */ +struct inv_icm45600_sensor_state { + const int *scales; + size_t scales_len; + enum inv_icm45600_sensor_mode power_mode; + struct inv_sensors_timestamp ts; +}; + +/* Virtual register addresses: @bank on MSB (16 bits), @address on LSB */ + +/* Indirect register access */ +#define INV_ICM45600_REG_IREG_ADDR 0x7C +#define INV_ICM45600_REG_IREG_DATA 0x7E + +/* Direct acces registers */ +#define INV_ICM45600_REG_MISC2 0x007F +#define INV_ICM45600_MISC2_SOFT_RESET BIT(1) + +#define INV_ICM45600_REG_DRIVE_CONFIG0 0x0032 +#define INV_ICM45600_DRIVE_CONFIG0_SPI_MASK GENMASK(3, 1) +#define INV_ICM45600_SPI_SLEW_RATE_0_5NS 6 +#define INV_ICM45600_SPI_SLEW_RATE_4NS 5 +#define INV_ICM45600_SPI_SLEW_RATE_5NS 4 +#define INV_ICM45600_SPI_SLEW_RATE_7NS 3 +#define INV_ICM45600_SPI_SLEW_RATE_10NS 2 +#define INV_ICM45600_SPI_SLEW_RATE_14NS 1 +#define INV_ICM45600_SPI_SLEW_RATE_38NS 0 + +#define INV_ICM45600_REG_INT1_CONFIG2 0x0018 +#define INV_ICM45600_INT1_CONFIG2_PUSH_PULL BIT(2) +#define INV_ICM45600_INT1_CONFIG2_LATCHED BIT(1) +#define INV_ICM45600_INT1_CONFIG2_ACTIVE_HIGH BIT(0) +#define INV_ICM45600_INT1_CONFIG2_ACTIVE_LOW 0x00 + +#define INV_ICM45600_REG_FIFO_CONFIG0 0x001D +#define INV_ICM45600_FIFO_CONFIG0_MODE_MASK GENMASK(7, 6) +#define INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS 0 +#define INV_ICM45600_FIFO_CONFIG0_MODE_STREAM 1 +#define INV_ICM45600_FIFO_CONFIG0_MODE_STOP_ON_FULL 2 +#define INV_ICM45600_FIFO_CONFIG0_FIFO_DEPTH_MAX 0x1F + +#define INV_ICM45600_REG_FIFO_CONFIG2 0x0020 +#define INV_ICM45600_REG_FIFO_CONFIG2_FIFO_FLUSH BIT(7) +#define INV_ICM45600_REG_FIFO_CONFIG2_WM_GT_TH BIT(3) + +#define INV_ICM45600_REG_FIFO_CONFIG3 0x0021 +#define INV_ICM45600_FIFO_CONFIG3_ES1_EN BIT(5) +#define INV_ICM45600_FIFO_CONFIG3_ES0_EN BIT(4) +#define INV_ICM45600_FIFO_CONFIG3_HIRES_EN BIT(3) +#define INV_ICM45600_FIFO_CONFIG3_GYRO_EN BIT(2) +#define INV_ICM45600_FIFO_CONFIG3_ACCEL_EN BIT(1) +#define INV_ICM45600_FIFO_CONFIG3_IF_EN BIT(0) + +#define INV_ICM45600_REG_FIFO_CONFIG4 0x0022 +#define INV_ICM45600_FIFO_CONFIG4_COMP_EN BIT(2) +#define INV_ICM45600_FIFO_CONFIG4_TMST_FSYNC_EN BIT(1) +#define INV_ICM45600_FIFO_CONFIG4_ES0_9B BIT(0) + +/* all sensor data are 16 bits (2 registers wide) in big-endian */ +#define INV_ICM45600_REG_TEMP_DATA 0x000C +#define INV_ICM45600_REG_ACCEL_DATA_X 0x0000 +#define INV_ICM45600_REG_ACCEL_DATA_Y 0x0002 +#define INV_ICM45600_REG_ACCEL_DATA_Z 0x0004 +#define INV_ICM45600_REG_GYRO_DATA_X 0x0006 +#define INV_ICM45600_REG_GYRO_DATA_Y 0x0008 +#define INV_ICM45600_REG_GYRO_DATA_Z 0x000A +#define INV_ICM45600_DATA_INVALID -32768 + +#define INV_ICM45600_REG_INT_STATUS 0x0019 +#define INV_ICM45600_INT_STATUS_RESET_DONE BIT(7) +#define INV_ICM45600_INT_STATUS_AUX1_AGC_RDY BIT(6) +#define INV_ICM45600_INT_STATUS_AP_AGC_RDY BIT(5) +#define INV_ICM45600_INT_STATUS_AP_FSYNC BIT(4) +#define INV_ICM45600_INT_STATUS_AUX1_DRDY BIT(3) +#define INV_ICM45600_INT_STATUS_DATA_RDY BIT(2) +#define INV_ICM45600_INT_STATUS_FIFO_THS BIT(1) +#define INV_ICM45600_INT_STATUS_FIFO_FULL BIT(0) + +/* + * FIFO access registers + * FIFO count is 16 bits (2 registers) + * FIFO data is a continuous read register to read FIFO content + */ +#define INV_ICM45600_REG_FIFO_COUNT 0x0012 +#define INV_ICM45600_REG_FIFO_DATA 0x0014 + +#define INV_ICM45600_REG_PWR_MGMT0 0x0010 +#define INV_ICM45600_PWR_MGMT0_GYRO_MODE_MASK GENMASK(3, 2) +#define INV_ICM45600_PWR_MGMT0_ACCEL_MODE_MASK GENMASK(1, 0) + +#define INV_ICM45600_REG_ACCEL_CONFIG0 0x001B +#define INV_ICM45600_ACCEL_CONFIG0_FS_MASK GENMASK(6, 4) +#define INV_ICM45600_ACCEL_CONFIG0_ODR_MASK GENMASK(3, 0) +#define INV_ICM45600_REG_GYRO_CONFIG0 0x001C +#define INV_ICM45600_GYRO_CONFIG0_FS_MASK GENMASK(7, 4) +#define INV_ICM45600_GYRO_CONFIG0_ODR_MASK GENMASK(3, 0) + +#define INV_ICM45600_REG_SMC_CONTROL_0 0xA258 +#define INV_ICM45600_SMC_CONTROL_0_ACCEL_LP_CLK_SEL BIT(4) +#define INV_ICM45600_SMC_CONTROL_0_TMST_EN BIT(0) + +/* FIFO watermark is 16 bits (2 registers wide) in little-endian */ +#define INV_ICM45600_REG_FIFO_WATERMARK 0x001E + +/* FIFO is configured for 8kb */ +#define INV_ICM45600_FIFO_SIZE_MAX (8 * 1024) + +#define INV_ICM45600_REG_INT1_CONFIG0 0x0016 +#define INV_ICM45600_INT1_CONFIG0_RESET_DONE_EN BIT(7) +#define INV_ICM45600_INT1_CONFIG0_AUX1_AGC_RDY_EN BIT(6) +#define INV_ICM45600_INT1_CONFIG0_AP_AGC_RDY_EN BIT(5) +#define INV_ICM45600_INT1_CONFIG0_AP_FSYNC_EN BIT(4) +#define INV_ICM45600_INT1_CONFIG0_AUX1_DRDY_EN BIT(3) +#define INV_ICM45600_INT1_CONFIG0_DRDY_EN BIT(2) +#define INV_ICM45600_INT1_CONFIG0_FIFO_THS_EN BIT(1) +#define INV_ICM45600_INT1_CONFIG0_FIFO_FULL_EN BIT(0) + +#define INV_ICM45600_REG_WHOAMI 0x0072 +#define INV_ICM45600_WHOAMI_ICM45605 0xE5 +#define INV_ICM45600_WHOAMI_ICM45686 0xE9 +#define INV_ICM45600_WHOAMI_ICM45688P 0xE7 +#define INV_ICM45600_WHOAMI_ICM45608 0x81 +#define INV_ICM45600_WHOAMI_ICM45634 0x82 +#define INV_ICM45600_WHOAMI_ICM45689 0x83 +#define INV_ICM45600_WHOAMI_ICM45606 0x84 +#define INV_ICM45600_WHOAMI_ICM45687 0x85 + +/* Gyro USER offset */ +#define INV_ICM45600_IPREG_SYS1_REG_42 0xA42A +#define INV_ICM45600_IPREG_SYS1_REG_56 0xA438 +#define INV_ICM45600_IPREG_SYS1_REG_70 0xA446 +#define INV_ICM45600_GYRO_OFFUSER_MASK GENMASK(13, 0) +/* Gyro Averaging filter */ +#define INV_ICM45600_IPREG_SYS1_REG_170 0xA4AA +#define INV_ICM45600_IPREG_SYS1_170_GYRO_LP_AVG_MASK GENMASK(4, 1) +#define INV_ICM45600_GYRO_LP_AVG_SEL_8X 5 +#define INV_ICM45600_GYRO_LP_AVG_SEL_2X 1 +/* Accel USER offset */ +#define INV_ICM45600_IPREG_SYS2_REG_24 0xA518 +#define INV_ICM45600_IPREG_SYS2_REG_32 0xA520 +#define INV_ICM45600_IPREG_SYS2_REG_40 0xA528 +#define INV_ICM45600_ACCEL_OFFUSER_MASK GENMASK(13, 0) +/* Accel averaging filter */ +#define INV_ICM45600_IPREG_SYS2_REG_129 0xA581 +#define INV_ICM45600_ACCEL_LP_AVG_SEL_1X 0x0000 +#define INV_ICM45600_ACCEL_LP_AVG_SEL_4X 0x0002 + +/* Sleep times required by the driver */ +#define INV_ICM45600_ACCEL_STARTUP_TIME_MS 60 +#define INV_ICM45600_GYRO_STARTUP_TIME_MS 60 +#define INV_ICM45600_GYRO_STOP_TIME_MS 150 +#define INV_ICM45600_IREG_DELAY_US 4 + +typedef int (*inv_icm45600_bus_setup)(struct inv_icm45600_state *); + +extern const struct dev_pm_ops inv_icm45600_pm_ops; + +const struct iio_mount_matrix * +inv_icm45600_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan); + +#define INV_ICM45600_TEMP_CHAN(_index) \ + { \ + .type =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); + +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_icm456= 00_chip_info *chip_info, + bool reset, inv_icm45600_bus_setup bus_setup); + +struct iio_dev *inv_icm45600_gyro_init(struct inv_icm45600_state *st); + +int inv_icm45600_gyro_parse_fifo(struct iio_dev *indio_dev); + +struct iio_dev *inv_icm45600_accel_init(struct inv_icm45600_state *st); + +int inv_icm45600_accel_parse_fifo(struct iio_dev *indio_dev); + +#endif diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio= /imu/inv_icm45600/inv_icm45600_core.c new file mode 100644 index 0000000000000000000000000000000000000000..b961f774e54d0ad109b4ed19eec= 1cd9b65803c96 --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c @@ -0,0 +1,737 @@ +// 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 +#include + +#include "inv_icm45600.h" + +static int inv_icm45600_ireg_read(struct regmap *map, unsigned int reg, + u8 *data, size_t count) +{ + int ret; + u8 addr[2]; + ssize_t i; + unsigned int d; + + addr[0] =3D FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg); + addr[1] =3D FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg); + + /* Burst write address. */ + ret =3D regmap_bulk_write(map, INV_ICM45600_REG_IREG_ADDR, addr, sizeof(a= ddr)); + /* 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) +{ + int ret; + u8 addr_data0[3]; + ssize_t i; + + addr_data0[0] =3D FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg); + addr_data0[1] =3D FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg); + addr_data0[2] =3D data[0]; + + /* Burst write address and first byte. */ + ret =3D regmap_bulk_write(map, INV_ICM45600_REG_IREG_ADDR, addr_data0, si= zeof(addr_data0)); + /* 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 u32 odr_periods[INV_ICM45600_ODR_MAX] =3D { + /* reserved values */ + 0, 0, 0, + /* 6.4kHz */ + 156250, + /* 3.2kHz */ + 312500, + /* 1.6kHz */ + 625000, + /* 800kHz */ + 1250000, + /* 400Hz */ + 2500000, + /* 200Hz */ + 5000000, + /* 100Hz */ + 10000000, + /* 50Hz */ + 20000000, + /* 25Hz */ + 40000000, + /* 12.5Hz */ + 80000000, + /* 6.25Hz */ + 160000000, + /* 3.125Hz */ + 320000000, + /* 1.5625Hz */ + 640000000, + }; + + return odr_periods[odr]; +} + +static int inv_icm45600_set_pwr_mgmt0(struct inv_icm45600_state *st, + enum inv_icm45600_sensor_mode gyro, + enum inv_icm45600_sensor_mode accel, + unsigned int *sleep_ms) +{ + enum inv_icm45600_sensor_mode oldgyro =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) { + if (sleepval < INV_ICM45600_ACCEL_STARTUP_TIME_MS) + sleepval =3D INV_ICM45600_ACCEL_STARTUP_TIME_MS; + } + if (gyro !=3D oldgyro) { + /* Gyro startup time. */ + if (oldgyro =3D=3D INV_ICM45600_SENSOR_MODE_OFF) { + if (sleepval < INV_ICM45600_GYRO_STARTUP_TIME_MS) + sleepval =3D INV_ICM45600_GYRO_STARTUP_TIME_MS; + /* Gyro stop time. */ + } else if (gyro =3D=3D INV_ICM45600_SENSOR_MODE_OFF) { + if (sleepval < INV_ICM45600_GYRO_STOP_TIME_MS) + sleepval =3D 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; +} + +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; + + /* 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; + + /* 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; + + /* 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; + + /* 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. */ + usleep_range(3000, 4000); + + return 0; +} + +static void inv_icm45600_disable_vddio_reg(void *_data) +{ + regulator_disable((struct regulator *) _data); +} + +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 irq, irq_type; + bool open_drain; + int ret; + + /* Get INT1 only supported interrupt. */ + fwnode =3D dev_fwnode(dev); + if (!fwnode) + return dev_err_probe(dev, -ENODEV, "Missing FW node\n"); + + irq =3D fwnode_irq_get_byname(fwnode, "INT1"); + if (irq < 0) { + if (irq !=3D -EPROBE_DEFER) + dev_err_probe(dev, irq, "Missing INT1 interrupt\n"); + return irq; + } + + 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)) + 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-= >vddio_supply); + if (ret) + return ret; + + /* Setup chip registers. */ + 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. + * Check first if runtime suspend has not already done the job. + */ +static int inv_icm45600_suspend(struct device *dev) +{ + struct inv_icm45600_state *st =3D dev_get_drvdata(dev); + int ret; + + guard(mutex)(&st->lock); + + st->suspended.gyro =3D st->conf.gyro.mode; + st->suspended.accel =3D st->conf.accel.mode; + if (pm_runtime_suspended(dev)) + return 0; + + 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; +} + +/* + * 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; + + guard(mutex)(&st->lock); + + if (pm_runtime_suspended(dev)) + return 0; + + ret =3D inv_icm45600_enable_regulator_vddio(st); + if (ret) + return ret; + + /* Restore sensors state. */ + return inv_icm45600_set_pwr_mgmt0(st, st->suspended.gyro, + st->suspended.accel, NULL); + +} + +/* Runtime suspend will turn off sensors that are enabled by iio devices. = */ +static int inv_icm45600_runtime_suspend(struct device *dev) +{ + struct inv_icm45600_state *st =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 { + SET_SYSTEM_SLEEP_PM_OPS(inv_icm45600_suspend, inv_icm45600_resume) + SET_RUNTIME_PM_OPS(inv_icm45600_runtime_suspend, + inv_icm45600_runtime_resume, NULL) +}; + +MODULE_AUTHOR("InvenSense, Inc."); +MODULE_DESCRIPTION("InvenSense ICM-456xx device driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_INV_SENSORS_TIMESTAMP"); --=20 2.34.1 From nobody Sat Sep 13 22:29:47 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 E3B7F2E49BE; Thu, 17 Jul 2025 13:26:04 +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=1752758765; cv=none; b=ZvKV8kGIeUKbnBoPkxwaFEWTTBofcn4UewyYYjBU65v1hA5RIbU4PZrfMI0CaHBNW5BHAtLZTg0qzJR7tU5UsOeuptj6xkbxKnmAOj85COB/lFKtD2qWceLseUxomt3hZRsYYWSLcpp6MWpxSgiu9Ct7yQ3Jgl6oT1nlW4Y2DQs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752758765; c=relaxed/simple; bh=kNLtdNjtsc/KXTZFfBMByGcDTmYIPqKr99aX5VW6RRc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=GdMw+Qpf25IYWLMDm9tWG7IdnOjiTmm7+uMARNSMPbtzM101reVwhmXT3/7fQFOIHJkYo9gGESbe4BYyaNhqdvH2KyBqKgeXH4559El5qgdqSV0so3r/qKd1fUr3qRCsINi2zjhQ3/QXpcpUN0Dp2sq1BgxeMdIxRe4o6qzLQxA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=sGMx7khO; 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="sGMx7khO" Received: by smtp.kernel.org (Postfix) with ESMTPS id 9A04DC4CEF5; Thu, 17 Jul 2025 13:26:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1752758764; bh=kNLtdNjtsc/KXTZFfBMByGcDTmYIPqKr99aX5VW6RRc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=sGMx7khOkTvp8mZsiQd6deINhcTKGO3kliN1wdOiNm0Z9WDJZH91ouc858StBlgkB i6uKMHl2xofRkVzu91ppThIsYTtEZ2a7jbVBuajQcHymHLJfG/Vhh258AD4/1CuGWY 1ly90Kx3f9g6tFj+28JRxoJXqmKas+08fZiByY9qCHm0j+Izfe1j2yHF3Qv2oOFB/z YtZueGj6OWp8L2TuTt7Eyhpx7jqNYShI/ea14CjqbbKDIzz+QEcojQSXCI2sXuU+nG v7uMiizN77pRJVS+9ICtdyatWq7AF44WaCXR1asE1UG/OFZemBc71WlDeAJcS60Jrf o0maWfB+QIrfA== 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 8AD6DC83F39; Thu, 17 Jul 2025 13:26:04 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Thu, 17 Jul 2025 13:25:55 +0000 Subject: [PATCH v3 3/8] 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: <20250717-add_newport_driver-v3-3-c6099e02c562@tdk.com> References: <20250717-add_newport_driver-v3-0-c6099e02c562@tdk.com> In-Reply-To: <20250717-add_newport_driver-v3-0-c6099e02c562@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=1752758762; l=27617; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=sjqHyfZ+Kzzb7Q79XXVNcxDsdSZGYH/XJsYPZCo5HrA=; b=OcJfFJ97O4x0Y8CVRpjRcE2Qqu7Dl+mqbhR8nrjnodv1F2d2etN/dJLsMhnBzTenSTEvK2oJE mzEjwWL5wLeC4HS67/8soxeRy33ECIdfqDR7bc6i2FSIMLdwpNR/wWA 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 | 514 +++++++++++++++++= ++++ drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h | 99 ++++ drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 137 +++++- 5 files changed, 757 insertions(+), 1 deletion(-) diff --git a/drivers/iio/imu/inv_icm45600/Makefile b/drivers/iio/imu/inv_ic= m45600/Makefile index 4f442b61896e91647c7947a044949792bae06a30..19c521ffba17b0d108a8ecb45ec= dea35dff6fd18 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 \ No newline at end of file diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600.h b/drivers/iio/imu/= inv_icm45600/inv_icm45600.h index 59aed59b94ca2d4709b0c986ddeda80b33064f90..5625c437b6f54336f6e652c2ae2= e4ea8f88e2396 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..b37607e52721097daf6362bac20= 001b0d4210f4b --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c @@ -0,0 +1,514 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Copyright (C) 2025 Invensense, Inc. */ + +#include +#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; +#define INV_ICM45600_FIFO_1SENSOR_PACKET_SIZE 8 + +struct inv_icm45600_fifo_2sensors_packet { + u8 header; + struct inv_icm45600_fifo_sensor_data accel; + struct inv_icm45600_fifo_sensor_data gyro; + s8 temp; + __le16 timestamp; +} __packed; +#define INV_ICM45600_FIFO_2SENSORS_PACKET_SIZE 16 + +ssize_t inv_icm45600_fifo_decode_packet(const void *packet, + const struct inv_icm45600_fifo_sensor_data **accel, + const struct inv_icm45600_fifo_sensor_data **gyro, + const s8 **temp, + const __le16 **timestamp, unsigned int *odr) +{ + const struct inv_icm45600_fifo_1sensor_packet *pack1 =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 INV_ICM45600_FIFO_2SENSORS_PACKET_SIZE; + } + + /* 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 INV_ICM45600_FIFO_1SENSOR_PACKET_SIZE; + } + + /* 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 INV_ICM45600_FIFO_1SENSOR_PACKET_SIZE; + } + + /* Invalid packet if here. */ + return -EINVAL; +} + +void inv_icm45600_buffer_update_fifo_period(struct inv_icm45600_state *st) +{ + u32 period_gyro, period_accel, period; + + if (st->fifo.en & INV_ICM45600_SENSOR_GYRO) + period_gyro =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; + + if (period_gyro <=3D period_accel) + period =3D period_gyro; + else + period =3D period_accel; + + st->fifo.period =3D period; +} + +int inv_icm45600_buffer_set_fifo_en(struct inv_icm45600_state *st, + unsigned int fifo_en) +{ + unsigned int mask, val; + int ret; + + /* Update only FIFO EN bits. */ + mask =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. */ + 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; + + if (watermark > watermark_max) + watermark =3D watermark_max; + + return watermark; +} + +/** + * inv_icm45600_buffer_update_watermark - update watermark FIFO threshold + * @st: driver internal state + * + * Returns 0 on success, a negative error code otherwise. + * + * FIFO watermark threshold is computed based on the required watermark 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 INV_ICM45600_FIFO_2SENSORS_PACKET_SIZE; + unsigned int wm_gyro, wm_accel, watermark; + u32 period_gyro, period_accel, period; + u32 latency_gyro, latency_accel, latency; + + /* Compute sensors latency, depending on sensor watermark and odr. */ + wm_gyro =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. */ + if (period_gyro <=3D period_accel) + period =3D period_gyro; + else + period =3D period_accel; + /* All this works because periods are multiple of each others. */ + watermark =3D latency / period; + if (watermark < 1) + watermark =3D 1; + /* Update effective watermark. */ + st->fifo.watermark.eff_gyro =3D latency / period_gyro; + if (st->fifo.watermark.eff_gyro < 1) + st->fifo.watermark.eff_gyro =3D 1; + st->fifo.watermark.eff_accel =3D latency / period_accel; + if (st->fifo.watermark.eff_accel < 1) + st->fifo.watermark.eff_accel =3D 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, + unsigned int max) +{ + const ssize_t packet_size =3D INV_ICM45600_FIFO_2SENSORS_PACKET_SIZE; + __le16 *raw_fifo_count; + size_t fifo_nb, i; + ssize_t size; + const struct inv_icm45600_fifo_sensor_data *accel, *gyro; + const __le16 *timestamp; + const s8 *temp; + unsigned int odr; + int ret; + + /* Reset all samples counters. */ + st->fifo.count =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; + if (max > 0 && fifo_nb > max) + fifo_nb =3D max; + + /* 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, &gyro, &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..09595a41cf637a3ba9bc44e4df5= 3a9d0aa11f485 --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2025 Invensense, Inc. */ + +#ifndef INV_ICM45600_BUFFER_H_ +#define INV_ICM45600_BUFFER_H_ + +#include +#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[8192] __aligned(IIO_DMA_MINALIGN); +}; + +/* FIFO data packet */ +struct inv_icm45600_fifo_sensor_data { + __le16 x; + __le16 y; + __le16 z; +} __packed; +#define INV_ICM45600_FIFO_DATA_INVALID -32768 + +static inline s16 inv_icm45600_fifo_get_sensor_data(__le16 d) +{ + return le16_to_cpu(d); +} + +static inline bool +inv_icm45600_fifo_is_data_valid(const struct inv_icm45600_fifo_sensor_data= *s) +{ + s16 x, y, z; + + x =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_FIFO_DATA_INVALID && + y =3D=3D INV_ICM45600_FIFO_DATA_INVALID && + z =3D=3D INV_ICM45600_FIFO_DATA_INVALID) + return false; + + return true; +} + +ssize_t inv_icm45600_fifo_decode_packet(const void *packet, + const struct inv_icm45600_fifo_sensor_data **accel, + const struct inv_icm45600_fifo_sensor_data **gyro, + const s8 **temp, + const __le16 **timestamp, unsigned int *odr); + +extern const struct iio_buffer_setup_ops inv_icm45600_buffer_ops; + +int inv_icm45600_buffer_init(struct inv_icm45600_state *st); + +void inv_icm45600_buffer_update_fifo_period(struct inv_icm45600_state *st); + +int inv_icm45600_buffer_set_fifo_en(struct inv_icm45600_state *st, + unsigned int fifo_en); + +int inv_icm45600_buffer_update_watermark(struct inv_icm45600_state *st); + +int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st, + unsigned int max); + +int inv_icm45600_buffer_hwfifo_flush(struct inv_icm45600_state *st, + unsigned int count); + +#endif diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio= /imu/inv_icm45600/inv_icm45600_core.c index b961f774e54d0ad109b4ed19eec1cd9b65803c96..0b85630610dcbae7890e69423e1= 878364ebc3f0d 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c @@ -17,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, @@ -530,6 +531,95 @@ 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, 0); + if (ret) { + dev_err(dev, "FIFO read error %d\n", ret); + return IRQ_HANDLED; + } + } + + /* FIFO full warning. */ + if (status & INV_ICM45600_INT_STATUS_FIFO_FULL) + dev_warn(dev, "FIFO full possible data lost!\n"); + + return IRQ_HANDLED; +} + +/** + * inv_icm45600_irq_init() - initialize int pin and interrupt handler + * @st: driver internal state + * @irq: irq number + * @irq_type: irq trigger type + * @open_drain: true if irq is open drain, false for push-pull + * + * Returns 0 on success, a negative error code otherwise. + */ +static int inv_icm45600_irq_init(struct inv_icm45600_state *st, int irq, + int irq_type, bool open_drain) +{ + struct device *dev =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; + + irq_type |=3D IRQF_ONESHOT; + return devm_request_threaded_irq(dev, irq, inv_icm45600_irq_timestamp, + inv_icm45600_irq_handler, irq_type, + "inv_icm45600", st); +} + static int inv_icm45600_timestamp_setup(struct inv_icm45600_state *st) { /* Enable timestamps. */ @@ -631,6 +721,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) @@ -662,6 +760,22 @@ static int inv_icm45600_suspend(struct device *dev) if (pm_runtime_suspended(dev)) return 0; =20 + /* Disable FIFO data streaming. */ + if (st->fifo.on) { + unsigned int val; + + 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; + } + ret =3D inv_icm45600_set_pwr_mgmt0(st, INV_ICM45600_SENSOR_MODE_OFF, INV_ICM45600_SENSOR_MODE_OFF, NULL); if (ret) @@ -679,6 +793,8 @@ static int inv_icm45600_suspend(struct device *dev) static int inv_icm45600_resume(struct device *dev) { struct inv_icm45600_state *st =3D dev_get_drvdata(dev); + 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); int ret =3D 0; =20 guard(mutex)(&st->lock); @@ -691,9 +807,28 @@ static int inv_icm45600_resume(struct device *dev) return ret; =20 /* Restore sensors state. */ - return inv_icm45600_set_pwr_mgmt0(st, st->suspended.gyro, + ret =3D inv_icm45600_set_pwr_mgmt0(st, st->suspended.gyro, st->suspended.accel, NULL); + if (ret) + return ret; =20 + /* Restore FIFO data streaming. */ + if (st->fifo.on) { + unsigned int val; + + inv_sensors_timestamp_reset(&gyro_st->ts); + inv_sensors_timestamp_reset(&accel_st->ts); + val =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; + ret =3D regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3, + INV_ICM45600_FIFO_CONFIG3_IF_EN); + } + + return ret; } =20 /* Runtime suspend will turn off sensors that are enabled by iio devices. = */ --=20 2.34.1 From nobody Sat Sep 13 22:29:47 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 0E38A2E5407; Thu, 17 Jul 2025 13:26:04 +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=1752758765; cv=none; b=f2dQIaRDBZ6lyu/ozuIpBxOlDYIJ++ZTU9dQQaAzAm/Cm/hlGk5tWoX165m4lJUounuxc8Cz/mS839JDzKppSd3vEoBF7SF52syCvWt/ogMSifPyvah/pRzFfnRtrRoHr1OlQaK/Ce5fQZVeSSolAupyN0s/9DRnl9EUvDd3NTs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752758765; c=relaxed/simple; bh=VJ3vhuaDiCOkbjiZF8BX8kE578P55dydhhPBj5ef8j0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=C+YCURYrsFu5fqIog4isdOhgAORzzwessUQYeeGR0g9nGJnlxRE6qwpx9uNEUscf4W3ZCM7s1xHpgAVY9MECBLsWsBwztMbbmTkmm1kJYCewLa8ONmZEMPNAjhY3H+TbkddHG5zp8wZo6fIw0R0GTXVOyTlVzl3n/kaRhAdHziM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=LtUGsQf4; 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="LtUGsQf4" Received: by smtp.kernel.org (Postfix) with ESMTPS id A7F96C4CEFC; Thu, 17 Jul 2025 13:26:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1752758764; bh=VJ3vhuaDiCOkbjiZF8BX8kE578P55dydhhPBj5ef8j0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=LtUGsQf49SWwlFwV/il0K1GuW1AilmZ3lQOSP22Bep9ruqQx7UWpwkhh9Y7bVysVc zPhBRBfkuK+Hn33MH7EzHv2+jwQFpK6mZ4WazCm9cN+l4k8ObMTcGNUIGDbiFbG7VS iezzaI0edT+KVHswQBTJRqCII0xtOHGBlslkM6KJc7z/DA3wB5ITZtqNTVvZdrXVwG aETIa7D8J5MJ5Zc6tNyOoW6pTgGXXBiNV3Hp6VNRG8GM+XAGgECfk/BLDfsJI+bd3F 9XdcwsPzB02O+CtECOoX+nCWLshzT4gbxDeU1MjIyR3kP6CVTzmOSad8RH8Cb5km6r 4EGSnTgqVQAUA== 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 9EC3BC83F22; Thu, 17 Jul 2025 13:26:04 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Thu, 17 Jul 2025 13:25:56 +0000 Subject: [PATCH v3 4/8] iio: imu: inv_icm45600: add IMU 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: <20250717-add_newport_driver-v3-4-c6099e02c562@tdk.com> References: <20250717-add_newport_driver-v3-0-c6099e02c562@tdk.com> In-Reply-To: <20250717-add_newport_driver-v3-0-c6099e02c562@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=1752758762; l=60200; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=rGxQuqZxVhm5H1jGZ1m9uyU4ozXVW6aVhIRN33gnRbM=; b=ajMIFB53gSzeXwstzlWwTQIFIy/xyHmmrV+n68LKJ0DT9eiSp7uTfxQ/yWUGMnco+gB2iXA5+ cWPxRkQy3wUCWT60uRYmSLQoHTZrYahgNceq3EIGXqgtiBl4IO/43pr 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 devices for accelerometer and gyroscope sensors with data polling interface and FIFO parsing. Attributes: raw, scale, sampling_frequency, calibbias. Temperature is available as a processed channel. Signed-off-by: Remi Buisson --- drivers/iio/imu/inv_icm45600/Kconfig | 2 + drivers/iio/imu/inv_icm45600/Makefile | 4 +- drivers/iio/imu/inv_icm45600/inv_icm45600.h | 9 + drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c | 798 +++++++++++++++++= +++ drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c | 71 ++ drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h | 2 + drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 122 ++++ drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c | 809 +++++++++++++++++= ++++ 8 files changed, 1816 insertions(+), 1 deletion(-) diff --git a/drivers/iio/imu/inv_icm45600/Kconfig b/drivers/iio/imu/inv_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 19c521ffba17b0d108a8ecb45ecdea35dff6fd18..ced5e1d9f2c10400cfa48781466= 72d41f815c22a 100644 --- a/drivers/iio/imu/inv_icm45600/Makefile +++ b/drivers/iio/imu/inv_icm45600/Makefile @@ -2,4 +2,6 @@ =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 \ No newline at end of file +inv-icm45600-y +=3D inv_icm45600_buffer.o +inv-icm45600-y +=3D inv_icm45600_accel.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 5625c437b6f54336f6e652c2ae2e4ea8f88e2396..cbc336867ddc65fcab3d738cbc4= bc266ada6ad1d 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600.h +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h @@ -109,6 +109,10 @@ struct inv_icm45600_chip_info { u8 whoami; const char *name; const struct inv_icm45600_conf *conf; + const int *accel_scales; + const int accel_scales_len; + const int *gyro_scales; + const int gyro_scales_len; }; =20 extern const struct inv_icm45600_chip_info inv_icm45605_chip_info; @@ -120,6 +124,11 @@ extern const struct inv_icm45600_chip_info inv_icm4568= 7_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]; + /** * struct inv_icm45600_state - driver state variables * @lock: lock for serializing multiple registers access. diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c b/drivers/ii= o/imu/inv_icm45600/inv_icm45600_accel.c new file mode 100644 index 0000000000000000000000000000000000000000..bd6b85e25e1792744769916f6d5= 29615f9acf723 --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c @@ -0,0 +1,798 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 Invensense, Inc. + */ + +#include +#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.5625Hz */ + 1, 562500, + /* 3.125Hz */ + 3, 125000, + /* 6.25Hz */ + 6, 250000, + /* 12.5Hz */ + 12, 500000, + /* 25Hz */ + 25, 0, + /* 50Hz */ + 50, 0, + /* 100Hz */ + 100, 0, + /* 200Hz */ + 200, 0, + /* 400Hz */ + 400, 0, + /* 800Hz */ + 800, 0, + /* 1.6kHz */ + 1600, 0, + /* 3.2kHz */ + 3200, 0, + /* 6.4kHz */ + 6400, 0, +}; + +static const int inv_icm45600_accel_odr_conv[] =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 */ + if (offset < -8192) + offset =3D -8192; + else if (offset > 8191) + offset =3D 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) + ret =3D st->fifo.nb.accel; + + return ret; +} + +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); + const char *name; + struct inv_icm45600_sensor_state *accel_st; + struct inv_sensors_timestamp_chip ts_chip; + struct iio_dev *indio_dev; + int ret; + + name =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; + const struct inv_icm45600_fifo_sensor_data *accel, *gyro; + const __le16 *timestamp; + const s8 *temp; + unsigned int odr; + s64 ts_val; + struct inv_icm45600_accel_buffer buffer; + + /* parse all fifo packets */ + for (i =3D 0, no =3D 0; i < st->fifo.count; i +=3D size, ++no) { + 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); + + /* buffer is copied to userspace, zeroing it to avoid any data leak */ + memset(&buffer, 0, sizeof(buffer)); + memcpy(&buffer.accel, accel, sizeof(buffer.accel)); + /* convert 8 bits FIFO temperature in high resolution format */ + buffer.temp =3D temp ? (*temp * 64) : 0; + ts_val =3D inv_sensors_timestamp_pop(ts); + iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val); + } + + return 0; +} diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c b/drivers/i= io/imu/inv_icm45600/inv_icm45600_buffer.c index b37607e52721097daf6362bac20001b0d4210f4b..2dcc13159091d2828ff374d40d8= da7b8df5e7b6e 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c @@ -481,6 +481,77 @@ int inv_icm45600_buffer_fifo_read(struct inv_icm45600_= state *st, return 0; } =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 09595a41cf637a3ba9bc44e4df53a9d0aa11f485..23a75fe19c483428f92cad1c7ef= 95cbcaa3c4d37 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h @@ -93,6 +93,8 @@ int inv_icm45600_buffer_update_watermark(struct inv_icm45= 600_state *st); int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st, unsigned int max); =20 +int inv_icm45600_buffer_fifo_parse(struct inv_icm45600_state *st); + int inv_icm45600_buffer_hwfifo_flush(struct inv_icm45600_state *st, unsigned int count); =20 diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio= /imu/inv_icm45600/inv_icm45600_core.c index 0b85630610dcbae7890e69423e1878364ebc3f0d..5aedd3bd2de2500f9b7e7df556d= 9fe8fb158d33b 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c @@ -153,6 +153,10 @@ 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, + .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, }; EXPORT_SYMBOL_NS_GPL(inv_icm45605_chip_info, "IIO_ICM45600"); =20 @@ -160,6 +164,10 @@ 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, + .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, }; EXPORT_SYMBOL_NS_GPL(inv_icm45606_chip_info, "IIO_ICM45600"); =20 @@ -167,6 +175,10 @@ 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, + .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, }; EXPORT_SYMBOL_NS_GPL(inv_icm45608_chip_info, "IIO_ICM45600"); =20 @@ -174,6 +186,10 @@ 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, + .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, }; EXPORT_SYMBOL_NS_GPL(inv_icm45634_chip_info, "IIO_ICM45600"); =20 @@ -181,6 +197,10 @@ 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, + .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, }; EXPORT_SYMBOL_NS_GPL(inv_icm45686_chip_info, "IIO_ICM45600"); =20 @@ -188,6 +208,10 @@ 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, + .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, }; EXPORT_SYMBOL_NS_GPL(inv_icm45687_chip_info, "IIO_ICM45600"); =20 @@ -195,6 +219,10 @@ 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, }; EXPORT_SYMBOL_NS_GPL(inv_icm45688p_chip_info, "IIO_ICM45600"); =20 @@ -202,6 +230,10 @@ 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, + .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, }; EXPORT_SYMBOL_NS_GPL(inv_icm45689_chip_info, "IIO_ICM45600"); =20 @@ -562,6 +594,9 @@ static irqreturn_t inv_icm45600_irq_handler(int irq, vo= id *_data) 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. */ @@ -725,6 +760,14 @@ 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); + + 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; @@ -860,6 +903,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 { SET_SYSTEM_SLEEP_PM_OPS(inv_icm45600_suspend, inv_icm45600_resume) SET_RUNTIME_PM_OPS(inv_icm45600_runtime_suspend, diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c b/drivers/iio= /imu/inv_icm45600/inv_icm45600_gyro.c new file mode 100644 index 0000000000000000000000000000000000000000..48373befafa0efe0e5dcb2c97b2= c836436ce7d78 --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c @@ -0,0 +1,809 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 Invensense, Inc. + */ + +#include +#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.5625Hz */ + 1, 562500, + /* 3.125Hz */ + 3, 125000, + /* 6.25Hz */ + 6, 250000, + /* 12.5Hz */ + 12, 500000, + /* 25Hz */ + 25, 0, + /* 50Hz */ + 50, 0, + /* 100Hz */ + 100, 0, + /* 200Hz */ + 200, 0, + /* 400Hz */ + 400, 0, + /* 800Hz */ + 800, 0, + /* 1.6kHz */ + 1600, 0, + /* 3.2kHz */ + 3200, 0, + /* 6.4kHz */ + 6400, 0, +}; + +static const int inv_icm45600_gyro_odr_conv[] =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 */ + if (offset < -8192) + offset =3D -8192; + else if (offset > 8191) + offset =3D 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) + ret =3D st->fifo.nb.gyro; + + return ret; +} + +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); + const char *name; + struct inv_icm45600_sensor_state *gyro_st; + struct inv_sensors_timestamp_chip ts_chip; + struct iio_dev *indio_dev; + int ret; + + name =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; + const struct inv_icm45600_fifo_sensor_data *accel, *gyro; + const __le16 *timestamp; + const s8 *temp; + unsigned int odr; + s64 ts_val; + struct inv_icm45600_gyro_buffer buffer; + + /* parse all fifo packets */ + for (i =3D 0, no =3D 0; i < st->fifo.count; i +=3D size, ++no) { + 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); + + /* buffer is copied to userspace, zeroing it to avoid any data leak */ + memset(&buffer, 0, sizeof(buffer)); + memcpy(&buffer.gyro, gyro, sizeof(buffer.gyro)); + /* convert 8 bits FIFO temperature in high resolution format */ + buffer.temp =3D temp ? (*temp * 64) : 0; + ts_val =3D inv_sensors_timestamp_pop(ts); + iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val); + } + + return 0; +} --=20 2.34.1 From nobody Sat Sep 13 22:29:47 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 531102E54AC; Thu, 17 Jul 2025 13:26:05 +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=1752758765; cv=none; b=HsN5uvmmPlUpHT9RtPlrVV5jEbN1XWyAapINo2WQbli4FpN0MaiRO6V+Z/cziwfM1M398r/+BZJKUFaWIp8hLJUxJCX04UWvuTSybh+h6UdVw4DfGFouSAjAjkJOh06fWhbG9ozw6BjnzeQrZ84f9cfuq7eHhgtPhoRgUfiCRwE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752758765; c=relaxed/simple; bh=OhptN+7B1XE2fgRUs9oHodmN33ZP4J2jyblze+ymoPg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=XCib019355IVCXtoUxy+5EUoGdwQCgHuWCw9cX1MmerAU7LY0T1T0WW3IzSfGkSE0+O1KV/IENsAdffjf4kBMk4Qe7WnsIsXAHqikq045REHdrlyd5esVJsL1U2aRKSH4xRSMvrWkPcs+PNwogQa2ezkl4pKLXHW5s2SMzVEFN4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=klcue6La; 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="klcue6La" Received: by smtp.kernel.org (Postfix) with ESMTPS id B919DC4CEFA; Thu, 17 Jul 2025 13:26:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1752758764; bh=OhptN+7B1XE2fgRUs9oHodmN33ZP4J2jyblze+ymoPg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=klcue6LaimfZ2j2dYug8Z0VzbjMgBgafmbzL2RqwmFVTpub/HrNNMkc3KSBl/SBjI iQ29+GyonMYuM/+JIL70wgQ6xlTzdgC/pQluxioanNM5Mt7Cjc+KniJVgW3LyDn7dW g5XbX87DvFN5PnoFYHmA1mj3yHPp9AhhvtM8kCHDFaduJBbwQ8IBL1Wn2yn4RTG91B q2MoiyNhnnkrdrlUfeInQrIPwMvmEklgLs/DWNzI06f8yi8FfWNGw+Gly+LMHWl9kL 3O6uVLP1+oRqldBk2Tmtbsrva9Ml21+WH+fktr07nV/BU7vhSfoFW9j61viEN/P0ud niP6d4ks5BwAg== 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 AF8E7C83F34; Thu, 17 Jul 2025 13:26:04 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Thu, 17 Jul 2025 13:25:57 +0000 Subject: [PATCH v3 5/8] 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: <20250717-add_newport_driver-v3-5-c6099e02c562@tdk.com> References: <20250717-add_newport_driver-v3-0-c6099e02c562@tdk.com> In-Reply-To: <20250717-add_newport_driver-v3-0-c6099e02c562@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=1752758762; l=5081; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=rm9K6Dx3nzkqESFb6IZ1u3aaGk1Q2lyM/F/KVpz+xTs=; b=+a3Ou+Jfrorb2h9rGGESippgEJBcFRzwdOTBI1/g8/9OL7VKDqGQ/02zNlyUI5mB1gnendQCX +hov9BJt4xvBEZ5Kt2BxMRhttAhPL4HmEDdrHh/c+cLV9xcwN4tmmdv 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 ced5e1d9f2c10400cfa4878146672d41f815c22a..371585e29e92e78a30c7d40c138= 9548fa22a999a 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_accel.o inv-icm45600-y +=3D inv_icm45600_gyro.o + +obj-$(CONFIG_INV_ICM45600_I2C) +=3D inv-icm45600-i2c.o +inv-icm45600-i2c-y +=3D inv_icm45600_i2c.o \ No newline at end of file diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c b/drivers/iio/= imu/inv_icm45600/inv_icm45600_i2c.c new file mode 100644 index 0000000000000000000000000000000000000000..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:29:47 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 541592E54AD; Thu, 17 Jul 2025 13:26:05 +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=1752758765; cv=none; b=OQQFUNsASSUqCgQQZtPhXB61+cPS3aM28RmH5S9PVOOI2hnL9YKYJ5hYCssyN4FXWE0rYLMMgqdka41NFZxf7jpyoI+ml/Qzv919hBHJiuzTfxHe/P2C6eo7JsM8+xD9cP4n8Ucumtoo8BLS53ZpXeB4/FzysmI4I4zQCQmYr9w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752758765; c=relaxed/simple; bh=c2IX8PPIIZJzerT/aw+ykeihrxVRi5Gp/7XdkvR+5kY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=H4RKxbffXvyaY3OddOZRpVS91Txgz9kosiF3lcxcTbb7AFxyPAKrOf2Qq97XwO+J1bmUeiSYP6UMlL4gA1TaaGTlc8dKmyauLsnWeS3TI7sRFG5ikwFWofT5zRCGkwuJQfmCz2cLFSWI2NRYv0wGTNYtob+RpgWgMyc8/NVCOdk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=JMWfkXWN; 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="JMWfkXWN" Received: by smtp.kernel.org (Postfix) with ESMTPS id C62CDC113D0; Thu, 17 Jul 2025 13:26:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1752758764; bh=c2IX8PPIIZJzerT/aw+ykeihrxVRi5Gp/7XdkvR+5kY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=JMWfkXWNI1bZD34Wj9G6fn6d4vUkCJirllS3rCwf+eppBISWZsmohDLQRP/QKfR0D gBW1lQvkDcA0wZYEKJN1oiR4L/WJu2PU9UJCNFLy2WUCZm+11vFiX00C4CGcNfJQBn 3fQ6i2GqnIB1tbmzAU2zAre8Pmq+9cXYnk5ttcxofiFoIU+H3WadqmA1PXR8cnuLKT 5/UhHPefK2RYhgWtTobG7Qee3H4Vyc8slu3R7JL5SVnyHG9pqphLKsuHzcVBMiktJx xhzxPhL5QL51ZCkr4FjxGGX4oNpjvKNfvs3HD+GX6axLI5rab7exdMRhb8IpKXWGyY x6YIfpL35uJsw== 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 BCA1DC83F1A; Thu, 17 Jul 2025 13:26:04 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Thu, 17 Jul 2025 13:25:58 +0000 Subject: [PATCH v3 6/8] 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: <20250717-add_newport_driver-v3-6-c6099e02c562@tdk.com> References: <20250717-add_newport_driver-v3-0-c6099e02c562@tdk.com> In-Reply-To: <20250717-add_newport_driver-v3-0-c6099e02c562@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=1752758762; l=5474; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=hnED/tFv/crwWZzbfKEjfj7vI38mqj6A3J7aBo72i3g=; b=5xwdtZLnl2BughHOJdnesAXNVMNT1D9ueZrfoBiRs9sdWVLxXXJ9W4wa1pt8LgcydiWy9mHjQ Ni0DoBBW21/C/bst1gmoBa/Q40kIKKAecJXZyNF6VWe7k7dabXKcGCO 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 | 5 +- drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c | 106 ++++++++++++++++++++= ++++ 3 files changed, 131 insertions(+), 1 deletion(-) diff --git a/drivers/iio/imu/inv_icm45600/Kconfig b/drivers/iio/imu/inv_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 371585e29e92e78a30c7d40c1389548fa22a999a..244cc8aa1f79e3367ac6925504c= fd9d5b918a0e6 100644 --- a/drivers/iio/imu/inv_icm45600/Makefile +++ b/drivers/iio/imu/inv_icm45600/Makefile @@ -7,4 +7,7 @@ inv-icm45600-y +=3D inv_icm45600_accel.o inv-icm45600-y +=3D inv_icm45600_gyro.o =20 obj-$(CONFIG_INV_ICM45600_I2C) +=3D inv-icm45600-i2c.o -inv-icm45600-i2c-y +=3D inv_icm45600_i2c.o \ No newline at end of file +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:29:47 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 6FC7E2E54C9; Thu, 17 Jul 2025 13:26:05 +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=1752758765; cv=none; b=a8pDro44Mz13+JpzJklgStP4lDvZIBpPsTvnrI2kRUnpezPuXjsh0zqCyLke7w4rswZfqDVhuPAZk+e4NyiKUv1g1gmz1yrOCm5HqYINMnhTMPoD7h8err55BoBa/W8OsxDAiLulotou5f+hddXgn1Vvya4UuUHmmfoOIY73tu4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752758765; c=relaxed/simple; bh=vsHjZJf51AfU8S+DT+3gZS1r3jeZjtw/LCAGqXyQnIc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=d0baaukHvnHMWCYCS6Kc8OytwS/fcDYQkf521L4XVXZNVV1ai05XqbigeMuTjrmWQtV/1wY+QkghPBCT9OR+91pxf5kxICGdOrByUc5BJE6TYSw/cci3jjEzW1DQislRzdxYp3PolxY/B6ljwutUVsaO6o7bnO153k9gz6a83GM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=b/ftGr9+; 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="b/ftGr9+" Received: by smtp.kernel.org (Postfix) with ESMTPS id D1BF7C116B1; Thu, 17 Jul 2025 13:26:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1752758764; bh=vsHjZJf51AfU8S+DT+3gZS1r3jeZjtw/LCAGqXyQnIc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=b/ftGr9+//vMPom69fmihuyNTohkLbo0qHkRCdUgP4A2vZh/WKwZLPnzusWaF8zZa az99xG6E293nhpQsaT7qG0R5B8YoHN4P7tfH5k7GviD7s/ePOCc4qAkIltLsPG02f6 8L3s489TR0inazJuuc/iauXKOIYArkueiVjPiTy0uRf/z0jOs3kkXM05+3tJyLwPj7 t6OfT/7DlnqDQEUUm6MjugpfrDDa3ZfHHKj/hJu09u3ysbODym14XtjtYu/P8NUbDd JLr/giH2xjYnEWF06bBDIg1TTT/9VVwklqRBlfnTfYYWXh3eg7eMp8kfyVkcE4qr70 1usPFWw1wBePw== 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 C9A73C83F38; Thu, 17 Jul 2025 13:26:04 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Thu, 17 Jul 2025 13:25:59 +0000 Subject: [PATCH v3 7/8] 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: <20250717-add_newport_driver-v3-7-c6099e02c562@tdk.com> References: <20250717-add_newport_driver-v3-0-c6099e02c562@tdk.com> In-Reply-To: <20250717-add_newport_driver-v3-0-c6099e02c562@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=1752758762; l=4456; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=4m9qBpGi25U3Cwi+7Mah1nGcPeoIUiDCyFwUUL17ZWI=; b=0n0+HPE0K0r/Lj3Rk/G0PjJxaRH0TbP9AjBGhxovMXQmkZiIpMuyGLwWiidFIhy+IEyi69tic xSjUWTDZMjKDjBwORu4hCwB7gMyrzSN1EnyOOOeK7CKjsjVhIhilm2r 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 | 82 +++++++++++++++++++++= ++++ 3 files changed, 106 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 244cc8aa1f79e3367ac6925504cfd9d5b918a0e6..fa646ea8339da3c41eab8aa16e1= 4b484fd884843 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..9db249ca53ec3fecb0f85792a35= 3d05463f52acb --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_i3c.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Copyright (C) 2025 InvenSense, Inc. */ + +#include +#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)) { + dev_err(&i3cdev->dev, "Failed to register i3c regmap %ld\n", PTR_ERR(reg= map)); + return PTR_ERR(regmap); + } + + ret =3D regmap_read(regmap, INV_ICM45600_REG_WHOAMI, &whoami); + if (ret) { + dev_err(&i3cdev->dev, "Failed to read part id %d\n", whoami); + return ret; + } + + for (chip =3D 0; chip < nb_chip; chip++) { + if (whoami =3D=3D i3c_chip_info[chip]->whoami) + break; + } + + if (chip =3D=3D nb_chip) { + dev_err(&i3cdev->dev, "Failed to match part id %d\n", whoami); + return -ENODEV; + } + + return inv_icm45600_core_probe(regmap, i3c_chip_info[chip], false, NULL); +} + +static struct i3c_driver inv_icm45600_driver =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:29:47 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 653212E54BF; Thu, 17 Jul 2025 13:26:05 +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=1752758765; cv=none; b=bC6SzK9IiyeuuVIHtyQRWEXzrgq+8/0WwwKlYuWGf1hdEJSASm9bygsfPqk8B2c+2Ow0z6lW9hkoYAgbPuDLmv6WPV3Fr8VWqMyjLd0AwW69WDpe1+mXejkMkPB5gNzuvNL5JRWW9MfVCUjaiwwapJVEEvaR62hI3aJ7FhEQe5I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752758765; c=relaxed/simple; bh=DSLa9aPeM077q6gvw6PURI/pH8PombVAOR3qpaCktpg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ZQ+DLt8SCV6Ua8DqieG0d5X5OjgMYVWH4mUZQvxsTiw0SzsQBUq9ViRETo9shJQ3H9/Jsrr5I1NZILS57C/S5mccaqdyue0al3mfNir9QpNQv5qcfFwjVYfD8n49hrMImd9i+2LecKYLdEwdLXY5J7qdsyvK686Fs8vGJRYa1sQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=KIxM8KGw; 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="KIxM8KGw" Received: by smtp.kernel.org (Postfix) with ESMTPS id DF96CC16AAE; Thu, 17 Jul 2025 13:26:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1752758764; bh=DSLa9aPeM077q6gvw6PURI/pH8PombVAOR3qpaCktpg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=KIxM8KGw/o3vWJ9ANjxrG4XGzJuQH4lvFyhbF6I+QU+uZwvA650isFQrmBA1KHkA5 fV4IuB9YAvsSQJh9mP00VjNch6RtMlodRxwpLsB+fpUEjX2kixzM35QlT6cwJtkt6o 78m8K46Ib/9KFuRaGjaT3P0RORzLzksSN4XRH3LS/cJuLgSQHMahtXXF7dRNLJy3RL Ejjkoi3Pa60wkfniTI3wCSrUXrv6DWoVtv77dTJQEtWPNlKmrX+sCAM4wWAfntHFJ/ BqQxzRRSUI4Y6WpX8vVbDPcRjFdNQJZXqIOrtLT57M1Y8TIUUx4h7yZzPJ0p7duKKg ZYjaoSI4oaKOA== 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 D72CBC83F3B; Thu, 17 Jul 2025 13:26:04 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Thu, 17 Jul 2025 13:26:00 +0000 Subject: [PATCH v3 8/8] 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: <20250717-add_newport_driver-v3-8-c6099e02c562@tdk.com> References: <20250717-add_newport_driver-v3-0-c6099e02c562@tdk.com> In-Reply-To: <20250717-add_newport_driver-v3-0-c6099e02c562@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=1752758762; l=945; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=8YxNmTOrgJXwqprkXECUw3/UJ3Lll4HOZd/QYkpH/Pw=; b=CRISjZffD+SMhq03XiO/aPRwjozD/Iq3lIPbjJZ7AFMF16jdUTKaZhGQIW3Pr+EfbdOc5DKtJ aZYj2vp/2YtCuYmDqZvNODY9OgEl9hNTt0+fjTVo2H6ctP5NuqHLsrR 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