405 lines
10 KiB
C
405 lines
10 KiB
C
/* 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();
|
||
}
|