summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Hurst <shurst@google.com>2020-03-24 16:46:14 -0700
committerCommit Bot <commit-bot@chromium.org>2020-04-02 09:40:09 +0000
commit28f07760f384789a6a28801c0c42d9d61af2f99b (patch)
tree6a3b98e52fa4fa3d49909be781df1c7f77c92a45
parentf648164f18293a2b3e2c5e0b766a8edef787ee9d (diff)
downloadchrome-ec-28f07760f384789a6a28801c0c42d9d61af2f99b.tar.gz
TCPMv2: Restore PD state after Sysjump
When a PD explicit contract is negotiated in RO, the contract is maintained while performing a sysjump to RW. This is done by serializing the PD Power, Data, and VCONN Roles, along with the explicit contract flag. After jumping to RW, deserialization is performed by restoring the power roles and setting the explicit contract flag. BUG=b:152350558,b:152027807,b:152967274 BRANCH=none TEST=make -j buildall Manual tests: Used total phase to verify that charging voltage was maintained across sysjump from RO to RW and that DP was reestablished when a dock was used. Tested that Kohaku booted properly without a battery and a charger connected to port 0. This test was repeated with a charger connected to port 1. Signed-off-by: Sam Hurst <shurst@google.com> Change-Id: I349c41f2279e9af9830564d44ac73ad8435f1f80 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2119131 Tested-by: Sam Hurst <shurst@google.com> Reviewed-by: Denis Brockus <dbrockus@chromium.org> Commit-Queue: Denis Brockus <dbrockus@chromium.org>
-rw-r--r--common/usb_pd_dual_role.c45
-rw-r--r--common/usb_pd_protocol.c52
-rw-r--r--common/usbc/usb_pe_drp_sm.c106
-rw-r--r--common/usbc/usb_tc_drp_acc_trysrc_sm.c81
-rw-r--r--include/system.h3
-rw-r--r--include/usb_common.h18
-rw-r--r--include/usb_pd.h1
-rw-r--r--include/usb_pe_sm.h12
-rw-r--r--test/fake_usbc.c4
9 files changed, 226 insertions, 96 deletions
diff --git a/common/usb_pd_dual_role.c b/common/usb_pd_dual_role.c
index 73e56f30eb..a424555b26 100644
--- a/common/usb_pd_dual_role.c
+++ b/common/usb_pd_dual_role.c
@@ -7,6 +7,7 @@
#include "charge_manager.h"
#include "charge_state.h"
+#include "system.h"
#include "usb_common.h"
#include "usb_pd.h"
#include "util.h"
@@ -391,3 +392,47 @@ bool pd_is_try_source_capable(void)
return new_try_src;
}
#endif /* CONFIG_USB_PD_TRY_SRC */
+
+static int get_bbram_idx(uint8_t port)
+{
+ if (port < MAX_SYSTEM_BBRAM_IDX_PD_PORTS)
+ return (port + SYSTEM_BBRAM_IDX_PD0);
+
+ return -1;
+}
+
+int pd_get_saved_port_flags(int port, uint8_t *flags)
+{
+ if (system_get_bbram(get_bbram_idx(port), flags) != EC_SUCCESS) {
+#ifndef CHIP_HOST
+ ccprintf("PD NVRAM FAIL");
+#endif
+ return EC_ERROR_UNKNOWN;
+ }
+
+ return EC_SUCCESS;
+}
+
+static void pd_set_saved_port_flags(int port, uint8_t flags)
+{
+ if (system_set_bbram(get_bbram_idx(port), flags) != EC_SUCCESS) {
+#ifndef CHIP_HOST
+ ccprintf("PD NVRAM FAIL");
+#endif
+ }
+}
+
+void pd_update_saved_port_flags(int port, uint8_t flag, uint8_t do_set)
+{
+ uint8_t saved_flags;
+
+ if (pd_get_saved_port_flags(port, &saved_flags) != EC_SUCCESS)
+ return;
+
+ if (do_set)
+ saved_flags |= flag;
+ else
+ saved_flags &= ~flag;
+
+ pd_set_saved_port_flags(port, saved_flags);
+}
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c
index 82e0a1e0e4..80e89017e1 100644
--- a/common/usb_pd_protocol.c
+++ b/common/usb_pd_protocol.c
@@ -575,58 +575,6 @@ static int reset_device_and_notify(int port)
#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */
-#ifdef CONFIG_USB_PD_DUAL_ROLE
-static int get_bbram_idx(int port)
-{
- switch (port) {
- case 2:
- return SYSTEM_BBRAM_IDX_PD2;
- case 1:
- return SYSTEM_BBRAM_IDX_PD1;
- case 0:
- return SYSTEM_BBRAM_IDX_PD0;
- default:
- return -1;
- }
-}
-
-static int pd_get_saved_port_flags(int port, uint8_t *flags)
-{
- if (system_get_bbram(get_bbram_idx(port), flags) != EC_SUCCESS) {
-#ifndef CHIP_HOST
- CPRINTS("PD NVRAM FAIL");
-#endif
- return EC_ERROR_UNKNOWN;
- }
-
- return EC_SUCCESS;
-}
-
-static void pd_set_saved_port_flags(int port, uint8_t flags)
-{
- if (system_set_bbram(get_bbram_idx(port), flags) != EC_SUCCESS) {
-#ifndef CHIP_HOST
- CPRINTS("PD NVRAM FAIL");
-#endif
- }
-}
-
-static void pd_update_saved_port_flags(int port, uint8_t flag, uint8_t val)
-{
- uint8_t saved_flags;
-
- if (pd_get_saved_port_flags(port, &saved_flags) != EC_SUCCESS)
- return;
-
- if (val)
- saved_flags |= flag;
- else
- saved_flags &= ~flag;
-
- pd_set_saved_port_flags(port, saved_flags);
-}
-#endif /* defined(CONFIG_USB_PD_DUAL_ROLE) */
-
/**
* Invalidate last message received at the port when the port gets disconnected
* or reset(soft/hard). This is used to identify and handle the duplicate
diff --git a/common/usbc/usb_pe_drp_sm.c b/common/usbc/usb_pe_drp_sm.c
index e570e27ce2..f16e4d3958 100644
--- a/common/usbc/usb_pe_drp_sm.c
+++ b/common/usbc/usb_pe_drp_sm.c
@@ -153,6 +153,9 @@
*/
typedef int (*svdm_rsp_func)(int port, uint32_t *payload);
+/* This is true only if a sysjump has occurred */
+static bool sysjump_occurred;
+
/* List of all Policy Engine level states */
enum usb_pe_state {
/* Normal States */
@@ -587,6 +590,11 @@ void pe_run(int port, int evt, int en)
}
}
+void pe_set_sysjump(void)
+{
+ sysjump_occurred = true;
+}
+
int pe_is_explicit_contract(int port)
{
return PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT);
@@ -672,10 +680,11 @@ static void pe_set_frs_enable(int port, int enable)
}
}
-static void pe_invalidate_explicit_contract(int port)
+void pe_invalidate_explicit_contract(int port)
{
pe_set_frs_enable(port, 0);
PE_CLR_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT);
+ pd_update_saved_port_flags(port, PD_BBRMFLG_EXPLICIT_CONTRACT, 0);
}
/*
@@ -833,9 +842,6 @@ void pe_send_vdm(int port, uint32_t vid, int cmd, const uint32_t *data,
void pe_exit_dp_mode(int port)
{
- /* This should only be called from the PD task */
- assert(port == TASK_ID_TO_PD_PORT(task_get_current()));
-
if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) {
int opos = pd_alt_mode(port, USB_SID_DISPLAYPORT);
@@ -1544,6 +1550,8 @@ static void pe_src_transition_supply_run(int port)
/* NOTE: Second pass through this code block */
/* Explicit Contract is now in place */
PE_SET_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT);
+ pd_update_saved_port_flags(port,
+ PD_BBRMFLG_EXPLICIT_CONTRACT, 1);
/*
* Set first message flag to trigger a wait and add
* jitter delay when operating in PD2.0 mode.
@@ -1986,8 +1994,14 @@ static void pe_snk_startup_entry(int port)
/* Set initial power role */
pe[port].power_role = PD_ROLE_SINK;
- /* Clear explicit contract */
- pe_invalidate_explicit_contract(port);
+ /*
+ * An explicit contract must be maintained across sysjumps,
+ * so do not invalidate it if a sysjump has occurred
+ */
+ if (!sysjump_occurred) {
+ /* Invalidate explicit contract */
+ pe_invalidate_explicit_contract(port);
+ }
if (PE_CHK_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE)) {
PE_CLR_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE);
@@ -2008,11 +2022,22 @@ static void pe_snk_startup_run(int port)
if (!prl_is_running(port))
return;
- /*
- * Once the reset process completes, the Policy Engine Shall
- * transition to the PE_SNK_Discovery state
- */
- set_state_pe(port, PE_SNK_DISCOVERY);
+ /* Soft reset the charger on sysjump */
+ if (sysjump_occurred) {
+ /*
+ * The sysjump flag is no longer needed, so clear it.
+ * After Soft Reset is sent, PE_ATTACHED_SNK state
+ * is entered.
+ */
+ sysjump_occurred = false;
+ set_state_pe(port, PE_SEND_SOFT_RESET);
+ } else {
+ /*
+ * Once the reset process completes, the Policy Engine Shall
+ * transition to the PE_SNK_Discovery state
+ */
+ set_state_pe(port, PE_SNK_DISCOVERY);
+ }
}
/**
@@ -2110,10 +2135,6 @@ static void pe_snk_evaluate_capability_entry(int port)
/* Evaluate the options based on supplied capabilities */
pd_process_source_cap(port, pe[port].src_cap_cnt, pe[port].src_caps);
- /* We are PD Connected */
- PE_SET_FLAG(port, PE_FLAGS_PD_CONNECTION);
- tc_pd_connection(port, 1);
-
/* Device Policy Response Received */
set_state_pe(port, PE_SNK_SELECT_CAPABILITY);
}
@@ -2125,9 +2146,13 @@ static void pe_snk_select_capability_entry(int port)
{
print_current_state(port);
- pe[port].sender_response_timer = TIMER_DISABLED;
/* Send Request */
pe_send_request_msg(port);
+
+ /* We are PD Connected */
+ PE_SET_FLAG(port, PE_FLAGS_PD_CONNECTION);
+ tc_pd_connection(port, 1);
+ pe[port].sender_response_timer = TIMER_DISABLED;
}
static void pe_snk_select_capability_run(int port)
@@ -2178,6 +2203,9 @@ static void pe_snk_select_capability_run(int port)
if (type == PD_CTRL_ACCEPT) {
/* explicit contract is now in place */
PE_SET_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT);
+ pd_update_saved_port_flags(port,
+ PD_BBRMFLG_EXPLICIT_CONTRACT, 1);
+
set_state_pe(port, PE_SNK_TRANSITION_SINK);
/*
@@ -2316,14 +2344,14 @@ static void pe_snk_ready_entry(int port)
prl_end_ams(port);
/*
- * On entry to the PE_SNK_Ready state as the result of a wait, then do
- * the following:
+ * On entry to the PE_SNK_Ready state as the result of a wait,
+ * then do the following:
* 1) Initialize and run the SinkRequestTimer
*/
if (PE_CHK_FLAG(port, PE_FLAGS_WAIT)) {
PE_CLR_FLAG(port, PE_FLAGS_WAIT);
pe[port].sink_request_timer =
- get_time().val + PD_T_SINK_REQUEST;
+ get_time().val + PD_T_SINK_REQUEST;
} else {
pe[port].sink_request_timer = TIMER_DISABLED;
}
@@ -2331,14 +2359,15 @@ static void pe_snk_ready_entry(int port)
/*
* Do port partner discovery
*
- * This function modifies state variables that are used in the run
- * part of this state. See pe_attempt_port_discovery for details.
+ * This function modifies state variables that are used in
+ * the run part of this state. See pe_attempt_port_discovery
+ * for details.
*/
pe_attempt_port_discovery(port);
/*
- * Wait and add jitter if we are operating in PD2.0 mode and no messages
- * have been sent since enter this state.
+ * Wait and add jitter if we are operating in PD2.0 mode and no
+ * messages have been sent since enter this state.
*/
pe_update_wait_and_add_jitter_timer(port);
}
@@ -2677,21 +2706,6 @@ static void pe_send_soft_reset_run(int port)
}
/*
- * Transition to PE_SNK_Hard_Reset or PE_SRC_Hard_Reset on Sender
- * Response Timer Timeout or Protocol Layer or Protocol Error
- */
- if (get_time().val > pe[port].sender_response_timer ||
- PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) {
- PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR);
-
- if (pe[port].power_role == PD_ROLE_SINK)
- set_state_pe(port, PE_SRC_HARD_RESET);
- else
- set_state_pe(port, PE_SRC_HARD_RESET);
- return;
- }
-
- /*
* Transition to the PE_SNK_Send_Capabilities or
* PE_SRC_Send_Capabilities state when:
* 1) An Accept Message has been received.
@@ -2713,6 +2727,22 @@ static void pe_send_soft_reset_run(int port)
return;
}
}
+
+ /*
+ * Transition to PE_SNK_Hard_Reset or PE_SRC_Hard_Reset on Sender
+ * Response Timer Timeout or Protocol Layer or Protocol Error
+ */
+ if (get_time().val > pe[port].sender_response_timer ||
+ PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) {
+ PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR);
+
+ if (pe[port].power_role == PD_ROLE_SINK)
+ set_state_pe(port, PE_SRC_HARD_RESET);
+ else
+ set_state_pe(port, PE_SRC_HARD_RESET);
+ return;
+ }
+
}
static void pe_send_soft_reset_exit(int port)
diff --git a/common/usbc/usb_tc_drp_acc_trysrc_sm.c b/common/usbc/usb_tc_drp_acc_trysrc_sm.c
index c18eff6219..b8ceb5b7da 100644
--- a/common/usbc/usb_tc_drp_acc_trysrc_sm.c
+++ b/common/usbc/usb_tc_drp_acc_trysrc_sm.c
@@ -262,6 +262,8 @@ enum pd_dual_role_states drp_state[CONFIG_USB_PD_PORT_MAX_COUNT] = {
[0 ... (CONFIG_USB_PD_PORT_MAX_COUNT - 1)] =
CONFIG_USB_PD_INITIAL_DRP_STATE};
+static uint8_t saved_flgs[CONFIG_USB_PD_PORT_MAX_COUNT];
+
#ifdef CONFIG_USBC_VCONN
static void set_vconn(int port, int enable);
#endif
@@ -1022,8 +1024,54 @@ static void restart_tc_sm(int port, enum usb_tc_state start_state)
void tc_state_init(int port)
{
- /* Unattached.SNK is the default starting state. */
- restart_tc_sm(port, TC_UNATTACHED_SNK);
+ /*
+ * If there's an explicit contract in place, let's restore the data and
+ * power roles such that any messages we send to the port partner will
+ * still be valid.
+ */
+ if (pd_comm_is_enabled(port) &&
+ (pd_get_saved_port_flags(port, &saved_flgs[port]) ==
+ EC_SUCCESS) &&
+ (saved_flgs[port] & PD_BBRMFLG_EXPLICIT_CONTRACT)) {
+ /* Only attempt to maintain previous sink contracts */
+ if ((saved_flgs[port] & PD_BBRMFLG_POWER_ROLE) ==
+ PD_ROLE_SINK) {
+ tc_set_power_role(port,
+ (saved_flgs[port] & PD_BBRMFLG_POWER_ROLE) ?
+ PD_ROLE_SOURCE : PD_ROLE_SINK);
+ tc_set_data_role(port,
+ (saved_flgs[port] & PD_BBRMFLG_DATA_ROLE) ?
+ PD_ROLE_DFP : PD_ROLE_UFP);
+#ifdef CONFIG_USBC_VCONN
+ set_vconn(port,
+ (saved_flgs[port] & PD_BBRMFLG_VCONN_ROLE) ?
+ PD_ROLE_VCONN_ON : PD_ROLE_VCONN_OFF);
+#endif /* CONFIG_USBC_VCONN */
+ if (IS_ENABLED(CONFIG_USB_PE_SM))
+ pe_set_sysjump();
+
+ set_state_tc(port, TC_ATTACHED_SNK);
+ } else {
+ restart_tc_sm(port, TC_UNATTACHED_SNK);
+ /*
+ * Vbus was turned off during the power supply reset
+ * earlier, so clear the contract flag and re-start as
+ * default role
+ */
+ pd_update_saved_port_flags(port,
+ PD_BBRMFLG_EXPLICIT_CONTRACT, 0);
+ }
+ /*
+ * Set the TCPC reset event such that we can set our CC
+ * terminations, determine polarity, and enable RX so we
+ * can hear back from our port partner if maintaining our old
+ * connection.
+ */
+ task_set_event(task_get_current(), PD_EVENT_TCPC_RESET, 0);
+ } else {
+ /* Unattached.SNK is the default starting state. */
+ restart_tc_sm(port, TC_UNATTACHED_SNK);
+ }
/*
* If the TCPC isn't accessed, it will enter low power mode
@@ -1076,6 +1124,7 @@ uint8_t tc_get_pd_enabled(int port)
void tc_set_power_role(int port, enum pd_power_role role)
{
tc[port].power_role = role;
+ pd_update_saved_port_flags(port, PD_BBRMFLG_POWER_ROLE, role);
}
/*
@@ -1133,10 +1182,19 @@ void tc_event_check(int port, int evt)
}
#endif /* CONFIG_POWER_COMMON */
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
- if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) {
- if (evt & PD_EVENT_SYSJUMP) {
- pe_exit_dp_mode(port);
- notify_sysjump_ready(&sysjump_task_waiting);
+ {
+ int i;
+
+ if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) {
+ /*
+ * Notify all ports of sysjump
+ */
+ if (evt & PD_EVENT_SYSJUMP) {
+ for (i = 0; i <
+ CONFIG_USB_PD_PORT_MAX_COUNT; i++)
+ pe_exit_dp_mode(i);
+ notify_sysjump_ready(&sysjump_task_waiting);
+ }
}
}
#endif
@@ -1163,6 +1221,8 @@ void tc_set_data_role(int port, enum pd_data_role role)
{
tc[port].data_role = role;
+ pd_update_saved_port_flags(port, PD_BBRMFLG_DATA_ROLE, role);
+
if (IS_ENABLED(CONFIG_USBC_SS_MUX))
set_usb_mux_with_current_data_role(port);
@@ -1210,6 +1270,8 @@ static void set_vconn(int port, int enable)
else
TC_CLR_FLAG(port, TC_FLAGS_VCONN_ON);
+ pd_update_saved_port_flags(port, PD_BBRMFLG_VCONN_ROLE, enable);
+
/*
* TODO(chromium:951681): When we are sourcing VCONN, we should make
* sure to remove our termination on that CC line first.
@@ -1647,6 +1709,13 @@ static void tc_unattached_snk_entry(const int port)
print_current_state(port);
}
+ /*
+ * Tell Policy Engine to invalidate the explicit contract.
+ * This mainly used to clear the BB Ram Explicit Contract
+ * value.
+ */
+ pe_invalidate_explicit_contract(port);
+
tc[port].data_role = PD_ROLE_DISCONNECTED;
/*
diff --git a/include/system.h b/include/system.h
index ced5446eee..03a2c15238 100644
--- a/include/system.h
+++ b/include/system.h
@@ -355,6 +355,9 @@ enum system_bbram_idx {
SYSTEM_BBRAM_IDX_TRY_SLOT,
};
+/* Maximum number of bbram indexes allotted for PD port state data */
+#define MAX_SYSTEM_BBRAM_IDX_PD_PORTS 3
+
/**
* Get/Set byte in battery-backed storage.
*
diff --git a/include/usb_common.h b/include/usb_common.h
index 5078ff071e..be7ead8055 100644
--- a/include/usb_common.h
+++ b/include/usb_common.h
@@ -159,4 +159,22 @@ void notify_sysjump_ready(volatile const task_id_t * const
* @param port USB-C port number
*/
void set_usb_mux_with_current_data_role(int port);
+
+/**
+ * Get the PD flags stored in BB Ram
+ *
+ * @param port USB-C port number
+ * @param flags pointer where flags are written to
+ * @return EC_SUCCESS on success
+ */
+int pd_get_saved_port_flags(int port, uint8_t *flags);
+
+/**
+ * Update the flag in BB Ram with the give value
+ *
+ * @param port USB-C port number
+ * @param flag BB Ram flag to update
+ * @param do_set value written to the BB Ram flag
+ */
+void pd_update_saved_port_flags(int port, uint8_t flag, uint8_t do_set);
#endif /* __CROS_EC_USB_COMMON_H */
diff --git a/include/usb_pd.h b/include/usb_pd.h
index 319d3471fe..1d32b86da0 100644
--- a/include/usb_pd.h
+++ b/include/usb_pd.h
@@ -228,6 +228,7 @@ enum pd_rx_errors {
#define PD_T_SRC_DISCONNECT (15*MSEC) /* 15ms */
#define PD_T_VCONN_STABLE (50*MSEC) /* 50ms */
#define PD_T_DISCOVER_IDENTITY (45*MSEC) /* between 40ms and 50ms */
+#define PD_T_SYSJUMP (1000*MSEC) /* 1s */
/* number of edges and time window to detect CC line is not idle */
#define PD_RX_TRANSITION_COUNT 3
diff --git a/include/usb_pe_sm.h b/include/usb_pe_sm.h
index c22103efa4..2a9d466b2e 100644
--- a/include/usb_pe_sm.h
+++ b/include/usb_pe_sm.h
@@ -177,5 +177,17 @@ void pe_dpm_request(int port, enum pe_dpm_request req);
*/
int pd_is_port_partner_dualrole(int port);
+/*
+ * Informs the Policy Engine that a sysjump has occurred
+ */
+void pe_set_sysjump(void);
+
+/*
+ * Informs the Policy Engine that it should invalidate the
+ * explicit contract.
+ *
+ * @param port USB-C port number
+ */
+void pe_invalidate_explicit_contract(int port);
#endif /* __CROS_EC_USB_PE_H */
diff --git a/test/fake_usbc.c b/test/fake_usbc.c
index 73c992676b..a096652020 100644
--- a/test/fake_usbc.c
+++ b/test/fake_usbc.c
@@ -126,6 +126,10 @@ __overridable void tc_start_error_recovery(int port)
__overridable void tc_snk_power_off(int port)
{}
+__overridable void pe_invalidate_explicit_contract(int port)
+{
+}
+
int pd_dev_store_rw_hash(int port, uint16_t dev_id, uint32_t *rw_hash,
uint32_t ec_image)
{