diff options
Diffstat (limited to 'common/usb_pd_policy.c')
-rw-r--r-- | common/usb_pd_policy.c | 273 |
1 files changed, 268 insertions, 5 deletions
diff --git a/common/usb_pd_policy.c b/common/usb_pd_policy.c index feb022d911..1b582b75d4 100644 --- a/common/usb_pd_policy.c +++ b/common/usb_pd_policy.c @@ -236,6 +236,86 @@ enum tbt_compat_rounded_support get_tbt_rounded_support(int port) return cable[port].cable_mode_resp.tbt_rounded; } +static enum usb_rev30_ss get_usb4_cable_speed(int port) +{ + if ((cable[port].rev == PD_REV30) && + (get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE) && + ((cable[port].attr.p_rev30.ss != USB_R30_SS_U32_U40_GEN2) || + !IS_ENABLED(CONFIG_USB_PD_TBT_GEN3_CAPABLE))) { + return cable[port].attr.p_rev30.ss; + } + + /* + * Converting Thunderolt-Compatible cable speed to equivalent USB4 cable + * speed. + */ + return cable[port].cable_mode_resp.tbt_cable_speed == TBT_SS_TBT_GEN3 ? + USB_R30_SS_U40_GEN3 : USB_R30_SS_U32_U40_GEN2; +} + +uint32_t get_enter_usb_msg_payload(int port) +{ + /* + * Ref: USB Power Delivery Specification Revision 3.0, Version 2.0 + * Table 6-47 Enter_USB Data Object + */ + union enter_usb_data_obj eudo; + + if (!IS_ENABLED(CONFIG_USB_PD_USB4)) + return 0; + + eudo.mode = USB_PD_40; + eudo.usb4_drd_cap = IS_ENABLED(CONFIG_USB_PD_USB4); + eudo.usb3_drd_cap = IS_ENABLED(CONFIG_USB_PD_USB32); + eudo.cable_speed = get_usb4_cable_speed(port); + + if ((cable[port].rev == PD_REV30) && + (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE)) { + eudo.cable_type = (cable[port].attr2.a2_rev30.active_elem == + ACTIVE_RETIMER) ? CABLE_TYPE_ACTIVE_RETIMER : + CABLE_TYPE_ACTIVE_REDRIVER; + /* TODO: Add eudo.cable_type for Revisiosn 2 active cables */ + } else { + eudo.cable_type = CABLE_TYPE_PASSIVE; + } + + switch (cable[port].attr.p_rev20.vbus_cur) { + case USB_VBUS_CUR_3A: + eudo.cable_current = USB4_CABLE_CURRENT_3A; + break; + case USB_VBUS_CUR_5A: + eudo.cable_current = USB4_CABLE_CURRENT_5A; + break; + default: + eudo.cable_current = USB4_CABLE_CURRENT_INVALID; + break; + } + eudo.pcie_supported = IS_ENABLED(CONFIG_USB_PD_PCIE_TUNNELING); + eudo.dp_supported = IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP); + eudo.tbt_supported = IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE); + eudo.host_present = 1; + + return eudo.raw_value; +} + +bool should_enter_usb4_mode(int port) +{ + return IS_ENABLED(CONFIG_USB_PD_USB4) && + cable[port].flags & CABLE_FLAGS_ENTER_USB_MODE; +} + +void enable_enter_usb4_mode(int port) +{ + if (IS_ENABLED(CONFIG_USB_PD_USB4)) + cable[port].flags |= CABLE_FLAGS_ENTER_USB_MODE; +} + +void disable_enter_usb4_mode(int port) +{ + if (IS_ENABLED(CONFIG_USB_PD_USB4)) + cable[port].flags &= ~CABLE_FLAGS_ENTER_USB_MODE; +} + #ifdef CONFIG_USB_PD_ALT_MODE #ifdef CONFIG_USB_PD_ALT_MODE_DFP @@ -376,6 +456,118 @@ static inline bool is_limit_tbt_cable_speed(int port) return !!(cable[port].flags & CABLE_FLAGS_TBT_COMPAT_LIMIT_SPEED); } +static inline bool is_usb4_mode_enabled(int port) +{ + return (IS_ENABLED(CONFIG_USB_PD_USB4) && + (cable[port].flags & CABLE_FLAGS_USB4_CAPABLE)); +} + +static inline void enable_usb4_mode(int port) +{ + if (IS_ENABLED(CONFIG_USB_PD_USB4)) + cable[port].flags |= CABLE_FLAGS_USB4_CAPABLE; +} + +static inline void disable_usb4_mode(int port) +{ + if (IS_ENABLED(CONFIG_USB_PD_USB4)) + cable[port].flags &= ~CABLE_FLAGS_USB4_CAPABLE; +} + +/* + * Ref: USB Type-C Cable and Connector Specification + * Figure 5-1 USB4 Discovery and Entry Flow Model. + * + * Note: USB Type-C Cable and Connector Specification + * doesn't include details for Revision 2 cables. + * + * Passive Cable + * | + * ----------------------------------- + * | | + * Revision 2 Revision 3 + * USB Signalling USB Signalling + * | | + * ------------------ ------------------------- + * | | | | | | | + * USB2.0 USB3.1 USB3.1 USB3.2 USB4 USB3.2 USB2 + * | Gen1 Gen1 Gen2 Gen2 Gen3 Gen1 | + * | | | | | | Exit + * -------- ------------ -------- USB4 + * | | | Discovery. + * Exit Is DFP Gen3 Capable? Enter USB4 + * USB4 | with respective + * Discovery. --- No ---|--- Yes --- cable speed. + * | | + * Enter USB4 with Is Cable TBT3 + * respective cable | + * speed. --- No ---|--- Yes --- + * | | + * Enter USB4 with Enter USB4 with + * TBT Gen2 passive TBT Gen3 passive + * cable. cable. + * + */ +static bool is_cable_ready_to_enter_usb4(int port, int cnt) +{ + /* TODO: USB4 enter mode for Active cables */ + if (IS_ENABLED(CONFIG_USB_PD_USB4) && + (get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE) && + is_vdo_present(cnt, VDO_INDEX_PTYPE_CABLE1)) { + switch (cable[port].rev) { + case PD_REV30: + switch (cable[port].attr.p_rev30.ss) { + case USB_R30_SS_U40_GEN3: + case USB_R30_SS_U32_U40_GEN1: + return true; + case USB_R30_SS_U32_U40_GEN2: + /* Check if DFP is Gen 3 capable */ + if (IS_ENABLED(CONFIG_USB_PD_TBT_GEN3_CAPABLE)) + return false; + return true; + default: + disable_usb4_mode(port); + return false; + } + case PD_REV20: + switch (cable[port].attr.p_rev20.ss) { + case USB_R20_SS_U31_GEN1_GEN2: + /* Check if DFP is Gen 3 capable */ + if (IS_ENABLED(CONFIG_USB_PD_TBT_GEN3_CAPABLE)) + return false; + return true; + default: + disable_usb4_mode(port); + return false; + } + default: + disable_usb4_mode(port); + } + } + return false; +} + +static bool is_usb4_vdo(int port, int cnt, uint32_t *payload) +{ + enum idh_ptype ptype = PD_IDH_PTYPE(payload[VDO_I(PRODUCT)]); + + /* + * Product types Hub and peripheral should use UFP product vdos + * Reference Table 6-30 USB PD spec 3.2. + */ + if (ptype == IDH_PTYPE_HUB || ptype == IDH_PTYPE_PERIPH) { + /* + * Ref: USB Type-C Cable and Connector Specification + * Figure 5-1 USB4 Discovery and Entry Flow Model + * Device USB4 VDO detection. + */ + return IS_ENABLED(CONFIG_USB_PD_USB4) && + is_vdo_present(cnt, VDO_INDEX_PTYPE_UFP1_VDO) && + PD_PRODUCT_IS_USB4(payload[VDO_INDEX_PTYPE_UFP1_VDO]); + } + return false; +} + void pd_dfp_pe_init(int port) { memset(&pe[port], 0, sizeof(struct pd_policy)); @@ -508,6 +700,20 @@ static void dfp_consume_modes(int port, int cnt, uint32_t *payload) pe[port].svid_idx++; } +/* + * This function returns + * True - If the THunderbolt cable speed is TBT_SS_TBT_GEN3 or + * TBT_SS_U32_GEN1_GEN2 + * False - Otherwise + */ +static bool check_tbt_cable_speed(int port) +{ + return (cable[port].cable_mode_resp.tbt_cable_speed == + TBT_SS_TBT_GEN3 || + cable[port].cable_mode_resp.tbt_cable_speed == + TBT_SS_U32_GEN1_GEN2); +} + static int get_mode_idx(int port, uint16_t svid) { int i; @@ -909,12 +1115,27 @@ static int process_tbt_compat_discover_modes(int port, uint32_t *payload) } /* - * Enter Mode SOP' (Cable Enter Mode) is skipped for - * passive cables. + * Enter Mode SOP' (Cable Enter Mode) and Enter USB SOP' is + * skipped for passive cables. */ if (get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE) disable_transmit_sop_prime(port); + if (is_usb4_mode_enabled(port)) { + /* + * If Cable is not Thunderbolt Gen 3 + * capable or Thunderbolt Gen1_Gen2 + * capable, disable USB4 mode and + * continue flow for + * Thunderbolt-compatible mode + */ + if (check_tbt_cable_speed(port)) { + enable_enter_usb4_mode(port); + usb_mux_set_safe_mode(port); + return 0; + } + disable_usb4_mode(port); + } rsize = enter_tbt_compat_mode(port, payload); } else { /* Store Discover Mode SOP response */ @@ -1025,27 +1246,63 @@ int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload, /* Store cable type */ dfp_consume_cable_response(port, cnt, payload, head); + + /* + * Enter USB4 mode if the cable supports USB4 + * operation and has USB4 VDO. + */ + if (is_usb4_mode_enabled(port) && + is_cable_ready_to_enter_usb4(port, cnt)) { + enable_enter_usb4_mode(port); + usb_mux_set_safe_mode(port); + disable_transmit_sop_prime(port); + /* + * To change the mode of operation from + * USB4 the port needs to be + * reconfigured. + * Ref: USB Type-C Cable and Connectot + * Specification section 5.4.4. + * + */ + disable_tbt_compat_mode(port); + rsize = 0; + break; + } + /* * Disable Thunderbolt-compatible mode if the * cable does not support superspeed */ if (is_tbt_compat_enabled(port) && - !is_tbt_cable_superspeed(port)) + !is_tbt_cable_superspeed(port)) { disable_tbt_compat_mode(port); + } rsize = dfp_discover_svids(payload); + disable_transmit_sop_prime(port); /* Received a SOP Discover Ident Message */ } else if (IS_ENABLED(CONFIG_USB_PD_DECODE_SOP) && board_is_tbt_usb4_port(port)) { dfp_consume_identity(port, cnt, payload); + /* Enable USB4 mode if USB4 VDO present + * and port partner supports USB Rev 3.0. + */ + if (is_usb4_vdo(port, cnt, payload) && + PD_HEADER_REV(head) == PD_REV30) { + enable_usb4_mode(port); + } + /* * Enable Thunderbolt-compatible mode * if the modal operation is supported */ - if (is_modal(port, cnt, payload)) { + if (is_modal(port, cnt, payload)) enable_tbt_compat_mode(port); + + if (is_modal(port, cnt, payload) || + is_usb4_vdo(port, cnt, payload)) { rsize = dfp_discover_ident(payload); enable_transmit_sop_prime(port); } else { @@ -1092,6 +1349,7 @@ int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload, } rsize = dfp_discover_modes(port, payload); + disable_transmit_sop_prime(port); } break; @@ -1104,10 +1362,15 @@ int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload, break; } - disable_tbt_compat_mode(port); rsize = dfp_discover_modes(port, payload); /* enter the default mode for DFP */ if (!rsize) { + /* + * Disabling Thunderbolt-Compatible mode if + * discover mode response doesn't include Intel + * SVID. + */ + disable_tbt_compat_mode(port); payload[0] = pd_dfp_enter_mode(port, 0, 0); if (payload[0]) rsize = 1; |