From nobody Tue Oct 7 11:56:55 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 2DB6E28D8D2; Thu, 10 Jul 2025 08:58:02 +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=1752137883; cv=none; b=mZP/DrZbFy0ZNPUIsmd+shZOnCDixPERZsWItEceD+IYYM1f3Agg37mRFSxyk/4YkAg9nlXs0hs9E0gvUSLzny35o+XDQQAI0BffZ2kLw0SaUbKaFi1bSg5Oxutpv8rGXftbYOY9+1dpvCpQep7ISYf375XYIyvTpohncrFaw7A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752137883; c=relaxed/simple; bh=6THoRxVWmeyUkb9eFDl9Nx1C4CZk72tZZgVUuoEYz3s=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Js6YgJ4K1rIY4vas0NUC3bCThbo4OA5gITa8WJpEqgFzQkjyIy5Z+Dqe0ep9KHzuHEpTgAEMS93VEBRP35wBdXF5lpGYh1g4BS7jGxi9/Zj/KS9ZKK9IvPbrA08Gd8w/c7wrHap88FNQCK0RSxY4M/v9bvzE5U9OBI/4n0pO4Hs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=jqueoUGv; 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="jqueoUGv" Received: by smtp.kernel.org (Postfix) with ESMTPS id C5CFDC4CEF5; Thu, 10 Jul 2025 08:58:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1752137882; bh=6THoRxVWmeyUkb9eFDl9Nx1C4CZk72tZZgVUuoEYz3s=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=jqueoUGvmw0ednAfg3Iy4kYyIDKz19PSVxbL7wFycyh03jIA3u+Qw1+gaYxwQ3+IJ N9Ut0FpX8iOI8u6P+VjA1cCJWngC08icHohuV8NVcDvIHPlZOwQxP0VR9yos+2hNxR 9b/DVmFq2uvUVxp7PLW6VLBvMhcVoUH/iZwgsugQu/Eki8kB2Tq9Jl0yu64mGN6XHA 61rckd0qaVYUThGKULXoFXIlSNEnrkGTP6BkPK6CPlupfuNXmknw471l2UnD/jCrJ5 SrUOVxoWizyKANZBtBVnn3BZ6XTIz7TtMaU/4WmfTp5IkKjq6ru/RWfc7U910tjCOJ dV3dQWIZqa1NQ== 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 B33E8C83F1A; Thu, 10 Jul 2025 08:58:02 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Thu, 10 Jul 2025 08:57:56 +0000 Subject: [PATCH v2 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: <20250710-add_newport_driver-v2-1-bf76d8142ef2@tdk.com> References: <20250710-add_newport_driver-v2-0-bf76d8142ef2@tdk.com> In-Reply-To: <20250710-add_newport_driver-v2-0-bf76d8142ef2@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=1752137881; l=4651; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=kn/sMAV7JFKvYcZy6R2/MG9kA2WFXmd+bAq1lHYc/Ps=; b=/6GH3zv6lQ1Rz8XZVzljWteqaaszyMSNlHe45SJQawXU7Y9mQhS74Z9cV7ICTAjyuZ7MQrf07 OEJrUfem798Bj/MlnwKr7nN59tHMMd0XDWJT8bpEhUaqMql2/GPrCiB 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 | 138 +++++++++++++++++= ++++ 1 file changed, 138 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..a651878791ffae8d1c8d6c8ff1e= 4becfc56af79f --- /dev/null +++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm45600.yaml @@ -0,0 +1,138 @@ +# 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 + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + i2c { + #address-cells =3D <1>; + #size-cells =3D <0>; + + icm45605@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"; + }; + }; + - | + #include + #include + spi { + #address-cells =3D <1>; + #size-cells =3D <0>; + + icm45605@0 { + compatible =3D "invensense,icm45605"; + reg =3D <0>; + spi-max-frequency =3D <24000000>; + interrupt-parent =3D <&gpio1>; + interrupt-names =3D "INT1"; + interrupts =3D <6 IRQ_TYPE_EDGE_RISING>; + vdd-supply =3D <&vdd>; + vddio-supply =3D <&vddio>; + mount-matrix =3D "0", "-1", "0", + "1", "0", "0", + "0", "0", "1"; + }; + }; + - | + #include + #include + i3c { + #address-cells =3D <3>; + #size-cells =3D <0>; + + icm45606@68,46A00000084 { + reg =3D <0x68 0x46A 0x84>; + interrupt-parent =3D <&gpio1>; + interrupt-names =3D "INT1"; + interrupts =3D <5 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 Tue Oct 7 11:56:55 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 50D3A28E5F3; Thu, 10 Jul 2025 08:58:03 +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=1752137883; cv=none; b=r1E87XRGkUrCuUE1HgeNM/ftRXKeotFeJbIWc/N08sVtKrQafxqGHtl/smbkbciUSf4i47XHdtjbzLKcb8E46kzU7n4UHcdFL9YAOuJznEjsvEC261d8X+/enVUs16TPyCDKKjPcUY/HA04aWPUEPnBWGPnA+hN4ICf2EhYInKs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752137883; c=relaxed/simple; bh=bgt1Q9FA9YIsIDoPB82of6sx9RLU9QW7PNhBlhy8Oj4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=JXd3O9DG9Jh7uRkV95objC8B40/F6VdoioDlJifMPq4RctwOArliAOeGy469xlVlACLtf+7tLfB3mH8IOY+dkYyhv7QxIbhNy2Y4e3y8Th505kfyWfmL5k3xWY9BucfSBFU/d3JrkhfN5AiHnofA9LEJHTLcF9HH6H1aJwxoNq4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=flVo+cBB; 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="flVo+cBB" Received: by smtp.kernel.org (Postfix) with ESMTPS id D5992C4CEF6; Thu, 10 Jul 2025 08:58:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1752137882; bh=bgt1Q9FA9YIsIDoPB82of6sx9RLU9QW7PNhBlhy8Oj4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=flVo+cBBKokIWmrlg3wZSrb1WW+iXXpxyNjCIceWmWRhi1+cti7zaepkZeSyelWNE +tFz7KtL4jSbMtTxqzg9lymlVqxhyQ7FT4vQdwTyrIDGHRDVhJPf8eWYEJDELvufU9 3dw/MCzpZ17zI1M2mWSYvTeveCV3NW32jWph/bBKiHQLXGzYEEbI6nhQbjpvudBzbJ rNJ5+fBLTz6qg9950VOVSFyi20YEUCsaxuBzmhtKR/2eSS2r6/64dDZZCIpjdC6H4l ca+1nj6dcamNF8/1nHNcP3bhxuCicIQSeu+qvpfeXw5RZl9YLi4gjdLmvnqRgVSPQD 5kC4Om9h3iHoA== 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 C5AAFC83F18; Thu, 10 Jul 2025 08:58:02 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Thu, 10 Jul 2025 08:57:57 +0000 Subject: [PATCH v2 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: <20250710-add_newport_driver-v2-2-bf76d8142ef2@tdk.com> References: <20250710-add_newport_driver-v2-0-bf76d8142ef2@tdk.com> In-Reply-To: <20250710-add_newport_driver-v2-0-bf76d8142ef2@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=1752137881; l=37582; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=JsZQHFMOyCyHPJkqJFRNLv95TPUEEyAOc8OomkWcX5k=; b=LivJDHECkJsKu5LBWx7su61navxTAcZvQigyqHuVWbHhXNspzREUxYpHfweKF+R+mmmyyZRgL /mRMDcBNV7QCKTdPoIIBFfKtqUiwr0MEeSuY3T9m9TvHylmzI9p2Wsd 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 | 364 +++++++++++ drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 734 +++++++++++++++++++= ++++ 6 files changed, 1109 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..d56de75ab7f2168f22e25b5816c= b361bef457c0d --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h @@ -0,0 +1,364 @@ +/* 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_NB +}; + +/* 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_NB +}; + +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_NB +}; + +/* 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_NB +}; + +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_NB +}; + +/* 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_NB +}; + +struct inv_icm45600_sensor_conf { + int mode; + int fs; + int odr; + int filter; +}; +#define INV_ICM45600_SENSOR_CONF_INIT {-1, -1, -1, -1} + +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..022eb9180b5a750e8fba4a1cd87= 3041c27f59880 --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c @@ -0,0 +1,734 @@ +// 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 "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, 2); + udelay(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); + data[i] =3D d; + udelay(INV_ICM45600_IREG_DELAY_US); + if (ret) + return ret; + } + + 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, 3); + udelay(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]); + udelay(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 (unsigned int) 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) +{ + u8 *d =3D (u8 *)data; + unsigned int reg =3D (unsigned int) 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_NB] =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 < 0) + conf->mode =3D oldconf->mode; + if (conf->fs < 0) + conf->fs =3D oldconf->fs; + if (conf->odr < 0) + conf->odr =3D oldconf->odr; + if (conf->filter < 0) + 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 < 0) + conf->mode =3D oldconf->mode; + if (conf->fs < 0) + conf->fs =3D oldconf->fs; + if (conf->odr < 0) + conf->odr =3D oldconf->odr; + if (conf->filter < 0) + 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) { + dev_err(dev, "invalid whoami %#02x expected %#02x (%s)\n", + val, chip_info->whoami, chip_info->name); + return -ENODEV; + } + + 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) +{ + struct inv_icm45600_state *st =3D _data; + const struct device *dev =3D regmap_get_device(st->map); + int ret; + + ret =3D regulator_disable(st->vddio_supply); + if (ret) + dev_err(dev, "failed to disable vddio error %d\n", ret); +} + +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 ret; + /* IMU start-up time. */ + msleep(100); + + ret =3D inv_icm45600_enable_regulator_vddio(st); + if (ret) + return ret; + + ret =3D devm_add_action_or_reset(dev, inv_icm45600_disable_vddio_reg, st); + if (ret) + return ret; + + /* 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; + + guard(mutex)(&st->lock); + + ret =3D inv_icm45600_enable_regulator_vddio(st); + if (ret) + return ret; + + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + /* 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 Tue Oct 7 11:56:55 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 50CB628DF0F; Thu, 10 Jul 2025 08:58:03 +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=1752137883; cv=none; b=TvrMRv2QmyxWHeFTMDg0gub3yZ0oZUCCVhf0cX3brohsfTwuYKUbRoqdpAOuTsCVPrX6jSguPUt2Wv0QrvxNekegomhgXmGUxoESlSH9QNgooInfPw91rrWjr9XQtZQWxKj3KzBoCIrJP53elJQF189UutKcLau2x1V3nkb2wwE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752137883; c=relaxed/simple; bh=4uGhn/LLwhztSL1dUvathaJz2kss8I/f0BvFCdY1dNM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=bOCB2YOWgnA/CDAkbFfXH5Mtpt54UWKBvugrkieHpZ3wu9gQY48kPgzq/awiuy5FyMm0kTe4SdejtzUQZDbxvp4iXla0blVkFvpSTJz/AH2Kzn+58eiNc4ZvNgP34PTgJtzo+jy73Olf+SEP11Ac9SEcZyYfBHgzlvilOE+hvok= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=QdL4oyuE; 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="QdL4oyuE" Received: by smtp.kernel.org (Postfix) with ESMTPS id E002FC4CEF7; Thu, 10 Jul 2025 08:58:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1752137883; bh=4uGhn/LLwhztSL1dUvathaJz2kss8I/f0BvFCdY1dNM=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=QdL4oyuEMWVPQeGRcvHvuQ2tYS4EMziHdaaKf8JcwUJ3Md387PdM57+RgMFRMHCbA p9Wdw11RzUO+sggjyOKG3YqyRZY2LHkCsHEZCi1ioJ3BP5hsSUuZ+ZrRFmBg6PWlU8 0X8Usw1612BZweVBYJ2qJwZ11b9iYJbcmqWIhOIp3/BY7sxDS6aopPTppPyslikSUo LKv5+GU5P9dPjkT2HoEkCtnCQERs9N5HCHOJuFmSZAo0Mx2iZswTDRDdhtoh7ZQa4d E+WEG5NjqhvlmqMCevuilujZ0BQzYKCInSFBRmiqc4aZmxB1vsj74bMlf+wQSdS8mM kNWS1Hv5pxQ2Q== 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 D7DC9C83F20; Thu, 10 Jul 2025 08:58:02 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Thu, 10 Jul 2025 08:57:58 +0000 Subject: [PATCH v2 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: <20250710-add_newport_driver-v2-3-bf76d8142ef2@tdk.com> References: <20250710-add_newport_driver-v2-0-bf76d8142ef2@tdk.com> In-Reply-To: <20250710-add_newport_driver-v2-0-bf76d8142ef2@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=1752137881; l=27238; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=y8PRcXXB1V/CcoheWOZgmKMX5zLHSkelwuntIfbNg30=; b=1GWZX3psyKB0grIygjjMh/bPYDUSTMGv3j3BYOskEUO2cRqkScNnVZ1U8K7KH9a/vUJxStjVg zQ0/gR1RPO3Ba4XMcOM+2lbatsKB/TDhvCoNKe4w6ycJ0bc3Yycd3vf 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 | 4 + 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, 754 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 d56de75ab7f2168f22e25b5816cb361bef457c0d..0b97c54f74b30aef3842e34da09= 8c2443347de00 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600.h +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h @@ -10,6 +10,8 @@ #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 @@ -128,6 +130,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 { @@ -144,6 +147,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..a07affa0e3c64f84330071c9201= 8438882cdf686 --- /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_INIT; + 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 022eb9180b5a750e8fba4a1cd873041c27f59880..37487e9174bf53f2fc6583f37ac= 7542ea31b0643 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c @@ -16,6 +16,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, @@ -521,6 +522,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. */ @@ -627,6 +717,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) @@ -658,6 +756,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) @@ -675,6 +789,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; =20 guard(mutex)(&st->lock); @@ -688,9 +804,28 @@ static int inv_icm45600_resume(struct device *dev) pm_runtime_enable(dev); =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; + + /* Restore FIFO data streaming. */ + if (st->fifo.on) { + unsigned int val; =20 + 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 Tue Oct 7 11:56:55 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 5EBE7290D94; Thu, 10 Jul 2025 08:58:03 +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=1752137883; cv=none; b=tITX78X+e8UnzTiwo8U4tozbDZBXlBtjSuTzFZraVfBKhLR+3rWk2HZwWk4cMoOyN6ugKZqN9Uq0tNb76/rg6gbOmo3GrMQqK1oMmksDVolAe+TlJUCICOo16JiRnid6bNBPkgsMrzktlcTZC1TM25Oabrq97X/kKv4wC7hRHR0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752137883; c=relaxed/simple; bh=CsOuSqpu3fl+KUPuyYiv1dknO6juSbCx/xLd7kLmiCc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=KhwCoMl/+Qkkirn+L/k9DQPsIO62yWcl9J4uaqHlDlkC5KmFp/WL7rJ+HsEbKNmfir79AYiTPdZCyYeN2BqM2+SV/fowBlDqxuVL5qbIuxzhwmwhl6pvmRfIdcNQFIjFEl+nxqmOLv8k8qSZ+w8yVlLbxx7iPCOcVioIHQ/bPGc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=WWOo1jnO; 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="WWOo1jnO" Received: by smtp.kernel.org (Postfix) with ESMTPS id 0705AC4CEFD; Thu, 10 Jul 2025 08:58:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1752137883; bh=CsOuSqpu3fl+KUPuyYiv1dknO6juSbCx/xLd7kLmiCc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=WWOo1jnOASQqzYrJB7sx6na1PATmi0LCRIMwYkBl5XMRwohoJKJEsyfH6MQDiC4nI lgU+jCR+pKe9ZKpui/94W0iCTAEM1N3d2BNyfXtr42mCLCxicmkyxtAJpcLsaMTIP3 vqNvp8EyQsoujtqWjxyoCwUkmWQPtxakgpLYFwq7VnBLreidGZfMBphnDGYnFyXQRy SxKlXf6sjOCarWgUEiHyXYMOp/DQzipQHet0dEDhmbL6tG4/IVa07EcAVFLtSmVaoK eMrKowkCok3Nmln9/Z7M22dhlGMtTZxy2yk2mAEsJh6Ul6cyDaMvfzzXkDqcDrk5qP 9ITebkHu2AH2A== 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 EC459C83F1F; Thu, 10 Jul 2025 08:58:02 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Thu, 10 Jul 2025 08:57:59 +0000 Subject: [PATCH v2 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: <20250710-add_newport_driver-v2-4-bf76d8142ef2@tdk.com> References: <20250710-add_newport_driver-v2-0-bf76d8142ef2@tdk.com> In-Reply-To: <20250710-add_newport_driver-v2-0-bf76d8142ef2@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=1752137881; l=60111; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=mbYQ1JkF6lu9s8YB4frvseZ4MLlTtTJ6wH64n5z4i10=; b=LptwGCwfyneh2/m+N6d9zzL85ZRX2i4+DTKlnFfFmqlne6QTdcBKK5Pz3xKp2W1yPaWRwVxIc MvKGKkTNvZUD+xaATubekX3a1yiE3DSSdfuem/SAmvL/9txftl2GjBy 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 0b97c54f74b30aef3842e34da098c2443347de00..dad53abf1b867da7ad2bc88c222= 031de791fed16 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600.h +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h @@ -107,6 +107,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; @@ -118,6 +122,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..f0a5e58c95fa3f6c05524771dd0= 3a5b3026b7a48 --- /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_INIT; + unsigned int fifo_en =3D 0; + unsigned int sleep; + 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_INIT; + 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_INIT; + 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_INIT; + 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 > 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 a07affa0e3c64f84330071c92018438882cdf686..ba6d76e71abf5835813bbbb4fae= ff4971029865c 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 37487e9174bf53f2fc6583f37ac7542ea31b0643..9319cd5c067d42b5f4e93c9a521= 87be17a71599c 100644 --- a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c @@ -148,6 +148,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_NB, + .gyro_scales =3D (const int *)inv_icm45600_gyro_scale, + .gyro_scales_len =3D INV_ICM45600_GYRO_FS_NB, }; EXPORT_SYMBOL_NS_GPL(inv_icm45605_chip_info, "IIO_ICM45600"); =20 @@ -155,6 +159,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_NB, + .gyro_scales =3D (const int *)inv_icm45600_gyro_scale, + .gyro_scales_len =3D INV_ICM45600_GYRO_FS_NB, }; EXPORT_SYMBOL_NS_GPL(inv_icm45606_chip_info, "IIO_ICM45600"); =20 @@ -162,6 +170,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_NB, + .gyro_scales =3D (const int *)inv_icm45600_gyro_scale, + .gyro_scales_len =3D INV_ICM45600_GYRO_FS_NB, }; EXPORT_SYMBOL_NS_GPL(inv_icm45608_chip_info, "IIO_ICM45600"); =20 @@ -169,6 +181,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_NB, + .gyro_scales =3D (const int *)inv_icm45600_gyro_scale, + .gyro_scales_len =3D INV_ICM45600_GYRO_FS_NB, }; EXPORT_SYMBOL_NS_GPL(inv_icm45634_chip_info, "IIO_ICM45600"); =20 @@ -176,6 +192,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_NB, + .gyro_scales =3D (const int *)inv_icm45686_gyro_scale, + .gyro_scales_len =3D INV_ICM45686_GYRO_FS_NB, }; EXPORT_SYMBOL_NS_GPL(inv_icm45686_chip_info, "IIO_ICM45600"); =20 @@ -183,6 +203,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_NB, + .gyro_scales =3D (const int *)inv_icm45686_gyro_scale, + .gyro_scales_len =3D INV_ICM45686_GYRO_FS_NB, }; EXPORT_SYMBOL_NS_GPL(inv_icm45687_chip_info, "IIO_ICM45600"); =20 @@ -190,6 +214,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_NB, + .gyro_scales =3D (const int *)inv_icm45686_gyro_scale, + .gyro_scales_len =3D INV_ICM45686_GYRO_FS_NB, }; EXPORT_SYMBOL_NS_GPL(inv_icm45688p_chip_info, "IIO_ICM45600"); =20 @@ -197,6 +225,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_NB, + .gyro_scales =3D (const int *)inv_icm45686_gyro_scale, + .gyro_scales_len =3D INV_ICM45686_GYRO_FS_NB, }; EXPORT_SYMBOL_NS_GPL(inv_icm45689_chip_info, "IIO_ICM45600"); =20 @@ -553,6 +585,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. */ @@ -721,6 +756,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; @@ -857,6 +900,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_INIT; + 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..8ba2e8e54c738a3289726d6f23a= 46f5307ebdffe --- /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_INIT; + unsigned int fifo_en =3D 0; + unsigned int sleep; + 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_INIT; + 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_INIT; + 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_INIT; + 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 > 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 Tue Oct 7 11:56:55 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 776BD290D96; Thu, 10 Jul 2025 08:58:03 +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=1752137883; cv=none; b=JaJkKI7q8KAIJ0j9Xp37n3b748J6+l2TR3kWnsnUWodWHn/5V/O+/liHLL3LqdoggBjT3S/0hGL9fb6O5vYup5rHzGuzNCzM0Dnx95iJZ7AItPRJGyR529fqwyT4V2hmP64YQzO1lYDJ64GimHzmnXqMeZkDkHyNL3It9L6/fXQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752137883; c=relaxed/simple; bh=OhptN+7B1XE2fgRUs9oHodmN33ZP4J2jyblze+ymoPg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=VEVbDt76GNQ1X13Eux8VsP8tM4qMFDWZ+mIwWxYdy8+DLpENmfJeSytx6MMtesXmUUV8dklmU907BfNESLCDU5nEGE+YWV4rPCNPqfyZZJSSMZERVS4g4fb+c1FOfQAjltys9dJR6BeiQN85Sb4eGRNVnQ5SdkoMO4DtX75KWt8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=cfXbWXbt; 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="cfXbWXbt" Received: by smtp.kernel.org (Postfix) with ESMTPS id 111FAC4AF11; Thu, 10 Jul 2025 08:58:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1752137883; bh=OhptN+7B1XE2fgRUs9oHodmN33ZP4J2jyblze+ymoPg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=cfXbWXbtHtLIy8+msInrHyby2cjAO+mbASADF6VLKksvpM52D/t2adWlGpt97Ocm0 b7EjZv0S1UPO9qmCjykdIYvPXCIkGaTf7ZWKGHQ33WllX+wFa2dUk9Z3Thu0OzL73y SuEwF8Q5S+02U69fZwqoEFEXIGuDx1JPDU24oODRd473rqnIdz4h1t6lQt238CMmFj oFcIUbOArgzECBan9ZJ7XfYMz5Ho86Rli1vcxfZ7iHg+W740ZgbD52OggTzfHU13X2 SF6sSeleE9TPSYRPXTeAtf7MnVb1wyGy1Cmy21Yrc92t/wInjGH//jVLlumDYJBUqs Grseg09Im22Ow== 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 06B7EC83F1A; Thu, 10 Jul 2025 08:58:03 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Thu, 10 Jul 2025 08:58:00 +0000 Subject: [PATCH v2 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: <20250710-add_newport_driver-v2-5-bf76d8142ef2@tdk.com> References: <20250710-add_newport_driver-v2-0-bf76d8142ef2@tdk.com> In-Reply-To: <20250710-add_newport_driver-v2-0-bf76d8142ef2@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=1752137881; l=5081; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=rm9K6Dx3nzkqESFb6IZ1u3aaGk1Q2lyM/F/KVpz+xTs=; b=X+czwMlFUgC3MdylQzq5lESszrJ66Z6jXTmZQbNo54ens+E7SmZmhDCHIsx/WeHPFXb5Hbn37 dc77EGJk4kJAkSDgUSZ1i0RFr4rcquB3663YgCh7y21XB7/rfBaqvT6 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 Tue Oct 7 11:56:55 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 777B6290DBB; Thu, 10 Jul 2025 08:58:03 +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=1752137883; cv=none; b=YZukqRHHBrRRBLtGs6B287wHbiSwmhFjF3xs7W9i+KKPffALLYBag/l9FZOeoSguDoltzOX+LBUBn/DeI5rTkAB8tdeimWI6dRnje4b1dv2xxY2jAOFNDtL3K06z2+rtmpxmWtCwyqTPohp5ykM4aqgq1e3YWXH40aSMKhD2xhg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752137883; 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=YPyoF82cCLpP1G3nRU34VNWP1L9jnVwpzWDx+4+wjusYJPuJtkViKhDXxwAh7ZmJR9fmU2BY2l+Ya6v0ux6dqbd7L6uKH3Yr220NEucc20R3Sybcdb4zyohA/3w/vuc5nayehhe9BU34bH9ZyR2+SmTSfSJ7N/ojnlpSJdaayzY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=nxyhOmfA; 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="nxyhOmfA" Received: by smtp.kernel.org (Postfix) with ESMTPS id 1F3CEC4CEFF; Thu, 10 Jul 2025 08:58:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1752137883; bh=c2IX8PPIIZJzerT/aw+ykeihrxVRi5Gp/7XdkvR+5kY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=nxyhOmfAen0rptGqh3s/gkXP/46NVFtkaf3PWr7fyOoFtMQSTXegIZ13o7kNXKZbW FDK4FCo9DptPbpu/Z3XZ+qbXXj4OnHFpL8nf8Mg61diplSh5cJZ276nnp5PNdYGdxD YhS5sL35tUAMNRrmenoOk64FEJKQ8+h71911NR99+Aifth/l+yhqEDu5vz+f2gZ3ca XprqUrFK9vxBRuRkulr4JPhzhl+MvBEvfvS5Gy1/dp9C00xH9QnT5s+y7j4COxsIj4 OCUhQw7fc9RuxsF5/30FiYRDTnSV0Q2eJPXmKMnePOIDKgl1rVi2I3wun05a5gN9Jm ABAKkml+TzAzg== 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 1565AC83F1D; Thu, 10 Jul 2025 08:58:03 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Thu, 10 Jul 2025 08:58:01 +0000 Subject: [PATCH v2 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: <20250710-add_newport_driver-v2-6-bf76d8142ef2@tdk.com> References: <20250710-add_newport_driver-v2-0-bf76d8142ef2@tdk.com> In-Reply-To: <20250710-add_newport_driver-v2-0-bf76d8142ef2@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=1752137881; l=5474; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=hnED/tFv/crwWZzbfKEjfj7vI38mqj6A3J7aBo72i3g=; b=8zMYvJUdvn/4xaxyzIY8A4MKjIW43K/AC/8YC3V7cbxAoSi6JuzCrfV0aWEUOXZ62IZVy19pP hl/iqsfM1MIChe/NXgib7yVf8/1Uf1+7+tjEhWS3FMHc2gHVMB3wczT 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 Tue Oct 7 11:56:55 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 7774B290D98; Thu, 10 Jul 2025 08:58:03 +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=1752137883; cv=none; b=RRYwT0KVCzYTpLJEXNeM6r6+PcO975ovweb3pkYKH2DNcwk+hbbT0rY5TeoqvKQ9XpTIR1wT6EEA3JAsDk92neevYI6dhuAhi7e9uCOteMWvWrhoOPNVhKsCb8v3cdoQhNC08qejFUF/GLusj6/s/xtz3R1nnIfSKcGGcchasYQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752137883; 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=cIpE3yBh501NpItaZqv1AtmTQMP3YG0KzzTrRdgcDIavUTQkdWPRC1x8g+fRtRRBkjhfBU8PC9BBzNyBe2qcu9dXVhIM77i7qqUxJF3zT9iulvOC9jXYN6nxmRsNbQCiGyisceFE2RdqnFniFR3xDpbtu/fbfefraQRM9xs9kq8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=M4p+LRal; 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="M4p+LRal" Received: by smtp.kernel.org (Postfix) with ESMTPS id 35ECEC4AF0F; Thu, 10 Jul 2025 08:58:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1752137883; bh=vsHjZJf51AfU8S+DT+3gZS1r3jeZjtw/LCAGqXyQnIc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=M4p+LRalgj3AeDRhEOYx13ukmHuwmdnNKbKvnhnQZOGN7d7pbeFY7/ab8hFlfIN/+ J/uyzdo8jkC7Db4x6pnX79ZPDfjM8tjQUJ2DRATh8hb2bQeVMgKXd9A1RU2tRHsNod NxKHCqYlyT15O8lLHS6wpzfM5enk1KG7+KVi9kgxeHRd4iROUHui9LXLHReqmj0tB6 FAUKRbY8eEXHP6ktT3wibvjRj3LRhHhrKYs5zu++twyx86T1mcJ5e6D3F8tTmp3ApZ LYWefuIZWNZ3YAAGzM7zlV+HgyJqgw5ZRLkOiOz9ann4yYJ3PtA7T9N74ncXPduGm8 Pbv18iB667hBg== 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 253E1C83F20; Thu, 10 Jul 2025 08:58:03 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Thu, 10 Jul 2025 08:58:02 +0000 Subject: [PATCH v2 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: <20250710-add_newport_driver-v2-7-bf76d8142ef2@tdk.com> References: <20250710-add_newport_driver-v2-0-bf76d8142ef2@tdk.com> In-Reply-To: <20250710-add_newport_driver-v2-0-bf76d8142ef2@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=1752137881; l=4456; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=4m9qBpGi25U3Cwi+7Mah1nGcPeoIUiDCyFwUUL17ZWI=; b=yYWecN4TqeFNQZ2Uy3aAt7gmjHgxNLAlomKLTC0aFl6Z4lnjDAVRhSTAPXNq2ZvTChTDIMpN0 uScb77BPs7iAtOdfexHiwrCfRPLKwVHnFg1K+HZwEBBsReLkcnHfOQH 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 Tue Oct 7 11:56:55 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 8F2ED292B44; Thu, 10 Jul 2025 08:58:03 +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=1752137883; cv=none; b=G/6Lrodpr7rC2tF+IDjVxu0rPKiMMVXy4C6ISe2lkG4CkyP5fWthAxMJ5lYtnbv4Kw9n57pSti8TNaennwBht1tTs1bXDWiHlr5RkYPqFRbHqRfyVP6amAFMjVpYtw3c2Aqk+X4jWvZX4DOfgEfF/K3CuHaQ67H6KU53b33keHo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752137883; c=relaxed/simple; bh=DSLa9aPeM077q6gvw6PURI/pH8PombVAOR3qpaCktpg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=bpegJE81H31CH/nXQfH/C0Kh7/uwTOQPR7bAr4JOqMkutEY6/0SvJFTWiPYIbsoDTvymsXiDnokgt1EAJJO0cDW5Tb3/ZSN3113hEK/OU1fJ99sT+FocqQ1pLdFfyhjaU10H9tpcJE+QrPNn3RPGadzf8EduwRk2Q1J6q8QDhdo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=kq2texk8; 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="kq2texk8" Received: by smtp.kernel.org (Postfix) with ESMTPS id 3DFE9C4CEFB; Thu, 10 Jul 2025 08:58:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1752137883; bh=DSLa9aPeM077q6gvw6PURI/pH8PombVAOR3qpaCktpg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=kq2texk8gHxg/rLT8FB5qi4XlE1hTSvHSat55VirxxkN+40n4qNpfoOVPydmgzas/ h2nopZvW4njGS3ZSvvhsQG6hNlZQxAAMLOq7ZVWrdUmFYNeNLREFBFCeV/4ss9+TwV Gr6TObihPoA9jwkdQSUFP08elZlRG1X52pYivFpxRZETH7Cw9DkQSFPcrQV3TPlNLf ntuxeai6/Efaf5pBSmri7Zi9RPHyzEsnb/jicng0mFwy9M/3YLdVPMkCtZgFBkKL4m Y22BFQqzsyPeuBINYxBX152ebeWWy6zGMuqPl/0X4A47JauKfQO6lyjKnaqlTYsoOJ mAX/UR5ytZ7GA== 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 344DBC83F1F; Thu, 10 Jul 2025 08:58:03 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Thu, 10 Jul 2025 08:58:03 +0000 Subject: [PATCH v2 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: <20250710-add_newport_driver-v2-8-bf76d8142ef2@tdk.com> References: <20250710-add_newport_driver-v2-0-bf76d8142ef2@tdk.com> In-Reply-To: <20250710-add_newport_driver-v2-0-bf76d8142ef2@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=1752137881; l=945; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=8YxNmTOrgJXwqprkXECUw3/UJ3Lll4HOZd/QYkpH/Pw=; b=l9dnyLhdkDbIBE9g0pBBhegUR2yle4XF5jKqvHXz8W23D53L7Ta5JiNhn5evxUXPgXEv0o1T8 KrBw4WHxtUFAwyoXDg9rLdGVfjmGfO75B+mYTZGoA41zSRm88cnaXki 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