From nobody Sat Sep 13 22:31:01 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 0AE7D19DF5B; Fri, 11 Apr 2025 13:28:41 +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=1744378122; cv=none; b=nVYKTpDQncRFjPL3oFJEKz9l1FvqL44cfI7PbqYY+faquN6TIUOzgD51fPm065q9+B6clFRXW9w/NUGTx7STc+ln+8GRu1df+AwRXCdF6ea2fAJ6PduC/rnTLMR1qN9D14MgV2AZOza5OeZqYn75mIFY5/uRYFdlolFDO793rFk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744378122; c=relaxed/simple; bh=Yx7UoGOLaHGrDnmANbmMF99mA9ZlaNx+pyRCw79KUgw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=s71wdN/E6qd/G7HAiLvrCtiwHpyeF/rCOZZ/U54RiQGOcPwz6a4BU3RaCcVBdx8svWTTVHTwwLRzlaTdsxMIVcZHJiRG04kmOb0VwQxy0o4MKm37y/VWgWeG1Eg12w7d/0gi0MoH0Z5aLsLA5KZBEsdIaFJ5mWzLHKnLygmWM/A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=eEgwnzCb; 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="eEgwnzCb" Received: by smtp.kernel.org (Postfix) with ESMTPS id 7A314C4CEE7; Fri, 11 Apr 2025 13:28:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1744378121; bh=Yx7UoGOLaHGrDnmANbmMF99mA9ZlaNx+pyRCw79KUgw=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=eEgwnzCboyUCxs2FNhuPCN7ANlY4RbqkQA9iANxjvvm8/a41DjV86uuZ9jW1+c68d aAQJ81VyEpRRY2GhGMAm05Y9XfNtqSS3n1aAQkWgoL0Z7pWCdbI6etvm1gZWfGUWUa CQjdKOA92JwdFii78sW++ImjibwWxSxI0JZpFcFFWzSjJdZwxwktIsV9THaDGRyF5i BTcxx5n7682Yv3XtebEV2mFHWnx3iuWWkmI8cTAKepuvGnJQYzXj20v3JhPiiaxCch 4a4ksfxFDTZ4hJvhCsHa7lLwCKLVeVlvBG0ifMre8538pzm+9U2Hw20nK5MjBA1aog PsoH6Db6GbrFA== 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 65B4CC369AD; Fri, 11 Apr 2025 13:28:41 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Fri, 11 Apr 2025 13:28:33 +0000 Subject: [PATCH 1/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: <20250411-add_newport_driver-v1-1-15082160b019@tdk.com> References: <20250411-add_newport_driver-v1-0-15082160b019@tdk.com> In-Reply-To: <20250411-add_newport_driver-v1-0-15082160b019@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=1744378119; l=100545; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=QO89jpUHe1g0Q9p9/sAfufif+vuMcR0Ec4HUUU9g/Cg=; b=mQWbZdHGlKF5n73GzJoT3RtU9CSboz5g4jCnIwjd3KPX7OhKjDpYsMmx7dRVXogt4pq6mQWaG ol5/wLbZ5wIDwOJ8TtlMZ55hmiAuMgqhx8/NhqXaeowel4e064ORRWz 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-456xx devices. It includes: - registers definition, main probe/setup, and device utility functions. - IIO device for gyroscope sensor with data polling interface. Attributes: raw, scale, sampling_frequency, calibbias. - IIO device for gyroscope sensor with data polling interface. Attributes: raw, scale, sampling_frequency, calibbias. - Temperature is available as a processed channel 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/inv_icm45600/inv_icm45600.h | 421 ++++++++++ drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c | 902 ++++++++++++++++++= +++ drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 906 ++++++++++++++++++= +++ drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c | 919 ++++++++++++++++++= ++++ drivers/iio/imu/inv_icm45600/inv_icm45600_temp.c | 82 ++ drivers/iio/imu/inv_icm45600/inv_icm45600_temp.h | 31 + 6 files changed, 3261 insertions(+) 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..6d10b3ffabbcbc054986d6cc001= 1891863b74e1a --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h @@ -0,0 +1,421 @@ +/* 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 +#include +#include +#include + +#include "inv_icm45600_buffer.h" + +#define INV_ICM45600_REG_GET_BANK(_r) FIELD_GET(GENMASK(15, 8), (_r)) +#define INV_ICM45600_REG_GET_ADDR(_r) FIELD_GET(GENMASK(7, 0), (_r)) + +enum inv_icm45600_chip { + INV_CHIP_INVALID, + INV_CHIP_ICM45605, + INV_CHIP_ICM45686, + INV_CHIP_ICM45688P, + INV_CHIP_ICM45608, + INV_CHIP_ICM45634, + INV_CHIP_ICM45689, + INV_CHIP_ICM45606, + INV_CHIP_ICM45687, + INV_CHIP_NB, +}; + +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_state - driver state variables + * @lock: lock for serializing multiple registers access. + * @chip: chip identifier. + * @name: chip name. + * @map: regmap pointer. + * @vdd_supply: VDD voltage regulator for the chip. + * @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. + * @fifo: FIFO management structure. + * @buffer: data transfer buffer aligned for DMA. + */ +struct inv_icm45600_state { + struct mutex lock; + enum inv_icm45600_chip chip; + const char *name; + struct regmap *map; + struct regulator *vdd_supply; + 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; + struct { + int64_t gyro; + int64_t accel; + } timestamp; + struct inv_icm45600_fifo fifo; + uint8_t buffer[2] __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_I2C_MASK GENMASK(6, 4) +#define INV_ICM45600_DRIVE_CONFIG0_I2C(_rate) \ + FIELD_PREP(INV_ICM45600_DRIVE_CONFIG0_I2C_MASK, (_rate)) +#define INV_ICM45600_I2C_SLEW_RATE_7NS \ + INV_ICM45600_DRIVE_CONFIG0_I2C(2) +#define INV_ICM45600_I2C_SLEW_RATE_20NS \ + INV_ICM45600_DRIVE_CONFIG0_I2C(0) +#define INV_ICM45600_DRIVE_CONFIG0_SPI_MASK GENMASK(3, 1) +#define INV_ICM45600_DRIVE_CONFIG0_SPI(_rate) \ + FIELD_PREP(INV_ICM45600_DRIVE_CONFIG0_SPI_MASK, (_rate)) +#define INV_ICM45600_SPI_SLEW_RATE_0_5NS \ + INV_ICM45600_DRIVE_CONFIG0_SPI(6) +#define INV_ICM45600_SPI_SLEW_RATE_4NS \ + INV_ICM45600_DRIVE_CONFIG0_SPI(5) +#define INV_ICM45600_SPI_SLEW_RATE_5NS \ + INV_ICM45600_DRIVE_CONFIG0_SPI(4) +#define INV_ICM45600_SPI_SLEW_RATE_7NS \ + INV_ICM45600_DRIVE_CONFIG0_SPI(3) +#define INV_ICM45600_SPI_SLEW_RATE_10NS \ + INV_ICM45600_DRIVE_CONFIG0_SPI(2) +#define INV_ICM45600_SPI_SLEW_RATE_14NS \ + INV_ICM45600_DRIVE_CONFIG0_SPI(1) +#define INV_ICM45600_SPI_SLEW_RATE_38NS \ + INV_ICM45600_DRIVE_CONFIG0_SPI(0) + +#define INV_ICM45600_REG_DRIVE_CONFIG1 0x0033 +#define INV_ICM45600_DRIVE_CONFIG1_I3C_DDR_SLEW_MASK GENMASK(5, 3) +#define INV_ICM45600_DRIVE_CONFIG1_I3C_DDR_SLEW(_rate) \ + FIELD_PREP(INV_ICM45600_DRIVE_CONFIG1_I3C_DDR_SLEW_MASK, (_rate)) +#define INV_ICM45600_I3C_DDR_SLEW_0_5NS \ + INV_ICM45600_DRIVE_CONFIG1_I3C_DDR_SLEW(6) +#define INV_ICM45600_I3C_DDR_SLEW_4NS \ + INV_ICM45600_DRIVE_CONFIG1_I3C_DDR_SLEW(5) +#define INV_ICM45600_I3C_DDR_SLEW_5NS \ + INV_ICM45600_DRIVE_CONFIG1_I3C_DDR_SLEW(4) +#define INV_ICM45600_I3C_DDR_SLEW_7NS \ + INV_ICM45600_DRIVE_CONFIG1_I3C_DDR_SLEW(3) +#define INV_ICM45600_I3C_DDR_SLEW_10NS \ + INV_ICM45600_DRIVE_CONFIG1_I3C_DDR_SLEW(2) +#define INV_ICM45600_I3C_DDR_SLEW_14NS \ + INV_ICM45600_DRIVE_CONFIG1_I3C_DDR_SLEW(1) +#define INV_ICM45600_I3C_DDR_SLEW_38NS \ + INV_ICM45600_DRIVE_CONFIG1_I3C_DDR_SLEW(0) +#define INV_ICM45600_DRIVE_CONFIG1_I3C_SDR_SLEW_MASK GENMASK(2, 0) +#define INV_ICM45600_DRIVE_CONFIG1_I3C_SDR_SLEW(_rate) \ + FIELD_PREP(INV_ICM45600_DRIVE_CONFIG1_I3C_SDR_SLEW_MASK, (_rate)) +#define INV_ICM45600_I3C_SDR_SLEW_0_5NS \ + INV_ICM45600_DRIVE_CONFIG1_I3C_SDR_SLEW(6) +#define INV_ICM45600_I3C_SDR_SLEW_4NS \ + INV_ICM45600_DRIVE_CONFIG1_I3C_SDR_SLEW(5) +#define INV_ICM45600_I3C_SDR_SLEW_5NS \ + INV_ICM45600_DRIVE_CONFIG1_I3C_SDR_SLEW(4) +#define INV_ICM45600_I3C_SDR_SLEW_7NS \ + INV_ICM45600_DRIVE_CONFIG1_I3C_SDR_SLEW(3) +#define INV_ICM45600_I3C_SDR_SLEW_10NS \ + INV_ICM45600_DRIVE_CONFIG1_I3C_SDR_SLEW(2) +#define INV_ICM45600_I3C_SDR_SLEW_14NS \ + INV_ICM45600_DRIVE_CONFIG1_I3C_SDR_SLEW(1) +#define INV_ICM45600_I3C_SDR_SLEW_38NS \ + INV_ICM45600_DRIVE_CONFIG1_I3C_SDR_SLEW(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 \ + FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK, 0) +#define INV_ICM45600_FIFO_CONFIG0_MODE_STREAM \ + FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK, 1) +#define INV_ICM45600_FIFO_CONFIG0_MODE_STOP_ON_FULL \ + FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK, 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) \ + FIELD_PREP(GENMASK(3, 2), (_mode)) +#define INV_ICM45600_PWR_MGMT0_ACCEL(_mode) \ + FIELD_PREP(GENMASK(1, 0), (_mode)) + +#define INV_ICM45600_REG_ACCEL_CONFIG0 0x001B +#define INV_ICM45600_ACCEL_CONFIG0_FS_MASK GENMASK(6, 4) +#define INV_ICM45600_ACCEL_CONFIG0_FS(_fs) \ + FIELD_PREP(INV_ICM45600_ACCEL_CONFIG0_FS_MASK, (_fs)) +#define INV_ICM45600_ACCEL_CONFIG0_FS_16G \ + INV_ICM45600_ACCEL_CONFIG0_FS(1) +#define INV_ICM45600_ACCEL_CONFIG0_ODR(_odr) \ + FIELD_PREP(GENMASK(3, 0), (_odr)) +#define INV_ICM45600_REG_GYRO_CONFIG0 0x001C +#define INV_ICM45600_GYRO_CONFIG0_FS_MASK GENMASK(7, 4) +#define INV_ICM45600_GYRO_CONFIG0_FS(_fs) \ + FIELD_PREP(INV_ICM45600_GYRO_CONFIG0_FS_MASK, (_fs)) +#define INV_ICM45600_GYRO_CONFIG0_FS_2000DPS \ + INV_ICM45600_GYRO_CONFIG0_FS(1) +#define INV_ICM45600_GYRO_CONFIG0_ODR(_odr) \ + FIELD_PREP(GENMASK(3, 0), (_odr)) + +#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 +#define INV_ICM45600_FIFO_WATERMARK_VAL(_wm) \ + cpu_to_le16(_wm) +/* 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 +/* Gyro Averaging filter */ +#define INV_ICM45600_IPREG_SYS1_REG_170 0xA4AA +#define INV_ICM45600_IPREG_SYS1_REG_170_MASK GENMASK(4, 1) +#define INV_ICM45600_GYRO_LP_AVG_SEL_8X \ + FIELD_PREP_CONST(INV_ICM45600_IPREG_SYS1_REG_170_MASK, 5) +#define INV_ICM45600_GYRO_LP_AVG_SEL_2X \ + FIELD_PREP_CONST(INV_ICM45600_IPREG_SYS1_REG_170_MASK, 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 +/* Accel averaging filter */ +#define INV_ICM45600_IPREG_SYS2_REG_129 0xA581 +#define INV_ICM45600_ACCEL_LP_AVG_SEL_4X 0x0002 + +/* Sleep times required by the driver */ +#define INV_ICM45600_POWER_UP_TIME_MS 100 +#define INV_ICM45600_RESET_TIME_MS 1 +#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_SUSPEND_DELAY_MS 2000 +#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); + +uint32_t 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, int chip, + 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_accel.c b/drivers/ii= o/imu/inv_icm45600/inv_icm45600_accel.c new file mode 100644 index 0000000000000000000000000000000000000000..3c046cad83474da43509295dd55= 42e40b7a0296a --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_accel.c @@ -0,0 +1,902 @@ +// 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_temp.h" +#include "inv_icm45600.h" + +#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, \ + } + +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 char * const inv_icm45600_accel_power_mode_items[] =3D { + "low-noise", + "low-power", +}; +static const int inv_icm45600_accel_power_mode_values[] =3D { + INV_ICM45600_SENSOR_MODE_LOW_NOISE, + INV_ICM45600_SENSOR_MODE_LOW_POWER, +}; + +static int inv_icm45600_accel_power_mode_set(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int idx) +{ + 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 val =3D 0; + int power_mode; + + if (chan->type !=3D IIO_ACCEL) + return -EINVAL; + + if (idx >=3D ARRAY_SIZE(inv_icm45600_accel_power_mode_values)) + return -EINVAL; + + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + + power_mode =3D inv_icm45600_accel_power_mode_values[idx]; + + guard(mutex)(&st->lock); + + /* prevent change if power mode is not supported by the ODR */ + switch (power_mode) { + case INV_ICM45600_SENSOR_MODE_LOW_NOISE: + if (st->conf.accel.odr >=3D INV_ICM45600_ODR_6_25HZ_LP && + st->conf.accel.odr <=3D INV_ICM45600_ODR_1_5625HZ_LP) + return -EPERM; + break; + case INV_ICM45600_SENSOR_MODE_LOW_POWER: + default: + if (st->conf.accel.odr <=3D INV_ICM45600_ODR_800HZ_LN) + return -EPERM; + break; + } + + accel_st->power_mode =3D power_mode; + + if (accel_st->power_mode =3D=3D INV_ICM45600_SENSOR_MODE_LOW_POWER) + val =3D INV_ICM45600_SMC_CONTROL_0_ACCEL_LP_CLK_SEL; + + return regmap_update_bits(st->map, INV_ICM45600_REG_SMC_CONTROL_0, + INV_ICM45600_SMC_CONTROL_0_ACCEL_LP_CLK_SEL, val); +} + +static int inv_icm45600_accel_power_mode_get(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + 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; + int power_mode; + + if (chan->type !=3D IIO_ACCEL) + return -EINVAL; + + guard(mutex)(&st->lock); + + /* if sensor is on, returns actual power mode and not configured one */ + switch (st->conf.accel.mode) { + case INV_ICM45600_SENSOR_MODE_LOW_POWER: + case INV_ICM45600_SENSOR_MODE_LOW_NOISE: + power_mode =3D st->conf.accel.mode; + break; + default: + power_mode =3D accel_st->power_mode; + break; + } + + for (idx =3D 0; idx < ARRAY_SIZE(inv_icm45600_accel_power_mode_values); += +idx) { + if (power_mode =3D=3D inv_icm45600_accel_power_mode_values[idx]) + break; + } + if (idx >=3D ARRAY_SIZE(inv_icm45600_accel_power_mode_values)) + return -EINVAL; + + return idx; +} + +static const struct iio_enum inv_icm45600_accel_power_mode_enum =3D { + .items =3D inv_icm45600_accel_power_mode_items, + .num_items =3D ARRAY_SIZE(inv_icm45600_accel_power_mode_items), + .set =3D inv_icm45600_accel_power_mode_set, + .get =3D inv_icm45600_accel_power_mode_get, +}; + +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), + IIO_ENUM_AVAILABLE("power_mode", IIO_SHARED_BY_TYPE, + &inv_icm45600_accel_power_mode_enum), + IIO_ENUM("power_mode", IIO_SHARED_BY_TYPE, + &inv_icm45600_accel_power_mode_enum), + {}, +}; + +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; + int16_t temp; + aligned_s64 timestamp; +}; + +#define INV_ICM45600_SCAN_MASK_ACCEL_3AXIS \ + (BIT(INV_ICM45600_ACCEL_SCAN_X) | \ + BIT(INV_ICM45600_ACCEL_SCAN_Y) | \ + BIT(INV_ICM45600_ACCEL_SCAN_Z)) + +#define INV_ICM45600_SCAN_MASK_TEMP BIT(INV_ICM45600_ACCEL_SCAN_TEMP) + +static const unsigned long inv_icm45600_accel_scan_masks[] =3D { + /* 3-axis accel + temperature */ + INV_ICM45600_SCAN_MASK_ACCEL_3AXIS | INV_ICM45600_SCAN_MASK_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_temp =3D 0; + unsigned int sleep_accel =3D 0; + unsigned int sleep; + int ret; + + scoped_guard(mutex, &st->lock) { + if (*scan_mask & INV_ICM45600_SCAN_MASK_TEMP) + fifo_en |=3D INV_ICM45600_SENSOR_TEMP; + + if (*scan_mask & INV_ICM45600_SCAN_MASK_ACCEL_3AXIS) { + /* enable accel sensor */ + conf.mode =3D accel_st->power_mode; + ret =3D inv_icm45600_set_accel_conf(st, &conf, &sleep_accel); + if (ret) + break; + 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 maximum required time */ + sleep =3D max(sleep_accel, sleep_temp); + if (sleep) + msleep(sleep); + return ret; +} + +static int inv_icm45600_accel_read_sensor(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int16_t *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); + struct inv_icm45600_sensor_conf conf =3D INV_ICM45600_SENSOR_CONF_INIT; + unsigned int reg; + __le16 *data; + 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; + } + + pm_runtime_get_sync(dev); + scoped_guard(mutex, &st->lock) { + /* enable accel sensor */ + conf.mode =3D accel_st->power_mode; + ret =3D inv_icm45600_set_accel_conf(st, &conf, NULL); + if (ret) + break; + + /* read accel register data */ + data =3D (__le16 *)&st->buffer[0]; + ret =3D regmap_bulk_read(st->map, reg, data, sizeof(*data)); + if (ret) + break; + + *val =3D (int16_t)le16_to_cpup(data); + if (*val =3D=3D INV_ICM45600_DATA_INVALID) + ret =3D -EINVAL; + } + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return ret; +} + +/* IIO format int + nano */ +static const int inv_icm45600_accel_scale[] =3D { + /* +/- 16G =3D> 0.004788403 m/s-2 */ + [2 * INV_ICM45600_ACCEL_FS_16G] =3D 0, + [2 * INV_ICM45600_ACCEL_FS_16G + 1] =3D 4788403, + /* +/- 8G =3D> 0.002394202 m/s-2 */ + [2 * INV_ICM45600_ACCEL_FS_8G] =3D 0, + [2 * INV_ICM45600_ACCEL_FS_8G + 1] =3D 2394202, + /* +/- 4G =3D> 0.001197101 m/s-2 */ + [2 * INV_ICM45600_ACCEL_FS_4G] =3D 0, + [2 * INV_ICM45600_ACCEL_FS_4G + 1] =3D 1197101, + /* +/- 2G =3D> 0.000598550 m/s-2 */ + [2 * INV_ICM45600_ACCEL_FS_2G] =3D 0, + [2 * INV_ICM45600_ACCEL_FS_2G + 1] =3D 598550, +}; +static const int inv_icm45686_accel_scale[] =3D { + /* +/- 32G =3D> 0.009576806 m/s-2 */ + [2 * INV_ICM45686_ACCEL_FS_32G] =3D 0, + [2 * INV_ICM45686_ACCEL_FS_32G + 1] =3D 9576806, + /* +/- 16G =3D> 0.004788403 m/s-2 */ + [2 * INV_ICM45686_ACCEL_FS_16G] =3D 0, + [2 * INV_ICM45686_ACCEL_FS_16G + 1] =3D 4788403, + /* +/- 8G =3D> 0.002394202 m/s-2 */ + [2 * INV_ICM45686_ACCEL_FS_8G] =3D 0, + [2 * INV_ICM45686_ACCEL_FS_8G + 1] =3D 2394202, + /* +/- 4G =3D> 0.001197101 m/s-2 */ + [2 * INV_ICM45686_ACCEL_FS_4G] =3D 0, + [2 * INV_ICM45686_ACCEL_FS_4G + 1] =3D 1197101, + /* +/- 2G =3D> 0.000598550 m/s-2 */ + [2 * INV_ICM45686_ACCEL_FS_2G] =3D 0, + [2 * INV_ICM45686_ACCEL_FS_2G + 1] =3D 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 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 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 inv_icm45600_accel_scale) + conf.fs++; + + pm_runtime_get_sync(dev); + scoped_guard(mutex, &st->lock) { + ret =3D inv_icm45600_set_accel_conf(st, &conf, NULL); + } + pm_runtime_mark_last_busy(dev); + 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 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 inv_sensors_timestamp *ts =3D &accel_st->ts; + 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 < 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; + + conf.odr =3D inv_icm45600_accel_odr_conv[idx / 2]; + + pm_runtime_get_sync(dev); + scoped_guard(mutex, &st->lock) { + ret =3D inv_sensors_timestamp_update_odr(ts, inv_icm45600_odr_to_period(= conf.odr), + iio_buffer_enabled(indio_dev)); + if (ret) + break; + + 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) + break; + inv_icm45600_buffer_update_fifo_period(st); + inv_icm45600_buffer_update_watermark(st); + } + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +/* + * Calibration bias values, IIO range format int + micro. + * Value is limited to +/-1g coded on 12 bits signed. Step is 0.5mg. + */ +static int inv_icm45600_accel_calibbias[] =3D { + -10, 42010, /* min: -10.042010 m/s=C2=B2 */ + 0, 4903, /* step: 0.004903 m/s=C2=B2 */ + 10, 37106, /* max: 10.037106 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); + int64_t val64; + int32_t bias; + unsigned int reg; + int16_t offset; + uint8_t data[2]; + 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; + } + + pm_runtime_get_sync(dev); + scoped_guard(mutex, &st->lock) { + ret =3D regmap_bulk_read(st->map, reg, st->buffer, sizeof(data)); + memcpy(data, st->buffer, sizeof(data)); + } + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + if (ret) + return ret; + + /* 12 bits signed value */ + switch (chan->channel2) { + case IIO_MOD_X: + offset =3D sign_extend32(((data[0] & 0xF0) << 4) | data[1], 11); + break; + case IIO_MOD_Y: + offset =3D sign_extend32(((data[1] & 0x0F) << 8) | data[0], 11); + break; + case IIO_MOD_Z: + offset =3D sign_extend32(((data[0] & 0xF0) << 4) | data[1], 11); + break; + default: + return -EINVAL; + } + + /* + * convert raw offset to g then to m/s=C2=B2 + * 12 bits signed raw step 0.5mg to g: 5 / 10000 + * g to m/s=C2=B2: 9.806650 + * result in micro (1000000) + * (offset * 5 * 9.806650 * 1000000) / 10000 + */ + val64 =3D (int64_t)offset * 5LL * 9806650LL; + /* for rounding, add + or - divisor (10000) divided by 2 */ + if (val64 >=3D 0) + val64 +=3D 10000LL / 2LL; + else + val64 -=3D 10000LL / 2LL; + bias =3D div_s64(val64, 10000L); + *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); + int64_t val64; + int32_t min, max; + unsigned int reg; + int16_t 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 (int64_t)val * 1000000LL + (int64_t)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 0.125mg: 1000000 / 125 + * val in micro (1000000) + * val * 1000000 / (9.806650 * 1000000 * 125) + */ + val64 =3D val64 * 1000000LL; + /* for rounding, add + or - divisor (9806650 * 125) divided by 2 */ + if (val64 >=3D 0) + val64 +=3D 9806650 * 125 / 2; + else + val64 -=3D 9806650 * 125 / 2; + offset =3D div_s64(val64, 9806650 * 125); + + /* clamp value limited to 14 bits signed */ + if (offset < -8192) + offset =3D -8192; + else if (offset > 8191) + offset =3D 8191; + + pm_runtime_get_sync(dev); + scoped_guard(mutex, &st->lock) { + ret =3D regmap_bulk_write(st->map, reg, st->buffer, 2); + } + pm_runtime_mark_last_busy(dev); + 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); + int16_t data; + 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, &data); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + *val =3D data; + 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); + int ret; + + guard(mutex)(&st->lock); + + st->fifo.watermark.accel =3D val; + ret =3D inv_icm45600_buffer_update_watermark(st); + + return ret; +} + +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->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); + + switch (st->chip) { + case INV_CHIP_ICM45686: + case INV_CHIP_ICM45688P: + case INV_CHIP_ICM45689: + case INV_CHIP_ICM45687: + accel_st->scales =3D inv_icm45686_accel_scale; + accel_st->scales_len =3D ARRAY_SIZE(inv_icm45686_accel_scale); + break; + default: + accel_st->scales =3D inv_icm45600_accel_scale; + accel_st->scales_len =3D ARRAY_SIZE(inv_icm45600_accel_scale); + /* Set Accel default FSR */ + ret =3D regmap_update_bits(st->map, INV_ICM45600_REG_ACCEL_CONFIG0, + INV_ICM45600_ACCEL_CONFIG0_FS_MASK, + INV_ICM45600_ACCEL_CONFIG0_FS_16G); + if (ret) + return ERR_PTR(ret); + break; + } + /* 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 void *accel, *gyro, *timestamp; + const int8_t *temp; + unsigned int odr; + int64_t 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_core.c b/drivers/iio= /imu/inv_icm45600/inv_icm45600_core.c new file mode 100644 index 0000000000000000000000000000000000000000..66e9826005a9fecf5b154c0a130= 8a4c4f40c4edd --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c @@ -0,0 +1,906 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 Invensense, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inv_icm45600_buffer.h" +#include "inv_icm45600.h" + +static int inv_icm45600_ireg_read(struct regmap *map, unsigned int reg, + unsigned int *data, size_t count) +{ + int ret; + uint8_t addr[2]; + ssize_t i; + + addr[0] =3D INV_ICM45600_REG_GET_BANK(reg); + addr[1] =3D INV_ICM45600_REG_GET_ADDR(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; + + for (i =3D 0; i < count; i++) { + ret =3D regmap_read(map, INV_ICM45600_REG_IREG_DATA, &data[i]); + udelay(INV_ICM45600_IREG_DELAY_US); + } + + return ret; +} + +static int inv_icm45600_ireg_write(struct regmap *map, unsigned int reg, + uint8_t *data, size_t count) +{ + int ret; + uint8_t addr_data0[3]; + ssize_t i; + + addr_data0[0] =3D INV_ICM45600_REG_GET_BANK(reg); + addr_data0[1] =3D INV_ICM45600_REG_GET_ADDR(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; + + 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); + } + + return ret; +} + +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 (INV_ICM45600_REG_GET_BANK(reg) =3D=3D 0) + return regmap_bulk_read(map, INV_ICM45600_REG_GET_ADDR(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) +{ + uint8_t *d =3D (uint8_t *)data; + unsigned int reg =3D (unsigned int) be16_to_cpup(data); + struct regmap *map =3D context; + + if (INV_ICM45600_REG_GET_BANK(reg) =3D=3D 0) + return regmap_bulk_write(map, INV_ICM45600_REG_GET_ADDR(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, +}; + +struct inv_icm45600_hw { + uint8_t whoami; + const char *name; + const struct inv_icm45600_conf *conf; +}; + +/* chip initial default configuration (default FS value is based on icm456= 86) */ +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, + }, +}; + +static const struct inv_icm45600_hw inv_icm45600_hw[INV_CHIP_NB] =3D { + [INV_CHIP_ICM45605] =3D { + .whoami =3D INV_ICM45600_WHOAMI_ICM45605, + .name =3D "icm45605", + .conf =3D &inv_icm45600_default_conf, + }, + [INV_CHIP_ICM45686] =3D { + .whoami =3D INV_ICM45600_WHOAMI_ICM45686, + .name =3D "icm45686", + .conf =3D &inv_icm45686_default_conf, + }, + [INV_CHIP_ICM45688P] =3D { + .whoami =3D INV_ICM45600_WHOAMI_ICM45688P, + .name =3D "icm45688p", + .conf =3D &inv_icm45686_default_conf, + }, + [INV_CHIP_ICM45608] =3D { + .whoami =3D INV_ICM45600_WHOAMI_ICM45608, + .name =3D "icm45608", + .conf =3D &inv_icm45600_default_conf, + }, + [INV_CHIP_ICM45634] =3D { + .whoami =3D INV_ICM45600_WHOAMI_ICM45634, + .name =3D "icm45634", + .conf =3D &inv_icm45600_default_conf, + }, + [INV_CHIP_ICM45689] =3D { + .whoami =3D INV_ICM45600_WHOAMI_ICM45689, + .name =3D "icm45689", + .conf =3D &inv_icm45686_default_conf, + }, + [INV_CHIP_ICM45606] =3D { + .whoami =3D INV_ICM45600_WHOAMI_ICM45606, + .name =3D "icm45606", + .conf =3D &inv_icm45600_default_conf, + }, + [INV_CHIP_ICM45687] =3D { + .whoami =3D INV_ICM45600_WHOAMI_ICM45687, + .name =3D "icm45687", + .conf =3D &inv_icm45686_default_conf, + }, +}; + +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; +} + +uint32_t inv_icm45600_odr_to_period(enum inv_icm45600_odr odr) +{ + static uint32_t 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 INV_ICM45600_PWR_MGMT0_GYRO(gyro) | + INV_ICM45600_PWR_MGMT0_ACCEL(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 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 power mode against ODR when sensor is on */ + switch (conf->mode) { + case INV_ICM45600_SENSOR_MODE_LOW_POWER: + case INV_ICM45600_SENSOR_MODE_LOW_NOISE: + if (conf->odr <=3D INV_ICM45600_ODR_800HZ_LN) { + conf->mode =3D INV_ICM45600_SENSOR_MODE_LOW_NOISE; + } else if (conf->odr >=3D INV_ICM45600_ODR_6_25HZ_LP && + conf->odr <=3D INV_ICM45600_ODR_1_5625HZ_LP) { + conf->mode =3D INV_ICM45600_SENSOR_MODE_LOW_POWER; + } + break; + default: + break; + } + + /* set ACCEL_CONFIG0 register (accel fullscale & odr) */ + if (conf->fs !=3D oldconf->fs || conf->odr !=3D oldconf->odr) { + val =3D INV_ICM45600_ACCEL_CONFIG0_FS(conf->fs) | + INV_ICM45600_ACCEL_CONFIG0_ODR(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 power mode against ODR when sensor is on */ + switch (conf->mode) { + case INV_ICM45600_SENSOR_MODE_LOW_POWER: + if (conf->odr !=3D INV_ICM45600_ODR_400HZ) + conf->filter =3D INV_ICM45600_GYRO_LP_AVG_SEL_8X; + else + conf->filter =3D INV_ICM45600_GYRO_LP_AVG_SEL_2X; + if (conf->odr <=3D INV_ICM45600_ODR_800HZ_LN) + conf->mode =3D INV_ICM45600_SENSOR_MODE_LOW_NOISE; + break; + case INV_ICM45600_SENSOR_MODE_LOW_NOISE: + if (conf->odr >=3D INV_ICM45600_ODR_6_25HZ_LP && + conf->odr <=3D INV_ICM45600_ODR_1_5625HZ_LP) { + conf->mode =3D INV_ICM45600_SENSOR_MODE_LOW_POWER; + conf->filter =3D INV_ICM45600_GYRO_LP_AVG_SEL_8X; + } + break; + default: + break; + } + + /* set GYRO_CONFIG0 register (gyro fullscale & odr) */ + if (conf->fs !=3D oldconf->fs || conf->odr !=3D oldconf->odr) { + val =3D INV_ICM45600_GYRO_CONFIG0_FS(conf->fs) | + INV_ICM45600_GYRO_CONFIG0_ODR(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) { + ret =3D regmap_update_bits(st->map, INV_ICM45600_IPREG_SYS1_REG_170, + INV_ICM45600_IPREG_SYS1_REG_170_MASK, conf->filter); + 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); + + return 0; +} + +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 INV_ICM45600_PWR_MGMT0_GYRO(conf->gyro.mode) | + INV_ICM45600_PWR_MGMT0_ACCEL(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 INV_ICM45600_GYRO_CONFIG0_FS(conf->gyro.fs) | + INV_ICM45600_GYRO_CONFIG0_ODR(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 INV_ICM45600_ACCEL_CONFIG0_FS(conf->accel.fs) | + INV_ICM45600_ACCEL_CONFIG0_ODR(conf->accel.odr); + ret =3D regmap_write(st->map, INV_ICM45600_REG_ACCEL_CONFIG0, val); + if (ret) + return ret; + + /* update internal conf */ + st->conf =3D *conf; + + return 0; +} + +/** + * inv_icm45600_setup() - check and setup chip + * @st: driver internal state + * @bus_setup: callback for setting up bus specific registers + * + * Returns 0 on success, a negative error code otherwise. + */ +static int inv_icm45600_setup(struct inv_icm45600_state *st, bool reset, + inv_icm45600_bus_setup bus_setup) +{ + const struct inv_icm45600_hw *hw =3D &inv_icm45600_hw[st->chip]; + 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 hw->whoami) { + dev_err(dev, "invalid whoami %#02x expected %#02x (%s)\n", + val, hw->whoami, hw->name); + return -ENODEV; + } + st->name =3D hw->name; + + 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; + msleep(INV_ICM45600_RESET_TIME_MS); + + 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, hw->conf); +} + +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 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; + } + ret =3D inv_icm45600_buffer_fifo_parse(st); + if (ret) + dev_err(dev, "FIFO parsing error %d\n", ret); + } + + /* 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 timestamp */ + 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_vdd_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->vdd_supply); + if (ret) + dev_err(dev, "failed to disable vdd error %d\n", ret); +} + +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); +} + +static void inv_icm45600_disable_pm(void *_data) +{ + struct device *dev =3D _data; + + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); +} + +int inv_icm45600_core_probe(struct regmap *regmap, int chip, 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; + + if (chip <=3D INV_CHIP_INVALID || chip >=3D INV_CHIP_NB) { + dev_err(dev, "invalid chip =3D %d\n", chip); + return -ENODEV; + } + + /* get INT1 only supported interrupt */ + fwnode =3D dev_fwnode(dev); + if (!fwnode) + return -ENODEV; + irq =3D fwnode_irq_get_byname(fwnode, "INT1"); + if (irq < 0) { + if (irq !=3D -EPROBE_DEFER) + dev_err(dev, "error missing INT1 interrupt\n"); + return irq; + } + + irq_type =3D irq_get_trigger_type(irq); + if (!irq_type) + irq_type =3D IRQF_TRIGGER_FALLING; + + 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)) { + dev_err(dev, "Failed to register icm45600 regmap %ld\n", PTR_ERR(regmap_= custom)); + return PTR_ERR(regmap_custom); + } + + st =3D devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + dev_set_drvdata(dev, st); + mutex_init(&st->lock); + st->chip =3D chip; + st->map =3D regmap_custom; + + ret =3D iio_read_mount_matrix(dev, &st->orientation); + if (ret) { + dev_err(dev, "failed to retrieve mounting matrix %d\n", ret); + return ret; + } + + st->vdd_supply =3D devm_regulator_get(dev, "vdd"); + if (IS_ERR(st->vdd_supply)) + return PTR_ERR(st->vdd_supply); + + st->vddio_supply =3D devm_regulator_get(dev, "vddio"); + if (IS_ERR(st->vddio_supply)) + return PTR_ERR(st->vddio_supply); + + ret =3D regulator_enable(st->vdd_supply); + if (ret) + return ret; + msleep(INV_ICM45600_POWER_UP_TIME_MS); + + ret =3D devm_add_action_or_reset(dev, inv_icm45600_disable_vdd_reg, st); + if (ret) + return ret; + + 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, reset, bus_setup); + if (ret) + return ret; + + ret =3D inv_icm45600_timestamp_setup(st); + if (ret) + return ret; + + ret =3D inv_icm45600_buffer_init(st); + if (ret) + return ret; + + 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; + + /* setup runtime power management */ + ret =3D pm_runtime_set_active(dev); + if (ret) + return ret; + pm_runtime_get_noresume(dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, INV_ICM45600_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_put(dev); + + return devm_add_action_or_reset(dev, inv_icm45600_disable_pm, dev); +} +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; + + /* disable FIFO data streaming */ + if (st->fifo.on) { + ret =3D regmap_clear_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3, + INV_ICM45600_FIFO_CONFIG3_IF_EN); + if (ret) + return ret; + ret =3D regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0, + INV_ICM45600_FIFO_CONFIG0_MODE_MASK, + INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS); + 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) + return ret; + + regulator_disable(st->vddio_supply); + + return ret; +} + +/* + * 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); + 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; + + 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 */ + 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) { + inv_sensors_timestamp_reset(&gyro_st->ts); + inv_sensors_timestamp_reset(&accel_st->ts); + ret =3D regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0, + INV_ICM45600_FIFO_CONFIG0_MODE_MASK, + INV_ICM45600_FIFO_CONFIG0_MODE_STREAM); + if (ret) + return ret; + ret =3D regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3, + INV_ICM45600_FIFO_CONFIG3_IF_EN); + } + + return ret; +} + +/* Runtime suspend will turn off sensors that are enabled by iio devices. = */ +static int inv_icm45600_runtime_suspend(struct device *dev) +{ + struct inv_icm45600_state *st =3D dev_get_drvdata(dev); + int ret; + + guard(mutex)(&st->lock); + + /* disable all sensors */ + ret =3D inv_icm45600_set_pwr_mgmt0(st, INV_ICM45600_SENSOR_MODE_OFF, + INV_ICM45600_SENSOR_MODE_OFF, NULL); + if (ret) + return ret; + + regulator_disable(st->vddio_supply); + + return ret; +} + +/* 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); + int ret; + + guard(mutex)(&st->lock); + + ret =3D inv_icm45600_enable_regulator_vddio(st); + + return ret; +} + + +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"); 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..4ada93f990c1ffdc1e0a00ae7c7= 8e03c09d1c682 --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c @@ -0,0 +1,919 @@ +// 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_temp.h" +#include "inv_icm45600.h" + + +#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, \ + } + +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 char * const inv_icm45600_gyro_power_mode_items[] =3D { + "low-noise", + "low-power", +}; +static const int inv_icm45600_gyro_power_mode_values[] =3D { + INV_ICM45600_SENSOR_MODE_LOW_NOISE, + INV_ICM45600_SENSOR_MODE_LOW_POWER, +}; + +static int inv_icm45600_gyro_power_mode_set(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int idx) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + struct inv_icm45600_sensor_state *gyro_st =3D iio_priv(indio_dev); + int power_mode; + + if (chan->type !=3D IIO_ANGL_VEL) + return -EINVAL; + + if (idx >=3D ARRAY_SIZE(inv_icm45600_gyro_power_mode_values)) + return -EINVAL; + + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + + power_mode =3D inv_icm45600_gyro_power_mode_values[idx]; + + guard(mutex)(&st->lock); + + /* prevent change if power mode is not supported by the ODR */ + switch (power_mode) { + case INV_ICM45600_SENSOR_MODE_LOW_NOISE: + if (st->conf.gyro.odr >=3D INV_ICM45600_ODR_6_25HZ_LP && + st->conf.gyro.odr <=3D INV_ICM45600_ODR_1_5625HZ_LP) + return -EPERM; + break; + case INV_ICM45600_SENSOR_MODE_LOW_POWER: + default: + if (st->conf.gyro.odr <=3D INV_ICM45600_ODR_800HZ_LN) + return -EPERM; + break; + } + + gyro_st->power_mode =3D power_mode; + + return 0; +} + +static int inv_icm45600_gyro_power_mode_get(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + 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; + int power_mode; + + if (chan->type !=3D IIO_ANGL_VEL) + return -EINVAL; + + guard(mutex)(&st->lock); + + /* if sensor is on, returns actual power mode and not configured one */ + switch (st->conf.gyro.mode) { + case INV_ICM45600_SENSOR_MODE_LOW_POWER: + case INV_ICM45600_SENSOR_MODE_LOW_NOISE: + power_mode =3D st->conf.gyro.mode; + break; + default: + power_mode =3D gyro_st->power_mode; + break; + } + + for (idx =3D 0; idx < ARRAY_SIZE(inv_icm45600_gyro_power_mode_values); ++= idx) { + if (power_mode =3D=3D inv_icm45600_gyro_power_mode_values[idx]) + break; + } + if (idx >=3D ARRAY_SIZE(inv_icm45600_gyro_power_mode_values)) + return -EINVAL; + + return idx; +} + +static const struct iio_enum inv_icm45600_gyro_power_mode_enum =3D { + .items =3D inv_icm45600_gyro_power_mode_items, + .num_items =3D ARRAY_SIZE(inv_icm45600_gyro_power_mode_items), + .set =3D inv_icm45600_gyro_power_mode_set, + .get =3D inv_icm45600_gyro_power_mode_get, +}; + +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), + IIO_ENUM_AVAILABLE("power_mode", IIO_SHARED_BY_TYPE, + &inv_icm45600_gyro_power_mode_enum), + IIO_ENUM("power_mode", IIO_SHARED_BY_TYPE, + &inv_icm45600_gyro_power_mode_enum), + {}, +}; + +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; + int16_t temp; + aligned_s64 timestamp; +}; + +#define INV_ICM45600_SCAN_MASK_GYRO_3AXIS \ + (BIT(INV_ICM45600_GYRO_SCAN_X) | \ + BIT(INV_ICM45600_GYRO_SCAN_Y) | \ + BIT(INV_ICM45600_GYRO_SCAN_Z)) + +#define INV_ICM45600_SCAN_MASK_TEMP BIT(INV_ICM45600_GYRO_SCAN_TEMP) + +static const unsigned long inv_icm45600_gyro_scan_masks[] =3D { + /* 3-axis gyro + temperature */ + INV_ICM45600_SCAN_MASK_GYRO_3AXIS | INV_ICM45600_SCAN_MASK_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_gyro =3D 0; + unsigned int sleep_temp =3D 0; + unsigned int sleep; + int ret; + + scoped_guard(mutex, &st->lock) { + if (*scan_mask & INV_ICM45600_SCAN_MASK_TEMP) + fifo_en |=3D INV_ICM45600_SENSOR_TEMP; + + if (*scan_mask & INV_ICM45600_SCAN_MASK_GYRO_3AXIS) { + /* enable gyro sensor */ + conf.mode =3D gyro_st->power_mode; + ret =3D inv_icm45600_set_gyro_conf(st, &conf, &sleep_gyro); + if (ret) + break; + 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 maximum required time */ + sleep =3D max(sleep_gyro, sleep_temp); + if (sleep) + msleep(sleep); + return ret; +} + +static int inv_icm45600_gyro_read_sensor(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int16_t *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); + struct inv_icm45600_sensor_conf conf =3D INV_ICM45600_SENSOR_CONF_INIT; + unsigned int reg; + __le16 *data; + 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; + } + + pm_runtime_get_sync(dev); + scoped_guard(mutex, &st->lock) { + /* enable gyro sensor */ + conf.mode =3D gyro_st->power_mode; + ret =3D inv_icm45600_set_gyro_conf(st, &conf, NULL); + if (ret) + break; + + /* read gyro register data */ + data =3D (__le16 *)&st->buffer[0]; + ret =3D regmap_bulk_read(st->map, reg, data, sizeof(*data)); + if (ret) + break; + + *val =3D (int16_t)le16_to_cpup(data); + if (*val =3D=3D INV_ICM45600_DATA_INVALID) + ret =3D -EINVAL; + } + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return ret; +} + +/* IIO format int + nano */ +static const int inv_icm45600_gyro_scale[] =3D { + /* +/- 2000dps =3D> 0.001065264 rad/s */ + [2 * INV_ICM45600_GYRO_FS_2000DPS] =3D 0, + [2 * INV_ICM45600_GYRO_FS_2000DPS + 1] =3D 1065264, + /* +/- 1000dps =3D> 0.000532632 rad/s */ + [2 * INV_ICM45600_GYRO_FS_1000DPS] =3D 0, + [2 * INV_ICM45600_GYRO_FS_1000DPS + 1] =3D 532632, + /* +/- 500dps =3D> 0.000266316 rad/s */ + [2 * INV_ICM45600_GYRO_FS_500DPS] =3D 0, + [2 * INV_ICM45600_GYRO_FS_500DPS + 1] =3D 266316, + /* +/- 250dps =3D> 0.000133158 rad/s */ + [2 * INV_ICM45600_GYRO_FS_250DPS] =3D 0, + [2 * INV_ICM45600_GYRO_FS_250DPS + 1] =3D 133158, + /* +/- 125dps =3D> 0.000066579 rad/s */ + [2 * INV_ICM45600_GYRO_FS_125DPS] =3D 0, + [2 * INV_ICM45600_GYRO_FS_125DPS + 1] =3D 66579, + /* +/- 62.5dps =3D> 0.000033290 rad/s */ + [2 * INV_ICM45600_GYRO_FS_62_5DPS] =3D 0, + [2 * INV_ICM45600_GYRO_FS_62_5DPS + 1] =3D 33290, + /* +/- 31.25dps =3D> 0.000016645 rad/s */ + [2 * INV_ICM45600_GYRO_FS_31_25DPS] =3D 0, + [2 * INV_ICM45600_GYRO_FS_31_25DPS + 1] =3D 16645, + /* +/- 15.625dps =3D> 0.000008322 rad/s */ + [2 * INV_ICM45600_GYRO_FS_15_625DPS] =3D 0, + [2 * INV_ICM45600_GYRO_FS_15_625DPS + 1] =3D 8322, +}; + +/* IIO format int + nano */ +static const int inv_icm45686_gyro_scale[] =3D { + /* +/- 4000dps =3D> 0.002130529 rad/s */ + [2 * INV_ICM45686_GYRO_FS_4000DPS] =3D 0, + [2 * INV_ICM45686_GYRO_FS_4000DPS + 1] =3D 2130529, + /* +/- 2000dps =3D> 0.001065264 rad/s */ + [2 * INV_ICM45686_GYRO_FS_2000DPS] =3D 0, + [2 * INV_ICM45686_GYRO_FS_2000DPS + 1] =3D 1065264, + /* +/- 1000dps =3D> 0.000532632 rad/s */ + [2 * INV_ICM45686_GYRO_FS_1000DPS] =3D 0, + [2 * INV_ICM45686_GYRO_FS_1000DPS + 1] =3D 532632, + /* +/- 500dps =3D> 0.000266316 rad/s */ + [2 * INV_ICM45686_GYRO_FS_500DPS] =3D 0, + [2 * INV_ICM45686_GYRO_FS_500DPS + 1] =3D 266316, + /* +/- 250dps =3D> 0.000133158 rad/s */ + [2 * INV_ICM45686_GYRO_FS_250DPS] =3D 0, + [2 * INV_ICM45686_GYRO_FS_250DPS + 1] =3D 133158, + /* +/- 125dps =3D> 0.000066579 rad/s */ + [2 * INV_ICM45686_GYRO_FS_125DPS] =3D 0, + [2 * INV_ICM45686_GYRO_FS_125DPS + 1] =3D 66579, + /* +/- 62.5dps =3D> 0.000033290 rad/s */ + [2 * INV_ICM45686_GYRO_FS_62_5DPS] =3D 0, + [2 * INV_ICM45686_GYRO_FS_62_5DPS + 1] =3D 33290, + /* +/- 31.25dps =3D> 0.000016645 rad/s */ + [2 * INV_ICM45686_GYRO_FS_31_25DPS] =3D 0, + [2 * INV_ICM45686_GYRO_FS_31_25DPS + 1] =3D 16645, + /* +/- 15.625dps =3D> 0.000008322 rad/s */ + [2 * INV_ICM45686_GYRO_FS_15_625DPS] =3D 0, + [2 * INV_ICM45686_GYRO_FS_15_625DPS + 1] =3D 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 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 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 inv_icm45600_gyro_scale) + conf.fs++; + + pm_runtime_get_sync(dev); + scoped_guard(mutex, &st->lock) { + ret =3D inv_icm45600_set_gyro_conf(st, &conf, NULL); + } + pm_runtime_mark_last_busy(dev); + 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 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 inv_sensors_timestamp *ts =3D &gyro_st->ts; + 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 < 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; + + conf.odr =3D inv_icm45600_gyro_odr_conv[idx / 2]; + + pm_runtime_get_sync(dev); + scoped_guard(mutex, &st->lock) { + ret =3D inv_sensors_timestamp_update_odr(ts, inv_icm45600_odr_to_period(= conf.odr), + iio_buffer_enabled(indio_dev)); + if (ret) + break; + + 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) + break; + inv_icm45600_buffer_update_fifo_period(st); + inv_icm45600_buffer_update_watermark(st); + } + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +/* + * Calibration bias values, IIO range format int + nano. + * Value is limited to +/-64dps coded on 12 bits signed. Step is 1/32 dps. + */ +static int inv_icm45600_gyro_calibbias[] =3D { + -1, 117010721, /* min: -1.117010721 rad/s */ + 0, 545415, /* step: 0.000545415 rad/s */ + 1, 116465306, /* max: 1.116465306 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); + int64_t val64; + int32_t bias; + unsigned int reg; + int16_t offset; + uint8_t data[2]; + 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; + } + + pm_runtime_get_sync(dev); + scoped_guard(mutex, &st->lock) { + ret =3D regmap_bulk_read(st->map, reg, st->buffer, sizeof(data)); + memcpy(data, st->buffer, sizeof(data)); + } + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + if (ret) + return ret; + + /* 12 bits signed value */ + switch (chan->channel2) { + case IIO_MOD_X: + offset =3D sign_extend32(((data[1] & 0x0F) << 8) | data[0], 11); + break; + case IIO_MOD_Y: + offset =3D sign_extend32(((data[0] & 0xF0) << 4) | data[1], 11); + break; + case IIO_MOD_Z: + offset =3D sign_extend32(((data[1] & 0x0F) << 8) | data[0], 11); + break; + default: + return -EINVAL; + } + + /* + * convert raw offset to dps then to rad/s + * 12 bits signed raw max 64 to dps: 64 / 2048 + * dps to rad: Pi / 180 + * result in nano (1000000000) + * (offset * 64 * Pi * 1000000000) / (2048 * 180) + */ + val64 =3D (int64_t)offset * 64LL * 3141592653LL; + /* for rounding, add + or - divisor (2048 * 180) divided by 2 */ + if (val64 >=3D 0) + val64 +=3D 2048 * 180 / 2; + else + val64 -=3D 2048 * 180 / 2; + bias =3D div_s64(val64, 2048 * 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); + int64_t val64, min, max; + unsigned int reg; + int16_t 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 (int64_t)inv_icm45600_gyro_calibbias[0] * 1000000000LL + + (int64_t)inv_icm45600_gyro_calibbias[1]; + max =3D (int64_t)inv_icm45600_gyro_calibbias[4] * 1000000000LL + + (int64_t)inv_icm45600_gyro_calibbias[5]; + val64 =3D (int64_t)val * 1000000000LL + (int64_t)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; + + pm_runtime_get_sync(dev); + scoped_guard(mutex, &st->lock) { + ret =3D regmap_bulk_write(st->map, reg, st->buffer, 2); + } + pm_runtime_mark_last_busy(dev); + 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); + int16_t data; + 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, &data); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + *val =3D data; + 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); + int ret; + + guard(mutex)(&st->lock); + + st->fifo.watermark.gyro =3D val; + ret =3D inv_icm45600_buffer_update_watermark(st); + + return ret; +} + +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->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); + + switch (st->chip) { + case INV_CHIP_ICM45686: + case INV_CHIP_ICM45688P: + case INV_CHIP_ICM45689: + case INV_CHIP_ICM45687: + gyro_st->scales =3D inv_icm45686_gyro_scale; + gyro_st->scales_len =3D ARRAY_SIZE(inv_icm45686_gyro_scale); + break; + default: + gyro_st->scales =3D inv_icm45600_gyro_scale; + gyro_st->scales_len =3D ARRAY_SIZE(inv_icm45600_gyro_scale); + /* Set Gyro default FSR */ + ret =3D regmap_update_bits(st->map, INV_ICM45600_REG_GYRO_CONFIG0, + INV_ICM45600_GYRO_CONFIG0_FS_MASK, + INV_ICM45600_GYRO_CONFIG0_FS_2000DPS); + if (ret) + return ERR_PTR(ret); + break; + } + /* 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 void *accel, *gyro, *timestamp; + const int8_t *temp; + unsigned int odr; + int64_t 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; +} diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_temp.c b/drivers/iio= /imu/inv_icm45600/inv_icm45600_temp.c new file mode 100644 index 0000000000000000000000000000000000000000..b2b5697da407de0a0338841eb85= 8b4322996923c --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_temp.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_temp.h" +#include "inv_icm45600.h" + +static int inv_icm45600_temp_read(struct inv_icm45600_state *st, int16_t *= temp) +{ + struct device *dev =3D regmap_get_device(st->map); + __le16 *raw; + int ret; + + pm_runtime_get_sync(dev); + scoped_guard(mutex, &st->lock) { + raw =3D (__le16 *)&st->buffer[0]; + ret =3D regmap_bulk_read(st->map, INV_ICM45600_REG_TEMP_DATA, raw, sizeo= f(*raw)); + if (ret) + break; + + *temp =3D (int16_t)le16_to_cpup(raw); + if (*temp =3D=3D INV_ICM45600_DATA_INVALID) + ret =3D -EINVAL; + } + pm_runtime_mark_last_busy(dev); + 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); + int16_t temp; + int ret; + + if (chan->type !=3D IIO_TEMP) + return -EINVAL; + + /* temperature sensor work only with accel and/or gyro */ + if (st->conf.accel.mode <=3D INV_ICM45600_SENSOR_MODE_STANDBY && + st->conf.gyro.mode <=3D INV_ICM45600_SENSOR_MODE_STANDBY) { + return -ENODATA; + } + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret =3D inv_icm45600_temp_read(st, &temp); + iio_device_release_direct(indio_dev); + 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; + } +} diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_temp.h b/drivers/iio= /imu/inv_icm45600/inv_icm45600_temp.h new file mode 100644 index 0000000000000000000000000000000000000000..1b93e1417e2ec1292e44f05b98c= 6393354c5297c --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_temp.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2025 Invensense, Inc. + */ + +#ifndef INV_ICM45600_TEMP_H_ +#define INV_ICM45600_TEMP_H_ + +#include + +#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); + +#endif --=20 2.34.1 From nobody Sat Sep 13 22:31:01 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 0AE3A19A28D; Fri, 11 Apr 2025 13:28:41 +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=1744378122; cv=none; b=XZEJYA0AxR9NwUejOvwFYbFlGM+HU3yz9MF4wNL2RPceFK5JU6Qms11fS5okOBqq+zAtjImebkOq2t3eaxElXIl9/ss1mqBgihyGCBoUOqs/waxG9dEmAP2awsmqhQ7IvGZUCrJJAR4osXBNZy20G1LPRmFQ/oemisEkAFP+VvM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744378122; c=relaxed/simple; bh=GQpDhvOw4vXgDuXyes+BHzlaE27xz0vtE396S51W9dU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=OS9Yh+WfNE7rh+ve7fBexArn4Gw3JiA8EozjtY8AY/yAwVHXNrC0PU4vTdruGK3FSBM9nCuBW4xEQc3NdTeSGIORsuNQ8E04dME1OsdUqHPh/i1+XuzvAJiUMATfDresvGJaLaETmp8YceQ+qchGcajyn4LTw9/Cs6zAAunltiI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=nU9nF7lF; 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="nU9nF7lF" Received: by smtp.kernel.org (Postfix) with ESMTPS id 869FFC4CEE8; Fri, 11 Apr 2025 13:28:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1744378121; bh=GQpDhvOw4vXgDuXyes+BHzlaE27xz0vtE396S51W9dU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=nU9nF7lFHLJ/MNqS6H1x3Y+PeDcLDHYBW+FaQTk88F/LuceJU7uM2U8Vnyk6fKiCR OzDETRh1Zflzn4lNDnLZYx5egDQfNAq2c7QDzzEZP+tJuvSI3vGqavf5dhUj8FQAjF S+emkRDwDrcl8AN6iyGjSCys5VXvOSoYecxr3bdd1QnqKSMx3nVsaRPPfBerQyjjbd 3QFo/ylM+/Iosx1j/HS1fAd0W4ctUuzlcHHCTifQcQukbDHNpkvwerPj4CREbFbz2h aBMkt1HI6pnbiCFr0+osp/oYJxtngC45kWEL45RGaeGOoB3m/aKUgzXd474YKPZjMo Xf7OmAzXlyIgQ== 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 7AE0FC369A9; Fri, 11 Apr 2025 13:28:41 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Fri, 11 Apr 2025 13:28:34 +0000 Subject: [PATCH 2/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: <20250411-add_newport_driver-v1-2-15082160b019@tdk.com> References: <20250411-add_newport_driver-v1-0-15082160b019@tdk.com> In-Reply-To: <20250411-add_newport_driver-v1-0-15082160b019@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=1744378119; l=3387; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=zvnek+WzKcKPqMBcpGOllwv/2GulHHduPuhOxyb8B+A=; b=BcxzJbKACafNrxxo6i1IRkESitg/K8jcjxJBoVAyzwizlkSHE+F+Nnm8e11x55+uMPeOU4cjc x+JNcNKqJAiAZPT7kf1EqKRjUge6bh3vAljqJsMNeZxU/XKvCZ4eexW 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-456xxx devices. Signed-off-by: Remi Buisson --- drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c | 103 ++++++++++++++++++++= ++++ 1 file changed, 103 insertions(+) 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..93fa9e7570846b6dfc3cfb96a2d= 6c058da3cc746 --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_i2c.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 InvenSense, Inc. + */ + +#include +#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 void *match; + enum inv_icm45600_chip chip; + struct regmap *regmap; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) + return -ENOTSUPP; + + match =3D device_get_match_data(&client->dev); + if (!match) + return -EINVAL; + chip =3D (uintptr_t)match; + + 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, true, NULL); +} + +/* + * device id table is used to identify what device can be + * supported by this driver + */ +static const struct i2c_device_id inv_icm45600_id[] =3D { + { "icm45605", INV_CHIP_ICM45605 }, + { "icm45686", INV_CHIP_ICM45686 }, + { "icm45688p", INV_CHIP_ICM45688P }, + { "icm45608", INV_CHIP_ICM45608 }, + { "icm45634", INV_CHIP_ICM45634 }, + { "icm45689", INV_CHIP_ICM45689 }, + { "icm45606", INV_CHIP_ICM45606 }, + { "icm45687", INV_CHIP_ICM45687 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, inv_icm45600_id); + +static const struct of_device_id inv_icm45600_of_matches[] =3D { + { + .compatible =3D "invensense,icm45605", + .data =3D (void *)INV_CHIP_ICM45605, + }, { + .compatible =3D "invensense,icm45686", + .data =3D (void *)INV_CHIP_ICM45686, + }, { + .compatible =3D "invensense,icm45688p", + .data =3D (void *)INV_CHIP_ICM45688P, + }, { + .compatible =3D "invensense,icm45608", + .data =3D (void *)INV_CHIP_ICM45608, + }, { + .compatible =3D "invensense,icm45634", + .data =3D (void *)INV_CHIP_ICM45634, + }, { + .compatible =3D "invensense,icm45689", + .data =3D (void *)INV_CHIP_ICM45689, + }, { + .compatible =3D "invensense,icm45606", + .data =3D (void *)INV_CHIP_ICM45606, + }, { + .compatible =3D "invensense,icm45687", + .data =3D (void *)INV_CHIP_ICM45687, + }, + {} +}; +MODULE_DEVICE_TABLE(of, inv_icm45600_of_matches); + +static struct i2c_driver inv_icm45600_driver =3D { + .driver =3D { + .name =3D "inv-icm45600-i2c", + .of_match_table =3D inv_icm45600_of_matches, + .pm =3D pm_ptr(&inv_icm45600_pm_ops), + }, + .id_table =3D inv_icm45600_id, + .probe =3D inv_icm45600_probe, +}; +module_i2c_driver(inv_icm45600_driver); + +MODULE_AUTHOR("InvenSense, Inc."); +MODULE_DESCRIPTION("InvenSense ICM-456xx I2C driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_ICM45600"); --=20 2.34.1 From nobody Sat Sep 13 22:31:01 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 61F971AAE17; Fri, 11 Apr 2025 13:28:42 +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=1744378122; cv=none; b=GizfgGQRjaMyMwXaIuaD0WzOAc5yDyzeTK/mxaetBLjVguFrJVavEbIxI3Al0gubXoUSgvQ0hiGPJIBsHsjE1SxaT43vHC37DFjPjqnag+iDkbG4zjCDT8QgM+fe8fDt/5aEnSqhMrFWfpaquGdVCuBDwruCA8lZ3ETvT4YoQJ0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744378122; c=relaxed/simple; bh=zdCSDIXvp4S4/c3ewltB0IiwGxcP/fWw9CcKslvterA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=EjOmePacKJvAwl+IE45PXYCenLG2tMluM1iij7LIzJDJ6xojpw1rlUMrmwPsa5Qs5gpKMs76V27wh2xkylcAi695d58xwXC1iyYILWtVFoeacHdszltbDXyGSU/8KJx5TMW8smbEPLpeJpYGg3TaKGBR9D4/eKvgHfNsFuzx/oM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=uN1j2jzr; 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="uN1j2jzr" Received: by smtp.kernel.org (Postfix) with ESMTPS id 9D15BC4CEEC; Fri, 11 Apr 2025 13:28:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1744378121; bh=zdCSDIXvp4S4/c3ewltB0IiwGxcP/fWw9CcKslvterA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=uN1j2jzr9d6xeoIxjv2yS/F9b/Bha+FVEhQbhhQ5Ds9XbIex62d0uxORn4zkBsY1L 4u1/Yl/dbSmXDMMtYJjkpGoWmZV41EGfZh4CKRWx5luaHEFDlmSU7sJ5gD8APnsavx AZx9fmCOt8gL6C2M+xh+73FMaNgwQ5LwOv4Suc46Wj1F1S00Jr+LoOZcat7IKf7BOf m6C0YgS6K3lQOMwrK30OgRf6bFnKkCiQjYFUqovXx9Z/Ue2horFRQJnWLQge4XeLwy kACi2zAB2z/gW3uIidGWKVcOGVWKKFFOxn3d3frQoqYSpz+uMxGbA8Np0LWD9gU+ai JJ7wydAj/KgEw== 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 8ADA2C36010; Fri, 11 Apr 2025 13:28:41 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Fri, 11 Apr 2025 13:28:35 +0000 Subject: [PATCH 3/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: <20250411-add_newport_driver-v1-3-15082160b019@tdk.com> References: <20250411-add_newport_driver-v1-0-15082160b019@tdk.com> In-Reply-To: <20250411-add_newport_driver-v1-0-15082160b019@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=1744378119; l=3615; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=kqX0rbDG6+UEQFF0zrY6sK4rRJYJeEFgrhZxCbw57Rw=; b=AP71d1UNFHiRuw1VvbmHLzAqFCdSRmI2MGJnQlK6npqHwBzB8nj8UZ3pviuLvPVUIW93GETU1 n3Vp53wooyrCujcPNU9kKW7wjYIx923N5q9OsRaKY/g4bhcNk9/xjvU 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-456xxx devices. Signed-off-by: Remi Buisson --- drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c | 110 ++++++++++++++++++++= ++++ 1 file changed, 110 insertions(+) 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..fba0953343aaf9a6009a740d3a6= e6446b5bea53f --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_spi.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 InvenSense, Inc. + */ + +#include +#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, + INV_ICM45600_SPI_SLEW_RATE_5NS); +} + +static int inv_icm45600_probe(struct spi_device *spi) +{ + const void *match; + enum inv_icm45600_chip chip; + struct regmap *regmap; + + match =3D device_get_match_data(&spi->dev); + if (!match) + return -EINVAL; + chip =3D (uintptr_t)match; + + /* 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, true, + inv_icm45600_spi_bus_setup); +} + +/* + * device id table is used to identify what device can be + * supported by this driver + */ +static const struct spi_device_id inv_icm45600_id[] =3D { + { "icm45605", INV_CHIP_ICM45605 }, + { "icm45686", INV_CHIP_ICM45686 }, + { "icm45688p", INV_CHIP_ICM45688P }, + { "icm45608", INV_CHIP_ICM45608 }, + { "icm45634", INV_CHIP_ICM45634 }, + { "icm45689", INV_CHIP_ICM45689 }, + { "icm45606", INV_CHIP_ICM45606 }, + { "icm45687", INV_CHIP_ICM45687 }, + { } +}; +MODULE_DEVICE_TABLE(spi, inv_icm45600_id); + +static const struct of_device_id inv_icm45600_of_matches[] =3D { + { + .compatible =3D "invensense,icm45605", + .data =3D (void *)INV_CHIP_ICM45605, + }, { + .compatible =3D "invensense,icm45686", + .data =3D (void *)INV_CHIP_ICM45686, + }, { + .compatible =3D "invensense,icm45688p", + .data =3D (void *)INV_CHIP_ICM45688P, + }, { + .compatible =3D "invensense,icm45608", + .data =3D (void *)INV_CHIP_ICM45608, + }, { + .compatible =3D "invensense,icm45634", + .data =3D (void *)INV_CHIP_ICM45634, + }, { + .compatible =3D "invensense,icm45689", + .data =3D (void *)INV_CHIP_ICM45689, + }, { + .compatible =3D "invensense,icm45606", + .data =3D (void *)INV_CHIP_ICM45606, + }, { + .compatible =3D "invensense,icm45687", + .data =3D (void *)INV_CHIP_ICM45687, + }, + {} +}; +MODULE_DEVICE_TABLE(of, inv_icm45600_of_matches); + +static struct spi_driver inv_icm45600_driver =3D { + .driver =3D { + .name =3D "inv-icm45600-spi", + .of_match_table =3D inv_icm45600_of_matches, + .pm =3D pm_ptr(&inv_icm45600_pm_ops), + }, + .id_table =3D inv_icm45600_id, + .probe =3D inv_icm45600_probe, +}; +module_spi_driver(inv_icm45600_driver); + +MODULE_AUTHOR("InvenSense, Inc."); +MODULE_DESCRIPTION("InvenSense ICM-456xx SPI driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_ICM45600"); --=20 2.34.1 From nobody Sat Sep 13 22:31:01 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 748B61B3F3D; Fri, 11 Apr 2025 13:28:42 +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=1744378122; cv=none; b=rCLA0LbqiK/DxodTejARRzr9znTaRbSGOl6sy6BmHOLPE7wN+MoJiF4I6yEdcOCEX9l2y8IBketWAUnuLXq54oxaetIESwLuVjAw1ynrsmuRFLKqinKtaLBJ8qWfGmIuRW/9o9illW5vn8gc6af8HSzZm1m7Ux3gZ1a+10RB8Ok= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744378122; c=relaxed/simple; bh=Hns3lEJU2PrajODTTCnU56xq9Xnx68YLhhUSEVwSqIo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=dQSoaT45vt72SPMeI7TWshBn+9Pw16EgT/pSzCwDY1o35jDJITkvGZF7KoEByyDC1lfBA2+ss5Q3S4d1DMErDhwot4006rM6XAHsTvyF1bztT8YsLcUQFHfTyVTxS4OYRJMQDBSfwCoJpaPlOyiazqPirWDhIMLAcJVEXjHcfVE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=dJcieiro; 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="dJcieiro" Received: by smtp.kernel.org (Postfix) with ESMTPS id AA16CC4CEF3; Fri, 11 Apr 2025 13:28:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1744378121; bh=Hns3lEJU2PrajODTTCnU56xq9Xnx68YLhhUSEVwSqIo=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=dJcieiroiyGs2+dcWAinR2TXgzTG3fw3hHf51eXtiuHkbT411V78zj2CWFqVwFNy9 YMDgtTaW3Jr5Rqkyx5ik/5UAgaOM2VzObaMXIz4iUGvCqFdOMimA0oJvejXuC2mzNI +Rh7q1jM84ymY5RLxOiXNyXeyytgEWQusLIVNUqvxAv20hcVJXoB6/GH9kMcGz3Vbl F5RRQxjvpB8r020FHaify2HMiyzRUlPzDiNR88MAm+LTvZm09stgRPxB084FbAGgYJ pkfXyKeN+6P7j6iqjI7U87of6yJ9qg3xB4kIbMMfQT7fgfr5+OkukLM7PGA3fxzHDq zGsMB8wZlk1Ig== 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 9EC80C369AF; Fri, 11 Apr 2025 13:28:41 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Fri, 11 Apr 2025 13:28:36 +0000 Subject: [PATCH 4/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: <20250411-add_newport_driver-v1-4-15082160b019@tdk.com> References: <20250411-add_newport_driver-v1-0-15082160b019@tdk.com> In-Reply-To: <20250411-add_newport_driver-v1-0-15082160b019@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=1744378119; l=3115; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=AAAjeWu8beshqeqWWoKXRzNOSHP5dILEmgEVvq21vZs=; b=r+DS/JcwPXMldmQvkfrQqwevn33FPPQWPoCJ9dxNeZC4oh0TXQdbl6GSrNwftU362KezarD96 389/OVBFAghCZCTmFEG3knzFzmwyYg/vkwzuoWCF7gtVTyyUxwGZTkC 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-456xxx devices. Signed-off-by: Remi Buisson --- drivers/iio/imu/inv_icm45600/inv_icm45600_i3c.c | 84 +++++++++++++++++++++= ++++ 1 file changed, 84 insertions(+) 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..da939a5da5da88cbbb4a41edd90= deae950cb9760 --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_i3c.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 InvenSense, Inc. + */ + +#include +#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 uint8_t inv_icm45600_i3c_code[INV_CHIP_NB] =3D { + [INV_CHIP_ICM45605] =3D INV_ICM45600_WHOAMI_ICM45605, + [INV_CHIP_ICM45686] =3D INV_ICM45600_WHOAMI_ICM45686, + [INV_CHIP_ICM45688P] =3D INV_ICM45600_WHOAMI_ICM45688P, + [INV_CHIP_ICM45608] =3D INV_ICM45600_WHOAMI_ICM45608, + [INV_CHIP_ICM45634] =3D INV_ICM45600_WHOAMI_ICM45634, + [INV_CHIP_ICM45689] =3D INV_ICM45600_WHOAMI_ICM45689, + [INV_CHIP_ICM45606] =3D INV_ICM45600_WHOAMI_ICM45606, + [INV_CHIP_ICM45687] =3D INV_ICM45600_WHOAMI_ICM45687 +}; + +static int inv_icm45600_i3c_probe(struct i3c_device *i3cdev) +{ + int ret; + unsigned int whoami; + enum inv_icm45600_chip chip; + struct regmap *regmap; + + 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 INV_CHIP_ICM45605; chip < INV_CHIP_NB; chip++) { + if (whoami =3D=3D inv_icm45600_i3c_code[chip]) + break; + } + + if (chip =3D=3D INV_CHIP_NB) { + dev_err(&i3cdev->dev, "Failed to match part id %d\n", whoami); + return -ENODEV; + } + + return inv_icm45600_core_probe(regmap, chip, false, NULL); +} + +static struct i3c_driver inv_icm45600_driver =3D { + .driver =3D { + .name =3D "inv_icm45600_i3c", + .pm =3D pm_sleep_ptr(&inv_icm45600_pm_ops), + }, + .probe =3D inv_icm45600_i3c_probe, + .id_table =3D inv_icm45600_i3c_ids, +}; +module_i3c_driver(inv_icm45600_driver); + +MODULE_AUTHOR("Remi Buisson "); +MODULE_DESCRIPTION("InvenSense ICM-456xx i3c driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_ICM45600"); --=20 2.34.1 From nobody Sat Sep 13 22:31:01 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 8713D1C6FF4; Fri, 11 Apr 2025 13:28:42 +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=1744378122; cv=none; b=r4gbhe61MAXUdE0g1n/zB3pb9lt28R+b4sOfpH2redeanjnvlAU/ELGDYTQ8YNjYeHwT5SKdQ+8V4H1QqFxV7WGKFbLeHhNTxggOLSSuHhDq0lsBu0aDMDJ2wUWeLbWVQ2tl/okIQN0yM5SMv1v+IAUGpjZ35sYgbOEdLBBESPc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744378122; c=relaxed/simple; bh=0nY/AtFpoH954yRrlAuta5Tv6bDZX8S17v1gqTdTo8g=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Vmg43WdOPPaaa0/gqgHsUVicztYL4iK8TtSXQFrhmWM4l99ur0BxKhYZySU34Fl2YqUpDdGsSgtO7ggZv854fIIqIcKmhlUNnhKkTKvv22kijscWDwRpzSlMa2Gz4pD1DRYPW6p8oO9rZDWEOOOBIkUQC8uMJqF656oENXdgfx8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=o3u4g3pt; 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="o3u4g3pt" Received: by smtp.kernel.org (Postfix) with ESMTPS id BA9DBC4CEF4; Fri, 11 Apr 2025 13:28:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1744378121; bh=0nY/AtFpoH954yRrlAuta5Tv6bDZX8S17v1gqTdTo8g=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=o3u4g3ptHX+0CgGY3p6yrXTUXYSnKKdCXEClQ3qS3L6xn24TSg6z83crJwsfGsam+ MumWhsca8qNEXZsExpAtm8f3T+85hyJFK4pFlVtZY0i9HHm02t4jRWq2Ju71+ftCPR 60L6Y5QaddfPyyN8VeveNhpVRRPSwUPrb5oIDkjNPA9gG7/BXEEZWTMYc4TldGut4q NZ38AR+yzghzoRINuX29wmnq5qpg9AH4BVo4CHPlqn2Zn/5Pniasr4OdYkO+w3cttK 8mHsrwZ7dEIcUPN0/BFiEzkI/H0XHyXMMn7BlsuzzvRUSRTmq+GuCeakRyOyCmQVmJ 7nZXP1yGuTNQw== 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 B0D81C369AE; Fri, 11 Apr 2025 13:28:41 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Fri, 11 Apr 2025 13:28:37 +0000 Subject: [PATCH 5/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: <20250411-add_newport_driver-v1-5-15082160b019@tdk.com> References: <20250411-add_newport_driver-v1-0-15082160b019@tdk.com> In-Reply-To: <20250411-add_newport_driver-v1-0-15082160b019@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=1744378119; l=21460; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=shWLFNOSbbL6CH4BKWqSsJcmmzEV7CGMLBJhh0eB/Dg=; b=jJjMV3KRKxjUYcvJlYudevJebWaPY/1G2u/GFKmRkKnSNBfPVm4Qts2WYSCjVi5fHZ+yS5UTz eSxnvLMpwI+CIwnaj/EMwqRb4m+MfHv1YsdjPuaME8kgXEWQc2P2uHe 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 all FIFO parsing and reading functions. Add accel and gyro kfifo buffer and FIFO data parsing. Use device interrupt for reading data FIFO and launching accel and gyro parsing. Support hwfifo watermark by multiplexing gyro and accel settings. Support hwfifo flush. Signed-off-by: Remi Buisson --- drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c | 572 +++++++++++++++++= ++++ drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h | 100 ++++ 2 files changed, 672 insertions(+) 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..ed6f46eb4865fa3299b0240cc5f= 520e0326a7648 --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c @@ -0,0 +1,572 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025 Invensense, Inc. + */ + +#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 { + uint8_t header; + struct inv_icm45600_fifo_sensor_data data; + int8_t temp; +} __packed; +#define INV_ICM45600_FIFO_1SENSOR_PACKET_SIZE 8 + +struct inv_icm45600_fifo_2sensors_packet { + uint8_t header; + struct inv_icm45600_fifo_sensor_data accel; + struct inv_icm45600_fifo_sensor_data gyro; + int8_t temp; + __le16 timestamp; +} __packed; +#define INV_ICM45600_FIFO_2SENSORS_PACKET_SIZE 16 + +ssize_t inv_icm45600_fifo_decode_packet(const void *packet, const void **a= ccel, + const void **gyro, const int8_t **temp, + const void **timestamp, unsigned int *odr) +{ + const struct inv_icm45600_fifo_1sensor_packet *pack1 =3D packet; + const struct inv_icm45600_fifo_2sensors_packet *pack2 =3D packet; + uint8_t header =3D *((const uint8_t *)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 */ + 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 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 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) +{ + uint32_t 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; + uint32_t period_gyro, period_accel, period; + uint32_t latency_gyro, latency_accel, latency; + __le16 raw_wm; + int ret; + + /* 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; + } + + raw_wm =3D INV_ICM45600_FIFO_WATERMARK_VAL(watermark); + memcpy(st->buffer, &raw_wm, sizeof(raw_wm)); + ret =3D regmap_bulk_write(st->map, INV_ICM45600_REG_FIFO_WATERMARK, + st->buffer, sizeof(raw_wm)); + if (ret) + return ret; + + return 0; +} + +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; + + pm_runtime_get_sync(dev); + + 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); + int ret; + + guard(mutex)(&st->lock); + + /* exit if FIFO is already on */ + if (st->fifo.on) { + ret =3D 0; + goto out_on; + } + + /* flush 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 */ + ret =3D regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0, + INV_ICM45600_FIFO_CONFIG0_MODE_MASK, + INV_ICM45600_FIFO_CONFIG0_MODE_STREAM); + 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; + +out_on: + /* increase FIFO on counter */ + st->fifo.on++; + return ret; +} + +static int inv_icm45600_buffer_predisable(struct iio_dev *indio_dev) +{ + struct inv_icm45600_state *st =3D iio_device_get_drvdata(indio_dev); + int ret; + + guard(mutex)(&st->lock); + + /* exit if there are several sensors using the FIFO */ + if (st->fifo.on > 1) { + ret =3D 0; + goto out_off; + } + + /* 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 */ + ret =3D regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0, + INV_ICM45600_FIFO_CONFIG0_MODE_MASK, + INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS); + 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 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; + +out_off: + /* decrease FIFO on counter */ + st->fifo.on--; + 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; + struct inv_icm45600_sensor_conf conf =3D INV_ICM45600_SENSOR_CONF_INIT; + unsigned int sleep_temp =3D 0; + unsigned int sleep_sensor =3D 0; + 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_set_fifo_en(st, st->fifo.en & ~sensor); + if (ret) + break; + + *watermark =3D 0; + ret =3D inv_icm45600_buffer_update_watermark(st); + if (ret) + break; + + 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_sensor); + else + ret =3D inv_icm45600_set_accel_conf(st, &conf, &sleep_sensor); + } + /* sleep maximum required time */ + sleep =3D max(sleep_sensor, sleep_temp); + if (sleep) + msleep(sleep); + + pm_runtime_mark_last_busy(dev); + 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 void *accel, *gyro, *timestamp; + const int8_t *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 (__le16 *)st->buffer; + 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_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; + int64_t 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; + + 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 */ + ret =3D regmap_write(st->map, INV_ICM45600_REG_FIFO_CONFIG0, + INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS | + INV_ICM45600_FIFO_CONFIG0_FIFO_DEPTH_MAX); + 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..f725f841e07bc1e738ca7df07ac= cd50484c8ceda --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.h @@ -0,0 +1,100 @@ +/* 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 + +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; + uint32_t 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; + uint8_t 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 int16_t 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) +{ + int16_t 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 void **a= ccel, + const void **gyro, const int8_t **temp, + const void **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_fifo_parse(struct inv_icm45600_state *st); + +int inv_icm45600_buffer_hwfifo_flush(struct inv_icm45600_state *st, + unsigned int count); + +#endif --=20 2.34.1 From nobody Sat Sep 13 22:31:01 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 936FB1C84BC; Fri, 11 Apr 2025 13:28:42 +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=1744378122; cv=none; b=cx3MUmhgo0jZ1ezuBOnUEeb+150sRmIIwK7ipvHgJfWVnZumf8q5IWuWN7VtMpIFHq+5zG7xwYlAOqcVir9i8ghH/UTYzCK/k0I08K3a5J5xLtWdcG+NZAs6l3ZInRrcFasF4ar4rWnK+0b8H1zR1OAlaiWUcxZ2SNjpMxV/6pw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744378122; c=relaxed/simple; bh=06F3uWhubW/MmNs70dPjAwsBGFSu4UP7REe4gzXRvrc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=GVIQFca2pNpp8oeJZvrN79sHeusTWC32A9vOb61Hif47Bi3cNHlVTP58kndnvJnN93pE73xPeA2/nfoIYFSkZ2VbhK31Ch7Bl8CiCf1gT1ymC92zHkF+9rpe7uE6+43Qx97lkfthFUAQfh1RnSEYNH9Mk0mQ7SOAP4V1Fej7ENI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=AB2z5jxh; 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="AB2z5jxh" Received: by smtp.kernel.org (Postfix) with ESMTPS id CE666C4CEFB; Fri, 11 Apr 2025 13:28:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1744378121; bh=06F3uWhubW/MmNs70dPjAwsBGFSu4UP7REe4gzXRvrc=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=AB2z5jxhDYDnx+mAZAbLs6h67qSfstJEns1HckWAOu0+x9WZxhbQfF3PX7b45eefO 7I6PX643rGFMs3mCkrxZHHtfnLyAncPHtHdEdqB5q9VvbZ8W+/pBnWimMH2EKjzTpV m8+oWCASG7ka1KDwFYdtcmbd7F+XowcQlxqC02sByGIq8EtDuSwYF9I5KMSUATfaOe uB0FQPc7x/YzieldKM52juGBPsX088Xn7MbEAq3z3MHipMfvEOQdDwG8YCn+nK4qKn oEOANgDCcRQQ4hESAQrElQ4JQHODH0R68t+VptyM+GyfKl/Lx7ha4VcDdR05o523wZ Apu4bI7eqdhxQ== 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 C0F6AC369B1; Fri, 11 Apr 2025 13:28:41 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Fri, 11 Apr 2025 13:28:38 +0000 Subject: [PATCH 6/8] iio: imu: add Kconfig and Makefile 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: <20250411-add_newport_driver-v1-6-15082160b019@tdk.com> References: <20250411-add_newport_driver-v1-0-15082160b019@tdk.com> In-Reply-To: <20250411-add_newport_driver-v1-0-15082160b019@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=1744378119; l=4140; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=JKi2GN1rX4ptrduYYDNWdyAYIQv9H73qs4FmqmLloQw=; b=/TXZTOwq25RwAGuqKUwN+ZtntsPPAgi8phGEgNImREFZdnkwGsxs6M44j1HgXhFIVxc41u4Hc K+VDRjApQERBtMJkC2O9bT+vgPICtUnF+dcq5R1ht8xr2zyoB9jD58+ 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 4 modules: - inv-icm45600 - inv-icm45600-i2c - inv-icm45600-spi - inv-icm45600-i3c. Signed-off-by: Remi Buisson --- drivers/iio/imu/Kconfig | 1 + drivers/iio/imu/Makefile | 1 + drivers/iio/imu/inv_icm45600/Kconfig | 70 +++++++++++++++++++++++++++++++= ++++ drivers/iio/imu/inv_icm45600/Makefile | 17 +++++++++ 4 files changed, 89 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..4fade8852a0dcf54df2bbd67b92= 69ed2c59f8699 --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/Kconfig @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +config INV_ICM45600 + tristate + 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-45686 + - ICM-45688-P + - ICM-45608 + - ICM-45634 + - ICM-45689 + - ICM-45606 + - ICM-45687 + + 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-45686 + - ICM-45688-P + - ICM-45608 + - ICM-45634 + - ICM-45689 + - ICM-45606 + - ICM-45687 + + 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-45686 + - ICM-45688-P + - ICM-45608 + - ICM-45634 + - ICM-45689 + - ICM-45606 + - ICM-45687 + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..0fd6fbce0286f24504dbfe71925= f9c35e717c446 --- /dev/null +++ b/drivers/iio/imu/inv_icm45600/Makefile @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +obj-$(CONFIG_INV_ICM45600) +=3D inv-icm45600.o +inv-icm45600-y +=3D inv_icm45600_core.o +inv-icm45600-y +=3D inv_icm45600_gyro.o +inv-icm45600-y +=3D inv_icm45600_accel.o +inv-icm45600-y +=3D inv_icm45600_temp.o +inv-icm45600-y +=3D inv_icm45600_buffer.o + +obj-$(CONFIG_INV_ICM45600_I2C) +=3D inv-icm45600-i2c.o +inv-icm45600-i2c-y +=3D inv_icm45600_i2c.o + +obj-$(CONFIG_INV_ICM45600_SPI) +=3D inv-icm45600-spi.o +inv-icm45600-spi-y +=3D inv_icm45600_spi.o + +obj-$(CONFIG_INV_ICM45600_I3C) +=3D inv-icm45600-i3c.o +inv-icm45600-i3c-y +=3D inv_icm45600_i3c.o --=20 2.34.1 From nobody Sat Sep 13 22:31:01 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 50AD61A5B85; Fri, 11 Apr 2025 13:28:42 +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=1744378122; cv=none; b=J7imlUjpexssslqGQeP/87/Y46t66dz4bm5g/t/ztbZ9fytuJQg/ckfkcV1qiM1lWME8B/eTCHkdoXsYd3Q+ag0J4okRJ1++SFKaGZuoMDnPS8YHKZjhTxvVZdCMXRAaTABA53lWocvmJUmCmD4mLyAEOpv9mgJ7MTD9x9/WWBM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744378122; c=relaxed/simple; bh=0A5y2B3CkTHSgD4tqnDeSpLhj3pw/FCweLnBblikG5I=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=hRCY0KqotwsG0U97/YwGbcCZG90/rS6Rr2dm40TEGd03oe/OOG/1qlT9XaiIV1G5z8Uc0ee2XWR8GUMO9vL5Vv3jdHPayxH1G0oMW1bKik6FW9BCmbXWClc0WASWZqjcF0fZWyOQXSu34FnRhebh05HFbiEEN27zdpFDrWnwvog= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=sR9ibisT; 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="sR9ibisT" Received: by smtp.kernel.org (Postfix) with ESMTPS id D91F5C4CEFA; Fri, 11 Apr 2025 13:28:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1744378121; bh=0A5y2B3CkTHSgD4tqnDeSpLhj3pw/FCweLnBblikG5I=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=sR9ibisT3FuS/Isolq09sOiALGY+MTHZxhaPXIuOxeg85s1XiXPDmU4+QCPdZHEhQ nX0hMUrmFxTmXzHBIBPDnSOOKsuChsIpf59RNem0u5vIotoZIrIX+1g0CpVeTK6jk3 g4G/yUiGk98zxRNgmdfTr4DwTrqC6jw4T3cB0F7xaHVhmfcAjv1/HBE0HGNjIO1/nd cJZaFC1I/GKPlWZTT7TCZuX8HtZe5Uigl4N6Myet4gGm4X9l6Llrg6xlj41VAi2c6J SO9/PKrOM9KILlz3CQckmBMw2HeSIURTl73QnWUFvcl2mi+lsduzKNSqEIWkTBUFso +D5jDnnCcnscA== 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 CFFCCC369AF; Fri, 11 Apr 2025 13:28:41 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Fri, 11 Apr 2025 13:28:39 +0000 Subject: [PATCH 7/8] dt-bindings: iio: imu: Add inv_icm45600 documentation 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: <20250411-add_newport_driver-v1-7-15082160b019@tdk.com> References: <20250411-add_newport_driver-v1-0-15082160b019@tdk.com> In-Reply-To: <20250411-add_newport_driver-v1-0-15082160b019@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=1744378119; l=6701; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=ppRiZsCqcoXPb0CsC4/6ztBvt4B21/TyWOUreV5Lxuw=; b=njVa4DZIxLfVh0DTqnRVXlaWB8Gb9eO2zaYfWZLDiyDMxoY6VxhPoRF2wHGhSO27IbNM5KgQU EfkveOOSsUhC2eJM4Ak0WgmAzfd1OJ2iRCd0nP+HMzibYTUyyj90s8W 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-456xxx devices devicetree bindings. Describe custom sysfs API for controlling the power modes. Signed-off-by: Remi Buisson --- .../ABI/testing/sysfs-bus-iio-inv_icm45600 | 37 ++++++ .../bindings/iio/imu/invensense,icm45600.yaml | 136 +++++++++++++++++= ++++ 2 files changed, 173 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio-inv_icm45600 b/Documen= tation/ABI/testing/sysfs-bus-iio-inv_icm45600 new file mode 100644 index 0000000000000000000000000000000000000000..8d2d9b68ad9e35fe0d6c157e984= afc327eab92ec --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-inv_icm45600 @@ -0,0 +1,37 @@ +What: /sys/bus/iio/devices/iio:deviceX/in_accel_power_mode +KernelVersion: 6.16 +Contact: linux-iio@vger.kernel.org +Description: + Accelerometer power mode. Setting this attribute will set the + requested power mode to use if the ODR support it. If ODR + support only 1 mode, power mode will be enforced. + Reading this attribute will return the current accelerometer + power mode if the sensor is on, or the requested value if the + sensor is off. The value between real and requested value can + be different for ODR supporting only 1 mode. + +What: /sys/bus/iio/devices/iio:deviceX/in_accel_power_mode_available +KernelVersion: 6.16 +Contact: linux-iio@vger.kernel.org +Description: + List of available accelerometer power modes that can be set in + in_accel_power_mode attribute. + +What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_power_mode +KernelVersion: 6.16 +Contact: linux-iio@vger.kernel.org +Description: + Gyroscope power mode. Setting this attribute will set the + requested power mode to use if the ODR support it. If ODR + support only 1 mode, power mode will be enforced. + Reading this attribute will return the current gyroscope + power mode if the sensor is on, or the requested value if the + sensor is off. The value between real and requested value can + be different for ODR supporting only 1 mode. + +What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_power_mode_available +KernelVersion: 6.16 +Contact: linux-iio@vger.kernel.org +Description: + List of available gyroscope power modes that can be set in + in_anglvel_power_mode attribute. 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..51455f0b5cb90abdd823f154e45= 891ad364296e6 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm45600.yaml @@ -0,0 +1,136 @@ +# 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-456xx 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,icm45686 + - invensense,icm45688p + - invensense,icm45608 + - invensense,icm45634 + - invensense,icm45689 + - invensense,icm45606 + - invensense,icm45687 + + 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 + +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_RAISING>; + vdd-supply =3D <&vdd>; + vddio-supply =3D <&vddio>; + mount-matrix =3D "1", "0", "0", + "0", "1", "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_RAISING>; + vdd-supply =3D <&vdd>; + vddio-supply =3D <&vddio>; + mount-matrix =3D "1", "0", "0", + "0", "1", "0", + "0", "0", "1"; + }; + }; + - | + #include + #include + i3c { + #address-cells =3D <3>; + #size-cells =3D <0>; + + icm45600@68,46A00000011 { + compatible =3D "invensense,icm45600"; + 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 "1", "0", "0", + "0", "1", "0", + "0", "0", "1"; + }; + }; --=20 2.34.1 From nobody Sat Sep 13 22:31:01 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 B8A1E1D619D; Fri, 11 Apr 2025 13:28:42 +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=1744378122; cv=none; b=qg5Q1RV1z0s3rGYQfgeFPt6HxwIDN/PNCwOYCuoWp8azOQ3gOSBKqLyyUj+eR5Jp/1m2EV8r/Bgf3lnM4OPcS6FSzscw0zmjyZI6aI1TDN4k9TUQvqWeo7OXZwpQTrf8KWdUWTrxdoq4j66paiEmyvVxbeNVDnU+WeE+mXD0nr8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744378122; c=relaxed/simple; bh=TpzgJyJ4WTRHf6o2yGhmsml0lRhJS4SJG/+gmJU5g/4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=O13fce/tTALGkXVz44YoUek9LtqYboySsXg3sE1YgqGjn5ArJQlxQguEzRkO+SEzYKOb+FUCzzYK0ow7bSPGtvoORUjydxwTP+mxV4rwvARc0AEp/dqd2LAYdHEPrtu7T+2qSafljPMIibJKZ0QQI/uoU8gWr33lf+luR3RfPJk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FgJvMBCj; 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="FgJvMBCj" Received: by smtp.kernel.org (Postfix) with ESMTPS id E9C22C4CEFE; Fri, 11 Apr 2025 13:28:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1744378122; bh=TpzgJyJ4WTRHf6o2yGhmsml0lRhJS4SJG/+gmJU5g/4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=FgJvMBCj1wptdBewuoIqalYSa+l25zAazIgnnD26vd9xp5U22rfVHLSxNAz3ju1fm caHaKl2vgqKs9U5Vaa6kHx/tCuqgGRvvcAche5YupaF/6fApa+SYjXvjbUYFekQ/nC CH6/4IBxGzdUf1Rq0lOgHaakKYq95ou794nhXSljEgXLMeih/job8Yl5Z5LZm6ewgG eepDQVEXS/X7uUqHJPcJFSvJDfQh37zHyfDPbEp8PAyTnrhaeziS0YYx9oRsg67nKD eaNaKlsQQO/6p4DWUVWQw6Rx2yRydM36UG05wICk+2Jn7ZfBEbmw0jYKNnn0zvbQvP mAzpWP5f3ujAw== 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 E120BC36010; Fri, 11 Apr 2025 13:28:41 +0000 (UTC) From: Remi Buisson via B4 Relay Date: Fri, 11 Apr 2025 13:28:40 +0000 Subject: [PATCH 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: <20250411-add_newport_driver-v1-8-15082160b019@tdk.com> References: <20250411-add_newport_driver-v1-0-15082160b019@tdk.com> In-Reply-To: <20250411-add_newport_driver-v1-0-15082160b019@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=1744378119; l=1004; i=remi.buisson@tdk.com; s=20250411; h=from:subject:message-id; bh=kZKXeMI2KjRSPBEamKTN7FTc+Klt4KAQtMhrcXfCwTE=; b=IfheCnRVke3TO/1sC7BKOQBfcx8MRmhZxO40Wzel1Rw8pIIhLmJ1HRv8hMIwimGglnkaWY+U1 JjDeXdxmdAPA25jU8DvmoGjUiUNRWSsy5wi78s0uiFc9TQ1aEevNdBl 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-456xx IMU device. Signed-off-by: Remi Buisson --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 030d90d383411bbfe949cfff4f5bce27e3dd37c4..2d4b6755a9e240432ec1abfb131= 358ae4df48362 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12397,6 +12397,15 @@ 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/ABI/testing/sysfs-bus-iio-inv_icm45600 +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