diff options
Diffstat (limited to 'common/usbc')
-rw-r--r-- | common/usbc/build.mk | 51 | ||||
-rw-r--r-- | common/usbc/dp_alt_mode.c | 292 | ||||
-rw-r--r-- | common/usbc/tbt_alt_mode.c | 579 | ||||
-rw-r--r-- | common/usbc/usb_mode.c | 311 | ||||
-rw-r--r-- | common/usbc/usb_pd_console.c | 212 | ||||
-rw-r--r-- | common/usbc/usb_pd_dp_ufp.c | 448 | ||||
-rw-r--r-- | common/usbc/usb_pd_dpm.c | 704 | ||||
-rw-r--r-- | common/usbc/usb_pd_host.c | 179 | ||||
-rw-r--r-- | common/usbc/usb_pd_timer.c | 268 | ||||
-rw-r--r-- | common/usbc/usb_pe_ctvpd_sm.c | 237 | ||||
-rw-r--r-- | common/usbc/usb_pe_drp_sm.c | 7486 | ||||
-rw-r--r-- | common/usbc/usb_prl_sm.c | 2471 | ||||
-rw-r--r-- | common/usbc/usb_retimer_fw_update.c | 189 | ||||
-rw-r--r-- | common/usbc/usb_sm.c | 202 | ||||
-rw-r--r-- | common/usbc/usb_tc_ctvpd_sm.c | 1716 | ||||
-rw-r--r-- | common/usbc/usb_tc_drp_acc_trysrc_sm.c | 4160 | ||||
-rw-r--r-- | common/usbc/usb_tc_vpd_sm.c | 430 | ||||
-rw-r--r-- | common/usbc/usbc_pd_policy.c | 48 | ||||
-rw-r--r-- | common/usbc/usbc_task.c | 182 |
19 files changed, 0 insertions, 20165 deletions
diff --git a/common/usbc/build.mk b/common/usbc/build.mk deleted file mode 100644 index 48ab5351b8..0000000000 --- a/common/usbc/build.mk +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright 2019 The Chromium OS Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# Build for USB Type-C and Power Delivery - -# Note that this variable includes the trailing "/" -_usbc_dir:=$(dir $(lastword $(MAKEFILE_LIST))) - -ifneq ($(CONFIG_USB_PD_TCPMV2),) -all-obj-$(CONFIG_USB_PD_TCPMV2)+=$(_usbc_dir)usb_pd_timer.o -all-obj-$(CONFIG_USB_PD_TCPMV2)+=$(_usbc_dir)usb_sm.o -all-obj-$(CONFIG_USB_PD_TCPMV2)+=$(_usbc_dir)usbc_task.o - -# Type-C state machines -ifneq ($(CONFIG_USB_TYPEC_SM),) -all-obj-$(CONFIG_USB_VPD)+=$(_usbc_dir)usb_tc_vpd_sm.o -all-obj-$(CONFIG_USB_CTVPD)+=$(_usbc_dir)usb_tc_ctvpd_sm.o -all-obj-$(CONFIG_USB_DRP_ACC_TRYSRC)+=$(_usbc_dir)usb_tc_drp_acc_trysrc_sm.o -endif # CONFIG_USB_TYPEC_SM - -# Protocol state machine -ifneq ($(CONFIG_USB_PRL_SM),) -all-obj-$(CONFIG_USB_PD_TCPMV2)+=$(_usbc_dir)usb_prl_sm.o -endif # CONFIG_USB_PRL_SM - -# Policy Engine state machines -ifneq ($(CONFIG_USB_PE_SM),) -all-obj-$(CONFIG_USB_VPD)+=$(_usbc_dir)usb_pe_ctvpd_sm.o -all-obj-$(CONFIG_USB_CTVPD)+=$(_usbc_dir)usb_pe_ctvpd_sm.o -all-obj-$(CONFIG_USB_DRP_ACC_TRYSRC)+=$(_usbc_dir)usbc_pd_policy.o -all-obj-$(CONFIG_USB_DRP_ACC_TRYSRC)+=$(_usbc_dir)usb_pe_drp_sm.o -all-obj-$(CONFIG_USB_DRP_ACC_TRYSRC)+=$(_usbc_dir)usb_pd_dpm.o -all-obj-$(CONFIG_USB_DRP_ACC_TRYSRC)+=$(_usbc_dir)dp_alt_mode.o -all-obj-$(CONFIG_USB_PD_TBT_COMPAT_MODE)+=$(_usbc_dir)tbt_alt_mode.o -all-obj-$(CONFIG_USB_PD_USB4)+=$(_usbc_dir)usb_mode.o -all-obj-$(CONFIG_CMD_PD)+=$(_usbc_dir)usb_pd_console.o -all-obj-$(CONFIG_USB_PD_HOST_CMD)+=$(_usbc_dir)usb_pd_host.o -endif # CONFIG_USB_PE_SM - -# Retimer firmware update -all-obj-$(CONFIG_USBC_RETIMER_FW_UPDATE)+=$(_usbc_dir)usb_retimer_fw_update.o - -# ALT-DP mode for UFP ports -all-obj-$(CONFIG_USB_PD_ALT_MODE_UFP_DP)+=$(_usbc_dir)usb_pd_dp_ufp.o -endif # CONFIG_USB_PD_TCPMV2 - -# For testing -all-obj-$(CONFIG_TEST_USB_PE_SM)+=$(_usbc_dir)usbc_pd_policy.o -all-obj-$(CONFIG_TEST_USB_PE_SM)+=$(_usbc_dir)usb_pe_drp_sm.o -all-obj-$(CONFIG_TEST_SM)+=$(_usbc_dir)usb_sm.o diff --git a/common/usbc/dp_alt_mode.c b/common/usbc/dp_alt_mode.c deleted file mode 100644 index 9a3493c6e1..0000000000 --- a/common/usbc/dp_alt_mode.c +++ /dev/null @@ -1,292 +0,0 @@ -/* Copyright 2020 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -/* - * DisplayPort alternate mode support - * Refer to VESA DisplayPort Alt Mode on USB Type-C Standard, version 2.0, - * section 5.2 - */ - -#include <stdbool.h> -#include <stdint.h> -#include "assert.h" -#include "usb_common.h" -#include "usb_dp_alt_mode.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* The state of the DP negotiation */ -enum dp_states { - DP_START = 0, - DP_ENTER_ACKED, - DP_ENTER_NAKED, - DP_STATUS_ACKED, - DP_ACTIVE, - DP_ENTER_RETRY, - DP_INACTIVE, - DP_STATE_COUNT -}; -static enum dp_states dp_state[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* - * Map of states to expected VDM commands in responses. - * Default of 0 indicates no command expected. - */ -static const uint8_t state_vdm_cmd[DP_STATE_COUNT] = { - [DP_START] = CMD_ENTER_MODE, - [DP_ENTER_ACKED] = CMD_DP_STATUS, - [DP_STATUS_ACKED] = CMD_DP_CONFIG, - [DP_ACTIVE] = CMD_EXIT_MODE, - [DP_ENTER_NAKED] = CMD_EXIT_MODE, - [DP_ENTER_RETRY] = CMD_ENTER_MODE, -}; - -bool dp_is_active(int port) -{ - return dp_state[port] == DP_ACTIVE; -} - -void dp_init(int port) -{ - dp_state[port] = DP_START; -} - -bool dp_entry_is_done(int port) -{ - return dp_state[port] == DP_ACTIVE || - dp_state[port] == DP_INACTIVE; -} - -static void dp_entry_failed(int port) -{ - CPRINTS("C%d: DP alt mode protocol failed!", port); - dp_state[port] = DP_INACTIVE; -} - -static bool dp_response_valid(int port, enum tcpci_msg_type type, - char *cmdt, int vdm_cmd) -{ - enum dp_states st = dp_state[port]; - - /* - * Check for an unexpected response. - * If DP is inactive, ignore the command. - */ - if (type != TCPCI_MSG_SOP || - (st != DP_INACTIVE && state_vdm_cmd[st] != vdm_cmd)) { - CPRINTS("C%d: Received unexpected DP VDM %s (cmd %d) from" - " %s in state %d", port, cmdt, vdm_cmd, - type == TCPCI_MSG_SOP ? "port partner" : "cable plug", - st); - dp_entry_failed(port); - return false; - } - return true; -} - -static void dp_exit_to_usb_mode(int port) -{ - int opos = pd_alt_mode(port, TCPCI_MSG_SOP, USB_SID_DISPLAYPORT); - - pd_dfp_exit_mode(port, TCPCI_MSG_SOP, USB_SID_DISPLAYPORT, opos); - set_usb_mux_with_current_data_role(port); - - CPRINTS("C%d: Exited DP mode", port); - /* - * If the EC exits an alt mode autonomously, don't try to enter it again. If - * the AP commands the EC to exit DP mode, it might command the EC to enter - * again later, so leave the state machine ready for that possibility. - */ - dp_state[port] = IS_ENABLED(CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY) - ? DP_START : DP_INACTIVE; -} - -void dp_vdm_acked(int port, enum tcpci_msg_type type, int vdo_count, - uint32_t *vdm) -{ - const struct svdm_amode_data *modep = - pd_get_amode_data(port, type, USB_SID_DISPLAYPORT); - const uint8_t vdm_cmd = PD_VDO_CMD(vdm[0]); - - if (!dp_response_valid(port, type, "ACK", vdm_cmd)) - return; - - /* TODO(b/155890173): Validate VDO count for specific commands */ - - switch (dp_state[port]) { - case DP_START: - case DP_ENTER_RETRY: - dp_state[port] = DP_ENTER_ACKED; - /* Inform PE layer that alt mode is now active */ - pd_set_dfp_enter_mode_flag(port, true); - break; - case DP_ENTER_ACKED: - /* DP status response & UFP's DP attention have same payload. */ - dfp_consume_attention(port, vdm); - dp_state[port] = DP_STATUS_ACKED; - break; - case DP_STATUS_ACKED: - if (modep && modep->opos && modep->fx->post_config) - modep->fx->post_config(port); - dp_state[port] = DP_ACTIVE; - CPRINTS("C%d: Entered DP mode", port); - break; - case DP_ACTIVE: - /* - * Request to exit mode successful, so put the module in an - * inactive state. - */ - dp_exit_to_usb_mode(port); - break; - case DP_ENTER_NAKED: - /* - * The request to exit the mode was successful, - * so try to enter the mode again. - */ - dp_state[port] = DP_ENTER_RETRY; - break; - case DP_INACTIVE: - /* - * This can occur if the mode is shutdown because - * the CPU is being turned off, and an exit mode - * command has been sent. - */ - break; - default: - /* Invalid or unexpected negotiation state */ - CPRINTF("%s called with invalid state %d\n", - __func__, dp_state[port]); - dp_entry_failed(port); - break; - } -} - -void dp_vdm_naked(int port, enum tcpci_msg_type type, uint8_t vdm_cmd) -{ - if (!dp_response_valid(port, type, "NAK", vdm_cmd)) - return; - - switch (dp_state[port]) { - case DP_START: - /* - * If a request to enter DP mode is NAK'ed, this likely - * means the partner is already in DP alt mode, so - * request to exit the mode first before retrying - * the enter command. This can happen if the EC - * is restarted (e.g to go into recovery mode) while - * DP alt mode is active. - */ - dp_state[port] = DP_ENTER_NAKED; - break; - case DP_ENTER_RETRY: - /* - * Another NAK on the second attempt to enter DP mode. - * Give up. - */ - dp_entry_failed(port); - break; - case DP_ACTIVE: - /* Treat an Exit Mode NAK the same as an Exit Mode ACK. */ - dp_exit_to_usb_mode(port); - break; - default: - CPRINTS("C%d: NAK for cmd %d in state %d", port, - vdm_cmd, dp_state[port]); - dp_entry_failed(port); - break; - } -} - -int dp_setup_next_vdm(int port, int vdo_count, uint32_t *vdm) -{ - const struct svdm_amode_data *modep = pd_get_amode_data(port, - TCPCI_MSG_SOP, USB_SID_DISPLAYPORT); - int vdo_count_ret; - - if (vdo_count < VDO_MAX_SIZE) - return -1; - - switch (dp_state[port]) { - case DP_START: - case DP_ENTER_RETRY: - /* Enter the first supported mode for DisplayPort. */ - vdm[0] = pd_dfp_enter_mode(port, TCPCI_MSG_SOP, - USB_SID_DISPLAYPORT, 0); - if (vdm[0] == 0) - return -1; - /* CMDT_INIT is 0, so this is a no-op */ - vdm[0] |= VDO_CMDT(CMDT_INIT); - vdm[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP)); - vdo_count_ret = 1; - if (dp_state[port] == DP_START) - CPRINTS("C%d: Attempting to enter DP mode", port); - break; - case DP_ENTER_ACKED: - if (!(modep && modep->opos)) - return -1; - - vdo_count_ret = modep->fx->status(port, vdm); - if (vdo_count_ret == 0) - return -1; - vdm[0] |= PD_VDO_OPOS(modep->opos); - vdm[0] |= VDO_CMDT(CMDT_INIT); - vdm[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP)); - break; - case DP_STATUS_ACKED: - if (!(modep && modep->opos)) - return -1; - - vdo_count_ret = modep->fx->config(port, vdm); - if (vdo_count_ret == 0) - return -1; - vdm[0] |= VDO_CMDT(CMDT_INIT); - vdm[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP)); - break; - case DP_ENTER_NAKED: - case DP_ACTIVE: - /* - * Called to exit DP alt mode, either when the mode - * is active and the system is shutting down, or - * when an initial request to enter the mode is NAK'ed. - * This can happen if the EC is restarted (e.g to go - * into recovery mode) while DP alt mode is active. - * It would be good to invoke modep->fx->exit but - * this doesn't set up the VDM, it clears state. - * TODO(b/159856063): Clean up the API to the fx functions. - */ - if (!(modep && modep->opos)) - return -1; - - usb_mux_set_safe_mode_exit(port); - - vdm[0] = VDO(USB_SID_DISPLAYPORT, - 1, /* structured */ - CMD_EXIT_MODE); - - vdm[0] |= VDO_OPOS(modep->opos); - vdm[0] |= VDO_CMDT(CMDT_INIT); - vdm[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP)); - vdo_count_ret = 1; - break; - case DP_INACTIVE: - /* - * DP mode is inactive. - */ - return -1; - default: - CPRINTF("%s called with invalid state %d\n", - __func__, dp_state[port]); - return -1; - } - return vdo_count_ret; -} diff --git a/common/usbc/tbt_alt_mode.c b/common/usbc/tbt_alt_mode.c deleted file mode 100644 index 73e2796345..0000000000 --- a/common/usbc/tbt_alt_mode.c +++ /dev/null @@ -1,579 +0,0 @@ -/* Copyright 2020 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -/* - * Thunderbolt alternate mode support - * Refer to USB Type-C Cable and Connector Specification Release 2.0 Section F - */ - -#include "atomic.h" -#include <stdbool.h> -#include <stdint.h> -#include "compile_time_macros.h" -#include "console.h" -#include "tcpm/tcpm.h" -#include "usb_common.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_tbt.h" -#include "usb_pd_tcpm.h" -#include "usb_pe_sm.h" -#include "usb_tbt_alt_mode.h" - -/* - * Enter/Exit TBT mode with active cable - * - * - * TBT_START |------------ - * retry_done = false | | - * | v | - * |<------------------| Exit Mode SOP | - * | retry_done = true | | | - * v | | ACK/NAK | - * Enter Mode SOP' | --------|--------- | - * ACK | NAK | Exit Mode SOP'' | - * |------|------| | | | - * | | | | ACK/NAK | - * v | | --------|--------- | - * Enter Mode SOP'' | | Exit Mode SOP' | - * | | | | | - * ACK | NAK | | | ACK/NAK | - * |------|------| | | ------------------ | - * | | | | retry_done == true? | - * v | | | | | - * Enter Mode SOP | | | No | | - * | | | |----------- | - * ACK | NAK | | |Yes | - * |-------|------| | | v | - * | | | | TBT_INACTIVE | - * v | | | retry_done = false | - * TBT_ACTIVE | | | | - * retry_done = true | | | | - * | | | | | - * v v v v | - * -----------------------------------------------------------------| - */ - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* - * If a partner sends an Enter Mode NAK, Exit Mode and try again. This has - * happened when the EC loses state after previously entering an alt mode - * with a partner. It may be fixed in b/159495742, in which case this - * logic is unneeded. - */ -#define TBT_FLAG_RETRY_DONE BIT(0) -#define TBT_FLAG_EXIT_DONE BIT(1) -#define TBT_FLAG_CABLE_ENTRY_DONE BIT(2) - -static uint8_t tbt_flags[CONFIG_USB_PD_PORT_MAX_COUNT]; - -#define TBT_SET_FLAG(port, flag) (tbt_flags[port] |= (flag)) -#define TBT_CLR_FLAG(port, flag) (tbt_flags[port] &= (~flag)) -#define TBT_CHK_FLAG(port, flag) (tbt_flags[port] & (flag)) - -static int tbt_prints(const char *string, int port) -{ - return CPRINTS("C%d: TBT %s", port, string); -} - -/* The states of Thunderbolt negotiation */ -enum tbt_states { - TBT_START = 0, - TBT_ENTER_SOP, - TBT_ACTIVE, - TBT_EXIT_SOP, - TBT_INACTIVE, - /* Active cable only */ - TBT_ENTER_SOP_PRIME, - TBT_ENTER_SOP_PRIME_PRIME, - TBT_EXIT_SOP_PRIME, - TBT_EXIT_SOP_PRIME_PRIME, - TBT_STATE_COUNT, -}; -static enum tbt_states tbt_state[CONFIG_USB_PD_PORT_MAX_COUNT]; - -static const uint8_t state_vdm_cmd[TBT_STATE_COUNT] = { - [TBT_ENTER_SOP] = CMD_ENTER_MODE, - [TBT_ACTIVE] = CMD_EXIT_MODE, - [TBT_EXIT_SOP] = CMD_EXIT_MODE, - /* Active cable only */ - [TBT_ENTER_SOP_PRIME] = CMD_ENTER_MODE, - [TBT_ENTER_SOP_PRIME_PRIME] = CMD_ENTER_MODE, - [TBT_EXIT_SOP_PRIME] = CMD_EXIT_MODE, - [TBT_EXIT_SOP_PRIME_PRIME] = CMD_EXIT_MODE, -}; - -void tbt_init(int port) -{ - tbt_state[port] = TBT_START; - TBT_CLR_FLAG(port, TBT_FLAG_RETRY_DONE); - TBT_SET_FLAG(port, TBT_FLAG_EXIT_DONE); - TBT_CLR_FLAG(port, TBT_FLAG_CABLE_ENTRY_DONE); -} - -bool tbt_is_active(int port) -{ - return tbt_state[port] != TBT_INACTIVE && - tbt_state[port] != TBT_START; -} - -bool tbt_entry_is_done(int port) -{ - return tbt_state[port] == TBT_ACTIVE || - tbt_state[port] == TBT_INACTIVE; -} - -bool tbt_cable_entry_is_done(int port) -{ - return TBT_CHK_FLAG(port, TBT_FLAG_CABLE_ENTRY_DONE); -} - -static void tbt_exit_done(int port) -{ - /* - * If the EC exits an alt mode autonomously, don't try to enter it again. If - * the AP commands the EC to exit DP mode, it might command the EC to enter - * again later, so leave the state machine ready for that possibility. - */ - tbt_state[port] = IS_ENABLED(CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY) - ? TBT_START : TBT_INACTIVE; - TBT_CLR_FLAG(port, TBT_FLAG_RETRY_DONE); - TBT_CLR_FLAG(port, TBT_FLAG_CABLE_ENTRY_DONE); - - if (!TBT_CHK_FLAG(port, TBT_FLAG_EXIT_DONE)) { - TBT_SET_FLAG(port, TBT_FLAG_EXIT_DONE); - tbt_prints("Exited alternate mode", port); - return; - } - - tbt_prints("alt mode protocol failed!", port); -} - -void tbt_exit_mode_request(int port) -{ - union tbt_mode_resp_cable cable_mode_resp; - - TBT_SET_FLAG(port, TBT_FLAG_RETRY_DONE); - TBT_CLR_FLAG(port, TBT_FLAG_EXIT_DONE); - /* - * If the port has entered USB4 mode with Thunderbolt mode for the - * cable, on request to exit, only exit Thunderbolt mode for the - * cable. - * TODO (b/156749387): Remove once data reset feature is in place. - */ - if (tbt_state[port] == TBT_ENTER_SOP) { - cable_mode_resp.raw_value = - pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME); - - /* - * For Linear re-driver cables, the port enters USB4 mode - * with Thunderbolt mode for SOP prime. Hence, on request to - * exit, only exit Thunderbolt mode SOP prime - */ - tbt_state[port] = - cable_mode_resp.tbt_active_passive == TBT_CABLE_ACTIVE ? - TBT_EXIT_SOP_PRIME : TBT_EXIT_SOP_PRIME_PRIME; - } -} - -static bool tbt_response_valid(int port, enum tcpci_msg_type type, - char *cmdt, int vdm_cmd) -{ - enum tbt_states st = tbt_state[port]; - union tbt_mode_resp_cable cable_mode_resp = { - .raw_value = pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME) }; - - /* - * Check for an unexpected response. - * 1. invalid command - * 2. invalid Tx type for passive cable - * If Thunderbolt is inactive, ignore the command. - */ - if ((st != TBT_INACTIVE && state_vdm_cmd[st] != vdm_cmd) || - (get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE && - cable_mode_resp.tbt_active_passive == TBT_CABLE_PASSIVE && - type != TCPCI_MSG_SOP)) { - tbt_exit_done(port); - return false; - } - return true; -} - -/* Exit Mode process is complete, but retry Enter Mode process */ -static void tbt_retry_enter_mode(int port) -{ - tbt_state[port] = TBT_START; - TBT_SET_FLAG(port, TBT_FLAG_RETRY_DONE); -} - -/* Send Exit Mode to SOP''(if supported), or SOP' */ -static void tbt_active_cable_exit_mode(int port) -{ - const struct pd_discovery *disc; - - disc = pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - - if (disc->identity.product_t1.a_rev20.sop_p_p) - tbt_state[port] = TBT_EXIT_SOP_PRIME_PRIME; - else - tbt_state[port] = TBT_EXIT_SOP_PRIME; -} - -bool tbt_cable_entry_required_for_usb4(int port) -{ - const struct pd_discovery *disc_sop_prime; - union tbt_mode_resp_cable cable_mode_resp; - - /* Request to enter Thunderbolt mode for the cable prior to entering - * USB4 mode if - - * 1. Thunderbolt Mode SOP' VDO active/passive bit (B25) is - * TBT_CABLE_ACTIVE or - * 2. It's an active cable with VDM version < 2.0 or - * VDO version < 1.3 - */ - if (tbt_cable_entry_is_done(port)) - return false; - - cable_mode_resp.raw_value = - pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME); - - if (cable_mode_resp.tbt_active_passive == TBT_CABLE_ACTIVE) - return true; - - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) { - disc_sop_prime = pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - if (pd_get_vdo_ver(port, TCPCI_MSG_SOP_PRIME) < VDM_VER20 || - disc_sop_prime->identity.product_t1.a_rev30.vdo_ver < - VDO_VERSION_1_3) - return true; - } - return false; -} - -void intel_vdm_acked(int port, enum tcpci_msg_type type, int vdo_count, - uint32_t *vdm) -{ - const struct pd_discovery *disc; - const uint8_t vdm_cmd = PD_VDO_CMD(vdm[0]); - int opos_sop, opos_sop_prime; - union tbt_mode_resp_cable cable_mode_resp; - - if (!tbt_response_valid(port, type, "ACK", vdm_cmd)) - return; - - disc = pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - - switch (tbt_state[port]) { - case TBT_ENTER_SOP_PRIME: - tbt_prints("enter mode SOP'", port); - cable_mode_resp.raw_value = - pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME); - /* For LRD cables, Enter mode SOP' -> Enter mode SOP */ - if (disc->identity.product_t1.a_rev20.sop_p_p && - cable_mode_resp.tbt_active_passive != TBT_CABLE_ACTIVE) { - tbt_state[port] = TBT_ENTER_SOP_PRIME_PRIME; - } else { - TBT_SET_FLAG(port, TBT_FLAG_CABLE_ENTRY_DONE); - tbt_state[port] = TBT_ENTER_SOP; - } - break; - case TBT_ENTER_SOP_PRIME_PRIME: - tbt_prints("enter mode SOP''", port); - TBT_SET_FLAG(port, TBT_FLAG_CABLE_ENTRY_DONE); - tbt_state[port] = TBT_ENTER_SOP; - break; - case TBT_ENTER_SOP: - set_tbt_compat_mode_ready(port); - tbt_state[port] = TBT_ACTIVE; - tbt_prints("enter mode SOP", port); - TBT_SET_FLAG(port, TBT_FLAG_RETRY_DONE); - /* Indicate to PE layer that alt mode is active */ - pd_set_dfp_enter_mode_flag(port, true); - break; - case TBT_ACTIVE: - tbt_prints("exit mode SOP", port); - opos_sop = pd_alt_mode(port, TCPCI_MSG_SOP, USB_VID_INTEL); - - /* Clear Thunderbolt related signals */ - pd_dfp_exit_mode(port, TCPCI_MSG_SOP, USB_VID_INTEL, opos_sop); - set_usb_mux_with_current_data_role(port); - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) { - tbt_active_cable_exit_mode(port); - } else { - /* - * Exit Mode process is complete; go to inactive state. - */ - tbt_exit_done(port); - } - break; - case TBT_EXIT_SOP: - set_usb_mux_with_current_data_role(port); - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) - tbt_active_cable_exit_mode(port); - else { - if (TBT_CHK_FLAG(port, TBT_FLAG_RETRY_DONE)) - /* retried enter mode, still failed, give up */ - tbt_exit_done(port); - else - tbt_retry_enter_mode(port); - } - break; - case TBT_EXIT_SOP_PRIME_PRIME: - tbt_prints("exit mode SOP''", port); - tbt_state[port] = TBT_EXIT_SOP_PRIME; - set_usb_mux_with_current_data_role(port); - break; - case TBT_EXIT_SOP_PRIME: - tbt_prints("exit mode SOP'", port); - if (TBT_CHK_FLAG(port, TBT_FLAG_RETRY_DONE)) { - /* - * Exit mode process is complete; go to inactive state. - */ - tbt_exit_done(port); - opos_sop_prime = - pd_alt_mode(port, TCPCI_MSG_SOP_PRIME, - USB_VID_INTEL); - - /* Clear Thunderbolt related signals */ - pd_dfp_exit_mode(port, TCPCI_MSG_SOP_PRIME, - USB_VID_INTEL, opos_sop_prime); - set_usb_mux_with_current_data_role(port); - } else { - tbt_retry_enter_mode(port); - } - break; - case TBT_INACTIVE: - /* - * This can occur if the mode is shutdown because - * the CPU is being turned off, and an exit mode - * command has been sent. - */ - break; - default: - /* Invalid or unexpected negotiation state */ - CPRINTF("%s called with invalid state %d\n", - __func__, tbt_state[port]); - tbt_exit_done(port); - break; - } -} - -void intel_vdm_naked(int port, enum tcpci_msg_type type, uint8_t vdm_cmd) -{ - if (!tbt_response_valid(port, type, "NAK", vdm_cmd)) - return; - - switch (tbt_state[port]) { - case TBT_ENTER_SOP_PRIME: - case TBT_ENTER_SOP_PRIME_PRIME: - case TBT_ENTER_SOP: - /* - * If a request to enter Thunderbolt mode is NAK'ed, this - * likely means the partner is already in Thunderbolt alt mode, - * so request to exit the mode first before retrying the enter - * command. This can happen if the EC is restarted - */ - tbt_state[port] = TBT_EXIT_SOP; - break; - case TBT_ACTIVE: - /* Exit SOP got NAK'ed */ - set_usb_mux_with_current_data_role(port); - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) - tbt_active_cable_exit_mode(port); - else { - tbt_prints("exit mode SOP failed", port); - tbt_state[port] = TBT_INACTIVE; - TBT_CLR_FLAG(port, TBT_FLAG_RETRY_DONE); - } - break; - case TBT_EXIT_SOP: - /* Exit SOP got NAK'ed */ - tbt_prints("exit mode SOP failed", port); - set_usb_mux_with_current_data_role(port); - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) - tbt_active_cable_exit_mode(port); - else { - if (TBT_CHK_FLAG(port, TBT_FLAG_RETRY_DONE)) - /* Retried enter mode, still failed, give up */ - tbt_exit_done(port); - else - tbt_retry_enter_mode(port); - } - break; - case TBT_EXIT_SOP_PRIME_PRIME: - set_usb_mux_with_current_data_role(port); - tbt_prints("exit mode SOP'' failed", port); - tbt_state[port] = TBT_EXIT_SOP_PRIME; - break; - case TBT_EXIT_SOP_PRIME: - set_usb_mux_with_current_data_role(port); - if (TBT_CHK_FLAG(port, TBT_FLAG_RETRY_DONE)) { - /* - * Exit mode process is complete; go to inactive state. - */ - tbt_prints("exit mode SOP' failed", port); - tbt_exit_done(port); - } else { - tbt_retry_enter_mode(port); - } - break; - default: - CPRINTS("C%d: NAK for cmd %d in state %d", port, - vdm_cmd, tbt_state[port]); - tbt_exit_done(port); - break; - } -} - -static bool tbt_mode_is_supported(int port, int vdo_count) -{ - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP); - - if (!disc->identity.idh.modal_support) - return false; - - if (get_tbt_cable_speed(port) < TBT_SS_U31_GEN1) - return false; - - /* - * TBT4 PD Discovery Flow Application Notes Revision 0.9: - * Figure 2: for active cable, SOP' should support - * SVID USB_VID_INTEL to enter Thunderbolt alt mode - */ - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE && - !pd_is_mode_discovered_for_svid( - port, TCPCI_MSG_SOP_PRIME, USB_VID_INTEL)) - return false; - - return true; -} - -int tbt_setup_next_vdm(int port, int vdo_count, uint32_t *vdm, - enum tcpci_msg_type *tx_type) -{ - struct svdm_amode_data *modep; - int vdo_count_ret = 0; - union tbt_mode_resp_cable cable_mode_resp; - - *tx_type = TCPCI_MSG_SOP; - - if (vdo_count < VDO_MAX_SIZE) - return -1; - - switch (tbt_state[port]) { - case TBT_START: - if (!tbt_mode_is_supported(port, vdo_count)) - return 0; - - if (!TBT_CHK_FLAG(port, TBT_FLAG_RETRY_DONE)) - tbt_prints("attempt to enter mode", port); - else - tbt_prints("retry to enter mode", port); - - cable_mode_resp.raw_value = - pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME); - - /* Active cable and LRD cables send Enter Mode SOP' first */ - if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE || - cable_mode_resp.tbt_active_passive == TBT_CABLE_ACTIVE) { - vdo_count_ret = enter_tbt_compat_mode(port, - TCPCI_MSG_SOP_PRIME, vdm); - *tx_type = TCPCI_MSG_SOP_PRIME; - tbt_state[port] = TBT_ENTER_SOP_PRIME; - } else { - /* Passive cable send Enter Mode SOP */ - vdo_count_ret = - enter_tbt_compat_mode(port, TCPCI_MSG_SOP, vdm); - tbt_state[port] = TBT_ENTER_SOP; - } - break; - case TBT_ENTER_SOP_PRIME: - vdo_count_ret = - enter_tbt_compat_mode(port, TCPCI_MSG_SOP_PRIME, vdm); - *tx_type = TCPCI_MSG_SOP_PRIME; - break; - case TBT_ENTER_SOP_PRIME_PRIME: - vdo_count_ret = - enter_tbt_compat_mode( - port, TCPCI_MSG_SOP_PRIME_PRIME, vdm); - *tx_type = TCPCI_MSG_SOP_PRIME_PRIME; - break; - case TBT_ENTER_SOP: - vdo_count_ret = - enter_tbt_compat_mode(port, TCPCI_MSG_SOP, vdm); - break; - case TBT_EXIT_SOP: - case TBT_ACTIVE: - /* - * Called to exit Thunderbolt alt mode, either when the mode is - * active and the system is shutting down, or when an initial - * request to enter the mode is NAK'ed. This can happen if EC - * is restarted while Thunderbolt mode is active. - */ - modep = pd_get_amode_data(port, - TCPCI_MSG_SOP, USB_VID_INTEL); - if (!(modep && modep->opos)) - return -1; - - usb_mux_set_safe_mode_exit(port); - - vdm[0] = VDO(USB_VID_INTEL, 1, CMD_EXIT_MODE) | - VDO_OPOS(modep->opos) | - VDO_CMDT(CMDT_INIT) | - VDO_SVDM_VERS( - pd_get_vdo_ver(port, TCPCI_MSG_SOP)); - vdo_count_ret = 1; - break; - case TBT_EXIT_SOP_PRIME_PRIME: - modep = pd_get_amode_data(port, - TCPCI_MSG_SOP_PRIME, USB_VID_INTEL); - if (!(modep && modep->opos)) - return -1; - - usb_mux_set_safe_mode_exit(port); - - vdm[0] = VDO(USB_VID_INTEL, 1, CMD_EXIT_MODE) | - VDO_OPOS(modep->opos) | - VDO_CMDT(CMDT_INIT) | - VDO_SVDM_VERS(pd_get_vdo_ver(port, - TCPCI_MSG_SOP_PRIME_PRIME)); - vdo_count_ret = 1; - *tx_type = TCPCI_MSG_SOP_PRIME_PRIME; - break; - case TBT_EXIT_SOP_PRIME: - modep = pd_get_amode_data(port, - TCPCI_MSG_SOP_PRIME, USB_VID_INTEL); - if (!(modep && modep->opos)) - return -1; - - usb_mux_set_safe_mode_exit(port); - - vdm[0] = VDO(USB_VID_INTEL, 1, CMD_EXIT_MODE) | - VDO_OPOS(modep->opos) | - VDO_CMDT(CMDT_INIT) | - VDO_SVDM_VERS(pd_get_vdo_ver(port, - TCPCI_MSG_SOP_PRIME)); - vdo_count_ret = 1; - *tx_type = TCPCI_MSG_SOP_PRIME; - break; - case TBT_INACTIVE: - /* Thunderbolt mode is inactive */ - return 0; - default: - CPRINTF("%s called with invalid state %d\n", - __func__, tbt_state[port]); - return -1; - } - - return vdo_count_ret; -} diff --git a/common/usbc/usb_mode.c b/common/usbc/usb_mode.c deleted file mode 100644 index b9dc4973bc..0000000000 --- a/common/usbc/usb_mode.c +++ /dev/null @@ -1,311 +0,0 @@ -/* Copyright 2020 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -/* - * USB4 mode support - * Refer USB Type-C Cable and Connector Specification Release 2.0 Section 5 and - * USB Power Delivery Specification Revision 3.0, Version 2.0 Section 6.4.8 - */ - -#include <stdbool.h> -#include <stdint.h> -#include "compile_time_macros.h" -#include "console.h" -#include "tcpm/tcpm.h" -#include "usb_common.h" -#include "usb_mode.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_dpm.h" -#include "usb_pd_tcpm.h" -#include "usb_pe_sm.h" -#include "usbc_ppc.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -enum usb4_mode_status { - USB4_MODE_FAILURE, - USB4_MODE_SUCCESS, -}; - -enum usb4_states { - USB4_START, - USB4_ENTER_SOP, - USB4_ENTER_SOP_PRIME, - USB4_ENTER_SOP_PRIME_PRIME, - USB4_ACTIVE, - USB4_INACTIVE, - USB4_STATE_COUNT, -}; - -/* - * USB4 PD flow: - * - * Cable type - * | - * |-------- Passive ---|---- Active -----| - * | | - * USB Highest Speed Structured VDM version - * | (cable revision)-- <2.0---->| - * --------|--------|------| | | - * | | | | >=2.0 | - * >=Gen3 Gen2 Gen1 USB2.0 | | - * | | | | VDO version--- <1.3 ---> Modal op? -- N --| - * Enter USB | | | (B21:23 of | | - * SOP with | | | Discover ID SOP'- y | - * Gen3 cable | | Skip Active cable VDO1) | | - * speed | | USB4 | TBT SVID? -- N --| - * | | mode >=1.3 | | - * Is modal op? | entry | y | - * | | Cable USB4 - N | | - * y | support? | Gen4 cable? - N - Skip - * | | | Skip USB4 | USB4 - * Is TBT SVID? -N- Enter | mode entry | mode - * | USB4 SOP | | entry - * y with Gen2 y | - * | cable speed | | - * | | | - * Is Discover mode | | - * SOP' B25? - N - Enter Enter USB4 mode | - * | USB4 SOP (SOP, SOP', SOP'') | - * | with speed | - * y from TBT mode | - * | SOP' VDO | - * | |<-- NAK -- Enter mode TBT SOP'<---| - * |---->Enter TBT SOP'-------NAK------>| | | | - * | | | | ACK | - * | ACK | | | | - * | | | |<-- NAK -- Enter mode TBT SOP'' | - * | Enter USB4 SOP | | | | - * | with speed from Exit TBT mode SOP ACK | - * | TBT mode SOP' VDO | | | | - * | ACK/NAK Enter USB4 SOP | - * | | | with speed from | - * | Exit TBT mode SOP'' TBT mode SOP' VDO | - * | | | | - * | ACK/NAK | - * | | | | - * | Exit TBT mode SOP' | - * | | | | - * | ACK/NAK | - * | | | | - * |---- N ----Retry done? -------------| |--------Retry done? ---- N -------| - * | | - * y y - * | | - * Skip USB4 mode entry Skip USB4 mode entry - */ - -static enum usb4_states usb4_state[CONFIG_USB_PD_PORT_MAX_COUNT]; - -static void usb4_debug_prints(int port, enum usb4_mode_status usb4_status) -{ - CPRINTS("C%d: USB4: State:%d Status:%d", port, usb4_state[port], - usb4_status); -} - -bool enter_usb_entry_is_done(int port) -{ - return usb4_state[port] == USB4_ACTIVE || - usb4_state[port] == USB4_INACTIVE; -} - -void usb4_exit_mode_request(int port) -{ - usb4_state[port] = USB4_START; - usb_mux_set_safe_mode_exit(port); - set_usb_mux_with_current_data_role(port); -} - -void enter_usb_init(int port) -{ - usb4_state[port] = USB4_START; -} - -void enter_usb_failed(int port) -{ - /* - * Since Enter USB sets the mux state to SAFE mode, fall back - * to USB mode on receiving a NAK. - */ - usb_mux_set(port, USB_PD_MUX_USB_ENABLED, USB_SWITCH_CONNECT, - polarity_rm_dts(pd_get_polarity(port))); - - usb4_debug_prints(port, USB4_MODE_FAILURE); - usb4_state[port] = USB4_INACTIVE; -} - -static bool enter_usb_response_valid(int port, enum tcpci_msg_type type) -{ - /* - * Check for an unexpected response. - */ - if (get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE && - type != TCPCI_MSG_SOP) { - enter_usb_failed(port); - return false; - } - return true; -} - -bool enter_usb_port_partner_is_capable(int port) -{ - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP); - - if (usb4_state[port] == USB4_INACTIVE) - return false; - - if (!PD_PRODUCT_IS_USB4(disc->identity.product_t1.raw_value)) - return false; - - return true; -} - -bool enter_usb_cable_is_capable(int port) -{ - if (get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE) { - if (get_usb4_cable_speed(port) < USB_R30_SS_U32_U40_GEN1) - return false; - } else if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) { - const struct pd_discovery *disc_sop_prime = - pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - - if (pd_get_vdo_ver(port, TCPCI_MSG_SOP_PRIME) >= VDM_VER20 && - disc_sop_prime->identity.product_t1.a_rev30.vdo_ver >= - VDO_VERSION_1_3) { - union active_cable_vdo2_rev30 a2_rev30 = - disc_sop_prime->identity.product_t2.a2_rev30; - /* - * For VDM version >= 2.0 and VD0 version is >= 1.3, - * do not enter USB4 mode if the cable isn't USB4 - * capable. - */ - if (a2_rev30.usb_40_support == USB4_NOT_SUPPORTED) - return false; - /* - * For VDM version < 2.0 or VDO version < 1.3, do not enter USB4 - * mode if the cable - - * doesn't support modal operation or - * doesn't support Intel SVID or - * doesn't have rounded support. - */ - } else { - const struct pd_discovery *disc = - pd_get_am_discovery(port, TCPCI_MSG_SOP); - union tbt_mode_resp_cable cable_mode_resp = { - .raw_value = pd_get_tbt_mode_vdo(port, - TCPCI_MSG_SOP_PRIME) }; - - if (!disc->identity.idh.modal_support || - !pd_is_mode_discovered_for_svid(port, - TCPCI_MSG_SOP_PRIME, USB_VID_INTEL) || - cable_mode_resp.tbt_rounded != - TBT_GEN3_GEN4_ROUNDED_NON_ROUNDED) - return false; - } - } else { - /* Not Emark cable */ - return false; - } - - return true; -} - -void enter_usb_accepted(int port, enum tcpci_msg_type type) -{ - const struct pd_discovery *disc; - - if (!enter_usb_response_valid(port, type)) - return; - - switch (usb4_state[port]) { - case USB4_ENTER_SOP_PRIME: - disc = pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - if (disc->identity.product_t1.a_rev20.sop_p_p) - usb4_state[port] = USB4_ENTER_SOP_PRIME_PRIME; - else - usb4_state[port] = USB4_ENTER_SOP; - break; - case USB4_ENTER_SOP_PRIME_PRIME: - usb4_state[port] = USB4_ENTER_SOP; - break; - case USB4_ENTER_SOP: - /* Connect the SBU and USB lines to the connector */ - if (IS_ENABLED(CONFIG_USBC_PPC_SBU)) - ppc_set_sbu(port, 1); - - usb4_state[port] = USB4_ACTIVE; - - /* Set usb mux to USB4 mode */ - usb_mux_set(port, USB_PD_MUX_USB4_ENABLED, USB_SWITCH_CONNECT, - polarity_rm_dts(pd_get_polarity(port))); - - usb4_debug_prints(port, USB4_MODE_SUCCESS); - break; - case USB4_ACTIVE: - break; - default: - enter_usb_failed(port); - } -} - -void enter_usb_rejected(int port, enum tcpci_msg_type type) -{ - if (!enter_usb_response_valid(port, type) || - usb4_state[port] == USB4_ACTIVE) - return; - - enter_usb_failed(port); -} - -uint32_t enter_usb_setup_next_msg(int port, enum tcpci_msg_type *type) -{ - const struct pd_discovery *disc_sop_prime; - - switch (usb4_state[port]) { - case USB4_START: - disc_sop_prime = pd_get_am_discovery(port, TCPCI_MSG_SOP_PRIME); - /* - * Ref: Tiger Lake Platform PD Controller Interface Requirements - * for Integrated USBC, section A.2.2: USB4 as DFP. - * Enter safe mode before sending Enter USB SOP/SOP'/SOP'' - * TODO (b/156749387): Remove once data reset feature is in - * place. - */ - usb_mux_set_safe_mode(port); - - if (pd_get_vdo_ver(port, TCPCI_MSG_SOP_PRIME) < VDM_VER20 || - disc_sop_prime->identity.product_t1.a_rev30.vdo_ver < - VDO_VERSION_1_3 || - get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE) { - usb4_state[port] = USB4_ENTER_SOP; - } else { - usb4_state[port] = USB4_ENTER_SOP_PRIME; - *type = TCPCI_MSG_SOP_PRIME; - } - break; - case USB4_ENTER_SOP_PRIME: - *type = TCPCI_MSG_SOP_PRIME; - break; - case USB4_ENTER_SOP_PRIME_PRIME: - *type = TCPCI_MSG_SOP_PRIME_PRIME; - break; - case USB4_ENTER_SOP: - *type = TCPCI_MSG_SOP; - break; - case USB4_ACTIVE: - return -1; - default: - return 0; - } - return get_enter_usb_msg_payload(port); -} diff --git a/common/usbc/usb_pd_console.c b/common/usbc/usb_pd_console.c deleted file mode 100644 index bbee776611..0000000000 --- a/common/usbc/usb_pd_console.c +++ /dev/null @@ -1,212 +0,0 @@ -/* Copyright 2020 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "common.h" -#include "console.h" -#include "usb_common.h" -#include "usb_pd_timer.h" -#include "usb_pe_sm.h" -#include "usb_prl_sm.h" -#include "usb_tc_sm.h" -#include "usb_pd.h" -#include "util.h" - -test_export_static int command_pd(int argc, char **argv) -{ - int port; - char *e; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - if (!strcasecmp(argv[1], "dump")) { - if (argc >= 3) { - int level = strtoi(argv[2], &e, 10); - - if (*e) - return EC_ERROR_PARAM2; - - if (level < DEBUG_DISABLE) - level = DEBUG_DISABLE; - else if (level > DEBUG_LEVEL_MAX) - level = DEBUG_LEVEL_MAX; - - prl_set_debug_level(level); - pe_set_debug_level(level); - tc_set_debug_level(level); - ccprintf("debug=%d\n", level); - return EC_SUCCESS; - } - } else if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC) && - !strcasecmp(argv[1], "trysrc")) { - enum try_src_override_t ov = tc_get_try_src_override(); - - if (argc >= 3) { - ov = strtoi(argv[2], &e, 10); - if (*e || ov > TRY_SRC_NO_OVERRIDE) - return EC_ERROR_PARAM3; - tc_try_src_override(ov); - } - - if (ov == TRY_SRC_NO_OVERRIDE) - ccprintf("Try.SRC System controlled\n"); - else - ccprintf("Try.SRC Forced %s\n", ov ? "ON" : "OFF"); - - return EC_SUCCESS; - } else if (!strcasecmp(argv[1], "version")) { - ccprintf("%d\n", PD_STACK_VERSION); - return EC_SUCCESS; - } - - /* command: pd <port> <subcmd> [args] */ - port = strtoi(argv[1], &e, 10); - if (argc < 3) - return EC_ERROR_PARAM_COUNT; - - if (*e || port >= CONFIG_USB_PD_PORT_MAX_COUNT) - return EC_ERROR_PARAM2; - - if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE)) { - if (!strcasecmp(argv[2], "tx")) { - pd_dpm_request(port, DPM_REQUEST_SNK_STARTUP); - } else if (!strcasecmp(argv[2], "charger")) { - pd_dpm_request(port, DPM_REQUEST_SRC_STARTUP); - } else if (!strcasecmp(argv[2], "dev")) { - int max_volt; - - if (argc >= 4) { - max_volt = strtoi(argv[3], &e, 10) * 1000; - if (*e) - return EC_ERROR_PARAM3; - } else { - max_volt = pd_get_max_voltage(); - } - pd_request_source_voltage(port, max_volt); - pd_dpm_request(port, DPM_REQUEST_NEW_POWER_LEVEL); - ccprintf("max req: %dmV\n", max_volt); - } else if (!strcasecmp(argv[2], "disable")) { - pd_comm_enable(port, 0); - ccprintf("Port C%d disable\n", port); - return EC_SUCCESS; - } else if (!strcasecmp(argv[2], "enable")) { - pd_comm_enable(port, 1); - ccprintf("Port C%d enabled\n", port); - return EC_SUCCESS; - } else if (!strcasecmp(argv[2], "hard")) { - pd_dpm_request(port, DPM_REQUEST_HARD_RESET_SEND); - } else if (!strcasecmp(argv[2], "soft")) { - pd_dpm_request(port, DPM_REQUEST_SOFT_RESET_SEND); - } else if (!strcasecmp(argv[2], "swap")) { - if (argc < 4) - return EC_ERROR_PARAM_COUNT; - - if (!strcasecmp(argv[3], "power")) - pd_dpm_request(port, DPM_REQUEST_PR_SWAP); - else if (!strcasecmp(argv[3], "data")) - pd_dpm_request(port, DPM_REQUEST_DR_SWAP); - else if (IS_ENABLED(CONFIG_USBC_VCONN_SWAP) && - !strcasecmp(argv[3], "vconn")) - pd_dpm_request(port, DPM_REQUEST_VCONN_SWAP); - else - return EC_ERROR_PARAM3; - } else if (!strcasecmp(argv[2], "dualrole")) { - if (argc < 4) { - cflush(); - ccprintf("dual-role toggling: "); - switch (pd_get_dual_role(port)) { - case PD_DRP_TOGGLE_ON: - ccprintf("on\n"); - break; - case PD_DRP_TOGGLE_OFF: - ccprintf("off\n"); - break; - case PD_DRP_FREEZE: - ccprintf("freeze\n"); - break; - case PD_DRP_FORCE_SINK: - ccprintf("force sink\n"); - break; - case PD_DRP_FORCE_SOURCE: - ccprintf("force source\n"); - break; - cflush(); - } - } else { - if (!strcasecmp(argv[3], "on")) - pd_set_dual_role(port, - PD_DRP_TOGGLE_ON); - else if (!strcasecmp(argv[3], "off")) - pd_set_dual_role(port, - PD_DRP_TOGGLE_OFF); - else if (!strcasecmp(argv[3], "freeze")) - pd_set_dual_role(port, PD_DRP_FREEZE); - else if (!strcasecmp(argv[3], "sink")) - pd_set_dual_role(port, - PD_DRP_FORCE_SINK); - else if (!strcasecmp(argv[3], "source")) - pd_set_dual_role(port, - PD_DRP_FORCE_SOURCE); - else - return EC_ERROR_PARAM4; - } - return EC_SUCCESS; - } - } - - if (!strcasecmp(argv[2], "state")) { - cflush(); - ccprintf("Port C%d CC%d, %s - Role: %s-%s", - port, pd_get_polarity(port) + 1, - pd_comm_is_enabled(port) ? "Enable" : "Disable", - pd_get_power_role(port) == - PD_ROLE_SOURCE ? "SRC" : "SNK", - pd_get_data_role(port) == PD_ROLE_DFP ? "DFP" : "UFP"); - - if (IS_ENABLED(CONFIG_USBC_VCONN)) - ccprintf("%s ", tc_is_vconn_src(port) ? "-VC" : ""); - - ccprintf("TC State: %s, Flags: 0x%04x", - tc_get_current_state(port), - tc_get_flags(port)); - - if (IS_ENABLED(CONFIG_USB_PE_SM)) - ccprintf(" PE State: %s, Flags: 0x%04x\n", - pe_get_current_state(port), - pe_get_flags(port)); - else - ccprintf("\n"); - - cflush(); - } else if (!strcasecmp(argv[2], "srccaps")) { - pd_srccaps_dump(port); - } - - if (IS_ENABLED(CONFIG_CMD_PD_TIMER) && - !strcasecmp(argv[2], "timer")) { - pd_timer_dump(port); - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(pd, command_pd, - "version" - "\ndump [0|1|2|3]" -#ifdef CONFIG_USB_PD_TRY_SRC - "\ntrysrc [0|1|2]" -#endif - "\n\t<port> state" - "\n\t<port> srccaps" -#ifdef CONFIG_CMD_PD_TIMER - "\n\t<port> timer" -#endif /* CONFIG_CMD_PD_TIMER */ -#ifdef CONFIG_USB_PD_DUAL_ROLE - "|tx|charger|dev" - "\n\t<port> disable|enable|soft|hard" - "\n\t<port> dualrole [on|off|freeze|sink|source]" - "\n\t<port> swap [power|data|vconn]" -#endif /* CONFIG_USB_PD_DUAL_ROLE */ - , - "USB PD"); diff --git a/common/usbc/usb_pd_dp_ufp.c b/common/usbc/usb_pd_dp_ufp.c deleted file mode 100644 index 0009b5c710..0000000000 --- a/common/usbc/usb_pd_dp_ufp.c +++ /dev/null @@ -1,448 +0,0 @@ -/* Copyright 2021 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -/* - * Functions required for UFP_D operation - */ - -#include "console.h" -#include "gpio.h" -#include "hooks.h" -#include "system.h" -#include "task.h" -#include "usb_pd.h" -#include "usb_pd_dp_ufp.h" - - -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) - -enum hpd_state { - LOW_WAIT, - HIGH_CHECK, - HIGH_WAIT, - LOW_CHECK, - IRQ_CHECK, -}; - -#define EDGE_QUEUE_DEPTH BIT(3) -#define EDGE_QUEUE_MASK (EDGE_QUEUE_DEPTH - 1) -#define HPD_QUEUE_DEPTH BIT(2) -#define HPD_QUEUE_MASK (HPD_QUEUE_DEPTH - 1) -#define HPD_T_IRQ_MIN_PULSE 250 -#define HPD_T_IRQ_MAX_PULSE (2 * MSEC) -#define HPD_T_MIN_DP_ATTEN (10 * MSEC) - -struct hpd_mark { - int level; - uint64_t ts; -}; - -struct hpd_edge { - int overflow; - uint32_t head; - uint32_t tail; - struct hpd_mark buffer[EDGE_QUEUE_DEPTH]; -}; - -struct hpd_info { - enum hpd_state state; - int count; - int send_enable; - uint64_t timer; - uint64_t last_send_ts; - enum hpd_event queue[HPD_QUEUE_DEPTH]; - struct hpd_edge edges; -}; - -static struct hpd_info hpd; -static struct mutex hpd_mutex; - -static int alt_dp_mode_opos[CONFIG_USB_PD_PORT_MAX_COUNT]; - -void pd_ufp_set_dp_opos(int port, int opos) -{ - alt_dp_mode_opos[port] = opos; -} - -int pd_ufp_get_dp_opos(int port) -{ - return alt_dp_mode_opos[port]; -} - -void pd_ufp_enable_hpd_send(int port) -{ - /* - * This control is used ensure that a DP_ATTENTION message is not sent - * to the DFP-D before a DP_CONFIG messaage has been received. This - * control is not strictly required by the spec, but some port partners - * will get confused if DP_ATTENTION is sent prior to DP_CONFIG. - */ - hpd.send_enable = 1; -} - -static void hpd_to_dp_attention(void) -{ - int port = hpd_config.port; - int evt_index = hpd.count - 1; - uint32_t vdm[2]; - uint32_t svdm_header; - enum hpd_event evt; - int opos = pd_ufp_get_dp_opos(port); - - if (!opos) - return; - - /* Get the next hpd event from the queue */ - evt = hpd.queue[evt_index]; - /* Save timestamp of when most recent DP attention message was sent */ - hpd.last_send_ts = get_time().val; - - /* - * Construct DP Attention message. This consists of the VDM header and - * the DP_STATUS VDO. - */ - svdm_header = VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP)) | - VDO_OPOS(opos) | CMD_ATTENTION; - vdm[0] = VDO(USB_SID_DISPLAYPORT, 1, svdm_header); - - vdm[1] = VDO_DP_STATUS((evt == hpd_irq), /* IRQ_HPD */ - (evt != hpd_low), /* HPD_HI|LOW */ - 0, /* request exit DP */ - 0, /* request exit USB */ - dock_get_mf_preference(), /* MF pref */ - 1, /* enabled */ - 0, /* power low */ - 0x2); - - /* Send request to DPM to send an attention VDM */ - pd_request_vdm_attention(port, vdm, ARRAY_SIZE(vdm)); - - /* If there are still events, need to shift the buffer */ - if (--hpd.count) { - int i; - - for (i = 0; i < hpd.count; i++) - hpd.queue[i] = hpd.queue[i + 1]; - } -} - -static void hpd_queue_event(enum hpd_event evt) -{ - /* - * HPD events are put into a queue. However, this queue is not a typical - * FIFO queue. Instead there are special rules based on which type of - * event is being added. - * HPD_LOW -> always resets the queue and must be in slot 0 - * HPD_HIGH -> must follow a HPD_LOW, so can only be in slot 0 or - * slot 1. - * HPD_IRQ -> There shall never be more than 2 HPD_IRQ events - * stored in the queue and HPD_IRQ must follow HPD_HIGH - * - * Worst case for queueing HPD events is 4 events in the queue: - * 0 - HPD_LOW - * 1 - HPD_HIGH - * 2 - HPD_IRQ - * 3 - HPD_IRQ - * - * The above rules mean that HPD_LOW and HPD_HIGH events can always be - * added to the queue since high must follow low and a low event resets - * the queue. HPD_IRQ events are checked to make sure that they don't - * overflow the queue and to ensure that no more than 2 hpd_irq events - * are kept in the queue. - */ - if (evt == hpd_irq) { - if ((hpd.count >= HPD_QUEUE_DEPTH) || ((hpd.count >= 2) && - (hpd.queue[hpd.count - 2] == hpd_irq))) { - CPRINTS("hpd: discard hpd: count - %d", - hpd.count); - return; - } - } - - if (evt == hpd_low) { - hpd.count = 0; - } - - /* Add event to the queue */ - hpd.queue[hpd.count++] = evt; -} - -static void hpd_to_pd_converter(int level, uint64_t ts) -{ - /* - * HPD edges are marked in the irq routine. The converter state machine - * runs in the hooks task and so there will be some delay between when - * the edge was captured and when that edge is processed here in the - * state machine. This means that the delitch timer (250 uSec) may have - * already expired or is about to expire. - * - * If transitioning to timing dependent state, need to ensure the state - * machine is executed again. All timers are relative to the ts value - * passed into this routine. The timestamps passed into this routine - * are either the values latched in the irq routine, or the current - * time latched by the calling function. From the perspective of the - * state machine, ts represents the current time. - * - * Note that all hpd queue events are contingent on detecting edges - * on the incoming hpd gpio signal. The hpd->dp attention converter is - * enabled/disabled as part of the svdm dp enter/exit response handler - * functions. When the converter is disabled, gpio interrupts for the - * hpd gpio signal are disabled so it will never execute, unless the - * converter is enabled, and the converter is only enabled when the - * UFP-D is actively in ALT-DP mode. - */ - switch (hpd.state) { - case LOW_WAIT: - /* - * In this state only expected event is a level change from low - * to high. - */ - if (level) { - hpd.state = HIGH_CHECK; - hpd.timer = ts + HPD_T_IRQ_MIN_PULSE; - } - break; - case HIGH_CHECK: - /* - * In this state if level is high and deglitch timer is - * exceeded, then state advances to HIGH_WAIT, otherwise return - * to LOW_WAIT state. - */ - if (!level || (ts <= hpd.timer)) { - hpd.state = LOW_WAIT; - } else { - hpd.state = HIGH_WAIT; - hpd_queue_event(hpd_high); - } - break; - case HIGH_WAIT: - /* - * In this state, only expected event is a level change from - * high to low. If current level is low, then advance to - * LOW_CHECK for deglitch checking. - */ - if (!level) { - hpd.state = LOW_CHECK; - hpd.timer = ts + HPD_T_IRQ_MIN_PULSE; - } - break; - case LOW_CHECK: - /* - * This state is used to deglitch high->low level - * change. However, due to processing latency, it's possible to - * detect hpd_irq event if level is high and low pulse width was - * valid. - */ - if (!level) { - /* Still low, now wait for IRQ or LOW determination */ - hpd.timer = ts + (HPD_T_IRQ_MAX_PULSE - - HPD_T_IRQ_MIN_PULSE); - hpd.state = IRQ_CHECK; - - } else { - uint64_t irq_ts = hpd.timer + HPD_T_IRQ_MAX_PULSE - - HPD_T_IRQ_MIN_PULSE; - /* - * If hpd is high now, this must have been an edge - * event, but still need to determine if the pulse width - * is longer than hpd_irq min pulse width. State will - * advance to HIGH_WAIT, but if pulse width is < 2 msec, - * must send hpd_irq event. - */ - if ((ts >= hpd.timer) && (ts <= irq_ts)) { - /* hpd irq detected */ - hpd_queue_event(hpd_irq); - } - hpd.state = HIGH_WAIT; - } - break; - case IRQ_CHECK: - /* - * In this state deglitch time has already passed. If current - * level is low and hpd_irq timer has expired, then go to - * LOW_WAIT as hpd_low event has been detected. If level is high - * and low pulse is < hpd_irq, hpd_irq event has been detected. - */ - if (level) { - hpd.state = HIGH_WAIT; - if (ts <= hpd.timer) { - hpd_queue_event(hpd_irq); - } - } else if (ts > hpd.timer) { - hpd.state = LOW_WAIT; - hpd_queue_event(hpd_low); - } - break; - } -} - -static void manage_hpd(void); -DECLARE_DEFERRED(manage_hpd); - -static void manage_hpd(void) -{ - int level; - uint64_t ts = get_time().val; - uint32_t num_hpd_events = (hpd.edges.head - hpd.edges.tail) & - EDGE_QUEUE_MASK; - - /* - * HPD edges are detected via GPIO interrupts. The ISR routine adds edge - * info to a queue and scheudles this routine. If this routine is called - * without a new edge detected, then it is being called due to a timer - * event. - */ - - /* First check to see overflow condition has occurred */ - if (hpd.edges.overflow) { - /* Disable hpd interrupts */ - usb_pd_hpd_converter_enable(0); - /* Re-enable hpd converter */ - usb_pd_hpd_converter_enable(1); - } - - if (num_hpd_events) { - while(num_hpd_events-- > 0) { - int idx = hpd.edges.tail; - - level = hpd.edges.buffer[idx].level; - ts = hpd.edges.buffer[idx].ts; - - hpd_to_pd_converter(level, ts); - hpd.edges.tail = (hpd.edges.tail + 1) & EDGE_QUEUE_MASK; - } - } else { - /* no new edge event, so get current time and level */ - level = gpio_get_level(hpd_config.signal); - ts = get_time().val; - hpd_to_pd_converter(level, ts); - } - - /* - * If min time spacing requirement is exceeded and a hpd_event is - * queued, then send DP_ATTENTION message. - */ - if (hpd.count > 0) { - /* - * If at least one hpd event is pending in the queue, send - * a DP_ATTENTION message if a DP_CONFIG message has been - * received and have passed the minimum spacing interval. - */ - if (hpd.send_enable && - ((get_time().val - hpd.last_send_ts) > - HPD_T_MIN_DP_ATTEN)) { - /* Generate DP_ATTENTION event pending in queue */ - hpd_to_dp_attention(); - } else { - uint32_t callback_us; - - /* - * Need to wait until until min spacing requirement of - * DP attention messages. Set callback time to the min - * value required. This callback time could be changed - * based on hpd interrupts. - * - * This wait is also used to prevent a DP_ATTENTION - * message from being sent before at least one DP_CONFIG - * message has been received. If DP_ATTENTION messages - * need to be delayed for this reason, then just wait - * the minimum time spacing. - */ - callback_us = HPD_T_MIN_DP_ATTEN - - (get_time().val - hpd.last_send_ts); - if (callback_us <= 0 || - callback_us > HPD_T_MIN_DP_ATTEN) - callback_us = HPD_T_MIN_DP_ATTEN; - hook_call_deferred(&manage_hpd_data, callback_us); - } - } - - /* - * Because of the delay between gpio edge irq, and when those edge - * events are processed here, all timers must be done relative to the - * timing marker stored in the hpd edge queue. If the state machine - * required a new timer, then hpd.timer will be advanced relative to the - * ts that was passed into the state machine. - * - * If the deglitch timer is active, then it can likely already have been - * expired when the edge gets processed. So if the timer is active the - * deferred callback must be requested. - *. - */ - if (hpd.timer > ts) { - uint64_t callback_us = 0; - uint64_t now = get_time().val; - - /* If timer is in the future, adjust the callback timer */ - if (now < hpd.timer) - callback_us = (hpd.timer - now) & 0xffffffff; - - hook_call_deferred(&manage_hpd_data, callback_us); - } -} - -void usb_pd_hpd_converter_enable(int enable) -{ - /* - * The hpd converter should be enabled as part of the UFP-D enter mode - * response function. Likewise, the converter should be disabled by the - * exit mode function. In addition, the coverter may get disabled so - * that it can be reset in the case that the input gpio edges queue - * overflows. A muxtex must be used here since this function may be - * called from the PD task (enter/exit response mode functions) or from - * the hpd event handler state machine (hook task). - */ - mutex_lock(&hpd_mutex); - - if (enable) { - gpio_disable_interrupt(hpd_config.signal); - /* Reset HPD event queue */ - hpd.state = LOW_WAIT; - hpd.count = 0; - hpd.timer = 0; - hpd.last_send_ts = 0; - hpd.send_enable = 0; - - /* Reset hpd signal edges queue */ - hpd.edges.head = 0; - hpd.edges.tail = 0; - hpd.edges.overflow = 0; - - /* If signal is high, need to ensure state machine executes */ - if (gpio_get_level(hpd_config.signal)) - hook_call_deferred(&manage_hpd_data, 0); - - /* Enable hpd edge detection */ - gpio_enable_interrupt(hpd_config.signal); - } else { - gpio_disable_interrupt(hpd_config.signal); - hook_call_deferred(&manage_hpd_data, -1); - } - - mutex_unlock(&hpd_mutex); -} - -void usb_pd_hpd_edge_event(int signal) -{ - int next_head = (hpd.edges.head + 1) & EDGE_QUEUE_MASK; - struct hpd_mark mark; - - /* Get current timestamp and level */ - mark.ts = get_time().val; - mark.level = gpio_get_level(hpd_config.signal); - - /* Add this edge to the buffer if there is space */ - if (next_head != hpd.edges.tail) { - hpd.edges.buffer[hpd.edges.head].ts = mark.ts; - hpd.edges.buffer[hpd.edges.head].level = mark.level; - hpd.edges.head = next_head; - } else { - /* Edge queue is overflowing, need to reset the converter */ - hpd.edges.overflow = 1; - } - /* Schedule HPD state machine to run ASAP */ - hook_call_deferred(&manage_hpd_data, 0); -} diff --git a/common/usbc/usb_pd_dpm.c b/common/usbc/usb_pd_dpm.c deleted file mode 100644 index eb2dfc52c0..0000000000 --- a/common/usbc/usb_pd_dpm.c +++ /dev/null @@ -1,704 +0,0 @@ -/* Copyright 2020 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -/* - * Device Policy Manager implementation - * Refer to USB PD 3.0 spec, version 2.0, sections 8.2 and 8.3 - */ - -#include "charge_state.h" -#include "compile_time_macros.h" -#include "console.h" -#include "ec_commands.h" -#include "hooks.h" -#include "system.h" -#include "task.h" -#include "tcpm/tcpm.h" -#include "usb_dp_alt_mode.h" -#include "usb_mode.h" -#include "usb_pd.h" -#include "usb_pd_dpm.h" -#include "usb_pd_tcpm.h" -#include "usb_tbt_alt_mode.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* Max Attention length is header + 1 VDO */ -#define DPM_ATTENION_MAX_VDO 2 - -static struct { - uint32_t flags; - uint32_t vdm_attention[DPM_ATTENION_MAX_VDO]; - int vdm_cnt; - mutex_t vdm_attention_mutex; -} dpm[CONFIG_USB_PD_PORT_MAX_COUNT]; - -#define DPM_SET_FLAG(port, flag) atomic_or(&dpm[(port)].flags, (flag)) -#define DPM_CLR_FLAG(port, flag) atomic_clear_bits(&dpm[(port)].flags, (flag)) -#define DPM_CHK_FLAG(port, flag) (dpm[(port)].flags & (flag)) - -/* Flags for internal DPM state */ -#define DPM_FLAG_MODE_ENTRY_DONE BIT(0) -#define DPM_FLAG_EXIT_REQUEST BIT(1) -#define DPM_FLAG_ENTER_DP BIT(2) -#define DPM_FLAG_ENTER_TBT BIT(3) -#define DPM_FLAG_ENTER_USB4 BIT(4) -#define DPM_FLAG_SEND_ATTENTION BIT(5) - -#ifdef CONFIG_ZEPHYR -static int init_vdm_attention_mutex(const struct device *dev) -{ - int port; - - ARG_UNUSED(dev); - - for (port = 0; port < CONFIG_USB_PD_PORT_MAX_COUNT; port++) - k_mutex_init(&dpm[port].vdm_attention_mutex); - - return 0; -} -SYS_INIT(init_vdm_attention_mutex, POST_KERNEL, 50); -#endif /* CONFIG_ZEPHYR */ - -enum ec_status pd_request_vdm_attention(int port, const uint32_t *data, - int vdo_count) -{ - mutex_lock(&dpm[port].vdm_attention_mutex); - - /* Only one Attention message may be pending */ - if (DPM_CHK_FLAG(port, DPM_FLAG_SEND_ATTENTION)) { - mutex_unlock(&dpm[port].vdm_attention_mutex); - return EC_RES_UNAVAILABLE; - } - - /* SVDM Attention message must be 1 or 2 VDOs in length */ - if (!vdo_count || (vdo_count > DPM_ATTENION_MAX_VDO)) { - mutex_unlock(&dpm[port].vdm_attention_mutex); - return EC_RES_INVALID_PARAM; - } - - /* Save contents of Attention message */ - memcpy(dpm[port].vdm_attention, data, vdo_count * sizeof(uint32_t)); - dpm[port].vdm_cnt = vdo_count; - - /* - * Indicate to DPM that an Attention message needs to be sent. This flag - * will be cleared when the Attention message is sent to the policy - * engine. - */ - DPM_SET_FLAG(port, DPM_FLAG_SEND_ATTENTION); - - mutex_unlock(&dpm[port].vdm_attention_mutex); - - return EC_RES_SUCCESS; -} - -enum ec_status pd_request_enter_mode(int port, enum typec_mode mode) -{ - if (port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - /* Only one enter request may be active at a time. */ - if (DPM_CHK_FLAG(port, DPM_FLAG_ENTER_DP | - DPM_FLAG_ENTER_TBT | - DPM_FLAG_ENTER_USB4)) - return EC_RES_BUSY; - - switch (mode) { - case TYPEC_MODE_DP: - DPM_SET_FLAG(port, DPM_FLAG_ENTER_DP); - break; -#ifdef CONFIG_USB_PD_TBT_COMPAT_MODE - case TYPEC_MODE_TBT: - DPM_SET_FLAG(port, DPM_FLAG_ENTER_TBT); - break; -#endif /* CONFIG_USB_PD_TBT_COMPAT_MODE */ -#ifdef CONFIG_USB_PD_USB4 - case TYPEC_MODE_USB4: - DPM_SET_FLAG(port, DPM_FLAG_ENTER_USB4); - break; -#endif - default: - return EC_RES_INVALID_PARAM; - } - - DPM_CLR_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE); - DPM_CLR_FLAG(port, DPM_FLAG_EXIT_REQUEST); - - return EC_RES_SUCCESS; -} - -void dpm_init(int port) -{ - dpm[port].flags = 0; -} - -static void dpm_set_mode_entry_done(int port) -{ - DPM_SET_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE); - DPM_CLR_FLAG(port, DPM_FLAG_ENTER_DP | DPM_FLAG_ENTER_TBT | - DPM_FLAG_ENTER_USB4); -} - -void dpm_set_mode_exit_request(int port) -{ - DPM_SET_FLAG(port, DPM_FLAG_EXIT_REQUEST); -} - -static void dpm_clear_mode_exit_request(int port) -{ - DPM_CLR_FLAG(port, DPM_FLAG_EXIT_REQUEST); -} - -/* - * Returns true if the current policy requests that the EC try to enter this - * mode on this port. If the EC is in charge of policy, the answer is always - * yes. - */ -static bool dpm_mode_entry_requested(int port, enum typec_mode mode) -{ - /* If the AP isn't controlling policy, the EC is. */ - if (!IS_ENABLED(CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY)) - return true; - - switch (mode) { - case TYPEC_MODE_DP: - return !!DPM_CHK_FLAG(port, DPM_FLAG_ENTER_DP); - case TYPEC_MODE_TBT: - return !!DPM_CHK_FLAG(port, DPM_FLAG_ENTER_TBT); - case TYPEC_MODE_USB4: - return !!DPM_CHK_FLAG(port, DPM_FLAG_ENTER_USB4); - default: - return false; - } -} - -void dpm_vdm_acked(int port, enum tcpci_msg_type type, int vdo_count, - uint32_t *vdm) -{ - const uint16_t svid = PD_VDO_VID(vdm[0]); - - assert(vdo_count >= 1); - - switch (svid) { - case USB_SID_DISPLAYPORT: - dp_vdm_acked(port, type, vdo_count, vdm); - break; - case USB_VID_INTEL: - if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE)) { - intel_vdm_acked(port, type, vdo_count, vdm); - break; - } - default: - CPRINTS("C%d: Received unexpected VDM ACK for SVID %d", port, - svid); - } -} - -void dpm_vdm_naked(int port, enum tcpci_msg_type type, uint16_t svid, - uint8_t vdm_cmd) -{ - switch (svid) { - case USB_SID_DISPLAYPORT: - dp_vdm_naked(port, type, vdm_cmd); - break; - case USB_VID_INTEL: - if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE)) { - intel_vdm_naked(port, type, vdm_cmd); - break; - } - default: - CPRINTS("C%d: Received unexpected VDM NAK for SVID %d", port, - svid); - } -} - -/* - * Requests that the PE send one VDM, whichever is next in the mode entry - * sequence. This only happens if preconditions for mode entry are met. If - * CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY is enabled, this function waits for the - * AP to direct mode entry. - */ -static void dpm_attempt_mode_entry(int port) -{ - int vdo_count = 0; - uint32_t vdm[VDO_MAX_SIZE]; - enum tcpci_msg_type tx_type = TCPCI_MSG_SOP; - bool enter_mode_requested = - IS_ENABLED(CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY) ? false : true; - - if (pd_get_data_role(port) != PD_ROLE_DFP) { - if (DPM_CHK_FLAG(port, DPM_FLAG_ENTER_DP | - DPM_FLAG_ENTER_TBT | - DPM_FLAG_ENTER_USB4)) - DPM_CLR_FLAG(port, DPM_FLAG_ENTER_DP | - DPM_FLAG_ENTER_TBT | - DPM_FLAG_ENTER_USB4); - /* - * TODO(b/168030639): Notify the AP that the enter mode request - * failed. - */ - return; - } - -#ifdef HAS_TASK_CHIPSET - /* - * Do not try to enter mode while CPU is off. - * CPU transitions (e.g b/158634281) can occur during the discovery - * phase or during enter/exit negotiations, and the state - * of the modes can get out of sync, causing the attempt to - * enter the mode to fail prematurely. - */ - if (chipset_in_or_transitioning_to_state(CHIPSET_STATE_ANY_OFF)) - return; -#endif - /* - * If discovery has not occurred for modes, do not attempt to switch - * to alt mode. - */ - if (pd_get_svids_discovery(port, TCPCI_MSG_SOP) != PD_DISC_COMPLETE || - pd_get_modes_discovery(port, TCPCI_MSG_SOP) != PD_DISC_COMPLETE) - return; - - if (dp_entry_is_done(port) || - (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE) && - tbt_entry_is_done(port)) || - (IS_ENABLED(CONFIG_USB_PD_USB4) && enter_usb_entry_is_done(port))) { - dpm_set_mode_entry_done(port); - return; - } - - /* Check if port, port partner and cable support USB4. */ - if (IS_ENABLED(CONFIG_USB_PD_USB4) && - board_is_tbt_usb4_port(port) && - enter_usb_port_partner_is_capable(port) && - enter_usb_cable_is_capable(port) && - dpm_mode_entry_requested(port, TYPEC_MODE_USB4)) { - /* - * For certain cables, enter Thunderbolt alt mode with the - * cable and USB4 mode with the port partner. - */ - if (tbt_cable_entry_required_for_usb4(port)) { - vdo_count = tbt_setup_next_vdm(port, - ARRAY_SIZE(vdm), vdm, &tx_type); - } else { - pd_dpm_request(port, DPM_REQUEST_ENTER_USB); - return; - } - } - - /* If not, check if they support Thunderbolt alt mode. */ - if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE) && - board_is_tbt_usb4_port(port) && - pd_is_mode_discovered_for_svid(port, TCPCI_MSG_SOP, - USB_VID_INTEL) && - dpm_mode_entry_requested(port, TYPEC_MODE_TBT)) { - enter_mode_requested = true; - vdo_count = tbt_setup_next_vdm(port, - ARRAY_SIZE(vdm), vdm, &tx_type); - } - - /* If not, check if they support DisplayPort alt mode. */ - if (vdo_count == 0 && !DPM_CHK_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE) && - pd_is_mode_discovered_for_svid(port, TCPCI_MSG_SOP, - USB_SID_DISPLAYPORT) && - dpm_mode_entry_requested(port, TYPEC_MODE_DP)) { - enter_mode_requested = true; - vdo_count = dp_setup_next_vdm(port, ARRAY_SIZE(vdm), vdm); - } - - /* - * If the PE didn't discover any supported (requested) alternate mode, - * just mark setup done and get out of here. - */ - if (vdo_count == 0 && !DPM_CHK_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE)) { - if (enter_mode_requested) { - /* - * TODO(b/168030639): Notify the AP that mode entry - * failed. - */ - CPRINTS("C%d: No supported alt mode discovered", port); - } - /* - * If the AP did not request mode entry, it may do so in the - * future, but the DPM is done trying for now. - */ - dpm_set_mode_entry_done(port); - return; - } - - if (vdo_count < 0) { - dpm_set_mode_entry_done(port); - CPRINTS("C%d: Couldn't construct alt mode VDM", port); - return; - } - - /* - * TODO(b/155890173): Provide a host command to request that the PE send - * an arbitrary VDM via this mechanism. - */ - if (!pd_setup_vdm_request(port, tx_type, vdm, vdo_count)) { - dpm_set_mode_entry_done(port); - return; - } - - pd_dpm_request(port, DPM_REQUEST_VDM); -} - -static void dpm_attempt_mode_exit(int port) -{ - uint32_t vdm = 0; - int vdo_count = 0; - enum tcpci_msg_type tx_type = TCPCI_MSG_SOP; - - if (IS_ENABLED(CONFIG_USB_PD_USB4) && - enter_usb_entry_is_done(port)) { - CPRINTS("C%d: USB4 teardown", port); - usb4_exit_mode_request(port); - } - if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE) && - tbt_is_active(port)) { - /* - * When the port is in USB4 mode and receives an exit request, - * it leaves USB4 SOP in active state. - * TODO(b/156749387): Support Data Reset for exiting USB4 SOP. - */ - CPRINTS("C%d: TBT teardown", port); - tbt_exit_mode_request(port); - vdo_count = tbt_setup_next_vdm(port, VDO_MAX_SIZE, &vdm, - &tx_type); - } else if (dp_is_active(port)) { - CPRINTS("C%d: DP teardown", port); - vdo_count = dp_setup_next_vdm(port, VDO_MAX_SIZE, &vdm); - } else { - /* Clear exit mode request */ - dpm_clear_mode_exit_request(port); - return; - } - - if (!pd_setup_vdm_request(port, tx_type, &vdm, vdo_count)) { - dpm_clear_mode_exit_request(port); - return; - } - - pd_dpm_request(port, DPM_REQUEST_VDM); -} - -static void dpm_send_attention_vdm(int port) -{ - /* Set up VDM ATTEN msg that was passed in previously */ - if (pd_setup_vdm_request(port, TCPCI_MSG_SOP, dpm[port].vdm_attention, - dpm[port].vdm_cnt) == true) - /* Trigger PE to start a VDM command run */ - pd_dpm_request(port, DPM_REQUEST_VDM); - - /* Clear flag after message is sent to PE layer */ - DPM_CLR_FLAG(port, DPM_FLAG_SEND_ATTENTION); -} - -void dpm_run(int port) -{ - if (pd_get_data_role(port) == PD_ROLE_DFP) { - /* Run DFP related DPM requests */ - if (DPM_CHK_FLAG(port, DPM_FLAG_EXIT_REQUEST)) - dpm_attempt_mode_exit(port); - else if (!DPM_CHK_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE)) - dpm_attempt_mode_entry(port); - } else { - /* Run UFP related DPM requests */ - if (DPM_CHK_FLAG(port, DPM_FLAG_SEND_ATTENTION)) - dpm_send_attention_vdm(port); - } -} - -/* - * Source-out policy variables and APIs - * - * Priority for the available 3.0 A ports is given in the following order: - * - sink partners which report requiring > 1.5 A in their Sink_Capabilities - */ - -/* - * Bitmasks of port numbers in each following category - * - * Note: request bitmasks should be accessed atomically as other ports may alter - * them - */ -static uint32_t max_current_claimed; -K_MUTEX_DEFINE(max_current_claimed_lock); - -/* Ports with PD sink needing > 1.5 A */ -static uint32_t sink_max_pdo_requested; -/* Ports with FRS source needing > 1.5 A */ -static uint32_t source_frs_max_requested; -/* Ports with non-PD sinks, so current requirements are unknown */ -static uint32_t non_pd_sink_max_requested; - -#define LOWEST_PORT(p) __builtin_ctz(p) /* Undefined behavior if p == 0 */ - -static int count_port_bits(uint32_t bitmask) -{ - int i, total = 0; - - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - if (bitmask & BIT(i)) - total++; - } - - return total; -} - -/* - * Centralized, mutex-controlled updates to the claimed 3.0 A ports - */ -static void balance_source_ports(void); -DECLARE_DEFERRED(balance_source_ports); - -static void balance_source_ports(void) -{ - uint32_t removed_ports, new_ports; - static bool deferred_waiting; - - if (task_get_current() == TASK_ID_HOOKS) - deferred_waiting = false; - - /* - * Ignore balance attempts while we're waiting for a downgraded port to - * finish the downgrade. - */ - if (deferred_waiting) - return; - - mutex_lock(&max_current_claimed_lock); - - /* Remove any ports which no longer require 3.0 A */ - removed_ports = max_current_claimed & ~(sink_max_pdo_requested | - source_frs_max_requested | - non_pd_sink_max_requested); - max_current_claimed &= ~removed_ports; - - /* Allocate 3.0 A to new PD sink ports that need it */ - new_ports = sink_max_pdo_requested & ~max_current_claimed; - while (new_ports) { - int new_max_port = LOWEST_PORT(new_ports); - - if (count_port_bits(max_current_claimed) < - CONFIG_USB_PD_3A_PORTS) { - max_current_claimed |= BIT(new_max_port); - typec_select_src_current_limit_rp(new_max_port, - TYPEC_RP_3A0); - } else if (non_pd_sink_max_requested & max_current_claimed) { - /* Always downgrade non-PD ports first */ - int rem_non_pd = LOWEST_PORT(non_pd_sink_max_requested & - max_current_claimed); - typec_select_src_current_limit_rp(rem_non_pd, - typec_get_default_current_limit_rp(rem_non_pd)); - max_current_claimed &= ~BIT(rem_non_pd); - - /* Wait tSinkAdj before using current */ - deferred_waiting = true; - hook_call_deferred(&balance_source_ports_data, - PD_T_SINK_ADJ); - goto unlock; - } else if (source_frs_max_requested & max_current_claimed) { - /* Downgrade lowest FRS port from 3.0 A slot */ - int rem_frs = LOWEST_PORT(source_frs_max_requested & - max_current_claimed); - pd_dpm_request(rem_frs, DPM_REQUEST_FRS_DET_DISABLE); - max_current_claimed &= ~BIT(rem_frs); - - /* Give 20 ms for the PD task to process DPM flag */ - deferred_waiting = true; - hook_call_deferred(&balance_source_ports_data, - 20 * MSEC); - goto unlock; - } else { - /* No lower priority ports to downgrade */ - goto unlock; - } - new_ports &= ~BIT(new_max_port); - } - - /* Allocate 3.0 A to any new FRS ports that need it */ - new_ports = source_frs_max_requested & ~max_current_claimed; - while (new_ports) { - int new_frs_port = LOWEST_PORT(new_ports); - - if (count_port_bits(max_current_claimed) < - CONFIG_USB_PD_3A_PORTS) { - max_current_claimed |= BIT(new_frs_port); - pd_dpm_request(new_frs_port, - DPM_REQUEST_FRS_DET_ENABLE); - } else if (non_pd_sink_max_requested & max_current_claimed) { - int rem_non_pd = LOWEST_PORT(non_pd_sink_max_requested & - max_current_claimed); - typec_select_src_current_limit_rp(rem_non_pd, - typec_get_default_current_limit_rp(rem_non_pd)); - max_current_claimed &= ~BIT(rem_non_pd); - - /* Wait tSinkAdj before using current */ - deferred_waiting = true; - hook_call_deferred(&balance_source_ports_data, - PD_T_SINK_ADJ); - goto unlock; - } else { - /* No lower priority ports to downgrade */ - goto unlock; - } - new_ports &= ~BIT(new_frs_port); - } - - /* Allocate 3.0 A to any non-PD ports which could need it */ - new_ports = non_pd_sink_max_requested & ~max_current_claimed; - while (new_ports) { - int new_max_port = LOWEST_PORT(new_ports); - - if (count_port_bits(max_current_claimed) < - CONFIG_USB_PD_3A_PORTS) { - max_current_claimed |= BIT(new_max_port); - typec_select_src_current_limit_rp(new_max_port, - TYPEC_RP_3A0); - } else { - /* No lower priority ports to downgrade */ - goto unlock; - } - new_ports &= ~BIT(new_max_port); - } -unlock: - mutex_unlock(&max_current_claimed_lock); -} - -/* Process port's first Sink_Capabilities PDO for port current consideration */ -void dpm_evaluate_sink_fixed_pdo(int port, uint32_t vsafe5v_pdo) -{ - /* Verify partner supplied valid vSafe5V fixed object first */ - if ((vsafe5v_pdo & PDO_TYPE_MASK) != PDO_TYPE_FIXED) - return; - - if (PDO_FIXED_VOLTAGE(vsafe5v_pdo) != 5000) - return; - - if (pd_get_power_role(port) == PD_ROLE_SOURCE) { - if (CONFIG_USB_PD_3A_PORTS == 0) - return; - - /* Valid PDO to process, so evaluate whether >1.5A is needed */ - if (PDO_FIXED_CURRENT(vsafe5v_pdo) <= 1500) - return; - - atomic_or(&sink_max_pdo_requested, BIT(port)); - } else { - int frs_current = vsafe5v_pdo & PDO_FIXED_FRS_CURR_MASK; - - if (!IS_ENABLED(CONFIG_USB_PD_FRS)) - return; - - /* FRS is only supported in PD 3.0 and higher */ - if (pd_get_rev(port, TCPCI_MSG_SOP) == PD_REV20) - return; - - if ((vsafe5v_pdo & PDO_FIXED_DUAL_ROLE) && frs_current) { - /* Always enable FRS when 3.0 A is not needed */ - if (frs_current == PDO_FIXED_FRS_CURR_DFLT_USB_POWER || - frs_current == PDO_FIXED_FRS_CURR_1A5_AT_5V) { - pd_dpm_request(port, - DPM_REQUEST_FRS_DET_ENABLE); - return; - } - - if (CONFIG_USB_PD_3A_PORTS == 0) - return; - - atomic_or(&source_frs_max_requested, BIT(port)); - } else { - return; - } - } - - balance_source_ports(); -} - -void dpm_add_non_pd_sink(int port) -{ - if (CONFIG_USB_PD_3A_PORTS == 0) - return; - - atomic_or(&non_pd_sink_max_requested, BIT(port)); - - balance_source_ports(); -} - -void dpm_remove_sink(int port) -{ - if (CONFIG_USB_PD_3A_PORTS == 0) - return; - - if (!(BIT(port) & sink_max_pdo_requested) && - !(BIT(port) & non_pd_sink_max_requested)) - return; - - atomic_clear_bits(&sink_max_pdo_requested, BIT(port)); - atomic_clear_bits(&non_pd_sink_max_requested, BIT(port)); - - /* Restore selected default Rp on the port */ - typec_select_src_current_limit_rp(port, - typec_get_default_current_limit_rp(port)); - - balance_source_ports(); -} - -void dpm_remove_source(int port) -{ - if (CONFIG_USB_PD_3A_PORTS == 0) - return; - - if (!IS_ENABLED(CONFIG_USB_PD_FRS)) - return; - - if (!(BIT(port) & source_frs_max_requested)) - return; - - atomic_clear_bits(&source_frs_max_requested, BIT(port)); - - balance_source_ports(); -} - -/* - * Note: all ports receive the 1.5 A source offering until they are found to - * match a criteria on the 3.0 A priority list (ex. through sink capability - * probing), at which point they will be offered a new 3.0 A source capability. - */ -__overridable int dpm_get_source_pdo(const uint32_t **src_pdo, const int port) -{ - /* Max PDO may not exist on boards which don't offer 3 A */ -#if CONFIG_USB_PD_3A_PORTS > 0 - if (max_current_claimed & BIT(port)) { - *src_pdo = pd_src_pdo_max; - return pd_src_pdo_max_cnt; - } -#endif - - *src_pdo = pd_src_pdo; - return pd_src_pdo_cnt; -} - -int dpm_get_source_current(const int port) -{ - if (pd_get_power_role(port) == PD_ROLE_SINK) - return 0; - - if (max_current_claimed & BIT(port)) - return 3000; - else if (typec_get_default_current_limit_rp(port) == TYPEC_RP_1A5) - return 1500; - else - return 500; -} diff --git a/common/usbc/usb_pd_host.c b/common/usbc/usb_pd_host.c deleted file mode 100644 index 4d0fadeec3..0000000000 --- a/common/usbc/usb_pd_host.c +++ /dev/null @@ -1,179 +0,0 @@ -/* Copyright 2020 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - * - * Host commands for TCPMv2 USB PD module - */ - -#include <string.h> - -#include "console.h" -#include "ec_commands.h" -#include "host_command.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" -#include "util.h" - -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) - -/* Retrieve all discovery results for the given port and transmit type */ -static enum ec_status hc_typec_discovery(struct host_cmd_handler_args *args) -{ - const struct ec_params_typec_discovery *p = args->params; - struct ec_response_typec_discovery *r = args->response; - const struct pd_discovery *disc; - enum tcpci_msg_type type; - - /* Confirm the number of HC VDOs matches our stored VDOs */ - BUILD_ASSERT(sizeof(r->discovery_vdo) == sizeof(union disc_ident_ack)); - - if (p->port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - if (p->partner_type > TYPEC_PARTNER_SOP_PRIME) - return EC_RES_INVALID_PARAM; - - type = p->partner_type == TYPEC_PARTNER_SOP ? - TCPCI_MSG_SOP : TCPCI_MSG_SOP_PRIME; - - /* - * Clear out access mask so we can track if tasks have touched data - * since read started. - */ - pd_discovery_access_clear(p->port, type); - - disc = pd_get_am_discovery_and_notify_access(p->port, type); - - /* Initialize return size to that of discovery with no SVIDs */ - args->response_size = sizeof(*r); - - if (pd_get_identity_discovery(p->port, type) == PD_DISC_COMPLETE) { - r->identity_count = disc->identity_cnt; - memcpy(r->discovery_vdo, - pd_get_identity_response(p->port, type)->raw_value, - sizeof(r->discovery_vdo)); - } else { - r->identity_count = 0; - return EC_RES_SUCCESS; - } - - if (pd_get_modes_discovery(p->port, type) == PD_DISC_COMPLETE) { - int svid_i; - int max_resp_svids = (args->response_max - args->response_size)/ - sizeof(struct svid_mode_info); - - if (disc->svid_cnt > max_resp_svids) { - CPRINTS("Warn: SVIDS exceeded HC response"); - r->svid_count = max_resp_svids; - } else { - r->svid_count = disc->svid_cnt; - } - - for (svid_i = 0; svid_i < r->svid_count; svid_i++) { - r->svids[svid_i].svid = disc->svids[svid_i].svid; - r->svids[svid_i].mode_count = - disc->svids[svid_i].mode_cnt; - memcpy(r->svids[svid_i].mode_vdo, - disc->svids[svid_i].mode_vdo, - sizeof(r->svids[svid_i].mode_vdo)); - args->response_size += sizeof(struct svid_mode_info); - } - } else { - r->svid_count = 0; - } - - /* - * Verify that another task did not access this data during the duration - * of the copy. If the data was accessed, return BUSY so the AP will - * try retrieving again and get the updated data. - */ - if (!pd_discovery_access_validate(p->port, type)) { - CPRINTS("[C%d] %s returns EC_RES_BUSY!!\n", p->port, __func__); - return EC_RES_BUSY; - } - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_TYPEC_DISCOVERY, - hc_typec_discovery, - EC_VER_MASK(0)); - -static enum ec_status hc_typec_control(struct host_cmd_handler_args *args) -{ - const struct ec_params_typec_control *p = args->params; - - if (p->port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - switch (p->command) { - case TYPEC_CONTROL_COMMAND_EXIT_MODES: - pd_dpm_request(p->port, DPM_REQUEST_EXIT_MODES); - break; - case TYPEC_CONTROL_COMMAND_CLEAR_EVENTS: - pd_clear_events(p->port, p->clear_events_mask); - break; - case TYPEC_CONTROL_COMMAND_ENTER_MODE: - return pd_request_enter_mode(p->port, p->mode_to_enter); - default: - return EC_RES_INVALID_PARAM; - } - - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_TYPEC_CONTROL, hc_typec_control, EC_VER_MASK(0)); - -static enum ec_status hc_typec_status(struct host_cmd_handler_args *args) -{ - const struct ec_params_typec_status *p = args->params; - struct ec_response_typec_status *r = args->response; - const char *tc_state_name; - - if (p->port >= board_get_usb_pd_port_count()) - return EC_RES_INVALID_PARAM; - - if (args->response_max < sizeof(*r)) - return EC_RES_RESPONSE_TOO_BIG; - - args->response_size = sizeof(*r); - - r->pd_enabled = pd_comm_is_enabled(p->port); - r->dev_connected = pd_is_connected(p->port); - r->sop_connected = pd_capable(p->port); - - r->power_role = pd_get_power_role(p->port); - r->data_role = pd_get_data_role(p->port); - r->vconn_role = pd_get_vconn_state(p->port) ? PD_ROLE_VCONN_SRC : - PD_ROLE_VCONN_OFF; - r->polarity = pd_get_polarity(p->port); - r->cc_state = pd_get_task_cc_state(p->port); - r->dp_pin = get_dp_pin_mode(p->port); - r->mux_state = usb_mux_get(p->port); - - tc_state_name = pd_get_task_state_name(p->port); - strzcpy(r->tc_state, tc_state_name, sizeof(r->tc_state)); - - r->events = pd_get_events(p->port); - - r->sop_revision = r->sop_connected ? - PD_STATUS_REV_SET_MAJOR(pd_get_rev(p->port, TCPCI_MSG_SOP)) : 0; - r->sop_prime_revision = - pd_get_identity_discovery(p->port, TCPCI_MSG_SOP_PRIME) == - PD_DISC_COMPLETE ? - PD_STATUS_REV_SET_MAJOR(pd_get_rev(p->port, - TCPCI_MSG_SOP_PRIME)) - : 0; - - r->source_cap_count = pd_get_src_cap_cnt(p->port); - memcpy(r->source_cap_pdos, pd_get_src_caps(p->port), - r->source_cap_count * sizeof(uint32_t)); - - r->sink_cap_count = pd_get_snk_cap_cnt(p->port); - memcpy(r->sink_cap_pdos, pd_get_snk_caps(p->port), - r->sink_cap_count * sizeof(uint32_t)); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_TYPEC_STATUS, hc_typec_status, EC_VER_MASK(0)); diff --git a/common/usbc/usb_pd_timer.c b/common/usbc/usb_pd_timer.c deleted file mode 100644 index 67a574904f..0000000000 --- a/common/usbc/usb_pd_timer.c +++ /dev/null @@ -1,268 +0,0 @@ -/* Copyright 2021 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "assert.h" -#include "common.h" -#include "console.h" -#include "limits.h" -#include "system.h" -#include "usb_pd_timer.h" -#include "usb_tc_sm.h" - -#define MAX_PD_PORTS CONFIG_USB_PD_PORT_MAX_COUNT -#define MAX_PD_TIMERS PD_TIMER_COUNT -#define PD_TIMERS_ALL_MASK ((uint32_t)(((uint64_t)1 << PD_TIMER_COUNT) - 1)) - -#define MAX_EXPIRE (0x7FFFFFFF) -#define NO_TIMEOUT (-1) -#define EXPIRE_NOW (0) - -#define PD_SET_ACTIVE(p, m) atomic_or(&timer_active[p], (m)) -#define PD_CLR_ACTIVE(p, m) atomic_clear_bits(&timer_active[p], (m)) -#define PD_CHK_ACTIVE(p, m) (timer_active[p] & (m)) - -#define PD_SET_DISABLED(p, m) atomic_or(&timer_disabled[p], (m)) -#define PD_CLR_DISABLED(p, m) atomic_clear_bits(&timer_disabled[p], (m)) -#define PD_CHK_DISABLED(p, m) (timer_disabled[p] & (m)) - -static uint32_t timer_active[MAX_PD_PORTS]; -static uint32_t timer_disabled[MAX_PD_PORTS]; -static uint64_t timer_expires[MAX_PD_PORTS][MAX_PD_TIMERS]; - -/* - * CONFIG_CMD_PD_TIMER debug variables - */ -static int count[MAX_PD_PORTS]; -static int max_count[MAX_PD_PORTS]; - -__maybe_unused static __const_data const char * const pd_timer_names[] = { - [PE_TIMER_BIST_CONT_MODE] = "PE-BIST_CONT_MODE", - [PE_TIMER_CHUNKING_NOT_SUPPORTED] = "PE-CHUNKING_NOT_SUPPORTED", - [PE_TIMER_DISCOVER_IDENTITY] = "PE-DISCOVER_IDENTITY", - [PE_TIMER_NO_RESPONSE] = "PE-NO_RESPONSE", - [PE_TIMER_PR_SWAP_WAIT] = "PE-PR_SWAP_WAIT", - [PE_TIMER_PS_HARD_RESET] = "PE-PS_HARD_RESET", - [PE_TIMER_PS_SOURCE] = "PE-PS_SOURCE", - [PE_TIMER_PS_TRANSITION] = "PE-PS_TRANSITION", - [PE_TIMER_SENDER_RESPONSE] = "PE-SENDER_RESPONSE", - [PE_TIMER_SINK_REQUEST] = "PE-SINK_REQUEST", - [PE_TIMER_SOURCE_CAP] = "PE-SOURCE_CAP", - [PE_TIMER_SRC_TRANSITION] = "PE-SRC_TRANSITION", - [PE_TIMER_SWAP_SOURCE_START] = "PE-SWAP_SOURCE_START", - [PE_TIMER_TIMEOUT] = "PE-TIMEOUT", - [PE_TIMER_VCONN_ON] = "PE-VCONN_ON", - [PE_TIMER_VDM_RESPONSE] = "PE-VDM_RESPONSE", - [PE_TIMER_WAIT_AND_ADD_JITTER] = "PE-WAIT_AND_ADD_JITTER", - - [PR_TIMER_CHUNK_SENDER_REQUEST] = "PR-CHUNK_SENDER_REQUEST", - [PR_TIMER_CHUNK_SENDER_RESPONSE] = "PR-CHUNK_SENDER_RESPONSE", - [PR_TIMER_HARD_RESET_COMPLETE] = "PR-HARD_RESET_COMPLETE", - [PR_TIMER_SINK_TX] = "PR-SINK_TX", - [PR_TIMER_TCPC_TX_TIMEOUT] = "PR-TCPC_TX_TIMEOUT", - - [TC_TIMER_CC_DEBOUNCE] = "TC-CC_DEBOUNCE", - [TC_TIMER_LOW_POWER_EXIT_TIME] = "TC-LOW_POWER_EXIT_TIME", - [TC_TIMER_LOW_POWER_TIME] = "TC-LOW_POWER_TIME", - [TC_TIMER_NEXT_ROLE_SWAP] = "TC-NEXT_ROLE_SWAP", - [TC_TIMER_PD_DEBOUNCE] = "TC-PD_DEBOUNCE", - [TC_TIMER_TIMEOUT] = "TC-TIMEOUT", - [TC_TIMER_TRY_WAIT_DEBOUNCE] = "TC-TRY_WAIT_DEBOUNCE", - [TC_TIMER_VBUS_DEBOUNCE] = "TC-VBUS_DEBOUNCE", -}; - -/***************************************************************************** - * PD_TIMER private functions - * - * The view of timers to the outside world is enabled and disabled. Internally - * timers that are enabled are in the active and inactive states. An active - * timer has a valid timeout value that gets checked for expiration and can - * adjust the task wakeup time. An inactive timer is assumed to have expired - * already and will always return that it is still expired. This timer state - * will not adjust the task scheduling timeout value. - */ -static void pd_timer_inactive(int port, enum pd_task_timer timer) -{ - uint32_t mask = 1 << timer; - - if (PD_CHK_ACTIVE(port, mask)) { - PD_CLR_ACTIVE(port, mask); - - if (IS_ENABLED(CONFIG_CMD_PD_TIMER)) - count[port]--; - } - PD_CLR_DISABLED(port, mask); -} - -static bool pd_timer_is_active(int port, enum pd_task_timer timer) -{ - uint32_t mask = 1 << timer; - - return PD_CHK_ACTIVE(port, mask); -} - -static bool pd_timer_is_inactive(int port, enum pd_task_timer timer) -{ - uint32_t mask = 1 << timer; - - return !PD_CHK_ACTIVE(port, mask) && !PD_CHK_DISABLED(port, mask); -} - -/***************************************************************************** - * PD_TIMER public functions - */ -void pd_timer_init(int port) -{ - if (IS_ENABLED(CONFIG_CMD_PD_TIMER)) - count[port] = 0; - - PD_CLR_ACTIVE(port, PD_TIMERS_ALL_MASK); - PD_SET_DISABLED(port, PD_TIMERS_ALL_MASK); -} - -void pd_timer_enable(int port, enum pd_task_timer timer, uint32_t expires_us) -{ - uint32_t mask = 1 << timer; - - if (!PD_CHK_ACTIVE(port, mask)) { - PD_SET_ACTIVE(port, mask); - - if (IS_ENABLED(CONFIG_CMD_PD_TIMER)) { - count[port]++; - if (count[port] > max_count[port]) - max_count[port] = count[port]; - } - } - PD_CLR_DISABLED(port, mask); - timer_expires[port][timer] = get_time().val + expires_us; -} - -void pd_timer_disable(int port, enum pd_task_timer timer) -{ - uint32_t mask = 1 << timer; - - if (PD_CHK_ACTIVE(port, mask)) { - PD_CLR_ACTIVE(port, mask); - - if (IS_ENABLED(CONFIG_CMD_PD_TIMER)) - count[port]--; - } - PD_SET_DISABLED(port, mask); -} - -void pd_timer_disable_range(int port, enum pd_timer_range range) -{ - int start, end; - enum pd_task_timer timer; - - switch (range) { - case PE_TIMER_RANGE: - start = PE_TIMER_START; - end = PE_TIMER_END; - break; - case PR_TIMER_RANGE: - start = PR_TIMER_START; - end = PR_TIMER_END; - break; - case TC_TIMER_RANGE: - start = TC_TIMER_START; - end = TC_TIMER_END; - break; - default: - return; - } - - for (timer = start; timer <= end; ++timer) - pd_timer_disable(port, timer); -} - -bool pd_timer_is_disabled(int port, enum pd_task_timer timer) -{ - uint32_t mask = 1 << timer; - - return PD_CHK_DISABLED(port, mask); -} - -bool pd_timer_is_expired(int port, enum pd_task_timer timer) -{ - if (pd_timer_is_active(port, timer)) { - if (get_time().val >= timer_expires[port][timer]) { - pd_timer_inactive(port, timer); - return true; - } - return false; - } - return pd_timer_is_inactive(port, timer); -} - -void pd_timer_manage_expired(int port) -{ - int timer; - - if (timer_active[port]) - for (timer = 0; timer < MAX_PD_TIMERS; ++timer) - if (pd_timer_is_active(port, timer) && - pd_timer_is_expired(port, timer)) - pd_timer_inactive(port, timer); -} - -int pd_timer_next_expiration(int port) -{ - int timer; - int ret_value = MAX_EXPIRE; - uint64_t now = get_time().val; - - for (timer = 0; timer < MAX_PD_TIMERS; ++timer) { - /* Only use active timers for the next expired value */ - if (pd_timer_is_active(port, timer)) { - int delta; - uint64_t t_value = timer_expires[port][timer]; - - if (t_value <= now) { - ret_value = EXPIRE_NOW; - break; - } - - delta = t_value - now; - if (ret_value > delta) - ret_value = delta; - } - } - - if (ret_value == MAX_EXPIRE) - ret_value = NO_TIMEOUT; - - return ret_value; -} - -#ifdef CONFIG_CMD_PD_TIMER -void pd_timer_dump(int port) -{ - int timer; - uint64_t now = get_time().val; - - ccprints("Timers(%d): cur=%d max=%d", - port, count[port], max_count[port]); - - for (timer = 0; timer < MAX_PD_TIMERS; ++timer) { - if (pd_timer_is_disabled(port, timer)) { - continue; - } else if (pd_timer_is_active(port, timer)) { - uint32_t delta = 0; - - if (now < timer_expires[port][timer]) - delta = timer_expires[port][timer] - now; - - ccprints("[%2d] Active: %s (%d%s)", - timer, pd_timer_names[timer], (uint32_t)delta, - tc_event_loop_is_paused(port) - ? "-PAUSED" - : ""); - } else { - ccprints("[%2d] Inactive: %s", - timer, pd_timer_names[timer]); - } - } -} -#endif /* CONFIG_CMD_PD_TIMER */ diff --git a/common/usbc/usb_pe_ctvpd_sm.c b/common/usbc/usb_pe_ctvpd_sm.c deleted file mode 100644 index 346a57a461..0000000000 --- a/common/usbc/usb_pe_ctvpd_sm.c +++ /dev/null @@ -1,237 +0,0 @@ -/* Copyright 2019 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "common.h" -#include "console.h" -#include "task.h" -#include "util.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" -#include "usb_pe_sm.h" -#include "usb_prl_sm.h" -#include "usb_pd_tcpm.h" -#include "usb_tc_sm.h" -#include "usb_emsg.h" -#include "usb_sm.h" - -/* USB Policy Engine Charge-Through VCONN Powered Device module */ - -/* Policy Engine Flags */ -#define PE_FLAGS_MSG_RECEIVED BIT(0) - -/** - * This is the PE Port object that contains information needed to - * implement a VCONN and Charge-Through VCONN Powered Device. - */ -static struct policy_engine { - /* state machine context */ - struct sm_ctx ctx; - /* port flags, see PE_FLAGS_* */ - uint32_t flags; -} pe[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* List of all policy-engine-level states */ -enum usb_pe_state { - PE_REQUEST, -}; - -/* Forward declare the full list of states. This is indexed by usb_pe_states */ -static const struct usb_state pe_states[]; - -static void set_state_pe(const int port, enum usb_pe_state new_state) -{ - set_state(port, &pe[port].ctx, &pe_states[new_state]); -} - -static void pe_init(int port) -{ - const struct sm_ctx cleared = {}; - - pe[port].flags = 0; - pe[port].ctx = cleared; - set_state_pe(port, PE_REQUEST); -} - -bool pe_in_frs_mode(int port) -{ - /* Will never be in FRS mode */ - return false; -} - -bool pe_in_local_ams(int port) -{ - /* We never start a local AMS */ - return false; -} - -void pe_run(int port, int evt, int en) -{ - static enum sm_local_state local_state[CONFIG_USB_PD_PORT_MAX_COUNT]; - - switch (local_state[port]) { - case SM_PAUSED: - if (!en) - break; - /* fall through */ - case SM_INIT: - pe_init(port); - local_state[port] = SM_RUN; - /* fall through */ - case SM_RUN: - if (en) - run_state(port, &pe[port].ctx); - else - local_state[port] = SM_PAUSED; - break; - } -} - -void pe_message_received(int port) -{ - pe[port].flags |= PE_FLAGS_MSG_RECEIVED; - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -/** - * NOTE: - * The Charge-Through Vconn Powered Device's Policy Engine is very - * simple and no implementation is needed for the following functions - * that might be called by the Protocol Layer. - */ - -void pe_hard_reset_sent(int port) -{ - /* No implementation needed by this policy engine */ -} - -void pe_got_hard_reset(int port) -{ - /* No implementation needed by this policy engine */ -} - -void pe_report_error(int port, enum pe_error e, enum tcpci_msg_type type) -{ - /* No implementation needed by this policy engine */ -} - -void pe_report_discard(int port) -{ - /* No implementation needed by this policy engine */ -} - -void pe_got_soft_reset(int port) -{ - /* No implementation needed by this policy engine */ -} - -void pe_message_sent(int port) -{ - /* No implementation needed by this policy engine */ -} - -static void pe_request_run(const int port) -{ - uint32_t *payload = (uint32_t *)tx_emsg[port].buf; - uint32_t header = rx_emsg[port].header; - uint32_t vdo = *(uint32_t *)rx_emsg[port].buf; - - if (pe[port].flags & PE_FLAGS_MSG_RECEIVED) { - pe[port].flags &= ~PE_FLAGS_MSG_RECEIVED; - - /* - * Only support Structured VDM Discovery - * Identity message - */ - - if (PD_HEADER_TYPE(header) != PD_DATA_VENDOR_DEF) - return; - - if (PD_HEADER_CNT(header) == 0) - return; - - if (!PD_VDO_SVDM(vdo)) - return; - - if (PD_VDO_CMD(vdo) != CMD_DISCOVER_IDENT) - return; - -#ifdef CONFIG_USB_CTVPD - /* - * We have a valid DISCOVER IDENTITY message. - * Attempt to reset support timer - */ - tc_reset_support_timer(port); -#endif - /* Prepare to send ACK */ - - /* VDM Header */ - payload[0] = VDO( - USB_VID_GOOGLE, - 1, /* Structured VDM */ - VDO_SVDM_VERS(1) | - VDO_CMDT(CMDT_RSP_ACK) | - CMD_DISCOVER_IDENT); - - /* ID Header VDO */ - payload[1] = VDO_IDH( - 0, /* Not a USB Host */ - 1, /* Capable of being enumerated as USB Device */ - IDH_PTYPE_VPD, - 0, /* Modal Operation Not Supported */ - USB_VID_GOOGLE); - - /* Cert State VDO */ - payload[2] = 0; - - /* Product VDO */ - payload[3] = VDO_PRODUCT( - CONFIG_USB_PID, - USB_BCD_DEVICE); - - /* VPD VDO */ - payload[4] = VDO_VPD( - VPD_HW_VERSION, - VPD_FW_VERSION, - VPD_MAX_VBUS_20V, - IS_ENABLED(CONFIG_USB_CTVPD) ? VPD_CT_CURRENT - : 0, - IS_ENABLED(CONFIG_USB_CTVPD) ? VPD_VBUS_IMP( - VPD_VBUS_IMPEDANCE) - : 0, - IS_ENABLED(CONFIG_USB_CTVPD) ? VPD_GND_IMP( - VPD_GND_IMPEDANCE) - : 0, - IS_ENABLED(CONFIG_USB_CTVPD) ? VPD_CTS_SUPPORTED - : VPD_CTS_NOT_SUPPORTED); - - /* 20 bytes, 5 data objects */ - tx_emsg[port].len = 20; - - /* Set to highest revision supported by both ports. */ - prl_set_rev(port, TCPCI_MSG_SOP_PRIME, - (PD_HEADER_REV(header) > PD_REV30) ? - PD_REV30 : PD_HEADER_REV(header)); - /* Send the ACK */ - prl_send_data_msg(port, TCPCI_MSG_SOP_PRIME, - PD_DATA_VENDOR_DEF); - } -} - -/* All policy-engine-level states. */ -static const struct usb_state pe_states[] = { - [PE_REQUEST] = { - .run = pe_request_run, - }, -}; - -#ifdef TEST_BUILD -const struct test_sm_data test_pe_sm_data[] = { - { - .base = pe_states, - .size = ARRAY_SIZE(pe_states), - }, -}; -const int test_pe_sm_data_size = ARRAY_SIZE(test_pe_sm_data); -#endif diff --git a/common/usbc/usb_pe_drp_sm.c b/common/usbc/usb_pe_drp_sm.c deleted file mode 100644 index 096f689b0a..0000000000 --- a/common/usbc/usb_pe_drp_sm.c +++ /dev/null @@ -1,7486 +0,0 @@ -/* Copyright 2019 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "atomic.h" -#include "battery.h" -#include "battery_smart.h" -#include "charge_manager.h" -#include "charge_state.h" -#include "common.h" -#include "console.h" -#include "dps.h" -#include "driver/tcpm/tcpm.h" -#include "ec_commands.h" -#include "hooks.h" -#include "host_command.h" -#include "stdbool.h" -#include "system.h" -#include "task.h" -#include "tcpm/tcpm.h" -#include "util.h" -#include "usb_common.h" -#include "usb_dp_alt_mode.h" -#include "usb_mode.h" -#include "usb_pd_dpm.h" -#include "usb_pd_policy.h" -#include "usb_pd.h" -#include "usb_pd_tcpm.h" -#include "usb_pd_timer.h" -#include "usb_pe_sm.h" -#include "usb_tbt_alt_mode.h" -#include "usb_prl_sm.h" -#include "usb_tc_sm.h" -#include "usb_emsg.h" -#include "usb_sm.h" -#include "usbc_ppc.h" - -/* - * USB Policy Engine Sink / Source module - * - * Based on Revision 3.0, Version 1.2 of - * the USB Power Delivery Specification. - */ - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -#define CPRINTF_LX(x, format, args...) \ - do { \ - if (pe_debug_level >= x) \ - CPRINTF(format, ## args); \ - } while (0) -#define CPRINTF_L1(format, args...) CPRINTF_LX(1, format, ## args) -#define CPRINTF_L2(format, args...) CPRINTF_LX(2, format, ## args) -#define CPRINTF_L3(format, args...) CPRINTF_LX(3, format, ## args) - -#define CPRINTS_LX(x, format, args...) \ - do { \ - if (pe_debug_level >= x) \ - CPRINTS(format, ## args); \ - } while (0) -#define CPRINTS_L1(format, args...) CPRINTS_LX(1, format, ## args) -#define CPRINTS_L2(format, args...) CPRINTS_LX(2, format, ## args) -#define CPRINTS_L3(format, args...) CPRINTS_LX(3, format, ## args) - -#define PE_SET_FLAG(port, flag) atomic_or(&pe[port].flags, (flag)) -#define PE_CLR_FLAG(port, flag) atomic_clear_bits(&pe[port].flags, (flag)) -#define PE_CHK_FLAG(port, flag) (pe[port].flags & (flag)) - -/* - * These macros SET, CLEAR, and CHECK, a DPM (Device Policy Manager) - * Request. The Requests are listed in usb_pe_sm.h. - */ -#define PE_SET_DPM_REQUEST(port, req) atomic_or(&pe[port].dpm_request, (req)) -#define PE_CLR_DPM_REQUEST(port, req) \ - atomic_clear_bits(&pe[port].dpm_request, (req)) -#define PE_CHK_DPM_REQUEST(port, req) (pe[port].dpm_request & (req)) - -/* - * Policy Engine Layer Flags - * These are reproduced in test/usb_pe.h. If they change here, they must change - * there. - */ - -/* At least one successful PD communication packet received from port partner */ -#define PE_FLAGS_PD_CONNECTION BIT(0) -/* Accept message received from port partner */ -#define PE_FLAGS_ACCEPT BIT(1) -/* Power Supply Ready message received from port partner */ -#define PE_FLAGS_PS_READY BIT(2) -/* Protocol Error was determined based on error recovery current state */ -#define PE_FLAGS_PROTOCOL_ERROR BIT(3) -/* Set if we are in Modal Operation */ -#define PE_FLAGS_MODAL_OPERATION BIT(4) -/* A message we requested to be sent has been transmitted */ -#define PE_FLAGS_TX_COMPLETE BIT(5) -/* A message sent by a port partner has been received */ -#define PE_FLAGS_MSG_RECEIVED BIT(6) -/* A hard reset has been requested but has not been sent, not currently used */ -#define PE_FLAGS_HARD_RESET_PENDING BIT(7) -/* Port partner sent a Wait message. Wait before we resend our message */ -#define PE_FLAGS_WAIT BIT(8) -/* An explicit contract is in place with our port partner */ -#define PE_FLAGS_EXPLICIT_CONTRACT BIT(9) -/* Waiting for Sink Capabailities timed out. Used for retry error handling */ -#define PE_FLAGS_SNK_WAIT_CAP_TIMEOUT BIT(10) -/* Power Supply voltage/current transition timed out */ -#define PE_FLAGS_PS_TRANSITION_TIMEOUT BIT(11) -/* Flag to note current Atomic Message Sequence is interruptible */ -#define PE_FLAGS_INTERRUPTIBLE_AMS BIT(12) -/* Flag to note Power Supply reset has completed */ -#define PE_FLAGS_PS_RESET_COMPLETE BIT(13) -/* VCONN swap operation has completed */ -#define PE_FLAGS_VCONN_SWAP_COMPLETE BIT(14) -/* Flag to note no more setup VDMs (discovery, etc.) should be sent */ -#define PE_FLAGS_VDM_SETUP_DONE BIT(15) -/* Flag to note PR Swap just completed for Startup entry */ -#define PE_FLAGS_PR_SWAP_COMPLETE BIT(16) -/* Flag to note Port Discovery port partner replied with BUSY */ -#define PE_FLAGS_VDM_REQUEST_BUSY BIT(17) -/* Flag to note Port Discovery port partner replied with NAK */ -#define PE_FLAGS_VDM_REQUEST_NAKED BIT(18) -/* Flag to note FRS/PRS context in shared state machine path */ -#define PE_FLAGS_FAST_ROLE_SWAP_PATH BIT(19) -/* Flag to note if FRS listening is enabled */ -#define PE_FLAGS_FAST_ROLE_SWAP_ENABLED BIT(20) -/* Flag to note TCPC passed on FRS signal from port partner */ -#define PE_FLAGS_FAST_ROLE_SWAP_SIGNALED BIT(21) -/* TODO: POLICY decision: Triggers a DR SWAP attempt from UFP to DFP */ -#define PE_FLAGS_DR_SWAP_TO_DFP BIT(22) -/* - * TODO: POLICY decision - * Flag to trigger a message resend after receiving a WAIT from port partner - */ -#define PE_FLAGS_WAITING_PR_SWAP BIT(23) -/* FLAG is set when an AMS is initiated locally. ie. AP requested a PR_SWAP */ -#define PE_FLAGS_LOCALLY_INITIATED_AMS BIT(24) -/* Flag to note the first message sent in PE_SRC_READY and PE_SNK_READY */ -#define PE_FLAGS_FIRST_MSG BIT(25) -/* Flag to continue a VDM request if it was interrupted */ -#define PE_FLAGS_VDM_REQUEST_CONTINUE BIT(26) -/* TODO: POLICY decision: Triggers a Vconn SWAP attempt to on */ -#define PE_FLAGS_VCONN_SWAP_TO_ON BIT(27) -/* FLAG to track that VDM request to port partner timed out */ -#define PE_FLAGS_VDM_REQUEST_TIMEOUT BIT(28) -/* FLAG to note message was discarded due to incoming message */ -#define PE_FLAGS_MSG_DISCARDED BIT(29) -/* FLAG to note that hard reset can't be performed due to battery low */ -#define PE_FLAGS_SNK_WAITING_BATT BIT(30) - -/* Message flags which should not persist on returning to ready state */ -#define PE_FLAGS_READY_CLR (PE_FLAGS_LOCALLY_INITIATED_AMS \ - | PE_FLAGS_MSG_DISCARDED \ - | PE_FLAGS_VDM_REQUEST_TIMEOUT \ - | PE_FLAGS_INTERRUPTIBLE_AMS) - -/* - * Combination to check whether a reply to a message was received. Our message - * should have sent (i.e. not been discarded) and a partner message is ready to - * process. - * - * When chunking is disabled (ex. for PD 2.0), these flags will set - * on the same run cycle. With chunking, received message will take an - * additional cycle to be flagged. - */ -#define PE_CHK_REPLY(port) (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED) && \ - !PE_CHK_FLAG(port, PE_FLAGS_MSG_DISCARDED)) - -/* 6.7.3 Hard Reset Counter */ -#define N_HARD_RESET_COUNT 2 - -/* 6.7.4 Capabilities Counter */ -#define N_CAPS_COUNT 25 - -/* 6.7.5 Discover Identity Counter */ -/* - * NOTE: The Protocol Layer tries to send a message 3 time before giving up, - * so a Discover Identity SOP' message will be sent 3*6 = 18 times (slightly - * less than spec maximum of 20). This counter applies only to cable plug - * discovery. - */ -#define N_DISCOVER_IDENTITY_COUNT 6 - -/* - * It is permitted to send SOP' Discover Identity messages before a PD contract - * is in place. However, this is only beneficial if the cable powers up quickly - * solely from VCONN. Limit the number of retries without a contract to - * ensure we attempt some cable discovery after a contract is in place. - */ -#define N_DISCOVER_IDENTITY_PRECONTRACT_LIMIT 2 - -/* - * Once this limit of SOP' Discover Identity messages has been set, downgrade - * to PD 2.0 in case the cable is non-compliant about GoodCRC-ing higher - * revisions. This limit should be higher than the precontract limit. - */ -#define N_DISCOVER_IDENTITY_PD3_0_LIMIT 4 - -/* - * tDiscoverIdentity is only defined while an explicit contract is in place, so - * extend the interval between retries pre-contract. - */ -#define PE_T_DISCOVER_IDENTITY_NO_CONTRACT (200*MSEC) - -/* - * Only VCONN source can communicate with the cable plug. Hence, try VCONN swap - * 3 times before giving up. - * - * Note: This is not a part of power delivery specification - */ -#define N_VCONN_SWAP_COUNT 3 - -/* - * Counter to track how many times to attempt SRC to SNK PR swaps before giving - * up. - * - * Note: This is not a part of power delivery specification - */ -#define N_SNK_SRC_PR_SWAP_COUNT 5 - -/* - * ChromeOS policy: - * For PD2.0, We must be DFP before sending Discover Identity message - * to the port partner. Attempt to DR SWAP from UFP to DFP - * N_DR_SWAP_ATTEMPT_COUNT times before giving up on sending a - * Discover Identity message. - */ -#define N_DR_SWAP_ATTEMPT_COUNT 5 - -#define TIMER_DISABLED 0xffffffffffffffff /* Unreachable time in future */ - -/* - * The time that we allow the port partner to send any messages after an - * explicit contract is established. 200ms was chosen somewhat arbitrarily as - * it should be long enough for sources to decide to send a message if they were - * going to, but not so long that a "low power charger connected" notification - * would be shown in the chrome OS UI. Setting t0o large a delay can cause - * problems if the PD discovery time exceeds 1s (tAMETimeout) - */ -#define SRC_SNK_READY_HOLD_OFF_US (200 * MSEC) - -/* - * Function pointer to a Structured Vendor Defined Message (SVDM) response - * function defined in the board's usb_pd_policy.c file. - */ -typedef int (*svdm_rsp_func)(int port, uint32_t *payload); - -/* List of all Policy Engine level states */ -enum usb_pe_state { - /* Super States */ - PE_PRS_FRS_SHARED, - PE_VDM_SEND_REQUEST, - - /* Normal States */ - PE_SRC_STARTUP, - PE_SRC_DISCOVERY, - PE_SRC_SEND_CAPABILITIES, - PE_SRC_NEGOTIATE_CAPABILITY, - PE_SRC_TRANSITION_SUPPLY, - PE_SRC_READY, - PE_SRC_DISABLED, - PE_SRC_CAPABILITY_RESPONSE, - PE_SRC_HARD_RESET, - PE_SRC_HARD_RESET_RECEIVED, - PE_SRC_TRANSITION_TO_DEFAULT, - PE_SNK_STARTUP, - PE_SNK_DISCOVERY, - PE_SNK_WAIT_FOR_CAPABILITIES, - PE_SNK_EVALUATE_CAPABILITY, - PE_SNK_SELECT_CAPABILITY, - PE_SNK_READY, - PE_SNK_HARD_RESET, - PE_SNK_TRANSITION_TO_DEFAULT, - PE_SNK_GIVE_SINK_CAP, - PE_SNK_GET_SOURCE_CAP, - PE_SNK_TRANSITION_SINK, - PE_SEND_SOFT_RESET, - PE_SOFT_RESET, - PE_SEND_NOT_SUPPORTED, - PE_SRC_PING, - PE_DRS_EVALUATE_SWAP, - PE_DRS_CHANGE, - PE_DRS_SEND_SWAP, - PE_PRS_SRC_SNK_EVALUATE_SWAP, - PE_PRS_SRC_SNK_TRANSITION_TO_OFF, - PE_PRS_SRC_SNK_ASSERT_RD, - PE_PRS_SRC_SNK_WAIT_SOURCE_ON, - PE_PRS_SRC_SNK_SEND_SWAP, - PE_PRS_SNK_SRC_EVALUATE_SWAP, - PE_PRS_SNK_SRC_TRANSITION_TO_OFF, - PE_PRS_SNK_SRC_ASSERT_RP, - PE_PRS_SNK_SRC_SOURCE_ON, - PE_PRS_SNK_SRC_SEND_SWAP, - PE_VCS_EVALUATE_SWAP, - PE_VCS_SEND_SWAP, - PE_VCS_WAIT_FOR_VCONN_SWAP, - PE_VCS_TURN_ON_VCONN_SWAP, - PE_VCS_TURN_OFF_VCONN_SWAP, - PE_VCS_SEND_PS_RDY_SWAP, - PE_VCS_CBL_SEND_SOFT_RESET, - PE_VDM_IDENTITY_REQUEST_CBL, - PE_INIT_PORT_VDM_IDENTITY_REQUEST, - PE_INIT_VDM_SVIDS_REQUEST, - PE_INIT_VDM_MODES_REQUEST, - PE_VDM_REQUEST_DPM, - PE_VDM_RESPONSE, - PE_HANDLE_CUSTOM_VDM_REQUEST, - PE_WAIT_FOR_ERROR_RECOVERY, - PE_BIST_TX, - PE_DEU_SEND_ENTER_USB, - PE_DR_GET_SINK_CAP, - PE_DR_SNK_GIVE_SOURCE_CAP, - PE_DR_SRC_GET_SOURCE_CAP, - - /* PD3.0 only states below here*/ - PE_FRS_SNK_SRC_START_AMS, - PE_GIVE_BATTERY_CAP, - PE_GIVE_BATTERY_STATUS, - PE_SEND_ALERT, - PE_SRC_CHUNK_RECEIVED, - PE_SNK_CHUNK_RECEIVED, - PE_VCS_FORCE_VCONN, -}; - -/* - * The result of a previously sent DPM request; used by PE_VDM_SEND_REQUEST to - * indicate to child states when they need to handle a response. - */ -enum vdm_response_result { - /* The parent state is still waiting for a response. */ - VDM_RESULT_WAITING, - /* - * The parent state parsed a message, but there is nothing for the child - * to handle, e.g. BUSY. - */ - VDM_RESULT_NO_ACTION, - /* The parent state processed an ACK response. */ - VDM_RESULT_ACK, - /* - * The parent state processed a NAK-like response (NAK, Not Supported, - * or response timeout. - */ - VDM_RESULT_NAK, -}; - -/* Forward declare the full list of states. This is indexed by usb_pe_state */ -static const struct usb_state pe_states[]; - -/* - * We will use DEBUG LABELS if we will be able to print (COMMON RUNTIME) - * and either CONFIG_USB_PD_DEBUG_LEVEL is not defined (no override) or - * we are overriding and the level is not DISABLED. - * - * If we can't print or the CONFIG_USB_PD_DEBUG_LEVEL is defined to be 0 - * then the DEBUG LABELS will be removed from the build. - */ -#if defined(CONFIG_COMMON_RUNTIME) && \ - (!defined(CONFIG_USB_PD_DEBUG_LEVEL) || \ - (CONFIG_USB_PD_DEBUG_LEVEL > 0)) -#define USB_PD_DEBUG_LABELS -#endif - -/* List of human readable state names for console debugging */ -__maybe_unused static __const_data const char * const pe_state_names[] = { - /* Super States */ -#ifdef CONFIG_USB_PD_REV30 - [PE_PRS_FRS_SHARED] = "SS:PE_PRS_FRS_SHARED", -#endif - [PE_VDM_SEND_REQUEST] = "SS:PE_VDM_Send_Request", - - /* Normal States */ - [PE_SRC_STARTUP] = "PE_SRC_Startup", - [PE_SRC_DISCOVERY] = "PE_SRC_Discovery", - [PE_SRC_SEND_CAPABILITIES] = "PE_SRC_Send_Capabilities", - [PE_SRC_NEGOTIATE_CAPABILITY] = "PE_SRC_Negotiate_Capability", - [PE_SRC_TRANSITION_SUPPLY] = "PE_SRC_Transition_Supply", - [PE_SRC_READY] = "PE_SRC_Ready", - [PE_SRC_DISABLED] = "PE_SRC_Disabled", - [PE_SRC_CAPABILITY_RESPONSE] = "PE_SRC_Capability_Response", - [PE_SRC_HARD_RESET] = "PE_SRC_Hard_Reset", - [PE_SRC_HARD_RESET_RECEIVED] = "PE_SRC_Hard_Reset_Received", - [PE_SRC_TRANSITION_TO_DEFAULT] = "PE_SRC_Transition_to_default", - [PE_SNK_STARTUP] = "PE_SNK_Startup", - [PE_SNK_DISCOVERY] = "PE_SNK_Discovery", - [PE_SNK_WAIT_FOR_CAPABILITIES] = "PE_SNK_Wait_for_Capabilities", - [PE_SNK_EVALUATE_CAPABILITY] = "PE_SNK_Evaluate_Capability", - [PE_SNK_SELECT_CAPABILITY] = "PE_SNK_Select_Capability", - [PE_SNK_READY] = "PE_SNK_Ready", - [PE_SNK_HARD_RESET] = "PE_SNK_Hard_Reset", - [PE_SNK_TRANSITION_TO_DEFAULT] = "PE_SNK_Transition_to_default", - [PE_SNK_GIVE_SINK_CAP] = "PE_SNK_Give_Sink_Cap", - [PE_SNK_GET_SOURCE_CAP] = "PE_SNK_Get_Source_Cap", - [PE_SNK_TRANSITION_SINK] = "PE_SNK_Transition_Sink", - [PE_SEND_SOFT_RESET] = "PE_Send_Soft_Reset", - [PE_SOFT_RESET] = "PE_Soft_Reset", - [PE_SEND_NOT_SUPPORTED] = "PE_Send_Not_Supported", - [PE_SRC_PING] = "PE_SRC_Ping", - [PE_DRS_EVALUATE_SWAP] = "PE_DRS_Evaluate_Swap", - [PE_DRS_CHANGE] = "PE_DRS_Change", - [PE_DRS_SEND_SWAP] = "PE_DRS_Send_Swap", - [PE_PRS_SRC_SNK_EVALUATE_SWAP] = "PE_PRS_SRC_SNK_Evaluate_Swap", - [PE_PRS_SRC_SNK_TRANSITION_TO_OFF] = "PE_PRS_SRC_SNK_Transition_To_Off", - [PE_PRS_SRC_SNK_ASSERT_RD] = "PE_PRS_SRC_SNK_Assert_Rd", - [PE_PRS_SRC_SNK_WAIT_SOURCE_ON] = "PE_PRS_SRC_SNK_Wait_Source_On", - [PE_PRS_SRC_SNK_SEND_SWAP] = "PE_PRS_SRC_SNK_Send_Swap", - [PE_PRS_SNK_SRC_EVALUATE_SWAP] = "PE_PRS_SNK_SRC_Evaluate_Swap", - [PE_PRS_SNK_SRC_TRANSITION_TO_OFF] = "PE_PRS_SNK_SRC_Transition_To_Off", - [PE_PRS_SNK_SRC_ASSERT_RP] = "PE_PRS_SNK_SRC_Assert_Rp", - [PE_PRS_SNK_SRC_SOURCE_ON] = "PE_PRS_SNK_SRC_Source_On", - [PE_PRS_SNK_SRC_SEND_SWAP] = "PE_PRS_SNK_SRC_Send_Swap", -#ifdef CONFIG_USBC_VCONN - [PE_VCS_EVALUATE_SWAP] = "PE_VCS_Evaluate_Swap", - [PE_VCS_SEND_SWAP] = "PE_VCS_Send_Swap", - [PE_VCS_WAIT_FOR_VCONN_SWAP] = "PE_VCS_Wait_For_Vconn_Swap", - [PE_VCS_TURN_ON_VCONN_SWAP] = "PE_VCS_Turn_On_Vconn_Swap", - [PE_VCS_TURN_OFF_VCONN_SWAP] = "PE_VCS_Turn_Off_Vconn_Swap", - [PE_VCS_SEND_PS_RDY_SWAP] = "PE_VCS_Send_Ps_Rdy_Swap", - [PE_VCS_CBL_SEND_SOFT_RESET] = "PE_VCS_CBL_Send_Soft_Reset", -#endif - [PE_VDM_IDENTITY_REQUEST_CBL] = "PE_VDM_Identity_Request_Cbl", - [PE_INIT_PORT_VDM_IDENTITY_REQUEST] = - "PE_INIT_PORT_VDM_Identity_Request", - [PE_INIT_VDM_SVIDS_REQUEST] = "PE_INIT_VDM_SVIDs_Request", - [PE_INIT_VDM_MODES_REQUEST] = "PE_INIT_VDM_Modes_Request", - [PE_VDM_REQUEST_DPM] = "PE_VDM_Request_DPM", - [PE_VDM_RESPONSE] = "PE_VDM_Response", - [PE_HANDLE_CUSTOM_VDM_REQUEST] = "PE_Handle_Custom_Vdm_Request", - [PE_WAIT_FOR_ERROR_RECOVERY] = "PE_Wait_For_Error_Recovery", - [PE_BIST_TX] = "PE_Bist_TX", - [PE_DEU_SEND_ENTER_USB] = "PE_DEU_Send_Enter_USB", - [PE_DR_GET_SINK_CAP] = "PE_DR_Get_Sink_Cap", - [PE_DR_SNK_GIVE_SOURCE_CAP] = "PE_DR_SNK_Give_Source_Cap", - [PE_DR_SRC_GET_SOURCE_CAP] = "PE_DR_SRC_Get_Source_Cap", - - /* PD3.0 only states below here*/ -#ifdef CONFIG_USB_PD_REV30 - [PE_FRS_SNK_SRC_START_AMS] = "PE_FRS_SNK_SRC_Start_Ams", -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - [PE_GIVE_BATTERY_CAP] = "PE_Give_Battery_Cap", - [PE_GIVE_BATTERY_STATUS] = "PE_Give_Battery_Status", - [PE_SEND_ALERT] = "PE_Send_Alert", -#else - [PE_SRC_CHUNK_RECEIVED] = "PE_SRC_Chunk_Received", - [PE_SNK_CHUNK_RECEIVED] = "PE_SNK_Chunk_Received", -#endif -#ifdef CONFIG_USBC_VCONN - [PE_VCS_FORCE_VCONN] = "PE_VCS_Force_Vconn", -#endif -#endif /* CONFIG_USB_PD_REV30 */ -}; - -#ifndef CONFIG_USBC_VCONN -GEN_NOT_SUPPORTED(PE_VCS_EVALUATE_SWAP); -#define PE_VCS_EVALUATE_SWAP PE_VCS_EVALUATE_SWAP_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_VCS_SEND_SWAP); -#define PE_VCS_SEND_SWAP PE_VCS_SEND_SWAP_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_VCS_WAIT_FOR_VCONN_SWAP); -#define PE_VCS_WAIT_FOR_VCONN_SWAP PE_VCS_WAIT_FOR_VCONN_SWAP_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_VCS_TURN_ON_VCONN_SWAP); -#define PE_VCS_TURN_ON_VCONN_SWAP PE_VCS_TURN_ON_VCONN_SWAP_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_VCS_TURN_OFF_VCONN_SWAP); -#define PE_VCS_TURN_OFF_VCONN_SWAP PE_VCS_TURN_OFF_VCONN_SWAP_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_VCS_SEND_PS_RDY_SWAP); -#define PE_VCS_SEND_PS_RDY_SWAP PE_VCS_SEND_PS_RDY_SWAP_NOT_SUPPORTED -#endif /* CONFIG_USBC_VCONN */ - -#ifndef CONFIG_USB_PD_REV30 -GEN_NOT_SUPPORTED(PE_FRS_SNK_SRC_START_AMS); -#define PE_FRS_SNK_SRC_START_AMS PE_FRS_SNK_SRC_START_AMS_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_PRS_FRS_SHARED); -#define PE_PRS_FRS_SHARED PE_PRS_FRS_SHARED_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_SRC_CHUNK_RECEIVED); -#define PE_SRC_CHUNK_RECEIVED PE_SRC_CHUNK_RECEIVED_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_SNK_CHUNK_RECEIVED); -#define PE_SNK_CHUNK_RECEIVED PE_SNK_CHUNK_RECEIVED_NOT_SUPPORTED -#endif /* CONFIG_USB_PD_REV30 */ - -#if !defined(CONFIG_USBC_VCONN) || !defined(CONFIG_USB_PD_REV30) -GEN_NOT_SUPPORTED(PE_VCS_FORCE_VCONN); -#define PE_VCS_FORCE_VCONN PE_VCS_FORCE_VCONN_NOT_SUPPORTED -#endif - -#ifndef CONFIG_USB_PD_EXTENDED_MESSAGES -GEN_NOT_SUPPORTED(PE_GIVE_BATTERY_CAP); -#define PE_GIVE_BATTERY_CAP PE_GIVE_BATTERY_CAP_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_GIVE_BATTERY_STATUS); -#define PE_GIVE_BATTERY_STATUS PE_GIVE_BATTERY_STATUS_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_SEND_ALERT); -#define PE_SEND_ALERT PE_SEND_ALERT_NOT_SUPPORTED -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ - -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES -GEN_NOT_SUPPORTED(PE_SRC_CHUNK_RECEIVED); -#define PE_SRC_CHUNK_RECEIVED PE_SRC_CHUNK_RECEIVED_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PE_SNK_CHUNK_RECEIVED); -#define PE_SNK_CHUNK_RECEIVED PE_SNK_CHUNK_RECEIVED_NOT_SUPPORTED -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ - -static enum sm_local_state local_state[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* - * Common message send checking - * - * PE_MSG_SEND_PENDING: A message has been requested to be sent. It has - * not been GoodCRCed or Discarded. - * PE_MSG_SEND_COMPLETED: The message that was requested has been sent. - * This will only be returned one time and any other - * request for message send status will just return - * PE_MSG_SENT. This message actually includes both - * The COMPLETED and the SENT bit for easier checking. - * NOTE: PE_MSG_SEND_COMPLETED will only be returned - * a single time, directly after TX_COMPLETE. - * PE_MSG_SENT: The message that was requested to be sent has - * successfully been transferred to the partner. - * PE_MSG_DISCARDED: The message that was requested to be sent was - * discarded. The partner did not receive it. - * NOTE: PE_MSG_DISCARDED will only be returned - * one time and it is up to the caller to process - * what ever is needed to handle the Discard. - * PE_MSG_DPM_DISCARDED: The message that was requested to be sent was - * discarded and an active DRP_REQUEST was active. - * The DRP_REQUEST that was current will be moved - * back to the drp_requests so it can be performed - * later if needed. - * NOTE: PE_MSG_DPM_DISCARDED will only be returned - * one time and it is up to the caller to process - * what ever is needed to handle the Discard. - */ -enum pe_msg_check { - PE_MSG_SEND_PENDING = BIT(0), - PE_MSG_SENT = BIT(1), - PE_MSG_DISCARDED = BIT(2), - - PE_MSG_SEND_COMPLETED = BIT(3) | PE_MSG_SENT, - PE_MSG_DPM_DISCARDED = BIT(4) | PE_MSG_DISCARDED, -}; -static void pe_sender_response_msg_entry(const int port); -static enum pe_msg_check pe_sender_response_msg_run(const int port); -static void pe_sender_response_msg_exit(const int port); - -/* Debug log level - higher number == more log */ -#ifdef CONFIG_USB_PD_DEBUG_LEVEL -static const enum debug_level pe_debug_level = CONFIG_USB_PD_DEBUG_LEVEL; -#else -static enum debug_level pe_debug_level = DEBUG_LEVEL_1; -#endif - -/* - * Policy Engine State Machine Object - */ -static struct policy_engine { - /* state machine context */ - struct sm_ctx ctx; - /* current port power role (SOURCE or SINK) */ - enum pd_power_role power_role; - /* current port data role (DFP or UFP) */ - enum pd_data_role data_role; - /* state machine flags */ - uint32_t flags; - /* Device Policy Manager Request */ - uint32_t dpm_request; - uint32_t dpm_curr_request; - /* last requested voltage PDO index */ - int requested_idx; - - /* - * Port events - PD_STATUS_EVENT_* values - * Set from PD task but may be cleared by host command - */ - uint32_t events; - - /* port address where soft resets are sent */ - enum tcpci_msg_type soft_reset_sop; - - /* Current limit / voltage based on the last request message */ - uint32_t curr_limit; - uint32_t supply_voltage; - - /* PD_VDO_INVALID is used when there is an invalid VDO */ - int32_t ama_vdo; - int32_t vpd_vdo; - /* Alternate mode discovery results */ - struct pd_discovery discovery[DISCOVERY_TYPE_COUNT]; - /* Active alternate modes */ - struct partner_active_modes partner_amodes[AMODE_TYPE_COUNT]; - - /* Partner type to send */ - enum tcpci_msg_type tx_type; - - /* VDM - used to send information to shared VDM Request state */ - uint32_t vdm_cnt; - uint32_t vdm_data[VDO_HDR_SIZE + VDO_MAX_SIZE]; - uint8_t vdm_ack_min_data_objects; - - /* Counters */ - - /* - * This counter is used to retry the Hard Reset whenever there is no - * response from the remote device. - */ - uint32_t hard_reset_counter; - - /* - * This counter is used to count the number of Source_Capabilities - * Messages which have been sent by a Source at power up or after a - * Hard Reset. - */ - uint32_t caps_counter; - - /* - * This counter maintains a count of Discover Identity Messages sent - * to a cable. If no GoodCRC messages are received after - * nDiscoverIdentityCount, the port shall not send any further - * SOP'/SOP'' messages. - */ - uint32_t discover_identity_counter; - /* - * For PD2.0, we need to be a DFP before sending a discovery identity - * message to our port partner. This counter keeps track of how - * many attempts to DR SWAP from UFP to DFP. - */ - uint32_t dr_swap_attempt_counter; - - /* - * This counter tracks how many PR Swap messages are sent when the - * partner responds with a Wait message. Only used during SRC to SNK - * PR swaps - */ - uint8_t src_snk_pr_swap_counter; - - /* - * This counter maintains a count of VCONN swap requests. If VCONN swap - * isn't successful after N_VCONN_SWAP_COUNT, the port calls - * dpm_vdm_naked(). - */ - uint8_t vconn_swap_counter; - - /* Last received source cap */ - uint32_t src_caps[PDO_MAX_OBJECTS]; - int src_cap_cnt; /* -1 on error retrieving source caps */ - - /* Last received sink cap */ - uint32_t snk_caps[PDO_MAX_OBJECTS]; - int snk_cap_cnt; - - /* Attached ChromeOS device id, RW hash, and current RO / RW image */ - uint16_t dev_id; - uint32_t dev_rw_hash[PD_RW_HASH_SIZE/4]; - enum ec_image current_image; -} pe[CONFIG_USB_PD_PORT_MAX_COUNT]; - -test_export_static enum usb_pe_state get_state_pe(const int port); -test_export_static void set_state_pe(const int port, - const enum usb_pe_state new_state); -static void pe_set_dpm_curr_request(const int port, const int request); -/* - * The spec. revision is used to index into this array. - * PD 1.0 (VDO 1.0) - return VDM_VER10 - * PD 2.0 (VDO 1.0) - return VDM_VER10 - * PD 3.0 (VDO 2.0) - return VDM_VER20 - */ -static const uint8_t vdo_ver[] = { - [PD_REV10] = VDM_VER10, - [PD_REV20] = VDM_VER10, - [PD_REV30] = VDM_VER20, -}; - -int pd_get_rev(int port, enum tcpci_msg_type type) -{ - return prl_get_rev(port, type); -} - -int pd_get_vdo_ver(int port, enum tcpci_msg_type type) -{ - enum pd_rev_type rev = prl_get_rev(port, type); - - if (rev < PD_REV30) - return vdo_ver[rev]; - else - return VDM_VER20; -} - -static void pe_set_ready_state(int port) -{ - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_READY); - else - set_state_pe(port, PE_SNK_READY); -} - -static inline void send_data_msg(int port, enum tcpci_msg_type type, - enum pd_data_msg_type msg) -{ - /* Clear any previous TX status before sending a new message */ - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - prl_send_data_msg(port, type, msg); -} - -static __maybe_unused inline void send_ext_data_msg( - int port, enum tcpci_msg_type type, enum pd_ext_msg_type msg) -{ - /* Clear any previous TX status before sending a new message */ - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - prl_send_ext_data_msg(port, type, msg); -} - -static inline void send_ctrl_msg(int port, enum tcpci_msg_type type, - enum pd_ctrl_msg_type msg) -{ - /* Clear any previous TX status before sending a new message */ - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - prl_send_ctrl_msg(port, type, msg); -} - -static void set_cable_rev(int port) -{ - /* - * If port partner runs PD 2.0, cable communication must - * also be PD 2.0 - */ - if (prl_get_rev(port, TCPCI_MSG_SOP) == PD_REV20) { - /* - * If the cable supports PD 3.0, but the port partner supports PD 2.0, - * redo the cable discover with PD 2.0 - */ - if (prl_get_rev(port, TCPCI_MSG_SOP_PRIME) == PD_REV30 && - pd_get_identity_discovery(port, TCPCI_MSG_SOP_PRIME) == - PD_DISC_COMPLETE) { - pd_set_identity_discovery(port, TCPCI_MSG_SOP_PRIME, - PD_DISC_NEEDED); - } - prl_set_rev(port, TCPCI_MSG_SOP_PRIME, PD_REV20); - } -} - -/* Compile-time insurance to ensure this code does not call into prl directly */ -#define prl_send_data_msg DO_NOT_USE -#define prl_send_ext_data_msg DO_NOT_USE -#define prl_send_ctrl_msg DO_NOT_USE - -static void pe_init(int port) -{ - pe[port].flags = 0; - pe[port].dpm_request = 0; - pe[port].dpm_curr_request = 0; - pd_timer_disable_range(port, PE_TIMER_RANGE); - pe[port].data_role = pd_get_data_role(port); - pe[port].tx_type = TCPCI_MSG_INVALID; - pe[port].events = 0; - - tc_pd_connection(port, 0); - - if (pd_get_power_role(port) == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_STARTUP); - else - set_state_pe(port, PE_SNK_STARTUP); -} - -int pe_is_running(int port) -{ - return local_state[port] == SM_RUN; -} - -bool pe_in_frs_mode(int port) -{ - return PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH); -} - -bool pe_in_local_ams(int port) -{ - return !!PE_CHK_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS); -} - -void pe_set_debug_level(enum debug_level debug_level) -{ -#ifndef CONFIG_USB_PD_DEBUG_LEVEL - pe_debug_level = debug_level; -#endif -} - -void pe_run(int port, int evt, int en) -{ - switch (local_state[port]) { - case SM_PAUSED: - if (!en) - break; - /* fall through */ - case SM_INIT: - pe_init(port); - local_state[port] = SM_RUN; - /* fall through */ - case SM_RUN: - if (!en) { - local_state[port] = SM_PAUSED; - /* - * While we are paused, exit all states and wait until - * initialized again. - */ - set_state(port, &pe[port].ctx, NULL); - break; - } - - /* - * 8.3.3.3.8 PE_SNK_Hard_Reset State - * The Policy Engine Shall transition to the PE_SNK_Hard_Reset - * state from any state when: - * - Hard Reset request from Device Policy Manager - * - * USB PD specification clearly states that we should go to - * PE_SNK_Hard_Reset from ANY state (including states in which - * port is source) when DPM requests that. This can lead to - * execute Hard Reset path for sink when actually our power - * role is source. In our implementation we will choose Hard - * Reset path depending on current power role. - */ - if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_HARD_RESET_SEND)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_HARD_RESET_SEND); - if (pd_get_power_role(port) == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_HARD_RESET); - else - set_state_pe(port, PE_SNK_HARD_RESET); - } - - /* - * Check for Fast Role Swap signal - * This is not a typical pattern for adding state changes. - * I added this here because FRS SIGNALED can happen at any - * state once we are listening for the signal and we want to - * make sure to handle it immediately. - */ - if (IS_ENABLED(CONFIG_USB_PD_REV30) && - PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_SIGNALED)) { - PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_SIGNALED); - set_state_pe(port, PE_FRS_SNK_SRC_START_AMS); - } - - /* Run state machine */ - run_state(port, &pe[port].ctx); - break; - } -} - -int pe_is_explicit_contract(int port) -{ - return PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT); -} - -void pe_message_received(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - PE_SET_FLAG(port, PE_FLAGS_MSG_RECEIVED); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void pe_hard_reset_sent(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - PE_CLR_FLAG(port, PE_FLAGS_HARD_RESET_PENDING); -} - -void pe_got_hard_reset(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - /* - * Transition from any state to the PE_SRC_Hard_Reset_Received or - * PE_SNK_Transition_to_default state when: - * 1) Hard Reset Signaling is detected. - */ - pe[port].power_role = pd_get_power_role(port); - - /* Exit BIST Test mode, in case the TCPC entered it. */ - tcpc_set_bist_test_mode(port, false); - - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_HARD_RESET_RECEIVED); - else - set_state_pe(port, PE_SNK_TRANSITION_TO_DEFAULT); -} - -#ifdef CONFIG_USB_PD_REV30 -/* - * pd_got_frs_signal - * - * Called by the handler that detects the FRS signal in order to - * switch PE states to complete the FRS that the hardware has - * started. - * - * If the PE is not running, generate an error recovery to turn off - * Vbus and get the port back into a known state. - */ -void pd_got_frs_signal(int port) -{ - if (pe_is_running(port)) - PE_SET_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_SIGNALED); - else - pd_set_error_recovery(port); - - task_wake(PD_PORT_TO_TASK_ID(port)); -} -#endif /* CONFIG_USB_PD_REV30 */ - -/* - * PE_Set_FRS_Enable - * - * This function should be called every time an explicit contract - * is disabled, to disable FRS. - * - * Enabling an explicit contract is not enough to enable FRS, it - * also requires a Sink Capability power requirement from a Source - * that supports FRS so we can determine if this is something we - * can handle. - */ -static void pe_set_frs_enable(int port, int enable) -{ - int current = PE_CHK_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_ENABLED); - - /* This should only be called from the PD task */ - if (!IS_ENABLED(TEST_BUILD)) - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - if (!IS_ENABLED(CONFIG_USB_PD_FRS) || !IS_ENABLED(CONFIG_USB_PD_REV30)) - return; - - /* Request an FRS change, only if the state has changed */ - if (!!current == !!enable) - return; - - pd_set_frs_enable(port, enable); - if (enable) { - int curr_limit = *pd_get_snk_caps(port) - & PDO_FIXED_FRS_CURR_MASK; - - typec_select_src_current_limit_rp(port, - curr_limit == - PDO_FIXED_FRS_CURR_3A0_AT_5V ? - TYPEC_RP_3A0 : TYPEC_RP_1A5); - PE_SET_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_ENABLED); - } else { - PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_ENABLED); - } -} - -void pe_set_explicit_contract(int port) -{ - PE_SET_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT); - - /* Set Rp for collision avoidance */ - if (IS_ENABLED(CONFIG_USB_PD_REV30)) - typec_update_cc(port); -} - -void pe_invalidate_explicit_contract(int port) -{ - pe_set_frs_enable(port, 0); - - PE_CLR_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT); - - /* Set Rp for current limit if still attached */ - if (IS_ENABLED(CONFIG_USB_PD_REV30) && pd_is_connected(port)) - typec_update_cc(port); -} - -void pd_notify_event(int port, uint32_t event_mask) -{ - atomic_or(&pe[port].events, event_mask); - - /* Notify the host that new events are available to read */ - pd_send_host_event(PD_EVENT_TYPEC); -} - -void pd_clear_events(int port, uint32_t clear_mask) -{ - atomic_clear_bits(&pe[port].events, clear_mask); -} - -uint32_t pd_get_events(int port) -{ - return pe[port].events; -} - -void pe_set_snk_caps(int port, int cnt, uint32_t *snk_caps) -{ - pe[port].snk_cap_cnt = cnt; - - memcpy(pe[port].snk_caps, snk_caps, sizeof(uint32_t) * cnt); -} - -const uint32_t * const pd_get_snk_caps(int port) -{ - return pe[port].snk_caps; -} - -uint8_t pd_get_snk_cap_cnt(int port) -{ - return pe[port].snk_cap_cnt; -} - -uint32_t pd_get_requested_voltage(int port) -{ - return pe[port].supply_voltage; -} - -uint32_t pd_get_requested_current(int port) -{ - return pe[port].curr_limit; -} - -/* - * Determine if this port may communicate with the cable plug. - * - * In both PD 2.0 and 3.0 (2.5.4 SOP'/SOP'' Communication with Cable Plugs): - * - * When no Contract or an Implicit Contract is in place (e.g. after a Power Role - * Swap or Fast Role Swap) only the Source port that is supplying Vconn is - * allowed to send packets to a Cable Plug - * - * When in an explicit contract, PD 3.0 requires that a port be Vconn source to - * communicate with the cable. PD 2.0 requires that a port be DFP to - * communicate with the cable plug, with an implication that it must be Vconn - * source as well (6.3.11 VCONN_Swap Message). - */ -static bool pe_can_send_sop_prime(int port) -{ - if (IS_ENABLED(CONFIG_USBC_VCONN)) { - if (PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT)) { - if (prl_get_rev(port, TCPCI_MSG_SOP) == PD_REV20) - return tc_is_vconn_src(port) && - pe[port].data_role == PD_ROLE_DFP; - else - return tc_is_vconn_src(port); - } else { - return tc_is_vconn_src(port) && - pe[port].power_role == PD_ROLE_SOURCE; - } - } else { - return false; - } -} - -/* - * Determine if this port may send the given VDM type - * - * For PD 2.0, "Only the DFP Shall be an Initrator of Structured VDMs except for - * the Attention Command that Shall only be initiated by the UFP" - * - * For PD 3.0, "Either port May be an Initiator of Structured VDMs except for - * the Enter Mode and Exit Mode Commands which shall only be initiated by the - * DFP" (6.4.4.2 Structured VDM) - * - * In both revisions, VDMs may only be initiated while in an explicit contract, - * with the only exception being for cable plug discovery. - */ -static bool pe_can_send_sop_vdm(int port, int vdm_cmd) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT)) { - if (prl_get_rev(port, TCPCI_MSG_SOP) == PD_REV20) { - if (pe[port].data_role == PD_ROLE_UFP && - vdm_cmd != CMD_ATTENTION) { - return false; - } - } else { - if (pe[port].data_role == PD_ROLE_UFP && - (vdm_cmd == CMD_ENTER_MODE || - vdm_cmd == CMD_EXIT_MODE)) { - return false; - } - } - return true; - } - - return false; -} - -static void pe_send_soft_reset(const int port, enum tcpci_msg_type type) -{ - pe[port].soft_reset_sop = type; - set_state_pe(port, PE_SEND_SOFT_RESET); -} - -void pe_report_discard(int port) -{ - /* - * Clear local AMS indicator as our AMS message was discarded, and flag - * the discard for the PE - */ - PE_CLR_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS); - PE_SET_FLAG(port, PE_FLAGS_MSG_DISCARDED); - - /* TODO(b/157228506): Ensure all states are checking discard */ -} - -/* - * Utility function to check for an outgoing message discard during states which - * send a message as a part of an AMS and wait for the transmit to complete. - * Note these states should not be power transitioning. - * - * In these states, discard due to an incoming message is a protocol error. - */ -static bool pe_check_outgoing_discard(int port) -{ - /* - * On outgoing discard, soft reset with SOP* of incoming message - * - * See Table 6-65 Response to an incoming Message (except VDM) in PD 3.0 - * Version 2.0 Specification. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_DISCARDED) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - enum tcpci_msg_type sop = - PD_HEADER_GET_SOP(rx_emsg[port].header); - - PE_CLR_FLAG(port, PE_FLAGS_MSG_DISCARDED); - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - pe_send_soft_reset(port, sop); - return true; - } - - return false; -} - -void pe_report_error(int port, enum pe_error e, enum tcpci_msg_type type) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - /* - * If there is a timeout error while waiting for a chunk of a chunked - * message, there is no requirement to trigger a soft reset. - */ - if (e == ERR_RCH_CHUNK_WAIT_TIMEOUT) - return; - - /* - * Generate Hard Reset if Protocol Error occurred - * while in PE_Send_Soft_Reset state. - */ - if (get_state_pe(port) == PE_SEND_SOFT_RESET) { - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, PE_SNK_HARD_RESET); - else - set_state_pe(port, PE_SRC_HARD_RESET); - return; - } - - /* - * The following states require custom handling of protocol errors, - * because they either need special handling of the no GoodCRC case - * (cable identity request, send capabilities), occur before explicit - * contract (discovery), or happen during a power transition. - * - * TODO(b/150774779): TCPMv2: Improve pe_error documentation - */ - if ((get_state_pe(port) == PE_SRC_SEND_CAPABILITIES || - get_state_pe(port) == PE_SRC_TRANSITION_SUPPLY || - get_state_pe(port) == PE_PRS_SNK_SRC_EVALUATE_SWAP || - get_state_pe(port) == PE_PRS_SNK_SRC_SOURCE_ON || - get_state_pe(port) == PE_PRS_SRC_SNK_WAIT_SOURCE_ON || - get_state_pe(port) == PE_SRC_DISABLED || - get_state_pe(port) == PE_SRC_DISCOVERY || - get_state_pe(port) == PE_VCS_CBL_SEND_SOFT_RESET || - get_state_pe(port) == PE_VDM_IDENTITY_REQUEST_CBL) || - (pe_in_frs_mode(port) && - get_state_pe(port) == PE_PRS_SNK_SRC_SEND_SWAP) - ) { - PE_SET_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - task_wake(PD_PORT_TO_TASK_ID(port)); - return; - } - - /* - * See section 8.3.3.4.1.1 PE_SRC_Send_Soft_Reset State: - * - * The PE_Send_Soft_Reset state shall be entered from - * any state when - * * A Protocol Error is detected by Protocol Layer during a - * Non-Interruptible AMS or - * * A message has not been sent after retries or - * * When not in an explicit contract and - * * Protocol Errors occurred on SOP during an Interruptible AMS or - * * Protocol Errors occurred on SOP during any AMS where the first - * Message in the sequence has not yet been sent i.e. an unexpected - * Message is received instead of the expected GoodCRC Message - * response. - */ - /* All error types besides transmit errors are Protocol Errors. */ - if ((e != ERR_TCH_XMIT && - !PE_CHK_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS)) - || e == ERR_TCH_XMIT - || (!PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT) && - type == TCPCI_MSG_SOP)) { - pe_send_soft_reset(port, type); - } - /* - * Transition to PE_Snk_Ready or PE_Src_Ready by a Protocol - * Error during an Interruptible AMS. - */ - else { - pe_set_ready_state(port); - } -} - -void pe_got_soft_reset(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - /* - * The PE_SRC_Soft_Reset state Shall be entered from any state when a - * Soft_Reset Message is received from the Protocol Layer. - */ - set_state_pe(port, PE_SOFT_RESET); -} - -__overridable bool pd_can_charge_from_device(int port, const int pdo_cnt, - const uint32_t *pdos) -{ - /* - * Don't attempt to charge from a device we have no SrcCaps from. Or, if - * drp_state is FORCE_SOURCE then don't attempt a PRS. - */ - if (pdo_cnt == 0 || pd_get_dual_role(port) == PD_DRP_FORCE_SOURCE) - return false; - - /* - * Treat device as a dedicated charger (meaning we should charge - * from it) if: - * - it does not support power swap, or - * - it is unconstrained power, or - * - it presents at least 27 W of available power - */ - - /* Unconstrained Power or NOT Dual Role Power we can charge from */ - if (pdos[0] & PDO_FIXED_UNCONSTRAINED || - (pdos[0] & PDO_FIXED_DUAL_ROLE) == 0) - return true; - - /* [virtual] allow_list */ - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - uint32_t max_ma, max_mv, max_pdo, max_mw, unused; - - /* - * Get max power that the partner offers (not necessarily what - * this board will request) - */ - pd_find_pdo_index(pdo_cnt, pdos, - PD_REV3_MAX_VOLTAGE, - &max_pdo); - pd_extract_pdo_power(max_pdo, &max_ma, &max_mv, &unused); - max_mw = max_ma * max_mv / 1000; - - if (max_mw >= PD_DRP_CHARGE_POWER_MIN) - return true; - } - return false; -} - -void pd_resume_check_pr_swap_needed(int port) -{ - /* - * Explicit contract, current power role of SNK, the device - * indicates it should not power us, and device isn't selected - * as the charging port (ex. through the GUI) then trigger a PR_Swap - */ - if (pe_is_explicit_contract(port) && - pd_get_power_role(port) == PD_ROLE_SINK && - !pd_can_charge_from_device(port, pd_get_src_cap_cnt(port), - pd_get_src_caps(port)) && - (!IS_ENABLED(CONFIG_CHARGE_MANAGER) || - charge_manager_get_active_charge_port() != port)) - pd_dpm_request(port, DPM_REQUEST_PR_SWAP); -} - -void pd_dpm_request(int port, enum pd_dpm_request req) -{ - PE_SET_DPM_REQUEST(port, req); -} - -void pe_vconn_swap_complete(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - PE_SET_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE); -} - -void pe_ps_reset_complete(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - PE_SET_FLAG(port, PE_FLAGS_PS_RESET_COMPLETE); -} - -void pe_message_sent(int port) -{ - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - PE_SET_FLAG(port, PE_FLAGS_TX_COMPLETE); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void pd_send_vdm(int port, uint32_t vid, int cmd, const uint32_t *data, - int count) -{ - /* Copy VDM Header */ - pe[port].vdm_data[0] = - VDO(vid, ((vid & USB_SID_PD) == USB_SID_PD) ? 1 : - (PD_VDO_CMD(cmd) <= CMD_ATTENTION), - VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP)) | - cmd); - - /* - * Copy VDOs after the VDM Header. Note that the count refers to VDO - * count. - */ - memcpy((pe[port].vdm_data + 1), data, count * sizeof(uint32_t)); - - pe[port].vdm_cnt = count + 1; - - /* - * The PE transmit routine assumes that tx_type was set already. Note, - * that this function is likely called from outside the PD task. - * (b/180465870) - */ - pe[port].tx_type = TCPCI_MSG_SOP; - pd_dpm_request(port, DPM_REQUEST_VDM); - - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -#ifdef TEST_BUILD -/* - * Allow unit tests to access this function to clear internal state data between - * runs - */ -void pe_clear_port_data(int port) -#else -static void pe_clear_port_data(int port) -#endif /* TEST_BUILD */ -{ - /* - * PD 3.0 Section 8.3.3.3.8 - * Note: The HardResetCounter is reset on a power cycle or Detach. - */ - pe[port].hard_reset_counter = 0; - - /* Reset port events */ - pd_clear_events(port, GENMASK(31, 0)); - - /* But then set disconnected event */ - pd_notify_event(port, PD_STATUS_EVENT_DISCONNECTED); - - /* Tell Policy Engine to invalidate the explicit contract */ - pe_invalidate_explicit_contract(port); - - /* - * Saved Source and Sink Capabilities are no longer valid on disconnect - */ - pd_set_src_caps(port, 0, NULL); - pe_set_snk_caps(port, 0, NULL); - - dpm_remove_sink(port); - dpm_remove_source(port); - - /* Exit BIST Test mode, in case the TCPC entered it. */ - tcpc_set_bist_test_mode(port, false); -} - -static void pe_handle_detach(void) -{ - const int port = TASK_ID_TO_PD_PORT(task_get_current()); - - pe_clear_port_data(port); -} -DECLARE_HOOK(HOOK_USB_PD_DISCONNECT, pe_handle_detach, HOOK_PRIO_DEFAULT); - -#ifdef CONFIG_USB_PD_RESET_MIN_BATT_SOC -static void pe_update_waiting_batt_flag(void) -{ - int i; - int batt_soc = usb_get_battery_soc(); - - if (batt_soc < CONFIG_USB_PD_RESET_MIN_BATT_SOC || - battery_get_disconnect_state() != BATTERY_NOT_DISCONNECTED) - return; - - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - if (PE_CHK_FLAG(i, PE_FLAGS_SNK_WAITING_BATT)) { - /* - * Battery has gained sufficient charge to kick off PD - * negotiation and withstand a hard reset. Clear the - * flag and perform Hard Reset. - */ - PE_CLR_FLAG(i, PE_FLAGS_SNK_WAITING_BATT); - CPRINTS("C%d: Battery has enough charge (%d%%) " \ - "to withstand a hard reset", i, batt_soc); - pd_dpm_request(i, DPM_REQUEST_HARD_RESET_SEND); - } - } -} -DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, pe_update_waiting_batt_flag, - HOOK_PRIO_DEFAULT); -#endif - -/* - * Private functions - */ -static void pe_set_dpm_curr_request(const int port, - const int request) -{ - PE_CLR_DPM_REQUEST(port, request); - pe[port].dpm_curr_request = request; -} - -/* Set the TypeC state machine to a new state. */ -test_export_static void set_state_pe(const int port, - const enum usb_pe_state new_state) -{ - set_state(port, &pe[port].ctx, &pe_states[new_state]); -} - -/* Get the current TypeC state. */ -test_export_static enum usb_pe_state get_state_pe(const int port) -{ - return pe[port].ctx.current - &pe_states[0]; -} - -/* - * Handle common DPM requests to both source and sink. - * - * Note: it is assumed the calling state set PE_FLAGS_LOCALLY_INITIATED_AMS - * - * Returns true if state was set and calling run state should now return. - */ -static bool common_src_snk_dpm_requests(int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES) && - PE_CHK_DPM_REQUEST(port, DPM_REQUEST_SEND_ALERT)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_SEND_ALERT); - set_state_pe(port, PE_SEND_ALERT); - return true; - } else if (IS_ENABLED(CONFIG_USBC_VCONN) && - PE_CHK_DPM_REQUEST(port, DPM_REQUEST_VCONN_SWAP)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_VCONN_SWAP); - set_state_pe(port, PE_VCS_SEND_SWAP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_BIST_TX)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_BIST_TX); - set_state_pe(port, PE_BIST_TX); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_SNK_STARTUP)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_SNK_STARTUP); - set_state_pe(port, PE_SNK_STARTUP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_SRC_STARTUP)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_SRC_STARTUP); - set_state_pe(port, PE_SRC_STARTUP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_SOFT_RESET_SEND)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_SOFT_RESET_SEND); - /* Currently only support sending soft reset to SOP */ - pe_send_soft_reset(port, TCPCI_MSG_SOP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_PORT_DISCOVERY)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_PORT_DISCOVERY); - if (!PE_CHK_FLAG(port, PE_FLAGS_MODAL_OPERATION)) { - /* - * Clear counters and reset timer to trigger a - * port discovery, and also clear any pending VDM send - * requests. - */ - pd_dfp_discovery_init(port); - /* - * TODO(b/189353401): Do not reinitialize modes when no - * longer required. - */ - pd_dfp_mode_init(port); - pe[port].dr_swap_attempt_counter = 0; - pe[port].discover_identity_counter = 0; - pd_timer_enable(port, PE_TIMER_DISCOVER_IDENTITY, - PD_T_DISCOVER_IDENTITY); - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_VDM); - } - return true; - } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_VDM)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_VDM); - /* Send previously set up SVDM. */ - set_state_pe(port, PE_VDM_REQUEST_DPM); - return true; - } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_ENTER_USB)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_ENTER_USB); - set_state_pe(port, PE_DEU_SEND_ENTER_USB); - return true; - } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_EXIT_MODES)) { - pe_set_dpm_curr_request(port, DPM_REQUEST_EXIT_MODES); - dpm_set_mode_exit_request(port); - return true; - } else if (PE_CHK_DPM_REQUEST(port, DPM_REQUEST_GET_SNK_CAPS)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_GET_SNK_CAPS); - set_state_pe(port, PE_DR_GET_SINK_CAP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND); - pe[port].tx_type = TCPCI_MSG_SOP_PRIME; - set_state_pe(port, PE_VCS_CBL_SEND_SOFT_RESET); - return true; - } - - return false; -} - -/* - * Handle source-specific DPM requests - * - * Returns true if state was set and calling run state should now return. - */ -static bool source_dpm_requests(int port) -{ - /* - * Ignore sink-specific request: - * DPM_REQUEST_NEW_POWER_LEVEL - * DPM_REQUEST_SOURCE_CAP - * DPM_REQUEST_FRS_DET_ENABLE - * DPM_REQURST_FRS_DET_DISABLE - */ - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_NEW_POWER_LEVEL | - DPM_REQUEST_SOURCE_CAP | - DPM_REQUEST_FRS_DET_ENABLE | - DPM_REQUEST_FRS_DET_DISABLE); - - if (pe[port].dpm_request) { - uint32_t dpm_request = pe[port].dpm_request; - - PE_SET_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS); - - if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_DR_SWAP)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_DR_SWAP); - if (PE_CHK_FLAG(port, PE_FLAGS_MODAL_OPERATION)) - set_state_pe(port, PE_SRC_HARD_RESET); - else - set_state_pe(port, PE_DRS_SEND_SWAP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_PR_SWAP)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_PR_SWAP); - set_state_pe(port, PE_PRS_SRC_SNK_SEND_SWAP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_GOTO_MIN)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_GOTO_MIN); - set_state_pe(port, PE_SRC_TRANSITION_SUPPLY); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_SRC_CAP_CHANGE)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_SRC_CAP_CHANGE); - set_state_pe(port, PE_SRC_SEND_CAPABILITIES); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_GET_SRC_CAPS)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_GET_SRC_CAPS); - set_state_pe(port, PE_DR_SRC_GET_SOURCE_CAP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_SEND_PING)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_SEND_PING); - set_state_pe(port, PE_SRC_PING); - return true; - } else if (common_src_snk_dpm_requests(port)) { - return true; - } - - CPRINTF("Unhandled DPM Request %x received\n", - dpm_request); - PE_CLR_DPM_REQUEST(port, dpm_request); - PE_CLR_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS); - } - return false; -} - -/* - * Handle sink-specific DPM requests - * - * Returns true if state was set and calling run state should now return. - */ -static bool sink_dpm_requests(int port) -{ - /* - * Ignore source specific requests: - * DPM_REQUEST_GOTO_MIN - * DPM_REQUEST_SRC_CAP_CHANGE, - * DPM_REQUEST_SEND_PING - */ - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_GOTO_MIN | - DPM_REQUEST_SRC_CAP_CHANGE | - DPM_REQUEST_SEND_PING); - - if (pe[port].dpm_request) { - uint32_t dpm_request = pe[port].dpm_request; - - PE_SET_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS); - - if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_DR_SWAP)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_DR_SWAP); - if (PE_CHK_FLAG(port, PE_FLAGS_MODAL_OPERATION)) - set_state_pe(port, PE_SNK_HARD_RESET); - else - set_state_pe(port, PE_DRS_SEND_SWAP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_PR_SWAP)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_PR_SWAP); - set_state_pe(port, PE_PRS_SNK_SRC_SEND_SWAP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_SOURCE_CAP)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_SOURCE_CAP); - set_state_pe(port, PE_SNK_GET_SOURCE_CAP); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_NEW_POWER_LEVEL)) { - pe_set_dpm_curr_request(port, - DPM_REQUEST_NEW_POWER_LEVEL); - set_state_pe(port, PE_SNK_SELECT_CAPABILITY); - return true; - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_FRS_DET_ENABLE)) { - pe_set_frs_enable(port, 1); - - /* Requires no state change, fall through to false */ - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_FRS_DET_ENABLE); - } else if (PE_CHK_DPM_REQUEST(port, - DPM_REQUEST_FRS_DET_DISABLE)) { - pe_set_frs_enable(port, 0); - /* Restore a default port current limit */ - typec_select_src_current_limit_rp(port, - CONFIG_USB_PD_PULLUP); - - /* Requires no state change, fall through to false */ - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_FRS_DET_DISABLE); - } else if (common_src_snk_dpm_requests(port)) { - return true; - } else { - CPRINTF("Unhandled DPM Request %x received\n", - dpm_request); - PE_CLR_DPM_REQUEST(port, dpm_request); - } - - PE_CLR_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS); - } - return false; -} - -/* Get the previous TypeC state. */ -static enum usb_pe_state get_last_state_pe(const int port) -{ - return pe[port].ctx.previous - &pe_states[0]; -} - -static void print_current_state(const int port) -{ - const char *mode = ""; - - if (IS_ENABLED(CONFIG_USB_PD_REV30) && - pe_in_frs_mode(port)) - mode = " FRS-MODE"; - - if (IS_ENABLED(USB_PD_DEBUG_LABELS)) - CPRINTS_L1("C%d: %s%s", port, - pe_state_names[get_state_pe(port)], mode); - else - CPRINTS("C%d: pe-st%d", port, get_state_pe(port)); -} - -static void send_source_cap(int port) -{ - const uint32_t *src_pdo; - const int src_pdo_cnt = dpm_get_source_pdo(&src_pdo, port); - - if (src_pdo_cnt == 0) { - /* No source capabilities defined, sink only */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT); - } - - tx_emsg[port].len = src_pdo_cnt * 4; - memcpy(tx_emsg[port].buf, (uint8_t *)src_pdo, tx_emsg[port].len); - - send_data_msg(port, TCPCI_MSG_SOP, PD_DATA_SOURCE_CAP); -} - -/* - * Request desired charge voltage from source. - */ -static void pe_send_request_msg(int port) -{ - uint32_t vpd_vdo = 0; - uint32_t rdo; - uint32_t curr_limit; - uint32_t supply_voltage; - - /* - * If we are charging through a VPD, the requested voltage and current - * might need adjusting. - */ - if ((get_usb_pd_cable_type(port) == IDH_PTYPE_VPD) && - is_vpd_ct_supported(port)) { - union vpd_vdo vpd = pd_get_am_discovery(port, - TCPCI_MSG_SOP_PRIME)->identity.product_t1.vpd; - - /* The raw vpd_vdo is passed to pd_build_request */ - vpd_vdo = vpd.raw_value; - } - - /* Build and send request RDO */ - pd_build_request(vpd_vdo, &rdo, &curr_limit, - &supply_voltage, port); - - CPRINTF("C%d: Req [%d] %dmV %dmA", port, RDO_POS(rdo), - supply_voltage, curr_limit); - if (rdo & RDO_CAP_MISMATCH) - CPRINTF(" Mismatch"); - CPRINTF("\n"); - - pe[port].curr_limit = curr_limit; - pe[port].supply_voltage = supply_voltage; - - tx_emsg[port].len = 4; - - memcpy(tx_emsg[port].buf, (uint8_t *)&rdo, tx_emsg[port].len); - send_data_msg(port, TCPCI_MSG_SOP, PD_DATA_REQUEST); -} - -static void pe_update_src_pdo_flags(int port, int pdo_cnt, uint32_t *pdos) -{ - /* - * Only parse PDO flags if type is fixed - * - * Note: From 6.4.1 Capabilities Message "The vSafe5V Fixed Supply - * Object Shall always be the first object." so hitting this condition - * would mean the partner is voilating spec. - */ - if ((pdos[0] & PDO_TYPE_MASK) != PDO_TYPE_FIXED) - return; - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - if (pd_can_charge_from_device(port, pdo_cnt, pdos)) { - charge_manager_update_dualrole(port, CAP_DEDICATED); - } else { - charge_manager_update_dualrole(port, CAP_DUALROLE); - } - } -} - -/* - * Evaluate whether our PR role is in the middle of changing, meaning we our - * current PR role is not the one we expect to have very shortly. - */ -bool pe_is_pr_swapping(int port) -{ - enum usb_pe_state cur_state = get_state_pe(port); - - if (cur_state == PE_PRS_SRC_SNK_EVALUATE_SWAP || - cur_state == PE_PRS_SRC_SNK_TRANSITION_TO_OFF || - cur_state == PE_PRS_SNK_SRC_EVALUATE_SWAP || - cur_state == PE_PRS_SNK_SRC_TRANSITION_TO_OFF) - return true; - - return false; -} - -void pd_request_power_swap(int port) -{ - /* Ignore requests when the board does not wish to swap */ - if (!pd_check_power_swap(port)) - return; - - /* Ignore requests when our power role is transitioning */ - if (pe_is_pr_swapping(port)) - return; - - /* - * Always reset the SRC to SNK PR swap counter when a PR swap is - * requested by policy. - */ - pe[port].src_snk_pr_swap_counter = 0; - pd_dpm_request(port, DPM_REQUEST_PR_SWAP); -} - -/* The function returns true if there is a PE state change, false otherwise */ -static bool port_try_vconn_swap(int port) -{ - if (pe[port].vconn_swap_counter < N_VCONN_SWAP_COUNT) { - pd_dpm_request(port, DPM_REQUEST_VCONN_SWAP); - set_state_pe(port, get_last_state_pe(port)); - return true; - } - return false; -} - -/* - * Run discovery at our leisure from PE_SNK_Ready or PE_SRC_Ready, after - * attempting to get into the desired default policy of DFP/Vconn source - * - * Return indicates whether set_state was called, in which case the calling - * function should return as well. - */ -__maybe_unused static bool pe_attempt_port_discovery(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - assert(0); - - /* - * DONE set once modal entry is successful, discovery completes, or - * discovery results in a NAK - */ - if (PE_CHK_FLAG(port, PE_FLAGS_VDM_SETUP_DONE)) - return false; - - /* Apply Port Discovery DR Swap Policy */ - if (port_discovery_dr_swap_policy(port, pe[port].data_role, - PE_CHK_FLAG(port, PE_FLAGS_DR_SWAP_TO_DFP))) { - PE_SET_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS); - PE_CLR_FLAG(port, PE_FLAGS_DR_SWAP_TO_DFP); - set_state_pe(port, PE_DRS_SEND_SWAP); - return true; - } - - /* Apply Port Discovery VCONN Swap Policy */ - if (IS_ENABLED(CONFIG_USBC_VCONN) && - port_discovery_vconn_swap_policy(port, - PE_CHK_FLAG(port, PE_FLAGS_VCONN_SWAP_TO_ON))) { - PE_SET_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS); - PE_CLR_FLAG(port, PE_FLAGS_VCONN_SWAP_TO_ON); - set_state_pe(port, PE_VCS_SEND_SWAP); - return true; - } - - /* If mode entry was successful, disable the timer */ - if (PE_CHK_FLAG(port, PE_FLAGS_VDM_SETUP_DONE)) { - pd_timer_disable(port, PE_TIMER_DISCOVER_IDENTITY); - return false; - } - - /* - * Run discovery functions when the timer indicating either cable - * discovery spacing or BUSY spacing runs out. - */ - if (pd_timer_is_expired(port, PE_TIMER_DISCOVER_IDENTITY)) { - if (pd_get_identity_discovery(port, TCPCI_MSG_SOP_PRIME) == - PD_DISC_NEEDED) { - pe[port].tx_type = TCPCI_MSG_SOP_PRIME; - set_state_pe(port, PE_VDM_IDENTITY_REQUEST_CBL); - return true; - } else if (pd_get_identity_discovery(port, TCPCI_MSG_SOP) == - PD_DISC_NEEDED && - pe_can_send_sop_vdm(port, CMD_DISCOVER_IDENT)) { - pe[port].tx_type = TCPCI_MSG_SOP; - set_state_pe(port, - PE_INIT_PORT_VDM_IDENTITY_REQUEST); - return true; - } else if (pd_get_svids_discovery(port, TCPCI_MSG_SOP) == - PD_DISC_NEEDED && - pe_can_send_sop_vdm(port, CMD_DISCOVER_SVID)) { - pe[port].tx_type = TCPCI_MSG_SOP; - set_state_pe(port, PE_INIT_VDM_SVIDS_REQUEST); - return true; - } else if (pd_get_modes_discovery(port, TCPCI_MSG_SOP) == - PD_DISC_NEEDED && - pe_can_send_sop_vdm(port, CMD_DISCOVER_MODES)) { - pe[port].tx_type = TCPCI_MSG_SOP; - set_state_pe(port, PE_INIT_VDM_MODES_REQUEST); - return true; - } else if (pd_get_svids_discovery(port, TCPCI_MSG_SOP_PRIME) - == PD_DISC_NEEDED) { - pe[port].tx_type = TCPCI_MSG_SOP_PRIME; - set_state_pe(port, PE_INIT_VDM_SVIDS_REQUEST); - return true; - } else if (pd_get_modes_discovery(port, TCPCI_MSG_SOP_PRIME) == - PD_DISC_NEEDED) { - pe[port].tx_type = TCPCI_MSG_SOP_PRIME; - set_state_pe(port, PE_INIT_VDM_MODES_REQUEST); - return true; - } - } - - return false; -} - -bool pd_setup_vdm_request(int port, enum tcpci_msg_type tx_type, - uint32_t *vdm, uint32_t vdo_cnt) -{ - if (vdo_cnt < VDO_HDR_SIZE || vdo_cnt > VDO_MAX_SIZE) - return false; - - pe[port].tx_type = tx_type; - memcpy(pe[port].vdm_data, vdm, vdo_cnt * sizeof(*vdm)); - pe[port].vdm_cnt = vdo_cnt; - - return true; -} - -int pd_dev_store_rw_hash(int port, uint16_t dev_id, uint32_t *rw_hash, - uint32_t current_image) -{ - pe[port].dev_id = dev_id; - memcpy(pe[port].dev_rw_hash, rw_hash, PD_RW_HASH_SIZE); -#ifdef CONFIG_CMD_PD_DEV_DUMP_INFO - pd_dev_dump_info(dev_id, rw_hash); -#endif - pe[port].current_image = current_image; - - if (IS_ENABLED(CONFIG_USB_PD_HOST_CMD)) { - int i; - - /* Search table for matching device / hash */ - for (i = 0; i < RW_HASH_ENTRIES; i++) - if (dev_id == rw_hash_table[i].dev_id) - return !memcmp(rw_hash, - rw_hash_table[i].dev_rw_hash, - PD_RW_HASH_SIZE); - } - - return 0; -} - -void pd_dev_get_rw_hash(int port, uint16_t *dev_id, uint8_t *rw_hash, - uint32_t *current_image) -{ - *dev_id = pe[port].dev_id; - *current_image = pe[port].current_image; - if (*dev_id) - memcpy(rw_hash, pe[port].dev_rw_hash, PD_RW_HASH_SIZE); -} - -/* - * This function must only be called from the PE_SNK_READY entry and - * PE_SRC_READY entry State. - * - * TODO(b:181339670) Rethink jitter timer restart if this is the first - * message but the partner gets a message in first, may not want to - * disable and restart it. - */ -static void pe_update_wait_and_add_jitter_timer(int port) -{ - /* - * In PD2.0 Mode - * - * For Source: - * Give the sink some time to send any messages - * before we may send messages of our own. Add - * some jitter of up to ~345ms, to prevent - * multiple collisions. This delay also allows - * the sink device to request power role swap - * and allow the the accept message to be sent - * prior to CMD_DISCOVER_IDENT being sent in the - * SRC_READY state. - * - * For Sink: - * Give the source some time to send any messages before - * we start our interrogation. Add some jitter of up to - * ~345ms to prevent multiple collisions. - */ - if (prl_get_rev(port, TCPCI_MSG_SOP) == PD_REV20 && - PE_CHK_FLAG(port, PE_FLAGS_FIRST_MSG) && - pd_timer_is_disabled(port, PE_TIMER_WAIT_AND_ADD_JITTER)) { - pd_timer_enable(port, PE_TIMER_WAIT_AND_ADD_JITTER, - SRC_SNK_READY_HOLD_OFF_US + - (get_time().le.lo & 0xf) * 23 * MSEC); - } -} - -/** - * Common sender response message handling - * - * This is setup like a pseudo state machine parent state. It - * centralizes the SenderResponseTimer for the calling states, as - * well as checking message send status. - */ -/* - * pe_sender_response_msg_entry - * Initialization for handling sender response messages. - * - * @param port USB-C Port number - */ -static void pe_sender_response_msg_entry(const int port) -{ - /* Stop sender response timer */ - pd_timer_disable(port, PE_TIMER_SENDER_RESPONSE); -} - -/* - * pe_sender_response_msg_run - * Check status of sender response messages. - * - * The normal progression of pe_sender_response_msg_entry is: - * PENDING -> (COMPLETED/SENT) -> SENT -> SENT ... - * or - * PENDING -> DISCARDED - * PENDING -> DPM_DISCARDED - * - * NOTE: it is not valid to call this function for a message after - * receiving either PE_MSG_DISCARDED or PE_MSG_DPM_DISCARDED until - * another message has been sent and pe_sender_response_msg_entry is called - * again. - * - * @param port USB-C Port number - * @return the current pe_msg_check - */ -static enum pe_msg_check pe_sender_response_msg_run(const int port) -{ - timestamp_t tx_success_ts; - uint32_t offset; - if (pd_timer_is_disabled(port, PE_TIMER_SENDER_RESPONSE)) { - /* Check for Discard */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_DISCARDED)) { - int dpm_request = pe[port].dpm_curr_request; - - PE_CLR_FLAG(port, PE_FLAGS_MSG_DISCARDED); - /* Restore the DPM Request */ - if (dpm_request) { - PE_SET_DPM_REQUEST(port, dpm_request); - return PE_MSG_DPM_DISCARDED; - } - return PE_MSG_DISCARDED; - } - - /* Check for GoodCRC */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* TCPC TX success time stamp */ - tx_success_ts = prl_get_tcpc_tx_success_ts(port); - /* Calculate the delay from TX success to PE */ - offset = time_since32(tx_success_ts); - - /* - * Initialize and run the SenderResponseTimer by - * offsetting it with TX transmit success time. - * This would remove the effect of the latency from - * propagating the TX status. - */ - pd_timer_enable(port, PE_TIMER_SENDER_RESPONSE, - PD_T_SENDER_RESPONSE - offset); - return PE_MSG_SEND_COMPLETED; - } - return PE_MSG_SEND_PENDING; - } - return PE_MSG_SENT; -} - -/* - * pe_sender_response_msg_exit - * Exit cleanup for handling sender response messages. - * - * @param port USB-C Port number - */ -static void pe_sender_response_msg_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_SENDER_RESPONSE); -} - -/** - * PE_SRC_Startup - */ -static void pe_src_startup_entry(int port) -{ - print_current_state(port); - - /* Reset CapsCounter */ - pe[port].caps_counter = 0; - - /* Reset the protocol layer */ - prl_reset_soft(port); - - /* Set initial data role */ - pe[port].data_role = pd_get_data_role(port); - - /* Set initial power role */ - pe[port].power_role = PD_ROLE_SOURCE; - - /* Clear explicit contract. */ - pe_invalidate_explicit_contract(port); - - if (PE_CHK_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE); - /* - * Protocol layer reset clears the message IDs for all SOP - * types. Indicate that a SOP' soft reset is required before any - * other messages are sent to the cable. - * - * Note that other paths into this state are for the initial - * connection and for a hard reset. In both cases the cable - * should also automatically clear the message IDs so don't - * generate an SOP' soft reset for those cases. Sending - * unnecessary SOP' soft resets causes bad behavior with - * some devices. See b/179325862. - */ - pd_dpm_request(port, DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND); - - /* Start SwapSourceStartTimer */ - pd_timer_enable(port, PE_TIMER_SWAP_SOURCE_START, - PD_T_SWAP_SOURCE_START); - - /* - * Evaluate port's sink caps for preferred current, if - * already available - */ - if (pd_get_snk_cap_cnt(port) > 0) - dpm_evaluate_sink_fixed_pdo(port, - *pd_get_snk_caps(port)); - - /* - * Remove prior FRS claims to 3.0 A now that sink current has - * been claimed, to avoid issues with lower priority ports - * potentially receiving a 3.0 A claim between calls. - */ - dpm_remove_source(port); - } else { - /* - * SwapSourceStartTimer delay is not needed, so trigger now. - * We can't use set_state_pe here, since we need to ensure that - * the protocol layer is running again (done in run function). - */ - pd_timer_enable(port, PE_TIMER_SWAP_SOURCE_START, 0); - - /* - * Set DiscoverIdentityTimer to trigger when we enter - * src_discovery for the first time. After initial startup - * set, vdm_identity_request_cbl will handle the timer updates. - */ - pd_timer_enable(port, PE_TIMER_DISCOVER_IDENTITY, 0); - - /* Clear port discovery/mode flags */ - pd_dfp_discovery_init(port); - pd_dfp_mode_init(port); - pe[port].ama_vdo = PD_VDO_INVALID; - pe[port].vpd_vdo = PD_VDO_INVALID; - pe[port].discover_identity_counter = 0; - - /* Reset dr swap attempt counter */ - pe[port].dr_swap_attempt_counter = 0; - - /* Reset VCONN swap counter */ - pe[port].vconn_swap_counter = 0; - - /* Request partner sink caps if a feature requires them */ - if (IS_ENABLED(CONFIG_USB_PD_HOST_CMD) || - CONFIG_USB_PD_3A_PORTS > 0 || - IS_ENABLED(CONFIG_USB_PD_FRS)) - pd_dpm_request(port, DPM_REQUEST_GET_SNK_CAPS); - } -} - -static void pe_src_startup_run(int port) -{ - /* Wait until protocol layer is running */ - if (!prl_is_running(port)) - return; - - if (pd_timer_is_expired(port, PE_TIMER_SWAP_SOURCE_START)) - set_state_pe(port, PE_SRC_SEND_CAPABILITIES); -} - -static void pe_src_startup_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_SWAP_SOURCE_START); -} - -/** - * PE_SRC_Discovery - */ -static void pe_src_discovery_entry(int port) -{ - print_current_state(port); - - /* - * Initialize and run the SourceCapabilityTimer in order - * to trigger sending a Source_Capabilities Message. - * - * The SourceCapabilityTimer Shall continue to run during - * identity discover and Shall Not be initialized on re-entry - * to PE_SRC_Discovery. - * - * Note: Cable identity is the only valid VDM to probe before a contract - * is in place. All other probing must happen from ready states. - */ - if (get_last_state_pe(port) != PE_VDM_IDENTITY_REQUEST_CBL) - pd_timer_enable(port, PE_TIMER_SOURCE_CAP, - PD_T_SEND_SOURCE_CAP); -} - -static void pe_src_discovery_run(int port) -{ - /* - * Transition to the PE_SRC_Send_Capabilities state when: - * 1) The SourceCapabilityTimer times out and - * CapsCounter ≤ nCapsCount. - * - * Transition to the PE_SRC_Disabled state when: - * 1) The Port Partners are not presently PD Connected - * 2) And the SourceCapabilityTimer times out - * 3) And CapsCounter > nCapsCount. - * - * Transition to the PE_SRC_VDM_Identity_request state when: - * 1) DPM requests the identity of the cable plug and - * 2) DiscoverIdentityCounter < nDiscoverIdentityCount - */ - if (pd_timer_is_expired(port, PE_TIMER_SOURCE_CAP)) { - if (pe[port].caps_counter <= N_CAPS_COUNT) { - set_state_pe(port, PE_SRC_SEND_CAPABILITIES); - return; - } else if (!PE_CHK_FLAG(port, PE_FLAGS_PD_CONNECTION)) { - set_state_pe(port, PE_SRC_DISABLED); - return; - } - } - - /* - * Note: While the DiscoverIdentityTimer is only required in an explicit - * contract, we use it here to ensure we space any potential BUSY - * requests properly. - */ - if (pd_get_identity_discovery(port, TCPCI_MSG_SOP_PRIME) == - PD_DISC_NEEDED - && pd_timer_is_expired(port, PE_TIMER_DISCOVER_IDENTITY) - && pe_can_send_sop_prime(port) - && (pe[port].discover_identity_counter < - N_DISCOVER_IDENTITY_PRECONTRACT_LIMIT)) { - pe[port].tx_type = TCPCI_MSG_SOP_PRIME; - set_state_pe(port, PE_VDM_IDENTITY_REQUEST_CBL); - return; - } - - /* - * Transition to the PE_SRC_Disabled state when: - * 1) The Port Partners have not been PD Connected. - * 2) And the NoResponseTimer times out. - * 3) And the HardResetCounter > nHardResetCount. - */ - if (!PE_CHK_FLAG(port, PE_FLAGS_PD_CONNECTION) && - pd_timer_is_expired(port, PE_TIMER_NO_RESPONSE) && - pe[port].hard_reset_counter > N_HARD_RESET_COUNT) { - set_state_pe(port, PE_SRC_DISABLED); - return; - } -} - -/** - * PE_SRC_Send_Capabilities - */ -static void pe_src_send_capabilities_entry(int port) -{ - print_current_state(port); - - /* Send PD Capabilities message */ - send_source_cap(port); - pe_sender_response_msg_entry(port); - - /* Increment CapsCounter */ - pe[port].caps_counter++; -} - -static void pe_src_send_capabilities_run(int port) -{ - enum pe_msg_check msg_check; - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - /* - * Handle Discarded message - * PE_SNK/SRC_READY if DPM_REQUEST - * PE_SEND_SOFT_RESET otherwise - */ - if (msg_check == PE_MSG_DPM_DISCARDED) { - set_state_pe(port, PE_SRC_READY); - return; - } else if (msg_check == PE_MSG_DISCARDED) { - pe_send_soft_reset(port, TCPCI_MSG_SOP); - return; - } - - /* - * Handle message that was just sent - */ - if (msg_check == PE_MSG_SEND_COMPLETED) { - /* - * If a GoodCRC Message is received then the Policy Engine - * Shall: - * 1) Stop the NoResponseTimer. - * 2) Reset the HardResetCounter and CapsCounter to zero. - * 3) Initialize and run the SenderResponseTimer. - */ - /* Stop the NoResponseTimer */ - pd_timer_disable(port, PE_TIMER_NO_RESPONSE); - - /* Reset the HardResetCounter to zero */ - pe[port].hard_reset_counter = 0; - - /* Reset the CapsCounter to zero */ - pe[port].caps_counter = 0; - } - - /* - * Transition to the PE_SRC_Negotiate_Capability state when: - * 1) A Request Message is received from the Sink - */ - if ((msg_check & PE_MSG_SENT) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - /* - * Request Message Received? - */ - if (PD_HEADER_CNT(rx_emsg[port].header) > 0 && - PD_HEADER_TYPE(rx_emsg[port].header) == - PD_DATA_REQUEST) { - - /* - * Set to highest revision supported by both - * ports. - */ - prl_set_rev(port, TCPCI_MSG_SOP, - MIN(PD_REVISION, PD_HEADER_REV(rx_emsg[port].header))); - - set_cable_rev(port); - - /* We are PD connected */ - PE_SET_FLAG(port, PE_FLAGS_PD_CONNECTION); - tc_pd_connection(port, 1); - - /* - * Handle the Sink Request in - * PE_SRC_Negotiate_Capability state - */ - set_state_pe(port, PE_SRC_NEGOTIATE_CAPABILITY); - return; - } - - /* - * We have a Protocol Error. - * PE_SEND_SOFT_RESET - */ - pe_send_soft_reset(port, - PD_HEADER_GET_SOP(rx_emsg[port].header)); - return; - } - - /* - * Transition to the PE_SRC_Discovery state when: - * 1) The Protocol Layer indicates that the Message has not been sent - * and we are presently not Connected - * - * Send soft reset when: - * 1) The Protocol Layer indicates that the Message has not been sent - * and we are already Connected - * - * See section 8.3.3.4.1.1 PE_SRC_Send_Soft_Reset State and section - * 8.3.3.2.3 PE_SRC_Send_Capabilities State. - * - * NOTE: The PE_FLAGS_PROTOCOL_ERROR is set if a GoodCRC Message - * is not received. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - if (!PE_CHK_FLAG(port, PE_FLAGS_PD_CONNECTION)) - set_state_pe(port, PE_SRC_DISCOVERY); - else - pe_send_soft_reset(port, TCPCI_MSG_SOP); - return; - } - - /* - * Transition to the PE_SRC_Disabled state when: - * 1) The Port Partners have not been PD Connected - * 2) The NoResponseTimer times out - * 3) And the HardResetCounter > nHardResetCount. - * - * Transition to the Error Recovery state when: - * 1) The Port Partners have previously been PD Connected - * 2) The NoResponseTimer times out - * 3) And the HardResetCounter > nHardResetCount. - */ - if (pd_timer_is_expired(port, PE_TIMER_NO_RESPONSE)) { - if (pe[port].hard_reset_counter <= N_HARD_RESET_COUNT) - set_state_pe(port, PE_SRC_HARD_RESET); - else if (PE_CHK_FLAG(port, PE_FLAGS_PD_CONNECTION)) - set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY); - else - set_state_pe(port, PE_SRC_DISABLED); - return; - } - - /* - * Transition to the PE_SRC_Hard_Reset state when: - * 1) The SenderResponseTimer times out. - */ - if (pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) { - set_state_pe(port, PE_SRC_HARD_RESET); - return; - } -} - -static void pe_src_send_capabilities_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -/** - * PE_SRC_Negotiate_Capability - */ -static void pe_src_negotiate_capability_entry(int port) -{ - uint32_t payload; - - print_current_state(port); - - /* Get message payload */ - payload = *(uint32_t *)(&rx_emsg[port].buf); - - /* - * Evaluate the Request from the Attached Sink - */ - - /* - * Transition to the PE_SRC_Capability_Response state when: - * 1) The Request cannot be met. - * 2) Or the Request can be met later from the Power Reserve - * - * Transition to the PE_SRC_Transition_Supply state when: - * 1) The Request can be met - * - */ - if (pd_check_requested_voltage(payload, port) != EC_SUCCESS) { - set_state_pe(port, PE_SRC_CAPABILITY_RESPONSE); - } else { - PE_SET_FLAG(port, PE_FLAGS_ACCEPT); - pe[port].requested_idx = RDO_POS(payload); - set_state_pe(port, PE_SRC_TRANSITION_SUPPLY); - } -} - -/** - * PE_SRC_Transition_Supply - */ -static void pe_src_transition_supply_entry(int port) -{ - print_current_state(port); - - /* Send a GotoMin Message or otherwise an Accept Message */ - if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) { - PE_CLR_FLAG(port, PE_FLAGS_ACCEPT); - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_ACCEPT); - } else { - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_GOTO_MIN); - } -} - -static void pe_src_transition_supply_run(int port) -{ - /* - * Transition to the PE_SRC_Ready state when: - * 1) The power supply is ready. - * - * NOTE: This code block is executed twice: - * First Pass) - * When PE_FLAGS_TX_COMPLETE is set due to the - * PD_CTRL_ACCEPT or PD_CTRL_GOTO_MIN messages - * being sent. - * - * Second Pass) - * When PE_FLAGS_TX_COMPLETE is set due to the - * PD_CTRL_PS_RDY message being sent. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* - * NOTE: If a message was received, - * pe_src_ready state will handle it. - */ - - if (PE_CHK_FLAG(port, PE_FLAGS_PS_READY)) { - PE_CLR_FLAG(port, PE_FLAGS_PS_READY); - - /* - * Set first message flag to trigger a wait and add - * jitter delay when operating in PD2.0 mode. Skip - * if we already have a contract. - */ - if (!pe_is_explicit_contract(port)) { - PE_SET_FLAG(port, PE_FLAGS_FIRST_MSG); - pd_timer_disable(port, - PE_TIMER_WAIT_AND_ADD_JITTER); - } - - /* NOTE: Second pass through this code block */ - /* Explicit Contract is now in place */ - pe_set_explicit_contract(port); - - /* - * Setup to get Device Policy Manager to request - * Source Capabilities, if needed, for possible - * PR_Swap. Get the number directly to avoid re-probing - * if the partner generated an error and left -1 for the - * count. - */ - if (pe[port].src_cap_cnt == 0) - pd_dpm_request(port, DPM_REQUEST_GET_SRC_CAPS); - - set_state_pe(port, PE_SRC_READY); - } else { - /* NOTE: First pass through this code block */ - /* Wait for tSrcTransition before changing supply. */ - pd_timer_enable(port, PE_TIMER_SRC_TRANSITION, - PD_T_SRC_TRANSITION); - } - - return; - } - - if (pd_timer_is_expired(port, PE_TIMER_SRC_TRANSITION)) { - pd_timer_disable(port, PE_TIMER_SRC_TRANSITION); - /* Transition power supply and send PS_RDY. */ - pd_transition_voltage(pe[port].requested_idx); - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PS_RDY); - PE_SET_FLAG(port, PE_FLAGS_PS_READY); - } - - /* - * Transition to the PE_SRC_Hard_Reset state when: - * 1) A Protocol Error occurs. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - set_state_pe(port, PE_SRC_HARD_RESET); - } -} - -static void pe_src_transition_supply_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_SRC_TRANSITION); -} - -/* - * Transitions state after receiving a Not Supported extended message. Under - * appropriate conditions, transitions to a PE_{SRC,SNK}_Chunk_Received. - */ -static void extended_message_not_supported(int port, uint32_t *payload) -{ - uint16_t ext_header = GET_EXT_HEADER(*payload); - - if (IS_ENABLED(CONFIG_USB_PD_REV30) && - !IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES) && - PD_EXT_HEADER_CHUNKED(ext_header) && - PD_EXT_HEADER_DATA_SIZE(ext_header) > - PD_MAX_EXTENDED_MSG_CHUNK_LEN) { - set_state_pe(port, - pe[port].power_role == PD_ROLE_SOURCE ? - PE_SRC_CHUNK_RECEIVED : PE_SNK_CHUNK_RECEIVED); - return; - } - - set_state_pe(port, PE_SEND_NOT_SUPPORTED); -} - -/** - * PE_SRC_Ready - */ -static void pe_src_ready_entry(int port) -{ - print_current_state(port); - - /* Ensure any message send flags are cleaned up */ - PE_CLR_FLAG(port, PE_FLAGS_READY_CLR); - - /* Clear DPM Current Request */ - pe[port].dpm_curr_request = 0; - - /* - * Wait and add jitter if we are operating in PD2.0 mode and no messages - * have been sent since enter this state. - */ - pe_update_wait_and_add_jitter_timer(port); -} - -static void pe_src_ready_run(int port) -{ - /* - * Handle incoming messages before discovery and DPMs other than hard - * reset - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - uint8_t type = PD_HEADER_TYPE(rx_emsg[port].header); - uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header); - uint8_t ext = PD_HEADER_EXT(rx_emsg[port].header); - uint32_t *payload = (uint32_t *)rx_emsg[port].buf; - - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - /* Extended Message Requests */ - if (ext > 0) { - switch (type) { -#if defined(CONFIG_USB_PD_EXTENDED_MESSAGES) && defined(CONFIG_BATTERY) - case PD_EXT_GET_BATTERY_CAP: - set_state_pe(port, PE_GIVE_BATTERY_CAP); - break; - case PD_EXT_GET_BATTERY_STATUS: - set_state_pe(port, PE_GIVE_BATTERY_STATUS); - break; -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES && CONFIG_BATTERY */ - default: - extended_message_not_supported(port, payload); - } - return; - } - /* Data Message Requests */ - else if (cnt > 0) { - switch (type) { - case PD_DATA_REQUEST: - set_state_pe(port, PE_SRC_NEGOTIATE_CAPABILITY); - return; - case PD_DATA_SINK_CAP: - break; - case PD_DATA_VENDOR_DEF: - if (PD_HEADER_TYPE(rx_emsg[port].header) == - PD_DATA_VENDOR_DEF) { - if (PD_VDO_SVDM(*payload)) { - set_state_pe(port, - PE_VDM_RESPONSE); - } else - set_state_pe(port, - PE_HANDLE_CUSTOM_VDM_REQUEST); - } - return; - case PD_DATA_BIST: - set_state_pe(port, PE_BIST_TX); - return; - default: - set_state_pe(port, PE_SEND_NOT_SUPPORTED); - return; - } - } - /* Control Message Requests */ - else { - switch (type) { - case PD_CTRL_GOOD_CRC: - break; - case PD_CTRL_NOT_SUPPORTED: - break; - case PD_CTRL_PING: - break; - case PD_CTRL_GET_SOURCE_CAP: - set_state_pe(port, PE_SRC_SEND_CAPABILITIES); - return; - case PD_CTRL_GET_SINK_CAP: - set_state_pe(port, PE_SNK_GIVE_SINK_CAP); - return; - case PD_CTRL_GOTO_MIN: - break; - case PD_CTRL_PR_SWAP: - set_state_pe(port, - PE_PRS_SRC_SNK_EVALUATE_SWAP); - return; - case PD_CTRL_DR_SWAP: - if (PE_CHK_FLAG(port, - PE_FLAGS_MODAL_OPERATION)) { - set_state_pe(port, PE_SRC_HARD_RESET); - return; - } - - set_state_pe(port, PE_DRS_EVALUATE_SWAP); - return; - case PD_CTRL_VCONN_SWAP: - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_state_pe(port, - PE_VCS_EVALUATE_SWAP); - else - set_state_pe(port, - PE_SEND_NOT_SUPPORTED); - return; - /* - * USB PD 3.0 6.8.1: - * Receiving an unexpected message shall be responded - * to with a soft reset message. - */ - case PD_CTRL_ACCEPT: - case PD_CTRL_REJECT: - case PD_CTRL_WAIT: - case PD_CTRL_PS_RDY: - pe_send_soft_reset(port, - PD_HEADER_GET_SOP(rx_emsg[port].header)); - return; - /* - * Receiving an unknown or unsupported message - * shall be responded to with a not supported message. - */ - default: - set_state_pe(port, PE_SEND_NOT_SUPPORTED); - return; - } - } - } - - /* - * Make sure the PRL layer isn't busy with receiving or transmitting - * chunked messages before attempting to transmit a new message. - */ - if (prl_is_busy(port)) - return; - - if (PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_CONTINUE)) { - PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_CONTINUE); - set_state_pe(port, PE_VDM_REQUEST_DPM); - return; - } - - if (PE_CHK_FLAG(port, PE_FLAGS_WAITING_PR_SWAP) && - pd_timer_is_expired(port, PE_TIMER_PR_SWAP_WAIT)) { - PE_CLR_FLAG(port, PE_FLAGS_WAITING_PR_SWAP); - PE_SET_DPM_REQUEST(port, DPM_REQUEST_PR_SWAP); - } - - if (pd_timer_is_disabled(port, PE_TIMER_WAIT_AND_ADD_JITTER) || - pd_timer_is_expired(port, PE_TIMER_WAIT_AND_ADD_JITTER)) { - - PE_CLR_FLAG(port, PE_FLAGS_FIRST_MSG); - pd_timer_disable(port, PE_TIMER_WAIT_AND_ADD_JITTER); - - /* - * Handle Device Policy Manager Requests - */ - if (source_dpm_requests(port)) - return; - - /* - * Attempt discovery if possible, and return if state was - * changed for that discovery. - */ - if (pe_attempt_port_discovery(port)) - return; - - /* No DPM requests; attempt mode entry/exit if needed */ - dpm_run(port); - } -} - -/** - * PE_SRC_Disabled - */ -static void pe_src_disabled_entry(int port) -{ - print_current_state(port); - - if ((get_usb_pd_cable_type(port) == IDH_PTYPE_VPD) && - is_vpd_ct_supported(port)) { - /* - * Inform the Device Policy Manager that a Charge-Through VCONN - * Powered Device was detected. - */ - tc_ctvpd_detected(port); - } - - if (pd_get_power_role(port) == PD_ROLE_SOURCE) - dpm_add_non_pd_sink(port); - - /* - * Unresponsive to USB Power Delivery messaging, but not to Hard Reset - * Signaling. See pe_got_hard_reset - */ -} - -/** - * PE_SRC_Capability_Response - */ -static void pe_src_capability_response_entry(int port) -{ - print_current_state(port); - - /* NOTE: Wait messaging should be implemented. */ - - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT); -} - -static void pe_src_capability_response_run(int port) -{ - /* - * Transition to the PE_SRC_Ready state when: - * 1) There is an Explicit Contract and - * 2) A Reject Message has been sent and the present Contract is still - * Valid or - * 3) A Wait Message has been sent. - * - * Transition to the PE_SRC_Hard_Reset state when: - * 1) There is an Explicit Contract and - * 2) The Reject Message has been sent and the present - * Contract is Invalid - * - * Transition to the PE_SRC_Wait_New_Capabilities state when: - * 1) There is no Explicit Contract and - * 2) A Reject Message has been sent or - * 3) A Wait Message has been sent. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - if (PE_CHK_FLAG(port, PE_FLAGS_EXPLICIT_CONTRACT)) - /* - * NOTE: The src capabilities listed in - * board/xxx/usb_pd_policy.c will not - * change so the present contract will - * never be invalid. - */ - set_state_pe(port, PE_SRC_READY); - else - /* - * NOTE: The src capabilities listed in - * board/xxx/usb_pd_policy.c will not - * change, so no need to resending them - * again. Transition to disabled state. - */ - set_state_pe(port, PE_SRC_DISABLED); - } -} - -/** - * PE_SRC_Hard_Reset - */ -static void pe_src_hard_reset_entry(int port) -{ - print_current_state(port); - - /* Generate Hard Reset Signal */ - prl_execute_hard_reset(port); - - /* Increment the HardResetCounter */ - pe[port].hard_reset_counter++; - - /* Start NoResponseTimer */ - pd_timer_enable(port, PE_TIMER_NO_RESPONSE, PD_T_NO_RESPONSE); - - /* Start PSHardResetTimer */ - pd_timer_enable(port, PE_TIMER_PS_HARD_RESET, PD_T_PS_HARD_RESET); - - /* Clear error flags */ - PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_NAKED | - PE_FLAGS_PROTOCOL_ERROR | - PE_FLAGS_VDM_REQUEST_BUSY); -} - -static void pe_src_hard_reset_run(int port) -{ - /* - * Transition to the PE_SRC_Transition_to_default state when: - * 1) The PSHardResetTimer times out. - */ - if (pd_timer_is_expired(port, PE_TIMER_PS_HARD_RESET)) - set_state_pe(port, PE_SRC_TRANSITION_TO_DEFAULT); -} - -static void pe_src_hard_reset_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_PS_HARD_RESET); -} - -/** - * PE_SRC_Hard_Reset_Received - */ -static void pe_src_hard_reset_received_entry(int port) -{ - print_current_state(port); - - /* Start NoResponseTimer */ - pd_timer_enable(port, PE_TIMER_NO_RESPONSE, PD_T_NO_RESPONSE); - - /* Start PSHardResetTimer */ - pd_timer_enable(port, PE_TIMER_PS_HARD_RESET, PD_T_PS_HARD_RESET); -} - -static void pe_src_hard_reset_received_run(int port) -{ - /* - * Transition to the PE_SRC_Transition_to_default state when: - * 1) The PSHardResetTimer times out. - */ - if (pd_timer_is_expired(port, PE_TIMER_PS_HARD_RESET)) - set_state_pe(port, PE_SRC_TRANSITION_TO_DEFAULT); -} - -static void pe_src_hard_reset_received_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_PS_HARD_RESET); -} - -/** - * PE_SRC_Transition_To_Default - */ -static void pe_src_transition_to_default_entry(int port) -{ - print_current_state(port); - - /* Reset flags */ - pe[port].flags = 0; - - /* Reset DPM Request */ - pe[port].dpm_request = 0; - - /* - * Request Device Policy Manager to request power - * supply Hard Resets to vSafe5V via vSafe0V - * Reset local HW - * Request Device Policy Manager to set Port Data - * Role to DFP and turn off VCONN - */ - tc_hard_reset_request(port); -} - -static void pe_src_transition_to_default_run(int port) -{ - /* - * Transition to the PE_SRC_Startup state when: - * 1) The power supply has reached the default level. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_PS_RESET_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_PS_RESET_COMPLETE); - /* Inform the Protocol Layer that the Hard Reset is complete */ - prl_hard_reset_complete(port); - set_state_pe(port, PE_SRC_STARTUP); - } -} - -/** - * PE_SNK_Startup State - */ -static void pe_snk_startup_entry(int port) -{ - print_current_state(port); - - /* Reset the protocol layer */ - prl_reset_soft(port); - - /* Set initial data role */ - pe[port].data_role = pd_get_data_role(port); - - /* Set initial power role */ - pe[port].power_role = PD_ROLE_SINK; - - /* Invalidate explicit contract */ - pe_invalidate_explicit_contract(port); - - if (PE_CHK_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE); - /* - * Protocol layer reset clears the message IDs for all SOP - * types. Indicate that a SOP' soft reset is required before any - * other messages are sent to the cable. - * - * Note that other paths into this state are for the initial - * connection and for a hard reset. In both cases the cable - * should also automatically clear the message IDs so don't - * generate an SOP' soft reset for those cases. Sending - * unnecessary SOP' soft resets causes bad behavior with - * some devices. See b/179325862. - */ - pd_dpm_request(port, DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND); - - /* - * Some port partners may violate spec and attempt to - * communicate with the cable after power role swaps, despite - * not being Vconn source. Disable our SOP' receiving here to - * avoid GoodCRC-ing any erroneous cable probes, and re-enable - * after our contract is in place. - */ - if (tc_is_vconn_src(port)) - tcpm_sop_prime_enable(port, false); - - dpm_remove_sink(port); - } else { - /* - * Set DiscoverIdentityTimer to trigger when we enter - * snk_ready for the first time. - */ - pd_timer_enable(port, PE_TIMER_DISCOVER_IDENTITY, 0); - - /* Clear port discovery/mode flags */ - pd_dfp_discovery_init(port); - pd_dfp_mode_init(port); - pe[port].discover_identity_counter = 0; - - /* Reset dr swap attempt counter */ - pe[port].dr_swap_attempt_counter = 0; - - /* Reset VCONN swap counter */ - pe[port].vconn_swap_counter = 0; - /* - * TODO: POLICY decision: - * Mark that we'd like to try being Vconn source and DFP - */ - PE_SET_FLAG(port, PE_FLAGS_DR_SWAP_TO_DFP); - PE_SET_FLAG(port, PE_FLAGS_VCONN_SWAP_TO_ON); - } - - /* - * Request sink caps for FRS, output power consideration, or reporting - * to the AP through host commands. - * - * On entry to the PE_SNK_Ready state if the Sink supports Fast Role - * Swap, then the Policy Engine Shall do the following: - * - Send a Get_Sink_Cap Message - */ - if (IS_ENABLED(CONFIG_USB_PD_HOST_CMD) || - CONFIG_USB_PD_3A_PORTS > 0 || - IS_ENABLED(CONFIG_USB_PD_FRS)) - pd_dpm_request(port, DPM_REQUEST_GET_SNK_CAPS); - -} - -static void pe_snk_startup_run(int port) -{ - /* Wait until protocol layer is running */ - if (!prl_is_running(port)) - return; - - /* - * Once the reset process completes, the Policy Engine Shall - * transition to the PE_SNK_Discovery state - */ - set_state_pe(port, PE_SNK_DISCOVERY); -} - -/** - * PE_SNK_Discovery State - */ -static void pe_snk_discovery_entry(int port) -{ - print_current_state(port); -} - -static void pe_snk_discovery_run(int port) -{ - /* - * Transition to the PE_SNK_Wait_for_Capabilities state when: - * 1) VBUS has been detected - */ - if (!pd_check_vbus_level(port, VBUS_REMOVED)) - set_state_pe(port, PE_SNK_WAIT_FOR_CAPABILITIES); -} - -/** - * PE_SNK_Wait_For_Capabilities State - */ -static void pe_snk_wait_for_capabilities_entry(int port) -{ - print_current_state(port); - - /* Initialize and start the SinkWaitCapTimer */ - pd_timer_enable(port, PE_TIMER_TIMEOUT, PD_T_SINK_WAIT_CAP); -} - -static void pe_snk_wait_for_capabilities_run(int port) -{ - uint8_t type; - uint8_t cnt; - uint8_t ext; - - /* - * Transition to the PE_SNK_Evaluate_Capability state when: - * 1) A Source_Capabilities Message is received. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - ext = PD_HEADER_EXT(rx_emsg[port].header); - - if ((ext == 0) && (cnt > 0) && (type == PD_DATA_SOURCE_CAP)) { - set_state_pe(port, PE_SNK_EVALUATE_CAPABILITY); - return; - } - } - - /* When the SinkWaitCapTimer times out, perform a Hard Reset. */ - if (pd_timer_is_expired(port, PE_TIMER_TIMEOUT)) { - PE_SET_FLAG(port, PE_FLAGS_SNK_WAIT_CAP_TIMEOUT); - set_state_pe(port, PE_SNK_HARD_RESET); - } -} - -static void pe_snk_wait_for_capabilities_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_TIMEOUT); -} - -/** - * PE_SNK_Evaluate_Capability State - */ -static void pe_snk_evaluate_capability_entry(int port) -{ - uint32_t *pdo = (uint32_t *)rx_emsg[port].buf; - uint32_t num = rx_emsg[port].len >> 2; - - print_current_state(port); - - /* Reset Hard Reset counter to zero */ - pe[port].hard_reset_counter = 0; - - /* Set to highest revision supported by both ports. */ - prl_set_rev(port, TCPCI_MSG_SOP, - MIN(PD_REVISION, PD_HEADER_REV(rx_emsg[port].header))); - - set_cable_rev(port); - - /* Parse source caps if they have changed */ - if (pe[port].src_cap_cnt != num || - memcmp(pdo, pe[port].src_caps, num << 2)) { - /* - * If port policy preference is to be a power role source, - * then request a power role swap. If we'd previously queued a - * PR swap but can now charge from this device, clear it. - */ - if (!pd_can_charge_from_device(port, num, pdo)) - pd_request_power_swap(port); - else - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_PR_SWAP); - } - - pe_update_src_pdo_flags(port, num, pdo); - pd_set_src_caps(port, num, pdo); - - /* Evaluate the options based on supplied capabilities */ - pd_process_source_cap(port, pe[port].src_cap_cnt, pe[port].src_caps); - - /* Device Policy Response Received */ - set_state_pe(port, PE_SNK_SELECT_CAPABILITY); - -#ifdef HAS_TASK_DPS - /* Wake DPS task to evaluate the SrcCaps */ - task_wake(TASK_ID_DPS); -#endif -} - -/** - * PE_SNK_Select_Capability State - */ -static void pe_snk_select_capability_entry(int port) -{ - print_current_state(port); - - /* Send Request */ - pe_send_request_msg(port); - pe_sender_response_msg_entry(port); - - /* We are PD Connected */ - PE_SET_FLAG(port, PE_FLAGS_PD_CONNECTION); - tc_pd_connection(port, 1); -} - -static void pe_snk_select_capability_run(int port) -{ - uint8_t type; - uint8_t cnt; - enum tcpci_msg_type sop; - enum pe_msg_check msg_check; - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - /* - * Handle discarded message - */ - if (msg_check & PE_MSG_DISCARDED) { - /* - * The sent REQUEST message was discarded. This can be at - * the start of an AMS or in the middle. Handle what to - * do based on where we came from. - * 1) SE_SNK_EVALUATE_CAPABILITY: sends SoftReset - * 2) SE_SNK_READY: goes back to SNK Ready - */ - if (get_last_state_pe(port) == PE_SNK_EVALUATE_CAPABILITY) - pe_send_soft_reset(port, TCPCI_MSG_SOP); - else - set_state_pe(port, PE_SNK_READY); - return; - } - - if ((msg_check & PE_MSG_SENT) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - - /* - * Transition to the PE_SNK_Transition_Sink state when: - * 1) An Accept Message is received from the Source. - * - * Transition to the PE_SNK_Wait_for_Capabilities state when: - * 1) There is no Explicit Contract in place and - * 2) A Reject Message is received from the Source or - * 3) A Wait Message is received from the Source. - * - * Transition to the PE_SNK_Ready state when: - * 1) There is an Explicit Contract in place and - * 2) A Reject Message is received from the Source or - * 3) A Wait Message is received from the Source. - * - * Transition to the PE_SNK_Hard_Reset state when: - * 1) A SenderResponseTimer timeout occurs. - */ - - /* Only look at control messages */ - if (cnt == 0) { - /* - * Accept Message Received - */ - if (type == PD_CTRL_ACCEPT) { - /* explicit contract is now in place */ - pe_set_explicit_contract(port); - - set_state_pe(port, PE_SNK_TRANSITION_SINK); - - return; - } - /* - * Reject or Wait Message Received - */ - else if (type == PD_CTRL_REJECT || - type == PD_CTRL_WAIT) { - if (type == PD_CTRL_WAIT) - PE_SET_FLAG(port, PE_FLAGS_WAIT); - - pd_timer_disable(port, PE_TIMER_SINK_REQUEST); - - /* - * We had a previous explicit contract, so - * transition to PE_SNK_Ready - */ - if (PE_CHK_FLAG(port, - PE_FLAGS_EXPLICIT_CONTRACT)) - set_state_pe(port, PE_SNK_READY); - /* - * No previous explicit contract, so transition - * to PE_SNK_Wait_For_Capabilities - */ - else - set_state_pe(port, - PE_SNK_WAIT_FOR_CAPABILITIES); - return; - } - /* - * Unexpected Control Message Received - */ - else { - /* Send Soft Reset */ - pe_send_soft_reset(port, sop); - return; - } - } - /* - * Unexpected Data Message - */ - else { - /* Send Soft Reset */ - pe_send_soft_reset(port, sop); - return; - } - } - - /* SenderResponsetimer timeout */ - if (pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) - set_state_pe(port, PE_SNK_HARD_RESET); -} - -void pe_snk_select_capability_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -/** - * PE_SNK_Transition_Sink State - */ -static void pe_snk_transition_sink_entry(int port) -{ - print_current_state(port); - - /* Initialize and run PSTransitionTimer */ - pd_timer_enable(port, PE_TIMER_PS_TRANSITION, PD_T_PS_TRANSITION); -} - -static void pe_snk_transition_sink_run(int port) -{ - /* - * Transition to the PE_SNK_Ready state when: - * 1) A PS_RDY Message is received from the Source. - * - * Transition to the PE_SNK_Hard_Reset state when: - * 1) A Protocol Error occurs. - */ - - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - /* - * PS_RDY message received - */ - if ((PD_HEADER_CNT(rx_emsg[port].header) == 0) && - (PD_HEADER_TYPE(rx_emsg[port].header) == - PD_CTRL_PS_RDY)) { - /* - * Set first message flag to trigger a wait and add - * jitter delay when operating in PD2.0 mode. - */ - PE_SET_FLAG(port, PE_FLAGS_FIRST_MSG); - pd_timer_disable(port, PE_TIMER_WAIT_AND_ADD_JITTER); - - /* - * If we've successfully completed our new power - * contract, ensure SOP' communication is enabled before - * entering PE_SNK_READY. It may have been disabled - * during a power role swap to avoid interoperability - * issues with out-of-spec partners. - */ - if (tc_is_vconn_src(port)) - tcpm_sop_prime_enable(port, true); - - /* - * Evaluate port's sink caps for FRS current, if - * already available - */ - if (pd_get_snk_cap_cnt(port) > 0) - dpm_evaluate_sink_fixed_pdo(port, - *pd_get_snk_caps(port)); - - set_state_pe(port, PE_SNK_READY); - } else { - /* - * Protocol Error - */ - set_state_pe(port, PE_SNK_HARD_RESET); - } - return; - } - - /* - * Timeout will lead to a Hard Reset - */ - if (pd_timer_is_expired(port, PE_TIMER_PS_TRANSITION) && - pe[port].hard_reset_counter <= N_HARD_RESET_COUNT) { - PE_SET_FLAG(port, PE_FLAGS_PS_TRANSITION_TIMEOUT); - - set_state_pe(port, PE_SNK_HARD_RESET); - } -} - -static void pe_snk_transition_sink_exit(int port) -{ - /* Transition Sink's power supply to the new power level */ - pd_set_input_current_limit(port, - pe[port].curr_limit, pe[port].supply_voltage); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - /* Set ceiling based on what's negotiated */ - charge_manager_set_ceil(port, - CEIL_REQUESTOR_PD, pe[port].curr_limit); - - pd_timer_disable(port, PE_TIMER_PS_TRANSITION); - - if (IS_ENABLED(CONFIG_USB_PD_DPS)) - if (charge_manager_get_active_charge_port() == port) - dps_update_stabilized_time(port); -} - - -/** - * PE_SNK_Ready State - */ -static void pe_snk_ready_entry(int port) -{ - print_current_state(port); - - /* Ensure any message send flags are cleaned up */ - PE_CLR_FLAG(port, PE_FLAGS_READY_CLR); - - /* Clear DPM Current Request */ - pe[port].dpm_curr_request = 0; - - /* - * On entry to the PE_SNK_Ready state as the result of a wait, - * then do the following: - * 1) Initialize and run the SinkRequestTimer - */ - if (PE_CHK_FLAG(port, PE_FLAGS_WAIT)) { - PE_CLR_FLAG(port, PE_FLAGS_WAIT); - pd_timer_enable(port, PE_TIMER_SINK_REQUEST, - PD_T_SINK_REQUEST); - } - - /* - * Wait and add jitter if we are operating in PD2.0 mode and no messages - * have been sent since enter this state. - */ - pe_update_wait_and_add_jitter_timer(port); -} - -static void pe_snk_ready_run(int port) -{ - /* - * Handle incoming messages before discovery and DPMs other than hard - * reset - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - uint8_t type = PD_HEADER_TYPE(rx_emsg[port].header); - uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header); - uint8_t ext = PD_HEADER_EXT(rx_emsg[port].header); - uint32_t *payload = (uint32_t *)rx_emsg[port].buf; - - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - /* Extended Message Request */ - if (ext > 0) { - switch (type) { -#if defined(CONFIG_USB_PD_EXTENDED_MESSAGES) && defined(CONFIG_BATTERY) - case PD_EXT_GET_BATTERY_CAP: - set_state_pe(port, PE_GIVE_BATTERY_CAP); - break; - case PD_EXT_GET_BATTERY_STATUS: - set_state_pe(port, PE_GIVE_BATTERY_STATUS); - break; -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES && CONFIG_BATTERY */ - default: - extended_message_not_supported(port, payload); - } - return; - } - /* Data Messages */ - else if (cnt > 0) { - switch (type) { - case PD_DATA_SOURCE_CAP: - set_state_pe(port, - PE_SNK_EVALUATE_CAPABILITY); - break; - case PD_DATA_VENDOR_DEF: - if (PD_HEADER_TYPE(rx_emsg[port].header) == - PD_DATA_VENDOR_DEF) { - if (PD_VDO_SVDM(*payload)) - set_state_pe(port, - PE_VDM_RESPONSE); - else - set_state_pe(port, - PE_HANDLE_CUSTOM_VDM_REQUEST); - } - break; - case PD_DATA_BIST: - set_state_pe(port, PE_BIST_TX); - break; - default: - set_state_pe(port, PE_SEND_NOT_SUPPORTED); - } - return; - } - /* Control Messages */ - else { - switch (type) { - case PD_CTRL_GOOD_CRC: - /* Do nothing */ - break; - case PD_CTRL_PING: - /* Do nothing */ - break; - case PD_CTRL_GET_SOURCE_CAP: - set_state_pe(port, PE_DR_SNK_GIVE_SOURCE_CAP); - return; - case PD_CTRL_GET_SINK_CAP: - set_state_pe(port, PE_SNK_GIVE_SINK_CAP); - return; - case PD_CTRL_GOTO_MIN: - set_state_pe(port, PE_SNK_TRANSITION_SINK); - return; - case PD_CTRL_PR_SWAP: - set_state_pe(port, - PE_PRS_SNK_SRC_EVALUATE_SWAP); - return; - case PD_CTRL_DR_SWAP: - if (PE_CHK_FLAG(port, PE_FLAGS_MODAL_OPERATION)) - set_state_pe(port, PE_SNK_HARD_RESET); - else - set_state_pe(port, - PE_DRS_EVALUATE_SWAP); - return; - case PD_CTRL_VCONN_SWAP: - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_state_pe(port, - PE_VCS_EVALUATE_SWAP); - else - set_state_pe(port, - PE_SEND_NOT_SUPPORTED); - return; - case PD_CTRL_NOT_SUPPORTED: - /* Do nothing */ - break; - /* - * USB PD 3.0 6.8.1: - * Receiving an unexpected message shall be responded - * to with a soft reset message. - */ - case PD_CTRL_ACCEPT: - case PD_CTRL_REJECT: - case PD_CTRL_WAIT: - case PD_CTRL_PS_RDY: - pe_send_soft_reset(port, - PD_HEADER_GET_SOP(rx_emsg[port].header)); - return; - /* - * Receiving an unknown or unsupported message - * shall be responded to with a not supported message. - */ - default: - set_state_pe(port, PE_SEND_NOT_SUPPORTED); - return; - } - } - } - - /* - * Make sure the PRL layer isn't busy with receiving or transmitting - * chunked messages before attempting to transmit a new message. - */ - if (prl_is_busy(port)) - return; - - if (PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_CONTINUE)) { - PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_CONTINUE); - set_state_pe(port, PE_VDM_REQUEST_DPM); - return; - } - - if (pd_timer_is_disabled(port, PE_TIMER_WAIT_AND_ADD_JITTER) || - pd_timer_is_expired(port, PE_TIMER_WAIT_AND_ADD_JITTER)) { - PE_CLR_FLAG(port, PE_FLAGS_FIRST_MSG); - pd_timer_disable(port, PE_TIMER_WAIT_AND_ADD_JITTER); - - if (pd_timer_is_expired(port, PE_TIMER_SINK_REQUEST)) { - pd_timer_disable(port, PE_TIMER_SINK_REQUEST); - set_state_pe(port, PE_SNK_SELECT_CAPABILITY); - return; - } - - /* - * Handle Device Policy Manager Requests - */ - if (sink_dpm_requests(port)) - return; - - /* - * Attempt discovery if possible, and return if state was - * changed for that discovery. - */ - if (pe_attempt_port_discovery(port)) - return; - - /* No DPM requests; attempt mode entry/exit if needed */ - dpm_run(port); - - } -} - -/** - * PE_SNK_Hard_Reset - */ -static void pe_snk_hard_reset_entry(int port) -{ -#ifdef CONFIG_USB_PD_RESET_MIN_BATT_SOC - int batt_soc; -#endif - - print_current_state(port); - - /* - * Note: If the SinkWaitCapTimer times out and the HardResetCounter is - * greater than nHardResetCount the Sink Shall assume that the - * Source is non-responsive. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_SNK_WAIT_CAP_TIMEOUT) && - pe[port].hard_reset_counter > N_HARD_RESET_COUNT) { - set_state_pe(port, PE_SRC_DISABLED); - return; - } - - /* - * If we're about to kill our active charge port and have no battery to - * supply power, disable the PE layer instead. If we have no battery, - * but we haven't determined our active charge port yet, also avoid - * performing the HardReset. It might be that this port was our active - * charge port. - * - * Note: On systems without batteries (ex. chromeboxes), it's preferable - * to brown out rather than leave the port only semi-functional for a - * customer. For systems which should have a battery, this condition is - * not expected to be encountered by a customer. - */ - if (IS_ENABLED(CONFIG_BATTERY) && (battery_is_present() == BP_NO) && - IS_ENABLED(CONFIG_CHARGE_MANAGER) && - ((port == charge_manager_get_active_charge_port() || - (charge_manager_get_active_charge_port() == CHARGE_PORT_NONE))) && - system_get_reset_flags() & EC_RESET_FLAG_SYSJUMP) { - CPRINTS("C%d: Disabling port to avoid brown out, " - "please reboot EC to enable port again", port); - set_state_pe(port, PE_SRC_DISABLED); - return; - - } - -#ifdef CONFIG_USB_PD_RESET_MIN_BATT_SOC - /* - * If the battery has not met a configured safe level for hard - * resets, set state to PE_SRC_Disabled as a hard - * reset could brown out the board. - * Note this may mean that high-power chargers will stay at - * 15W until a reset is sent, depending on boot timing. - * - * PE_FLAGS_SNK_WAITING_BATT flags will be cleared and - * PE state will be switched to PE_SNK_Startup when - * battery reaches CONFIG_USB_PD_RESET_MIN_BATT_SOC. - * See pe_update_waiting_batt_flag() for more details. - */ - batt_soc = usb_get_battery_soc(); - - if (batt_soc < CONFIG_USB_PD_RESET_MIN_BATT_SOC || - battery_get_disconnect_state() != BATTERY_NOT_DISCONNECTED) { - PE_SET_FLAG(port, PE_FLAGS_SNK_WAITING_BATT); - CPRINTS("C%d: Battery low %d%%! Stay in disabled state " \ - "until battery level reaches %d%%", port, batt_soc, - CONFIG_USB_PD_RESET_MIN_BATT_SOC); - set_state_pe(port, PE_SRC_DISABLED); - return; - } -#endif - - PE_CLR_FLAG(port, PE_FLAGS_SNK_WAIT_CAP_TIMEOUT | - PE_FLAGS_VDM_REQUEST_NAKED | - PE_FLAGS_PROTOCOL_ERROR | - PE_FLAGS_VDM_REQUEST_BUSY); - - /* Request the generation of Hard Reset Signaling by the PHY Layer */ - prl_execute_hard_reset(port); - - /* Increment the HardResetCounter */ - pe[port].hard_reset_counter++; - - /* - * Transition the Sink’s power supply to the new power level if - * PSTransistionTimer timeout occurred. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_PS_TRANSITION_TIMEOUT)) { - PE_CLR_FLAG(port, PE_FLAGS_PS_TRANSITION_TIMEOUT); - - /* Transition Sink's power supply to the new power level */ - pd_set_input_current_limit(port, pe[port].curr_limit, - pe[port].supply_voltage); - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - /* Set ceiling based on what's negotiated */ - charge_manager_set_ceil(port, CEIL_REQUESTOR_PD, - pe[port].curr_limit); - } -} - -static void pe_snk_hard_reset_run(int port) -{ - /* - * Transition to the PE_SNK_Transition_to_default state when: - * 1) The Hard Reset is complete. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_HARD_RESET_PENDING)) - return; - - set_state_pe(port, PE_SNK_TRANSITION_TO_DEFAULT); -} - -/** - * PE_SNK_Transition_to_default - */ -static void pe_snk_transition_to_default_entry(int port) -{ - print_current_state(port); - - /* Reset flags */ - pe[port].flags = 0; - - /* Reset DPM Request */ - pe[port].dpm_request = 0; - - /* Inform the TC Layer of Hard Reset */ - tc_hard_reset_request(port); -} - -static void pe_snk_transition_to_default_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_PS_RESET_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_PS_RESET_COMPLETE); - /* Inform the Protocol Layer that the Hard Reset is complete */ - prl_hard_reset_complete(port); - set_state_pe(port, PE_SNK_STARTUP); - } -} - -/** - * PE_SNK_Get_Source_Cap - */ -static void pe_snk_get_source_cap_entry(int port) -{ - print_current_state(port); - - /* Send a Get_Source_Cap Message */ - tx_emsg[port].len = 0; - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_GET_SOURCE_CAP); -} - -static void pe_snk_get_source_cap_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - set_state_pe(port, PE_SNK_READY); - } -} - -/** - * PE_SNK_Send_Soft_Reset and PE_SRC_Send_Soft_Reset - */ -static void pe_send_soft_reset_entry(int port) -{ - print_current_state(port); - - /* Reset Protocol Layer (softly) */ - prl_reset_soft(port); - - pe_sender_response_msg_entry(port); - - /* - * Mark the temporary timer PE_TIMER_TIMEOUT as expired to limit - * to sending a single SoftReset message. - */ - pd_timer_enable(port, PE_TIMER_TIMEOUT, 0); -} - -static void pe_send_soft_reset_run(int port) -{ - int type; - int cnt; - int ext; - enum pe_msg_check msg_check; - - /* Wait until protocol layer is running */ - if (!prl_is_running(port)) - return; - - /* - * Protocol layer is running, so need to send a single SoftReset. - * Use temporary timer to act as a flag to keep this as a single - * message send. - */ - if (!pd_timer_is_disabled(port, PE_TIMER_TIMEOUT)) { - pd_timer_disable(port, PE_TIMER_TIMEOUT); - - /* - * TODO(b/150614211): Soft reset type should match - * unexpected incoming message type - */ - /* Send Soft Reset message */ - send_ctrl_msg(port, - pe[port].soft_reset_sop, PD_CTRL_SOFT_RESET); - - return; - } - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - /* - * Handle discarded message - */ - if (msg_check == PE_MSG_DISCARDED) { - pe_set_ready_state(port); - return; - } - - /* - * Transition to the PE_SNK_Send_Capabilities or - * PE_SRC_Send_Capabilities state when: - * 1) An Accept Message has been received. - */ - if (msg_check == PE_MSG_SENT && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - ext = PD_HEADER_EXT(rx_emsg[port].header); - - if ((ext == 0) && (cnt == 0) && (type == PD_CTRL_ACCEPT)) { - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, - PE_SNK_WAIT_FOR_CAPABILITIES); - else - set_state_pe(port, - PE_SRC_SEND_CAPABILITIES); - return; - } - } - - /* - * Transition to PE_SNK_Hard_Reset or PE_SRC_Hard_Reset on Sender - * Response Timer Timeout or Protocol Layer or Protocol Error - */ - if (pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE) || - PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, PE_SNK_HARD_RESET); - else - set_state_pe(port, PE_SRC_HARD_RESET); - return; - } -} - -static void pe_send_soft_reset_exit(int port) -{ - pe_sender_response_msg_exit(port); - pd_timer_disable(port, PE_TIMER_TIMEOUT); -} - -/** - * PE_SNK_Soft_Reset and PE_SNK_Soft_Reset - */ -static void pe_soft_reset_entry(int port) -{ - print_current_state(port); - - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_ACCEPT); -} - -static void pe_soft_reset_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, PE_SNK_WAIT_FOR_CAPABILITIES); - else - set_state_pe(port, PE_SRC_SEND_CAPABILITIES); - } else if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - - if (pe[port].power_role == PD_ROLE_SINK) - set_state_pe(port, PE_SNK_HARD_RESET); - else - set_state_pe(port, PE_SRC_HARD_RESET); - } -} - -/** - * PE_SRC_Not_Supported and PE_SNK_Not_Supported - * - * 6.7.1 Soft Reset and Protocol Error (Revision 2.0, Version 1.3) - * An unrecognized or unsupported Message (except for a Structured VDM), - * received in the PE_SNK_Ready or PE_SRC_Ready states, Shall Not cause - * a Soft_Reset Message to be generated but instead a Reject Message - * Shall be generated. - */ -static void pe_send_not_supported_entry(int port) -{ - print_current_state(port); - - /* Request the Protocol Layer to send a Not_Supported Message. */ - if (prl_get_rev(port, TCPCI_MSG_SOP) > PD_REV20) - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_NOT_SUPPORTED); - else - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT); -} - -static void pe_send_not_supported_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - pe_set_ready_state(port); - - } -} - -/** - * PE_SRC_Chunk_Received and PE_SNK_Chunk_Received - * - * 6.11.2.1.1 Architecture of Device Including Chunking Layer (Revision 3.0, - * Version 2.0): If a PD Device or Cable Marker has no requirement to handle any - * message requiring more than one Chunk of any Extended Message, it May omit - * the Chunking Layer. In this case it Shall implement the - * ChunkingNotSupportedTimer to ensure compatible operation with partners which - * support Chunking. - * - * See also: - * 6.6.18.1 ChunkingNotSupportedTimer - * 8.3.3.6 Not Supported Message State Diagrams - */ -__maybe_unused static void pe_chunk_received_entry(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_REV30) || - IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) - assert(0); - - print_current_state(port); - pd_timer_enable(port, PE_TIMER_CHUNKING_NOT_SUPPORTED, - PD_T_CHUNKING_NOT_SUPPORTED); -} - -__maybe_unused static void pe_chunk_received_run(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_REV30) || - IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) - assert(0); - - if (pd_timer_is_expired(port, PE_TIMER_CHUNKING_NOT_SUPPORTED)) - set_state_pe(port, PE_SEND_NOT_SUPPORTED); -} - -__maybe_unused static void pe_chunk_received_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_CHUNKING_NOT_SUPPORTED); -} - -/** - * PE_SRC_Ping - */ -static void pe_src_ping_entry(int port) -{ - print_current_state(port); - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PING); -} - -static void pe_src_ping_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - set_state_pe(port, PE_SRC_READY); - } -} - -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES -/** - * PE_Give_Battery_Cap - */ -static void pe_give_battery_cap_entry(int port) -{ - uint8_t *payload = rx_emsg[port].buf; - uint16_t *msg = (uint16_t *)tx_emsg[port].buf; - - if (!IS_ENABLED(CONFIG_BATTERY)) - return; - print_current_state(port); - - /* Set VID */ - msg[BCDB_VID] = USB_VID_GOOGLE; - - /* Set PID */ - msg[BCDB_PID] = CONFIG_USB_PID; - - if (battery_is_present()) { - /* - * We only have one fixed battery, - * so make sure batt cap ref is 0. - * This value is the first byte after the headers. - */ - if (payload[0] != 0) { - /* Invalid battery reference */ - msg[BCDB_DESIGN_CAP] = 0; - msg[BCDB_FULL_CAP] = 0; - /* Set invalid battery bit in response bit 0, byte 8 */ - msg[BCDB_BATT_TYPE] = 1; - } else { - /* - * The Battery Design Capacity field shall return the - * Battery’s design capacity in tenths of Wh. If the - * Battery is Hot Swappable and is not present, the - * Battery Design Capacity field shall be set to 0. If - * the Battery is unable to report its Design Capacity, - * it shall return 0xFFFF - */ - msg[BCDB_DESIGN_CAP] = 0xffff; - - /* - * The Battery Last Full Charge Capacity field shall - * return the Battery’s last full charge capacity in - * tenths of Wh. If the Battery is Hot Swappable and - * is not present, the Battery Last Full Charge Capacity - * field shall be set to 0. If the Battery is unable to - * report its Design Capacity, the Battery Last Full - * Charge Capacity field shall be set to 0xFFFF. - */ - msg[BCDB_FULL_CAP] = 0xffff; - - - if (IS_ENABLED(HAS_TASK_HOSTCMD) && - *host_get_memmap(EC_MEMMAP_BATTERY_VERSION) != 0) { - int design_volt, design_cap, full_cap; - - design_volt = *(int *)host_get_memmap( - EC_MEMMAP_BATT_DVLT); - design_cap = *(int *)host_get_memmap( - EC_MEMMAP_BATT_DCAP); - full_cap = *(int *)host_get_memmap( - EC_MEMMAP_BATT_LFCC); - - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - msg[BCDB_DESIGN_CAP] = DIV_ROUND_NEAREST( - (design_cap * design_volt), - 100000); - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - msg[BCDB_FULL_CAP] = DIV_ROUND_NEAREST( - (design_cap * full_cap), - 100000); - } else { - uint32_t v; - uint32_t c; - - if (battery_design_voltage(&v) == 0) { - if (battery_design_capacity(&c) == 0) { - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - msg[BCDB_DESIGN_CAP] = - DIV_ROUND_NEAREST( - (c * v), - 100000); - } - - if (battery_full_charge_capacity(&c) - == 0) { - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - msg[BCDB_FULL_CAP] = - DIV_ROUND_NEAREST( - (c * v), - 100000); - } - } - - } - /* Valid battery selected */ - msg[BCDB_BATT_TYPE] = 0; - } - } else { - /* Battery not present indicated by 0's in the capacity */ - msg[BCDB_DESIGN_CAP] = 0; - msg[BCDB_FULL_CAP] = 0; - if (payload[0] != 0) - msg[BCDB_BATT_TYPE] = 1; - else - msg[BCDB_BATT_TYPE] = 0; - } - - /* Extended Battery Cap data is 9 bytes */ - tx_emsg[port].len = 9; - - send_ext_data_msg(port, TCPCI_MSG_SOP, PD_EXT_BATTERY_CAP); -} - -static void pe_give_battery_cap_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - pe_set_ready_state(port); - } -} - -/** - * PE_Give_Battery_Status - */ -static void pe_give_battery_status_entry(int port) -{ - uint8_t *payload = rx_emsg[port].buf; - uint32_t *msg = (uint32_t *)tx_emsg[port].buf; - - if (!IS_ENABLED(CONFIG_BATTERY)) - return; - print_current_state(port); - - if (battery_is_present()) { - /* - * We only have one fixed battery, - * so make sure batt cap ref is 0. - * This value is the first byte after the headers. - */ - if (payload[0] != 0) { - /* Invalid battery reference */ - *msg = BSDO_CAP(BSDO_CAP_UNKNOWN); - *msg |= BSDO_INVALID; - } else { - uint32_t v; - uint32_t c; - - *msg = BSDO_CAP(BSDO_CAP_UNKNOWN); - - if (IS_ENABLED(HAS_TASK_HOSTCMD) && - *host_get_memmap(EC_MEMMAP_BATTERY_VERSION) != 0) { - v = *(int *)host_get_memmap( - EC_MEMMAP_BATT_DVLT); - c = *(int *)host_get_memmap( - EC_MEMMAP_BATT_CAP); - - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - *msg = BSDO_CAP(DIV_ROUND_NEAREST((c * v), - 100000)); - } else if (battery_design_voltage(&v) == 0 && - battery_remaining_capacity(&c) == 0) { - /* - * Wh = (c * v) / 1000000 - * 10th of a Wh = Wh * 10 - */ - *msg = BSDO_CAP(DIV_ROUND_NEAREST((c * v), - 100000)); - } - - /* Battery is present */ - *msg |= BSDO_PRESENT; - - /* - * For drivers that are not smart battery compliant, - * battery_status() returns EC_ERROR_UNIMPLEMENTED and - * the battery is assumed to be idle. - */ - if (battery_status(&c) != 0) { - *msg |= BSDO_IDLE; /* assume idle */ - } else { - if (c & STATUS_FULLY_CHARGED) - /* Fully charged */ - *msg |= BSDO_IDLE; - else if (c & STATUS_DISCHARGING) - /* Discharging */ - *msg |= BSDO_DISCHARGING; - /* else battery is charging.*/ - } - } - } else { - *msg = BSDO_CAP(BSDO_CAP_UNKNOWN); - if (payload[0] != 0) - *msg |= BSDO_INVALID; - } - - /* Battery Status data is 4 bytes */ - tx_emsg[port].len = 4; - - send_data_msg(port, TCPCI_MSG_SOP, PD_DATA_BATTERY_STATUS); -} - -static void pe_give_battery_status_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - set_state_pe(port, PE_SRC_READY); - } -} - -/** - * PE_SRC_Send_Source_Alert and - * PE_SNK_Send_Sink_Alert - */ -static void pe_send_alert_entry(int port) -{ - uint32_t *msg = (uint32_t *)tx_emsg[port].buf; - uint32_t *len = &tx_emsg[port].len; - - print_current_state(port); - - if (pd_build_alert_msg(msg, len, pe[port].power_role) != EC_SUCCESS) - pe_set_ready_state(port); - - /* Request the Protocol Layer to send Alert Message. */ - send_data_msg(port, TCPCI_MSG_SOP, PD_DATA_ALERT); -} - -static void pe_send_alert_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - pe_set_ready_state(port); - } -} -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ - -/** - * PE_DRS_Evaluate_Swap - */ -static void pe_drs_evaluate_swap_entry(int port) -{ - print_current_state(port); - - /* Get evaluation of Data Role Swap request from DPM */ - if (pd_check_data_swap(port, pe[port].data_role)) { - PE_SET_FLAG(port, PE_FLAGS_ACCEPT); - /* - * PE_DRS_UFP_DFP_Evaluate_Swap and - * PE_DRS_DFP_UFP_Evaluate_Swap states embedded here. - */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_ACCEPT); - } else { - /* - * PE_DRS_UFP_DFP_Reject_Swap and PE_DRS_DFP_UFP_Reject_Swap - * states embedded here. - */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT); - } -} - -static void pe_drs_evaluate_swap_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* Accept Message sent. Transtion to PE_DRS_Change */ - if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) { - PE_CLR_FLAG(port, PE_FLAGS_ACCEPT); - set_state_pe(port, PE_DRS_CHANGE); - } else { - /* - * Message sent. Transition back to PE_SRC_Ready or - * PE_SNK_Ready. - */ - pe_set_ready_state(port); - } - } -} - -/** - * PE_DRS_Change - */ -static void pe_drs_change_entry(int port) -{ - print_current_state(port); - - /* - * PE_DRS_UFP_DFP_Change_to_DFP and PE_DRS_DFP_UFP_Change_to_UFP - * states embedded here. - */ - /* Request DPM to change port data role */ - pd_request_data_swap(port); -} - -static void pe_drs_change_run(int port) -{ - /* Wait until the data role is changed */ - if (pe[port].data_role == pd_get_data_role(port)) - return; - - /* Update the data role */ - pe[port].data_role = pd_get_data_role(port); - - if (pe[port].data_role == PD_ROLE_DFP) - PE_CLR_FLAG(port, PE_FLAGS_DR_SWAP_TO_DFP); - - /* - * Port changed. Transition back to PE_SRC_Ready or - * PE_SNK_Ready. - */ - pe_set_ready_state(port); -} - -/** - * PE_DRS_Send_Swap - */ -static void pe_drs_send_swap_entry(int port) -{ - print_current_state(port); - - /* - * PE_DRS_UFP_DFP_Send_Swap and PE_DRS_DFP_UFP_Send_Swap - * states embedded here. - */ - /* Request the Protocol Layer to send a DR_Swap Message */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_DR_SWAP); - pe_sender_response_msg_entry(port); -} - -static void pe_drs_send_swap_run(int port) -{ - int type; - int cnt; - int ext; - enum pe_msg_check msg_check; - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - /* - * Transition to PE_DRS_Change when: - * 1) An Accept Message is received. - * - * Transition to PE_SRC_Ready or PE_SNK_Ready state when: - * 1) A Reject Message is received. - * 2) Or a Wait Message is received. - */ - if ((msg_check & PE_MSG_SENT) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - ext = PD_HEADER_EXT(rx_emsg[port].header); - - if ((ext == 0) && (cnt == 0)) { - if (type == PD_CTRL_ACCEPT) { - set_state_pe(port, PE_DRS_CHANGE); - return; - } else if ((type == PD_CTRL_REJECT) || - (type == PD_CTRL_WAIT) || - (type == PD_CTRL_NOT_SUPPORTED)) { - pe_set_ready_state(port); - return; - } - } - } - - /* - * Transition to PE_SRC_Ready or PE_SNK_Ready state when: - * 1) the SenderResponseTimer times out. - * 2) Message was discarded. - */ - if ((msg_check & PE_MSG_DISCARDED) || - pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) - pe_set_ready_state(port); -} - -static void pe_drs_send_swap_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -/** - * PE_PRS_SRC_SNK_Evaluate_Swap - */ -static void pe_prs_src_snk_evaluate_swap_entry(int port) -{ - print_current_state(port); - - if (!pd_check_power_swap(port)) { - /* PE_PRS_SRC_SNK_Reject_PR_Swap state embedded here */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT); - } else { - tc_request_power_swap(port); - /* PE_PRS_SRC_SNK_Accept_Swap state embedded here */ - PE_SET_FLAG(port, PE_FLAGS_ACCEPT); - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_ACCEPT); - } -} - -static void pe_prs_src_snk_evaluate_swap_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) { - PE_CLR_FLAG(port, PE_FLAGS_ACCEPT); - - /* - * Clear any pending DPM power role swap request so we - * don't trigger a power role swap request back to src - * power role. - */ - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_PR_SWAP); - /* - * Power Role Swap OK, transition to - * PE_PRS_SRC_SNK_Transition_to_off - */ - set_state_pe(port, PE_PRS_SRC_SNK_TRANSITION_TO_OFF); - } else { - /* Message sent, return to PE_SRC_Ready */ - set_state_pe(port, PE_SRC_READY); - } - } -} - -/** - * PE_PRS_SRC_SNK_Transition_To_Off - */ -static void pe_prs_src_snk_transition_to_off_entry(int port) -{ - print_current_state(port); - - /* Contract is invalid */ - pe_invalidate_explicit_contract(port); - - /* Tell TypeC to power off the source */ - tc_src_power_off(port); - - pd_timer_enable(port, PE_TIMER_PS_SOURCE, - PD_POWER_SUPPLY_TURN_OFF_DELAY); -} - -static void pe_prs_src_snk_transition_to_off_run(int port) -{ - /* - * This is a non-interruptible AMS and power is transitioning - hard - * reset on interruption. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - tc_pr_swap_complete(port, 0); - set_state_pe(port, PE_SRC_HARD_RESET); - } - - /* Give time for supply to power off */ - if (pd_timer_is_expired(port, PE_TIMER_PS_SOURCE) && - pd_check_vbus_level(port, VBUS_SAFE0V)) - set_state_pe(port, PE_PRS_SRC_SNK_ASSERT_RD); -} - -static void pe_prs_src_snk_transition_to_off_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_PS_SOURCE); -} - -/** - * PE_PRS_SRC_SNK_Assert_Rd - */ -static void pe_prs_src_snk_assert_rd_entry(int port) -{ - print_current_state(port); - - /* Tell TypeC to swap from Attached.SRC to Attached.SNK */ - tc_prs_src_snk_assert_rd(port); -} - -static void pe_prs_src_snk_assert_rd_run(int port) -{ - /* Wait until Rd is asserted */ - if (tc_is_attached_snk(port)) - set_state_pe(port, PE_PRS_SRC_SNK_WAIT_SOURCE_ON); -} - -/** - * PE_PRS_SRC_SNK_Wait_Source_On - */ -static void pe_prs_src_snk_wait_source_on_entry(int port) -{ - print_current_state(port); - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PS_RDY); -} - -static void pe_prs_src_snk_wait_source_on_run(int port) -{ - if (pd_timer_is_disabled(port, PE_TIMER_PS_SOURCE) && - PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* Update pe power role */ - pe[port].power_role = pd_get_power_role(port); - pd_timer_enable(port, PE_TIMER_PS_SOURCE, PD_T_PS_SOURCE_ON); - } - - /* - * Transition to PE_SNK_Startup when: - * 1) A PS_RDY Message is received. - */ - if (!pd_timer_is_disabled(port, PE_TIMER_PS_SOURCE) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - int type = PD_HEADER_TYPE(rx_emsg[port].header); - int cnt = PD_HEADER_CNT(rx_emsg[port].header); - int ext = PD_HEADER_EXT(rx_emsg[port].header); - - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - if ((ext == 0) && (cnt == 0) && (type == PD_CTRL_PS_RDY)) { - PE_SET_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE); - set_state_pe(port, PE_SNK_STARTUP); - } else { - int sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - /* - * USB PD 3.0 6.8.1: - * Receiving an unexpected message shall be responded - * to with a soft reset message. - */ - pe_send_soft_reset(port, sop); - } - return; - } - - /* - * Transition to ErrorRecovery state when: - * 1) The PSSourceOnTimer times out. - * 2) PS_RDY not sent after retries. - */ - if (pd_timer_is_expired(port, PE_TIMER_PS_SOURCE) || - PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - - set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY); - return; - } -} - -static void pe_prs_src_snk_wait_source_on_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_PS_SOURCE); - tc_pr_swap_complete(port, - PE_CHK_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE)); -} - -/** - * PE_PRS_SRC_SNK_Send_Swap - */ -static void pe_prs_src_snk_send_swap_entry(int port) -{ - print_current_state(port); - - /* Making an attempt to PR_Swap, clear we were possibly waiting */ - pd_timer_disable(port, PE_TIMER_PR_SWAP_WAIT); - - /* Request the Protocol Layer to send a PR_Swap Message. */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PR_SWAP); - pe_sender_response_msg_entry(port); -} - -static void pe_prs_src_snk_send_swap_run(int port) -{ - int type; - int cnt; - int ext; - enum pe_msg_check msg_check; - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - /* - * Transition to PE_PRS_SRC_SNK_Transition_To_Off when: - * 1) An Accept Message is received. - * - * Transition to PE_SRC_Ready state when: - * 1) A Reject Message is received. - * 2) Or a Wait Message is received. - */ - if ((msg_check & PE_MSG_SENT) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - ext = PD_HEADER_EXT(rx_emsg[port].header); - - if ((ext == 0) && (cnt == 0)) { - if (type == PD_CTRL_ACCEPT) { - pe[port].src_snk_pr_swap_counter = 0; - tc_request_power_swap(port); - set_state_pe(port, - PE_PRS_SRC_SNK_TRANSITION_TO_OFF); - } else if (type == PD_CTRL_REJECT) { - pe[port].src_snk_pr_swap_counter = 0; - set_state_pe(port, PE_SRC_READY); - } else if (type == PD_CTRL_WAIT) { - if (pe[port].src_snk_pr_swap_counter < - N_SNK_SRC_PR_SWAP_COUNT) { - PE_SET_FLAG(port, - PE_FLAGS_WAITING_PR_SWAP); - pd_timer_enable(port, - PE_TIMER_PR_SWAP_WAIT, - PD_T_PR_SWAP_WAIT); - } - pe[port].src_snk_pr_swap_counter++; - set_state_pe(port, PE_SRC_READY); - } - return; - } - } - - /* - * Transition to PE_SRC_Ready state when: - * 1) Or the SenderResponseTimer times out. - * 2) Message was discarded. - */ - if ((msg_check & PE_MSG_DISCARDED) || - pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) - set_state_pe(port, PE_SRC_READY); -} - -static void pe_prs_src_snk_send_swap_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -/** - * PE_PRS_SNK_SRC_Evaluate_Swap - */ -static void pe_prs_snk_src_evaluate_swap_entry(int port) -{ - print_current_state(port); - - /* - * Cancel any pending PR swap request due to a received Wait since the - * partner just sent us a PR swap message. - */ - PE_CLR_FLAG(port, PE_FLAGS_WAITING_PR_SWAP); - pe[port].src_snk_pr_swap_counter = 0; - - if (!pd_check_power_swap(port)) { - /* PE_PRS_SNK_SRC_Reject_Swap state embedded here */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT); - } else { - tc_request_power_swap(port); - /* PE_PRS_SNK_SRC_Accept_Swap state embedded here */ - PE_SET_FLAG(port, PE_FLAGS_ACCEPT); - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_ACCEPT); - } -} - -static void pe_prs_snk_src_evaluate_swap_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) { - PE_CLR_FLAG(port, PE_FLAGS_ACCEPT); - - /* - * Clear any pending DPM power role swap request so we - * don't trigger a power role swap request back to sink - * power role. - */ - PE_CLR_DPM_REQUEST(port, DPM_REQUEST_PR_SWAP); - /* - * Accept message sent, transition to - * PE_PRS_SNK_SRC_Transition_to_off - */ - set_state_pe(port, PE_PRS_SNK_SRC_TRANSITION_TO_OFF); - } else { - /* Message sent, return to PE_SNK_Ready */ - set_state_pe(port, PE_SNK_READY); - } - } - - if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - /* - * Protocol Error occurs while PR swap, this may - * brown out if the port-parnter can't hold VBUS - * for tSrcTransition. Notify TC that we end the PR - * swap and start to watch VBUS. - * - * TODO(b:155181980): issue soft reset on protocol error. - */ - tc_pr_swap_complete(port, 0); - } -} - -/** - * PE_PRS_SNK_SRC_Transition_To_Off - * PE_FRS_SNK_SRC_Transition_To_Off - * - * NOTE: Shared action code used for Power Role Swap and Fast Role Swap - */ -static void pe_prs_snk_src_transition_to_off_entry(int port) -{ - print_current_state(port); - - if (!IS_ENABLED(CONFIG_USB_PD_REV30) || - !pe_in_frs_mode(port)) - tc_snk_power_off(port); - - pd_timer_enable(port, PE_TIMER_PS_SOURCE, PD_T_PS_SOURCE_OFF); -} - -static void pe_prs_snk_src_transition_to_off_run(int port) -{ - int type; - int cnt; - int ext; - - /* - * Transition to ErrorRecovery state when: - * 1) The PSSourceOffTimer times out. - */ - if (pd_timer_is_expired(port, PE_TIMER_PS_SOURCE)) - set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY); - - /* - * Transition to PE_PRS_SNK_SRC_Assert_Rp when: - * 1) An PS_RDY Message is received. - */ - else if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - ext = PD_HEADER_EXT(rx_emsg[port].header); - - if ((ext == 0) && (cnt == 0) && (type == PD_CTRL_PS_RDY)) { - /* - * FRS: We are always ready to drive vSafe5v, so just - * skip PE_FRS_SNK_SRC_Vbus_Applied and go direct to - * PE_FRS_SNK_SRC_Assert_Rp - */ - set_state_pe(port, PE_PRS_SNK_SRC_ASSERT_RP); - } - } -} - -static void pe_prs_snk_src_transition_to_off_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_PS_SOURCE); -} - -/** - * PE_PRS_SNK_SRC_Assert_Rp - * PE_FRS_SNK_SRC_Assert_Rp - * - * NOTE: Shared action code used for Power Role Swap and Fast Role Swap - */ -static void pe_prs_snk_src_assert_rp_entry(int port) -{ - print_current_state(port); - - /* - * Tell TypeC to Power/Fast Role Swap (PRS/FRS) from - * Attached.SNK to Attached.SRC - */ - tc_prs_snk_src_assert_rp(port); -} - -static void pe_prs_snk_src_assert_rp_run(int port) -{ - /* Wait until TypeC is in the Attached.SRC state */ - if (tc_is_attached_src(port)) { - if (!IS_ENABLED(CONFIG_USB_PD_REV30) || - !pe_in_frs_mode(port)) { - /* Contract is invalid now */ - pe_invalidate_explicit_contract(port); - } - set_state_pe(port, PE_PRS_SNK_SRC_SOURCE_ON); - } -} - -/** - * PE_PRS_SNK_SRC_Source_On - * PE_FRS_SNK_SRC_Source_On - * - * NOTE: Shared action code used for Power Role Swap and Fast Role Swap - */ -static void pe_prs_snk_src_source_on_entry(int port) -{ - print_current_state(port); - - /* - * VBUS was enabled when the TypeC state machine entered - * Attached.SRC state - */ - pd_timer_enable(port, PE_TIMER_PS_SOURCE, - PD_POWER_SUPPLY_TURN_ON_DELAY); -} - -static void pe_prs_snk_src_source_on_run(int port) -{ - /* Wait until power supply turns on */ - if (!pd_timer_is_disabled(port, PE_TIMER_PS_SOURCE)) { - if (!pd_timer_is_expired(port, PE_TIMER_PS_SOURCE)) - return; - - /* update pe power role */ - pe[port].power_role = pd_get_power_role(port); - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PS_RDY); - /* reset timer so PD_CTRL_PS_RDY isn't sent again */ - pd_timer_disable(port, PE_TIMER_PS_SOURCE); - } - - /* - * Transition to ErrorRecovery state when: - * 1) On protocol error - */ - else if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY); - } - - else if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* Run swap source timer on entry to pe_src_startup */ - PE_SET_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE); - set_state_pe(port, PE_SRC_STARTUP); - } -} - -static void pe_prs_snk_src_source_on_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_PS_SOURCE); - tc_pr_swap_complete(port, - PE_CHK_FLAG(port, PE_FLAGS_PR_SWAP_COMPLETE)); -} - -/** - * PE_PRS_SNK_SRC_Send_Swap - * PE_FRS_SNK_SRC_Send_Swap - * - * NOTE: Shared action code used for Power Role Swap and Fast Role Swap - */ -static void pe_prs_snk_src_send_swap_entry(int port) -{ - print_current_state(port); - - /* - * PRS_SNK_SRC_SEND_SWAP - * Request the Protocol Layer to send a PR_Swap Message. - * - * FRS_SNK_SRC_SEND_SWAP - * Hardware should have turned off sink power and started - * bringing Vbus to vSafe5. - * Request the Protocol Layer to send a FR_Swap Message. - */ - if (IS_ENABLED(CONFIG_USB_PD_REV30)) { - send_ctrl_msg(port, - TCPCI_MSG_SOP, - pe_in_frs_mode(port) - ? PD_CTRL_FR_SWAP - : PD_CTRL_PR_SWAP); - } else { - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PR_SWAP); - } - pe_sender_response_msg_entry(port); -} - -static void pe_prs_snk_src_send_swap_run(int port) -{ - int type; - int cnt; - int ext; - enum pe_msg_check msg_check; - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - /* - * Handle discarded message - */ - if (msg_check & PE_MSG_DISCARDED) { - if (pe_in_frs_mode(port)) - set_state_pe(port, PE_SNK_HARD_RESET); - else - set_state_pe(port, PE_SNK_READY); - return; - } - - /* - * Transition to PE_PRS_SNK_SRC_Transition_to_off when: - * 1) An Accept Message is received. - * - * PRS: Transition to PE_SNK_Ready state when: - * FRS: Transition to ErrorRecovery state when: - * 1) A Reject Message is received. - * 2) Or a Wait Message is received. - */ - if ((msg_check & PE_MSG_SENT) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - ext = PD_HEADER_EXT(rx_emsg[port].header); - - if ((ext == 0) && (cnt == 0)) { - if (type == PD_CTRL_ACCEPT) { - tc_request_power_swap(port); - set_state_pe(port, - PE_PRS_SNK_SRC_TRANSITION_TO_OFF); - } else if ((type == PD_CTRL_REJECT) || - (type == PD_CTRL_WAIT)) { - if (IS_ENABLED(CONFIG_USB_PD_REV30)) - set_state_pe(port, - pe_in_frs_mode(port) - ? PE_WAIT_FOR_ERROR_RECOVERY - : PE_SNK_READY); - else - set_state_pe(port, PE_SNK_READY); - } - return; - } - } - - /* - * PRS: Transition to PE_SNK_Ready state when: - * FRS: Transition to ErrorRecovery state when: - * 1) The SenderResponseTimer times out. - */ - if (pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) { - if (IS_ENABLED(CONFIG_USB_PD_REV30)) - set_state_pe(port, - pe_in_frs_mode(port) - ? PE_WAIT_FOR_ERROR_RECOVERY - : PE_SNK_READY); - else - set_state_pe(port, PE_SNK_READY); - return; - } - /* - * FRS Only: Transition to ErrorRecovery state when: - * 2) The FR_Swap Message is not sent after retries (a GoodCRC Message - * has not been received). A soft reset Shall Not be initiated in - * this case. - */ - if (IS_ENABLED(CONFIG_USB_PD_REV30) && - pe_in_frs_mode(port) && - PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - set_state_pe(port, PE_WAIT_FOR_ERROR_RECOVERY); - } -} - -static void pe_prs_snk_src_send_swap_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -/** - * PE_FRS_SNK_SRC_Start_AMS - */ -__maybe_unused static void pe_frs_snk_src_start_ams_entry(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_REV30)) - assert(0); - - print_current_state(port); - - /* Contract is invalid now */ - pe_invalidate_explicit_contract(port); - - /* Inform Protocol Layer this is start of AMS */ - PE_SET_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS); - - /* Shared PRS/FRS code, indicate FRS path */ - PE_SET_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH); - set_state_pe(port, PE_PRS_SNK_SRC_SEND_SWAP); -} - -/** - * PE_PRS_FRS_SHARED - */ -__maybe_unused static void pe_prs_frs_shared_entry(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_REV30)) - assert(0); - - /* - * Shared PRS/FRS code, assume PRS path - * - * This is the super state entry. It will be called before - * the first entry state to get into the PRS/FRS path. - * For FRS, PE_FRS_SNK_SRC_START_AMS entry will be called - * after this and that will set for the FRS path. - */ - PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH); -} - -__maybe_unused static void pe_prs_frs_shared_exit(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_REV30)) - assert(0); - - /* - * Shared PRS/FRS code, when not in shared path - * indicate PRS path - */ - PE_CLR_FLAG(port, PE_FLAGS_FAST_ROLE_SWAP_PATH); -} - -/** - * PE_BIST_TX - */ -static void pe_bist_tx_entry(int port) -{ - uint32_t *payload = (uint32_t *)rx_emsg[port].buf; - uint8_t mode = BIST_MODE(payload[0]); - int vbus_mv; - int ibus_ma; - - print_current_state(port); - - /* Get the current nominal VBUS value */ - if (pe[port].power_role == PD_ROLE_SOURCE) { - const uint32_t *src_pdo; - uint32_t unused; - - dpm_get_source_pdo(&src_pdo, port); - pd_extract_pdo_power(src_pdo[pe[port].requested_idx - 1], - &ibus_ma, &vbus_mv, &unused); - } else { - vbus_mv = pe[port].supply_voltage; - } - - /* If VBUS is not at vSafe5V, then don't enter BIST test mode */ - if (vbus_mv != PD_V_SAFE5V_NOM) { - pe_set_ready_state(port); - return; - } - - if (mode == BIST_CARRIER_MODE_2) { - /* - * PE_BIST_Carrier_Mode embedded here. - * See PD 3.0 section 6.4.3.1 BIST Carrier Mode 2: With a BIST - * Carrier Mode 2 BIST Data Object, the UUT Shall send out a - * continuous string of BMC-encoded alternating "1"s and “0”s. - * The UUT Shall exit the Continuous BIST Mode within - * tBISTContMode of this Continuous BIST Mode being enabled. - */ - send_ctrl_msg(port, TCPCI_MSG_TX_BIST_MODE_2, 0); - pd_timer_enable(port, PE_TIMER_BIST_CONT_MODE, - PD_T_BIST_CONT_MODE); - } else if (mode == BIST_TEST_DATA) { - /* - * See PD 3.0 section 6.4.3.2 BIST Test Data: - * With a BIST Test Data BIST Data Object, the UUT Shall return - * a GoodCRC Message and Shall enter a test mode in which it - * sends no further Messages except for GoodCRC Messages in - * response to received Messages.... The test Shall be ended by - * sending Hard Reset Signaling to reset the UUT. - */ - if (tcpc_set_bist_test_mode(port, true) != EC_SUCCESS) - CPRINTS("C%d: Failed to enter BIST Test Mode", port); - } else { - /* Ignore unsupported BIST messages. */ - pe_set_ready_state(port); - return; - } -} - -static void pe_bist_tx_run(int port) -{ - if (pd_timer_is_expired(port, PE_TIMER_BIST_CONT_MODE)) { - /* - * Entry point to disable BIST in TCPC if that's not already - * handled automatically by the TCPC. Unless this method is - * implemented in a TCPM driver, this function does nothing. - */ - tcpm_reset_bist_type_2(port); - - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_TRANSITION_TO_DEFAULT); - else - set_state_pe(port, PE_SNK_TRANSITION_TO_DEFAULT); - } else { - /* - * We are in test data mode and no further Messages except for - * GoodCRC Messages in response to received Messages will - * be sent. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - } -} - -static void pe_bist_tx_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_BIST_CONT_MODE); -} - -/** - * Give_Sink_Cap Message - */ -static void pe_snk_give_sink_cap_entry(int port) -{ - print_current_state(port); - - /* Send a Sink_Capabilities Message */ - tx_emsg[port].len = pd_snk_pdo_cnt * 4; - memcpy(tx_emsg[port].buf, (uint8_t *)pd_snk_pdo, tx_emsg[port].len); - send_data_msg(port, TCPCI_MSG_SOP, PD_DATA_SINK_CAP); -} - -static void pe_snk_give_sink_cap_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - pe_set_ready_state(port); - return; - } - - if (pe_check_outgoing_discard(port)) - return; -} - -/** - * Wait For Error Recovery - */ -static void pe_wait_for_error_recovery_entry(int port) -{ - print_current_state(port); - tc_start_error_recovery(port); -} - -static void pe_wait_for_error_recovery_run(int port) -{ - /* Stay here until error recovery is complete */ -} - -/** - * PE_Handle_Custom_Vdm_Request - */ -static void pe_handle_custom_vdm_request_entry(int port) -{ - /* Get the message */ - uint32_t *payload = (uint32_t *)rx_emsg[port].buf; - int cnt = PD_HEADER_CNT(rx_emsg[port].header); - int sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - int rlen = 0; - uint32_t *rdata; - - print_current_state(port); - - /* This is an Interruptible AMS */ - PE_SET_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS); - - rlen = pd_custom_vdm(port, cnt, payload, &rdata); - if (rlen > 0) { - tx_emsg[port].len = rlen * 4; - memcpy(tx_emsg[port].buf, (uint8_t *)rdata, tx_emsg[port].len); - send_data_msg(port, sop, PD_DATA_VENDOR_DEF); - } else { - if (prl_get_rev(port, TCPCI_MSG_SOP) > PD_REV20) { - set_state_pe(port, PE_SEND_NOT_SUPPORTED); - } else { - PE_CLR_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS); - pe_set_ready_state(port); - } - } -} - -static void pe_handle_custom_vdm_request_run(int port) -{ - /* Wait for ACCEPT, WAIT or Reject message to send. */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* - * Message sent. Transition back to - * PE_SRC_Ready or PE_SINK_Ready - */ - pe_set_ready_state(port); - } -} - -static void pe_handle_custom_vdm_request_exit(int port) -{ - PE_CLR_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS); -} - -static enum vdm_response_result parse_vdm_response_common(int port) -{ - /* Retrieve the message information */ - uint32_t *payload; - int sop; - uint8_t type; - uint8_t cnt; - uint8_t ext; - - if (!PE_CHK_REPLY(port)) - return VDM_RESULT_WAITING; - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - payload = (uint32_t *)rx_emsg[port].buf; - sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - ext = PD_HEADER_EXT(rx_emsg[port].header); - - if (sop == pe[port].tx_type && type == PD_DATA_VENDOR_DEF && cnt >= 1 - && ext == 0) { - if (PD_VDO_CMDT(payload[0]) == CMDT_RSP_ACK && - cnt >= pe[port].vdm_ack_min_data_objects) { - /* Handle ACKs in state-specific code. */ - return VDM_RESULT_ACK; - } else if (PD_VDO_CMDT(payload[0]) == CMDT_RSP_NAK) { - /* Handle NAKs in state-specific code. */ - return VDM_RESULT_NAK; - } else if (PD_VDO_CMDT(payload[0]) == CMDT_RSP_BUSY) { - /* - * Don't fill in the discovery field so we re-probe in - * tVDMBusy - */ - CPRINTS("C%d: Partner BUSY, request will be retried", - port); - pd_timer_enable(port, PE_TIMER_DISCOVER_IDENTITY, - PD_T_VDM_BUSY); - - return VDM_RESULT_NO_ACTION; - } else if (PD_VDO_CMDT(payload[0]) == CMDT_INIT) { - /* - * Unexpected VDM REQ received. Let Src.Ready or - * Snk.Ready handle it. - */ - PE_SET_FLAG(port, PE_FLAGS_MSG_RECEIVED); - return VDM_RESULT_NO_ACTION; - } - - /* - * Partner gave us an incorrect size or command; mark discovery - * as failed. - */ - CPRINTS("C%d: Unexpected VDM response: 0x%04x 0x%04x", - port, rx_emsg[port].header, payload[0]); - return VDM_RESULT_NAK; - } else if (sop == pe[port].tx_type && ext == 0 && cnt == 0 && - type == PD_CTRL_NOT_SUPPORTED) { - /* - * A NAK would be more expected here, but Not Supported is still - * allowed with the same meaning. - */ - return VDM_RESULT_NAK; - } - - /* Unexpected Message Received. Src.Ready or Snk.Ready can handle it. */ - PE_SET_FLAG(port, PE_FLAGS_MSG_RECEIVED); - return VDM_RESULT_NO_ACTION; -} - -/** - * PE_VDM_SEND_REQUEST - * Shared parent to manage VDM timer and other shared parts of the VDM request - * process - */ -static void pe_vdm_send_request_entry(int port) -{ - if (pe[port].tx_type == TCPCI_MSG_INVALID) { - if (IS_ENABLED(USB_PD_DEBUG_LABELS)) - CPRINTS("C%d: %s: Tx type expected to be set, " - "returning", - port, pe_state_names[get_state_pe(port)]); - set_state_pe(port, get_last_state_pe(port)); - return; - } - - if ((pe[port].tx_type == TCPCI_MSG_SOP_PRIME || - pe[port].tx_type == TCPCI_MSG_SOP_PRIME_PRIME) && - !tc_is_vconn_src(port) && port_discovery_vconn_swap_policy(port, - PE_FLAGS_VCONN_SWAP_TO_ON)) { - if (port_try_vconn_swap(port)) - return; - } - - /* All VDM sequences are Interruptible */ - PE_SET_FLAG(port, PE_FLAGS_LOCALLY_INITIATED_AMS | - PE_FLAGS_INTERRUPTIBLE_AMS); -} - -static void pe_vdm_send_request_run(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE) && - pd_timer_is_disabled(port, PE_TIMER_VDM_RESPONSE)) { - /* Message was sent */ - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - /* Start no response timer */ - /* TODO(b/155890173): Support DPM-supplied timeout */ - pd_timer_enable(port, PE_TIMER_VDM_RESPONSE, - PD_T_VDM_SNDR_RSP); - } - - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_DISCARDED)) { - /* - * Go back to ready on first AMS message discard - * (ready states will clear the discard flag) - */ - pe_set_ready_state(port); - return; - } - - /* - * Check the VDM timer, child will be responsible for processing - * messages and reacting appropriately to unexpected messages. - */ - if (pd_timer_is_expired(port, PE_TIMER_VDM_RESPONSE)) { - CPRINTF("VDM %s Response Timeout\n", - pe[port].tx_type == TCPCI_MSG_SOP ? - "Port" : "Cable"); - /* - * Flag timeout so child state can mark appropriate discovery - * item as failed. - */ - PE_SET_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT); - - set_state_pe(port, get_last_state_pe(port)); - } -} - -static void pe_vdm_send_request_exit(int port) -{ - /* - * Clear TX complete in case child called set_state_pe() before parent - * could process transmission - */ - PE_CLR_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS); - - /* Invalidate TX type so it must be set before next call */ - pe[port].tx_type = TCPCI_MSG_INVALID; - - pd_timer_disable(port, PE_TIMER_VDM_RESPONSE); -} - -/** - * PE_VDM_IDENTITY_REQUEST_CBL - * Combination of PE_INIT_PORT_VDM_Identity_Request State specific to the - * cable and PE_SRC_VDM_Identity_Request State. - * pe[port].tx_type must be set (to SOP') prior to entry. - */ -static void pe_vdm_identity_request_cbl_entry(int port) -{ - uint32_t *msg = (uint32_t *)tx_emsg[port].buf; - - print_current_state(port); - - if (!pe_can_send_sop_prime(port)) { - /* - * The parent state already tried to enable SOP' traffic. If it - * is still disabled, there's nothing left to try. - */ - pd_set_identity_discovery(port, pe[port].tx_type, PD_DISC_FAIL); - set_state_pe(port, get_last_state_pe(port)); - return; - } - - msg[0] = VDO(USB_SID_PD, 1, - VDO_SVDM_VERS(pd_get_vdo_ver(port, pe[port].tx_type)) | - CMD_DISCOVER_IDENT); - tx_emsg[port].len = sizeof(uint32_t); - - send_data_msg(port, pe[port].tx_type, PD_DATA_VENDOR_DEF); - - pe[port].discover_identity_counter++; - - /* - * Valid DiscoverIdentity responses should have at least 4 objects - * (header, ID header, Cert Stat, Product VDO). - */ - pe[port].vdm_ack_min_data_objects = 4; -} - -static void pe_vdm_identity_request_cbl_run(int port) -{ - /* Retrieve the message information */ - uint32_t *payload = (uint32_t *) rx_emsg[port].buf; - int sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - uint8_t type = PD_HEADER_TYPE(rx_emsg[port].header); - uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header); - uint8_t ext = PD_HEADER_EXT(rx_emsg[port].header); - - switch (parse_vdm_response_common(port)) { - case VDM_RESULT_WAITING: - /* - * The common code didn't parse a message. Handle protocol - * errors; otherwise, continue waiting. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - /* - * No Good CRC: See section 6.4.4.3.1 - Discover - * Identity. - * - * Discover Identity Command request sent to SOP' Shall - * Not cause a Soft Reset if a GoodCRC Message response - * is not returned since this can indicate a non-PD - * Capable cable. - */ - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - set_state_pe(port, get_last_state_pe(port)); - } - return; - case VDM_RESULT_NO_ACTION: - /* - * If the received message doesn't change the discovery state, - * there is nothing to do but return to the previous ready - * state. - */ - if (get_last_state_pe(port) == PE_SRC_DISCOVERY && - (sop != pe[port].tx_type || - type != PD_DATA_VENDOR_DEF || - cnt == 0 || ext != 0)) { - /* - * Unexpected non-VDM received: Before an explicit - * contract, an unexpected message shall generate a soft - * reset using the SOP* of the incoming message. - */ - pe_send_soft_reset(port, sop); - return; - } - break; - case VDM_RESULT_ACK: - /* PE_INIT_PORT_VDM_Identity_ACKed embedded here */ - dfp_consume_identity(port, sop, cnt, payload); - - /* - * Note: If port partner runs PD 2.0, we must use PD 2.0 to - * communicate with the cable plug when in an explicit contract. - * - * PD Spec Table 6-2: Revision Interoperability during an - * Explicit Contract - */ - if (prl_get_rev(port, TCPCI_MSG_SOP) != PD_REV20) - prl_set_rev(port, sop, - PD_HEADER_REV(rx_emsg[port].header)); - break; - case VDM_RESULT_NAK: - /* PE_INIT_PORT_VDM_IDENTITY_NAKed embedded here */ - pd_set_identity_discovery(port, pe[port].tx_type, PD_DISC_FAIL); - break; - } - - /* Return to calling state (PE_{SRC,SNK}_Ready or PE_SRC_Discovery) */ - set_state_pe(port, get_last_state_pe(port)); -} - -static void pe_vdm_identity_request_cbl_exit(int port) -{ - /* - * When cable GoodCRCs but does not reply, down-rev to PD 2.0 and try - * again. - * - * PD 3.0 Rev 2.0 6.2.1.1.5 Specification Revision - * - * "When a Cable Plug does not respond to a Revision 3.0 Discover - * Identity REQ with a Discover Identity ACK or BUSY the Vconn Source - * May repeat steps 1-4 using a Revision 2.0 Discover Identity REQ in - * step 1 before establishing that there is no Cable Plug to - * communicate with" - */ - if (PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT)) { - PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT); - prl_set_rev(port, TCPCI_MSG_SOP_PRIME, PD_REV20); - } - - /* - * 6.6.15 DiscoverIdentityTimer - * - * No more than nDiscoverIdentityCount Discover Identity Messages - * without a GoodCRC Message response Shall be sent. If no GoodCRC - * Message response is received after nDiscoverIdentityCount Discover - * Identity Command requests have been sent by a Port, the Port Shall - * Not send any further SOP’/SOP’’ Messages. - */ - if (pe[port].discover_identity_counter >= N_DISCOVER_IDENTITY_COUNT) - pd_set_identity_discovery(port, pe[port].tx_type, - PD_DISC_FAIL); - else if (pe[port].discover_identity_counter == - N_DISCOVER_IDENTITY_PD3_0_LIMIT) - /* - * Downgrade to PD 2.0 if the partner hasn't replied before - * all retries are exhausted in case the cable is - * non-compliant about GoodCRC-ing higher revisions - */ - prl_set_rev(port, TCPCI_MSG_SOP_PRIME, PD_REV20); - - /* - * Set discover identity timer unless BUSY case already did so. - */ - if (pd_get_identity_discovery(port, pe[port].tx_type) == PD_DISC_NEEDED - && pd_timer_is_expired(port, PE_TIMER_DISCOVER_IDENTITY)) { - /* - * The tDiscoverIdentity timer is used during an explicit - * contract when discovering whether a cable is PD capable. - * - * Pre-contract, slow the rate Discover Identity commands are - * sent. This permits operation with captive cable devices that - * power the SOP' responder from VBUS instead of VCONN. - */ - pd_timer_enable(port, PE_TIMER_DISCOVER_IDENTITY, - pe_is_explicit_contract(port) - ? PD_T_DISCOVER_IDENTITY - : PE_T_DISCOVER_IDENTITY_NO_CONTRACT); - } - - /* Do not attempt further discovery if identity discovery failed. */ - if (pd_get_identity_discovery(port, pe[port].tx_type) == PD_DISC_FAIL) { - pd_set_svids_discovery(port, pe[port].tx_type, PD_DISC_FAIL); - pd_notify_event(port, pe[port].tx_type == TCPCI_MSG_SOP ? - PD_STATUS_EVENT_SOP_DISC_DONE : - PD_STATUS_EVENT_SOP_PRIME_DISC_DONE); - } -} - -/** - * PE_INIT_PORT_VDM_Identity_Request - * - * Specific to SOP requests, as cables require additions for the discover - * identity counter, must tolerate not receiving a GoodCRC, and need to set the - * cable revision based on response. - * pe[port].tx_type must be set (to SOP) prior to entry. - */ -static void pe_init_port_vdm_identity_request_entry(int port) -{ - uint32_t *msg = (uint32_t *)tx_emsg[port].buf; - - print_current_state(port); - - msg[0] = VDO(USB_SID_PD, 1, - VDO_SVDM_VERS(pd_get_vdo_ver(port, pe[port].tx_type)) | - CMD_DISCOVER_IDENT); - tx_emsg[port].len = sizeof(uint32_t); - - send_data_msg(port, pe[port].tx_type, PD_DATA_VENDOR_DEF); - - /* - * Valid DiscoverIdentity responses should have at least 4 objects - * (header, ID header, Cert Stat, Product VDO). - */ - pe[port].vdm_ack_min_data_objects = 4; -} - -static void pe_init_port_vdm_identity_request_run(int port) -{ - switch (parse_vdm_response_common(port)) { - case VDM_RESULT_WAITING: - /* If common code didn't parse a message, continue waiting. */ - return; - case VDM_RESULT_NO_ACTION: - /* - * If the received message doesn't change the discovery state, - * there is nothing to do but return to the previous ready - * state. - */ - break; - case VDM_RESULT_ACK: { - /* Retrieve the message information. */ - uint32_t *payload = (uint32_t *) rx_emsg[port].buf; - int sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header); - - /* PE_INIT_PORT_VDM_Identity_ACKed embedded here */ - dfp_consume_identity(port, sop, cnt, payload); - - break; - } - case VDM_RESULT_NAK: - /* PE_INIT_PORT_VDM_IDENTITY_NAKed embedded here */ - pd_set_identity_discovery(port, pe[port].tx_type, PD_DISC_FAIL); - break; - } - - /* Return to calling state (PE_{SRC,SNK}_Ready) */ - set_state_pe(port, get_last_state_pe(port)); -} - -static void pe_init_port_vdm_identity_request_exit(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT)) { - PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT); - /* - * Mark failure to respond as discovery failure. - * - * For PD 2.0 partners (6.10.3 Applicability of Structured VDM - * Commands Note 3): - * - * If Structured VDMs are not supported, a Structured VDM - * Command received by a DFP or UFP Shall be Ignored. - */ - pd_set_identity_discovery(port, pe[port].tx_type, PD_DISC_FAIL); - } - - /* Do not attempt further discovery if identity discovery failed. */ - if (pd_get_identity_discovery(port, pe[port].tx_type) == PD_DISC_FAIL) { - pd_set_svids_discovery(port, pe[port].tx_type, PD_DISC_FAIL); - pd_notify_event(port, pe[port].tx_type == TCPCI_MSG_SOP ? - PD_STATUS_EVENT_SOP_DISC_DONE : - PD_STATUS_EVENT_SOP_PRIME_DISC_DONE); - } -} - -/** - * PE_INIT_VDM_SVIDs_Request - * - * Used for SOP and SOP' requests, selected by pe[port].tx_type prior to entry. - */ -static void pe_init_vdm_svids_request_entry(int port) -{ - uint32_t *msg = (uint32_t *)tx_emsg[port].buf; - - print_current_state(port); - - if (pe[port].tx_type == TCPCI_MSG_SOP_PRIME && - !pe_can_send_sop_prime(port)) { - /* - * The parent state already tried to enable SOP' traffic. If it - * is still disabled, there's nothing left to try. - */ - pd_set_svids_discovery(port, pe[port].tx_type, PD_DISC_FAIL); - set_state_pe(port, get_last_state_pe(port)); - return; - } - - msg[0] = VDO(USB_SID_PD, 1, - VDO_SVDM_VERS(pd_get_vdo_ver(port, pe[port].tx_type)) | - CMD_DISCOVER_SVID); - tx_emsg[port].len = sizeof(uint32_t); - - send_data_msg(port, pe[port].tx_type, PD_DATA_VENDOR_DEF); - - /* - * Valid Discover SVIDs ACKs should have at least 2 objects (VDM header - * and at least 1 SVID VDO). - */ - pe[port].vdm_ack_min_data_objects = 2; -} - -static void pe_init_vdm_svids_request_run(int port) -{ - switch (parse_vdm_response_common(port)) { - case VDM_RESULT_WAITING: - /* If common code didn't parse a message, continue waiting. */ - return; - case VDM_RESULT_NO_ACTION: - /* - * If the received message doesn't change the discovery state, - * there is nothing to do but return to the previous ready - * state. - */ - break; - case VDM_RESULT_ACK: { - /* Retrieve the message information. */ - uint32_t *payload = (uint32_t *) rx_emsg[port].buf; - int sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header); - - /* PE_INIT_VDM_SVIDs_ACKed embedded here */ - dfp_consume_svids(port, sop, cnt, payload); - break; - } - case VDM_RESULT_NAK: - /* PE_INIT_VDM_SVIDs_NAKed embedded here */ - pd_set_svids_discovery(port, pe[port].tx_type, PD_DISC_FAIL); - break; - } - - /* Return to calling state (PE_{SRC,SNK}_Ready) */ - set_state_pe(port, get_last_state_pe(port)); -} - -static void pe_init_vdm_svids_request_exit(int port) -{ - if (PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT)) { - PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_TIMEOUT); - /* - * Mark failure to respond as discovery failure. - * - * For PD 2.0 partners (6.10.3 Applicability of Structured VDM - * Commands Note 3): - * - * If Structured VDMs are not supported, a Structured VDM - * Command received by a DFP or UFP Shall be Ignored. - */ - pd_set_svids_discovery(port, pe[port].tx_type, PD_DISC_FAIL); - } - - /* If SVID discovery failed, discovery is done at this point */ - if (pd_get_svids_discovery(port, pe[port].tx_type) == PD_DISC_FAIL) - pd_notify_event(port, pe[port].tx_type == TCPCI_MSG_SOP ? - PD_STATUS_EVENT_SOP_DISC_DONE : - PD_STATUS_EVENT_SOP_PRIME_DISC_DONE); -} - -/** - * PE_INIT_VDM_Modes_Request - * - * Used for SOP and SOP' requests, selected by pe[port].tx_type prior to entry. - */ -static void pe_init_vdm_modes_request_entry(int port) -{ - uint32_t *msg = (uint32_t *)tx_emsg[port].buf; - const struct svid_mode_data *mode_data = - pd_get_next_mode(port, pe[port].tx_type); - uint16_t svid; - /* - * The caller should have checked that there was something to discover - * before entering this state. - */ - assert(mode_data); - assert(mode_data->discovery == PD_DISC_NEEDED); - svid = mode_data->svid; - - print_current_state(port); - - if (pe[port].tx_type == TCPCI_MSG_SOP_PRIME && - !pe_can_send_sop_prime(port)) { - /* - * The parent state already tried to enable SOP' traffic. If it - * is still disabled, there's nothing left to try. - */ - pd_set_modes_discovery(port, pe[port].tx_type, svid, - PD_DISC_FAIL); - set_state_pe(port, get_last_state_pe(port)); - return; - } - - msg[0] = VDO((uint16_t) svid, 1, - VDO_SVDM_VERS(pd_get_vdo_ver(port, pe[port].tx_type)) | - CMD_DISCOVER_MODES); - tx_emsg[port].len = sizeof(uint32_t); - - send_data_msg(port, pe[port].tx_type, PD_DATA_VENDOR_DEF); - - /* - * Valid Discover Modes responses should have at least 2 objects (VDM - * header and at least 1 mode VDO). - */ - pe[port].vdm_ack_min_data_objects = 2; -} - -static void pe_init_vdm_modes_request_run(int port) -{ - const struct svid_mode_data *mode_data; - uint16_t requested_svid; - - mode_data = pd_get_next_mode(port, pe[port].tx_type); - - assert(mode_data); - assert(mode_data->discovery == PD_DISC_NEEDED); - requested_svid = mode_data->svid; - - switch (parse_vdm_response_common(port)) { - case VDM_RESULT_WAITING: - /* If common code didn't parse a message, continue waiting. */ - return; - case VDM_RESULT_NO_ACTION: - /* - * If the received message doesn't change the discovery state, - * there is nothing to do but return to the previous ready - * state. - */ - break; - case VDM_RESULT_ACK: { - /* Retrieve the message information. */ - uint32_t *payload = (uint32_t *) rx_emsg[port].buf; - int sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header); - uint16_t response_svid = (uint16_t) PD_VDO_VID(payload[0]); - - /* - * Accept ACK if the request and response SVIDs are equal; - * otherwise, treat this as a NAK of the request SVID. - * - * TODO(b:169242812): support valid mode checking in - * dfp_consume_modes. - */ - if (requested_svid == response_svid) { - /* PE_INIT_VDM_Modes_ACKed embedded here */ - dfp_consume_modes(port, sop, cnt, payload); - break; - } - } - /* Fall Through */ - case VDM_RESULT_NAK: - /* PE_INIT_VDM_Modes_NAKed embedded here */ - pd_set_modes_discovery(port, pe[port].tx_type, requested_svid, - PD_DISC_FAIL); - break; - } - - /* Return to calling state (PE_{SRC,SNK}_Ready) */ - set_state_pe(port, get_last_state_pe(port)); -} - -static void pe_init_vdm_modes_request_exit(int port) -{ - if (pd_get_modes_discovery(port, pe[port].tx_type) != PD_DISC_NEEDED) - /* Mode discovery done, notify the AP */ - pd_notify_event(port, pe[port].tx_type == TCPCI_MSG_SOP ? - PD_STATUS_EVENT_SOP_DISC_DONE : - PD_STATUS_EVENT_SOP_PRIME_DISC_DONE); - -} - -/** - * PE_VDM_REQUEST_DPM - * - * Makes a VDM request with contents and SOP* type previously set up by the DPM. - */ - -static void pe_vdm_request_dpm_entry(int port) -{ - print_current_state(port); - - if ((pe[port].tx_type == TCPCI_MSG_SOP_PRIME || - pe[port].tx_type == TCPCI_MSG_SOP_PRIME_PRIME) && - !pe_can_send_sop_prime(port)) { - /* - * The parent state already tried to enable SOP' traffic. If it - * is still disabled, there's nothing left to try. - */ - dpm_vdm_naked(port, pe[port].tx_type, - PD_VDO_VID(pe[port].vdm_data[0]), - PD_VDO_CMD(pe[port].vdm_data[0])); - set_state_pe(port, get_last_state_pe(port)); - return; - } - - /* Copy Vendor Data Objects (VDOs) into message buffer */ - if (pe[port].vdm_cnt > 0) { - /* Copy data after header */ - memcpy(&tx_emsg[port].buf, - (uint8_t *)pe[port].vdm_data, - pe[port].vdm_cnt * 4); - /* Update len with the number of VDO bytes */ - tx_emsg[port].len = pe[port].vdm_cnt * 4; - } - - /* - * Clear the VDM nak'ed flag so that each request is - * treated separately (NAKs are handled by the - * DPM layer). Otherwise previous NAKs received will - * cause the state to exit early. - */ - PE_CLR_FLAG(port, PE_FLAGS_VDM_REQUEST_NAKED); - send_data_msg(port, pe[port].tx_type, PD_DATA_VENDOR_DEF); - - /* - * In general, valid VDM ACKs must have a VDM header. Other than that, - * ACKs must be validated based on the command and SVID. - */ - pe[port].vdm_ack_min_data_objects = 1; -} - -static void pe_vdm_request_dpm_run(int port) -{ - uint32_t vdm_hdr; - - switch (parse_vdm_response_common(port)) { - case VDM_RESULT_WAITING: - /* - * USB-PD 3.0 Rev 1.1 - 6.4.4.2.5 - * Structured VDM command consists of a command request and a - * command response (ACK, NAK, or BUSY). An exception is made - * for the Attention command which shall have no response. - * - * Since Attention commands do not have an expected reply, - * the SVDM command is complete once the Attention command - * transmit is complete. - */ - vdm_hdr = pe[port].vdm_data[0]; - if(PD_VDO_SVDM(vdm_hdr) && - (PD_VDO_CMD(vdm_hdr) == CMD_ATTENTION)) { - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - break; - } - } - /* - * If common code didn't parse a message, and the VDM - * just sent was not an Attention message, then continue - * waiting. - */ - return; - case VDM_RESULT_NO_ACTION: - /* - * If the received message doesn't change the discovery state, - * there is nothing to do but return to the previous ready - * state. This includes Attention commands which have no - * expected SVDM response. - */ - break; - case VDM_RESULT_ACK: { - /* Retrieve the message information. */ - uint32_t *payload = (uint32_t *) rx_emsg[port].buf; - int sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - uint8_t cnt = PD_HEADER_CNT(rx_emsg[port].header); - uint16_t svid = PD_VDO_VID(payload[0]); - uint8_t vdm_cmd = PD_VDO_CMD(payload[0]); - - /* - * PE initiator VDM-ACKed state for requested VDM, like - * PE_INIT_VDM_FOO_ACKed, embedded here. - */ - dpm_vdm_acked(port, sop, cnt, payload); - - if (sop == TCPCI_MSG_SOP && svid == USB_SID_DISPLAYPORT && - vdm_cmd == CMD_DP_CONFIG) { - PE_SET_FLAG(port, PE_FLAGS_VDM_SETUP_DONE); - } - break; - } - case VDM_RESULT_NAK: - /* - * PE initiator VDM-NAKed state for requested VDM, like - * PE_INIT_VDM_FOO_NAKed, embedded here. - */ - PE_SET_FLAG(port, PE_FLAGS_VDM_SETUP_DONE); - - /* - * Because Not Supported messages or response timeouts are - * treated as NAKs, there may not be a NAK message to parse. - * Extract the needed information from the sent VDM. - */ - dpm_vdm_naked(port, pe[port].tx_type, - PD_VDO_VID(pe[port].vdm_data[0]), - PD_VDO_CMD(pe[port].vdm_data[0])); - break; - } - - /* Return to calling state (PE_{SRC,SNK}_Ready) */ - set_state_pe(port, get_last_state_pe(port)); -} - -static void pe_vdm_request_dpm_exit(int port) -{ - /* - * Force Tx type to be reset before reentering a VDM state, unless the - * current VDM request will be resumed. - */ - if (!PE_CHK_FLAG(port, PE_FLAGS_VDM_REQUEST_CONTINUE)) - pe[port].tx_type = TCPCI_MSG_INVALID; -} - -/** - * PE_VDM_Response - */ -static void pe_vdm_response_entry(int port) -{ - int vdo_len = 0; - uint32_t *rx_payload; - uint32_t *tx_payload; - uint8_t vdo_cmd; - svdm_rsp_func func = NULL; - - print_current_state(port); - - /* This is an Interruptible AMS */ - PE_SET_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS); - - /* Get the message */ - rx_payload = (uint32_t *)rx_emsg[port].buf; - - /* Extract VDM command from the VDM header */ - vdo_cmd = PD_VDO_CMD(rx_payload[0]); - /* This must be a command request to proceed further */ - if (PD_VDO_CMDT(rx_payload[0]) != CMDT_INIT) { - CPRINTF("ERR:CMDT:%d:%d\n", PD_VDO_CMDT(rx_payload[0]), - vdo_cmd); - - pe_set_ready_state(port); - return; - } - - tx_payload = (uint32_t *)tx_emsg[port].buf; - /* - * Designed in TCPMv1, svdm_response functions use same - * buffer to take received data and overwrite with response - * data. To work with this interface, here copy rx data to - * tx buffer and pass tx_payload to func. - * TODO(b/166455363): change the interface to pass both rx - * and tx buffer. - * - * The SVDM header is dependent on both VDM command request being - * replied to and the result of response function. The SVDM command - * message is copied into tx_payload. tx_payload[0] is the VDM header - * for the response message. The SVDM response function takes the role - * of the DPM layer and will indicate the response type (ACK/NAK/BUSY) - * by its return value (vdo_len) - * vdo_len > 0 --> ACK - * vdo_len == 0 --> NAK - * vdo_len < 0 --> BUSY - */ - memcpy(tx_payload, rx_payload, PD_HEADER_CNT(rx_emsg[port].header) * 4); - /* - * Clear fields in SVDM response message that will be set based on the - * result of the svdm response function. - */ - tx_payload[0] &= ~VDO_CMDT_MASK; - tx_payload[0] &= ~VDO_SVDM_VERS(0x3); - - /* Add SVDM structured version being used */ - tx_payload[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP)); - - /* Use VDM command to select the response handler function */ - switch (vdo_cmd) { - case CMD_DISCOVER_IDENT: - func = svdm_rsp.identity; - break; - case CMD_DISCOVER_SVID: - func = svdm_rsp.svids; - break; - case CMD_DISCOVER_MODES: - func = svdm_rsp.modes; - break; - case CMD_ENTER_MODE: - func = svdm_rsp.enter_mode; - break; - case CMD_DP_STATUS: - if (svdm_rsp.amode) - func = svdm_rsp.amode->status; - break; - case CMD_DP_CONFIG: - if (svdm_rsp.amode) - func = svdm_rsp.amode->config; - break; - case CMD_EXIT_MODE: - func = svdm_rsp.exit_mode; - break; -#ifdef CONFIG_USB_PD_ALT_MODE_DFP - case CMD_ATTENTION: - /* - * attention is only SVDM with no response - * (just goodCRC) return zero here. - */ - dfp_consume_attention(port, rx_payload); - pe_set_ready_state(port); - return; -#endif - default: - CPRINTF("VDO ERR:CMD:%d\n", vdo_cmd); - } - - /* - * If the port partner is PD_REV20 and our data role is DFP, we must - * reply to any SVDM command with a NAK. If the SVDM was an Attention - * command, it does not have a response, and exits the function above. - */ - if (func && (prl_get_rev(port, TCPCI_MSG_SOP) != PD_REV20 || - pe[port].data_role == PD_ROLE_UFP)) { - /* - * Execute SVDM response function selected above and set the - * correct response type in the VDM header. - */ - vdo_len = func(port, tx_payload); - if (vdo_len > 0) { - tx_payload[0] |= VDO_CMDT(CMDT_RSP_ACK); - /* - * If command response is an ACK and if the command was - * either enter/exit mode, then update the PE modal flag - * accordingly. - */ - if (vdo_cmd == CMD_ENTER_MODE) - PE_SET_FLAG(port, PE_FLAGS_MODAL_OPERATION); - if (vdo_cmd == CMD_EXIT_MODE) - PE_CLR_FLAG(port, PE_FLAGS_MODAL_OPERATION); - } else if (!vdo_len) { - tx_payload[0] |= VDO_CMDT(CMDT_RSP_NAK); - vdo_len = 1; - } else { - tx_payload[0] |= VDO_CMDT(CMDT_RSP_BUSY); - vdo_len = 1; - } - } else { - /* - * Received at VDM command which is not supported. PD 2.0 may - * NAK or ignore the message (see TD.PD.VNDI.E1. VDM Identity - * steps), but PD 3.0 must send Not_Supported (PD 3.0 Ver 2.0 + - * ECNs 2020-12-10 Table 6-64 Response to an incoming - * VDM or TD.PD.VNDI3.E3 VDM Identity steps) - */ - if (prl_get_rev(port, TCPCI_MSG_SOP) == PD_REV30) { - set_state_pe(port, PE_SEND_NOT_SUPPORTED); - return; - } - tx_payload[0] |= VDO_CMDT(CMDT_RSP_NAK); - vdo_len = 1; - } - - /* Send response message. Note len is in bytes, not VDO objects */ - tx_emsg[port].len = (vdo_len * sizeof(uint32_t)); - send_data_msg(port, TCPCI_MSG_SOP, PD_DATA_VENDOR_DEF); -} - -static void pe_vdm_response_run(int port) -{ - /* - * This state waits for a VDM response message to be sent. Return to the - * ready state once the message has been sent, a protocol error was - * detected, or if the VDM response msg was discarded based on being - * interrupted by another rx message. Since VDM sequences are AMS - * interruptible, there is no need to soft reset regardless of exit - * reason. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE) || - PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR) || - PE_CHK_FLAG(port, PE_FLAGS_MSG_DISCARDED)) { - - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE | - PE_FLAGS_PROTOCOL_ERROR | - PE_FLAGS_MSG_DISCARDED); - - pe_set_ready_state(port); - } -} - -static void pe_vdm_response_exit(int port) -{ - PE_CLR_FLAG(port, PE_FLAGS_INTERRUPTIBLE_AMS); -} - -/** - * PE_DEU_SEND_ENTER_USB - */ -static void pe_enter_usb_entry(int port) -{ - uint32_t usb4_payload; - - print_current_state(port); - - if (!IS_ENABLED(CONFIG_USB_PD_USB4)) { - pe_set_ready_state(port); - return; - } - - /* Port is already in USB4 mode, do not send enter USB message again */ - if (enter_usb_entry_is_done(port)) { - pe_set_ready_state(port); - return; - } - - if ((pe[port].tx_type == TCPCI_MSG_SOP_PRIME || - pe[port].tx_type == TCPCI_MSG_SOP_PRIME_PRIME) && - !tc_is_vconn_src(port)) { - if (port_try_vconn_swap(port)) - return; - } - - pe[port].tx_type = TCPCI_MSG_SOP; - usb4_payload = enter_usb_setup_next_msg(port, &pe[port].tx_type); - - if (!usb4_payload) { - enter_usb_failed(port); - pe_set_ready_state(port); - return; - } - - tx_emsg[port].len = sizeof(usb4_payload); - - memcpy(tx_emsg[port].buf, &usb4_payload, tx_emsg[port].len); - send_data_msg(port, pe[port].tx_type, PD_DATA_ENTER_USB); - pe_sender_response_msg_entry(port); -} - -static void pe_enter_usb_run(int port) -{ - enum pe_msg_check msg_check; - - if (!IS_ENABLED(CONFIG_USB_PD_USB4)) { - pe_set_ready_state(port); - return; - } - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - /* - * Handle Discarded message, return to PE_SNK/SRC_READY - */ - if (msg_check & PE_MSG_DISCARDED) { - pe_set_ready_state(port); - return; - } else if (msg_check == PE_MSG_SEND_PENDING) { - /* Wait until message is sent */ - return; - } - - if (pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) { - pe_set_ready_state(port); - enter_usb_failed(port); - return; - } - - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - int cnt = PD_HEADER_CNT(rx_emsg[port].header); - int type = PD_HEADER_TYPE(rx_emsg[port].header); - int sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - /* Only look at control messages */ - if (cnt == 0) { - /* Accept message received */ - if (type == PD_CTRL_ACCEPT) { - enter_usb_accepted(port, sop); - } else if (type == PD_CTRL_REJECT) { - enter_usb_rejected(port, sop); - } else { - /* - * Unexpected control message received. - * Send Soft Reset. - */ - pe_send_soft_reset(port, sop); - return; - } - } else { - /* Unexpected data message received. Send Soft reset */ - pe_send_soft_reset(port, sop); - return; - } - pe_set_ready_state(port); - } -} - -static void pe_enter_usb_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -#ifdef CONFIG_USBC_VCONN -/* - * PE_VCS_Evaluate_Swap - */ -static void pe_vcs_evaluate_swap_entry(int port) -{ - print_current_state(port); - - /* - * Request the DPM for an evaluation of the VCONN Swap request. - * Note: Ports that are presently the VCONN Source must always - * accept a VCONN - */ - - /* - * Transition to the PE_VCS_Accept_Swap state when: - * 1) The Device Policy Manager indicates that a VCONN Swap is ok. - * - * Transition to the PE_VCS_Reject_Swap state when: - * 1) Port is not presently the VCONN Source and - * 2) The DPM indicates that a VCONN Swap is not ok or - * 3) The DPM indicates that a VCONN Swap cannot be done at this time. - */ - - /* DPM rejects a VCONN Swap and port is not a VCONN source*/ - if (!tc_check_vconn_swap(port) && tc_is_vconn_src(port) < 1) { - /* NOTE: PE_VCS_Reject_Swap State embedded here */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_REJECT); - } - /* Port is not ready to perform a VCONN swap */ - else if (tc_is_vconn_src(port) < 0) { - /* NOTE: PE_VCS_Reject_Swap State embedded here */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_WAIT); - } - /* Port is ready to perform a VCONN swap */ - else { - /* NOTE: PE_VCS_Accept_Swap State embedded here */ - PE_SET_FLAG(port, PE_FLAGS_ACCEPT); - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_ACCEPT); - } -} - -static void pe_vcs_evaluate_swap_run(int port) -{ - /* Wait for ACCEPT, WAIT or Reject message to send. */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - - if (PE_CHK_FLAG(port, PE_FLAGS_ACCEPT)) { - PE_CLR_FLAG(port, PE_FLAGS_ACCEPT); - /* Accept Message sent and Presently VCONN Source */ - if (tc_is_vconn_src(port)) - set_state_pe(port, PE_VCS_WAIT_FOR_VCONN_SWAP); - /* Accept Message sent and Not presently VCONN Source */ - else - set_state_pe(port, PE_VCS_TURN_ON_VCONN_SWAP); - } else { - /* - * Message sent. Transition back to PE_SRC_Ready or - * PE_SINK_Ready - */ - pe_set_ready_state(port); - } - return; - } - - if (pe_check_outgoing_discard(port)) - return; -} - -/* - * PE_VCS_Send_Swap - */ -static void pe_vcs_send_swap_entry(int port) -{ - print_current_state(port); - - /* Send a VCONN_Swap Message */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_VCONN_SWAP); - pe_sender_response_msg_entry(port); -} - -static void pe_vcs_send_swap_run(int port) -{ - uint8_t type; - uint8_t cnt; - enum tcpci_msg_type sop; - enum pe_msg_check msg_check; - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - if ((msg_check & PE_MSG_SENT) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - /* Increment once message has successfully sent */ - pe[port].vconn_swap_counter++; - - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - - /* Only look at control messages */ - if (cnt == 0) { - /* - * Transition to the PE_VCS_Wait_For_VCONN state when: - * 1) Accept Message Received and - * 2) The Port is presently the VCONN Source. - * - * Transition to the PE_VCS_Turn_On_VCONN state when: - * 1) Accept Message Received and - * 2) The Port is not presently the VCONN Source. - */ - if (type == PD_CTRL_ACCEPT) { - if (tc_is_vconn_src(port)) { - set_state_pe(port, - PE_VCS_WAIT_FOR_VCONN_SWAP); - } else { - set_state_pe(port, - PE_VCS_TURN_ON_VCONN_SWAP); - } - return; - } - /* - * Transition back to either the PE_SRC_Ready or - * PE_SNK_Ready state when: - * 2) Reject message is received or - * 3) Wait message Received. - */ - if (type == PD_CTRL_REJECT || type == PD_CTRL_WAIT) { - pe_set_ready_state(port); - return; - } - - /* - * The Policy Engine May transition to the - * PE_VCS_Force_Vconn state when: - * - A Not_Supported Message is received and - * - The Port is not presently the VCONN Source - */ - if (type == PD_CTRL_NOT_SUPPORTED) { - if (IS_ENABLED(CONFIG_USB_PD_REV30) && - !tc_is_vconn_src(port)) - set_state_pe(port, PE_VCS_FORCE_VCONN); - else - pe_set_ready_state(port); - return; - } - } - /* - * Unexpected Message Received, send soft reset with SOP* of - * incoming message. - */ - pe_send_soft_reset(port, sop); - return; - } - - /* - * Transition back to either the PE_SRC_Ready or - * PE_SNK_Ready state when: - * 1) SenderResponseTimer Timeout - * 2) Message was discarded. - */ - if ((msg_check & PE_MSG_DISCARDED) || - pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) - pe_set_ready_state(port); -} - -static void pe_vcs_send_swap_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -/* - * PE_VCS_Wait_for_VCONN_Swap - */ -static void pe_vcs_wait_for_vconn_swap_entry(int port) -{ - print_current_state(port); - - /* Start the VCONNOnTimer */ - pd_timer_enable(port, PE_TIMER_VCONN_ON, PD_T_VCONN_SOURCE_ON); - - /* - * The USB PD 3.0 spec indicates that the initial VCONN source - * shall cease sourcing VCONN within tVCONNSourceOff (25ms) - * after receiving the PS_RDY message. However, some partners - * begin sending SOP' messages only 1 ms after sending PS_RDY - * during VCONN swap. - * - * Preemptively disable receipt of SOP' and SOP'' messages while - * we wait for PS_RDY so we don't attempt to process messages - * directed at the cable. - * - * We continue to source VCONN while we wait as required by the - * spec. - */ - tcpm_sop_prime_enable(port, false); -} - -static void pe_vcs_wait_for_vconn_swap_run(int port) -{ - /* - * Transition to the PE_VCS_Turn_Off_VCONN state when: - * 1) A PS_RDY Message is received. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - /* - * PS_RDY message received - * - * Note: intentionally leave the receive flag set to indicate - * our route on exit when PS_RDY is received. - */ - if ((PD_HEADER_CNT(rx_emsg[port].header) == 0) && - (PD_HEADER_EXT(rx_emsg[port].header) == 0) && - (PD_HEADER_TYPE(rx_emsg[port].header) == PD_CTRL_PS_RDY)) { - set_state_pe(port, PE_VCS_TURN_OFF_VCONN_SWAP); - return; - } else { - /* - * Unexpected message received - reset with the SOP* of - * the incoming message. - */ - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - pe_send_soft_reset(port, - PD_HEADER_GET_SOP(rx_emsg[port].header)); - return; - } - } - - /* - * Transition to either the PE_SRC_Hard_Reset or - * PE_SNK_Hard_Reset state when: - * 1) The VCONNOnTimer times out. - */ - if (pd_timer_is_expired(port, PE_TIMER_VCONN_ON)) { - if (pe[port].power_role == PD_ROLE_SOURCE) - set_state_pe(port, PE_SRC_HARD_RESET); - else - set_state_pe(port, PE_SNK_HARD_RESET); - } -} - -static void pe_vcs_wait_for_vconn_swap_exit(int port) -{ - /* - * If we exited without getting PS_RDY, re-enable SOP' messaging since - * we are still the Vconn source. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - else - tcpm_sop_prime_enable(port, true); - - pd_timer_disable(port, PE_TIMER_VCONN_ON); -} - -/* - * PE_VCS_Turn_On_VCONN_Swap - */ -static void pe_vcs_turn_on_vconn_swap_entry(int port) -{ - print_current_state(port); - - /* Request DPM to turn on VCONN */ - pd_request_vconn_swap_on(port); -} - -static void pe_vcs_turn_on_vconn_swap_run(int port) -{ - - /* - * Transition to the PE_VCS_Send_Ps_Rdy state when: - * 1) The Port’s VCONN is on. - */ - if (pd_timer_is_disabled(port, PE_TIMER_TIMEOUT) && - PE_CHK_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE); - pd_timer_enable(port, PE_TIMER_TIMEOUT, - CONFIG_USBC_VCONN_SWAP_DELAY_US); - } - - if (pd_timer_is_expired(port, PE_TIMER_TIMEOUT)) - set_state_pe(port, PE_VCS_SEND_PS_RDY_SWAP); -} - -static void pe_vcs_turn_on_vconn_swap_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_TIMEOUT); -} - -/* - * PE_VCS_Turn_Off_VCONN_Swap - */ -static void pe_vcs_turn_off_vconn_swap_entry(int port) -{ - print_current_state(port); - - /* Request DPM to turn off VCONN */ - pd_request_vconn_swap_off(port); -} - -static void pe_vcs_turn_off_vconn_swap_run(int port) -{ - /* Wait for VCONN to turn off */ - if (PE_CHK_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE); - - /* - * A VCONN Swap Shall reset the DiscoverIdentityCounter - * to zero - */ - pe[port].discover_identity_counter = 0; - pe[port].dr_swap_attempt_counter = 0; - - pe_set_ready_state(port); - return; - } -} - -/* - * PE_VCS_Send_PS_Rdy_Swap - */ -static void pe_vcs_send_ps_rdy_swap_entry(int port) -{ - print_current_state(port); - - /* Check for any interruptions to this non-interruptible AMS */ - if (PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - enum tcpci_msg_type sop = - PD_HEADER_GET_SOP(rx_emsg[port].header); - - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - /* Soft reset with the SOP* of the incoming message */ - pe_send_soft_reset(port, sop); - return; - } - - /* Send a PS_RDY Message */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_PS_RDY); -} - -static void pe_vcs_send_ps_rdy_swap_run(int port) -{ - /* - * After a VCONN Swap the VCONN Source needs to reset - * the Cable Plug’s Protocol Layer in order to ensure - * MessageID synchronization. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - /* - * A VCONN Swap Shall reset the - * DiscoverIdentityCounter to zero - */ - pe[port].discover_identity_counter = 0; - pe[port].dr_swap_attempt_counter = 0; - - /* A SOP' soft reset is required after VCONN swap */ - pd_dpm_request(port, DPM_REQUEST_SOP_PRIME_SOFT_RESET_SEND); - pe_set_ready_state(port); - } - - if (pe_check_outgoing_discard(port)) - return; - - if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - /* PS_RDY didn't send, soft reset */ - pe_send_soft_reset(port, TCPCI_MSG_SOP); - } -} - -/* - * PE_VCS_Force_Vconn - */ -__maybe_unused static void pe_vcs_force_vconn_entry(int port) -{ - print_current_state(port); - - /* Request DPM to turn on VCONN */ - pd_request_vconn_swap_on(port); -} - -__maybe_unused static void pe_vcs_force_vconn_run(int port) -{ - /* - * The Policy Engine Shall transition back to either the PE_SRC_Ready - * or PE_SNK_Ready state when: - * 1) The Port’s VCONN is on. - * - * Note we'll wait CONFIG_USBC_VCONN_SWAP_DELAY_US, as defined by the - * board, to ensure Vconn is on. - */ - if (pd_timer_is_disabled(port, PE_TIMER_TIMEOUT) && - PE_CHK_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_VCONN_SWAP_COMPLETE); - pd_timer_enable(port, PE_TIMER_TIMEOUT, - CONFIG_USBC_VCONN_SWAP_DELAY_US); - } - - if (pd_timer_is_expired(port, PE_TIMER_TIMEOUT)) { - /* - * Note: A cable soft reset shouldn't be necessary as a - * Not_Supported reply means the partner doesn't support - * sourcing Vconn and did not communicate with the cable. - */ - pe_set_ready_state(port); - return; - } -} - -__maybe_unused static void pe_vcs_force_vconn_exit(int port) -{ - pd_timer_disable(port, PE_TIMER_TIMEOUT); -} - -/* - * PE_VCS_CBL_SEND_SOFT_RESET - * Note - Entry is only when directed by the DPM. Protocol errors are handled - * by the PE_SEND_SOFT_RESET state. - */ -static void pe_vcs_cbl_send_soft_reset_entry(int port) -{ - print_current_state(port); - - if (!pe_can_send_sop_prime(port)) { - /* - * If we're not VCONN source, return the appropriate state. - * A VCONN swap re-triggers sending SOP' soft reset - */ - if (pe_is_explicit_contract(port)) { - /* Return to PE_{SRC,SNK}_Ready state */ - pe_set_ready_state(port); - } else { - /* - * Not in Explicit Contract, so we must be a SRC, - * return to PE_Src_Send_Capabilities. - */ - set_state_pe(port, PE_SRC_SEND_CAPABILITIES); - } - return; - } - - send_ctrl_msg(port, TCPCI_MSG_SOP_PRIME, PD_CTRL_SOFT_RESET); - pe_sender_response_msg_entry(port); -} - -static void pe_vcs_cbl_send_soft_reset_run(int port) -{ - bool cable_soft_reset_complete = false; - enum pe_msg_check msg_check; - - msg_check = pe_sender_response_msg_run(port); - - /* Got ACCEPT or REJECT from Cable Plug */ - if ((msg_check & PE_MSG_SENT) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - cable_soft_reset_complete = true; - - /* - * Note: If port partner runs PD 2.0, we must use PD 2.0 to - * communicate with the cable plug when in an explicit contract. - * - * PD Spec Table 6-2: Revision Interoperability during an - * Explicit Contract - */ - if (prl_get_rev(port, TCPCI_MSG_SOP) != PD_REV20) - prl_set_rev(port, TCPCI_MSG_SOP_PRIME, - PD_HEADER_REV(rx_emsg[port].header)); - } - - /* No GoodCRC received, cable is not present */ - if (PE_CHK_FLAG(port, PE_FLAGS_PROTOCOL_ERROR)) { - PE_CLR_FLAG(port, PE_FLAGS_PROTOCOL_ERROR); - /* - * TODO(b/171823328): TCPMv2: Implement cable reset - * Cable reset will only be done here if we know for certain - * a cable is present (we've received the SOP' DiscId response). - */ - cable_soft_reset_complete = true; - } - - if (cable_soft_reset_complete || - pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE) || - (msg_check & PE_MSG_DISCARDED)) { - if (pe_is_explicit_contract(port)) { - /* Return to PE_{SRC,SNK}_Ready state */ - pe_set_ready_state(port); - } else { - /* - * Not in Explicit Contract, so we must be a SRC, - * return to PE_Src_Send_Capabilities. - */ - set_state_pe(port, PE_SRC_SEND_CAPABILITIES); - } - } -} - -static void pe_vcs_cbl_send_soft_reset_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -#endif /* CONFIG_USBC_VCONN */ - -/* - * PE_DR_SNK_Get_Sink_Cap and PE_SRC_Get_Sink_Cap State (shared) - */ -static void pe_dr_get_sink_cap_entry(int port) -{ - print_current_state(port); - - /* Send a Get Sink Cap Message */ - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_GET_SINK_CAP); - pe_sender_response_msg_entry(port); -} - -static void pe_dr_get_sink_cap_run(int port) -{ - int type; - int cnt; - int ext; - enum pe_msg_check msg_check; - enum tcpci_msg_type sop; - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - /* - * Transition to PE_[SRC,SNK]_Ready when: - * 1) A Sink_Capabilities Message is received - * 2) Or SenderResponseTimer times out - * 3) Or a Reject Message is received. - * - * Transition to PE_SEND_SOFT_RESET state when: - * 1) An unexpected message is received - */ - if ((msg_check & PE_MSG_SENT) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - ext = PD_HEADER_EXT(rx_emsg[port].header); - sop = PD_HEADER_GET_SOP(rx_emsg[port].header); - - if (ext == 0 && sop == TCPCI_MSG_SOP) { - if ((cnt > 0) && (type == PD_DATA_SINK_CAP)) { - uint32_t *payload = - (uint32_t *)rx_emsg[port].buf; - uint8_t cap_cnt = rx_emsg[port].len / - sizeof(uint32_t); - - pe_set_snk_caps(port, cap_cnt, payload); - - dpm_evaluate_sink_fixed_pdo(port, payload[0]); - pe_set_ready_state(port); - return; - } else if (cnt == 0 && (type == PD_CTRL_REJECT || - type == PD_CTRL_NOT_SUPPORTED)) { - pe_set_ready_state(port); - return; - } - /* Unexpected messages fall through to soft reset */ - } - - pe_send_soft_reset(port, sop); - return; - } - - /* - * Transition to PE_[SRC,SNK]_Ready state when: - * 1) SenderResponseTimer times out. - * 2) Message was discarded. - */ - if ((msg_check & PE_MSG_DISCARDED) || - pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) - pe_set_ready_state(port); -} - -static void pe_dr_get_sink_cap_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -/* - * PE_DR_SNK_Give_Source_Cap - */ -static void pe_dr_snk_give_source_cap_entry(int port) -{ - print_current_state(port); - - /* Send source capabilities. */ - send_source_cap(port); -} - -static void pe_dr_snk_give_source_cap_run(int port) -{ - /* - * Transition back to PE_SNK_Ready when the Source_Capabilities message - * has been successfully sent. - * - * Get Source Capabilities AMS is uninterruptible, but in case the - * partner violates the spec then send a soft reset rather than get - * stuck here. - */ - if (PE_CHK_FLAG(port, PE_FLAGS_TX_COMPLETE)) { - PE_CLR_FLAG(port, PE_FLAGS_TX_COMPLETE); - set_state_pe(port, PE_SNK_READY); - } else if (PE_CHK_FLAG(port, PE_FLAGS_MSG_DISCARDED)) { - pe_send_soft_reset(port, TCPCI_MSG_SOP); - } -} - -/* - * PE_DR_SRC_Get_Source_Cap - */ -static void pe_dr_src_get_source_cap_entry(int port) -{ - print_current_state(port); - - /* Send a Get_Source_Cap Message */ - tx_emsg[port].len = 0; - send_ctrl_msg(port, TCPCI_MSG_SOP, PD_CTRL_GET_SOURCE_CAP); - pe_sender_response_msg_entry(port); -} - -static void pe_dr_src_get_source_cap_run(int port) -{ - int type; - int cnt; - int ext; - enum pe_msg_check msg_check; - - /* - * Check the state of the message sent - */ - msg_check = pe_sender_response_msg_run(port); - - /* - * Transition to PE_SRC_Ready when: - * 1) A Source Capabilities Message is received. - * 2) A Reject Message is received. - */ - if ((msg_check & PE_MSG_SENT) && - PE_CHK_FLAG(port, PE_FLAGS_MSG_RECEIVED)) { - PE_CLR_FLAG(port, PE_FLAGS_MSG_RECEIVED); - - type = PD_HEADER_TYPE(rx_emsg[port].header); - cnt = PD_HEADER_CNT(rx_emsg[port].header); - ext = PD_HEADER_EXT(rx_emsg[port].header); - - if (ext == 0) { - if ((cnt > 0) && (type == PD_DATA_SOURCE_CAP)) { - uint32_t *payload = - (uint32_t *)rx_emsg[port].buf; - - pd_set_src_caps(port, cnt, payload); - - /* - * If we'd prefer to charge from this partner, - * then propose a PR swap. - */ - if (pd_can_charge_from_device(port, cnt, - payload)) - pd_request_power_swap(port); - - /* - * Report dual role power capability to the - * charge manager if present - */ - if (IS_ENABLED(CONFIG_CHARGE_MANAGER) && - pd_get_partner_dual_role_power(port)) - charge_manager_update_dualrole(port, - CAP_DUALROLE); - - set_state_pe(port, PE_SRC_READY); - } else if ((cnt == 0) && (type == PD_CTRL_REJECT || - type == PD_CTRL_NOT_SUPPORTED)) { - pd_set_src_caps(port, -1, NULL); - set_state_pe(port, PE_SRC_READY); - } else { - /* - * On protocol error, consider source cap - * retrieval a failure - */ - pd_set_src_caps(port, -1, NULL); - set_state_pe(port, PE_SEND_SOFT_RESET); - } - return; - } else { - pd_set_src_caps(port, -1, NULL); - set_state_pe(port, PE_SEND_SOFT_RESET); - return; - } - } - - /* - * Transition to PE_SRC_Ready state when: - * 1) the SenderResponseTimer times out. - * 2) Message was discarded. - */ - if ((msg_check & PE_MSG_DISCARDED) || - pd_timer_is_expired(port, PE_TIMER_SENDER_RESPONSE)) - set_state_pe(port, PE_SRC_READY); -} - -static void pe_dr_src_get_source_cap_exit(int port) -{ - pe_sender_response_msg_exit(port); -} - -const uint32_t * const pd_get_src_caps(int port) -{ - return pe[port].src_caps; -} - -void pd_set_src_caps(int port, int cnt, uint32_t *src_caps) -{ - int i; - - pe[port].src_cap_cnt = cnt; - - for (i = 0; i < cnt; i++) - pe[port].src_caps[i] = *src_caps++; -} - -uint8_t pd_get_src_cap_cnt(int port) -{ - if (pe[port].src_cap_cnt > 0) - return pe[port].src_cap_cnt; - - return 0; -} - -/* Track access to the PD discovery structures during HC execution */ -uint32_t task_access[CONFIG_USB_PD_PORT_MAX_COUNT][DISCOVERY_TYPE_COUNT]; - -void pd_dfp_discovery_init(int port) -{ - atomic_or(&task_access[port][TCPCI_MSG_SOP], BIT(task_get_current())); - atomic_or(&task_access[port][TCPCI_MSG_SOP_PRIME], - BIT(task_get_current())); - - memset(pe[port].discovery, 0, sizeof(pe[port].discovery)); - -} - -void pd_dfp_mode_init(int port) -{ - /* - * Clear the VDM Setup Done and Modal Operation flags so we will - * have a fresh discovery - */ - PE_CLR_FLAG(port, PE_FLAGS_VDM_SETUP_DONE | - PE_FLAGS_MODAL_OPERATION); - - memset(pe[port].partner_amodes, 0, sizeof(pe[port].partner_amodes)); - - /* Reset the DPM and DP modules to enable alternate mode entry. */ - dpm_init(port); - dp_init(port); - - if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE)) - tbt_init(port); - - if (IS_ENABLED(CONFIG_USB_PD_USB4)) - enter_usb_init(port); - - if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_UFP_DP)) - pd_ufp_set_dp_opos(port, 0); -} - -__maybe_unused void pd_discovery_access_clear(int port, - enum tcpci_msg_type type) -{ - if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - assert(0); - - atomic_clear_bits(&task_access[port][type], 0xFFFFFFFF); -} - -__maybe_unused bool pd_discovery_access_validate(int port, - enum tcpci_msg_type type) -{ - if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - assert(0); - - return !(task_access[port][type] & ~BIT(task_get_current())); -} - -__maybe_unused struct pd_discovery *pd_get_am_discovery_and_notify_access( - int port, enum tcpci_msg_type type) -{ - atomic_or(&task_access[port][type], BIT(task_get_current())); - return (struct pd_discovery *)pd_get_am_discovery(port, type); -} - -__maybe_unused const struct pd_discovery *pd_get_am_discovery(int port, - enum tcpci_msg_type type) -{ - if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - assert(0); - ASSERT(type < DISCOVERY_TYPE_COUNT); - - return &pe[port].discovery[type]; -} - -__maybe_unused struct partner_active_modes *pd_get_partner_active_modes( - int port, enum tcpci_msg_type type) -{ - if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - assert(0); - ASSERT(type < AMODE_TYPE_COUNT); - return &pe[port].partner_amodes[type]; -} - -__maybe_unused void pd_set_dfp_enter_mode_flag(int port, bool set) -{ - if (!IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - assert(0); - - if (set) - PE_SET_FLAG(port, PE_FLAGS_MODAL_OPERATION); - else - PE_CLR_FLAG(port, PE_FLAGS_MODAL_OPERATION); -} - -const char *pe_get_current_state(int port) -{ - if (pe_is_running(port) && IS_ENABLED(USB_PD_DEBUG_LABELS)) - return pe_state_names[get_state_pe(port)]; - else - return ""; -} - -uint32_t pe_get_flags(int port) -{ - return pe[port].flags; -} - -static __const_data const struct usb_state pe_states[] = { - /* Super States */ -#ifdef CONFIG_USB_PD_REV30 - [PE_PRS_FRS_SHARED] = { - .entry = pe_prs_frs_shared_entry, - .exit = pe_prs_frs_shared_exit, - }, -#endif - [PE_VDM_SEND_REQUEST] = { - .entry = pe_vdm_send_request_entry, - .run = pe_vdm_send_request_run, - .exit = pe_vdm_send_request_exit, - }, - - /* Normal States */ - [PE_SRC_STARTUP] = { - .entry = pe_src_startup_entry, - .run = pe_src_startup_run, - .exit = pe_src_startup_exit, - }, - [PE_SRC_DISCOVERY] = { - .entry = pe_src_discovery_entry, - .run = pe_src_discovery_run, - }, - [PE_SRC_SEND_CAPABILITIES] = { - .entry = pe_src_send_capabilities_entry, - .run = pe_src_send_capabilities_run, - .exit = pe_src_send_capabilities_exit, - }, - [PE_SRC_NEGOTIATE_CAPABILITY] = { - .entry = pe_src_negotiate_capability_entry, - }, - [PE_SRC_TRANSITION_SUPPLY] = { - .entry = pe_src_transition_supply_entry, - .run = pe_src_transition_supply_run, - .exit = pe_src_transition_supply_exit, - }, - [PE_SRC_READY] = { - .entry = pe_src_ready_entry, - .run = pe_src_ready_run, - }, - [PE_SRC_DISABLED] = { - .entry = pe_src_disabled_entry, - }, - [PE_SRC_CAPABILITY_RESPONSE] = { - .entry = pe_src_capability_response_entry, - .run = pe_src_capability_response_run, - }, - [PE_SRC_HARD_RESET] = { - .entry = pe_src_hard_reset_entry, - .run = pe_src_hard_reset_run, - .exit = pe_src_hard_reset_exit, - }, - [PE_SRC_HARD_RESET_RECEIVED] = { - .entry = pe_src_hard_reset_received_entry, - .run = pe_src_hard_reset_received_run, - .exit = pe_src_hard_reset_received_exit, - }, - [PE_SRC_TRANSITION_TO_DEFAULT] = { - .entry = pe_src_transition_to_default_entry, - .run = pe_src_transition_to_default_run, - }, - [PE_SNK_STARTUP] = { - .entry = pe_snk_startup_entry, - .run = pe_snk_startup_run, - }, - [PE_SNK_DISCOVERY] = { - .entry = pe_snk_discovery_entry, - .run = pe_snk_discovery_run, - }, - [PE_SNK_WAIT_FOR_CAPABILITIES] = { - .entry = pe_snk_wait_for_capabilities_entry, - .run = pe_snk_wait_for_capabilities_run, - .exit = pe_snk_wait_for_capabilities_exit, - }, - [PE_SNK_EVALUATE_CAPABILITY] = { - .entry = pe_snk_evaluate_capability_entry, - }, - [PE_SNK_SELECT_CAPABILITY] = { - .entry = pe_snk_select_capability_entry, - .run = pe_snk_select_capability_run, - .exit = pe_snk_select_capability_exit, - }, - [PE_SNK_READY] = { - .entry = pe_snk_ready_entry, - .run = pe_snk_ready_run, - }, - [PE_SNK_HARD_RESET] = { - .entry = pe_snk_hard_reset_entry, - .run = pe_snk_hard_reset_run, - }, - [PE_SNK_TRANSITION_TO_DEFAULT] = { - .entry = pe_snk_transition_to_default_entry, - .run = pe_snk_transition_to_default_run, - }, - [PE_SNK_GIVE_SINK_CAP] = { - .entry = pe_snk_give_sink_cap_entry, - .run = pe_snk_give_sink_cap_run, - }, - [PE_SNK_GET_SOURCE_CAP] = { - .entry = pe_snk_get_source_cap_entry, - .run = pe_snk_get_source_cap_run, - }, - [PE_SNK_TRANSITION_SINK] = { - .entry = pe_snk_transition_sink_entry, - .run = pe_snk_transition_sink_run, - .exit = pe_snk_transition_sink_exit, - }, - [PE_SEND_SOFT_RESET] = { - .entry = pe_send_soft_reset_entry, - .run = pe_send_soft_reset_run, - .exit = pe_send_soft_reset_exit, - }, - [PE_SOFT_RESET] = { - .entry = pe_soft_reset_entry, - .run = pe_soft_reset_run, - }, - [PE_SEND_NOT_SUPPORTED] = { - .entry = pe_send_not_supported_entry, - .run = pe_send_not_supported_run, - }, - [PE_SRC_PING] = { - .entry = pe_src_ping_entry, - .run = pe_src_ping_run, - }, - [PE_DRS_EVALUATE_SWAP] = { - .entry = pe_drs_evaluate_swap_entry, - .run = pe_drs_evaluate_swap_run, - }, - [PE_DRS_CHANGE] = { - .entry = pe_drs_change_entry, - .run = pe_drs_change_run, - }, - [PE_DRS_SEND_SWAP] = { - .entry = pe_drs_send_swap_entry, - .run = pe_drs_send_swap_run, - .exit = pe_drs_send_swap_exit, - }, - [PE_PRS_SRC_SNK_EVALUATE_SWAP] = { - .entry = pe_prs_src_snk_evaluate_swap_entry, - .run = pe_prs_src_snk_evaluate_swap_run, - }, - [PE_PRS_SRC_SNK_TRANSITION_TO_OFF] = { - .entry = pe_prs_src_snk_transition_to_off_entry, - .run = pe_prs_src_snk_transition_to_off_run, - .exit = pe_prs_src_snk_transition_to_off_exit, - }, - [PE_PRS_SRC_SNK_ASSERT_RD] = { - .entry = pe_prs_src_snk_assert_rd_entry, - .run = pe_prs_src_snk_assert_rd_run, - }, - [PE_PRS_SRC_SNK_WAIT_SOURCE_ON] = { - .entry = pe_prs_src_snk_wait_source_on_entry, - .run = pe_prs_src_snk_wait_source_on_run, - .exit = pe_prs_src_snk_wait_source_on_exit, - }, - [PE_PRS_SRC_SNK_SEND_SWAP] = { - .entry = pe_prs_src_snk_send_swap_entry, - .run = pe_prs_src_snk_send_swap_run, - .exit = pe_prs_src_snk_send_swap_exit, - }, - [PE_PRS_SNK_SRC_EVALUATE_SWAP] = { - .entry = pe_prs_snk_src_evaluate_swap_entry, - .run = pe_prs_snk_src_evaluate_swap_run, - }, - /* - * Some of the Power Role Swap actions are shared with the very - * similar actions of Fast Role Swap. - */ - /* State actions are shared with PE_FRS_SNK_SRC_TRANSITION_TO_OFF */ - [PE_PRS_SNK_SRC_TRANSITION_TO_OFF] = { - .entry = pe_prs_snk_src_transition_to_off_entry, - .run = pe_prs_snk_src_transition_to_off_run, - .exit = pe_prs_snk_src_transition_to_off_exit, -#ifdef CONFIG_USB_PD_REV30 - .parent = &pe_states[PE_PRS_FRS_SHARED], -#endif /* CONFIG_USB_PD_REV30 */ - }, - /* State actions are shared with PE_FRS_SNK_SRC_ASSERT_RP */ - [PE_PRS_SNK_SRC_ASSERT_RP] = { - .entry = pe_prs_snk_src_assert_rp_entry, - .run = pe_prs_snk_src_assert_rp_run, -#ifdef CONFIG_USB_PD_REV30 - .parent = &pe_states[PE_PRS_FRS_SHARED], -#endif /* CONFIG_USB_PD_REV30 */ - }, - /* State actions are shared with PE_FRS_SNK_SRC_SOURCE_ON */ - [PE_PRS_SNK_SRC_SOURCE_ON] = { - .entry = pe_prs_snk_src_source_on_entry, - .run = pe_prs_snk_src_source_on_run, - .exit = pe_prs_snk_src_source_on_exit, -#ifdef CONFIG_USB_PD_REV30 - .parent = &pe_states[PE_PRS_FRS_SHARED], -#endif /* CONFIG_USB_PD_REV30 */ - }, - /* State actions are shared with PE_FRS_SNK_SRC_SEND_SWAP */ - [PE_PRS_SNK_SRC_SEND_SWAP] = { - .entry = pe_prs_snk_src_send_swap_entry, - .run = pe_prs_snk_src_send_swap_run, - .exit = pe_prs_snk_src_send_swap_exit, -#ifdef CONFIG_USB_PD_REV30 - .parent = &pe_states[PE_PRS_FRS_SHARED], -#endif /* CONFIG_USB_PD_REV30 */ - }, -#ifdef CONFIG_USBC_VCONN - [PE_VCS_EVALUATE_SWAP] = { - .entry = pe_vcs_evaluate_swap_entry, - .run = pe_vcs_evaluate_swap_run, - }, - [PE_VCS_SEND_SWAP] = { - .entry = pe_vcs_send_swap_entry, - .run = pe_vcs_send_swap_run, - .exit = pe_vcs_send_swap_exit, - }, - [PE_VCS_WAIT_FOR_VCONN_SWAP] = { - .entry = pe_vcs_wait_for_vconn_swap_entry, - .run = pe_vcs_wait_for_vconn_swap_run, - .exit = pe_vcs_wait_for_vconn_swap_exit, - }, - [PE_VCS_TURN_ON_VCONN_SWAP] = { - .entry = pe_vcs_turn_on_vconn_swap_entry, - .run = pe_vcs_turn_on_vconn_swap_run, - .exit = pe_vcs_turn_on_vconn_swap_exit, - }, - [PE_VCS_TURN_OFF_VCONN_SWAP] = { - .entry = pe_vcs_turn_off_vconn_swap_entry, - .run = pe_vcs_turn_off_vconn_swap_run, - }, - [PE_VCS_SEND_PS_RDY_SWAP] = { - .entry = pe_vcs_send_ps_rdy_swap_entry, - .run = pe_vcs_send_ps_rdy_swap_run, - }, - [PE_VCS_CBL_SEND_SOFT_RESET] = { - .entry = pe_vcs_cbl_send_soft_reset_entry, - .run = pe_vcs_cbl_send_soft_reset_run, - .exit = pe_vcs_cbl_send_soft_reset_exit, - }, -#endif /* CONFIG_USBC_VCONN */ - [PE_VDM_IDENTITY_REQUEST_CBL] = { - .entry = pe_vdm_identity_request_cbl_entry, - .run = pe_vdm_identity_request_cbl_run, - .exit = pe_vdm_identity_request_cbl_exit, - .parent = &pe_states[PE_VDM_SEND_REQUEST], - }, - [PE_INIT_PORT_VDM_IDENTITY_REQUEST] = { - .entry = pe_init_port_vdm_identity_request_entry, - .run = pe_init_port_vdm_identity_request_run, - .exit = pe_init_port_vdm_identity_request_exit, - .parent = &pe_states[PE_VDM_SEND_REQUEST], - }, - [PE_INIT_VDM_SVIDS_REQUEST] = { - .entry = pe_init_vdm_svids_request_entry, - .run = pe_init_vdm_svids_request_run, - .exit = pe_init_vdm_svids_request_exit, - .parent = &pe_states[PE_VDM_SEND_REQUEST], - }, - [PE_INIT_VDM_MODES_REQUEST] = { - .entry = pe_init_vdm_modes_request_entry, - .run = pe_init_vdm_modes_request_run, - .exit = pe_init_vdm_modes_request_exit, - .parent = &pe_states[PE_VDM_SEND_REQUEST], - }, - [PE_VDM_REQUEST_DPM] = { - .entry = pe_vdm_request_dpm_entry, - .run = pe_vdm_request_dpm_run, - .exit = pe_vdm_request_dpm_exit, - .parent = &pe_states[PE_VDM_SEND_REQUEST], - }, - [PE_VDM_RESPONSE] = { - .entry = pe_vdm_response_entry, - .run = pe_vdm_response_run, - .exit = pe_vdm_response_exit, - }, - [PE_HANDLE_CUSTOM_VDM_REQUEST] = { - .entry = pe_handle_custom_vdm_request_entry, - .run = pe_handle_custom_vdm_request_run, - .exit = pe_handle_custom_vdm_request_exit, - }, - [PE_DEU_SEND_ENTER_USB] = { - .entry = pe_enter_usb_entry, - .run = pe_enter_usb_run, - .exit = pe_enter_usb_exit, - }, - [PE_WAIT_FOR_ERROR_RECOVERY] = { - .entry = pe_wait_for_error_recovery_entry, - .run = pe_wait_for_error_recovery_run, - }, - [PE_BIST_TX] = { - .entry = pe_bist_tx_entry, - .run = pe_bist_tx_run, - .exit = pe_bist_tx_exit, - }, - [PE_DR_GET_SINK_CAP] = { - .entry = pe_dr_get_sink_cap_entry, - .run = pe_dr_get_sink_cap_run, - .exit = pe_dr_get_sink_cap_exit, - }, - [PE_DR_SNK_GIVE_SOURCE_CAP] = { - .entry = pe_dr_snk_give_source_cap_entry, - .run = pe_dr_snk_give_source_cap_run, - }, - [PE_DR_SRC_GET_SOURCE_CAP] = { - .entry = pe_dr_src_get_source_cap_entry, - .run = pe_dr_src_get_source_cap_run, - .exit = pe_dr_src_get_source_cap_exit, - }, -#ifdef CONFIG_USB_PD_REV30 - [PE_FRS_SNK_SRC_START_AMS] = { - .entry = pe_frs_snk_src_start_ams_entry, - .parent = &pe_states[PE_PRS_FRS_SHARED], - }, -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - [PE_GIVE_BATTERY_CAP] = { - .entry = pe_give_battery_cap_entry, - .run = pe_give_battery_cap_run, - }, - [PE_GIVE_BATTERY_STATUS] = { - .entry = pe_give_battery_status_entry, - .run = pe_give_battery_status_run, - }, - [PE_SEND_ALERT] = { - .entry = pe_send_alert_entry, - .run = pe_send_alert_run, - }, -#else - [PE_SRC_CHUNK_RECEIVED] = { - .entry = pe_chunk_received_entry, - .run = pe_chunk_received_run, - .exit = pe_chunk_received_exit, - }, - [PE_SNK_CHUNK_RECEIVED] = { - .entry = pe_chunk_received_entry, - .run = pe_chunk_received_run, - .exit = pe_chunk_received_exit, - }, -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ -#ifdef CONFIG_USBC_VCONN - [PE_VCS_FORCE_VCONN] = { - .entry = pe_vcs_force_vconn_entry, - .run = pe_vcs_force_vconn_run, - .exit = pe_vcs_force_vconn_exit, - }, -#endif /* CONFIG_USBC_VCONN */ -#endif /* CONFIG_USB_PD_REV30 */ -}; - -#ifdef TEST_BUILD -/* TODO(b/173791979): Unit tests shouldn't need to access internal states */ -const struct test_sm_data test_pe_sm_data[] = { - { - .base = pe_states, - .size = ARRAY_SIZE(pe_states), - .names = pe_state_names, - .names_size = ARRAY_SIZE(pe_state_names), - }, -}; -BUILD_ASSERT(ARRAY_SIZE(pe_states) == ARRAY_SIZE(pe_state_names)); -const int test_pe_sm_data_size = ARRAY_SIZE(test_pe_sm_data); - -void pe_set_flag(int port, int flag) -{ - PE_SET_FLAG(port, flag); -} -void pe_clr_flag(int port, int flag) -{ - PE_CLR_FLAG(port, flag); -} -int pe_chk_flag(int port, int flag) -{ - return PE_CHK_FLAG(port, flag); -} -int pe_get_all_flags(int port) -{ - return pe[port].flags; -} -void pe_set_all_flags(int port, int flags) -{ - pe[port].flags = flags; -} -void pe_clr_dpm_requests(int port) -{ - pe[port].dpm_request = 0; -} -#endif diff --git a/common/usbc/usb_prl_sm.c b/common/usbc/usb_prl_sm.c deleted file mode 100644 index a58a579775..0000000000 --- a/common/usbc/usb_prl_sm.c +++ /dev/null @@ -1,2471 +0,0 @@ -/* Copyright 2019 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "battery.h" -#include "battery_smart.h" -#include "board.h" -#include "charge_manager.h" -#include "charge_state.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "cros_version.h" -#include "ec_commands.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "registers.h" -#include "system.h" -#include "task.h" -#include "tcpm/tcpm.h" -#include "util.h" -#include "usb_charge.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_timer.h" -#include "usb_pe_sm.h" -#include "usb_prl_sm.h" -#include "usb_tc_sm.h" -#include "usb_emsg.h" -#include "usb_sm.h" -#include "vpd_api.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* - * Define DEBUG_PRINT_FLAG_NAMES to print flag names when set and cleared. - */ -#undef DEBUG_PRINT_FLAG_NAMES - -#ifdef DEBUG_PRINT_FLAG_NAMES -__maybe_unused static void print_flag(const char *group, - int set_or_clear, - int flag); -#define SET_FLAG(group, flags, flag) \ - do { \ - print_flag(group, 1, flag); \ - atomic_or(flags, (flag)); \ - } while (0) -#define CLR_FLAG(group, flags, flag) \ - do { \ - int before = *flags; \ - atomic_clear_bits(flags, (flag)); \ - if (*flags != before) \ - print_flag(group, 0, flag); \ - } while (0) -#else -#define SET_FLAG(group, flags, flag) atomic_or(flags, (flag)) -#define CLR_FLAG(group, flags, flag) atomic_clear_bits(flags, (flag)) -#endif - - -#define RCH_SET_FLAG(port, flag) SET_FLAG("RCH", &rch[port].flags, (flag)) -#define RCH_CLR_FLAG(port, flag) CLR_FLAG("RCH", &rch[port].flags, (flag)) -#define RCH_CHK_FLAG(port, flag) (rch[port].flags & (flag)) - -#define TCH_SET_FLAG(port, flag) SET_FLAG("TCH", &tch[port].flags, (flag)) -#define TCH_CLR_FLAG(port, flag) CLR_FLAG("TCH", &tch[port].flags, (flag)) -#define TCH_CHK_FLAG(port, flag) (tch[port].flags & (flag)) - -#define PRL_TX_SET_FLAG(port, flag) \ - SET_FLAG("PRL_TX", &prl_tx[port].flags, (flag)) -#define PRL_TX_CLR_FLAG(port, flag) \ - CLR_FLAG("PRL_TX", &prl_tx[port].flags, (flag)) -#define PRL_TX_CHK_FLAG(port, flag) (prl_tx[port].flags & (flag)) - -#define PRL_HR_SET_FLAG(port, flag) \ - SET_FLAG("PRL_HR", &prl_hr[port].flags, (flag)) -#define PRL_HR_CLR_FLAG(port, flag) \ - CLR_FLAG("PRL_HR", &prl_hr[port].flags, (flag)) -#define PRL_HR_CHK_FLAG(port, flag) (prl_hr[port].flags & (flag)) - -#define PDMSG_SET_FLAG(port, flag) SET_FLAG("PDMSG", &pdmsg[port].flags, (flag)) -#define PDMSG_CLR_FLAG(port, flag) CLR_FLAG("PDMSG", &pdmsg[port].flags, (flag)) -#define PDMSG_CHK_FLAG(port, flag) (pdmsg[port].flags & (flag)) - -/* Protocol Layer Flags */ -/* - * NOTE: - * These flags are used in multiple state machines and could have - * different meanings in each state machine. - */ -/* Flag to note message transmission completed */ -#define PRL_FLAGS_TX_COMPLETE BIT(0) -/* Flag to note that PRL requested to set SINK_NG CC state */ -#define PRL_FLAGS_SINK_NG BIT(1) -/* Flag to note PRL waited for SINK_OK CC state before transmitting */ -#define PRL_FLAGS_WAIT_SINK_OK BIT(2) -/* Flag to note transmission error occurred */ -#define PRL_FLAGS_TX_ERROR BIT(3) -/* Flag to note PE triggered a hard reset */ -#define PRL_FLAGS_PE_HARD_RESET BIT(4) -/* Flag to note hard reset has completed */ -#define PRL_FLAGS_HARD_RESET_COMPLETE BIT(5) -/* Flag to note port partner sent a hard reset */ -#define PRL_FLAGS_PORT_PARTNER_HARD_RESET BIT(6) -/* - * Flag to note a message transmission has been requested. It is only cleared - * when we send the message to the TCPC layer. - */ -#define PRL_FLAGS_MSG_XMIT BIT(7) -/* Flag to note a message was received */ -#define PRL_FLAGS_MSG_RECEIVED BIT(8) -/* Flag to note aborting current TX message, not currently set */ -#define PRL_FLAGS_ABORT BIT(9) -/* Flag to note current TX message uses chunking */ -#define PRL_FLAGS_CHUNKING BIT(10) - -struct bit_name { - int value; - const char *name; -}; - -static __const_data struct bit_name flag_bit_names[] = { - { PRL_FLAGS_TX_COMPLETE, "PRL_FLAGS_TX_COMPLETE" }, - { PRL_FLAGS_SINK_NG, "PRL_FLAGS_SINK_NG" }, - { PRL_FLAGS_WAIT_SINK_OK, "PRL_FLAGS_WAIT_SINK_OK" }, - { PRL_FLAGS_TX_ERROR, "PRL_FLAGS_TX_ERROR" }, - { PRL_FLAGS_PE_HARD_RESET, "PRL_FLAGS_PE_HARD_RESET" }, - { PRL_FLAGS_HARD_RESET_COMPLETE, "PRL_FLAGS_HARD_RESET_COMPLETE" }, - { PRL_FLAGS_PORT_PARTNER_HARD_RESET, - "PRL_FLAGS_PORT_PARTNER_HARD_RESET" }, - { PRL_FLAGS_MSG_XMIT, "PRL_FLAGS_MSG_XMIT" }, - { PRL_FLAGS_MSG_RECEIVED, "PRL_FLAGS_MSG_RECEIVED" }, - { PRL_FLAGS_ABORT, "PRL_FLAGS_ABORT" }, - { PRL_FLAGS_CHUNKING, "PRL_FLAGS_CHUNKING" }, -}; - -__maybe_unused static void print_bits(const char *group, - const char *desc, - int value, - struct bit_name *names, - int names_size) -{ - int i; - - CPRINTF("%s %s 0x%x : ", group, desc, value); - for (i = 0; i < names_size; i++) { - if (value & names[i].value) - CPRINTF("%s | ", names[i].name); - value &= ~names[i].value; - } - if (value != 0) - CPRINTF("0x%x", value); - CPRINTF("\n"); -} - -__maybe_unused static void print_flag(const char *group, - int set_or_clear, - int flag) -{ - print_bits(group, set_or_clear ? "Set" : "Clr", flag, flag_bit_names, - ARRAY_SIZE(flag_bit_names)); -} - -/* PD counter definitions */ -#define PD_MESSAGE_ID_COUNT 7 - -/* Size of PDMSG Chunk Buffer */ -#define CHK_BUF_SIZE 7 -#define CHK_BUF_SIZE_BYTES 28 - -/* - * Debug log level - higher number == more log - * Level 0: disabled - * Level 1: not currently used - * Level 2: plus non-ping messages - * Level 3: plus ping packet and PRL states - * - * Note that higher log level causes timing changes and thus may affect - * performance. - */ -#ifdef CONFIG_USB_PD_DEBUG_LEVEL -static const enum debug_level prl_debug_level = CONFIG_USB_PD_DEBUG_LEVEL; -#else -static enum debug_level prl_debug_level = DEBUG_LEVEL_1; -#endif - -static enum sm_local_state local_state[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Protocol Transmit States (Section 6.11.2.2) */ -enum usb_prl_tx_state { - PRL_TX_PHY_LAYER_RESET, - PRL_TX_WAIT_FOR_MESSAGE_REQUEST, - PRL_TX_LAYER_RESET_FOR_TRANSMIT, - PRL_TX_WAIT_FOR_PHY_RESPONSE, - PRL_TX_SRC_SOURCE_TX, - PRL_TX_SNK_START_AMS, - PRL_TX_SRC_PENDING, - PRL_TX_SNK_PENDING, - PRL_TX_DISCARD_MESSAGE, -}; - -/* Protocol Hard Reset States (Section 6.11.2.4) */ -enum usb_prl_hr_state { - PRL_HR_WAIT_FOR_REQUEST, - PRL_HR_RESET_LAYER, - PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE, - PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE, -}; - -/* Chunked Rx states (Section 6.11.2.1.2) */ -enum usb_rch_state { - RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER, - RCH_PASS_UP_MESSAGE, - RCH_PROCESSING_EXTENDED_MESSAGE, - RCH_REQUESTING_CHUNK, - RCH_WAITING_CHUNK, - RCH_REPORT_ERROR, -}; - -/* Chunked Tx states (Section 6.11.2.1.3) */ -enum usb_tch_state { - TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE, - TCH_WAIT_FOR_TRANSMISSION_COMPLETE, - TCH_CONSTRUCT_CHUNKED_MESSAGE, - TCH_SENDING_CHUNKED_MESSAGE, - TCH_WAIT_CHUNK_REQUEST, - TCH_MESSAGE_RECEIVED, - TCH_MESSAGE_SENT, - TCH_REPORT_ERROR, -}; - -static const char * const prl_tx_state_names[] = { - [PRL_TX_PHY_LAYER_RESET] = "PRL_TX_PHY_LAYER_RESET", - [PRL_TX_WAIT_FOR_MESSAGE_REQUEST] = "PRL_TX_WAIT_FOR_MESSAGE_REQUEST", - [PRL_TX_LAYER_RESET_FOR_TRANSMIT] = "PRL_TX_LAYER_RESET_FOR_TRANSMIT", - [PRL_TX_WAIT_FOR_PHY_RESPONSE] = "PRL_TX_WAIT_FOR_PHY_RESPONSE", - [PRL_TX_SRC_SOURCE_TX] = "PRL_TX_SRC_SOURCE_TX", - [PRL_TX_SNK_START_AMS] = "PRL_TX_SNK_START_AMS", - [PRL_TX_SRC_PENDING] = "PRL_TX_SRC_PENDING", - [PRL_TX_SNK_PENDING] = "PRL_TX_SNK_PENDING", - [PRL_TX_DISCARD_MESSAGE] = "PRL_TX_DISCARD_MESSAGE", -}; - -static const char * const prl_hr_state_names[] = { - [PRL_HR_WAIT_FOR_REQUEST] = "PRL_HR_WAIT_FOR_REQUEST", - [PRL_HR_RESET_LAYER] = "PRL_HR_RESET_LAYER", - [PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE] - = "PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE", - [PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE] - = "PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE", -}; - -__maybe_unused static const char * const rch_state_names[] = { - [RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER] - = "RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER", - [RCH_PASS_UP_MESSAGE] = "RCH_PASS_UP_MESSAGE", - [RCH_PROCESSING_EXTENDED_MESSAGE] = "RCH_PROCESSING_EXTENDED_MESSAGE", - [RCH_REQUESTING_CHUNK] = "RCH_REQUESTING_CHUNK", - [RCH_WAITING_CHUNK] = "RCH_WAITING_CHUNK", - [RCH_REPORT_ERROR] = "RCH_REPORT_ERROR", -}; - -__maybe_unused static const char * const tch_state_names[] = { - [TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE] - = "TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE", - [TCH_WAIT_FOR_TRANSMISSION_COMPLETE] - = "TCH_WAIT_FOR_TRANSMISSION_COMPLETE", - [TCH_CONSTRUCT_CHUNKED_MESSAGE] = "TCH_CONSTRUCT_CHUNKED_MESSAGE", - [TCH_SENDING_CHUNKED_MESSAGE] = "TCH_SENDING_CHUNKED_MESSAGE", - [TCH_WAIT_CHUNK_REQUEST] = "TCH_WAIT_CHUNK_REQUEST", - [TCH_MESSAGE_RECEIVED] = "TCH_MESSAGE_RECEIVED", - [TCH_MESSAGE_SENT] = "TCH_MESSAGE_SENT", - [TCH_REPORT_ERROR] = "TCH_REPORT_ERROR", -}; - -/* Forward declare full list of states. Index by above enums. */ -static const struct usb_state prl_tx_states[]; -static const struct usb_state prl_hr_states[]; - -__maybe_unused static const struct usb_state rch_states[]; -__maybe_unused static const struct usb_state tch_states[]; - -/* Chunked Rx State Machine Object */ -static struct rx_chunked { - /* state machine context */ - struct sm_ctx ctx; - /* PRL_FLAGS */ - uint32_t flags; - /* error to report when moving to rch_report_error state */ - enum pe_error error; -} rch[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Chunked Tx State Machine Object */ -static struct tx_chunked { - /* state machine context */ - struct sm_ctx ctx; - /* state machine flags */ - uint32_t flags; - /* error to report when moving to tch_report_error state */ - enum pe_error error; -} tch[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Message Reception State Machine Object */ -static struct protocol_layer_rx { - /* received message type */ - enum tcpci_msg_type sop; - /* message ids for all valid port partners */ - int msg_id[NUM_SOP_STAR_TYPES]; -} prl_rx[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Message Transmission State Machine Object */ -static struct protocol_layer_tx { - /* state machine context */ - struct sm_ctx ctx; - /* state machine flags */ - uint32_t flags; - /* last message type we transmitted */ - enum tcpci_msg_type last_xmit_type; - /* message id counters for all 6 port partners */ - uint32_t msg_id_counter[NUM_SOP_STAR_TYPES]; - /* transmit status */ - int xmit_status; -} prl_tx[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Hard Reset State Machine Object */ -static struct protocol_hard_reset { - /* state machine context */ - struct sm_ctx ctx; - /* state machine flags */ - uint32_t flags; -} prl_hr[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Chunking Message Object */ -static struct pd_message { - /* message status flags */ - uint32_t flags; - /* SOP* */ - enum tcpci_msg_type xmit_type; - /* type of message */ - uint8_t msg_type; - /* PD revision */ - enum pd_rev_type rev[NUM_SOP_STAR_TYPES]; - /* Number of 32-bit objects in chk_buf */ - uint16_t data_objs; - /* temp chunk buffer */ - uint32_t tx_chk_buf[CHK_BUF_SIZE]; - uint32_t rx_chk_buf[CHK_BUF_SIZE]; - uint32_t chunk_number_expected; - uint32_t num_bytes_received; -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - /* extended message */ - uint8_t ext; - uint32_t chunk_number_to_send; - uint32_t send_offset; -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ -} pdmsg[CONFIG_USB_PD_PORT_MAX_COUNT]; - -struct extended_msg rx_emsg[CONFIG_USB_PD_PORT_MAX_COUNT]; -struct extended_msg tx_emsg[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Common Protocol Layer Message Transmission */ -static void prl_tx_construct_message(int port); -static void prl_rx_wait_for_phy_message(const int port, int evt); -static void prl_copy_msg_to_buffer(int port); - -#ifndef CONFIG_USB_PD_REV30 -GEN_NOT_SUPPORTED(PRL_TX_SRC_SOURCE_TX); -#define PRL_TX_SRC_SOURCE_TX PRL_TX_SRC_SOURCE_TX_NOT_SUPPORTED -GEN_NOT_SUPPORTED(PRL_TX_SNK_START_AMS); -#define PRL_TX_SNK_START_AMS PRL_TX_SNK_START_AMS_NOT_SUPPORTED - -GEN_NOT_SUPPORTED(RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); -#define RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER \ - RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER_NOT_SUPPORTED -GEN_NOT_SUPPORTED(RCH_PASS_UP_MESSAGE); -#define RCH_PASS_UP_MESSAGE RCH_PASS_UP_MESSAGE_NOT_SUPPORTED -GEN_NOT_SUPPORTED(RCH_PROCESSING_EXTENDED_MESSAGE); -#define RCH_PROCESSING_EXTENDED_MESSAGE \ - RCH_PROCESSING_EXTENDED_MESSAGE_NOT_SUPPORTED -GEN_NOT_SUPPORTED(RCH_REQUESTING_CHUNK); -#define RCH_REQUESTING_CHUNK RCH_REQUESTING_CHUNK_NOT_SUPPORTED -GEN_NOT_SUPPORTED(RCH_WAITING_CHUNK); -#define RCH_WAITING_CHUNK RCH_WAITING_CHUNK_NOT_SUPPORTED -GEN_NOT_SUPPORTED(RCH_REPORT_ERROR); -#define RCH_REPORT_ERROR RCH_REPORT_ERROR_NOT_SUPPORTED - -GEN_NOT_SUPPORTED(TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); -#define TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE \ - TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE_NOT_SUPPORTED -GEN_NOT_SUPPORTED(TCH_WAIT_FOR_TRANSMISSION_COMPLETE); -#define TCH_WAIT_FOR_TRANSMISSION_COMPLETE \ - TCH_WAIT_FOR_TRANSMISSION_COMPLETE_NOT_SUPPORTED -GEN_NOT_SUPPORTED(TCH_CONSTRUCT_CHUNKED_MESSAGE); -#define TCH_CONSTRUCT_CHUNKED_MESSAGE \ - TCH_CONSTRUCT_CHUNKED_MESSAGE_NOT_SUPPORTED -GEN_NOT_SUPPORTED(TCH_SENDING_CHUNKED_MESSAGE); -#define TCH_SENDING_CHUNKED_MESSAGE TCH_SENDING_CHUNKED_MESSAGE_NOT_SUPPORTED -GEN_NOT_SUPPORTED(TCH_WAIT_CHUNK_REQUEST); -#define TCH_WAIT_CHUNK_REQUEST TCH_WAIT_CHUNK_REQUEST_NOT_SUPPORTED -GEN_NOT_SUPPORTED(TCH_MESSAGE_RECEIVED); -#define TCH_MESSAGE_RECEIVED TCH_MESSAGE_RECEIVED_NOT_SUPPORTED -GEN_NOT_SUPPORTED(TCH_MESSAGE_SENT); -#define TCH_MESSAGE_SENT TCH_MESSAGE_SENT_NOT_SUPPORTED -GEN_NOT_SUPPORTED(TCH_REPORT_ERROR); -#define TCH_REPORT_ERROR TCH_REPORT_ERROR_NOT_SUPPORTED -#endif /* !CONFIG_USB_PD_REV30 */ - -/* To store the time stamp when TCPC sets TX Complete Success */ -static timestamp_t tcpc_tx_success_ts[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Set the protocol transmit statemachine to a new state. */ -static void set_state_prl_tx(const int port, - const enum usb_prl_tx_state new_state) -{ - set_state(port, &prl_tx[port].ctx, &prl_tx_states[new_state]); -} - -/* Get the protocol transmit statemachine's current state. */ -test_export_static enum usb_prl_tx_state prl_tx_get_state(const int port) -{ - return prl_tx[port].ctx.current - &prl_tx_states[0]; -} - -/* Print the protocol transmit statemachine's current state. */ -static void print_current_prl_tx_state(const int port) -{ - if (prl_debug_level >= DEBUG_LEVEL_3) - CPRINTS("C%d: %s", port, - prl_tx_state_names[prl_tx_get_state(port)]); -} - -/* Set the hard reset statemachine to a new state. */ -static void set_state_prl_hr(const int port, - const enum usb_prl_hr_state new_state) -{ - set_state(port, &prl_hr[port].ctx, &prl_hr_states[new_state]); -} - -/* Get the hard reset statemachine's current state. */ -enum usb_prl_hr_state prl_hr_get_state(const int port) -{ - return prl_hr[port].ctx.current - &prl_hr_states[0]; -} - -/* Print the hard reset statemachine's current state. */ -static void print_current_prl_hr_state(const int port) -{ - if (prl_debug_level >= DEBUG_LEVEL_3) - CPRINTS("C%d: %s", port, - prl_hr_state_names[prl_hr_get_state(port)]); -} - -/* Set the chunked Rx statemachine to a new state. */ -static void set_state_rch(const int port, const enum usb_rch_state new_state) -{ - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) - set_state(port, &rch[port].ctx, &rch_states[new_state]); -} - -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES -/* Get the chunked Rx statemachine's current state. */ -test_export_static enum usb_rch_state rch_get_state(const int port) -{ - return rch[port].ctx.current - &rch_states[0]; -} - -/* Print the chunked Rx statemachine's current state. */ -static void print_current_rch_state(const int port) -{ - if (prl_debug_level >= DEBUG_LEVEL_3) - CPRINTS("C%d: %s", port, - rch_state_names[rch_get_state(port)]); -} -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ - -/* Set the chunked Tx statemachine to a new state. */ -static void set_state_tch(const int port, const enum usb_tch_state new_state) -{ - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) - set_state(port, &tch[port].ctx, &tch_states[new_state]); -} - -/* Get the chunked Tx statemachine's current state. */ -test_export_static enum usb_tch_state tch_get_state(const int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) - return tch[port].ctx.current - &tch_states[0]; - else - return 0; -} - -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES -/* Print the chunked Tx statemachine's current state. */ -static void print_current_tch_state(const int port) -{ - if (prl_debug_level >= DEBUG_LEVEL_3) - CPRINTS("C%d: %s", port, - tch_state_names[tch_get_state(port)]); -} -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ - - -timestamp_t prl_get_tcpc_tx_success_ts(int port) -{ - return tcpc_tx_success_ts[port]; -} - -/* Sets the time stamp when TCPC reports TX success. */ -static void set_tcpc_tx_success_ts(int port) -{ - tcpc_tx_success_ts[port] = get_time(); -} - -void pd_transmit_complete(int port, int status) -{ - if (status == TCPC_TX_COMPLETE_SUCCESS) - set_tcpc_tx_success_ts(port); - prl_tx[port].xmit_status = status; -} - -void pd_execute_hard_reset(int port) -{ - /* Only allow async. function calls when state machine is running */ - if (!prl_is_running(port)) - return; - - PRL_HR_SET_FLAG(port, PRL_FLAGS_PORT_PARTNER_HARD_RESET); - set_state_prl_hr(port, PRL_HR_RESET_LAYER); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void prl_execute_hard_reset(int port) -{ - /* Only allow async. function calls when state machine is running */ - if (!prl_is_running(port)) - return; - - PRL_HR_SET_FLAG(port, PRL_FLAGS_PE_HARD_RESET); - set_state_prl_hr(port, PRL_HR_RESET_LAYER); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -int prl_is_running(int port) -{ - return local_state[port] == SM_RUN; -} - -static void prl_init(int port) -{ - int i; - const struct sm_ctx cleared = {}; - - /* - * flags without PRL_FLAGS_SINK_NG present means we are initially - * in SinkTxOK state - */ - prl_tx[port].flags = 0; - if (IS_ENABLED(CONFIG_USB_PD_REV30)) - typec_select_src_collision_rp(port, SINK_TX_OK); - prl_tx[port].last_xmit_type = TCPCI_MSG_SOP; - prl_tx[port].xmit_status = TCPC_TX_UNSET; - - if (IS_ENABLED(CONFIG_USB_PD_REV30)) { - tch[port].flags = 0; - rch[port].flags = 0; - } - - pdmsg[port].flags = 0; - - prl_hr[port].flags = 0; - - for (i = 0; i < NUM_SOP_STAR_TYPES; i++) { - prl_rx[port].msg_id[i] = -1; - prl_tx[port].msg_id_counter[i] = 0; - } - - pd_timer_disable_range(port, PR_TIMER_RANGE); - - /* Clear state machines and set initial states */ - prl_tx[port].ctx = cleared; - set_state_prl_tx(port, PRL_TX_PHY_LAYER_RESET); - - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { - rch[port].ctx = cleared; - set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); - - tch[port].ctx = cleared; - set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); - } - - prl_hr[port].ctx = cleared; - set_state_prl_hr(port, PRL_HR_WAIT_FOR_REQUEST); -} - -bool prl_is_busy(int port) -{ -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - return rch_get_state(port) != - RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER || - tch_get_state(port) != - TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE; -#else - return false; -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ -} - -void prl_set_debug_level(enum debug_level debug_level) -{ -#ifndef CONFIG_USB_PD_DEBUG_LEVEL - prl_debug_level = debug_level; -#endif -} - -void prl_hard_reset_complete(int port) -{ - PRL_HR_SET_FLAG(port, PRL_FLAGS_HARD_RESET_COMPLETE); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void prl_send_ctrl_msg(int port, - enum tcpci_msg_type type, - enum pd_ctrl_msg_type msg) -{ - pdmsg[port].xmit_type = type; - pdmsg[port].msg_type = msg; - pdmsg[port].data_objs = 0; - tx_emsg[port].len = 0; - -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - pdmsg[port].ext = 0; - - TCH_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); -#else - PRL_TX_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ - - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void prl_send_data_msg(int port, - enum tcpci_msg_type type, - enum pd_data_msg_type msg) -{ - pdmsg[port].xmit_type = type; - pdmsg[port].msg_type = msg; - -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - pdmsg[port].ext = 0; - - TCH_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); -#else - prl_copy_msg_to_buffer(port); - PRL_TX_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ - - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES -void prl_send_ext_data_msg(int port, - enum tcpci_msg_type type, - enum pd_ext_msg_type msg) -{ - pdmsg[port].xmit_type = type; - pdmsg[port].msg_type = msg; - pdmsg[port].ext = 1; - - TCH_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); - task_wake(PD_PORT_TO_TASK_ID(port)); -} -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ - -void prl_set_default_pd_revision(int port) -{ - /* - * Initialize to highest revision supported. If the port or cable - * partner doesn't support this revision, the Protocol Engine will - * lower this value to the revision supported by the partner. - */ - pdmsg[port].rev[TCPCI_MSG_SOP] = PD_REVISION; - pdmsg[port].rev[TCPCI_MSG_SOP_PRIME] = PD_REVISION; - pdmsg[port].rev[TCPCI_MSG_SOP_PRIME_PRIME] = PD_REVISION; - pdmsg[port].rev[TCPCI_MSG_SOP_DEBUG_PRIME] = PD_REVISION; - pdmsg[port].rev[TCPCI_MSG_SOP_DEBUG_PRIME_PRIME] = PD_REVISION; -} - -void prl_reset_soft(int port) -{ - /* Do not change negotiated PD Revision Specification level */ - local_state[port] = SM_INIT; - - /* Ensure we process the reset quickly */ - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void prl_run(int port, int evt, int en) -{ - switch (local_state[port]) { - case SM_PAUSED: - if (!en) - break; - /* fall through */ - case SM_INIT: - prl_init(port); - local_state[port] = SM_RUN; - /* fall through */ - case SM_RUN: - if (!en) { - /* Disable RX */ - if (IS_ENABLED(CONFIG_USB_CTVPD) || - IS_ENABLED(CONFIG_USB_VPD)) - vpd_rx_enable(0); - else - tcpm_set_rx_enable(port, 0); - - local_state[port] = SM_PAUSED; - break; - } - - /* Run Protocol Layer Hard Reset state machine */ - run_state(port, &prl_hr[port].ctx); - - /* - * If the Hard Reset state machine is active, then there is no - * need to execute any other PRL state machines. When the hard - * reset is complete, all PRL state machines will have been - * reset. - */ - if (prl_hr_get_state(port) == PRL_HR_WAIT_FOR_REQUEST) { - - /* Run Protocol Layer Message Reception */ - prl_rx_wait_for_phy_message(port, evt); - - - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { - /* - * Run RX Chunked state machine after prl_rx. - * This is what informs the PE of incoming - * message. Its input is prl_rx - */ - run_state(port, &rch[port].ctx); - - /* - * Run TX Chunked state machine before prl_tx - * in case we need to split an extended message - * and prl_tx can send it for us - */ - run_state(port, &tch[port].ctx); - } - - /* Run Protocol Layer Message Tx state machine */ - run_state(port, &prl_tx[port].ctx); - - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) - /* - * Run TX Chunked state machine again after - * prl_tx so we can handle passing TX_COMPLETE - * (or failure) up to PE in a single iteration. - */ - run_state(port, &tch[port].ctx); - } - break; - } -} - -void prl_set_rev(int port, enum tcpci_msg_type type, - enum pd_rev_type rev) -{ - /* We only store revisions for SOP* types. */ - ASSERT(type < NUM_SOP_STAR_TYPES); - - pdmsg[port].rev[type] = rev; -} - -enum pd_rev_type prl_get_rev(int port, enum tcpci_msg_type type) -{ - /* We only store revisions for SOP* types. */ - ASSERT(type < NUM_SOP_STAR_TYPES); - - return pdmsg[port].rev[type]; -} - -static void prl_copy_msg_to_buffer(int port) -{ - /* - * Control Messages will have a length of 0 and - * no need to spend time with the tx_chk_buf - * for this path - */ - if (tx_emsg[port].len == 0) { - pdmsg[port].data_objs = 0; - return; - } - - /* - * Make sure the Policy Engine isn't sending - * more than CHK_BUF_SIZE_BYTES. If so, - * truncate len. This will surely send a - * malformed packet resulting in the port - * partner soft\hard resetting us. - */ - if (tx_emsg[port].len > CHK_BUF_SIZE_BYTES) - tx_emsg[port].len = CHK_BUF_SIZE_BYTES; - - /* Copy message to chunked buffer */ - memset((uint8_t *)pdmsg[port].tx_chk_buf, 0, CHK_BUF_SIZE_BYTES); - memcpy((uint8_t *)pdmsg[port].tx_chk_buf, (uint8_t *)tx_emsg[port].buf, - tx_emsg[port].len); - /* - * Pad length to 4-byte boundary and - * convert to number of 32-bit objects. - * Since the value is shifted right by 2, - * no need to explicitly clear the lower - * 2-bits. - */ - pdmsg[port].data_objs = (tx_emsg[port].len + 3) >> 2; -} - -static __maybe_unused int pdmsg_xmit_type_is_rev30(const int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_REV30)) - return ((pdmsg[port].xmit_type < NUM_SOP_STAR_TYPES) - && (prl_get_rev(port, pdmsg[port].xmit_type) == PD_REV30)); - else - return 0; -} - -/* Returns true if the SOP port partner operates at PD rev3.0 */ -static bool is_sop_rev30(const int port) -{ - return IS_ENABLED(CONFIG_USB_PD_REV30) && - prl_get_rev(port, TCPCI_MSG_SOP) == PD_REV30; -} - -/* Common Protocol Layer Message Transmission */ -static void prl_tx_phy_layer_reset_entry(const int port) -{ - print_current_prl_tx_state(port); - - if (IS_ENABLED(CONFIG_USB_CTVPD) - || IS_ENABLED(CONFIG_USB_VPD)) { - vpd_rx_enable(pd_is_connected(port)); - } else { - /* Note: can't clear PHY messages due to TCPC architecture */ - /* Enable communications*/ - tcpm_set_rx_enable(port, pd_is_connected(port)); - } - set_state_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); -} - -static void prl_tx_wait_for_message_request_entry(const int port) -{ - /* No phy layer response is pending */ - prl_tx[port].xmit_status = TCPC_TX_UNSET; - print_current_prl_tx_state(port); -} - -static void prl_tx_wait_for_message_request_run(const int port) -{ - /* Clear any AMS flags and state if we are no longer in an AMS */ - if (IS_ENABLED(CONFIG_USB_PD_REV30) && !pe_in_local_ams(port)) { - /* Note PRL_Tx_Src_Sink_Tx is embedded here. */ - if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_SINK_NG)) { - typec_select_src_collision_rp(port, SINK_TX_OK); - typec_update_cc(port); - } - PRL_TX_CLR_FLAG(port, - PRL_FLAGS_SINK_NG | PRL_FLAGS_WAIT_SINK_OK); - } - - /* - * Check if we are starting an AMS and need to wait and/or set the CC - * lines appropriately. - */ - if (IS_ENABLED(CONFIG_USB_PD_REV30) && is_sop_rev30(port) && - pe_in_local_ams(port)) { - if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_SINK_NG | - PRL_FLAGS_WAIT_SINK_OK)) { - /* - * If we are already in an AMS then allow the - * multi-message AMS to continue, even if we - * swap power roles. - * - * Fall Through using the current AMS - */ - } else { - /* - * Start of SRC AMS notification received from - * Policy Engine - */ - if (pd_get_power_role(port) == PD_ROLE_SOURCE) { - PRL_TX_SET_FLAG(port, PRL_FLAGS_SINK_NG); - set_state_prl_tx(port, PRL_TX_SRC_SOURCE_TX); - } else { - PRL_TX_SET_FLAG(port, PRL_FLAGS_WAIT_SINK_OK); - set_state_prl_tx(port, PRL_TX_SNK_START_AMS); - } - return; - } - } - - /* Handle non Rev 3.0 or subsequent messages in AMS sequence */ - if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { - PRL_TX_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); - /* - * Soft Reset Message Message pending - */ - if ((pdmsg[port].msg_type == PD_CTRL_SOFT_RESET) && - (tx_emsg[port].len == 0)) { - set_state_prl_tx(port, PRL_TX_LAYER_RESET_FOR_TRANSMIT); - } - /* - * Message pending (except Soft Reset) - */ - else { - /* NOTE: PRL_TX_Construct_Message State embedded here */ - prl_tx_construct_message(port); - set_state_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); - } - - return; - } -} - -static void increment_msgid_counter(int port) -{ - /* If the last message wasn't an SOP* message, no need to increment */ - if (prl_tx[port].last_xmit_type >= NUM_SOP_STAR_TYPES) - return; - - prl_tx[port].msg_id_counter[prl_tx[port].last_xmit_type] = - (prl_tx[port].msg_id_counter[prl_tx[port].last_xmit_type] + 1) & - PD_MESSAGE_ID_COUNT; -} - -/* - * PrlTxDiscard - */ -static void prl_tx_discard_message_entry(const int port) -{ - print_current_prl_tx_state(port); - - /* - * Discard queued message - * Note: We differ from spec here, which allows us to not discard on - * incoming SOP' or SOP''. However this would get the TCH out of sync. - * - * prl_tx will be set to this state following message reception in - * prl_rx. So this path will be entered following each rx message. If - * this state is entered, and there is either a message from the PE - * pending, or if a message was passed to the phy and there is either no - * response yet, or it was discarded in the phy layer, then a tx message - * discard event has been detected. - */ - if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT) || - prl_tx[port].xmit_status == TCPC_TX_WAIT || - prl_tx[port].xmit_status == TCPC_TX_COMPLETE_DISCARDED) { - PRL_TX_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); - increment_msgid_counter(port); - pe_report_discard(port); - } - - set_state_prl_tx(port, PRL_TX_PHY_LAYER_RESET); -} - -#ifdef CONFIG_USB_PD_REV30 -/* - * PrlTxSrcSourceTx - */ -static void prl_tx_src_source_tx_entry(const int port) -{ - print_current_prl_tx_state(port); - - /* Set Rp = SinkTxNG */ - typec_select_src_collision_rp(port, SINK_TX_NG); - typec_update_cc(port); -} - -static void prl_tx_src_source_tx_run(const int port) -{ - if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { - /* - * Don't clear pending XMIT flag here. Wait until we send so - * we can detect if we dropped this message or not. - */ - set_state_prl_tx(port, PRL_TX_SRC_PENDING); - } -} - -/* - * PrlTxSnkStartAms - */ -static void prl_tx_snk_start_ams_entry(const int port) -{ - print_current_prl_tx_state(port); -} - -static void prl_tx_snk_start_ams_run(const int port) -{ - if (PRL_TX_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { - /* - * Don't clear pending XMIT flag here. Wait until we send so - * we can detect if we dropped this message or not. - */ - set_state_prl_tx(port, PRL_TX_SNK_PENDING); - } -} -#endif /* CONFIG_USB_PD_REV30 */ - -/* - * PrlTxLayerResetForTransmit - */ -static void prl_tx_layer_reset_for_transmit_entry(const int port) -{ - print_current_prl_tx_state(port); - - if (pdmsg[port].xmit_type < NUM_SOP_STAR_TYPES) { - /* - * This state is only used during soft resets. Reset only the - * matching message type. - * - * From section 6.3.13 Soft Reset Message in the USB PD 3.0 - * v2.0 spec, Soft_Reset Message Shall be targeted at a - * specific entity depending on the type of SOP* Packet used. - */ - prl_tx[port].msg_id_counter[pdmsg[port].xmit_type] = 0; - - /* - * From section 6.11.2.3.2, the MessageID should be cleared - * from the PRL_Rx_Layer_Reset_for_Receive state. However, we - * don't implement a full state machine for PRL RX states so - * clear the MessageID here. - */ - prl_rx[port].msg_id[pdmsg[port].xmit_type] = -1; - } -} - -static void prl_tx_layer_reset_for_transmit_run(const int port) -{ - /* NOTE: PRL_Tx_Construct_Message State embedded here */ - prl_tx_construct_message(port); - set_state_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); -} - -static uint32_t get_sop_star_header(const int port) -{ - const int is_sop_packet = pdmsg[port].xmit_type == TCPCI_MSG_SOP; - int ext; - -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - ext = pdmsg[port].ext; -#else - ext = 0; -#endif - - /* SOP vs SOP'/SOP" headers are different. Replace fields as needed */ - return PD_HEADER( - pdmsg[port].msg_type, - is_sop_packet ? - pd_get_power_role(port) : tc_get_cable_plug(port), - is_sop_packet ? - pd_get_data_role(port) : 0, - prl_tx[port].msg_id_counter[pdmsg[port].xmit_type], - pdmsg[port].data_objs, - pdmsg[port].rev[pdmsg[port].xmit_type], - ext); -} - -static void prl_tx_construct_message(const int port) -{ - /* The header is unused for hard reset, etc. */ - const uint32_t header = pdmsg[port].xmit_type < NUM_SOP_STAR_TYPES ? - get_sop_star_header(port) : 0; - - /* Save SOP* so the correct msg_id_counter can be incremented */ - prl_tx[port].last_xmit_type = pdmsg[port].xmit_type; - - /* Indicate that a tx message is being passed to the phy layer */ - prl_tx[port].xmit_status = TCPC_TX_WAIT; - /* - * PRL_FLAGS_TX_COMPLETE could be set if this function is called before - * the Policy Engine is informed of the previous transmission. Clear the - * flag so that this message can be sent. - */ - PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_COMPLETE); - - /* - * Pass message to PHY Layer. It handles retries in hardware as the EC - * cannot handle the required timing ~ 1ms (tReceive + tRetry). - * - * Note if we ever start sending large, extendend messages, then we - * should not retry those messages. We do not support that and probably - * never will (since we support chunking). - */ - tcpm_transmit(port, pdmsg[port].xmit_type, header, - pdmsg[port].tx_chk_buf); -} - -/* - * PrlTxWaitForPhyResponse - */ -static void prl_tx_wait_for_phy_response_entry(const int port) -{ - print_current_prl_tx_state(port); - - pd_timer_enable(port, PR_TIMER_TCPC_TX_TIMEOUT, PD_T_TCPC_TX_TIMEOUT); -} - -static void prl_tx_wait_for_phy_response_run(const int port) -{ - /* Wait until TX is complete */ - - /* - * NOTE: The TCPC will set xmit_status to TCPC_TX_COMPLETE_DISCARDED - * when a GoodCRC containing an incorrect MessageID is received. - * This condition satisfies the PRL_Tx_Match_MessageID state - * requirement. - */ - - if (prl_tx[port].xmit_status == TCPC_TX_COMPLETE_SUCCESS) { - /* NOTE: PRL_TX_Message_Sent State embedded here. */ - /* Increment messageId counter */ - increment_msgid_counter(port); - - /* Inform Policy Engine Message was sent */ - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) - PDMSG_SET_FLAG(port, PRL_FLAGS_TX_COMPLETE); - else - pe_message_sent(port); - - /* - * This event reduces the time of informing the policy engine of - * the transmission by one state machine cycle - */ - task_wake(PD_PORT_TO_TASK_ID(port)); - set_state_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); - } else if (pd_timer_is_expired(port, PR_TIMER_TCPC_TX_TIMEOUT) || - prl_tx[port].xmit_status == TCPC_TX_COMPLETE_FAILED) { - /* - * NOTE: PRL_Tx_Transmission_Error State embedded - * here. - */ - - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { - /* - * State tch_wait_for_transmission_complete will - * inform policy engine of error - */ - PDMSG_SET_FLAG(port, PRL_FLAGS_TX_ERROR); - } else { - /* Report Error To Policy Engine */ - pe_report_error(port, ERR_TCH_XMIT, - prl_tx[port].last_xmit_type); - } - - /* Increment message id counter */ - increment_msgid_counter(port); - set_state_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); - } -} - -static void prl_tx_wait_for_phy_response_exit(const int port) -{ - pd_timer_disable(port, PR_TIMER_TCPC_TX_TIMEOUT); -} - -/* Source Protocol Layer Message Transmission */ -/* - * PrlTxSrcPending - */ -static void prl_tx_src_pending_entry(const int port) -{ - print_current_prl_tx_state(port); - - /* Start SinkTxTimer */ - pd_timer_enable(port, PR_TIMER_SINK_TX, PD_T_SINK_TX); -} - -static void prl_tx_src_pending_run(const int port) -{ - if (pd_timer_is_expired(port, PR_TIMER_SINK_TX)) { - /* - * We clear the pending XMIT flag here right before we send so - * we can detect if we discarded this message or not - */ - PRL_TX_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); - - /* - * Soft Reset Message pending & - * SinkTxTimer timeout - */ - if ((tx_emsg[port].len == 0) && - (pdmsg[port].msg_type == PD_CTRL_SOFT_RESET)) { - set_state_prl_tx(port, PRL_TX_LAYER_RESET_FOR_TRANSMIT); - } - /* Message pending (except Soft Reset) & - * SinkTxTimer timeout - */ - else { - prl_tx_construct_message(port); - set_state_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); - } - - return; - } -} - -static void prl_tx_src_pending_exit(int port) -{ - pd_timer_disable(port, PR_TIMER_SINK_TX); -} - -/* - * PrlTxSnkPending - */ -static void prl_tx_snk_pending_entry(const int port) -{ - print_current_prl_tx_state(port); -} - -static void prl_tx_snk_pending_run(const int port) -{ - bool start_tx = false; - - /* - * Wait unit the SRC applies SINK_TX_OK so we can transmit. In FRS mode, - * don't wait for SINK_TX_OK since either the source (and Rp) could be - * gone or the TCPC CC_STATUS update time could be too long to meet - * tFRSwapInit. - */ - if (pe_in_frs_mode(port)) { - /* shortcut to save some i2c_xfer calls on the FRS path. */ - start_tx = true; - } else { - enum tcpc_cc_voltage_status cc1, cc2; - - tcpm_get_cc(port, &cc1, &cc2); - start_tx = (cc1 == TYPEC_CC_VOLT_RP_3_0 || - cc2 == TYPEC_CC_VOLT_RP_3_0); - } - if (start_tx) { - /* - * We clear the pending XMIT flag here right before we send so - * we can detect if we discarded this message or not - */ - PRL_TX_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); - - /* - * Soft Reset Message Message pending & - * Rp = SinkTxOk - */ - if ((pdmsg[port].msg_type == PD_CTRL_SOFT_RESET) && - (tx_emsg[port].len == 0)) { - set_state_prl_tx(port, PRL_TX_LAYER_RESET_FOR_TRANSMIT); - } - /* - * Message pending (except Soft Reset) & - * Rp = SinkTxOk - */ - else { - prl_tx_construct_message(port); - set_state_prl_tx(port, PRL_TX_WAIT_FOR_PHY_RESPONSE); - } - return; - } -} - -/* Hard Reset Operation */ -void prl_hr_send_msg_to_phy(const int port) -{ - /* Header is not used for hard reset */ - const uint32_t header = 0; - - pdmsg[port].xmit_type = TCPCI_MSG_TX_HARD_RESET; - - /* - * These flags could be set if this function is called before the - * Policy Engine is informed of the previous transmission. Clear the - * flags so that this message can be sent. - */ - prl_tx[port].xmit_status = TCPC_TX_UNSET; - PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_COMPLETE); - - /* Pass message to PHY Layer */ - tcpm_transmit(port, pdmsg[port].xmit_type, header, - pdmsg[port].tx_chk_buf); -} - -static void prl_hr_wait_for_request_entry(const int port) -{ - print_current_prl_hr_state(port); - - prl_hr[port].flags = 0; -} - -static void prl_hr_wait_for_request_run(const int port) -{ - if (PRL_HR_CHK_FLAG(port, PRL_FLAGS_PE_HARD_RESET | - PRL_FLAGS_PORT_PARTNER_HARD_RESET)) - set_state_prl_hr(port, PRL_HR_RESET_LAYER); -} - -/* - * PrlHrResetLayer - */ -static void prl_hr_reset_layer_entry(const int port) -{ - int i; - - print_current_prl_hr_state(port); - - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { - tch[port].flags = 0; - rch[port].flags = 0; - } - - pdmsg[port].flags = 0; - - /* Hard reset resets messageIDCounters for all TX types */ - for (i = 0; i < NUM_SOP_STAR_TYPES; i++) { - prl_rx[port].msg_id[i] = -1; - prl_tx[port].msg_id_counter[i] = 0; - } - - /* Disable RX */ - if (IS_ENABLED(CONFIG_USB_CTVPD) || - IS_ENABLED(CONFIG_USB_VPD)) - vpd_rx_enable(0); - else - tcpm_set_rx_enable(port, 0); - - /* - * PD r3.0 v2.0, ss6.2.1.1.5: - * After a physical or logical (USB Type-C Error Recovery) Attach, a - * Port discovers the common Specification Revision level between itself - * and its Port Partner and/or the Cable Plug(s), and uses this - * Specification Revision level until a Detach, Hard Reset or Error - * Recovery happens. - * - * This covers the Hard Reset case. - */ - prl_set_default_pd_revision(port); - - /* Inform the AP of Hard Reset */ - if (IS_ENABLED(CONFIG_USB_PD_HOST_CMD)) - pd_notify_event(port, PD_STATUS_EVENT_HARD_RESET); - - /* - * Protocol Layer message transmission transitions to - * PRL_Tx_Wait_For_Message_Request state. - */ - set_state_prl_tx(port, PRL_TX_WAIT_FOR_MESSAGE_REQUEST); - - return; -} - -static void prl_hr_reset_layer_run(const int port) -{ - /* - * Protocol Layer reset Complete & - * Hard Reset was initiated by Policy Engine - */ - if (PRL_HR_CHK_FLAG(port, PRL_FLAGS_PE_HARD_RESET)) { - /* - * Request PHY to perform a Hard Reset. Note - * PRL_HR_Request_Reset state is embedded here. - */ - prl_hr_send_msg_to_phy(port); - set_state_prl_hr(port, PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE); - } - /* - * Protocol Layer reset complete & - * Hard Reset was initiated by Port Partner - */ - else { - /* Inform Policy Engine of the Hard Reset */ - pe_got_hard_reset(port); - set_state_prl_hr(port, PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE); - } -} - -/* - * PrlHrWaitForPhyHardResetComplete - */ -static void prl_hr_wait_for_phy_hard_reset_complete_entry(const int port) -{ - print_current_prl_hr_state(port); - - /* Start HardResetCompleteTimer */ - pd_timer_enable(port, PR_TIMER_HARD_RESET_COMPLETE, - PD_T_PS_HARD_RESET); -} - -static void prl_hr_wait_for_phy_hard_reset_complete_run(const int port) -{ - /* - * Wait for hard reset from PHY - * or timeout - */ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_COMPLETE) || - pd_timer_is_expired(port, PR_TIMER_HARD_RESET_COMPLETE)) { - /* PRL_HR_PHY_Hard_Reset_Requested */ - - /* Inform Policy Engine Hard Reset was sent */ - pe_hard_reset_sent(port); - set_state_prl_hr(port, PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE); - - return; - } -} - -static void prl_hr_wait_for_phy_hard_reset_complete_exit(int port) -{ - pd_timer_disable(port, PR_TIMER_HARD_RESET_COMPLETE); -} - -/* - * PrlHrWaitForPeHardResetComplete - */ -static void prl_hr_wait_for_pe_hard_reset_complete_entry(const int port) -{ - print_current_prl_hr_state(port); -} - -static void prl_hr_wait_for_pe_hard_reset_complete_run(const int port) -{ - /* - * Wait for Hard Reset complete indication from Policy Engine - */ - if (PRL_HR_CHK_FLAG(port, PRL_FLAGS_HARD_RESET_COMPLETE)) - set_state_prl_hr(port, PRL_HR_WAIT_FOR_REQUEST); -} - -static void prl_hr_wait_for_pe_hard_reset_complete_exit(const int port) -{ - /* Exit from Hard Reset */ - - set_state_prl_tx(port, PRL_TX_PHY_LAYER_RESET); - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { - set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); - set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); - } -} - -static void copy_chunk_to_ext(int port) -{ - /* Calculate number of bytes */ - pdmsg[port].num_bytes_received = - (PD_HEADER_CNT(rx_emsg[port].header) * 4); - - /* Copy chunk into extended message */ - memcpy((uint8_t *)rx_emsg[port].buf, (uint8_t *)pdmsg[port].rx_chk_buf, - pdmsg[port].num_bytes_received); - - /* Set extended message length */ - rx_emsg[port].len = pdmsg[port].num_bytes_received; -} - -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES -/* - * Chunked Rx State Machine - */ -/* - * RchWaitForMessageFromProtocolLayer - */ -static void rch_wait_for_message_from_protocol_layer_entry(const int port) -{ - print_current_rch_state(port); - - /* Clear Abort flag */ - PDMSG_CLR_FLAG(port, PRL_FLAGS_ABORT); - - /* All Messages are chunked */ - rch[port].flags = PRL_FLAGS_CHUNKING; -} - -static void rch_wait_for_message_from_protocol_layer_run(const int port) -{ - if (RCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - RCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - /* - * Are we communicating with a PD3.0 device and is - * this an extended message? - */ - if (pdmsg_xmit_type_is_rev30(port) - && PD_HEADER_EXT(rx_emsg[port].header)) { - uint16_t exhdr = - GET_EXT_HEADER(*pdmsg[port].rx_chk_buf); - uint8_t chunked = PD_EXT_HEADER_CHUNKED(exhdr); - - /* - * Received Extended Message & - * (Chunking = 1 & Chunked = 1) - */ - if ((RCH_CHK_FLAG(port, PRL_FLAGS_CHUNKING)) && - chunked) { - /* - * RCH_Processing_Extended_Message first chunk - * entry processing embedded here - * - * This is the first chunk: - * Set Chunk_number_expected = 0 and - * Num_Bytes_Received = 0 - */ - pdmsg[port].chunk_number_expected = 0; - pdmsg[port].num_bytes_received = 0; - pdmsg[port].msg_type = - PD_HEADER_TYPE(rx_emsg[port].header); - - set_state_rch(port, - RCH_PROCESSING_EXTENDED_MESSAGE); - } - /* - * (Received Extended Message & - * (Chunking = 0 & Chunked = 0)) - */ - else if (!RCH_CHK_FLAG(port, PRL_FLAGS_CHUNKING) && - !chunked) { - /* Copy chunk to extended buffer */ - copy_chunk_to_ext(port); - set_state_rch(port, RCH_PASS_UP_MESSAGE); - } - /* - * Chunked != Chunking - */ - else { - rch[port].error = ERR_RCH_CHUNKED; - set_state_rch(port, RCH_REPORT_ERROR); - } - } - /* - * Received Non-Extended Message - */ - else if (!PD_HEADER_EXT(rx_emsg[port].header)) { - /* Copy chunk to extended buffer */ - copy_chunk_to_ext(port); - set_state_rch(port, RCH_PASS_UP_MESSAGE); - } - /* - * Received an Extended Message while communicating at a - * revision lower than PD3.0 - */ - else { - rch[port].error = ERR_RCH_CHUNKED; - set_state_rch(port, RCH_REPORT_ERROR); - } - } -} - -/* - * RchPassUpMessage - */ -static void rch_pass_up_message_entry(const int port) -{ - print_current_rch_state(port); - - /* Pass Message to Policy Engine */ - pe_message_received(port); - set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); -} - -/* - * RchProcessingExtendedMessage - */ -static void rch_processing_extended_message_entry(const int port) -{ - print_current_rch_state(port); -} - -static void rch_processing_extended_message_run(const int port) -{ - uint16_t exhdr = GET_EXT_HEADER(pdmsg[port].rx_chk_buf[0]); - uint8_t chunk_num = PD_EXT_HEADER_CHUNK_NUM(exhdr); - uint32_t data_size = PD_EXT_HEADER_DATA_SIZE(exhdr); - uint32_t byte_num; - - /* - * Abort Flag Set - */ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_ABORT)) - set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); - - /* - * If expected Chunk Number: - * Append data to Extended_Message_Buffer - * Increment Chunk_number_Expected - * Adjust Num Bytes Received - */ - else if (chunk_num == pdmsg[port].chunk_number_expected) { - byte_num = data_size - pdmsg[port].num_bytes_received; - - if (byte_num >= PD_MAX_EXTENDED_MSG_CHUNK_LEN) - byte_num = PD_MAX_EXTENDED_MSG_CHUNK_LEN; - - /* Make sure extended message buffer does not overflow */ - if (pdmsg[port].num_bytes_received + - byte_num > EXTENDED_BUFFER_SIZE) { - rch[port].error = ERR_RCH_CHUNKED; - set_state_rch(port, RCH_REPORT_ERROR); - return; - } - - /* Append data */ - /* Add 2 to chk_buf to skip over extended message header */ - memcpy(((uint8_t *)rx_emsg[port].buf + - pdmsg[port].num_bytes_received), - (uint8_t *)pdmsg[port].rx_chk_buf + 2, - byte_num); - /* increment chunk number expected */ - pdmsg[port].chunk_number_expected++; - /* adjust num bytes received */ - pdmsg[port].num_bytes_received += byte_num; - - /* Was that the last chunk? */ - if (pdmsg[port].num_bytes_received >= data_size) { - rx_emsg[port].len = pdmsg[port].num_bytes_received; - /* Pass Message to Policy Engine */ - set_state_rch(port, RCH_PASS_UP_MESSAGE); - } - /* - * Message not Complete - */ - else - set_state_rch(port, RCH_REQUESTING_CHUNK); - } - /* - * Unexpected Chunk Number - */ - else { - rch[port].error = ERR_RCH_CHUNKED; - set_state_rch(port, RCH_REPORT_ERROR); - } -} - -/* - * RchRequestingChunk - */ -static void rch_requesting_chunk_entry(const int port) -{ - print_current_rch_state(port); - - /* - * Send Chunk Request to Protocol Layer - * with chunk number = Chunk_Number_Expected - */ - pdmsg[port].tx_chk_buf[0] = PD_EXT_HEADER( - pdmsg[port].chunk_number_expected, - 1, /* Request Chunk */ - 0 /* Data Size */ - ); - - pdmsg[port].data_objs = 1; - pdmsg[port].ext = 1; - pdmsg[port].xmit_type = prl_rx[port].sop; - PRL_TX_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_TX); -} - -static void rch_requesting_chunk_run(const int port) -{ - /* - * Message Transmitted received from Protocol Layer - */ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_COMPLETE)) { - PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_COMPLETE); - set_state_rch(port, RCH_WAITING_CHUNK); - } else if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_ERROR)) { - /* Transmission Error from Protocol Layer detetected */ - rch[port].error = ERR_RCH_CHUNKED; - set_state_rch(port, RCH_REPORT_ERROR); - } else if (RCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - /* - * It is possible to have both message received and the chunk - * request transmit complete before a full PRL SM run. But, the - * PRL_RX state machine runs prior to RCH, but before PRL_TX, so - * PRL_FLAGS_MSG_RECEIVED can be set without - * PRL_FLAGS_TX_COMPLETE set at this point (though it will be - * set as soon as PRL_TX is executed next. - */ - set_state_rch(port, RCH_WAITING_CHUNK); - } -} - -/* - * RchWaitingChunk - */ -static void rch_waiting_chunk_entry(const int port) -{ - print_current_rch_state(port); - - /* - * Start ChunkSenderResponseTimer - */ - pd_timer_enable(port, PR_TIMER_CHUNK_SENDER_RESPONSE, - PD_T_CHUNK_SENDER_RESPONSE); -} - -static void rch_waiting_chunk_run(const int port) -{ - if (RCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - /* - * Because of the 5 msec tick time, it is possible to have both - * msg_received and tx_complete flags set for a given PRL sm - * run. Since prl_rx runs prior to the tx state machines, clear - * the tx_complete flag as the next chunk has already been - * received. - */ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_COMPLETE)) - PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_COMPLETE); - - /* - * Leave PRL_FLAGS_MSG_RECEIVED flag set just in case an error - * is detected. If an error is detected, PRL_FLAGS_MSG_RECEIVED - * will be cleared in rch_report_error state. - */ - - if (PD_HEADER_EXT(rx_emsg[port].header)) { - uint16_t exhdr = - GET_EXT_HEADER(pdmsg[port].rx_chk_buf[0]); - /* - * Other Message Received from Protocol Layer - */ - if (PD_EXT_HEADER_REQ_CHUNK(exhdr) || - !PD_EXT_HEADER_CHUNKED(exhdr)) { - rch[port].error = ERR_RCH_CHUNKED; - set_state_rch(port, RCH_REPORT_ERROR); - } - /* - * Chunk response Received from Protocol Layer - */ - else { - /* - * No error was detected, so clear - * PRL_FLAGS_MSG_RECEIVED flag. - */ - RCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - set_state_rch(port, - RCH_PROCESSING_EXTENDED_MESSAGE); - } - } - } - /* - * ChunkSenderResponseTimer Timeout - */ - else if (pd_timer_is_expired(port, PR_TIMER_CHUNK_SENDER_RESPONSE)) { - rch[port].error = ERR_RCH_CHUNK_WAIT_TIMEOUT; - set_state_rch(port, RCH_REPORT_ERROR); - } -} - -static void rch_waiting_chunk_exit(int port) -{ - pd_timer_disable(port, PR_TIMER_CHUNK_SENDER_RESPONSE); -} - -/* - * RchReportError - */ -static void rch_report_error_entry(const int port) -{ - print_current_rch_state(port); - - /* - * If the state was entered because a message was received, - * this message is passed to the Policy Engine. - */ - if (RCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - RCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - - /* Copy chunk to extended buffer */ - copy_chunk_to_ext(port); - /* Pass Message to Policy Engine */ - pe_message_received(port); - /* Report error */ - pe_report_error(port, ERR_RCH_MSG_REC, prl_rx[port].sop); - } else { - pe_report_error(port, rch[port].error, prl_rx[port].sop); - } -} - -static void rch_report_error_run(const int port) -{ - set_state_rch(port, RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); -} - -/* - * Chunked Tx State Machine - */ - -/* - * TchWaitForMessageRequestFromPe - */ -static void tch_wait_for_message_request_from_pe_entry(const int port) -{ - print_current_tch_state(port); - - /* Clear Abort flag */ - PDMSG_CLR_FLAG(port, PRL_FLAGS_ABORT); - - /* All Messages are chunked */ - tch[port].flags = PRL_FLAGS_CHUNKING; -} - -static void tch_wait_for_message_request_from_pe_run(const int port) -{ - /* - * Any message received and not in state TCH_Wait_Chunk_Request - */ - if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - set_state_tch(port, TCH_MESSAGE_RECEIVED); - } else if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); - /* - * Rx Chunking State != RCH_Wait_For_Message_From_Protocol_Layer - * & Abort Supported - * - * Discard the Message - */ - if (rch_get_state(port) != - RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER) { - tch[port].error = ERR_TCH_XMIT; - set_state_tch(port, TCH_REPORT_ERROR); - } else { - /* - * Extended Message Request & Chunking - */ - if (pdmsg_xmit_type_is_rev30(port) - && pdmsg[port].ext - && TCH_CHK_FLAG(port, PRL_FLAGS_CHUNKING)) { - /* - * NOTE: TCH_Prepare_To_Send_Chunked_Message - * embedded here. - */ - pdmsg[port].send_offset = 0; - pdmsg[port].chunk_number_to_send = 0; - set_state_tch(port, - TCH_CONSTRUCT_CHUNKED_MESSAGE); - } else - /* - * Non-Extended Message Request - */ - { - /* NOTE: TCH_Pass_Down_Message embedded here */ - prl_copy_msg_to_buffer(port); - - /* Pass Message to Protocol Layer */ - PRL_TX_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); - set_state_tch(port, - TCH_WAIT_FOR_TRANSMISSION_COMPLETE); - } - } - } -} - -/* - * TchWaitForTransmissionComplete - */ -static void tch_wait_for_transmission_complete_entry(const int port) -{ - print_current_tch_state(port); -} - -static void tch_wait_for_transmission_complete_run(const int port) -{ - /* - * Inform Policy Engine that Message was sent. - */ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_COMPLETE)) { - PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_COMPLETE); - set_state_tch(port, TCH_MESSAGE_SENT); - return; - } - /* - * Inform Policy Engine of Tx Error - */ - else if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_ERROR)) { - PDMSG_CLR_FLAG(port, PRL_FLAGS_TX_ERROR); - tch[port].error = ERR_TCH_XMIT; - set_state_tch(port, TCH_REPORT_ERROR); - return; - } - /* - * A message was received while TCH is waiting for the phy to complete - * sending a tx message. - * - * Because of our prl_sm architecture and I2C access delays for TCPCs, - * it's possible to have a message received and the prl_tx state not be - * in its default waiting state. To avoid a false protocol error, only - * jump to TCH_MESSAGE_RECEIVED if the phy layer has not indicated that - * the tx message was sent successfully. - */ - if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED) && - prl_tx[port].xmit_status != TCPC_TX_COMPLETE_SUCCESS) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - set_state_tch(port, TCH_MESSAGE_RECEIVED); - return; - } -} - -/* - * TchConstructChunkedMessage - */ -static void tch_construct_chunked_message_entry(const int port) -{ - uint16_t *ext_hdr; - uint8_t *data; - uint16_t num; - - print_current_tch_state(port); - - /* - * Any message received and not in state TCH_Wait_Chunk_Request - */ - if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - set_state_tch(port, TCH_MESSAGE_RECEIVED); - return; - } - - /* Prepare to copy chunk into chk_buf */ - - ext_hdr = (uint16_t *)pdmsg[port].tx_chk_buf; - data = ((uint8_t *)pdmsg[port].tx_chk_buf + 2); - num = tx_emsg[port].len - pdmsg[port].send_offset; - - if (num > PD_MAX_EXTENDED_MSG_CHUNK_LEN) - num = PD_MAX_EXTENDED_MSG_CHUNK_LEN; - - /* Set the chunks extended header */ - *ext_hdr = PD_EXT_HEADER(pdmsg[port].chunk_number_to_send, - 0, /* Chunk Request */ - tx_emsg[port].len); - - /* Copy the message chunk into chk_buf */ - memset(data, 0, 28); - memcpy(data, tx_emsg[port].buf + pdmsg[port].send_offset, num); - pdmsg[port].send_offset += num; - - /* - * Add in 2 bytes for extended header - * pad out to 4-byte boundary - * convert to number of 4-byte words - * Since the value is shifted right by 2, - * no need to explicitly clear the lower - * 2-bits. - */ - pdmsg[port].data_objs = (num + 2 + 3) >> 2; - - /* Pass message chunk to Protocol Layer */ - PRL_TX_SET_FLAG(port, PRL_FLAGS_MSG_XMIT); -} - -static void tch_construct_chunked_message_run(const int port) -{ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_ABORT)) - set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); - else - set_state_tch(port, TCH_SENDING_CHUNKED_MESSAGE); -} - -/* - * TchSendingChunkedMessage - */ -static void tch_sending_chunked_message_entry(const int port) -{ - print_current_tch_state(port); -} - -static void tch_sending_chunked_message_run(const int port) -{ - /* - * Transmission Error - */ - if (PDMSG_CHK_FLAG(port, PRL_FLAGS_TX_ERROR)) { - tch[port].error = ERR_TCH_XMIT; - set_state_tch(port, TCH_REPORT_ERROR); - } - /* - * Message Transmitted from Protocol Layer & - * Last Chunk - */ - else if (tx_emsg[port].len == pdmsg[port].send_offset) - set_state_tch(port, TCH_MESSAGE_SENT); - /* - * Any message received and not in state TCH_Wait_Chunk_Request - */ - else if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - set_state_tch(port, TCH_MESSAGE_RECEIVED); - } - /* - * Message Transmitted from Protocol Layer & - * Not Last Chunk - */ - else - set_state_tch(port, TCH_WAIT_CHUNK_REQUEST); -} - -/* - * TchWaitChunkRequest - */ -static void tch_wait_chunk_request_entry(const int port) -{ - print_current_tch_state(port); - - /* Increment Chunk Number to Send */ - pdmsg[port].chunk_number_to_send++; - /* Start Chunk Sender Request Timer */ - pd_timer_enable(port, PR_TIMER_CHUNK_SENDER_REQUEST, - PD_T_CHUNK_SENDER_REQUEST); -} - -static void tch_wait_chunk_request_run(const int port) -{ - if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - - if (PD_HEADER_EXT(rx_emsg[port].header)) { - uint16_t exthdr; - - exthdr = GET_EXT_HEADER(pdmsg[port].rx_chk_buf[0]); - if (PD_EXT_HEADER_REQ_CHUNK(exthdr)) { - /* - * Chunk Request Received & - * Chunk Number = Chunk Number to Send - */ - if (PD_EXT_HEADER_CHUNK_NUM(exthdr) == - pdmsg[port].chunk_number_to_send) { - set_state_tch(port, - TCH_CONSTRUCT_CHUNKED_MESSAGE); - } - /* - * Chunk Request Received & - * Chunk Number != Chunk Number to Send - */ - else { - tch[port].error = ERR_TCH_CHUNKED; - set_state_tch(port, TCH_REPORT_ERROR); - } - return; - } - } - - /* - * Other message received - */ - set_state_tch(port, TCH_MESSAGE_RECEIVED); - } - /* - * ChunkSenderRequestTimer timeout - */ - else if (pd_timer_is_expired(port, PR_TIMER_CHUNK_SENDER_REQUEST)) - set_state_tch(port, TCH_MESSAGE_SENT); -} - -static void tch_wait_chunk_request_exit(int port) -{ - pd_timer_disable(port, PR_TIMER_CHUNK_SENDER_REQUEST); -} - -/* - * TchMessageReceived - */ -static void tch_message_received_entry(const int port) -{ - print_current_tch_state(port); - - /* Pass message to chunked Rx */ - RCH_SET_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - - /* Clear extended message objects */ - if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_XMIT); - pe_report_discard(port); - } - pdmsg[port].data_objs = 0; -} - -static void tch_message_received_run(const int port) -{ - set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); -} - -/* - * TchMessageSent - */ -static void tch_message_sent_entry(const int port) -{ - print_current_tch_state(port); - - /* Tell PE message was sent */ - pe_message_sent(port); - - /* - * Any message received and not in state TCH_Wait_Chunk_Request - * MUST be checked after notifying PE - */ - if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - set_state_tch(port, TCH_MESSAGE_RECEIVED); - return; - } - - - - set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); -} - -/* - * TchReportError - */ -static void tch_report_error_entry(const int port) -{ - print_current_tch_state(port); - - /* Report Error To Policy Engine */ - pe_report_error(port, tch[port].error, prl_tx[port].last_xmit_type); - - /* - * Any message received and not in state TCH_Wait_Chunk_Request - * MUST be checked after notifying PE - */ - if (TCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) { - TCH_CLR_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - set_state_tch(port, TCH_MESSAGE_RECEIVED); - return; - } - - - set_state_tch(port, TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); -} -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ - -/* - * Protocol Layer Message Reception State Machine - */ -static void prl_rx_wait_for_phy_message(const int port, int evt) -{ - uint32_t header; - uint8_t type; - uint8_t cnt; - int8_t msid; - - /* - * If PD3, wait for the RX chunk SM to copy the pdmsg into the extended - * buffer before overwriting pdmsg. - */ - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES) && - RCH_CHK_FLAG(port, PRL_FLAGS_MSG_RECEIVED)) - return; - - /* If we don't have any message, just stop processing now. */ - if (!tcpm_has_pending_message(port) || - tcpm_dequeue_message(port, pdmsg[port].rx_chk_buf, &header)) - return; - - rx_emsg[port].header = header; - type = PD_HEADER_TYPE(header); - cnt = PD_HEADER_CNT(header); - msid = PD_HEADER_ID(header); - prl_rx[port].sop = PD_HEADER_GET_SOP(header); - - /* Make sure an incorrect count doesn't overflow the chunk buffer */ - if (cnt > CHK_BUF_SIZE) - cnt = CHK_BUF_SIZE; - - /* dump received packet content (only dump ping at debug level MAX) */ - if ((prl_debug_level >= DEBUG_LEVEL_2 && type != PD_CTRL_PING) || - prl_debug_level >= DEBUG_LEVEL_3) { - int p; - - ccprintf("C%d: RECV %04x/%d ", port, header, cnt); - for (p = 0; p < cnt; p++) - ccprintf("[%d]%08x ", p, pdmsg[port].rx_chk_buf[p]); - ccprintf("\n"); - } - - /* - * Ignore messages sent to the cable from our - * port partner if we aren't Vconn powered device. - */ - if (!IS_ENABLED(CONFIG_USB_CTVPD) && - !IS_ENABLED(CONFIG_USB_VPD) && - PD_HEADER_GET_SOP(header) != TCPCI_MSG_SOP && - PD_HEADER_PROLE(header) == PD_PLUG_FROM_DFP_UFP) - return; - - /* Handle incoming soft reset as special case */ - if (cnt == 0 && type == PD_CTRL_SOFT_RESET) { - /* Clear MessageIdCounter */ - prl_tx[port].msg_id_counter[prl_rx[port].sop] = 0; - /* Clear stored MessageID value */ - prl_rx[port].msg_id[prl_rx[port].sop] = -1; - - /* Soft Reset occurred */ - set_state_prl_tx(port, PRL_TX_PHY_LAYER_RESET); - - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { - set_state_rch(port, - RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); - set_state_tch(port, - TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); - } - - /* - * Inform Policy Engine of Soft Reset. Note perform this after - * performing the protocol layer reset, otherwise we will lose - * the PE's outgoing ACCEPT message to the soft reset. - */ - pe_got_soft_reset(port); - - return; - } - - /* - * Ignore if this is a duplicate message. Stop processing. - */ - if (prl_rx[port].msg_id[prl_rx[port].sop] == msid) - return; - - /* - * Discard any pending tx message if this is - * not a ping message (length must be checked to verify this is a - * control message, rather than data) - */ - if ((cnt > 0) || (type != PD_CTRL_PING)) { - /* - * Note: Spec dictates that we always go into - * PRL_Tx_Discard_Message upon receivng a message. However, due - * to our TCPC architecture we may be receiving a transmit - * complete at the same time as a response so only do this if a - * message is pending. - */ - if (prl_tx[port].xmit_status != TCPC_TX_COMPLETE_SUCCESS || - PRL_TX_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) - set_state_prl_tx(port, PRL_TX_DISCARD_MESSAGE); - } - - /* Store Message Id */ - prl_rx[port].msg_id[prl_rx[port].sop] = msid; - - if (IS_ENABLED(CONFIG_USB_PD_EXTENDED_MESSAGES)) { - /* RTR Chunked Message Router States. */ - /* - * Received Ping from Protocol Layer - */ - if (cnt == 0 && type == PD_CTRL_PING) { - /* NOTE: RTR_PING State embedded here. */ - rx_emsg[port].len = 0; - pe_message_received(port); - return; - } - /* - * Message (not Ping) Received from - * Protocol Layer & Doing Tx Chunks - * - * Also, handle the case where a message has been - * queued for sending but a message is received before - * tch_wait_for_message_request_from_pe has been run - */ - else if (tch_get_state(port) != - TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE || - TCH_CHK_FLAG(port, PRL_FLAGS_MSG_XMIT)) { - /* NOTE: RTR_TX_CHUNKS State embedded here. */ - /* - * Send Message to Tx Chunk - * Chunk State Machine - */ - TCH_SET_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - } - /* - * Message (not Ping) Received from - * Protocol Layer & Not Doing Tx Chunks - */ - else { - /* NOTE: RTR_RX_CHUNKS State embedded here. */ - /* - * Send Message to Rx - * Chunk State Machine - */ - RCH_SET_FLAG(port, PRL_FLAGS_MSG_RECEIVED); - } - } else { - /* Copy chunk to extended buffer */ - copy_chunk_to_ext(port); - /* Send message to Policy Engine */ - pe_message_received(port); - } - - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -/* All necessary Protocol Transmit States (Section 6.11.2.2) */ -static __const_data const struct usb_state prl_tx_states[] = { - [PRL_TX_PHY_LAYER_RESET] = { - .entry = prl_tx_phy_layer_reset_entry, - }, - [PRL_TX_WAIT_FOR_MESSAGE_REQUEST] = { - .entry = prl_tx_wait_for_message_request_entry, - .run = prl_tx_wait_for_message_request_run, - }, - [PRL_TX_LAYER_RESET_FOR_TRANSMIT] = { - .entry = prl_tx_layer_reset_for_transmit_entry, - .run = prl_tx_layer_reset_for_transmit_run, - }, - [PRL_TX_WAIT_FOR_PHY_RESPONSE] = { - .entry = prl_tx_wait_for_phy_response_entry, - .run = prl_tx_wait_for_phy_response_run, - .exit = prl_tx_wait_for_phy_response_exit, - }, -#ifdef CONFIG_USB_PD_REV30 - [PRL_TX_SRC_SOURCE_TX] = { - .entry = prl_tx_src_source_tx_entry, - .run = prl_tx_src_source_tx_run, - }, - [PRL_TX_SNK_START_AMS] = { - .entry = prl_tx_snk_start_ams_entry, - .run = prl_tx_snk_start_ams_run, - }, -#endif /* CONFIG_USB_PD_REV30 */ - [PRL_TX_SRC_PENDING] = { - .entry = prl_tx_src_pending_entry, - .run = prl_tx_src_pending_run, - .exit = prl_tx_src_pending_exit, - }, - [PRL_TX_SNK_PENDING] = { - .entry = prl_tx_snk_pending_entry, - .run = prl_tx_snk_pending_run, - }, - [PRL_TX_DISCARD_MESSAGE] = { - .entry = prl_tx_discard_message_entry, - }, -}; - -/* All necessary Protocol Hard Reset States (Section 6.11.2.4) */ -static __const_data const struct usb_state prl_hr_states[] = { - [PRL_HR_WAIT_FOR_REQUEST] = { - .entry = prl_hr_wait_for_request_entry, - .run = prl_hr_wait_for_request_run, - }, - [PRL_HR_RESET_LAYER] = { - .entry = prl_hr_reset_layer_entry, - .run = prl_hr_reset_layer_run, - }, - [PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE] = { - .entry = prl_hr_wait_for_phy_hard_reset_complete_entry, - .run = prl_hr_wait_for_phy_hard_reset_complete_run, - .exit = prl_hr_wait_for_phy_hard_reset_complete_exit, - }, - [PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE] = { - .entry = prl_hr_wait_for_pe_hard_reset_complete_entry, - .run = prl_hr_wait_for_pe_hard_reset_complete_run, - .exit = prl_hr_wait_for_pe_hard_reset_complete_exit, - }, -}; - -/* All necessary Chunked Rx states (Section 6.11.2.1.2) */ -__maybe_unused static const struct usb_state rch_states[] = { -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - [RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER] = { - .entry = rch_wait_for_message_from_protocol_layer_entry, - .run = rch_wait_for_message_from_protocol_layer_run, - }, - [RCH_PASS_UP_MESSAGE] = { - .entry = rch_pass_up_message_entry, - }, - [RCH_PROCESSING_EXTENDED_MESSAGE] = { - .entry = rch_processing_extended_message_entry, - .run = rch_processing_extended_message_run, - }, - [RCH_REQUESTING_CHUNK] = { - .entry = rch_requesting_chunk_entry, - .run = rch_requesting_chunk_run, - }, - [RCH_WAITING_CHUNK] = { - .entry = rch_waiting_chunk_entry, - .run = rch_waiting_chunk_run, - .exit = rch_waiting_chunk_exit, - }, - [RCH_REPORT_ERROR] = { - .entry = rch_report_error_entry, - .run = rch_report_error_run, - }, -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ -}; - -/* All necessary Chunked Tx states (Section 6.11.2.1.3) */ -__maybe_unused static const struct usb_state tch_states[] = { -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - [TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE] = { - .entry = tch_wait_for_message_request_from_pe_entry, - .run = tch_wait_for_message_request_from_pe_run, - }, - [TCH_WAIT_FOR_TRANSMISSION_COMPLETE] = { - .entry = tch_wait_for_transmission_complete_entry, - .run = tch_wait_for_transmission_complete_run, - }, - [TCH_CONSTRUCT_CHUNKED_MESSAGE] = { - .entry = tch_construct_chunked_message_entry, - .run = tch_construct_chunked_message_run, - }, - [TCH_SENDING_CHUNKED_MESSAGE] = { - .entry = tch_sending_chunked_message_entry, - .run = tch_sending_chunked_message_run, - }, - [TCH_WAIT_CHUNK_REQUEST] = { - .entry = tch_wait_chunk_request_entry, - .run = tch_wait_chunk_request_run, - .exit = tch_wait_chunk_request_exit, - }, - [TCH_MESSAGE_RECEIVED] = { - .entry = tch_message_received_entry, - .run = tch_message_received_run, - }, - [TCH_MESSAGE_SENT] = { - .entry = tch_message_sent_entry, - }, - [TCH_REPORT_ERROR] = { - .entry = tch_report_error_entry, - }, -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ -}; - -#ifdef TEST_BUILD - -const struct test_sm_data test_prl_sm_data[] = { - { - .base = prl_tx_states, - .size = ARRAY_SIZE(prl_tx_states), - .names = prl_tx_state_names, - .names_size = ARRAY_SIZE(prl_tx_state_names), - }, - { - .base = prl_hr_states, - .size = ARRAY_SIZE(prl_hr_states), - .names = prl_hr_state_names, - .names_size = ARRAY_SIZE(prl_hr_state_names), - }, -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES - { - .base = rch_states, - .size = ARRAY_SIZE(rch_states), - .names = rch_state_names, - .names_size = ARRAY_SIZE(rch_state_names), - }, - { - .base = tch_states, - .size = ARRAY_SIZE(tch_states), - .names = tch_state_names, - .names_size = ARRAY_SIZE(tch_state_names), - }, -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ -}; -BUILD_ASSERT(ARRAY_SIZE(prl_tx_states) == ARRAY_SIZE(prl_tx_state_names)); -BUILD_ASSERT(ARRAY_SIZE(prl_hr_states) == ARRAY_SIZE(prl_hr_state_names)); -#ifdef CONFIG_USB_PD_EXTENDED_MESSAGES -BUILD_ASSERT(ARRAY_SIZE(rch_states) == ARRAY_SIZE(rch_state_names)); -BUILD_ASSERT(ARRAY_SIZE(tch_states) == ARRAY_SIZE(tch_state_names)); -#endif /* CONFIG_USB_PD_EXTENDED_MESSAGES */ -const int test_prl_sm_data_size = ARRAY_SIZE(test_prl_sm_data); -#endif diff --git a/common/usbc/usb_retimer_fw_update.c b/common/usbc/usb_retimer_fw_update.c deleted file mode 100644 index 1ff198c78f..0000000000 --- a/common/usbc/usb_retimer_fw_update.c +++ /dev/null @@ -1,189 +0,0 @@ -/* Copyright 2021 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include <stdbool.h> -#include <stdint.h> -#include "compile_time_macros.h" -#include "console.h" -#include "hooks.h" -#include "timer.h" -#include "usb_common.h" -#include "usb_mux.h" -#include "usb_tc_sm.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#else -#define CPRINTS(format, args...) -#define CPRINTF(format, args...) -#endif - -/* - * Retimer firmware update is initiated by AP. - * The operations requested by AP are: - * 0 - USB_RETIMER_FW_UPDATE_QUERY_PORT - * 1 - USB_RETIMER_FW_UPDATE_SUSPEND_PD - * 2 - USB_RETIMER_FW_UPDATE_RESUME_PD - * 3 - USB_RETIMER_FW_UPDATE_GET_MUX - * 4 - USB_RETIMER_FW_UPDATE_SET_USB - * 5 - USB_RETIMER_FW_UPDATE_SET_SAFE - * 6 - USB_RETIMER_FW_UPDATE_SET_TBT - * 7 - USB_RETIMER_FW_UPDATE_DISCONNECT - * - * Operation 0 is processed immediately. - * Operations 1 to 7 are deferred and processed inside tc_run(). - * Operations 1/2/3 can be processed any time; while 4/5/6/7 have - * to be processed when PD task is suspended. - * Two TC flags are created for this situation. - * If Op 1/2/3 is received, TC_FLAGS_USB_RETIMER_FW_UPDATE_RUN - * is set, PD task will be waken up and process it. - * If 4/5/6/7 is received, TC_FLAGS_USB_RETIMER_FW_UPDATE_LTD_RUN is - * set, PD task should be in suspended mode and process it. - * - */ - -#define SUSPEND 1 -#define RESUME 0 - -/* Track current port AP requested to update retimer firmware */ -static int cur_port; -static int last_op; /* Operation received from AP via ACPI_WRITE */ -/* Operation result returned to ACPI_READ */ -static int last_result; - -int usb_retimer_fw_update_get_result(void) -{ - int result = 0; - - switch (last_op) { - case USB_RETIMER_FW_UPDATE_SUSPEND_PD: - if (last_result == USB_RETIMER_FW_UPDATE_ERR) { - result = last_result; - break; - } - /* fall through */ - case USB_RETIMER_FW_UPDATE_RESUME_PD: - result = pd_is_port_enabled(cur_port); - break; - case USB_RETIMER_FW_UPDATE_QUERY_PORT: - result = usb_mux_retimer_fw_update_port_info(); - break; - case USB_RETIMER_FW_UPDATE_GET_MUX: - case USB_RETIMER_FW_UPDATE_SET_USB: - case USB_RETIMER_FW_UPDATE_SET_SAFE: - case USB_RETIMER_FW_UPDATE_SET_TBT: - case USB_RETIMER_FW_UPDATE_DISCONNECT: - result = last_result; - break; - default: - break; - } - - return result; -} - -static void deferred_pd_suspend(void) -{ - pd_set_suspend(cur_port, SUSPEND); -} -DECLARE_DEFERRED(deferred_pd_suspend); - -static inline mux_state_t retimer_fw_update_usb_mux_get(int port) -{ - return usb_mux_get(port) & USB_RETIMER_FW_UPDATE_MUX_MASK; -} - -void usb_retimer_fw_update_process_op_cb(int port) -{ - switch (last_op) { - case USB_RETIMER_FW_UPDATE_SUSPEND_PD: - last_result = 0; - /* - * Do not perform retimer firmware update process - * if battery is not present, or battery level is low. - */ - if (!pd_firmware_upgrade_check_power_readiness(port)) { - last_result = USB_RETIMER_FW_UPDATE_ERR; - break; - } - - /* - * If the port has entered low power mode, the PD task - * is paused and will not complete processing of - * pd_set_suspend(). Move pd_set_suspend() into a deferred - * call so that it runs from the HOOKS task and can generate - * a wake event to the PD task and enter suspended mode. - */ - hook_call_deferred(&deferred_pd_suspend_data, 0); - break; - case USB_RETIMER_FW_UPDATE_RESUME_PD: - pd_set_suspend(port, RESUME); - break; - case USB_RETIMER_FW_UPDATE_GET_MUX: - last_result = retimer_fw_update_usb_mux_get(port); - break; - case USB_RETIMER_FW_UPDATE_SET_USB: - usb_mux_set(port, USB_PD_MUX_USB_ENABLED, - USB_SWITCH_CONNECT, pd_get_polarity(port)); - last_result = retimer_fw_update_usb_mux_get(port); - break; - case USB_RETIMER_FW_UPDATE_SET_SAFE: - usb_mux_set_safe_mode(port); - last_result = retimer_fw_update_usb_mux_get(port); - break; - case USB_RETIMER_FW_UPDATE_SET_TBT: - usb_mux_set(port, USB_PD_MUX_TBT_COMPAT_ENABLED, - USB_SWITCH_CONNECT, pd_get_polarity(port)); - last_result = retimer_fw_update_usb_mux_get(port); - break; - case USB_RETIMER_FW_UPDATE_DISCONNECT: - usb_mux_set(port, USB_PD_MUX_NONE, - USB_SWITCH_DISCONNECT, pd_get_polarity(port)); - last_result = retimer_fw_update_usb_mux_get(port); - break; - default: - break; - } -} - -void usb_retimer_fw_update_process_op(int port, int op) -{ - ASSERT(port >= 0 && port < CONFIG_USB_PD_PORT_MAX_COUNT); - - /* - * TODO(b/179220036): check not overlapping requests; - * not change cur_port if retimer scan is in progress - */ - last_op = op; - - switch (op) { - case USB_RETIMER_FW_UPDATE_QUERY_PORT: - break; - /* Operations can't be processed in ISR, defer to later */ - case USB_RETIMER_FW_UPDATE_GET_MUX: - last_result = USB_RETIMER_FW_UPDATE_INVALID_MUX; - tc_usb_firmware_fw_update_run(port); - break; - case USB_RETIMER_FW_UPDATE_SUSPEND_PD: - case USB_RETIMER_FW_UPDATE_RESUME_PD: - cur_port = port; - tc_usb_firmware_fw_update_run(port); - break; - case USB_RETIMER_FW_UPDATE_SET_USB: - case USB_RETIMER_FW_UPDATE_SET_SAFE: - case USB_RETIMER_FW_UPDATE_SET_TBT: - case USB_RETIMER_FW_UPDATE_DISCONNECT: - if (pd_is_port_enabled(port)) { - last_result = USB_RETIMER_FW_UPDATE_ERR; - } else { - last_result = USB_RETIMER_FW_UPDATE_INVALID_MUX; - tc_usb_firmware_fw_update_limited_run(port); - } - break; - default: - break; - } -} diff --git a/common/usbc/usb_sm.c b/common/usbc/usb_sm.c deleted file mode 100644 index 04b7193c0f..0000000000 --- a/common/usbc/usb_sm.c +++ /dev/null @@ -1,202 +0,0 @@ -/* Copyright 2019 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "common.h" -#include "console.h" -#include "stdbool.h" -#include "task.h" -#include "usb_pd.h" -#include "usb_sm.h" -#include "util.h" - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USB, format, ## args) -#else /* CONFIG_COMMON_RUNTIME */ -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* Private structure (to this file) used to track state machine context */ -struct internal_ctx { - usb_state_ptr last_entered; - uint32_t running : 1; - uint32_t enter : 1; - uint32_t exit : 1; -}; -BUILD_ASSERT(sizeof(struct internal_ctx) == - member_size(struct sm_ctx, internal)); - -/* Gets the first shared parent state between a and b (inclusive) */ -static usb_state_ptr shared_parent_state(usb_state_ptr a, usb_state_ptr b) -{ - const usb_state_ptr orig_b = b; - - /* There are no common ancestors */ - if (b == NULL) - return NULL; - - /* This assumes that both A and B are NULL terminated without cycles */ - while (a != NULL) { - /* We found a match return */ - if (a == b) - return a; - - /* - * Otherwise, increment b down the list for comparison until we - * run out, then increment a and start over on b for comparison - */ - if (b->parent == NULL) { - a = a->parent; - b = orig_b; - } else { - b = b->parent; - } - } - - return NULL; -} - -/* - * Call all entry functions of parents before children. If set_state is called - * during one of the entry functions, then do not call any remaining entry - * functions. - */ -static void call_entry_functions(const int port, - struct internal_ctx *const internal, - const usb_state_ptr stop, - const usb_state_ptr current) -{ - if (current == stop) - return; - - call_entry_functions(port, internal, stop, current->parent); - - /* - * If the previous entry function called set_state, then don't enter - * remaining states. - */ - if (!internal->enter) - return; - - /* Track the latest state that was entered, so we can exit properly. */ - internal->last_entered = current; - if (current->entry) - current->entry(port); -} - -/* - * Call all exit functions of children before parents. Note set_state is ignored - * during an exit function. - */ -static void call_exit_functions(const int port, const usb_state_ptr stop, - const usb_state_ptr current) -{ - if (current == stop) - return; - - if (current->exit) - current->exit(port); - - call_exit_functions(port, stop, current->parent); -} - -void set_state(const int port, struct sm_ctx *const ctx, - const usb_state_ptr new_state) -{ - struct internal_ctx * const internal = (void *) ctx->internal; - usb_state_ptr last_state; - usb_state_ptr shared_parent; - - /* - * It does not make sense to call set_state in an exit phase of a state - * since we are already in a transition; we would always ignore the - * intended state to transition into. - */ - if (internal->exit) { - CPRINTF("C%d: Ignoring set state to 0x%pP within 0x%pP", - port, new_state, ctx->current); - return; - } - - /* - * Determine the last state that was entered. Normally it is current, - * but we could have called set_state within an entry phase, so we - * shouldn't exit any states that weren't fully entered. - */ - last_state = internal->enter ? internal->last_entered : ctx->current; - - /* We don't exit and re-enter shared parent states */ - shared_parent = shared_parent_state(last_state, new_state); - - /* - * Exit all of the non-common states from the last state. - */ - internal->exit = true; - call_exit_functions(port, shared_parent, last_state); - internal->exit = false; - - ctx->previous = ctx->current; - ctx->current = new_state; - - /* - * Enter all new non-common states. last_entered will contain the last - * state that successfully entered before another set_state was called. - */ - internal->last_entered = NULL; - internal->enter = true; - call_entry_functions(port, internal, shared_parent, ctx->current); - /* - * Setting enter to false ensures that all pending entry calls will be - * skipped (in the case of a parent state calling set_state, which means - * we should not enter any child states) - */ - internal->enter = false; - - /* - * If we set_state while we are running a child state, then stop running - * any remaining parent states. - */ - internal->running = false; - - /* - * Since we are changing states, we want to ensure that we process the - * next state's run method as soon as we can to ensure that we don't - * delay important processing until the next task interval. - */ - if (IS_ENABLED(HAS_TASK_PD_C0)) - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -/* - * Call all run functions of children before parents. If set_state is called - * during one of the entry functions, then do not call any remaining entry - * functions. - */ -static void call_run_functions(const int port, - const struct internal_ctx *const internal, - const usb_state_ptr current) -{ - if (!current) - return; - - /* If set_state is called during run, don't call remain functions. */ - if (!internal->running) - return; - - if (current->run) - current->run(port); - - call_run_functions(port, internal, current->parent); -} - -void run_state(const int port, struct sm_ctx *const ctx) -{ - struct internal_ctx * const internal = (void *) ctx->internal; - - internal->running = true; - call_run_functions(port, internal, ctx->current); - internal->running = false; -} diff --git a/common/usbc/usb_tc_ctvpd_sm.c b/common/usbc/usb_tc_ctvpd_sm.c deleted file mode 100644 index a2babe754a..0000000000 --- a/common/usbc/usb_tc_ctvpd_sm.c +++ /dev/null @@ -1,1716 +0,0 @@ -/* Copyright 2019 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "common.h" -#include "console.h" -#include "system.h" -#include "task.h" -#include "tcpm/tcpm.h" -#include "usb_pd.h" -#include "usb_tc_sm.h" -#include "vpd_api.h" - -/* USB Type-C CTVPD module */ - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else /* CONFIG_COMMON_RUNTIME */ -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* Type-C Layer Flags */ -#define TC_FLAGS_VCONN_ON BIT(0) - -#define SUPPORT_TIMER_RESET_INIT 0 -#define SUPPORT_TIMER_RESET_REQUEST 1 -#define SUPPORT_TIMER_RESET_COMPLETE 2 - -/** - * This is the Type-C Port object that contains information needed to - * implement a Charge Through VCONN Powered Device. - */ -static struct type_c { - /* state machine context */ - struct sm_ctx ctx; - /* Higher-level power deliver state machines are enabled if true. */ - uint8_t pd_enable; - /* port flags, see TC_FLAGS_* */ - uint32_t flags; - /* - * Time a charge-through port shall wait before it can determine it - * is attached - */ - uint64_t cc_debounce; - /* Time a host port shall wait before it can determine it is attached */ - uint64_t host_cc_debounce; - /* Time a Sink port shall wait before it can determine it is detached - * due to the potential for USB PD signaling on CC as described in - * the state definitions. - */ - uint64_t pd_debounce; - /* Maintains state of billboard device */ - int billboard_presented; - /* - * Time a port shall wait before it can determine it is - * re-attached during the try-wait process. - */ - uint64_t try_wait_debounce; - /* charge-through support timer */ - uint64_t support_timer; - /* reset the charge-through support timer */ - uint8_t support_timer_reset; - /* VPD host port cc state */ - enum pd_cc_states host_cc_state; - uint8_t ct_cc; - /* The cc state */ - enum pd_cc_states cc_state; - uint64_t next_role_swap; -} tc[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* List of all TypeC-level states */ -enum usb_tc_state { - /* Normal States */ - TC_DISABLED, - TC_UNATTACHED_SNK, - TC_ATTACH_WAIT_SNK, - TC_ATTACHED_SNK, - TC_ERROR_RECOVERY, - TC_TRY_SNK, - TC_UNATTACHED_SRC, - TC_ATTACH_WAIT_SRC, - TC_TRY_WAIT_SRC, - TC_ATTACHED_SRC, - TC_CT_TRY_SNK, - TC_CT_ATTACH_WAIT_UNSUPPORTED, - TC_CT_ATTACHED_UNSUPPORTED, - TC_CT_UNATTACHED_UNSUPPORTED, - TC_CT_UNATTACHED_VPD, - TC_CT_DISABLED_VPD, - TC_CT_ATTACHED_VPD, - TC_CT_ATTACH_WAIT_VPD, - /* Super States */ - TC_VBUS_CC_ISO, - TC_HOST_RARD_CT_RD, - TC_HOST_OPEN_CT_OPEN, - TC_HOST_RP3_CT_RD, - TC_HOST_RP3_CT_RPU, - TC_HOST_RPU_CT_RD, -}; - -/* Forward declare the full list of states. This is indexed by usb_tc_state */ -static const struct usb_state tc_states[]; - - -/* List of human readable state names for console debugging */ -__maybe_unused const char * const tc_state_names[] = { -#ifdef CONFIG_COMMON_RUNTIME - [TC_DISABLED] = "Disabled", - [TC_UNATTACHED_SNK] = "Unattached.SNK", - [TC_ATTACH_WAIT_SNK] = "AttachWait.SNK", - [TC_ATTACHED_SNK] = "Attached.SNK", - [TC_ERROR_RECOVERY] = "ErrorRecovery", - [TC_TRY_SNK] = "Try.SNK", - [TC_UNATTACHED_SRC] = "Unattached.SRC", - [TC_ATTACH_WAIT_SRC] = "AttachWait.SRC", - [TC_TRY_WAIT_SRC] = "TryWait.SRC", - [TC_ATTACHED_SRC] = "Attached.SRC", - [TC_CT_TRY_SNK] = "CTTry.SNK", - [TC_CT_ATTACH_WAIT_UNSUPPORTED] = "CTAttachWait.Unsupported", - [TC_CT_ATTACHED_UNSUPPORTED] = "CTAttached.Unsupported", - [TC_CT_UNATTACHED_UNSUPPORTED] = "CTUnattached.Unsupported", - [TC_CT_UNATTACHED_VPD] = "CTUnattached.VPD", - [TC_CT_DISABLED_VPD] = "CTDisabled.VPD", - [TC_CT_ATTACHED_VPD] = "CTAttached.VPD", - [TC_CT_ATTACH_WAIT_VPD] = "CTAttachWait.VPD", -#endif -}; - -/* Forward declare private, common functions */ -static void set_state_tc(const int port, enum usb_tc_state new_state); - -/* Public TypeC functions */ - -enum pd_power_role pd_get_power_role(int port) -{ - /* Vconn power device is always the sink */ - return PD_ROLE_SINK; -} - -enum pd_cable_plug tc_get_cable_plug(int port) -{ - /* Vconn power device is always the cable */ - return PD_PLUG_FROM_CABLE; -} - -enum pd_data_role pd_get_data_role(int port) -{ - /* Vconn power device doesn't have a data role, but UFP matches SNK */ - return PD_ROLE_UFP; -} - -/* Note tc_set_power_role and tc_set_data_role are unimplemented */ - -uint8_t tc_get_polarity(int port) -{ - /* Does not track polarity */ - return 0; -} - -uint8_t tc_get_pd_enabled(int port) -{ - return tc[port].pd_enable; -} - -void tc_reset_support_timer(int port) -{ - tc[port].support_timer_reset |= SUPPORT_TIMER_RESET_REQUEST; -} - -/* - * TCPC CC/Rp management - * - * Stub for linking purposes. - * This is not supported for ctvpd, we are never the SOP partner. - */ -void typec_select_pull(int port, enum tcpc_cc_pull pull) -{ -} -void typec_select_src_current_limit_rp(int port, enum tcpc_rp_value rp) -{ -} -void typec_select_src_collision_rp(int port, enum tcpc_rp_value rp) -{ -} -int typec_update_cc(int port) -{ - return EC_SUCCESS; -} - -void tc_state_init(int port) -{ - int res = 0; - - res = tcpm_init(port); - - CPRINTS("C%d: init %s", port, res ? "failed" : "ready"); - - /* Disable if restart failed, otherwise start in default state. */ - set_state_tc(port, res ? TC_DISABLED : TC_UNATTACHED_SNK); - - /* Disable pd state machines */ - tc[port].pd_enable = 0; - tc[port].billboard_presented = 0; - tc[port].flags = 0; -} - -void tc_event_check(int port, int evt) -{ - /* Do Nothing */ -} - -void tc_run(const int port) -{ - run_state(port, &tc[port].ctx); -} - -/* Internal Functions */ - -/* Set the TypeC state machine to a new state. */ -static void set_state_tc(const int port, enum usb_tc_state new_state) -{ - set_state(port, &tc[port].ctx, &tc_states[new_state]); -} - -/* Get the current TypeC state. */ -test_export_static enum usb_tc_state get_state_tc(const int port) -{ - return tc[port].ctx.current - &tc_states[0]; -} - -/* Get the previous TypeC state. */ -static enum usb_tc_state get_last_state_tc(const int port) -{ - return tc[port].ctx.previous - &tc_states[0]; -} - -test_mockable_static void print_current_state(const int port) -{ - CPRINTS("C%d: %s", port, tc_state_names[get_state_tc(port)]); -} - -int pd_is_connected(int port) -{ - return (get_state_tc(port) == TC_ATTACHED_SNK) || - (get_state_tc(port) == TC_ATTACHED_SRC) || - (get_state_tc(port) == TC_CT_ATTACHED_UNSUPPORTED) || - (get_state_tc(port) == TC_CT_ATTACHED_VPD); -} - -bool pd_is_disconnected(int port) -{ - return !pd_is_connected(port); -} - -/** - * Disabled - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Remove the terminations from Host - * Remove the terminations from Charge-Through - */ -static void tc_disabled_entry(const int port) -{ - print_current_state(port); -} - -static void tc_disabled_run(const int port) -{ - task_wait_event(-1); -} - -static void tc_disabled_exit(const int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TCPC)) { - if (tcpm_init(port) != 0) { - CPRINTS("C%d: restart failed!", port); - return; - } - } - - CPRINTS("C%d: resumed!", port); -} - -void pd_set_suspend(int port, int suspend) -{ - /* - * This shouldn't happen. If it does, we need to send an event to the - * PD task to put the SM into the disabled state. It is not safe to - * directly set_state here since this may be in another task. - */ - assert(false); -} - -/** - * ErrorRecovery - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Remove the terminations from Host - * Remove the terminations from Charge-Through - */ -static void tc_error_recovery_entry(const int port) -{ - print_current_state(port); - /* Use cc_debounce state variable for error recovery timeout */ - tc[port].cc_debounce = get_time().val + PD_T_ERROR_RECOVERY; -} - -static void tc_error_recovery_run(const int port) -{ - if (get_time().val > tc[port].cc_debounce) - set_state_tc(port, TC_UNATTACHED_SNK); -} - -/** - * Unattached.SNK - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place Ra on VCONN and Rd on Host CC - * Place Rd on Charge-Through CCs - */ -static void tc_unattached_snk_entry(const int port) -{ - if (get_last_state_tc(port) != TC_UNATTACHED_SRC) - print_current_state(port); - - tc[port].flags &= ~TC_FLAGS_VCONN_ON; - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_unattached_snk_run(const int port) -{ - int host_cc; - int new_cc_state; - int cc1; - int cc2; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - /* - * Transition to AttachWait.SNK when a Source connection is - * detected, as indicated by the SNK.Rp state on its Host-side - * port’s CC pin. - */ - if (cc_is_rp(host_cc)) { - set_state_tc(port, TC_ATTACH_WAIT_SNK); - return; - } - - /* Check Charge-Through CCs for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - if (cc_is_rp(cc1) != cc_is_rp(cc2)) - new_cc_state = PD_CC_DFP_ATTACHED; - else - new_cc_state = PD_CC_NONE; - - /* Debounce Charge-Through CC state */ - if (tc[port].cc_state != new_cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE; - } - - /* If we are here, Host CC must be open */ - - /* Wait for Charge-Through CC debounce */ - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Unattached.SRC when the state of the Host-side port’s CC pin is - * SNK.Open for tDRP − dcSRC.DRP ∙ tDRP and both of the following - * is detected on the Charge-Through port. - * 1) SNK.Rp state is detected on exactly one of the CC1 or CC2 - * pins for at least tCCDebounce - * 2) VBUS is detected - */ - if (vpd_is_ct_vbus_present() && - tc[port].cc_state == PD_CC_DFP_ATTACHED) { - set_state_tc(port, TC_UNATTACHED_SRC); - return; - } -} - -/** - * AttachWait.SNK - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place Ra on VCONN and Rd on Host CC - * Place Rd on Charge-Through CCs - */ -static void tc_attach_wait_snk_entry(const int port) -{ - print_current_state(port); - tc[port].host_cc_state = PD_CC_UNSET; -} - -static void tc_attach_wait_snk_run(const int port) -{ - int host_new_cc_state; - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - if (cc_is_rp(host_cc)) - host_new_cc_state = PD_CC_DFP_ATTACHED; - else - host_new_cc_state = PD_CC_NONE; - - /* Debounce the Host CC state */ - if (tc[port].host_cc_state != host_new_cc_state) { - tc[port].host_cc_state = host_new_cc_state; - if (host_new_cc_state == PD_CC_DFP_ATTACHED) - tc[port].host_cc_debounce = get_time().val + - PD_T_CC_DEBOUNCE; - else - tc[port].host_cc_debounce = get_time().val + - PD_T_PD_DEBOUNCE; - return; - } - - /* Wait for Host CC debounce */ - if (get_time().val < tc[port].host_cc_debounce) - return; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Attached.SNK after the state of the Host-side port’s CC pin is - * SNK.Rp for at least tCCDebounce and either host-side VCONN or - * VBUS is detected. - * - * Transition to Unattached.SNK when the state of both the CC1 and - * CC2 pins is SNK.Open for at least tPDDebounce. - */ - if (tc[port].host_cc_state == PD_CC_DFP_ATTACHED && - (vpd_is_vconn_present() || vpd_is_host_vbus_present())) - set_state_tc(port, TC_ATTACHED_SNK); - else if (tc[port].host_cc_state == PD_CC_NONE) - set_state_tc(port, TC_UNATTACHED_SNK); -} - -/** - * Attached.SNK - */ -static void tc_attached_snk_entry(const int port) -{ - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - pd_set_polarity(port, 0); - - /* - * This state can only be entered from states AttachWait.SNK - * and Try.SNK. So the Host port is isolated from the - * Charge-Through port. We only need to High-Z the - * Charge-Through ports CC1 and CC2 pins. - */ - vpd_ct_set_pull(TYPEC_CC_OPEN, 0); - - tc[port].host_cc_state = PD_CC_UNSET; - - /* Start Charge-Through support timer */ - tc[port].support_timer_reset = SUPPORT_TIMER_RESET_INIT; - tc[port].support_timer = get_time().val + PD_T_AME; -} - -static void tc_attached_snk_run(const int port) -{ - int host_new_cc_state; - int host_cc; - - /* Has host vbus and vconn been removed */ - if (!vpd_is_host_vbus_present() && !vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* - * Reset the Charge-Through Support Timer when it first - * receives any USB PD Structured VDM Command it supports, - * which is the Discover Identity command. And this is only - * done one time. - */ - if (tc[port].support_timer_reset == SUPPORT_TIMER_RESET_REQUEST) { - tc[port].support_timer_reset |= SUPPORT_TIMER_RESET_COMPLETE; - tc[port].support_timer = get_time().val + PD_T_AME; - } - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - if (cc_is_rp(host_cc)) - host_new_cc_state = PD_CC_DFP_ATTACHED; - else - host_new_cc_state = PD_CC_NONE; - - /* Debounce the Host CC state */ - if (tc[port].host_cc_state != host_new_cc_state) { - tc[port].host_cc_state = host_new_cc_state; - tc[port].host_cc_debounce = get_time().val + PD_T_VPDCTDD; - return; - } - - /* Wait for Host CC debounce */ - if (get_time().val < tc[port].host_cc_debounce) - return; - - if (vpd_is_vconn_present()) { - if (!(tc[port].flags & TC_FLAGS_VCONN_ON)) { - /* VCONN detected. Remove RA */ - vpd_host_set_pull(TYPEC_CC_RD, 0); - tc[port].flags |= TC_FLAGS_VCONN_ON; - } - - /* - * A Charge-Through VCONN-Powered USB Device shall transition - * to CTUnattached.VPD if VCONN is present and the state of - * its Host-side port’s CC pin is SNK.Open for tVPDCTDD. - */ - if (tc[port].host_cc_state == PD_CC_NONE) { - set_state_tc(port, TC_CT_UNATTACHED_VPD); - return; - } - } - - /* Check the Support Timer */ - if (get_time().val > tc[port].support_timer && - !tc[port].billboard_presented) { - /* - * Present USB Billboard Device Class interface - * indicating that Charge-Through is not supported - */ - tc[port].billboard_presented = 1; - vpd_present_billboard(BB_SNK); - } -} - -static void tc_attached_snk_exit(const int port) -{ - tc[port].billboard_presented = 0; - vpd_present_billboard(BB_NONE); -} - -/** - * Super State HOST_RA_CT_RD - */ -static void tc_host_rard_ct_rd_entry(const int port) -{ - /* Place Ra on VCONN and Rd on Host CC */ - vpd_host_set_pull(TYPEC_CC_RA_RD, 0); - - /* Place Rd on Charge-Through CCs */ - vpd_ct_set_pull(TYPEC_CC_RD, 0); -} - -/** - * Super State HOST_OPEN_CT_OPEN - */ -static void tc_host_open_ct_open_entry(const int port) -{ - /* Remove the terminations from Host */ - vpd_host_set_pull(TYPEC_CC_OPEN, 0); - - /* Remove the terminations from Charge-Through */ - vpd_ct_set_pull(TYPEC_CC_OPEN, 0); -} - -/** - * Super State VBUS_CC_ISO - */ -static void tc_vbus_cc_iso_entry(const int port) -{ - /* Isolate the Host-side port from the Charge-Through port */ - vpd_vbus_pass_en(0); - - /* Remove Charge-Through side port CCs */ - vpd_ct_cc_sel(CT_OPEN); - - /* Enable mcu communication and cc */ - vpd_mcu_cc_en(1); -} - -/** - * Unattached.SRC - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RpUSB on Host CC - * Place Rd on Charge-Through CCs - */ -static void tc_unattached_src_entry(const int port) -{ - if (get_last_state_tc(port) != TC_UNATTACHED_SNK) - print_current_state(port); - - /* Get power from VBUS */ - vpd_vconn_pwr_sel_odl(PWR_VBUS); - - /* Make sure it's the Charge-Through Port's VBUS */ - if (!vpd_is_ct_vbus_present()) { - set_state_tc(port, TC_ERROR_RECOVERY); - return; - } - - tc[port].next_role_swap = get_time().val + PD_T_DRP_SRC; -} - -static void tc_unattached_src_run(const int port) -{ - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - /* - * Transition to AttachWait.SRC when host-side VBUS is - * vSafe0V and SRC.Rd state is detected on the Host-side - * port’s CC pin. - */ - if (!vpd_is_host_vbus_present() && host_cc == TYPEC_CC_VOLT_RD) { - set_state_tc(port, TC_ATTACH_WAIT_SRC); - return; - } - - /* - * Transition to Unattached.SNK within tDRPTransition or - * if Charge-Through VBUS is removed. - */ - if (!vpd_is_ct_vbus_present() || - get_time().val > tc[port].next_role_swap) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } -} - -/** - * AttachWait.SRC - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RpUSB on Host CC - * Place Rd on Charge-Through CCs - */ -static void tc_attach_wait_src_entry(const int port) -{ - print_current_state(port); - - tc[port].host_cc_state = PD_CC_UNSET; -} - -static void tc_attach_wait_src_run(const int port) -{ - int host_new_cc_state; - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - if (host_cc == TYPEC_CC_VOLT_RD) - host_new_cc_state = PD_CC_UFP_ATTACHED; - else - host_new_cc_state = PD_CC_NONE; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition - * to Unattached.SNK when the SRC.Open state is detected on the - * Host-side port’s CC or if Charge-Through VBUS falls below - * vSinkDisconnect. The Charge-Through VCONN-Powered USB Device - * shall detect the SRC.Open state within tSRCDisconnect, but - * should detect it as quickly as possible. - */ - if (host_new_cc_state == PD_CC_NONE || !vpd_is_ct_vbus_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* Debounce the Host CC state */ - if (tc[port].host_cc_state != host_new_cc_state) { - tc[port].host_cc_state = host_new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE; - return; - } - - /* Wait for Host CC debounce */ - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Try.SNK when the host-side VBUS is at vSafe0V and the SRC.Rd - * state is on the Host-side port’s CC pin for at least tCCDebounce. - */ - if (tc[port].host_cc_state == PD_CC_UFP_ATTACHED && - !vpd_is_host_vbus_present()) { - set_state_tc(port, TC_TRY_SNK); - return; - } -} - -/** - * Attached.SRC - */ -static void tc_attached_src_entry(const int port) -{ - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - pd_set_polarity(port, 0); - - /* Connect Charge-Through VBUS to Host VBUS */ - vpd_vbus_pass_en(1); - - /* - * Get power from VBUS. No need to test because - * the Host VBUS is connected to the Charge-Through - * VBUS - */ - vpd_vconn_pwr_sel_odl(PWR_VBUS); -} - -static void tc_attached_src_run(const int port) -{ - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Unattached.SNK when VBUS falls below vSinkDisconnect or the - * Host-side port’s CC pin is SRC.Open. The Charge-Through - * VCONNPowered USB Device shall detect the SRC.Open state within - * tSRCDisconnect, but should detect it as quickly as possible. - */ - if (!vpd_is_ct_vbus_present() || host_cc == TYPEC_CC_VOLT_OPEN) - set_state_tc(port, TC_UNATTACHED_SNK); -} - -/** - * Super State HOST_RPU_CT_RD - */ -static void tc_host_rpu_ct_rd_entry(const int port) -{ - /* Place RpUSB on Host CC */ - vpd_host_set_pull(TYPEC_CC_RP, TYPEC_RP_USB); - - /* Place Rd on Charge-Through CCs */ - vpd_ct_set_pull(TYPEC_CC_RD, 0); -} - -/** - * Try.SNK - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place Ra on VCONN and Rd on Host CC - * Place Rd on Charge-Through CCs - */ -static void tc_try_snk_entry(const int port) -{ - print_current_state(port); - - /* Get power from VBUS */ - vpd_vconn_pwr_sel_odl(PWR_VBUS); - - /* Make sure it's the Charge-Through Port's VBUS */ - if (!vpd_is_ct_vbus_present()) { - set_state_tc(port, TC_ERROR_RECOVERY); - return; - } - - tc[port].host_cc_state = PD_CC_UNSET; - - /* Using next_role_swap timer as try_src timer */ - tc[port].next_role_swap = get_time().val + PD_T_DRP_TRY; -} - -static void tc_try_snk_run(const int port) -{ - int host_new_cc_state; - int host_cc; - - /* - * Wait for tDRPTry before monitoring the Charge-Through - * port’s CC pins for the SNK.Rp - */ - if (get_time().val < tc[port].next_role_swap) - return; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - if (cc_is_rp(host_cc)) - host_new_cc_state = PD_CC_DFP_ATTACHED; - else - host_new_cc_state = PD_CC_NONE; - - /* Debounce the Host CC state */ - if (tc[port].host_cc_state != host_new_cc_state) { - tc[port].host_cc_state = host_new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_DEBOUNCE; - return; - } - - /* Wait for Host CC debounce */ - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * The Charge-Through VCONN-Powered USB Device shall then transition to - * Attached.SNK when the SNK.Rp state is detected on the Host-side - * port’s CC pin for at least tTryCCDebounce and VBUS or VCONN is - * detected on Host-side port. - * - * Alternatively, the Charge-Through VCONN-Powered USB Device shall - * transition to TryWait.SRC if Host-side SNK.Rp state is not detected - * for tTryCCDebounce. - */ - if (tc[port].host_cc_state == PD_CC_DFP_ATTACHED && - (vpd_is_host_vbus_present() || vpd_is_vconn_present())) - set_state_tc(port, TC_ATTACHED_SNK); - else if (tc[port].host_cc_state == PD_CC_NONE) - set_state_tc(port, TC_TRY_WAIT_SRC); -} - -/** - * TryWait.SRC - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RpUSB on Host CC - * Place Rd on Charge-Through CCs - */ -static void tc_try_wait_src_entry(const int port) -{ - print_current_state(port); - - tc[port].host_cc_state = PD_CC_UNSET; - tc[port].next_role_swap = get_time().val + PD_T_DRP_TRY; -} - -static void tc_try_wait_src_run(const int port) -{ - int host_new_cc_state; - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - if (host_cc == TYPEC_CC_VOLT_RD) - host_new_cc_state = PD_CC_UFP_ATTACHED; - else - host_new_cc_state = PD_CC_NONE; - - /* Debounce the Host CC state */ - if (tc[port].host_cc_state != host_new_cc_state) { - tc[port].host_cc_state = host_new_cc_state; - tc[port].host_cc_debounce = - get_time().val + PD_T_TRY_CC_DEBOUNCE; - return; - } - - if (get_time().val > tc[port].host_cc_debounce) { - /* - * A Charge-Through VCONN-Powered USB Device shall transition - * to Attached.SRC when host-side VBUS is at vSafe0V and the - * SRC.Rd state is detected on the Host-side port’s CC pin for - * at least tTryCCDebounce. - */ - if (tc[port].host_cc_state == PD_CC_UFP_ATTACHED && - !vpd_is_host_vbus_present()) { - set_state_tc(port, TC_ATTACHED_SRC); - return; - } - } - - if (get_time().val > tc[port].next_role_swap) { - /* - * The Charge-Through VCONN-Powered USB Device shall transition - * to Unattached.SNK after tDRPTry if the Host-side port’s CC - * pin is not in the SRC.Rd state. - */ - if (tc[port].host_cc_state == PD_CC_NONE) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - } -} - -/** - * CTTry.SNK - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RP3A0 on Host CC - * Connect Charge-Through Rd - * Get power from VCONN - */ -static void tc_ct_try_snk_entry(const int port) -{ - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - pd_set_polarity(port, 0); - - tc[port].cc_state = PD_CC_UNSET; - tc[port].next_role_swap = get_time().val + PD_T_DRP_TRY; -} - -static void tc_ct_try_snk_run(const int port) -{ - int new_cc_state; - int cc1; - int cc2; - - /* - * Wait for tDRPTry before monitoring the Charge-Through - * port’s CC pins for the SNK.Rp - */ - if (get_time().val < tc[port].next_role_swap) - return; - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - if (cc_is_rp(cc1) || cc_is_rp(cc2)) - new_cc_state = PD_CC_DFP_ATTACHED; - else - new_cc_state = PD_CC_NONE; - - /* - * The Charge-Through VCONN-Powered USB Device shall transition - * to Unattached.SNK if VCONN falls below vVCONNDisconnect. - */ - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* Debounce the CT CC state */ - if (tc[port].cc_state != new_cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_DEBOUNCE; - tc[port].try_wait_debounce = get_time().val + PD_T_TRY_WAIT; - - return; - } - - if (get_time().val > tc[port].cc_debounce) { - /* - * The Charge-Through VCONN-Powered USB Device shall then - * transition to CTAttached.VPD when the SNK.Rp state is - * detected on the Charge-Through port’s CC pins for at - * least tTryCCDebounce and VBUS is detected on - * Charge-Through port. - */ - if (tc[port].cc_state == PD_CC_DFP_ATTACHED && - vpd_is_ct_vbus_present()) { - set_state_tc(port, TC_CT_ATTACHED_VPD); - return; - } - } - - if (get_time().val > tc[port].try_wait_debounce) { - /* - * A Charge-Through VCONN-Powered USB Device shall transition - * to CTAttached.Unsupported if SNK.Rp state is not detected - * for tDRPTryWait. - */ - if (tc[port].cc_state == PD_CC_NONE) { - set_state_tc(port, - TC_CT_ATTACHED_UNSUPPORTED); - return; - } - } -} - -static void tc_ct_try_snk_exit(const int port) -{ - /* Disable PD */ - tc[port].pd_enable = 0; -} - -/** - * CTAttachWait.Unsupported - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RP3A0 on Host CC - * Place RPUSB on Charge-Through CC - * Get power from VCONN - */ -static void tc_ct_attach_wait_unsupported_entry(const int port) -{ - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - pd_set_polarity(port, 0); - - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_ct_attach_wait_unsupported_run(const int port) -{ - int new_cc_state; - int cc1; - int cc2; - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - if (cc_is_at_least_one_rd(cc1, cc2)) - new_cc_state = PD_CC_UFP_ATTACHED; - else if (cc_is_audio_acc(cc1, cc2)) - new_cc_state = PD_CC_UFP_AUDIO_ACC; - else /* (cc1 == TYPEC_CC_VOLT_OPEN or cc2 == TYPEC_CC_VOLT_OPEN */ - new_cc_state = PD_CC_NONE; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Unattached.SNK if VCONN falls below vVCONNDisconnect. - */ - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* Debounce the cc state */ - if (tc[port].cc_state != new_cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE; - return; - } - - /* Wait for CC debounce */ - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTUnattached.VPD when the state of either the Charge-Through - * Port’s CC1 or CC2 pin is SRC.Open for at least tCCDebounce. - * - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTTry.SNK if the state of at least one of the Charge-Through - * port’s CC pins is SRC.Rd, or if the state of both the CC1 and CC2 - * pins is SRC.Ra. for at least tCCDebounce. - */ - if (new_cc_state == PD_CC_NONE) - set_state_tc(port, TC_CT_UNATTACHED_VPD); - else /* PD_CC_UFP_ATTACHED or PD_CC_UFP_AUDIO_ACC */ - set_state_tc(port, TC_CT_TRY_SNK); -} - -static void tc_ct_attach_wait_unsupported_exit(const int port) -{ - /* Disable PD */ - tc[port].pd_enable = 0; -} - -/** - * CTAttached.Unsupported - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RP3A0 on Host CC - * Place RPUSB on Charge-Through CC - * Get power from VCONN - */ -static void tc_ct_attached_unsupported_entry(const int port) -{ - print_current_state(port); - - /* Present Billboard device */ - vpd_present_billboard(BB_SNK); -} - -static void tc_ct_attached_unsupported_run(const int port) -{ - int cc1; - int cc2; - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* - * The Charge-Through VCONN-Powered USB Device shall transition to - * CTUnattached.VPD when SRC.Open state is detected on both the - * Charge-Through port’s CC pins or the SRC.Open state is detected - * on one CC pin and SRC.Ra is detected on the other CC pin. - */ - if ((cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_OPEN) || - (cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_RA) || - (cc1 == TYPEC_CC_VOLT_RA && cc2 == TYPEC_CC_VOLT_OPEN)) { - set_state_tc(port, TC_CT_UNATTACHED_VPD); - return; - } -} - -static void tc_ct_attached_unsupported_exit(const int port) -{ - vpd_present_billboard(BB_NONE); -} - -/** - * CTUnattached.Unsupported - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RP3A0 on Host CC - * Place RPUSB on Charge-Through CC - * Get power from VCONN - */ -static void tc_ct_unattached_unsupported_entry(const int port) -{ - if (get_last_state_tc(port) != TC_CT_UNATTACHED_VPD) - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - pd_set_polarity(port, 0); - - tc[port].next_role_swap = get_time().val + PD_T_DRP_SRC; -} - -static void tc_ct_unattached_unsupported_run(const int port) -{ - int cc1; - int cc2; - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTAttachWait.Unsupported when a Sink connection is detected on - * the Charge-Through port, as indicated by the SRC.Rd state on at - * least one of the Charge-Through port’s CC pins or SRC.Ra state - * on both the CC1 and CC2 pins. - */ - if (cc_is_at_least_one_rd(cc1, cc2) || cc_is_audio_acc(cc1, cc2)) { - set_state_tc(port, - TC_CT_ATTACH_WAIT_UNSUPPORTED); - return; - } - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Unattached.SNK if VCONN falls below vVCONNDisconnect. - */ - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTUnattached.VPD within tDRPTransition after dcSRC.DRP ∙ tDRP. - */ - if (get_time().val > tc[port].next_role_swap) { - set_state_tc(port, TC_CT_UNATTACHED_VPD); - return; - } -} - -static void tc_ct_unattached_unsupported_exit(const int port) -{ - /* Disable PD */ - tc[port].pd_enable = 0; -} - -/** - * CTUnattached.VPD - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RP3A0 on Host CC - * Connect Charge-Through Rd - * Get power from VCONN - */ -static void tc_ct_unattached_vpd_entry(const int port) -{ - if (get_last_state_tc(port) != TC_CT_UNATTACHED_UNSUPPORTED) - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - pd_set_polarity(port, 0); - - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_ct_unattached_vpd_run(const int port) -{ - int new_cc_state; - int cc1; - int cc2; - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - if (cc_is_rp(cc1) != cc_is_rp(cc2)) - new_cc_state = PD_CC_DFP_ATTACHED; - else if (!cc_is_rp(cc1) && !cc_is_rp(cc2)) - new_cc_state = PD_CC_NONE; - else - new_cc_state = PD_CC_UNSET; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTAttachWait.VPD when a Source connection is detected on the - * Charge-Through port, as indicated by the SNK.Rp state on - * exactly one of the Charge-Through port’s CC pins. - */ - if (new_cc_state == PD_CC_DFP_ATTACHED) { - set_state_tc(port, TC_CT_ATTACH_WAIT_VPD); - return; - } - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * Unattached.SNK if VCONN falls below vVCONNDisconnect. - */ - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_DRP_SRC; - return; - } - - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTUnattached.Unsupported within tDRPTransition after the state - * of both the Charge-Through port’s CC1 and CC2 pins is SNK.Open - * for tDRP-dcSRC.DRP ∙ tDRP, or if directed. - */ - if (tc[port].cc_state == PD_CC_NONE) { - set_state_tc(port, TC_CT_UNATTACHED_UNSUPPORTED); - return; - } -} - -static void tc_ct_unattached_vpd_exit(const int port) -{ - /* Disable PD */ - tc[port].pd_enable = 0; -} - -/** - * CTDisabled.VPD - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Remove the terminations from Host - * Remove the terminations from Charge-Through - */ -static void tc_ct_disabled_vpd_entry(const int port) -{ - print_current_state(port); - - /* Get power from VBUS */ - vpd_vconn_pwr_sel_odl(PWR_VBUS); - - tc[port].next_role_swap = get_time().val + PD_T_VPDDISABLE; -} - -static void tc_ct_disabled_vpd_run(const int port) -{ - /* - * A Charge-Through VCONN-Powered USB Device shall transition - * to Unattached.SNK after tVPDDisable. - */ - if (get_time().val > tc[port].next_role_swap) - set_state_tc(port, TC_UNATTACHED_SNK); -} - -/** - * CTAttached.VPD - */ -static void tc_ct_attached_vpd_entry(const int port) -{ - int cc1; - int cc2; - print_current_state(port); - - /* Get power from VCONN */ - vpd_vconn_pwr_sel_odl(PWR_VCONN); - - /* - * Detect which of the Charge-Through port’s CC1 or CC2 - * pins is connected through the cable - */ - vpd_ct_get_cc(&cc1, &cc2); - tc[port].ct_cc = cc_is_rp(cc2) ? CT_CC2 : CT_CC1; - - /* - * 1. Remove or reduce any additional capacitance on the - * Host-side CC port - */ - vpd_mcu_cc_en(0); - - /* - * 2. Disable the Rp termination advertising 3.0 A on the - * host port’s CC pin - */ - vpd_host_set_pull(TYPEC_CC_OPEN, 0); - - /* - * 3. Passively multiplex the detected Charge-Through port’s - * CC pin through to the host port’s CC - */ - vpd_ct_cc_sel(tc[port].ct_cc); - - /* - * 4. Disable the Rd on the Charge-Through port’s CC1 and CC2 - * pins - */ - vpd_ct_set_pull(TYPEC_CC_OPEN, 0); - - /* - * 5. Connect the Charge-Through port’s VBUS through to the - * host port’s VBUS - */ - vpd_vbus_pass_en(1); - - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_ct_attached_vpd_run(const int port) -{ - int new_cc_state; - int cc1; - int cc2; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTDisabled.VPD if VCONN falls below vVCONNDisconnect. - */ - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_CT_DISABLED_VPD); - return; - } - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - if ((tc[port].ct_cc ? cc2 : cc1) == TYPEC_CC_VOLT_OPEN) - new_cc_state = PD_CC_NONE; - else - new_cc_state = PD_CC_DFP_ATTACHED; - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + PD_T_VPDCTDD; - return; - } - - if (get_time().val < tc[port].pd_debounce) - return; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTUnattached.VPD when VBUS falls below vSinkDisconnect and the - * state of the passed-through CC pin is SNK.Open for tVPDCTDD. - */ - if (tc[port].cc_state == PD_CC_NONE && !vpd_is_ct_vbus_present()) - set_state_tc(port, TC_CT_UNATTACHED_VPD); -} - -/** - * CTAttachWait.VPD - * - * Super State Entry Actions: - * Isolate the Host-side port from the Charge-Through port - * Enable mcu communication - * Place RP3A0 on Host CC - * Connect Charge-Through Rd - * Get power from VCONN - */ -static void tc_ct_attach_wait_vpd_entry(const int port) -{ - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - pd_set_polarity(port, 0); - - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_ct_attach_wait_vpd_run(const int port) -{ - int new_cc_state; - int cc1; - int cc2; - - /* Check CT CC for connection */ - vpd_ct_get_cc(&cc1, &cc2); - - if (cc_is_rp(cc1) != cc_is_rp(cc2)) - new_cc_state = PD_CC_DFP_ATTACHED; - else if (!cc_is_rp(cc1) && !cc_is_rp(cc2)) - new_cc_state = PD_CC_NONE; - else - new_cc_state = PD_CC_UNSET; - - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTDisabled.VPD if VCONN falls below vVCONNDisconnect. - */ - if (!vpd_is_vconn_present()) { - set_state_tc(port, TC_CT_DISABLED_VPD); - return; - } - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - tc[port].cc_debounce = get_time().val + - PD_T_CC_DEBOUNCE; - tc[port].pd_debounce = get_time().val + - PD_T_PD_DEBOUNCE; - return; - } - - if (get_time().val > tc[port].pd_debounce) { - /* - * A Charge-Through VCONN-Powered USB Device shall transition - * to CTUnattached.VPD when the state of both the Charge-Through - * port’s CC1 and CC2 pins are SNK.Open for at least - * tPDDebounce. - */ - if (tc[port].cc_state == PD_CC_NONE) { - set_state_tc(port, TC_CT_UNATTACHED_VPD); - return; - } - } - - if (get_time().val > tc[port].cc_debounce) { - /* - * A Charge-Through VCONN-Powered USB Device shall transition to - * CTAttached.VPD after the state of only one of the - * Charge-Through port’s CC1 or CC2 pins is SNK.Rp for at - * least tCCDebounce and VBUS on the Charge-Through port is - * detected. - */ - if (tc[port].cc_state == PD_CC_DFP_ATTACHED && - vpd_is_ct_vbus_present()) { - set_state_tc(port, TC_CT_ATTACHED_VPD); - return; - } - } -} - -static void tc_ct_attach_wait_vpd_exit(const int port) -{ - /* Disable PD */ - tc[port].pd_enable = 0; -} - -/** - * Super State HOST_RP3_CT_RD - */ -static void tc_host_rp3_ct_rd_entry(const int port) -{ - /* Place RP3A0 on Host CC */ - vpd_host_set_pull(TYPEC_CC_RP, TYPEC_RP_3A0); - - /* Connect Charge-Through Rd */ - vpd_ct_set_pull(TYPEC_CC_RD, 0); - - /* - * A Charge-Through VCONN-Powered USB Device shall - * ensure that it is powered by VCONN - */ - - /* Make sure vconn is on */ - if (!vpd_is_vconn_present()) - set_state_tc(port, TC_ERROR_RECOVERY); - - /* Get power from VCONN */ - vpd_vconn_pwr_sel_odl(PWR_VCONN); -} - -/** - * Super State HOST_RP3_CT_RPU - */ -static void tc_host_rp3_ct_rpu_entry(const int port) -{ - /* Place RP3A0 on Host CC */ - vpd_host_set_pull(TYPEC_CC_RP, TYPEC_RP_3A0); - - /* Place RPUSB on Charge-Through CC */ - vpd_ct_set_pull(TYPEC_CC_RP, TYPEC_RP_USB); - - /* - * A Charge-Through VCONN-Powered USB Device shall - * ensure that it is powered by VCONN - */ - - /* Make sure vconn is on */ - if (!vpd_is_vconn_present()) - set_state_tc(port, TC_ERROR_RECOVERY); - - /* Get power from VCONN */ - vpd_vconn_pwr_sel_odl(PWR_VCONN); -} - -/* All necessary Type-C states */ - -/* - * Type-C State Hierarchy (Sub-States are listed inside the boxes) - * - * | TC_VBUS_CC_ISO ------------------------------------------------------| - * | | - * | | TC_HOST_RARD_CT_RD -----------| | TC_HOST_OPEN_CT_OPEN ---------| | - * | | | | | | - * | | TC_UNATTACHED_SNK | | TC_DISABLED | | - * | | TC_ATTACH_WAIT_SNK | | TC_ERROR_RECOVERY | | - * | | TC_TRY_SNK | |-------------------------------| | - * | |-------------------------------| | - * | | - * | | TC_HOST_RP3_CT_RD ------------| | TC_HOST_RPU_CT_RD ------------| | - * | | | | | | - * | | TC_CT_TRY_SNK | | TC_UNATTACHED_SRC | | - * | | TC_CT_UNATTACHED_VPD | | TC_ATTACH_WAIT_SRC | | - * | | TC_CT_ATTACH_WAIT_VPD | | TC_TRY_WAIT_SR | | - * | |-------------------------------| |-------------------------------| | - * | | - * | | TC_HOST_RP3_CT_RPU -----------| | - * | | | | - * | | TC_CT_ATTACH_WAIT_UNSUPPORTED | | - * | | TC_CT_ATTACHED_UNSUPPORTED | | - * | | TC_CT_UNATTACHED_UNSUPPORTED | | - * | |-------------------------------| | - * |----------------------------------------------------------------------| - * - * TC_ATTACHED_SNK - * TC_ATTACHED_SRC - * TC_CT_ATTACHED_VPD - * - */ -static const struct usb_state tc_states[] = { - /* Super States */ - [TC_VBUS_CC_ISO] = { - .entry = tc_vbus_cc_iso_entry, - }, - [TC_HOST_RARD_CT_RD] = { - .entry = tc_host_rard_ct_rd_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - [TC_HOST_OPEN_CT_OPEN] = { - .entry = tc_host_open_ct_open_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - [TC_HOST_RP3_CT_RD] = { - .entry = tc_host_rp3_ct_rd_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - [TC_HOST_RP3_CT_RPU] = { - .entry = tc_host_rp3_ct_rpu_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - [TC_HOST_RPU_CT_RD] = { - .entry = tc_host_rpu_ct_rd_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - /* Normal States */ - [TC_DISABLED] = { - .entry = tc_disabled_entry, - .run = tc_disabled_run, - .exit = tc_disabled_exit, - .parent = &tc_states[TC_HOST_OPEN_CT_OPEN], - }, - [TC_UNATTACHED_SNK] = { - .entry = tc_unattached_snk_entry, - .run = tc_unattached_snk_run, - .parent = &tc_states[TC_HOST_RARD_CT_RD], - }, - [TC_ATTACH_WAIT_SNK] = { - .entry = tc_attach_wait_snk_entry, - .run = tc_attach_wait_snk_run, - .parent = &tc_states[TC_HOST_RARD_CT_RD], - }, - [TC_ATTACHED_SNK] = { - .entry = tc_attached_snk_entry, - .run = tc_attached_snk_run, - .exit = tc_attached_snk_exit, - }, - [TC_ERROR_RECOVERY] = { - .entry = tc_error_recovery_entry, - .run = tc_error_recovery_run, - .parent = &tc_states[TC_HOST_OPEN_CT_OPEN], - }, - [TC_TRY_SNK] = { - .entry = tc_try_snk_entry, - .run = tc_try_snk_run, - .parent = &tc_states[TC_HOST_RARD_CT_RD], - }, - [TC_UNATTACHED_SRC] = { - .entry = tc_unattached_src_entry, - .run = tc_unattached_src_run, - .parent = &tc_states[TC_HOST_RPU_CT_RD], - }, - [TC_ATTACH_WAIT_SRC] = { - .entry = tc_attach_wait_src_entry, - .run = tc_attach_wait_src_run, - .parent = &tc_states[TC_HOST_RPU_CT_RD], - }, - [TC_TRY_WAIT_SRC] = { - .entry = tc_try_wait_src_entry, - .run = tc_try_wait_src_run, - .parent = &tc_states[TC_HOST_RPU_CT_RD], - }, - [TC_ATTACHED_SRC] = { - .entry = tc_attached_src_entry, - .run = tc_attached_src_run, - }, - [TC_CT_TRY_SNK] = { - .entry = tc_ct_try_snk_entry, - .run = tc_ct_try_snk_run, - .exit = tc_ct_try_snk_exit, - .parent = &tc_states[TC_HOST_RP3_CT_RD], - }, - [TC_CT_ATTACH_WAIT_UNSUPPORTED] = { - .entry = tc_ct_attach_wait_unsupported_entry, - .run = tc_ct_attach_wait_unsupported_run, - .exit = tc_ct_attach_wait_unsupported_exit, - .parent = &tc_states[TC_HOST_RP3_CT_RPU], - }, - [TC_CT_ATTACHED_UNSUPPORTED] = { - .entry = tc_ct_attached_unsupported_entry, - .run = tc_ct_attached_unsupported_run, - .exit = tc_ct_attached_unsupported_exit, - .parent = &tc_states[TC_HOST_RP3_CT_RPU], - }, - [TC_CT_UNATTACHED_UNSUPPORTED] = { - .entry = tc_ct_unattached_unsupported_entry, - .run = tc_ct_unattached_unsupported_run, - .exit = tc_ct_unattached_unsupported_exit, - .parent = &tc_states[TC_HOST_RP3_CT_RPU], - }, - [TC_CT_UNATTACHED_VPD] = { - .entry = tc_ct_unattached_vpd_entry, - .run = tc_ct_unattached_vpd_run, - .exit = tc_ct_unattached_vpd_exit, - .parent = &tc_states[TC_HOST_RP3_CT_RD], - }, - [TC_CT_DISABLED_VPD] = { - .entry = tc_ct_disabled_vpd_entry, - .run = tc_ct_disabled_vpd_run, - .parent = &tc_states[TC_HOST_OPEN_CT_OPEN], - }, - [TC_CT_ATTACHED_VPD] = { - .entry = tc_ct_attached_vpd_entry, - .run = tc_ct_attached_vpd_run, - }, - [TC_CT_ATTACH_WAIT_VPD] = { - .entry = tc_ct_attach_wait_vpd_entry, - .run = tc_ct_attach_wait_vpd_run, - .exit = tc_ct_attach_wait_vpd_exit, - .parent = &tc_states[TC_HOST_RP3_CT_RD], - }, -}; - -#ifdef TEST_BUILD -const struct test_sm_data test_tc_sm_data[] = { - { - .base = tc_states, - .size = ARRAY_SIZE(tc_states), - .names = tc_state_names, - .names_size = ARRAY_SIZE(tc_state_names), - }, -}; -const int test_tc_sm_data_size = ARRAY_SIZE(test_tc_sm_data); -#endif diff --git a/common/usbc/usb_tc_drp_acc_trysrc_sm.c b/common/usbc/usb_tc_drp_acc_trysrc_sm.c deleted file mode 100644 index 182ea686ec..0000000000 --- a/common/usbc/usb_tc_drp_acc_trysrc_sm.c +++ /dev/null @@ -1,4160 +0,0 @@ -/* Copyright 2019 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "charge_manager.h" -#include "charge_state.h" -#include "common.h" -#include "console.h" -#include "hooks.h" -#include "system.h" -#include "task.h" -#include "tcpm/tcpm.h" -#include "usb_common.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_dpm.h" -#include "usb_pd_tcpm.h" -#include "usb_pd_timer.h" -#include "usb_pe_sm.h" -#include "usb_prl_sm.h" -#include "usb_sm.h" -#include "usb_tc_sm.h" -#include "usbc_ocp.h" -#include "usbc_ppc.h" -#include "vboot.h" - -/* - * USB Type-C DRP with Accessory and Try.SRC module - * See Figure 4-16 in Release 1.4 of USB Type-C Spec. - */ -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -#else /* CONFIG_COMMON_RUNTIME */ -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -#define CPRINTF_LX(x, format, args...) \ - do { \ - if (tc_debug_level >= x) \ - CPRINTF(format, ## args); \ - } while (0) -#define CPRINTF_L1(format, args...) CPRINTF_LX(1, format, ## args) -#define CPRINTF_L2(format, args...) CPRINTF_LX(2, format, ## args) -#define CPRINTF_L3(format, args...) CPRINTF_LX(3, format, ## args) - -#define CPRINTS_LX(x, format, args...) \ - do { \ - if (tc_debug_level >= x) \ - CPRINTS(format, ## args); \ - } while (0) -#define CPRINTS_L1(format, args...) CPRINTS_LX(1, format, ## args) -#define CPRINTS_L2(format, args...) CPRINTS_LX(2, format, ## args) -#define CPRINTS_L3(format, args...) CPRINTS_LX(3, format, ## args) - -/* - * Define DEBUG_PRINT_FLAG_AND_EVENT_NAMES to print flag names when set and - * cleared, and event names when handled by tc_event_check(). - */ -#undef DEBUG_PRINT_FLAG_AND_EVENT_NAMES - -#ifdef DEBUG_PRINT_FLAG_AND_EVENT_NAMES -void print_flag(int port, int set_or_clear, int flag); -#define TC_SET_FLAG(port, flag) \ - do { \ - print_flag(port, 1, flag); \ - atomic_or(&tc[port].flags, (flag)); \ - } while (0) -#define TC_CLR_FLAG(port, flag) \ - do { \ - print_flag(port, 0, flag); \ - atomic_clear_bits(&tc[port].flags, (flag)); \ - } while (0) -#else -#define TC_SET_FLAG(port, flag) atomic_or(&tc[port].flags, (flag)) -#define TC_CLR_FLAG(port, flag) atomic_clear_bits(&tc[port].flags, (flag)) -#endif -#define TC_CHK_FLAG(port, flag) (tc[port].flags & (flag)) - -/* Type-C Layer Flags */ -/* Flag to note we are sourcing VCONN */ -#define TC_FLAGS_VCONN_ON BIT(0) -/* Flag to note port partner has Rp/Rp or Rd/Rd */ -#define TC_FLAGS_TS_DTS_PARTNER BIT(1) -/* Flag to note VBus input has never been low */ -#define TC_FLAGS_VBUS_NEVER_LOW BIT(2) -/* Flag to note Low Power Mode transition is currently happening */ -#define TC_FLAGS_LPM_TRANSITION BIT(3) -/* Flag to note Low Power Mode is currently on */ -#define TC_FLAGS_LPM_ENGAGED BIT(4) -/* Flag to note CVTPD has been detected */ -#define TC_FLAGS_CTVPD_DETECTED BIT(5) -/* Flag to note request to swap to VCONN on */ -#define TC_FLAGS_REQUEST_VC_SWAP_ON BIT(6) -/* Flag to note request to swap to VCONN off */ -#define TC_FLAGS_REQUEST_VC_SWAP_OFF BIT(7) -/* Flag to note request to swap VCONN is being rejected */ -#define TC_FLAGS_REJECT_VCONN_SWAP BIT(8) -/* Flag to note request to power role swap */ -#define TC_FLAGS_REQUEST_PR_SWAP BIT(9) -/* Flag to note request to data role swap */ -#define TC_FLAGS_REQUEST_DR_SWAP BIT(10) -/* Flag to note request to power off sink */ -#define TC_FLAGS_POWER_OFF_SNK BIT(11) -/* Flag to note port partner is Power Delivery capable */ -#define TC_FLAGS_PARTNER_PD_CAPABLE BIT(12) -/* Flag to note hard reset has been requested */ -#define TC_FLAGS_HARD_RESET_REQUESTED BIT(13) -/* Flag to note we are currently performing PR Swap */ -#define TC_FLAGS_PR_SWAP_IN_PROGRESS BIT(14) -/* Flag to note we should check for connection */ -#define TC_FLAGS_CHECK_CONNECTION BIT(15) -/* Flag to note request from pd_set_suspend to enter TC_DISABLED state */ -#define TC_FLAGS_REQUEST_SUSPEND BIT(16) -/* Flag to note we are in TC_DISABLED state */ -#define TC_FLAGS_SUSPENDED BIT(17) -/* Flag to indicate the port current limit has changed */ -#define TC_FLAGS_UPDATE_CURRENT BIT(18) -/* Flag to indicate USB mux should be updated */ -#define TC_FLAGS_UPDATE_USB_MUX BIT(19) -/* Flag for retimer firmware update */ -#define TC_FLAGS_USB_RETIMER_FW_UPDATE_RUN BIT(20) -#define TC_FLAGS_USB_RETIMER_FW_UPDATE_LTD_RUN BIT(21) -/* Flag for asynchronous call to request Error Recovery */ -#define TC_FLAGS_REQUEST_ERROR_RECOVERY BIT(22) - -/* For checking flag_bit_names[] array */ -#define TC_FLAGS_COUNT 23 - -/* On disconnect, clear most of the flags. */ -#define CLR_FLAGS_ON_DISCONNECT(port) TC_CLR_FLAG(port, \ - ~(TC_FLAGS_LPM_ENGAGED | TC_FLAGS_REQUEST_SUSPEND | TC_FLAGS_SUSPENDED)) - -/* - * 10 ms is enough time for any TCPC transaction to complete - * - * This value must be below ~39.7 ms to put ANX7447 into LPM due to bug in - * silicon (see b/77544959 and b/149761477 for more details). - */ -#define PD_LPM_DEBOUNCE_US (10 * MSEC) - -/* - * This delay is not part of the USB Type-C specification or the USB port - * controller specification. Some TCPCs require extra time before the CC_STATUS - * register is updated when exiting low power mode. - * - * This delay can be possibly shortened or removed by checking VBUS state - * before trying to re-enter LPM. - * - * TODO(b/162347811): TCPMv2: Wait for debounce on Vbus and CC lines - */ -#define PD_LPM_EXIT_DEBOUNCE_US CONFIG_USB_PD_TCPC_LPM_EXIT_DEBOUNCE - -/* - * The TypeC state machine uses this bit to disable/enable PD - * This bit corresponds to bit-0 of pd_disabled_mask - */ -#define PD_DISABLED_NO_CONNECTION BIT(0) -/* - * Console and Host commands use this bit to override the - * PD_DISABLED_NO_CONNECTION bit that was set by the TypeC - * state machine. - * This bit corresponds to bit-1 of pd_disabled_mask - */ -#define PD_DISABLED_BY_POLICY BIT(1) - -/* Unreachable time in future */ -#define TIMER_DISABLED 0xffffffffffffffff - -enum ps_reset_sequence { - PS_STATE0, - PS_STATE1, - PS_STATE2, -}; - -/* List of all TypeC-level states */ -enum usb_tc_state { - /* Super States */ - TC_CC_OPEN, - TC_CC_RD, - TC_CC_RP, - /* Normal States */ - TC_DISABLED, - TC_ERROR_RECOVERY, - TC_UNATTACHED_SNK, - TC_ATTACH_WAIT_SNK, - TC_ATTACHED_SNK, - TC_UNATTACHED_SRC, - TC_ATTACH_WAIT_SRC, - TC_ATTACHED_SRC, - TC_TRY_SRC, - TC_TRY_WAIT_SNK, - TC_DRP_AUTO_TOGGLE, - TC_LOW_POWER_MODE, - TC_CT_UNATTACHED_SNK, - TC_CT_ATTACHED_SNK, - - TC_STATE_COUNT, -}; -/* Forward declare the full list of states. This is indexed by usb_tc_state */ -static const struct usb_state tc_states[]; - -/* - * Remove all of the states that aren't support at link time. This allows - * IS_ENABLED to work. - */ -#ifndef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE -GEN_NOT_SUPPORTED(TC_DRP_AUTO_TOGGLE); -#define TC_DRP_AUTO_TOGGLE TC_DRP_AUTO_TOGGLE_NOT_SUPPORTED -#endif /* CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE */ - -#ifndef CONFIG_USB_PD_TCPC_LOW_POWER -GEN_NOT_SUPPORTED(TC_LOW_POWER_MODE); -#define TC_LOW_POWER_MODE TC_LOW_POWER_MODE_NOT_SUPPORTED -#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ - -#ifndef CONFIG_USB_PE_SM -GEN_NOT_SUPPORTED(TC_CT_UNATTACHED_SNK); -#define TC_CT_UNATTACHED_SNK TC_CT_UNATTACHED_SNK_NOT_SUPPORTED -GEN_NOT_SUPPORTED(TC_CT_ATTACHED_SNK); -#define TC_CT_ATTACHED_SNK TC_CT_ATTACHED_SNK_NOT_SUPPORTED -#endif /* CONFIG_USB_PE_SM */ - -/* - * If CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT is not defined then - * _GPIO_CCD_MODE_ODL is not needed. Declare as extern so IS_ENABLED will work. - */ -#ifndef CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT -extern int _GPIO_CCD_MODE_ODL; -#else -#define _GPIO_CCD_MODE_ODL GPIO_CCD_MODE_ODL -#endif /* CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT */ - -/* - * We will use DEBUG LABELS if we will be able to print (COMMON RUNTIME) - * and either CONFIG_USB_PD_DEBUG_LEVEL is not defined (no override) or - * we are overriding and the level is not DISABLED. - * - * If we can't print or the CONFIG_USB_PD_DEBUG_LEVEL is defined to be 0 - * then the DEBUG LABELS will be removed from the build. - */ -#if defined(CONFIG_COMMON_RUNTIME) && \ - (!defined(CONFIG_USB_PD_DEBUG_LEVEL) || \ - (CONFIG_USB_PD_DEBUG_LEVEL > 0)) -#define USB_PD_DEBUG_LABELS -#endif - -/* - * Helper Macro to determine if the machine is in state - * TC_ATTACHED_SRC - */ -#define IS_ATTACHED_SRC(port) (get_state_tc(port) == TC_ATTACHED_SRC) - -/* - * Helper Macro to determine if the machine is in state - * TC_ATTACHED_SNK - */ -#define IS_ATTACHED_SNK(port) (get_state_tc(port) == TC_ATTACHED_SNK) - - -/* List of human readable state names for console debugging */ -__maybe_unused static __const_data const char * const tc_state_names[] = { -#ifdef USB_PD_DEBUG_LABELS - [TC_DISABLED] = "Disabled", - [TC_ERROR_RECOVERY] = "ErrorRecovery", - [TC_UNATTACHED_SNK] = "Unattached.SNK", - [TC_ATTACH_WAIT_SNK] = "AttachWait.SNK", - [TC_ATTACHED_SNK] = "Attached.SNK", - [TC_UNATTACHED_SRC] = "Unattached.SRC", - [TC_ATTACH_WAIT_SRC] = "AttachWait.SRC", - [TC_ATTACHED_SRC] = "Attached.SRC", - [TC_TRY_SRC] = "Try.SRC", - [TC_TRY_WAIT_SNK] = "TryWait.SNK", -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - [TC_DRP_AUTO_TOGGLE] = "DRPAutoToggle", -#endif -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - [TC_LOW_POWER_MODE] = "LowPowerMode", -#endif -#ifdef CONFIG_USB_PE_SM - [TC_CT_UNATTACHED_SNK] = "CTUnattached.SNK", - [TC_CT_ATTACHED_SNK] = "CTAttached.SNK", -#endif - /* Super States */ - [TC_CC_OPEN] = "SS:CC_OPEN", - [TC_CC_RD] = "SS:CC_RD", - [TC_CC_RP] = "SS:CC_RP", - - [TC_STATE_COUNT] = "", -#endif -}; - -/* Debug log level - higher number == more log */ -#ifdef CONFIG_USB_PD_DEBUG_LEVEL -static const enum debug_level tc_debug_level = CONFIG_USB_PD_DEBUG_LEVEL; -#else -static enum debug_level tc_debug_level = DEBUG_LEVEL_1; -#endif - -#ifdef DEBUG_PRINT_FLAG_AND_EVENT_NAMES -struct bit_name { - int value; - const char *name; -}; - -static struct bit_name flag_bit_names[] = { - { TC_FLAGS_VCONN_ON, "VCONN_ON" }, - { TC_FLAGS_TS_DTS_PARTNER, "TS_DTS_PARTNER" }, - { TC_FLAGS_VBUS_NEVER_LOW, "VBUS_NEVER_LOW" }, - { TC_FLAGS_LPM_TRANSITION, "LPM_TRANSITION" }, - { TC_FLAGS_LPM_ENGAGED, "LPM_ENGAGED" }, - { TC_FLAGS_CTVPD_DETECTED, "CTVPD_DETECTED" }, - { TC_FLAGS_REQUEST_VC_SWAP_ON, "REQUEST_VC_SWAP_ON" }, - { TC_FLAGS_REQUEST_VC_SWAP_OFF, "REQUEST_VC_SWAP_OFF" }, - { TC_FLAGS_REJECT_VCONN_SWAP, "REJECT_VCONN_SWAP" }, - { TC_FLAGS_REQUEST_PR_SWAP, "REQUEST_PR_SWAP" }, - { TC_FLAGS_REQUEST_DR_SWAP, "REQUEST_DR_SWAP" }, - { TC_FLAGS_POWER_OFF_SNK, "POWER_OFF_SNK" }, - { TC_FLAGS_PARTNER_PD_CAPABLE, "PARTNER_PD_CAPABLE" }, - { TC_FLAGS_HARD_RESET_REQUESTED, "HARD_RESET_REQUESTED" }, - { TC_FLAGS_PR_SWAP_IN_PROGRESS, "PR_SWAP_IN_PROGRESS" }, - { TC_FLAGS_CHECK_CONNECTION, "CHECK_CONNECTION" }, - { TC_FLAGS_REQUEST_SUSPEND, "REQUEST_SUSPEND" }, - { TC_FLAGS_SUSPENDED, "SUSPENDED" }, - { TC_FLAGS_UPDATE_CURRENT, "UPDATE_CURRENT" }, - { TC_FLAGS_UPDATE_USB_MUX, "UPDATE_USB_MUX" }, - { TC_FLAGS_USB_RETIMER_FW_UPDATE_RUN, - "USB_RETIMER_FW_UPDATE_RUN" }, - { TC_FLAGS_USB_RETIMER_FW_UPDATE_LTD_RUN, - "USB_RETIMER_FW_UPDATE_LTD_RUN" }, - { TC_FLAGS_REQUEST_ERROR_RECOVERY, "REQUEST_ERROR_RECOCVERY"}, -}; -BUILD_ASSERT(ARRAY_SIZE(flag_bit_names) == TC_FLAGS_COUNT); - -static struct bit_name event_bit_names[] = { - { TASK_EVENT_SYSJUMP_READY, "SYSJUMP_READY" }, - { TASK_EVENT_IPC_READY, "IPC_READY" }, - { TASK_EVENT_PD_AWAKE, "PD_AWAKE" }, - { TASK_EVENT_PECI_DONE, "PECI_DONE" }, - { TASK_EVENT_I2C_IDLE, "I2C_IDLE" }, -#ifdef TASK_EVENT_PS2_DONE - { TASK_EVENT_PS2_DONE, "PS2_DONE" }, -#endif - { TASK_EVENT_DMA_TC, "DMA_TC" }, - { TASK_EVENT_ADC_DONE, "ADC_DONE" }, - { TASK_EVENT_RESET_DONE, "RESET_DONE" }, - { TASK_EVENT_WAKE, "WAKE" }, - { TASK_EVENT_MUTEX, "MUTEX" }, - { TASK_EVENT_TIMER, "TIMER" }, - { PD_EVENT_TX, "TX" }, - { PD_EVENT_CC, "CC" }, - { PD_EVENT_TCPC_RESET, "TCPC_RESET" }, - { PD_EVENT_UPDATE_DUAL_ROLE, "UPDATE_DUAL_ROLE" }, - { PD_EVENT_DEVICE_ACCESSED, "DEVICE_ACCESSED" }, - { PD_EVENT_POWER_STATE_CHANGE, "POWER_STATE_CHANGE" }, - { PD_EVENT_SEND_HARD_RESET, "SEND_HARD_RESET" }, - { PD_EVENT_SYSJUMP, "SYSJUMP" }, -}; - -static void print_bits(int port, const char *desc, int value, - struct bit_name *names, int names_size) -{ - int i; - - CPRINTF("C%d: %s 0x%x : ", port, desc, value); - for (i = 0; i < names_size; i++) { - if (value & names[i].value) - CPRINTF("%s | ", names[i].name); - value &= ~names[i].value; - } - if (value != 0) - CPRINTF("0x%x", value); - CPRINTF("\n"); -} - -void print_flag(int port, int set_or_clear, int flag) -{ - print_bits(port, set_or_clear ? "Set" : "Clr", flag, flag_bit_names, - ARRAY_SIZE(flag_bit_names)); -} -#endif /* DEBUG_PRINT_FLAG_AND_EVENT_NAMES */ - -#ifndef CONFIG_USB_PD_TRY_SRC -extern int TC_TRY_SRC_UNDEFINED; -extern int TC_TRY_WAIT_SNK_UNDEFINED; -#define TC_TRY_SRC TC_TRY_SRC_UNDEFINED -#define TC_TRY_WAIT_SNK TC_TRY_WAIT_SNK_UNDEFINED -#endif - -static struct type_c { - /* state machine context */ - struct sm_ctx ctx; - /* current port power role (SOURCE or SINK) */ - enum pd_power_role power_role; - /* current port data role (DFP or UFP) */ - enum pd_data_role data_role; - /* - * Higher-level power deliver state machines are enabled if false, - * else they're disabled if bits PD_DISABLED_NO_CONNECTION or - * PD_DISABLED_BY_POLICY are set. - */ - uint32_t pd_disabled_mask; - /* - * Timer for handling TOGGLE_OFF/FORCE_SINK mode when auto-toggle - * enabled. See drp_auto_toggle_next_state() for details. - */ - uint64_t drp_sink_time; -#ifdef CONFIG_USB_PE_SM - /* Power supply reset sequence during a hard reset */ - enum ps_reset_sequence ps_reset_state; -#endif - /* Port polarity */ - enum tcpc_cc_polarity polarity; - /* port flags, see TC_FLAGS_* */ - uint32_t flags; - /* The cc state */ - enum pd_cc_states cc_state; - /* Tasks to notify after TCPC has been reset */ - int tasks_waiting_on_reset; - /* Tasks preventing TCPC from entering low power mode */ - int tasks_preventing_lpm; - /* Voltage on CC pin */ - enum tcpc_cc_voltage_status cc_voltage; - /* Type-C current */ - typec_current_t typec_curr; - /* Type-C current change */ - typec_current_t typec_curr_change; - - /* Selected TCPC CC/Rp values */ - enum tcpc_cc_pull select_cc_pull; - enum tcpc_rp_value select_current_limit_rp; - enum tcpc_rp_value select_collision_rp; -} tc[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* Port dual-role state */ -static volatile __maybe_unused -enum pd_dual_role_states drp_state[CONFIG_USB_PD_PORT_MAX_COUNT] = { - [0 ... (CONFIG_USB_PD_PORT_MAX_COUNT - 1)] = - CONFIG_USB_PD_INITIAL_DRP_STATE}; - -static void set_vconn(int port, int enable); - -/* Forward declare common, private functions */ -static __maybe_unused int reset_device_and_notify(int port); -static __maybe_unused void check_drp_connection(const int port); -static void sink_power_sub_states(int port); -static void set_ccd_mode(int port, bool enable); - -__maybe_unused static void handle_new_power_state(int port); - -static void pd_update_dual_role_config(int port); - -/* Forward declare common, private functions */ -static void set_state_tc(const int port, const enum usb_tc_state new_state); -test_export_static enum usb_tc_state get_state_tc(const int port); - -/* Enable variable for Try.SRC states */ -static uint32_t pd_try_src; -static volatile enum try_src_override_t pd_try_src_override; -static void pd_update_try_source(void); - -static void sink_stop_drawing_current(int port); - -__maybe_unused static bool is_try_src_enabled(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - assert(0); - - return ((pd_try_src_override == TRY_SRC_OVERRIDE_ON) || - (pd_try_src_override == TRY_SRC_NO_OVERRIDE && pd_try_src)); -} - -/* - * Public Functions - * - * NOTE: Functions prefixed with pd_ are defined in usb_pd.h - * Functions prefixed with tc_ are defined int usb_tc_sm.h - */ - -#ifndef CONFIG_USB_PRL_SM - -/* - * These pd_ functions are implemented in common/usb_prl_sm.c - */ - -void pd_transmit_complete(int port, int status) -{ - /* DO NOTHING */ -} - -void pd_execute_hard_reset(int port) -{ - /* DO NOTHING */ -} - -__overridable void pd_set_vbus_discharge(int port, int enable) -{ - /* DO NOTHING */ -} - -#endif /* !CONFIG_USB_PRL_SM */ - -#ifndef CONFIG_USB_PE_SM - -/* - * These pd_ functions are implemented in the PE layer - */ -const uint32_t * const pd_get_src_caps(int port) -{ - return NULL; -} - -uint8_t pd_get_src_cap_cnt(int port) -{ - return 0; -} - -const uint32_t * const pd_get_snk_caps(int port) -{ - return NULL; -} - -uint8_t pd_get_snk_cap_cnt(int port) -{ - return 0; -} - -void pd_set_src_caps(int port, int cnt, uint32_t *src_caps) -{ -} - -int pd_get_rev(int port, enum tcpci_msg_type type) -{ - return PD_REV30; -} - -#endif /* !CONFIG_USB_PR_SM */ - -#ifndef HAS_TASK_CHIPSET -__overridable enum pd_dual_role_states board_tc_get_initial_drp_mode(int port) -{ - /* - * DRP state is typically adjusted as the chipset state is changed. For - * projects which don't include an AP this function can be used for to - * specify what the starting DRP state should be. - */ - return PD_DRP_FORCE_SINK; -} -#endif - -void pd_update_contract(int port) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - if (IS_ATTACHED_SRC(port)) - pd_dpm_request(port, DPM_REQUEST_SRC_CAP_CHANGE); - } -} - -void pd_request_source_voltage(int port, int mv) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - pd_set_max_voltage(mv); - - if (IS_ATTACHED_SNK(port)) - pd_dpm_request(port, DPM_REQUEST_NEW_POWER_LEVEL); - else - pd_dpm_request(port, DPM_REQUEST_PR_SWAP); - - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -void pd_set_external_voltage_limit(int port, int mv) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - pd_set_max_voltage(mv); - - /* Must be in Attached.SNK when this function is called */ - if (get_state_tc(port) == TC_ATTACHED_SNK) - pd_dpm_request(port, DPM_REQUEST_NEW_POWER_LEVEL); - - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -void pd_set_new_power_request(int port) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - /* Must be in Attached.SNK when this function is called */ - if (get_state_tc(port) == TC_ATTACHED_SNK) - pd_dpm_request(port, DPM_REQUEST_NEW_POWER_LEVEL); - } -} - -void tc_request_power_swap(int port) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - /* - * Must be in Attached.SRC or Attached.SNK - */ - if (IS_ATTACHED_SRC(port) || IS_ATTACHED_SNK(port)) { - TC_SET_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS); - - /* Let tc_pr_swap_complete start the Vbus debounce */ - pd_timer_disable(port, TC_TIMER_VBUS_DEBOUNCE); - } - - /* - * TCPCI Rev2 V1.1 4.4.5.4.4 - * Disconnect Detection by the Sink TCPC during a Connection - * - * Upon reception of or prior to transmitting a PR_Swap - * message, the TCPM acting as a Sink shall disable the Sink - * disconnect detection to retain PD message delivery when - * Power Role Swap happens. Disable AutoDischargeDisconnect. - */ - if (IS_ATTACHED_SNK(port)) - tcpm_enable_auto_discharge_disconnect(port, 0); - } -} - -/* Flag to indicate PD comm is disabled on init */ -static int pd_disabled_on_init; - -static void pd_update_pd_comm(void) -{ - int i; - - /* - * Some batteries take much longer time to report its SOC. - * The init function disabled PD comm on startup. Need this - * hook to enable PD comm when the battery level is enough. - */ - if (pd_disabled_on_init && pd_is_battery_capable()) { - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) - pd_comm_enable(i, 1); - pd_disabled_on_init = 0; - } -} -DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, pd_update_pd_comm, HOOK_PRIO_DEFAULT); - -static bool pd_comm_allowed_by_policy(void) -{ - if (system_is_in_rw()) - return true; - - if (vboot_allow_usb_pd()) - return true; - - /* - * If enable PD in RO on a non-EFS2 device, a hard reset will be issued - * when sysjump to RW that makes the device brownout on the dead-battery - * case. Disable PD for this special case as a workaround. - */ - if (!system_is_locked()) { - if (IS_ENABLED(CONFIG_VBOOT_EFS2)) - return true; - - if (pd_is_battery_capable()) - return true; - - pd_disabled_on_init = 1; - } - - return false; -} - -static void tc_policy_pd_enable(int port, int en) -{ - if (en) - atomic_clear_bits(&tc[port].pd_disabled_mask, - PD_DISABLED_BY_POLICY); - else - atomic_or(&tc[port].pd_disabled_mask, PD_DISABLED_BY_POLICY); - - CPRINTS("C%d: PD comm policy %sabled", port, en ? "en" : "dis"); -} - -static void tc_enable_pd(int port, int en) -{ - if (en) - atomic_clear_bits(&tc[port].pd_disabled_mask, - PD_DISABLED_NO_CONNECTION); - else - atomic_or(&tc[port].pd_disabled_mask, - PD_DISABLED_NO_CONNECTION); -} - -__maybe_unused static void tc_enable_try_src(int en) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - assert(0); - - if (en) - atomic_or(&pd_try_src, 1); - else - atomic_clear_bits(&pd_try_src, 1); -} - -/* - * Exit all modes due to a detach event - * Note: this skips the ExitMode VDM steps in the PE because it is assumed the - * partner is not present to receive them, and the PE will no longer be running. - */ -static void tc_set_modes_exit(int port) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM) && - IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) { - pd_dfp_exit_mode(port, TCPCI_MSG_SOP, 0, 0); - pd_dfp_exit_mode(port, TCPCI_MSG_SOP_PRIME, 0, 0); - pd_dfp_exit_mode(port, TCPCI_MSG_SOP_PRIME_PRIME, 0, 0); - } -} - -static void tc_detached(int port) -{ - TC_CLR_FLAG(port, TC_FLAGS_TS_DTS_PARTNER); - hook_notify(HOOK_USB_PD_DISCONNECT); - tc_pd_connection(port, 0); - tcpm_debug_accessory(port, 0); - set_ccd_mode(port, 0); - tc_set_modes_exit(port); - if (IS_ENABLED(CONFIG_USB_PRL_SM)) - prl_set_default_pd_revision(port); - - /* Clear any mux connection on detach */ - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - usb_mux_set(port, USB_PD_MUX_NONE, - USB_SWITCH_DISCONNECT, tc[port].polarity); -} - -static inline void pd_set_dual_role_and_event(int port, - enum pd_dual_role_states state, uint32_t event) -{ - drp_state[port] = state; - - if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - pd_update_try_source(); - - if (event != 0) - task_set_event(PD_PORT_TO_TASK_ID(port), event); -} - -void pd_set_dual_role(int port, enum pd_dual_role_states state) -{ - pd_set_dual_role_and_event(port, state, PD_EVENT_UPDATE_DUAL_ROLE); -} - -int pd_comm_is_enabled(int port) -{ - return tc_get_pd_enabled(port); -} - -void pd_request_data_swap(int port) -{ - /* - * Must be in Attached.SRC, Attached.SNK, DebugAccessory.SNK, - * or UnorientedDebugAccessory.SRC when this function - * is called - */ - if (IS_ATTACHED_SRC(port) || IS_ATTACHED_SNK(port)) { - TC_SET_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP); - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -/* Return true if partner port is known to be PD capable. */ -bool pd_capable(int port) -{ - return !!TC_CHK_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE); -} - -/* - * Return true if we transition through Unattached.SNK, but we're still waiting - * to receive source caps from the partner. This indicates that the PD - * capabilities are not yet known. - */ -bool pd_waiting_on_partner_src_caps(int port) -{ - return !pd_get_src_cap_cnt(port); -} - -enum pd_dual_role_states pd_get_dual_role(int port) -{ - return drp_state[port]; -} - -#ifdef CONFIG_CMD_PD_DEV_DUMP_INFO -static inline void pd_dev_dump_info(uint16_t dev_id, uint32_t *hash) -{ - int j; - - ccprintf("DevId:%d.%d Hash:", HW_DEV_ID_MAJ(dev_id), - HW_DEV_ID_MIN(dev_id)); - for (j = 0; j < PD_RW_HASH_SIZE / 4; j++) - ccprintf(" %08x ", hash[j]); - ccprintf("\n"); -} -#endif /* CONFIG_CMD_PD_DEV_DUMP_INFO */ - -const char *tc_get_current_state(int port) -{ - if (IS_ENABLED(USB_PD_DEBUG_LABELS)) - return tc_state_names[get_state_tc(port)]; - else - return ""; -} - -uint32_t tc_get_flags(int port) -{ - return tc[port].flags; -} - -int tc_is_attached_src(int port) -{ - return IS_ATTACHED_SRC(port); -} - -int tc_is_attached_snk(int port) -{ - return IS_ATTACHED_SNK(port); -} - -void tc_pd_connection(int port, int en) -{ - if (en) { - bool new_pd_capable = false; - - if (!TC_CHK_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE)) - new_pd_capable = true; - - TC_SET_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE); - /* If a PD device is attached then disable deep sleep */ - if (IS_ENABLED(CONFIG_LOW_POWER_IDLE) && - !IS_ENABLED(CONFIG_USB_PD_TCPC_ON_CHIP)) { - disable_sleep(SLEEP_MASK_USB_PD); - } - - /* - * Update the mux state, only when the PD capable flag - * transitions from 0 to 1. This ensures that PD charger - * devices, without data capability are not marked as having - * USB. - */ - if (new_pd_capable) - set_usb_mux_with_current_data_role(port); - } else { - TC_CLR_FLAG(port, TC_FLAGS_PARTNER_PD_CAPABLE); - /* If a PD device isn't attached then enable deep sleep */ - if (IS_ENABLED(CONFIG_LOW_POWER_IDLE) && - !IS_ENABLED(CONFIG_USB_PD_TCPC_ON_CHIP)) { - int i; - - /* If all ports are not connected, allow the sleep */ - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - if (pd_capable(i)) - break; - } - if (i == board_get_usb_pd_port_count()) - enable_sleep(SLEEP_MASK_USB_PD); - } - } -} - -void tc_ctvpd_detected(int port) -{ - TC_SET_FLAG(port, TC_FLAGS_CTVPD_DETECTED); -} - -void pd_try_vconn_src(int port) -{ - set_vconn(port, 1); -} - -int tc_check_vconn_swap(int port) -{ - if (IS_ENABLED(CONFIG_USBC_VCONN)) { - if (TC_CHK_FLAG(port, TC_FLAGS_REJECT_VCONN_SWAP)) - return 0; - - return pd_check_vconn_swap(port); - } else - return 0; -} - -void tc_pr_swap_complete(int port, bool success) -{ - if (IS_ATTACHED_SNK(port)) { - /* - * Give the ADCs in the TCPC or PPC time to react following - * a PS_RDY message received during a SRC to SNK swap. - * Note: This is empirically determined, not strictly - * part of the USB PD spec. - * Note: Swap in progress should not be cleared until the - * debounce is completed. - */ - pd_timer_enable(port, TC_TIMER_VBUS_DEBOUNCE, PD_T_DEBOUNCE); - } else { - /* PR Swap is no longer in progress */ - TC_CLR_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS); - - /* - * AutoDischargeDisconnect was turned off near the SNK->SRC - * PR-Swap message. If the swap was a success, Vbus should be - * valid, so re-enable AutoDischargeDisconnect - */ - if (success) - tcpm_enable_auto_discharge_disconnect(port, 1); - } -} - -void tc_prs_src_snk_assert_rd(int port) -{ - /* - * Must be in Attached.SRC or UnorientedDebugAccessory.SRC - * when this function is called - */ - if (IS_ATTACHED_SRC(port)) { - /* - * Transition to Attached.SNK to - * DebugAccessory.SNK assert Rd - */ - TC_SET_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP); - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -void tc_prs_snk_src_assert_rp(int port) -{ - /* - * Must be in Attached.SNK or DebugAccessory.SNK - * when this function is called - */ - if (IS_ATTACHED_SNK(port)) { - /* - * Transition to Attached.SRC or - * UnorientedDebugAccessory.SRC to assert Rp - */ - TC_SET_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP); - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -/* - * Hard Reset is being requested. This should not allow a TC connection - * to go to an unattached state until the connection is recovered from - * the hard reset. It is possible for a Hard Reset to cause a timeout - * in trying to recover and an additional Hard Reset would be issued. - * During this entire process it is important that the TC is not allowed - * to go to an unattached state. - * - * Type-C Spec Rev 2.0 section 4.5.2.2.5.2 - * Exiting from Attached.SNK State - * A port that is not a V CONN-Powered USB Device and is not in the - * process of a USB PD PR_Swap or a USB PD Hard Reset or a USB PD - * FR_Swap shall transition to Unattached.SNK - */ -void tc_hard_reset_request(int port) -{ - TC_SET_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void tc_try_src_override(enum try_src_override_t ov) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - assert(0); - - if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) { - switch (ov) { - case TRY_SRC_OVERRIDE_OFF: /* 0 */ - pd_try_src_override = TRY_SRC_OVERRIDE_OFF; - break; - case TRY_SRC_OVERRIDE_ON: /* 1 */ - pd_try_src_override = TRY_SRC_OVERRIDE_ON; - break; - default: - pd_try_src_override = TRY_SRC_NO_OVERRIDE; - } - } -} - -enum try_src_override_t tc_get_try_src_override(void) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - assert(0); - - return pd_try_src_override; -} - -void tc_snk_power_off(int port) -{ - if (IS_ATTACHED_SNK(port)) { - TC_SET_FLAG(port, TC_FLAGS_POWER_OFF_SNK); - sink_stop_drawing_current(port); - } -} - -int tc_src_power_on(int port) -{ - /* - * Check our OC event counter. If we've exceeded our threshold, then - * let's latch our source path off to prevent continuous cycling. When - * the PD state machine detects a disconnection on the CC lines, we will - * reset our OC event counter. - */ - if (IS_ENABLED(CONFIG_USBC_OCP) && usbc_ocp_is_port_latched_off(port)) - return EC_ERROR_ACCESS_DENIED; - - if (IS_ATTACHED_SRC(port)) - return pd_set_power_supply_ready(port); - - return 0; -} - -void tc_src_power_off(int port) -{ - /* Remove VBUS */ - pd_power_supply_reset(port); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - charge_manager_set_ceil(port, CEIL_REQUESTOR_PD, - CHARGE_CEIL_NONE); -} - -/* Set what role the partner is right now, for the PPC and OCP module */ -static void tc_set_partner_role(int port, enum ppc_device_role role) -{ - if (IS_ENABLED(CONFIG_USBC_PPC)) - ppc_dev_is_connected(port, role); - - if (IS_ENABLED(CONFIG_USBC_OCP)) { - usbc_ocp_snk_is_connected(port, role == PPC_DEV_SNK); - /* - * Clear the overcurrent event counter - * since we've detected a disconnect. - */ - if (role == PPC_DEV_DISCONNECTED) - usbc_ocp_clear_event_counter(port); - } -} - -/* - * Depending on the load on the processor and the tasks running - * it can take a while for the task associated with this port - * to run. So build in 1ms delays, for up to 300ms, to wait for - * the suspend to actually happen. - */ -#define SUSPEND_SLEEP_DELAY 1 -#define SUSPEND_SLEEP_RETRIES 300 - -void pd_set_suspend(int port, int suspend) -{ - if (pd_is_port_enabled(port) == !suspend) - return; - - /* Track if we are suspended or not */ - if (suspend) { - int wait = 0; - - TC_SET_FLAG(port, TC_FLAGS_REQUEST_SUSPEND); - - /* - * Avoid deadlock when running from task - * which we are going to suspend - */ - if (PD_PORT_TO_TASK_ID(port) == task_get_current()) - return; - - task_wake(PD_PORT_TO_TASK_ID(port)); - - /* Sleep this task if we are not suspended */ - while (pd_is_port_enabled(port)) { - if (++wait > SUSPEND_SLEEP_RETRIES) { - CPRINTS("C%d: NOT SUSPENDED after %dms", - port, wait * SUSPEND_SLEEP_DELAY); - return; - } - msleep(SUSPEND_SLEEP_DELAY); - } - } else { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_SUSPEND); - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -void pd_set_error_recovery(int port) -{ - TC_SET_FLAG(port, TC_FLAGS_REQUEST_ERROR_RECOVERY); -} - -int pd_is_port_enabled(int port) -{ - /* - * Checking get_state_tc(port) from another task isn't safe since it - * can return TC_DISABLED before tc_cc_open_entry and tc_disabled_entry - * are complete. So check TC_FLAGS_SUSPENDED instead. - */ - return !TC_CHK_FLAG(port, TC_FLAGS_SUSPENDED); -} - -int pd_fetch_acc_log_entry(int port) -{ - if (IS_ENABLED(CONFIG_USB_PE_SM)) - pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_GET_LOG, NULL, 0); - - return EC_RES_SUCCESS; -} - -enum tcpc_cc_polarity pd_get_polarity(int port) -{ - return tc[port].polarity; -} - -enum pd_data_role pd_get_data_role(int port) -{ - return tc[port].data_role; -} - -enum pd_power_role pd_get_power_role(int port) -{ - return tc[port].power_role; -} - -enum pd_cc_states pd_get_task_cc_state(int port) -{ - return tc[port].cc_state; -} - -uint8_t pd_get_task_state(int port) -{ - return get_state_tc(port); -} - -bool pd_get_vconn_state(int port) -{ - return !!TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON); -} - -const char *pd_get_task_state_name(int port) -{ - return tc_get_current_state(port); -} - -void pd_vbus_low(int port) -{ - TC_CLR_FLAG(port, TC_FLAGS_VBUS_NEVER_LOW); -} - -int pd_is_connected(int port) -{ - return (IS_ATTACHED_SRC(port) || - (IS_ENABLED(CONFIG_USB_PE_SM) && - ((get_state_tc(port) == TC_CT_UNATTACHED_SNK) || - (get_state_tc(port) == TC_CT_ATTACHED_SNK))) || - IS_ATTACHED_SNK(port)); -} - -bool pd_is_disconnected(int port) -{ - return !pd_is_connected(port); -} - -/* - * PD functions which query our fixed PDO flags. Both the source and sink - * capabilities can present these values, and they should match between the two - * for compliant partners. - */ -static bool pd_check_fixed_flag(int port, uint32_t flag) -{ - uint32_t fixed_pdo; - - if (pd_get_src_cap_cnt(port) != 0) - fixed_pdo = *pd_get_src_caps(port); - else if (pd_get_snk_cap_cnt(port) != 0) - fixed_pdo = *pd_get_snk_caps(port); - else - return false; - - /* - * Error check that first PDO is fixed, as 6.4.1 Capabilities requires - * in the Power Delivery Specification. - * "The vSafe5V Fixed Supply Object Shall always be the first object" - */ - if ((fixed_pdo & PDO_TYPE_MASK) != PDO_TYPE_FIXED) - return false; - - return fixed_pdo & flag; -} - -bool pd_get_partner_data_swap_capable(int port) -{ - return pd_check_fixed_flag(port, PDO_FIXED_DATA_SWAP); -} - -bool pd_get_partner_usb_comm_capable(int port) -{ - return pd_check_fixed_flag(port, PDO_FIXED_COMM_CAP); -} - -bool pd_get_partner_dual_role_power(int port) -{ - return pd_check_fixed_flag(port, PDO_FIXED_DUAL_ROLE); -} - -bool pd_get_partner_unconstr_power(int port) -{ - return pd_check_fixed_flag(port, PDO_FIXED_UNCONSTRAINED); -} - -static void bc12_role_change_handler(int port, enum pd_data_role prev_data_role, - enum pd_data_role data_role) -{ - int event = 0; - int task_id = USB_CHG_PORT_TO_TASK_ID(port); - bool role_changed = (data_role != prev_data_role); - - if (!IS_ENABLED(CONFIG_BC12_DETECT_DATA_ROLE_TRIGGER)) - return; - - /* Get the data role of our device */ - switch (data_role) { - case PD_ROLE_UFP: - /* Only trigger BC12 detection on a role change */ - if (role_changed) - event = USB_CHG_EVENT_DR_UFP; - break; - case PD_ROLE_DFP: - /* Only trigger BC12 host mode on a role change */ - if (role_changed) - event = USB_CHG_EVENT_DR_DFP; - break; - case PD_ROLE_DISCONNECTED: - event = USB_CHG_EVENT_CC_OPEN; - break; - default: - return; - } - - if (event) - task_set_event(task_id, event); -} - -/* - * TCPC CC/Rp management - */ -static void typec_select_pull(int port, enum tcpc_cc_pull pull) -{ - tc[port].select_cc_pull = pull; -} -void typec_select_src_current_limit_rp(int port, enum tcpc_rp_value rp) -{ - tc[port].select_current_limit_rp = rp; - if (IS_ATTACHED_SRC(port)) - TC_SET_FLAG(port, TC_FLAGS_UPDATE_CURRENT); -} -__overridable int typec_get_default_current_limit_rp(int port) -{ - return CONFIG_USB_PD_PULLUP; -} -void typec_select_src_collision_rp(int port, enum tcpc_rp_value rp) -{ - tc[port].select_collision_rp = rp; -} -static enum tcpc_rp_value typec_get_active_select_rp(int port) -{ - /* Explicit contract will use the collision Rp */ - if (IS_ENABLED(CONFIG_USB_PD_REV30) && - pe_is_explicit_contract(port)) - return tc[port].select_collision_rp; - return tc[port].select_current_limit_rp; -} -int typec_update_cc(int port) -{ - int rv; - enum tcpc_cc_pull pull = tc[port].select_cc_pull; - enum tcpc_rp_value rp = typec_get_active_select_rp(port); - - rv = tcpm_select_rp_value(port, rp); - if (rv) - return rv; - - return tcpm_set_cc(port, pull); -} - -#ifdef CONFIG_USB_PE_SM -/* - * This function performs a source hard reset. It should be called - * repeatedly until a true value is returned, signaling that the - * source hard reset is complete. A false value is returned otherwise. - */ -static bool tc_perform_src_hard_reset(int port) -{ - switch (tc[port].ps_reset_state) { - case PS_STATE0: - /* Remove VBUS */ - tc_src_power_off(port); - - /* Turn off VCONN */ - set_vconn(port, 0); - - /* Set role to DFP */ - tc_set_data_role(port, PD_ROLE_DFP); - - tc[port].ps_reset_state = PS_STATE1; - pd_timer_enable(port, TC_TIMER_TIMEOUT, PD_T_SRC_RECOVER); - return false; - case PS_STATE1: - /* Enable VBUS */ - tc_src_power_on(port); - - /* Update the Rp Value */ - typec_update_cc(port); - - /* Turn off VCONN */ - set_vconn(port, 1); - - tc[port].ps_reset_state = PS_STATE2; - pd_timer_enable(port, TC_TIMER_TIMEOUT, - PD_POWER_SUPPLY_TURN_ON_DELAY); - return false; - case PS_STATE2: - /* Tell Policy Engine Hard Reset is complete */ - pe_ps_reset_complete(port); - - tc[port].ps_reset_state = PS_STATE0; - return true; - } - - /* - * This return is added to appease the compiler. It should - * never be reached because the switch handles all possible - * cases of the enum ps_reset_sequence type. - */ - return true; -} - -/* - * Wait for recovery after a hard reset. Call repeatedly until true is - * returned, signaling that the hard reset is complete. - */ -static bool tc_perform_snk_hard_reset(int port) -{ - switch (tc[port].ps_reset_state) { - case PS_STATE0: - /* Hard reset sets us back to default data role */ - tc_set_data_role(port, PD_ROLE_UFP); - - /* - * When VCONN is supported, the Hard Reset Shall cause - * the Port with the Rd resistor asserted to turn off - * VCONN. - */ - if (IS_ENABLED(CONFIG_USBC_VCONN) && - TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON)) - set_vconn(port, 0); - - /* Wait up to tVSafe0V for Vbus to disappear */ - tc[port].ps_reset_state = PS_STATE1; - pd_timer_enable(port, TC_TIMER_TIMEOUT, PD_T_SAFE_0V); - return false; - case PS_STATE1: - if (pd_check_vbus_level(port, VBUS_SAFE0V)) { - /* - * Partner dropped Vbus, reduce our current consumption - * and await its return. - */ - sink_stop_drawing_current(port); - - tcpm_enable_auto_discharge_disconnect(port, 0); - - /* Move on to waiting for the return of Vbus */ - tc[port].ps_reset_state = PS_STATE2; - pd_timer_enable(port, TC_TIMER_TIMEOUT, - PD_T_SRC_RECOVER_MAX + - PD_T_SRC_TURN_ON); - } - - if (pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) { - /* - * No Vbus drop likely indicates a non-PD port partner, - * move to the next stage anyway. - */ - tc[port].ps_reset_state = PS_STATE2; - pd_timer_enable(port, TC_TIMER_TIMEOUT, - PD_T_SRC_RECOVER_MAX + - PD_T_SRC_TURN_ON); - } - return false; - case PS_STATE2: - /* - * Look for the voltage to be above disconnect. Since we didn't - * drop our draw on non-PD partners, they may have dipped below - * vSafe5V but still be in a valid connected voltage. - */ - if (!pd_check_vbus_level(port, VBUS_REMOVED)) { - /* - * Inform policy engine that power supply - * reset is complete - */ - tc[port].ps_reset_state = PS_STATE0; - pe_ps_reset_complete(port); - - /* - * Now that VBUS is back, let's notify charge manager - * regarding the source's current capabilities. - * sink_power_sub_states() reacts to changes in CC - * terminations, however during a HardReset, the - * terminations of a non-PD port partner will not - * change. Therefore, set the debounce time to right - * now, such that we'll actually reset the correct input - * current limit. - */ - pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, 0); - sink_power_sub_states(port); - - /* Power is back, Enable AutoDischargeDisconnect */ - tcpm_enable_auto_discharge_disconnect(port, 1); - return true; - } - /* - * If Vbus isn't back after wait + tSrcTurnOn, go unattached - */ - if (pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) { - tc[port].ps_reset_state = PS_STATE0; - set_state_tc(port, TC_UNATTACHED_SNK); - return true; - } - } - - return false; -} -#endif /* CONFIG_USB_PE_SM */ - -void tc_start_error_recovery(int port) -{ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - /* - * The port should transition to the ErrorRecovery state - * from any other state when directed. - */ - set_state_tc(port, TC_ERROR_RECOVERY); -} - -static void restart_tc_sm(int port, enum usb_tc_state start_state) -{ - int res; - - /* Clear flags before we transitions states */ - tc[port].flags = 0; - - res = tcpm_init(port); - - CPRINTS("C%d: TCPC init %s", port, res ? "failed" : "ready"); - - /* - * Update the Rp Value. We don't need to update CC lines though as that - * happens in below set_state transition. - */ - typec_select_src_current_limit_rp(port, - typec_get_default_current_limit_rp(port)); - - /* Disable if restart failed, otherwise start in default state. */ - set_state_tc(port, res ? TC_DISABLED : start_state); - - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - /* Initialize USB mux to its default state */ - usb_mux_init(port); - - if (IS_ENABLED(CONFIG_USBC_PPC)) { - /* - * Wait to initialize the PPC after tcpc, which sets - * the correct Rd values; otherwise the TCPC might - * not be pulling the CC lines down when the PPC connects the - * CC lines from the USB connector to the TCPC cause the source - * to drop Vbus causing a brown out. - */ - ppc_init(port); - } - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - /* - * Only initialize PD supplier current limit to 0. - * Defer initializing type-C supplier current limit - * to Unattached.SNK or Attached.SNK. - */ - pd_set_input_current_limit(port, 0, 0); - charge_manager_update_dualrole(port, CAP_UNKNOWN); - } - - /* - * 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 Error Recovery case, because TC_ERROR_RECOVERY - * reinitializes the TC state machine. This also covers the implicit - * case when PD is suspended and resumed or when the state machine is - * first initialized. - */ - if (IS_ENABLED(CONFIG_USB_PRL_SM)) - prl_set_default_pd_revision(port); - -#ifdef CONFIG_USB_PE_SM - tc_enable_pd(port, 0); - tc[port].ps_reset_state = PS_STATE0; -#endif -} - -void tc_state_init(int port) -{ - enum usb_tc_state first_state; - - /* For test builds, replicate static initialization */ - if (IS_ENABLED(TEST_BUILD)) { - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; ++i) { - memset(&tc[i], 0, sizeof(tc[i])); - drp_state[i] = CONFIG_USB_PD_INITIAL_DRP_STATE; - } - } - - /* If port is not available, there is nothing to initialize */ - if (port >= board_get_usb_pd_port_count()) { - tc_enable_pd(port, 0); - TC_SET_FLAG(port, TC_FLAGS_REQUEST_SUSPEND); - return; - } - - - /* Allow system to set try src enable */ - if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - tc_try_src_override(TRY_SRC_NO_OVERRIDE); - - /* - * Set initial PD communication policy. - */ - tc_policy_pd_enable(port, pd_comm_allowed_by_policy()); - -#ifdef HAS_TASK_CHIPSET - /* Set dual-role state based on chipset power state */ - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) - pd_set_dual_role_and_event(port, PD_DRP_FORCE_SINK, 0); - else if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND)) - pd_set_dual_role_and_event(port, pd_get_drp_state_in_suspend(), 0); - else /* CHIPSET_STATE_ON */ - pd_set_dual_role_and_event(port, PD_DRP_TOGGLE_ON, 0); -#else - pd_set_dual_role_and_event(port, board_tc_get_initial_drp_mode(port), 0); -#endif - - /* - * We are going to apply CC open (start with ErrorRecovery state) - * unless there is something which forbids us to do that (one of - * conditions below is true) - */ - first_state = TC_ERROR_RECOVERY; - - /* - * If we just lost power, don't apply CC open. Otherwise we would boot - * loop, and if this is a fresh power on, then we know there isn't any - * stale PD state as well. - */ - if (system_get_reset_flags() & - (EC_RESET_FLAG_BROWNOUT | EC_RESET_FLAG_POWER_ON)) { - first_state = TC_UNATTACHED_SNK; - } - - /* - * If this is non-EFS2 device, battery is not present and EC RO doesn't - * keep power-on reset flag after reset caused by H1, then don't apply - * CC open because it will cause brown out. - * - * Please note that we are checking if CONFIG_BOARD_RESET_AFTER_POWER_ON - * is defined now, but actually we need to know if it was enabled in - * EC RO! It was assumed that if CONFIG_BOARD_RESET_AFTER_POWER_ON is - * defined now it was defined in EC RO too. - */ - if (!IS_ENABLED(CONFIG_BOARD_RESET_AFTER_POWER_ON) && - !IS_ENABLED(CONFIG_VBOOT_EFS2) && IS_ENABLED(CONFIG_BATTERY) && - (battery_is_present() == BP_NO)) { - first_state = TC_UNATTACHED_SNK; - } - - if (first_state == TC_UNATTACHED_SNK) { - /* Turn off any previous sourcing */ - tc_src_power_off(port); - set_vconn(port, 0); - } - -#ifdef CONFIG_USB_PD_TCPC_BOARD_INIT - /* Board specific TCPC init */ - board_tcpc_init(); -#endif - - /* - * Start with ErrorRecovery state if we can to put us in - * a clean state from any previous boots. - */ - restart_tc_sm(port, first_state); -} - -enum pd_cable_plug tc_get_cable_plug(int port) -{ - /* - * Messages sent by this state machine are always from a DFP/UFP, - * i.e. the chromebook. - */ - return PD_PLUG_FROM_DFP_UFP; -} - -void pd_comm_enable(int port, int en) -{ - tc_policy_pd_enable(port, en); -} - -uint8_t tc_get_polarity(int port) -{ - return tc[port].polarity; -} - -uint8_t tc_get_pd_enabled(int port) -{ - return !tc[port].pd_disabled_mask; -} - -bool pd_alt_mode_capable(int port) -{ - return IS_ENABLED(CONFIG_USB_PE_SM) && tc_get_pd_enabled(port); -} - -void tc_set_power_role(int port, enum pd_power_role role) -{ - tc[port].power_role = role; -} - -/* - * Private Functions - */ - -/* Set GPIO_CCD_MODE_ODL gpio */ -static void set_ccd_mode(const int port, const bool enable) -{ - if (IS_ENABLED(CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT) && - port == CONFIG_CCD_USBC_PORT_NUMBER) { - if (enable) - CPRINTS("Asserting GPIO_CCD_MODE_ODL"); - gpio_set_level(_GPIO_CCD_MODE_ODL, !enable); - } -} - -/* Set the TypeC state machine to a new state. */ -static void set_state_tc(const int port, const enum usb_tc_state new_state) -{ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - set_state(port, &tc[port].ctx, &tc_states[new_state]); -} - -/* Get the current TypeC state. */ -test_export_static enum usb_tc_state get_state_tc(const int port) -{ - /* Default to returning TC_STATE_COUNT if no state has been set */ - if (tc[port].ctx.current == NULL) - return TC_STATE_COUNT; - else - return tc[port].ctx.current - &tc_states[0]; -} - -/* Get the previous TypeC state. */ -static enum usb_tc_state get_last_state_tc(const int port) -{ - return tc[port].ctx.previous - &tc_states[0]; -} - -static void print_current_state(const int port) -{ - if (IS_ENABLED(USB_PD_DEBUG_LABELS)) - CPRINTS_L1("C%d: %s", port, tc_state_names[get_state_tc(port)]); - else - CPRINTS("C%d: tc-st%d", port, get_state_tc(port)); -} - -static void handle_device_access(int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER) && - get_state_tc(port) == TC_LOW_POWER_MODE) { - tc_start_event_loop(port); - pd_timer_enable(port, TC_TIMER_LOW_POWER_TIME, - PD_LPM_DEBOUNCE_US); - } -} - -void tc_event_check(int port, int evt) -{ -#ifdef DEBUG_PRINT_FLAG_AND_EVENT_NAMES - if (evt != TASK_EVENT_TIMER) - print_bits(port, "Event", evt, event_bit_names, - ARRAY_SIZE(event_bit_names)); -#endif - - if (evt & PD_EXIT_LOW_POWER_EVENT_MASK) - TC_SET_FLAG(port, TC_FLAGS_CHECK_CONNECTION); - - if (evt & PD_EVENT_DEVICE_ACCESSED) - handle_device_access(port); - - if (evt & PD_EVENT_TCPC_RESET) - reset_device_and_notify(port); - - if (evt & PD_EVENT_RX_HARD_RESET) - pd_execute_hard_reset(port); - - if (evt & PD_EVENT_SEND_HARD_RESET) { - /* Pass Hard Reset request to PE layer if available */ - if (IS_ENABLED(CONFIG_USB_PE_SM) && tc_get_pd_enabled(port)) - pd_dpm_request(port, DPM_REQUEST_HARD_RESET_SEND); - } - -#ifdef CONFIG_POWER_COMMON - if (IS_ENABLED(CONFIG_POWER_COMMON)) { - if (evt & PD_EVENT_POWER_STATE_CHANGE) - handle_new_power_state(port); - } -#endif /* CONFIG_POWER_COMMON */ - - if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) { - int i; - - /* - * Notify all ports of sysjump - */ - if (evt & PD_EVENT_SYSJUMP) { - for (i = 0; i < - CONFIG_USB_PD_PORT_MAX_COUNT; i++) - dpm_set_mode_exit_request(i); - notify_sysjump_ready(); - } - } - - if (evt & PD_EVENT_UPDATE_DUAL_ROLE) - pd_update_dual_role_config(port); -} - -/* - * CC values for regular sources and Debug sources (aka DTS) - * - * Source type Mode of Operation CC1 CC2 - * --------------------------------------------- - * Regular Default USB Power RpUSB Open - * Regular USB-C @ 1.5 A Rp1A5 Open - * Regular USB-C @ 3 A Rp3A0 Open - * DTS Default USB Power Rp3A0 Rp1A5 - * DTS USB-C @ 1.5 A Rp1A5 RpUSB - * DTS USB-C @ 3 A Rp3A0 RpUSB - */ - -void tc_set_data_role(int port, enum pd_data_role role) -{ - enum pd_data_role prev_data_role; - - prev_data_role = tc[port].data_role; - tc[port].data_role = role; - - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - set_usb_mux_with_current_data_role(port); - - /* - * Run any board-specific code for role swap (e.g. setting OTG signals - * to SoC). - */ - pd_execute_data_swap(port, role); - - /* - * For BC1.2 detection that is triggered on data role change events - * instead of VBUS changes, need to set an event to wake up the USB_CHG - * task and indicate the current data role. - */ - bc12_role_change_handler(port, prev_data_role, tc[port].data_role); - - /* Notify TCPC of role update */ - tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role); -} - -static void sink_stop_drawing_current(int port) -{ - pd_set_input_current_limit(port, 0, 0); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - typec_set_input_current_limit(port, 0, 0); - charge_manager_set_ceil(port, - CEIL_REQUESTOR_PD, CHARGE_CEIL_NONE); - } -} - -static void pd_update_try_source(void) -{ -#ifdef CONFIG_USB_PD_TRY_SRC - tc_enable_try_src(pd_is_try_source_capable()); -#endif -} -DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, pd_update_try_source, HOOK_PRIO_DEFAULT); - -static void set_vconn(int port, int enable) -{ - if (enable) - TC_SET_FLAG(port, TC_FLAGS_VCONN_ON); - else - TC_CLR_FLAG(port, TC_FLAGS_VCONN_ON); - - /* - * Check our OC event counter. If we've exceeded our threshold, then - * let's latch our source path off to prevent continuous cycling. When - * the PD state machine detects a disconnection on the CC lines, we will - * reset our OC event counter. - */ - if (IS_ENABLED(CONFIG_USBC_OCP) && - enable && usbc_ocp_is_port_latched_off(port)) - return; - - /* - * Disable PPC Vconn first then TCPC in case the voltage feeds back - * to TCPC and damages. - */ - if (IS_ENABLED(CONFIG_USBC_PPC_VCONN) && !enable) - ppc_set_vconn(port, 0); - - /* - * Some TCPCs/PPC combinations can trigger OVP if the TCPC doesn't - * source VCONN. This happens if the TCPC will trip OVP with 5V, and the - * PPC doesn't isolate the TCPC from VCONN when sourcing. But, some PPCs - * which do isolate the TCPC can't handle 5V on its host-side CC pins, - * so the TCPC shouldn't source VCONN in those cases. - * - * In the first case, both TCPC and PPC will potentially source Vconn, - * but that should be okay since Vconn has "make before break" - * electrical requirements when swapping anyway. - * - * See b/72961003 and b/180973460 - */ - tcpm_set_vconn(port, enable); - - if (IS_ENABLED(CONFIG_USBC_PPC_VCONN) && enable) - ppc_set_vconn(port, 1); -} - -/* This must only be called from the PD task */ -static void pd_update_dual_role_config(int port) -{ - if (tc[port].power_role == PD_ROLE_SOURCE && - (drp_state[port] == PD_DRP_FORCE_SINK || - (drp_state[port] == PD_DRP_TOGGLE_OFF && - get_state_tc(port) == TC_UNATTACHED_SRC))) { - /* - * Change to sink if port is currently a source AND (new DRP - * state is force sink OR new DRP state is toggle off and we are - * in the source disconnected state). - */ - set_state_tc(port, TC_UNATTACHED_SNK); - } else if (tc[port].power_role == PD_ROLE_SINK && - drp_state[port] == PD_DRP_FORCE_SOURCE) { - /* - * Change to source if port is currently a sink and the - * new DRP state is force source. - */ - set_state_tc(port, TC_UNATTACHED_SRC); - } -} - -__maybe_unused static void handle_new_power_state(int port) -{ - if (!IS_ENABLED(CONFIG_POWER_COMMON)) - assert(0); - - if (IS_ENABLED(CONFIG_POWER_COMMON) && - IS_ENABLED(CONFIG_USB_PE_SM)) { - if (chipset_in_or_transitioning_to_state( - CHIPSET_STATE_ANY_OFF)) { - /* - * The SoC will negotiate alternate mode again when it - * boots up - */ - dpm_set_mode_exit_request(port); - } - } - - /* - * If the sink port was sourcing Vconn, and can no longer, request a - * hard reset on this port to restore Vconn to the source. - */ - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - if (tc_is_vconn_src(port) && tc_is_attached_snk(port) && - !pd_check_vconn_swap(port)) - pd_dpm_request(port, DPM_REQUEST_HARD_RESET_SEND); - } - - /* - * TC_FLAGS_UPDATE_USB_MUX is set on chipset startup and shutdown. - * Set the USB mux according to the new power state. If the chipset - * is transitioning to OFF, this disconnects USB and DP mux. - * - * Transitions to and from suspend states do not change the USB mux - * or the alternate mode configuration. - */ - if (TC_CHK_FLAG(port, TC_FLAGS_UPDATE_USB_MUX)) { - TC_CLR_FLAG(port, TC_FLAGS_UPDATE_USB_MUX); - set_usb_mux_with_current_data_role(port); - } -} - -#ifdef CONFIG_USBC_VCONN_SWAP -void pd_request_vconn_swap_off(int port) -{ - if (get_state_tc(port) == TC_ATTACHED_SRC || - get_state_tc(port) == TC_ATTACHED_SNK) { - TC_SET_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_OFF); - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -void pd_request_vconn_swap_on(int port) -{ - if (get_state_tc(port) == TC_ATTACHED_SRC || - get_state_tc(port) == TC_ATTACHED_SNK) { - TC_SET_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON); - task_wake(PD_PORT_TO_TASK_ID(port)); - } -} - -void pd_request_vconn_swap(int port) -{ - pd_dpm_request(port, DPM_REQUEST_VCONN_SWAP); -} -#endif - -int tc_is_vconn_src(int port) -{ - if (IS_ENABLED(CONFIG_USBC_VCONN)) - return TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON); - else - return 0; -} - -static __maybe_unused int reset_device_and_notify(int port) -{ - int rv; - int task, waiting_tasks; - - /* This should only be called from the PD task */ - assert(port == TASK_ID_TO_PD_PORT(task_get_current())); - - TC_SET_FLAG(port, TC_FLAGS_LPM_TRANSITION); - rv = tcpm_init(port); - TC_CLR_FLAG(port, TC_FLAGS_LPM_TRANSITION); - TC_CLR_FLAG(port, TC_FLAGS_LPM_ENGAGED); - tc_start_event_loop(port); - - CPRINTS("C%d: TCPC init %s", port, rv ? "failed!" : "ready"); - - /* - * Before getting the other tasks that are waiting, clear the reset - * event from this PD task to prevent multiple reset/init events - * occurring. - * - * The double reset event happens when the higher priority PD interrupt - * task gets an interrupt during the above tcpm_init function. When that - * occurs, the higher priority task waits correctly for us to finish - * waking the TCPC, but it has also set PD_EVENT_TCPC_RESET again, which - * would result in a second, unnecessary init. - */ - atomic_clear_bits(task_get_event_bitmap(task_get_current()), - PD_EVENT_TCPC_RESET); - - waiting_tasks = atomic_clear(&tc[port].tasks_waiting_on_reset); - - /* Wake up all waiting tasks. */ - while (waiting_tasks) { - task = __fls(waiting_tasks); - waiting_tasks &= ~BIT(task); - task_set_event(task, TASK_EVENT_PD_AWAKE); - } - - return rv; -} - -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER -void pd_wait_exit_low_power(int port) -{ - if (!TC_CHK_FLAG(port, TC_FLAGS_LPM_ENGAGED)) - return; - - if (port == TASK_ID_TO_PD_PORT(task_get_current())) { - if (!TC_CHK_FLAG(port, TC_FLAGS_LPM_TRANSITION)) - reset_device_and_notify(port); - } else { - /* Otherwise, we need to wait for the TCPC reset to complete */ - atomic_or(&tc[port].tasks_waiting_on_reset, - 1 << task_get_current()); - /* - * NOTE: We could be sending the PD task the reset event while - * it is already processing the reset event. If that occurs, - * then we will reset the TCPC multiple times, which is - * undesirable but most likely benign. Empirically, this doesn't - * happen much, but it if starts occurring, we can add a guard - * to prevent/reduce it. - */ - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_TCPC_RESET); - task_wait_event_mask(TASK_EVENT_PD_AWAKE, -1); - } -} - -/* - * This can be called from any task. If we are in the PD task, we can handle - * immediately. Otherwise, we need to notify the PD task via event. - */ -void pd_device_accessed(int port) -{ - if (port == TASK_ID_TO_PD_PORT(task_get_current())) - handle_device_access(port); - else - task_set_event(PD_PORT_TO_TASK_ID(port), - PD_EVENT_DEVICE_ACCESSED); -} - -/* - * TODO(b/137493121): Move this function to a separate file that's shared - * between the this and the original stack. - */ -void pd_prevent_low_power_mode(int port, int prevent) -{ - const int current_task_mask = (1 << task_get_current()); - - if (prevent) - atomic_or(&tc[port].tasks_preventing_lpm, current_task_mask); - else - atomic_clear_bits(&tc[port].tasks_preventing_lpm, - current_task_mask); -} -#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ - -static void sink_power_sub_states(int port) -{ - enum tcpc_cc_voltage_status cc1, cc2, cc; - enum tcpc_cc_voltage_status new_cc_voltage; - - tcpm_get_cc(port, &cc1, &cc2); - - cc = polarity_rm_dts(tc[port].polarity) ? cc2 : cc1; - - if (cc == TYPEC_CC_VOLT_RP_DEF) - new_cc_voltage = TYPEC_CC_VOLT_RP_DEF; - else if (cc == TYPEC_CC_VOLT_RP_1_5) - new_cc_voltage = TYPEC_CC_VOLT_RP_1_5; - else if (cc == TYPEC_CC_VOLT_RP_3_0) - new_cc_voltage = TYPEC_CC_VOLT_RP_3_0; - else - new_cc_voltage = TYPEC_CC_VOLT_OPEN; - - /* Debounce the cc state */ - if (new_cc_voltage != tc[port].cc_voltage) { - tc[port].cc_voltage = new_cc_voltage; - pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, - PD_T_RP_VALUE_CHANGE); - return; - } - - if (!pd_timer_is_disabled(port, TC_TIMER_CC_DEBOUNCE)) { - if (!pd_timer_is_expired(port, TC_TIMER_CC_DEBOUNCE)) - return; - - pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - tc[port].typec_curr = usb_get_typec_current_limit( - tc[port].polarity, cc1, cc2); - - typec_set_input_current_limit(port, - tc[port].typec_curr, TYPE_C_VOLTAGE); - charge_manager_update_dualrole(port, CAP_DEDICATED); - } - } -} - - -/* - * TYPE-C State Implementations - */ - -/** - * Disabled - * - * Super State Entry Actions: - * Remove the terminations from CC - * Set VBUS and VCONN off - */ -static void tc_disabled_entry(const int port) -{ - print_current_state(port); - /* - * We have completed tc_cc_open_entry (our super state), so set flag - * to indicate to pd_is_port_enabled that we are now suspended. - */ - TC_SET_FLAG(port, TC_FLAGS_SUSPENDED); -} - -static void tc_disabled_run(const int port) -{ - /* If pd_set_suspend clears the request, go to TC_UNATTACHED_SNK/SRC. */ - if (!TC_CHK_FLAG(port, TC_FLAGS_REQUEST_SUSPEND)) { - set_state_tc(port, drp_state[port] == PD_DRP_FORCE_SOURCE ? - TC_UNATTACHED_SRC : TC_UNATTACHED_SNK); - } else { - if (IS_ENABLED(CONFIG_USBC_RETIMER_FW_UPDATE)) { - if (TC_CHK_FLAG(port, - TC_FLAGS_USB_RETIMER_FW_UPDATE_LTD_RUN)) { - TC_CLR_FLAG(port, - TC_FLAGS_USB_RETIMER_FW_UPDATE_LTD_RUN); - usb_retimer_fw_update_process_op_cb(port); - } - } - tc_pause_event_loop(port); - } -} - -static void tc_disabled_exit(const int port) -{ - int rv; - - tc_start_event_loop(port); - TC_CLR_FLAG(port, TC_FLAGS_SUSPENDED); - - rv = tcpm_init(port); - CPRINTS("C%d: TCPC init %s", port, rv ? "failed!" : "ready"); -} - -/** - * ErrorRecovery - * - * Super State Entry Actions: - * Remove the terminations from CC - * Set's VBUS and VCONN off - */ -static void tc_error_recovery_entry(const int port) -{ - print_current_state(port); - - pd_timer_enable(port, TC_TIMER_TIMEOUT, PD_T_ERROR_RECOVERY); - - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_ERROR_RECOVERY); -} - -static void tc_error_recovery_run(const int port) -{ - enum usb_tc_state start_state; - - if (!pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) - return; - - /* - * If we transitioned to error recovery as the first state and we - * didn't brown out, we don't need to reinitialized the tc statemachine - * because we just did that. So transition to the state directly. - */ - if (tc[port].ctx.previous == NULL) { - set_state_tc(port, drp_state[port] == PD_DRP_FORCE_SOURCE ? - TC_UNATTACHED_SRC : TC_UNATTACHED_SNK); - return; - } - - /* - * If try src support is active (e.g. in S0). Then try to become the - * SRC, otherwise we should try to be the sink. - */ - start_state = TC_UNATTACHED_SNK; - if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - if (is_try_src_enabled(port) || - drp_state[port] == PD_DRP_FORCE_SOURCE) - start_state = TC_UNATTACHED_SRC; - - restart_tc_sm(port, start_state); -} - -static void tc_error_recovery_exit(const int port) -{ - pd_timer_disable(port, TC_TIMER_TIMEOUT); -} - -/** - * Unattached.SNK - */ -static void tc_unattached_snk_entry(const int port) -{ - enum pd_data_role prev_data_role; - - if (get_last_state_tc(port) != TC_UNATTACHED_SRC) { - tc_detached(port); - print_current_state(port); - } - - /* - * We are in an unattached state and considering to be a SNK - * searching for a SRC partner. We set the CC pull value to - * to indicate our intent to be SNK in hopes a partner SRC - * will is there to attach to. - * - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rd. - * - * Restore default current limit Rp in case we swap to source - * - * Run any debug detaches needed before setting CC, as some TCPCs may - * require we set CC Open before changing power roles with a debug - * accessory. - */ - tcpm_debug_detach(port); - typec_select_pull(port, TYPEC_CC_RD); - typec_select_src_current_limit_rp(port, - typec_get_default_current_limit_rp(port)); - typec_update_cc(port); - - - prev_data_role = tc[port].data_role; - tc[port].data_role = PD_ROLE_DISCONNECTED; - /* - * When data role set events are used to enable BC1.2, then CC - * detach events are used to notify BC1.2 that it can be powered - * down. - */ - bc12_role_change_handler(port, prev_data_role, tc[port].data_role); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - charge_manager_update_dualrole(port, CAP_UNKNOWN); - - tc_set_partner_role(port, PPC_DEV_DISCONNECTED); - - /* - * Indicate that the port is disconnected so the board - * can restore state from any previous data swap. - */ - pd_execute_data_swap(port, PD_ROLE_DISCONNECTED); - pd_timer_enable(port, TC_TIMER_NEXT_ROLE_SWAP, PD_T_DRP_SNK); - - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - CLR_FLAGS_ON_DISCONNECT(port); - tc_enable_pd(port, 0); - } -} - -static void tc_unattached_snk_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - /* - * TODO(b/137498392): Add wait before sampling the CC - * status after role changes - */ - - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED); - tc_set_data_role(port, PD_ROLE_UFP); - /* Inform Policy Engine that hard reset is complete */ - pe_ps_reset_complete(port); - } - } - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - /* - * The port shall transition to AttachWait.SNK when a Source - * connection is detected, as indicated by the SNK.Rp state - * on at least one of its CC pins. - * - * A DRP shall transition to Unattached.SRC within tDRPTransition - * after the state of both CC pins is SNK.Open for - * tDRP − dcSRC.DRP ∙ tDRP. - */ - if (cc_is_rp(cc1) || cc_is_rp(cc2)) { - /* Connection Detected */ - set_state_tc(port, TC_ATTACH_WAIT_SNK); - return; - } - - /* - * Debounce the CC open status. Some TCPC needs time to get the CC - * status valid. Before that, CC open is reported by default. Wait - * to make sure the CC is really open. Reuse the role toggle timer. - */ - if (!pd_timer_is_expired(port, TC_TIMER_NEXT_ROLE_SWAP)) - return; - - /* - * Initialize type-C supplier current limits to 0. The charge - * manage is now seeded if it was not. - */ - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - typec_set_input_current_limit(port, 0, 0); - - /* - * Attempt TCPC auto DRP toggle if it is - * not already auto toggling. - */ - if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) && - drp_state[port] == PD_DRP_TOGGLE_ON && - tcpm_auto_toggle_supported(port)) { - set_state_tc(port, TC_DRP_AUTO_TOGGLE); - } else if (drp_state[port] == PD_DRP_TOGGLE_ON) { - /* DRP Toggle. The timer was checked above. */ - set_state_tc(port, TC_UNATTACHED_SRC); - } else if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER) && - (drp_state[port] == PD_DRP_FORCE_SINK || - drp_state[port] == PD_DRP_TOGGLE_OFF)) { - set_state_tc(port, TC_LOW_POWER_MODE); - } -} - -static void tc_unattached_snk_exit(const int port) -{ - pd_timer_disable(port, TC_TIMER_NEXT_ROLE_SWAP); -} - -/** - * AttachWait.SNK - * - * Super State Entry Actions: - * Vconn Off - * Place Rd on CC - * Set power role to SINK - */ -static void tc_attach_wait_snk_entry(const int port) -{ - print_current_state(port); - - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_attach_wait_snk_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - enum pd_cc_states new_cc_state; - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - if (cc_is_rp(cc1) && cc_is_rp(cc2) && board_is_dts_port(port)) - new_cc_state = PD_CC_DFP_DEBUG_ACC; - else if (cc_is_rp(cc1) || cc_is_rp(cc2)) - new_cc_state = PD_CC_DFP_ATTACHED; - else - new_cc_state = PD_CC_NONE; - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, PD_T_CC_DEBOUNCE); - pd_timer_enable(port, TC_TIMER_PD_DEBOUNCE, PD_T_PD_DEBOUNCE); - tc[port].cc_state = new_cc_state; - return; - } - - /* - * A DRP shall transition to Unattached.SRC when the state of both - * the CC1 and CC2 pins is SNK.Open for at least tPDDebounce, however - * when DRP state prevents switch to SRC the next state should be - * Unattached.SNK. - */ - if (new_cc_state == PD_CC_NONE && - pd_timer_is_expired(port, TC_TIMER_PD_DEBOUNCE)) { - /* We are detached */ - if (drp_state[port] == PD_DRP_TOGGLE_OFF - || drp_state[port] == PD_DRP_FREEZE - || drp_state[port] == PD_DRP_FORCE_SINK) - set_state_tc(port, TC_UNATTACHED_SNK); - else - set_state_tc(port, TC_UNATTACHED_SRC); - return; - } - - /* Wait for CC debounce */ - if (!pd_timer_is_expired(port, TC_TIMER_CC_DEBOUNCE)) - return; - - /* - * The port shall transition to Attached.SNK after the state of only - * one of the CC1 or CC2 pins is SNK.Rp for at least tCCDebounce and - * VBUS is detected. - * - * A DRP that strongly prefers the Source role may optionally - * transition to Try.SRC instead of Attached.SNK when the state of only - * one CC pin has been SNK.Rp for at least tCCDebounce and VBUS is - * detected. - * - * If the port supports Debug Accessory Mode, the port shall transition - * to DebugAccessory.SNK if the state of both the CC1 and CC2 pins is - * SNK.Rp for at least tCCDebounce and VBUS is detected. - */ - if (pd_is_vbus_present(port)) { - if (new_cc_state == PD_CC_DFP_ATTACHED) { - if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC) && - is_try_src_enabled(port)) - set_state_tc(port, TC_TRY_SRC); - else - set_state_tc(port, TC_ATTACHED_SNK); - } else { - /* new_cc_state is PD_CC_DFP_DEBUG_ACC */ - CPRINTS("C%d: Debug accessory detected", port); - TC_SET_FLAG(port, TC_FLAGS_TS_DTS_PARTNER); - set_state_tc(port, TC_ATTACHED_SNK); - } - - if (IS_ENABLED(CONFIG_USB_PE_SM) && - IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) { - hook_call_deferred(&pd_usb_billboard_deferred_data, - PD_T_AME); - } - } -} - -static void tc_attach_wait_snk_exit(const int port) -{ - pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE); - pd_timer_disable(port, TC_TIMER_PD_DEBOUNCE); -} - -/** - * Attached.SNK, shared with Debug Accessory.SNK - */ -static void tc_attached_snk_entry(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - print_current_state(port); - - /* - * Known state of attach is SNK. We need to apply this pull value - * to make it set in hardware at the correct time but set the common - * pull here. - * - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rd. - */ - typec_select_pull(port, TYPEC_CC_RD); - - /* Inform the PPC and OCP module that a source is connected */ - tc_set_partner_role(port, PPC_DEV_SRC); - - if (IS_ENABLED(CONFIG_USB_PE_SM) && - TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { - /* Flipping power role - Disable AutoDischargeDisconnect */ - tcpm_enable_auto_discharge_disconnect(port, 0); - - /* Apply Rd */ - typec_update_cc(port); - - /* Change role to sink */ - tc_set_power_role(port, PD_ROLE_SINK); - tcpm_set_msg_header(port, tc[port].power_role, - tc[port].data_role); - - /* - * Maintain VCONN supply state, whether ON or OFF, and its - * data role / usb mux connections. Do not re-enable - * AutoDischargeDisconnect until the swap is completed - * and tc_pr_swap_complete is called. - */ - } else { - /* Get connector orientation */ - tcpm_get_cc(port, &cc1, &cc2); - tc[port].polarity = get_snk_polarity(cc1, cc2); - pd_set_polarity(port, tc[port].polarity); - - tc_set_data_role(port, PD_ROLE_UFP); - - hook_notify(HOOK_USB_PD_CONNECT); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { - tc[port].typec_curr = - usb_get_typec_current_limit(tc[port].polarity, - cc1, cc2); - typec_set_input_current_limit(port, - tc[port].typec_curr, TYPE_C_VOLTAGE); - /* - * Start new connections as dedicated until source caps - * are received, at which point the PE will update the - * flag. - */ - charge_manager_update_dualrole(port, CAP_DEDICATED); - } - - /* Apply Rd */ - typec_update_cc(port); - - /* - * Attached.SNK - enable AutoDischargeDisconnect - * Do this after applying Rd to CC lines to avoid - * TCPC_REG_FAULT_STATUS_AUTO_DISCHARGE_FAIL (b/171567398) - */ - tcpm_enable_auto_discharge_disconnect(port, 1); - } - - pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE); - - /* Enable PD */ - if (IS_ENABLED(CONFIG_USB_PE_SM)) - tc_enable_pd(port, 1); - - if (TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) { - tcpm_debug_accessory(port, 1); - set_ccd_mode(port, 1); - } -} - -/* - * Check whether Vbus has been removed on this port, accounting for some Vbus - * debounce if FRS is enabled. - * - * Returns true if a new state was set and the calling run should exit. - */ -static bool tc_snk_check_vbus_removed(const int port) -{ - if (IS_ENABLED(CONFIG_USB_PD_FRS)) { - /* - * Debounce Vbus presence when FRS is enabled. Note that we may - * lose Vbus before the FRS signal comes in to let us know - * we're PR swapping, but we must still transition to unattached - * within tSinkDisconnect. - * - * We may safely re-use the Vbus debounce timer here - * since a PR swap would no longer be in progress when Vbus - * removal is checked. - */ - if (pd_check_vbus_level(port, VBUS_REMOVED)) { - if (pd_timer_is_disabled(port, - TC_TIMER_VBUS_DEBOUNCE)) { - pd_timer_enable(port, TC_TIMER_VBUS_DEBOUNCE, - PD_T_FRS_VBUS_DEBOUNCE); - } else if (pd_timer_is_expired(port, - TC_TIMER_VBUS_DEBOUNCE)) { - set_state_tc(port, TC_UNATTACHED_SNK); - return true; - } - } else { - pd_timer_disable(port, TC_TIMER_VBUS_DEBOUNCE); - } - } else if (pd_check_vbus_level(port, VBUS_REMOVED)) { - set_state_tc(port, TC_UNATTACHED_SNK); - return true; - } - - return false; -} - -static void tc_attached_snk_run(const int port) -{ -#ifdef CONFIG_USB_PE_SM - /* - * Perform Hard Reset - */ - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED)) { - /* - * Wait to clear the hard reset request until Vbus has returned - * to default (or, if it didn't return, we transition to - * unattached) - */ - if (tc_perform_snk_hard_reset(port)) - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED); - - return; - } - - /* - * From 4.5.2.2.5.2 Exiting from Attached.SNK State: - * - * "A port that is not a Vconn-Powered USB Device and is not in the - * process of a USB PD PR_Swap or a USB PD Hard Reset or a USB PD - * FR_Swap shall transition to Unattached.SNK within tSinkDisconnect - * when Vbus falls below vSinkDisconnect for Vbus operating at or - * below 5 V or below vSinkDisconnectPD when negotiated by USB PD - * to operate above 5 V." - * - * TODO(b/149530538): Use vSinkDisconnectPD when above 5V - */ - - /* - * Debounce Vbus before we drop that we are doing a PR_Swap - */ - if (TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS) && - pd_timer_is_expired(port, TC_TIMER_VBUS_DEBOUNCE)) { - /* PR Swap is no longer in progress */ - TC_CLR_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS); - pd_timer_disable(port, TC_TIMER_VBUS_DEBOUNCE); - - /* - * AutoDischargeDisconnect was turned off when we - * hit Safe0V on SRC->SNK PR-Swap. We now are done - * with the swap and should have Vbus, so re-enable - * AutoDischargeDisconnect. - */ - if (!pd_check_vbus_level(port, VBUS_REMOVED)) - tcpm_enable_auto_discharge_disconnect(port, 1); - } - - /* - * The sink will be powered off during a power role swap but we don't - * want to trigger a disconnect. - */ - if (!TC_CHK_FLAG(port, TC_FLAGS_POWER_OFF_SNK) && - !TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { - /* - * Detach detection - */ - if (tc_snk_check_vbus_removed(port)) - return; - - if (!pe_is_explicit_contract(port)) - sink_power_sub_states(port); - } - - /* - * PD swap commands - */ - if (tc_get_pd_enabled(port) && prl_is_running(port)) { - /* - * Power Role Swap - */ - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP)) { - /* - * We may want to verify partner is applying Rd before - * we swap. However, some TCPCs (such as TUSB422) will - * not report the correct CC status before VBUS falls to - * vSafe0V, so this will be problematic in the FRS case. - */ - set_state_tc(port, TC_ATTACHED_SRC); - return; - } - - /* - * Data Role Swap - */ - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP); - - /* Perform Data Role Swap */ - tc_set_data_role(port, - tc[port].data_role == PD_ROLE_UFP ? - PD_ROLE_DFP : PD_ROLE_UFP); - } - - /* - * VCONN Swap - * UnorientedDebugAccessory.SRC shall not drive Vconn - */ - if (IS_ENABLED(CONFIG_USBC_VCONN) && - !TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) { - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON); - - set_vconn(port, 1); - /* - * Inform policy engine that vconn swap is - * complete - */ - pe_vconn_swap_complete(port); - } else if (TC_CHK_FLAG(port, - TC_FLAGS_REQUEST_VC_SWAP_OFF)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_OFF); - - set_vconn(port, 0); - /* - * Inform policy engine that vconn swap is - * complete - */ - pe_vconn_swap_complete(port); - } - } - - if (!TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) { - /* - * If the port supports Charge-Through VCONN-Powered USB - * devices, and an explicit PD contract has failed to be - * negotiated, the port shall query the identity of the - * cable via USB PD on SOP’ - */ - if (!pe_is_explicit_contract(port) && - TC_CHK_FLAG(port, TC_FLAGS_CTVPD_DETECTED)) { - /* - * A port that via SOP’ has detected an - * attached Charge-Through VCONN-Powered USB - * device shall transition to Unattached.SRC - * if an explicit PD contract has failed to - * be negotiated. - */ - /* CTVPD detected */ - set_state_tc(port, TC_UNATTACHED_SRC); - } - } - } - -#else /* CONFIG_USB_PE_SM */ - - /* Detach detection */ - if (tc_snk_check_vbus_removed(port)) - return; - - /* Run Sink Power Sub-State */ - sink_power_sub_states(port); -#endif /* CONFIG_USB_PE_SM */ -} - -static void tc_attached_snk_exit(const int port) -{ - if (!TC_CHK_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP)) { - /* - * If supplying VCONN, the port shall cease to supply - * it within tVCONNOFF of exiting Attached.SNK if not - * PR swapping. - */ - if (TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON)) - set_vconn(port, 0); - - /* - * Attached.SNK exit - disable AutoDischargeDisconnect - * NOTE: This should not happen if we are suspending. It will - * happen in tc_cc_open_entry if that is the path we are - * taking. - */ - if (!TC_CHK_FLAG(port, TC_FLAGS_REQUEST_SUSPEND)) - tcpm_enable_auto_discharge_disconnect(port, 0); - } - - /* Clear flags after checking Vconn status */ - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP | TC_FLAGS_POWER_OFF_SNK); - - /* Stop drawing power */ - sink_stop_drawing_current(port); - - if (TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) - tcpm_debug_detach(port); - - pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE); - pd_timer_disable(port, TC_TIMER_TIMEOUT); - pd_timer_disable(port, TC_TIMER_VBUS_DEBOUNCE); -} - -/** - * Unattached.SRC - */ -static void tc_unattached_src_entry(const int port) -{ - enum pd_data_role prev_data_role; - - if (get_last_state_tc(port) != TC_UNATTACHED_SNK) { - tc_detached(port); - print_current_state(port); - } - - /* - * We are in an unattached state and considering to be a SRC - * searching for a SNK partner. We set the CC pull value to - * to indicate our intent to be SRC in hopes a partner SNK - * will is there to attach to. - * - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rp. - * - * Restore default current limit Rp. - * - * Run any debug detaches needed before setting CC, as some TCPCs may - * require we set CC Open before changing power roles with a debug - * accessory. - */ - tcpm_debug_detach(port); - typec_select_pull(port, TYPEC_CC_RP); - typec_select_src_current_limit_rp(port, - typec_get_default_current_limit_rp(port)); - typec_update_cc(port); - - prev_data_role = tc[port].data_role; - tc[port].data_role = PD_ROLE_DISCONNECTED; - - /* - * When data role set events are used to enable BC1.2, then CC - * detach events are used to notify BC1.2 that it can be powered - * down. - */ - bc12_role_change_handler(port, prev_data_role, tc[port].data_role); - - tc_set_partner_role(port, PPC_DEV_DISCONNECTED); - - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - charge_manager_update_dualrole(port, CAP_UNKNOWN); - - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - CLR_FLAGS_ON_DISCONNECT(port); - tc_enable_pd(port, 0); - } - - pd_timer_enable(port, TC_TIMER_NEXT_ROLE_SWAP, PD_T_DRP_SRC); -} - -static void tc_unattached_src_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED); - tc_set_data_role(port, PD_ROLE_DFP); - /* Inform Policy Engine that hard reset is complete */ - pe_ps_reset_complete(port); - } - } - - if (IS_ENABLED(CONFIG_USBC_OCP)) { - /* - * If the port is latched off, just continue to - * monitor for a detach. - */ - if (usbc_ocp_is_port_latched_off(port)) - return; - } - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - /* - * Transition to AttachWait.SRC when: - * 1) The SRC.Rd state is detected on either CC1 or CC2 pin or - * 2) The SRC.Ra state is detected on both the CC1 and CC2 pins. - * - * A DRP shall transition to Unattached.SNK within tDRPTransition - * after dcSRC.DRP ∙ tDRP - */ - if (cc_is_at_least_one_rd(cc1, cc2) || cc_is_audio_acc(cc1, cc2)) - set_state_tc(port, TC_ATTACH_WAIT_SRC); - else if (pd_timer_is_expired(port, TC_TIMER_NEXT_ROLE_SWAP) && - drp_state[port] != PD_DRP_FORCE_SOURCE && - drp_state[port] != PD_DRP_FREEZE) - set_state_tc(port, TC_UNATTACHED_SNK); - /* - * Attempt TCPC auto DRP toggle - */ - else if (IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) && - drp_state[port] == PD_DRP_TOGGLE_ON && - tcpm_auto_toggle_supported(port) && cc_is_open(cc1, cc2)) - set_state_tc(port, TC_DRP_AUTO_TOGGLE); - else if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER) && - (drp_state[port] == PD_DRP_FORCE_SOURCE || - drp_state[port] == PD_DRP_TOGGLE_OFF)) - set_state_tc(port, TC_LOW_POWER_MODE); -} - -static void tc_unattached_src_exit(const int port) -{ - pd_timer_disable(port, TC_TIMER_NEXT_ROLE_SWAP); -} - -/** - * AttachWait.SRC - * - * Super State Entry Actions: - * Vconn Off - * Place Rp on CC - * Set power role to SOURCE - */ -static void tc_attach_wait_src_entry(const int port) -{ - print_current_state(port); - - tc[port].cc_state = PD_CC_UNSET; -} - -static void tc_attach_wait_src_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - enum pd_cc_states new_cc_state; - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - if (cc_is_snk_dbg_acc(cc1, cc2) && board_is_dts_port(port)) { - /* - * Debug accessory. - * A debug accessory in a non-DTS port will be - * recognized by at_least_one_rd as UFP attached. - */ - new_cc_state = PD_CC_UFP_DEBUG_ACC; - } else if (cc_is_at_least_one_rd(cc1, cc2)) { - /* UFP attached */ - new_cc_state = PD_CC_UFP_ATTACHED; - } else if (cc_is_audio_acc(cc1, cc2)) { - /* AUDIO Accessory not supported. Just ignore */ - new_cc_state = PD_CC_UFP_AUDIO_ACC; - } else { - /* No UFP */ - if (drp_state[port] == PD_DRP_FORCE_SOURCE) - set_state_tc(port, TC_UNATTACHED_SRC); - else - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, PD_T_CC_DEBOUNCE); - tc[port].cc_state = new_cc_state; - return; - } - - /* Wait for CC debounce */ - if (!pd_timer_is_expired(port, TC_TIMER_CC_DEBOUNCE)) - return; - - /* - * The port shall transition to Attached.SRC when VBUS is at vSafe0V - * and the SRC.Rd state is detected on exactly one of the CC1 or CC2 - * pins for at least tCCDebounce. - * - * If the port supports Debug Accessory Mode, it shall transition to - * UnorientedDebugAccessory.SRC when VBUS is at vSafe0V and the SRC.Rd - * state is detected on both the CC1 and CC2 pins for at least - * tCCDebounce. - */ - if (pd_check_vbus_level(port, VBUS_SAFE0V)) { - if (new_cc_state == PD_CC_UFP_ATTACHED) { - set_state_tc(port, TC_ATTACHED_SRC); - return; - } else if (new_cc_state == PD_CC_UFP_DEBUG_ACC) { - CPRINTS("C%d: Debug accessory detected", port); - TC_SET_FLAG(port, TC_FLAGS_TS_DTS_PARTNER); - set_state_tc(port, TC_ATTACHED_SRC); - return; - } - } -} - -static void tc_attach_wait_src_exit(const int port) -{ - pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE); -} - -/** - * Attached.SRC, shared with UnorientedDebugAccessory.SRC - */ -static void tc_attached_src_entry(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - print_current_state(port); - - pd_timer_disable(port, TC_TIMER_TIMEOUT); - - /* - * Known state of attach is SRC. We need to apply this pull value - * to make it set in hardware at the correct time but set the common - * pull here. - * - * Both CC1 and CC2 pins shall be independently terminated to - * pulled up through Rp. - * - * Set selected current limit in the hardware. - */ - typec_select_pull(port, TYPEC_CC_RP); - typec_set_source_current_limit(port, tc[port].select_current_limit_rp); - - if (IS_ENABLED(CONFIG_USB_PE_SM)) { - if (TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { - /* Change role to source */ - tc_set_power_role(port, PD_ROLE_SOURCE); - tcpm_set_msg_header(port, - tc[port].power_role, - tc[port].data_role); - - /* Enable VBUS */ - tc_src_power_on(port); - - /* Apply Rp */ - typec_update_cc(port); - - /* - * Maintain VCONN supply state, whether ON or OFF, and - * its data role / usb mux connections. Do not - * re-enable AutoDischargeDisconnect until the swap is - * completed and tc_pr_swap_complete is called. - */ - } else { - /* - * Set up CC's, Vconn, and ADD before Vbus, as per - * Figure 4-24. DRP Initialization and Connection - * Detection in TCPCI r2 v1.2 specification. - */ - - /* Get connector orientation */ - tcpm_get_cc(port, &cc1, &cc2); - tc[port].polarity = get_src_polarity(cc1, cc2); - pd_set_polarity(port, tc[port].polarity); - - /* Attached.SRC - enable AutoDischargeDisconnect */ - tcpm_enable_auto_discharge_disconnect(port, 1); - - /* Apply Rp */ - typec_update_cc(port); - - /* - * Initial data role for sink is DFP - * This also sets the usb mux - */ - tc_set_data_role(port, PD_ROLE_DFP); - - /* - * Start sourcing Vconn before Vbus to ensure - * we are within USB Type-C Spec 1.4 tVconnON - * - * UnorientedDebugAccessory.SRC shall not drive Vconn - */ - if (IS_ENABLED(CONFIG_USBC_VCONN) && - !TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) - set_vconn(port, 1); - - /* Enable VBUS */ - if (tc_src_power_on(port)) { - /* Stop sourcing Vconn if Vbus failed */ - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_vconn(port, 0); - - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - usb_mux_set(port, - USB_PD_MUX_NONE, - USB_SWITCH_DISCONNECT, - tc[port].polarity); - } - - tc_enable_pd(port, 0); - pd_timer_enable(port, TC_TIMER_TIMEOUT, - MAX(PD_POWER_SUPPLY_TURN_ON_DELAY, - PD_T_VCONN_STABLE)); - } - } else { - /* - * Set up CC's, Vconn, and ADD before Vbus, as per - * Figure 4-24. DRP Initialization and Connection - * Detection in TCPCI r2 v1.2 specification. - */ - - /* Get connector orientation */ - tcpm_get_cc(port, &cc1, &cc2); - tc[port].polarity = get_src_polarity(cc1, cc2); - pd_set_polarity(port, tc[port].polarity); - - /* Attached.SRC - enable AutoDischargeDisconnect */ - tcpm_enable_auto_discharge_disconnect(port, 1); - - /* Apply Rp */ - typec_update_cc(port); - - /* - * Initial data role for sink is DFP - * This also sets the usb mux - */ - tc_set_data_role(port, PD_ROLE_DFP); - - /* - * Start sourcing Vconn before Vbus to ensure - * we are within USB Type-C Spec 1.4 tVconnON - * - * UnorientedDebugAccessory.SRC shall not drive Vconn - */ - if (IS_ENABLED(CONFIG_USBC_VCONN) && - !TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) - set_vconn(port, 1); - - /* Enable VBUS */ - if (tc_src_power_on(port)) { - /* Stop sourcing Vconn if Vbus failed */ - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_vconn(port, 0); - - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - usb_mux_set(port, USB_PD_MUX_NONE, - USB_SWITCH_DISCONNECT, tc[port].polarity); - } - } - - /* Inform PPC and OCP module that a sink is connected. */ - tc_set_partner_role(port, PPC_DEV_SNK); - - /* Initialize type-C supplier to seed the charge manger */ - if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) - typec_set_input_current_limit(port, 0, 0); - - /* - * Only notify if we're not performing a power role swap. During a - * power role swap, the port partner is not disconnecting/connecting. - */ - if (!TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { - hook_notify(HOOK_USB_PD_CONNECT); - } - - if (TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) { - tcpm_debug_accessory(port, 1); - set_ccd_mode(port, 1); - } - - /* - * Some TCPCs require time to correctly return CC status after - * changing the ROLE_CONTROL register. Due to that, we have to ignore - * CC_NONE state until PD_T_SRC_DISCONNECT delay has elapsed. - * From the "Universal Serial Bus Type-C Cable and Connector - * Specification" Release 2.0 paragraph 4.5.2.2.9.2: - * The Source shall detect the SRC.Open state within tSRCDisconnect, - * but should detect it as quickly as possible - */ - pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, PD_T_SRC_DISCONNECT); -} - -static void tc_attached_src_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - if (polarity_rm_dts(tc[port].polarity)) - cc1 = cc2; - - if (cc1 == TYPEC_CC_VOLT_OPEN) - tc[port].cc_state = PD_CC_NONE; - else - tc[port].cc_state = PD_CC_UFP_ATTACHED; - - /* - * When the SRC.Open state is detected on the monitored CC pin, a DRP - * shall transition to Unattached.SNK unless it strongly prefers the - * Source role. In that case, it shall transition to TryWait.SNK. - * This transition to TryWait.SNK is needed so that two devices that - * both prefer the Source role do not loop endlessly between Source - * and Sink. In other words, a DRP that would enter Try.SRC from - * AttachWait.SNK shall enter TryWait.SNK for a Sink detach from - * Attached.SRC. - */ - if (tc[port].cc_state == PD_CC_NONE && - pd_timer_is_expired(port, TC_TIMER_CC_DEBOUNCE)) { - bool tryWait; - enum usb_tc_state new_tc_state = TC_UNATTACHED_SNK; - - if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - tryWait = is_try_src_enabled(port) && - !TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER); - - if (drp_state[port] == PD_DRP_FORCE_SOURCE) - new_tc_state = TC_UNATTACHED_SRC; - else if(IS_ENABLED(CONFIG_USB_PD_TRY_SRC)) - new_tc_state = tryWait ? - TC_TRY_WAIT_SNK : TC_UNATTACHED_SNK; - - set_state_tc(port, new_tc_state); - return; - } - -#ifdef CONFIG_USB_PE_SM - /* - * Enable PD communications after power supply has fully - * turned on - */ - if (pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) { - tc_enable_pd(port, 1); - pd_timer_disable(port, TC_TIMER_TIMEOUT); - } - - if (!tc_get_pd_enabled(port)) - return; - - /* - * Handle Hard Reset from Policy Engine - */ - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED)) { - /* Ignoring Hard Resets while the power supply is resetting.*/ - if (!pd_timer_is_disabled(port, TC_TIMER_TIMEOUT) && - !pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) - return; - - if (tc_perform_src_hard_reset(port)) - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED); - - return; - } - - /* - * PD swap commands - */ - if (tc_get_pd_enabled(port) && prl_is_running(port)) { - /* - * Power Role Swap Request - */ - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP)) { - /* Clear TC_FLAGS_REQUEST_PR_SWAP on exit */ - return set_state_tc(port, TC_ATTACHED_SNK); - } - - /* - * Data Role Swap Request - */ - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP); - - /* Perform Data Role Swap */ - tc_set_data_role(port, - tc[port].data_role == PD_ROLE_DFP ? - PD_ROLE_UFP : PD_ROLE_DFP); - } - - /* - * Vconn Swap Request - * UnorientedDebugAccessory.SRC shall not drive Vconn - */ - if (IS_ENABLED(CONFIG_USBC_VCONN) && - !TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) { - /* - * VCONN Swap Request - */ - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_ON); - set_vconn(port, 1); - pe_vconn_swap_complete(port); - } else if (TC_CHK_FLAG(port, - TC_FLAGS_REQUEST_VC_SWAP_OFF)) { - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_VC_SWAP_OFF); - set_vconn(port, 0); - pe_vconn_swap_complete(port); - } - } - - /* - * A DRP that supports Charge-Through VCONN-Powered USB Devices - * shall transition to CTUnattached.SNK if the connected device - * identifies itself as a Charge-Through VCONN-Powered USB - * Device in its Discover Identity Command response. - */ - - /* - * A DRP that supports Charge-Through VCONN-Powered USB Devices - * shall transition to CTUnattached.SNK if the connected device - * identifies itself as a Charge-Through VCONN-Powered USB - * Device in its Discover Identity Command response. - * - * If it detects that it is connected to a VCONN-Powered USB - * Device, the port may remove VBUS and discharge it to - * vSafe0V, while continuing to remain in this state with VCONN - * applied. - */ - if (!TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER) && - TC_CHK_FLAG(port, TC_FLAGS_CTVPD_DETECTED)) { - - set_state_tc(port, TC_CT_UNATTACHED_SNK); - } - } -#endif - - if (TC_CHK_FLAG(port, TC_FLAGS_UPDATE_CURRENT)) { - TC_CLR_FLAG(port, TC_FLAGS_UPDATE_CURRENT); - typec_set_source_current_limit(port, - tc[port].select_current_limit_rp); - pd_update_contract(port); - - /* Update Rp if no contract is present */ - if (!IS_ENABLED(CONFIG_USB_PE_SM) || - !pe_is_explicit_contract(port)) - typec_update_cc(port); - } -} - -static void tc_attached_src_exit(const int port) -{ - /* - * A port shall cease to supply VBUS within tVBUSOFF of exiting - * Attached.SRC. - */ - tc_src_power_off(port); - - if (!TC_CHK_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP)) { - /* Attached.SRC exit - disable AutoDischargeDisconnect */ - tcpm_enable_auto_discharge_disconnect(port, 0); - - /* - * Disable VCONN if not power role swapping and - * a CTVPD was not detected - */ - if (TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON) && - !TC_CHK_FLAG(port, TC_FLAGS_CTVPD_DETECTED)) - set_vconn(port, 0); - } - - /* Clear CTVPD detected after checking for Vconn */ - TC_CLR_FLAG(port, TC_FLAGS_CTVPD_DETECTED); - - /* Clear PR swap flag after checking for Vconn */ - TC_CLR_FLAG(port, TC_FLAGS_REQUEST_PR_SWAP); - - if (TC_CHK_FLAG(port, TC_FLAGS_TS_DTS_PARTNER)) - tcpm_debug_detach(port); - - pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE); - pd_timer_disable(port, TC_TIMER_TIMEOUT); -} - -static __maybe_unused void check_drp_connection(const int port) -{ - enum pd_drp_next_states next_state; - enum tcpc_cc_voltage_status cc1, cc2; - - TC_CLR_FLAG(port, TC_FLAGS_CHECK_CONNECTION); - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - tc[port].drp_sink_time = get_time().val; - - /* Get the next toggle state */ - next_state = drp_auto_toggle_next_state(&tc[port].drp_sink_time, - tc[port].power_role, drp_state[port], cc1, cc2, - tcpm_auto_toggle_supported(port)); - - if (next_state == DRP_TC_DEFAULT) - next_state = (PD_ROLE_DEFAULT(port) == PD_ROLE_SOURCE) - ? DRP_TC_UNATTACHED_SRC - : DRP_TC_UNATTACHED_SNK; - - switch (next_state) { - case DRP_TC_UNATTACHED_SNK: - set_state_tc(port, TC_UNATTACHED_SNK); - break; - case DRP_TC_ATTACHED_WAIT_SNK: - set_state_tc(port, TC_ATTACH_WAIT_SNK); - break; - case DRP_TC_UNATTACHED_SRC: - set_state_tc(port, TC_UNATTACHED_SRC); - break; - case DRP_TC_ATTACHED_WAIT_SRC: - set_state_tc(port, TC_ATTACH_WAIT_SRC); - break; - -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - case DRP_TC_DRP_AUTO_TOGGLE: - set_state_tc(port, TC_DRP_AUTO_TOGGLE); - break; -#endif - - default: - CPRINTS("C%d: Error: DRP next state %d", port, next_state); - break; - } -} - -/** - * DrpAutoToggle - */ -__maybe_unused static void tc_drp_auto_toggle_entry(const int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE)) - assert(0); - - print_current_state(port); - - /* - * We need to ensure that we are waiting in the previous Rd or Rp state - * for the minimum of DRP SNK or SRC so the first toggle cause by - * transition into auto toggle doesn't violate spec timing. - */ - pd_timer_enable(port, TC_TIMER_TIMEOUT, - MAX(PD_T_DRP_SNK, PD_T_DRP_SRC)); -} - -__maybe_unused static void tc_drp_auto_toggle_run(const int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE)) - assert(0); - - /* - * A timer is running, but if a connection comes in while waiting - * then allow that to take higher priority. - */ - if (TC_CHK_FLAG(port, TC_FLAGS_CHECK_CONNECTION)) - check_drp_connection(port); - - else if (!pd_timer_is_disabled(port, TC_TIMER_TIMEOUT)) { - if (!pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) - return; - - pd_timer_disable(port, TC_TIMER_TIMEOUT); - tcpm_enable_drp_toggle(port); - - if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER)) { - set_state_tc(port, TC_LOW_POWER_MODE); - } - } -} - -__maybe_unused static void tc_drp_auto_toggle_exit(const int port) -{ - pd_timer_disable(port, TC_TIMER_TIMEOUT); -} - -__maybe_unused static void tc_low_power_mode_entry(const int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER)) - assert(0); - - print_current_state(port); - pd_timer_enable(port, TC_TIMER_LOW_POWER_TIME, PD_LPM_DEBOUNCE_US); -} - -__maybe_unused static void tc_low_power_mode_run(const int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER)) - assert(0); - - if (TC_CHK_FLAG(port, TC_FLAGS_CHECK_CONNECTION)) { - tc_start_event_loop(port); - if (pd_timer_is_disabled(port, TC_TIMER_LOW_POWER_EXIT_TIME)) { - pd_timer_enable(port, TC_TIMER_LOW_POWER_EXIT_TIME, - PD_LPM_EXIT_DEBOUNCE_US); - } else if (pd_timer_is_expired(port, - TC_TIMER_LOW_POWER_EXIT_TIME)) { - CPRINTS("C%d: Exit Low Power Mode", port); - check_drp_connection(port); - } - return; - } - - if (tc[port].tasks_preventing_lpm) - pd_timer_enable(port, TC_TIMER_LOW_POWER_TIME, - PD_LPM_DEBOUNCE_US); - - if (pd_timer_is_expired(port, TC_TIMER_LOW_POWER_TIME)) { - CPRINTS("C%d: TCPC Enter Low Power Mode", port); - TC_SET_FLAG(port, TC_FLAGS_LPM_ENGAGED); - TC_SET_FLAG(port, TC_FLAGS_LPM_TRANSITION); - tcpm_enter_low_power_mode(port); - TC_CLR_FLAG(port, TC_FLAGS_LPM_TRANSITION); - tc_pause_event_loop(port); - - pd_timer_disable(port, TC_TIMER_LOW_POWER_EXIT_TIME); - } -} - -__maybe_unused static void tc_low_power_mode_exit(const int port) -{ - pd_timer_disable(port, TC_TIMER_LOW_POWER_TIME); - pd_timer_disable(port, TC_TIMER_LOW_POWER_EXIT_TIME); -} - -/** - * Try.SRC - * - * Super State Entry Actions: - * Vconn Off - * Place Rp on CC - * Set power role to SOURCE - */ -#ifdef CONFIG_USB_PD_TRY_SRC -static void tc_try_src_entry(const int port) -{ - print_current_state(port); - - tc[port].cc_state = PD_CC_UNSET; - pd_timer_enable(port, TC_TIMER_TRY_WAIT_DEBOUNCE, PD_T_DRP_TRY); - pd_timer_enable(port, TC_TIMER_TIMEOUT, PD_T_TRY_TIMEOUT); - - /* - * We are a SNK but would prefer to be a SRC. Set the pull to - * indicate we want to be a SRC and looking for a SNK. - * - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rp. - */ - typec_select_pull(port, TYPEC_CC_RP); - - typec_select_src_current_limit_rp(port, - typec_get_default_current_limit_rp(port)); - - /* Apply Rp */ - typec_update_cc(port); -} - -static void tc_try_src_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - enum pd_cc_states new_cc_state; - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - if ((cc1 == TYPEC_CC_VOLT_RD && cc2 != TYPEC_CC_VOLT_RD) || - (cc1 != TYPEC_CC_VOLT_RD && cc2 == TYPEC_CC_VOLT_RD)) - new_cc_state = PD_CC_UFP_ATTACHED; - else - new_cc_state = PD_CC_NONE; - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, PD_T_CC_DEBOUNCE); - } - - /* - * The port shall transition to Attached.SRC when the SRC.Rd state is - * detected on exactly one of the CC1 or CC2 pins for at least - * tTryCCDebounce. - */ - if (new_cc_state == PD_CC_UFP_ATTACHED && - pd_timer_is_expired(port, TC_TIMER_CC_DEBOUNCE)) - set_state_tc(port, TC_ATTACHED_SRC); - - /* - * The port shall transition to TryWait.SNK after tDRPTry and the - * SRC.Rd state has not been detected and VBUS is within vSafe0V, - * or after tTryTimeout and the SRC.Rd state has not been detected. - */ - if (new_cc_state == PD_CC_NONE) { - if ((pd_timer_is_expired(port, TC_TIMER_TRY_WAIT_DEBOUNCE) && - pd_check_vbus_level(port, VBUS_SAFE0V)) || - pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) { - set_state_tc(port, TC_TRY_WAIT_SNK); - } - } -} - -static void tc_try_src_exit(const int port) -{ - pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE); - pd_timer_disable(port, TC_TIMER_TIMEOUT); - pd_timer_disable(port, TC_TIMER_TRY_WAIT_DEBOUNCE); -} - -/** - * TryWait.SNK - * - * Super State Entry Actions: - * Vconn Off - * Place Rd on CC - * Set power role to SINK - */ -static void tc_try_wait_snk_entry(const int port) -{ - print_current_state(port); - - tc_enable_pd(port, 0); - tc[port].cc_state = PD_CC_UNSET; - pd_timer_enable(port, TC_TIMER_TRY_WAIT_DEBOUNCE, PD_T_CC_DEBOUNCE); - - /* - * We were a SNK, tried to be a SRC and it didn't work out. Try to - * go back to being a SNK. Set the pull to indicate we want to be - * a SNK and looking for a SRC. - * - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rd. - */ - typec_select_pull(port, TYPEC_CC_RD); - - /* Apply Rd */ - typec_update_cc(port); -} - -static void tc_try_wait_snk_run(const int port) -{ - enum tcpc_cc_voltage_status cc1, cc2; - enum pd_cc_states new_cc_state; - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - /* We only care about CCs being open */ - if (cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_OPEN) - new_cc_state = PD_CC_NONE; - else - new_cc_state = PD_CC_UNSET; - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - pd_timer_enable(port, TC_TIMER_PD_DEBOUNCE, PD_T_PD_DEBOUNCE); - } - - /* - * The port shall transition to Unattached.SNK when the state of both - * of the CC1 and CC2 pins is SNK.Open for at least tPDDebounce. - */ - if (new_cc_state == PD_CC_NONE && - pd_timer_is_expired(port, TC_TIMER_PD_DEBOUNCE)) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - /* - * The port shall transition to Attached.SNK after tCCDebounce if or - * when VBUS is detected. - */ - if (pd_timer_is_expired(port, TC_TIMER_TRY_WAIT_DEBOUNCE) && - pd_is_vbus_present(port)) - set_state_tc(port, TC_ATTACHED_SNK); -} - -static void tc_try_wait_snk_exit(const int port) -{ - pd_timer_disable(port, TC_TIMER_PD_DEBOUNCE); - pd_timer_disable(port, TC_TIMER_TRY_WAIT_DEBOUNCE); -} -#endif - -/* - * CTUnattached.SNK - */ -__maybe_unused static void tc_ct_unattached_snk_entry(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PE_SM)) - assert(0); - - print_current_state(port); - - /* - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rd. - */ - typec_select_pull(port, TYPEC_CC_RD); - typec_update_cc(port); - - tc[port].cc_state = PD_CC_UNSET; - - /* Set power role to sink */ - tc_set_power_role(port, PD_ROLE_SINK); - tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role); - - /* - * The policy engine is in the disabled state. Disable PD and - * re-enable it - */ - tc_enable_pd(port, 0); - - pd_timer_enable(port, TC_TIMER_TIMEOUT, PD_POWER_SUPPLY_TURN_ON_DELAY); -} - -__maybe_unused static void tc_ct_unattached_snk_run(int port) -{ - enum tcpc_cc_voltage_status cc1; - enum tcpc_cc_voltage_status cc2; - enum pd_cc_states new_cc_state; - - if (!IS_ENABLED(CONFIG_USB_PE_SM)) - assert(0); - - if (!pd_timer_is_disabled(port, TC_TIMER_TIMEOUT)) { - if (pd_timer_is_expired(port, TC_TIMER_TIMEOUT)) { - tc_enable_pd(port, 1); - pd_timer_disable(port, TC_TIMER_TIMEOUT); - } else { - return; - } - } - - /* Wait until Protocol Layer is ready */ - if (!prl_is_running(port)) - return; - - /* - * Hard Reset is sent when the PE layer is disabled due to a - * CTVPD connection. - */ - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED); - /* Nothing to do. Just signal hard reset completion */ - pe_ps_reset_complete(port); - } - - /* Check for connection */ - tcpm_get_cc(port, &cc1, &cc2); - - /* We only care about CCs being open */ - if (cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_OPEN) - new_cc_state = PD_CC_NONE; - else - new_cc_state = PD_CC_UNSET; - - /* Debounce the cc state */ - if (new_cc_state != tc[port].cc_state) { - tc[port].cc_state = new_cc_state; - pd_timer_enable(port, TC_TIMER_CC_DEBOUNCE, PD_T_VPDDETACH); - } - - /* - * The port shall transition to Unattached.SNK if the state of - * the CC pin is SNK.Open for tVPDDetach after VBUS is vSafe0V. - */ - else if (pd_timer_is_expired(port, TC_TIMER_CC_DEBOUNCE)) { - if (new_cc_state == PD_CC_NONE && - pd_check_vbus_level(port, VBUS_SAFE0V)) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - } - - /* - * The port shall transition to CTAttached.SNK when VBUS is detected. - */ - if (pd_is_vbus_present(port)) - set_state_tc(port, TC_CT_ATTACHED_SNK); -} - -__maybe_unused static void tc_ct_unattached_snk_exit(int port) -{ - pd_timer_disable(port, TC_TIMER_CC_DEBOUNCE); - pd_timer_disable(port, TC_TIMER_TIMEOUT); -} - -/** - * CTAttached.SNK - */ -__maybe_unused static void tc_ct_attached_snk_entry(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PE_SM)) - assert(0); - - print_current_state(port); - - /* The port shall reject a VCONN swap request. */ - TC_SET_FLAG(port, TC_FLAGS_REJECT_VCONN_SWAP); -} - -__maybe_unused static void tc_ct_attached_snk_run(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PE_SM)) - assert(0); - - /* - * Hard Reset is sent when the PE layer is disabled due to a - * CTVPD connection. - */ - if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED)) { - TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET_REQUESTED); - /* Nothing to do. Just signal hard reset completion */ - pe_ps_reset_complete(port); - } - - /* - * A port that is not in the process of a USB PD Hard Reset shall - * transition to CTUnattached.SNK within tSinkDisconnect when VBUS - * falls below vSinkDisconnect - */ - if (pd_check_vbus_level(port, VBUS_REMOVED)) { - set_state_tc(port, TC_CT_UNATTACHED_SNK); - return; - } - - /* - * The port shall operate in one of the Sink Power Sub-States - * and remain within the Sink Power Sub-States, until either VBUS is - * removed or a USB PD contract is established with the source. - */ - if (!pe_is_explicit_contract(port)) - sink_power_sub_states(port); -} - -__maybe_unused static void tc_ct_attached_snk_exit(int port) -{ - if (!IS_ENABLED(CONFIG_USB_PE_SM)) - assert(0); - - /* Stop drawing power */ - sink_stop_drawing_current(port); - - TC_CLR_FLAG(port, TC_FLAGS_REJECT_VCONN_SWAP); -} - -/** - * Super State CC_RD - */ -static void tc_cc_rd_entry(const int port) -{ - /* Disable VCONN */ - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_vconn(port, 0); - - /* Set power role to sink */ - tc_set_power_role(port, PD_ROLE_SINK); - tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role); -} - - -/** - * Super State CC_RP - */ -static void tc_cc_rp_entry(const int port) -{ - /* Disable VCONN */ - if (IS_ENABLED(CONFIG_USBC_VCONN)) - set_vconn(port, 0); - - /* Set power role to source */ - tc_set_power_role(port, PD_ROLE_SOURCE); - tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role); -} - -/** - * Super State CC_OPEN - */ -static void tc_cc_open_entry(const int port) -{ - /* Ensure we are not sourcing Vbus */ - tc_src_power_off(port); - - /* Disable VCONN */ - set_vconn(port, 0); - - /* - * Ensure we disable discharging before setting CC lines to open. - * If we were sourcing above, then we already drained Vbus. If partner - * is sourcing Vbus they will drain Vbus if they are PD-capable. This - * should only be done if a battery is present as a batteryless - * device will brown out when AutoDischargeDisconnect is disabled and - * we do not want this to happen until the set_cc open/open to make - * sure the TCPC has managed its internal states for disconnecting - * the only source of power it has. - */ - if (battery_is_present()) - tcpm_enable_auto_discharge_disconnect(port, 0); - - /* - * We may brown out after applying CC open, so flush console first. - * Console flush can take a long time, so if we aren't in danger of - * browning out, don't do it so we can meet certain compliance timing - * requirements. - */ - CPRINTS("C%d: Applying CC Open!", port); - if (!battery_is_present()) - cflush(); - - /* Remove terminations from CC */ - typec_select_pull(port, TYPEC_CC_OPEN); - typec_update_cc(port); - - tc_set_partner_role(port, PPC_DEV_DISCONNECTED); - tc_detached(port); -} - -void tc_set_debug_level(enum debug_level debug_level) -{ -#ifndef CONFIG_USB_PD_DEBUG_LEVEL - tc_debug_level = debug_level; -#endif -} - -void tc_usb_firmware_fw_update_limited_run(int port) -{ - TC_SET_FLAG(port, TC_FLAGS_USB_RETIMER_FW_UPDATE_LTD_RUN); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void tc_usb_firmware_fw_update_run(int port) -{ - TC_SET_FLAG(port, TC_FLAGS_USB_RETIMER_FW_UPDATE_RUN); - task_wake(PD_PORT_TO_TASK_ID(port)); -} - -void tc_run(const int port) -{ - /* - * If pd_set_suspend set TC_FLAGS_REQUEST_SUSPEND, go directly to - * TC_DISABLED. - */ - if (get_state_tc(port) != TC_DISABLED - && TC_CHK_FLAG(port, TC_FLAGS_REQUEST_SUSPEND)) { - /* Invalidate a contract, if there is one */ - if (IS_ENABLED(CONFIG_USB_PE_SM)) - pe_invalidate_explicit_contract(port); - - set_state_tc(port, TC_DISABLED); - } - - /* If error recovery has been requested, transition now */ - if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_ERROR_RECOVERY)) { - if (IS_ENABLED(CONFIG_USB_PE_SM)) - pe_invalidate_explicit_contract(port); - set_state_tc(port, TC_ERROR_RECOVERY); - } - - if (IS_ENABLED(CONFIG_USBC_RETIMER_FW_UPDATE)) { - if (TC_CHK_FLAG(port, TC_FLAGS_USB_RETIMER_FW_UPDATE_RUN)) { - TC_CLR_FLAG(port, TC_FLAGS_USB_RETIMER_FW_UPDATE_RUN); - usb_retimer_fw_update_process_op_cb(port); - } - } - - run_state(port, &tc[port].ctx); -} - -static void pd_chipset_resume(void) -{ - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) { - if(IS_ENABLED(CONFIG_USB_PE_SM)) - pd_resume_check_pr_swap_needed(i); - - pd_set_dual_role_and_event(i, - PD_DRP_TOGGLE_ON, - PD_EVENT_UPDATE_DUAL_ROLE - | PD_EVENT_POWER_STATE_CHANGE); - } - - CPRINTS("PD:S3->S0"); -} -DECLARE_HOOK(HOOK_CHIPSET_RESUME, pd_chipset_resume, HOOK_PRIO_DEFAULT); - -static void pd_chipset_suspend(void) -{ - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) { - pd_set_dual_role_and_event(i, - pd_get_drp_state_in_suspend(), - PD_EVENT_UPDATE_DUAL_ROLE - | PD_EVENT_POWER_STATE_CHANGE); - } - - CPRINTS("PD:S0->S3"); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, pd_chipset_suspend, HOOK_PRIO_DEFAULT); - -static void pd_chipset_reset(void) -{ - int i; - - if (!IS_ENABLED(CONFIG_USB_PE_SM)) - return; - - for (i = 0; i < board_get_usb_pd_port_count(); i++) { - enum tcpci_msg_type tx; - - /* Do not notify the AP of irrelevant past Hard Resets. */ - pd_clear_events(i, PD_STATUS_EVENT_HARD_RESET); - - /* - * Re-set events for SOP and SOP' discovery complete so the - * kernel knows to consume discovery information for them. - */ - for (tx = TCPCI_MSG_SOP; tx <= TCPCI_MSG_SOP_PRIME; tx++) { - if (pd_get_identity_discovery(i, tx) != PD_DISC_NEEDED - && pd_get_svids_discovery(i, tx) != PD_DISC_NEEDED - && pd_get_modes_discovery(i, tx) != PD_DISC_NEEDED) - pd_notify_event(i, tx == TCPCI_MSG_SOP ? - PD_STATUS_EVENT_SOP_DISC_DONE : - PD_STATUS_EVENT_SOP_PRIME_DISC_DONE); - } - - /* Exit mode so AP can enter mode again after reset */ - if (IS_ENABLED(CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY)) - dpm_set_mode_exit_request(i); - } -} -DECLARE_HOOK(HOOK_CHIPSET_RESET, pd_chipset_reset, HOOK_PRIO_DEFAULT); - -static void pd_chipset_startup(void) -{ - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) { - TC_SET_FLAG(i, TC_FLAGS_UPDATE_USB_MUX); - pd_set_dual_role_and_event(i, - pd_get_drp_state_in_suspend(), - PD_EVENT_UPDATE_DUAL_ROLE - | PD_EVENT_POWER_STATE_CHANGE); - /* - * Request port discovery to restore any - * alt modes. - * TODO(b/158042116): Do not start port discovery if there - * is an existing connection. - */ - if (IS_ENABLED(CONFIG_USB_PE_SM)) - pd_dpm_request(i, DPM_REQUEST_PORT_DISCOVERY); - } - - CPRINTS("PD:S5->S3"); -} -DECLARE_HOOK(HOOK_CHIPSET_STARTUP, pd_chipset_startup, HOOK_PRIO_DEFAULT); - -static void pd_chipset_shutdown(void) -{ - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) { - TC_SET_FLAG(i, TC_FLAGS_UPDATE_USB_MUX); - pd_set_dual_role_and_event(i, - PD_DRP_FORCE_SINK, - PD_EVENT_UPDATE_DUAL_ROLE - | PD_EVENT_POWER_STATE_CHANGE); - } - - CPRINTS("PD:S3->S5"); -} -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pd_chipset_shutdown, HOOK_PRIO_DEFAULT); - -static void pd_set_power_change(void) -{ - int i; - - for (i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) { - task_set_event(PD_PORT_TO_TASK_ID(i), - PD_EVENT_POWER_STATE_CHANGE); - } -} -DECLARE_DEFERRED(pd_set_power_change); - -static void pd_chipset_hard_off(void) -{ - /* - * Wait 1 second to check our Vconn sourcing status, as the power rails - * which were supporting it may take some time to change after entering - * G3. - */ - hook_call_deferred(&pd_set_power_change_data, 1 * SECOND); -} -DECLARE_HOOK(HOOK_CHIPSET_HARD_OFF, pd_chipset_hard_off, HOOK_PRIO_DEFAULT); - -/* - * Type-C State Hierarchy (Sub-States are listed inside the boxes) - * - * |TC_CC_RD --------------| |TC_CC_RP ------------------------| - * | | | | - * | TC_UNATTACHED_SNK | | TC_UNATTACHED_SRC | - * | TC_ATTACH_WAIT_SNK | | TC_ATTACH_WAIT_SRC | - * | TC_TRY_WAIT_SNK | | TC_TRY_SRC | - * |-----------------------| |---------------------------------| - * - * |TC_CC_OPEN -----------| - * | | - * | TC_DISABLED | - * | TC_ERROR_RECOVERY | - * |----------------------| - * - * TC_ATTACHED_SNK TC_ATTACHED_SRC TC_DRP_AUTO_TOGGLE TC_LOW_POWER_MODE - * - */ -static __const_data const struct usb_state tc_states[] = { - /* Super States */ - [TC_CC_OPEN] = { - .entry = tc_cc_open_entry, - }, - [TC_CC_RD] = { - .entry = tc_cc_rd_entry, - }, - [TC_CC_RP] = { - .entry = tc_cc_rp_entry, - }, - /* Normal States */ - [TC_DISABLED] = { - .entry = tc_disabled_entry, - .run = tc_disabled_run, - .exit = tc_disabled_exit, - .parent = &tc_states[TC_CC_OPEN], - }, - [TC_ERROR_RECOVERY] = { - .entry = tc_error_recovery_entry, - .run = tc_error_recovery_run, - .exit = tc_error_recovery_exit, - .parent = &tc_states[TC_CC_OPEN], - }, - [TC_UNATTACHED_SNK] = { - .entry = tc_unattached_snk_entry, - .run = tc_unattached_snk_run, - .exit = tc_unattached_snk_exit, - .parent = &tc_states[TC_CC_RD], - }, - [TC_ATTACH_WAIT_SNK] = { - .entry = tc_attach_wait_snk_entry, - .run = tc_attach_wait_snk_run, - .exit = tc_attach_wait_snk_exit, - .parent = &tc_states[TC_CC_RD], - }, - [TC_ATTACHED_SNK] = { - .entry = tc_attached_snk_entry, - .run = tc_attached_snk_run, - .exit = tc_attached_snk_exit, - }, - [TC_UNATTACHED_SRC] = { - .entry = tc_unattached_src_entry, - .run = tc_unattached_src_run, - .exit = tc_unattached_src_exit, - .parent = &tc_states[TC_CC_RP], - }, - [TC_ATTACH_WAIT_SRC] = { - .entry = tc_attach_wait_src_entry, - .run = tc_attach_wait_src_run, - .exit = tc_attach_wait_src_exit, - .parent = &tc_states[TC_CC_RP], - }, - [TC_ATTACHED_SRC] = { - .entry = tc_attached_src_entry, - .run = tc_attached_src_run, - .exit = tc_attached_src_exit, - }, -#ifdef CONFIG_USB_PD_TRY_SRC - [TC_TRY_SRC] = { - .entry = tc_try_src_entry, - .run = tc_try_src_run, - .exit = tc_try_src_exit, - .parent = &tc_states[TC_CC_RP], - }, - [TC_TRY_WAIT_SNK] = { - .entry = tc_try_wait_snk_entry, - .run = tc_try_wait_snk_run, - .exit = tc_try_wait_snk_exit, - .parent = &tc_states[TC_CC_RD], - }, -#endif /* CONFIG_USB_PD_TRY_SRC */ -#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE - [TC_DRP_AUTO_TOGGLE] = { - .entry = tc_drp_auto_toggle_entry, - .run = tc_drp_auto_toggle_run, - .exit = tc_drp_auto_toggle_exit, - }, -#endif /* CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE */ -#ifdef CONFIG_USB_PD_TCPC_LOW_POWER - [TC_LOW_POWER_MODE] = { - .entry = tc_low_power_mode_entry, - .run = tc_low_power_mode_run, - .exit = tc_low_power_mode_exit, - }, -#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ -#ifdef CONFIG_USB_PE_SM - [TC_CT_UNATTACHED_SNK] = { - .entry = tc_ct_unattached_snk_entry, - .run = tc_ct_unattached_snk_run, - .exit = tc_ct_unattached_snk_exit, - }, - [TC_CT_ATTACHED_SNK] = { - .entry = tc_ct_attached_snk_entry, - .run = tc_ct_attached_snk_run, - .exit = tc_ct_attached_snk_exit, - }, -#endif -}; - -#if defined(TEST_BUILD) && defined(USB_PD_DEBUG_LABELS) -const struct test_sm_data test_tc_sm_data[] = { - { - .base = tc_states, - .size = ARRAY_SIZE(tc_states), - .names = tc_state_names, - .names_size = ARRAY_SIZE(tc_state_names), - }, -}; -const int test_tc_sm_data_size = ARRAY_SIZE(test_tc_sm_data); -#endif diff --git a/common/usbc/usb_tc_vpd_sm.c b/common/usbc/usb_tc_vpd_sm.c deleted file mode 100644 index f230d15003..0000000000 --- a/common/usbc/usb_tc_vpd_sm.c +++ /dev/null @@ -1,430 +0,0 @@ -/* Copyright 2019 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "common.h" -#include "console.h" -#include "system.h" -#include "task.h" -#include "tcpm/tcpm.h" -#include "usb_pd.h" -#include "usb_tc_sm.h" -#include "usb_sm.h" -#include "vpd_api.h" - -/* USB Type-C VCONN Powered Device module */ - -#ifdef CONFIG_COMMON_RUNTIME -#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USB, format, ## args) -#else /* CONFIG_COMMON_RUNTIME */ -#define CPRINTF(format, args...) -#define CPRINTS(format, args...) -#endif - -/* Type-C Layer Flags */ -#define TC_FLAGS_VCONN_ON BIT(0) - -/** - * This is the Type-C Port object that contains information needed to - * implement a VCONN Powered Device. - */ -static struct type_c { - /* state machine context */ - struct sm_ctx ctx; - /* Higher-level power deliver state machines are enabled if true. */ - uint8_t pd_enable; - /* port flags, see TC_FLAGS_* */ - uint32_t flags; - /* Time a port shall wait before it can determine it is attached */ - uint64_t cc_debounce; - /* VPD host port cc state */ - enum pd_cc_states host_cc_state; - uint8_t ct_cc; -} tc[CONFIG_USB_PD_PORT_MAX_COUNT]; - -/* List of all TypeC-level states */ -enum usb_tc_state { - /* Normal States */ - TC_DISABLED, - TC_UNATTACHED_SNK, - TC_ATTACH_WAIT_SNK, - TC_ATTACHED_SNK, - /* Super States */ - TC_VBUS_CC_ISO, - TC_HOST_RARD, - TC_HOST_OPEN, -}; -/* Forward declare the full list of states. This is indexed by usb_tc_state */ -static const struct usb_state tc_states[]; - -/* List of human readable state names for console debugging */ -__maybe_unused static const char * const tc_state_names[] = { -#ifdef CONFIG_COMMON_RUNTIME - [TC_DISABLED] = "Disabled", - [TC_UNATTACHED_SNK] = "Unattached.SNK", - [TC_ATTACH_WAIT_SNK] = "AttachWait.SNK", - [TC_ATTACHED_SNK] = "Attached.SNK", -#endif -}; - -/* Forward declare private, common functions */ -static void set_state_tc(const int port, enum usb_tc_state new_state); - -/* - * TCPC CC/Rp management - * - * Stub for linking purposes. - * This is not supported for vpd, it uses a different mechanism to update - * cc values. - */ -void typec_select_pull(int port, enum tcpc_cc_pull pull) -{ -} -void typec_select_src_current_limit_rp(int port, enum tcpc_rp_value rp) -{ -} -void typec_select_src_collision_rp(int port, enum tcpc_rp_value rp) -{ -} -int typec_update_cc(int port) -{ - return EC_SUCCESS; -} - -/* Public TypeC functions */ - -void tc_state_init(int port) -{ - int res = 0; - - res = tcpm_init(port); - - CPRINTS("C%d: init %s", port, res ? "failed" : "ready"); - - /* Disable TCPC RX until connection is established */ - tcpm_set_rx_enable(port, 0); - - set_state_tc(port, res ? TC_DISABLED : TC_UNATTACHED_SNK); - - /* Disable pd state machines */ - tc[port].pd_enable = 0; - tc[port].flags = 0; -} - -enum pd_power_role pd_get_power_role(int port) -{ - /* Vconn power device is always the sink */ - return PD_ROLE_SINK; -} - -enum pd_cable_plug tc_get_cable_plug(int port) -{ - /* Vconn power device is always the cable */ - return PD_PLUG_FROM_CABLE; -} - -enum pd_data_role pd_get_data_role(int port) -{ - /* Vconn power device doesn't have a data role, but UFP match SNK */ - return PD_ROLE_UFP; -} - -/* Note tc_set_power_role and tc_set_data_role are unimplemented */ - -uint8_t tc_get_polarity(int port) -{ - /* Does not track polarity yet */ - return 0; -} - -uint8_t tc_get_pd_enabled(int port) -{ - return tc[port].pd_enable; -} - -void tc_event_check(int port, int evt) -{ - /* Do Nothing */ -} - -/* - * Private Functions - */ - -/* Set the TypeC state machine to a new state. */ -static void set_state_tc(const int port, const enum usb_tc_state new_state) -{ - set_state(port, &tc[port].ctx, &tc_states[new_state]); -} - -/* Get the current TypeC state. */ -test_export_static enum usb_tc_state get_state_tc(const int port) -{ - return tc[port].ctx.current - &tc_states[0]; -} - -test_mockable_static void print_current_state(const int port) -{ - CPRINTS("C%d: %s", port, tc_state_names[get_state_tc(port)]); -} - -/** - * Disabled - * - * Super State Entries: - * Enable mcu communication - * Remove the terminations from Host CC - */ -static void tc_disabled_entry(const int port) -{ - print_current_state(port); -} - -static void tc_disabled_run(const int port) -{ - task_wait_event(-1); -} - -void pd_set_suspend(int port, int suspend) -{ - /* - * This shouldn't happen. If it does, we need to send an event to the - * PD task to put the SM into the disabled state. It is not safe to - * directly set_state here since this may be in another task. - */ - assert(false); -} - -static void tc_disabled_exit(const int port) -{ - if (!IS_ENABLED(CONFIG_USB_PD_TCPC)) { - if (tcpm_init(port) != 0) { - CPRINTS("C%d: restart failed!", port); - return; - } - } - - CPRINTS("C%d: resumed!", port); -} - -/** - * Unattached.SNK - * - * Super State Entry: - * Enable mcu communication - * Place Ra on VCONN and Rd on Host CC - */ -static void tc_unattached_snk_entry(const int port) -{ - print_current_state(port); -} - -static void tc_unattached_snk_run(const int port) -{ - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - /* - * Transition to AttachWait.SNK when a Source connection is - * detected, as indicated by the SNK.Rp state on its Host-side - * port’s CC pin. - */ - if (cc_is_rp(host_cc)) - set_state_tc(port, TC_ATTACH_WAIT_SNK); -} - -/** - * AttachedWait.SNK - * - * Super State Entry: - * Enable mcu communication - * Place Ra on VCONN and Rd on Host CC - */ -static void tc_attach_wait_snk_entry(const int port) -{ - print_current_state(port); - - /* Forces an initial debounce in run function */ - tc[port].host_cc_state = -1; -} - -static void tc_attach_wait_snk_run(const int port) -{ - int host_new_cc_state; - int host_cc; - - /* Check Host CC for connection */ - vpd_host_get_cc(&host_cc); - - if (cc_is_rp(host_cc)) - host_new_cc_state = PD_CC_DFP_ATTACHED; - else - host_new_cc_state = PD_CC_NONE; - - /* Debounce the Host CC state */ - if (tc[port].host_cc_state != host_new_cc_state) { - tc[port].host_cc_state = host_new_cc_state; - if (host_new_cc_state == PD_CC_DFP_ATTACHED) - tc[port].cc_debounce = get_time().val + - PD_T_CC_DEBOUNCE; - else - tc[port].cc_debounce = get_time().val + - PD_T_PD_DEBOUNCE; - - return; - } - - /* Wait for Host CC debounce */ - if (get_time().val < tc[port].cc_debounce) - return; - - /* - * A VCONN-Powered USB Device shall transition to - * Attached.SNK after the state of the Host-side port’s CC pin is - * SNK.Rp for at least tCCDebounce and either host-side VCONN or - * VBUS is detected. - * - * Transition to Unattached.SNK when the state of both the CC1 and - * CC2 pins is SNK.Open for at least tPDDebounce. - */ - if (tc[port].host_cc_state == PD_CC_DFP_ATTACHED && - (vpd_is_vconn_present() || vpd_is_host_vbus_present())) - set_state_tc(port, TC_ATTACHED_SNK); - else if (tc[port].host_cc_state == PD_CC_NONE) - set_state_tc(port, TC_UNATTACHED_SNK); -} - -/** - * Attached.SNK - */ -static void tc_attached_snk_entry(const int port) -{ - print_current_state(port); - - /* Enable PD */ - tc[port].pd_enable = 1; - pd_set_polarity(port, 0); -} - -static void tc_attached_snk_run(const int port) -{ - /* Has host vbus and vconn been removed */ - if (!vpd_is_host_vbus_present() && !vpd_is_vconn_present()) { - set_state_tc(port, TC_UNATTACHED_SNK); - return; - } - - if (vpd_is_vconn_present()) { - if (!(tc[port].flags & TC_FLAGS_VCONN_ON)) { - /* VCONN detected. Remove RA */ - vpd_host_set_pull(TYPEC_CC_RD, 0); - tc[port].flags |= TC_FLAGS_VCONN_ON; - } - } -} - -static void tc_attached_snk_exit(const int port) -{ - /* Disable PD */ - tc[port].pd_enable = 0; - tc[port].flags &= ~TC_FLAGS_VCONN_ON; -} - -/** - * Super State HOST_RARD - */ -static void tc_host_rard_entry(const int port) -{ - /* Place Ra on VCONN and Rd on Host CC */ - vpd_host_set_pull(TYPEC_CC_RA_RD, 0); -} - -/** - * Super State HOST_OPEN - */ -static void tc_host_open_entry(const int port) -{ - /* Remove the terminations from Host CC */ - vpd_host_set_pull(TYPEC_CC_OPEN, 0); -} - -/** - * Super State VBUS_CC_ISO - */ -static void tc_vbus_cc_iso_entry(const int port) -{ - /* Enable mcu communication and cc */ - vpd_mcu_cc_en(1); -} - -void tc_run(const int port) -{ - run_state(port, &tc[port].ctx); -} - -/* - * Type-C State Hierarchy (Sub-States are listed inside the boxes) - * - * | TC_VBUS_CC_ISO ----------------------------------------| - * | | - * | | TC_HOST_RARD -----------| | TC_HOST_OPEN ---------| | - * | | | | | | - * | | TC_UNATTACHED_SNK | | TC_DISABLED | | - * | | TC_ATTACH_WAIT_SNK | |-----------------------| | - * | |-------------------------| | - * |--------------------------------------------------------| - * - * TC_ATTACHED_SNK - */ -static const struct usb_state tc_states[] = { - /* Super States */ - [TC_VBUS_CC_ISO] = { - .entry = tc_vbus_cc_iso_entry, - }, - [TC_HOST_RARD] = { - .entry = tc_host_rard_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - [TC_HOST_OPEN] = { - .entry = tc_host_open_entry, - .parent = &tc_states[TC_VBUS_CC_ISO], - }, - /* Normal States */ - [TC_DISABLED] = { - .entry = tc_disabled_entry, - .run = tc_disabled_run, - .exit = tc_disabled_exit, - .parent = &tc_states[TC_HOST_OPEN], - }, - [TC_UNATTACHED_SNK] = { - .entry = tc_unattached_snk_entry, - .run = tc_unattached_snk_run, - .parent = &tc_states[TC_HOST_RARD], - }, - [TC_ATTACH_WAIT_SNK] = { - .entry = tc_attach_wait_snk_entry, - .run = tc_attach_wait_snk_run, - .parent = &tc_states[TC_HOST_RARD], - }, - [TC_ATTACHED_SNK] = { - .entry = tc_attached_snk_entry, - .run = tc_attached_snk_run, - .exit = tc_attached_snk_exit, - }, -}; - -#ifdef TEST_BUILD -const struct test_sm_data test_tc_sm_data[] = { - { - .base = tc_states, - .size = ARRAY_SIZE(tc_states), - .names = tc_state_names, - .names_size = ARRAY_SIZE(tc_state_names), - }, -}; -const int test_tc_sm_data_size = ARRAY_SIZE(test_tc_sm_data); -#endif diff --git a/common/usbc/usbc_pd_policy.c b/common/usbc/usbc_pd_policy.c deleted file mode 100644 index 6a06d4014f..0000000000 --- a/common/usbc/usbc_pd_policy.c +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright 2021 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "console.h" -#include "ec_commands.h" -#include "usb_pe_sm.h" -#include "usb_tc_sm.h" - -/* - * TODO(b:159715784): Implement a more robust solution - * to managing PD Policies. - */ - -/* - * Default Port Discovery DR Swap Policy. - * - * 1) If dr_swap_to_dfp_flag == true and port data role is UFP, - * transition to pe_drs_send_swap - */ -__overridable bool port_discovery_dr_swap_policy(int port, - enum pd_data_role dr, bool dr_swap_flag) -{ - if (dr_swap_flag && dr == PD_ROLE_UFP) - return true; - - /* Do not perform a DR swap */ - return false; -} - -/* - * Default Port Discovery VCONN Swap Policy. - * - * 1) If vconn_swap_to_on_flag == true, and vconn is currently off, - * 2) Sourcing VCONN is possible - * then transition to pe_vcs_send_swap - */ -__overridable bool port_discovery_vconn_swap_policy(int port, - bool vconn_swap_flag) -{ - if (IS_ENABLED(CONFIG_USBC_VCONN) && vconn_swap_flag && - !tc_is_vconn_src(port) && tc_check_vconn_swap(port)) - return true; - - /* Do not perform a VCONN swap */ - return false; -} diff --git a/common/usbc/usbc_task.c b/common/usbc/usbc_task.c deleted file mode 100644 index 09be36cd5e..0000000000 --- a/common/usbc/usbc_task.c +++ /dev/null @@ -1,182 +0,0 @@ -/* Copyright 2019 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "battery.h" -#include "battery_smart.h" -#include "board.h" -#include "charge_manager.h" -#include "charge_state.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "cros_version.h" -#include "ec_commands.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "registers.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" -#include "usb_charge.h" -#include "usb_mux.h" -#include "usb_pd.h" -#include "usb_pd_timer.h" -#include "usb_prl_sm.h" -#include "tcpm/tcpm.h" -#include "usb_pe_sm.h" -#include "usb_prl_sm.h" -#include "usb_sm.h" -#include "usb_tc_sm.h" -#include "usbc_ppc.h" - -#define USBC_EVENT_TIMEOUT (5 * MSEC) -#define USBC_MIN_EVENT_TIMEOUT (1 * MSEC) - -#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) -#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) - -/* - * If CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT is not defined then - * _GPIO_CCD_MODE_ODL is not needed. Declare as extern so IS_ENABLED will work. - */ -#ifndef CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT -extern int _GPIO_CCD_MODE_ODL; -#else -#define _GPIO_CCD_MODE_ODL GPIO_CCD_MODE_ODL -#endif /* CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT */ - -static uint8_t paused[CONFIG_USB_PD_PORT_MAX_COUNT]; - -void tc_pause_event_loop(int port) -{ - paused[port] = 1; -} - -bool tc_event_loop_is_paused(int port) -{ - return paused[port]; -} - -void tc_start_event_loop(int port) -{ - /* - * Only generate TASK_EVENT_WAKE event if state - * machine is transitioning to un-paused - */ - if (paused[port]) { - paused[port] = 0; - task_set_event(PD_PORT_TO_TASK_ID(port), TASK_EVENT_WAKE); - } -} - -static void pd_task_init(int port) -{ - if (IS_ENABLED(CONFIG_USB_TYPEC_SM)) - tc_state_init(port); - paused[port] = 0; - - /* - * Since most boards configure the TCPC interrupt as edge - * and it is possible that the interrupt line was asserted between init - * and calling set_state, we need to process any pending interrupts now. - * Otherwise future interrupts will never fire because another edge - * never happens. Note this needs to happen after set_state() is called. - */ - if (IS_ENABLED(CONFIG_HAS_TASK_PD_INT)) - schedule_deferred_pd_interrupt(port); - - /* - * GPIO_CCD_MODE_ODL must be initialized with GPIO_ODR_HIGH - * when CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT is enabled - */ - if (IS_ENABLED(CONFIG_ASSERT_CCD_MODE_ON_DTS_CONNECT)) - ASSERT(gpio_get_default_flags(_GPIO_CCD_MODE_ODL) & - GPIO_ODR_HIGH); -} - -static int pd_task_timeout(int port) -{ - int timeout; - - if (paused[port]) - timeout = -1; - else { - timeout = pd_timer_next_expiration(port); - if (timeout < 0 || timeout > USBC_EVENT_TIMEOUT) - timeout = USBC_EVENT_TIMEOUT; - else if (timeout < USBC_MIN_EVENT_TIMEOUT) - timeout = USBC_MIN_EVENT_TIMEOUT; - } - return timeout; -} - -static bool pd_task_loop(int port) -{ - /* wait for next event/packet or timeout expiration */ - const uint32_t evt = task_wait_event(pd_task_timeout(port)); - - /* Manage expired PD Timers on timeouts */ - if (evt & TASK_EVENT_TIMER) - pd_timer_manage_expired(port); - - /* - * Re-use TASK_EVENT_RESET_DONE in tests to restart the USB task - * if this code is running in a unit test. - */ - if (IS_ENABLED(TEST_BUILD) && (evt & TASK_EVENT_RESET_DONE)) - return false; - - /* handle events that affect the state machine as a whole */ - if (IS_ENABLED(CONFIG_USB_TYPEC_SM)) - tc_event_check(port, evt); - - /* - * run port controller task to check CC and/or read incoming - * messages - */ - if (IS_ENABLED(CONFIG_USB_PD_TCPC)) - tcpc_run(port, evt); - - /* Run policy engine state machine */ - if (IS_ENABLED(CONFIG_USB_PE_SM)) - pe_run(port, evt, tc_get_pd_enabled(port)); - - /* Run protocol state machine */ - if (IS_ENABLED(CONFIG_USB_PRL_SM) || IS_ENABLED(CONFIG_TEST_USB_PE_SM)) - prl_run(port, evt, tc_get_pd_enabled(port)); - - /* Run TypeC state machine */ - if (IS_ENABLED(CONFIG_USB_TYPEC_SM)) - tc_run(port); - - return true; -} - -void pd_task(void *u) -{ - int port = TASK_ID_TO_PD_PORT(task_get_current()); - - /* - * If port does not exist, return - */ - if (port >= board_get_usb_pd_port_count()) - return; - - while (1) { - pd_timer_init(port); - pd_task_init(port); - - /* As long as pd_task_loop returns true, keep running the loop. - * pd_task_loop returns false when the code needs to re-init - * the task, so once the code breaks out of the inner while - * loop, the re-init code at the top of the outer while loop - * will run. - */ - while (pd_task_loop(port)) - continue; - } -} |