summaryrefslogtreecommitdiff
path: root/common/usbc/usb_tc_drp_acc_trysrc_sm.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/usbc/usb_tc_drp_acc_trysrc_sm.c')
-rw-r--r--common/usbc/usb_tc_drp_acc_trysrc_sm.c4160
1 files changed, 0 insertions, 4160 deletions
diff --git a/common/usbc/usb_tc_drp_acc_trysrc_sm.c b/common/usbc/usb_tc_drp_acc_trysrc_sm.c
deleted file mode 100644
index 182ea686ec..0000000000
--- a/common/usbc/usb_tc_drp_acc_trysrc_sm.c
+++ /dev/null
@@ -1,4160 +0,0 @@
-/* Copyright 2019 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "charge_manager.h"
-#include "charge_state.h"
-#include "common.h"
-#include "console.h"
-#include "hooks.h"
-#include "system.h"
-#include "task.h"
-#include "tcpm/tcpm.h"
-#include "usb_common.h"
-#include "usb_mux.h"
-#include "usb_pd.h"
-#include "usb_pd_dpm.h"
-#include "usb_pd_tcpm.h"
-#include "usb_pd_timer.h"
-#include "usb_pe_sm.h"
-#include "usb_prl_sm.h"
-#include "usb_sm.h"
-#include "usb_tc_sm.h"
-#include "usbc_ocp.h"
-#include "usbc_ppc.h"
-#include "vboot.h"
-
-/*
- * USB Type-C DRP with Accessory and Try.SRC module
- * See Figure 4-16 in Release 1.4 of USB Type-C Spec.
- */
-#ifdef CONFIG_COMMON_RUNTIME
-#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
-#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args)
-#else /* CONFIG_COMMON_RUNTIME */
-#define CPRINTF(format, args...)
-#define CPRINTS(format, args...)
-#endif
-
-#define CPRINTF_LX(x, format, args...) \
- do { \
- if (tc_debug_level >= x) \
- CPRINTF(format, ## args); \
- } while (0)
-#define CPRINTF_L1(format, args...) CPRINTF_LX(1, format, ## args)
-#define CPRINTF_L2(format, args...) CPRINTF_LX(2, format, ## args)
-#define CPRINTF_L3(format, args...) CPRINTF_LX(3, format, ## args)
-
-#define CPRINTS_LX(x, format, args...) \
- do { \
- if (tc_debug_level >= x) \
- CPRINTS(format, ## args); \
- } while (0)
-#define CPRINTS_L1(format, args...) CPRINTS_LX(1, format, ## args)
-#define CPRINTS_L2(format, args...) CPRINTS_LX(2, format, ## args)
-#define CPRINTS_L3(format, args...) CPRINTS_LX(3, format, ## args)
-
-/*
- * Define DEBUG_PRINT_FLAG_AND_EVENT_NAMES to print flag names when set and
- * cleared, and event names when handled by tc_event_check().
- */
-#undef DEBUG_PRINT_FLAG_AND_EVENT_NAMES
-
-#ifdef DEBUG_PRINT_FLAG_AND_EVENT_NAMES
-void print_flag(int port, int set_or_clear, int flag);
-#define TC_SET_FLAG(port, flag) \
- do { \
- print_flag(port, 1, flag); \
- atomic_or(&tc[port].flags, (flag)); \
- } while (0)
-#define TC_CLR_FLAG(port, flag) \
- do { \
- print_flag(port, 0, flag); \
- atomic_clear_bits(&tc[port].flags, (flag)); \
- } while (0)
-#else
-#define TC_SET_FLAG(port, flag) atomic_or(&tc[port].flags, (flag))
-#define TC_CLR_FLAG(port, flag) atomic_clear_bits(&tc[port].flags, (flag))
-#endif
-#define TC_CHK_FLAG(port, flag) (tc[port].flags & (flag))
-
-/* 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 */
-#define TC_FLAGS_TS_DTS_PARTNER BIT(1)
-/* Flag to note VBus input has never been low */
-#define TC_FLAGS_VBUS_NEVER_LOW BIT(2)
-/* Flag to note Low Power Mode transition is currently happening */
-#define TC_FLAGS_LPM_TRANSITION BIT(3)
-/* Flag to note Low Power Mode is currently on */
-#define TC_FLAGS_LPM_ENGAGED BIT(4)
-/* Flag to note CVTPD has been detected */
-#define TC_FLAGS_CTVPD_DETECTED BIT(5)
-/* Flag to note request to swap to VCONN on */
-#define TC_FLAGS_REQUEST_VC_SWAP_ON BIT(6)
-/* Flag to note request to swap to VCONN off */
-#define TC_FLAGS_REQUEST_VC_SWAP_OFF BIT(7)
-/* Flag to note request to swap VCONN is being rejected */
-#define TC_FLAGS_REJECT_VCONN_SWAP BIT(8)
-/* Flag to note request to power role swap */
-#define TC_FLAGS_REQUEST_PR_SWAP BIT(9)
-/* Flag to note request to data role swap */
-#define TC_FLAGS_REQUEST_DR_SWAP BIT(10)
-/* Flag to note request to power off sink */
-#define TC_FLAGS_POWER_OFF_SNK BIT(11)
-/* Flag to note port partner is Power Delivery capable */
-#define TC_FLAGS_PARTNER_PD_CAPABLE BIT(12)
-/* Flag to note hard reset has been requested */
-#define TC_FLAGS_HARD_RESET_REQUESTED BIT(13)
-/* Flag to note we are currently performing PR Swap */
-#define TC_FLAGS_PR_SWAP_IN_PROGRESS BIT(14)
-/* Flag to note we should check for connection */
-#define TC_FLAGS_CHECK_CONNECTION BIT(15)
-/* Flag to note request from pd_set_suspend to enter TC_DISABLED state */
-#define TC_FLAGS_REQUEST_SUSPEND BIT(16)
-/* Flag to note we are in TC_DISABLED state */
-#define TC_FLAGS_SUSPENDED BIT(17)
-/* Flag to indicate the port current limit has changed */
-#define TC_FLAGS_UPDATE_CURRENT BIT(18)
-/* Flag to indicate USB mux should be updated */
-#define TC_FLAGS_UPDATE_USB_MUX BIT(19)
-/* Flag for retimer firmware update */
-#define TC_FLAGS_USB_RETIMER_FW_UPDATE_RUN BIT(20)
-#define TC_FLAGS_USB_RETIMER_FW_UPDATE_LTD_RUN BIT(21)
-/* Flag for asynchronous call to request Error Recovery */
-#define TC_FLAGS_REQUEST_ERROR_RECOVERY BIT(22)
-
-/* For checking flag_bit_names[] array */
-#define TC_FLAGS_COUNT 23
-
-/* On disconnect, clear most of the flags. */
-#define CLR_FLAGS_ON_DISCONNECT(port) TC_CLR_FLAG(port, \
- ~(TC_FLAGS_LPM_ENGAGED | TC_FLAGS_REQUEST_SUSPEND | TC_FLAGS_SUSPENDED))
-
-/*
- * 10 ms is enough time for any TCPC transaction to complete
- *
- * This value must be below ~39.7 ms to put ANX7447 into LPM due to bug in
- * silicon (see b/77544959 and b/149761477 for more details).
- */
-#define PD_LPM_DEBOUNCE_US (10 * MSEC)
-
-/*
- * This delay is not part of the USB Type-C specification or the USB port
- * controller specification. Some TCPCs require extra time before the CC_STATUS
- * register is updated when exiting low power mode.
- *
- * This delay can be possibly shortened or removed by checking VBUS state
- * before trying to re-enter LPM.
- *
- * TODO(b/162347811): TCPMv2: Wait for debounce on Vbus and CC lines
- */
-#define PD_LPM_EXIT_DEBOUNCE_US CONFIG_USB_PD_TCPC_LPM_EXIT_DEBOUNCE
-
-/*
- * The TypeC state machine uses this bit to disable/enable PD
- * This bit corresponds to bit-0 of pd_disabled_mask
- */
-#define PD_DISABLED_NO_CONNECTION BIT(0)
-/*
- * Console and Host commands use this bit to override the
- * PD_DISABLED_NO_CONNECTION bit that was set by the TypeC
- * state machine.
- * This bit corresponds to bit-1 of pd_disabled_mask
- */
-#define PD_DISABLED_BY_POLICY BIT(1)
-
-/* Unreachable time in future */
-#define TIMER_DISABLED 0xffffffffffffffff
-
-enum ps_reset_sequence {
- PS_STATE0,
- PS_STATE1,
- PS_STATE2,
-};
-
-/* List of all TypeC-level states */
-enum usb_tc_state {
- /* Super States */
- TC_CC_OPEN,
- TC_CC_RD,
- TC_CC_RP,
- /* Normal States */
- TC_DISABLED,
- TC_ERROR_RECOVERY,
- TC_UNATTACHED_SNK,
- TC_ATTACH_WAIT_SNK,
- TC_ATTACHED_SNK,
- TC_UNATTACHED_SRC,
- TC_ATTACH_WAIT_SRC,
- TC_ATTACHED_SRC,
- TC_TRY_SRC,
- TC_TRY_WAIT_SNK,
- TC_DRP_AUTO_TOGGLE,
- TC_LOW_POWER_MODE,
- TC_CT_UNATTACHED_SNK,
- TC_CT_ATTACHED_SNK,
-
- TC_STATE_COUNT,
-};
-/* Forward declare the full list of states. This is indexed by usb_tc_state */
-static const struct usb_state tc_states[];
-
-/*
- * Remove all of the states that aren't support at link time. This allows
- * IS_ENABLED to work.
- */
-#ifndef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
-GEN_NOT_SUPPORTED(TC_DRP_AUTO_TOGGLE);
-#define TC_DRP_AUTO_TOGGLE TC_DRP_AUTO_TOGGLE_NOT_SUPPORTED
-#endif /* CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE */
-
-#ifndef CONFIG_USB_PD_TCPC_LOW_POWER
-GEN_NOT_SUPPORTED(TC_LOW_POWER_MODE);
-#define TC_LOW_POWER_MODE TC_LOW_POWER_MODE_NOT_SUPPORTED
-#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */
-
-#ifndef CONFIG_USB_PE_SM
-GEN_NOT_SUPPORTED(TC_CT_UNATTACHED_SNK);
-#define TC_CT_UNATTACHED_SNK TC_CT_UNATTACHED_SNK_NOT_SUPPORTED
-GEN_NOT_SUPPORTED(TC_CT_ATTACHED_SNK);
-#define TC_CT_ATTACHED_SNK TC_CT_ATTACHED_SNK_NOT_SUPPORTED
-#endif /* CONFIG_USB_PE_SM */
-
-/*
- * If CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT is not defined then
- * _GPIO_CCD_MODE_ODL is not needed. Declare as extern so IS_ENABLED will work.
- */
-#ifndef CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT
-extern int _GPIO_CCD_MODE_ODL;
-#else
-#define _GPIO_CCD_MODE_ODL GPIO_CCD_MODE_ODL
-#endif /* CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT */
-
-/*
- * We will use DEBUG LABELS if we will be able to print (COMMON RUNTIME)
- * and either CONFIG_USB_PD_DEBUG_LEVEL is not defined (no override) or
- * we are overriding and the level is not DISABLED.
- *
- * If we can't print or the CONFIG_USB_PD_DEBUG_LEVEL is defined to be 0
- * then the DEBUG LABELS will be removed from the build.
- */
-#if defined(CONFIG_COMMON_RUNTIME) && \
- (!defined(CONFIG_USB_PD_DEBUG_LEVEL) || \
- (CONFIG_USB_PD_DEBUG_LEVEL > 0))
-#define USB_PD_DEBUG_LABELS
-#endif
-
-/*
- * Helper Macro to determine if the machine is in state
- * TC_ATTACHED_SRC
- */
-#define IS_ATTACHED_SRC(port) (get_state_tc(port) == TC_ATTACHED_SRC)
-
-/*
- * Helper Macro to determine if the machine is in state
- * TC_ATTACHED_SNK
- */
-#define IS_ATTACHED_SNK(port) (get_state_tc(port) == TC_ATTACHED_SNK)
-
-
-/* List of human readable state names for console debugging */
-__maybe_unused static __const_data const char * const tc_state_names[] = {
-#ifdef USB_PD_DEBUG_LABELS
- [TC_DISABLED] = "Disabled",
- [TC_ERROR_RECOVERY] = "ErrorRecovery",
- [TC_UNATTACHED_SNK] = "Unattached.SNK",
- [TC_ATTACH_WAIT_SNK] = "AttachWait.SNK",
- [TC_ATTACHED_SNK] = "Attached.SNK",
- [TC_UNATTACHED_SRC] = "Unattached.SRC",
- [TC_ATTACH_WAIT_SRC] = "AttachWait.SRC",
- [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
- /* Super States */
- [TC_CC_OPEN] = "SS:CC_OPEN",
- [TC_CC_RD] = "SS:CC_RD",
- [TC_CC_RP] = "SS:CC_RP",
-
- [TC_STATE_COUNT] = "",
-#endif
-};
-
-/* Debug log level - higher number == more log */
-#ifdef CONFIG_USB_PD_DEBUG_LEVEL
-static const enum debug_level tc_debug_level = CONFIG_USB_PD_DEBUG_LEVEL;
-#else
-static enum debug_level tc_debug_level = DEBUG_LEVEL_1;
-#endif
-
-#ifdef DEBUG_PRINT_FLAG_AND_EVENT_NAMES
-struct bit_name {
- int value;
- const char *name;
-};
-
-static struct bit_name flag_bit_names[] = {
- { TC_FLAGS_VCONN_ON, "VCONN_ON" },
- { TC_FLAGS_TS_DTS_PARTNER, "TS_DTS_PARTNER" },
- { TC_FLAGS_VBUS_NEVER_LOW, "VBUS_NEVER_LOW" },
- { TC_FLAGS_LPM_TRANSITION, "LPM_TRANSITION" },
- { TC_FLAGS_LPM_ENGAGED, "LPM_ENGAGED" },
- { TC_FLAGS_CTVPD_DETECTED, "CTVPD_DETECTED" },
- { TC_FLAGS_REQUEST_VC_SWAP_ON, "REQUEST_VC_SWAP_ON" },
- { TC_FLAGS_REQUEST_VC_SWAP_OFF, "REQUEST_VC_SWAP_OFF" },
- { TC_FLAGS_REJECT_VCONN_SWAP, "REJECT_VCONN_SWAP" },
- { TC_FLAGS_REQUEST_PR_SWAP, "REQUEST_PR_SWAP" },
- { TC_FLAGS_REQUEST_DR_SWAP, "REQUEST_DR_SWAP" },
- { TC_FLAGS_POWER_OFF_SNK, "POWER_OFF_SNK" },
- { TC_FLAGS_PARTNER_PD_CAPABLE, "PARTNER_PD_CAPABLE" },
- { TC_FLAGS_HARD_RESET_REQUESTED, "HARD_RESET_REQUESTED" },
- { TC_FLAGS_PR_SWAP_IN_PROGRESS, "PR_SWAP_IN_PROGRESS" },
- { TC_FLAGS_CHECK_CONNECTION, "CHECK_CONNECTION" },
- { TC_FLAGS_REQUEST_SUSPEND, "REQUEST_SUSPEND" },
- { TC_FLAGS_SUSPENDED, "SUSPENDED" },
- { TC_FLAGS_UPDATE_CURRENT, "UPDATE_CURRENT" },
- { TC_FLAGS_UPDATE_USB_MUX, "UPDATE_USB_MUX" },
- { TC_FLAGS_USB_RETIMER_FW_UPDATE_RUN,
- "USB_RETIMER_FW_UPDATE_RUN" },
- { TC_FLAGS_USB_RETIMER_FW_UPDATE_LTD_RUN,
- "USB_RETIMER_FW_UPDATE_LTD_RUN" },
- { TC_FLAGS_REQUEST_ERROR_RECOVERY, "REQUEST_ERROR_RECOCVERY"},
-};
-BUILD_ASSERT(ARRAY_SIZE(flag_bit_names) == TC_FLAGS_COUNT);
-
-static struct bit_name event_bit_names[] = {
- { TASK_EVENT_SYSJUMP_READY, "SYSJUMP_READY" },
- { TASK_EVENT_IPC_READY, "IPC_READY" },
- { TASK_EVENT_PD_AWAKE, "PD_AWAKE" },
- { TASK_EVENT_PECI_DONE, "PECI_DONE" },
- { TASK_EVENT_I2C_IDLE, "I2C_IDLE" },
-#ifdef TASK_EVENT_PS2_DONE
- { TASK_EVENT_PS2_DONE, "PS2_DONE" },
-#endif
- { TASK_EVENT_DMA_TC, "DMA_TC" },
- { TASK_EVENT_ADC_DONE, "ADC_DONE" },
- { TASK_EVENT_RESET_DONE, "RESET_DONE" },
- { TASK_EVENT_WAKE, "WAKE" },
- { TASK_EVENT_MUTEX, "MUTEX" },
- { TASK_EVENT_TIMER, "TIMER" },
- { PD_EVENT_TX, "TX" },
- { PD_EVENT_CC, "CC" },
- { PD_EVENT_TCPC_RESET, "TCPC_RESET" },
- { PD_EVENT_UPDATE_DUAL_ROLE, "UPDATE_DUAL_ROLE" },
- { PD_EVENT_DEVICE_ACCESSED, "DEVICE_ACCESSED" },
- { PD_EVENT_POWER_STATE_CHANGE, "POWER_STATE_CHANGE" },
- { PD_EVENT_SEND_HARD_RESET, "SEND_HARD_RESET" },
- { PD_EVENT_SYSJUMP, "SYSJUMP" },
-};
-
-static void print_bits(int port, const char *desc, int value,
- struct bit_name *names, int names_size)
-{
- int i;
-
- CPRINTF("C%d: %s 0x%x : ", port, desc, value);
- for (i = 0; i < names_size; i++) {
- if (value & names[i].value)
- CPRINTF("%s | ", names[i].name);
- value &= ~names[i].value;
- }
- if (value != 0)
- CPRINTF("0x%x", value);
- CPRINTF("\n");
-}
-
-void print_flag(int port, int set_or_clear, int flag)
-{
- print_bits(port, set_or_clear ? "Set" : "Clr", flag, flag_bit_names,
- ARRAY_SIZE(flag_bit_names));
-}
-#endif /* DEBUG_PRINT_FLAG_AND_EVENT_NAMES */
-
-#ifndef CONFIG_USB_PD_TRY_SRC
-extern int TC_TRY_SRC_UNDEFINED;
-extern int TC_TRY_WAIT_SNK_UNDEFINED;
-#define TC_TRY_SRC TC_TRY_SRC_UNDEFINED
-#define TC_TRY_WAIT_SNK TC_TRY_WAIT_SNK_UNDEFINED
-#endif
-
-static struct type_c {
- /* state machine context */
- struct sm_ctx ctx;
- /* current port power role (SOURCE or SINK) */
- enum pd_power_role power_role;
- /* current port data role (DFP or UFP) */
- enum pd_data_role data_role;
- /*
- * Higher-level power deliver state machines are enabled if false,
- * else they're disabled if bits PD_DISABLED_NO_CONNECTION or
- * PD_DISABLED_BY_POLICY are set.
- */
- uint32_t pd_disabled_mask;
- /*
- * 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;
-#endif
- /* Port polarity */
- enum tcpc_cc_polarity polarity;
- /* port flags, see TC_FLAGS_* */
- uint32_t flags;
- /* The cc state */
- enum pd_cc_states cc_state;
- /* 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;
- /* Voltage on CC pin */
- enum tcpc_cc_voltage_status cc_voltage;
- /* Type-C current */
- typec_current_t typec_curr;
- /* Type-C current change */
- typec_current_t typec_curr_change;
-
- /* Selected TCPC CC/Rp values */
- enum tcpc_cc_pull select_cc_pull;
- enum tcpc_rp_value select_current_limit_rp;
- enum tcpc_rp_value select_collision_rp;
-} 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};
-
-static void set_vconn(int port, int enable);
-
-/* Forward declare common, private functions */
-static __maybe_unused int reset_device_and_notify(int port);
-static __maybe_unused void check_drp_connection(const int port);
-static void sink_power_sub_states(int port);
-static void set_ccd_mode(int port, bool enable);
-
-__maybe_unused static void handle_new_power_state(int port);
-
-static void pd_update_dual_role_config(int port);
-
-/* Forward declare common, private functions */
-static void set_state_tc(const int port, const enum usb_tc_state new_state);
-test_export_static enum usb_tc_state get_state_tc(const int port);
-
-/* Enable variable for Try.SRC states */
-static uint32_t pd_try_src;
-static volatile enum try_src_override_t pd_try_src_override;
-static void pd_update_try_source(void);
-
-static void sink_stop_drawing_current(int port);
-
-__maybe_unused static bool is_try_src_enabled(int port)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_TRY_SRC))
- assert(0);
-
- return ((pd_try_src_override == TRY_SRC_OVERRIDE_ON) ||
- (pd_try_src_override == TRY_SRC_NO_OVERRIDE && pd_try_src));
-}
-
-/*
- * Public Functions
- *
- * NOTE: Functions prefixed with pd_ are defined in usb_pd.h
- * Functions prefixed with tc_ are defined int usb_tc_sm.h
- */
-
-#ifndef CONFIG_USB_PRL_SM
-
-/*
- * These pd_ functions are implemented in common/usb_prl_sm.c
- */
-
-void pd_transmit_complete(int port, int status)
-{
- /* DO NOTHING */
-}
-
-void pd_execute_hard_reset(int port)
-{
- /* DO NOTHING */
-}
-
-__overridable void pd_set_vbus_discharge(int port, int enable)
-{
- /* DO NOTHING */
-}
-
-#endif /* !CONFIG_USB_PRL_SM */
-
-#ifndef CONFIG_USB_PE_SM
-
-/*
- * These pd_ functions are implemented in the PE layer
- */
-const uint32_t * const pd_get_src_caps(int port)
-{
- return NULL;
-}
-
-uint8_t pd_get_src_cap_cnt(int port)
-{
- return 0;
-}
-
-const uint32_t * const pd_get_snk_caps(int port)
-{
- return NULL;
-}
-
-uint8_t pd_get_snk_cap_cnt(int port)
-{
- return 0;
-}
-
-void pd_set_src_caps(int port, int cnt, uint32_t *src_caps)
-{
-}
-
-int pd_get_rev(int port, enum tcpci_msg_type type)
-{
- return PD_REV30;
-}
-
-#endif /* !CONFIG_USB_PR_SM */
-
-#ifndef HAS_TASK_CHIPSET
-__overridable enum pd_dual_role_states board_tc_get_initial_drp_mode(int port)
-{
- /*
- * DRP state is typically adjusted as the chipset state is changed. For
- * projects which don't include an AP this function can be used for to
- * specify what the starting DRP state should be.
- */
- return PD_DRP_FORCE_SINK;
-}
-#endif
-
-void pd_update_contract(int port)
-{
- if (IS_ENABLED(CONFIG_USB_PE_SM)) {
- if (IS_ATTACHED_SRC(port))
- pd_dpm_request(port, DPM_REQUEST_SRC_CAP_CHANGE);
- }
-}
-
-void pd_request_source_voltage(int port, int mv)
-{
- if (IS_ENABLED(CONFIG_USB_PE_SM)) {
- pd_set_max_voltage(mv);
-
- if (IS_ATTACHED_SNK(port))
- pd_dpm_request(port, DPM_REQUEST_NEW_POWER_LEVEL);
- else
- pd_dpm_request(port, DPM_REQUEST_PR_SWAP);
-
- task_wake(PD_PORT_TO_TASK_ID(port));
- }
-}
-
-void pd_set_external_voltage_limit(int port, int mv)
-{
- if (IS_ENABLED(CONFIG_USB_PE_SM)) {
- pd_set_max_voltage(mv);
-
- /* Must be in Attached.SNK when this function is called */
- if (get_state_tc(port) == TC_ATTACHED_SNK)
- pd_dpm_request(port, DPM_REQUEST_NEW_POWER_LEVEL);
-
- task_wake(PD_PORT_TO_TASK_ID(port));
- }
-}
-
-void pd_set_new_power_request(int port)
-{
- if (IS_ENABLED(CONFIG_USB_PE_SM)) {
- /* Must be in Attached.SNK when this function is called */
- if (get_state_tc(port) == TC_ATTACHED_SNK)
- pd_dpm_request(port, DPM_REQUEST_NEW_POWER_LEVEL);
- }
-}
-
-void tc_request_power_swap(int port)
-{
- if (IS_ENABLED(CONFIG_USB_PE_SM)) {
- /*
- * Must be in Attached.SRC or Attached.SNK
- */
- if (IS_ATTACHED_SRC(port) || IS_ATTACHED_SNK(port)) {
- TC_SET_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS);
-
- /* Let tc_pr_swap_complete start the Vbus debounce */
- pd_timer_disable(port, TC_TIMER_VBUS_DEBOUNCE);
- }
-
- /*
- * TCPCI Rev2 V1.1 4.4.5.4.4
- * Disconnect Detection by the Sink TCPC during a Connection
- *
- * Upon reception of or prior to transmitting a PR_Swap
- * message, the TCPM acting as a Sink shall disable the Sink
- * disconnect detection to retain PD message delivery when
- * Power Role Swap happens. Disable AutoDischargeDisconnect.
- */
- if (IS_ATTACHED_SNK(port))
- tcpm_enable_auto_discharge_disconnect(port, 0);
- }
-}
-
-/* Flag to indicate PD comm is disabled on init */
-static int pd_disabled_on_init;
-
-static void pd_update_pd_comm(void)
-{
- int i;
-
- /*
- * Some batteries take much longer time to report its SOC.
- * The init function disabled PD comm on startup. Need this
- * hook to enable PD comm when the battery level is enough.
- */
- if (pd_disabled_on_init && pd_is_battery_capable()) {
- for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++)
- pd_comm_enable(i, 1);
- pd_disabled_on_init = 0;
- }
-}
-DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, pd_update_pd_comm, HOOK_PRIO_DEFAULT);
-
-static bool pd_comm_allowed_by_policy(void)
-{
- if (system_is_in_rw())
- return true;
-
- if (vboot_allow_usb_pd())
- return true;
-
- /*
- * If enable PD in RO on a non-EFS2 device, a hard reset will be issued
- * when sysjump to RW that makes the device brownout on the dead-battery
- * case. Disable PD for this special case as a workaround.
- */
- if (!system_is_locked()) {
- if (IS_ENABLED(CONFIG_VBOOT_EFS2))
- return true;
-
- if (pd_is_battery_capable())
- return true;
-
- pd_disabled_on_init = 1;
- }
-
- return false;
-}
-
-static void tc_policy_pd_enable(int port, int en)
-{
- if (en)
- atomic_clear_bits(&tc[port].pd_disabled_mask,
- PD_DISABLED_BY_POLICY);
- else
- atomic_or(&tc[port].pd_disabled_mask, PD_DISABLED_BY_POLICY);
-
- CPRINTS("C%d: PD comm policy %sabled", port, en ? "en" : "dis");
-}
-
-static void tc_enable_pd(int port, int en)
-{
- if (en)
- atomic_clear_bits(&tc[port].pd_disabled_mask,
- PD_DISABLED_NO_CONNECTION);
- else
- atomic_or(&tc[port].pd_disabled_mask,
- PD_DISABLED_NO_CONNECTION);
-}
-
-__maybe_unused static void tc_enable_try_src(int en)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_TRY_SRC))
- assert(0);
-
- if (en)
- atomic_or(&pd_try_src, 1);
- else
- atomic_clear_bits(&pd_try_src, 1);
-}
-
-/*
- * Exit all modes due to a detach event
- * Note: this skips the ExitMode VDM steps in the PE because it is assumed the
- * partner is not present to receive them, and the PE will no longer be running.
- */
-static void tc_set_modes_exit(int port)
-{
- if (IS_ENABLED(CONFIG_USB_PE_SM) &&
- IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) {
- pd_dfp_exit_mode(port, TCPCI_MSG_SOP, 0, 0);
- pd_dfp_exit_mode(port, TCPCI_MSG_SOP_PRIME, 0, 0);
- pd_dfp_exit_mode(port, TCPCI_MSG_SOP_PRIME_PRIME, 0, 0);
- }
-}
-
-static void tc_detached(int port)
-{
- TC_CLR_FLAG(port, TC_FLAGS_TS_DTS_PARTNER);
- hook_notify(HOOK_USB_PD_DISCONNECT);
- tc_pd_connection(port, 0);
- tcpm_debug_accessory(port, 0);
- set_ccd_mode(port, 0);
- tc_set_modes_exit(port);
- if (IS_ENABLED(CONFIG_USB_PRL_SM))
- prl_set_default_pd_revision(port);
-
- /* Clear any mux connection on detach */
- if (IS_ENABLED(CONFIG_USBC_SS_MUX))
- usb_mux_set(port, USB_PD_MUX_NONE,
- USB_SWITCH_DISCONNECT, tc[port].polarity);
-}
-
-static inline void pd_set_dual_role_and_event(int port,
- enum pd_dual_role_states state, uint32_t event)
-{
- drp_state[port] = state;
-
- if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC))
- pd_update_try_source();
-
- if (event != 0)
- task_set_event(PD_PORT_TO_TASK_ID(port), event);
-}
-
-void pd_set_dual_role(int port, enum pd_dual_role_states state)
-{
- pd_set_dual_role_and_event(port, state, PD_EVENT_UPDATE_DUAL_ROLE);
-}
-
-int pd_comm_is_enabled(int port)
-{
- return tc_get_pd_enabled(port);
-}
-
-void pd_request_data_swap(int port)
-{
- /*
- * Must be in Attached.SRC, Attached.SNK, DebugAccessory.SNK,
- * or UnorientedDebugAccessory.SRC when this function
- * is called
- */
- if (IS_ATTACHED_SRC(port) || IS_ATTACHED_SNK(port)) {
- TC_SET_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP);
- task_wake(PD_PORT_TO_TASK_ID(port));
- }
-}
-
-/* Return true if partner port is known to be PD capable. */
-bool pd_capable(int port)
-{
- return !!TC_CHK_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE);
-}
-
-/*
- * Return true if we transition through Unattached.SNK, but we're still waiting
- * to receive source caps from the partner. This indicates that the PD
- * capabilities are not yet known.
- */
-bool pd_waiting_on_partner_src_caps(int port)
-{
- return !pd_get_src_cap_cnt(port);
-}
-
-enum pd_dual_role_states pd_get_dual_role(int port)
-{
- return drp_state[port];
-}
-
-#ifdef CONFIG_CMD_PD_DEV_DUMP_INFO
-static inline void pd_dev_dump_info(uint16_t dev_id, uint32_t *hash)
-{
- int j;
-
- ccprintf("DevId:%d.%d Hash:", HW_DEV_ID_MAJ(dev_id),
- HW_DEV_ID_MIN(dev_id));
- for (j = 0; j < PD_RW_HASH_SIZE / 4; j++)
- ccprintf(" %08x ", hash[j]);
- ccprintf("\n");
-}
-#endif /* CONFIG_CMD_PD_DEV_DUMP_INFO */
-
-const char *tc_get_current_state(int port)
-{
- if (IS_ENABLED(USB_PD_DEBUG_LABELS))
- return tc_state_names[get_state_tc(port)];
- else
- return "";
-}
-
-uint32_t tc_get_flags(int port)
-{
- return tc[port].flags;
-}
-
-int tc_is_attached_src(int port)
-{
- return IS_ATTACHED_SRC(port);
-}
-
-int tc_is_attached_snk(int port)
-{
- return IS_ATTACHED_SNK(port);
-}
-
-void tc_pd_connection(int port, int en)
-{
- if (en) {
- bool new_pd_capable = false;
-
- if (!TC_CHK_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE))
- new_pd_capable = true;
-
- TC_SET_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE);
- /* If a PD device is attached then disable deep sleep */
- if (IS_ENABLED(CONFIG_LOW_POWER_IDLE) &&
- !IS_ENABLED(CONFIG_USB_PD_TCPC_ON_CHIP)) {
- disable_sleep(SLEEP_MASK_USB_PD);
- }
-
- /*
- * Update the mux state, only when the PD capable flag
- * transitions from 0 to 1. This ensures that PD charger
- * devices, without data capability are not marked as having
- * USB.
- */
- if (new_pd_capable)
- set_usb_mux_with_current_data_role(port);
- } else {
- TC_CLR_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE);
- /* If a PD device isn't attached then enable deep sleep */
- if (IS_ENABLED(CONFIG_LOW_POWER_IDLE) &&
- !IS_ENABLED(CONFIG_USB_PD_TCPC_ON_CHIP)) {
- int i;
-
- /* If all ports are not connected, allow the sleep */
- for (i = 0; i < board_get_usb_pd_port_count(); i++) {
- if (pd_capable(i))
- break;
- }
- if (i == board_get_usb_pd_port_count())
- enable_sleep(SLEEP_MASK_USB_PD);
- }
- }
-}
-
-void tc_ctvpd_detected(int port)
-{
- TC_SET_FLAG(port, TC_FLAGS_CTVPD_DETECTED);
-}
-
-void pd_try_vconn_src(int port)
-{
- set_vconn(port, 1);
-}
-
-int tc_check_vconn_swap(int port)
-{
- if (IS_ENABLED(CONFIG_USBC_VCONN)) {
- if (TC_CHK_FLAG(port, TC_FLAGS_REJECT_VCONN_SWAP))
- return 0;
-
- return pd_check_vconn_swap(port);
- } else
- return 0;
-}
-
-void tc_pr_swap_complete(int port, bool success)
-{
- if (IS_ATTACHED_SNK(port)) {
- /*
- * Give the ADCs in the TCPC or PPC time to react following
- * a PS_RDY message received during a SRC to SNK swap.
- * Note: This is empirically determined, not strictly
- * part of the USB PD spec.
- * Note: Swap in progress should not be cleared until the
- * debounce is completed.
- */
- pd_timer_enable(port, TC_TIMER_VBUS_DEBOUNCE, PD_T_DEBOUNCE);
- } else {
- /* PR Swap is no longer in progress */
- TC_CLR_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS);
-
- /*
- * AutoDischargeDisconnect was turned off near the SNK->SRC
- * PR-Swap message. If the swap was a success, Vbus should be
- * valid, so re-enable AutoDischargeDisconnect
- */
- if (success)
- tcpm_enable_auto_discharge_disconnect(port, 1);
- }
-}
-
-void tc_prs_src_snk_assert_rd(int port)
-{
- /*
- * Must be in Attached.SRC or UnorientedDebugAccessory.SRC
- * when this function is called
- */
- if (IS_ATTACHED_SRC(port)) {
- /*
- * Transition to Attached.SNK to
- * DebugAccessory.SNK assert Rd
- */
- TC_SET_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP);
- task_wake(PD_PORT_TO_TASK_ID(port));
- }
-}
-
-void tc_prs_snk_src_assert_rp(int port)
-{
- /*
- * Must be in Attached.SNK or DebugAccessory.SNK
- * when this function is called
- */
- if (IS_ATTACHED_SNK(port)) {
- /*
- * Transition to Attached.SRC or
- * UnorientedDebugAccessory.SRC to assert Rp
- */
- TC_SET_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP);
- task_wake(PD_PORT_TO_TASK_ID(port));
- }
-}
-
-/*
- * Hard Reset is being requested. This should not allow a TC connection
- * to go to an unattached state until the connection is recovered from
- * the hard reset. It is possible for a Hard Reset to cause a timeout
- * in trying to recover and an additional Hard Reset would be issued.
- * During this entire process it is important that the TC is not allowed
- * to go to an unattached state.
- *
- * Type-C Spec Rev 2.0 section 4.5.2.2.5.2
- * Exiting from Attached.SNK State
- * A port that is not a V CONN-Powered USB Device and is not in the
- * process of a USB PD PR_Swap or a USB PD Hard Reset or a USB PD
- * FR_Swap shall transition to Unattached.SNK
- */
-void tc_hard_reset_request(int port)
-{
- TC_SET_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED);
- task_wake(PD_PORT_TO_TASK_ID(port));
-}
-
-void tc_try_src_override(enum try_src_override_t ov)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_TRY_SRC))
- assert(0);
-
- if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) {
- switch (ov) {
- case TRY_SRC_OVERRIDE_OFF: /* 0 */
- pd_try_src_override = TRY_SRC_OVERRIDE_OFF;
- break;
- case TRY_SRC_OVERRIDE_ON: /* 1 */
- pd_try_src_override = TRY_SRC_OVERRIDE_ON;
- break;
- default:
- pd_try_src_override = TRY_SRC_NO_OVERRIDE;
- }
- }
-}
-
-enum try_src_override_t tc_get_try_src_override(void)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_TRY_SRC))
- assert(0);
-
- return pd_try_src_override;
-}
-
-void tc_snk_power_off(int port)
-{
- if (IS_ATTACHED_SNK(port)) {
- TC_SET_FLAG(port, TC_FLAGS_POWER_OFF_SNK);
- sink_stop_drawing_current(port);
- }
-}
-
-int tc_src_power_on(int port)
-{
- /*
- * Check our OC event counter. If we've exceeded our threshold, then
- * let's latch our source path off to prevent continuous cycling. When
- * the PD state machine detects a disconnection on the CC lines, we will
- * reset our OC event counter.
- */
- if (IS_ENABLED(CONFIG_USBC_OCP) && usbc_ocp_is_port_latched_off(port))
- return EC_ERROR_ACCESS_DENIED;
-
- if (IS_ATTACHED_SRC(port))
- return pd_set_power_supply_ready(port);
-
- return 0;
-}
-
-void tc_src_power_off(int port)
-{
- /* Remove VBUS */
- pd_power_supply_reset(port);
-
- if (IS_ENABLED(CONFIG_CHARGE_MANAGER))
- charge_manager_set_ceil(port, CEIL_REQUESTOR_PD,
- CHARGE_CEIL_NONE);
-}
-
-/* Set what role the partner is right now, for the PPC and OCP module */
-static void tc_set_partner_role(int port, enum ppc_device_role role)
-{
- if (IS_ENABLED(CONFIG_USBC_PPC))
- ppc_dev_is_connected(port, role);
-
- if (IS_ENABLED(CONFIG_USBC_OCP)) {
- usbc_ocp_snk_is_connected(port, role == PPC_DEV_SNK);
- /*
- * Clear the overcurrent event counter
- * since we've detected a disconnect.
- */
- if (role == PPC_DEV_DISCONNECTED)
- usbc_ocp_clear_event_counter(port);
- }
-}
-
-/*
- * Depending on the load on the processor and the tasks running
- * it can take a while for the task associated with this port
- * to run. So build in 1ms delays, for up to 300ms, to wait for
- * the suspend to actually happen.
- */
-#define SUSPEND_SLEEP_DELAY 1
-#define SUSPEND_SLEEP_RETRIES 300
-
-void pd_set_suspend(int port, int suspend)
-{
- if (pd_is_port_enabled(port) == !suspend)
- return;
-
- /* Track if we are suspended or not */
- if (suspend) {
- int wait = 0;
-
- TC_SET_FLAG(port, TC_FLAGS_REQUEST_SUSPEND);
-
- /*
- * Avoid deadlock when running from task
- * which we are going to suspend
- */
- if (PD_PORT_TO_TASK_ID(port) == task_get_current())
- return;
-
- task_wake(PD_PORT_TO_TASK_ID(port));
-
- /* Sleep this task if we are not suspended */
- while (pd_is_port_enabled(port)) {
- if (++wait > SUSPEND_SLEEP_RETRIES) {
- CPRINTS("C%d: NOT SUSPENDED after %dms",
- port, wait * SUSPEND_SLEEP_DELAY);
- return;
- }
- msleep(SUSPEND_SLEEP_DELAY);
- }
- } else {
- TC_CLR_FLAG(port, TC_FLAGS_REQUEST_SUSPEND);
- task_wake(PD_PORT_TO_TASK_ID(port));
- }
-}
-
-void pd_set_error_recovery(int port)
-{
- TC_SET_FLAG(port, TC_FLAGS_REQUEST_ERROR_RECOVERY);
-}
-
-int pd_is_port_enabled(int port)
-{
- /*
- * Checking get_state_tc(port) from another task isn't safe since it
- * can return TC_DISABLED before tc_cc_open_entry and tc_disabled_entry
- * are complete. So check TC_FLAGS_SUSPENDED instead.
- */
- return !TC_CHK_FLAG(port, TC_FLAGS_SUSPENDED);
-}
-
-int pd_fetch_acc_log_entry(int port)
-{
- if (IS_ENABLED(CONFIG_USB_PE_SM))
- pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_GET_LOG, NULL, 0);
-
- return EC_RES_SUCCESS;
-}
-
-enum tcpc_cc_polarity pd_get_polarity(int port)
-{
- return tc[port].polarity;
-}
-
-enum pd_data_role pd_get_data_role(int port)
-{
- return tc[port].data_role;
-}
-
-enum pd_power_role pd_get_power_role(int port)
-{
- return tc[port].power_role;
-}
-
-enum pd_cc_states pd_get_task_cc_state(int port)
-{
- return tc[port].cc_state;
-}
-
-uint8_t pd_get_task_state(int port)
-{
- return get_state_tc(port);
-}
-
-bool pd_get_vconn_state(int port)
-{
- return !!TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON);
-}
-
-const char *pd_get_task_state_name(int port)
-{
- return tc_get_current_state(port);
-}
-
-void pd_vbus_low(int port)
-{
- TC_CLR_FLAG(port, TC_FLAGS_VBUS_NEVER_LOW);
-}
-
-int pd_is_connected(int port)
-{
- return (IS_ATTACHED_SRC(port) ||
- (IS_ENABLED(CONFIG_USB_PE_SM) &&
- ((get_state_tc(port) == TC_CT_UNATTACHED_SNK) ||
- (get_state_tc(port) == TC_CT_ATTACHED_SNK))) ||
- IS_ATTACHED_SNK(port));
-}
-
-bool pd_is_disconnected(int port)
-{
- return !pd_is_connected(port);
-}
-
-/*
- * PD functions which query our fixed PDO flags. Both the source and sink
- * capabilities can present these values, and they should match between the two
- * for compliant partners.
- */
-static bool pd_check_fixed_flag(int port, uint32_t flag)
-{
- uint32_t fixed_pdo;
-
- if (pd_get_src_cap_cnt(port) != 0)
- fixed_pdo = *pd_get_src_caps(port);
- else if (pd_get_snk_cap_cnt(port) != 0)
- fixed_pdo = *pd_get_snk_caps(port);
- else
- return false;
-
- /*
- * Error check that first PDO is fixed, as 6.4.1 Capabilities requires
- * in the Power Delivery Specification.
- * "The vSafe5V Fixed Supply Object Shall always be the first object"
- */
- if ((fixed_pdo & PDO_TYPE_MASK) != PDO_TYPE_FIXED)
- return false;
-
- return fixed_pdo & flag;
-}
-
-bool pd_get_partner_data_swap_capable(int port)
-{
- return pd_check_fixed_flag(port, PDO_FIXED_DATA_SWAP);
-}
-
-bool pd_get_partner_usb_comm_capable(int port)
-{
- return pd_check_fixed_flag(port, PDO_FIXED_COMM_CAP);
-}
-
-bool pd_get_partner_dual_role_power(int port)
-{
- return pd_check_fixed_flag(port, PDO_FIXED_DUAL_ROLE);
-}
-
-bool pd_get_partner_unconstr_power(int port)
-{
- return pd_check_fixed_flag(port, PDO_FIXED_UNCONSTRAINED);
-}
-
-static void bc12_role_change_handler(int port, enum pd_data_role prev_data_role,
- enum pd_data_role data_role)
-{
- int event = 0;
- int task_id = USB_CHG_PORT_TO_TASK_ID(port);
- bool role_changed = (data_role != prev_data_role);
-
- if (!IS_ENABLED(CONFIG_BC12_DETECT_DATA_ROLE_TRIGGER))
- return;
-
- /* Get the data role of our device */
- switch (data_role) {
- case PD_ROLE_UFP:
- /* Only trigger BC12 detection on a role change */
- if (role_changed)
- event = USB_CHG_EVENT_DR_UFP;
- break;
- case PD_ROLE_DFP:
- /* Only trigger BC12 host mode on a role change */
- if (role_changed)
- event = USB_CHG_EVENT_DR_DFP;
- break;
- case PD_ROLE_DISCONNECTED:
- event = USB_CHG_EVENT_CC_OPEN;
- break;
- default:
- return;
- }
-
- if (event)
- task_set_event(task_id, event);
-}
-
-/*
- * TCPC CC/Rp management
- */
-static void typec_select_pull(int port, enum tcpc_cc_pull pull)
-{
- tc[port].select_cc_pull = pull;
-}
-void typec_select_src_current_limit_rp(int port, enum tcpc_rp_value rp)
-{
- tc[port].select_current_limit_rp = rp;
- if (IS_ATTACHED_SRC(port))
- TC_SET_FLAG(port, TC_FLAGS_UPDATE_CURRENT);
-}
-__overridable int typec_get_default_current_limit_rp(int port)
-{
- return CONFIG_USB_PD_PULLUP;
-}
-void typec_select_src_collision_rp(int port, enum tcpc_rp_value rp)
-{
- tc[port].select_collision_rp = rp;
-}
-static enum tcpc_rp_value typec_get_active_select_rp(int port)
-{
- /* Explicit contract will use the collision Rp */
- if (IS_ENABLED(CONFIG_USB_PD_REV30) &&
- pe_is_explicit_contract(port))
- return tc[port].select_collision_rp;
- return tc[port].select_current_limit_rp;
-}
-int typec_update_cc(int port)
-{
- int rv;
- enum tcpc_cc_pull pull = tc[port].select_cc_pull;
- enum tcpc_rp_value rp = typec_get_active_select_rp(port);
-
- rv = tcpm_select_rp_value(port, rp);
- if (rv)
- return rv;
-
- return tcpm_set_cc(port, pull);
-}
-
-#ifdef CONFIG_USB_PE_SM
-/*
- * This function performs a source hard reset. It should be called
- * repeatedly until a true value is returned, signaling that the
- * source hard reset is complete. A false value is returned otherwise.
- */
-static bool tc_perform_src_hard_reset(int port)
-{
- switch (tc[port].ps_reset_state) {
- case PS_STATE0:
- /* Remove VBUS */
- tc_src_power_off(port);
-
- /* Turn off VCONN */
- set_vconn(port, 0);
-
- /* Set role to DFP */
- tc_set_data_role(port, PD_ROLE_DFP);
-
- tc[port].ps_reset_state = PS_STATE1;
- pd_timer_enable(port, TC_TIMER_TIMEOUT, PD_T_SRC_RECOVER);
- return false;
- case PS_STATE1:
- /* Enable VBUS */
- tc_src_power_on(port);
-
- /* Update the Rp Value */
- typec_update_cc(port);
-
- /* Turn off VCONN */
- set_vconn(port, 1);
-
- tc[port].ps_reset_state = PS_STATE2;
- pd_timer_enable(port, TC_TIMER_TIMEOUT,
- PD_POWER_SUPPLY_TURN_ON_DELAY);
- return false;
- case PS_STATE2:
- /* Tell Policy Engine Hard Reset is complete */
- pe_ps_reset_complete(port);
-
- tc[port].ps_reset_state = PS_STATE0;
- return true;
- }
-
- /*
- * This return is added to appease the compiler. It should
- * never be reached because the switch handles all possible
- * cases of the enum ps_reset_sequence type.
- */
- return true;
-}
-
-/*
- * Wait for recovery after a hard reset. Call repeatedly until true is
- * returned, signaling that the hard reset is complete.
- */
-static bool tc_perform_snk_hard_reset(int port)
-{
- switch (tc[port].ps_reset_state) {
- case PS_STATE0:
- /* Hard reset sets us back to default data role */
- tc_set_data_role(port, PD_ROLE_UFP);
-
- /*
- * When VCONN is supported, the Hard Reset Shall cause
- * the Port with the Rd resistor asserted to turn off
- * VCONN.
- */
- if (IS_ENABLED(CONFIG_USBC_VCONN) &&
- TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON))
- set_vconn(port, 0);
-
- /* Wait up to tVSafe0V for Vbus to disappear */
- tc[port].ps_reset_state = PS_STATE1;
- pd_timer_enable(port, TC_TIMER_TIMEOUT, PD_T_SAFE_0V);
- return false;
- case PS_STATE1:
- if (pd_check_vbus_level(port, VBUS_SAFE0V)) {
- /*
- * Partner dropped Vbus, reduce our current consumption
- * and await its return.
- */
- sink_stop_drawing_current(port);
-
- tcpm_enable_auto_discharge_disconnect(port, 0);
-
- /* Move on to waiting for the return of Vbus */
- tc[port].ps_reset_state = PS_STATE2;
- pd_timer_enable(port, TC_TIMER_TIMEOUT,
- PD_T_SRC_RECOVER_MAX +
- PD_T_SRC_TURN_ON);
- }
-
- if (pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) {
- /*
- * No Vbus drop likely indicates a non-PD port partner,
- * move to the next stage anyway.
- */
- tc[port].ps_reset_state = PS_STATE2;
- pd_timer_enable(port, TC_TIMER_TIMEOUT,
- PD_T_SRC_RECOVER_MAX +
- PD_T_SRC_TURN_ON);
- }
- return false;
- case PS_STATE2:
- /*
- * Look for the voltage to be above disconnect. Since we didn't
- * drop our draw on non-PD partners, they may have dipped below
- * vSafe5V but still be in a valid connected voltage.
- */
- if (!pd_check_vbus_level(port, VBUS_REMOVED)) {
- /*
- * Inform policy engine that power supply
- * reset is complete
- */
- tc[port].ps_reset_state = PS_STATE0;
- pe_ps_reset_complete(port);
-
- /*
- * Now that VBUS is back, let's notify charge manager
- * regarding the source's current capabilities.
- * sink_power_sub_states() reacts to changes in CC
- * terminations, however during a HardReset, the
- * terminations of a non-PD port partner will not
- * change. Therefore, set the debounce time to right
- * now, such that we'll actually reset the correct input
- * current limit.
- */
- pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, 0);
- sink_power_sub_states(port);
-
- /* Power is back, Enable AutoDischargeDisconnect */
- tcpm_enable_auto_discharge_disconnect(port, 1);
- return true;
- }
- /*
- * If Vbus isn't back after wait + tSrcTurnOn, go unattached
- */
- if (pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) {
- tc[port].ps_reset_state = PS_STATE0;
- set_state_tc(port, TC_UNATTACHED_SNK);
- return true;
- }
- }
-
- return false;
-}
-#endif /* CONFIG_USB_PE_SM */
-
-void tc_start_error_recovery(int port)
-{
- assert(port == TASK_ID_TO_PD_PORT(task_get_current()));
-
- /*
- * The port should transition to the ErrorRecovery state
- * from any other state when directed.
- */
- set_state_tc(port, TC_ERROR_RECOVERY);
-}
-
-static void restart_tc_sm(int port, enum usb_tc_state start_state)
-{
- int res;
-
- /* Clear flags before we transitions states */
- tc[port].flags = 0;
-
- res = tcpm_init(port);
-
- CPRINTS("C%d: TCPC init %s", port, res ? "failed" : "ready");
-
- /*
- * Update the Rp Value. We don't need to update CC lines though as that
- * happens in below set_state transition.
- */
- typec_select_src_current_limit_rp(port,
- typec_get_default_current_limit_rp(port));
-
- /* Disable if restart failed, otherwise start in default state. */
- set_state_tc(port, res ? TC_DISABLED : start_state);
-
- if (IS_ENABLED(CONFIG_USBC_SS_MUX))
- /* Initialize USB mux to its default state */
- usb_mux_init(port);
-
- if (IS_ENABLED(CONFIG_USBC_PPC)) {
- /*
- * Wait to initialize the PPC after tcpc, which sets
- * the correct Rd values; otherwise the TCPC might
- * not be pulling the CC lines down when the PPC connects the
- * CC lines from the USB connector to the TCPC cause the source
- * to drop Vbus causing a brown out.
- */
- ppc_init(port);
- }
-
- if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) {
- /*
- * Only initialize PD supplier current limit to 0.
- * Defer initializing type-C supplier current limit
- * to Unattached.SNK or Attached.SNK.
- */
- pd_set_input_current_limit(port, 0, 0);
- charge_manager_update_dualrole(port, CAP_UNKNOWN);
- }
-
- /*
- * PD r3.0 v2.0, ss6.2.1.1.5:
- * After a physical or logical (USB Type-C Error Recovery) Attach, a
- * Port discovers the common Specification Revision level between itself
- * and its Port Partner and/or the Cable Plug(s), and uses this
- * Specification Revision level until a Detach, Hard Reset or Error
- * Recovery happens.
- *
- * This covers the Error Recovery case, because TC_ERROR_RECOVERY
- * reinitializes the TC state machine. This also covers the implicit
- * case when PD is suspended and resumed or when the state machine is
- * first initialized.
- */
- if (IS_ENABLED(CONFIG_USB_PRL_SM))
- prl_set_default_pd_revision(port);
-
-#ifdef CONFIG_USB_PE_SM
- tc_enable_pd(port, 0);
- tc[port].ps_reset_state = PS_STATE0;
-#endif
-}
-
-void tc_state_init(int port)
-{
- enum usb_tc_state first_state;
-
- /* For test builds, replicate static initialization */
- if (IS_ENABLED(TEST_BUILD)) {
- int i;
-
- for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; ++i) {
- memset(&tc[i], 0, sizeof(tc[i]));
- drp_state[i] = CONFIG_USB_PD_INITIAL_DRP_STATE;
- }
- }
-
- /* If port is not available, there is nothing to initialize */
- if (port >= board_get_usb_pd_port_count()) {
- tc_enable_pd(port, 0);
- TC_SET_FLAG(port, TC_FLAGS_REQUEST_SUSPEND);
- return;
- }
-
-
- /* Allow system to set try src enable */
- if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC))
- tc_try_src_override(TRY_SRC_NO_OVERRIDE);
-
- /*
- * Set initial PD communication policy.
- */
- tc_policy_pd_enable(port, pd_comm_allowed_by_policy());
-
-#ifdef HAS_TASK_CHIPSET
- /* Set dual-role state based on chipset power state */
- if (chipset_in_state(CHIPSET_STATE_ANY_OFF))
- pd_set_dual_role_and_event(port, PD_DRP_FORCE_SINK, 0);
- else if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND))
- pd_set_dual_role_and_event(port, pd_get_drp_state_in_suspend(), 0);
- else /* CHIPSET_STATE_ON */
- pd_set_dual_role_and_event(port, PD_DRP_TOGGLE_ON, 0);
-#else
- pd_set_dual_role_and_event(port, board_tc_get_initial_drp_mode(port), 0);
-#endif
-
- /*
- * We are going to apply CC open (start with ErrorRecovery state)
- * unless there is something which forbids us to do that (one of
- * conditions below is true)
- */
- first_state = TC_ERROR_RECOVERY;
-
- /*
- * If we just lost power, don't apply CC open. Otherwise we would boot
- * loop, and if this is a fresh power on, then we know there isn't any
- * stale PD state as well.
- */
- if (system_get_reset_flags() &
- (EC_RESET_FLAG_BROWNOUT | EC_RESET_FLAG_POWER_ON)) {
- first_state = TC_UNATTACHED_SNK;
- }
-
- /*
- * If this is non-EFS2 device, battery is not present and EC RO doesn't
- * keep power-on reset flag after reset caused by H1, then don't apply
- * CC open because it will cause brown out.
- *
- * Please note that we are checking if CONFIG_BOARD_RESET_AFTER_POWER_ON
- * is defined now, but actually we need to know if it was enabled in
- * EC RO! It was assumed that if CONFIG_BOARD_RESET_AFTER_POWER_ON is
- * defined now it was defined in EC RO too.
- */
- if (!IS_ENABLED(CONFIG_BOARD_RESET_AFTER_POWER_ON) &&
- !IS_ENABLED(CONFIG_VBOOT_EFS2) && IS_ENABLED(CONFIG_BATTERY) &&
- (battery_is_present() == BP_NO)) {
- first_state = TC_UNATTACHED_SNK;
- }
-
- if (first_state == TC_UNATTACHED_SNK) {
- /* Turn off any previous sourcing */
- tc_src_power_off(port);
- set_vconn(port, 0);
- }
-
-#ifdef CONFIG_USB_PD_TCPC_BOARD_INIT
- /* Board specific TCPC init */
- board_tcpc_init();
-#endif
-
- /*
- * Start with ErrorRecovery state if we can to put us in
- * a clean state from any previous boots.
- */
- restart_tc_sm(port, first_state);
-}
-
-enum pd_cable_plug tc_get_cable_plug(int port)
-{
- /*
- * Messages sent by this state machine are always from a DFP/UFP,
- * i.e. the chromebook.
- */
- return PD_PLUG_FROM_DFP_UFP;
-}
-
-void pd_comm_enable(int port, int en)
-{
- tc_policy_pd_enable(port, en);
-}
-
-uint8_t tc_get_polarity(int port)
-{
- return tc[port].polarity;
-}
-
-uint8_t tc_get_pd_enabled(int port)
-{
- return !tc[port].pd_disabled_mask;
-}
-
-bool pd_alt_mode_capable(int port)
-{
- return IS_ENABLED(CONFIG_USB_PE_SM) && tc_get_pd_enabled(port);
-}
-
-void tc_set_power_role(int port, enum pd_power_role role)
-{
- tc[port].power_role = role;
-}
-
-/*
- * Private Functions
- */
-
-/* Set GPIO_CCD_MODE_ODL gpio */
-static void set_ccd_mode(const int port, const bool enable)
-{
- if (IS_ENABLED(CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT) &&
- port == CONFIG_CCD_USBC_PORT_NUMBER) {
- if (enable)
- CPRINTS("Asserting GPIO_CCD_MODE_ODL");
- gpio_set_level(_GPIO_CCD_MODE_ODL, !enable);
- }
-}
-
-/* Set the TypeC state machine to a new state. */
-static void set_state_tc(const int port, const enum usb_tc_state new_state)
-{
- assert(port == TASK_ID_TO_PD_PORT(task_get_current()));
-
- set_state(port, &tc[port].ctx, &tc_states[new_state]);
-}
-
-/* Get the current TypeC state. */
-test_export_static enum usb_tc_state get_state_tc(const int port)
-{
- /* Default to returning TC_STATE_COUNT if no state has been set */
- if (tc[port].ctx.current == NULL)
- return TC_STATE_COUNT;
- else
- return tc[port].ctx.current - &tc_states[0];
-}
-
-/* Get the previous TypeC state. */
-static enum usb_tc_state get_last_state_tc(const int port)
-{
- return tc[port].ctx.previous - &tc_states[0];
-}
-
-static void print_current_state(const int port)
-{
- if (IS_ENABLED(USB_PD_DEBUG_LABELS))
- CPRINTS_L1("C%d: %s", port, tc_state_names[get_state_tc(port)]);
- else
- CPRINTS("C%d: tc-st%d", port, get_state_tc(port));
-}
-
-static void handle_device_access(int port)
-{
- if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER) &&
- get_state_tc(port) == TC_LOW_POWER_MODE) {
- tc_start_event_loop(port);
- pd_timer_enable(port, TC_TIMER_LOW_POWER_TIME,
- PD_LPM_DEBOUNCE_US);
- }
-}
-
-void tc_event_check(int port, int evt)
-{
-#ifdef DEBUG_PRINT_FLAG_AND_EVENT_NAMES
- if (evt != TASK_EVENT_TIMER)
- print_bits(port, "Event", evt, event_bit_names,
- ARRAY_SIZE(event_bit_names));
-#endif
-
- if (evt & PD_EXIT_LOW_POWER_EVENT_MASK)
- TC_SET_FLAG(port, TC_FLAGS_CHECK_CONNECTION);
-
- if (evt & PD_EVENT_DEVICE_ACCESSED)
- handle_device_access(port);
-
- if (evt & PD_EVENT_TCPC_RESET)
- reset_device_and_notify(port);
-
- if (evt & PD_EVENT_RX_HARD_RESET)
- pd_execute_hard_reset(port);
-
- if (evt & PD_EVENT_SEND_HARD_RESET) {
- /* Pass Hard Reset request to PE layer if available */
- if (IS_ENABLED(CONFIG_USB_PE_SM) && tc_get_pd_enabled(port))
- pd_dpm_request(port, DPM_REQUEST_HARD_RESET_SEND);
- }
-
-#ifdef CONFIG_POWER_COMMON
- if (IS_ENABLED(CONFIG_POWER_COMMON)) {
- if (evt & PD_EVENT_POWER_STATE_CHANGE)
- handle_new_power_state(port);
- }
-#endif /* CONFIG_POWER_COMMON */
-
- if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) {
- int i;
-
- /*
- * Notify all ports of sysjump
- */
- if (evt & PD_EVENT_SYSJUMP) {
- for (i = 0; i <
- CONFIG_USB_PD_PORT_MAX_COUNT; i++)
- dpm_set_mode_exit_request(i);
- notify_sysjump_ready();
- }
- }
-
- if (evt & PD_EVENT_UPDATE_DUAL_ROLE)
- pd_update_dual_role_config(port);
-}
-
-/*
- * CC values for regular sources and Debug sources (aka DTS)
- *
- * Source type Mode of Operation CC1 CC2
- * ---------------------------------------------
- * Regular Default USB Power RpUSB Open
- * Regular USB-C @ 1.5 A Rp1A5 Open
- * Regular USB-C @ 3 A Rp3A0 Open
- * DTS Default USB Power Rp3A0 Rp1A5
- * DTS USB-C @ 1.5 A Rp1A5 RpUSB
- * DTS USB-C @ 3 A Rp3A0 RpUSB
- */
-
-void tc_set_data_role(int port, enum pd_data_role role)
-{
- enum pd_data_role prev_data_role;
-
- prev_data_role = tc[port].data_role;
- tc[port].data_role = role;
-
- if (IS_ENABLED(CONFIG_USBC_SS_MUX))
- set_usb_mux_with_current_data_role(port);
-
- /*
- * Run any board-specific code for role swap (e.g. setting OTG signals
- * to SoC).
- */
- pd_execute_data_swap(port, role);
-
- /*
- * For BC1.2 detection that is triggered on data role change events
- * instead of VBUS changes, need to set an event to wake up the USB_CHG
- * task and indicate the current data role.
- */
- bc12_role_change_handler(port, prev_data_role, tc[port].data_role);
-
- /* Notify TCPC of role update */
- tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role);
-}
-
-static void sink_stop_drawing_current(int port)
-{
- pd_set_input_current_limit(port, 0, 0);
-
- if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) {
- typec_set_input_current_limit(port, 0, 0);
- charge_manager_set_ceil(port,
- CEIL_REQUESTOR_PD, CHARGE_CEIL_NONE);
- }
-}
-
-static void pd_update_try_source(void)
-{
-#ifdef CONFIG_USB_PD_TRY_SRC
- tc_enable_try_src(pd_is_try_source_capable());
-#endif
-}
-DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, pd_update_try_source, HOOK_PRIO_DEFAULT);
-
-static void set_vconn(int port, int enable)
-{
- if (enable)
- TC_SET_FLAG(port, TC_FLAGS_VCONN_ON);
- else
- TC_CLR_FLAG(port, TC_FLAGS_VCONN_ON);
-
- /*
- * Check our OC event counter. If we've exceeded our threshold, then
- * let's latch our source path off to prevent continuous cycling. When
- * the PD state machine detects a disconnection on the CC lines, we will
- * reset our OC event counter.
- */
- if (IS_ENABLED(CONFIG_USBC_OCP) &&
- enable && usbc_ocp_is_port_latched_off(port))
- return;
-
- /*
- * Disable PPC Vconn first then TCPC in case the voltage feeds back
- * to TCPC and damages.
- */
- if (IS_ENABLED(CONFIG_USBC_PPC_VCONN) && !enable)
- ppc_set_vconn(port, 0);
-
- /*
- * Some TCPCs/PPC combinations can trigger OVP if the TCPC doesn't
- * source VCONN. This happens if the TCPC will trip OVP with 5V, and the
- * PPC doesn't isolate the TCPC from VCONN when sourcing. But, some PPCs
- * which do isolate the TCPC can't handle 5V on its host-side CC pins,
- * so the TCPC shouldn't source VCONN in those cases.
- *
- * In the first case, both TCPC and PPC will potentially source Vconn,
- * but that should be okay since Vconn has "make before break"
- * electrical requirements when swapping anyway.
- *
- * See b/72961003 and b/180973460
- */
- tcpm_set_vconn(port, enable);
-
- if (IS_ENABLED(CONFIG_USBC_PPC_VCONN) && enable)
- ppc_set_vconn(port, 1);
-}
-
-/* This must only be called from the PD task */
-static void pd_update_dual_role_config(int port)
-{
- if (tc[port].power_role == PD_ROLE_SOURCE &&
- (drp_state[port] == PD_DRP_FORCE_SINK ||
- (drp_state[port] == PD_DRP_TOGGLE_OFF &&
- get_state_tc(port) == TC_UNATTACHED_SRC))) {
- /*
- * Change to sink if port is currently a source AND (new DRP
- * state is force sink OR new DRP state is toggle off and we are
- * in the source disconnected state).
- */
- set_state_tc(port, TC_UNATTACHED_SNK);
- } else if (tc[port].power_role == PD_ROLE_SINK &&
- drp_state[port] == PD_DRP_FORCE_SOURCE) {
- /*
- * Change to source if port is currently a sink and the
- * new DRP state is force source.
- */
- set_state_tc(port, TC_UNATTACHED_SRC);
- }
-}
-
-__maybe_unused static void handle_new_power_state(int port)
-{
- if (!IS_ENABLED(CONFIG_POWER_COMMON))
- assert(0);
-
- if (IS_ENABLED(CONFIG_POWER_COMMON) &&
- IS_ENABLED(CONFIG_USB_PE_SM)) {
- if (chipset_in_or_transitioning_to_state(
- CHIPSET_STATE_ANY_OFF)) {
- /*
- * The SoC will negotiate alternate mode again when it
- * boots up
- */
- dpm_set_mode_exit_request(port);
- }
- }
-
- /*
- * If the sink port was sourcing Vconn, and can no longer, request a
- * hard reset on this port to restore Vconn to the source.
- */
- if (IS_ENABLED(CONFIG_USB_PE_SM)) {
- if (tc_is_vconn_src(port) && tc_is_attached_snk(port) &&
- !pd_check_vconn_swap(port))
- pd_dpm_request(port, DPM_REQUEST_HARD_RESET_SEND);
- }
-
- /*
- * TC_FLAGS_UPDATE_USB_MUX is set on chipset startup and shutdown.
- * Set the USB mux according to the new power state. If the chipset
- * is transitioning to OFF, this disconnects USB and DP mux.
- *
- * Transitions to and from suspend states do not change the USB mux
- * or the alternate mode configuration.
- */
- if (TC_CHK_FLAG(port, TC_FLAGS_UPDATE_USB_MUX)) {
- TC_CLR_FLAG(port, TC_FLAGS_UPDATE_USB_MUX);
- set_usb_mux_with_current_data_role(port);
- }
-}
-
-#ifdef CONFIG_USBC_VCONN_SWAP
-void pd_request_vconn_swap_off(int port)
-{
- if (get_state_tc(port) == TC_ATTACHED_SRC ||
- get_state_tc(port) == TC_ATTACHED_SNK) {
- TC_SET_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_OFF);
- task_wake(PD_PORT_TO_TASK_ID(port));
- }
-}
-
-void pd_request_vconn_swap_on(int port)
-{
- if (get_state_tc(port) == TC_ATTACHED_SRC ||
- get_state_tc(port) == TC_ATTACHED_SNK) {
- TC_SET_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON);
- task_wake(PD_PORT_TO_TASK_ID(port));
- }
-}
-
-void pd_request_vconn_swap(int port)
-{
- pd_dpm_request(port, DPM_REQUEST_VCONN_SWAP);
-}
-#endif
-
-int tc_is_vconn_src(int port)
-{
- if (IS_ENABLED(CONFIG_USBC_VCONN))
- return TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON);
- else
- return 0;
-}
-
-static __maybe_unused int reset_device_and_notify(int port)
-{
- int rv;
- int task, waiting_tasks;
-
- /* 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);
- TC_CLR_FLAG(port, TC_FLAGS_LPM_ENGAGED);
- tc_start_event_loop(port);
-
- CPRINTS("C%d: TCPC init %s", port, rv ? "failed!" : "ready");
-
- /*
- * Before getting the other tasks that are waiting, clear the reset
- * event from this PD task to prevent multiple reset/init events
- * occurring.
- *
- * The double reset event happens when the higher priority PD interrupt
- * task gets an interrupt during the above tcpm_init function. When that
- * occurs, the higher priority task waits correctly for us to finish
- * waking the TCPC, but it has also set PD_EVENT_TCPC_RESET again, which
- * would result in a second, unnecessary init.
- */
- atomic_clear_bits(task_get_event_bitmap(task_get_current()),
- PD_EVENT_TCPC_RESET);
-
- waiting_tasks = atomic_clear(&tc[port].tasks_waiting_on_reset);
-
- /* Wake up all waiting tasks. */
- while (waiting_tasks) {
- task = __fls(waiting_tasks);
- waiting_tasks &= ~BIT(task);
- task_set_event(task, TASK_EVENT_PD_AWAKE);
- }
-
- return rv;
-}
-
-#ifdef CONFIG_USB_PD_TCPC_LOW_POWER
-void pd_wait_exit_low_power(int port)
-{
- if (!TC_CHK_FLAG(port, TC_FLAGS_LPM_ENGAGED))
- return;
-
- if (port == TASK_ID_TO_PD_PORT(task_get_current())) {
- if (!TC_CHK_FLAG(port, TC_FLAGS_LPM_TRANSITION))
- 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);
- task_wait_event_mask(TASK_EVENT_PD_AWAKE, -1);
- }
-}
-
-/*
- * 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 (port == TASK_ID_TO_PD_PORT(task_get_current()))
- handle_device_access(port);
- else
- task_set_event(PD_PORT_TO_TASK_ID(port),
- PD_EVENT_DEVICE_ACCESSED);
-}
-
-/*
- * TODO(b/137493121): Move this function to a separate file that's shared
- * between the this and the original stack.
- */
-void pd_prevent_low_power_mode(int port, int prevent)
-{
- const int current_task_mask = (1 << task_get_current());
-
- if (prevent)
- atomic_or(&tc[port].tasks_preventing_lpm, current_task_mask);
- else
- atomic_clear_bits(&tc[port].tasks_preventing_lpm,
- current_task_mask);
-}
-#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */
-
-static void sink_power_sub_states(int port)
-{
- enum tcpc_cc_voltage_status cc1, cc2, cc;
- enum tcpc_cc_voltage_status new_cc_voltage;
-
- tcpm_get_cc(port, &cc1, &cc2);
-
- cc = polarity_rm_dts(tc[port].polarity) ? cc2 : cc1;
-
- if (cc == TYPEC_CC_VOLT_RP_DEF)
- new_cc_voltage = TYPEC_CC_VOLT_RP_DEF;
- else if (cc == TYPEC_CC_VOLT_RP_1_5)
- new_cc_voltage = TYPEC_CC_VOLT_RP_1_5;
- else if (cc == TYPEC_CC_VOLT_RP_3_0)
- new_cc_voltage = TYPEC_CC_VOLT_RP_3_0;
- else
- new_cc_voltage = TYPEC_CC_VOLT_OPEN;
-
- /* Debounce the cc state */
- if (new_cc_voltage != tc[port].cc_voltage) {
- tc[port].cc_voltage = new_cc_voltage;
- pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE,
- PD_T_RP_VALUE_CHANGE);
- return;
- }
-
- if (!pd_timer_is_disabled(port, TC_TIMER_CC_DEBOUNCE)) {
- if (!pd_timer_is_expired(port, TC_TIMER_CC_DEBOUNCE))
- return;
-
- pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE);
-
- if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) {
- tc[port].typec_curr = usb_get_typec_current_limit(
- tc[port].polarity, cc1, cc2);
-
- typec_set_input_current_limit(port,
- tc[port].typec_curr, TYPE_C_VOLTAGE);
- charge_manager_update_dualrole(port, CAP_DEDICATED);
- }
- }
-}
-
-
-/*
- * TYPE-C State Implementations
- */
-
-/**
- * Disabled
- *
- * Super State Entry Actions:
- * Remove the terminations from CC
- * Set VBUS and VCONN off
- */
-static void tc_disabled_entry(const int port)
-{
- print_current_state(port);
- /*
- * We have completed tc_cc_open_entry (our super state), so set flag
- * to indicate to pd_is_port_enabled that we are now suspended.
- */
- TC_SET_FLAG(port, TC_FLAGS_SUSPENDED);
-}
-
-static void tc_disabled_run(const int port)
-{
- /* If pd_set_suspend clears the request, go to TC_UNATTACHED_SNK/SRC. */
- if (!TC_CHK_FLAG(port, TC_FLAGS_REQUEST_SUSPEND)) {
- set_state_tc(port, drp_state[port] == PD_DRP_FORCE_SOURCE ?
- TC_UNATTACHED_SRC : TC_UNATTACHED_SNK);
- } else {
- if (IS_ENABLED(CONFIG_USBC_RETIMER_FW_UPDATE)) {
- if (TC_CHK_FLAG(port,
- TC_FLAGS_USB_RETIMER_FW_UPDATE_LTD_RUN)) {
- TC_CLR_FLAG(port,
- TC_FLAGS_USB_RETIMER_FW_UPDATE_LTD_RUN);
- usb_retimer_fw_update_process_op_cb(port);
- }
- }
- tc_pause_event_loop(port);
- }
-}
-
-static void tc_disabled_exit(const int port)
-{
- int rv;
-
- tc_start_event_loop(port);
- TC_CLR_FLAG(port, TC_FLAGS_SUSPENDED);
-
- rv = tcpm_init(port);
- CPRINTS("C%d: TCPC init %s", port, rv ? "failed!" : "ready");
-}
-
-/**
- * ErrorRecovery
- *
- * Super State Entry Actions:
- * Remove the terminations from CC
- * Set's VBUS and VCONN off
- */
-static void tc_error_recovery_entry(const int port)
-{
- print_current_state(port);
-
- pd_timer_enable(port, TC_TIMER_TIMEOUT, PD_T_ERROR_RECOVERY);
-
- TC_CLR_FLAG(port, TC_FLAGS_REQUEST_ERROR_RECOVERY);
-}
-
-static void tc_error_recovery_run(const int port)
-{
- enum usb_tc_state start_state;
-
- if (!pd_timer_is_expired(port, TC_TIMER_TIMEOUT))
- return;
-
- /*
- * If we transitioned to error recovery as the first state and we
- * didn't brown out, we don't need to reinitialized the tc statemachine
- * because we just did that. So transition to the state directly.
- */
- if (tc[port].ctx.previous == NULL) {
- set_state_tc(port, drp_state[port] == PD_DRP_FORCE_SOURCE ?
- TC_UNATTACHED_SRC : TC_UNATTACHED_SNK);
- return;
- }
-
- /*
- * If try src support is active (e.g. in S0). Then try to become the
- * SRC, otherwise we should try to be the sink.
- */
- start_state = TC_UNATTACHED_SNK;
- if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC))
- if (is_try_src_enabled(port) ||
- drp_state[port] == PD_DRP_FORCE_SOURCE)
- start_state = TC_UNATTACHED_SRC;
-
- restart_tc_sm(port, start_state);
-}
-
-static void tc_error_recovery_exit(const int port)
-{
- pd_timer_disable(port, TC_TIMER_TIMEOUT);
-}
-
-/**
- * Unattached.SNK
- */
-static void tc_unattached_snk_entry(const int port)
-{
- enum pd_data_role prev_data_role;
-
- if (get_last_state_tc(port) != TC_UNATTACHED_SRC) {
- tc_detached(port);
- print_current_state(port);
- }
-
- /*
- * We are in an unattached state and considering to be a SNK
- * searching for a SRC partner. We set the CC pull value to
- * to indicate our intent to be SNK in hopes a partner SRC
- * will is there to attach to.
- *
- * Both CC1 and CC2 pins shall be independently terminated to
- * ground through Rd.
- *
- * Restore default current limit Rp in case we swap to source
- *
- * Run any debug detaches needed before setting CC, as some TCPCs may
- * require we set CC Open before changing power roles with a debug
- * accessory.
- */
- tcpm_debug_detach(port);
- typec_select_pull(port, TYPEC_CC_RD);
- typec_select_src_current_limit_rp(port,
- typec_get_default_current_limit_rp(port));
- typec_update_cc(port);
-
-
- prev_data_role = tc[port].data_role;
- tc[port].data_role = PD_ROLE_DISCONNECTED;
- /*
- * When data role set events are used to enable BC1.2, then CC
- * detach events are used to notify BC1.2 that it can be powered
- * down.
- */
- bc12_role_change_handler(port, prev_data_role, tc[port].data_role);
-
- if (IS_ENABLED(CONFIG_CHARGE_MANAGER))
- charge_manager_update_dualrole(port, CAP_UNKNOWN);
-
- tc_set_partner_role(port, PPC_DEV_DISCONNECTED);
-
- /*
- * Indicate that the port is disconnected so the board
- * can restore state from any previous data swap.
- */
- pd_execute_data_swap(port, PD_ROLE_DISCONNECTED);
- pd_timer_enable(port, TC_TIMER_NEXT_ROLE_SWAP, PD_T_DRP_SNK);
-
- if (IS_ENABLED(CONFIG_USB_PE_SM)) {
- CLR_FLAGS_ON_DISCONNECT(port);
- tc_enable_pd(port, 0);
- }
-}
-
-static void tc_unattached_snk_run(const int port)
-{
- enum tcpc_cc_voltage_status cc1, cc2;
-
- /*
- * TODO(b/137498392): Add wait before sampling the CC
- * status after role changes
- */
-
- if (IS_ENABLED(CONFIG_USB_PE_SM)) {
- if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED)) {
- TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED);
- tc_set_data_role(port, PD_ROLE_UFP);
- /* Inform Policy Engine that hard reset is complete */
- pe_ps_reset_complete(port);
- }
- }
-
- /* Check for connection */
- tcpm_get_cc(port, &cc1, &cc2);
-
- /*
- * The port shall transition to AttachWait.SNK when a Source
- * connection is detected, as indicated by the SNK.Rp state
- * on at least one of its CC pins.
- *
- * A DRP shall transition to Unattached.SRC within tDRPTransition
- * after the state of both CC pins is SNK.Open for
- * tDRP − dcSRC.DRP ∙ tDRP.
- */
- if (cc_is_rp(cc1) || cc_is_rp(cc2)) {
- /* Connection Detected */
- set_state_tc(port, TC_ATTACH_WAIT_SNK);
- return;
- }
-
- /*
- * Debounce the CC open status. Some TCPC needs time to get the CC
- * status valid. Before that, CC open is reported by default. Wait
- * to make sure the CC is really open. Reuse the role toggle timer.
- */
- if (!pd_timer_is_expired(port, TC_TIMER_NEXT_ROLE_SWAP))
- return;
-
- /*
- * Initialize type-C supplier current limits to 0. The charge
- * manage is now seeded if it was not.
- */
- if (IS_ENABLED(CONFIG_CHARGE_MANAGER))
- typec_set_input_current_limit(port, 0, 0);
-
- /*
- * Attempt TCPC auto DRP toggle if it is
- * not already auto toggling.
- */
- if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) &&
- drp_state[port] == PD_DRP_TOGGLE_ON &&
- tcpm_auto_toggle_supported(port)) {
- set_state_tc(port, TC_DRP_AUTO_TOGGLE);
- } else if (drp_state[port] == PD_DRP_TOGGLE_ON) {
- /* DRP Toggle. The timer was checked above. */
- set_state_tc(port, TC_UNATTACHED_SRC);
- } else if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER) &&
- (drp_state[port] == PD_DRP_FORCE_SINK ||
- drp_state[port] == PD_DRP_TOGGLE_OFF)) {
- set_state_tc(port, TC_LOW_POWER_MODE);
- }
-}
-
-static void tc_unattached_snk_exit(const int port)
-{
- pd_timer_disable(port, TC_TIMER_NEXT_ROLE_SWAP);
-}
-
-/**
- * AttachWait.SNK
- *
- * Super State Entry Actions:
- * Vconn Off
- * Place Rd on CC
- * Set power role to SINK
- */
-static void tc_attach_wait_snk_entry(const int port)
-{
- print_current_state(port);
-
- tc[port].cc_state = PD_CC_UNSET;
-}
-
-static void tc_attach_wait_snk_run(const int port)
-{
- enum tcpc_cc_voltage_status cc1, cc2;
- enum pd_cc_states new_cc_state;
-
- /* Check for connection */
- tcpm_get_cc(port, &cc1, &cc2);
-
- if (cc_is_rp(cc1) && cc_is_rp(cc2) && board_is_dts_port(port))
- new_cc_state = PD_CC_DFP_DEBUG_ACC;
- else if (cc_is_rp(cc1) || cc_is_rp(cc2))
- new_cc_state = PD_CC_DFP_ATTACHED;
- else
- new_cc_state = PD_CC_NONE;
-
- /* Debounce the cc state */
- if (new_cc_state != tc[port].cc_state) {
- pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, PD_T_CC_DEBOUNCE);
- pd_timer_enable(port, TC_TIMER_PD_DEBOUNCE, PD_T_PD_DEBOUNCE);
- tc[port].cc_state = new_cc_state;
- return;
- }
-
- /*
- * A DRP shall transition to Unattached.SRC when the state of both
- * the CC1 and CC2 pins is SNK.Open for at least tPDDebounce, however
- * when DRP state prevents switch to SRC the next state should be
- * Unattached.SNK.
- */
- if (new_cc_state == PD_CC_NONE &&
- pd_timer_is_expired(port, TC_TIMER_PD_DEBOUNCE)) {
- /* We are detached */
- if (drp_state[port] == PD_DRP_TOGGLE_OFF
- || drp_state[port] == PD_DRP_FREEZE
- || drp_state[port] == PD_DRP_FORCE_SINK)
- set_state_tc(port, TC_UNATTACHED_SNK);
- else
- set_state_tc(port, TC_UNATTACHED_SRC);
- return;
- }
-
- /* Wait for CC debounce */
- if (!pd_timer_is_expired(port, TC_TIMER_CC_DEBOUNCE))
- return;
-
- /*
- * The port shall transition to Attached.SNK after the state of only
- * one of the CC1 or CC2 pins is SNK.Rp for at least tCCDebounce and
- * VBUS is detected.
- *
- * A DRP that strongly prefers the Source role may optionally
- * transition to Try.SRC instead of Attached.SNK when the state of only
- * one CC pin has been SNK.Rp for at least tCCDebounce and VBUS is
- * detected.
- *
- * If the port supports Debug Accessory Mode, the port shall transition
- * to DebugAccessory.SNK if the state of both the CC1 and CC2 pins is
- * SNK.Rp for at least tCCDebounce and VBUS is detected.
- */
- if (pd_is_vbus_present(port)) {
- if (new_cc_state == PD_CC_DFP_ATTACHED) {
- if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC) &&
- is_try_src_enabled(port))
- set_state_tc(port, TC_TRY_SRC);
- else
- set_state_tc(port, TC_ATTACHED_SNK);
- } else {
- /* new_cc_state is PD_CC_DFP_DEBUG_ACC */
- CPRINTS("C%d: Debug accessory detected", port);
- TC_SET_FLAG(port, TC_FLAGS_TS_DTS_PARTNER);
- set_state_tc(port, TC_ATTACHED_SNK);
- }
-
- if (IS_ENABLED(CONFIG_USB_PE_SM) &&
- IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) {
- hook_call_deferred(&pd_usb_billboard_deferred_data,
- PD_T_AME);
- }
- }
-}
-
-static void tc_attach_wait_snk_exit(const int port)
-{
- pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE);
- pd_timer_disable(port, TC_TIMER_PD_DEBOUNCE);
-}
-
-/**
- * Attached.SNK, shared with Debug Accessory.SNK
- */
-static void tc_attached_snk_entry(const int port)
-{
- enum tcpc_cc_voltage_status cc1, cc2;
-
- print_current_state(port);
-
- /*
- * Known state of attach is SNK. We need to apply this pull value
- * to make it set in hardware at the correct time but set the common
- * pull here.
- *
- * Both CC1 and CC2 pins shall be independently terminated to
- * ground through Rd.
- */
- typec_select_pull(port, TYPEC_CC_RD);
-
- /* Inform the PPC and OCP module that a source is connected */
- tc_set_partner_role(port, PPC_DEV_SRC);
-
- if (IS_ENABLED(CONFIG_USB_PE_SM) &&
- TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) {
- /* Flipping power role - Disable AutoDischargeDisconnect */
- tcpm_enable_auto_discharge_disconnect(port, 0);
-
- /* Apply Rd */
- typec_update_cc(port);
-
- /* Change role to sink */
- tc_set_power_role(port, PD_ROLE_SINK);
- tcpm_set_msg_header(port, tc[port].power_role,
- tc[port].data_role);
-
- /*
- * Maintain VCONN supply state, whether ON or OFF, and its
- * data role / usb mux connections. Do not re-enable
- * AutoDischargeDisconnect until the swap is completed
- * and tc_pr_swap_complete is called.
- */
- } else {
- /* Get connector orientation */
- tcpm_get_cc(port, &cc1, &cc2);
- tc[port].polarity = get_snk_polarity(cc1, cc2);
- pd_set_polarity(port, tc[port].polarity);
-
- tc_set_data_role(port, PD_ROLE_UFP);
-
- hook_notify(HOOK_USB_PD_CONNECT);
-
- if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) {
- tc[port].typec_curr =
- usb_get_typec_current_limit(tc[port].polarity,
- cc1, cc2);
- typec_set_input_current_limit(port,
- tc[port].typec_curr, TYPE_C_VOLTAGE);
- /*
- * Start new connections as dedicated until source caps
- * are received, at which point the PE will update the
- * flag.
- */
- charge_manager_update_dualrole(port, CAP_DEDICATED);
- }
-
- /* Apply Rd */
- typec_update_cc(port);
-
- /*
- * Attached.SNK - enable AutoDischargeDisconnect
- * Do this after applying Rd to CC lines to avoid
- * TCPC_REG_FAULT_STATUS_AUTO_DISCHARGE_FAIL (b/171567398)
- */
- tcpm_enable_auto_discharge_disconnect(port, 1);
- }
-
- pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE);
-
- /* Enable PD */
- if (IS_ENABLED(CONFIG_USB_PE_SM))
- tc_enable_pd(port, 1);
-
- if (TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) {
- tcpm_debug_accessory(port, 1);
- set_ccd_mode(port, 1);
- }
-}
-
-/*
- * Check whether Vbus has been removed on this port, accounting for some Vbus
- * debounce if FRS is enabled.
- *
- * Returns true if a new state was set and the calling run should exit.
- */
-static bool tc_snk_check_vbus_removed(const int port)
-{
- if (IS_ENABLED(CONFIG_USB_PD_FRS)) {
- /*
- * Debounce Vbus presence when FRS is enabled. Note that we may
- * lose Vbus before the FRS signal comes in to let us know
- * we're PR swapping, but we must still transition to unattached
- * within tSinkDisconnect.
- *
- * We may safely re-use the Vbus debounce timer here
- * since a PR swap would no longer be in progress when Vbus
- * removal is checked.
- */
- if (pd_check_vbus_level(port, VBUS_REMOVED)) {
- if (pd_timer_is_disabled(port,
- TC_TIMER_VBUS_DEBOUNCE)) {
- pd_timer_enable(port, TC_TIMER_VBUS_DEBOUNCE,
- PD_T_FRS_VBUS_DEBOUNCE);
- } else if (pd_timer_is_expired(port,
- TC_TIMER_VBUS_DEBOUNCE)) {
- set_state_tc(port, TC_UNATTACHED_SNK);
- return true;
- }
- } else {
- pd_timer_disable(port, TC_TIMER_VBUS_DEBOUNCE);
- }
- } else if (pd_check_vbus_level(port, VBUS_REMOVED)) {
- set_state_tc(port, TC_UNATTACHED_SNK);
- return true;
- }
-
- return false;
-}
-
-static void tc_attached_snk_run(const int port)
-{
-#ifdef CONFIG_USB_PE_SM
- /*
- * Perform Hard Reset
- */
- if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED)) {
- /*
- * Wait to clear the hard reset request until Vbus has returned
- * to default (or, if it didn't return, we transition to
- * unattached)
- */
- if (tc_perform_snk_hard_reset(port))
- TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED);
-
- return;
- }
-
- /*
- * From 4.5.2.2.5.2 Exiting from Attached.SNK State:
- *
- * "A port that is not a Vconn-Powered USB Device and is not in the
- * process of a USB PD PR_Swap or a USB PD Hard Reset or a USB PD
- * FR_Swap shall transition to Unattached.SNK within tSinkDisconnect
- * when Vbus falls below vSinkDisconnect for Vbus operating at or
- * below 5 V or below vSinkDisconnectPD when negotiated by USB PD
- * to operate above 5 V."
- *
- * TODO(b/149530538): Use vSinkDisconnectPD when above 5V
- */
-
- /*
- * Debounce Vbus before we drop that we are doing a PR_Swap
- */
- if (TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS) &&
- pd_timer_is_expired(port, TC_TIMER_VBUS_DEBOUNCE)) {
- /* PR Swap is no longer in progress */
- TC_CLR_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS);
- pd_timer_disable(port, TC_TIMER_VBUS_DEBOUNCE);
-
- /*
- * AutoDischargeDisconnect was turned off when we
- * hit Safe0V on SRC->SNK PR-Swap. We now are done
- * with the swap and should have Vbus, so re-enable
- * AutoDischargeDisconnect.
- */
- if (!pd_check_vbus_level(port, VBUS_REMOVED))
- tcpm_enable_auto_discharge_disconnect(port, 1);
- }
-
- /*
- * The sink will be powered off during a power role swap but we don't
- * want to trigger a disconnect.
- */
- if (!TC_CHK_FLAG(port, TC_FLAGS_POWER_OFF_SNK) &&
- !TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) {
- /*
- * Detach detection
- */
- if (tc_snk_check_vbus_removed(port))
- return;
-
- if (!pe_is_explicit_contract(port))
- sink_power_sub_states(port);
- }
-
- /*
- * PD swap commands
- */
- if (tc_get_pd_enabled(port) && prl_is_running(port)) {
- /*
- * Power Role Swap
- */
- if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP)) {
- /*
- * We may want to verify partner is applying Rd before
- * we swap. However, some TCPCs (such as TUSB422) will
- * not report the correct CC status before VBUS falls to
- * vSafe0V, so this will be problematic in the FRS case.
- */
- set_state_tc(port, TC_ATTACHED_SRC);
- return;
- }
-
- /*
- * Data Role Swap
- */
- if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP)) {
- TC_CLR_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP);
-
- /* Perform Data Role Swap */
- tc_set_data_role(port,
- tc[port].data_role == PD_ROLE_UFP ?
- PD_ROLE_DFP : PD_ROLE_UFP);
- }
-
- /*
- * VCONN Swap
- * UnorientedDebugAccessory.SRC shall not drive Vconn
- */
- if (IS_ENABLED(CONFIG_USBC_VCONN) &&
- !TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) {
- if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON)) {
- TC_CLR_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON);
-
- set_vconn(port, 1);
- /*
- * Inform policy engine that vconn swap is
- * complete
- */
- pe_vconn_swap_complete(port);
- } else if (TC_CHK_FLAG(port,
- TC_FLAGS_REQUEST_VC_SWAP_OFF)) {
- TC_CLR_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_OFF);
-
- set_vconn(port, 0);
- /*
- * Inform policy engine that vconn swap is
- * complete
- */
- pe_vconn_swap_complete(port);
- }
- }
-
- if (!TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) {
- /*
- * If the port supports Charge-Through VCONN-Powered USB
- * devices, and an explicit PD contract has failed to be
- * negotiated, the port shall query the identity of the
- * cable via USB PD on SOP’
- */
- if (!pe_is_explicit_contract(port) &&
- TC_CHK_FLAG(port, TC_FLAGS_CTVPD_DETECTED)) {
- /*
- * A port that via SOP’ has detected an
- * attached Charge-Through VCONN-Powered USB
- * device shall transition to Unattached.SRC
- * if an explicit PD contract has failed to
- * be negotiated.
- */
- /* CTVPD detected */
- set_state_tc(port, TC_UNATTACHED_SRC);
- }
- }
- }
-
-#else /* CONFIG_USB_PE_SM */
-
- /* Detach detection */
- if (tc_snk_check_vbus_removed(port))
- return;
-
- /* Run Sink Power Sub-State */
- sink_power_sub_states(port);
-#endif /* CONFIG_USB_PE_SM */
-}
-
-static void tc_attached_snk_exit(const int port)
-{
- if (!TC_CHK_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP)) {
- /*
- * If supplying VCONN, the port shall cease to supply
- * it within tVCONNOFF of exiting Attached.SNK if not
- * PR swapping.
- */
- if (TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON))
- set_vconn(port, 0);
-
- /*
- * Attached.SNK exit - disable AutoDischargeDisconnect
- * NOTE: This should not happen if we are suspending. It will
- * happen in tc_cc_open_entry if that is the path we are
- * taking.
- */
- if (!TC_CHK_FLAG(port, TC_FLAGS_REQUEST_SUSPEND))
- tcpm_enable_auto_discharge_disconnect(port, 0);
- }
-
- /* Clear flags after checking Vconn status */
- TC_CLR_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP | TC_FLAGS_POWER_OFF_SNK);
-
- /* Stop drawing power */
- sink_stop_drawing_current(port);
-
- if (TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER))
- tcpm_debug_detach(port);
-
- pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE);
- pd_timer_disable(port, TC_TIMER_TIMEOUT);
- pd_timer_disable(port, TC_TIMER_VBUS_DEBOUNCE);
-}
-
-/**
- * Unattached.SRC
- */
-static void tc_unattached_src_entry(const int port)
-{
- enum pd_data_role prev_data_role;
-
- if (get_last_state_tc(port) != TC_UNATTACHED_SNK) {
- tc_detached(port);
- print_current_state(port);
- }
-
- /*
- * We are in an unattached state and considering to be a SRC
- * searching for a SNK partner. We set the CC pull value to
- * to indicate our intent to be SRC in hopes a partner SNK
- * will is there to attach to.
- *
- * Both CC1 and CC2 pins shall be independently terminated to
- * ground through Rp.
- *
- * Restore default current limit Rp.
- *
- * Run any debug detaches needed before setting CC, as some TCPCs may
- * require we set CC Open before changing power roles with a debug
- * accessory.
- */
- tcpm_debug_detach(port);
- typec_select_pull(port, TYPEC_CC_RP);
- typec_select_src_current_limit_rp(port,
- typec_get_default_current_limit_rp(port));
- typec_update_cc(port);
-
- prev_data_role = tc[port].data_role;
- tc[port].data_role = PD_ROLE_DISCONNECTED;
-
- /*
- * When data role set events are used to enable BC1.2, then CC
- * detach events are used to notify BC1.2 that it can be powered
- * down.
- */
- bc12_role_change_handler(port, prev_data_role, tc[port].data_role);
-
- tc_set_partner_role(port, PPC_DEV_DISCONNECTED);
-
- if (IS_ENABLED(CONFIG_CHARGE_MANAGER))
- charge_manager_update_dualrole(port, CAP_UNKNOWN);
-
- if (IS_ENABLED(CONFIG_USB_PE_SM)) {
- CLR_FLAGS_ON_DISCONNECT(port);
- tc_enable_pd(port, 0);
- }
-
- pd_timer_enable(port, TC_TIMER_NEXT_ROLE_SWAP, PD_T_DRP_SRC);
-}
-
-static void tc_unattached_src_run(const int port)
-{
- enum tcpc_cc_voltage_status cc1, cc2;
-
- if (IS_ENABLED(CONFIG_USB_PE_SM)) {
- if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED)) {
- TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED);
- tc_set_data_role(port, PD_ROLE_DFP);
- /* Inform Policy Engine that hard reset is complete */
- pe_ps_reset_complete(port);
- }
- }
-
- if (IS_ENABLED(CONFIG_USBC_OCP)) {
- /*
- * If the port is latched off, just continue to
- * monitor for a detach.
- */
- if (usbc_ocp_is_port_latched_off(port))
- return;
- }
-
- /* Check for connection */
- tcpm_get_cc(port, &cc1, &cc2);
-
- /*
- * Transition to AttachWait.SRC when:
- * 1) The SRC.Rd state is detected on either CC1 or CC2 pin or
- * 2) The SRC.Ra state is detected on both the CC1 and CC2 pins.
- *
- * A DRP shall transition to Unattached.SNK within tDRPTransition
- * after dcSRC.DRP ∙ tDRP
- */
- 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 (pd_timer_is_expired(port, TC_TIMER_NEXT_ROLE_SWAP) &&
- drp_state[port] != PD_DRP_FORCE_SOURCE &&
- drp_state[port] != PD_DRP_FREEZE)
- set_state_tc(port, TC_UNATTACHED_SNK);
- /*
- * Attempt TCPC auto DRP toggle
- */
- else if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) &&
- drp_state[port] == PD_DRP_TOGGLE_ON &&
- tcpm_auto_toggle_supported(port) && cc_is_open(cc1, cc2))
- set_state_tc(port, TC_DRP_AUTO_TOGGLE);
- else if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER) &&
- (drp_state[port] == PD_DRP_FORCE_SOURCE ||
- drp_state[port] == PD_DRP_TOGGLE_OFF))
- set_state_tc(port, TC_LOW_POWER_MODE);
-}
-
-static void tc_unattached_src_exit(const int port)
-{
- pd_timer_disable(port, TC_TIMER_NEXT_ROLE_SWAP);
-}
-
-/**
- * AttachWait.SRC
- *
- * Super State Entry Actions:
- * Vconn Off
- * Place Rp on CC
- * Set power role to SOURCE
- */
-static void tc_attach_wait_src_entry(const int port)
-{
- print_current_state(port);
-
- tc[port].cc_state = PD_CC_UNSET;
-}
-
-static void tc_attach_wait_src_run(const int port)
-{
- enum tcpc_cc_voltage_status cc1, cc2;
- enum pd_cc_states new_cc_state;
-
- /* Check for connection */
- tcpm_get_cc(port, &cc1, &cc2);
-
- if (cc_is_snk_dbg_acc(cc1, cc2) && board_is_dts_port(port)) {
- /*
- * Debug accessory.
- * A debug accessory in a non-DTS port will be
- * recognized by at_least_one_rd as UFP attached.
- */
- new_cc_state = PD_CC_UFP_DEBUG_ACC;
- } else if (cc_is_at_least_one_rd(cc1, cc2)) {
- /* UFP attached */
- new_cc_state = PD_CC_UFP_ATTACHED;
- } else if (cc_is_audio_acc(cc1, cc2)) {
- /* AUDIO Accessory not supported. Just ignore */
- new_cc_state = PD_CC_UFP_AUDIO_ACC;
- } else {
- /* No UFP */
- if (drp_state[port] == PD_DRP_FORCE_SOURCE)
- set_state_tc(port, TC_UNATTACHED_SRC);
- else
- set_state_tc(port, TC_UNATTACHED_SNK);
- return;
- }
-
- /* Debounce the cc state */
- if (new_cc_state != tc[port].cc_state) {
- pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, PD_T_CC_DEBOUNCE);
- tc[port].cc_state = new_cc_state;
- return;
- }
-
- /* Wait for CC debounce */
- if (!pd_timer_is_expired(port, TC_TIMER_CC_DEBOUNCE))
- return;
-
- /*
- * The port shall transition to Attached.SRC when VBUS is at vSafe0V
- * and the SRC.Rd state is detected on exactly one of the CC1 or CC2
- * pins for at least tCCDebounce.
- *
- * If the port supports Debug Accessory Mode, it shall transition to
- * UnorientedDebugAccessory.SRC when VBUS is at vSafe0V and the SRC.Rd
- * state is detected on both the CC1 and CC2 pins for at least
- * tCCDebounce.
- */
- if (pd_check_vbus_level(port, VBUS_SAFE0V)) {
- if (new_cc_state == PD_CC_UFP_ATTACHED) {
- set_state_tc(port, TC_ATTACHED_SRC);
- return;
- } else if (new_cc_state == PD_CC_UFP_DEBUG_ACC) {
- CPRINTS("C%d: Debug accessory detected", port);
- TC_SET_FLAG(port, TC_FLAGS_TS_DTS_PARTNER);
- set_state_tc(port, TC_ATTACHED_SRC);
- return;
- }
- }
-}
-
-static void tc_attach_wait_src_exit(const int port)
-{
- pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE);
-}
-
-/**
- * Attached.SRC, shared with UnorientedDebugAccessory.SRC
- */
-static void tc_attached_src_entry(const int port)
-{
- enum tcpc_cc_voltage_status cc1, cc2;
-
- print_current_state(port);
-
- pd_timer_disable(port, TC_TIMER_TIMEOUT);
-
- /*
- * Known state of attach is SRC. We need to apply this pull value
- * to make it set in hardware at the correct time but set the common
- * pull here.
- *
- * Both CC1 and CC2 pins shall be independently terminated to
- * pulled up through Rp.
- *
- * Set selected current limit in the hardware.
- */
- typec_select_pull(port, TYPEC_CC_RP);
- typec_set_source_current_limit(port, tc[port].select_current_limit_rp);
-
- if (IS_ENABLED(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);
-
- /* Enable VBUS */
- tc_src_power_on(port);
-
- /* Apply Rp */
- typec_update_cc(port);
-
- /*
- * Maintain VCONN supply state, whether ON or OFF, and
- * its data role / usb mux connections. Do not
- * re-enable AutoDischargeDisconnect until the swap is
- * completed and tc_pr_swap_complete is called.
- */
- } else {
- /*
- * Set up CC's, Vconn, and ADD before Vbus, as per
- * Figure 4-24. DRP Initialization and Connection
- * Detection in TCPCI r2 v1.2 specification.
- */
-
- /* Get connector orientation */
- tcpm_get_cc(port, &cc1, &cc2);
- tc[port].polarity = get_src_polarity(cc1, cc2);
- pd_set_polarity(port, tc[port].polarity);
-
- /* Attached.SRC - enable AutoDischargeDisconnect */
- tcpm_enable_auto_discharge_disconnect(port, 1);
-
- /* Apply Rp */
- typec_update_cc(port);
-
- /*
- * 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
- *
- * UnorientedDebugAccessory.SRC shall not drive Vconn
- */
- if (IS_ENABLED(CONFIG_USBC_VCONN) &&
- !TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER))
- set_vconn(port, 1);
-
- /* Enable VBUS */
- if (tc_src_power_on(port)) {
- /* Stop sourcing Vconn if Vbus failed */
- if (IS_ENABLED(CONFIG_USBC_VCONN))
- set_vconn(port, 0);
-
- if (IS_ENABLED(CONFIG_USBC_SS_MUX))
- usb_mux_set(port,
- USB_PD_MUX_NONE,
- USB_SWITCH_DISCONNECT,
- tc[port].polarity);
- }
-
- tc_enable_pd(port, 0);
- pd_timer_enable(port, TC_TIMER_TIMEOUT,
- MAX(PD_POWER_SUPPLY_TURN_ON_DELAY,
- PD_T_VCONN_STABLE));
- }
- } else {
- /*
- * Set up CC's, Vconn, and ADD before Vbus, as per
- * Figure 4-24. DRP Initialization and Connection
- * Detection in TCPCI r2 v1.2 specification.
- */
-
- /* Get connector orientation */
- tcpm_get_cc(port, &cc1, &cc2);
- tc[port].polarity = get_src_polarity(cc1, cc2);
- pd_set_polarity(port, tc[port].polarity);
-
- /* Attached.SRC - enable AutoDischargeDisconnect */
- tcpm_enable_auto_discharge_disconnect(port, 1);
-
- /* Apply Rp */
- typec_update_cc(port);
-
- /*
- * 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
- *
- * UnorientedDebugAccessory.SRC shall not drive Vconn
- */
- if (IS_ENABLED(CONFIG_USBC_VCONN) &&
- !TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER))
- set_vconn(port, 1);
-
- /* Enable VBUS */
- if (tc_src_power_on(port)) {
- /* Stop sourcing Vconn if Vbus failed */
- if (IS_ENABLED(CONFIG_USBC_VCONN))
- set_vconn(port, 0);
-
- if (IS_ENABLED(CONFIG_USBC_SS_MUX))
- usb_mux_set(port, USB_PD_MUX_NONE,
- USB_SWITCH_DISCONNECT, tc[port].polarity);
- }
- }
-
- /* Inform PPC and OCP module that a sink is connected. */
- tc_set_partner_role(port, PPC_DEV_SNK);
-
- /* Initialize type-C supplier to seed the charge manger */
- if (IS_ENABLED(CONFIG_CHARGE_MANAGER))
- typec_set_input_current_limit(port, 0, 0);
-
- /*
- * Only notify if we're not performing a power role swap. During a
- * power role swap, the port partner is not disconnecting/connecting.
- */
- if (!TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) {
- hook_notify(HOOK_USB_PD_CONNECT);
- }
-
- if (TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) {
- tcpm_debug_accessory(port, 1);
- set_ccd_mode(port, 1);
- }
-
- /*
- * Some TCPCs require time to correctly return CC status after
- * changing the ROLE_CONTROL register. Due to that, we have to ignore
- * CC_NONE state until PD_T_SRC_DISCONNECT delay has elapsed.
- * From the "Universal Serial Bus Type-C Cable and Connector
- * Specification" Release 2.0 paragraph 4.5.2.2.9.2:
- * The Source shall detect the SRC.Open state within tSRCDisconnect,
- * but should detect it as quickly as possible
- */
- pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, PD_T_SRC_DISCONNECT);
-}
-
-static void tc_attached_src_run(const int port)
-{
- enum tcpc_cc_voltage_status cc1, cc2;
-
- /* Check for connection */
- tcpm_get_cc(port, &cc1, &cc2);
-
- if (polarity_rm_dts(tc[port].polarity))
- cc1 = cc2;
-
- if (cc1 == TYPEC_CC_VOLT_OPEN)
- tc[port].cc_state = PD_CC_NONE;
- else
- tc[port].cc_state = PD_CC_UFP_ATTACHED;
-
- /*
- * When the SRC.Open state is detected on the monitored CC pin, a DRP
- * shall transition to Unattached.SNK unless it strongly prefers the
- * Source role. In that case, it shall transition to TryWait.SNK.
- * This transition to TryWait.SNK is needed so that two devices that
- * both prefer the Source role do not loop endlessly between Source
- * and Sink. In other words, a DRP that would enter Try.SRC from
- * AttachWait.SNK shall enter TryWait.SNK for a Sink detach from
- * Attached.SRC.
- */
- if (tc[port].cc_state == PD_CC_NONE &&
- pd_timer_is_expired(port, TC_TIMER_CC_DEBOUNCE)) {
- bool tryWait;
- enum usb_tc_state new_tc_state = TC_UNATTACHED_SNK;
-
- if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC))
- tryWait = is_try_src_enabled(port) &&
- !TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER);
-
- if (drp_state[port] == PD_DRP_FORCE_SOURCE)
- new_tc_state = TC_UNATTACHED_SRC;
- else if(IS_ENABLED(CONFIG_USB_PD_TRY_SRC))
- new_tc_state = tryWait ?
- TC_TRY_WAIT_SNK : TC_UNATTACHED_SNK;
-
- set_state_tc(port, new_tc_state);
- return;
- }
-
-#ifdef CONFIG_USB_PE_SM
- /*
- * Enable PD communications after power supply has fully
- * turned on
- */
- if (pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) {
- tc_enable_pd(port, 1);
- pd_timer_disable(port, TC_TIMER_TIMEOUT);
- }
-
- if (!tc_get_pd_enabled(port))
- return;
-
- /*
- * Handle Hard Reset from Policy Engine
- */
- if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED)) {
- /* Ignoring Hard Resets while the power supply is resetting.*/
- if (!pd_timer_is_disabled(port, TC_TIMER_TIMEOUT) &&
- !pd_timer_is_expired(port, TC_TIMER_TIMEOUT))
- return;
-
- if (tc_perform_src_hard_reset(port))
- TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED);
-
- return;
- }
-
- /*
- * PD swap commands
- */
- if (tc_get_pd_enabled(port) && prl_is_running(port)) {
- /*
- * Power Role Swap Request
- */
- if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP)) {
- /* Clear TC_FLAGS_REQUEST_PR_SWAP on exit */
- return set_state_tc(port, TC_ATTACHED_SNK);
- }
-
- /*
- * Data Role Swap Request
- */
- if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP)) {
- TC_CLR_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP);
-
- /* Perform Data Role Swap */
- tc_set_data_role(port,
- tc[port].data_role == PD_ROLE_DFP ?
- PD_ROLE_UFP : PD_ROLE_DFP);
- }
-
- /*
- * Vconn Swap Request
- * UnorientedDebugAccessory.SRC shall not drive Vconn
- */
- if (IS_ENABLED(CONFIG_USBC_VCONN) &&
- !TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) {
- /*
- * VCONN Swap Request
- */
- if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON)) {
- TC_CLR_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON);
- set_vconn(port, 1);
- pe_vconn_swap_complete(port);
- } else if (TC_CHK_FLAG(port,
- TC_FLAGS_REQUEST_VC_SWAP_OFF)) {
- TC_CLR_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_OFF);
- set_vconn(port, 0);
- pe_vconn_swap_complete(port);
- }
- }
-
- /*
- * A DRP that supports Charge-Through VCONN-Powered USB Devices
- * shall transition to CTUnattached.SNK if the connected device
- * identifies itself as a Charge-Through VCONN-Powered USB
- * Device in its Discover Identity Command response.
- */
-
- /*
- * A DRP that supports Charge-Through VCONN-Powered USB Devices
- * shall transition to CTUnattached.SNK if the connected device
- * identifies itself as a Charge-Through VCONN-Powered USB
- * Device in its Discover Identity Command response.
- *
- * If it detects that it is connected to a VCONN-Powered USB
- * Device, the port may remove VBUS and discharge it to
- * vSafe0V, while continuing to remain in this state with VCONN
- * applied.
- */
- if (!TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER) &&
- TC_CHK_FLAG(port, TC_FLAGS_CTVPD_DETECTED)) {
-
- set_state_tc(port, TC_CT_UNATTACHED_SNK);
- }
- }
-#endif
-
- if (TC_CHK_FLAG(port, TC_FLAGS_UPDATE_CURRENT)) {
- TC_CLR_FLAG(port, TC_FLAGS_UPDATE_CURRENT);
- typec_set_source_current_limit(port,
- tc[port].select_current_limit_rp);
- pd_update_contract(port);
-
- /* Update Rp if no contract is present */
- if (!IS_ENABLED(CONFIG_USB_PE_SM) ||
- !pe_is_explicit_contract(port))
- typec_update_cc(port);
- }
-}
-
-static void tc_attached_src_exit(const int port)
-{
- /*
- * A port shall cease to supply VBUS within tVBUSOFF of exiting
- * Attached.SRC.
- */
- tc_src_power_off(port);
-
- if (!TC_CHK_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP)) {
- /* Attached.SRC exit - disable AutoDischargeDisconnect */
- tcpm_enable_auto_discharge_disconnect(port, 0);
-
- /*
- * Disable VCONN if not power role swapping and
- * a CTVPD was not detected
- */
- if (TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON) &&
- !TC_CHK_FLAG(port, TC_FLAGS_CTVPD_DETECTED))
- set_vconn(port, 0);
- }
-
- /* Clear CTVPD detected after checking for Vconn */
- TC_CLR_FLAG(port, TC_FLAGS_CTVPD_DETECTED);
-
- /* Clear PR swap flag after checking for Vconn */
- TC_CLR_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP);
-
- if (TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER))
- tcpm_debug_detach(port);
-
- pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE);
- pd_timer_disable(port, TC_TIMER_TIMEOUT);
-}
-
-static __maybe_unused void check_drp_connection(const int port)
-{
- enum pd_drp_next_states next_state;
- enum tcpc_cc_voltage_status cc1, cc2;
-
- TC_CLR_FLAG(port, TC_FLAGS_CHECK_CONNECTION);
-
- /* Check for connection */
- tcpm_get_cc(port, &cc1, &cc2);
-
- tc[port].drp_sink_time = get_time().val;
-
- /* Get the next toggle state */
- next_state = drp_auto_toggle_next_state(&tc[port].drp_sink_time,
- tc[port].power_role, drp_state[port], cc1, cc2,
- tcpm_auto_toggle_supported(port));
-
- if (next_state == DRP_TC_DEFAULT)
- next_state = (PD_ROLE_DEFAULT(port) == PD_ROLE_SOURCE)
- ? DRP_TC_UNATTACHED_SRC
- : DRP_TC_UNATTACHED_SNK;
-
- switch (next_state) {
- case DRP_TC_UNATTACHED_SNK:
- set_state_tc(port, TC_UNATTACHED_SNK);
- break;
- case DRP_TC_ATTACHED_WAIT_SNK:
- set_state_tc(port, TC_ATTACH_WAIT_SNK);
- break;
- case DRP_TC_UNATTACHED_SRC:
- set_state_tc(port, TC_UNATTACHED_SRC);
- break;
- case DRP_TC_ATTACHED_WAIT_SRC:
- set_state_tc(port, TC_ATTACH_WAIT_SRC);
- break;
-
-#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
- case DRP_TC_DRP_AUTO_TOGGLE:
- set_state_tc(port, TC_DRP_AUTO_TOGGLE);
- break;
-#endif
-
- default:
- CPRINTS("C%d: Error: DRP next state %d", port, next_state);
- break;
- }
-}
-
-/**
- * DrpAutoToggle
- */
-__maybe_unused static void tc_drp_auto_toggle_entry(const int port)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE))
- assert(0);
-
- print_current_state(port);
-
- /*
- * We need to ensure that we are waiting in the previous Rd or Rp state
- * for the minimum of DRP SNK or SRC so the first toggle cause by
- * transition into auto toggle doesn't violate spec timing.
- */
- pd_timer_enable(port, TC_TIMER_TIMEOUT,
- MAX(PD_T_DRP_SNK, PD_T_DRP_SRC));
-}
-
-__maybe_unused static void tc_drp_auto_toggle_run(const int port)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE))
- assert(0);
-
- /*
- * A timer is running, but if a connection comes in while waiting
- * then allow that to take higher priority.
- */
- if (TC_CHK_FLAG(port, TC_FLAGS_CHECK_CONNECTION))
- check_drp_connection(port);
-
- else if (!pd_timer_is_disabled(port, TC_TIMER_TIMEOUT)) {
- if (!pd_timer_is_expired(port, TC_TIMER_TIMEOUT))
- return;
-
- pd_timer_disable(port, TC_TIMER_TIMEOUT);
- tcpm_enable_drp_toggle(port);
-
- if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER)) {
- set_state_tc(port, TC_LOW_POWER_MODE);
- }
- }
-}
-
-__maybe_unused static void tc_drp_auto_toggle_exit(const int port)
-{
- pd_timer_disable(port, TC_TIMER_TIMEOUT);
-}
-
-__maybe_unused static void tc_low_power_mode_entry(const int port)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER))
- assert(0);
-
- print_current_state(port);
- pd_timer_enable(port, TC_TIMER_LOW_POWER_TIME, PD_LPM_DEBOUNCE_US);
-}
-
-__maybe_unused static void tc_low_power_mode_run(const int port)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER))
- assert(0);
-
- if (TC_CHK_FLAG(port, TC_FLAGS_CHECK_CONNECTION)) {
- tc_start_event_loop(port);
- if (pd_timer_is_disabled(port, TC_TIMER_LOW_POWER_EXIT_TIME)) {
- pd_timer_enable(port, TC_TIMER_LOW_POWER_EXIT_TIME,
- PD_LPM_EXIT_DEBOUNCE_US);
- } else if (pd_timer_is_expired(port,
- TC_TIMER_LOW_POWER_EXIT_TIME)) {
- CPRINTS("C%d: Exit Low Power Mode", port);
- check_drp_connection(port);
- }
- return;
- }
-
- if (tc[port].tasks_preventing_lpm)
- pd_timer_enable(port, TC_TIMER_LOW_POWER_TIME,
- PD_LPM_DEBOUNCE_US);
-
- if (pd_timer_is_expired(port, TC_TIMER_LOW_POWER_TIME)) {
- CPRINTS("C%d: TCPC Enter Low Power Mode", port);
- TC_SET_FLAG(port, TC_FLAGS_LPM_ENGAGED);
- TC_SET_FLAG(port, TC_FLAGS_LPM_TRANSITION);
- tcpm_enter_low_power_mode(port);
- TC_CLR_FLAG(port, TC_FLAGS_LPM_TRANSITION);
- tc_pause_event_loop(port);
-
- pd_timer_disable(port, TC_TIMER_LOW_POWER_EXIT_TIME);
- }
-}
-
-__maybe_unused static void tc_low_power_mode_exit(const int port)
-{
- pd_timer_disable(port, TC_TIMER_LOW_POWER_TIME);
- pd_timer_disable(port, TC_TIMER_LOW_POWER_EXIT_TIME);
-}
-
-/**
- * Try.SRC
- *
- * Super State Entry Actions:
- * Vconn Off
- * Place Rp on CC
- * Set power role to SOURCE
- */
-#ifdef CONFIG_USB_PD_TRY_SRC
-static void tc_try_src_entry(const int port)
-{
- print_current_state(port);
-
- tc[port].cc_state = PD_CC_UNSET;
- pd_timer_enable(port, TC_TIMER_TRY_WAIT_DEBOUNCE, PD_T_DRP_TRY);
- pd_timer_enable(port, TC_TIMER_TIMEOUT, PD_T_TRY_TIMEOUT);
-
- /*
- * We are a SNK but would prefer to be a SRC. Set the pull to
- * indicate we want to be a SRC and looking for a SNK.
- *
- * Both CC1 and CC2 pins shall be independently terminated to
- * ground through Rp.
- */
- typec_select_pull(port, TYPEC_CC_RP);
-
- typec_select_src_current_limit_rp(port,
- typec_get_default_current_limit_rp(port));
-
- /* Apply Rp */
- typec_update_cc(port);
-}
-
-static void tc_try_src_run(const int port)
-{
- enum tcpc_cc_voltage_status cc1, cc2;
- enum pd_cc_states new_cc_state;
-
- /* Check for connection */
- tcpm_get_cc(port, &cc1, &cc2);
-
- if ((cc1 == TYPEC_CC_VOLT_RD && cc2 != TYPEC_CC_VOLT_RD) ||
- (cc1 != TYPEC_CC_VOLT_RD && cc2 == TYPEC_CC_VOLT_RD))
- new_cc_state = PD_CC_UFP_ATTACHED;
- else
- new_cc_state = PD_CC_NONE;
-
- /* Debounce the cc state */
- if (new_cc_state != tc[port].cc_state) {
- tc[port].cc_state = new_cc_state;
- pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, PD_T_CC_DEBOUNCE);
- }
-
- /*
- * The port shall transition to Attached.SRC when the SRC.Rd state is
- * detected on exactly one of the CC1 or CC2 pins for at least
- * tTryCCDebounce.
- */
- if (new_cc_state == PD_CC_UFP_ATTACHED &&
- pd_timer_is_expired(port, TC_TIMER_CC_DEBOUNCE))
- set_state_tc(port, TC_ATTACHED_SRC);
-
- /*
- * The port shall transition to TryWait.SNK after tDRPTry and the
- * SRC.Rd state has not been detected and VBUS is within vSafe0V,
- * or after tTryTimeout and the SRC.Rd state has not been detected.
- */
- if (new_cc_state == PD_CC_NONE) {
- if ((pd_timer_is_expired(port, TC_TIMER_TRY_WAIT_DEBOUNCE) &&
- pd_check_vbus_level(port, VBUS_SAFE0V)) ||
- pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) {
- set_state_tc(port, TC_TRY_WAIT_SNK);
- }
- }
-}
-
-static void tc_try_src_exit(const int port)
-{
- pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE);
- pd_timer_disable(port, TC_TIMER_TIMEOUT);
- pd_timer_disable(port, TC_TIMER_TRY_WAIT_DEBOUNCE);
-}
-
-/**
- * TryWait.SNK
- *
- * Super State Entry Actions:
- * Vconn Off
- * Place Rd on CC
- * Set power role to SINK
- */
-static void tc_try_wait_snk_entry(const int port)
-{
- print_current_state(port);
-
- tc_enable_pd(port, 0);
- tc[port].cc_state = PD_CC_UNSET;
- pd_timer_enable(port, TC_TIMER_TRY_WAIT_DEBOUNCE, PD_T_CC_DEBOUNCE);
-
- /*
- * We were a SNK, tried to be a SRC and it didn't work out. Try to
- * go back to being a SNK. Set the pull to indicate we want to be
- * a SNK and looking for a SRC.
- *
- * Both CC1 and CC2 pins shall be independently terminated to
- * ground through Rd.
- */
- typec_select_pull(port, TYPEC_CC_RD);
-
- /* Apply Rd */
- typec_update_cc(port);
-}
-
-static void tc_try_wait_snk_run(const int port)
-{
- enum tcpc_cc_voltage_status cc1, cc2;
- enum pd_cc_states new_cc_state;
-
- /* Check for connection */
- tcpm_get_cc(port, &cc1, &cc2);
-
- /* We only care about CCs being open */
- if (cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_OPEN)
- new_cc_state = PD_CC_NONE;
- else
- new_cc_state = PD_CC_UNSET;
-
- /* Debounce the cc state */
- if (new_cc_state != tc[port].cc_state) {
- tc[port].cc_state = new_cc_state;
- pd_timer_enable(port, TC_TIMER_PD_DEBOUNCE, PD_T_PD_DEBOUNCE);
- }
-
- /*
- * The port shall transition to Unattached.SNK when the state of both
- * of the CC1 and CC2 pins is SNK.Open for at least tPDDebounce.
- */
- if (new_cc_state == PD_CC_NONE &&
- pd_timer_is_expired(port, TC_TIMER_PD_DEBOUNCE)) {
- set_state_tc(port, TC_UNATTACHED_SNK);
- return;
- }
-
- /*
- * The port shall transition to Attached.SNK after tCCDebounce if or
- * when VBUS is detected.
- */
- if (pd_timer_is_expired(port, TC_TIMER_TRY_WAIT_DEBOUNCE) &&
- pd_is_vbus_present(port))
- set_state_tc(port, TC_ATTACHED_SNK);
-}
-
-static void tc_try_wait_snk_exit(const int port)
-{
- pd_timer_disable(port, TC_TIMER_PD_DEBOUNCE);
- pd_timer_disable(port, TC_TIMER_TRY_WAIT_DEBOUNCE);
-}
-#endif
-
-/*
- * CTUnattached.SNK
- */
-__maybe_unused static void tc_ct_unattached_snk_entry(int port)
-{
- if (!IS_ENABLED(CONFIG_USB_PE_SM))
- assert(0);
-
- print_current_state(port);
-
- /*
- * Both CC1 and CC2 pins shall be independently terminated to
- * ground through Rd.
- */
- typec_select_pull(port, TYPEC_CC_RD);
- typec_update_cc(port);
-
- tc[port].cc_state = PD_CC_UNSET;
-
- /* 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);
-
- /*
- * The policy engine is in the disabled state. Disable PD and
- * re-enable it
- */
- tc_enable_pd(port, 0);
-
- pd_timer_enable(port, TC_TIMER_TIMEOUT, PD_POWER_SUPPLY_TURN_ON_DELAY);
-}
-
-__maybe_unused static void tc_ct_unattached_snk_run(int port)
-{
- enum tcpc_cc_voltage_status cc1;
- enum tcpc_cc_voltage_status cc2;
- enum pd_cc_states new_cc_state;
-
- if (!IS_ENABLED(CONFIG_USB_PE_SM))
- assert(0);
-
- if (!pd_timer_is_disabled(port, TC_TIMER_TIMEOUT)) {
- if (pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) {
- tc_enable_pd(port, 1);
- pd_timer_disable(port, TC_TIMER_TIMEOUT);
- } else {
- return;
- }
- }
-
- /* Wait until Protocol Layer is ready */
- if (!prl_is_running(port))
- return;
-
- /*
- * Hard Reset is sent when the PE layer is disabled due to a
- * CTVPD connection.
- */
- if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED)) {
- TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED);
- /* Nothing to do. Just signal hard reset completion */
- pe_ps_reset_complete(port);
- }
-
- /* Check for connection */
- tcpm_get_cc(port, &cc1, &cc2);
-
- /* We only care about CCs being open */
- if (cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_OPEN)
- new_cc_state = PD_CC_NONE;
- else
- new_cc_state = PD_CC_UNSET;
-
- /* Debounce the cc state */
- if (new_cc_state != tc[port].cc_state) {
- tc[port].cc_state = new_cc_state;
- pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, PD_T_VPDDETACH);
- }
-
- /*
- * The port shall transition to Unattached.SNK if the state of
- * the CC pin is SNK.Open for tVPDDetach after VBUS is vSafe0V.
- */
- else if (pd_timer_is_expired(port, TC_TIMER_CC_DEBOUNCE)) {
- if (new_cc_state == PD_CC_NONE &&
- pd_check_vbus_level(port, VBUS_SAFE0V)) {
- set_state_tc(port, TC_UNATTACHED_SNK);
- return;
- }
- }
-
- /*
- * The port shall transition to CTAttached.SNK when VBUS is detected.
- */
- if (pd_is_vbus_present(port))
- set_state_tc(port, TC_CT_ATTACHED_SNK);
-}
-
-__maybe_unused static void tc_ct_unattached_snk_exit(int port)
-{
- pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE);
- pd_timer_disable(port, TC_TIMER_TIMEOUT);
-}
-
-/**
- * CTAttached.SNK
- */
-__maybe_unused static void tc_ct_attached_snk_entry(int port)
-{
- if (!IS_ENABLED(CONFIG_USB_PE_SM))
- assert(0);
-
- print_current_state(port);
-
- /* The port shall reject a VCONN swap request. */
- TC_SET_FLAG(port, TC_FLAGS_REJECT_VCONN_SWAP);
-}
-
-__maybe_unused static void tc_ct_attached_snk_run(int port)
-{
- if (!IS_ENABLED(CONFIG_USB_PE_SM))
- assert(0);
-
- /*
- * Hard Reset is sent when the PE layer is disabled due to a
- * CTVPD connection.
- */
- if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED)) {
- TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED);
- /* Nothing to do. Just signal hard reset completion */
- pe_ps_reset_complete(port);
- }
-
- /*
- * A port that is not in the process of a USB PD Hard Reset shall
- * transition to CTUnattached.SNK within tSinkDisconnect when VBUS
- * falls below vSinkDisconnect
- */
- if (pd_check_vbus_level(port, VBUS_REMOVED)) {
- set_state_tc(port, TC_CT_UNATTACHED_SNK);
- return;
- }
-
- /*
- * The port shall operate in one of the Sink Power Sub-States
- * and remain within the Sink Power Sub-States, until either VBUS is
- * removed or a USB PD contract is established with the source.
- */
- if (!pe_is_explicit_contract(port))
- sink_power_sub_states(port);
-}
-
-__maybe_unused static void tc_ct_attached_snk_exit(int port)
-{
- if (!IS_ENABLED(CONFIG_USB_PE_SM))
- assert(0);
-
- /* Stop drawing power */
- sink_stop_drawing_current(port);
-
- TC_CLR_FLAG(port, TC_FLAGS_REJECT_VCONN_SWAP);
-}
-
-/**
- * Super State CC_RD
- */
-static void tc_cc_rd_entry(const int port)
-{
- /* Disable VCONN */
- 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);
-}
-
-
-/**
- * Super State CC_RP
- */
-static void tc_cc_rp_entry(const int port)
-{
- /* Disable VCONN */
- if (IS_ENABLED(CONFIG_USBC_VCONN))
- set_vconn(port, 0);
-
- /* Set power role to source */
- tc_set_power_role(port, PD_ROLE_SOURCE);
- tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role);
-}
-
-/**
- * Super State CC_OPEN
- */
-static void tc_cc_open_entry(const int port)
-{
- /* Ensure we are not sourcing Vbus */
- tc_src_power_off(port);
-
- /* Disable VCONN */
- set_vconn(port, 0);
-
- /*
- * Ensure we disable discharging before setting CC lines to open.
- * If we were sourcing above, then we already drained Vbus. If partner
- * is sourcing Vbus they will drain Vbus if they are PD-capable. This
- * should only be done if a battery is present as a batteryless
- * device will brown out when AutoDischargeDisconnect is disabled and
- * we do not want this to happen until the set_cc open/open to make
- * sure the TCPC has managed its internal states for disconnecting
- * the only source of power it has.
- */
- if (battery_is_present())
- tcpm_enable_auto_discharge_disconnect(port, 0);
-
- /*
- * We may brown out after applying CC open, so flush console first.
- * Console flush can take a long time, so if we aren't in danger of
- * browning out, don't do it so we can meet certain compliance timing
- * requirements.
- */
- CPRINTS("C%d: Applying CC Open!", port);
- if (!battery_is_present())
- cflush();
-
- /* Remove terminations from CC */
- typec_select_pull(port, TYPEC_CC_OPEN);
- typec_update_cc(port);
-
- tc_set_partner_role(port, PPC_DEV_DISCONNECTED);
- tc_detached(port);
-}
-
-void tc_set_debug_level(enum debug_level debug_level)
-{
-#ifndef CONFIG_USB_PD_DEBUG_LEVEL
- tc_debug_level = debug_level;
-#endif
-}
-
-void tc_usb_firmware_fw_update_limited_run(int port)
-{
- TC_SET_FLAG(port, TC_FLAGS_USB_RETIMER_FW_UPDATE_LTD_RUN);
- task_wake(PD_PORT_TO_TASK_ID(port));
-}
-
-void tc_usb_firmware_fw_update_run(int port)
-{
- TC_SET_FLAG(port, TC_FLAGS_USB_RETIMER_FW_UPDATE_RUN);
- task_wake(PD_PORT_TO_TASK_ID(port));
-}
-
-void tc_run(const int port)
-{
- /*
- * If pd_set_suspend set TC_FLAGS_REQUEST_SUSPEND, go directly to
- * TC_DISABLED.
- */
- if (get_state_tc(port) != TC_DISABLED
- && TC_CHK_FLAG(port, TC_FLAGS_REQUEST_SUSPEND)) {
- /* Invalidate a contract, if there is one */
- if (IS_ENABLED(CONFIG_USB_PE_SM))
- pe_invalidate_explicit_contract(port);
-
- set_state_tc(port, TC_DISABLED);
- }
-
- /* If error recovery has been requested, transition now */
- if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_ERROR_RECOVERY)) {
- if (IS_ENABLED(CONFIG_USB_PE_SM))
- pe_invalidate_explicit_contract(port);
- set_state_tc(port, TC_ERROR_RECOVERY);
- }
-
- if (IS_ENABLED(CONFIG_USBC_RETIMER_FW_UPDATE)) {
- if (TC_CHK_FLAG(port, TC_FLAGS_USB_RETIMER_FW_UPDATE_RUN)) {
- TC_CLR_FLAG(port, TC_FLAGS_USB_RETIMER_FW_UPDATE_RUN);
- usb_retimer_fw_update_process_op_cb(port);
- }
- }
-
- run_state(port, &tc[port].ctx);
-}
-
-static void pd_chipset_resume(void)
-{
- int i;
-
- for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) {
- if(IS_ENABLED(CONFIG_USB_PE_SM))
- pd_resume_check_pr_swap_needed(i);
-
- pd_set_dual_role_and_event(i,
- PD_DRP_TOGGLE_ON,
- PD_EVENT_UPDATE_DUAL_ROLE
- | PD_EVENT_POWER_STATE_CHANGE);
- }
-
- 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_and_event(i,
- pd_get_drp_state_in_suspend(),
- PD_EVENT_UPDATE_DUAL_ROLE
- | PD_EVENT_POWER_STATE_CHANGE);
- }
-
- CPRINTS("PD:S0->S3");
-}
-DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, pd_chipset_suspend, HOOK_PRIO_DEFAULT);
-
-static void pd_chipset_reset(void)
-{
- int i;
-
- if (!IS_ENABLED(CONFIG_USB_PE_SM))
- return;
-
- for (i = 0; i < board_get_usb_pd_port_count(); i++) {
- enum tcpci_msg_type tx;
-
- /* Do not notify the AP of irrelevant past Hard Resets. */
- pd_clear_events(i, PD_STATUS_EVENT_HARD_RESET);
-
- /*
- * Re-set events for SOP and SOP' discovery complete so the
- * kernel knows to consume discovery information for them.
- */
- for (tx = TCPCI_MSG_SOP; tx <= TCPCI_MSG_SOP_PRIME; tx++) {
- if (pd_get_identity_discovery(i, tx) != PD_DISC_NEEDED
- && pd_get_svids_discovery(i, tx) != PD_DISC_NEEDED
- && pd_get_modes_discovery(i, tx) != PD_DISC_NEEDED)
- pd_notify_event(i, tx == TCPCI_MSG_SOP ?
- PD_STATUS_EVENT_SOP_DISC_DONE :
- PD_STATUS_EVENT_SOP_PRIME_DISC_DONE);
- }
-
- /* Exit mode so AP can enter mode again after reset */
- if (IS_ENABLED(CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY))
- dpm_set_mode_exit_request(i);
- }
-}
-DECLARE_HOOK(HOOK_CHIPSET_RESET, pd_chipset_reset, HOOK_PRIO_DEFAULT);
-
-static void pd_chipset_startup(void)
-{
- int i;
-
- for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) {
- TC_SET_FLAG(i, TC_FLAGS_UPDATE_USB_MUX);
- pd_set_dual_role_and_event(i,
- pd_get_drp_state_in_suspend(),
- PD_EVENT_UPDATE_DUAL_ROLE
- | PD_EVENT_POWER_STATE_CHANGE);
- /*
- * Request port discovery to restore any
- * alt modes.
- * TODO(b/158042116): Do not start port discovery if there
- * is an existing connection.
- */
- if (IS_ENABLED(CONFIG_USB_PE_SM))
- pd_dpm_request(i, DPM_REQUEST_PORT_DISCOVERY);
- }
-
- 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++) {
- TC_SET_FLAG(i, TC_FLAGS_UPDATE_USB_MUX);
- pd_set_dual_role_and_event(i,
- PD_DRP_FORCE_SINK,
- PD_EVENT_UPDATE_DUAL_ROLE
- | PD_EVENT_POWER_STATE_CHANGE);
- }
-
- CPRINTS("PD:S3->S5");
-}
-DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pd_chipset_shutdown, HOOK_PRIO_DEFAULT);
-
-static void pd_set_power_change(void)
-{
- int i;
-
- for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) {
- task_set_event(PD_PORT_TO_TASK_ID(i),
- PD_EVENT_POWER_STATE_CHANGE);
- }
-}
-DECLARE_DEFERRED(pd_set_power_change);
-
-static void pd_chipset_hard_off(void)
-{
- /*
- * Wait 1 second to check our Vconn sourcing status, as the power rails
- * which were supporting it may take some time to change after entering
- * G3.
- */
- hook_call_deferred(&pd_set_power_change_data, 1 * SECOND);
-}
-DECLARE_HOOK(HOOK_CHIPSET_HARD_OFF, pd_chipset_hard_off, HOOK_PRIO_DEFAULT);
-
-/*
- * Type-C State Hierarchy (Sub-States are listed inside the boxes)
- *
- * |TC_CC_RD --------------| |TC_CC_RP ------------------------|
- * | | | |
- * | TC_UNATTACHED_SNK | | TC_UNATTACHED_SRC |
- * | TC_ATTACH_WAIT_SNK | | TC_ATTACH_WAIT_SRC |
- * | TC_TRY_WAIT_SNK | | TC_TRY_SRC |
- * |-----------------------| |---------------------------------|
- *
- * |TC_CC_OPEN -----------|
- * | |
- * | TC_DISABLED |
- * | TC_ERROR_RECOVERY |
- * |----------------------|
- *
- * TC_ATTACHED_SNK TC_ATTACHED_SRC TC_DRP_AUTO_TOGGLE TC_LOW_POWER_MODE
- *
- */
-static __const_data const struct usb_state tc_states[] = {
- /* Super States */
- [TC_CC_OPEN] = {
- .entry = tc_cc_open_entry,
- },
- [TC_CC_RD] = {
- .entry = tc_cc_rd_entry,
- },
- [TC_CC_RP] = {
- .entry = tc_cc_rp_entry,
- },
- /* Normal States */
- [TC_DISABLED] = {
- .entry = tc_disabled_entry,
- .run = tc_disabled_run,
- .exit = tc_disabled_exit,
- .parent = &tc_states[TC_CC_OPEN],
- },
- [TC_ERROR_RECOVERY] = {
- .entry = tc_error_recovery_entry,
- .run = tc_error_recovery_run,
- .exit = tc_error_recovery_exit,
- .parent = &tc_states[TC_CC_OPEN],
- },
- [TC_UNATTACHED_SNK] = {
- .entry = tc_unattached_snk_entry,
- .run = tc_unattached_snk_run,
- .exit = tc_unattached_snk_exit,
- .parent = &tc_states[TC_CC_RD],
- },
- [TC_ATTACH_WAIT_SNK] = {
- .entry = tc_attach_wait_snk_entry,
- .run = tc_attach_wait_snk_run,
- .exit = tc_attach_wait_snk_exit,
- .parent = &tc_states[TC_CC_RD],
- },
- [TC_ATTACHED_SNK] = {
- .entry = tc_attached_snk_entry,
- .run = tc_attached_snk_run,
- .exit = tc_attached_snk_exit,
- },
- [TC_UNATTACHED_SRC] = {
- .entry = tc_unattached_src_entry,
- .run = tc_unattached_src_run,
- .exit = tc_unattached_src_exit,
- .parent = &tc_states[TC_CC_RP],
- },
- [TC_ATTACH_WAIT_SRC] = {
- .entry = tc_attach_wait_src_entry,
- .run = tc_attach_wait_src_run,
- .exit = tc_attach_wait_src_exit,
- .parent = &tc_states[TC_CC_RP],
- },
- [TC_ATTACHED_SRC] = {
- .entry = tc_attached_src_entry,
- .run = tc_attached_src_run,
- .exit = tc_attached_src_exit,
- },
-#ifdef CONFIG_USB_PD_TRY_SRC
- [TC_TRY_SRC] = {
- .entry = tc_try_src_entry,
- .run = tc_try_src_run,
- .exit = tc_try_src_exit,
- .parent = &tc_states[TC_CC_RP],
- },
- [TC_TRY_WAIT_SNK] = {
- .entry = tc_try_wait_snk_entry,
- .run = tc_try_wait_snk_run,
- .exit = tc_try_wait_snk_exit,
- .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,
- .exit = tc_drp_auto_toggle_exit,
- },
-#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,
- .run = tc_ct_unattached_snk_run,
- .exit = tc_ct_unattached_snk_exit,
- },
- [TC_CT_ATTACHED_SNK] = {
- .entry = tc_ct_attached_snk_entry,
- .run = tc_ct_attached_snk_run,
- .exit = tc_ct_attached_snk_exit,
- },
-#endif
-};
-
-#if defined(TEST_BUILD) && defined(USB_PD_DEBUG_LABELS)
-const struct test_sm_data test_tc_sm_data[] = {
- {
- .base = tc_states,
- .size = ARRAY_SIZE(tc_states),
- .names = tc_state_names,
- .names_size = ARRAY_SIZE(tc_state_names),
- },
-};
-const int test_tc_sm_data_size = ARRAY_SIZE(test_tc_sm_data);
-#endif