summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeith Short <keithshort@chromium.org>2020-12-16 20:45:41 -0700
committerCommit Bot <commit-bot@chromium.org>2021-01-09 00:44:44 +0000
commit88d46d61c3081b7fc32225f3aed7426a1d62e36e (patch)
tree82368a4b5a93e6eba6eb7d2c121a50fcdf7515c6
parent6be1ff9b14b3593cfc6b657a41334cc8a7f5811d (diff)
downloadchrome-ec-88d46d61c3081b7fc32225f3aed7426a1d62e36e.tar.gz
tcpmv2: always issue SOP' soft reset
After entering a PD contract, always issue an SOP' soft reset before sending any discovery VDMs to the cable. BUG=b:172364575 BRANCH=volteer TEST=connect monitor with emarked cable, verify SOP' soft reset is sent when EC starts as SNK/DFP. TEST=Connect monitor with non-emark cable. Verify SOP' soft reset is sent once regardless of starting role. TEST=Connect USB4 dock, verify USB4 entry. Signed-off-by: Keith Short <keithshort@chromium.org> Change-Id: Id5026a2c8c9877b860e1356dd33763bad3e51841 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2596838 Reviewed-by: Diana Z <dzigterman@chromium.org> Reviewed-by: Abe Levkoy <alevkoy@chromium.org>
-rw-r--r--common/usbc/usb_pe_drp_sm.c114
-rw-r--r--include/usb_pd.h41
-rw-r--r--test/usb_pe.h1
-rw-r--r--test/usb_pe_drp.c20
-rw-r--r--test/usb_tcpmv2_compliance_common.c9
5 files changed, 164 insertions, 21 deletions
diff --git a/common/usbc/usb_pe_drp_sm.c b/common/usbc/usb_pe_drp_sm.c
index de8be98707..9659614d49 100644
--- a/common/usbc/usb_pe_drp_sm.c
+++ b/common/usbc/usb_pe_drp_sm.c
@@ -297,6 +297,7 @@ enum usb_pe_state {
PE_VCS_TURN_ON_VCONN_SWAP,
PE_VCS_TURN_OFF_VCONN_SWAP,
PE_VCS_SEND_PS_RDY_SWAP,
+ PE_VCS_CBL_SEND_SOFT_RESET,
PE_VDM_IDENTITY_REQUEST_CBL,
PE_INIT_PORT_VDM_IDENTITY_REQUEST,
PE_INIT_VDM_SVIDS_REQUEST,
@@ -413,6 +414,7 @@ __maybe_unused static const char * const pe_state_names[] = {
[PE_VCS_TURN_ON_VCONN_SWAP] = "PE_VCS_Turn_On_Vconn_Swap",
[PE_VCS_TURN_OFF_VCONN_SWAP] = "PE_VCS_Turn_Off_Vconn_Swap",
[PE_VCS_SEND_PS_RDY_SWAP] = "PE_VCS_Send_Ps_Rdy_Swap",
+ [PE_VCS_CBL_SEND_SOFT_RESET] = "PE_VCS_CBL_Send_Soft_Reset",
#endif
[PE_VDM_IDENTITY_REQUEST_CBL] = "PE_VDM_Identity_Request_Cbl",
[PE_INIT_PORT_VDM_IDENTITY_REQUEST] =
@@ -1248,6 +1250,7 @@ void pe_report_error(int port, enum pe_error e, enum tcpm_transmit_type type)
get_state_pe(port) == PE_PRS_SRC_SNK_WAIT_SOURCE_ON ||
get_state_pe(port) == PE_SRC_DISABLED ||
get_state_pe(port) == PE_SRC_DISCOVERY ||
+ get_state_pe(port) == PE_VCS_CBL_SEND_SOFT_RESET ||
get_state_pe(port) == PE_VDM_IDENTITY_REQUEST_CBL) ||
(PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH) &&
get_state_pe(port) == PE_PRS_SNK_SRC_SEND_SWAP) ||
@@ -1685,6 +1688,18 @@ __maybe_unused static bool pe_attempt_port_discovery(int port)
}
}
+ /*
+ * TODO(b/177001425): TCPMv2 - move SOP' soft reset check into
+ * common_src_snk_dpm_requests()
+ */
+ if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND)) {
+ pe_set_dpm_curr_request(port,
+ DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND);
+ pe[port].tx_type = TCPC_TX_SOP_PRIME;
+ set_state_pe(port, PE_VCS_CBL_SEND_SOFT_RESET);
+ return true;
+ }
+
/* If mode entry was successful, disable the timer */
if (PE_CHK_FLAG(port, PE_FLAGS_VDM_SETUP_DONE)) {
pe[port].discover_identity_timer = TIMER_DISABLED;
@@ -1894,6 +1909,13 @@ static void pe_src_startup_entry(int port)
/* Reset the protocol layer */
prl_reset(port);
+ /*
+ * Protocol layer reset clears the message IDs for all SOP types.
+ * Indicate that a SOP' soft reset is required before any other
+ * messages are sent to the cable.
+ */
+ pd_dpm_request(port, DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND);
+
/* Set initial data role */
pe[port].data_role = pd_get_data_role(port);
@@ -2770,6 +2792,13 @@ static void pe_snk_startup_entry(int port)
/* Reset the protocol layer */
prl_reset(port);
+ /*
+ * Protocol layer reset clears the message IDs for all SOP types.
+ * Indicate that a SOP' soft reset is required before any other
+ * messages are sent to the cable.
+ */
+ pd_dpm_request(port, DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND);
+
/* Set initial data role */
pe[port].data_role = pd_get_data_role(port);
@@ -6169,6 +6198,13 @@ static void pe_vcs_send_ps_rdy_swap_run(int port)
/* TODO(b/152058087): TCPMv2: Break up pe_vcs_send_ps_rdy_swap */
switch (pe[port].sub) {
case PE_SUB0:
+
+ /*
+ * TODO: use DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND to
+ * send cable soft reset.
+ */
+ PE_CLR_DPM_REQUEST(port, DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND);
+
/*
* After a VCONN Swap the VCONN Source needs to reset
* the Cable Plug’s Protocol Layer in order to ensure
@@ -6233,6 +6269,80 @@ static void pe_vcs_send_ps_rdy_swap_run(int port)
}
}
}
+
+/*
+ * PE_VCS_CBL_SEND_SOFT_RESET
+ * Note - Entry is only when directed by the DPM. Protocol errors are handled
+ * by the PE_SEND_SOFT_RESET state.
+ */
+static void pe_vcs_cbl_send_soft_reset_entry(int port)
+{
+ print_current_state(port);
+
+ if (!pe_can_send_sop_prime(port)) {
+ /*
+ * If we're not VCONN source, return the appropriate state.
+ * A VCONN swap re-triggers sending SOP' soft reset
+ */
+ if (pe_is_explicit_contract(port)) {
+ /* Return to PE_{SRC,SNK}_Ready state */
+ pe_set_ready_state(port);
+ } else {
+ /*
+ * Not in Explicit Contract, so we must be a SRC,
+ * return to PE_Src_Send_Capabilities.
+ */
+ set_state_pe(port, PE_SRC_SEND_CAPABILITIES);
+ }
+ return;
+ }
+
+ send_ctrl_msg(port, TCPC_TX_SOP_PRIME, PD_CTRL_SOFT_RESET);
+ pe[port].sender_response_timer = TIMER_DISABLED;
+}
+
+static void pe_vcs_cbl_send_soft_reset_run(int port)
+{
+ bool cable_soft_reset_complete = false;
+
+ if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
+ PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
+ pe[port].sender_response_timer = get_time().val +
+ PD_T_SENDER_RESPONSE;
+ }
+
+ /* Got ACCEPT or REJECT from Cable Plug */
+ if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
+ PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
+ cable_soft_reset_complete = true;
+ }
+
+ /* No GoodCRC received, cable is not present */
+ if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) {
+ PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR);
+ /*
+ * TODO(b/171823328): TCPMv2: Implement cable reset
+ * Cable reset will only be done here if we know for certain
+ * a cable is present (we've received the SOP' DiscId response).
+ */
+ cable_soft_reset_complete = true;
+ }
+
+ if (cable_soft_reset_complete ||
+ get_time().val > pe[port].sender_response_timer) {
+ if (pe_is_explicit_contract(port)) {
+ /* Return to PE_{SRC,SNK}_Ready state */
+ pe_set_ready_state(port);
+ } else {
+ /*
+ * Not in Explicit Contract, so we must be a SRC,
+ * return to PE_Src_Send_Capabilities.
+ */
+ set_state_pe(port, PE_SRC_SEND_CAPABILITIES);
+ }
+ }
+}
+
#endif /* CONFIG_USBC_VCONN */
/*
@@ -6749,6 +6859,10 @@ static const struct usb_state pe_states[] = {
.entry = pe_vcs_send_ps_rdy_swap_entry,
.run = pe_vcs_send_ps_rdy_swap_run,
},
+ [PE_VCS_CBL_SEND_SOFT_RESET] = {
+ .entry = pe_vcs_cbl_send_soft_reset_entry,
+ .run = pe_vcs_cbl_send_soft_reset_run,
+ },
#endif /* CONFIG_USBC_VCONN */
[PE_VDM_IDENTITY_REQUEST_CBL] = {
.entry = pe_vdm_identity_request_cbl_entry,
diff --git a/include/usb_pd.h b/include/usb_pd.h
index e0f0ac0e28..f0997de7b0 100644
--- a/include/usb_pd.h
+++ b/include/usb_pd.h
@@ -956,26 +956,27 @@ enum pd_dual_role_states {
* NOTE: These are usually set by host commands from the AP.
*/
enum pd_dpm_request {
- DPM_REQUEST_DR_SWAP = BIT(0),
- DPM_REQUEST_PR_SWAP = BIT(1),
- DPM_REQUEST_VCONN_SWAP = BIT(2),
- DPM_REQUEST_GOTO_MIN = BIT(3),
- DPM_REQUEST_SRC_CAP_CHANGE = BIT(4),
- DPM_REQUEST_GET_SNK_CAPS = BIT(5),
- DPM_REQUEST_SEND_PING = BIT(6),
- DPM_REQUEST_SOURCE_CAP = BIT(7),
- DPM_REQUEST_NEW_POWER_LEVEL = BIT(8),
- DPM_REQUEST_VDM = BIT(9),
- DPM_REQUEST_BIST_TX = BIT(10),
- DPM_REQUEST_SNK_STARTUP = BIT(11),
- DPM_REQUEST_SRC_STARTUP = BIT(12),
- DPM_REQUEST_HARD_RESET_SEND = BIT(13),
- DPM_REQUEST_SOFT_RESET_SEND = BIT(14),
- DPM_REQUEST_PORT_DISCOVERY = BIT(15),
- DPM_REQUEST_SEND_ALERT = BIT(16),
- DPM_REQUEST_ENTER_USB = BIT(17),
- DPM_REQUEST_GET_SRC_CAPS = BIT(18),
- DPM_REQUEST_EXIT_MODES = BIT(19),
+ DPM_REQUEST_DR_SWAP = BIT(0),
+ DPM_REQUEST_PR_SWAP = BIT(1),
+ DPM_REQUEST_VCONN_SWAP = BIT(2),
+ DPM_REQUEST_GOTO_MIN = BIT(3),
+ DPM_REQUEST_SRC_CAP_CHANGE = BIT(4),
+ DPM_REQUEST_GET_SNK_CAPS = BIT(5),
+ DPM_REQUEST_SEND_PING = BIT(6),
+ DPM_REQUEST_SOURCE_CAP = BIT(7),
+ DPM_REQUEST_NEW_POWER_LEVEL = BIT(8),
+ DPM_REQUEST_VDM = BIT(9),
+ DPM_REQUEST_BIST_TX = BIT(10),
+ DPM_REQUEST_SNK_STARTUP = BIT(11),
+ DPM_REQUEST_SRC_STARTUP = BIT(12),
+ DPM_REQUEST_HARD_RESET_SEND = BIT(13),
+ DPM_REQUEST_SOFT_RESET_SEND = BIT(14),
+ DPM_REQUEST_PORT_DISCOVERY = BIT(15),
+ DPM_REQUEST_SEND_ALERT = BIT(16),
+ DPM_REQUEST_ENTER_USB = BIT(17),
+ DPM_REQUEST_GET_SRC_CAPS = BIT(18),
+ DPM_REQUEST_EXIT_MODES = BIT(19),
+ DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND = BIT(20),
};
/**
diff --git a/test/usb_pe.h b/test/usb_pe.h
index 373bf9f5b2..a4967d02e5 100644
--- a/test/usb_pe.h
+++ b/test/usb_pe.h
@@ -120,6 +120,7 @@ enum usb_pe_state {
PE_VCS_TURN_ON_VCONN_SWAP,
PE_VCS_TURN_OFF_VCONN_SWAP,
PE_VCS_SEND_PS_RDY_SWAP,
+ PE_VCS_CBL_SEND_SOFT_RESET,
PE_VDM_IDENTITY_REQUEST_CBL,
PE_INIT_PORT_VDM_IDENTITY_REQUEST,
PE_INIT_VDM_SVIDS_REQUEST,
diff --git a/test/usb_pe_drp.c b/test/usb_pe_drp.c
index 10269db779..8138bb5ddf 100644
--- a/test/usb_pe_drp.c
+++ b/test/usb_pe_drp.c
@@ -189,6 +189,16 @@ test_static int test_send_caps_error_before_connected(void)
EC_SUCCESS, "%d");
mock_prl_message_sent(PORT0);
+ /*
+ * Cable soft reset is always issued after entry into Src/Snk_Ready
+ * simulate no cable response.
+ */
+ TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPC_TX_SOP_PRIME,
+ PD_CTRL_SOFT_RESET, 0,
+ 60 * MSEC),
+ EC_SUCCESS, "%d");
+ mock_prl_report_error(PORT0, ERR_TCH_XMIT, TCPC_TX_SOP_PRIME);
+
TEST_EQ(finish_src_discovery(), EC_SUCCESS, "%d");
task_wait_event(5 * SECOND);
@@ -225,6 +235,16 @@ test_static int test_send_caps_error_when_connected(void)
mock_prl_message_sent(PORT0);
/*
+ * Cable soft reset is always issued after entry into Src/Snk_Ready
+ * simulate no cable response.
+ */
+ TEST_EQ(mock_prl_wait_for_tx_msg(PORT0, TCPC_TX_SOP_PRIME,
+ PD_CTRL_SOFT_RESET, 0,
+ 60 * MSEC),
+ EC_SUCCESS, "%d");
+ mock_prl_report_error(PORT0, ERR_TCH_XMIT, TCPC_TX_SOP_PRIME);
+
+ /*
* Expect VENDOR_DEF for cable identity, simulate no cable (so no
* GoodCRC, so ERR_TCH_XMIT). Don't reply NOT_SUPPORTED, since the spec
* says a cable never does that.
diff --git a/test/usb_tcpmv2_compliance_common.c b/test/usb_tcpmv2_compliance_common.c
index 92efe5a616..a5ff2348d4 100644
--- a/test/usb_tcpmv2_compliance_common.c
+++ b/test/usb_tcpmv2_compliance_common.c
@@ -320,7 +320,6 @@ int proc_pd_e1(enum pd_data_role data_role, enum proc_pd_e1_attach attach)
PD_CTRL_PS_RDY, 0),
EC_SUCCESS, "%d");
mock_set_alert(TCPC_REG_ALERT_TX_SUCCESS);
- task_wait_event(1 * MSEC);
TEST_EQ(tc_is_attached_src(PORT0), true, "%d");
break;
@@ -366,6 +365,14 @@ int proc_pd_e3(void)
int handle_attach_expected_msgs(enum pd_data_role data_role)
{
if (data_role == PD_ROLE_DFP) {
+ TEST_EQ(verify_tcpci_transmit(TCPC_TX_SOP_PRIME,
+ PD_CTRL_SOFT_RESET, 0),
+ EC_SUCCESS, "%d");
+ mock_set_alert(TCPC_REG_ALERT_TX_SUCCESS);
+ task_wait_event(10 * MSEC);
+ partner_send_msg(PD_MSG_SOP_PRIME, PD_CTRL_NOT_SUPPORTED, 0, 0,
+ NULL);
+
TEST_EQ(verify_tcpci_transmit(TCPC_TX_SOP_PRIME, 0,
PD_DATA_VENDOR_DEF),
EC_SUCCESS, "%d");