blob: 940168e41843e7e85fff8dede2188786b9eb2940 [file] [log] [blame]
/*
* Copyright (c) 2021 Bosch Sensortec GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT bosch_bmi270
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/init.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/logging/log.h>
#include "bmi270.h"
#include "bmi270_config_file.h"
LOG_MODULE_REGISTER(bmi270, CONFIG_SENSOR_LOG_LEVEL);
#define BMI270_WR_LEN 256
#define BMI270_CONFIG_FILE_RETRIES 15
#define BMI270_CONFIG_FILE_POLL_PERIOD_US 10000
#define BMI270_INTER_WRITE_DELAY_US 1000
static int reg_read(const struct device *dev, uint8_t reg, uint8_t *data, uint16_t length)
{
const struct bmi270_dev_config *cfg = dev->config;
return i2c_burst_read_dt(&cfg->i2c, reg, data, length);
}
static int reg_write(const struct device *dev, uint8_t reg, const uint8_t *data, uint16_t length)
{
const struct bmi270_dev_config *cfg = dev->config;
return i2c_burst_write_dt(&cfg->i2c, reg, data, length);
}
static int reg_write_with_delay(const struct device *dev, uint8_t reg, const uint8_t *data,
uint16_t length, uint32_t delay_us)
{
int ret = 0;
ret = reg_write(dev, reg, data, length);
if (ret == 0) {
k_usleep(delay_us);
}
return ret;
}
static void channel_accel_convert(struct sensor_value *val, int64_t raw_val,
uint8_t range)
{
/* 16 bit accelerometer. 2^15 bits represent the range in G */
/* Converting from G to m/s^2 */
raw_val = (raw_val * SENSOR_G * (int64_t) range) / INT16_MAX;
val->val1 = raw_val / 1000000LL;
val->val2 = raw_val % 1000000LL;
/* Normalize val to make sure val->val2 is positive */
if (val->val2 < 0) {
val->val1 -= 1LL;
val->val2 += 1000000LL;
}
}
static void channel_gyro_convert(struct sensor_value *val, int64_t raw_val,
uint16_t range)
{
/* 16 bit gyroscope. 2^15 bits represent the range in degrees/s */
/* Converting from degrees/s to radians/s */
val->val1 = ((raw_val * (int64_t) range * SENSOR_PI)
/ (180LL * INT16_MAX)) / 1000000LL;
val->val2 = ((raw_val * (int64_t) range * SENSOR_PI)
/ (180LL * INT16_MAX)) % 1000000LL;
/* Normalize val to make sure val->val2 is positive */
if (val->val2 < 0) {
val->val1 -= 1LL;
val->val2 += 1000000LL;
}
}
static uint8_t acc_odr_to_reg(const struct sensor_value *val)
{
double odr = sensor_value_to_double((struct sensor_value *) val);
uint8_t reg = 0;
if ((odr >= 0.78125) && (odr < 1.5625)) {
reg = BMI270_ACC_ODR_25D32_HZ;
} else if ((odr >= 1.5625) && (odr < 3.125)) {
reg = BMI270_ACC_ODR_25D16_HZ;
} else if ((odr >= 3.125) && (odr < 6.25)) {
reg = BMI270_ACC_ODR_25D8_HZ;
} else if ((odr >= 6.25) && (odr < 12.5)) {
reg = BMI270_ACC_ODR_25D4_HZ;
} else if ((odr >= 12.5) && (odr < 25.0)) {
reg = BMI270_ACC_ODR_25D2_HZ;
} else if ((odr >= 25.0) && (odr < 50.0)) {
reg = BMI270_ACC_ODR_25_HZ;
} else if ((odr >= 50.0) && (odr < 100.0)) {
reg = BMI270_ACC_ODR_50_HZ;
} else if ((odr >= 100.0) && (odr < 200.0)) {
reg = BMI270_ACC_ODR_100_HZ;
} else if ((odr >= 200.0) && (odr < 400.0)) {
reg = BMI270_ACC_ODR_200_HZ;
} else if ((odr >= 400.0) && (odr < 800.0)) {
reg = BMI270_ACC_ODR_400_HZ;
} else if ((odr >= 800.0) && (odr < 1600.0)) {
reg = BMI270_ACC_ODR_800_HZ;
} else if (odr >= 1600.0) {
reg = BMI270_ACC_ODR_1600_HZ;
}
return reg;
}
static int set_accel_odr_osr(const struct device *dev, const struct sensor_value *odr,
const struct sensor_value *osr)
{
struct bmi270_data *drv_dev = dev->data;
uint8_t acc_conf, odr_bits, pwr_ctrl, osr_bits;
int ret = 0;
if (odr || osr) {
ret = reg_read(dev, BMI270_REG_ACC_CONF, &acc_conf, 1);
if (ret != 0) {
return ret;
}
ret = reg_read(dev, BMI270_REG_PWR_CTRL, &pwr_ctrl, 1);
if (ret != 0) {
return ret;
}
}
if (odr) {
odr_bits = acc_odr_to_reg(odr);
acc_conf = BMI270_SET_BITS_POS_0(acc_conf, BMI270_ACC_ODR,
odr_bits);
/* If odr_bits is 0, implies that the sampling frequency is 0Hz
* or invalid too.
*/
if (odr_bits) {
pwr_ctrl |= BMI270_PWR_CTRL_ACC_EN;
} else {
pwr_ctrl &= ~BMI270_PWR_CTRL_ACC_EN;
}
/* If the Sampling frequency (odr) >= 100Hz, enter performance
* mode else, power optimized. This also has a consequence
* for the OSR
*/
if (odr_bits >= BMI270_ACC_ODR_100_HZ) {
acc_conf = BMI270_SET_BITS(acc_conf, BMI270_ACC_FILT,
BMI270_ACC_FILT_PERF_OPT);
} else {
acc_conf = BMI270_SET_BITS(acc_conf, BMI270_ACC_FILT,
BMI270_ACC_FILT_PWR_OPT);
}
drv_dev->acc_odr = odr_bits;
}
if (osr) {
if (drv_dev->acc_odr >= BMI270_ACC_ODR_100_HZ) {
/* Performance mode */
/* osr->val2 should be unused */
switch (osr->val1) {
case 4:
osr_bits = BMI270_ACC_BWP_OSR4_AVG1;
break;
case 2:
osr_bits = BMI270_ACC_BWP_OSR2_AVG2;
break;
case 1:
osr_bits = BMI270_ACC_BWP_NORM_AVG4;
break;
default:
osr_bits = BMI270_ACC_BWP_CIC_AVG8;
break;
}
} else {
/* Power optimized mode */
/* osr->val2 should be unused */
switch (osr->val1) {
case 1:
osr_bits = BMI270_ACC_BWP_OSR4_AVG1;
break;
case 2:
osr_bits = BMI270_ACC_BWP_OSR2_AVG2;
break;
case 4:
osr_bits = BMI270_ACC_BWP_NORM_AVG4;
break;
case 8:
osr_bits = BMI270_ACC_BWP_CIC_AVG8;
break;
case 16:
osr_bits = BMI270_ACC_BWP_RES_AVG16;
break;
case 32:
osr_bits = BMI270_ACC_BWP_RES_AVG32;
break;
case 64:
osr_bits = BMI270_ACC_BWP_RES_AVG64;
break;
case 128:
osr_bits = BMI270_ACC_BWP_RES_AVG128;
break;
default:
return -ENOTSUP;
}
}
acc_conf = BMI270_SET_BITS(acc_conf, BMI270_ACC_BWP,
osr_bits);
}
if (odr || osr) {
ret = reg_write(dev, BMI270_REG_ACC_CONF, &acc_conf, 1);
if (ret != 0) {
return ret;
}
/* Assuming we have advance power save enabled */
k_usleep(BMI270_TRANSC_DELAY_SUSPEND);
pwr_ctrl &= BMI270_PWR_CTRL_MSK;
ret = reg_write_with_delay(dev, BMI270_REG_PWR_CTRL, &pwr_ctrl, 1,
BMI270_INTER_WRITE_DELAY_US);
}
return ret;
}
static int set_accel_range(const struct device *dev, const struct sensor_value *range)
{
struct bmi270_data *drv_dev = dev->data;
int ret = 0;
uint8_t acc_range, reg;
ret = reg_read(dev, BMI270_REG_ACC_RANGE, &acc_range, 1);
if (ret != 0) {
return ret;
}
/* range->val2 is unused */
switch (range->val1) {
case 2:
reg = BMI270_ACC_RANGE_2G;
drv_dev->acc_range = 2;
break;
case 4:
reg = BMI270_ACC_RANGE_4G;
drv_dev->acc_range = 4;
break;
case 8:
reg = BMI270_ACC_RANGE_8G;
drv_dev->acc_range = 8;
break;
case 16:
reg = BMI270_ACC_RANGE_16G;
drv_dev->acc_range = 16;
break;
default:
return -ENOTSUP;
}
acc_range = BMI270_SET_BITS_POS_0(acc_range, BMI270_ACC_RANGE,
reg);
ret = reg_write_with_delay(dev, BMI270_REG_ACC_RANGE, &acc_range, 1,
BMI270_INTER_WRITE_DELAY_US);
return ret;
}
static uint8_t gyr_odr_to_reg(const struct sensor_value *val)
{
double odr = sensor_value_to_double((struct sensor_value *) val);
uint8_t reg = 0;
if ((odr >= 25.0) && (odr < 50.0)) {
reg = BMI270_GYR_ODR_25_HZ;
} else if ((odr >= 50.0) && (odr < 100.0)) {
reg = BMI270_GYR_ODR_50_HZ;
} else if ((odr >= 100.0) && (odr < 200.0)) {
reg = BMI270_GYR_ODR_100_HZ;
} else if ((odr >= 200.0) && (odr < 400.0)) {
reg = BMI270_GYR_ODR_200_HZ;
} else if ((odr >= 400.0) && (odr < 800.0)) {
reg = BMI270_GYR_ODR_400_HZ;
} else if ((odr >= 800.0) && (odr < 1600.0)) {
reg = BMI270_GYR_ODR_800_HZ;
} else if ((odr >= 1600.0) && (odr < 3200.0)) {
reg = BMI270_GYR_ODR_1600_HZ;
} else if (odr >= 3200.0) {
reg = BMI270_GYR_ODR_3200_HZ;
}
return reg;
}
static int set_gyro_odr_osr(const struct device *dev, const struct sensor_value *odr,
const struct sensor_value *osr)
{
struct bmi270_data *drv_dev = dev->data;
uint8_t gyr_conf, odr_bits, pwr_ctrl, osr_bits;
int ret = 0;
if (odr || osr) {
ret = reg_read(dev, BMI270_REG_GYR_CONF, &gyr_conf, 1);
if (ret != 0) {
return ret;
}
ret = reg_read(dev, BMI270_REG_PWR_CTRL, &pwr_ctrl, 1);
if (ret != 0) {
return ret;
}
}
if (odr) {
odr_bits = gyr_odr_to_reg(odr);
gyr_conf = BMI270_SET_BITS_POS_0(gyr_conf, BMI270_GYR_ODR,
odr_bits);
/* If odr_bits is 0, implies that the sampling frequency is
* 0Hz or invalid too.
*/
if (odr_bits) {
pwr_ctrl |= BMI270_PWR_CTRL_GYR_EN;
} else {
pwr_ctrl &= ~BMI270_PWR_CTRL_GYR_EN;
}
/* If the Sampling frequency (odr) >= 100Hz, enter performance
* mode else, power optimized. This also has a consequence for
* the OSR
*/
if (odr_bits >= BMI270_GYR_ODR_100_HZ) {
gyr_conf = BMI270_SET_BITS(gyr_conf,
BMI270_GYR_FILT,
BMI270_GYR_FILT_PERF_OPT);
gyr_conf = BMI270_SET_BITS(gyr_conf,
BMI270_GYR_FILT_NOISE,
BMI270_GYR_FILT_NOISE_PERF);
} else {
gyr_conf = BMI270_SET_BITS(gyr_conf,
BMI270_GYR_FILT,
BMI270_GYR_FILT_PWR_OPT);
gyr_conf = BMI270_SET_BITS(gyr_conf,
BMI270_GYR_FILT_NOISE,
BMI270_GYR_FILT_NOISE_PWR);
}
drv_dev->gyr_odr = odr_bits;
}
if (osr) {
/* osr->val2 should be unused */
switch (osr->val1) {
case 4:
osr_bits = BMI270_GYR_BWP_OSR4;
break;
case 2:
osr_bits = BMI270_GYR_BWP_OSR2;
break;
default:
osr_bits = BMI270_GYR_BWP_NORM;
break;
}
gyr_conf = BMI270_SET_BITS(gyr_conf, BMI270_GYR_BWP,
osr_bits);
}
if (odr || osr) {
ret = reg_write(dev, BMI270_REG_GYR_CONF, &gyr_conf, 1);
if (ret != 0) {
return ret;
}
/* Assuming we have advance power save enabled */
k_usleep(BMI270_TRANSC_DELAY_SUSPEND);
pwr_ctrl &= BMI270_PWR_CTRL_MSK;
ret = reg_write_with_delay(dev, BMI270_REG_PWR_CTRL, &pwr_ctrl, 1,
BMI270_INTER_WRITE_DELAY_US);
}
return ret;
}
static int set_gyro_range(const struct device *dev, const struct sensor_value *range)
{
struct bmi270_data *drv_dev = dev->data;
int ret = 0;
uint8_t gyr_range, reg;
ret = reg_read(dev, BMI270_REG_GYR_RANGE, &gyr_range, 1);
if (ret != 0) {
return ret;
}
/* range->val2 is unused */
switch (range->val1) {
case 125:
reg = BMI270_GYR_RANGE_125DPS;
drv_dev->gyr_range = 125;
break;
case 250:
reg = BMI270_GYR_RANGE_250DPS;
drv_dev->gyr_range = 250;
break;
case 500:
reg = BMI270_GYR_RANGE_500DPS;
drv_dev->gyr_range = 500;
break;
case 1000:
reg = BMI270_GYR_RANGE_1000DPS;
drv_dev->gyr_range = 1000;
break;
case 2000:
reg = BMI270_GYR_RANGE_2000DPS;
drv_dev->gyr_range = 2000;
break;
default:
return -ENOTSUP;
}
gyr_range = BMI270_SET_BITS_POS_0(gyr_range, BMI270_GYR_RANGE, reg);
ret = reg_write_with_delay(dev, BMI270_REG_GYR_RANGE, &gyr_range, 1,
BMI270_INTER_WRITE_DELAY_US);
return ret;
}
static int8_t write_config_file(const struct device *dev)
{
int8_t ret = 0;
uint16_t index = 0;
uint8_t addr_array[2] = { 0 };
/* Disable loading of the configuration */
for (index = 0; index < sizeof(bmi270_config_file);
index += BMI270_WR_LEN) {
/* Store 0 to 3 bits of address in first byte */
addr_array[0] = (uint8_t)((index / 2) & 0x0F);
/* Store 4 to 11 bits of address in the second byte */
addr_array[1] = (uint8_t)((index / 2) >> 4);
ret = reg_write_with_delay(dev, BMI270_REG_INIT_ADDR_0, addr_array, 2,
BMI270_INTER_WRITE_DELAY_US);
if (ret == 0) {
ret = reg_write_with_delay(dev, BMI270_REG_INIT_DATA,
(bmi270_config_file + index),
BMI270_WR_LEN, BMI270_INTER_WRITE_DELAY_US);
}
}
return ret;
}
static int bmi270_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
struct bmi270_data *drv_dev = dev->data;
uint8_t data[12];
int ret;
if (chan != SENSOR_CHAN_ALL) {
return -ENOTSUP;
}
ret = reg_read(dev, BMI270_REG_ACC_X_LSB, data, 12);
if (ret == 0) {
drv_dev->ax = (int16_t)sys_get_le16(&data[0]);
drv_dev->ay = (int16_t)sys_get_le16(&data[2]);
drv_dev->az = (int16_t)sys_get_le16(&data[4]);
drv_dev->gx = (int16_t)sys_get_le16(&data[6]);
drv_dev->gy = (int16_t)sys_get_le16(&data[8]);
drv_dev->gz = (int16_t)sys_get_le16(&data[10]);
} else {
drv_dev->ax = 0;
drv_dev->ay = 0;
drv_dev->az = 0;
drv_dev->gx = 0;
drv_dev->gy = 0;
drv_dev->gz = 0;
}
return ret;
}
static int bmi270_channel_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
struct bmi270_data *drv_dev = dev->data;
if (chan == SENSOR_CHAN_ACCEL_X) {
channel_accel_convert(val, drv_dev->ax, drv_dev->acc_range);
} else if (chan == SENSOR_CHAN_ACCEL_Y) {
channel_accel_convert(val, drv_dev->ay, drv_dev->acc_range);
} else if (chan == SENSOR_CHAN_ACCEL_Z) {
channel_accel_convert(val, drv_dev->az, drv_dev->acc_range);
} else if (chan == SENSOR_CHAN_ACCEL_XYZ) {
channel_accel_convert(&val[0], drv_dev->ax,
drv_dev->acc_range);
channel_accel_convert(&val[1], drv_dev->ay,
drv_dev->acc_range);
channel_accel_convert(&val[2], drv_dev->az,
drv_dev->acc_range);
} else if (chan == SENSOR_CHAN_GYRO_X) {
channel_gyro_convert(val, drv_dev->gx, drv_dev->gyr_range);
} else if (chan == SENSOR_CHAN_GYRO_Y) {
channel_gyro_convert(val, drv_dev->gy, drv_dev->gyr_range);
} else if (chan == SENSOR_CHAN_GYRO_Z) {
channel_gyro_convert(val, drv_dev->gz, drv_dev->gyr_range);
} else if (chan == SENSOR_CHAN_GYRO_XYZ) {
channel_gyro_convert(&val[0], drv_dev->gx,
drv_dev->gyr_range);
channel_gyro_convert(&val[1], drv_dev->gy,
drv_dev->gyr_range);
channel_gyro_convert(&val[2], drv_dev->gz,
drv_dev->gyr_range);
} else {
return -ENOTSUP;
}
return 0;
}
static int bmi270_attr_set(const struct device *dev, enum sensor_channel chan,
enum sensor_attribute attr, const struct sensor_value *val)
{
int ret = -ENOTSUP;
if ((chan == SENSOR_CHAN_ACCEL_X) || (chan == SENSOR_CHAN_ACCEL_Y)
|| (chan == SENSOR_CHAN_ACCEL_Z)
|| (chan == SENSOR_CHAN_ACCEL_XYZ)) {
switch (attr) {
case SENSOR_ATTR_SAMPLING_FREQUENCY:
ret = set_accel_odr_osr(dev, val, NULL);
break;
case SENSOR_ATTR_OVERSAMPLING:
ret = set_accel_odr_osr(dev, NULL, val);
break;
case SENSOR_ATTR_FULL_SCALE:
ret = set_accel_range(dev, val);
break;
default:
ret = -ENOTSUP;
}
} else if ((chan == SENSOR_CHAN_GYRO_X) || (chan == SENSOR_CHAN_GYRO_Y)
|| (chan == SENSOR_CHAN_GYRO_Z)
|| (chan == SENSOR_CHAN_GYRO_XYZ)) {
switch (attr) {
case SENSOR_ATTR_SAMPLING_FREQUENCY:
ret = set_gyro_odr_osr(dev, val, NULL);
break;
case SENSOR_ATTR_OVERSAMPLING:
ret = set_gyro_odr_osr(dev, NULL, val);
break;
case SENSOR_ATTR_FULL_SCALE:
ret = set_gyro_range(dev, val);
break;
default:
ret = -ENOTSUP;
}
} else {
ret = -ENOTSUP;
}
return ret;
}
static int bmi270_init(const struct device *dev)
{
int ret;
struct bmi270_data *drv_dev = dev->data;
const struct bmi270_dev_config *cfg = dev->config;
uint8_t chip_id;
uint8_t soft_reset_cmd;
uint8_t init_ctrl;
uint8_t msg;
uint8_t tries;
uint8_t adv_pwr_save;
if (!device_is_ready(cfg->i2c.bus)) {
LOG_ERR("I2C bus device not ready");
return -ENODEV;
}
drv_dev->acc_odr = BMI270_ACC_ODR_100_HZ;
drv_dev->acc_range = 8;
drv_dev->gyr_odr = BMI270_GYR_ODR_200_HZ;
drv_dev->gyr_range = 2000;
k_usleep(BMI270_POWER_ON_TIME);
ret = reg_read(dev, BMI270_REG_CHIP_ID, &chip_id, 1);
if (ret != 0) {
return ret;
}
if (chip_id != BMI270_CHIP_ID) {
LOG_ERR("Unexpected chip id (%x). Expected (%x)",
chip_id, BMI270_CHIP_ID);
return -EIO;
}
soft_reset_cmd = BMI270_CMD_SOFT_RESET;
ret = reg_write(dev, BMI270_REG_CMD, &soft_reset_cmd, 1);
if (ret != 0) {
return ret;
}
k_usleep(BMI270_SOFT_RESET_TIME);
ret = reg_read(dev, BMI270_REG_PWR_CONF, &adv_pwr_save, 1);
if (ret != 0) {
return ret;
}
adv_pwr_save = BMI270_SET_BITS_POS_0(adv_pwr_save,
BMI270_PWR_CONF_ADV_PWR_SAVE,
BMI270_PWR_CONF_ADV_PWR_SAVE_DIS);
ret = reg_write_with_delay(dev, BMI270_REG_PWR_CONF, &adv_pwr_save, 1,
BMI270_INTER_WRITE_DELAY_US);
if (ret != 0) {
return ret;
}
init_ctrl = BMI270_PREPARE_CONFIG_LOAD;
ret = reg_write(dev, BMI270_REG_INIT_CTRL, &init_ctrl, 1);
if (ret != 0) {
return ret;
}
ret = write_config_file(dev);
if (ret != 0) {
return ret;
}
init_ctrl = BMI270_COMPLETE_CONFIG_LOAD;
ret = reg_write(dev, BMI270_REG_INIT_CTRL, &init_ctrl, 1);
if (ret != 0) {
return ret;
}
/* Timeout after BMI270_CONFIG_FILE_RETRIES x
* BMI270_CONFIG_FILE_POLL_PERIOD_US microseconds.
* If tries is 0 by the end of the loop,
* report an error
*/
for (tries = 0; tries <= BMI270_CONFIG_FILE_RETRIES; tries++) {
ret = reg_read(dev, BMI270_REG_INTERNAL_STATUS, &msg, 1);
if (ret != 0) {
return ret;
}
msg &= BMI270_INST_MESSAGE_MSK;
if (msg == BMI270_INST_MESSAGE_INIT_OK) {
break;
}
k_usleep(BMI270_CONFIG_FILE_POLL_PERIOD_US);
}
if (tries == 0) {
return -EIO;
}
adv_pwr_save = BMI270_SET_BITS_POS_0(adv_pwr_save,
BMI270_PWR_CONF_ADV_PWR_SAVE,
BMI270_PWR_CONF_ADV_PWR_SAVE_EN);
ret = reg_write_with_delay(dev, BMI270_REG_PWR_CONF, &adv_pwr_save, 1,
BMI270_INTER_WRITE_DELAY_US);
return ret;
}
static const struct sensor_driver_api bmi270_driver_api = {
.sample_fetch = bmi270_sample_fetch,
.channel_get = bmi270_channel_get,
.attr_set = bmi270_attr_set
};
#define BMI270_CREATE_INST(inst) \
\
static struct bmi270_data bmi270_drv_##inst; \
\
static const struct bmi270_dev_config bmi270_config_##inst = { \
.i2c = I2C_DT_SPEC_INST_GET(inst), \
}; \
\
DEVICE_DT_INST_DEFINE(inst, \
bmi270_init, \
NULL, \
&bmi270_drv_##inst, \
&bmi270_config_##inst, \
POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, \
&bmi270_driver_api);
DT_INST_FOREACH_STATUS_OKAY(BMI270_CREATE_INST)