diff options
Diffstat (limited to 'common/usb_pd_policy.c')
-rw-r--r-- | common/usb_pd_policy.c | 231 |
1 files changed, 223 insertions, 8 deletions
diff --git a/common/usb_pd_policy.c b/common/usb_pd_policy.c index f31f859641..c648016a83 100644 --- a/common/usb_pd_policy.c +++ b/common/usb_pd_policy.c @@ -166,12 +166,10 @@ int pd_charge_from_device(uint16_t vid, uint16_t pid) static struct pd_cable cable[CONFIG_USB_PD_PORT_MAX_COUNT]; -static uint8_t is_transmit_msg_sop_prime(int port) +static bool is_transmit_msg_sop_prime(int port) { - if (IS_ENABLED(CONFIG_USB_PD_DECODE_SOP)) - return !!(cable[port].flags & CABLE_FLAGS_SOP_PRIME_ENABLE); - - return 0; + return (IS_ENABLED(CONFIG_USB_PD_DECODE_SOP) && + (cable[port].flags & CABLE_FLAGS_SOP_PRIME_ENABLE)); } uint8_t is_sop_prime_ready(int port, uint8_t data_role, uint32_t pd_flags) @@ -218,12 +216,119 @@ static int is_vdo_present(int cnt, int index) static void enable_transmit_sop_prime(int port) { - cable[port].flags |= CABLE_FLAGS_SOP_PRIME_ENABLE; + if (IS_ENABLED(CONFIG_USB_PD_DECODE_SOP)) + cable[port].flags |= CABLE_FLAGS_SOP_PRIME_ENABLE; } static void disable_transmit_sop_prime(int port) { - cable[port].flags &= ~CABLE_FLAGS_SOP_PRIME_ENABLE; + if (IS_ENABLED(CONFIG_USB_PD_DECODE_SOP)) + cable[port].flags &= ~CABLE_FLAGS_SOP_PRIME_ENABLE; +} + +static bool is_tbt_compat_enabled(int port) +{ + return (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE) && + (cable[port].flags & CABLE_FLAGS_TBT_COMPAT_ENABLE)); +} + +static void enable_tbt_compat_mode(int port) +{ + if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE)) + cable[port].flags |= CABLE_FLAGS_TBT_COMPAT_ENABLE; +} + +static inline void disable_tbt_compat_mode(int port) +{ + if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE)) + cable[port].flags &= ~CABLE_FLAGS_TBT_COMPAT_ENABLE; +} + +/* + * TODO (b/146006708): Make the below three functions independent of + * Thunderbolt-compatible mode enabled as these USB PD 3.0 VDO responses + * can be used to identify other cable attributes like cable speed. + */ +static bool is_cable_superspeed(int port) +{ + if (is_tbt_compat_enabled(port) && + IS_ENABLED(CONFIG_USB_PD_DECODE_SOP)) { + /* + * Bit 4 gives if USB SS is supported for active cables + * for Rev 3.0 + * Ref: PD Spec 3.0 Active Cable VDO 2 + */ + if (IS_ENABLED(CONFIG_USB_PD_REV30) && + (cable[port].type == IDH_PTYPE_ACABLE)) + return !!cable[port].attr2.a2_rev30.usb_ss_support; + + /* + * Bits 2:0 gives USB SS support for passive cable + * for both Rev2.0 and Rev3.0 + * Ref: PD Spec 3.0 Passive cable VDO and + * Spec 2.0 Passive cable VDO + * + * For rev2.0 active cable, bits 2:0 give USB SS support + * (Ref: spec 2.0 Passive cable VDO) + */ + return !!(cable[port].attr.rev20.ss & USB_SS_U31_GEN2); + } + return false; +} + +/* Check if product supports any Modal Operation (Alternate Modes) */ +static bool is_modal(int port, int cnt, uint32_t *payload) +{ + return (is_tbt_compat_enabled(port) && + is_vdo_present(cnt, VDO_INDEX_IDH) && + PD_IDH_IS_MODAL(payload[VDO_INDEX_IDH])); +} + +static bool is_intel_svid(int port, uint32_t *payload) +{ + /* + * Check if SVID0 = USB_VID_INTEL + * (Ref: USB Type-C cable and connector specification, Table F-9) + * + * TODO (b/146081601): For the Discover SVIDs, responder may present + * the SVIDs in any order in the VDOs. Modify the code to check + * Intel SVID in all the VDOs. + */ + if (is_tbt_compat_enabled(port)) { + /* + * errata: All the Thunderbolt certified cables and docks + * tested have SVID1 = 0x8087 + */ + if (is_transmit_msg_sop_prime(port)) + return PD_VDO_SVID_SVID1(payload[VDO_INDEX_IDH]) == + USB_VID_INTEL; + + return PD_VDO_SVID_SVID0(payload[VDO_INDEX_IDH]) == + USB_VID_INTEL; + } + return false; +} + +static inline bool is_tbt_compat_mode(int port, int cnt, uint32_t *payload) +{ + /* + * Ref: USB Type-C cable and connector specification + * F.2.5 TBT3 Device Discover Mode Responses + */ + + return is_tbt_compat_enabled(port) && + is_vdo_present(cnt, VDO_INDEX_IDH) && + PD_VDO_RESP_MODE_INTEL_TBT(payload[VDO_INDEX_IDH]); +} + +static inline void limit_cable_speed(int port) +{ + cable[port].flags |= CABLE_FLAGS_TBT_COMPAT_LIMIT_SPEED; +} + +static inline bool is_limit_cable_speed(int port) +{ + return !!(cable[port].flags & CABLE_FLAGS_TBT_COMPAT_LIMIT_SPEED); } void pd_dfp_pe_init(int port) @@ -238,6 +343,12 @@ static void dfp_consume_identity(int port, int cnt, uint32_t *payload) (cnt - 1) * sizeof(uint32_t)); pd_dfp_pe_init(port); memcpy(&pe[port].identity, payload + 1, identity_size); + /* + * Enable Thunderbolt-compatible mode to further check if the + * cable and the port partner support Thunderbolt-compatible mode. + */ + enable_tbt_compat_mode(port); + switch (ptype) { case IDH_PTYPE_AMA: /* Leave vbus ON if the following macro is false */ @@ -444,6 +555,18 @@ uint32_t pd_dfp_enter_mode(int port, uint16_t svid, int opos) return VDO(modep->fx->svid, 1, CMD_ENTER_MODE | VDO_OPOS(modep->opos)); } +static int enter_tbt_compat_mode(int port, uint32_t *payload) +{ + /* If Passive cable Enter mode SOP */ + if (get_usb_pd_mux_cable_type(port) == IDH_PTYPE_PCABLE) + disable_transmit_sop_prime(port); + + /* Enter Thunderbolt-compatible mode */ + payload[0] = pd_dfp_enter_mode(port, USB_VID_INTEL, 1); + + return !!payload[0]; +} + static int validate_mode_request(struct svdm_amode_data *modep, uint16_t svid, int opos) { @@ -642,6 +765,40 @@ DECLARE_CONSOLE_COMMAND(pe, command_pe, "USB PE"); #endif /* CONFIG_CMD_USB_PD_PE */ +static int process_tbt_compat_discover_modes(int port, uint32_t *payload) +{ + int rsize; + + /* + * For active cables, Enter mode: SOP', SOP'', SOP + * Ref: USB Type-C Cable and Connector Specification, figure F-1: TBT3 + * Discovery Flow and Section F.2.7 TBT3 Cable Enter Mode Command. + */ + /* + * TODO: Support for entering Thunderbolt-compatible mode for + * active cables. + */ + if (is_transmit_msg_sop_prime(port)) { + /* Store Discover Mode SOP' response */ + cable[port].cable_mode_resp.raw_value = payload[1]; + if (is_limit_cable_speed(port)) + cable[port].cable_mode_resp.tbt_cable_speed = + USB3_GEN1_USB4_GEN2; + + rsize = enter_tbt_compat_mode(port, payload); + disable_transmit_sop_prime(port); + } else { + /* Store Discover Mode SOP response */ + cable[port].dev_mode_resp.raw_value = payload[1]; + + /* Discover modes for SOP' */ + pe[port].svid_idx--; + rsize = dfp_discover_modes(port, payload); + enable_transmit_sop_prime(port); + } + + return rsize; +} #endif /* CONFIG_USB_PD_ALT_MODE_DFP */ int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload) @@ -720,6 +877,13 @@ int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload) if (is_transmit_msg_sop_prime(port)) { /* Store cable type */ dfp_consume_cable_response(port, cnt, payload); + /* + * Disable Thunderbolt-compatible mode if cable + * doesn't support superspeed + */ + if (!is_cable_superspeed(port)) + disable_tbt_compat_mode(port); + disable_transmit_sop_prime(port); rsize = dfp_discover_svids(payload); /* Received a SOP Discover Ident Message */ @@ -729,6 +893,13 @@ int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload) if (!cable[port].is_identified) { rsize = dfp_discover_ident(payload); enable_transmit_sop_prime(port); + /* + * Disable Thunderbolt-compatible mode + * if modal op not supported + */ + } else if (!is_modal(port, cnt, payload)) { + disable_tbt_compat_mode(port); + rsize = dfp_discover_svids(payload); } } else { dfp_consume_identity(port, cnt, payload); @@ -743,10 +914,42 @@ int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload) break; case CMD_DISCOVER_SVID: dfp_consume_svids(port, cnt, payload); + /* + * Check if 0x8087 is received for Discover SVID SOP. + * If not, disable Thunderbolt-compatible mode + * Ref: USB Type-C Cable and Connector Specification, + * figure F-1: TBT3 Discovery Flow + */ + if (is_intel_svid(port, payload)) { + if (!is_transmit_msg_sop_prime(port)) { + rsize = dfp_discover_svids(payload); + enable_transmit_sop_prime(port); + break; + } + /* + * If 0x8087 is not received for Discover SVID SOP' + * limit to TBT passive Gen 2 cable + * Ref: USB Type-C Cable and Connector Specification, + * figure F-1: TBT3 Discovery Flow + */ + } else if (is_tbt_compat_enabled(port) && + is_transmit_msg_sop_prime(port)) { + limit_cable_speed(port); + } else { + disable_tbt_compat_mode(port); + } + rsize = dfp_discover_modes(port, payload); + disable_transmit_sop_prime(port); break; case CMD_DISCOVER_MODES: dfp_consume_modes(port, cnt, payload); + if (is_tbt_compat_mode(port, cnt, payload)) { + rsize = process_tbt_compat_discover_modes( + port, payload); + break; + } + rsize = dfp_discover_modes(port, payload); /* enter the default mode for DFP */ if (!rsize) { @@ -756,7 +959,19 @@ int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload) } break; case CMD_ENTER_MODE: - if (!modep) { + /* No response once device (and cable) acks */ + if (is_tbt_compat_enabled(port)) { + /* + * Update Mux state to Thunderbolt-compatible + * mode. + */ + set_tbt_compat_mode_ready(port); + rsize = 0; + /* + * Continue with PD flow if Thunderbolt-compatible mode + * is disabled. + */ + } else if (!modep) { rsize = 0; } else { if (!modep->opos) |