| /* Bosch BMI160 inertial measurement unit driver |
| * |
| * Copyright (c) 2016 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Datasheet: |
| * http://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMI160-DS000-07.pdf |
| */ |
| |
| #include <zephyr/init.h> |
| #include <zephyr/drivers/i2c.h> |
| #include <zephyr/drivers/sensor.h> |
| #include <zephyr/sys/byteorder.h> |
| #include <zephyr/kernel.h> |
| #include <zephyr/sys/__assert.h> |
| #include <zephyr/pm/pm.h> |
| #include <zephyr/pm/device.h> |
| #include <zephyr/pm/device_runtime.h> |
| #include <zephyr/logging/log.h> |
| |
| #include "bmi160.h" |
| |
| LOG_MODULE_REGISTER(BMI160, CONFIG_SENSOR_LOG_LEVEL); |
| |
| #if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 0 |
| #warning "BMI160 driver enabled without any devices" |
| #endif |
| |
| #if BMI160_BUS_SPI |
| static int bmi160_transceive(const struct device *dev, uint8_t reg, |
| bool write, void *buf, size_t length) |
| { |
| const struct bmi160_cfg *cfg = dev->config; |
| const struct spi_buf tx_buf[2] = { |
| { |
| .buf = ®, |
| .len = 1 |
| }, |
| { |
| .buf = buf, |
| .len = length |
| } |
| }; |
| const struct spi_buf_set tx = { |
| .buffers = tx_buf, |
| .count = buf ? 2 : 1 |
| }; |
| |
| if (!write) { |
| const struct spi_buf_set rx = { |
| .buffers = tx_buf, |
| .count = 2 |
| }; |
| |
| return spi_transceive_dt(&cfg->bus.spi, &tx, &rx); |
| } |
| |
| return spi_write_dt(&cfg->bus.spi, &tx); |
| } |
| |
| bool bmi160_bus_ready_spi(const struct device *dev) |
| { |
| const struct bmi160_cfg *cfg = dev->config; |
| |
| return spi_is_ready_dt(&cfg->bus.spi); |
| } |
| |
| int bmi160_read_spi(const struct device *dev, |
| uint8_t reg_addr, void *buf, uint8_t len) |
| { |
| return bmi160_transceive(dev, reg_addr | BMI160_REG_READ, false, |
| buf, len); |
| } |
| |
| int bmi160_write_spi(const struct device *dev, |
| uint8_t reg_addr, void *buf, uint8_t len) |
| { |
| return bmi160_transceive(dev, reg_addr & BMI160_REG_MASK, true, |
| buf, len); |
| } |
| |
| static const struct bmi160_bus_io bmi160_bus_io_spi = { |
| .ready = bmi160_bus_ready_spi, |
| .read = bmi160_read_spi, |
| .write = bmi160_write_spi, |
| }; |
| #endif /* BMI160_BUS_SPI */ |
| |
| #if BMI160_BUS_I2C |
| |
| bool bmi160_bus_ready_i2c(const struct device *dev) |
| { |
| const struct bmi160_cfg *cfg = dev->config; |
| |
| return device_is_ready(cfg->bus.i2c.bus); |
| } |
| |
| int bmi160_read_i2c(const struct device *dev, |
| uint8_t reg_addr, void *buf, uint8_t len) |
| { |
| const struct bmi160_cfg *cfg = dev->config; |
| |
| return i2c_burst_read_dt(&cfg->bus.i2c, reg_addr, buf, len); |
| } |
| |
| int bmi160_write_i2c(const struct device *dev, |
| uint8_t reg_addr, void *buf, uint8_t len) |
| { |
| const struct bmi160_cfg *cfg = dev->config; |
| |
| return i2c_burst_write_dt(&cfg->bus.i2c, reg_addr, buf, len); |
| } |
| |
| static const struct bmi160_bus_io bmi160_bus_io_i2c = { |
| .ready = bmi160_bus_ready_i2c, |
| .read = bmi160_read_i2c, |
| .write = bmi160_write_i2c, |
| }; |
| #endif |
| |
| int bmi160_read(const struct device *dev, uint8_t reg_addr, void *buf, |
| uint8_t len) |
| { |
| const struct bmi160_cfg *cfg = dev->config; |
| |
| return cfg->bus_io->read(dev, reg_addr, buf, len); |
| } |
| |
| int bmi160_byte_read(const struct device *dev, uint8_t reg_addr, uint8_t *byte) |
| { |
| return bmi160_read(dev, reg_addr, byte, 1); |
| } |
| |
| static int bmi160_word_read(const struct device *dev, uint8_t reg_addr, |
| uint16_t *word) |
| { |
| int rc; |
| |
| rc = bmi160_read(dev, reg_addr, word, 2); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| *word = sys_le16_to_cpu(*word); |
| |
| return 0; |
| } |
| |
| int bmi160_write(const struct device *dev, uint8_t reg_addr, void *buf, |
| uint8_t len) |
| { |
| const struct bmi160_cfg *cfg = dev->config; |
| |
| return cfg->bus_io->write(dev, reg_addr, buf, len); |
| } |
| |
| int bmi160_byte_write(const struct device *dev, uint8_t reg_addr, |
| uint8_t byte) |
| { |
| return bmi160_write(dev, reg_addr & BMI160_REG_MASK, &byte, 1); |
| } |
| |
| int bmi160_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 bmi160_write(dev, reg_addr & BMI160_REG_MASK, tx_word, 2); |
| } |
| |
| int bmi160_reg_field_update(const struct device *dev, uint8_t reg_addr, |
| uint8_t pos, uint8_t mask, uint8_t val) |
| { |
| uint8_t old_val; |
| |
| if (bmi160_byte_read(dev, reg_addr, &old_val) < 0) { |
| return -EIO; |
| } |
| |
| return bmi160_byte_write(dev, reg_addr, |
| (old_val & ~mask) | ((val << pos) & mask)); |
| } |
| |
| static int bmi160_pmu_set(const struct device *dev, |
| union bmi160_pmu_status *pmu_sts) |
| { |
| struct { |
| uint8_t cmd; |
| uint16_t delay_us; /* values taken from page 82 */ |
| } cmds[] = { |
| {BMI160_CMD_PMU_MAG | pmu_sts->mag, 350}, |
| {BMI160_CMD_PMU_ACC | pmu_sts->acc, 3200}, |
| {BMI160_CMD_PMU_GYR | pmu_sts->gyr, 55000} |
| }; |
| size_t i; |
| |
| for (i = 0; i < ARRAY_SIZE(cmds); i++) { |
| union bmi160_pmu_status sts; |
| bool pmu_set = false; |
| |
| if (bmi160_byte_write(dev, BMI160_REG_CMD, cmds[i].cmd) < 0) { |
| return -EIO; |
| } |
| |
| /* |
| * Cannot use a timer here since this is called from the |
| * init function and the timeouts were not initialized yet. |
| */ |
| k_busy_wait(cmds[i].delay_us); |
| |
| /* make sure the PMU_STATUS was set, though */ |
| do { |
| if (bmi160_byte_read(dev, BMI160_REG_PMU_STATUS, |
| &sts.raw) < 0) { |
| return -EIO; |
| } |
| |
| if (i == 0) { |
| pmu_set = (pmu_sts->mag == sts.mag); |
| } else if (i == 1) { |
| pmu_set = (pmu_sts->acc == sts.acc); |
| } else { |
| pmu_set = (pmu_sts->gyr == sts.gyr); |
| } |
| |
| } while (!pmu_set); |
| } |
| |
| /* set the undersampling flag for accelerometer */ |
| return bmi160_reg_field_update(dev, BMI160_REG_ACC_CONF, |
| BMI160_ACC_CONF_US_POS, BMI160_ACC_CONF_US_MASK, |
| pmu_sts->acc != BMI160_PMU_NORMAL); |
| } |
| |
| #if defined(CONFIG_BMI160_GYRO_ODR_RUNTIME) ||\ |
| defined(CONFIG_BMI160_ACCEL_ODR_RUNTIME) |
| /* |
| * Output data rate map with allowed frequencies: |
| * freq = freq_int + freq_milli / 1000 |
| * |
| * Since we don't need a finer frequency resolution than milliHz, use uint16_t |
| * to save some flash. |
| */ |
| struct { |
| uint16_t freq_int; |
| uint16_t freq_milli; /* User should convert to uHz before setting the |
| * SENSOR_ATTR_SAMPLING_FREQUENCY attribute. |
| */ |
| } bmi160_odr_map[] = { |
| {0, 0 }, {0, 780}, {1, 562}, {3, 120}, {6, 250}, |
| {12, 500}, {25, 0 }, {50, 0 }, {100, 0 }, {200, 0 }, |
| {400, 0 }, {800, 0 }, {1600, 0 }, {3200, 0 }, |
| }; |
| |
| static int bmi160_freq_to_odr_val(uint16_t freq_int, uint16_t freq_milli) |
| { |
| size_t i; |
| |
| /* An ODR of 0 Hz is not allowed */ |
| if (freq_int == 0U && freq_milli == 0U) { |
| return -EINVAL; |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(bmi160_odr_map); i++) { |
| if (freq_int < bmi160_odr_map[i].freq_int || |
| (freq_int == bmi160_odr_map[i].freq_int && |
| freq_milli <= bmi160_odr_map[i].freq_milli)) { |
| return i; |
| } |
| } |
| |
| return -EINVAL; |
| } |
| #endif |
| |
| #if defined(CONFIG_BMI160_ACCEL_ODR_RUNTIME) |
| static int bmi160_acc_odr_set(const struct device *dev, uint16_t freq_int, |
| uint16_t freq_milli) |
| { |
| struct bmi160_data *data = dev->data; |
| int odr = bmi160_freq_to_odr_val(freq_int, freq_milli); |
| |
| if (odr < 0) { |
| return odr; |
| } |
| |
| /* some odr values cannot be set in certain power modes */ |
| if ((data->pmu_sts.acc == BMI160_PMU_NORMAL && |
| odr < BMI160_ODR_25_2) || |
| (data->pmu_sts.acc == BMI160_PMU_LOW_POWER && |
| odr < BMI160_ODR_25_32) || odr > BMI160_ODR_1600) { |
| return -ENOTSUP; |
| } |
| |
| return bmi160_reg_field_update(dev, BMI160_REG_ACC_CONF, |
| BMI160_ACC_CONF_ODR_POS, |
| BMI160_ACC_CONF_ODR_MASK, |
| (uint8_t) odr); |
| } |
| #endif |
| |
| static const struct bmi160_range bmi160_acc_range_map[] = { |
| {2, BMI160_ACC_RANGE_2G}, |
| {4, BMI160_ACC_RANGE_4G}, |
| {8, BMI160_ACC_RANGE_8G}, |
| {16, BMI160_ACC_RANGE_16G}, |
| }; |
| #define BMI160_ACC_RANGE_MAP_SIZE ARRAY_SIZE(bmi160_acc_range_map) |
| |
| static const struct bmi160_range bmi160_gyr_range_map[] = { |
| {2000, BMI160_GYR_RANGE_2000DPS}, |
| {1000, BMI160_GYR_RANGE_1000DPS}, |
| {500, BMI160_GYR_RANGE_500DPS}, |
| {250, BMI160_GYR_RANGE_250DPS}, |
| {125, BMI160_GYR_RANGE_125DPS}, |
| }; |
| #define BMI160_GYR_RANGE_MAP_SIZE ARRAY_SIZE(bmi160_gyr_range_map) |
| |
| #if defined(CONFIG_BMI160_ACCEL_RANGE_RUNTIME) ||\ |
| defined(CONFIG_BMI160_GYRO_RANGE_RUNTIME) |
| static int32_t bmi160_range_to_reg_val(uint16_t range, |
| const struct bmi160_range *range_map, |
| uint16_t range_map_size) |
| { |
| int i; |
| |
| for (i = 0; i < range_map_size; i++) { |
| if (range <= range_map[i].range) { |
| return range_map[i].reg_val; |
| } |
| } |
| |
| return -EINVAL; |
| } |
| #endif |
| |
| static int32_t bmi160_reg_val_to_range(uint8_t reg_val, |
| const struct bmi160_range *range_map, |
| uint16_t range_map_size) |
| { |
| int i; |
| |
| for (i = 0; i < range_map_size; i++) { |
| if (reg_val == range_map[i].reg_val) { |
| return range_map[i].range; |
| } |
| } |
| |
| return -EINVAL; |
| } |
| |
| int32_t bmi160_acc_reg_val_to_range(uint8_t reg_val) |
| { |
| return bmi160_reg_val_to_range(reg_val, bmi160_acc_range_map, |
| BMI160_ACC_RANGE_MAP_SIZE); |
| } |
| |
| int32_t bmi160_gyr_reg_val_to_range(uint8_t reg_val) |
| { |
| return bmi160_reg_val_to_range(reg_val, bmi160_gyr_range_map, |
| BMI160_GYR_RANGE_MAP_SIZE); |
| } |
| |
| static int bmi160_do_calibration(const struct device *dev, uint8_t foc_conf) |
| { |
| if (bmi160_byte_write(dev, BMI160_REG_FOC_CONF, foc_conf) < 0) { |
| return -EIO; |
| } |
| |
| if (bmi160_byte_write(dev, BMI160_REG_CMD, BMI160_CMD_START_FOC) < 0) { |
| return -EIO; |
| } |
| |
| k_busy_wait(250000); /* calibration takes a maximum of 250ms */ |
| |
| return 0; |
| } |
| |
| #if defined(CONFIG_BMI160_ACCEL_RANGE_RUNTIME) |
| static int bmi160_acc_range_set(const struct device *dev, int32_t range) |
| { |
| struct bmi160_data *data = dev->data; |
| int32_t reg_val = bmi160_range_to_reg_val(range, |
| bmi160_acc_range_map, |
| BMI160_ACC_RANGE_MAP_SIZE); |
| |
| if (reg_val < 0) { |
| return reg_val; |
| } |
| |
| if (bmi160_byte_write(dev, BMI160_REG_ACC_RANGE, reg_val & 0xff) < 0) { |
| return -EIO; |
| } |
| |
| data->scale.acc = BMI160_ACC_SCALE(range); |
| |
| return 0; |
| } |
| #endif |
| |
| #if !defined(CONFIG_BMI160_ACCEL_PMU_SUSPEND) |
| /* |
| * Accelerometer offset scale, taken from pg. 79, converted to micro m/s^2: |
| * 3.9 * 9.80665 * 1000 |
| */ |
| #define BMI160_ACC_OFS_LSB 38246 |
| static int bmi160_acc_ofs_set(const struct device *dev, |
| enum sensor_channel chan, |
| const struct sensor_value *ofs) |
| { |
| uint8_t reg_addr[] = { |
| BMI160_REG_OFFSET_ACC_X, |
| BMI160_REG_OFFSET_ACC_Y, |
| BMI160_REG_OFFSET_ACC_Z |
| }; |
| int i; |
| int32_t ofs_u; |
| int8_t reg_val; |
| |
| /* we need the offsets for all axis */ |
| if (chan != SENSOR_CHAN_ACCEL_XYZ) { |
| return -ENOTSUP; |
| } |
| |
| for (i = 0; i < BMI160_AXES; i++, ofs++) { |
| /* convert offset to micro m/s^2 */ |
| ofs_u = ofs->val1 * 1000000ULL + ofs->val2; |
| reg_val = ofs_u / BMI160_ACC_OFS_LSB; |
| |
| if (bmi160_byte_write(dev, reg_addr[i], reg_val) < 0) { |
| return -EIO; |
| } |
| } |
| |
| /* activate accel HW compensation */ |
| return bmi160_reg_field_update(dev, BMI160_REG_OFFSET_EN, |
| BMI160_ACC_OFS_EN_POS, |
| BIT(BMI160_ACC_OFS_EN_POS), 1); |
| } |
| |
| static int bmi160_acc_calibrate(const struct device *dev, |
| enum sensor_channel chan, |
| const struct sensor_value *xyz_calib_value) |
| { |
| struct bmi160_data *data = dev->data; |
| uint8_t foc_pos[] = { |
| BMI160_FOC_ACC_X_POS, |
| BMI160_FOC_ACC_Y_POS, |
| BMI160_FOC_ACC_Z_POS, |
| }; |
| int i; |
| uint8_t reg_val = 0U; |
| |
| /* Calibration has to be done in normal mode. */ |
| if (data->pmu_sts.acc != BMI160_PMU_NORMAL) { |
| return -ENOTSUP; |
| } |
| |
| /* |
| * Hardware calibration is done knowing the expected values on all axis. |
| */ |
| if (chan != SENSOR_CHAN_ACCEL_XYZ) { |
| return -ENOTSUP; |
| } |
| |
| for (i = 0; i < BMI160_AXES; i++, xyz_calib_value++) { |
| int32_t accel_g; |
| uint8_t accel_val; |
| |
| accel_g = sensor_ms2_to_g(xyz_calib_value); |
| if (accel_g == 0) { |
| accel_val = 3U; |
| } else if (accel_g == 1) { |
| accel_val = 1U; |
| } else if (accel_g == -1) { |
| accel_val = 2U; |
| } else { |
| accel_val = 0U; |
| } |
| reg_val |= (accel_val << foc_pos[i]); |
| } |
| |
| if (bmi160_do_calibration(dev, reg_val) < 0) { |
| return -EIO; |
| } |
| |
| /* activate accel HW compensation */ |
| return bmi160_reg_field_update(dev, BMI160_REG_OFFSET_EN, |
| BMI160_ACC_OFS_EN_POS, |
| BIT(BMI160_ACC_OFS_EN_POS), 1); |
| } |
| |
| static int bmi160_acc_config(const struct device *dev, |
| enum sensor_channel chan, |
| enum sensor_attribute attr, |
| const struct sensor_value *val) |
| { |
| switch (attr) { |
| #if defined(CONFIG_BMI160_ACCEL_RANGE_RUNTIME) |
| case SENSOR_ATTR_FULL_SCALE: |
| return bmi160_acc_range_set(dev, sensor_ms2_to_g(val)); |
| #endif |
| #if defined(CONFIG_BMI160_ACCEL_ODR_RUNTIME) |
| case SENSOR_ATTR_SAMPLING_FREQUENCY: |
| return bmi160_acc_odr_set(dev, val->val1, val->val2 / 1000); |
| #endif |
| case SENSOR_ATTR_OFFSET: |
| return bmi160_acc_ofs_set(dev, chan, val); |
| case SENSOR_ATTR_CALIB_TARGET: |
| return bmi160_acc_calibrate(dev, chan, val); |
| #if defined(CONFIG_BMI160_TRIGGER) |
| case SENSOR_ATTR_SLOPE_TH: |
| case SENSOR_ATTR_SLOPE_DUR: |
| return bmi160_acc_slope_config(dev, attr, val); |
| #endif |
| default: |
| LOG_DBG("Accel attribute not supported."); |
| return -ENOTSUP; |
| } |
| |
| return 0; |
| } |
| #endif /* !defined(CONFIG_BMI160_ACCEL_PMU_SUSPEND) */ |
| |
| #if defined(CONFIG_BMI160_GYRO_ODR_RUNTIME) |
| static int bmi160_gyr_odr_set(const struct device *dev, uint16_t freq_int, |
| uint16_t freq_milli) |
| { |
| int odr = bmi160_freq_to_odr_val(freq_int, freq_milli); |
| |
| if (odr < 0) { |
| return odr; |
| } |
| |
| if (odr < BMI160_ODR_25 || odr > BMI160_ODR_3200) { |
| return -ENOTSUP; |
| } |
| |
| return bmi160_reg_field_update(dev, BMI160_REG_GYR_CONF, |
| BMI160_GYR_CONF_ODR_POS, |
| BMI160_GYR_CONF_ODR_MASK, |
| (uint8_t) odr); |
| } |
| #endif |
| |
| #if defined(CONFIG_BMI160_GYRO_RANGE_RUNTIME) |
| static int bmi160_gyr_range_set(const struct device *dev, uint16_t range) |
| { |
| struct bmi160_data *data = dev->data; |
| int32_t reg_val = bmi160_range_to_reg_val(range, |
| bmi160_gyr_range_map, |
| BMI160_GYR_RANGE_MAP_SIZE); |
| |
| if (reg_val < 0) { |
| return reg_val; |
| } |
| |
| if (bmi160_byte_write(dev, BMI160_REG_GYR_RANGE, reg_val) < 0) { |
| return -EIO; |
| } |
| |
| data->scale.gyr = BMI160_GYR_SCALE(range); |
| |
| return 0; |
| } |
| #endif |
| |
| #if !defined(CONFIG_BMI160_GYRO_PMU_SUSPEND) |
| /* |
| * Gyro offset scale, taken from pg. 79, converted to micro rad/s: |
| * 0.061 * (pi / 180) * 1000000, where pi = 3.141592 |
| */ |
| #define BMI160_GYR_OFS_LSB 1065 |
| static int bmi160_gyr_ofs_set(const struct device *dev, |
| enum sensor_channel chan, |
| const struct sensor_value *ofs) |
| { |
| struct { |
| uint8_t lsb_addr; |
| uint8_t msb_pos; |
| } ofs_desc[] = { |
| {BMI160_REG_OFFSET_GYR_X, BMI160_GYR_MSB_OFS_X_POS}, |
| {BMI160_REG_OFFSET_GYR_Y, BMI160_GYR_MSB_OFS_Y_POS}, |
| {BMI160_REG_OFFSET_GYR_Z, BMI160_GYR_MSB_OFS_Z_POS}, |
| }; |
| int i; |
| int32_t ofs_u; |
| int16_t val; |
| |
| /* we need the offsets for all axis */ |
| if (chan != SENSOR_CHAN_GYRO_XYZ) { |
| return -ENOTSUP; |
| } |
| |
| for (i = 0; i < BMI160_AXES; i++, ofs++) { |
| /* convert offset to micro rad/s */ |
| ofs_u = ofs->val1 * 1000000ULL + ofs->val2; |
| |
| val = ofs_u / BMI160_GYR_OFS_LSB; |
| |
| /* |
| * The gyro offset is a 10 bit two-complement value. Make sure |
| * the passed value is within limits. |
| */ |
| if (val < -512 || val > 512) { |
| return -EINVAL; |
| } |
| |
| /* write the LSB */ |
| if (bmi160_byte_write(dev, ofs_desc[i].lsb_addr, |
| val & 0xff) < 0) { |
| return -EIO; |
| } |
| |
| /* write the MSB */ |
| if (bmi160_reg_field_update(dev, BMI160_REG_OFFSET_EN, |
| ofs_desc[i].msb_pos, |
| 0x3 << ofs_desc[i].msb_pos, |
| (val >> 8) & 0x3) < 0) { |
| return -EIO; |
| } |
| } |
| |
| /* activate gyro HW compensation */ |
| return bmi160_reg_field_update(dev, BMI160_REG_OFFSET_EN, |
| BMI160_GYR_OFS_EN_POS, |
| BIT(BMI160_GYR_OFS_EN_POS), 1); |
| } |
| |
| static int bmi160_gyr_calibrate(const struct device *dev, |
| enum sensor_channel chan) |
| { |
| struct bmi160_data *data = dev->data; |
| |
| ARG_UNUSED(chan); |
| |
| /* Calibration has to be done in normal mode. */ |
| if (data->pmu_sts.gyr != BMI160_PMU_NORMAL) { |
| return -ENOTSUP; |
| } |
| |
| if (bmi160_do_calibration(dev, BIT(BMI160_FOC_GYR_EN_POS)) < 0) { |
| return -EIO; |
| } |
| |
| /* activate gyro HW compensation */ |
| return bmi160_reg_field_update(dev, BMI160_REG_OFFSET_EN, |
| BMI160_GYR_OFS_EN_POS, |
| BIT(BMI160_GYR_OFS_EN_POS), 1); |
| } |
| |
| static int bmi160_gyr_config(const struct device *dev, |
| enum sensor_channel chan, |
| enum sensor_attribute attr, |
| const struct sensor_value *val) |
| { |
| switch (attr) { |
| #if defined(CONFIG_BMI160_GYRO_RANGE_RUNTIME) |
| case SENSOR_ATTR_FULL_SCALE: |
| return bmi160_gyr_range_set(dev, sensor_rad_to_degrees(val)); |
| #endif |
| #if defined(CONFIG_BMI160_GYRO_ODR_RUNTIME) |
| case SENSOR_ATTR_SAMPLING_FREQUENCY: |
| return bmi160_gyr_odr_set(dev, val->val1, val->val2 / 1000); |
| #endif |
| case SENSOR_ATTR_OFFSET: |
| return bmi160_gyr_ofs_set(dev, chan, val); |
| |
| case SENSOR_ATTR_CALIB_TARGET: |
| return bmi160_gyr_calibrate(dev, chan); |
| |
| default: |
| LOG_DBG("Gyro attribute not supported."); |
| return -ENOTSUP; |
| } |
| |
| return 0; |
| } |
| #endif /* !defined(CONFIG_BMI160_GYRO_PMU_SUSPEND) */ |
| |
| static int bmi160_attr_set(const struct device *dev, enum sensor_channel chan, |
| enum sensor_attribute attr, |
| const struct sensor_value *val) |
| { |
| switch (chan) { |
| #if !defined(CONFIG_BMI160_GYRO_PMU_SUSPEND) |
| case SENSOR_CHAN_GYRO_X: |
| case SENSOR_CHAN_GYRO_Y: |
| case SENSOR_CHAN_GYRO_Z: |
| case SENSOR_CHAN_GYRO_XYZ: |
| return bmi160_gyr_config(dev, chan, attr, val); |
| #endif |
| #if !defined(CONFIG_BMI160_ACCEL_PMU_SUSPEND) |
| case SENSOR_CHAN_ACCEL_X: |
| case SENSOR_CHAN_ACCEL_Y: |
| case SENSOR_CHAN_ACCEL_Z: |
| case SENSOR_CHAN_ACCEL_XYZ: |
| return bmi160_acc_config(dev, chan, attr, val); |
| #endif |
| default: |
| LOG_DBG("attr_set() not supported on this channel."); |
| return -ENOTSUP; |
| } |
| |
| return 0; |
| } |
| |
| static int bmi160_sample_fetch(const struct device *dev, |
| enum sensor_channel chan) |
| { |
| struct bmi160_data *data = dev->data; |
| uint8_t status; |
| size_t i; |
| int ret = 0; |
| enum pm_device_state pm_state; |
| |
| (void)pm_device_state_get(dev, &pm_state); |
| if (pm_state != PM_DEVICE_STATE_ACTIVE) { |
| LOG_DBG("Device is suspended, fetch is unavailable"); |
| ret = -EIO; |
| goto out; |
| } |
| |
| __ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL); |
| |
| status = 0; |
| while ((status & BMI160_DATA_READY_BIT_MASK) == 0) { |
| |
| if (bmi160_byte_read(dev, BMI160_REG_STATUS, &status) < 0) { |
| ret = -EIO; |
| goto out; |
| } |
| } |
| |
| if (bmi160_read(dev, BMI160_SAMPLE_BURST_READ_ADDR, data->sample.raw, |
| BMI160_BUF_SIZE) < 0) { |
| ret = -EIO; |
| goto out; |
| } |
| |
| /* convert samples to cpu endianness */ |
| for (i = 0; i < BMI160_SAMPLE_SIZE; i += 2) { |
| uint16_t *sample = |
| (uint16_t *) &data->sample.raw[i]; |
| |
| *sample = sys_le16_to_cpu(*sample); |
| } |
| |
| out: |
| return ret; |
| } |
| |
| static void bmi160_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 bmi160_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: |
| case SENSOR_CHAN_GYRO_X: |
| ofs_start = ofs_stop = 0U; |
| break; |
| case SENSOR_CHAN_ACCEL_Y: |
| case SENSOR_CHAN_GYRO_Y: |
| ofs_start = ofs_stop = 1U; |
| break; |
| case SENSOR_CHAN_ACCEL_Z: |
| case SENSOR_CHAN_GYRO_Z: |
| ofs_start = ofs_stop = 2U; |
| break; |
| default: |
| ofs_start = 0U; ofs_stop = 2U; |
| break; |
| } |
| |
| for (i = ofs_start; i <= ofs_stop ; i++, val++) { |
| bmi160_to_fixed_point(raw_xyz[i], scale, val); |
| } |
| } |
| |
| #if !defined(CONFIG_BMI160_GYRO_PMU_SUSPEND) |
| static inline void bmi160_gyr_channel_get(const struct device *dev, |
| enum sensor_channel chan, |
| struct sensor_value *val) |
| { |
| struct bmi160_data *data = dev->data; |
| |
| bmi160_channel_convert(chan, data->scale.gyr, data->sample.gyr, val); |
| } |
| #endif |
| |
| #if !defined(CONFIG_BMI160_ACCEL_PMU_SUSPEND) |
| static inline void bmi160_acc_channel_get(const struct device *dev, |
| enum sensor_channel chan, |
| struct sensor_value *val) |
| { |
| struct bmi160_data *data = dev->data; |
| |
| bmi160_channel_convert(chan, data->scale.acc, data->sample.acc, val); |
| } |
| #endif |
| |
| static int bmi160_temp_channel_get(const struct device *dev, |
| struct sensor_value *val) |
| { |
| uint16_t temp_raw = 0U; |
| int32_t temp_micro = 0; |
| struct bmi160_data *data = dev->data; |
| |
| if (data->pmu_sts.raw == 0U) { |
| return -EINVAL; |
| } |
| |
| if (bmi160_word_read(dev, BMI160_REG_TEMPERATURE0, &temp_raw) < 0) { |
| return -EIO; |
| } |
| |
| /* the scale is 1/2^9/LSB = 1953 micro degrees */ |
| temp_micro = BMI160_TEMP_OFFSET * 1000000ULL + temp_raw * 1953ULL; |
| |
| val->val1 = temp_micro / 1000000ULL; |
| val->val2 = temp_micro % 1000000ULL; |
| |
| return 0; |
| } |
| |
| static int bmi160_channel_get(const struct device *dev, |
| enum sensor_channel chan, |
| struct sensor_value *val) |
| { |
| switch (chan) { |
| #if !defined(CONFIG_BMI160_GYRO_PMU_SUSPEND) |
| case SENSOR_CHAN_GYRO_X: |
| case SENSOR_CHAN_GYRO_Y: |
| case SENSOR_CHAN_GYRO_Z: |
| case SENSOR_CHAN_GYRO_XYZ: |
| bmi160_gyr_channel_get(dev, chan, val); |
| return 0; |
| #endif |
| #if !defined(CONFIG_BMI160_ACCEL_PMU_SUSPEND) |
| case SENSOR_CHAN_ACCEL_X: |
| case SENSOR_CHAN_ACCEL_Y: |
| case SENSOR_CHAN_ACCEL_Z: |
| case SENSOR_CHAN_ACCEL_XYZ: |
| bmi160_acc_channel_get(dev, chan, val); |
| return 0; |
| #endif |
| case SENSOR_CHAN_DIE_TEMP: |
| return bmi160_temp_channel_get(dev, val); |
| default: |
| LOG_DBG("Channel not supported."); |
| return -ENOTSUP; |
| } |
| |
| return 0; |
| } |
| |
| static const struct sensor_driver_api bmi160_api = { |
| .attr_set = bmi160_attr_set, |
| #ifdef CONFIG_BMI160_TRIGGER |
| .trigger_set = bmi160_trigger_set, |
| #endif |
| .sample_fetch = bmi160_sample_fetch, |
| .channel_get = bmi160_channel_get, |
| }; |
| |
| |
| static inline int bmi160_resume(const struct device *dev) |
| { |
| struct bmi160_data *data = dev->data; |
| |
| return bmi160_pmu_set(dev, &data->pmu_sts); |
| } |
| |
| static inline int bmi160_suspend(const struct device *dev) |
| { |
| struct bmi160_data *data = dev->data; |
| |
| /* Suspend everything */ |
| union bmi160_pmu_status st = { |
| .acc = BMI160_PMU_SUSPEND, |
| .gyr = BMI160_PMU_SUSPEND, |
| .mag = BMI160_PMU_SUSPEND, |
| }; |
| |
| int ret = bmi160_pmu_set(dev, &st); |
| |
| if (ret == 0) { |
| memset(data->sample.raw, 0, sizeof(data->sample.raw)); |
| } |
| return ret; |
| } |
| |
| int bmi160_init(const struct device *dev) |
| { |
| const struct bmi160_cfg *cfg = dev->config; |
| struct bmi160_data *data = dev->data; |
| uint8_t val = 0U; |
| int32_t acc_range, gyr_range; |
| |
| if (!cfg->bus_io->ready(dev)) { |
| LOG_ERR("Bus not ready"); |
| return -EINVAL; |
| } |
| |
| /* reboot the chip */ |
| if (bmi160_byte_write(dev, BMI160_REG_CMD, BMI160_CMD_SOFT_RESET) < 0) { |
| LOG_DBG("Cannot reboot chip."); |
| return -EIO; |
| } |
| |
| k_busy_wait(1000); |
| |
| /* do a dummy read from 0x7F to activate SPI */ |
| if (bmi160_byte_read(dev, BMI160_SPI_START, &val) < 0) { |
| LOG_DBG("Cannot read from 0x7F.."); |
| return -EIO; |
| } |
| |
| k_busy_wait(150); |
| |
| if (bmi160_byte_read(dev, BMI160_REG_CHIPID, &val) < 0) { |
| LOG_DBG("Failed to read chip id."); |
| return -EIO; |
| } |
| |
| if (val != BMI160_CHIP_ID) { |
| LOG_DBG("Unsupported chip detected (0x%x)!", val); |
| return -ENODEV; |
| } |
| |
| /* set default PMU for gyro, accelerometer */ |
| data->pmu_sts.gyr = BMI160_DEFAULT_PMU_GYR; |
| data->pmu_sts.acc = BMI160_DEFAULT_PMU_ACC; |
| |
| /* compass not supported, yet */ |
| data->pmu_sts.mag = BMI160_PMU_SUSPEND; |
| |
| /* Start in a suspended state (never turning on the mems sensors) if |
| * PM_DEVICE_RUNTIME is enabled. |
| */ |
| #ifdef CONFIG_PM_DEVICE_RUNTIME |
| pm_device_init_suspended(dev); |
| |
| int ret = pm_device_runtime_enable(dev); |
| |
| if (ret < 0 && ret != -ENOSYS) { |
| LOG_ERR("Failed to enabled runtime power management"); |
| return -EIO; |
| } |
| #else |
| |
| /* |
| * The next command will take around 100ms (contains some necessary busy |
| * waits), but we cannot do it in a separate thread since we need to |
| * guarantee the BMI is up and running, before the app's main() is |
| * called. |
| */ |
| if (bmi160_pmu_set(dev, &data->pmu_sts) < 0) { |
| LOG_DBG("Failed to set power mode."); |
| return -EIO; |
| } |
| #endif |
| |
| /* set accelerometer default range */ |
| if (bmi160_byte_write(dev, BMI160_REG_ACC_RANGE, |
| BMI160_DEFAULT_RANGE_ACC) < 0) { |
| LOG_DBG("Cannot set default range for accelerometer."); |
| return -EIO; |
| } |
| |
| acc_range = bmi160_acc_reg_val_to_range(BMI160_DEFAULT_RANGE_ACC); |
| |
| data->scale.acc = BMI160_ACC_SCALE(acc_range); |
| |
| /* set gyro default range */ |
| if (bmi160_byte_write(dev, BMI160_REG_GYR_RANGE, |
| BMI160_DEFAULT_RANGE_GYR) < 0) { |
| LOG_DBG("Cannot set default range for gyroscope."); |
| return -EIO; |
| } |
| |
| gyr_range = bmi160_gyr_reg_val_to_range(BMI160_DEFAULT_RANGE_GYR); |
| |
| data->scale.gyr = BMI160_GYR_SCALE(gyr_range); |
| |
| if (bmi160_reg_field_update(dev, BMI160_REG_ACC_CONF, |
| BMI160_ACC_CONF_ODR_POS, |
| BMI160_ACC_CONF_ODR_MASK, |
| BMI160_DEFAULT_ODR_ACC) < 0) { |
| LOG_DBG("Failed to set accel's default ODR."); |
| return -EIO; |
| } |
| |
| if (bmi160_reg_field_update(dev, BMI160_REG_GYR_CONF, |
| BMI160_GYR_CONF_ODR_POS, |
| BMI160_GYR_CONF_ODR_MASK, |
| BMI160_DEFAULT_ODR_GYR) < 0) { |
| LOG_DBG("Failed to set gyro's default ODR."); |
| return -EIO; |
| } |
| |
| #ifdef CONFIG_BMI160_TRIGGER |
| if (bmi160_trigger_mode_init(dev) < 0) { |
| LOG_DBG("Cannot set up trigger mode."); |
| return -EINVAL; |
| } |
| #endif |
| |
| return 0; |
| } |
| |
| int bmi160_pm(const struct device *dev, enum pm_device_action action) |
| { |
| int ret = 0; |
| |
| switch (action) { |
| case PM_DEVICE_ACTION_RESUME: |
| bmi160_resume(dev); |
| break; |
| case PM_DEVICE_ACTION_SUSPEND: |
| bmi160_suspend(dev); |
| break; |
| default: |
| ret = -ENOTSUP; |
| } |
| |
| return ret; |
| } |
| |
| #if defined(CONFIG_BMI160_TRIGGER) |
| #define BMI160_TRIGGER_CFG(inst) \ |
| .interrupt = GPIO_DT_SPEC_INST_GET(inst, int_gpios), |
| #else |
| #define BMI160_TRIGGER_CFG(inst) |
| #endif |
| |
| #define BMI160_DEVICE_INIT(inst) \ |
| IF_ENABLED(CONFIG_PM_DEVICE_RUNTIME, (PM_DEVICE_DT_INST_DEFINE(inst, bmi160_pm))); \ |
| SENSOR_DEVICE_DT_INST_DEFINE(inst, bmi160_init, \ |
| COND_CODE_1(CONFIG_PM_DEVICE_RUNTIME, (PM_DEVICE_DT_INST_GET(inst)), (NULL)), \ |
| &bmi160_data_##inst, &bmi160_cfg_##inst, \ |
| POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ |
| &bmi160_api); |
| |
| /* Instantiation macros used when a device is on a SPI bus */ |
| #define BMI160_DEFINE_SPI(inst) \ |
| static struct bmi160_data bmi160_data_##inst; \ |
| static const struct bmi160_cfg bmi160_cfg_##inst = { \ |
| .bus.spi = SPI_DT_SPEC_INST_GET(inst, SPI_WORD_SET(8), 0), \ |
| .bus_io = &bmi160_bus_io_spi, \ |
| BMI160_TRIGGER_CFG(inst) \ |
| }; \ |
| BMI160_DEVICE_INIT(inst) |
| |
| /* Instantiation macros used when a device is on an I2C bus */ |
| #define BMI160_CONFIG_I2C(inst) \ |
| { \ |
| .bus.i2c = I2C_DT_SPEC_INST_GET(inst), \ |
| .bus_io = &bmi160_bus_io_i2c, \ |
| BMI160_TRIGGER_CFG(inst) \ |
| } |
| |
| #define BMI160_DEFINE_I2C(inst) \ |
| static struct bmi160_data bmi160_data_##inst; \ |
| static const struct bmi160_cfg bmi160_cfg_##inst = BMI160_CONFIG_I2C(inst); \ |
| BMI160_DEVICE_INIT(inst) |
| |
| /* |
| * Main instantiation macro. Use of COND_CODE_1() selects the right |
| * bus-specific macro at preprocessor time. |
| */ |
| #define BMI160_DEFINE(inst) \ |
| COND_CODE_1(DT_INST_ON_BUS(inst, spi), \ |
| (BMI160_DEFINE_SPI(inst)), \ |
| (BMI160_DEFINE_I2C(inst))) |
| |
| DT_INST_FOREACH_STATUS_OKAY(BMI160_DEFINE) |