diff options
Diffstat (limited to 'driver/retimer/kb800x.c')
-rw-r--r-- | driver/retimer/kb800x.c | 543 |
1 files changed, 0 insertions, 543 deletions
diff --git a/driver/retimer/kb800x.c b/driver/retimer/kb800x.c deleted file mode 100644 index 48e47404c2..0000000000 --- a/driver/retimer/kb800x.c +++ /dev/null @@ -1,543 +0,0 @@ -/* Copyright 2021 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - * - * 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, TCPCI_MSG_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, - bool *ack_required) -{ - int rv; - - /* This driver does not use host command ACKs */ - *ack_required = false; - - 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, -}; |