summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomasz Michalec <tm@semihalf.com>2021-08-20 12:19:52 +0200
committerCommit Bot <commit-bot@chromium.org>2021-09-02 21:00:56 +0000
commit16245fe2c3f2b8b98627a06f0577c84c22ef1eb4 (patch)
tree97275fbda753bf3e5cb6c797cda0d9e943f6d43e
parent20c87cd1de3c0a06be92bf64b01ea9d2584f5b5b (diff)
downloadchrome-ec-16245fe2c3f2b8b98627a06f0577c84c22ef1eb4.tar.gz
zephyr: Add BB retimer emulator
Add BB retimer emulator on i2c bus. Emulator properties can be defined using device tree or runtime emulator API. Emulator checks if RO registers and reserved bits are accessed correctly. API allows to set custom read/write i2c messagess handlers to emulate complex behaviour. BUG=b:184856919 BRANCH=none TEST=none Signed-off-by: Tomasz Michalec <tm@semihalf.com> Change-Id: I4b641a90e6fb55e89aaee388c0ac04ab7bf367ba Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3110085 Commit-Queue: Jeremy Bettis <jbettis@chromium.org> Reviewed-by: Jeremy Bettis <jbettis@chromium.org> Reviewed-by: Yuval Peress <peress@chromium.org>
-rw-r--r--baseboard/intelrvp/adlrvp.c3
-rw-r--r--board/boldar/board.c3
-rw-r--r--board/copano/board.c3
-rw-r--r--board/drobit/board.c3
-rw-r--r--board/halvor/board.c2
-rw-r--r--board/lingcod/board.c2
-rw-r--r--board/malefor/board.c2
-rw-r--r--board/terrador/board.c3
-rw-r--r--board/tglrvpu_ite/board.c2
-rw-r--r--board/todor/board.c3
-rw-r--r--board/trondo/board.c3
-rw-r--r--board/volteer/board.c3
-rw-r--r--board/voxel/board.c3
-rw-r--r--driver/retimer/bb_retimer.c2
-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.yaml30
-rw-r--r--zephyr/emul/CMakeLists.txt1
-rw-r--r--zephyr/emul/Kconfig9
-rw-r--r--zephyr/emul/emul_bb_retimer.c549
-rw-r--r--zephyr/include/emul/emul_bb_retimer.h188
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 */