summaryrefslogtreecommitdiff
path: root/common/usbc/usb_pe_drp_sm.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/usbc/usb_pe_drp_sm.c')
-rw-r--r--common/usbc/usb_pe_drp_sm.c7486
1 files changed, 0 insertions, 7486 deletions
diff --git a/common/usbc/usb_pe_drp_sm.c b/common/usbc/usb_pe_drp_sm.c
deleted file mode 100644
index 096f689b0a..0000000000
--- a/common/usbc/usb_pe_drp_sm.c
+++ /dev/null
@@ -1,7486 +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 "atomic.h"
-#include "battery.h"
-#include "battery_smart.h"
-#include "charge_manager.h"
-#include "charge_state.h"
-#include "common.h"
-#include "console.h"
-#include "dps.h"
-#include "driver/tcpm/tcpm.h"
-#include "ec_commands.h"
-#include "hooks.h"
-#include "host_command.h"
-#include "stdbool.h"
-#include "system.h"
-#include "task.h"
-#include "tcpm/tcpm.h"
-#include "util.h"
-#include "usb_common.h"
-#include "usb_dp_alt_mode.h"
-#include "usb_mode.h"
-#include "usb_pd_dpm.h"
-#include "usb_pd_policy.h"
-#include "usb_pd.h"
-#include "usb_pd_tcpm.h"
-#include "usb_pd_timer.h"
-#include "usb_pe_sm.h"
-#include "usb_tbt_alt_mode.h"
-#include "usb_prl_sm.h"
-#include "usb_tc_sm.h"
-#include "usb_emsg.h"
-#include "usb_sm.h"
-#include "usbc_ppc.h"
-
-/*
- * USB Policy Engine Sink / Source module
- *
- * Based on Revision 3.0, Version 1.2 of
- * the USB Power Delivery Specification.
- */
-
-#ifdef CONFIG_COMMON_RUNTIME
-#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
-#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args)
-#else
-#define CPRINTF(format, args...)
-#define CPRINTS(format, args...)
-#endif
-
-#define CPRINTF_LX(x, format, args...) \
- do { \
- if (pe_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 (pe_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 PE_SET_FLAG(port, flag) atomic_or(&pe[port].flags, (flag))
-#define PE_CLR_FLAG(port, flag) atomic_clear_bits(&pe[port].flags, (flag))
-#define PE_CHK_FLAG(port, flag) (pe[port].flags & (flag))
-
-/*
- * These macros SET, CLEAR, and CHECK, a DPM (Device Policy Manager)
- * Request. The Requests are listed in usb_pe_sm.h.
- */
-#define PE_SET_DPM_REQUEST(port, req) atomic_or(&pe[port].dpm_request, (req))
-#define PE_CLR_DPM_REQUEST(port, req) \
- atomic_clear_bits(&pe[port].dpm_request, (req))
-#define PE_CHK_DPM_REQUEST(port, req) (pe[port].dpm_request & (req))
-
-/*
- * Policy Engine Layer Flags
- * These are reproduced in test/usb_pe.h. If they change here, they must change
- * there.
- */
-
-/* At least one successful PD communication packet received from port partner */
-#define PE_FLAGS_PD_CONNECTION BIT(0)
-/* Accept message received from port partner */
-#define PE_FLAGS_ACCEPT BIT(1)
-/* Power Supply Ready message received from port partner */
-#define PE_FLAGS_PS_READY BIT(2)
-/* Protocol Error was determined based on error recovery current state */
-#define PE_FLAGS_PROTOCOL_ERROR BIT(3)
-/* Set if we are in Modal Operation */
-#define PE_FLAGS_MODAL_OPERATION BIT(4)
-/* A message we requested to be sent has been transmitted */
-#define PE_FLAGS_TX_COMPLETE BIT(5)
-/* A message sent by a port partner has been received */
-#define PE_FLAGS_MSG_RECEIVED BIT(6)
-/* A hard reset has been requested but has not been sent, not currently used */
-#define PE_FLAGS_HARD_RESET_PENDING BIT(7)
-/* Port partner sent a Wait message. Wait before we resend our message */
-#define PE_FLAGS_WAIT BIT(8)
-/* An explicit contract is in place with our port partner */
-#define PE_FLAGS_EXPLICIT_CONTRACT BIT(9)
-/* Waiting for Sink Capabailities timed out. Used for retry error handling */
-#define PE_FLAGS_SNK_WAIT_CAP_TIMEOUT BIT(10)
-/* Power Supply voltage/current transition timed out */
-#define PE_FLAGS_PS_TRANSITION_TIMEOUT BIT(11)
-/* Flag to note current Atomic Message Sequence is interruptible */
-#define PE_FLAGS_INTERRUPTIBLE_AMS BIT(12)
-/* Flag to note Power Supply reset has completed */
-#define PE_FLAGS_PS_RESET_COMPLETE BIT(13)
-/* VCONN swap operation has completed */
-#define PE_FLAGS_VCONN_SWAP_COMPLETE BIT(14)
-/* Flag to note no more setup VDMs (discovery, etc.) should be sent */
-#define PE_FLAGS_VDM_SETUP_DONE BIT(15)
-/* Flag to note PR Swap just completed for Startup entry */
-#define PE_FLAGS_PR_SWAP_COMPLETE BIT(16)
-/* Flag to note Port Discovery port partner replied with BUSY */
-#define PE_FLAGS_VDM_REQUEST_BUSY BIT(17)
-/* Flag to note Port Discovery port partner replied with NAK */
-#define PE_FLAGS_VDM_REQUEST_NAKED BIT(18)
-/* Flag to note FRS/PRS context in shared state machine path */
-#define PE_FLAGS_FAST_ROLE_SWAP_PATH BIT(19)
-/* Flag to note if FRS listening is enabled */
-#define PE_FLAGS_FAST_ROLE_SWAP_ENABLED BIT(20)
-/* Flag to note TCPC passed on FRS signal from port partner */
-#define PE_FLAGS_FAST_ROLE_SWAP_SIGNALED BIT(21)
-/* TODO: POLICY decision: Triggers a DR SWAP attempt from UFP to DFP */
-#define PE_FLAGS_DR_SWAP_TO_DFP BIT(22)
-/*
- * TODO: POLICY decision
- * Flag to trigger a message resend after receiving a WAIT from port partner
- */
-#define PE_FLAGS_WAITING_PR_SWAP BIT(23)
-/* FLAG is set when an AMS is initiated locally. ie. AP requested a PR_SWAP */
-#define PE_FLAGS_LOCALLY_INITIATED_AMS BIT(24)
-/* Flag to note the first message sent in PE_SRC_READY and PE_SNK_READY */
-#define PE_FLAGS_FIRST_MSG BIT(25)
-/* Flag to continue a VDM request if it was interrupted */
-#define PE_FLAGS_VDM_REQUEST_CONTINUE BIT(26)
-/* TODO: POLICY decision: Triggers a Vconn SWAP attempt to on */
-#define PE_FLAGS_VCONN_SWAP_TO_ON BIT(27)
-/* FLAG to track that VDM request to port partner timed out */
-#define PE_FLAGS_VDM_REQUEST_TIMEOUT BIT(28)
-/* FLAG to note message was discarded due to incoming message */
-#define PE_FLAGS_MSG_DISCARDED BIT(29)
-/* FLAG to note that hard reset can't be performed due to battery low */
-#define PE_FLAGS_SNK_WAITING_BATT BIT(30)
-
-/* Message flags which should not persist on returning to ready state */
-#define PE_FLAGS_READY_CLR (PE_FLAGS_LOCALLY_INITIATED_AMS \
- | PE_FLAGS_MSG_DISCARDED \
- | PE_FLAGS_VDM_REQUEST_TIMEOUT \
- | PE_FLAGS_INTERRUPTIBLE_AMS)
-
-/*
- * Combination to check whether a reply to a message was received. Our message
- * should have sent (i.e. not been discarded) and a partner message is ready to
- * process.
- *
- * When chunking is disabled (ex. for PD 2.0), these flags will set
- * on the same run cycle. With chunking, received message will take an
- * additional cycle to be flagged.
- */
-#define PE_CHK_REPLY(port) (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED) && \
- !PE_CHK_FLAG(port, PE_FLAGS_MSG_DISCARDED))
-
-/* 6.7.3 Hard Reset Counter */
-#define N_HARD_RESET_COUNT 2
-
-/* 6.7.4 Capabilities Counter */
-#define N_CAPS_COUNT 25
-
-/* 6.7.5 Discover Identity Counter */
-/*
- * NOTE: The Protocol Layer tries to send a message 3 time before giving up,
- * so a Discover Identity SOP' message will be sent 3*6 = 18 times (slightly
- * less than spec maximum of 20). This counter applies only to cable plug
- * discovery.
- */
-#define N_DISCOVER_IDENTITY_COUNT 6
-
-/*
- * It is permitted to send SOP' Discover Identity messages before a PD contract
- * is in place. However, this is only beneficial if the cable powers up quickly
- * solely from VCONN. Limit the number of retries without a contract to
- * ensure we attempt some cable discovery after a contract is in place.
- */
-#define N_DISCOVER_IDENTITY_PRECONTRACT_LIMIT 2
-
-/*
- * Once this limit of SOP' Discover Identity messages has been set, downgrade
- * to PD 2.0 in case the cable is non-compliant about GoodCRC-ing higher
- * revisions. This limit should be higher than the precontract limit.
- */
-#define N_DISCOVER_IDENTITY_PD3_0_LIMIT 4
-
-/*
- * tDiscoverIdentity is only defined while an explicit contract is in place, so
- * extend the interval between retries pre-contract.
- */
-#define PE_T_DISCOVER_IDENTITY_NO_CONTRACT (200*MSEC)
-
-/*
- * Only VCONN source can communicate with the cable plug. Hence, try VCONN swap
- * 3 times before giving up.
- *
- * Note: This is not a part of power delivery specification
- */
-#define N_VCONN_SWAP_COUNT 3
-
-/*
- * Counter to track how many times to attempt SRC to SNK PR swaps before giving
- * up.
- *
- * Note: This is not a part of power delivery specification
- */
-#define N_SNK_SRC_PR_SWAP_COUNT 5
-
-/*
- * ChromeOS policy:
- * For PD2.0, We must be DFP before sending Discover Identity message
- * to the port partner. Attempt to DR SWAP from UFP to DFP
- * N_DR_SWAP_ATTEMPT_COUNT times before giving up on sending a
- * Discover Identity message.
- */
-#define N_DR_SWAP_ATTEMPT_COUNT 5
-
-#define TIMER_DISABLED 0xffffffffffffffff /* Unreachable time in future */
-
-/*
- * The time that we allow the port partner to send any messages after an
- * explicit contract is established. 200ms was chosen somewhat arbitrarily as
- * it should be long enough for sources to decide to send a message if they were
- * going to, but not so long that a "low power charger connected" notification
- * would be shown in the chrome OS UI. Setting t0o large a delay can cause
- * problems if the PD discovery time exceeds 1s (tAMETimeout)
- */
-#define SRC_SNK_READY_HOLD_OFF_US (200 * MSEC)
-
-/*
- * Function pointer to a Structured Vendor Defined Message (SVDM) response
- * function defined in the board's usb_pd_policy.c file.
- */
-typedef int (*svdm_rsp_func)(int port, uint32_t *payload);
-
-/* List of all Policy Engine level states */
-enum usb_pe_state {
- /* Super States */
- PE_PRS_FRS_SHARED,
- PE_VDM_SEND_REQUEST,
-
- /* Normal States */
- PE_SRC_STARTUP,
- PE_SRC_DISCOVERY,
- PE_SRC_SEND_CAPABILITIES,
- PE_SRC_NEGOTIATE_CAPABILITY,
- PE_SRC_TRANSITION_SUPPLY,
- PE_SRC_READY,
- PE_SRC_DISABLED,
- PE_SRC_CAPABILITY_RESPONSE,
- PE_SRC_HARD_RESET,
- PE_SRC_HARD_RESET_RECEIVED,
- PE_SRC_TRANSITION_TO_DEFAULT,
- PE_SNK_STARTUP,
- PE_SNK_DISCOVERY,
- PE_SNK_WAIT_FOR_CAPABILITIES,
- PE_SNK_EVALUATE_CAPABILITY,
- PE_SNK_SELECT_CAPABILITY,
- PE_SNK_READY,
- PE_SNK_HARD_RESET,
- PE_SNK_TRANSITION_TO_DEFAULT,
- PE_SNK_GIVE_SINK_CAP,
- PE_SNK_GET_SOURCE_CAP,
- PE_SNK_TRANSITION_SINK,
- PE_SEND_SOFT_RESET,
- PE_SOFT_RESET,
- PE_SEND_NOT_SUPPORTED,
- PE_SRC_PING,
- PE_DRS_EVALUATE_SWAP,
- PE_DRS_CHANGE,
- PE_DRS_SEND_SWAP,
- PE_PRS_SRC_SNK_EVALUATE_SWAP,
- PE_PRS_SRC_SNK_TRANSITION_TO_OFF,
- PE_PRS_SRC_SNK_ASSERT_RD,
- PE_PRS_SRC_SNK_WAIT_SOURCE_ON,
- PE_PRS_SRC_SNK_SEND_SWAP,
- PE_PRS_SNK_SRC_EVALUATE_SWAP,
- PE_PRS_SNK_SRC_TRANSITION_TO_OFF,
- PE_PRS_SNK_SRC_ASSERT_RP,
- PE_PRS_SNK_SRC_SOURCE_ON,
- PE_PRS_SNK_SRC_SEND_SWAP,
- PE_VCS_EVALUATE_SWAP,
- PE_VCS_SEND_SWAP,
- PE_VCS_WAIT_FOR_VCONN_SWAP,
- PE_VCS_TURN_ON_VCONN_SWAP,
- PE_VCS_TURN_OFF_VCONN_SWAP,
- PE_VCS_SEND_PS_RDY_SWAP,
- PE_VCS_CBL_SEND_SOFT_RESET,
- PE_VDM_IDENTITY_REQUEST_CBL,
- PE_INIT_PORT_VDM_IDENTITY_REQUEST,
- PE_INIT_VDM_SVIDS_REQUEST,
- PE_INIT_VDM_MODES_REQUEST,
- PE_VDM_REQUEST_DPM,
- PE_VDM_RESPONSE,
- PE_HANDLE_CUSTOM_VDM_REQUEST,
- PE_WAIT_FOR_ERROR_RECOVERY,
- PE_BIST_TX,
- PE_DEU_SEND_ENTER_USB,
- PE_DR_GET_SINK_CAP,
- PE_DR_SNK_GIVE_SOURCE_CAP,
- PE_DR_SRC_GET_SOURCE_CAP,
-
- /* PD3.0 only states below here*/
- PE_FRS_SNK_SRC_START_AMS,
- PE_GIVE_BATTERY_CAP,
- PE_GIVE_BATTERY_STATUS,
- PE_SEND_ALERT,
- PE_SRC_CHUNK_RECEIVED,
- PE_SNK_CHUNK_RECEIVED,
- PE_VCS_FORCE_VCONN,
-};
-
-/*
- * The result of a previously sent DPM request; used by PE_VDM_SEND_REQUEST to
- * indicate to child states when they need to handle a response.
- */
-enum vdm_response_result {
- /* The parent state is still waiting for a response. */
- VDM_RESULT_WAITING,
- /*
- * The parent state parsed a message, but there is nothing for the child
- * to handle, e.g. BUSY.
- */
- VDM_RESULT_NO_ACTION,
- /* The parent state processed an ACK response. */
- VDM_RESULT_ACK,
- /*
- * The parent state processed a NAK-like response (NAK, Not Supported,
- * or response timeout.
- */
- VDM_RESULT_NAK,
-};
-
-/* Forward declare the full list of states. This is indexed by usb_pe_state */
-static const struct usb_state pe_states[];
-
-/*
- * 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
-
-/* List of human readable state names for console debugging */
-__maybe_unused static __const_data const char * const pe_state_names[] = {
- /* Super States */
-#ifdef CONFIG_USB_PD_REV30
- [PE_PRS_FRS_SHARED] = "SS:PE_PRS_FRS_SHARED",
-#endif
- [PE_VDM_SEND_REQUEST] = "SS:PE_VDM_Send_Request",
-
- /* Normal States */
- [PE_SRC_STARTUP] = "PE_SRC_Startup",
- [PE_SRC_DISCOVERY] = "PE_SRC_Discovery",
- [PE_SRC_SEND_CAPABILITIES] = "PE_SRC_Send_Capabilities",
- [PE_SRC_NEGOTIATE_CAPABILITY] = "PE_SRC_Negotiate_Capability",
- [PE_SRC_TRANSITION_SUPPLY] = "PE_SRC_Transition_Supply",
- [PE_SRC_READY] = "PE_SRC_Ready",
- [PE_SRC_DISABLED] = "PE_SRC_Disabled",
- [PE_SRC_CAPABILITY_RESPONSE] = "PE_SRC_Capability_Response",
- [PE_SRC_HARD_RESET] = "PE_SRC_Hard_Reset",
- [PE_SRC_HARD_RESET_RECEIVED] = "PE_SRC_Hard_Reset_Received",
- [PE_SRC_TRANSITION_TO_DEFAULT] = "PE_SRC_Transition_to_default",
- [PE_SNK_STARTUP] = "PE_SNK_Startup",
- [PE_SNK_DISCOVERY] = "PE_SNK_Discovery",
- [PE_SNK_WAIT_FOR_CAPABILITIES] = "PE_SNK_Wait_for_Capabilities",
- [PE_SNK_EVALUATE_CAPABILITY] = "PE_SNK_Evaluate_Capability",
- [PE_SNK_SELECT_CAPABILITY] = "PE_SNK_Select_Capability",
- [PE_SNK_READY] = "PE_SNK_Ready",
- [PE_SNK_HARD_RESET] = "PE_SNK_Hard_Reset",
- [PE_SNK_TRANSITION_TO_DEFAULT] = "PE_SNK_Transition_to_default",
- [PE_SNK_GIVE_SINK_CAP] = "PE_SNK_Give_Sink_Cap",
- [PE_SNK_GET_SOURCE_CAP] = "PE_SNK_Get_Source_Cap",
- [PE_SNK_TRANSITION_SINK] = "PE_SNK_Transition_Sink",
- [PE_SEND_SOFT_RESET] = "PE_Send_Soft_Reset",
- [PE_SOFT_RESET] = "PE_Soft_Reset",
- [PE_SEND_NOT_SUPPORTED] = "PE_Send_Not_Supported",
- [PE_SRC_PING] = "PE_SRC_Ping",
- [PE_DRS_EVALUATE_SWAP] = "PE_DRS_Evaluate_Swap",
- [PE_DRS_CHANGE] = "PE_DRS_Change",
- [PE_DRS_SEND_SWAP] = "PE_DRS_Send_Swap",
- [PE_PRS_SRC_SNK_EVALUATE_SWAP] = "PE_PRS_SRC_SNK_Evaluate_Swap",
- [PE_PRS_SRC_SNK_TRANSITION_TO_OFF] = "PE_PRS_SRC_SNK_Transition_To_Off",
- [PE_PRS_SRC_SNK_ASSERT_RD] = "PE_PRS_SRC_SNK_Assert_Rd",
- [PE_PRS_SRC_SNK_WAIT_SOURCE_ON] = "PE_PRS_SRC_SNK_Wait_Source_On",
- [PE_PRS_SRC_SNK_SEND_SWAP] = "PE_PRS_SRC_SNK_Send_Swap",
- [PE_PRS_SNK_SRC_EVALUATE_SWAP] = "PE_PRS_SNK_SRC_Evaluate_Swap",
- [PE_PRS_SNK_SRC_TRANSITION_TO_OFF] = "PE_PRS_SNK_SRC_Transition_To_Off",
- [PE_PRS_SNK_SRC_ASSERT_RP] = "PE_PRS_SNK_SRC_Assert_Rp",
- [PE_PRS_SNK_SRC_SOURCE_ON] = "PE_PRS_SNK_SRC_Source_On",
- [PE_PRS_SNK_SRC_SEND_SWAP] = "PE_PRS_SNK_SRC_Send_Swap",
-#ifdef CONFIG_USBC_VCONN
- [PE_VCS_EVALUATE_SWAP] = "PE_VCS_Evaluate_Swap",
- [PE_VCS_SEND_SWAP] = "PE_VCS_Send_Swap",
- [PE_VCS_WAIT_FOR_VCONN_SWAP] = "PE_VCS_Wait_For_Vconn_Swap",
- [PE_VCS_TURN_ON_VCONN_SWAP] = "PE_VCS_Turn_On_Vconn_Swap",
- [PE_VCS_TURN_OFF_VCONN_SWAP] = "PE_VCS_Turn_Off_Vconn_Swap",
- [PE_VCS_SEND_PS_RDY_SWAP] = "PE_VCS_Send_Ps_Rdy_Swap",
- [PE_VCS_CBL_SEND_SOFT_RESET] = "PE_VCS_CBL_Send_Soft_Reset",
-#endif
- [PE_VDM_IDENTITY_REQUEST_CBL] = "PE_VDM_Identity_Request_Cbl",
- [PE_INIT_PORT_VDM_IDENTITY_REQUEST] =
- "PE_INIT_PORT_VDM_Identity_Request",
- [PE_INIT_VDM_SVIDS_REQUEST] = "PE_INIT_VDM_SVIDs_Request",
- [PE_INIT_VDM_MODES_REQUEST] = "PE_INIT_VDM_Modes_Request",
- [PE_VDM_REQUEST_DPM] = "PE_VDM_Request_DPM",
- [PE_VDM_RESPONSE] = "PE_VDM_Response",
- [PE_HANDLE_CUSTOM_VDM_REQUEST] = "PE_Handle_Custom_Vdm_Request",
- [PE_WAIT_FOR_ERROR_RECOVERY] = "PE_Wait_For_Error_Recovery",
- [PE_BIST_TX] = "PE_Bist_TX",
- [PE_DEU_SEND_ENTER_USB] = "PE_DEU_Send_Enter_USB",
- [PE_DR_GET_SINK_CAP] = "PE_DR_Get_Sink_Cap",
- [PE_DR_SNK_GIVE_SOURCE_CAP] = "PE_DR_SNK_Give_Source_Cap",
- [PE_DR_SRC_GET_SOURCE_CAP] = "PE_DR_SRC_Get_Source_Cap",
-
- /* PD3.0 only states below here*/
-#ifdef CONFIG_USB_PD_REV30
- [PE_FRS_SNK_SRC_START_AMS] = "PE_FRS_SNK_SRC_Start_Ams",
-#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES
- [PE_GIVE_BATTERY_CAP] = "PE_Give_Battery_Cap",
- [PE_GIVE_BATTERY_STATUS] = "PE_Give_Battery_Status",
- [PE_SEND_ALERT] = "PE_Send_Alert",
-#else
- [PE_SRC_CHUNK_RECEIVED] = "PE_SRC_Chunk_Received",
- [PE_SNK_CHUNK_RECEIVED] = "PE_SNK_Chunk_Received",
-#endif
-#ifdef CONFIG_USBC_VCONN
- [PE_VCS_FORCE_VCONN] = "PE_VCS_Force_Vconn",
-#endif
-#endif /* CONFIG_USB_PD_REV30 */
-};
-
-#ifndef CONFIG_USBC_VCONN
-GEN_NOT_SUPPORTED(PE_VCS_EVALUATE_SWAP);
-#define PE_VCS_EVALUATE_SWAP PE_VCS_EVALUATE_SWAP_NOT_SUPPORTED
-GEN_NOT_SUPPORTED(PE_VCS_SEND_SWAP);
-#define PE_VCS_SEND_SWAP PE_VCS_SEND_SWAP_NOT_SUPPORTED
-GEN_NOT_SUPPORTED(PE_VCS_WAIT_FOR_VCONN_SWAP);
-#define PE_VCS_WAIT_FOR_VCONN_SWAP PE_VCS_WAIT_FOR_VCONN_SWAP_NOT_SUPPORTED
-GEN_NOT_SUPPORTED(PE_VCS_TURN_ON_VCONN_SWAP);
-#define PE_VCS_TURN_ON_VCONN_SWAP PE_VCS_TURN_ON_VCONN_SWAP_NOT_SUPPORTED
-GEN_NOT_SUPPORTED(PE_VCS_TURN_OFF_VCONN_SWAP);
-#define PE_VCS_TURN_OFF_VCONN_SWAP PE_VCS_TURN_OFF_VCONN_SWAP_NOT_SUPPORTED
-GEN_NOT_SUPPORTED(PE_VCS_SEND_PS_RDY_SWAP);
-#define PE_VCS_SEND_PS_RDY_SWAP PE_VCS_SEND_PS_RDY_SWAP_NOT_SUPPORTED
-#endif /* CONFIG_USBC_VCONN */
-
-#ifndef CONFIG_USB_PD_REV30
-GEN_NOT_SUPPORTED(PE_FRS_SNK_SRC_START_AMS);
-#define PE_FRS_SNK_SRC_START_AMS PE_FRS_SNK_SRC_START_AMS_NOT_SUPPORTED
-GEN_NOT_SUPPORTED(PE_PRS_FRS_SHARED);
-#define PE_PRS_FRS_SHARED PE_PRS_FRS_SHARED_NOT_SUPPORTED
-GEN_NOT_SUPPORTED(PE_SRC_CHUNK_RECEIVED);
-#define PE_SRC_CHUNK_RECEIVED PE_SRC_CHUNK_RECEIVED_NOT_SUPPORTED
-GEN_NOT_SUPPORTED(PE_SNK_CHUNK_RECEIVED);
-#define PE_SNK_CHUNK_RECEIVED PE_SNK_CHUNK_RECEIVED_NOT_SUPPORTED
-#endif /* CONFIG_USB_PD_REV30 */
-
-#if !defined(CONFIG_USBC_VCONN) || !defined(CONFIG_USB_PD_REV30)
-GEN_NOT_SUPPORTED(PE_VCS_FORCE_VCONN);
-#define PE_VCS_FORCE_VCONN PE_VCS_FORCE_VCONN_NOT_SUPPORTED
-#endif
-
-#ifndef CONFIG_USB_PD_EXTENDED_MESSAGES
-GEN_NOT_SUPPORTED(PE_GIVE_BATTERY_CAP);
-#define PE_GIVE_BATTERY_CAP PE_GIVE_BATTERY_CAP_NOT_SUPPORTED
-GEN_NOT_SUPPORTED(PE_GIVE_BATTERY_STATUS);
-#define PE_GIVE_BATTERY_STATUS PE_GIVE_BATTERY_STATUS_NOT_SUPPORTED
-GEN_NOT_SUPPORTED(PE_SEND_ALERT);
-#define PE_SEND_ALERT PE_SEND_ALERT_NOT_SUPPORTED
-#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */
-
-#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES
-GEN_NOT_SUPPORTED(PE_SRC_CHUNK_RECEIVED);
-#define PE_SRC_CHUNK_RECEIVED PE_SRC_CHUNK_RECEIVED_NOT_SUPPORTED
-GEN_NOT_SUPPORTED(PE_SNK_CHUNK_RECEIVED);
-#define PE_SNK_CHUNK_RECEIVED PE_SNK_CHUNK_RECEIVED_NOT_SUPPORTED
-#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */
-
-static enum sm_local_state local_state[CONFIG_USB_PD_PORT_MAX_COUNT];
-
-/*
- * Common message send checking
- *
- * PE_MSG_SEND_PENDING: A message has been requested to be sent. It has
- * not been GoodCRCed or Discarded.
- * PE_MSG_SEND_COMPLETED: The message that was requested has been sent.
- * This will only be returned one time and any other
- * request for message send status will just return
- * PE_MSG_SENT. This message actually includes both
- * The COMPLETED and the SENT bit for easier checking.
- * NOTE: PE_MSG_SEND_COMPLETED will only be returned
- * a single time, directly after TX_COMPLETE.
- * PE_MSG_SENT: The message that was requested to be sent has
- * successfully been transferred to the partner.
- * PE_MSG_DISCARDED: The message that was requested to be sent was
- * discarded. The partner did not receive it.
- * NOTE: PE_MSG_DISCARDED will only be returned
- * one time and it is up to the caller to process
- * what ever is needed to handle the Discard.
- * PE_MSG_DPM_DISCARDED: The message that was requested to be sent was
- * discarded and an active DRP_REQUEST was active.
- * The DRP_REQUEST that was current will be moved
- * back to the drp_requests so it can be performed
- * later if needed.
- * NOTE: PE_MSG_DPM_DISCARDED will only be returned
- * one time and it is up to the caller to process
- * what ever is needed to handle the Discard.
- */
-enum pe_msg_check {
- PE_MSG_SEND_PENDING = BIT(0),
- PE_MSG_SENT = BIT(1),
- PE_MSG_DISCARDED = BIT(2),
-
- PE_MSG_SEND_COMPLETED = BIT(3) | PE_MSG_SENT,
- PE_MSG_DPM_DISCARDED = BIT(4) | PE_MSG_DISCARDED,
-};
-static void pe_sender_response_msg_entry(const int port);
-static enum pe_msg_check pe_sender_response_msg_run(const int port);
-static void pe_sender_response_msg_exit(const int port);
-
-/* Debug log level - higher number == more log */
-#ifdef CONFIG_USB_PD_DEBUG_LEVEL
-static const enum debug_level pe_debug_level = CONFIG_USB_PD_DEBUG_LEVEL;
-#else
-static enum debug_level pe_debug_level = DEBUG_LEVEL_1;
-#endif
-
-/*
- * Policy Engine State Machine Object
- */
-static struct policy_engine {
- /* 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;
- /* state machine flags */
- uint32_t flags;
- /* Device Policy Manager Request */
- uint32_t dpm_request;
- uint32_t dpm_curr_request;
- /* last requested voltage PDO index */
- int requested_idx;
-
- /*
- * Port events - PD_STATUS_EVENT_* values
- * Set from PD task but may be cleared by host command
- */
- uint32_t events;
-
- /* port address where soft resets are sent */
- enum tcpci_msg_type soft_reset_sop;
-
- /* Current limit / voltage based on the last request message */
- uint32_t curr_limit;
- uint32_t supply_voltage;
-
- /* PD_VDO_INVALID is used when there is an invalid VDO */
- int32_t ama_vdo;
- int32_t vpd_vdo;
- /* Alternate mode discovery results */
- struct pd_discovery discovery[DISCOVERY_TYPE_COUNT];
- /* Active alternate modes */
- struct partner_active_modes partner_amodes[AMODE_TYPE_COUNT];
-
- /* Partner type to send */
- enum tcpci_msg_type tx_type;
-
- /* VDM - used to send information to shared VDM Request state */
- uint32_t vdm_cnt;
- uint32_t vdm_data[VDO_HDR_SIZE + VDO_MAX_SIZE];
- uint8_t vdm_ack_min_data_objects;
-
- /* Counters */
-
- /*
- * This counter is used to retry the Hard Reset whenever there is no
- * response from the remote device.
- */
- uint32_t hard_reset_counter;
-
- /*
- * This counter is used to count the number of Source_Capabilities
- * Messages which have been sent by a Source at power up or after a
- * Hard Reset.
- */
- uint32_t caps_counter;
-
- /*
- * This counter maintains a count of Discover Identity Messages sent
- * to a cable. If no GoodCRC messages are received after
- * nDiscoverIdentityCount, the port shall not send any further
- * SOP'/SOP'' messages.
- */
- uint32_t discover_identity_counter;
- /*
- * For PD2.0, we need to be a DFP before sending a discovery identity
- * message to our port partner. This counter keeps track of how
- * many attempts to DR SWAP from UFP to DFP.
- */
- uint32_t dr_swap_attempt_counter;
-
- /*
- * This counter tracks how many PR Swap messages are sent when the
- * partner responds with a Wait message. Only used during SRC to SNK
- * PR swaps
- */
- uint8_t src_snk_pr_swap_counter;
-
- /*
- * This counter maintains a count of VCONN swap requests. If VCONN swap
- * isn't successful after N_VCONN_SWAP_COUNT, the port calls
- * dpm_vdm_naked().
- */
- uint8_t vconn_swap_counter;
-
- /* Last received source cap */
- uint32_t src_caps[PDO_MAX_OBJECTS];
- int src_cap_cnt; /* -1 on error retrieving source caps */
-
- /* Last received sink cap */
- uint32_t snk_caps[PDO_MAX_OBJECTS];
- int snk_cap_cnt;
-
- /* 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_image current_image;
-} pe[CONFIG_USB_PD_PORT_MAX_COUNT];
-
-test_export_static enum usb_pe_state get_state_pe(const int port);
-test_export_static void set_state_pe(const int port,
- const enum usb_pe_state new_state);
-static void pe_set_dpm_curr_request(const int port, const int request);
-/*
- * The spec. revision is used to index into this array.
- * PD 1.0 (VDO 1.0) - return VDM_VER10
- * PD 2.0 (VDO 1.0) - return VDM_VER10
- * PD 3.0 (VDO 2.0) - return VDM_VER20
- */
-static const uint8_t vdo_ver[] = {
- [PD_REV10] = VDM_VER10,
- [PD_REV20] = VDM_VER10,
- [PD_REV30] = VDM_VER20,
-};
-
-int pd_get_rev(int port, enum tcpci_msg_type type)
-{
- return prl_get_rev(port, type);
-}
-
-int pd_get_vdo_ver(int port, enum tcpci_msg_type type)
-{
- enum pd_rev_type rev = prl_get_rev(port, type);
-
- if (rev < PD_REV30)
- return vdo_ver[rev];
- else
- return VDM_VER20;
-}
-
-static void pe_set_ready_state(int port)
-{
- if (pe[port].power_role == PD_ROLE_SOURCE)
- set_state_pe(port, PE_SRC_READY);
- else
- set_state_pe(port, PE_SNK_READY);
-}
-
-static inline void send_data_msg(int port, enum tcpci_msg_type type,
- enum pd_data_msg_type msg)
-{
- /* Clear any previous TX status before sending a new message */
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
- prl_send_data_msg(port, type, msg);
-}
-
-static __maybe_unused inline void send_ext_data_msg(
- int port, enum tcpci_msg_type type, enum pd_ext_msg_type msg)
-{
- /* Clear any previous TX status before sending a new message */
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
- prl_send_ext_data_msg(port, type, msg);
-}
-
-static inline void send_ctrl_msg(int port, enum tcpci_msg_type type,
- enum pd_ctrl_msg_type msg)
-{
- /* Clear any previous TX status before sending a new message */
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
- prl_send_ctrl_msg(port, type, msg);
-}
-
-static void set_cable_rev(int port)
-{
- /*
- * If port partner runs PD 2.0, cable communication must
- * also be PD 2.0
- */
- if (prl_get_rev(port, TCPCI_MSG_SOP) == PD_REV20) {
- /*
- * If the cable supports PD 3.0, but the port partner supports PD 2.0,
- * redo the cable discover with PD 2.0
- */
- if (prl_get_rev(port, TCPCI_MSG_SOP_PRIME) == PD_REV30 &&
- pd_get_identity_discovery(port, TCPCI_MSG_SOP_PRIME) ==
- PD_DISC_COMPLETE) {
- pd_set_identity_discovery(port, TCPCI_MSG_SOP_PRIME,
- PD_DISC_NEEDED);
- }
- prl_set_rev(port, TCPCI_MSG_SOP_PRIME, PD_REV20);
- }
-}
-
-/* Compile-time insurance to ensure this code does not call into prl directly */
-#define prl_send_data_msg DO_NOT_USE
-#define prl_send_ext_data_msg DO_NOT_USE
-#define prl_send_ctrl_msg DO_NOT_USE
-
-static void pe_init(int port)
-{
- pe[port].flags = 0;
- pe[port].dpm_request = 0;
- pe[port].dpm_curr_request = 0;
- pd_timer_disable_range(port, PE_TIMER_RANGE);
- pe[port].data_role = pd_get_data_role(port);
- pe[port].tx_type = TCPCI_MSG_INVALID;
- pe[port].events = 0;
-
- tc_pd_connection(port, 0);
-
- if (pd_get_power_role(port) == PD_ROLE_SOURCE)
- set_state_pe(port, PE_SRC_STARTUP);
- else
- set_state_pe(port, PE_SNK_STARTUP);
-}
-
-int pe_is_running(int port)
-{
- return local_state[port] == SM_RUN;
-}
-
-bool pe_in_frs_mode(int port)
-{
- return PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH);
-}
-
-bool pe_in_local_ams(int port)
-{
- return !!PE_CHK_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS);
-}
-
-void pe_set_debug_level(enum debug_level debug_level)
-{
-#ifndef CONFIG_USB_PD_DEBUG_LEVEL
- pe_debug_level = debug_level;
-#endif
-}
-
-void pe_run(int port, int evt, int en)
-{
- switch (local_state[port]) {
- case SM_PAUSED:
- if (!en)
- break;
- /* fall through */
- case SM_INIT:
- pe_init(port);
- local_state[port] = SM_RUN;
- /* fall through */
- case SM_RUN:
- if (!en) {
- local_state[port] = SM_PAUSED;
- /*
- * While we are paused, exit all states and wait until
- * initialized again.
- */
- set_state(port, &pe[port].ctx, NULL);
- break;
- }
-
- /*
- * 8.3.3.3.8 PE_SNK_Hard_Reset State
- * The Policy Engine Shall transition to the PE_SNK_Hard_Reset
- * state from any state when:
- * - Hard Reset request from Device Policy Manager
- *
- * USB PD specification clearly states that we should go to
- * PE_SNK_Hard_Reset from ANY state (including states in which
- * port is source) when DPM requests that. This can lead to
- * execute Hard Reset path for sink when actually our power
- * role is source. In our implementation we will choose Hard
- * Reset path depending on current power role.
- */
- if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_HARD_RESET_SEND)) {
- pe_set_dpm_curr_request(port, DPM_REQUEST_HARD_RESET_SEND);
- if (pd_get_power_role(port) == PD_ROLE_SOURCE)
- set_state_pe(port, PE_SRC_HARD_RESET);
- else
- set_state_pe(port, PE_SNK_HARD_RESET);
- }
-
- /*
- * Check for Fast Role Swap signal
- * This is not a typical pattern for adding state changes.
- * I added this here because FRS SIGNALED can happen at any
- * state once we are listening for the signal and we want to
- * make sure to handle it immediately.
- */
- if (IS_ENABLED(CONFIG_USB_PD_REV30) &&
- PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_SIGNALED)) {
- PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_SIGNALED);
- set_state_pe(port, PE_FRS_SNK_SRC_START_AMS);
- }
-
- /* Run state machine */
- run_state(port, &pe[port].ctx);
- break;
- }
-}
-
-int pe_is_explicit_contract(int port)
-{
- return PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT);
-}
-
-void pe_message_received(int port)
-{
- /* This should only be called from the PD task */
- assert(port == TASK_ID_TO_PD_PORT(task_get_current()));
-
- PE_SET_FLAG(port, PE_FLAGS_MSG_RECEIVED);
- task_wake(PD_PORT_TO_TASK_ID(port));
-}
-
-void pe_hard_reset_sent(int port)
-{
- /* This should only be called from the PD task */
- assert(port == TASK_ID_TO_PD_PORT(task_get_current()));
-
- PE_CLR_FLAG(port, PE_FLAGS_HARD_RESET_PENDING);
-}
-
-void pe_got_hard_reset(int port)
-{
- /* This should only be called from the PD task */
- assert(port == TASK_ID_TO_PD_PORT(task_get_current()));
-
- /*
- * Transition from any state to the PE_SRC_Hard_Reset_Received or
- * PE_SNK_Transition_to_default state when:
- * 1) Hard Reset Signaling is detected.
- */
- pe[port].power_role = pd_get_power_role(port);
-
- /* Exit BIST Test mode, in case the TCPC entered it. */
- tcpc_set_bist_test_mode(port, false);
-
- if (pe[port].power_role == PD_ROLE_SOURCE)
- set_state_pe(port, PE_SRC_HARD_RESET_RECEIVED);
- else
- set_state_pe(port, PE_SNK_TRANSITION_TO_DEFAULT);
-}
-
-#ifdef CONFIG_USB_PD_REV30
-/*
- * pd_got_frs_signal
- *
- * Called by the handler that detects the FRS signal in order to
- * switch PE states to complete the FRS that the hardware has
- * started.
- *
- * If the PE is not running, generate an error recovery to turn off
- * Vbus and get the port back into a known state.
- */
-void pd_got_frs_signal(int port)
-{
- if (pe_is_running(port))
- PE_SET_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_SIGNALED);
- else
- pd_set_error_recovery(port);
-
- task_wake(PD_PORT_TO_TASK_ID(port));
-}
-#endif /* CONFIG_USB_PD_REV30 */
-
-/*
- * PE_Set_FRS_Enable
- *
- * This function should be called every time an explicit contract
- * is disabled, to disable FRS.
- *
- * Enabling an explicit contract is not enough to enable FRS, it
- * also requires a Sink Capability power requirement from a Source
- * that supports FRS so we can determine if this is something we
- * can handle.
- */
-static void pe_set_frs_enable(int port, int enable)
-{
- int current = PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_ENABLED);
-
- /* This should only be called from the PD task */
- if (!IS_ENABLED(TEST_BUILD))
- assert(port == TASK_ID_TO_PD_PORT(task_get_current()));
-
- if (!IS_ENABLED(CONFIG_USB_PD_FRS) || !IS_ENABLED(CONFIG_USB_PD_REV30))
- return;
-
- /* Request an FRS change, only if the state has changed */
- if (!!current == !!enable)
- return;
-
- pd_set_frs_enable(port, enable);
- if (enable) {
- int curr_limit = *pd_get_snk_caps(port)
- & PDO_FIXED_FRS_CURR_MASK;
-
- typec_select_src_current_limit_rp(port,
- curr_limit ==
- PDO_FIXED_FRS_CURR_3A0_AT_5V ?
- TYPEC_RP_3A0 : TYPEC_RP_1A5);
- PE_SET_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_ENABLED);
- } else {
- PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_ENABLED);
- }
-}
-
-void pe_set_explicit_contract(int port)
-{
- PE_SET_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT);
-
- /* Set Rp for collision avoidance */
- if (IS_ENABLED(CONFIG_USB_PD_REV30))
- typec_update_cc(port);
-}
-
-void pe_invalidate_explicit_contract(int port)
-{
- pe_set_frs_enable(port, 0);
-
- PE_CLR_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT);
-
- /* Set Rp for current limit if still attached */
- if (IS_ENABLED(CONFIG_USB_PD_REV30) && pd_is_connected(port))
- typec_update_cc(port);
-}
-
-void pd_notify_event(int port, uint32_t event_mask)
-{
- atomic_or(&pe[port].events, event_mask);
-
- /* Notify the host that new events are available to read */
- pd_send_host_event(PD_EVENT_TYPEC);
-}
-
-void pd_clear_events(int port, uint32_t clear_mask)
-{
- atomic_clear_bits(&pe[port].events, clear_mask);
-}
-
-uint32_t pd_get_events(int port)
-{
- return pe[port].events;
-}
-
-void pe_set_snk_caps(int port, int cnt, uint32_t *snk_caps)
-{
- pe[port].snk_cap_cnt = cnt;
-
- memcpy(pe[port].snk_caps, snk_caps, sizeof(uint32_t) * cnt);
-}
-
-const uint32_t * const pd_get_snk_caps(int port)
-{
- return pe[port].snk_caps;
-}
-
-uint8_t pd_get_snk_cap_cnt(int port)
-{
- return pe[port].snk_cap_cnt;
-}
-
-uint32_t pd_get_requested_voltage(int port)
-{
- return pe[port].supply_voltage;
-}
-
-uint32_t pd_get_requested_current(int port)
-{
- return pe[port].curr_limit;
-}
-
-/*
- * Determine if this port may communicate with the cable plug.
- *
- * In both PD 2.0 and 3.0 (2.5.4 SOP'/SOP'' Communication with Cable Plugs):
- *
- * When no Contract or an Implicit Contract is in place (e.g. after a Power Role
- * Swap or Fast Role Swap) only the Source port that is supplying Vconn is
- * allowed to send packets to a Cable Plug
- *
- * When in an explicit contract, PD 3.0 requires that a port be Vconn source to
- * communicate with the cable. PD 2.0 requires that a port be DFP to
- * communicate with the cable plug, with an implication that it must be Vconn
- * source as well (6.3.11 VCONN_Swap Message).
- */
-static bool pe_can_send_sop_prime(int port)
-{
- if (IS_ENABLED(CONFIG_USBC_VCONN)) {
- if (PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT)) {
- if (prl_get_rev(port, TCPCI_MSG_SOP) == PD_REV20)
- return tc_is_vconn_src(port) &&
- pe[port].data_role == PD_ROLE_DFP;
- else
- return tc_is_vconn_src(port);
- } else {
- return tc_is_vconn_src(port) &&
- pe[port].power_role == PD_ROLE_SOURCE;
- }
- } else {
- return false;
- }
-}
-
-/*
- * Determine if this port may send the given VDM type
- *
- * For PD 2.0, "Only the DFP Shall be an Initrator of Structured VDMs except for
- * the Attention Command that Shall only be initiated by the UFP"
- *
- * For PD 3.0, "Either port May be an Initiator of Structured VDMs except for
- * the Enter Mode and Exit Mode Commands which shall only be initiated by the
- * DFP" (6.4.4.2 Structured VDM)
- *
- * In both revisions, VDMs may only be initiated while in an explicit contract,
- * with the only exception being for cable plug discovery.
- */
-static bool pe_can_send_sop_vdm(int port, int vdm_cmd)
-{
- if (PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT)) {
- if (prl_get_rev(port, TCPCI_MSG_SOP) == PD_REV20) {
- if (pe[port].data_role == PD_ROLE_UFP &&
- vdm_cmd != CMD_ATTENTION) {
- return false;
- }
- } else {
- if (pe[port].data_role == PD_ROLE_UFP &&
- (vdm_cmd == CMD_ENTER_MODE ||
- vdm_cmd == CMD_EXIT_MODE)) {
- return false;
- }
- }
- return true;
- }
-
- return false;
-}
-
-static void pe_send_soft_reset(const int port, enum tcpci_msg_type type)
-{
- pe[port].soft_reset_sop = type;
- set_state_pe(port, PE_SEND_SOFT_RESET);
-}
-
-void pe_report_discard(int port)
-{
- /*
- * Clear local AMS indicator as our AMS message was discarded, and flag
- * the discard for the PE
- */
- PE_CLR_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS);
- PE_SET_FLAG(port, PE_FLAGS_MSG_DISCARDED);
-
- /* TODO(b/157228506): Ensure all states are checking discard */
-}
-
-/*
- * Utility function to check for an outgoing message discard during states which
- * send a message as a part of an AMS and wait for the transmit to complete.
- * Note these states should not be power transitioning.
- *
- * In these states, discard due to an incoming message is a protocol error.
- */
-static bool pe_check_outgoing_discard(int port)
-{
- /*
- * On outgoing discard, soft reset with SOP* of incoming message
- *
- * See Table 6-65 Response to an incoming Message (except VDM) in PD 3.0
- * Version 2.0 Specification.
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_MSG_DISCARDED) &&
- PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- enum tcpci_msg_type sop =
- PD_HEADER_GET_SOP(rx_emsg[port].header);
-
- PE_CLR_FLAG(port, PE_FLAGS_MSG_DISCARDED);
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
-
- pe_send_soft_reset(port, sop);
- return true;
- }
-
- return false;
-}
-
-void pe_report_error(int port, enum pe_error e, enum tcpci_msg_type type)
-{
- /* This should only be called from the PD task */
- assert(port == TASK_ID_TO_PD_PORT(task_get_current()));
-
- /*
- * If there is a timeout error while waiting for a chunk of a chunked
- * message, there is no requirement to trigger a soft reset.
- */
- if (e == ERR_RCH_CHUNK_WAIT_TIMEOUT)
- return;
-
- /*
- * Generate Hard Reset if Protocol Error occurred
- * while in PE_Send_Soft_Reset state.
- */
- if (get_state_pe(port) == PE_SEND_SOFT_RESET) {
- if (pe[port].power_role == PD_ROLE_SINK)
- set_state_pe(port, PE_SNK_HARD_RESET);
- else
- set_state_pe(port, PE_SRC_HARD_RESET);
- return;
- }
-
- /*
- * The following states require custom handling of protocol errors,
- * because they either need special handling of the no GoodCRC case
- * (cable identity request, send capabilities), occur before explicit
- * contract (discovery), or happen during a power transition.
- *
- * TODO(b/150774779): TCPMv2: Improve pe_error documentation
- */
- if ((get_state_pe(port) == PE_SRC_SEND_CAPABILITIES ||
- get_state_pe(port) == PE_SRC_TRANSITION_SUPPLY ||
- get_state_pe(port) == PE_PRS_SNK_SRC_EVALUATE_SWAP ||
- get_state_pe(port) == PE_PRS_SNK_SRC_SOURCE_ON ||
- get_state_pe(port) == PE_PRS_SRC_SNK_WAIT_SOURCE_ON ||
- get_state_pe(port) == PE_SRC_DISABLED ||
- get_state_pe(port) == PE_SRC_DISCOVERY ||
- get_state_pe(port) == PE_VCS_CBL_SEND_SOFT_RESET ||
- get_state_pe(port) == PE_VDM_IDENTITY_REQUEST_CBL) ||
- (pe_in_frs_mode(port) &&
- get_state_pe(port) == PE_PRS_SNK_SRC_SEND_SWAP)
- ) {
- PE_SET_FLAG(port, PE_FLAGS_PROTOCOL_ERROR);
- task_wake(PD_PORT_TO_TASK_ID(port));
- return;
- }
-
- /*
- * See section 8.3.3.4.1.1 PE_SRC_Send_Soft_Reset State:
- *
- * The PE_Send_Soft_Reset state shall be entered from
- * any state when
- * * A Protocol Error is detected by Protocol Layer during a
- * Non-Interruptible AMS or
- * * A message has not been sent after retries or
- * * When not in an explicit contract and
- * * Protocol Errors occurred on SOP during an Interruptible AMS or
- * * Protocol Errors occurred on SOP during any AMS where the first
- * Message in the sequence has not yet been sent i.e. an unexpected
- * Message is received instead of the expected GoodCRC Message
- * response.
- */
- /* All error types besides transmit errors are Protocol Errors. */
- if ((e != ERR_TCH_XMIT &&
- !PE_CHK_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS))
- || e == ERR_TCH_XMIT
- || (!PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT) &&
- type == TCPCI_MSG_SOP)) {
- pe_send_soft_reset(port, type);
- }
- /*
- * Transition to PE_Snk_Ready or PE_Src_Ready by a Protocol
- * Error during an Interruptible AMS.
- */
- else {
- pe_set_ready_state(port);
- }
-}
-
-void pe_got_soft_reset(int port)
-{
- /* This should only be called from the PD task */
- assert(port == TASK_ID_TO_PD_PORT(task_get_current()));
-
- /*
- * The PE_SRC_Soft_Reset state Shall be entered from any state when a
- * Soft_Reset Message is received from the Protocol Layer.
- */
- set_state_pe(port, PE_SOFT_RESET);
-}
-
-__overridable bool pd_can_charge_from_device(int port, const int pdo_cnt,
- const uint32_t *pdos)
-{
- /*
- * Don't attempt to charge from a device we have no SrcCaps from. Or, if
- * drp_state is FORCE_SOURCE then don't attempt a PRS.
- */
- if (pdo_cnt == 0 || pd_get_dual_role(port) == PD_DRP_FORCE_SOURCE)
- return false;
-
- /*
- * Treat device as a dedicated charger (meaning we should charge
- * from it) if:
- * - it does not support power swap, or
- * - it is unconstrained power, or
- * - it presents at least 27 W of available power
- */
-
- /* Unconstrained Power or NOT Dual Role Power we can charge from */
- if (pdos[0] & PDO_FIXED_UNCONSTRAINED ||
- (pdos[0] & PDO_FIXED_DUAL_ROLE) == 0)
- return true;
-
- /* [virtual] allow_list */
- if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) {
- uint32_t max_ma, max_mv, max_pdo, max_mw, unused;
-
- /*
- * Get max power that the partner offers (not necessarily what
- * this board will request)
- */
- pd_find_pdo_index(pdo_cnt, pdos,
- PD_REV3_MAX_VOLTAGE,
- &max_pdo);
- pd_extract_pdo_power(max_pdo, &max_ma, &max_mv, &unused);
- max_mw = max_ma * max_mv / 1000;
-
- if (max_mw >= PD_DRP_CHARGE_POWER_MIN)
- return true;
- }
- return false;
-}
-
-void pd_resume_check_pr_swap_needed(int port)
-{
- /*
- * Explicit contract, current power role of SNK, the device
- * indicates it should not power us, and device isn't selected
- * as the charging port (ex. through the GUI) then trigger a PR_Swap
- */
- if (pe_is_explicit_contract(port) &&
- pd_get_power_role(port) == PD_ROLE_SINK &&
- !pd_can_charge_from_device(port, pd_get_src_cap_cnt(port),
- pd_get_src_caps(port)) &&
- (!IS_ENABLED(CONFIG_CHARGE_MANAGER) ||
- charge_manager_get_active_charge_port() != port))
- pd_dpm_request(port, DPM_REQUEST_PR_SWAP);
-}
-
-void pd_dpm_request(int port, enum pd_dpm_request req)
-{
- PE_SET_DPM_REQUEST(port, req);
-}
-
-void pe_vconn_swap_complete(int port)
-{
- /* This should only be called from the PD task */
- assert(port == TASK_ID_TO_PD_PORT(task_get_current()));
-
- PE_SET_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE);
-}
-
-void pe_ps_reset_complete(int port)
-{
- /* This should only be called from the PD task */
- assert(port == TASK_ID_TO_PD_PORT(task_get_current()));
-
- PE_SET_FLAG(port, PE_FLAGS_PS_RESET_COMPLETE);
-}
-
-void pe_message_sent(int port)
-{
- /* This should only be called from the PD task */
- assert(port == TASK_ID_TO_PD_PORT(task_get_current()));
-
- PE_SET_FLAG(port, PE_FLAGS_TX_COMPLETE);
- task_wake(PD_PORT_TO_TASK_ID(port));
-}
-
-void pd_send_vdm(int port, uint32_t vid, int cmd, const uint32_t *data,
- int count)
-{
- /* Copy VDM Header */
- pe[port].vdm_data[0] =
- VDO(vid, ((vid & USB_SID_PD) == USB_SID_PD) ? 1 :
- (PD_VDO_CMD(cmd) <= CMD_ATTENTION),
- VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP)) |
- cmd);
-
- /*
- * Copy VDOs after the VDM Header. Note that the count refers to VDO
- * count.
- */
- memcpy((pe[port].vdm_data + 1), data, count * sizeof(uint32_t));
-
- pe[port].vdm_cnt = count + 1;
-
- /*
- * The PE transmit routine assumes that tx_type was set already. Note,
- * that this function is likely called from outside the PD task.
- * (b/180465870)
- */
- pe[port].tx_type = TCPCI_MSG_SOP;
- pd_dpm_request(port, DPM_REQUEST_VDM);
-
- task_wake(PD_PORT_TO_TASK_ID(port));
-}
-
-#ifdef TEST_BUILD
-/*
- * Allow unit tests to access this function to clear internal state data between
- * runs
- */
-void pe_clear_port_data(int port)
-#else
-static void pe_clear_port_data(int port)
-#endif /* TEST_BUILD */
-{
- /*
- * PD 3.0 Section 8.3.3.3.8
- * Note: The HardResetCounter is reset on a power cycle or Detach.
- */
- pe[port].hard_reset_counter = 0;
-
- /* Reset port events */
- pd_clear_events(port, GENMASK(31, 0));
-
- /* But then set disconnected event */
- pd_notify_event(port, PD_STATUS_EVENT_DISCONNECTED);
-
- /* Tell Policy Engine to invalidate the explicit contract */
- pe_invalidate_explicit_contract(port);
-
- /*
- * Saved Source and Sink Capabilities are no longer valid on disconnect
- */
- pd_set_src_caps(port, 0, NULL);
- pe_set_snk_caps(port, 0, NULL);
-
- dpm_remove_sink(port);
- dpm_remove_source(port);
-
- /* Exit BIST Test mode, in case the TCPC entered it. */
- tcpc_set_bist_test_mode(port, false);
-}
-
-static void pe_handle_detach(void)
-{
- const int port = TASK_ID_TO_PD_PORT(task_get_current());
-
- pe_clear_port_data(port);
-}
-DECLARE_HOOK(HOOK_USB_PD_DISCONNECT, pe_handle_detach, HOOK_PRIO_DEFAULT);
-
-#ifdef CONFIG_USB_PD_RESET_MIN_BATT_SOC
-static void pe_update_waiting_batt_flag(void)
-{
- int i;
- int batt_soc = usb_get_battery_soc();
-
- if (batt_soc < CONFIG_USB_PD_RESET_MIN_BATT_SOC ||
- battery_get_disconnect_state() != BATTERY_NOT_DISCONNECTED)
- return;
-
- for (i = 0; i < board_get_usb_pd_port_count(); i++) {
- if (PE_CHK_FLAG(i, PE_FLAGS_SNK_WAITING_BATT)) {
- /*
- * Battery has gained sufficient charge to kick off PD
- * negotiation and withstand a hard reset. Clear the
- * flag and perform Hard Reset.
- */
- PE_CLR_FLAG(i, PE_FLAGS_SNK_WAITING_BATT);
- CPRINTS("C%d: Battery has enough charge (%d%%) " \
- "to withstand a hard reset", i, batt_soc);
- pd_dpm_request(i, DPM_REQUEST_HARD_RESET_SEND);
- }
- }
-}
-DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, pe_update_waiting_batt_flag,
- HOOK_PRIO_DEFAULT);
-#endif
-
-/*
- * Private functions
- */
-static void pe_set_dpm_curr_request(const int port,
- const int request)
-{
- PE_CLR_DPM_REQUEST(port, request);
- pe[port].dpm_curr_request = request;
-}
-
-/* Set the TypeC state machine to a new state. */
-test_export_static void set_state_pe(const int port,
- const enum usb_pe_state new_state)
-{
- set_state(port, &pe[port].ctx, &pe_states[new_state]);
-}
-
-/* Get the current TypeC state. */
-test_export_static enum usb_pe_state get_state_pe(const int port)
-{
- return pe[port].ctx.current - &pe_states[0];
-}
-
-/*
- * Handle common DPM requests to both source and sink.
- *
- * Note: it is assumed the calling state set PE_FLAGS_LOCALLY_INITIATED_AMS
- *
- * Returns true if state was set and calling run state should now return.
- */
-static bool common_src_snk_dpm_requests(int port)
-{
- if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES) &&
- PE_CHK_DPM_REQUEST(port, DPM_REQUEST_SEND_ALERT)) {
- pe_set_dpm_curr_request(port, DPM_REQUEST_SEND_ALERT);
- set_state_pe(port, PE_SEND_ALERT);
- return true;
- } else if (IS_ENABLED(CONFIG_USBC_VCONN) &&
- PE_CHK_DPM_REQUEST(port, DPM_REQUEST_VCONN_SWAP)) {
- pe_set_dpm_curr_request(port, DPM_REQUEST_VCONN_SWAP);
- set_state_pe(port, PE_VCS_SEND_SWAP);
- return true;
- } else if (PE_CHK_DPM_REQUEST(port,
- DPM_REQUEST_BIST_TX)) {
- pe_set_dpm_curr_request(port, DPM_REQUEST_BIST_TX);
- set_state_pe(port, PE_BIST_TX);
- return true;
- } else if (PE_CHK_DPM_REQUEST(port,
- DPM_REQUEST_SNK_STARTUP)) {
- pe_set_dpm_curr_request(port, DPM_REQUEST_SNK_STARTUP);
- set_state_pe(port, PE_SNK_STARTUP);
- return true;
- } else if (PE_CHK_DPM_REQUEST(port,
- DPM_REQUEST_SRC_STARTUP)) {
- pe_set_dpm_curr_request(port, DPM_REQUEST_SRC_STARTUP);
- set_state_pe(port, PE_SRC_STARTUP);
- return true;
- } else if (PE_CHK_DPM_REQUEST(port,
- DPM_REQUEST_SOFT_RESET_SEND)) {
- pe_set_dpm_curr_request(port, DPM_REQUEST_SOFT_RESET_SEND);
- /* Currently only support sending soft reset to SOP */
- pe_send_soft_reset(port, TCPCI_MSG_SOP);
- return true;
- } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_PORT_DISCOVERY)) {
- pe_set_dpm_curr_request(port, DPM_REQUEST_PORT_DISCOVERY);
- if (!PE_CHK_FLAG(port, PE_FLAGS_MODAL_OPERATION)) {
- /*
- * Clear counters and reset timer to trigger a
- * port discovery, and also clear any pending VDM send
- * requests.
- */
- pd_dfp_discovery_init(port);
- /*
- * TODO(b/189353401): Do not reinitialize modes when no
- * longer required.
- */
- pd_dfp_mode_init(port);
- pe[port].dr_swap_attempt_counter = 0;
- pe[port].discover_identity_counter = 0;
- pd_timer_enable(port, PE_TIMER_DISCOVER_IDENTITY,
- PD_T_DISCOVER_IDENTITY);
- PE_CLR_DPM_REQUEST(port, DPM_REQUEST_VDM);
- }
- return true;
- } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_VDM)) {
- pe_set_dpm_curr_request(port, DPM_REQUEST_VDM);
- /* Send previously set up SVDM. */
- set_state_pe(port, PE_VDM_REQUEST_DPM);
- return true;
- } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_ENTER_USB)) {
- pe_set_dpm_curr_request(port, DPM_REQUEST_ENTER_USB);
- set_state_pe(port, PE_DEU_SEND_ENTER_USB);
- return true;
- } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_EXIT_MODES)) {
- pe_set_dpm_curr_request(port, DPM_REQUEST_EXIT_MODES);
- dpm_set_mode_exit_request(port);
- return true;
- } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_GET_SNK_CAPS)) {
- pe_set_dpm_curr_request(port,
- DPM_REQUEST_GET_SNK_CAPS);
- set_state_pe(port, PE_DR_GET_SINK_CAP);
- return true;
- } else if (PE_CHK_DPM_REQUEST(port,
- DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND)) {
- pe_set_dpm_curr_request(port,
- DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND);
- pe[port].tx_type = TCPCI_MSG_SOP_PRIME;
- set_state_pe(port, PE_VCS_CBL_SEND_SOFT_RESET);
- return true;
- }
-
- return false;
-}
-
-/*
- * Handle source-specific DPM requests
- *
- * Returns true if state was set and calling run state should now return.
- */
-static bool source_dpm_requests(int port)
-{
- /*
- * Ignore sink-specific request:
- * DPM_REQUEST_NEW_POWER_LEVEL
- * DPM_REQUEST_SOURCE_CAP
- * DPM_REQUEST_FRS_DET_ENABLE
- * DPM_REQURST_FRS_DET_DISABLE
- */
- PE_CLR_DPM_REQUEST(port, DPM_REQUEST_NEW_POWER_LEVEL |
- DPM_REQUEST_SOURCE_CAP |
- DPM_REQUEST_FRS_DET_ENABLE |
- DPM_REQUEST_FRS_DET_DISABLE);
-
- if (pe[port].dpm_request) {
- uint32_t dpm_request = pe[port].dpm_request;
-
- PE_SET_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS);
-
- if (PE_CHK_DPM_REQUEST(port,
- DPM_REQUEST_DR_SWAP)) {
- pe_set_dpm_curr_request(port,
- DPM_REQUEST_DR_SWAP);
- if (PE_CHK_FLAG(port, PE_FLAGS_MODAL_OPERATION))
- set_state_pe(port, PE_SRC_HARD_RESET);
- else
- set_state_pe(port, PE_DRS_SEND_SWAP);
- return true;
- } else if (PE_CHK_DPM_REQUEST(port,
- DPM_REQUEST_PR_SWAP)) {
- pe_set_dpm_curr_request(port,
- DPM_REQUEST_PR_SWAP);
- set_state_pe(port, PE_PRS_SRC_SNK_SEND_SWAP);
- return true;
- } else if (PE_CHK_DPM_REQUEST(port,
- DPM_REQUEST_GOTO_MIN)) {
- pe_set_dpm_curr_request(port,
- DPM_REQUEST_GOTO_MIN);
- set_state_pe(port, PE_SRC_TRANSITION_SUPPLY);
- return true;
- } else if (PE_CHK_DPM_REQUEST(port,
- DPM_REQUEST_SRC_CAP_CHANGE)) {
- pe_set_dpm_curr_request(port,
- DPM_REQUEST_SRC_CAP_CHANGE);
- set_state_pe(port, PE_SRC_SEND_CAPABILITIES);
- return true;
- } else if (PE_CHK_DPM_REQUEST(port,
- DPM_REQUEST_GET_SRC_CAPS)) {
- pe_set_dpm_curr_request(port,
- DPM_REQUEST_GET_SRC_CAPS);
- set_state_pe(port, PE_DR_SRC_GET_SOURCE_CAP);
- return true;
- } else if (PE_CHK_DPM_REQUEST(port,
- DPM_REQUEST_SEND_PING)) {
- pe_set_dpm_curr_request(port,
- DPM_REQUEST_SEND_PING);
- set_state_pe(port, PE_SRC_PING);
- return true;
- } else if (common_src_snk_dpm_requests(port)) {
- return true;
- }
-
- CPRINTF("Unhandled DPM Request %x received\n",
- dpm_request);
- PE_CLR_DPM_REQUEST(port, dpm_request);
- PE_CLR_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS);
- }
- return false;
-}
-
-/*
- * Handle sink-specific DPM requests
- *
- * Returns true if state was set and calling run state should now return.
- */
-static bool sink_dpm_requests(int port)
-{
- /*
- * Ignore source specific requests:
- * DPM_REQUEST_GOTO_MIN
- * DPM_REQUEST_SRC_CAP_CHANGE,
- * DPM_REQUEST_SEND_PING
- */
- PE_CLR_DPM_REQUEST(port, DPM_REQUEST_GOTO_MIN |
- DPM_REQUEST_SRC_CAP_CHANGE |
- DPM_REQUEST_SEND_PING);
-
- if (pe[port].dpm_request) {
- uint32_t dpm_request = pe[port].dpm_request;
-
- PE_SET_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS);
-
- if (PE_CHK_DPM_REQUEST(port,
- DPM_REQUEST_DR_SWAP)) {
- pe_set_dpm_curr_request(port,
- DPM_REQUEST_DR_SWAP);
- if (PE_CHK_FLAG(port, PE_FLAGS_MODAL_OPERATION))
- set_state_pe(port, PE_SNK_HARD_RESET);
- else
- set_state_pe(port, PE_DRS_SEND_SWAP);
- return true;
- } else if (PE_CHK_DPM_REQUEST(port,
- DPM_REQUEST_PR_SWAP)) {
- pe_set_dpm_curr_request(port,
- DPM_REQUEST_PR_SWAP);
- set_state_pe(port, PE_PRS_SNK_SRC_SEND_SWAP);
- return true;
- } else if (PE_CHK_DPM_REQUEST(port,
- DPM_REQUEST_SOURCE_CAP)) {
- pe_set_dpm_curr_request(port,
- DPM_REQUEST_SOURCE_CAP);
- set_state_pe(port, PE_SNK_GET_SOURCE_CAP);
- return true;
- } else if (PE_CHK_DPM_REQUEST(port,
- DPM_REQUEST_NEW_POWER_LEVEL)) {
- pe_set_dpm_curr_request(port,
- DPM_REQUEST_NEW_POWER_LEVEL);
- set_state_pe(port, PE_SNK_SELECT_CAPABILITY);
- return true;
- } else if (PE_CHK_DPM_REQUEST(port,
- DPM_REQUEST_FRS_DET_ENABLE)) {
- pe_set_frs_enable(port, 1);
-
- /* Requires no state change, fall through to false */
- PE_CLR_DPM_REQUEST(port, DPM_REQUEST_FRS_DET_ENABLE);
- } else if (PE_CHK_DPM_REQUEST(port,
- DPM_REQUEST_FRS_DET_DISABLE)) {
- pe_set_frs_enable(port, 0);
- /* Restore a default port current limit */
- typec_select_src_current_limit_rp(port,
- CONFIG_USB_PD_PULLUP);
-
- /* Requires no state change, fall through to false */
- PE_CLR_DPM_REQUEST(port, DPM_REQUEST_FRS_DET_DISABLE);
- } else if (common_src_snk_dpm_requests(port)) {
- return true;
- } else {
- CPRINTF("Unhandled DPM Request %x received\n",
- dpm_request);
- PE_CLR_DPM_REQUEST(port, dpm_request);
- }
-
- PE_CLR_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS);
- }
- return false;
-}
-
-/* Get the previous TypeC state. */
-static enum usb_pe_state get_last_state_pe(const int port)
-{
- return pe[port].ctx.previous - &pe_states[0];
-}
-
-static void print_current_state(const int port)
-{
- const char *mode = "";
-
- if (IS_ENABLED(CONFIG_USB_PD_REV30) &&
- pe_in_frs_mode(port))
- mode = " FRS-MODE";
-
- if (IS_ENABLED(USB_PD_DEBUG_LABELS))
- CPRINTS_L1("C%d: %s%s", port,
- pe_state_names[get_state_pe(port)], mode);
- else
- CPRINTS("C%d: pe-st%d", port, get_state_pe(port));
-}
-
-static void send_source_cap(int port)
-{
- const uint32_t *src_pdo;
- const int src_pdo_cnt = dpm_get_source_pdo(&src_pdo, port);
-
- if (src_pdo_cnt == 0) {
- /* No source capabilities defined, sink only */
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT);
- }
-
- tx_emsg[port].len = src_pdo_cnt * 4;
- memcpy(tx_emsg[port].buf, (uint8_t *)src_pdo, tx_emsg[port].len);
-
- send_data_msg(port, TCPCI_MSG_SOP, PD_DATA_SOURCE_CAP);
-}
-
-/*
- * Request desired charge voltage from source.
- */
-static void pe_send_request_msg(int port)
-{
- uint32_t vpd_vdo = 0;
- uint32_t rdo;
- uint32_t curr_limit;
- uint32_t supply_voltage;
-
- /*
- * If we are charging through a VPD, the requested voltage and current
- * might need adjusting.
- */
- if ((get_usb_pd_cable_type(port) == IDH_PTYPE_VPD) &&
- is_vpd_ct_supported(port)) {
- union vpd_vdo vpd = pd_get_am_discovery(port,
- TCPCI_MSG_SOP_PRIME)->identity.product_t1.vpd;
-
- /* The raw vpd_vdo is passed to pd_build_request */
- vpd_vdo = vpd.raw_value;
- }
-
- /* Build and send request RDO */
- pd_build_request(vpd_vdo, &rdo, &curr_limit,
- &supply_voltage, port);
-
- CPRINTF("C%d: Req [%d] %dmV %dmA", port, RDO_POS(rdo),
- supply_voltage, curr_limit);
- if (rdo & RDO_CAP_MISMATCH)
- CPRINTF(" Mismatch");
- CPRINTF("\n");
-
- pe[port].curr_limit = curr_limit;
- pe[port].supply_voltage = supply_voltage;
-
- tx_emsg[port].len = 4;
-
- memcpy(tx_emsg[port].buf, (uint8_t *)&rdo, tx_emsg[port].len);
- send_data_msg(port, TCPCI_MSG_SOP, PD_DATA_REQUEST);
-}
-
-static void pe_update_src_pdo_flags(int port, int pdo_cnt, uint32_t *pdos)
-{
- /*
- * Only parse PDO flags if type is fixed
- *
- * Note: From 6.4.1 Capabilities Message "The vSafe5V Fixed Supply
- * Object Shall always be the first object." so hitting this condition
- * would mean the partner is voilating spec.
- */
- if ((pdos[0] & PDO_TYPE_MASK) != PDO_TYPE_FIXED)
- return;
-
- if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) {
- if (pd_can_charge_from_device(port, pdo_cnt, pdos)) {
- charge_manager_update_dualrole(port, CAP_DEDICATED);
- } else {
- charge_manager_update_dualrole(port, CAP_DUALROLE);
- }
- }
-}
-
-/*
- * Evaluate whether our PR role is in the middle of changing, meaning we our
- * current PR role is not the one we expect to have very shortly.
- */
-bool pe_is_pr_swapping(int port)
-{
- enum usb_pe_state cur_state = get_state_pe(port);
-
- if (cur_state == PE_PRS_SRC_SNK_EVALUATE_SWAP ||
- cur_state == PE_PRS_SRC_SNK_TRANSITION_TO_OFF ||
- cur_state == PE_PRS_SNK_SRC_EVALUATE_SWAP ||
- cur_state == PE_PRS_SNK_SRC_TRANSITION_TO_OFF)
- return true;
-
- return false;
-}
-
-void pd_request_power_swap(int port)
-{
- /* Ignore requests when the board does not wish to swap */
- if (!pd_check_power_swap(port))
- return;
-
- /* Ignore requests when our power role is transitioning */
- if (pe_is_pr_swapping(port))
- return;
-
- /*
- * Always reset the SRC to SNK PR swap counter when a PR swap is
- * requested by policy.
- */
- pe[port].src_snk_pr_swap_counter = 0;
- pd_dpm_request(port, DPM_REQUEST_PR_SWAP);
-}
-
-/* The function returns true if there is a PE state change, false otherwise */
-static bool port_try_vconn_swap(int port)
-{
- if (pe[port].vconn_swap_counter < N_VCONN_SWAP_COUNT) {
- pd_dpm_request(port, DPM_REQUEST_VCONN_SWAP);
- set_state_pe(port, get_last_state_pe(port));
- return true;
- }
- return false;
-}
-
-/*
- * Run discovery at our leisure from PE_SNK_Ready or PE_SRC_Ready, after
- * attempting to get into the desired default policy of DFP/Vconn source
- *
- * Return indicates whether set_state was called, in which case the calling
- * function should return as well.
- */
-__maybe_unused static bool pe_attempt_port_discovery(int port)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP))
- assert(0);
-
- /*
- * DONE set once modal entry is successful, discovery completes, or
- * discovery results in a NAK
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_VDM_SETUP_DONE))
- return false;
-
- /* Apply Port Discovery DR Swap Policy */
- if (port_discovery_dr_swap_policy(port, pe[port].data_role,
- PE_CHK_FLAG(port, PE_FLAGS_DR_SWAP_TO_DFP))) {
- PE_SET_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS);
- PE_CLR_FLAG(port, PE_FLAGS_DR_SWAP_TO_DFP);
- set_state_pe(port, PE_DRS_SEND_SWAP);
- return true;
- }
-
- /* Apply Port Discovery VCONN Swap Policy */
- if (IS_ENABLED(CONFIG_USBC_VCONN) &&
- port_discovery_vconn_swap_policy(port,
- PE_CHK_FLAG(port, PE_FLAGS_VCONN_SWAP_TO_ON))) {
- PE_SET_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS);
- PE_CLR_FLAG(port, PE_FLAGS_VCONN_SWAP_TO_ON);
- set_state_pe(port, PE_VCS_SEND_SWAP);
- return true;
- }
-
- /* If mode entry was successful, disable the timer */
- if (PE_CHK_FLAG(port, PE_FLAGS_VDM_SETUP_DONE)) {
- pd_timer_disable(port, PE_TIMER_DISCOVER_IDENTITY);
- return false;
- }
-
- /*
- * Run discovery functions when the timer indicating either cable
- * discovery spacing or BUSY spacing runs out.
- */
- if (pd_timer_is_expired(port, PE_TIMER_DISCOVER_IDENTITY)) {
- if (pd_get_identity_discovery(port, TCPCI_MSG_SOP_PRIME) ==
- PD_DISC_NEEDED) {
- pe[port].tx_type = TCPCI_MSG_SOP_PRIME;
- set_state_pe(port, PE_VDM_IDENTITY_REQUEST_CBL);
- return true;
- } else if (pd_get_identity_discovery(port, TCPCI_MSG_SOP) ==
- PD_DISC_NEEDED &&
- pe_can_send_sop_vdm(port, CMD_DISCOVER_IDENT)) {
- pe[port].tx_type = TCPCI_MSG_SOP;
- set_state_pe(port,
- PE_INIT_PORT_VDM_IDENTITY_REQUEST);
- return true;
- } else if (pd_get_svids_discovery(port, TCPCI_MSG_SOP) ==
- PD_DISC_NEEDED &&
- pe_can_send_sop_vdm(port, CMD_DISCOVER_SVID)) {
- pe[port].tx_type = TCPCI_MSG_SOP;
- set_state_pe(port, PE_INIT_VDM_SVIDS_REQUEST);
- return true;
- } else if (pd_get_modes_discovery(port, TCPCI_MSG_SOP) ==
- PD_DISC_NEEDED &&
- pe_can_send_sop_vdm(port, CMD_DISCOVER_MODES)) {
- pe[port].tx_type = TCPCI_MSG_SOP;
- set_state_pe(port, PE_INIT_VDM_MODES_REQUEST);
- return true;
- } else if (pd_get_svids_discovery(port, TCPCI_MSG_SOP_PRIME)
- == PD_DISC_NEEDED) {
- pe[port].tx_type = TCPCI_MSG_SOP_PRIME;
- set_state_pe(port, PE_INIT_VDM_SVIDS_REQUEST);
- return true;
- } else if (pd_get_modes_discovery(port, TCPCI_MSG_SOP_PRIME) ==
- PD_DISC_NEEDED) {
- pe[port].tx_type = TCPCI_MSG_SOP_PRIME;
- set_state_pe(port, PE_INIT_VDM_MODES_REQUEST);
- return true;
- }
- }
-
- return false;
-}
-
-bool pd_setup_vdm_request(int port, enum tcpci_msg_type tx_type,
- uint32_t *vdm, uint32_t vdo_cnt)
-{
- if (vdo_cnt < VDO_HDR_SIZE || vdo_cnt > VDO_MAX_SIZE)
- return false;
-
- pe[port].tx_type = tx_type;
- memcpy(pe[port].vdm_data, vdm, vdo_cnt * sizeof(*vdm));
- pe[port].vdm_cnt = vdo_cnt;
-
- return true;
-}
-
-int pd_dev_store_rw_hash(int port, uint16_t dev_id, uint32_t *rw_hash,
- uint32_t current_image)
-{
- pe[port].dev_id = dev_id;
- memcpy(pe[port].dev_rw_hash, rw_hash, PD_RW_HASH_SIZE);
-#ifdef CONFIG_CMD_PD_DEV_DUMP_INFO
- pd_dev_dump_info(dev_id, rw_hash);
-#endif
- pe[port].current_image = current_image;
-
- if (IS_ENABLED(CONFIG_USB_PD_HOST_CMD)) {
- int i;
-
- /* 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_dev_get_rw_hash(int port, uint16_t *dev_id, uint8_t *rw_hash,
- uint32_t *current_image)
-{
- *dev_id = pe[port].dev_id;
- *current_image = pe[port].current_image;
- if (*dev_id)
- memcpy(rw_hash, pe[port].dev_rw_hash, PD_RW_HASH_SIZE);
-}
-
-/*
- * This function must only be called from the PE_SNK_READY entry and
- * PE_SRC_READY entry State.
- *
- * TODO(b:181339670) Rethink jitter timer restart if this is the first
- * message but the partner gets a message in first, may not want to
- * disable and restart it.
- */
-static void pe_update_wait_and_add_jitter_timer(int port)
-{
- /*
- * In PD2.0 Mode
- *
- * For Source:
- * Give the sink some time to send any messages
- * before we may send messages of our own. Add
- * some jitter of up to ~345ms, to prevent
- * multiple collisions. This delay also allows
- * the sink device to request power role swap
- * and allow the the accept message to be sent
- * prior to CMD_DISCOVER_IDENT being sent in the
- * SRC_READY state.
- *
- * For Sink:
- * Give the source some time to send any messages before
- * we start our interrogation. Add some jitter of up to
- * ~345ms to prevent multiple collisions.
- */
- if (prl_get_rev(port, TCPCI_MSG_SOP) == PD_REV20 &&
- PE_CHK_FLAG(port, PE_FLAGS_FIRST_MSG) &&
- pd_timer_is_disabled(port, PE_TIMER_WAIT_AND_ADD_JITTER)) {
- pd_timer_enable(port, PE_TIMER_WAIT_AND_ADD_JITTER,
- SRC_SNK_READY_HOLD_OFF_US +
- (get_time().le.lo & 0xf) * 23 * MSEC);
- }
-}
-
-/**
- * Common sender response message handling
- *
- * This is setup like a pseudo state machine parent state. It
- * centralizes the SenderResponseTimer for the calling states, as
- * well as checking message send status.
- */
-/*
- * pe_sender_response_msg_entry
- * Initialization for handling sender response messages.
- *
- * @param port USB-C Port number
- */
-static void pe_sender_response_msg_entry(const int port)
-{
- /* Stop sender response timer */
- pd_timer_disable(port, PE_TIMER_SENDER_RESPONSE);
-}
-
-/*
- * pe_sender_response_msg_run
- * Check status of sender response messages.
- *
- * The normal progression of pe_sender_response_msg_entry is:
- * PENDING -> (COMPLETED/SENT) -> SENT -> SENT ...
- * or
- * PENDING -> DISCARDED
- * PENDING -> DPM_DISCARDED
- *
- * NOTE: it is not valid to call this function for a message after
- * receiving either PE_MSG_DISCARDED or PE_MSG_DPM_DISCARDED until
- * another message has been sent and pe_sender_response_msg_entry is called
- * again.
- *
- * @param port USB-C Port number
- * @return the current pe_msg_check
- */
-static enum pe_msg_check pe_sender_response_msg_run(const int port)
-{
- timestamp_t tx_success_ts;
- uint32_t offset;
- if (pd_timer_is_disabled(port, PE_TIMER_SENDER_RESPONSE)) {
- /* Check for Discard */
- if (PE_CHK_FLAG(port, PE_FLAGS_MSG_DISCARDED)) {
- int dpm_request = pe[port].dpm_curr_request;
-
- PE_CLR_FLAG(port, PE_FLAGS_MSG_DISCARDED);
- /* Restore the DPM Request */
- if (dpm_request) {
- PE_SET_DPM_REQUEST(port, dpm_request);
- return PE_MSG_DPM_DISCARDED;
- }
- return PE_MSG_DISCARDED;
- }
-
- /* Check for GoodCRC */
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
-
- /* TCPC TX success time stamp */
- tx_success_ts = prl_get_tcpc_tx_success_ts(port);
- /* Calculate the delay from TX success to PE */
- offset = time_since32(tx_success_ts);
-
- /*
- * Initialize and run the SenderResponseTimer by
- * offsetting it with TX transmit success time.
- * This would remove the effect of the latency from
- * propagating the TX status.
- */
- pd_timer_enable(port, PE_TIMER_SENDER_RESPONSE,
- PD_T_SENDER_RESPONSE - offset);
- return PE_MSG_SEND_COMPLETED;
- }
- return PE_MSG_SEND_PENDING;
- }
- return PE_MSG_SENT;
-}
-
-/*
- * pe_sender_response_msg_exit
- * Exit cleanup for handling sender response messages.
- *
- * @param port USB-C Port number
- */
-static void pe_sender_response_msg_exit(int port)
-{
- pd_timer_disable(port, PE_TIMER_SENDER_RESPONSE);
-}
-
-/**
- * PE_SRC_Startup
- */
-static void pe_src_startup_entry(int port)
-{
- print_current_state(port);
-
- /* Reset CapsCounter */
- pe[port].caps_counter = 0;
-
- /* Reset the protocol layer */
- prl_reset_soft(port);
-
- /* Set initial data role */
- pe[port].data_role = pd_get_data_role(port);
-
- /* Set initial power role */
- pe[port].power_role = PD_ROLE_SOURCE;
-
- /* Clear explicit contract. */
- pe_invalidate_explicit_contract(port);
-
- if (PE_CHK_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE);
- /*
- * Protocol layer reset clears the message IDs for all SOP
- * types. Indicate that a SOP' soft reset is required before any
- * other messages are sent to the cable.
- *
- * Note that other paths into this state are for the initial
- * connection and for a hard reset. In both cases the cable
- * should also automatically clear the message IDs so don't
- * generate an SOP' soft reset for those cases. Sending
- * unnecessary SOP' soft resets causes bad behavior with
- * some devices. See b/179325862.
- */
- pd_dpm_request(port, DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND);
-
- /* Start SwapSourceStartTimer */
- pd_timer_enable(port, PE_TIMER_SWAP_SOURCE_START,
- PD_T_SWAP_SOURCE_START);
-
- /*
- * Evaluate port's sink caps for preferred current, if
- * already available
- */
- if (pd_get_snk_cap_cnt(port) > 0)
- dpm_evaluate_sink_fixed_pdo(port,
- *pd_get_snk_caps(port));
-
- /*
- * Remove prior FRS claims to 3.0 A now that sink current has
- * been claimed, to avoid issues with lower priority ports
- * potentially receiving a 3.0 A claim between calls.
- */
- dpm_remove_source(port);
- } else {
- /*
- * SwapSourceStartTimer delay is not needed, so trigger now.
- * We can't use set_state_pe here, since we need to ensure that
- * the protocol layer is running again (done in run function).
- */
- pd_timer_enable(port, PE_TIMER_SWAP_SOURCE_START, 0);
-
- /*
- * Set DiscoverIdentityTimer to trigger when we enter
- * src_discovery for the first time. After initial startup
- * set, vdm_identity_request_cbl will handle the timer updates.
- */
- pd_timer_enable(port, PE_TIMER_DISCOVER_IDENTITY, 0);
-
- /* Clear port discovery/mode flags */
- pd_dfp_discovery_init(port);
- pd_dfp_mode_init(port);
- pe[port].ama_vdo = PD_VDO_INVALID;
- pe[port].vpd_vdo = PD_VDO_INVALID;
- pe[port].discover_identity_counter = 0;
-
- /* Reset dr swap attempt counter */
- pe[port].dr_swap_attempt_counter = 0;
-
- /* Reset VCONN swap counter */
- pe[port].vconn_swap_counter = 0;
-
- /* Request partner sink caps if a feature requires them */
- if (IS_ENABLED(CONFIG_USB_PD_HOST_CMD) ||
- CONFIG_USB_PD_3A_PORTS > 0 ||
- IS_ENABLED(CONFIG_USB_PD_FRS))
- pd_dpm_request(port, DPM_REQUEST_GET_SNK_CAPS);
- }
-}
-
-static void pe_src_startup_run(int port)
-{
- /* Wait until protocol layer is running */
- if (!prl_is_running(port))
- return;
-
- if (pd_timer_is_expired(port, PE_TIMER_SWAP_SOURCE_START))
- set_state_pe(port, PE_SRC_SEND_CAPABILITIES);
-}
-
-static void pe_src_startup_exit(int port)
-{
- pd_timer_disable(port, PE_TIMER_SWAP_SOURCE_START);
-}
-
-/**
- * PE_SRC_Discovery
- */
-static void pe_src_discovery_entry(int port)
-{
- print_current_state(port);
-
- /*
- * Initialize and run the SourceCapabilityTimer in order
- * to trigger sending a Source_Capabilities Message.
- *
- * The SourceCapabilityTimer Shall continue to run during
- * identity discover and Shall Not be initialized on re-entry
- * to PE_SRC_Discovery.
- *
- * Note: Cable identity is the only valid VDM to probe before a contract
- * is in place. All other probing must happen from ready states.
- */
- if (get_last_state_pe(port) != PE_VDM_IDENTITY_REQUEST_CBL)
- pd_timer_enable(port, PE_TIMER_SOURCE_CAP,
- PD_T_SEND_SOURCE_CAP);
-}
-
-static void pe_src_discovery_run(int port)
-{
- /*
- * Transition to the PE_SRC_Send_Capabilities state when:
- * 1) The SourceCapabilityTimer times out and
- * CapsCounter ≤ nCapsCount.
- *
- * Transition to the PE_SRC_Disabled state when:
- * 1) The Port Partners are not presently PD Connected
- * 2) And the SourceCapabilityTimer times out
- * 3) And CapsCounter > nCapsCount.
- *
- * Transition to the PE_SRC_VDM_Identity_request state when:
- * 1) DPM requests the identity of the cable plug and
- * 2) DiscoverIdentityCounter < nDiscoverIdentityCount
- */
- if (pd_timer_is_expired(port, PE_TIMER_SOURCE_CAP)) {
- if (pe[port].caps_counter <= N_CAPS_COUNT) {
- set_state_pe(port, PE_SRC_SEND_CAPABILITIES);
- return;
- } else if (!PE_CHK_FLAG(port, PE_FLAGS_PD_CONNECTION)) {
- set_state_pe(port, PE_SRC_DISABLED);
- return;
- }
- }
-
- /*
- * Note: While the DiscoverIdentityTimer is only required in an explicit
- * contract, we use it here to ensure we space any potential BUSY
- * requests properly.
- */
- if (pd_get_identity_discovery(port, TCPCI_MSG_SOP_PRIME) ==
- PD_DISC_NEEDED
- && pd_timer_is_expired(port, PE_TIMER_DISCOVER_IDENTITY)
- && pe_can_send_sop_prime(port)
- && (pe[port].discover_identity_counter <
- N_DISCOVER_IDENTITY_PRECONTRACT_LIMIT)) {
- pe[port].tx_type = TCPCI_MSG_SOP_PRIME;
- set_state_pe(port, PE_VDM_IDENTITY_REQUEST_CBL);
- return;
- }
-
- /*
- * Transition to the PE_SRC_Disabled state when:
- * 1) The Port Partners have not been PD Connected.
- * 2) And the NoResponseTimer times out.
- * 3) And the HardResetCounter > nHardResetCount.
- */
- if (!PE_CHK_FLAG(port, PE_FLAGS_PD_CONNECTION) &&
- pd_timer_is_expired(port, PE_TIMER_NO_RESPONSE) &&
- pe[port].hard_reset_counter > N_HARD_RESET_COUNT) {
- set_state_pe(port, PE_SRC_DISABLED);
- return;
- }
-}
-
-/**
- * PE_SRC_Send_Capabilities
- */
-static void pe_src_send_capabilities_entry(int port)
-{
- print_current_state(port);
-
- /* Send PD Capabilities message */
- send_source_cap(port);
- pe_sender_response_msg_entry(port);
-
- /* Increment CapsCounter */
- pe[port].caps_counter++;
-}
-
-static void pe_src_send_capabilities_run(int port)
-{
- enum pe_msg_check msg_check;
-
- /*
- * Check the state of the message sent
- */
- msg_check = pe_sender_response_msg_run(port);
-
- /*
- * Handle Discarded message
- * PE_SNK/SRC_READY if DPM_REQUEST
- * PE_SEND_SOFT_RESET otherwise
- */
- if (msg_check == PE_MSG_DPM_DISCARDED) {
- set_state_pe(port, PE_SRC_READY);
- return;
- } else if (msg_check == PE_MSG_DISCARDED) {
- pe_send_soft_reset(port, TCPCI_MSG_SOP);
- return;
- }
-
- /*
- * Handle message that was just sent
- */
- if (msg_check == PE_MSG_SEND_COMPLETED) {
- /*
- * If a GoodCRC Message is received then the Policy Engine
- * Shall:
- * 1) Stop the NoResponseTimer.
- * 2) Reset the HardResetCounter and CapsCounter to zero.
- * 3) Initialize and run the SenderResponseTimer.
- */
- /* Stop the NoResponseTimer */
- pd_timer_disable(port, PE_TIMER_NO_RESPONSE);
-
- /* Reset the HardResetCounter to zero */
- pe[port].hard_reset_counter = 0;
-
- /* Reset the CapsCounter to zero */
- pe[port].caps_counter = 0;
- }
-
- /*
- * Transition to the PE_SRC_Negotiate_Capability state when:
- * 1) A Request Message is received from the Sink
- */
- if ((msg_check & PE_MSG_SENT) &&
- PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
-
- /*
- * Request Message Received?
- */
- if (PD_HEADER_CNT(rx_emsg[port].header) > 0 &&
- PD_HEADER_TYPE(rx_emsg[port].header) ==
- PD_DATA_REQUEST) {
-
- /*
- * Set to highest revision supported by both
- * ports.
- */
- prl_set_rev(port, TCPCI_MSG_SOP,
- MIN(PD_REVISION, PD_HEADER_REV(rx_emsg[port].header)));
-
- set_cable_rev(port);
-
- /* We are PD connected */
- PE_SET_FLAG(port, PE_FLAGS_PD_CONNECTION);
- tc_pd_connection(port, 1);
-
- /*
- * Handle the Sink Request in
- * PE_SRC_Negotiate_Capability state
- */
- set_state_pe(port, PE_SRC_NEGOTIATE_CAPABILITY);
- return;
- }
-
- /*
- * We have a Protocol Error.
- * PE_SEND_SOFT_RESET
- */
- pe_send_soft_reset(port,
- PD_HEADER_GET_SOP(rx_emsg[port].header));
- return;
- }
-
- /*
- * Transition to the PE_SRC_Discovery state when:
- * 1) The Protocol Layer indicates that the Message has not been sent
- * and we are presently not Connected
- *
- * Send soft reset when:
- * 1) The Protocol Layer indicates that the Message has not been sent
- * and we are already Connected
- *
- * See section 8.3.3.4.1.1 PE_SRC_Send_Soft_Reset State and section
- * 8.3.3.2.3 PE_SRC_Send_Capabilities State.
- *
- * NOTE: The PE_FLAGS_PROTOCOL_ERROR is set if a GoodCRC Message
- * is not received.
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) {
- PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR);
- if (!PE_CHK_FLAG(port, PE_FLAGS_PD_CONNECTION))
- set_state_pe(port, PE_SRC_DISCOVERY);
- else
- pe_send_soft_reset(port, TCPCI_MSG_SOP);
- return;
- }
-
- /*
- * Transition to the PE_SRC_Disabled state when:
- * 1) The Port Partners have not been PD Connected
- * 2) The NoResponseTimer times out
- * 3) And the HardResetCounter > nHardResetCount.
- *
- * Transition to the Error Recovery state when:
- * 1) The Port Partners have previously been PD Connected
- * 2) The NoResponseTimer times out
- * 3) And the HardResetCounter > nHardResetCount.
- */
- if (pd_timer_is_expired(port, PE_TIMER_NO_RESPONSE)) {
- if (pe[port].hard_reset_counter <= N_HARD_RESET_COUNT)
- set_state_pe(port, PE_SRC_HARD_RESET);
- else if (PE_CHK_FLAG(port, PE_FLAGS_PD_CONNECTION))
- set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY);
- else
- set_state_pe(port, PE_SRC_DISABLED);
- return;
- }
-
- /*
- * Transition to the PE_SRC_Hard_Reset state when:
- * 1) The SenderResponseTimer times out.
- */
- if (pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) {
- set_state_pe(port, PE_SRC_HARD_RESET);
- return;
- }
-}
-
-static void pe_src_send_capabilities_exit(int port)
-{
- pe_sender_response_msg_exit(port);
-}
-
-/**
- * PE_SRC_Negotiate_Capability
- */
-static void pe_src_negotiate_capability_entry(int port)
-{
- uint32_t payload;
-
- print_current_state(port);
-
- /* Get message payload */
- payload = *(uint32_t *)(&rx_emsg[port].buf);
-
- /*
- * Evaluate the Request from the Attached Sink
- */
-
- /*
- * Transition to the PE_SRC_Capability_Response state when:
- * 1) The Request cannot be met.
- * 2) Or the Request can be met later from the Power Reserve
- *
- * Transition to the PE_SRC_Transition_Supply state when:
- * 1) The Request can be met
- *
- */
- if (pd_check_requested_voltage(payload, port) != EC_SUCCESS) {
- set_state_pe(port, PE_SRC_CAPABILITY_RESPONSE);
- } else {
- PE_SET_FLAG(port, PE_FLAGS_ACCEPT);
- pe[port].requested_idx = RDO_POS(payload);
- set_state_pe(port, PE_SRC_TRANSITION_SUPPLY);
- }
-}
-
-/**
- * PE_SRC_Transition_Supply
- */
-static void pe_src_transition_supply_entry(int port)
-{
- print_current_state(port);
-
- /* Send a GotoMin Message or otherwise an Accept Message */
- if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) {
- PE_CLR_FLAG(port, PE_FLAGS_ACCEPT);
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_ACCEPT);
- } else {
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_GOTO_MIN);
- }
-}
-
-static void pe_src_transition_supply_run(int port)
-{
- /*
- * Transition to the PE_SRC_Ready state when:
- * 1) The power supply is ready.
- *
- * NOTE: This code block is executed twice:
- * First Pass)
- * When PE_FLAGS_TX_COMPLETE is set due to the
- * PD_CTRL_ACCEPT or PD_CTRL_GOTO_MIN messages
- * being sent.
- *
- * Second Pass)
- * When PE_FLAGS_TX_COMPLETE is set due to the
- * PD_CTRL_PS_RDY message being sent.
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
-
- /*
- * NOTE: If a message was received,
- * pe_src_ready state will handle it.
- */
-
- if (PE_CHK_FLAG(port, PE_FLAGS_PS_READY)) {
- PE_CLR_FLAG(port, PE_FLAGS_PS_READY);
-
- /*
- * Set first message flag to trigger a wait and add
- * jitter delay when operating in PD2.0 mode. Skip
- * if we already have a contract.
- */
- if (!pe_is_explicit_contract(port)) {
- PE_SET_FLAG(port, PE_FLAGS_FIRST_MSG);
- pd_timer_disable(port,
- PE_TIMER_WAIT_AND_ADD_JITTER);
- }
-
- /* NOTE: Second pass through this code block */
- /* Explicit Contract is now in place */
- pe_set_explicit_contract(port);
-
- /*
- * Setup to get Device Policy Manager to request
- * Source Capabilities, if needed, for possible
- * PR_Swap. Get the number directly to avoid re-probing
- * if the partner generated an error and left -1 for the
- * count.
- */
- if (pe[port].src_cap_cnt == 0)
- pd_dpm_request(port, DPM_REQUEST_GET_SRC_CAPS);
-
- set_state_pe(port, PE_SRC_READY);
- } else {
- /* NOTE: First pass through this code block */
- /* Wait for tSrcTransition before changing supply. */
- pd_timer_enable(port, PE_TIMER_SRC_TRANSITION,
- PD_T_SRC_TRANSITION);
- }
-
- return;
- }
-
- if (pd_timer_is_expired(port, PE_TIMER_SRC_TRANSITION)) {
- pd_timer_disable(port, PE_TIMER_SRC_TRANSITION);
- /* Transition power supply and send PS_RDY. */
- pd_transition_voltage(pe[port].requested_idx);
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PS_RDY);
- PE_SET_FLAG(port, PE_FLAGS_PS_READY);
- }
-
- /*
- * Transition to the PE_SRC_Hard_Reset state when:
- * 1) A Protocol Error occurs.
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) {
- PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR);
- set_state_pe(port, PE_SRC_HARD_RESET);
- }
-}
-
-static void pe_src_transition_supply_exit(int port)
-{
- pd_timer_disable(port, PE_TIMER_SRC_TRANSITION);
-}
-
-/*
- * Transitions state after receiving a Not Supported extended message. Under
- * appropriate conditions, transitions to a PE_{SRC,SNK}_Chunk_Received.
- */
-static void extended_message_not_supported(int port, uint32_t *payload)
-{
- uint16_t ext_header = GET_EXT_HEADER(*payload);
-
- if (IS_ENABLED(CONFIG_USB_PD_REV30) &&
- !IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES) &&
- PD_EXT_HEADER_CHUNKED(ext_header) &&
- PD_EXT_HEADER_DATA_SIZE(ext_header) >
- PD_MAX_EXTENDED_MSG_CHUNK_LEN) {
- set_state_pe(port,
- pe[port].power_role == PD_ROLE_SOURCE ?
- PE_SRC_CHUNK_RECEIVED : PE_SNK_CHUNK_RECEIVED);
- return;
- }
-
- set_state_pe(port, PE_SEND_NOT_SUPPORTED);
-}
-
-/**
- * PE_SRC_Ready
- */
-static void pe_src_ready_entry(int port)
-{
- print_current_state(port);
-
- /* Ensure any message send flags are cleaned up */
- PE_CLR_FLAG(port, PE_FLAGS_READY_CLR);
-
- /* Clear DPM Current Request */
- pe[port].dpm_curr_request = 0;
-
- /*
- * Wait and add jitter if we are operating in PD2.0 mode and no messages
- * have been sent since enter this state.
- */
- pe_update_wait_and_add_jitter_timer(port);
-}
-
-static void pe_src_ready_run(int port)
-{
- /*
- * Handle incoming messages before discovery and DPMs other than hard
- * reset
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- uint8_t type = PD_HEADER_TYPE(rx_emsg[port].header);
- uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header);
- uint8_t ext = PD_HEADER_EXT(rx_emsg[port].header);
- uint32_t *payload = (uint32_t *)rx_emsg[port].buf;
-
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
-
- /* Extended Message Requests */
- if (ext > 0) {
- switch (type) {
-#if defined(CONFIG_USB_PD_EXTENDED_MESSAGES) && defined(CONFIG_BATTERY)
- case PD_EXT_GET_BATTERY_CAP:
- set_state_pe(port, PE_GIVE_BATTERY_CAP);
- break;
- case PD_EXT_GET_BATTERY_STATUS:
- set_state_pe(port, PE_GIVE_BATTERY_STATUS);
- break;
-#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES && CONFIG_BATTERY */
- default:
- extended_message_not_supported(port, payload);
- }
- return;
- }
- /* Data Message Requests */
- else if (cnt > 0) {
- switch (type) {
- case PD_DATA_REQUEST:
- set_state_pe(port, PE_SRC_NEGOTIATE_CAPABILITY);
- return;
- case PD_DATA_SINK_CAP:
- break;
- case PD_DATA_VENDOR_DEF:
- if (PD_HEADER_TYPE(rx_emsg[port].header) ==
- PD_DATA_VENDOR_DEF) {
- if (PD_VDO_SVDM(*payload)) {
- set_state_pe(port,
- PE_VDM_RESPONSE);
- } else
- set_state_pe(port,
- PE_HANDLE_CUSTOM_VDM_REQUEST);
- }
- return;
- case PD_DATA_BIST:
- set_state_pe(port, PE_BIST_TX);
- return;
- default:
- set_state_pe(port, PE_SEND_NOT_SUPPORTED);
- return;
- }
- }
- /* Control Message Requests */
- else {
- switch (type) {
- case PD_CTRL_GOOD_CRC:
- break;
- case PD_CTRL_NOT_SUPPORTED:
- break;
- case PD_CTRL_PING:
- break;
- case PD_CTRL_GET_SOURCE_CAP:
- set_state_pe(port, PE_SRC_SEND_CAPABILITIES);
- return;
- case PD_CTRL_GET_SINK_CAP:
- set_state_pe(port, PE_SNK_GIVE_SINK_CAP);
- return;
- case PD_CTRL_GOTO_MIN:
- break;
- case PD_CTRL_PR_SWAP:
- set_state_pe(port,
- PE_PRS_SRC_SNK_EVALUATE_SWAP);
- return;
- case PD_CTRL_DR_SWAP:
- if (PE_CHK_FLAG(port,
- PE_FLAGS_MODAL_OPERATION)) {
- set_state_pe(port, PE_SRC_HARD_RESET);
- return;
- }
-
- set_state_pe(port, PE_DRS_EVALUATE_SWAP);
- return;
- case PD_CTRL_VCONN_SWAP:
- if (IS_ENABLED(CONFIG_USBC_VCONN))
- set_state_pe(port,
- PE_VCS_EVALUATE_SWAP);
- else
- set_state_pe(port,
- PE_SEND_NOT_SUPPORTED);
- return;
- /*
- * USB PD 3.0 6.8.1:
- * Receiving an unexpected message shall be responded
- * to with a soft reset message.
- */
- case PD_CTRL_ACCEPT:
- case PD_CTRL_REJECT:
- case PD_CTRL_WAIT:
- case PD_CTRL_PS_RDY:
- pe_send_soft_reset(port,
- PD_HEADER_GET_SOP(rx_emsg[port].header));
- return;
- /*
- * Receiving an unknown or unsupported message
- * shall be responded to with a not supported message.
- */
- default:
- set_state_pe(port, PE_SEND_NOT_SUPPORTED);
- return;
- }
- }
- }
-
- /*
- * Make sure the PRL layer isn't busy with receiving or transmitting
- * chunked messages before attempting to transmit a new message.
- */
- if (prl_is_busy(port))
- return;
-
- if (PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_CONTINUE)) {
- PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_CONTINUE);
- set_state_pe(port, PE_VDM_REQUEST_DPM);
- return;
- }
-
- if (PE_CHK_FLAG(port, PE_FLAGS_WAITING_PR_SWAP) &&
- pd_timer_is_expired(port, PE_TIMER_PR_SWAP_WAIT)) {
- PE_CLR_FLAG(port, PE_FLAGS_WAITING_PR_SWAP);
- PE_SET_DPM_REQUEST(port, DPM_REQUEST_PR_SWAP);
- }
-
- if (pd_timer_is_disabled(port, PE_TIMER_WAIT_AND_ADD_JITTER) ||
- pd_timer_is_expired(port, PE_TIMER_WAIT_AND_ADD_JITTER)) {
-
- PE_CLR_FLAG(port, PE_FLAGS_FIRST_MSG);
- pd_timer_disable(port, PE_TIMER_WAIT_AND_ADD_JITTER);
-
- /*
- * Handle Device Policy Manager Requests
- */
- if (source_dpm_requests(port))
- return;
-
- /*
- * Attempt discovery if possible, and return if state was
- * changed for that discovery.
- */
- if (pe_attempt_port_discovery(port))
- return;
-
- /* No DPM requests; attempt mode entry/exit if needed */
- dpm_run(port);
- }
-}
-
-/**
- * PE_SRC_Disabled
- */
-static void pe_src_disabled_entry(int port)
-{
- print_current_state(port);
-
- if ((get_usb_pd_cable_type(port) == IDH_PTYPE_VPD) &&
- is_vpd_ct_supported(port)) {
- /*
- * Inform the Device Policy Manager that a Charge-Through VCONN
- * Powered Device was detected.
- */
- tc_ctvpd_detected(port);
- }
-
- if (pd_get_power_role(port) == PD_ROLE_SOURCE)
- dpm_add_non_pd_sink(port);
-
- /*
- * Unresponsive to USB Power Delivery messaging, but not to Hard Reset
- * Signaling. See pe_got_hard_reset
- */
-}
-
-/**
- * PE_SRC_Capability_Response
- */
-static void pe_src_capability_response_entry(int port)
-{
- print_current_state(port);
-
- /* NOTE: Wait messaging should be implemented. */
-
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT);
-}
-
-static void pe_src_capability_response_run(int port)
-{
- /*
- * Transition to the PE_SRC_Ready state when:
- * 1) There is an Explicit Contract and
- * 2) A Reject Message has been sent and the present Contract is still
- * Valid or
- * 3) A Wait Message has been sent.
- *
- * Transition to the PE_SRC_Hard_Reset state when:
- * 1) There is an Explicit Contract and
- * 2) The Reject Message has been sent and the present
- * Contract is Invalid
- *
- * Transition to the PE_SRC_Wait_New_Capabilities state when:
- * 1) There is no Explicit Contract and
- * 2) A Reject Message has been sent or
- * 3) A Wait Message has been sent.
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
-
- if (PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT))
- /*
- * NOTE: The src capabilities listed in
- * board/xxx/usb_pd_policy.c will not
- * change so the present contract will
- * never be invalid.
- */
- set_state_pe(port, PE_SRC_READY);
- else
- /*
- * NOTE: The src capabilities listed in
- * board/xxx/usb_pd_policy.c will not
- * change, so no need to resending them
- * again. Transition to disabled state.
- */
- set_state_pe(port, PE_SRC_DISABLED);
- }
-}
-
-/**
- * PE_SRC_Hard_Reset
- */
-static void pe_src_hard_reset_entry(int port)
-{
- print_current_state(port);
-
- /* Generate Hard Reset Signal */
- prl_execute_hard_reset(port);
-
- /* Increment the HardResetCounter */
- pe[port].hard_reset_counter++;
-
- /* Start NoResponseTimer */
- pd_timer_enable(port, PE_TIMER_NO_RESPONSE, PD_T_NO_RESPONSE);
-
- /* Start PSHardResetTimer */
- pd_timer_enable(port, PE_TIMER_PS_HARD_RESET, PD_T_PS_HARD_RESET);
-
- /* Clear error flags */
- PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_NAKED |
- PE_FLAGS_PROTOCOL_ERROR |
- PE_FLAGS_VDM_REQUEST_BUSY);
-}
-
-static void pe_src_hard_reset_run(int port)
-{
- /*
- * Transition to the PE_SRC_Transition_to_default state when:
- * 1) The PSHardResetTimer times out.
- */
- if (pd_timer_is_expired(port, PE_TIMER_PS_HARD_RESET))
- set_state_pe(port, PE_SRC_TRANSITION_TO_DEFAULT);
-}
-
-static void pe_src_hard_reset_exit(int port)
-{
- pd_timer_disable(port, PE_TIMER_PS_HARD_RESET);
-}
-
-/**
- * PE_SRC_Hard_Reset_Received
- */
-static void pe_src_hard_reset_received_entry(int port)
-{
- print_current_state(port);
-
- /* Start NoResponseTimer */
- pd_timer_enable(port, PE_TIMER_NO_RESPONSE, PD_T_NO_RESPONSE);
-
- /* Start PSHardResetTimer */
- pd_timer_enable(port, PE_TIMER_PS_HARD_RESET, PD_T_PS_HARD_RESET);
-}
-
-static void pe_src_hard_reset_received_run(int port)
-{
- /*
- * Transition to the PE_SRC_Transition_to_default state when:
- * 1) The PSHardResetTimer times out.
- */
- if (pd_timer_is_expired(port, PE_TIMER_PS_HARD_RESET))
- set_state_pe(port, PE_SRC_TRANSITION_TO_DEFAULT);
-}
-
-static void pe_src_hard_reset_received_exit(int port)
-{
- pd_timer_disable(port, PE_TIMER_PS_HARD_RESET);
-}
-
-/**
- * PE_SRC_Transition_To_Default
- */
-static void pe_src_transition_to_default_entry(int port)
-{
- print_current_state(port);
-
- /* Reset flags */
- pe[port].flags = 0;
-
- /* Reset DPM Request */
- pe[port].dpm_request = 0;
-
- /*
- * Request Device Policy Manager to request power
- * supply Hard Resets to vSafe5V via vSafe0V
- * Reset local HW
- * Request Device Policy Manager to set Port Data
- * Role to DFP and turn off VCONN
- */
- tc_hard_reset_request(port);
-}
-
-static void pe_src_transition_to_default_run(int port)
-{
- /*
- * Transition to the PE_SRC_Startup state when:
- * 1) The power supply has reached the default level.
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_PS_RESET_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_PS_RESET_COMPLETE);
- /* Inform the Protocol Layer that the Hard Reset is complete */
- prl_hard_reset_complete(port);
- set_state_pe(port, PE_SRC_STARTUP);
- }
-}
-
-/**
- * PE_SNK_Startup State
- */
-static void pe_snk_startup_entry(int port)
-{
- print_current_state(port);
-
- /* Reset the protocol layer */
- prl_reset_soft(port);
-
- /* Set initial data role */
- pe[port].data_role = pd_get_data_role(port);
-
- /* Set initial power role */
- pe[port].power_role = PD_ROLE_SINK;
-
- /* Invalidate explicit contract */
- pe_invalidate_explicit_contract(port);
-
- if (PE_CHK_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE);
- /*
- * Protocol layer reset clears the message IDs for all SOP
- * types. Indicate that a SOP' soft reset is required before any
- * other messages are sent to the cable.
- *
- * Note that other paths into this state are for the initial
- * connection and for a hard reset. In both cases the cable
- * should also automatically clear the message IDs so don't
- * generate an SOP' soft reset for those cases. Sending
- * unnecessary SOP' soft resets causes bad behavior with
- * some devices. See b/179325862.
- */
- pd_dpm_request(port, DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND);
-
- /*
- * Some port partners may violate spec and attempt to
- * communicate with the cable after power role swaps, despite
- * not being Vconn source. Disable our SOP' receiving here to
- * avoid GoodCRC-ing any erroneous cable probes, and re-enable
- * after our contract is in place.
- */
- if (tc_is_vconn_src(port))
- tcpm_sop_prime_enable(port, false);
-
- dpm_remove_sink(port);
- } else {
- /*
- * Set DiscoverIdentityTimer to trigger when we enter
- * snk_ready for the first time.
- */
- pd_timer_enable(port, PE_TIMER_DISCOVER_IDENTITY, 0);
-
- /* Clear port discovery/mode flags */
- pd_dfp_discovery_init(port);
- pd_dfp_mode_init(port);
- pe[port].discover_identity_counter = 0;
-
- /* Reset dr swap attempt counter */
- pe[port].dr_swap_attempt_counter = 0;
-
- /* Reset VCONN swap counter */
- pe[port].vconn_swap_counter = 0;
- /*
- * TODO: POLICY decision:
- * Mark that we'd like to try being Vconn source and DFP
- */
- PE_SET_FLAG(port, PE_FLAGS_DR_SWAP_TO_DFP);
- PE_SET_FLAG(port, PE_FLAGS_VCONN_SWAP_TO_ON);
- }
-
- /*
- * Request sink caps for FRS, output power consideration, or reporting
- * to the AP through host commands.
- *
- * On entry to the PE_SNK_Ready state if the Sink supports Fast Role
- * Swap, then the Policy Engine Shall do the following:
- * - Send a Get_Sink_Cap Message
- */
- if (IS_ENABLED(CONFIG_USB_PD_HOST_CMD) ||
- CONFIG_USB_PD_3A_PORTS > 0 ||
- IS_ENABLED(CONFIG_USB_PD_FRS))
- pd_dpm_request(port, DPM_REQUEST_GET_SNK_CAPS);
-
-}
-
-static void pe_snk_startup_run(int port)
-{
- /* Wait until protocol layer is running */
- if (!prl_is_running(port))
- return;
-
- /*
- * Once the reset process completes, the Policy Engine Shall
- * transition to the PE_SNK_Discovery state
- */
- set_state_pe(port, PE_SNK_DISCOVERY);
-}
-
-/**
- * PE_SNK_Discovery State
- */
-static void pe_snk_discovery_entry(int port)
-{
- print_current_state(port);
-}
-
-static void pe_snk_discovery_run(int port)
-{
- /*
- * Transition to the PE_SNK_Wait_for_Capabilities state when:
- * 1) VBUS has been detected
- */
- if (!pd_check_vbus_level(port, VBUS_REMOVED))
- set_state_pe(port, PE_SNK_WAIT_FOR_CAPABILITIES);
-}
-
-/**
- * PE_SNK_Wait_For_Capabilities State
- */
-static void pe_snk_wait_for_capabilities_entry(int port)
-{
- print_current_state(port);
-
- /* Initialize and start the SinkWaitCapTimer */
- pd_timer_enable(port, PE_TIMER_TIMEOUT, PD_T_SINK_WAIT_CAP);
-}
-
-static void pe_snk_wait_for_capabilities_run(int port)
-{
- uint8_t type;
- uint8_t cnt;
- uint8_t ext;
-
- /*
- * Transition to the PE_SNK_Evaluate_Capability state when:
- * 1) A Source_Capabilities Message is received.
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
-
- type = PD_HEADER_TYPE(rx_emsg[port].header);
- cnt = PD_HEADER_CNT(rx_emsg[port].header);
- ext = PD_HEADER_EXT(rx_emsg[port].header);
-
- if ((ext == 0) && (cnt > 0) && (type == PD_DATA_SOURCE_CAP)) {
- set_state_pe(port, PE_SNK_EVALUATE_CAPABILITY);
- return;
- }
- }
-
- /* When the SinkWaitCapTimer times out, perform a Hard Reset. */
- if (pd_timer_is_expired(port, PE_TIMER_TIMEOUT)) {
- PE_SET_FLAG(port, PE_FLAGS_SNK_WAIT_CAP_TIMEOUT);
- set_state_pe(port, PE_SNK_HARD_RESET);
- }
-}
-
-static void pe_snk_wait_for_capabilities_exit(int port)
-{
- pd_timer_disable(port, PE_TIMER_TIMEOUT);
-}
-
-/**
- * PE_SNK_Evaluate_Capability State
- */
-static void pe_snk_evaluate_capability_entry(int port)
-{
- uint32_t *pdo = (uint32_t *)rx_emsg[port].buf;
- uint32_t num = rx_emsg[port].len >> 2;
-
- print_current_state(port);
-
- /* Reset Hard Reset counter to zero */
- pe[port].hard_reset_counter = 0;
-
- /* Set to highest revision supported by both ports. */
- prl_set_rev(port, TCPCI_MSG_SOP,
- MIN(PD_REVISION, PD_HEADER_REV(rx_emsg[port].header)));
-
- set_cable_rev(port);
-
- /* Parse source caps if they have changed */
- if (pe[port].src_cap_cnt != num ||
- memcmp(pdo, pe[port].src_caps, num << 2)) {
- /*
- * If port policy preference is to be a power role source,
- * then request a power role swap. If we'd previously queued a
- * PR swap but can now charge from this device, clear it.
- */
- if (!pd_can_charge_from_device(port, num, pdo))
- pd_request_power_swap(port);
- else
- PE_CLR_DPM_REQUEST(port, DPM_REQUEST_PR_SWAP);
- }
-
- pe_update_src_pdo_flags(port, num, pdo);
- pd_set_src_caps(port, num, pdo);
-
- /* Evaluate the options based on supplied capabilities */
- pd_process_source_cap(port, pe[port].src_cap_cnt, pe[port].src_caps);
-
- /* Device Policy Response Received */
- set_state_pe(port, PE_SNK_SELECT_CAPABILITY);
-
-#ifdef HAS_TASK_DPS
- /* Wake DPS task to evaluate the SrcCaps */
- task_wake(TASK_ID_DPS);
-#endif
-}
-
-/**
- * PE_SNK_Select_Capability State
- */
-static void pe_snk_select_capability_entry(int port)
-{
- print_current_state(port);
-
- /* Send Request */
- pe_send_request_msg(port);
- pe_sender_response_msg_entry(port);
-
- /* We are PD Connected */
- PE_SET_FLAG(port, PE_FLAGS_PD_CONNECTION);
- tc_pd_connection(port, 1);
-}
-
-static void pe_snk_select_capability_run(int port)
-{
- uint8_t type;
- uint8_t cnt;
- enum tcpci_msg_type sop;
- enum pe_msg_check msg_check;
-
- /*
- * Check the state of the message sent
- */
- msg_check = pe_sender_response_msg_run(port);
-
- /*
- * Handle discarded message
- */
- if (msg_check & PE_MSG_DISCARDED) {
- /*
- * The sent REQUEST message was discarded. This can be at
- * the start of an AMS or in the middle. Handle what to
- * do based on where we came from.
- * 1) SE_SNK_EVALUATE_CAPABILITY: sends SoftReset
- * 2) SE_SNK_READY: goes back to SNK Ready
- */
- if (get_last_state_pe(port) == PE_SNK_EVALUATE_CAPABILITY)
- pe_send_soft_reset(port, TCPCI_MSG_SOP);
- else
- set_state_pe(port, PE_SNK_READY);
- return;
- }
-
- if ((msg_check & PE_MSG_SENT) &&
- PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
- type = PD_HEADER_TYPE(rx_emsg[port].header);
- cnt = PD_HEADER_CNT(rx_emsg[port].header);
- sop = PD_HEADER_GET_SOP(rx_emsg[port].header);
-
- /*
- * Transition to the PE_SNK_Transition_Sink state when:
- * 1) An Accept Message is received from the Source.
- *
- * Transition to the PE_SNK_Wait_for_Capabilities state when:
- * 1) There is no Explicit Contract in place and
- * 2) A Reject Message is received from the Source or
- * 3) A Wait Message is received from the Source.
- *
- * Transition to the PE_SNK_Ready state when:
- * 1) There is an Explicit Contract in place and
- * 2) A Reject Message is received from the Source or
- * 3) A Wait Message is received from the Source.
- *
- * Transition to the PE_SNK_Hard_Reset state when:
- * 1) A SenderResponseTimer timeout occurs.
- */
-
- /* Only look at control messages */
- if (cnt == 0) {
- /*
- * Accept Message Received
- */
- if (type == PD_CTRL_ACCEPT) {
- /* explicit contract is now in place */
- pe_set_explicit_contract(port);
-
- set_state_pe(port, PE_SNK_TRANSITION_SINK);
-
- return;
- }
- /*
- * Reject or Wait Message Received
- */
- else if (type == PD_CTRL_REJECT ||
- type == PD_CTRL_WAIT) {
- if (type == PD_CTRL_WAIT)
- PE_SET_FLAG(port, PE_FLAGS_WAIT);
-
- pd_timer_disable(port, PE_TIMER_SINK_REQUEST);
-
- /*
- * We had a previous explicit contract, so
- * transition to PE_SNK_Ready
- */
- if (PE_CHK_FLAG(port,
- PE_FLAGS_EXPLICIT_CONTRACT))
- set_state_pe(port, PE_SNK_READY);
- /*
- * No previous explicit contract, so transition
- * to PE_SNK_Wait_For_Capabilities
- */
- else
- set_state_pe(port,
- PE_SNK_WAIT_FOR_CAPABILITIES);
- return;
- }
- /*
- * Unexpected Control Message Received
- */
- else {
- /* Send Soft Reset */
- pe_send_soft_reset(port, sop);
- return;
- }
- }
- /*
- * Unexpected Data Message
- */
- else {
- /* Send Soft Reset */
- pe_send_soft_reset(port, sop);
- return;
- }
- }
-
- /* SenderResponsetimer timeout */
- if (pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE))
- set_state_pe(port, PE_SNK_HARD_RESET);
-}
-
-void pe_snk_select_capability_exit(int port)
-{
- pe_sender_response_msg_exit(port);
-}
-
-/**
- * PE_SNK_Transition_Sink State
- */
-static void pe_snk_transition_sink_entry(int port)
-{
- print_current_state(port);
-
- /* Initialize and run PSTransitionTimer */
- pd_timer_enable(port, PE_TIMER_PS_TRANSITION, PD_T_PS_TRANSITION);
-}
-
-static void pe_snk_transition_sink_run(int port)
-{
- /*
- * Transition to the PE_SNK_Ready state when:
- * 1) A PS_RDY Message is received from the Source.
- *
- * Transition to the PE_SNK_Hard_Reset state when:
- * 1) A Protocol Error occurs.
- */
-
- if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
-
- /*
- * PS_RDY message received
- */
- if ((PD_HEADER_CNT(rx_emsg[port].header) == 0) &&
- (PD_HEADER_TYPE(rx_emsg[port].header) ==
- PD_CTRL_PS_RDY)) {
- /*
- * Set first message flag to trigger a wait and add
- * jitter delay when operating in PD2.0 mode.
- */
- PE_SET_FLAG(port, PE_FLAGS_FIRST_MSG);
- pd_timer_disable(port, PE_TIMER_WAIT_AND_ADD_JITTER);
-
- /*
- * If we've successfully completed our new power
- * contract, ensure SOP' communication is enabled before
- * entering PE_SNK_READY. It may have been disabled
- * during a power role swap to avoid interoperability
- * issues with out-of-spec partners.
- */
- if (tc_is_vconn_src(port))
- tcpm_sop_prime_enable(port, true);
-
- /*
- * Evaluate port's sink caps for FRS current, if
- * already available
- */
- if (pd_get_snk_cap_cnt(port) > 0)
- dpm_evaluate_sink_fixed_pdo(port,
- *pd_get_snk_caps(port));
-
- set_state_pe(port, PE_SNK_READY);
- } else {
- /*
- * Protocol Error
- */
- set_state_pe(port, PE_SNK_HARD_RESET);
- }
- return;
- }
-
- /*
- * Timeout will lead to a Hard Reset
- */
- if (pd_timer_is_expired(port, PE_TIMER_PS_TRANSITION) &&
- pe[port].hard_reset_counter <= N_HARD_RESET_COUNT) {
- PE_SET_FLAG(port, PE_FLAGS_PS_TRANSITION_TIMEOUT);
-
- set_state_pe(port, PE_SNK_HARD_RESET);
- }
-}
-
-static void pe_snk_transition_sink_exit(int port)
-{
- /* Transition Sink's power supply to the new power level */
- pd_set_input_current_limit(port,
- pe[port].curr_limit, pe[port].supply_voltage);
-
- if (IS_ENABLED(CONFIG_CHARGE_MANAGER))
- /* Set ceiling based on what's negotiated */
- charge_manager_set_ceil(port,
- CEIL_REQUESTOR_PD, pe[port].curr_limit);
-
- pd_timer_disable(port, PE_TIMER_PS_TRANSITION);
-
- if (IS_ENABLED(CONFIG_USB_PD_DPS))
- if (charge_manager_get_active_charge_port() == port)
- dps_update_stabilized_time(port);
-}
-
-
-/**
- * PE_SNK_Ready State
- */
-static void pe_snk_ready_entry(int port)
-{
- print_current_state(port);
-
- /* Ensure any message send flags are cleaned up */
- PE_CLR_FLAG(port, PE_FLAGS_READY_CLR);
-
- /* Clear DPM Current Request */
- pe[port].dpm_curr_request = 0;
-
- /*
- * On entry to the PE_SNK_Ready state as the result of a wait,
- * then do the following:
- * 1) Initialize and run the SinkRequestTimer
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_WAIT)) {
- PE_CLR_FLAG(port, PE_FLAGS_WAIT);
- pd_timer_enable(port, PE_TIMER_SINK_REQUEST,
- PD_T_SINK_REQUEST);
- }
-
- /*
- * Wait and add jitter if we are operating in PD2.0 mode and no messages
- * have been sent since enter this state.
- */
- pe_update_wait_and_add_jitter_timer(port);
-}
-
-static void pe_snk_ready_run(int port)
-{
- /*
- * Handle incoming messages before discovery and DPMs other than hard
- * reset
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- uint8_t type = PD_HEADER_TYPE(rx_emsg[port].header);
- uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header);
- uint8_t ext = PD_HEADER_EXT(rx_emsg[port].header);
- uint32_t *payload = (uint32_t *)rx_emsg[port].buf;
-
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
-
- /* Extended Message Request */
- if (ext > 0) {
- switch (type) {
-#if defined(CONFIG_USB_PD_EXTENDED_MESSAGES) && defined(CONFIG_BATTERY)
- case PD_EXT_GET_BATTERY_CAP:
- set_state_pe(port, PE_GIVE_BATTERY_CAP);
- break;
- case PD_EXT_GET_BATTERY_STATUS:
- set_state_pe(port, PE_GIVE_BATTERY_STATUS);
- break;
-#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES && CONFIG_BATTERY */
- default:
- extended_message_not_supported(port, payload);
- }
- return;
- }
- /* Data Messages */
- else if (cnt > 0) {
- switch (type) {
- case PD_DATA_SOURCE_CAP:
- set_state_pe(port,
- PE_SNK_EVALUATE_CAPABILITY);
- break;
- case PD_DATA_VENDOR_DEF:
- if (PD_HEADER_TYPE(rx_emsg[port].header) ==
- PD_DATA_VENDOR_DEF) {
- if (PD_VDO_SVDM(*payload))
- set_state_pe(port,
- PE_VDM_RESPONSE);
- else
- set_state_pe(port,
- PE_HANDLE_CUSTOM_VDM_REQUEST);
- }
- break;
- case PD_DATA_BIST:
- set_state_pe(port, PE_BIST_TX);
- break;
- default:
- set_state_pe(port, PE_SEND_NOT_SUPPORTED);
- }
- return;
- }
- /* Control Messages */
- else {
- switch (type) {
- case PD_CTRL_GOOD_CRC:
- /* Do nothing */
- break;
- case PD_CTRL_PING:
- /* Do nothing */
- break;
- case PD_CTRL_GET_SOURCE_CAP:
- set_state_pe(port, PE_DR_SNK_GIVE_SOURCE_CAP);
- return;
- case PD_CTRL_GET_SINK_CAP:
- set_state_pe(port, PE_SNK_GIVE_SINK_CAP);
- return;
- case PD_CTRL_GOTO_MIN:
- set_state_pe(port, PE_SNK_TRANSITION_SINK);
- return;
- case PD_CTRL_PR_SWAP:
- set_state_pe(port,
- PE_PRS_SNK_SRC_EVALUATE_SWAP);
- return;
- case PD_CTRL_DR_SWAP:
- if (PE_CHK_FLAG(port, PE_FLAGS_MODAL_OPERATION))
- set_state_pe(port, PE_SNK_HARD_RESET);
- else
- set_state_pe(port,
- PE_DRS_EVALUATE_SWAP);
- return;
- case PD_CTRL_VCONN_SWAP:
- if (IS_ENABLED(CONFIG_USBC_VCONN))
- set_state_pe(port,
- PE_VCS_EVALUATE_SWAP);
- else
- set_state_pe(port,
- PE_SEND_NOT_SUPPORTED);
- return;
- case PD_CTRL_NOT_SUPPORTED:
- /* Do nothing */
- break;
- /*
- * USB PD 3.0 6.8.1:
- * Receiving an unexpected message shall be responded
- * to with a soft reset message.
- */
- case PD_CTRL_ACCEPT:
- case PD_CTRL_REJECT:
- case PD_CTRL_WAIT:
- case PD_CTRL_PS_RDY:
- pe_send_soft_reset(port,
- PD_HEADER_GET_SOP(rx_emsg[port].header));
- return;
- /*
- * Receiving an unknown or unsupported message
- * shall be responded to with a not supported message.
- */
- default:
- set_state_pe(port, PE_SEND_NOT_SUPPORTED);
- return;
- }
- }
- }
-
- /*
- * Make sure the PRL layer isn't busy with receiving or transmitting
- * chunked messages before attempting to transmit a new message.
- */
- if (prl_is_busy(port))
- return;
-
- if (PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_CONTINUE)) {
- PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_CONTINUE);
- set_state_pe(port, PE_VDM_REQUEST_DPM);
- return;
- }
-
- if (pd_timer_is_disabled(port, PE_TIMER_WAIT_AND_ADD_JITTER) ||
- pd_timer_is_expired(port, PE_TIMER_WAIT_AND_ADD_JITTER)) {
- PE_CLR_FLAG(port, PE_FLAGS_FIRST_MSG);
- pd_timer_disable(port, PE_TIMER_WAIT_AND_ADD_JITTER);
-
- if (pd_timer_is_expired(port, PE_TIMER_SINK_REQUEST)) {
- pd_timer_disable(port, PE_TIMER_SINK_REQUEST);
- set_state_pe(port, PE_SNK_SELECT_CAPABILITY);
- return;
- }
-
- /*
- * Handle Device Policy Manager Requests
- */
- if (sink_dpm_requests(port))
- return;
-
- /*
- * Attempt discovery if possible, and return if state was
- * changed for that discovery.
- */
- if (pe_attempt_port_discovery(port))
- return;
-
- /* No DPM requests; attempt mode entry/exit if needed */
- dpm_run(port);
-
- }
-}
-
-/**
- * PE_SNK_Hard_Reset
- */
-static void pe_snk_hard_reset_entry(int port)
-{
-#ifdef CONFIG_USB_PD_RESET_MIN_BATT_SOC
- int batt_soc;
-#endif
-
- print_current_state(port);
-
- /*
- * Note: If the SinkWaitCapTimer times out and the HardResetCounter is
- * greater than nHardResetCount the Sink Shall assume that the
- * Source is non-responsive.
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_SNK_WAIT_CAP_TIMEOUT) &&
- pe[port].hard_reset_counter > N_HARD_RESET_COUNT) {
- set_state_pe(port, PE_SRC_DISABLED);
- return;
- }
-
- /*
- * If we're about to kill our active charge port and have no battery to
- * supply power, disable the PE layer instead. If we have no battery,
- * but we haven't determined our active charge port yet, also avoid
- * performing the HardReset. It might be that this port was our active
- * charge port.
- *
- * Note: On systems without batteries (ex. chromeboxes), it's preferable
- * to brown out rather than leave the port only semi-functional for a
- * customer. For systems which should have a battery, this condition is
- * not expected to be encountered by a customer.
- */
- if (IS_ENABLED(CONFIG_BATTERY) && (battery_is_present() == BP_NO) &&
- IS_ENABLED(CONFIG_CHARGE_MANAGER) &&
- ((port == charge_manager_get_active_charge_port() ||
- (charge_manager_get_active_charge_port() == CHARGE_PORT_NONE))) &&
- system_get_reset_flags() & EC_RESET_FLAG_SYSJUMP) {
- CPRINTS("C%d: Disabling port to avoid brown out, "
- "please reboot EC to enable port again", port);
- set_state_pe(port, PE_SRC_DISABLED);
- return;
-
- }
-
-#ifdef CONFIG_USB_PD_RESET_MIN_BATT_SOC
- /*
- * If the battery has not met a configured safe level for hard
- * resets, set state to PE_SRC_Disabled as a hard
- * reset could brown out the board.
- * Note this may mean that high-power chargers will stay at
- * 15W until a reset is sent, depending on boot timing.
- *
- * PE_FLAGS_SNK_WAITING_BATT flags will be cleared and
- * PE state will be switched to PE_SNK_Startup when
- * battery reaches CONFIG_USB_PD_RESET_MIN_BATT_SOC.
- * See pe_update_waiting_batt_flag() for more details.
- */
- batt_soc = usb_get_battery_soc();
-
- if (batt_soc < CONFIG_USB_PD_RESET_MIN_BATT_SOC ||
- battery_get_disconnect_state() != BATTERY_NOT_DISCONNECTED) {
- PE_SET_FLAG(port, PE_FLAGS_SNK_WAITING_BATT);
- CPRINTS("C%d: Battery low %d%%! Stay in disabled state " \
- "until battery level reaches %d%%", port, batt_soc,
- CONFIG_USB_PD_RESET_MIN_BATT_SOC);
- set_state_pe(port, PE_SRC_DISABLED);
- return;
- }
-#endif
-
- PE_CLR_FLAG(port, PE_FLAGS_SNK_WAIT_CAP_TIMEOUT |
- PE_FLAGS_VDM_REQUEST_NAKED |
- PE_FLAGS_PROTOCOL_ERROR |
- PE_FLAGS_VDM_REQUEST_BUSY);
-
- /* Request the generation of Hard Reset Signaling by the PHY Layer */
- prl_execute_hard_reset(port);
-
- /* Increment the HardResetCounter */
- pe[port].hard_reset_counter++;
-
- /*
- * Transition the Sink’s power supply to the new power level if
- * PSTransistionTimer timeout occurred.
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_PS_TRANSITION_TIMEOUT)) {
- PE_CLR_FLAG(port, PE_FLAGS_PS_TRANSITION_TIMEOUT);
-
- /* Transition Sink's power supply to the new power level */
- pd_set_input_current_limit(port, pe[port].curr_limit,
- pe[port].supply_voltage);
- if (IS_ENABLED(CONFIG_CHARGE_MANAGER))
- /* Set ceiling based on what's negotiated */
- charge_manager_set_ceil(port, CEIL_REQUESTOR_PD,
- pe[port].curr_limit);
- }
-}
-
-static void pe_snk_hard_reset_run(int port)
-{
- /*
- * Transition to the PE_SNK_Transition_to_default state when:
- * 1) The Hard Reset is complete.
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_HARD_RESET_PENDING))
- return;
-
- set_state_pe(port, PE_SNK_TRANSITION_TO_DEFAULT);
-}
-
-/**
- * PE_SNK_Transition_to_default
- */
-static void pe_snk_transition_to_default_entry(int port)
-{
- print_current_state(port);
-
- /* Reset flags */
- pe[port].flags = 0;
-
- /* Reset DPM Request */
- pe[port].dpm_request = 0;
-
- /* Inform the TC Layer of Hard Reset */
- tc_hard_reset_request(port);
-}
-
-static void pe_snk_transition_to_default_run(int port)
-{
- if (PE_CHK_FLAG(port, PE_FLAGS_PS_RESET_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_PS_RESET_COMPLETE);
- /* Inform the Protocol Layer that the Hard Reset is complete */
- prl_hard_reset_complete(port);
- set_state_pe(port, PE_SNK_STARTUP);
- }
-}
-
-/**
- * PE_SNK_Get_Source_Cap
- */
-static void pe_snk_get_source_cap_entry(int port)
-{
- print_current_state(port);
-
- /* Send a Get_Source_Cap Message */
- tx_emsg[port].len = 0;
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_GET_SOURCE_CAP);
-}
-
-static void pe_snk_get_source_cap_run(int port)
-{
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
-
- set_state_pe(port, PE_SNK_READY);
- }
-}
-
-/**
- * PE_SNK_Send_Soft_Reset and PE_SRC_Send_Soft_Reset
- */
-static void pe_send_soft_reset_entry(int port)
-{
- print_current_state(port);
-
- /* Reset Protocol Layer (softly) */
- prl_reset_soft(port);
-
- pe_sender_response_msg_entry(port);
-
- /*
- * Mark the temporary timer PE_TIMER_TIMEOUT as expired to limit
- * to sending a single SoftReset message.
- */
- pd_timer_enable(port, PE_TIMER_TIMEOUT, 0);
-}
-
-static void pe_send_soft_reset_run(int port)
-{
- int type;
- int cnt;
- int ext;
- enum pe_msg_check msg_check;
-
- /* Wait until protocol layer is running */
- if (!prl_is_running(port))
- return;
-
- /*
- * Protocol layer is running, so need to send a single SoftReset.
- * Use temporary timer to act as a flag to keep this as a single
- * message send.
- */
- if (!pd_timer_is_disabled(port, PE_TIMER_TIMEOUT)) {
- pd_timer_disable(port, PE_TIMER_TIMEOUT);
-
- /*
- * TODO(b/150614211): Soft reset type should match
- * unexpected incoming message type
- */
- /* Send Soft Reset message */
- send_ctrl_msg(port,
- pe[port].soft_reset_sop, PD_CTRL_SOFT_RESET);
-
- return;
- }
-
- /*
- * Check the state of the message sent
- */
- msg_check = pe_sender_response_msg_run(port);
-
- /*
- * Handle discarded message
- */
- if (msg_check == PE_MSG_DISCARDED) {
- pe_set_ready_state(port);
- return;
- }
-
- /*
- * Transition to the PE_SNK_Send_Capabilities or
- * PE_SRC_Send_Capabilities state when:
- * 1) An Accept Message has been received.
- */
- if (msg_check == PE_MSG_SENT &&
- PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
-
- type = PD_HEADER_TYPE(rx_emsg[port].header);
- cnt = PD_HEADER_CNT(rx_emsg[port].header);
- ext = PD_HEADER_EXT(rx_emsg[port].header);
-
- if ((ext == 0) && (cnt == 0) && (type == PD_CTRL_ACCEPT)) {
- if (pe[port].power_role == PD_ROLE_SINK)
- set_state_pe(port,
- PE_SNK_WAIT_FOR_CAPABILITIES);
- else
- set_state_pe(port,
- PE_SRC_SEND_CAPABILITIES);
- return;
- }
- }
-
- /*
- * Transition to PE_SNK_Hard_Reset or PE_SRC_Hard_Reset on Sender
- * Response Timer Timeout or Protocol Layer or Protocol Error
- */
- if (pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE) ||
- PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) {
- PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR);
-
- if (pe[port].power_role == PD_ROLE_SINK)
- set_state_pe(port, PE_SNK_HARD_RESET);
- else
- set_state_pe(port, PE_SRC_HARD_RESET);
- return;
- }
-}
-
-static void pe_send_soft_reset_exit(int port)
-{
- pe_sender_response_msg_exit(port);
- pd_timer_disable(port, PE_TIMER_TIMEOUT);
-}
-
-/**
- * PE_SNK_Soft_Reset and PE_SNK_Soft_Reset
- */
-static void pe_soft_reset_entry(int port)
-{
- print_current_state(port);
-
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_ACCEPT);
-}
-
-static void pe_soft_reset_run(int port)
-{
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
-
- if (pe[port].power_role == PD_ROLE_SINK)
- set_state_pe(port, PE_SNK_WAIT_FOR_CAPABILITIES);
- else
- set_state_pe(port, PE_SRC_SEND_CAPABILITIES);
- } else if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) {
- PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR);
-
- if (pe[port].power_role == PD_ROLE_SINK)
- set_state_pe(port, PE_SNK_HARD_RESET);
- else
- set_state_pe(port, PE_SRC_HARD_RESET);
- }
-}
-
-/**
- * PE_SRC_Not_Supported and PE_SNK_Not_Supported
- *
- * 6.7.1 Soft Reset and Protocol Error (Revision 2.0, Version 1.3)
- * An unrecognized or unsupported Message (except for a Structured VDM),
- * received in the PE_SNK_Ready or PE_SRC_Ready states, Shall Not cause
- * a Soft_Reset Message to be generated but instead a Reject Message
- * Shall be generated.
- */
-static void pe_send_not_supported_entry(int port)
-{
- print_current_state(port);
-
- /* Request the Protocol Layer to send a Not_Supported Message. */
- if (prl_get_rev(port, TCPCI_MSG_SOP) > PD_REV20)
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_NOT_SUPPORTED);
- else
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT);
-}
-
-static void pe_send_not_supported_run(int port)
-{
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
- pe_set_ready_state(port);
-
- }
-}
-
-/**
- * PE_SRC_Chunk_Received and PE_SNK_Chunk_Received
- *
- * 6.11.2.1.1 Architecture of Device Including Chunking Layer (Revision 3.0,
- * Version 2.0): If a PD Device or Cable Marker has no requirement to handle any
- * message requiring more than one Chunk of any Extended Message, it May omit
- * the Chunking Layer. In this case it Shall implement the
- * ChunkingNotSupportedTimer to ensure compatible operation with partners which
- * support Chunking.
- *
- * See also:
- * 6.6.18.1 ChunkingNotSupportedTimer
- * 8.3.3.6 Not Supported Message State Diagrams
- */
-__maybe_unused static void pe_chunk_received_entry(int port)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_REV30) ||
- IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES))
- assert(0);
-
- print_current_state(port);
- pd_timer_enable(port, PE_TIMER_CHUNKING_NOT_SUPPORTED,
- PD_T_CHUNKING_NOT_SUPPORTED);
-}
-
-__maybe_unused static void pe_chunk_received_run(int port)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_REV30) ||
- IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES))
- assert(0);
-
- if (pd_timer_is_expired(port, PE_TIMER_CHUNKING_NOT_SUPPORTED))
- set_state_pe(port, PE_SEND_NOT_SUPPORTED);
-}
-
-__maybe_unused static void pe_chunk_received_exit(int port)
-{
- pd_timer_disable(port, PE_TIMER_CHUNKING_NOT_SUPPORTED);
-}
-
-/**
- * PE_SRC_Ping
- */
-static void pe_src_ping_entry(int port)
-{
- print_current_state(port);
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PING);
-}
-
-static void pe_src_ping_run(int port)
-{
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
- set_state_pe(port, PE_SRC_READY);
- }
-}
-
-#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES
-/**
- * PE_Give_Battery_Cap
- */
-static void pe_give_battery_cap_entry(int port)
-{
- uint8_t *payload = rx_emsg[port].buf;
- uint16_t *msg = (uint16_t *)tx_emsg[port].buf;
-
- if (!IS_ENABLED(CONFIG_BATTERY))
- return;
- print_current_state(port);
-
- /* Set VID */
- msg[BCDB_VID] = USB_VID_GOOGLE;
-
- /* Set PID */
- msg[BCDB_PID] = CONFIG_USB_PID;
-
- if (battery_is_present()) {
- /*
- * We only have one fixed battery,
- * so make sure batt cap ref is 0.
- * This value is the first byte after the headers.
- */
- if (payload[0] != 0) {
- /* Invalid battery reference */
- msg[BCDB_DESIGN_CAP] = 0;
- msg[BCDB_FULL_CAP] = 0;
- /* Set invalid battery bit in response bit 0, byte 8 */
- msg[BCDB_BATT_TYPE] = 1;
- } else {
- /*
- * The Battery Design Capacity field shall return the
- * Battery’s design capacity in tenths of Wh. If the
- * Battery is Hot Swappable and is not present, the
- * Battery Design Capacity field shall be set to 0. If
- * the Battery is unable to report its Design Capacity,
- * it shall return 0xFFFF
- */
- msg[BCDB_DESIGN_CAP] = 0xffff;
-
- /*
- * The Battery Last Full Charge Capacity field shall
- * return the Battery’s last full charge capacity in
- * tenths of Wh. If the Battery is Hot Swappable and
- * is not present, the Battery Last Full Charge Capacity
- * field shall be set to 0. If the Battery is unable to
- * report its Design Capacity, the Battery Last Full
- * Charge Capacity field shall be set to 0xFFFF.
- */
- msg[BCDB_FULL_CAP] = 0xffff;
-
-
- if (IS_ENABLED(HAS_TASK_HOSTCMD) &&
- *host_get_memmap(EC_MEMMAP_BATTERY_VERSION) != 0) {
- int design_volt, design_cap, full_cap;
-
- design_volt = *(int *)host_get_memmap(
- EC_MEMMAP_BATT_DVLT);
- design_cap = *(int *)host_get_memmap(
- EC_MEMMAP_BATT_DCAP);
- full_cap = *(int *)host_get_memmap(
- EC_MEMMAP_BATT_LFCC);
-
- /*
- * Wh = (c * v) / 1000000
- * 10th of a Wh = Wh * 10
- */
- msg[BCDB_DESIGN_CAP] = DIV_ROUND_NEAREST(
- (design_cap * design_volt),
- 100000);
- /*
- * Wh = (c * v) / 1000000
- * 10th of a Wh = Wh * 10
- */
- msg[BCDB_FULL_CAP] = DIV_ROUND_NEAREST(
- (design_cap * full_cap),
- 100000);
- } else {
- uint32_t v;
- uint32_t c;
-
- if (battery_design_voltage(&v) == 0) {
- if (battery_design_capacity(&c) == 0) {
- /*
- * Wh = (c * v) / 1000000
- * 10th of a Wh = Wh * 10
- */
- msg[BCDB_DESIGN_CAP] =
- DIV_ROUND_NEAREST(
- (c * v),
- 100000);
- }
-
- if (battery_full_charge_capacity(&c)
- == 0) {
- /*
- * Wh = (c * v) / 1000000
- * 10th of a Wh = Wh * 10
- */
- msg[BCDB_FULL_CAP] =
- DIV_ROUND_NEAREST(
- (c * v),
- 100000);
- }
- }
-
- }
- /* Valid battery selected */
- msg[BCDB_BATT_TYPE] = 0;
- }
- } else {
- /* Battery not present indicated by 0's in the capacity */
- msg[BCDB_DESIGN_CAP] = 0;
- msg[BCDB_FULL_CAP] = 0;
- if (payload[0] != 0)
- msg[BCDB_BATT_TYPE] = 1;
- else
- msg[BCDB_BATT_TYPE] = 0;
- }
-
- /* Extended Battery Cap data is 9 bytes */
- tx_emsg[port].len = 9;
-
- send_ext_data_msg(port, TCPCI_MSG_SOP, PD_EXT_BATTERY_CAP);
-}
-
-static void pe_give_battery_cap_run(int port)
-{
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
- pe_set_ready_state(port);
- }
-}
-
-/**
- * PE_Give_Battery_Status
- */
-static void pe_give_battery_status_entry(int port)
-{
- uint8_t *payload = rx_emsg[port].buf;
- uint32_t *msg = (uint32_t *)tx_emsg[port].buf;
-
- if (!IS_ENABLED(CONFIG_BATTERY))
- return;
- print_current_state(port);
-
- if (battery_is_present()) {
- /*
- * We only have one fixed battery,
- * so make sure batt cap ref is 0.
- * This value is the first byte after the headers.
- */
- if (payload[0] != 0) {
- /* Invalid battery reference */
- *msg = BSDO_CAP(BSDO_CAP_UNKNOWN);
- *msg |= BSDO_INVALID;
- } else {
- uint32_t v;
- uint32_t c;
-
- *msg = BSDO_CAP(BSDO_CAP_UNKNOWN);
-
- if (IS_ENABLED(HAS_TASK_HOSTCMD) &&
- *host_get_memmap(EC_MEMMAP_BATTERY_VERSION) != 0) {
- v = *(int *)host_get_memmap(
- EC_MEMMAP_BATT_DVLT);
- c = *(int *)host_get_memmap(
- EC_MEMMAP_BATT_CAP);
-
- /*
- * Wh = (c * v) / 1000000
- * 10th of a Wh = Wh * 10
- */
- *msg = BSDO_CAP(DIV_ROUND_NEAREST((c * v),
- 100000));
- } else if (battery_design_voltage(&v) == 0 &&
- battery_remaining_capacity(&c) == 0) {
- /*
- * Wh = (c * v) / 1000000
- * 10th of a Wh = Wh * 10
- */
- *msg = BSDO_CAP(DIV_ROUND_NEAREST((c * v),
- 100000));
- }
-
- /* Battery is present */
- *msg |= BSDO_PRESENT;
-
- /*
- * For drivers that are not smart battery compliant,
- * battery_status() returns EC_ERROR_UNIMPLEMENTED and
- * the battery is assumed to be idle.
- */
- if (battery_status(&c) != 0) {
- *msg |= BSDO_IDLE; /* assume idle */
- } else {
- if (c & STATUS_FULLY_CHARGED)
- /* Fully charged */
- *msg |= BSDO_IDLE;
- else if (c & STATUS_DISCHARGING)
- /* Discharging */
- *msg |= BSDO_DISCHARGING;
- /* else battery is charging.*/
- }
- }
- } else {
- *msg = BSDO_CAP(BSDO_CAP_UNKNOWN);
- if (payload[0] != 0)
- *msg |= BSDO_INVALID;
- }
-
- /* Battery Status data is 4 bytes */
- tx_emsg[port].len = 4;
-
- send_data_msg(port, TCPCI_MSG_SOP, PD_DATA_BATTERY_STATUS);
-}
-
-static void pe_give_battery_status_run(int port)
-{
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
- set_state_pe(port, PE_SRC_READY);
- }
-}
-
-/**
- * PE_SRC_Send_Source_Alert and
- * PE_SNK_Send_Sink_Alert
- */
-static void pe_send_alert_entry(int port)
-{
- uint32_t *msg = (uint32_t *)tx_emsg[port].buf;
- uint32_t *len = &tx_emsg[port].len;
-
- print_current_state(port);
-
- if (pd_build_alert_msg(msg, len, pe[port].power_role) != EC_SUCCESS)
- pe_set_ready_state(port);
-
- /* Request the Protocol Layer to send Alert Message. */
- send_data_msg(port, TCPCI_MSG_SOP, PD_DATA_ALERT);
-}
-
-static void pe_send_alert_run(int port)
-{
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
- pe_set_ready_state(port);
- }
-}
-#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */
-
-/**
- * PE_DRS_Evaluate_Swap
- */
-static void pe_drs_evaluate_swap_entry(int port)
-{
- print_current_state(port);
-
- /* Get evaluation of Data Role Swap request from DPM */
- if (pd_check_data_swap(port, pe[port].data_role)) {
- PE_SET_FLAG(port, PE_FLAGS_ACCEPT);
- /*
- * PE_DRS_UFP_DFP_Evaluate_Swap and
- * PE_DRS_DFP_UFP_Evaluate_Swap states embedded here.
- */
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_ACCEPT);
- } else {
- /*
- * PE_DRS_UFP_DFP_Reject_Swap and PE_DRS_DFP_UFP_Reject_Swap
- * states embedded here.
- */
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT);
- }
-}
-
-static void pe_drs_evaluate_swap_run(int port)
-{
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
-
- /* Accept Message sent. Transtion to PE_DRS_Change */
- if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) {
- PE_CLR_FLAG(port, PE_FLAGS_ACCEPT);
- set_state_pe(port, PE_DRS_CHANGE);
- } else {
- /*
- * Message sent. Transition back to PE_SRC_Ready or
- * PE_SNK_Ready.
- */
- pe_set_ready_state(port);
- }
- }
-}
-
-/**
- * PE_DRS_Change
- */
-static void pe_drs_change_entry(int port)
-{
- print_current_state(port);
-
- /*
- * PE_DRS_UFP_DFP_Change_to_DFP and PE_DRS_DFP_UFP_Change_to_UFP
- * states embedded here.
- */
- /* Request DPM to change port data role */
- pd_request_data_swap(port);
-}
-
-static void pe_drs_change_run(int port)
-{
- /* Wait until the data role is changed */
- if (pe[port].data_role == pd_get_data_role(port))
- return;
-
- /* Update the data role */
- pe[port].data_role = pd_get_data_role(port);
-
- if (pe[port].data_role == PD_ROLE_DFP)
- PE_CLR_FLAG(port, PE_FLAGS_DR_SWAP_TO_DFP);
-
- /*
- * Port changed. Transition back to PE_SRC_Ready or
- * PE_SNK_Ready.
- */
- pe_set_ready_state(port);
-}
-
-/**
- * PE_DRS_Send_Swap
- */
-static void pe_drs_send_swap_entry(int port)
-{
- print_current_state(port);
-
- /*
- * PE_DRS_UFP_DFP_Send_Swap and PE_DRS_DFP_UFP_Send_Swap
- * states embedded here.
- */
- /* Request the Protocol Layer to send a DR_Swap Message */
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_DR_SWAP);
- pe_sender_response_msg_entry(port);
-}
-
-static void pe_drs_send_swap_run(int port)
-{
- int type;
- int cnt;
- int ext;
- enum pe_msg_check msg_check;
-
- /*
- * Check the state of the message sent
- */
- msg_check = pe_sender_response_msg_run(port);
-
- /*
- * Transition to PE_DRS_Change when:
- * 1) An Accept Message is received.
- *
- * Transition to PE_SRC_Ready or PE_SNK_Ready state when:
- * 1) A Reject Message is received.
- * 2) Or a Wait Message is received.
- */
- if ((msg_check & PE_MSG_SENT) &&
- PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
-
- type = PD_HEADER_TYPE(rx_emsg[port].header);
- cnt = PD_HEADER_CNT(rx_emsg[port].header);
- ext = PD_HEADER_EXT(rx_emsg[port].header);
-
- if ((ext == 0) && (cnt == 0)) {
- if (type == PD_CTRL_ACCEPT) {
- set_state_pe(port, PE_DRS_CHANGE);
- return;
- } else if ((type == PD_CTRL_REJECT) ||
- (type == PD_CTRL_WAIT) ||
- (type == PD_CTRL_NOT_SUPPORTED)) {
- pe_set_ready_state(port);
- return;
- }
- }
- }
-
- /*
- * Transition to PE_SRC_Ready or PE_SNK_Ready state when:
- * 1) the SenderResponseTimer times out.
- * 2) Message was discarded.
- */
- if ((msg_check & PE_MSG_DISCARDED) ||
- pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE))
- pe_set_ready_state(port);
-}
-
-static void pe_drs_send_swap_exit(int port)
-{
- pe_sender_response_msg_exit(port);
-}
-
-/**
- * PE_PRS_SRC_SNK_Evaluate_Swap
- */
-static void pe_prs_src_snk_evaluate_swap_entry(int port)
-{
- print_current_state(port);
-
- if (!pd_check_power_swap(port)) {
- /* PE_PRS_SRC_SNK_Reject_PR_Swap state embedded here */
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT);
- } else {
- tc_request_power_swap(port);
- /* PE_PRS_SRC_SNK_Accept_Swap state embedded here */
- PE_SET_FLAG(port, PE_FLAGS_ACCEPT);
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_ACCEPT);
- }
-}
-
-static void pe_prs_src_snk_evaluate_swap_run(int port)
-{
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
-
- if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) {
- PE_CLR_FLAG(port, PE_FLAGS_ACCEPT);
-
- /*
- * Clear any pending DPM power role swap request so we
- * don't trigger a power role swap request back to src
- * power role.
- */
- PE_CLR_DPM_REQUEST(port, DPM_REQUEST_PR_SWAP);
- /*
- * Power Role Swap OK, transition to
- * PE_PRS_SRC_SNK_Transition_to_off
- */
- set_state_pe(port, PE_PRS_SRC_SNK_TRANSITION_TO_OFF);
- } else {
- /* Message sent, return to PE_SRC_Ready */
- set_state_pe(port, PE_SRC_READY);
- }
- }
-}
-
-/**
- * PE_PRS_SRC_SNK_Transition_To_Off
- */
-static void pe_prs_src_snk_transition_to_off_entry(int port)
-{
- print_current_state(port);
-
- /* Contract is invalid */
- pe_invalidate_explicit_contract(port);
-
- /* Tell TypeC to power off the source */
- tc_src_power_off(port);
-
- pd_timer_enable(port, PE_TIMER_PS_SOURCE,
- PD_POWER_SUPPLY_TURN_OFF_DELAY);
-}
-
-static void pe_prs_src_snk_transition_to_off_run(int port)
-{
- /*
- * This is a non-interruptible AMS and power is transitioning - hard
- * reset on interruption.
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
-
- tc_pr_swap_complete(port, 0);
- set_state_pe(port, PE_SRC_HARD_RESET);
- }
-
- /* Give time for supply to power off */
- if (pd_timer_is_expired(port, PE_TIMER_PS_SOURCE) &&
- pd_check_vbus_level(port, VBUS_SAFE0V))
- set_state_pe(port, PE_PRS_SRC_SNK_ASSERT_RD);
-}
-
-static void pe_prs_src_snk_transition_to_off_exit(int port)
-{
- pd_timer_disable(port, PE_TIMER_PS_SOURCE);
-}
-
-/**
- * PE_PRS_SRC_SNK_Assert_Rd
- */
-static void pe_prs_src_snk_assert_rd_entry(int port)
-{
- print_current_state(port);
-
- /* Tell TypeC to swap from Attached.SRC to Attached.SNK */
- tc_prs_src_snk_assert_rd(port);
-}
-
-static void pe_prs_src_snk_assert_rd_run(int port)
-{
- /* Wait until Rd is asserted */
- if (tc_is_attached_snk(port))
- set_state_pe(port, PE_PRS_SRC_SNK_WAIT_SOURCE_ON);
-}
-
-/**
- * PE_PRS_SRC_SNK_Wait_Source_On
- */
-static void pe_prs_src_snk_wait_source_on_entry(int port)
-{
- print_current_state(port);
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PS_RDY);
-}
-
-static void pe_prs_src_snk_wait_source_on_run(int port)
-{
- if (pd_timer_is_disabled(port, PE_TIMER_PS_SOURCE) &&
- PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
-
- /* Update pe power role */
- pe[port].power_role = pd_get_power_role(port);
- pd_timer_enable(port, PE_TIMER_PS_SOURCE, PD_T_PS_SOURCE_ON);
- }
-
- /*
- * Transition to PE_SNK_Startup when:
- * 1) A PS_RDY Message is received.
- */
- if (!pd_timer_is_disabled(port, PE_TIMER_PS_SOURCE) &&
- PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- int type = PD_HEADER_TYPE(rx_emsg[port].header);
- int cnt = PD_HEADER_CNT(rx_emsg[port].header);
- int ext = PD_HEADER_EXT(rx_emsg[port].header);
-
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
-
- if ((ext == 0) && (cnt == 0) && (type == PD_CTRL_PS_RDY)) {
- PE_SET_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE);
- set_state_pe(port, PE_SNK_STARTUP);
- } else {
- int sop = PD_HEADER_GET_SOP(rx_emsg[port].header);
- /*
- * USB PD 3.0 6.8.1:
- * Receiving an unexpected message shall be responded
- * to with a soft reset message.
- */
- pe_send_soft_reset(port, sop);
- }
- return;
- }
-
- /*
- * Transition to ErrorRecovery state when:
- * 1) The PSSourceOnTimer times out.
- * 2) PS_RDY not sent after retries.
- */
- if (pd_timer_is_expired(port, PE_TIMER_PS_SOURCE) ||
- PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) {
- PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR);
-
- set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY);
- return;
- }
-}
-
-static void pe_prs_src_snk_wait_source_on_exit(int port)
-{
- pd_timer_disable(port, PE_TIMER_PS_SOURCE);
- tc_pr_swap_complete(port,
- PE_CHK_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE));
-}
-
-/**
- * PE_PRS_SRC_SNK_Send_Swap
- */
-static void pe_prs_src_snk_send_swap_entry(int port)
-{
- print_current_state(port);
-
- /* Making an attempt to PR_Swap, clear we were possibly waiting */
- pd_timer_disable(port, PE_TIMER_PR_SWAP_WAIT);
-
- /* Request the Protocol Layer to send a PR_Swap Message. */
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PR_SWAP);
- pe_sender_response_msg_entry(port);
-}
-
-static void pe_prs_src_snk_send_swap_run(int port)
-{
- int type;
- int cnt;
- int ext;
- enum pe_msg_check msg_check;
-
- /*
- * Check the state of the message sent
- */
- msg_check = pe_sender_response_msg_run(port);
-
- /*
- * Transition to PE_PRS_SRC_SNK_Transition_To_Off when:
- * 1) An Accept Message is received.
- *
- * Transition to PE_SRC_Ready state when:
- * 1) A Reject Message is received.
- * 2) Or a Wait Message is received.
- */
- if ((msg_check & PE_MSG_SENT) &&
- PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
-
- type = PD_HEADER_TYPE(rx_emsg[port].header);
- cnt = PD_HEADER_CNT(rx_emsg[port].header);
- ext = PD_HEADER_EXT(rx_emsg[port].header);
-
- if ((ext == 0) && (cnt == 0)) {
- if (type == PD_CTRL_ACCEPT) {
- pe[port].src_snk_pr_swap_counter = 0;
- tc_request_power_swap(port);
- set_state_pe(port,
- PE_PRS_SRC_SNK_TRANSITION_TO_OFF);
- } else if (type == PD_CTRL_REJECT) {
- pe[port].src_snk_pr_swap_counter = 0;
- set_state_pe(port, PE_SRC_READY);
- } else if (type == PD_CTRL_WAIT) {
- if (pe[port].src_snk_pr_swap_counter <
- N_SNK_SRC_PR_SWAP_COUNT) {
- PE_SET_FLAG(port,
- PE_FLAGS_WAITING_PR_SWAP);
- pd_timer_enable(port,
- PE_TIMER_PR_SWAP_WAIT,
- PD_T_PR_SWAP_WAIT);
- }
- pe[port].src_snk_pr_swap_counter++;
- set_state_pe(port, PE_SRC_READY);
- }
- return;
- }
- }
-
- /*
- * Transition to PE_SRC_Ready state when:
- * 1) Or the SenderResponseTimer times out.
- * 2) Message was discarded.
- */
- if ((msg_check & PE_MSG_DISCARDED) ||
- pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE))
- set_state_pe(port, PE_SRC_READY);
-}
-
-static void pe_prs_src_snk_send_swap_exit(int port)
-{
- pe_sender_response_msg_exit(port);
-}
-
-/**
- * PE_PRS_SNK_SRC_Evaluate_Swap
- */
-static void pe_prs_snk_src_evaluate_swap_entry(int port)
-{
- print_current_state(port);
-
- /*
- * Cancel any pending PR swap request due to a received Wait since the
- * partner just sent us a PR swap message.
- */
- PE_CLR_FLAG(port, PE_FLAGS_WAITING_PR_SWAP);
- pe[port].src_snk_pr_swap_counter = 0;
-
- if (!pd_check_power_swap(port)) {
- /* PE_PRS_SNK_SRC_Reject_Swap state embedded here */
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT);
- } else {
- tc_request_power_swap(port);
- /* PE_PRS_SNK_SRC_Accept_Swap state embedded here */
- PE_SET_FLAG(port, PE_FLAGS_ACCEPT);
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_ACCEPT);
- }
-}
-
-static void pe_prs_snk_src_evaluate_swap_run(int port)
-{
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
- if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) {
- PE_CLR_FLAG(port, PE_FLAGS_ACCEPT);
-
- /*
- * Clear any pending DPM power role swap request so we
- * don't trigger a power role swap request back to sink
- * power role.
- */
- PE_CLR_DPM_REQUEST(port, DPM_REQUEST_PR_SWAP);
- /*
- * Accept message sent, transition to
- * PE_PRS_SNK_SRC_Transition_to_off
- */
- set_state_pe(port, PE_PRS_SNK_SRC_TRANSITION_TO_OFF);
- } else {
- /* Message sent, return to PE_SNK_Ready */
- set_state_pe(port, PE_SNK_READY);
- }
- }
-
- if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) {
- PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR);
- /*
- * Protocol Error occurs while PR swap, this may
- * brown out if the port-parnter can't hold VBUS
- * for tSrcTransition. Notify TC that we end the PR
- * swap and start to watch VBUS.
- *
- * TODO(b:155181980): issue soft reset on protocol error.
- */
- tc_pr_swap_complete(port, 0);
- }
-}
-
-/**
- * PE_PRS_SNK_SRC_Transition_To_Off
- * PE_FRS_SNK_SRC_Transition_To_Off
- *
- * NOTE: Shared action code used for Power Role Swap and Fast Role Swap
- */
-static void pe_prs_snk_src_transition_to_off_entry(int port)
-{
- print_current_state(port);
-
- if (!IS_ENABLED(CONFIG_USB_PD_REV30) ||
- !pe_in_frs_mode(port))
- tc_snk_power_off(port);
-
- pd_timer_enable(port, PE_TIMER_PS_SOURCE, PD_T_PS_SOURCE_OFF);
-}
-
-static void pe_prs_snk_src_transition_to_off_run(int port)
-{
- int type;
- int cnt;
- int ext;
-
- /*
- * Transition to ErrorRecovery state when:
- * 1) The PSSourceOffTimer times out.
- */
- if (pd_timer_is_expired(port, PE_TIMER_PS_SOURCE))
- set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY);
-
- /*
- * Transition to PE_PRS_SNK_SRC_Assert_Rp when:
- * 1) An PS_RDY Message is received.
- */
- else if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
-
- type = PD_HEADER_TYPE(rx_emsg[port].header);
- cnt = PD_HEADER_CNT(rx_emsg[port].header);
- ext = PD_HEADER_EXT(rx_emsg[port].header);
-
- if ((ext == 0) && (cnt == 0) && (type == PD_CTRL_PS_RDY)) {
- /*
- * FRS: We are always ready to drive vSafe5v, so just
- * skip PE_FRS_SNK_SRC_Vbus_Applied and go direct to
- * PE_FRS_SNK_SRC_Assert_Rp
- */
- set_state_pe(port, PE_PRS_SNK_SRC_ASSERT_RP);
- }
- }
-}
-
-static void pe_prs_snk_src_transition_to_off_exit(int port)
-{
- pd_timer_disable(port, PE_TIMER_PS_SOURCE);
-}
-
-/**
- * PE_PRS_SNK_SRC_Assert_Rp
- * PE_FRS_SNK_SRC_Assert_Rp
- *
- * NOTE: Shared action code used for Power Role Swap and Fast Role Swap
- */
-static void pe_prs_snk_src_assert_rp_entry(int port)
-{
- print_current_state(port);
-
- /*
- * Tell TypeC to Power/Fast Role Swap (PRS/FRS) from
- * Attached.SNK to Attached.SRC
- */
- tc_prs_snk_src_assert_rp(port);
-}
-
-static void pe_prs_snk_src_assert_rp_run(int port)
-{
- /* Wait until TypeC is in the Attached.SRC state */
- if (tc_is_attached_src(port)) {
- if (!IS_ENABLED(CONFIG_USB_PD_REV30) ||
- !pe_in_frs_mode(port)) {
- /* Contract is invalid now */
- pe_invalidate_explicit_contract(port);
- }
- set_state_pe(port, PE_PRS_SNK_SRC_SOURCE_ON);
- }
-}
-
-/**
- * PE_PRS_SNK_SRC_Source_On
- * PE_FRS_SNK_SRC_Source_On
- *
- * NOTE: Shared action code used for Power Role Swap and Fast Role Swap
- */
-static void pe_prs_snk_src_source_on_entry(int port)
-{
- print_current_state(port);
-
- /*
- * VBUS was enabled when the TypeC state machine entered
- * Attached.SRC state
- */
- pd_timer_enable(port, PE_TIMER_PS_SOURCE,
- PD_POWER_SUPPLY_TURN_ON_DELAY);
-}
-
-static void pe_prs_snk_src_source_on_run(int port)
-{
- /* Wait until power supply turns on */
- if (!pd_timer_is_disabled(port, PE_TIMER_PS_SOURCE)) {
- if (!pd_timer_is_expired(port, PE_TIMER_PS_SOURCE))
- return;
-
- /* update pe power role */
- pe[port].power_role = pd_get_power_role(port);
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PS_RDY);
- /* reset timer so PD_CTRL_PS_RDY isn't sent again */
- pd_timer_disable(port, PE_TIMER_PS_SOURCE);
- }
-
- /*
- * Transition to ErrorRecovery state when:
- * 1) On protocol error
- */
- else if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) {
- PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR);
- set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY);
- }
-
- else if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
-
- /* Run swap source timer on entry to pe_src_startup */
- PE_SET_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE);
- set_state_pe(port, PE_SRC_STARTUP);
- }
-}
-
-static void pe_prs_snk_src_source_on_exit(int port)
-{
- pd_timer_disable(port, PE_TIMER_PS_SOURCE);
- tc_pr_swap_complete(port,
- PE_CHK_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE));
-}
-
-/**
- * PE_PRS_SNK_SRC_Send_Swap
- * PE_FRS_SNK_SRC_Send_Swap
- *
- * NOTE: Shared action code used for Power Role Swap and Fast Role Swap
- */
-static void pe_prs_snk_src_send_swap_entry(int port)
-{
- print_current_state(port);
-
- /*
- * PRS_SNK_SRC_SEND_SWAP
- * Request the Protocol Layer to send a PR_Swap Message.
- *
- * FRS_SNK_SRC_SEND_SWAP
- * Hardware should have turned off sink power and started
- * bringing Vbus to vSafe5.
- * Request the Protocol Layer to send a FR_Swap Message.
- */
- if (IS_ENABLED(CONFIG_USB_PD_REV30)) {
- send_ctrl_msg(port,
- TCPCI_MSG_SOP,
- pe_in_frs_mode(port)
- ? PD_CTRL_FR_SWAP
- : PD_CTRL_PR_SWAP);
- } else {
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PR_SWAP);
- }
- pe_sender_response_msg_entry(port);
-}
-
-static void pe_prs_snk_src_send_swap_run(int port)
-{
- int type;
- int cnt;
- int ext;
- enum pe_msg_check msg_check;
-
- /*
- * Check the state of the message sent
- */
- msg_check = pe_sender_response_msg_run(port);
-
- /*
- * Handle discarded message
- */
- if (msg_check & PE_MSG_DISCARDED) {
- if (pe_in_frs_mode(port))
- set_state_pe(port, PE_SNK_HARD_RESET);
- else
- set_state_pe(port, PE_SNK_READY);
- return;
- }
-
- /*
- * Transition to PE_PRS_SNK_SRC_Transition_to_off when:
- * 1) An Accept Message is received.
- *
- * PRS: Transition to PE_SNK_Ready state when:
- * FRS: Transition to ErrorRecovery state when:
- * 1) A Reject Message is received.
- * 2) Or a Wait Message is received.
- */
- if ((msg_check & PE_MSG_SENT) &&
- PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
-
- type = PD_HEADER_TYPE(rx_emsg[port].header);
- cnt = PD_HEADER_CNT(rx_emsg[port].header);
- ext = PD_HEADER_EXT(rx_emsg[port].header);
-
- if ((ext == 0) && (cnt == 0)) {
- if (type == PD_CTRL_ACCEPT) {
- tc_request_power_swap(port);
- set_state_pe(port,
- PE_PRS_SNK_SRC_TRANSITION_TO_OFF);
- } else if ((type == PD_CTRL_REJECT) ||
- (type == PD_CTRL_WAIT)) {
- if (IS_ENABLED(CONFIG_USB_PD_REV30))
- set_state_pe(port,
- pe_in_frs_mode(port)
- ? PE_WAIT_FOR_ERROR_RECOVERY
- : PE_SNK_READY);
- else
- set_state_pe(port, PE_SNK_READY);
- }
- return;
- }
- }
-
- /*
- * PRS: Transition to PE_SNK_Ready state when:
- * FRS: Transition to ErrorRecovery state when:
- * 1) The SenderResponseTimer times out.
- */
- if (pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) {
- if (IS_ENABLED(CONFIG_USB_PD_REV30))
- set_state_pe(port,
- pe_in_frs_mode(port)
- ? PE_WAIT_FOR_ERROR_RECOVERY
- : PE_SNK_READY);
- else
- set_state_pe(port, PE_SNK_READY);
- return;
- }
- /*
- * FRS Only: Transition to ErrorRecovery state when:
- * 2) The FR_Swap Message is not sent after retries (a GoodCRC Message
- * has not been received). A soft reset Shall Not be initiated in
- * this case.
- */
- if (IS_ENABLED(CONFIG_USB_PD_REV30) &&
- pe_in_frs_mode(port) &&
- PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) {
- PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR);
- set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY);
- }
-}
-
-static void pe_prs_snk_src_send_swap_exit(int port)
-{
- pe_sender_response_msg_exit(port);
-}
-
-/**
- * PE_FRS_SNK_SRC_Start_AMS
- */
-__maybe_unused static void pe_frs_snk_src_start_ams_entry(int port)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_REV30))
- assert(0);
-
- print_current_state(port);
-
- /* Contract is invalid now */
- pe_invalidate_explicit_contract(port);
-
- /* Inform Protocol Layer this is start of AMS */
- PE_SET_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS);
-
- /* Shared PRS/FRS code, indicate FRS path */
- PE_SET_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH);
- set_state_pe(port, PE_PRS_SNK_SRC_SEND_SWAP);
-}
-
-/**
- * PE_PRS_FRS_SHARED
- */
-__maybe_unused static void pe_prs_frs_shared_entry(int port)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_REV30))
- assert(0);
-
- /*
- * Shared PRS/FRS code, assume PRS path
- *
- * This is the super state entry. It will be called before
- * the first entry state to get into the PRS/FRS path.
- * For FRS, PE_FRS_SNK_SRC_START_AMS entry will be called
- * after this and that will set for the FRS path.
- */
- PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH);
-}
-
-__maybe_unused static void pe_prs_frs_shared_exit(int port)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_REV30))
- assert(0);
-
- /*
- * Shared PRS/FRS code, when not in shared path
- * indicate PRS path
- */
- PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH);
-}
-
-/**
- * PE_BIST_TX
- */
-static void pe_bist_tx_entry(int port)
-{
- uint32_t *payload = (uint32_t *)rx_emsg[port].buf;
- uint8_t mode = BIST_MODE(payload[0]);
- int vbus_mv;
- int ibus_ma;
-
- print_current_state(port);
-
- /* Get the current nominal VBUS value */
- if (pe[port].power_role == PD_ROLE_SOURCE) {
- const uint32_t *src_pdo;
- uint32_t unused;
-
- dpm_get_source_pdo(&src_pdo, port);
- pd_extract_pdo_power(src_pdo[pe[port].requested_idx - 1],
- &ibus_ma, &vbus_mv, &unused);
- } else {
- vbus_mv = pe[port].supply_voltage;
- }
-
- /* If VBUS is not at vSafe5V, then don't enter BIST test mode */
- if (vbus_mv != PD_V_SAFE5V_NOM) {
- pe_set_ready_state(port);
- return;
- }
-
- if (mode == BIST_CARRIER_MODE_2) {
- /*
- * PE_BIST_Carrier_Mode embedded here.
- * See PD 3.0 section 6.4.3.1 BIST Carrier Mode 2: With a BIST
- * Carrier Mode 2 BIST Data Object, the UUT Shall send out a
- * continuous string of BMC-encoded alternating "1"s and “0”s.
- * The UUT Shall exit the Continuous BIST Mode within
- * tBISTContMode of this Continuous BIST Mode being enabled.
- */
- send_ctrl_msg(port, TCPCI_MSG_TX_BIST_MODE_2, 0);
- pd_timer_enable(port, PE_TIMER_BIST_CONT_MODE,
- PD_T_BIST_CONT_MODE);
- } else if (mode == BIST_TEST_DATA) {
- /*
- * See PD 3.0 section 6.4.3.2 BIST Test Data:
- * With a BIST Test Data BIST Data Object, the UUT Shall return
- * a GoodCRC Message and Shall enter a test mode in which it
- * sends no further Messages except for GoodCRC Messages in
- * response to received Messages.... The test Shall be ended by
- * sending Hard Reset Signaling to reset the UUT.
- */
- if (tcpc_set_bist_test_mode(port, true) != EC_SUCCESS)
- CPRINTS("C%d: Failed to enter BIST Test Mode", port);
- } else {
- /* Ignore unsupported BIST messages. */
- pe_set_ready_state(port);
- return;
- }
-}
-
-static void pe_bist_tx_run(int port)
-{
- if (pd_timer_is_expired(port, PE_TIMER_BIST_CONT_MODE)) {
- /*
- * Entry point to disable BIST in TCPC if that's not already
- * handled automatically by the TCPC. Unless this method is
- * implemented in a TCPM driver, this function does nothing.
- */
- tcpm_reset_bist_type_2(port);
-
- if (pe[port].power_role == PD_ROLE_SOURCE)
- set_state_pe(port, PE_SRC_TRANSITION_TO_DEFAULT);
- else
- set_state_pe(port, PE_SNK_TRANSITION_TO_DEFAULT);
- } else {
- /*
- * We are in test data mode and no further Messages except for
- * GoodCRC Messages in response to received Messages will
- * be sent.
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED))
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
- }
-}
-
-static void pe_bist_tx_exit(int port)
-{
- pd_timer_disable(port, PE_TIMER_BIST_CONT_MODE);
-}
-
-/**
- * Give_Sink_Cap Message
- */
-static void pe_snk_give_sink_cap_entry(int port)
-{
- print_current_state(port);
-
- /* Send a Sink_Capabilities Message */
- tx_emsg[port].len = pd_snk_pdo_cnt * 4;
- memcpy(tx_emsg[port].buf, (uint8_t *)pd_snk_pdo, tx_emsg[port].len);
- send_data_msg(port, TCPCI_MSG_SOP, PD_DATA_SINK_CAP);
-}
-
-static void pe_snk_give_sink_cap_run(int port)
-{
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
- pe_set_ready_state(port);
- return;
- }
-
- if (pe_check_outgoing_discard(port))
- return;
-}
-
-/**
- * Wait For Error Recovery
- */
-static void pe_wait_for_error_recovery_entry(int port)
-{
- print_current_state(port);
- tc_start_error_recovery(port);
-}
-
-static void pe_wait_for_error_recovery_run(int port)
-{
- /* Stay here until error recovery is complete */
-}
-
-/**
- * PE_Handle_Custom_Vdm_Request
- */
-static void pe_handle_custom_vdm_request_entry(int port)
-{
- /* Get the message */
- uint32_t *payload = (uint32_t *)rx_emsg[port].buf;
- int cnt = PD_HEADER_CNT(rx_emsg[port].header);
- int sop = PD_HEADER_GET_SOP(rx_emsg[port].header);
- int rlen = 0;
- uint32_t *rdata;
-
- print_current_state(port);
-
- /* This is an Interruptible AMS */
- PE_SET_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS);
-
- rlen = pd_custom_vdm(port, cnt, payload, &rdata);
- if (rlen > 0) {
- tx_emsg[port].len = rlen * 4;
- memcpy(tx_emsg[port].buf, (uint8_t *)rdata, tx_emsg[port].len);
- send_data_msg(port, sop, PD_DATA_VENDOR_DEF);
- } else {
- if (prl_get_rev(port, TCPCI_MSG_SOP) > PD_REV20) {
- set_state_pe(port, PE_SEND_NOT_SUPPORTED);
- } else {
- PE_CLR_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS);
- pe_set_ready_state(port);
- }
- }
-}
-
-static void pe_handle_custom_vdm_request_run(int port)
-{
- /* Wait for ACCEPT, WAIT or Reject message to send. */
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
-
- /*
- * Message sent. Transition back to
- * PE_SRC_Ready or PE_SINK_Ready
- */
- pe_set_ready_state(port);
- }
-}
-
-static void pe_handle_custom_vdm_request_exit(int port)
-{
- PE_CLR_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS);
-}
-
-static enum vdm_response_result parse_vdm_response_common(int port)
-{
- /* Retrieve the message information */
- uint32_t *payload;
- int sop;
- uint8_t type;
- uint8_t cnt;
- uint8_t ext;
-
- if (!PE_CHK_REPLY(port))
- return VDM_RESULT_WAITING;
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
-
- payload = (uint32_t *)rx_emsg[port].buf;
- sop = PD_HEADER_GET_SOP(rx_emsg[port].header);
- type = PD_HEADER_TYPE(rx_emsg[port].header);
- cnt = PD_HEADER_CNT(rx_emsg[port].header);
- ext = PD_HEADER_EXT(rx_emsg[port].header);
-
- if (sop == pe[port].tx_type && type == PD_DATA_VENDOR_DEF && cnt >= 1
- && ext == 0) {
- if (PD_VDO_CMDT(payload[0]) == CMDT_RSP_ACK &&
- cnt >= pe[port].vdm_ack_min_data_objects) {
- /* Handle ACKs in state-specific code. */
- return VDM_RESULT_ACK;
- } else if (PD_VDO_CMDT(payload[0]) == CMDT_RSP_NAK) {
- /* Handle NAKs in state-specific code. */
- return VDM_RESULT_NAK;
- } else if (PD_VDO_CMDT(payload[0]) == CMDT_RSP_BUSY) {
- /*
- * Don't fill in the discovery field so we re-probe in
- * tVDMBusy
- */
- CPRINTS("C%d: Partner BUSY, request will be retried",
- port);
- pd_timer_enable(port, PE_TIMER_DISCOVER_IDENTITY,
- PD_T_VDM_BUSY);
-
- return VDM_RESULT_NO_ACTION;
- } else if (PD_VDO_CMDT(payload[0]) == CMDT_INIT) {
- /*
- * Unexpected VDM REQ received. Let Src.Ready or
- * Snk.Ready handle it.
- */
- PE_SET_FLAG(port, PE_FLAGS_MSG_RECEIVED);
- return VDM_RESULT_NO_ACTION;
- }
-
- /*
- * Partner gave us an incorrect size or command; mark discovery
- * as failed.
- */
- CPRINTS("C%d: Unexpected VDM response: 0x%04x 0x%04x",
- port, rx_emsg[port].header, payload[0]);
- return VDM_RESULT_NAK;
- } else if (sop == pe[port].tx_type && ext == 0 && cnt == 0 &&
- type == PD_CTRL_NOT_SUPPORTED) {
- /*
- * A NAK would be more expected here, but Not Supported is still
- * allowed with the same meaning.
- */
- return VDM_RESULT_NAK;
- }
-
- /* Unexpected Message Received. Src.Ready or Snk.Ready can handle it. */
- PE_SET_FLAG(port, PE_FLAGS_MSG_RECEIVED);
- return VDM_RESULT_NO_ACTION;
-}
-
-/**
- * PE_VDM_SEND_REQUEST
- * Shared parent to manage VDM timer and other shared parts of the VDM request
- * process
- */
-static void pe_vdm_send_request_entry(int port)
-{
- if (pe[port].tx_type == TCPCI_MSG_INVALID) {
- if (IS_ENABLED(USB_PD_DEBUG_LABELS))
- CPRINTS("C%d: %s: Tx type expected to be set, "
- "returning",
- port, pe_state_names[get_state_pe(port)]);
- set_state_pe(port, get_last_state_pe(port));
- return;
- }
-
- if ((pe[port].tx_type == TCPCI_MSG_SOP_PRIME ||
- pe[port].tx_type == TCPCI_MSG_SOP_PRIME_PRIME) &&
- !tc_is_vconn_src(port) && port_discovery_vconn_swap_policy(port,
- PE_FLAGS_VCONN_SWAP_TO_ON)) {
- if (port_try_vconn_swap(port))
- return;
- }
-
- /* All VDM sequences are Interruptible */
- PE_SET_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS |
- PE_FLAGS_INTERRUPTIBLE_AMS);
-}
-
-static void pe_vdm_send_request_run(int port)
-{
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE) &&
- pd_timer_is_disabled(port, PE_TIMER_VDM_RESPONSE)) {
- /* Message was sent */
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
-
- /* Start no response timer */
- /* TODO(b/155890173): Support DPM-supplied timeout */
- pd_timer_enable(port, PE_TIMER_VDM_RESPONSE,
- PD_T_VDM_SNDR_RSP);
- }
-
- if (PE_CHK_FLAG(port, PE_FLAGS_MSG_DISCARDED)) {
- /*
- * Go back to ready on first AMS message discard
- * (ready states will clear the discard flag)
- */
- pe_set_ready_state(port);
- return;
- }
-
- /*
- * Check the VDM timer, child will be responsible for processing
- * messages and reacting appropriately to unexpected messages.
- */
- if (pd_timer_is_expired(port, PE_TIMER_VDM_RESPONSE)) {
- CPRINTF("VDM %s Response Timeout\n",
- pe[port].tx_type == TCPCI_MSG_SOP ?
- "Port" : "Cable");
- /*
- * Flag timeout so child state can mark appropriate discovery
- * item as failed.
- */
- PE_SET_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT);
-
- set_state_pe(port, get_last_state_pe(port));
- }
-}
-
-static void pe_vdm_send_request_exit(int port)
-{
- /*
- * Clear TX complete in case child called set_state_pe() before parent
- * could process transmission
- */
- PE_CLR_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS);
-
- /* Invalidate TX type so it must be set before next call */
- pe[port].tx_type = TCPCI_MSG_INVALID;
-
- pd_timer_disable(port, PE_TIMER_VDM_RESPONSE);
-}
-
-/**
- * PE_VDM_IDENTITY_REQUEST_CBL
- * Combination of PE_INIT_PORT_VDM_Identity_Request State specific to the
- * cable and PE_SRC_VDM_Identity_Request State.
- * pe[port].tx_type must be set (to SOP') prior to entry.
- */
-static void pe_vdm_identity_request_cbl_entry(int port)
-{
- uint32_t *msg = (uint32_t *)tx_emsg[port].buf;
-
- print_current_state(port);
-
- if (!pe_can_send_sop_prime(port)) {
- /*
- * The parent state already tried to enable SOP' traffic. If it
- * is still disabled, there's nothing left to try.
- */
- pd_set_identity_discovery(port, pe[port].tx_type, PD_DISC_FAIL);
- set_state_pe(port, get_last_state_pe(port));
- return;
- }
-
- msg[0] = VDO(USB_SID_PD, 1,
- VDO_SVDM_VERS(pd_get_vdo_ver(port, pe[port].tx_type)) |
- CMD_DISCOVER_IDENT);
- tx_emsg[port].len = sizeof(uint32_t);
-
- send_data_msg(port, pe[port].tx_type, PD_DATA_VENDOR_DEF);
-
- pe[port].discover_identity_counter++;
-
- /*
- * Valid DiscoverIdentity responses should have at least 4 objects
- * (header, ID header, Cert Stat, Product VDO).
- */
- pe[port].vdm_ack_min_data_objects = 4;
-}
-
-static void pe_vdm_identity_request_cbl_run(int port)
-{
- /* Retrieve the message information */
- uint32_t *payload = (uint32_t *) rx_emsg[port].buf;
- int sop = PD_HEADER_GET_SOP(rx_emsg[port].header);
- uint8_t type = PD_HEADER_TYPE(rx_emsg[port].header);
- uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header);
- uint8_t ext = PD_HEADER_EXT(rx_emsg[port].header);
-
- switch (parse_vdm_response_common(port)) {
- case VDM_RESULT_WAITING:
- /*
- * The common code didn't parse a message. Handle protocol
- * errors; otherwise, continue waiting.
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) {
- /*
- * No Good CRC: See section 6.4.4.3.1 - Discover
- * Identity.
- *
- * Discover Identity Command request sent to SOP' Shall
- * Not cause a Soft Reset if a GoodCRC Message response
- * is not returned since this can indicate a non-PD
- * Capable cable.
- */
- PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR);
- set_state_pe(port, get_last_state_pe(port));
- }
- return;
- case VDM_RESULT_NO_ACTION:
- /*
- * If the received message doesn't change the discovery state,
- * there is nothing to do but return to the previous ready
- * state.
- */
- if (get_last_state_pe(port) == PE_SRC_DISCOVERY &&
- (sop != pe[port].tx_type ||
- type != PD_DATA_VENDOR_DEF ||
- cnt == 0 || ext != 0)) {
- /*
- * Unexpected non-VDM received: Before an explicit
- * contract, an unexpected message shall generate a soft
- * reset using the SOP* of the incoming message.
- */
- pe_send_soft_reset(port, sop);
- return;
- }
- break;
- case VDM_RESULT_ACK:
- /* PE_INIT_PORT_VDM_Identity_ACKed embedded here */
- dfp_consume_identity(port, sop, cnt, payload);
-
- /*
- * Note: If port partner runs PD 2.0, we must use PD 2.0 to
- * communicate with the cable plug when in an explicit contract.
- *
- * PD Spec Table 6-2: Revision Interoperability during an
- * Explicit Contract
- */
- if (prl_get_rev(port, TCPCI_MSG_SOP) != PD_REV20)
- prl_set_rev(port, sop,
- PD_HEADER_REV(rx_emsg[port].header));
- break;
- case VDM_RESULT_NAK:
- /* PE_INIT_PORT_VDM_IDENTITY_NAKed embedded here */
- pd_set_identity_discovery(port, pe[port].tx_type, PD_DISC_FAIL);
- break;
- }
-
- /* Return to calling state (PE_{SRC,SNK}_Ready or PE_SRC_Discovery) */
- set_state_pe(port, get_last_state_pe(port));
-}
-
-static void pe_vdm_identity_request_cbl_exit(int port)
-{
- /*
- * When cable GoodCRCs but does not reply, down-rev to PD 2.0 and try
- * again.
- *
- * PD 3.0 Rev 2.0 6.2.1.1.5 Specification Revision
- *
- * "When a Cable Plug does not respond to a Revision 3.0 Discover
- * Identity REQ with a Discover Identity ACK or BUSY the Vconn Source
- * May repeat steps 1-4 using a Revision 2.0 Discover Identity REQ in
- * step 1 before establishing that there is no Cable Plug to
- * communicate with"
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT)) {
- PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT);
- prl_set_rev(port, TCPCI_MSG_SOP_PRIME, PD_REV20);
- }
-
- /*
- * 6.6.15 DiscoverIdentityTimer
- *
- * No more than nDiscoverIdentityCount Discover Identity Messages
- * without a GoodCRC Message response Shall be sent. If no GoodCRC
- * Message response is received after nDiscoverIdentityCount Discover
- * Identity Command requests have been sent by a Port, the Port Shall
- * Not send any further SOP’/SOP’’ Messages.
- */
- if (pe[port].discover_identity_counter >= N_DISCOVER_IDENTITY_COUNT)
- pd_set_identity_discovery(port, pe[port].tx_type,
- PD_DISC_FAIL);
- else if (pe[port].discover_identity_counter ==
- N_DISCOVER_IDENTITY_PD3_0_LIMIT)
- /*
- * Downgrade to PD 2.0 if the partner hasn't replied before
- * all retries are exhausted in case the cable is
- * non-compliant about GoodCRC-ing higher revisions
- */
- prl_set_rev(port, TCPCI_MSG_SOP_PRIME, PD_REV20);
-
- /*
- * Set discover identity timer unless BUSY case already did so.
- */
- if (pd_get_identity_discovery(port, pe[port].tx_type) == PD_DISC_NEEDED
- && pd_timer_is_expired(port, PE_TIMER_DISCOVER_IDENTITY)) {
- /*
- * The tDiscoverIdentity timer is used during an explicit
- * contract when discovering whether a cable is PD capable.
- *
- * Pre-contract, slow the rate Discover Identity commands are
- * sent. This permits operation with captive cable devices that
- * power the SOP' responder from VBUS instead of VCONN.
- */
- pd_timer_enable(port, PE_TIMER_DISCOVER_IDENTITY,
- pe_is_explicit_contract(port)
- ? PD_T_DISCOVER_IDENTITY
- : PE_T_DISCOVER_IDENTITY_NO_CONTRACT);
- }
-
- /* Do not attempt further discovery if identity discovery failed. */
- if (pd_get_identity_discovery(port, pe[port].tx_type) == PD_DISC_FAIL) {
- pd_set_svids_discovery(port, pe[port].tx_type, PD_DISC_FAIL);
- pd_notify_event(port, pe[port].tx_type == TCPCI_MSG_SOP ?
- PD_STATUS_EVENT_SOP_DISC_DONE :
- PD_STATUS_EVENT_SOP_PRIME_DISC_DONE);
- }
-}
-
-/**
- * PE_INIT_PORT_VDM_Identity_Request
- *
- * Specific to SOP requests, as cables require additions for the discover
- * identity counter, must tolerate not receiving a GoodCRC, and need to set the
- * cable revision based on response.
- * pe[port].tx_type must be set (to SOP) prior to entry.
- */
-static void pe_init_port_vdm_identity_request_entry(int port)
-{
- uint32_t *msg = (uint32_t *)tx_emsg[port].buf;
-
- print_current_state(port);
-
- msg[0] = VDO(USB_SID_PD, 1,
- VDO_SVDM_VERS(pd_get_vdo_ver(port, pe[port].tx_type)) |
- CMD_DISCOVER_IDENT);
- tx_emsg[port].len = sizeof(uint32_t);
-
- send_data_msg(port, pe[port].tx_type, PD_DATA_VENDOR_DEF);
-
- /*
- * Valid DiscoverIdentity responses should have at least 4 objects
- * (header, ID header, Cert Stat, Product VDO).
- */
- pe[port].vdm_ack_min_data_objects = 4;
-}
-
-static void pe_init_port_vdm_identity_request_run(int port)
-{
- switch (parse_vdm_response_common(port)) {
- case VDM_RESULT_WAITING:
- /* If common code didn't parse a message, continue waiting. */
- return;
- case VDM_RESULT_NO_ACTION:
- /*
- * If the received message doesn't change the discovery state,
- * there is nothing to do but return to the previous ready
- * state.
- */
- break;
- case VDM_RESULT_ACK: {
- /* Retrieve the message information. */
- uint32_t *payload = (uint32_t *) rx_emsg[port].buf;
- int sop = PD_HEADER_GET_SOP(rx_emsg[port].header);
- uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header);
-
- /* PE_INIT_PORT_VDM_Identity_ACKed embedded here */
- dfp_consume_identity(port, sop, cnt, payload);
-
- break;
- }
- case VDM_RESULT_NAK:
- /* PE_INIT_PORT_VDM_IDENTITY_NAKed embedded here */
- pd_set_identity_discovery(port, pe[port].tx_type, PD_DISC_FAIL);
- break;
- }
-
- /* Return to calling state (PE_{SRC,SNK}_Ready) */
- set_state_pe(port, get_last_state_pe(port));
-}
-
-static void pe_init_port_vdm_identity_request_exit(int port)
-{
- if (PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT)) {
- PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT);
- /*
- * Mark failure to respond as discovery failure.
- *
- * For PD 2.0 partners (6.10.3 Applicability of Structured VDM
- * Commands Note 3):
- *
- * If Structured VDMs are not supported, a Structured VDM
- * Command received by a DFP or UFP Shall be Ignored.
- */
- pd_set_identity_discovery(port, pe[port].tx_type, PD_DISC_FAIL);
- }
-
- /* Do not attempt further discovery if identity discovery failed. */
- if (pd_get_identity_discovery(port, pe[port].tx_type) == PD_DISC_FAIL) {
- pd_set_svids_discovery(port, pe[port].tx_type, PD_DISC_FAIL);
- pd_notify_event(port, pe[port].tx_type == TCPCI_MSG_SOP ?
- PD_STATUS_EVENT_SOP_DISC_DONE :
- PD_STATUS_EVENT_SOP_PRIME_DISC_DONE);
- }
-}
-
-/**
- * PE_INIT_VDM_SVIDs_Request
- *
- * Used for SOP and SOP' requests, selected by pe[port].tx_type prior to entry.
- */
-static void pe_init_vdm_svids_request_entry(int port)
-{
- uint32_t *msg = (uint32_t *)tx_emsg[port].buf;
-
- print_current_state(port);
-
- if (pe[port].tx_type == TCPCI_MSG_SOP_PRIME &&
- !pe_can_send_sop_prime(port)) {
- /*
- * The parent state already tried to enable SOP' traffic. If it
- * is still disabled, there's nothing left to try.
- */
- pd_set_svids_discovery(port, pe[port].tx_type, PD_DISC_FAIL);
- set_state_pe(port, get_last_state_pe(port));
- return;
- }
-
- msg[0] = VDO(USB_SID_PD, 1,
- VDO_SVDM_VERS(pd_get_vdo_ver(port, pe[port].tx_type)) |
- CMD_DISCOVER_SVID);
- tx_emsg[port].len = sizeof(uint32_t);
-
- send_data_msg(port, pe[port].tx_type, PD_DATA_VENDOR_DEF);
-
- /*
- * Valid Discover SVIDs ACKs should have at least 2 objects (VDM header
- * and at least 1 SVID VDO).
- */
- pe[port].vdm_ack_min_data_objects = 2;
-}
-
-static void pe_init_vdm_svids_request_run(int port)
-{
- switch (parse_vdm_response_common(port)) {
- case VDM_RESULT_WAITING:
- /* If common code didn't parse a message, continue waiting. */
- return;
- case VDM_RESULT_NO_ACTION:
- /*
- * If the received message doesn't change the discovery state,
- * there is nothing to do but return to the previous ready
- * state.
- */
- break;
- case VDM_RESULT_ACK: {
- /* Retrieve the message information. */
- uint32_t *payload = (uint32_t *) rx_emsg[port].buf;
- int sop = PD_HEADER_GET_SOP(rx_emsg[port].header);
- uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header);
-
- /* PE_INIT_VDM_SVIDs_ACKed embedded here */
- dfp_consume_svids(port, sop, cnt, payload);
- break;
- }
- case VDM_RESULT_NAK:
- /* PE_INIT_VDM_SVIDs_NAKed embedded here */
- pd_set_svids_discovery(port, pe[port].tx_type, PD_DISC_FAIL);
- break;
- }
-
- /* Return to calling state (PE_{SRC,SNK}_Ready) */
- set_state_pe(port, get_last_state_pe(port));
-}
-
-static void pe_init_vdm_svids_request_exit(int port)
-{
- if (PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT)) {
- PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT);
- /*
- * Mark failure to respond as discovery failure.
- *
- * For PD 2.0 partners (6.10.3 Applicability of Structured VDM
- * Commands Note 3):
- *
- * If Structured VDMs are not supported, a Structured VDM
- * Command received by a DFP or UFP Shall be Ignored.
- */
- pd_set_svids_discovery(port, pe[port].tx_type, PD_DISC_FAIL);
- }
-
- /* If SVID discovery failed, discovery is done at this point */
- if (pd_get_svids_discovery(port, pe[port].tx_type) == PD_DISC_FAIL)
- pd_notify_event(port, pe[port].tx_type == TCPCI_MSG_SOP ?
- PD_STATUS_EVENT_SOP_DISC_DONE :
- PD_STATUS_EVENT_SOP_PRIME_DISC_DONE);
-}
-
-/**
- * PE_INIT_VDM_Modes_Request
- *
- * Used for SOP and SOP' requests, selected by pe[port].tx_type prior to entry.
- */
-static void pe_init_vdm_modes_request_entry(int port)
-{
- uint32_t *msg = (uint32_t *)tx_emsg[port].buf;
- const struct svid_mode_data *mode_data =
- pd_get_next_mode(port, pe[port].tx_type);
- uint16_t svid;
- /*
- * The caller should have checked that there was something to discover
- * before entering this state.
- */
- assert(mode_data);
- assert(mode_data->discovery == PD_DISC_NEEDED);
- svid = mode_data->svid;
-
- print_current_state(port);
-
- if (pe[port].tx_type == TCPCI_MSG_SOP_PRIME &&
- !pe_can_send_sop_prime(port)) {
- /*
- * The parent state already tried to enable SOP' traffic. If it
- * is still disabled, there's nothing left to try.
- */
- pd_set_modes_discovery(port, pe[port].tx_type, svid,
- PD_DISC_FAIL);
- set_state_pe(port, get_last_state_pe(port));
- return;
- }
-
- msg[0] = VDO((uint16_t) svid, 1,
- VDO_SVDM_VERS(pd_get_vdo_ver(port, pe[port].tx_type)) |
- CMD_DISCOVER_MODES);
- tx_emsg[port].len = sizeof(uint32_t);
-
- send_data_msg(port, pe[port].tx_type, PD_DATA_VENDOR_DEF);
-
- /*
- * Valid Discover Modes responses should have at least 2 objects (VDM
- * header and at least 1 mode VDO).
- */
- pe[port].vdm_ack_min_data_objects = 2;
-}
-
-static void pe_init_vdm_modes_request_run(int port)
-{
- const struct svid_mode_data *mode_data;
- uint16_t requested_svid;
-
- mode_data = pd_get_next_mode(port, pe[port].tx_type);
-
- assert(mode_data);
- assert(mode_data->discovery == PD_DISC_NEEDED);
- requested_svid = mode_data->svid;
-
- switch (parse_vdm_response_common(port)) {
- case VDM_RESULT_WAITING:
- /* If common code didn't parse a message, continue waiting. */
- return;
- case VDM_RESULT_NO_ACTION:
- /*
- * If the received message doesn't change the discovery state,
- * there is nothing to do but return to the previous ready
- * state.
- */
- break;
- case VDM_RESULT_ACK: {
- /* Retrieve the message information. */
- uint32_t *payload = (uint32_t *) rx_emsg[port].buf;
- int sop = PD_HEADER_GET_SOP(rx_emsg[port].header);
- uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header);
- uint16_t response_svid = (uint16_t) PD_VDO_VID(payload[0]);
-
- /*
- * Accept ACK if the request and response SVIDs are equal;
- * otherwise, treat this as a NAK of the request SVID.
- *
- * TODO(b:169242812): support valid mode checking in
- * dfp_consume_modes.
- */
- if (requested_svid == response_svid) {
- /* PE_INIT_VDM_Modes_ACKed embedded here */
- dfp_consume_modes(port, sop, cnt, payload);
- break;
- }
- }
- /* Fall Through */
- case VDM_RESULT_NAK:
- /* PE_INIT_VDM_Modes_NAKed embedded here */
- pd_set_modes_discovery(port, pe[port].tx_type, requested_svid,
- PD_DISC_FAIL);
- break;
- }
-
- /* Return to calling state (PE_{SRC,SNK}_Ready) */
- set_state_pe(port, get_last_state_pe(port));
-}
-
-static void pe_init_vdm_modes_request_exit(int port)
-{
- if (pd_get_modes_discovery(port, pe[port].tx_type) != PD_DISC_NEEDED)
- /* Mode discovery done, notify the AP */
- pd_notify_event(port, pe[port].tx_type == TCPCI_MSG_SOP ?
- PD_STATUS_EVENT_SOP_DISC_DONE :
- PD_STATUS_EVENT_SOP_PRIME_DISC_DONE);
-
-}
-
-/**
- * PE_VDM_REQUEST_DPM
- *
- * Makes a VDM request with contents and SOP* type previously set up by the DPM.
- */
-
-static void pe_vdm_request_dpm_entry(int port)
-{
- print_current_state(port);
-
- if ((pe[port].tx_type == TCPCI_MSG_SOP_PRIME ||
- pe[port].tx_type == TCPCI_MSG_SOP_PRIME_PRIME) &&
- !pe_can_send_sop_prime(port)) {
- /*
- * The parent state already tried to enable SOP' traffic. If it
- * is still disabled, there's nothing left to try.
- */
- dpm_vdm_naked(port, pe[port].tx_type,
- PD_VDO_VID(pe[port].vdm_data[0]),
- PD_VDO_CMD(pe[port].vdm_data[0]));
- set_state_pe(port, get_last_state_pe(port));
- return;
- }
-
- /* Copy Vendor Data Objects (VDOs) into message buffer */
- if (pe[port].vdm_cnt > 0) {
- /* Copy data after header */
- memcpy(&tx_emsg[port].buf,
- (uint8_t *)pe[port].vdm_data,
- pe[port].vdm_cnt * 4);
- /* Update len with the number of VDO bytes */
- tx_emsg[port].len = pe[port].vdm_cnt * 4;
- }
-
- /*
- * Clear the VDM nak'ed flag so that each request is
- * treated separately (NAKs are handled by the
- * DPM layer). Otherwise previous NAKs received will
- * cause the state to exit early.
- */
- PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_NAKED);
- send_data_msg(port, pe[port].tx_type, PD_DATA_VENDOR_DEF);
-
- /*
- * In general, valid VDM ACKs must have a VDM header. Other than that,
- * ACKs must be validated based on the command and SVID.
- */
- pe[port].vdm_ack_min_data_objects = 1;
-}
-
-static void pe_vdm_request_dpm_run(int port)
-{
- uint32_t vdm_hdr;
-
- switch (parse_vdm_response_common(port)) {
- case VDM_RESULT_WAITING:
- /*
- * USB-PD 3.0 Rev 1.1 - 6.4.4.2.5
- * Structured VDM command consists of a command request and a
- * command response (ACK, NAK, or BUSY). An exception is made
- * for the Attention command which shall have no response.
- *
- * Since Attention commands do not have an expected reply,
- * the SVDM command is complete once the Attention command
- * transmit is complete.
- */
- vdm_hdr = pe[port].vdm_data[0];
- if(PD_VDO_SVDM(vdm_hdr) &&
- (PD_VDO_CMD(vdm_hdr) == CMD_ATTENTION)) {
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
- break;
- }
- }
- /*
- * If common code didn't parse a message, and the VDM
- * just sent was not an Attention message, then continue
- * waiting.
- */
- return;
- case VDM_RESULT_NO_ACTION:
- /*
- * If the received message doesn't change the discovery state,
- * there is nothing to do but return to the previous ready
- * state. This includes Attention commands which have no
- * expected SVDM response.
- */
- break;
- case VDM_RESULT_ACK: {
- /* Retrieve the message information. */
- uint32_t *payload = (uint32_t *) rx_emsg[port].buf;
- int sop = PD_HEADER_GET_SOP(rx_emsg[port].header);
- uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header);
- uint16_t svid = PD_VDO_VID(payload[0]);
- uint8_t vdm_cmd = PD_VDO_CMD(payload[0]);
-
- /*
- * PE initiator VDM-ACKed state for requested VDM, like
- * PE_INIT_VDM_FOO_ACKed, embedded here.
- */
- dpm_vdm_acked(port, sop, cnt, payload);
-
- if (sop == TCPCI_MSG_SOP && svid == USB_SID_DISPLAYPORT &&
- vdm_cmd == CMD_DP_CONFIG) {
- PE_SET_FLAG(port, PE_FLAGS_VDM_SETUP_DONE);
- }
- break;
- }
- case VDM_RESULT_NAK:
- /*
- * PE initiator VDM-NAKed state for requested VDM, like
- * PE_INIT_VDM_FOO_NAKed, embedded here.
- */
- PE_SET_FLAG(port, PE_FLAGS_VDM_SETUP_DONE);
-
- /*
- * Because Not Supported messages or response timeouts are
- * treated as NAKs, there may not be a NAK message to parse.
- * Extract the needed information from the sent VDM.
- */
- dpm_vdm_naked(port, pe[port].tx_type,
- PD_VDO_VID(pe[port].vdm_data[0]),
- PD_VDO_CMD(pe[port].vdm_data[0]));
- break;
- }
-
- /* Return to calling state (PE_{SRC,SNK}_Ready) */
- set_state_pe(port, get_last_state_pe(port));
-}
-
-static void pe_vdm_request_dpm_exit(int port)
-{
- /*
- * Force Tx type to be reset before reentering a VDM state, unless the
- * current VDM request will be resumed.
- */
- if (!PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_CONTINUE))
- pe[port].tx_type = TCPCI_MSG_INVALID;
-}
-
-/**
- * PE_VDM_Response
- */
-static void pe_vdm_response_entry(int port)
-{
- int vdo_len = 0;
- uint32_t *rx_payload;
- uint32_t *tx_payload;
- uint8_t vdo_cmd;
- svdm_rsp_func func = NULL;
-
- print_current_state(port);
-
- /* This is an Interruptible AMS */
- PE_SET_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS);
-
- /* Get the message */
- rx_payload = (uint32_t *)rx_emsg[port].buf;
-
- /* Extract VDM command from the VDM header */
- vdo_cmd = PD_VDO_CMD(rx_payload[0]);
- /* This must be a command request to proceed further */
- if (PD_VDO_CMDT(rx_payload[0]) != CMDT_INIT) {
- CPRINTF("ERR:CMDT:%d:%d\n", PD_VDO_CMDT(rx_payload[0]),
- vdo_cmd);
-
- pe_set_ready_state(port);
- return;
- }
-
- tx_payload = (uint32_t *)tx_emsg[port].buf;
- /*
- * Designed in TCPMv1, svdm_response functions use same
- * buffer to take received data and overwrite with response
- * data. To work with this interface, here copy rx data to
- * tx buffer and pass tx_payload to func.
- * TODO(b/166455363): change the interface to pass both rx
- * and tx buffer.
- *
- * The SVDM header is dependent on both VDM command request being
- * replied to and the result of response function. The SVDM command
- * message is copied into tx_payload. tx_payload[0] is the VDM header
- * for the response message. The SVDM response function takes the role
- * of the DPM layer and will indicate the response type (ACK/NAK/BUSY)
- * by its return value (vdo_len)
- * vdo_len > 0 --> ACK
- * vdo_len == 0 --> NAK
- * vdo_len < 0 --> BUSY
- */
- memcpy(tx_payload, rx_payload, PD_HEADER_CNT(rx_emsg[port].header) * 4);
- /*
- * Clear fields in SVDM response message that will be set based on the
- * result of the svdm response function.
- */
- tx_payload[0] &= ~VDO_CMDT_MASK;
- tx_payload[0] &= ~VDO_SVDM_VERS(0x3);
-
- /* Add SVDM structured version being used */
- tx_payload[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP));
-
- /* Use VDM command to select the response handler function */
- switch (vdo_cmd) {
- case CMD_DISCOVER_IDENT:
- func = svdm_rsp.identity;
- break;
- case CMD_DISCOVER_SVID:
- func = svdm_rsp.svids;
- break;
- case CMD_DISCOVER_MODES:
- func = svdm_rsp.modes;
- break;
- case CMD_ENTER_MODE:
- func = svdm_rsp.enter_mode;
- break;
- case CMD_DP_STATUS:
- if (svdm_rsp.amode)
- func = svdm_rsp.amode->status;
- break;
- case CMD_DP_CONFIG:
- if (svdm_rsp.amode)
- func = svdm_rsp.amode->config;
- break;
- case CMD_EXIT_MODE:
- func = svdm_rsp.exit_mode;
- break;
-#ifdef CONFIG_USB_PD_ALT_MODE_DFP
- case CMD_ATTENTION:
- /*
- * attention is only SVDM with no response
- * (just goodCRC) return zero here.
- */
- dfp_consume_attention(port, rx_payload);
- pe_set_ready_state(port);
- return;
-#endif
- default:
- CPRINTF("VDO ERR:CMD:%d\n", vdo_cmd);
- }
-
- /*
- * If the port partner is PD_REV20 and our data role is DFP, we must
- * reply to any SVDM command with a NAK. If the SVDM was an Attention
- * command, it does not have a response, and exits the function above.
- */
- if (func && (prl_get_rev(port, TCPCI_MSG_SOP) != PD_REV20 ||
- pe[port].data_role == PD_ROLE_UFP)) {
- /*
- * Execute SVDM response function selected above and set the
- * correct response type in the VDM header.
- */
- vdo_len = func(port, tx_payload);
- if (vdo_len > 0) {
- tx_payload[0] |= VDO_CMDT(CMDT_RSP_ACK);
- /*
- * If command response is an ACK and if the command was
- * either enter/exit mode, then update the PE modal flag
- * accordingly.
- */
- if (vdo_cmd == CMD_ENTER_MODE)
- PE_SET_FLAG(port, PE_FLAGS_MODAL_OPERATION);
- if (vdo_cmd == CMD_EXIT_MODE)
- PE_CLR_FLAG(port, PE_FLAGS_MODAL_OPERATION);
- } else if (!vdo_len) {
- tx_payload[0] |= VDO_CMDT(CMDT_RSP_NAK);
- vdo_len = 1;
- } else {
- tx_payload[0] |= VDO_CMDT(CMDT_RSP_BUSY);
- vdo_len = 1;
- }
- } else {
- /*
- * Received at VDM command which is not supported. PD 2.0 may
- * NAK or ignore the message (see TD.PD.VNDI.E1. VDM Identity
- * steps), but PD 3.0 must send Not_Supported (PD 3.0 Ver 2.0 +
- * ECNs 2020-12-10 Table 6-64 Response to an incoming
- * VDM or TD.PD.VNDI3.E3 VDM Identity steps)
- */
- if (prl_get_rev(port, TCPCI_MSG_SOP) == PD_REV30) {
- set_state_pe(port, PE_SEND_NOT_SUPPORTED);
- return;
- }
- tx_payload[0] |= VDO_CMDT(CMDT_RSP_NAK);
- vdo_len = 1;
- }
-
- /* Send response message. Note len is in bytes, not VDO objects */
- tx_emsg[port].len = (vdo_len * sizeof(uint32_t));
- send_data_msg(port, TCPCI_MSG_SOP, PD_DATA_VENDOR_DEF);
-}
-
-static void pe_vdm_response_run(int port)
-{
- /*
- * This state waits for a VDM response message to be sent. Return to the
- * ready state once the message has been sent, a protocol error was
- * detected, or if the VDM response msg was discarded based on being
- * interrupted by another rx message. Since VDM sequences are AMS
- * interruptible, there is no need to soft reset regardless of exit
- * reason.
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE) ||
- PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR) ||
- PE_CHK_FLAG(port, PE_FLAGS_MSG_DISCARDED)) {
-
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE |
- PE_FLAGS_PROTOCOL_ERROR |
- PE_FLAGS_MSG_DISCARDED);
-
- pe_set_ready_state(port);
- }
-}
-
-static void pe_vdm_response_exit(int port)
-{
- PE_CLR_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS);
-}
-
-/**
- * PE_DEU_SEND_ENTER_USB
- */
-static void pe_enter_usb_entry(int port)
-{
- uint32_t usb4_payload;
-
- print_current_state(port);
-
- if (!IS_ENABLED(CONFIG_USB_PD_USB4)) {
- pe_set_ready_state(port);
- return;
- }
-
- /* Port is already in USB4 mode, do not send enter USB message again */
- if (enter_usb_entry_is_done(port)) {
- pe_set_ready_state(port);
- return;
- }
-
- if ((pe[port].tx_type == TCPCI_MSG_SOP_PRIME ||
- pe[port].tx_type == TCPCI_MSG_SOP_PRIME_PRIME) &&
- !tc_is_vconn_src(port)) {
- if (port_try_vconn_swap(port))
- return;
- }
-
- pe[port].tx_type = TCPCI_MSG_SOP;
- usb4_payload = enter_usb_setup_next_msg(port, &pe[port].tx_type);
-
- if (!usb4_payload) {
- enter_usb_failed(port);
- pe_set_ready_state(port);
- return;
- }
-
- tx_emsg[port].len = sizeof(usb4_payload);
-
- memcpy(tx_emsg[port].buf, &usb4_payload, tx_emsg[port].len);
- send_data_msg(port, pe[port].tx_type, PD_DATA_ENTER_USB);
- pe_sender_response_msg_entry(port);
-}
-
-static void pe_enter_usb_run(int port)
-{
- enum pe_msg_check msg_check;
-
- if (!IS_ENABLED(CONFIG_USB_PD_USB4)) {
- pe_set_ready_state(port);
- return;
- }
-
- /*
- * Check the state of the message sent
- */
- msg_check = pe_sender_response_msg_run(port);
-
- /*
- * Handle Discarded message, return to PE_SNK/SRC_READY
- */
- if (msg_check & PE_MSG_DISCARDED) {
- pe_set_ready_state(port);
- return;
- } else if (msg_check == PE_MSG_SEND_PENDING) {
- /* Wait until message is sent */
- return;
- }
-
- if (pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) {
- pe_set_ready_state(port);
- enter_usb_failed(port);
- return;
- }
-
- if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- int cnt = PD_HEADER_CNT(rx_emsg[port].header);
- int type = PD_HEADER_TYPE(rx_emsg[port].header);
- int sop = PD_HEADER_GET_SOP(rx_emsg[port].header);
-
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
-
- /* Only look at control messages */
- if (cnt == 0) {
- /* Accept message received */
- if (type == PD_CTRL_ACCEPT) {
- enter_usb_accepted(port, sop);
- } else if (type == PD_CTRL_REJECT) {
- enter_usb_rejected(port, sop);
- } else {
- /*
- * Unexpected control message received.
- * Send Soft Reset.
- */
- pe_send_soft_reset(port, sop);
- return;
- }
- } else {
- /* Unexpected data message received. Send Soft reset */
- pe_send_soft_reset(port, sop);
- return;
- }
- pe_set_ready_state(port);
- }
-}
-
-static void pe_enter_usb_exit(int port)
-{
- pe_sender_response_msg_exit(port);
-}
-
-#ifdef CONFIG_USBC_VCONN
-/*
- * PE_VCS_Evaluate_Swap
- */
-static void pe_vcs_evaluate_swap_entry(int port)
-{
- print_current_state(port);
-
- /*
- * Request the DPM for an evaluation of the VCONN Swap request.
- * Note: Ports that are presently the VCONN Source must always
- * accept a VCONN
- */
-
- /*
- * Transition to the PE_VCS_Accept_Swap state when:
- * 1) The Device Policy Manager indicates that a VCONN Swap is ok.
- *
- * Transition to the PE_VCS_Reject_Swap state when:
- * 1) Port is not presently the VCONN Source and
- * 2) The DPM indicates that a VCONN Swap is not ok or
- * 3) The DPM indicates that a VCONN Swap cannot be done at this time.
- */
-
- /* DPM rejects a VCONN Swap and port is not a VCONN source*/
- if (!tc_check_vconn_swap(port) && tc_is_vconn_src(port) < 1) {
- /* NOTE: PE_VCS_Reject_Swap State embedded here */
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT);
- }
- /* Port is not ready to perform a VCONN swap */
- else if (tc_is_vconn_src(port) < 0) {
- /* NOTE: PE_VCS_Reject_Swap State embedded here */
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_WAIT);
- }
- /* Port is ready to perform a VCONN swap */
- else {
- /* NOTE: PE_VCS_Accept_Swap State embedded here */
- PE_SET_FLAG(port, PE_FLAGS_ACCEPT);
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_ACCEPT);
- }
-}
-
-static void pe_vcs_evaluate_swap_run(int port)
-{
- /* Wait for ACCEPT, WAIT or Reject message to send. */
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
-
- if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) {
- PE_CLR_FLAG(port, PE_FLAGS_ACCEPT);
- /* Accept Message sent and Presently VCONN Source */
- if (tc_is_vconn_src(port))
- set_state_pe(port, PE_VCS_WAIT_FOR_VCONN_SWAP);
- /* Accept Message sent and Not presently VCONN Source */
- else
- set_state_pe(port, PE_VCS_TURN_ON_VCONN_SWAP);
- } else {
- /*
- * Message sent. Transition back to PE_SRC_Ready or
- * PE_SINK_Ready
- */
- pe_set_ready_state(port);
- }
- return;
- }
-
- if (pe_check_outgoing_discard(port))
- return;
-}
-
-/*
- * PE_VCS_Send_Swap
- */
-static void pe_vcs_send_swap_entry(int port)
-{
- print_current_state(port);
-
- /* Send a VCONN_Swap Message */
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_VCONN_SWAP);
- pe_sender_response_msg_entry(port);
-}
-
-static void pe_vcs_send_swap_run(int port)
-{
- uint8_t type;
- uint8_t cnt;
- enum tcpci_msg_type sop;
- enum pe_msg_check msg_check;
-
- /*
- * Check the state of the message sent
- */
- msg_check = pe_sender_response_msg_run(port);
-
- if ((msg_check & PE_MSG_SENT) &&
- PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
-
- /* Increment once message has successfully sent */
- pe[port].vconn_swap_counter++;
-
- type = PD_HEADER_TYPE(rx_emsg[port].header);
- cnt = PD_HEADER_CNT(rx_emsg[port].header);
- sop = PD_HEADER_GET_SOP(rx_emsg[port].header);
-
- /* Only look at control messages */
- if (cnt == 0) {
- /*
- * Transition to the PE_VCS_Wait_For_VCONN state when:
- * 1) Accept Message Received and
- * 2) The Port is presently the VCONN Source.
- *
- * Transition to the PE_VCS_Turn_On_VCONN state when:
- * 1) Accept Message Received and
- * 2) The Port is not presently the VCONN Source.
- */
- if (type == PD_CTRL_ACCEPT) {
- if (tc_is_vconn_src(port)) {
- set_state_pe(port,
- PE_VCS_WAIT_FOR_VCONN_SWAP);
- } else {
- set_state_pe(port,
- PE_VCS_TURN_ON_VCONN_SWAP);
- }
- return;
- }
- /*
- * Transition back to either the PE_SRC_Ready or
- * PE_SNK_Ready state when:
- * 2) Reject message is received or
- * 3) Wait message Received.
- */
- if (type == PD_CTRL_REJECT || type == PD_CTRL_WAIT) {
- pe_set_ready_state(port);
- return;
- }
-
- /*
- * The Policy Engine May transition to the
- * PE_VCS_Force_Vconn state when:
- * - A Not_Supported Message is received and
- * - The Port is not presently the VCONN Source
- */
- if (type == PD_CTRL_NOT_SUPPORTED) {
- if (IS_ENABLED(CONFIG_USB_PD_REV30) &&
- !tc_is_vconn_src(port))
- set_state_pe(port, PE_VCS_FORCE_VCONN);
- else
- pe_set_ready_state(port);
- return;
- }
- }
- /*
- * Unexpected Message Received, send soft reset with SOP* of
- * incoming message.
- */
- pe_send_soft_reset(port, sop);
- return;
- }
-
- /*
- * Transition back to either the PE_SRC_Ready or
- * PE_SNK_Ready state when:
- * 1) SenderResponseTimer Timeout
- * 2) Message was discarded.
- */
- if ((msg_check & PE_MSG_DISCARDED) ||
- pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE))
- pe_set_ready_state(port);
-}
-
-static void pe_vcs_send_swap_exit(int port)
-{
- pe_sender_response_msg_exit(port);
-}
-
-/*
- * PE_VCS_Wait_for_VCONN_Swap
- */
-static void pe_vcs_wait_for_vconn_swap_entry(int port)
-{
- print_current_state(port);
-
- /* Start the VCONNOnTimer */
- pd_timer_enable(port, PE_TIMER_VCONN_ON, PD_T_VCONN_SOURCE_ON);
-
- /*
- * The USB PD 3.0 spec indicates that the initial VCONN source
- * shall cease sourcing VCONN within tVCONNSourceOff (25ms)
- * after receiving the PS_RDY message. However, some partners
- * begin sending SOP' messages only 1 ms after sending PS_RDY
- * during VCONN swap.
- *
- * Preemptively disable receipt of SOP' and SOP'' messages while
- * we wait for PS_RDY so we don't attempt to process messages
- * directed at the cable.
- *
- * We continue to source VCONN while we wait as required by the
- * spec.
- */
- tcpm_sop_prime_enable(port, false);
-}
-
-static void pe_vcs_wait_for_vconn_swap_run(int port)
-{
- /*
- * Transition to the PE_VCS_Turn_Off_VCONN state when:
- * 1) A PS_RDY Message is received.
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- /*
- * PS_RDY message received
- *
- * Note: intentionally leave the receive flag set to indicate
- * our route on exit when PS_RDY is received.
- */
- if ((PD_HEADER_CNT(rx_emsg[port].header) == 0) &&
- (PD_HEADER_EXT(rx_emsg[port].header) == 0) &&
- (PD_HEADER_TYPE(rx_emsg[port].header) == PD_CTRL_PS_RDY)) {
- set_state_pe(port, PE_VCS_TURN_OFF_VCONN_SWAP);
- return;
- } else {
- /*
- * Unexpected message received - reset with the SOP* of
- * the incoming message.
- */
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
- pe_send_soft_reset(port,
- PD_HEADER_GET_SOP(rx_emsg[port].header));
- return;
- }
- }
-
- /*
- * Transition to either the PE_SRC_Hard_Reset or
- * PE_SNK_Hard_Reset state when:
- * 1) The VCONNOnTimer times out.
- */
- if (pd_timer_is_expired(port, PE_TIMER_VCONN_ON)) {
- if (pe[port].power_role == PD_ROLE_SOURCE)
- set_state_pe(port, PE_SRC_HARD_RESET);
- else
- set_state_pe(port, PE_SNK_HARD_RESET);
- }
-}
-
-static void pe_vcs_wait_for_vconn_swap_exit(int port)
-{
- /*
- * If we exited without getting PS_RDY, re-enable SOP' messaging since
- * we are still the Vconn source.
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED))
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
- else
- tcpm_sop_prime_enable(port, true);
-
- pd_timer_disable(port, PE_TIMER_VCONN_ON);
-}
-
-/*
- * PE_VCS_Turn_On_VCONN_Swap
- */
-static void pe_vcs_turn_on_vconn_swap_entry(int port)
-{
- print_current_state(port);
-
- /* Request DPM to turn on VCONN */
- pd_request_vconn_swap_on(port);
-}
-
-static void pe_vcs_turn_on_vconn_swap_run(int port)
-{
-
- /*
- * Transition to the PE_VCS_Send_Ps_Rdy state when:
- * 1) The Port’s VCONN is on.
- */
- if (pd_timer_is_disabled(port, PE_TIMER_TIMEOUT) &&
- PE_CHK_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE);
- pd_timer_enable(port, PE_TIMER_TIMEOUT,
- CONFIG_USBC_VCONN_SWAP_DELAY_US);
- }
-
- if (pd_timer_is_expired(port, PE_TIMER_TIMEOUT))
- set_state_pe(port, PE_VCS_SEND_PS_RDY_SWAP);
-}
-
-static void pe_vcs_turn_on_vconn_swap_exit(int port)
-{
- pd_timer_disable(port, PE_TIMER_TIMEOUT);
-}
-
-/*
- * PE_VCS_Turn_Off_VCONN_Swap
- */
-static void pe_vcs_turn_off_vconn_swap_entry(int port)
-{
- print_current_state(port);
-
- /* Request DPM to turn off VCONN */
- pd_request_vconn_swap_off(port);
-}
-
-static void pe_vcs_turn_off_vconn_swap_run(int port)
-{
- /* Wait for VCONN to turn off */
- if (PE_CHK_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE);
-
- /*
- * A VCONN Swap Shall reset the DiscoverIdentityCounter
- * to zero
- */
- pe[port].discover_identity_counter = 0;
- pe[port].dr_swap_attempt_counter = 0;
-
- pe_set_ready_state(port);
- return;
- }
-}
-
-/*
- * PE_VCS_Send_PS_Rdy_Swap
- */
-static void pe_vcs_send_ps_rdy_swap_entry(int port)
-{
- print_current_state(port);
-
- /* Check for any interruptions to this non-interruptible AMS */
- if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- enum tcpci_msg_type sop =
- PD_HEADER_GET_SOP(rx_emsg[port].header);
-
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
-
- /* Soft reset with the SOP* of the incoming message */
- pe_send_soft_reset(port, sop);
- return;
- }
-
- /* Send a PS_RDY Message */
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PS_RDY);
-}
-
-static void pe_vcs_send_ps_rdy_swap_run(int port)
-{
- /*
- * After a VCONN Swap the VCONN Source needs to reset
- * the Cable Plug’s Protocol Layer in order to ensure
- * MessageID synchronization.
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
- /*
- * A VCONN Swap Shall reset the
- * DiscoverIdentityCounter to zero
- */
- pe[port].discover_identity_counter = 0;
- pe[port].dr_swap_attempt_counter = 0;
-
- /* A SOP' soft reset is required after VCONN swap */
- pd_dpm_request(port, DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND);
- pe_set_ready_state(port);
- }
-
- if (pe_check_outgoing_discard(port))
- return;
-
- if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) {
- PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR);
- /* PS_RDY didn't send, soft reset */
- pe_send_soft_reset(port, TCPCI_MSG_SOP);
- }
-}
-
-/*
- * PE_VCS_Force_Vconn
- */
-__maybe_unused static void pe_vcs_force_vconn_entry(int port)
-{
- print_current_state(port);
-
- /* Request DPM to turn on VCONN */
- pd_request_vconn_swap_on(port);
-}
-
-__maybe_unused static void pe_vcs_force_vconn_run(int port)
-{
- /*
- * The Policy Engine Shall transition back to either the PE_SRC_Ready
- * or PE_SNK_Ready state when:
- * 1) The Port’s VCONN is on.
- *
- * Note we'll wait CONFIG_USBC_VCONN_SWAP_DELAY_US, as defined by the
- * board, to ensure Vconn is on.
- */
- if (pd_timer_is_disabled(port, PE_TIMER_TIMEOUT) &&
- PE_CHK_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE);
- pd_timer_enable(port, PE_TIMER_TIMEOUT,
- CONFIG_USBC_VCONN_SWAP_DELAY_US);
- }
-
- if (pd_timer_is_expired(port, PE_TIMER_TIMEOUT)) {
- /*
- * Note: A cable soft reset shouldn't be necessary as a
- * Not_Supported reply means the partner doesn't support
- * sourcing Vconn and did not communicate with the cable.
- */
- pe_set_ready_state(port);
- return;
- }
-}
-
-__maybe_unused static void pe_vcs_force_vconn_exit(int port)
-{
- pd_timer_disable(port, PE_TIMER_TIMEOUT);
-}
-
-/*
- * PE_VCS_CBL_SEND_SOFT_RESET
- * Note - Entry is only when directed by the DPM. Protocol errors are handled
- * by the PE_SEND_SOFT_RESET state.
- */
-static void pe_vcs_cbl_send_soft_reset_entry(int port)
-{
- print_current_state(port);
-
- if (!pe_can_send_sop_prime(port)) {
- /*
- * If we're not VCONN source, return the appropriate state.
- * A VCONN swap re-triggers sending SOP' soft reset
- */
- if (pe_is_explicit_contract(port)) {
- /* Return to PE_{SRC,SNK}_Ready state */
- pe_set_ready_state(port);
- } else {
- /*
- * Not in Explicit Contract, so we must be a SRC,
- * return to PE_Src_Send_Capabilities.
- */
- set_state_pe(port, PE_SRC_SEND_CAPABILITIES);
- }
- return;
- }
-
- send_ctrl_msg(port, TCPCI_MSG_SOP_PRIME, PD_CTRL_SOFT_RESET);
- pe_sender_response_msg_entry(port);
-}
-
-static void pe_vcs_cbl_send_soft_reset_run(int port)
-{
- bool cable_soft_reset_complete = false;
- enum pe_msg_check msg_check;
-
- msg_check = pe_sender_response_msg_run(port);
-
- /* Got ACCEPT or REJECT from Cable Plug */
- if ((msg_check & PE_MSG_SENT) &&
- PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
- cable_soft_reset_complete = true;
-
- /*
- * Note: If port partner runs PD 2.0, we must use PD 2.0 to
- * communicate with the cable plug when in an explicit contract.
- *
- * PD Spec Table 6-2: Revision Interoperability during an
- * Explicit Contract
- */
- if (prl_get_rev(port, TCPCI_MSG_SOP) != PD_REV20)
- prl_set_rev(port, TCPCI_MSG_SOP_PRIME,
- PD_HEADER_REV(rx_emsg[port].header));
- }
-
- /* No GoodCRC received, cable is not present */
- if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) {
- PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR);
- /*
- * TODO(b/171823328): TCPMv2: Implement cable reset
- * Cable reset will only be done here if we know for certain
- * a cable is present (we've received the SOP' DiscId response).
- */
- cable_soft_reset_complete = true;
- }
-
- if (cable_soft_reset_complete ||
- pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE) ||
- (msg_check & PE_MSG_DISCARDED)) {
- if (pe_is_explicit_contract(port)) {
- /* Return to PE_{SRC,SNK}_Ready state */
- pe_set_ready_state(port);
- } else {
- /*
- * Not in Explicit Contract, so we must be a SRC,
- * return to PE_Src_Send_Capabilities.
- */
- set_state_pe(port, PE_SRC_SEND_CAPABILITIES);
- }
- }
-}
-
-static void pe_vcs_cbl_send_soft_reset_exit(int port)
-{
- pe_sender_response_msg_exit(port);
-}
-
-#endif /* CONFIG_USBC_VCONN */
-
-/*
- * PE_DR_SNK_Get_Sink_Cap and PE_SRC_Get_Sink_Cap State (shared)
- */
-static void pe_dr_get_sink_cap_entry(int port)
-{
- print_current_state(port);
-
- /* Send a Get Sink Cap Message */
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_GET_SINK_CAP);
- pe_sender_response_msg_entry(port);
-}
-
-static void pe_dr_get_sink_cap_run(int port)
-{
- int type;
- int cnt;
- int ext;
- enum pe_msg_check msg_check;
- enum tcpci_msg_type sop;
-
- /*
- * Check the state of the message sent
- */
- msg_check = pe_sender_response_msg_run(port);
-
- /*
- * Transition to PE_[SRC,SNK]_Ready when:
- * 1) A Sink_Capabilities Message is received
- * 2) Or SenderResponseTimer times out
- * 3) Or a Reject Message is received.
- *
- * Transition to PE_SEND_SOFT_RESET state when:
- * 1) An unexpected message is received
- */
- if ((msg_check & PE_MSG_SENT) &&
- PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
-
- type = PD_HEADER_TYPE(rx_emsg[port].header);
- cnt = PD_HEADER_CNT(rx_emsg[port].header);
- ext = PD_HEADER_EXT(rx_emsg[port].header);
- sop = PD_HEADER_GET_SOP(rx_emsg[port].header);
-
- if (ext == 0 && sop == TCPCI_MSG_SOP) {
- if ((cnt > 0) && (type == PD_DATA_SINK_CAP)) {
- uint32_t *payload =
- (uint32_t *)rx_emsg[port].buf;
- uint8_t cap_cnt = rx_emsg[port].len /
- sizeof(uint32_t);
-
- pe_set_snk_caps(port, cap_cnt, payload);
-
- dpm_evaluate_sink_fixed_pdo(port, payload[0]);
- pe_set_ready_state(port);
- return;
- } else if (cnt == 0 && (type == PD_CTRL_REJECT ||
- type == PD_CTRL_NOT_SUPPORTED)) {
- pe_set_ready_state(port);
- return;
- }
- /* Unexpected messages fall through to soft reset */
- }
-
- pe_send_soft_reset(port, sop);
- return;
- }
-
- /*
- * Transition to PE_[SRC,SNK]_Ready state when:
- * 1) SenderResponseTimer times out.
- * 2) Message was discarded.
- */
- if ((msg_check & PE_MSG_DISCARDED) ||
- pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE))
- pe_set_ready_state(port);
-}
-
-static void pe_dr_get_sink_cap_exit(int port)
-{
- pe_sender_response_msg_exit(port);
-}
-
-/*
- * PE_DR_SNK_Give_Source_Cap
- */
-static void pe_dr_snk_give_source_cap_entry(int port)
-{
- print_current_state(port);
-
- /* Send source capabilities. */
- send_source_cap(port);
-}
-
-static void pe_dr_snk_give_source_cap_run(int port)
-{
- /*
- * Transition back to PE_SNK_Ready when the Source_Capabilities message
- * has been successfully sent.
- *
- * Get Source Capabilities AMS is uninterruptible, but in case the
- * partner violates the spec then send a soft reset rather than get
- * stuck here.
- */
- if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) {
- PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE);
- set_state_pe(port, PE_SNK_READY);
- } else if (PE_CHK_FLAG(port, PE_FLAGS_MSG_DISCARDED)) {
- pe_send_soft_reset(port, TCPCI_MSG_SOP);
- }
-}
-
-/*
- * PE_DR_SRC_Get_Source_Cap
- */
-static void pe_dr_src_get_source_cap_entry(int port)
-{
- print_current_state(port);
-
- /* Send a Get_Source_Cap Message */
- tx_emsg[port].len = 0;
- send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_GET_SOURCE_CAP);
- pe_sender_response_msg_entry(port);
-}
-
-static void pe_dr_src_get_source_cap_run(int port)
-{
- int type;
- int cnt;
- int ext;
- enum pe_msg_check msg_check;
-
- /*
- * Check the state of the message sent
- */
- msg_check = pe_sender_response_msg_run(port);
-
- /*
- * Transition to PE_SRC_Ready when:
- * 1) A Source Capabilities Message is received.
- * 2) A Reject Message is received.
- */
- if ((msg_check & PE_MSG_SENT) &&
- PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) {
- PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED);
-
- type = PD_HEADER_TYPE(rx_emsg[port].header);
- cnt = PD_HEADER_CNT(rx_emsg[port].header);
- ext = PD_HEADER_EXT(rx_emsg[port].header);
-
- if (ext == 0) {
- if ((cnt > 0) && (type == PD_DATA_SOURCE_CAP)) {
- uint32_t *payload =
- (uint32_t *)rx_emsg[port].buf;
-
- pd_set_src_caps(port, cnt, payload);
-
- /*
- * If we'd prefer to charge from this partner,
- * then propose a PR swap.
- */
- if (pd_can_charge_from_device(port, cnt,
- payload))
- pd_request_power_swap(port);
-
- /*
- * Report dual role power capability to the
- * charge manager if present
- */
- if (IS_ENABLED(CONFIG_CHARGE_MANAGER) &&
- pd_get_partner_dual_role_power(port))
- charge_manager_update_dualrole(port,
- CAP_DUALROLE);
-
- set_state_pe(port, PE_SRC_READY);
- } else if ((cnt == 0) && (type == PD_CTRL_REJECT ||
- type == PD_CTRL_NOT_SUPPORTED)) {
- pd_set_src_caps(port, -1, NULL);
- set_state_pe(port, PE_SRC_READY);
- } else {
- /*
- * On protocol error, consider source cap
- * retrieval a failure
- */
- pd_set_src_caps(port, -1, NULL);
- set_state_pe(port, PE_SEND_SOFT_RESET);
- }
- return;
- } else {
- pd_set_src_caps(port, -1, NULL);
- set_state_pe(port, PE_SEND_SOFT_RESET);
- return;
- }
- }
-
- /*
- * Transition to PE_SRC_Ready state when:
- * 1) the SenderResponseTimer times out.
- * 2) Message was discarded.
- */
- if ((msg_check & PE_MSG_DISCARDED) ||
- pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE))
- set_state_pe(port, PE_SRC_READY);
-}
-
-static void pe_dr_src_get_source_cap_exit(int port)
-{
- pe_sender_response_msg_exit(port);
-}
-
-const uint32_t * const pd_get_src_caps(int port)
-{
- return pe[port].src_caps;
-}
-
-void pd_set_src_caps(int port, int cnt, uint32_t *src_caps)
-{
- int i;
-
- pe[port].src_cap_cnt = cnt;
-
- for (i = 0; i < cnt; i++)
- pe[port].src_caps[i] = *src_caps++;
-}
-
-uint8_t pd_get_src_cap_cnt(int port)
-{
- if (pe[port].src_cap_cnt > 0)
- return pe[port].src_cap_cnt;
-
- return 0;
-}
-
-/* Track access to the PD discovery structures during HC execution */
-uint32_t task_access[CONFIG_USB_PD_PORT_MAX_COUNT][DISCOVERY_TYPE_COUNT];
-
-void pd_dfp_discovery_init(int port)
-{
- atomic_or(&task_access[port][TCPCI_MSG_SOP], BIT(task_get_current()));
- atomic_or(&task_access[port][TCPCI_MSG_SOP_PRIME],
- BIT(task_get_current()));
-
- memset(pe[port].discovery, 0, sizeof(pe[port].discovery));
-
-}
-
-void pd_dfp_mode_init(int port)
-{
- /*
- * Clear the VDM Setup Done and Modal Operation flags so we will
- * have a fresh discovery
- */
- PE_CLR_FLAG(port, PE_FLAGS_VDM_SETUP_DONE |
- PE_FLAGS_MODAL_OPERATION);
-
- memset(pe[port].partner_amodes, 0, sizeof(pe[port].partner_amodes));
-
- /* Reset the DPM and DP modules to enable alternate mode entry. */
- dpm_init(port);
- dp_init(port);
-
- if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE))
- tbt_init(port);
-
- if (IS_ENABLED(CONFIG_USB_PD_USB4))
- enter_usb_init(port);
-
- if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_UFP_DP))
- pd_ufp_set_dp_opos(port, 0);
-}
-
-__maybe_unused void pd_discovery_access_clear(int port,
- enum tcpci_msg_type type)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP))
- assert(0);
-
- atomic_clear_bits(&task_access[port][type], 0xFFFFFFFF);
-}
-
-__maybe_unused bool pd_discovery_access_validate(int port,
- enum tcpci_msg_type type)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP))
- assert(0);
-
- return !(task_access[port][type] & ~BIT(task_get_current()));
-}
-
-__maybe_unused struct pd_discovery *pd_get_am_discovery_and_notify_access(
- int port, enum tcpci_msg_type type)
-{
- atomic_or(&task_access[port][type], BIT(task_get_current()));
- return (struct pd_discovery *)pd_get_am_discovery(port, type);
-}
-
-__maybe_unused const struct pd_discovery *pd_get_am_discovery(int port,
- enum tcpci_msg_type type)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP))
- assert(0);
- ASSERT(type < DISCOVERY_TYPE_COUNT);
-
- return &pe[port].discovery[type];
-}
-
-__maybe_unused struct partner_active_modes *pd_get_partner_active_modes(
- int port, enum tcpci_msg_type type)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP))
- assert(0);
- ASSERT(type < AMODE_TYPE_COUNT);
- return &pe[port].partner_amodes[type];
-}
-
-__maybe_unused void pd_set_dfp_enter_mode_flag(int port, bool set)
-{
- if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP))
- assert(0);
-
- if (set)
- PE_SET_FLAG(port, PE_FLAGS_MODAL_OPERATION);
- else
- PE_CLR_FLAG(port, PE_FLAGS_MODAL_OPERATION);
-}
-
-const char *pe_get_current_state(int port)
-{
- if (pe_is_running(port) && IS_ENABLED(USB_PD_DEBUG_LABELS))
- return pe_state_names[get_state_pe(port)];
- else
- return "";
-}
-
-uint32_t pe_get_flags(int port)
-{
- return pe[port].flags;
-}
-
-static __const_data const struct usb_state pe_states[] = {
- /* Super States */
-#ifdef CONFIG_USB_PD_REV30
- [PE_PRS_FRS_SHARED] = {
- .entry = pe_prs_frs_shared_entry,
- .exit = pe_prs_frs_shared_exit,
- },
-#endif
- [PE_VDM_SEND_REQUEST] = {
- .entry = pe_vdm_send_request_entry,
- .run = pe_vdm_send_request_run,
- .exit = pe_vdm_send_request_exit,
- },
-
- /* Normal States */
- [PE_SRC_STARTUP] = {
- .entry = pe_src_startup_entry,
- .run = pe_src_startup_run,
- .exit = pe_src_startup_exit,
- },
- [PE_SRC_DISCOVERY] = {
- .entry = pe_src_discovery_entry,
- .run = pe_src_discovery_run,
- },
- [PE_SRC_SEND_CAPABILITIES] = {
- .entry = pe_src_send_capabilities_entry,
- .run = pe_src_send_capabilities_run,
- .exit = pe_src_send_capabilities_exit,
- },
- [PE_SRC_NEGOTIATE_CAPABILITY] = {
- .entry = pe_src_negotiate_capability_entry,
- },
- [PE_SRC_TRANSITION_SUPPLY] = {
- .entry = pe_src_transition_supply_entry,
- .run = pe_src_transition_supply_run,
- .exit = pe_src_transition_supply_exit,
- },
- [PE_SRC_READY] = {
- .entry = pe_src_ready_entry,
- .run = pe_src_ready_run,
- },
- [PE_SRC_DISABLED] = {
- .entry = pe_src_disabled_entry,
- },
- [PE_SRC_CAPABILITY_RESPONSE] = {
- .entry = pe_src_capability_response_entry,
- .run = pe_src_capability_response_run,
- },
- [PE_SRC_HARD_RESET] = {
- .entry = pe_src_hard_reset_entry,
- .run = pe_src_hard_reset_run,
- .exit = pe_src_hard_reset_exit,
- },
- [PE_SRC_HARD_RESET_RECEIVED] = {
- .entry = pe_src_hard_reset_received_entry,
- .run = pe_src_hard_reset_received_run,
- .exit = pe_src_hard_reset_received_exit,
- },
- [PE_SRC_TRANSITION_TO_DEFAULT] = {
- .entry = pe_src_transition_to_default_entry,
- .run = pe_src_transition_to_default_run,
- },
- [PE_SNK_STARTUP] = {
- .entry = pe_snk_startup_entry,
- .run = pe_snk_startup_run,
- },
- [PE_SNK_DISCOVERY] = {
- .entry = pe_snk_discovery_entry,
- .run = pe_snk_discovery_run,
- },
- [PE_SNK_WAIT_FOR_CAPABILITIES] = {
- .entry = pe_snk_wait_for_capabilities_entry,
- .run = pe_snk_wait_for_capabilities_run,
- .exit = pe_snk_wait_for_capabilities_exit,
- },
- [PE_SNK_EVALUATE_CAPABILITY] = {
- .entry = pe_snk_evaluate_capability_entry,
- },
- [PE_SNK_SELECT_CAPABILITY] = {
- .entry = pe_snk_select_capability_entry,
- .run = pe_snk_select_capability_run,
- .exit = pe_snk_select_capability_exit,
- },
- [PE_SNK_READY] = {
- .entry = pe_snk_ready_entry,
- .run = pe_snk_ready_run,
- },
- [PE_SNK_HARD_RESET] = {
- .entry = pe_snk_hard_reset_entry,
- .run = pe_snk_hard_reset_run,
- },
- [PE_SNK_TRANSITION_TO_DEFAULT] = {
- .entry = pe_snk_transition_to_default_entry,
- .run = pe_snk_transition_to_default_run,
- },
- [PE_SNK_GIVE_SINK_CAP] = {
- .entry = pe_snk_give_sink_cap_entry,
- .run = pe_snk_give_sink_cap_run,
- },
- [PE_SNK_GET_SOURCE_CAP] = {
- .entry = pe_snk_get_source_cap_entry,
- .run = pe_snk_get_source_cap_run,
- },
- [PE_SNK_TRANSITION_SINK] = {
- .entry = pe_snk_transition_sink_entry,
- .run = pe_snk_transition_sink_run,
- .exit = pe_snk_transition_sink_exit,
- },
- [PE_SEND_SOFT_RESET] = {
- .entry = pe_send_soft_reset_entry,
- .run = pe_send_soft_reset_run,
- .exit = pe_send_soft_reset_exit,
- },
- [PE_SOFT_RESET] = {
- .entry = pe_soft_reset_entry,
- .run = pe_soft_reset_run,
- },
- [PE_SEND_NOT_SUPPORTED] = {
- .entry = pe_send_not_supported_entry,
- .run = pe_send_not_supported_run,
- },
- [PE_SRC_PING] = {
- .entry = pe_src_ping_entry,
- .run = pe_src_ping_run,
- },
- [PE_DRS_EVALUATE_SWAP] = {
- .entry = pe_drs_evaluate_swap_entry,
- .run = pe_drs_evaluate_swap_run,
- },
- [PE_DRS_CHANGE] = {
- .entry = pe_drs_change_entry,
- .run = pe_drs_change_run,
- },
- [PE_DRS_SEND_SWAP] = {
- .entry = pe_drs_send_swap_entry,
- .run = pe_drs_send_swap_run,
- .exit = pe_drs_send_swap_exit,
- },
- [PE_PRS_SRC_SNK_EVALUATE_SWAP] = {
- .entry = pe_prs_src_snk_evaluate_swap_entry,
- .run = pe_prs_src_snk_evaluate_swap_run,
- },
- [PE_PRS_SRC_SNK_TRANSITION_TO_OFF] = {
- .entry = pe_prs_src_snk_transition_to_off_entry,
- .run = pe_prs_src_snk_transition_to_off_run,
- .exit = pe_prs_src_snk_transition_to_off_exit,
- },
- [PE_PRS_SRC_SNK_ASSERT_RD] = {
- .entry = pe_prs_src_snk_assert_rd_entry,
- .run = pe_prs_src_snk_assert_rd_run,
- },
- [PE_PRS_SRC_SNK_WAIT_SOURCE_ON] = {
- .entry = pe_prs_src_snk_wait_source_on_entry,
- .run = pe_prs_src_snk_wait_source_on_run,
- .exit = pe_prs_src_snk_wait_source_on_exit,
- },
- [PE_PRS_SRC_SNK_SEND_SWAP] = {
- .entry = pe_prs_src_snk_send_swap_entry,
- .run = pe_prs_src_snk_send_swap_run,
- .exit = pe_prs_src_snk_send_swap_exit,
- },
- [PE_PRS_SNK_SRC_EVALUATE_SWAP] = {
- .entry = pe_prs_snk_src_evaluate_swap_entry,
- .run = pe_prs_snk_src_evaluate_swap_run,
- },
- /*
- * Some of the Power Role Swap actions are shared with the very
- * similar actions of Fast Role Swap.
- */
- /* State actions are shared with PE_FRS_SNK_SRC_TRANSITION_TO_OFF */
- [PE_PRS_SNK_SRC_TRANSITION_TO_OFF] = {
- .entry = pe_prs_snk_src_transition_to_off_entry,
- .run = pe_prs_snk_src_transition_to_off_run,
- .exit = pe_prs_snk_src_transition_to_off_exit,
-#ifdef CONFIG_USB_PD_REV30
- .parent = &pe_states[PE_PRS_FRS_SHARED],
-#endif /* CONFIG_USB_PD_REV30 */
- },
- /* State actions are shared with PE_FRS_SNK_SRC_ASSERT_RP */
- [PE_PRS_SNK_SRC_ASSERT_RP] = {
- .entry = pe_prs_snk_src_assert_rp_entry,
- .run = pe_prs_snk_src_assert_rp_run,
-#ifdef CONFIG_USB_PD_REV30
- .parent = &pe_states[PE_PRS_FRS_SHARED],
-#endif /* CONFIG_USB_PD_REV30 */
- },
- /* State actions are shared with PE_FRS_SNK_SRC_SOURCE_ON */
- [PE_PRS_SNK_SRC_SOURCE_ON] = {
- .entry = pe_prs_snk_src_source_on_entry,
- .run = pe_prs_snk_src_source_on_run,
- .exit = pe_prs_snk_src_source_on_exit,
-#ifdef CONFIG_USB_PD_REV30
- .parent = &pe_states[PE_PRS_FRS_SHARED],
-#endif /* CONFIG_USB_PD_REV30 */
- },
- /* State actions are shared with PE_FRS_SNK_SRC_SEND_SWAP */
- [PE_PRS_SNK_SRC_SEND_SWAP] = {
- .entry = pe_prs_snk_src_send_swap_entry,
- .run = pe_prs_snk_src_send_swap_run,
- .exit = pe_prs_snk_src_send_swap_exit,
-#ifdef CONFIG_USB_PD_REV30
- .parent = &pe_states[PE_PRS_FRS_SHARED],
-#endif /* CONFIG_USB_PD_REV30 */
- },
-#ifdef CONFIG_USBC_VCONN
- [PE_VCS_EVALUATE_SWAP] = {
- .entry = pe_vcs_evaluate_swap_entry,
- .run = pe_vcs_evaluate_swap_run,
- },
- [PE_VCS_SEND_SWAP] = {
- .entry = pe_vcs_send_swap_entry,
- .run = pe_vcs_send_swap_run,
- .exit = pe_vcs_send_swap_exit,
- },
- [PE_VCS_WAIT_FOR_VCONN_SWAP] = {
- .entry = pe_vcs_wait_for_vconn_swap_entry,
- .run = pe_vcs_wait_for_vconn_swap_run,
- .exit = pe_vcs_wait_for_vconn_swap_exit,
- },
- [PE_VCS_TURN_ON_VCONN_SWAP] = {
- .entry = pe_vcs_turn_on_vconn_swap_entry,
- .run = pe_vcs_turn_on_vconn_swap_run,
- .exit = pe_vcs_turn_on_vconn_swap_exit,
- },
- [PE_VCS_TURN_OFF_VCONN_SWAP] = {
- .entry = pe_vcs_turn_off_vconn_swap_entry,
- .run = pe_vcs_turn_off_vconn_swap_run,
- },
- [PE_VCS_SEND_PS_RDY_SWAP] = {
- .entry = pe_vcs_send_ps_rdy_swap_entry,
- .run = pe_vcs_send_ps_rdy_swap_run,
- },
- [PE_VCS_CBL_SEND_SOFT_RESET] = {
- .entry = pe_vcs_cbl_send_soft_reset_entry,
- .run = pe_vcs_cbl_send_soft_reset_run,
- .exit = pe_vcs_cbl_send_soft_reset_exit,
- },
-#endif /* CONFIG_USBC_VCONN */
- [PE_VDM_IDENTITY_REQUEST_CBL] = {
- .entry = pe_vdm_identity_request_cbl_entry,
- .run = pe_vdm_identity_request_cbl_run,
- .exit = pe_vdm_identity_request_cbl_exit,
- .parent = &pe_states[PE_VDM_SEND_REQUEST],
- },
- [PE_INIT_PORT_VDM_IDENTITY_REQUEST] = {
- .entry = pe_init_port_vdm_identity_request_entry,
- .run = pe_init_port_vdm_identity_request_run,
- .exit = pe_init_port_vdm_identity_request_exit,
- .parent = &pe_states[PE_VDM_SEND_REQUEST],
- },
- [PE_INIT_VDM_SVIDS_REQUEST] = {
- .entry = pe_init_vdm_svids_request_entry,
- .run = pe_init_vdm_svids_request_run,
- .exit = pe_init_vdm_svids_request_exit,
- .parent = &pe_states[PE_VDM_SEND_REQUEST],
- },
- [PE_INIT_VDM_MODES_REQUEST] = {
- .entry = pe_init_vdm_modes_request_entry,
- .run = pe_init_vdm_modes_request_run,
- .exit = pe_init_vdm_modes_request_exit,
- .parent = &pe_states[PE_VDM_SEND_REQUEST],
- },
- [PE_VDM_REQUEST_DPM] = {
- .entry = pe_vdm_request_dpm_entry,
- .run = pe_vdm_request_dpm_run,
- .exit = pe_vdm_request_dpm_exit,
- .parent = &pe_states[PE_VDM_SEND_REQUEST],
- },
- [PE_VDM_RESPONSE] = {
- .entry = pe_vdm_response_entry,
- .run = pe_vdm_response_run,
- .exit = pe_vdm_response_exit,
- },
- [PE_HANDLE_CUSTOM_VDM_REQUEST] = {
- .entry = pe_handle_custom_vdm_request_entry,
- .run = pe_handle_custom_vdm_request_run,
- .exit = pe_handle_custom_vdm_request_exit,
- },
- [PE_DEU_SEND_ENTER_USB] = {
- .entry = pe_enter_usb_entry,
- .run = pe_enter_usb_run,
- .exit = pe_enter_usb_exit,
- },
- [PE_WAIT_FOR_ERROR_RECOVERY] = {
- .entry = pe_wait_for_error_recovery_entry,
- .run = pe_wait_for_error_recovery_run,
- },
- [PE_BIST_TX] = {
- .entry = pe_bist_tx_entry,
- .run = pe_bist_tx_run,
- .exit = pe_bist_tx_exit,
- },
- [PE_DR_GET_SINK_CAP] = {
- .entry = pe_dr_get_sink_cap_entry,
- .run = pe_dr_get_sink_cap_run,
- .exit = pe_dr_get_sink_cap_exit,
- },
- [PE_DR_SNK_GIVE_SOURCE_CAP] = {
- .entry = pe_dr_snk_give_source_cap_entry,
- .run = pe_dr_snk_give_source_cap_run,
- },
- [PE_DR_SRC_GET_SOURCE_CAP] = {
- .entry = pe_dr_src_get_source_cap_entry,
- .run = pe_dr_src_get_source_cap_run,
- .exit = pe_dr_src_get_source_cap_exit,
- },
-#ifdef CONFIG_USB_PD_REV30
- [PE_FRS_SNK_SRC_START_AMS] = {
- .entry = pe_frs_snk_src_start_ams_entry,
- .parent = &pe_states[PE_PRS_FRS_SHARED],
- },
-#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES
- [PE_GIVE_BATTERY_CAP] = {
- .entry = pe_give_battery_cap_entry,
- .run = pe_give_battery_cap_run,
- },
- [PE_GIVE_BATTERY_STATUS] = {
- .entry = pe_give_battery_status_entry,
- .run = pe_give_battery_status_run,
- },
- [PE_SEND_ALERT] = {
- .entry = pe_send_alert_entry,
- .run = pe_send_alert_run,
- },
-#else
- [PE_SRC_CHUNK_RECEIVED] = {
- .entry = pe_chunk_received_entry,
- .run = pe_chunk_received_run,
- .exit = pe_chunk_received_exit,
- },
- [PE_SNK_CHUNK_RECEIVED] = {
- .entry = pe_chunk_received_entry,
- .run = pe_chunk_received_run,
- .exit = pe_chunk_received_exit,
- },
-#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */
-#ifdef CONFIG_USBC_VCONN
- [PE_VCS_FORCE_VCONN] = {
- .entry = pe_vcs_force_vconn_entry,
- .run = pe_vcs_force_vconn_run,
- .exit = pe_vcs_force_vconn_exit,
- },
-#endif /* CONFIG_USBC_VCONN */
-#endif /* CONFIG_USB_PD_REV30 */
-};
-
-#ifdef TEST_BUILD
-/* TODO(b/173791979): Unit tests shouldn't need to access internal states */
-const struct test_sm_data test_pe_sm_data[] = {
- {
- .base = pe_states,
- .size = ARRAY_SIZE(pe_states),
- .names = pe_state_names,
- .names_size = ARRAY_SIZE(pe_state_names),
- },
-};
-BUILD_ASSERT(ARRAY_SIZE(pe_states) == ARRAY_SIZE(pe_state_names));
-const int test_pe_sm_data_size = ARRAY_SIZE(test_pe_sm_data);
-
-void pe_set_flag(int port, int flag)
-{
- PE_SET_FLAG(port, flag);
-}
-void pe_clr_flag(int port, int flag)
-{
- PE_CLR_FLAG(port, flag);
-}
-int pe_chk_flag(int port, int flag)
-{
- return PE_CHK_FLAG(port, flag);
-}
-int pe_get_all_flags(int port)
-{
- return pe[port].flags;
-}
-void pe_set_all_flags(int port, int flags)
-{
- pe[port].flags = flags;
-}
-void pe_clr_dpm_requests(int port)
-{
- pe[port].dpm_request = 0;
-}
-#endif