| /* 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 "tcpci.h" |
| #include "test_util.h" |
| #include "timer.h" |
| #include "usb_mux.h" |
| #include "usb_tc_sm.h" |
| #include "usb_prl_sm.h" |
| |
| #define PORT0 0 |
| |
| enum mock_cc_state { |
| MOCK_CC_SRC_OPEN = 0, |
| MOCK_CC_SNK_OPEN = 0, |
| MOCK_CC_SRC_RA = 1, |
| MOCK_CC_SNK_RP_DEF = 1, |
| MOCK_CC_SRC_RD = 2, |
| MOCK_CC_SNK_RP_1_5 = 2, |
| MOCK_CC_SNK_RP_3_0 = 3, |
| }; |
| enum mock_connect_result { |
| MOCK_CC_WE_ARE_SRC = 0, |
| MOCK_CC_WE_ARE_SNK = 1, |
| }; |
| |
| __maybe_unused static 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)); |
| } |
| |
| __maybe_unused static 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; |
| |
| __maybe_unused static void mock_set_alert(int alert) |
| { |
| mock_tcpci_set_reg(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; |
| } |
| |
| static int rx_id; |
| |
| const struct svdm_response svdm_rsp = { |
| .identity = NULL, |
| .svids = NULL, |
| .modes = NULL, |
| }; |
| |
| bool vboot_allow_usb_pd(void) |
| { |
| return 1; |
| } |
| |
| int pd_check_vconn_swap(int port) |
| { |
| return 1; |
| } |
| |
| void board_reset_pd_mcu(void) {} |
| |
| 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, |
| } |
| }; |
| |
| __maybe_unused static int test_connect_as_nonpd_sink(void) |
| { |
| task_wait_event(10 * SECOND); |
| |
| /* Simulate a non-PD power supply being plugged in. */ |
| mock_set_cc(MOCK_CC_WE_ARE_SNK, MOCK_CC_SNK_OPEN, MOCK_CC_SNK_RP_3_0); |
| mock_set_alert(TCPC_REG_ALERT_CC_STATUS); |
| |
| task_wait_event(50 * MSEC); |
| |
| mock_tcpci_set_reg(TCPC_REG_POWER_STATUS, |
| TCPC_REG_POWER_STATUS_VBUS_PRES); |
| mock_set_alert(TCPC_REG_ALERT_POWER_STATUS); |
| |
| task_wait_event(10 * SECOND); |
| TEST_EQ(tc_is_attached_snk(PORT0), true, "%d"); |
| |
| return EC_SUCCESS; |
| } |
| |
| __maybe_unused static int test_startup_and_resume(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"); |
| |
| return EC_SUCCESS; |
| } |
| |
| |
| __maybe_unused static int test_connect_as_pd3_source(void) |
| { |
| uint32_t rdo = RDO_FIXED(1, 500, 500, 0); |
| uint32_t pdo = PDO_FIXED(5000, 500, |
| PDO_FIXED_DUAL_ROLE | |
| PDO_FIXED_DATA_SWAP | |
| PDO_FIXED_COMM_CAP); |
| |
| /* DRP auto-toggling with AP in S0, source enabled. */ |
| TEST_EQ(test_startup_and_resume(), EC_SUCCESS, "%d"); |
| |
| /* |
| * PROC.PD.E1. Bring-up procedure for DFP(Source) UUT: |
| * 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); |
| |
| /* |
| * b) The Tester applies Rd and waits for Vbus for tNoResponse max. |
| */ |
| mock_set_cc(MOCK_CC_WE_ARE_SRC, MOCK_CC_SRC_OPEN, MOCK_CC_SRC_RD); |
| mock_set_alert(TCPC_REG_ALERT_CC_STATUS); |
| |
| /* |
| * c) The Tester waits for Source_Capabilities for tNoResponse max. |
| */ |
| TEST_EQ(verify_tcpci_transmit(TCPC_TX_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. |
| */ |
| mock_tcpci_receive(PD_MSG_SOP, |
| PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK, |
| PD_ROLE_UFP, rx_id, |
| 1, PD_REV30, 0), |
| &rdo); |
| rx_id++; |
| mock_set_alert(TCPC_REG_ALERT_RX_STATUS); |
| TEST_EQ(verify_tcpci_transmit(TCPC_TX_SOP, PD_CTRL_ACCEPT, 0), |
| EC_SUCCESS, "%d"); |
| mock_set_alert(TCPC_REG_ALERT_TX_SUCCESS); |
| /* |
| * f) The Tester waits for PS_RDY for tPSSourceOn max. |
| */ |
| TEST_EQ(verify_tcpci_transmit(TCPC_TX_SOP, PD_CTRL_PS_RDY, 0), |
| EC_SUCCESS, "%d"); |
| mock_set_alert(TCPC_REG_ALERT_TX_SUCCESS); |
| |
| /* |
| * 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(verify_tcpci_transmit(TCPC_TX_SOP_PRIME, 0, PD_DATA_VENDOR_DEF), |
| EC_SUCCESS, "%d"); |
| mock_set_alert(TCPC_REG_ALERT_TX_SUCCESS); |
| task_wait_event(10 * MSEC); |
| mock_tcpci_receive(PD_MSG_SOP_PRIME, |
| PD_HEADER(PD_CTRL_NOT_SUPPORTED, PD_PLUG_FROM_CABLE, |
| PD_ROLE_UFP, rx_id, |
| 0, PD_REV30, 0), |
| NULL); |
| rx_id++; |
| mock_set_alert(TCPC_REG_ALERT_RX_STATUS); |
| |
| TEST_EQ(verify_tcpci_transmit(TCPC_TX_SOP, 0, PD_DATA_VENDOR_DEF), |
| EC_SUCCESS, "%d"); |
| mock_set_alert(TCPC_REG_ALERT_TX_SUCCESS); |
| task_wait_event(10 * MSEC); |
| mock_tcpci_receive(PD_MSG_SOP, |
| PD_HEADER(PD_CTRL_NOT_SUPPORTED, PD_ROLE_SINK, |
| PD_ROLE_UFP, rx_id, |
| 0, PD_REV30, 0), |
| NULL); |
| rx_id++; |
| mock_set_alert(TCPC_REG_ALERT_RX_STATUS); |
| |
| TEST_EQ(verify_tcpci_transmit(TCPC_TX_SOP, PD_CTRL_GET_SOURCE_CAP, 0), |
| EC_SUCCESS, "%d"); |
| mock_set_alert(TCPC_REG_ALERT_TX_SUCCESS); |
| task_wait_event(10 * MSEC); |
| mock_tcpci_receive(PD_MSG_SOP, |
| PD_HEADER(PD_DATA_SOURCE_CAP, PD_ROLE_SINK, |
| PD_ROLE_UFP, rx_id, |
| 1, PD_REV30, 0), |
| &pdo); |
| rx_id++; |
| mock_set_alert(TCPC_REG_ALERT_RX_STATUS); |
| |
| TEST_EQ(verify_tcpci_transmit(TCPC_TX_SOP, PD_CTRL_GET_SINK_CAP, 0), |
| EC_SUCCESS, "%d"); |
| mock_set_alert(TCPC_REG_ALERT_TX_SUCCESS); |
| task_wait_event(10 * MSEC); |
| mock_tcpci_receive(PD_MSG_SOP, |
| PD_HEADER(PD_DATA_SINK_CAP, PD_ROLE_SINK, |
| PD_ROLE_UFP, rx_id, |
| 1, PD_REV30, 0), |
| &pdo); |
| rx_id++; |
| mock_set_alert(TCPC_REG_ALERT_RX_STATUS); |
| |
| task_wait_event(1 * SECOND); |
| 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; |
| } |
| |
| __maybe_unused static int test_retry_count_sop(void) |
| { |
| /* DRP auto-toggling with AP in S0, source enabled. */ |
| TEST_EQ(test_startup_and_resume(), EC_SUCCESS, "%d"); |
| |
| /* |
| * 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); |
| |
| /* |
| * The Tester applies Rd and waits for Vbus for tNoResponse max. |
| */ |
| mock_set_cc(MOCK_CC_WE_ARE_SRC, MOCK_CC_SRC_OPEN, MOCK_CC_SRC_RD); |
| mock_set_alert(TCPC_REG_ALERT_CC_STATUS); |
| |
| /* |
| * The Tester waits for Source_Capabilities for tNoResponse max. |
| * |
| * Source Caps is SOP message which should be retried at TCPC layer |
| */ |
| TEST_EQ(verify_tcpci_tx_retry_count(TCPC_TX_SOP, CONFIG_PD_RETRY_COUNT), |
| EC_SUCCESS, "%d"); |
| return EC_SUCCESS; |
| } |
| |
| __maybe_unused static int test_retry_count_hard_reset(void) |
| { |
| /* DRP auto-toggling with AP in S0, source enabled. */ |
| TEST_EQ(test_startup_and_resume(), EC_SUCCESS, "%d"); |
| |
| /* |
| * 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); |
| |
| /* |
| * The Tester applies Rd and waits for Vbus for tNoResponse max. |
| */ |
| mock_set_cc(MOCK_CC_WE_ARE_SRC, MOCK_CC_SRC_OPEN, MOCK_CC_SRC_RD); |
| mock_set_alert(TCPC_REG_ALERT_CC_STATUS); |
| |
| /* |
| * The Tester waits for Source_Capabilities for tNoResponse max. |
| */ |
| TEST_EQ(verify_tcpci_transmit(TCPC_TX_SOP, 0, PD_DATA_SOURCE_CAP), |
| EC_SUCCESS, "%d"); |
| /* |
| * The Tester replies GoodCrc on reception of the Source_Capabilities. |
| */ |
| mock_set_alert(TCPC_REG_ALERT_TX_SUCCESS); |
| task_wait_event(10 * MSEC); |
| |
| /* |
| * Now that PRL is running since we are connected, we can send a hard |
| * reset. |
| */ |
| |
| /* Request that DUT send hard reset */ |
| prl_execute_hard_reset(PORT0); |
| |
| /* The retry count for hard resets should be 0 */ |
| TEST_EQ(verify_tcpci_tx_retry_count(TCPC_TX_HARD_RESET, 0), |
| EC_SUCCESS, "%d"); |
| |
| return EC_SUCCESS; |
| } |
| |
| __maybe_unused static int test_pd3_source_send_soft_reset(void) |
| { |
| /* |
| * TD.PD.SRC3.E26.Soft_Reset sent regardless of Rp value |
| * a) Run PROC.PD.E1 Bring-up according to the UUT role. |
| * b) The Tester waits until it can start an AMS (Run PROC.PD.E3)... |
| */ |
| TEST_EQ(test_connect_as_pd3_source(), EC_SUCCESS, "%d"); |
| |
| /* |
| * ...and sends a Get_Source_Cap message to the UUT. |
| */ |
| mock_tcpci_receive(PD_MSG_SOP, |
| PD_HEADER(PD_CTRL_GET_SOURCE_CAP, PD_ROLE_SINK, |
| PD_ROLE_UFP, rx_id, |
| 0, PD_REV30, 0), |
| NULL); |
| rx_id++; |
| mock_set_alert(TCPC_REG_ALERT_RX_STATUS); |
| |
| /* |
| * c) Upon receipt of the Source_Capabilities Message, the Tester |
| * doesn’t reply with GoodCRC. |
| */ |
| TEST_EQ(verify_tcpci_transmit(TCPC_TX_SOP, 0, PD_DATA_SOURCE_CAP), |
| EC_SUCCESS, "%d"); |
| mock_set_alert(TCPC_REG_ALERT_TX_FAILED); |
| |
| /* |
| * d) The Tester verifies that a Soft_Reset message is sent by the UUT |
| * within tReceive max (1.1 ms) + tSoftReset max (15 ms). |
| */ |
| TEST_EQ(verify_tcpci_tx_timeout( |
| TCPC_TX_SOP, PD_CTRL_SOFT_RESET, 0, 15 * MSEC), |
| EC_SUCCESS, "%d"); |
| mock_set_alert(TCPC_REG_ALERT_TX_SUCCESS); |
| |
| return EC_SUCCESS; |
| } |
| |
| void before_test(void) |
| { |
| rx_id = 0; |
| |
| mock_usb_mux_reset(); |
| mock_tcpci_reset(); |
| |
| /* Restart the PD task and let it settle */ |
| task_set_event(TASK_ID_PD_C0, TASK_EVENT_RESET_DONE); |
| task_wait_event(SECOND); |
| } |
| |
| void run_test(int argc, char **argv) |
| { |
| test_reset(); |
| |
| RUN_TEST(test_connect_as_nonpd_sink); |
| RUN_TEST(test_startup_and_resume); |
| RUN_TEST(test_connect_as_pd3_source); |
| RUN_TEST(test_retry_count_sop); |
| RUN_TEST(test_retry_count_hard_reset); |
| RUN_TEST(test_pd3_source_send_soft_reset); |
| |
| test_print_result(); |
| } |