From: Remi Buisson <remi.buisson@tdk.com>
Core component of a new driver for InvenSense ICM-45600 devices.
It includes registers definition, main probe/setup, and device
utility functions.
ICM-456xx devices are latest generation of 6-axis IMU,
gyroscope+accelerometer and temperature sensor. This device
includes a 8K FIFO, supports I2C/I3C/SPI, and provides
intelligent motion features like pedometer, tilt detection,
and tap detection.
Signed-off-by: Remi Buisson <remi.buisson@tdk.com>
---
drivers/iio/imu/Kconfig | 1 +
drivers/iio/imu/Makefile | 1 +
drivers/iio/imu/inv_icm45600/Kconfig | 5 +
drivers/iio/imu/inv_icm45600/Makefile | 4 +
drivers/iio/imu/inv_icm45600/inv_icm45600.h | 364 +++++++++++
drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 734 +++++++++++++++++++++++
6 files changed, 1109 insertions(+)
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
index 15612f0f189b5114deb414ef840339678abdc562..9d732bed9fcdac12a13713dba3455c1fdf9f4a53 100644
--- a/drivers/iio/imu/Kconfig
+++ b/drivers/iio/imu/Kconfig
@@ -109,6 +109,7 @@ config KMX61
be called kmx61.
source "drivers/iio/imu/inv_icm42600/Kconfig"
+source "drivers/iio/imu/inv_icm45600/Kconfig"
source "drivers/iio/imu/inv_mpu6050/Kconfig"
config SMI240
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
index e901aea498d37e5897e8b71268356a19eac2cb59..2ae6344f84699b2f85fff1c8077cb412f6ae2658 100644
--- a/drivers/iio/imu/Makefile
+++ b/drivers/iio/imu/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_FXOS8700_I2C) += fxos8700_i2c.o
obj-$(CONFIG_FXOS8700_SPI) += fxos8700_spi.o
obj-y += inv_icm42600/
+obj-y += inv_icm45600/
obj-y += inv_mpu6050/
obj-$(CONFIG_KMX61) += kmx61.o
diff --git a/drivers/iio/imu/inv_icm45600/Kconfig b/drivers/iio/imu/inv_icm45600/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..8cb5543e0a5817323ab7b2d520dd3430ac5dbc99
--- /dev/null
+++ b/drivers/iio/imu/inv_icm45600/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+config INV_ICM45600
+ tristate
+ select IIO_INV_SENSORS_TIMESTAMP
diff --git a/drivers/iio/imu/inv_icm45600/Makefile b/drivers/iio/imu/inv_icm45600/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..4f442b61896e91647c7947a044949792bae06a30
--- /dev/null
+++ b/drivers/iio/imu/inv_icm45600/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+obj-$(CONFIG_INV_ICM45600) += inv-icm45600.o
+inv-icm45600-y += inv_icm45600_core.o
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600.h b/drivers/iio/imu/inv_icm45600/inv_icm45600.h
new file mode 100644
index 0000000000000000000000000000000000000000..d56de75ab7f2168f22e25b5816cb361bef457c0d
--- /dev/null
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h
@@ -0,0 +1,364 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2025 Invensense, Inc. */
+
+#ifndef INV_ICM45600_H_
+#define INV_ICM45600_H_
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/iio/common/inv_sensors_timestamp.h>
+#include <linux/iio/iio.h>
+#include <linux/types.h>
+
+#define INV_ICM45600_REG_BANK_MASK GENMASK(15, 8)
+#define INV_ICM45600_REG_ADDR_MASK GENMASK(7, 0)
+
+enum inv_icm45600_sensor_mode {
+ INV_ICM45600_SENSOR_MODE_OFF,
+ INV_ICM45600_SENSOR_MODE_STANDBY,
+ INV_ICM45600_SENSOR_MODE_LOW_POWER,
+ INV_ICM45600_SENSOR_MODE_LOW_NOISE,
+ INV_ICM45600_SENSOR_MODE_NB
+};
+
+/* gyroscope fullscale values */
+enum inv_icm45600_gyro_fs {
+ INV_ICM45600_GYRO_FS_2000DPS,
+ INV_ICM45600_GYRO_FS_1000DPS,
+ INV_ICM45600_GYRO_FS_500DPS,
+ INV_ICM45600_GYRO_FS_250DPS,
+ INV_ICM45600_GYRO_FS_125DPS,
+ INV_ICM45600_GYRO_FS_62_5DPS,
+ INV_ICM45600_GYRO_FS_31_25DPS,
+ INV_ICM45600_GYRO_FS_15_625DPS,
+ INV_ICM45600_GYRO_FS_NB
+};
+
+enum inv_icm45686_gyro_fs {
+ INV_ICM45686_GYRO_FS_4000DPS,
+ INV_ICM45686_GYRO_FS_2000DPS,
+ INV_ICM45686_GYRO_FS_1000DPS,
+ INV_ICM45686_GYRO_FS_500DPS,
+ INV_ICM45686_GYRO_FS_250DPS,
+ INV_ICM45686_GYRO_FS_125DPS,
+ INV_ICM45686_GYRO_FS_62_5DPS,
+ INV_ICM45686_GYRO_FS_31_25DPS,
+ INV_ICM45686_GYRO_FS_15_625DPS,
+ INV_ICM45686_GYRO_FS_NB
+};
+
+/* accelerometer fullscale values */
+enum inv_icm45600_accel_fs {
+ INV_ICM45600_ACCEL_FS_16G,
+ INV_ICM45600_ACCEL_FS_8G,
+ INV_ICM45600_ACCEL_FS_4G,
+ INV_ICM45600_ACCEL_FS_2G,
+ INV_ICM45600_ACCEL_FS_NB
+};
+
+enum inv_icm45686_accel_fs {
+ INV_ICM45686_ACCEL_FS_32G,
+ INV_ICM45686_ACCEL_FS_16G,
+ INV_ICM45686_ACCEL_FS_8G,
+ INV_ICM45686_ACCEL_FS_4G,
+ INV_ICM45686_ACCEL_FS_2G,
+ INV_ICM45686_ACCEL_FS_NB
+};
+
+/* ODR suffixed by LN or LP are Low-Noise or Low-Power mode only */
+enum inv_icm45600_odr {
+ INV_ICM45600_ODR_6400HZ_LN = 0x03,
+ INV_ICM45600_ODR_3200HZ_LN,
+ INV_ICM45600_ODR_1600HZ_LN,
+ INV_ICM45600_ODR_800HZ_LN,
+ INV_ICM45600_ODR_400HZ,
+ INV_ICM45600_ODR_200HZ,
+ INV_ICM45600_ODR_100HZ,
+ INV_ICM45600_ODR_50HZ,
+ INV_ICM45600_ODR_25HZ,
+ INV_ICM45600_ODR_12_5HZ,
+ INV_ICM45600_ODR_6_25HZ_LP,
+ INV_ICM45600_ODR_3_125HZ_LP,
+ INV_ICM45600_ODR_1_5625HZ_LP,
+ INV_ICM45600_ODR_NB
+};
+
+struct inv_icm45600_sensor_conf {
+ int mode;
+ int fs;
+ int odr;
+ int filter;
+};
+#define INV_ICM45600_SENSOR_CONF_INIT {-1, -1, -1, -1}
+
+struct inv_icm45600_conf {
+ struct inv_icm45600_sensor_conf gyro;
+ struct inv_icm45600_sensor_conf accel;
+};
+
+struct inv_icm45600_suspended {
+ enum inv_icm45600_sensor_mode gyro;
+ enum inv_icm45600_sensor_mode accel;
+};
+
+struct inv_icm45600_chip_info {
+ u8 whoami;
+ const char *name;
+ const struct inv_icm45600_conf *conf;
+};
+
+extern const struct inv_icm45600_chip_info inv_icm45605_chip_info;
+extern const struct inv_icm45600_chip_info inv_icm45606_chip_info;
+extern const struct inv_icm45600_chip_info inv_icm45608_chip_info;
+extern const struct inv_icm45600_chip_info inv_icm45634_chip_info;
+extern const struct inv_icm45600_chip_info inv_icm45686_chip_info;
+extern const struct inv_icm45600_chip_info inv_icm45687_chip_info;
+extern const struct inv_icm45600_chip_info inv_icm45688p_chip_info;
+extern const struct inv_icm45600_chip_info inv_icm45689_chip_info;
+
+/**
+ * struct inv_icm45600_state - driver state variables
+ * @lock: lock for serializing multiple registers access.
+ * @chip: chip identifier.
+ * @map: regmap pointer.
+ * @vddio_supply: I/O voltage regulator for the chip.
+ * @orientation: sensor chip orientation relative to main hardware.
+ * @conf: chip sensors configurations.
+ * @suspended: suspended sensors configuration.
+ * @indio_gyro: gyroscope IIO device.
+ * @indio_accel: accelerometer IIO device.
+ * @timestamp: interrupt timestamps.
+ * @buffer: data transfer buffer aligned for DMA.
+ */
+struct inv_icm45600_state {
+ struct mutex lock;
+ struct regmap *map;
+ struct regulator *vddio_supply;
+ struct iio_mount_matrix orientation;
+ struct inv_icm45600_conf conf;
+ struct inv_icm45600_suspended suspended;
+ struct iio_dev *indio_gyro;
+ struct iio_dev *indio_accel;
+ const struct inv_icm45600_chip_info *chip_info;
+ struct {
+ s64 gyro;
+ s64 accel;
+ } timestamp;
+ union {
+ u8 buff[2];
+ __le16 u16;
+ } buffer __aligned(IIO_DMA_MINALIGN);
+};
+
+/**
+ * struct inv_icm45600_sensor_state - sensor state variables
+ * @scales: table of scales.
+ * @scales_len: length (nb of items) of the scales table.
+ * @power_mode: sensor requested power mode (for common frequencies)
+ * @ts: timestamp module states.
+ */
+struct inv_icm45600_sensor_state {
+ const int *scales;
+ size_t scales_len;
+ enum inv_icm45600_sensor_mode power_mode;
+ struct inv_sensors_timestamp ts;
+};
+
+/* Virtual register addresses: @bank on MSB (16 bits), @address on LSB */
+
+/* Indirect register access */
+#define INV_ICM45600_REG_IREG_ADDR 0x7C
+#define INV_ICM45600_REG_IREG_DATA 0x7E
+
+/* Direct acces registers */
+#define INV_ICM45600_REG_MISC2 0x007F
+#define INV_ICM45600_MISC2_SOFT_RESET BIT(1)
+
+#define INV_ICM45600_REG_DRIVE_CONFIG0 0x0032
+#define INV_ICM45600_DRIVE_CONFIG0_SPI_MASK GENMASK(3, 1)
+#define INV_ICM45600_SPI_SLEW_RATE_0_5NS 6
+#define INV_ICM45600_SPI_SLEW_RATE_4NS 5
+#define INV_ICM45600_SPI_SLEW_RATE_5NS 4
+#define INV_ICM45600_SPI_SLEW_RATE_7NS 3
+#define INV_ICM45600_SPI_SLEW_RATE_10NS 2
+#define INV_ICM45600_SPI_SLEW_RATE_14NS 1
+#define INV_ICM45600_SPI_SLEW_RATE_38NS 0
+
+#define INV_ICM45600_REG_INT1_CONFIG2 0x0018
+#define INV_ICM45600_INT1_CONFIG2_PUSH_PULL BIT(2)
+#define INV_ICM45600_INT1_CONFIG2_LATCHED BIT(1)
+#define INV_ICM45600_INT1_CONFIG2_ACTIVE_HIGH BIT(0)
+#define INV_ICM45600_INT1_CONFIG2_ACTIVE_LOW 0x00
+
+#define INV_ICM45600_REG_FIFO_CONFIG0 0x001D
+#define INV_ICM45600_FIFO_CONFIG0_MODE_MASK GENMASK(7, 6)
+#define INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS 0
+#define INV_ICM45600_FIFO_CONFIG0_MODE_STREAM 1
+#define INV_ICM45600_FIFO_CONFIG0_MODE_STOP_ON_FULL 2
+#define INV_ICM45600_FIFO_CONFIG0_FIFO_DEPTH_MAX 0x1F
+
+#define INV_ICM45600_REG_FIFO_CONFIG2 0x0020
+#define INV_ICM45600_REG_FIFO_CONFIG2_FIFO_FLUSH BIT(7)
+#define INV_ICM45600_REG_FIFO_CONFIG2_WM_GT_TH BIT(3)
+
+#define INV_ICM45600_REG_FIFO_CONFIG3 0x0021
+#define INV_ICM45600_FIFO_CONFIG3_ES1_EN BIT(5)
+#define INV_ICM45600_FIFO_CONFIG3_ES0_EN BIT(4)
+#define INV_ICM45600_FIFO_CONFIG3_HIRES_EN BIT(3)
+#define INV_ICM45600_FIFO_CONFIG3_GYRO_EN BIT(2)
+#define INV_ICM45600_FIFO_CONFIG3_ACCEL_EN BIT(1)
+#define INV_ICM45600_FIFO_CONFIG3_IF_EN BIT(0)
+
+#define INV_ICM45600_REG_FIFO_CONFIG4 0x0022
+#define INV_ICM45600_FIFO_CONFIG4_COMP_EN BIT(2)
+#define INV_ICM45600_FIFO_CONFIG4_TMST_FSYNC_EN BIT(1)
+#define INV_ICM45600_FIFO_CONFIG4_ES0_9B BIT(0)
+
+/* all sensor data are 16 bits (2 registers wide) in big-endian */
+#define INV_ICM45600_REG_TEMP_DATA 0x000C
+#define INV_ICM45600_REG_ACCEL_DATA_X 0x0000
+#define INV_ICM45600_REG_ACCEL_DATA_Y 0x0002
+#define INV_ICM45600_REG_ACCEL_DATA_Z 0x0004
+#define INV_ICM45600_REG_GYRO_DATA_X 0x0006
+#define INV_ICM45600_REG_GYRO_DATA_Y 0x0008
+#define INV_ICM45600_REG_GYRO_DATA_Z 0x000A
+#define INV_ICM45600_DATA_INVALID -32768
+
+#define INV_ICM45600_REG_INT_STATUS 0x0019
+#define INV_ICM45600_INT_STATUS_RESET_DONE BIT(7)
+#define INV_ICM45600_INT_STATUS_AUX1_AGC_RDY BIT(6)
+#define INV_ICM45600_INT_STATUS_AP_AGC_RDY BIT(5)
+#define INV_ICM45600_INT_STATUS_AP_FSYNC BIT(4)
+#define INV_ICM45600_INT_STATUS_AUX1_DRDY BIT(3)
+#define INV_ICM45600_INT_STATUS_DATA_RDY BIT(2)
+#define INV_ICM45600_INT_STATUS_FIFO_THS BIT(1)
+#define INV_ICM45600_INT_STATUS_FIFO_FULL BIT(0)
+
+/*
+ * FIFO access registers
+ * FIFO count is 16 bits (2 registers)
+ * FIFO data is a continuous read register to read FIFO content
+ */
+#define INV_ICM45600_REG_FIFO_COUNT 0x0012
+#define INV_ICM45600_REG_FIFO_DATA 0x0014
+
+#define INV_ICM45600_REG_PWR_MGMT0 0x0010
+#define INV_ICM45600_PWR_MGMT0_GYRO_MODE_MASK GENMASK(3, 2)
+#define INV_ICM45600_PWR_MGMT0_ACCEL_MODE_MASK GENMASK(1, 0)
+
+#define INV_ICM45600_REG_ACCEL_CONFIG0 0x001B
+#define INV_ICM45600_ACCEL_CONFIG0_FS_MASK GENMASK(6, 4)
+#define INV_ICM45600_ACCEL_CONFIG0_ODR_MASK GENMASK(3, 0)
+#define INV_ICM45600_REG_GYRO_CONFIG0 0x001C
+#define INV_ICM45600_GYRO_CONFIG0_FS_MASK GENMASK(7, 4)
+#define INV_ICM45600_GYRO_CONFIG0_ODR_MASK GENMASK(3, 0)
+
+#define INV_ICM45600_REG_SMC_CONTROL_0 0xA258
+#define INV_ICM45600_SMC_CONTROL_0_ACCEL_LP_CLK_SEL BIT(4)
+#define INV_ICM45600_SMC_CONTROL_0_TMST_EN BIT(0)
+
+/* FIFO watermark is 16 bits (2 registers wide) in little-endian */
+#define INV_ICM45600_REG_FIFO_WATERMARK 0x001E
+
+/* FIFO is configured for 8kb */
+#define INV_ICM45600_FIFO_SIZE_MAX (8 * 1024)
+
+#define INV_ICM45600_REG_INT1_CONFIG0 0x0016
+#define INV_ICM45600_INT1_CONFIG0_RESET_DONE_EN BIT(7)
+#define INV_ICM45600_INT1_CONFIG0_AUX1_AGC_RDY_EN BIT(6)
+#define INV_ICM45600_INT1_CONFIG0_AP_AGC_RDY_EN BIT(5)
+#define INV_ICM45600_INT1_CONFIG0_AP_FSYNC_EN BIT(4)
+#define INV_ICM45600_INT1_CONFIG0_AUX1_DRDY_EN BIT(3)
+#define INV_ICM45600_INT1_CONFIG0_DRDY_EN BIT(2)
+#define INV_ICM45600_INT1_CONFIG0_FIFO_THS_EN BIT(1)
+#define INV_ICM45600_INT1_CONFIG0_FIFO_FULL_EN BIT(0)
+
+#define INV_ICM45600_REG_WHOAMI 0x0072
+#define INV_ICM45600_WHOAMI_ICM45605 0xE5
+#define INV_ICM45600_WHOAMI_ICM45686 0xE9
+#define INV_ICM45600_WHOAMI_ICM45688P 0xE7
+#define INV_ICM45600_WHOAMI_ICM45608 0x81
+#define INV_ICM45600_WHOAMI_ICM45634 0x82
+#define INV_ICM45600_WHOAMI_ICM45689 0x83
+#define INV_ICM45600_WHOAMI_ICM45606 0x84
+#define INV_ICM45600_WHOAMI_ICM45687 0x85
+
+/* Gyro USER offset */
+#define INV_ICM45600_IPREG_SYS1_REG_42 0xA42A
+#define INV_ICM45600_IPREG_SYS1_REG_56 0xA438
+#define INV_ICM45600_IPREG_SYS1_REG_70 0xA446
+#define INV_ICM45600_GYRO_OFFUSER_MASK GENMASK(13, 0)
+/* Gyro Averaging filter */
+#define INV_ICM45600_IPREG_SYS1_REG_170 0xA4AA
+#define INV_ICM45600_IPREG_SYS1_170_GYRO_LP_AVG_MASK GENMASK(4, 1)
+#define INV_ICM45600_GYRO_LP_AVG_SEL_8X 5
+#define INV_ICM45600_GYRO_LP_AVG_SEL_2X 1
+/* Accel USER offset */
+#define INV_ICM45600_IPREG_SYS2_REG_24 0xA518
+#define INV_ICM45600_IPREG_SYS2_REG_32 0xA520
+#define INV_ICM45600_IPREG_SYS2_REG_40 0xA528
+#define INV_ICM45600_ACCEL_OFFUSER_MASK GENMASK(13, 0)
+/* Accel averaging filter */
+#define INV_ICM45600_IPREG_SYS2_REG_129 0xA581
+#define INV_ICM45600_ACCEL_LP_AVG_SEL_1X 0x0000
+#define INV_ICM45600_ACCEL_LP_AVG_SEL_4X 0x0002
+
+/* Sleep times required by the driver */
+#define INV_ICM45600_ACCEL_STARTUP_TIME_MS 60
+#define INV_ICM45600_GYRO_STARTUP_TIME_MS 60
+#define INV_ICM45600_GYRO_STOP_TIME_MS 150
+#define INV_ICM45600_IREG_DELAY_US 4
+
+typedef int (*inv_icm45600_bus_setup)(struct inv_icm45600_state *);
+
+extern const struct dev_pm_ops inv_icm45600_pm_ops;
+
+const struct iio_mount_matrix *
+inv_icm45600_get_mount_matrix(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan);
+
+#define INV_ICM45600_TEMP_CHAN(_index) \
+ { \
+ .type = IIO_TEMP, \
+ .info_mask_separate = \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+ }, \
+ }
+
+int inv_icm45600_temp_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask);
+
+u32 inv_icm45600_odr_to_period(enum inv_icm45600_odr odr);
+
+int inv_icm45600_set_accel_conf(struct inv_icm45600_state *st,
+ struct inv_icm45600_sensor_conf *conf,
+ unsigned int *sleep_ms);
+
+int inv_icm45600_set_gyro_conf(struct inv_icm45600_state *st,
+ struct inv_icm45600_sensor_conf *conf,
+ unsigned int *sleep_ms);
+
+int inv_icm45600_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int writeval, unsigned int *readval);
+
+int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm45600_chip_info *chip_info,
+ bool reset, inv_icm45600_bus_setup bus_setup);
+
+struct iio_dev *inv_icm45600_gyro_init(struct inv_icm45600_state *st);
+
+int inv_icm45600_gyro_parse_fifo(struct iio_dev *indio_dev);
+
+struct iio_dev *inv_icm45600_accel_init(struct inv_icm45600_state *st);
+
+int inv_icm45600_accel_parse_fifo(struct iio_dev *indio_dev);
+
+#endif
diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
new file mode 100644
index 0000000000000000000000000000000000000000..022eb9180b5a750e8fba4a1cd873041c27f59880
--- /dev/null
+++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c
@@ -0,0 +1,734 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (C) 2025 Invensense, Inc. */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+
+#include "inv_icm45600.h"
+
+static int inv_icm45600_ireg_read(struct regmap *map, unsigned int reg,
+ u8 *data, size_t count)
+{
+ int ret;
+ u8 addr[2];
+ ssize_t i;
+ unsigned int d;
+
+ addr[0] = FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg);
+ addr[1] = FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg);
+
+ /* Burst write address. */
+ ret = regmap_bulk_write(map, INV_ICM45600_REG_IREG_ADDR, addr, 2);
+ udelay(INV_ICM45600_IREG_DELAY_US);
+ if (ret)
+ return ret;
+
+ /* Read the data. */
+ for (i = 0; i < count; i++) {
+ ret = regmap_read(map, INV_ICM45600_REG_IREG_DATA, &d);
+ data[i] = d;
+ udelay(INV_ICM45600_IREG_DELAY_US);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int inv_icm45600_ireg_write(struct regmap *map, unsigned int reg,
+ const u8 *data, size_t count)
+{
+ int ret;
+ u8 addr_data0[3];
+ ssize_t i;
+
+ addr_data0[0] = FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg);
+ addr_data0[1] = FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg);
+ addr_data0[2] = data[0];
+
+ /* Burst write address and first byte. */
+ ret = regmap_bulk_write(map, INV_ICM45600_REG_IREG_ADDR, addr_data0, 3);
+ udelay(INV_ICM45600_IREG_DELAY_US);
+ if (ret)
+ return ret;
+
+ /* Write the remaining bytes. */
+ for (i = 1; i < count; i++) {
+ ret = regmap_write(map, INV_ICM45600_REG_IREG_DATA, data[i]);
+ udelay(INV_ICM45600_IREG_DELAY_US);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int inv_icm45600_read(void *context, const void *reg_buf, size_t reg_size,
+ void *val_buf, size_t val_size)
+{
+ unsigned int reg = (unsigned int) be16_to_cpup(reg_buf);
+ struct regmap *map = context;
+
+ if (FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg) == 0)
+ return regmap_bulk_read(map, FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg),
+ val_buf, val_size);
+
+ return inv_icm45600_ireg_read(map, reg, val_buf, val_size);
+}
+
+static int inv_icm45600_write(void *context, const void *data,
+ size_t count)
+{
+ u8 *d = (u8 *)data;
+ unsigned int reg = (unsigned int) be16_to_cpup(data);
+ struct regmap *map = context;
+
+ if (FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg) == 0)
+ return regmap_bulk_write(map, FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg),
+ d + 2, count - 2);
+
+ return inv_icm45600_ireg_write(map, reg, d + 2, count - 2);
+}
+
+static const struct regmap_bus inv_icm45600_regmap_bus = {
+ .read = inv_icm45600_read,
+ .write = inv_icm45600_write,
+};
+
+static const struct regmap_config inv_icm45600_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+};
+
+/* These are the chip initial default configurations (default FS value is based on icm45686) */
+static const struct inv_icm45600_conf inv_icm45600_default_conf = {
+ .gyro = {
+ .mode = INV_ICM45600_SENSOR_MODE_OFF,
+ .fs = INV_ICM45686_GYRO_FS_2000DPS,
+ .odr = INV_ICM45600_ODR_800HZ_LN,
+ .filter = INV_ICM45600_GYRO_LP_AVG_SEL_8X,
+ },
+ .accel = {
+ .mode = INV_ICM45600_SENSOR_MODE_OFF,
+ .fs = INV_ICM45686_ACCEL_FS_16G,
+ .odr = INV_ICM45600_ODR_800HZ_LN,
+ .filter = INV_ICM45600_ACCEL_LP_AVG_SEL_4X,
+ },
+};
+
+static const struct inv_icm45600_conf inv_icm45686_default_conf = {
+ .gyro = {
+ .mode = INV_ICM45600_SENSOR_MODE_OFF,
+ .fs = INV_ICM45686_GYRO_FS_4000DPS,
+ .odr = INV_ICM45600_ODR_800HZ_LN,
+ .filter = INV_ICM45600_GYRO_LP_AVG_SEL_8X,
+ },
+ .accel = {
+ .mode = INV_ICM45600_SENSOR_MODE_OFF,
+ .fs = INV_ICM45686_ACCEL_FS_32G,
+ .odr = INV_ICM45600_ODR_800HZ_LN,
+ .filter = INV_ICM45600_ACCEL_LP_AVG_SEL_4X,
+ },
+};
+
+const struct inv_icm45600_chip_info inv_icm45605_chip_info = {
+ .whoami = INV_ICM45600_WHOAMI_ICM45605,
+ .name = "icm45605",
+ .conf = &inv_icm45600_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm45605_chip_info, "IIO_ICM45600");
+
+const struct inv_icm45600_chip_info inv_icm45606_chip_info = {
+ .whoami = INV_ICM45600_WHOAMI_ICM45606,
+ .name = "icm45606",
+ .conf = &inv_icm45600_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm45606_chip_info, "IIO_ICM45600");
+
+const struct inv_icm45600_chip_info inv_icm45608_chip_info = {
+ .whoami = INV_ICM45600_WHOAMI_ICM45608,
+ .name = "icm45608",
+ .conf = &inv_icm45600_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm45608_chip_info, "IIO_ICM45600");
+
+const struct inv_icm45600_chip_info inv_icm45634_chip_info = {
+ .whoami = INV_ICM45600_WHOAMI_ICM45634,
+ .name = "icm45634",
+ .conf = &inv_icm45600_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm45634_chip_info, "IIO_ICM45600");
+
+const struct inv_icm45600_chip_info inv_icm45686_chip_info = {
+ .whoami = INV_ICM45600_WHOAMI_ICM45686,
+ .name = "icm45686",
+ .conf = &inv_icm45686_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm45686_chip_info, "IIO_ICM45600");
+
+const struct inv_icm45600_chip_info inv_icm45687_chip_info = {
+ .whoami = INV_ICM45600_WHOAMI_ICM45687,
+ .name = "icm45687",
+ .conf = &inv_icm45686_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm45687_chip_info, "IIO_ICM45600");
+
+const struct inv_icm45600_chip_info inv_icm45688p_chip_info = {
+ .whoami = INV_ICM45600_WHOAMI_ICM45688P,
+ .name = "icm45688p",
+ .conf = &inv_icm45686_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm45688p_chip_info, "IIO_ICM45600");
+
+const struct inv_icm45600_chip_info inv_icm45689_chip_info = {
+ .whoami = INV_ICM45600_WHOAMI_ICM45689,
+ .name = "icm45689",
+ .conf = &inv_icm45686_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm45689_chip_info, "IIO_ICM45600");
+
+const struct iio_mount_matrix *
+inv_icm45600_get_mount_matrix(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ const struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+
+ return &st->orientation;
+}
+
+u32 inv_icm45600_odr_to_period(enum inv_icm45600_odr odr)
+{
+ static u32 odr_periods[INV_ICM45600_ODR_NB] = {
+ /* 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 = st->conf.gyro.mode;
+ enum inv_icm45600_sensor_mode oldaccel = st->conf.accel.mode;
+ unsigned int sleepval;
+ unsigned int val;
+ int ret;
+
+ /* if nothing changed, exit */
+ if (gyro == oldgyro && accel == oldaccel)
+ return 0;
+
+ val = FIELD_PREP(INV_ICM45600_PWR_MGMT0_GYRO_MODE_MASK, gyro) |
+ FIELD_PREP(INV_ICM45600_PWR_MGMT0_ACCEL_MODE_MASK, accel);
+ ret = regmap_write(st->map, INV_ICM45600_REG_PWR_MGMT0, val);
+ if (ret)
+ return ret;
+
+ st->conf.gyro.mode = gyro;
+ st->conf.accel.mode = accel;
+
+ /* Compute the required wait time for sensors to stabilize. */
+ sleepval = 0;
+
+ /* Accel startup time. */
+ if (accel != oldaccel && oldaccel == INV_ICM45600_SENSOR_MODE_OFF) {
+ if (sleepval < INV_ICM45600_ACCEL_STARTUP_TIME_MS)
+ sleepval = INV_ICM45600_ACCEL_STARTUP_TIME_MS;
+ }
+ if (gyro != oldgyro) {
+ /* Gyro startup time. */
+ if (oldgyro == INV_ICM45600_SENSOR_MODE_OFF) {
+ if (sleepval < INV_ICM45600_GYRO_STARTUP_TIME_MS)
+ sleepval = INV_ICM45600_GYRO_STARTUP_TIME_MS;
+ /* Gyro stop time. */
+ } else if (gyro == INV_ICM45600_SENSOR_MODE_OFF) {
+ if (sleepval < INV_ICM45600_GYRO_STOP_TIME_MS)
+ sleepval = INV_ICM45600_GYRO_STOP_TIME_MS;
+ }
+ }
+
+ /* Deferred sleep value if sleep pointer is provided or direct sleep */
+ if (sleep_ms)
+ *sleep_ms = 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 = &st->conf.accel;
+ unsigned int val;
+ int ret;
+
+ /* Sanitize missing values with current values. */
+ if (conf->mode < 0)
+ conf->mode = oldconf->mode;
+ if (conf->fs < 0)
+ conf->fs = oldconf->fs;
+ if (conf->odr < 0)
+ conf->odr = oldconf->odr;
+ if (conf->filter < 0)
+ conf->filter = oldconf->filter;
+
+ /* Force the power mode against the ODR when sensor is on. */
+ if (conf->mode > INV_ICM45600_SENSOR_MODE_STANDBY) {
+ if (conf->odr <= INV_ICM45600_ODR_800HZ_LN) {
+ conf->mode = INV_ICM45600_SENSOR_MODE_LOW_NOISE;
+ } else {
+ conf->mode = INV_ICM45600_SENSOR_MODE_LOW_POWER;
+ /* sanitize averaging value depending on ODR for low-power mode */
+ /* maximum 1x @400Hz */
+ if (conf->odr == INV_ICM45600_ODR_400HZ)
+ conf->filter = INV_ICM45600_ACCEL_LP_AVG_SEL_1X;
+ else
+ conf->filter = INV_ICM45600_ACCEL_LP_AVG_SEL_4X;
+ }
+ }
+
+ /* Set ACCEL_CONFIG0 register (accel fullscale & odr). */
+ if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) {
+ val = FIELD_PREP(INV_ICM45600_ACCEL_CONFIG0_FS_MASK, conf->fs) |
+ FIELD_PREP(INV_ICM45600_ACCEL_CONFIG0_ODR_MASK, conf->odr);
+ ret = regmap_write(st->map, INV_ICM45600_REG_ACCEL_CONFIG0, val);
+ if (ret)
+ return ret;
+ oldconf->fs = conf->fs;
+ oldconf->odr = conf->odr;
+ }
+
+ /* Set ACCEL_LP_AVG_SEL register (accel low-power average filter). */
+ if (conf->filter != oldconf->filter) {
+ ret = regmap_write(st->map, INV_ICM45600_IPREG_SYS2_REG_129,
+ conf->filter);
+ if (ret)
+ return ret;
+ oldconf->filter = 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 = &st->conf.gyro;
+ unsigned int val;
+ int ret;
+
+ /* Sanitize missing values with current values. */
+ if (conf->mode < 0)
+ conf->mode = oldconf->mode;
+ if (conf->fs < 0)
+ conf->fs = oldconf->fs;
+ if (conf->odr < 0)
+ conf->odr = oldconf->odr;
+ if (conf->filter < 0)
+ conf->filter = oldconf->filter;
+
+ /* Force the power mode against ODR when sensor is on. */
+ if (conf->mode > INV_ICM45600_SENSOR_MODE_STANDBY) {
+ if (conf->odr >= INV_ICM45600_ODR_6_25HZ_LP) {
+ conf->mode = INV_ICM45600_SENSOR_MODE_LOW_POWER;
+ conf->filter = INV_ICM45600_GYRO_LP_AVG_SEL_8X;
+ } else {
+ conf->mode = INV_ICM45600_SENSOR_MODE_LOW_NOISE;
+ }
+ }
+
+ /* Set GYRO_CONFIG0 register (gyro fullscale & odr). */
+ if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) {
+ val = FIELD_PREP(INV_ICM45600_GYRO_CONFIG0_FS_MASK, conf->fs) |
+ FIELD_PREP(INV_ICM45600_GYRO_CONFIG0_ODR_MASK, conf->odr);
+ ret = regmap_write(st->map, INV_ICM45600_REG_GYRO_CONFIG0, val);
+ if (ret)
+ return ret;
+ oldconf->fs = conf->fs;
+ oldconf->odr = conf->odr;
+ }
+
+ /* Set GYRO_LP_AVG_SEL register (gyro low-power average filter). */
+ if (conf->filter != oldconf->filter) {
+ val = FIELD_PREP(INV_ICM45600_IPREG_SYS1_170_GYRO_LP_AVG_MASK, conf->filter);
+ ret = regmap_update_bits(st->map, INV_ICM45600_IPREG_SYS1_REG_170,
+ INV_ICM45600_IPREG_SYS1_170_GYRO_LP_AVG_MASK, val);
+ if (ret)
+ return ret;
+ oldconf->filter = conf->filter;
+ }
+
+ /* Set PWR_MGMT0 register (gyro sensor mode). */
+ return inv_icm45600_set_pwr_mgmt0(st, conf->mode, st->conf.accel.mode,
+ sleep_ms);
+}
+
+int inv_icm45600_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int writeval, unsigned int *readval)
+{
+ struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ if (readval)
+ ret = regmap_read(st->map, reg, readval);
+ else
+ ret = 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 = FIELD_PREP(INV_ICM45600_PWR_MGMT0_GYRO_MODE_MASK, conf->gyro.mode) |
+ FIELD_PREP(INV_ICM45600_PWR_MGMT0_ACCEL_MODE_MASK, conf->accel.mode);
+ ret = regmap_write(st->map, INV_ICM45600_REG_PWR_MGMT0, val);
+ if (ret)
+ return ret;
+
+ /* Set GYRO_CONFIG0 register (gyro fullscale & odr). */
+ val = FIELD_PREP(INV_ICM45600_GYRO_CONFIG0_FS_MASK, conf->gyro.fs) |
+ FIELD_PREP(INV_ICM45600_GYRO_CONFIG0_ODR_MASK, conf->gyro.odr);
+ ret = regmap_write(st->map, INV_ICM45600_REG_GYRO_CONFIG0, val);
+ if (ret)
+ return ret;
+
+ /* Set ACCEL_CONFIG0 register (accel fullscale & odr). */
+ val = FIELD_PREP(INV_ICM45600_ACCEL_CONFIG0_FS_MASK, conf->accel.fs) |
+ FIELD_PREP(INV_ICM45600_ACCEL_CONFIG0_ODR_MASK, conf->accel.odr);
+ ret = regmap_write(st->map, INV_ICM45600_REG_ACCEL_CONFIG0, val);
+ if (ret)
+ return ret;
+
+ /* Update the internal configuration. */
+ st->conf = *conf;
+
+ return 0;
+}
+
+/**
+ * inv_icm45600_setup() - check and setup chip
+ * @st: driver internal state
+ * @chip_info: detected chip description
+ * @reset: define whether a reset is required or not
+ * @bus_setup: callback for setting up bus specific registers
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+static int inv_icm45600_setup(struct inv_icm45600_state *st,
+ const struct inv_icm45600_chip_info *chip_info,
+ bool reset, inv_icm45600_bus_setup bus_setup)
+{
+ const struct device *dev = regmap_get_device(st->map);
+ unsigned int val;
+ int ret;
+
+ /* Set chip bus configuration if specified. */
+ if (bus_setup) {
+ ret = bus_setup(st);
+ if (ret)
+ return ret;
+ }
+
+ /* Check chip self-identification value. */
+ ret = regmap_read(st->map, INV_ICM45600_REG_WHOAMI, &val);
+ if (ret)
+ return ret;
+ if (val != chip_info->whoami) {
+ dev_err(dev, "invalid whoami %#02x expected %#02x (%s)\n",
+ val, chip_info->whoami, chip_info->name);
+ return -ENODEV;
+ }
+
+ st->chip_info = chip_info;
+
+ if (reset) {
+ /* Reset to make sure previous state are not there. */
+ ret = regmap_write(st->map, INV_ICM45600_REG_MISC2,
+ INV_ICM45600_MISC2_SOFT_RESET);
+ if (ret)
+ return ret;
+ /* IMU reset time: 1ms. */
+ fsleep(1000);
+
+ if (bus_setup) {
+ ret = bus_setup(st);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_read(st->map, INV_ICM45600_REG_INT_STATUS, &val);
+ if (ret)
+ return ret;
+ if (!(val & INV_ICM45600_INT_STATUS_RESET_DONE)) {
+ dev_err(dev, "reset error, reset done bit not set\n");
+ return -ENODEV;
+ }
+ }
+
+ return inv_icm45600_set_conf(st, chip_info->conf);
+}
+
+static int inv_icm45600_timestamp_setup(struct inv_icm45600_state *st)
+{
+ /* Enable timestamps. */
+ return regmap_set_bits(st->map, INV_ICM45600_REG_SMC_CONTROL_0,
+ INV_ICM45600_SMC_CONTROL_0_TMST_EN);
+}
+
+static int inv_icm45600_enable_regulator_vddio(struct inv_icm45600_state *st)
+{
+ int ret;
+
+ ret = regulator_enable(st->vddio_supply);
+ if (ret)
+ return ret;
+
+ /* Wait a little for supply ramp. */
+ usleep_range(3000, 4000);
+
+ return 0;
+}
+
+static void inv_icm45600_disable_vddio_reg(void *_data)
+{
+ struct inv_icm45600_state *st = _data;
+ const struct device *dev = regmap_get_device(st->map);
+ int ret;
+
+ ret = regulator_disable(st->vddio_supply);
+ if (ret)
+ dev_err(dev, "failed to disable vddio error %d\n", ret);
+}
+
+int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm45600_chip_info *chip_info,
+ bool reset, inv_icm45600_bus_setup bus_setup)
+{
+ struct device *dev = regmap_get_device(regmap);
+ struct fwnode_handle *fwnode;
+ struct inv_icm45600_state *st;
+ struct regmap *regmap_custom;
+ int irq, irq_type;
+ bool open_drain;
+ int ret;
+
+ /* Get INT1 only supported interrupt. */
+ fwnode = dev_fwnode(dev);
+ if (!fwnode)
+ return dev_err_probe(dev, -ENODEV, "Missing FW node\n");
+
+ irq = fwnode_irq_get_byname(fwnode, "INT1");
+ if (irq < 0) {
+ if (irq != -EPROBE_DEFER)
+ dev_err_probe(dev, irq, "Missing INT1 interrupt\n");
+ return irq;
+ }
+
+ irq_type = irq_get_trigger_type(irq);
+
+ open_drain = device_property_read_bool(dev, "drive-open-drain");
+
+ regmap_custom = devm_regmap_init(dev, &inv_icm45600_regmap_bus,
+ regmap, &inv_icm45600_regmap_config);
+ if (IS_ERR(regmap_custom))
+ return dev_err_probe(dev, PTR_ERR(regmap_custom), "Failed to register regmap\n");
+
+ st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return dev_err_probe(dev, -ENOMEM, "Cannot allocate memory\n");
+
+ dev_set_drvdata(dev, st);
+ ret = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
+ st->map = regmap_custom;
+
+ ret = iio_read_mount_matrix(dev, &st->orientation);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to retrieve mounting matrix\n");
+
+ st->vddio_supply = devm_regulator_get(dev, "vddio");
+ if (IS_ERR(st->vddio_supply))
+ return PTR_ERR(st->vddio_supply);
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return ret;
+ /* IMU start-up time. */
+ msleep(100);
+
+ ret = inv_icm45600_enable_regulator_vddio(st);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, inv_icm45600_disable_vddio_reg, st);
+ if (ret)
+ return ret;
+
+ /* Setup chip registers. */
+ ret = inv_icm45600_setup(st, chip_info, reset, bus_setup);
+ if (ret)
+ return ret;
+
+ ret = inv_icm45600_timestamp_setup(st);
+ if (ret)
+ return ret;
+
+ /* Setup runtime power management. */
+ ret = devm_pm_runtime_set_active_enabled(dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_get_noresume(dev);
+ /* Suspend after 2 seconds. */
+ pm_runtime_set_autosuspend_delay(dev, 2000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_put(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(inv_icm45600_core_probe, "IIO_ICM45600");
+
+/*
+ * Suspend saves sensors state and turns everything off.
+ * Check first if runtime suspend has not already done the job.
+ */
+static int inv_icm45600_suspend(struct device *dev)
+{
+ struct inv_icm45600_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ st->suspended.gyro = st->conf.gyro.mode;
+ st->suspended.accel = st->conf.accel.mode;
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ ret = inv_icm45600_set_pwr_mgmt0(st, INV_ICM45600_SENSOR_MODE_OFF,
+ INV_ICM45600_SENSOR_MODE_OFF, NULL);
+ if (ret)
+ return ret;
+
+ regulator_disable(st->vddio_supply);
+
+ return 0;
+}
+
+/*
+ * System resume gets the system back on and restores the sensors state.
+ * Manually put runtime power management in system active state.
+ */
+static int inv_icm45600_resume(struct device *dev)
+{
+ struct inv_icm45600_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ ret = inv_icm45600_enable_regulator_vddio(st);
+ if (ret)
+ return ret;
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ /* Restore sensors state. */
+ return inv_icm45600_set_pwr_mgmt0(st, st->suspended.gyro,
+ st->suspended.accel, NULL);
+
+}
+
+/* Runtime suspend will turn off sensors that are enabled by iio devices. */
+static int inv_icm45600_runtime_suspend(struct device *dev)
+{
+ struct inv_icm45600_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ /* disable all sensors */
+ ret = inv_icm45600_set_pwr_mgmt0(st, INV_ICM45600_SENSOR_MODE_OFF,
+ INV_ICM45600_SENSOR_MODE_OFF, NULL);
+ if (ret)
+ return ret;
+
+ regulator_disable(st->vddio_supply);
+
+ return 0;
+}
+
+/* Sensors are enabled by iio devices, no need to turn them back on here. */
+static int inv_icm45600_runtime_resume(struct device *dev)
+{
+ struct inv_icm45600_state *st = dev_get_drvdata(dev);
+
+ guard(mutex)(&st->lock);
+
+ return inv_icm45600_enable_regulator_vddio(st);
+}
+
+EXPORT_NS_GPL_DEV_PM_OPS(inv_icm45600_pm_ops, IIO_ICM45600) = {
+ 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");
--
2.34.1
On Thu, 10 Jul 2025 08:57:57 +0000 Remi Buisson via B4 Relay <devnull+remi.buisson.tdk.com@kernel.org> wrote: > From: Remi Buisson <remi.buisson@tdk.com> > > Core component of a new driver for InvenSense ICM-45600 devices. > It includes registers definition, main probe/setup, and device > utility functions. > > ICM-456xx devices are latest generation of 6-axis IMU, > gyroscope+accelerometer and temperature sensor. This device > includes a 8K FIFO, supports I2C/I3C/SPI, and provides > intelligent motion features like pedometer, tilt detection, > and tap detection. > > Signed-off-by: Remi Buisson <remi.buisson@tdk.com> Hi Remi, A few minor things inline. Given Sean has been looking at a closely related driver and you have some of the things he's been fixing up I +CC Sean on off chance he has time to take a look at this. > --- > drivers/iio/imu/Kconfig | 1 + > drivers/iio/imu/Makefile | 1 + > drivers/iio/imu/inv_icm45600/Kconfig | 5 + > drivers/iio/imu/inv_icm45600/Makefile | 4 + > drivers/iio/imu/inv_icm45600/inv_icm45600.h | 364 +++++++++++ > drivers/iio/imu/inv_icm45600/inv_icm45600_core.c | 734 +++++++++++++++++++++++ > 6 files changed, 1109 insertions(+) > > diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600.h b/drivers/iio/imu/inv_icm45600/inv_icm45600.h > new file mode 100644 > index 0000000000000000000000000000000000000000..d56de75ab7f2168f22e25b5816cb361bef457c0d > --- /dev/null > +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600.h > @@ -0,0 +1,364 @@ > +struct inv_icm45600_sensor_conf { > + int mode; > + int fs; > + int odr; > + int filter; > +}; > +#define INV_ICM45600_SENSOR_CONF_INIT {-1, -1, -1, -1} +#define INV_ICM45600_SENSOR_CONF_INIT { -1, -1, -1, -1, } Though I'm not sure the define is that useful vs pushing this down into the code. > 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..022eb9180b5a750e8fba4a1cd873041c27f59880 > --- /dev/null > +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_core.c > +static int inv_icm45600_read(void *context, const void *reg_buf, size_t reg_size, > + void *val_buf, size_t val_size) > +{ > + unsigned int reg = (unsigned int) be16_to_cpup(reg_buf); Why is the cast needed? It's a u16 so why not keep it like that? > + struct regmap *map = context; > + > + if (FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg) == 0) > + return regmap_bulk_read(map, FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg), > + val_buf, val_size); > + > + return inv_icm45600_ireg_read(map, reg, val_buf, val_size); > +} > + > +static int inv_icm45600_write(void *context, const void *data, > + size_t count) > +{ > + u8 *d = (u8 *)data; const u8 *d = data; should be fine. > + unsigned int reg = (unsigned int) be16_to_cpup(data); u16 > + struct regmap *map = context; > + > + if (FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg) == 0) > + return regmap_bulk_write(map, FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg), > + d + 2, count - 2); > + > + return inv_icm45600_ireg_write(map, reg, d + 2, count - 2); > +} > + * inv_icm45600_setup() - check and setup chip > + * @st: driver internal state > + * @chip_info: detected chip description > + * @reset: define whether a reset is required or not > + * @bus_setup: callback for setting up bus specific registers > + * > + * Returns 0 on success, a negative error code otherwise. > + */ > +static int inv_icm45600_setup(struct inv_icm45600_state *st, > + const struct inv_icm45600_chip_info *chip_info, > + bool reset, inv_icm45600_bus_setup bus_setup) > +{ > + const struct device *dev = regmap_get_device(st->map); > + unsigned int val; > + int ret; > + > + /* Set chip bus configuration if specified. */ > + if (bus_setup) { > + ret = bus_setup(st); > + if (ret) > + return ret; > + } > + > + /* Check chip self-identification value. */ > + ret = regmap_read(st->map, INV_ICM45600_REG_WHOAMI, &val); > + if (ret) > + return ret; > + if (val != chip_info->whoami) { > + dev_err(dev, "invalid whoami %#02x expected %#02x (%s)\n", This should be relaxed to a dev_info and carry on anyway. The reasoning for this is fallback compatibles in DT. If a new compatible chip with a different whoami value comes along we still want it to run with older kernels so we use fallback compatibles. Unfortunately the ID won't match. > + val, chip_info->whoami, chip_info->name); > + return -ENODEV; > + } > + > + st->chip_info = chip_info; > + > + if (reset) { > + /* Reset to make sure previous state are not there. */ > + ret = regmap_write(st->map, INV_ICM45600_REG_MISC2, > + INV_ICM45600_MISC2_SOFT_RESET); > + if (ret) > + return ret; > + /* IMU reset time: 1ms. */ > + fsleep(1000); > + > + if (bus_setup) { > + ret = bus_setup(st); > + if (ret) > + return ret; > + } > + > + ret = regmap_read(st->map, INV_ICM45600_REG_INT_STATUS, &val); > + if (ret) > + return ret; > + if (!(val & INV_ICM45600_INT_STATUS_RESET_DONE)) { > + dev_err(dev, "reset error, reset done bit not set\n"); > + return -ENODEV; > + } > + } > + > + return inv_icm45600_set_conf(st, chip_info->conf); > +} > + > +static void inv_icm45600_disable_vddio_reg(void *_data) > +{ > + struct inv_icm45600_state *st = _data; > + const struct device *dev = regmap_get_device(st->map); > + int ret; > + > + ret = regulator_disable(st->vddio_supply); > + if (ret) > + dev_err(dev, "failed to disable vddio error %d\n", ret); As per Sean's set - is this useful given such failures are already noisy. > +} > + > +int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm45600_chip_info *chip_info, > + bool reset, inv_icm45600_bus_setup bus_setup) > +{ > + struct device *dev = regmap_get_device(regmap); > + struct fwnode_handle *fwnode; > + struct inv_icm45600_state *st; > + struct regmap *regmap_custom; > + int irq, irq_type; > + bool open_drain; > + int ret; > + > + /* Get INT1 only supported interrupt. */ > + fwnode = dev_fwnode(dev); > + if (!fwnode) > + return dev_err_probe(dev, -ENODEV, "Missing FW node\n"); > + > + irq = fwnode_irq_get_byname(fwnode, "INT1"); > + if (irq < 0) { > + if (irq != -EPROBE_DEFER) > + dev_err_probe(dev, irq, "Missing INT1 interrupt\n"); > + return irq; > + } > + > + irq_type = irq_get_trigger_type(irq); > + > + open_drain = device_property_read_bool(dev, "drive-open-drain"); > + > + regmap_custom = devm_regmap_init(dev, &inv_icm45600_regmap_bus, > + regmap, &inv_icm45600_regmap_config); > + if (IS_ERR(regmap_custom)) > + return dev_err_probe(dev, PTR_ERR(regmap_custom), "Failed to register regmap\n"); > + > + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); > + if (!st) > + return dev_err_probe(dev, -ENOMEM, "Cannot allocate memory\n"); > + > + dev_set_drvdata(dev, st); > + ret = devm_mutex_init(dev, &st->lock); > + if (ret) > + return ret; > + > + st->map = regmap_custom; > + > + ret = iio_read_mount_matrix(dev, &st->orientation); > + if (ret) > + return dev_err_probe(dev, ret, "Failed to retrieve mounting matrix\n"); > + > + st->vddio_supply = devm_regulator_get(dev, "vddio"); > + if (IS_ERR(st->vddio_supply)) > + return PTR_ERR(st->vddio_supply); > + > + ret = devm_regulator_get_enable(dev, "vdd"); > + if (ret) > + return ret; > + /* IMU start-up time. */ > + msleep(100); > + > + ret = inv_icm45600_enable_regulator_vddio(st); > + if (ret) > + return ret; > + > + ret = devm_add_action_or_reset(dev, inv_icm45600_disable_vddio_reg, st); > + if (ret) > + return ret; > + > + /* Setup chip registers. */ > + ret = inv_icm45600_setup(st, chip_info, reset, bus_setup); > + if (ret) > + return ret; > + > + ret = inv_icm45600_timestamp_setup(st); > + if (ret) > + return ret; > + > + /* Setup runtime power management. */ > + ret = devm_pm_runtime_set_active_enabled(dev); > + if (ret) > + return ret; > + > + pm_runtime_get_noresume(dev); > + /* Suspend after 2 seconds. */ > + pm_runtime_set_autosuspend_delay(dev, 2000); > + pm_runtime_use_autosuspend(dev); > + pm_runtime_put(dev); See below. Sean also reworks this. + I suggested he look at devm_pm_runtime_enable but the set active version seems even better. I'm not setting the gets and puts I'd expect for working runtime PM though. > + > + return 0; > +} > +EXPORT_SYMBOL_NS_GPL(inv_icm45600_core_probe, "IIO_ICM45600"); > + > +/* > + * 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 = dev_get_drvdata(dev); > + int ret; > + > + guard(mutex)(&st->lock); > + > + ret = inv_icm45600_enable_regulator_vddio(st); > + if (ret) > + return ret; > + > + pm_runtime_disable(dev); > + pm_runtime_set_active(dev); > + pm_runtime_enable(dev); Take a look at https://lore.kernel.org/all/20250709-icm42pmreg-v1-0-3d0e793c99b2@geanix.com/ It would be good if you an review Sean's series as well as applying anything you agree with here. For example he got rid of the 3 lines above. > + > + /* Restore sensors state. */ > + return inv_icm45600_set_pwr_mgmt0(st, st->suspended.gyro, > + st->suspended.accel, NULL); > + > +} > + > +/* Runtime suspend will turn off sensors that are enabled by iio devices. */ > +static int inv_icm45600_runtime_suspend(struct device *dev) > +{ > + struct inv_icm45600_state *st = dev_get_drvdata(dev); > + int ret; > + > + guard(mutex)(&st->lock); > + > + /* disable all sensors */ > + ret = inv_icm45600_set_pwr_mgmt0(st, INV_ICM45600_SENSOR_MODE_OFF, > + INV_ICM45600_SENSOR_MODE_OFF, NULL); > + if (ret) > + return ret; > + > + regulator_disable(st->vddio_supply); > + > + return 0; > +} > + > +/* Sensors are enabled by iio devices, no need to turn them back on here. */ > +static int inv_icm45600_runtime_resume(struct device *dev) > +{ > + struct inv_icm45600_state *st = dev_get_drvdata(dev); > + > + guard(mutex)(&st->lock); > + > + return inv_icm45600_enable_regulator_vddio(st); > +} > + > +EXPORT_NS_GPL_DEV_PM_OPS(inv_icm45600_pm_ops, IIO_ICM45600) = { > + SET_SYSTEM_SLEEP_PM_OPS(inv_icm45600_suspend, inv_icm45600_resume) > + SET_RUNTIME_PM_OPS(inv_icm45600_runtime_suspend, > + inv_icm45600_runtime_resume, NULL) > +};
Hi Remi, kernel test robot noticed the following build warnings: [auto build test WARNING on f8f559752d573a051a984adda8d2d1464f92f954] url: https://github.com/intel-lab-lkp/linux/commits/Remi-Buisson-via-B4-Relay/dt-bindings-iio-imu-Add-inv_icm45600/20250710-170143 base: f8f559752d573a051a984adda8d2d1464f92f954 patch link: https://lore.kernel.org/r/20250710-add_newport_driver-v2-2-bf76d8142ef2%40tdk.com patch subject: [PATCH v2 2/8] iio: imu: inv_icm45600: add new inv_icm45600 driver config: i386-buildonly-randconfig-001-20250711 (https://download.01.org/0day-ci/archive/20250711/202507111042.NdeRL3wM-lkp@intel.com/config) compiler: gcc-12 (Debian 12.2.0-14+deb12u1) 12.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250711/202507111042.NdeRL3wM-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202507111042.NdeRL3wM-lkp@intel.com/ All warnings (new ones prefixed by >>): drivers/hsi/hsi_boardinfo.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hsi/hsi_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hte/hte.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hv/channel.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hv/channel_mgmt.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hv/hv_proc.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hv/mshv_common.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hv/mshv_root_hv_call.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hv/ring_buffer.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hv/vmbus_drv.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hwmon/adt7x10.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hwmon/hwmon-vid.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hwmon/hwmon.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hwmon/ltc2947-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hwmon/max1111.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hwmon/nct6775-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hwmon/pmbus/pmbus_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hwmon/sch56xx-common.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hwspinlock/hwspinlock_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hwtracing/coresight/coresight-etm-perf.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hwtracing/coresight/coresight-platform.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hwtracing/coresight/coresight-syscfg.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hwtracing/coresight/coresight-sysfs.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hwtracing/coresight/coresight-tmc-etr.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hwtracing/coresight/coresight-trace-id.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hwtracing/intel_th/core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hwtracing/intel_th/msu.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hwtracing/stm/core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/hwtracing/stm/policy.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/i2c/algos/i2c-algo-bit.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/i2c/algos/i2c-algo-pca.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/i2c/algos/i2c-algo-pcf.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/i2c/busses/i2c-amd-mp2-pci.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/i2c/busses/i2c-designware-slave.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/i2c/busses/i2c-pasemi-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/i2c/busses/i2c-piix4.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/i2c/busses/i2c-viai2c-common.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/i2c/i2c-atr.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/i2c/i2c-core-acpi.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/i2c/i2c-core-base.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/i2c/i2c-core-of-prober.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/i2c/i2c-core-of.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/i2c/i2c-core-slave.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/i2c/i2c-core-smbus.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/i2c/i2c-mux.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/i2c/i2c-smbus.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/i3c/device.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/i3c/master/dw-i3c-master.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/accel/adxl313_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/accel/adxl345_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/accel/adxl355_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/accel/adxl367.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/accel/adxl372.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/accel/adxl380.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/accel/bma400_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/accel/bmc150-accel-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/accel/bmi088-accel-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/accel/fxls8962af-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/accel/kxsd9.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/accel/mma7455_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/accel/mma9551_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/accel/st_accel_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/adc/ad7091r-base.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/adc/ad7606.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/adc/ltc2497-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/adc/qcom-vadc-common.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/afe/iio-rescale.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/buffer/industrialio-buffer-dma.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/buffer/industrialio-buffer-dmaengine.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/buffer/kfifo_buf.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/chemical/bme680_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/chemical/ens160_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/chemical/sps30.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/common/hid-sensors/hid-sensor-attributes.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/common/hid-sensors/hid-sensor-trigger.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/common/inv_sensors/inv_sensors_timestamp.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/common/ms_sensors/ms_sensors_i2c.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/common/ssp_sensors/ssp_dev.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/common/ssp_sensors/ssp_iio.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/common/st_sensors/st_sensors_buffer.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/common/st_sensors/st_sensors_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/common/st_sensors/st_sensors_i2c.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/common/st_sensors/st_sensors_spi.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/common/st_sensors/st_sensors_trigger.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/dac/ad3552r-common.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/dac/ad5592r-base.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/dac/ad5686.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/dummy/iio_dummy_evgen.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/gyro/bmg160_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/gyro/fxas21002c_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/gyro/st_gyro_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/humidity/hts221_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/imu/adis.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/imu/bmi160/bmi160_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/imu/bmi270/bmi270_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/imu/bmi323/bmi323_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/imu/bno055/bno055.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/imu/fxos8700_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/imu/inv_icm42600/inv_icm42600_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing >> drivers/iio/imu/inv_icm45600/inv_icm45600_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/imu/inv_mpu6050/inv_mpu_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/industrialio-backend.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/industrialio-configfs.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/industrialio-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/industrialio-event.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/industrialio-sw-device.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/industrialio-sw-trigger.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/industrialio-trigger.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/light/st_uvis25_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/magnetometer/bmc150_magn.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/magnetometer/hmc5843_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/magnetometer/rm3100-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/magnetometer/st_magn_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/pressure/bmp280-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/pressure/bmp280-regmap.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/pressure/hsc030pa.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/pressure/mpl115.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/pressure/mprls0025pa.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/pressure/ms5611_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/pressure/st_pressure_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iio/pressure/zpa2326.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/addr.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/cache.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/cgroup.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/cm.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/cma.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/cq.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/device.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/ib_core_uverbs.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/iwcm.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/mad.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/mr_pool.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/nldev.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/rdma_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/restrack.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/roce_gid_mgmt.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/rw.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/sa_query.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/security.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/sysfs.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/ucaps.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/umem_dmabuf.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/uverbs_cmd.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/uverbs_ioctl.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/uverbs_main.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/core/uverbs_std_types.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/sw/rdmavt/ah.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/sw/rdmavt/cq.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/sw/rdmavt/mcast.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/sw/rdmavt/mr.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/sw/rdmavt/qp.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/sw/rdmavt/rc.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/sw/rdmavt/vt.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/ulp/rtrs/rtrs-clt.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/ulp/rtrs/rtrs-srv.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/infiniband/ulp/rtrs/rtrs.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/ff-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/ff-memless.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/gameport/gameport.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/input-poller.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/input.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/joystick/iforce/iforce-main.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/joystick/iforce/iforce-packets.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/misc/ad714x.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/misc/adxl34x.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/misc/cma3000_d0x.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/rmi4/rmi_2d_sensor.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/rmi4/rmi_bus.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/rmi4/rmi_driver.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/serio/hil_mlc.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/serio/hp_sdc.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/serio/i8042.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/serio/libps2.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/serio/serio.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/sparse-keymap.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/touchscreen.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/touchscreen/ad7879.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/touchscreen/cyttsp_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/touchscreen/goodix_berlin_core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/touchscreen/tsc200x-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/touchscreen/wm9705.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/touchscreen/wm9712.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/touchscreen/wm9713.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/input/touchscreen/wm97xx-core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/interconnect/core.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/interconnect/icc-clk.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/interconnect/imx/imx.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/interconnect/mediatek/icc-emi.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/interconnect/qcom/bcm-voter.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/interconnect/qcom/icc-common.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/interconnect/qcom/icc-rpm-clocks.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/interconnect/qcom/icc-rpm.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/interconnect/qcom/icc-rpmh.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/interconnect/qcom/smd-rpm.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iommu/dma-iommu.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iommu/intel/dmar.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iommu/intel/iommu.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing drivers/iommu/io-pgfault.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
On Thu, Jul 10, 2025 at 08:57:57AM +0000, Remi Buisson via B4 Relay wrote: > From: Remi Buisson <remi.buisson@tdk.com> > > Core component of a new driver for InvenSense ICM-45600 devices. > It includes registers definition, main probe/setup, and device > utility functions. > > ICM-456xx devices are latest generation of 6-axis IMU, > gyroscope+accelerometer and temperature sensor. This device > includes a 8K FIFO, supports I2C/I3C/SPI, and provides > intelligent motion features like pedometer, tilt detection, > and tap detection. ... > + INV_ICM45600_SENSOR_MODE_NB What does the _NB stand for? Number of Bullets? ... > +struct inv_icm45600_sensor_conf { > + int mode; > + int fs; > + int odr; > + int filter; Any of them can hold negative value? > +}; ... > +#define INV_ICM45600_SENSOR_CONF_INIT {-1, -1, -1, -1} Unused. > +#include <linux/bitfield.h> > +#include <linux/delay.h> > +#include <linux/device.h> > +#include <linux/err.h> > +#include <linux/iio/iio.h> > +#include <linux/interrupt.h> > +#include <linux/irq.h> > +#include <linux/module.h> > +#include <linux/mutex.h> > +#include <linux/pm_runtime.h> > +#include <linux/property.h> > +#include <linux/regmap.h> > +#include <linux/regulator/consumer.h> > +#include <linux/types.h> ... > +static int inv_icm45600_ireg_read(struct regmap *map, unsigned int reg, > + u8 *data, size_t count) > +{ > + int ret; > + u8 addr[2]; > + ssize_t i; > + unsigned int d; > + > + addr[0] = FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg); > + addr[1] = FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg); > + > + /* Burst write address. */ > + ret = regmap_bulk_write(map, INV_ICM45600_REG_IREG_ADDR, addr, 2); sizeof()? > + udelay(INV_ICM45600_IREG_DELAY_US); See below. This is also weird. > + if (ret) > + return ret; > + > + /* Read the data. */ > + for (i = 0; i < count; i++) { > + ret = regmap_read(map, INV_ICM45600_REG_IREG_DATA, &d); > + data[i] = d; > + udelay(INV_ICM45600_IREG_DELAY_US); Can fsleep() be used here? > + if (ret) > + return ret; This is weird. First you assign a garbage to the output, delay and return an error. It seems entire code is broken... Please, fix all these and try again, I stop my review here. > + } > + > + return 0; > +} -- With Best Regards, Andy Shevchenko
© 2016 - 2025 Red Hat, Inc.