diff options
Diffstat (limited to 'zephyr/emul/emul_bma255.c')
-rw-r--r-- | zephyr/emul/emul_bma255.c | 1042 |
1 files changed, 0 insertions, 1042 deletions
diff --git a/zephyr/emul/emul_bma255.c b/zephyr/emul/emul_bma255.c deleted file mode 100644 index 77b1f5246c..0000000000 --- a/zephyr/emul/emul_bma255.c +++ /dev/null @@ -1,1042 +0,0 @@ -/* 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_bma255 - -#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL -#include <logging/log.h> -LOG_MODULE_REGISTER(emul_bma255); - -#include <device.h> -#include <emul.h> -#include <drivers/i2c.h> -#include <drivers/i2c_emul.h> - -#include "emul/emul_common_i2c.h" -#include "emul/emul_bma255.h" - -#include "driver/accel_bma2x2.h" - -#define BMA_DATA_FROM_I2C_EMUL(_emul) \ - CONTAINER_OF(CONTAINER_OF(_emul, struct i2c_common_emul_data, emul), \ - struct bma_emul_data, common) - -/** Run-time data used by the emulator */ -struct bma_emul_data { - /** Common I2C data */ - struct i2c_common_emul_data common; - - /** Value of data byte in ongoing write message */ - uint8_t write_byte; - - /** Current state of all emulated BMA255 registers */ - uint8_t reg[0x40]; - /** Current state of NVM where offset and GP0/1 can be saved */ - uint8_t nvm_x; - uint8_t nvm_y; - uint8_t nvm_z; - uint8_t nvm_gp0; - uint8_t nvm_gp1; - /** Internal offset values used in calculations */ - int16_t off_x; - int16_t off_y; - int16_t off_z; - /** Internal values of accelerometr */ - int16_t acc_x; - int16_t acc_y; - int16_t acc_z; - - /** - * Return error when trying to start offset compensation when not ready - * flag is set. - */ - bool error_on_cal_trg_nrdy; - /** - * Return error when trying to start offset compensation with range - * set to value different than 2G. - */ - bool error_on_cal_trg_bad_range; - /** 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; - /** Return error when trying to access MSB before LSB */ - bool error_on_msb_first; - /** - * Flag set when LSB register is accessed and cleared when MSB is - * accessed. Allows to track order of accessing acc registers - */ - bool lsb_x_read; - bool lsb_y_read; - bool lsb_z_read; -}; - -/** Check description in emul_bma255.h */ -void bma_emul_set_reg(struct i2c_emul *emul, int reg, uint8_t val) -{ - struct bma_emul_data *data; - - if (reg < 0 || reg > BMA2x2_FIFO_DATA_OUTPUT_ADDR) { - return; - } - - data = BMA_DATA_FROM_I2C_EMUL(emul); - data->reg[reg] = val; -} - -/** Check description in emul_bma255.h */ -uint8_t bma_emul_get_reg(struct i2c_emul *emul, int reg) -{ - struct bma_emul_data *data; - - if (reg < 0 || reg > BMA2x2_FIFO_DATA_OUTPUT_ADDR) { - return 0; - } - - data = BMA_DATA_FROM_I2C_EMUL(emul); - - return data->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 uint16_t bma_emul_val_to_twos_comp(int16_t val) -{ - uint16_t twos_comp_val; - - /* Make sure that value is converted to twos compliment format */ - if (val < 0) { - twos_comp_val = (uint16_t)(-val); - twos_comp_val = ~twos_comp_val + 1; - } else { - twos_comp_val = (uint16_t)val; - } - - return twos_comp_val; -} - -/** - * @brief Convert value from NVM format (8bit, 0x01 == 7.8mg) to internal - * offset format (16bit, 0x01 == 0.97mg). - * - * @param nvm Value in NVM format (8bit, 0x01 == 7.8mg). This is binary - * representation of two's complement signed number. - * - * @return offset Internal representation of @p nvm (16bit, 0x01 == 0.97mg) - */ -static int16_t bma_emul_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 7.8mg, while LSB in internal offset is 0.97mg */ - offset *= sign * 8; - - return offset; -} - -/** - * @brief Convert value from internal offset format (16bit, 0x01 == 0.97mg) 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.97mg). - * - * @return nvm NVM format representation of @p val (8bit, 0x01 == 7.8mg) - */ -static uint8_t bma_emul_off_to_nvm(int16_t off) -{ - uint16_t twos_comp_val; - uint8_t nvm = 0; - - twos_comp_val = bma_emul_val_to_twos_comp(off); - - /* - * LSB in internal representation has value 0.97mg, while in NVM - * LSB is 7.8mg. Skip 0.97mg, 1.9mg and 3.9mg bits. - */ - nvm |= (twos_comp_val >> 3) & 0x7f; - /* Set sign bit */ - nvm |= (twos_comp_val & BIT(15)) ? BIT(7) : 0x00; - - return nvm; -} - -/** Check description in emul_bma255.h */ -int16_t bma_emul_get_off(struct i2c_emul *emul, int axis) -{ - struct bma_emul_data *data; - - data = BMA_DATA_FROM_I2C_EMUL(emul); - - switch (axis) { - case BMA_EMUL_AXIS_X: - return data->off_x; - case BMA_EMUL_AXIS_Y: - return data->off_y; - case BMA_EMUL_AXIS_Z: - return data->off_z; - } - - return 0; -} - -/** Check description in emul_bma255.h */ -void bma_emul_set_off(struct i2c_emul *emul, int axis, int16_t val) -{ - struct bma_emul_data *data; - - data = BMA_DATA_FROM_I2C_EMUL(emul); - - switch (axis) { - case BMA_EMUL_AXIS_X: - data->off_x = val; - data->reg[BMA2x2_OFFSET_X_AXIS_ADDR] = bma_emul_off_to_nvm( - data->off_x); - break; - case BMA_EMUL_AXIS_Y: - data->off_y = val; - data->reg[BMA2x2_OFFSET_Y_AXIS_ADDR] = bma_emul_off_to_nvm( - data->off_y); - break; - case BMA_EMUL_AXIS_Z: - data->off_z = val; - data->reg[BMA2x2_OFFSET_Z_AXIS_ADDR] = bma_emul_off_to_nvm( - data->off_z); - break; - } -} - -/** Check description in emul_bma255.h */ -int16_t bma_emul_get_acc(struct i2c_emul *emul, int axis) -{ - struct bma_emul_data *data; - - data = BMA_DATA_FROM_I2C_EMUL(emul); - - switch (axis) { - case BMA_EMUL_AXIS_X: - return data->acc_x; - case BMA_EMUL_AXIS_Y: - return data->acc_y; - case BMA_EMUL_AXIS_Z: - return data->acc_z; - } - - return 0; -} - -/** Check description in emul_bma255.h */ -void bma_emul_set_acc(struct i2c_emul *emul, int axis, int16_t val) -{ - struct bma_emul_data *data; - - data = BMA_DATA_FROM_I2C_EMUL(emul); - - switch (axis) { - case BMA_EMUL_AXIS_X: - data->acc_x = val; - break; - case BMA_EMUL_AXIS_Y: - data->acc_y = val; - break; - case BMA_EMUL_AXIS_Z: - data->acc_z = val; - break; - } -} - -/** Check description in emul_bma255.h */ -void bma_emul_set_err_on_cal_nrdy(struct i2c_emul *emul, bool set) -{ - struct bma_emul_data *data; - - data = BMA_DATA_FROM_I2C_EMUL(emul); - data->error_on_cal_trg_nrdy = set; -} - -/** Check description in emul_bma255.h */ -void bma_emul_set_err_on_cal_bad_range(struct i2c_emul *emul, bool set) -{ - struct bma_emul_data *data; - - data = BMA_DATA_FROM_I2C_EMUL(emul); - data->error_on_cal_trg_bad_range = set; -} - -/** Check description in emul_bma255.h */ -void bma_emul_set_err_on_ro_write(struct i2c_emul *emul, bool set) -{ - struct bma_emul_data *data; - - data = BMA_DATA_FROM_I2C_EMUL(emul); - data->error_on_ro_write = set; -} - -/** Check description in emul_bma255.h */ -void bma_emul_set_err_on_rsvd_write(struct i2c_emul *emul, bool set) -{ - struct bma_emul_data *data; - - data = BMA_DATA_FROM_I2C_EMUL(emul); - data->error_on_rsvd_write = set; -} - -/** Check description in emul_bma255.h */ -void bma_emul_set_err_on_msb_first(struct i2c_emul *emul, bool set) -{ - struct bma_emul_data *data; - - data = BMA_DATA_FROM_I2C_EMUL(emul); - data->error_on_msb_first = set; -} - -/** Mask reserved bits in each register of BMA255 */ -static const uint8_t bma_emul_rsvd_mask[] = { - [BMA2x2_CHIP_ID_ADDR] = 0x00, - [0x01] = 0xff, /* Reserved */ - [BMA2x2_X_AXIS_LSB_ADDR] = 0x0e, - [BMA2x2_X_AXIS_MSB_ADDR] = 0x00, - [BMA2x2_Y_AXIS_LSB_ADDR] = 0x0e, - [BMA2x2_Y_AXIS_MSB_ADDR] = 0x00, - [BMA2x2_Z_AXIS_LSB_ADDR] = 0x0e, - [BMA2x2_Z_AXIS_MSB_ADDR] = 0x00, - [BMA2x2_TEMP_ADDR] = 0x00, - [BMA2x2_STAT1_ADDR] = 0x00, - [BMA2x2_STAT2_ADDR] = 0x1f, - [BMA2x2_STAT_TAP_SLOPE_ADDR] = 0x00, - [BMA2x2_STAT_ORIENT_HIGH_ADDR] = 0x00, - [0x0d] = 0xff, /* Reserved */ - [BMA2x2_STAT_FIFO_ADDR] = 0x00, - [BMA2x2_RANGE_SELECT_ADDR] = 0xf0, - [BMA2x2_BW_SELECT_ADDR] = 0xe0, - [BMA2x2_MODE_CTRL_ADDR] = 0x01, - [BMA2x2_LOW_NOISE_CTRL_ADDR] = 0x9f, - [BMA2x2_DATA_CTRL_ADDR] = 0x3f, - [BMA2x2_RST_ADDR] = 0x00, - [0x15] = 0xff, /* Reserved */ - [BMA2x2_INTR_ENABLE1_ADDR] = 0x08, - [BMA2x2_INTR_ENABLE2_ADDR] = 0x80, - [BMA2x2_INTR_SLOW_NO_MOTION_ADDR] = 0xf0, - [BMA2x2_INTR1_PAD_SELECT_ADDR] = 0x00, - [BMA2x2_INTR_DATA_SELECT_ADDR] = 0x18, - [BMA2x2_INTR2_PAD_SELECT_ADDR] = 0x00, - [0x1c] = 0xff, /* Reserved */ - [0x1d] = 0xff, /* Reserved */ - [BMA2x2_INTR_SOURCE_ADDR] = 0xc0, - [0x1f] = 0xff, /* Reserved */ - [BMA2x2_INTR_SET_ADDR] = 0xf0, - [BMA2x2_INTR_CTRL_ADDR] = 0x70, - [BMA2x2_LOW_DURN_ADDR] = 0x00, - [BMA2x2_LOW_THRES_ADDR] = 0x00, - [BMA2x2_LOW_HIGH_HYST_ADDR] = 0x38, - [BMA2x2_HIGH_DURN_ADDR] = 0x00, - [BMA2x2_HIGH_THRES_ADDR] = 0x00, - [BMA2x2_SLOPE_DURN_ADDR] = 0x00, - [BMA2x2_SLOPE_THRES_ADDR] = 0x00, - [BMA2x2_SLOW_NO_MOTION_THRES_ADDR] = 0x00, - [BMA2x2_TAP_PARAM_ADDR] = 0x38, - [BMA2x2_TAP_THRES_ADDR] = 0x20, - [BMA2x2_ORIENT_PARAM_ADDR] = 0x80, - [BMA2x2_THETA_BLOCK_ADDR] = 0x80, - [BMA2x2_THETA_FLAT_ADDR] = 0xc0, - [BMA2x2_FLAT_HOLD_TIME_ADDR] = 0xc8, - [BMA2x2_FIFO_WML_TRIG] = 0xc0, - [0x31] = 0xff, /* Reserved */ - [BMA2x2_SELFTEST_ADDR] = 0xf8, - [BMA2x2_EEPROM_CTRL_ADDR] = 0x00, - [BMA2x2_SERIAL_CTRL_ADDR] = 0xf8, - [0x35] = 0xff, /* Reserved */ - [BMA2x2_OFFSET_CTRL_ADDR] = 0x08, - [BMA2x2_OFC_SETTING_ADDR] = 0x80, - [BMA2x2_OFFSET_X_AXIS_ADDR] = 0x00, - [BMA2x2_OFFSET_Y_AXIS_ADDR] = 0x00, - [BMA2x2_OFFSET_Z_AXIS_ADDR] = 0x00, - [BMA2x2_GP0_ADDR] = 0x00, - [BMA2x2_GP1_ADDR] = 0x00, - [0x3d] = 0xff, /* Reserved */ - [BMA2x2_FIFO_MODE_ADDR] = 0x3c, - [BMA2x2_FIFO_DATA_OUTPUT_ADDR] = 0x00, -}; - -/** - * @brief Reset register values and internal representation of offset and two - * general purpose registers - * - * @param emul Pointer to BMA255 emulator - */ -static void bma_emul_restore_nvm(struct i2c_emul *emul) -{ - struct bma_emul_data *data; - - data = BMA_DATA_FROM_I2C_EMUL(emul); - - /* Restore registers values */ - data->reg[BMA2x2_OFFSET_X_AXIS_ADDR] = data->nvm_x; - data->reg[BMA2x2_OFFSET_Y_AXIS_ADDR] = data->nvm_y; - data->reg[BMA2x2_OFFSET_Z_AXIS_ADDR] = data->nvm_z; - data->reg[BMA2x2_GP0_ADDR] = data->nvm_gp0; - data->reg[BMA2x2_GP1_ADDR] = data->nvm_gp1; - - /* Restore internal offset values */ - data->off_x = bma_emul_nvm_to_off(data->nvm_x); - data->off_y = bma_emul_nvm_to_off(data->nvm_y); - data->off_z = bma_emul_nvm_to_off(data->nvm_z); -} - -/** - * @brief Reset registers to default values and restore registers backed by NVM - * - * @param emul Pointer to BMA255 emulator - */ -static void bma_emul_reset(struct i2c_emul *emul) -{ - struct bma_emul_data *data; - - data = BMA_DATA_FROM_I2C_EMUL(emul); - - data->reg[BMA2x2_CHIP_ID_ADDR] = 0xfa; - data->reg[0x01] = 0x00; /* Reserved */ - data->reg[BMA2x2_X_AXIS_LSB_ADDR] = 0x00; - data->reg[BMA2x2_X_AXIS_MSB_ADDR] = 0x00; - data->reg[BMA2x2_Y_AXIS_LSB_ADDR] = 0x00; - data->reg[BMA2x2_Y_AXIS_MSB_ADDR] = 0x00; - data->reg[BMA2x2_Z_AXIS_LSB_ADDR] = 0x00; - data->reg[BMA2x2_Z_AXIS_MSB_ADDR] = 0x00; - data->reg[BMA2x2_TEMP_ADDR] = 0x00; - data->reg[BMA2x2_STAT1_ADDR] = 0x00; - data->reg[BMA2x2_STAT2_ADDR] = 0x00; - data->reg[BMA2x2_STAT_TAP_SLOPE_ADDR] = 0x00; - data->reg[BMA2x2_STAT_ORIENT_HIGH_ADDR] = 0x00; - data->reg[0x0d] = 0xff; /* Reserved */ - data->reg[BMA2x2_STAT_FIFO_ADDR] = 0x00; - data->reg[BMA2x2_RANGE_SELECT_ADDR] = 0x03; - data->reg[BMA2x2_BW_SELECT_ADDR] = 0x0f; - data->reg[BMA2x2_MODE_CTRL_ADDR] = 0x00; - data->reg[BMA2x2_LOW_NOISE_CTRL_ADDR] = 0x00; - data->reg[BMA2x2_DATA_CTRL_ADDR] = 0x00; - data->reg[BMA2x2_RST_ADDR] = 0x00; - data->reg[0x15] = 0xff; /* Reserved */ - data->reg[BMA2x2_INTR_ENABLE1_ADDR] = 0x00; - data->reg[BMA2x2_INTR_ENABLE2_ADDR] = 0x00; - data->reg[BMA2x2_INTR_SLOW_NO_MOTION_ADDR] = 0x00; - data->reg[BMA2x2_INTR1_PAD_SELECT_ADDR] = 0x00; - data->reg[BMA2x2_INTR_DATA_SELECT_ADDR] = 0x00; - data->reg[BMA2x2_INTR2_PAD_SELECT_ADDR] = 0x00; - data->reg[0x1c] = 0xff; /* Reserved */ - data->reg[0x1d] = 0xff; /* Reserved */ - data->reg[BMA2x2_INTR_SOURCE_ADDR] = 0x00; - data->reg[0x1f] = 0xff; /* Reserved */ - data->reg[BMA2x2_INTR_SET_ADDR] = 0x05; - data->reg[BMA2x2_INTR_CTRL_ADDR] = 0x00; - data->reg[BMA2x2_LOW_DURN_ADDR] = 0x09; - data->reg[BMA2x2_LOW_THRES_ADDR] = 0x30; - data->reg[BMA2x2_LOW_HIGH_HYST_ADDR] = 0x81; - data->reg[BMA2x2_HIGH_DURN_ADDR] = 0x0f; - data->reg[BMA2x2_HIGH_THRES_ADDR] = 0xc0; - data->reg[BMA2x2_SLOPE_DURN_ADDR] = 0x00; - data->reg[BMA2x2_SLOPE_THRES_ADDR] = 0x14; - data->reg[BMA2x2_SLOW_NO_MOTION_THRES_ADDR] = 0x14; - data->reg[BMA2x2_TAP_PARAM_ADDR] = 0x04; - data->reg[BMA2x2_TAP_THRES_ADDR] = 0x0a; - data->reg[BMA2x2_ORIENT_PARAM_ADDR] = 0x18; - data->reg[BMA2x2_THETA_BLOCK_ADDR] = 0x48; - data->reg[BMA2x2_THETA_FLAT_ADDR] = 0x08; - data->reg[BMA2x2_FLAT_HOLD_TIME_ADDR] = 0x11; - data->reg[BMA2x2_FIFO_WML_TRIG] = 0x00; - data->reg[0x31] = 0xff; /* Reserved */ - data->reg[BMA2x2_SELFTEST_ADDR] = 0x00; - data->reg[BMA2x2_EEPROM_CTRL_ADDR] = 0xf0; - data->reg[BMA2x2_SERIAL_CTRL_ADDR] = 0x00; - data->reg[0x35] = 0x00; /* Reserved */ - data->reg[BMA2x2_OFFSET_CTRL_ADDR] = 0x10; - data->reg[BMA2x2_OFC_SETTING_ADDR] = 0x00; - data->reg[0x3d] = 0xff; /* Reserved */ - data->reg[BMA2x2_FIFO_MODE_ADDR] = 0x00; - data->reg[BMA2x2_FIFO_DATA_OUTPUT_ADDR] = 0x00; - - /* Restore registers backed in NVM */ - bma_emul_restore_nvm(emul); -} - -/** - * @brief Convert range in format of RANGE_SELECT register to number of bits - * that should be shifted right to obtain 12 bit reported accelerometer - * value from internal 16 bit value - * - * @param range Value of RANGE_SELECT register - * - * @return shift Number of LSB that should be ignored from internal - * accelerometer value - */ -static int bma_emul_range_to_shift(uint8_t range) -{ - switch (range & BMA2x2_RANGE_SELECT_MSK) { - case BMA2x2_RANGE_2G: - return 0; - case BMA2x2_RANGE_4G: - return 1; - case BMA2x2_RANGE_8G: - return 2; - case BMA2x2_RANGE_16G: - return 3; - default: - return -1; - } -} - -/** - * @brief Handle write requests to NVM control register. Allows to load/store - * NVM only when ready in current NVM control register is set. Load and - * stores to NVM are backed in bma_emul_data structure - * - * @param emul Pointer to BMA255 emulator - * @param val Value that is being written to NVM contorl register - * - * @return 0 on success - */ -static int bma_emul_handle_nvm_write(struct i2c_emul *emul, uint8_t val) -{ - struct bma_emul_data *data; - uint8_t writes_rem; - - data = BMA_DATA_FROM_I2C_EMUL(emul); - - /* NVM not ready, ignore write/load requests */ - if (!(data->reg[BMA2x2_EEPROM_CTRL_ADDR] & BMA2x2_EEPROM_RDY)) { - return 0; - } - - /* Restore data from NVM */ - if (val & BMA2x2_EEPROM_LOAD) { - bma_emul_restore_nvm(emul); - } - - writes_rem = (data->reg[BMA2x2_EEPROM_CTRL_ADDR] & - BMA2x2_EEPROM_REMAIN_MSK) >> BMA2x2_EEPROM_REMAIN_OFF; - /* Trigger write is set, write is unlocked and writes remaining */ - if (val & BMA2x2_EEPROM_PROG && - data->reg[BMA2x2_EEPROM_CTRL_ADDR] & BMA2x2_EEPROM_PROG_EN && - writes_rem > 0) { - data->nvm_x = data->reg[BMA2x2_OFFSET_X_AXIS_ADDR]; - data->nvm_y = data->reg[BMA2x2_OFFSET_Y_AXIS_ADDR]; - data->nvm_z = data->reg[BMA2x2_OFFSET_Z_AXIS_ADDR]; - data->nvm_gp0 = data->reg[BMA2x2_GP0_ADDR]; - data->nvm_gp1 = data->reg[BMA2x2_GP1_ADDR]; - /* Decrement number of remaining writes and save it in reg */ - writes_rem--; - data->reg[BMA2x2_EEPROM_CTRL_ADDR] &= - ~BMA2x2_EEPROM_REMAIN_MSK; - data->reg[BMA2x2_EEPROM_CTRL_ADDR] |= - writes_rem << BMA2x2_EEPROM_REMAIN_OFF; - } - - return 0; -} - -/** - * @brief Clear all interrupt registers - * - * @param emul Pointer to BMA255 emulator - */ -static void bma_emul_clear_int(struct i2c_emul *emul) -{ - struct bma_emul_data *data; - - data = BMA_DATA_FROM_I2C_EMUL(emul); - - data->reg[BMA2x2_STAT1_ADDR] = 0x00; - data->reg[BMA2x2_STAT2_ADDR] = 0x00; - data->reg[BMA2x2_STAT_TAP_SLOPE_ADDR] = 0x00; - data->reg[BMA2x2_STAT_ORIENT_HIGH_ADDR] = 0x00; -} - -/** - * @brief Get target value from offset compensation setting register for given - * @p axis - * - * @param emul Pointer to BMA255 emulator - * @param axis Axis to access: 0 - X, 1 - Y, 2 - Z - * - * @return target Value to which offset compensation should be calculated - */ -static int16_t bma_emul_get_target(struct i2c_emul *emul, int axis) -{ - struct bma_emul_data *data; - uint8_t target; - - data = BMA_DATA_FROM_I2C_EMUL(emul); - - target = data->reg[BMA2x2_OFC_SETTING_ADDR] >> - BMA2x2_OFC_TARGET_AXIS(axis); - switch (target) { - case BMA2x2_OFC_TARGET_0G: - return 0; - case BMA2x2_OFC_TARGET_PLUS_1G: - return BMA_EMUL_1G; - case BMA2x2_OFC_TARGET_MINUS_1G: - return -((int)BMA_EMUL_1G); - } - - return 0; -} - -/** - * @brief Handle writes to offset compensation control register. It allows to - * reset offset registers. It check if offset compenstation is ready - * and if range is set to 2G (required for fast compensation according - * to BMA255 documentation). If fast compensation is successfully - * triggered, internal offset value is set to - * (target - internal accelerometer value). - * - * @param emul Pointer to BMA255 emulator - * @param val Value being written to offset compensation control register - * - * @return 0 on success - * @return -EIO when trying to start fast compensation in wrong emulator state - */ -static int bma_emul_handle_off_comp(struct i2c_emul *emul, uint8_t val) -{ - struct bma_emul_data *data; - uint8_t trigger; - int16_t target; - - data = BMA_DATA_FROM_I2C_EMUL(emul); - - if (val & BMA2x2_OFFSET_RESET) { - data->off_x = 0; - data->off_y = 0; - data->off_z = 0; - data->reg[BMA2x2_OFFSET_X_AXIS_ADDR] = 0; - data->reg[BMA2x2_OFFSET_Y_AXIS_ADDR] = 0; - data->reg[BMA2x2_OFFSET_Z_AXIS_ADDR] = 0; - } - - - trigger = (val & BMA2x2_OFFSET_TRIGGER_MASK) >> - BMA2x2_OFFSET_TRIGGER_OFF; - - if (!(data->reg[BMA2x2_OFFSET_CTRL_ADDR] & BMA2x2_OFFSET_CAL_READY)) { - if (data->error_on_cal_trg_nrdy && trigger) { - LOG_ERR("Trying to start offset comp when not ready"); - return -EIO; - } - - return 0; - } - - if (bma_emul_range_to_shift(data->reg[BMA2x2_RANGE_SELECT_ADDR]) != 0 && - trigger && data->error_on_cal_trg_bad_range) { - LOG_ERR("Trying to start offset comp with range other than 2G"); - return -EIO; - } - - switch (trigger) { - case 1: - target = bma_emul_get_target(emul, BMA_EMUL_AXIS_X); - bma_emul_set_off(emul, BMA_EMUL_AXIS_X, target - data->acc_x); - break; - case 2: - target = bma_emul_get_target(emul, BMA_EMUL_AXIS_Y); - bma_emul_set_off(emul, BMA_EMUL_AXIS_Y, target - data->acc_y); - break; - case 3: - target = bma_emul_get_target(emul, BMA_EMUL_AXIS_Z); - bma_emul_set_off(emul, BMA_EMUL_AXIS_Z, target - data->acc_z); - break; - } - - return 0; -} - -/** - * @brief Handle I2C write message. It is checked if accessed register isn't RO - * and reserved bits are set to 0. Write set value of reg field of bma - * emulator data ignoring reserved bits and write only bits. Some - * commands are handled specialy. - * - * @param emul Pointer to BMA255 emulator - * @param reg Register which is written - * @param bytes Number of bytes in I2C write message - * - * @return 0 on success - * @return -EIO on error - */ -static int bma_emul_handle_write(struct i2c_emul *emul, int reg, int bytes) -{ - struct bma_emul_data *data; - uint8_t val; - int ret; - - data = BMA_DATA_FROM_I2C_EMUL(emul); - - val = data->write_byte; - - if (bytes > 2) { - LOG_ERR("Too long write command"); - return -EIO; - } - - /* This write only selected register for I2C read message */ - if (bytes < 2) { - return 0; - } - - if (reg <= BMA2x2_STAT_FIFO_ADDR || - reg >= BMA2x2_FIFO_DATA_OUTPUT_ADDR) { - if (data->error_on_ro_write) { - LOG_ERR("Writing to reg 0x%x which is RO", reg); - return -EIO; - } - - return 0; - } - - if (data->error_on_rsvd_write && bma_emul_rsvd_mask[reg] & val) { - LOG_ERR("Writing 0x%x to reg 0x%x with rsvd bits mask 0x%x", - val, reg, bma_emul_rsvd_mask[reg]); - return -EIO; - } - - - switch (reg) { - case BMA2x2_RST_ADDR: - if (val == BMA2x2_CMD_SOFT_RESET) { - bma_emul_reset(emul); - } - return 0; - case BMA2x2_INTR_CTRL_ADDR: - if (val & BMA2x2_INTR_CTRL_RST_INT) { - bma_emul_clear_int(emul); - } - /* Don't set write only bit in register */ - val &= ~BMA2x2_INTR_CTRL_RST_INT; - break; - case BMA2x2_EEPROM_CTRL_ADDR: - bma_emul_handle_nvm_write(emul, val); - /* Only programing enable bit is RW */ - val &= BMA2x2_EEPROM_PROG_EN; - val |= data->reg[reg] & ~BMA2x2_EEPROM_PROG_EN; - break; - case BMA2x2_OFFSET_CTRL_ADDR: - ret = bma_emul_handle_off_comp(emul, val); - if (ret) { - return -EIO; - } - /* Only slow compensation bits are RW */ - val &= BMA2x2_OFFSET_CAL_SLOW_X | BMA2x2_OFFSET_CAL_SLOW_Y | - BMA2x2_OFFSET_CAL_SLOW_Z; - val |= data->reg[reg] & ~(BMA2x2_OFFSET_CAL_SLOW_X | - BMA2x2_OFFSET_CAL_SLOW_Y | - BMA2x2_OFFSET_CAL_SLOW_Z); - break; - /* Change internal offset to value set in I2C message */ - case BMA2x2_OFFSET_X_AXIS_ADDR: - data->off_x = bma_emul_nvm_to_off(val); - break; - case BMA2x2_OFFSET_Y_AXIS_ADDR: - data->off_y = bma_emul_nvm_to_off(val); - break; - case BMA2x2_OFFSET_Z_AXIS_ADDR: - data->off_z = bma_emul_nvm_to_off(val); - break; - case BMA2x2_RANGE_SELECT_ADDR: - ret = bma_emul_range_to_shift(val); - if (ret < 0) { - LOG_ERR("Unknown range select value 0x%x", val); - return -EIO; - } - break; - } - - /* Ignore all reserved bits */ - val &= ~bma_emul_rsvd_mask[reg]; - val |= data->reg[reg] & bma_emul_rsvd_mask[reg]; - - data->reg[reg] = val; - - return 0; -} - -/** - * @brief Get set accelerometer value for given register using internal axis - * state @p val. In case of accessing MSB with enabled shadowing, - * check if LSB was accessed first. - * - * @param emul Pointer to BMA255 emulator - * @param lsb_reg LSB register address (BMA2x2_X_AXIS_LSB_ADDR, - * BMA2x2_Y_AXIS_LSB_ADDR, BMA2x2_Z_AXIS_LSB_ADDR) - * @param lsb_read Pointer to variable which represent if last access to this - * accelerometer value was through LSB register - * @param lsb True if now accessing LSB, Flase if now accessing MSB - * @param val Internal value of accessed accelerometer axis - * - * @return 0 on success - * @return -EIO when accessing MSB before LSB with enabled shadowing - */ -static int bma_emul_get_acc_val(struct i2c_emul *emul, int lsb_reg, - bool *lsb_read, bool lsb, int16_t val) -{ - struct bma_emul_data *data; - uint16_t twos_comp_val; - uint8_t new_data; - int msb_reg; - int shift; - - data = BMA_DATA_FROM_I2C_EMUL(emul); - - if (lsb) { - *lsb_read = 1; - } else if (!(data->reg[BMA2x2_DATA_CTRL_ADDR] & - BMA2x2_DATA_SHADOW_DIS)) { - /* - * If shadowing is enabled, error on first accessing MSB and - * LSB wasn't accessed before, then return error. - */ - if (data->error_on_msb_first && !(*lsb_read)) { - return -EIO; - } - *lsb_read = 0; - /* If shadowing is enabled, LSB read should set correct value */ - return 0; - } - - twos_comp_val = bma_emul_val_to_twos_comp(val); - msb_reg = lsb_reg + 1; - shift = bma_emul_range_to_shift(data->reg[BMA2x2_RANGE_SELECT_ADDR]); - - /* Save new data bit from register */ - new_data = data->reg[lsb_reg] & BMA2x2_AXIS_LSB_NEW_DATA; - /* Shift 16 bit value to 12 bit set in range register */ - twos_comp_val >>= shift; - /* Set [3:0] bits in first register */ - data->reg[lsb_reg] = ((twos_comp_val << 4) & 0xf0) | new_data; - /* Set [11:4] bits in second register */ - data->reg[msb_reg] = (twos_comp_val >> 4) & 0xff; - - return 0; -} - -/** Check description in emul_bma255.h */ -int bma_emul_access_reg(struct i2c_emul *emul, int reg, int bytes, bool read) -{ - /* - * Exclude first byte (select register) from total number of bytes - * in I2C write message - */ - if (!read) { - bytes--; - } - - if (reg <= BMA2x2_FIFO_DATA_OUTPUT_ADDR && - reg + bytes >= BMA2x2_FIFO_DATA_OUTPUT_ADDR) { - return BMA2x2_FIFO_DATA_OUTPUT_ADDR; - } - - return reg + bytes; -} - -/** - * @brief Handle I2C read message. Response is obtained from reg field of bma - * emul data. When accessing accelerometer value, register data is first - * computed using internal emulator state. - * - * @param emul Pointer to BMA255 emulator - * @param reg Register address to read - * @param val Pointer where resultat should be stored - * @param bytes Number of bytes in I2C read message - * - * @return 0 on success - * @return -EIO on error - */ -static int bma_emul_handle_read(struct i2c_emul *emul, int reg, uint8_t *val, - int bytes) -{ - struct bma_emul_data *data; - int ret; - - data = BMA_DATA_FROM_I2C_EMUL(emul); - - reg = bma_emul_access_reg(emul, reg, bytes, true /* = read */); - - switch (reg) { - case BMA2x2_X_AXIS_LSB_ADDR: - /* Shouldn't fail for LSB */ - ret = bma_emul_get_acc_val(emul, reg, &data->lsb_x_read, true, - data->acc_x + data->off_x); - break; - case BMA2x2_X_AXIS_MSB_ADDR: - ret = bma_emul_get_acc_val(emul, reg - 1, &data->lsb_x_read, - false, data->acc_x + data->off_x); - if (ret) { - LOG_ERR("MSB X readed before LSB X"); - return -EIO; - } - break; - case BMA2x2_Y_AXIS_LSB_ADDR: - /* Shouldn't fail for LSB */ - ret = bma_emul_get_acc_val(emul, reg, &data->lsb_y_read, true, - data->acc_y + data->off_y); - break; - case BMA2x2_Y_AXIS_MSB_ADDR: - ret = bma_emul_get_acc_val(emul, reg - 1, &data->lsb_y_read, - false, data->acc_y + data->off_y); - if (ret) { - LOG_ERR("MSB Y readed before LSB Y"); - return -EIO; - } - break; - case BMA2x2_Z_AXIS_LSB_ADDR: - /* Shouldn't fail for LSB */ - ret = bma_emul_get_acc_val(emul, reg, &data->lsb_z_read, true, - data->acc_z + data->off_z); - break; - case BMA2x2_Z_AXIS_MSB_ADDR: - ret = bma_emul_get_acc_val(emul, reg - 1, &data->lsb_z_read, - false, data->acc_z + data->off_z); - if (ret) { - LOG_ERR("MSB Z readed before LSB Z"); - return -EIO; - } - break; - } - - *val = data->reg[reg]; - - return 0; -} - -/** - * @brief Handle I2C write message. Saves data that will be stored in register. - * - * @param emul Pointer to BMA emulator - * @param reg Register address that is accessed - * @param val Data to write to the register - * @param bytes Number of bytes already handled in this read message - * - * @return 0 on success - * @return -EIO on error - */ -static int bma_emul_write_byte(struct i2c_emul *emul, int reg, uint8_t val, - int bytes) -{ - struct bma_emul_data *data; - - data = BMA_DATA_FROM_I2C_EMUL(emul); - - data->write_byte = val; - - return 0; -} - -/* Device instantiation */ - -static struct i2c_emul_api bma_emul_api = { - .transfer = i2c_common_emul_transfer, -}; - -/** - * @brief Set up a new BMA255 emulator - * - * This should be called for each BMA255 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 bma_emul_init(const struct emul *emul, - const struct device *parent) -{ - const struct i2c_common_emul_cfg *cfg = emul->cfg; - struct i2c_common_emul_data *data = cfg->data; - int ret; - - data->emul.api = &bma_emul_api; - data->emul.addr = cfg->addr; - data->i2c = parent; - data->cfg = cfg; - i2c_common_emul_init(data); - - ret = i2c_emul_register(parent, emul->dev_label, &data->emul); - - bma_emul_reset(&data->emul); - - return ret; -} - -#define BMA255_EMUL(n) \ - static struct bma_emul_data bma_emul_data_##n = { \ - .nvm_x = DT_INST_PROP(n, nvm_off_x), \ - .nvm_y = DT_INST_PROP(n, nvm_off_y), \ - .nvm_z = DT_INST_PROP(n, nvm_off_z), \ - .nvm_gp0 = DT_INST_PROP(n, nvm_gp0), \ - .nvm_gp1 = DT_INST_PROP(n, nvm_gp1), \ - .acc_x = DT_INST_PROP(n, nvm_acc_x), \ - .acc_y = DT_INST_PROP(n, nvm_acc_y), \ - .acc_z = DT_INST_PROP(n, nvm_acc_z), \ - .error_on_cal_trg_nrdy = DT_INST_PROP(n, \ - error_on_compensation_not_ready), \ - .error_on_ro_write = DT_INST_PROP(n, error_on_ro_write),\ - .error_on_rsvd_write = DT_INST_PROP(n, \ - error_on_reserved_bit_write), \ - .error_on_msb_first = DT_INST_PROP(n, \ - error_on_msb_first_access), \ - .lsb_x_read = 0, \ - .lsb_y_read = 0, \ - .lsb_z_read = 0, \ - .common = { \ - .start_write = NULL, \ - .write_byte = bma_emul_write_byte, \ - .finish_write = bma_emul_handle_write, \ - .start_read = NULL, \ - .read_byte = bma_emul_handle_read, \ - .finish_read = NULL, \ - .access_reg = bma_emul_access_reg, \ - }, \ - }; \ - \ - static const struct i2c_common_emul_cfg bma_emul_cfg_##n = { \ - .i2c_label = DT_INST_BUS_LABEL(n), \ - .dev_label = DT_INST_LABEL(n), \ - .data = &bma_emul_data_##n.common, \ - .addr = DT_INST_REG_ADDR(n), \ - }; \ - EMUL_DEFINE(bma_emul_init, DT_DRV_INST(n), &bma_emul_cfg_##n, \ - &bma_emul_data_##n) - -DT_INST_FOREACH_STATUS_OKAY(BMA255_EMUL) - -#define BMA255_EMUL_CASE(n) \ - case DT_INST_DEP_ORD(n): return &bma_emul_data_##n.common.emul; - -/** Check description in emul_bma255.h */ -struct i2c_emul *bma_emul_get(int ord) -{ - switch (ord) { - DT_INST_FOREACH_STATUS_OKAY(BMA255_EMUL_CASE) - - default: - return NULL; - } -} |