/* 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)
