diff options
Diffstat (limited to 'common/usbc/tbt_alt_mode.c')
-rw-r--r-- | common/usbc/tbt_alt_mode.c | 579 |
1 files changed, 0 insertions, 579 deletions
diff --git a/common/usbc/tbt_alt_mode.c b/common/usbc/tbt_alt_mode.c deleted file mode 100644 index 73e2796345..0000000000 --- a/common/usbc/tbt_alt_mode.c +++ /dev/null @@ -1,579 +0,0 @@ -/* Copyright 2020 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. - */ - -/* - * Thunderbolt alternate mode support - * Refer to USB Type-C Cable and Connector Specification Release 2.0 Section F - */ - -#include "atomic.h" -#include <stdbool.h> -#include <stdint.h> -#include "compile_time_macros.h" -#include "console.h" -#include "tcpm/tcpm.h" -#include "usb_common.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_tbt.h" -#include "usb_pd_tcpm.h" -#include "usb_pe_sm.h" -#include "usb_tbt_alt_mode.h" - -/* - * Enter/Exit TBT mode with active cable - * - * - * TBT_START |------------ - * retry_done = false | | - * | v | - * |<------------------| Exit Mode SOP | - * | retry_done = true | | | - * v | | ACK/NAK | - * Enter Mode SOP' | --------|--------- | - * ACK | NAK | Exit Mode SOP'' | - * |------|------| | | | - * | | | | ACK/NAK | - * v | | --------|--------- | - * Enter Mode SOP'' | | Exit Mode SOP' | - * | | | | | - * ACK | NAK | | | ACK/NAK | - * |------|------| | | ------------------ | - * | | | | retry_done == true? | - * v | | | | | - * Enter Mode SOP | | | No | | - * | | | |----------- | - * ACK | NAK | | |Yes | - * |-------|------| | | v | - * | | | | TBT_INACTIVE | - * v | | | retry_done = false | - * TBT_ACTIVE | | | | - * retry_done = true | | | | - * | | | | | - * v v v v | - * -----------------------------------------------------------------| - */ - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* - * If a partner sends an Enter Mode NAK, Exit Mode and try again. This has - * happened when the EC loses state after previously entering an alt mode - * with a partner. It may be fixed in b/159495742, in which case this - * logic is unneeded. - */ -#define TBT_FLAG_RETRY_DONE BIT(0) -#define TBT_FLAG_EXIT_DONE BIT(1) -#define TBT_FLAG_CABLE_ENTRY_DONE BIT(2) - -static uint8_t tbt_flags[CONFIG_USB_PD_PORT_MAX_COUNT]; - -#define TBT_SET_FLAG(port, flag) (tbt_flags[port] |= (flag)) -#define TBT_CLR_FLAG(port, flag) (tbt_flags[port] &= (~flag)) -#define TBT_CHK_FLAG(port, flag) (tbt_flags[port] & (flag)) - -static int tbt_prints(const char *string, int port) -{ - return CPRINTS("C%d: TBT %s", port, string); -} - -/* The states of Thunderbolt negotiation */ -enum tbt_states { - TBT_START = 0, - TBT_ENTER_SOP, - TBT_ACTIVE, - TBT_EXIT_SOP, - TBT_INACTIVE, - /* Active cable only */ - TBT_ENTER_SOP_PRIME, - TBT_ENTER_SOP_PRIME_PRIME, - TBT_EXIT_SOP_PRIME, - TBT_EXIT_SOP_PRIME_PRIME, - TBT_STATE_COUNT, -}; -static enum tbt_states tbt_state[CONFIG_USB_PD_PORT_MAX_COUNT]; - -static const uint8_t state_vdm_cmd[TBT_STATE_COUNT] = { - [TBT_ENTER_SOP] = CMD_ENTER_MODE, - [TBT_ACTIVE] = CMD_EXIT_MODE, - [TBT_EXIT_SOP] = CMD_EXIT_MODE, - /* Active cable only */ - [TBT_ENTER_SOP_PRIME] = CMD_ENTER_MODE, - [TBT_ENTER_SOP_PRIME_PRIME] = CMD_ENTER_MODE, - [TBT_EXIT_SOP_PRIME] = CMD_EXIT_MODE, - [TBT_EXIT_SOP_PRIME_PRIME] = CMD_EXIT_MODE, -}; - -void tbt_init(int port) -{ - tbt_state[port] = TBT_START; - TBT_CLR_FLAG(port, TBT_FLAG_RETRY_DONE); - TBT_SET_FLAG(port, TBT_FLAG_EXIT_DONE); - TBT_CLR_FLAG(port, TBT_FLAG_CABLE_ENTRY_DONE); -} - -bool tbt_is_active(int port) -{ - return tbt_state[port] != TBT_INACTIVE && - tbt_state[port] != TBT_START; -} - -bool tbt_entry_is_done(int port) -{ - return tbt_state[port] == TBT_ACTIVE || - tbt_state[port] == TBT_INACTIVE; -} - -bool tbt_cable_entry_is_done(int port) -{ - return TBT_CHK_FLAG(port, TBT_FLAG_CABLE_ENTRY_DONE); -} - -static void tbt_exit_done(int port) -{ - /* - * If the EC exits an alt mode autonomously, don't try to enter it again. If - * the AP commands the EC to exit DP mode, it might command the EC to enter - * again later, so leave the state machine ready for that possibility. - */ - tbt_state[port] = IS_ENABLED(CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY) - ? TBT_START : TBT_INACTIVE; - TBT_CLR_FLAG(port, TBT_FLAG_RETRY_DONE); - TBT_CLR_FLAG(port, TBT_FLAG_CABLE_ENTRY_DONE); - - if (!TBT_CHK_FLAG(port, TBT_FLAG_EXIT_DONE)) { - TBT_SET_FLAG(port, TBT_FLAG_EXIT_DONE); - tbt_prints("Exited alternate mode", port); - return; - } - - tbt_prints("alt mode protocol failed!", port); -} - -void tbt_exit_mode_request(int port) -{ - union tbt_mode_resp_cable cable_mode_resp; - - TBT_SET_FLAG(port, TBT_FLAG_RETRY_DONE); - TBT_CLR_FLAG(port, TBT_FLAG_EXIT_DONE); - /* - * If the port has entered USB4 mode with Thunderbolt mode for the - * cable, on request to exit, only exit Thunderbolt mode for the - * cable. - * TODO (b/156749387): Remove once data reset feature is in place. - */ - if (tbt_state[port] == TBT_ENTER_SOP) { - cable_mode_resp.raw_value = - pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME); - - /* - * For Linear re-driver cables, the port enters USB4 mode - * with Thunderbolt mode for SOP prime. Hence, on request to - * exit, only exit Thunderbolt mode SOP prime - */ - tbt_state[port] = - cable_mode_resp.tbt_active_passive == TBT_CABLE_ACTIVE ? - TBT_EXIT_SOP_PRIME : TBT_EXIT_SOP_PRIME_PRIME; - } -} - -static bool tbt_response_valid(int port, enum tcpci_msg_type type, - char *cmdt, int vdm_cmd) -{ - enum tbt_states st = tbt_state[port]; - union tbt_mode_resp_cable cable_mode_resp = { - .raw_value = pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME) }; - - /* - * Check for an unexpected response. - * 1. invalid command - * 2. invalid Tx type for passive cable - * If Thunderbolt is inactive, ignore the command. - */ - if ((st != TBT_INACTIVE && state_vdm_cmd[st] != vdm_cmd) || - (get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE && - cable_mode_resp.tbt_active_passive == TBT_CABLE_PASSIVE && - type != TCPCI_MSG_SOP)) { - tbt_exit_done(port); - return false; - } - return true; -} - -/* Exit Mode process is complete, but retry Enter Mode process */ -static void tbt_retry_enter_mode(int port) -{ - tbt_state[port] = TBT_START; - TBT_SET_FLAG(port, TBT_FLAG_RETRY_DONE); -} - -/* Send Exit Mode to SOP''(if supported), or SOP' */ -static void tbt_active_cable_exit_mode(int port) -{ - const struct pd_discovery *disc; - - disc = pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - - if (disc->identity.product_t1.a_rev20.sop_p_p) - tbt_state[port] = TBT_EXIT_SOP_PRIME_PRIME; - else - tbt_state[port] = TBT_EXIT_SOP_PRIME; -} - -bool tbt_cable_entry_required_for_usb4(int port) -{ - const struct pd_discovery *disc_sop_prime; - union tbt_mode_resp_cable cable_mode_resp; - - /* Request to enter Thunderbolt mode for the cable prior to entering - * USB4 mode if - - * 1. Thunderbolt Mode SOP' VDO active/passive bit (B25) is - * TBT_CABLE_ACTIVE or - * 2. It's an active cable with VDM version < 2.0 or - * VDO version < 1.3 - */ - if (tbt_cable_entry_is_done(port)) - return false; - - cable_mode_resp.raw_value = - pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME); - - if (cable_mode_resp.tbt_active_passive == TBT_CABLE_ACTIVE) - return true; - - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) { - disc_sop_prime = pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - if (pd_get_vdo_ver(port, TCPCI_MSG_SOP_PRIME) < VDM_VER20 || - disc_sop_prime->identity.product_t1.a_rev30.vdo_ver < - VDO_VERSION_1_3) - return true; - } - return false; -} - -void intel_vdm_acked(int port, enum tcpci_msg_type type, int vdo_count, - uint32_t *vdm) -{ - const struct pd_discovery *disc; - const uint8_t vdm_cmd = PD_VDO_CMD(vdm[0]); - int opos_sop, opos_sop_prime; - union tbt_mode_resp_cable cable_mode_resp; - - if (!tbt_response_valid(port, type, "ACK", vdm_cmd)) - return; - - disc = pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - - switch (tbt_state[port]) { - case TBT_ENTER_SOP_PRIME: - tbt_prints("enter mode SOP'", port); - cable_mode_resp.raw_value = - pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME); - /* For LRD cables, Enter mode SOP' -> Enter mode SOP */ - if (disc->identity.product_t1.a_rev20.sop_p_p && - cable_mode_resp.tbt_active_passive != TBT_CABLE_ACTIVE) { - tbt_state[port] = TBT_ENTER_SOP_PRIME_PRIME; - } else { - TBT_SET_FLAG(port, TBT_FLAG_CABLE_ENTRY_DONE); - tbt_state[port] = TBT_ENTER_SOP; - } - break; - case TBT_ENTER_SOP_PRIME_PRIME: - tbt_prints("enter mode SOP''", port); - TBT_SET_FLAG(port, TBT_FLAG_CABLE_ENTRY_DONE); - tbt_state[port] = TBT_ENTER_SOP; - break; - case TBT_ENTER_SOP: - set_tbt_compat_mode_ready(port); - tbt_state[port] = TBT_ACTIVE; - tbt_prints("enter mode SOP", port); - TBT_SET_FLAG(port, TBT_FLAG_RETRY_DONE); - /* Indicate to PE layer that alt mode is active */ - pd_set_dfp_enter_mode_flag(port, true); - break; - case TBT_ACTIVE: - tbt_prints("exit mode SOP", port); - opos_sop = pd_alt_mode(port, TCPCI_MSG_SOP, USB_VID_INTEL); - - /* Clear Thunderbolt related signals */ - pd_dfp_exit_mode(port, TCPCI_MSG_SOP, USB_VID_INTEL, opos_sop); - set_usb_mux_with_current_data_role(port); - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) { - tbt_active_cable_exit_mode(port); - } else { - /* - * Exit Mode process is complete; go to inactive state. - */ - tbt_exit_done(port); - } - break; - case TBT_EXIT_SOP: - set_usb_mux_with_current_data_role(port); - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) - tbt_active_cable_exit_mode(port); - else { - if (TBT_CHK_FLAG(port, TBT_FLAG_RETRY_DONE)) - /* retried enter mode, still failed, give up */ - tbt_exit_done(port); - else - tbt_retry_enter_mode(port); - } - break; - case TBT_EXIT_SOP_PRIME_PRIME: - tbt_prints("exit mode SOP''", port); - tbt_state[port] = TBT_EXIT_SOP_PRIME; - set_usb_mux_with_current_data_role(port); - break; - case TBT_EXIT_SOP_PRIME: - tbt_prints("exit mode SOP'", port); - if (TBT_CHK_FLAG(port, TBT_FLAG_RETRY_DONE)) { - /* - * Exit mode process is complete; go to inactive state. - */ - tbt_exit_done(port); - opos_sop_prime = - pd_alt_mode(port, TCPCI_MSG_SOP_PRIME, - USB_VID_INTEL); - - /* Clear Thunderbolt related signals */ - pd_dfp_exit_mode(port, TCPCI_MSG_SOP_PRIME, - USB_VID_INTEL, opos_sop_prime); - set_usb_mux_with_current_data_role(port); - } else { - tbt_retry_enter_mode(port); - } - break; - case TBT_INACTIVE: - /* - * This can occur if the mode is shutdown because - * the CPU is being turned off, and an exit mode - * command has been sent. - */ - break; - default: - /* Invalid or unexpected negotiation state */ - CPRINTF("%s called with invalid state %d\n", - __func__, tbt_state[port]); - tbt_exit_done(port); - break; - } -} - -void intel_vdm_naked(int port, enum tcpci_msg_type type, uint8_t vdm_cmd) -{ - if (!tbt_response_valid(port, type, "NAK", vdm_cmd)) - return; - - switch (tbt_state[port]) { - case TBT_ENTER_SOP_PRIME: - case TBT_ENTER_SOP_PRIME_PRIME: - case TBT_ENTER_SOP: - /* - * If a request to enter Thunderbolt mode is NAK'ed, this - * likely means the partner is already in Thunderbolt alt mode, - * so request to exit the mode first before retrying the enter - * command. This can happen if the EC is restarted - */ - tbt_state[port] = TBT_EXIT_SOP; - break; - case TBT_ACTIVE: - /* Exit SOP got NAK'ed */ - set_usb_mux_with_current_data_role(port); - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) - tbt_active_cable_exit_mode(port); - else { - tbt_prints("exit mode SOP failed", port); - tbt_state[port] = TBT_INACTIVE; - TBT_CLR_FLAG(port, TBT_FLAG_RETRY_DONE); - } - break; - case TBT_EXIT_SOP: - /* Exit SOP got NAK'ed */ - tbt_prints("exit mode SOP failed", port); - set_usb_mux_with_current_data_role(port); - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) - tbt_active_cable_exit_mode(port); - else { - if (TBT_CHK_FLAG(port, TBT_FLAG_RETRY_DONE)) - /* Retried enter mode, still failed, give up */ - tbt_exit_done(port); - else - tbt_retry_enter_mode(port); - } - break; - case TBT_EXIT_SOP_PRIME_PRIME: - set_usb_mux_with_current_data_role(port); - tbt_prints("exit mode SOP'' failed", port); - tbt_state[port] = TBT_EXIT_SOP_PRIME; - break; - case TBT_EXIT_SOP_PRIME: - set_usb_mux_with_current_data_role(port); - if (TBT_CHK_FLAG(port, TBT_FLAG_RETRY_DONE)) { - /* - * Exit mode process is complete; go to inactive state. - */ - tbt_prints("exit mode SOP' failed", port); - tbt_exit_done(port); - } else { - tbt_retry_enter_mode(port); - } - break; - default: - CPRINTS("C%d: NAK for cmd %d in state %d", port, - vdm_cmd, tbt_state[port]); - tbt_exit_done(port); - break; - } -} - -static bool tbt_mode_is_supported(int port, int vdo_count) -{ - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP); - - if (!disc->identity.idh.modal_support) - return false; - - if (get_tbt_cable_speed(port) < TBT_SS_U31_GEN1) - return false; - - /* - * TBT4 PD Discovery Flow Application Notes Revision 0.9: - * Figure 2: for active cable, SOP' should support - * SVID USB_VID_INTEL to enter Thunderbolt alt mode - */ - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE && - !pd_is_mode_discovered_for_svid( - port, TCPCI_MSG_SOP_PRIME, USB_VID_INTEL)) - return false; - - return true; -} - -int tbt_setup_next_vdm(int port, int vdo_count, uint32_t *vdm, - enum tcpci_msg_type *tx_type) -{ - struct svdm_amode_data *modep; - int vdo_count_ret = 0; - union tbt_mode_resp_cable cable_mode_resp; - - *tx_type = TCPCI_MSG_SOP; - - if (vdo_count < VDO_MAX_SIZE) - return -1; - - switch (tbt_state[port]) { - case TBT_START: - if (!tbt_mode_is_supported(port, vdo_count)) - return 0; - - if (!TBT_CHK_FLAG(port, TBT_FLAG_RETRY_DONE)) - tbt_prints("attempt to enter mode", port); - else - tbt_prints("retry to enter mode", port); - - cable_mode_resp.raw_value = - pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME); - - /* Active cable and LRD cables send Enter Mode SOP' first */ - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE || - cable_mode_resp.tbt_active_passive == TBT_CABLE_ACTIVE) { - vdo_count_ret = enter_tbt_compat_mode(port, - TCPCI_MSG_SOP_PRIME, vdm); - *tx_type = TCPCI_MSG_SOP_PRIME; - tbt_state[port] = TBT_ENTER_SOP_PRIME; - } else { - /* Passive cable send Enter Mode SOP */ - vdo_count_ret = - enter_tbt_compat_mode(port, TCPCI_MSG_SOP, vdm); - tbt_state[port] = TBT_ENTER_SOP; - } - break; - case TBT_ENTER_SOP_PRIME: - vdo_count_ret = - enter_tbt_compat_mode(port, TCPCI_MSG_SOP_PRIME, vdm); - *tx_type = TCPCI_MSG_SOP_PRIME; - break; - case TBT_ENTER_SOP_PRIME_PRIME: - vdo_count_ret = - enter_tbt_compat_mode( - port, TCPCI_MSG_SOP_PRIME_PRIME, vdm); - *tx_type = TCPCI_MSG_SOP_PRIME_PRIME; - break; - case TBT_ENTER_SOP: - vdo_count_ret = - enter_tbt_compat_mode(port, TCPCI_MSG_SOP, vdm); - break; - case TBT_EXIT_SOP: - case TBT_ACTIVE: - /* - * Called to exit Thunderbolt alt mode, either when the mode is - * active and the system is shutting down, or when an initial - * request to enter the mode is NAK'ed. This can happen if EC - * is restarted while Thunderbolt mode is active. - */ - modep = pd_get_amode_data(port, - TCPCI_MSG_SOP, USB_VID_INTEL); - if (!(modep && modep->opos)) - return -1; - - usb_mux_set_safe_mode_exit(port); - - vdm[0] = VDO(USB_VID_INTEL, 1, CMD_EXIT_MODE) | - VDO_OPOS(modep->opos) | - VDO_CMDT(CMDT_INIT) | - VDO_SVDM_VERS( - pd_get_vdo_ver(port, TCPCI_MSG_SOP)); - vdo_count_ret = 1; - break; - case TBT_EXIT_SOP_PRIME_PRIME: - modep = pd_get_amode_data(port, - TCPCI_MSG_SOP_PRIME, USB_VID_INTEL); - if (!(modep && modep->opos)) - return -1; - - usb_mux_set_safe_mode_exit(port); - - vdm[0] = VDO(USB_VID_INTEL, 1, CMD_EXIT_MODE) | - VDO_OPOS(modep->opos) | - VDO_CMDT(CMDT_INIT) | - VDO_SVDM_VERS(pd_get_vdo_ver(port, - TCPCI_MSG_SOP_PRIME_PRIME)); - vdo_count_ret = 1; - *tx_type = TCPCI_MSG_SOP_PRIME_PRIME; - break; - case TBT_EXIT_SOP_PRIME: - modep = pd_get_amode_data(port, - TCPCI_MSG_SOP_PRIME, USB_VID_INTEL); - if (!(modep && modep->opos)) - return -1; - - usb_mux_set_safe_mode_exit(port); - - vdm[0] = VDO(USB_VID_INTEL, 1, CMD_EXIT_MODE) | - VDO_OPOS(modep->opos) | - VDO_CMDT(CMDT_INIT) | - VDO_SVDM_VERS(pd_get_vdo_ver(port, - TCPCI_MSG_SOP_PRIME)); - vdo_count_ret = 1; - *tx_type = TCPCI_MSG_SOP_PRIME; - break; - case TBT_INACTIVE: - /* Thunderbolt mode is inactive */ - return 0; - default: - CPRINTF("%s called with invalid state %d\n", - __func__, tbt_state[port]); - return -1; - } - - return vdo_count_ret; -} |