diff options
-rw-r--r-- | common/build.mk | 1 | ||||
-rw-r--r-- | driver/accelgyro_bmi323.h | 15 | ||||
-rw-r--r-- | driver/accelgyro_bmi3xx.c | 1194 | ||||
-rw-r--r-- | driver/accelgyro_bmi3xx.h | 302 | ||||
-rw-r--r-- | driver/accelgyro_bmi_common.c | 5 | ||||
-rw-r--r-- | driver/build.mk | 1 | ||||
-rw-r--r-- | include/config.h | 2 | ||||
-rw-r--r-- | include/ec_commands.h | 1 | ||||
-rw-r--r-- | zephyr/Kconfig.sensor_devices | 8 | ||||
-rw-r--r-- | zephyr/shim/include/config_chip.h | 5 |
10 files changed, 1532 insertions, 2 deletions
diff --git a/common/build.mk b/common/build.mk index cac924c9f5..36724d619e 100644 --- a/common/build.mk +++ b/common/build.mk @@ -14,6 +14,7 @@ common-y+=version.o printf.o queue.o queue_policies.o irq_locking.o common-$(CONFIG_ACCELGYRO_BMI160)+=math_util.o common-$(CONFIG_ACCELGYRO_BMI260)+=math_util.o +common-$(CONFIG_ACCELGYRO_BMI3XX)+=math_util.o common-$(CONFIG_ACCELGYRO_ICM426XX)+=math_util.o common-$(CONFIG_ACCELGYRO_ICM42607)+=math_util.o common-$(CONFIG_ACCELGYRO_LSM6DS0)+=math_util.o diff --git a/driver/accelgyro_bmi323.h b/driver/accelgyro_bmi323.h new file mode 100644 index 0000000000..544e9a4527 --- /dev/null +++ b/driver/accelgyro_bmi323.h @@ -0,0 +1,15 @@ +/* Copyright 2021 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* BMI323 gsensor module for Chrome EC */ + +#ifndef __CROS_EC_ACCELGYRO_BMI323_H +#define __CROS_EC_ACCELGYRO_BMI323_H + +#include "accelgyro_bmi3xx.h" + +#define BMI323_CHIP_ID 0x43 + +#endif /* __CROS_EC_ACCELGYRO_BMI323_H */ diff --git a/driver/accelgyro_bmi3xx.c b/driver/accelgyro_bmi3xx.c new file mode 100644 index 0000000000..14bbfe7b26 --- /dev/null +++ b/driver/accelgyro_bmi3xx.c @@ -0,0 +1,1194 @@ +/* Copyright 2021 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/** + * BMI3XX accelerometer and gyroscope module for Chrome EC + * 3D digital accelerometer & 3D digital gyroscope + */ + +#include "accelgyro.h" +#include "accelgyro_bmi323.h" +#include "accelgyro_bmi_common.h" +#include "console.h" +#include "hwtimer.h" +#include "i2c.h" +#include "init_rom.h" +#include "math_util.h" +#include "motion_sense_fifo.h" +#include "spi.h" +#include "task.h" +#include "timer.h" +#include "util.h" +#include "watchdog.h" + +#define CPUTS(outstr) cputs(CC_ACCEL, outstr) +#define CPRINTF(format, args...) cprintf(CC_ACCEL, format, ## args) +#define CPRINTS(format, args...) cprints(CC_ACCEL, format, ## args) + +/* Sensor definition */ +STATIC_IF(CONFIG_BMI_ORIENTATION_SENSOR) void irq_set_orientation( + struct motion_sensor_t *s); + +STATIC_IF(CONFIG_ACCEL_FIFO) volatile uint32_t last_interrupt_timestamp; + +static uint8_t bmi3_buffer[BMI3_FIFO_BUFFER]; + +static inline int bmi3_read_n(const struct motion_sensor_t *s, const int reg, + uint8_t *data_ptr, const int len) +{ + return bmi_read_n(s->port, s->i2c_spi_addr_flags, reg, data_ptr, len); +} + +static inline int bmi3_write_n(const struct motion_sensor_t *s, const int reg, + uint8_t *data_ptr, const int len) +{ + return bmi_write_n(s->port, s->i2c_spi_addr_flags, reg, data_ptr, len); +} + +#ifdef CONFIG_ACCEL_INTERRUPTS + +#ifdef CONFIG_BMI_ORIENTATION_SENSOR + +static void irq_set_orientation(struct motion_sensor_t *s) +{ + int ret; + uint8_t reg_data[4]; + uint8_t orient_data; + + enum motionsensor_orientation orientation = + MOTIONSENSE_ORIENTATION_UNKNOWN; + + RETURN_ERROR(bmi3_read_n(s, BMI3_FEATURE_EVENT_EXT, reg_data, 4)); + + orient_data = reg_data[2] & BMI3_PORTRAIT_LANDSCAPE_MASK; + + if (BMI_GET_DATA(s)->raw_orientation != orient_data) { + BMI_GET_DATA(s)->raw_orientation = orient_data; + + switch (orient_data) { + case BMI3_ORIENT_PORTRAIT: + orientation = MOTIONSENSE_ORIENTATION_PORTRAIT; + break; + case BMI3_PORTRAIT_INVERT: + orientation = + MOTIONSENSE_ORIENTATION_UPSIDE_DOWN_PORTRAIT; + break; + case BMI3_LANDSCAPE: + orientation = MOTIONSENSE_ORIENTATION_LANDSCAPE; + break; + case BMI3_LANDSCAPE_INVERT: + orientation = + MOTIONSENSE_ORIENTATION_UPSIDE_DOWN_LANDSCAPE; + break; + default: + break; + } + + orientation = motion_orientation_remap(s, orientation); + *motion_orientation_ptr(s) = orientation; + } +} + +#endif /* CONFIG_BMI_ORIENTATION_SENSOR */ + +/* + * bmi3xx_interrupt - called when the sensor activates the interrupt line. + * + * This is a "top half" interrupt handler, it just asks motion sense ask + * to schedule the "bottom half", ->irq_handler(). + */ +void bmi3xx_interrupt(enum gpio_signal signal) +{ + if (IS_ENABLED(CONFIG_ACCEL_FIFO)) + last_interrupt_timestamp = __hw_clock_source_read(); + + task_set_event(TASK_ID_MOTIONSENSE, CONFIG_ACCELGYRO_BMI3XX_INT_EVENT); +} + +static int enable_fifo(const struct motion_sensor_t *s, int enable) +{ + /* Set FIFO config to enable accel gyro data */ + uint8_t reg_data[2] = {0, 0}; + struct bmi_drv_data_t *data = BMI_GET_DATA(s); + + reg_data[0] = 0; + + if (enable) { + reg_data[1] = BMI3_FIFO_ACC_EN | BMI3_FIFO_GYR_EN; + data->flags |= 1 << (s->type + BMI_FIFO_FLAG_OFFSET); + } else { + data->flags &= ~(1 << (s->type + BMI_FIFO_FLAG_OFFSET)); + } + + return bmi3_write_n(s, BMI3_REG_FIFO_CONF, reg_data, 2); +} + +static int config_interrupt(const struct motion_sensor_t *s) +{ + int ret; + uint8_t reg_data[6] = {0}; + + if (s->type != MOTIONSENSE_TYPE_ACCEL) + return EC_SUCCESS; + + mutex_lock(s->mutex); + + /* Clear the FIFO using Flush command */ + reg_data[0] = BMI3_ENABLE; + reg_data[1] = 0; + ret = bmi3_write_n(s, BMI3_REG_FIFO_CTRL, reg_data, 2); + if (ret) + goto err_unlock; + + /* Map FIFO water-mark and FIFO full to INT1 pin */ + ret = bmi3_read_n(s, BMI3_REG_INT_MAP1, reg_data, 6); + if (ret) + goto err_unlock; + + reg_data[5] = BMI3_SET_BITS(reg_data[5], BMI3_FWM_INT, BMI3_INT1); + reg_data[5] = BMI3_SET_BITS(reg_data[5], BMI3_FFULL_INT, BMI3_INT1); + if (IS_ENABLED(CONFIG_BMI_ORIENTATION_SENSOR)) { + /* Map orientation to INT1 pin */ + reg_data[2] = BMI3_SET_BITS(reg_data[2], BMI3_ORIENT_INT, + BMI3_INT1); + } + + ret = bmi3_write_n(s, BMI3_REG_INT_MAP1, ®_data[2], 4); + if (ret) + goto err_unlock; + + /* Set FIFO water-mark to read data whenever available */ + reg_data[0] = 1; + reg_data[1] = 0; + + ret = bmi3_write_n(s, BMI3_REG_FIFO_WATERMARK, reg_data, 2); + if (ret) + goto err_unlock; + + /* Get the previous configuration data */ + ret = bmi3_read_n(s, BMI3_REG_IO_INT_CTRL, reg_data, 6); + if (ret) + goto err_unlock; + + reg_data[2] = BMI3_SET_BIT_POS0(reg_data[2], BMI3_INT1_LVL, + BMI3_INT_ACTIVE_LOW); + + reg_data[2] = BMI3_SET_BITS(reg_data[2], BMI3_INT1_OD, + BMI3_INT_PUSH_PULL); + + reg_data[2] = BMI3_SET_BITS(reg_data[2], BMI3_INT1_OUTPUT_EN, + BMI3_INT_OUTPUT_ENABLE); + + reg_data[4] = BMI3_SET_BIT_POS0(reg_data[4], BMI3_INT_LATCH, + BMI3_INT_LATCH_EN); + + /* + * Set the interrupt pin configurations and + * latch settings + */ + ret = bmi3_write_n(s, BMI3_REG_IO_INT_CTRL, ®_data[2], 4); + if (ret) + goto err_unlock; + + if (IS_ENABLED(CONFIG_BMI_ORIENTATION_SENSOR)) { + /* Enable the orientation feature in BMI3 */ + ret = bmi3_read_n(s, BMI3_FEATURE_IO_0, reg_data, 4); + if (ret) + goto err_unlock; + + reg_data[2] |= BMI3_ANY_MOTION_X_EN_MASK; + ret = bmi3_write_n(s, BMI3_FEATURE_IO_0, ®_data[2], 2); + if (ret) + goto err_unlock; + + /* Write to feature engine */ + reg_data[0] = 1; + reg_data[1] = 0; + ret = bmi3_write_n(s, BMI3_FEATURE_IO_STATUS, reg_data, 2); + } + +err_unlock: + mutex_unlock(s->mutex); + return ret; +} + +int bmi3_parse_fifo_data(struct motion_sensor_t *s, struct bmi3_fifo_frame + *fifo_frame, uint32_t last_ts) +{ + /* Start index for FIFO parsing after I2C sync byte removal */ + size_t fifo_index = 2; + + /* Variable to store LSB and MSB value */ + uint16_t data_lsb, data_msb; + + /* Variable to store I2C sync data which will get in FIFO data */ + uint16_t i2c_sync_data; + + uint16_t fifo_size = 0; + + struct ec_response_motion_sensor_data vect; + + bool observed[2]; + + struct bmi3_fifo_data raw_data[NUM_OF_PRIMARY_SENSOR]; + + uint8_t sens_cnt = 0, reg_data[2]; + + struct bmi_drv_data_t *data = BMI_GET_DATA(s); + + if (s->type != MOTIONSENSE_TYPE_ACCEL) + return EC_SUCCESS; + + if (!(data->flags & (BMI_FIFO_ALL_MASK << BMI_FIFO_FLAG_OFFSET))) { + + /* + * The FIFO was disabled while we were processing it + * Flush potential left over: + * When sensor is resumed, we won't read old data. + */ + + /* Clear the FIFO using Flush command */ + reg_data[0] = BMI3_ENABLE; + reg_data[1] = 0; + return bmi3_write_n(s, BMI3_REG_FIFO_CTRL, reg_data, 2); + } + + /* Parse the length of data read excluding I2C sync bytes */ + fifo_size = fifo_frame->available_fifo_len - 2; + + while (fifo_size > 0) { + observed[SENSOR_ACCEL] = false; + observed[SENSOR_GYRO] = false; + + /** + * If we are reading some constant 64 bytes every time + * then 0x80 may even come after + * SENSOR ACCEL IS ENABLED + */ + if ((fifo_frame->available_fifo_sens & BMI3_FIFO_ACC_EN) + && (fifo_size != 0)) { + /* In-case of FIFO read fail it has only 0x8000 */ + if (fifo_size >= 2) { + if (bmi3_buffer[fifo_index] == 0x00 + && bmi3_buffer[fifo_index+1] == 0x80) { + break; + } + } else { + observed[SENSOR_ACCEL] = false; + observed[SENSOR_GYRO] = false; + fifo_size = 0; + } + + if (fifo_size >= BMI3_LENGTH_FIFO_ACC) { + /* Accelerometer raw x data */ + data_lsb = bmi3_buffer[fifo_index++]; + data_msb = bmi3_buffer[fifo_index++]; + + /* To store the I2C sync data */ + i2c_sync_data = (uint16_t)((data_msb << 8) + | data_lsb); + + if (i2c_sync_data + != BMI3_FIFO_ACCEL_I2C_SYNC_FRAME) { + observed[SENSOR_ACCEL] = true; + raw_data[SENSOR_ACCEL].x = + (int16_t)((data_msb << 8) + | data_lsb); + /* Accelerometer raw y data */ + data_lsb = bmi3_buffer[fifo_index++]; + data_msb = bmi3_buffer[fifo_index++]; + raw_data[SENSOR_ACCEL].y = + (int16_t)((data_msb << 8) + | data_lsb); + + /* Accelerometer raw z data */ + data_lsb = bmi3_buffer[fifo_index++]; + data_msb = bmi3_buffer[fifo_index++]; + raw_data[SENSOR_ACCEL].z = + (int16_t)((data_msb << 8) + | data_lsb); + } else { + fifo_index = fifo_index + 4; + observed[SENSOR_ACCEL] = false; + } + + fifo_size -= BMI3_LENGTH_FIFO_ACC; + } else { + observed[SENSOR_ACCEL] = false; + observed[SENSOR_GYRO] = false; + fifo_size = 0; + } + } + + if ((fifo_frame->available_fifo_sens & BMI3_FIFO_GYR_EN) + && (fifo_size != 0)) { + + /* In-case of FIFO read fail it has only 0x8000 */ + if (fifo_size >= 2) { + if (bmi3_buffer[fifo_index] == 0x00 && + bmi3_buffer[fifo_index+1] == 0x80) { + break; + } + } else { + observed[SENSOR_ACCEL] = false; + observed[SENSOR_GYRO] = false; + fifo_size = 0; + } + + if (fifo_size >= BMI3_LENGTH_FIFO_GYR) { + + data_lsb = bmi3_buffer[fifo_index++]; + data_msb = bmi3_buffer[fifo_index++]; + + /* To store the I2C sync data */ + i2c_sync_data = (uint16_t)((data_msb << 8) + | data_lsb); + if (i2c_sync_data + != BMI3_FIFO_GYRO_I2C_SYNC_FRAME) { + + observed[SENSOR_GYRO] = true; + + raw_data[SENSOR_GYRO].x = + (int16_t)((data_msb << 8) + | data_lsb); + + /* Accelerometer raw y data */ + data_lsb = bmi3_buffer[fifo_index++]; + data_msb = bmi3_buffer[fifo_index++]; + raw_data[SENSOR_GYRO].y = + (int16_t)((data_msb << 8) + | data_lsb); + + /* Accelerometer raw z data */ + data_lsb = bmi3_buffer[fifo_index++]; + data_msb = bmi3_buffer[fifo_index++]; + raw_data[SENSOR_GYRO].z = + (int16_t)((data_msb << 8) | + data_lsb); + } else { + fifo_index = fifo_index + 4; + observed[SENSOR_GYRO] = false; + } + + fifo_size -= BMI3_LENGTH_FIFO_GYR; + } else { + observed[SENSOR_ACCEL] = false; + observed[SENSOR_GYRO] = false; + fifo_size = 0; + } + } + + for (sens_cnt = 0; sens_cnt < NUM_OF_PRIMARY_SENSOR; + sens_cnt++) { + + if (observed[sens_cnt]) { + + struct motion_sensor_t *sens_output = s + + sens_cnt; + + /* TODO:NORMALISE */ + + vect.data[X] = raw_data[sens_cnt].x; + vect.data[Y] = raw_data[sens_cnt].y; + vect.data[Z] = raw_data[sens_cnt].z; + + vect.flags = 0; + + /* TODO:check this s-motion_sensors */ + vect.sensor_num = sens_cnt; + + motion_sense_fifo_stage_data(&vect, + sens_output, 3, last_ts); + } + } + } + + return EC_SUCCESS; +} + +/* + * irq_handler - bottom half of the interrupt stack. + * Ran from the motion_sense task, finds the events that raised the interrupt. + * + * For now, we just print out. We should set a bitmask motion sense code will + * act upon. + */ +static int irq_handler(struct motion_sensor_t *s, + uint32_t *event) +{ + int8_t has_read_fifo = 0; + int ret = 0; + uint8_t reg_data[4]; + uint16_t int_status; + uint16_t fifo_fill_level; + + if ((s->type != MOTIONSENSE_TYPE_ACCEL) + || (!(*event & CONFIG_ACCELGYRO_BMI3XX_INT_EVENT))) + return EC_ERROR_NOT_HANDLED; + + /* Get the interrupt status */ + ret = bmi3_read_n(s, BMI3_REG_INT_STATUS_INT1, reg_data, 4); + int_status = (uint16_t) reg_data[2] | ((uint16_t) reg_data[3] << 8); + + if ((ret == EC_SUCCESS) && ((int_status & BMI3_INT_STATUS_FWM) || + (int_status & BMI3_INT_STATUS_FFULL))) { + + struct bmi3_fifo_frame fifo_frame; + + fifo_frame.data = bmi3_buffer; + fifo_frame.length = BMI3_FIFO_BUFFER; + + /* Get the FIFO frame configurations */ + ret = bmi3_read_n(s, BMI3_REG_FIFO_CONF, reg_data, 4); + fifo_frame.available_fifo_sens = reg_data[3] & BMI3_FIFO_ALL_EN; + + /* Get the FIFO fill level in words */ + ret = bmi3_read_n(s, BMI3_REG_FIFO_FILL_LVL, reg_data, 4); + + reg_data[3] = BMI3_GET_BIT_POS0(reg_data[3], + BMI3_FIFO_FILL_LVL); + + fifo_fill_level = ((uint16_t)reg_data[3] << 8 | reg_data[2]); + + /* + * fifo_fill_level is in word count so (x2) also we add 2 more + * bytes for I2C sync transaction + */ + fifo_frame.available_fifo_len = (fifo_fill_level * 2) + 2; + + /* Read FIFO data */ + ret = bmi3_read_n(s, BMI3_REG_FIFO_DATA, bmi3_buffer, + fifo_fill_level); + + bmi3_parse_fifo_data(s, &fifo_frame, last_interrupt_timestamp); + has_read_fifo = 1; + + if (IS_ENABLED(CONFIG_BMI_ORIENTATION_SENSOR)) + if (BMI3_INT_STATUS_ORIENTATION & int_status) + irq_set_orientation(s); + } + + if (IS_ENABLED(CONFIG_ACCEL_FIFO) && has_read_fifo) + motion_sense_fifo_commit_data(); + + return EC_SUCCESS; +} +#endif /* CONFIG_ACCEL_INTERRUPTS */ + +static int read_temp(const struct motion_sensor_t *s, int *temp_ptr) +{ + return EC_ERROR_UNIMPLEMENTED; +} + +int get_gyro_offset(const struct motion_sensor_t *s, intv3_t v) +{ + int i; + uint8_t reg_data[14] = { 0 }; + + /* Get the accel offset values */ + RETURN_ERROR(bmi3_read_n(s, GYR_DP_OFF_X, reg_data, 14)); + + v[0] = ((uint16_t)(reg_data[3] << 8) | reg_data[2]) & 0x03FF; + v[1] = ((uint16_t)(reg_data[7] << 8) | reg_data[6]) & 0x03FF; + v[2] = ((uint16_t)(reg_data[11] << 8) | reg_data[10]) & 0x03FF; + + for (i = X; i <= Z; ++i) { + if (v[i] > 0x01FF) + v[i] = -1024 + v[i]; + + v[i] = round_divide((int64_t)v[i] * BMI_OFFSET_GYRO_MULTI_MDS, + BMI_OFFSET_GYRO_DIV_MDS); + } + + return EC_SUCCESS; +} + +int set_gyro_offset(const struct motion_sensor_t *s, intv3_t v) +{ + uint8_t reg_data[6] = { 0 }; + uint8_t base_addr[2] = { BMI3_GYRO_OFFSET_ADDR, 0 }; + int i, val[3]; + + for (i = X; i <= Z; ++i) { + val[i] = round_divide((int64_t)v[i] * BMI_OFFSET_GYRO_DIV_MDS, + BMI_OFFSET_GYRO_MULTI_MDS); + if (val[i] > 511) + val[i] = 511; + if (val[i] < -512) + val[i] = -512; + if (val[i] < 0) + val[i] = 1024 + val[i]; + } + + /* + * Set the user accel offset base address to feature engine + * transmission address to start DMA transaction + */ + RETURN_ERROR(bmi3_write_n(s, BMI3_FEATURE_ENGINE_DMA_TX, base_addr, 2)); + + reg_data[0] = (uint8_t)(val[0] & BMI3_SET_LOW_BYTE); + reg_data[1] = (uint8_t)((val[0] & 0x0300) >> 8); + reg_data[2] = (uint8_t)(val[1] & BMI3_SET_LOW_BYTE); + reg_data[3] = (uint8_t)((val[1] & 0x0300) >> 8); + reg_data[4] = (uint8_t)(val[2] & BMI3_SET_LOW_BYTE); + reg_data[5] = (uint8_t)((val[2] & 0x0300) >> 8); + + /* Set the configuration to the feature engine register */ + RETURN_ERROR(bmi3_write_n(s, BMI3_FEATURE_ENGINE_DMA_TX_DATA, reg_data, + 6)); + + /* Update the offset to the sensor engine */ + reg_data[0] = (uint8_t)(BMI3_CMD_USR_GAIN_OFFS_UPDATE & + BMI3_SET_LOW_BYTE); + reg_data[1] = (uint8_t)((BMI3_CMD_USR_GAIN_OFFS_UPDATE & + BMI3_SET_HIGH_BYTE) >> 8); + RETURN_ERROR(bmi3_write_n(s, BMI3_REG_CMD, reg_data, 2)); + + return EC_SUCCESS; +} + +int get_accel_offset(const struct motion_sensor_t *s, intv3_t v) +{ + int i; + uint8_t reg_data[14] = { 0 }; + + /* Get the accel offset values from user registers */ + RETURN_ERROR(bmi3_read_n(s, ACC_DP_OFF_X, reg_data, 14)); + + v[0] = ((uint16_t)(reg_data[3] << 8) | reg_data[2]) & 0x1FFF; + v[1] = ((uint16_t)(reg_data[7] << 8) | reg_data[6]) & 0x1FFF; + v[2] = ((uint16_t)(reg_data[11] << 8) | reg_data[10]) & 0x1FFF; + + for (i = X; i <= Z; ++i) { + if (v[i] > 0x0FFF) + v[i] = -8192 + v[i]; + + v[i] = round_divide((int64_t)v[i] * BMI3_OFFSET_ACC_MULTI_MG, + BMI_OFFSET_ACC_DIV_MG); + } + + return EC_SUCCESS; +} + +int set_accel_offset(const struct motion_sensor_t *s, intv3_t v) +{ + uint8_t reg_data[6] = { 0 }; + uint8_t base_addr[2] = { BMI3_ACC_OFFSET_ADDR, 0 }; + int i, val[3]; + + for (i = X; i <= Z; ++i) { + val[i] = round_divide((int64_t)v[i] * BMI_OFFSET_ACC_DIV_MG, + BMI3_OFFSET_ACC_MULTI_MG); + if (val[i] > 4095) + val[i] = 4095; + if (val[i] < -4096) + val[i] = -4096; + if (val[i] < 0) + val[i] += 8192; + } + + /* + * Set the user accel offset base address to feature engine + * transmission address to start DMA transaction + */ + RETURN_ERROR(bmi3_write_n(s, BMI3_FEATURE_ENGINE_DMA_TX, base_addr, 2)); + + reg_data[0] = (uint8_t)(val[0] & BMI3_SET_LOW_BYTE); + reg_data[1] = (uint8_t)((val[0] & 0x1F00) >> 8); + reg_data[2] = (uint8_t)(val[1] & BMI3_SET_LOW_BYTE); + reg_data[3] = (uint8_t)((val[1] & 0x1F00) >> 8); + reg_data[4] = (uint8_t)(val[2] & BMI3_SET_LOW_BYTE); + reg_data[5] = (uint8_t)((val[2] & 0x1F00) >> 8); + + /* Set the configuration to the feature engine register */ + RETURN_ERROR(bmi3_write_n(s, BMI3_FEATURE_ENGINE_DMA_TX_DATA, reg_data, + 6)); + + /* Update the offset to the sensor engine */ + reg_data[0] = (uint8_t)(BMI3_CMD_USR_GAIN_OFFS_UPDATE & + BMI3_SET_LOW_BYTE); + + reg_data[1] = (uint8_t)((BMI3_CMD_USR_GAIN_OFFS_UPDATE & + BMI3_SET_HIGH_BYTE) >> 8); + + RETURN_ERROR(bmi3_write_n(s, BMI3_REG_CMD, reg_data, 2)); + + return EC_SUCCESS; +} + +static int wait_and_read_data(const struct motion_sensor_t *s, + intv3_t accel_data) +{ + uint8_t reg_data[8] = {0}; + + /* Retry 5 times */ + uint8_t try_cnt = FOC_TRY_COUNT; + + /* Check if data is ready */ + while (try_cnt && (!(reg_data[2] & BMI3_STAT_DATA_RDY_ACCEL_MSK))) { + /* 20ms delay for 50Hz ODR */ + msleep(FOC_DELAY); + + /* Read the status register */ + RETURN_ERROR(bmi3_read_n(s, BMI3_REG_STATUS, reg_data, 4)); + try_cnt--; + } + + if (!(reg_data[2] & BMI3_STAT_DATA_RDY_ACCEL_MSK)) + return EC_ERROR_TIMEOUT; + + /* Read the sensor data */ + RETURN_ERROR(bmi3_read_n(s, BMI3_REG_ACC_DATA_X, reg_data, 8)); + + accel_data[0] = ((int16_t)((reg_data[3] << 8) | reg_data[2])); + accel_data[1] = ((int16_t)((reg_data[5] << 8) | reg_data[4])); + accel_data[2] = ((int16_t)((reg_data[7] << 8) | reg_data[6])); + + rotate(accel_data, *s->rot_standard_ref, accel_data); + + return EC_SUCCESS; +} + +/*! + * @brief This internal API performs Fast Offset Compensation for accelerometer. + */ +static int8_t perform_accel_foc(struct motion_sensor_t *s, int *target, + int sens_range) +{ + intv3_t accel_data, offset; + int32_t delta_value[3] = {0, 0, 0}; + + /* Variable to define count */ + uint8_t i, loop, sample_count = 0; + + for (loop = 0; loop < BMI3_FOC_SAMPLE_LIMIT; loop++) { + + RETURN_ERROR(wait_and_read_data(s, accel_data)); + + sample_count++; + + /* Store the data in a temporary structure */ + delta_value[0] += accel_data[0] - target[X]; + delta_value[1] += accel_data[1] - target[Y]; + delta_value[2] += accel_data[2] - target[Z]; + } + + /* The data is in LSB so -> [(LSB)*1000*range/2^15] (mdps | mg) */ + for (i = X; i <= Z; ++i) { + offset[i] = (((int64_t)(delta_value[i] * 1000 * sens_range + / sample_count) >> 15) * -1); + } + + rotate_inv(offset, *s->rot_standard_ref, offset); + + RETURN_ERROR(set_accel_offset(s, offset)); + + return EC_SUCCESS; +} + +static int set_gyro_foc_config(struct motion_sensor_t *s) +{ + uint8_t reg_data[4] = { 0 }; + uint8_t base_addr[2] = { BMI3_BASE_ADDR_SC, 0 }; + + /* + * Set the user accel offset base address to feature engine + * transmission address to start DMA transaction + */ + RETURN_ERROR(bmi3_write_n(s, BMI3_FEATURE_ENGINE_DMA_TX, base_addr, 2)); + + /* Read the configuration from the feature engine register */ + RETURN_ERROR(bmi3_read_n(s, BMI3_FEATURE_ENGINE_DMA_TX_DATA, reg_data, + 4)); + /* Enable self calibration */ + reg_data[2] |= 0x07; + + RETURN_ERROR(bmi3_write_n(s, BMI3_FEATURE_ENGINE_DMA_TX, base_addr, 2)); + + /* Set the configuration to the feature engine register */ + RETURN_ERROR(bmi3_write_n(s, BMI3_FEATURE_ENGINE_DMA_TX_DATA, + ®_data[2], 2)); + + /* Trigger bmi3 gyro self calibration */ + reg_data[0] = (uint8_t)(BMI3_CMD_SELF_CALIB & BMI3_SET_LOW_BYTE); + reg_data[1] = (uint8_t)((BMI3_CMD_SELF_CALIB & BMI3_SET_HIGH_BYTE) + >> 8); + + RETURN_ERROR(bmi3_write_n(s, BMI3_REG_CMD, reg_data, 2)); + + return EC_SUCCESS; +} + +static int get_calib_result(struct motion_sensor_t *s) +{ + uint8_t i, reg_data[4]; + + for (i = 0; i < 25; i++) { + /* A delay of 120ms is required to read this status register */ + msleep(120); + + /* Read the configuration from the feature engine register */ + RETURN_ERROR(bmi3_read_n(s, BMI3_FEATURE_IO_1, reg_data, 4)); + + /* Check calibration complete status */ + if (reg_data[2] & BMI3_SC_ST_STATUS_MASK) { + /* Check cailbration result */ + if (reg_data[2] & BMI3_SC_RESULT_MASK) + return EC_SUCCESS; + } + } + + return EC_ERROR_NOT_CALIBRATED; +} + +static int perform_calib(struct motion_sensor_t *s, int enable) +{ + int ret; + intv3_t target = {0, 0, 0}; + uint8_t saved_conf[4] = {0}; + + /* Sensor is configured to be in 16G range */ + int sens_range = 16; + + /* Variable to set the accelerometer configuration value 50Hz for FOC */ + uint8_t acc_conf_data[2] = {BMI3_FOC_ACC_CONF_VAL_LSB, + BMI3_FOC_ACC_CONF_VAL_MSB}; + + if (!enable) + return EC_SUCCESS; + + /* Get default configurations for the type of feature selected. */ + RETURN_ERROR(bmi3_read_n(s, BMI3_REG_ACC_CONF + s->type, saved_conf, + 4)); + + /* Set the FOC configuration and add a delay */ + RETURN_ERROR(bmi3_write_n(s, BMI3_REG_ACC_CONF, acc_conf_data, 2)); + msleep(FOC_DELAY); + + switch (s->type) { + case MOTIONSENSE_TYPE_ACCEL: + target[Z] = BMI3_ACC_DATA_PLUS_1G(sens_range); + + /* Perform accel calibration */ + ret = perform_accel_foc(s, target, sens_range); + if (ret) + goto end_calib; + break; + case MOTIONSENSE_TYPE_GYRO: + ret = set_gyro_foc_config(s); + if (ret) + goto end_calib; + + ret = get_calib_result(s); + if (ret) + goto end_calib; + break; + default: + /* Not supported on Magnetometer */ + ret = EC_RES_INVALID_PARAM; + goto end_calib; + } + + +end_calib: + bmi3_write_n(s, BMI3_REG_ACC_CONF + s->type, &saved_conf[2], 2); + + return ret; +} + +static int get_offset(const struct motion_sensor_t *s, int16_t *offset, + int16_t *temp) +{ + int i; + intv3_t v; + + switch (s->type) { + case MOTIONSENSE_TYPE_ACCEL: + /* + * The offset of the accelerometer is a 8 bit + * two-complement number in units of 3.9 mg independent of the + * range selected for the accelerometer. + */ + RETURN_ERROR(get_accel_offset(s, v)); + break; + case MOTIONSENSE_TYPE_GYRO: + /* Gyro offset is in milli-dps */ + RETURN_ERROR(get_gyro_offset(s, v)); + break; + default: + for (i = X; i <= Z; i++) + v[i] = 0; + } + + rotate(v, *s->rot_standard_ref, v); + offset[X] = v[X]; + offset[Y] = v[Y]; + offset[Z] = v[Z]; + /* Saving temperature at calibration not supported yet */ + *temp = (int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP; + + return EC_SUCCESS; +} + +static int set_offset(const struct motion_sensor_t *s, + const int16_t *offset, + int16_t temp) +{ + intv3_t v = { offset[X], offset[Y], offset[Z] }; + (void)temp; + + rotate_inv(v, *s->rot_standard_ref, v); + + switch (s->type) { + case MOTIONSENSE_TYPE_ACCEL: + /* Offset should be in units of mg */ + RETURN_ERROR(set_accel_offset(s, v)); + break; + case MOTIONSENSE_TYPE_GYRO: + /* Offset should be in units of mdps */ + RETURN_ERROR(set_gyro_offset(s, v)); + break; + default: + return EC_RES_INVALID_PARAM; + } + + return EC_SUCCESS; +} + +#ifdef CONFIG_BODY_DETECTION +int get_rms_noise(const struct motion_sensor_t *s) +{ + return EC_ERROR_UNIMPLEMENTED; +} +#endif + +static int set_scale(const struct motion_sensor_t *s, const uint16_t *scale, + int16_t temp) +{ + struct accelgyro_saved_data_t *saved_data = BMI_GET_SAVED_DATA(s); + + saved_data->scale[X] = scale[X]; + saved_data->scale[Y] = scale[Y]; + saved_data->scale[Z] = scale[Z]; + + return EC_SUCCESS; +} + +static int get_scale(const struct motion_sensor_t *s, uint16_t *scale, + int16_t *temp) +{ + struct accelgyro_saved_data_t *saved_data = BMI_GET_SAVED_DATA(s); + + scale[X] = saved_data->scale[X]; + scale[Y] = saved_data->scale[Y]; + scale[Z] = saved_data->scale[Z]; + + *temp = (int16_t)EC_MOTION_SENSE_INVALID_CALIB_TEMP; + + return EC_SUCCESS; +} + + +static int get_data_rate(const struct motion_sensor_t *s) +{ + struct accelgyro_saved_data_t *saved_data = BMI_GET_SAVED_DATA(s); + + return saved_data->odr; +} + +static int set_data_rate(const struct motion_sensor_t *s, + int rate, int rnd) +{ + int ret; + int normalized_rate; + uint8_t reg_data[4]; + uint8_t reg_val; + + struct accelgyro_saved_data_t *saved_data = BMI_GET_SAVED_DATA(s); + + RETURN_ERROR(bmi_get_normalized_rate(s, rate, rnd, &normalized_rate, + ®_val)); + + /* + * Lock accel resource to prevent another task from attempting + * to write accel parameters until we are done. + */ + mutex_lock(s->mutex); + + /* + * Get default configurations for the type of feature selected. + */ + ret = bmi3_read_n(s, BMI3_REG_ACC_CONF + s->type, reg_data, 4); + if (ret) { + mutex_unlock(s->mutex); + return ret; + } + + if (s->type == MOTIONSENSE_TYPE_ACCEL) { + if (rate == 0) { + /* FIFO stop collecting events */ + if (IS_ENABLED(CONFIG_ACCEL_FIFO)) + ret = enable_fifo(s, 0); + + /* Set the sensor in suspend mode */ + reg_data[3] = BMI3_SET_BITS(reg_data[3], + BMI3_POWER_MODE, + BMI3_ACC_MODE_LOW_PWR); + + saved_data->odr = 0; + } else if (saved_data->odr == 0) { + /* Power mode changed from suspend to + * normal + */ + reg_data[3] = BMI3_SET_BITS(reg_data[3], + BMI3_POWER_MODE, + BMI3_ACC_MODE_NORMAL); + } + + /* Set accelerometer ODR */ + reg_data[2] = BMI3_SET_BIT_POS0(reg_data[2], + BMI3_SENS_ODR, reg_val); + } else if (s->type == MOTIONSENSE_TYPE_GYRO) { + if (rate == 0) { + /* FIFO stop collecting events */ + if (IS_ENABLED(CONFIG_ACCEL_FIFO)) + ret = enable_fifo(s, 0); + + /* Set the sensor in suspend mode */ + reg_data[3] = BMI3_SET_BITS(reg_data[3], + BMI3_POWER_MODE, + BMI3_GYR_MODE_SUSPEND); + + saved_data->odr = 0; + } else if (saved_data->odr == 0) { + /* Power mode changed from suspend to + * normal + */ + reg_data[3] = BMI3_SET_BITS(reg_data[3], + BMI3_POWER_MODE, + BMI3_GYR_MODE_NORMAL); + } + reg_data[2] = BMI3_SET_BIT_POS0(reg_data[2], BMI3_SENS_ODR, + reg_val); + } + + /* Set the accel/gyro configurations. */ + ret = bmi3_write_n(s, BMI3_REG_ACC_CONF + s->type, ®_data[2], 2); + + if (ret == EC_SUCCESS) { + saved_data->odr = normalized_rate; + + /* + * FIFO start collecting events. + * They will be discarded if AP does not want them. + */ + ret = enable_fifo(s, 1); + } + + mutex_unlock(s->mutex); + return ret; +} + +static int get_resolution(const struct motion_sensor_t *s) +{ + return BMI3_16_BIT_RESOLUTION; +} + +static int set_range(struct motion_sensor_t *s, int range, int rnd) +{ + int ret; + uint8_t index, sens_size = 0; + uint8_t reg_data[4] = { 0 }; + int (*sensor_range)[2]; + + int acc_sensor_range[4][2] = { + { 2, BMI3_ACC_RANGE_2G }, + { 4, BMI3_ACC_RANGE_4G }, + { 8, BMI3_ACC_RANGE_8G }, + { 16, BMI3_ACC_RANGE_16G }, + }; + + int gyr_sensor_range[5][2] = { + { 125, BMI3_GYR_RANGE_125DPS }, + { 250, BMI3_GYR_RANGE_250DPS }, + { 500, BMI3_GYR_RANGE_500DPS }, + { 1000, BMI3_GYR_RANGE_1000DPS }, + { 2000, BMI3_GYR_RANGE_2000DPS }, + }; + + if (s->type == MOTIONSENSE_TYPE_ACCEL) { + sens_size = ARRAY_SIZE(acc_sensor_range); + + sensor_range = acc_sensor_range; + } else { + sens_size = ARRAY_SIZE(gyr_sensor_range); + + sensor_range = gyr_sensor_range; + } + + for (index = 0; index < sens_size - 1; index++) { + if (range <= sensor_range[index][0]) + break; + + if (range < sensor_range[index + 1][0]) { + if (rnd) + index += 1; + + break; + } + } + + mutex_lock(s->mutex); + + /* + * Read the range register from sensor for accelerometer/gyroscope + * s->type should have MOTIONSENSE_TYPE_ACCEL = 0 ; + * MOTIONSENSE_TYPE_GYRO = 1 + */ + ret = bmi3_read_n(s, BMI3_REG_ACC_CONF + s->type, reg_data, 4); + + if (ret == EC_SUCCESS) { + /* Set accelerometer/Gyroscope range */ + /* Gravity range of the sensor (+/- 2G, 4G, 8G, 16G). */ + reg_data[2] = BMI3_SET_BITS(reg_data[2], BMI3_SENS_RANGE, + sensor_range[index][1]); + + /* Set the accel/gyro configurations. */ + ret = bmi3_write_n(s, BMI3_REG_ACC_CONF + s->type, + ®_data[2], 2); + + /* Now that we have set the range, update the driver's value. */ + if (ret == EC_SUCCESS) + s->current_range = sensor_range[index][0]; + } + + mutex_unlock(s->mutex); + + return ret; +} + +static int read(const struct motion_sensor_t *s, intv3_t v) +{ + int ret; + uint8_t reg_data[8] = { 0 }; + uint16_t status_val = 0; + + mutex_lock(s->mutex); + + /* Read the status register */ + ret = bmi3_read_n(s, BMI3_REG_STATUS, reg_data, 4); + + if (ret == EC_SUCCESS) { + status_val = (reg_data[2] | ((uint16_t)reg_data[3] << 8)); + /* + * If sensor data is not ready, return the previous read data. + * Note: return success so that motion sensor task can read + * again to get the latest updated sensor data quickly. + */ + if (!(status_val & BMI3_DRDY_MASK(s->type))) { + if (v != s->raw_xyz) + memcpy(v, s->raw_xyz, sizeof(s->raw_xyz)); + + mutex_unlock(s->mutex); + + return EC_SUCCESS; + } + + if (s->type == MOTIONSENSE_TYPE_ACCEL) { + /* Read the sensor data */ + ret = bmi3_read_n(s, BMI3_REG_ACC_DATA_X, reg_data, 8); + } else if (s->type == MOTIONSENSE_TYPE_GYRO) { + /* Read the sensor data */ + ret = bmi3_read_n(s, BMI3_REG_GYR_DATA_X, reg_data, 8); + } + + if (ret == EC_SUCCESS) { + v[0] = ((int16_t)((reg_data[3] << 8) | reg_data[2])); + v[1] = ((int16_t)((reg_data[5] << 8) | reg_data[4])); + v[2] = ((int16_t)((reg_data[7] << 8) | reg_data[6])); + + rotate(v, *s->rot_standard_ref, v); + } + } + + mutex_unlock(s->mutex); + + return ret; +} + +static int init(struct motion_sensor_t *s) +{ + /* Status of communication result */ + uint8_t i; + uint8_t reg_data[4] = { 0 }; + + /* Store the sensor configurations */ + struct accelgyro_saved_data_t *saved_data = BMI_GET_SAVED_DATA(s); + struct bmi_drv_data_t *data = BMI_GET_DATA(s); + + /* This driver requires a mutex */ + ASSERT(s->mutex); + + /* Reset bmi3 device */ + reg_data[0] = (uint8_t)(BMI3_CMD_SOFT_RESET & BMI3_SET_LOW_BYTE); + reg_data[1] = (uint8_t)((BMI3_CMD_SOFT_RESET & BMI3_SET_HIGH_BYTE) + >> 8); + + RETURN_ERROR(bmi3_write_n(s, BMI3_REG_CMD, reg_data, 2)); + + /* Delay of 2ms after soft reset*/ + msleep(2); + + /* Enable feature engine bit */ + reg_data[0] = BMI3_ENABLE; + reg_data[1] = 0; + + RETURN_ERROR(bmi3_write_n(s, BMI3_REG_FEATURE_ENGINE_GLOB_CTRL, + reg_data, 2)); + + /* Read chip id */ + RETURN_ERROR(bmi3_read_n(s, BMI3_REG_CHIP_ID, reg_data, 4)); + + if (reg_data[2] != BMI323_CHIP_ID) + return EC_ERROR_HW_INTERNAL; + + for (i = X; i <= Z; i++) + saved_data->scale[i] = MOTION_SENSE_DEFAULT_SCALE; + + /* The sensor is in Suspend mode at init, so set data rate to 0*/ + saved_data->odr = 0; + + /* Flags used in FIFO parsing */ + data->flags &= ~(BMI_FLAG_SEC_I2C_ENABLED + | (BMI_FIFO_ALL_MASK << BMI_FIFO_FLAG_OFFSET)); + + if (IS_ENABLED(CONFIG_ACCEL_INTERRUPTS) + && (s->type == MOTIONSENSE_TYPE_ACCEL)) + RETURN_ERROR(config_interrupt(s)); + + return sensor_init_done(s); +} + +/* Accelerometer/Gyroscope base driver structure */ +const struct accelgyro_drv bmi3xx_drv = { + .init = init, + .read = read, + .set_range = set_range, + .get_resolution = get_resolution, + .set_data_rate = set_data_rate, + .get_data_rate = get_data_rate, + .get_scale = get_scale, + .set_scale = set_scale, + .set_offset = set_offset, + .get_offset = get_offset, + .perform_calib = perform_calib, + .read_temp = read_temp, +#ifdef CONFIG_ACCEL_INTERRUPTS + .irq_handler = irq_handler, +#endif +#ifdef CONFIG_BODY_DETECTION + .get_rms_noise = get_rms_noise, +#endif +}; diff --git a/driver/accelgyro_bmi3xx.h b/driver/accelgyro_bmi3xx.h new file mode 100644 index 0000000000..a4d51bdddd --- /dev/null +++ b/driver/accelgyro_bmi3xx.h @@ -0,0 +1,302 @@ +/* Copyright 2021 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* BMI3XX gsensor module for Chrome EC */ + +#ifndef __CROS_EC_ACCELGYRO_BMI3XX_H +#define __CROS_EC_ACCELGYRO_BMI3XX_H + +/* Sensor Specific macros */ +#define BMI3_ADDR_I2C_PRIM 0x68 +#define BMI3_ADDR_I2C_SEC 0x69 +#define BMI3_16_BIT_RESOLUTION 16 + +/* Chip-specific registers */ +#define BMI3_REG_CHIP_ID 0x00 + +#define BMI3_REG_STATUS 0x02 +#define BMI3_STAT_DATA_RDY_ACCEL_POS 7 +#define BMI3_STAT_DATA_RDY_ACCEL_MSK 0x80 + +#define BMI3_REG_ACC_DATA_X 0x03 +#define BMI3_ACC_RANGE_2G 0x00 +#define BMI3_ACC_RANGE_4G 0x01 +#define BMI3_ACC_RANGE_8G 0x02 +#define BMI3_ACC_RANGE_16G 0x03 +#define BMI3_ACC_MODE_DISABLE 0x00 +#define BMI3_ACC_MODE_LOW_PWR 0x03 +#define BMI3_ACC_MODE_NORMAL 0X04 +#define BMI3_ACC_MODE_HIGH_PERF 0x07 + +#define BMI3_REG_GYR_DATA_X 0x06 +#define BMI3_GYR_RANGE_125DPS 0x00 +#define BMI3_GYR_RANGE_250DPS 0x01 +#define BMI3_GYR_RANGE_500DPS 0x02 +#define BMI3_GYR_RANGE_1000DPS 0x03 +#define BMI3_GYR_RANGE_2000DPS 0x04 +#define BMI3_GYR_MODE_DISABLE 0x00 +#define BMI3_GYR_MODE_SUSPEND 0X01 +#define BMI3_GYR_MODE_ULTRA_LOW_PWR 0X02 +#define BMI3_GYR_MODE_LOW_PWR 0x03 +#define BMI3_GYR_MODE_NORMAL 0X04 +#define BMI3_GYR_MODE_HIGH_PERF 0x07 + +#define BMI3_REG_INT_STATUS_INT1 0x0D +#define BMI3_REG_FIFO_FILL_LVL 0x15 +#define BMI3_REG_FIFO_DATA 0x16 +#define BMI3_REG_ACC_CONF 0x20 +#define BMI3_REG_GYR_CONF 0x21 +#define BMI3_REG_INT_MAP1 0x3A +#define BMI3_REG_FIFO_WATERMARK 0x35 + +#define BMI3_REG_FIFO_CONF 0x36 +#define BMI3_FIFO_STOP_ON_FULL 0x01 +#define BMI3_FIFO_TIME_EN 0x01 +#define BMI3_FIFO_ACC_EN 0x02 +#define BMI3_FIFO_GYR_EN 0x04 +#define BMI3_FIFO_TEMP_EN 0x08 +#define BMI3_FIFO_ALL_EN 0x0F + +#define BMI3_REG_FIFO_CTRL 0x37 +#define BMI3_REG_IO_INT_CTRL 0x38 +#define BMI3_INT1_LVL_MASK 0x01 +#define BMI3_INT1_OD_MASK 0x02 +#define BMI3_INT1_OD_POS 1 +#define BMI3_INT1_OUTPUT_EN_MASK 0x04 +#define BMI3_INT1_OUTPUT_EN_POS 2 +#define BMI3_INT_PUSH_PULL 0 +#define BMI3_INT_OPEN_DRAIN 1 +#define BMI3_INT_ACTIVE_LOW 0 +#define BMI3_INT_ACTIVE_HIGH 1 + +#define BMI3_REG_IO_INT_CONF 0x39 +#define BMI3_INT_LATCH_EN 1 +#define BMI3_INT_LATCH_DISABLE 0 + +#define BMI3_REG_FEATURE_ENGINE_GLOB_CTRL 0x40 + +#define BMI3_FEATURE_EVENT_EXT 0x47 +#define BMI3_PORTRAIT_LANDSCAPE_MASK 0x03 +#define BMI3_PORTRAIT 0 +#define BMI3_LANDSCAPE 1 +#define BMI3_PORTRAIT_INVERT 2 +#define BMI3_LANDSCAPE_INVERT 3 + +#define ACC_DP_OFF_X 0x60 +#define GYR_DP_OFF_X 0x66 + +#define BMI3_REG_CMD 0x7E +#define BMI3_CMD_SOFT_RESET 0xDEAF + +/* BMI3 Interrupt Output Enable */ +#define BMI3_INT_OUTPUT_DISABLE 0 +#define BMI3_INT_OUTPUT_ENABLE 1 + +/* FIFO sensor data lengths */ +#define BMI3_LENGTH_FIFO_ACC 0x6 +#define BMI3_LENGTH_FIFO_GYR 0x6 +/* Macro to define accelerometer configuration value for FOC */ +#define BMI3_FOC_ACC_CONF_VAL_LSB 0xB7 +#define BMI3_FOC_ACC_CONF_VAL_MSB 0x40 +/* Macro to define the accel FOC range */ +#define BMI3_ACC_FOC_2G_REF 16384 +#define BMI3_ACC_FOC_4G_REF 8192 +#define BMI3_ACC_FOC_8G_REF 4096 +#define BMI3_ACC_FOC_16G_REF 2048 +#define BMI3_FOC_SAMPLE_LIMIT 128 + +/* 20ms delay for 50Hz ODR */ +#define FOC_TRY_COUNT 5 +#define FOC_DELAY 20 +#define BMI3_INT_STATUS_FWM 0x4000 +#define BMI3_INT_STATUS_FFULL 0x8000 +#define BMI3_INT_STATUS_ORIENTATION 0x0008 + +#define BMI3_FIFO_ACC_LENGTH 6 +#define BMI3_FIFO_GYR_LENGTH 6 +#define BMI3_SENSOR_TIME_LENGTH 2 + +/* Masks for FIFO I2C sync data frames */ +#define BMI3_FIFO_GYRO_I2C_SYNC_FRAME 0x7f02 +#define BMI3_FIFO_ACCEL_I2C_SYNC_FRAME 0x7f01 + +/* Gyro self calibration address */ +#define BMI3_BASE_ADDR_SC 0x26 +#define BMI3_CMD_SELF_CALIB 0x0101 + +/* Feature engine General purpose register 1. */ +#define BMI3_FEATURE_IO_0 0x10 +#define BMI3_ANY_MOTION_X_EN_MASK 0x08 +#define BMI3_FEATURE_IO_1 0x11 +#define BMI3_FEATURE_IO_STATUS 0x14 +#define BMI3_SC_ST_STATUS_MASK 0x10 +#define BMI3_SC_RESULT_MASK 0x20 + +/* + * The max positive value of accel data is 0x7FFF, equal to range(g) + * So, in order to get +1g, divide the 0x7FFF by range + */ +#define BMI3_ACC_DATA_PLUS_1G(range) (0x7FFF / (range)) +#define BMI3_ACC_DATA_MINUS_1G(range) (-BMI3_ACC_DATA_PLUS_1G(range)) + +/* Offset DMA registers */ +#define BMI3_ACC_OFFSET_ADDR 0x40 +#define BMI3_GYRO_OFFSET_ADDR 0x46 + +/* + * Start address of the DMA transaction. Has to be written to initiate a + * transaction. + */ +#define BMI3_FEATURE_ENGINE_DMA_TX 0x41 + +/* DMA read/write data. On read transaction expect first word to be zero. */ +#define BMI3_FEATURE_ENGINE_DMA_TX_DATA 0x42 + +/* Command for offset update */ +#define BMI3_CMD_USR_GAIN_OFFS_UPDATE 0x301 + +/* 1LSB - 31 Micro-G */ +#define BMI3_OFFSET_ACC_MULTI_MG (31 * 1000) + +/* 1LSB = 61 milli-dps*/ +#define BMI3_OFFSET_GYR_MDPS (61 * 1000) + +/* Other definitions */ +#define BMI3_FIFO_BUFFER 64 + +/* General Macro Definitions */ +/* LSB and MSB mask definitions */ +#define BMI3_SET_LOW_BYTE 0x00FF +#define BMI3_SET_HIGH_BYTE 0xFF00 + +/* For enable and disable */ +#define BMI3_ENABLE 0x1 +#define BMI3_DISABLE 0x0 + +/* Defines mode of operation for Accelerometer */ +#define BMI3_POWER_MODE_MASK 0x70 +#define BMI3_POWER_MODE_POS 4 + +#define BMI3_SENS_ODR_MASK 0x0F + +/* Full scale, Resolution */ +#define BMI3_SENS_RANGE_MASK 0x70 +#define BMI3_SENS_RANGE_POS 4 + +#define BMI3_CHIP_ID_MASK 0xFF + +/* Map FIFO water-mark interrupt to either INT1 or INT2 or IBI */ +#define BMI3_FWM_INT_MASK 0x30 +#define BMI3_FWM_INT_POS 4 + +/* Map FIFO full interrupt to either INT1 or INT2 or IBI */ +#define BMI3_FFULL_INT_MASK 0xC0 +#define BMI3_FFULL_INT_POS 6 + +#define BMI3_ORIENT_INT_MASK 0xC0 +#define BMI3_ORIENT_INT_POS 6 + + + +/* Mask definitions for interrupt pin configuration */ +#define BMI3_INT_LATCH_MASK 0x0001 + +/** + * Current fill level of FIFO buffer + * An empty FIFO corresponds to 0x000. The word counter may be reset by reading + * out all frames from the FIFO buffer or when the FIFO is reset through + * fifo_flush. The word counter is updated each time a complete frame was read + * or written. + */ +#define BMI3_FIFO_FILL_LVL_MASK 0x07 + +/* Enum to define interrupt lines */ +enum bmi3_hw_int_pin { + BMI3_INT_NONE, + BMI3_INT1, + BMI3_INT2, + BMI3_I3C_INT, + BMI3_INT_PIN_MAX +}; + +/* Structure to define FIFO frame configuration */ +struct bmi3_fifo_frame { + /* Pointer to FIFO data */ + uint8_t *data; + + /* Number of user defined bytes of FIFO to be read */ + uint16_t length; + + /* Enables type of data to be streamed - accelerometer, + * gyroscope + */ + uint8_t available_fifo_sens; + + /* Water-mark level for water-mark interrupt */ + uint16_t wm_lvl; + + /* Available fifo length */ + uint16_t available_fifo_len; +}; + +enum sensor_index_t { + FIRST_CONT_SENSOR = 0, + SENSOR_ACCEL = FIRST_CONT_SENSOR, + SENSOR_GYRO, + NUM_OF_PRIMARY_SENSOR, +}; + +/* Structure to define FIFO accel, gyro x, y and z axes */ +struct bmi3_fifo_data { + /* Data in x-axis */ + int16_t x; + + /* Data in y-axis */ + int16_t y; + + /* Data in z-axis */ + int16_t z; +}; + +struct bmi3xx_drv_data { + struct accelgyro_saved_data_t saved_data[3]; + uint8_t flags; + uint8_t enabled_activities; + uint8_t disabled_activities; + /* Current resolution of accelerometer. */ + int sensor_resolution; + int16_t offset[3]; +}; + +#define BMI3_GET_DATA(_s) \ + ((struct bmi3xx_drv_data *)(_s)->drv_data) + +#define BMI3_GET_SAVED_DATA(_s) \ + (&BMI3_GET_DATA(_s)->saved_data) + +#define BMI3_DRDY_OFF(_sensor) (7 - (_sensor)) +#define BMI3_DRDY_MASK(_sensor) (1 << BMI3_DRDY_OFF(_sensor)) + +/* Utility macros */ +#define BMI3_SET_BITS(reg_data, bitname, data) \ + ((reg_data & ~(bitname##_MASK)) | \ + ((data << bitname##_POS) & bitname##_MASK)) + +#define BMI3_GET_BITS(reg_data, bitname) \ + ((reg_data & (bitname##_MASK)) >> \ + (bitname##_POS)) + +#define BMI3_SET_BIT_POS0(reg_data, bitname, data) \ + ((reg_data & ~(bitname##_MASK)) | \ + (data & bitname##_MASK)) + +#define BMI3_GET_BIT_POS0(reg_data, bitname) \ + (reg_data & (bitname##_MASK)) + +extern const struct accelgyro_drv bmi3xx_drv; + +void bmi3xx_interrupt(enum gpio_signal signal); + +#endif /* __CROS_EC_ACCELGYRO_BMI3XX_H */ diff --git a/driver/accelgyro_bmi_common.c b/driver/accelgyro_bmi_common.c index b8a9de2422..7dff59fc5b 100644 --- a/driver/accelgyro_bmi_common.c +++ b/driver/accelgyro_bmi_common.c @@ -23,8 +23,9 @@ #define CPRINTF(format, args...) cprintf(CC_ACCEL, format, ## args) #define CPRINTS(format, args...) cprints(CC_ACCEL, format, ## args) -#if !defined(CONFIG_ACCELGYRO_BMI160) && !defined(CONFIG_ACCELGYRO_BMI260) -#error "Must use either BMI160 or BMI260" +#if !defined(CONFIG_ACCELGYRO_BMI160) && !defined(CONFIG_ACCELGYRO_BMI260) \ +&& !defined(CONFIG_ACCELGYRO_BMI3XX) +#error "Must use following sensors BMI160 BMI260 BMI3XX" #endif #if defined(CONFIG_ACCELGYRO_BMI260) && !defined(CONFIG_ACCELGYRO_BMI160) diff --git a/driver/build.mk b/driver/build.mk index 3efe9cd467..0c7a183a1f 100644 --- a/driver/build.mk +++ b/driver/build.mk @@ -16,6 +16,7 @@ driver-$(CONFIG_ACCEL_KX022)+=accel_kionix.o driver-$(CONFIG_ACCELGYRO_LSM6DS0)+=accelgyro_lsm6ds0.o driver-$(CONFIG_ACCELGYRO_BMI160)+=accelgyro_bmi160.o accelgyro_bmi_common.o driver-$(CONFIG_ACCELGYRO_BMI260)+=accelgyro_bmi260.o accelgyro_bmi_common.o +driver-$(CONFIG_ACCELGYRO_BMI3XX)+=accelgyro_bmi3xx.o accelgyro_bmi_common.o driver-$(CONFIG_ACCEL_BMA4XX)+=accel_bma4xx.o driver-$(CONFIG_MAG_BMI_BMM150)+=mag_bmm150.o driver-$(CONFIG_ACCELGYRO_LSM6DSM)+=accelgyro_lsm6dsm.o stm_mems_common.o diff --git a/include/config.h b/include/config.h index 8daf9025f6..9356e307c0 100644 --- a/include/config.h +++ b/include/config.h @@ -120,6 +120,7 @@ #undef CONFIG_ACCELGYRO_BMI160 #undef CONFIG_ACCELGYRO_BMI260 +#undef CONFIG_ACCELGYRO_BMI3XX #undef CONFIG_ACCELGYRO_ICM426XX #undef CONFIG_ACCELGYRO_LSM6DS0 /* Use CONFIG_ACCELGYRO_LSM6DSM for LSM6DSL, LSM6DSM, and/or LSM6DS3 */ @@ -364,6 +365,7 @@ */ #undef CONFIG_ACCELGYRO_BMI160_INT_EVENT #undef CONFIG_ACCELGYRO_BMI260_INT_EVENT +#undef CONFIG_ACCELGYRO_BMI3XX_INT_EVENT #undef CONFIG_ACCELGYRO_ICM426XX_INT_EVENT #undef CONFIG_ACCEL_LSM6DSM_INT_EVENT #undef CONFIG_ACCEL_LSM6DSO_INT_EVENT diff --git a/include/ec_commands.h b/include/ec_commands.h index d1b5b3ea90..441db29d74 100644 --- a/include/ec_commands.h +++ b/include/ec_commands.h @@ -2680,6 +2680,7 @@ enum motionsensor_chip { MOTIONSENSE_CHIP_ICM426XX = 25, MOTIONSENSE_CHIP_ICM42607 = 26, MOTIONSENSE_CHIP_BMA422 = 27, + MOTIONSENSE_CHIP_BMI323 = 28, MOTIONSENSE_CHIP_MAX, }; diff --git a/zephyr/Kconfig.sensor_devices b/zephyr/Kconfig.sensor_devices index 6860dc69c5..1fcd8b35d8 100644 --- a/zephyr/Kconfig.sensor_devices +++ b/zephyr/Kconfig.sensor_devices @@ -70,6 +70,14 @@ config PLATFORM_EC_ACCELGYRO_BMI260 Unit (IMU) consisting of a 16-bit tri-axial gyroscope and a 16-bit tri-axial accelerometer. +config PLATFORM_EC_ACCELGYRO_BMI3XX + bool "BMI3XX Accelgyrometer Driver" + select PLATFORM_EC_ACCELGYRO_BMI + help + The driver supports Bosch's BMI3XX which is an Inertial Measurement + Unit (IMU) consisting of a 16-bit tri-axial gyroscope and a 16-bit + tri-axial accelerometer. + config PLATFORM_EC_ALS_TCS3400 bool "TCS3400 Ambient Light Senseor Driver" help diff --git a/zephyr/shim/include/config_chip.h b/zephyr/shim/include/config_chip.h index 70b00b86d8..81f822f966 100644 --- a/zephyr/shim/include/config_chip.h +++ b/zephyr/shim/include/config_chip.h @@ -1287,6 +1287,11 @@ #define CONFIG_ACCELGYRO_BMI260 #endif +#undef CONFIG_ACCELGYRO_BMI3XX +#ifdef CONFIG_PLATFORM_EC_ACCELGYRO_BMI3XX +#define CONFIG_ACCELGYRO_BMI3XX +#endif + #undef CONFIG_ACCEL_BMA255 #ifdef CONFIG_PLATFORM_EC_ACCEL_BMA255 #define CONFIG_ACCEL_BMA255 |