diff options
Diffstat (limited to 'common/usbc/usb_tc_drp_acc_trysrc_sm.c')
-rw-r--r-- | common/usbc/usb_tc_drp_acc_trysrc_sm.c | 3547 |
1 files changed, 0 insertions, 3547 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 6aa39b520b..0000000000 --- a/common/usbc/usb_tc_drp_acc_trysrc_sm.c +++ /dev/null @@ -1,3547 +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.h" -#include "usb_common.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pe_sm.h" -#include "usb_prl_sm.h" -#include "usb_sm.h" -#include "usb_tc_sm.h" -#include "usbc_ppc.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_HOOK, format, ## args) -#define CPRINTS(format, args...) cprints(CC_HOOK, format, ## args) -#else /* CONFIG_COMMON_RUNTIME */ -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* Type-C Layer Flags */ -/* Flag to note we are sourcing VCONN */ -#define TC_FLAGS_VCONN_ON BIT(0) -/* Flag to note port partner has Rp/Rp or Rd/Rd */ -#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 Low Power Mode is requested. Not currently used */ -#define TC_FLAGS_LPM_REQUESTED BIT(5) -/* Flag to note CVTPD has been detected */ -#define TC_FLAGS_CTVPD_DETECTED BIT(6) -/* Flag to note request to swap to VCONN on */ -#define TC_FLAGS_REQUEST_VC_SWAP_ON BIT(7) -/* Flag to note request to swap to VCONN off */ -#define TC_FLAGS_REQUEST_VC_SWAP_OFF BIT(8) -/* Flag to note request to swap VCONN is being rejected */ -#define TC_FLAGS_REJECT_VCONN_SWAP BIT(9) -/* Flag to note request to power role swap */ -#define TC_FLAGS_REQUEST_PR_SWAP BIT(10) -/* Flag to note request to data role swap */ -#define TC_FLAGS_REQUEST_DR_SWAP BIT(11) -/* Flag to note request to power off sink */ -#define TC_FLAGS_POWER_OFF_SNK BIT(12) -/* Flag to note port partner has external power */ -#define TC_FLAGS_PARTNER_EXTPOWER BIT(13) -/* Flag to note port partner is Dual Role Data */ -#define TC_FLAGS_PARTNER_DR_DATA BIT(14) -/* Flag to note port partner is Dual Role Power */ -#define TC_FLAGS_PARTNER_DR_POWER BIT(15) -/* Flag to note port partner is Power Delivery capable */ -#define TC_FLAGS_PARTNER_PD_CAPABLE BIT(16) -/* Flag to note hard reset has been triggered */ -#define TC_FLAGS_HARD_RESET BIT(17) -/* Flag to note port partner is USB comms capable */ -#define TC_FLAGS_PARTNER_USB_COMM BIT(18) -/* Flag to note we are currently performing PR Swap */ -#define TC_FLAGS_PR_SWAP_IN_PROGRESS BIT(19) -/* Flag to note we need to perform PR Swap */ -#define TC_FLAGS_DO_PR_SWAP BIT(20) -/* Flag to note we are performing Discover Identity */ -#define TC_FLAGS_DISC_IDENT_IN_PROGRESS BIT(21) -/* Flag to note we should wake from LPM */ -#define TC_FLAGS_WAKE_FROM_LPM BIT(22) -/* Flag to note a chipset power state has changed */ -#define TC_FLAGS_POWER_STATE_CHANGE BIT(23) -/* Flag to note the TCPM supports auto toggle */ -#define TC_FLAGS_AUTO_TOGGLE_SUPPORTED BIT(24) - -/* - * Clear all flags except TC_FLAGS_AUTO_TOGGLE_SUPPORTED, - * TC_FLAGS_LPM_REQUESTED, and TC_FLAGS_LPM_ENGAGED if - * they are set. - */ -#define CLR_ALL_BUT_LPM_FLAGS(port) (tc[port].flags &= \ - (TC_FLAGS_AUTO_TOGGLE_SUPPORTED | \ - TC_FLAGS_LPM_REQUESTED | \ - TC_FLAGS_LPM_ENGAGED)) - -/* 100 ms is enough time for any TCPC transaction to complete. */ -#define PD_LPM_DEBOUNCE_US (100 * MSEC) - -enum ps_reset_sequence { - PS_STATE0, - PS_STATE1, - PS_STATE2, - PS_STATE3 -}; - -/* List of all TypeC-level states */ -enum usb_tc_state { - /* Normal States */ - TC_DISABLED, - TC_ERROR_RECOVERY, - TC_UNATTACHED_SNK, - TC_ATTACH_WAIT_SNK, - TC_ATTACHED_SNK, - TC_UNORIENTED_DBG_ACC_SRC, - TC_DBG_ACC_SNK, - TC_UNATTACHED_SRC, - TC_ATTACH_WAIT_SRC, - TC_ATTACHED_SRC, - TC_TRY_SRC, - TC_TRY_WAIT_SNK, -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - TC_DRP_AUTO_TOGGLE, -#endif -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - TC_LOW_POWER_MODE, -#endif -#ifdef CONFIG_USB_PE_SM - TC_CT_UNATTACHED_SNK, - TC_CT_ATTACHED_SNK, -#endif - /* Super States */ - TC_CC_OPEN, - TC_CC_RD, - TC_CC_RP, - TC_UNATTACHED, -}; -/* Forward declare the full list of states. This is indexed by usb_tc_state */ -static const struct usb_state tc_states[]; - -#ifdef CONFIG_COMMON_RUNTIME -/* List of human readable state names for console debugging */ -static const char * const tc_state_names[] = { - [TC_DISABLED] = "Disabled", - [TC_ERROR_RECOVERY] = "ErrorRecovery", - [TC_UNATTACHED_SNK] = "Unattached.SNK", - [TC_ATTACH_WAIT_SNK] = "AttachWait.SNK", - [TC_ATTACHED_SNK] = "Attached.SNK", - [TC_UNORIENTED_DBG_ACC_SRC] = "UnorientedDebugAccessory.SRC", - [TC_DBG_ACC_SNK] = "DebugAccessory.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 - -}; -#endif - -/* Generate a compiler error if invalid states are referenced */ -#ifndef CONFIG_USB_PD_TRY_SRC -#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 true. */ - uint8_t pd_enable; - /* - * Timer for handling TOGGLE_OFF/FORCE_SINK mode when auto-toggle - * enabled. See drp_auto_toggle_next_state() for details. - */ - uint64_t drp_sink_time; -#ifdef CONFIG_USB_PE_SM - /* Power supply reset sequence during a hard reset */ - enum ps_reset_sequence ps_reset_state; -#endif - /* Port polarity : 0 => CC1 is CC line, 1 => CC2 is CC line */ - uint8_t polarity; - /* port flags, see TC_FLAGS_* */ - uint32_t flags; - /* event timeout */ - uint64_t evt_timeout; - /* Time a port shall wait before it can determine it is attached */ - uint64_t cc_debounce; - /* - * Time a Sink port shall wait before it can determine it is detached - * due to the potential for USB PD signaling on CC as described in - * the state definitions. - */ - uint64_t pd_debounce; -#ifdef CONFIG_USB_PD_TRY_SRC - /* - * Time a port shall wait before it can determine it is - * re-attached during the try-wait process. - */ - uint64_t try_wait_debounce; -#endif - /* The cc state */ - enum pd_cc_states cc_state; - /* Role toggle timer */ - uint64_t next_role_swap; - /* Generic timer */ - uint64_t timeout; - /* Time to enter low power mode */ - uint64_t low_power_time; - /* Tasks to notify after TCPC has been reset */ - int tasks_waiting_on_reset; - /* Tasks preventing TCPC from entering low power mode */ - int tasks_preventing_lpm; - /* 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; - /* Attached ChromeOS device id, RW hash, and current RO / RW image */ - uint16_t dev_id; - uint32_t dev_rw_hash[PD_RW_HASH_SIZE/4]; - enum ec_current_image current_image; -} 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}; - -#ifdef CONFIG_USBC_VCONN -static void set_vconn(int port, int enable); -#endif - -#ifdef CONFIG_USB_PE_SM - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP -/* Tracker for which task is waiting on sysjump prep to finish */ -static volatile task_id_t sysjump_task_waiting = TASK_ID_INVALID; -#endif - -/* - * 4 entry rw_hash table of type-C devices that AP has firmware updates for. - */ -#ifdef CONFIG_COMMON_RUNTIME -#define RW_HASH_ENTRIES 4 -static struct ec_params_usb_pd_rw_hash_entry rw_hash_table[RW_HASH_ENTRIES]; -#endif - -/* Forward declare common, private functions */ -static __maybe_unused int reset_device_and_notify(int port); - -#ifdef CONFIG_POWER_COMMON -static void handle_new_power_state(int port); -#endif /* CONFIG_POWER_COMMON */ - -static void pd_update_dual_role_config(int port); -#endif /* CONFIG_USB_PE_SM */ - -/* 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); - -#ifdef CONFIG_USB_PD_TRY_SRC -/* Enable variable for Try.SRC states */ -static uint8_t pd_try_src_enable; -static void pd_update_try_source(void); -#endif - -static void sink_stop_drawing_current(int port); - -/* - * 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 */ -} - -void pd_set_vbus_discharge(int port, int enable) -{ - /* DO NOTHING */ -} - -uint16_t pd_get_identity_vid(int port) -{ - /* DO NOTHING */ - return 0; -} - -#endif /* !CONFIG_USB_PRL_SM */ - -void pd_update_contract(int port) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - /* Must be in Attached.SRC when this function is called */ - if (get_state_tc(port) == TC_ATTACHED_SRC) - pe_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); - - /* Must be in Attached.SNK when this function is called */ - if (get_state_tc(port) == TC_ATTACHED_SNK) - pe_dpm_request(port, DPM_REQUEST_NEW_POWER_LEVEL); - else - TC_SET_FLAG(port, TC_FLAGS_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) - pe_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) - pe_dpm_request(port, DPM_REQUEST_NEW_POWER_LEVEL); - } -} - -void pd_request_power_swap(int port) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - /* - * Must be in Attached.SRC, Attached.SNK, UnorientedDbgAcc.SRC, - * or DbgAcc.SNK, when this function is called. - */ - if (get_state_tc(port) == TC_ATTACHED_SRC || - get_state_tc(port) == TC_ATTACHED_SNK || - get_state_tc(port) == TC_DBG_ACC_SNK || - get_state_tc(port) == - TC_UNORIENTED_DBG_ACC_SRC) { - TC_SET_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS); - } - } -} - -static inline void pd_set_dual_role_no_wakeup(int port, - enum pd_dual_role_states state) -{ - drp_state[port] = state; - - if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - pd_update_try_source(); -} - -void pd_set_dual_role(int port, enum pd_dual_role_states state) -{ - pd_set_dual_role_no_wakeup(port, state); - - /* Wake task up to process change */ - task_set_event(PD_PORT_TO_TASK_ID(port), - PD_EVENT_UPDATE_DUAL_ROLE, 0); -} - -#ifdef CONFIG_USB_PE_SM -int pd_get_partner_data_swap_capable(int port) -{ - /* return data swap capable status of port partner */ - return TC_CHK_FLAG(port, TC_FLAGS_PARTNER_DR_DATA); -} - -int pd_comm_is_enabled(int port) -{ - return tc[port].pd_enable; -} - -void pd_send_vdm(int port, uint32_t vid, int cmd, const uint32_t *data, - int count) -{ - pe_send_vdm(port, vid, cmd, data, count); -} - -void pd_request_data_swap(int port) -{ - /* - * Must be in Attached.SRC or Attached.SNK when this function - * is called - */ - if (get_state_tc(port) == TC_ATTACHED_SRC || - get_state_tc(port) == TC_ATTACHED_SNK) { - TC_SET_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP); - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); - } -} - -/* - * Return true if partner port is a DTS or TS capable of entering debug - * mode (eg. is presenting Rp/Rp or Rd/Rd). - */ -int pd_ts_dts_plugged(int port) -{ - return TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER); -} - -/* Return true if partner port is known to be PD capable. */ -int pd_capable(int port) -{ - return TC_CHK_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE); -} - -/* - * Return true if partner port is capable of communication over USB data - * lines. - */ -int pd_get_partner_usb_comm_capable(int port) -{ - return TC_CHK_FLAG(port, TC_FLAGS_PARTNER_USB_COMM); -} - -enum pd_dual_role_states pd_get_dual_role(int port) -{ - return drp_state[port]; -} - -int pd_dev_store_rw_hash(int port, uint16_t dev_id, uint32_t *rw_hash, - uint32_t current_image) -{ - int i; - - tc[port].dev_id = dev_id; - memcpy(tc[port].dev_rw_hash, rw_hash, PD_RW_HASH_SIZE); -#ifdef CONFIG_CMD_PD_DEV_DUMP_INFO - if (debug_level >= 2) - pd_dev_dump_info(dev_id, (uint8_t *)rw_hash); -#endif - tc[port].current_image = current_image; - - /* Search table for matching device / hash */ - for (i = 0; i < RW_HASH_ENTRIES; i++) - if (dev_id == rw_hash_table[i].dev_id) - return !memcmp(rw_hash, - rw_hash_table[i].dev_rw_hash, - PD_RW_HASH_SIZE); - return 0; -} - -void pd_got_frs_signal(int port) -{ - pe_got_frs_signal(port); -} - -int tc_is_attached_src(int port) -{ - return get_state_tc(port) == TC_ATTACHED_SRC; -} - -int tc_is_attached_snk(int port) -{ - return get_state_tc(port) == TC_ATTACHED_SNK; -} - -void tc_partner_dr_power(int port, int en) -{ - if (en) - TC_SET_FLAG(port, TC_FLAGS_PARTNER_DR_POWER); - else - TC_CLR_FLAG(port, TC_FLAGS_PARTNER_DR_POWER); -} - -void tc_partner_extpower(int port, int en) -{ - if (en) - TC_SET_FLAG(port, TC_FLAGS_PARTNER_EXTPOWER); - else - TC_CLR_FLAG(port, TC_FLAGS_PARTNER_EXTPOWER); -} - -void tc_partner_usb_comm(int port, int en) -{ - if (en) - TC_SET_FLAG(port, TC_FLAGS_PARTNER_USB_COMM); - else - TC_CLR_FLAG(port, TC_FLAGS_PARTNER_USB_COMM); -} - -void tc_partner_dr_data(int port, int en) -{ - if (en) - TC_SET_FLAG(port, TC_FLAGS_PARTNER_DR_DATA); - else - TC_CLR_FLAG(port, TC_FLAGS_PARTNER_DR_DATA); -} - -void tc_pd_connection(int port, int en) -{ - if (en) - TC_SET_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE); - else - TC_CLR_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE); -} - -void tc_ctvpd_detected(int port) -{ - TC_SET_FLAG(port, TC_FLAGS_CTVPD_DETECTED); -} - -void tc_vconn_on(int port) -{ - set_vconn(port, 1); -} - -int tc_check_vconn_swap(int port) -{ -#ifdef CONFIG_USBC_VCONN - if (TC_CHK_FLAG(port, TC_FLAGS_REJECT_VCONN_SWAP)) - return 0; - - return pd_check_vconn_swap(port); -#else - return 0; -#endif -} - -void tc_pr_swap_complete(int port) -{ - TC_CLR_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS); -} - -void tc_prs_src_snk_assert_rd(int port) -{ - /* Must be in Attached.SRC when this function is called */ - if (get_state_tc(port) == TC_ATTACHED_SRC) { - /* Transition to Attached.SNK to assert Rd */ - TC_SET_FLAG(port, TC_FLAGS_DO_PR_SWAP); - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); - } -} - -void tc_prs_snk_src_assert_rp(int port) -{ - /* Must be in Attached.SNK when this function is called */ - if (get_state_tc(port) == TC_ATTACHED_SNK) { - /* Transition to Attached.SRC to assert Rp */ - TC_SET_FLAG(port, TC_FLAGS_DO_PR_SWAP); - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); - } -} - -void tc_hard_reset(int port) -{ - TC_SET_FLAG(port, TC_FLAGS_HARD_RESET); - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0); -} - -void tc_disc_ident_in_progress(int port) -{ - TC_SET_FLAG(port, TC_FLAGS_DISC_IDENT_IN_PROGRESS); -} - -void tc_disc_ident_complete(int port) -{ - TC_CLR_FLAG(port, TC_FLAGS_DISC_IDENT_IN_PROGRESS); -} -#endif /* CONFIG_USB_PE_SM */ - -void tc_snk_power_off(int port) -{ - if (get_state_tc(port) == TC_ATTACHED_SNK || - get_state_tc(port) == TC_DBG_ACC_SNK) { - TC_SET_FLAG(port, TC_FLAGS_POWER_OFF_SNK); - sink_stop_drawing_current(port); - } -} - -int tc_src_power_on(int port) -{ - if (get_state_tc(port) == TC_ATTACHED_SRC) - return pd_set_power_supply_ready(port); - - return 0; -} - -void tc_src_power_off(int port) -{ - if (get_state_tc(port) == TC_ATTACHED_SRC || - get_state_tc(port) == TC_UNORIENTED_DBG_ACC_SRC) { - /* Remove VBUS */ - pd_power_supply_reset(port); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - charge_manager_set_ceil(port, CEIL_REQUESTOR_PD, - CHARGE_CEIL_NONE); - } -} - -void pd_set_suspend(int port, int enable) -{ - if (pd_is_port_enabled(port) == !enable) - return; - - set_state_tc(port, - enable ? TC_DISABLED : TC_UNATTACHED_SNK); -} - -int pd_is_port_enabled(int port) -{ - return get_state_tc(port) != TC_DISABLED; -} - -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; -} - -int pd_get_polarity(int port) -{ - return tc[port].polarity; -} - -int pd_get_role(int port) -{ - return tc[port].data_role; -} - -int pd_is_vbus_present(int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_VBUS_DETECT_TCPC)) - return tcpm_get_vbus_level(port); - else - return pd_snk_is_vbus_provided(port); -} - -void pd_vbus_low(int port) -{ - TC_CLR_FLAG(port, TC_FLAGS_VBUS_NEVER_LOW); -} - -int pd_is_connected(int port) -{ - return (get_state_tc(port) == TC_ATTACHED_SNK) || - (get_state_tc(port) == TC_ATTACHED_SRC); -} - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP -/* - * TODO(b/137493121): Move this function to a separate file that's shared - * between the this and the original stack. - */ -void pd_prepare_sysjump(void) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - int i; - - /* - * Exit modes before sysjump so we can cleanly enter again - * later - */ - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - /* - * We can't be in an alternate mode if PD comm is - * disabled, so no need to send the event - */ - if (!pd_comm_is_enabled(i)) - continue; - - sysjump_task_waiting = task_get_current(); - task_set_event(PD_PORT_TO_TASK_ID(i), - PD_EVENT_SYSJUMP, 0); - task_wait_event_mask(TASK_EVENT_SYSJUMP_READY, -1); - sysjump_task_waiting = TASK_ID_INVALID; - } - } -} -#endif - -#ifdef CONFIG_USB_PE_SM -static void 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; - tc[port].timeout = get_time().val + PD_T_SRC_RECOVER; - return; - case PS_STATE1: - /* Enable VBUS */ - pd_set_power_supply_ready(port); - - /* Turn off VCONN */ - set_vconn(port, 1); - - tc[port].ps_reset_state = PS_STATE3; - tc[port].timeout = get_time().val + - PD_POWER_SUPPLY_TURN_ON_DELAY; - return; - case PS_STATE2: - case PS_STATE3: - /* Tell Policy Engine Hard Reset is complete */ - pe_ps_reset_complete(port); - - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET); - tc[port].ps_reset_state = PS_STATE0; - return; - } -} -#endif - -static void tc_perform_snk_hard_reset(int port) -{ - tc_set_data_role(port, PD_ROLE_UFP); - - /* Clear the input current limit */ - sink_stop_drawing_current(port); - - /* - * When VCONN is supported, the Hard Reset Shall cause - * the Port with the Rd resistor asserted to turn off - * VCONN. - */ -#ifdef CONFIG_USBC_VCONN - if (TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON)) - set_vconn(port, 0); -#endif - - /* - * Inform policy engine that power supply - * reset is complete - */ - pe_ps_reset_complete(port); -} - -void tc_start_error_recovery(int port) -{ - /* - * Async. function call: - * 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 = 0; - - res = tc_restart_tcpc(port); - - CPRINTS("TCPC p%d init %s", port, res ? "failed" : "ready"); - - /* 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); - - tcpm_select_rp_value(port, CONFIG_USB_PD_PULLUP); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - /* Initialize PD and type-C supplier current limits to 0 */ - pd_set_input_current_limit(port, 0, 0); - typec_set_input_current_limit(port, 0, 0); - charge_manager_update_dualrole(port, CAP_UNKNOWN); - } - - tc[port].flags = 0; - -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - /* - * Some TCPCs may not support DRP Auto Toggle, so query the - * query the TCPC for DRP Auto toggle support. - */ - if (tcpm_auto_toggle_supported(port)) - TC_SET_FLAG(port, TC_FLAGS_AUTO_TOGGLE_SUPPORTED); -#endif - -#ifdef CONFIG_USB_PE_SM - tc[port].pd_enable = 0; - tc[port].ps_reset_state = PS_STATE0; -#endif -} - -void tc_state_init(int port) -{ - /* Unattached.SNK is the default starting state. */ - restart_tc_sm(port, TC_UNATTACHED_SNK); - - /* - * If the TCPC isn't accessed, it will enter low power mode - * after PD_LPM_DEBOUNCE_US. - */ - tc[port].low_power_time = get_time().val + PD_LPM_DEBOUNCE_US; -} - -enum pd_power_role tc_get_power_role(int port) -{ - return tc[port].power_role; -} - -enum pd_data_role tc_get_data_role(int port) -{ - return tc[port].data_role; -} - -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; -} - -uint8_t tc_get_polarity(int port) -{ - return tc[port].polarity; -} - -uint8_t tc_get_pd_enabled(int port) -{ - return tc[port].pd_enable; -} - -void tc_set_power_role(int port, enum pd_power_role role) -{ - tc[port].power_role = role; -} - -/* - * Private Functions - */ - -/* Set the TypeC state machine to a new state. */ -static void set_state_tc(const int port, const enum usb_tc_state new_state) -{ - 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) -{ - 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) -{ - CPRINTS("C%d: %s", port, tc_state_names[get_state_tc(port)]); -} - -#ifdef CONFIG_USB_PE_SM -static void handle_device_access(int port) -{ - tc[port].low_power_time = get_time().val + PD_LPM_DEBOUNCE_US; -} -#endif - -void tc_event_check(int port, int evt) -{ -#ifdef CONFIG_USB_PE_SM - if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER)) { - if (evt & PD_EXIT_LOW_POWER_EVENT_MASK) - TC_SET_FLAG(port, TC_FLAGS_WAKE_FROM_LPM); - if (evt & PD_EVENT_DEVICE_ACCESSED) - handle_device_access(port); - } - - /* if TCPC has reset, then need to initialize it again */ - if (evt & PD_EVENT_TCPC_RESET) - reset_device_and_notify(port); - -#ifdef CONFIG_POWER_COMMON - if (IS_ENABLED(CONFIG_POWER_COMMON)) { - if (evt & PD_EVENT_POWER_STATE_CHANGE) { - TC_SET_FLAG(port, TC_FLAGS_POWER_STATE_CHANGE); - handle_new_power_state(port); - } - } -#endif /* CONFIG_POWER_COMMON */ -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) { - if (evt & PD_EVENT_SYSJUMP) { - pe_exit_dp_mode(port); - notify_sysjump_ready(&sysjump_task_waiting); - } - } -#endif - - if (evt & PD_EVENT_UPDATE_DUAL_ROLE) - pd_update_dual_role_config(port); -#endif -} - -/* - * 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) -{ - 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); - - /* 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); - } -} - -#ifdef CONFIG_USB_PD_TRY_SRC -static void pd_update_try_source(void) -{ - int i; - int try_src = 0; - static struct mutex pd_try_src_enable_lock; - - int batt_soc = usb_get_battery_soc(); - - try_src = 0; - for (i = 0; i < board_get_usb_pd_port_count(); i++) - try_src |= drp_state[i] == PD_DRP_TOGGLE_ON; - - /* - * This function is called from this PD task and the hooks tasks. - * A lock is added here to serialize access to the - * pd_try_source_enable variable. - */ - mutex_lock(&pd_try_src_enable_lock); - - /* - * Enable try source when dual-role toggling AND battery is present - * and at some minimum percentage. - */ - pd_try_src_enable = try_src && - batt_soc >= CONFIG_USB_PD_TRY_SRC_MIN_BATT_SOC; - -#ifdef CONFIG_BATTERY_REVIVE_DISCONNECT - /* - * Don't attempt Try.Src if the battery is in the disconnect state. The - * discharge FET may not be enabled and so attempting Try.Src may cut - * off our only power source at the time. - */ - pd_try_src_enable &= (battery_get_disconnect_state() == - BATTERY_NOT_DISCONNECTED); -#elif defined(CONFIG_BATTERY_PRESENT_CUSTOM) || \ - defined(CONFIG_BATTERY_PRESENT_GPIO) - /* - * When battery is cutoff in ship mode it may not be reliable to - * check if battery is present with its state of charge. - * Also check if battery is initialized and ready to provide power. - */ - pd_try_src_enable &= (battery_is_present() == BP_YES); -#endif /* CONFIG_BATTERY_PRESENT_[CUSTOM|GPIO] */ - - mutex_unlock(&pd_try_src_enable_lock); -} -DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, pd_update_try_source, HOOK_PRIO_DEFAULT); -#endif /* CONFIG_USB_PD_TRY_SRC */ - -#ifdef CONFIG_CMD_PD_DEV_DUMP_INFO -static inline void pd_dev_dump_info(uint16_t dev_id, uint8_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; j += 4) { - ccprintf(" 0x%02x%02x%02x%02x", hash[j + 3], hash[j + 2], - hash[j + 1], hash[j]); - } - ccprintf("\n"); -} -#endif /* CONFIG_CMD_PD_DEV_DUMP_INFO */ - -static void set_vconn(int port, int enable) -{ - if (enable == TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON)) - return; - - if (enable) - TC_SET_FLAG(port, TC_FLAGS_VCONN_ON); - else - TC_CLR_FLAG(port, TC_FLAGS_VCONN_ON); - - /* - * We always need to tell the TCPC to enable Vconn first, otherwise some - * TCPCs get confused and think the CC line is in over voltage mode and - * immediately disconnects. If there is a PPC, both devices will - * potentially source Vconn, but that should be okay since Vconn has - * "make before break" electrical requirements when swapping anyway. - */ - tcpm_set_vconn(port, enable); - - if (IS_ENABLED(CONFIG_USBC_PPC_VCONN)) - ppc_set_vconn(port, enable); -} - -#ifdef CONFIG_USB_PD_TCPM_TCPCI -static uint32_t pd_ports_to_resume; -static void resume_pd_port(void) -{ - uint32_t port; - uint32_t suspended_ports = atomic_read_clear(&pd_ports_to_resume); - - while (suspended_ports) { - port = __builtin_ctz(suspended_ports); - suspended_ports &= ~(1 << port); - pd_set_suspend(port, 0); - } -} -DECLARE_DEFERRED(resume_pd_port); - -void pd_deferred_resume(int port) -{ - atomic_or(&pd_ports_to_resume, 1 << port); - hook_call_deferred(&resume_pd_port_data, SECOND); -} -#endif /* CONFIG_USB_PD_DEFERRED_RESUME */ - -#ifdef CONFIG_USB_PE_SM -/* This must only be called from the PD task */ -static void pd_update_dual_role_config(int port) -{ - /* - * Change to sink if port is currently a source AND (new DRP - * state is force sink OR new DRP state is either toggle off - * or debug accessory toggle only and we are in the source - * disconnected state). - */ - if (!IS_ENABLED(CONFIG_USB_PE_SM)) - return; - - if (tc[port].power_role == PD_ROLE_SOURCE && - ((drp_state[port] == PD_DRP_FORCE_SINK && - !pd_ts_dts_plugged(port)) || - (drp_state[port] == PD_DRP_TOGGLE_OFF && - get_state_tc(port) == TC_UNATTACHED_SRC))) { - 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); - } -} - -#ifdef CONFIG_POWER_COMMON -static void handle_new_power_state(int port) -{ - 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 negotiated DP mode again when it - * boots up - */ - pe_exit_dp_mode(port); - - /* Ensure mux is set properly after chipset transition */ - set_usb_mux_with_current_data_role(port); - } -} -#endif /* CONFIG_POWER_COMMON */ - -/* - * HOST COMMANDS - */ -#ifdef HAS_TASK_HOSTCMD -static enum ec_status hc_pd_ports(struct host_cmd_handler_args *args) -{ - struct ec_response_usb_pd_ports *r = args->response; - - r->num_ports = board_get_usb_pd_port_count(); - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_PORTS, - hc_pd_ports, - EC_VER_MASK(0)); -static const enum pd_dual_role_states dual_role_map[USB_PD_CTRL_ROLE_COUNT] = { - [USB_PD_CTRL_ROLE_TOGGLE_ON] = PD_DRP_TOGGLE_ON, - [USB_PD_CTRL_ROLE_TOGGLE_OFF] = PD_DRP_TOGGLE_OFF, - [USB_PD_CTRL_ROLE_FORCE_SINK] = PD_DRP_FORCE_SINK, - [USB_PD_CTRL_ROLE_FORCE_SOURCE] = PD_DRP_FORCE_SOURCE, - [USB_PD_CTRL_ROLE_FREEZE] = PD_DRP_FREEZE, -}; - -#ifdef CONFIG_USBC_SS_MUX -static const enum typec_mux typec_mux_map[USB_PD_CTRL_MUX_COUNT] = { - [USB_PD_CTRL_MUX_NONE] = TYPEC_MUX_NONE, - [USB_PD_CTRL_MUX_USB] = TYPEC_MUX_USB, - [USB_PD_CTRL_MUX_AUTO] = TYPEC_MUX_DP, - [USB_PD_CTRL_MUX_DP] = TYPEC_MUX_DP, - [USB_PD_CTRL_MUX_DOCK] = TYPEC_MUX_DOCK, -}; -#endif - -__overridable uint8_t board_get_dp_pin_mode(int port) -{ - return 0; -} - -/* - * TODO(b/142911453): Move this function to a common/usb_common.c to avoid - * duplicate code - */ -static enum ec_status hc_usb_pd_control(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_pd_control *p = args->params; - struct ec_response_usb_pd_control_v2 *r_v2 = args->response; - struct ec_response_usb_pd_control *r = args->response; - - if (p->port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - if (p->role >= USB_PD_CTRL_ROLE_COUNT || - p->mux >= USB_PD_CTRL_MUX_COUNT) - return EC_RES_INVALID_PARAM; - - if (p->role != USB_PD_CTRL_ROLE_NO_CHANGE) - pd_set_dual_role(p->port, dual_role_map[p->role]); - -#ifdef CONFIG_USBC_SS_MUX - if (p->mux != USB_PD_CTRL_MUX_NO_CHANGE) - usb_mux_set(p->port, typec_mux_map[p->mux], - typec_mux_map[p->mux] == TYPEC_MUX_NONE ? - USB_SWITCH_DISCONNECT : - USB_SWITCH_CONNECT, - pd_get_polarity(p->port)); -#endif /* CONFIG_USBC_SS_MUX */ - - if (p->swap == USB_PD_CTRL_SWAP_DATA) - pd_request_data_swap(p->port); - else if (p->swap == USB_PD_CTRL_SWAP_POWER) - pd_request_power_swap(p->port); -#ifdef CONFIG_USBC_VCONN_SWAP - else if (p->swap == USB_PD_CTRL_SWAP_VCONN) - pe_dpm_request(p->port, DPM_REQUEST_VCONN_SWAP); -#endif - - switch (args->version) { - case 0: - r->enabled = pd_comm_is_enabled(p->port); - r->role = tc[p->port].power_role; - r->polarity = tc[p->port].polarity; - r->state = get_state_tc(p->port); - args->response_size = sizeof(*r); - break; - case 1: - case 2: - if (sizeof(*r_v2) > args->response_max) - return EC_RES_INVALID_PARAM; - - r_v2->enabled = - (pd_comm_is_enabled(p->port) ? - PD_CTRL_RESP_ENABLED_COMMS : 0) | - (pd_is_connected(p->port) ? - PD_CTRL_RESP_ENABLED_CONNECTED : 0) | - (TC_CHK_FLAG(p->port, TC_FLAGS_PARTNER_PD_CAPABLE) ? - PD_CTRL_RESP_ENABLED_PD_CAPABLE : 0); - r_v2->role = - (tc[p->port].power_role ? PD_CTRL_RESP_ROLE_POWER : 0) | - (tc[p->port].data_role ? PD_CTRL_RESP_ROLE_DATA : 0) | - (TC_CHK_FLAG(p->port, TC_FLAGS_VCONN_ON) ? - PD_CTRL_RESP_ROLE_VCONN : 0) | - (TC_CHK_FLAG(p->port, TC_FLAGS_PARTNER_DR_POWER) ? - PD_CTRL_RESP_ROLE_DR_POWER : 0) | - (TC_CHK_FLAG(p->port, TC_FLAGS_PARTNER_DR_DATA) ? - PD_CTRL_RESP_ROLE_DR_DATA : 0) | - (TC_CHK_FLAG(p->port, TC_FLAGS_PARTNER_USB_COMM) ? - PD_CTRL_RESP_ROLE_USB_COMM : 0) | - (TC_CHK_FLAG(p->port, TC_FLAGS_PARTNER_EXTPOWER) ? - PD_CTRL_RESP_ROLE_EXT_POWERED : 0); - r_v2->polarity = tc[p->port].polarity; - r_v2->cc_state = tc[p->port].cc_state; - r_v2->dp_mode = board_get_dp_pin_mode(p->port); - r_v2->cable_type = get_usb_pd_mux_cable_type(p->port); - - strzcpy(r_v2->state, tc_state_names[get_state_tc(p->port)], - sizeof(r_v2->state)); - if (args->version == 1) { - /* - * ec_response_usb_pd_control_v2 (r_v2) is a - * strict superset of ec_response_usb_pd_control_v1 - */ - args->response_size = - sizeof(struct ec_response_usb_pd_control_v1); - } else - args->response_size = sizeof(*r_v2); - break; - default: - return EC_RES_INVALID_PARAM; - } - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_CONTROL, - hc_usb_pd_control, - EC_VER_MASK(0) | EC_VER_MASK(1) | EC_VER_MASK(2)); - -static enum ec_status hc_remote_flash(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_pd_fw_update *p = args->params; - int port = p->port; - int rv = EC_RES_SUCCESS; - const uint32_t *data = &(p->size) + 1; - int i, size; - - if (port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - if (p->size + sizeof(*p) > args->params_size) - return EC_RES_INVALID_PARAM; - -#if defined(CONFIG_BATTERY_PRESENT_CUSTOM) || \ -defined(CONFIG_BATTERY_PRESENT_GPIO) - /* - * Do not allow PD firmware update if no battery and this port - * is sinking power, because we will lose power. - */ - if (battery_is_present() != BP_YES && - charge_manager_get_active_charge_port() == port) - return EC_RES_UNAVAILABLE; -#endif - - switch (p->cmd) { - case USB_PD_FW_REBOOT: - pe_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_REBOOT, NULL, 0); - /* - * Return immediately to free pending i2c bus. Host needs to - * manage this delay. - */ - return EC_RES_SUCCESS; - - case USB_PD_FW_FLASH_ERASE: - pe_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_FLASH_ERASE, NULL, 0); - /* - * Return immediately. Host needs to manage delays here which - * can be as long as 1.2 seconds on 64KB RW flash. - */ - return EC_RES_SUCCESS; - - case USB_PD_FW_ERASE_SIG: - pe_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_ERASE_SIG, NULL, 0); - break; - - case USB_PD_FW_FLASH_WRITE: - /* Data size must be a multiple of 4 */ - if (!p->size || p->size % 4) - return EC_RES_INVALID_PARAM; - - size = p->size / 4; - for (i = 0; i < size; i += VDO_MAX_SIZE - 1) { - pe_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_FLASH_WRITE, - data + i, MIN(size - i, VDO_MAX_SIZE - 1)); - } - return EC_RES_SUCCESS; - - default: - return EC_RES_INVALID_PARAM; - } - - return rv; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_FW_UPDATE, - hc_remote_flash, - EC_VER_MASK(0)); - -static enum ec_status -hc_remote_rw_hash_entry(struct host_cmd_handler_args *args) -{ - int i, idx = 0, found = 0; - const struct ec_params_usb_pd_rw_hash_entry *p = args->params; - static int rw_hash_next_idx; - - if (!p->dev_id) - return EC_RES_INVALID_PARAM; - - for (i = 0; i < RW_HASH_ENTRIES; i++) { - if (p->dev_id == rw_hash_table[i].dev_id) { - idx = i; - found = 1; - break; - } - } - - if (!found) { - idx = rw_hash_next_idx; - rw_hash_next_idx = rw_hash_next_idx + 1; - if (rw_hash_next_idx == RW_HASH_ENTRIES) - rw_hash_next_idx = 0; - } - memcpy(&rw_hash_table[idx], p, sizeof(*p)); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_RW_HASH_ENTRY, - hc_remote_rw_hash_entry, - EC_VER_MASK(0)); - -static enum ec_status hc_remote_pd_dev_info(struct host_cmd_handler_args *args) -{ - const uint8_t *port = args->params; - struct ec_params_usb_pd_rw_hash_entry *r = args->response; - - if (*port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - r->dev_id = tc[*port].dev_id; - - if (r->dev_id) - memcpy(r->dev_rw_hash, tc[*port].dev_rw_hash, PD_RW_HASH_SIZE); - - r->current_image = tc[*port].current_image; - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} - -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_DEV_INFO, - hc_remote_pd_dev_info, - EC_VER_MASK(0)); - -#ifndef CONFIG_USB_PD_TCPC -#ifdef CONFIG_EC_CMD_PD_CHIP_INFO -static enum ec_status hc_remote_pd_chip_info(struct host_cmd_handler_args *args) -{ - const struct ec_params_pd_chip_info *p = args->params; - struct ec_response_pd_chip_info_v1 *info; - - if (p->port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - if (tcpm_get_chip_info(p->port, p->live, &info)) - return EC_RES_ERROR; - - /* - * Take advantage of the fact that v0 and v1 structs have the - * same layout for v0 data. (v1 just appends data) - */ - args->response_size = - args->version ? sizeof(struct ec_response_pd_chip_info_v1) - : sizeof(struct ec_response_pd_chip_info); - - memcpy(args->response, info, args->response_size); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_PD_CHIP_INFO, - hc_remote_pd_chip_info, - EC_VER_MASK(0) | EC_VER_MASK(1)); -#endif /* CONFIG_EC_CMD_PD_CHIP_INFO */ -#endif /* !CONFIG_USB_PD_TCPC */ - -#ifdef CONFIG_HOSTCMD_EVENTS -void pd_notify_dp_alt_mode_entry(void) -{ - /* - * Note: EC_HOST_EVENT_PD_MCU may be a more appropriate host event to - * send, but we do not send that here because there are other cases - * where we send EC_HOST_EVENT_PD_MCU such as charger insertion or - * removal. Currently, those do not wake the system up, but - * EC_HOST_EVENT_MODE_CHANGE does. If we made the system wake up on - * EC_HOST_EVENT_PD_MCU, we would be turning the internal display on on - * every charger insertion/removal, which is not desired. - */ - CPRINTS("Notifying AP of DP Alt Mode Entry..."); - host_set_single_event(EC_HOST_EVENT_MODE_CHANGE); -} -#endif /* CONFIG_HOSTCMD_EVENTS */ - -#ifdef CONFIG_USB_PD_ALT_MODE_DFP -static enum ec_status hc_remote_pd_set_amode(struct host_cmd_handler_args *args) -{ - const struct ec_params_usb_pd_set_mode_request *p = args->params; - - if ((p->port >= board_get_usb_pd_port_count()) || - (!p->svid) || (!p->opos)) - return EC_RES_INVALID_PARAM; - - switch (p->cmd) { - case PD_EXIT_MODE: - if (pd_dfp_exit_mode(p->port, p->svid, p->opos)) - pd_send_vdm(p->port, p->svid, - CMD_EXIT_MODE | VDO_OPOS(p->opos), NULL, 0); - else { - CPRINTF("Failed exit mode\n"); - return EC_RES_ERROR; - } - break; - case PD_ENTER_MODE: - if (pd_dfp_enter_mode(p->port, p->svid, p->opos)) - pd_send_vdm(p->port, p->svid, CMD_ENTER_MODE | - VDO_OPOS(p->opos), NULL, 0); - break; - default: - return EC_RES_INVALID_PARAM; - } - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_USB_PD_SET_AMODE, - hc_remote_pd_set_amode, - EC_VER_MASK(0)); -#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ -#endif /* HAS_TASK_HOSTCMD */ - -#if defined(CONFIG_USB_PD_ALT_MODE) && !defined(CONFIG_USB_PD_ALT_MODE_DFP) -void pd_send_hpd(int port, enum hpd_event hpd) -{ - uint32_t data[1]; - int opos = pd_alt_mode(port, USB_SID_DISPLAYPORT); - - if (!opos) - return; - - data[0] = - VDO_DP_STATUS((hpd == hpd_irq), /* IRQ_HPD */ - (hpd != hpd_low), /* HPD_HI|LOW */ - 0, /* request exit DP */ - 0, /* request exit USB */ - 0, /* MF pref */ - 1, /* enabled */ - 0, /* power low */ - 0x2); - pd_send_vdm(port, USB_SID_DISPLAYPORT, - VDO_OPOS(opos) | CMD_ATTENTION, data, 1); -} -#endif -#endif /* CONFIG_USB_PE_SM */ - -#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)); - } -} -#endif - -#ifdef CONFIG_USBC_VCONN -int tc_is_vconn_src(int port) -{ - if (get_state_tc(port) == TC_ATTACHED_SRC || - get_state_tc(port) == TC_ATTACHED_SNK) - return TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON); - else - return -1; -} -#endif - -#ifdef CONFIG_USBC_PPC -static void pd_send_hard_reset(int port) -{ - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SEND_HARD_RESET, 0); -} - -static uint32_t port_oc_reset_req; - -static void re_enable_ports(void) -{ - uint32_t ports = atomic_read_clear(&port_oc_reset_req); - - while (ports) { - int port = __fls(ports); - - ports &= ~BIT(port); - - /* - * Let the board know that the overcurrent is - * over since we're going to attempt re-enabling - * the port. - */ - board_overcurrent_event(port, 0); - - pd_send_hard_reset(port); - /* - * TODO(b/117854867): PD3.0 to send an alert message - * indicating OCP after explicit contract. - */ - } -} -DECLARE_DEFERRED(re_enable_ports); - -void pd_handle_overcurrent(int port) -{ - /* Keep track of the overcurrent events. */ - CPRINTS("C%d: overcurrent!", port); - - if (IS_ENABLED(CONFIG_USB_PD_LOGGING)) - pd_log_event(PD_EVENT_PS_FAULT, PD_LOG_PORT_SIZE(port, 0), - PS_FAULT_OCP, NULL); - - ppc_add_oc_event(port); - /* Let the board specific code know about the OC event. */ - board_overcurrent_event(port, 1); - - /* Wait 1s before trying to re-enable the port. */ - atomic_or(&port_oc_reset_req, BIT(port)); - hook_call_deferred(&re_enable_ports_data, SECOND); -} -#endif /* defined(CONFIG_USBC_PPC) */ - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER -static 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())); - - rv = tc_restart_tcpc(port); - - if (rv == EC_SUCCESS) - CPRINTS("TCPC p%d init ready", port); - else - CPRINTS("TCPC p%d init failed!", port); - - /* - * 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(task_get_event_bitmap(task_get_current()), - PD_EVENT_TCPC_RESET); - - waiting_tasks = atomic_read_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, 0); - } - - return rv; -} - -void pd_wait_exit_low_power(int port) -{ - if (TC_CHK_FLAG(port, TC_FLAGS_LPM_ENGAGED)) { - TC_SET_FLAG(port, TC_FLAGS_WAKE_FROM_LPM); - - if (port != TASK_ID_TO_PD_PORT(task_get_current())) { - /* - * Otherwise, we need to wait for the TCPC reset to - * complete - */ - atomic_or(&tc[port].tasks_waiting_on_reset, - 1 << task_get_current()); - /* - * NOTE: We could be sending the PD task the reset - * event while it is already processing the reset event. - * If that occurs, then we will reset the TCPC multiple - * times, which is undesirable but most likely benign. - * Empirically, this doesn't happen much, but it if - * starts occurring, we can add a guard to - * prevent/reduce it. - */ - task_set_event(PD_PORT_TO_TASK_ID(port), - PD_EVENT_TCPC_RESET, 0); - task_wait_event_mask(TASK_EVENT_PD_AWAKE, -1); - } - } -} - -/* - * 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, 0); -} - -/* - * 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 (!IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER)) - return; - - if (prevent) - atomic_or(&tc[port].tasks_preventing_lpm, current_task_mask); - else - atomic_clear(&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 = 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; - tc[port].cc_debounce = - get_time().val + PD_T_RP_VALUE_CHANGE; - return; - } - - if (tc[port].cc_debounce == 0 || - get_time().val < tc[port].cc_debounce) - return; - - tc[port].cc_debounce = 0; - - 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's VBUS and VCONN off - */ -static void tc_disabled_entry(const int port) -{ - print_current_state(port); -} - -static void tc_disabled_run(const int port) -{ - task_wait_event(-1); -} - -static void tc_disabled_exit(const int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TCPC)) { - if (tc_restart_tcpc(port) != 0) { - CPRINTS("TCPC p%d restart failed!", port); - return; - } - } - - CPRINTS("TCPC p%d resumed!", port); -} - -/** - * 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); - - tc[port].timeout = get_time().val + PD_T_ERROR_RECOVERY; -} - -static void tc_error_recovery_run(const int port) -{ - if (tc[port].timeout > 0 && get_time().val > tc[port].timeout) { - tc[port].timeout = 0; - restart_tc_sm(port, TC_UNATTACHED_SRC); - } -} - -/** - * Unattached.SNK - * - * Super State is Unattached state - */ -static void tc_unattached_snk_entry(const int port) -{ - if (get_last_state_tc(port) != TC_UNATTACHED_SRC) - print_current_state(port); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - charge_manager_update_dualrole(port, CAP_UNKNOWN); - - /* - * 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); - tc[port].next_role_swap = get_time().val + PD_T_DRP_SNK; - - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - usb_mux_set(port, TYPEC_MUX_NONE, - USB_SWITCH_DISCONNECT, tc[port].polarity); - - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - CLR_ALL_BUT_LPM_FLAGS(port); - tc[port].pd_enable = 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)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET); - 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); - -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - /* - * Attempt TCPC auto DRP toggle if it is - * not already auto toggling. - */ - if (drp_state[port] == PD_DRP_TOGGLE_ON && - TC_CHK_FLAG(port, TC_FLAGS_AUTO_TOGGLE_SUPPORTED) && - cc_is_open(cc1, cc2)) { - set_state_tc(port, TC_DRP_AUTO_TOGGLE); - return; - } -#endif - - /* - * The port shall transition to AttachWait.SNK when a Source - * connection is detected, as indicated by the SNK.Rp state - * 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); - } else if (get_time().val > tc[port].next_role_swap && - drp_state[port] == PD_DRP_TOGGLE_ON) { - /* DRP Toggle */ - set_state_tc(port, TC_UNATTACHED_SRC); - } - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - else if (drp_state[port] == PD_DRP_FORCE_SINK || - drp_state[port] == PD_DRP_TOGGLE_OFF) { - set_state_tc(port, TC_LOW_POWER_MODE); - } -#endif -} - -/** - * 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)) - 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) { - tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE; - tc[port].pd_debounce = get_time().val + PD_T_PD_DEBOUNCE; - tc[port].cc_state = new_cc_state; - return; - } - - /* - * A DRP shall transition to Unattached.SNK when the state of both - * the CC1 and CC2 pins is SNK.Open for at least tPDDebounce. - */ - if (new_cc_state == PD_CC_NONE && - get_time().val > tc[port].pd_debounce) { - if (IS_ENABLED(CONFIG_USB_PE_SM) && - IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) { - pd_dfp_exit_mode(port, 0, 0); - } - - /* We are detached */ - set_state_tc(port, TC_UNATTACHED_SRC); - return; - } - - /* Wait for CC debounce */ - if (get_time().val < tc[port].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) { -#ifdef CONFIG_USB_PD_TRY_SRC - if (pd_try_src_enable) - set_state_tc(port, TC_TRY_SRC); - else -#endif - set_state_tc(port, TC_ATTACHED_SNK); - } else { - /* new_cc_state is PD_CC_DFP_DEBUG_ACC */ - TC_SET_FLAG(port, TC_FLAGS_TS_DTS_PARTNER); - set_state_tc(port, TC_DBG_ACC_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); - } - } -} - -/** - * Attached.SNK - */ -static void tc_attached_snk_entry(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - print_current_state(port); - - /* Clear Low Power Mode Request */ - TC_CLR_FLAG(port, TC_FLAGS_LPM_REQUESTED); - -#ifdef CONFIG_USB_PE_SM - if (TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { - /* - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rd. - */ - tcpm_set_cc(port, TYPEC_CC_RD); - - /* 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. - */ - } else -#endif - { - /* Get connector orientation */ - tcpm_get_cc(port, &cc1, &cc2); - tc[port].polarity = get_snk_polarity(cc1, cc2); - set_polarity(port, tc[port].polarity); - - /* - * Initial data role for sink is UFP - * This also sets the usb mux - */ - tc_set_data_role(port, PD_ROLE_UFP); - - 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, - pd_is_port_partner_dualrole(port) ? - CAP_DUALROLE : CAP_DEDICATED); - } - } - - /* Apply Rd */ - tcpm_set_cc(port, TYPEC_CC_RD); - - tc[port].cc_debounce = 0; - - /* Enable PD */ - if (IS_ENABLED(CONFIG_USB_PE_SM)) - tc[port].pd_enable = 1; -} - -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)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET); - tc_perform_snk_hard_reset(port); - } - - /* - * 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 (!pd_is_vbus_present(port)) { - if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - pd_dfp_exit_mode(port, 0, 0); - - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - if (!pe_is_explicit_contract(port)) - sink_power_sub_states(port); - } - - /* - * PD swap commands - */ - if (tc[port].pd_enable && prl_is_running(port)) { - /* - * Power Role Swap - */ - if (TC_CHK_FLAG(port, TC_FLAGS_DO_PR_SWAP)) { - /* Clear PR_SWAP flag in exit */ - 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); - } - -#ifdef CONFIG_USBC_VCONN - /* - * VCONN Swap - */ - 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); - } -#endif - /* - * 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 (!pd_is_vbus_present(port)) { - set_state_tc(port, TC_UNATTACHED_SNK); - 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 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) && - !TC_CHK_FLAG(port, TC_FLAGS_DO_PR_SWAP)) - set_vconn(port, 0); - - /* Clear flags after checking Vconn status */ - TC_CLR_FLAG(port, TC_FLAGS_DO_PR_SWAP | TC_FLAGS_POWER_OFF_SNK); - - /* Stop drawing power */ - sink_stop_drawing_current(port); -} - -/** - * UnorientedDebugAccessory.SRC - * - * Super State Entry Actions: - * Place Rp on CC - * Set power role to SOURCE - */ -static void tc_unoriented_dbg_acc_src_entry(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - print_current_state(port); - - /* Run function relies on timeout being 0 or meaningful */ - tc[port].timeout = 0; - - /* Clear Low Power Mode Request */ - TC_CLR_FLAG(port, TC_FLAGS_LPM_REQUESTED); - - if (TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { - /* Enable VBUS */ - pd_set_power_supply_ready(port); - - /* - * Maintain VCONN supply state, whether ON or OFF, and its - * data role / usb mux connections. - */ - } else { - /* Get connector orientation */ - tcpm_get_cc(port, &cc1, &cc2); - tc[port].polarity = (cc1 != TYPEC_CC_VOLT_RD); - set_polarity(port, tc[port].polarity); - - /* - * Initial data role for sink is DFP - * This also sets the usb mux - */ - tc_set_data_role(port, PD_ROLE_DFP); - - /* Enable VBUS */ - if (pd_set_power_supply_ready(port)) { - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - usb_mux_set(port, TYPEC_MUX_NONE, - USB_SWITCH_DISCONNECT, tc[port].polarity); - } - -#ifdef CONFIG_USB_PE_SM - tc[port].pd_enable = 0; - tc[port].timeout = get_time().val + - PD_POWER_SUPPLY_TURN_ON_DELAY; -#endif - } - - /* Inform PPC that a sink is connected. */ - if (IS_ENABLED(CONFIG_USBC_PPC)) - ppc_sink_is_connected(port, 1); -} - -static void tc_unoriented_dbg_acc_src_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - enum pd_cc_states new_cc_state; - -#ifdef CONFIG_USB_PE_SM - /* Enable PD communications after power supply has fully turned on */ - if (tc[port].pd_enable == 0 && - get_time().val > tc[port].timeout) { - - tc[port].pd_enable = 1; - tc[port].timeout = 0; - } - - if (tc[port].pd_enable == 0) - return; - - /* - * Handle Hard Reset from Policy Engine - */ - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET)) { - if (get_time().val < tc[port].timeout) - return; - - /* - * This function clears TC_FLAGS_HARD_RESET - * when the hard reset is complete. - */ - tc_perform_src_hard_reset(port); - } -#endif - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - if (tc[port].polarity) - cc1 = cc2; - - if (cc1 == TYPEC_CC_VOLT_OPEN) - new_cc_state = PD_CC_NONE; - else - new_cc_state = PD_CC_UFP_ATTACHED; - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_SRC_DISCONNECT; - } - - if (get_time().val < tc[port].cc_debounce) - return; - - if (tc[port].cc_state == PD_CC_NONE && - !TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS) && - !TC_CHK_FLAG(port, TC_FLAGS_DISC_IDENT_IN_PROGRESS)) { - - tc[port].pd_enable = 0; - set_state_tc(port, TC_UNATTACHED_SNK); - } - -#ifdef CONFIG_USB_PE_SM - /* - * PD swap commands - */ - if (tc[port].pd_enable) { - /* - * Power Role Swap Request - */ - if (TC_CHK_FLAG(port, TC_FLAGS_DO_PR_SWAP)) { - /* Clear TC_FLAGS_DO_PR_SWAP on exit */ - return set_state_tc(port, TC_DBG_ACC_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); - } - } -#endif -} - -static void tc_unoriented_dbg_acc_src_exit(const int port) -{ - /* - * A port shall cease to supply VBUS within tVBUSOFF of exiting - * UnorientedDbg.SRC. - */ - tc_src_power_off(port); - - /* Clear PR swap flag */ - TC_CLR_FLAG(port, TC_FLAGS_DO_PR_SWAP); -} - -/** - * Debug Accessory.SNK - * - * Super State Entry Actions: - * Vconn Off - * Place Rd on CC - * Set power role to SINK - */ -static void tc_dbg_acc_snk_entry(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - print_current_state(port); - - if (TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { - /* - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rd. - */ - tcpm_set_cc(port, TYPEC_CC_RD); - - /* 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. - */ - } else { - /* Get connector orientation */ - tcpm_get_cc(port, &cc1, &cc2); - tc[port].polarity = get_snk_polarity(cc1, cc2); - set_polarity(port, tc[port].polarity); - - /* - * Initial data role for sink is UFP - * This also sets the usb mux - */ - tc_set_data_role(port, PD_ROLE_UFP); - - 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, - pd_is_port_partner_dualrole(port) ? - CAP_DUALROLE : CAP_DEDICATED); - } - } - - /* Enable PD */ - tc[port].pd_enable = 1; -} - -static void tc_dbg_acc_snk_run(const int port) -{ - - if (!IS_ENABLED(CONFIG_USB_PE_SM)) { - /* Detach detection */ - if (!pd_is_vbus_present(port)) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* Run Sink Power Sub-State */ - sink_power_sub_states(port); - - return; - } - - /* - * Perform Hard Reset - */ - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET); - tc_perform_snk_hard_reset(port); - } - - /* - * 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 (!pd_is_vbus_present(port)) { - if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - pd_dfp_exit_mode(port, 0, 0); - - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - if (!pe_is_explicit_contract(port)) - sink_power_sub_states(port); - } - - /* PD swap commands */ - - /* - * Power Role Swap - */ - if (TC_CHK_FLAG(port, TC_FLAGS_DO_PR_SWAP)) { - /* Clear PR_SWAP flag in exit */ - set_state_tc(port, TC_UNORIENTED_DBG_ACC_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); - } -} - -static void tc_dbg_acc_snk_exit(const int port) -{ - TC_CLR_FLAG(port, TC_FLAGS_DO_PR_SWAP | TC_FLAGS_POWER_OFF_SNK); - - /* Stop drawing power */ - sink_stop_drawing_current(port); -} - -/** - * Unattached.SRC - * - * Super State is Unattached state - */ -static void tc_unattached_src_entry(const int port) -{ - if (get_last_state_tc(port) != TC_UNATTACHED_SNK) - print_current_state(port); - - if (IS_ENABLED(CONFIG_USBC_PPC)) { - /* There is no sink connected. */ - ppc_sink_is_connected(port, 0); - - /* - * Clear the overcurrent event counter - * since we've detected a disconnect. - */ - ppc_clear_oc_event_counter(port); - } - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - charge_manager_update_dualrole(port, CAP_UNKNOWN); - - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - CLR_ALL_BUT_LPM_FLAGS(port); - tc[port].pd_enable = 0; - } - - tc[port].next_role_swap = get_time().val + 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)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET); - 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_PPC)) { - /* - * If the port is latched off, just continue to - * monitor for a detach. - */ - if (ppc_is_port_latched_off(port)) - return; - } - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - /* - * Transition to AttachWait.SRC when VBUS is vSafe0V and: - * 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 (get_time().val > tc[port].next_role_swap && - drp_state[port] != PD_DRP_FORCE_SOURCE && - drp_state[port] != PD_DRP_FREEZE) - set_state_tc(port, TC_UNATTACHED_SNK); -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - /* - * Attempt TCPC auto DRP toggle - */ - else if (drp_state[port] == PD_DRP_TOGGLE_ON && - TC_CHK_FLAG(port, TC_FLAGS_AUTO_TOGGLE_SUPPORTED) && - cc_is_open(cc1, cc2)) { - set_state_tc(port, TC_DRP_AUTO_TOGGLE); - } -#endif - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - else if (drp_state[port] == PD_DRP_FORCE_SOURCE || - drp_state[port] == PD_DRP_TOGGLE_OFF) { - set_state_tc(port, TC_LOW_POWER_MODE); - } -#endif -} - -/** - * 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); - - /* Debug accessory */ - if (cc_is_snk_dbg_acc(cc1, cc2)) { - /* Debug accessory */ - 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 */ - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE; - tc[port].cc_state = new_cc_state; - return; - } - - /* Wait for CC debounce */ - if (get_time().val < tc[port].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_is_vbus_present(port)) { - 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) { - set_state_tc(port, TC_UNORIENTED_DBG_ACC_SRC); - return; - } - } -} - -/** - * Attached.SRC - */ -static void tc_attached_src_entry(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - print_current_state(port); - - /* Run function relies on timeout being 0 or meaningful */ - tc[port].timeout = 0; - - /* Clear Low Power Mode Request */ - TC_CLR_FLAG(port, TC_FLAGS_LPM_REQUESTED); - -#if defined(CONFIG_USB_PE_SM) - if (TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { - /* Change role to source */ - tc_set_power_role(port, PD_ROLE_SOURCE); - tcpm_set_msg_header(port, - tc[port].power_role, tc[port].data_role); - /* - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rp. - */ - tcpm_select_rp_value(port, CONFIG_USB_PD_PULLUP); - - /* Enable VBUS */ - pd_set_power_supply_ready(port); - - /* - * Maintain VCONN supply state, whether ON or OFF, and its - * data role / usb mux connections. - */ - } else { - /* Get connector orientation */ - tcpm_get_cc(port, &cc1, &cc2); - tc[port].polarity = (cc1 != TYPEC_CC_VOLT_RD); - set_polarity(port, tc[port].polarity); - - /* - * Initial data role for sink is DFP - * This also sets the usb mux - */ - tc_set_data_role(port, PD_ROLE_DFP); - - /* - * Start sourcing Vconn before Vbus to ensure - * we are within USB Type-C Spec 1.4 tVconnON - */ - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_vconn(port, 1); - - /* Enable VBUS */ - if (pd_set_power_supply_ready(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, TYPEC_MUX_NONE, - USB_SWITCH_DISCONNECT, tc[port].polarity); - } - - tc[port].pd_enable = 0; - tc[port].timeout = get_time().val + - MAX(PD_POWER_SUPPLY_TURN_ON_DELAY, PD_T_VCONN_STABLE); - } -#else - /* Get connector orientation */ - tcpm_get_cc(port, &cc1, &cc2); - tc[port].polarity = (cc1 != TYPEC_CC_VOLT_RD); - set_polarity(port, tc[port].polarity); - - /* - * Initial data role for sink is DFP - * This also sets the usb mux - */ - tc_set_data_role(port, PD_ROLE_DFP); - - /* - * Start sourcing Vconn before Vbus to ensure - * we are within USB Type-C Spec 1.4 tVconnON - */ - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_vconn(port, 1); - - /* Enable VBUS */ - if (pd_set_power_supply_ready(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, TYPEC_MUX_NONE, - USB_SWITCH_DISCONNECT, tc[port].polarity); - } -#endif /* CONFIG_USB_PE_SM */ - - /* Inform PPC that a sink is connected. */ - if (IS_ENABLED(CONFIG_USBC_PPC)) - ppc_sink_is_connected(port, 1); -} - -static void tc_attached_src_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - enum pd_cc_states new_cc_state; - -#ifdef CONFIG_USB_PE_SM - /* Enable PD communications after power supply has fully turned on */ - if (tc[port].pd_enable == 0 && - get_time().val > tc[port].timeout) { - - tc[port].pd_enable = 1; - tc[port].timeout = 0; - } - - if (tc[port].pd_enable == 0) - return; - - /* - * Handle Hard Reset from Policy Engine - */ - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET)) { - if (get_time().val < tc[port].timeout) - return; - /* - * This function clears TC_FLAGS_HARD_RESET - * when the hard reset is complete. - */ - tc_perform_src_hard_reset(port); - } -#endif - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - if (tc[port].polarity) - cc1 = cc2; - - if (cc1 == TYPEC_CC_VOLT_OPEN) - new_cc_state = PD_CC_NONE; - else - new_cc_state = PD_CC_UFP_ATTACHED; - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_SRC_DISCONNECT; - } - - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * 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 && - !TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS) && - !TC_CHK_FLAG(port, TC_FLAGS_DISC_IDENT_IN_PROGRESS)) { - - if (IS_ENABLED(CONFIG_USB_PE_SM)) - if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - pd_dfp_exit_mode(port, 0, 0); - - tc[port].pd_enable = 0; - set_state_tc(port, IS_ENABLED(CONFIG_USB_PD_TRY_SRC) ? - TC_TRY_WAIT_SNK : TC_UNATTACHED_SNK); - } - -#ifdef CONFIG_USB_PE_SM - /* - * PD swap commands - */ - if (tc[port].pd_enable && prl_is_running(port)) { - /* - * Power Role Swap Request - */ - if (TC_CHK_FLAG(port, TC_FLAGS_DO_PR_SWAP)) { - TC_CLR_FLAG(port, TC_FLAGS_DO_PR_SWAP); - 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); - } - - if (IS_ENABLED(CONFIG_USBC_VCONN)) { - /* - * 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_CTVPD_DETECTED)) { - TC_CLR_FLAG(port, TC_FLAGS_CTVPD_DETECTED); - - /* Clear TC_FLAGS_DISC_IDENT_IN_PROGRESS */ - TC_CLR_FLAG(port, TC_FLAGS_DISC_IDENT_IN_PROGRESS); - - set_state_tc(port, TC_CT_UNATTACHED_SNK); - } - } -#endif -} - -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); - - /* Disable VCONN if not power role swapping */ - if (TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON) && - !TC_CHK_FLAG(port, TC_FLAGS_DO_PR_SWAP)) - set_vconn(port, 0); - - /* Clear PR swap flag after checking for Vconn */ - TC_CLR_FLAG(port, TC_FLAGS_DO_PR_SWAP); -} - -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE -/** - * DrpAutoToggle - */ -static void tc_drp_auto_toggle_entry(const int port) -{ - print_current_state(port); - - /* - * The PD_EXIT_LOW_POWER_EVENT_MASK flag may have been set - * due to a CC event. Clear it now since we haven't engaged - * low power mode. - */ - atomic_clear(task_get_event_bitmap(task_get_current()), - PD_EXIT_LOW_POWER_EVENT_MASK); - - if (drp_state[port] == PD_DRP_TOGGLE_ON) - tcpm_enable_drp_toggle(port); -} - -static void tc_drp_auto_toggle_run(const int port) -{ - enum pd_drp_next_states next_state; - enum tcpc_cc_voltage_status cc1, cc2; - - /* - * If SW decided we should be in a low power state and - * the CC lines did not change, then don't talk with the - * TCPC otherwise we might wake it up. - */ - if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER)) { - if (TC_CHK_FLAG(port, TC_FLAGS_LPM_REQUESTED) && - !TC_CHK_FLAG(port, TC_FLAGS_WAKE_FROM_LPM)) { - if (get_time().val > tc[port].low_power_time) - set_state_tc(port, TC_LOW_POWER_MODE); - return; - } - } - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - tc[port].drp_sink_time = get_time().val; - next_state = drp_auto_toggle_next_state(&tc[port].drp_sink_time, - tc[port].power_role, drp_state[port], cc1, cc2); - - /* - * The next state is not determined just by what is - * attached, but also depends on DRP_STATE. Regardless - * of next state, if nothing is attached, then always - * request low power mode. - */ - if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER)) { - if (cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_OPEN && - !tc[port].tasks_preventing_lpm) { - TC_SET_FLAG(port, TC_FLAGS_LPM_REQUESTED); - TC_CLR_FLAG(port, TC_FLAGS_WAKE_FROM_LPM); - } - } - - switch (next_state) { - case DRP_TC_DEFAULT: - set_state_tc(port, PD_DEFAULT_STATE(port)); - break; - case DRP_TC_UNATTACHED_SNK: - set_state_tc(port, TC_UNATTACHED_SNK); - break; - case DRP_TC_UNATTACHED_SRC: - set_state_tc(port, TC_UNATTACHED_SRC); - break; - case DRP_TC_DRP_AUTO_TOGGLE: - /* - * We are staying in PD_STATE_DRP_AUTO_TOGGLE - */ - break; - } -} -#endif /* CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE */ - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER -static void tc_low_power_mode_entry(const int port) -{ - print_current_state(port); - CPRINTS("TCPC p%d Enter Low Power Mode", port); - tcpm_enter_low_power_mode(port); - TC_SET_FLAG(port, TC_FLAGS_LPM_ENGAGED); -} - -static void tc_low_power_mode_run(const int port) -{ -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - if (TC_CHK_FLAG(port, TC_FLAGS_WAKE_FROM_LPM | - TC_FLAGS_POWER_STATE_CHANGE)) { - set_state_tc(port, TC_DRP_AUTO_TOGGLE); - return; - } -#endif - tc_pause_event_loop(port); -} - -static void tc_low_power_mode_exit(const int port) -{ - CPRINTS("TCPC p%d Exit Low Power Mode", port); - TC_CLR_FLAG(port, TC_FLAGS_LPM_REQUESTED | TC_FLAGS_LPM_ENGAGED | - TC_FLAGS_WAKE_FROM_LPM | TC_FLAGS_POWER_STATE_CHANGE); - reset_device_and_notify(port); - tc_start_event_loop(port); -} -#endif - - -/** - * Try.SRC - * - * 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; - tc[port].try_wait_debounce = get_time().val + PD_T_DRP_TRY; - tc[port].timeout = get_time().val + PD_T_TRY_TIMEOUT; -} - -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; - tc[port].cc_debounce = get_time().val + 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 (get_time().val > tc[port].cc_debounce) { - if (new_cc_state == PD_CC_UFP_ATTACHED) - 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 ((get_time().val > tc[port].try_wait_debounce && - !pd_is_vbus_present(port)) || - get_time().val > tc[port].timeout) { - set_state_tc(port, TC_TRY_WAIT_SNK); - } - } -} - -/** - * 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[port].cc_state = PD_CC_UNSET; - tc[port].try_wait_debounce = get_time().val + PD_T_CC_DEBOUNCE; -} - -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; - tc[port].pd_debounce = get_time().val + 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 ((get_time().val > tc[port].pd_debounce) && - (new_cc_state == PD_CC_NONE)) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* - * The port shall transition to Attached.SNK after tCCDebounce if or - * when VBUS is detected. - */ - if (get_time().val > tc[port].try_wait_debounce && - pd_is_vbus_present(port)) - set_state_tc(port, TC_ATTACHED_SNK); -} - -#endif - -#if defined(CONFIG_USB_PE_SM) -/* - * CTUnattached.SNK - */ -static void tc_ct_unattached_snk_entry(int port) -{ - print_current_state(port); - - /* - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rd. - */ - tcpm_select_rp_value(port, CONFIG_USB_PD_PULLUP); - tcpm_set_cc(port, TYPEC_CC_RD); - 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[port].pd_enable = 0; - - tc[port].timeout = get_time().val + PD_POWER_SUPPLY_TURN_ON_DELAY; -} - -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 (tc[port].timeout > 0 && get_time().val > tc[port].timeout) { - tc[port].pd_enable = 1; - tc[port].timeout = 0; - } - - if (tc[port].timeout > 0) - 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)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET); - /* 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; - tc[port].cc_debounce = get_time().val + 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. - */ - if (get_time().val > tc[port].cc_debounce) { - if (new_cc_state == PD_CC_NONE && !pd_is_vbus_present(port)) { - if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - pd_dfp_exit_mode(port, 0, 0); - - 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); -} - -/** - * CTAttached.SNK - */ -static void tc_ct_attached_snk_entry(int port) -{ - print_current_state(port); - - /* The port shall reject a VCONN swap request. */ - TC_SET_FLAG(port, TC_FLAGS_REJECT_VCONN_SWAP); -} - -static void tc_ct_attached_snk_run(int port) -{ - /* - * Hard Reset is sent when the PE layer is disabled due to a - * CTVPD connection. - */ - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET); - /* 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_is_vbus_present(port)) { - 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); -} - -static void tc_ct_attached_snk_exit(int port) -{ - /* Stop drawing power */ - sink_stop_drawing_current(port); - - TC_CLR_FLAG(port, TC_FLAGS_REJECT_VCONN_SWAP); -} -#endif /* CONFIG_USB_PE_SM */ - -/** - * 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); - - /* - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rd. - */ - tcpm_set_cc(port, TYPEC_CC_RD); -} - - -/** - * 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); - - /* - * Both CC1 and CC2 pins shall be independently pulled - * up through Rp. - */ - tcpm_select_rp_value(port, CONFIG_USB_PD_PULLUP); - tcpm_set_cc(port, TYPEC_CC_RP); -} - -/** - * Super State CC_OPEN - */ -static void tc_cc_open_entry(const int port) -{ - /* Disable VBUS */ - pd_power_supply_reset(port); - - /* Disable VCONN */ - if (TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON)) - set_vconn(port, 0); - - /* Remove terminations from CC */ - tcpm_set_cc(port, TYPEC_CC_OPEN); - - if (IS_ENABLED(CONFIG_USBC_PPC)) { - /* There is no sink connected. */ - ppc_sink_is_connected(port, 0); - - /* - * Clear the overcurrent event counter - * since we've detected a disconnect. - */ - ppc_clear_oc_event_counter(port); - } -} - -void tc_run(const int port) -{ - run_state(port, &tc[port].ctx); -} - -/** - * This function checks the current CC status of the port partner - * and returns true if the attached partner is UFP. - */ -int pd_partner_is_ufp(int port) -{ - return tc[port].cc_state == PD_CC_UFP_ATTACHED || - tc[port].cc_state == PD_CC_UFP_DEBUG_ACC || - tc[port].cc_state == PD_CC_UFP_AUDIO_ACC; -} - -int pd_is_debug_acc(int port) -{ - return tc[port].cc_state == PD_CC_UFP_DEBUG_ACC || - tc[port].cc_state == PD_CC_DFP_DEBUG_ACC; -} - -static void pd_chipset_resume(void) -{ - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) { - pd_set_dual_role(i, PD_DRP_TOGGLE_ON); - task_set_event(PD_PORT_TO_TASK_ID(i), - PD_EVENT_POWER_STATE_CHANGE, 0); - } - - CPRINTS("PD:S3->S0"); -} -DECLARE_HOOK(HOOK_CHIPSET_RESUME, pd_chipset_resume, HOOK_PRIO_DEFAULT); - -static void pd_chipset_suspend(void) -{ - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) { - pd_set_dual_role(i, PD_DRP_TOGGLE_OFF); - task_set_event(PD_PORT_TO_TASK_ID(i), - PD_EVENT_POWER_STATE_CHANGE, 0); - } - - CPRINTS("PD:S0->S3"); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, pd_chipset_suspend, HOOK_PRIO_DEFAULT); - -static void pd_chipset_startup(void) -{ - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) { - pd_set_dual_role_no_wakeup(i, PD_DRP_TOGGLE_OFF); - task_set_event(PD_PORT_TO_TASK_ID(i), - PD_EVENT_POWER_STATE_CHANGE | - PD_EVENT_UPDATE_DUAL_ROLE, - 0); - } - - CPRINTS("PD:S5->S3"); -} -DECLARE_HOOK(HOOK_CHIPSET_STARTUP, pd_chipset_startup, HOOK_PRIO_DEFAULT); - -static void pd_chipset_shutdown(void) -{ - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) { - pd_set_dual_role_no_wakeup(i, PD_DRP_FORCE_SINK); - task_set_event(PD_PORT_TO_TASK_ID(i), - PD_EVENT_POWER_STATE_CHANGE | - PD_EVENT_UPDATE_DUAL_ROLE, - 0); - } - - CPRINTS("PD:S3->S5"); -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pd_chipset_shutdown, HOOK_PRIO_DEFAULT); - - -/* - * Type-C State Hierarchy (Sub-States are listed inside the boxes) - * - * |TC_UNATTACHED ---------| - * | | - * | TC_UNATTACHED_SNK | - * | TC_UNATTACHED_SRC | - * |-----------------------| - * - * |TC_CC_RD --------------| |TC_CC_RP ------------------------| - * | | | | - * | TC_ATTACH_WAIT_SNK | | TC_ATTACH_WAIT_SRC | - * | TC_TRY_WAIT_SNK | | TC_TRY_SRC | - * | TC_DBG_ACC_SNK | | TC_UNORIENTED_DBG_ACC_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 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, - .parent = &tc_states[TC_CC_OPEN], - }, - [TC_UNATTACHED_SNK] = { - .entry = tc_unattached_snk_entry, - .run = tc_unattached_snk_run, - .parent = &tc_states[TC_CC_RD], - }, - [TC_ATTACH_WAIT_SNK] = { - .entry = tc_attach_wait_snk_entry, - .run = tc_attach_wait_snk_run, - .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_UNORIENTED_DBG_ACC_SRC] = { - .entry = tc_unoriented_dbg_acc_src_entry, - .run = tc_unoriented_dbg_acc_src_run, - .exit = tc_unoriented_dbg_acc_src_exit, - .parent = &tc_states[TC_CC_RP], - }, - [TC_DBG_ACC_SNK] = { - .entry = tc_dbg_acc_snk_entry, - .run = tc_dbg_acc_snk_run, - .exit = tc_dbg_acc_snk_exit, - .parent = &tc_states[TC_CC_RD], - }, - [TC_UNATTACHED_SRC] = { - .entry = tc_unattached_src_entry, - .run = tc_unattached_src_run, - .parent = &tc_states[TC_CC_RP], - }, - [TC_ATTACH_WAIT_SRC] = { - .entry = tc_attach_wait_src_entry, - .run = tc_attach_wait_src_run, - .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, - .parent = &tc_states[TC_CC_RP], - }, - [TC_TRY_WAIT_SNK] = { - .entry = tc_try_wait_snk_entry, - .run = tc_try_wait_snk_run, - .parent = &tc_states[TC_CC_RD], - }, -#endif /* CONFIG_USB_PD_TRY_SRC */ -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - [TC_DRP_AUTO_TOGGLE] = { - .entry = tc_drp_auto_toggle_entry, - .run = tc_drp_auto_toggle_run, - }, -#endif /* CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE */ -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - [TC_LOW_POWER_MODE] = { - .entry = tc_low_power_mode_entry, - .run = tc_low_power_mode_run, - .exit = tc_low_power_mode_exit, - }, -#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ -#ifdef CONFIG_USB_PE_SM - [TC_CT_UNATTACHED_SNK] = { - .entry = tc_ct_unattached_snk_entry, - .run = tc_ct_unattached_snk_run, - }, - [TC_CT_ATTACHED_SNK] = { - .entry = tc_ct_attached_snk_entry, - .run = tc_ct_attached_snk_run, - .exit = tc_ct_attached_snk_exit, - }, -#endif -}; - -#ifdef TEST_BUILD -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 |