blob: 34f64b76d84b77e86591242d4c939d3441cb1839 [file] [log] [blame]
/*
* Copyright (c) 2022 Esco Medical ApS
* Copyright (c) 2020 TDK Invensense
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT invensense_icm42670
#include <zephyr/drivers/sensor.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/sys/byteorder.h>
#include "icm42670.h"
#include "icm42670_reg.h"
#include "icm42670_spi.h"
#include "icm42670_trigger.h"
#include <zephyr/logging/log.h>
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)