diff options
Diffstat (limited to 'zephyr/emul/emul_tcs3400.c')
-rw-r--r-- | zephyr/emul/emul_tcs3400.c | 650 |
1 files changed, 0 insertions, 650 deletions
diff --git a/zephyr/emul/emul_tcs3400.c b/zephyr/emul/emul_tcs3400.c deleted file mode 100644 index 0fc432e9ff..0000000000 --- a/zephyr/emul/emul_tcs3400.c +++ /dev/null @@ -1,650 +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_tcs3400 - -#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL -#include <logging/log.h> -LOG_MODULE_REGISTER(emul_tcs); - -#include <device.h> -#include <emul.h> -#include <drivers/i2c.h> -#include <drivers/i2c_emul.h> - -#include "emul/emul_common_i2c.h" -#include "emul/emul_tcs3400.h" - -#include "driver/als_tcs3400.h" - -#define TCS_DATA_FROM_I2C_EMUL(_emul) \ - CONTAINER_OF(CONTAINER_OF(_emul, struct i2c_common_emul_data, emul), \ - struct tcs_emul_data, common) - -/** Run-time data used by the emulator */ -struct tcs_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 emulated TCS3400 registers */ - uint8_t reg[TCS_EMUL_REG_COUNT]; - /** Return IR value instead of clear */ - bool ir_select; - /** Internal values of light sensor registers */ - int red; - int green; - int blue; - int clear; - int ir; - - /** ID registers value */ - uint8_t revision; - uint8_t id; - - /** 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 data registers - */ - bool lsb_r_read; - bool lsb_g_read; - bool lsb_b_read; - bool lsb_c_ir_read; -}; - -/** Check description in emul_tcs3400.h */ -void tcs_emul_set_reg(struct i2c_emul *emul, int reg, uint8_t val) -{ - struct tcs_emul_data *data; - - if (reg < TCS_EMUL_FIRST_REG || reg > TCS_EMUL_LAST_REG) { - return; - } - - reg -= TCS_EMUL_FIRST_REG; - data = TCS_DATA_FROM_I2C_EMUL(emul); - data->reg[reg] = val; -} - -/** Check description in emul_tcs3400.h */ -uint8_t tcs_emul_get_reg(struct i2c_emul *emul, int reg) -{ - struct tcs_emul_data *data; - - if (reg < TCS_EMUL_FIRST_REG || reg > TCS_EMUL_LAST_REG) { - return 0; - } - - data = TCS_DATA_FROM_I2C_EMUL(emul); - reg -= TCS_EMUL_FIRST_REG; - - return data->reg[reg]; -} - -/** Check description in emul_tcs3400.h */ -int tcs_emul_get_val(struct i2c_emul *emul, enum tcs_emul_axis axis) -{ - struct tcs_emul_data *data; - - data = TCS_DATA_FROM_I2C_EMUL(emul); - - switch (axis) { - case TCS_EMUL_R: - return data->red; - case TCS_EMUL_G: - return data->green; - case TCS_EMUL_B: - return data->blue; - case TCS_EMUL_C: - return data->clear; - case TCS_EMUL_IR: - return data->ir; - } - - return 0; -} - -/** Check description in emul_tcs3400.h */ -void tcs_emul_set_val(struct i2c_emul *emul, enum tcs_emul_axis axis, int val) -{ - struct tcs_emul_data *data; - - data = TCS_DATA_FROM_I2C_EMUL(emul); - - switch (axis) { - case TCS_EMUL_R: - data->red = val; - break; - case TCS_EMUL_G: - data->green = val; - break; - case TCS_EMUL_B: - data->blue = val; - break; - case TCS_EMUL_C: - data->clear = val; - break; - case TCS_EMUL_IR: - data->ir = val; - break; - } -} - -/** Check description in emul_tcs3400.h */ -void tcs_emul_set_err_on_ro_write(struct i2c_emul *emul, bool set) -{ - struct tcs_emul_data *data; - - data = TCS_DATA_FROM_I2C_EMUL(emul); - data->error_on_ro_write = set; -} - -/** Check description in emul_tcs3400.h */ -void tcs_emul_set_err_on_rsvd_write(struct i2c_emul *emul, bool set) -{ - struct tcs_emul_data *data; - - data = TCS_DATA_FROM_I2C_EMUL(emul); - data->error_on_rsvd_write = set; -} - -/** Check description in emul_tcs3400.h */ -void tcs_emul_set_err_on_msb_first(struct i2c_emul *emul, bool set) -{ - struct tcs_emul_data *data; - - data = TCS_DATA_FROM_I2C_EMUL(emul); - data->error_on_msb_first = set; -} - -/** Mask reserved bits in registers of TCS3400 */ -static const uint8_t tcs_emul_rsvd_mask[] = { - [TCS_I2C_ENABLE - TCS_EMUL_FIRST_REG] = 0xa4, - [TCS_I2C_ATIME - TCS_EMUL_FIRST_REG] = 0x00, - [0x2] = 0xff, /* Reserved */ - [TCS_I2C_WTIME - TCS_EMUL_FIRST_REG] = 0x00, - [TCS_I2C_AILTL - TCS_EMUL_FIRST_REG] = 0x00, - [TCS_I2C_AILTH - TCS_EMUL_FIRST_REG] = 0x00, - [TCS_I2C_AIHTL - TCS_EMUL_FIRST_REG] = 0x00, - [TCS_I2C_AIHTH - TCS_EMUL_FIRST_REG] = 0x00, - [0x8 ... 0xb] = 0xff, /* Reserved */ - [TCS_I2C_PERS - TCS_EMUL_FIRST_REG] = 0xf0, - [TCS_I2C_CONFIG - TCS_EMUL_FIRST_REG] = 0x81, - [0xe] = 0xff, /* Reserved */ - [TCS_I2C_CONTROL - TCS_EMUL_FIRST_REG] = 0xfc, - [TCS_I2C_AUX - TCS_EMUL_FIRST_REG] = 0xdf, - [TCS_I2C_REVID - TCS_EMUL_FIRST_REG] = 0xf0, - [TCS_I2C_ID - TCS_EMUL_FIRST_REG] = 0x00, - [TCS_I2C_STATUS - TCS_EMUL_FIRST_REG] = 0x6e, - [TCS_I2C_CDATAL - TCS_EMUL_FIRST_REG] = 0x00, - [TCS_I2C_CDATAH - TCS_EMUL_FIRST_REG] = 0x00, - [TCS_I2C_RDATAL - TCS_EMUL_FIRST_REG] = 0x00, - [TCS_I2C_RDATAH - TCS_EMUL_FIRST_REG] = 0x00, - [TCS_I2C_GDATAL - TCS_EMUL_FIRST_REG] = 0x00, - [TCS_I2C_GDATAH - TCS_EMUL_FIRST_REG] = 0x00, - [TCS_I2C_BDATAL - TCS_EMUL_FIRST_REG] = 0x00, - [TCS_I2C_BDATAH - TCS_EMUL_FIRST_REG] = 0x00, -}; - -/** - * @brief Reset registers to default values - * - * @param emul Pointer to TCS3400 emulator - */ -static void tcs_emul_reset(struct i2c_emul *emul) -{ - struct tcs_emul_data *data; - - data = TCS_DATA_FROM_I2C_EMUL(emul); - - data->reg[TCS_I2C_ENABLE - TCS_EMUL_FIRST_REG] = 0x00; - data->reg[TCS_I2C_ATIME - TCS_EMUL_FIRST_REG] = 0xff; - data->reg[TCS_I2C_WTIME - TCS_EMUL_FIRST_REG] = 0xff; - data->reg[TCS_I2C_AILTL - TCS_EMUL_FIRST_REG] = 0x00; - data->reg[TCS_I2C_AILTH - TCS_EMUL_FIRST_REG] = 0x00; - data->reg[TCS_I2C_AIHTL - TCS_EMUL_FIRST_REG] = 0x00; - data->reg[TCS_I2C_AIHTH - TCS_EMUL_FIRST_REG] = 0x00; - data->reg[TCS_I2C_PERS - TCS_EMUL_FIRST_REG] = 0x00; - data->reg[TCS_I2C_CONFIG - TCS_EMUL_FIRST_REG] = 0x40; - data->reg[TCS_I2C_CONTROL - TCS_EMUL_FIRST_REG] = 0x00; - data->reg[TCS_I2C_AUX - TCS_EMUL_FIRST_REG] = 0x00; - data->reg[TCS_I2C_REVID - TCS_EMUL_FIRST_REG] = data->revision; - data->reg[TCS_I2C_ID - TCS_EMUL_FIRST_REG] = data->id; - data->reg[TCS_I2C_STATUS - TCS_EMUL_FIRST_REG] = 0x00; - data->reg[TCS_I2C_CDATAL - TCS_EMUL_FIRST_REG] = 0x00; - data->reg[TCS_I2C_CDATAH - TCS_EMUL_FIRST_REG] = 0x00; - data->reg[TCS_I2C_RDATAL - TCS_EMUL_FIRST_REG] = 0x00; - data->reg[TCS_I2C_RDATAH - TCS_EMUL_FIRST_REG] = 0x00; - data->reg[TCS_I2C_GDATAL - TCS_EMUL_FIRST_REG] = 0x00; - data->reg[TCS_I2C_GDATAH - TCS_EMUL_FIRST_REG] = 0x00; - data->reg[TCS_I2C_BDATAL - TCS_EMUL_FIRST_REG] = 0x00; - data->reg[TCS_I2C_BDATAH - TCS_EMUL_FIRST_REG] = 0x00; - - data->ir_select = false; -} - -/** - * @brief Convert gain in format of CONTROL register to multiplyer - * - * @param control Value of CONTROL register - * - * @return gain by which messured value should be multiplied - */ -static int tcs_emul_get_gain(uint8_t control) -{ - switch (control & TCS_I2C_CONTROL_MASK) { - case 0: - return 1; - case 1: - return 4; - case 2: - return 16; - case 3: - return 64; - default: - return -1; - } -} - -/** - * @brief Convert number of cycles in format of ATIME register - * - * @param atime Value of ATIME register - * - * @return cycles count that should be used to obtain light sensor values - */ -static int tcs_emul_get_cycles(uint8_t atime) -{ - return TCS_EMUL_MAX_CYCLES - (int)atime; -} - -/** - * @brief Clear all interrupt registers - * - * @param emul Pointer to TCS3400 emulator - */ -static void tcs_emul_clear_int(struct i2c_emul *emul) -{ - struct tcs_emul_data *data; - - data = TCS_DATA_FROM_I2C_EMUL(emul); - - data->reg[TCS_I2C_STATUS - TCS_EMUL_FIRST_REG] = 0x00; -} - -/** - * @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 TCS - * emulator data ignoring reserved bits and write only bits. Some - * commands are handled specialy. - * - * @param emul Pointer to TCS3400 emulator - * @param reg Register which is written - * @param bytes Number of bytes received in this write message - * - * @return 0 on success - * @return -EIO on error - */ -static int tcs_emul_handle_write(struct i2c_emul *emul, int reg, int bytes) -{ - struct tcs_emul_data *data; - uint8_t val; - - data = TCS_DATA_FROM_I2C_EMUL(emul); - - /* This write only selected register for I2C read message */ - if (bytes < 2) { - return 0; - } - - val = data->write_byte; - - /* Register is in data->reg */ - if (reg >= TCS_EMUL_FIRST_REG && reg <= TCS_EMUL_LAST_REG) { - if (reg >= TCS_I2C_REVID && reg <= TCS_I2C_BDATAH) { - if (data->error_on_ro_write) { - LOG_ERR("Writing to reg 0x%x which is RO", reg); - return -EIO; - } - - return 0; - } - - if (reg == TCS_I2C_CONFIG && data->error_on_rsvd_write && - !(BIT(6) & val)) { - LOG_ERR("CONFIG reg bit 6 is write as 6 (writing 0x%x)", - val); - return -EIO; - } - - reg -= TCS_EMUL_FIRST_REG; - if (data->error_on_rsvd_write && - tcs_emul_rsvd_mask[reg] & val) { - LOG_ERR("Writing 0x%x to reg 0x%x with rsvd mask 0x%x", - val, reg + TCS_EMUL_FIRST_REG, - tcs_emul_rsvd_mask[reg]); - return -EIO; - } - - /* Ignore all reserved bits */ - val &= ~tcs_emul_rsvd_mask[reg]; - val |= data->reg[reg] & tcs_emul_rsvd_mask[reg]; - - data->reg[reg] = val; - - return 0; - } - - switch (reg) { - case TCS_I2C_IR: - if (data->error_on_rsvd_write && 0x7f & val) { - LOG_ERR("Writing 0x%x to reg 0x%x with rsvd mask 0x7f", - val, reg); - return -EIO; - } - data->ir_select = !!(val & BIT(7)); - break; - case TCS_I2C_IFORCE: - /* Interrupt generate is not supported */ - break; - case TCS_I2C_CICLEAR: - case TCS_I2C_AICLEAR: - tcs_emul_clear_int(emul); - break; - default: - /* Assume that other registers are RO */ - if (data->error_on_ro_write) { - LOG_ERR("Writing to reg 0x%x which is RO (unknown)", - reg); - return -EIO; - } - } - - return 0; -} - -/** - * @brief Get set light sensor value for given register using internal - * state @p val. In case of accessing MSB check if LSB was accessed first - * - * @param emul Pointer to TCS3400 emulator - * @param reg LSB or MSB register address. LSB has to be aligned to 2 - * @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 light sensor - * - * @return 0 on success - * @return -EIO when accessing MSB before LSB - */ -static int tcs_emul_get_reg_val(struct i2c_emul *emul, int reg, - bool *lsb_read, bool lsb, unsigned int val) -{ - struct tcs_emul_data *data; - uint64_t reg_val; - int msb_reg; - int lsb_reg; - int cycles; - int gain; - - data = TCS_DATA_FROM_I2C_EMUL(emul); - - if (lsb) { - *lsb_read = 1; - } else { - /* - * If error on first accessing MSB is set and LSB wasn't - * accessed before, then return error. - */ - if (data->error_on_msb_first && !(*lsb_read)) { - return -EIO; - } - *lsb_read = 0; - /* LSB read should set correct value */ - return 0; - } - - lsb_reg = (reg - TCS_EMUL_FIRST_REG) & ~(0x1); - msb_reg = (reg - TCS_EMUL_FIRST_REG) | 0x1; - - gain = tcs_emul_get_gain(data->reg[TCS_I2C_CONTROL - - TCS_EMUL_FIRST_REG]); - cycles = tcs_emul_get_cycles(data->reg[TCS_I2C_ATIME - - TCS_EMUL_FIRST_REG]); - /* - * Internal value is with 256 cycles and x64 gain, so divide it to get - * registers value - */ - reg_val = (uint64_t)val * cycles * gain / TCS_EMUL_MAX_CYCLES / - TCS_EMUL_MAX_GAIN; - - if (reg_val > UINT16_MAX) { - reg_val = UINT16_MAX; - } - - data->reg[lsb_reg] = reg_val & 0xff; - data->reg[msb_reg] = (reg_val >> 8) & 0xff; - - return 0; -} - -/** - * @brief Handle I2C read message. Response is obtained from reg field of TCS - * emul data. When accessing light sensor value, register data is first - * computed using internal emulator state. - * - * @param emul Pointer to TCS3400 emulator - * @param reg First register address that is accessed in this read message - * @param buf Pointer where result should be stored - * @param bytes Number of bytes already handled in this read message - * - * @return 0 on success - * @return -EIO on error - */ -static int tcs_emul_handle_read(struct i2c_emul *emul, int reg, uint8_t *buf, - int bytes) -{ - struct tcs_emul_data *data; - unsigned int c_ir; - int ret; - - data = TCS_DATA_FROM_I2C_EMUL(emul); - - reg += bytes; - - if ((reg < TCS_EMUL_FIRST_REG || reg > TCS_EMUL_LAST_REG) && - reg != TCS_I2C_IR) { - LOG_ERR("Accessing register 0x%x which cannot be read", reg); - return -EIO; - } - - switch (reg) { - case TCS_I2C_CDATAL: - /* Shouldn't fail for LSB */ - c_ir = data->ir_select ? data->ir : data->clear; - ret = tcs_emul_get_reg_val(emul, reg, &data->lsb_c_ir_read, - true, c_ir); - break; - case TCS_I2C_CDATAH: - c_ir = data->ir_select ? data->ir : data->clear; - ret = tcs_emul_get_reg_val(emul, reg, &data->lsb_c_ir_read, - false, c_ir); - if (ret) { - LOG_ERR("MSB C read before LSB C"); - return -EIO; - } - break; - case TCS_I2C_RDATAL: - /* Shouldn't fail for LSB */ - ret = tcs_emul_get_reg_val(emul, reg, &data->lsb_r_read, - true, data->red); - break; - case TCS_I2C_RDATAH: - ret = tcs_emul_get_reg_val(emul, reg, &data->lsb_r_read, - false, data->red); - if (ret) { - LOG_ERR("MSB R read before LSB R"); - return -EIO; - } - break; - case TCS_I2C_GDATAL: - /* Shouldn't fail for LSB */ - ret = tcs_emul_get_reg_val(emul, reg, &data->lsb_g_read, - true, data->green); - break; - case TCS_I2C_GDATAH: - ret = tcs_emul_get_reg_val(emul, reg, &data->lsb_g_read, - false, data->green); - if (ret) { - LOG_ERR("MSB G read before LSB G"); - return -EIO; - } - break; - case TCS_I2C_BDATAL: - /* Shouldn't fail for LSB */ - ret = tcs_emul_get_reg_val(emul, reg, &data->lsb_b_read, - true, data->blue); - break; - case TCS_I2C_BDATAH: - ret = tcs_emul_get_reg_val(emul, reg, &data->lsb_b_read, - false, data->blue); - if (ret) { - LOG_ERR("MSB B read before LSB B"); - return -EIO; - } - break; - case TCS_I2C_IR: - *buf = data->ir_select ? BIT(7) : 0; - - return 0; - } - - *buf = data->reg[reg - TCS_EMUL_FIRST_REG]; - - return 0; -} - -/** - * @brief Handle I2C write message. Check if message is not too long and saves - * data that will be stored in register - * - * @param emul Pointer to TCS3400 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 tcs_emul_write_byte(struct i2c_emul *emul, int reg, uint8_t val, - int bytes) -{ - struct tcs_emul_data *data; - - data = TCS_DATA_FROM_I2C_EMUL(emul); - - if (bytes > 1) { - LOG_ERR("Too long write command"); - return -EIO; - } - - data->write_byte = val; - - return 0; -} - -/* Device instantiation */ - -static struct i2c_emul_api tcs_emul_api = { - .transfer = i2c_common_emul_transfer, -}; - -/** - * @brief Set up a new TCS3400 emulator - * - * This should be called for each TCS3400 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 tcs_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 = &tcs_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); - - tcs_emul_reset(&data->emul); - - return ret; -} - -#define TCS3400_EMUL(n) \ - static struct tcs_emul_data tcs_emul_data_##n = { \ - .revision = DT_INST_PROP(n, revision), \ - .id = DT_STRING_TOKEN(DT_DRV_INST(n), device_id), \ - .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_c_ir_read = 0, \ - .lsb_r_read = 0, \ - .lsb_g_read = 0, \ - .lsb_b_read = 0, \ - .common = { \ - .start_write = NULL, \ - .write_byte = tcs_emul_write_byte, \ - .finish_write = tcs_emul_handle_write, \ - .start_read = NULL, \ - .read_byte = tcs_emul_handle_read, \ - .finish_read = NULL, \ - .access_reg = NULL, \ - }, \ - }; \ - \ - static const struct i2c_common_emul_cfg tcs_emul_cfg_##n = { \ - .i2c_label = DT_INST_BUS_LABEL(n), \ - .dev_label = DT_INST_LABEL(n), \ - .data = &tcs_emul_data_##n.common, \ - .addr = DT_INST_REG_ADDR(n), \ - }; \ - EMUL_DEFINE(tcs_emul_init, DT_DRV_INST(n), &tcs_emul_cfg_##n, \ - &tcs_emul_data_##n) - -DT_INST_FOREACH_STATUS_OKAY(TCS3400_EMUL) - -#define TCS3400_EMUL_CASE(n) \ - case DT_INST_DEP_ORD(n): return &tcs_emul_data_##n.common.emul; - -/** Check description in emul_tcs3400.h */ -struct i2c_emul *tcs_emul_get(int ord) -{ - switch (ord) { - DT_INST_FOREACH_STATUS_OKAY(TCS3400_EMUL_CASE) - - default: - return NULL; - } -} |