summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVic Yang <victoryang@chromium.org>2014-10-01 13:46:18 +0800
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-10-04 21:08:48 +0000
commitf0dc012cf75a02110f3ac98a4dbd1a395a70e794 (patch)
tree7b3e93ec612d16d8acd54b674c02a5c1507fdd66
parent15eced037401c1749b634786754df17d8c58285f (diff)
downloadchrome-ec-f0dc012cf75a02110f3ac98a4dbd1a395a70e794.tar.gz
Add back unit test for usb_pd"
This is mostly the same as previous commits, but with increased delay. Previously, we have short delays (e.g. 3ms) which is too short and may cause instability. Now that we have slowed down the time when running unit tests and increased the delay, this shouldn't cause problems anymore. BUG=chrome-os-partner:31200 TEST=Repeatedly run multiple unit tests in parallel. BRANCH=Samus Change-Id: Ib55e3adc5fd27a8e233996b4799dab3cefd62318 Signed-off-by: Vic Yang <victoryang@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/220734 Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--board/host/build.mk1
-rw-r--r--board/host/usb_pd_config.c35
-rw-r--r--board/host/usb_pd_config.h41
-rw-r--r--board/host/usb_pd_policy.c122
-rw-r--r--chip/host/build.mk1
-rw-r--r--chip/host/usb_pd_phy.c289
-rw-r--r--common/usb_pd_protocol.c26
-rw-r--r--include/usb_pd.h26
-rw-r--r--test/build.mk6
-rw-r--r--test/test_config.h7
-rw-r--r--test/usb_pd.c212
-rw-r--r--test/usb_pd.tasklist19
-rw-r--r--test/usb_pd_test_util.h31
13 files changed, 788 insertions, 28 deletions
diff --git a/board/host/build.mk b/board/host/build.mk
index 9b50c501ae..91451188a7 100644
--- a/board/host/build.mk
+++ b/board/host/build.mk
@@ -12,3 +12,4 @@ board-y=board.o
board-$(HAS_TASK_CHIPSET)+=chipset.o
board-$(CONFIG_BATTERY_MOCK)+=battery.o charger.o
board-$(CONFIG_FANS)+=fan.o
+board-$(CONFIG_USB_POWER_DELIVERY)+=usb_pd_policy.o usb_pd_config.o
diff --git a/board/host/usb_pd_config.c b/board/host/usb_pd_config.c
new file mode 100644
index 0000000000..aec495a0c9
--- /dev/null
+++ b/board/host/usb_pd_config.c
@@ -0,0 +1,35 @@
+/* Copyright (c) 2014 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.
+ */
+
+/* USB Power delivery board configuration */
+
+#include "test_util.h"
+
+test_mockable void pd_select_polarity(int port, int polarity)
+{
+ /* Not implemented */
+}
+
+test_mockable void pd_tx_init(void)
+{
+ /* Not implemented */
+}
+
+test_mockable void pd_set_host_mode(int port, int enable)
+{
+ /* Not implemented */
+}
+
+test_mockable int pd_adc_read(int port, int cc)
+{
+ /* Not implemented */
+ return 0;
+}
+
+test_mockable int pd_snk_is_vbus_provided(int port)
+{
+ /* Not implemented */
+ return 1;
+}
diff --git a/board/host/usb_pd_config.h b/board/host/usb_pd_config.h
new file mode 100644
index 0000000000..6770b8e11d
--- /dev/null
+++ b/board/host/usb_pd_config.h
@@ -0,0 +1,41 @@
+/* Copyright (c) 2014 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.
+ */
+
+/* USB Power delivery board configuration */
+
+#ifndef __USB_PD_CONFIG_H
+#define __USB_PD_CONFIG_H
+
+/* Port and task configuration */
+#define PD_PORT_COUNT 2
+#define PORT_TO_TASK_ID(port) ((port) ? TASK_ID_PD_C1 : TASK_ID_PD_C0)
+#define TASK_ID_TO_PORT(id) ((id) == TASK_ID_PD_C0 ? 0 : 1)
+
+/* Use software CRC */
+#define CONFIG_SW_CRC
+
+void pd_select_polarity(int port, int polarity);
+
+void pd_tx_init(void);
+
+void pd_set_host_mode(int port, int enable);
+
+int pd_adc_read(int port, int cc);
+
+int pd_snk_is_vbus_provided(int port);
+
+/* Standard-current DFP : no-connect voltage is 1.55V */
+#define PD_SRC_VNC 1550 /* mV */
+
+/* UFP-side : threshold for DFP connection detection */
+#define PD_SNK_VA 200 /* mV */
+
+/* start as a sink in case we have no other power supply/battery */
+#define PD_DEFAULT_STATE PD_STATE_SNK_DISCONNECTED
+
+/* delay necessary for the voltage transition on the power supply */
+#define PD_POWER_SUPPLY_TRANSITION_DELAY 50000 /* us */
+
+#endif /* __USB_PD_CONFIG_H */
diff --git a/board/host/usb_pd_policy.c b/board/host/usb_pd_policy.c
new file mode 100644
index 0000000000..ffa6d53be9
--- /dev/null
+++ b/board/host/usb_pd_policy.c
@@ -0,0 +1,122 @@
+/* Copyright (c) 2014 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 "common.h"
+#include "console.h"
+#include "usb_pd.h"
+#include "util.h"
+
+const uint32_t pd_src_pdo[] = {
+ PDO_FIXED(5000, 500, PDO_FIXED_EXTERNAL),
+ PDO_FIXED(5000, 900, 0),
+};
+const int pd_src_pdo_cnt = ARRAY_SIZE(pd_src_pdo);
+
+const uint32_t pd_snk_pdo[] = {
+ PDO_BATT(4500, 5500, 15000),
+ PDO_BATT(11500, 12500, 36000),
+};
+const int pd_snk_pdo_cnt = ARRAY_SIZE(pd_snk_pdo);
+
+/* Cap on the max voltage requested as a sink (in millivolts) */
+static unsigned max_mv = -1; /* no cap */
+
+int pd_choose_voltage(int cnt, uint32_t *src_caps, uint32_t *rdo)
+{
+ int i;
+ int sel_mv;
+ int max_uw = 0;
+ int max_i = -1;
+
+ /* Get max power */
+ for (i = 0; i < cnt; i++) {
+ int uw;
+ int mv = ((src_caps[i] >> 10) & 0x3FF) * 50;
+ if ((src_caps[i] & PDO_TYPE_MASK) == PDO_TYPE_BATTERY) {
+ uw = 250000 * (src_caps[i] & 0x3FF);
+ } else {
+ int ma = (src_caps[i] & 0x3FF) * 10;
+ uw = ma * mv;
+ }
+ if ((uw > max_uw) && (mv <= max_mv)) {
+ max_i = i;
+ max_uw = uw;
+ sel_mv = mv;
+ }
+ }
+ if (max_i < 0)
+ return -EC_ERROR_UNKNOWN;
+
+ /* request all the power ... */
+ if ((src_caps[max_i] & PDO_TYPE_MASK) == PDO_TYPE_BATTERY) {
+ int uw = 250000 * (src_caps[max_i] & 0x3FF);
+ *rdo = RDO_BATT(max_i + 1, uw/2, uw, 0);
+ ccprintf("Request [%d] %dV %d/%d mW\n",
+ max_i, sel_mv/1000, uw/1000, uw/1000);
+ } else {
+ int ma = 10 * (src_caps[max_i] & 0x3FF);
+ *rdo = RDO_FIXED(max_i + 1, ma / 2, ma, 0);
+ ccprintf("Request [%d] %dV %d/%d mA\n",
+ max_i, sel_mv/1000, max_i, ma/2, ma);
+ }
+ return EC_SUCCESS;
+}
+
+void pd_set_max_voltage(unsigned mv)
+{
+ max_mv = mv;
+}
+
+int pd_request_voltage(uint32_t rdo)
+{
+ int op_ma = rdo & 0x3FF;
+ int max_ma = (rdo >> 10) & 0x3FF;
+ int idx = rdo >> 28;
+ uint32_t pdo;
+ uint32_t pdo_ma;
+
+ if (!idx || idx > pd_src_pdo_cnt)
+ return EC_ERROR_INVAL; /* Invalid index */
+
+ /* check current ... */
+ pdo = pd_src_pdo[idx - 1];
+ pdo_ma = (pdo & 0x3ff);
+ if (op_ma > pdo_ma)
+ return EC_ERROR_INVAL; /* too much op current */
+ if (max_ma > pdo_ma)
+ return EC_ERROR_INVAL; /* too much max current */
+
+ ccprintf("Switch to %d V %d mA (for %d/%d mA)\n",
+ ((pdo >> 10) & 0x3ff) * 50, (pdo & 0x3ff) * 10,
+ ((rdo >> 10) & 0x3ff) * 10, (rdo & 0x3ff) * 10);
+
+ return EC_SUCCESS;
+}
+
+int pd_set_power_supply_ready(int port)
+{
+ /* Not implemented */
+ return EC_SUCCESS;
+}
+
+void pd_power_supply_reset(int port)
+{
+ /* Not implemented */
+}
+
+void pd_set_input_current_limit(uint32_t max_ma)
+{
+ /* Not implemented */
+}
+
+int pd_board_checks(void)
+{
+ return EC_SUCCESS;
+}
+
+int pd_custom_vdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload)
+{
+ return 0;
+}
diff --git a/chip/host/build.mk b/chip/host/build.mk
index 56b091b8cf..40d2604fcb 100644
--- a/chip/host/build.mk
+++ b/chip/host/build.mk
@@ -11,3 +11,4 @@ CORE:=host
chip-y=system.o gpio.o uart.o persistence.o flash.o lpc.o reboot.o i2c.o \
clock.o
chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o
+chip-$(CONFIG_USB_POWER_DELIVERY)+=usb_pd_phy.o
diff --git a/chip/host/usb_pd_phy.c b/chip/host/usb_pd_phy.c
new file mode 100644
index 0000000000..1cb770bf8d
--- /dev/null
+++ b/chip/host/usb_pd_phy.c
@@ -0,0 +1,289 @@
+/* Copyright 2014 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 "common.h"
+#include "console.h"
+#include "crc.h"
+#include "task.h"
+#include "usb_pd.h"
+#include "usb_pd_config.h"
+#include "util.h"
+
+#define PREAMBLE_OFFSET 60 /* Any number should do */
+
+/*
+ * Maximum size of a Power Delivery packet (in bits on the wire) :
+ * 16-bit header + 0..7 32-bit data objects (+ 4b5b encoding)
+ * 64-bit preamble + SOP (4x 5b) + message in 4b5b + 32-bit CRC + EOP (1x 5b)
+ * = 64 + 4*5 + 16 * 5/4 + 7 * 32 * 5/4 + 32 * 5/4 + 5
+ */
+#define PD_BIT_LEN 429
+
+static struct pd_physical {
+ int hw_init_done;
+
+ uint8_t bits[PD_BIT_LEN];
+ int total;
+ int has_preamble;
+ int rx_started;
+ int rx_monitoring;
+
+ int preamble_written;
+ int has_msg;
+ int last_edge_written;
+ uint8_t out_msg[PD_BIT_LEN / 5];
+ int verified_idx;
+} pd_phy[PD_PORT_COUNT];
+
+static const uint16_t enc4b5b[] = {
+ 0x1E, 0x09, 0x14, 0x15, 0x0A, 0x0B, 0x0E, 0x0F, 0x12, 0x13, 0x16,
+ 0x17, 0x1A, 0x1B, 0x1C, 0x1D};
+
+/* Test utilities */
+
+void pd_test_rx_set_preamble(int port, int has_preamble)
+{
+ pd_phy[port].has_preamble = has_preamble;
+}
+
+void pd_test_rx_msg_append_bits(int port, uint32_t bits, int nb)
+{
+ int i;
+
+ for (i = 0; i < nb; ++i) {
+ pd_phy[port].bits[pd_phy[port].total++] = bits & 1;
+ bits >>= 1;
+ }
+}
+
+void pd_test_rx_msg_append_kcode(int port, uint8_t kcode)
+{
+ pd_test_rx_msg_append_bits(port, kcode, 5);
+}
+
+void pd_test_rx_msg_append_sop(int port)
+{
+ pd_test_rx_msg_append_kcode(port, PD_SYNC1);
+ pd_test_rx_msg_append_kcode(port, PD_SYNC1);
+ pd_test_rx_msg_append_kcode(port, PD_SYNC1);
+ pd_test_rx_msg_append_kcode(port, PD_SYNC2);
+}
+
+void pd_test_rx_msg_append_eop(int port)
+{
+ pd_test_rx_msg_append_kcode(port, PD_EOP);
+}
+
+void pd_test_rx_msg_append_4b(int port, uint8_t val)
+{
+ pd_test_rx_msg_append_bits(port, enc4b5b[val & 0xF], 5);
+}
+
+void pd_test_rx_msg_append_short(int port, uint16_t val)
+{
+ pd_test_rx_msg_append_4b(port, (val >> 0) & 0xF);
+ pd_test_rx_msg_append_4b(port, (val >> 4) & 0xF);
+ pd_test_rx_msg_append_4b(port, (val >> 8) & 0xF);
+ pd_test_rx_msg_append_4b(port, (val >> 12) & 0xF);
+}
+
+void pd_test_rx_msg_append_word(int port, uint32_t val)
+{
+ pd_test_rx_msg_append_short(port, val & 0xFFFF);
+ pd_test_rx_msg_append_short(port, val >> 16);
+}
+
+void pd_simulate_rx(int port)
+{
+ if (!pd_phy[port].rx_monitoring)
+ return;
+ pd_rx_start(port);
+ pd_rx_disable_monitoring(port);
+ pd_rx_event(port);
+}
+
+static int pd_test_tx_msg_verify(int port, uint8_t raw)
+{
+ int verified_idx = pd_phy[port].verified_idx++;
+ return pd_phy[port].out_msg[verified_idx] == raw;
+}
+
+int pd_test_tx_msg_verify_kcode(int port, uint8_t kcode)
+{
+ return pd_test_tx_msg_verify(port, kcode);
+}
+
+int pd_test_tx_msg_verify_sop(int port)
+{
+ crc32_init();
+ return pd_test_tx_msg_verify_kcode(port, PD_SYNC1) &&
+ pd_test_tx_msg_verify_kcode(port, PD_SYNC1) &&
+ pd_test_tx_msg_verify_kcode(port, PD_SYNC1) &&
+ pd_test_tx_msg_verify_kcode(port, PD_SYNC2);
+}
+
+int pd_test_tx_msg_verify_eop(int port)
+{
+ return pd_test_tx_msg_verify_kcode(port, PD_EOP);
+}
+
+int pd_test_tx_msg_verify_4b5b(int port, uint8_t b4)
+{
+ return pd_test_tx_msg_verify(port, enc4b5b[b4]);
+}
+
+int pd_test_tx_msg_verify_short(int port, uint16_t val)
+{
+ crc32_hash16(val);
+ return pd_test_tx_msg_verify_4b5b(port, (val >> 0) & 0xF) &&
+ pd_test_tx_msg_verify_4b5b(port, (val >> 4) & 0xF) &&
+ pd_test_tx_msg_verify_4b5b(port, (val >> 8) & 0xF) &&
+ pd_test_tx_msg_verify_4b5b(port, (val >> 12) & 0xF);
+}
+
+int pd_test_tx_msg_verify_word(int port, uint32_t val)
+{
+ return pd_test_tx_msg_verify_short(port, val & 0xFFFF) &&
+ pd_test_tx_msg_verify_short(port, val >> 16);
+}
+
+int pd_test_tx_msg_verify_crc(int port)
+{
+ return pd_test_tx_msg_verify_word(port, crc32_result());
+}
+
+
+/* Mock functions */
+
+void pd_init_dequeue(int port)
+{
+}
+
+int pd_dequeue_bits(int port, int off, int len, uint32_t *val)
+{
+ int i;
+
+ /* Rx must have started to receive message */
+ ASSERT(pd_phy[port].rx_started);
+
+ if (pd_phy[port].total <= off + len - PREAMBLE_OFFSET)
+ return -1;
+ *val = 0;
+ for (i = 0; i < len; ++i)
+ *val |= pd_phy[port].bits[off + i - PREAMBLE_OFFSET] << i;
+ return off + len;
+}
+
+int pd_find_preamble(int port)
+{
+ return pd_phy[port].has_preamble ? PREAMBLE_OFFSET : -1;
+}
+
+int pd_write_preamble(int port)
+{
+ ASSERT(pd_phy[port].preamble_written == 0);
+ pd_phy[port].preamble_written = 1;
+ ASSERT(pd_phy[port].has_msg == 0);
+ return 0;
+}
+
+static uint8_t decode_bmc(uint32_t val10)
+{
+ uint8_t ret = 0;
+ int i;
+
+ for (i = 0; i < 5; ++i)
+ if (!!(val10 & (1 << (2 * i))) !=
+ !!(val10 & (1 << (2 * i + 1))))
+ ret |= (1 << i);
+ return ret;
+}
+
+int pd_write_sym(int port, int bit_off, uint32_t val10)
+{
+ pd_phy[port].out_msg[bit_off] = decode_bmc(val10);
+ pd_phy[port].has_msg = 1;
+ return bit_off + 1;
+}
+
+int pd_write_last_edge(int port, int bit_off)
+{
+ pd_phy[port].last_edge_written = 1;
+ return bit_off;
+}
+
+void pd_dump_packet(int port, const char *msg)
+{
+ /* Not implemented */
+}
+
+void pd_tx_set_circular_mode(int port)
+{
+ /* Not implemented */
+}
+
+void pd_start_tx(int port, int polarity, int bit_len)
+{
+ ASSERT(pd_phy[port].hw_init_done);
+ pd_phy[port].has_msg = 0;
+ pd_phy[port].preamble_written = 0;
+ pd_phy[port].verified_idx = 0;
+
+ /*
+ * Hand over to test runner. The test runner must wake us after
+ * processing the packet.
+ */
+ task_wake(TASK_ID_TEST_RUNNER);
+ task_wait_event(-1);
+}
+
+void pd_tx_done(int port, int polarity)
+{
+ /* Nothing to do */
+}
+
+void pd_rx_start(int port)
+{
+ ASSERT(pd_phy[port].hw_init_done);
+ pd_phy[port].rx_started = 1;
+}
+
+void pd_rx_complete(int port)
+{
+ ASSERT(pd_phy[port].hw_init_done);
+ pd_phy[port].rx_started = 0;
+}
+
+int pd_rx_started(int port)
+{
+ return pd_phy[port].rx_started;
+}
+
+void pd_rx_enable_monitoring(int port)
+{
+ ASSERT(pd_phy[port].hw_init_done);
+ pd_phy[port].rx_monitoring = 1;
+}
+
+void pd_rx_disable_monitoring(int port)
+{
+ ASSERT(pd_phy[port].hw_init_done);
+ pd_phy[port].rx_monitoring = 0;
+}
+
+void pd_hw_release(int port)
+{
+ pd_phy[port].hw_init_done = 0;
+}
+
+void pd_hw_init(int port)
+{
+ pd_phy[port].hw_init_done = 1;
+}
+
+void pd_set_clock(int port, int freq)
+{
+ /* Not implemented */
+}
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c
index 7989e506ad..97d42475e9 100644
--- a/common/usb_pd_protocol.c
+++ b/common/usb_pd_protocol.c
@@ -178,32 +178,6 @@ static const uint8_t dec4b5b[] = {
#define PD_ROLE_DEFAULT PD_ROLE_SOURCE
#endif
-enum pd_states {
- PD_STATE_DISABLED,
-#ifdef CONFIG_USB_PD_DUAL_ROLE
- PD_STATE_SUSPENDED,
- PD_STATE_SNK_DISCONNECTED,
- PD_STATE_SNK_DISCOVERY,
- PD_STATE_SNK_REQUESTED,
- PD_STATE_SNK_TRANSITION,
- PD_STATE_SNK_READY,
-#endif /* CONFIG_USB_PD_DUAL_ROLE */
-
- PD_STATE_SRC_DISCONNECTED,
- PD_STATE_SRC_DISCOVERY,
- PD_STATE_SRC_NEGOCIATE,
- PD_STATE_SRC_ACCEPTED,
- PD_STATE_SRC_TRANSITION,
- PD_STATE_SRC_READY,
-
- PD_STATE_SOFT_RESET,
- PD_STATE_HARD_RESET,
- PD_STATE_BIST,
-
- /* Number of states. Not an actual state. */
- PD_STATE_COUNT,
-};
-
enum vdm_states {
VDM_STATE_ERR_BUSY = -3,
VDM_STATE_ERR_SEND = -2,
diff --git a/include/usb_pd.h b/include/usb_pd.h
index fbbd92902a..011c7244e5 100644
--- a/include/usb_pd.h
+++ b/include/usb_pd.h
@@ -155,6 +155,32 @@ enum pd_errors {
/* --- Protocol layer functions --- */
+enum pd_states {
+ PD_STATE_DISABLED,
+#ifdef CONFIG_USB_PD_DUAL_ROLE
+ PD_STATE_SUSPENDED,
+ PD_STATE_SNK_DISCONNECTED,
+ PD_STATE_SNK_DISCOVERY,
+ PD_STATE_SNK_REQUESTED,
+ PD_STATE_SNK_TRANSITION,
+ PD_STATE_SNK_READY,
+#endif /* CONFIG_USB_PD_DUAL_ROLE */
+
+ PD_STATE_SRC_DISCONNECTED,
+ PD_STATE_SRC_DISCOVERY,
+ PD_STATE_SRC_NEGOCIATE,
+ PD_STATE_SRC_ACCEPTED,
+ PD_STATE_SRC_TRANSITION,
+ PD_STATE_SRC_READY,
+
+ PD_STATE_SOFT_RESET,
+ PD_STATE_HARD_RESET,
+ PD_STATE_BIST,
+
+ /* Number of states. Not an actual state. */
+ PD_STATE_COUNT,
+};
+
#ifdef CONFIG_USB_PD_DUAL_ROLE
enum pd_dual_role_states {
PD_DRP_TOGGLE_ON,
diff --git a/test/build.mk b/test/build.mk
index f503188410..8995c41fb9 100644
--- a/test/build.mk
+++ b/test/build.mk
@@ -31,12 +31,13 @@ test-list-host+=thermal flash queue kb_8042 extpwr_gpio console_edit system
test-list-host+=sbs_charging adapter host_command thermal_falco led_spring
test-list-host+=bklight_lid bklight_passthru interrupt timer_dos button
test-list-host+=motion_sense math_util sbs_charging_v2 battery_get_params_smart
-test-list-host+=lightbar inductive_charging
+test-list-host+=lightbar inductive_charging usb_pd
adapter-y=adapter.o
-button-y=button.o
+battery_get_params_smart-y=battery_get_params_smart.o
bklight_lid-y=bklight_lid.o
bklight_passthru-y=bklight_passthru.o
+button-y=button.o
console_edit-y=console_edit.o
extpwr_gpio-y=extpwr_gpio.o
flash-y=flash.o
@@ -65,6 +66,7 @@ thermal-y=thermal.o
thermal_falco-y=thermal_falco.o
timer_calib-y=timer_calib.o
timer_dos-y=timer_dos.o
+usb_pd-y=usb_pd.o
utils-y=utils.o
battery_get_params_smart-y=battery_get_params_smart.o
lightbar-y=lightbar.o
diff --git a/test/test_config.h b/test/test_config.h
index 299e3e7930..24ccb19024 100644
--- a/test/test_config.h
+++ b/test/test_config.h
@@ -116,5 +116,12 @@ int board_discharge_on_ac(int enabled);
#define I2C_PORT_LIGHTBAR 1
#endif
+#ifdef TEST_USB_PD
+#define CONFIG_USB_POWER_DELIVERY
+#define CONFIG_USB_PD_CUSTOM_VDM
+#define CONFIG_USB_PD_DUAL_ROLE
+#define CONFIG_SW_CRC
+#endif
+
#endif /* TEST_BUILD */
#endif /* __CROS_EC_TEST_CONFIG_H */
diff --git a/test/usb_pd.c b/test/usb_pd.c
new file mode 100644
index 0000000000..208836fe5b
--- /dev/null
+++ b/test/usb_pd.c
@@ -0,0 +1,212 @@
+/* Copyright 2014 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 PD module.
+ */
+
+#include "common.h"
+#include "crc.h"
+#include "task.h"
+#include "test_util.h"
+#include "timer.h"
+#include "usb_pd.h"
+#include "usb_pd_config.h"
+#include "usb_pd_test_util.h"
+#include "util.h"
+
+struct pd_port_t {
+ int host_mode;
+ int cc_volt[2]; /* -1 for Hi-Z */
+ int has_vbus;
+ int msg_tx_id;
+ int msg_rx_id;
+ int polarity;
+} pd_port[PD_PORT_COUNT];
+
+/* Mock functions */
+
+int pd_adc_read(int port, int cc)
+{
+ int val = pd_port[port].cc_volt[cc];
+ if (val == -1)
+ return pd_port[port].host_mode ? 3000 : 0;
+ return val;
+}
+
+int pd_snk_is_vbus_provided(int port)
+{
+ return pd_port[port].has_vbus;
+}
+
+void pd_set_host_mode(int port, int enable)
+{
+ pd_port[port].host_mode = enable;
+}
+
+void pd_select_polarity(int port, int polarity)
+{
+ pd_port[port].polarity = polarity;
+}
+
+/* Tests */
+
+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 void init_ports(void)
+{
+ int i;
+
+ for (i = 0; i < PD_PORT_COUNT; ++i) {
+ pd_port[i].host_mode = 0;
+ pd_port[i].cc_volt[0] = pd_port[i].cc_volt[1] = -1;
+ pd_port[i].has_vbus = 0;
+ }
+}
+
+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_simulate_rx(port);
+}
+
+static void simulate_source_cap(int port)
+{
+ uint16_t header = PD_HEADER(PD_DATA_SOURCE_CAP, PD_ROLE_SOURCE,
+ pd_port[port].msg_rx_id, pd_src_pdo_cnt);
+ simulate_rx_msg(port, header, pd_src_pdo_cnt, pd_src_pdo);
+}
+
+static void simulate_goodcrc(int port, int role, int id)
+{
+ simulate_rx_msg(port, PD_HEADER(PD_CTRL_GOOD_CRC, role, id, 0),
+ 0, NULL);
+}
+
+static int verify_goodcrc(int port, int role, int id)
+{
+ return pd_test_tx_msg_verify_sop(0) &&
+ pd_test_tx_msg_verify_short(0, PD_HEADER(PD_CTRL_GOOD_CRC,
+ role, id, 0)) &&
+ pd_test_tx_msg_verify_crc(0) &&
+ pd_test_tx_msg_verify_eop(0);
+}
+
+static void plug_in_source(int port, int polarity)
+{
+ pd_port[port].has_vbus = 1;
+ pd_port[port].cc_volt[polarity] = 3000;
+}
+
+static void plug_in_sink(int port, int polarity)
+{
+ pd_port[port].has_vbus = 0;
+ pd_port[port].cc_volt[polarity] = 400; /* V_rd */
+}
+
+static void unplug(int port)
+{
+ pd_port[port].has_vbus = 0;
+ pd_port[port].cc_volt[0] = -1;
+ pd_port[port].cc_volt[1] = -1;
+ task_wake(PORT_TO_TASK_ID(port));
+ usleep(30 * MSEC);
+}
+
+static int test_request(void)
+{
+ plug_in_source(0, 0);
+ task_wake(PORT_TO_TASK_ID(0));
+ task_wait_event(100 * MSEC);
+ TEST_ASSERT(pd_port[0].polarity == 0);
+
+ /* We're in SNK_DISCOVERY now. Let's send the source cap. */
+ simulate_source_cap(0);
+ task_wait_event(30 * MSEC);
+ TEST_ASSERT(verify_goodcrc(0, PD_ROLE_SINK, pd_port[0].msg_rx_id));
+
+ /* Wait for the power request */
+ task_wake(PORT_TO_TASK_ID(0));
+ task_wait_event(35 * MSEC); /* tSenderResponse: 24~30 ms */
+ inc_rx_id(0);
+
+ /* Process the request */
+ TEST_ASSERT(pd_test_tx_msg_verify_sop(0));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(0,
+ PD_HEADER(PD_DATA_REQUEST, PD_ROLE_SINK,
+ pd_port[0].msg_tx_id, 1)));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(0, RDO_FIXED(2, 450, 900, 0)));
+ TEST_ASSERT(pd_test_tx_msg_verify_crc(0));
+ TEST_ASSERT(pd_test_tx_msg_verify_eop(0));
+ inc_tx_id(0);
+
+ /* We're done */
+ unplug(0);
+ return EC_SUCCESS;
+}
+
+static int test_sink(void)
+{
+ int i;
+
+ plug_in_sink(1, 1);
+ task_wake(PORT_TO_TASK_ID(1));
+ task_wait_event(250 * MSEC); /* tTypeCSinkWaitCap: 210~250 ms */
+ TEST_ASSERT(pd_port[1].polarity == 1);
+
+ /* The source cap should be sent */
+ TEST_ASSERT(pd_test_tx_msg_verify_sop(1));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(1,
+ PD_HEADER(PD_DATA_SOURCE_CAP, PD_ROLE_SOURCE,
+ pd_port[1].msg_tx_id, pd_src_pdo_cnt)));
+ for (i = 0; i < pd_src_pdo_cnt; ++i)
+ TEST_ASSERT(pd_test_tx_msg_verify_word(1, pd_src_pdo[i]));
+ TEST_ASSERT(pd_test_tx_msg_verify_crc(1));
+ TEST_ASSERT(pd_test_tx_msg_verify_eop(1));
+
+ /* Looks good. Ack the source cap. */
+ simulate_goodcrc(1, PD_ROLE_SINK, pd_port[1].msg_tx_id);
+ task_wake(PORT_TO_TASK_ID(1));
+ usleep(30 * MSEC);
+ inc_tx_id(1);
+
+ /* We're done */
+ unplug(1);
+ return EC_SUCCESS;
+}
+
+void run_test(void)
+{
+ test_reset();
+ init_ports();
+ pd_set_dual_role(PD_DRP_TOGGLE_ON);
+
+ RUN_TEST(test_request);
+ RUN_TEST(test_sink);
+
+ test_print_result();
+}
diff --git a/test/usb_pd.tasklist b/test/usb_pd.tasklist
new file mode 100644
index 0000000000..4d95d0b156
--- /dev/null
+++ b/test/usb_pd.tasklist
@@ -0,0 +1,19 @@
+/* Copyright (c) 2013 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_pd_test_util.h b/test/usb_pd_test_util.h
new file mode 100644
index 0000000000..5be8e0984a
--- /dev/null
+++ b/test/usb_pd_test_util.h
@@ -0,0 +1,31 @@
+/* Copyright 2014 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 utilities for USB PD unit test.
+ */
+
+#ifndef __USB_PD_TEST_UTIL_H
+#define __USB_PD_TEST_UTIL_H
+
+/* Simulate Rx message */
+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_eop(int port);
+void pd_test_rx_msg_append_4b(int port, uint8_t val);
+void pd_test_rx_msg_append_short(int port, uint16_t val);
+void pd_test_rx_msg_append_word(int port, uint32_t val);
+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_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);
+int pd_test_tx_msg_verify_word(int port, uint32_t val);
+int pd_test_tx_msg_verify_crc(int port);
+
+#endif /* __USB_PD_TEST_UTIL_H */