diff options
-rw-r--r-- | common/usb_pd_alt_mode_dfp.c | 24 | ||||
-rw-r--r-- | common/usb_pd_host_cmd.c | 4 | ||||
-rw-r--r-- | common/usbc/tbt_alt_mode.c | 28 | ||||
-rw-r--r-- | common/usbc/usb_mode.c | 171 | ||||
-rw-r--r-- | common/usbc/usb_pd_dpm.c | 32 | ||||
-rw-r--r-- | common/usbc/usb_pe_drp_sm.c | 20 | ||||
-rw-r--r-- | include/usb_mode.h | 18 | ||||
-rw-r--r-- | include/usb_pd.h | 2 | ||||
-rw-r--r-- | include/usb_pd_vdo.h | 13 | ||||
-rw-r--r-- | include/usb_tbt_alt_mode.h | 9 |
10 files changed, 267 insertions, 54 deletions
diff --git a/common/usb_pd_alt_mode_dfp.c b/common/usb_pd_alt_mode_dfp.c index b7d81e2e64..a2dc62afd8 100644 --- a/common/usb_pd_alt_mode_dfp.c +++ b/common/usb_pd_alt_mode_dfp.c @@ -929,6 +929,7 @@ uint32_t get_enter_usb_msg_payload(int port) */ union enter_usb_data_obj eudo; struct pd_discovery *disc; + union tbt_mode_resp_cable cable_mode_resp; if (!IS_ENABLED(CONFIG_USB_PD_USB4)) return 0; @@ -939,13 +940,22 @@ uint32_t get_enter_usb_msg_payload(int port) eudo.usb3_drd_cap = IS_ENABLED(CONFIG_USB_PD_USB32_DRD); eudo.cable_speed = get_usb4_cable_speed(port); - if (is_rev3_vdo(port, TCPC_TX_SOP_PRIME) && - (disc->identity.idh.product_type == IDH_PTYPE_ACABLE)) { - eudo.cable_type = - (disc->identity.product_t2.a2_rev30.active_elem == - ACTIVE_RETIMER) ? CABLE_TYPE_ACTIVE_RETIMER : - CABLE_TYPE_ACTIVE_REDRIVER; - /* TODO: Add eudo.cable_type for Revisiosn 2 active cables */ + if (disc->identity.idh.product_type == IDH_PTYPE_ACABLE) { + if (is_rev3_vdo(port, TCPC_TX_SOP_PRIME)) { + enum retimer_active_element active_element = + disc->identity.product_t2.a2_rev30.active_elem; + eudo.cable_type = active_element == ACTIVE_RETIMER ? + CABLE_TYPE_ACTIVE_RETIMER : + CABLE_TYPE_ACTIVE_REDRIVER; + } else { + cable_mode_resp.raw_value = + pd_get_tbt_mode_vdo(port, TCPC_TX_SOP_PRIME); + + eudo.cable_type = + cable_mode_resp.retimer_type == USB_RETIMER ? + CABLE_TYPE_ACTIVE_RETIMER : + CABLE_TYPE_ACTIVE_REDRIVER; + } } else { eudo.cable_type = CABLE_TYPE_PASSIVE; } diff --git a/common/usb_pd_host_cmd.c b/common/usb_pd_host_cmd.c index 11174ee7d4..62b7045630 100644 --- a/common/usb_pd_host_cmd.c +++ b/common/usb_pd_host_cmd.c @@ -372,7 +372,9 @@ static enum ec_status hc_usb_pd_control(struct host_cmd_handler_args *args) if (mux_state & USB_PD_MUX_USB4_ENABLED) { r_v2->cable_speed = get_usb4_cable_speed(p->port); - } else if (mux_state & USB_PD_MUX_TBT_COMPAT_ENABLED) { + } + if (mux_state & USB_PD_MUX_TBT_COMPAT_ENABLED || + mux_state & USB_PD_MUX_USB4_ENABLED) { r_v2->cable_speed = get_tbt_cable_speed(p->port); r_v2->cable_gen = diff --git a/common/usbc/tbt_alt_mode.c b/common/usbc/tbt_alt_mode.c index 1319263e74..0785376355 100644 --- a/common/usbc/tbt_alt_mode.c +++ b/common/usbc/tbt_alt_mode.c @@ -69,8 +69,9 @@ * 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_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]; @@ -115,6 +116,7 @@ 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) @@ -129,10 +131,16 @@ bool tbt_entry_is_done(int port) 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) { tbt_state[port] = 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); @@ -147,6 +155,14 @@ void tbt_exit_mode_request(int port) { 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) + tbt_state[port] = TBT_EXIT_SOP_PRIME_PRIME; } static bool tbt_response_valid(int port, enum tcpm_transmit_type type, @@ -203,12 +219,15 @@ void intel_vdm_acked(int port, enum tcpm_transmit_type type, int vdo_count, switch (tbt_state[port]) { case TBT_ENTER_SOP_PRIME: - if (disc->identity.product_t1.a_rev20.sop_p_p) + if (disc->identity.product_t1.a_rev20.sop_p_p) { tbt_state[port] = TBT_ENTER_SOP_PRIME_PRIME; - else + } else { + TBT_SET_FLAG(port, TBT_FLAG_CABLE_ENTRY_DONE); tbt_state[port] = TBT_ENTER_SOP; + } break; case TBT_ENTER_SOP_PRIME_PRIME: + TBT_SET_FLAG(port, TBT_FLAG_CABLE_ENTRY_DONE); tbt_state[port] = TBT_ENTER_SOP; break; case TBT_ENTER_SOP: @@ -315,6 +334,7 @@ void intel_vdm_naked(int port, enum tcpm_transmit_type type, uint8_t vdm_cmd) 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); diff --git a/common/usbc/usb_mode.c b/common/usbc/usb_mode.c index c48002f0d1..818f1e18c3 100644 --- a/common/usbc/usb_mode.c +++ b/common/usbc/usb_mode.c @@ -19,7 +19,6 @@ #include "usb_mux.h" #include "usb_pd.h" #include "usb_pd_dpm.h" -#include "usb_pd_tbt.h" #include "usb_pe_sm.h" #include "usbc_ppc.h" @@ -37,11 +36,63 @@ enum usb4_mode_status { }; enum usb4_states { + USB4_START, USB4_ENTER_SOP, + USB4_ENTER_SOP_PRIME, + USB4_ENTER_SOP_PRIME_PRIME, USB4_ACTIVE, USB4_INACTIVE, USB4_STATE_COUNT, }; + +/* + * USB4 flow for Active cable + * + * Structured + * VDM version + * (cable revision)-- <2.0 -------->| + * | | + * >=2.0 | + * | | + * VDO version---- <1.3 -------> Modal op? -- N --| + * (B21:23 of | | + * Discover ID SOP'- y | + * Active cable VDO1) | | + * | TBT SVID? -- N --| + * >=1.3 | | + * | y | + * Cable USB4 support? - N | | + * | | Gen4 cable? - N - Skip USB4 mode entry + * y Skip USB4 | + * | mode entry | + * Enter USB4 y + * (SOP',SOP'',SOP) | + * | + * |<---- NAK ----- Enter mode TBT SOP'<---| + * | | | + * | ACK | + * | | | + * |<---- NAK ----- Enter mode TBT SOP'' | + * | | | + * Exit TBT mode SOP ACK | + * | | | + * ACK/NAK Enter USB4 mode | + * | SOP | + * Exit TBT mode SOP'' | + * | | + * ACK/NAK | + * | | + * Exit TBT mode SOP' | + * | | + * ACK/NAK | + * | | + * |--------Retry done? ---- N ------------| + * | + * y + * | + * Skip USB4 mode entry + */ + static enum usb4_states usb4_state[CONFIG_USB_PD_PORT_MAX_COUNT]; static void usb4_debug_prints(int port, enum usb4_mode_status usb4_status) @@ -58,7 +109,7 @@ bool enter_usb_entry_is_done(int port) void enter_usb_init(int port) { - usb4_state[port] = USB4_ENTER_SOP; + usb4_state[port] = USB4_START; } void enter_usb_failed(int port) @@ -87,30 +138,86 @@ static bool enter_usb_response_valid(int port, enum tcpm_transmit_type type) return true; } -bool enter_usb_is_capable(int port) +bool enter_usb_port_partner_is_capable(int port) { const struct pd_discovery *disc = pd_get_am_discovery(port, TCPC_TX_SOP); - /* - * TODO: b/156749387 Add support for entering the USB4 mode with an - * active cable. - */ - if (!IS_ENABLED(CONFIG_USB_PD_USB4) || - !PD_PRODUCT_IS_USB4(disc->identity.product_t1.raw_value) || - get_usb4_cable_speed(port) < USB_R30_SS_U32_U40_GEN1 || - usb4_state[port] == USB4_INACTIVE || - get_usb_pd_cable_type(port) != IDH_PTYPE_PCABLE) + + if (usb4_state[port] == USB4_INACTIVE) + return false; + + if (!PD_PRODUCT_IS_USB4(disc->identity.product_t1.raw_value)) return false; return true; } +bool enter_usb_cable_is_capable(int port) +{ + /* TODO: b/156749387 Add support for LRD cable */ + + if (get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE) { + if (get_usb4_cable_speed(port) < USB_R30_SS_U32_U40_GEN1) + return false; + } else if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) { + struct pd_discovery *disc_sop_prime = + pd_get_am_discovery(port, TCPC_TX_SOP_PRIME); + + if (pd_get_vdo_ver(port, TCPC_TX_SOP_PRIME) >= VDM_VER20 && + disc_sop_prime->identity.product_t1.a_rev30.vdo_ver >= + VDO_VERSION_1_3) { + union active_cable_vdo2_rev30 a2_rev30 = + disc_sop_prime->identity.product_t2.a2_rev30; + /* + * For VDM version >= 2.0 and VD0 version is >= 1.3, + * do not enter USB4 mode if the cable isn't USB4 + * capable. + */ + if (a2_rev30.usb_40_support == USB4_NOT_SUPPORTED) + return false; + /* + * For VDM version < 2.0 or VDO version < 1.3, do not enter USB4 + * mode if the cable - + * doesn't support modal operation or + * doesn't support Intel SVID or + * doesn't have rounded support. + */ + } else { + const struct pd_discovery *disc = + pd_get_am_discovery(port, TCPC_TX_SOP); + union tbt_mode_resp_cable cable_mode_resp = { + .raw_value = pd_get_tbt_mode_vdo(port, + TCPC_TX_SOP_PRIME) }; + + if (!disc->identity.idh.modal_support || + !pd_is_mode_discovered_for_svid(port, + TCPC_TX_SOP_PRIME, USB_VID_INTEL) || + cable_mode_resp.tbt_rounded != + TBT_GEN3_GEN4_ROUNDED_NON_ROUNDED) + return false; + } + } + return true; +} + void enter_usb_accepted(int port, enum tcpm_transmit_type type) { + struct pd_discovery *disc; + if (!enter_usb_response_valid(port, type)) return; switch (usb4_state[port]) { + case USB4_ENTER_SOP_PRIME: + disc = pd_get_am_discovery(port, TCPC_TX_SOP_PRIME); + if (disc->identity.product_t1.a_rev20.sop_p_p) + usb4_state[port] = USB4_ENTER_SOP_PRIME_PRIME; + else + usb4_state[port] = USB4_ENTER_SOP; + break; + case USB4_ENTER_SOP_PRIME_PRIME: + usb4_state[port] = USB4_ENTER_SOP; + break; case USB4_ENTER_SOP: /* Connect the SBU and USB lines to the connector */ if (IS_ENABLED(CONFIG_USBC_PPC_SBU)) @@ -140,24 +247,44 @@ void enter_usb_rejected(int port, enum tcpm_transmit_type type) enter_usb_failed(port); } -uint32_t enter_usb_setup_next_msg(int port) +uint32_t enter_usb_setup_next_msg(int port, enum tcpm_transmit_type *type) { + struct pd_discovery *disc_sop_prime; + switch (usb4_state[port]) { + case USB4_START: + disc_sop_prime = pd_get_am_discovery(port, TCPC_TX_SOP_PRIME); + + if (pd_get_vdo_ver(port, TCPC_TX_SOP_PRIME) < VDM_VER20 || + disc_sop_prime->identity.product_t1.a_rev30.vdo_ver < + VDO_VERSION_1_3 || + get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE) { + usb4_state[port] = USB4_ENTER_SOP; + /* Ref: TBT4 PD Discover Flow */ + usb_mux_set_safe_mode(port); + } else { + usb4_state[port] = USB4_ENTER_SOP_PRIME; + *type = TCPC_TX_SOP_PRIME; + } + break; + case USB4_ENTER_SOP_PRIME: + *type = TCPC_TX_SOP_PRIME; + break; + case USB4_ENTER_SOP_PRIME_PRIME: + *type = TCPC_TX_SOP_PRIME_PRIME; + break; case USB4_ENTER_SOP: + *type = TCPC_TX_SOP; /* - * Set the USB mux to safe state to avoid damaging the mux pins - * since, they are being re-purposed for USB4. - * - * TODO: b/141363146 Remove once data reset feature is in place + * Set the USB mux to safe state to avoid damaging the mux pins, + * since they are being re-purposed for USB4. */ usb_mux_set_safe_mode(port); - - usb4_state[port] = USB4_ENTER_SOP; - return get_enter_usb_msg_payload(port); + break; case USB4_ACTIVE: return -1; default: - break; + return 0; } - return 0; + return get_enter_usb_msg_payload(port); } diff --git a/common/usbc/usb_pd_dpm.c b/common/usbc/usb_pd_dpm.c index 69956c3685..193ea9802c 100644 --- a/common/usbc/usb_pd_dpm.c +++ b/common/usbc/usb_pd_dpm.c @@ -216,10 +216,28 @@ static void dpm_attempt_mode_entry(int port) } /* Check if the device and cable support USB4. */ - if (IS_ENABLED(CONFIG_USB_PD_USB4) && enter_usb_is_capable(port) && - dpm_mode_entry_requested(port, TYPEC_MODE_USB4)) { - pd_dpm_request(port, DPM_REQUEST_ENTER_USB); - return; + if (IS_ENABLED(CONFIG_USB_PD_USB4) && + enter_usb_port_partner_is_capable(port) && + enter_usb_cable_is_capable(port) && + dpm_mode_entry_requested(port, TYPEC_MODE_USB4)) { + struct pd_discovery *disc_sop_prime = + pd_get_am_discovery(port, TCPC_TX_SOP_PRIME); + /* + * Enter USB mode if - + * 1. It's a passive cable or + * 2. It's an active cable with VDM version >= 2.0 and + * VDO version >= 1.3 or + * 3. The cable has entered Thunderbolt mode. + */ + if (get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE || + (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE && + pd_get_vdo_ver(port, TCPC_TX_SOP_PRIME) >= VDM_VER20 && + disc_sop_prime->identity.product_t1.a_rev30.vdo_ver >= + VDO_VERSION_1_3) || + tbt_cable_entry_is_done(port)) { + pd_dpm_request(port, DPM_REQUEST_ENTER_USB); + return; + } } /* If not, check if they support Thunderbolt alt mode. */ @@ -284,9 +302,13 @@ static void dpm_attempt_mode_exit(int port) int vdo_count = 0; enum tcpm_transmit_type tx_type = TCPC_TX_SOP; - /* TODO(b/156749387): Support Data Reset for exiting USB4. */ if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE) && tbt_is_active(port)) { + /* + * When the port is in USB4 mode and receives an exit request, + * it leaves USB4 SOP in active state. + * TODO(b/156749387): Support Data Reset for exiting USB4 SOP. + */ CPRINTS("C%d: TBT teardown", port); tbt_exit_mode_request(port); vdo_count = tbt_setup_next_vdm(port, VDO_MAX_SIZE, &vdm, diff --git a/common/usbc/usb_pe_drp_sm.c b/common/usbc/usb_pe_drp_sm.c index 37afaa9c0e..139e8454b0 100644 --- a/common/usbc/usb_pe_drp_sm.c +++ b/common/usbc/usb_pe_drp_sm.c @@ -5513,28 +5513,32 @@ static void pe_enter_usb_entry(int port) return; } - usb4_payload = enter_usb_setup_next_msg(port); - /* Port is already in USB4 mode, do not send enter USB message again */ - if (usb4_payload < 0) { + if (enter_usb_entry_is_done(port)) { pe_set_ready_state(port); return; } + if ((pe[port].tx_type == TCPC_TX_SOP_PRIME || + pe[port].tx_type == TCPC_TX_SOP_PRIME_PRIME) && + !tc_is_vconn_src(port)) { + if (port_try_vconn_swap(port)) + return; + } + + pe[port].tx_type = TCPC_TX_SOP; + usb4_payload = enter_usb_setup_next_msg(port, &pe[port].tx_type); + if (!usb4_payload) { enter_usb_failed(port); pe_set_ready_state(port); return; } - /* - * TODO: b/156749387 In case of Enter USB SOP'/SOP'', check if the port - * is the VCONN source, if not, request for a VCONN swap. - */ tx_emsg[port].len = sizeof(usb4_payload); memcpy(tx_emsg[port].buf, &usb4_payload, tx_emsg[port].len); - send_data_msg(port, TCPC_TX_SOP, PD_DATA_ENTER_USB); + send_data_msg(port, pe[port].tx_type, PD_DATA_ENTER_USB); pe_sender_response_msg_entry(port); } diff --git a/include/usb_mode.h b/include/usb_mode.h index 728d7abc44..69170594ab 100644 --- a/include/usb_mode.h +++ b/include/usb_mode.h @@ -40,13 +40,22 @@ bool enter_usb_entry_is_done(int port); void enter_usb_failed(int port); /* - * Returns True if port, port partner and cable supports USB4 mode + * Returns True if port partner supports USB4 mode * * @param port USB-C port number - * @return True if USB4 mode is supported, + * @return True if USB4 mode is supported by the port partner, * False otherwise */ -bool enter_usb_is_capable(int port); +bool enter_usb_port_partner_is_capable(int port); + +/* + * Returns True if cable supports USB4 mode + * + * @param port USB-C port number + * @return True if USB4 mode is supported by the cable, + * False otherwise + */ +bool enter_usb_cable_is_capable(int port); /* * Handles accepted USB4 response @@ -68,7 +77,8 @@ void enter_usb_rejected(int port, enum tcpm_transmit_type type); * Constructs the next USB4 EUDO that should be sent. * * @param port USB-C port number + * @param type Transmit type (SOP, SOP', SOP'') for request */ -uint32_t enter_usb_setup_next_msg(int port); +uint32_t enter_usb_setup_next_msg(int port, enum tcpm_transmit_type *type); #endif diff --git a/include/usb_pd.h b/include/usb_pd.h index c2eea9bd76..01c061c8db 100644 --- a/include/usb_pd.h +++ b/include/usb_pd.h @@ -2146,7 +2146,7 @@ bool is_tbt_cable_superspeed(int port); * port supports lesser speed than the cable * * @param port USB-C port number - * @return thunderbolt-cable cable speed + * @return Thunderbolt cable speed */ enum tbt_compat_cable_speed get_tbt_cable_speed(int port); diff --git a/include/usb_pd_vdo.h b/include/usb_pd_vdo.h index 2cecec5d84..e0b1bf65dc 100644 --- a/include/usb_pd_vdo.h +++ b/include/usb_pd_vdo.h @@ -243,6 +243,10 @@ union passive_cable_vdo_rev30 { * 011b = [USB4] Gen3 * 100b..111b = Reserved, Shall Not be used */ +enum vdo_version { + VDO_VERSION_1_3 = 3, +}; + union active_cable_vdo1_rev30 { struct { enum usb_rev30_ss ss: 3; @@ -257,7 +261,7 @@ union active_cable_vdo1_rev30 { uint32_t reserved0 : 1; uint32_t connector : 2; uint32_t reserved1 : 1; - uint32_t vdo_version : 3; + enum vdo_version vdo_ver : 3; uint32_t fw_version : 4; uint32_t hw_version : 4; }; @@ -326,6 +330,11 @@ enum active_cable_usb2_support { USB2_NOT_SUPPORTED, }; +enum active_cable_usb4_support { + USB4_SUPPORTED, + USB4_NOT_SUPPORTED, +}; + union active_cable_vdo2_rev30 { struct { uint8_t usb_gen : 1; @@ -335,7 +344,7 @@ union active_cable_vdo2_rev30 { uint8_t usb_32_support : 1; enum active_cable_usb2_support usb_20_support : 1; uint8_t usb_20_hub_hop : 2; - uint8_t usb_40_support : 1; + enum active_cable_usb4_support usb_40_support : 1; enum retimer_active_element active_elem : 1; uint8_t physical_conn : 1; uint8_t u3_to_u0 : 1; diff --git a/include/usb_tbt_alt_mode.h b/include/usb_tbt_alt_mode.h index 73a776e3dc..de203667b0 100644 --- a/include/usb_tbt_alt_mode.h +++ b/include/usb_tbt_alt_mode.h @@ -40,6 +40,15 @@ void tbt_exit_mode_request(int port); bool tbt_entry_is_done(int port); /* + * Checks if the cable entry into Thunderbolt alternate mode is done + * + * @param port USB-C port number + * @return True if TBT_FLAG_CABLE_ENTRY_DONE is set + * False otherwise + */ +bool tbt_cable_entry_is_done(int port); + +/* * Returns True if Thunderbolt mode is not in inactive state * * @param port USB-C port number |