| /* |
| * Copyright (c) 2022 The Chromium OS Authors |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/smf.h> |
| #include <zephyr/usb_c/usbc.h> |
| #include <zephyr/drivers/usb_c/usbc_pd.h> |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_DECLARE(usbc_stack, CONFIG_USBC_STACK_LOG_LEVEL); |
| |
| #include "usbc_stack.h" |
| |
| /** |
| * @file |
| * @brief USB Power Delivery Protocol Layer (PRL) |
| * |
| * The PRL implementation in this file is base on |
| * Specification Revision 3.1, Version 1.3 |
| */ |
| |
| /** |
| * @brief Protocol Layer Flags |
| * |
| * @note: These flags are used in multiple state machines and could have |
| * different meanings in each state machine. |
| */ |
| enum prl_flags { |
| /** Flag to note message transmission completed */ |
| PRL_FLAGS_TX_COMPLETE = 0, |
| /** Flag to note message was discarded */ |
| PRL_FLAGS_TX_DISCARDED = 1, |
| /** Flag to note PRL waited for SINK_OK CC state before transmitting */ |
| PRL_FLAGS_WAIT_SINK_OK = 2, |
| /** Flag to note transmission error occurred */ |
| PRL_FLAGS_TX_ERROR = 3, |
| /** Flag to note PE triggered a hard reset */ |
| PRL_FLAGS_PE_HARD_RESET = 4, |
| /** Flag to note hard reset has completed */ |
| PRL_FLAGS_HARD_RESET_COMPLETE = 5, |
| /** Flag to note port partner sent a hard reset */ |
| PRL_FLAGS_PORT_PARTNER_HARD_RESET = 6, |
| /** |
| * Flag to note a message transmission has been requested. It is only |
| * cleared when the message is sent to the TCPC layer. |
| */ |
| PRL_FLAGS_MSG_XMIT = 7, |
| /** Flag to track if first message in AMS is pending */ |
| PRL_FLAGS_FIRST_MSG_PENDING = 8, |
| /* Flag to note that PRL requested to set SINK_NG CC state */ |
| PRL_FLAGS_SINK_NG = 9, |
| }; |
| |
| /** |
| * @brief Protocol Layer Transmission States |
| */ |
| enum usbc_prl_tx_state_t { |
| /** 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_Snk_Start_of_AMS */ |
| PRL_TX_SNK_START_AMS, |
| /** PRL_Tx_Snk_Pending */ |
| PRL_TX_SNK_PENDING, |
| /** PRL_Tx_Discard_Message */ |
| PRL_TX_DISCARD_MESSAGE, |
| /** PRL_TX_SRC_Source_Tx */ |
| PRL_TX_SRC_SOURCE_TX, |
| /** PRL_TX_SRC_Pending */ |
| PRL_TX_SRC_PENDING, |
| |
| /** PRL_Tx_Suspend. Not part of the PD specification. */ |
| PRL_TX_SUSPEND, |
| |
| /** Number of PRL_TX States */ |
| PRL_TX_STATE_COUNT |
| }; |
| |
| /** |
| * @brief Protocol Layer Hard Reset States |
| */ |
| enum usbc_prl_hr_state_t { |
| /** 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, |
| |
| /** PRL_Hr_Suspend. Not part of the PD specification. */ |
| PRL_HR_SUSPEND, |
| |
| /** Number of PRL_HR States */ |
| PRL_HR_STATE_COUNT |
| }; |
| |
| static const struct smf_state prl_tx_states[PRL_TX_STATE_COUNT]; |
| static const struct smf_state prl_hr_states[PRL_HR_STATE_COUNT]; |
| |
| static void prl_tx_construct_message(const struct device *dev); |
| static void prl_rx_wait_for_phy_message(const struct device *dev); |
| static void prl_hr_set_state(const struct device *dev, const enum usbc_prl_hr_state_t state); |
| static void prl_tx_set_state(const struct device *dev, const enum usbc_prl_tx_state_t state); |
| static void prl_init(const struct device *dev); |
| static enum usbc_prl_hr_state_t prl_hr_get_state(const struct device *dev); |
| |
| /** |
| * @brief Initializes the TX an HR state machines and enters the |
| * PRL_TX_SUSPEND and PRL_TX_SUSPEND states respectively. |
| */ |
| void prl_subsys_init(const struct device *dev) |
| { |
| struct usbc_port_data *data = dev->data; |
| struct protocol_layer_tx_t *prl_tx = data->prl_tx; |
| struct protocol_hard_reset_t *prl_hr = data->prl_hr; |
| |
| /* Save the port device objects so states can access it */ |
| prl_tx->dev = dev; |
| prl_hr->dev = dev; |
| |
| /* Initialize the state machines */ |
| smf_set_initial(SMF_CTX(prl_hr), &prl_hr_states[PRL_HR_SUSPEND]); |
| smf_set_initial(SMF_CTX(prl_tx), &prl_tx_states[PRL_TX_SUSPEND]); |
| } |
| |
| /** |
| * @brief Test if the Protocol Layer State Machines are running |
| * |
| * @retval TRUE if the state machines are running |
| * @retval FALSE if the state machines are paused |
| */ |
| bool prl_is_running(const struct device *dev) |
| { |
| struct usbc_port_data *data = dev->data; |
| |
| return data->prl_sm_state == SM_RUN; |
| } |
| |
| /** |
| * @brief Directs the Protocol Layer to perform a hard reset. This function |
| * is called from the Policy Engine. |
| */ |
| void prl_execute_hard_reset(const struct device *dev) |
| { |
| struct usbc_port_data *data = dev->data; |
| struct protocol_hard_reset_t *prl_hr = data->prl_hr; |
| |
| /* Only allow async. function calls when state machine is running */ |
| if (prl_is_running(dev) == false) { |
| return; |
| } |
| |
| atomic_set_bit(&prl_hr->flags, PRL_FLAGS_PE_HARD_RESET); |
| prl_hr_set_state(dev, PRL_HR_RESET_LAYER); |
| } |
| |
| /** |
| * @brief Instructs the Protocol Layer that a hard reset is complete. |
| * This function is called from the Policy Engine. |
| */ |
| void prl_hard_reset_complete(const struct device *dev) |
| { |
| struct usbc_port_data *data = dev->data; |
| struct protocol_hard_reset_t *prl_hr = data->prl_hr; |
| |
| atomic_set_bit(&prl_hr->flags, PRL_FLAGS_HARD_RESET_COMPLETE); |
| } |
| |
| /** |
| * @brief Directs the Protocol Layer to construct and transmit a Power Delivery |
| * Control message. |
| */ |
| void prl_send_ctrl_msg(const struct device *dev, const enum pd_packet_type type, |
| const enum pd_ctrl_msg_type msg) |
| { |
| struct usbc_port_data *data = dev->data; |
| struct protocol_layer_tx_t *prl_tx = data->prl_tx; |
| |
| /* set packet type */ |
| prl_tx->emsg.type = type; |
| /* set message type */ |
| prl_tx->msg_type = msg; |
| /* control message. set data len to zero */ |
| prl_tx->emsg.len = 0; |
| |
| atomic_set_bit(&prl_tx->flags, PRL_FLAGS_MSG_XMIT); |
| } |
| |
| /** |
| * @brief Directs the Protocol Layer to construct and transmit a Power Delivery |
| * Data message. |
| * |
| * @note: Before calling this function prl_tx->emsg.data and prl_tx->emsg.len |
| * must be set. |
| */ |
| void prl_send_data_msg(const struct device *dev, const enum pd_packet_type type, |
| const enum pd_data_msg_type msg) |
| { |
| struct usbc_port_data *data = dev->data; |
| struct protocol_layer_tx_t *prl_tx = data->prl_tx; |
| |
| /* set packet type */ |
| prl_tx->emsg.type = type; |
| /* set message type */ |
| prl_tx->msg_type = msg; |
| |
| atomic_set_bit(&prl_tx->flags, PRL_FLAGS_MSG_XMIT); |
| } |
| |
| /** |
| * @brief Directs the Protocol Layer to reset the revision of each packet type |
| * to its default value. |
| */ |
| void prl_set_default_pd_revision(const struct device *dev) |
| { |
| struct usbc_port_data *data = dev->data; |
| |
| /* |
| * 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. |
| */ |
| data->rev[PD_PACKET_SOP] = PD_REV30; |
| data->rev[PD_PACKET_SOP_PRIME] = PD_REV30; |
| data->rev[PD_PACKET_PRIME_PRIME] = PD_REV30; |
| data->rev[PD_PACKET_DEBUG_PRIME] = PD_REV30; |
| data->rev[PD_PACKET_DEBUG_PRIME_PRIME] = PD_REV30; |
| } |
| |
| /** |
| * @brief Start the Protocol Layer state machines |
| */ |
| void prl_start(const struct device *dev) |
| { |
| struct usbc_port_data *data = dev->data; |
| |
| data->prl_enabled = true; |
| } |
| |
| /** |
| * @brief Pause the Protocol Layer state machines |
| */ |
| void prl_suspend(const struct device *dev) |
| { |
| struct usbc_port_data *data = dev->data; |
| |
| data->prl_enabled = false; |
| |
| /* |
| * While we are paused, exit all states |
| * and wait until initialized again. |
| */ |
| prl_tx_set_state(dev, PRL_TX_SUSPEND); |
| prl_hr_set_state(dev, PRL_HR_SUSPEND); |
| } |
| |
| /** |
| * @brief Reset the Protocol Layer state machines |
| */ |
| void prl_reset(const struct device *dev) |
| { |
| struct usbc_port_data *data = dev->data; |
| |
| if (data->prl_enabled) { |
| data->prl_sm_state = SM_INIT; |
| } |
| } |
| |
| /** |
| * @brief Inform the PRL that the first message in an AMS is being sent |
| */ |
| void prl_first_msg_notificaiton(const struct device *dev) |
| { |
| struct usbc_port_data *data = dev->data; |
| struct protocol_layer_tx_t *prl_tx = data->prl_tx; |
| |
| atomic_set_bit(&prl_tx->flags, PRL_FLAGS_FIRST_MSG_PENDING); |
| } |
| |
| /** |
| * @brief Run the Protocol Layer state machines |
| */ |
| void prl_run(const struct device *dev) |
| { |
| struct usbc_port_data *data = dev->data; |
| struct protocol_layer_tx_t *prl_tx = data->prl_tx; |
| struct protocol_hard_reset_t *prl_hr = data->prl_hr; |
| |
| switch (data->prl_sm_state) { |
| case SM_PAUSED: |
| if (data->prl_enabled == false) { |
| break; |
| } |
| /* fall through */ |
| case SM_INIT: |
| prl_init(dev); |
| data->prl_sm_state = SM_RUN; |
| /* fall through */ |
| case SM_RUN: |
| if (data->prl_enabled == false) { |
| data->prl_sm_state = SM_PAUSED; |
| /* Disable RX */ |
| tcpc_set_rx_enable(data->tcpc, false); |
| break; |
| } |
| |
| /* Run Protocol Layer Hard Reset state machine */ |
| smf_run_state(SMF_CTX(prl_hr)); |
| |
| /* |
| * During Hard Reset no USB Power Delivery Protocol Messages |
| * are sent or received; only Hard Reset Signaling is present |
| * after which the communication channel is assumed to have |
| * been disabled by the Physical Layer until completion of |
| * the Hard Reset. |
| */ |
| if (prl_hr_get_state(dev) == PRL_HR_WAIT_FOR_REQUEST) { |
| /* Run Protocol Layer Message Reception */ |
| prl_rx_wait_for_phy_message(dev); |
| |
| /* Run Protocol Layer Message Tx state machine */ |
| smf_run_state(SMF_CTX(prl_tx)); |
| } |
| break; |
| } |
| } |
| |
| /** |
| * @brief Set revision for the give packet type. This function is called |
| * from the Policy Engine. |
| */ |
| void prl_set_rev(const struct device *dev, const enum pd_packet_type type, |
| const enum pd_rev_type rev) |
| { |
| struct usbc_port_data *data = dev->data; |
| |
| data->rev[type] = rev; |
| } |
| |
| /** |
| * @brief Get the revision for the give packet type. |
| * This function is called from the Policy Engine. |
| */ |
| enum pd_rev_type prl_get_rev(const struct device *dev, const enum pd_packet_type type) |
| { |
| struct usbc_port_data *data = dev->data; |
| |
| return data->rev[type]; |
| } |
| |
| /** Private Protocol Layer API below */ |
| |
| /** |
| * @brief Alert Handler called by the TCPC driver |
| */ |
| static void alert_handler(const struct device *tcpc, void *port_dev, enum tcpc_alert alert) |
| { |
| const struct device *dev = (const struct device *)port_dev; |
| struct usbc_port_data *data = dev->data; |
| struct protocol_layer_tx_t *prl_tx = data->prl_tx; |
| struct protocol_hard_reset_t *prl_hr = data->prl_hr; |
| |
| switch (alert) { |
| case TCPC_ALERT_HARD_RESET_RECEIVED: |
| atomic_set_bit(&prl_hr->flags, PRL_FLAGS_PORT_PARTNER_HARD_RESET); |
| break; |
| case TCPC_ALERT_TRANSMIT_MSG_FAILED: |
| atomic_set_bit(&prl_tx->flags, PRL_FLAGS_TX_ERROR); |
| break; |
| case TCPC_ALERT_TRANSMIT_MSG_DISCARDED: |
| atomic_set_bit(&prl_tx->flags, PRL_FLAGS_TX_DISCARDED); |
| break; |
| case TCPC_ALERT_TRANSMIT_MSG_SUCCESS: |
| atomic_set_bit(&prl_tx->flags, PRL_FLAGS_TX_COMPLETE); |
| break; |
| /* These alerts are ignored and will just wake the thread. */ |
| default: |
| break; |
| } |
| |
| /* Wake the thread if it's sleeping */ |
| k_wakeup(data->port_thread); |
| } |
| |
| /** |
| * @brief Set the Protocol Layer Message Transmission state |
| */ |
| static void prl_tx_set_state(const struct device *dev, const enum usbc_prl_tx_state_t state) |
| { |
| struct usbc_port_data *data = dev->data; |
| struct protocol_layer_tx_t *prl_tx = data->prl_tx; |
| |
| __ASSERT(state < ARRAY_SIZE(prl_tx_states), "invalid prl_tx_state %d", state); |
| smf_set_state(SMF_CTX(prl_tx), &prl_tx_states[state]); |
| } |
| |
| /** |
| * @brief Set the Protocol Layer Hard Reset state |
| */ |
| static void prl_hr_set_state(const struct device *dev, const enum usbc_prl_hr_state_t state) |
| { |
| struct usbc_port_data *data = dev->data; |
| struct protocol_hard_reset_t *prl_hr = data->prl_hr; |
| |
| __ASSERT(state < ARRAY_SIZE(prl_hr_states), "invalid prl_hr_state %d", state); |
| smf_set_state(SMF_CTX(prl_hr), &prl_hr_states[state]); |
| } |
| |
| /** |
| * @brief Get the Protocol Layer Hard Reset state |
| */ |
| static enum usbc_prl_hr_state_t prl_hr_get_state(const struct device *dev) |
| { |
| struct usbc_port_data *data = dev->data; |
| struct protocol_hard_reset_t *prl_hr = data->prl_hr; |
| |
| return prl_hr->ctx.current - &prl_hr_states[0]; |
| } |
| |
| /** |
| * @brief Increment the message ID counter for the last transmitted packet type |
| */ |
| static void increment_msgid_counter(const struct device *dev) |
| { |
| struct usbc_port_data *data = dev->data; |
| struct protocol_layer_tx_t *prl_tx = data->prl_tx; |
| |
| /* If the last message wasn't an SOP* message, no need to increment */ |
| if (prl_tx->last_xmit_type >= NUM_SOP_STAR_TYPES) { |
| return; |
| } |
| |
| prl_tx->msg_id_counter[prl_tx->last_xmit_type] = |
| (prl_tx->msg_id_counter[prl_tx->last_xmit_type] + 1) & PD_MESSAGE_ID_COUNT; |
| } |
| |
| /** |
| * @brief Get the SOP* header for the current received message |
| */ |
| static uint16_t get_sop_star_header(const struct device *dev) |
| { |
| struct usbc_port_data *data = dev->data; |
| struct protocol_layer_tx_t *prl_tx = data->prl_tx; |
| const bool is_sop_packet = prl_tx->emsg.type == PD_PACKET_SOP; |
| union pd_header header; |
| |
| /* SOP vs SOP'/SOP" headers are different. Replace fields as needed */ |
| header.message_type = prl_tx->msg_type; |
| header.port_data_role = is_sop_packet ? pe_get_data_role(dev) : 0; |
| header.specification_revision = data->rev[prl_tx->emsg.type]; |
| header.port_power_role = is_sop_packet ? pe_get_power_role(dev) : pe_get_cable_plug(dev); |
| header.message_id = prl_tx->msg_id_counter[prl_tx->emsg.type]; |
| header.number_of_data_objects = PD_CONVERT_BYTES_TO_PD_HEADER_COUNT(prl_tx->emsg.len); |
| header.extended = false; |
| |
| return header.raw_value; |
| } |
| |
| /** |
| * @brief Construct and transmit a message |
| */ |
| static void prl_tx_construct_message(const struct device *dev) |
| { |
| struct usbc_port_data *data = dev->data; |
| struct protocol_layer_tx_t *prl_tx = data->prl_tx; |
| const struct device *tcpc = data->tcpc; |
| |
| /* The header is unused for hard reset, etc. */ |
| prl_tx->emsg.header.raw_value = |
| prl_tx->emsg.type < NUM_SOP_STAR_TYPES ? get_sop_star_header(dev) : 0; |
| |
| /* Save SOP* so the correct msg_id_counter can be incremented */ |
| prl_tx->last_xmit_type = prl_tx->emsg.type; |
| |
| /* |
| * 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. |
| */ |
| atomic_clear_bit(&prl_tx->flags, PRL_FLAGS_TX_COMPLETE); |
| |
| /* Clear PRL_FLAGS_MSG_XMIT flag */ |
| atomic_clear_bit(&prl_tx->flags, PRL_FLAGS_MSG_XMIT); |
| |
| /* |
| * Pass message to PHY Layer. It handles retries in hardware as |
| * software cannot handle the required timing ~ 1ms (tReceive + tRetry) |
| */ |
| tcpc_transmit_data(tcpc, &prl_tx->emsg); |
| } |
| |
| /** |
| * @brief Transmit a Hard Reset Message |
| */ |
| static void prl_hr_send_msg_to_phy(const struct device *dev) |
| { |
| struct usbc_port_data *data = dev->data; |
| struct protocol_layer_tx_t *prl_tx = data->prl_tx; |
| const struct device *tcpc = data->tcpc; |
| |
| /* Header is not used for hard reset */ |
| prl_tx->emsg.header.raw_value = 0; |
| prl_tx->emsg.type = PD_PACKET_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. |
| */ |
| data->prl_tx->flags = ATOMIC_INIT(0); |
| |
| /* Pass message to PHY Layer */ |
| tcpc_transmit_data(tcpc, &prl_tx->emsg); |
| } |
| |
| /** |
| * @brief Initialize the Protocol Layer State Machines |
| */ |
| static void prl_init(const struct device *dev) |
| { |
| struct usbc_port_data *data = dev->data; |
| struct protocol_layer_rx_t *prl_rx = data->prl_rx; |
| struct protocol_layer_tx_t *prl_tx = data->prl_tx; |
| struct protocol_hard_reset_t *prl_hr = data->prl_hr; |
| int i; |
| |
| LOG_INF("PRL_INIT"); |
| |
| /* Set all packet types to default revision */ |
| prl_set_default_pd_revision(dev); |
| |
| /* |
| * Set TCPC alert handler so we are notified when messages |
| * are received, transmitted, etc. |
| */ |
| tcpc_set_alert_handler_cb(data->tcpc, alert_handler, (void *)dev); |
| |
| /* Initialize the PRL_HR state machine */ |
| prl_hr->flags = ATOMIC_INIT(0); |
| usbc_timer_init(&prl_hr->pd_t_hard_reset_complete, PD_T_HARD_RESET_COMPLETE_MAX_MS); |
| prl_hr_set_state(dev, PRL_HR_WAIT_FOR_REQUEST); |
| |
| /* Initialize the PRL_TX state machine */ |
| prl_tx->flags = ATOMIC_INIT(0); |
| prl_tx->last_xmit_type = PD_PACKET_SOP; |
| for (i = 0; i < NUM_SOP_STAR_TYPES; i++) { |
| prl_tx->msg_id_counter[i] = 0; |
| } |
| usbc_timer_init(&prl_tx->pd_t_tx_timeout, PD_T_TX_TIMEOUT_MS); |
| usbc_timer_init(&prl_tx->pd_t_sink_tx, PD_T_SINK_TX_MAX_MS); |
| prl_tx_set_state(dev, PRL_TX_PHY_LAYER_RESET); |
| |
| /* Initialize the PRL_RX state machine */ |
| prl_rx->flags = ATOMIC_INIT(0); |
| for (i = 0; i < NUM_SOP_STAR_TYPES; i++) { |
| prl_rx->msg_id[i] = -1; |
| } |
| } |
| |
| /** |
| * @brief PRL_Tx_PHY_Layer_Reset State |
| */ |
| static void prl_tx_phy_layer_reset_entry(void *obj) |
| { |
| struct protocol_layer_tx_t *prl_tx = (struct protocol_layer_tx_t *)obj; |
| const struct device *dev = prl_tx->dev; |
| struct usbc_port_data *data = dev->data; |
| const struct device *tcpc = data->tcpc; |
| |
| LOG_INF("PRL_Tx_PHY_Layer_Reset"); |
| |
| /* Enable communications */ |
| tcpc_set_rx_enable(tcpc, tc_is_in_attached_state(dev)); |
| |
| /* Reset complete */ |
| prl_tx_set_state(dev, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); |
| } |
| |
| /** |
| * @brief PRL_Tx_Wait_for_Message_Request Entry State |
| */ |
| static void prl_tx_wait_for_message_request_entry(void *obj) |
| { |
| struct protocol_layer_tx_t *prl_tx = (struct protocol_layer_tx_t *)obj; |
| |
| LOG_INF("PRL_Tx_Wait_for_Message_Request"); |
| |
| /* Clear outstanding messages */ |
| prl_tx->flags = ATOMIC_INIT(0); |
| } |
| |
| /** |
| * @brief PRL_Tx_Wait_for_Message_Request Run State |
| */ |
| static void prl_tx_wait_for_message_request_run(void *obj) |
| { |
| struct protocol_layer_tx_t *prl_tx = (struct protocol_layer_tx_t *)obj; |
| const struct device *dev = prl_tx->dev; |
| struct usbc_port_data *data = dev->data; |
| |
| /* Clear any AMS flags and state if we are no longer in an AMS */ |
| if (pe_dpm_initiated_ams(dev) == false) { |
| #ifdef CONFIG_USBC_CSM_SOURCE_ONLY |
| /* Note PRL_Tx_Src_Sink_Tx is embedded here. */ |
| if (atomic_test_and_clear_bit(&prl_tx->flags, PRL_FLAGS_SINK_NG)) { |
| tc_select_src_collision_rp(dev, SINK_TX_OK); |
| } |
| #endif |
| atomic_clear_bit(&prl_tx->flags, PRL_FLAGS_WAIT_SINK_OK); |
| } |
| |
| /* |
| * Check if we are starting an AMS and need to wait and/or set the CC |
| * lines appropriately. |
| */ |
| if (data->rev[PD_PACKET_SOP] == PD_REV30 && pe_dpm_initiated_ams(dev)) { |
| if (atomic_test_bit(&prl_tx->flags, PRL_FLAGS_WAIT_SINK_OK) || |
| atomic_test_bit(&prl_tx->flags, PRL_FLAGS_SINK_NG)) { |
| /* |
| * If we are already in an AMS then allow the |
| * multi-message AMS to continue. |
| * |
| * Fall Through using the current AMS |
| */ |
| } else { |
| /* |
| * Start of AMS notification received from |
| * Policy Engine |
| */ |
| if (IS_ENABLED(CONFIG_USBC_CSM_SOURCE_ONLY) && |
| pe_get_power_role(dev) == TC_ROLE_SOURCE) { |
| atomic_set_bit(&prl_tx->flags, PRL_FLAGS_SINK_NG); |
| prl_tx_set_state(dev, PRL_TX_SRC_SOURCE_TX); |
| } else { |
| atomic_set_bit(&prl_tx->flags, PRL_FLAGS_WAIT_SINK_OK); |
| prl_tx_set_state(dev, PRL_TX_SNK_START_AMS); |
| } |
| return; |
| } |
| } |
| |
| /* Handle non Rev 3.0 or subsequent messages in AMS sequence */ |
| if (atomic_test_and_clear_bit(&prl_tx->flags, PRL_FLAGS_MSG_XMIT)) { |
| /* |
| * Soft Reset Message pending |
| */ |
| if ((prl_tx->msg_type == PD_CTRL_SOFT_RESET) && (prl_tx->emsg.len == 0)) { |
| prl_tx_set_state(dev, PRL_TX_LAYER_RESET_FOR_TRANSMIT); |
| } else { |
| /* Message pending (except Soft Reset) */ |
| |
| /* NOTE: PRL_TX_Construct_Message State embedded here */ |
| prl_tx_construct_message(dev); |
| prl_tx_set_state(dev, PRL_TX_WAIT_FOR_PHY_RESPONSE); |
| } |
| return; |
| } |
| } |
| |
| /** |
| * @brief PRL_Tx_Layer_Reset_for_Transmit Entry State |
| */ |
| static void prl_tx_layer_reset_for_transmit_entry(void *obj) |
| { |
| struct protocol_layer_tx_t *prl_tx = (struct protocol_layer_tx_t *)obj; |
| const struct device *dev = prl_tx->dev; |
| struct usbc_port_data *data = dev->data; |
| struct protocol_layer_rx_t *prl_rx = data->prl_rx; |
| |
| LOG_INF("PRL_Tx_Layer_Reset_for_Transmit"); |
| |
| if (prl_tx->emsg.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->msg_id_counter[prl_tx->emsg.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->msg_id[prl_tx->emsg.type] = -1; |
| } |
| |
| /* NOTE: PRL_Tx_Construct_Message State embedded here */ |
| prl_tx_construct_message(dev); |
| prl_tx_set_state(dev, PRL_TX_WAIT_FOR_PHY_RESPONSE); |
| } |
| |
| /** |
| * @brief PRL_Tx_Wait_for_PHY_response Entry State |
| */ |
| static void prl_tx_wait_for_phy_response_entry(void *obj) |
| { |
| struct protocol_layer_tx_t *prl_tx = (struct protocol_layer_tx_t *)obj; |
| |
| LOG_INF("PRL_Tx_Wait_for_PHY_response"); |
| usbc_timer_start(&prl_tx->pd_t_tx_timeout); |
| } |
| |
| /** |
| * @brief PRL_Tx_Wait_for_PHY_response Run State |
| */ |
| static void prl_tx_wait_for_phy_response_run(void *obj) |
| { |
| struct protocol_layer_tx_t *prl_tx = (struct protocol_layer_tx_t *)obj; |
| const struct device *dev = prl_tx->dev; |
| |
| /* Wait until TX is complete */ |
| if (atomic_test_and_clear_bit(&prl_tx->flags, PRL_FLAGS_TX_DISCARDED)) { |
| /* NOTE: PRL_TX_DISCARD_MESSAGE State embedded here. */ |
| /* Inform Policy Engine Message was discarded */ |
| pe_report_discard(dev); |
| prl_tx_set_state(dev, PRL_TX_PHY_LAYER_RESET); |
| return; |
| } |
| if (atomic_test_bit(&prl_tx->flags, PRL_FLAGS_TX_COMPLETE)) { |
| /* NOTE: PRL_TX_Message_Sent State embedded here. */ |
| /* Inform Policy Engine Message was sent */ |
| pe_message_sent(dev); |
| /* |
| * This event reduces the time of informing the policy engine |
| * of the transmission by one state machine cycle |
| */ |
| prl_tx_set_state(dev, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); |
| return; |
| } else if (usbc_timer_expired(&prl_tx->pd_t_tx_timeout) || |
| atomic_test_bit(&prl_tx->flags, PRL_FLAGS_TX_ERROR)) { |
| /* |
| * NOTE: PRL_Tx_Transmission_Error State embedded |
| * here. |
| */ |
| /* Report Error To Policy Engine */ |
| pe_report_error(dev, ERR_XMIT, prl_tx->last_xmit_type); |
| prl_tx_set_state(dev, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); |
| return; |
| } |
| } |
| |
| /** |
| * @brief PRL_Tx_Wait_for_PHY_response Exit State |
| */ |
| static void prl_tx_wait_for_phy_response_exit(void *obj) |
| { |
| struct protocol_layer_tx_t *prl_tx = (struct protocol_layer_tx_t *)obj; |
| const struct device *dev = prl_tx->dev; |
| |
| usbc_timer_stop(&prl_tx->pd_t_tx_timeout); |
| |
| /* Increment messageId counter */ |
| increment_msgid_counter(dev); |
| } |
| |
| #ifdef CONFIG_USBC_CSM_SOURCE_ONLY |
| /** |
| * @brief 6.11.2.2.2.1 PRL_Tx_Src_Source_Tx |
| */ |
| static void prl_tx_src_source_tx_entry(void *obj) |
| { |
| struct protocol_layer_tx_t *prl_tx = (struct protocol_layer_tx_t *)obj; |
| const struct device *dev = prl_tx->dev; |
| |
| LOG_INF("PRL_Tx_Src_Tx"); |
| |
| /* Set Rp = SinkTxNG */ |
| tc_select_src_collision_rp(dev, SINK_TX_NG); |
| } |
| |
| static void prl_tx_src_source_tx_run(void *obj) |
| { |
| struct protocol_layer_tx_t *prl_tx = (struct protocol_layer_tx_t *)obj; |
| const struct device *dev = prl_tx->dev; |
| |
| if (atomic_test_bit(&prl_tx->flags, 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. |
| */ |
| prl_tx_set_state(dev, PRL_TX_SRC_PENDING); |
| } |
| } |
| #endif |
| #if CONFIG_USBC_CSM_SINK_ONLY |
| /** |
| * @brief PRL_Tx_Snk_Start_of_AMS Entry State |
| */ |
| static void prl_tx_snk_start_ams_entry(void *obj) |
| { |
| LOG_INF("PRL_Tx_Snk_Start_of_AMS"); |
| } |
| |
| /** |
| * @brief PRL_Tx_Snk_Start_of_AMS Run State |
| */ |
| static void prl_tx_snk_start_ams_run(void *obj) |
| { |
| struct protocol_layer_tx_t *prl_tx = (struct protocol_layer_tx_t *)obj; |
| const struct device *dev = prl_tx->dev; |
| |
| if (atomic_test_bit(&prl_tx->flags, 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. |
| */ |
| prl_tx_set_state(dev, PRL_TX_SNK_PENDING); |
| } |
| } |
| #endif |
| #ifdef CONFIG_USBC_CSM_SOURCE_ONLY |
| /** |
| * @brief PRL_Tx_Src_Pending Entry State |
| */ |
| static void prl_tx_src_pending_entry(void *obj) |
| { |
| struct protocol_layer_tx_t *prl_tx = (struct protocol_layer_tx_t *)obj; |
| |
| LOG_INF("PRL_Tx_Src_Pending"); |
| |
| /* Start SinkTxTimer */ |
| usbc_timer_start(&prl_tx->pd_t_sink_tx); |
| } |
| |
| /** |
| * @brief PRL_Tx_Src_Pending Run State |
| */ |
| static void prl_tx_src_pending_run(void *obj) |
| { |
| struct protocol_layer_tx_t *prl_tx = (struct protocol_layer_tx_t *)obj; |
| const struct device *dev = prl_tx->dev; |
| |
| if (usbc_timer_expired(&prl_tx->pd_t_sink_tx)) { |
| /* |
| * We clear the pending XMIT flag here right before we send so |
| * we can detect if we discarded this message or not |
| */ |
| atomic_clear_bit(&prl_tx->flags, PRL_FLAGS_MSG_XMIT); |
| |
| /* Soft Reset Message pending & SinkTxTimer timeout */ |
| if ((prl_tx->msg_type == PD_CTRL_SOFT_RESET) && (prl_tx->emsg.len == 0)) { |
| prl_tx_set_state(dev, PRL_TX_LAYER_RESET_FOR_TRANSMIT); |
| } |
| /* Message pending (except Soft Reset) & SinkTxTimer timeout */ |
| else { |
| /* If this is the first AMS message, inform the PE that it's been sent */ |
| if (atomic_test_bit(&prl_tx->flags, PRL_FLAGS_FIRST_MSG_PENDING)) { |
| atomic_clear_bit(&prl_tx->flags, PRL_FLAGS_FIRST_MSG_PENDING); |
| pe_first_msg_sent(dev); |
| } |
| |
| prl_tx_construct_message(dev); |
| prl_tx_set_state(dev, PRL_TX_WAIT_FOR_PHY_RESPONSE); |
| } |
| } |
| } |
| |
| /** |
| * @brief PRL_Tx_Src_Pending Exit State |
| */ |
| static void prl_tx_src_pending_exit(void *obj) |
| { |
| struct protocol_layer_tx_t *prl_tx = (struct protocol_layer_tx_t *)obj; |
| |
| /* Stop SinkTxTimer */ |
| usbc_timer_stop(&prl_tx->pd_t_sink_tx); |
| } |
| #endif |
| |
| #ifdef CONFIG_USBC_CSM_SINK_ONLY |
| /** |
| * @brief PRL_Tx_Snk_Pending Entry State |
| */ |
| static void prl_tx_snk_pending_entry(void *obj) |
| { |
| LOG_INF("PRL_Tx_Snk_Pending"); |
| } |
| |
| /** |
| * @brief PRL_Tx_Snk_Pending Run State |
| */ |
| static void prl_tx_snk_pending_run(void *obj) |
| { |
| struct protocol_layer_tx_t *prl_tx = (struct protocol_layer_tx_t *)obj; |
| const struct device *dev = prl_tx->dev; |
| struct usbc_port_data *data = dev->data; |
| const struct device *tcpc = data->tcpc; |
| enum tc_cc_voltage_state cc1; |
| enum tc_cc_voltage_state cc2; |
| |
| /* |
| * Wait unit the SRC applies SINK_TX_OK so we can transmit. |
| */ |
| tcpc_get_cc(tcpc, &cc1, &cc2); |
| |
| /* |
| * We clear the pending XMIT flag here right before we send so |
| * we can detect if we discarded this message or not |
| */ |
| atomic_clear_bit(&prl_tx->flags, PRL_FLAGS_MSG_XMIT); |
| |
| /* |
| * The Protocol Layer Shall transition to the |
| * PRL_Tx_Layer_Reset_for_Transmit state when a Soft_Reset |
| * Message is pending. |
| */ |
| if ((prl_tx->msg_type == PD_CTRL_SOFT_RESET) && (prl_tx->emsg.len == 0)) { |
| prl_tx_set_state(dev, PRL_TX_LAYER_RESET_FOR_TRANSMIT); |
| } else if (cc1 == TC_CC_VOLT_RP_3A0 || cc2 == TC_CC_VOLT_RP_3A0) { |
| /* If this is the first AMS message, inform the PE that it's been sent */ |
| if (atomic_test_bit(&prl_tx->flags, PRL_FLAGS_FIRST_MSG_PENDING)) { |
| atomic_clear_bit(&prl_tx->flags, PRL_FLAGS_FIRST_MSG_PENDING); |
| pe_first_msg_sent(dev); |
| } |
| |
| /* |
| * The Protocol Layer Shall transition to the PRL_Tx_Construct_Message |
| * state when Rp is set to SinkTxOk and a Soft_Reset Message is not |
| * pending. |
| */ |
| |
| /* |
| * Message pending (except Soft Reset) & |
| * Rp = SinkTxOk |
| */ |
| prl_tx_construct_message(dev); |
| prl_tx_set_state(dev, PRL_TX_WAIT_FOR_PHY_RESPONSE); |
| } |
| } |
| #endif |
| |
| static void prl_tx_suspend_entry(void *obj) |
| { |
| LOG_INF("PRL_TX_SUSPEND"); |
| } |
| |
| static void prl_tx_suspend_run(void *obj) |
| { |
| /* Do nothing */ |
| } |
| |
| /** |
| * All necessary Protocol Hard Reset States (Section 6.12.2.4) |
| */ |
| |
| /** |
| * @brief PRL_HR_Wait_for_Request Entry State |
| * |
| * @note This state is not part of the PRL_HR State Diagram found in |
| * Figure 6-66. The PRL_HR state machine waits here until a |
| * Hard Reset is requested by either the Policy Engine or the |
| * PHY Layer. |
| */ |
| static void prl_hr_wait_for_request_entry(void *obj) |
| { |
| struct protocol_hard_reset_t *prl_hr = (struct protocol_hard_reset_t *)obj; |
| |
| LOG_INF("PRL_HR_Wait_for_Request"); |
| |
| /* Reset all Protocol Layer Hard Reset flags */ |
| prl_hr->flags = ATOMIC_INIT(0); |
| } |
| |
| /** |
| * @brief PRL_HR_Wait_for_Request Run State |
| */ |
| static void prl_hr_wait_for_request_run(void *obj) |
| { |
| struct protocol_hard_reset_t *prl_hr = (struct protocol_hard_reset_t *)obj; |
| const struct device *dev = prl_hr->dev; |
| |
| /* |
| * The PRL_FLAGS_PE_HARD_RESET flag is set when a Hard Reset request is |
| * received from the Policy Engine. |
| * |
| * The PRL_FLAGS_PORT_PARTNER_HARD_RESET flag is set when Hard Reset |
| * signaling is received by the PHY Layer. |
| */ |
| if (atomic_test_bit(&prl_hr->flags, PRL_FLAGS_PE_HARD_RESET) || |
| atomic_test_bit(&prl_hr->flags, PRL_FLAGS_PORT_PARTNER_HARD_RESET)) { |
| /* Start Hard Reset */ |
| prl_hr_set_state(dev, PRL_HR_RESET_LAYER); |
| } |
| } |
| |
| /** |
| * @brief PRL_HR_Reset_Layer Entry State |
| */ |
| static void prl_hr_reset_layer_entry(void *obj) |
| { |
| struct protocol_hard_reset_t *prl_hr = (struct protocol_hard_reset_t *)obj; |
| const struct device *dev = prl_hr->dev; |
| struct usbc_port_data *data = dev->data; |
| struct protocol_layer_rx_t *prl_rx = data->prl_rx; |
| struct protocol_layer_tx_t *prl_tx = data->prl_tx; |
| const struct device *tcpc = data->tcpc; |
| int i; |
| |
| LOG_INF("PRL_HR_Reset_Layer"); |
| |
| /* Reset all Protocol Layer message reception flags */ |
| prl_rx->flags = ATOMIC_INIT(0); |
| /* Reset all Protocol Layer message transmission flags */ |
| prl_tx->flags = ATOMIC_INIT(0); |
| |
| /* Hard reset resets messageIDCounters for all TX types */ |
| for (i = 0; i < NUM_SOP_STAR_TYPES; i++) { |
| prl_rx->msg_id[i] = -1; |
| prl_tx->msg_id_counter[i] = 0; |
| } |
| |
| /* Disable RX */ |
| tcpc_set_rx_enable(tcpc, false); |
| |
| /* |
| * 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(dev); |
| |
| /* |
| * Protocol Layer message transmission transitions to |
| * PRL_Tx_Wait_For_Message_Request state. |
| */ |
| prl_tx_set_state(dev, PRL_TX_PHY_LAYER_RESET); |
| |
| /* |
| * Protocol Layer message reception transitions to |
| * PRL_Rx_Wait_for_PHY_Message state. |
| * |
| * Note: The PRL_Rx_Wait_for_PHY_Message state is implemented |
| * as a single function, named prl_rx_wait_for_phy_message. |
| */ |
| |
| /* |
| * Protocol Layer reset Complete & |
| * Hard Reset was initiated by Policy Engine |
| */ |
| if (atomic_test_bit(&prl_hr->flags, 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(dev); |
| prl_hr_set_state(dev, PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE); |
| } else { |
| /* |
| * Protocol Layer reset complete & |
| * Hard Reset was initiated by Port Partner |
| */ |
| |
| /* Inform Policy Engine of the Hard Reset */ |
| pe_got_hard_reset(dev); |
| prl_hr_set_state(dev, PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE); |
| } |
| } |
| |
| /** |
| * @brief PRL_HR_Wait_for_PHY_Hard_Reset_Complete Entry State |
| */ |
| static void prl_hr_wait_for_phy_hard_reset_complete_entry(void *obj) |
| { |
| struct protocol_hard_reset_t *prl_hr = (struct protocol_hard_reset_t *)obj; |
| |
| LOG_INF("PRL_HR_Wait_for_PHY_Hard_Reset_Complete"); |
| |
| /* |
| * Start the HardResetCompleteTimer and wait for the PHY Layer to |
| * indicate that the Hard Reset completed. |
| */ |
| usbc_timer_start(&prl_hr->pd_t_hard_reset_complete); |
| } |
| |
| /** |
| * @brief PRL_HR_Wait_for_PHY_Hard_Reset_Complete Run State |
| */ |
| static void prl_hr_wait_for_phy_hard_reset_complete_run(void *obj) |
| { |
| struct protocol_hard_reset_t *prl_hr = (struct protocol_hard_reset_t *)obj; |
| const struct device *dev = prl_hr->dev; |
| struct usbc_port_data *data = dev->data; |
| struct protocol_layer_tx_t *prl_tx = data->prl_tx; |
| |
| /* |
| * Wait for hard reset from PHY or timeout |
| */ |
| if (atomic_test_bit(&prl_tx->flags, PRL_FLAGS_TX_COMPLETE) || |
| usbc_timer_expired(&prl_hr->pd_t_hard_reset_complete)) { |
| /* PRL_HR_PHY_Hard_Reset_Requested */ |
| /* Inform Policy Engine Hard Reset was sent */ |
| pe_hard_reset_sent(dev); |
| prl_hr_set_state(dev, PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE); |
| } |
| } |
| |
| /** |
| * @brief PRL_HR_Wait_for_PHY_Hard_Reset_Complete Exit State |
| */ |
| static void prl_hr_wait_for_phy_hard_reset_complete_exit(void *obj) |
| { |
| struct protocol_hard_reset_t *prl_hr = (struct protocol_hard_reset_t *)obj; |
| |
| /* Stop the HardResetCompleteTimer */ |
| usbc_timer_stop(&prl_hr->pd_t_hard_reset_complete); |
| } |
| |
| /** |
| * @brief PRL_HR_Wait_For_PE_Hard_Reset_Complete Entry State |
| */ |
| static void prl_hr_wait_for_pe_hard_reset_complete_entry(void *obj) |
| { |
| LOG_INF("PRL_HR_Wait_For_PE_Hard_Reset_Complete"); |
| } |
| |
| /** |
| * @brief PRL_HR_Wait_For_PE_Hard_Reset_Complete Run State |
| */ |
| static void prl_hr_wait_for_pe_hard_reset_complete_run(void *obj) |
| { |
| struct protocol_hard_reset_t *prl_hr = (struct protocol_hard_reset_t *)obj; |
| const struct device *dev = prl_hr->dev; |
| |
| /* Wait for Hard Reset complete indication from Policy Engine */ |
| if (atomic_test_bit(&prl_hr->flags, PRL_FLAGS_HARD_RESET_COMPLETE)) { |
| prl_hr_set_state(dev, PRL_HR_WAIT_FOR_REQUEST); |
| } |
| } |
| |
| static void prl_hr_suspend_entry(void *obj) |
| { |
| LOG_INF("PRL_HR_SUSPEND"); |
| } |
| |
| static void prl_hr_suspend_run(void *obj) |
| { |
| /* Do nothing */ |
| } |
| |
| /** |
| * @brief This function implements both the Protocol Layer Message Reception |
| * State Machine. See Figure 6-55 Protocol layer Message reception |
| * |
| * The states of the two state machines can be identified by the |
| * comments preceded by a NOTE: <state name> |
| */ |
| static void prl_rx_wait_for_phy_message(const struct device *dev) |
| { |
| struct usbc_port_data *data = dev->data; |
| struct protocol_layer_rx_t *prl_rx = data->prl_rx; |
| struct protocol_layer_tx_t *prl_tx = data->prl_tx; |
| struct pd_msg *rx_emsg = &prl_rx->emsg; |
| const struct device *tcpc = data->tcpc; |
| uint8_t msg_type; |
| uint8_t pkt_type; |
| uint8_t ext; |
| int8_t msid; |
| uint8_t num_data_objs; |
| uint8_t power_role; |
| |
| /* Get the message */ |
| if (tcpc_get_rx_pending_msg(tcpc, rx_emsg) <= 0) { |
| /* No pending message or problem getting the message */ |
| return; |
| } |
| |
| num_data_objs = rx_emsg->header.number_of_data_objects; |
| msid = rx_emsg->header.message_id; |
| msg_type = rx_emsg->header.message_type; |
| ext = rx_emsg->header.extended; |
| pkt_type = rx_emsg->type; |
| power_role = rx_emsg->header.port_power_role; |
| |
| /* Dump the received packet content, except for Pings */ |
| if (msg_type != PD_CTRL_PING) { |
| int p; |
| |
| LOG_INF("RECV %04x/%d ", rx_emsg->header.raw_value, num_data_objs); |
| for (p = 0; p < num_data_objs; p++) { |
| LOG_INF("\t[%d]%08x ", p, *((uint32_t *)rx_emsg->data + p)); |
| } |
| } |
| |
| /* Ignore messages sent to the cable from our port partner */ |
| if (pkt_type != PD_PACKET_SOP && power_role == PD_PLUG_FROM_DFP_UFP) { |
| return; |
| } |
| |
| /* Soft Reset Message received from PHY */ |
| if (num_data_objs == 0 && msg_type == PD_CTRL_SOFT_RESET) { |
| /* NOTE: PRL_Rx_Layer_Reset_for_Receive State embedded here */ |
| |
| /* Reset MessageIdCounter */ |
| prl_tx->msg_id_counter[pkt_type] = 0; |
| |
| /* Clear stored MessageID value */ |
| prl_rx->msg_id[pkt_type] = -1; |
| |
| /* |
| * Protocol Layer message transmission transitions to |
| * PRL_Tx_PHY_Layer_Reset state |
| */ |
| prl_tx_set_state(dev, PRL_TX_PHY_LAYER_RESET); |
| |
| /* |
| * 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(dev); |
| return; |
| } |
| |
| /* Ignore if this is a duplicate message. Stop processing */ |
| if (prl_rx->msg_id[pkt_type] == msid) { |
| return; |
| } |
| |
| /* |
| * Discard any pending TX message if this RX message is from SOP, |
| * except for ping messages. |
| */ |
| |
| /* Check if message transmit is pending */ |
| if (atomic_test_bit(&prl_tx->flags, PRL_FLAGS_MSG_XMIT)) { |
| /* Don't discard message if a PING was received */ |
| if ((num_data_objs > 0) || (msg_type != PD_CTRL_PING)) { |
| /* Only discard message if received from SOP */ |
| if (pkt_type == PD_PACKET_SOP) { |
| atomic_set_bit(&prl_tx->flags, PRL_FLAGS_TX_DISCARDED); |
| } |
| } |
| } |
| |
| /* Store Message Id */ |
| prl_rx->msg_id[pkt_type] = msid; |
| |
| /* Pass message to Policy Engine */ |
| pe_message_received(dev); |
| } |
| |
| /** |
| * @brief Protocol Layer Transmit State table |
| */ |
| static const struct smf_state prl_tx_states[PRL_TX_STATE_COUNT] = { |
| [PRL_TX_PHY_LAYER_RESET] = SMF_CREATE_STATE( |
| prl_tx_phy_layer_reset_entry, |
| NULL, |
| NULL, |
| NULL, |
| NULL), |
| [PRL_TX_WAIT_FOR_MESSAGE_REQUEST] = SMF_CREATE_STATE( |
| prl_tx_wait_for_message_request_entry, |
| prl_tx_wait_for_message_request_run, |
| NULL, |
| NULL, |
| NULL), |
| [PRL_TX_LAYER_RESET_FOR_TRANSMIT] = SMF_CREATE_STATE( |
| prl_tx_layer_reset_for_transmit_entry, |
| NULL, |
| NULL, |
| NULL, |
| NULL), |
| [PRL_TX_WAIT_FOR_PHY_RESPONSE] = SMF_CREATE_STATE( |
| prl_tx_wait_for_phy_response_entry, |
| prl_tx_wait_for_phy_response_run, |
| prl_tx_wait_for_phy_response_exit, |
| NULL, |
| NULL), |
| [PRL_TX_SUSPEND] = SMF_CREATE_STATE( |
| prl_tx_suspend_entry, |
| prl_tx_suspend_run, |
| NULL, |
| NULL, |
| NULL), |
| #ifdef CONFIG_USBC_CSM_SINK_ONLY |
| [PRL_TX_SNK_START_AMS] = SMF_CREATE_STATE( |
| prl_tx_snk_start_ams_entry, |
| prl_tx_snk_start_ams_run, |
| NULL, |
| NULL, |
| NULL), |
| [PRL_TX_SNK_PENDING] = SMF_CREATE_STATE( |
| prl_tx_snk_pending_entry, |
| prl_tx_snk_pending_run, |
| NULL, |
| NULL, |
| NULL), |
| #endif |
| #ifdef CONFIG_USBC_CSM_SOURCE_ONLY |
| [PRL_TX_SRC_SOURCE_TX] = SMF_CREATE_STATE( |
| prl_tx_src_source_tx_entry, |
| prl_tx_src_source_tx_run, |
| NULL, |
| NULL, |
| NULL), |
| [PRL_TX_SRC_PENDING] = SMF_CREATE_STATE( |
| prl_tx_src_pending_entry, |
| prl_tx_src_pending_run, |
| prl_tx_src_pending_exit, |
| NULL, |
| NULL), |
| #endif |
| }; |
| BUILD_ASSERT(ARRAY_SIZE(prl_tx_states) == PRL_TX_STATE_COUNT); |
| |
| /** |
| * @brief Protocol Layer Hard Reset State table |
| */ |
| static const struct smf_state prl_hr_states[PRL_HR_STATE_COUNT] = { |
| [PRL_HR_WAIT_FOR_REQUEST] = SMF_CREATE_STATE( |
| prl_hr_wait_for_request_entry, |
| prl_hr_wait_for_request_run, |
| NULL, |
| NULL, |
| NULL), |
| [PRL_HR_RESET_LAYER] = SMF_CREATE_STATE( |
| prl_hr_reset_layer_entry, |
| NULL, |
| NULL, |
| NULL, |
| NULL), |
| [PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE] = SMF_CREATE_STATE( |
| prl_hr_wait_for_phy_hard_reset_complete_entry, |
| prl_hr_wait_for_phy_hard_reset_complete_run, |
| prl_hr_wait_for_phy_hard_reset_complete_exit, |
| NULL, |
| NULL), |
| [PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE] = SMF_CREATE_STATE( |
| prl_hr_wait_for_pe_hard_reset_complete_entry, |
| prl_hr_wait_for_pe_hard_reset_complete_run, |
| NULL, |
| NULL, |
| NULL), |
| [PRL_HR_SUSPEND] = SMF_CREATE_STATE( |
| prl_hr_suspend_entry, |
| prl_hr_suspend_run, |
| NULL, |
| NULL, |
| NULL), |
| }; |
| BUILD_ASSERT(ARRAY_SIZE(prl_hr_states) == PRL_HR_STATE_COUNT); |