summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Herrmann <eherrmann@chromium.org>2021-06-01 12:46:14 -0700
committerCommit Bot <commit-bot@chromium.org>2021-06-18 01:49:33 +0000
commit8317b1136aaf03aa6988e98300b50cebb90e8de7 (patch)
tree2fed8fd7bd6956b7179396bed77697bea693c9ed
parent4fb0165b58bf216a4db876a1dc2b992e00d93e93 (diff)
downloadchrome-ec-8317b1136aaf03aa6988e98300b50cebb90e8de7.tar.gz
KB800x: Add Driver
Add KB800x driver. Add config options to Kconfig. BUG=b:168930682 TEST=On Volteer, check USB4, TBT3, DPMF, DP, and USB3 functionality BRANCH=none Signed-off-by: Eric Herrmann <eherrmann@chromium.org> Change-Id: Ic71b0d4236037522455a0561ba87fd9a874a4968 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2930581 Reviewed-by: Abe Levkoy <alevkoy@chromium.org>
-rw-r--r--driver/build.mk1
-rw-r--r--driver/retimer/kb800x.c539
-rw-r--r--driver/retimer/kb800x.h110
-rw-r--r--include/config.h4
-rw-r--r--zephyr/Kconfig.usbc19
5 files changed, 673 insertions, 0 deletions
diff --git a/driver/build.mk b/driver/build.mk
index db25932f40..2c04dad6ad 100644
--- a/driver/build.mk
+++ b/driver/build.mk
@@ -162,6 +162,7 @@ driver-$(CONFIG_USB_PD_TCPM_STM32GX)+=tcpm/stm32gx.o
# Type-C Retimer drivers
driver-$(CONFIG_USBC_RETIMER_INTEL_BB)+=retimer/bb_retimer.o
+driver-$(CONFIG_USBC_RETIMER_KB800X)+=retimer/kb800x.o
driver-$(CONFIG_USBC_RETIMER_NB7V904M)+=retimer/nb7v904m.o
driver-$(CONFIG_USBC_RETIMER_PI3DPX1207)+=retimer/pi3dpx1207.o
driver-$(CONFIG_USBC_RETIMER_PI3HDX1204)+=retimer/pi3hdx1204.o
diff --git a/driver/retimer/kb800x.c b/driver/retimer/kb800x.c
new file mode 100644
index 0000000000..3fbb52cbc1
--- /dev/null
+++ b/driver/retimer/kb800x.c
@@ -0,0 +1,539 @@
+/* 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.
+ *
+ * Driver for Kandou KB800x USB-C 40 Gb/s multiprotocol switch.
+ */
+
+#include "common.h"
+#include "console.h"
+#include "i2c.h"
+#include "kb800x.h"
+#include "time.h"
+
+/* Time between load switch enable and the reset being de-asserted */
+#define KB800X_POWER_ON_DELAY_MS 20
+
+static mux_state_t cached_mux_state[CONFIG_USB_PD_PORT_MAX_COUNT];
+
+static int kb800x_write(const struct usb_mux *me, uint16_t address,
+ uint8_t data)
+{
+ uint8_t kb800x_config[3] = { 0x00, 0x00, 0x00 };
+
+ kb800x_config[0] = (address >> 8) & 0xff;
+ kb800x_config[1] = address & 0xff;
+ kb800x_config[2] = data;
+ return i2c_xfer(me->i2c_port, me->i2c_addr_flags, kb800x_config,
+ sizeof(kb800x_config), NULL, 0);
+}
+
+static int kb800x_read(const struct usb_mux *me, uint16_t address,
+ uint8_t *data)
+{
+ uint8_t kb800x_config[2] = { 0x00, 0x00 };
+
+ kb800x_config[0] = (address >> 8) & 0xff;
+ kb800x_config[1] = address & 0xff;
+ return i2c_xfer(me->i2c_port, me->i2c_addr_flags, kb800x_config,
+ sizeof(kb800x_config), data, 1);
+}
+
+#ifdef CONFIG_KB800X_CUSTOM_XBAR
+
+/* These lookup tables are derived from the KB8001 EVB GUI register map */
+
+/* Map elastic buffer (EB) to register field for TX configuration. */
+static const uint8_t tx_eb_to_field_ab[] = {
+ [KB800X_EB1] = 4, [KB800X_EB2] = 0, [KB800X_EB3] = 0,
+ [KB800X_EB4] = 1, [KB800X_EB5] = 2, [KB800X_EB6] = 3
+};
+static const uint8_t tx_eb_to_field_cd[] = {
+ [KB800X_EB1] = 1, [KB800X_EB2] = 2, [KB800X_EB3] = 3,
+ [KB800X_EB4] = 4, [KB800X_EB5] = 0, [KB800X_EB6] = 0
+};
+/* Map phy lane to register field for RX configuration */
+static const uint8_t rx_phy_lane_to_field[] = {
+ [KB800X_A0] = 1, [KB800X_A1] = 2, [KB800X_B0] = 5, [KB800X_B1] = 6,
+ [KB800X_C0] = 1, [KB800X_C1] = 2, [KB800X_D0] = 5, [KB800X_D1] = 6
+};
+/* Map EB to address for RX configuration */
+static const uint16_t rx_eb_to_address[] = {
+ [KB800X_EB1] = KB800X_REG_XBAR_EB1SEL,
+ [KB800X_EB2] = KB800X_REG_XBAR_EB23SEL,
+ [KB800X_EB3] = KB800X_REG_XBAR_EB23SEL,
+ [KB800X_EB4] = KB800X_REG_XBAR_EB4SEL,
+ [KB800X_EB5] = KB800X_REG_XBAR_EB56SEL,
+ [KB800X_EB6] = KB800X_REG_XBAR_EB56SEL
+};
+/* Map SS lane to EB for DP or USB/CIO protocols */
+static const uint8_t dp_ss_lane_to_eb[] = { [KB800X_TX0] = KB800X_EB4,
+ [KB800X_TX1] = KB800X_EB5,
+ [KB800X_RX0] = KB800X_EB6,
+ [KB800X_RX1] = KB800X_EB1 };
+static const uint8_t usb_ss_lane_to_eb[] = { [KB800X_TX0] = KB800X_EB4,
+ [KB800X_TX1] = KB800X_EB5,
+ [KB800X_RX0] = KB800X_EB1,
+ [KB800X_RX1] = KB800X_EB2 };
+
+/* Assign a phy TX to an elastic buffer */
+static int kb800x_assign_tx_to_eb(const struct usb_mux *me,
+ enum kb800x_phy_lane phy_lane, enum kb800x_eb eb)
+{
+ uint8_t field_value = 0;
+ uint8_t regval;
+ int rv;
+
+ field_value = KB800X_PHY_IS_AB(phy_lane) ? tx_eb_to_field_ab[eb] :
+ tx_eb_to_field_cd[eb];
+
+ /* For lane1 of each PHY, shift by 3 bits */
+ field_value <<= 3 * KB800X_LANE_NUMBER_FROM_PHY(phy_lane);
+
+ rv = kb800x_read(me, KB800X_REG_TXSEL_FROM_PHY(phy_lane), &regval);
+ if (rv)
+ return rv;
+ return kb800x_write(me, KB800X_REG_TXSEL_FROM_PHY(phy_lane),
+ regval | field_value);
+}
+
+
+/* Assign a phy RX to an elastic buffer */
+static int kb800x_assign_rx_to_eb(const struct usb_mux *me,
+ enum kb800x_phy_lane phy_lane, enum kb800x_eb eb)
+{
+ uint16_t address = 0;
+ uint8_t field_value = 0;
+ uint8_t regval = 0;
+ int rv;
+
+
+ field_value = rx_phy_lane_to_field[phy_lane];
+ address = rx_eb_to_address[eb];
+
+ /*
+ * need to shift by 4 for reverse EB or 3rd EB in set based on the
+ * register definition from the KB8001 EVB register map
+ */
+ switch (eb) {
+ case KB800X_EB1:
+ if (!KB800X_PHY_IS_AB(phy_lane))
+ field_value <<= 4;
+ break;
+ case KB800X_EB4:
+ if (KB800X_PHY_IS_AB(phy_lane))
+ field_value <<= 4;
+ break;
+ case KB800X_EB3:
+ case KB800X_EB6:
+ field_value <<= 4;
+ break;
+ default:
+ break;
+ }
+
+ rv = kb800x_read(me, address, &regval);
+ if (rv)
+ return rv;
+ return kb800x_write(me, address, regval | field_value);
+}
+
+static bool kb800x_in_dpmf(const struct usb_mux *me)
+{
+ if ((cached_mux_state[me->usb_port] & USB_PD_MUX_DP_ENABLED) &&
+ (cached_mux_state[me->usb_port] & USB_PD_MUX_USB_ENABLED))
+ return true;
+ else
+ return false;
+}
+
+static bool kb800x_is_dp_lane(const struct usb_mux *me,
+ enum kb800x_ss_lane ss_lane)
+{
+ if (cached_mux_state[me->usb_port] & USB_PD_MUX_DP_ENABLED) {
+ /* DP ALT mode */
+ if (kb800x_in_dpmf(me)) {
+ /* DPMF pin configuration */
+ if ((ss_lane == KB800X_TX1) ||
+ (ss_lane == KB800X_RX1)) {
+ return true; /* ML0 or ML1 */
+ }
+ } else {
+ /* Pure, 4-lane DP mode */
+ return true;
+ }
+ }
+ /* Not a DP mode or ML2/3 while in DPMF */
+ return false;
+}
+
+/* Assigning this PHY to this SS lane means it should be RX */
+static bool kb800x_phy_ss_lane_is_rx(enum kb800x_phy_lane phy_lane,
+ enum kb800x_ss_lane ss_lane)
+{
+ bool rx;
+
+ switch (ss_lane) {
+ case KB800X_TX0:
+ case KB800X_TX1:
+ rx = false;
+ break;
+ case KB800X_RX0:
+ case KB800X_RX1:
+ rx = true;
+ break;
+ }
+ /* invert for C/D (host side), since it is receiving the TX signal*/
+ if (!KB800X_PHY_IS_AB(phy_lane))
+ return !rx;
+ return rx;
+}
+
+/* Assign SS lane to PHY. Assumes A/B is connector-side, and C/D is host-side */
+static int kb800x_assign_lane(const struct usb_mux *me,
+ enum kb800x_phy_lane phy_lane,
+ enum kb800x_ss_lane ss_lane)
+{
+ enum kb800x_eb eb = 0;
+
+ /*
+ * Easiest way to handle flipping is to just swap lane 1/0. This assumes
+ * lanes are flipped in the AP. If they are not, they shouldn't be
+ * flipped for the AP-side lanes, but should for connector-side
+ */
+ if (cached_mux_state[me->usb_port] & USB_PD_MUX_POLARITY_INVERTED)
+ ss_lane = KB800X_FLIP_SS_LANE(ss_lane);
+
+ if (kb800x_is_dp_lane(me, ss_lane)) {
+ if (kb800x_in_dpmf(me)) {
+ /* Route USB3 RX/TX to EB1/4, and ML0/1 to EB5/6 */
+ switch (ss_lane) {
+ case KB800X_TX1: /* ML1 */
+ eb = KB800X_EB6;
+ break;
+ case KB800X_RX1: /* ML0 */
+ eb = KB800X_EB5;
+ break;
+ default:
+ break;
+ }
+ } else {
+ /* Route ML0/1/2/3 through EB1/5/4/6 */
+ eb = dp_ss_lane_to_eb[ss_lane];
+ }
+
+ /* For DP lanes, always DFP so A/B is TX, C/D is RX */
+ if (KB800X_PHY_IS_AB(phy_lane))
+ return kb800x_assign_tx_to_eb(me, phy_lane, eb);
+ else
+ return kb800x_assign_rx_to_eb(me, phy_lane, eb);
+ }
+
+ /* Lane is either USB3 or CIO */
+ if (kb800x_phy_ss_lane_is_rx(phy_lane, ss_lane))
+ return kb800x_assign_rx_to_eb(me, phy_lane,
+ usb_ss_lane_to_eb[ss_lane]);
+ else
+ return kb800x_assign_tx_to_eb(me, phy_lane,
+ usb_ss_lane_to_eb[ss_lane]);
+}
+
+static int kb800x_xbar_override(const struct usb_mux *me)
+{
+ int rv;
+ int i;
+
+ for (i = KB800X_A0; i < KB800X_PHY_LANE_COUNT; ++i) {
+ rv = kb800x_assign_lane(
+ me, i,
+ kb800x_control[me->usb_port].ss_lanes[i]);
+ if (rv)
+ return rv;
+ }
+ return kb800x_write(me, KB800X_REG_XBAR_OVR,
+ KB800X_XBAR_OVR_EN);
+}
+#endif /* CONFIG_KB800X_CUSTOM_XBAR */
+
+/*
+ * The initialization writes for each protocol can be found in the KB8001/KB8002
+ * Programming Guidelines
+ */
+static const uint16_t global_init_addresses[] = {
+ 0x5058, 0x5059, 0xFF63, 0xF021, 0xF022, 0xF057, 0xF058,
+ 0x8194, 0xF0C9, 0xF0CA, 0xF0CB, 0xF0CC, 0xF0CD, 0xF0CE,
+ 0xF0DF, 0xF0E0, 0xF0E1, 0x8198, 0x8191
+};
+static const uint8_t global_init_values[] = { 0x12, 0x12, 0x3C, 0x02, 0x02,
+ 0x02, 0x02, 0x37, 0x0C, 0x0B,
+ 0x0A, 0x09, 0x08, 0x07, 0x57,
+ 0x66, 0x66, 0x33, 0x00 };
+static const uint16_t usb3_init_addresses[] = { 0xF020, 0xF056 };
+static const uint8_t usb3_init_values[] = { 0x2f, 0x2f };
+static const uint16_t dp_init_addresses[] = { 0xF2CB, 0x0011 };
+static const uint8_t dp_init_values[] = { 0x30, 0x00 };
+/*
+ * The first 2 CIO writes apply an SBRX pullup to the host side (C/D)
+ * This is required when the CPU doesn't apply a pullup.
+ */
+static const uint16_t cio_init_addresses[] = { 0x81fd, 0x81fe, 0xF26B, 0xF26E };
+static const uint8_t cio_init_values[] = { 0x08, 0x80, 0x01, 0x19 };
+
+static int kb800x_bulk_write(const struct usb_mux *me,
+ const uint16_t *addresses, const uint8_t *values,
+ const uint8_t size)
+{
+ int i;
+ int rv;
+
+ for (i = 0; i < size; ++i) {
+ rv = kb800x_write(me, addresses[i], values[i]);
+ if (rv != EC_SUCCESS)
+ return rv;
+ }
+
+ return EC_SUCCESS;
+}
+
+static int kb800x_global_init(const struct usb_mux *me)
+{
+ return kb800x_bulk_write(me, global_init_addresses, global_init_values,
+ sizeof(global_init_values));
+}
+
+static int kb800x_dp_init(const struct usb_mux *me, mux_state_t mux_state)
+{
+ int rv;
+
+ rv = kb800x_bulk_write(me, dp_init_addresses, dp_init_values,
+ sizeof(dp_init_values));
+ if (rv)
+ return rv;
+ return kb800x_write(
+ me, KB800X_REG_ORIENTATION,
+ KB800X_ORIENTATION_DP_DFP |
+ ((mux_state & USB_PD_MUX_POLARITY_INVERTED) ?
+ KB800X_ORIENTATION_POLARITY :
+ 0x0));
+}
+
+static int kb800x_usb3_init(const struct usb_mux *me, mux_state_t mux_state)
+{
+ int rv;
+
+ rv = kb800x_bulk_write(me, usb3_init_addresses, usb3_init_values,
+ sizeof(usb3_init_values));
+ if (rv)
+ return rv;
+ if (mux_state & USB_PD_MUX_POLARITY_INVERTED)
+ /* This will be overwritten in the DPMF case */
+ return kb800x_write(me, KB800X_REG_ORIENTATION,
+ KB800X_ORIENTATION_POLARITY);
+ return EC_SUCCESS;
+}
+
+static int kb800x_cio_init(const struct usb_mux *me, mux_state_t mux_state)
+{
+ uint8_t orientation = 0x0;
+ int rv;
+
+ enum idh_ptype cable_type = get_usb_pd_cable_type(me->usb_port);
+ union tbt_mode_resp_cable cable_resp = {
+ .raw_value =
+ pd_get_tbt_mode_vdo(me->usb_port, TCPC_TX_SOP_PRIME)
+ };
+
+ rv = kb800x_bulk_write(me, cio_init_addresses, cio_init_values,
+ sizeof(cio_init_values));
+ if (rv)
+ return rv;
+
+ if (mux_state & USB_PD_MUX_POLARITY_INVERTED)
+ orientation = KB800X_ORIENTATION_CIO_LANE_SWAP |
+ KB800X_ORIENTATION_POLARITY;
+
+ if (!(mux_state & USB_PD_MUX_USB4_ENABLED)) {
+ /* Special configuration only for legacy mode */
+ if (cable_type == IDH_PTYPE_ACABLE ||
+ cable_resp.tbt_active_passive == TBT_CABLE_ACTIVE) {
+ /* Active cable */
+ if (cable_resp.lsrx_comm == UNIDIR_LSRX_COMM) {
+ orientation |=
+ KB800X_ORIENTATION_CIO_LEGACY_UNIDIR;
+ } else {
+ /* 'Pre-Coding on a TBT3-Compatible Link' ECN */
+ rv = kb800x_write(me, 0x8194, 0x31);
+ if (rv)
+ return rv;
+ orientation |=
+ KB800X_ORIENTATION_CIO_LEGACY_BIDIR;
+ }
+ } else {
+ /* Passive Cable */
+ orientation |= KB800X_ORIENTATION_CIO_LEGACY_PASSIVE;
+ }
+ }
+ return kb800x_write(me, KB800X_REG_ORIENTATION, orientation);
+}
+
+static int kb800x_set_state(const struct usb_mux *me, mux_state_t mux_state)
+{
+ int rv;
+
+ cached_mux_state[me->usb_port] = mux_state;
+ rv = kb800x_write(me, KB800X_REG_RESET, KB800X_RESET_MASK);
+ if (rv)
+ return rv;
+ /* Release memory map reset */
+ rv = kb800x_write(me, KB800X_REG_RESET,
+ KB800X_RESET_MASK & ~KB800X_RESET_MM);
+ if (rv)
+ return rv;
+
+ /* Already in reset, nothing to do */
+ if ((mux_state == USB_PD_MUX_NONE) ||
+ (mux_state & USB_PD_MUX_SAFE_MODE))
+ return EC_SUCCESS;
+
+ rv = kb800x_global_init(me);
+ if (rv)
+ return rv;
+
+ /* CIO mode (USB4/TBT) */
+ if (mux_state &
+ (USB_PD_MUX_USB4_ENABLED | USB_PD_MUX_TBT_COMPAT_ENABLED)) {
+ rv = kb800x_cio_init(me, mux_state);
+ if (rv)
+ return rv;
+ rv = kb800x_write(me, KB800X_REG_PROTOCOL, KB800X_PROTOCOL_CIO);
+ } else {
+ /* USB3 enabled (USB3-only or DPMF) */
+ if (mux_state & USB_PD_MUX_USB_ENABLED) {
+ rv = kb800x_usb3_init(me, mux_state);
+ if (rv)
+ return rv;
+ /* USB3-only is the default KB800X_REG_PROTOCOL value */
+ }
+
+ /* DP alt modes (DP-only or DPMF) */
+ if (mux_state & USB_PD_MUX_DP_ENABLED) {
+ rv = kb800x_dp_init(me, mux_state);
+ if (rv)
+ return rv;
+ if (mux_state & USB_PD_MUX_USB_ENABLED)
+ rv = kb800x_write(me, KB800X_REG_PROTOCOL,
+ KB800X_PROTOCOL_DPMF);
+ else
+ rv = kb800x_write(me, KB800X_REG_PROTOCOL,
+ KB800X_PROTOCOL_DP);
+ }
+ }
+ if (rv)
+ return rv;
+
+#ifdef CONFIG_KB800X_CUSTOM_XBAR
+ rv = kb800x_xbar_override(me);
+ if (rv)
+ return rv;
+#endif /* CONFIG_KB800X_CUSTOM_XBAR */
+
+ return kb800x_write(me, KB800X_REG_RESET, 0x00);
+}
+
+static int kb800x_init(const struct usb_mux *me)
+{
+ gpio_set_level(kb800x_control[me->usb_port].usb_ls_en_gpio, 1);
+ gpio_set_level(kb800x_control[me->usb_port].retimer_rst_gpio, 1);
+
+ /*
+ * Delay after enabling power and releasing the reset to allow the power
+ * to come up and the reset to be released by the power sequencing
+ * logic. If after the delay, the reset is still held low - return an
+ * error.
+ */
+ msleep(KB800X_POWER_ON_DELAY_MS);
+ if (!gpio_get_level(kb800x_control[me->usb_port].retimer_rst_gpio))
+ return EC_ERROR_NOT_POWERED;
+
+ return kb800x_set_state(me, USB_PD_MUX_NONE);
+}
+
+static int kb800x_enter_low_power_mode(const struct usb_mux *me)
+{
+ gpio_set_level(kb800x_control[me->usb_port].retimer_rst_gpio, 0);
+ /* Power-down sequencing must be handled in HW */
+ gpio_set_level(kb800x_control[me->usb_port].usb_ls_en_gpio, 0);
+
+ return EC_SUCCESS;
+}
+
+#ifdef CONFIG_CMD_RETIMER
+
+static int console_command_kb800x_xfer(int argc, char **argv)
+{
+ char rw, *e;
+ int rv, port, reg, val;
+ uint8_t data;
+ const struct usb_mux *mux;
+
+ if (argc < 4)
+ return EC_ERROR_PARAM_COUNT;
+
+ /* Get port number */
+ port = strtoi(argv[1], &e, 0);
+ if (*e || !board_is_usb_pd_port_present(port))
+ return EC_ERROR_PARAM1;
+
+ mux = &usb_muxes[port];
+ while (mux) {
+ if (mux->driver == &kb800x_usb_mux_driver)
+ break;
+ mux = mux->next_mux;
+ }
+
+ if (!mux)
+ return EC_ERROR_PARAM1;
+
+ /* Validate r/w selection */
+ rw = argv[2][0];
+ if (rw != 'w' && rw != 'r')
+ return EC_ERROR_PARAM2;
+
+ /* Get register address */
+ reg = strtoi(argv[3], &e, 0);
+ if (*e || reg < 0)
+ return EC_ERROR_PARAM3;
+ rv = EC_SUCCESS;
+ if (rw == 'r')
+ rv = kb800x_read(mux, reg, &data);
+ else {
+ if (argc < 5)
+ return EC_ERROR_PARAM_COUNT;
+ /* Get value to be written */
+ val = strtoi(argv[4], &e, 0);
+ if (*e || val < 0)
+ return EC_ERROR_PARAM4;
+ rv = kb800x_write(mux, reg, val);
+ if (rv == EC_SUCCESS) {
+ rv = kb800x_read(mux, reg, &data);
+ if (rv == EC_SUCCESS && data != val)
+ rv = EC_ERROR_UNKNOWN;
+ }
+ }
+
+ if (rv == EC_SUCCESS)
+ ccprintf("register 0x%x [%d] = 0x%x [%d]\n", reg, reg, data,
+ data);
+
+ return rv;
+}
+DECLARE_CONSOLE_COMMAND(kbxfer, console_command_kb800x_xfer,
+ "<port> <r/w> <reg> | <val>",
+ "Read or write to KB retimer register");
+#endif /* CONFIG_CMD_RETIMER */
+
+const struct usb_mux_driver kb800x_usb_mux_driver = {
+ .init = kb800x_init,
+ .set = kb800x_set_state,
+ .enter_low_power_mode = kb800x_enter_low_power_mode,
+};
diff --git a/driver/retimer/kb800x.h b/driver/retimer/kb800x.h
new file mode 100644
index 0000000000..5f8cf2810d
--- /dev/null
+++ b/driver/retimer/kb800x.h
@@ -0,0 +1,110 @@
+/* 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.
+ *
+ * Driver for Kandou KB8001 USB-C 40 Gb/s multiprotocol switch.
+ */
+
+#ifndef __CROS_EC_KB800X_H
+#define __CROS_EC_KB800X_H
+
+#include "compile_time_macros.h"
+#include "gpio_signal.h"
+#include "usb_mux.h"
+
+#define KB800X_I2C_ADDR0_FLAGS 0x08
+#define KB800X_I2C_ADDR1_FLAGS 0x0C
+
+extern const struct usb_mux_driver kb800x_usb_mux_driver;
+
+/* Set the protocol */
+#define KB800X_REG_PROTOCOL 0x0001
+#define KB800X_PROTOCOL_USB3 0x0
+#define KB800X_PROTOCOL_DPMF 0x1
+#define KB800X_PROTOCOL_DP 0x2
+#define KB800X_PROTOCOL_CIO 0x3
+
+/* Configure the lane orientaitons */
+#define KB800X_REG_ORIENTATION 0x0002
+#define KB800X_ORIENTATION_POLARITY 0x1
+#define KB800X_ORIENTATION_DP_UFP 0x4
+#define KB800X_ORIENTATION_DP_DFP 0x6
+#define KB800X_ORIENTATION_CIO_LANE_SWAP 0x8
+/* Select one, 0x0 for non-legacy */
+#define KB800X_ORIENTATION_CIO_LEGACY_PASSIVE (0x1 << 4)
+#define KB800X_ORIENTATION_CIO_LEGACY_UNIDIR (0x2 << 4)
+#define KB800X_ORIENTATION_CIO_LEGACY_BIDIR (0x3 << 4)
+
+#define KB800X_REG_RESET 0x0006
+#define KB800X_RESET_FSM BIT(0)
+#define KB800X_RESET_MM BIT(1)
+#define KB800X_RESET_SERDES BIT(2)
+#define KB800X_RESET_COM BIT(3)
+#define KB800X_RESET_MASK GENMASK(3, 0)
+
+#define KB800X_REG_XBAR_OVR 0x5040
+#define KB800X_XBAR_OVR_EN BIT(6)
+
+/* Registers to configure the elastic buffer input connection */
+#define KB800X_REG_XBAR_EB1SEL 0x5044
+#define KB800X_REG_XBAR_EB23SEL 0x5045
+#define KB800X_REG_XBAR_EB4SEL 0x5046
+#define KB800X_REG_XBAR_EB56SEL 0x5047
+
+/* Registers to configure the elastic buffer output connection (x=0-7) */
+#define KB800X_REG_TXSEL_FROM_PHY(x) (0x5048+((x)/2))
+
+enum kb800x_ss_lane {
+ KB800X_TX0 = 0,
+ KB800X_TX1,
+ KB800X_RX0,
+ KB800X_RX1
+};
+
+enum kb800x_phy_lane {
+ KB800X_A0 = 0,
+ KB800X_A1,
+ KB800X_B0,
+ KB800X_B1,
+ KB800X_C0,
+ KB800X_C1,
+ KB800X_D0,
+ KB800X_D1,
+ KB800X_PHY_LANE_COUNT
+};
+
+enum kb800x_eb {
+ KB800X_EB1 = 0,
+ KB800X_EB2,
+ KB800X_EB3,
+ KB800X_EB4,
+ KB800X_EB5,
+ KB800X_EB6
+};
+
+#define KB800X_FLIP_SS_LANE(x) ((x) + 1 - 2*((x) & 0x1))
+#define KB800X_LANE_NUMBER_FROM_PHY(x) ((x) & 0x1)
+#define KB800X_PHY_IS_AB(x) ((x) <= KB800X_B1)
+
+struct kb800x_control_t {
+ enum gpio_signal retimer_rst_gpio;
+ enum gpio_signal usb_ls_en_gpio;
+#ifdef CONFIG_KB800X_CUSTOM_XBAR
+ enum kb800x_ss_lane ss_lanes[KB800X_PHY_LANE_COUNT];
+#endif /* CONFIG_KB800X_CUSTOM_XBAR */
+};
+
+/*
+ * Default 'example' lane mapping. With this mapping, CONFIG_KB800X_CUSTOM_XBAR
+ * can be undefined, since a custom xbar mapping is not needed.
+ * ss_lanes = {
+ * [KB800X_A0] = KB800X_TX0, [KB800X_A1] = KB800X_RX0,
+ * [KB800X_B0] = KB800X_RX1, [KB800X_B1] = KB800X_TX1,
+ * [KB800X_C0] = KB800X_RX0, [KB800X_C1] = KB800X_TX0,
+ * [KB800X_D0] = KB800X_TX1, [KB800X_D1] = KB800X_RX1,}
+ */
+
+extern struct kb800x_control_t kb800x_control[];
+
+
+#endif /* __CROS_EC_KB800X_H */
diff --git a/include/config.h b/include/config.h
index 25098f3bca..175d64a0bb 100644
--- a/include/config.h
+++ b/include/config.h
@@ -4493,6 +4493,7 @@
* Type-C retimer drivers to be used.
*/
#undef CONFIG_USBC_RETIMER_INTEL_BB
+#undef CONFIG_USBC_RETIMER_KB800X
#undef CONFIG_USBC_RETIMER_NB7V904M
#undef CONFIG_USBC_RETIMER_PI3DPX1207
#undef CONFIG_USBC_RETIMER_PI3HDX1204
@@ -4528,6 +4529,9 @@
/* Allow run-time configuration of the Burnside Bridge driver structure */
#undef CONFIG_USBC_RETIMER_INTEL_BB_RUNTIME_CONFIG
+/* Require manual configuration of the KB800x crossbar mapping. */
+#undef CONFIG_KB800X_CUSTOM_XBAR
+
/* Enables debug console commands for the STM32 UCPD driver */
#undef CONFIG_STM32G4_UCPD_DEBUG
diff --git a/zephyr/Kconfig.usbc b/zephyr/Kconfig.usbc
index f4eaf8e6c6..9e0bb36422 100644
--- a/zephyr/Kconfig.usbc
+++ b/zephyr/Kconfig.usbc
@@ -215,6 +215,25 @@ config PLATFORM_EC_USBC_RETIMER_PS8811
signals for long media link applications. It supports USB 3.1 Gen 2
with operation speed up to 10Gbps as well as Gen 1 operation at 5Gbps.
+config PLATFORM_EC_USBC_RETIMER_KB800X
+ bool "Enable KB800X retimer"
+ help
+ The KB8001 is a Universal Serial Bus (USB) Type-C 40 Gb/s multiprotocol
+ switch and bidirectional Bit-Level Retimer (BLR) which supports:
+ - Display Port: four unidirectional DP lanes
+ - USB3.1 Gen1/2: one bi-directional USB lane
+ - USB4/Thunderbolt: two bi-directional CIO lanes
+ - Multifunction Display (MFD): two unidirectional lanes of DP and
+ one bidirectional lane of USB3.1 Gen1/2
+
+config PLATFORM_EC_KB800X_CUSTOM_XBAR
+ bool "Use custom remapping of HSIO XBAR"
+ depends on PLATFORM_EC_USBC_RETIMER_KB800X
+ default n
+ help
+ Enable this to allow using a custom crossbar configuration for the HSIO
+ lanes.
+
menuconfig PLATFORM_EC_USB_POWER_DELIVERY
bool "USB Type-C Power Delivery (PD)"
default y