/* * Copyright (c) 2023 The Chromium OS Authors * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * @brief USB-C Power Policy Engine (PE) * * The information in this file was taken from the USB PD * Specification Revision 3.0, Version 2.0 */ #include #include #include #include #include #include LOG_MODULE_DECLARE(usbc_stack, CONFIG_USBC_STACK_LOG_LEVEL); #include "usbc_stack.h" /** * @brief Initialize the Source Policy Engine layer */ void pe_src_init(const struct device *dev) { struct usbc_port_data *data = dev->data; struct policy_engine *pe = data->pe; /* Initial role of source is DFP */ pe_set_data_role(dev, TC_ROLE_DFP); /* Reject Sink Request by default */ pe->snk_request_reply = SNK_REQUEST_REJECT; /* Initialize timers */ usbc_timer_init(&pe->pd_t_typec_send_source_cap, PD_T_TYPEC_SEND_SOURCE_CAP_MIN_MS); usbc_timer_init(&pe->pd_t_ps_hard_reset, PD_T_PS_HARD_RESET_MAX_MS); /* Goto startup state */ pe_set_state(dev, PE_SRC_STARTUP); } /** * @brief Handle source-specific DPM requests */ bool source_dpm_requests(const struct device *dev) { struct usbc_port_data *data = dev->data; struct policy_engine *pe = data->pe; if (pe->dpm_request == REQUEST_GET_SNK_CAPS) { atomic_set_bit(pe->flags, PE_FLAGS_DPM_INITIATED_AMS); pe_set_state(dev, PE_GET_SINK_CAP); return true; } else if (pe->dpm_request == REQUEST_PE_GOTO_MIN) { atomic_set_bit(pe->flags, PE_FLAGS_DPM_INITIATED_AMS); pe_set_state(dev, PE_SRC_TRANSITION_SUPPLY); return true; } return false; } /** * @brief Send Source Caps to Sink */ static void send_src_caps(struct policy_engine *pe) { const struct device *dev = pe->dev; struct usbc_port_data *data = dev->data; struct protocol_layer_tx_t *prl_tx = data->prl_tx; struct pd_msg *msg = &prl_tx->emsg; const uint32_t *pdos; uint32_t num_pdos = 0; /* This callback must be implemented */ __ASSERT(data->policy_cb_get_src_caps != NULL, "Callback pointer should not be NULL"); data->policy_cb_get_src_caps(dev, &pdos, &num_pdos); msg->len = PD_CONVERT_PD_HEADER_COUNT_TO_BYTES(num_pdos); memcpy(msg->data, pdos, msg->len); pe_send_data_msg(dev, PD_PACKET_SOP, PD_DATA_SOURCE_CAP); } /** * @brief 8.3.3.2.1 PE_SRC_Startup State */ void pe_src_startup_entry(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; const struct device *dev = pe->dev; LOG_INF("PE_SRC_Startup"); /* Reset CapsCounter */ pe->caps_counter = 0; /* Reset the protocol layer */ prl_reset(dev); /* Set power role to Source */ pe->power_role = TC_ROLE_SOURCE; /* Invalidate explicit contract */ atomic_clear_bit(pe->flags, PE_FLAGS_EXPLICIT_CONTRACT); policy_notify(dev, NOT_PD_CONNECTED); } void pe_src_startup_run(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; const struct device *dev = pe->dev; /* * Once the reset process completes, the Policy Engine Shall * transition to the PE_SRC_Send_Capabilities state */ if (prl_is_running(dev)) { pe_set_state(dev, PE_SRC_SEND_CAPABILITIES); } } /** * @brief 8.3.3.2.2 PE_SRC_Discovery State */ void pe_src_discovery_entry(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; LOG_INF("PE_SRC_Discovery"); /* * Start the SourceCapabilityTimer in order to trigger sending a * Source_Capabilities message */ usbc_timer_start(&pe->pd_t_typec_send_source_cap); } void pe_src_discovery_run(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; const struct device *dev = pe->dev; /* * The Policy Engine Shall transition to the PE_SRC_Send_Capabilities state when: * 1) The SourceCapabilityTimer times out * 2) And CapsCounter ≤ nCapsCount */ if (usbc_timer_expired(&pe->pd_t_typec_send_source_cap)) { if (pe->caps_counter <= PD_N_CAPS_COUNT) { pe_set_state(dev, PE_SRC_SEND_CAPABILITIES); } else { pe_set_state(dev, PE_SRC_DISABLED); } } } void pe_src_discovery_exit(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; usbc_timer_stop(&pe->pd_t_typec_send_source_cap); } /** * @brief 8.3.3.2.3 PE_SRC_Send_Capabilities State */ void pe_src_send_capabilities_entry(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; /* Request present source capabilities from Device Policy Manager */ send_src_caps(pe); /* Increment CapsCounter */ pe->caps_counter++; /* Init submachine */ pe->submachine = SM_WAIT_FOR_TX; LOG_INF("PE_SRC_Send_Capabilities"); } void pe_src_send_capabilities_run(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; const struct device *dev = pe->dev; struct usbc_port_data *data = dev->data; struct protocol_layer_rx_t *prl_rx = data->prl_rx; switch (pe->submachine) { case SM_WAIT_FOR_TX: /* * When message is sent, the Policy Engine Shall: * 1) Stop the NoResponseTimer . * 2) Reset the HardResetCounter and CapsCounter to zero. * 3) Initialize and run the SenderResponseTimer */ if (atomic_test_and_clear_bit(pe->flags, PE_FLAGS_TX_COMPLETE)) { usbc_timer_stop(&pe->pd_t_no_response); pe->hard_reset_counter = 0; pe->caps_counter = 0; pe->submachine = SM_WAIT_FOR_RX; } /* * The Policy Engine Shall transition to the PE_SRC_Discovery * state when: * 1) The Protocol Layer indicates that the Message has * not been sent * 2) And we are presently not Connected. */ else if ((atomic_test_and_clear_bit(pe->flags, PE_FLAGS_MSG_XMIT_ERROR) || atomic_test_and_clear_bit(pe->flags, PE_FLAGS_MSG_DISCARDED)) && (atomic_test_bit(pe->flags, PE_FLAGS_PD_CONNECTED) == false)) { pe_set_state(dev, PE_SRC_DISCOVERY); } break; case SM_WAIT_FOR_RX: /* * The Policy Engine Shall transition to the PE_SRC_Negotiate_Capability state when: * 1) A Request Message is received from the Sink. */ if (atomic_test_and_clear_bit(pe->flags, PE_FLAGS_MSG_RECEIVED)) { union pd_header header = prl_rx->emsg.header; if (received_data_message(dev, header, PD_DATA_REQUEST)) { /* Set to highest revision supported by both ports */ prl_set_rev(dev, PD_PACKET_SOP, MIN(PD_REV30, header.specification_revision)); pe_set_state(dev, PE_SRC_NEGOTIATE_CAPABILITY); } } /* * The Policy Engine Shall transition to the PE_SRC_Hard_Reset * state when: * 1) The SenderResponseTimer times out */ else if (usbc_timer_expired(&pe->pd_t_sender_response)) { pe_set_state(dev, PE_SRC_HARD_RESET); } break; } } /** * @brief 8.3.3.2.4 PE_SRC_Negotiate_Capability State */ void pe_src_negotiate_capability_entry(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; const struct device *dev = pe->dev; struct usbc_port_data *data = dev->data; struct protocol_layer_rx_t *prl_rx = data->prl_rx; LOG_INF("PE_SRC_Negotiate_Capability"); /* Get sink request */ pe->snk_request = *(uint32_t *)prl_rx->emsg.data; /* * Ask the Device Policy Manager to evaluate the Request * from the Attached Sink. */ pe->snk_request_reply = policy_check_sink_request(dev, pe->snk_request); /* * The Policy Engine Shall transition to the * PE_SRC_Transition_Supply state when: * 1) The Request can be met. */ if (pe->snk_request_reply == SNK_REQUEST_VALID) { pe_set_state(dev, PE_SRC_TRANSITION_SUPPLY); } /* * The Policy Engine Shall transition to the * PE_SRC_Capability_Response state when: * 1) The Request cannot be met. * 2) Or the Request can be met later from the Power Reserve. */ else { pe_set_state(dev, PE_SRC_CAPABILITY_RESPONSE); } } /** * @brief 8.3.3.2.5 PE_SRC_Transition_Supply State */ void pe_src_transition_supply_entry(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; const struct device *dev = pe->dev; LOG_INF("PE_SRC_Transition_Supply"); /* * If snk_request_reply is set, this state was entered * from PE_SRC_Negotiate_Capability. So send Accept Message * and inform the Device Policy Manager that it Shall transition * the power supply to the Requested power level. */ if (pe->snk_request_reply == SNK_REQUEST_VALID) { pe_send_ctrl_msg(dev, PD_PACKET_SOP, PD_CTRL_ACCEPT); policy_notify(dev, TRANSITION_PS); } /* * If snk_request_reply is not valid, this state was entered * from PE_SRC_Ready. So send GotoMin Message. */ else { pe_send_ctrl_msg(dev, PD_PACKET_SOP, PD_CTRL_GOTO_MIN); } } void pe_src_transition_supply_run(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; const struct device *dev = pe->dev; /* * The Policy Engine Shall transition to the PE_SRC_Ready state when: * 1) The Device Policy Manager informs the Policy Engine that * the power supply is ready. */ if (atomic_test_bit(pe->flags, PE_FLAGS_TX_COMPLETE)) { if (policy_is_ps_ready(dev)) { pe_set_state(dev, PE_SRC_READY); } } /* * The Policy Engine Shall transition to the PE_SRC_Hard_Reset * state when: * 1) A Protocol Error occurs. */ else if (atomic_test_bit(pe->flags, PE_FLAGS_PROTOCOL_ERROR)) { pe_set_state(dev, PE_SRC_HARD_RESET); } } void pe_src_transition_supply_exit(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; const struct device *dev = pe->dev; /* Send PS_RDY message */ if (pe->snk_request_reply == SNK_REQUEST_VALID) { /* Clear request reply and reject by default */ pe->snk_request_reply = SNK_REQUEST_REJECT; /* Send PS Ready */ pe_send_ctrl_msg(dev, PD_PACKET_SOP, PD_CTRL_PS_RDY); /* Explicit Contract is now in place */ atomic_set_bit(pe->flags, PE_FLAGS_EXPLICIT_CONTRACT); /* Update present contract */ pe->present_contract = pe->snk_request; } } /** * @brief 8.3.3.2.6 PE_SRC_Ready State */ void pe_src_ready_entry(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; const struct device *dev = pe->dev; LOG_INF("PE_SRC_Ready"); /* * If the transition into PE_SRC_Ready is the result of Protocol Error * that has not caused a Soft Reset then the notification to the * Protocol Layer of the end of the AMS Shall Not be sent since there * is a Message to be processed. * * Else on entry to the PE_SRC_Ready state the Source Shall notify the * Protocol Layer of the end of the Atomic Message Sequence (AMS). */ if (atomic_test_and_clear_bit(pe->flags, PE_FLAGS_PROTOCOL_ERROR_NO_SOFT_RESET)) { pe_dpm_end_ams(dev); } } void pe_src_ready_run(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; const struct device *dev = pe->dev; struct usbc_port_data *data = dev->data; struct protocol_layer_rx_t *prl_rx = data->prl_rx; /* Handle incoming messages */ if (atomic_test_and_clear_bit(pe->flags, PE_FLAGS_MSG_RECEIVED)) { union pd_header header = prl_rx->emsg.header; /* * Extended Message Requests */ if (header.extended) { extended_message_not_supported(dev); } /* * Data Message Requests */ else if (header.number_of_data_objects > 0) { switch (header.message_type) { case PD_DATA_REQUEST: pe_set_state(dev, PE_SRC_NEGOTIATE_CAPABILITY); break; case PD_DATA_VENDOR_DEF: /** * VDM is unsupported. PD2.0 ignores and PD3.0 * reply with not supported. */ if (prl_get_rev(dev, PD_PACKET_SOP) > PD_REV20) { pe_set_state(dev, PE_SEND_NOT_SUPPORTED); } break; default: pe_set_state(dev, PE_SEND_NOT_SUPPORTED); } } /* * Control Message Requests */ else { switch (header.message_type) { case PD_CTRL_GOOD_CRC: /* Do nothing */ break; case PD_CTRL_NOT_SUPPORTED: /* Notify DPM */ policy_notify(dev, MSG_NOT_SUPPORTED_RECEIVED); break; case PD_CTRL_PING: /* Do nothing */ break; case PD_CTRL_GET_SOURCE_CAP: pe_set_state(dev, PE_SRC_SEND_CAPABILITIES); break; case PD_CTRL_DR_SWAP: pe_set_state(dev, PE_DRS_EVALUATE_SWAP); break; /* * USB PD 3.0 6.8.1: * Receiving an unexpected message shall be responded * to with a soft reset message. */ case PD_CTRL_ACCEPT: case PD_CTRL_REJECT: case PD_CTRL_WAIT: case PD_CTRL_PS_RDY: pe_send_soft_reset(dev, prl_rx->emsg.type); break; /* * Receiving an unknown or unsupported message * shall be responded to with a not supported * message. */ default: pe_set_state(dev, PE_SEND_NOT_SUPPORTED); break; } } } else { /* Handle Source DPManager Requests */ source_dpm_requests(dev); } } void pe_src_ready_exit(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; const struct device *dev = pe->dev; /* * If the Source is initiating an AMS, then notify the * PRL that the first message in an AMS will follow. */ if (pe_dpm_initiated_ams(dev)) { prl_first_msg_notificaiton(dev); } } /** * @brief 8.3.3.2.7 PE_SRC_Disabled State */ void pe_src_disabled_entry(void *obj) { LOG_INF("PE_SRC_Disabled"); /* * Unresponsive to USB Power Delivery messaging, but not to Hard Reset * Signaling. See pe_got_hard_reset */ } /** * @brief 8.3.3.2.11 PE_SRC_Transition_to_default State */ void pe_src_transition_to_default_entry(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; const struct device *dev = pe->dev; /* * On entry to the PE_SRC_Transition_to_default state the * Policy Engine Shall: * 1: indicate to the Device Policy Manager that the power * supply Shall Hard Reset * 2: request a reset of the local hardware * 3: request the Device Policy Manager to set the Port * Data Role to DFP and turn off VCONN. * * NOTE: 1, 2 and VCONN off are done by Device Policy Manager when * it receives the HARD_RESET_RECEIVED notification. */ policy_notify(dev, HARD_RESET_RECEIVED); pe->data_role = TC_ROLE_DFP; policy_notify(dev, DATA_ROLE_IS_DFP); } void pe_src_transition_to_default_run(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; const struct device *dev = pe->dev; /* * The Policy Engine Shall transition to the PE_SRC_Startup * state when: * 1: The Device Policy Manager indicates that the power * supply has reached the default level. */ if (policy_check(dev, CHECK_SRC_PS_AT_DEFAULT_LEVEL)) { pe_set_state(dev, PE_SRC_STARTUP); } } void pe_src_transition_to_default_exit(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; const struct device *dev = pe->dev; /* * On exit from the PE_SRC_Transition_to_default state the * Policy Engine Shall: * 1: request the Device Policy Manager to turn on VCONN * 2: inform the Protocol Layer that the Hard Reset is complete. * * NOTE: The Device Policy Manager turns on VCONN when it notifies the * PE that the Power Supply is at the default level. */ prl_hard_reset_complete(dev); } /** * 8.3.3.2.8 PE_SRC_Capability_Response State */ void pe_src_capability_response_entry(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; const struct device *dev = pe->dev; /* * On entry to the PE_SRC_Capability_Response state the Policy Engine * Shall request the Protocol Layer to send one of the following: */ /* * 1: Reject Message – if the request cannot be met or the present * Contract is Invalid. */ if (pe->snk_request_reply == SNK_REQUEST_REJECT) { pe_send_ctrl_msg(dev, PD_PACKET_SOP, PD_CTRL_REJECT); } /* * 2: Wait Message – if the request could be met later from the Power * Reserve. A Wait Message Shall Not be sent if the present Contract * is Invalid. */ else { pe_send_ctrl_msg(dev, PD_PACKET_SOP, PD_CTRL_WAIT); } } void pe_src_capability_response_run(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; const struct device *dev = pe->dev; /* Wait until message has been sent */ if (!atomic_test_and_clear_bit(pe->flags, PE_FLAGS_TX_COMPLETE)) { return; } /* * The Policy Engine Shall transition to the PE_SRC_Ready state when: * 1: There is an Explicit Contract AND * 2: A Reject Message has been sent and the present Contract * is still Valid OR * 3: A Wait Message has been sent. */ if (atomic_test_bit(pe->flags, PE_FLAGS_EXPLICIT_CONTRACT) && ((pe->snk_request_reply == SNK_REQUEST_REJECT && policy_present_contract_is_valid(dev, pe->present_contract)) || (pe->snk_request_reply == SNK_REQUEST_WAIT))) { pe_set_state(dev, PE_SRC_READY); } /* * The Policy Engine Shall transition to the PE_SRC_Hard_Reset state * when: * 1: There is an Explicit Contract and * 2: The Reject Message has been sent and the present Contract * is Invalid */ else if (atomic_test_bit(pe->flags, PE_FLAGS_EXPLICIT_CONTRACT) && policy_present_contract_is_valid(dev, pe->present_contract) == false) { pe_set_state(dev, PE_SRC_HARD_RESET); } /* * The Policy Engine Shall transition to the PE_SRC_Wait_New_Capabilities * state when: * 1: There is no Explicit Contract and * 2: A Reject Message has been sent or * 3: A Wait Message has been sent. */ else { /* 8.3.3.2.13 PE_SRC_Wait_New_Capabilities embedded here */ /* * In the PE_SRC_Wait_New_Capabilities State the Device Policy Manager * Should either decide to send no further Source Capabilities or * Should send a different set of Source Capabilities. Continuing * to send the same set of Source Capabilities could result in a live * lock situation. */ /* Notify DPM to send a different set of Source Capabilities */ if (policy_change_src_caps(dev)) { /* DPM will send different set of Source Capabilities */ pe_set_state(dev, PE_SRC_SEND_CAPABILITIES); } else { /* * DPM can not send a different set of Source * Capabilities, so disable port. */ pe_set_state(dev, PE_SUSPEND); } } } void pe_src_hard_reset_parent_entry(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; pe->submachine = SM_HARD_RESET_START; } void pe_src_hard_reset_parent_run(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; const struct device *dev = pe->dev; switch (pe->submachine) { case SM_HARD_RESET_START: /* * Initialize and run the NoResponseTimer. * Note that the NoResponseTimer Shall continue to run * in every state until it is stopped or times out. */ usbc_timer_start(&pe->pd_t_no_response); /* Initialize and run the PSHardResetTimer */ usbc_timer_start(&pe->pd_t_ps_hard_reset); pe->submachine = SM_HARD_RESET_WAIT; break; case SM_HARD_RESET_WAIT: /* * The Policy Engine Shall transition to the * PE_SRC_Transition_to_default state when: * The PSHardResetTimer times out. */ if (usbc_timer_expired(&pe->pd_t_ps_hard_reset)) { pe_set_state(dev, PE_SRC_TRANSITION_TO_DEFAULT); } break; } } void pe_src_hard_reset_parent_exit(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; /* Stop the Hard Reset Timer */ usbc_timer_stop(&pe->pd_t_ps_hard_reset); } /** * @brief 8.3.3.2.9 PE_SRC_Hard_Reset State */ void pe_src_hard_reset_entry(void *obj) { struct policy_engine *pe = (struct policy_engine *)obj; const struct device *dev = pe->dev; /* * On entry to the PE_SRC_Hard_Reset state the * Policy Engine Shall: */ /* * Request the generation of Hard Reset Signaling by * the PHY Layer */ prl_execute_hard_reset(dev); }