summaryrefslogtreecommitdiff
path: root/test/usb_typec_ctvpd.c
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 /test/usb_typec_ctvpd.c
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 'test/usb_typec_ctvpd.c')
-rw-r--r--test/usb_typec_ctvpd.c1488
1 files changed, 1488 insertions, 0 deletions
diff --git a/test/usb_typec_ctvpd.c b/test/usb_typec_ctvpd.c
new file mode 100644
index 0000000000..19fbfa1b8a
--- /dev/null
+++ b/test/usb_typec_ctvpd.c
@@ -0,0 +1,1488 @@
+/* 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.
+ *
+ * Test USB Type-C VPD and CTVPD module.
+ */
+#include "common.h"
+#include "crc.h"
+#include "task.h"
+#include "test_util.h"
+#include "timer.h"
+#include "usb_pd.h"
+#include "usb_sm.h"
+#include "usb_tc_sm.h"
+#include "util.h"
+#include "usb_pd_tcpm.h"
+#include "usb_pd_test_util.h"
+#include "vpd_api.h"
+
+#define PORT0 0
+
+enum cc_type {CC1, CC2};
+enum vbus_type {VBUS_0 = 0, VBUS_5 = 5000};
+enum vconn_type {VCONN_0 = 0, VCONN_3 = 3000, VCONN_5 = 5000};
+enum snk_con_voltage_type {SRC_CON_DEF, SRC_CON_1_5, SRC_CON_3_0};
+
+struct pd_port_t {
+ int host_mode;
+ int has_vbus;
+ int msg_tx_id;
+ int msg_rx_id;
+ int polarity;
+ int partner_role; /* -1 for none */
+ int partner_polarity;
+ int rev;
+} pd_port[CONFIG_USB_PD_PORT_COUNT];
+
+uint64_t wait_for_state_change(int port, uint64_t timeout)
+{
+ uint64_t start;
+ uint64_t wait;
+ uint32_t state = get_typec_state_id(port);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+
+ wait = get_time().val + timeout;
+ start = get_time().val;
+ while (get_typec_state_id(port) == state && get_time().val < wait)
+ task_wait_event(5 * MSEC);
+
+ return get_time().val - start;
+}
+
+#if defined(TEST_USB_TYPEC_CTVPD)
+static int ct_connect_sink(enum cc_type cc, enum snk_con_voltage_type v)
+{
+ int ret;
+
+ switch (v) {
+ case SRC_CON_DEF:
+ ret = (cc) ? mock_set_cc2_rp3a0_rd_l(PD_SRC_DEF_RD_THRESH_MV) :
+ mock_set_cc1_rp3a0_rd_l(PD_SRC_DEF_RD_THRESH_MV);
+ break;
+ case SRC_CON_1_5:
+ ret = (cc) ? mock_set_cc2_rp3a0_rd_l(PD_SRC_1_5_RD_THRESH_MV) :
+ mock_set_cc1_rp3a0_rd_l(PD_SRC_1_5_RD_THRESH_MV);
+ break;
+ case SRC_CON_3_0:
+ ret = (cc) ? mock_set_cc2_rp3a0_rd_l(PD_SRC_3_0_RD_THRESH_MV) :
+ mock_set_cc1_rp3a0_rd_l(PD_SRC_3_0_RD_THRESH_MV);
+ break;
+ default:
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int ct_disconnect_sink(void)
+{
+ int r1;
+ int r2;
+
+ r1 = mock_set_cc1_rp3a0_rd_l(PD_SRC_DEF_VNC_MV);
+ r2 = mock_set_cc2_rp3a0_rd_l(PD_SRC_DEF_VNC_MV);
+
+ return r1 & r2;
+}
+
+static int ct_connect_source(enum cc_type cc, enum vbus_type vbus)
+{
+ mock_set_ct_vbus(vbus);
+ return (cc) ? mock_set_cc2_rpusb_odh(PD_SNK_VA_MV) :
+ mock_set_cc1_rpusb_odh(PD_SNK_VA_MV);
+}
+
+static int ct_disconnect_source(void)
+{
+ int r1;
+ int r2;
+
+ mock_set_ct_vbus(VBUS_0);
+ r1 = mock_set_cc1_rpusb_odh(0);
+ r2 = mock_set_cc2_rpusb_odh(0);
+
+ return r1 & r2;
+}
+#endif
+
+static void host_disconnect_source(void)
+{
+ mock_set_host_vbus(VBUS_0);
+ mock_set_host_cc_source_voltage(0);
+ mock_set_host_cc_sink_voltage(0);
+}
+
+static void host_connect_source(enum vbus_type vbus)
+{
+ mock_set_host_vbus(vbus);
+ mock_set_host_cc_source_voltage(PD_SNK_VA_MV);
+}
+
+#if defined(TEST_USB_TYPEC_CTVPD)
+static void host_connect_sink(enum snk_con_voltage_type v)
+{
+ switch (v) {
+ case SRC_CON_DEF:
+ mock_set_host_cc_sink_voltage(PD_SRC_DEF_RD_THRESH_MV);
+ break;
+ case SRC_CON_1_5:
+ mock_set_host_cc_sink_voltage(PD_SRC_1_5_RD_THRESH_MV);
+ break;
+ case SRC_CON_3_0:
+ mock_set_host_cc_sink_voltage(PD_SRC_3_0_RD_THRESH_MV);
+ break;
+ }
+}
+#endif
+
+static void init_port(int port)
+{
+ pd_port[port].polarity = 0;
+ pd_port[port].rev = PD_REV30;
+ pd_port[port].msg_tx_id = 0;
+ pd_port[port].msg_rx_id = 0;
+}
+
+static int check_host_ra_rd(void)
+{
+ /* Make sure CC_RP3A0_RD_L is configured as GPO */
+ if (mock_get_cfg_cc_rp3a0_rd_l() != PIN_GPO)
+ return 0;
+
+ /* Make sure CC_RP3A0_RD_L is asserted low */
+ if (mock_get_cc_rp3a0_rd_l() != 0)
+ return 0;
+
+ /* Make sure VPDMCU_CC_EN is enabled */
+ if (mock_get_mcu_cc_en() != 1)
+ return 0;
+
+ /* Make sure CC_VPDMCU is configured as ADC */
+ if (mock_get_cfg_cc_vpdmcu() != PIN_ADC)
+ return 0;
+
+ /* Make sure CC_DB_EN_OD is HZ */
+ if (mock_get_cc_db_en_od() != GPO_HZ)
+ return 0;
+
+ return 1;
+}
+
+static int check_host_rd(void)
+{
+ /* Make sure CC_RP3A0_RD_L is configured as GPO */
+ if (mock_get_cfg_cc_rp3a0_rd_l() != PIN_GPO)
+ return 0;
+
+ /* Make sure CC_RP3A0_RD_L is asserted low */
+ if (mock_get_cc_rp3a0_rd_l() != 0)
+ return 0;
+
+ /* Make sure VPDMCU_CC_EN is enabled */
+ if (mock_get_mcu_cc_en() != 1)
+ return 0;
+
+ /* Make sure CC_VPDMCU is configured as ADC */
+ if (mock_get_cfg_cc_vpdmcu() != PIN_ADC)
+ return 0;
+
+ /* Make sure CC_DB_EN_OD is LOW */
+ if (mock_get_cc_db_en_od() != GPO_LOW)
+ return 0;
+
+ return 1;
+}
+
+#if defined(TEST_USB_TYPEC_CTVPD)
+static int check_host_rp3a0(void)
+{
+ /* Make sure CC_RP3A0_RD_L is asserted high */
+ if (mock_get_cc_rp3a0_rd_l() != 1)
+ return 0;
+
+ return 1;
+}
+
+static int check_host_rpusb(void)
+{
+ /* Make sure CC_RPUSB_ODH is asserted high */
+ if (mock_get_cc_rpusb_odh() != 1)
+ return 0;
+
+ /* Make sure CC_RP3A0_RD_L is configured as comparator */
+ if (mock_get_cfg_cc_rp3a0_rd_l() != PIN_CMP)
+ return 0;
+
+ return 1;
+}
+
+static int check_host_cc_open(void)
+{
+ /* Make sure CC_RPUSB_ODH is hi-z */
+ if (mock_get_cc_rpusb_odh() != GPO_HZ)
+ return 0;
+
+ /* Make sure CC_RP3A0_RD_L is set to comparitor */
+ if (mock_get_cfg_cc_rp3a0_rd_l() != PIN_CMP)
+ return 0;
+
+ /* Make sure cc_db_en_od is set low */
+ if (mock_get_cc_db_en_od() != GPO_LOW)
+ return 0;
+
+ return 1;
+}
+
+static int check_ct_ccs_hz(void)
+{
+ return (mock_get_ct_rd() == GPO_HIGH);
+}
+
+static int check_ct_ccs_rd(void)
+{
+ return (mock_get_ct_rd() == GPO_LOW);
+}
+
+static int check_ct_ccs_cc1_rpusb(void)
+{
+ return (mock_get_ct_cc1_rpusb() == 1);
+}
+#endif
+
+void inc_tx_id(int port)
+{
+ pd_port[port].msg_tx_id = (pd_port[port].msg_tx_id + 1) % 7;
+}
+
+void inc_rx_id(int port)
+{
+ pd_port[port].msg_rx_id = (pd_port[port].msg_rx_id + 1) % 7;
+}
+
+static int verify_goodcrc(int port, int role, int id)
+{
+ return pd_test_tx_msg_verify_sop_prime(port) &&
+ pd_test_tx_msg_verify_short(port, PD_HEADER(PD_CTRL_GOOD_CRC,
+ role, role, id, 0, 0, 0)) &&
+ pd_test_tx_msg_verify_crc(port) &&
+ pd_test_tx_msg_verify_eop(port);
+}
+
+static void simulate_rx_msg(int port, uint16_t header, int cnt,
+ const uint32_t *data)
+{
+ int i;
+
+ pd_test_rx_set_preamble(port, 1);
+ pd_test_rx_msg_append_sop_prime(port);
+ pd_test_rx_msg_append_short(port, header);
+
+ crc32_init();
+ crc32_hash16(header);
+
+ for (i = 0; i < cnt; ++i) {
+ pd_test_rx_msg_append_word(port, data[i]);
+ crc32_hash32(data[i]);
+ }
+
+ pd_test_rx_msg_append_word(port, crc32_result());
+
+ pd_test_rx_msg_append_eop(port);
+ pd_test_rx_msg_append_last_edge(port);
+
+ pd_simulate_rx(port);
+}
+
+static void simulate_goodcrc(int port, int role, int id)
+{
+ simulate_rx_msg(port, PD_HEADER(PD_CTRL_GOOD_CRC, role, role, id, 0,
+ pd_port[port].rev, 0), 0, NULL);
+}
+
+static void simulate_discovery_identity(int port)
+{
+ uint16_t header = PD_HEADER(PD_DATA_VENDOR_DEF, PD_ROLE_SOURCE,
+ 0, pd_port[port].msg_rx_id,
+ 1, pd_port[port].rev, 0);
+ uint32_t msg = VDO(USB_SID_PD,
+ 1, /* Structured VDM */
+ VDO_SVDM_VERS(1) |
+ VDO_CMDT(CMDT_INIT) |
+ CMD_DISCOVER_IDENT);
+
+ simulate_rx_msg(port, header, 1, (const uint32_t *)&msg);
+}
+
+static int test_vpd_host_src_detection(void)
+{
+ int port = PORT0;
+
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * TEST:
+ * Host is configured properly and start state is UNATTACHED_SNK
+ */
+ TEST_ASSERT(check_host_ra_rd());
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ /*
+ * TEST:
+ * Host PORT Source Connection Detected
+ */
+
+ host_connect_source(VBUS_0);
+ mock_set_vconn(VCONN_0);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ /*
+ * TEST:
+ * Host CC debounce in ATTACH_WAIT_SNK state
+ */
+
+ host_disconnect_source();
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(5 * MSEC);
+
+ /*
+ * TEST:
+ * Host CC debounce in ATTACH_WAIT_SNK state
+ */
+
+ host_connect_source(VBUS_0);
+ mock_set_vconn(VCONN_0);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(50 * MSEC);
+
+ /*
+ * TEST:
+ * Host Port Connection Removed
+ */
+ host_disconnect_source();
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ return EC_SUCCESS;
+}
+
+static int test_vpd_host_src_detection_vbus(void)
+{
+ int port = PORT0;
+
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * TEST:
+ * Host is configured properly and start state is UNATTACHED_SNK
+ */
+
+ TEST_ASSERT(check_host_ra_rd());
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ /*
+ * TEST:
+ * Host Port Source Connection Detected
+ */
+
+ host_connect_source(VBUS_0);
+ mock_set_vconn(VCONN_0);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ /*
+ * TEST:
+ * Host Port Source Detected for tCCDebounce and Host Port VBUS
+ * Detected.
+ */
+
+ host_connect_source(VBUS_5);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACHED_SNK);
+
+ /*
+ * TEST:
+ * Host Port VBUS Removed
+ */
+
+ host_connect_source(VBUS_0);
+
+ wait_for_state_change(port, 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ return EC_SUCCESS;
+}
+
+static int test_vpd_host_src_detection_vconn(void)
+{
+ int port = PORT0;
+
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * TEST:
+ * Host is configured properly and start state is UNATTACHED_SNK
+ */
+
+ TEST_ASSERT(check_host_ra_rd());
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ /*
+ * TEST:
+ * Host Source Connection Detected
+ */
+
+ host_connect_source(VBUS_0);
+ mock_set_vconn(VCONN_0);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ /*
+ * TEST:
+ * Host Port Source Detected for tCCDebounce and VCONN Detected
+ */
+
+ host_connect_source(VBUS_0);
+ mock_set_vconn(VCONN_3);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACHED_SNK);
+
+ /* VCONN was detected. Make sure RA is removed */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+ TEST_ASSERT(check_host_rd());
+
+ /*
+ * TEST:
+ * Host Port VCONN Removed
+ */
+
+ mock_set_vconn(VCONN_0);
+
+ wait_for_state_change(port, 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ host_disconnect_source();
+
+ return EC_SUCCESS;
+}
+
+static int test_vpd_host_src_detection_message_reception(void)
+{
+ int port = PORT0;
+ uint32_t expected_vdm_header = VDO(USB_VID_GOOGLE,
+ 1, /* Structured VDM */
+ VDO_SVDM_VERS(1) |
+ VDO_CMDT(CMDT_RSP_ACK) |
+ CMD_DISCOVER_IDENT);
+ uint32_t expected_vdo_id_header = 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);
+ uint32_t expected_vdo_cert = 0;
+ uint32_t expected_vdo_product = VDO_PRODUCT(
+ CONFIG_USB_PID,
+ USB_BCD_DEVICE);
+ uint32_t expected_vdo_vpd = 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
+ );
+
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * TEST:
+ * Host is configured properly and start state is UNATTACHED_SNK
+ */
+
+ TEST_ASSERT(check_host_ra_rd());
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ /*
+ * Transition to ATTACHED_SNK
+ */
+
+ host_connect_source(VBUS_5);
+
+ wait_for_state_change(port, 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 20 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACHED_SNK);
+
+ /* Run state machines to enable rx monitoring */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ /*
+ * TEST:
+ * Reception of Discovery Identity message
+ */
+
+ simulate_discovery_identity(port);
+ task_wait_event(30 * MSEC);
+
+ TEST_ASSERT(verify_goodcrc(port,
+ PD_ROLE_SINK, pd_port[port].msg_rx_id));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_rx_id(port);
+
+ /* Test Discover Identity Ack */
+ TEST_ASSERT(pd_test_tx_msg_verify_sop_prime(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port,
+ PD_HEADER(PD_DATA_VENDOR_DEF, PD_PLUG_CABLE_VPD, 0,
+ pd_port[port].msg_tx_id, 5, pd_port[port].rev, 0)));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdm_header));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_id_header));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_cert));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_product));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_vpd));
+ TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ /* Ack was good. Send GoodCRC */
+ simulate_goodcrc(port, PD_ROLE_SOURCE, pd_port[port].msg_tx_id);
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_tx_id(port);
+
+ /*
+ * TEST:
+ * Host Port VBUS Removed
+ */
+
+ host_connect_source(VBUS_0);
+
+ wait_for_state_change(port, 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ host_disconnect_source();
+
+ return EC_SUCCESS;
+}
+
+#if defined(TEST_USB_TYPEC_CTVPD)
+static int test_ctvpd_behavior_case1(void)
+{
+ int port = PORT0;
+
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+ TEST_ASSERT(ct_disconnect_source());
+ TEST_ASSERT(ct_disconnect_sink());
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * CASE 1: The following tests the behavior when a DRP is connected to a
+ * Charge-Through VCONN-Powered USB Device (abbreviated CTVPD),
+ * with no Power Source attached to the ChargeThrough port on
+ * the CTVPD.
+ */
+
+ /* 1. DRP and CTVPD are both in the unattached state */
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ /*
+ * a. DRP alternates between Unattached.SRC and Unattached.SNK
+ *
+ * b. CTVPD has applied Rd on its Charge-Through port’s CC1 and CC2
+ * pins and Rd on the Host-side port’s CC pin
+ */
+ TEST_ASSERT(check_host_ra_rd());
+ TEST_ASSERT(check_ct_ccs_rd());
+
+ /*
+ * 2. DRP transitions from Unattached.SRC to AttachWait.SRC to
+ * Attached.SRC
+ *
+ * a. DRP in Unattached.SRC detects the CC pull-down of CTVPD which
+ * is in Unattached.SNK and DRP enters AttachWait.SRC
+ * b. DRP in AttachWait.SRC detects that pull down on CC persists for
+ * tCCDebounce, enters Attached.SRC and turns on VBUS and VCONN
+ */
+ host_connect_source(VBUS_5);
+ mock_set_vconn(VCONN_3);
+
+ /*
+ * 3. CTVPD transitions from Unattached.SNK to Attached.SNK through
+ * AttachWait.SNK.
+ *
+ * a. CTVPD detects the host-side CC pull-up of the DRP and CTVPD
+ * enters AttachWait.SNK
+ * b. CTVPD in AttachWait.SNK detects that pull up on the Host-side
+ * port’s CC persists for tCCDebounce, VCONN present and enters
+ * Attached.SNK
+ * c. CTVPD present a high-impedance to ground (above zOPEN) on its
+ * Charge-Through port’s CC1 and CC2 pins
+ */
+ wait_for_state_change(port, 40 * MSEC);
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+ TEST_ASSERT(get_typec_state_id(port) == ATTACHED_SNK);
+ TEST_ASSERT(check_ct_ccs_hz());
+
+ /*
+ * 4. While DRP and CTVPD are in their respective attached states, DRP
+ * discovers the ChargeThrough CTVPD and transitions to
+ * CTUnattached.SNK
+ *
+ * a. DRP (as Source) queries the device identity via USB PD
+ * (Device Identity Command) on SOP’.
+ * b. CTVPD responds on SOP’, advertising that it is a
+ * Charge-Through VCONN-Powered USB Device
+ * c. DRP (as Source) removes VBUS
+ * d. DRP (as Source) changes its Rp to a Rd
+ * e. DRP (as Sink) continues to provide VCONN and enters
+ * CTUnattached.SNK
+ */
+ host_disconnect_source();
+
+ /*
+ * 5. CTVPD transitions to CTUnattached.VPD
+ *
+ * a. CTVPD detects VBUS removal, VCONN presence, the low Host-side
+ * CC pin and enters CTUnattached.VPD
+ * b. CTVPD changes its host-side Rd to a Rp advertising 3.0 A
+ * c. CTVPD isolates itself from VBUS
+ * d. CTVPD apply Rd on its Charge-Through port’s CC1 and CC2 pins
+ */
+ wait_for_state_change(port, 40 * MSEC);
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_VPD);
+
+ /*
+ * 6. While the CTVPD in CTUnattached.VPD state and the DRP in
+ * CTUnattached.SNK state:
+ *
+ * a. CTVPD monitors Charge-Though CC pins for a source or sink;
+ * when a Power Source attach is detected, enters
+ * CTAttachWait.VPD; when a sink is detected, enters
+ * CTAttachWait.Unsupported
+ * b. CTVPD monitors VCONN for Host detach and when detected, enters
+ * Unattached.SNK
+ * c. DRP monitors VBUS and CC for CTVPD detach for tVPDDetach and
+ * when detected, enters Unattached.SNK
+ * d. DRP monitors VBUS for Power Source attach and when detected,
+ * enters CTAttached.SNK
+ */
+ /* Attach Power Source */
+ TEST_ASSERT(ct_connect_source(CC2, VBUS_0));
+
+ wait_for_state_change(port, 40 * MSEC);
+ TEST_ASSERT(get_typec_state_id(port) == CTATTACH_WAIT_VPD);
+
+ /* Remove Power Source */
+ TEST_ASSERT(ct_disconnect_source());
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_VPD);
+
+ /* Attach Sink */
+ TEST_ASSERT(ct_connect_sink(CC1, SRC_CON_DEF));
+
+ wait_for_state_change(port, PD_T_DRP_SNK);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_UNSUPPORTED);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTATTACH_WAIT_UNSUPPORTED);
+
+ /* Remove VCONN (Host detach) */
+ mock_set_vconn(VCONN_0);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ return EC_SUCCESS;
+}
+
+static int test_ctvpd_behavior_case2(void)
+{
+ int port = PORT0;
+
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+ TEST_ASSERT(ct_disconnect_source());
+ TEST_ASSERT(ct_disconnect_sink());
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * CASE 2: The following tests the behavior when a Power Source is
+ * connected to a Charge-Through VCONN-Powered USB Device
+ * (abbreviated CTVPD), with a Host already attached to the
+ * Host-Side port on the CTVPD.
+ */
+
+ /*
+ * 1. DRP is in CTUnattached.SNK state, CTVPD in CTUnattached.VPD, and
+ * Power Source in the unattached state
+ *
+ * a. CTVPD has applied Rd on the Charge-Through port’s CC1 and CC2
+ * pins and Rp termination advertising 3.0 A on the Host-side
+ * port’s CC pin
+ */
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ host_connect_source(VBUS_5);
+ mock_set_vconn(VCONN_3);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACHED_SNK);
+
+ /* Remove Host CC */
+ mock_set_host_cc_source_voltage(0);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_VPD);
+ TEST_ASSERT(check_ct_ccs_rd());
+ TEST_ASSERT(check_host_rp3a0());
+
+ /*
+ * 2. Power Source transitions from Unattached.SRC to Attached.SRC
+ * through AttachWait.SRC.
+ *
+ * a. Power Source detects the CC pull-down of the CTVPD and enters
+ * AttachWait.SRC
+ * b. Power Source in AttachWait.SRC detects that pull down on CC
+ * persists for tCCDebounce, enters Attached.SRC and turns on
+ * VBUS
+ */
+ TEST_ASSERT(ct_connect_source(CC2, VBUS_5));
+
+ /*
+ * 3. CTVPD transitions from CTUnattached.VPD through CTAttachWait.VPD
+ * to CTAttached.VPD
+ *
+ * a. CTVPD detects the Source’s Rp on one of its Charge-Through CC
+ * pins, and transitions to CTAttachWait.VPD
+ * b. CTVPD finishes any active USB PD communication on SOP’ and
+ * ceases to respond to SOP’ queries
+ * c. CTVPD in CTAttachWait.VPD detects that the pull up on
+ * Charge-Through CC pin persists for tCCDebounce, detects VBUS
+ * and enters CTAttached.VPD
+ * d. CTVPD connects the active Charge-Through CC pin to the
+ * Host-side port’s CC pin
+ * e. CTVPD disables its Rp termination advertising 3.0 A on the
+ * Host-side port’s CC pin
+ * f. CTVPD disables its Rd on the Charge-Through CC pins
+ * g. CTVPD connects VBUS from the Charge-Through side to the Host
+ * side
+ */
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTATTACH_WAIT_VPD);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTATTACHED_VPD);
+ TEST_ASSERT(moch_get_ct_cl_sel() == CT_CC2);
+ TEST_ASSERT(check_host_cc_open());
+ TEST_ASSERT(check_ct_ccs_hz());
+ TEST_ASSERT(mock_get_vbus_pass_en());
+
+ /*
+ * 4. DRP (as Sink) transitions to CTAttached.SNK
+ * a. DRP (as Sink) detects VBUS, monitors vRd for available current
+ * and enter CTAttached.SNK
+ */
+
+ /*
+ * 5. While the devices are all in their respective attached states:
+ * a. CTVPD monitors VCONN for DRP detach and when detected,
+ * enters CTDisabled.VPD
+ * b. CTVPD monitors VBUS and CC for Power Source detach and when
+ * detected, enters CTUnattached.VPD within tVPDCTDD
+ * c. DRP (as Sink) monitors VBUS for Charge-Through Power Source
+ * detach and when detected, enters CTUnattached.SNK
+ * d. DRP (as Sink) monitors VBUS and CC for CTVPD detach and when
+ * detected, enters Unattached.SNK (and resumes toggling between
+ * Unattached.SNK and Unattached.SRC)
+ * e. Power Source monitors CC for CTVPD detach and when detected,
+ * enters Unattached.SRC
+ */
+ mock_set_vconn(VCONN_0);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTDISABLED_VPD);
+
+ return EC_SUCCESS;
+}
+
+static int test_ctvpd_behavior_case3(void)
+{
+ int port = PORT0;
+
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+ TEST_ASSERT(ct_disconnect_source());
+ TEST_ASSERT(ct_disconnect_sink());
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * CASE 3: The following describes the behavior when a Power Source is
+ * connected to a ChargeThrough VCONN-Powered USB Device
+ * (abbreviated CTVPD), with no Host attached to the Host-side
+ * port on the CTVPD.
+ */
+
+ /*
+ * 1. CTVPD and Power Source are both in the unattached state
+ * a. CTVPD has applied Rd on the Charge-Through port’s CC1 and CC2
+ * pins and Rd on the Host-side port’s CC pin
+ */
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ TEST_ASSERT(check_ct_ccs_rd());
+ TEST_ASSERT(check_host_ra_rd());
+ TEST_ASSERT(ct_connect_source(CC2, VBUS_5));
+
+ /*
+ * 2. Power Source transitions from Unattached.SRC to Attached.SRC
+ * through AttachWait.SRC.
+ *
+ * a. Power Source detects the CC pull-down of the CTVPD and enters
+ * AttachWait.SRC
+ * b. Power Source in AttachWait.SRC detects that pull down on CC
+ * persists for tCCDebounce, enters Attached.SRC and turns on
+ * VBUS
+ */
+
+ /* 3. CTVPD alternates between Unattached.SNk and Unattached.SRC
+ *
+ * a. CTVPD detects the Source’s Rp on one of its Charge-Through CC
+ * pins, detects VBUS for tCCDebounce and starts alternating
+ * between Unattached.SRC and Unattached.SNK
+ */
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SRC);
+
+ /*
+ * 4. While the CTVPD alternates between Unattached.SRC and
+ * Unattached.SNK state and the Power Source in Attached.SRC state:
+ *
+ * a. CTVPD monitors the Host-side port’s CC pin for device attach
+ * and when detected, enters AttachWait.SRC
+ * b. CTVPD monitors VBUS for Power Source detach and when detected,
+ * enters Unattached.SNK
+ * c. Power Source monitors CC for CTVPD detach and when detected,
+ * enters Unattached.SRC
+ */
+
+ /* Attached host side device */
+ host_connect_sink(SRC_CON_DEF);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SRC);
+
+ /* Remove VBUS */
+ TEST_ASSERT(ct_disconnect_source());
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ return EC_SUCCESS;
+}
+
+static int test_ctvpd_behavior_case4(void)
+{
+ int port = PORT0;
+ uint32_t expected_vdm_header = VDO(USB_VID_GOOGLE,
+ 1, /* Structured VDM */
+ VDO_SVDM_VERS(1) |
+ VDO_CMDT(CMDT_RSP_ACK) |
+ CMD_DISCOVER_IDENT);
+ uint32_t expected_vdo_id_header = 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);
+ uint32_t expected_vdo_cert = 0;
+ uint32_t expected_vdo_product = VDO_PRODUCT(
+ CONFIG_USB_PID,
+ USB_BCD_DEVICE);
+ uint32_t expected_vdo_vpd = VDO_VPD(
+ VPD_HW_VERSION,
+ VPD_FW_VERSION,
+ VPD_MAX_VBUS_20V,
+ VPD_VBUS_IMP(VPD_VBUS_IMPEDANCE),
+ VPD_GND_IMP(VPD_GND_IMPEDANCE),
+ VPD_CTS_SUPPORTED
+ );
+
+ init_port(port);
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+ TEST_ASSERT(ct_disconnect_source());
+ TEST_ASSERT(ct_disconnect_sink());
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * CASE 4: The following describes the behavior when a DRP is connected
+ * to a Charge-Through VCONN-Powered USB Device
+ * (abbreviated CTVPD), with a Power Source already attached to
+ * the Charge-Through side on the CTVPD.
+ */
+
+ /*
+ * 1. DRP, CTVPD and Sink are all in the unattached state
+ *
+ * a. DRP alternates between Unattached.SRC and Unattached.SNK
+ * b. CTVPD has applied Rd on its Charge-Through port’s CC1 and CC2
+ * pins and Rd on the Host-side port’s CC pin
+ */
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ TEST_ASSERT(check_ct_ccs_rd());
+ TEST_ASSERT(check_host_ra_rd());
+
+ /*
+ * 2. DRP transitions from Unattached.SRC to AttachWait.SRC to
+ * Attached.SRC
+ *
+ * a. DRP in Unattached.SRC detects the CC pull-down of CTVPD which
+ * is in Unattached.SNK and DRP enters AttachWait.SRC
+ * b. DRP in AttachWait.SRC detects that pull down on CC persists
+ * for tCCDebounce, enters Attached.SRC and turns on VBUS and
+ * VCONN
+ */
+
+ host_connect_source(VBUS_5);
+ mock_set_vconn(VCONN_3);
+
+ /*
+ * 3. CTVPD transitions from Unattached.SNK to Attached.SNK through
+ * AttachWait.SNK.
+ *
+ * a. CTVPD detects the host-side CC pull-up of the DRP and CTVPD
+ * enters AttachWait.SNK
+ * b. CTVPD in AttachWait.SNK detects that pull up on the
+ * Host-side port’s CC persists for tCCDebounce, VCONN present
+ * and enters Attached.SNK
+ * c. CTVPD present a high-impedance to ground (above zOPEN) on its
+ * Charge-Through port’s CC1 and CC2 pins
+ */
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACHED_SNK);
+ TEST_ASSERT(check_ct_ccs_hz());
+
+ /*
+ * 4. While DRP and CTVPD are in their respective attached states, DRP
+ * discovers the ChargeThrough CTVPD and transitions to
+ * CTUnattached.SNK
+ *
+ * a. DRP (as Source) queries the device identity via USB PD
+ * (Discover Identity Command) on SOP’.
+ * b. CTVPD responds on SOP’, advertising that it is a
+ * Charge-Through VCONN-Powered USB Device
+ * c. DRP (as Source) removes VBUS
+ * d. DRP (as Source) changes its Rp to a Rd
+ * e. DRP (as Sink) continues to provide VCONN and enters
+ * CTUnattached.SNK
+ */
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ simulate_discovery_identity(port);
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(verify_goodcrc(port,
+ PD_ROLE_SINK, pd_port[port].msg_rx_id));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+ inc_rx_id(port);
+
+ /* Test Discover Identity Ack */
+ TEST_ASSERT(pd_test_tx_msg_verify_sop_prime(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port,
+ PD_HEADER(PD_DATA_VENDOR_DEF, PD_PLUG_CABLE_VPD, 0,
+ pd_port[port].msg_tx_id, 5, pd_port[port].rev, 0)));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdm_header));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_id_header));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_cert));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_product));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_vpd));
+ TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /* Ack was good. Send GoodCRC */
+ simulate_goodcrc(port, PD_ROLE_SOURCE, pd_port[port].msg_tx_id);
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+ inc_tx_id(port);
+
+ /*
+ * 5. CTVPD transitions to CTUnattached.VPD
+ *
+ * a. CTVPD detects VBUS removal, VCONN presence, the low Host-side
+ * CC pin and enters CTUnattached.VPD
+ * b. CTVPD changes its host-side Rd to a Rp termination advertising
+ * 3.0 A
+ * c. CTVPD isolates itself from VBUS
+ * d. CTVPD apply Rd on its Charge-Through port’s CC1 and CC2 pins
+ */
+ host_disconnect_source();
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_VPD);
+ TEST_ASSERT(check_ct_ccs_rd());
+ TEST_ASSERT(check_host_rp3a0());
+
+ /*
+ * 6. CTVPD alternates between CTUnattached.VPD and
+ * CTUnattached.Unsupported
+ */
+ wait_for_state_change(port, PD_T_DRP_SRC + 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_UNSUPPORTED);
+
+ wait_for_state_change(port, PD_T_DRP_SRC + 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_VPD);
+ TEST_ASSERT(ct_connect_source(CC2, VBUS_5));
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTATTACH_WAIT_VPD);
+
+ return EC_SUCCESS;
+}
+
+static int test_ctvpd_behavior_case5(void)
+{
+ int port = PORT0;
+
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+ TEST_ASSERT(ct_disconnect_source());
+ TEST_ASSERT(ct_disconnect_sink());
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * CASE 5: The following describes the behavior when a Power Source is
+ * connected to a ChargeThrough VCONN-Powered USB Device
+ * (abbreviated CTVPD), with a DRP (with dead battery) attached
+ * to the Host-side port on the CTVPD.
+ */
+
+ /*
+ * 1. DRP, CTVPD and Power Source are all in the unattached state
+ *
+ * a. DRP apply dead battery Rd
+ * b. CTVPD apply Rd on the Charge-Through port’s CC1 and CC2 pins
+ * and Rd on the Host-side port’s CC pin
+ */
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ TEST_ASSERT(check_ct_ccs_rd());
+ TEST_ASSERT(check_host_ra_rd());
+
+ /*
+ * 2. Power Source transitions from Unattached.SRC to Attached.SRC
+ * through AttachWait.SRC.
+ *
+ * a. Power Source detects the CC pull-down of the CTVPD and enters
+ * AttachWait.SRC
+ * b. Power Source in AttachWait.SRC detects that pull down on CC
+ * persists for tCCDebounce, enters Attached.SRC and enable VBUS
+ */
+ TEST_ASSERT(ct_connect_source(CC2, VBUS_5));
+
+ /*
+ * 3. CTVPD alternates between Unattached.SNK and Unattached.SRC
+ *
+ * a. CTVPD detects the Source’s Rp on one of its Charge-Through CC
+ * pins, detects VBUS for tCCDebounce and starts alternating
+ * between Unattached.SRC and Unattached.SNK
+ */
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SRC);
+
+ /* Connect Host With Dead Battery */
+ host_connect_sink(SRC_CON_DEF);
+
+ /*
+ * 4. CTVPD transitions from Unattached.SRC to Try.SNK through
+ * AttachWait.SRC
+ *
+ * a. CTVPD in Unattached.SRC detects the CC pull-down of DRP which
+ * is in Unattached.SNK and CTVPD enters AttachWait.SRC
+ * b. CTVPD in AttachWait.SRC detects that pull down on CC persists
+ * for tCCDebounce and enters Try.SNK
+ * c. CTVPD disables Rp termination advertising Default USB Power on
+ * the Host-side port’s CC
+ * d. CTVPD enables Rd on the Host-side port’s CC
+ */
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SRC);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == TRY_SNK);
+ TEST_ASSERT(check_host_ra_rd());
+
+ /* 5. DRP in dead battery condition remains in Unattached.SNK */
+
+ /*
+ * 6. CTVPD transitions from Try.SNK to Attached.SRC through
+ * TryWait.SRC
+ *
+ * a. CTVPD didn’t detect the CC pull-up of the DRP for
+ * tTryDebounce after tDRPTry and enters TryWait.SRC
+ * b. CTVPD disables Rd on the Host-side port’s CC
+ * c. CTVPD enables Rp termination advertising Default USB Power on
+ * the Host-side port’s CC
+ * d. CTVPD detects the CC pull-down of the DRP for tTryCCDebounce
+ * and enters Attached.SRC
+ * e. CTVPD connects VBUS from the Charge-Through side to the Host
+ * side
+ */
+ wait_for_state_change(port, PD_T_TRY_CC_DEBOUNCE +
+ PD_T_DRP_TRY + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == TRY_WAIT_SRC);
+ TEST_ASSERT(check_host_rpusb());
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACHED_SRC);
+ TEST_ASSERT(mock_get_vbus_pass_en());
+
+ /*
+ * 7. DRP transitions from Unattached.SNK to Attached.SNK through
+ * AttachWait.SNK
+ *
+ * a. DRP in Unattached.SNK detects the CC pull-up of CTVPD which is
+ * in Attached.SRC and DRP enters AttachWait.SNK
+ * b. DRP in AttachWait.SNK detects that pull up on CC persists for
+ * tCCDebounce, VBUS present and enters Attached.SNK
+ */
+
+ /*
+ * 8. While the devices are all in their respective attached states:
+ * a. CTVPD monitors the Host-side port’s CC pin for device attach
+ * and when detected, enters Unattached.SNK
+ * b. CTVPD monitors VBUS for Power Source detach and when detected,
+ * enters Unattached.SNK
+ * c. Power Source monitors CC for CTVPD detach and when detected,
+ * enters Unattached.SRC
+ * d. DRP monitors VBUS for CTVPD detach and when detected, enters
+ * Unattached.SNK
+ * e. Additionally, the DRP may query the identity of the cable via
+ * USB PD on SOP’ when it has sufficient battery power and when
+ * a Charge-Through VPD is identified enters TryWait.SRC if
+ * implemented, or enters Unattached.SRC if TryWait.SRC is not
+ * supported
+ */
+ TEST_ASSERT(ct_connect_source(CC2, VBUS_0));
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ return EC_SUCCESS;
+}
+
+static int test_ctvpd_behavior_case6(void)
+{
+ int port = PORT0;
+
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+ TEST_ASSERT(ct_disconnect_source());
+ TEST_ASSERT(ct_disconnect_sink());
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * CASE 6: The following describes the behavior when a DRP is connected
+ * to a Charge-Through VCONN-Powered USB Device
+ * (abbreviated CTVPD) and a Sink is attached to the
+ * Charge-Through port on the CTVPD.
+ */
+
+ /*
+ * 1. DRP, CTVPD and Sink are all in the unattached state
+ *
+ * a. DRP alternates between Unattached.SRC and Unattached.SNK
+ * b. CTVPD has applied Rd on its Charge-Through port’s CC1 and CC2
+ * pins and Rd on the Host-side port’s CC pin
+ */
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+ TEST_ASSERT(check_ct_ccs_rd());
+ TEST_ASSERT(check_host_ra_rd());
+
+ /*
+ * 2. DRP transitions from Unattached.SRC to AttachWait.SRC to
+ * Attached.SRC
+ *
+ * a. DRP in Unattached.SRC detects the CC pull-down of CTVPD which
+ * is in Unattached.SNK and DRP enters AttachWait.SRC
+ * b. DRP in AttachWait.SRC detects that pull down on CC persists
+ * for tCCDebounce, enters Attached.SRC and turns on VBUS and
+ * VCONN
+ */
+ host_connect_source(VBUS_5);
+ mock_set_vconn(VCONN_3);
+
+ /*
+ * 3. CTVPD transitions from Unattached.SNK to Attached.SNK through
+ * AttachWait.SNK.
+ *
+ * a. CTVPD detects the host-side CC pull-up of the DRP and CTVPD
+ * enters AttachWait.SNK
+ * b. CTVPD in AttachWait.SNK detects that pull up on the Host-side
+ * port’s CC persists for tCCDebounce, VCONN present and enters
+ * Attached.SNK
+ * c. CTVPD present a high-impedance to ground (above zOPEN) on its
+ * Charge-Through port’s CC1 and CC2 pins
+ */
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACHED_SNK);
+ TEST_ASSERT(check_ct_ccs_hz());
+
+ /*
+ * 4. While DRP and CTVPD are in their respective attached states, DRP
+ * discovers the ChargeThrough CTVPD and transitions to
+ * CTUnattached.SNK
+ *
+ * a. DRP (as Source) queries the device identity via USB PD
+ * (Discover Identity Command) on SOP’.
+ * b. CTVPD responds on SOP’, advertising that it is a
+ * Charge-Through VCONN-Powered USB Device
+ * c. DRP (as Source) removes VBUS
+ * d. DRP (as Source) changes its Rp to a Rd
+ * e. DRP (as Sink) continues to provide VCONN and enters
+ * CTUnattached.SNK
+ */
+
+ host_disconnect_source();
+ host_connect_sink(SRC_CON_DEF);
+
+ /*
+ * 5. CTVPD transitions to CTUnattached.VPD
+ *
+ * a. CTVPD detects VBUS removal, VCONN presence, the low Host-side
+ * CC pin and enters CTUnattached.VPD
+ * b. CTVPD changes its host-side Rd to a Rp termination advertising
+ * 3.0 A
+ * c. CTVPD isolates itself from VBUS
+ * d. CTVPD apply Rd on its Charge-Through port’s CC1 and CC2 pins
+ */
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_VPD);
+ TEST_ASSERT(check_host_rp3a0());
+ TEST_ASSERT(mock_get_vbus_pass_en() == 0);
+ TEST_ASSERT(check_ct_ccs_rd());
+
+ /*
+ * 6. CTVPD alternates between CTUnattached.VPD and
+ * CTUnattached.Unsupported
+ *
+ * a. CTVPD detects SRC.open on its Charge-Through CC pins and
+ * starts alternating between CTUnattached.VPD and
+ * CTUnattached.Unsupported
+ */
+ wait_for_state_change(port, PD_T_DRP_SNK + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_UNSUPPORTED);
+
+ wait_for_state_change(port, PD_T_DRP_SNK + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_VPD);
+
+ wait_for_state_change(port, PD_T_DRP_SNK + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_UNSUPPORTED);
+
+ /*
+ * 7. CTVPD transitions from CTUnattached.Unsupported to CTTry.SNK
+ * through CTAttachWait.Unsupported
+ *
+ * a. CTVPD in CTUnattached.Unsupported detects the CC pull-down of
+ * the Sink which is in Unattached.SNK and CTVPD enters
+ * CTAttachWait.Unsupported
+ * b. CTVPD in CTAttachWait.Unsupported detects that pull down on CC
+ * persists for tCCDebounce and enters CTTry.SNK
+ * c. CTVPD disables Rp termination advertising Default USB Power on
+ * the ChargeThrough port’s CC pins
+ * d. CTVPD enables Rd on the Charge-Through port’s CC pins
+ */
+ TEST_ASSERT(ct_connect_sink(CC1, SRC_CON_DEF));
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTATTACH_WAIT_UNSUPPORTED);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTTRY_SNK);
+ TEST_ASSERT(check_ct_ccs_rd());
+
+ /*
+ * 8. CTVPD transitions from CTTry.SNK to CTAttached.Unsupported
+ *
+ * a. CTVPD didn’t detect the CC pull-up of the potential Source
+ * for tDRPTryWait after tDRPTry and enters
+ * CTAttached.Unsupported
+ */
+
+ wait_for_state_change(port, PD_T_DRP_TRY + PD_T_TRY_WAIT + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTATTACHED_UNSUPPORTED);
+
+ /*
+ * 9. While the CTVPD in CTAttached.Unsupported state, the DRP in
+ * CTUnattached.SNK state and the Sink in Unattached.SNK state:
+ *
+ * a. CTVPD disables the Rd termination on the Charge-Through
+ * port’s CC pins and applies Rp termination advertising
+ * Default USB Power
+ * b. CTVPD exposes a USB Billboard Device Class to the DRP
+ * indicating that it is connected to an unsupported device on
+ * its Charge Through port
+ * c. CTVPD monitors Charge-Though CC pins for Sink detach and when
+ * detected, enters CTUnattached.VPD
+ * d. CTVPD monitors VCONN for Host detach and when detected, enters
+ * Unattached.SNK
+ * e. DRP monitors CC for CTVPD detach for tVPDDetach and when
+ * detected, enters Unattached.SNK
+ * f. DRP monitors VBUS for CTVPD Charge-Through source attach and,
+ * when detected, enters CTAttached.SNK
+ */
+
+ TEST_ASSERT(check_ct_ccs_cc1_rpusb());
+ TEST_ASSERT(mock_get_present_billboard() == BB_SNK);
+
+ TEST_ASSERT(ct_disconnect_sink());
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_VPD);
+
+ return EC_SUCCESS;
+}
+#endif
+
+void run_test(void)
+{
+ test_reset();
+
+ init_port(PORT0);
+
+ /* VPD and CTVPD tests */
+ RUN_TEST(test_vpd_host_src_detection);
+ RUN_TEST(test_vpd_host_src_detection_vbus);
+ RUN_TEST(test_vpd_host_src_detection_vconn);
+ RUN_TEST(test_vpd_host_src_detection_message_reception);
+
+ /* CTVPD only tests */
+#if defined(TEST_USB_TYPEC_CTVPD)
+ /* DRP to VCONN-Powered USB Device (CTVPD) Behavior Tests */
+ RUN_TEST(test_ctvpd_behavior_case1);
+ RUN_TEST(test_ctvpd_behavior_case2);
+ RUN_TEST(test_ctvpd_behavior_case3);
+ RUN_TEST(test_ctvpd_behavior_case4);
+ RUN_TEST(test_ctvpd_behavior_case5);
+ RUN_TEST(test_ctvpd_behavior_case6);
+#endif
+ test_print_result();
+}
+