/*
 * Copyright (c) 2023 Trackunit Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include "bmi323.h"
#include "bmi323_spi.h"

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/device_runtime.h>

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bosch_bmi323);

#define DT_DRV_COMPAT bosch_bmi323

/* Value taken from BMI323 Datasheet section 5.8.1 */
#define IMU_BOSCH_FEATURE_ENGINE_STARTUP_CONFIG (0x012C)

#define IMU_BOSCH_DIE_TEMP_OFFSET_MICRO_DEG_CELCIUS (23000000LL)
#define IMU_BOSCH_DIE_TEMP_MICRO_DEG_CELCIUS_LSB    (1953L)

typedef void (*bosch_bmi323_gpio_callback_ptr)(const struct device *dev, struct gpio_callback *cb,
					       uint32_t pins);

struct bosch_bmi323_config {
	const struct bosch_bmi323_bus *bus;
	const struct gpio_dt_spec int_gpio;

	const bosch_bmi323_gpio_callback_ptr int_gpio_callback;
};

struct bosch_bmi323_data {
	struct k_mutex lock;

	struct sensor_value acc_samples[3];
	struct sensor_value gyro_samples[3];
	struct sensor_value temperature;

	bool acc_samples_valid;
	bool gyro_samples_valid;
	bool temperature_valid;

	uint32_t acc_full_scale;
	uint32_t gyro_full_scale;

	struct gpio_callback gpio_callback;
	const struct sensor_trigger *trigger;
	sensor_trigger_handler_t trigger_handler;
	struct k_work callback_work;
	const struct device *dev;
};

static int bosch_bmi323_bus_init(const struct device *dev)
{
	const struct bosch_bmi323_config *config = (const struct bosch_bmi323_config *)dev->config;

	const struct bosch_bmi323_bus *bus = config->bus;

	return bus->api->init(bus->context);
}

static int bosch_bmi323_bus_read_words(const struct device *dev, uint8_t offset, uint16_t *words,
				       uint16_t words_count)
{
	const struct bosch_bmi323_config *config = (const struct bosch_bmi323_config *)dev->config;

	const struct bosch_bmi323_bus *bus = config->bus;

	return bus->api->read_words(bus->context, offset, words, words_count);
}

static int bosch_bmi323_bus_write_words(const struct device *dev, uint8_t offset, uint16_t *words,
					uint16_t words_count)
{
	const struct bosch_bmi323_config *config = (const struct bosch_bmi323_config *)dev->config;

	const struct bosch_bmi323_bus *bus = config->bus;

	return bus->api->write_words(bus->context, offset, words, words_count);
}

static int32_t bosch_bmi323_lsb_from_fullscale(int64_t fullscale)
{
	return (fullscale * 1000) / INT16_MAX;
}

/* lsb is the value of one 1/1000000 LSB */
static int64_t bosch_bmi323_value_to_micro(int16_t value, int32_t lsb)
{
	return ((int64_t)value) * lsb;
}

/* lsb is the value of one 1/1000000 LSB */
static void bosch_bmi323_value_to_sensor_value(struct sensor_value *result, int16_t value,
					       int32_t lsb)
{
	int64_t ll_value = (int64_t)value * lsb;
	int32_t int_part = (int32_t)(ll_value / 1000000);
	int32_t frac_part = (int32_t)(ll_value % 1000000);

	result->val1 = int_part;
	result->val2 = frac_part;
}

static void bosch_bmi323_sensor_value_from_micro(struct sensor_value *result, int64_t micro)
{
	int32_t int_part = (int32_t)(micro / 1000000);
	int32_t frac_part = (int32_t)(micro % 1000000);

	result->val1 = int_part;
	result->val2 = frac_part;
}

static bool bosch_bmi323_value_is_valid(int16_t value)
{
	return ((uint16_t)value == 0x8000) ? false : true;
}

static int bosch_bmi323_validate_chip_id(const struct device *dev)
{
	uint16_t sensor_id;
	int ret;

	ret = bosch_bmi323_bus_read_words(dev, 0, &sensor_id, 1);

	if (ret < 0) {
		return ret;
	}

	if ((sensor_id & 0xFF) != 0x43) {
		return -ENODEV;
	}

	return 0;
}

static int bosch_bmi323_soft_reset(const struct device *dev)
{
	uint16_t cmd;
	int ret;

	cmd = IMU_BOSCH_BMI323_REG_VALUE(CMD, CMD, SOFT_RESET);

	ret = bosch_bmi323_bus_write_words(dev, IMU_BOSCH_BMI323_REG_CMD, &cmd, 1);

	if (ret < 0) {
		return ret;
	}

	k_usleep(1500);

	return 0;
}

static int bosch_bmi323_enable_feature_engine(const struct device *dev)
{
	uint16_t buf;
	int ret;

	buf = IMU_BOSCH_FEATURE_ENGINE_STARTUP_CONFIG;

	ret = bosch_bmi323_bus_write_words(dev, IMU_BOSCH_BMI323_REG_FEATURE_IO2, &buf, 1);

	if (ret < 0) {
		return ret;
	}

	buf = IMU_BOSCH_BMI323_REG_VALUE(FEATURE_IO_STATUS, STATUS, SET);

	ret = bosch_bmi323_bus_write_words(dev, IMU_BOSCH_BMI323_REG_FEATURE_IO_STATUS, &buf, 1);

	if (ret < 0) {
		return ret;
	}

	buf = IMU_BOSCH_BMI323_REG_VALUE(FEATURE_CTRL, ENABLE, EN);

	return bosch_bmi323_bus_write_words(dev, IMU_BOSCH_BMI323_REG_FEATURE_CTRL, &buf, 1);
}

static int bosch_bmi323_driver_api_set_acc_odr(const struct device *dev,
					       const struct sensor_value *val)
{
	int ret;
	uint16_t acc_conf;
	int64_t odr = sensor_value_to_milli(val);

	ret = bosch_bmi323_bus_read_words(dev, IMU_BOSCH_BMI323_REG_ACC_CONF, &acc_conf, 1);

	if (ret < 0) {
		return ret;
	}

	acc_conf &= ~IMU_BOSCH_BMI323_REG_MASK(ACC_CONF, ODR);

	if (odr <= 782) {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, ODR, HZ0P78125);
	} else if (odr <= 1563) {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, ODR, HZ1P5625);
	} else if (odr <= 3125) {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, ODR, HZ3P125);
	} else if (odr <= 6250) {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, ODR, HZ6P25);
	} else if (odr <= 12500) {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, ODR, HZ12P5);
	} else if (odr <= 25000) {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, ODR, HZ25);
	} else if (odr <= 50000) {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, ODR, HZ50);
	} else if (odr <= 100000) {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, ODR, HZ100);
	} else if (odr <= 200000) {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, ODR, HZ200);
	} else if (odr <= 400000) {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, ODR, HZ400);
	} else if (odr <= 800000) {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, ODR, HZ800);
	} else if (odr <= 1600000) {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, ODR, HZ1600);
	} else if (odr <= 3200000) {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, ODR, HZ3200);
	} else {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, ODR, HZ6400);
	}

	return bosch_bmi323_bus_write_words(dev, IMU_BOSCH_BMI323_REG_ACC_CONF, &acc_conf, 1);
}

static int bosch_bmi323_driver_api_set_acc_full_scale(const struct device *dev,
						      const struct sensor_value *val)
{
	struct bosch_bmi323_data *data = (struct bosch_bmi323_data *)dev->data;
	int ret;
	uint16_t acc_conf;
	int64_t fullscale = sensor_value_to_milli(val);

	ret = bosch_bmi323_bus_read_words(dev, IMU_BOSCH_BMI323_REG_ACC_CONF, &acc_conf, 1);

	if (ret < 0) {
		return ret;
	}

	acc_conf &= ~IMU_BOSCH_BMI323_REG_MASK(ACC_CONF, RANGE);

	if (fullscale <= 2000) {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, RANGE, G2);
	} else if (fullscale <= 4000) {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, RANGE, G4);
	} else if (fullscale <= 8000) {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, RANGE, G8);
	} else {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, RANGE, G16);
	}

	data->acc_full_scale = 0;

	return bosch_bmi323_bus_write_words(dev, IMU_BOSCH_BMI323_REG_ACC_CONF, &acc_conf, 1);
}

static int bosch_bmi323_driver_api_set_acc_feature_mask(const struct device *dev,
							const struct sensor_value *val)
{
	int ret;
	uint16_t acc_conf;

	ret = bosch_bmi323_bus_read_words(dev, IMU_BOSCH_BMI323_REG_ACC_CONF, &acc_conf, 1);

	if (ret < 0) {
		return ret;
	}

	acc_conf &= ~IMU_BOSCH_BMI323_REG_MASK(ACC_CONF, MODE);

	if (val->val1) {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, MODE, HPWR);
	} else {
		acc_conf |= IMU_BOSCH_BMI323_REG_VALUE(ACC_CONF, MODE, DIS);
	}

	return bosch_bmi323_bus_write_words(dev, IMU_BOSCH_BMI323_REG_ACC_CONF, &acc_conf, 1);
}

static int bosch_bmi323_driver_api_set_gyro_odr(const struct device *dev,
						const struct sensor_value *val)
{
	int ret;
	uint16_t gyro_conf;
	int64_t odr = sensor_value_to_milli(val);

	ret = bosch_bmi323_bus_read_words(dev, IMU_BOSCH_BMI323_REG_GYRO_CONF, &gyro_conf, 1);

	if (ret < 0) {
		return ret;
	}

	gyro_conf &= ~IMU_BOSCH_BMI323_REG_MASK(GYRO_CONF, ODR);

	if (odr <= 782) {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, ODR, HZ0P78125);
	} else if (odr <= 1563) {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, ODR, HZ1P5625);
	} else if (odr <= 3125) {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, ODR, HZ3P125);
	} else if (odr <= 6250) {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, ODR, HZ6P25);
	} else if (odr <= 12500) {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, ODR, HZ12P5);
	} else if (odr <= 25000) {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, ODR, HZ25);
	} else if (odr <= 50000) {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, ODR, HZ50);
	} else if (odr <= 100000) {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, ODR, HZ100);
	} else if (odr <= 200000) {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, ODR, HZ200);
	} else if (odr <= 400000) {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, ODR, HZ400);
	} else if (odr <= 800000) {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, ODR, HZ800);
	} else if (odr <= 1600000) {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, ODR, HZ1600);
	} else if (odr <= 3200000) {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, ODR, HZ3200);
	} else {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, ODR, HZ6400);
	}

	return bosch_bmi323_bus_write_words(dev, IMU_BOSCH_BMI323_REG_GYRO_CONF, &gyro_conf, 1);
}

static int bosch_bmi323_driver_api_set_gyro_full_scale(const struct device *dev,
						       const struct sensor_value *val)
{
	struct bosch_bmi323_data *data = (struct bosch_bmi323_data *)dev->data;
	int ret;
	uint16_t gyro_conf;
	int32_t fullscale = sensor_value_to_milli(val);

	ret = bosch_bmi323_bus_read_words(dev, IMU_BOSCH_BMI323_REG_GYRO_CONF, &gyro_conf, 1);

	if (ret < 0) {
		return ret;
	}

	gyro_conf &= ~IMU_BOSCH_BMI323_REG_MASK(GYRO_CONF, RANGE);

	if (fullscale <= 125000) {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, RANGE, DPS125);
	} else if (fullscale <= 250000) {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, RANGE, DPS250);
	} else if (fullscale <= 500000) {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, RANGE, DPS500);
	} else if (fullscale <= 1000000) {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, RANGE, DPS1000);
	} else {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, RANGE, DPS2000);
	}

	data->gyro_full_scale = 0;

	return bosch_bmi323_bus_write_words(dev, IMU_BOSCH_BMI323_REG_GYRO_CONF, &gyro_conf, 1);
}

static int bosch_bmi323_driver_api_set_gyro_feature_mask(const struct device *dev,
							 const struct sensor_value *val)
{
	int ret;
	uint16_t gyro_conf;

	ret = bosch_bmi323_bus_read_words(dev, IMU_BOSCH_BMI323_REG_GYRO_CONF, &gyro_conf, 1);

	if (ret < 0) {
		return ret;
	}

	gyro_conf &= ~IMU_BOSCH_BMI323_REG_MASK(GYRO_CONF, MODE);

	if (val->val1) {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, MODE, HPWR);
	} else {
		gyro_conf |= IMU_BOSCH_BMI323_REG_VALUE(GYRO_CONF, MODE, DIS);
	}

	return bosch_bmi323_bus_write_words(dev, IMU_BOSCH_BMI323_REG_GYRO_CONF, &gyro_conf, 1);
}

static int bosch_bmi323_driver_api_attr_set(const struct device *dev, enum sensor_channel chan,
					    enum sensor_attribute attr,
					    const struct sensor_value *val)
{
	struct bosch_bmi323_data *data = (struct bosch_bmi323_data *)dev->data;
	int ret;

	k_mutex_lock(&data->lock, K_FOREVER);

	switch (chan) {
	case SENSOR_CHAN_ACCEL_XYZ:
		switch (attr) {
		case SENSOR_ATTR_SAMPLING_FREQUENCY:
			ret = bosch_bmi323_driver_api_set_acc_odr(dev, val);

			break;

		case SENSOR_ATTR_FULL_SCALE:
			ret = bosch_bmi323_driver_api_set_acc_full_scale(dev, val);

			break;

		case SENSOR_ATTR_FEATURE_MASK:
			ret = bosch_bmi323_driver_api_set_acc_feature_mask(dev, val);

			break;

		default:
			ret = -ENODEV;

			break;
		}

		break;

	case SENSOR_CHAN_GYRO_XYZ:
		switch (attr) {
		case SENSOR_ATTR_SAMPLING_FREQUENCY:
			ret = bosch_bmi323_driver_api_set_gyro_odr(dev, val);

			break;

		case SENSOR_ATTR_FULL_SCALE:
			ret = bosch_bmi323_driver_api_set_gyro_full_scale(dev, val);

			break;

		case SENSOR_ATTR_FEATURE_MASK:
			ret = bosch_bmi323_driver_api_set_gyro_feature_mask(dev, val);

			break;

		default:
			ret = -ENODEV;

			break;
		}

		break;

	default:
		ret = -ENODEV;

		break;
	}

	k_mutex_unlock(&data->lock);

	return ret;
}

static int bosch_bmi323_driver_api_get_acc_odr(const struct device *dev, struct sensor_value *val)
{
	uint16_t acc_conf;
	int ret;

	ret = bosch_bmi323_bus_read_words(dev, IMU_BOSCH_BMI323_REG_ACC_CONF, &acc_conf, 1);

	if (ret < 0) {
		return ret;
	}

	switch (IMU_BOSCH_BMI323_REG_VALUE_GET_FIELD(acc_conf, ACC_CONF, ODR)) {
	case IMU_BOSCH_BMI323_REG_ACC_CONF_ODR_VAL_HZ0P78125:
		val->val1 = 0;
		val->val2 = 781250;
		break;
	case IMU_BOSCH_BMI323_REG_ACC_CONF_ODR_VAL_HZ1P5625:
		val->val1 = 1;
		val->val2 = 562500;
		break;
	case IMU_BOSCH_BMI323_REG_ACC_CONF_ODR_VAL_HZ3P125:
		val->val1 = 3;
		val->val2 = 125000;
		break;
	case IMU_BOSCH_BMI323_REG_ACC_CONF_ODR_VAL_HZ6P25:
		val->val1 = 6;
		val->val2 = 250000;
		break;
	case IMU_BOSCH_BMI323_REG_ACC_CONF_ODR_VAL_HZ12P5:
		val->val1 = 12;
		val->val2 = 500000;
		break;
	case IMU_BOSCH_BMI323_REG_ACC_CONF_ODR_VAL_HZ25:
		val->val1 = 25;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_ACC_CONF_ODR_VAL_HZ50:
		val->val1 = 50;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_ACC_CONF_ODR_VAL_HZ100:
		val->val1 = 100;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_ACC_CONF_ODR_VAL_HZ200:
		val->val1 = 200;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_ACC_CONF_ODR_VAL_HZ400:
		val->val1 = 400;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_ACC_CONF_ODR_VAL_HZ800:
		val->val1 = 800;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_ACC_CONF_ODR_VAL_HZ1600:
		val->val1 = 1600;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_ACC_CONF_ODR_VAL_HZ3200:
		val->val1 = 3200;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_ACC_CONF_ODR_VAL_HZ6400:
		val->val1 = 6400;
		val->val2 = 0;
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static int bosch_bmi323_driver_api_get_acc_full_scale(const struct device *dev,
						      struct sensor_value *val)
{
	uint16_t acc_conf;
	int ret;

	ret = bosch_bmi323_bus_read_words(dev, IMU_BOSCH_BMI323_REG_ACC_CONF, &acc_conf, 1);

	if (ret < 0) {
		return ret;
	}

	switch (IMU_BOSCH_BMI323_REG_VALUE_GET_FIELD(acc_conf, ACC_CONF, RANGE)) {
	case IMU_BOSCH_BMI323_REG_ACC_CONF_RANGE_VAL_G2:
		val->val1 = 2;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_ACC_CONF_RANGE_VAL_G4:
		val->val1 = 4;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_ACC_CONF_RANGE_VAL_G8:
		val->val1 = 8;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_ACC_CONF_RANGE_VAL_G16:
		val->val1 = 16;
		val->val2 = 0;
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static int bosch_bmi323_driver_api_get_acc_feature_mask(const struct device *dev,
							struct sensor_value *val)
{
	uint16_t acc_conf;
	int ret;

	ret = bosch_bmi323_bus_read_words(dev, IMU_BOSCH_BMI323_REG_ACC_CONF, &acc_conf, 1);

	if (ret < 0) {
		return ret;
	}

	if (IMU_BOSCH_BMI323_REG_VALUE_GET_FIELD(acc_conf, ACC_CONF, MODE)) {
		val->val1 = 1;
		val->val2 = 0;
	} else {
		val->val1 = 0;
		val->val2 = 0;
	}

	return 0;
}

static int bosch_bmi323_driver_api_get_gyro_odr(const struct device *dev, struct sensor_value *val)
{
	uint16_t gyro_conf;
	int ret;

	ret = bosch_bmi323_bus_read_words(dev, IMU_BOSCH_BMI323_REG_GYRO_CONF, &gyro_conf, 1);

	if (ret < 0) {
		return ret;
	}

	switch (IMU_BOSCH_BMI323_REG_VALUE_GET_FIELD(gyro_conf, GYRO_CONF, ODR)) {
	case IMU_BOSCH_BMI323_REG_GYRO_CONF_ODR_VAL_HZ0P78125:
		val->val1 = 0;
		val->val2 = 781250;
		break;
	case IMU_BOSCH_BMI323_REG_GYRO_CONF_ODR_VAL_HZ1P5625:
		val->val1 = 1;
		val->val2 = 562500;
		break;
	case IMU_BOSCH_BMI323_REG_GYRO_CONF_ODR_VAL_HZ3P125:
		val->val1 = 3;
		val->val2 = 125000;
		break;
	case IMU_BOSCH_BMI323_REG_GYRO_CONF_ODR_VAL_HZ6P25:
		val->val1 = 6;
		val->val2 = 250000;
		break;
	case IMU_BOSCH_BMI323_REG_GYRO_CONF_ODR_VAL_HZ12P5:
		val->val1 = 12;
		val->val2 = 500000;
		break;
	case IMU_BOSCH_BMI323_REG_GYRO_CONF_ODR_VAL_HZ25:
		val->val1 = 25;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_GYRO_CONF_ODR_VAL_HZ50:
		val->val1 = 50;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_GYRO_CONF_ODR_VAL_HZ100:
		val->val1 = 100;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_GYRO_CONF_ODR_VAL_HZ200:
		val->val1 = 200;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_GYRO_CONF_ODR_VAL_HZ400:
		val->val1 = 400;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_GYRO_CONF_ODR_VAL_HZ800:
		val->val1 = 800;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_GYRO_CONF_ODR_VAL_HZ1600:
		val->val1 = 1600;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_GYRO_CONF_ODR_VAL_HZ3200:
		val->val1 = 3200;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_GYRO_CONF_ODR_VAL_HZ6400:
		val->val1 = 6400;
		val->val2 = 0;
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static int bosch_bmi323_driver_api_get_gyro_full_scale(const struct device *dev,
						       struct sensor_value *val)
{
	uint16_t gyro_conf;
	int ret;

	ret = bosch_bmi323_bus_read_words(dev, IMU_BOSCH_BMI323_REG_GYRO_CONF, &gyro_conf, 1);

	if (ret < 0) {
		return ret;
	}

	switch (IMU_BOSCH_BMI323_REG_VALUE_GET_FIELD(gyro_conf, GYRO_CONF, RANGE)) {
	case IMU_BOSCH_BMI323_REG_GYRO_CONF_RANGE_VAL_DPS125:
		val->val1 = 125;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_GYRO_CONF_RANGE_VAL_DPS250:
		val->val1 = 250;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_GYRO_CONF_RANGE_VAL_DPS500:
		val->val1 = 500;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_GYRO_CONF_RANGE_VAL_DPS1000:
		val->val1 = 1000;
		val->val2 = 0;
		break;
	case IMU_BOSCH_BMI323_REG_GYRO_CONF_RANGE_VAL_DPS2000:
		val->val1 = 2000;
		val->val2 = 0;
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static int bosch_bmi323_driver_api_get_gyro_feature_mask(const struct device *dev,
							 struct sensor_value *val)
{
	uint16_t gyro_conf;
	int ret;

	ret = bosch_bmi323_bus_read_words(dev, IMU_BOSCH_BMI323_REG_GYRO_CONF, &gyro_conf, 1);

	if (ret < 0) {
		return ret;
	}

	if (IMU_BOSCH_BMI323_REG_VALUE_GET_FIELD(gyro_conf, GYRO_CONF, MODE)) {
		val->val1 = 1;
		val->val2 = 0;
	} else {
		val->val1 = 0;
		val->val2 = 0;
	}

	return 0;
}

static int bosch_bmi323_driver_api_attr_get(const struct device *dev, enum sensor_channel chan,
					    enum sensor_attribute attr, struct sensor_value *val)
{
	struct bosch_bmi323_data *data = (struct bosch_bmi323_data *)dev->data;
	int ret;

	k_mutex_lock(&data->lock, K_FOREVER);

	switch (chan) {
	case SENSOR_CHAN_ACCEL_XYZ:
		switch (attr) {
		case SENSOR_ATTR_SAMPLING_FREQUENCY:
			ret = bosch_bmi323_driver_api_get_acc_odr(dev, val);

			break;

		case SENSOR_ATTR_FULL_SCALE:
			ret = bosch_bmi323_driver_api_get_acc_full_scale(dev, val);

			break;

		case SENSOR_ATTR_FEATURE_MASK:
			ret = bosch_bmi323_driver_api_get_acc_feature_mask(dev, val);

			break;

		default:
			ret = -ENODEV;

			break;
		}

		break;

	case SENSOR_CHAN_GYRO_XYZ:
		switch (attr) {
		case SENSOR_ATTR_SAMPLING_FREQUENCY:
			ret = bosch_bmi323_driver_api_get_gyro_odr(dev, val);

			break;

		case SENSOR_ATTR_FULL_SCALE:
			ret = bosch_bmi323_driver_api_get_gyro_full_scale(dev, val);

			break;

		case SENSOR_ATTR_FEATURE_MASK:
			ret = bosch_bmi323_driver_api_get_gyro_feature_mask(dev, val);

			break;

		default:
			ret = -ENODEV;

			break;
		}

		break;

	default:
		ret = -ENODEV;
		break;
	}

	k_mutex_unlock(&data->lock);

	return ret;
}

static int bosch_bmi323_driver_api_trigger_set_acc_drdy(const struct device *dev)
{
	uint16_t buf[2];

	buf[0] = 0;
	buf[1] = IMU_BOSCH_BMI323_REG_VALUE(INT_MAP2, ACC_DRDY_INT, INT1);

	return bosch_bmi323_bus_write_words(dev, IMU_BOSCH_BMI323_REG_INT_MAP1, buf, 2);
}

static int bosch_bmi323_driver_api_trigger_set_acc_motion(const struct device *dev)
{
	uint16_t buf[2];
	int ret;

	buf[0] = IMU_BOSCH_BMI323_REG_VALUE(INT_MAP1, MOTION_OUT, INT1);
	buf[1] = 0;

	ret = bosch_bmi323_bus_write_words(dev, IMU_BOSCH_BMI323_REG_INT_MAP1, buf, 2);

	if (ret < 0) {
		return ret;
	}

	buf[0] = 0;

	ret = bosch_bmi323_bus_write_words(dev, IMU_BOSCH_BMI323_REG_FEATURE_IO0, buf, 1);

	if (ret < 0) {
		return ret;
	}

	buf[0] = IMU_BOSCH_BMI323_REG_VALUE(FEATURE_IO0, MOTION_X_EN, EN) |
		 IMU_BOSCH_BMI323_REG_VALUE(FEATURE_IO0, MOTION_Y_EN, EN) |
		 IMU_BOSCH_BMI323_REG_VALUE(FEATURE_IO0, MOTION_Z_EN, EN);

	ret = bosch_bmi323_bus_write_words(dev, IMU_BOSCH_BMI323_REG_FEATURE_IO0, buf, 1);

	if (ret < 0) {
		return ret;
	}

	buf[0] = 1;

	return bosch_bmi323_bus_write_words(dev, IMU_BOSCH_BMI323_REG_FEATURE_IO_STATUS, buf, 1);
}

static int bosch_bmi323_driver_api_trigger_set(const struct device *dev,
					       const struct sensor_trigger *trig,
					       sensor_trigger_handler_t handler)
{
	struct bosch_bmi323_data *data = (struct bosch_bmi323_data *)dev->data;
	int ret = -ENODEV;

	k_mutex_lock(&data->lock, K_FOREVER);

	data->trigger = trig;
	data->trigger_handler = handler;

	switch (trig->chan) {
	case SENSOR_CHAN_ACCEL_XYZ:
		switch (trig->type) {
		case SENSOR_TRIG_DATA_READY:
			ret = bosch_bmi323_driver_api_trigger_set_acc_drdy(dev);

			break;

		case SENSOR_TRIG_MOTION:
			ret = bosch_bmi323_driver_api_trigger_set_acc_motion(dev);

			break;

		default:
			break;
		}

		break;

	default:
		break;
	}

	k_mutex_unlock(&data->lock);

	return ret;
}

static int bosch_bmi323_driver_api_fetch_acc_samples(const struct device *dev)
{
	struct bosch_bmi323_data *data = (struct bosch_bmi323_data *)dev->data;
	struct sensor_value full_scale;
	int16_t *buf = (int16_t *)data->acc_samples;
	int ret;
	int32_t lsb;

	if (data->acc_full_scale == 0) {
		ret = bosch_bmi323_driver_api_get_acc_full_scale(dev, &full_scale);

		if (ret < 0) {
			return ret;
		}

		data->acc_full_scale = sensor_value_to_milli(&full_scale);
	}

	ret = bosch_bmi323_bus_read_words(dev, IMU_BOSCH_BMI323_REG_ACC_DATA_X, (uint16_t *)buf, 3);

	if (ret < 0) {
		return ret;
	}

	if ((bosch_bmi323_value_is_valid(buf[0]) == false) ||
	    (bosch_bmi323_value_is_valid(buf[1]) == false) ||
	    (bosch_bmi323_value_is_valid(buf[2]) == false)) {
		return -ENODATA;
	}

	lsb = bosch_bmi323_lsb_from_fullscale(data->acc_full_scale);

	/* Reuse vector backwards to avoid overwriting the raw values */
	bosch_bmi323_value_to_sensor_value(&data->acc_samples[2], buf[2], lsb);
	bosch_bmi323_value_to_sensor_value(&data->acc_samples[1], buf[1], lsb);
	bosch_bmi323_value_to_sensor_value(&data->acc_samples[0], buf[0], lsb);

	data->acc_samples_valid = true;

	return 0;
}

static int bosch_bmi323_driver_api_fetch_gyro_samples(const struct device *dev)
{
	struct bosch_bmi323_data *data = (struct bosch_bmi323_data *)dev->data;
	struct sensor_value full_scale;
	int16_t *buf = (int16_t *)data->gyro_samples;
	int ret;
	int32_t lsb;

	if (data->gyro_full_scale == 0) {
		ret = bosch_bmi323_driver_api_get_gyro_full_scale(dev, &full_scale);

		if (ret < 0) {
			return ret;
		}

		data->gyro_full_scale = sensor_value_to_milli(&full_scale);
	}

	ret = bosch_bmi323_bus_read_words(dev, IMU_BOSCH_BMI323_REG_GYRO_DATA_X, (uint16_t *)buf,
					  3);

	if (ret < 0) {
		return ret;
	}

	if ((bosch_bmi323_value_is_valid(buf[0]) == false) ||
	    (bosch_bmi323_value_is_valid(buf[1]) == false) ||
	    (bosch_bmi323_value_is_valid(buf[2]) == false)) {
		return -ENODATA;
	}

	lsb = bosch_bmi323_lsb_from_fullscale(data->gyro_full_scale);

	/* Reuse vector backwards to avoid overwriting the raw values */
	bosch_bmi323_value_to_sensor_value(&data->gyro_samples[2], buf[2], lsb);
	bosch_bmi323_value_to_sensor_value(&data->gyro_samples[1], buf[1], lsb);
	bosch_bmi323_value_to_sensor_value(&data->gyro_samples[0], buf[0], lsb);

	data->gyro_samples_valid = true;

	return 0;
}

static int bosch_bmi323_driver_api_fetch_temperature(const struct device *dev)
{
	struct bosch_bmi323_data *data = (struct bosch_bmi323_data *)dev->data;
	int16_t buf;
	int64_t micro;
	int ret;

	ret = bosch_bmi323_bus_read_words(dev, IMU_BOSCH_BMI323_REG_TEMP_DATA, &buf, 1);

	if (ret < 0) {
		return ret;
	}

	if (bosch_bmi323_value_is_valid(buf) == false) {
		return -ENODATA;
	}

	micro = bosch_bmi323_value_to_micro(buf, IMU_BOSCH_DIE_TEMP_MICRO_DEG_CELCIUS_LSB);

	micro += IMU_BOSCH_DIE_TEMP_OFFSET_MICRO_DEG_CELCIUS;

	bosch_bmi323_sensor_value_from_micro(&data->temperature, micro);

	data->temperature_valid = true;

	return 0;
}

static int bosch_bmi323_driver_api_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
	struct bosch_bmi323_data *data = (struct bosch_bmi323_data *)dev->data;
	int ret;

	k_mutex_lock(&data->lock, K_FOREVER);

	switch (chan) {
	case SENSOR_CHAN_ACCEL_XYZ:
		ret = bosch_bmi323_driver_api_fetch_acc_samples(dev);

		break;

	case SENSOR_CHAN_GYRO_XYZ:
		ret = bosch_bmi323_driver_api_fetch_gyro_samples(dev);

		break;

	case SENSOR_CHAN_DIE_TEMP:
		ret = bosch_bmi323_driver_api_fetch_temperature(dev);

		break;

	case SENSOR_CHAN_ALL:
		ret = bosch_bmi323_driver_api_fetch_acc_samples(dev);

		if (ret < 0) {
			break;
		}

		ret = bosch_bmi323_driver_api_fetch_gyro_samples(dev);

		if (ret < 0) {
			break;
		}

		ret = bosch_bmi323_driver_api_fetch_temperature(dev);

		break;

	default:
		ret = -ENODEV;

		break;
	}

	k_mutex_unlock(&data->lock);

	return ret;
}

static int bosch_bmi323_driver_api_channel_get(const struct device *dev, enum sensor_channel chan,
					       struct sensor_value *val)
{
	struct bosch_bmi323_data *data = (struct bosch_bmi323_data *)dev->data;
	int ret = 0;

	k_mutex_lock(&data->lock, K_FOREVER);

	switch (chan) {
	case SENSOR_CHAN_ACCEL_XYZ:
		if (data->acc_samples_valid == false) {
			ret = -ENODATA;

			break;
		}

		memcpy(val, data->acc_samples, sizeof(data->acc_samples));

		break;

	case SENSOR_CHAN_GYRO_XYZ:
		if (data->gyro_samples_valid == false) {
			ret = -ENODATA;

			break;
		}

		memcpy(val, data->gyro_samples, sizeof(data->gyro_samples));

		break;

	case SENSOR_CHAN_DIE_TEMP:
		if (data->temperature_valid == false) {
			ret = -ENODATA;

			break;
		}

		(*val) = data->temperature;

		break;

	default:
		ret = -ENOTSUP;

		break;
	}

	k_mutex_unlock(&data->lock);

	return ret;
}

static const struct sensor_driver_api bosch_bmi323_api = {
	.attr_set = bosch_bmi323_driver_api_attr_set,
	.attr_get = bosch_bmi323_driver_api_attr_get,
	.trigger_set = bosch_bmi323_driver_api_trigger_set,
	.sample_fetch = bosch_bmi323_driver_api_sample_fetch,
	.channel_get = bosch_bmi323_driver_api_channel_get,
};

static void bosch_bmi323_irq_callback(const struct device *dev)
{
	struct bosch_bmi323_data *data = (struct bosch_bmi323_data *)dev->data;

	k_work_submit(&data->callback_work);
}

static int bosch_bmi323_init_irq(const struct device *dev)
{
	struct bosch_bmi323_data *data = (struct bosch_bmi323_data *)dev->data;
	struct bosch_bmi323_config *config = (struct bosch_bmi323_config *)dev->config;
	int ret;

	ret = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);

	if (ret < 0) {
		return ret;
	}

	gpio_init_callback(&data->gpio_callback, config->int_gpio_callback,
			   BIT(config->int_gpio.pin));

	ret = gpio_add_callback(config->int_gpio.port, &data->gpio_callback);

	if (ret < 0) {
		return ret;
	}

	return gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_DISABLE);
}

static int bosch_bmi323_init_int1(const struct device *dev)
{
	uint16_t buf;

	buf = IMU_BOSCH_BMI323_REG_VALUE(IO_INT_CTRL, INT1_LVL, ACT_HIGH) |
	      IMU_BOSCH_BMI323_REG_VALUE(IO_INT_CTRL, INT1_OD, PUSH_PULL) |
	      IMU_BOSCH_BMI323_REG_VALUE(IO_INT_CTRL, INT1_OUTPUT_EN, EN);

	return bosch_bmi323_bus_write_words(dev, IMU_BOSCH_BMI323_REG_IO_INT_CTRL, &buf, 1);
}

static void bosch_bmi323_irq_callback_handler(struct k_work *item)
{
	struct bosch_bmi323_data *data =
		CONTAINER_OF(item, struct bosch_bmi323_data, callback_work);

	k_mutex_lock(&data->lock, K_FOREVER);

	if (data->trigger_handler != NULL) {
		data->trigger_handler(data->dev, data->trigger);
	}

	k_mutex_unlock(&data->lock);
}

static int bosch_bmi323_pm_resume(const struct device *dev)
{
	const struct bosch_bmi323_config *config = (const struct bosch_bmi323_config *)dev->config;
	int ret;

	ret = bosch_bmi323_bus_init(dev);

	if (ret < 0) {
		LOG_WRN("Failed to init bus");

		return ret;
	}

	ret = bosch_bmi323_validate_chip_id(dev);

	if (ret < 0) {
		LOG_WRN("Failed to validate chip id");

		return ret;
	}

	ret = bosch_bmi323_soft_reset(dev);

	if (ret < 0) {
		LOG_WRN("Failed to soft reset chip");

		return ret;
	}

	ret = bosch_bmi323_bus_init(dev);

	if (ret < 0) {
		LOG_WRN("Failed to re-init bus");

		return ret;
	}

	ret = bosch_bmi323_enable_feature_engine(dev);

	if (ret < 0) {
		LOG_WRN("Failed to enable feature engine");

		return ret;
	}

	ret = bosch_bmi323_init_int1(dev);

	if (ret < 0) {
		LOG_WRN("Failed to enable INT1");
		return ret;
	}

	ret = gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
	if (ret < 0) {
		LOG_WRN("Failed to configure int");
	}

	return ret;
}

#ifdef CONFIG_PM_DEVICE
static int bosch_bmi323_pm_suspend(const struct device *dev)
{
	const struct bosch_bmi323_config *config = (const struct bosch_bmi323_config *)dev->config;
	int ret;

	ret = gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_DISABLE);
	if (ret < 0) {
		LOG_WRN("Failed to disable int");
	}

	/* Soft reset device to put it into suspend */
	return bosch_bmi323_soft_reset(dev);
}
#endif /* CONFIG_PM_DEVICE */

#ifdef CONFIG_PM_DEVICE
static int bosch_bmi323_pm_action(const struct device *dev, enum pm_device_action action)
{
	struct bosch_bmi323_data *data = (struct bosch_bmi323_data *)dev->data;
	int ret;

	k_mutex_lock(&data->lock, K_FOREVER);

	switch (action) {
	case PM_DEVICE_ACTION_RESUME:
		ret = bosch_bmi323_pm_resume(dev);

		break;

	case PM_DEVICE_ACTION_SUSPEND:
		ret = bosch_bmi323_pm_suspend(dev);

		break;

	default:
		ret = -ENOTSUP;

		break;
	}

	k_mutex_unlock(&data->lock);

	return ret;
}
#endif /* CONFIG_PM_DEVICE */

static int bosch_bmi323_init(const struct device *dev)
{
	struct bosch_bmi323_data *data = (struct bosch_bmi323_data *)dev->data;
	int ret;

	k_mutex_init(&data->lock);

	k_work_init(&data->callback_work, bosch_bmi323_irq_callback_handler);

	data->dev = dev;

	ret = bosch_bmi323_init_irq(dev);

	if (ret < 0) {
		LOG_WRN("Failed to init irq");

		return ret;
	}

#ifndef CONFIG_PM_DEVICE_RUNTIME
	ret = bosch_bmi323_pm_resume(dev);

	if (ret < 0) {
		LOG_WRN("Failed to initialize device");
	}
#else
	pm_device_init_suspended(dev);

	ret = pm_device_runtime_enable(dev);

	if (ret < 0) {
		LOG_WRN("Failed to enable device pm runtime");
	}
#endif /* CONFIG_PM_DEVICE_RUNTIME */

	return ret;
}

/*
 * Currently only support for the SPI bus is implemented. This shall be updated to
 * select the appropriate bus once I2C is implemented.
 */
#define BMI323_DEVICE_BUS(inst)                                                                    \
	BUILD_ASSERT(DT_INST_ON_BUS(inst, spi), "Unimplemented bus");                              \
	BMI323_DEVICE_SPI_BUS(inst)

#define BMI323_DEVICE(inst)                                                                        \
	static struct bosch_bmi323_data bosch_bmi323_data_##inst;                                  \
                                                                                                   \
	BMI323_DEVICE_BUS(inst);                                                                   \
                                                                                                   \
	static void bosch_bmi323_irq_callback##inst(const struct device *dev,                      \
						    struct gpio_callback *cb, uint32_t pins)       \
	{                                                                                          \
		bosch_bmi323_irq_callback(DEVICE_DT_INST_GET(inst));                               \
	}                                                                                          \
                                                                                                   \
	static const struct bosch_bmi323_config bosch_bmi323_config_##inst = {                     \
		.bus = &bosch_bmi323_bus_api##inst,                                                \
		.int_gpio = GPIO_DT_SPEC_INST_GET(inst, int_gpios),                                \
		.int_gpio_callback = bosch_bmi323_irq_callback##inst,                              \
	};                                                                                         \
                                                                                                   \
	PM_DEVICE_DT_INST_DEFINE(inst, bosch_bmi323_pm_action);                                    \
                                                                                                   \
	SENSOR_DEVICE_DT_INST_DEFINE(inst, bosch_bmi323_init, PM_DEVICE_DT_INST_GET(inst),         \
				     &bosch_bmi323_data_##inst, &bosch_bmi323_config_##inst,       \
				     POST_KERNEL, 99, &bosch_bmi323_api);

DT_INST_FOREACH_STATUS_OKAY(BMI323_DEVICE)
