summaryrefslogtreecommitdiff
path: root/common/usbc/usb_pe_drp_sm.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/usbc/usb_pe_drp_sm.c')
-rw-r--r--common/usbc/usb_pe_drp_sm.c208
1 files changed, 189 insertions, 19 deletions
diff --git a/common/usbc/usb_pe_drp_sm.c b/common/usbc/usb_pe_drp_sm.c
index 70c7a32eb7..ac9e4883e1 100644
--- a/common/usbc/usb_pe_drp_sm.c
+++ b/common/usbc/usb_pe_drp_sm.c
@@ -71,7 +71,9 @@
#define PE_FLAGS_RUN_SOURCE_START_TIMER BIT(19)
#define PE_FLAGS_VDM_REQUEST_BUSY BIT(20)
#define PE_FLAGS_VDM_REQUEST_NAKED BIT(21)
-#define PE_FLAGS_FAST_ROLE_SWAP BIT(22)
+#define PE_FLAGS_FAST_ROLE_SWAP_PATH BIT(22)/* FRS/PRS Exec Path */
+#define PE_FLAGS_FAST_ROLE_SWAP_ENABLED BIT(23)/* FRS Listening State */
+#define PE_FLAGS_FAST_ROLE_SWAP_SIGNALED BIT(24)/* FRS PPC/TCPC Signal */
/* 6.7.3 Hard Reset Counter */
#define N_HARD_RESET_COUNT 2
@@ -146,6 +148,7 @@ enum usb_pe_state {
PE_HANDLE_CUSTOM_VDM_REQUEST,
PE_WAIT_FOR_ERROR_RECOVERY,
PE_BIST,
+ PE_DR_SNK_GET_SINK_CAP,
/* Super States */
PE_PRS_FRS_SHARED,
@@ -212,6 +215,7 @@ static const char * const pe_state_names[] = {
[PE_HANDLE_CUSTOM_VDM_REQUEST] = "PE_Handle_Custom_Vdm_Request",
[PE_WAIT_FOR_ERROR_RECOVERY] = "PE_Wait_For_Error_Recovery",
[PE_BIST] = "PE_Bist",
+ [PE_DR_SNK_GET_SINK_CAP] = "PE_DR_SNK_Get_Sink_Cap",
};
#endif
@@ -476,6 +480,18 @@ void pe_run(int port, int evt, int en)
break;
}
+ /*
+ * Check for Fast Role Swap signal
+ * This is not a typical pattern for adding state changes.
+ * I added this here because FRS SIGNALED can happen at any
+ * state once we are listening for the signal and we want to
+ * make sure to handle it immediately.
+ */
+ if (PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_SIGNALED)) {
+ PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_SIGNALED);
+ set_state_pe(port, PE_FRS_SNK_SRC_START_AMS);
+ }
+
/* Run state machine */
exe_state(port, &pe[port].ctx);
break;
@@ -521,6 +537,58 @@ void pe_got_hard_reset(int port)
set_state_pe(port, PE_SNK_TRANSITION_TO_DEFAULT);
}
+/*
+ * pe_got_frs_signal
+ *
+ * Called by the handler that detects the FRS signal in order to
+ * switch PE states to complete the FRS that the hardware has
+ * started.
+ */
+void pe_got_frs_signal(int port)
+{
+ PE_SET_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_SIGNALED);
+}
+
+/*
+ * PE_Set_FRS_Enable
+ *
+ * This function should be called every time an explicit contract
+ * is disabled, to disable FRS.
+ *
+ * Enabling an explicit contract is not enough to enable FRS, it
+ * also requires a Sink Capability power requirement from a Source
+ * that supports FRS so we can determine if this is something we
+ * can handle.
+ */
+static void pe_set_frs_enable(int port, int enable)
+{
+ /* This should only be called from the PD task */
+ assert(port == TASK_ID_TO_PD_PORT(task_get_current()));
+
+ if (IS_ENABLED(CONFIG_USB_TYPEC_PD_FAST_ROLE_SWAP)) {
+ int current = PE_CHK_FLAG(port,
+ PE_FLAGS_FAST_ROLE_SWAP_ENABLED);
+
+ /* Request an FRS change, only if the state has changed */
+ if (!!current != !!enable) {
+ tcpm_set_frs_enable(port, enable);
+
+ if (enable)
+ PE_SET_FLAG(port,
+ PE_FLAGS_FAST_ROLE_SWAP_ENABLED);
+ else
+ PE_CLR_FLAG(port,
+ PE_FLAGS_FAST_ROLE_SWAP_ENABLED);
+ }
+ }
+}
+
+static void pe_invalidate_explicit_contract(int port)
+{
+ pe_set_frs_enable(port, 0);
+ PE_CLR_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT);
+}
+
void pe_report_error(int port, enum pe_error e)
{
/* This should only be called from the PD task */
@@ -681,7 +749,7 @@ static void print_current_state(const int port)
{
const char *mode = "";
- if (PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP))
+ if (PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH))
mode = " FRS-MODE";
CPRINTS("C%d: %s%s", port, pe_state_names[get_state_pe(port)], mode);
@@ -848,7 +916,7 @@ static void pe_src_startup_entry(int port)
pe[port].power_role = PD_ROLE_SOURCE;
/* Clear explicit contract. */
- PE_CLR_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT);
+ pe_invalidate_explicit_contract(port);
pe[port].cable_discover_identity_count = 0;
pe[port].port_discover_identity_count = 0;
@@ -1614,7 +1682,7 @@ static void pe_snk_startup_entry(int port)
pe[port].power_role = PD_ROLE_SINK;
/* Clear explicit contract */
- PE_CLR_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT);
+ pe_invalidate_explicit_contract(port);
}
static void pe_snk_startup_run(int port)
@@ -1789,6 +1857,12 @@ static void pe_snk_select_capability_run(int port)
/* explicit contract is now in place */
PE_SET_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT);
set_state_pe(port, PE_SNK_TRANSITION_SINK);
+
+ /*
+ * Setup to get Device Policy Manager to
+ * request Sink Capabilities for possible FRS
+ */
+ pe_dpm_request(port, DPM_REQUEST_GET_SNK_CAPS);
return;
}
/*
@@ -1967,12 +2041,10 @@ static void pe_snk_ready_run(int port)
* Ignore source specific requests:
* DPM_REQUEST_GOTO_MIN
* DPM_REQUEST_SRC_CAP_CHANGE,
- * DPM_REQUEST_GET_SNK_CAPS,
* DPM_REQUEST_SEND_PING
*/
PE_CLR_DPM_REQUEST(port, DPM_REQUEST_GOTO_MIN |
DPM_REQUEST_SRC_CAP_CHANGE |
- DPM_REQUEST_GET_SNK_CAPS |
DPM_REQUEST_SEND_PING);
if (pe[port].dpm_request) {
@@ -1997,7 +2069,8 @@ static void pe_snk_ready_run(int port)
set_state_pe(port, PE_SNK_SELECT_CAPABILITY);
} else if (PE_CHK_DPM_REQUEST(port,
DPM_REQUEST_DISCOVER_IDENTITY)) {
- PE_CLR_DPM_REQUEST(port, DPM_REQUEST_DISCOVER_IDENTITY);
+ PE_CLR_DPM_REQUEST(port,
+ DPM_REQUEST_DISCOVER_IDENTITY);
pe[port].partner_type = CABLE;
pe[port].vdm_cmd = DISCOVER_IDENTITY;
@@ -2008,6 +2081,10 @@ static void pe_snk_ready_run(int port)
pe[port].vdm_cnt = 1;
set_state_pe(port, PE_VDM_REQUEST);
+ } else if (PE_CHK_DPM_REQUEST(port,
+ DPM_REQUEST_GET_SNK_CAPS)) {
+ PE_CLR_DPM_REQUEST(port, DPM_REQUEST_GET_SNK_CAPS);
+ set_state_pe(port, PE_DR_SNK_GET_SINK_CAP);
}
return;
}
@@ -2743,7 +2820,7 @@ static void pe_prs_src_snk_transition_to_off_run(int port)
/* Wait until Rd is asserted */
if (tc_is_attached_snk(port)) {
/* Contract is invalid */
- PE_CLR_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT);
+ pe_invalidate_explicit_contract(port);
set_state_pe(port, PE_PRS_SRC_SNK_WAIT_SOURCE_ON);
}
}
@@ -2916,7 +2993,7 @@ static void pe_prs_snk_src_transition_to_off_entry(int port)
{
print_current_state(port);
- if (!PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP))
+ if (!PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH))
tc_snk_power_off(port);
pe[port].ps_source_timer = get_time().val + PD_T_PS_SOURCE_OFF;
@@ -2978,9 +3055,10 @@ static void pe_prs_snk_src_assert_rp_run(int port)
{
/* Wait until TypeC is in the Attached.SRC state */
if (tc_is_attached_src(port)) {
- if (!PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP))
+ if (!PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH)) {
/* Contract is invalid now */
- PE_CLR_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT);
+ pe_invalidate_explicit_contract(port);
+ }
set_state_pe(port, PE_PRS_SNK_SRC_SOURCE_ON);
}
@@ -3058,7 +3136,7 @@ static void pe_prs_snk_src_send_swap_entry(int port)
*/
prl_send_ctrl_msg(port,
TCPC_TX_SOP,
- PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP)
+ PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH)
? PD_CTRL_FR_SWAP
: PD_CTRL_PR_SWAP);
@@ -3080,7 +3158,7 @@ static void pe_prs_snk_src_send_swap_run(int port)
*/
if (get_time().val > pe[port].sender_response_timer)
set_state_pe(port,
- PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP)
+ PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH)
? PE_WAIT_FOR_ERROR_RECOVERY
: PE_SNK_READY);
@@ -3108,7 +3186,7 @@ static void pe_prs_snk_src_send_swap_run(int port)
(type == PD_CTRL_WAIT))
set_state_pe(port,
PE_CHK_FLAG(port,
- PE_FLAGS_FAST_ROLE_SWAP)
+ PE_FLAGS_FAST_ROLE_SWAP_PATH)
? PE_WAIT_FOR_ERROR_RECOVERY
: PE_SNK_READY);
}
@@ -3129,14 +3207,13 @@ static void pe_frs_snk_src_start_ams_entry(int port)
print_current_state(port);
/* Contract is invalid now */
- PE_CLR_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT);
+ pe_invalidate_explicit_contract(port);
/* Inform Protocol Layer this is start of AMS */
prl_start_ams(port);
- tc_set_timeout(port, 2 * MSEC);
/* Shared PRS/FRS code, indicate FRS path */
- PE_SET_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP);
+ PE_SET_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH);
set_state_pe(port, PE_PRS_SNK_SRC_SEND_SWAP);
}
@@ -3153,7 +3230,7 @@ static void pe_prs_frs_shared_entry(int port)
* For FRS, PE_FRS_SNK_SRC_START_AMS entry will be called
* after this and that will set for the FRS path.
*/
- PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP);
+ PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH);
}
static void pe_prs_frs_shared_exit(int port)
@@ -3162,7 +3239,7 @@ static void pe_prs_frs_shared_exit(int port)
* Shared PRS/FRS code, when not in shared path
* indicate PRS path
*/
- PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP);
+ PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH);
}
/**
@@ -4140,6 +4217,95 @@ static void pe_vcs_send_ps_rdy_swap_run(int port)
}
}
+/*
+ * PE_DR_SNK_Get_Sink_Cap
+ */
+static void pe_dr_snk_get_sink_cap_entry(int port)
+{
+ print_current_state(port);
+
+ /* Send a Get Sink Cap Message */
+ prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_GET_SINK_CAP);
+
+ /* Don't start the timer until message sent */
+ pe[port].sender_response_timer = 0;
+}
+
+static void pe_dr_snk_get_sink_cap_run(int port)
+{
+ int type;
+ int cnt;
+ int ext;
+ uint32_t payload;
+
+ /* Wait until message is sent */
+ if (pe[port].sender_response_timer == 0) {
+ if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
+ PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
+ /* start the SenderResponseTimer */
+ pe[port].sender_response_timer =
+ get_time().val + PD_T_SENDER_RESPONSE;
+ } else {
+ return;
+ }
+ }
+
+ /*
+ * Determine if FRS is possible based on the returned Sink Caps
+ * and transition to PE_SNK_Ready when:
+ * 1) An Accept Message is received.
+ *
+ * Transition to PE_SNK_Ready state when:
+ * 1) A Reject Message is received.
+ * 2) Or a Wait Message is received.
+ */
+ if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
+ PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
+
+ type = PD_HEADER_TYPE(emsg[port].header);
+ cnt = PD_HEADER_CNT(emsg[port].header);
+ ext = PD_HEADER_EXT(emsg[port].header);
+ payload = *(uint32_t *)emsg[port].buf;
+
+ if ((ext == 0) && (cnt == 0)) {
+ if (type == PD_CTRL_ACCEPT) {
+ /*
+ * Check message to see if we can handle
+ * FRS for this connection.
+ *
+ * TODO(b/14191267): Make sure we can handle
+ * the required current before we enable FRS.
+ */
+ if (payload & PDO_FIXED_DUAL_ROLE) {
+ switch (payload &
+ PDO_FIXED_FRS_CURR_MASK) {
+ case PDO_FIXED_FRS_CURR_NOT_SUPPORTED:
+ break;
+ case PDO_FIXED_FRS_CURR_DFLT_USB_POWER:
+ case PDO_FIXED_FRS_CURR_1A5_AT_5V:
+ case PDO_FIXED_FRS_CURR_3A0_AT_5V:
+ pe_set_frs_enable(port, 1);
+ return;
+ }
+ }
+ set_state_pe(port, PE_SNK_READY);
+ return;
+ } else if ((type == PD_CTRL_REJECT) ||
+ (type == PD_CTRL_WAIT)) {
+ set_state_pe(port, PE_SNK_READY);
+ return;
+ }
+ }
+ }
+
+ /*
+ * Transition to PE_SNK_Ready state when:
+ * 1) SenderResponseTimer times out.
+ */
+ if (get_time().val > pe[port].sender_response_timer)
+ set_state_pe(port, PE_SNK_READY);
+}
+
/* Policy Engine utility functions */
int pd_check_requested_voltage(uint32_t rdo, const int port)
{
@@ -4980,6 +5146,10 @@ static const struct usb_state pe_states[] = {
.entry = pe_bist_entry,
.run = pe_bist_run,
},
+ [PE_DR_SNK_GET_SINK_CAP] = {
+ .entry = pe_dr_snk_get_sink_cap_entry,
+ .run = pe_dr_snk_get_sink_cap_run,
+ },
};
#ifdef TEST_BUILD