summaryrefslogtreecommitdiff
path: root/common/usb_pd_policy.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/usb_pd_policy.c')
-rw-r--r--common/usb_pd_policy.c273
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;