/* * Copyright (c) 2022 Esco Medical ApS * Copyright (c) 2020 TDK Invensense * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT invensense_icm42670 #include #include #include #include "icm42670.h" #include "icm42670_reg.h" #include "icm42670_spi.h" #include "icm42670_trigger.h" #include LOG_MODULE_REGISTER(ICM42670, CONFIG_SENSOR_LOG_LEVEL); /* * Gyro FS to scaling factor mapping. * See datasheet section 3.1 for details */ static const uint16_t icm42670_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 */ }; static int icm42670_set_accel_fs(const struct device *dev, uint16_t fs) { const struct icm42670_config *cfg = dev->config; struct icm42670_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 + temp; return icm42670_spi_update_register(&cfg->spi, REG_ACCEL_CONFIG0, (uint8_t)MASK_ACCEL_UI_FS_SEL, temp); } static int icm42670_set_gyro_fs(const struct device *dev, uint16_t fs) { const struct icm42670_config *cfg = dev->config; struct icm42670_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 { temp = BIT_GYRO_UI_FS_250; } data->gyro_sensitivity_x10 = icm42670_gyro_sensitivity_x10[temp]; return icm42670_spi_update_register(&cfg->spi, REG_GYRO_CONFIG0, (uint8_t)MASK_GYRO_UI_FS_SEL, temp); } static int icm42670_set_accel_odr(const struct device *dev, uint16_t rate) { const struct icm42670_config *cfg = dev->config; uint8_t temp; if ((rate > 1600) || (rate < 1)) { LOG_ERR("Unsupported frequency"); return -ENOTSUP; } if (rate > 800) { temp = BIT_ACCEL_ODR_1600; } else if (rate > 400) { temp = BIT_ACCEL_ODR_800; } else if (rate > 200) { temp = BIT_ACCEL_ODR_400; } 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 icm42670_spi_update_register(&cfg->spi, REG_ACCEL_CONFIG0, (uint8_t)MASK_ACCEL_ODR, temp); } static int icm42670_set_gyro_odr(const struct device *dev, uint16_t rate) { const struct icm42670_config *cfg = dev->config; uint8_t temp; if ((rate > 1600) || (rate < 12)) { LOG_ERR("Unsupported frequency"); return -ENOTSUP; } if (rate > 800) { temp = BIT_GYRO_ODR_1600; } else if (rate > 400) { temp = BIT_GYRO_ODR_800; } else if (rate > 200) { temp = BIT_GYRO_ODR_400; } 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 icm42670_spi_update_register(&cfg->spi, REG_GYRO_CONFIG0, (uint8_t)MASK_GYRO_ODR, temp); } static int icm42670_enable_mclk(const struct device *dev) { const struct icm42670_config *cfg = dev->config; /* switch on MCLK by setting the IDLE bit */ int res = icm42670_spi_single_write(&cfg->spi, REG_PWR_MGMT0, BIT_IDLE); if (res) { return res; } /* wait for the MCLK to stabilize by polling MCLK_RDY register */ for (int i = 0; i < MCLK_POLL_ATTEMPTS; i++) { uint8_t value = 0; k_usleep(MCLK_POLL_INTERVAL_US); res = icm42670_spi_read(&cfg->spi, REG_MCLK_RDY, &value, 1); if (res) { return res; } if (FIELD_GET(BIT_MCLK_RDY, value)) { return 0; } } return -EIO; } static int icm42670_sensor_init(const struct device *dev) { int res; uint8_t value; const struct icm42670_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 = icm42670_spi_single_write(&cfg->spi, REG_SIGNAL_PATH_RESET, 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); /* force SPI-4w hardware configuration (so that next read is correct) */ res = icm42670_spi_single_write(&cfg->spi, REG_DEVICE_CONFIG, BIT_SPI_AP_4WIRE); if (res) { return res; } /* always use internal RC oscillator */ res = icm42670_spi_single_write(&cfg->spi, REG_INTF_CONFIG1, (uint8_t)FIELD_PREP(MASK_CLKSEL, BIT_CLKSEL_INT_RC)); if (res) { return res; } /* clear reset done int flag */ res = icm42670_spi_read(&cfg->spi, 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; } /* enable the master clock to ensure proper operation */ res = icm42670_enable_mclk(dev); if (res) { return res; } res = icm42670_spi_read(&cfg->spi, REG_WHO_AM_I, &value, 1); if (res) { return res; } if (value != WHO_AM_I_ICM42670) { LOG_ERR("invalid WHO_AM_I value, was %i but expected %i", value, WHO_AM_I_ICM42670); return -EINVAL; } LOG_DBG("device id: 0x%02X", value); return 0; } static int icm42670_turn_on_sensor(const struct device *dev) { struct icm42670_data *data = dev->data; const struct icm42670_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 = icm42670_spi_update_register(&cfg->spi, REG_PWR_MGMT0, (uint8_t)(MASK_ACCEL_MODE | MASK_GYRO_MODE), value); if (res) { return res; } res = icm42670_set_accel_fs(dev, data->accel_fs); if (res) { return res; } res = icm42670_set_accel_odr(dev, data->accel_hz); if (res) { return res; } res = icm42670_set_gyro_fs(dev, data->gyro_fs); if (res) { return res; } res = icm42670_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 icm42670_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 icm42670_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 icm42670_convert_temp(struct sensor_value *val, int16_t raw_val) { /* see datasheet section 15.9 for details */ val->val1 = (((int64_t)raw_val * 100) / 12800) + 25; val->val2 = ((((int64_t)raw_val * 100) % 12800) * 1000000) / 12800; if (val->val2 < 0) { val->val1--; val->val2 += 1000000; } else if (val->val2 >= 1000000) { val->val1++; val->val2 -= 1000000; } } static int icm42670_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) { int res = 0; const struct icm42670_data *data = dev->data; icm42670_lock(dev); switch (chan) { case SENSOR_CHAN_ACCEL_XYZ: icm42670_convert_accel(&val[0], data->accel_x, data->accel_sensitivity_shift); icm42670_convert_accel(&val[1], data->accel_y, data->accel_sensitivity_shift); icm42670_convert_accel(&val[2], data->accel_z, data->accel_sensitivity_shift); break; case SENSOR_CHAN_ACCEL_X: icm42670_convert_accel(val, data->accel_x, data->accel_sensitivity_shift); break; case SENSOR_CHAN_ACCEL_Y: icm42670_convert_accel(val, data->accel_y, data->accel_sensitivity_shift); break; case SENSOR_CHAN_ACCEL_Z: icm42670_convert_accel(val, data->accel_z, data->accel_sensitivity_shift); break; case SENSOR_CHAN_GYRO_XYZ: icm42670_convert_gyro(&val[0], data->gyro_x, data->gyro_sensitivity_x10); icm42670_convert_gyro(&val[1], data->gyro_y, data->gyro_sensitivity_x10); icm42670_convert_gyro(&val[2], data->gyro_z, data->gyro_sensitivity_x10); break; case SENSOR_CHAN_GYRO_X: icm42670_convert_gyro(val, data->gyro_x, data->gyro_sensitivity_x10); break; case SENSOR_CHAN_GYRO_Y: icm42670_convert_gyro(val, data->gyro_y, data->gyro_sensitivity_x10); break; case SENSOR_CHAN_GYRO_Z: icm42670_convert_gyro(val, data->gyro_z, data->gyro_sensitivity_x10); break; case SENSOR_CHAN_DIE_TEMP: icm42670_convert_temp(val, data->temp); break; default: res = -ENOTSUP; break; } icm42670_unlock(dev); return res; } static int icm42670_sample_fetch_accel(const struct device *dev) { const struct icm42670_config *cfg = dev->config; struct icm42670_data *data = dev->data; uint8_t buffer[ACCEL_DATA_SIZE]; int res = icm42670_spi_read(&cfg->spi, 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 icm42670_sample_fetch_gyro(const struct device *dev) { const struct icm42670_config *cfg = dev->config; struct icm42670_data *data = dev->data; uint8_t buffer[GYRO_DATA_SIZE]; int res = icm42670_spi_read(&cfg->spi, 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 icm42670_sample_fetch_temp(const struct device *dev) { const struct icm42670_config *cfg = dev->config; struct icm42670_data *data = dev->data; uint8_t buffer[TEMP_DATA_SIZE]; int res = icm42670_spi_read(&cfg->spi, REG_TEMP_DATA1, buffer, TEMP_DATA_SIZE); if (res) { return res; } data->temp = (int16_t)sys_get_be16(&buffer[0]); return 0; } static int icm42670_sample_fetch(const struct device *dev, enum sensor_channel chan) { uint8_t status; const struct icm42670_config *cfg = dev->config; icm42670_lock(dev); int res = icm42670_spi_read(&cfg->spi, REG_INT_STATUS_DRDY, &status, 1); if (res) { goto cleanup; } if (!FIELD_GET(BIT_INT_STATUS_DATA_DRDY, status)) { res = -EBUSY; goto cleanup; } switch (chan) { case SENSOR_CHAN_ALL: res |= icm42670_sample_fetch_accel(dev); res |= icm42670_sample_fetch_gyro(dev); res |= icm42670_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 = icm42670_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 = icm42670_sample_fetch_gyro(dev); break; case SENSOR_CHAN_DIE_TEMP: res = icm42670_sample_fetch_temp(dev); break; default: res = -ENOTSUP; break; } cleanup: icm42670_unlock(dev); return res; } static int icm42670_attr_set(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val) { int res = 0; struct icm42670_data *data = dev->data; __ASSERT_NO_MSG(val != NULL); icm42670_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 = icm42670_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 = icm42670_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 = icm42670_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 = icm42670_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; } icm42670_unlock(dev); return res; } static int icm42670_attr_get(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, struct sensor_value *val) { const struct icm42670_data *data = dev->data; int res = 0; __ASSERT_NO_MSG(val != NULL); icm42670_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; } icm42670_unlock(dev); return res; } static int icm42670_init(const struct device *dev) { struct icm42670_data *data = dev->data; const struct icm42670_config *cfg = dev->config; if (!spi_is_ready_dt(&cfg->spi)) { LOG_ERR("SPI 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 (icm42670_sensor_init(dev)) { LOG_ERR("could not initialize sensor"); return -EIO; } #ifdef CONFIG_ICM42670_TRIGGER if (icm42670_trigger_init(dev)) { LOG_ERR("Failed to initialize interrupts."); return -EIO; } #endif int res = icm42670_turn_on_sensor(dev); #ifdef CONFIG_ICM42670_TRIGGER if (icm42670_trigger_enable_interrupt(dev)) { LOG_ERR("Failed to enable interrupts"); return -EIO; } #endif return res; } #ifndef CONFIG_ICM42670_TRIGGER void icm42670_lock(const struct device *dev) { ARG_UNUSED(dev); } void icm42670_unlock(const struct device *dev) { ARG_UNUSED(dev); } #endif static const struct sensor_driver_api icm42670_driver_api = { #ifdef CONFIG_ICM42670_TRIGGER .trigger_set = icm42670_trigger_set, #endif .sample_fetch = icm42670_sample_fetch, .channel_get = icm42670_channel_get, .attr_set = icm42670_attr_set, .attr_get = icm42670_attr_get, }; /* device defaults to spi mode 0/3 support */ #define ICM42670_SPI_CFG \ SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_WORD_SET(8) | SPI_TRANSFER_MSB #define ICM42670_INIT(inst) \ static struct icm42670_data icm42670_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 icm42670_config icm42670_cfg_##inst = { \ .spi = SPI_DT_SPEC_INST_GET(inst, ICM42670_SPI_CFG, 0U), \ .gpio_int = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, { 0 }), \ }; \ \ SENSOR_DEVICE_DT_INST_DEFINE(inst, icm42670_init, NULL, &icm42670_driver_##inst, \ &icm42670_cfg_##inst, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ &icm42670_driver_api); DT_INST_FOREACH_STATUS_OKAY(ICM42670_INIT)