diff --git a/drivers/sensor/tdk/CMakeLists.txt b/drivers/sensor/tdk/CMakeLists.txt index 7e6c4090391a3..e4b8605a4495d 100644 --- a/drivers/sensor/tdk/CMakeLists.txt +++ b/drivers/sensor/tdk/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory_ifdef(CONFIG_ICM42605 icm42605) add_subdirectory_ifdef(CONFIG_ICM42688 icm42688) add_subdirectory_ifdef(CONFIG_ICM42X70 icm42x70) +add_subdirectory_ifdef(CONFIG_ICM45686 icm45686) add_subdirectory_ifdef(CONFIG_ICP101XX icp101xx) add_subdirectory_ifdef(CONFIG_MPU6050 mpu6050) add_subdirectory_ifdef(CONFIG_MPU9250 mpu9250) diff --git a/drivers/sensor/tdk/Kconfig b/drivers/sensor/tdk/Kconfig index c903842785121..8f5d61a4edd88 100644 --- a/drivers/sensor/tdk/Kconfig +++ b/drivers/sensor/tdk/Kconfig @@ -5,6 +5,7 @@ source "drivers/sensor/tdk/icm42605/Kconfig" source "drivers/sensor/tdk/icm42688/Kconfig" source "drivers/sensor/tdk/icm42x70/Kconfig" +source "drivers/sensor/tdk/icm45686/Kconfig" source "drivers/sensor/tdk/icp101xx/Kconfig" source "drivers/sensor/tdk/mpu6050/Kconfig" source "drivers/sensor/tdk/mpu9250/Kconfig" diff --git a/drivers/sensor/tdk/icm45686/CMakeLists.txt b/drivers/sensor/tdk/icm45686/CMakeLists.txt new file mode 100644 index 0000000000000..aedf82fa6c417 --- /dev/null +++ b/drivers/sensor/tdk/icm45686/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (c) 2025 Croxel Inc. +# Copyright (c) 2025 CogniPilot Foundation +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_include_directories(.) +zephyr_library_sources( + icm45686.c +) +zephyr_library_sources_ifdef(CONFIG_SENSOR_ASYNC_API + icm45686_decoder.c +) +zephyr_library_sources_ifdef(CONFIG_ICM45686_TRIGGER + icm45686_trigger.c +) diff --git a/drivers/sensor/tdk/icm45686/Kconfig b/drivers/sensor/tdk/icm45686/Kconfig new file mode 100644 index 0000000000000..f755e420d377d --- /dev/null +++ b/drivers/sensor/tdk/icm45686/Kconfig @@ -0,0 +1,57 @@ +# Copyright (c) 2025 Croxel Inc. +# Copyright (c) 2025 CogniPilot Foundation +# SPDX-License-Identifier: Apache-2.0 + +menuconfig ICM45686 + bool "ICM45686 High-precision 6-Axis Motion Tracking Device" + default y + depends on DT_HAS_INVENSENSE_ICM45686_ENABLED + select SPI + select SPI_RTIO + help + Enable driver for ICM45686 High-precision 6-axis motion + tracking device. + +if ICM45686 + +choice + prompt "Trigger mode" + default ICM45686_TRIGGER_GLOBAL_THREAD + help + Specify the type of triggering to be used by the driver + +config ICM45686_TRIGGER_NONE + bool "No trigger" + +config ICM45686_TRIGGER_GLOBAL_THREAD + bool "Use global thread" + depends on GPIO + depends on $(dt_compat_any_has_prop,$(DT_COMPAT_INVENSENSE_ICM45686),int-gpios) + select ICM45686_TRIGGER + +config ICM45686_TRIGGER_OWN_THREAD + bool "Use own thread" + depends on GPIO + depends on $(dt_compat_any_has_prop,$(DT_COMPAT_INVENSENSE_ICM45686),int-gpios) + select ICM45686_TRIGGER + +endchoice + +config ICM45686_TRIGGER + bool + +config ICM45686_THREAD_PRIORITY + int "Own thread priority" + depends on ICM45686_TRIGGER_OWN_THREAD + default 10 + help + The priority of the thread used for handling triggers. + +config ICM45686_THREAD_STACK_SIZE + int "Own thread stack size" + depends on ICM45686_TRIGGER_OWN_THREAD + default 1024 + help + The thread stack size. + +endif # ICM45686 diff --git a/drivers/sensor/tdk/icm45686/icm45686.c b/drivers/sensor/tdk/icm45686/icm45686.c new file mode 100644 index 0000000000000..7f6cf49fba739 --- /dev/null +++ b/drivers/sensor/tdk/icm45686/icm45686.c @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2022 Intel Corporation + * Copyright (c) 2025 Croxel Inc. + * Copyright (c) 2025 CogniPilot Foundation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT invensense_icm45686 + +#include +#include +#include +#include + +#if defined(CONFIG_SENSOR_ASYNC_API) +#include +#endif /* CONFIG_SENSOR_ASYNC_API */ + +#include "icm45686.h" +#include "icm45686_reg.h" +#include "icm45686_bus.h" +#include "icm45686_decoder.h" +#include "icm45686_trigger.h" + +#include +LOG_MODULE_REGISTER(ICM45686, CONFIG_SENSOR_LOG_LEVEL); + +static inline int reg_write(const struct device *dev, uint8_t reg, uint8_t val) +{ + return icm45686_bus_write(dev, reg, &val, 1); +} + +static inline int reg_read(const struct device *dev, uint8_t reg, uint8_t *val) +{ + return icm45686_bus_read(dev, reg, val, 1); +} + +static int icm45686_sample_fetch(const struct device *dev, + enum sensor_channel chan) +{ + int err; + struct icm45686_data *data = dev->data; + struct icm45686_encoded_data *edata = &data->edata; + + if (chan != SENSOR_CHAN_ALL) { + return -ENOTSUP; + } + + err = icm45686_bus_read(dev, + REG_ACCEL_DATA_X1_UI, + edata->payload.buf, + sizeof(edata->payload.buf)); + + LOG_HEXDUMP_DBG(edata->payload.buf, + sizeof(edata->payload.buf), + "ICM45686 data"); + + return err; +} + +static int icm45686_channel_get(const struct device *dev, + enum sensor_channel chan, + struct sensor_value *val) +{ + struct icm45686_data *data = dev->data; + + switch (chan) { + case SENSOR_CHAN_ACCEL_X: + icm45686_accel_ms(&data->edata, data->edata.payload.accel.x, + &val->val1, &val->val2); + break; + case SENSOR_CHAN_ACCEL_Y: + icm45686_accel_ms(&data->edata, data->edata.payload.accel.y, + &val->val1, &val->val2); + break; + case SENSOR_CHAN_ACCEL_Z: + icm45686_accel_ms(&data->edata, data->edata.payload.accel.z, + &val->val1, &val->val2); + break; + case SENSOR_CHAN_GYRO_X: + icm45686_gyro_rads(&data->edata, data->edata.payload.gyro.x, + &val->val1, &val->val2); + break; + case SENSOR_CHAN_GYRO_Y: + icm45686_gyro_rads(&data->edata, data->edata.payload.gyro.y, + &val->val1, &val->val2); + break; + case SENSOR_CHAN_GYRO_Z: + icm45686_gyro_rads(&data->edata, data->edata.payload.gyro.z, + &val->val1, &val->val2); + break; + case SENSOR_CHAN_DIE_TEMP: + icm45686_temp_c(data->edata.payload.temp, &val->val1, &val->val2); + break; + case SENSOR_CHAN_ACCEL_XYZ: + icm45686_accel_ms(&data->edata, data->edata.payload.accel.x, + &val[0].val1, &val[0].val2); + icm45686_accel_ms(&data->edata, data->edata.payload.accel.y, + &val[1].val1, &val[1].val2); + icm45686_accel_ms(&data->edata, data->edata.payload.accel.z, + &val[2].val1, &val[2].val2); + break; + case SENSOR_CHAN_GYRO_XYZ: + icm45686_gyro_rads(&data->edata, data->edata.payload.gyro.x, + &val->val1, &val->val2); + icm45686_gyro_rads(&data->edata, data->edata.payload.gyro.y, + &val[1].val1, &val[1].val2); + icm45686_gyro_rads(&data->edata, data->edata.payload.gyro.z, + &val[2].val1, &val[2].val2); + break; + default: + return -ENOTSUP; + } + + return 0; +} + +#if defined(CONFIG_SENSOR_ASYNC_API) + +static void icm45686_complete_result(struct rtio *ctx, + const struct rtio_sqe *sqe, + void *arg) +{ + struct rtio_iodev_sqe *iodev_sqe = (struct rtio_iodev_sqe *)sqe->userdata; + struct rtio_cqe *cqe; + int err = 0; + + do { + cqe = rtio_cqe_consume(ctx); + if (cqe != NULL) { + err = cqe->result; + rtio_cqe_release(ctx, cqe); + } + } while (cqe != NULL); + + if (err) { + rtio_iodev_sqe_err(iodev_sqe, err); + } else { + rtio_iodev_sqe_ok(iodev_sqe, 0); + } + + LOG_DBG("One-shot fetch completed"); +} + +static inline void icm45686_submit_one_shot(const struct device *dev, + struct rtio_iodev_sqe *iodev_sqe) +{ + const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data; + const struct sensor_chan_spec *const channels = cfg->channels; + const size_t num_channels = cfg->count; + uint32_t min_buf_len = sizeof(struct icm45686_encoded_data); + int err; + uint8_t *buf; + uint32_t buf_len; + struct icm45686_encoded_data *edata; + struct icm45686_data *data = dev->data; + + err = rtio_sqe_rx_buf(iodev_sqe, min_buf_len, min_buf_len, &buf, &buf_len); + if (err != 0) { + LOG_ERR("Failed to get a read buffer of size %u bytes", min_buf_len); + rtio_iodev_sqe_err(iodev_sqe, err); + return; + } + + edata = (struct icm45686_encoded_data *)buf; + + err = icm45686_encode(dev, channels, num_channels, buf); + if (err != 0) { + LOG_ERR("Failed to encode sensor data"); + rtio_iodev_sqe_err(iodev_sqe, err); + return; + } + + struct rtio_sqe *write_sqe = rtio_sqe_acquire(data->rtio.ctx); + struct rtio_sqe *read_sqe = rtio_sqe_acquire(data->rtio.ctx); + struct rtio_sqe *complete_sqe = rtio_sqe_acquire(data->rtio.ctx); + + if (!write_sqe || !read_sqe | !complete_sqe) { + LOG_ERR("Failed to acquire RTIO SQEs"); + rtio_iodev_sqe_err(iodev_sqe, -ENOMEM); + return; + } + + uint8_t val = REG_ACCEL_DATA_X1_UI | REG_SPI_READ_BIT; + + rtio_sqe_prep_tiny_write(write_sqe, + data->rtio.iodev, + RTIO_PRIO_HIGH, + &val, + 1, + NULL); + write_sqe->flags |= RTIO_SQE_TRANSACTION; + + rtio_sqe_prep_read(read_sqe, + data->rtio.iodev, + RTIO_PRIO_HIGH, + edata->payload.buf, + sizeof(edata->payload.buf), + NULL); + read_sqe->flags |= RTIO_SQE_CHAINED; + + rtio_sqe_prep_callback_no_cqe(complete_sqe, + icm45686_complete_result, + (void *)dev, + iodev_sqe); + + rtio_submit(data->rtio.ctx, 0); +} + +static void icm45686_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) +{ + const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data; + + if (!cfg->is_streaming) { + icm45686_submit_one_shot(dev, iodev_sqe); + } else { + LOG_ERR("Streaming not supported"); + rtio_iodev_sqe_err(iodev_sqe, -ENOTSUP); + } +} + +#endif /* CONFIG_SENSOR_ASYNC_API */ + +static DEVICE_API(sensor, icm45686_driver_api) = { + .sample_fetch = icm45686_sample_fetch, + .channel_get = icm45686_channel_get, +#if defined(CONFIG_ICM45686_TRIGGER) + .trigger_set = icm45686_trigger_set, +#endif /* CONFIG_ICM45686_TRIGGER */ +#if defined(CONFIG_SENSOR_ASYNC_API) + .get_decoder = icm45686_get_decoder, + .submit = icm45686_submit, +#endif /* CONFIG_SENSOR_ASYNC_API */ +}; + +static int icm45686_init(const struct device *dev) +{ + struct icm45686_data *data = dev->data; + const struct icm45686_config *cfg = dev->config; + uint8_t read_val = 0; + uint8_t val; + int err; + + if (!spi_is_ready_iodev(data->rtio.iodev)) { + LOG_ERR("Bus is not ready"); + return -ENODEV; + } + + /* Soft-reset sensor to restore config to defaults */ + + err = reg_write(dev, REG_MISC2, REG_MISC2_SOFT_RST(1)); + if (err) { + LOG_ERR("Failed to write soft-reset: %d", err); + return err; + } + /* Wait for soft-reset to take effect */ + k_sleep(K_MSEC(1)); + + /* A complete soft-reset clears the bit */ + err = reg_read(dev, REG_MISC2, &read_val); + if (err) { + LOG_ERR("Failed to read soft-reset: %d", err); + return err; + } + if ((read_val & REG_MISC2_SOFT_RST(1)) != 0) { + LOG_ERR("Soft-reset command failed"); + return -EIO; + } + + /* Set Slew-rate to 10-ns typical, to allow proper SPI readouts */ + + err = reg_write(dev, REG_DRIVE_CONFIG0, REG_DRIVE_CONFIG0_SPI_SLEW(2)); + if (err) { + LOG_ERR("Failed to write slew-rate: %d", err); + return err; + } + /* Wait for register to take effect */ + k_sleep(K_USEC(2)); + + /* Confirm ID Value matches */ + err = reg_read(dev, REG_WHO_AM_I, &read_val); + if (err) { + LOG_ERR("Failed to read WHO_AM_I: %d", err); + return err; + } + if (read_val != WHO_AM_I_ICM45686) { + LOG_ERR("Unexpected WHO_AM_I value - expected: 0x%02x, actual: 0x%02x", + WHO_AM_I_ICM45686, read_val); + return -EIO; + } + + /* Sensor Configuration */ + + val = REG_PWR_MGMT0_ACCEL_MODE(cfg->settings.accel.pwr_mode) | + REG_PWR_MGMT0_GYRO_MODE(cfg->settings.gyro.pwr_mode); + err = reg_write(dev, REG_PWR_MGMT0, val); + if (err) { + LOG_ERR("Failed to write Power settings: %d", err); + return err; + } + + val = REG_ACCEL_CONFIG0_ODR(cfg->settings.accel.odr) | + REG_ACCEL_CONFIG0_FS(cfg->settings.accel.fs); + err = reg_write(dev, REG_ACCEL_CONFIG0, val); + if (err) { + LOG_ERR("Failed to write Accel settings: %d", err); + return err; + } + + val = REG_GYRO_CONFIG0_ODR(cfg->settings.gyro.odr) | + REG_GYRO_CONFIG0_FS(cfg->settings.gyro.fs); + err = reg_write(dev, REG_GYRO_CONFIG0, val); + if (err) { + LOG_ERR("Failed to write Gyro settings: %d", err); + return err; + } + + /** Write Low-pass filter settings through indirect register access */ + uint8_t gyro_lpf_write_array[] = REG_IREG_PREPARE_WRITE_ARRAY( + REG_IPREG_SYS1_OFFSET, + REG_IPREG_SYS1_REG_172, + REG_IPREG_SYS1_REG_172_GYRO_LPFBW_SEL( + cfg->settings.gyro.lpf)); + + err = icm45686_bus_write(dev, REG_IREG_ADDR_15_8, gyro_lpf_write_array, + sizeof(gyro_lpf_write_array)); + if (err) { + LOG_ERR("Failed to set Gyro BW settings: %d", err); + return err; + } + + /** Wait before indirect register write is made effective + * before proceeding with next one. + */ + k_sleep(K_MSEC(1)); + + uint8_t accel_lpf_write_array[] = REG_IREG_PREPARE_WRITE_ARRAY( + REG_IPREG_SYS2_OFFSET, + REG_IPREG_SYS2_REG_131, + REG_IPREG_SYS2_REG_131_ACCEL_LPFBW_SEL( + cfg->settings.accel.lpf)); + + err = icm45686_bus_write(dev, REG_IREG_ADDR_15_8, accel_lpf_write_array, + sizeof(accel_lpf_write_array)); + if (err) { + LOG_ERR("Failed to set Accel BW settings: %d", err); + return err; + } + + if (IS_ENABLED(CONFIG_ICM45686_TRIGGER)) { + err = icm45686_trigger_init(dev); + if (err) { + LOG_ERR("Failed to initialize triggers: %d", err); + return err; + } + } + + LOG_DBG("Init OK"); + + return 0; +} + +#define ICM45686_VALID_ACCEL_ODR(pwr_mode, odr) \ + ((pwr_mode == ICM45686_DT_ACCEL_LP && odr >= ICM45686_DT_ACCEL_ODR_400) || \ + (pwr_mode == ICM45686_DT_ACCEL_LN && odr <= ICM45686_DT_ACCEL_ODR_12_5) || \ + (pwr_mode == ICM45686_DT_ACCEL_OFF)) + +#define ICM45686_VALID_GYRO_ODR(pwr_mode, odr) \ + ((pwr_mode == ICM45686_DT_GYRO_LP && odr >= ICM45686_DT_GYRO_ODR_400) || \ + (pwr_mode == ICM45686_DT_GYRO_LN && odr <= ICM45686_DT_GYRO_ODR_12_5) || \ + (pwr_mode == ICM45686_DT_GYRO_OFF)) + +#define ICM45686_INIT(inst) \ + \ + RTIO_DEFINE(icm45686_rtio_ctx_##inst, 8, 8); \ + SPI_DT_IODEV_DEFINE(icm45686_bus_##inst, \ + DT_DRV_INST(inst), \ + SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_TRANSFER_MSB, \ + 0U); \ + \ + static const struct icm45686_config icm45686_cfg_##inst = { \ + .settings = { \ + .accel = { \ + .pwr_mode = DT_INST_PROP(inst, accel_pwr_mode), \ + .fs = DT_INST_PROP(inst, accel_fs), \ + .odr = DT_INST_PROP(inst, accel_odr), \ + .lpf = DT_INST_PROP_OR(inst, accel_lpf, 0), \ + }, \ + .gyro = { \ + .pwr_mode = DT_INST_PROP(inst, gyro_pwr_mode), \ + .fs = DT_INST_PROP(inst, gyro_fs), \ + .odr = DT_INST_PROP(inst, gyro_odr), \ + .lpf = DT_INST_PROP_OR(inst, gyro_lpf, 0), \ + }, \ + }, \ + .int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0}), \ + }; \ + static struct icm45686_data icm45686_data_##inst = { \ + .edata.header = { \ + .is_fifo = false, \ + .accel_fs = DT_INST_PROP(inst, accel_fs), \ + .gyro_fs = DT_INST_PROP(inst, gyro_fs), \ + }, \ + .rtio = { \ + .iodev = &icm45686_bus_##inst, \ + .ctx = &icm45686_rtio_ctx_##inst, \ + }, \ + }; \ + \ + /* Build-time settings verification: Inform the user of invalid settings at build time */ \ + BUILD_ASSERT(ICM45686_VALID_ACCEL_ODR(DT_INST_PROP(inst, accel_pwr_mode), \ + DT_INST_PROP(inst, accel_odr)), \ + "Invalid accel ODR setting. Please check supported ODRs for LP and LN"); \ + BUILD_ASSERT(ICM45686_VALID_GYRO_ODR(DT_INST_PROP(inst, gyro_pwr_mode), \ + DT_INST_PROP(inst, gyro_odr)), \ + "Invalid gyro ODR setting. Please check supported ODRs for LP and LN"); \ + \ + SENSOR_DEVICE_DT_INST_DEFINE(inst, icm45686_init, \ + NULL, \ + &icm45686_data_##inst, \ + &icm45686_cfg_##inst, \ + POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, \ + &icm45686_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(ICM45686_INIT) diff --git a/drivers/sensor/tdk/icm45686/icm45686.h b/drivers/sensor/tdk/icm45686/icm45686.h new file mode 100644 index 0000000000000..238e386656641 --- /dev/null +++ b/drivers/sensor/tdk/icm45686/icm45686.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2022 Intel Corporation + * Copyright (c) 2025 Croxel Inc. + * Copyright (c) 2025 CogniPilot Foundation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ICM45686_H_ +#define ZEPHYR_DRIVERS_SENSOR_ICM45686_H_ + +#include +#include +#include +#include +#include + +struct icm45686_encoded_payload { + union { + uint8_t buf[14]; + int16_t readings[7]; + struct { + struct { + int16_t x; + int16_t y; + int16_t z; + } accel; + struct { + int16_t x; + int16_t y; + int16_t z; + } gyro; + int16_t temp; + } __attribute__((__packed__)); + }; +}; + +struct icm45686_encoded_header { + uint64_t timestamp; + uint8_t accel_fs : 4; + uint8_t gyro_fs : 4; + uint8_t is_fifo : 1; + uint8_t channels : 7; +}; + +struct icm45686_encoded_data { + struct icm45686_encoded_header header; + struct icm45686_encoded_payload payload; +}; + +struct icm45686_triggers { + struct gpio_callback cb; + const struct device *dev; + struct k_mutex lock; + struct { + struct sensor_trigger trigger; + sensor_trigger_handler_t handler; + } entry; +#if defined(CONFIG_ICM45686_TRIGGER_OWN_THREAD) + K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_ICM45686_THREAD_STACK_SIZE); + struct k_thread thread; + struct k_sem sem; +#elif defined(CONFIG_ICM45686_TRIGGER_GLOBAL_THREAD) + struct k_work work; +#endif +}; + +struct icm45686_data { + struct { + struct rtio_iodev *iodev; + struct rtio *ctx; + } rtio; + /** Single-shot encoded data instance to support fetch/get API */ + struct icm45686_encoded_data edata; +#if defined(CONFIG_ICM45686_TRIGGER) + struct icm45686_triggers triggers; +#endif /* CONFIG_ICM45686_TRIGGER */ +}; + +struct icm45686_config { + struct { + struct { + uint8_t pwr_mode : 2; + uint8_t fs : 4; + uint8_t odr : 4; + uint8_t lpf : 3; + } accel; + struct { + uint8_t pwr_mode : 2; + uint8_t fs : 4; + uint8_t odr : 4; + uint8_t lpf : 3; + } gyro; + } settings; + struct gpio_dt_spec int_gpio; +}; + +static inline void icm45686_accel_ms(struct icm45686_encoded_data *edata, + int32_t in, + int32_t *out_ms, + int32_t *out_ums) +{ + int64_t sensitivity; + + switch (edata->header.accel_fs) { + case ICM45686_DT_ACCEL_FS_32: + sensitivity = 32768 / 32; + break; + case ICM45686_DT_ACCEL_FS_16: + sensitivity = 32768 / 16; + break; + case ICM45686_DT_ACCEL_FS_8: + sensitivity = 32768 / 8; + break; + case ICM45686_DT_ACCEL_FS_4: + sensitivity = 32768 / 4; + break; + case ICM45686_DT_ACCEL_FS_2: + sensitivity = 32768 / 2; + break; + default: + CODE_UNREACHABLE; + } + + /* Convert to micrometers/s^2 */ + int64_t in_ms = in * SENSOR_G; + + /* meters/s^2 whole values */ + *out_ms = in_ms / (sensitivity * 1000000LL); + + /* micrometers/s^2 */ + *out_ums = (in_ms - (*out_ms * sensitivity * 1000000LL)) / sensitivity; +} + +static inline void icm45686_gyro_rads(struct icm45686_encoded_data *edata, + int32_t in, + int32_t *out_rads, + int32_t *out_urads) +{ + int64_t sensitivity_x10; + + switch (edata->header.gyro_fs) { + case ICM45686_DT_GYRO_FS_4000: + sensitivity_x10 = 32768 * 10 / 4000; + break; + case ICM45686_DT_GYRO_FS_2000: + sensitivity_x10 = 32768 * 10 / 2000; + break; + case ICM45686_DT_GYRO_FS_1000: + sensitivity_x10 = 32768 * 10 / 1000; + break; + case ICM45686_DT_GYRO_FS_500: + sensitivity_x10 = 32768 * 10 / 500; + break; + case ICM45686_DT_GYRO_FS_250: + sensitivity_x10 = 32768 * 10 / 250; + break; + case ICM45686_DT_GYRO_FS_125: + sensitivity_x10 = 32768 * 10 / 125; + break; + case ICM45686_DT_GYRO_FS_62_5: + sensitivity_x10 = 32768 * 10 * 10 / 625; + break; + case ICM45686_DT_GYRO_FS_31_25: + sensitivity_x10 = 32768 * 10 * 100 / 3125; + break; + case ICM45686_DT_GYRO_FS_15_625: + sensitivity_x10 = 32768 * 10 * 1000 / 15625; + break; + default: + CODE_UNREACHABLE; + } + + int64_t in10_rads = (int64_t)in * SENSOR_PI * 10LL; + + /* Whole rad/s */ + *out_rads = in10_rads / (sensitivity_x10 * 180LL * 1000000LL); + + /* microrad/s */ + *out_urads = (in10_rads - (*out_rads * sensitivity_x10 * 180LL * 1000000LL)) / + (sensitivity_x10 * 180LL); +} + +static inline void icm45686_temp_c(int32_t in, int32_t *out_c, uint32_t *out_uc) +{ + int64_t sensitivity = 13248; /* value equivalent for x100 1c */ + + /* Offset by 25 degrees Celsius */ + int64_t in100 = (in * 100) + (25 * sensitivity); + + /* Whole celsius */ + *out_c = in100 / sensitivity; + + /* Micro celsius */ + *out_uc = ((in100 - (*out_c) * sensitivity) * INT64_C(1000000)) / sensitivity; +} + +#endif /* ZEPHYR_DRIVERS_SENSOR_ICM45686_H_ */ diff --git a/drivers/sensor/tdk/icm45686/icm45686_bus.h b/drivers/sensor/tdk/icm45686/icm45686_bus.h new file mode 100644 index 0000000000000..a43fb82a137c3 --- /dev/null +++ b/drivers/sensor/tdk/icm45686/icm45686_bus.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2025 Croxel Inc. + * Copyright (c) 2025 CogniPilot Foundation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ICM45686_BUS_H_ +#define ZEPHYR_DRIVERS_SENSOR_ICM45686_BUS_H_ + +#include +#include + +#include "icm45686.h" +#include "icm45686_reg.h" + +static inline int icm45686_bus_read(const struct device *dev, + uint8_t reg, + uint8_t *buf, + uint16_t len) +{ + struct icm45686_data *data = dev->data; + struct rtio *ctx = data->rtio.ctx; + struct rtio_iodev *iodev = data->rtio.iodev; + struct rtio_sqe *write_sqe = rtio_sqe_acquire(ctx); + struct rtio_sqe *read_sqe = rtio_sqe_acquire(ctx); + struct rtio_cqe *cqe; + int err; + + if (!write_sqe || !read_sqe) { + return -ENOMEM; + } + + reg = reg | REG_SPI_READ_BIT; + + rtio_sqe_prep_write(write_sqe, iodev, RTIO_PRIO_HIGH, ®, 1, NULL); + write_sqe->flags |= RTIO_SQE_TRANSACTION; + rtio_sqe_prep_read(read_sqe, iodev, RTIO_PRIO_HIGH, buf, len, NULL); + + err = rtio_submit(ctx, 2); + if (err) { + return err; + } + + do { + cqe = rtio_cqe_consume(ctx); + if (cqe != NULL) { + err = cqe->result; + rtio_cqe_release(ctx, cqe); + } + } while (cqe != NULL); + + return err; +} + +static inline int icm45686_bus_write(const struct device *dev, + uint8_t reg, + const uint8_t *buf, + uint16_t len) +{ + struct icm45686_data *data = dev->data; + struct rtio *ctx = data->rtio.ctx; + struct rtio_iodev *iodev = data->rtio.iodev; + struct rtio_sqe *write_reg_sqe = rtio_sqe_acquire(ctx); + struct rtio_sqe *write_buf_sqe = rtio_sqe_acquire(ctx); + struct rtio_cqe *cqe; + int err; + + if (!write_reg_sqe || !write_buf_sqe) { + return -ENOMEM; + } + + rtio_sqe_prep_write(write_reg_sqe, iodev, RTIO_PRIO_HIGH, ®, 1, NULL); + write_reg_sqe->flags |= RTIO_SQE_TRANSACTION; + rtio_sqe_prep_write(write_buf_sqe, iodev, RTIO_PRIO_HIGH, buf, len, NULL); + + err = rtio_submit(ctx, 2); + if (err) { + return err; + } + + do { + cqe = rtio_cqe_consume(ctx); + if (cqe != NULL) { + err = cqe->result; + rtio_cqe_release(ctx, cqe); + } + } while (cqe != NULL); + + return err; +} + +#endif /* ZEPHYR_DRIVERS_SENSOR_ICM45686_BUS_H_ */ diff --git a/drivers/sensor/tdk/icm45686/icm45686_decoder.c b/drivers/sensor/tdk/icm45686/icm45686_decoder.c new file mode 100644 index 0000000000000..e1e73568b6bb8 --- /dev/null +++ b/drivers/sensor/tdk/icm45686/icm45686_decoder.c @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2023 Google LLC + * Copyright (c) 2025 Croxel Inc. + * Copyright (c) 2025 CogniPilot Foundation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "icm45686.h" +#include "icm45686_decoder.h" + +#include +LOG_MODULE_REGISTER(ICM45686_DECODER, CONFIG_SENSOR_LOG_LEVEL); + +#define DT_DRV_COMPAT invensense_icm45686 + +static int icm45686_get_shift(enum sensor_channel channel, + int accel_fs, + int gyro_fs, + int8_t *shift) +{ + switch (channel) { + case SENSOR_CHAN_ACCEL_XYZ: + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + switch (accel_fs) { + case ICM45686_DT_ACCEL_FS_32: + *shift = 9; + return 0; + case ICM45686_DT_ACCEL_FS_16: + *shift = 8; + return 0; + case ICM45686_DT_ACCEL_FS_8: + *shift = 7; + return 0; + case ICM45686_DT_ACCEL_FS_4: + *shift = 6; + return 0; + case ICM45686_DT_ACCEL_FS_2: + *shift = 5; + return 0; + default: + return -EINVAL; + } + case SENSOR_CHAN_GYRO_XYZ: + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + switch (gyro_fs) { + case ICM45686_DT_GYRO_FS_4000: + *shift = 12; + return 0; + case ICM45686_DT_GYRO_FS_2000: + *shift = 11; + return 0; + case ICM45686_DT_GYRO_FS_1000: + *shift = 10; + return 0; + case ICM45686_DT_GYRO_FS_500: + *shift = 9; + return 0; + case ICM45686_DT_GYRO_FS_250: + *shift = 8; + return 0; + case ICM45686_DT_GYRO_FS_125: + *shift = 7; + return 0; + case ICM45686_DT_GYRO_FS_62_5: + *shift = 6; + return 0; + case ICM45686_DT_GYRO_FS_31_25: + *shift = 5; + return 0; + default: + return -EINVAL; + } + case SENSOR_CHAN_DIE_TEMP: + *shift = 9; + return 0; + default: + return -EINVAL; + } +} + +int icm45686_convert_raw_to_q31(struct icm45686_encoded_data *edata, + enum sensor_channel chan, + int32_t reading, + q31_t *out) +{ + int32_t whole; + int32_t fraction; + int64_t intermediate; + int8_t shift; + int rc; + + rc = icm45686_get_shift(chan, edata->header.accel_fs, edata->header.gyro_fs, &shift); + if (rc != 0) { + return rc; + } + + switch (chan) { + case SENSOR_CHAN_ACCEL_XYZ: + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + icm45686_accel_ms(edata, reading, &whole, &fraction); + break; + case SENSOR_CHAN_GYRO_XYZ: + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + icm45686_gyro_rads(edata, reading, &whole, &fraction); + break; + case SENSOR_CHAN_DIE_TEMP: + icm45686_temp_c(reading, &whole, &fraction); + break; + default: + return -ENOTSUP; + } + intermediate = ((int64_t)whole * INT64_C(1000000) + fraction); + if (shift < 0) { + intermediate = + intermediate * ((int64_t)INT32_MAX + 1) * (1 << -shift) / INT64_C(1000000); + } else if (shift > 0) { + intermediate = + intermediate * ((int64_t)INT32_MAX + 1) / ((1 << shift) * INT64_C(1000000)); + } + *out = CLAMP(intermediate, INT32_MIN, INT32_MAX); + + return 0; +} + +static int icm45686_get_channel_position(enum sensor_channel chan) +{ + switch (chan) { + case SENSOR_CHAN_ACCEL_XYZ: + case SENSOR_CHAN_ACCEL_X: + return offsetof(struct icm45686_encoded_payload, accel.x) / sizeof(int16_t); + case SENSOR_CHAN_ACCEL_Y: + return offsetof(struct icm45686_encoded_payload, accel.y) / sizeof(int16_t); + case SENSOR_CHAN_ACCEL_Z: + return offsetof(struct icm45686_encoded_payload, accel.z) / sizeof(int16_t); + case SENSOR_CHAN_GYRO_XYZ: + case SENSOR_CHAN_GYRO_X: + return offsetof(struct icm45686_encoded_payload, gyro.x) / sizeof(int16_t); + case SENSOR_CHAN_GYRO_Y: + return offsetof(struct icm45686_encoded_payload, gyro.y) / sizeof(int16_t); + case SENSOR_CHAN_GYRO_Z: + return offsetof(struct icm45686_encoded_payload, gyro.z) / sizeof(int16_t); + case SENSOR_CHAN_DIE_TEMP: + return offsetof(struct icm45686_encoded_payload, temp) / sizeof(int16_t); + default: + return 0; + } +} + +static uint8_t icm45686_encode_channel(enum sensor_channel chan) +{ + uint8_t encode_bmask = 0; + + switch (chan) { + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + case SENSOR_CHAN_DIE_TEMP: + encode_bmask = BIT(icm45686_get_channel_position(chan)); + break; + case SENSOR_CHAN_ACCEL_XYZ: + encode_bmask = BIT(icm45686_get_channel_position(SENSOR_CHAN_ACCEL_X)) | + BIT(icm45686_get_channel_position(SENSOR_CHAN_ACCEL_Y)) | + BIT(icm45686_get_channel_position(SENSOR_CHAN_ACCEL_Z)); + break; + case SENSOR_CHAN_GYRO_XYZ: + encode_bmask = BIT(icm45686_get_channel_position(SENSOR_CHAN_GYRO_X)) | + BIT(icm45686_get_channel_position(SENSOR_CHAN_GYRO_Y)) | + BIT(icm45686_get_channel_position(SENSOR_CHAN_GYRO_Z)); + break; + default: + break; + } + + return encode_bmask; +} + +int icm45686_encode(const struct device *dev, + const struct sensor_chan_spec *const channels, + const size_t num_channels, + uint8_t *buf) +{ + struct icm45686_encoded_data *edata = (struct icm45686_encoded_data *)buf; + const struct icm45686_config *dev_config = dev->config; + uint64_t cycles; + int err; + + edata->header.channels = 0; + + for (size_t i = 0 ; i < num_channels ; i++) { + edata->header.channels |= icm45686_encode_channel(channels[i].chan_type); + } + + err = sensor_clock_get_cycles(&cycles); + if (err != 0) { + return err; + } + + edata->header.is_fifo = false; + edata->header.accel_fs = dev_config->settings.accel.fs; + edata->header.gyro_fs = dev_config->settings.gyro.fs; + edata->header.timestamp = sensor_clock_cycles_to_ns(cycles); + + return 0; +} + +static int icm45686_decoder_get_frame_count(const uint8_t *buffer, + struct sensor_chan_spec chan_spec, + uint16_t *frame_count) +{ + struct icm45686_encoded_data *edata = (struct icm45686_encoded_data *)buffer; + + if (chan_spec.chan_idx != 0) { + return -ENOTSUP; + } + + uint8_t channel_request = icm45686_encode_channel(chan_spec.chan_type); + + if ((!edata->header.is_fifo) && + (edata->header.channels & channel_request) != channel_request) { + return -ENODATA; + } + + if (!edata->header.is_fifo) { + switch (chan_spec.chan_type) { + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_ACCEL_XYZ: + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + case SENSOR_CHAN_GYRO_XYZ: + case SENSOR_CHAN_DIE_TEMP: + *frame_count = 1; + return 0; + default: + return -ENOTSUP; + } + } + + LOG_ERR("FIFO Data frame not supported"); + return -1; +} + +static int icm45686_decoder_get_size_info(struct sensor_chan_spec chan_spec, + size_t *base_size, + size_t *frame_size) +{ + switch (chan_spec.chan_type) { + case SENSOR_CHAN_ACCEL_XYZ: + case SENSOR_CHAN_GYRO_XYZ: + *base_size = sizeof(struct sensor_three_axis_data); + *frame_size = sizeof(struct sensor_three_axis_sample_data); + return 0; + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + case SENSOR_CHAN_DIE_TEMP: + *base_size = sizeof(struct sensor_q31_data); + *frame_size = sizeof(struct sensor_q31_sample_data); + return 0; + default: + return -ENOTSUP; + } +} + +static int icm45686_one_shot_decode(const uint8_t *buffer, + struct sensor_chan_spec chan_spec, + uint32_t *fit, + uint16_t max_count, + void *data_out) +{ + struct icm45686_encoded_data *edata = (struct icm45686_encoded_data *)buffer; + uint8_t channel_request; + int err; + + if (*fit != 0) { + return 0; + } + + if (max_count == 0 || chan_spec.chan_idx != 0) { + return -EINVAL; + } + + switch (chan_spec.chan_type) { + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + case SENSOR_CHAN_DIE_TEMP: { + channel_request = icm45686_encode_channel(chan_spec.chan_type); + if ((channel_request & edata->header.channels) != channel_request) { + return -ENODATA; + } + + struct sensor_q31_data *out = data_out; + + out->header.base_timestamp_ns = edata->header.timestamp; + out->header.reading_count = 1; + + err = icm45686_get_shift(chan_spec.chan_type, + edata->header.accel_fs, + edata->header.gyro_fs, + &out->shift); + if (err != 0) { + return -EINVAL; + } + + icm45686_convert_raw_to_q31( + edata, + chan_spec.chan_type, + edata->payload.readings[icm45686_get_channel_position(chan_spec.chan_type)], + &out->readings[0].value); + *fit = 1; + return 1; + } + case SENSOR_CHAN_ACCEL_XYZ: + case SENSOR_CHAN_GYRO_XYZ: { + channel_request = icm45686_encode_channel(chan_spec.chan_type); + if ((channel_request & edata->header.channels) != channel_request) { + return -ENODATA; + } + + struct sensor_three_axis_data *out = data_out; + struct icm45686_encoded_payload *payload = &edata->payload; + + out->header.base_timestamp_ns = edata->header.timestamp; + out->header.reading_count = 1; + + icm45686_convert_raw_to_q31( + edata, + chan_spec.chan_type - 3, + payload->readings[icm45686_get_channel_position(chan_spec.chan_type - 3)], + &out->readings[0].x); + icm45686_convert_raw_to_q31( + edata, + chan_spec.chan_type - 2, + payload->readings[icm45686_get_channel_position(chan_spec.chan_type - 2)], + &out->readings[0].y); + icm45686_convert_raw_to_q31( + edata, + chan_spec.chan_type - 1, + payload->readings[icm45686_get_channel_position(chan_spec.chan_type - 1)], + &out->readings[0].z); + *fit = 1; + return 1; + } + default: + return -EINVAL; + } +} + +static int icm45686_decoder_decode(const uint8_t *buffer, + struct sensor_chan_spec chan_spec, + uint32_t *fit, + uint16_t max_count, + void *data_out) +{ + struct icm45686_encoded_data *edata = (struct icm45686_encoded_data *)buffer; + + if (edata->header.is_fifo) { + return -ENOTSUP; + } else { + return icm45686_one_shot_decode(buffer, chan_spec, fit, max_count, data_out); + } +} + +static bool icm45686_decoder_has_trigger(const uint8_t *buffer, enum sensor_trigger_type trigger) +{ + return false; +} + +SENSOR_DECODER_API_DT_DEFINE() = { + .get_frame_count = icm45686_decoder_get_frame_count, + .get_size_info = icm45686_decoder_get_size_info, + .decode = icm45686_decoder_decode, + .has_trigger = icm45686_decoder_has_trigger, +}; + +int icm45686_get_decoder(const struct device *dev, + const struct sensor_decoder_api **decoder) +{ + ARG_UNUSED(dev); + *decoder = &SENSOR_DECODER_NAME(); + + return 0; +} diff --git a/drivers/sensor/tdk/icm45686/icm45686_decoder.h b/drivers/sensor/tdk/icm45686/icm45686_decoder.h new file mode 100644 index 0000000000000..bb669c842c8f6 --- /dev/null +++ b/drivers/sensor/tdk/icm45686/icm45686_decoder.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Google LLC + * Copyright (c) 2025 Croxel Inc. + * Copyright (c) 2025 CogniPilot Foundation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ICM45686_DECODER_H_ +#define ZEPHYR_DRIVERS_SENSOR_ICM45686_DECODER_H_ + +#include +#include +#include "icm45686.h" + +int icm45686_encode(const struct device *dev, + const struct sensor_chan_spec *const channels, + const size_t num_channels, + uint8_t *buf); + +int icm45686_get_decoder(const struct device *dev, + const struct sensor_decoder_api **decoder); + +#endif /* ZEPHYR_DRIVERS_SENSOR_ICM45686_DECODER_H_ */ diff --git a/drivers/sensor/tdk/icm45686/icm45686_reg.h b/drivers/sensor/tdk/icm45686/icm45686_reg.h new file mode 100644 index 0000000000000..2063b21476ac7 --- /dev/null +++ b/drivers/sensor/tdk/icm45686/icm45686_reg.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2022 Intel Corporation + * Copyright (c) 2025 Croxel Inc. + * Copyright (c) 2025 CogniPilot Foundation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ICM45686_REG_H_ +#define ZEPHYR_DRIVERS_SENSOR_ICM45686_REG_H_ + +#include +#include + +/* Address value has a read bit */ +#define REG_SPI_READ_BIT BIT(7) + +/* Registers */ +/* Register Bank 0 */ +#define REG_ACCEL_DATA_X1_UI 0x00 +#define REG_ACCEL_DATA_X0_UI 0x01 +#define REG_ACCEL_DATA_Y1_UI 0x02 +#define REG_ACCEL_DATA_Y0_UI 0x03 +#define REG_ACCEL_DATA_Z1_UI 0x04 +#define REG_ACCEL_DATA_Z0_UI 0x05 +#define REG_GYRO_DATA_X1_UI 0x06 +#define REG_GYRO_DATA_X0_UI 0x07 +#define REG_GYRO_DATA_Y1_UI 0x08 +#define REG_GYRO_DATA_Y0_UI 0x09 +#define REG_GYRO_DATA_Z1_UI 0x0A +#define REG_GYRO_DATA_Z0_UI 0x0B +#define REG_TEMP_DATA1_UI 0x0C +#define REG_TEMP_DATA0_UI 0x0D +#define REG_PWR_MGMT0 0x10 +#define REG_INT1_CONFIG0 0x16 +#define REG_INT1_CONFIG1 0x17 +#define REG_INT1_CONFIG2 0x18 +#define REG_INT1_STATUS0 0x19 +#define REG_INT1_STATUS1 0x1A +#define REG_ACCEL_CONFIG0 0x1B +#define REG_GYRO_CONFIG0 0x1C +#define REG_DRIVE_CONFIG0 0x32 +#define REG_WHO_AM_I 0x72 +#define REG_IREG_ADDR_15_8 0x7C +#define REG_IREG_ADDR_7_0 0x7D +#define REG_IREG_DATA 0x7E +#define REG_MISC2 0x7F + +/* User Bank IPREG_SYS1 - Gyro-related config settings */ +#define REG_IPREG_SYS1_OFFSET 0xA400 +#define REG_IPREG_SYS1_REG_172 0xAC + +/* User Bank IPREG_SYS2 - Accel-related config settings */ +#define REG_IPREG_SYS2_OFFSET 0xA500 +#define REG_IPREG_SYS2_REG_131 0x83 + +/* Helper Macros for register manipulation */ +#define REG_PWR_MGMT0_ACCEL_MODE(val) ((val) & BIT_MASK(2)) +#define REG_PWR_MGMT0_GYRO_MODE(val) (((val) & BIT_MASK(2)) << 2) + +#define REG_ACCEL_CONFIG0_ODR(val) ((val) & BIT_MASK(4)) +#define REG_ACCEL_CONFIG0_FS(val) (((val) & BIT_MASK(3)) << 4) + +#define REG_GYRO_CONFIG0_ODR(val) ((val) & BIT_MASK(4)) +#define REG_GYRO_CONFIG0_FS(val) (((val) & BIT_MASK(4)) << 4) + +#define REG_DRIVE_CONFIG0_SPI_SLEW(val) (((val) & BIT_MASK(2)) << 1) + +#define REG_MISC2_SOFT_RST(val) ((val << 1) & BIT(1)) + +#define REG_IPREG_SYS1_REG_172_GYRO_LPFBW_SEL(val) (val & BIT_MASK(3)) + +#define REG_IPREG_SYS2_REG_131_ACCEL_LPFBW_SEL(val) (val & BIT_MASK(3)) + +#define REG_INT1_CONFIG0_STATUS_EN_DRDY(val) (((val) & BIT_MASK(1)) << 2) +#define REG_INT1_CONFIG0_STATUS_EN_FIFO_THS(val) (((val) & BIT_MASK(1)) << 1) +#define REG_INT1_CONFIG0_STATUS_EN_FIFO_FULL(val) ((val) & BIT_MASK(1)) + +#define REG_INT1_CONFIG2_EN_OPEN_DRAIN(val) (((val) & BIT_MASK(1)) << 2) +#define REG_INT1_CONFIG2_EN_LATCH_MODE(val) (((val) & BIT_MASK(1)) << 1) +#define REG_INT1_CONFIG2_EN_ACTIVE_HIGH(val) ((val) & BIT_MASK(1)) + +#define REG_INT1_STATUS0_DRDY(val) (((val) & BIT_MASK(1)) << 2) +#define REG_INT1_STATUS0_FIFO_THS(val) (((val) & BIT_MASK(1)) << 1) +#define REG_INT1_STATUS0_FIFO_FULL(val) ((val) & BIT_MASK(1)) + +/* Misc. Defines */ +#define WHO_AM_I_ICM45686 0xE9 + +#define REG_IREG_PREPARE_WRITE_ARRAY(base, reg, val) {((base) >> 8) & 0xFF, reg, val} + +#endif /* ZEPHYR_DRIVERS_SENSOR_ICM45686_REG_H_ */ diff --git a/drivers/sensor/tdk/icm45686/icm45686_trigger.c b/drivers/sensor/tdk/icm45686/icm45686_trigger.c new file mode 100644 index 0000000000000..62411593446ee --- /dev/null +++ b/drivers/sensor/tdk/icm45686/icm45686_trigger.c @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2022 Intel Corporation + * Copyright (c) 2023 Google LLC + * Copyright (c) 2025 Croxel Inc. + * Copyright (c) 2025 CogniPilot Foundation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "icm45686.h" +#include "icm45686_bus.h" +#include "icm45686_trigger.h" + +#include +LOG_MODULE_REGISTER(ICM45686_TRIGGER, CONFIG_SENSOR_LOG_LEVEL); + +static void icm45686_gpio_callback(const struct device *dev, + struct gpio_callback *cb, + uint32_t pins) +{ + struct icm45686_data *data = CONTAINER_OF(cb, + struct icm45686_data, + triggers.cb); + + ARG_UNUSED(dev); + ARG_UNUSED(pins); + +#if defined(CONFIG_ICM45686_TRIGGER_OWN_THREAD) + k_sem_give(&data->triggers.sem); +#elif defined(CONFIG_ICM45686_TRIGGER_GLOBAL_THREAD) + k_work_submit(&data->triggers.work); +#endif +} + +static void icm45686_thread_cb(const struct device *dev) +{ + struct icm45686_data *data = dev->data; + + (void)k_mutex_lock(&data->triggers.lock, K_FOREVER); + + if (data->triggers.entry.handler) { + data->triggers.entry.handler(dev, &data->triggers.entry.trigger); + } + + (void)k_mutex_unlock(&data->triggers.lock); +} + +#if defined(CONFIG_ICM45686_TRIGGER_OWN_THREAD) + +static void icm45686_thread(void *p1, void *p2, void *p3) +{ + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + struct icm45686_data *data = p1; + + while (true) { + k_sem_take(&data->triggers.sem, K_FOREVER); + + icm45686_thread_cb(data->triggers.dev); + } +} + +#elif defined(CONFIG_ICM45686_TRIGGER_GLOBAL_THREAD) + +static void icm45686_work_handler(struct k_work *work) +{ + struct icm45686_data *data = CONTAINER_OF(work, + struct icm45686_data, + triggers.work); + + icm45686_thread_cb(data->triggers.dev); +} + +#endif + +static int icm45686_enable_drdy(const struct device *dev, bool enable) +{ + uint8_t val; + int err; + + err = icm45686_bus_read(dev, REG_INT1_CONFIG0, &val, 1); + if (err) { + return err; + } + + val &= ~REG_INT1_CONFIG0_STATUS_EN_DRDY(true); + err = icm45686_bus_write(dev, REG_INT1_CONFIG0, &val, 1); + if (err) { + return err; + } + + if (enable) { + val |= REG_INT1_CONFIG0_STATUS_EN_DRDY(true); + } + + return icm45686_bus_write(dev, REG_INT1_CONFIG0, &val, 1); +} + +int icm45686_trigger_set(const struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + int err = 0; + struct icm45686_data *data = dev->data; + + (void)k_mutex_lock(&data->triggers.lock, K_FOREVER); + + switch (trig->type) { + case SENSOR_TRIG_DATA_READY: + data->triggers.entry.trigger = *trig; + data->triggers.entry.handler = handler; + + if (handler) { + /* Enable data ready interrupt */ + err = icm45686_enable_drdy(dev, true); + } else { + /* Disable data ready interrupt */ + err = icm45686_enable_drdy(dev, false); + } + break; + default: + err = -ENOTSUP; + break; + } + + (void)k_mutex_unlock(&data->triggers.lock); + + return err; +} + +int icm45686_trigger_init(const struct device *dev) +{ + const struct icm45686_config *cfg = dev->config; + struct icm45686_data *data = dev->data; + uint8_t val = 0; + int err; + + err = k_mutex_init(&data->triggers.lock); + __ASSERT_NO_MSG(!err); + + /** Needed to get back the device handle from the callback context */ + data->triggers.dev = dev; + +#if defined(CONFIG_ICM45686_TRIGGER_OWN_THREAD) + + err = k_sem_init(&data->triggers.sem, 0, 1); + __ASSERT_NO_MSG(!err); + + (void)k_thread_create(&data->triggers.thread, + data->triggers.thread_stack, + K_KERNEL_STACK_SIZEOF(data->triggers.thread_stack), + icm45686_thread, + data, + NULL, + NULL, + K_PRIO_COOP(CONFIG_ICM45686_THREAD_PRIORITY), + 0, + K_NO_WAIT); + +#elif defined(CONFIG_ICM45686_TRIGGER_GLOBAL_THREAD) + k_work_init(&data->triggers.work, icm45686_work_handler); +#endif + + if (!cfg->int_gpio.port) { + LOG_ERR("Interrupt GPIO not supplied"); + return -ENODEV; + } + + if (!gpio_is_ready_dt(&cfg->int_gpio)) { + LOG_ERR("Interrupt GPIO not ready"); + return -ENODEV; + } + + err = gpio_pin_configure_dt(&cfg->int_gpio, GPIO_INPUT); + if (err) { + LOG_ERR("Failed to configure interrupt GPIO"); + return -EIO; + } + + gpio_init_callback(&data->triggers.cb, + icm45686_gpio_callback, + BIT(cfg->int_gpio.pin)); + + err = gpio_add_callback(cfg->int_gpio.port, &data->triggers.cb); + if (err) { + LOG_ERR("Failed to add interrupt callback"); + return -EIO; + } + + err = gpio_pin_interrupt_configure_dt(&cfg->int_gpio, + GPIO_INT_EDGE_TO_ACTIVE); + if (err) { + LOG_ERR("Failed to configure interrupt"); + } + + err = icm45686_bus_write(dev, REG_INT1_CONFIG0, &val, 1); + if (err) { + LOG_ERR("Failed to disable all INTs"); + } + + val = REG_INT1_CONFIG2_EN_OPEN_DRAIN(false) | + REG_INT1_CONFIG2_EN_ACTIVE_HIGH(true); + + err = icm45686_bus_write(dev, REG_INT1_CONFIG2, &val, 1); + if (err) { + LOG_ERR("Failed to configure INT as push-pull: %d", err); + } + + return err; +} diff --git a/drivers/sensor/tdk/icm45686/icm45686_trigger.h b/drivers/sensor/tdk/icm45686/icm45686_trigger.h new file mode 100644 index 0000000000000..0c15ff41ba2a0 --- /dev/null +++ b/drivers/sensor/tdk/icm45686/icm45686_trigger.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 Intel Corporation + * Copyright (c) 2023 Google LLC + * Copyright (c) 2025 Croxel Inc. + * Copyright (c) 2025 CogniPilot Foundation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ICM45686_TRIGGER_H_ +#define ZEPHYR_DRIVERS_SENSOR_ICM45686_TRIGGER_H_ + +#include +#include + +/* Configure triggers for the icm45686 sensor */ +int icm45686_trigger_set(const struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler); + +/* Initialization for icm45686 Triggers module */ +int icm45686_trigger_init(const struct device *dev); + +#endif /* ZEPHYR_DRIVERS_SENSOR_ICM45686_TRIGGER_H_ */ diff --git a/dts/bindings/sensor/invensense,icm45686.yaml b/dts/bindings/sensor/invensense,icm45686.yaml new file mode 100644 index 0000000000000..bb428965a1a72 --- /dev/null +++ b/dts/bindings/sensor/invensense,icm45686.yaml @@ -0,0 +1,162 @@ +# Copyright (c) 2024 Intel Corporation +# Copyright (c) 2025 Croxel Inc. +# Copyright (c) 2025 CogniPilot Foundation +# SPDX-License-Identifier: Apache-2.0 + +description: | + ICM45686 High-precision 6-axis motion tracking device + When setting the accel-pm, accel-range, accel-odr, gyro-pm, gyro-range, + gyro-odr properties in a .dts or .dtsi file you may include icm45686.h + and use the macros defined there. + + Example: + #include + + icm42688: icm45686@0 { + ... + + accel-pwr-mode = ; + accel-fs = ; + accel-odr = ; + gyro-pwr-mode= ; + gyro-fs = ; + gyro-odr = ; + }; + +compatible: "invensense,icm45686" +include: [sensor-device.yaml, spi-device.yaml] + +properties: + int-gpios: + type: phandle-array + description: | + The INT signal default configuration is active-high. The + property value should ensure the flags properly describe the + signal that is presented to the driver. + + accel-pwr-mode: + type: int + default: 0 + description: | + Specify the default accelerometer power mode. + Default is power-up configuration. + enum: + - 0 # ICM45686_DT_ACCEL_OFF + - 2 # ICM45686_DT_ACCEL_LP + - 3 # ICM45686_DT_ACCEL_LN + + accel-odr: + type: int + default: 6 + description: | + Specify the default accelerometer output data rate expressed in samples per second (Hz). + Default is power-up configuration. + enum: + - 3 # ICM45686_DT_ACCEL_ODR_6400 (LN-mode only) + - 4 # ICM45686_DT_ACCEL_ODR_3200 (LN-mode only) + - 5 # ICM45686_DT_ACCEL_ODR_1600 (LN-mode only) + - 6 # ICM45686_DT_ACCEL_ODR_800 (LN-mode only) + - 7 # ICM45686_DT_ACCEL_ODR_400 + - 8 # ICM45686_DT_ACCEL_ODR_200 + - 9 # ICM45686_DT_ACCEL_ODR_100 + - 10 # ICM45686_DT_ACCEL_ODR_50 + - 11 # ICM45686_DT_ACCEL_ODR_25 + - 12 # ICM45686_DT_ACCEL_ODR_12_5 + - 13 # ICM45686_DT_ACCEL_ODR_6_25 (LP-mode only) + - 14 # ICM45686_DT_ACCEL_ODR_3_125 (LP-mode only) + - 15 # ICM45686_DT_ACCEL_ODR_1_5625 (LP-mode only) + + accel-fs: + type: int + default: 0 + description: | + Specify the accelerometer range in g. + Default is power-up configuration. + enum: + - 0 # ICM45686_DT_ACCEL_FS_32 + - 1 # ICM45686_DT_ACCEL_FS_16 + - 2 # ICM45686_DT_ACCEL_FS_8 + - 3 # ICM45686_DT_ACCEL_FS_4 + - 4 # ICM45686_DT_ACCEL_FS_2 + + accel-lpf: + type: int + default: 0 + description: | + Specify the accelerometer low pass filter cut-off frequency + as a fraction of the output data rate. + Default is power-up configuration. + enum: + - 0 # ICM45686_DT_ACCEL_LPF_BW_OFF + - 1 # ICM45686_DT_ACCEL_LPF_BW_1_4 + - 2 # ICM45686_DT_ACCEL_LPF_BW_1_8 + - 3 # ICM45686_DT_ACCEL_LPF_BW_1_16 + - 4 # ICM45686_DT_ACCEL_LPF_BW_1_32 + - 5 # ICM45686_DT_ACCEL_LPF_BW_1_64 + - 6 # ICM45686_DT_ACCEL_LPF_BW_1_128 + + gyro-pwr-mode: + type: int + default: 0 + description: | + Specify the default gyro power mode. + Default is power-up configuration. + enum: + - 0 # ICM42688_DT_GYRO_OFF + - 1 # ICM42688_DT_GYRO_STANDBY + - 2 # ICM42688_DT_GYRO_LP + - 3 # ICM42688_DT_GYRO_LN + + gyro-odr: + type: int + default: 6 + description: | + Specify the default gyro output data rate expressed in samples per second (Hz). + Default is power-up configuration. + enum: + - 3 # ICM45686_DT_GYRO_ODR_6400 (LN-mode only) + - 4 # ICM45686_DT_GYRO_ODR_3200 (LN-mode only) + - 5 # ICM45686_DT_GYRO_ODR_1600 (LN-mode only) + - 6 # ICM45686_DT_GYRO_ODR_800 (LN-mode only) + - 7 # ICM45686_DT_GYRO_ODR_400 + - 8 # ICM45686_DT_GYRO_ODR_200 + - 9 # ICM45686_DT_GYRO_ODR_100 + - 10 # ICM45686_DT_GYRO_ODR_50 + - 11 # ICM45686_DT_GYRO_ODR_25 + - 12 # ICM45686_DT_GYRO_ODR_12_5 + - 13 # ICM45686_DT_GYRO_ODR_6_25 (LP-mode only) + - 14 # ICM45686_DT_GYRO_ODR_3_125 (LP-mode only) + - 15 # ICM45686_DT_GYRO_ODR_1_5625 (LP-mode only) + + gyro-fs: + type: int + default: 0 + description: | + Specify the gyro range in degrees per second. + Default is power-up configuration. + enum: + - 0 # ICM45686_DT_GYRO_FS_4000 + - 1 # ICM45686_DT_GYRO_FS_2000 + - 2 # ICM45686_DT_GYRO_FS_1000 + - 3 # ICM45686_DT_GYRO_FS_500 + - 4 # ICM45686_DT_GYRO_FS_250 + - 5 # ICM45686_DT_GYRO_FS_125 + - 6 # ICM45686_DT_GYRO_FS_62_5 + - 7 # ICM45686_DT_GYRO_FS_31_25 + - 8 # ICM45686_DT_GYRO_FS_15_625 + + gyro-lpf: + type: int + default: 0 + description: | + Specify the gyro low pass filter cut-off frequency + as a fraction of the output data rate. + Default is power-up configuration. + enum: + - 0 # ICM45686_DT_GYRO_LPF_BW_OFF + - 1 # ICM45686_DT_GYRO_LPF_BW_1_4 + - 2 # ICM45686_DT_GYRO_LPF_BW_1_8 + - 3 # ICM45686_DT_GYRO_LPF_BW_1_16 + - 4 # ICM45686_DT_GYRO_LPF_BW_1_32 + - 5 # ICM45686_DT_GYRO_LPF_BW_1_64 + - 6 # ICM45686_DT_GYRO_LPF_BW_1_128 diff --git a/include/zephyr/dt-bindings/sensor/icm45686.h b/include/zephyr/dt-bindings/sensor/icm45686.h new file mode 100644 index 0000000000000..11bf790c822f3 --- /dev/null +++ b/include/zephyr/dt-bindings/sensor/icm45686.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2024 Intel Corporation + * Copyright (c) 2025 Croxel Inc. + * Copyright (c) 2025 CogniPilot Foundation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_TDK_ICM45686_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_TDK_ICM45686_H_ + +/** + * @defgroup ICM45686 Invensense (TDK) ICM45686 DT Options + * @ingroup sensor_interface + * @{ + */ + +/** + * @defgroup ICM45686_ACCEL_POWER_MODES Accelerometer power modes + * @{ + */ +#define ICM45686_DT_ACCEL_OFF 0 +#define ICM45686_DT_ACCEL_LP 2 +#define ICM45686_DT_ACCEL_LN 3 +/** @} */ + +/** + * @defgroup ICM45686_GYRO_POWER_MODES Gyroscope power modes + * @{ + */ +#define ICM45686_DT_GYRO_OFF 0 +#define ICM45686_DT_GYRO_STANDBY 1 +#define ICM45686_DT_GYRO_LP 2 +#define ICM45686_DT_GYRO_LN 3 +/** @} */ + +/** + * @defgroup ICM45686_ACCEL_SCALE Accelerometer scale options + * @{ + */ +#define ICM45686_DT_ACCEL_FS_32 0 +#define ICM45686_DT_ACCEL_FS_16 1 +#define ICM45686_DT_ACCEL_FS_8 2 +#define ICM45686_DT_ACCEL_FS_4 3 +#define ICM45686_DT_ACCEL_FS_2 4 +/** @} */ + +/** + * @defgroup ICM45686_GYRO_SCALE Gyroscope scale options + * @{ + */ +#define ICM45686_DT_GYRO_FS_4000 0 +#define ICM45686_DT_GYRO_FS_2000 1 +#define ICM45686_DT_GYRO_FS_1000 2 +#define ICM45686_DT_GYRO_FS_500 3 +#define ICM45686_DT_GYRO_FS_250 4 +#define ICM45686_DT_GYRO_FS_125 5 +#define ICM45686_DT_GYRO_FS_62_5 6 +#define ICM45686_DT_GYRO_FS_31_25 7 +#define ICM45686_DT_GYRO_FS_15_625 8 +/** @} */ + +/** + * @defgroup ICM45686_ACCEL_DATA_RATE Accelerometer data rate options + * @{ + */ +#define ICM45686_DT_ACCEL_ODR_6400 3 /* LN-mode only */ +#define ICM45686_DT_ACCEL_ODR_3200 4 /* LN-mode only */ +#define ICM45686_DT_ACCEL_ODR_1600 5 /* LN-mode only */ +#define ICM45686_DT_ACCEL_ODR_800 6 /* LN-mode only */ +#define ICM45686_DT_ACCEL_ODR_400 7 /* Both LN-mode and LP-mode */ +#define ICM45686_DT_ACCEL_ODR_200 8 /* Both LN-mode and LP-mode */ +#define ICM45686_DT_ACCEL_ODR_100 9 /* Both LN-mode and LP-mode */ +#define ICM45686_DT_ACCEL_ODR_50 10 /* Both LN-mode and LP-mode */ +#define ICM45686_DT_ACCEL_ODR_25 11 /* Both LN-mode and LP-mode */ +#define ICM45686_DT_ACCEL_ODR_12_5 12 /* Both LN-mode and LP-mode */ +#define ICM45686_DT_ACCEL_ODR_6_25 13 /* LP-mode only */ +#define ICM45686_DT_ACCEL_ODR_3_125 14 /* LP-mode only */ +#define ICM45686_DT_ACCEL_ODR_1_5625 15 /* LP-mode only */ +/** @} */ + +/** + * @defgroup ICM45686_GYRO_DATA_RATE Gyroscope data rate options + * @{ + */ +#define ICM45686_DT_GYRO_ODR_6400 3 /* LN-mode only */ +#define ICM45686_DT_GYRO_ODR_3200 4 /* LN-mode only */ +#define ICM45686_DT_GYRO_ODR_1600 5 /* LN-mode only */ +#define ICM45686_DT_GYRO_ODR_800 6 /* LN-mode only */ +#define ICM45686_DT_GYRO_ODR_400 7 /* Both LN-mode and LP-mode */ +#define ICM45686_DT_GYRO_ODR_200 8 /* Both LN-mode and LP-mode */ +#define ICM45686_DT_GYRO_ODR_100 9 /* Both LN-mode and LP-mode */ +#define ICM45686_DT_GYRO_ODR_50 10 /* Both LN-mode and LP-mode */ +#define ICM45686_DT_GYRO_ODR_25 11 /* Both LN-mode and LP-mode */ +#define ICM45686_DT_GYRO_ODR_12_5 12 /* Both LN-mode and LP-mode */ +#define ICM45686_DT_GYRO_ODR_6_25 13 /* LP-mode only */ +#define ICM45686_DT_GYRO_ODR_3_125 14 /* LP-mode only */ +#define ICM45686_DT_GYRO_ODR_1_5625 15 /* LP-mode only */ +/** @} */ + +/** + * @defgroup ICM45686_GYRO_LPF Gyroscope Low-pass Filtering options + * @{ + */ +#define ICM45686_DT_GYRO_LPF_BW_OFF 0 +#define ICM45686_DT_GYRO_LPF_BW_1_4 1 +#define ICM45686_DT_GYRO_LPF_BW_1_8 2 +#define ICM45686_DT_GYRO_LPF_BW_1_16 3 +#define ICM45686_DT_GYRO_LPF_BW_1_32 4 +#define ICM45686_DT_GYRO_LPF_BW_1_64 5 +#define ICM45686_DT_GYRO_LPF_BW_1_128 6 +/** @} */ + +/** + * @defgroup ICM45686_ACCEL_LPF Accelerometer Low-pass Filtering options + * @{ + */ +#define ICM45686_DT_ACCEL_LPF_BW_OFF 0 +#define ICM45686_DT_ACCEL_LPF_BW_1_4 1 +#define ICM45686_DT_ACCEL_LPF_BW_1_8 2 +#define ICM45686_DT_ACCEL_LPF_BW_1_16 3 +#define ICM45686_DT_ACCEL_LPF_BW_1_32 4 +#define ICM45686_DT_ACCEL_LPF_BW_1_64 5 +#define ICM45686_DT_ACCEL_LPF_BW_1_128 6 +/** @} */ + + +/** @} */ + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_TDK_ICM45686_H_ */ diff --git a/tests/drivers/build_all/sensor/sensors_trigger_global.conf b/tests/drivers/build_all/sensor/sensors_trigger_global.conf index 86b89e93c12eb..5b2de334273fa 100644 --- a/tests/drivers/build_all/sensor/sensors_trigger_global.conf +++ b/tests/drivers/build_all/sensor/sensors_trigger_global.conf @@ -26,6 +26,7 @@ CONFIG_HTS221_TRIGGER_GLOBAL_THREAD=y CONFIG_ICM42605_TRIGGER_GLOBAL_THREAD=y CONFIG_ICM42X70_TRIGGER_GLOBAL_THREAD=y CONFIG_ICM42688_TRIGGER_GLOBAL_THREAD=y +CONFIG_ICM45686_TRIGGER_GLOBAL_THREAD=y CONFIG_IIS2DH_TRIGGER_GLOBAL_THREAD=y CONFIG_IIS2DLPC_TRIGGER_GLOBAL_THREAD=y CONFIG_IIS2ICLX_TRIGGER_GLOBAL_THREAD=y diff --git a/tests/drivers/build_all/sensor/sensors_trigger_none.conf b/tests/drivers/build_all/sensor/sensors_trigger_none.conf index bc23289ae4345..5cfa14f6e5fff 100644 --- a/tests/drivers/build_all/sensor/sensors_trigger_none.conf +++ b/tests/drivers/build_all/sensor/sensors_trigger_none.conf @@ -26,6 +26,7 @@ CONFIG_HTS221_TRIGGER_NONE=y CONFIG_ICM42605_TRIGGER_NONE=y CONFIG_ICM42X70_TRIGGER_NONE=y CONFIG_ICM42688_TRIGGER_NONE=y +CONFIG_ICM45686_TRIGGER_NONE=y CONFIG_IIS2DH_TRIGGER_NONE=y CONFIG_IIS2DLPC_TRIGGER_NONE=y CONFIG_IIS2ICLX_TRIGGER_NONE=y diff --git a/tests/drivers/build_all/sensor/sensors_trigger_own.conf b/tests/drivers/build_all/sensor/sensors_trigger_own.conf index 7192dd15a39d0..13fa82497ceb6 100644 --- a/tests/drivers/build_all/sensor/sensors_trigger_own.conf +++ b/tests/drivers/build_all/sensor/sensors_trigger_own.conf @@ -24,6 +24,7 @@ CONFIG_HMC5883L_TRIGGER_OWN_THREAD=y CONFIG_HTS221_TRIGGER_OWN_THREAD=y CONFIG_ICM42X70_TRIGGER_OWN_THREAD=y CONFIG_ICM42688_TRIGGER_OWN_THREAD=y +CONFIG_ICM45686_TRIGGER_OWN_THREAD=y CONFIG_IIS2DH_TRIGGER_OWN_THREAD=y CONFIG_IIS2DLPC_TRIGGER_OWN_THREAD=y CONFIG_IIS2ICLX_TRIGGER_OWN_THREAD=y diff --git a/tests/drivers/build_all/sensor/spi.dtsi b/tests/drivers/build_all/sensor/spi.dtsi index 6f48188a2411c..2f6b6e3ee0185 100644 --- a/tests/drivers/build_all/sensor/spi.dtsi +++ b/tests/drivers/build_all/sensor/spi.dtsi @@ -399,3 +399,10 @@ test_spi_ilps22qs: ilps22qs@30 { spi-max-frequency = <0>; status = "okay"; }; + +test_spi_icm45686:icm45686@31 { + compatible = "invensense,icm45686"; + reg = <0x31>; + spi-max-frequency = <0>; + int-gpios = <&test_gpio 0 0>; +};