diff options
-rw-r--r-- | baseboard/intelrvp/adlrvp.c | 3 | ||||
-rw-r--r-- | board/boldar/board.c | 3 | ||||
-rw-r--r-- | board/copano/board.c | 3 | ||||
-rw-r--r-- | board/drobit/board.c | 3 | ||||
-rw-r--r-- | board/halvor/board.c | 2 | ||||
-rw-r--r-- | board/lingcod/board.c | 2 | ||||
-rw-r--r-- | board/malefor/board.c | 2 | ||||
-rw-r--r-- | board/terrador/board.c | 3 | ||||
-rw-r--r-- | board/tglrvpu_ite/board.c | 2 | ||||
-rw-r--r-- | board/todor/board.c | 3 | ||||
-rw-r--r-- | board/trondo/board.c | 3 | ||||
-rw-r--r-- | board/volteer/board.c | 3 | ||||
-rw-r--r-- | board/voxel/board.c | 3 | ||||
-rw-r--r-- | driver/retimer/bb_retimer.c | 2 | ||||
-rw-r--r-- | include/driver/retimer/bb_retimer.h (renamed from driver/retimer/bb_retimer.h) | 5 | ||||
-rw-r--r-- | zephyr/dts/bindings/emul/cros,bb-retimer-emul.yaml | 30 | ||||
-rw-r--r-- | zephyr/emul/CMakeLists.txt | 1 | ||||
-rw-r--r-- | zephyr/emul/Kconfig | 9 | ||||
-rw-r--r-- | zephyr/emul/emul_bb_retimer.c | 549 | ||||
-rw-r--r-- | zephyr/include/emul/emul_bb_retimer.h | 188 |
20 files changed, 797 insertions, 22 deletions
diff --git a/baseboard/intelrvp/adlrvp.c b/baseboard/intelrvp/adlrvp.c index ad7066fe5d..3bc70ac581 100644 --- a/baseboard/intelrvp/adlrvp.c +++ b/baseboard/intelrvp/adlrvp.c @@ -5,9 +5,9 @@ /* Intel ADLRVP board-specific common configuration */ -#include "bb_retimer.h" #include "charger.h" #include "common.h" +#include "driver/retimer/bb_retimer_public.h" #include "hooks.h" #include "ioexpander.h" #include "isl9241.h" @@ -16,6 +16,7 @@ #include "sn5s330.h" #include "system.h" #include "task.h" +#include "usb_mux.h" #include "usbc_ppc.h" #include "util.h" diff --git a/board/boldar/board.c b/board/boldar/board.c index 59b7166123..abc3fecfe0 100644 --- a/board/boldar/board.c +++ b/board/boldar/board.c @@ -4,7 +4,6 @@ */ /* Volteer board-specific configuration */ -#include "bb_retimer.h" #include "button.h" #include "common.h" #include "accelgyro.h" @@ -15,7 +14,7 @@ #include "driver/bc12/pi3usb9201.h" #include "driver/ppc/sn5s330.h" #include "driver/ppc/syv682x.h" -#include "driver/retimer/bb_retimer.h" +#include "driver/retimer/bb_retimer_public.h" #include "driver/sync.h" #include "driver/tcpm/ps8xxx.h" #include "driver/tcpm/rt1715.h" diff --git a/board/copano/board.c b/board/copano/board.c index 23395b0a61..6992731208 100644 --- a/board/copano/board.c +++ b/board/copano/board.c @@ -4,7 +4,6 @@ */ /* Volteer board-specific configuration */ -#include "bb_retimer.h" #include "button.h" #include "common.h" #include "accelgyro.h" @@ -15,7 +14,7 @@ #include "driver/ppc/syv682x.h" #include "driver/tcpm/tcpci.h" #include "driver/tcpm/rt1715.h" -#include "driver/retimer/bb_retimer.h" +#include "driver/retimer/bb_retimer_public.h" #include "driver/sync.h" #include "extpower.h" #include "fan.h" diff --git a/board/drobit/board.c b/board/drobit/board.c index 3fb336976c..56d8f3f4a7 100644 --- a/board/drobit/board.c +++ b/board/drobit/board.c @@ -4,7 +4,6 @@ */ /* Volteer board-specific configuration */ -#include "bb_retimer.h" #include "button.h" #include "common.h" #include "accelgyro.h" @@ -12,7 +11,7 @@ #include "charge_state_v2.h" #include "driver/bc12/pi3usb9201.h" #include "driver/ppc/syv682x.h" -#include "driver/retimer/bb_retimer.h" +#include "driver/retimer/bb_retimer_public.h" #include "driver/sync.h" #include "driver/tcpm/rt1715.h" #include "driver/tcpm/tcpci.h" diff --git a/board/halvor/board.c b/board/halvor/board.c index 078d0520f6..31b68906d4 100644 --- a/board/halvor/board.c +++ b/board/halvor/board.c @@ -6,7 +6,6 @@ /* Volteer board-specific configuration */ #include "accelgyro.h" #include "assert.h" -#include "bb_retimer.h" #include "button.h" #include "common.h" #include "cbi_ec_fw_config.h" @@ -14,6 +13,7 @@ #include "driver/als_tcs3400.h" #include "driver/bc12/pi3usb9201.h" #include "driver/ppc/syv682x.h" +#include "driver/retimer/bb_retimer_public.h" #include "driver/sync.h" #include "driver/tcpm/tcpci.h" #include "driver/tcpm/tusb422.h" diff --git a/board/lingcod/board.c b/board/lingcod/board.c index feeed33119..5b810c9795 100644 --- a/board/lingcod/board.c +++ b/board/lingcod/board.c @@ -4,7 +4,6 @@ */ /* Malefor board-specific configuration */ -#include "bb_retimer.h" #include "button.h" #include "cbi_ec_fw_config.h" #include "common.h" @@ -15,6 +14,7 @@ #include "driver/ppc/syv682x.h" #include "driver/tcpm/tcpci.h" #include "driver/tcpm/tusb422.h" +#include "driver/retimer/bb_retimer_public.h" #include "driver/sync.h" #include "extpower.h" #include "fan.h" diff --git a/board/malefor/board.c b/board/malefor/board.c index 2dd92fabec..c584969b81 100644 --- a/board/malefor/board.c +++ b/board/malefor/board.c @@ -4,7 +4,6 @@ */ /* Malefor board-specific configuration */ -#include "bb_retimer.h" #include "button.h" #include "cbi_ec_fw_config.h" #include "common.h" @@ -13,6 +12,7 @@ #include "driver/bc12/pi3usb9201.h" #include "driver/ppc/sn5s330.h" #include "driver/ppc/syv682x.h" +#include "driver/retimer/bb_retimer_public.h" #include "driver/sync.h" #include "driver/tcpm/ps8xxx.h" #include "driver/tcpm/tcpci.h" diff --git a/board/terrador/board.c b/board/terrador/board.c index e13b376c3f..a0ea2e6918 100644 --- a/board/terrador/board.c +++ b/board/terrador/board.c @@ -4,7 +4,6 @@ */ /* Volteer board-specific configuration */ -#include "bb_retimer.h" #include "button.h" #include "common.h" #include "accelgyro.h" @@ -16,7 +15,7 @@ #include "driver/ppc/syv682x.h" #include "driver/tcpm/tcpci.h" #include "driver/tcpm/tusb422.h" -#include "driver/retimer/bb_retimer.h" +#include "driver/retimer/bb_retimer_public.h" #include "driver/sync.h" #include "extpower.h" #include "fan.h" diff --git a/board/tglrvpu_ite/board.c b/board/tglrvpu_ite/board.c index c81e66b78f..def69448ad 100644 --- a/board/tglrvpu_ite/board.c +++ b/board/tglrvpu_ite/board.c @@ -5,10 +5,10 @@ /* Intel TGL-U-RVP-ITE board-specific configuration */ -#include "bb_retimer.h" #include "button.h" #include "charger.h" #include "driver/charger/isl9241.h" +#include "driver/retimer/bb_retimer_public.h" #include "extpower.h" #include "i2c.h" #include "intc.h" diff --git a/board/todor/board.c b/board/todor/board.c index 06c030dea6..9dbfbfd2ee 100644 --- a/board/todor/board.c +++ b/board/todor/board.c @@ -4,7 +4,6 @@ */ /* Volteer board-specific configuration */ -#include "bb_retimer.h" #include "button.h" #include "common.h" #include "accelgyro.h" @@ -16,7 +15,7 @@ #include "driver/ppc/syv682x.h" #include "driver/tcpm/tcpci.h" #include "driver/tcpm/tusb422.h" -#include "driver/retimer/bb_retimer.h" +#include "driver/retimer/bb_retimer_public.h" #include "driver/sync.h" #include "extpower.h" #include "fan.h" diff --git a/board/trondo/board.c b/board/trondo/board.c index d6e68b35a7..10e346da5c 100644 --- a/board/trondo/board.c +++ b/board/trondo/board.c @@ -4,7 +4,6 @@ */ /* Volteer board-specific configuration */ -#include "bb_retimer.h" #include "button.h" #include "common.h" #include "accelgyro.h" @@ -17,7 +16,7 @@ #include "driver/ppc/syv682x.h" #include "driver/tcpm/tcpci.h" #include "driver/tcpm/tusb422.h" -#include "driver/retimer/bb_retimer.h" +#include "driver/retimer/bb_retimer_public.h" #include "driver/sync.h" #include "extpower.h" #include "fan.h" diff --git a/board/volteer/board.c b/board/volteer/board.c index 1112b4b4f3..8c1be1bf64 100644 --- a/board/volteer/board.c +++ b/board/volteer/board.c @@ -4,7 +4,6 @@ */ /* Volteer board-specific configuration */ -#include "bb_retimer.h" #include "button.h" #include "common.h" #include "accelgyro.h" @@ -12,7 +11,7 @@ #include "driver/accel_bma2x2.h" #include "driver/accelgyro_bmi260.h" #include "driver/als_tcs3400.h" -#include "driver/retimer/bb_retimer.h" +#include "driver/retimer/bb_retimer_public.h" #include "driver/sync.h" #include "driver/tcpm/ps8xxx.h" #include "extpower.h" diff --git a/board/voxel/board.c b/board/voxel/board.c index 2c7f7feac5..c13686d3b4 100644 --- a/board/voxel/board.c +++ b/board/voxel/board.c @@ -4,7 +4,6 @@ */ /* Volteer board-specific configuration */ -#include "bb_retimer.h" #include "button.h" #include "common.h" #include "accelgyro.h" @@ -17,7 +16,7 @@ #include "driver/tcpm/tcpci.h" #include "driver/tcpm/tusb422.h" #include "driver/tcpm/rt1715.h" -#include "driver/retimer/bb_retimer.h" +#include "driver/retimer/bb_retimer_public.h" #include "driver/sync.h" #include "extpower.h" #include "fan.h" diff --git a/driver/retimer/bb_retimer.c b/driver/retimer/bb_retimer.c index 75da6c653d..319c965228 100644 --- a/driver/retimer/bb_retimer.c +++ b/driver/retimer/bb_retimer.c @@ -5,7 +5,7 @@ * Driver for Intel Burnside Bridge - Thunderbolt/USB/DisplayPort Retimer */ -#include "bb_retimer.h" +#include "driver/retimer/bb_retimer.h" #include "chipset.h" #include "common.h" #include "console.h" diff --git a/driver/retimer/bb_retimer.h b/include/driver/retimer/bb_retimer.h index ddbdec0986..35b2352704 100644 --- a/driver/retimer/bb_retimer.h +++ b/include/driver/retimer/bb_retimer.h @@ -43,4 +43,9 @@ #define BB_RETIMER_USB4_TBT_CABLE_SPEED_SUPPORT(x) (((x) & 0x7) << 25) #define BB_RETIMER_TBT_CABLE_GENERATION(x) (((x) & 0x3) << 28) +#define BB_RETIMER_REG_TBT_CONTROL 5 +#define BB_RETIMER_REG_EXT_CONNECTION_MODE 6 + +#define BB_RETIMER_REG_COUNT 7 + #endif /* __CROS_EC_BB_RETIMER_H */ diff --git a/zephyr/dts/bindings/emul/cros,bb-retimer-emul.yaml b/zephyr/dts/bindings/emul/cros,bb-retimer-emul.yaml new file mode 100644 index 0000000000..007a73b17b --- /dev/null +++ b/zephyr/dts/bindings/emul/cros,bb-retimer-emul.yaml @@ -0,0 +1,30 @@ +# 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 BB retimer Emulator + +compatible: "cros,bb-retimer-emul" + +include: base.yaml + +properties: + vendor: + type: string + required: true + enum: + - BB_RETIMER_VENDOR_ID_1 + - BB_RETIMER_VENDOR_ID_2 + description: Vendor ID used by 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-reserved-bit-write: + type: boolean + description: + Flag indicating if error should be generated when reserved bit + is being written. diff --git a/zephyr/emul/CMakeLists.txt b/zephyr/emul/CMakeLists.txt index 500d1e8b2f..bd1d749267 100644 --- a/zephyr/emul/CMakeLists.txt +++ b/zephyr/emul/CMakeLists.txt @@ -10,3 +10,4 @@ zephyr_library_sources_ifdef(CONFIG_EMUL_BMI emul_bmi.c) zephyr_library_sources_ifdef(CONFIG_EMUL_BMI emul_bmi160.c) zephyr_library_sources_ifdef(CONFIG_EMUL_BMI emul_bmi260.c) zephyr_library_sources_ifdef(CONFIG_EMUL_TCS3400 emul_tcs3400.c) +zephyr_library_sources_ifdef(CONFIG_EMUL_BB_RETIMER emul_bb_retimer.c) diff --git a/zephyr/emul/Kconfig b/zephyr/emul/Kconfig index b4461213be..346d419862 100644 --- a/zephyr/emul/Kconfig +++ b/zephyr/emul/Kconfig @@ -27,6 +27,7 @@ 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 @@ -45,3 +46,11 @@ config EMUL_TCS3400 TCS3400 registers support read and write with optional checking of proper access to reserved bits. Emulators API is available in zephyr/include/emul/emul_tcs3400.h + +config EMUL_BB_RETIMER + bool "BB retimer emulator" + help + Enable the BB (Burnside Bridge) retimer emulator. This driver use + emulated I2C bus. It is used to test bb_retimer driver. It supports + reads and writes to all emulator registers. Emulators API is + available in zephyr/include/emul/emul_bb_retimer.h diff --git a/zephyr/emul/emul_bb_retimer.c b/zephyr/emul/emul_bb_retimer.c new file mode 100644 index 0000000000..e016cf709e --- /dev/null +++ b/zephyr/emul/emul_bb_retimer.c @@ -0,0 +1,549 @@ +/* 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 cros_bb_retimer_emul + +#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL +#include <logging/log.h> +LOG_MODULE_REGISTER(emul_bb_retimer); + +#include <device.h> +#include <emul.h> +#include <drivers/i2c.h> +#include <drivers/i2c_emul.h> + +#include "emul/emul_bb_retimer.h" + +#include "driver/retimer/bb_retimer.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 bb_emul_msg_state { + BB_EMUL_NONE_MSG, + BB_EMUL_IN_WRITE, + BB_EMUL_IN_READ +}; + +/** Run-time data used by the emulator */ +struct bb_emul_data { + /** I2C emulator detail */ + struct i2c_emul emul; + /** BB retimer device being emulated */ + const struct device *i2c; + /** Configuration information */ + const struct bb_emul_cfg *cfg; + + /** Current state of all emulated BB retimer registers */ + uint32_t reg[BB_RETIMER_REG_COUNT]; + + /** Vendor ID of emulated device */ + uint32_t vendor_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; + + /** Current state of I2C bus (if emulator is handling message) */ + enum bb_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 dword in ongoing i2c message */ + uint32_t data_dword; + + /** Custom write function called on I2C write opperation */ + bb_emul_write_func write_func; + /** Data passed to custom write function */ + void *write_func_data; + /** Custom read function called on I2C read opperation */ + bb_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; + + /** Mutex used to control access to emulator data */ + struct k_mutex data_mtx; +}; + +/** Static configuration for the emulator */ +struct bb_emul_cfg { + /** Label of the I2C bus this emulator connects to */ + const char *i2c_label; + /** Pointer to run-time data */ + struct bb_emul_data *data; + /** Address of BB retimer on i2c bus */ + uint16_t addr; +}; + +/** Check description in emul_bb_retimer.h */ +int bb_emul_lock_data(struct i2c_emul *emul, k_timeout_t timeout) +{ + struct bb_emul_data *data; + + data = CONTAINER_OF(emul, struct bb_emul_data, emul); + + return k_mutex_lock(&data->data_mtx, timeout); +} + +/** Check description in emul_bb_retimer.h */ +int bb_emul_unlock_data(struct i2c_emul *emul) +{ + struct bb_emul_data *data; + + data = CONTAINER_OF(emul, struct bb_emul_data, emul); + + return k_mutex_unlock(&data->data_mtx); +} + +/** Check description in emul_bb_retimer.h */ +void bb_emul_set_write_func(struct i2c_emul *emul, + bb_emul_write_func func, void *data) +{ + struct bb_emul_data *emul_data; + + emul_data = CONTAINER_OF(emul, struct bb_emul_data, emul); + emul_data->write_func = func; + emul_data->write_func_data = data; +} + +/** Check description in emul_bb_retimer.h */ +void bb_emul_set_read_func(struct i2c_emul *emul, + bb_emul_read_func func, void *data) +{ + struct bb_emul_data *emul_data; + + emul_data = CONTAINER_OF(emul, struct bb_emul_data, emul); + emul_data->read_func = func; + emul_data->read_func_data = data; +} + +/** Check description in emul_bb_retimer.h */ +void bb_emul_set_reg(struct i2c_emul *emul, int reg, uint32_t val) +{ + struct bb_emul_data *data; + + if (reg < 0 || reg > BB_RETIMER_REG_COUNT) { + return; + } + + data = CONTAINER_OF(emul, struct bb_emul_data, emul); + data->reg[reg] = val; +} + +/** Check description in emul_bb_retimer.h */ +uint32_t bb_emul_get_reg(struct i2c_emul *emul, int reg) +{ + struct bb_emul_data *data; + + if (reg < 0 || reg > BB_RETIMER_REG_COUNT) { + return 0; + } + + data = CONTAINER_OF(emul, struct bb_emul_data, emul); + + return data->reg[reg]; +} + +/** Check description in emul_bb_retimer.h */ +void bb_emul_set_read_fail_reg(struct i2c_emul *emul, int reg) +{ + struct bb_emul_data *data; + + data = CONTAINER_OF(emul, struct bb_emul_data, emul); + data->read_fail_reg = reg; +} + +/** Check description in emul_bb_retimer.h */ +void bb_emul_set_write_fail_reg(struct i2c_emul *emul, int reg) +{ + struct bb_emul_data *data; + + data = CONTAINER_OF(emul, struct bb_emul_data, emul); + data->write_fail_reg = reg; +} + +/** Check description in emul_bb_retimer.h */ +void bb_emul_set_err_on_ro_write(struct i2c_emul *emul, bool set) +{ + struct bb_emul_data *data; + + data = CONTAINER_OF(emul, struct bb_emul_data, emul); + data->error_on_ro_write = set; +} + +/** Check description in emul_bb_retimer.h */ +void bb_emul_set_err_on_rsvd_write(struct i2c_emul *emul, bool set) +{ + struct bb_emul_data *data; + + data = CONTAINER_OF(emul, struct bb_emul_data, emul); + data->error_on_rsvd_write = set; +} + +/** Mask reserved bits in each register of BB retimer */ +static const uint32_t bb_emul_rsvd_mask[] = { + [BB_RETIMER_REG_VENDOR_ID] = 0x00000000, + [BB_RETIMER_REG_DEVICE_ID] = 0x00000000, + [0x02] = 0xffffffff, /* Reserved */ + [0x03] = 0xffffffff, /* Reserved */ + [BB_RETIMER_REG_CONNECTION_STATE] = 0xc0201000, + [BB_RETIMER_REG_TBT_CONTROL] = 0xffffdfff, + [0x06] = 0xffffffff, /* Reserved */ + [BB_RETIMER_REG_EXT_CONNECTION_MODE] = 0x08007f00, +}; + +/** + * @brief Reset registers to default values + * + * @param emul Pointer to BB retimer emulator + */ +static void bb_emul_reset(struct i2c_emul *emul) +{ + struct bb_emul_data *data; + + data = CONTAINER_OF(emul, struct bb_emul_data, emul); + + data->reg[BB_RETIMER_REG_VENDOR_ID] = data->vendor_id; + data->reg[BB_RETIMER_REG_DEVICE_ID] = BB_RETIMER_DEVICE_ID; + data->reg[0x02] = 0x00; /* Reserved */ + data->reg[0x03] = 0x00; /* Reserved */ + data->reg[BB_RETIMER_REG_CONNECTION_STATE] = 0x00; + data->reg[BB_RETIMER_REG_TBT_CONTROL] = 0x00; + data->reg[0x06] = 0x00; /* Reserved */ + data->reg[BB_RETIMER_REG_EXT_CONNECTION_MODE] = 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 BB + * retimer emulator data ignoring reserved bits and write only bits. Some + * commands are handled specialy. Before any handling, custom function + * is called if provided. + * + * @param emul Pointer to BB retimer emulator + * @param reg Register which is written + * @param val Value being written to @p reg + * @param msg_len Length of handled I2C message + * + * @return 0 on success + * @return -EIO on error + */ +static int bb_emul_handle_write(struct i2c_emul *emul, int reg, uint32_t val, + int msg_len) +{ + struct bb_emul_data *data; + int ret; + + data = CONTAINER_OF(emul, struct bb_emul_data, emul); + + /* + * BB retimer ignores data bytes above 4 and use zeros if there is less + * then 4 data bytes. Emulator prints warning in that case. + */ + if (msg_len != 6) { + LOG_WRN("Got %d bytes of WR data, expected 4", msg_len - 2); + } + + if (data->write_func) { + ret = data->write_func(emul, reg, val, data->write_func_data); + if (ret < 0) { + return -EIO; + } else if (ret == 0) { + return 0; + } + } + + if (data->write_fail_reg == reg || + data->write_fail_reg == BB_EMUL_FAIL_ALL_REG) { + return -EIO; + } + + if (reg <= BB_RETIMER_REG_DEVICE_ID || + reg >= BB_RETIMER_REG_COUNT || + reg == BB_RETIMER_REG_TBT_CONTROL) { + 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 && bb_emul_rsvd_mask[reg] & val) { + LOG_ERR("Writing 0x%x to reg 0x%x with rsvd bits mask 0x%x", + val, reg, bb_emul_rsvd_mask[reg]); + return -EIO; + } + + /* Ignore all reserved bits */ + val &= ~bb_emul_rsvd_mask[reg]; + val |= data->reg[reg] & bb_emul_rsvd_mask[reg]; + + data->reg[reg] = val; + + return 0; +} + +/** + * @brief Handle I2C read message. Response is obtained from reg field of bb + * emul data. When accessing accelerometer value, register data is first + * computed using internal emulator state. Before default handler, custom + * user read function is called if provided. + * + * @param emul Pointer to BB retimer emulator + * @param reg Register address to read + * @param buf Pointer where result should be stored + * + * @return 0 on success + * @return -EIO on error + */ +static int bb_emul_handle_read(struct i2c_emul *emul, int reg, uint32_t *buf) +{ + struct bb_emul_data *data; + int ret; + + data = CONTAINER_OF(emul, struct bb_emul_data, emul); + + if (data->read_func) { + ret = data->read_func(emul, reg, 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; + } + } + + if (data->read_fail_reg == reg || + data->read_fail_reg == BB_EMUL_FAIL_ALL_REG) { + return -EIO; + } + + if (reg >= BB_RETIMER_REG_COUNT) { + LOG_ERR("Read unknown register 0x%x", reg); + + return -EIO; + } + + *buf = data->reg[reg]; + + return 0; +} + +/** + * @biref Emulate an I2C transfer to a BB retimer + * + * 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 bb_emul_transfer(struct i2c_emul *emul, struct i2c_msg *msgs, + int num_msgs, int addr) +{ + const struct bb_emul_cfg *cfg; + struct bb_emul_data *data; + int ret, i; + bool read; + + data = CONTAINER_OF(emul, struct bb_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 BB_EMUL_NONE_MSG: + data->data_dword = 0; + data->msg_byte = 0; + break; + case BB_EMUL_IN_WRITE: + if (read) { + /* Finish write command */ + if (data->msg_byte >= 2) { + k_mutex_lock(&data->data_mtx, + K_FOREVER); + ret = bb_emul_handle_write(emul, + data->cur_reg, + data->data_dword, + data->msg_byte); + k_mutex_unlock(&data->data_mtx); + if (ret) { + return -EIO; + } + } + data->data_dword = 0; + data->msg_byte = 0; + } + break; + case BB_EMUL_IN_READ: + if (!read) { + data->data_dword = 0; + data->msg_byte = 0; + } + break; + } + data->msg_state = read ? BB_EMUL_IN_READ : BB_EMUL_IN_WRITE; + + if (msgs->flags & I2C_MSG_STOP) { + data->msg_state = BB_EMUL_NONE_MSG; + } + + if (!read) { + /* Dispatch wrtie command */ + for (i = 0; i < msgs->len; i++) { + switch (data->msg_byte) { + case 0: + data->cur_reg = msgs->buf[i]; + break; + case 1: + /* + * BB retimer ignores size, but it + * should be 4, so emulator check this. + */ + if (msgs->buf[i] != 4) { + LOG_WRN("Invalid write size"); + } + break; + default: + data->data_dword |= + (msgs->buf[i] & 0xff) << + (8 * (data->msg_byte - 2)); + } + data->msg_byte++; + } + + /* Execute write command */ + if (msgs->flags & I2C_MSG_STOP && data->msg_byte >= 2) { + k_mutex_lock(&data->data_mtx, K_FOREVER); + ret = bb_emul_handle_write(emul, data->cur_reg, + data->data_dword, + data->msg_byte); + k_mutex_unlock(&data->data_mtx); + if (ret) { + return -EIO; + } + } + } else { + /* Prepare response */ + if (data->msg_byte == 0) { + k_mutex_lock(&data->data_mtx, K_FOREVER); + ret = bb_emul_handle_read(emul, data->cur_reg, + &data->data_dword); + k_mutex_unlock(&data->data_mtx); + if (ret) { + return -EIO; + } + } + + for (i = 0; i < msgs->len; i++) { + msgs->buf[i] = data->data_dword & 0xff; + data->data_dword >>= 8; + + data->msg_byte++; + } + } + } + + return 0; +} + +/* Device instantiation */ + +static struct i2c_emul_api bb_emul_api = { + .transfer = bb_emul_transfer, +}; + +/** + * @brief Set up a new BB retimer emulator + * + * This should be called for each BB retimer 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 bb_emul_init(const struct emul *emul, + const struct device *parent) +{ + const struct bb_emul_cfg *cfg = emul->cfg; + struct bb_emul_data *data = cfg->data; + int ret; + + data->emul.api = &bb_emul_api; + data->emul.addr = cfg->addr; + data->i2c = parent; + data->cfg = cfg; + k_mutex_init(&data->data_mtx); + + ret = i2c_emul_register(parent, emul->dev_label, &data->emul); + + bb_emul_reset(&data->emul); + + return ret; +} + +#define BB_RETIMER_EMUL(n) \ + static struct bb_emul_data bb_emul_data_##n = { \ + .vendor_id = DT_ENUM_TOKEN(DT_DRV_INST(n), vendor), \ + .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), \ + .msg_state = BB_EMUL_NONE_MSG, \ + .cur_reg = 0, \ + .write_func = NULL, \ + .read_func = NULL, \ + .write_fail_reg = BB_EMUL_NO_FAIL_REG, \ + .read_fail_reg = BB_EMUL_NO_FAIL_REG, \ + }; \ + \ + static const struct bb_emul_cfg bb_emul_cfg_##n = { \ + .i2c_label = DT_INST_BUS_LABEL(n), \ + .data = &bb_emul_data_##n, \ + .addr = DT_INST_REG_ADDR(n), \ + }; \ + EMUL_DEFINE(bb_emul_init, DT_DRV_INST(n), &bb_emul_cfg_##n) + +DT_INST_FOREACH_STATUS_OKAY(BB_RETIMER_EMUL) + +#define BB_RETIMER_EMUL_CASE(n) \ + case DT_INST_DEP_ORD(n): return &bb_emul_data_##n.emul; + +/** Check description in emul_bb_emulator.h */ +struct i2c_emul *bb_emul_get(int ord) +{ + switch (ord) { + DT_INST_FOREACH_STATUS_OKAY(BB_RETIMER_EMUL_CASE) + + default: + return NULL; + } +} diff --git a/zephyr/include/emul/emul_bb_retimer.h b/zephyr/include/emul/emul_bb_retimer.h new file mode 100644 index 0000000000..2820c59934 --- /dev/null +++ b/zephyr/include/emul/emul_bb_retimer.h @@ -0,0 +1,188 @@ +/* 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 BB retimer emulator + */ + +#ifndef __EMUL_BB_RETIMER_H +#define __EMUL_BB_RETIMER_H + +#include <emul.h> +#include <drivers/i2c.h> +#include <drivers/i2c_emul.h> + +/** + * @brief BB retimer emulator backend API + * @defgroup bb_emul BB retimer emulator + * @{ + * + * BB retimer emulator supports access to all its registers using I2C messages. + * It supports not four bytes writes by padding zeros (the same as real + * device), but show warning in that case. + * Application may alter emulator state: + * + * - define a Device Tree overlay file to set default vendor ID and which + * inadvisable driver behaviour should be treated as errors + * - call @ref bb_emul_set_read_func and @ref bb_emul_set_write_func to setup + * custom handlers for I2C messages + * - call @ref bb_emul_set_reg and @ref bb_emul_get_reg to set and get value + * of BB retimers registers + * - call bb_emul_set_err_* to change emulator behaviour on inadvisable driver + * behaviour + * - call @ref bb_emul_set_read_fail_reg and @ref bb_emul_set_write_fail_reg + * to configure emulator to fail on given register read or write + */ + +/** + * Special register values used in @ref bb_emul_set_read_fail_reg and + * @ref bb_emul_set_write_fail_reg + */ +#define BB_EMUL_FAIL_ALL_REG (-1) +#define BB_EMUL_NO_FAIL_REG (-2) + +/** + * @brief Get pointer to BB retimer emulator using device tree order number. + * + * @param ord Device tree order number obtained from DT_DEP_ORD macro + * + * @return Pointer to BB retimer emulator + */ +struct i2c_emul *bb_emul_get(int ord); + +/** + * @brief Custom function type that is used as user-defined callback in read + * I2C messages handling. + * + * @param emul Pointer to BB retimer emulator + * @param reg Address which is now accessed by read command + * @param data Pointer to custom user data + * + * @return 0 on success. Value of @p reg should be set by @ref bb_emul_set_reg + * @return 1 continue with normal BB retimer emulator handler + * @return negative on error + */ +typedef int (*bb_emul_read_func)(struct i2c_emul *emul, int reg, void *data); + +/** + * @brief Custom function type that is used as user-defined callback in write + * I2C messages handling. + * + * @param emul Pointer to BB retimer emulator + * @param reg Address which is now accessed by write command + * @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 BB retimer emulator handler + * @return negative on error + */ +typedef int (*bb_emul_write_func)(struct i2c_emul *emul, int reg, uint32_t val, + void *data); + +/** + * @brief Lock access to BB retimer properties. After acquiring lock, user + * may change emulator behaviour in multi-thread setup. + * + * @param emul Pointer to BB retimer emulator + * @param timeout Timeout in getting lock + * + * @return k_mutex_lock return code + */ +int bb_emul_lock_data(struct i2c_emul *emul, k_timeout_t timeout); + +/** + * @brief Unlock access to BB retimer properties. + * + * @param emul Pointer to BB retimer emulator + * + * @return k_mutex_unlock return code + */ +int bb_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 BB retimer emulator + * @param func Pointer to custom function + * @param data User data passed on call of custom function + */ +void bb_emul_set_write_func(struct i2c_emul *emul, bb_emul_write_func func, + void *data); + +/** + * @brief Set read handler for I2C messages. This function is called before + * generic handler. + * + * @param emul Pointer to BB retimer emulator + * @param func Pointer to custom function + * @param data User data passed on call of custom function + */ +void bb_emul_set_read_func(struct i2c_emul *emul, bb_emul_read_func func, + void *data); + +/** + * @brief Set value of given register of BB retimer + * + * @param emul Pointer to BB retimer emulator + * @param reg Register address which value will be changed + * @param val New value of the register + */ +void bb_emul_set_reg(struct i2c_emul *emul, int reg, uint32_t val); + +/** + * @brief Get value of given register of BB retimer + * + * @param emul Pointer to BB retimer emulator + * @param reg Register address + * + * @return Value of the register + */ +uint32_t bb_emul_get_reg(struct i2c_emul *emul, int reg); + +/** + * @brief Setup fail on read of given register of BB retimer + * + * @param emul Pointer to BB retimer emulator + * @param reg Register address or one of special values (BB_EMUL_FAIL_ALL_REG, + * BB_EMUL_NO_FAIL_REG) + */ +void bb_emul_set_read_fail_reg(struct i2c_emul *emul, int reg); + +/** + * @brief Setup fail on write of given register of BB retimer + * + * @param emul Pointer to BB retimer emulator + * @param reg Register address or one of special values (BB_EMUL_FAIL_ALL_REG, + * BB_EMUL_NO_FAIL_REG) + */ +void bb_emul_set_write_fail_reg(struct i2c_emul *emul, int reg); + +/** + * @brief Set if error should be generated when read only register is being + * written + * + * @param emul Pointer to BB retimer emulator + * @param set Check for this error + */ +void bb_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 BB retimer emulator + * @param set Check for this error + */ +void bb_emul_set_err_on_rsvd_write(struct i2c_emul *emul, bool set); + +/** + * @} + */ + +#endif /* __EMUL_BB_RETIMER */ |