blob: d2a69b7c98901915e1cafe125a989f1a44913d56 [file] [log] [blame]
/* Bosch BMI08X inertial measurement unit driver
*
* Copyright (c) 2022 Meta Platforms, Inc. and its affiliates
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/sensor.h>
#include <zephyr/pm/device.h>
#include <zephyr/init.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/byteorder.h>
#define DT_DRV_COMPAT bosch_bmi08x_accel
#include "bmi08x.h"
#include "bmi08x_config_file.h"
LOG_MODULE_REGISTER(BMI08X_ACCEL, CONFIG_SENSOR_LOG_LEVEL);
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
static int bmi08x_accel_transceive_i2c(const struct device *dev, uint8_t reg, bool write,
void *data, size_t length)
{
const struct bmi08x_accel_config *bmi08x = dev->config;
if (!write) {
return i2c_write_read_dt(&bmi08x->bus.i2c, &reg, 1, data, length);
}
if (length > CONFIG_BMI08X_I2C_WRITE_BURST_SIZE) {
return -EINVAL;
}
uint8_t buf[1 + CONFIG_BMI08X_I2C_WRITE_BURST_SIZE];
buf[0] = reg;
memcpy(&buf[1], data, length);
return i2c_write_dt(&bmi08x->bus.i2c, buf, 1 + length);
}
#if BMI08X_ACCEL_ANY_INST_HAS_DATA_SYNC
static int bmi08x_stream_transfer_write_i2c(const struct device *dev, uint16_t index,
const uint8_t *stream_data, uint16_t stream_length)
{
uint8_t asic_msb = (uint8_t)((index / 2) >> 4);
uint8_t asic_lsb = ((index / 2) & 0x0F);
int ret;
ret = bmi08x_accel_byte_write(dev, BMI08X_ACCEL_RESERVED_5B_REG, asic_lsb);
if (ret != 0) {
LOG_ERR("Cannot write index");
return ret;
}
ret = bmi08x_accel_byte_write(dev, BMI08X_ACCEL_RESERVED_5C_REG, asic_msb);
if (ret != 0) {
LOG_ERR("Cannot write index");
return ret;
}
ret = bmi08x_accel_write(dev, BMI08X_ACCEL_FEATURE_CFG_REG, (uint8_t *)stream_data,
stream_length);
if (ret != 0) {
LOG_ERR("Cannot write configuration for accelerometer.");
return ret;
}
return ret;
}
static int bmi08x_write_config_file_i2c(const struct device *dev)
{
const uint8_t *data = bmi08x_config_file;
uint16_t length = sizeof(bmi08x_config_file);
uint16_t index = 0;
int ret = 0;
while (length != 0) {
uint16_t len1 = length;
if (len1 > CONFIG_BMI08X_I2C_WRITE_BURST_SIZE) {
len1 = CONFIG_BMI08X_I2C_WRITE_BURST_SIZE;
}
ret = bmi08x_stream_transfer_write_i2c(dev, index, data, len1);
if (ret != 0) {
return ret;
}
index += len1;
data += len1;
length -= len1;
}
return ret;
}
#endif
static int bmi08x_bus_check_i2c(const union bmi08x_bus *bus)
{
return i2c_is_ready_dt(&bus->i2c) ? 0 : -ENODEV;
}
static const struct bmi08x_accel_bus_io bmi08x_i2c_api = {.check = bmi08x_bus_check_i2c,
.transceive = bmi08x_accel_transceive_i2c,
#if BMI08X_ACCEL_ANY_INST_HAS_DATA_SYNC
.write_config_file =
bmi08x_write_config_file_i2c
#endif
};
#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
static int bmi08x_accel_transceive_spi(const struct device *dev, uint8_t reg, bool write,
void *data, size_t length)
{
const struct bmi08x_accel_config *bmi08x = dev->config;
const struct spi_buf tx_buf[2] = {{.buf = &reg, .len = 1}, {.buf = data, .len = length}};
const struct spi_buf_set tx = {.buffers = tx_buf, .count = write ? 2 : 1};
if (!write) {
uint16_t dummy;
const struct spi_buf rx_buf[2] = {{.buf = &dummy, .len = 2},
{.buf = data, .len = length}};
const struct spi_buf_set rx = {.buffers = rx_buf, .count = 2};
return spi_transceive_dt(&bmi08x->bus.spi, &tx, &rx);
}
return spi_write_dt(&bmi08x->bus.spi, &tx);
}
#if BMI08X_ACCEL_ANY_INST_HAS_DATA_SYNC
static int bmi08x_write_config_file_spi(const struct device *dev)
{
int ret;
ret = bmi08x_accel_byte_write(dev, BMI08X_ACCEL_RESERVED_5B_REG, 0);
if (ret < 0) {
LOG_ERR("Cannot write index");
return ret;
}
ret = bmi08x_accel_byte_write(dev, BMI08X_ACCEL_RESERVED_5C_REG, 0);
if (ret < 0) {
LOG_ERR("Cannot write index");
return ret;
}
/* write config file */
ret = bmi08x_accel_write(dev, BMI08X_ACCEL_FEATURE_CFG_REG, (uint8_t *)bmi08x_config_file,
sizeof(bmi08x_config_file));
if (ret < 0) {
LOG_ERR("Cannot write configuration for accelerometer.");
return ret;
}
return ret;
}
#endif
static int bmi08x_bus_check_spi(const union bmi08x_bus *bus)
{
return spi_is_ready_dt(&bus->spi) ? 0 : -ENODEV;
}
static int bmi08x_bus_init_spi(const struct device *dev)
{
uint8_t val;
int ret;
/* do a dummy read from 0x7F to activate SPI */
ret = bmi08x_accel_byte_read(dev, 0x7F, &val);
if (ret < 0) {
LOG_ERR("Cannot read from 0x7F..");
return ret;
}
k_usleep(100);
return ret;
}
static const struct bmi08x_accel_bus_io bmi08x_spi_api = {.check = bmi08x_bus_check_spi,
.bus_init = bmi08x_bus_init_spi,
.transceive = bmi08x_accel_transceive_spi,
#if BMI08X_ACCEL_ANY_INST_HAS_DATA_SYNC
.write_config_file =
bmi08x_write_config_file_spi
#endif
};
#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) */
static inline int bmi08x_bus_check(const struct device *dev)
{
const struct bmi08x_accel_config *config = dev->config;
return config->api->check(&config->bus);
}
static inline int bmi08x_bus_init(const struct device *dev)
{
const struct bmi08x_accel_config *config = dev->config;
/* optional, only needed to initialize SPI according to datasheet */
if (config->api->bus_init) {
return config->api->bus_init(dev);
}
return 0;
}
static int bmi08x_accel_transceive(const struct device *dev, uint8_t reg, bool write, void *data,
size_t length)
{
const struct bmi08x_accel_config *config = dev->config;
return config->api->transceive(dev, reg, write, data, length);
}
int bmi08x_accel_read(const struct device *dev, uint8_t reg_addr, uint8_t *data, uint8_t len)
{
return bmi08x_accel_transceive(dev, reg_addr | BIT(7), false, data, len);
}
int bmi08x_accel_write(const struct device *dev, uint8_t reg_addr, uint8_t *data, uint16_t len)
{
return bmi08x_accel_transceive(dev, reg_addr, true, data, len);
}
int bmi08x_accel_byte_read(const struct device *dev, uint8_t reg_addr, uint8_t *byte)
{
return bmi08x_accel_transceive(dev, reg_addr | BIT(7), false, byte, 1);
}
static int bmi08x_accel_word_read(const struct device *dev, uint8_t reg_addr, uint16_t *word)
{
int ret;
ret = bmi08x_accel_transceive(dev, reg_addr | BIT(7), false, word, 2);
if (ret != 0) {
return ret;
}
*word = sys_le16_to_cpu(*word);
return ret;
}
int bmi08x_accel_byte_write(const struct device *dev, uint8_t reg_addr, uint8_t byte)
{
return bmi08x_accel_transceive(dev, reg_addr & 0x7F, true, &byte, 1);
}
int bmi08x_accel_word_write(const struct device *dev, uint8_t reg_addr, uint16_t word)
{
uint8_t tx_word[2] = {(uint8_t)(word & 0xff), (uint8_t)(word >> 8)};
return bmi08x_accel_transceive(dev, reg_addr & 0x7F, true, tx_word, 2);
}
int bmi08x_accel_reg_field_update(const struct device *dev, uint8_t reg_addr, uint8_t pos,
uint8_t mask, uint8_t val)
{
uint8_t old_val;
int ret;
ret = bmi08x_accel_byte_read(dev, reg_addr, &old_val);
if (ret < 0) {
return ret;
}
return bmi08x_accel_byte_write(dev, reg_addr, (old_val & ~mask) | ((val << pos) & mask));
}
static int bmi08x_acc_odr_set(const struct device *dev, uint16_t freq_int, uint16_t freq_milli)
{
int odr = bmi08x_freq_to_odr_val(freq_int, freq_milli);
if (odr < BMI08X_ACCEL_ODR_12_5_HZ) {
return odr;
}
return bmi08x_accel_reg_field_update(dev, BMI08X_REG_ACCEL_CONF, 0, BMI08X_ACCEL_ODR_MASK,
(uint8_t)odr);
}
static const struct bmi08x_range bmi085_acc_range_map[] = {
{2, BMI085_ACCEL_RANGE_2G},
{4, BMI085_ACCEL_RANGE_4G},
{8, BMI085_ACCEL_RANGE_8G},
{16, BMI085_ACCEL_RANGE_16G},
};
#define BMI085_ACC_RANGE_MAP_SIZE ARRAY_SIZE(bmi085_acc_range_map)
static const struct bmi08x_range bmi088_acc_range_map[] = {
{3, BMI088_ACCEL_RANGE_3G},
{6, BMI088_ACCEL_RANGE_6G},
{12, BMI088_ACCEL_RANGE_12G},
{24, BMI088_ACCEL_RANGE_24G},
};
#define BMI088_ACC_RANGE_MAP_SIZE ARRAY_SIZE(bmi088_acc_range_map)
static int bmi08x_acc_range_set(const struct device *dev, int32_t range)
{
struct bmi08x_accel_data *data = dev->data;
int32_t reg_val = -1;
int ret;
if (data->accel_chip_id == BMI085_ACCEL_CHIP_ID) {
reg_val = bmi08x_range_to_reg_val(range, bmi085_acc_range_map,
BMI085_ACC_RANGE_MAP_SIZE);
} else if (data->accel_chip_id == BMI088_ACCEL_CHIP_ID) {
reg_val = bmi08x_range_to_reg_val(range, bmi088_acc_range_map,
BMI088_ACC_RANGE_MAP_SIZE);
} else {
return -ENODEV;
}
if (reg_val < 0) {
return reg_val;
}
ret = bmi08x_accel_byte_write(dev, BMI08X_REG_ACCEL_RANGE, reg_val & 0xff);
if (ret < 0) {
return ret;
}
data->scale = BMI08X_ACC_SCALE(range);
return ret;
}
static int bmi08x_acc_config(const struct device *dev, enum sensor_channel chan,
enum sensor_attribute attr, const struct sensor_value *val)
{
switch (attr) {
case SENSOR_ATTR_FULL_SCALE:
return bmi08x_acc_range_set(dev, sensor_ms2_to_g(val));
case SENSOR_ATTR_SAMPLING_FREQUENCY:
return bmi08x_acc_odr_set(dev, val->val1, val->val2 / 1000);
default:
LOG_DBG("Accel attribute not supported.");
return -ENOTSUP;
}
}
static int bmi08x_attr_set(const struct device *dev, enum sensor_channel chan,
enum sensor_attribute attr, const struct sensor_value *val)
{
#ifdef CONFIG_PM_DEVICE
enum pm_device_state state;
(void)pm_device_state_get(dev, &state);
if (state != PM_DEVICE_STATE_ACTIVE) {
return -EBUSY;
}
#endif
switch (chan) {
case SENSOR_CHAN_ACCEL_X:
case SENSOR_CHAN_ACCEL_Y:
case SENSOR_CHAN_ACCEL_Z:
case SENSOR_CHAN_ACCEL_XYZ:
return bmi08x_acc_config(dev, chan, attr, val);
default:
LOG_DBG("attr_set() not supported on this channel.");
return -ENOTSUP;
}
}
static int bmi08x_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
struct bmi08x_accel_data *data = dev->data;
size_t i;
int ret;
if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_ACCEL_XYZ) {
LOG_DBG("Unsupported sensor channel");
return -ENOTSUP;
}
#ifdef CONFIG_PM_DEVICE
enum pm_device_state state;
(void)pm_device_state_get(dev, &state);
if (state != PM_DEVICE_STATE_ACTIVE) {
return -EBUSY;
}
#endif
pm_device_busy_set(dev);
ret = bmi08x_accel_read(dev, BMI08X_REG_ACCEL_X_LSB, (uint8_t *)data->acc_sample,
sizeof(data->acc_sample));
if (ret < 0) {
pm_device_busy_clear(dev);
return ret;
}
/* convert samples to cpu endianness */
for (i = 0; i < ARRAY_SIZE(data->acc_sample); i++) {
data->acc_sample[i] = sys_le16_to_cpu(data->acc_sample[i]);
}
pm_device_busy_clear(dev);
return ret;
}
static void bmi08x_to_fixed_point(int16_t raw_val, uint16_t scale, struct sensor_value *val)
{
int32_t converted_val;
/*
* maximum converted value we can get is: max(raw_val) * max(scale)
* max(raw_val) = +/- 2^15
* max(scale) = 4785
* max(converted_val) = 156794880 which is less than 2^31
*/
converted_val = raw_val * scale;
val->val1 = converted_val / 1000000;
val->val2 = converted_val % 1000000;
}
static void bmi08x_channel_convert(enum sensor_channel chan, uint16_t scale, uint16_t *raw_xyz,
struct sensor_value *val)
{
int i;
uint8_t ofs_start, ofs_stop;
switch (chan) {
case SENSOR_CHAN_ACCEL_X:
ofs_start = ofs_stop = 0U;
break;
case SENSOR_CHAN_ACCEL_Y:
ofs_start = ofs_stop = 1U;
break;
case SENSOR_CHAN_ACCEL_Z:
ofs_start = ofs_stop = 2U;
break;
default:
ofs_start = 0U;
ofs_stop = 2U;
break;
}
for (i = ofs_start; i <= ofs_stop; i++, val++) {
bmi08x_to_fixed_point(raw_xyz[i], scale, val);
}
}
static inline void bmi08x_acc_channel_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
struct bmi08x_accel_data *data = dev->data;
bmi08x_channel_convert(chan, data->scale, data->acc_sample, val);
}
static int bmi08x_temp_channel_get(const struct device *dev, struct sensor_value *val)
{
uint16_t temp_raw = 0U;
int32_t temp_micro = 0;
int ret;
ret = bmi08x_accel_word_read(dev, BMI08X_REG_TEMP_MSB, &temp_raw);
if (ret < 0) {
return ret;
}
/* the scale is 1/2^5/LSB = 31250 micro degrees */
temp_micro = BMI08X_TEMP_OFFSET * 1000000ULL + temp_raw * 31250ULL;
val->val1 = temp_micro / 1000000ULL;
val->val2 = temp_micro % 1000000ULL;
return ret;
}
static int bmi08x_channel_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
#ifdef CONFIG_PM_DEVICE
enum pm_device_state state;
(void)pm_device_state_get(dev, &state);
if (state != PM_DEVICE_STATE_ACTIVE) {
return -EBUSY;
}
#endif
switch ((int16_t)chan) {
case SENSOR_CHAN_ACCEL_X:
case SENSOR_CHAN_ACCEL_Y:
case SENSOR_CHAN_ACCEL_Z:
case SENSOR_CHAN_ACCEL_XYZ:
bmi08x_acc_channel_get(dev, chan, val);
return 0;
case SENSOR_CHAN_DIE_TEMP:
return bmi08x_temp_channel_get(dev, val);
default:
LOG_DBG("Channel not supported.");
return -ENOTSUP;
}
return 0;
}
#ifdef CONFIG_PM_DEVICE
static int bmi08x_accel_pm_action(const struct device *dev, enum pm_device_action action)
{
uint8_t conf_reg_val;
uint8_t ctrl_reg_val;
int ret;
switch (action) {
case PM_DEVICE_ACTION_RESUME:
conf_reg_val = BMI08X_ACCEL_PM_ACTIVE;
ctrl_reg_val = BMI08X_ACCEL_POWER_ENABLE;
break;
case PM_DEVICE_ACTION_SUSPEND:
conf_reg_val = BMI08X_ACCEL_PM_SUSPEND;
ctrl_reg_val = BMI08X_ACCEL_POWER_DISABLE;
break;
default:
return -ENOTSUP;
}
ret = bmi08x_accel_byte_write(dev, BMI08X_REG_ACCEL_PWR_CONF, conf_reg_val);
if (ret < 0) {
LOG_ERR("Failed to set conf power mode");
return ret;
}
k_msleep(BMI08X_POWER_CONFIG_DELAY);
ret = bmi08x_accel_byte_write(dev, BMI08X_REG_ACCEL_PWR_CTRL, ctrl_reg_val);
if (ret < 0) {
LOG_ERR("Failed to set ctrl power mode");
return ret;
}
k_msleep(BMI08X_POWER_CONFIG_DELAY);
return ret;
}
#endif /* CONFIG_PM_DEVICE */
static const struct sensor_driver_api bmi08x_api = {
.attr_set = bmi08x_attr_set,
#ifdef CONFIG_BMI08X_ACCEL_TRIGGER
.trigger_set = bmi08x_trigger_set_acc,
#endif
.sample_fetch = bmi08x_sample_fetch,
.channel_get = bmi08x_channel_get,
};
#if BMI08X_ACCEL_ANY_INST_HAS_DATA_SYNC
static int bmi08x_apply_sync_binary_config(const struct device *dev)
{
const struct bmi08x_accel_config *config = dev->config;
int ret;
ret = bmi08x_accel_byte_write(dev, BMI08X_REG_ACCEL_PWR_CONF, BMI08X_ACCEL_PM_ACTIVE);
if (ret < 0) {
LOG_ERR("Cannot deactivate advanced power save mode.");
return ret;
}
/* required when switching power modes */
k_msleep(BMI08X_POWER_CONFIG_DELAY);
/* deactivate accel, otherwise post processing can not be enabled safely */
ret = bmi08x_accel_byte_write(dev, BMI08X_REG_ACCEL_PWR_CTRL, BMI08X_ACCEL_POWER_DISABLE);
if (ret < 0) {
LOG_ERR("Cannot deactivate accel.");
return ret;
}
/* required when switching power modes */
k_msleep(BMI08X_POWER_CONFIG_DELAY);
/* disable config loading */
ret = bmi08x_accel_byte_write(dev, BMI08X_REG_ACCEL_INIT_CTRL,
BMI08X_ACCEL_INIT_CTRL_DISABLE);
if (ret < 0) {
LOG_ERR("Cannot disable config loading.");
return ret;
}
if (config->api->write_config_file(dev) != 0) {
LOG_ERR("Cannot write configuration for accelerometer.");
return -EIO;
}
k_msleep(5U);
ret = bmi08x_accel_byte_write(dev, BMI08X_REG_ACCEL_INIT_CTRL,
BMI08X_ACCEL_INIT_CTRL_ENABLE);
if (ret < 0) {
LOG_ERR("Cannot write configuration for accelerometer.");
return ret;
}
k_msleep(BMI08X_ASIC_INIT_TIME_MS);
/* check config initialization status */
uint8_t val;
ret = bmi08x_accel_byte_read(dev, BMI08X_REG_ACCEL_INTERNAL_STAT, &val);
if (ret < 0) {
LOG_ERR("Cannot write configuration for accelerometer.");
return ret;
}
if (val != 1) {
LOG_ERR("Configuration stream error.");
return -EIO;
}
/* write feature configuration */
uint8_t fdata[8];
ret = bmi08x_accel_read(dev, BMI08X_ACCEL_FEATURE_CFG_REG, fdata, 6);
if (ret < 0) {
LOG_ERR("Cannot read configuration for accelerometer.");
return ret;
}
fdata[4] = config->data_sync;
fdata[5] = 0x00;
ret = bmi08x_accel_write(dev, BMI08X_ACCEL_FEATURE_CFG_REG, fdata, 6);
if (ret < 0) {
LOG_ERR("Cannot write configuration for accelerometer.");
return ret;
}
k_msleep(100U);
ret = bmi08x_accel_byte_write(dev, BMI08X_REG_ACCEL_PWR_CTRL, BMI08X_ACCEL_POWER_ENABLE);
if (ret < 0) {
LOG_ERR("Cannot activate accel.");
return ret;
}
/* required when switching power modes */
k_msleep(BMI08X_POWER_CONFIG_DELAY);
return ret;
}
#endif
int bmi08x_accel_init(const struct device *dev)
{
const struct bmi08x_accel_config *config = dev->config;
struct bmi08x_accel_data *data = dev->data;
uint8_t val = 0U;
int ret;
ret = bmi08x_bus_check(dev);
if (ret < 0) {
LOG_ERR("Bus not ready for '%s'", dev->name);
return ret;
}
/* reboot the chip */
ret = bmi08x_accel_byte_write(dev, BMI08X_REG_ACCEL_SOFTRESET, BMI08X_SOFT_RESET_CMD);
if (ret < 0) {
LOG_ERR("Cannot reboot chip.");
return ret;
}
k_msleep(BMI08X_ACCEL_SOFTRESET_DELAY_MS);
ret = bmi08x_bus_init(dev);
if (ret < 0) {
LOG_ERR("Can't initialize bus for %s", dev->name);
return ret;
}
ret = bmi08x_accel_byte_read(dev, BMI08X_REG_ACCEL_CHIP_ID, &val);
if (ret < 0) {
LOG_ERR("Failed to read chip id.");
return ret;
}
if ((val != BMI085_ACCEL_CHIP_ID) && (val != BMI088_ACCEL_CHIP_ID)) {
LOG_ERR("Unsupported chip detected (0x%02x)!", val);
return -ENODEV;
}
data->accel_chip_id = val;
/* enable power */
ret = bmi08x_accel_byte_write(dev, BMI08X_REG_ACCEL_PWR_CONF, BMI08X_ACCEL_PM_ACTIVE);
if (ret < 0) {
LOG_ERR("Failed to set conf power mode");
return ret;
}
k_msleep(BMI08X_POWER_CONFIG_DELAY);
ret = bmi08x_accel_byte_write(dev, BMI08X_REG_ACCEL_PWR_CTRL, BMI08X_ACCEL_POWER_ENABLE);
if (ret < 0) {
LOG_ERR("Failed to set ctrl power mode");
return ret;
}
k_msleep(BMI08X_POWER_CONFIG_DELAY);
#if BMI08X_ACCEL_ANY_INST_HAS_DATA_SYNC
if (config->data_sync != 0) {
ret = bmi08x_apply_sync_binary_config(dev);
if (ret < 0) {
return ret;
}
}
#endif
/* set accelerometer default range, divide by two because the dts contains both bmi085 and
* bmi088 valid values even values in the enum are for the bmi085 and odd values are for the
* bmi088
*/
ret = bmi08x_acc_range_set(dev, config->accel_fs);
if (ret < 0) {
LOG_ERR("Cannot set default range for accelerometer.");
return ret;
}
/* set accelerometer default odr */
/* add 5 to offset from the dts enum */
ret = bmi08x_accel_reg_field_update(dev, BMI08X_REG_ACCEL_CONF, 0, BMI08X_ACCEL_ODR_MASK,
config->accel_hz);
if (ret < 0) {
LOG_ERR("Failed to set accel's default ODR.");
return ret;
}
#ifdef CONFIG_BMI08X_ACCEL_TRIGGER
ret = bmi08x_acc_trigger_mode_init(dev);
if (ret < 0) {
LOG_ERR("Cannot set up trigger mode.");
return ret;
}
#endif
return ret;
}
#define BMI08X_CONFIG_SPI(inst) \
.bus.spi = SPI_DT_SPEC_INST_GET( \
inst, SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8), 2),
#define BMI08X_CONFIG_I2C(inst) .bus.i2c = I2C_DT_SPEC_INST_GET(inst),
#define BMI08X_ACCEL_TRIG(inst) \
.int1_map = DT_INST_PROP(inst, int1_map_io), .int2_map = DT_INST_PROP(inst, int2_map_io), \
.int1_conf_io = DT_INST_PROP(inst, int1_conf_io), \
.int2_conf_io = DT_INST_PROP(inst, int2_conf_io),
/* verify the bmi08x-accel is paired with a bmi08x-gyro */
#define BMI08X_VERIFY_DATA_SYNC(inst) \
BUILD_ASSERT(DT_NODE_HAS_COMPAT(DT_INST_PHANDLE(inst, data_sync), bosch_bmi08x_gyro) != 0, \
"bmi08x-accel data sync not paired with a bmi08x-gyro")
/*
* verify data sync odr, the only valid odr combinitions with the gyro are
* (gyro-hz == "400_47" and accel-hz == "400") or (gyro-hz == "1000_116" and accel-hz == "800")
* or ((gyro-hz == "2000_230" or gyro-hz == "2000_532") and accel-hz == "1600")
*/
#define BMI08X_GYRO_ODR(inst) DT_ENUM_IDX(DT_INST_PHANDLE(inst, data_sync), gyro_hz)
#define BMI08X_ACCEL_ODR(inst) DT_INST_ENUM_IDX(inst, accel_hz)
/* As the dts uses strings to define the definition, ints must be used for comparision */
#define BMI08X_VERIFY_DATA_SYNC_ODR(inst) \
BUILD_ASSERT((BMI08X_GYRO_ODR(inst) == 3 && BMI08X_ACCEL_ODR(inst) == 5) || \
(BMI08X_GYRO_ODR(inst) == 2 && BMI08X_ACCEL_ODR(inst) == 6) || \
((BMI08X_GYRO_ODR(inst) == 1 || BMI08X_GYRO_ODR(inst) == 0) && \
BMI08X_ACCEL_ODR(inst) == 7), \
"Invalid gyro and accel odr for data-sync")
/* Assert if the gyro does not have data-sync enabled */
#define BMI08X_VERIFY_GYRO_DATA_SYNC_EN(inst) \
BUILD_ASSERT(DT_PROP(DT_INST_PHANDLE(inst, data_sync), data_sync), \
"paired bmi08x-gyro does not have data-sync enabled")
/* infer the data-sync value from the gyro and accel odr 2000=1, 1000=2, 400=3, otherwise it is 0 if
* it is not enabled. the build_assert should prevent any invalid values when it is enabled
*/
#define BMI08X_DATA_SYNC_REG_VAL(inst) \
(BMI08X_GYRO_ODR(inst) == 3 && BMI08X_ACCEL_ODR(inst) == 5) ? 3 \
: (BMI08X_GYRO_ODR(inst) == 2 && BMI08X_ACCEL_ODR(inst) == 6) ? 2 \
: ((BMI08X_GYRO_ODR(inst) == 1 || BMI08X_GYRO_ODR(inst) == 0) && \
BMI08X_ACCEL_ODR(inst) == 7) \
? 1 \
: 0
/* define the .data_sync in the driver config */
#if BMI08X_ACCEL_ANY_INST_HAS_DATA_SYNC
/* if another bmi08x as the data sync enabled, and one doesn't, it will get the value of 0 and won't
* have the config file sent over
*/
#define BMI08X_DATA_SYNC_REG(inst) \
.data_sync = COND_CODE_1(BMI08X_ACCEL_DATA_SYNC_EN(inst), \
(BMI08X_DATA_SYNC_REG_VAL(inst)), (0)),
#define BMI08X_ACCEL_TRIGGER_PINS(inst) BMI08X_ACCEL_TRIG(inst)
#else
#define BMI08X_DATA_SYNC_REG(inst)
#define BMI08X_ACCEL_TRIGGER_PINS(inst) \
IF_ENABLED(CONFIG_BMI08X_ACCEL_TRIGGER, (BMI08X_ACCEL_TRIG(inst)))
#endif
#define BMI08X_CREATE_INST(inst) \
\
IF_ENABLED(BMI08X_ACCEL_DATA_SYNC_EN(inst), (BMI08X_VERIFY_DATA_SYNC(inst);)) \
IF_ENABLED(BMI08X_ACCEL_DATA_SYNC_EN(inst), (BMI08X_VERIFY_DATA_SYNC_ODR(inst);)) \
IF_ENABLED(BMI08X_ACCEL_DATA_SYNC_EN(inst), (BMI08X_VERIFY_GYRO_DATA_SYNC_EN(inst);)) \
\
static struct bmi08x_accel_data bmi08x_drv_##inst; \
\
static const struct bmi08x_accel_config bmi08x_config_##inst = { \
COND_CODE_1(DT_INST_ON_BUS(inst, spi), (BMI08X_CONFIG_SPI(inst)), \
(BMI08X_CONFIG_I2C(inst))) \
.api = COND_CODE_1(DT_INST_ON_BUS(inst, spi), (&bmi08x_spi_api), \
(&bmi08x_i2c_api)), \
IF_ENABLED(CONFIG_BMI08X_ACCEL_TRIGGER, \
(.int_gpio = GPIO_DT_SPEC_INST_GET(inst, int_gpios),)) \
BMI08X_ACCEL_TRIGGER_PINS(inst) \
.accel_hz = DT_INST_ENUM_IDX(inst, accel_hz) + 5, \
.accel_fs = DT_INST_PROP(inst, accel_fs), BMI08X_DATA_SYNC_REG(inst)}; \
\
PM_DEVICE_DT_INST_DEFINE(inst, bmi08x_accel_pm_action); \
SENSOR_DEVICE_DT_INST_DEFINE(inst, bmi08x_accel_init, PM_DEVICE_DT_INST_GET(inst), \
&bmi08x_drv_##inst, &bmi08x_config_##inst, POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, &bmi08x_api);
/* Create the struct device for every status "okay" node in the devicetree. */
DT_INST_FOREACH_STATUS_OKAY(BMI08X_CREATE_INST)