diff options
author | Sam Hurst <shurst@google.com> | 2018-09-13 09:27:08 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2019-04-08 22:30:19 -0700 |
commit | 0fe6147a9d8d9feef5049aa6c6c4a6ad30d12b26 (patch) | |
tree | 05d4509bcfe68a248ec3fa58168f3de2536c2d9c /test | |
parent | e097feb8b2db20cd2435a483517356defa222db1 (diff) | |
download | chrome-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')
-rw-r--r-- | test/build.mk | 14 | ||||
-rw-r--r-- | test/test_config.h | 87 | ||||
-rw-r--r-- | test/usb_pd_test_util.h | 2 | ||||
-rw-r--r-- | test/usb_prl.c | 1306 | ||||
-rw-r--r-- | test/usb_prl.tasklist | 19 | ||||
l--------- | test/usb_sm_framework_h0.tasklist | 1 | ||||
l--------- | test/usb_sm_framework_h1.tasklist | 1 | ||||
l--------- | test/usb_sm_framework_h2.tasklist | 1 | ||||
-rw-r--r-- | test/usb_sm_framework_h3.c | 1219 | ||||
-rw-r--r-- | test/usb_sm_framework_h3.tasklist | 18 | ||||
-rw-r--r-- | test/usb_typec_ctvpd.c | 1488 | ||||
-rw-r--r-- | test/usb_typec_ctvpd.tasklist | 18 | ||||
l--------- | test/usb_typec_vpd.tasklist | 1 | ||||
-rw-r--r-- | test/vpd_api.c | 586 | ||||
-rw-r--r-- | test/vpd_api.h | 333 |
15 files changed, 5094 insertions, 0 deletions
diff --git a/test/build.mk b/test/build.mk index 6900c97e3d..b5b6e8bbea 100644 --- a/test/build.mk +++ b/test/build.mk @@ -65,6 +65,13 @@ test-list-host += timer_dos test-list-host += usb_pd test-list-host += usb_pd_giveback test-list-host += usb_pd_rev30 +test-list-host += usb_sm_framework_h3 +test-list-host += usb_sm_framework_h2 +test-list-host += usb_sm_framework_h1 +test-list-host += usb_sm_framework_h0 +test-list-host += usb_typec_vpd +test-list-host += usb_typec_ctvpd +test-list-host += usb_prl test-list-host += utils test-list-host += utils_str test-list-host += vboot @@ -127,6 +134,13 @@ timer_dos-y=timer_dos.o usb_pd-y=usb_pd.o usb_pd_giveback-y=usb_pd.o usb_pd_rev30-y=usb_pd.o +usb_sm_framework_h3-y=usb_sm_framework_h3.o +usb_sm_framework_h2-y=usb_sm_framework_h3.o +usb_sm_framework_h1-y=usb_sm_framework_h3.o +usb_sm_framework_h0-y=usb_sm_framework_h3.o +usb_typec_vpd-y=usb_typec_ctvpd.o vpd_api.o +usb_typec_ctvpd-y=usb_typec_ctvpd.o vpd_api.o +usb_prl-y=usb_prl.o utils-y=utils.o utils_str-y=utils_str.o vboot-y=vboot.o diff --git a/test/test_config.h b/test/test_config.h index 17e7d6f44d..afb2350681 100644 --- a/test/test_config.h +++ b/test/test_config.h @@ -217,6 +217,93 @@ int ncp15wb_calculate_temp(uint16_t adc); #define CONFIG_ALS_LIGHTBAR_DIMMING 0 #endif +#if defined(TEST_USB_SM_FRAMEWORK_H3) +#define CONFIG_USB_PD_PORT_COUNT 1 +#undef CONFIG_USB_PRL_SM +#undef CONFIG_USB_PE_SM +#undef CONFIG_USB_TYPEC_SM +#undef CONFIG_SM_NESTING_NUM +#define CONFIG_SM_NESTING_NUM 3 +#define CONFIG_USB_SM_FRAMEWORK +#endif + +#if defined(TEST_USB_SM_FRAMEWORK_H2) +#define CONFIG_USB_PD_PORT_COUNT 1 +#undef CONFIG_USB_PRL_SM +#undef CONFIG_USB_PE_SM +#undef CONFIG_USB_TYPEC_SM +#undef CONFIG_SM_NESTING_NUM +#define CONFIG_SM_NESTING_NUM 2 +#define CONFIG_USB_SM_FRAMEWORK +#endif + +#if defined(TEST_USB_SM_FRAMEWORK_H1) +#define CONFIG_USB_PD_PORT_COUNT 1 +#undef CONFIG_USB_PRL_SM +#undef CONFIG_USB_PE_SM +#undef CONFIG_USB_TYPEC_SM +#undef CONFIG_SM_NESTING_NUM +#define CONFIG_SM_NESTING_NUM 1 +#define CONFIG_USB_SM_FRAMEWORK +#endif + +#if defined(TEST_USB_SM_FRAMEWORK_H0) +#define CONFIG_USB_PD_PORT_COUNT 1 +#undef CONFIG_USB_PRL_SM +#undef CONFIG_USB_PE_SM +#undef CONFIG_USB_TYPEC_SM +#undef CONFIG_SM_NESTING_NUM +#define CONFIG_SM_NESTING_NUM 0 +#define CONFIG_USB_SM_FRAMEWORK +#endif + +#if defined(TEST_USB_PRL) +#undef CONFIG_SM_NESTING_NUM +#define CONFIG_SM_NESTING_NUM 3 +#define CONFIG_USB_PD_PORT_COUNT 2 +#define CONFIG_USB_SM_FRAMEWORK +#undef CONFIG_USB_PE_SM +#undef CONFIG_USB_TYPEC_SM +#define CONFIG_USB_PRL_SM +#define CONFIG_USB_PD_TCPC +#define CONFIG_USB_PD_TCPM_STUB +#define CONFIG_USB_POWER_DELIVERY +#define CONFIG_SHA256 +#define CONFIG_SW_CRC +#endif + +#if defined(TEST_USB_TYPEC_VPD) || defined(TEST_USB_TYPEC_CTVPD) +#if defined(TEST_USB_TYPEC_VPD) +#define CONFIG_USB_TYPEC_VPD +#else +#define CONFIG_USB_TYPEC_CTVPD +#endif +#undef CONFIG_SM_NESTING_NUM +#define CONFIG_SM_NESTING_NUM 3 + +#define CONFIG_USB_PID 0x5036 +#define VPD_HW_VERSION 0x0001 +#define VPD_FW_VERSION 0x0001 +#define USB_BCD_DEVICE 0 + +/* Vbus impedance in milliohms */ +#define VPD_VBUS_IMPEDANCE 65 + +/* GND impedance in milliohms */ +#define VPD_GND_IMPEDANCE 33 + +#define CONFIG_USB_PD_PORT_COUNT 1 +#define CONFIG_USB_SM_FRAMEWORK +#define CONFIG_USB_PE_SM +#define CONFIG_USB_PRL_SM +#define CONFIG_USB_TYPEC_SM +#define CONFIG_USB_PD_TCPC +#define CONFIG_USB_PD_TCPM_STUB +#define CONFIG_USB_POWER_DELIVERY +#define CONFIG_SHA256 +#define CONFIG_SW_CRC +#endif /* TEST_USB_TYPEC_VPD or TEST_USB_TYPEC_CTVPD */ + #if defined(TEST_USB_PD) || defined(TEST_USB_PD_GIVEBACK) || \ defined(TEST_USB_PD_REV30) #define CONFIG_USB_POWER_DELIVERY diff --git a/test/usb_pd_test_util.h b/test/usb_pd_test_util.h index 561033d8fa..02fae22b41 100644 --- a/test/usb_pd_test_util.h +++ b/test/usb_pd_test_util.h @@ -13,6 +13,7 @@ void pd_test_rx_set_preamble(int port, int has_preamble); void pd_test_rx_msg_append_bits(int port, uint32_t bits, int nb); void pd_test_rx_msg_append_kcode(int port, uint8_t kcode); void pd_test_rx_msg_append_sop(int port); +void pd_test_rx_msg_append_sop_prime(int port); void pd_test_rx_msg_append_eop(int port); void pd_test_rx_msg_append_last_edge(int port); void pd_test_rx_msg_append_4b(int port, uint8_t val); @@ -23,6 +24,7 @@ void pd_simulate_rx(int port); /* Verify Tx message */ int pd_test_tx_msg_verify_kcode(int port, uint8_t kcode); int pd_test_tx_msg_verify_sop(int port); +int pd_test_tx_msg_verify_sop_prime(int port); int pd_test_tx_msg_verify_eop(int port); int pd_test_tx_msg_verify_4b5b(int port, uint8_t b4); int pd_test_tx_msg_verify_short(int port, uint16_t val); diff --git a/test/usb_prl.c b/test/usb_prl.c new file mode 100644 index 0000000000..ee15143986 --- /dev/null +++ b/test/usb_prl.c @@ -0,0 +1,1306 @@ +/* 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 Protocol Layer module. + */ +#include "common.h" +#include "crc.h" +#include "task.h" +#include "test_util.h" +#include "timer.h" +#include "tcpm.h" +#include "usb_emsg.h" +#include "usb_pe_sm.h" +#include "usb_pd.h" +#include "usb_pd_test_util.h" +#include "usb_prl_sm.h" +#include "util.h" + +#define PORT0 0 +#define PORT1 1 + +static uint32_t test_data[] = { + 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, + 0x10111213, 0x14151617, 0x1819a0b0, 0xc0d0e0f0, + 0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f, + 0x30313233, 0x34353637, 0x38393a3b, 0x3c3d3e3f, + 0x40414243, 0x44454647, 0x48494a4b, 0x4c4d4e4f, + 0x50515253, 0x54555657, 0x58595a5b, 0x5c5d5e5f, + 0x60616263, 0x64656667, 0x68696a6b, 0x6c6d6e6f, + 0x70717273, 0x74757677, 0x78797a7b, 0x7c7d7e7f, + 0x80818283, 0x84858687, 0x88898a8b, 0x8c8d8e8f, + 0x90919293, 0x94959697, 0x98999a9b, 0x9c9d9e9f, + 0xa0a1a2a3, 0xa4a5a6a7, 0xa8a9aaab, 0xacadaeaf, + 0xb0b1b2b3, 0xb4b5b6b7, 0xb8b9babb, 0xbcbdbebf, + 0xc0c1c2c3, 0xc4c5c6c7, 0xc8c9cacb, 0xcccdcecf, + 0xd0d1d2d3, 0xd4d5d6d7, 0xd8d9dadb, 0xdcdddedf, + 0xe0e1e2e3, 0xe4e5e6e7, 0xe8e9eaeb, 0xecedeeef, + 0xf0f1f2f3, 0xf4f5f6f7, 0xf8f9fafb, 0xfcfdfeff, + 0x11223344 +}; + +static struct pd_prl { + int rev; + int pd_enable; + int power_role; + int data_role; + int msg_tx_id; + int msg_rx_id; + + int mock_pe_message_sent; + int mock_pe_error; + int mock_pe_hard_reset_sent; + int mock_pe_got_hard_reset; + int mock_pe_pass_up_message; + int mock_got_soft_reset; +} pd_port[CONFIG_USB_PD_PORT_COUNT]; + +static void init_port(int port, int rev) +{ + pd_port[port].rev = rev; + pd_port[port].pd_enable = 0; + pd_port[port].power_role = PD_ROLE_SINK; + pd_port[port].data_role = PD_ROLE_UFP; + pd_port[port].msg_tx_id = 0; + pd_port[port].msg_rx_id = 0; + tcpm_init(port); + tcpm_set_polarity(port, 0); + tcpm_set_rx_enable(port, 0); +} + +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(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(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 cycle_through_state_machine(int port, uint32_t num, uint32_t time) +{ + int i; + + for (i = 0; i < num; i++) { + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(time); + } +} + +static int simulate_request_chunk(int port, enum pd_data_msg_type msg_type, + int chunk_num, int len) +{ + uint16_t header = PD_HEADER(msg_type, pd_port[port].power_role, + pd_port[port].data_role, + pd_port[port].msg_rx_id, + 1, pd_port[port].rev, 1); + uint32_t msg = PD_EXT_HEADER(chunk_num, 1, len); + + simulate_rx_msg(port, header, 1, (const uint32_t *)&msg); + task_wait_event(30 * MSEC); + + if (!verify_goodcrc(port, pd_port[port].data_role, + pd_port[port].msg_rx_id)) + return 0; + + return 1; +} + +static int simulate_receive_ctrl_msg(int port, enum pd_ctrl_msg_type msg_type) +{ + uint16_t header = PD_HEADER(msg_type, pd_port[port].power_role, + pd_port[port].data_role, pd_port[port].msg_rx_id, + 0, pd_port[port].rev, 0); + + simulate_rx_msg(port, header, 0, NULL); + task_wait_event(30 * MSEC); + + if (!verify_goodcrc(port, pd_port[port].data_role, + pd_port[port].msg_rx_id)) + return 0; + + return 1; +} + +static int verify_data_reception(int port, uint16_t header, int len) +{ + int i; + int cnt = (len + 3) & ~3; + + cycle_through_state_machine(port, 3, 10 * MSEC); + + if (pd_port[port].mock_pe_error >= 0) + return 0; + + if (!pd_port[port].mock_pe_pass_up_message) + return 0; + + if (emsg[port].header != header) + return 0; + + if (emsg[port].len != cnt) + return 0; + + for (i = 0; i < cnt; i++) { + if (i < len) { + if (emsg[port].buf[i] != + *((unsigned char *)test_data + i)) + return 0; + } else { + if (emsg[port].buf[i] != 0) + return 0; + } + } + + return 1; +} + +static int verify_chunk_data_reception(int port, uint16_t header, int len) +{ + int i; + uint8_t *td = (uint8_t *)test_data; + + if (pd_port[port].mock_got_soft_reset) + return 0; + + if (!pd_port[port].mock_pe_pass_up_message) + return 0; + + if (pd_port[port].mock_pe_error >= 0) + return 0; + + if (emsg[port].len != len) + return 0; + + for (i = 0; i < len; i++) + if (emsg[port].buf[i] != td[i]) + return 0; + + return 1; +} + +static int simulate_receive_data(int port, enum pd_data_msg_type msg_type, + int len) +{ + int i; + int nw = (len + 3) >> 2; + uint8_t td[28]; + uint16_t header = PD_HEADER(msg_type, pd_port[port].power_role, + pd_port[port].data_role, pd_port[port].msg_rx_id, + nw, pd_port[port].rev, 0); + + pd_port[port].mock_pe_error = -1; + pd_port[port].mock_pe_pass_up_message = 0; + emsg[port].header = 0; + emsg[port].len = 0; + memset(emsg[port].buf, 0, 260); + + for (i = 0; i < 28; i++) { + if (i < len) + td[i] = *((uint8_t *)test_data + i); + else + td[i] = 0; + } + + simulate_rx_msg(port, header, nw, (uint32_t *)td); + task_wait_event(30 * MSEC); + + if (!verify_goodcrc(port, pd_port[port].data_role, + pd_port[port].msg_rx_id)) + return 0; + + inc_rx_id(port); + + return verify_data_reception(port, header, len); +} + +static int simulate_receive_extended_data(int port, + enum pd_data_msg_type msg_type, int len) +{ + int i; + int j; + int byte_len; + int nw; + int dsize; + uint8_t td[28]; + int chunk_num = 0; + int data_offset = 0; + uint8_t *expected_data = (uint8_t *)test_data; + uint16_t header; + int req_timeout; + + pd_port[port].mock_pe_error = -1; + pd_port[port].mock_pe_pass_up_message = 0; + emsg[port].header = 0; + emsg[port].len = 0; + memset(emsg[port].buf, 0, 260); + + dsize = len; + + cycle_through_state_machine(port, 2, 40 * MSEC); + + for (j = 0; j < 10; j++) { + byte_len = len; + if (byte_len > 26) + byte_len = 26; + + len -= 26; + + memset(td, 0, 28); + *(uint16_t *)td = PD_EXT_HEADER(chunk_num, 0, dsize); + + for (i = 0; i < byte_len; i++) + td[i + 2] = *(expected_data + data_offset++); + + nw = (byte_len + 2 + 3) >> 2; + header = PD_HEADER(msg_type, pd_port[port].power_role, + pd_port[port].data_role, pd_port[port].msg_rx_id, + nw, pd_port[port].rev, 1); + + cycle_through_state_machine(port, 2, 40 * MSEC); + + if (pd_port[port].mock_pe_error >= 0) + return 0; + + if (pd_port[port].mock_pe_pass_up_message) + return 0; + + if (emsg[port].len != 0) + return 0; + + simulate_rx_msg(port, header, nw, (uint32_t *)td); + task_wait_event(40 * MSEC); + + if (!verify_goodcrc(port, pd_port[port].data_role, + pd_port[port].msg_rx_id)) + return 0; + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(40); + inc_rx_id(port); + + /* + * If no more data, do expected to get a chunk request + */ + if (len <= 0) + break; + + /* + * Wait for request chunk message + */ + req_timeout = 0; + while (get_rch_state_id(port) != RCH_REQUESTING_CHUNK && + req_timeout < 5) { + req_timeout++; + msleep(2); + } + + chunk_num++; + + /* Test Request next chunk packet */ + if (!pd_test_tx_msg_verify_sop(port)) + return 0; + + if (!pd_test_tx_msg_verify_short(port, + PD_HEADER(msg_type, + pd_port[port].power_role, + pd_port[port].data_role, + pd_port[port].msg_tx_id, + 1, pd_port[port].rev, 1))) + return 0; + + if (!pd_test_tx_msg_verify_word(port, + PD_EXT_HEADER(chunk_num, 1, 0))) + return 0; + + if (!pd_test_tx_msg_verify_crc(port)) + return 0; + + if (!pd_test_tx_msg_verify_eop(port)) + return 0; + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(30 * MSEC); + + /* Request next chunk packet was good. Send GoodCRC */ + simulate_goodcrc(port, pd_port[port].power_role, + pd_port[port].msg_tx_id); + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(40 * MSEC); + inc_tx_id(port); + } + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(20 * MSEC); + + return verify_chunk_data_reception(port, header, dsize); +} + +static int verify_ctrl_msg_transmission(int port, + enum pd_ctrl_msg_type msg_type) +{ + if (!pd_test_tx_msg_verify_sop(port)) + return 0; + + if (!pd_test_tx_msg_verify_short(port, + PD_HEADER(msg_type, pd_port[port].power_role, + pd_port[port].data_role, pd_port[port].msg_tx_id, 0, + pd_port[port].rev, 0))) + return 0; + + if (!pd_test_tx_msg_verify_crc(port)) + return 0; + + if (!pd_test_tx_msg_verify_eop(port)) + return 0; + + return 1; +} + +static int simulate_send_ctrl_msg_request_from_pe(int port, + enum tcpm_transmit_type type, enum pd_ctrl_msg_type msg_type) +{ + pd_port[port].mock_got_soft_reset = 0; + pd_port[port].mock_pe_error = -1; + pd_port[port].mock_pe_message_sent = 0; + prl_send_ctrl_msg(port, type, msg_type); + task_wait_event(40 * MSEC); + + if (msg_type == PD_CTRL_SOFT_RESET) + cycle_through_state_machine(port, 1, 20 * MSEC); + + return verify_ctrl_msg_transmission(port, msg_type); +} + +static int verify_data_msg_transmission(int port, + enum pd_data_msg_type msg_type, int len) +{ + int i; + int num_words = (len + 3) >> 2; + int data_obj_in_bytes; + uint32_t td; + + if (!pd_test_tx_msg_verify_sop(port)) + return 0; + + if (!pd_test_tx_msg_verify_short(port, + PD_HEADER(msg_type, pd_port[port].power_role, + pd_port[port].data_role, pd_port[port].msg_tx_id, + num_words, pd_port[port].rev, 0))) + return 0; + + for (i = 0; i < num_words; i++) { + td = test_data[i]; + data_obj_in_bytes = (i + 1) * 4; + if (data_obj_in_bytes > len) { + switch (data_obj_in_bytes - len) { + case 1: + td &= 0x00ffffff; + break; + case 2: + td &= 0x0000ffff; + break; + case 3: + td &= 0x000000ff; + break; + } + } + + if (!pd_test_tx_msg_verify_word(port, td)) + return 0; + } + + if (!pd_test_tx_msg_verify_crc(port)) + return 0; + + if (!pd_test_tx_msg_verify_eop(port)) + return 0; + + return 1; +} + +static int simulate_send_data_msg_request_from_pe(int port, + enum tcpm_transmit_type type, enum pd_ctrl_msg_type msg_type, int len) +{ + int i; + uint8_t *buf = emsg[port].buf; + uint8_t *td = (uint8_t *)test_data; + + pd_port[port].mock_got_soft_reset = 0; + pd_port[port].mock_pe_error = -1; + pd_port[port].mock_pe_message_sent = 0; + + for (i = 0; i < len; i++) + buf[i] = td[i]; + + emsg[port].len = len; + + prl_send_data_msg(port, type, msg_type); + task_wait_event(30 * MSEC); + + return verify_data_msg_transmission(port, msg_type, len); +} + +static int verify_extended_data_msg_transmission(int port, + enum pd_data_msg_type msg_type, int len) +{ + int i; + int j; + int nw; + int byte_len; + int dsize; + uint32_t td; + uint8_t *expected_data = (uint8_t *)&test_data; + int data_offset = 0; + int chunk_number_to_send = 0; + + dsize = len; + + for (j = 0; j < 10; j++) { + byte_len = len; + if (byte_len > 26) + byte_len = 26; + + nw = (byte_len + 2 + 3) >> 2; + + if (!pd_test_tx_msg_verify_sop(port)) + return 0; + + if (!pd_test_tx_msg_verify_short(port, + PD_HEADER(msg_type, pd_port[port].power_role, + pd_port[port].data_role, + pd_port[port].msg_tx_id, + nw, pd_port[port].rev, 1))) + return 0; + td = PD_EXT_HEADER(chunk_number_to_send, 0, dsize); + td |= *(expected_data + data_offset++) << 16; + td |= *(expected_data + data_offset++) << 24; + + if (byte_len == 1) + td &= 0x00ffffff; + + if (!pd_test_tx_msg_verify_word(port, td)) + return 0; + + byte_len -= 2; + + if (byte_len > 0) { + nw = (byte_len + 3) >> 2; + for (i = 0; i < nw; i++) { + td = *(expected_data + data_offset++) << 0; + td |= *(expected_data + data_offset++) << 8; + td |= *(expected_data + data_offset++) << 16; + td |= *(expected_data + data_offset++) << 24; + + switch (byte_len) { + case 3: + td &= 0x00ffffff; + break; + case 2: + td &= 0x0000ffff; + break; + case 1: + td &= 0x000000ff; + break; + } + + if (!pd_test_tx_msg_verify_word(port, td)) + return 0; + byte_len -= 4; + } + } + + if (!pd_test_tx_msg_verify_crc(port)) + return 0; + + if (!pd_test_tx_msg_verify_eop(port)) + return 0; + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(10 * MSEC); + + /* Send GoodCRC */ + simulate_goodcrc(port, pd_port[port].power_role, + pd_port[port].msg_tx_id); + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(30 * MSEC); + inc_tx_id(port); + + len -= 26; + if (len <= 0) + break; + + chunk_number_to_send++; + cycle_through_state_machine(port, 4, 10 * MSEC); + if (!simulate_request_chunk(port, msg_type, + chunk_number_to_send, dsize)) + return 0; + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(30 * MSEC); + inc_rx_id(port); + } + + return 1; +} + +static int simulate_send_extended_data_msg(int port, + enum tcpm_transmit_type type, enum pd_ctrl_msg_type msg_type, + int len) +{ + int i; + uint8_t *buf = emsg[port].buf; + uint8_t *td = (uint8_t *)test_data; + + memset(buf, 0, 260); + emsg[port].len = len; + + /* don't overflow buffer */ + if (len > 260) + len = 260; + + for (i = 0; i < len; i++) + buf[i] = td[i]; + + prl_send_ext_data_msg(port, type, msg_type); + task_wait_event(30 * MSEC); + + return verify_extended_data_msg_transmission(port, msg_type, + len); +} + +static void enable_prl(int port, int en) +{ + tcpm_set_rx_enable(port, en); + + pd_port[port].pd_enable = en; + pd_port[port].msg_tx_id = 0; + pd_port[port].msg_rx_id = 0; + + /* Init PRL */ + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(10 * MSEC); + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(10 * MSEC); + + prl_set_rev(port, pd_port[port].rev); +} + +int tc_get_power_role(int port) +{ + return pd_port[port].power_role; +} + +int tc_get_data_role(int port) +{ + return pd_port[port].data_role; +} + +void pe_report_error(int port, enum pe_error e) +{ + pd_port[port].mock_pe_error = e; +} + +void pe_got_hard_reset(int port) +{ + pd_port[port].mock_pe_got_hard_reset = 1; +} + +void pe_pass_up_message(int port) +{ + pd_port[port].mock_pe_pass_up_message = 1; +} + +void pe_message_sent(int port) +{ + pd_port[port].mock_pe_message_sent = 1; +} + +void pe_hard_reset_sent(int port) +{ + pd_port[port].mock_pe_hard_reset_sent = 1; +} + +void pe_got_soft_reset(int port) +{ + pd_port[port].mock_got_soft_reset = 1; +} + +static int test_initial_states(void) +{ + int port = PORT0; + + enable_prl(port, 1); + + TEST_ASSERT(get_prl_tx_state_id(port) == + PRL_TX_WAIT_FOR_MESSAGE_REQUEST); + TEST_ASSERT(get_rch_state_id(port) == + RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); + TEST_ASSERT(get_tch_state_id(port) == + TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE); + TEST_ASSERT(get_prl_hr_state_id(port) == + PRL_HR_WAIT_FOR_REQUEST); + + return EC_SUCCESS; +} + +static int test_send_ctrl_msg(void) +{ + int i; + int port = PORT0; + + enable_prl(port, 1); + + /* + * TEST: Control message transmission and tx_id increment + */ + for (i = 0; i < 10; i++) { + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(40 * MSEC); + + TEST_ASSERT(get_prl_tx_state_id(port) == + PRL_TX_WAIT_FOR_MESSAGE_REQUEST); + + TEST_ASSERT(simulate_send_ctrl_msg_request_from_pe(port, + TCPC_TX_SOP, PD_CTRL_ACCEPT)); + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(30 * MSEC); + + simulate_goodcrc(port, pd_port[port].power_role, + pd_port[port].msg_tx_id); + inc_tx_id(port); + + cycle_through_state_machine(port, 3, 10 * MSEC); + + TEST_ASSERT(!pd_port[port].mock_got_soft_reset); + TEST_ASSERT(pd_port[port].mock_pe_message_sent); + TEST_ASSERT(pd_port[port].mock_pe_error < 0); + } + + enable_prl(port, 0); + + return EC_SUCCESS; +} + +static int test_send_ctrl_msg_with_retry_and_fail(void) +{ + int i; + int port = PORT0; + + enable_prl(port, 1); + + /* + * TEST: Control message transmission fail with retry + */ + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(40 * MSEC); + + TEST_ASSERT(get_prl_tx_state_id(port) == + PRL_TX_WAIT_FOR_MESSAGE_REQUEST); + + TEST_ASSERT(simulate_send_ctrl_msg_request_from_pe(port, + TCPC_TX_SOP, PD_CTRL_ACCEPT)); + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(30 * MSEC); + + simulate_goodcrc(port, pd_port[port].power_role, + pd_port[port].msg_tx_id); + + /* Do not increment tx_id so phy layer will not transmit message */ + + cycle_through_state_machine(port, 3, 10 * MSEC); + + TEST_ASSERT(!pd_port[port].mock_got_soft_reset); + TEST_ASSERT(pd_port[port].mock_pe_message_sent); + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(40 * MSEC); + + TEST_ASSERT(get_prl_tx_state_id(port) == + PRL_TX_WAIT_FOR_MESSAGE_REQUEST); + + pd_port[port].mock_pe_message_sent = 0; + prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_ACCEPT); + task_wait_event(30 * MSEC); + + for (i = 0; i < N_RETRY_COUNT + 1; i++) { + cycle_through_state_machine(port, 10, 10 * MSEC); + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(PD_T_TCPC_TX_TIMEOUT); + + TEST_ASSERT(!pd_port[port].mock_got_soft_reset); + TEST_ASSERT(pd_port[port].mock_pe_message_sent == 0); + if (i == N_RETRY_COUNT) + TEST_ASSERT(pd_port[port].mock_pe_error == + ERR_TCH_XMIT); + else + TEST_ASSERT(pd_port[port].mock_pe_error < 0); + } + + enable_prl(port, 0); + + return EC_SUCCESS; +} + +static int test_send_ctrl_msg_with_retry_and_success(void) +{ + int i; + int port = PORT0; + + enable_prl(port, 1); + + /* + * TEST: Control message transmission fail with retry + */ + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(40 * MSEC); + + TEST_ASSERT(get_prl_tx_state_id(port) == + PRL_TX_WAIT_FOR_MESSAGE_REQUEST); + + pd_port[port].mock_got_soft_reset = 0; + pd_port[port].mock_pe_error = -1; + pd_port[port].mock_pe_message_sent = 0; + + prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_ACCEPT); + task_wait_event(40 * MSEC); + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(40 * MSEC); + + simulate_goodcrc(port, pd_port[port].power_role, + pd_port[port].msg_tx_id); + + /* Do not increment tx_id. */ + + cycle_through_state_machine(port, 3, 10 * MSEC); + + TEST_ASSERT(!pd_port[port].mock_got_soft_reset); + TEST_ASSERT(pd_port[port].mock_pe_message_sent); + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(40 * MSEC); + + TEST_ASSERT(get_prl_tx_state_id(port) == + PRL_TX_WAIT_FOR_MESSAGE_REQUEST); + + pd_port[port].mock_pe_message_sent = 0; + prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_ACCEPT); + task_wait_event(30 * MSEC); + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(30 * MSEC); + + for (i = 0; i < N_RETRY_COUNT + 1; i++) { + if (i == N_RETRY_COUNT) + inc_tx_id(port); + + simulate_goodcrc(port, pd_port[port].power_role, + pd_port[port].msg_tx_id); + + cycle_through_state_machine(port, 8, 10 * MSEC); + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(PD_T_TCPC_TX_TIMEOUT); + + TEST_ASSERT(!pd_port[port].mock_got_soft_reset); + if (i == N_RETRY_COUNT) + TEST_ASSERT(pd_port[port].mock_pe_message_sent); + else + TEST_ASSERT(pd_port[port].mock_pe_message_sent == 0); + TEST_ASSERT(pd_port[port].mock_pe_error < 0); + } + + enable_prl(port, 0); + + return EC_SUCCESS; +} + +static int test_send_data_msg(void) +{ + int i; + int port = PORT0; + + enable_prl(port, 1); + + /* + * TEST: Sending data message with 1 to 28 bytes + */ + for (i = 1; i <= 28; i++) { + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(40 * MSEC); + + TEST_ASSERT(get_prl_tx_state_id(port) == + PRL_TX_WAIT_FOR_MESSAGE_REQUEST); + + TEST_ASSERT(simulate_send_data_msg_request_from_pe(port, + TCPC_TX_SOP, PD_DATA_SOURCE_CAP, i)); + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(30 * MSEC); + + simulate_goodcrc(port, pd_port[port].power_role, + pd_port[port].msg_tx_id); + inc_tx_id(port); + + cycle_through_state_machine(port, 3, 10 * MSEC); + + TEST_ASSERT(!pd_port[port].mock_got_soft_reset); + TEST_ASSERT(pd_port[port].mock_pe_message_sent); + TEST_ASSERT(pd_port[port].mock_pe_error < 0); + } + + enable_prl(port, 0); + + return EC_SUCCESS; +} + +static int test_send_data_msg_to_much_data(void) +{ + int port = PORT0; + + enable_prl(port, 1); + + /* + * TEST: Send data message with more than 28-bytes, should fail + */ + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(40 * MSEC); + + TEST_ASSERT(get_prl_tx_state_id(port) == + PRL_TX_WAIT_FOR_MESSAGE_REQUEST); + + /* Try to send 29-bytes */ + TEST_ASSERT(!simulate_send_data_msg_request_from_pe(port, + TCPC_TX_SOP, PD_DATA_SOURCE_CAP, 29)); + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(30 * MSEC); + + cycle_through_state_machine(port, 3, 10 * MSEC); + + TEST_ASSERT(!pd_port[port].mock_got_soft_reset); + TEST_ASSERT(!pd_port[port].mock_pe_message_sent); + TEST_ASSERT(pd_port[port].mock_pe_error = ERR_TCH_XMIT); + + enable_prl(port, 0); + + return EC_SUCCESS; +} + +static int test_send_extended_data_msg(void) +{ + int i; + int port = PORT0; + + enable_prl(port, 1); + + /* + * TEST: Sending extended data message with 29 to 260 bytes + */ + + pd_port[port].mock_got_soft_reset = 0; + pd_port[port].mock_pe_error = -1; + + for (i = 29; i <= 260; i++) { + pd_port[port].mock_pe_message_sent = 0; + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(40 * MSEC); + + TEST_ASSERT(get_prl_tx_state_id(port) == + PRL_TX_WAIT_FOR_MESSAGE_REQUEST); + + TEST_ASSERT(simulate_send_extended_data_msg( + port, TCPC_TX_SOP, PD_EXT_MANUFACTURER_INFO, i)); + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(30 * MSEC); + + cycle_through_state_machine(port, 3, 10 * MSEC); + + TEST_ASSERT(!pd_port[port].mock_got_soft_reset); + TEST_ASSERT(pd_port[port].mock_pe_message_sent); + TEST_ASSERT(pd_port[port].mock_pe_error < 0); + } + enable_prl(port, 0); + + return EC_SUCCESS; +} + +static int test_receive_soft_reset_msg(void) +{ + int port = PORT0; + int expected_header = PD_HEADER(PD_CTRL_SOFT_RESET, + pd_port[port].power_role, + pd_port[port].data_role, + pd_port[port].msg_rx_id, + 0, pd_port[port].rev, 0); + + enable_prl(port, 1); + + /* + * TEST: Receiving Soft Reset + */ + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(40 * MSEC); + + TEST_ASSERT(get_rch_state_id(port) == + RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); + + pd_port[port].mock_got_soft_reset = 0; + pd_port[port].mock_pe_error = -1; + pd_port[port].mock_pe_pass_up_message = 0; + + TEST_ASSERT(simulate_receive_ctrl_msg(port, PD_CTRL_SOFT_RESET)); + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(30 * MSEC); + + cycle_through_state_machine(port, 3, 10 * MSEC); + + TEST_ASSERT(pd_port[port].mock_got_soft_reset); + TEST_ASSERT(pd_port[port].mock_pe_error < 0); + TEST_ASSERT(pd_port[port].mock_pe_pass_up_message); + TEST_ASSERT(expected_header == emsg[port].header); + TEST_ASSERT(emsg[port].len == 0); + + enable_prl(port, 0); + + return EC_SUCCESS; +} + +static int test_receive_control_msg(void) +{ + int port = PORT0; + int expected_header = PD_HEADER(PD_CTRL_DR_SWAP, + pd_port[port].power_role, + pd_port[port].data_role, + pd_port[port].msg_rx_id, + 0, pd_port[port].rev, 0); + + enable_prl(port, 1); + + /* + * TEST: Receiving a control message + */ + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(40 * MSEC); + + TEST_ASSERT(get_rch_state_id(port) == + RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); + + pd_port[port].mock_got_soft_reset = 0; + pd_port[port].mock_pe_error = -1; + pd_port[port].mock_pe_pass_up_message = 0; + + TEST_ASSERT(simulate_receive_ctrl_msg(port, PD_CTRL_DR_SWAP)); + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(30 * MSEC); + + cycle_through_state_machine(port, 3, 10 * MSEC); + + TEST_ASSERT(!pd_port[port].mock_got_soft_reset); + TEST_ASSERT(pd_port[port].mock_pe_error < 0); + TEST_ASSERT(pd_port[port].mock_pe_pass_up_message); + TEST_ASSERT(expected_header == emsg[port].header); + TEST_ASSERT(emsg[port].len == 0); + + enable_prl(port, 0); + + return EC_SUCCESS; +} + +static int test_receive_data_msg(void) +{ + int port = PORT0; + int i; + + enable_prl(port, 1); + + /* + * TEST: Receiving data message with 1 to 28 bytes + */ + + for (i = 1; i <= 28; i++) { + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(40 * MSEC); + + TEST_ASSERT(get_rch_state_id(port) == + RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); + TEST_ASSERT(simulate_receive_data(port, + PD_DATA_BATTERY_STATUS, i)); + } + + enable_prl(port, 0); + + return EC_SUCCESS; +} + +static int test_receive_extended_data_msg(void) +{ + int len; + int port = PORT0; + + enable_prl(port, 1); + + /* + * TEST: Receiving extended data message with 29 to 260 bytes + */ + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(40 * MSEC); + + TEST_ASSERT(get_rch_state_id(port) == + RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER); + + for (len = 29; len <= 260; len++) + TEST_ASSERT(simulate_receive_extended_data(port, + PD_DATA_BATTERY_STATUS, len)); + + enable_prl(port, 0); + + return EC_SUCCESS; +} + +static int test_send_soft_reset_msg(void) +{ + int port = PORT0; + + enable_prl(port, 1); + + /* + * TEST: Send soft reset + */ + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(40 * MSEC); + + TEST_ASSERT(get_prl_tx_state_id(port) == + PRL_TX_WAIT_FOR_MESSAGE_REQUEST); + + TEST_ASSERT(simulate_send_ctrl_msg_request_from_pe(port, + TCPC_TX_SOP, PD_CTRL_SOFT_RESET)); + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(30 * MSEC); + + simulate_goodcrc(port, pd_port[port].power_role, + pd_port[port].msg_tx_id); + inc_tx_id(port); + + TEST_ASSERT(get_prl_tx_state_id(port) == + PRL_TX_LAYER_RESET_FOR_TRANSMIT); + + cycle_through_state_machine(port, 3, 10 * MSEC); + + TEST_ASSERT(!pd_port[port].mock_got_soft_reset); + TEST_ASSERT(pd_port[port].mock_pe_message_sent); + TEST_ASSERT(pd_port[port].mock_pe_error < 0); + + enable_prl(port, 0); + + return EC_SUCCESS; +} + +static int test_pe_execute_hard_reset_msg(void) +{ + int port = PORT0; + + enable_prl(port, 1); + + pd_port[port].mock_pe_hard_reset_sent = 0; + + /* + * TEST: Policy Engine initiated hard reset + */ + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(40 * MSEC); + + TEST_ASSERT(get_prl_hr_state_id(port) == PRL_HR_WAIT_FOR_REQUEST); + + /* Simulate receiving hard reset from policy engine */ + prl_execute_hard_reset(port); + + TEST_ASSERT(get_prl_hr_state_id(port) == PRL_HR_RESET_LAYER); + TEST_ASSERT(get_prl_tx_state_id(port) == + PRL_TX_WAIT_FOR_MESSAGE_REQUEST); + + cycle_through_state_machine(port, 1, 10 * MSEC); + + TEST_ASSERT(get_prl_hr_state_id(port) == + PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE); + + cycle_through_state_machine(port, 2, PD_T_PS_HARD_RESET); + TEST_ASSERT(pd_port[port].mock_pe_hard_reset_sent); + + TEST_ASSERT(get_prl_hr_state_id(port) == + PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE); + + /* Simulate policy engine indicating that it is done hard reset */ + prl_hard_reset_complete(port); + + cycle_through_state_machine(port, 1, 10 * MSEC); + + TEST_ASSERT(get_prl_hr_state_id(port) == PRL_HR_WAIT_FOR_REQUEST); + + enable_prl(port, 0); + + return EC_SUCCESS; +} + +static int test_phy_execute_hard_reset_msg(void) +{ + int port = PORT0; + + enable_prl(port, 1); + + /* + * TEST: Port partner initiated hard reset + */ + + pd_port[port].mock_pe_got_hard_reset = 0; + + task_wake(PD_PORT_TO_TASK_ID(port)); + task_wait_event(40 * MSEC); + + TEST_ASSERT(get_prl_hr_state_id(port) == PRL_HR_WAIT_FOR_REQUEST); + + /* Simulate receiving hard reset from port partner */ + pd_execute_hard_reset(port); + + TEST_ASSERT(get_prl_hr_state_id(port) == PRL_HR_RESET_LAYER); + TEST_ASSERT(get_prl_tx_state_id(port) == + PRL_TX_WAIT_FOR_MESSAGE_REQUEST); + + cycle_through_state_machine(port, 1, 10 * MSEC); + + TEST_ASSERT(get_prl_hr_state_id(port) == + PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE); + + cycle_through_state_machine(port, 2, PD_T_PS_HARD_RESET); + TEST_ASSERT(pd_port[port].mock_pe_got_hard_reset); + + TEST_ASSERT(get_prl_hr_state_id(port) == + PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE); + + /* Simulate policy engine indicating that it is done hard reset */ + prl_hard_reset_complete(port); + + cycle_through_state_machine(port, 1, 10 * MSEC); + + TEST_ASSERT(get_prl_hr_state_id(port) == PRL_HR_WAIT_FOR_REQUEST); + + enable_prl(port, 0); + + return EC_SUCCESS; +} + +int pd_task(void *u) +{ + int port = PORT0; + int evt; + + while (1) { + evt = task_wait_event(-1); + + tcpc_run(port, evt); + protocol_layer(port, evt, pd_port[port].pd_enable); + } + + return EC_SUCCESS; +} + +void run_test(void) +{ + test_reset(); + + /* Test PD 2.0 Protocol */ + init_port(PORT0, PD_REV20); + RUN_TEST(test_initial_states); + RUN_TEST(test_send_ctrl_msg); + RUN_TEST(test_send_ctrl_msg_with_retry_and_fail); + RUN_TEST(test_send_ctrl_msg_with_retry_and_success); + RUN_TEST(test_send_data_msg); + RUN_TEST(test_send_data_msg_to_much_data); + RUN_TEST(test_receive_control_msg); + RUN_TEST(test_receive_data_msg); + RUN_TEST(test_receive_soft_reset_msg); + RUN_TEST(test_send_soft_reset_msg); + RUN_TEST(test_pe_execute_hard_reset_msg); + RUN_TEST(test_phy_execute_hard_reset_msg); + + /* TODO(shurst): More PD 2.0 Tests */ + + /* Test PD 3.0 Protocol */ + init_port(PORT0, PD_REV30); + RUN_TEST(test_initial_states); + RUN_TEST(test_send_ctrl_msg); + RUN_TEST(test_send_ctrl_msg_with_retry_and_fail); + RUN_TEST(test_send_ctrl_msg_with_retry_and_success); + RUN_TEST(test_send_data_msg); + RUN_TEST(test_send_data_msg_to_much_data); + RUN_TEST(test_send_extended_data_msg); + RUN_TEST(test_receive_control_msg); + RUN_TEST(test_receive_data_msg); + RUN_TEST(test_receive_extended_data_msg); + RUN_TEST(test_receive_soft_reset_msg); + RUN_TEST(test_send_soft_reset_msg); + RUN_TEST(test_pe_execute_hard_reset_msg); + RUN_TEST(test_phy_execute_hard_reset_msg); + + /* TODO(shurst): More PD 3.0 Tests */ + + test_print_result(); +} + diff --git a/test/usb_prl.tasklist b/test/usb_prl.tasklist new file mode 100644 index 0000000000..88eca7d9b7 --- /dev/null +++ b/test/usb_prl.tasklist @@ -0,0 +1,19 @@ +/* Copyright (c) 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. + */ + +/** + * List of enabled tasks in the priority order + * + * The first one has the lowest priority. + * + * For each task, use the macro TASK_TEST(n, r, d, s) where : + * 'n' in the name of the task + * 'r' in the main routine of the task + * 'd' in an opaque parameter passed to the routine at startup + * 's' is the stack size in bytes; must be a multiple of 8 + */ +#define CONFIG_TEST_TASK_LIST \ + TASK_TEST(PD_C0, pd_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_TEST(PD_C1, pd_task, NULL, LARGER_TASK_STACK_SIZE) diff --git a/test/usb_sm_framework_h0.tasklist b/test/usb_sm_framework_h0.tasklist new file mode 120000 index 0000000000..b55922b1ee --- /dev/null +++ b/test/usb_sm_framework_h0.tasklist @@ -0,0 +1 @@ +usb_sm_framework_h3.tasklist
\ No newline at end of file diff --git a/test/usb_sm_framework_h1.tasklist b/test/usb_sm_framework_h1.tasklist new file mode 120000 index 0000000000..b55922b1ee --- /dev/null +++ b/test/usb_sm_framework_h1.tasklist @@ -0,0 +1 @@ +usb_sm_framework_h3.tasklist
\ No newline at end of file diff --git a/test/usb_sm_framework_h2.tasklist b/test/usb_sm_framework_h2.tasklist new file mode 120000 index 0000000000..b55922b1ee --- /dev/null +++ b/test/usb_sm_framework_h2.tasklist @@ -0,0 +1 @@ +usb_sm_framework_h3.tasklist
\ No newline at end of file diff --git a/test/usb_sm_framework_h3.c b/test/usb_sm_framework_h3.c new file mode 100644 index 0000000000..db88338387 --- /dev/null +++ b/test/usb_sm_framework_h3.c @@ -0,0 +1,1219 @@ +/* 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 "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_test_util.h" +#include "vpd_api.h" + +/* + * Test State Hierarchy + * SM_TEST_A4 transitions to SM_TEST_B4 + * SM_TEST_B4 transitions to SM_TEST_B5 + * SM_TEST_B5 transitions to SM_TEST_B6 + * SM_TEST_B6 transitions to SM_TEST_C + * SM_TEST_C transitions to SM_TEST_A7 + * SM_TEST_A7 transitions to SM_TEST_A6 + * SM_TEST_A6 transitions to SM_TEST_A5 + * SM_TEST_A5 transitions to SM_TEST_A4 + * + * --------------------------- --------------------------- + * | SM_TEST_SUPER_A1 | | SM_TEST_SUPER_B1 | + * | ----------------------- | | ----------------------- | + * | | SM_TEST_SUPER_A2 | | | | SM_TEST_SUPER_B2 | | + * | | ------------------- | | | | ------------------- | | + * | | |SM_TEST_SUPER_A3 | | | | | |SM_TEST_SUPER_B3 | | | + * | | | | | | | | | | | | + * | | | ------------- | | | | | | ------------- | | | + * | | | | SM_TEST_A4|------------------>| SM_TEST_B4| | | | + * | | | ------------- | | | | | | ------------- | | | + * | | | ^ | | | | | |--------|--------| | | + * | | | | | | | | | | | | + * | | | -------------- | | | | | \/ | | + * | | | | SM_TEST_A5 | | | | | | -------------- | | + * | | | -------------- | | | | | | SM_TEST_B5 | | | + * | | |--------^--------| | | | | -------------- | | + * | | | | | | | | | | + * | | -------------- | | | -----------|----------- | + * | | | SM_TEST_A6 | | | | \/ | + * | | -------------- | | | -------------- | + * | |----------^----------| | | | SM_TEST_B6 | | + * | | | | -------------- | + * | -------------- | |--------/----------------| + * | | SM_TEST_A7 | | / + * | -------------- | / + * |------------------^------| / + * \ / + * \ \/ + * ------------- + * | SM_TEST_C | + * ------------- + * + * test_hierarchy_0: Tests a flat state machine without super states + * test_hierarchy_1: Tests a hierarchical state machine with 1 super state + * test_hierarchy_2: Tests a hierarchical state machine with 2 super states + * test_hierarchy_3: Tests a hierarchical state machine with 3 super states + * + */ + +#define SEQUENCE_SIZE 55 + +enum state_id { + ENTER_A1 = 1, + RUN_A1, + EXIT_A1, + ENTER_A2, + RUN_A2, + EXIT_A2, + ENTER_A3, + RUN_A3, + EXIT_A3, + ENTER_A4, + RUN_A4, + EXIT_A4, + ENTER_A5, + RUN_A5, + EXIT_A5, + ENTER_A6, + RUN_A6, + EXIT_A6, + ENTER_A7, + RUN_A7, + EXIT_A7, + ENTER_B1, + RUN_B1, + EXIT_B1, + ENTER_B2, + RUN_B2, + EXIT_B2, + ENTER_B3, + RUN_B3, + EXIT_B3, + ENTER_B4, + RUN_B4, + EXIT_B4, + ENTER_B5, + RUN_B5, + EXIT_B5, + ENTER_B6, + RUN_B6, + EXIT_B6, + ENTER_C, + RUN_C, + EXIT_C, +}; + +#define PORT0 0 +#define TSM_OBJ(port) (SM_OBJ(sm[port])) + +struct sm_ { + /* struct sm_obj must be first */ + struct sm_obj obj; + int sv_tmp; + int idx; + int seq[55]; +} sm[1]; + +#if defined(TEST_USB_SM_FRAMEWORK_H3) +static unsigned int sm_test_super_A1(int port, enum signal sig); +static unsigned int sm_test_super_A1_entry(int port); +static unsigned int sm_test_super_A1_run(int port); +static unsigned int sm_test_super_A1_exit(int port); + +static unsigned int sm_test_super_B1(int port, enum signal sig); +static unsigned int sm_test_super_B1_entry(int port); +static unsigned int sm_test_super_B1_run(int port); +static unsigned int sm_test_super_B1_exit(int port); +#endif + +#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) +static unsigned int sm_test_super_A2(int port, enum signal sig); +static unsigned int sm_test_super_A2_entry(int port); +static unsigned int sm_test_super_A2_run(int port); +static unsigned int sm_test_super_A2_exit(int port); + +static unsigned int sm_test_super_B2(int port, enum signal sig); +static unsigned int sm_test_super_B2_entry(int port); +static unsigned int sm_test_super_B2_run(int port); +static unsigned int sm_test_super_B2_exit(int port); +#endif + +#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) || \ + defined(TEST_USB_SM_FRAMEWORK_H1) +static unsigned int sm_test_super_A3(int port, enum signal sig); +static unsigned int sm_test_super_A3_entry(int port); +static unsigned int sm_test_super_A3_run(int port); +static unsigned int sm_test_super_A3_exit(int port); + +static unsigned int sm_test_super_B3(int port, enum signal sig); +static unsigned int sm_test_super_B3_entry(int port); +static unsigned int sm_test_super_B3_run(int port); +static unsigned int sm_test_super_B3_exit(int port); +#endif + +static unsigned int sm_test_A4(int port, enum signal sig); +static unsigned int sm_test_A4_entry(int port); +static unsigned int sm_test_A4_run(int port); +static unsigned int sm_test_A4_exit(int port); + +static unsigned int sm_test_A5(int port, enum signal sig); +static unsigned int sm_test_A5_entry(int port); +static unsigned int sm_test_A5_run(int port); +static unsigned int sm_test_A5_exit(int port); + +static unsigned int sm_test_A6(int port, enum signal sig); +static unsigned int sm_test_A6_entry(int port); +static unsigned int sm_test_A6_run(int port); +static unsigned int sm_test_A6_exit(int port); + +static unsigned int sm_test_A7(int port, enum signal sig); +static unsigned int sm_test_A7_entry(int port); +static unsigned int sm_test_A7_run(int port); +static unsigned int sm_test_A7_exit(int port); + +static unsigned int sm_test_B4(int port, enum signal sig); +static unsigned int sm_test_B4_entry(int port); +static unsigned int sm_test_B4_run(int port); +static unsigned int sm_test_B4_exit(int port); + +static unsigned int sm_test_B5(int port, enum signal sig); +static unsigned int sm_test_B5_entry(int port); +static unsigned int sm_test_B5_run(int port); +static unsigned int sm_test_B5_exit(int port); + +static unsigned int sm_test_B6(int port, enum signal sig); +static unsigned int sm_test_B6_entry(int port); +static unsigned int sm_test_B6_run(int port); +static unsigned int sm_test_B6_exit(int port); + +static unsigned int sm_test_C(int port, enum signal sig); +static unsigned int sm_test_C_entry(int port); +static unsigned int sm_test_C_run(int port); +static unsigned int sm_test_C_exit(int port); + +static unsigned int get_super_state(int port); + +#if defined(TEST_USB_SM_FRAMEWORK_H3) +static const state_sig sm_test_super_A1_sig[] = { + sm_test_super_A1_entry, + sm_test_super_A1_run, + sm_test_super_A1_exit, + get_super_state +}; + +static const state_sig sm_test_super_B1_sig[] = { + sm_test_super_B1_entry, + sm_test_super_B1_run, + sm_test_super_B1_exit, + get_super_state +}; +#endif + +#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) +static const state_sig sm_test_super_A2_sig[] = { + sm_test_super_A2_entry, + sm_test_super_A2_run, + sm_test_super_A2_exit, + get_super_state +}; + +static const state_sig sm_test_super_B2_sig[] = { + sm_test_super_B2_entry, + sm_test_super_B2_run, + sm_test_super_B2_exit, + get_super_state +}; +#endif + +#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) || \ + defined(TEST_USB_SM_FRAMEWORK_H1) +static const state_sig sm_test_super_A3_sig[] = { + sm_test_super_A3_entry, + sm_test_super_A3_run, + sm_test_super_A3_exit, + get_super_state +}; + +static const state_sig sm_test_super_B3_sig[] = { + sm_test_super_B3_entry, + sm_test_super_B3_run, + sm_test_super_B3_exit, + get_super_state +}; +#endif + +static const state_sig sm_test_A4_sig[] = { + sm_test_A4_entry, + sm_test_A4_run, + sm_test_A4_exit, + get_super_state +}; + +static const state_sig sm_test_A5_sig[] = { + sm_test_A5_entry, + sm_test_A5_run, + sm_test_A5_exit, + get_super_state +}; + +static const state_sig sm_test_A6_sig[] = { + sm_test_A6_entry, + sm_test_A6_run, + sm_test_A6_exit, + get_super_state +}; + +static const state_sig sm_test_A7_sig[] = { + sm_test_A7_entry, + sm_test_A7_run, + sm_test_A7_exit, + get_super_state +}; + +static const state_sig sm_test_B4_sig[] = { + sm_test_B4_entry, + sm_test_B4_run, + sm_test_B4_exit, + get_super_state +}; + +static const state_sig sm_test_B5_sig[] = { + sm_test_B5_entry, + sm_test_B5_run, + sm_test_B5_exit, + get_super_state +}; + +static const state_sig sm_test_B6_sig[] = { + sm_test_B6_entry, + sm_test_B6_run, + sm_test_B6_exit, + get_super_state +}; + +static const state_sig sm_test_C_sig[] = { + sm_test_C_entry, + sm_test_C_run, + sm_test_C_exit, + get_super_state +}; + +static void clear_seq(int port) +{ + int i; + + sm[port].idx = 0; + + for (i = 0; i < 8; i++) + sm[port].seq[i] = 0; +} + +#if defined(TEST_USB_SM_FRAMEWORK_H3) +static unsigned int sm_test_super_A1(int port, enum signal sig) +{ + int ret; + + ret = (*sm_test_super_A1_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int sm_test_super_A1_entry(int port) +{ + sm[port].seq[sm[port].idx++] = ENTER_A1; + return 0; +} + +static unsigned int sm_test_super_A1_run(int port) +{ + sm[port].seq[sm[port].idx++] = RUN_A1; + return 0; +} + +static unsigned int sm_test_super_A1_exit(int port) +{ + sm[port].seq[sm[port].idx++] = EXIT_A1; + return 0; +} + +static unsigned int sm_test_super_B1(int port, enum signal sig) +{ + int ret; + + ret = (*sm_test_super_B1_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int sm_test_super_B1_entry(int port) +{ + sm[port].seq[sm[port].idx++] = ENTER_B1; + return 0; +} + +static unsigned int sm_test_super_B1_run(int port) +{ + sm[port].seq[sm[port].idx++] = RUN_B1; + return 0; +} + +static unsigned int sm_test_super_B1_exit(int port) +{ + sm[port].seq[sm[port].idx++] = EXIT_B1; + return 0; +} +#endif + +#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) +static unsigned int sm_test_super_A2(int port, enum signal sig) +{ + int ret; + + ret = (*sm_test_super_A2_sig[sig])(port); +#if defined(TEST_USB_SM_FRAMEWORK_H3) + return SUPER(ret, sig, sm_test_super_A1); +#else + return SUPER(ret, sig, 0); +#endif +} + +static unsigned int sm_test_super_A2_entry(int port) +{ + sm[port].seq[sm[port].idx++] = ENTER_A2; + return 0; +} + +static unsigned int sm_test_super_A2_run(int port) +{ + sm[port].seq[sm[port].idx++] = RUN_A2; + return RUN_SUPER; +} + +static unsigned int sm_test_super_A2_exit(int port) +{ + sm[port].seq[sm[port].idx++] = EXIT_A2; + return 0; +} + +static unsigned int sm_test_super_B2(int port, enum signal sig) +{ + int ret; + + ret = (*sm_test_super_B2_sig[sig])(port); +#if defined(TEST_USB_SM_FRAMEWORK_H3) + return SUPER(ret, sig, sm_test_super_B1); +#else + return SUPER(ret, sig, 0); +#endif +} + +static unsigned int sm_test_super_B2_entry(int port) +{ + sm[port].seq[sm[port].idx++] = ENTER_B2; + return 0; +} + +static unsigned int sm_test_super_B2_run(int port) +{ + sm[port].seq[sm[port].idx++] = RUN_B2; + return RUN_SUPER; +} + +static unsigned int sm_test_super_B2_exit(int port) +{ + sm[port].seq[sm[port].idx++] = EXIT_B2; + return 0; +} +#endif + +#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) || \ + defined(TEST_USB_SM_FRAMEWORK_H1) +static unsigned int sm_test_super_A3(int port, enum signal sig) +{ + int ret; + + ret = (*sm_test_super_A3_sig[sig])(port); +#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) + return SUPER(ret, sig, sm_test_super_A2); +#else + return SUPER(ret, sig, 0); +#endif +} + +static unsigned int sm_test_super_A3_entry(int port) +{ + sm[port].seq[sm[port].idx++] = ENTER_A3; + return 0; +} + +static unsigned int sm_test_super_A3_run(int port) +{ + sm[port].seq[sm[port].idx++] = RUN_A3; +#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) + return RUN_SUPER; +#else + return 0; +#endif +} + +static unsigned int sm_test_super_A3_exit(int port) +{ + sm[port].seq[sm[port].idx++] = EXIT_A3; + return 0; +} + +static unsigned int sm_test_super_B3(int port, enum signal sig) +{ + int ret; + + ret = (*sm_test_super_B3_sig[sig])(port); +#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) + return SUPER(ret, sig, sm_test_super_B2); +#else + return SUPER(ret, sig, 0); +#endif +} + +static unsigned int sm_test_super_B3_entry(int port) +{ + sm[port].seq[sm[port].idx++] = ENTER_B3; + return 0; +} + +static unsigned int sm_test_super_B3_run(int port) +{ + sm[port].seq[sm[port].idx++] = RUN_B3; +#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) + return RUN_SUPER; +#else + return 0; +#endif +} + +static unsigned int sm_test_super_B3_exit(int port) +{ + sm[port].seq[sm[port].idx++] = EXIT_B3; + return 0; +} +#endif + + +static unsigned int sm_test_A4(int port, enum signal sig) +{ + int ret; + + ret = (*sm_test_A4_sig[sig])(port); +#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) || \ + defined(TEST_USB_SM_FRAMEWORK_H1) + return SUPER(ret, sig, sm_test_super_A3); +#else + return SUPER(ret, sig, 0); +#endif +} + +static unsigned int sm_test_A4_entry(int port) +{ + sm[port].sv_tmp = 0; + sm[port].seq[sm[port].idx++] = ENTER_A4; + return 0; +} + +static unsigned int sm_test_A4_run(int port) +{ + if (sm[port].sv_tmp == 0) { + sm[port].sv_tmp = 1; + sm[port].seq[sm[port].idx++] = RUN_A4; + } else { + set_state(port, TSM_OBJ(port), sm_test_B4); + return 0; + } + + return RUN_SUPER; +} + +static unsigned int sm_test_A4_exit(int port) +{ + sm[port].seq[sm[port].idx++] = EXIT_A4; + return 0; +} + +static unsigned int sm_test_A5(int port, enum signal sig) +{ + int ret; + + ret = (*sm_test_A5_sig[sig])(port); +#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) || \ + defined(TEST_USB_SM_FRAMEWORK_H1) + return SUPER(ret, sig, sm_test_super_A3); +#else + return SUPER(ret, sig, 0); +#endif +} + +static unsigned int sm_test_A5_entry(int port) +{ + sm[port].sv_tmp = 0; + sm[port].seq[sm[port].idx++] = ENTER_A5; + return 0; +} + +static unsigned int sm_test_A5_run(int port) +{ + if (sm[port].sv_tmp == 0) { + sm[port].sv_tmp = 1; + sm[port].seq[sm[port].idx++] = RUN_A5; + } else { + set_state(port, TSM_OBJ(port), sm_test_A4); + return 0; + } + + return RUN_SUPER; +} + +static unsigned int sm_test_A5_exit(int port) +{ + sm[port].seq[sm[port].idx++] = EXIT_A5; + return 0; +} + +static unsigned int sm_test_A6(int port, enum signal sig) +{ + int ret; + + ret = (*sm_test_A6_sig[sig])(port); +#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) + return SUPER(ret, sig, sm_test_super_A2); +#else + return SUPER(ret, sig, 0); +#endif +} + +static unsigned int sm_test_A6_entry(int port) +{ + sm[port].sv_tmp = 0; + sm[port].seq[sm[port].idx++] = ENTER_A6; + return 0; +} + +static unsigned int sm_test_A6_run(int port) +{ + if (sm[port].sv_tmp == 0) { + sm[port].sv_tmp = 1; + sm[port].seq[sm[port].idx++] = RUN_A6; + } else { + set_state(port, TSM_OBJ(port), sm_test_A5); + return 0; + } + + return RUN_SUPER; +} + +static unsigned int sm_test_A6_exit(int port) +{ + sm[port].seq[sm[port].idx++] = EXIT_A6; + return 0; +} + +static unsigned int sm_test_A7(int port, enum signal sig) +{ + int ret; + + ret = (*sm_test_A7_sig[sig])(port); +#if defined(TEST_USB_SM_FRAMEWORK_H3) + return SUPER(ret, sig, sm_test_super_A1); +#else + return SUPER(ret, sig, 0); +#endif +} + +static unsigned int sm_test_A7_entry(int port) +{ + sm[port].sv_tmp = 0; + sm[port].seq[sm[port].idx++] = ENTER_A7; + return 0; +} + +static unsigned int sm_test_A7_run(int port) +{ + if (sm[port].sv_tmp == 0) { + sm[port].sv_tmp = 1; + sm[port].seq[sm[port].idx++] = RUN_A7; + } else { + set_state(port, TSM_OBJ(port), sm_test_A6); + return 0; + } + + return RUN_SUPER; +} + +static unsigned int sm_test_A7_exit(int port) +{ + sm[port].seq[sm[port].idx++] = EXIT_A7; + return 0; +} + +static unsigned int sm_test_B4(int port, enum signal sig) +{ + int ret; + + ret = (*sm_test_B4_sig[sig])(port); +#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) || \ + defined(TEST_USB_SM_FRAMEWORK_H1) + return SUPER(ret, sig, sm_test_super_B3); +#else + return SUPER(ret, sig, 0); +#endif +} + +static unsigned int sm_test_B4_entry(int port) +{ + sm[port].sv_tmp = 0; + sm[port].seq[sm[port].idx++] = ENTER_B4; + return 0; +} + +static unsigned int sm_test_B4_run(int port) +{ + if (sm[port].sv_tmp == 0) { + sm[port].seq[sm[port].idx++] = RUN_B4; + sm[port].sv_tmp = 1; + } else { + set_state(port, TSM_OBJ(port), sm_test_B5); + return 0; + } + + return RUN_SUPER; +} + +static unsigned int sm_test_B4_exit(int port) +{ + sm[port].seq[sm[port].idx++] = EXIT_B4; + return 0; +} + +static unsigned int sm_test_B5(int port, enum signal sig) +{ + int ret; + + ret = (*sm_test_B5_sig[sig])(port); +#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) + return SUPER(ret, sig, sm_test_super_B2); +#else + return SUPER(ret, sig, 0); +#endif +} + +static unsigned int sm_test_B5_entry(int port) +{ + sm[port].sv_tmp = 0; + sm[port].seq[sm[port].idx++] = ENTER_B5; + return 0; +} + +static unsigned int sm_test_B5_run(int port) +{ + if (sm[port].sv_tmp == 0) { + sm[port].sv_tmp = 1; + sm[port].seq[sm[port].idx++] = RUN_B5; + } else { + set_state(port, TSM_OBJ(port), sm_test_B6); + return 0; + } + + return RUN_SUPER; +} + +static unsigned int sm_test_B5_exit(int port) +{ + sm[port].seq[sm[port].idx++] = EXIT_B5; + return 0; +} + +static unsigned int sm_test_B6(int port, enum signal sig) +{ + int ret; + + ret = (*sm_test_B6_sig[sig])(port); +#if defined(TEST_USB_SM_FRAMEWORK_H3) + return SUPER(ret, sig, sm_test_super_B1); +#else + return SUPER(ret, sig, 0); +#endif +} + +static unsigned int sm_test_B6_entry(int port) +{ + sm[port].sv_tmp = 0; + sm[port].seq[sm[port].idx++] = ENTER_B6; + return 0; +} + +static unsigned int sm_test_B6_run(int port) +{ + if (sm[port].sv_tmp == 0) { + sm[port].sv_tmp = 1; + sm[port].seq[sm[port].idx++] = RUN_B6; + } else { + set_state(port, TSM_OBJ(port), sm_test_C); + return 0; + } + + return RUN_SUPER; +} + +static unsigned int sm_test_B6_exit(int port) +{ + sm[port].seq[sm[port].idx++] = EXIT_B6; + return 0; +} + +static unsigned int get_super_state(int port) +{ + return RUN_SUPER; +} + +static unsigned int sm_test_C(int port, enum signal sig) +{ + int ret; + + ret = (*sm_test_C_sig[sig])(port); + return SUPER(ret, sig, 0); +} + +static unsigned int sm_test_C_entry(int port) +{ + sm[port].sv_tmp = 0; + sm[port].seq[sm[port].idx++] = ENTER_C; + return 0; +} + +static unsigned int sm_test_C_run(int port) +{ + if (sm[port].sv_tmp == 0) { + sm[port].seq[sm[port].idx++] = RUN_C; + sm[port].sv_tmp = 1; + } else { + set_state(port, TSM_OBJ(port), sm_test_A7); + } + + return 0; +} + +static unsigned int sm_test_C_exit(int port) +{ + sm[port].seq[sm[port].idx++] = EXIT_C; + return 0; +} + +#if defined(TEST_USB_SM_FRAMEWORK_H0) +static int test_hierarchy_0(void) +{ + int port = PORT0; + int i; + + clear_seq(port); + init_state(port, TSM_OBJ(port), sm_test_A4); + + for (i = 0; i < 17; i++) { + task_wake(TASK_ID_TEST); + task_wait_event(5 * MSEC); + } + + /* i == 0 */ + TEST_ASSERT(sm[port].seq[0] == ENTER_A4); + + /* i == 1 */ + TEST_ASSERT(sm[port].seq[1] == RUN_A4); + + /* i == 2 */ + TEST_ASSERT(sm[port].seq[2] == EXIT_A4); + TEST_ASSERT(sm[port].seq[3] == ENTER_B4); + + /* i == 3 */ + TEST_ASSERT(sm[port].seq[4] == RUN_B4); + + /* i == 4 */ + TEST_ASSERT(sm[port].seq[5] == EXIT_B4); + TEST_ASSERT(sm[port].seq[6] == ENTER_B5); + + /* i == 5 */ + TEST_ASSERT(sm[port].seq[7] == RUN_B5); + + /* i == 6 */ + TEST_ASSERT(sm[port].seq[8] == EXIT_B5); + TEST_ASSERT(sm[port].seq[9] == ENTER_B6); + + /* i == 7 */ + TEST_ASSERT(sm[port].seq[10] == RUN_B6); + + /* i == 8 */ + TEST_ASSERT(sm[port].seq[11] == EXIT_B6); + TEST_ASSERT(sm[port].seq[12] == ENTER_C); + + /* i == 9 */ + TEST_ASSERT(sm[port].seq[13] == RUN_C); + + /* i == 10 */ + TEST_ASSERT(sm[port].seq[14] == EXIT_C); + TEST_ASSERT(sm[port].seq[15] == ENTER_A7); + + /* i == 11 */ + TEST_ASSERT(sm[port].seq[16] == RUN_A7); + + /* i == 12 */ + TEST_ASSERT(sm[port].seq[17] == EXIT_A7); + TEST_ASSERT(sm[port].seq[18] == ENTER_A6); + + /* i == 13 */ + TEST_ASSERT(sm[port].seq[19] == RUN_A6); + + /* i == 14 */ + TEST_ASSERT(sm[port].seq[20] == EXIT_A6); + TEST_ASSERT(sm[port].seq[21] == ENTER_A5); + + /* i == 15 */ + TEST_ASSERT(sm[port].seq[22] == RUN_A5); + + /* i == 16 */ + TEST_ASSERT(sm[port].seq[23] == EXIT_A5); + TEST_ASSERT(sm[port].seq[24] == ENTER_A4); + + for (i = 25; i < SEQUENCE_SIZE; i++) + TEST_ASSERT(sm[port].seq[i] == 0); + + return EC_SUCCESS; +} +#endif + + +#if defined(TEST_USB_SM_FRAMEWORK_H1) +static int test_hierarchy_1(void) +{ + int port = PORT0; + int i; + + clear_seq(port); + init_state(port, TSM_OBJ(port), sm_test_A4); + + for (i = 0; i < 17; i++) { + task_wake(TASK_ID_TEST); + task_wait_event(5 * MSEC); + } + + /* i == 0 */ + TEST_ASSERT(sm[port].seq[0] == ENTER_A3); + TEST_ASSERT(sm[port].seq[1] == ENTER_A4); + + /* i == 1 */ + TEST_ASSERT(sm[port].seq[2] == RUN_A4); + TEST_ASSERT(sm[port].seq[3] == RUN_A3); + + /* i == 2 */ + TEST_ASSERT(sm[port].seq[4] == EXIT_A4); + TEST_ASSERT(sm[port].seq[5] == EXIT_A3); + TEST_ASSERT(sm[port].seq[6] == ENTER_B3); + TEST_ASSERT(sm[port].seq[7] == ENTER_B4); + + /* i == 3 */ + TEST_ASSERT(sm[port].seq[8] == RUN_B4); + TEST_ASSERT(sm[port].seq[9] == RUN_B3); + + /* i == 4 */ + TEST_ASSERT(sm[port].seq[10] == EXIT_B4); + TEST_ASSERT(sm[port].seq[11] == EXIT_B3); + TEST_ASSERT(sm[port].seq[12] == ENTER_B5); + + /* i == 5 */ + TEST_ASSERT(sm[port].seq[13] == RUN_B5); + + /* i == 6 */ + TEST_ASSERT(sm[port].seq[14] == EXIT_B5); + TEST_ASSERT(sm[port].seq[15] == ENTER_B6); + + /* i == 7 */ + TEST_ASSERT(sm[port].seq[16] == RUN_B6); + + /* i == 8 */ + TEST_ASSERT(sm[port].seq[17] == EXIT_B6); + TEST_ASSERT(sm[port].seq[18] == ENTER_C); + + /* i == 9 */ + TEST_ASSERT(sm[port].seq[19] == RUN_C); + + /* i == 10 */ + TEST_ASSERT(sm[port].seq[20] == EXIT_C); + TEST_ASSERT(sm[port].seq[21] == ENTER_A7); + + /* i == 11 */ + TEST_ASSERT(sm[port].seq[22] == RUN_A7); + + /* i == 12 */ + TEST_ASSERT(sm[port].seq[23] == EXIT_A7); + TEST_ASSERT(sm[port].seq[24] == ENTER_A6); + + /* i == 13 */ + TEST_ASSERT(sm[port].seq[25] == RUN_A6); + + /* i == 14 */ + TEST_ASSERT(sm[port].seq[26] == EXIT_A6); + TEST_ASSERT(sm[port].seq[27] == ENTER_A3); + TEST_ASSERT(sm[port].seq[28] == ENTER_A5); + + /* i == 15 */ + TEST_ASSERT(sm[port].seq[29] == RUN_A5); + TEST_ASSERT(sm[port].seq[30] == RUN_A3); + + /* i == 16 */ + TEST_ASSERT(sm[port].seq[31] == EXIT_A5); + TEST_ASSERT(sm[port].seq[32] == ENTER_A4); + + for (i = 33; i < SEQUENCE_SIZE; i++) + TEST_ASSERT(sm[port].seq[i] == 0); + + return EC_SUCCESS; +} +#endif + + +#if defined(TEST_USB_SM_FRAMEWORK_H2) +static int test_hierarchy_2(void) +{ + + int port = PORT0; + int i; + + clear_seq(port); + init_state(port, TSM_OBJ(port), sm_test_A4); + + for (i = 0; i < 17; i++) { + task_wake(TASK_ID_TEST); + task_wait_event(5 * MSEC); + } + + /* i == 0 */ + TEST_ASSERT(sm[port].seq[0] == ENTER_A2); + TEST_ASSERT(sm[port].seq[1] == ENTER_A3); + TEST_ASSERT(sm[port].seq[2] == ENTER_A4); + + /* i == 1 */ + TEST_ASSERT(sm[port].seq[3] == RUN_A4); + TEST_ASSERT(sm[port].seq[4] == RUN_A3); + TEST_ASSERT(sm[port].seq[5] == RUN_A2); + + /* i == 2 */ + TEST_ASSERT(sm[port].seq[6] == EXIT_A4); + TEST_ASSERT(sm[port].seq[7] == EXIT_A3); + TEST_ASSERT(sm[port].seq[8] == EXIT_A2); + TEST_ASSERT(sm[port].seq[9] == ENTER_B2); + TEST_ASSERT(sm[port].seq[10] == ENTER_B3); + TEST_ASSERT(sm[port].seq[11] == ENTER_B4); + + /* i == 3 */ + TEST_ASSERT(sm[port].seq[12] == RUN_B4); + TEST_ASSERT(sm[port].seq[13] == RUN_B3); + TEST_ASSERT(sm[port].seq[14] == RUN_B2); + + /* i == 4 */ + TEST_ASSERT(sm[port].seq[15] == EXIT_B4); + TEST_ASSERT(sm[port].seq[16] == EXIT_B3); + TEST_ASSERT(sm[port].seq[17] == ENTER_B5); + + /* i == 5 */ + TEST_ASSERT(sm[port].seq[18] == RUN_B5); + TEST_ASSERT(sm[port].seq[19] == RUN_B2); + + /* i == 6 */ + TEST_ASSERT(sm[port].seq[20] == EXIT_B5); + TEST_ASSERT(sm[port].seq[21] == EXIT_B2); + TEST_ASSERT(sm[port].seq[22] == ENTER_B6); + + /* i == 7 */ + TEST_ASSERT(sm[port].seq[23] == RUN_B6); + + /* i == 8 */ + TEST_ASSERT(sm[port].seq[24] == EXIT_B6); + TEST_ASSERT(sm[port].seq[25] == ENTER_C); + + /* i == 9 */ + TEST_ASSERT(sm[port].seq[26] == RUN_C); + + /* i == 10 */ + TEST_ASSERT(sm[port].seq[27] == EXIT_C); + TEST_ASSERT(sm[port].seq[28] == ENTER_A7); + + /* i == 11 */ + TEST_ASSERT(sm[port].seq[29] == RUN_A7); + + /* i == 12 */ + TEST_ASSERT(sm[port].seq[30] == EXIT_A7); + TEST_ASSERT(sm[port].seq[31] == ENTER_A2); + TEST_ASSERT(sm[port].seq[32] == ENTER_A6); + + /* i == 13 */ + TEST_ASSERT(sm[port].seq[33] == RUN_A6); + TEST_ASSERT(sm[port].seq[34] == RUN_A2); + + /* i == 14 */ + TEST_ASSERT(sm[port].seq[35] == EXIT_A6); + TEST_ASSERT(sm[port].seq[36] == ENTER_A3); + TEST_ASSERT(sm[port].seq[37] == ENTER_A5); + + /* i == 15 */ + TEST_ASSERT(sm[port].seq[38] == RUN_A5); + TEST_ASSERT(sm[port].seq[39] == RUN_A3); + TEST_ASSERT(sm[port].seq[40] == RUN_A2); + + /* i == 16 */ + TEST_ASSERT(sm[port].seq[41] == EXIT_A5); + TEST_ASSERT(sm[port].seq[42] == ENTER_A4); + + for (i = 43; i < SEQUENCE_SIZE; i++) + TEST_ASSERT(sm[port].seq[i] == 0); + + return EC_SUCCESS; +} +#endif + +#if defined(TEST_USB_SM_FRAMEWORK_H3) +static int test_hierarchy_3(void) +{ + + int port = PORT0; + int i; + + clear_seq(port); + init_state(port, TSM_OBJ(port), sm_test_A4); + + for (i = 0; i < 17; i++) { + task_wake(TASK_ID_TEST); + task_wait_event(5 * MSEC); + } + + /* i == 0 */ + TEST_ASSERT(sm[port].seq[0] == ENTER_A1); + TEST_ASSERT(sm[port].seq[1] == ENTER_A2); + TEST_ASSERT(sm[port].seq[2] == ENTER_A3); + TEST_ASSERT(sm[port].seq[3] == ENTER_A4); + + /* i == 1 */ + TEST_ASSERT(sm[port].seq[4] == RUN_A4); + TEST_ASSERT(sm[port].seq[5] == RUN_A3); + TEST_ASSERT(sm[port].seq[6] == RUN_A2); + TEST_ASSERT(sm[port].seq[7] == RUN_A1); + + /* i == 2 */ + TEST_ASSERT(sm[port].seq[8] == EXIT_A4); + TEST_ASSERT(sm[port].seq[9] == EXIT_A3); + TEST_ASSERT(sm[port].seq[10] == EXIT_A2); + TEST_ASSERT(sm[port].seq[11] == EXIT_A1); + TEST_ASSERT(sm[port].seq[12] == ENTER_B1); + TEST_ASSERT(sm[port].seq[13] == ENTER_B2); + TEST_ASSERT(sm[port].seq[14] == ENTER_B3); + TEST_ASSERT(sm[port].seq[15] == ENTER_B4); + + /* i == 3 */ + TEST_ASSERT(sm[port].seq[16] == RUN_B4); + TEST_ASSERT(sm[port].seq[17] == RUN_B3); + TEST_ASSERT(sm[port].seq[18] == RUN_B2); + TEST_ASSERT(sm[port].seq[19] == RUN_B1); + + /* i == 4 */ + TEST_ASSERT(sm[port].seq[20] == EXIT_B4); + TEST_ASSERT(sm[port].seq[21] == EXIT_B3); + TEST_ASSERT(sm[port].seq[22] == ENTER_B5); + + /* i == 5 */ + TEST_ASSERT(sm[port].seq[23] == RUN_B5); + TEST_ASSERT(sm[port].seq[24] == RUN_B2); + TEST_ASSERT(sm[port].seq[25] == RUN_B1); + + /* i == 6 */ + TEST_ASSERT(sm[port].seq[26] == EXIT_B5); + TEST_ASSERT(sm[port].seq[27] == EXIT_B2); + TEST_ASSERT(sm[port].seq[28] == ENTER_B6); + + /* i == 7 */ + TEST_ASSERT(sm[port].seq[29] == RUN_B6); + TEST_ASSERT(sm[port].seq[30] == RUN_B1); + + /* i == 8 */ + TEST_ASSERT(sm[port].seq[31] == EXIT_B6); + TEST_ASSERT(sm[port].seq[32] == EXIT_B1); + TEST_ASSERT(sm[port].seq[33] == ENTER_C); + + /* i == 9 */ + TEST_ASSERT(sm[port].seq[34] == RUN_C); + + /* i == 10 */ + TEST_ASSERT(sm[port].seq[35] == EXIT_C); + TEST_ASSERT(sm[port].seq[36] == ENTER_A1); + TEST_ASSERT(sm[port].seq[37] == ENTER_A7); + + /* i == 11 */ + TEST_ASSERT(sm[port].seq[38] == RUN_A7); + TEST_ASSERT(sm[port].seq[39] == RUN_A1); + + /* i == 12 */ + TEST_ASSERT(sm[port].seq[40] == EXIT_A7); + TEST_ASSERT(sm[port].seq[41] == ENTER_A2); + TEST_ASSERT(sm[port].seq[42] == ENTER_A6); + + /* i == 13 */ + TEST_ASSERT(sm[port].seq[43] == RUN_A6); + TEST_ASSERT(sm[port].seq[44] == RUN_A2); + TEST_ASSERT(sm[port].seq[45] == RUN_A1); + + /* i == 14 */ + TEST_ASSERT(sm[port].seq[46] == EXIT_A6); + TEST_ASSERT(sm[port].seq[47] == ENTER_A3); + TEST_ASSERT(sm[port].seq[48] == ENTER_A5); + + /* i == 15 */ + TEST_ASSERT(sm[port].seq[49] == RUN_A5); + TEST_ASSERT(sm[port].seq[50] == RUN_A3); + TEST_ASSERT(sm[port].seq[51] == RUN_A2); + TEST_ASSERT(sm[port].seq[52] == RUN_A1); + + /* i == 16 */ + TEST_ASSERT(sm[port].seq[53] == EXIT_A5); + TEST_ASSERT(sm[port].seq[54] == ENTER_A4); + + return EC_SUCCESS; +} +#endif + +int test_task(void *u) +{ + int port = PORT0; + + while (1) { + /* wait for next event/packet or timeout expiration */ + task_wait_event(-1); + /* run state machine */ + exe_state(port, TSM_OBJ(port), RUN_SIG); + } + + return EC_SUCCESS; +} + +void run_test(void) +{ + test_reset(); +#if defined(TEST_USB_SM_FRAMEWORK_H3) + RUN_TEST(test_hierarchy_3); +#elif defined(TEST_USB_SM_FRAMEWORK_H2) + RUN_TEST(test_hierarchy_2); +#elif defined(TEST_USB_SM_FRAMEWORK_H1) + RUN_TEST(test_hierarchy_1); +#else + RUN_TEST(test_hierarchy_0); +#endif + test_print_result(); +} diff --git a/test/usb_sm_framework_h3.tasklist b/test/usb_sm_framework_h3.tasklist new file mode 100644 index 0000000000..d1b4e6e2ca --- /dev/null +++ b/test/usb_sm_framework_h3.tasklist @@ -0,0 +1,18 @@ +/* Copyright (c) 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. + */ + +/** + * List of enabled tasks in the priority order + * + * The first one has the lowest priority. + * + * For each task, use the macro TASK_TEST(n, r, d, s) where : + * 'n' in the name of the task + * 'r' in the main routine of the task + * 'd' in an opaque parameter passed to the routine at startup + * 's' is the stack size in bytes; must be a multiple of 8 + */ +#define CONFIG_TEST_TASK_LIST \ + TASK_TEST(TEST, test_task, NULL, LARGER_TASK_STACK_SIZE) 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(); +} + diff --git a/test/usb_typec_ctvpd.tasklist b/test/usb_typec_ctvpd.tasklist new file mode 100644 index 0000000000..96ce0f08eb --- /dev/null +++ b/test/usb_typec_ctvpd.tasklist @@ -0,0 +1,18 @@ +/* Copyright (c) 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. + */ + +/** + * List of enabled tasks in the priority order + * + * The first one has the lowest priority. + * + * For each task, use the macro TASK_TEST(n, r, d, s) where : + * 'n' in the name of the task + * 'r' in the main routine of the task + * 'd' in an opaque parameter passed to the routine at startup + * 's' is the stack size in bytes; must be a multiple of 8 + */ +#define CONFIG_TEST_TASK_LIST \ + TASK_TEST(PD_C0, pd_task, NULL, LARGER_TASK_STACK_SIZE) diff --git a/test/usb_typec_vpd.tasklist b/test/usb_typec_vpd.tasklist new file mode 120000 index 0000000000..3e39415ded --- /dev/null +++ b/test/usb_typec_vpd.tasklist @@ -0,0 +1 @@ +usb_typec_ctvpd.tasklist
\ No newline at end of file diff --git a/test/vpd_api.c b/test/vpd_api.c new file mode 100644 index 0000000000..960c0c664b --- /dev/null +++ b/test/vpd_api.c @@ -0,0 +1,586 @@ +/* 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 "registers.h" +#include "vpd_api.h" +#include "driver/tcpm/tcpm.h" +#include "console.h" +/* + * Polarity based on 'DFP Perspective' (see table USB Type-C Cable and Connector + * Specification) + * + * CC1 CC2 STATE POSITION + * ---------------------------------------- + * open open NC N/A + * Rd open UFP attached 1 + * open Rd UFP attached 2 + * open Ra pwr cable no UFP N/A + * Ra open pwr cable no UFP N/A + * Rd Ra pwr cable & UFP 1 + * Ra Rd pwr cable & UFP 2 + * Rd Rd dbg accessory N/A + * Ra Ra audio accessory N/A + * + * Note, V(Rd) > V(Ra) + */ +#ifndef PD_SRC_RD_THRESHOLD +#define PD_SRC_RD_THRESHOLD PD_SRC_DEF_RD_THRESH_MV +#endif +#ifndef PD_SRC_VNC +#define PD_SRC_VNC PD_SRC_DEF_VNC_MV +#endif + +#ifndef CC_RA +#define CC_RA(port, cc, sel) (cc < pd_src_rd_threshold[ct_cc_rp_value]) +#endif +#define CC_RD(cc) ((cc >= PD_SRC_RD_THRESHOLD) && (cc < PD_SRC_VNC)) +#ifndef CC_NC +#define CC_NC(port, cc, sel) (cc >= PD_SRC_VNC) +#endif + +/* + * Polarity based on 'UFP Perspective'. + * + * CC1 CC2 STATE POSITION + * ---------------------------------------- + * open open NC N/A + * Rp open DFP attached 1 + * open Rp DFP attached 2 + * Rp Rp Accessory attached N/A + */ +#ifndef PD_SNK_VA +#define PD_SNK_VA PD_SNK_VA_MV +#endif + +#define CC_RP(cc) (cc >= PD_SNK_VA) + +/* Mock Board State */ +static enum vpd_pwr mock_vconn_pwr_sel_odl; +static enum vpd_gpo mock_cc1_cc2_rd_l; +static enum vpd_gpo mock_cc_db_en_od; +static enum vpd_gpo mock_cc_rpusb_odh; +static enum vpd_cc mock_ct_cl_sel; +static int mock_mcu_cc_en; +static enum vpd_billboard mock_present_billboard; +static int mock_red_led; +static int mock_green_led; +static int mock_vbus_pass_en; + +static int mock_read_host_vbus; +static int mock_read_ct_vbus; +static int mock_read_vconn; + +static struct mock_pin mock_cc2_rpusb_odh; +static struct mock_pin mock_cc2_rp3a0_rd_l; +static struct mock_pin mock_cc1_rpusb_odh; +static struct mock_pin mock_cc1_rp3a0_rd_l; +static struct mock_pin mock_cc_vpdmcu; +static struct mock_pin mock_cc_rp3a0_rd_l; + +/* Charge-Through pull up/down enabled */ +static int ct_cc_pull; +/* Charge-Through pull up value */ +static int ct_cc_rp_value; + +/* Charge-Through pull up/down enabled */ +static int host_cc_pull; +/* Charge-Through pull up value */ +static int host_cc_rp_value; + +/* Voltage thresholds for Ra attach in normal SRC mode */ +static int pd_src_rd_threshold[TYPEC_RP_RESERVED] = { + PD_SRC_DEF_RD_THRESH_MV, + PD_SRC_1_5_RD_THRESH_MV, + PD_SRC_3_0_RD_THRESH_MV, +}; + +enum vpd_pwr mock_get_vconn_pwr_source(void) +{ + return mock_vconn_pwr_sel_odl; +} + +int mock_get_ct_cc1_rpusb(void) +{ + return mock_cc1_rpusb_odh.value; +} + +int mock_get_ct_cc2_rpusb(void) +{ + return mock_cc2_rpusb_odh.value; +} + +enum vpd_gpo mock_get_ct_rd(void) +{ + return mock_cc1_cc2_rd_l; +} + +enum vpd_gpo mock_get_cc_rpusb_odh(void) +{ + return mock_cc_rpusb_odh; +} + +enum vpd_gpo mock_get_cc_db_en_od(void) +{ + return mock_cc_db_en_od; +} + +enum vpd_cc moch_get_ct_cl_sel(void) +{ + return mock_ct_cl_sel; +} + +int mock_get_mcu_cc_en(void) +{ + return mock_mcu_cc_en; +} + +enum vpd_billboard mock_get_present_billboard(void) +{ + return mock_present_billboard; +} + +int mock_get_red_led(void) +{ + return mock_red_led; +} + +int mock_get_green_led(void) +{ + return mock_green_led; +} + +int mock_get_vbus_pass_en(void) +{ + return mock_vbus_pass_en; +} + +void mock_set_host_cc_sink_voltage(int v) +{ + mock_cc_vpdmcu.value = v; +} + +void mock_set_host_cc_source_voltage(int v) +{ + mock_cc_vpdmcu.value2 = v; +} + +void mock_set_host_vbus(int v) +{ + mock_read_host_vbus = v; +} + +void mock_set_ct_vbus(int v) +{ + mock_read_ct_vbus = v; +} + +void mock_set_vconn(int v) +{ + mock_read_vconn = v; +} + +int mock_get_cfg_cc2_rpusb_odh(void) +{ + return mock_cc2_rpusb_odh.cfg; +} + +int mock_set_cc2_rpusb_odh(int v) +{ + if (mock_cc2_rpusb_odh.cfg == PIN_ADC) { + mock_cc2_rpusb_odh.value = v; + return 1; + } + return 0; +} + +int mock_get_cfg_cc2_rp3a0_rd_l(void) +{ + return mock_cc2_rp3a0_rd_l.cfg; +} + +int mock_set_cc2_rp3a0_rd_l(int v) +{ + if (mock_cc2_rp3a0_rd_l.cfg == PIN_ADC) { + mock_cc2_rp3a0_rd_l.value = v; + return 1; + } + + return 0; +} + +int mock_get_cc1_rpusb_odh(void) +{ + return mock_cc1_rpusb_odh.cfg; +} + +int mock_set_cc1_rpusb_odh(int v) +{ + if (mock_cc1_rpusb_odh.cfg == PIN_ADC) { + mock_cc1_rpusb_odh.value = v; + return 1; + } + + return 0; +} + +int mock_get_cfg_cc_vpdmcu(void) +{ + return mock_cc_vpdmcu.cfg; +} + +enum vpd_pin mock_get_cfg_cc_rp3a0_rd_l(void) +{ + return mock_cc_rp3a0_rd_l.cfg; +} + +int mock_get_cc_rp3a0_rd_l(void) +{ + return mock_cc_rp3a0_rd_l.value; +} + +int mock_get_cfg_cc1_rp3a0_rd_l(void) +{ + return mock_cc1_rp3a0_rd_l.cfg; +} + +int mock_set_cc1_rp3a0_rd_l(int v) +{ + if (mock_cc1_rp3a0_rd_l.cfg == PIN_ADC) { + mock_cc1_rp3a0_rd_l.value = v; + return 1; + } + + return 0; +} + +/* Convert CC voltage to CC status */ +static int vpd_cc_voltage_to_status(int cc_volt, int cc_pull) +{ + /* If we have a pull-up, then we are source, check for Rd. */ + if (cc_pull == TYPEC_CC_RP) { + if (CC_NC(0, cc_volt, 0)) + return TYPEC_CC_VOLT_OPEN; + else if (CC_RA(0, cc_volt, 0)) + return TYPEC_CC_VOLT_RA; + else + return TYPEC_CC_VOLT_RD; + /* If we have a pull-down, then we are sink, check for Rp. */ + } else if (cc_pull == TYPEC_CC_RD || cc_pull == TYPEC_CC_RA_RD) { + if (cc_volt >= TYPE_C_SRC_3000_THRESHOLD) + return TYPEC_CC_VOLT_RP_3_0; + else if (cc_volt >= TYPE_C_SRC_1500_THRESHOLD) + return TYPEC_CC_VOLT_RP_1_5; + else if (CC_RP(cc_volt)) + return TYPEC_CC_VOLT_RP_DEF; + else + return TYPEC_CC_VOLT_OPEN; + } else { + /* If we are open, then always return 0 */ + return 0; + } +} + +void vpd_ct_set_pull(int pull, int rp_value) +{ + ct_cc_pull = pull; + + switch (pull) { + case TYPEC_CC_RP: + ct_cc_rp_value = rp_value; + vpd_cc1_cc2_db_en_l(GPO_HIGH); + switch (rp_value) { + case TYPEC_RP_USB: + vpd_config_cc1_rp3a0_rd_l(PIN_ADC, 0); + vpd_config_cc2_rp3a0_rd_l(PIN_ADC, 0); + vpd_config_cc1_rpusb_odh(PIN_GPO, 1); + vpd_config_cc2_rpusb_odh(PIN_GPO, 1); + break; + case TYPEC_RP_3A0: + vpd_config_cc1_rpusb_odh(PIN_ADC, 0); + vpd_config_cc2_rpusb_odh(PIN_ADC, 0); + vpd_config_cc1_rp3a0_rd_l(PIN_GPO, 1); + vpd_config_cc2_rp3a0_rd_l(PIN_GPO, 1); + break; + } + break; + case TYPEC_CC_RD: + vpd_config_cc1_rpusb_odh(PIN_ADC, 0); + vpd_config_cc2_rpusb_odh(PIN_ADC, 0); + vpd_config_cc1_rp3a0_rd_l(PIN_ADC, 0); + vpd_config_cc2_rp3a0_rd_l(PIN_ADC, 0); + vpd_cc1_cc2_db_en_l(GPO_LOW); + break; + case TYPEC_CC_OPEN: + vpd_cc1_cc2_db_en_l(GPO_HIGH); + vpd_config_cc1_rpusb_odh(PIN_ADC, 0); + vpd_config_cc2_rpusb_odh(PIN_ADC, 0); + vpd_config_cc1_rp3a0_rd_l(PIN_ADC, 0); + vpd_config_cc2_rp3a0_rd_l(PIN_ADC, 0); + break; + } +} + +void vpd_ct_get_cc(int *cc1, int *cc2) +{ + int cc1_v; + int cc2_v; + + switch (ct_cc_pull) { + case TYPEC_CC_RP: + switch (ct_cc_rp_value) { + case TYPEC_RP_USB: + cc1_v = mock_cc1_rp3a0_rd_l.value; + cc2_v = mock_cc2_rp3a0_rd_l.value; + break; + case TYPEC_RP_3A0: + cc1_v = mock_cc1_rpusb_odh.value; + cc2_v = mock_cc2_rpusb_odh.value; + break; + } + + if (!cc1_v && !cc2_v) { + cc1_v = PD_SRC_VNC; + cc2_v = PD_SRC_VNC; + } + break; + case TYPEC_CC_RD: + cc1_v = mock_cc1_rpusb_odh.value; + cc2_v = mock_cc2_rpusb_odh.value; + break; + case TYPEC_CC_OPEN: + *cc1 = 0; + *cc2 = 0; + return; + } + + *cc1 = vpd_cc_voltage_to_status(cc1_v, ct_cc_pull); + *cc2 = vpd_cc_voltage_to_status(cc2_v, ct_cc_pull); +} + +void vpd_host_set_pull(int pull, int rp_value) +{ + host_cc_pull = pull; + + switch (pull) { + case TYPEC_CC_RP: + vpd_cc_db_en_od(GPO_LOW); + host_cc_rp_value = rp_value; + switch (rp_value) { + case TYPEC_RP_USB: + vpd_config_cc_rp3a0_rd_l(PIN_CMP, 0); + vpd_cc_rpusb_odh(GPO_HIGH); + break; + case TYPEC_RP_3A0: + vpd_cc_rpusb_odh(GPO_HZ); + vpd_config_cc_rp3a0_rd_l(PIN_GPO, 1); + break; + } + break; + case TYPEC_CC_RD: + vpd_cc_rpusb_odh(GPO_HZ); + vpd_cc_db_en_od(GPO_LOW); + + vpd_config_cc_rp3a0_rd_l(PIN_GPO, 0); + break; + case TYPEC_CC_RA_RD: + vpd_cc_rpusb_odh(GPO_HZ); + vpd_config_cc_rp3a0_rd_l(PIN_GPO, 0); + + /* + * RA is connected to VCONN + * RD is connected to CC + */ + vpd_cc_db_en_od(GPO_HZ); + break; + case TYPEC_CC_OPEN: + vpd_cc_rpusb_odh(GPO_HZ); + vpd_config_cc_rp3a0_rd_l(PIN_CMP, 0); + vpd_cc_db_en_od(GPO_LOW); + + /* + * Do nothing. CC is open on entry to this function + */ + break; + } +} + +void vpd_host_get_cc(int *cc) +{ + int v; + + if (host_cc_pull == TYPEC_CC_OPEN) { + *cc = 0; + return; + } else if (host_cc_pull == TYPEC_CC_RP) { + v = mock_cc_vpdmcu.value; + } else { + v = mock_cc_vpdmcu.value2; + } + + *cc = vpd_cc_voltage_to_status(v, host_cc_pull); +} + +void vpd_rx_enable(int en) +{ + if (en) { + mock_ct_cl_sel = 0; + mock_mcu_cc_en = 1; + } + + tcpm_set_polarity(0, 0); + tcpm_set_rx_enable(0, en); +} + +/* + * PA1: Configure as ADC, CMP, or GPO + */ +void vpd_config_cc_vpdmcu(enum vpd_pin cfg, int en) +{ + mock_cc_vpdmcu.cfg = cfg; + + if (cfg == PIN_GPO) + mock_cc_vpdmcu.value = en ? 1 : 0; +} + +/* + * PA2: Configure as COMP2_INM6 or GPO + */ +void vpd_config_cc_rp3a0_rd_l(enum vpd_pin cfg, int en) +{ + mock_cc_rp3a0_rd_l.cfg = cfg; + + if (cfg == PIN_GPO) + mock_cc_rp3a0_rd_l.value = en ? 1 : 0; +} + +/* + * PA4: Configure as ADC, CMP, or GPO + */ +void vpd_config_cc1_rp3a0_rd_l(enum vpd_pin cfg, int en) +{ + mock_cc1_rp3a0_rd_l.cfg = cfg; + + if (cfg == PIN_GPO) + mock_cc1_rp3a0_rd_l.value = en ? 1 : 0; +} + +/* + * PA5: Configure as ADC, COMP, or GPO + */ +void vpd_config_cc2_rp3a0_rd_l(enum vpd_pin cfg, int en) +{ + mock_cc2_rp3a0_rd_l.cfg = cfg; + + if (cfg == PIN_GPO) + mock_cc2_rp3a0_rd_l.value = en ? 1 : 0; +} + +/* + * PB0: Configure as ADC or GPO + */ +void vpd_config_cc1_rpusb_odh(enum vpd_pin cfg, int en) +{ + mock_cc1_rpusb_odh.cfg = cfg; + + if (cfg == PIN_GPO) + mock_cc1_rpusb_odh.value = en ? 1 : 0; +} + +/* + * PB1: Configure as ADC or GPO + */ +void vpd_config_cc2_rpusb_odh(enum vpd_pin cfg, int en) +{ + mock_cc2_rpusb_odh.cfg = cfg; + + if (cfg == PIN_GPO) + mock_cc2_rpusb_odh.value = en ? 1 : 0; +} + +int vpd_read_host_vbus(void) +{ + return mock_read_host_vbus; +} + +int vpd_read_ct_vbus(void) +{ + return mock_read_ct_vbus; +} + +int vpd_read_vconn(void) +{ + return mock_read_vconn; +} + +int vpd_is_host_vbus_present(void) +{ + return (vpd_read_host_vbus() >= PD_SNK_VA); +} + +int vpd_is_ct_vbus_present(void) +{ + return (vpd_read_ct_vbus() >= PD_SNK_VA); +} + +int vpd_is_vconn_present(void) +{ + return (vpd_read_vconn() >= PD_SNK_VA); +} + +int vpd_read_rdconnect_ref(void) +{ + return 200; /* 200 mV */ +} + +void vpd_red_led(int on) +{ + mock_red_led = on ? 0 : 1; +} + +void vpd_green_led(int on) +{ + mock_green_led = on ? 0 : 1; +} + +void vpd_vbus_pass_en(int en) +{ + mock_vbus_pass_en = en ? 1 : 0; +} + +void vpd_present_billboard(enum vpd_billboard bb) +{ + mock_present_billboard = bb; +} + +void vpd_mcu_cc_en(int en) +{ + mock_mcu_cc_en = en ? 1 : 0; +} + +void vpd_ct_cc_sel(enum vpd_cc sel) +{ + mock_ct_cl_sel = sel; +} + +/* Set as GPO High, GPO Low, or High-Z */ +void vpd_cc_db_en_od(enum vpd_gpo val) +{ + mock_cc_db_en_od = val; +} + +void vpd_cc_rpusb_odh(enum vpd_gpo val) +{ + mock_cc_rpusb_odh = val; +} + +void vpd_cc1_cc2_db_en_l(enum vpd_gpo val) +{ + mock_cc1_cc2_rd_l = val; +} + +void vpd_vconn_pwr_sel_odl(enum vpd_pwr en) +{ + mock_vconn_pwr_sel_odl = en; +} diff --git a/test/vpd_api.h b/test/vpd_api.h new file mode 100644 index 0000000000..3db4803288 --- /dev/null +++ b/test/vpd_api.h @@ -0,0 +1,333 @@ +/* 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. + */ + +/* Vconn Power Device API module */ + +#ifndef __CROS_EC_VPD_API_H +#define __CROS_EC_VPD_API_H + +#include "adc.h" +#include "gpio.h" +#include "usb_pd.h" + +/* + * Type C power source charge current limits are identified by their cc + * voltage (set by selecting the proper Rd resistor). Any voltage below + * TYPE_C_SRC_DEFAULT_THRESHOLD will not be identified as a type C charger. + */ +#define TYPE_C_SRC_DEFAULT_THRESHOLD 200 /* mV */ +#define TYPE_C_SRC_1500_THRESHOLD 660 /* mV */ +#define TYPE_C_SRC_3000_THRESHOLD 1230 /* mV */ + + +enum vpd_pin { + PIN_ADC, + PIN_CMP, + PIN_GPO +}; + +enum vpd_gpo { + GPO_HZ, + GPO_HIGH, + GPO_LOW +}; + +enum vpd_pwr { + PWR_VCONN, + PWR_VBUS, +}; + +enum vpd_cc { + CT_OPEN, + CT_CC1, + CT_CC2 +}; + +enum vpd_billboard { + BB_NONE, + BB_SRC, + BB_SNK +}; + +struct mock_pin { + enum vpd_pin cfg; + int value; + int value2; +}; + +enum vpd_pwr mock_get_vconn_pwr_source(void); +enum vpd_gpo mock_get_ct_rd(void); +enum vpd_gpo mock_get_cc_rp1a5_odh(void); +enum vpd_gpo mock_get_cc_rpusb_odh(void); +enum vpd_gpo mock_get_cc_db_en_od(void); +enum vpd_cc moch_get_ct_cl_sel(void); +int mock_get_mcu_cc_en(void); +enum vpd_billboard mock_get_present_billboard(void); +int mock_get_red_led(void); +int mock_get_green_led(void); +int mock_get_vbus_pass_en(void); +int mock_set_cc_vpdmcu(int v); +void mock_set_host_vbus(int v); +void mock_set_ct_vbus(int v); +void mock_set_vconn(int v); +int mock_get_cfg_cc2_rpusb_odh(void); +int mock_set_cc2_rpusb_odh(int v); +int mock_get_cfg_cc2_rp3a0_rd_l(void); +int mock_set_cc2_rp3a0_rd_l(int v); +int mock_get_cfg_cc1_rpusb_odh(void); +int mock_set_cc1_rpusb_odh(int v); +int mock_get_cfg_cc_vpdmcu(void); +int mock_get_cc_vpdmcu(int v); +enum vpd_pin mock_get_cfg_cc_rp3a0_rd_l(void); +int mock_get_cc_rp3a0_rd_l(void); +int mock_get_cfg_cc1_rp3a0_rd_l(void); +int mock_set_cc1_rp3a0_rd_l(int v); +void mock_set_host_cc_sink_voltage(int v); +void mock_set_host_cc_source_voltage(int v); +int mock_get_ct_cc1_rpusb(void); +int mock_get_ct_cc2_rpusb(void); + +/** + * Set Charge-Through Rp or Rd on CC lines + * + * @param pull Either TYPEC_CC_RP or TYPEC_CC_RD + * @param rp_value When pull is RP, set this to + * TYPEC_RP_USB or TYPEC_RP_1A5. Ignored + * for TYPEC_CC_RD + */ +void vpd_ct_set_pull(int pull, int rp_value); + +/** + * Get the status of the Charge-Through CC lines + * + * @param cc1 Either TYPEC_CC_VOLT_OPEN, + * TYPEC_CC_VOLT_RA, + * TYPEC_CC_VOLT_RD, + * any other value is considered RP + * @param cc2 Either TYPEC_CC_VOLT_OPEN, + * TYPEC_CC_VOLT_RA, + * TYPEC_CC_VOLT_RD, + * any other value is considered RP + */ +void vpd_ct_get_cc(int *cc1, int *cc2); + +/** + * Set Host Rp or Rd on CC lines + * + * @param pull Either TYPEC_CC_RP or TYPEC_CC_RD + * @param rp_value When pull is RP, set this to + * TYPEC_RP_USB or TYPEC_RP_1A5. Ignored + * for TYPEC_CC_RD + */ +void vpd_host_set_pull(int pull, int rp_value); + +/** + * Get the status of the Host CC line + * + * @param cc Either TYPEC_CC_VOLT_SNK_DEF, TYPEC_CC_VOLT_SNK_1_5, + * TYPEC_CC_VOLT_SNK_3_0, or TYPEC_CC_RD + */ +void vpd_host_get_cc(int *cc); + +/** + * Set RX Enable flag + * + * @param en 1 for enable, 0 for disable + */ +void vpd_rx_enable(int en); + +/** + * Configure the cc_vpdmcu pin as ADC, CMP, or GPO + * + * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO + * @param en When cfg is PIN_GPO, 1 sets pin high + * and 0 sets pin low. Else ignored + */ +void vpd_config_cc_vpdmcu(enum vpd_pin cfg, int en); + +/** + * Configure the cc_rp3a0_rd_l pin as ADC, CMP, or GPO + * + * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO + * @param en When cfg is PIN_GPO, 1 sets pin high + * and 0 sets pin low. Else ignored + */ +void vpd_config_cc_rp3a0_rd_l(enum vpd_pin cfg, int en); + +/** + * Configure the cc1_rp3a0_rd_l pin as ADC, CMP, or GPO + * + * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO + * @param en When cfg is PIN_GPO, 1 sets pin high + * and 0 sets pin low. Else ignored + */ +void vpd_config_cc1_rp3a0_rd_l(enum vpd_pin cfg, int en); + +/** + * Configure the cc2_rp3a0_rd_l pin as ADC, CMP, or GPO + * + * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO + * @param en When cfg is PIN_GPO, 1 sets pin high + * and 0 sets pin low. Else ignored + */ +void vpd_config_cc2_rp3a0_rd_l(enum vpd_pin cfg, int en); + +/** + * Configure the cc1_rpusb_odh pin as ADC, CMP, or GPO + * + * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO + * @param en When cfg is PIN_GPO, 1 sets pin high + * and 0 sets pin low. Else ignored + */ +void vpd_config_cc1_rpusb_odh(enum vpd_pin cfg, int en); + +/** + * Configure the cc2_rpusb_odh pin as ADC, CMP, or GPO + * + * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO + * @param en When cfg is PIN_GPO, 1 sets pin high + * and 0 sets pin low. Else ignored + */ +void vpd_config_cc2_rpusb_odh(enum vpd_pin cfg, int en); + +/** + * Configure the cc_db_en_od pin to High-Impedance, low, or high + * + * @param val GPO_HZ, GPO_HIGH, GPO_LOW + */ +void vpd_cc_db_en_od(enum vpd_gpo val); + +/** + * Configure the cc_rpusb_odh pin to High-Impedance, low, or high + * + * @param val GPO_HZ, GPO_HIGH, GPO_LOW + */ +void vpd_cc_rpusb_odh(enum vpd_gpo val); + +/** + * Configure the cc_rp1a5_odh pin to High-Impedance, low, or high + * + * @param val GPO_HZ, GPO_HIGH, GPO_LOW + */ +void vpd_cc_rp1a5_odh(enum vpd_gpo val); + +/** + * Configure the cc1_cc2_db_en_l pin to High-Impedance, low, or high + * + * @param val GPO_HZ, GPO_HIGH, GPO_LOW + */ +void vpd_cc1_cc2_db_en_l(enum vpd_gpo val); + +/** + * Get status of host vbus + * + * @return 1 if host vbus is present, else 0 + */ +int vpd_is_host_vbus_present(void); + +/** + * Get status of charge-through vbus + * + * @return 1 if charge-through vbus is present, else 0 + */ +int vpd_is_ct_vbus_present(void); + +/** + * Get status of vconn + * + * @return 1 if vconn is present, else 0 + */ +int vpd_is_vconn_present(void); + +/** + * Read Host VBUS voltage. Range from 22000mV to 3000mV + * + * @return vbus voltage + */ +int vpd_read_host_vbus(void); + +/** + * Read Host CC voltage. + * + * @return cc voltage + */ +int vpd_read_cc_host(void); + +/** + * Read voltage on cc_vpdmcu pin + * + * @return cc_vpdmcu voltage + */ +int vpd_read_cc_vpdmcu(void); + +/** + * Read charge-through VBUS voltage. Range from 22000mV to 3000mV + * + * @return charge-through vbus voltage + */ +int vpd_read_ct_vbus(void); + +/** + * Read VCONN Voltage. Range from 5500mV to 3000mV + * + * @return vconn voltage + */ +int vpd_read_vconn(void); + +/** + * Turn ON/OFF Red LED. Should be off when performing power + * measurements. + * + * @param on 0 turns LED off, any other value turns it ON + */ +void vpd_red_led(int on); + +/** + * Turn ON/OFF Green LED. Should be off when performing power + * measurements. + * + * @param on 0 turns LED off, any other value turns it ON + */ +void vpd_green_led(int on); + +/** + * Connects/Disconnects the Host VBUS to the Charge-Through VBUS. + * + * @param en 0 disconnectes the VBUS, any other value connects VBUS. + */ +void vpd_vbus_pass_en(int en); + +/** + * Preset Billboard device + * + * @param bb BB_NONE no billboard presented, + * BB_SRC source connected but not in charge-through + * BB_SNK sink connected + */ +void vpd_present_billboard(enum vpd_billboard bb); + +/** + * Enables the MCU to host cc communication + * + * @param en 1 enabled, 0 disabled + */ +void vpd_mcu_cc_en(int en); + +/** + * Selects which supply to power the VPD from + * + * @param en PWR_VCONN or PWR_VBUS + */ +void vpd_vconn_pwr_sel_odl(enum vpd_pwr en); + +/** + * Controls if the Charge-Through's CC1, CC2, or neither is + * connected to Host CC + * + * @param sel CT_OPEN neither, CT_CC1 cc1, CT_CC2 cc2 + */ +void vpd_ct_cc_sel(enum vpd_cc sel); + +#endif /* __CROS_EC_VPD_API_H */ |