/* Copyright 2020 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "hooks.h" #include "mock/tcpci_i2c_mock.h" #include "mock/usb_mux_mock.h" #include "task.h" #include "tcpm/tcpci.h" #include "test_util.h" #include "timer.h" #include "usb_tcpmv2_compliance.h" #include "usb_tc_sm.h" uint32_t rdo = RDO_FIXED(1, 500, 500, 0); uint32_t pdo = PDO_FIXED(5000, 3000, PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | PDO_FIXED_COMM_CAP); const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_MAX_COUNT] = { { .bus_type = EC_BUS_TYPE_I2C, .i2c_info = { .port = I2C_PORT_HOST_TCPC, .addr_flags = MOCK_TCPCI_I2C_ADDR_FLAGS, }, .drv = &tcpci_tcpm_drv, .flags = TCPC_FLAGS_TCPCI_REV2_0, }, }; const struct usb_mux usb_muxes[CONFIG_USB_PD_PORT_MAX_COUNT] = { { .driver = &mock_usb_mux_driver, } }; void mock_set_cc(enum mock_connect_result cr, enum mock_cc_state cc1, enum mock_cc_state cc2) { mock_tcpci_set_reg(TCPC_REG_CC_STATUS, TCPC_REG_CC_STATUS_SET(cr, cc1, cc2)); } void mock_set_role(int drp, enum tcpc_rp_value rp, enum tcpc_cc_pull cc1, enum tcpc_cc_pull cc2) { mock_tcpci_set_reg(TCPC_REG_ROLE_CTRL, TCPC_REG_ROLE_CTRL_SET(drp, rp, cc1, cc2)); } static int mock_alert_count; void mock_set_alert(int alert) { mock_tcpci_set_reg_bits(TCPC_REG_ALERT, alert); mock_alert_count = 1; schedule_deferred_pd_interrupt(PORT0); } uint16_t tcpc_get_alert_status(void) { ccprints("mock_alert_count %d", mock_alert_count); if (mock_alert_count > 0) { mock_alert_count--; return PD_STATUS_TCPC_ALERT_0; } return 0; } bool vboot_allow_usb_pd(void) { return 1; } int pd_check_vconn_swap(int port) { return 1; } void board_reset_pd_mcu(void) {} /***************************************************************************** * Partner utility functions */ static enum pd_data_role partner_data_role; void partner_set_data_role(enum pd_data_role data_role) { partner_data_role = data_role; } enum pd_data_role partner_get_data_role(void) { return partner_data_role; } static enum pd_power_role partner_power_role; void partner_set_power_role(enum pd_power_role power_role) { partner_power_role = power_role; } enum pd_power_role partner_get_power_role(void) { return partner_power_role; } static enum pd_rev_type partner_pd_rev; void partner_set_pd_rev(enum pd_rev_type pd_rev) { partner_pd_rev = pd_rev; } enum pd_rev_type partner_get_pd_rev(void) { return partner_pd_rev; } static int partner_tx_id[NUM_SOP_STAR_TYPES]; void partner_tx_msg_id_reset(int sop) { if (sop == TCPCI_MSG_SOP_ALL) for (sop = 0; sop < NUM_SOP_STAR_TYPES; ++sop) partner_tx_id[sop] = 0; else partner_tx_id[sop] = 0; } void partner_send_msg(enum tcpci_msg_type sop, uint16_t type, uint16_t cnt, uint16_t ext, uint32_t *payload) { uint16_t header; partner_tx_id[sop] &= 7; header = PD_HEADER(type, sop == TCPCI_MSG_SOP ? partner_get_power_role() : PD_PLUG_FROM_CABLE, partner_get_data_role(), partner_tx_id[sop], cnt, partner_get_pd_rev(), ext); mock_tcpci_receive(sop, header, payload); ++partner_tx_id[sop]; mock_set_alert(TCPC_REG_ALERT_RX_STATUS); } /***************************************************************************** * TCPCI clean power up */ int tcpci_startup(void) { /* Should be in low power mode before AP boots. */ TEST_EQ(mock_tcpci_get_reg(TCPC_REG_COMMAND), TCPC_REG_COMMAND_I2CIDLE, "%d"); task_wait_event(10 * SECOND); hook_notify(HOOK_CHIPSET_STARTUP); task_wait_event(5 * MSEC); hook_notify(HOOK_CHIPSET_RESUME); task_wait_event(10 * SECOND); /* Should be in low power mode and DRP auto-toggling with AP in S0. */ TEST_EQ((mock_tcpci_get_reg(TCPC_REG_ROLE_CTRL) & TCPC_REG_ROLE_CTRL_DRP_MASK), TCPC_REG_ROLE_CTRL_DRP_MASK, "%d"); /* TODO: check previous command was TCPC_REG_COMMAND_LOOK4CONNECTION */ TEST_EQ(mock_tcpci_get_reg(TCPC_REG_COMMAND), TCPC_REG_COMMAND_I2CIDLE, "%d"); /* TODO: this should be performed in TCPCI mock on startup but needs * more TCPCI functionality added before that can happen. So until * that time, if the FAULT register is set, send the alert here. */ if (mock_tcpci_get_reg(TCPC_REG_FAULT_STATUS)) mock_set_alert(TCPC_REG_ALERT_FAULT); return EC_SUCCESS; } /***************************************************************************** * PROC.PD.E1. Bring-up procedure */ int proc_pd_e1(enum pd_data_role data_role, enum proc_pd_e1_attach attach) { if (attach & INITIAL_ATTACH) { /* * a) The test starts in a disconnected state. */ mock_tcpci_set_reg(TCPC_REG_EXT_STATUS, TCPC_REG_EXT_STATUS_SAFE0V); mock_set_alert(TCPC_REG_ALERT_EXT_STATUS); task_wait_event(10 * SECOND); TEST_EQ(pd_get_data_role(I2C_PORT_HOST_TCPC), PD_ROLE_DISCONNECTED, "%d"); partner_set_data_role((data_role == PD_ROLE_UFP) ? PD_ROLE_DFP : PD_ROLE_UFP); partner_set_power_role((data_role == PD_ROLE_UFP) ? PD_ROLE_SOURCE : PD_ROLE_SINK); switch (partner_get_power_role()) { case PD_ROLE_SOURCE: /* * b) The tester applies Rp (PD3=1.5A, PD2=3A) and * waits for the UUT attachment. */ mock_set_cc(MOCK_CC_DUT_IS_SNK, MOCK_CC_SNK_OPEN, (partner_get_pd_rev() == PD_REV30 ? MOCK_CC_SNK_RP_1_5 : MOCK_CC_SNK_RP_3_0)); mock_set_alert(TCPC_REG_ALERT_CC_STATUS); task_wait_event(5 * MSEC); /* * c) If Ra is detected, the tester applies Vconn. */ /* * d) The tester applies Vbus and waits 50 ms. */ mock_tcpci_set_reg_bits(TCPC_REG_POWER_STATUS, TCPC_REG_POWER_STATUS_VBUS_PRES); mock_tcpci_clr_reg_bits(TCPC_REG_EXT_STATUS, TCPC_REG_EXT_STATUS_SAFE0V); mock_set_alert(TCPC_REG_ALERT_EXT_STATUS | TCPC_REG_ALERT_POWER_STATUS); task_wait_event(50 * MSEC); break; case PD_ROLE_SINK: /* * b) The tester applies Rd and waits for Vbus for * tNoResponse max (5.5 s). */ mock_set_cc(MOCK_CC_DUT_IS_SRC, MOCK_CC_SRC_OPEN, MOCK_CC_SRC_RD); mock_set_alert(TCPC_REG_ALERT_CC_STATUS); break; } } if (attach & ALREADY_ATTACHED) { switch (partner_get_power_role()) { case PD_ROLE_SOURCE: /* * e) The tester transmits Source Capabilities until * reception of GoodCrc for tNoResponse max (5.5s). * The Source Capabilities includes Fixed 5V 3A PDO. */ task_wait_event(1 * MSEC); partner_send_msg(TCPCI_MSG_SOP, PD_DATA_SOURCE_CAP, 1, 0, &pdo); /* * f) The tester waits for the Request from the UUT for * tSenderResponse max (30 ms). */ TEST_EQ(verify_tcpci_transmit(TCPCI_MSG_SOP, 0, PD_DATA_REQUEST), EC_SUCCESS, "%d"); mock_set_alert(TCPC_REG_ALERT_TX_SUCCESS); /* * g) The tester sends Accept, and when Vbus is stable * at the target voltage, sends PS_RDY. */ partner_send_msg(TCPCI_MSG_SOP, PD_CTRL_ACCEPT, 0, 0, NULL); task_wait_event(10 * MSEC); partner_send_msg(TCPCI_MSG_SOP, PD_CTRL_PS_RDY, 0, 0, NULL); task_wait_event(1 * MSEC); TEST_EQ(tc_is_attached_snk(PORT0), true, "%d"); break; case PD_ROLE_SINK: /* * c) The tester waits Source Capabilities for for * tNoResponse max (5.5 s). */ TEST_EQ(verify_tcpci_transmit(TCPCI_MSG_SOP, 0, PD_DATA_SOURCE_CAP), EC_SUCCESS, "%d"); /* * d) The tester replies GoodCrc on reception of the * Source Capabilities. */ mock_set_alert(TCPC_REG_ALERT_TX_SUCCESS); task_wait_event(10 * MSEC); /* * e) The tester requests 5V 0.5A. */ partner_send_msg(TCPCI_MSG_SOP, PD_DATA_REQUEST, 1, 0, &rdo); TEST_EQ(verify_tcpci_transmit(TCPCI_MSG_SOP, PD_CTRL_ACCEPT, 0), EC_SUCCESS, "%d"); mock_set_alert(TCPC_REG_ALERT_TX_SUCCESS); /* * f) The tester waits PS_RDY for tPSSourceOn max * (480 ms). */ TEST_EQ(verify_tcpci_transmit(TCPCI_MSG_SOP, PD_CTRL_PS_RDY, 0), EC_SUCCESS, "%d"); mock_set_alert(TCPC_REG_ALERT_TX_SUCCESS); TEST_EQ(tc_is_attached_src(PORT0), true, "%d"); break; } TEST_EQ(pd_get_data_role(I2C_PORT_HOST_TCPC), data_role, "%d"); } return EC_SUCCESS; } /***************************************************************************** * PROC.PD.E3. Wait to Start AMS for DFP(Source) UUT */ int proc_pd_e3(void) { /* * Make sure we are idle. Reject everything that is pending */ TEST_EQ(handle_attach_expected_msgs(PD_ROLE_DFP), EC_SUCCESS, "%d"); /* * PROC.PD.E3. Wait to Start AMS for DFP(Source) UUT: * a) The Tester keeps monitoring the Rp value and if the UUT doesn't * set the value to SinkTXOK if it doesn't have anything to send in 1s, * the test fails. During this period, the Tester replies any message * sent from the UUT with a proper response. */ TEST_EQ(tc_is_attached_src(PORT0), true, "%d"); TEST_EQ(TCPC_REG_ROLE_CTRL_RP(mock_tcpci_get_reg(TCPC_REG_ROLE_CTRL)), SINK_TX_OK, "%d"); task_wait_event(10 * SECOND); return EC_SUCCESS; } /***************************************************************************** * handle_attach_expected_msgs * * Depending on the data role, the DUT will send a sequence of messages on * attach. Most of these can be rejected. */ int handle_attach_expected_msgs(enum pd_data_role data_role) { int rv; int found_index; struct possible_tx possible[4]; if (data_role == PD_ROLE_DFP) { possible[0].tx_type = TCPCI_MSG_SOP; possible[0].ctrl_msg = PD_CTRL_GET_SOURCE_CAP; possible[0].data_msg = 0; possible[1].tx_type = TCPCI_MSG_SOP; possible[1].ctrl_msg = PD_CTRL_GET_SINK_CAP; possible[1].data_msg = 0; possible[2].tx_type = TCPCI_MSG_SOP_PRIME; possible[2].ctrl_msg = 0; possible[2].data_msg = PD_DATA_VENDOR_DEF; possible[3].tx_type = TCPCI_MSG_SOP; possible[3].ctrl_msg = 0; possible[3].data_msg = PD_DATA_VENDOR_DEF; do { rv = verify_tcpci_possible_tx(possible, 4, &found_index, NULL, 0, NULL, -1); TEST_NE(rv, EC_ERROR_UNKNOWN, "%d"); if (rv == EC_ERROR_TIMEOUT) break; mock_set_alert(TCPC_REG_ALERT_TX_SUCCESS); task_wait_event(10 * MSEC); switch (found_index) { case 0: /* TCPCI_MSG_SOP PD_CTRL_GET_SOURCE_CAP */ partner_send_msg(TCPCI_MSG_SOP, PD_DATA_SOURCE_CAP, 1, 0, &pdo); break; case 1: /* TCPCI_MSG_SOP PD_CTRL_GET_SINK_CAP */ partner_send_msg(TCPCI_MSG_SOP, PD_DATA_SINK_CAP, 1, 0, &pdo); break; case 2: /* TCPCI_MSG_SOP_PRIME PD_DATA_VENDOR_DEF */ partner_send_msg(TCPCI_MSG_SOP_PRIME, PD_CTRL_NOT_SUPPORTED, 0, 0, NULL); break; case 3: /* TCPCI_MSG_SOP PD_DATA_VENDOR_DEF */ partner_send_msg(TCPCI_MSG_SOP, PD_CTRL_NOT_SUPPORTED, 0, 0, NULL); break; default: TEST_ASSERT(0); break; } } while (rv != EC_ERROR_TIMEOUT); } else if (data_role == PD_ROLE_UFP) { int vcs = 0; possible[0].tx_type = TCPCI_MSG_SOP; possible[0].ctrl_msg = PD_CTRL_GET_SINK_CAP; possible[0].data_msg = 0; possible[1].tx_type = TCPCI_MSG_SOP; possible[1].ctrl_msg = PD_CTRL_DR_SWAP; possible[1].data_msg = 0; possible[2].tx_type = TCPCI_MSG_SOP; possible[2].ctrl_msg = PD_CTRL_PR_SWAP; possible[2].data_msg = 0; possible[3].tx_type = TCPCI_MSG_SOP; possible[3].ctrl_msg = PD_CTRL_VCONN_SWAP; possible[3].data_msg = 0; do { rv = verify_tcpci_possible_tx(possible, 4, &found_index, NULL, 0, NULL, -1); TEST_NE(rv, EC_ERROR_UNKNOWN, "%d"); if (rv == EC_ERROR_TIMEOUT) break; mock_set_alert(TCPC_REG_ALERT_TX_SUCCESS); task_wait_event(10 * MSEC); switch (found_index) { case 0: /* TCPCI_MSG_SOP PD_CTRL_GET_SINK_CAP */ partner_send_msg(TCPCI_MSG_SOP, PD_DATA_SINK_CAP, 1, 0, &pdo); break; case 1: /* TCPCI_MSG_SOP PD_CTRL_DR_SWAP */ partner_send_msg(TCPCI_MSG_SOP, PD_CTRL_REJECT, 0, 0, NULL); break; case 2: /* TCPCI_MSG_SOP PD_CTRL_PR_SWAP */ partner_send_msg(TCPCI_MSG_SOP, PD_CTRL_REJECT, 0, 0, NULL); break; case 3: /* TCPCI_MSG_SOP PD_CTRL_VCONN_SWAP */ TEST_LT(vcs++, 4, "%d"); partner_send_msg(TCPCI_MSG_SOP, PD_CTRL_REJECT, 0, 0, NULL); break; default: TEST_ASSERT(0); break; } } while (rv != EC_ERROR_TIMEOUT); } task_wait_event(1 * SECOND); return EC_SUCCESS; }