summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorSam Hurst <shurst@google.com>2018-09-13 09:27:08 -0700
committerchrome-bot <chrome-bot@chromium.org>2019-04-08 22:30:19 -0700
commit0fe6147a9d8d9feef5049aa6c6c4a6ad30d12b26 (patch)
tree05d4509bcfe68a248ec3fa58168f3de2536c2d9c /include
parente097feb8b2db20cd2435a483517356defa222db1 (diff)
downloadchrome-ec-0fe6147a9d8d9feef5049aa6c6c4a6ad30d12b26.tar.gz
chocodile_vpdmcu: Firmware for chocodile mcu
Implement Chocodile Charge-Through Vconn Powered firmware for mcu using new Type-C/PD State machine stack. BUG=b:115626873 BRANCH=none TEST=manual Charge-Through was tested on an Atlas running a DRP USB-C/PD state machine with CTUnattached.SNK and CTAttached.SNK states. Signed-off-by: Sam Hurst <shurst@chromium.org> Change-Id: I847f1bcd2fc3ce41e66edd133a10c943d5e8c819 Reviewed-on: https://chromium-review.googlesource.com/1225250 Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com> Tested-by: Sam Hurst <shurst@google.com> Reviewed-by: Stefan Reinauer <reinauer@google.com>
Diffstat (limited to 'include')
-rw-r--r--include/config.h33
-rw-r--r--include/usb_emsg.h23
-rw-r--r--include/usb_pd.h63
-rw-r--r--include/usb_pd_tcpm.h5
-rw-r--r--include/usb_pe_ctvpd_sm.h239
-rw-r--r--include/usb_pe_sm.h79
-rw-r--r--include/usb_prl_sm.h213
-rw-r--r--include/usb_sm.h67
-rw-r--r--include/usb_tc_ctvpd_sm.h2039
-rw-r--r--include/usb_tc_sm.h91
-rw-r--r--include/usb_tc_vpd_sm.h486
11 files changed, 3335 insertions, 3 deletions
diff --git a/include/config.h b/include/config.h
index 8c03da112b..89ade13c9c 100644
--- a/include/config.h
+++ b/include/config.h
@@ -779,6 +779,33 @@
*/
#undef CONFIG_CHARGER_INPUT_CURRENT
+/* Define to use Power Delivery State Machine Framework */
+#undef CONFIG_USB_SM_FRAMEWORK
+
+/*
+ * This is the maximum number of levels in the hierarchical
+ * state machine framework. Set to 0 for a flat state machine.
+ */
+#define CONFIG_SM_NESTING_NUM 3
+
+/*
+ * Define to enable Type-C State Machine. Must be enabled
+ * with CONFIG_USB_SM_FRAMEWORK
+ */
+#define CONFIG_USB_TYPEC_SM
+
+/*
+ * Define to enable Protocol Layer State Machine. Must be enabled
+ * with CONFIG_USB_SM_FRAMEWORK and CONFIG_USB_TYPEC_SM
+ */
+#define CONFIG_USB_PRL_SM
+
+/*
+ * Define to enable Policy Engine State Machine. Must be enabled
+ * with CONFIG_USB_SM_FRAMEWORK and CONFIG_USB_TYPEC_SM
+ */
+#define CONFIG_USB_PE_SM
+
/*
* Board specific maximum input current limit, in mA.
*/
@@ -3500,6 +3527,12 @@
/* Use DAC as reference for comparator at 850mV. */
#undef CONFIG_PD_USE_DAC_AS_REF
+/* Type-C VCONN Powered Device */
+#undef CONFIG_USB_TYPEC_VPD
+
+/* Type-C Charge Through VCONN Powered Device */
+#undef CONFIG_USB_TYPEC_CTVPD
+
/* USB Product ID. */
#undef CONFIG_USB_PID
diff --git a/include/usb_emsg.h b/include/usb_emsg.h
new file mode 100644
index 0000000000..ffbaa93a0e
--- /dev/null
+++ b/include/usb_emsg.h
@@ -0,0 +1,23 @@
+/* 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.
+ */
+
+/* USB Extended message buffer */
+
+#ifndef __CROS_EC_USB_EBUF_H
+#define __CROS_EC_USB_EBUF_H
+
+#define EXTENDED_BUFFER_SIZE 260
+#define BUFFER_SIZE 28
+
+struct extended_msg {
+ uint32_t header;
+ uint32_t len;
+ uint8_t buf[EXTENDED_BUFFER_SIZE];
+};
+
+/* Defined in usb_prl_sm.c */
+extern struct extended_msg emsg[CONFIG_USB_PD_PORT_COUNT];
+
+#endif /* __CROS_EC_USB_EBUF_H */
diff --git a/include/usb_pd.h b/include/usb_pd.h
index 89202a7ad3..b3888dae7f 100644
--- a/include/usb_pd.h
+++ b/include/usb_pd.h
@@ -49,6 +49,7 @@ enum pd_rx_errors {
#define PD_EVENT_DEVICE_ACCESSED (1<<7)
#define PD_EVENT_POWER_STATE_CHANGE (1<<8) /* Chipset power state changed */
#define PD_EVENT_SEND_HARD_RESET (1<<9) /* Issue a Hard Reset. */
+#define PD_EVENT_SM (1<<10) /* PD State machine event */
/* Ensure TCPC is out of low power mode before handling these events. */
#define PD_EXIT_LOW_POWER_EVENT_MASK \
@@ -168,6 +169,7 @@ enum pd_rx_errors {
#define PD_T_DRP_SNK (40*MSEC) /* toggle time for sink DRP */
#define PD_T_DRP_SRC (30*MSEC) /* toggle time for source DRP */
#define PD_T_DEBOUNCE (15*MSEC) /* between 10ms and 20ms */
+#define PD_T_TRY_CC_DEBOUNCE (15*MSEC) /* between 10ms and 20ms */
#define PD_T_SINK_ADJ (55*MSEC) /* between PD_T_DEBOUNCE and 60ms */
#define PD_T_SRC_RECOVER (760*MSEC) /* between 660ms and 1000ms */
#define PD_T_SRC_RECOVER_MAX (1000*MSEC) /* 1000ms */
@@ -181,6 +183,10 @@ enum pd_rx_errors {
#define PD_T_TRY_TIMEOUT (550*MSEC) /* between 550ms and 1100ms */
#define PD_T_TRY_WAIT (600*MSEC) /* Max time for TryWait.SNK state */
#define PD_T_SINK_REQUEST (100*MSEC) /* Wait 100ms before next request */
+#define PD_T_PD_DEBOUNCE (15*MSEC) /* between 10ms and 20ms */
+#define PD_T_CHUNK_SENDER_RESPONSE (25*MSEC) /* 25ms */
+#define PD_T_CHUNK_SENDER_REQUEST (25*MSEC) /* 25ms */
+#define PD_T_SWAP_SOURCE_START (25*MSEC) /* Min of 20ms */
/* number of edges and time window to detect CC line is not idle */
#define PD_RX_TRANSITION_COUNT 3
@@ -196,6 +202,11 @@ enum pd_rx_errors {
#define PD_T_VDM_SNDR_RSP (30*MSEC) /* max of 30ms */
#define PD_T_VDM_WAIT_MODE_E (100*MSEC) /* enter/exit the same max */
+/* CTVPD Timers ( USB Type-C ECN Table 4-27 ) */
+#define PD_T_VPDDETACH (20*MSEC) /* max of 20*MSEC */
+#define PD_T_VPDCTDD (4*MSEC) /* max of 4ms */
+#define PD_T_VPDDISABLE (25*MSEC) /* min of 25ms */
+
/* function table for entered mode */
struct amode_fx {
int (*status)(int port, uint32_t *payload);
@@ -391,6 +402,7 @@ struct pd_policy {
#define IDH_PTYPE_PCABLE 3
#define IDH_PTYPE_ACABLE 4
#define IDH_PTYPE_AMA 5
+#define IDH_PTYPE_VPD 6
#define VDO_IDH(usbh, usbd, ptype, is_modal, vid) \
((usbh) << 31 | (usbd) << 30 | ((ptype) & 0x7) << 27 \
@@ -491,6 +503,34 @@ struct pd_policy {
#define AMA_USBSS_BBONLY 3
/*
+ * VPD VDO
+ * ---------
+ * <31:28> :: HW version
+ * <27:24> :: FW version
+ * <23:21> :: VDO version
+ * <20:17> :: SBZ
+ * <16:15> :: Maximum VBUS Voltage
+ * <14:13> :: SBZ
+ * <12:7> :: VBUS Impedance
+ * <6:1> :: Ground Impedance
+ * <0> :: Charge Through Support
+ */
+#define VDO_VPD(hw, fw, vbus, vbusz, gndz, cts) \
+ (((hw) & 0xf) << 28 | ((fw) & 0xf) << 24 \
+ | ((vbus) & 0x3) << 15 \
+ | ((vbusz) & 0x3f) << 7 \
+ | ((gndz) & 0x3f) << 1 | (cts))
+
+#define VPD_MAX_VBUS_20V 0
+#define VPD_MAX_VBUS_30V 1
+#define VPD_MAX_VBUS_40V 2
+#define VPD_MAX_VBUS_50V 3
+#define VPD_VBUS_IMP(mo) ((mo + 1) >> 1)
+#define VPD_GND_IMP(mo) (mo)
+#define VPD_CTS_SUPPORTED 1
+#define VPD_CTS_NOT_SUPPORTED 0
+
+/*
* SVDM Discover SVIDs request -> response
*
* Request is properly formatted VDM Header with discover SVIDs command.
@@ -787,6 +827,9 @@ enum pd_states {
#define PD_BBRMFLG_DATA_ROLE BIT(2)
#define PD_BBRMFLG_VCONN_ROLE BIT(3)
+/* Initial value for CC debounce variable */
+#define PD_CC_UNSET -1
+
enum pd_cc_states {
PD_CC_NONE,
@@ -912,14 +955,25 @@ enum pd_data_msg_type {
PD_DATA_VENDOR_DEF = 15,
};
+/* CC Polarity type */
+enum pd_cc_polarity_type {
+ POLARITY_CC1,
+ POLARITY_CC2
+};
+
/* Protocol revision */
-#define PD_REV10 0
-#define PD_REV20 1
-#define PD_REV30 2
+enum pd_rev_type {
+ PD_REV10,
+ PD_REV20,
+ PD_REV30
+};
/* Power role */
#define PD_ROLE_SINK 0
#define PD_ROLE_SOURCE 1
+/* Cable plug */
+#define PD_PLUG_DFP_UFP 0
+#define PD_PLUG_CABLE_VPD 1
/* Data role */
#define PD_ROLE_UFP 0
#define PD_ROLE_DFP 1
@@ -998,6 +1052,9 @@ enum pd_data_msg_type {
#define PD_EXT_HEADER_REQ_CHUNK(header) (((header) >> 10) & 1)
#define PD_EXT_HEADER_DATA_SIZE(header) ((header) & 0x1ff)
+/* Used to get extended header from the first 32-bit word of the message */
+#define GET_EXT_HEADER(msg) (msg & 0xffff)
+
/* K-codes for special symbols */
#define PD_SYNC1 0x18
#define PD_SYNC2 0x11
diff --git a/include/usb_pd_tcpm.h b/include/usb_pd_tcpm.h
index d46aef454d..bdd497b563 100644
--- a/include/usb_pd_tcpm.h
+++ b/include/usb_pd_tcpm.h
@@ -16,6 +16,9 @@
/* Time to wait for TCPC to complete transmit */
#define PD_T_TCPC_TX_TIMEOUT (100*MSEC)
+/* Number of valid Transmit Types */
+#define NUM_XMIT_TYPES (TCPC_TX_SOP_DEBUG_PRIME_PRIME + 1)
+
/* Detected resistor values of port partner */
enum tcpc_cc_voltage_status {
TYPEC_CC_VOLT_OPEN = 0,
@@ -32,6 +35,7 @@ enum tcpc_cc_pull {
TYPEC_CC_RP = 1,
TYPEC_CC_RD = 2,
TYPEC_CC_OPEN = 3,
+ TYPEC_CC_RA_RD = 4, /* Powered cable with Sink */
};
/* Pull-up values we apply as a SRC to advertise different current limits */
@@ -54,6 +58,7 @@ enum tcpm_transmit_type {
};
enum tcpc_transmit_complete {
+ TCPC_TX_UNSET = -1,
TCPC_TX_COMPLETE_SUCCESS = 0,
TCPC_TX_COMPLETE_DISCARDED = 1,
TCPC_TX_COMPLETE_FAILED = 2,
diff --git a/include/usb_pe_ctvpd_sm.h b/include/usb_pe_ctvpd_sm.h
new file mode 100644
index 0000000000..e2b2cde8c4
--- /dev/null
+++ b/include/usb_pe_ctvpd_sm.h
@@ -0,0 +1,239 @@
+/* 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_tc_sm.h"
+#include "usb_emsg.h"
+#include "usb_sm.h"
+
+/* USB Policy Engine Charge-Through VCONN Powered Device module */
+
+#ifndef __CROS_EC_USB_PE_CTVPD_H
+#define __CROS_EC_USB_PE_CTVPD_H
+
+/* Policy Engine Flags */
+#define PE_FLAGS_MSG_RECEIVED (1 << 0)
+
+enum l_state {
+ PE_INIT,
+ PE_RUN,
+ PE_PAUSED
+};
+
+static enum l_state local_state = PE_INIT;
+
+/*
+ * PE_OBJ is a convenience macro to access struct sm_obj, which
+ * must be the first member of struct policy_engine.
+ */
+#define PE_OBJ(port) (SM_OBJ(pe[port]))
+
+/**
+ * This is the PE Port object that contains information needed to
+ * implement a VCONN and Charge-Through VCONN Powered Device.
+ */
+static struct policy_engine {
+ /*
+ * struct sm_obj must be first. This is the state machine
+ * object that keeps track of the current and last state
+ * of the state machine.
+ */
+ struct sm_obj obj;
+ /* port flags, see PE_FLAGS_* */
+ uint32_t flags;
+} pe[CONFIG_USB_PD_PORT_COUNT];
+
+static unsigned int pe_request(int port, enum signal sig);
+static unsigned int pe_request_entry(int port);
+static unsigned int pe_request_run(int port);
+
+static unsigned int do_nothing_exit(int port);
+static unsigned int get_super_state(int port);
+
+static const state_sig pe_request_sig[] = {
+ pe_request_entry,
+ pe_request_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+void pe_init(int port)
+{
+ pe[port].flags = 0;
+ init_state(port, PE_OBJ(port), pe_request);
+}
+
+void policy_engine(int port, int evt, int en)
+{
+ switch (local_state) {
+ case PE_INIT:
+ pe_init(port);
+ local_state = PE_RUN;
+ /* fall through */
+ case PE_RUN:
+ if (!en) {
+ local_state = PE_PAUSED;
+ break;
+ }
+
+ exe_state(port, PE_OBJ(port), RUN_SIG);
+ break;
+ case PE_PAUSED:
+ if (en)
+ local_state = PE_INIT;
+ break;
+ }
+}
+
+void pe_pass_up_message(int port)
+{
+ pe[port].flags |= PE_FLAGS_MSG_RECEIVED;
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0);
+}
+
+void pe_hard_reset_sent(int port)
+{
+ /* Do nothing */
+}
+
+void pe_got_hard_reset(int port)
+{
+ /* Do nothing */
+}
+
+void pe_report_error(int port, enum pe_error e)
+{
+ /* Do nothing */
+}
+
+void pe_got_soft_reset(int port)
+{
+ /* Do nothing */
+}
+
+void pe_message_sent(int port)
+{
+ /* Do nothing */
+}
+
+static unsigned int pe_request(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*pe_request_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int pe_request_entry(int port)
+{
+ return 0;
+}
+
+static unsigned int pe_request_run(int port)
+{
+ uint32_t *payload = (uint32_t *)emsg[port].buf;
+ uint32_t header = emsg[port].header;
+ uint32_t vdo = payload[0];
+
+ 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 0;
+
+ if (PD_HEADER_CNT(header) == 0)
+ return 0;
+
+ if (!PD_VDO_SVDM(vdo))
+ return 0;
+
+ if (PD_VDO_CMD(vdo) != CMD_DISCOVER_IDENT)
+ return 0;
+
+#ifdef CONFIG_USB_TYPEC_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,
+ VPD_VBUS_IMP(VPD_VBUS_IMPEDANCE),
+ VPD_GND_IMP(VPD_GND_IMPEDANCE),
+#ifdef CONFIG_USB_TYPEC_CTVPD
+ VPD_CTS_SUPPORTED
+#else
+ VPD_CTS_NOT_SUPPORTED
+#endif
+ );
+
+ /* 20 bytes, 5 data objects */
+ emsg[port].len = 20;
+
+ /* Set to highest revision supported by both ports. */
+ prl_set_rev(port, (PD_HEADER_REV(header) > PD_REV30) ?
+ PD_REV30 : PD_HEADER_REV(header));
+
+ /* Send the ACK */
+ prl_send_data_msg(port, TCPC_TX_SOP_PRIME,
+ PD_DATA_VENDOR_DEF);
+ }
+
+ return 0;
+}
+
+static unsigned int do_nothing_exit(int port)
+{
+ return 0;
+}
+
+static unsigned int get_super_state(int port)
+{
+ return RUN_SUPER;
+}
+
+#endif /* __CROS_EC_USB_PE_CTVPD_H */
diff --git a/include/usb_pe_sm.h b/include/usb_pe_sm.h
new file mode 100644
index 0000000000..a8fd59b08c
--- /dev/null
+++ b/include/usb_pe_sm.h
@@ -0,0 +1,79 @@
+/* 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.
+ */
+
+/* USB Policy Engine module */
+
+#ifndef __CROS_EC_USB_PE_H
+#define __CROS_EC_USB_PE_H
+
+enum pe_error {
+ ERR_RCH_CHUNKED,
+ ERR_RCH_MSG_REC,
+ ERR_TCH_CHUNKED,
+ ERR_TCH_XMIT,
+};
+
+/**
+ * Initialize the Policy Engine State Machine
+ *
+ * @param port USB-C port number
+ */
+void pe_init(int port);
+
+/**
+ * Runs the Policy Engine State Machine
+ *
+ * @param port USB-C port number
+ * @param evt system event, ie: PD_EVENT_RX
+ * @param en 0 to disable the machine, 1 to enable the machine
+ */
+void policy_engine(int port, int evt, int en);
+
+/**
+ * Informs the Policy Engine that a message was successfully sent
+ *
+ * @param port USB-C port number
+ */
+void pe_message_sent(int port);
+
+/**
+ * Informs the Policy Engine of an error.
+ *
+ * @param port USB-C port number
+ * @parm e error
+ */
+void pe_report_error(int port, enum pe_error e);
+
+/**
+ * Informs the Policy Engine that a message has been received
+ *
+ * @param port USB-C port number
+ * @parm e error
+ */
+void pe_pass_up_message(int port);
+
+/**
+ * Informs the Policy Engine that a hard reset was received.
+ *
+ * @param port USB-C port number
+ */
+void pe_got_hard_reset(int port);
+
+/**
+ * Informs the Policy Engine that a soft reset was received.
+ *
+ * @param port USB-C port number
+ */
+void pe_got_soft_reset(int port);
+
+/**
+ * Informs the Policy Engine that a hard reset was sent.
+ *
+ * @param port USB-C port number
+ */
+void pe_hard_reset_sent(int port);
+
+#endif /* __CROS_EC_USB_PE_H */
+
diff --git a/include/usb_prl_sm.h b/include/usb_prl_sm.h
new file mode 100644
index 0000000000..c02725baad
--- /dev/null
+++ b/include/usb_prl_sm.h
@@ -0,0 +1,213 @@
+/* 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.
+ */
+
+/* USB Protocol Layer module */
+
+#ifndef __CROS_EC_USB_PRL_H
+#define __CROS_EC_USB_PRL_H
+#include "common.h"
+#include "usb_pd.h"
+#include "usb_pd_tcpm.h"
+
+enum prl_tx_state_id {
+ PRL_TX_PHY_LAYER_RESET,
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST,
+ PRL_TX_LAYER_RESET_FOR_TRANSMIT,
+ PRL_TX_CONSTRUCT_MESSAGE,
+ PRL_TX_WAIT_FOR_PHY_RESPONSE,
+ PRL_TX_MATCH_MESSAGE_ID,
+ PRL_TX_MESSAGE_SENT,
+ PRL_TX_CHECK_RETRY_COUNTER,
+ PRL_TX_TRANSMISSION_ERROR,
+ PRL_TX_DISCARD_MESSAGE,
+
+ PRL_TX_SRC_SINK_TX,
+ PRL_TX_SRC_SOURCE_TX,
+ PRL_TX_SRC_PENDING,
+
+ PRL_TX_SNK_START_OF_AMS,
+ PRL_TX_SNK_PENDING,
+};
+
+enum prl_hr_state_id {
+ PRL_HR_WAIT_FOR_REQUEST,
+ PRL_HR_RESET_LAYER,
+ PRL_HR_INDICATE_HARD_RESET,
+ PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE,
+ PRL_HR_PHY_HARD_RESET_REQUESTED,
+ PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE,
+ PRL_HR_PE_HARD_RESET_COMPLETE,
+};
+
+enum rch_state_id {
+ RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER,
+ RCH_PASS_UP_MESSAGE,
+ RCH_PROCESSING_EXTENDED_MESSAGE,
+ RCH_REQUESTING_CHUNK,
+ RCH_WAITING_CHUNK,
+ RCH_REPORT_ERROR,
+};
+
+enum tch_state_id {
+ TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE,
+ TCH_PASS_DOWN_MESSAGE,
+ TCH_WAIT_FOR_TRANSMISSION_COMPLETE,
+ TCH_MESSAGE_SENT,
+ TCH_PREPARE_TO_SEND_CHUNKED_MESSAGE,
+ TCH_CONSTRUCT_CHUNKED_MESSAGE,
+ TCH_SENDING_CHUNKED_MESSAGE,
+ TCH_WAIT_CHUNK_REQUEST,
+ TCH_MESSAGE_RECEIVED,
+ TCH_REPORT_ERROR,
+};
+
+/*
+ * Number of times the Protocol Layer will try to transmit a message
+ * before giving up and signaling an error
+ */
+#define N_RETRY_COUNT 2
+
+/**
+ * Initialize the Protocol Layer State Machine
+ *
+ * @param port USB-C port number
+ */
+void prl_init(int port);
+
+/**
+ * Resets the Protocol Layer State Machine
+ *
+ * @param port USB-C port number
+ */
+void prl_reset(int port);
+
+/**
+ * Get Chunked Rx State Machine state id
+ *
+ * @param port USB-C port number
+ * @return id
+ */
+enum rch_state_id get_rch_state_id(int port);
+
+/**
+ * Get Chunked Tx State Machine state id
+ *
+ * @param port USB-C port number
+ * @return id
+ */
+enum tch_state_id get_tch_state_id(int port);
+
+/**
+ * Get Message Transmission State Machine state id
+ *
+ * @param port USB-C port number
+ * @return id
+ */
+enum prl_tx_state_id get_prl_tx_state_id(int port);
+
+/**
+ * Get Hard Reset State Machine state id
+ *
+ * @param port USB-C port number
+ * @return id
+ */
+enum prl_hr_state_id get_prl_hr_state_id(int port);
+
+/**
+ * Returns the state of the PRL state machine
+ * @return SM_INIT for initializing
+ * SM_RUN for running
+ * SM_PAUSED for paused
+ */
+enum sm_local_state prl_get_local_state(int port);
+
+/**
+ * Runs the Protocol Layer State Machine
+ *
+ * @param port USB-C port number
+ * @param evt system event, ie: PD_EVENT_RX
+ * @param en 0 to disable the machine, 1 to enable the machine
+ */
+void protocol_layer(int port, int evt, int en);
+
+/**
+ * Set the PD revision
+ *
+ * @param port USB-C port number
+ * @param rev revision
+ */
+void prl_set_rev(int port, enum pd_rev_type rev);
+
+/**
+ * Get the PD revision
+ *
+ * @param port USB-C port number
+ * @return pd rev
+ */
+enum pd_rev_type prl_get_rev(int port);
+
+/**
+ * Sends a PD control message
+ *
+ * @param port USB-C port number
+ * @param type Transmit type
+ * @param msg Control message type
+ * @return 0 on EC_SUCCESS, else EC_ERROR_BUSY
+ */
+void prl_send_ctrl_msg(int port, enum tcpm_transmit_type type,
+ enum pd_ctrl_msg_type msg);
+
+/**
+ * Sends a PD data message
+ *
+ * @param port USB-C port number
+ * @param type Transmit type
+ * @param msg Data message type
+ * @return 0 on EC_SUCCESS, else EC_ERROR_BUSY
+ */
+void prl_send_data_msg(int port, enum tcpm_transmit_type type,
+ enum pd_data_msg_type msg);
+
+/**
+ * Sends a PD extended data message
+ *
+ * @param port USB-C port number
+ * @param type Transmit type
+ * @param msg Extended data message type
+ * @return 0 on EC_SUCCESS, else EC_ERROR_BUSY
+ */
+void prl_send_ext_data_msg(int port, enum tcpm_transmit_type type,
+ enum pd_ext_msg_type msg);
+
+/**
+ * Informs the Protocol Layer that a hard reset has completed
+ *
+ * @param port USB-C port number
+ */
+void prl_hard_reset_complete(int port);
+
+/**
+ * Policy Engine calls this function to execute a hard reset.
+ *
+ * @param port USB-C port number
+ */
+void prl_execute_hard_reset(int port);
+
+/**
+ * Informs the Protocol Layer to start an Atomic Message Sequence
+ *
+ * @param port USB-C port number
+ */
+void prl_start_ams(int port);
+
+/**
+ * Informs the Protocol Layer to end an Atomic Message Sequence
+ *
+ * @param port USB-C port number
+ */
+void prl_end_ams(int port);
+
+#endif /* __CROS_EC_USB_PRL_H */
+
diff --git a/include/usb_sm.h b/include/usb_sm.h
new file mode 100644
index 0000000000..15de5f9ef4
--- /dev/null
+++ b/include/usb_sm.h
@@ -0,0 +1,67 @@
+/* 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.
+ */
+
+/* USB State Machine Framework */
+
+#ifndef __CROS_EC_USB_SM_H
+#define __CROS_EC_USB_SM_H
+
+#define SM_OBJ(smo) ((struct sm_obj *)&smo)
+#define SUPER(r, sig, s) ((((r) == 0) || ((sig) == ENTRY_SIG) || \
+ ((sig) == EXIT_SIG)) ? 0 : ((uintptr_t)(s)))
+#define RUN_SUPER 1
+
+/* Local state machine states */
+enum sm_local_state {
+ SM_INIT,
+ SM_RUN,
+ SM_PAUSED
+};
+
+/* State Machine signals */
+enum signal {
+ ENTRY_SIG = 0,
+ RUN_SIG,
+ EXIT_SIG,
+ SUPER_SIG,
+};
+
+typedef unsigned int (*state_sig)(int port);
+typedef unsigned int (*sm_state)(int port, enum signal sig);
+
+struct sm_obj {
+ sm_state task_state;
+ sm_state last_state;
+};
+
+/**
+ * Initialize a State Machine
+ *
+ * @param port USB-C port number
+ * @param obj State machine object
+ * @param target Initial state of state machine
+ */
+void init_state(int port, struct sm_obj *obj, sm_state target);
+
+/**
+ * Changes a state machines state
+ *
+ * @param port USB-C port number
+ * @param obj State machine object
+ * @param target State to transition to
+ * @return 0
+ */
+int set_state(int port, struct sm_obj *obj, sm_state target);
+
+/**
+ * Executes a state machine
+ *
+ * @param port USB-C port number
+ * @param obj State machine object
+ * @param sig State machine signal
+ */
+void exe_state(int port, struct sm_obj *obj, enum signal sig);
+
+#endif /* __CROS_EC_USB_SM_H */
diff --git a/include/usb_tc_ctvpd_sm.h b/include/usb_tc_ctvpd_sm.h
new file mode 100644
index 0000000000..3b47d35ffa
--- /dev/null
+++ b/include/usb_tc_ctvpd_sm.h
@@ -0,0 +1,2039 @@
+/* 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 "vpd_api.h"
+
+/* USB Type-C CTVPD module */
+
+#ifndef __CROS_EC_USB_TC_VPD_H
+#define __CROS_EC_USB_TC_VPD_H
+
+/* Type-C Layer Flags */
+#define TC_FLAGS_VCONN_ON (1 << 0)
+
+
+#undef PD_DEFAULT_STATE
+/* Port default state at startup */
+#define PD_DEFAULT_STATE(port) tc_state_unattached_snk
+
+#define TC_OBJ(port) (SM_OBJ(tc[port]))
+#define TC_TEST_OBJ(port) (SM_OBJ(tc[(port)].obj))
+
+#define SUPPORT_TIMER_RESET_INIT 0
+#define SUPPORT_TIMER_RESET_REQUEST 1
+#define SUPPORT_TIMER_RESET_COMPLETE 2
+
+static struct type_c {
+ /* struct sm_obj must be first */
+ struct sm_obj obj;
+ /* state id */
+ enum typec_state_id state_id;
+ /* current port power role (VPD, SOURCE or SINK) */
+ uint8_t power_role;
+ /* current port data role (DFP or UFP) */
+ uint8_t data_role;
+ /* enable power delivery state machines */
+ uint8_t pd_enable;
+ /* event timeout */
+ uint64_t evt_timeout;
+ /* state machine event */
+ int evt;
+ /* 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_COUNT];
+
+/* Type-C states */
+static unsigned int tc_state_disabled(int port, enum signal sig);
+static unsigned int tc_state_disabled_entry(int port);
+static unsigned int tc_state_disabled_run(int port);
+static unsigned int tc_state_disabled_exit(int port);
+
+static unsigned int tc_state_error_recovery(int port, enum signal sig);
+static unsigned int tc_state_error_recovery_entry(int port);
+static unsigned int tc_state_error_recovery_run(int port);
+
+static unsigned int tc_state_unattached_snk(int port, enum signal sig);
+static unsigned int tc_state_unattached_snk_entry(int port);
+static unsigned int tc_state_unattached_snk_run(int port);
+
+static unsigned int tc_state_attach_wait_snk(int port, enum signal sig);
+static unsigned int tc_state_attach_wait_snk_entry(int port);
+static unsigned int tc_state_attach_wait_snk_run(int port);
+
+static unsigned int tc_state_attached_snk(int port, enum signal sig);
+static unsigned int tc_state_attached_snk_entry(int port);
+static unsigned int tc_state_attached_snk_run(int port);
+static unsigned int tc_state_attached_snk_exit(int port);
+
+static unsigned int tc_state_try_snk(int port, enum signal sig);
+static unsigned int tc_state_try_snk_entry(int port);
+static unsigned int tc_state_try_snk_run(int port);
+
+static unsigned int tc_state_unattached_src(int port, enum signal sig);
+static unsigned int tc_state_unattached_src_entry(int port);
+static unsigned int tc_state_unattached_src_run(int port);
+
+static unsigned int tc_state_attach_wait_src(int port, enum signal sig);
+static unsigned int tc_state_attach_wait_src_entry(int port);
+static unsigned int tc_state_attach_wait_src_run(int port);
+
+static unsigned int tc_state_try_wait_src(int port, enum signal sig);
+static unsigned int tc_state_try_wait_src_entry(int port);
+static unsigned int tc_state_try_wait_src_run(int port);
+
+static unsigned int tc_state_attached_src(int port, enum signal sig);
+static unsigned int tc_state_attached_src_entry(int port);
+static unsigned int tc_state_attached_src_run(int port);
+
+static unsigned int tc_state_ct_try_snk(int port, enum signal sig);
+static unsigned int tc_state_ct_try_snk_entry(int port);
+static unsigned int tc_state_ct_try_snk_run(int port);
+static unsigned int tc_state_ct_try_snk_exit(int port);
+
+static unsigned int
+ tc_state_ct_attach_wait_unsupported(int port, enum signal sig);
+static unsigned int tc_state_ct_attach_wait_unsupported_entry(int port);
+static unsigned int tc_state_ct_attach_wait_unsupported_run(int port);
+static unsigned int tc_state_ct_attach_wait_unsupported_exit(int port);
+
+static unsigned int tc_state_ct_attached_unsupported(int port, enum signal sig);
+static unsigned int tc_state_ct_attached_unsupported_entry(int port);
+static unsigned int tc_state_ct_attached_unsupported_run(int port);
+static unsigned int tc_state_ct_attached_unsupported_exit(int port);
+
+static unsigned int
+ tc_state_ct_unattached_unsupported(int port, enum signal sig);
+static unsigned int tc_state_ct_unattached_unsupported_entry(int port);
+static unsigned int tc_state_ct_unattached_unsupported_run(int port);
+static unsigned int tc_state_ct_unattached_unsupported_exit(int port);
+
+static unsigned int tc_state_ct_unattached_vpd(int port, enum signal sig);
+static unsigned int tc_state_ct_unattached_vpd_entry(int port);
+static unsigned int tc_state_ct_unattached_vpd_run(int port);
+static unsigned int tc_state_ct_unattached_vpd_exit(int port);
+
+static unsigned int tc_state_ct_disabled_vpd(int port, enum signal sig);
+static unsigned int tc_state_ct_disabled_vpd_entry(int port);
+static unsigned int tc_state_ct_disabled_vpd_run(int port);
+
+static unsigned int tc_state_ct_attached_vpd(int port, enum signal sig);
+static unsigned int tc_state_ct_attached_vpd_entry(int port);
+static unsigned int tc_state_ct_attached_vpd_run(int port);
+
+static unsigned int tc_state_ct_attach_wait_vpd(int port, enum signal sig);
+static unsigned int tc_state_ct_attach_wait_vpd_entry(int port);
+static unsigned int tc_state_ct_attach_wait_vpd_run(int port);
+static unsigned int tc_state_ct_attach_wait_vpd_exit(int port);
+
+
+/* Super States */
+static unsigned int tc_state_host_rard_ct_rd(int port, enum signal sig);
+static unsigned int tc_state_host_rard_ct_rd_entry(int port);
+static unsigned int tc_state_host_rard_ct_rd_run(int port);
+
+static unsigned int tc_state_host_open_ct_open(int port, enum signal sig);
+static unsigned int tc_state_host_open_ct_open_entry(int port);
+static unsigned int tc_state_host_open_ct_open_run(int port);
+
+static unsigned int tc_state_vbus_cc_iso(int port, enum signal sig);
+static unsigned int tc_state_vbus_cc_iso_entry(int port);
+static unsigned int tc_state_vbus_cc_iso_run(int port);
+
+static unsigned int tc_state_host_rp3_ct_rd(int port, enum signal sig);
+static unsigned int tc_state_host_rp3_ct_rd_entry(int port);
+static unsigned int tc_state_host_rp3_ct_rd_run(int port);
+
+static unsigned int tc_state_host_rp3_ct_rpu(int port, enum signal sig);
+static unsigned int tc_state_host_rp3_ct_rpu_entry(int port);
+static unsigned int tc_state_host_rp3_ct_rpu_run(int port);
+
+static unsigned int tc_state_host_rpu_ct_rd(int port, enum signal sig);
+static unsigned int tc_state_host_rpu_ct_rd_entry(int port);
+static unsigned int tc_state_host_rpu_ct_rd_run(int port);
+
+static unsigned int do_nothing_exit(int port);
+static unsigned int get_super_state(int port);
+
+
+static const state_sig tc_state_disabled_sig[] = {
+ tc_state_disabled_entry,
+ tc_state_disabled_run,
+ tc_state_disabled_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_error_recovery_sig[] = {
+ tc_state_error_recovery_entry,
+ tc_state_error_recovery_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_unattached_snk_sig[] = {
+ tc_state_unattached_snk_entry,
+ tc_state_unattached_snk_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_attach_wait_snk_sig[] = {
+ tc_state_attach_wait_snk_entry,
+ tc_state_attach_wait_snk_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_attached_snk_sig[] = {
+ tc_state_attached_snk_entry,
+ tc_state_attached_snk_run,
+ tc_state_attached_snk_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_try_snk_sig[] = {
+ tc_state_try_snk_entry,
+ tc_state_try_snk_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_unattached_src_sig[] = {
+ tc_state_unattached_src_entry,
+ tc_state_unattached_src_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_attach_wait_src_sig[] = {
+ tc_state_attach_wait_src_entry,
+ tc_state_attach_wait_src_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_try_wait_src_sig[] = {
+ tc_state_try_wait_src_entry,
+ tc_state_try_wait_src_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_attached_src_sig[] = {
+ tc_state_attached_src_entry,
+ tc_state_attached_src_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_try_snk_sig[] = {
+ tc_state_ct_try_snk_entry,
+ tc_state_ct_try_snk_run,
+ tc_state_ct_try_snk_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_attach_wait_unsupported_sig[] = {
+ tc_state_ct_attach_wait_unsupported_entry,
+ tc_state_ct_attach_wait_unsupported_run,
+ tc_state_ct_attach_wait_unsupported_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_attached_unsupported_sig[] = {
+ tc_state_ct_attached_unsupported_entry,
+ tc_state_ct_attached_unsupported_run,
+ tc_state_ct_attached_unsupported_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_unattached_unsupported_sig[] = {
+ tc_state_ct_unattached_unsupported_entry,
+ tc_state_ct_unattached_unsupported_run,
+ tc_state_ct_unattached_unsupported_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_unattached_vpd_sig[] = {
+ tc_state_ct_unattached_vpd_entry,
+ tc_state_ct_unattached_vpd_run,
+ tc_state_ct_unattached_vpd_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_disabled_vpd_sig[] = {
+ tc_state_ct_disabled_vpd_entry,
+ tc_state_ct_disabled_vpd_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_attached_vpd_sig[] = {
+ tc_state_ct_attached_vpd_entry,
+ tc_state_ct_attached_vpd_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_attach_wait_vpd_sig[] = {
+ tc_state_ct_attach_wait_vpd_entry,
+ tc_state_ct_attach_wait_vpd_run,
+ tc_state_ct_attach_wait_vpd_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_rard_ct_rd_sig[] = {
+ tc_state_host_rard_ct_rd_entry,
+ tc_state_host_rard_ct_rd_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_open_ct_open_sig[] = {
+ tc_state_host_open_ct_open_entry,
+ tc_state_host_open_ct_open_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_vbus_cc_iso_sig[] = {
+ tc_state_vbus_cc_iso_entry,
+ tc_state_vbus_cc_iso_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_rp3_ct_rd_sig[] = {
+ tc_state_host_rp3_ct_rd_entry,
+ tc_state_host_rp3_ct_rd_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_rp3_ct_rpu_sig[] = {
+ tc_state_host_rp3_ct_rpu_entry,
+ tc_state_host_rp3_ct_rpu_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_rpu_ct_rd_sig[] = {
+ tc_state_host_rpu_ct_rd_entry,
+ tc_state_host_rpu_ct_rd_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+void tc_reset_support_timer(int port)
+{
+ tc[port].support_timer_reset |= SUPPORT_TIMER_RESET_REQUEST;
+}
+
+static void tc_state_init(int port)
+{
+ int res = 0;
+ sm_state this_state;
+
+ res = tc_restart_tcpc(port);
+
+ CPRINTS("TCPC p%d init %s", port, res ? "failed" : "ready");
+ this_state = res ? tc_state_disabled : PD_DEFAULT_STATE(port);
+
+ init_state(port, TC_OBJ(port), this_state);
+
+ /* Disable pd state machines */
+ tc[port].pd_enable = 0;
+ tc[port].evt_timeout = 10*MSEC;
+ tc[port].power_role = PD_PLUG_CABLE_VPD;
+ tc[port].data_role = 0; /* Reserved for VPD */
+ tc[port].billboard_presented = 0;
+ tc[port].flags = 0;
+}
+
+static void tc_event_check(int port, int evt)
+{
+ /* Do Nothing */
+}
+
+/**
+ * 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 unsigned int tc_state_disabled(int port, enum signal sig)
+{
+ int ret = 0;
+
+ ret = (*tc_state_disabled_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_open_ct_open);
+}
+
+static unsigned int tc_state_disabled_entry(int port)
+{
+ tc[port].state_id = DISABLED;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+ return 0;
+}
+
+static unsigned int tc_state_disabled_run(int port)
+{
+ task_wait_event(-1);
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_disabled_exit(int port)
+{
+#ifndef CONFIG_USB_PD_TCPC
+ if (tc_restart_tcpc(port) != 0) {
+ CPRINTS("TCPC p%d restart failed!", port);
+ return 0;
+ }
+#endif
+ CPRINTS("TCPC p%d resumed!", port);
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+
+ return 0;
+}
+
+/**
+ * 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 unsigned int tc_state_error_recovery(int port, enum signal sig)
+{
+ int ret = 0;
+
+ ret = (*tc_state_error_recovery_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_open_ct_open);
+}
+
+static unsigned int tc_state_error_recovery_entry(int port)
+{
+ tc[port].state_id = ERROR_RECOVERY;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+ /* Use cc_debounce state variable for error recovery timeout */
+ tc[port].cc_debounce = get_time().val + PD_T_ERROR_RECOVERY;
+ return 0;
+}
+
+static unsigned int tc_state_error_recovery_run(int port)
+{
+ if (get_time().val > tc[port].cc_debounce) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/**
+ * 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 unsigned int tc_state_unattached_snk(int port, enum signal sig)
+{
+ int ret = 0;
+
+ ret = (*tc_state_unattached_snk_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rard_ct_rd);
+}
+
+static unsigned int tc_state_unattached_snk_entry(int port)
+{
+ tc[port].state_id = UNATTACHED_SNK;
+ if (tc[port].obj.last_state != tc_state_unattached_src)
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ tc[port].flags &= ~TC_FLAGS_VCONN_ON;
+ tc[port].cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_unattached_snk_run(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(port, TC_OBJ(port), tc_state_attach_wait_snk);
+ return 0;
+ }
+
+ /* 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 0;
+
+ /*
+ * 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(port, TC_OBJ(port), tc_state_unattached_src);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/**
+ * 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 unsigned int tc_state_attach_wait_snk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_attach_wait_snk_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rard_ct_rd);
+}
+
+static unsigned int tc_state_attach_wait_snk_entry(int port)
+{
+ tc[port].state_id = ATTACH_WAIT_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+ tc[port].host_cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_attach_wait_snk_run(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 0;
+ }
+
+ /* Wait for Host CC debounce */
+ if (get_time().val < tc[port].host_cc_debounce)
+ return 0;
+
+ /*
+ * 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(port, TC_OBJ(port), tc_state_attached_snk);
+ else if (tc[port].host_cc_state == PD_CC_NONE)
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+
+ return 0;
+}
+
+/**
+ * Attached.SNK
+ */
+static unsigned int tc_state_attached_snk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_attached_snk_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tc_state_attached_snk_entry(int port)
+{
+ tc[port].state_id = ATTACHED_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ 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;
+
+ /* Sample host CC every 2ms */
+ tc_set_timeout(port, 2*MSEC);
+
+ return 0;
+}
+
+static unsigned int tc_state_attached_snk_run(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(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ /*
+ * 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 0;
+ }
+
+ /* Wait for Host CC debounce */
+ if (get_time().val < tc[port].host_cc_debounce)
+ return 0;
+
+ 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(port, TC_OBJ(port),
+ tc_state_ct_unattached_vpd);
+ return 0;
+ }
+ }
+
+ /* 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);
+ }
+
+ return 0;
+}
+
+static unsigned int tc_state_attached_snk_exit(int port)
+{
+ /* Reset timeout value to 10ms */
+ tc_set_timeout(port, 10*MSEC);
+ tc[port].billboard_presented = 0;
+ vpd_present_billboard(BB_NONE);
+
+ return 0;
+}
+
+/**
+ * Super State HOST_RA_CT_RD
+ */
+static unsigned int tc_state_host_rard_ct_rd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_rard_ct_rd_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_rard_ct_rd_entry(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);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_rard_ct_rd_run(int port)
+{
+ return RUN_SUPER;
+}
+
+/**
+ * Super State HOST_OPEN_CT_OPEN
+ */
+static unsigned int tc_state_host_open_ct_open(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_open_ct_open_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_open_ct_open_entry(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);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_open_ct_open_run(int port)
+{
+ return RUN_SUPER;
+}
+
+/**
+ * Super State VBUS_CC_ISO
+ */
+static unsigned int tc_state_vbus_cc_iso(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_vbus_cc_iso_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tc_state_vbus_cc_iso_entry(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);
+
+ return 0;
+}
+
+static unsigned int tc_state_vbus_cc_iso_run(int port)
+{
+ return 0;
+}
+
+/**
+ * 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 unsigned int tc_state_unattached_src(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_unattached_src_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rpu_ct_rd);
+}
+
+static unsigned int tc_state_unattached_src_entry(int port)
+{
+ tc[port].state_id = UNATTACHED_SRC;
+ if (tc[port].obj.last_state != tc_state_unattached_snk)
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* 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(port, TC_OBJ(port), tc_state_error_recovery);
+ return 0;
+ }
+
+ tc[port].next_role_swap = get_time().val + PD_T_DRP_SRC;
+
+ return 0;
+}
+
+static unsigned int tc_state_unattached_src_run(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(port, TC_OBJ(port), tc_state_attach_wait_src);
+ return 0;
+ }
+
+ /*
+ * 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(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/**
+ * 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 unsigned int tc_state_attach_wait_src(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_attach_wait_src_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rpu_ct_rd);
+}
+
+static unsigned int tc_state_attach_wait_src_entry(int port)
+{
+ tc[port].state_id = ATTACH_WAIT_SRC;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ tc[port].host_cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_attach_wait_src_run(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(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ /* 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 0;
+ }
+
+ /* Wait for Host CC debounce */
+ if (get_time().val < tc[port].cc_debounce)
+ return 0;
+
+ /*
+ * 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(port, TC_OBJ(port), tc_state_try_snk);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/**
+ * Attached.SRC
+ */
+static unsigned int tc_state_attached_src(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_attached_src_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tc_state_attached_src_entry(int port)
+{
+ tc[port].state_id = ATTACHED_SRC;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ 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);
+
+ return 0;
+}
+
+static unsigned int tc_state_attached_src_run(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(port, TC_OBJ(port), tc_state_unattached_snk);
+
+ return 0;
+}
+
+/**
+ * Super State HOST_RPU_CT_RD
+ */
+static unsigned int tc_state_host_rpu_ct_rd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_rpu_ct_rd_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_rpu_ct_rd_entry(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);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_rpu_ct_rd_run(int port)
+{
+ return RUN_SUPER;
+}
+
+/**
+ * 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 unsigned int tc_state_try_snk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_try_snk_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rard_ct_rd);
+}
+
+static unsigned int tc_state_try_snk_entry(int port)
+{
+ tc[port].state_id = TRY_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* 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(port, TC_OBJ(port), tc_state_error_recovery);
+ return 0;
+ }
+
+ 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;
+
+ return 0;
+}
+
+static unsigned int tc_state_try_snk_run(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 0;
+
+ /* 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 0;
+ }
+
+ /* Wait for Host CC debounce */
+ if (get_time().val < tc[port].cc_debounce)
+ return 0;
+
+ /*
+ * 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(port, TC_OBJ(port), tc_state_attached_snk);
+ else if (tc[port].host_cc_state == PD_CC_NONE)
+ set_state(port, TC_OBJ(port), tc_state_try_wait_src);
+
+ return 0;
+}
+
+/**
+ * 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 unsigned int tc_state_try_wait_src(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_try_wait_src_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rpu_ct_rd);
+}
+
+static unsigned int tc_state_try_wait_src_entry(int port)
+{
+ tc[port].state_id = TRY_WAIT_SRC;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ tc[port].host_cc_state = PD_CC_UNSET;
+ tc[port].next_role_swap = get_time().val + PD_T_DRP_TRY;
+
+ return 0;
+}
+
+static unsigned int tc_state_try_wait_src_run(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 0;
+ }
+
+ 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(port, TC_OBJ(port), tc_state_attached_src);
+ return 0;
+ }
+ }
+
+ 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(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+ }
+
+ return RUN_SUPER;
+}
+
+/**
+ * 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 unsigned int tc_state_ct_try_snk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_try_snk_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rp3_ct_rd);
+}
+
+static unsigned int tc_state_ct_try_snk_entry(int port)
+{
+ tc[port].state_id = CTTRY_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ tc[port].cc_state = PD_CC_UNSET;
+ tc[port].next_role_swap = get_time().val + PD_T_DRP_TRY;
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_try_snk_run(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 0;
+
+ /* 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(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ /* 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 0;
+ }
+
+ 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(port, TC_OBJ(port), tc_state_ct_attached_vpd);
+ return 0;
+ }
+ }
+
+ 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(port, TC_OBJ(port),
+ tc_state_ct_attached_unsupported);
+ return 0;
+ }
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_ct_try_snk_exit(int port)
+{
+ /* Disable PD */
+ tc[port].pd_enable = 0;
+
+ return 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 unsigned int
+ tc_state_ct_attach_wait_unsupported(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_attach_wait_unsupported_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rp3_ct_rpu);
+}
+
+static unsigned int tc_state_ct_attach_wait_unsupported_entry(int port)
+{
+ tc[port].state_id = CTATTACH_WAIT_UNSUPPORTED;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ tc[port].cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_attach_wait_unsupported_run(int port)
+{
+ int new_cc_state;
+ int cc1;
+ int cc2;
+
+ /* Check CT CC for connection */
+ vpd_ct_get_cc(&cc1, &cc2);
+
+ if (cc1 == TYPEC_CC_VOLT_RD || cc2 == TYPEC_CC_VOLT_RD)
+ new_cc_state = PD_CC_DFP_ATTACHED;
+ else if (cc1 == TYPEC_CC_VOLT_RA && cc2 == TYPEC_CC_VOLT_RA)
+ new_cc_state = PD_CC_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(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ /* 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 0;
+ }
+
+ /* Wait for CC debounce */
+ if (get_time().val < tc[port].cc_debounce)
+ return 0;
+
+ /*
+ * 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(port, TC_OBJ(port), tc_state_ct_unattached_vpd);
+ else /* PD_CC_DFP_ATTACHED or PD_CC_AUDIO_ACC */
+ set_state(port, TC_OBJ(port), tc_state_ct_try_snk);
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_attach_wait_unsupported_exit(int port)
+{
+ /* Disable PD */
+ tc[port].pd_enable = 0;
+
+ return 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 unsigned int tc_state_ct_attached_unsupported(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_attached_unsupported_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rp3_ct_rpu);
+}
+
+static unsigned int tc_state_ct_attached_unsupported_entry(int port)
+{
+ tc[port].state_id = CTATTACHED_UNSUPPORTED;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Present Billboard device */
+ vpd_present_billboard(BB_SNK);
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_attached_unsupported_run(int port)
+{
+ int cc1;
+ int cc2;
+
+ /* Check CT CC for connection */
+ vpd_ct_get_cc(&cc1, &cc2);
+
+ if (!vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ /*
+ * 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(port, TC_OBJ(port), tc_state_ct_unattached_vpd);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_ct_attached_unsupported_exit(int port)
+{
+ vpd_present_billboard(BB_NONE);
+
+ return 0;
+}
+
+/**
+ * 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 unsigned int
+ tc_state_ct_unattached_unsupported(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_unattached_unsupported_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rp3_ct_rpu);
+}
+
+static unsigned int tc_state_ct_unattached_unsupported_entry(int port)
+{
+ tc[port].state_id = CTUNATTACHED_UNSUPPORTED;
+ if (tc[port].obj.last_state != tc_state_ct_unattached_vpd)
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ tc[port].next_role_swap = get_time().val + PD_T_DRP_SRC;
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_unattached_unsupported_run(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 ((cc1 == TYPEC_CC_VOLT_RD || cc2 == TYPEC_CC_VOLT_RD) ||
+ (cc1 == TYPEC_CC_VOLT_RA &&
+ cc2 == TYPEC_CC_VOLT_RA)) {
+ set_state(port, TC_OBJ(port),
+ tc_state_ct_attach_wait_unsupported);
+ return 0;
+ }
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * Unattached.SNK if VCONN falls below vVCONNDisconnect.
+ */
+ if (!vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ /*
+ * 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(port, TC_OBJ(port), tc_state_ct_unattached_vpd);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_ct_unattached_unsupported_exit(int port)
+{
+ /* Disable PD */
+ tc[port].pd_enable = 0;
+
+ return 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 unsigned int tc_state_ct_unattached_vpd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_unattached_vpd_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rp3_ct_rd);
+}
+
+static unsigned int tc_state_ct_unattached_vpd_entry(int port)
+{
+ tc[port].state_id = CTUNATTACHED_VPD;
+ if (tc[port].obj.last_state != tc_state_ct_unattached_unsupported)
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ tc[port].cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_unattached_vpd_run(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(port, TC_OBJ(port),
+ tc_state_ct_attach_wait_vpd);
+ return 0;
+ }
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * Unattached.SNK if VCONN falls below vVCONNDisconnect.
+ */
+ if (!vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port),
+ tc_state_unattached_snk);
+ return 0;
+ }
+
+ /* 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 0;
+ }
+
+ if (get_time().val < tc[port].cc_debounce)
+ return 0;
+
+ /*
+ * 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(port, TC_OBJ(port),
+ tc_state_ct_unattached_unsupported);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_ct_unattached_vpd_exit(int port)
+{
+ /* Disable PD */
+ tc[port].pd_enable = 0;
+
+ return 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 unsigned int tc_state_ct_disabled_vpd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_disabled_vpd_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_open_ct_open);
+}
+
+static unsigned int tc_state_ct_disabled_vpd_entry(int port)
+{
+ tc[port].state_id = CTDISABLED_VPD;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Get power from VBUS */
+ vpd_vconn_pwr_sel_odl(PWR_VBUS);
+
+ tc[port].next_role_swap = get_time().val + PD_T_VPDDISABLE;
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_disabled_vpd_run(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(port, TC_OBJ(port), tc_state_unattached_snk);
+
+ return 0;
+}
+
+/**
+ * CTAttached.VPD
+ */
+static unsigned int tc_state_ct_attached_vpd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_attached_vpd_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tc_state_ct_attached_vpd_entry(int port)
+{
+ int cc1;
+ int cc2;
+
+ tc[port].state_id = CTATTACHED_VPD;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* 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;
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_attached_vpd_run(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(port, TC_OBJ(port), tc_state_ct_disabled_vpd);
+ return 0;
+ }
+
+ /* 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 0;
+ }
+
+ if (get_time().val < tc[port].pd_debounce)
+ return 0;
+
+ /*
+ * 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(port, TC_OBJ(port), tc_state_ct_unattached_vpd);
+
+ return 0;
+}
+
+/**
+ * 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 unsigned int tc_state_ct_attach_wait_vpd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_attach_wait_vpd_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rp3_ct_rd);
+}
+
+static unsigned int tc_state_ct_attach_wait_vpd_entry(int port)
+{
+ tc[port].state_id = CTATTACH_WAIT_VPD;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ tc[port].cc_state = PD_CC_UNSET;
+
+ /* Sample CCs every 2ms */
+ tc_set_timeout(port, 2 * MSEC);
+ return 0;
+}
+
+static unsigned int tc_state_ct_attach_wait_vpd_run(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(port, TC_OBJ(port), tc_state_ct_disabled_vpd);
+ return 0;
+ }
+
+ /* 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 0;
+ }
+
+ 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(port, TC_OBJ(port),
+ tc_state_ct_unattached_vpd);
+ return 0;
+ }
+ }
+
+ 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(port, TC_OBJ(port), tc_state_ct_attached_vpd);
+ return 0;
+ }
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_ct_attach_wait_vpd_exit(int port)
+{
+ /* Disable PD */
+ tc[port].pd_enable = 0;
+
+ /* Reset timeout value to 10ms */
+ tc_set_timeout(port, 10 * MSEC);
+
+ return 0;
+}
+
+/**
+ * Super State HOST_RP3_CT_RD
+ */
+static unsigned int tc_state_host_rp3_ct_rd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_rp3_ct_rd_sig[sig])(port);
+
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_rp3_ct_rd_entry(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(port, TC_OBJ(port), tc_state_error_recovery);
+
+ /* Get power from VCONN */
+ vpd_vconn_pwr_sel_odl(PWR_VCONN);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_rp3_ct_rd_run(int port)
+{
+ return 0;
+}
+
+/**
+ * Super State HOST_RP3_CT_RPU
+ */
+static unsigned int tc_state_host_rp3_ct_rpu(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_rp3_ct_rpu_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_rp3_ct_rpu_entry(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(port, TC_OBJ(port), tc_state_error_recovery);
+
+ /* Get power from VCONN */
+ vpd_vconn_pwr_sel_odl(PWR_VCONN);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_rp3_ct_rpu_run(int port)
+{
+ return 0;
+}
+
+static unsigned int do_nothing_exit(int port)
+{
+ return 0;
+}
+
+static unsigned int get_super_state(int port)
+{
+ return RUN_SUPER;
+}
+
+#endif /* CONFIG_USB_TYPEC_CTVPD */
diff --git a/include/usb_tc_sm.h b/include/usb_tc_sm.h
new file mode 100644
index 0000000000..08e9ebbf54
--- /dev/null
+++ b/include/usb_tc_sm.h
@@ -0,0 +1,91 @@
+/* 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.
+ */
+
+/* USB Type-C module */
+
+#ifndef __CROS_EC_USB_TC_H
+#define __CROS_EC_USB_TC_H
+
+enum typec_state_id {
+ DISABLED,
+ UNATTACHED_SNK,
+ ATTACH_WAIT_SNK,
+ ATTACHED_SNK,
+#if !defined(CONFIG_USB_TYPEC_VPD)
+ ERROR_RECOVERY,
+ UNATTACHED_SRC,
+ ATTACH_WAIT_SRC,
+ ATTACHED_SRC,
+#endif
+#if !defined(CONFIG_USB_TYPEC_CTVPD) && !defined(CONFIG_USB_TYPEC_VPD)
+ AUDIO_ACCESSORY,
+ ORIENTED_DEBUG_ACCESSORY_SRC,
+ UNORIENTED_DEBUG_ACCESSORY_SRC,
+ DEBUG_ACCESSORY_SNK,
+ TRY_SRC,
+ TRY_WAIT_SNK,
+ CTUNATTACHED_SNK,
+ CTATTACHED_SNK,
+#endif
+#if defined(CONFIG_USB_TYPEC_CTVPD)
+ CTTRY_SNK,
+ CTATTACHED_UNSUPPORTED,
+ CTATTACH_WAIT_UNSUPPORTED,
+ CTUNATTACHED_UNSUPPORTED,
+ CTUNATTACHED_VPD,
+ CTATTACH_WAIT_VPD,
+ CTATTACHED_VPD,
+ CTDISABLED_VPD,
+ TRY_SNK,
+ TRY_WAIT_SRC,
+#endif
+ /* Number of states. Not an actual state. */
+ TC_STATE_COUNT,
+};
+
+/**
+ * Get the id of the current Type-C state
+ *
+ * @param port USB-C port number
+ */
+enum typec_state_id get_typec_state_id(int port);
+
+/**
+ * Get current data role
+ *
+ * @param port USB-C port number
+ * @return 0 for ufp, 1 for dfp, 2 for disconnected
+ */
+int tc_get_data_role(int port);
+
+/**
+ * Get current power role
+ *
+ * @param port USB-C port number
+ * @return 0 for sink, 1 for source or vpd
+ */
+int tc_get_power_role(int port);
+
+/**
+ * Set loop timeout value
+ *
+ * @param port USB-C port number
+ * @timeout time in ms
+ */
+void tc_set_timeout(int port, uint64_t timeout);
+
+#ifdef CONFIG_USB_TYPEC_CTVPD
+/**
+ * Resets the charge-through support timer. This can be
+ * called many times but the support timer will only
+ * reset once, while in the Attached.SNK state.
+ *
+ * @param port USB-C port number
+ */
+void tc_reset_support_timer(int port);
+
+#endif /* CONFIG_USB_TYPEC_CTVPD */
+#endif /* __CROS_EC_USB_TC_H */
+
diff --git a/include/usb_tc_vpd_sm.h b/include/usb_tc_vpd_sm.h
new file mode 100644
index 0000000000..c8a4dd48a3
--- /dev/null
+++ b/include/usb_tc_vpd_sm.h
@@ -0,0 +1,486 @@
+/* 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 "vpd_api.h"
+
+/* USB Type-C VCONN Powered Device module */
+
+#ifndef __CROS_EC_USB_TC_VPD_H
+#define __CROS_EC_USB_TC_VPD_H
+
+/* Type-C Layer Flags */
+#define TC_FLAGS_VCONN_ON BIT(0)
+
+#undef PD_DEFAULT_STATE
+/* Port default state at startup */
+#define PD_DEFAULT_STATE(port) tc_state_unattached_snk
+
+/*
+ * TC_OBJ is a convenience macro to access struct sm_obj, which
+ * must be the first member of struct type_c.
+ */
+#define TC_OBJ(port) (SM_OBJ(tc[port]))
+
+/**
+ * This is the Type-C Port object that contains information needed to
+ * implement a VCONN Powered Device.
+ */
+static struct type_c {
+ /*
+ * struct sm_obj must be first. This is the state machine
+ * object that keeps track of the current and last state
+ * of the state machine.
+ */
+ struct sm_obj obj;
+ /* state id */
+ enum typec_state_id state_id;
+ /* current port power role (VPD, SOURCE or SINK) */
+ uint8_t power_role;
+ /* current port data role (DFP or UFP) */
+ uint8_t data_role;
+ /* bool: enable power delivery state machines */
+ uint8_t pd_enable;
+ /* event timeout */
+ uint64_t evt_timeout;
+ /* state machine event */
+ int evt;
+ /* 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_COUNT];
+
+/* Type-C states */
+static unsigned int tc_state_disabled(int port, enum signal sig);
+static unsigned int tc_state_disabled_entry(int port);
+static unsigned int tc_state_disabled_run(int port);
+static unsigned int tc_state_disabled_exit(int port);
+
+static unsigned int tc_state_unattached_snk(int port, enum signal sig);
+static unsigned int tc_state_unattached_snk_entry(int port);
+static unsigned int tc_state_unattached_snk_run(int port);
+static unsigned int tc_state_unattached_snk_exit(int port);
+
+static unsigned int tc_state_attach_wait_snk(int port, enum signal sig);
+static unsigned int tc_state_attach_wait_snk_entry(int port);
+static unsigned int tc_state_attach_wait_snk_run(int port);
+static unsigned int tc_state_attach_wait_snk_exit(int port);
+
+static unsigned int tc_state_attached_snk(int port, enum signal sig);
+static unsigned int tc_state_attached_snk_entry(int port);
+static unsigned int tc_state_attached_snk_run(int port);
+static unsigned int tc_state_attached_snk_exit(int port);
+
+/* Super States */
+static unsigned int tc_state_host_rard(int port, enum signal sig);
+static unsigned int tc_state_host_rard_entry(int port);
+static unsigned int tc_state_host_rard_run(int port);
+static unsigned int tc_state_host_rard_exit(int port);
+
+static unsigned int tc_state_host_open(int port, enum signal sig);
+static unsigned int tc_state_host_open_entry(int port);
+static unsigned int tc_state_host_open_run(int port);
+static unsigned int tc_state_host_open_exit(int port);
+
+static unsigned int tc_state_vbus_cc_iso(int port, enum signal sig);
+static unsigned int tc_state_vbus_cc_iso_entry(int port);
+static unsigned int tc_state_vbus_cc_iso_run(int port);
+static unsigned int tc_state_vbus_cc_iso_exit(int port);
+
+static unsigned int get_super_state(int port);
+
+static const state_sig tc_state_disabled_sig[] = {
+ tc_state_disabled_entry,
+ tc_state_disabled_run,
+ tc_state_disabled_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_unattached_snk_sig[] = {
+ tc_state_unattached_snk_entry,
+ tc_state_unattached_snk_run,
+ tc_state_unattached_snk_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_attach_wait_snk_sig[] = {
+ tc_state_attach_wait_snk_entry,
+ tc_state_attach_wait_snk_run,
+ tc_state_attach_wait_snk_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_attached_snk_sig[] = {
+ tc_state_attached_snk_entry,
+ tc_state_attached_snk_run,
+ tc_state_attached_snk_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_rard_sig[] = {
+ tc_state_host_rard_entry,
+ tc_state_host_rard_run,
+ tc_state_host_rard_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_open_sig[] = {
+ tc_state_host_open_entry,
+ tc_state_host_open_run,
+ tc_state_host_open_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_vbus_cc_iso_sig[] = {
+ tc_state_vbus_cc_iso_entry,
+ tc_state_vbus_cc_iso_run,
+ tc_state_vbus_cc_iso_exit,
+ get_super_state
+};
+
+static void tc_state_init(int port)
+{
+ int res = 0;
+ sm_state this_state;
+
+ res = tc_restart_tcpc(port);
+
+ CPRINTS("TCPC p%d init %s", port, res ? "failed" : "ready");
+ this_state = res ? tc_state_disabled : PD_DEFAULT_STATE(port);
+
+ /* Disable TCPC RX until connection is established */
+ tcpm_set_rx_enable(port, 0);
+
+ init_state(port, TC_OBJ(port), this_state);
+
+ /* Disable pd state machines */
+ tc[port].pd_enable = 0;
+ tc[port].evt_timeout = 10*MSEC;
+ tc[port].power_role = PD_PLUG_CABLE_VPD;
+ tc[port].data_role = 0; /* Reserved for VPD */
+ tc[port].flags = 0;
+}
+
+static void tc_event_check(int port, int evt)
+{
+ /* Do Nothing */
+}
+
+/**
+ * Disabled
+ *
+ * Super State Entries:
+ * Enable mcu communication
+ * Remove the terminations from Host CC
+ */
+static unsigned int tc_state_disabled(int port, enum signal sig)
+{
+ int ret = 0;
+
+ ret = (*tc_state_disabled_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_open);
+}
+
+static unsigned int tc_state_disabled_entry(int port)
+{
+ tc[port].state_id = DISABLED;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ return 0;
+}
+
+static unsigned int tc_state_disabled_run(int port)
+{
+ task_wait_event(-1);
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_disabled_exit(int port)
+{
+#ifndef CONFIG_USB_PD_TCPC
+ if (tc_restart_tcpc(port) != 0) {
+ CPRINTS("TCPC p%d restart failed!", port);
+ return 0;
+ }
+#endif
+ CPRINTS("TCPC p%d resumed!", port);
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+
+ return 0;
+}
+
+/**
+ * Unattached.SNK
+ *
+ * Super State Entry:
+ * Enable mcu communication
+ * Place Ra on VCONN and Rd on Host CC
+ */
+static unsigned int tc_state_unattached_snk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_unattached_snk_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rard);
+}
+
+static unsigned int tc_state_unattached_snk_entry(int port)
+{
+ tc[port].state_id = UNATTACHED_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ return 0;
+}
+
+static unsigned int tc_state_unattached_snk_run(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(port, TC_OBJ(port), tc_state_attach_wait_snk);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_unattached_snk_exit(int port)
+{
+ return 0;
+}
+
+/**
+ * AttachedWait.SNK
+ *
+ * Super State Entry:
+ * Enable mcu communication
+ * Place Ra on VCONN and Rd on Host CC
+ */
+static unsigned int tc_state_attach_wait_snk(int port, enum signal sig)
+{
+ int ret = 0;
+
+ ret = (*tc_state_attach_wait_snk_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rard);
+}
+
+static unsigned int tc_state_attach_wait_snk_entry(int port)
+{
+ tc[port].state_id = ATTACH_WAIT_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+ tc[port].host_cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_attach_wait_snk_run(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 0;
+ }
+
+ /* Wait for Host CC debounce */
+ if (get_time().val < tc[port].cc_debounce)
+ return 0;
+
+ /*
+ * 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(port, TC_OBJ(port), tc_state_attached_snk);
+ else if (tc[port].host_cc_state == PD_CC_NONE)
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+
+ return 0;
+}
+
+static unsigned int tc_state_attach_wait_snk_exit(int port)
+{
+ return 0;
+}
+
+/**
+ * Attached.SNK
+ */
+static unsigned int tc_state_attached_snk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_attached_snk_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tc_state_attached_snk_entry(int port)
+{
+ tc[port].state_id = ATTACHED_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ return 0;
+}
+
+static unsigned int tc_state_attached_snk_run(int port)
+{
+ /* Has host vbus and vconn been removed */
+ if (!vpd_is_host_vbus_present() && !vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ 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;
+ }
+ }
+
+ return 0;
+}
+
+static unsigned int tc_state_attached_snk_exit(int port)
+{
+ /* Disable PD */
+ tc[port].pd_enable = 0;
+ tc[port].flags &= ~TC_FLAGS_VCONN_ON;
+
+ return 0;
+}
+
+/**
+ * Super State HOST_RARD
+ */
+static unsigned int tc_state_host_rard(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_rard_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_rard_entry(int port)
+{
+ /* Place Ra on VCONN and Rd on Host CC */
+ vpd_host_set_pull(TYPEC_CC_RA_RD, 0);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_rard_run(int port)
+{
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_host_rard_exit(int port)
+{
+ return 0;
+}
+
+/**
+ * Super State HOST_OPEN
+ */
+static unsigned int tc_state_host_open(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_open_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_open_entry(int port)
+{
+ /* Remove the terminations from Host CC */
+ vpd_host_set_pull(TYPEC_CC_OPEN, 0);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_open_run(int port)
+{
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_host_open_exit(int port)
+{
+ return 0;
+}
+
+/**
+ * Super State VBUS_CC_ISO
+ */
+static unsigned int tc_state_vbus_cc_iso(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_vbus_cc_iso_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tc_state_vbus_cc_iso_entry(int port)
+{
+ /* Enable mcu communication and cc */
+ vpd_mcu_cc_en(1);
+
+ return 0;
+}
+
+static unsigned int tc_state_vbus_cc_iso_run(int port)
+{
+ return 0;
+}
+
+static unsigned int tc_state_vbus_cc_iso_exit(int port)
+{
+ return 0;
+}
+
+static unsigned int get_super_state(int port)
+{
+ return RUN_SUPER;
+}
+
+#endif /*__CROS_EC_USB_TC_VPD_H */