From fcf3dbe29aa0fd65abd68d6615a82d25abadd0bf Mon Sep 17 00:00:00 2001 From: Florijan Plohl Date: Mon, 16 Dec 2024 12:53:16 +0100 Subject: [PATCH] drivers: sensor: Add icm40627 6-axis accelerometer driver Initial driver for the icm40627 from Invensense/TDK, a 6-axis accelerometer with gyroscope and temperature sensing capabilities. Signed-off-by: Florijan Plohl --- drivers/sensor/tdk/CMakeLists.txt | 1 + drivers/sensor/tdk/Kconfig | 1 + drivers/sensor/tdk/icm40627/CMakeLists.txt | 10 + drivers/sensor/tdk/icm40627/Kconfig | 58 ++ drivers/sensor/tdk/icm40627/icm40627.c | 685 ++++++++++++++++++ drivers/sensor/tdk/icm40627/icm40627.h | 88 +++ drivers/sensor/tdk/icm40627/icm40627_i2c.c | 94 +++ drivers/sensor/tdk/icm40627/icm40627_reg.h | 237 ++++++ .../sensor/tdk/icm40627/icm40627_trigger.c | 173 +++++ .../sensor/tdk/icm40627/icm40627_trigger.h | 46 ++ .../sensor/invensense,icm40627-i2c.yaml | 8 + dts/bindings/sensor/invensense,icm40627.yaml | 86 +++ 12 files changed, 1487 insertions(+) create mode 100644 drivers/sensor/tdk/icm40627/CMakeLists.txt create mode 100644 drivers/sensor/tdk/icm40627/Kconfig create mode 100644 drivers/sensor/tdk/icm40627/icm40627.c create mode 100644 drivers/sensor/tdk/icm40627/icm40627.h create mode 100644 drivers/sensor/tdk/icm40627/icm40627_i2c.c create mode 100644 drivers/sensor/tdk/icm40627/icm40627_reg.h create mode 100644 drivers/sensor/tdk/icm40627/icm40627_trigger.c create mode 100644 drivers/sensor/tdk/icm40627/icm40627_trigger.h create mode 100644 dts/bindings/sensor/invensense,icm40627-i2c.yaml create mode 100644 dts/bindings/sensor/invensense,icm40627.yaml diff --git a/drivers/sensor/tdk/CMakeLists.txt b/drivers/sensor/tdk/CMakeLists.txt index 7e6c4090391a..127fa0c08fce 100644 --- a/drivers/sensor/tdk/CMakeLists.txt +++ b/drivers/sensor/tdk/CMakeLists.txt @@ -2,6 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # zephyr-keep-sorted-start +add_subdirectory_ifdef(CONFIG_ICM40627 icm40627) add_subdirectory_ifdef(CONFIG_ICM42605 icm42605) add_subdirectory_ifdef(CONFIG_ICM42688 icm42688) add_subdirectory_ifdef(CONFIG_ICM42X70 icm42x70) diff --git a/drivers/sensor/tdk/Kconfig b/drivers/sensor/tdk/Kconfig index c90384278512..382291950b2a 100644 --- a/drivers/sensor/tdk/Kconfig +++ b/drivers/sensor/tdk/Kconfig @@ -2,6 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 # zephyr-keep-sorted-start +source "drivers/sensor/tdk/icm40627/Kconfig" source "drivers/sensor/tdk/icm42605/Kconfig" source "drivers/sensor/tdk/icm42688/Kconfig" source "drivers/sensor/tdk/icm42x70/Kconfig" diff --git a/drivers/sensor/tdk/icm40627/CMakeLists.txt b/drivers/sensor/tdk/icm40627/CMakeLists.txt new file mode 100644 index 000000000000..9c7caeda2744 --- /dev/null +++ b/drivers/sensor/tdk/icm40627/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources( + icm40627.c + icm40627_i2c.c +) + +zephyr_library_sources_ifdef(CONFIG_ICM40627_TRIGGER icm40627_trigger.c) diff --git a/drivers/sensor/tdk/icm40627/Kconfig b/drivers/sensor/tdk/icm40627/Kconfig new file mode 100644 index 000000000000..8cf79783f562 --- /dev/null +++ b/drivers/sensor/tdk/icm40627/Kconfig @@ -0,0 +1,58 @@ +# ICM40627 Six-Axis Motion Tracking device configuration options +# +# Copyright 2024 PHYTEC America, LLC +# +# SPDX-License-Identifier: Apache-2.0 + +menuconfig ICM40627 + bool "ICM40627 Six-Axis Motion Tracking Device" + default y + depends on DT_HAS_INVENSENSE_ICM40627_ENABLED + select SPI if $(dt_compat_on_bus,$(DT_COMPAT_INVENSENSE_ICM40627),spi) + select I2C if $(dt_compat_on_bus,$(DT_COMPAT_INVENSENSE_ICM40627),i2c) + help + Enable driver for ICM40627 SPI-based six-axis motion tracking device. + +if ICM40627 + +choice ICM40627_TRIGGER_MODE + prompt "Trigger mode" + default ICM40627_TRIGGER_NONE + help + Specify the type of triggering to be used by the driver. + +config ICM40627_TRIGGER_NONE + bool "No trigger" + +config ICM40627_TRIGGER_GLOBAL_THREAD + bool "Use global thread" + depends on GPIO + depends on $(dt_compat_any_has_prop,$(DT_COMPAT_INVENSENSE_ICM40627),int-gpios) + select ICM40627_TRIGGER + +config ICM40627_TRIGGER_OWN_THREAD + bool "Use own thread" + depends on GPIO + depends on $(dt_compat_any_has_prop,$(DT_COMPAT_INVENSENSE_ICM40627),int-gpios) + select ICM40627_TRIGGER + +endchoice + +config ICM40627_TRIGGER + bool + +config ICM40627_THREAD_PRIORITY + int "Thread priority" + depends on ICM40627_TRIGGER_OWN_THREAD + default 10 + help + Priority of thread used by the driver to handle interrupts. + +config ICM40627_THREAD_STACK_SIZE + int "Thread stack size" + depends on ICM40627_TRIGGER_OWN_THREAD + default 1024 + help + Stack size of thread used by the driver to handle interrupts. + +endif # ICM40627 diff --git a/drivers/sensor/tdk/icm40627/icm40627.c b/drivers/sensor/tdk/icm40627/icm40627.c new file mode 100644 index 000000000000..10cd29758799 --- /dev/null +++ b/drivers/sensor/tdk/icm40627/icm40627.c @@ -0,0 +1,685 @@ +/* + * Copyright 2024 PHYTEC America, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT invensense_icm40627 + +#include +#include +#include "icm40627.h" +#include "icm40627_reg.h" +#include "icm40627_trigger.h" + +#include +LOG_MODULE_REGISTER(ICM40627, CONFIG_SENSOR_LOG_LEVEL); + +/* + * Gyro FS to scaling factor mapping. + * See datasheet section 3.1 for details + */ +static const uint16_t icm40627_gyro_sensitivity_x10[] = { + 164, /* BIT_GYRO_UI_FS_2000 */ + 328, /* BIT_GYRO_UI_FS_1000 */ + 655, /* BIT_GYRO_UI_FS_500 */ + 1310, /* BIT_GYRO_UI_FS_250 */ + 2620, /* BIT_GYRO_UI_FS_125 */ + 5243, /* BIT_GYRO_UI_FS_62_5 */ + 10486, /* BIT_GYRO_UI_FS_31_25 */ + 20972, /* BIT_GYRO_UI_FS_15_625 */ +}; + +static int icm40627_set_accel_fs(const struct device *dev, uint16_t fs) +{ + const struct icm40627_config *cfg = dev->config; + struct icm40627_data *data = dev->data; + uint8_t temp; + + if ((fs > 16) || (fs < 2)) { + LOG_ERR("Unsupported range"); + return -ENOTSUP; + } + + if (fs > 8) { + temp = BIT_ACCEL_UI_FS_16; + } else if (fs > 4) { + temp = BIT_ACCEL_UI_FS_8; + } else if (fs > 2) { + temp = BIT_ACCEL_UI_FS_4; + } else { + temp = BIT_ACCEL_UI_FS_2; + } + + data->accel_sensitivity_shift = MIN_ACCEL_SENS_SHIFT; + + return cfg->bus_io->update(&cfg->bus, REG_ACCEL_CONFIG0, (uint8_t)MASK_ACCEL_UI_FS_SEL, + temp); +} + +static int icm40627_set_gyro_fs(const struct device *dev, uint16_t fs) +{ + const struct icm40627_config *cfg = dev->config; + struct icm40627_data *data = dev->data; + uint8_t temp; + + if ((fs > 2000) || (fs < 250)) { + LOG_ERR("Unsupported range"); + return -ENOTSUP; + } + + if (fs > 1000) { + temp = BIT_GYRO_UI_FS_2000; + } else if (fs > 500) { + temp = BIT_GYRO_UI_FS_1000; + } else if (fs > 250) { + temp = BIT_GYRO_UI_FS_500; + } else if (fs > 125) { + temp = BIT_GYRO_UI_FS_250; + } else if (fs > 62) { + temp = BIT_GYRO_UI_FS_125; + } else if (fs > 31) { + temp = BIT_GYRO_UI_FS_62; + } else if (fs > 15) { + temp = BIT_GYRO_UI_FS_31; + } else { + temp = BIT_GYRO_UI_FS_15; + } + + data->gyro_sensitivity_x10 = icm40627_gyro_sensitivity_x10[temp]; + + return cfg->bus_io->update(&cfg->bus, REG_GYRO_CONFIG0, (uint8_t)MASK_GYRO_UI_FS_SEL, temp); +} + +static int icm40627_set_accel_odr(const struct device *dev, uint16_t rate) +{ + const struct icm40627_config *cfg = dev->config; + uint8_t temp; + + if ((rate > 8000) || (rate < 1)) { + LOG_ERR("Unsupported frequency"); + return -ENOTSUP; + } + + if (rate > 4000) { + temp = BIT_ACCEL_ODR_8000; + } else if (rate > 2000) { + temp = BIT_ACCEL_ODR_4000; + } else if (rate > 1000) { + temp = BIT_ACCEL_ODR_2000; + } else if (rate > 500) { + temp = BIT_ACCEL_ODR_1000; + } else if (rate > 200) { + temp = BIT_ACCEL_ODR_500; + } else if (rate > 100) { + temp = BIT_ACCEL_ODR_200; + } else if (rate > 50) { + temp = BIT_ACCEL_ODR_100; + } else if (rate > 25) { + temp = BIT_ACCEL_ODR_50; + } else if (rate > 12) { + temp = BIT_ACCEL_ODR_25; + } else if (rate > 6) { + temp = BIT_ACCEL_ODR_12; + } else if (rate > 3) { + temp = BIT_ACCEL_ODR_6; + } else if (rate > 1) { + temp = BIT_ACCEL_ODR_3; + } else { + temp = BIT_ACCEL_ODR_1; + } + + return cfg->bus_io->update(&cfg->bus, REG_ACCEL_CONFIG0, (uint8_t)MASK_ACCEL_ODR, temp); +} + +static int icm40627_set_gyro_odr(const struct device *dev, uint16_t rate) +{ + const struct icm40627_config *cfg = dev->config; + uint8_t temp; + + if ((rate > 8000) || (rate < 12)) { + LOG_ERR("Unsupported frequency"); + return -ENOTSUP; + } + + if (rate > 4000) { + temp = BIT_GYRO_ODR_8000; + } else if (rate > 2000) { + temp = BIT_GYRO_ODR_4000; + } else if (rate > 1000) { + temp = BIT_GYRO_ODR_2000; + } else if (rate > 500) { + temp = BIT_GYRO_ODR_1000; + } else if (rate > 200) { + temp = BIT_GYRO_ODR_500; + } else if (rate > 100) { + temp = BIT_GYRO_ODR_200; + } else if (rate > 50) { + temp = BIT_GYRO_ODR_100; + } else if (rate > 25) { + temp = BIT_GYRO_ODR_50; + } else if (rate > 12) { + temp = BIT_GYRO_ODR_25; + } else { + temp = BIT_GYRO_ODR_12; + } + + return cfg->bus_io->update(&cfg->bus, REG_GYRO_CONFIG0, (uint8_t)MASK_GYRO_ODR, temp); +} + +static int icm40627_sensor_init(const struct device *dev) +{ + int res; + uint8_t value; + const struct icm40627_config *cfg = dev->config; + + /* start up time for register read/write after POR is 1ms and supply ramp time is 3ms */ + k_msleep(3); + + /* perform a soft reset to ensure a clean slate, reset bit will auto-clear */ + res = cfg->bus_io->write(&cfg->bus, REG_DEVICE_CONFIG, BIT_SOFT_RESET); + + if (res) { + LOG_ERR("write REG_SIGNAL_PATH_RESET failed"); + return res; + } + + /* wait for soft reset to take effect */ + k_msleep(SOFT_RESET_TIME_MS); + + /* always use internal RC oscillator */ + res = cfg->bus_io->write(&cfg->bus, REG_INTF_CONFIG1, + (uint8_t)FIELD_PREP(MASK_CLKSEL, BIT_CLKSEL_INT_RC)); + + if (res) { + return res; + } + + /* clear reset done int flag */ + res = cfg->bus_io->read(&cfg->bus, REG_INT_STATUS, &value, 1); + + if (res) { + return res; + } + + if (FIELD_GET(BIT_STATUS_RESET_DONE_INT, value) != 1) { + LOG_ERR("unexpected RESET_DONE_INT value, %i", value); + return -EINVAL; + } + + res = cfg->bus_io->read(&cfg->bus, REG_WHO_AM_I, &value, 1); + + if (res) { + return res; + } + + if (value != WHO_AM_I_ICM40627) { + LOG_ERR("invalid WHO_AM_I value, was %i but expected %i", value, WHO_AM_I_ICM40627); + return -EINVAL; + } + + LOG_DBG("device id: 0x%02X", value); + + return 0; +} + +static int icm40627_turn_on_sensor(const struct device *dev) +{ + struct icm40627_data *data = dev->data; + const struct icm40627_config *cfg = dev->config; + uint8_t value; + int res; + + value = FIELD_PREP(MASK_ACCEL_MODE, BIT_ACCEL_MODE_LNM) | + FIELD_PREP(MASK_GYRO_MODE, BIT_GYRO_MODE_LNM); + + res = cfg->bus_io->update(&cfg->bus, REG_PWR_MGMT0, + (uint8_t)(MASK_ACCEL_MODE | MASK_GYRO_MODE), value); + + if (res) { + return res; + } + + res = icm40627_set_accel_fs(dev, data->accel_fs); + + if (res) { + return res; + } + + res = icm40627_set_accel_odr(dev, data->accel_hz); + + if (res) { + return res; + } + + res = icm40627_set_gyro_fs(dev, data->gyro_fs); + + if (res) { + return res; + } + + res = icm40627_set_gyro_odr(dev, data->gyro_hz); + + if (res) { + return res; + } + + /* + * Accelerometer sensor need at least 10ms startup time + * Gyroscope sensor need at least 30ms startup time + */ + k_msleep(100); + + return 0; +} + +static void icm40627_convert_accel(struct sensor_value *val, int16_t raw_val, + uint16_t sensitivity_shift) +{ + /* see datasheet section 3.2 for details */ + int64_t conv_val = ((int64_t)raw_val * SENSOR_G) >> sensitivity_shift; + + val->val1 = conv_val / 1000000LL; + val->val2 = conv_val % 1000000LL; +} + +static void icm40627_convert_gyro(struct sensor_value *val, int16_t raw_val, + uint16_t sensitivity_x10) +{ + /* see datasheet section 3.1 for details */ + int64_t conv_val = ((int64_t)raw_val * SENSOR_PI * 10) / (sensitivity_x10 * 180LL); + + val->val1 = conv_val / 1000000LL; + val->val2 = conv_val % 1000000LL; +} + +static inline void icm40627_convert_temp(struct sensor_value *val, int16_t raw_val) +{ + /* see datasheet section 14.6 for details */ + val->val1 = (((int64_t)raw_val * 100) / 13248) + 25; + val->val2 = ((((int64_t)raw_val * 100) % 13248) * 1000000) / 13248; + + if (val->val2 < 0) { + val->val1--; + val->val2 += 1000000; + } else if (val->val2 >= 1000000) { + val->val1++; + val->val2 -= 1000000; + } +} + +static int icm40627_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + int res = 0; + const struct icm40627_data *data = dev->data; + + icm40627_lock(dev); + + switch (chan) { + case SENSOR_CHAN_ACCEL_XYZ: + icm40627_convert_accel(&val[0], data->accel_x, data->accel_sensitivity_shift); + icm40627_convert_accel(&val[1], data->accel_y, data->accel_sensitivity_shift); + icm40627_convert_accel(&val[2], data->accel_z, data->accel_sensitivity_shift); + break; + case SENSOR_CHAN_ACCEL_X: + icm40627_convert_accel(val, data->accel_x, data->accel_sensitivity_shift); + break; + case SENSOR_CHAN_ACCEL_Y: + icm40627_convert_accel(val, data->accel_y, data->accel_sensitivity_shift); + break; + case SENSOR_CHAN_ACCEL_Z: + icm40627_convert_accel(val, data->accel_z, data->accel_sensitivity_shift); + break; + case SENSOR_CHAN_GYRO_XYZ: + icm40627_convert_gyro(&val[0], data->gyro_x, data->gyro_sensitivity_x10); + icm40627_convert_gyro(&val[1], data->gyro_y, data->gyro_sensitivity_x10); + icm40627_convert_gyro(&val[2], data->gyro_z, data->gyro_sensitivity_x10); + break; + case SENSOR_CHAN_GYRO_X: + icm40627_convert_gyro(val, data->gyro_x, data->gyro_sensitivity_x10); + break; + case SENSOR_CHAN_GYRO_Y: + icm40627_convert_gyro(val, data->gyro_y, data->gyro_sensitivity_x10); + break; + case SENSOR_CHAN_GYRO_Z: + icm40627_convert_gyro(val, data->gyro_z, data->gyro_sensitivity_x10); + break; + case SENSOR_CHAN_DIE_TEMP: + icm40627_convert_temp(val, data->temp); + break; + default: + res = -ENOTSUP; + break; + } + + icm40627_unlock(dev); + + return res; +} + +static int icm40627_sample_fetch_accel(const struct device *dev) +{ + const struct icm40627_config *cfg = dev->config; + struct icm40627_data *data = dev->data; + uint8_t buffer[ACCEL_DATA_SIZE]; + + int res = cfg->bus_io->read(&cfg->bus, REG_ACCEL_DATA_X1, buffer, ACCEL_DATA_SIZE); + + if (res) { + return res; + } + + data->accel_x = (int16_t)sys_get_be16(&buffer[0]); + data->accel_y = (int16_t)sys_get_be16(&buffer[2]); + data->accel_z = (int16_t)sys_get_be16(&buffer[4]); + + return 0; +} + +static int icm40627_sample_fetch_gyro(const struct device *dev) +{ + const struct icm40627_config *cfg = dev->config; + struct icm40627_data *data = dev->data; + uint8_t buffer[GYRO_DATA_SIZE]; + + int res = cfg->bus_io->read(&cfg->bus, REG_GYRO_DATA_X1, buffer, GYRO_DATA_SIZE); + + if (res) { + return res; + } + + data->gyro_x = (int16_t)sys_get_be16(&buffer[0]); + data->gyro_y = (int16_t)sys_get_be16(&buffer[2]); + data->gyro_z = (int16_t)sys_get_be16(&buffer[4]); + + return 0; +} + +static int icm40627_sample_fetch_temp(const struct device *dev) +{ + const struct icm40627_config *cfg = dev->config; + struct icm40627_data *data = dev->data; + uint8_t buffer[TEMP_DATA_SIZE]; + + int res = cfg->bus_io->read(&cfg->bus, REG_TEMP_DATA1, buffer, TEMP_DATA_SIZE); + + if (res) { + return res; + } + + data->temp = (int16_t)sys_get_be16(&buffer[0]); + + return 0; +} + +static int icm40627_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + uint8_t status; + const struct icm40627_config *cfg = dev->config; + + icm40627_lock(dev); + + int res = cfg->bus_io->read(&cfg->bus, REG_INT_STATUS, &status, 1); + + if (res) { + goto cleanup; + } + + if (!FIELD_GET(BIT_INT_STATUS_DATA_RDY_INT, status)) { + res = -EBUSY; + goto cleanup; + } + + switch (chan) { + case SENSOR_CHAN_ALL: + res |= icm40627_sample_fetch_accel(dev); + res |= icm40627_sample_fetch_gyro(dev); + res |= icm40627_sample_fetch_temp(dev); + break; + case SENSOR_CHAN_ACCEL_XYZ: + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + res = icm40627_sample_fetch_accel(dev); + break; + case SENSOR_CHAN_GYRO_XYZ: + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + res = icm40627_sample_fetch_gyro(dev); + break; + case SENSOR_CHAN_DIE_TEMP: + res = icm40627_sample_fetch_temp(dev); + break; + default: + res = -ENOTSUP; + break; + } + +cleanup: + icm40627_unlock(dev); + return res; +} + +static int icm40627_attr_set(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, const struct sensor_value *val) +{ + int res = 0; + struct icm40627_data *data = dev->data; + + __ASSERT_NO_MSG(val != NULL); + + icm40627_lock(dev); + + switch (chan) { + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_ACCEL_XYZ: + if (attr == SENSOR_ATTR_SAMPLING_FREQUENCY) { + res = icm40627_set_accel_odr(dev, data->accel_hz); + + if (res) { + LOG_ERR("Incorrect sampling value"); + } else { + data->accel_hz = val->val1; + } + } else if (attr == SENSOR_ATTR_FULL_SCALE) { + res = icm40627_set_accel_fs(dev, data->accel_fs); + + if (res) { + LOG_ERR("Incorrect fullscale value"); + } else { + data->accel_fs = val->val1; + } + } else { + LOG_ERR("Unsupported attribute"); + res = -ENOTSUP; + } + break; + + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + case SENSOR_CHAN_GYRO_XYZ: + if (attr == SENSOR_ATTR_SAMPLING_FREQUENCY) { + res = icm40627_set_gyro_odr(dev, data->gyro_hz); + + if (res) { + LOG_ERR("Incorrect sampling value"); + } else { + data->gyro_hz = val->val1; + } + } else if (attr == SENSOR_ATTR_FULL_SCALE) { + res = icm40627_set_gyro_fs(dev, data->gyro_fs); + + if (res) { + LOG_ERR("Incorrect fullscale value"); + } else { + data->gyro_fs = val->val1; + } + } else { + LOG_ERR("Unsupported attribute"); + res = -EINVAL; + } + break; + + default: + LOG_ERR("Unsupported channel"); + res = -EINVAL; + break; + } + + icm40627_unlock(dev); + + return res; +} + +static int icm40627_attr_get(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, struct sensor_value *val) +{ + const struct icm40627_data *data = dev->data; + int res = 0; + + __ASSERT_NO_MSG(val != NULL); + + icm40627_lock(dev); + + switch (chan) { + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_ACCEL_XYZ: + if (attr == SENSOR_ATTR_SAMPLING_FREQUENCY) { + val->val1 = data->accel_hz; + } else if (attr == SENSOR_ATTR_FULL_SCALE) { + val->val1 = data->accel_fs; + } else { + LOG_ERR("Unsupported attribute"); + res = -EINVAL; + } + break; + + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + case SENSOR_CHAN_GYRO_XYZ: + if (attr == SENSOR_ATTR_SAMPLING_FREQUENCY) { + val->val1 = data->gyro_hz; + } else if (attr == SENSOR_ATTR_FULL_SCALE) { + val->val1 = data->gyro_fs; + } else { + LOG_ERR("Unsupported attribute"); + res = -EINVAL; + } + break; + + default: + LOG_ERR("Unsupported channel"); + res = -EINVAL; + break; + } + + icm40627_unlock(dev); + + return res; +} + +static inline int icm40627_bus_check(const struct device *dev) +{ + const struct icm40627_config *cfg = dev->config; + + return cfg->bus_io->check(&cfg->bus); +} + +static int icm40627_init(const struct device *dev) +{ + struct icm40627_data *data = dev->data; + + if (icm40627_bus_check(dev) < 0) { + LOG_ERR("Bus is not ready"); + return -ENODEV; + } + + data->accel_x = 0; + data->accel_y = 0; + data->accel_z = 0; + data->gyro_x = 0; + data->gyro_y = 0; + data->gyro_z = 0; + data->temp = 0; + + if (icm40627_sensor_init(dev)) { + LOG_ERR("could not initialize sensor"); + return -EIO; + } + +#ifdef CONFIG_ICM40627_TRIGGER + if (icm40627_trigger_init(dev)) { + LOG_ERR("Failed to initialize interrupts."); + return -EIO; + } +#endif + + int res = icm40627_turn_on_sensor(dev); + +#ifdef CONFIG_ICM40627_TRIGGER + if (icm40627_trigger_enable_interrupt(dev)) { + LOG_ERR("Failed to enable interrupts"); + return -EIO; + } +#endif + + return res; +} + +#ifndef CONFIG_ICM40627_TRIGGER + +void icm40627_lock(const struct device *dev) +{ + ARG_UNUSED(dev); +} + +void icm40627_unlock(const struct device *dev) +{ + ARG_UNUSED(dev); +} + +#endif + +static const struct sensor_driver_api icm40627_driver_api = { +#ifdef CONFIG_ICM40627_TRIGGER + .trigger_set = icm40627_trigger_set, +#endif + .sample_fetch = icm40627_sample_fetch, + .channel_get = icm40627_channel_get, + .attr_set = icm40627_attr_set, + .attr_get = icm40627_attr_get, +}; + +/* Initializes the bus members for an instance on an I2C bus. */ +#define ICM40627_CONFIG_I2C(inst) \ + .bus.i2c = I2C_DT_SPEC_INST_GET(inst), .bus_io = &icm40627_bus_io_i2c, + +#define ICM40627_INIT(inst) \ + static struct icm40627_data icm40627_driver_##inst = { \ + .accel_hz = DT_INST_PROP(inst, accel_hz), \ + .accel_fs = DT_INST_PROP(inst, accel_fs), \ + .gyro_hz = DT_INST_PROP(inst, gyro_hz), \ + .gyro_fs = DT_INST_PROP(inst, gyro_fs), \ + }; \ + \ + static const struct icm40627_config icm40627_cfg_##inst = { \ + COND_CODE_1(DT_INST_ON_BUS(inst, spi), \ + (ICM40627_CONFIG_SPI(inst)), \ + (ICM40627_CONFIG_I2C(inst))) .gpio_int = \ + GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, \ + {0}), \ + }; \ + \ + SENSOR_DEVICE_DT_INST_DEFINE(inst, icm40627_init, NULL, &icm40627_driver_##inst, \ + &icm40627_cfg_##inst, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &icm40627_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(ICM40627_INIT) diff --git a/drivers/sensor/tdk/icm40627/icm40627.h b/drivers/sensor/tdk/icm40627/icm40627.h new file mode 100644 index 000000000000..998cf0ca1c66 --- /dev/null +++ b/drivers/sensor/tdk/icm40627/icm40627.h @@ -0,0 +1,88 @@ +/* + * Copyright 2024 PHYTEC America, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ICM40627_H_ +#define ZEPHYR_DRIVERS_SENSOR_ICM40627_H_ + +#include +#include +#include +#include +#include + +#define ICM40627_BUS_SPI DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(invensense_icm40627, spi) +#define ICM40627_BUS_I2C DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(invensense_icm40627, i2c) + +union icm40627_bus { +#if ICM40627_BUS_SPI + struct spi_dt_spec spi; +#endif +#if ICM40627_BUS_I2C + struct i2c_dt_spec i2c; +#endif +}; + +typedef int (*icm40627_bus_check_fn)(const union icm40627_bus *bus); +typedef int (*icm40627_reg_read_fn)(const union icm40627_bus *bus, uint16_t reg, uint8_t *data, + size_t size); +typedef int (*icm40627_reg_write_fn)(const union icm40627_bus *bus, uint16_t reg, uint8_t data); + +typedef int (*icm40627_reg_update_fn)(const union icm40627_bus *bus, uint16_t reg, uint8_t mask, + uint8_t data); + +struct icm40627_bus_io { + icm40627_bus_check_fn check; + icm40627_reg_read_fn read; + icm40627_reg_write_fn write; + icm40627_reg_update_fn update; +}; + +#if ICM40627_BUS_SPI +extern const struct icm40627_bus_io icm40627_bus_io_spi; +#endif + +#if ICM40627_BUS_I2C +extern const struct icm40627_bus_io icm40627_bus_io_i2c; +#endif + +struct icm40627_data { + int16_t accel_x; + int16_t accel_y; + int16_t accel_z; + uint16_t accel_sensitivity_shift; + uint16_t accel_hz; + uint16_t accel_fs; + int16_t gyro_x; + int16_t gyro_y; + int16_t gyro_z; + uint16_t gyro_sensitivity_x10; + uint16_t gyro_hz; + uint16_t gyro_fs; + int16_t temp; +#ifdef CONFIG_ICM40627_TRIGGER + const struct device *dev; + struct gpio_callback gpio_cb; + sensor_trigger_handler_t data_ready_handler; + const struct sensor_trigger *data_ready_trigger; + struct k_mutex mutex; +#endif +#ifdef CONFIG_ICM40627_TRIGGER_OWN_THREAD + K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_ICM40627_THREAD_STACK_SIZE); + struct k_thread thread; + struct k_sem gpio_sem; +#endif +#ifdef CONFIG_ICM40627_TRIGGER_GLOBAL_THREAD + struct k_work work; +#endif +}; + +struct icm40627_config { + union icm40627_bus bus; + const struct icm40627_bus_io *bus_io; + struct gpio_dt_spec gpio_int; +}; + +#endif /* ZEPHYR_DRIVERS_SENSOR_ICM40627_H_ */ diff --git a/drivers/sensor/tdk/icm40627/icm40627_i2c.c b/drivers/sensor/tdk/icm40627/icm40627_i2c.c new file mode 100644 index 000000000000..7bd11da00481 --- /dev/null +++ b/drivers/sensor/tdk/icm40627/icm40627_i2c.c @@ -0,0 +1,94 @@ +/* + * Copyright 2024 PHYTEC America, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Bus-specific functionality for ICM40627 accessed via I2C. + */ + +#include "icm40627.h" +#include "icm40627_reg.h" + +#if ICM40627_BUS_I2C +static int icm40627_bus_check_i2c(const union icm40627_bus *bus) +{ + return i2c_is_ready_dt(&bus->i2c) ? 0 : -ENODEV; +} + +static int i2c_read_bank(const union icm40627_bus *bus, uint8_t reg, uint8_t bank, uint8_t *buf, + size_t len) +{ + int res = i2c_reg_write_byte_dt(&bus->i2c, REG_REG_BANK_SEL, bank); + + if (res) { + return res; + } + + for (size_t i = 0; i < len; i++) { + uint8_t addr = reg + i; + + res = i2c_reg_read_byte_dt(&bus->i2c, addr, &buf[i]); + + if (res) { + return res; + } + } + + return 0; +} + +static int icm40627_reg_read_i2c(const union icm40627_bus *bus, uint16_t reg, uint8_t *data, + size_t len) +{ + int res = 0; + uint8_t bank = FIELD_GET(REG_BANK_MASK, reg); + uint8_t address = FIELD_GET(REG_ADDRESS_MASK, reg); + + res = i2c_read_bank(bus, address, bank, data, len); + + return res; +} + +static int i2c_write_bank(const union icm40627_bus *bus, uint16_t reg, uint8_t bank, uint8_t buf) +{ + int res = i2c_reg_write_byte_dt(&bus->i2c, REG_REG_BANK_SEL, bank); + + if (res) { + return res; + } + + res = i2c_reg_write_byte_dt(&bus->i2c, reg, buf); + + if (res) { + return res; + } + + return 0; +} + +static int icm40627_reg_write_i2c(const union icm40627_bus *bus, uint16_t reg, uint8_t data) +{ + int res = 0; + uint8_t bank = FIELD_GET(REG_BANK_MASK, reg); + uint8_t address = FIELD_GET(REG_ADDRESS_MASK, reg); + + res = i2c_write_bank(bus, address, bank, data); + + return res; +} + +static int icm40627_reg_update_i2c(const union icm40627_bus *bus, uint16_t reg, uint8_t mask, + uint8_t val) +{ + return i2c_reg_update_byte_dt(&bus->i2c, reg, mask, val); +} + +const struct icm40627_bus_io icm40627_bus_io_i2c = { + .check = icm40627_bus_check_i2c, + .read = icm40627_reg_read_i2c, + .write = icm40627_reg_write_i2c, + .update = icm40627_reg_update_i2c, +}; +#endif /* ICM40627_BUS_I2C */ diff --git a/drivers/sensor/tdk/icm40627/icm40627_reg.h b/drivers/sensor/tdk/icm40627/icm40627_reg.h new file mode 100644 index 000000000000..389759f56c2c --- /dev/null +++ b/drivers/sensor/tdk/icm40627/icm40627_reg.h @@ -0,0 +1,237 @@ +/* + * Copyright 2024 PHYTEC America, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ICM40627_REG_H_ +#define ZEPHYR_DRIVERS_SENSOR_ICM40627_REG_H_ + +#include + +/* Helper macros for addressing registers in BANKs 1-4 */ +#define REG_ADDRESS_MASK GENMASK(7, 0) +#define REG_BANK_MASK GENMASK(15, 8) + +/* BANK 0 */ +#define REG_DEVICE_CONFIG 0x11 +#define REG_DRIVE_CONFIG 0x13 +#define REG_INT_CONFIG 0x14 +#define REG_FIFO_CONFIG 0x16 +#define REG_TEMP_DATA1 0x1D +#define REG_ACCEL_DATA_X1 0x1F +#define REG_GYRO_DATA_X1 0x25 +#define REG_TMST_FSYNCH 0x2B +#define REG_INT_STATUS 0x2D +#define REG_FIFO_COUNTH 0x2E +#define REG_FIFO_COUNTL 0x2F +#define REG_FIFO_DATA 0x30 +#define REG_APEX_DATA0 0x31 +#define REG_APEX_DATA1 0x32 +#define REG_APEX_DATA2 0x33 +#define REG_APEX_DATA3 0x34 +#define REG_APEX_DATA4 0x35 +#define REG_APEX_DATA5 0x36 +#define REG_INT_STATUS2 0x37 +#define REG_INT_STATUS3 0x38 +#define REG_SIGNAL_PATH_RESET 0x4B +#define REG_INTF_CONFIG0 0x4C +#define REG_INTF_CONFIG1 0x4D +#define REG_PWR_MGMT0 0x4E +#define REG_GYRO_CONFIG0 0x4F +#define REG_ACCEL_CONFIG0 0x50 +#define REG_GYRO_CONFIG1 0x51 +#define REG_ACCEL_GYRO_CONFIG0 0x52 +#define REG_ACCEL_CONFIG1 0x53 +#define REG_TMST_CONFIG 0x54 +#define REG_APEX_CONFIG0 0x56 +#define REG_SMD_CONFIG 0x57 +#define REG_FIFO_CONFIG1 0x5F +#define REG_FIFO_CONFIG2 0x60 +#define REG_FSYNC_CONFIG 0x62 +#define REG_INT_CONFIG0 0x63 +#define REG_INT_CONFIG1 0x64 +#define REG_INT_SOURCE0 0x65 +#define REG_INT_SOURCE1 0x66 +#define REG_INT_SOURCE2 0x67 +#define REG_INT_SOURCE3 0x68 +#define REG_INT_SOURCE4 0x69 +#define REG_INT_SOURCE5 0x6A +#define REG_FIFO_LOST_PKT0 0x6C +#define REG_SELF_TEST_CONFIG 0x70 +#define REG_WHO_AM_I 0x75 +#define REG_REG_BANK_SEL 0x76 + +/* BANK 1 */ +#define REG_GYRO_CONFIG_STATIC2_B1 0x0B +#define REG_GYRO_CONFIG_STATIC3_B1 0x0C +#define REG_GYRO_CONFIG_STATIC4_B1 0x0D +#define REG_GYRO_CONFIG_STATIC5_B1 0x0E +#define REG_XG_ST_DATA_B1 0x5F +#define REG_YG_ST_DATA_B1 0x60 +#define REG_ZG_ST_DATA_B1 0x61 +#define REG_TMST_VAL0_B1 0x62 +#define REG_INTF_CONFIG4_B1 0x7A +#define REG_INTF_CONFIG5_B1 0x7B +#define REG_INTF_CONFIG6_B1 0x7C + +/* BANK 2 */ +#define REG_ACCEL_CONFIG_STATIC2_B2 0x03 +#define REG_ACCEL_CONFIG_STATIC3_B2 0x04 +#define REG_ACCEL_CONFIG_STATIC4_B2 0x05 +#define REG_ACCEL_CONFIG_STATIC0_B2 0x39 +#define REG_XA_ST_DATA_B2 0x3B +#define REG_YA_ST_DATA_B2 0x3C +#define REG_ZA_ST_DATA_B2 0x3D + +/* BANK 4 */ +#define REG_APEX_CONFIG1_B4 0x40 +#define REG_APEX_CONFIG2_B4 0x41 +#define REG_APEX_CONFIG3_B4 0x42 +#define REG_APEX_CONFIG4_B4 0x43 +#define REG_APEX_CONFIG5_B4 0x44 +#define REG_APEX_CONFIG6_B4 0x45 +#define REG_APEX_CONFIG7_B4 0x46 +#define REG_APEX_CONFIG8_B4 0x47 +#define REG_APEX_CONFIG9_B4 0x48 +#define REG_ACCEL_WOM_X_THR_B4 0x4A +#define REG_ACCEL_WOM_Y_THR_B4 0x4B +#define REG_ACCEL_WOM_Z_THR_B4 0x4C +#define REG_INT_SOURCE6_B4 0x4D +#define REG_INT_SOURCE7_B4 0x4E +#define REG_INT_SOURCE8_B4 0x4F +#define REG_INT_SOURCE9_B4 0x50 +#define REG_INT_SOURCE10_B4 0x51 +#define REG_OFFSET_USER_0_B4 0x77 +#define REG_OFFSET_USER_1_B4 0x78 +#define REG_OFFSET_USER_2_B4 0x79 +#define REG_OFFSET_USER_3_B4 0x7A +#define REG_OFFSET_USER_4_B4 0x7B +#define REG_OFFSET_USER_5_B4 0x7C +#define REG_OFFSET_USER_6_B4 0x7D +#define REG_OFFSET_USER_7_B4 0x7E +#define REG_OFFSET_USER_8_B4 0x7F + +/* Bank0 REG_SIGNAL_PATH_RESET */ +#define BIT_FIFO_FLUSH BIT(2) +#define BIT_SOFT_RESET BIT(0) + +/* Bank0 REG_INST_STATUS */ +#define BIT_STATUS_RESET_DONE_INT BIT(4) + +/* Bank0 REG_INT_CONFIG */ +#define BIT_INT1_POLARITY BIT(0) +#define BIT_INT1_DRIVE_CIRCUIT BIT(1) +#define BIT_INT1_MODE BIT(2) +#define BIT_INT2_POLARITY BIT(3) +#define BIT_INT2_DRIVE_CIRCUIT BIT(4) +#define BIT_INT2_MODE BIT(5) + +/* Bank0 REG_PWR_MGMT_0 */ +#define MASK_ACCEL_MODE GENMASK(1, 0) +#define BIT_ACCEL_MODE_OFF 0x00 +#define BIT_ACCEL_MODE_LPM 0x02 +#define BIT_ACCEL_MODE_LNM 0x03 +#define MASK_GYRO_MODE GENMASK(3, 2) +#define BIT_GYRO_MODE_OFF 0x00 +#define BIT_GYRO_MODE_STBY 0x01 +#define BIT_GYRO_MODE_LNM 0x03 +#define BIT_IDLE BIT(4) +#define BIT_ACCEL_LP_CLK_SEL BIT(7) + +/* Bank0 REG_INT_SOURCE0 */ +#define BIT_INT_AGC_RDY_INT1_EN BIT(0) +#define BIT_INT_FIFO_FULL_INT1_EN BIT(1) +#define BIT_INT_FIFO_THS_INT1_EN BIT(2) +#define BIT_INT_DRDY_INT1_EN BIT(3) +#define BIT_INT_RESET_DONE_INT1_EN BIT(4) +#define BIT_INT_PLL_RDY_INT1_EN BIT(5) +#define BIT_INT_FSYNC_INT1_EN BIT(6) +#define BIT_INT_ST_INT1_EN BIT(7) + +/* Bank0 REG_INT_STATUS_DATA_RDY_INT */ +#define BIT_INT_STATUS_DATA_RDY_INT BIT(3) + +/* Bank9 REG_INTF_CONFIG1 */ +#define BIT_ACCELL_LP_CLK_SEL BIT(3) +#define MASK_CLKSEL GENMASK(1, 0) +#define BIT_CLKSEL_INT_RC 0x00 +#define BIT_CLKSEL_PLL_OR_RC 0x01 +#define BIT_CLKSEL_DISABLE 0x11 + +/* Bank0 REG_INT_STATUS */ +#define BIT_INT_STATUS_AGC_RDY BIT(0) +#define BIT_INT_STATUS_FIFO_FULL BIT(1) +#define BIT_INT_STATUS_FIFO_THS BIT(2) +#define BIT_INT_STATUS_RESET_DONE BIT(4) +#define BIT_INT_STATUS_PLL_RDY BIT(5) +#define BIT_INT_STATUS_FSYNC BIT(6) +#define BIT_INT_STATUS_ST BIT(7) + +/* Bank0 REG_INT_STATUS2 */ +#define BIT_INT_STATUS_WOM_Z BIT(0) +#define BIT_INT_STATUS_WOM_Y BIT(1) +#define BIT_INT_STATUS_WOM_X BIT(2) +#define BIT_INT_STATUS_SMD BIT(3) + +/* Bank0 REG_INT_STATUS3 */ +#define BIT_INT_STATUS_LOWG_DET BIT(1) +#define BIT_INT_STATUS_FF_DET BIT(2) +#define BIT_INT_STATUS_TILT_DET BIT(3) +#define BIT_INT_STATUS_STEP_CNT_OVFL BIT(4) +#define BIT_INT_STATUS_STEP_DET BIT(5) + +/* Bank0 REG_ACCEL_CONFIG0 */ +#define MASK_ACCEL_UI_FS_SEL GENMASK(6, 5) +#define BIT_ACCEL_UI_FS_16 0x00 +#define BIT_ACCEL_UI_FS_8 0x01 +#define BIT_ACCEL_UI_FS_4 0x02 +#define BIT_ACCEL_UI_FS_2 0x03 +#define MASK_ACCEL_ODR GENMASK(3, 0) +#define BIT_ACCEL_ODR_8000 0x03 +#define BIT_ACCEL_ODR_4000 0x04 +#define BIT_ACCEL_ODR_2000 0x05 +#define BIT_ACCEL_ODR_1000 0x06 +#define BIT_ACCEL_ODR_200 0x07 +#define BIT_ACCEL_ODR_100 0x08 +#define BIT_ACCEL_ODR_50 0x09 +#define BIT_ACCEL_ODR_25 0x0A +#define BIT_ACCEL_ODR_12 0x0B +#define BIT_ACCEL_ODR_6 0x0C +#define BIT_ACCEL_ODR_3 0x0D +#define BIT_ACCEL_ODR_1 0x0E +#define BIT_ACCEL_ODR_500 0x0F + +/* Bank0 REG_GYRO_CONFIG0 */ +#define MASK_GYRO_UI_FS_SEL GENMASK(6, 5) +#define BIT_GYRO_UI_FS_2000 0x00 +#define BIT_GYRO_UI_FS_1000 0x01 +#define BIT_GYRO_UI_FS_500 0x02 +#define BIT_GYRO_UI_FS_250 0x03 +#define BIT_GYRO_UI_FS_125 0x04 +#define BIT_GYRO_UI_FS_62 0x05 +#define BIT_GYRO_UI_FS_31 0x06 +#define BIT_GYRO_UI_FS_15 0x07 +#define MASK_GYRO_ODR GENMASK(3, 0) +#define BIT_GYRO_ODR_8000 0x03 +#define BIT_GYRO_ODR_4000 0x04 +#define BIT_GYRO_ODR_2000 0x05 +#define BIT_GYRO_ODR_1000 0x06 +#define BIT_GYRO_ODR_200 0x07 +#define BIT_GYRO_ODR_100 0x08 +#define BIT_GYRO_ODR_50 0x09 +#define BIT_GYRO_ODR_25 0x0A +#define BIT_GYRO_ODR_12 0x0B +#define BIT_GYRO_ODR_500 0x0F + +/* misc. defines */ +#define WHO_AM_I_ICM40627 0x4E +#define MIN_ACCEL_SENS_SHIFT 10 +#define ACCEL_DATA_SIZE 6 +#define GYRO_DATA_SIZE 6 +#define TEMP_DATA_SIZE 2 +#define MCLK_POLL_INTERVAL_US 250 +#define MCLK_POLL_ATTEMPTS 100 +#define SOFT_RESET_TIME_MS 2 + +#endif /* ZEPHYR_DRIVERS_SENSOR_ICM40627_REG_H_ */ diff --git a/drivers/sensor/tdk/icm40627/icm40627_trigger.c b/drivers/sensor/tdk/icm40627/icm40627_trigger.c new file mode 100644 index 000000000000..d3f86543eef3 --- /dev/null +++ b/drivers/sensor/tdk/icm40627/icm40627_trigger.c @@ -0,0 +1,173 @@ +/* + * Copyright 2024 PHYTEC America, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "icm40627.h" +#include "icm40627_reg.h" +#include "icm40627_trigger.h" + +#include +LOG_MODULE_DECLARE(ICM40627, CONFIG_SENSOR_LOG_LEVEL); + +static void icm40627_gpio_callback(const struct device *dev, struct gpio_callback *cb, + uint32_t pins) +{ + ARG_UNUSED(dev); + ARG_UNUSED(pins); + + struct icm40627_data *data = CONTAINER_OF(cb, struct icm40627_data, gpio_cb); + +#if defined(CONFIG_ICM40627_TRIGGER_OWN_THREAD) + k_sem_give(&data->gpio_sem); +#elif defined(CONFIG_ICM40627_TRIGGER_GLOBAL_THREAD) + k_work_submit(&data->work); +#endif +} + +static void icm40627_thread_cb(const struct device *dev) +{ + struct icm40627_data *data = dev->data; + const struct icm40627_config *cfg = dev->config; + + icm40627_lock(dev); + gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_DISABLE); + + if (data->data_ready_handler) { + data->data_ready_handler(dev, data->data_ready_trigger); + } + + gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_EDGE_TO_ACTIVE); + icm40627_unlock(dev); +} + +#if defined(CONFIG_ICM40627_TRIGGER_OWN_THREAD) + +static void icm40627_thread(void *p1, void *p2, void *p3) +{ + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + struct icm40627_data *data = p1; + + while (1) { + k_sem_take(&data->gpio_sem, K_FOREVER); + icm40627_thread_cb(data->dev); + } +} + +#elif defined(CONFIG_ICM40627_TRIGGER_GLOBAL_THREAD) + +static void icm40627_work_handler(struct k_work *work) +{ + struct icm40627_data *data = CONTAINER_OF(work, struct icm40627_data, work); + + icm40627_thread_cb(data->dev); +} + +#endif + +int icm40627_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + int res = 0; + struct icm40627_data *data = dev->data; + const struct icm40627_config *cfg = dev->config; + + if (!handler) { + return -EINVAL; + } + + icm40627_lock(dev); + gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_DISABLE); + + switch (trig->type) { + case SENSOR_TRIG_DATA_READY: + data->data_ready_handler = handler; + data->data_ready_trigger = trig; + break; + default: + res = -ENOTSUP; + break; + } + + icm40627_unlock(dev); + gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_EDGE_TO_ACTIVE); + + return res; +} + +int icm40627_trigger_init(const struct device *dev) +{ + struct icm40627_data *data = dev->data; + const struct icm40627_config *cfg = dev->config; + int res = 0; + + if (!cfg->gpio_int.port) { + LOG_ERR("trigger enabled but no interrupt gpio supplied"); + return -ENODEV; + } + + if (!gpio_is_ready_dt(&cfg->gpio_int)) { + LOG_ERR("gpio_int gpio not ready"); + return -ENODEV; + } + + data->dev = dev; + gpio_pin_configure_dt(&cfg->gpio_int, GPIO_INPUT); + gpio_init_callback(&data->gpio_cb, icm40627_gpio_callback, BIT(cfg->gpio_int.pin)); + res = gpio_add_callback(cfg->gpio_int.port, &data->gpio_cb); + + if (res < 0) { + LOG_ERR("Failed to set gpio callback"); + return res; + } + + k_mutex_init(&data->mutex); + +#if defined(CONFIG_ICM40627_TRIGGER_OWN_THREAD) + k_sem_init(&data->gpio_sem, 0, K_SEM_MAX_LIMIT); + k_thread_create(&data->thread, data->thread_stack, CONFIG_ICM40627_THREAD_STACK_SIZE, + icm40627_thread, data, NULL, NULL, + K_PRIO_COOP(CONFIG_ICM40627_THREAD_PRIORITY), 0, K_NO_WAIT); +#elif defined(CONFIG_ICM40627_TRIGGER_GLOBAL_THREAD) + data->work.handler = icm40627_work_handler; +#endif + + return gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_EDGE_TO_ACTIVE); +} + +int icm40627_trigger_enable_interrupt(const struct device *dev) +{ + int res; + const struct icm40627_config *cfg = dev->config; + + /* pulse-mode (auto clearing), push-pull and active-high */ + res = cfg->bus_io->write(&cfg->bus, REG_INT_CONFIG, + BIT_INT1_DRIVE_CIRCUIT | BIT_INT1_POLARITY); + + if (res) { + return res; + } + + /* enable data ready interrupt on INT1 pin */ + return cfg->bus_io->write(&cfg->bus, REG_INT_SOURCE0, BIT_INT_DRDY_INT1_EN); +} + +void icm40627_lock(const struct device *dev) +{ + struct icm40627_data *data = dev->data; + + k_mutex_lock(&data->mutex, K_FOREVER); +} + +void icm40627_unlock(const struct device *dev) +{ + struct icm40627_data *data = dev->data; + + k_mutex_unlock(&data->mutex); +} diff --git a/drivers/sensor/tdk/icm40627/icm40627_trigger.h b/drivers/sensor/tdk/icm40627/icm40627_trigger.h new file mode 100644 index 000000000000..0c00c448cc76 --- /dev/null +++ b/drivers/sensor/tdk/icm40627/icm40627_trigger.h @@ -0,0 +1,46 @@ +/* + * Copyright 2024 PHYTEC America, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ICM40627_TRIGGER_H_ +#define ZEPHYR_DRIVERS_SENSOR_ICM40627_TRIGGER_H_ + +#include + +/** implement the trigger_set sensor api function */ +int icm40627_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler); + +/** + * @brief initialize the icm40627 trigger system + * + * @param dev icm40627 device pointer + * @return int 0 on success, negative error code otherwise + */ +int icm40627_trigger_init(const struct device *dev); + +/** + * @brief enable the trigger gpio interrupt + * + * @param dev icm40627 device pointer + * @return int 0 on success, negative error code otherwise + */ +int icm40627_trigger_enable_interrupt(const struct device *dev); + +/** + * @brief lock access to the icm40627 device driver + * + * @param dev icm40627 device pointer + */ +void icm40627_lock(const struct device *dev); + +/** + * @brief lock access to the icm40627 device driver + * + * @param dev icm40627 device pointer + */ +void icm40627_unlock(const struct device *dev); + +#endif /* ZEPHYR_DRIVERS_SENSOR_ICM40627_TRIGGER_H_ */ diff --git a/dts/bindings/sensor/invensense,icm40627-i2c.yaml b/dts/bindings/sensor/invensense,icm40627-i2c.yaml new file mode 100644 index 000000000000..eae5ef42648d --- /dev/null +++ b/dts/bindings/sensor/invensense,icm40627-i2c.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Espressif Systems (Shanghai) Co., Ltd. +# SPDX-License-Identifier: Apache-2.0 + +description: ICM-40627 motion tracking device + +compatible: "invensense,icm40627" + +include: [i2c-device.yaml, "invensense,icm40627.yaml"] diff --git a/dts/bindings/sensor/invensense,icm40627.yaml b/dts/bindings/sensor/invensense,icm40627.yaml new file mode 100644 index 000000000000..cb349af76884 --- /dev/null +++ b/dts/bindings/sensor/invensense,icm40627.yaml @@ -0,0 +1,86 @@ +# Copyright (c) 2022 Esco Medical ApS +# Copyright (c) 2020 TDK Invensense +# SPDX-License-Identifier: Apache-2.0 + +description: ICM-40627 motion tracking device + +include: [sensor-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-hz: + type: int + required: true + description: | + Default frequency of accelerometer. (Unit - Hz) + Maps to ACCEL_ODR field in ACCEL_CONFIG0 setting + Power-on reset value is 1000. + enum: + - 1 + - 3 + - 6 + - 12 + - 25 + - 50 + - 100 + - 200 + - 500 + - 1000 + - 2000 + - 4000 + - 8000 + + gyro-hz: + type: int + required: true + description: | + Default frequency of gyroscope. (Unit - Hz) + Maps to GYRO_ODR field in GYRO_CONFIG0 setting + Power-on reset value is 1000. + enum: + - 12 + - 25 + - 50 + - 100 + - 200 + - 500 + - 1000 + - 2000 + - 4000 + - 8000 + + accel-fs: + type: int + required: true + description: | + Default full scale of accelerometer. (Unit - g) + Maps to ACCEL_FS_SEL field in ACCEL_CONFIG0 setting + Power-on reset value is 16 + enum: + - 16 + - 8 + - 4 + - 2 + + gyro-fs: + type: int + required: true + description: | + Default full scale of gyroscope. (Unit - DPS) + Maps to GYRO_FS_SEL field in GYRO_CONFIG0 setting + Power-on reset value is 2000 + enum: + - 2000 + - 1000 + - 500 + - 250 + - 125 + - 62 + - 31 + - 15