diff options
author | Eric Herrmann <eherrmann@chromium.org> | 2021-06-01 12:46:14 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-06-18 01:49:33 +0000 |
commit | 8317b1136aaf03aa6988e98300b50cebb90e8de7 (patch) | |
tree | 2fed8fd7bd6956b7179396bed77697bea693c9ed | |
parent | 4fb0165b58bf216a4db876a1dc2b992e00d93e93 (diff) | |
download | chrome-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.mk | 1 | ||||
-rw-r--r-- | driver/retimer/kb800x.c | 539 | ||||
-rw-r--r-- | driver/retimer/kb800x.h | 110 | ||||
-rw-r--r-- | include/config.h | 4 | ||||
-rw-r--r-- | zephyr/Kconfig.usbc | 19 |
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), ®val); + 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, ®val); + 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 |