summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Hurst <shurst@google.com>2019-11-07 17:55:55 -0800
committerCommit Bot <commit-bot@chromium.org>2019-11-21 23:41:16 +0000
commit2e2dcb986bfe952cd9a87bf2cd3498fafde6e805 (patch)
tree2d63c1ceb0ab7bad0f2e414b73243307dff87676
parent154f597b8656fbcc16f6652f6fe8713943440939 (diff)
downloadchrome-ec-2e2dcb986bfe952cd9a87bf2cd3498fafde6e805.tar.gz
TCPMv2: Enable TCPC low power when port is looking for a connection
When the port is in a state where it is looking for a connection, to save power, we should put the TCPC in its low power mode and enable auto toggling. Low power mode can happen when DRP auto toggling, acting as a SNK only, or acting as a SRC only. BUG=chromium:1022217 BRANCH=none TEST=make -j buildall manual tests: 1: (S0) Nothing plugged in, port is drp and low power mode 2: (S5/S3/S0ix) Port is SNK only, and low power with nothing plugged in 3: (S3/S0ix) If TypeC sink was previously plugged in, port remains powered 4: (S5/S3/S0ix) TypeC source is recognized 5: (S3->S0) TypeC sink plugged in, port is powered when S0 is reached Low power exit test: Using this command from the AP console: ectool i2cread 8 2 0x16 0x0d Transfer failed with status=0x1 # This means the TCPC was asleep. On the EC console: 2019-11-21 09:50:24 [315.235538 TCPC p1 init ready] 2019-11-21 09:50:24 [315.236048 TCPC p1 Exit Low Power Mode] 2019-11-21 09:50:24 [315.242837 TCPC p1 init ready] 2019-11-21 09:50:24 [315.243229 C1: DRPAutoToggle] 2019-11-21 09:50:24 [315.246471 C1: Unattached.SNK] 2019-11-21 09:50:24 [315.252504 C1: DRPAutoToggle] 2019-11-21 09:50:24 [315.362878 C1: LowPowerMode] 2019-11-21 09:50:24 [315.363314 TCPC p1 Enter Low Power Mode] Change-Id: I7e853d05e0ece1f6b3031f17a18fcbf0d9a15a51 Signed-off-by: Sam Hurst <shurst@google.com> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1904974 Reviewed-by: Edward Hill <ecgh@chromium.org> Reviewed-by: Jett Rink <jettrink@chromium.org>
-rw-r--r--common/usb_common.c69
-rw-r--r--common/usb_pd_protocol.c99
-rw-r--r--common/usbc/usb_tc_drp_acc_trysrc_sm.c542
-rw-r--r--common/usbc/usbc_task.c16
-rw-r--r--fuzz/usb_pd_fuzz.c8
-rw-r--r--include/usb_common.h22
-rw-r--r--include/usb_pd.h3
-rw-r--r--include/usb_tc_sm.h14
-rw-r--r--test/usb_typec_drp_acc_trysrc.c1
9 files changed, 518 insertions, 256 deletions
diff --git a/common/usb_common.c b/common/usb_common.c
index f7c47fc787..a068142ae5 100644
--- a/common/usb_common.c
+++ b/common/usb_common.c
@@ -11,6 +11,7 @@
#include "common.h"
#include "charge_state.h"
#include "task.h"
+#include "usb_common.h"
#include "usb_pd.h"
#include "usb_pd_tcpm.h"
#include "util.h"
@@ -310,3 +311,71 @@ __attribute__((weak)) uint8_t board_get_usb_pd_port_count(void)
{
return CONFIG_USB_PD_PORT_MAX_COUNT;
}
+
+enum pd_drp_next_states drp_auto_toggle_next_state(
+ uint64_t *drp_sink_time,
+ enum pd_power_role power_role,
+ enum pd_dual_role_states drp_state,
+ enum tcpc_cc_voltage_status cc1,
+ enum tcpc_cc_voltage_status cc2)
+{
+ /* Set to appropriate port state */
+ if (cc_is_open(cc1, cc2)) {
+ /*
+ * If nothing is attached then use drp_state to determine next
+ * state. If DRP auto toggle is still on, then remain in the
+ * DRP_AUTO_TOGGLE state. Otherwise, stop dual role toggling
+ * and go to a disconnected state.
+ */
+ switch (drp_state) {
+ case PD_DRP_TOGGLE_OFF:
+ return DRP_TC_DEFAULT;
+ case PD_DRP_FREEZE:
+ if (power_role == PD_ROLE_SINK)
+ return DRP_TC_UNATTACHED_SNK;
+ else
+ return DRP_TC_UNATTACHED_SRC;
+ case PD_DRP_FORCE_SINK:
+ return DRP_TC_UNATTACHED_SNK;
+ case PD_DRP_FORCE_SOURCE:
+ return DRP_TC_UNATTACHED_SRC;
+ case PD_DRP_TOGGLE_ON:
+ default:
+ return DRP_TC_DRP_AUTO_TOGGLE;
+ }
+ } else if ((cc_is_rp(cc1) || cc_is_rp(cc2)) &&
+ drp_state != PD_DRP_FORCE_SOURCE) {
+ /* SNK allowed unless ForceSRC */
+ return DRP_TC_UNATTACHED_SNK;
+ } else if (cc_is_at_least_one_rd(cc1, cc2) ||
+ cc_is_audio_acc(cc1, cc2)) {
+ /*
+ * SRC allowed unless ForceSNK or Toggle Off
+ *
+ * Ideally we wouldn't use auto-toggle when drp_state is
+ * TOGGLE_OFF/FORCE_SINK, but for some TCPCs, auto-toggle can't
+ * be prevented in low power mode. Try being a sink in case the
+ * connected device is dual-role (this ensures reliable charging
+ * from a hub, b/72007056). 100 ms is enough time for a
+ * dual-role partner to switch from sink to source. If the
+ * connected device is sink-only, then we will attempt
+ * TC_UNATTACHED_SNK twice (due to debounce time), then return
+ * to low power mode (and stay there). After 200 ms, reset
+ * ready for a new connection.
+ */
+ if (drp_state == PD_DRP_TOGGLE_OFF ||
+ drp_state == PD_DRP_FORCE_SINK) {
+ if (get_time().val > *drp_sink_time + 200*MSEC)
+ *drp_sink_time = get_time().val;
+ if (get_time().val < *drp_sink_time + 100*MSEC)
+ return DRP_TC_UNATTACHED_SNK;
+ else
+ return DRP_TC_DRP_AUTO_TOGGLE;
+ } else {
+ return DRP_TC_UNATTACHED_SRC;
+ }
+ } else {
+ /* Anything else, keep toggling */
+ return DRP_TC_DRP_AUTO_TOGGLE;
+ }
+}
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c
index 61850a98c2..641909cbb9 100644
--- a/common/usb_pd_protocol.c
+++ b/common/usb_pd_protocol.c
@@ -2578,82 +2578,6 @@ static void pd_partner_port_reset(int port)
}
#endif /* CONFIG_USB_PD_DUAL_ROLE */
-#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
-static enum pd_states drp_auto_toggle_next_state(int port,
- enum tcpc_cc_voltage_status cc1, enum tcpc_cc_voltage_status cc2)
-{
- enum pd_states next_state;
-
- /* Set to appropriate port state */
- if (cc_is_open(cc1, cc2)) {
- /*
- * If nothing is attached then use drp_state to determine next
- * state. If DRP auto toggle is still on, then remain in the
- * DRP_AUTO_TOGGLE state. Otherwise, stop dual role toggling
- * and go to a disconnected state.
- */
- switch (drp_state[port]) {
- case PD_DRP_TOGGLE_OFF:
- next_state = PD_DEFAULT_STATE(port);
- break;
-
- case PD_DRP_FREEZE:
- if (pd[port].power_role == PD_ROLE_SINK)
- next_state = PD_STATE_SNK_DISCONNECTED;
- else
- next_state = PD_STATE_SRC_DISCONNECTED;
- break;
-
- case PD_DRP_FORCE_SINK:
- next_state = PD_STATE_SNK_DISCONNECTED;
- break;
-
- case PD_DRP_FORCE_SOURCE:
- next_state = PD_STATE_SRC_DISCONNECTED;
- break;
-
- case PD_DRP_TOGGLE_ON:
- default:
- next_state = PD_STATE_DRP_AUTO_TOGGLE;
- break;
- }
- } else if ((cc_is_rp(cc1) || cc_is_rp(cc2)) &&
- drp_state[port] != PD_DRP_FORCE_SOURCE) {
- /* SNK allowed unless ForceSRC */
- next_state = PD_STATE_SNK_DISCONNECTED;
- } else if (cc_is_at_least_one_rd(cc1, cc2) ||
- cc_is_audio_acc(cc1, cc2)) {
- /*
- * SRC allowed unless ForceSNK or Toggle Off
- *
- * Ideally we wouldn't use auto-toggle when drp_state is
- * TOGGLE_OFF/FORCE_SINK, but for some TCPCs, auto-toggle can't
- * be prevented in low power mode. Try being a sink in case the
- * connected device is dual-role (this ensures reliable charging
- * from a hub, b/72007056). 100 ms is enough time for a
- * dual-role partner to switch from sink to source. If the
- * connected device is sink-only, then we will attempt
- * SNK_DISCONNECTED twice (due to debounce time), then return to
- * low power mode (and stay there). After 200 ms, reset ready
- * for a new connection.
- */
- if (drp_state[port] == PD_DRP_TOGGLE_OFF ||
- drp_state[port] == PD_DRP_FORCE_SINK) {
- if (get_time().val > pd[port].drp_sink_time + 200*MSEC)
- pd[port].drp_sink_time = get_time().val;
- if (get_time().val < pd[port].drp_sink_time + 100*MSEC)
- next_state = PD_STATE_SNK_DISCONNECTED;
- else
- next_state = PD_STATE_DRP_AUTO_TOGGLE;
- } else
- next_state = PD_STATE_SRC_DISCONNECTED;
- } else
- /* Anything else, keep toggling */
- next_state = PD_STATE_DRP_AUTO_TOGGLE;
- return next_state;
-}
-#endif /* CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE */
-
int pd_get_polarity(int port)
{
return pd[port].polarity;
@@ -4593,7 +4517,7 @@ void pd_task(void *u)
#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
case PD_STATE_DRP_AUTO_TOGGLE:
{
- enum pd_states next_state;
+ enum pd_drp_next_states next_state;
assert(auto_toggle_supported);
@@ -4611,7 +4535,11 @@ void pd_task(void *u)
/* Check for connection */
tcpm_get_cc(port, &cc1, &cc2);
- next_state = drp_auto_toggle_next_state(port, cc1, cc2);
+ next_state = drp_auto_toggle_next_state(
+ &pd[port].drp_sink_time,
+ pd[port].power_role,
+ drp_state[port],
+ cc1, cc2);
#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
/*
@@ -4623,15 +4551,24 @@ void pd_task(void *u)
if (cc_is_open(cc1, cc2))
pd[port].flags |= PD_FLAGS_LPM_REQUESTED;
#endif
+ if (next_state == DRP_TC_DEFAULT) {
+ if (PD_DEFAULT_STATE(port) ==
+ PD_STATE_SNK_DISCONNECTED)
+ next_state = DRP_TC_UNATTACHED_SNK;
+ else
+ next_state = DRP_TC_UNATTACHED_SRC;
+ }
- if (next_state == PD_STATE_SNK_DISCONNECTED) {
+ if (next_state == DRP_TC_UNATTACHED_SNK) {
tcpm_set_cc(port, TYPEC_CC_RD);
pd_set_power_role(port, PD_ROLE_SINK);
timeout = 2*MSEC;
- } else if (next_state == PD_STATE_SRC_DISCONNECTED) {
+ set_state(port, PD_STATE_SNK_DISCONNECTED);
+ } else if (next_state == DRP_TC_UNATTACHED_SRC) {
tcpm_set_cc(port, TYPEC_CC_RP);
pd_set_power_role(port, PD_ROLE_SOURCE);
timeout = 2*MSEC;
+ set_state(port, PD_STATE_SRC_DISCONNECTED);
} else {
/*
* We are staying in PD_STATE_DRP_AUTO_TOGGLE,
@@ -4640,8 +4577,8 @@ void pd_task(void *u)
tcpm_enable_drp_toggle(port);
pd[port].flags |= PD_FLAGS_TCPC_DRP_TOGGLE;
timeout = -1;
+ set_state(port, PD_STATE_DRP_AUTO_TOGGLE);
}
- set_state(port, next_state);
break;
}
diff --git a/common/usbc/usb_tc_drp_acc_trysrc_sm.c b/common/usbc/usb_tc_drp_acc_trysrc_sm.c
index de95682a64..9ec21cdd26 100644
--- a/common/usbc/usb_tc_drp_acc_trysrc_sm.c
+++ b/common/usbc/usb_tc_drp_acc_trysrc_sm.c
@@ -34,7 +34,6 @@
#endif
/* Type-C Layer Flags */
-
/* Flag to note we are sourcing VCONN */
#define TC_FLAGS_VCONN_ON BIT(0)
/* Flag to note port partner has Rp/Rp or Rd/Rd */
@@ -79,6 +78,25 @@
#define TC_FLAGS_DO_PR_SWAP BIT(20)
/* Flag to note we are performing Discover Identity */
#define TC_FLAGS_DISC_IDENT_IN_PROGRESS BIT(21)
+/* Flag to note we should wake from LPM */
+#define TC_FLAGS_WAKE_FROM_LPM BIT(22)
+/* Flag to note a chipset power state has changed */
+#define TC_FLAGS_POWER_STATE_CHANGE BIT(23)
+/* Flag to note the TCPM supports auto toggle */
+#define TC_FLAGS_AUTO_TOGGLE_SUPPORTED BIT(24)
+
+/*
+ * Clear all flags except TC_FLAGS_AUTO_TOGGLE_SUPPORTED,
+ * TC_FLAGS_LPM_REQUESTED, and TC_FLAGS_LPM_ENGAGED if
+ * they are set.
+ */
+#define CLR_ALL_BUT_LPM_FLAGS(port) (tc[port].flags &= \
+ (TC_FLAGS_AUTO_TOGGLE_SUPPORTED | \
+ TC_FLAGS_LPM_REQUESTED | \
+ TC_FLAGS_LPM_ENGAGED))
+
+/* 100 ms is enough time for any TCPC transaction to complete. */
+#define PD_LPM_DEBOUNCE_US (100 * MSEC)
enum ps_reset_sequence {
PS_STATE0,
@@ -102,6 +120,12 @@ enum usb_tc_state {
TC_ATTACHED_SRC,
TC_TRY_SRC,
TC_TRY_WAIT_SNK,
+#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
+ TC_DRP_AUTO_TOGGLE,
+#endif
+#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
+ TC_LOW_POWER_MODE,
+#endif
#ifdef CONFIG_USB_PE_SM
TC_CT_UNATTACHED_SNK,
TC_CT_ATTACHED_SNK,
@@ -130,6 +154,17 @@ static const char * const tc_state_names[] = {
[TC_ATTACHED_SRC] = "Attached.SRC",
[TC_TRY_SRC] = "Try.SRC",
[TC_TRY_WAIT_SNK] = "TryWait.SNK",
+#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
+ [TC_DRP_AUTO_TOGGLE] = "DRPAutoToggle",
+#endif
+#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
+ [TC_LOW_POWER_MODE] = "LowPowerMode",
+#endif
+#ifdef CONFIG_USB_PE_SM
+ [TC_CT_UNATTACHED_SNK] = "CTUnattached.SNK",
+ [TC_CT_ATTACHED_SNK] = "CTAttached.SNK",
+#endif
+
};
#endif
@@ -148,6 +183,11 @@ static struct type_c {
enum pd_data_role data_role;
/* Higher-level power deliver state machines are enabled if true. */
uint8_t pd_enable;
+ /*
+ * Timer for handling TOGGLE_OFF/FORCE_SINK mode when auto-toggle
+ * enabled. See drp_auto_toggle_next_state() for details.
+ */
+ uint64_t drp_sink_time;
#ifdef CONFIG_USB_PE_SM
/* Power supply reset sequence during a hard reset */
enum ps_reset_sequence ps_reset_state;
@@ -179,14 +219,12 @@ static struct type_c {
uint64_t next_role_swap;
/* Generic timer */
uint64_t timeout;
-#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
/* Time to enter low power mode */
uint64_t low_power_time;
/* Tasks to notify after TCPC has been reset */
int tasks_waiting_on_reset;
/* Tasks preventing TCPC from entering low power mode */
int tasks_preventing_lpm;
-#endif
/* Voltage on CC pin */
enum tcpc_cc_voltage_status cc_voltage;
/* Type-C current */
@@ -200,6 +238,7 @@ static struct type_c {
} tc[CONFIG_USB_PD_PORT_MAX_COUNT];
/* Port dual-role state */
+static volatile __maybe_unused
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};
@@ -224,17 +263,11 @@ static struct ec_params_usb_pd_rw_hash_entry rw_hash_table[RW_HASH_ENTRIES];
#endif
/* Forward declare common, private functions */
-#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
-static void exit_low_power_mode(int port);
-static void handle_device_access(int port);
-static int pd_device_in_low_power(int port);
-static void pd_wait_for_wakeup(int port);
-static int reset_device_and_notify(int port);
-#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */
+static __maybe_unused int reset_device_and_notify(int port);
#ifdef CONFIG_POWER_COMMON
static void handle_new_power_state(int port);
-#endif
+#endif /* CONFIG_POWER_COMMON */
static void pd_update_dual_role_config(int port);
#endif /* CONFIG_USB_PE_SM */
@@ -347,8 +380,8 @@ void pd_request_power_swap(int port)
}
}
-#ifdef CONFIG_USB_PE_SM
-void pd_set_dual_role(int port, enum pd_dual_role_states state)
+static inline void pd_set_dual_role_no_wakeup(int port,
+ enum pd_dual_role_states state)
{
drp_state[port] = state;
@@ -356,6 +389,16 @@ void pd_set_dual_role(int port, enum pd_dual_role_states state)
pd_update_try_source();
}
+void pd_set_dual_role(int port, enum pd_dual_role_states state)
+{
+ pd_set_dual_role_no_wakeup(port, state);
+
+ /* Wake task up to process change */
+ task_set_event(PD_PORT_TO_TASK_ID(port),
+ PD_EVENT_UPDATE_DUAL_ROLE, 0);
+}
+
+#ifdef CONFIG_USB_PE_SM
int pd_get_partner_data_swap_capable(int port)
{
/* return data swap capable status of port partner */
@@ -703,6 +746,15 @@ static void restart_tc_sm(int port, enum usb_tc_state start_state)
tc[port].flags = 0;
+#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
+ /*
+ * Some TCPCs may not support DRP Auto Toggle, so query the
+ * query the TCPC for DRP Auto toggle support.
+ */
+ if (tcpm_auto_toggle_supported(port))
+ TC_SET_FLAG(port, TC_FLAGS_AUTO_TOGGLE_SUPPORTED);
+#endif
+
#ifdef CONFIG_USB_PE_SM
tc[port].pd_enable = 0;
tc[port].ps_reset_state = PS_STATE0;
@@ -713,6 +765,12 @@ void tc_state_init(int port)
{
/* 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
+ * after PD_LPM_DEBOUNCE_US.
+ */
+ tc[port].low_power_time = get_time().val + PD_LPM_DEBOUNCE_US;
}
enum pd_power_role tc_get_power_role(int port)
@@ -777,40 +835,34 @@ static void print_current_state(const int port)
}
#ifdef CONFIG_USB_PE_SM
-#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
-/* This is only called from the PD tasks that owns the port. */
-static void exit_low_power_mode(int port)
+static void handle_device_access(int port)
{
- if (!IS_ENABLED(CONFIG_USB_PE_SM) &&
- !IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER))
- return;
-
- if (TC_CHK_FLAG(port, TC_FLAGS_LPM_ENGAGED))
- reset_device_and_notify(port);
- else
- TC_CLR_FLAG(port, TC_FLAGS_LPM_REQUESTED);
+ tc[port].low_power_time = get_time().val + PD_LPM_DEBOUNCE_US;
}
#endif
-#endif
void tc_event_check(int port, int evt)
{
#ifdef CONFIG_USB_PE_SM
if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER)) {
if (evt & PD_EXIT_LOW_POWER_EVENT_MASK)
- exit_low_power_mode(port);
-
+ TC_SET_FLAG(port, TC_FLAGS_WAKE_FROM_LPM);
if (evt & PD_EVENT_DEVICE_ACCESSED)
handle_device_access(port);
}
+ /* if TCPC has reset, then need to initialize it again */
+ if (evt & PD_EVENT_TCPC_RESET)
+ reset_device_and_notify(port);
+
#ifdef CONFIG_POWER_COMMON
if (IS_ENABLED(CONFIG_POWER_COMMON)) {
- if (evt & PD_EVENT_POWER_STATE_CHANGE)
+ if (evt & PD_EVENT_POWER_STATE_CHANGE) {
+ TC_SET_FLAG(port, TC_FLAGS_POWER_STATE_CHANGE);
handle_new_power_state(port);
+ }
}
-#endif
-
+#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) {
@@ -867,14 +919,11 @@ static void sink_stop_drawing_current(int port)
}
#ifdef CONFIG_USB_PD_TRY_SRC
-/*
- * TODO(b/137493121): Move this function to a separate file that's shared
- * between the this and the original stack.
- */
static void pd_update_try_source(void)
{
int i;
int try_src = 0;
+ static struct mutex pd_try_src_enable_lock;
int batt_soc = usb_get_battery_soc();
@@ -883,6 +932,13 @@ static void pd_update_try_source(void)
try_src |= drp_state[i] == PD_DRP_TOGGLE_ON;
/*
+ * This function is called from this PD task and the hooks tasks.
+ * A lock is added here to serialize access to the
+ * pd_try_source_enable variable.
+ */
+ mutex_lock(&pd_try_src_enable_lock);
+
+ /*
* Enable try source when dual-role toggling AND battery is present
* and at some minimum percentage.
*/
@@ -907,6 +963,7 @@ static void pd_update_try_source(void)
pd_try_src_enable &= (battery_is_present() == BP_YES);
#endif /* CONFIG_BATTERY_PRESENT_[CUSTOM|GPIO] */
+ mutex_unlock(&pd_try_src_enable_lock);
}
DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, pd_update_try_source, HOOK_PRIO_DEFAULT);
#endif /* CONFIG_USB_PD_TRY_SRC */
@@ -972,7 +1029,6 @@ void pd_deferred_resume(int port)
#endif /* CONFIG_USB_PD_DEFERRED_RESUME */
#ifdef CONFIG_USB_PE_SM
-
/* This must only be called from the PD task */
static void pd_update_dual_role_config(int port)
{
@@ -1469,64 +1525,15 @@ void pd_handle_overcurrent(int port)
#endif /* defined(CONFIG_USBC_PPC) */
#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
-/* 10 ms is enough time for any TCPC transaction to complete. */
-#define PD_LPM_DEBOUNCE_US (10 * MSEC)
-
-/* This is only called from the PD tasks that owns the port. */
-static void handle_device_access(int port)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER))
- return;
-
- /* This should only be called from the PD task */
- assert(port == TASK_ID_TO_PD_PORT(task_get_current()));
-
- tc[port].low_power_time = get_time().val + PD_LPM_DEBOUNCE_US;
- if (TC_CHK_FLAG(port, TC_FLAGS_LPM_ENGAGED)) {
- CPRINTS("TCPC p%d Exit Low Power Mode", port);
- TC_CLR_FLAG(port, TC_FLAGS_LPM_ENGAGED |
- TC_FLAGS_LPM_REQUESTED);
- /*
- * Wake to ensure we make another pass through the main task
- * loop after clearing the flags.
- */
- task_wake(PD_PORT_TO_TASK_ID(port));
- }
-}
-
-static int pd_device_in_low_power(int port)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER))
- return 0;
- /*
- * If we are actively waking the device up in the PD task, do not
- * let TCPC operation wait or retry because we are in low power mode.
- */
- if (port == TASK_ID_TO_PD_PORT(task_get_current()) &&
- TC_CHK_FLAG(port, TC_FLAGS_LPM_TRANSITION))
- return 0;
-
- return TC_CHK_FLAG(port, TC_FLAGS_LPM_ENGAGED);
-}
-
-/*
- * TODO(b/137493121): Move this function to a separate file that's shared
- * between the this and the original stack.
- */
static int reset_device_and_notify(int port)
{
int rv;
int task, waiting_tasks;
- if (!IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER))
- return 0;
-
/* This should only be called from the PD task */
assert(port == TASK_ID_TO_PD_PORT(task_get_current()));
- TC_SET_FLAG(port, TC_FLAGS_LPM_TRANSITION);
- rv = tcpm_init(port);
- TC_CLR_FLAG(port, TC_FLAGS_LPM_TRANSITION);
+ rv = tc_restart_tcpc(port);
if (rv == EC_SUCCESS)
CPRINTS("TCPC p%d init ready", port);
@@ -1549,15 +1556,6 @@ static int reset_device_and_notify(int port)
waiting_tasks = atomic_read_clear(&tc[port].tasks_waiting_on_reset);
- /*
- * Now that we are done waking up the device, handle device access
- * manually because we ignored it while waking up device.
- */
- handle_device_access(port);
-
- /* Clear SW LPM state; the state machine will set it again if needed */
- TC_CLR_FLAG(port, TC_FLAGS_LPM_REQUESTED);
-
/* Wake up all waiting tasks. */
while (waiting_tasks) {
task = __fls(waiting_tasks);
@@ -1568,69 +1566,45 @@ static int reset_device_and_notify(int port)
return rv;
}
-/*
- * TODO(b/137493121): Move this function to a separate file that's shared
- * between the this and the original stack.
- */
-static void pd_wait_for_wakeup(int port)
+void pd_wait_exit_low_power(int port)
{
- if (!IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER))
- return;
+ if (TC_CHK_FLAG(port, TC_FLAGS_LPM_ENGAGED)) {
+ TC_SET_FLAG(port, TC_FLAGS_WAKE_FROM_LPM);
- if (port == TASK_ID_TO_PD_PORT(task_get_current())) {
- /* If we are in the PD task, we can directly reset */
- reset_device_and_notify(port);
- } else {
- /* Otherwise, we need to wait for the TCPC reset to complete */
- atomic_or(&tc[port].tasks_waiting_on_reset,
- 1 << task_get_current());
- /*
- * NOTE: We could be sending the PD task the reset event while
- * it is already processing the reset event. If that occurs,
- * then we will reset the TCPC multiple times, which is
- * undesirable but most likely benign. Empirically, this doesn't
- * happen much, but it if starts occurring, we can add a guard
- * to prevent/reduce it.
- */
- task_set_event(PD_PORT_TO_TASK_ID(port),
- PD_EVENT_TCPC_RESET, 0);
- task_wait_event_mask(TASK_EVENT_PD_AWAKE, -1);
+ if (port != TASK_ID_TO_PD_PORT(task_get_current())) {
+ /*
+ * Otherwise, we need to wait for the TCPC reset to
+ * complete
+ */
+ atomic_or(&tc[port].tasks_waiting_on_reset,
+ 1 << task_get_current());
+ /*
+ * NOTE: We could be sending the PD task the reset
+ * event while it is already processing the reset event.
+ * If that occurs, then we will reset the TCPC multiple
+ * times, which is undesirable but most likely benign.
+ * Empirically, this doesn't happen much, but it if
+ * starts occurring, we can add a guard to
+ * prevent/reduce it.
+ */
+ task_set_event(PD_PORT_TO_TASK_ID(port),
+ PD_EVENT_TCPC_RESET, 0);
+ task_wait_event_mask(TASK_EVENT_PD_AWAKE, -1);
+ }
}
}
/*
- * TODO(b/137493121): Move this function to a separate file that's shared
- * between the this and the original stack.
- */
-void pd_wait_exit_low_power(int port)
-{
- if (pd_device_in_low_power(port))
- pd_wait_for_wakeup(port);
-}
-
-/*
- * TODO(b/137493121): Move this function to a separate file that's shared
- * between the this and the original stack.
- */
-/*
* This can be called from any task. If we are in the PD task, we can handle
* immediately. Otherwise, we need to notify the PD task via event.
*/
void pd_device_accessed(int port)
{
- if (!IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER))
- return;
-
- if (port == TASK_ID_TO_PD_PORT(task_get_current())) {
- /* Ignore any access to device while it is waking up */
- if (TC_CHK_FLAG(port, TC_FLAGS_LPM_TRANSITION))
- return;
-
+ if (port == TASK_ID_TO_PD_PORT(task_get_current()))
handle_device_access(port);
- } else {
+ else
task_set_event(PD_PORT_TO_TASK_ID(port),
PD_EVENT_DEVICE_ACCESSED, 0);
- }
}
/*
@@ -1774,7 +1748,7 @@ static void tc_unattached_snk_entry(const int port)
USB_SWITCH_DISCONNECT, tc[port].polarity);
if (IS_ENABLED(CONFIG_USB_PE_SM)) {
- tc[port].flags = 0;
+ CLR_ALL_BUT_LPM_FLAGS(port);
tc[port].pd_enable = 0;
}
}
@@ -1800,6 +1774,19 @@ static void tc_unattached_snk_run(const int port)
/* Check for connection */
tcpm_get_cc(port, &cc1, &cc2);
+#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
+ /*
+ * Attempt TCPC auto DRP toggle if it is
+ * not already auto toggling.
+ */
+ if (drp_state[port] == PD_DRP_TOGGLE_ON &&
+ TC_CHK_FLAG(port, TC_FLAGS_AUTO_TOGGLE_SUPPORTED) &&
+ cc_is_open(cc1, cc2)) {
+ set_state_tc(port, TC_DRP_AUTO_TOGGLE);
+ return;
+ }
+#endif
+
/*
* The port shall transition to AttachWait.SNK when a Source
* connection is detected, as indicated by the SNK.Rp state
@@ -1812,12 +1799,18 @@ static void tc_unattached_snk_run(const int port)
if (cc_is_rp(cc1) || cc_is_rp(cc2)) {
/* Connection Detected */
set_state_tc(port, TC_ATTACH_WAIT_SNK);
- } else if (get_time().val > tc[port].next_role_swap) {
+ } else if (get_time().val > tc[port].next_role_swap &&
+ drp_state[port] == PD_DRP_TOGGLE_ON) {
/* DRP Toggle */
set_state_tc(port, TC_UNATTACHED_SRC);
}
- return;
+#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
+ else if (drp_state[port] == PD_DRP_FORCE_SINK ||
+ drp_state[port] == PD_DRP_TOGGLE_OFF) {
+ set_state_tc(port, TC_LOW_POWER_MODE);
+ }
+#endif
}
/**
@@ -1923,6 +1916,9 @@ static void tc_attached_snk_entry(const int port)
print_current_state(port);
+ /* Clear Low Power Mode Request */
+ TC_CLR_FLAG(port, TC_FLAGS_LPM_REQUESTED);
+
#ifdef CONFIG_USB_PE_SM
if (TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) {
/*
@@ -2209,7 +2205,7 @@ static void tc_unattached_src_entry(const int port)
charge_manager_update_dualrole(port, CAP_UNKNOWN);
if (IS_ENABLED(CONFIG_USB_PE_SM)) {
- tc[port].flags = 0;
+ CLR_ALL_BUT_LPM_FLAGS(port);
tc[port].pd_enable = 0;
}
@@ -2251,8 +2247,27 @@ static void tc_unattached_src_run(const int port)
*/
if (cc_is_at_least_one_rd(cc1, cc2) || cc_is_audio_acc(cc1, cc2))
set_state_tc(port, TC_ATTACH_WAIT_SRC);
- else if (get_time().val > tc[port].next_role_swap)
+ else if (get_time().val > tc[port].next_role_swap &&
+ drp_state[port] != PD_DRP_FORCE_SOURCE &&
+ drp_state[port] != PD_DRP_FREEZE)
set_state_tc(port, TC_UNATTACHED_SNK);
+#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
+ /*
+ * Attempt TCPC auto DRP toggle
+ */
+ else if (drp_state[port] == PD_DRP_TOGGLE_ON &&
+ TC_CHK_FLAG(port, TC_FLAGS_AUTO_TOGGLE_SUPPORTED) &&
+ cc_is_open(cc1, cc2)) {
+ set_state_tc(port, TC_DRP_AUTO_TOGGLE);
+ }
+#endif
+
+#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
+ else if (drp_state[port] == PD_DRP_FORCE_SOURCE ||
+ drp_state[port] == PD_DRP_TOGGLE_OFF) {
+ set_state_tc(port, TC_LOW_POWER_MODE);
+ }
+#endif
}
/**
@@ -2324,7 +2339,6 @@ static void tc_attach_wait_src_run(const int port)
return;
}
}
-
}
/**
@@ -2336,13 +2350,18 @@ static void tc_attached_src_entry(const int port)
print_current_state(port);
+ /* Run function relies on timeout being 0 or meaningful */
+ tc[port].timeout = 0;
+
+ /* Clear Low Power Mode Request */
+ TC_CLR_FLAG(port, TC_FLAGS_LPM_REQUESTED);
+
#if defined(CONFIG_USB_PE_SM)
if (TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) {
/* Change role to source */
tc_set_power_role(port, PD_ROLE_SOURCE);
tcpm_set_msg_header(port,
tc[port].power_role, tc[port].data_role);
-
/*
* Both CC1 and CC2 pins shall be independently terminated to
* ground through Rp.
@@ -2357,6 +2376,17 @@ static void tc_attached_src_entry(const int port)
* data role / usb mux connections.
*/
} else {
+ /* Get connector orientation */
+ tcpm_get_cc(port, &cc1, &cc2);
+ tc[port].polarity = (cc1 != TYPEC_CC_VOLT_RD);
+ set_polarity(port, tc[port].polarity);
+
+ /*
+ * Initial data role for sink is DFP
+ * This also sets the usb mux
+ */
+ tc_set_data_role(port, PD_ROLE_DFP);
+
/*
* Start sourcing Vconn before Vbus to ensure
* we are within USB Type-C Spec 1.4 tVconnON
@@ -2375,17 +2405,6 @@ static void tc_attached_src_entry(const int port)
USB_SWITCH_DISCONNECT, tc[port].polarity);
}
- /* Get connector orientation */
- tcpm_get_cc(port, &cc1, &cc2);
- tc[port].polarity = (cc1 != TYPEC_CC_VOLT_RD);
- set_polarity(port, tc[port].polarity);
-
- /*
- * Initial data role for sink is DFP
- * This also sets the usb mux
- */
- tc_set_data_role(port, PD_ROLE_DFP);
-
tc[port].pd_enable = 0;
tc[port].timeout = get_time().val +
MAX(PD_POWER_SUPPLY_TURN_ON_DELAY, PD_T_VCONN_STABLE);
@@ -2421,12 +2440,6 @@ static void tc_attached_src_entry(const int port)
}
#endif /* CONFIG_USB_PE_SM */
- /* Apply Rp */
- tcpm_set_cc(port, TYPEC_CC_RP);
-
- tc[port].cc_debounce = 0;
- tc[port].cc_state = PD_CC_NONE;
-
/* Inform PPC that a sink is connected. */
if (IS_ENABLED(CONFIG_USBC_PPC))
ppc_sink_is_connected(port, 1);
@@ -2634,6 +2647,117 @@ static void tc_attached_src_exit(const int port)
TC_CLR_FLAG(port, TC_FLAGS_DO_PR_SWAP);
}
+#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
+/**
+ * DrpAutoToggle
+ */
+static void tc_drp_auto_toggle_entry(const int port)
+{
+ print_current_state(port);
+
+ /*
+ * The PD_EXIT_LOW_POWER_EVENT_MASK flag may have been set
+ * due to a CC event. Clear it now since we haven't engaged
+ * low power mode.
+ */
+ atomic_clear(task_get_event_bitmap(task_get_current()),
+ PD_EXIT_LOW_POWER_EVENT_MASK);
+
+ if (drp_state[port] == PD_DRP_TOGGLE_ON)
+ tcpm_enable_drp_toggle(port);
+}
+
+static void tc_drp_auto_toggle_run(const int port)
+{
+ enum pd_drp_next_states next_state;
+ enum tcpc_cc_voltage_status cc1, cc2;
+
+ /*
+ * If SW decided we should be in a low power state and
+ * the CC lines did not change, then don't talk with the
+ * TCPC otherwise we might wake it up.
+ */
+ if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER)) {
+ if (TC_CHK_FLAG(port, TC_FLAGS_LPM_REQUESTED) &&
+ !TC_CHK_FLAG(port, TC_FLAGS_WAKE_FROM_LPM)) {
+ if (get_time().val > tc[port].low_power_time)
+ set_state_tc(port, TC_LOW_POWER_MODE);
+ return;
+ }
+ }
+
+ /* Check for connection */
+ tcpm_get_cc(port, &cc1, &cc2);
+
+ tc[port].drp_sink_time = get_time().val;
+ next_state = drp_auto_toggle_next_state(&tc[port].drp_sink_time,
+ tc[port].power_role, drp_state[port], cc1, cc2);
+
+ /*
+ * The next state is not determined just by what is
+ * attached, but also depends on DRP_STATE. Regardless
+ * of next state, if nothing is attached, then always
+ * request low power mode.
+ */
+ if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER)) {
+ if (cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_OPEN &&
+ !tc[port].tasks_preventing_lpm) {
+ TC_SET_FLAG(port, TC_FLAGS_LPM_REQUESTED);
+ TC_CLR_FLAG(port, TC_FLAGS_WAKE_FROM_LPM);
+ }
+ }
+
+ switch (next_state) {
+ case DRP_TC_DEFAULT:
+ set_state_tc(port, PD_DEFAULT_STATE(port));
+ break;
+ case DRP_TC_UNATTACHED_SNK:
+ set_state_tc(port, TC_UNATTACHED_SNK);
+ break;
+ case DRP_TC_UNATTACHED_SRC:
+ set_state_tc(port, TC_UNATTACHED_SRC);
+ break;
+ case DRP_TC_DRP_AUTO_TOGGLE:
+ /*
+ * We are staying in PD_STATE_DRP_AUTO_TOGGLE
+ */
+ break;
+ }
+}
+#endif /* CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE */
+
+#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
+static void tc_low_power_mode_entry(const int port)
+{
+ print_current_state(port);
+ CPRINTS("TCPC p%d Enter Low Power Mode", port);
+ tcpm_enter_low_power_mode(port);
+ TC_SET_FLAG(port, TC_FLAGS_LPM_ENGAGED);
+}
+
+static void tc_low_power_mode_run(const int port)
+{
+#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
+ if (TC_CHK_FLAG(port, TC_FLAGS_WAKE_FROM_LPM |
+ TC_FLAGS_POWER_STATE_CHANGE)) {
+ set_state_tc(port, TC_DRP_AUTO_TOGGLE);
+ return;
+ }
+#endif
+ tc_pause_event_loop(port);
+}
+
+static void tc_low_power_mode_exit(const int port)
+{
+ CPRINTS("TCPC p%d Exit Low Power Mode", port);
+ TC_CLR_FLAG(port, TC_FLAGS_LPM_REQUESTED | TC_FLAGS_LPM_ENGAGED |
+ TC_FLAGS_WAKE_FROM_LPM | TC_FLAGS_POWER_STATE_CHANGE);
+ reset_device_and_notify(port);
+ tc_start_event_loop(port);
+}
+#endif
+
+
/**
* Try.SRC
*
@@ -2906,15 +3030,17 @@ static void tc_cc_rd_entry(const int port)
if (IS_ENABLED(CONFIG_USBC_VCONN))
set_vconn(port, 0);
+ /* Set power role to sink */
+ tc_set_power_role(port, PD_ROLE_SINK);
+ tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role);
+
/*
* Both CC1 and CC2 pins shall be independently terminated to
* ground through Rd.
*/
tcpm_set_cc(port, TYPEC_CC_RD);
- /* Set power role to sink */
- tc_set_power_role(port, PD_ROLE_SINK);
- tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role);
+
}
@@ -2988,6 +3114,67 @@ int pd_is_debug_acc(int port)
tc[port].cc_state == PD_CC_DFP_DEBUG_ACC;
}
+static void pd_chipset_resume(void)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) {
+ pd_set_dual_role(i, PD_DRP_TOGGLE_ON);
+ task_set_event(PD_PORT_TO_TASK_ID(i),
+ PD_EVENT_POWER_STATE_CHANGE, 0);
+ }
+
+ CPRINTS("PD:S3->S0");
+}
+DECLARE_HOOK(HOOK_CHIPSET_RESUME, pd_chipset_resume, HOOK_PRIO_DEFAULT);
+
+static void pd_chipset_suspend(void)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) {
+ pd_set_dual_role(i, PD_DRP_TOGGLE_OFF);
+ task_set_event(PD_PORT_TO_TASK_ID(i),
+ PD_EVENT_POWER_STATE_CHANGE, 0);
+ }
+
+ CPRINTS("PD:S0->S3");
+}
+DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, pd_chipset_suspend, HOOK_PRIO_DEFAULT);
+
+static void pd_chipset_startup(void)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) {
+ pd_set_dual_role_no_wakeup(i, PD_DRP_TOGGLE_OFF);
+ task_set_event(PD_PORT_TO_TASK_ID(i),
+ PD_EVENT_POWER_STATE_CHANGE |
+ PD_EVENT_UPDATE_DUAL_ROLE,
+ 0);
+ }
+
+ CPRINTS("PD:S5->S3");
+}
+DECLARE_HOOK(HOOK_CHIPSET_STARTUP, pd_chipset_startup, HOOK_PRIO_DEFAULT);
+
+static void pd_chipset_shutdown(void)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) {
+ pd_set_dual_role_no_wakeup(i, PD_DRP_FORCE_SINK);
+ task_set_event(PD_PORT_TO_TASK_ID(i),
+ PD_EVENT_POWER_STATE_CHANGE |
+ PD_EVENT_UPDATE_DUAL_ROLE,
+ 0);
+ }
+
+ CPRINTS("PD:S3->S5");
+}
+DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pd_chipset_shutdown, HOOK_PRIO_DEFAULT);
+
+
/*
* Type-C State Hierarchy (Sub-States are listed inside the boxes)
*
@@ -3010,7 +3197,7 @@ int pd_is_debug_acc(int port)
* | TC_ERROR_RECOVERY |
* |----------------------|
*
- * TC_ATTACHED_SNK TC_ATTACHED_SRC
+ * TC_ATTACHED_SNK TC_ATTACHED_SRC TC_DRP_AUTO_TOGGLE TC_LOW_POWER_MODE
*
*/
static const struct usb_state tc_states[] = {
@@ -3088,6 +3275,19 @@ static const struct usb_state tc_states[] = {
.parent = &tc_states[TC_CC_RD],
},
#endif /* CONFIG_USB_PD_TRY_SRC */
+#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
+ [TC_DRP_AUTO_TOGGLE] = {
+ .entry = tc_drp_auto_toggle_entry,
+ .run = tc_drp_auto_toggle_run,
+ },
+#endif /* CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE */
+#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
+ [TC_LOW_POWER_MODE] = {
+ .entry = tc_low_power_mode_entry,
+ .run = tc_low_power_mode_run,
+ .exit = tc_low_power_mode_exit,
+ },
+#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */
#ifdef CONFIG_USB_PE_SM
[TC_CT_UNATTACHED_SNK] = {
.entry = tc_ct_unattached_snk_entry,
diff --git a/common/usbc/usbc_task.c b/common/usbc/usbc_task.c
index 86f1bcc12d..219cf73b9a 100644
--- a/common/usbc/usbc_task.c
+++ b/common/usbc/usbc_task.c
@@ -34,11 +34,24 @@
#define USBC_EVENT_TIMEOUT (5 * MSEC)
+static uint8_t paused;
+
int tc_restart_tcpc(int port)
{
return tcpm_init(port);
}
+void tc_pause_event_loop(int port)
+{
+ paused = 1;
+}
+
+void tc_start_event_loop(int port)
+{
+ paused = 0;
+ task_set_event(PD_PORT_TO_TASK_ID(port), TASK_EVENT_WAKE, 0);
+}
+
void set_polarity(int port, int polarity)
{
tcpm_set_polarity(port, polarity);
@@ -164,7 +177,8 @@ void pd_task(void *u)
while (1) {
/* wait for next event/packet or timeout expiration */
- const uint32_t evt = task_wait_event(USBC_EVENT_TIMEOUT);
+ const uint32_t evt =
+ task_wait_event(paused ? -1 : USBC_EVENT_TIMEOUT);
/* handle events that affect the state machine as a whole */
tc_event_check(port, evt);
diff --git a/fuzz/usb_pd_fuzz.c b/fuzz/usb_pd_fuzz.c
index eeb72586b4..1d44921817 100644
--- a/fuzz/usb_pd_fuzz.c
+++ b/fuzz/usb_pd_fuzz.c
@@ -45,6 +45,11 @@ static int mock_tcpci_get_chip_info(int port, int live,
return EC_ERROR_UNIMPLEMENTED;
}
+static __maybe_unused int mock_enter_low_power_mode(int port)
+{
+ return EC_SUCCESS;
+}
+
#define MAX_TCPC_PAYLOAD 28
struct message {
@@ -130,6 +135,9 @@ static const struct tcpm_drv mock_tcpm_drv = {
.transmit = &mock_tcpm_transmit,
.tcpc_alert = &mock_tcpc_alert,
.get_chip_info = &mock_tcpci_get_chip_info,
+#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
+ .enter_low_power_mode = &mock_enter_low_power_mode,
+#endif
};
/* TCPC mux configuration */
diff --git a/include/usb_common.h b/include/usb_common.h
index 85260d539d..b35e5f27f9 100644
--- a/include/usb_common.h
+++ b/include/usb_common.h
@@ -10,6 +10,28 @@
#include "usb_pd_tcpm.h"
#include "task_id.h"
+enum pd_drp_next_states {
+ DRP_TC_DEFAULT,
+ DRP_TC_UNATTACHED_SNK,
+ DRP_TC_UNATTACHED_SRC,
+ DRP_TC_DRP_AUTO_TOGGLE
+};
+
+/**
+ * Returns the next state to transition to while in the drp auto toggle state.
+ *
+ * @param drp_sink_time timer for handling TOGGLE_OFF/FORCE_SINK mode when
+ * auto-toggle enabled. This is an in/out variable.
+ * @param power_role current power role
+ * @param drp_state dual role states
+ * @param cc1 value of CC1 set by tcpm_get_cc
+ * @param cc2 value of CC2 set by tcpm_get_cc
+ *
+ */
+enum pd_drp_next_states drp_auto_toggle_next_state(uint64_t *drp_sink_time,
+ enum pd_power_role power_role, enum pd_dual_role_states drp_state,
+ enum tcpc_cc_voltage_status cc1, enum tcpc_cc_voltage_status cc2);
+
/* Returns the battery percentage [0-100] of the system. */
int usb_get_battery_soc(void);
diff --git a/include/usb_pd.h b/include/usb_pd.h
index a73bb382af..ca6f63254b 100644
--- a/include/usb_pd.h
+++ b/include/usb_pd.h
@@ -1119,7 +1119,6 @@ enum pd_states {
/* Initial value for CC debounce variable */
#define PD_CC_UNSET -1
-#ifdef CONFIG_USB_PD_DUAL_ROLE
enum pd_dual_role_states {
/* While disconnected, toggle between src and sink */
PD_DRP_TOGGLE_ON,
@@ -1155,8 +1154,6 @@ void pd_set_dual_role(int port, enum pd_dual_role_states state);
*/
int pd_get_role(int port);
-#endif
-
/* Control Message type */
enum pd_ctrl_msg_type {
/* 0 Reserved */
diff --git a/include/usb_tc_sm.h b/include/usb_tc_sm.h
index f6971b07e3..f94360ad3f 100644
--- a/include/usb_tc_sm.h
+++ b/include/usb_tc_sm.h
@@ -335,6 +335,20 @@ void tc_start_error_recovery(int port);
*/
void tc_hard_reset(int port);
+/**
+ * Start the state machine event loop
+ *
+ * @param port USB-C port number
+ */
+void tc_start_event_loop(int port);
+
+/**
+ * Pauses the state machine event loop
+ *
+ * @param port USB-C port number
+ */
+void tc_pause_event_loop(int port);
+
#ifdef CONFIG_USB_TYPEC_CTVPD
/**
diff --git a/test/usb_typec_drp_acc_trysrc.c b/test/usb_typec_drp_acc_trysrc.c
index 07496ca600..205ef6ad3a 100644
--- a/test/usb_typec_drp_acc_trysrc.c
+++ b/test/usb_typec_drp_acc_trysrc.c
@@ -40,6 +40,7 @@ __maybe_unused static int test_mux_con_dis_as_src(void)
mock_tcpc.cc1 = TYPEC_CC_VOLT_RD;
mock_tcpc.cc2 = TYPEC_CC_VOLT_OPEN;
task_set_event(TASK_ID_PD_C0, PD_EVENT_CC, 0);
+ pd_set_dual_role(0, PD_DRP_TOGGLE_ON);
/* This wait trainsitions through AttachWait.SRC then Attached.SRC */
task_wait_event(SECOND);