/* 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 "battery.h" #include "battery_smart.h" #include "board.h" #include "charge_manager.h" #include "charge_state.h" #include "chipset.h" #include "common.h" #include "console.h" #include "cros_version.h" #include "ec_commands.h" #include "gpio.h" #include "hooks.h" #include "host_command.h" #include "registers.h" #include "system.h" #include "task.h" #include "tcpm/tcpm.h" #include "util.h" #include "usb_charge.h" #include "usb_mux.h" #include "usb_pd.h" #include "usb_pd_timer.h" #include "usb_pe_sm.h" #include "usb_prl_sm.h" #include "usb_tc_sm.h" #include "usb_emsg.h" #include "usb_sm.h" #include "vpd_api.h" #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 DEBUG_PRINT_FLAG_NAMES to print flag names when set and cleared. */ #undef DEBUG_PRINT_FLAG_NAMES #ifdef DEBUG_PRINT_FLAG_NAMES __maybe_unused static void print_flag(const char *group, int set_or_clear, int flag); #define SET_FLAG(group, flags, flag) \ do { \ print_flag(group, 1, flag); \ atomic_or(flags, (flag)); \ } while (0) #define CLR_FLAG(group, flags, flag) \ do { \ int before = *flags; \ atomic_clear_bits(flags, (flag)); \ if (*flags != before) \ print_flag(group, 0, flag); \ } while (0) #else #define SET_FLAG(group, flags, flag) atomic_or(flags, (flag)) #define CLR_FLAG(group, flags, flag) atomic_clear_bits(flags, (flag)) #endif #define RCH_SET_FLAG(port, flag) SET_FLAG("RCH", &rch[port].flags, (flag)) #define RCH_CLR_FLAG(port, flag) CLR_FLAG("RCH", &rch[port].flags, (flag)) #define RCH_CHK_FLAG(port, flag) (rch[port].flags & (flag)) #define TCH_SET_FLAG(port, flag) SET_FLAG("TCH", &tch[port].flags, (flag)) #define TCH_CLR_FLAG(port, flag) CLR_FLAG("TCH", &tch[port].flags, (flag)) #define TCH_CHK_FLAG(port, flag) (tch[port].flags & (flag)) #define PRL_TX_SET_FLAG(port, flag) \ SET_FLAG("PRL_TX", &prl_tx[port].flags, (flag)) #define PRL_TX_CLR_FLAG(port, flag) \ CLR_FLAG("PRL_TX", &prl_tx[port].flags, (flag)) #define PRL_TX_CHK_FLAG(port, flag) (prl_tx[port].flags & (flag)) #define PRL_HR_SET_FLAG(port, flag) \ SET_FLAG("PRL_HR", &prl_hr[port].flags, (flag)) #define PRL_HR_CLR_FLAG(port, flag) \ CLR_FLAG("PRL_HR", &prl_hr[port].flags, (flag)) #define PRL_HR_CHK_FLAG(port, flag) (prl_hr[port].flags & (flag)) #define PDMSG_SET_FLAG(port, flag) SET_FLAG("PDMSG", &pdmsg[port].flags, (flag)) #define PDMSG_CLR_FLAG(port, flag) CLR_FLAG("PDMSG", &pdmsg[port].flags, (flag)) #define PDMSG_CHK_FLAG(port, flag) (pdmsg[port].flags & (flag)) /* Protocol Layer Flags */ /* * NOTE: * These flags are used in multiple state machines and could have * different meanings in each state machine. */ /* Flag to note message transmission completed */ #define PRL_FLAGS_TX_COMPLETE BIT(0) /* Flag to note that PRL requested to set SINK_NG CC state */ #define PRL_FLAGS_SINK_NG BIT(1) /* Flag to note PRL waited for SINK_OK CC state before transmitting */ #define PRL_FLAGS_WAIT_SINK_OK BIT(2) /* Flag to note transmission error occurred */ #define PRL_FLAGS_TX_ERROR BIT(3) /* Flag to note PE triggered a hard reset */ #define PRL_FLAGS_PE_HARD_RESET BIT(4) /* Flag to note hard reset has completed */ #define PRL_FLAGS_HARD_RESET_COMPLETE BIT(5) /* Flag to note port partner sent a hard reset */ #define PRL_FLAGS_PORT_PARTNER_HARD_RESET BIT(6) /* * Flag to note a message transmission has been requested. It is only cleared * when we send the message to the TCPC layer. */ #define PRL_FLAGS_MSG_XMIT BIT(7) /* Flag to note a message was received */ #define PRL_FLAGS_MSG_RECEIVED BIT(8) /* Flag to note aborting current TX message, not currently set */ #define PRL_FLAGS_ABORT BIT(9) /* Flag to note current TX message uses chunking */ #define PRL_FLAGS_CHUNKING BIT(10) struct bit_name { int value; const char *name; }; static __const_data struct bit_name flag_bit_names[] = { { PRL_FLAGS_TX_COMPLETE, "PRL_FLAGS_TX_COMPLETE" }, { PRL_FLAGS_SINK_NG, "PRL_FLAGS_SINK_NG" }, { PRL_FLAGS_WAIT_SINK_OK, "PRL_FLAGS_WAIT_SINK_OK" }, { PRL_FLAGS_TX_ERROR, "PRL_FLAGS_TX_ERROR" }, { PRL_FLAGS_PE_HARD_RESET, "PRL_FLAGS_PE_HARD_RESET" }, { PRL_FLAGS_HARD_RESET_COMPLETE, "PRL_FLAGS_HARD_RESET_COMPLETE" }, { PRL_FLAGS_PORT_PARTNER_HARD_RESET, "PRL_FLAGS_PORT_PARTNER_HARD_RESET" }, { PRL_FLAGS_MSG_XMIT, "PRL_FLAGS_MSG_XMIT" }, { PRL_FLAGS_MSG_RECEIVED, "PRL_FLAGS_MSG_RECEIVED" }, { PRL_FLAGS_ABORT, "PRL_FLAGS_ABORT" }, { PRL_FLAGS_CHUNKING, "PRL_FLAGS_CHUNKING" }, }; __maybe_unused static void print_bits(const char *group, const char *desc, int value, struct bit_name *names, int names_size) { int i; CPRINTF("%s %s 0x%x : ", group, desc, value); for (i = 0; i < names_size; i++) { if (value & names[i].value) CPRINTF("%s | ", names[i].name); value &= ~names[i].value; } if (value != 0) CPRINTF("0x%x", value); CPRINTF("\n"); } __maybe_unused static void print_flag(const char *group, int set_or_clear, int flag) { print_bits(group, set_or_clear ? "Set" : "Clr", flag, flag_bit_names, ARRAY_SIZE(flag_bit_names)); } /* PD counter definitions */ #define PD_MESSAGE_ID_COUNT 7 /* Size of PDMSG Chunk Buffer */ #define CHK_BUF_SIZE 7 #define CHK_BUF_SIZE_BYTES 28 /* * Debug log level - higher number == more log * Level 0: disabled * Level 1: not currently used * Level 2: plus non-ping messages * Level 3: plus ping packet and PRL states * * Note that higher log level causes timing changes and thus may affect * performance. */ #ifdef CONFIG_USB_PD_DEBUG_LEVEL static const enum debug_level prl_debug_level = CONFIG_USB_PD_DEBUG_LEVEL; #else static enum debug_level prl_debug_level = DEBUG_LEVEL_1; #endif static enum sm_local_state local_state[CONFIG_USB_PD_PORT_MAX_COUNT]; /* Protocol Transmit States (Section 6.11.2.2) */ enum usb_prl_tx_state { PRL_TX_PHY_LAYER_RESET, PRL_TX_WAIT_FOR_MESSAGE_REQUEST, PRL_TX_LAYER_RESET_FOR_TRANSMIT, PRL_TX_WAIT_FOR_PHY_RESPONSE, PRL_TX_SRC_SOURCE_TX, PRL_TX_SNK_START_AMS, PRL_TX_SRC_PENDING, PRL_TX_SNK_PENDING, PRL_TX_DISCARD_MESSAGE, }; /* Protocol Hard Reset States (Section 6.11.2.4) */ enum usb_prl_hr_state { PRL_HR_WAIT_FOR_REQUEST, PRL_HR_RESET_LAYER, PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE, PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE, }; /* Chunked Rx states (Section 6.11.2.1.2) */ enum usb_rch_state { RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER, RCH_PASS_UP_MESSAGE, RCH_PROCESSING_EXTENDED_MESSAGE, RCH_REQUESTING_CHUNK, RCH_WAITING_CHUNK, RCH_REPORT_ERROR, }; /* Chunked Tx states (Section 6.11.2.1.3) */ enum usb_tch_state { TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE, TCH_WAIT_FOR_TRANSMISSION_COMPLETE, TCH_CONSTRUCT_CHUNKED_MESSAGE, TCH_SENDING_CHUNKED_MESSAGE, TCH_WAIT_CHUNK_REQUEST, TCH_MESSAGE_RECEIVED, TCH_MESSAGE_SENT, TCH_REPORT_ERROR, }; static const char * const prl_tx_state_names[] = { [PRL_TX_PHY_LAYER_RESET] = "PRL_TX_PHY_LAYER_RESET", [PRL_TX_WAIT_FOR_MESSAGE_REQUEST] = "PRL_TX_WAIT_FOR_MESSAGE_REQUEST", [PRL_TX_LAYER_RESET_FOR_TRANSMIT] = "PRL_TX_LAYER_RESET_FOR_TRANSMIT", [PRL_TX_WAIT_FOR_PHY_RESPONSE] = "PRL_TX_WAIT_FOR_PHY_RESPONSE", [PRL_TX_SRC_SOURCE_TX] = "PRL_TX_SRC_SOURCE_TX", [PRL_TX_SNK_START_AMS] = "PRL_TX_SNK_START_AMS", [PRL_TX_SRC_PENDING] = "PRL_TX_SRC_PENDING", [PRL_TX_SNK_PENDING] = "PRL_TX_SNK_PENDING", [PRL_TX_DISCARD_MESSAGE] = "PRL_TX_DISCARD_MESSAGE", }; static const char * const prl_hr_state_names[] = { [PRL_HR_WAIT_FOR_REQUEST] = "PRL_HR_WAIT_FOR_REQUEST", [PRL_HR_RESET_LAYER] = "PRL_HR_RESET_LAYER", [PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE] = "PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE", [PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE] = "PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE", }; __maybe_unused static const char * const rch_state_names[] = { [RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER] = "RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER", [RCH_PASS_UP_MESSAGE] = "RCH_PASS_UP_MESSAGE", [RCH_PROCESSING_EXTENDED_MESSAGE] = "RCH_PROCESSING_EXTENDED_MESSAGE", [RCH_REQUESTING_CHUNK] = "RCH_REQUESTING_CHUNK", [RCH_WAITING_CHUNK] = "RCH_WAITING_CHUNK", [RCH_REPORT_ERROR] = "RCH_REPORT_ERROR", }; __maybe_unused static const char * const tch_state_names[] = { [TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE] = "TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE", [TCH_WAIT_FOR_TRANSMISSION_COMPLETE] = "TCH_WAIT_FOR_TRANSMISSION_COMPLETE", [TCH_CONSTRUCT_CHUNKED_MESSAGE] = "TCH_CONSTRUCT_CHUNKED_MESSAGE", [TCH_SENDING_CHUNKED_MESSAGE] = "TCH_SENDING_CHUNKED_MESSAGE", [TCH_WAIT_CHUNK_REQUEST] = "TCH_WAIT_CHUNK_REQUEST", [TCH_MESSAGE_RECEIVED] = "TCH_MESSAGE_RECEIVED", [TCH_MESSAGE_SENT] = "TCH_MESSAGE_SENT", [TCH_REPORT_ERROR] = "TCH_REPORT_ERROR", }; /* Forward declare full list of states. Index by above enums. */ static const struct usb_state prl_tx_states[]; static const struct usb_state prl_hr_states[]; __maybe_unused static const struct usb_state rch_states[]; __maybe_unused static const struct usb_state tch_states[]; /* Chunked Rx State Machine Object */ static struct rx_chunked { /* state machine context */ struct sm_ctx ctx; /* PRL_FLAGS */ uint32_t flags; /* error to report when moving to rch_report_error state */ enum pe_error error; } rch[CONFIG_USB_PD_PORT_MAX_COUNT]; /* Chunked Tx State Machine Object */ static struct tx_chunked { /* state machine context */ struct sm_ctx ctx; /* state machine flags */ uint32_t flags; /* error to report when moving to tch_report_error state */ enum pe_error error; } tch[CONFIG_USB_PD_PORT_MAX_COUNT]; /* Message Reception State Machine Object */ static struct protocol_layer_rx { /* received message type */ enum tcpci_msg_type sop; /* message ids for all valid port partners */ int msg_id[NUM_SOP_STAR_TYPES]; } prl_rx[CONFIG_USB_PD_PORT_MAX_COUNT]; /* Message Transmission State Machine Object */ static struct protocol_layer_tx { /* state machine context */ struct sm_ctx ctx; /* state machine flags */ uint32_t flags; /* last message type we transmitted */ enum tcpci_msg_type last_xmit_type; /* message id counters for all 6 port partners */ uint32_t msg_id_counter[NUM_SOP_STAR_TYPES]; /* transmit status */ int xmit_status; } prl_tx[CONFIG_USB_PD_PORT_MAX_COUNT]; /* Hard Reset State Machine Object */ static struct protocol_hard_reset { /* state machine context */ struct sm_ctx ctx; /* state machine flags */ uint32_t flags; } prl_hr[CONFIG_USB_PD_PORT_MAX_COUNT]; /* Chunking Message Object */ static struct pd_message { /* message status flags */ uint32_t flags; /* SOP* */ enum tcpci_msg_type xmit_type; /* type of message */ uint8_t msg_type; /* PD revision */ enum pd_rev_type rev[NUM_SOP_STAR_TYPES]; /* Number of 32-bit objects in chk_buf */ uint16_t data_objs; /* temp chunk buffer */ uint32_t tx_chk_buf[CHK_BUF_SIZE]; uint32_t rx_chk_buf[CHK_BUF_SIZE]; uint32_t chunk_number_expected; uint32_t num_bytes_received; #ifdef CONFIG_USB_PD_EXTENDED_MESSAGES /* extended message */ uint8_t ext; uint32_t chunk_number_to_send; uint32_t send_offset; #endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ } pdmsg[CONFIG_USB_PD_PORT_MAX_COUNT]; struct extended_msg rx_emsg[CONFIG_USB_PD_PORT_MAX_COUNT]; struct extended_msg tx_emsg[CONFIG_USB_PD_PORT_MAX_COUNT]; /* Common Protocol Layer Message Transmission */ static void prl_tx_construct_message(int port); static void prl_rx_wait_for_phy_message(const int port, int evt); static void prl_copy_msg_to_buffer(int port); #ifndef CONFIG_USB_PD_REV30 GEN_NOT_SUPPORTED(PRL_TX_SRC_SOURCE_TX); #define PRL_TX_SRC_SOURCE_TX PRL_TX_SRC_SOURCE_TX_NOT_SUPPORTED GEN_NOT_SUPPORTED(PRL_TX_SNK_START_AMS); #define PRL_TX_SNK_START_AMS PRL_TX_SNK_START_AMS_NOT_SUPPORTED GEN_NOT_SUPPORTED(RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); #define RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER \ RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER_NOT_SUPPORTED GEN_NOT_SUPPORTED(RCH_PASS_UP_MESSAGE); #define RCH_PASS_UP_MESSAGE RCH_PASS_UP_MESSAGE_NOT_SUPPORTED GEN_NOT_SUPPORTED(RCH_PROCESSING_EXTENDED_MESSAGE); #define RCH_PROCESSING_EXTENDED_MESSAGE \ RCH_PROCESSING_EXTENDED_MESSAGE_NOT_SUPPORTED GEN_NOT_SUPPORTED(RCH_REQUESTING_CHUNK); #define RCH_REQUESTING_CHUNK RCH_REQUESTING_CHUNK_NOT_SUPPORTED GEN_NOT_SUPPORTED(RCH_WAITING_CHUNK); #define RCH_WAITING_CHUNK RCH_WAITING_CHUNK_NOT_SUPPORTED GEN_NOT_SUPPORTED(RCH_REPORT_ERROR); #define RCH_REPORT_ERROR RCH_REPORT_ERROR_NOT_SUPPORTED GEN_NOT_SUPPORTED(TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); #define TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE \ TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE_NOT_SUPPORTED GEN_NOT_SUPPORTED(TCH_WAIT_FOR_TRANSMISSION_COMPLETE); #define TCH_WAIT_FOR_TRANSMISSION_COMPLETE \ TCH_WAIT_FOR_TRANSMISSION_COMPLETE_NOT_SUPPORTED GEN_NOT_SUPPORTED(TCH_CONSTRUCT_CHUNKED_MESSAGE); #define TCH_CONSTRUCT_CHUNKED_MESSAGE \ TCH_CONSTRUCT_CHUNKED_MESSAGE_NOT_SUPPORTED GEN_NOT_SUPPORTED(TCH_SENDING_CHUNKED_MESSAGE); #define TCH_SENDING_CHUNKED_MESSAGE TCH_SENDING_CHUNKED_MESSAGE_NOT_SUPPORTED GEN_NOT_SUPPORTED(TCH_WAIT_CHUNK_REQUEST); #define TCH_WAIT_CHUNK_REQUEST TCH_WAIT_CHUNK_REQUEST_NOT_SUPPORTED GEN_NOT_SUPPORTED(TCH_MESSAGE_RECEIVED); #define TCH_MESSAGE_RECEIVED TCH_MESSAGE_RECEIVED_NOT_SUPPORTED GEN_NOT_SUPPORTED(TCH_MESSAGE_SENT); #define TCH_MESSAGE_SENT TCH_MESSAGE_SENT_NOT_SUPPORTED GEN_NOT_SUPPORTED(TCH_REPORT_ERROR); #define TCH_REPORT_ERROR TCH_REPORT_ERROR_NOT_SUPPORTED #endif /* !CONFIG_USB_PD_REV30 */ /* To store the time stamp when TCPC sets TX Complete Success */ static timestamp_t tcpc_tx_success_ts[CONFIG_USB_PD_PORT_MAX_COUNT]; /* Set the protocol transmit statemachine to a new state. */ static void set_state_prl_tx(const int port, const enum usb_prl_tx_state new_state) { set_state(port, &prl_tx[port].ctx, &prl_tx_states[new_state]); } /* Get the protocol transmit statemachine's current state. */ test_export_static enum usb_prl_tx_state prl_tx_get_state(const int port) { return prl_tx[port].ctx.current - &prl_tx_states[0]; } /* Print the protocol transmit statemachine's current state. */ static void print_current_prl_tx_state(const int port) { if (prl_debug_level >= DEBUG_LEVEL_3) CPRINTS("C%d: %s", port, prl_tx_state_names[prl_tx_get_state(port)]); } /* Set the hard reset statemachine to a new state. */ static void set_state_prl_hr(const int port, const enum usb_prl_hr_state new_state) { set_state(port, &prl_hr[port].ctx, &prl_hr_states[new_state]); } /* Get the hard reset statemachine's current state. */ enum usb_prl_hr_state prl_hr_get_state(const int port) { return prl_hr[port].ctx.current - &prl_hr_states[0]; } /* Print the hard reset statemachine's current state. */ static void print_current_prl_hr_state(const int port) { if (prl_debug_level >= DEBUG_LEVEL_3) CPRINTS("C%d: %s", port, prl_hr_state_names[prl_hr_get_state(port)]); } /* Set the chunked Rx statemachine to a new state. */ static void set_state_rch(const int port, const enum usb_rch_state new_state) { if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) set_state(port, &rch[port].ctx, &rch_states[new_state]); } #ifdef CONFIG_USB_PD_EXTENDED_MESSAGES /* Get the chunked Rx statemachine's current state. */ test_export_static enum usb_rch_state rch_get_state(const int port) { return rch[port].ctx.current - &rch_states[0]; } /* Print the chunked Rx statemachine's current state. */ static void print_current_rch_state(const int port) { if (prl_debug_level >= DEBUG_LEVEL_3) CPRINTS("C%d: %s", port, rch_state_names[rch_get_state(port)]); } #endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ /* Set the chunked Tx statemachine to a new state. */ static void set_state_tch(const int port, const enum usb_tch_state new_state) { if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) set_state(port, &tch[port].ctx, &tch_states[new_state]); } /* Get the chunked Tx statemachine's current state. */ test_export_static enum usb_tch_state tch_get_state(const int port) { if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) return tch[port].ctx.current - &tch_states[0]; else return 0; } #ifdef CONFIG_USB_PD_EXTENDED_MESSAGES /* Print the chunked Tx statemachine's current state. */ static void print_current_tch_state(const int port) { if (prl_debug_level >= DEBUG_LEVEL_3) CPRINTS("C%d: %s", port, tch_state_names[tch_get_state(port)]); } #endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ timestamp_t prl_get_tcpc_tx_success_ts(int port) { return tcpc_tx_success_ts[port]; } /* Sets the time stamp when TCPC reports TX success. */ static void set_tcpc_tx_success_ts(int port) { tcpc_tx_success_ts[port] = get_time(); } void pd_transmit_complete(int port, int status) { if (status == TCPC_TX_COMPLETE_SUCCESS) set_tcpc_tx_success_ts(port); prl_tx[port].xmit_status = status; } void pd_execute_hard_reset(int port) { /* Only allow async. function calls when state machine is running */ if (!prl_is_running(port)) return; PRL_HR_SET_FLAG(port, PRL_FLAGS_PORT_PARTNER_HARD_RESET); set_state_prl_hr(port, PRL_HR_RESET_LAYER); task_wake(PD_PORT_TO_TASK_ID(port)); } void prl_execute_hard_reset(int port) { /* Only allow async. function calls when state machine is running */ if (!prl_is_running(port)) return; PRL_HR_SET_FLAG(port, PRL_FLAGS_PE_HARD_RESET); set_state_prl_hr(port, PRL_HR_RESET_LAYER); task_wake(PD_PORT_TO_TASK_ID(port)); } int prl_is_running(int port) { return local_state[port] == SM_RUN; } static void prl_init(int port) { int i; const struct sm_ctx cleared = {}; /* * flags without PRL_FLAGS_SINK_NG present means we are initially * in SinkTxOK state */ prl_tx[port].flags = 0; if (IS_ENABLED(CONFIG_USB_PD_REV30)) typec_select_src_collision_rp(port, SINK_TX_OK); prl_tx[port].last_xmit_type = TCPCI_MSG_SOP; prl_tx[port].xmit_status = TCPC_TX_UNSET; if (IS_ENABLED(CONFIG_USB_PD_REV30)) { tch[port].flags = 0; rch[port].flags = 0; } pdmsg[port].flags = 0; prl_hr[port].flags = 0; for (i = 0; i < NUM_SOP_STAR_TYPES; i++) { prl_rx[port].msg_id[i] = -1; prl_tx[port].msg_id_counter[i] = 0; } pd_timer_disable_range(port, PR_TIMER_RANGE); /* Clear state machines and set initial states */ prl_tx[port].ctx = cleared; set_state_prl_tx(port, PRL_TX_PHY_LAYER_RESET); if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { rch[port].ctx = cleared; set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); tch[port].ctx = cleared; set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); } prl_hr[port].ctx = cleared; set_state_prl_hr(port, PRL_HR_WAIT_FOR_REQUEST); } bool prl_is_busy(int port) { #ifdef CONFIG_USB_PD_EXTENDED_MESSAGES return rch_get_state(port) != RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER || tch_get_state(port) != TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE; #else return false; #endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ } void prl_set_debug_level(enum debug_level debug_level) { #ifndef CONFIG_USB_PD_DEBUG_LEVEL prl_debug_level = debug_level; #endif } void prl_hard_reset_complete(int port) { PRL_HR_SET_FLAG(port, PRL_FLAGS_HARD_RESET_COMPLETE); task_wake(PD_PORT_TO_TASK_ID(port)); } void prl_send_ctrl_msg(int port, enum tcpci_msg_type type, enum pd_ctrl_msg_type msg) { pdmsg[port].xmit_type = type; pdmsg[port].msg_type = msg; pdmsg[port].data_objs = 0; tx_emsg[port].len = 0; #ifdef CONFIG_USB_PD_EXTENDED_MESSAGES pdmsg[port].ext = 0; TCH_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); #else PRL_TX_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); #endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ task_wake(PD_PORT_TO_TASK_ID(port)); } void prl_send_data_msg(int port, enum tcpci_msg_type type, enum pd_data_msg_type msg) { pdmsg[port].xmit_type = type; pdmsg[port].msg_type = msg; #ifdef CONFIG_USB_PD_EXTENDED_MESSAGES pdmsg[port].ext = 0; TCH_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); #else prl_copy_msg_to_buffer(port); PRL_TX_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); #endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ task_wake(PD_PORT_TO_TASK_ID(port)); } #ifdef CONFIG_USB_PD_EXTENDED_MESSAGES void prl_send_ext_data_msg(int port, enum tcpci_msg_type type, enum pd_ext_msg_type msg) { pdmsg[port].xmit_type = type; pdmsg[port].msg_type = msg; pdmsg[port].ext = 1; TCH_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); task_wake(PD_PORT_TO_TASK_ID(port)); } #endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ void prl_set_default_pd_revision(int port) { /* * Initialize to highest revision supported. If the port or cable * partner doesn't support this revision, the Protocol Engine will * lower this value to the revision supported by the partner. */ pdmsg[port].rev[TCPCI_MSG_SOP] = PD_REVISION; pdmsg[port].rev[TCPCI_MSG_SOP_PRIME] = PD_REVISION; pdmsg[port].rev[TCPCI_MSG_SOP_PRIME_PRIME] = PD_REVISION; pdmsg[port].rev[TCPCI_MSG_SOP_DEBUG_PRIME] = PD_REVISION; pdmsg[port].rev[TCPCI_MSG_SOP_DEBUG_PRIME_PRIME] = PD_REVISION; } void prl_reset_soft(int port) { /* Do not change negotiated PD Revision Specification level */ local_state[port] = SM_INIT; /* Ensure we process the reset quickly */ task_wake(PD_PORT_TO_TASK_ID(port)); } void prl_run(int port, int evt, int en) { switch (local_state[port]) { case SM_PAUSED: if (!en) break; /* fall through */ case SM_INIT: prl_init(port); local_state[port] = SM_RUN; /* fall through */ case SM_RUN: if (!en) { /* Disable RX */ if (IS_ENABLED(CONFIG_USB_CTVPD) || IS_ENABLED(CONFIG_USB_VPD)) vpd_rx_enable(0); else tcpm_set_rx_enable(port, 0); local_state[port] = SM_PAUSED; break; } /* Run Protocol Layer Hard Reset state machine */ run_state(port, &prl_hr[port].ctx); /* * If the Hard Reset state machine is active, then there is no * need to execute any other PRL state machines. When the hard * reset is complete, all PRL state machines will have been * reset. */ if (prl_hr_get_state(port) == PRL_HR_WAIT_FOR_REQUEST) { /* Run Protocol Layer Message Reception */ prl_rx_wait_for_phy_message(port, evt); if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { /* * Run RX Chunked state machine after prl_rx. * This is what informs the PE of incoming * message. Its input is prl_rx */ run_state(port, &rch[port].ctx); /* * Run TX Chunked state machine before prl_tx * in case we need to split an extended message * and prl_tx can send it for us */ run_state(port, &tch[port].ctx); } /* Run Protocol Layer Message Tx state machine */ run_state(port, &prl_tx[port].ctx); if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) /* * Run TX Chunked state machine again after * prl_tx so we can handle passing TX_COMPLETE * (or failure) up to PE in a single iteration. */ run_state(port, &tch[port].ctx); } break; } } void prl_set_rev(int port, enum tcpci_msg_type type, enum pd_rev_type rev) { /* We only store revisions for SOP* types. */ ASSERT(type < NUM_SOP_STAR_TYPES); pdmsg[port].rev[type] = rev; } enum pd_rev_type prl_get_rev(int port, enum tcpci_msg_type type) { /* We only store revisions for SOP* types. */ ASSERT(type < NUM_SOP_STAR_TYPES); return pdmsg[port].rev[type]; } static void prl_copy_msg_to_buffer(int port) { /* * Control Messages will have a length of 0 and * no need to spend time with the tx_chk_buf * for this path */ if (tx_emsg[port].len == 0) { pdmsg[port].data_objs = 0; return; } /* * Make sure the Policy Engine isn't sending * more than CHK_BUF_SIZE_BYTES. If so, * truncate len. This will surely send a * malformed packet resulting in the port * partner soft\hard resetting us. */ if (tx_emsg[port].len > CHK_BUF_SIZE_BYTES) tx_emsg[port].len = CHK_BUF_SIZE_BYTES; /* Copy message to chunked buffer */ memset((uint8_t *)pdmsg[port].tx_chk_buf, 0, CHK_BUF_SIZE_BYTES); memcpy((uint8_t *)pdmsg[port].tx_chk_buf, (uint8_t *)tx_emsg[port].buf, tx_emsg[port].len); /* * Pad length to 4-byte boundary and * convert to number of 32-bit objects. * Since the value is shifted right by 2, * no need to explicitly clear the lower * 2-bits. */ pdmsg[port].data_objs = (tx_emsg[port].len + 3) >> 2; } static __maybe_unused int pdmsg_xmit_type_is_rev30(const int port) { if (IS_ENABLED(CONFIG_USB_PD_REV30)) return ((pdmsg[port].xmit_type < NUM_SOP_STAR_TYPES) && (prl_get_rev(port, pdmsg[port].xmit_type) == PD_REV30)); else return 0; } /* Returns true if the SOP port partner operates at PD rev3.0 */ static bool is_sop_rev30(const int port) { return IS_ENABLED(CONFIG_USB_PD_REV30) && prl_get_rev(port, TCPCI_MSG_SOP) == PD_REV30; } /* Common Protocol Layer Message Transmission */ static void prl_tx_phy_layer_reset_entry(const int port) { print_current_prl_tx_state(port); if (IS_ENABLED(CONFIG_USB_CTVPD) || IS_ENABLED(CONFIG_USB_VPD)) { vpd_rx_enable(pd_is_connected(port)); } else { /* Note: can't clear PHY messages due to TCPC architecture */ /* Enable communications*/ tcpm_set_rx_enable(port, pd_is_connected(port)); } set_state_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); } static void prl_tx_wait_for_message_request_entry(const int port) { /* No phy layer response is pending */ prl_tx[port].xmit_status = TCPC_TX_UNSET; print_current_prl_tx_state(port); } static void prl_tx_wait_for_message_request_run(const int port) { /* Clear any AMS flags and state if we are no longer in an AMS */ if (IS_ENABLED(CONFIG_USB_PD_REV30) && !pe_in_local_ams(port)) { /* Note PRL_Tx_Src_Sink_Tx is embedded here. */ if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_SINK_NG)) { typec_select_src_collision_rp(port, SINK_TX_OK); typec_update_cc(port); } PRL_TX_CLR_FLAG(port, PRL_FLAGS_SINK_NG | PRL_FLAGS_WAIT_SINK_OK); } /* * Check if we are starting an AMS and need to wait and/or set the CC * lines appropriately. */ if (IS_ENABLED(CONFIG_USB_PD_REV30) && is_sop_rev30(port) && pe_in_local_ams(port)) { if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_SINK_NG | PRL_FLAGS_WAIT_SINK_OK)) { /* * If we are already in an AMS then allow the * multi-message AMS to continue, even if we * swap power roles. * * Fall Through using the current AMS */ } else { /* * Start of SRC AMS notification received from * Policy Engine */ if (pd_get_power_role(port) == PD_ROLE_SOURCE) { PRL_TX_SET_FLAG(port, PRL_FLAGS_SINK_NG); set_state_prl_tx(port, PRL_TX_SRC_SOURCE_TX); } else { PRL_TX_SET_FLAG(port, PRL_FLAGS_WAIT_SINK_OK); set_state_prl_tx(port, PRL_TX_SNK_START_AMS); } return; } } /* Handle non Rev 3.0 or subsequent messages in AMS sequence */ if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { PRL_TX_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); /* * Soft Reset Message Message pending */ if ((pdmsg[port].msg_type == PD_CTRL_SOFT_RESET) && (tx_emsg[port].len == 0)) { set_state_prl_tx(port, PRL_TX_LAYER_RESET_FOR_TRANSMIT); } /* * Message pending (except Soft Reset) */ else { /* NOTE: PRL_TX_Construct_Message State embedded here */ prl_tx_construct_message(port); set_state_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); } return; } } static void increment_msgid_counter(int port) { /* If the last message wasn't an SOP* message, no need to increment */ if (prl_tx[port].last_xmit_type >= NUM_SOP_STAR_TYPES) return; prl_tx[port].msg_id_counter[prl_tx[port].last_xmit_type] = (prl_tx[port].msg_id_counter[prl_tx[port].last_xmit_type] + 1) & PD_MESSAGE_ID_COUNT; } /* * PrlTxDiscard */ static void prl_tx_discard_message_entry(const int port) { print_current_prl_tx_state(port); /* * Discard queued message * Note: We differ from spec here, which allows us to not discard on * incoming SOP' or SOP''. However this would get the TCH out of sync. * * prl_tx will be set to this state following message reception in * prl_rx. So this path will be entered following each rx message. If * this state is entered, and there is either a message from the PE * pending, or if a message was passed to the phy and there is either no * response yet, or it was discarded in the phy layer, then a tx message * discard event has been detected. */ if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT) || prl_tx[port].xmit_status == TCPC_TX_WAIT || prl_tx[port].xmit_status == TCPC_TX_COMPLETE_DISCARDED) { PRL_TX_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); increment_msgid_counter(port); pe_report_discard(port); } set_state_prl_tx(port, PRL_TX_PHY_LAYER_RESET); } #ifdef CONFIG_USB_PD_REV30 /* * PrlTxSrcSourceTx */ static void prl_tx_src_source_tx_entry(const int port) { print_current_prl_tx_state(port); /* Set Rp = SinkTxNG */ typec_select_src_collision_rp(port, SINK_TX_NG); typec_update_cc(port); } static void prl_tx_src_source_tx_run(const int port) { if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { /* * Don't clear pending XMIT flag here. Wait until we send so * we can detect if we dropped this message or not. */ set_state_prl_tx(port, PRL_TX_SRC_PENDING); } } /* * PrlTxSnkStartAms */ static void prl_tx_snk_start_ams_entry(const int port) { print_current_prl_tx_state(port); } static void prl_tx_snk_start_ams_run(const int port) { if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { /* * Don't clear pending XMIT flag here. Wait until we send so * we can detect if we dropped this message or not. */ set_state_prl_tx(port, PRL_TX_SNK_PENDING); } } #endif /* CONFIG_USB_PD_REV30 */ /* * PrlTxLayerResetForTransmit */ static void prl_tx_layer_reset_for_transmit_entry(const int port) { print_current_prl_tx_state(port); if (pdmsg[port].xmit_type < NUM_SOP_STAR_TYPES) { /* * This state is only used during soft resets. Reset only the * matching message type. * * From section 6.3.13 Soft Reset Message in the USB PD 3.0 * v2.0 spec, Soft_Reset Message Shall be targeted at a * specific entity depending on the type of SOP* Packet used. */ prl_tx[port].msg_id_counter[pdmsg[port].xmit_type] = 0; /* * From section 6.11.2.3.2, the MessageID should be cleared * from the PRL_Rx_Layer_Reset_for_Receive state. However, we * don't implement a full state machine for PRL RX states so * clear the MessageID here. */ prl_rx[port].msg_id[pdmsg[port].xmit_type] = -1; } } static void prl_tx_layer_reset_for_transmit_run(const int port) { /* NOTE: PRL_Tx_Construct_Message State embedded here */ prl_tx_construct_message(port); set_state_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); } static uint32_t get_sop_star_header(const int port) { const int is_sop_packet = pdmsg[port].xmit_type == TCPCI_MSG_SOP; int ext; #ifdef CONFIG_USB_PD_EXTENDED_MESSAGES ext = pdmsg[port].ext; #else ext = 0; #endif /* SOP vs SOP'/SOP" headers are different. Replace fields as needed */ return PD_HEADER( pdmsg[port].msg_type, is_sop_packet ? pd_get_power_role(port) : tc_get_cable_plug(port), is_sop_packet ? pd_get_data_role(port) : 0, prl_tx[port].msg_id_counter[pdmsg[port].xmit_type], pdmsg[port].data_objs, pdmsg[port].rev[pdmsg[port].xmit_type], ext); } static void prl_tx_construct_message(const int port) { /* The header is unused for hard reset, etc. */ const uint32_t header = pdmsg[port].xmit_type < NUM_SOP_STAR_TYPES ? get_sop_star_header(port) : 0; /* Save SOP* so the correct msg_id_counter can be incremented */ prl_tx[port].last_xmit_type = pdmsg[port].xmit_type; /* Indicate that a tx message is being passed to the phy layer */ prl_tx[port].xmit_status = TCPC_TX_WAIT; /* * PRL_FLAGS_TX_COMPLETE could be set if this function is called before * the Policy Engine is informed of the previous transmission. Clear the * flag so that this message can be sent. */ PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_COMPLETE); /* * Pass message to PHY Layer. It handles retries in hardware as the EC * cannot handle the required timing ~ 1ms (tReceive + tRetry). * * Note if we ever start sending large, extendend messages, then we * should not retry those messages. We do not support that and probably * never will (since we support chunking). */ tcpm_transmit(port, pdmsg[port].xmit_type, header, pdmsg[port].tx_chk_buf); } /* * PrlTxWaitForPhyResponse */ static void prl_tx_wait_for_phy_response_entry(const int port) { print_current_prl_tx_state(port); pd_timer_enable(port, PR_TIMER_TCPC_TX_TIMEOUT, PD_T_TCPC_TX_TIMEOUT); } static void prl_tx_wait_for_phy_response_run(const int port) { /* Wait until TX is complete */ /* * NOTE: The TCPC will set xmit_status to TCPC_TX_COMPLETE_DISCARDED * when a GoodCRC containing an incorrect MessageID is received. * This condition satisfies the PRL_Tx_Match_MessageID state * requirement. */ if (prl_tx[port].xmit_status == TCPC_TX_COMPLETE_SUCCESS) { /* NOTE: PRL_TX_Message_Sent State embedded here. */ /* Increment messageId counter */ increment_msgid_counter(port); /* Inform Policy Engine Message was sent */ if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) PDMSG_SET_FLAG(port, PRL_FLAGS_TX_COMPLETE); else pe_message_sent(port); /* * This event reduces the time of informing the policy engine of * the transmission by one state machine cycle */ task_wake(PD_PORT_TO_TASK_ID(port)); set_state_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); } else if (pd_timer_is_expired(port, PR_TIMER_TCPC_TX_TIMEOUT) || prl_tx[port].xmit_status == TCPC_TX_COMPLETE_FAILED) { /* * NOTE: PRL_Tx_Transmission_Error State embedded * here. */ if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { /* * State tch_wait_for_transmission_complete will * inform policy engine of error */ PDMSG_SET_FLAG(port, PRL_FLAGS_TX_ERROR); } else { /* Report Error To Policy Engine */ pe_report_error(port, ERR_TCH_XMIT, prl_tx[port].last_xmit_type); } /* Increment message id counter */ increment_msgid_counter(port); set_state_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); } } static void prl_tx_wait_for_phy_response_exit(const int port) { pd_timer_disable(port, PR_TIMER_TCPC_TX_TIMEOUT); } /* Source Protocol Layer Message Transmission */ /* * PrlTxSrcPending */ static void prl_tx_src_pending_entry(const int port) { print_current_prl_tx_state(port); /* Start SinkTxTimer */ pd_timer_enable(port, PR_TIMER_SINK_TX, PD_T_SINK_TX); } static void prl_tx_src_pending_run(const int port) { if (pd_timer_is_expired(port, PR_TIMER_SINK_TX)) { /* * We clear the pending XMIT flag here right before we send so * we can detect if we discarded this message or not */ PRL_TX_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); /* * Soft Reset Message pending & * SinkTxTimer timeout */ if ((tx_emsg[port].len == 0) && (pdmsg[port].msg_type == PD_CTRL_SOFT_RESET)) { set_state_prl_tx(port, PRL_TX_LAYER_RESET_FOR_TRANSMIT); } /* Message pending (except Soft Reset) & * SinkTxTimer timeout */ else { prl_tx_construct_message(port); set_state_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); } return; } } static void prl_tx_src_pending_exit(int port) { pd_timer_disable(port, PR_TIMER_SINK_TX); } /* * PrlTxSnkPending */ static void prl_tx_snk_pending_entry(const int port) { print_current_prl_tx_state(port); } static void prl_tx_snk_pending_run(const int port) { bool start_tx = false; /* * Wait unit the SRC applies SINK_TX_OK so we can transmit. In FRS mode, * don't wait for SINK_TX_OK since either the source (and Rp) could be * gone or the TCPC CC_STATUS update time could be too long to meet * tFRSwapInit. */ if (pe_in_frs_mode(port)) { /* shortcut to save some i2c_xfer calls on the FRS path. */ start_tx = true; } else { enum tcpc_cc_voltage_status cc1, cc2; tcpm_get_cc(port, &cc1, &cc2); start_tx = (cc1 == TYPEC_CC_VOLT_RP_3_0 || cc2 == TYPEC_CC_VOLT_RP_3_0); } if (start_tx) { /* * We clear the pending XMIT flag here right before we send so * we can detect if we discarded this message or not */ PRL_TX_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); /* * Soft Reset Message Message pending & * Rp = SinkTxOk */ if ((pdmsg[port].msg_type == PD_CTRL_SOFT_RESET) && (tx_emsg[port].len == 0)) { set_state_prl_tx(port, PRL_TX_LAYER_RESET_FOR_TRANSMIT); } /* * Message pending (except Soft Reset) & * Rp = SinkTxOk */ else { prl_tx_construct_message(port); set_state_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); } return; } } /* Hard Reset Operation */ void prl_hr_send_msg_to_phy(const int port) { /* Header is not used for hard reset */ const uint32_t header = 0; pdmsg[port].xmit_type = TCPCI_MSG_TX_HARD_RESET; /* * These flags could be set if this function is called before the * Policy Engine is informed of the previous transmission. Clear the * flags so that this message can be sent. */ prl_tx[port].xmit_status = TCPC_TX_UNSET; PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_COMPLETE); /* Pass message to PHY Layer */ tcpm_transmit(port, pdmsg[port].xmit_type, header, pdmsg[port].tx_chk_buf); } static void prl_hr_wait_for_request_entry(const int port) { print_current_prl_hr_state(port); prl_hr[port].flags = 0; } static void prl_hr_wait_for_request_run(const int port) { if (PRL_HR_CHK_FLAG(port, PRL_FLAGS_PE_HARD_RESET | PRL_FLAGS_PORT_PARTNER_HARD_RESET)) set_state_prl_hr(port, PRL_HR_RESET_LAYER); } /* * PrlHrResetLayer */ static void prl_hr_reset_layer_entry(const int port) { int i; print_current_prl_hr_state(port); if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { tch[port].flags = 0; rch[port].flags = 0; } pdmsg[port].flags = 0; /* Hard reset resets messageIDCounters for all TX types */ for (i = 0; i < NUM_SOP_STAR_TYPES; i++) { prl_rx[port].msg_id[i] = -1; prl_tx[port].msg_id_counter[i] = 0; } /* Disable RX */ if (IS_ENABLED(CONFIG_USB_CTVPD) || IS_ENABLED(CONFIG_USB_VPD)) vpd_rx_enable(0); else tcpm_set_rx_enable(port, 0); /* * PD r3.0 v2.0, ss6.2.1.1.5: * After a physical or logical (USB Type-C Error Recovery) Attach, a * Port discovers the common Specification Revision level between itself * and its Port Partner and/or the Cable Plug(s), and uses this * Specification Revision level until a Detach, Hard Reset or Error * Recovery happens. * * This covers the Hard Reset case. */ prl_set_default_pd_revision(port); /* Inform the AP of Hard Reset */ if (IS_ENABLED(CONFIG_USB_PD_HOST_CMD)) pd_notify_event(port, PD_STATUS_EVENT_HARD_RESET); /* * Protocol Layer message transmission transitions to * PRL_Tx_Wait_For_Message_Request state. */ set_state_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); return; } static void prl_hr_reset_layer_run(const int port) { /* * Protocol Layer reset Complete & * Hard Reset was initiated by Policy Engine */ if (PRL_HR_CHK_FLAG(port, PRL_FLAGS_PE_HARD_RESET)) { /* * Request PHY to perform a Hard Reset. Note * PRL_HR_Request_Reset state is embedded here. */ prl_hr_send_msg_to_phy(port); set_state_prl_hr(port, PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE); } /* * Protocol Layer reset complete & * Hard Reset was initiated by Port Partner */ else { /* Inform Policy Engine of the Hard Reset */ pe_got_hard_reset(port); set_state_prl_hr(port, PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE); } } /* * PrlHrWaitForPhyHardResetComplete */ static void prl_hr_wait_for_phy_hard_reset_complete_entry(const int port) { print_current_prl_hr_state(port); /* Start HardResetCompleteTimer */ pd_timer_enable(port, PR_TIMER_HARD_RESET_COMPLETE, PD_T_PS_HARD_RESET); } static void prl_hr_wait_for_phy_hard_reset_complete_run(const int port) { /* * Wait for hard reset from PHY * or timeout */ if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_COMPLETE) || pd_timer_is_expired(port, PR_TIMER_HARD_RESET_COMPLETE)) { /* PRL_HR_PHY_Hard_Reset_Requested */ /* Inform Policy Engine Hard Reset was sent */ pe_hard_reset_sent(port); set_state_prl_hr(port, PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE); return; } } static void prl_hr_wait_for_phy_hard_reset_complete_exit(int port) { pd_timer_disable(port, PR_TIMER_HARD_RESET_COMPLETE); } /* * PrlHrWaitForPeHardResetComplete */ static void prl_hr_wait_for_pe_hard_reset_complete_entry(const int port) { print_current_prl_hr_state(port); } static void prl_hr_wait_for_pe_hard_reset_complete_run(const int port) { /* * Wait for Hard Reset complete indication from Policy Engine */ if (PRL_HR_CHK_FLAG(port, PRL_FLAGS_HARD_RESET_COMPLETE)) set_state_prl_hr(port, PRL_HR_WAIT_FOR_REQUEST); } static void prl_hr_wait_for_pe_hard_reset_complete_exit(const int port) { /* Exit from Hard Reset */ set_state_prl_tx(port, PRL_TX_PHY_LAYER_RESET); if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); } } static void copy_chunk_to_ext(int port) { /* Calculate number of bytes */ pdmsg[port].num_bytes_received = (PD_HEADER_CNT(rx_emsg[port].header) * 4); /* Copy chunk into extended message */ memcpy((uint8_t *)rx_emsg[port].buf, (uint8_t *)pdmsg[port].rx_chk_buf, pdmsg[port].num_bytes_received); /* Set extended message length */ rx_emsg[port].len = pdmsg[port].num_bytes_received; } #ifdef CONFIG_USB_PD_EXTENDED_MESSAGES /* * Chunked Rx State Machine */ /* * RchWaitForMessageFromProtocolLayer */ static void rch_wait_for_message_from_protocol_layer_entry(const int port) { print_current_rch_state(port); /* Clear Abort flag */ PDMSG_CLR_FLAG(port, PRL_FLAGS_ABORT); /* All Messages are chunked */ rch[port].flags = PRL_FLAGS_CHUNKING; } static void rch_wait_for_message_from_protocol_layer_run(const int port) { if (RCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { RCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); /* * Are we communicating with a PD3.0 device and is * this an extended message? */ if (pdmsg_xmit_type_is_rev30(port) && PD_HEADER_EXT(rx_emsg[port].header)) { uint16_t exhdr = GET_EXT_HEADER(*pdmsg[port].rx_chk_buf); uint8_t chunked = PD_EXT_HEADER_CHUNKED(exhdr); /* * Received Extended Message & * (Chunking = 1 & Chunked = 1) */ if ((RCH_CHK_FLAG(port, PRL_FLAGS_CHUNKING)) && chunked) { /* * RCH_Processing_Extended_Message first chunk * entry processing embedded here * * This is the first chunk: * Set Chunk_number_expected = 0 and * Num_Bytes_Received = 0 */ pdmsg[port].chunk_number_expected = 0; pdmsg[port].num_bytes_received = 0; pdmsg[port].msg_type = PD_HEADER_TYPE(rx_emsg[port].header); set_state_rch(port, RCH_PROCESSING_EXTENDED_MESSAGE); } /* * (Received Extended Message & * (Chunking = 0 & Chunked = 0)) */ else if (!RCH_CHK_FLAG(port, PRL_FLAGS_CHUNKING) && !chunked) { /* Copy chunk to extended buffer */ copy_chunk_to_ext(port); set_state_rch(port, RCH_PASS_UP_MESSAGE); } /* * Chunked != Chunking */ else { rch[port].error = ERR_RCH_CHUNKED; set_state_rch(port, RCH_REPORT_ERROR); } } /* * Received Non-Extended Message */ else if (!PD_HEADER_EXT(rx_emsg[port].header)) { /* Copy chunk to extended buffer */ copy_chunk_to_ext(port); set_state_rch(port, RCH_PASS_UP_MESSAGE); } /* * Received an Extended Message while communicating at a * revision lower than PD3.0 */ else { rch[port].error = ERR_RCH_CHUNKED; set_state_rch(port, RCH_REPORT_ERROR); } } } /* * RchPassUpMessage */ static void rch_pass_up_message_entry(const int port) { print_current_rch_state(port); /* Pass Message to Policy Engine */ pe_message_received(port); set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); } /* * RchProcessingExtendedMessage */ static void rch_processing_extended_message_entry(const int port) { print_current_rch_state(port); } static void rch_processing_extended_message_run(const int port) { uint16_t exhdr = GET_EXT_HEADER(pdmsg[port].rx_chk_buf[0]); uint8_t chunk_num = PD_EXT_HEADER_CHUNK_NUM(exhdr); uint32_t data_size = PD_EXT_HEADER_DATA_SIZE(exhdr); uint32_t byte_num; /* * Abort Flag Set */ if (PDMSG_CHK_FLAG(port, PRL_FLAGS_ABORT)) set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); /* * If expected Chunk Number: * Append data to Extended_Message_Buffer * Increment Chunk_number_Expected * Adjust Num Bytes Received */ else if (chunk_num == pdmsg[port].chunk_number_expected) { byte_num = data_size - pdmsg[port].num_bytes_received; if (byte_num >= PD_MAX_EXTENDED_MSG_CHUNK_LEN) byte_num = PD_MAX_EXTENDED_MSG_CHUNK_LEN; /* Make sure extended message buffer does not overflow */ if (pdmsg[port].num_bytes_received + byte_num > EXTENDED_BUFFER_SIZE) { rch[port].error = ERR_RCH_CHUNKED; set_state_rch(port, RCH_REPORT_ERROR); return; } /* Append data */ /* Add 2 to chk_buf to skip over extended message header */ memcpy(((uint8_t *)rx_emsg[port].buf + pdmsg[port].num_bytes_received), (uint8_t *)pdmsg[port].rx_chk_buf + 2, byte_num); /* increment chunk number expected */ pdmsg[port].chunk_number_expected++; /* adjust num bytes received */ pdmsg[port].num_bytes_received += byte_num; /* Was that the last chunk? */ if (pdmsg[port].num_bytes_received >= data_size) { rx_emsg[port].len = pdmsg[port].num_bytes_received; /* Pass Message to Policy Engine */ set_state_rch(port, RCH_PASS_UP_MESSAGE); } /* * Message not Complete */ else set_state_rch(port, RCH_REQUESTING_CHUNK); } /* * Unexpected Chunk Number */ else { rch[port].error = ERR_RCH_CHUNKED; set_state_rch(port, RCH_REPORT_ERROR); } } /* * RchRequestingChunk */ static void rch_requesting_chunk_entry(const int port) { print_current_rch_state(port); /* * Send Chunk Request to Protocol Layer * with chunk number = Chunk_Number_Expected */ pdmsg[port].tx_chk_buf[0] = PD_EXT_HEADER( pdmsg[port].chunk_number_expected, 1, /* Request Chunk */ 0 /* Data Size */ ); pdmsg[port].data_objs = 1; pdmsg[port].ext = 1; pdmsg[port].xmit_type = prl_rx[port].sop; PRL_TX_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_TX); } static void rch_requesting_chunk_run(const int port) { /* * Message Transmitted received from Protocol Layer */ if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_COMPLETE)) { PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_COMPLETE); set_state_rch(port, RCH_WAITING_CHUNK); } else if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_ERROR)) { /* Transmission Error from Protocol Layer detetected */ rch[port].error = ERR_RCH_CHUNKED; set_state_rch(port, RCH_REPORT_ERROR); } else if (RCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { /* * It is possible to have both message received and the chunk * request transmit complete before a full PRL SM run. But, the * PRL_RX state machine runs prior to RCH, but before PRL_TX, so * PRL_FLAGS_MSG_RECEIVED can be set without * PRL_FLAGS_TX_COMPLETE set at this point (though it will be * set as soon as PRL_TX is executed next. */ set_state_rch(port, RCH_WAITING_CHUNK); } } /* * RchWaitingChunk */ static void rch_waiting_chunk_entry(const int port) { print_current_rch_state(port); /* * Start ChunkSenderResponseTimer */ pd_timer_enable(port, PR_TIMER_CHUNK_SENDER_RESPONSE, PD_T_CHUNK_SENDER_RESPONSE); } static void rch_waiting_chunk_run(const int port) { if (RCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { /* * Because of the 5 msec tick time, it is possible to have both * msg_received and tx_complete flags set for a given PRL sm * run. Since prl_rx runs prior to the tx state machines, clear * the tx_complete flag as the next chunk has already been * received. */ if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_COMPLETE)) PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_COMPLETE); /* * Leave PRL_FLAGS_MSG_RECEIVED flag set just in case an error * is detected. If an error is detected, PRL_FLAGS_MSG_RECEIVED * will be cleared in rch_report_error state. */ if (PD_HEADER_EXT(rx_emsg[port].header)) { uint16_t exhdr = GET_EXT_HEADER(pdmsg[port].rx_chk_buf[0]); /* * Other Message Received from Protocol Layer */ if (PD_EXT_HEADER_REQ_CHUNK(exhdr) || !PD_EXT_HEADER_CHUNKED(exhdr)) { rch[port].error = ERR_RCH_CHUNKED; set_state_rch(port, RCH_REPORT_ERROR); } /* * Chunk response Received from Protocol Layer */ else { /* * No error was detected, so clear * PRL_FLAGS_MSG_RECEIVED flag. */ RCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); set_state_rch(port, RCH_PROCESSING_EXTENDED_MESSAGE); } } } /* * ChunkSenderResponseTimer Timeout */ else if (pd_timer_is_expired(port, PR_TIMER_CHUNK_SENDER_RESPONSE)) { rch[port].error = ERR_RCH_CHUNK_WAIT_TIMEOUT; set_state_rch(port, RCH_REPORT_ERROR); } } static void rch_waiting_chunk_exit(int port) { pd_timer_disable(port, PR_TIMER_CHUNK_SENDER_RESPONSE); } /* * RchReportError */ static void rch_report_error_entry(const int port) { print_current_rch_state(port); /* * If the state was entered because a message was received, * this message is passed to the Policy Engine. */ if (RCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { RCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); /* Copy chunk to extended buffer */ copy_chunk_to_ext(port); /* Pass Message to Policy Engine */ pe_message_received(port); /* Report error */ pe_report_error(port, ERR_RCH_MSG_REC, prl_rx[port].sop); } else { pe_report_error(port, rch[port].error, prl_rx[port].sop); } } static void rch_report_error_run(const int port) { set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); } /* * Chunked Tx State Machine */ /* * TchWaitForMessageRequestFromPe */ static void tch_wait_for_message_request_from_pe_entry(const int port) { print_current_tch_state(port); /* Clear Abort flag */ PDMSG_CLR_FLAG(port, PRL_FLAGS_ABORT); /* All Messages are chunked */ tch[port].flags = PRL_FLAGS_CHUNKING; } static void tch_wait_for_message_request_from_pe_run(const int port) { /* * Any message received and not in state TCH_Wait_Chunk_Request */ if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); set_state_tch(port, TCH_MESSAGE_RECEIVED); } else if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { TCH_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); /* * Rx Chunking State != RCH_Wait_For_Message_From_Protocol_Layer * & Abort Supported * * Discard the Message */ if (rch_get_state(port) != RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER) { tch[port].error = ERR_TCH_XMIT; set_state_tch(port, TCH_REPORT_ERROR); } else { /* * Extended Message Request & Chunking */ if (pdmsg_xmit_type_is_rev30(port) && pdmsg[port].ext && TCH_CHK_FLAG(port, PRL_FLAGS_CHUNKING)) { /* * NOTE: TCH_Prepare_To_Send_Chunked_Message * embedded here. */ pdmsg[port].send_offset = 0; pdmsg[port].chunk_number_to_send = 0; set_state_tch(port, TCH_CONSTRUCT_CHUNKED_MESSAGE); } else /* * Non-Extended Message Request */ { /* NOTE: TCH_Pass_Down_Message embedded here */ prl_copy_msg_to_buffer(port); /* Pass Message to Protocol Layer */ PRL_TX_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); set_state_tch(port, TCH_WAIT_FOR_TRANSMISSION_COMPLETE); } } } } /* * TchWaitForTransmissionComplete */ static void tch_wait_for_transmission_complete_entry(const int port) { print_current_tch_state(port); } static void tch_wait_for_transmission_complete_run(const int port) { /* * Inform Policy Engine that Message was sent. */ if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_COMPLETE)) { PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_COMPLETE); set_state_tch(port, TCH_MESSAGE_SENT); return; } /* * Inform Policy Engine of Tx Error */ else if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_ERROR)) { PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_ERROR); tch[port].error = ERR_TCH_XMIT; set_state_tch(port, TCH_REPORT_ERROR); return; } /* * A message was received while TCH is waiting for the phy to complete * sending a tx message. * * Because of our prl_sm architecture and I2C access delays for TCPCs, * it's possible to have a message received and the prl_tx state not be * in its default waiting state. To avoid a false protocol error, only * jump to TCH_MESSAGE_RECEIVED if the phy layer has not indicated that * the tx message was sent successfully. */ if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED) && prl_tx[port].xmit_status != TCPC_TX_COMPLETE_SUCCESS) { TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); set_state_tch(port, TCH_MESSAGE_RECEIVED); return; } } /* * TchConstructChunkedMessage */ static void tch_construct_chunked_message_entry(const int port) { uint16_t *ext_hdr; uint8_t *data; uint16_t num; print_current_tch_state(port); /* * Any message received and not in state TCH_Wait_Chunk_Request */ if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); set_state_tch(port, TCH_MESSAGE_RECEIVED); return; } /* Prepare to copy chunk into chk_buf */ ext_hdr = (uint16_t *)pdmsg[port].tx_chk_buf; data = ((uint8_t *)pdmsg[port].tx_chk_buf + 2); num = tx_emsg[port].len - pdmsg[port].send_offset; if (num > PD_MAX_EXTENDED_MSG_CHUNK_LEN) num = PD_MAX_EXTENDED_MSG_CHUNK_LEN; /* Set the chunks extended header */ *ext_hdr = PD_EXT_HEADER(pdmsg[port].chunk_number_to_send, 0, /* Chunk Request */ tx_emsg[port].len); /* Copy the message chunk into chk_buf */ memset(data, 0, 28); memcpy(data, tx_emsg[port].buf + pdmsg[port].send_offset, num); pdmsg[port].send_offset += num; /* * Add in 2 bytes for extended header * pad out to 4-byte boundary * convert to number of 4-byte words * Since the value is shifted right by 2, * no need to explicitly clear the lower * 2-bits. */ pdmsg[port].data_objs = (num + 2 + 3) >> 2; /* Pass message chunk to Protocol Layer */ PRL_TX_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); } static void tch_construct_chunked_message_run(const int port) { if (PDMSG_CHK_FLAG(port, PRL_FLAGS_ABORT)) set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); else set_state_tch(port, TCH_SENDING_CHUNKED_MESSAGE); } /* * TchSendingChunkedMessage */ static void tch_sending_chunked_message_entry(const int port) { print_current_tch_state(port); } static void tch_sending_chunked_message_run(const int port) { /* * Transmission Error */ if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_ERROR)) { tch[port].error = ERR_TCH_XMIT; set_state_tch(port, TCH_REPORT_ERROR); } /* * Message Transmitted from Protocol Layer & * Last Chunk */ else if (tx_emsg[port].len == pdmsg[port].send_offset) set_state_tch(port, TCH_MESSAGE_SENT); /* * Any message received and not in state TCH_Wait_Chunk_Request */ else if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); set_state_tch(port, TCH_MESSAGE_RECEIVED); } /* * Message Transmitted from Protocol Layer & * Not Last Chunk */ else set_state_tch(port, TCH_WAIT_CHUNK_REQUEST); } /* * TchWaitChunkRequest */ static void tch_wait_chunk_request_entry(const int port) { print_current_tch_state(port); /* Increment Chunk Number to Send */ pdmsg[port].chunk_number_to_send++; /* Start Chunk Sender Request Timer */ pd_timer_enable(port, PR_TIMER_CHUNK_SENDER_REQUEST, PD_T_CHUNK_SENDER_REQUEST); } static void tch_wait_chunk_request_run(const int port) { if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); if (PD_HEADER_EXT(rx_emsg[port].header)) { uint16_t exthdr; exthdr = GET_EXT_HEADER(pdmsg[port].rx_chk_buf[0]); if (PD_EXT_HEADER_REQ_CHUNK(exthdr)) { /* * Chunk Request Received & * Chunk Number = Chunk Number to Send */ if (PD_EXT_HEADER_CHUNK_NUM(exthdr) == pdmsg[port].chunk_number_to_send) { set_state_tch(port, TCH_CONSTRUCT_CHUNKED_MESSAGE); } /* * Chunk Request Received & * Chunk Number != Chunk Number to Send */ else { tch[port].error = ERR_TCH_CHUNKED; set_state_tch(port, TCH_REPORT_ERROR); } return; } } /* * Other message received */ set_state_tch(port, TCH_MESSAGE_RECEIVED); } /* * ChunkSenderRequestTimer timeout */ else if (pd_timer_is_expired(port, PR_TIMER_CHUNK_SENDER_REQUEST)) set_state_tch(port, TCH_MESSAGE_SENT); } static void tch_wait_chunk_request_exit(int port) { pd_timer_disable(port, PR_TIMER_CHUNK_SENDER_REQUEST); } /* * TchMessageReceived */ static void tch_message_received_entry(const int port) { print_current_tch_state(port); /* Pass message to chunked Rx */ RCH_SET_FLAG(port, PRL_FLAGS_MSG_RECEIVED); /* Clear extended message objects */ if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { TCH_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); pe_report_discard(port); } pdmsg[port].data_objs = 0; } static void tch_message_received_run(const int port) { set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); } /* * TchMessageSent */ static void tch_message_sent_entry(const int port) { print_current_tch_state(port); /* Tell PE message was sent */ pe_message_sent(port); /* * Any message received and not in state TCH_Wait_Chunk_Request * MUST be checked after notifying PE */ if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); set_state_tch(port, TCH_MESSAGE_RECEIVED); return; } set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); } /* * TchReportError */ static void tch_report_error_entry(const int port) { print_current_tch_state(port); /* Report Error To Policy Engine */ pe_report_error(port, tch[port].error, prl_tx[port].last_xmit_type); /* * Any message received and not in state TCH_Wait_Chunk_Request * MUST be checked after notifying PE */ if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); set_state_tch(port, TCH_MESSAGE_RECEIVED); return; } set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); } #endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ /* * Protocol Layer Message Reception State Machine */ static void prl_rx_wait_for_phy_message(const int port, int evt) { uint32_t header; uint8_t type; uint8_t cnt; int8_t msid; /* * If PD3, wait for the RX chunk SM to copy the pdmsg into the extended * buffer before overwriting pdmsg. */ if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES) && RCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) return; /* If we don't have any message, just stop processing now. */ if (!tcpm_has_pending_message(port) || tcpm_dequeue_message(port, pdmsg[port].rx_chk_buf, &header)) return; rx_emsg[port].header = header; type = PD_HEADER_TYPE(header); cnt = PD_HEADER_CNT(header); msid = PD_HEADER_ID(header); prl_rx[port].sop = PD_HEADER_GET_SOP(header); /* Make sure an incorrect count doesn't overflow the chunk buffer */ if (cnt > CHK_BUF_SIZE) cnt = CHK_BUF_SIZE; /* dump received packet content (only dump ping at debug level MAX) */ if ((prl_debug_level >= DEBUG_LEVEL_2 && type != PD_CTRL_PING) || prl_debug_level >= DEBUG_LEVEL_3) { int p; ccprintf("C%d: RECV %04x/%d ", port, header, cnt); for (p = 0; p < cnt; p++) ccprintf("[%d]%08x ", p, pdmsg[port].rx_chk_buf[p]); ccprintf("\n"); } /* * Ignore messages sent to the cable from our * port partner if we aren't Vconn powered device. */ if (!IS_ENABLED(CONFIG_USB_CTVPD) && !IS_ENABLED(CONFIG_USB_VPD) && PD_HEADER_GET_SOP(header) != TCPCI_MSG_SOP && PD_HEADER_PROLE(header) == PD_PLUG_FROM_DFP_UFP) return; /* Handle incoming soft reset as special case */ if (cnt == 0 && type == PD_CTRL_SOFT_RESET) { /* Clear MessageIdCounter */ prl_tx[port].msg_id_counter[prl_rx[port].sop] = 0; /* Clear stored MessageID value */ prl_rx[port].msg_id[prl_rx[port].sop] = -1; /* Soft Reset occurred */ set_state_prl_tx(port, PRL_TX_PHY_LAYER_RESET); if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); } /* * Inform Policy Engine of Soft Reset. Note perform this after * performing the protocol layer reset, otherwise we will lose * the PE's outgoing ACCEPT message to the soft reset. */ pe_got_soft_reset(port); return; } /* * Ignore if this is a duplicate message. Stop processing. */ if (prl_rx[port].msg_id[prl_rx[port].sop] == msid) return; /* * Discard any pending tx message if this is * not a ping message (length must be checked to verify this is a * control message, rather than data) */ if ((cnt > 0) || (type != PD_CTRL_PING)) { /* * Note: Spec dictates that we always go into * PRL_Tx_Discard_Message upon receivng a message. However, due * to our TCPC architecture we may be receiving a transmit * complete at the same time as a response so only do this if a * message is pending. */ if (prl_tx[port].xmit_status != TCPC_TX_COMPLETE_SUCCESS || PRL_TX_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) set_state_prl_tx(port, PRL_TX_DISCARD_MESSAGE); } /* Store Message Id */ prl_rx[port].msg_id[prl_rx[port].sop] = msid; if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { /* RTR Chunked Message Router States. */ /* * Received Ping from Protocol Layer */ if (cnt == 0 && type == PD_CTRL_PING) { /* NOTE: RTR_PING State embedded here. */ rx_emsg[port].len = 0; pe_message_received(port); return; } /* * Message (not Ping) Received from * Protocol Layer & Doing Tx Chunks * * Also, handle the case where a message has been * queued for sending but a message is received before * tch_wait_for_message_request_from_pe has been run */ else if (tch_get_state(port) != TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE || TCH_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { /* NOTE: RTR_TX_CHUNKS State embedded here. */ /* * Send Message to Tx Chunk * Chunk State Machine */ TCH_SET_FLAG(port, PRL_FLAGS_MSG_RECEIVED); } /* * Message (not Ping) Received from * Protocol Layer & Not Doing Tx Chunks */ else { /* NOTE: RTR_RX_CHUNKS State embedded here. */ /* * Send Message to Rx * Chunk State Machine */ RCH_SET_FLAG(port, PRL_FLAGS_MSG_RECEIVED); } } else { /* Copy chunk to extended buffer */ copy_chunk_to_ext(port); /* Send message to Policy Engine */ pe_message_received(port); } task_wake(PD_PORT_TO_TASK_ID(port)); } /* All necessary Protocol Transmit States (Section 6.11.2.2) */ static __const_data const struct usb_state prl_tx_states[] = { [PRL_TX_PHY_LAYER_RESET] = { .entry = prl_tx_phy_layer_reset_entry, }, [PRL_TX_WAIT_FOR_MESSAGE_REQUEST] = { .entry = prl_tx_wait_for_message_request_entry, .run = prl_tx_wait_for_message_request_run, }, [PRL_TX_LAYER_RESET_FOR_TRANSMIT] = { .entry = prl_tx_layer_reset_for_transmit_entry, .run = prl_tx_layer_reset_for_transmit_run, }, [PRL_TX_WAIT_FOR_PHY_RESPONSE] = { .entry = prl_tx_wait_for_phy_response_entry, .run = prl_tx_wait_for_phy_response_run, .exit = prl_tx_wait_for_phy_response_exit, }, #ifdef CONFIG_USB_PD_REV30 [PRL_TX_SRC_SOURCE_TX] = { .entry = prl_tx_src_source_tx_entry, .run = prl_tx_src_source_tx_run, }, [PRL_TX_SNK_START_AMS] = { .entry = prl_tx_snk_start_ams_entry, .run = prl_tx_snk_start_ams_run, }, #endif /* CONFIG_USB_PD_REV30 */ [PRL_TX_SRC_PENDING] = { .entry = prl_tx_src_pending_entry, .run = prl_tx_src_pending_run, .exit = prl_tx_src_pending_exit, }, [PRL_TX_SNK_PENDING] = { .entry = prl_tx_snk_pending_entry, .run = prl_tx_snk_pending_run, }, [PRL_TX_DISCARD_MESSAGE] = { .entry = prl_tx_discard_message_entry, }, }; /* All necessary Protocol Hard Reset States (Section 6.11.2.4) */ static __const_data const struct usb_state prl_hr_states[] = { [PRL_HR_WAIT_FOR_REQUEST] = { .entry = prl_hr_wait_for_request_entry, .run = prl_hr_wait_for_request_run, }, [PRL_HR_RESET_LAYER] = { .entry = prl_hr_reset_layer_entry, .run = prl_hr_reset_layer_run, }, [PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE] = { .entry = prl_hr_wait_for_phy_hard_reset_complete_entry, .run = prl_hr_wait_for_phy_hard_reset_complete_run, .exit = prl_hr_wait_for_phy_hard_reset_complete_exit, }, [PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE] = { .entry = prl_hr_wait_for_pe_hard_reset_complete_entry, .run = prl_hr_wait_for_pe_hard_reset_complete_run, .exit = prl_hr_wait_for_pe_hard_reset_complete_exit, }, }; /* All necessary Chunked Rx states (Section 6.11.2.1.2) */ __maybe_unused static const struct usb_state rch_states[] = { #ifdef CONFIG_USB_PD_EXTENDED_MESSAGES [RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER] = { .entry = rch_wait_for_message_from_protocol_layer_entry, .run = rch_wait_for_message_from_protocol_layer_run, }, [RCH_PASS_UP_MESSAGE] = { .entry = rch_pass_up_message_entry, }, [RCH_PROCESSING_EXTENDED_MESSAGE] = { .entry = rch_processing_extended_message_entry, .run = rch_processing_extended_message_run, }, [RCH_REQUESTING_CHUNK] = { .entry = rch_requesting_chunk_entry, .run = rch_requesting_chunk_run, }, [RCH_WAITING_CHUNK] = { .entry = rch_waiting_chunk_entry, .run = rch_waiting_chunk_run, .exit = rch_waiting_chunk_exit, }, [RCH_REPORT_ERROR] = { .entry = rch_report_error_entry, .run = rch_report_error_run, }, #endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ }; /* All necessary Chunked Tx states (Section 6.11.2.1.3) */ __maybe_unused static const struct usb_state tch_states[] = { #ifdef CONFIG_USB_PD_EXTENDED_MESSAGES [TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE] = { .entry = tch_wait_for_message_request_from_pe_entry, .run = tch_wait_for_message_request_from_pe_run, }, [TCH_WAIT_FOR_TRANSMISSION_COMPLETE] = { .entry = tch_wait_for_transmission_complete_entry, .run = tch_wait_for_transmission_complete_run, }, [TCH_CONSTRUCT_CHUNKED_MESSAGE] = { .entry = tch_construct_chunked_message_entry, .run = tch_construct_chunked_message_run, }, [TCH_SENDING_CHUNKED_MESSAGE] = { .entry = tch_sending_chunked_message_entry, .run = tch_sending_chunked_message_run, }, [TCH_WAIT_CHUNK_REQUEST] = { .entry = tch_wait_chunk_request_entry, .run = tch_wait_chunk_request_run, .exit = tch_wait_chunk_request_exit, }, [TCH_MESSAGE_RECEIVED] = { .entry = tch_message_received_entry, .run = tch_message_received_run, }, [TCH_MESSAGE_SENT] = { .entry = tch_message_sent_entry, }, [TCH_REPORT_ERROR] = { .entry = tch_report_error_entry, }, #endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ }; #ifdef TEST_BUILD const struct test_sm_data test_prl_sm_data[] = { { .base = prl_tx_states, .size = ARRAY_SIZE(prl_tx_states), .names = prl_tx_state_names, .names_size = ARRAY_SIZE(prl_tx_state_names), }, { .base = prl_hr_states, .size = ARRAY_SIZE(prl_hr_states), .names = prl_hr_state_names, .names_size = ARRAY_SIZE(prl_hr_state_names), }, #ifdef CONFIG_USB_PD_EXTENDED_MESSAGES { .base = rch_states, .size = ARRAY_SIZE(rch_states), .names = rch_state_names, .names_size = ARRAY_SIZE(rch_state_names), }, { .base = tch_states, .size = ARRAY_SIZE(tch_states), .names = tch_state_names, .names_size = ARRAY_SIZE(tch_state_names), }, #endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ }; BUILD_ASSERT(ARRAY_SIZE(prl_tx_states) == ARRAY_SIZE(prl_tx_state_names)); BUILD_ASSERT(ARRAY_SIZE(prl_hr_states) == ARRAY_SIZE(prl_hr_state_names)); #ifdef CONFIG_USB_PD_EXTENDED_MESSAGES BUILD_ASSERT(ARRAY_SIZE(rch_states) == ARRAY_SIZE(rch_state_names)); BUILD_ASSERT(ARRAY_SIZE(tch_states) == ARRAY_SIZE(tch_state_names)); #endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ const int test_prl_sm_data_size = ARRAY_SIZE(test_prl_sm_data); #endif