From 3d41adc1d1d13a76b68f7927caa37315ed90977e Mon Sep 17 00:00:00 2001 From: Tomasz Michalec Date: Tue, 22 Jun 2021 11:09:31 +0200 Subject: zephyr: Add BMI160 emulator Add BMI emulator which is emulated device on i2c bus. Emulated accelerometer and gyroscope properties are defined through device tree, but they can be changed in runtime through BMI emulator API. It allows to set custom handlers for write and read messages to emulate more complex scenarios or malfunctioning device. BMI emulator is designed to implement support for different BMI models as an extension to common emulator code. BUG=b:184856157 BRANCH=none TEST=none Signed-off-by: Tomasz Michalec Change-Id: I63e9d3aca98c8923372437f7a66257a4c82817f2 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2977559 Reviewed-by: Simon Glass Reviewed-by: Jeremy Bettis --- driver/accelgyro_bmi160.h | 382 --------- driver/accelgyro_bmi260.h | 324 -------- driver/accelgyro_bmi_common.h | 316 ------- driver/mag_bmm150.h | 150 ---- include/driver/accelgyro_bmi160.h | 394 +++++++++ include/driver/accelgyro_bmi260.h | 324 ++++++++ include/driver/accelgyro_bmi_common.h | 316 +++++++ include/driver/mag_bmm150.h | 150 ++++ zephyr/dts/bindings/emul/zephyr,bmi.yaml | 41 + zephyr/emul/CMakeLists.txt | 2 + zephyr/emul/Kconfig | 6 + zephyr/emul/emul_bmi.c | 1342 ++++++++++++++++++++++++++++++ zephyr/emul/emul_bmi160.c | 747 +++++++++++++++++ zephyr/include/emul/emul_bmi.h | 518 ++++++++++++ 14 files changed, 3840 insertions(+), 1172 deletions(-) delete mode 100644 driver/accelgyro_bmi160.h delete mode 100644 driver/accelgyro_bmi260.h delete mode 100644 driver/accelgyro_bmi_common.h delete mode 100644 driver/mag_bmm150.h create mode 100644 include/driver/accelgyro_bmi160.h create mode 100644 include/driver/accelgyro_bmi260.h create mode 100644 include/driver/accelgyro_bmi_common.h create mode 100644 include/driver/mag_bmm150.h create mode 100644 zephyr/dts/bindings/emul/zephyr,bmi.yaml create mode 100644 zephyr/emul/emul_bmi.c create mode 100644 zephyr/emul/emul_bmi160.c create mode 100644 zephyr/include/emul/emul_bmi.h diff --git a/driver/accelgyro_bmi160.h b/driver/accelgyro_bmi160.h deleted file mode 100644 index ee9e821022..0000000000 --- a/driver/accelgyro_bmi160.h +++ /dev/null @@ -1,382 +0,0 @@ -/* Copyright 2015 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. - */ - -/* BMI160 accelerometer and gyro and BMM150 compass module for Chrome EC */ - -#ifndef __CROS_EC_ACCELGYRO_BMI160_H -#define __CROS_EC_ACCELGYRO_BMI160_H - -#include "accelgyro.h" -#include "driver/accelgyro_bmi160_public.h" -#include "mag_bmm150.h" - -#define BMI160_CHIP_ID 0x00 -#define BMI160_CHIP_ID_MAJOR 0xd1 -#define BMI168_CHIP_ID_MAJOR 0xd2 - -#define BMI160_SPEC_ACC_STARTUP_TIME_MS 10 -#define BMI160_SPEC_GYR_STARTUP_TIME_MS 80 -#define BMI160_SPEC_MAG_STARTUP_TIME_MS 60 - - -#define BMI160_ERR_REG 0x02 -#define BMI160_PMU_STATUS 0x03 -#define BMI160_PMU_MAG_OFFSET 0 -#define BMI160_PMU_GYR_OFFSET 2 -#define BMI160_PMU_ACC_OFFSET 4 -#define BMI160_PMU_SENSOR_STATUS(_sensor_type, _val) \ - (((_val) >> (4 - 2 * (_sensor_type))) & 0x3) -#define BMI160_PMU_SUSPEND 0 -#define BMI160_PMU_NORMAL 1 -#define BMI160_PMU_LOW_POWER 2 -#define BMI160_PMU_FAST_STARTUP 3 - -#define BMI160_MAG_X_L_G 0x04 -#define BMI160_MAG_X_H_G 0x05 -#define BMI160_MAG_Y_L_G 0x06 -#define BMI160_MAG_Y_H_G 0x07 -#define BMI160_MAG_Z_L_G 0x08 -#define BMI160_MAG_Z_H_G 0x09 -#define BMI160_RHALL_L_G 0x0a -#define BMI160_RHALL_H_G 0x0b -#define BMI160_GYR_X_L_G 0x0c -#define BMI160_GYR_X_H_G 0x0d -#define BMI160_GYR_Y_L_G 0x0e -#define BMI160_GYR_Y_H_G 0x0f -#define BMI160_GYR_Z_L_G 0x10 -#define BMI160_GYR_Z_H_G 0x11 -#define BMI160_ACC_X_L_G 0x12 -#define BMI160_ACC_X_H_G 0x13 -#define BMI160_ACC_Y_L_G 0x14 -#define BMI160_ACC_Y_H_G 0x15 -#define BMI160_ACC_Z_L_G 0x16 -#define BMI160_ACC_Z_H_G 0x17 - -#define BMI160_SENSORTIME_0 0x18 -#define BMI160_SENSORTIME_1 0x19 -#define BMI160_SENSORTIME_2 0x1a - -#define BMI160_STATUS 0x1b -#define BMI160_POR_DETECTED BIT(0) -#define BMI160_GYR_SLF_TST BIT(1) -#define BMI160_MAG_MAN_OP BIT(2) -#define BMI160_FOC_RDY BIT(3) -#define BMI160_NVM_RDY BIT(4) -#define BMI160_DRDY_MAG BIT(5) -#define BMI160_DRDY_GYR BIT(6) -#define BMI160_DRDY_ACC BIT(7) -#define BMI160_DRDY_OFF(_sensor) (7 - (_sensor)) -#define BMI160_DRDY_MASK(_sensor) (1 << BMI160_DRDY_OFF(_sensor)) - -/* first 2 bytes are the interrupt reasons, next 2 some qualifier */ -#define BMI160_INT_STATUS_0 0x1c -#define BMI160_STEP_INT BIT(0) -#define BMI160_SIGMOT_INT BIT(1) -#define BMI160_ANYM_INT BIT(2) -#define BMI160_PMU_TRIGGER_INT BIT(3) -#define BMI160_D_TAP_INT BIT(4) -#define BMI160_S_TAP_INT BIT(5) -#define BMI160_ORIENT_INT BIT(6) -#define BMI160_FLAT_INT BIT(7) -#define BMI160_ORIENT_XY_MASK 0x30 -#define BMI160_ORIENT_PORTRAIT (0 << 4) -#define BMI160_ORIENT_PORTRAIT_INVERT BIT(4) -#define BMI160_ORIENT_LANDSCAPE (2 << 4) -#define BMI160_ORIENT_LANDSCAPE_INVERT (3 << 4) - - -#define BMI160_INT_STATUS_1 0x1d -#define BMI160_HIGHG_INT (1 << (2 + 8)) -#define BMI160_LOWG_INT (1 << (3 + 8)) -#define BMI160_DRDY_INT (1 << (4 + 8)) -#define BMI160_FFULL_INT (1 << (5 + 8)) -#define BMI160_FWM_INT (1 << (6 + 8)) -#define BMI160_NOMO_INT (1 << (7 + 8)) - -#define BMI160_INT_MASK 0xFFFF - -#define BMI160_INT_STATUS_2 0x1e -#define BMI160_INT_STATUS_3 0x1f -#define BMI160_FIRST_X (1 << (0 + 16)) -#define BMI160_FIRST_Y (1 << (1 + 16)) -#define BMI160_FIRST_Z (1 << (2 + 16)) -#define BMI160_SIGN (1 << (3 + 16)) -#define BMI160_ANYM_OFFSET 0 -#define BMI160_TAP_OFFSET 4 -#define BMI160_HIGH_OFFSET 8 -#define BMI160_INT_INFO(_type, _data) \ -(CONCAT2(BMI160_, _data) << CONCAT3(BMI160_, _type, _OFFSET)) - -#define BMI160_ORIENT_Z (1 << (6 + 24)) -#define BMI160_FLAT (1 << (7 + 24)) - -#define BMI160_TEMPERATURE_0 0x20 -#define BMI160_TEMPERATURE_1 0x21 - - -#define BMI160_FIFO_LENGTH_0 0x22 -#define BMI160_FIFO_LENGTH_1 0x23 -#define BMI160_FIFO_LENGTH_MASK (BIT(11) - 1) -#define BMI160_FIFO_DATA 0x24 - -#define BMI160_ACC_CONF 0x40 -#define BMI160_ACC_BW_OFFSET 4 -#define BMI160_ACC_BW_MASK (0x7 << BMI160_ACC_BW_OFFSET) - -#define BMI160_ACC_RANGE 0x41 -#define BMI160_GSEL_2G 0x03 -#define BMI160_GSEL_4G 0x05 -#define BMI160_GSEL_8G 0x08 -#define BMI160_GSEL_16G 0x0c - -#define BMI160_GYR_CONF 0x42 -#define BMI160_GYR_BW_OFFSET 4 -#define BMI160_GYR_BW_MASK (0x3 << BMI160_GYR_BW_OFFSET) - -#define BMI160_GYR_RANGE 0x43 -#define BMI160_DPS_SEL_2000 0x00 -#define BMI160_DPS_SEL_1000 0x01 -#define BMI160_DPS_SEL_500 0x02 -#define BMI160_DPS_SEL_250 0x03 -#define BMI160_DPS_SEL_125 0x04 - -#define BMI160_MAG_CONF 0x44 - -#define BMI160_FIFO_DOWNS 0x45 -#define BMI160_FIFO_CONFIG_0 0x46 -#define BMI160_FIFO_CONFIG_1 0x47 -#define BMI160_FIFO_TAG_TIME_EN BIT(1) -#define BMI160_FIFO_TAG_INT2_EN BIT(2) -#define BMI160_FIFO_TAG_INT1_EN BIT(3) -#define BMI160_FIFO_HEADER_EN BIT(4) -#define BMI160_FIFO_MAG_EN BIT(5) -#define BMI160_FIFO_ACC_EN BIT(6) -#define BMI160_FIFO_GYR_EN BIT(7) -#define BMI160_FIFO_TARG_INT(_i) CONCAT3(BMI160_FIFO_TAG_INT, _i, _EN) -#define BMI160_FIFO_SENSOR_EN(_sensor) \ - ((_sensor) == MOTIONSENSE_TYPE_ACCEL ? BMI160_FIFO_ACC_EN : \ - ((_sensor) == MOTIONSENSE_TYPE_GYRO ? BMI160_FIFO_GYR_EN : \ - BMI160_FIFO_MAG_EN)) - -#define BMI160_MAG_IF_0 0x4b -#define BMI160_MAG_I2C_ADDRESS BMI160_MAG_IF_0 -#define BMI160_MAG_IF_1 0x4c -#define BMI160_MAG_I2C_CONTROL BMI160_MAG_IF_1 -#define BMI160_MAG_READ_BURST_MASK 3 -#define BMI160_MAG_READ_BURST_1 0 -#define BMI160_MAG_READ_BURST_2 1 -#define BMI160_MAG_READ_BURST_6 2 -#define BMI160_MAG_READ_BURST_8 3 -#define BMI160_MAG_OFFSET_OFF 3 -#define BMI160_MAG_OFFSET_MASK (0xf << BMI160_MAG_OFFSET_OFF) -#define BMI160_MAG_MANUAL_EN BIT(7) - -#define BMI160_MAG_IF_2 0x4d -#define BMI160_MAG_I2C_READ_ADDR BMI160_MAG_IF_2 -#define BMI160_MAG_IF_3 0x4e -#define BMI160_MAG_I2C_WRITE_ADDR BMI160_MAG_IF_3 -#define BMI160_MAG_IF_4 0x4f -#define BMI160_MAG_I2C_WRITE_DATA BMI160_MAG_IF_4 -#define BMI160_MAG_I2C_READ_DATA BMI160_MAG_X_L_G - -#define BMI160_INT_EN_0 0x50 -#define BMI160_INT_ANYMO_X_EN BIT(0) -#define BMI160_INT_ANYMO_Y_EN BIT(1) -#define BMI160_INT_ANYMO_Z_EN BIT(2) -#define BMI160_INT_D_TAP_EN BIT(4) -#define BMI160_INT_S_TAP_EN BIT(5) -#define BMI160_INT_ORIENT_EN BIT(6) -#define BMI160_INT_FLAT_EN BIT(7) -#define BMI160_INT_EN_1 0x51 -#define BMI160_INT_HIGHG_X_EN BIT(0) -#define BMI160_INT_HIGHG_Y_EN BIT(1) -#define BMI160_INT_HIGHG_Z_EN BIT(2) -#define BMI160_INT_LOW_EN BIT(3) -#define BMI160_INT_DRDY_EN BIT(4) -#define BMI160_INT_FFUL_EN BIT(5) -#define BMI160_INT_FWM_EN BIT(6) -#define BMI160_INT_EN_2 0x52 -#define BMI160_INT_NOMOX_EN BIT(0) -#define BMI160_INT_NOMOY_EN BIT(1) -#define BMI160_INT_NOMOZ_EN BIT(2) -#define BMI160_INT_STEP_DET_EN BIT(3) - -#define BMI160_INT_OUT_CTRL 0x53 -#define BMI160_INT_EDGE_CTRL BIT(0) -#define BMI160_INT_LVL_CTRL BIT(1) -#define BMI160_INT_OD BIT(2) -#define BMI160_INT_OUTPUT_EN BIT(3) -#define BMI160_INT1_CTRL_OFFSET 0 -#define BMI160_INT2_CTRL_OFFSET 4 -#define BMI160_INT_CTRL(_i, _bit) \ -(CONCAT2(BMI160_INT_, _bit) << CONCAT3(BMI160_INT, _i, _CTRL_OFFSET)) - -#define BMI160_INT_LATCH 0x54 -#define BMI160_INT1_INPUT_EN BIT(4) -#define BMI160_INT2_INPUT_EN BIT(5) -#define BMI160_LATCH_MASK 0xf -#define BMI160_LATCH_NONE 0 -#define BMI160_LATCH_5MS 5 -#define BMI160_LATCH_FOREVER 0xf - -#define BMI160_INT_MAP_0 0x55 -#define BMI160_INT_LOWG_STEP BIT(0) -#define BMI160_INT_HIGHG BIT(1) -#define BMI160_INT_ANYMOTION BIT(2) -#define BMI160_INT_NOMOTION BIT(3) -#define BMI160_INT_D_TAP BIT(4) -#define BMI160_INT_S_TAP BIT(5) -#define BMI160_INT_ORIENT BIT(6) -#define BMI160_INT_FLAT BIT(7) - -#define BMI160_INT_MAP_1 0x56 -#define BMI160_INT_PMU_TRIG BIT(0) -#define BMI160_INT_FFULL BIT(1) -#define BMI160_INT_FWM BIT(2) -#define BMI160_INT_DRDY BIT(3) -#define BMI160_INT1_MAP_OFFSET 4 -#define BMI160_INT2_MAP_OFFSET 0 -#define BMI160_INT_MAP(_i, _bit) \ -(CONCAT2(BMI160_INT_, _bit) << CONCAT3(BMI160_INT, _i, _MAP_OFFSET)) -#define BMI160_INT_FIFO_MAP BMI160_INT_MAP_1 - -#define BMI160_INT_MAP_2 0x57 - -#define BMI160_INT_MAP_INT_1 BMI160_INT_MAP_0 -#define BMI160_INT_MAP_INT_2 BMI160_INT_MAP_2 -#define BMI160_INT_MAP_REG(_i) CONCAT2(BMI160_INT_MAP_INT_, _i) - -#define BMI160_INT_DATA_0 0x58 -#define BMI160_INT_DATA_1 0x59 - -#define BMI160_INT_MOTION_0 0x5f -#define BMI160_INT_MOTION_1 0x60 -/* - * The formula is defined in 2.11.25 (any motion interrupt [1]). - * - * if we want threshold at a (in mg), the register should be x, where - * x * 7.81mg = a, assuming a range of 4G, which is - * x * 4 * 1.953 = a so - * x = a * 1000 / range * 1953 - */ -#define BMI160_MOTION_TH(_s, _mg) \ - (MIN(((_mg) * 1000) / ((_s)->current_range * 1953), 0xff)) -#define BMI160_INT_MOTION_2 0x61 -#define BMI160_INT_MOTION_3 0x62 -#define BMI160_MOTION_NO_MOT_SEL BIT(0) -#define BMI160_MOTION_SIG_MOT_SEL BIT(1) -#define BMI160_MOTION_SKIP_OFF 2 -#define BMI160_MOTION_SKIP_MASK 0x3 -#define BMI160_MOTION_SKIP_TIME(_ms) \ - (MIN(__fls((_ms) / 1500), BMI160_MOTION_SKIP_MASK)) -#define BMI160_MOTION_PROOF_OFF 4 -#define BMI160_MOTION_PROOF_MASK 0x3 -#define BMI160_MOTION_PROOF_TIME(_ms) \ - (MIN(__fls((_ms) / 250), BMI160_MOTION_PROOF_MASK)) - -#define BMI160_INT_TAP_0 0x63 -#define BMI160_TAP_DUR(_s, _ms) \ - ((_ms) <= 250 ? MAX((_ms), 50) / 50 - 1 : \ - (_ms) <= 500 ? 4 + ((_ms) - 250) / 125 : \ - (_ms) < 700 ? 6 : 7) - -#define BMI160_INT_TAP_1 0x64 -#define BMI160_TAP_TH(_s, _mg) \ - (MIN(((_mg) * 1000) / ((_s)->current_range * 31250), 0x1f)) - -#define BMI160_INT_ORIENT_0 0x65 - -/* No hysterisis, theta block, int on slope > 0.2 or axis > 1.5, symmetrical */ -#define BMI160_INT_ORIENT_0_INIT_VAL 0x48 - -#define BMI160_INT_ORIENT_1 0x66 - -/* no axes remap, no int on up/down, no blocking angle */ -#define BMI160_INT_ORIENT_1_INIT_VAL 0x00 - -#define BMI160_INT_FLAT_0 0x67 -#define BMI160_INT_FLAT_1 0x68 - -#define BMI160_FOC_CONF 0x69 -#define BMI160_FOC_GYRO_EN BIT(6) -#define BMI160_FOC_ACC_PLUS_1G 1 -#define BMI160_FOC_ACC_MINUS_1G 2 -#define BMI160_FOC_ACC_0G 3 -#define BMI160_FOC_ACC_Z_OFFSET 0 -#define BMI160_FOC_ACC_Y_OFFSET 2 -#define BMI160_FOC_ACC_X_OFFSET 4 - -#define BMI160_CONF 0x6a -#define BMI160_IF_CONF 0x6b -#define BMI160_IF_MODE_OFF 4 -#define BMI160_IF_MODE_MASK 3 -#define BMI160_IF_MODE_AUTO_OFF 0 -#define BMI160_IF_MODE_I2C_IOS 1 -#define BMI160_IF_MODE_AUTO_I2C 2 - -#define BMI160_PMU_TRIGGER 0x6c -#define BMI160_SELF_TEST 0x6d - -#define BMI160_OFFSET_ACC70 0x71 -#define BMI160_OFFSET_GYR70 0x74 -#define BMI160_OFFSET_EN_GYR98 0x77 -#define BMI160_OFFSET_ACC_EN BIT(6) -#define BMI160_OFFSET_GYRO_EN BIT(7) - - -#define BMI160_CMD_REG 0x7e -#define BMI160_CMD_SOFT_RESET 0xb6 -#define BMI160_CMD_NOOP 0x00 -#define BMI160_CMD_START_FOC 0x03 -#define BMI160_CMD_ACC_MODE_OFFSET 0x10 -#define BMI160_CMD_ACC_MODE_SUSP 0x10 -#define BMI160_CMD_ACC_MODE_NORMAL 0x11 -#define BMI160_CMD_ACC_MODE_LOWPOWER 0x12 -#define BMI160_CMD_GYR_MODE_SUSP 0x14 -#define BMI160_CMD_GYR_MODE_NORMAL 0x15 -#define BMI160_CMD_GYR_MODE_FAST_STARTUP 0x17 -#define BMI160_CMD_MAG_MODE_SUSP 0x18 -#define BMI160_CMD_MAG_MODE_NORMAL 0x19 -#define BMI160_CMD_MAG_MODE_LOWPOWER 0x1a -#define BMI160_CMD_MODE_SUSPEND(_sensor_type) \ - (BMI160_CMD_ACC_MODE_OFFSET | (_sensor_type) << 2 | BMI160_PMU_SUSPEND) -#define BMI160_CMD_MODE_NORMAL(_sensor_type) \ - (BMI160_CMD_ACC_MODE_OFFSET | (_sensor_type) << 2 | BMI160_PMU_NORMAL) - -#define BMI160_CMD_FIFO_FLUSH 0xb0 -#define BMI160_CMD_INT_RESET 0xb1 -#define BMI160_CMD_SOFT_RESET 0xb6 -#define BMI160_CMD_EXT_MODE_EN_B0 0x37 -#define BMI160_CMD_EXT_MODE_EN_B1 0x9a -#define BMI160_CMD_EXT_MODE_EN_B2 0xc0 - -#define BMI160_CMD_EXT_MODE_ADDR 0x7f -#define BMI160_CMD_PAGING_EN BIT(7) -#define BMI160_CMD_TARGET_PAGE BIT(4) -#define BMI160_COM_C_TRIM_ADDR 0x85 -#define BMI160_COM_C_TRIM (3 << 4) - -#define BMI160_CMD_TGT_PAGE 0 -#define BMI160_CMD_TGT_PAGE_COM 1 -#define BMI160_CMD_TGT_PAGE_ACC 2 -#define BMI160_CMD_TGT_PAGE_GYR 3 - -#define BMI160_FF_FRAME_LEN_TS 4 -#define BMI160_FF_DATA_LEN_ACC 6 -#define BMI160_FF_DATA_LEN_GYR 6 -#define BMI160_FF_DATA_LEN_MAG 8 - -/* Root mean square noise of 100 Hz accelerometer, units: ug */ -#define BMI160_ACCEL_RMS_NOISE_100HZ 1300 - -#ifdef CONFIG_BMI_SEC_I2C -/* Functions to access the secondary device through the accel/gyro. */ -int bmi160_sec_raw_read8(const int port, const uint16_t addr_flags, - const uint8_t reg, int *data_ptr); -int bmi160_sec_raw_write8(const int port, const uint16_t addr_flags, - const uint8_t reg, int data); -#endif - -#endif /* __CROS_EC_ACCELGYRO_BMI160_H */ diff --git a/driver/accelgyro_bmi260.h b/driver/accelgyro_bmi260.h deleted file mode 100644 index 9f39dd568a..0000000000 --- a/driver/accelgyro_bmi260.h +++ /dev/null @@ -1,324 +0,0 @@ -/* Copyright 2020 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. - */ - -/* BMI260 accelerometer and gyro for Chrome EC */ - -#ifndef __CROS_EC_ACCELGYRO_BMI260_H -#define __CROS_EC_ACCELGYRO_BMI260_H - -#include "accelgyro.h" -#include "common.h" -#include "mag_bmm150.h" -#include "driver/accelgyro_bmi260_public.h" - -#define BMI260_CHIP_ID 0x00 -#define BMI260_CHIP_ID_MAJOR 0x27 - -#define BMI260_ERR_REG 0x02 - -#define BMI260_STATUS 0x03 -#define BMI260_AUX_BUSY BIT(2) -#define BMI260_CMD_RDY BIT(4) -#define BMI260_DRDY_AUX BIT(5) -#define BMI260_DRDY_GYR BIT(6) -#define BMI260_DRDY_ACC BIT(7) -#define BMI260_DRDY_OFF(_sensor) (7 - (_sensor)) -#define BMI260_DRDY_MASK(_sensor) (1 << BMI260_DRDY_OFF(_sensor)) - -#define BMI260_AUX_X_L_G 0x04 -#define BMI260_AUX_X_H_G 0x05 -#define BMI260_AUX_Y_L_G 0x06 -#define BMI260_AUX_Y_H_G 0x07 -#define BMI260_AUX_Z_L_G 0x08 -#define BMI260_AUX_Z_H_G 0x09 -#define BMI260_AUX_R_L_G 0x0a -#define BMI260_AUX_R_H_G 0x0b -#define BMI260_ACC_X_L_G 0x0c -#define BMI260_ACC_X_H_G 0x0d -#define BMI260_ACC_Y_L_G 0x0e -#define BMI260_ACC_Y_H_G 0x0f -#define BMI260_ACC_Z_L_G 0x10 -#define BMI260_ACC_Z_H_G 0x11 -#define BMI260_GYR_X_L_G 0x12 -#define BMI260_GYR_X_H_G 0x13 -#define BMI260_GYR_Y_L_G 0x14 -#define BMI260_GYR_Y_H_G 0x15 -#define BMI260_GYR_Z_L_G 0x16 -#define BMI260_GYR_Z_H_G 0x17 - -#define BMI260_SENSORTIME_0 0x18 -#define BMI260_SENSORTIME_1 0x19 -#define BMI260_SENSORTIME_2 0x1a - -#define BMI260_EVENT 0x1b - -/* 2 bytes interrupt reasons*/ -#define BMI260_INT_STATUS_0 0x1c -#define BMI260_SIG_MOTION_OUT BIT(0) -#define BMI260_STEP_COUNTER_OUT BIT(1) -#define BMI260_HIGH_LOW_G_OUT BIT(2) -#define BMI260_TAP_OUT BIT(3) -#define BMI260_FLAT_OUT BIT(4) -#define BMI260_NO_MOTION_OUT BIT(5) -#define BMI260_ANY_MOTION_OUT BIT(6) -#define BMI260_ORIENTATION_OUT BIT(7) - -#define BMI260_INT_STATUS_1 0x1d -#define BMI260_FFULL_INT BIT(0 + 8) -#define BMI260_FWM_INT BIT(1 + 8) -#define BMI260_ERR_INT BIT(2 + 8) -#define BMI260_AUX_DRDY_INT BIT(5 + 8) -#define BMI260_GYR_DRDY_INT BIT(6 + 8) -#define BMI260_ACC_DRDY_INT BIT(7 + 8) - -#define BMI260_INT_MASK 0xFFFF - -#define BMI260_SC_OUT_0 0x1e -#define BMI260_SC_OUT_1 0x1f - -#define BMI260_ORIENT_ACT 0x20 - -#define BMI260_INTERNAL_STATUS 0X21 -#define BMI260_MESSAGE_MASK 0xf -#define BMI260_NOT_INIT 0x00 -#define BMI260_INIT_OK 0x01 -#define BMI260_INIT_ERR 0x02 -#define BMI260_DRV_ERR 0x03 -#define BMI260_SNS_STOP 0x04 -#define BMI260_NVM_ERROR 0x05 -#define BMI260_START_UP_ERROR 0x06 -#define BMI260_COMPAT_ERROR 0x07 - -#define BMI260_TEMPERATURE_0 0x22 -#define BMI260_TEMPERATURE_1 0x23 - -#define BMI260_FIFO_LENGTH_0 0x24 -#define BMI260_FIFO_LENGTH_1 0x25 -#define BMI260_FIFO_LENGTH_MASK (BIT(14) - 1) -#define BMI260_FIFO_DATA 0x26 - -#define BMI260_FEAT_PAGE 0x2f -/* - * The register of feature page should be read/write as 16-bit register - * Otherwise, there can be invalid data - */ -/* Features page 0 */ -#define BMI260_ORIENT_OUT 0x36 -#define BMI260_ORIENT_OUT_PORTRAIT_LANDSCAPE_MASK 3 -#define BMI260_ORIENT_PORTRAIT 0x0 -#define BMI260_ORIENT_LANDSCAPE 0x1 -#define BMI260_ORIENT_PORTRAIT_INVERT 0x2 -#define BMI260_ORIENT_LANDSCAPE_INVERT 0x3 - -/* Features page 1 */ -#define BMI260_GEN_SET_1 0x34 -#define BMI260_GYR_SELF_OFF BIT(9) - -#define BMI260_TAP_1 0x3e -#define BMI260_TAP_1_EN BIT(0) -#define BMI260_TAP_1_SENSITIVITY_OFFSET 1 -#define BMI260_TAP_1_SENSITIVITY_MASK \ - (0x7 << BMI260_TAP_1_SENSITIVITY_OFFSET) - -/* Features page 2 */ -#define BMI260_ORIENT_1 0x30 -#define BMI260_ORIENT_1_EN BIT(0) -#define BMI260_ORIENT_1_UD_EN BIT(1) -#define BMI260_ORIENT_1_MODE_OFFSET 2 -#define BMI260_ORIENT_1_MODE_MASK (0x3 << BMI260_ORIENT_1_MODE_OFFSET) -#define BMI260_ORIENT_1_BLOCK_OFFSET 4 -#define BMI260_ORIENT_1_BLOCK_MASK (0x3 << BMI260_ORIENT_1_BLOCK_OFFSET) -#define BMI260_ORIENT_1_THETA_OFFSET 6 -#define BMI260_ORIENT_1_THETA_MASK \ - ((BIT(6) - 1) << BMI260_ORIENT_1_THETA_OFFSET) - -#define BMI260_ORIENT_2 0x32 -/* hysteresis(10...0) range is 0~1g, default is 128 (0.0625g) */ -#define BMI260_ORIENT_2_HYSTERESIS_MASK (BIT(11) - 1) - -#define BMI260_ACC_CONF 0x40 -#define BMI260_ACC_BW_OFFSET 4 -#define BMI260_ACC_BW_MASK (0x7 << BMI260_ACC_BW_OFFSET) -#define BMI260_FILTER_PERF BIT(7) -#define BMI260_ULP 0x0 -#define BMI260_HP 0x1 - -#define BMI260_ACC_RANGE 0x41 -#define BMI260_GSEL_2G 0x00 -#define BMI260_GSEL_4G 0x01 -#define BMI260_GSEL_8G 0x02 -#define BMI260_GSEL_16G 0x03 - -/* The max positvie value of accel data is 0x7FFF, equal to range(g) */ -/* So, in order to get +1g, divide the 0x7FFF by range */ -#define BMI260_ACC_DATA_PLUS_1G(range) (0x7FFF / (range)) -#define BMI260_ACC_DATA_MINUS_1G(range) (-BMI260_ACC_DATA_PLUS_1G(range)) - -#define BMI260_GYR_CONF 0x42 -#define BMI260_GYR_BW_OFFSET 4 -#define BMI260_GYR_BW_MASK (0x3 << BMI260_GYR_BW_OFFSET) -#define BMI260_GYR_NOISE_PERF BIT(6) - -#define BMI260_GYR_RANGE 0x43 -#define BMI260_DPS_SEL_2000 0x00 -#define BMI260_DPS_SEL_1000 0x01 -#define BMI260_DPS_SEL_500 0x02 -#define BMI260_DPS_SEL_250 0x03 -#define BMI260_DPS_SEL_125 0x04 - -#define BMI260_AUX_CONF 0x44 - -#define BMI260_FIFO_DOWNS 0x45 - -#define BMI260_FIFO_WTM_0 0x46 -#define BMI260_FIFO_WTM_1 0x47 - -#define BMI260_FIFO_CONFIG_0 0x48 -#define BMI260_FIFO_STOP_ON_FULL BIT(0) -#define BMI260_FIFO_TIME_EN BIT(1) - -#define BMI260_FIFO_CONFIG_1 0x49 -#define BMI260_FIFO_TAG_INT1_EN_OFFSET 0 -#define BMI260_FIFO_TAG_INT1_EN_MASK (0x3 << BMI260_FIFO_TAG_INT1_EN_OFFSET) -#define BMI260_FIFO_TAG_INT2_EN_OFFSET 2 -#define BMI260_FIFO_TAG_INT2_EN_MASK (0x3 << BMI260_FIFO_TAG_INT2_EN_OFFSET) -#define BMI260_FIFO_TAG_INT_EDGE 0x0 -#define BMI260_FIFO_TAG_INT_LEVEL 0x1 -#define BMI260_FIFO_TAG_ACC_SAT 0x2 -#define BMI260_FIFO_TAG_GYR_SAT 0x3 -#define BMI260_FIFO_HEADER_EN BIT(4) -#define BMI260_FIFO_AUX_EN BIT(5) -#define BMI260_FIFO_ACC_EN BIT(6) -#define BMI260_FIFO_GYR_EN BIT(7) -#define BMI260_FIFO_SENSOR_EN(_sensor) \ - ((_sensor) == MOTIONSENSE_TYPE_ACCEL ? BMI260_FIFO_ACC_EN : \ - ((_sensor) == MOTIONSENSE_TYPE_GYRO ? BMI260_FIFO_GYR_EN : \ - BMI260_FIFO_AUX_EN)) - -#define BMI260_AUX_DEV_ID 0x4b -#define BMI260_AUX_I2C_ADDRESS BMI260_AUX_DEV_ID - -#define BMI260_AUX_IF_CONF 0x4c -#define BMI260_AUX_I2C_CONTROL BMI260_AUX_IF_CONF -#define BMI260_AUX_READ_BURST_MASK 3 -#define BMI260_AUX_MAN_READ_BURST_OFF 2 -#define BMI260_AUX_MAN_READ_BURST_MASK (0x3 << BMI280_AUX_MAN_READ_BURST_OFF) -#define BMI260_AUX_READ_BURST_1 0 -#define BMI260_AUX_READ_BURST_2 1 -#define BMI260_AUX_READ_BURST_6 2 -#define BMI260_AUX_READ_BURST_8 3 -#define BMI260_AUX_FCU_WRITE_EN BIT(6) -#define BMI260_AUX_MANUAL_EN BIT(7) - -#define BMI260_AUX_RD_ADDR 0x4d -#define BMI260_AUX_I2C_READ_ADDR BMI260_AUX_RD_ADDR -#define BMI260_AUX_WR_ADDR 0x4e -#define BMI260_AUX_I2C_WRITE_ADDR BMI260_AUX_WR_ADDR -#define BMI260_AUX_WR_DATA 0x4f -#define BMI260_AUX_I2C_WRITE_DATA BMI260_AUX_WR_DATA -#define BMI260_AUX_I2C_READ_DATA BMI260_AUX_X_L_G - -#define BMI260_ERR_REG_MSK 0x52 -#define BMI260_FATAL_ERR BIT(0) -#define BMI260_INTERNAL_ERR_OFF 1 -#define BMI260_INTERNAL_ERR_MASK (0xf << BMI260_INTERNAL_ERR_OFF) -#define BMI260_FIFO_ERR BIT(6) -#define BMI260_AUX_ERR BIT(7) - -#define BMI260_INT1_IO_CTRL 0x53 -#define BMI260_INT1_LVL BIT(1) -#define BMI260_INT1_OD BIT(2) -#define BMI260_INT1_OUTPUT_EN BIT(3) -#define BMI260_INT1_INPUT_EN BIT(4) - -#define BMI260_INT2_IO_CTRL 0x54 -#define BMI260_INT2_LVL BIT(1) -#define BMI260_INT2_OD BIT(2) -#define BMI260_INT2_OUTPUT_EN BIT(3) -#define BMI260_INT2_INPUT_EN BIT(4) - -#define BMI260_INT_LATCH 0x55 -#define BMI260_INT_LATCH_EN BIT(0) - -#define BMI260_INT1_MAP_FEAT 0x56 -#define BMI260_INT2_MAP_FEAT 0x57 -#define BMI260_MAP_SIG_MOTION_OUT BIT(0) -#define BMI260_MAP_STEP_COUNTER_OUT BIT(1) -#define BMI260_MAP_HIGH_LOW_G_OUT BIT(2) -#define BMI260_MAP_TAP_OUT BIT(3) -#define BMI260_MAP_FLAT_OUT BIT(4) -#define BMI260_MAP_NO_MOTION_OUT BIT(5) -#define BMI260_MAP_ANY_MOTION_OUT BIT(6) -#define BMI260_MAP_ORIENTAION_OUT BIT(7) - -#define BMI260_INT_MAP_DATA 0x58 -#define BMI260_MAP_FFULL_INT BIT(0) -#define BMI260_MAP_FWM_INT BIT(1) -#define BMI260_MAP_DRDY_INT BIT(2) -#define BMI260_MAP_ERR_INT BIT(3) -#define BMI260_INT_MAP_DATA_INT1_OFFSET 0 -#define BMI260_INT_MAP_DATA_INT2_OFFSET 4 -#define BMI260_INT_MAP_DATA_REG(_i, _bit) \ - (CONCAT3(BMI260_MAP_, _bit, _INT) << \ - CONCAT3(BMI260_INT_MAP_DATA_INT, _i, _OFFSET)) - -#define BMI260_INIT_CTRL 0x59 -#define BMI260_INIT_ADDR_0 0x5b -#define BMI260_INIT_ADDR_1 0x5c -#define BMI260_INIT_DATA 0x5e -#define BMI260_INTERNAL_ERROR 0x5f -#define BMI260_INT_ERR_1 BIT(1) -#define BMI260_INT_ERR_2 BIT(2) -#define BMI260_FEAT_ENG_DISABLED BIT(4) - -#define BMI260_AUX_IF_TRIM 0x68 -#define BMI260_GYR_CRT_CONF 0x69 - -#define BMI260_NVM_CONF 0x6a -#define BMI260_NVM_PROG_EN BIT(1) - -#define BMI260_IF_CONF 0x6b -#define BMI260_IF_SPI3 BIT(0) -#define BMI260_IF_SPI3_OIS BIT(1) -#define BMI260_IF_OIS_EN BIT(4) -#define BMI260_IF_AUX_EN BIT(5) - -#define BMI260_DRV 0x6c -#define BMI260_ACC_SELF_TEST 0x6d - -#define BMI260_NV_CONF 0x70 -#define BMI260_ACC_OFFSET_EN BIT(3) - -#define BMI260_OFFSET_ACC70 0x71 -#define BMI260_OFFSET_GYR70 0x74 -#define BMI260_OFFSET_EN_GYR98 0x77 -#define BMI260_OFFSET_GYRO_EN BIT(6) -#define BMI260_GYR_GAIN_EN BIT(7) - -#define BMI260_PWR_CONF 0x7c -#define BMI260_ADV_POWER_SAVE BIT(0) -#define BMI260_FIFO_SELF_WAKE_UP BIT(1) -#define BMI260_FUP_EN BIT(2) - -#define BMI260_PWR_CTRL 0x7d -#define BMI260_AUX_EN BIT(0) -#define BMI260_GYR_EN BIT(1) -#define BMI260_ACC_EN BIT(2) -#define BMI260_PWR_EN(_sensor_type) BIT(2 - _sensor_type) -#define BMI260_TEMP_EN BIT(3) - -#define BMI260_CMD_REG 0x7e -#define BMI260_CMD_FIFO_FLUSH 0xb0 -#define BMI260_CMD_SOFT_RESET 0xb6 - -#define BMI260_FF_FRAME_LEN_TS 4 -#define BMI260_FF_DATA_LEN_ACC 6 -#define BMI260_FF_DATA_LEN_GYR 6 -#define BMI260_FF_DATA_LEN_MAG 8 - -/* Root mean square noise of 100Hz accelerometer, units: ug */ -#define BMI260_ACCEL_RMS_NOISE_100HZ 1060 - -#endif /* __CROS_EC_ACCELGYRO_BMI260_H */ diff --git a/driver/accelgyro_bmi_common.h b/driver/accelgyro_bmi_common.h deleted file mode 100644 index 398f04dc42..0000000000 --- a/driver/accelgyro_bmi_common.h +++ /dev/null @@ -1,316 +0,0 @@ -/* Copyright 2020 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. - */ -/* BMI accelerometer and gyro common definitions for Chrome EC */ - -#ifndef __CROS_EC_ACCELGYRO_BMI_COMMON_H -#define __CROS_EC_ACCELGYRO_BMI_COMMON_H - -#include "accelgyro.h" -#include "accelgyro_bmi160.h" -#include "accelgyro_bmi260.h" -#include "mag_bmm150.h" -#include "accelgyro_bmi_common_public.h" - -#if !defined(CONFIG_ACCELGYRO_BMI_COMM_SPI) && \ - !defined(CONFIG_ACCELGYRO_BMI_COMM_I2C) -#error "BMI must use either SPI or I2C communication" -#endif - -#define BMI_CONF_REG(_sensor) (0x40 + 2 * (_sensor)) -#define BMI_RANGE_REG(_sensor) (0x41 + 2 * (_sensor)) - -#define BMI_ODR_MASK 0x0F -/* odr = 100 / (1 << (8 - reg)) , within limit */ -#define BMI_ODR_0_78HZ 0x01 -#define BMI_ODR_100HZ 0x08 - -#define BMI_REG_TO_ODR(_regval) \ - ((_regval) < BMI_ODR_100HZ ? 100000 / (1 << (8 - (_regval))) : \ - 100000 * (1 << ((_regval) - 8))) -#define BMI_ODR_TO_REG(_odr) \ - ((_odr) < 100000 ? (__builtin_clz(100000 / ((_odr) + 1)) - 24) : \ - (39 - __builtin_clz((_odr) / 100000))) - -enum fifo_header { - BMI_FH_EMPTY = 0x80, - BMI_FH_SKIP = 0x40, - BMI_FH_TIME = 0x44, - BMI_FH_CONFIG = 0x48 -}; - -#define BMI_FH_MODE_MASK 0xc0 -#define BMI_FH_PARM_OFFSET 2 -#define BMI_FH_PARM_MASK (0x7 << BMI_FH_PARM_OFFSET) -#define BMI_FH_EXT_MASK 0x03 - -/* Sensor resolution in number of bits. This sensor has fixed resolution. */ -#define BMI_RESOLUTION 16 -/* Min and Max sampling frequency in mHz */ -#define BMI_ACCEL_MIN_FREQ 12500 -#define BMI_ACCEL_MAX_FREQ MOTION_MAX_SENSOR_FREQUENCY(1600000, 100000) -#define BMI_GYRO_MIN_FREQ 25000 -#define BMI_GYRO_MAX_FREQ MOTION_MAX_SENSOR_FREQUENCY(3200000, 100000) - -enum bmi_running_mode { - STANDARD_UI_9DOF_FIFO = 0, - STANDARD_UI_IMU_FIFO = 1, - STANDARD_UI_IMU = 2, - STANDARD_UI_ADVANCEPOWERSAVE = 3, - ACCEL_PEDOMETER = 4, - APPLICATION_HEAD_TRACKING = 5, - APPLICATION_NAVIGATION = 6, - APPLICATION_REMOTE_CONTROL = 7, - APPLICATION_INDOOR_NAVIGATION = 8, -}; - -#define BMI_FLAG_SEC_I2C_ENABLED BIT(0) -#define BMI_FIFO_FLAG_OFFSET 4 -#define BMI_FIFO_ALL_MASK 7 - -#define BMI_GET_DATA(_s) \ - ((struct bmi_drv_data_t *)(_s)->drv_data) -#define BMI_GET_SAVED_DATA(_s) \ - (&BMI_GET_DATA(_s)->saved_data[(_s)->type]) - -#define BMI_ACC_DATA(v) (BMI160_ACC_X_L_G + \ - (v) * (BMI260_ACC_X_L_G - BMI160_ACC_X_L_G)) -#define BMI_GYR_DATA(v) (BMI160_GYR_X_L_G + \ - (v) * (BMI260_GYR_X_L_G - BMI160_GYR_X_L_G)) -#define BMI_AUX_DATA(v) (BMI160_MAG_X_L_G + \ - (v) * (BMI260_AUX_X_L_G - BMI160_MAG_X_L_G)) - -#define BMI_FIFO_CONFIG_0(v) (BMI160_FIFO_CONFIG_0 + \ - (v) * (BMI260_FIFO_CONFIG_0 - BMI160_FIFO_CONFIG_0)) -#define BMI_FIFO_CONFIG_1(v) (BMI160_FIFO_CONFIG_1 + \ - (v) * (BMI260_FIFO_CONFIG_1 - BMI160_FIFO_CONFIG_1)) -#define BMI_FIFO_SENSOR_EN(v, _sensor) (BMI160_FIFO_SENSOR_EN(_sensor) + \ - (v) * (BMI260_FIFO_SENSOR_EN(_sensor) - BMI160_FIFO_SENSOR_EN(_sensor))) - -#define BMI_TEMPERATURE_0(v) (BMI160_TEMPERATURE_0 + \ - (v) * (BMI260_TEMPERATURE_0 - BMI160_TEMPERATURE_0)) -#define BMI_INVALID_TEMP 0x8000 - -#define BMI_STATUS(v) (BMI160_STATUS + \ - (v) * (BMI260_STATUS - BMI160_STATUS)) -#define BMI_DRDY_OFF(_sensor) (7 - (_sensor)) -#define BMI_DRDY_MASK(_sensor) (1 << BMI160_DRDY_OFF(_sensor)) - -#define BMI_OFFSET_ACC70(v) (BMI160_OFFSET_ACC70 + \ - (v) * (BMI260_OFFSET_ACC70 - BMI160_OFFSET_ACC70)) -#define BMI_OFFSET_GYR70(v) (BMI160_OFFSET_GYR70 + \ - (v) * (BMI260_OFFSET_GYR70 - BMI160_OFFSET_GYR70)) -/* - * There is some bits in this register that differ between BMI160 and BMI260 - * Only use this macro for gyro offset 9:8 (BMI_OFFSET_EN_GYR98 5:0). - */ -#define BMI_OFFSET_EN_GYR98(v) (BMI160_OFFSET_EN_GYR98 + \ - (v) * (BMI260_OFFSET_EN_GYR98 - BMI160_OFFSET_EN_GYR98)) -#define BMI_OFFSET_GYR98_MASK (BIT(6) - 1) -#define BMI_OFFSET_ACC_MULTI_MG (3900 * 1024) -#define BMI_OFFSET_ACC_DIV_MG 1000000 -#define BMI_OFFSET_GYRO_MULTI_MDS (61 * 1024) -#define BMI_OFFSET_GYRO_DIV_MDS 1000 - -#define BMI_FIFO_LENGTH_0(v) (BMI160_FIFO_LENGTH_0 + \ - (v) * (BMI260_FIFO_LENGTH_0 - BMI160_FIFO_LENGTH_0)) -#define BMI_FIFO_LENGTH_MASK(v) (BMI160_FIFO_LENGTH_MASK + \ - (v) * (BMI260_FIFO_LENGTH_MASK - BMI160_FIFO_LENGTH_MASK)) -#define BMI_FIFO_DATA(v) (BMI160_FIFO_DATA + \ - (v) * (BMI260_FIFO_DATA - BMI160_FIFO_DATA)) - -#define BMI_CMD_REG(v) (BMI160_CMD_REG + \ - (v) * (BMI260_CMD_REG - BMI160_CMD_REG)) -#define BMI_CMD_FIFO_FLUSH 0xb0 - -#define BMI_ACCEL_RMS_NOISE_100HZ(v) (BMI160_ACCEL_RMS_NOISE_100HZ + \ - (v) * (BMI260_ACCEL_RMS_NOISE_100HZ - BMI160_ACCEL_RMS_NOISE_100HZ)) -#define BMI_ACCEL_100HZ 100 - -/* - * Struct for pairing an engineering value with the register value for a - * parameter. - */ -struct bmi_accel_param_pair { - int val; /* Value in engineering units. */ - int reg_val; /* Corresponding register value. */ -}; - -int bmi_get_xyz_reg(const struct motion_sensor_t *s); - -/** - * @param type Accel/Gyro - * @param psize Size of the table - * - * @return Range table of the type. - */ -const struct bmi_accel_param_pair *bmi_get_range_table( - const struct motion_sensor_t *s, int *psize); - -/** - * @return reg value that matches the given engineering value passed in. - * The round_up flag is used to specify whether to round up or down. - * Note, this function always returns a valid reg value. If the request is - * outside the range of values, it returns the closest valid reg value. - */ -int bmi_get_reg_val(const int eng_val, const int round_up, - const struct bmi_accel_param_pair *pairs, - const int size); - -/** - * @return engineering value that matches the given reg val - */ -int bmi_get_engineering_val(const int reg_val, - const struct bmi_accel_param_pair *pairs, - const int size); - -/** - * Read 8bit register from accelerometer. - */ -int bmi_read8(const int port, const uint16_t i2c_spi_addr_flags, - const int reg, int *data_ptr); - -/** - * Write 8bit register from accelerometer. - */ -int bmi_write8(const int port, const uint16_t i2c_spi_addr_flags, - const int reg, int data); - -/** - * Read 16bit register from accelerometer. - */ -int bmi_read16(const int port, const uint16_t i2c_spi_addr_flags, - const uint8_t reg, int *data_ptr); - -/** - * Write 16bit register from accelerometer. - */ -int bmi_write16(const int port, const uint16_t i2c_spi_addr_flags, - const int reg, int data); - -/** - * Read 32bit register from accelerometer. - */ -int bmi_read32(const int port, const uint16_t i2c_spi_addr_flags, - const uint8_t reg, int *data_ptr); - -/** - * Read n bytes from accelerometer. - */ -int bmi_read_n(const int port, const uint16_t i2c_spi_addr_flags, - const uint8_t reg, uint8_t *data_ptr, const int len); - -/** - * Write n bytes from accelerometer. - */ -int bmi_write_n(const int port, const uint16_t i2c_spi_addr_flags, - const uint8_t reg, const uint8_t *data_ptr, const int len); - -/* - * Enable/Disable specific bit set of a 8-bit reg. - */ -int bmi_enable_reg8(const struct motion_sensor_t *s, - int reg, uint8_t bits, int enable); - -/* - * Set specific bit set to certain value of a 8-bit reg. - */ -int bmi_set_reg8(const struct motion_sensor_t *s, int reg, - uint8_t bits, int mask); - -/* - * @s: base sensor. - * @v: output vector. - * @input: 6-bits array input. - */ -void bmi_normalize(const struct motion_sensor_t *s, intv3_t v, uint8_t *input); - -/* - * Decode the header from the fifo. - * Return 0 if we need further processing. - * Sensor mutex must be held during processing, to protect the fifos. - * - * @accel: base sensor - * @hdr: the header to decode - * @last_ts: the last timestamp of fifo interrupt. - * @bp: current pointer in the buffer, updated when processing the header. - * @ep: pointer to the end of the valid data in the buffer. - */ -int bmi_decode_header(struct motion_sensor_t *accel, - enum fifo_header hdr, uint32_t last_ts, - uint8_t **bp, uint8_t *ep); -/** - * Retrieve hardware FIFO from sensor, - * - put data in Sensor Hub fifo. - * - update sensor raw_xyz vector with the last information. - * We put raw data in hub fifo and process data from there. - * @s: Pointer to sensor data. - * @last_ts: The last timestamp of fifo interrupt. - * - * Read only up to bmi_buffer. If more reads are needed, we will be called - * again by the interrupt routine. - * - * NOTE: If a new driver supports this function, be sure to add a check - * for spoof_mode in order to load the sensor stack with the spoofed - * data. See accelgyro_bmi260.c::load_fifo for an example. - */ -int bmi_load_fifo(struct motion_sensor_t *s, uint32_t last_ts); - -int bmi_set_range(struct motion_sensor_t *s, int range, int rnd); - -int bmi_get_data_rate(const struct motion_sensor_t *s); - - -int bmi_get_offset(const struct motion_sensor_t *s, - int16_t *offset, int16_t *temp); - -int bmi_get_resolution(const struct motion_sensor_t *s); - -#ifdef CONFIG_BODY_DETECTION -int bmi_get_rms_noise(const struct motion_sensor_t *s); -#endif - -int bmi_set_scale(const struct motion_sensor_t *s, - const uint16_t *scale, int16_t temp); - -int bmi_get_scale(const struct motion_sensor_t *s, - uint16_t *scale, int16_t *temp); - -/* Start/Stop the FIFO collecting events */ -int bmi_enable_fifo(const struct motion_sensor_t *s, int enable); - -/* Read the xyz data of accel/gyro */ -int bmi_read(const struct motion_sensor_t *s, intv3_t v); - -/* Read temperature of sensor s */ -int bmi_read_temp(const struct motion_sensor_t *s, int *temp_ptr); - -/* Read temperature of sensor idx */ -int bmi_get_sensor_temp(int idx, int *temp_ptr); - -/* - * Get the normalized rate according to input rate and input rnd - * @rate: input rate - * @rnd: round up - * @normalized_rate_ptr: normalized rate pointer for output - * @reg_val_ptr: pointer to the actual register value of normalized rate for - * output. - */ -int bmi_get_normalized_rate(const struct motion_sensor_t *s, int rate, int rnd, - int *normalized_rate_ptr, uint8_t *reg_val_ptr); - -/* Get the accelerometer offset */ -void bmi_accel_get_offset(const struct motion_sensor_t *accel, intv3_t v); - -/* Get the gyroscope offset */ -void bmi_gyro_get_offset(const struct motion_sensor_t *gyro, intv3_t v); - -/* Set the accelerometer offset */ -void bmi_set_accel_offset(const struct motion_sensor_t *accel, intv3_t v); - -/* Set the gyroscope offset */ -void bmi_set_gyro_offset(const struct motion_sensor_t *gyro, intv3_t v, - int *val98_ptr); - -#endif /* __CROS_EC_ACCELGYRO_BMI_COMMON_H */ diff --git a/driver/mag_bmm150.h b/driver/mag_bmm150.h deleted file mode 100644 index 9f517f8097..0000000000 --- a/driver/mag_bmm150.h +++ /dev/null @@ -1,150 +0,0 @@ -/* Copyright 2015 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. - */ - -/* BMM150 magnetometer definition */ - -#ifndef __CROS_EC_MAG_BMM150_H -#define __CROS_EC_MAG_BMM150_H - -#include "accelgyro.h" -#include "mag_cal.h" - -#define BMM150_ADDR0_FLAGS 0x10 -#define BMM150_ADDR1_FLAGS 0x11 -#define BMM150_ADDR2_FLAGS 0x12 -#define BMM150_ADDR3_FLAGS 0x13 - -#define BMM150_CHIP_ID 0x40 -#define BMM150_CHIP_ID_MAJOR 0x32 - -#define BMM150_BASE_DATA 0x42 - -#define BMM150_INT_STATUS 0x4a -#define BMM150_PWR_CTRL 0x4b -#define BMM150_SRST (BIT(7) | BIT(1)) -#define BMM150_PWR_ON BIT(0) - -#define BMM150_OP_CTRL 0x4c -#define BMM150_OP_MODE_OFFSET 1 -#define BMM150_OP_MODE_MASK 3 -#define BMM150_OP_MODE_NORMAL 0x00 -#define BMM150_OP_MODE_FORCED 0x01 -#define BMM150_OP_MODE_SLEEP 0x03 - -#define BMM150_INT_CTRL 0x4d - -#define BMM150_REPXY 0x51 -#define BMM150_LOW_POWER_nXY 3 -#define BMM150_REGULAR_nXY 9 -#define BMM150_ENHANCED_nXY 15 -#define BMM150_HIGH_ACCURACY_nXY 47 -#define BMM150_SPECIAL_nXY 75 -#define BMM150_REPZ 0x52 -#define BMM150_LOW_POWER_nZ 3 -#define BMM150_REGULAR_nZ 15 -#define BMM150_ENHANCED_nZ 27 -#define BMM150_HIGH_ACCURACY_nZ 83 -#define BMM150_SPECIAL_nZ 27 - -#define BMM150_REP(_preset, _axis) CONCAT4(BMM150_, _preset, _n, _axis) - -/* Hidden registers for RHALL calculation */ -#define BMM150_REGA_DIG_X1 0x5d -#define BMM150_REGA_DIG_Y1 0x5e -#define BMM150_REGA_DIG_Z4_LSB 0x62 -#define BMM150_REGA_DIG_Z4_MSB 0x63 -#define BMM150_REGA_DIG_X2 0x64 -#define BMM150_REGA_DIG_Y2 0x65 -#define BMM150_REGA_DIG_Z2_LSB 0x68 -#define BMM150_REGA_DIG_Z2_MSB 0x69 -#define BMM150_REGA_DIG_Z1_LSB 0x6a -#define BMM150_REGA_DIG_Z1_MSB 0x6b -#define BMM150_REGA_DIG_XYZ1_LSB 0x6c -#define BMM150_REGA_DIG_XYZ1_MSB 0x6d -#define BMM150_REGA_DIG_Z3_LSB 0x6e -#define BMM150_REGA_DIG_Z3_MSB 0x6f -#define BMM150_REGA_DIG_XY2 0x70 -#define BMM150_REGA_DIG_XY1 0x71 - -/* Overflow */ - -#define BMM150_FLIP_OVERFLOW_ADCVAL (-4096) -#define BMM150_HALL_OVERFLOW_ADCVAL (-16384) -#define BMM150_OVERFLOW_OUTPUT (0x8000) - -/* Min and Max sampling frequency in mHz */ -#define BMM150_MAG_MIN_FREQ 781 - -/* - * From Section 4.2.4, max frequency depends on the preset. - * - * Fmax ~= 1 / (145us * nXY + 500us * nZ + 980us) - * - * To be safe, declare only 75% of the value. - */ -#define __BMM150_MAG_MAX_FREQ(_preset) (750000000 / \ - (145 * BMM150_REP(_preset, XY) + 500 * BMM150_REP(_preset, Z) + 980)) - -#if (__BMM150_MAG_MAX_FREQ(SPECIAL) > CONFIG_EC_MAX_SENSOR_FREQ_MILLIHZ) -#error "EC too slow for magnetometer" -#endif - -struct bmm150_comp_registers { - /* Local copy of the compensation registers. */ - int8_t dig1[2]; - int8_t dig2[2]; - - uint16_t dig_z1; - int16_t dig_z2; - int16_t dig_z3; - int16_t dig_z4; - - uint8_t dig_xy1; - int8_t dig_xy2; - - uint16_t dig_xyz1; -}; - -struct bmm150_private_data { - /* lsm6dsm_data union requires cal be first element */ - struct mag_cal_t cal; - struct bmm150_comp_registers comp; -}; - -#ifdef CONFIG_MAG_BMI_BMM150 -#include "accelgyro_bmi_common.h" - -#define BMM150_COMP_REG(_s) \ - (&BMI_GET_DATA(_s)->compass.comp) - -#define BMM150_CAL(_s) \ - (&BMI_GET_DATA(_s)->compass.cal) -/* - * Behind a BMI, the BMM150 is in forced mode. Be sure to choose a frequency - * compatible with BMI. - */ -#define BMM150_MAG_MAX_FREQ(_preset) \ - BMI_REG_TO_ODR(BMI_ODR_TO_REG(__BMM150_MAG_MAX_FREQ(_preset))) -#else -#define BMM150_COMP_REG(_s) NULL -#define BMM150_CAL(_s) NULL -#define BMM150_MAG_MAX_FREQ(_preset) __BMM150_MAG_MAX_FREQ(_preset) -#endif - -/* Specific initialization of BMM150 when behing BMI160 */ -int bmm150_init(struct motion_sensor_t *s); - -/* Command to normalize and apply temperature compensation */ -void bmm150_normalize(const struct motion_sensor_t *s, - intv3_t v, - uint8_t *data); - -int bmm150_set_offset(const struct motion_sensor_t *s, - const intv3_t offset); - -int bmm150_get_offset(const struct motion_sensor_t *s, - intv3_t offset); - -#endif /* __CROS_EC_MAG_BMM150_H */ diff --git a/include/driver/accelgyro_bmi160.h b/include/driver/accelgyro_bmi160.h new file mode 100644 index 0000000000..c916576130 --- /dev/null +++ b/include/driver/accelgyro_bmi160.h @@ -0,0 +1,394 @@ +/* Copyright 2015 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. + */ + +/* BMI160 accelerometer and gyro and BMM150 compass module for Chrome EC */ + +#ifndef __CROS_EC_ACCELGYRO_BMI160_H +#define __CROS_EC_ACCELGYRO_BMI160_H + +#include "accelgyro.h" +#include "driver/accelgyro_bmi160_public.h" +#include "mag_bmm150.h" + +#define BMI160_CHIP_ID 0x00 +#define BMI160_CHIP_ID_MAJOR 0xd1 +#define BMI168_CHIP_ID_MAJOR 0xd2 + +#define BMI160_SPEC_ACC_STARTUP_TIME_MS 10 +#define BMI160_SPEC_GYR_STARTUP_TIME_MS 80 +#define BMI160_SPEC_MAG_STARTUP_TIME_MS 60 + + +#define BMI160_ERR_REG 0x02 +#define BMI160_PMU_STATUS 0x03 +#define BMI160_PMU_MAG_OFFSET 0 +#define BMI160_PMU_GYR_OFFSET 2 +#define BMI160_PMU_ACC_OFFSET 4 +#define BMI160_PMU_SENSOR_STATUS(_sensor_type, _val) \ + (((_val) >> (4 - 2 * (_sensor_type))) & 0x3) +#define BMI160_PMU_SUSPEND 0 +#define BMI160_PMU_NORMAL 1 +#define BMI160_PMU_LOW_POWER 2 +#define BMI160_PMU_FAST_STARTUP 3 + +#define BMI160_MAG_X_L_G 0x04 +#define BMI160_MAG_X_H_G 0x05 +#define BMI160_MAG_Y_L_G 0x06 +#define BMI160_MAG_Y_H_G 0x07 +#define BMI160_MAG_Z_L_G 0x08 +#define BMI160_MAG_Z_H_G 0x09 +#define BMI160_RHALL_L_G 0x0a +#define BMI160_RHALL_H_G 0x0b +#define BMI160_GYR_X_L_G 0x0c +#define BMI160_GYR_X_H_G 0x0d +#define BMI160_GYR_Y_L_G 0x0e +#define BMI160_GYR_Y_H_G 0x0f +#define BMI160_GYR_Z_L_G 0x10 +#define BMI160_GYR_Z_H_G 0x11 +#define BMI160_ACC_X_L_G 0x12 +#define BMI160_ACC_X_H_G 0x13 +#define BMI160_ACC_Y_L_G 0x14 +#define BMI160_ACC_Y_H_G 0x15 +#define BMI160_ACC_Z_L_G 0x16 +#define BMI160_ACC_Z_H_G 0x17 + +#define BMI160_SENSORTIME_0 0x18 +#define BMI160_SENSORTIME_1 0x19 +#define BMI160_SENSORTIME_2 0x1a + +#define BMI160_STATUS 0x1b +#define BMI160_POR_DETECTED BIT(0) +#define BMI160_GYR_SLF_TST BIT(1) +#define BMI160_MAG_MAN_OP BIT(2) +#define BMI160_FOC_RDY BIT(3) +#define BMI160_NVM_RDY BIT(4) +#define BMI160_DRDY_MAG BIT(5) +#define BMI160_DRDY_GYR BIT(6) +#define BMI160_DRDY_ACC BIT(7) +#define BMI160_DRDY_OFF(_sensor) (7 - (_sensor)) +#define BMI160_DRDY_MASK(_sensor) (1 << BMI160_DRDY_OFF(_sensor)) + +/* first 2 bytes are the interrupt reasons, next 2 some qualifier */ +#define BMI160_INT_STATUS_0 0x1c +#define BMI160_STEP_INT BIT(0) +#define BMI160_SIGMOT_INT BIT(1) +#define BMI160_ANYM_INT BIT(2) +#define BMI160_PMU_TRIGGER_INT BIT(3) +#define BMI160_D_TAP_INT BIT(4) +#define BMI160_S_TAP_INT BIT(5) +#define BMI160_ORIENT_INT BIT(6) +#define BMI160_FLAT_INT BIT(7) +#define BMI160_ORIENT_XY_MASK 0x30 +#define BMI160_ORIENT_PORTRAIT (0 << 4) +#define BMI160_ORIENT_PORTRAIT_INVERT BIT(4) +#define BMI160_ORIENT_LANDSCAPE (2 << 4) +#define BMI160_ORIENT_LANDSCAPE_INVERT (3 << 4) + + +#define BMI160_INT_STATUS_1 0x1d +#define BMI160_HIGHG_INT (1 << (2 + 8)) +#define BMI160_LOWG_INT (1 << (3 + 8)) +#define BMI160_DRDY_INT (1 << (4 + 8)) +#define BMI160_FFULL_INT (1 << (5 + 8)) +#define BMI160_FWM_INT (1 << (6 + 8)) +#define BMI160_NOMO_INT (1 << (7 + 8)) + +#define BMI160_INT_MASK 0xFFFF + +#define BMI160_INT_STATUS_2 0x1e +#define BMI160_INT_STATUS_3 0x1f +#define BMI160_FIRST_X (1 << (0 + 16)) +#define BMI160_FIRST_Y (1 << (1 + 16)) +#define BMI160_FIRST_Z (1 << (2 + 16)) +#define BMI160_SIGN (1 << (3 + 16)) +#define BMI160_ANYM_OFFSET 0 +#define BMI160_TAP_OFFSET 4 +#define BMI160_HIGH_OFFSET 8 +#define BMI160_INT_INFO(_type, _data) \ +(CONCAT2(BMI160_, _data) << CONCAT3(BMI160_, _type, _OFFSET)) + +#define BMI160_ORIENT_Z (1 << (6 + 24)) +#define BMI160_FLAT (1 << (7 + 24)) + +#define BMI160_TEMPERATURE_0 0x20 +#define BMI160_TEMPERATURE_1 0x21 + + +#define BMI160_FIFO_LENGTH_0 0x22 +#define BMI160_FIFO_LENGTH_1 0x23 +#define BMI160_FIFO_LENGTH_MASK (BIT(11) - 1) +#define BMI160_FIFO_DATA 0x24 + +#define BMI160_ACC_CONF 0x40 +#define BMI160_ACC_BW_OFFSET 4 +#define BMI160_ACC_BW_MASK (0x7 << BMI160_ACC_BW_OFFSET) + +#define BMI160_ACC_RANGE 0x41 +#define BMI160_GSEL_2G 0x03 +#define BMI160_GSEL_4G 0x05 +#define BMI160_GSEL_8G 0x08 +#define BMI160_GSEL_16G 0x0c + +#define BMI160_GYR_CONF 0x42 +#define BMI160_GYR_BW_OFFSET 4 +#define BMI160_GYR_BW_MASK (0x3 << BMI160_GYR_BW_OFFSET) + +#define BMI160_GYR_RANGE 0x43 +#define BMI160_DPS_SEL_2000 0x00 +#define BMI160_DPS_SEL_1000 0x01 +#define BMI160_DPS_SEL_500 0x02 +#define BMI160_DPS_SEL_250 0x03 +#define BMI160_DPS_SEL_125 0x04 + +#define BMI160_MAG_CONF 0x44 + +#define BMI160_FIFO_DOWNS 0x45 +#define BMI160_FIFO_CONFIG_0 0x46 +#define BMI160_FIFO_CONFIG_1 0x47 +#define BMI160_FIFO_TAG_TIME_EN BIT(1) +#define BMI160_FIFO_TAG_INT2_EN BIT(2) +#define BMI160_FIFO_TAG_INT1_EN BIT(3) +#define BMI160_FIFO_HEADER_EN BIT(4) +#define BMI160_FIFO_MAG_EN BIT(5) +#define BMI160_FIFO_ACC_EN BIT(6) +#define BMI160_FIFO_GYR_EN BIT(7) +#define BMI160_FIFO_TARG_INT(_i) CONCAT3(BMI160_FIFO_TAG_INT, _i, _EN) +#define BMI160_FIFO_SENSOR_EN(_sensor) \ + ((_sensor) == MOTIONSENSE_TYPE_ACCEL ? BMI160_FIFO_ACC_EN : \ + ((_sensor) == MOTIONSENSE_TYPE_GYRO ? BMI160_FIFO_GYR_EN : \ + BMI160_FIFO_MAG_EN)) + +#define BMI160_MAG_IF_0 0x4b +#define BMI160_MAG_I2C_ADDRESS BMI160_MAG_IF_0 +#define BMI160_MAG_IF_1 0x4c +#define BMI160_MAG_I2C_CONTROL BMI160_MAG_IF_1 +#define BMI160_MAG_READ_BURST_MASK 3 +#define BMI160_MAG_READ_BURST_1 0 +#define BMI160_MAG_READ_BURST_2 1 +#define BMI160_MAG_READ_BURST_6 2 +#define BMI160_MAG_READ_BURST_8 3 +#define BMI160_MAG_OFFSET_OFF 3 +#define BMI160_MAG_OFFSET_MASK (0xf << BMI160_MAG_OFFSET_OFF) +#define BMI160_MAG_MANUAL_EN BIT(7) + +#define BMI160_MAG_IF_2 0x4d +#define BMI160_MAG_I2C_READ_ADDR BMI160_MAG_IF_2 +#define BMI160_MAG_IF_3 0x4e +#define BMI160_MAG_I2C_WRITE_ADDR BMI160_MAG_IF_3 +#define BMI160_MAG_IF_4 0x4f +#define BMI160_MAG_I2C_WRITE_DATA BMI160_MAG_IF_4 +#define BMI160_MAG_I2C_READ_DATA BMI160_MAG_X_L_G + +#define BMI160_INT_EN_0 0x50 +#define BMI160_INT_ANYMO_X_EN BIT(0) +#define BMI160_INT_ANYMO_Y_EN BIT(1) +#define BMI160_INT_ANYMO_Z_EN BIT(2) +#define BMI160_INT_D_TAP_EN BIT(4) +#define BMI160_INT_S_TAP_EN BIT(5) +#define BMI160_INT_ORIENT_EN BIT(6) +#define BMI160_INT_FLAT_EN BIT(7) +#define BMI160_INT_EN_1 0x51 +#define BMI160_INT_HIGHG_X_EN BIT(0) +#define BMI160_INT_HIGHG_Y_EN BIT(1) +#define BMI160_INT_HIGHG_Z_EN BIT(2) +#define BMI160_INT_LOW_EN BIT(3) +#define BMI160_INT_DRDY_EN BIT(4) +#define BMI160_INT_FFUL_EN BIT(5) +#define BMI160_INT_FWM_EN BIT(6) +#define BMI160_INT_EN_2 0x52 +#define BMI160_INT_NOMOX_EN BIT(0) +#define BMI160_INT_NOMOY_EN BIT(1) +#define BMI160_INT_NOMOZ_EN BIT(2) +#define BMI160_INT_STEP_DET_EN BIT(3) + +#define BMI160_INT_OUT_CTRL 0x53 +#define BMI160_INT_EDGE_CTRL BIT(0) +#define BMI160_INT_LVL_CTRL BIT(1) +#define BMI160_INT_OD BIT(2) +#define BMI160_INT_OUTPUT_EN BIT(3) +#define BMI160_INT1_CTRL_OFFSET 0 +#define BMI160_INT2_CTRL_OFFSET 4 +#define BMI160_INT_CTRL(_i, _bit) \ +(CONCAT2(BMI160_INT_, _bit) << CONCAT3(BMI160_INT, _i, _CTRL_OFFSET)) + +#define BMI160_INT_LATCH 0x54 +#define BMI160_INT1_INPUT_EN BIT(4) +#define BMI160_INT2_INPUT_EN BIT(5) +#define BMI160_LATCH_MASK 0xf +#define BMI160_LATCH_NONE 0 +#define BMI160_LATCH_5MS 5 +#define BMI160_LATCH_FOREVER 0xf + +#define BMI160_INT_MAP_0 0x55 +#define BMI160_INT_LOWG_STEP BIT(0) +#define BMI160_INT_HIGHG BIT(1) +#define BMI160_INT_ANYMOTION BIT(2) +#define BMI160_INT_NOMOTION BIT(3) +#define BMI160_INT_D_TAP BIT(4) +#define BMI160_INT_S_TAP BIT(5) +#define BMI160_INT_ORIENT BIT(6) +#define BMI160_INT_FLAT BIT(7) + +#define BMI160_INT_MAP_1 0x56 +#define BMI160_INT_PMU_TRIG BIT(0) +#define BMI160_INT_FFULL BIT(1) +#define BMI160_INT_FWM BIT(2) +#define BMI160_INT_DRDY BIT(3) +#define BMI160_INT1_MAP_OFFSET 4 +#define BMI160_INT2_MAP_OFFSET 0 +#define BMI160_INT_MAP(_i, _bit) \ +(CONCAT2(BMI160_INT_, _bit) << CONCAT3(BMI160_INT, _i, _MAP_OFFSET)) +#define BMI160_INT_FIFO_MAP BMI160_INT_MAP_1 + +#define BMI160_INT_MAP_2 0x57 + +#define BMI160_INT_MAP_INT_1 BMI160_INT_MAP_0 +#define BMI160_INT_MAP_INT_2 BMI160_INT_MAP_2 +#define BMI160_INT_MAP_REG(_i) CONCAT2(BMI160_INT_MAP_INT_, _i) + +#define BMI160_INT_DATA_0 0x58 +#define BMI160_INT_DATA_1 0x59 + +#define BMI160_INT_LOW_HIGH_0 0x5a +#define BMI160_INT_LOW_HIGH_1 0x5b +#define BMI160_INT_LOW_HIGH_2 0x5c +#define BMI160_INT_LOW_HIGH_3 0x5d +#define BMI160_INT_LOW_HIGH_4 0x5e + +#define BMI160_INT_MOTION_0 0x5f +#define BMI160_INT_MOTION_1 0x60 +/* + * The formula is defined in 2.11.25 (any motion interrupt [1]). + * + * if we want threshold at a (in mg), the register should be x, where + * x * 7.81mg = a, assuming a range of 4G, which is + * x * 4 * 1.953 = a so + * x = a * 1000 / range * 1953 + */ +#define BMI160_MOTION_TH(_s, _mg) \ + (MIN(((_mg) * 1000) / ((_s)->current_range * 1953), 0xff)) +#define BMI160_INT_MOTION_2 0x61 +#define BMI160_INT_MOTION_3 0x62 +#define BMI160_MOTION_NO_MOT_SEL BIT(0) +#define BMI160_MOTION_SIG_MOT_SEL BIT(1) +#define BMI160_MOTION_SKIP_OFF 2 +#define BMI160_MOTION_SKIP_MASK 0x3 +#define BMI160_MOTION_SKIP_TIME(_ms) \ + (MIN(__fls((_ms) / 1500), BMI160_MOTION_SKIP_MASK)) +#define BMI160_MOTION_PROOF_OFF 4 +#define BMI160_MOTION_PROOF_MASK 0x3 +#define BMI160_MOTION_PROOF_TIME(_ms) \ + (MIN(__fls((_ms) / 250), BMI160_MOTION_PROOF_MASK)) + +#define BMI160_INT_TAP_0 0x63 +#define BMI160_TAP_DUR(_s, _ms) \ + ((_ms) <= 250 ? MAX((_ms), 50) / 50 - 1 : \ + (_ms) <= 500 ? 4 + ((_ms) - 250) / 125 : \ + (_ms) < 700 ? 6 : 7) + +#define BMI160_INT_TAP_1 0x64 +#define BMI160_TAP_TH(_s, _mg) \ + (MIN(((_mg) * 1000) / ((_s)->current_range * 31250), 0x1f)) + +#define BMI160_INT_ORIENT_0 0x65 + +/* No hysterisis, theta block, int on slope > 0.2 or axis > 1.5, symmetrical */ +#define BMI160_INT_ORIENT_0_INIT_VAL 0x48 + +#define BMI160_INT_ORIENT_1 0x66 + +/* no axes remap, no int on up/down, no blocking angle */ +#define BMI160_INT_ORIENT_1_INIT_VAL 0x00 + +#define BMI160_INT_FLAT_0 0x67 +#define BMI160_INT_FLAT_1 0x68 + +#define BMI160_FOC_CONF 0x69 +#define BMI160_FOC_GYRO_EN BIT(6) +#define BMI160_FOC_ACC_PLUS_1G 1 +#define BMI160_FOC_ACC_MINUS_1G 2 +#define BMI160_FOC_ACC_0G 3 +#define BMI160_FOC_ACC_Z_OFFSET 0 +#define BMI160_FOC_ACC_Y_OFFSET 2 +#define BMI160_FOC_ACC_X_OFFSET 4 + +#define BMI160_CONF 0x6a +#define BMI160_IF_CONF 0x6b +#define BMI160_IF_MODE_OFF 4 +#define BMI160_IF_MODE_MASK 3 +#define BMI160_IF_MODE_AUTO_OFF 0 +#define BMI160_IF_MODE_I2C_IOS 1 +#define BMI160_IF_MODE_AUTO_I2C 2 + +#define BMI160_PMU_TRIGGER 0x6c +#define BMI160_SELF_TEST 0x6d + +#define BMI160_NV_CONF 0x70 + +#define BMI160_OFFSET_ACC70 0x71 +#define BMI160_OFFSET_GYR70 0x74 +#define BMI160_OFFSET_EN_GYR98 0x77 +#define BMI160_OFFSET_ACC_EN BIT(6) +#define BMI160_OFFSET_GYRO_EN BIT(7) + +#define BMI160_STEP_CNT_0 0x78 +#define BMI160_STEP_CNT_1 0x79 +#define BMI160_STEP_CONF_0 0x7a +#define BMI160_STEP_CONF_1 0x7b + +#define BMI160_CMD_REG 0x7e +#define BMI160_CMD_SOFT_RESET 0xb6 +#define BMI160_CMD_NOOP 0x00 +#define BMI160_CMD_START_FOC 0x03 +#define BMI160_CMD_ACC_MODE_OFFSET 0x10 +#define BMI160_CMD_ACC_MODE_SUSP 0x10 +#define BMI160_CMD_ACC_MODE_NORMAL 0x11 +#define BMI160_CMD_ACC_MODE_LOWPOWER 0x12 +#define BMI160_CMD_GYR_MODE_SUSP 0x14 +#define BMI160_CMD_GYR_MODE_NORMAL 0x15 +#define BMI160_CMD_GYR_MODE_FAST_STARTUP 0x17 +#define BMI160_CMD_MAG_MODE_SUSP 0x18 +#define BMI160_CMD_MAG_MODE_NORMAL 0x19 +#define BMI160_CMD_MAG_MODE_LOWPOWER 0x1a +#define BMI160_CMD_MODE_SUSPEND(_sensor_type) \ + (BMI160_CMD_ACC_MODE_OFFSET | (_sensor_type) << 2 | BMI160_PMU_SUSPEND) +#define BMI160_CMD_MODE_NORMAL(_sensor_type) \ + (BMI160_CMD_ACC_MODE_OFFSET | (_sensor_type) << 2 | BMI160_PMU_NORMAL) + +#define BMI160_CMD_FIFO_FLUSH 0xb0 +#define BMI160_CMD_INT_RESET 0xb1 +#define BMI160_CMD_SOFT_RESET 0xb6 +#define BMI160_CMD_EXT_MODE_EN_B0 0x37 +#define BMI160_CMD_EXT_MODE_EN_B1 0x9a +#define BMI160_CMD_EXT_MODE_EN_B2 0xc0 + +#define BMI160_CMD_EXT_MODE_ADDR 0x7f +#define BMI160_CMD_PAGING_EN BIT(7) +#define BMI160_CMD_TARGET_PAGE BIT(4) +#define BMI160_COM_C_TRIM_ADDR 0x85 +#define BMI160_COM_C_TRIM (3 << 4) + +#define BMI160_CMD_TGT_PAGE 0 +#define BMI160_CMD_TGT_PAGE_COM 1 +#define BMI160_CMD_TGT_PAGE_ACC 2 +#define BMI160_CMD_TGT_PAGE_GYR 3 + +#define BMI160_FF_FRAME_LEN_TS 4 +#define BMI160_FF_DATA_LEN_ACC 6 +#define BMI160_FF_DATA_LEN_GYR 6 +#define BMI160_FF_DATA_LEN_MAG 8 + +/* Root mean square noise of 100 Hz accelerometer, units: ug */ +#define BMI160_ACCEL_RMS_NOISE_100HZ 1300 + +#ifdef CONFIG_BMI_SEC_I2C +/* Functions to access the secondary device through the accel/gyro. */ +int bmi160_sec_raw_read8(const int port, const uint16_t addr_flags, + const uint8_t reg, int *data_ptr); +int bmi160_sec_raw_write8(const int port, const uint16_t addr_flags, + const uint8_t reg, int data); +#endif + +#endif /* __CROS_EC_ACCELGYRO_BMI160_H */ diff --git a/include/driver/accelgyro_bmi260.h b/include/driver/accelgyro_bmi260.h new file mode 100644 index 0000000000..9f39dd568a --- /dev/null +++ b/include/driver/accelgyro_bmi260.h @@ -0,0 +1,324 @@ +/* Copyright 2020 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. + */ + +/* BMI260 accelerometer and gyro for Chrome EC */ + +#ifndef __CROS_EC_ACCELGYRO_BMI260_H +#define __CROS_EC_ACCELGYRO_BMI260_H + +#include "accelgyro.h" +#include "common.h" +#include "mag_bmm150.h" +#include "driver/accelgyro_bmi260_public.h" + +#define BMI260_CHIP_ID 0x00 +#define BMI260_CHIP_ID_MAJOR 0x27 + +#define BMI260_ERR_REG 0x02 + +#define BMI260_STATUS 0x03 +#define BMI260_AUX_BUSY BIT(2) +#define BMI260_CMD_RDY BIT(4) +#define BMI260_DRDY_AUX BIT(5) +#define BMI260_DRDY_GYR BIT(6) +#define BMI260_DRDY_ACC BIT(7) +#define BMI260_DRDY_OFF(_sensor) (7 - (_sensor)) +#define BMI260_DRDY_MASK(_sensor) (1 << BMI260_DRDY_OFF(_sensor)) + +#define BMI260_AUX_X_L_G 0x04 +#define BMI260_AUX_X_H_G 0x05 +#define BMI260_AUX_Y_L_G 0x06 +#define BMI260_AUX_Y_H_G 0x07 +#define BMI260_AUX_Z_L_G 0x08 +#define BMI260_AUX_Z_H_G 0x09 +#define BMI260_AUX_R_L_G 0x0a +#define BMI260_AUX_R_H_G 0x0b +#define BMI260_ACC_X_L_G 0x0c +#define BMI260_ACC_X_H_G 0x0d +#define BMI260_ACC_Y_L_G 0x0e +#define BMI260_ACC_Y_H_G 0x0f +#define BMI260_ACC_Z_L_G 0x10 +#define BMI260_ACC_Z_H_G 0x11 +#define BMI260_GYR_X_L_G 0x12 +#define BMI260_GYR_X_H_G 0x13 +#define BMI260_GYR_Y_L_G 0x14 +#define BMI260_GYR_Y_H_G 0x15 +#define BMI260_GYR_Z_L_G 0x16 +#define BMI260_GYR_Z_H_G 0x17 + +#define BMI260_SENSORTIME_0 0x18 +#define BMI260_SENSORTIME_1 0x19 +#define BMI260_SENSORTIME_2 0x1a + +#define BMI260_EVENT 0x1b + +/* 2 bytes interrupt reasons*/ +#define BMI260_INT_STATUS_0 0x1c +#define BMI260_SIG_MOTION_OUT BIT(0) +#define BMI260_STEP_COUNTER_OUT BIT(1) +#define BMI260_HIGH_LOW_G_OUT BIT(2) +#define BMI260_TAP_OUT BIT(3) +#define BMI260_FLAT_OUT BIT(4) +#define BMI260_NO_MOTION_OUT BIT(5) +#define BMI260_ANY_MOTION_OUT BIT(6) +#define BMI260_ORIENTATION_OUT BIT(7) + +#define BMI260_INT_STATUS_1 0x1d +#define BMI260_FFULL_INT BIT(0 + 8) +#define BMI260_FWM_INT BIT(1 + 8) +#define BMI260_ERR_INT BIT(2 + 8) +#define BMI260_AUX_DRDY_INT BIT(5 + 8) +#define BMI260_GYR_DRDY_INT BIT(6 + 8) +#define BMI260_ACC_DRDY_INT BIT(7 + 8) + +#define BMI260_INT_MASK 0xFFFF + +#define BMI260_SC_OUT_0 0x1e +#define BMI260_SC_OUT_1 0x1f + +#define BMI260_ORIENT_ACT 0x20 + +#define BMI260_INTERNAL_STATUS 0X21 +#define BMI260_MESSAGE_MASK 0xf +#define BMI260_NOT_INIT 0x00 +#define BMI260_INIT_OK 0x01 +#define BMI260_INIT_ERR 0x02 +#define BMI260_DRV_ERR 0x03 +#define BMI260_SNS_STOP 0x04 +#define BMI260_NVM_ERROR 0x05 +#define BMI260_START_UP_ERROR 0x06 +#define BMI260_COMPAT_ERROR 0x07 + +#define BMI260_TEMPERATURE_0 0x22 +#define BMI260_TEMPERATURE_1 0x23 + +#define BMI260_FIFO_LENGTH_0 0x24 +#define BMI260_FIFO_LENGTH_1 0x25 +#define BMI260_FIFO_LENGTH_MASK (BIT(14) - 1) +#define BMI260_FIFO_DATA 0x26 + +#define BMI260_FEAT_PAGE 0x2f +/* + * The register of feature page should be read/write as 16-bit register + * Otherwise, there can be invalid data + */ +/* Features page 0 */ +#define BMI260_ORIENT_OUT 0x36 +#define BMI260_ORIENT_OUT_PORTRAIT_LANDSCAPE_MASK 3 +#define BMI260_ORIENT_PORTRAIT 0x0 +#define BMI260_ORIENT_LANDSCAPE 0x1 +#define BMI260_ORIENT_PORTRAIT_INVERT 0x2 +#define BMI260_ORIENT_LANDSCAPE_INVERT 0x3 + +/* Features page 1 */ +#define BMI260_GEN_SET_1 0x34 +#define BMI260_GYR_SELF_OFF BIT(9) + +#define BMI260_TAP_1 0x3e +#define BMI260_TAP_1_EN BIT(0) +#define BMI260_TAP_1_SENSITIVITY_OFFSET 1 +#define BMI260_TAP_1_SENSITIVITY_MASK \ + (0x7 << BMI260_TAP_1_SENSITIVITY_OFFSET) + +/* Features page 2 */ +#define BMI260_ORIENT_1 0x30 +#define BMI260_ORIENT_1_EN BIT(0) +#define BMI260_ORIENT_1_UD_EN BIT(1) +#define BMI260_ORIENT_1_MODE_OFFSET 2 +#define BMI260_ORIENT_1_MODE_MASK (0x3 << BMI260_ORIENT_1_MODE_OFFSET) +#define BMI260_ORIENT_1_BLOCK_OFFSET 4 +#define BMI260_ORIENT_1_BLOCK_MASK (0x3 << BMI260_ORIENT_1_BLOCK_OFFSET) +#define BMI260_ORIENT_1_THETA_OFFSET 6 +#define BMI260_ORIENT_1_THETA_MASK \ + ((BIT(6) - 1) << BMI260_ORIENT_1_THETA_OFFSET) + +#define BMI260_ORIENT_2 0x32 +/* hysteresis(10...0) range is 0~1g, default is 128 (0.0625g) */ +#define BMI260_ORIENT_2_HYSTERESIS_MASK (BIT(11) - 1) + +#define BMI260_ACC_CONF 0x40 +#define BMI260_ACC_BW_OFFSET 4 +#define BMI260_ACC_BW_MASK (0x7 << BMI260_ACC_BW_OFFSET) +#define BMI260_FILTER_PERF BIT(7) +#define BMI260_ULP 0x0 +#define BMI260_HP 0x1 + +#define BMI260_ACC_RANGE 0x41 +#define BMI260_GSEL_2G 0x00 +#define BMI260_GSEL_4G 0x01 +#define BMI260_GSEL_8G 0x02 +#define BMI260_GSEL_16G 0x03 + +/* The max positvie value of accel data is 0x7FFF, equal to range(g) */ +/* So, in order to get +1g, divide the 0x7FFF by range */ +#define BMI260_ACC_DATA_PLUS_1G(range) (0x7FFF / (range)) +#define BMI260_ACC_DATA_MINUS_1G(range) (-BMI260_ACC_DATA_PLUS_1G(range)) + +#define BMI260_GYR_CONF 0x42 +#define BMI260_GYR_BW_OFFSET 4 +#define BMI260_GYR_BW_MASK (0x3 << BMI260_GYR_BW_OFFSET) +#define BMI260_GYR_NOISE_PERF BIT(6) + +#define BMI260_GYR_RANGE 0x43 +#define BMI260_DPS_SEL_2000 0x00 +#define BMI260_DPS_SEL_1000 0x01 +#define BMI260_DPS_SEL_500 0x02 +#define BMI260_DPS_SEL_250 0x03 +#define BMI260_DPS_SEL_125 0x04 + +#define BMI260_AUX_CONF 0x44 + +#define BMI260_FIFO_DOWNS 0x45 + +#define BMI260_FIFO_WTM_0 0x46 +#define BMI260_FIFO_WTM_1 0x47 + +#define BMI260_FIFO_CONFIG_0 0x48 +#define BMI260_FIFO_STOP_ON_FULL BIT(0) +#define BMI260_FIFO_TIME_EN BIT(1) + +#define BMI260_FIFO_CONFIG_1 0x49 +#define BMI260_FIFO_TAG_INT1_EN_OFFSET 0 +#define BMI260_FIFO_TAG_INT1_EN_MASK (0x3 << BMI260_FIFO_TAG_INT1_EN_OFFSET) +#define BMI260_FIFO_TAG_INT2_EN_OFFSET 2 +#define BMI260_FIFO_TAG_INT2_EN_MASK (0x3 << BMI260_FIFO_TAG_INT2_EN_OFFSET) +#define BMI260_FIFO_TAG_INT_EDGE 0x0 +#define BMI260_FIFO_TAG_INT_LEVEL 0x1 +#define BMI260_FIFO_TAG_ACC_SAT 0x2 +#define BMI260_FIFO_TAG_GYR_SAT 0x3 +#define BMI260_FIFO_HEADER_EN BIT(4) +#define BMI260_FIFO_AUX_EN BIT(5) +#define BMI260_FIFO_ACC_EN BIT(6) +#define BMI260_FIFO_GYR_EN BIT(7) +#define BMI260_FIFO_SENSOR_EN(_sensor) \ + ((_sensor) == MOTIONSENSE_TYPE_ACCEL ? BMI260_FIFO_ACC_EN : \ + ((_sensor) == MOTIONSENSE_TYPE_GYRO ? BMI260_FIFO_GYR_EN : \ + BMI260_FIFO_AUX_EN)) + +#define BMI260_AUX_DEV_ID 0x4b +#define BMI260_AUX_I2C_ADDRESS BMI260_AUX_DEV_ID + +#define BMI260_AUX_IF_CONF 0x4c +#define BMI260_AUX_I2C_CONTROL BMI260_AUX_IF_CONF +#define BMI260_AUX_READ_BURST_MASK 3 +#define BMI260_AUX_MAN_READ_BURST_OFF 2 +#define BMI260_AUX_MAN_READ_BURST_MASK (0x3 << BMI280_AUX_MAN_READ_BURST_OFF) +#define BMI260_AUX_READ_BURST_1 0 +#define BMI260_AUX_READ_BURST_2 1 +#define BMI260_AUX_READ_BURST_6 2 +#define BMI260_AUX_READ_BURST_8 3 +#define BMI260_AUX_FCU_WRITE_EN BIT(6) +#define BMI260_AUX_MANUAL_EN BIT(7) + +#define BMI260_AUX_RD_ADDR 0x4d +#define BMI260_AUX_I2C_READ_ADDR BMI260_AUX_RD_ADDR +#define BMI260_AUX_WR_ADDR 0x4e +#define BMI260_AUX_I2C_WRITE_ADDR BMI260_AUX_WR_ADDR +#define BMI260_AUX_WR_DATA 0x4f +#define BMI260_AUX_I2C_WRITE_DATA BMI260_AUX_WR_DATA +#define BMI260_AUX_I2C_READ_DATA BMI260_AUX_X_L_G + +#define BMI260_ERR_REG_MSK 0x52 +#define BMI260_FATAL_ERR BIT(0) +#define BMI260_INTERNAL_ERR_OFF 1 +#define BMI260_INTERNAL_ERR_MASK (0xf << BMI260_INTERNAL_ERR_OFF) +#define BMI260_FIFO_ERR BIT(6) +#define BMI260_AUX_ERR BIT(7) + +#define BMI260_INT1_IO_CTRL 0x53 +#define BMI260_INT1_LVL BIT(1) +#define BMI260_INT1_OD BIT(2) +#define BMI260_INT1_OUTPUT_EN BIT(3) +#define BMI260_INT1_INPUT_EN BIT(4) + +#define BMI260_INT2_IO_CTRL 0x54 +#define BMI260_INT2_LVL BIT(1) +#define BMI260_INT2_OD BIT(2) +#define BMI260_INT2_OUTPUT_EN BIT(3) +#define BMI260_INT2_INPUT_EN BIT(4) + +#define BMI260_INT_LATCH 0x55 +#define BMI260_INT_LATCH_EN BIT(0) + +#define BMI260_INT1_MAP_FEAT 0x56 +#define BMI260_INT2_MAP_FEAT 0x57 +#define BMI260_MAP_SIG_MOTION_OUT BIT(0) +#define BMI260_MAP_STEP_COUNTER_OUT BIT(1) +#define BMI260_MAP_HIGH_LOW_G_OUT BIT(2) +#define BMI260_MAP_TAP_OUT BIT(3) +#define BMI260_MAP_FLAT_OUT BIT(4) +#define BMI260_MAP_NO_MOTION_OUT BIT(5) +#define BMI260_MAP_ANY_MOTION_OUT BIT(6) +#define BMI260_MAP_ORIENTAION_OUT BIT(7) + +#define BMI260_INT_MAP_DATA 0x58 +#define BMI260_MAP_FFULL_INT BIT(0) +#define BMI260_MAP_FWM_INT BIT(1) +#define BMI260_MAP_DRDY_INT BIT(2) +#define BMI260_MAP_ERR_INT BIT(3) +#define BMI260_INT_MAP_DATA_INT1_OFFSET 0 +#define BMI260_INT_MAP_DATA_INT2_OFFSET 4 +#define BMI260_INT_MAP_DATA_REG(_i, _bit) \ + (CONCAT3(BMI260_MAP_, _bit, _INT) << \ + CONCAT3(BMI260_INT_MAP_DATA_INT, _i, _OFFSET)) + +#define BMI260_INIT_CTRL 0x59 +#define BMI260_INIT_ADDR_0 0x5b +#define BMI260_INIT_ADDR_1 0x5c +#define BMI260_INIT_DATA 0x5e +#define BMI260_INTERNAL_ERROR 0x5f +#define BMI260_INT_ERR_1 BIT(1) +#define BMI260_INT_ERR_2 BIT(2) +#define BMI260_FEAT_ENG_DISABLED BIT(4) + +#define BMI260_AUX_IF_TRIM 0x68 +#define BMI260_GYR_CRT_CONF 0x69 + +#define BMI260_NVM_CONF 0x6a +#define BMI260_NVM_PROG_EN BIT(1) + +#define BMI260_IF_CONF 0x6b +#define BMI260_IF_SPI3 BIT(0) +#define BMI260_IF_SPI3_OIS BIT(1) +#define BMI260_IF_OIS_EN BIT(4) +#define BMI260_IF_AUX_EN BIT(5) + +#define BMI260_DRV 0x6c +#define BMI260_ACC_SELF_TEST 0x6d + +#define BMI260_NV_CONF 0x70 +#define BMI260_ACC_OFFSET_EN BIT(3) + +#define BMI260_OFFSET_ACC70 0x71 +#define BMI260_OFFSET_GYR70 0x74 +#define BMI260_OFFSET_EN_GYR98 0x77 +#define BMI260_OFFSET_GYRO_EN BIT(6) +#define BMI260_GYR_GAIN_EN BIT(7) + +#define BMI260_PWR_CONF 0x7c +#define BMI260_ADV_POWER_SAVE BIT(0) +#define BMI260_FIFO_SELF_WAKE_UP BIT(1) +#define BMI260_FUP_EN BIT(2) + +#define BMI260_PWR_CTRL 0x7d +#define BMI260_AUX_EN BIT(0) +#define BMI260_GYR_EN BIT(1) +#define BMI260_ACC_EN BIT(2) +#define BMI260_PWR_EN(_sensor_type) BIT(2 - _sensor_type) +#define BMI260_TEMP_EN BIT(3) + +#define BMI260_CMD_REG 0x7e +#define BMI260_CMD_FIFO_FLUSH 0xb0 +#define BMI260_CMD_SOFT_RESET 0xb6 + +#define BMI260_FF_FRAME_LEN_TS 4 +#define BMI260_FF_DATA_LEN_ACC 6 +#define BMI260_FF_DATA_LEN_GYR 6 +#define BMI260_FF_DATA_LEN_MAG 8 + +/* Root mean square noise of 100Hz accelerometer, units: ug */ +#define BMI260_ACCEL_RMS_NOISE_100HZ 1060 + +#endif /* __CROS_EC_ACCELGYRO_BMI260_H */ diff --git a/include/driver/accelgyro_bmi_common.h b/include/driver/accelgyro_bmi_common.h new file mode 100644 index 0000000000..398f04dc42 --- /dev/null +++ b/include/driver/accelgyro_bmi_common.h @@ -0,0 +1,316 @@ +/* Copyright 2020 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. + */ +/* BMI accelerometer and gyro common definitions for Chrome EC */ + +#ifndef __CROS_EC_ACCELGYRO_BMI_COMMON_H +#define __CROS_EC_ACCELGYRO_BMI_COMMON_H + +#include "accelgyro.h" +#include "accelgyro_bmi160.h" +#include "accelgyro_bmi260.h" +#include "mag_bmm150.h" +#include "accelgyro_bmi_common_public.h" + +#if !defined(CONFIG_ACCELGYRO_BMI_COMM_SPI) && \ + !defined(CONFIG_ACCELGYRO_BMI_COMM_I2C) +#error "BMI must use either SPI or I2C communication" +#endif + +#define BMI_CONF_REG(_sensor) (0x40 + 2 * (_sensor)) +#define BMI_RANGE_REG(_sensor) (0x41 + 2 * (_sensor)) + +#define BMI_ODR_MASK 0x0F +/* odr = 100 / (1 << (8 - reg)) , within limit */ +#define BMI_ODR_0_78HZ 0x01 +#define BMI_ODR_100HZ 0x08 + +#define BMI_REG_TO_ODR(_regval) \ + ((_regval) < BMI_ODR_100HZ ? 100000 / (1 << (8 - (_regval))) : \ + 100000 * (1 << ((_regval) - 8))) +#define BMI_ODR_TO_REG(_odr) \ + ((_odr) < 100000 ? (__builtin_clz(100000 / ((_odr) + 1)) - 24) : \ + (39 - __builtin_clz((_odr) / 100000))) + +enum fifo_header { + BMI_FH_EMPTY = 0x80, + BMI_FH_SKIP = 0x40, + BMI_FH_TIME = 0x44, + BMI_FH_CONFIG = 0x48 +}; + +#define BMI_FH_MODE_MASK 0xc0 +#define BMI_FH_PARM_OFFSET 2 +#define BMI_FH_PARM_MASK (0x7 << BMI_FH_PARM_OFFSET) +#define BMI_FH_EXT_MASK 0x03 + +/* Sensor resolution in number of bits. This sensor has fixed resolution. */ +#define BMI_RESOLUTION 16 +/* Min and Max sampling frequency in mHz */ +#define BMI_ACCEL_MIN_FREQ 12500 +#define BMI_ACCEL_MAX_FREQ MOTION_MAX_SENSOR_FREQUENCY(1600000, 100000) +#define BMI_GYRO_MIN_FREQ 25000 +#define BMI_GYRO_MAX_FREQ MOTION_MAX_SENSOR_FREQUENCY(3200000, 100000) + +enum bmi_running_mode { + STANDARD_UI_9DOF_FIFO = 0, + STANDARD_UI_IMU_FIFO = 1, + STANDARD_UI_IMU = 2, + STANDARD_UI_ADVANCEPOWERSAVE = 3, + ACCEL_PEDOMETER = 4, + APPLICATION_HEAD_TRACKING = 5, + APPLICATION_NAVIGATION = 6, + APPLICATION_REMOTE_CONTROL = 7, + APPLICATION_INDOOR_NAVIGATION = 8, +}; + +#define BMI_FLAG_SEC_I2C_ENABLED BIT(0) +#define BMI_FIFO_FLAG_OFFSET 4 +#define BMI_FIFO_ALL_MASK 7 + +#define BMI_GET_DATA(_s) \ + ((struct bmi_drv_data_t *)(_s)->drv_data) +#define BMI_GET_SAVED_DATA(_s) \ + (&BMI_GET_DATA(_s)->saved_data[(_s)->type]) + +#define BMI_ACC_DATA(v) (BMI160_ACC_X_L_G + \ + (v) * (BMI260_ACC_X_L_G - BMI160_ACC_X_L_G)) +#define BMI_GYR_DATA(v) (BMI160_GYR_X_L_G + \ + (v) * (BMI260_GYR_X_L_G - BMI160_GYR_X_L_G)) +#define BMI_AUX_DATA(v) (BMI160_MAG_X_L_G + \ + (v) * (BMI260_AUX_X_L_G - BMI160_MAG_X_L_G)) + +#define BMI_FIFO_CONFIG_0(v) (BMI160_FIFO_CONFIG_0 + \ + (v) * (BMI260_FIFO_CONFIG_0 - BMI160_FIFO_CONFIG_0)) +#define BMI_FIFO_CONFIG_1(v) (BMI160_FIFO_CONFIG_1 + \ + (v) * (BMI260_FIFO_CONFIG_1 - BMI160_FIFO_CONFIG_1)) +#define BMI_FIFO_SENSOR_EN(v, _sensor) (BMI160_FIFO_SENSOR_EN(_sensor) + \ + (v) * (BMI260_FIFO_SENSOR_EN(_sensor) - BMI160_FIFO_SENSOR_EN(_sensor))) + +#define BMI_TEMPERATURE_0(v) (BMI160_TEMPERATURE_0 + \ + (v) * (BMI260_TEMPERATURE_0 - BMI160_TEMPERATURE_0)) +#define BMI_INVALID_TEMP 0x8000 + +#define BMI_STATUS(v) (BMI160_STATUS + \ + (v) * (BMI260_STATUS - BMI160_STATUS)) +#define BMI_DRDY_OFF(_sensor) (7 - (_sensor)) +#define BMI_DRDY_MASK(_sensor) (1 << BMI160_DRDY_OFF(_sensor)) + +#define BMI_OFFSET_ACC70(v) (BMI160_OFFSET_ACC70 + \ + (v) * (BMI260_OFFSET_ACC70 - BMI160_OFFSET_ACC70)) +#define BMI_OFFSET_GYR70(v) (BMI160_OFFSET_GYR70 + \ + (v) * (BMI260_OFFSET_GYR70 - BMI160_OFFSET_GYR70)) +/* + * There is some bits in this register that differ between BMI160 and BMI260 + * Only use this macro for gyro offset 9:8 (BMI_OFFSET_EN_GYR98 5:0). + */ +#define BMI_OFFSET_EN_GYR98(v) (BMI160_OFFSET_EN_GYR98 + \ + (v) * (BMI260_OFFSET_EN_GYR98 - BMI160_OFFSET_EN_GYR98)) +#define BMI_OFFSET_GYR98_MASK (BIT(6) - 1) +#define BMI_OFFSET_ACC_MULTI_MG (3900 * 1024) +#define BMI_OFFSET_ACC_DIV_MG 1000000 +#define BMI_OFFSET_GYRO_MULTI_MDS (61 * 1024) +#define BMI_OFFSET_GYRO_DIV_MDS 1000 + +#define BMI_FIFO_LENGTH_0(v) (BMI160_FIFO_LENGTH_0 + \ + (v) * (BMI260_FIFO_LENGTH_0 - BMI160_FIFO_LENGTH_0)) +#define BMI_FIFO_LENGTH_MASK(v) (BMI160_FIFO_LENGTH_MASK + \ + (v) * (BMI260_FIFO_LENGTH_MASK - BMI160_FIFO_LENGTH_MASK)) +#define BMI_FIFO_DATA(v) (BMI160_FIFO_DATA + \ + (v) * (BMI260_FIFO_DATA - BMI160_FIFO_DATA)) + +#define BMI_CMD_REG(v) (BMI160_CMD_REG + \ + (v) * (BMI260_CMD_REG - BMI160_CMD_REG)) +#define BMI_CMD_FIFO_FLUSH 0xb0 + +#define BMI_ACCEL_RMS_NOISE_100HZ(v) (BMI160_ACCEL_RMS_NOISE_100HZ + \ + (v) * (BMI260_ACCEL_RMS_NOISE_100HZ - BMI160_ACCEL_RMS_NOISE_100HZ)) +#define BMI_ACCEL_100HZ 100 + +/* + * Struct for pairing an engineering value with the register value for a + * parameter. + */ +struct bmi_accel_param_pair { + int val; /* Value in engineering units. */ + int reg_val; /* Corresponding register value. */ +}; + +int bmi_get_xyz_reg(const struct motion_sensor_t *s); + +/** + * @param type Accel/Gyro + * @param psize Size of the table + * + * @return Range table of the type. + */ +const struct bmi_accel_param_pair *bmi_get_range_table( + const struct motion_sensor_t *s, int *psize); + +/** + * @return reg value that matches the given engineering value passed in. + * The round_up flag is used to specify whether to round up or down. + * Note, this function always returns a valid reg value. If the request is + * outside the range of values, it returns the closest valid reg value. + */ +int bmi_get_reg_val(const int eng_val, const int round_up, + const struct bmi_accel_param_pair *pairs, + const int size); + +/** + * @return engineering value that matches the given reg val + */ +int bmi_get_engineering_val(const int reg_val, + const struct bmi_accel_param_pair *pairs, + const int size); + +/** + * Read 8bit register from accelerometer. + */ +int bmi_read8(const int port, const uint16_t i2c_spi_addr_flags, + const int reg, int *data_ptr); + +/** + * Write 8bit register from accelerometer. + */ +int bmi_write8(const int port, const uint16_t i2c_spi_addr_flags, + const int reg, int data); + +/** + * Read 16bit register from accelerometer. + */ +int bmi_read16(const int port, const uint16_t i2c_spi_addr_flags, + const uint8_t reg, int *data_ptr); + +/** + * Write 16bit register from accelerometer. + */ +int bmi_write16(const int port, const uint16_t i2c_spi_addr_flags, + const int reg, int data); + +/** + * Read 32bit register from accelerometer. + */ +int bmi_read32(const int port, const uint16_t i2c_spi_addr_flags, + const uint8_t reg, int *data_ptr); + +/** + * Read n bytes from accelerometer. + */ +int bmi_read_n(const int port, const uint16_t i2c_spi_addr_flags, + const uint8_t reg, uint8_t *data_ptr, const int len); + +/** + * Write n bytes from accelerometer. + */ +int bmi_write_n(const int port, const uint16_t i2c_spi_addr_flags, + const uint8_t reg, const uint8_t *data_ptr, const int len); + +/* + * Enable/Disable specific bit set of a 8-bit reg. + */ +int bmi_enable_reg8(const struct motion_sensor_t *s, + int reg, uint8_t bits, int enable); + +/* + * Set specific bit set to certain value of a 8-bit reg. + */ +int bmi_set_reg8(const struct motion_sensor_t *s, int reg, + uint8_t bits, int mask); + +/* + * @s: base sensor. + * @v: output vector. + * @input: 6-bits array input. + */ +void bmi_normalize(const struct motion_sensor_t *s, intv3_t v, uint8_t *input); + +/* + * Decode the header from the fifo. + * Return 0 if we need further processing. + * Sensor mutex must be held during processing, to protect the fifos. + * + * @accel: base sensor + * @hdr: the header to decode + * @last_ts: the last timestamp of fifo interrupt. + * @bp: current pointer in the buffer, updated when processing the header. + * @ep: pointer to the end of the valid data in the buffer. + */ +int bmi_decode_header(struct motion_sensor_t *accel, + enum fifo_header hdr, uint32_t last_ts, + uint8_t **bp, uint8_t *ep); +/** + * Retrieve hardware FIFO from sensor, + * - put data in Sensor Hub fifo. + * - update sensor raw_xyz vector with the last information. + * We put raw data in hub fifo and process data from there. + * @s: Pointer to sensor data. + * @last_ts: The last timestamp of fifo interrupt. + * + * Read only up to bmi_buffer. If more reads are needed, we will be called + * again by the interrupt routine. + * + * NOTE: If a new driver supports this function, be sure to add a check + * for spoof_mode in order to load the sensor stack with the spoofed + * data. See accelgyro_bmi260.c::load_fifo for an example. + */ +int bmi_load_fifo(struct motion_sensor_t *s, uint32_t last_ts); + +int bmi_set_range(struct motion_sensor_t *s, int range, int rnd); + +int bmi_get_data_rate(const struct motion_sensor_t *s); + + +int bmi_get_offset(const struct motion_sensor_t *s, + int16_t *offset, int16_t *temp); + +int bmi_get_resolution(const struct motion_sensor_t *s); + +#ifdef CONFIG_BODY_DETECTION +int bmi_get_rms_noise(const struct motion_sensor_t *s); +#endif + +int bmi_set_scale(const struct motion_sensor_t *s, + const uint16_t *scale, int16_t temp); + +int bmi_get_scale(const struct motion_sensor_t *s, + uint16_t *scale, int16_t *temp); + +/* Start/Stop the FIFO collecting events */ +int bmi_enable_fifo(const struct motion_sensor_t *s, int enable); + +/* Read the xyz data of accel/gyro */ +int bmi_read(const struct motion_sensor_t *s, intv3_t v); + +/* Read temperature of sensor s */ +int bmi_read_temp(const struct motion_sensor_t *s, int *temp_ptr); + +/* Read temperature of sensor idx */ +int bmi_get_sensor_temp(int idx, int *temp_ptr); + +/* + * Get the normalized rate according to input rate and input rnd + * @rate: input rate + * @rnd: round up + * @normalized_rate_ptr: normalized rate pointer for output + * @reg_val_ptr: pointer to the actual register value of normalized rate for + * output. + */ +int bmi_get_normalized_rate(const struct motion_sensor_t *s, int rate, int rnd, + int *normalized_rate_ptr, uint8_t *reg_val_ptr); + +/* Get the accelerometer offset */ +void bmi_accel_get_offset(const struct motion_sensor_t *accel, intv3_t v); + +/* Get the gyroscope offset */ +void bmi_gyro_get_offset(const struct motion_sensor_t *gyro, intv3_t v); + +/* Set the accelerometer offset */ +void bmi_set_accel_offset(const struct motion_sensor_t *accel, intv3_t v); + +/* Set the gyroscope offset */ +void bmi_set_gyro_offset(const struct motion_sensor_t *gyro, intv3_t v, + int *val98_ptr); + +#endif /* __CROS_EC_ACCELGYRO_BMI_COMMON_H */ diff --git a/include/driver/mag_bmm150.h b/include/driver/mag_bmm150.h new file mode 100644 index 0000000000..9f517f8097 --- /dev/null +++ b/include/driver/mag_bmm150.h @@ -0,0 +1,150 @@ +/* Copyright 2015 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. + */ + +/* BMM150 magnetometer definition */ + +#ifndef __CROS_EC_MAG_BMM150_H +#define __CROS_EC_MAG_BMM150_H + +#include "accelgyro.h" +#include "mag_cal.h" + +#define BMM150_ADDR0_FLAGS 0x10 +#define BMM150_ADDR1_FLAGS 0x11 +#define BMM150_ADDR2_FLAGS 0x12 +#define BMM150_ADDR3_FLAGS 0x13 + +#define BMM150_CHIP_ID 0x40 +#define BMM150_CHIP_ID_MAJOR 0x32 + +#define BMM150_BASE_DATA 0x42 + +#define BMM150_INT_STATUS 0x4a +#define BMM150_PWR_CTRL 0x4b +#define BMM150_SRST (BIT(7) | BIT(1)) +#define BMM150_PWR_ON BIT(0) + +#define BMM150_OP_CTRL 0x4c +#define BMM150_OP_MODE_OFFSET 1 +#define BMM150_OP_MODE_MASK 3 +#define BMM150_OP_MODE_NORMAL 0x00 +#define BMM150_OP_MODE_FORCED 0x01 +#define BMM150_OP_MODE_SLEEP 0x03 + +#define BMM150_INT_CTRL 0x4d + +#define BMM150_REPXY 0x51 +#define BMM150_LOW_POWER_nXY 3 +#define BMM150_REGULAR_nXY 9 +#define BMM150_ENHANCED_nXY 15 +#define BMM150_HIGH_ACCURACY_nXY 47 +#define BMM150_SPECIAL_nXY 75 +#define BMM150_REPZ 0x52 +#define BMM150_LOW_POWER_nZ 3 +#define BMM150_REGULAR_nZ 15 +#define BMM150_ENHANCED_nZ 27 +#define BMM150_HIGH_ACCURACY_nZ 83 +#define BMM150_SPECIAL_nZ 27 + +#define BMM150_REP(_preset, _axis) CONCAT4(BMM150_, _preset, _n, _axis) + +/* Hidden registers for RHALL calculation */ +#define BMM150_REGA_DIG_X1 0x5d +#define BMM150_REGA_DIG_Y1 0x5e +#define BMM150_REGA_DIG_Z4_LSB 0x62 +#define BMM150_REGA_DIG_Z4_MSB 0x63 +#define BMM150_REGA_DIG_X2 0x64 +#define BMM150_REGA_DIG_Y2 0x65 +#define BMM150_REGA_DIG_Z2_LSB 0x68 +#define BMM150_REGA_DIG_Z2_MSB 0x69 +#define BMM150_REGA_DIG_Z1_LSB 0x6a +#define BMM150_REGA_DIG_Z1_MSB 0x6b +#define BMM150_REGA_DIG_XYZ1_LSB 0x6c +#define BMM150_REGA_DIG_XYZ1_MSB 0x6d +#define BMM150_REGA_DIG_Z3_LSB 0x6e +#define BMM150_REGA_DIG_Z3_MSB 0x6f +#define BMM150_REGA_DIG_XY2 0x70 +#define BMM150_REGA_DIG_XY1 0x71 + +/* Overflow */ + +#define BMM150_FLIP_OVERFLOW_ADCVAL (-4096) +#define BMM150_HALL_OVERFLOW_ADCVAL (-16384) +#define BMM150_OVERFLOW_OUTPUT (0x8000) + +/* Min and Max sampling frequency in mHz */ +#define BMM150_MAG_MIN_FREQ 781 + +/* + * From Section 4.2.4, max frequency depends on the preset. + * + * Fmax ~= 1 / (145us * nXY + 500us * nZ + 980us) + * + * To be safe, declare only 75% of the value. + */ +#define __BMM150_MAG_MAX_FREQ(_preset) (750000000 / \ + (145 * BMM150_REP(_preset, XY) + 500 * BMM150_REP(_preset, Z) + 980)) + +#if (__BMM150_MAG_MAX_FREQ(SPECIAL) > CONFIG_EC_MAX_SENSOR_FREQ_MILLIHZ) +#error "EC too slow for magnetometer" +#endif + +struct bmm150_comp_registers { + /* Local copy of the compensation registers. */ + int8_t dig1[2]; + int8_t dig2[2]; + + uint16_t dig_z1; + int16_t dig_z2; + int16_t dig_z3; + int16_t dig_z4; + + uint8_t dig_xy1; + int8_t dig_xy2; + + uint16_t dig_xyz1; +}; + +struct bmm150_private_data { + /* lsm6dsm_data union requires cal be first element */ + struct mag_cal_t cal; + struct bmm150_comp_registers comp; +}; + +#ifdef CONFIG_MAG_BMI_BMM150 +#include "accelgyro_bmi_common.h" + +#define BMM150_COMP_REG(_s) \ + (&BMI_GET_DATA(_s)->compass.comp) + +#define BMM150_CAL(_s) \ + (&BMI_GET_DATA(_s)->compass.cal) +/* + * Behind a BMI, the BMM150 is in forced mode. Be sure to choose a frequency + * compatible with BMI. + */ +#define BMM150_MAG_MAX_FREQ(_preset) \ + BMI_REG_TO_ODR(BMI_ODR_TO_REG(__BMM150_MAG_MAX_FREQ(_preset))) +#else +#define BMM150_COMP_REG(_s) NULL +#define BMM150_CAL(_s) NULL +#define BMM150_MAG_MAX_FREQ(_preset) __BMM150_MAG_MAX_FREQ(_preset) +#endif + +/* Specific initialization of BMM150 when behing BMI160 */ +int bmm150_init(struct motion_sensor_t *s); + +/* Command to normalize and apply temperature compensation */ +void bmm150_normalize(const struct motion_sensor_t *s, + intv3_t v, + uint8_t *data); + +int bmm150_set_offset(const struct motion_sensor_t *s, + const intv3_t offset); + +int bmm150_get_offset(const struct motion_sensor_t *s, + intv3_t offset); + +#endif /* __CROS_EC_MAG_BMM150_H */ diff --git a/zephyr/dts/bindings/emul/zephyr,bmi.yaml b/zephyr/dts/bindings/emul/zephyr,bmi.yaml new file mode 100644 index 0000000000..0067407e61 --- /dev/null +++ b/zephyr/dts/bindings/emul/zephyr,bmi.yaml @@ -0,0 +1,41 @@ +# 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. + +description: Zephyr BMI Emulator + +compatible: "zephyr,bmi" + +include: base.yaml + +properties: + device-model: + type: string + required: true + enum: + - BMI_EMUL_160 + description: Model of device that is emulated. + + error-on-ro-write: + type: boolean + description: + Flag indicating if error should be generated when read only register + is being written. + + error-on-wo-read: + type: boolean + description: + Flag indicating if error should be generated when write only register + is being read. + + error-on-reserved-bit-write: + type: boolean + description: + Flag indicating if error should be generated when reserved bit + is being written. + + simulate-command-exec-time: + type: boolean + description: + Flag indicating if emulator should wait the same amount of time before + finishing command as real device would. diff --git a/zephyr/emul/CMakeLists.txt b/zephyr/emul/CMakeLists.txt index fc91a74e21..763372cddc 100644 --- a/zephyr/emul/CMakeLists.txt +++ b/zephyr/emul/CMakeLists.txt @@ -6,3 +6,5 @@ zephyr_library_sources_ifdef(CONFIG_EMUL_SMART_BATTERY emul_smart_battery.c) zephyr_library_sources_ifdef(CONFIG_EMUL_BMA255 emul_bma255.c) zephyr_library_sources_ifdef(CONFIG_EMUL_BC12_DETECT_PI3USB9201 emul_pi3usb9201.c) zephyr_library_sources_ifdef(CONFIG_EMUL_PPC_SYV682X emul_syv682x.c) +zephyr_library_sources_ifdef(CONFIG_EMUL_BMI emul_bmi.c) +zephyr_library_sources_ifdef(CONFIG_EMUL_BMI emul_bmi160.c) diff --git a/zephyr/emul/Kconfig b/zephyr/emul/Kconfig index 5a5747d922..76b4cb5566 100644 --- a/zephyr/emul/Kconfig +++ b/zephyr/emul/Kconfig @@ -27,3 +27,9 @@ config EMUL_PPC_SYV682X help Enable the SYV682x emulator. SYV682 is a USB Type-C PPC. This driver uses the emulated I2C bus. +config EMUL_BMI + bool "BMI emulator" + help + Enable the BMI emulator. This driver use emulated I2C bus. + It is used to test bmi 160 and 260 drivers. Emulators API is + available in zephyr/include/emul/emul_bmi.h diff --git a/zephyr/emul/emul_bmi.c b/zephyr/emul/emul_bmi.c new file mode 100644 index 0000000000..0bcd01c4dc --- /dev/null +++ b/zephyr/emul/emul_bmi.c @@ -0,0 +1,1342 @@ +/* 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. + */ + +#define DT_DRV_COMPAT zephyr_bmi + +#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL +#include +LOG_MODULE_REGISTER(emul_bmi); + +#include +#include +#include +#include + +#include "emul/emul_bmi.h" + +#include "driver/accelgyro_bmi160.h" +#include "driver/accelgyro_bmi260.h" +#include "driver/accelgyro_bmi_common.h" + +/** + * Describe if there is no ongoing I2C message or if there is message handled + * at the moment (last message doesn't ended with stop or write is not followed + * by read). + */ +enum bmi_emul_msg_state { + BMI_EMUL_NONE_MSG, + BMI_EMUL_IN_WRITE, + BMI_EMUL_IN_READ +}; + +/** Run-time data used by the emulator */ +struct bmi_emul_data { + /** I2C emulator detail */ + struct i2c_emul emul; + /** BMI device being emulated */ + const struct device *i2c; + /** Configuration information */ + const struct bmi_emul_cfg *cfg; + + /** Current state of all emulated BMI registers */ + uint8_t reg[BMI_EMUL_MAX_REG]; + /** Internal offset values used in calculations */ + int16_t off_acc_x; + int16_t off_acc_y; + int16_t off_acc_z; + int16_t off_gyr_x; + int16_t off_gyr_y; + int16_t off_gyr_z; + /** Internal values of sensors */ + int32_t acc_x; + int32_t acc_y; + int32_t acc_z; + int32_t gyr_x; + int32_t gyr_y; + int32_t gyr_z; + /** Current state of NVM where offset and configuration can be saved */ + uint8_t nvm[BMI_EMUL_MAX_NVM_REGS]; + + /** Return error when trying to write to RO register */ + bool error_on_ro_write; + /** Return error when trying to write 1 to reserved bit */ + bool error_on_rsvd_write; + /** + * If effect of command is vissable after simulated time from issuing + * command + */ + bool simulate_command_exec_time; + /** Return error when trying to read WO register */ + bool error_on_wo_read; + + /** Current state of I2C bus (if emulator is handling message) */ + enum bmi_emul_msg_state msg_state; + /** Number of already handled bytes in ongoing message */ + int msg_byte; + /** Register selected in last write command */ + uint8_t cur_reg; + /** Value of data byte in ongoing write message */ + uint8_t write_byte; + + /** Custom write function called on I2C write opperation */ + bmi_emul_write_func write_func; + /** Data passed to custom write function */ + void *write_func_data; + /** Custom read function called on I2C read opperation */ + bmi_emul_read_func read_func; + /** Data passed to custom read function */ + void *read_func_data; + + /** Control if read should fail on given register */ + int read_fail_reg; + /** Control if write should fail on given register */ + int write_fail_reg; + + /** List of FIFO frames */ + struct bmi_emul_frame *fifo_frame; + /** First FIFO frame in byte format */ + uint8_t fifo[21]; + /** Number of FIFO frames that were skipped */ + uint8_t fifo_skip; + /** Currently accessed byte of first frame */ + int fifo_frame_byte; + /** Length of first frame */ + int fifo_frame_len; + + /** Last time when emulator was resetted in sensor time units */ + int64_t zero_time; + /** Time when current command should end */ + uint32_t cmd_end_time; + + /** Emulated model of BMI */ + int type; + /** Pointer to data specific for emulated model of BMI */ + const struct bmi_emul_type_data *type_data; + + /** Mutex used to control access to emulator data */ + struct k_mutex data_mtx; +}; + +/** Static configuration for the emulator */ +struct bmi_emul_cfg { + /** Label of the I2C bus this emulator connects to */ + const char *i2c_label; + /** Pointer to run-time data */ + struct bmi_emul_data *data; + /** Address of BMI on i2c bus */ + uint16_t addr; +}; + +/** Check description in emul_bmi.h */ +int bmi_emul_lock_data(struct i2c_emul *emul, k_timeout_t timeout) +{ + struct bmi_emul_data *data; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + return k_mutex_lock(&data->data_mtx, timeout); +} + +/** Check description in emul_bmi.h */ +int bmi_emul_unlock_data(struct i2c_emul *emul) +{ + struct bmi_emul_data *data; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + return k_mutex_unlock(&data->data_mtx); +} + +/** Check description in emul_bmi.h */ +void bmi_emul_set_write_func(struct i2c_emul *emul, + bmi_emul_write_func func, void *data) +{ + struct bmi_emul_data *emul_data; + + emul_data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + emul_data->write_func = func; + emul_data->write_func_data = data; +} + +/** Check description in emul_bmi.h */ +void bmi_emul_set_read_func(struct i2c_emul *emul, + bmi_emul_read_func func, void *data) +{ + struct bmi_emul_data *emul_data; + + emul_data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + emul_data->read_func = func; + emul_data->read_func_data = data; +} + +/** Check description in emul_bmi.h */ +void bmi_emul_set_reg(struct i2c_emul *emul, int reg, uint8_t val) +{ + struct bmi_emul_data *data; + + if (reg < 0 || reg > BMI_EMUL_MAX_REG) { + return; + } + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + data->reg[reg] = val; +} + +/** Check description in emul_bmi.h */ +uint8_t bmi_emul_get_reg(struct i2c_emul *emul, int reg) +{ + struct bmi_emul_data *data; + + if (reg < 0 || reg > BMI_EMUL_MAX_REG) { + return 0; + } + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + return data->reg[reg]; +} + +/** Check description in emul_bmi.h */ +void bmi_emul_set_read_fail_reg(struct i2c_emul *emul, int reg) +{ + struct bmi_emul_data *data; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + data->read_fail_reg = reg; +} + +/** Check description in emul_bmi.h */ +void bmi_emul_set_write_fail_reg(struct i2c_emul *emul, int reg) +{ + struct bmi_emul_data *data; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + data->write_fail_reg = reg; +} + +/** + * @brief Convert @p val to two's complement representation. It makes sure that + * bit representation is correct even on platforms which represent + * signed inteager in different format. Unsigned bit representation + * allows to use well defined bitwise operations on returned value. + * + * @param val Inteager that is converted + * + * @return two's complement representation of @p val + */ +static uint32_t bmi_emul_val_to_twos_comp(int32_t val) +{ + uint32_t twos_comp_val; + + /* Make sure that value is converted to twos compliment format */ + if (val < 0) { + twos_comp_val = (uint32_t)(-val); + twos_comp_val = ~twos_comp_val + 1; + } else { + twos_comp_val = (uint32_t)val; + } + + return twos_comp_val; +} + +/** + * @brief Convert accelerometer value from NVM format (8bit, 0x01 == 3.9mg) + * to internal offset format (16bit, 0x01 == 0.061mg). + * + * @param nvm Value in NVM format (8bit, 0x01 == 3.9mg). This is binary + * representation of two's complement signed number. + * + * @return offset Internal representation of @p nvm (16bit, 0x01 == 0.061mg) + */ +static int16_t bmi_emul_acc_nvm_to_off(uint8_t nvm) +{ + int16_t offset; + int8_t sign; + + if (nvm & BIT(7)) { + sign = -1; + /* NVM value is in two's complement format */ + nvm = ~nvm + 1; + } else { + sign = 1; + } + + offset = (int16_t)nvm; + /* LSB in NVM is 3.9mg, while LSB in internal offset is 0.061mg */ + offset *= sign * 64; + + return offset; +} + +/** + * @brief Convert gyroscope value from NVM format (10bit, 0x01 == 0.061 °/s) + * to internal offset format (16bit, 0x01 == 0.0038 °/s) + * + * @param nvm Value in NVM format (10bit, 0x01 == 0.061 °/s). This is binary + * representation of two's complement signed number. + * + * @return offset Internal representation of @p nvm (16bit, 0x01 == 0.0038 °/s) + */ +static int16_t bmi_emul_gyr_nvm_to_off(uint16_t nvm) +{ + int16_t offset; + int8_t sign; + + if (nvm & BIT(9)) { + sign = -1; + /* NVM value is in two's complement format */ + nvm = ~nvm + 1; + } else { + sign = 1; + } + + /* Mask 10 bits which holds value */ + nvm &= 0x3ff; + + offset = (int16_t)nvm; + /* LSB in NVM is 0.061°/s, while LSB in internal offset is 0.0038°/s */ + offset *= sign * 16; + + return offset; +} + +/** + * @brief Convert accelerometer value from internal offset format + * (16bit, 0x01 == 0.061mg) to NVM format (8bit, 0x01 == 7.8mg). + * Function makes sure that NVM value is representation of two's + * complement signed number. + * + * @param val Value in internal offset format (16bit, 0x01 == 0.061mg). + * + * @return nvm NVM format representation of @p val (8bit, 0x01 == 3.9mg) + */ +static uint8_t bmi_emul_acc_off_to_nvm(int16_t off) +{ + uint32_t twos_comp_val; + uint8_t nvm = 0; + + twos_comp_val = bmi_emul_val_to_twos_comp(off); + + /* + * LSB in internal representation has value 0.061mg, while in NVM + * LSB is 3.9mg. Skip 0.06mg, 0.12mg, 0.24mg, 0.48mg, 0.97mg and + * 1.9mg bits. + */ + nvm |= (twos_comp_val >> 6) & 0x7f; + /* Set sign bit */ + nvm |= (twos_comp_val & BIT(31)) ? BIT(7) : 0x00; + + return nvm; +} + +/** + * @brief Convert gyroscope value from internal offset format + * (16bit, 0x01 == 0.0038°/s) to NVM format (10bit, 0x01 == 0.061°/s). + * Function makes sure that NVM value is representation of two's + * complement signed number. + * + * @param val Value in internal offset format (16bit, 0x01 == 0.0038°/s). + * + * @return nvm NVM format representation of @p val (10bit, 0x01 == 0.061°/s) + */ +static uint16_t bmi_emul_gyr_off_to_nvm(int16_t off) +{ + uint32_t twos_comp_val; + uint16_t nvm = 0; + + twos_comp_val = bmi_emul_val_to_twos_comp(off); + + /* + * LSB in internal representation has value 0.0038°/s, while in NVM + * LSB is 0.061°/s. Skip 0.0038°/s, 0.0076°/s, 0.015°/s, and + * 0.03°/s bits. + */ + nvm |= (twos_comp_val >> 4) & 0x1ff; + /* Set sign bit */ + nvm |= (twos_comp_val & BIT(31)) ? BIT(9) : 0x00; + + return nvm; +} + +/** Check description in emul_bmi.h */ +int16_t bmi_emul_get_off(struct i2c_emul *emul, enum bmi_emul_axis axis) +{ + struct bmi_emul_data *data; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + switch (axis) { + case BMI_EMUL_ACC_X: + return data->off_acc_x; + case BMI_EMUL_ACC_Y: + return data->off_acc_y; + case BMI_EMUL_ACC_Z: + return data->off_acc_z; + case BMI_EMUL_GYR_X: + return data->off_gyr_x; + case BMI_EMUL_GYR_Y: + return data->off_gyr_y; + case BMI_EMUL_GYR_Z: + return data->off_gyr_z; + } + + return 0; +} + +/** Check description in emul_bmi.h */ +void bmi_emul_set_off(struct i2c_emul *emul, enum bmi_emul_axis axis, + int16_t val) +{ + struct bmi_emul_data *data; + uint16_t gyr_off; + uint8_t gyr98_shift; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + switch (axis) { + case BMI_EMUL_ACC_X: + data->off_acc_x = val; + data->reg[data->type_data->acc_off_reg] = + bmi_emul_acc_off_to_nvm(data->off_acc_x); + break; + case BMI_EMUL_ACC_Y: + data->off_acc_y = val; + data->reg[data->type_data->acc_off_reg + 1] = + bmi_emul_acc_off_to_nvm(data->off_acc_y); + break; + case BMI_EMUL_ACC_Z: + data->off_acc_z = val; + data->reg[data->type_data->acc_off_reg + 2] = + bmi_emul_acc_off_to_nvm(data->off_acc_z); + break; + case BMI_EMUL_GYR_X: + data->off_gyr_x = val; + gyr_off = bmi_emul_gyr_off_to_nvm(data->off_gyr_x); + data->reg[data->type_data->gyr_off_reg] = gyr_off & 0xff; + gyr98_shift = 0; + data->reg[data->type_data->gyr98_off_reg] &= + ~(0x3 << gyr98_shift); + data->reg[data->type_data->gyr98_off_reg] |= + (gyr_off & 0x300) >> (8 - gyr98_shift); + break; + case BMI_EMUL_GYR_Y: + data->off_gyr_y = val; + gyr_off = bmi_emul_gyr_off_to_nvm(data->off_gyr_y); + data->reg[data->type_data->gyr_off_reg + 1] = gyr_off & 0xff; + gyr98_shift = 2; + data->reg[data->type_data->gyr98_off_reg] &= + ~(0x3 << gyr98_shift); + data->reg[data->type_data->gyr98_off_reg] |= + (gyr_off & 0x300) >> (8 - gyr98_shift); + break; + case BMI_EMUL_GYR_Z: + data->off_gyr_z = val; + gyr_off = bmi_emul_gyr_off_to_nvm(data->off_gyr_z); + data->reg[data->type_data->gyr_off_reg + 2] = gyr_off & 0xff; + gyr98_shift = 4; + data->reg[data->type_data->gyr98_off_reg] &= + ~(0x3 << gyr98_shift); + data->reg[data->type_data->gyr98_off_reg] |= + (gyr_off & 0x300) >> (8 - gyr98_shift); + break; + } +} + +/** Check description in emul_bmi.h */ +int32_t bmi_emul_get_value(struct i2c_emul *emul, enum bmi_emul_axis axis) +{ + struct bmi_emul_data *data; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + switch (axis) { + case BMI_EMUL_ACC_X: + return data->acc_x; + case BMI_EMUL_ACC_Y: + return data->acc_y; + case BMI_EMUL_ACC_Z: + return data->acc_z; + case BMI_EMUL_GYR_X: + return data->gyr_x; + case BMI_EMUL_GYR_Y: + return data->gyr_y; + case BMI_EMUL_GYR_Z: + return data->gyr_z; + } + + return 0; +} + +/** Check description in emul_bmi.h */ +void bmi_emul_set_value(struct i2c_emul *emul, enum bmi_emul_axis axis, + int32_t val) +{ + struct bmi_emul_data *data; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + switch (axis) { + case BMI_EMUL_ACC_X: + data->acc_x = val; + break; + case BMI_EMUL_ACC_Y: + data->acc_y = val; + break; + case BMI_EMUL_ACC_Z: + data->acc_z = val; + break; + case BMI_EMUL_GYR_X: + data->gyr_x = val; + break; + case BMI_EMUL_GYR_Y: + data->gyr_y = val; + break; + case BMI_EMUL_GYR_Z: + data->gyr_z = val; + break; + } +} + +/** Check description in emul_bmi.h */ +void bmi_emul_set_err_on_ro_write(struct i2c_emul *emul, bool set) +{ + struct bmi_emul_data *data; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + data->error_on_ro_write = set; +} + +/** Check description in emul_bmi.h */ +void bmi_emul_set_err_on_rsvd_write(struct i2c_emul *emul, bool set) +{ + struct bmi_emul_data *data; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + data->error_on_rsvd_write = set; +} + +/** Check description in emul_bmi.h */ +void bmi_emul_set_err_on_wo_read(struct i2c_emul *emul, bool set) +{ + struct bmi_emul_data *data; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + data->error_on_wo_read = set; +} + +/** Check description in emul_bmi.h */ +void bmi_emul_simulate_cmd_exec_time(struct i2c_emul *emul, bool set) +{ + struct bmi_emul_data *data; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + data->simulate_command_exec_time = set; +} + +/** Check description in emul_bmi.h */ +void bmi_emul_set_skipped_frames(struct i2c_emul *emul, uint8_t skip) +{ + struct bmi_emul_data *data; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + data->fifo_skip = skip; +} + +/** + * @brief Convert current time to sensor time (39 us units) + * + * @return time in 39 us units + */ +static int64_t bmi_emul_get_sensortime(void) +{ + return k_uptime_ticks() * 1000000 / 39 / CONFIG_SYS_CLOCK_TICKS_PER_SEC; +} + +/** + * @brief Set registers at address @p reg with sensor time that elapsed since + * last reset of emulator + * + * @param emul Pointer to BMI emulator + * @param reg Pointer to 3 byte array, where current sensor time should be + * stored + */ +static void bmi_emul_set_sensortime_reg(struct i2c_emul *emul, uint8_t *reg) +{ + struct bmi_emul_data *data; + uint32_t twos_comp_val; + int64_t time; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + time = bmi_emul_get_sensortime(); + + twos_comp_val = bmi_emul_val_to_twos_comp(time - data->zero_time); + + *reg = twos_comp_val & 0xff; + *(reg + 1) = (twos_comp_val >> 8) & 0xff; + *(reg + 2) = (twos_comp_val >> 16) & 0xff; +} + +/** + * @brief Convert given sensor axis @p val from internal units to register + * units. It shifts value by @p shift bits to the right to account + * range set in emulator's registers. Result is saved at address @p reg + * + * @param emul Pointer to BMI emulator + * @param val Accelerometer or gyroscope value in internal units + * @param reg Pointer to 2 byte array, where sensor value should be stored + * @param shift How many bits should be shift to the right + */ +static void bmi_emul_set_data_reg(struct i2c_emul *emul, int32_t val, + uint8_t *reg, int shift) +{ + struct bmi_emul_data *data; + uint32_t twos_comp_val; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + twos_comp_val = bmi_emul_val_to_twos_comp(val); + + /* Shift unused bits because of selected range */ + twos_comp_val >>= shift; + + *reg = twos_comp_val & 0xff; + *(reg + 1) = (twos_comp_val >> 8) & 0xff; +} + +/** + * @brief Compute length of given FIFO @p frame. If frame is null then length + * of empty frame is returned. + * + * @param emul Pointer to BMI emulator + * @param frame Pointer to FIFO frame + * @param tag_time Indicate if sensor time should be included in empty frame + * @param header Indicate if header should be included in frame + * + * @return length of frame + */ +static uint8_t bmi_emul_get_frame_len(struct i2c_emul *emul, + struct bmi_emul_frame *frame, + bool tag_time, bool header) +{ + struct bmi_emul_data *data; + int len; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + /* Empty FIFO frame */ + if (frame == NULL) { + if (tag_time && header) { + /* Header of sensortime + sensortime + empty FIFO */ + return 5; + } + + /* Empty fifo */ + return 1; + } + + /* Config FIFO frame */ + if (frame->type & BMI_EMUL_FRAME_CONFIG) { + if (header) { + /* Header + byte of data */ + len = 2; + if (data->type_data->sensortime_follow_config_frame) { + /* Sensortime data */ + len += 3; + } + + return len; + } + + /* This frame doesn't exist in headerless mode */ + return 0; + } + + /* Sensor data FIFO frame */ + if (header) { + len = 1; + } else { + len = 0; + } + + if (frame->type & BMI_EMUL_FRAME_ACC) { + len += 6; + } + if (frame->type & BMI_EMUL_FRAME_MAG) { + len += 8; + } + if (frame->type & BMI_EMUL_FRAME_GYR) { + len += 6; + } + + return len; +} + +/** + * @brief Set given FIFO @p frame as current frame in fifo field of emulator + * data structure + * + * @param emul Pointer to BMI emulator + * @param frame Pointer to FIFO frame + * @param tag_time Indicate if sensor time should be included in empty frame + * @param header Indicate if header should be included in frame + * @param acc_shift How many bits should be right shifted from accelerometer + * data + * @param gyr_shift How many bits should be right shifted from gyroscope data + */ +static void bmi_emul_set_current_frame(struct i2c_emul *emul, + struct bmi_emul_frame *frame, + bool tag_time, bool header, + int acc_shift, int gyr_shift) +{ + struct bmi_emul_data *data; + int i = 0; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + data->fifo_frame_byte = 0; + data->fifo_frame_len = bmi_emul_get_frame_len(emul, frame, tag_time, + header); + /* Empty FIFO frame */ + if (frame == NULL) { + if (tag_time && header) { + /* Header */ + data->fifo[0] = BMI_EMUL_FIFO_HEAD_TIME; + bmi_emul_set_sensortime_reg(emul, &(data->fifo[1])); + i = 4; + } + + /* Empty header */ + data->fifo[i] = BMI_EMUL_FIFO_HEAD_EMPTY; + + return; + } + + /* Config FIFO frame */ + if (frame->type & BMI_EMUL_FRAME_CONFIG) { + /* Header */ + data->fifo[0] = BMI_EMUL_FIFO_HEAD_CONFIG; + data->fifo[1] = frame->config; + if (data->type_data->sensortime_follow_config_frame) { + bmi_emul_set_sensortime_reg(emul, &(data->fifo[2])); + } + + return; + } + + /* Sensor data FIFO frame */ + if (header) { + data->fifo[0] = BMI_EMUL_FIFO_HEAD_DATA; + data->fifo[0] |= frame->type & BMI_EMUL_FRAME_MAG ? + BMI_EMUL_FIFO_HEAD_DATA_MAG : 0; + data->fifo[0] |= frame->type & BMI_EMUL_FRAME_GYR ? + BMI_EMUL_FIFO_HEAD_DATA_GYR : 0; + data->fifo[0] |= frame->type & BMI_EMUL_FRAME_ACC ? + BMI_EMUL_FIFO_HEAD_DATA_ACC : 0; + data->fifo[0] |= frame->tag & BMI_EMUL_FIFO_HEAD_DATA_TAG_MASK; + i = 1; + } + + if (frame->type & BMI_EMUL_FRAME_MAG) { + bmi_emul_set_data_reg(emul, frame->mag_x, &(data->fifo[i]), 0); + i += 2; + bmi_emul_set_data_reg(emul, frame->mag_y, &(data->fifo[i]), 0); + i += 2; + bmi_emul_set_data_reg(emul, frame->mag_z, &(data->fifo[i]), 0); + i += 2; + bmi_emul_set_data_reg(emul, frame->rhall, &(data->fifo[i]), 0); + i += 2; + } + + if (frame->type & BMI_EMUL_FRAME_GYR) { + bmi_emul_set_data_reg(emul, frame->gyr_x, &(data->fifo[i]), + gyr_shift); + i += 2; + bmi_emul_set_data_reg(emul, frame->gyr_y, &(data->fifo[i]), + gyr_shift); + i += 2; + bmi_emul_set_data_reg(emul, frame->gyr_z, &(data->fifo[i]), + gyr_shift); + i += 2; + } + + if (frame->type & BMI_EMUL_FRAME_ACC) { + bmi_emul_set_data_reg(emul, frame->acc_x, &(data->fifo[i]), + acc_shift); + i += 2; + bmi_emul_set_data_reg(emul, frame->acc_y, &(data->fifo[i]), + acc_shift); + i += 2; + bmi_emul_set_data_reg(emul, frame->acc_z, &(data->fifo[i]), + acc_shift); + i += 2; + } +} + +/** + * @brief Update internal sensors offset values using values from emulated + * registers. + * + * @param emul Pointer to BMI emulator + */ +static void bmi_emul_updata_int_off(struct i2c_emul *emul) +{ + struct bmi_emul_data *data; + uint16_t gyr_nvm; + uint8_t gyr98; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + data->off_acc_x = bmi_emul_acc_nvm_to_off( + data->reg[data->type_data->acc_off_reg]); + data->off_acc_y = bmi_emul_acc_nvm_to_off( + data->reg[data->type_data->acc_off_reg + 1]); + data->off_acc_z = bmi_emul_acc_nvm_to_off( + data->reg[data->type_data->acc_off_reg + 2]); + + gyr98 = data->reg[data->type_data->gyr98_off_reg]; + + gyr_nvm = data->reg[data->type_data->gyr_off_reg]; + gyr_nvm |= (gyr98 & 0x3) << 8; + data->off_gyr_x = bmi_emul_gyr_nvm_to_off(gyr_nvm); + gyr_nvm = data->reg[data->type_data->gyr_off_reg + 1]; + gyr_nvm |= (gyr98 & 0xc) << 6; + data->off_gyr_y = bmi_emul_gyr_nvm_to_off(gyr_nvm); + gyr_nvm = data->reg[data->type_data->gyr_off_reg + 2]; + gyr_nvm |= (gyr98 & 0x30) << 4; + data->off_gyr_z = bmi_emul_gyr_nvm_to_off(gyr_nvm); +} + +/** + * @brief Restore registers backed in NVM to emulator's registers. Each model + * of BMI may have different set of NVM backed registers. + * + * @param emul Pointer to BMI emulator + */ +static void bmi_emul_restore_nvm(struct i2c_emul *emul) +{ + struct bmi_emul_data *data; + int i; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + ASSERT(data->type_data->nvm_len <= BMI_EMUL_MAX_NVM_REGS); + + /* Restore registers values */ + for (i = 0; i < data->type_data->nvm_len; i++) { + data->reg[data->type_data->nvm_reg[i]] = data->nvm[i]; + } + + bmi_emul_updata_int_off(emul); +} + +/** Check description in emul_bmi.h */ +void bmi_emul_flush_fifo(struct i2c_emul *emul, bool tag_time, bool header) +{ + struct bmi_emul_data *data; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + data->fifo_skip = 0; + data->fifo_frame = NULL; + /* + * Gyroscope and accelerometer shift (last two arguments) + * are not important for NULL (empty) FIFO frame. + */ + bmi_emul_set_current_frame(emul, NULL, tag_time, header, 0, 0); +} + +/** Check description in emul_bmi.h */ +void bmi_emul_reset_common(struct i2c_emul *emul, bool tag_time, bool header) +{ + struct bmi_emul_data *data; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + /* Restore registers backed in NVM */ + bmi_emul_restore_nvm(emul); + + /* Flush FIFO */ + bmi_emul_flush_fifo(emul, tag_time, header); + + /* Reset sensor timer */ + data->zero_time = bmi_emul_get_sensortime(); +} + +/** Check description in emul_bmi.h */ +void bmi_emul_set_cmd_end_time(struct i2c_emul *emul, int time) +{ + struct bmi_emul_data *data; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + data->cmd_end_time = k_uptime_get_32() + time; +} + +/** Check description in emul_bmi.h */ +bool bmi_emul_is_cmd_end(struct i2c_emul *emul) +{ + struct bmi_emul_data *data; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + /* We are simulating command execution time and it doesn't expired */ + if (data->simulate_command_exec_time && + data->cmd_end_time > k_uptime_get_32()) { + return false; + } + + return true; +} + +/** + * @brief Handle I2C write message. Before any handling, custom function + * is called if provided. Next BMI model specific write function is + * called. It is checked if accessed register isn't RO and reserved bits + * are set to 0. Write set value of reg field of bmi emulator data + * ignoring reserved bits. If required internal sensor offset values are + * updated. Emulator may be configured to fail on write to specific + * register. + * + * @param emul Pointer to BMI emulator + * @param reg Register which is written + * @param byte Number of handled bytes in this write command + * @param val Value being written to @p reg + * + * @return 0 on success + * @return -EIO on error + */ +static int bmi_emul_handle_write(struct i2c_emul *emul, int reg, int byte, + uint8_t val) +{ + struct bmi_emul_data *data; + uint8_t rsvd_mask; + int ret; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + if (data->write_func) { + ret = data->write_func(emul, reg, byte, val, + data->write_func_data); + if (ret < 0) { + return -EIO; + } else if (ret == 0) { + return 0; + } + } + + ret = data->type_data->handle_write(data->reg, emul, ®, byte, val); + if (ret != 0) { + if (ret == BMI_EMUL_ACCESS_E) { + if (!data->error_on_ro_write) { + return 0; + } + LOG_ERR("Writing to reg 0x%x which is RO", reg); + } + + return -EIO; + } + + if (data->write_fail_reg == reg || + data->write_fail_reg == BMI_EMUL_FAIL_ALL_REG) { + return -EIO; + } + + rsvd_mask = data->type_data->rsvd_mask[reg]; + + if (data->error_on_rsvd_write && rsvd_mask & val) { + LOG_ERR("Writing 0x%x to reg 0x%x with rsvd bits mask 0x%x", + val, reg, rsvd_mask); + return -EIO; + } + + /* Ignore all reserved bits */ + val &= ~rsvd_mask; + val |= data->reg[reg] & rsvd_mask; + + data->reg[reg] = val; + + if ((reg >= data->type_data->acc_off_reg && + reg <= data->type_data->acc_off_reg + 2) || + (reg >= data->type_data->gyr_off_reg && + reg <= data->type_data->gyr_off_reg + 2) || + reg == data->type_data->gyr98_off_reg) { + /* + * Internal offset value should be updated to new value of + * offset registers + */ + bmi_emul_updata_int_off(emul); + } + + return 0; +} + +/** Check description in emul_bmi.h */ +void bmi_emul_state_to_reg(struct i2c_emul *emul, int acc_shift, + int gyr_shift, int acc_reg, int gyr_reg, + int sensortime_reg, bool acc_off_en, + bool gyr_off_en) +{ + struct bmi_emul_data *data; + int32_t val[3]; + int i; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + if (gyr_off_en) { + val[0] = data->gyr_x - data->off_gyr_x; + val[1] = data->gyr_y - data->off_gyr_y; + val[2] = data->gyr_z - data->off_gyr_z; + } else { + val[0] = data->gyr_x; + val[1] = data->gyr_y; + val[2] = data->gyr_z; + } + + for (i = 0; i < 3; i++) { + bmi_emul_set_data_reg(emul, val[i], + &(data->reg[gyr_reg + i * 2]), gyr_shift); + } + + if (acc_off_en) { + val[0] = data->acc_x - data->off_acc_x; + val[1] = data->acc_y - data->off_acc_y; + val[2] = data->acc_z - data->off_acc_z; + } else { + val[0] = data->acc_x; + val[1] = data->acc_y; + val[2] = data->acc_z; + } + + for (i = 0; i < 3; i++) { + bmi_emul_set_data_reg(emul, val[i], + &(data->reg[acc_reg + i * 2]), acc_shift); + } + + bmi_emul_set_sensortime_reg(emul, &(data->reg[sensortime_reg])); +} + +/** Check description in emul_bmi.h */ +void bmi_emul_append_frame(struct i2c_emul *emul, struct bmi_emul_frame *frame) +{ + struct bmi_emul_data *data; + struct bmi_emul_frame *tmp_frame; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + if (data->fifo_frame == NULL) { + data->fifo_frame = frame; + } else { + tmp_frame = data->fifo_frame; + while (tmp_frame->next != NULL) { + tmp_frame = tmp_frame->next; + } + tmp_frame->next = frame; + } +} + +/** Check description in emul_bmi.h */ +uint16_t bmi_emul_fifo_len(struct i2c_emul *emul, bool tag_time, bool header) +{ + struct bmi_emul_frame *frame; + struct bmi_emul_data *data; + uint16_t len = 0; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + if (data->fifo_skip != 0 && header) { + len += 2; + } + + frame = data->fifo_frame; + while (frame != NULL) { + len += bmi_emul_get_frame_len(emul, frame, tag_time, header); + frame = frame->next; + } + + len += bmi_emul_get_frame_len(emul, NULL, tag_time, header); + /* Do not count last empty frame byte */ + len--; + + return len; +} + +/** Check description in emul_bmi.h */ +uint8_t bmi_emul_get_fifo_data(struct i2c_emul *emul, int byte, + bool tag_time, bool header, int acc_shift, + int gyr_shift) +{ + struct bmi_emul_data *data; + int ret; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + if (byte == 0) { + /* Repeat uncompleated read of frame */ + bmi_emul_set_current_frame(emul, data->fifo_frame, tag_time, + header, acc_shift, gyr_shift); + + /* Return header for skip frame */ + if (data->fifo_skip != 0 && header) { + return BMI_EMUL_FIFO_HEAD_SKIP; + } + } + + if (data->fifo_skip != 0 && byte == 1 && header) { + /* Return number of skipped frames */ + ret = data->fifo_skip; + data->fifo_skip = 0; + + return ret; + } + + /* Get next valid frame */ + while (data->fifo_frame_byte >= data->fifo_frame_len) { + /* No data */ + if (data->fifo_frame == NULL) { + return 0; + } + data->fifo_frame = data->fifo_frame->next; + bmi_emul_set_current_frame(emul, data->fifo_frame, tag_time, + header, acc_shift, gyr_shift); + } + + return data->fifo[data->fifo_frame_byte++]; +} + +/** + * @brief Handle I2C read message. Before any handling, custom function + * is called if provided. Next BMI model specific read function is + * called. It is checked if accessed register isn't WO. Emulator may + * be configured to fail on given register read. + * + * @param emul Pointer to BMI emulator + * @param reg Register address to read + * @param byte Byte which is accessed during block read + * @param buf Pointer where result should be stored + * + * @return 0 on success + * @return -EIO on error + */ +static int bmi_emul_handle_read(struct i2c_emul *emul, int reg, int byte, + char *buf) +{ + struct bmi_emul_data *data; + uint16_t fifo_len; + int ret; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + + if (data->read_func) { + ret = data->read_func(emul, reg, byte, data->read_func_data); + if (ret < 0) { + return -EIO; + } else if (ret == 0) { + /* Immediately return value set by custom function */ + *buf = data->reg[reg]; + + return 0; + } + } + + ret = data->type_data->handle_read(data->reg, emul, ®, byte, buf); + if (ret == BMI_EMUL_ACCESS_E && data->error_on_wo_read) { + LOG_ERR("Reading reg 0x%x which is WO", reg); + } else if (ret != 0) { + return ret; + } + + if (data->read_fail_reg == reg || + data->read_fail_reg == BMI_EMUL_FAIL_ALL_REG) { + return -EIO; + } + + return 0; +} + +/** + * @biref Emulate an I2C transfer to a BMI accelerometer + * + * This handles simple reads and writes + * + * @param emul I2C emulation information + * @param msgs List of messages to process + * @param num_msgs Number of messages to process + * @param addr Address of the I2C target device + * + * @retval 0 If successful + * @retval -EIO General input / output error + */ +static int bmi_emul_transfer(struct i2c_emul *emul, struct i2c_msg *msgs, + int num_msgs, int addr) +{ + const struct bmi_emul_cfg *cfg; + struct bmi_emul_data *data; + unsigned int len; + int ret, i; + bool read; + + data = CONTAINER_OF(emul, struct bmi_emul_data, emul); + cfg = data->cfg; + + if (cfg->addr != addr) { + LOG_ERR("Address mismatch, expected %02x, got %02x", cfg->addr, + addr); + return -EIO; + } + + i2c_dump_msgs("emul", msgs, num_msgs, addr); + + for (; num_msgs > 0; num_msgs--, msgs++) { + read = msgs->flags & I2C_MSG_READ; + + switch (data->msg_state) { + case BMI_EMUL_NONE_MSG: + data->msg_byte = 0; + break; + case BMI_EMUL_IN_WRITE: + if (read) { + data->msg_byte = 0; + } + break; + case BMI_EMUL_IN_READ: + if (!read) { + data->msg_byte = 0; + } + break; + } + data->msg_state = read ? BMI_EMUL_IN_READ : BMI_EMUL_IN_WRITE; + + if (msgs->flags & I2C_MSG_STOP) { + data->msg_state = BMI_EMUL_NONE_MSG; + } + + if (!read) { + /* Dispatch wrtie command */ + i = 0; + /* + * Save first byte of write command as currently + * accessed register. + */ + if (data->msg_byte == 0 && msgs->len) { + data->cur_reg = msgs->buf[0]; + i++; + data->msg_byte++; + } + + /* Handle rest of write command bytes */ + for (; i < msgs->len; i++) { + k_mutex_lock(&data->data_mtx, K_FOREVER); + ret = bmi_emul_handle_write(emul, data->cur_reg, + data->msg_byte, + msgs->buf[i]); + k_mutex_unlock(&data->data_mtx); + if (ret) { + return -EIO; + } + data->msg_byte++; + } + } else { + /* Dispatch read command */ + for (i = 0; i < msgs->len; i++) { + k_mutex_lock(&data->data_mtx, K_FOREVER); + ret = bmi_emul_handle_read(emul, data->cur_reg, + data->msg_byte, + &(msgs->buf[i])); + k_mutex_unlock(&data->data_mtx); + if (ret) { + return -EIO; + } + data->msg_byte++; + } + } + } + + return 0; +} + +/* Device instantiation */ + +static struct i2c_emul_api bmi_emul_api = { + .transfer = bmi_emul_transfer, +}; + +/** + * @brief Set up a new BMI emulator + * + * This should be called for each BMI device that needs to be + * emulated. It registers it with the I2C emulation controller. + * + * @param emul Emulation information + * @param parent Device to emulate + * + * @return 0 indicating success (always) + */ +static int bmi_emul_init(const struct emul *emul, + const struct device *parent) +{ + const struct bmi_emul_cfg *cfg = emul->cfg; + struct bmi_emul_data *data = cfg->data; + int ret; + + data->emul.api = &bmi_emul_api; + data->emul.addr = cfg->addr; + data->i2c = parent; + data->cfg = cfg; + k_mutex_init(&data->data_mtx); + + switch (data->type) { + case BMI_EMUL_160: + data->type_data = get_bmi160_emul_type_data(); + break; + } + + ret = i2c_emul_register(parent, emul->dev_label, &data->emul); + + data->type_data->reset(data->reg, &data->emul); + + return ret; +} + +#define BMI_EMUL(n) \ + static struct bmi_emul_data bmi_emul_data_##n = { \ + .error_on_ro_write = DT_INST_PROP(n, error_on_ro_write),\ + .error_on_wo_read = DT_INST_PROP(n, error_on_wo_read), \ + .error_on_rsvd_write = DT_INST_PROP(n, \ + error_on_reserved_bit_write), \ + .simulate_command_exec_time = DT_INST_PROP(n, \ + simulate_command_exec_time), \ + .type = DT_ENUM_TOKEN(DT_DRV_INST(n), device_model), \ + .msg_state = BMI_EMUL_NONE_MSG, \ + .cur_reg = 0, \ + .write_func = NULL, \ + .read_func = NULL, \ + .write_fail_reg = BMI_EMUL_NO_FAIL_REG, \ + .read_fail_reg = BMI_EMUL_NO_FAIL_REG, \ + }; \ + \ + static const struct bmi_emul_cfg bmi_emul_cfg_##n = { \ + .i2c_label = DT_INST_BUS_LABEL(n), \ + .data = &bmi_emul_data_##n, \ + .addr = DT_INST_REG_ADDR(n), \ + }; \ + EMUL_DEFINE(bmi_emul_init, DT_DRV_INST(n), &bmi_emul_cfg_##n) + +DT_INST_FOREACH_STATUS_OKAY(BMI_EMUL) + +#define BMI_EMUL_CASE(n) \ + case DT_INST_DEP_ORD(n): return &bmi_emul_data_##n.emul; + +/** Check description in emul_bmi.h */ +struct i2c_emul *bmi_emul_get(int ord) +{ + switch (ord) { + DT_INST_FOREACH_STATUS_OKAY(BMI_EMUL_CASE) + + default: + return NULL; + } +} diff --git a/zephyr/emul/emul_bmi160.c b/zephyr/emul/emul_bmi160.c new file mode 100644 index 0000000000..8f6572604f --- /dev/null +++ b/zephyr/emul/emul_bmi160.c @@ -0,0 +1,747 @@ +/* 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. + */ + +#define DT_DRV_COMPAT zephyr_bmi + +#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL +#include +LOG_MODULE_REGISTER(emul_bmi160); + +#include +#include +#include +#include + +#include "emul/emul_bmi.h" + +#include "driver/accelgyro_bmi160.h" +#include "driver/accelgyro_bmi_common.h" + +/** Mask reserved bits in each register of BMI160 */ +static const uint8_t bmi_emul_160_rsvd_mask[] = { + [BMI160_CHIP_ID] = 0x00, + [0x01] = 0xff, /* Reserved */ + [BMI160_ERR_REG] = 0x00, + [BMI160_PMU_STATUS] = 0xc0, + [BMI160_MAG_X_L_G] = 0x00, + [BMI160_MAG_X_H_G] = 0x00, + [BMI160_MAG_Y_L_G] = 0x00, + [BMI160_MAG_Y_H_G] = 0x00, + [BMI160_MAG_Z_L_G] = 0x00, + [BMI160_MAG_Z_H_G] = 0x00, + [BMI160_RHALL_L_G] = 0x00, + [BMI160_RHALL_H_G] = 0x00, + [BMI160_GYR_X_L_G] = 0x00, + [BMI160_GYR_X_H_G] = 0x00, + [BMI160_GYR_Y_L_G] = 0x00, + [BMI160_GYR_Y_H_G] = 0x00, + [BMI160_GYR_Z_L_G] = 0x00, + [BMI160_GYR_Z_H_G] = 0x00, + [BMI160_ACC_X_L_G] = 0x00, + [BMI160_ACC_X_H_G] = 0x00, + [BMI160_ACC_Y_L_G] = 0x00, + [BMI160_ACC_Y_H_G] = 0x00, + [BMI160_ACC_Z_L_G] = 0x00, + [BMI160_ACC_Z_H_G] = 0x00, + [BMI160_SENSORTIME_0] = 0x00, + [BMI160_SENSORTIME_1] = 0x00, + [BMI160_SENSORTIME_2] = 0x00, + [BMI160_STATUS] = 0x01, + [BMI160_INT_STATUS_0] = 0x00, + [BMI160_INT_STATUS_1] = 0x03, + [BMI160_INT_STATUS_2] = 0x00, + [BMI160_INT_STATUS_3] = 0x00, + [BMI160_TEMPERATURE_0] = 0x00, + [BMI160_TEMPERATURE_1] = 0x00, + [BMI160_FIFO_LENGTH_0] = 0x00, + [BMI160_FIFO_LENGTH_1] = 0xf8, + [BMI160_FIFO_DATA] = 0x00, + [0x25 ... 0x3f] = 0xff, /* Reserved */ + [BMI160_ACC_CONF] = 0x00, + [BMI160_ACC_RANGE] = 0xf0, + [BMI160_GYR_CONF] = 0xc0, + [BMI160_GYR_RANGE] = 0xf8, + [BMI160_MAG_CONF] = 0xf0, + [BMI160_FIFO_DOWNS] = 0x00, + [BMI160_FIFO_CONFIG_0] = 0x00, + [BMI160_FIFO_CONFIG_1] = 0x01, + [0x48 ... 0x4a] = 0xff, /* Reserved */ + [BMI160_MAG_IF_0] = 0x01, + [BMI160_MAG_IF_1] = 0x40, + [BMI160_MAG_IF_2] = 0x00, + [BMI160_MAG_IF_3] = 0x00, + [BMI160_MAG_IF_4] = 0x00, + [BMI160_INT_EN_0] = 0x08, + [BMI160_INT_EN_1] = 0x80, + [BMI160_INT_EN_2] = 0xf0, + [BMI160_INT_OUT_CTRL] = 0x00, + [BMI160_INT_LATCH] = 0xc0, + [BMI160_INT_MAP_0] = 0x00, + [BMI160_INT_MAP_1] = 0x00, + [BMI160_INT_MAP_2] = 0x00, + [BMI160_INT_DATA_0] = 0x77, + [BMI160_INT_DATA_1] = 0x7f, + [BMI160_INT_LOW_HIGH_0] = 0x00, + [BMI160_INT_LOW_HIGH_1] = 0x00, + [BMI160_INT_LOW_HIGH_2] = 0x3c, + [BMI160_INT_LOW_HIGH_3] = 0x00, + [BMI160_INT_LOW_HIGH_4] = 0x00, + [BMI160_INT_MOTION_0] = 0x00, + [BMI160_INT_MOTION_1] = 0x00, + [BMI160_INT_MOTION_2] = 0x00, + [BMI160_INT_MOTION_3] = 0xc0, + [BMI160_INT_TAP_0] = 0x38, + [BMI160_INT_TAP_1] = 0xe0, + [BMI160_INT_ORIENT_0] = 0x00, + [BMI160_INT_ORIENT_1] = 0x00, + [BMI160_INT_FLAT_0] = 0xc0, + [BMI160_INT_FLAT_1] = 0xc8, + [BMI160_FOC_CONF] = 0x80, + [BMI160_CONF] = 0xfd, + [BMI160_IF_CONF] = 0xce, + [BMI160_PMU_TRIGGER] = 0x80, + [BMI160_SELF_TEST] = 0xe0, + [0x6e] = 0xff, /* Reserved */ + [0x6f] = 0xff, /* Reserved */ + [BMI160_NV_CONF] = 0xf0, + [BMI160_OFFSET_ACC70] = 0x00, + [BMI160_OFFSET_ACC70 + 1] = 0x00, + [BMI160_OFFSET_ACC70 + 2] = 0x00, + [BMI160_OFFSET_GYR70] = 0x00, + [BMI160_OFFSET_GYR70 + 1] = 0x00, + [BMI160_OFFSET_GYR70 + 2] = 0x00, + [BMI160_OFFSET_EN_GYR98] = 0x00, + [BMI160_STEP_CNT_0] = 0x00, + [BMI160_STEP_CNT_1] = 0x00, + [BMI160_STEP_CONF_0] = 0x00, + [BMI160_STEP_CONF_1] = 0xf0, + [0x7c] = 0xff, /* Reserved */ + [0x7d] = 0xff, /* Reserved */ + [BMI160_CMD_REG] = 0x00, +}; + +/** + * @brief Convert range in format of ACC_RANGE register to number of bits + * that should be shifted right to obtain 16 bit reported accelerometer + * value from internal 32 bit value + * + * @param range Value of ACC_RANGE register + * + * @return shift Number of LSB that should be ignored from internal + * accelerometer value + */ +static int bmi160_emul_acc_range_to_shift(uint8_t range) +{ + switch (range & 0xf) { + case BMI160_GSEL_2G: + return 0; + case BMI160_GSEL_4G: + return 1; + case BMI160_GSEL_8G: + return 2; + case BMI160_GSEL_16G: + return 3; + default: + return 0; + } +} + +/** + * @brief Convert range in format of GYR_RANGE register to number of bits + * that should be shifted right to obtain 16 bit reported gyroscope + * value from internal 32 bit value + * + * @param range Value of GYR_RANGE register + * + * @return shift Number of LSB that should be ignored from internal + * gyroscope value + */ +static int bmi160_emul_gyr_range_to_shift(uint8_t range) +{ + switch (range & 0x7) { + case BMI160_DPS_SEL_2000: + return 4; + case BMI160_DPS_SEL_1000: + return 3; + case BMI160_DPS_SEL_500: + return 2; + case BMI160_DPS_SEL_250: + return 1; + case BMI160_DPS_SEL_125: + return 0; + default: + return 0; + } +} + +/** + * @brief Reset registers to default values and restore registers backed by NVM + * + * @param regs Pointer to array of emulator's registers + * @param emul Pointer to BMI emulator + */ +static void bmi160_emul_reset(uint8_t *regs, struct i2c_emul *emul) +{ + bool tag_time; + bool header; + + regs[BMI160_CHIP_ID] = 0xd1; + regs[BMI160_ERR_REG] = 0x00; + regs[BMI160_PMU_STATUS] = 0x00; + regs[BMI160_MAG_X_L_G] = 0x00; + regs[BMI160_MAG_X_H_G] = 0x00; + regs[BMI160_MAG_Y_L_G] = 0x00; + regs[BMI160_MAG_Y_H_G] = 0x00; + regs[BMI160_MAG_Z_L_G] = 0x00; + regs[BMI160_MAG_Z_H_G] = 0x00; + regs[BMI160_RHALL_L_G] = 0x00; + regs[BMI160_RHALL_H_G] = 0x00; + regs[BMI160_GYR_X_L_G] = 0x00; + regs[BMI160_GYR_X_H_G] = 0x00; + regs[BMI160_GYR_Y_L_G] = 0x00; + regs[BMI160_GYR_Y_H_G] = 0x00; + regs[BMI160_GYR_Z_L_G] = 0x00; + regs[BMI160_GYR_Z_H_G] = 0x00; + regs[BMI160_ACC_X_L_G] = 0x00; + regs[BMI160_ACC_X_H_G] = 0x00; + regs[BMI160_ACC_Y_L_G] = 0x00; + regs[BMI160_ACC_Y_H_G] = 0x00; + regs[BMI160_ACC_Z_L_G] = 0x00; + regs[BMI160_ACC_Z_H_G] = 0x00; + regs[BMI160_SENSORTIME_0] = 0x00; + regs[BMI160_SENSORTIME_1] = 0x00; + regs[BMI160_SENSORTIME_2] = 0x00; + regs[BMI160_STATUS] = 0x01; + regs[BMI160_INT_STATUS_0] = 0x00; + regs[BMI160_INT_STATUS_1] = 0x00; + regs[BMI160_INT_STATUS_2] = 0x00; + regs[BMI160_INT_STATUS_3] = 0x00; + regs[BMI160_TEMPERATURE_0] = 0x00; + regs[BMI160_TEMPERATURE_1] = 0x00; + regs[BMI160_FIFO_LENGTH_0] = 0x00; + regs[BMI160_FIFO_LENGTH_1] = 0x00; + regs[BMI160_FIFO_DATA] = 0x00; + regs[BMI160_ACC_CONF] = 0x28; + regs[BMI160_ACC_RANGE] = 0x03; + regs[BMI160_GYR_CONF] = 0x28; + regs[BMI160_GYR_RANGE] = 0x00; + regs[BMI160_MAG_CONF] = 0x0b; + regs[BMI160_FIFO_DOWNS] = 0x88; + regs[BMI160_FIFO_CONFIG_0] = 0x80; + regs[BMI160_FIFO_CONFIG_1] = 0x10; + regs[BMI160_MAG_IF_0] = 0x20; + regs[BMI160_MAG_IF_1] = 0x80; + regs[BMI160_MAG_IF_2] = 0x42; + regs[BMI160_MAG_IF_3] = 0x4c; + regs[BMI160_MAG_IF_4] = 0x00; + regs[BMI160_INT_EN_0] = 0x00; + regs[BMI160_INT_EN_1] = 0x00; + regs[BMI160_INT_EN_2] = 0x00; + regs[BMI160_INT_OUT_CTRL] = 0x00; + regs[BMI160_INT_LATCH] = 0x00; + regs[BMI160_INT_MAP_0] = 0x00; + regs[BMI160_INT_MAP_1] = 0x00; + regs[BMI160_INT_MAP_2] = 0x00; + regs[BMI160_INT_DATA_0] = 0x00; + regs[BMI160_INT_DATA_1] = 0x00; + regs[BMI160_INT_LOW_HIGH_0] = 0x07; + regs[BMI160_INT_LOW_HIGH_1] = 0x30; + regs[BMI160_INT_LOW_HIGH_2] = 0x81; + regs[BMI160_INT_LOW_HIGH_3] = 0xdb; + regs[BMI160_INT_LOW_HIGH_4] = 0xc0; + regs[BMI160_INT_MOTION_0] = 0x00; + regs[BMI160_INT_MOTION_1] = 0x14; + regs[BMI160_INT_MOTION_2] = 0x14; + regs[BMI160_INT_MOTION_3] = 0x24; + regs[BMI160_INT_TAP_0] = 0x04; + regs[BMI160_INT_TAP_1] = 0xda; + regs[BMI160_INT_ORIENT_0] = 0x18; + regs[BMI160_INT_ORIENT_1] = 0x48; + regs[BMI160_INT_FLAT_0] = 0x08; + regs[BMI160_INT_FLAT_1] = 0x11; + regs[BMI160_FOC_CONF] = 0x00; + regs[BMI160_CONF] = 0x00; + regs[BMI160_IF_CONF] = 0x00; + regs[BMI160_PMU_TRIGGER] = 0x00; + regs[BMI160_SELF_TEST] = 0x00; + regs[BMI160_STEP_CNT_0] = 0x00; + regs[BMI160_STEP_CNT_1] = 0x00; + regs[BMI160_STEP_CONF_0] = 0x00; + regs[BMI160_STEP_CONF_1] = 0x15; + regs[BMI160_CMD_REG] = 0x03; + + /* Call generic reset */ + tag_time = regs[BMI160_FIFO_CONFIG_1] & BMI160_FIFO_TAG_TIME_EN; + header = regs[BMI160_FIFO_CONFIG_1] & BMI160_FIFO_HEADER_EN; + bmi_emul_reset_common(emul, tag_time, header); +} + +/** + * @brief Clear all interrupt registers + * + * @param regs Pointer to array of emulator's registers + */ +static void bmi160_emul_clear_int(uint8_t *regs) +{ + regs[BMI160_INT_STATUS_0] = 0x00; + regs[BMI160_INT_STATUS_1] = 0x00; + regs[BMI160_INT_STATUS_2] = 0x00; + regs[BMI160_INT_STATUS_3] = 0x00; +} + +/** + * @brief Get offset value for given gyroscope value. If gyroscope value is + * above maximum (belowe minimum), then minimum -31,25°/s + * (maximum 31,25°/s) offset value is returned. + * + * @param gyr Gyroscope value + */ +static int16_t bmi160_emul_get_gyr_target_off(int32_t gyr) +{ + if (gyr > (int32_t)BMI_EMUL_125_DEG_S / 4) { + return -((int32_t)BMI_EMUL_125_DEG_S / 4); + } + + if (gyr < -((int32_t)BMI_EMUL_125_DEG_S / 4)) { + return BMI_EMUL_125_DEG_S / 4; + } + + return -gyr; +} + +/** + * @brief Get offset value for given accelerometer value. If accelerometer + * value - target is above maximum (belowe minimum), then minimum -0.5g + * (maximum 0.5g) offset value is returned. + * + * @param acc Accelerometer value + * @param target Target value in FOC configuration register format + */ +static int16_t bmi160_emul_get_acc_target_off(int32_t acc, uint8_t target) +{ + switch (target) { + case BMI160_FOC_ACC_PLUS_1G: + acc -= BMI_EMUL_1G; + break; + case BMI160_FOC_ACC_MINUS_1G: + acc += BMI_EMUL_1G; + break; + } + + if (acc > (int32_t)BMI_EMUL_1G / 2) { + return -((int32_t)BMI_EMUL_1G / 2); + } + + if (acc < -((int32_t)BMI_EMUL_1G / 2)) { + return BMI_EMUL_1G / 2; + } + + return -acc; +} + +/** + * @brief Handle fast offset compensation. Check FOC configuration register + * and sets gyroscope and/or accelerometer offset using current emulator + * state. + * + * @param regs Pointer to array of emulator's registers + * @param emul Pointer to BMI emulator + */ +static void bmi160_emul_handle_off_comp(uint8_t *regs, struct i2c_emul *emul) +{ + uint8_t target; + int16_t off; + int32_t val; + + if (regs[BMI160_FOC_CONF] & BMI160_FOC_GYRO_EN) { + val = bmi_emul_get_value(emul, BMI_EMUL_GYR_X); + off = bmi160_emul_get_gyr_target_off(val); + bmi_emul_set_off(emul, BMI_EMUL_GYR_X, off); + val = bmi_emul_get_value(emul, BMI_EMUL_GYR_Y); + off = bmi160_emul_get_gyr_target_off(val); + bmi_emul_set_off(emul, BMI_EMUL_GYR_Y, off); + val = bmi_emul_get_value(emul, BMI_EMUL_GYR_Z); + off = bmi160_emul_get_gyr_target_off(val); + bmi_emul_set_off(emul, BMI_EMUL_GYR_Z, off); + } + + target = (regs[BMI160_FOC_CONF] >> BMI160_FOC_ACC_X_OFFSET) & 0x3; + if (target) { + val = bmi_emul_get_value(emul, BMI_EMUL_ACC_X); + off = bmi160_emul_get_acc_target_off(val, target); + bmi_emul_set_off(emul, BMI_EMUL_ACC_X, off); + } + + target = (regs[BMI160_FOC_CONF] >> BMI160_FOC_ACC_Y_OFFSET) & 0x3; + if (target) { + val = bmi_emul_get_value(emul, BMI_EMUL_ACC_Y); + off = bmi160_emul_get_acc_target_off(val, target); + bmi_emul_set_off(emul, BMI_EMUL_ACC_Y, off); + } + + target = (regs[BMI160_FOC_CONF] >> BMI160_FOC_ACC_Z_OFFSET) & 0x3; + if (target) { + val = bmi_emul_get_value(emul, BMI_EMUL_ACC_Z); + off = bmi160_emul_get_acc_target_off(val, target); + bmi_emul_set_off(emul, BMI_EMUL_ACC_Z, off); + } +} + +/** + * @brief Execute first part of command. Emulate state of device which is + * during handling command (status bits etc). This function save time + * on which command should end. + * + * @param regs Pointer to array of emulator's registers + * @param emul Pointer to BMI emulator + * @param cmd Command that is starting + * + * @return 0 on success + * @return -EIO on failure + */ +static int bmi160_emul_start_cmd(uint8_t *regs, struct i2c_emul *emul, int cmd) +{ + int time; + int ret; + + switch (cmd) { + case BMI160_CMD_SOFT_RESET: + time = 1; + break; + case BMI160_CMD_START_FOC: + if ((regs[BMI160_FOC_CONF] & BMI160_FOC_GYRO_EN) && + ((regs[BMI160_PMU_STATUS] & + (0x3 << BMI160_PMU_GYR_OFFSET)) != + BMI160_PMU_NORMAL << BMI160_PMU_GYR_OFFSET)) { + LOG_ERR("Starting gyroscope FOC in low power mode"); + return -EIO; + } + + if ((regs[BMI160_FOC_CONF] & ~BMI160_FOC_GYRO_EN) && + ((regs[BMI160_PMU_STATUS] & + (0x3 << BMI160_PMU_ACC_OFFSET)) != + BMI160_PMU_NORMAL << BMI160_PMU_ACC_OFFSET)) { + LOG_ERR("Starting accelerometer FOC in low power mode"); + return -EIO; + } + + regs[BMI160_STATUS] &= ~BMI160_FOC_RDY; + time = 250; + break; + case BMI160_CMD_ACC_MODE_SUSP: + case BMI160_CMD_GYR_MODE_SUSP: + case BMI160_CMD_MAG_MODE_SUSP: + time = 0; + break; + /* Real hardware probably switch faster if not in suspend mode */ + case BMI160_CMD_ACC_MODE_NORMAL: + case BMI160_CMD_ACC_MODE_LOWPOWER: + time = 4; + break; + case BMI160_CMD_GYR_MODE_NORMAL: + case BMI160_CMD_GYR_MODE_FAST_STARTUP: + time = 80; + break; + case BMI160_CMD_MAG_MODE_NORMAL: + case BMI160_CMD_MAG_MODE_LOWPOWER: + time = 1; + break; + case BMI160_CMD_FIFO_FLUSH: + time = 0; + break; + case BMI160_CMD_INT_RESET: + time = 0; + break; + default: + LOG_ERR("Unknown command 0x%x", cmd); + return -EIO; + } + + regs[BMI160_CMD_REG] = cmd; + bmi_emul_set_cmd_end_time(emul, time); + + return 0; +} + +/** + * @brief Emulate end of ongoing command. + * + * @param regs Pointer to array of emulator's registers + * @param emul Pointer to BMI emulator + */ +static void bmi160_emul_end_cmd(uint8_t *regs, struct i2c_emul *emul) +{ + uint8_t pmu_status; + bool tag_time; + bool header; + int cmd; + + pmu_status = regs[BMI160_PMU_STATUS]; + cmd = regs[BMI160_CMD_REG]; + regs[BMI160_CMD_REG] = BMI160_CMD_NOOP; + tag_time = regs[BMI160_FIFO_CONFIG_1] & BMI160_FIFO_TAG_TIME_EN; + header = regs[BMI160_FIFO_CONFIG_1] & BMI160_FIFO_HEADER_EN; + + switch (cmd) { + case BMI160_CMD_SOFT_RESET: + bmi160_emul_reset(regs, emul); + break; + case BMI160_CMD_START_FOC: + bmi160_emul_handle_off_comp(regs, emul); + regs[BMI160_STATUS] |= BMI160_FOC_RDY; + break; + case BMI160_CMD_ACC_MODE_SUSP: + pmu_status &= ~(0x3 << BMI160_PMU_ACC_OFFSET); + pmu_status |= BMI160_PMU_SUSPEND << BMI160_PMU_ACC_OFFSET; + break; + case BMI160_CMD_ACC_MODE_NORMAL: + pmu_status &= ~(0x3 << BMI160_PMU_ACC_OFFSET); + pmu_status |= BMI160_PMU_NORMAL << BMI160_PMU_ACC_OFFSET; + break; + case BMI160_CMD_ACC_MODE_LOWPOWER: + pmu_status &= ~(0x3 << BMI160_PMU_ACC_OFFSET); + pmu_status |= BMI160_PMU_LOW_POWER << BMI160_PMU_ACC_OFFSET; + break; + case BMI160_CMD_GYR_MODE_SUSP: + pmu_status &= ~(0x3 << BMI160_PMU_GYR_OFFSET); + pmu_status |= BMI160_PMU_SUSPEND << BMI160_PMU_GYR_OFFSET; + break; + case BMI160_CMD_GYR_MODE_NORMAL: + pmu_status &= ~(0x3 << BMI160_PMU_GYR_OFFSET); + pmu_status |= BMI160_PMU_NORMAL << BMI160_PMU_GYR_OFFSET; + break; + case BMI160_CMD_GYR_MODE_FAST_STARTUP: + pmu_status &= ~(0x3 << BMI160_PMU_GYR_OFFSET); + pmu_status |= BMI160_PMU_FAST_STARTUP << BMI160_PMU_GYR_OFFSET; + break; + case BMI160_CMD_MAG_MODE_SUSP: + pmu_status &= ~(0x3 << BMI160_PMU_MAG_OFFSET); + pmu_status |= BMI160_PMU_SUSPEND << BMI160_PMU_MAG_OFFSET; + break; + case BMI160_CMD_MAG_MODE_NORMAL: + pmu_status &= ~(0x3 << BMI160_PMU_MAG_OFFSET); + pmu_status |= BMI160_PMU_NORMAL << BMI160_PMU_MAG_OFFSET; + break; + case BMI160_CMD_MAG_MODE_LOWPOWER: + pmu_status &= ~(0x3 << BMI160_PMU_MAG_OFFSET); + pmu_status |= BMI160_PMU_LOW_POWER << BMI160_PMU_MAG_OFFSET; + break; + case BMI160_CMD_FIFO_FLUSH: + bmi_emul_flush_fifo(emul, tag_time, header); + break; + case BMI160_CMD_INT_RESET: + bmi160_emul_clear_int(regs); + break; + } + + /* Clear FIFO on sensor on/off in headerless mode */ + if (pmu_status != regs[BMI160_PMU_STATUS] && !header) { + bmi_emul_flush_fifo(emul, tag_time, header); + } + + regs[BMI160_PMU_STATUS] = pmu_status; +} + +/** + * @brief BMI160 specific write function. It doesn't handle block writes. + * Check if read only register is not accessed. Before writing value, + * ongoing command is finished if possible. Write to CMD register is + * handled by BMI160 specific function. On changing of FIFO + * header/headerless mode, FIFO is flushed. + * + * @param regs Pointer to array of emulator's registers + * @param emul Pointer to BMI emulator + * @param reg Pointer to accessed reg + * @param byte Number of handled bytes in this write command + * @param val Value that is being written + * + * @return 0 on success + * @return BMI_EMUL_ACCESS_E on RO register access + * @return -EIO on error + */ +static int bmi160_emul_handle_write(uint8_t *regs, struct i2c_emul *emul, + int *reg, int byte, uint8_t val) +{ + bool tag_time; + bool header; + int ret; + + if (byte > 1) { + LOG_ERR("Block writes are not allowed"); + return -EIO; + } + + if (*reg <= BMI160_FIFO_DATA || + (*reg >= BMI160_STEP_CNT_0 && *reg <= BMI160_STEP_CNT_1)) { + return BMI_EMUL_ACCESS_E; + } + + /* Stop on going command if required */ + if (regs[BMI160_CMD_REG] != BMI160_CMD_NOOP && + bmi_emul_is_cmd_end(emul)) { + bmi160_emul_end_cmd(regs, emul); + } + + switch (*reg) { + case BMI160_CMD_REG: + if (regs[BMI160_CMD_REG] != BMI160_CMD_NOOP) { + LOG_ERR("Issued command before previous end"); + return -EIO; + } + + return bmi160_emul_start_cmd(regs, emul, val); + case BMI160_FIFO_CONFIG_1: + tag_time = regs[BMI160_FIFO_CONFIG_1] & BMI160_FIFO_TAG_TIME_EN; + header = regs[BMI160_FIFO_CONFIG_1] & BMI160_FIFO_HEADER_EN; + /* + * Clear FIFO on transition between headerless and + * header mode + */ + if (!!(val & BMI160_FIFO_HEADER_EN) != header) { + bmi_emul_flush_fifo(emul, tag_time, header); + } + break; + } + + return 0; +} + +/** + * @brief BMI160 specific read function. It handle block reads but only if + * device is not suspended. FIFO data register is trap register, so + * after reaching it, register address is not increased on block reads. + * Before reading value, ongoing command is finished if possible. + * Read of sensor data traps current emulator state in registers. + * Read of FIFO length and FIFO data triggers default BMI functions. + * + * @param regs Pointer to array of emulator's registers + * @param emul Pointer to BMI emulator + * @param reg Pointer to accessed reg + * @param byte Byte which is accessed during block read + * @param buf Pointer where read byte should be stored + * + * @return 0 on success + * @return BMI_EMUL_ACCESS_E on WO register access + * @return -EIO on other error + */ +static int bmi160_emul_handle_read(uint8_t *regs, struct i2c_emul *emul, + int *reg, int byte, char *buf) +{ + uint16_t fifo_len; + bool acc_off_en; + bool gyr_off_en; + bool tag_time; + bool header; + int gyr_shift; + int acc_shift; + int ret; + + /* + * If register is FIFO data, then read data from FIFO. + * Else block read access subsequent registers. + */ + if (*reg <= BMI160_FIFO_DATA && *reg + byte >= BMI160_FIFO_DATA) { + byte -= *reg - BMI160_FIFO_DATA; + *reg = BMI160_FIFO_DATA; + } else { + *reg += byte; + } + + /* Stop on going command if required */ + if (regs[BMI160_CMD_REG] != BMI160_CMD_NOOP && + bmi_emul_is_cmd_end(emul)) { + bmi160_emul_end_cmd(regs, emul); + } + + /* Burst reads are not supported if all sensors are in suspend mode */ + if ((regs[BMI160_PMU_STATUS] & 0x3f) == 0 && byte > 0) { + LOG_ERR("Block reads are not supported in suspend mode"); + return -EIO; + } + + tag_time = regs[BMI160_FIFO_CONFIG_1] & BMI160_FIFO_TAG_TIME_EN; + header = regs[BMI160_FIFO_CONFIG_1] & BMI160_FIFO_HEADER_EN; + acc_off_en = regs[BMI160_OFFSET_EN_GYR98] & BMI160_OFFSET_ACC_EN; + gyr_off_en = regs[BMI160_OFFSET_EN_GYR98] & BMI160_OFFSET_GYRO_EN; + gyr_shift = bmi160_emul_gyr_range_to_shift(regs[BMI160_GYR_RANGE]); + acc_shift = bmi160_emul_acc_range_to_shift(regs[BMI160_ACC_RANGE]); + + switch (*reg) { + case BMI160_GYR_X_L_G: + case BMI160_GYR_X_H_G: + case BMI160_GYR_Y_L_G: + case BMI160_GYR_Y_H_G: + case BMI160_GYR_Z_L_G: + case BMI160_GYR_Z_H_G: + case BMI160_ACC_X_L_G: + case BMI160_ACC_X_H_G: + case BMI160_ACC_Y_L_G: + case BMI160_ACC_Y_H_G: + case BMI160_ACC_Z_L_G: + case BMI160_ACC_Z_H_G: + case BMI160_SENSORTIME_0: + case BMI160_SENSORTIME_1: + case BMI160_SENSORTIME_2: + /* + * Snapshot of current emulator state is created on data read + * and shouldn't be changed until next I2C operation + */ + if (byte == 0) { + bmi_emul_state_to_reg(emul, acc_shift, gyr_shift, + BMI160_ACC_X_L_G, + BMI160_GYR_X_L_G, + BMI160_SENSORTIME_0, + acc_off_en, gyr_off_en); + } + break; + case BMI160_FIFO_LENGTH_0: + case BMI160_FIFO_LENGTH_1: + if (byte == 0) { + fifo_len = bmi_emul_fifo_len(emul, tag_time, header); + regs[BMI160_FIFO_LENGTH_0] = fifo_len & 0xff; + regs[BMI160_FIFO_LENGTH_1] = (fifo_len >> 8) & 0x7; + } + break; + case BMI160_FIFO_DATA: + regs[*reg] = bmi_emul_get_fifo_data(emul, byte, tag_time, + header, acc_shift, + gyr_shift); + break; + } + + *buf = regs[*reg]; + + return 0; +} + +/** Registers backed in NVM by BMI160 */ +const int bmi160_nvm_reg[] = {BMI160_NV_CONF, + BMI160_OFFSET_ACC70, + BMI160_OFFSET_ACC70 + 1, + BMI160_OFFSET_ACC70 + 2, + BMI160_OFFSET_GYR70, + BMI160_OFFSET_GYR70 + 1, + BMI160_OFFSET_GYR70 + 2, + BMI160_OFFSET_EN_GYR98}; + +/** Confguration of BMI160 */ +struct bmi_emul_type_data bmi160_emul = { + .sensortime_follow_config_frame = false, + .handle_write = bmi160_emul_handle_write, + .handle_read = bmi160_emul_handle_read, + .reset = bmi160_emul_reset, + .rsvd_mask = bmi_emul_160_rsvd_mask, + .nvm_reg = bmi160_nvm_reg, + .nvm_len = ARRAY_SIZE(bmi160_nvm_reg), + .gyr_off_reg = BMI160_OFFSET_GYR70, + .acc_off_reg = BMI160_OFFSET_ACC70, + .gyr98_off_reg = BMI160_OFFSET_EN_GYR98, +}; + +/** Check description in emul_bmi.h */ +const struct bmi_emul_type_data *get_bmi160_emul_type_data(void) +{ + return &bmi160_emul; +} diff --git a/zephyr/include/emul/emul_bmi.h b/zephyr/include/emul/emul_bmi.h new file mode 100644 index 0000000000..81d889a301 --- /dev/null +++ b/zephyr/include/emul/emul_bmi.h @@ -0,0 +1,518 @@ +/* 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. + */ + +/** + * @file + * + * @brief Backend API for BMI emulator + */ + +#ifndef __EMUL_BMI_H +#define __EMUL_BMI_H + +#include +#include +#include + +/** + * @brief BMI emulator backend API + * @defgroup bmi_emul BMI emulator + * @{ + * + * BMI emulator supports responses to all write and read I2C messages. + * Accelerometer and gyroscope registers are obtained from internal emulator + * state, range register and offset. FIFO is fully simulated. Emulator can be + * extended to support more models of BMI. + * Application may alter emulator state: + * + * - define a Device Tree overlay file to set which inadvisable driver behaviour + * should be treated as errors and which model is emulated + * - call @ref bmi_emul_set_read_func and @ref bmi_emul_set_write_func to setup + * custom handlers for I2C messages + * - call @ref bmi_emul_set_reg and @ref bmi_emul_get_reg to set and get value + * of BMI registers + * - call @ref bmi_emul_set_off and @ref bmi_emul_get_off to set and get + * internal offset value + * - call @ref bmi_emul_set_value and @ref bmi_emul_get_value to set and get + * accelerometer or gyroscope value + * - call bmi_emul_set_err_* to change emulator behaviour on inadvisable driver + * behaviour + * - call @ref bmi_emul_simulate_cmd_exec_time to enable or disable simulation + * of command execution time + * - call @ref bmi_emul_set_read_fail_reg and @ref bmi_emul_set_write_fail_reg + * to configure emulator to fail on given register read or write + * - call @ref bmi_emul_append_frame to add frame to FIFO + * - call @reg bmi_emul_set_skipped_frames to generate skip frame on next access + * to FIFO + */ + +/** + * Axis argument used in @ref bmi_emul_set_value @ref bmi_emul_get_value + * @ref bmi_emul_set_off and @ref bmi_emul_get_off + */ +enum bmi_emul_axis { + BMI_EMUL_ACC_X, + BMI_EMUL_ACC_Y, + BMI_EMUL_ACC_Z, + BMI_EMUL_GYR_X, + BMI_EMUL_GYR_Y, + BMI_EMUL_GYR_Z, +}; + +/** BMI emulator models */ +#define BMI_EMUL_160 1 + +/** Last register supported by emulator */ +#define BMI_EMUL_MAX_REG 0x80 +/** Maximum number of registers that can be backed in NVM */ +#define BMI_EMUL_MAX_NVM_REGS 10 + +/** Headers used in FIFO frames */ +#define BMI_EMUL_FIFO_HEAD_SKIP 0x40 +#define BMI_EMUL_FIFO_HEAD_TIME 0x44 +#define BMI_EMUL_FIFO_HEAD_CONFIG 0x48 +#define BMI_EMUL_FIFO_HEAD_EMPTY 0x80 +#define BMI_EMUL_FIFO_HEAD_DATA 0x80 +#define BMI_EMUL_FIFO_HEAD_DATA_MAG BIT(4) +#define BMI_EMUL_FIFO_HEAD_DATA_GYR BIT(3) +#define BMI_EMUL_FIFO_HEAD_DATA_ACC BIT(2) +#define BMI_EMUL_FIFO_HEAD_DATA_TAG_MASK 0x03 + +/** + * Acceleration 1g in internal emulator units. It is helpful for using + * functions @ref bmi_emul_set_value @ref bmi_emul_get_value + * @ref bmi_emul_set_off and @ref bmi_emul_get_off + */ +#define BMI_EMUL_1G BIT(14) +/** + * Gyroscope 125°/s in internal emulator units. It is helpful for using + * functions @ref bmi_emul_set_value @ref bmi_emul_get_value + * @ref bmi_emul_set_off and @ref bmi_emul_get_off + */ +#define BMI_EMUL_125_DEG_S BIT(15) + +/** Type of frames that can be added to the emulator frames list */ +#define BMI_EMUL_FRAME_CONFIG BIT(0) +#define BMI_EMUL_FRAME_ACC BIT(1) +#define BMI_EMUL_FRAME_MAG BIT(2) +#define BMI_EMUL_FRAME_GYR BIT(3) + +/** + * Code returned by model specific handle_read and handle_write functions, when + * RO register is accessed on write or WO register is accessed on read + */ +#define BMI_EMUL_ACCESS_E 1 + +/** + * Special register values used in @ref bmi_emul_set_read_fail_reg and + * @ref bmi_emul_set_write_fail_reg + */ +#define BMI_EMUL_FAIL_ALL_REG (-1) +#define BMI_EMUL_NO_FAIL_REG (-2) + +/** Structure used to describe single FIFO frame */ +struct bmi_emul_frame { + /** Type of frame */ + uint8_t type; + /** Tag added to data frame */ + uint8_t tag; + /** Value used in config frame */ + uint8_t config; + /** Accelerometer sensor values in internal emulator units */ + int32_t acc_x; + int32_t acc_y; + int32_t acc_z; + /** Gyroscope sensor values in internal emulator units */ + int32_t gyr_x; + int32_t gyr_y; + int32_t gyr_z; + /** Magnetometer/other sensor values in internal emulator units */ + int32_t mag_x; + int32_t mag_y; + int32_t mag_z; + int32_t rhall; + + /** Pointer to next frame or NULL */ + struct bmi_emul_frame *next; +}; + +/** Structure describing specific BMI model */ +struct bmi_emul_type_data { + /** Indicate if time frame should follow config frame */ + bool sensortime_follow_config_frame; + + /** + * @brief Model specific write function. It should modify state of + * emulator if required. @p reg value should be updated to + * register which is acctually accessed. + * + * @param regs Pointer to array of emulator's registers + * @param emul Pointer to BMI emulator + * @param reg Pointer to accessed reg. If different reg is accessed, + * this value should be modified. + * @param byte Number of handled bytes in this write command + * @param val Value that is being written + * + * @return 0 on success + * @return BMI_EMUL_ACCESS_E on RO register access + * @return other on error + */ + int (*handle_write)(uint8_t *regs, struct i2c_emul *emul, int *reg, + int byte, uint8_t val); + /** + * @brief Model specific read function. It should modify state of + * emulator if required. @p reg value should be updated to + * register which is acctually accessed. @p buf should be + * set to response value. + * + * @param regs Pointer to array of emulator's registers + * @param emul Pointer to BMI emulator + * @param reg Pointer to accessed reg. If different reg is accessed, + * this value should be modified. + * @param byte Byte which is accessed during block read + * @param buf Pointer where read byte should be stored + * + * @return 0 on success + * @return BMI_EMUL_ACCESS_E on WO register access + * @return other on error + */ + int (*handle_read)(uint8_t *regs, struct i2c_emul *emul, int *reg, + int byte, char *buf); + /** + * @brief Model specific reset function. It should modify state of + * emulator to imitate after reset conditions. + * + * @param regs Pointer to array of emulator's registers + * @param emul Pointer to BMI emulator + */ + void (*reset)(uint8_t *regs, struct i2c_emul *emul); + + /** Array of reserved bits mask for each register */ + const uint8_t *rsvd_mask; + + /** Array of registers that are backed in NVM */ + const int *nvm_reg; + /** Number of registers backed in NVM */ + int nvm_len; + + /** Gyroscope X axis register */ + int gyr_off_reg; + /** Accelerometer X axis register */ + int acc_off_reg; + /** Gyroscope 9 and 8 bits register */ + int gyr98_off_reg; +}; + +/** + * @brief Get BMI160 model specific structure. + * + * @return Pointer to BMI160 specific structure + */ +const struct bmi_emul_type_data *get_bmi160_emul_type_data(void); + +/** + * @brief Get pointer to BMI emulator using device tree order number. + * + * @param ord Device tree order number obtained from DT_DEP_ORD macro + * + * @return Pointer to BMI emulator + */ +struct i2c_emul *bmi_emul_get(int ord); + +/** + * @brief Custom function type that is used as user-defined callback in read + * I2C messages handling. + * + * @param emul Pointer to BMI emulator + * @param reg Address which is now accessed by read command + * @param byte Byte which is accessed during block read + * @param data Pointer to custom user data + * + * @return 0 on success. Value of @p reg should be set by @ref bmi_emul_set_reg + * @return 1 continue with normal BMI emulator handler + * @return negative on error + */ +typedef int (*bmi_emul_read_func)(struct i2c_emul *emul, int reg, int byte, + void *data); + +/** + * @brief Custom function type that is used as user-defined callback in write + * I2C messages handling. + * + * @param emul Pointer to BMA255 emulator + * @param reg Address which is now accessed by write command + * @param byte Number of handled bytes in this write command. It does include + * first byte containing accessed register address. + * @param val Value which is being written to @p reg + * @param data Pointer to custom user data + * + * @return 0 on success + * @return 1 continue with normal BMI emulator handler + * @return negative on error + */ +typedef int (*bmi_emul_write_func)(struct i2c_emul *emul, int reg, int byte, + uint8_t val, void *data); + +/** + * @brief Lock access to BMI properties. After acquiring lock, user + * may change emulator behaviour in multi-thread setup. + * + * @param emul Pointer to BMI emulator + * @param timeout Timeout in getting lock + * + * @return k_mutex_lock return code + */ +int bmi_emul_lock_data(struct i2c_emul *emul, k_timeout_t timeout); + +/** + * @brief Unlock access to BMI properties. + * + * @param emul Pointer to BMI emulator + * + * @return k_mutex_unlock return code + */ +int bmi_emul_unlock_data(struct i2c_emul *emul); + +/** + * @brief Set write handler for I2C messages. This function is called before + * generic handler. + * + * @param emul Pointer to BMI emulator + * @param func Pointer to custom function + * @param data User data passed on call of custom function + */ +void bmi_emul_set_write_func(struct i2c_emul *emul, bmi_emul_write_func func, + void *data); + +/** + * @brief Set read handler for I2C messages. This function is called before + * generic handler. + * + * @param emul Pointer to BMI emulator + * @param func Pointer to custom function + * @param data User data passed on call of custom function + */ +void bmi_emul_set_read_func(struct i2c_emul *emul, bmi_emul_read_func func, + void *data); + +/** + * @brief Set value of given register of BMI + * + * @param emul Pointer to BMI emulator + * @param reg Register address which value will be changed + * @param val New value of the register + */ +void bmi_emul_set_reg(struct i2c_emul *emul, int reg, uint8_t val); + +/** + * @brief Get value of given register of BMI + * + * @param emul Pointer to BMI emulator + * @param reg Register address + * + * @return Value of the register + */ +uint8_t bmi_emul_get_reg(struct i2c_emul *emul, int reg); + +/** + * @brief Setup fail on read of given register of BMI + * + * @param emul Pointer to BMI emulator + * @param reg Register address or one of special values (BMI_EMUL_FAIL_ALL_REG, + * BMI_EMUL_NO_FAIL_REG) + */ +void bmi_emul_set_read_fail_reg(struct i2c_emul *emul, int reg); + +/** + * @brief Setup fail on write of given register of BMI + * + * @param emul Pointer to BMI emulator + * @param reg Register address or one of special values (BMI_EMUL_FAIL_ALL_REG, + * BMI_EMUL_NO_FAIL_REG) + */ +void bmi_emul_set_write_fail_reg(struct i2c_emul *emul, int reg); + +/** + * @brief Get internal value of offset for given axis and sensor + * + * @param emul Pointer to BMI emulator + * @param axis Axis to access + * + * @return Offset of given axis. LSB for accelerometer is 0.061mg and for + * gyroscope is 0.0037°/s. + */ +int16_t bmi_emul_get_off(struct i2c_emul *emul, enum bmi_emul_axis axis); + +/** + * @brief Set internal value of offset for given axis and sensor + * + * @param emul Pointer to BMI emulator + * @param axis Axis to access + * @param val New value of given axis. LSB for accelerometer is 0.061mg and for + * gyroscope is 0.0037°/s. + */ +void bmi_emul_set_off(struct i2c_emul *emul, enum bmi_emul_axis axis, + int16_t val); + +/** + * @brief Get internal value of sensor for given axis + * + * @param emul Pointer to BMI emulator + * @param axis Axis to access + * + * @return Sensor value of given axis. LSB for accelerometer is 0.061mg and for + * gyroscope is 0.0037°/s. + */ +int32_t bmi_emul_get_value(struct i2c_emul *emul, enum bmi_emul_axis axis); + +/** + * @brief Set internal value of sensor for given axis + * + * @param emul Pointer to BMI emulator + * @param axis Axis to access + * @param val New value of given axis. LSB for accelerometer is 0.061mg and for + * gyroscope is 0.0037°/s. + */ +void bmi_emul_set_value(struct i2c_emul *emul, enum bmi_emul_axis axis, + int32_t val); + +/** + * @brief Set if error should be generated when read only register is being + * written + * + * @param emul Pointer to BMI emulator + * @param set Check for this error + */ +void bmi_emul_set_err_on_ro_write(struct i2c_emul *emul, bool set); + +/** + * @brief Set if error should be generated when reserved bits of register are + * not set to 0 on write I2C message + * + * @param emul Pointer to BMI emulator + * @param set Check for this error + */ +void bmi_emul_set_err_on_rsvd_write(struct i2c_emul *emul, bool set); + +/** + * @brief Set if error should be generated when write only register is read + * + * @param emul Pointer to BMI emulator + * @param set Check for this error + */ +void bmi_emul_set_err_on_wo_read(struct i2c_emul *emul, bool set); + +/** + * @brief Set if effect of simulated command should take place after simulated + * time pass from issuing command. + * + * @param emul Pointer to BMI emulator + * @param set Simulate command execution time + */ +void bmi_emul_simulate_cmd_exec_time(struct i2c_emul *emul, bool set); + +/** + * @brief Set number of skipped frames. It will generate skip frame on next + * access to FIFO. After that number of skipped frames is reset to 0. + * + * @param emul Pointer to BMI emulator + * @param skip Number of skipped frames + */ +void bmi_emul_set_skipped_frames(struct i2c_emul *emul, uint8_t skip); + +/** + * @brief Clear all FIFO frames, set current frame to empty and reset fifo_skip + * counter + * + * @param emul Pointer to BMI emulator + * @param tag_time Indicate if sensor time should be included in empty frame + * @param header Indicate if header should be included in frame + */ +void bmi_emul_flush_fifo(struct i2c_emul *emul, bool tag_time, bool header); + +/** + * @brief Restore registers backed by NVM, reset sensor time and flush FIFO + * + * @param emul Pointer to BMI emulator + */ +void bmi_emul_reset_common(struct i2c_emul *emul, bool tag_time, bool header); + +/** + * @brief Set command end time to @p time ms from now + * + * @param emul Pointer to BMI emulator + * @param time After this amount of ms command should end + */ +void bmi_emul_set_cmd_end_time(struct i2c_emul *emul, int time); + +/** + * @brief Check if command should end + * + * @param emul Pointer to BMI emulator + */ +bool bmi_emul_is_cmd_end(struct i2c_emul *emul); + +/** + * @brief Append FIFO @p frame to the emulator list of frames. It can be read + * using I2C interface. + * + * @param emul Pointer to BMI emulator + * @param frame Pointer to new FIFO frame. Pointed data has to be valid while + * emulator may use this frame (until flush of FIFO or reading + * it out through I2C) + */ +void bmi_emul_append_frame(struct i2c_emul *emul, struct bmi_emul_frame *frame); + +/** + * @brief Get length of all frames that are on the emulator list of frames. + * + * @param emul Pointer to BMI emulator + * @param tag_time Indicate if sensor time should be included in empty frame + * @param header Indicate if header should be included in frame + */ +uint16_t bmi_emul_fifo_len(struct i2c_emul *emul, bool tag_time, bool header); + +/** + * @brief Get next byte that should be returned on FIFO data access. + * + * @param emul Pointer to BMI emulator + * @param byte Which byte of block read command is currently handled + * @param tag_time Indicate if sensor time should be included in empty frame + * @param header Indicate if header should be included in frame + * @param acc_shift How many bits should be right shifted from accelerometer + * data + * @param gyr_shift How many bits should be right shifted from gyroscope data + * + * @return FIFO data byte + */ +uint8_t bmi_emul_get_fifo_data(struct i2c_emul *emul, int byte, + bool tag_time, bool header, int acc_shift, + int gyr_shift); + +/** + * @brief Saves current internal state of sensors to emulator's registers. + * + * @param emul Pointer to BMI emulator + * @param acc_shift How many bits should be right shifted from accelerometer + * data + * @param gyr_shift How many bits should be right shifted from gyroscope data + * @param acc_reg Register which holds LSB of accelerometer sensor + * @param gyr_reg Register which holds LSB of gyroscope sensor + * @param sensortime_reg Register which holds LSB of sensor time + * @param acc_off_en Indicate if accelerometer offset should be included to + * sensor data value + * @param gyr_off_en Indicate if gyroscope offset should be included to + * sensor data value + */ +void bmi_emul_state_to_reg(struct i2c_emul *emul, int acc_shift, + int gyr_shift, int acc_reg, int gyr_reg, + int sensortime_reg, bool acc_off_en, + bool gyr_off_en); + +/** + * @} + */ + +#endif /* __EMUL_BMI_H */ -- cgit v1.2.1