/* * Copyright (c) 2022 The Chromium OS Authors * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include 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: */ 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);