summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Hurst <shurst@google.com>2018-09-13 09:27:08 -0700
committerchrome-bot <chrome-bot@chromium.org>2019-04-08 22:30:19 -0700
commit0fe6147a9d8d9feef5049aa6c6c4a6ad30d12b26 (patch)
tree05d4509bcfe68a248ec3fa58168f3de2536c2d9c
parente097feb8b2db20cd2435a483517356defa222db1 (diff)
downloadchrome-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>
-rw-r--r--board/chocodile_vpdmcu/board.c70
-rw-r--r--board/chocodile_vpdmcu/board.h139
-rw-r--r--board/chocodile_vpdmcu/build.mk16
-rw-r--r--board/chocodile_vpdmcu/ec.tasklist22
-rw-r--r--board/chocodile_vpdmcu/gpio.inc80
-rw-r--r--board/chocodile_vpdmcu/usb_pd_config.h163
-rw-r--r--board/chocodile_vpdmcu/vpd_api.c531
-rw-r--r--board/chocodile_vpdmcu/vpd_api.h276
-rw-r--r--chip/stm32/adc-stm32f0.c8
-rw-r--r--chip/stm32/usb_pd_phy.c11
-rw-r--r--common/build.mk7
-rw-r--r--common/usb_pd_tcpc.c28
-rw-r--r--common/usb_pe_sm.c12
-rw-r--r--common/usb_prl_sm.c2163
-rw-r--r--common/usb_sm.c166
-rw-r--r--common/usb_tc_sm.c203
-rw-r--r--include/config.h33
-rw-r--r--include/usb_emsg.h23
-rw-r--r--include/usb_pd.h63
-rw-r--r--include/usb_pd_tcpm.h5
-rw-r--r--include/usb_pe_ctvpd_sm.h239
-rw-r--r--include/usb_pe_sm.h79
-rw-r--r--include/usb_prl_sm.h213
-rw-r--r--include/usb_sm.h67
-rw-r--r--include/usb_tc_ctvpd_sm.h2039
-rw-r--r--include/usb_tc_sm.h91
-rw-r--r--include/usb_tc_vpd_sm.h486
-rw-r--r--test/build.mk14
-rw-r--r--test/test_config.h87
-rw-r--r--test/usb_pd_test_util.h2
-rw-r--r--test/usb_prl.c1306
-rw-r--r--test/usb_prl.tasklist19
l---------test/usb_sm_framework_h0.tasklist1
l---------test/usb_sm_framework_h1.tasklist1
l---------test/usb_sm_framework_h2.tasklist1
-rw-r--r--test/usb_sm_framework_h3.c1219
-rw-r--r--test/usb_sm_framework_h3.tasklist18
-rw-r--r--test/usb_typec_ctvpd.c1488
-rw-r--r--test/usb_typec_ctvpd.tasklist18
l---------test/usb_typec_vpd.tasklist1
-rw-r--r--test/vpd_api.c586
-rw-r--r--test/vpd_api.h333
42 files changed, 12322 insertions, 5 deletions
diff --git a/board/chocodile_vpdmcu/board.c b/board/chocodile_vpdmcu/board.c
new file mode 100644
index 0000000000..ea4d129502
--- /dev/null
+++ b/board/chocodile_vpdmcu/board.c
@@ -0,0 +1,70 @@
+/* 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.
+ */
+
+/* chocodile board configuration */
+
+#include "adc.h"
+#include "adc_chip.h"
+#include "common.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "i2c.h"
+#include "registers.h"
+#include "switch.h"
+#include "system.h"
+#include "task.h"
+#include "usb_pd.h"
+#include "usb_pd_tcpc.h"
+#include "util.h"
+#include "vpd_api.h"
+
+#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args)
+
+void board_config_pre_init(void)
+{
+ /* enable SYSCFG clock */
+ STM32_RCC_APB2ENR |= 1 << 0;
+}
+
+#include "gpio_list.h"
+
+/* Initialize board. */
+static void board_init(void)
+{
+ /* Do nothing */
+}
+DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);
+
+/* ADC channels */
+const struct adc_t adc_channels[] = {
+ /* USB PD CC lines sensing. Converted to mV (3300mV/4096). */
+ [ADC_VCONN_VSENSE] = {
+ "VCONN_VSENSE", 3000, 4096, 0, STM32_AIN(ADC_VCONN_VSENSE)},
+ [ADC_CC_VPDMCU] = {
+ "CC_VPDMCU", 3000, 4096, 0, STM32_AIN(ADC_CC_VPDMCU)},
+ [ADC_CC_RP3A0_RD_L] = {
+ "CC_RP3A0_RD_L", 3000, 4096, 0, STM32_AIN(ADC_CC_RP3A0_RD_L)},
+ [ADC_RDCONNECT_REF] = {
+ "RDCONNECT_REF", 3000, 4096, 0, STM32_AIN(ADC_RDCONNECT_REF)},
+ [ADC_CC1_RP3A0_RD_L] = {
+ "CC1_RP1A5_ODH", 3000, 4096, 0, STM32_AIN(ADC_CC1_RP3A0_RD_L)},
+ [ADC_CC2_RP3A0_RD_L] = {
+ "CC2_RP1A5_ODH", 3000, 4096, 0, STM32_AIN(ADC_CC2_RP3A0_RD_L)},
+ [ADC_HOST_VBUS_VSENSE] = {
+ "HOST_VBUS_VSENSE", 3000, 4096, 0, STM32_AIN(ADC_HOST_VBUS_VSENSE)},
+ [ADC_CHARGE_VBUS_VSENSE] = {
+ "CHARGE_VBUS_VSENSE", 3000, 4096, 0, STM32_AIN(ADC_CHARGE_VBUS_VSENSE)},
+ [ADC_CC1_RPUSB_ODH] = {
+ "CC1_RPUSB_ODH", 3000, 4096, 0, STM32_AIN(ADC_CC1_RPUSB_ODH)},
+ [ADC_CC2_RPUSB_ODH] = {
+ "CC2_RPUSB_ODH", 3000, 4096, 0, STM32_AIN(ADC_CC2_RPUSB_ODH)},
+};
+BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT);
+
+void tcpc_alert_clear(int port)
+{
+ /* Do nothing */
+}
diff --git a/board/chocodile_vpdmcu/board.h b/board/chocodile_vpdmcu/board.h
new file mode 100644
index 0000000000..850d1d0011
--- /dev/null
+++ b/board/chocodile_vpdmcu/board.h
@@ -0,0 +1,139 @@
+/* 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.
+ */
+
+/* chocodile_mcu board configuration */
+
+#ifndef __CROS_EC_BOARD_H
+#define __CROS_EC_BOARD_H
+
+/*
+ * The console task is too big to include in both RO and RW images. Therefore,
+ * if the console task is defined, then only build an RW image. This can be
+ * useful for debugging to have a full console. Otherwise, without this task,
+ * a full RO and RW is built with a limited one-way output console.
+ */
+#ifdef HAS_TASK_CONSOLE
+/*
+ * The flash size is only 32kB.
+ * No space for 2 partitions,
+ * put only RW at the beginning of the flash
+ */
+#undef CONFIG_FW_INCLUDE_RO
+#undef CONFIG_RW_MEM_OFF
+#define CONFIG_RW_MEM_OFF 0
+#undef CONFIG_RO_SIZE
+#define CONFIG_RO_SIZE 0
+/* Fake full size if we had a RO partition */
+#undef CONFIG_RW_SIZE
+#define CONFIG_RW_SIZE CONFIG_FLASH_SIZE
+#endif /* HAS_TASK_CONSOLE */
+
+/* 48 MHz SYSCLK clock frequency */
+#define CPU_CLOCK 48000000
+
+/* the UART console is on USART1 (PA9/PA10) */
+#undef CONFIG_UART_CONSOLE
+#define CONFIG_UART_CONSOLE 1
+
+/* Optional features */
+#define CONFIG_ADC
+#undef CONFIG_ADC_WATCHDOG
+#define CONFIG_ADC_SAMPLE_TIME STM32_ADC_SMPR_41_5_CY
+#define CONFIG_BOARD_PRE_INIT
+#define CONFIG_COMMON_GPIO_SHORTNAMES
+#undef CONFIG_DEBUG_ASSERT
+#define CONFIG_FORCE_CONSOLE_RESUME
+#define CONFIG_HIBERNATE
+#undef CONFIG_HOSTCMD_EVENTS
+#define CONFIG_HW_CRC
+#undef CONFIG_LID_SWITCH
+#define CONFIG_LOW_POWER_IDLE
+#define CONFIG_LTO
+#define CONFIG_STM_HWTIMER32
+#undef CONFIG_TASK_PROFILING
+#undef CONFIG_UART_TX_BUF_SIZE
+#undef CONFIG_UART_TX_DMA
+#undef CONFIG_UART_RX_DMA
+#define CONFIG_UART_TX_BUF_SIZE 128
+#define CONFIG_USB_PD_PORT_COUNT 1
+#define CONFIG_USB_PD_TCPC
+#define CONFIG_USB_PD_VBUS_DETECT_NONE
+#define CONFIG_USB_PD_TCPM_STUB
+#define CONFIG_USB_SM_FRAMEWORK
+#define CONFIG_USB_TYPEC_CTVPD
+#define CONFIG_USB_PD_DUAL_ROLE
+#define CONFIG_USB_PD_INTERNAL_COMP
+#define CONFIG_VBOOT_HASH
+#define CONFIG_WATCHDOG
+#undef CONFIG_WATCHDOG_HELP
+#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
+
+/* USB bcdDevice */
+#define USB_BCD_DEVICE 0
+
+/* Vbus impedance in milliohms */
+#define VPD_VBUS_IMPEDANCE 65
+
+/* GND impedance in milliohms */
+#define VPD_GND_IMPEDANCE 33
+
+/*
+ * TODO(crosbug.com/p/50519): Remove CONFIG_SYSTEM_UNLOCKED prior to building
+ * MP FW.
+ */
+#define CONFIG_SYSTEM_UNLOCKED
+
+#ifdef HAS_TASK_CONSOLE
+#undef CONFIG_CONSOLE_HISTORY
+#define CONFIG_CONSOLE_HISTORY 2
+
+#else
+#undef CONFIG_CONSOLE_CMDHELP
+#define CONFIG_DEBUG_PRINTF
+#define UARTN CONFIG_UART_CONSOLE
+#define UARTN_BASE STM32_USART_BASE(CONFIG_UART_CONSOLE)
+#endif /* HAS_TASK_CONSOLE */
+
+/* Use PSTATE embedded in the RO image, not in its own erase block */
+#undef CONFIG_FLASH_PSTATE_BANK
+#undef CONFIG_FW_PSTATE_SIZE
+#define CONFIG_FW_PSTATE_SIZE 0
+
+#ifndef __ASSEMBLER__
+
+/* Timer selection */
+#define TIM_CLOCK32 2
+#define TIM_ADC 3
+
+#include "gpio_signal.h"
+
+/* ADC signal */
+enum adc_channel {
+ ADC_VCONN_VSENSE = 0,
+ ADC_CC_VPDMCU,
+ ADC_CC_RP3A0_RD_L,
+ ADC_RDCONNECT_REF,
+ ADC_CC1_RP3A0_RD_L,
+ ADC_CC2_RP3A0_RD_L,
+ ADC_HOST_VBUS_VSENSE,
+ ADC_CHARGE_VBUS_VSENSE,
+ ADC_CC1_RPUSB_ODH,
+ ADC_CC2_RPUSB_ODH,
+ /* Number of ADC channels */
+ ADC_CH_COUNT
+};
+
+/* 1.5A Rp */
+#define PD_SRC_VNC PD_SRC_1_5_VNC_MV
+#define PD_SRC_RD_THRESHOLD PD_SRC_1_5_RD_THRESH_MV
+
+#endif /* !__ASSEMBLER__ */
+
+#endif /* __CROS_EC_BOARD_H */
diff --git a/board/chocodile_vpdmcu/build.mk b/board/chocodile_vpdmcu/build.mk
new file mode 100644
index 0000000000..d4e5f58962
--- /dev/null
+++ b/board/chocodile_vpdmcu/build.mk
@@ -0,0 +1,16 @@
+# -*- makefile -*-
+# 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.
+#
+# Board specific files build
+
+# the IC is STmicro STM32F051K8U6TR
+CHIP:=stm32
+CHIP_FAMILY:=stm32f0
+CHIP_VARIANT:=stm32f05x
+
+board-y=board.o vpd_api.o
+#
+# This target builds RW only. Therefore, remove RO from dependencies.
+all_deps=$(patsubst ro,,$(def_all_deps))
diff --git a/board/chocodile_vpdmcu/ec.tasklist b/board/chocodile_vpdmcu/ec.tasklist
new file mode 100644
index 0000000000..e386c745e3
--- /dev/null
+++ b/board/chocodile_vpdmcu/ec.tasklist
@@ -0,0 +1,22 @@
+/* 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.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK_ALWAYS(n, r, d, s) for base tasks and
+ * TASK_NOTEST(n, r, d, s) for tasks that can be excluded in test binaries,
+ * 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_TASK_LIST \
+ TASK_ALWAYS(HOOKS, hook_task, NULL, LARGER_TASK_STACK_SIZE) \
+ TASK_ALWAYS(CONSOLE, console_task, NULL, TASK_STACK_SIZE) \
+ TASK_ALWAYS(PD_C0, pd_task, NULL, LARGER_TASK_STACK_SIZE)
diff --git a/board/chocodile_vpdmcu/gpio.inc b/board/chocodile_vpdmcu/gpio.inc
new file mode 100644
index 0000000000..a34c617ef1
--- /dev/null
+++ b/board/chocodile_vpdmcu/gpio.inc
@@ -0,0 +1,80 @@
+/* -*- mode:c -*-
+ *
+ * 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.
+ */
+
+/* Declare symbolic names for all the GPIOs that we care about.
+ * Note: Those with interrupt handlers must be declared first. */
+
+/* Divided Vconn voltage sense */
+GPIO(VCONN_VSENSE, PIN(A, 0), GPIO_ANALOG)
+/* CC ADC, PD in comparator, or tx enable out (low) */
+GPIO(CC_VPDMCU, PIN(A, 1), GPIO_ANALOG)
+/* CC 0.2V comparator during charge-through, active Rd (low) or Rp3A0 (high) */
+GPIO(CC_RP3A0_RD_L, PIN(A, 2), GPIO_ANALOG)
+/* 0.2V resistor divider for various CC comapators */
+GPIO(RDCONNECT_REF, PIN(A, 3), GPIO_ANALOG)
+/* Charger CC1 0.2V comparator and ADC, drive a Rp3A0 (high) or Rd (low) */
+GPIO(CC1_RP3A0_RD_L, PIN(A, 4), GPIO_ANALOG)
+/* Charger CC2 0.2V comparator and ADC, drive a Rp3A0 (high) or Rd (low) */
+GPIO(CC2_RP3A0_RD_L, PIN(A, 5), GPIO_ANALOG)
+/* Divided host VBUS voltage sense */
+GPIO(HOST_VBUS_VSENSE, PIN(A, 6), GPIO_ANALOG)
+/* Divided charger VBUS voltage sense */
+GPIO(CHARGER_VBUS_VSENSE, PIN(A, 7), GPIO_ANALOG)
+/* Charger CC1 ADC, or drive a RpUSB (high) */
+GPIO(CC1_RPUSB_ODH, PIN(B, 0), GPIO_ANALOG)
+/* Charger CC2 ADC, or drive a RpUSB (high) */
+GPIO(CC2_RPUSB_ODH, PIN(B, 1), GPIO_ANALOG)
+
+/* PD TX data output */
+GPIO(CC_TX_DATA, PIN(B, 4), GPIO_INPUT)
+
+/* Enables the VBUS pass-through (high) */
+GPIO(VBUS_PASS_EN, PIN(B, 2), GPIO_OUT_LOW)
+
+/*
+ * Desired billboard state. One of "no billboard/nothing connected" (low),
+ * "source connected but not in charge-through" (pull-up), or "sink connected"
+ * (high)
+ */
+GPIO(PRESENT_BILLBOARD, PIN(A, 8), GPIO_OUT_LOW)
+
+/* Enables cReceiver and the path to the PD RX/TX, RpUSB, and Rp1A5 */
+GPIO(VPDMCU_CC_EN, PIN(A, 11), GPIO_OUT_LOW)
+
+/* Disables dead battery Rd on host side (low) */
+GPIO(CC_DB_EN_OD, PIN(A, 12), GPIO_ODR_HIGH)
+
+/* RpUSB on host side (high) */
+GPIO(CC_RPUSB_ODH, PIN(A, 13), GPIO_INPUT)
+
+/*
+ * Controls the dead-battery pull-downs on charger side; either dead battery
+ * Rd (low) or Hi-Z (high)
+ */
+GPIO(CC1_CC2_DB_EN_L, PIN(A, 15), GPIO_OUT_LOW)
+
+/* Chooses between Vconn (low) and VBUS (high) */
+GPIO(VCONN_PWR_SEL_ODL, PIN(B, 6), GPIO_INPUT)
+
+/* Passes CC1 to the host CC (high) */
+GPIO(CC1_SEL, PIN(F, 0), GPIO_OUT_LOW)
+/* Passes CC2 to the host CC (high) */
+GPIO(CC2_SEL, PIN(F, 1), GPIO_OUT_LOW)
+/* Debug red LED driver (low). Keep off for power measurements */
+GPIO(DEBUG_LED_R_L, PIN(B, 5), GPIO_ODR_HIGH)
+/* Debug green LED driver (low). Keep off for power measurements */
+GPIO(DEBUG_LED_G_L, PIN(B, 7), GPIO_ODR_HIGH)
+
+UNIMPLEMENTED(WP_L)
+UNIMPLEMENTED(ENTERING_RW)
+
+/* SCK(PB3): PD_TX_CLK_IN - Clock input for PD TX */
+ALTERNATE(PIN_MASK(B, 0x0008), 0, MODULE_USB_PD, 0)
+/* TIM16_CH1(PB8): PD_TX_CLK_OUT - Clock generator for PD TX */
+ALTERNATE(PIN_MASK(B, 0x0100), 2, MODULE_USB_PD, 0)
+/* USART1 (PA9/PA10): TX/RX for debug and programming */
+ALTERNATE(PIN_MASK(A, 0x0600), 1, MODULE_UART, 0)
diff --git a/board/chocodile_vpdmcu/usb_pd_config.h b/board/chocodile_vpdmcu/usb_pd_config.h
new file mode 100644
index 0000000000..a6c1adbc61
--- /dev/null
+++ b/board/chocodile_vpdmcu/usb_pd_config.h
@@ -0,0 +1,163 @@
+/* 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 "adc.h"
+#include "chip/stm32/registers.h"
+#include "ec_commands.h"
+#include "gpio.h"
+#include "vpd_api.h"
+
+/* USB Power delivery board configuration */
+
+#ifndef __CROS_EC_USB_PD_CONFIG_H
+#define __CROS_EC_USB_PD_CONFIG_H
+
+/* Timer selection for baseband PD communication */
+#define TIM_CLOCK_PD_TX_C0 16
+#define TIM_CLOCK_PD_RX_C0 1
+
+#define TIM_CLOCK_PD_TX(p) TIM_CLOCK_PD_TX_C0
+#define TIM_CLOCK_PD_RX(p) TIM_CLOCK_PD_RX_C0
+
+/* Timer channel */
+#define TIM_TX_CCR_C0 1
+#define TIM_RX_CCR_C0 1
+
+/* RX timer capture/compare register */
+#define TIM_CCR_C0 (&STM32_TIM_CCRx(TIM_CLOCK_PD_RX_C0, TIM_RX_CCR_C0))
+#define TIM_RX_CCR_REG(p) TIM_CCR_C0
+
+/* TX and RX timer register */
+#define TIM_REG_TX_C0 (STM32_TIM_BASE(TIM_CLOCK_PD_TX_C0))
+#define TIM_REG_RX_C0 (STM32_TIM_BASE(TIM_CLOCK_PD_RX_C0))
+#define TIM_REG_TX(p) TIM_REG_TX_C0
+#define TIM_REG_RX(p) TIM_REG_RX_C0
+
+/* use the hardware accelerator for CRC */
+#define CONFIG_HW_CRC
+
+/* TX uses SPI1 on PB3-4 for port C0 */
+#define SPI_REGS(p) STM32_SPI1_REGS
+
+static inline void spi_enable_clock(int port)
+{
+ STM32_RCC_APB2ENR |= STM32_RCC_PB2_SPI1;
+}
+
+/* SPI1_TX no remap needed */
+#define DMAC_SPI_TX(p) STM32_DMAC_CH3
+
+/* RX is using COMP1 triggering TIM1 CH1 */
+#define CMP1OUTSEL STM32_COMP_CMP1OUTSEL_TIM1_IC1
+#define CMP2OUTSEL 0
+
+#define TIM_TX_CCR_IDX(p) TIM_TX_CCR_C0
+#define TIM_RX_CCR_IDX(p) TIM_RX_CCR_C0
+#define TIM_CCR_CS 1
+
+/* EXTI line 21 is connected to the CMP1 output */
+#define EXTI_COMP1_MASK (1 << 21)
+/* EXTI line 22 is connected to the CMP1 output */
+#define EXTI_COMP2_MASK (1 << 22)
+
+#define EXTI_COMP_MASK(p) (EXTI_COMP1_MASK | EXTI_COMP2_MASK)
+#define IRQ_COMP STM32_IRQ_COMP
+/* triggers packet detection on comparator falling edge */
+#define EXTI_XTSR STM32_EXTI_FTSR
+
+/* TIM1_CH1 no remap needed */
+#define DMAC_TIM_RX(p) STM32_DMAC_CH2
+
+/* the pins used for communication need to be hi-speed */
+static inline void pd_set_pins_speed(int port)
+{
+ /*
+ * 40 MHz pin speed on SPI PB3&4,
+ * (USB_C0_TX_CLKIN & USB_C0_CC1_TX_DATA)
+ *
+ * 40 MHz pin speed on TIM17_CH1 (PB7),
+ * (PD_TX_CLK_OUT)
+ */
+ STM32_GPIO_OSPEEDR(GPIO_B) |= 0x0000C3C0;
+}
+
+/* Reset SPI peripheral used for TX */
+static inline void pd_tx_spi_reset(int port)
+{
+ /* Reset SPI1 */
+ STM32_RCC_APB2RSTR |= (1 << 12);
+ STM32_RCC_APB2RSTR &= ~(1 << 12);
+}
+
+/* Drive the CC line from the TX block */
+static inline void pd_tx_enable(int port, int polarity)
+{
+ /* USB_CC_TX_DATA: PB4 is SPI1 MISO */
+ STM32_GPIO_MODER(GPIO_B) = (STM32_GPIO_MODER(GPIO_B)
+ & ~(3 << (2*4))) /* PB4 disable ADC */
+ | (2 << (2*4)); /* Set as SPI1_MISO */
+ /* MCU ADC PA1 pin output low */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ & ~(3 << (2*1))) /* PA1 disable ADC */
+ | (1 << (2*1)); /* Set as GPO */
+ gpio_set_level(GPIO_CC_VPDMCU, 0);
+}
+
+/* Put the TX driver in Hi-Z state */
+static inline void pd_tx_disable(int port, int polarity)
+{
+ /* Set CC_TX_DATA to Hi-Z, PB4 is SPI1 MISO */
+ STM32_GPIO_MODER(GPIO_B) = (STM32_GPIO_MODER(GPIO_B)
+ & ~(3 << (2*4)));
+ /* set ADC PA1 pin to ADC function (Hi-Z) */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ | (3 << (2*1))); /* PA1 as ADC */
+}
+
+/* we know the plug polarity, do the right configuration */
+static inline void pd_select_polarity(int port, int polarity)
+{
+ /*
+ * use the right comparator : CC1 -> PA1 (COMP1 INP)
+ * use VrefInt / 2 as INM (about 600mV)
+ */
+ STM32_COMP_CSR = (STM32_COMP_CSR & ~STM32_COMP_CMP1INSEL_MASK)
+ | STM32_COMP_CMP1EN | STM32_COMP_CMP1INSEL_VREF12;
+}
+
+/* Initialize pins used for TX and put them in Hi-Z */
+static inline void pd_tx_init(void)
+{
+ gpio_config_module(MODULE_USB_PD, 1);
+}
+
+static inline void pd_set_host_mode(int port, int enable)
+{
+ /* Do nothing */
+}
+
+/**
+ * Initialize various GPIOs and interfaces to safe state at start of pd_task.
+ *
+ * These include:
+ * Physical layer CC transmit.
+ *
+ * @param port USB-C port number
+ * @param power_role Power role of device
+ */
+static inline void pd_config_init(int port, uint8_t power_role)
+{
+ /* Initialize TX pins and put them in Hi-Z */
+ pd_tx_init();
+ pd_tx_disable(0, 0);
+}
+
+static inline int pd_adc_read(int port, int cc)
+{
+ return 0;
+}
+
+#endif /* __CROS_EC_USB_PD_CONFIG_H */
+
diff --git a/board/chocodile_vpdmcu/vpd_api.c b/board/chocodile_vpdmcu/vpd_api.c
new file mode 100644
index 0000000000..ecbedd082f
--- /dev/null
+++ b/board/chocodile_vpdmcu/vpd_api.c
@@ -0,0 +1,531 @@
+/* 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 "adc.h"
+#include "gpio.h"
+#include "registers.h"
+#include "vpd_api.h"
+#include "driver/tcpm/tcpm.h"
+
+/*
+ * Polarity based on 'DFP Perspective' (see table 4-10 USB Type-C Cable and
+ * Connector Specification Release 1.3)
+ *
+ * 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
+
+#undef CC_RA
+#define CC_RA(cc, sel) (cc < pd_src_rd_threshold[sel])
+#undef CC_RD
+#define CC_RD(cc, sel) ((cc >= pd_src_rd_threshold[sel]) && (cc < PD_SRC_VNC))
+
+/* (15.8K / (100K + 15.8K)) * 1000 = 136.4 */
+#define VBUS_SCALE_FACTOR 136
+/* (118K / (100K + 118K)) * 1000 = 541.3 */
+#define VCONN_SCALE_FACTOR 541
+
+#define VBUS_DETECT_THRESHOLD 2500 /* mV */
+#define VCONN_DETECT_THRESHOLD 2500 /* mV */
+
+#define SCALE(vmeas, sfactor) (((vmeas) * 1000) / (sfactor))
+
+/*
+ * 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_500_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 */
+
+/* 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,
+};
+
+/* 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_RD(cc_volt, ct_cc_rp_value))
+ return TYPEC_CC_RD;
+ else if (CC_RA(cc_volt, ct_cc_rp_value))
+ return TYPEC_CC_VOLT_RA;
+ else
+ return TYPEC_CC_VOLT_OPEN;
+ /* 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_volt >= TYPE_C_SRC_DEFAULT_THRESHOLD)
+ 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_GPO, 0);
+ vpd_config_cc2_rp3a0_rd_l(PIN_GPO, 0);
+ vpd_cc1_cc2_db_en_l(GPO_HIGH);
+ 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 = adc_read_channel(ADC_CC1_RP3A0_RD_L);
+ cc2_v = adc_read_channel(ADC_CC2_RP3A0_RD_L);
+ break;
+ case TYPEC_RP_3A0:
+ cc1_v = adc_read_channel(ADC_CC1_RPUSB_ODH);
+ cc2_v = adc_read_channel(ADC_CC2_RPUSB_ODH);
+ break;
+ }
+ break;
+ case TYPEC_CC_RD:
+ cc1_v = adc_read_channel(ADC_CC1_RPUSB_ODH);
+ cc2_v = adc_read_channel(ADC_CC2_RPUSB_ODH);
+ 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);
+ break;
+ }
+}
+
+void vpd_host_get_cc(int *cc)
+{
+ *cc = vpd_cc_voltage_to_status(
+ adc_read_channel(ADC_CC_VPDMCU), host_cc_pull);
+}
+
+void vpd_rx_enable(int en)
+{
+ tcpm_set_rx_enable(0, en);
+}
+
+/*
+ * PA2: Configure as COMP2_INM6 or GPO
+ */
+void vpd_config_cc_rp3a0_rd_l(enum vpd_pin cfg, int en)
+{
+ if (cfg == PIN_GPO) {
+ /* Set output value in register */
+ gpio_set_level(GPIO_CC_RP3A0_RD_L, en ? 1 : 0);
+
+ /* Disable Analog mode and Enable GPO */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ & ~(3 << (2*2))) /* PA2 disable ADC */
+ | (1 << (2*2)); /* Set as GPO */
+ } else {
+ /* Set PA2 pin to ANALOG function */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ | (3 << (2*2))); /* PA2 in ANALOG mode */
+
+ /* Set PA3 pin to ANALOG function */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ | (3 << (2*3))); /* PA3 in ANALOG mode */
+
+ /* Disable Window Mode. Select PA3 */
+ STM32_COMP_CSR &= ~STM32_COMP_WNDWEN;
+
+ /* No output selection. We will use Interrupt */
+ STM32_COMP_CSR &= ~STM32_COMP_CMP2OUTSEL_NONE;
+
+ /* Not inverting */
+ STM32_COMP_CSR &= ~STM32_COMP_CMP2POL;
+
+ /* Select COMP2_INM6 (PA2) */
+ STM32_COMP_CSR |= STM32_COMP_CMP2INSEL_INM6;
+
+ /* COMP Enable */
+ STM32_COMP_CSR |= STM32_COMP_CMP2EN;
+ }
+}
+
+/*
+ * PA4: Configure as ADC, CMP, or GPO
+ */
+void vpd_config_cc1_rp3a0_rd_l(enum vpd_pin cfg, int en)
+{
+ if (cfg == PIN_GPO) {
+ /* Default high. Enable cc1 Rp3A0 pullup */
+ gpio_set_level(GPIO_CC1_RP3A0_RD_L, en ? 1 : 0);
+
+ /* Disable Analog mode and Enable GPO */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ & ~(3 << (2*4))) /* PA4 disable ADC */
+ | (1 << (2*4)); /* Set as GPO */
+ }
+
+ if (cfg == PIN_ADC || cfg == PIN_CMP) {
+ /* Disable COMP2 */
+ STM32_COMP_CSR &= ~STM32_COMP_CMP2EN;
+
+ /* Set PA4 pin to Analog mode */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ | (3 << (2*4))); /* PA4 in ANALOG mode */
+
+ if (cfg == PIN_CMP) {
+ /* Set PA3 pin to ANALOG function */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ | (3 << (2*3))); /* PA3 in ANALOG mode */
+
+ /* Disable Window Mode. Select PA3*/
+ STM32_COMP_CSR &= ~STM32_COMP_WNDWEN;
+
+ /* No output selection. We will use Interrupt */
+ STM32_COMP_CSR &= ~STM32_COMP_CMP2OUTSEL_NONE;
+
+ /* Select COMP2_INM4 (PA4) */
+ STM32_COMP_CSR |= STM32_COMP_CMP2INSEL_INM4;
+
+ /* COMP2 Enable */
+ STM32_COMP_CSR |= STM32_COMP_CMP2EN;
+ }
+ }
+}
+
+/*
+ * PA5: Configure as ADC, COMP, or GPO
+ */
+void vpd_config_cc2_rp3a0_rd_l(enum vpd_pin cfg, int en)
+{
+ if (cfg == PIN_GPO) {
+ /* Set output value in register */
+ gpio_set_level(GPIO_CC2_RP3A0_RD_L, en ? 1 : 0);
+
+ /* Disable Analog mode and Enable GPO */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ & ~(3 << (2*5))) /* PA5 disable ADC */
+ | (1 << (2*5)); /* Set as GPO */
+ }
+
+ if (cfg == PIN_ADC || cfg == PIN_CMP) {
+ /* Disable COMP2 */
+ STM32_COMP_CSR &= ~STM32_COMP_CMP2EN;
+
+ /* Set PA5 pin to ANALOG function */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ | (3 << (2*5))); /* PA5 in ANALOG mode */
+
+ if (cfg == PIN_CMP) {
+ /* Set PA3 pin to ANALOG function */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ | (3 << (2*3))); /* PA3 in ANALOG mode */
+
+ /* Disable Window Mode. */
+ STM32_COMP_CSR &= ~STM32_COMP_WNDWEN;
+
+ /* No output selection. We will use Interrupt */
+ STM32_COMP_CSR &= ~STM32_COMP_CMP2OUTSEL_NONE;
+
+ /* Select COMP2_INM5 (PA5) */
+ STM32_COMP_CSR |= STM32_COMP_CMP2INSEL_INM5;
+
+ /* COMP2 Enable */
+ STM32_COMP_CSR |= STM32_COMP_CMP2EN;
+ }
+ }
+}
+
+/*
+ * PB0: Configure as ADC or GPO
+ */
+void vpd_config_cc1_rpusb_odh(enum vpd_pin cfg, int en)
+{
+ if (cfg == PIN_GPO) {
+ /* Set output value in register */
+ gpio_set_level(GPIO_CC1_RPUSB_ODH, en ? 1 : 0);
+
+ /* Disable Analog mode and Enable GPO */
+ STM32_GPIO_MODER(GPIO_B) = (STM32_GPIO_MODER(GPIO_B)
+ & ~(3 << (2*0))) /* PB0 disable ADC */
+ | (1 << (2*0)); /* Set as GPO */
+ } else {
+ /* Enable Analog mode */
+ STM32_GPIO_MODER(GPIO_B) = (STM32_GPIO_MODER(GPIO_B)
+ | (3 << (2*0))); /* PB0 in ANALOG mode */
+ }
+}
+
+/*
+ * PB1: Configure as ADC or GPO
+ */
+void vpd_config_cc2_rpusb_odh(enum vpd_pin cfg, int en)
+{
+ if (cfg == PIN_GPO) {
+ /* Set output value in register */
+ gpio_set_level(GPIO_CC2_RPUSB_ODH, en ? 1 : 0);
+
+ /* Disable Analog mode and Enable GPO */
+ STM32_GPIO_MODER(GPIO_B) = (STM32_GPIO_MODER(GPIO_B)
+ & ~(3 << (2*1))) /* PB1 disable ADC */
+ | (1 << (2*1)); /* Set as GPO */
+ } else {
+ /* Enable Analog mode */
+ STM32_GPIO_MODER(GPIO_B) = (STM32_GPIO_MODER(GPIO_B)
+ | (3 << (2*1))); /* PB1 in ANALOG mode */
+ }
+}
+
+inline int vpd_read_cc_vpdmcu(void)
+{
+ return adc_read_channel(ADC_CC_VPDMCU);
+}
+
+inline int vpd_read_host_vbus(void)
+{
+ return SCALE(adc_read_channel(ADC_HOST_VBUS_VSENSE), VBUS_SCALE_FACTOR);
+}
+
+inline int vpd_read_ct_vbus(void)
+{
+ return SCALE(adc_read_channel(ADC_CHARGE_VBUS_VSENSE),
+ VBUS_SCALE_FACTOR);
+}
+
+inline int vpd_read_vconn(void)
+{
+ return SCALE(adc_read_channel(ADC_VCONN_VSENSE), VCONN_SCALE_FACTOR);
+}
+
+inline int vpd_is_host_vbus_present(void)
+{
+ return (vpd_read_host_vbus() >= VBUS_DETECT_THRESHOLD);
+}
+
+inline int vpd_is_ct_vbus_present(void)
+{
+ return (vpd_read_ct_vbus() >= VBUS_DETECT_THRESHOLD);
+}
+
+inline int vpd_is_vconn_present(void)
+{
+ return (vpd_read_vconn() >= VCONN_DETECT_THRESHOLD);
+}
+
+inline int vpd_read_rdconnect_ref(void)
+{
+ return adc_read_channel(ADC_RDCONNECT_REF);
+}
+
+void vpd_red_led(int on)
+{
+ gpio_set_level(GPIO_DEBUG_LED_R_L, (on) ? 0 : 1);
+}
+
+void vpd_green_led(int on)
+{
+ gpio_set_level(GPIO_DEBUG_LED_G_L, (on) ? 0 : 1);
+}
+
+void vpd_vbus_pass_en(int en)
+{
+ gpio_set_level(GPIO_VBUS_PASS_EN, (en) ? 1 : 0);
+}
+
+void vpd_present_billboard(enum vpd_billboard bb)
+{
+ switch (bb) {
+ case BB_NONE:
+ gpio_set_level(GPIO_PRESENT_BILLBOARD, 0);
+ gpio_set_flags(GPIO_PRESENT_BILLBOARD, GPIO_OUTPUT);
+ break;
+ case BB_SRC:
+ gpio_set_flags(GPIO_PRESENT_BILLBOARD, GPIO_INPUT);
+ /* Enable Pull-up on PA8 */
+ STM32_GPIO_PUPDR(GPIO_A) |= (1 << (2 * 8));
+ break;
+ case BB_SNK:
+ gpio_set_level(GPIO_PRESENT_BILLBOARD, 1);
+ gpio_set_flags(GPIO_PRESENT_BILLBOARD, GPIO_OUTPUT);
+ break;
+ }
+}
+
+void vpd_mcu_cc_en(int en)
+{
+ gpio_set_level(GPIO_VPDMCU_CC_EN, (en) ? 1 : 0);
+}
+
+void vpd_ct_cc_sel(enum vpd_cc sel)
+{
+ switch (sel) {
+ case CT_OPEN:
+ gpio_set_level(GPIO_CC1_SEL, 0);
+ gpio_set_level(GPIO_CC2_SEL, 0);
+ break;
+ case CT_CC1:
+ gpio_set_level(GPIO_CC2_SEL, 0);
+ gpio_set_level(GPIO_CC1_SEL, 1);
+ break;
+ case CT_CC2:
+ gpio_set_level(GPIO_CC1_SEL, 0);
+ gpio_set_level(GPIO_CC2_SEL, 1);
+ break;
+ }
+}
+
+/* Set as GPO High, GPO Low, or High-Z */
+void vpd_cc_db_en_od(enum vpd_gpo val)
+{
+ if (val == GPO_HZ) {
+ gpio_set_flags(GPIO_CC_DB_EN_OD, GPIO_INPUT);
+ } else {
+ if (val == GPO_HIGH)
+ gpio_set_level(GPIO_CC_DB_EN_OD, 1);
+ else
+ gpio_set_level(GPIO_CC_DB_EN_OD, 0);
+
+ gpio_set_flags(GPIO_CC_DB_EN_OD, GPIO_OUTPUT);
+ }
+}
+
+void vpd_cc_rpusb_odh(enum vpd_gpo val)
+{
+ if (val == GPO_HZ) {
+ gpio_set_flags(GPIO_CC_RPUSB_ODH, GPIO_INPUT);
+ } else {
+ gpio_set_level(GPIO_CC_RPUSB_ODH, (val == GPO_HIGH) ? 1 : 0);
+ gpio_set_flags(GPIO_CC_RPUSB_ODH, GPIO_OUTPUT);
+ }
+}
+
+void vpd_cc1_cc2_db_en_l(enum vpd_gpo val)
+{
+ if (val == GPO_HZ) {
+ gpio_set_flags(GPIO_CC1_CC2_DB_EN_L, GPIO_INPUT);
+ } else {
+ gpio_set_level(GPIO_CC1_CC2_DB_EN_L, (val == GPO_HIGH) ? 1 : 0);
+ gpio_set_flags(GPIO_CC1_CC2_DB_EN_L, GPIO_OUTPUT);
+ }
+}
+
+void vpd_vconn_pwr_sel_odl(enum vpd_pwr en)
+{
+ gpio_set_level(GPIO_VCONN_PWR_SEL_ODL, (en == PWR_VBUS) ? 1 : 0);
+}
diff --git a/board/chocodile_vpdmcu/vpd_api.h b/board/chocodile_vpdmcu/vpd_api.h
new file mode 100644
index 0000000000..df50f92006
--- /dev/null
+++ b/board/chocodile_vpdmcu/vpd_api.h
@@ -0,0 +1,276 @@
+/* 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"
+
+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
+};
+
+/**
+ * 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_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 */
diff --git a/chip/stm32/adc-stm32f0.c b/chip/stm32/adc-stm32f0.c
index c448b4b42f..add5c0c30c 100644
--- a/chip/stm32/adc-stm32f0.c
+++ b/chip/stm32/adc-stm32f0.c
@@ -40,7 +40,11 @@ static const struct adc_profile_t profile = {
/* Sample all channels once using DMA */
.cfgr1_reg = STM32_ADC_CFGR1_OVRMOD,
.cfgr2_reg = 0,
+#ifdef CONFIG_ADC_SAMPLE_TIME
+ .smpr_reg = CONFIG_ADC_SAMPLE_TIME,
+#else
.smpr_reg = STM32_ADC_SMPR_13_5_CY,
+#endif
.ier_reg = 0,
.dma_option = &dma_single,
.dma_buffer_size = 1,
@@ -60,7 +64,11 @@ static const struct adc_profile_t profile = {
STM32_ADC_CFGR1_CONT |
STM32_ADC_CFGR1_DMACFG,
.cfgr2_reg = 0,
+#ifdef CONFIG_ADC_SAMPLE_TIME
+ .smpr_reg = CONFIG_ADC_SAMPLE_TIME,
+#else
.smpr_reg = STM32_ADC_SMPR_1_5_CY,
+#endif
/* Fire interrupt at end of sequence. */
.ier_reg = STM32_ADC_IER_EOSEQIE,
.dma_option = &dma_continuous,
diff --git a/chip/stm32/usb_pd_phy.c b/chip/stm32/usb_pd_phy.c
index 92656a1582..0efa947423 100644
--- a/chip/stm32/usb_pd_phy.c
+++ b/chip/stm32/usb_pd_phy.c
@@ -452,6 +452,16 @@ void pd_rx_handler(void)
int next_idx;
pending = STM32_EXTI_PR;
+#ifdef CONFIG_USB_TYPEC_CTVPD
+ /* Charge-Through Side detach event */
+ if (pending & EXTI_COMP2_MASK) {
+ task_set_event(PD_PORT_TO_TASK_ID(0), PD_EVENT_SM, 0);
+ /* Clear interrupt */
+ STM32_EXTI_PR = EXTI_COMP2_MASK;
+ pending &= ~EXTI_COMP2_MASK;
+ }
+#endif
+
for (i = 0; i < CONFIG_USB_PD_PORT_COUNT; i++) {
if (pending & EXTI_COMP_MASK(i)) {
rx_edge_ts[i][rx_edge_ts_idx[i]].val = get_time().val;
@@ -648,6 +658,7 @@ void pd_hw_init(int port, int role)
phy->tim_tx->ccmr1 = val;
else
phy->tim_tx->ccmr2 = val;
+
phy->tim_tx->ccer = 1 << ((TIM_TX_CCR_IDX(port) - 1) * 4);
phy->tim_tx->bdtr = 0x8000;
/* set prescaler to /1 */
diff --git a/common/build.mk b/common/build.mk
index 5ba841734e..78c97149c5 100644
--- a/common/build.mk
+++ b/common/build.mk
@@ -128,7 +128,14 @@ common-$(CONFIG_USB_I2C)+=usb_i2c.o
common-$(CONFIG_USB_CHARGER)+=usb_charger.o
common-$(CONFIG_USB_PORT_POWER_DUMB)+=usb_port_power_dumb.o
common-$(CONFIG_USB_PORT_POWER_SMART)+=usb_port_power_smart.o
+ifeq ($(CONFIG_USB_SM_FRAMEWORK),)
common-$(CONFIG_USB_POWER_DELIVERY)+=usb_pd_protocol.o usb_pd_policy.o
+else
+common-$(CONFIG_USB_SM_FRAMEWORK)+=usb_sm.o
+common-$(CONFIG_USB_TYPEC_SM)+=usb_tc_sm.o
+common-$(CONFIG_USB_PRL_SM)+=usb_prl_sm.o
+common-$(CONFIG_USB_PE_SM)+=usb_pe_sm.o
+endif
common-$(CONFIG_USB_PD_LOGGING)+=event_log.o pd_log.o
common-$(CONFIG_USB_PD_TCPC)+=usb_pd_tcpc.o
common-$(CONFIG_USB_UPDATE)+=usb_update.o update_fw.o
diff --git a/common/usb_pd_tcpc.c b/common/usb_pd_tcpc.c
index 83a1569223..2bdadcec7e 100644
--- a/common/usb_pd_tcpc.c
+++ b/common/usb_pd_tcpc.c
@@ -343,11 +343,19 @@ int prepare_message(int port, uint16_t header, uint8_t cnt,
int off, i;
/* 64-bit preamble */
off = pd_write_preamble(port);
+#if defined(CONFIG_USB_TYPEC_VPD) || defined(CONFIG_USB_TYPEC_CTVPD)
+ /* Start Of Packet Prime: 2x Sync-1 + 2x Sync-3 */
+ off = pd_write_sym(port, off, BMC(PD_SYNC1));
+ off = pd_write_sym(port, off, BMC(PD_SYNC1));
+ off = pd_write_sym(port, off, BMC(PD_SYNC3));
+ off = pd_write_sym(port, off, BMC(PD_SYNC3));
+#else
/* Start Of Packet: 3x Sync-1 + 1x Sync-2 */
off = pd_write_sym(port, off, BMC(PD_SYNC1));
off = pd_write_sym(port, off, BMC(PD_SYNC1));
off = pd_write_sym(port, off, BMC(PD_SYNC1));
off = pd_write_sym(port, off, BMC(PD_SYNC2));
+#endif
/* header */
off = encode_short(port, off, header);
@@ -682,6 +690,17 @@ int pd_analyze_rx(int port, uint32_t *payload)
/* Find the Start Of Packet sequence */
while (bit > 0) {
bit = pd_dequeue_bits(port, bit, 20, &val);
+#if defined(CONFIG_USB_TYPEC_VPD) || defined(CONFIG_USB_TYPEC_CTVPD)
+ if (val == PD_SOP_PRIME) {
+ break;
+ } else if (val == PD_SOP) {
+ CPRINTF("SOP\n");
+ return PD_RX_ERR_UNSUPPORTED_SOP;
+ } else if (val == PD_SOP_PRIME_PRIME) {
+ CPRINTF("SOP''\n");
+ return PD_RX_ERR_UNSUPPORTED_SOP;
+ }
+#else /* CONFIG_USB_TYPEC_VPD || CONFIG_USB_TYPEC_CTVPD */
#ifdef CONFIG_USB_PD_DECODE_SOP
if (val == PD_SOP || val == PD_SOP_PRIME ||
val == PD_SOP_PRIME_PRIME)
@@ -696,7 +715,8 @@ int pd_analyze_rx(int port, uint32_t *payload)
CPRINTF("SOP''\n");
return PD_RX_ERR_UNSUPPORTED_SOP;
}
-#endif
+#endif /* CONFIG_USB_PD_DECODE_SOP */
+#endif /* CONFIG_USB_TYPEC_VPD || CONFIG_USB_TYPEC_CTVPD */
}
if (bit < 0) {
#ifdef CONFIG_USB_PD_DECODE_SOP
@@ -872,7 +892,11 @@ int tcpc_run(int port, int evt)
/* outgoing packet ? */
if ((evt & PD_EVENT_TX) && pd[port].rx_enabled) {
switch (pd[port].tx_type) {
+#if defined(CONFIG_USB_TYPEC_VPD) || defined(CONFIG_USB_TYPEC_CTVPD)
+ case TCPC_TX_SOP_PRIME:
+#else
case TCPC_TX_SOP:
+#endif
res = send_validate_message(port,
pd[port].tx_head,
pd[port].tx_data);
@@ -938,7 +962,7 @@ int tcpc_run(int port, int evt)
#endif
}
-#ifndef CONFIG_USB_POWER_DELIVERY
+#if !defined(CONFIG_USB_POWER_DELIVERY) && !defined(CONFIG_USB_SM_FRAMEWORK)
void pd_task(void *u)
{
int port = TASK_ID_TO_PD_PORT(task_get_current());
diff --git a/common/usb_pe_sm.c b/common/usb_pe_sm.c
new file mode 100644
index 0000000000..91f8b587ed
--- /dev/null
+++ b/common/usb_pe_sm.c
@@ -0,0 +1,12 @@
+/* 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 "common.h"
+
+/* Include USB PD Policy Engine State Machine */
+#if defined(CONFIG_USB_TYPEC_VPD) || defined(CONFIG_USB_TYPEC_CTVPD)
+#include "usb_pe_ctvpd_sm.h"
+#else
+#error "A USB PD Policy Engine State Machine must be defined."
+#endif
diff --git a/common/usb_prl_sm.c b/common/usb_prl_sm.c
new file mode 100644
index 0000000000..42b4ee3c24
--- /dev/null
+++ b/common/usb_prl_sm.c
@@ -0,0 +1,2163 @@
+/* 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 "battery.h"
+#include "battery_smart.h"
+#include "board.h"
+#include "charge_manager.h"
+#include "charge_state.h"
+#include "chipset.h"
+#include "common.h"
+#include "console.h"
+#include "ec_commands.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "registers.h"
+#include "system.h"
+#include "task.h"
+#include "timer.h"
+#include "tcpm.h"
+#include "util.h"
+#include "usb_charge.h"
+#include "usb_mux.h"
+#include "usb_pd.h"
+#include "usb_pe_sm.h"
+#include "usb_prl_sm.h"
+#include "usb_tc_sm.h"
+#include "usb_emsg.h"
+#include "usb_sm.h"
+#include "vpd_api.h"
+#include "version.h"
+
+/* Protocol Layer Flags */
+#define PRL_FLAGS_TX_COMPLETE BIT(0)
+#define PRL_FLAGS_START_AMS BIT(1)
+#define PRL_FLAGS_END_AMS BIT(2)
+#define PRL_FLAGS_TX_ERROR BIT(3)
+#define PRL_FLAGS_PE_HARD_RESET BIT(4)
+#define PRL_FLAGS_HARD_RESET_COMPLETE BIT(5)
+#define PRL_FLAGS_PORT_PARTNER_HARD_RESET BIT(6)
+#define PRL_FLAGS_MSG_XMIT BIT(7)
+#define PRL_FLAGS_MSG_RECEIVED BIT(8)
+#define PRL_FLAGS_ABORT BIT(9)
+#define PRL_FLAGS_CHUNKING BIT(10)
+
+/* PD counter definitions */
+#define PD_MESSAGE_ID_COUNT 7
+
+#define RCH_OBJ(port) (SM_OBJ(rch[port]))
+#define TCH_OBJ(port) (SM_OBJ(tch[port]))
+#define PRL_TX_OBJ(port) (SM_OBJ(prl_tx[port]))
+#define PRL_HR_OBJ(port) (SM_OBJ(prl_hr[port]))
+
+#define RCH_TEST_OBJ(port) (SM_OBJ(rch[(port)].obj))
+#define TCH_TEST_OBJ(port) (SM_OBJ(tch[(port)].obj))
+#define PRL_TX_TEST_OBJ(port) (SM_OBJ(prl_tx[(port)].obj))
+#define PRL_HR_TEST_OBJ(port) (SM_OBJ(prl_hr[(port)].obj))
+
+static enum sm_local_state local_state[CONFIG_USB_PD_PORT_COUNT] = {SM_INIT};
+
+/* Chunked Rx State Machine Object */
+static struct rx_chunked {
+ /* struct sm_obj must be first. */
+ struct sm_obj obj;
+ /* state id */
+ enum rch_state_id state_id;
+ /* PRL_FLAGS */
+ uint32_t flags;
+ /* protocol timer */
+ uint64_t chunk_sender_response_timer;
+} rch[CONFIG_USB_PD_PORT_COUNT];
+
+/* Chunked Tx State Machine Object */
+static struct tx_chunked {
+ /* struct sm_obj must be first. */
+ struct sm_obj obj;
+ /* state id */
+ enum tch_state_id state_id;
+ /* state machine flags */
+ uint32_t flags;
+ /* protocol timer */
+ uint64_t chunk_sender_request_timer;
+} tch[CONFIG_USB_PD_PORT_COUNT];
+
+/* Message Reception State Machine Object */
+static struct protocol_layer_rx {
+ /* message ids for all valid port partners */
+ int msg_id[NUM_XMIT_TYPES];
+} prl_rx[CONFIG_USB_PD_PORT_COUNT];
+
+/* Message Transmission State Machine Object */
+static struct protocol_layer_tx {
+ /* struct sm_obj must be first. */
+ struct sm_obj obj;
+ /* state id */
+ enum prl_tx_state_id state_id;
+ /* state machine flags */
+ uint32_t flags;
+ /* protocol timer */
+ uint64_t sink_tx_timer;
+ /* tcpc transmit timeout */
+ uint64_t tcpc_tx_timeout;
+ /* Last SOP* we transmitted to */
+ uint8_t sop;
+ /* message id counters for all 6 port partners */
+ uint32_t msg_id_counter[NUM_XMIT_TYPES];
+ /* message retry counter */
+ uint32_t retry_counter;
+ /* transmit status */
+ int xmit_status;
+} prl_tx[CONFIG_USB_PD_PORT_COUNT];
+
+/* Hard Reset State Machine Object */
+static struct protocol_hard_reset {
+ /* struct sm_obj must be first. */
+ struct sm_obj obj;
+ /* state id */
+ enum prl_hr_state_id state_id;
+ /* state machine flags */
+ uint32_t flags;
+ /* protocol timer */
+ uint64_t hard_reset_complete_timer;
+} prl_hr[CONFIG_USB_PD_PORT_COUNT];
+
+/* Chunking Message Object */
+static struct pd_message {
+ /* message status flags */
+ uint32_t status_flags;
+
+ /* SOP* */
+ enum tcpm_transmit_type xmit_type;
+ /* type of message */
+ uint8_t msg_type;
+ /* extended message */
+ uint8_t ext;
+ /* PD revision */
+ enum pd_rev_type rev;
+ /* Number of 32-bit objects in chk_buf */
+ uint16_t data_objs;
+ /* temp chunk buffer */
+ uint32_t chk_buf[7];
+ uint32_t chunk_number_expected;
+ uint32_t num_bytes_received;
+ uint32_t chunk_number_to_send;
+ uint32_t send_offset;
+} pdmsg[CONFIG_USB_PD_PORT_COUNT];
+
+struct extended_msg emsg[CONFIG_USB_PD_PORT_COUNT];
+
+/* Protocol Layer States */
+/* Common Protocol Layer Message Transmission */
+static void prl_tx_construct_message(int port);
+
+static unsigned int prl_tx_phy_layer_reset(int port, enum signal sig);
+static unsigned int prl_tx_phy_layer_reset_entry(int port);
+static unsigned int prl_tx_phy_layer_reset_run(int port);
+
+static unsigned int prl_tx_wait_for_message_request(int port, enum signal sig);
+static unsigned int prl_tx_wait_for_message_request_entry(int port);
+static unsigned int prl_tx_wait_for_message_request_run(int port);
+
+static unsigned int prl_tx_layer_reset_for_transmit(int port, enum signal sig);
+static unsigned int prl_tx_layer_reset_for_transmit_entry(int port);
+static unsigned int prl_tx_layer_reset_for_transmit_run(int port);
+
+static unsigned int prl_tx_wait_for_phy_response(int port, enum signal sig);
+static unsigned int prl_tx_wait_for_phy_response_entry(int port);
+static unsigned int prl_tx_wait_for_phy_response_run(int port);
+static unsigned int prl_tx_wait_for_phy_response_exit(int port);
+
+static unsigned int prl_tx_src_source_tx(int port, enum signal sig);
+static unsigned int prl_tx_src_source_tx_entry(int port);
+static unsigned int prl_tx_src_source_tx_run(int port);
+
+static unsigned int prl_tx_snk_start_ams(int port, enum signal sig);
+static unsigned int prl_tx_snk_start_ams_entry(int port);
+static unsigned int prl_tx_snk_start_ams_run(int port);
+
+/* Source Protocol Layser Message Transmission */
+static unsigned int prl_tx_src_pending(int port, enum signal sig);
+static unsigned int prl_tx_src_pending_entry(int port);
+static unsigned int prl_tx_src_pending_run(int port);
+
+/* Sink Protocol Layer Message Transmission */
+static unsigned int prl_tx_snk_pending(int port, enum signal sig);
+static unsigned int prl_tx_snk_pending_entry(int port);
+static unsigned int prl_tx_snk_pending_run(int port);
+
+static unsigned int prl_tx_discard_message(int port, enum signal sig);
+static unsigned int prl_tx_discard_message_entry(int port);
+static unsigned int prl_tx_discard_message_run(int port);
+
+/* Protocol Layer Message Reception */
+static unsigned int prl_rx_wait_for_phy_message(int port, int evt);
+
+/* Hard Reset Operation */
+static unsigned int prl_hr_wait_for_request(int port, enum signal sig);
+static unsigned int prl_hr_wait_for_request_entry(int port);
+static unsigned int prl_hr_wait_for_request_run(int port);
+
+static unsigned int prl_hr_reset_layer(int port, enum signal sig);
+static unsigned int prl_hr_reset_layer_entry(int port);
+static unsigned int prl_hr_reset_layer_run(int port);
+
+static unsigned int
+ prl_hr_wait_for_phy_hard_reset_complete(int port, enum signal sig);
+static unsigned int prl_hr_wait_for_phy_hard_reset_complete_entry(int port);
+static unsigned int prl_hr_wait_for_phy_hard_reset_complete_run(int port);
+
+static unsigned int
+ prl_hr_wait_for_pe_hard_reset_complete(int port, enum signal sig);
+static unsigned int prl_hr_wait_for_pe_hard_reset_complete_entry(int port);
+static unsigned int prl_hr_wait_for_pe_hard_reset_complete_run(int port);
+static unsigned int prl_hr_wait_for_pe_hard_reset_complete_exit(int port);
+
+/* Chunked Rx */
+static unsigned int
+ rch_wait_for_message_from_protocol_layer(int port, enum signal sig);
+static unsigned int rch_wait_for_message_from_protocol_layer_entry(int port);
+static unsigned int rch_wait_for_message_from_protocol_layer_run(int port);
+
+static unsigned int rch_processing_extended_message(int port, enum signal sig);
+static unsigned int rch_processing_extended_message_entry(int port);
+static unsigned int rch_processing_extended_message_run(int port);
+
+static unsigned int rch_requesting_chunk(int port, enum signal sig);
+static unsigned int rch_requesting_chunk_entry(int port);
+static unsigned int rch_requesting_chunk_run(int port);
+
+static unsigned int rch_waiting_chunk(int port, enum signal sig);
+static unsigned int rch_waiting_chunk_entry(int port);
+static unsigned int rch_waiting_chunk_run(int port);
+
+static unsigned int rch_report_error(int port, enum signal sig);
+static unsigned int rch_report_error_entry(int port);
+static unsigned int rch_report_error_run(int port);
+
+/* Chunked Tx */
+static unsigned int
+ tch_wait_for_message_request_from_pe(int port, enum signal sig);
+static unsigned int tch_wait_for_message_request_from_pe_entry(int port);
+static unsigned int tch_wait_for_message_request_from_pe_run(int port);
+
+static unsigned int
+ tch_wait_for_transmission_complete(int port, enum signal sig);
+static unsigned int tch_wait_for_transmission_complete_entry(int port);
+static unsigned int tch_wait_for_transmission_complete_run(int port);
+
+static unsigned int tch_construct_chunked_message(int port, enum signal sig);
+static unsigned int tch_construct_chunked_message_entry(int port);
+static unsigned int tch_construct_chunked_message_run(int port);
+
+static unsigned int tch_sending_chunked_message(int port, enum signal sig);
+static unsigned int tch_sending_chunked_message_entry(int port);
+static unsigned int tch_sending_chunked_message_run(int port);
+
+static unsigned int tch_wait_chunk_request(int port, enum signal sig);
+static unsigned int tch_wait_chunk_request_entry(int port);
+static unsigned int tch_wait_chunk_request_run(int port);
+
+static unsigned int tch_message_received(int port, enum signal sig);
+static unsigned int tch_message_received_entry(int port);
+static unsigned int tch_message_received_run(int port);
+
+static unsigned int do_nothing_exit(int port);
+static unsigned int get_super_state(int port);
+
+static const state_sig prl_tx_phy_layer_reset_sig[] = {
+ prl_tx_phy_layer_reset_entry,
+ prl_tx_phy_layer_reset_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig prl_tx_wait_for_message_request_sig[] = {
+ prl_tx_wait_for_message_request_entry,
+ prl_tx_wait_for_message_request_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig prl_tx_layer_reset_for_transmit_sig[] = {
+ prl_tx_layer_reset_for_transmit_entry,
+ prl_tx_layer_reset_for_transmit_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig prl_tx_wait_for_phy_response_sig[] = {
+ prl_tx_wait_for_phy_response_entry,
+ prl_tx_wait_for_phy_response_run,
+ prl_tx_wait_for_phy_response_exit,
+ get_super_state
+};
+
+static const state_sig prl_tx_src_source_tx_sig[] = {
+ prl_tx_src_source_tx_entry,
+ prl_tx_src_source_tx_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig prl_tx_snk_start_ams_sig[] = {
+ prl_tx_snk_start_ams_entry,
+ prl_tx_snk_start_ams_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+/* Source Protocol Layser Message Transmission */
+static const state_sig prl_tx_src_pending_sig[] = {
+ prl_tx_src_pending_entry,
+ prl_tx_src_pending_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+/* Sink Protocol Layer Message Transmission */
+static const state_sig prl_tx_snk_pending_sig[] = {
+ prl_tx_snk_pending_entry,
+ prl_tx_snk_pending_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig prl_tx_discard_message_sig[] = {
+ prl_tx_discard_message_entry,
+ prl_tx_discard_message_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+/* Hard Reset Operation */
+static const state_sig prl_hr_wait_for_request_sig[] = {
+ prl_hr_wait_for_request_entry,
+ prl_hr_wait_for_request_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig prl_hr_reset_layer_sig[] = {
+ prl_hr_reset_layer_entry,
+ prl_hr_reset_layer_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig prl_hr_wait_for_phy_hard_reset_complete_sig[] = {
+ prl_hr_wait_for_phy_hard_reset_complete_entry,
+ prl_hr_wait_for_phy_hard_reset_complete_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig prl_hr_wait_for_pe_hard_reset_complete_sig[] = {
+ prl_hr_wait_for_pe_hard_reset_complete_entry,
+ prl_hr_wait_for_pe_hard_reset_complete_run,
+ prl_hr_wait_for_pe_hard_reset_complete_exit,
+ get_super_state
+};
+
+/* Chunked Rx */
+static const state_sig rch_wait_for_message_from_protocol_layer_sig[] = {
+ rch_wait_for_message_from_protocol_layer_entry,
+ rch_wait_for_message_from_protocol_layer_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig rch_processing_extended_message_sig[] = {
+ rch_processing_extended_message_entry,
+ rch_processing_extended_message_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig rch_requesting_chunk_sig[] = {
+ rch_requesting_chunk_entry,
+ rch_requesting_chunk_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig rch_waiting_chunk_sig[] = {
+ rch_waiting_chunk_entry,
+ rch_waiting_chunk_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig rch_report_error_sig[] = {
+ rch_report_error_entry,
+ rch_report_error_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+/* Chunked Tx */
+static const state_sig tch_wait_for_message_request_from_pe_sig[] = {
+ tch_wait_for_message_request_from_pe_entry,
+ tch_wait_for_message_request_from_pe_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tch_wait_for_transmission_complete_sig[] = {
+ tch_wait_for_transmission_complete_entry,
+ tch_wait_for_transmission_complete_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tch_construct_chunked_message_sig[] = {
+ tch_construct_chunked_message_entry,
+ tch_construct_chunked_message_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tch_sending_chunked_message_sig[] = {
+ tch_sending_chunked_message_entry,
+ tch_sending_chunked_message_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tch_wait_chunk_request_sig[] = {
+ tch_wait_chunk_request_entry,
+ tch_wait_chunk_request_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tch_message_received_sig[] = {
+ tch_message_received_entry,
+ tch_message_received_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+void pd_transmit_complete(int port, int status)
+{
+ prl_tx[port].xmit_status = status;
+}
+
+void pd_execute_hard_reset(int port)
+{
+ /* Only allow async. function calls when state machine is running */
+ if (local_state[port] != SM_RUN)
+ return;
+
+ prl_hr[port].flags |= PRL_FLAGS_PORT_PARTNER_HARD_RESET;
+ set_state(port, PRL_HR_OBJ(port), prl_hr_reset_layer);
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0);
+}
+
+void prl_execute_hard_reset(int port)
+{
+ /* Only allow async. function calls when state machine is running */
+ if (local_state[port] != SM_RUN)
+ return;
+
+ prl_hr[port].flags |= PRL_FLAGS_PE_HARD_RESET;
+ set_state(port, PRL_HR_OBJ(port), prl_hr_reset_layer);
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0);
+}
+
+void prl_init(int port)
+{
+ int i;
+
+ prl_tx[port].flags = 0;
+ prl_tx[port].xmit_status = TCPC_TX_UNSET;
+
+ tch[port].flags = 0;
+ rch[port].flags = 0;
+
+ /*
+ * Initialize to highest revision supported. If the port partner
+ * doesn't support this revision, the Protocol Engine will lower
+ * this value to the revision supported by the port partner.
+ */
+ pdmsg[port].rev = PD_REV30;
+ pdmsg[port].status_flags = 0;
+
+ prl_hr[port].flags = 0;
+
+ for (i = 0; i < NUM_XMIT_TYPES; i++) {
+ prl_rx[port].msg_id[i] = -1;
+ prl_tx[port].msg_id_counter[i] = 0;
+ }
+
+ init_state(port, PRL_TX_OBJ(port), prl_tx_phy_layer_reset);
+ init_state(port, RCH_OBJ(port),
+ rch_wait_for_message_from_protocol_layer);
+ init_state(port, TCH_OBJ(port), tch_wait_for_message_request_from_pe);
+ init_state(port, PRL_HR_OBJ(port), prl_hr_wait_for_request);
+}
+
+enum rch_state_id get_rch_state_id(int port)
+{
+ return rch[port].state_id;
+}
+
+enum tch_state_id get_tch_state_id(int port)
+{
+ return tch[port].state_id;
+}
+
+enum prl_tx_state_id get_prl_tx_state_id(int port)
+{
+ return prl_tx[port].state_id;
+}
+
+enum prl_hr_state_id get_prl_hr_state_id(int port)
+{
+ return prl_hr[port].state_id;
+}
+
+void prl_start_ams(int port)
+{
+ prl_tx[port].flags |= PRL_FLAGS_START_AMS;
+}
+
+void prl_end_ams(int port)
+{
+ prl_tx[port].flags |= PRL_FLAGS_END_AMS;
+}
+
+void prl_hard_reset_complete(int port)
+{
+ prl_hr[port].flags |= PRL_FLAGS_HARD_RESET_COMPLETE;
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0);
+}
+
+void prl_send_ctrl_msg(int port,
+ enum tcpm_transmit_type type,
+ enum pd_ctrl_msg_type msg)
+{
+ pdmsg[port].xmit_type = type;
+ pdmsg[port].msg_type = msg;
+ pdmsg[port].ext = 0;
+ emsg[port].len = 0;
+
+ tch[port].flags |= PRL_FLAGS_MSG_XMIT;
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0);
+}
+
+void prl_send_data_msg(int port,
+ enum tcpm_transmit_type type,
+ enum pd_data_msg_type msg)
+{
+ pdmsg[port].xmit_type = type;
+ pdmsg[port].msg_type = msg;
+ pdmsg[port].ext = 0;
+
+ tch[port].flags |= PRL_FLAGS_MSG_XMIT;
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0);
+}
+
+void prl_send_ext_data_msg(int port,
+ enum tcpm_transmit_type type,
+ enum pd_ext_msg_type msg)
+{
+ pdmsg[port].xmit_type = type;
+ pdmsg[port].msg_type = msg;
+ pdmsg[port].ext = 1;
+
+ tch[port].flags |= PRL_FLAGS_MSG_XMIT;
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0);
+}
+
+void prl_reset(int port)
+{
+ local_state[port] = SM_INIT;
+}
+
+void protocol_layer(int port, int evt, int en)
+{
+ switch (local_state[port]) {
+ case SM_INIT:
+ prl_init(port);
+ local_state[port] = SM_RUN;
+ /* fall through */
+ case SM_RUN:
+ /* If disabling, wait until message is sent. */
+ if (!en && tch[port].state_id ==
+ TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE) {
+ /* Disable RX */
+#if defined(CONFIG_USB_TYPEC_CTVPD) || defined(CONFIG_USB_TYPEC_VPD)
+ vpd_rx_enable(0);
+#else
+ tcpm_set_rx_enable(port, 0);
+#endif
+ local_state[port] = SM_PAUSED;
+ break;
+ }
+
+ /* Run Protocol Layer Message Reception */
+ prl_rx_wait_for_phy_message(port, evt);
+
+ /* Run RX Chunked state machine */
+ exe_state(port, RCH_OBJ(port), RUN_SIG);
+
+ /* Run TX Chunked state machine */
+ exe_state(port, TCH_OBJ(port), RUN_SIG);
+
+ /* Run Protocol Layer Message Transmission state machine */
+ exe_state(port, PRL_TX_OBJ(port), RUN_SIG);
+
+ /* Run Protocol Layer Hard Reset state machine */
+ exe_state(port, PRL_HR_OBJ(port), RUN_SIG);
+ break;
+ case SM_PAUSED:
+ if (en)
+ local_state[port] = SM_INIT;
+ break;
+ }
+}
+
+enum sm_local_state prl_get_local_state(int port)
+{
+ return local_state[port];
+}
+
+void prl_set_rev(int port, enum pd_rev_type rev)
+{
+ pdmsg[port].rev = rev;
+}
+
+enum pd_rev_type prl_get_rev(int port)
+{
+ return pdmsg[port].rev;
+}
+
+/* Common Protocol Layer Message Transmission */
+static unsigned int prl_tx_phy_layer_reset(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_tx_phy_layer_reset_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_tx_phy_layer_reset_entry(int port)
+{
+ prl_tx[port].state_id = PRL_TX_PHY_LAYER_RESET;
+
+#if defined(CONFIG_USB_TYPEC_CTVPD) || defined(CONFIG_USB_TYPEC_VPD)
+ vpd_rx_enable(1);
+#else
+ tcpm_init(port);
+ tcpm_set_rx_enable(port, 1);
+#endif
+
+ return 0;
+}
+
+static unsigned int prl_tx_phy_layer_reset_run(int port)
+{
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_wait_for_message_request);
+ return 0;
+}
+
+static unsigned int prl_tx_wait_for_message_request(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_tx_wait_for_message_request_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_tx_wait_for_message_request_entry(int port)
+{
+ prl_tx[port].state_id = PRL_TX_WAIT_FOR_MESSAGE_REQUEST;
+
+ /* Reset RetryCounter */
+ prl_tx[port].retry_counter = 0;
+
+ return 0;
+}
+
+static unsigned int prl_tx_wait_for_message_request_run(int port)
+{
+ if (prl_tx[port].flags & PRL_FLAGS_MSG_XMIT) {
+ prl_tx[port].flags &= ~PRL_FLAGS_MSG_XMIT;
+ /*
+ * Soft Reset Message Message pending
+ */
+ if ((pdmsg[port].msg_type == PD_CTRL_SOFT_RESET) &&
+ (emsg[port].len == 0)) {
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_layer_reset_for_transmit);
+ }
+ /*
+ * Message pending (except Soft Reset)
+ */
+ else {
+ /* NOTE: PRL_TX_Construct_Message State embedded here */
+ prl_tx_construct_message(port);
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_wait_for_phy_response);
+ }
+
+ return 0;
+ } else if ((pdmsg[port].rev == PD_REV30) &&
+ (prl_tx[port].flags &
+ (PRL_FLAGS_START_AMS | PRL_FLAGS_END_AMS))) {
+ if (tc_get_power_role(port) == PD_ROLE_SOURCE) {
+ /*
+ * Start of AMS notification received from
+ * Policy Engine
+ */
+ if (prl_tx[port].flags & PRL_FLAGS_START_AMS) {
+ prl_tx[port].flags &= ~PRL_FLAGS_START_AMS;
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_src_source_tx);
+ return 0;
+ }
+ /*
+ * End of AMS notification received from
+ * Policy Engine
+ */
+ else if (prl_tx[port].flags & PRL_FLAGS_END_AMS) {
+ prl_tx[port].flags &= ~PRL_FLAGS_END_AMS;
+ /* Set Rp = SinkTxOk */
+ tcpm_select_rp_value(port, SINK_TX_OK);
+ tcpm_set_cc(port, TYPEC_CC_RP);
+ prl_tx[port].retry_counter = 0;
+ prl_tx[port].flags = 0;
+ }
+ } else {
+ if (prl_tx[port].flags & PRL_FLAGS_START_AMS) {
+ prl_tx[port].flags &= ~PRL_FLAGS_START_AMS;
+ /*
+ * First Message in AMS notification
+ * received from Policy Engine.
+ */
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_snk_start_ams);
+ return 0;
+ }
+ }
+ }
+
+ return RUN_SUPER;
+}
+
+static void increment_msgid_counter(int port)
+{
+ prl_tx[port].msg_id_counter[prl_tx[port].sop] =
+ (prl_tx[port].msg_id_counter[prl_tx[port].sop] + 1) &
+ PD_MESSAGE_ID_COUNT;
+}
+
+/*
+ * PrlTxDiscard
+ */
+static unsigned int prl_tx_discard_message(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_tx_discard_message_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_tx_discard_message_entry(int port)
+{
+ prl_tx[port].state_id = PRL_TX_DISCARD_MESSAGE;
+
+ /* Increment msgidCounter */
+ increment_msgid_counter(port);
+ set_state(port, PRL_TX_OBJ(port), prl_tx_phy_layer_reset);
+
+ return 0;
+}
+
+static unsigned int prl_tx_discard_message_run(int port)
+{
+ return RUN_SUPER;
+}
+
+/*
+ * PrlTxSrcSourceTx
+ */
+static unsigned int prl_tx_src_source_tx(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_tx_src_source_tx_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_tx_src_source_tx_entry(int port)
+{
+ prl_tx[port].state_id = PRL_TX_SRC_SOURCE_TX;
+
+ /* Set Rp = SinkTxNG */
+ tcpm_select_rp_value(port, SINK_TX_NG);
+ tcpm_set_cc(port, TYPEC_CC_RP);
+
+ return 0;
+}
+
+static unsigned int prl_tx_src_source_tx_run(int port)
+{
+ if (prl_tx[port].flags & PRL_FLAGS_MSG_XMIT) {
+ prl_tx[port].flags &= ~PRL_FLAGS_MSG_XMIT;
+
+ set_state(port, PRL_TX_OBJ(port), prl_tx_src_pending);
+ }
+
+ return RUN_SUPER;
+}
+
+/*
+ * PrlTxSnkStartAms
+ */
+static unsigned int prl_tx_snk_start_ams(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_tx_snk_start_ams_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_tx_snk_start_ams_entry(int port)
+{
+ prl_tx[port].state_id = PRL_TX_SNK_START_OF_AMS;
+ return 0;
+}
+
+static unsigned int prl_tx_snk_start_ams_run(int port)
+{
+ if (prl_tx[port].flags & PRL_FLAGS_MSG_XMIT) {
+ prl_tx[port].flags &= ~PRL_FLAGS_MSG_XMIT;
+
+ set_state(port, PRL_TX_OBJ(port), prl_tx_snk_pending);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/*
+ * PrlTxLayerResetForTransmit
+ */
+static unsigned int prl_tx_layer_reset_for_transmit(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_tx_layer_reset_for_transmit_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_tx_layer_reset_for_transmit_entry(int port)
+{
+ int i;
+
+ prl_tx[port].state_id = PRL_TX_LAYER_RESET_FOR_TRANSMIT;
+
+ /* Reset MessageIdCounters */
+ for (i = 0; i < NUM_XMIT_TYPES; i++)
+ prl_tx[port].msg_id_counter[i] = 0;
+
+ return 0;
+}
+
+static unsigned int prl_tx_layer_reset_for_transmit_run(int port)
+{
+ /* NOTE: PRL_Tx_Construct_Message State embedded here */
+ prl_tx_construct_message(port);
+ set_state(port, PRL_TX_OBJ(port), prl_tx_wait_for_phy_response);
+
+ return 0;
+}
+
+static void prl_tx_construct_message(int port)
+{
+ uint32_t header = PD_HEADER(
+ pdmsg[port].msg_type,
+ tc_get_power_role(port),
+ tc_get_data_role(port),
+ prl_tx[port].msg_id_counter[pdmsg[port].xmit_type],
+ pdmsg[port].data_objs,
+ pdmsg[port].rev,
+ pdmsg[port].ext);
+
+ /* Save SOP* so the correct msg_id_counter can be incremented */
+ prl_tx[port].sop = pdmsg[port].xmit_type;
+
+ /* Pass message to PHY Layer */
+ tcpm_transmit(port, pdmsg[port].xmit_type, header,
+ pdmsg[port].chk_buf);
+}
+
+/*
+ * PrlTxWaitForPhyResponse
+ */
+static unsigned int prl_tx_wait_for_phy_response(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_tx_wait_for_phy_response_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_tx_wait_for_phy_response_entry(int port)
+{
+ prl_tx[port].state_id = PRL_TX_WAIT_FOR_PHY_RESPONSE;
+
+ prl_tx[port].tcpc_tx_timeout = get_time().val + PD_T_TCPC_TX_TIMEOUT;
+ return 0;
+}
+
+static unsigned int prl_tx_wait_for_phy_response_run(int port)
+{
+ /* Wait until TX is complete */
+
+ /*
+ * NOTE: The TCPC will set xmit_status to TCPC_TX_COMPLETE_DISCARDED
+ * when a GoodCRC containing an incorrect MessageID is received.
+ * This condition satifies the PRL_Tx_Match_MessageID state
+ * requirement.
+ */
+
+ if (get_time().val > prl_tx[port].tcpc_tx_timeout ||
+ prl_tx[port].xmit_status == TCPC_TX_COMPLETE_FAILED ||
+ prl_tx[port].xmit_status == TCPC_TX_COMPLETE_DISCARDED) {
+
+ /* NOTE: PRL_Tx_Check_RetryCounter State embedded here. */
+
+ /* Increment check RetryCounter */
+ prl_tx[port].retry_counter++;
+
+ /*
+ * (RetryCounter > nRetryCount) | Large Extended Message
+ */
+ if (prl_tx[port].retry_counter > N_RETRY_COUNT ||
+ (pdmsg[port].ext &&
+ PD_EXT_HEADER_DATA_SIZE(GET_EXT_HEADER(
+ pdmsg[port].chk_buf[0]) > 26))) {
+
+ /*
+ * NOTE: PRL_Tx_Transmission_Error State embedded
+ * here.
+ */
+
+ /*
+ * State tch_wait_for_transmission_complete will
+ * inform policy engine of error
+ */
+ pdmsg[port].status_flags |= PRL_FLAGS_TX_ERROR;
+
+ /* Increment message id counter */
+ increment_msgid_counter(port);
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_wait_for_message_request);
+ return 0;
+ }
+
+ /* Try to resend the message. */
+ /* NOTE: PRL_TX_Construct_Message State embedded here. */
+ prl_tx_construct_message(port);
+ return 0;
+ }
+
+ if (prl_tx[port].xmit_status == TCPC_TX_COMPLETE_SUCCESS) {
+
+ /* NOTE: PRL_TX_Message_Sent State embedded here. */
+
+ /* Increment messageId counter */
+ increment_msgid_counter(port);
+ /* Inform Policy Engine Message was sent */
+ pdmsg[port].status_flags |= PRL_FLAGS_TX_COMPLETE;
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_wait_for_message_request);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int prl_tx_wait_for_phy_response_exit(int port)
+{
+ prl_tx[port].xmit_status = TCPC_TX_UNSET;
+ return 0;
+}
+
+/* Source Protocol Layer Message Transmission */
+/*
+ * PrlTxSrcPending
+ */
+static unsigned int prl_tx_src_pending(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_tx_src_pending_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_tx_src_pending_entry(int port)
+{
+ prl_tx[port].state_id = PRL_TX_SRC_PENDING;
+
+ /* Start SinkTxTimer */
+ prl_tx[port].sink_tx_timer = get_time().val + PD_T_SINK_TX;
+
+ return 0;
+}
+
+static unsigned int prl_tx_src_pending_run(int port)
+{
+
+ if (get_time().val > prl_tx[port].sink_tx_timer) {
+ /*
+ * Soft Reset Message pending &
+ * SinkTxTimer timeout
+ */
+ if ((emsg[port].len == 0) &&
+ (pdmsg[port].msg_type == PD_CTRL_SOFT_RESET)) {
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_layer_reset_for_transmit);
+ }
+ /* Message pending (except Soft Reset) &
+ * SinkTxTimer timeout
+ */
+ else {
+ prl_tx_construct_message(port);
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_wait_for_phy_response);
+ }
+
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/*
+ * PrlTxSnkPending
+ */
+static unsigned int prl_tx_snk_pending(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_tx_snk_pending_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_tx_snk_pending_entry(int port)
+{
+ prl_tx[port].state_id = PRL_TX_SNK_PENDING;
+ return 0;
+}
+
+static unsigned int prl_tx_snk_pending_run(int port)
+{
+ int cc1;
+ int cc2;
+
+ tcpm_get_cc(port, &cc1, &cc2);
+ if (cc1 == TYPEC_CC_VOLT_RP_3_0 || cc2 == TYPEC_CC_VOLT_RP_3_0) {
+ /*
+ * Soft Reset Message Message pending &
+ * Rp = SinkTxOk
+ */
+ if ((pdmsg[port].msg_type == PD_CTRL_SOFT_RESET) &&
+ (emsg[port].len == 0)) {
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_layer_reset_for_transmit);
+ }
+ /*
+ * Message pending (except Soft Reset) &
+ * Rp = SinkTxOk
+ */
+ else {
+ prl_tx_construct_message(port);
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_wait_for_phy_response);
+ }
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/* Hard Reset Operation */
+
+static unsigned int prl_hr_wait_for_request(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_hr_wait_for_request_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_hr_wait_for_request_entry(int port)
+{
+ prl_hr[port].state_id = PRL_HR_WAIT_FOR_REQUEST;
+
+ prl_hr[port].flags = 0;
+ return 0;
+}
+
+static unsigned int prl_hr_wait_for_request_run(int port)
+{
+ if (prl_hr[port].flags & PRL_FLAGS_PE_HARD_RESET ||
+ prl_hr[port].flags & PRL_FLAGS_PORT_PARTNER_HARD_RESET) {
+ set_state(port, PRL_HR_OBJ(port), prl_hr_reset_layer);
+ }
+
+ return 0;
+}
+
+/*
+ * PrlHrResetLayer
+ */
+static unsigned int prl_hr_reset_layer(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_hr_reset_layer_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_hr_reset_layer_entry(int port)
+{
+ int i;
+
+ prl_hr[port].state_id = PRL_HR_RESET_LAYER;
+
+ /* reset messageIDCounters */
+ for (i = 0; i < NUM_XMIT_TYPES; i++)
+ prl_tx[port].msg_id_counter[i] = 0;
+ /*
+ * Protocol Layer message transmission transitions to
+ * PRL_Tx_Wait_For_Message_Request state.
+ */
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_wait_for_message_request);
+
+ return 0;
+}
+
+static unsigned int prl_hr_reset_layer_run(int port)
+{
+ /*
+ * Protocol Layer reset Complete &
+ * Hard Reset was initiated by Policy Engine
+ */
+ if (prl_hr[port].flags & PRL_FLAGS_PE_HARD_RESET) {
+ /* Request PHY to perform a Hard Reset */
+ prl_send_ctrl_msg(port, TCPC_TX_HARD_RESET, 0);
+ set_state(port, PRL_HR_OBJ(port),
+ prl_hr_wait_for_phy_hard_reset_complete);
+ }
+ /*
+ * Protocol Layer reset complete &
+ * Hard Reset was initiated by Port Partner
+ */
+ else {
+ /* Inform Policy Engine of the Hard Reset */
+ pe_got_hard_reset(port);
+ set_state(port, PRL_HR_OBJ(port),
+ prl_hr_wait_for_pe_hard_reset_complete);
+ }
+
+ return 0;
+}
+
+/*
+ * PrlHrWaitForPhyHardResetComplete
+ */
+static unsigned int
+ prl_hr_wait_for_phy_hard_reset_complete(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_hr_wait_for_phy_hard_reset_complete_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_hr_wait_for_phy_hard_reset_complete_entry(int port)
+{
+ prl_hr[port].state_id = PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE;
+
+ /* Start HardResetCompleteTimer */
+ prl_hr[port].hard_reset_complete_timer =
+ get_time().val + PD_T_PS_HARD_RESET;
+
+ return 0;
+}
+
+static unsigned int prl_hr_wait_for_phy_hard_reset_complete_run(int port)
+{
+ /*
+ * Wait for hard reset from PHY
+ * or timeout
+ */
+ if ((pdmsg[port].status_flags & PRL_FLAGS_TX_COMPLETE) ||
+ (get_time().val > prl_hr[port].hard_reset_complete_timer)) {
+ /* PRL_HR_PHY_Hard_Reset_Requested */
+
+ /* Inform Policy Engine Hard Reset was sent */
+ pe_hard_reset_sent(port);
+ set_state(port, PRL_HR_OBJ(port),
+ prl_hr_wait_for_pe_hard_reset_complete);
+
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/*
+ * PrlHrWaitForPeHardResetComplete
+ */
+static unsigned int
+ prl_hr_wait_for_pe_hard_reset_complete(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_hr_wait_for_pe_hard_reset_complete_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_hr_wait_for_pe_hard_reset_complete_entry(int port)
+{
+ prl_hr[port].state_id = PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE;
+ return 0;
+}
+
+static unsigned int prl_hr_wait_for_pe_hard_reset_complete_run(int port)
+{
+ /*
+ * Wait for Hard Reset complete indication from Policy Engine
+ */
+ if (prl_hr[port].flags & PRL_FLAGS_HARD_RESET_COMPLETE)
+ set_state(port, PRL_HR_OBJ(port), prl_hr_wait_for_request);
+
+ return RUN_SUPER;
+}
+
+static unsigned int prl_hr_wait_for_pe_hard_reset_complete_exit(int port)
+{
+ /* Exit from Hard Reset */
+
+ set_state(port, PRL_TX_OBJ(port), prl_tx_phy_layer_reset);
+ set_state(port, RCH_OBJ(port),
+ rch_wait_for_message_from_protocol_layer);
+ set_state(port, TCH_OBJ(port), tch_wait_for_message_request_from_pe);
+
+ return 0;
+}
+
+static void copy_chunk_to_ext(int port)
+{
+ /* Calculate number of bytes */
+ pdmsg[port].num_bytes_received = (PD_HEADER_CNT(emsg[port].header) * 4);
+
+ /* Copy chunk into extended message */
+ memcpy((uint8_t *)emsg[port].buf, (uint8_t *)pdmsg[port].chk_buf,
+ pdmsg[port].num_bytes_received);
+
+ /* Set extended message length */
+ emsg[port].len = pdmsg[port].num_bytes_received;
+}
+
+/*
+ * Chunked Rx State Machine
+ */
+static unsigned int
+ rch_wait_for_message_from_protocol_layer(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*rch_wait_for_message_from_protocol_layer_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static inline void rch_clear_abort_set_chunking(int port)
+{
+ /* Clear Abort flag */
+ pdmsg[port].status_flags &= ~PRL_FLAGS_ABORT;
+
+ /* All Messages are chunked */
+ rch[port].flags = PRL_FLAGS_CHUNKING;
+}
+
+static unsigned int rch_wait_for_message_from_protocol_layer_entry(int port)
+{
+ rch[port].state_id = RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER;
+ rch_clear_abort_set_chunking(port);
+ return 0;
+}
+
+static unsigned int rch_wait_for_message_from_protocol_layer_run(int port)
+{
+ if (rch[port].flags & PRL_FLAGS_MSG_RECEIVED) {
+ rch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED;
+ /*
+ * Are we communicating with a PD3.0 device and is
+ * this an extended message?
+ */
+ if (pdmsg[port].rev == PD_REV30 &&
+ PD_HEADER_EXT(emsg[port].header)) {
+ uint16_t exhdr = GET_EXT_HEADER(*pdmsg[port].chk_buf);
+ uint8_t chunked = PD_EXT_HEADER_CHUNKED(exhdr);
+
+ /*
+ * Received Extended Message &
+ * (Chunking = 1 & Chunked = 1)
+ */
+ if ((rch[port].flags & PRL_FLAGS_CHUNKING) &&
+ chunked) {
+ set_state(port, RCH_OBJ(port),
+ rch_processing_extended_message);
+ return 0;
+ }
+ /*
+ * (Received Extended Message &
+ * (Chunking = 0 & Chunked = 0))
+ */
+ else if (!(rch[port].flags &
+ PRL_FLAGS_CHUNKING) && !chunked) {
+ /* Copy chunk to extended buffer */
+ copy_chunk_to_ext(port);
+ /* Pass Message to Policy Engine */
+ pe_pass_up_message(port);
+ /* Clear Abort flag and set Chunking */
+ rch_clear_abort_set_chunking(port);
+ }
+ /*
+ * Chunked != Chunking
+ */
+ else {
+ set_state(port, RCH_OBJ(port),
+ rch_report_error);
+ return 0;
+ }
+ }
+ /*
+ * Received Non-Extended Message
+ */
+ else if (!PD_HEADER_EXT(emsg[port].header)) {
+ /* Copy chunk to extended buffer */
+ copy_chunk_to_ext(port);
+ /* Pass Message to Policy Engine */
+ pe_pass_up_message(port);
+ /* Clear Abort flag and set Chunking */
+ rch_clear_abort_set_chunking(port);
+ }
+ /*
+ * Received an Extended Message while communicating at a
+ * revision lower than PD3.0
+ */
+ else {
+ set_state(port, RCH_OBJ(port),
+ rch_report_error);
+ return 0;
+ }
+ }
+
+ return RUN_SUPER;
+}
+
+/*
+ * RchProcessingExtendedMessage
+ */
+static unsigned int rch_processing_extended_message(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*rch_processing_extended_message_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int rch_processing_extended_message_entry(int port)
+{
+ uint32_t header = emsg[port].header;
+ uint16_t exhdr = GET_EXT_HEADER(pdmsg[port].chk_buf[0]);
+ uint8_t chunk_num = PD_EXT_HEADER_CHUNK_NUM(exhdr);
+
+ rch[port].state_id = RCH_PROCESSING_EXTENDED_MESSAGE;
+
+ /*
+ * If first chunk:
+ * Set Chunk_number_expected = 0 and
+ * Num_Bytes_Received = 0
+ */
+ if (chunk_num == 0) {
+ pdmsg[port].chunk_number_expected = 0;
+ pdmsg[port].num_bytes_received = 0;
+ pdmsg[port].msg_type = PD_HEADER_TYPE(header);
+ }
+
+ return 0;
+}
+
+static unsigned int rch_processing_extended_message_run(int port)
+{
+ uint16_t exhdr = GET_EXT_HEADER(pdmsg[port].chk_buf[0]);
+ uint8_t chunk_num = PD_EXT_HEADER_CHUNK_NUM(exhdr);
+ uint32_t data_size = PD_EXT_HEADER_DATA_SIZE(exhdr);
+ uint32_t byte_num;
+
+ /*
+ * Abort Flag Set
+ */
+ if (pdmsg[port].status_flags & PRL_FLAGS_ABORT) {
+ set_state(port, RCH_OBJ(port),
+ rch_wait_for_message_from_protocol_layer);
+ }
+ /*
+ * If expected Chunk Number:
+ * Append data to Extended_Message_Buffer
+ * Increment Chunk_number_Expected
+ * Adjust Num Bytes Received
+ */
+ else if (chunk_num == pdmsg[port].chunk_number_expected) {
+ byte_num = data_size - pdmsg[port].num_bytes_received;
+
+ if (byte_num > 25)
+ byte_num = 26;
+
+ /* Make sure extended message buffer does not overflow */
+ if (pdmsg[port].num_bytes_received +
+ byte_num > EXTENDED_BUFFER_SIZE) {
+ set_state(port, RCH_OBJ(port), rch_report_error);
+ return 0;
+ }
+
+ /* Append data */
+ /* Add 2 to chk_buf to skip over extended message header */
+ memcpy(((uint8_t *)emsg[port].buf +
+ pdmsg[port].num_bytes_received),
+ (uint8_t *)pdmsg[port].chk_buf + 2, byte_num);
+ /* increment chunk number expected */
+ pdmsg[port].chunk_number_expected++;
+ /* adjust num bytes received */
+ pdmsg[port].num_bytes_received += byte_num;
+
+ /* Was that the last chunk? */
+ if (pdmsg[port].num_bytes_received >= data_size) {
+ emsg[port].len = pdmsg[port].num_bytes_received;
+ /* Pass Message to Policy Engine */
+ pe_pass_up_message(port);
+ set_state(port, RCH_OBJ(port),
+ rch_wait_for_message_from_protocol_layer);
+ }
+ /*
+ * Message not Complete
+ */
+ else
+ set_state(port, RCH_OBJ(port), rch_requesting_chunk);
+ }
+ /*
+ * Unexpected Chunk Number
+ */
+ else
+ set_state(port, RCH_OBJ(port), rch_report_error);
+
+ return 0;
+}
+
+/*
+ * RchRequestingChunk
+ */
+static unsigned int rch_requesting_chunk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*rch_requesting_chunk_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int rch_requesting_chunk_entry(int port)
+{
+ rch[port].state_id = RCH_REQUESTING_CHUNK;
+
+ /*
+ * Send Chunk Request to Protocol Layer
+ * with chunk number = Chunk_Number_Expected
+ */
+ pdmsg[port].chk_buf[0] = PD_EXT_HEADER(
+ pdmsg[port].chunk_number_expected,
+ 1, /* Request Chunk */
+ 0 /* Data Size */
+ );
+
+ pdmsg[port].data_objs = 1;
+ pdmsg[port].ext = 1;
+ prl_tx[port].flags |= PRL_FLAGS_MSG_XMIT;
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_TX, 0);
+
+ return 0;
+}
+
+static unsigned int rch_requesting_chunk_run(int port)
+{
+ /*
+ * Transmission Error from Protocol Layer or
+ * Message Received From Protocol Layer
+ */
+ if (rch[port].flags & PRL_FLAGS_MSG_RECEIVED ||
+ pdmsg[port].status_flags & PRL_FLAGS_TX_ERROR) {
+ /*
+ * Leave PRL_FLAGS_MSG_RECEIVED flag set. It'll be
+ * cleared in rch_report_error state
+ */
+ set_state(port, RCH_OBJ(port), rch_report_error);
+ }
+ /*
+ * Message Transmitted received from Protocol Layer
+ */
+ else if (pdmsg[port].status_flags & PRL_FLAGS_TX_COMPLETE) {
+ pdmsg[port].status_flags &= ~PRL_FLAGS_TX_COMPLETE;
+ set_state(port, RCH_OBJ(port), rch_waiting_chunk);
+ } else
+ return RUN_SUPER;
+
+ return 0;
+}
+
+/*
+ * RchWaitingChunk
+ */
+static unsigned int rch_waiting_chunk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*rch_waiting_chunk_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int rch_waiting_chunk_entry(int port)
+{
+ rch[port].state_id = RCH_WAITING_CHUNK;
+
+ /*
+ * Start ChunkSenderResponseTimer
+ */
+ rch[port].chunk_sender_response_timer =
+ get_time().val + PD_T_CHUNK_SENDER_RESPONSE;
+
+ return 0;
+}
+
+static unsigned int rch_waiting_chunk_run(int port)
+{
+ if ((rch[port].flags & PRL_FLAGS_MSG_RECEIVED)) {
+ /*
+ * Leave PRL_FLAGS_MSG_RECEIVED flag set just in case an error
+ * is detected. If an error is detected, PRL_FLAGS_MSG_RECEIVED
+ * will be cleared in rch_report_error state.
+ */
+
+ if (PD_HEADER_EXT(emsg[port].header)) {
+ uint16_t exhdr = GET_EXT_HEADER(pdmsg[port].chk_buf[0]);
+ /*
+ * Other Message Received from Protocol Layer
+ */
+ if (PD_EXT_HEADER_REQ_CHUNK(exhdr) ||
+ !PD_EXT_HEADER_CHUNKED(exhdr)) {
+ set_state(port, RCH_OBJ(port),
+ rch_report_error);
+ }
+ /*
+ * Chunk response Received from Protocol Layer
+ */
+ else {
+ /*
+ * No error wad detected, so clear
+ * PRL_FLAGS_MSG_RECEIVED flag.
+ */
+ rch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED;
+ set_state(port, RCH_OBJ(port),
+ rch_processing_extended_message);
+ }
+
+ return 0;
+ }
+ }
+ /*
+ * ChunkSenderResponseTimer Timeout
+ */
+ else if (get_time().val > rch[port].chunk_sender_response_timer) {
+ set_state(port, RCH_OBJ(port), rch_report_error);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/*
+ * RchReportError
+ */
+static unsigned int rch_report_error(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*rch_report_error_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int rch_report_error_entry(int port)
+{
+ rch[port].state_id = RCH_REPORT_ERROR;
+
+ /*
+ * If the state was entered because a message was received,
+ * this message is passed to the Policy Engine.
+ */
+ if (rch[port].flags & PRL_FLAGS_MSG_RECEIVED) {
+ rch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED;
+
+ /* Copy chunk to extended buffer */
+ copy_chunk_to_ext(port);
+ /* Pass Message to Policy Engine */
+ pe_pass_up_message(port);
+ /* Report error */
+ pe_report_error(port, ERR_RCH_MSG_REC);
+ } else {
+ /* Report error */
+ pe_report_error(port, ERR_RCH_CHUNKED);
+ }
+
+ return 0;
+}
+
+static unsigned int rch_report_error_run(int port)
+{
+ set_state(port, RCH_OBJ(port),
+ rch_wait_for_message_from_protocol_layer);
+
+ return 0;
+}
+
+/*
+ * Chunked Tx State Machine
+ */
+static unsigned int
+ tch_wait_for_message_request_from_pe(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tch_wait_for_message_request_from_pe_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static inline void tch_clear_abort_set_chunking(int port)
+{
+ /* Clear Abort flag */
+ pdmsg[port].status_flags &= ~PRL_FLAGS_ABORT;
+
+ /* All Messages are chunked */
+ tch[port].flags = PRL_FLAGS_CHUNKING;
+}
+
+static unsigned int tch_wait_for_message_request_from_pe_entry(int port)
+{
+ tch[port].state_id = TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE;
+ tch_clear_abort_set_chunking(port);
+ return 0;
+}
+
+static unsigned int tch_wait_for_message_request_from_pe_run(int port)
+{
+ /*
+ * Any message received and not in state TCH_Wait_Chunk_Request
+ */
+ if (tch[port].flags & PRL_FLAGS_MSG_RECEIVED) {
+ tch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED;
+ set_state(port, TCH_OBJ(port), tch_message_received);
+ return 0;
+ } else if (tch[port].flags & PRL_FLAGS_MSG_XMIT) {
+ tch[port].flags &= ~PRL_FLAGS_MSG_XMIT;
+ /*
+ * Rx Chunking State != RCH_Wait_For_Message_From_Protocol_Layer
+ * & Abort Supported
+ *
+ * Discard the Message
+ */
+ if (rch[port].state_id !=
+ RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER) {
+ /* Report Error To Policy Engine */
+ pe_report_error(port, ERR_TCH_XMIT);
+ tch_clear_abort_set_chunking(port);
+ } else {
+ /*
+ * Extended Message Request & Chunking
+ */
+ if ((pdmsg[port].rev == PD_REV30) && pdmsg[port].ext &&
+ (tch[port].flags & PRL_FLAGS_CHUNKING)) {
+ pdmsg[port].send_offset = 0;
+ pdmsg[port].chunk_number_to_send = 0;
+ set_state(port, TCH_OBJ(port),
+ tch_construct_chunked_message);
+ } else
+ /*
+ * Non-Extended Message Request
+ */
+ {
+ /* Make sure buffer doesn't overflow */
+ if (emsg[port].len > BUFFER_SIZE) {
+ /* Report Error To Policy Engine */
+ pe_report_error(port, ERR_TCH_XMIT);
+ tch_clear_abort_set_chunking(port);
+ return 0;
+ }
+
+ /* Copy message to chunked buffer */
+ memset((uint8_t *)pdmsg[port].chk_buf,
+ 0, BUFFER_SIZE);
+ memcpy((uint8_t *)pdmsg[port].chk_buf,
+ (uint8_t *)emsg[port].buf,
+ emsg[port].len);
+ /*
+ * Pad length to 4-byte boundery and
+ * convert to number of 32-bit objects.
+ * Since the value is shifted right by 2,
+ * no need to explicitly clear the lower
+ * 2-bits.
+ */
+ pdmsg[port].data_objs =
+ (emsg[port].len + 3) >> 2;
+ /* Pass Message to Protocol Layer */
+ prl_tx[port].flags |= PRL_FLAGS_MSG_XMIT;
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_transmission_complete);
+ }
+
+ return 0;
+ }
+ }
+
+ return RUN_SUPER;
+}
+
+/*
+ * TchWaitForTransmissionComplete
+ */
+static unsigned int
+ tch_wait_for_transmission_complete(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tch_wait_for_transmission_complete_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tch_wait_for_transmission_complete_entry(int port)
+{
+ tch[port].state_id = TCH_WAIT_FOR_TRANSMISSION_COMPLETE;
+ return 0;
+}
+
+static unsigned int tch_wait_for_transmission_complete_run(int port)
+{
+ /*
+ * Any message received and not in state TCH_Wait_Chunk_Request
+ */
+ if (tch[port].flags & PRL_FLAGS_MSG_RECEIVED) {
+ tch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED;
+ set_state(port, TCH_OBJ(port), tch_message_received);
+ return 0;
+ }
+
+ /*
+ * Inform Policy Engine that Message was sent.
+ */
+ if (pdmsg[port].status_flags & PRL_FLAGS_TX_COMPLETE) {
+ pdmsg[port].status_flags &= ~PRL_FLAGS_TX_COMPLETE;
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_message_request_from_pe);
+
+ /* Tell PE message was sent */
+ pe_message_sent(port);
+ }
+ /*
+ * Inform Policy Engine of Tx Error
+ */
+ else if (pdmsg[port].status_flags & PRL_FLAGS_TX_ERROR) {
+ pdmsg[port].status_flags &= ~PRL_FLAGS_TX_ERROR;
+ /* Tell PE an error occurred */
+ pe_report_error(port, ERR_TCH_XMIT);
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_message_request_from_pe);
+ }
+
+ return 0;
+}
+
+/*
+ * TchConstructChunkedMessage
+ */
+static unsigned int tch_construct_chunked_message(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tch_construct_chunked_message_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tch_construct_chunked_message_entry(int port)
+{
+ uint16_t *ext_hdr;
+ uint8_t *data;
+ uint16_t num;
+
+ tch[port].state_id = TCH_CONSTRUCT_CHUNKED_MESSAGE;
+
+ /*
+ * Any message received and not in state TCH_Wait_Chunk_Request
+ */
+ if (tch[port].flags & PRL_FLAGS_MSG_RECEIVED) {
+ tch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED;
+ set_state(port, TCH_OBJ(port), tch_message_received);
+ return 0;
+ }
+ /* Prepare to copy chunk into chk_buf */
+
+ ext_hdr = (uint16_t *)pdmsg[port].chk_buf;
+ data = ((uint8_t *)pdmsg[port].chk_buf + 2);
+ num = emsg[port].len - pdmsg[port].send_offset;
+
+ if (num > 26)
+ num = 26;
+
+ /* Set the chunks extended header */
+ *ext_hdr = PD_EXT_HEADER(pdmsg[port].chunk_number_to_send,
+ 0, /* Chunk Request */
+ emsg[port].len);
+
+ /* Copy the message chunk into chk_buf */
+ memset(data, 0, 28);
+ memcpy(data, emsg[port].buf + pdmsg[port].send_offset, num);
+ pdmsg[port].send_offset += num;
+
+ /*
+ * Add in 2 bytes for extended header
+ * pad out to 4-byte boundary
+ * convert to number of 4-byte words
+ * Since the value is shifted right by 2,
+ * no need to explicitly clear the lower
+ * 2-bits.
+ */
+ pdmsg[port].data_objs = (num + 2 + 3) >> 2;
+
+ /* Pass message chunk to Protocol Layer */
+ prl_tx[port].flags |= PRL_FLAGS_MSG_XMIT;
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0);
+
+ return 0;
+}
+
+static unsigned int tch_construct_chunked_message_run(int port)
+{
+ if (pdmsg[port].status_flags & PRL_FLAGS_ABORT)
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_message_request_from_pe);
+ else
+ set_state(port, TCH_OBJ(port),
+ tch_sending_chunked_message);
+ return 0;
+}
+
+/*
+ * TchSendingChunkedMessage
+ */
+static unsigned int tch_sending_chunked_message(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tch_sending_chunked_message_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tch_sending_chunked_message_entry(int port)
+{
+ tch[port].state_id = TCH_SENDING_CHUNKED_MESSAGE;
+ return 0;
+}
+
+static unsigned int tch_sending_chunked_message_run(int port)
+{
+ /*
+ * Any message received and not in state TCH_Wait_Chunk_Request
+ */
+ if (tch[port].flags & PRL_FLAGS_MSG_RECEIVED) {
+ tch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED;
+ set_state(port, TCH_OBJ(port), tch_message_received);
+ return 0;
+ }
+
+ /*
+ * Transmission Error
+ */
+ if (pdmsg[port].status_flags & PRL_FLAGS_TX_ERROR) {
+ pe_report_error(port, ERR_TCH_XMIT);
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_message_request_from_pe);
+ }
+ /*
+ * Message Transmitted from Protocol Layer &
+ * Last Chunk
+ */
+ else if (emsg[port].len == pdmsg[port].send_offset) {
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_message_request_from_pe);
+
+ /* Tell PE message was sent */
+ pe_message_sent(port);
+ }
+ /*
+ * Message Transmitted from Protocol Layer &
+ * Not Last Chunk
+ */
+ else
+ set_state(port, TCH_OBJ(port), tch_wait_chunk_request);
+
+ return 0;
+}
+
+/*
+ * TchWaitChunkRequest
+ */
+static unsigned int tch_wait_chunk_request(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tch_wait_chunk_request_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tch_wait_chunk_request_entry(int port)
+{
+ tch[port].state_id = TCH_WAIT_CHUNK_REQUEST;
+
+ /* Increment Chunk Number to Send */
+ pdmsg[port].chunk_number_to_send++;
+ /* Start Chunk Sender Request Timer */
+ tch[port].chunk_sender_request_timer =
+ get_time().val + PD_T_CHUNK_SENDER_REQUEST;
+ return 0;
+}
+
+static unsigned int tch_wait_chunk_request_run(int port)
+{
+ if (tch[port].flags & PRL_FLAGS_MSG_RECEIVED) {
+ tch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED;
+
+ if (PD_HEADER_EXT(emsg[port].header)) {
+ uint16_t exthdr;
+
+ exthdr = GET_EXT_HEADER(pdmsg[port].chk_buf[0]);
+ if (PD_EXT_HEADER_REQ_CHUNK(exthdr)) {
+ /*
+ * Chunk Request Received &
+ * Chunk Number = Chunk Number to Send
+ */
+ if (PD_EXT_HEADER_CHUNK_NUM(exthdr) ==
+ pdmsg[port].chunk_number_to_send) {
+ set_state(port, TCH_OBJ(port),
+ tch_construct_chunked_message);
+ }
+ /*
+ * Chunk Request Received &
+ * Chunk Number != Chunk Number to Send
+ */
+ else {
+ pe_report_error(port, ERR_TCH_CHUNKED);
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_message_request_from_pe);
+ }
+ return 0;
+ }
+ }
+
+ /*
+ * Other message received
+ */
+ set_state(port, TCH_OBJ(port), tch_message_received);
+ }
+ /*
+ * ChunkSenderRequestTimer timeout
+ */
+ else if (get_time().val >=
+ tch[port].chunk_sender_request_timer) {
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_message_request_from_pe);
+
+ /* Tell PE message was sent */
+ pe_message_sent(port);
+ }
+
+ return 0;
+}
+
+/*
+ * TchMessageReceived
+ */
+static unsigned int tch_message_received(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tch_message_received_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tch_message_received_entry(int port)
+{
+ tch[port].state_id = TCH_MESSAGE_RECEIVED;
+
+ /* Pass message to chunked Rx */
+ rch[port].flags |= PRL_FLAGS_MSG_RECEIVED;
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0);
+
+ return 0;
+}
+
+static unsigned int tch_message_received_run(int port)
+{
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_message_request_from_pe);
+
+ return 0;
+}
+
+/*
+ * Protocol Layer Message Reception State Machine
+ */
+static unsigned int prl_rx_wait_for_phy_message(int port, int evt)
+{
+ uint32_t header;
+ uint8_t type;
+ uint8_t cnt;
+ uint8_t sop;
+ int8_t msid;
+ int ret;
+
+ /* process any potential incoming message */
+ if (tcpm_has_pending_message(port)) {
+ ret = tcpm_dequeue_message(port, pdmsg[port].chk_buf, &header);
+ if (ret == 0) {
+ emsg[port].header = header;
+ type = PD_HEADER_TYPE(header);
+ cnt = PD_HEADER_CNT(header);
+ msid = PD_HEADER_ID(header);
+ sop = PD_HEADER_GET_SOP(header);
+
+ if (cnt == 0 && type == PD_CTRL_SOFT_RESET) {
+ int i;
+
+ for (i = 0; i < NUM_XMIT_TYPES; i++) {
+ /* Clear MessageIdCounter */
+ prl_tx[port].msg_id_counter[i] = 0;
+ /* Clear stored MessageID value */
+ prl_rx[port].msg_id[i] = -1;
+ }
+
+ /* Inform Policy Engine of Soft Reset */
+ pe_got_soft_reset(port);
+
+ /* Soft Reset occurred */
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_phy_layer_reset);
+ set_state(port, RCH_OBJ(port),
+ rch_wait_for_message_from_protocol_layer);
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_message_request_from_pe);
+ }
+
+ /*
+ * Ignore if this is a duplicate message.
+ */
+ if (prl_rx[port].msg_id[sop] != msid) {
+ /*
+ * Discard any pending tx message if this is
+ * not a ping message
+ */
+ if ((pdmsg[port].rev == PD_REV30) &&
+ (cnt == 0) && type != PD_CTRL_PING) {
+ if (prl_tx[port].state_id ==
+ PRL_TX_SRC_PENDING ||
+ prl_tx[port].state_id ==
+ PRL_TX_SNK_PENDING) {
+ set_state(port,
+ PRL_TX_OBJ(port),
+ prl_tx_discard_message);
+ }
+ }
+
+ /* Store Message Id */
+ prl_rx[port].msg_id[sop] = msid;
+
+ /* RTR Chunked Message Router States. */
+ /*
+ * Received Ping from Protocol Layer
+ */
+ if (cnt == 0 && type == PD_CTRL_PING) {
+ /* NOTE: RTR_PING State embedded
+ * here.
+ */
+ emsg[port].len = 0;
+ pe_pass_up_message(port);
+ return 0;
+ }
+ /*
+ * Message (not Ping) Received from
+ * Protocol Layer & Doing Tx Chunks
+ */
+ else if (tch[port].state_id !=
+ TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE) {
+ /* NOTE: RTR_TX_CHUNKS State embedded
+ * here.
+ */
+ /*
+ * Send Message to Tx Chunk
+ * Chunk State Machine
+ */
+ tch[port].flags |=
+ PRL_FLAGS_MSG_RECEIVED;
+ }
+ /*
+ * Message (not Ping) Received from
+ * Protocol Layer & Not Doing Tx Chunks
+ */
+ else {
+ /*
+ * NOTE: RTR_RX_CHUNKS State embedded
+ * here.
+ */
+ /*
+ * Send Message to Rx
+ * Chunk State Machine
+ */
+ rch[port].flags |=
+ PRL_FLAGS_MSG_RECEIVED;
+ }
+
+ task_set_event(PD_PORT_TO_TASK_ID(port),
+ PD_EVENT_SM, 0);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static unsigned int do_nothing_exit(int port)
+{
+ return 0;
+}
+
+static unsigned int get_super_state(int port)
+{
+ return RUN_SUPER;
+}
diff --git a/common/usb_sm.c b/common/usb_sm.c
new file mode 100644
index 0000000000..6d3cacdb3b
--- /dev/null
+++ b/common/usb_sm.c
@@ -0,0 +1,166 @@
+/* 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 "common.h"
+#include "task.h"
+#include "usb_pd.h"
+#include "usb_sm.h"
+#include "util.h"
+#include "console.h"
+
+void init_state(int port, struct sm_obj *obj, sm_state target)
+{
+#if (CONFIG_SM_NESTING_NUM > 0)
+ int i;
+
+ sm_state tmp_super[CONFIG_SM_NESTING_NUM];
+#endif
+
+ obj->last_state = NULL;
+ obj->task_state = target;
+
+#if (CONFIG_SM_NESTING_NUM > 0)
+
+ /* Prepare to execute all entry actions of the target's super states */
+
+ /*
+ * Get targets super state. This will be NULL if the target
+ * has no super state
+ */
+ tmp_super[CONFIG_SM_NESTING_NUM - 1] =
+ (sm_state)(uintptr_t)target(port, SUPER_SIG);
+
+ /* Get all super states of the target */
+ for (i = CONFIG_SM_NESTING_NUM - 1; i > 0; i--) {
+ if (tmp_super[i] != NULL)
+ tmp_super[i - 1] =
+ (sm_state)(uintptr_t)tmp_super[i](port, SUPER_SIG);
+ else
+ tmp_super[i - 1] = NULL;
+ }
+
+ /* Execute all super state entry actions in forward order */
+ for (i = 0; i < CONFIG_SM_NESTING_NUM; i++)
+ if (tmp_super[i] != NULL)
+ tmp_super[i](port, ENTRY_SIG);
+#endif
+
+ /* Now execute the target entry action */
+ target(port, ENTRY_SIG);
+}
+
+int set_state(int port, struct sm_obj *obj, sm_state target)
+{
+#if (CONFIG_SM_NESTING_NUM > 0)
+ int i;
+ int no_execute;
+
+ sm_state tmp_super[CONFIG_SM_NESTING_NUM];
+ sm_state target_super;
+ sm_state last_super;
+ sm_state super;
+
+ /* Execute all exit actions is reverse order */
+
+ /* Get target's super state */
+ target_super = (sm_state)(uintptr_t)target(port, SUPER_SIG);
+ tmp_super[0] = obj->task_state;
+
+ do {
+ /* Execute exit action */
+ tmp_super[0](port, EXIT_SIG);
+
+ /* Get super state */
+ tmp_super[0] =
+ (sm_state)(uintptr_t)tmp_super[0](port, SUPER_SIG);
+ /*
+ * No need to execute a super state's exit action that has
+ * shared ancestry with the target.
+ */
+ super = target_super;
+ while (super != NULL) {
+ if (tmp_super[0] == super) {
+ tmp_super[0] = NULL;
+ break;
+ }
+
+ /* Get target state next super state if it exists */
+ super = (sm_state)(uintptr_t)super(port, SUPER_SIG);
+ }
+ } while (tmp_super[0] != NULL);
+
+ /* All done executing the exit actions */
+#else
+ obj->task_state(port, EXIT_SIG);
+#endif
+ /* update the state variables */
+ obj->last_state = obj->task_state;
+ obj->task_state = target;
+
+#if (CONFIG_SM_NESTING_NUM > 0)
+ /* Prepare to execute all entry actions of the target's super states */
+
+ tmp_super[CONFIG_SM_NESTING_NUM - 1] =
+ (sm_state)(uintptr_t)target(port, SUPER_SIG);
+
+ /* Get all super states of the target */
+ for (i = CONFIG_SM_NESTING_NUM - 1; i > 0; i--) {
+ if (tmp_super[i] != NULL)
+ tmp_super[i - 1] =
+ (sm_state)(uintptr_t)tmp_super[i](port, SUPER_SIG);
+ else
+ tmp_super[i - 1] = NULL;
+ }
+
+ /* Get super state of last state */
+ last_super = (sm_state)(uintptr_t)obj->last_state(port, SUPER_SIG);
+
+ /* Execute all super state entry actions in forward order */
+ for (i = 0; i < CONFIG_SM_NESTING_NUM; i++) {
+ /* No super state */
+ if (tmp_super[i] == NULL)
+ continue;
+
+ /*
+ * We only want to execute the target state's super state entry
+ * action if it doesn't share a super state with the previous
+ * state.
+ */
+ super = last_super;
+ no_execute = 0;
+ while (super != NULL) {
+ if (tmp_super[i] == super) {
+ no_execute = 1;
+ break;
+ }
+
+ /* Get last state's next super state if it exists */
+ super = (sm_state)(uintptr_t)super(port, SUPER_SIG);
+ }
+
+ /* Execute super state's entry */
+ if (!no_execute)
+ tmp_super[i](port, ENTRY_SIG);
+ }
+#endif
+
+ /* Now execute the target entry action */
+ target(port, ENTRY_SIG);
+
+ return 0;
+}
+
+void exe_state(int port, struct sm_obj *obj, enum signal sig)
+{
+#if (CONFIG_SM_NESTING_NUM > 0)
+ sm_state state = obj->task_state;
+
+ do {
+ state = (sm_state)(uintptr_t)state(port, sig);
+ } while (state != NULL);
+#else
+ obj->task_state(port, sig);
+#endif
+}
diff --git a/common/usb_tc_sm.c b/common/usb_tc_sm.c
new file mode 100644
index 0000000000..d9c7992b82
--- /dev/null
+++ b/common/usb_tc_sm.c
@@ -0,0 +1,203 @@
+/* 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 "battery.h"
+#include "battery_smart.h"
+#include "board.h"
+#include "charge_manager.h"
+#include "charge_state.h"
+#include "chipset.h"
+#include "common.h"
+#include "console.h"
+#include "ec_commands.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "registers.h"
+#include "system.h"
+#include "task.h"
+#include "timer.h"
+#include "util.h"
+#include "usb_charge.h"
+#include "usb_mux.h"
+#include "usb_pd.h"
+#include "usb_pd_tcpm.h"
+#include "usb_prl_sm.h"
+#include "tcpm.h"
+#include "usb_pe_sm.h"
+#include "usb_prl_sm.h"
+#include "usb_sm.h"
+#include "usb_tc_sm.h"
+#include "version.h"
+
+#ifdef CONFIG_COMMON_RUNTIME
+#define CPRINTF(format, args...) cprintf(CC_HOOK, format, ## args)
+#define CPRINTS(format, args...) cprints(CC_HOOK, format, ## args)
+#else /* CONFIG_COMMON_RUNTIME */
+#define CPRINTF(format, args...)
+#define CPRINTS(format, args...)
+#endif
+
+/* Private Function Prototypes */
+
+static inline int cc_is_rp(int cc);
+static inline enum pd_cc_polarity_type get_snk_polarity(int cc1, int cc2);
+static int tc_restart_tcpc(int port);
+static void set_polarity(int port, int polarity);
+
+#ifdef CONFIG_COMMON_RUNTIME
+static const char * const tc_state_names[] = {
+ "Disabled",
+ "Unattached.SNK",
+ "AttachWait.SNK",
+ "Attached.SNK",
+#if !defined(CONFIG_USB_TYPEC_VPD)
+ "ErrorRecovery",
+ "Unattached.SRC",
+ "AttachWait.SRC",
+ "Attached.SRC",
+#endif
+#if !defined(CONFIG_USB_TYPEC_CTVPD) && !defined(CONFIG_USB_TYPEC_VPD)
+ "AudioAccessory",
+ "OrientedDebugAccessory.SRC",
+ "UnorientedDebugAccessory.SRC",
+ "DebugAccessory.SNK",
+ "Try.SRC",
+ "TryWait.SNK",
+ "CTUnattached.SNK",
+ "CTAttached.SNK",
+#endif
+#if defined(CONFIG_USB_TYPEC_CTVPD)
+ "CTTry.SNK",
+ "CTAttached.Unsupported",
+ "CTAttachWait.Unsupported",
+ "CTUnattached.Unsupported",
+ "CTUnattached.VPD",
+ "CTAttachWait.VPD",
+ "CTAttached.VPD",
+ "CTDisabled.VPD",
+ "Try.SNK",
+ "TryWait.SRC"
+#endif
+};
+BUILD_ASSERT(ARRAY_SIZE(tc_state_names) == TC_STATE_COUNT);
+#endif
+
+/* Include USB Type-C State Machine */
+#if defined(CONFIG_USB_TYPEC_CTVPD)
+#include "usb_tc_ctvpd_sm.h"
+#elif defined(CONFIG_USB_TYPEC_VPD)
+#include "usb_tc_vpd_sm.h"
+#else
+#error "A USB Type-C State Machine must be defined."
+#endif
+
+/* Public Functions */
+
+int tc_get_power_role(int port)
+{
+ return tc[port].power_role;
+}
+
+int tc_get_data_role(int port)
+{
+ return tc[port].data_role;
+}
+
+void tc_set_timeout(int port, uint64_t timeout)
+{
+ tc[port].evt_timeout = timeout;
+}
+
+enum typec_state_id get_typec_state_id(int port)
+{
+ return tc[port].state_id;
+}
+
+/* Private Functions */
+
+/**
+ * Returns whether the sink has detected a Rp resistor on the other side.
+ */
+static inline int cc_is_rp(int cc)
+{
+ return (cc == TYPEC_CC_VOLT_RP_DEF) || (cc == TYPEC_CC_VOLT_RP_1_5) ||
+ (cc == TYPEC_CC_VOLT_RP_3_0);
+}
+
+/*
+ * CC values for regular sources and Debug sources (aka DTS)
+ *
+ * Source type Mode of Operation CC1 CC2
+ * ---------------------------------------------
+ * Regular Default USB Power RpUSB Open
+ * Regular USB-C @ 1.5 A Rp1A5 Open
+ * Regular USB-C @ 3 A Rp3A0 Open
+ * DTS Default USB Power Rp3A0 Rp1A5
+ * DTS USB-C @ 1.5 A Rp1A5 RpUSB
+ * DTS USB-C @ 3 A Rp3A0 RpUSB
+ */
+
+/**
+ * Returns the polarity of a Sink.
+ */
+static inline enum pd_cc_polarity_type get_snk_polarity(int cc1, int cc2)
+{
+ /* the following assumes:
+ * TYPEC_CC_VOLT_RP_3_0 > TYPEC_CC_VOLT_RP_1_5
+ * TYPEC_CC_VOLT_RP_1_5 > TYPEC_CC_VOLT_RP_DEF
+ * TYPEC_CC_VOLT_RP_DEF > TYPEC_CC_VOLT_OPEN
+ */
+ return (cc2 > cc1) ? POLARITY_CC2 : POLARITY_CC1;
+}
+
+static int tc_restart_tcpc(int port)
+{
+ return tcpm_init(port);
+}
+
+static void set_polarity(int port, int polarity)
+{
+ tcpm_set_polarity(port, polarity);
+#ifdef CONFIG_USBC_PPC_POLARITY
+ ppc_set_polarity(port, polarity);
+#endif /* defined(CONFIG_USBC_PPC_POLARITY) */
+}
+
+void pd_task(void *u)
+{
+ int port = TASK_ID_TO_PD_PORT(task_get_current());
+
+ tc_state_init(port);
+
+ while (1) {
+ /* wait for next event/packet or timeout expiration */
+ tc[port].evt = task_wait_event(tc[port].evt_timeout);
+
+ /* handle events that affect the state machine as a whole */
+ tc_event_check(port, tc[port].evt);
+
+#ifdef CONFIG_USB_PD_TCPC
+ /*
+ * run port controller task to check CC and/or read incoming
+ * messages
+ */
+ tcpc_run(port, tc[port].evt);
+#endif
+
+#ifdef CONFIG_USB_PE_SM
+ /* run policy engine state machine */
+ policy_engine(port, tc[port].evt, tc[port].pd_enable);
+#endif /* CONFIG_USB_PE_SM */
+
+#ifdef CONFIG_USB_PRL_SM
+ /* run protocol state machine */
+ protocol_layer(port, tc[port].evt, tc[port].pd_enable);
+#endif /* CONFIG_USB_PRL_SM */
+
+ /* run state machine */
+ exe_state(port, TC_OBJ(port), RUN_SIG);
+ }
+}
diff --git a/include/config.h b/include/config.h
index 8c03da112b..89ade13c9c 100644
--- a/include/config.h
+++ b/include/config.h
@@ -779,6 +779,33 @@
*/
#undef CONFIG_CHARGER_INPUT_CURRENT
+/* Define to use Power Delivery State Machine Framework */
+#undef CONFIG_USB_SM_FRAMEWORK
+
+/*
+ * This is the maximum number of levels in the hierarchical
+ * state machine framework. Set to 0 for a flat state machine.
+ */
+#define CONFIG_SM_NESTING_NUM 3
+
+/*
+ * Define to enable Type-C State Machine. Must be enabled
+ * with CONFIG_USB_SM_FRAMEWORK
+ */
+#define CONFIG_USB_TYPEC_SM
+
+/*
+ * Define to enable Protocol Layer State Machine. Must be enabled
+ * with CONFIG_USB_SM_FRAMEWORK and CONFIG_USB_TYPEC_SM
+ */
+#define CONFIG_USB_PRL_SM
+
+/*
+ * Define to enable Policy Engine State Machine. Must be enabled
+ * with CONFIG_USB_SM_FRAMEWORK and CONFIG_USB_TYPEC_SM
+ */
+#define CONFIG_USB_PE_SM
+
/*
* Board specific maximum input current limit, in mA.
*/
@@ -3500,6 +3527,12 @@
/* Use DAC as reference for comparator at 850mV. */
#undef CONFIG_PD_USE_DAC_AS_REF
+/* Type-C VCONN Powered Device */
+#undef CONFIG_USB_TYPEC_VPD
+
+/* Type-C Charge Through VCONN Powered Device */
+#undef CONFIG_USB_TYPEC_CTVPD
+
/* USB Product ID. */
#undef CONFIG_USB_PID
diff --git a/include/usb_emsg.h b/include/usb_emsg.h
new file mode 100644
index 0000000000..ffbaa93a0e
--- /dev/null
+++ b/include/usb_emsg.h
@@ -0,0 +1,23 @@
+/* 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.
+ */
+
+/* USB Extended message buffer */
+
+#ifndef __CROS_EC_USB_EBUF_H
+#define __CROS_EC_USB_EBUF_H
+
+#define EXTENDED_BUFFER_SIZE 260
+#define BUFFER_SIZE 28
+
+struct extended_msg {
+ uint32_t header;
+ uint32_t len;
+ uint8_t buf[EXTENDED_BUFFER_SIZE];
+};
+
+/* Defined in usb_prl_sm.c */
+extern struct extended_msg emsg[CONFIG_USB_PD_PORT_COUNT];
+
+#endif /* __CROS_EC_USB_EBUF_H */
diff --git a/include/usb_pd.h b/include/usb_pd.h
index 89202a7ad3..b3888dae7f 100644
--- a/include/usb_pd.h
+++ b/include/usb_pd.h
@@ -49,6 +49,7 @@ enum pd_rx_errors {
#define PD_EVENT_DEVICE_ACCESSED (1<<7)
#define PD_EVENT_POWER_STATE_CHANGE (1<<8) /* Chipset power state changed */
#define PD_EVENT_SEND_HARD_RESET (1<<9) /* Issue a Hard Reset. */
+#define PD_EVENT_SM (1<<10) /* PD State machine event */
/* Ensure TCPC is out of low power mode before handling these events. */
#define PD_EXIT_LOW_POWER_EVENT_MASK \
@@ -168,6 +169,7 @@ enum pd_rx_errors {
#define PD_T_DRP_SNK (40*MSEC) /* toggle time for sink DRP */
#define PD_T_DRP_SRC (30*MSEC) /* toggle time for source DRP */
#define PD_T_DEBOUNCE (15*MSEC) /* between 10ms and 20ms */
+#define PD_T_TRY_CC_DEBOUNCE (15*MSEC) /* between 10ms and 20ms */
#define PD_T_SINK_ADJ (55*MSEC) /* between PD_T_DEBOUNCE and 60ms */
#define PD_T_SRC_RECOVER (760*MSEC) /* between 660ms and 1000ms */
#define PD_T_SRC_RECOVER_MAX (1000*MSEC) /* 1000ms */
@@ -181,6 +183,10 @@ enum pd_rx_errors {
#define PD_T_TRY_TIMEOUT (550*MSEC) /* between 550ms and 1100ms */
#define PD_T_TRY_WAIT (600*MSEC) /* Max time for TryWait.SNK state */
#define PD_T_SINK_REQUEST (100*MSEC) /* Wait 100ms before next request */
+#define PD_T_PD_DEBOUNCE (15*MSEC) /* between 10ms and 20ms */
+#define PD_T_CHUNK_SENDER_RESPONSE (25*MSEC) /* 25ms */
+#define PD_T_CHUNK_SENDER_REQUEST (25*MSEC) /* 25ms */
+#define PD_T_SWAP_SOURCE_START (25*MSEC) /* Min of 20ms */
/* number of edges and time window to detect CC line is not idle */
#define PD_RX_TRANSITION_COUNT 3
@@ -196,6 +202,11 @@ enum pd_rx_errors {
#define PD_T_VDM_SNDR_RSP (30*MSEC) /* max of 30ms */
#define PD_T_VDM_WAIT_MODE_E (100*MSEC) /* enter/exit the same max */
+/* CTVPD Timers ( USB Type-C ECN Table 4-27 ) */
+#define PD_T_VPDDETACH (20*MSEC) /* max of 20*MSEC */
+#define PD_T_VPDCTDD (4*MSEC) /* max of 4ms */
+#define PD_T_VPDDISABLE (25*MSEC) /* min of 25ms */
+
/* function table for entered mode */
struct amode_fx {
int (*status)(int port, uint32_t *payload);
@@ -391,6 +402,7 @@ struct pd_policy {
#define IDH_PTYPE_PCABLE 3
#define IDH_PTYPE_ACABLE 4
#define IDH_PTYPE_AMA 5
+#define IDH_PTYPE_VPD 6
#define VDO_IDH(usbh, usbd, ptype, is_modal, vid) \
((usbh) << 31 | (usbd) << 30 | ((ptype) & 0x7) << 27 \
@@ -491,6 +503,34 @@ struct pd_policy {
#define AMA_USBSS_BBONLY 3
/*
+ * VPD VDO
+ * ---------
+ * <31:28> :: HW version
+ * <27:24> :: FW version
+ * <23:21> :: VDO version
+ * <20:17> :: SBZ
+ * <16:15> :: Maximum VBUS Voltage
+ * <14:13> :: SBZ
+ * <12:7> :: VBUS Impedance
+ * <6:1> :: Ground Impedance
+ * <0> :: Charge Through Support
+ */
+#define VDO_VPD(hw, fw, vbus, vbusz, gndz, cts) \
+ (((hw) & 0xf) << 28 | ((fw) & 0xf) << 24 \
+ | ((vbus) & 0x3) << 15 \
+ | ((vbusz) & 0x3f) << 7 \
+ | ((gndz) & 0x3f) << 1 | (cts))
+
+#define VPD_MAX_VBUS_20V 0
+#define VPD_MAX_VBUS_30V 1
+#define VPD_MAX_VBUS_40V 2
+#define VPD_MAX_VBUS_50V 3
+#define VPD_VBUS_IMP(mo) ((mo + 1) >> 1)
+#define VPD_GND_IMP(mo) (mo)
+#define VPD_CTS_SUPPORTED 1
+#define VPD_CTS_NOT_SUPPORTED 0
+
+/*
* SVDM Discover SVIDs request -> response
*
* Request is properly formatted VDM Header with discover SVIDs command.
@@ -787,6 +827,9 @@ enum pd_states {
#define PD_BBRMFLG_DATA_ROLE BIT(2)
#define PD_BBRMFLG_VCONN_ROLE BIT(3)
+/* Initial value for CC debounce variable */
+#define PD_CC_UNSET -1
+
enum pd_cc_states {
PD_CC_NONE,
@@ -912,14 +955,25 @@ enum pd_data_msg_type {
PD_DATA_VENDOR_DEF = 15,
};
+/* CC Polarity type */
+enum pd_cc_polarity_type {
+ POLARITY_CC1,
+ POLARITY_CC2
+};
+
/* Protocol revision */
-#define PD_REV10 0
-#define PD_REV20 1
-#define PD_REV30 2
+enum pd_rev_type {
+ PD_REV10,
+ PD_REV20,
+ PD_REV30
+};
/* Power role */
#define PD_ROLE_SINK 0
#define PD_ROLE_SOURCE 1
+/* Cable plug */
+#define PD_PLUG_DFP_UFP 0
+#define PD_PLUG_CABLE_VPD 1
/* Data role */
#define PD_ROLE_UFP 0
#define PD_ROLE_DFP 1
@@ -998,6 +1052,9 @@ enum pd_data_msg_type {
#define PD_EXT_HEADER_REQ_CHUNK(header) (((header) >> 10) & 1)
#define PD_EXT_HEADER_DATA_SIZE(header) ((header) & 0x1ff)
+/* Used to get extended header from the first 32-bit word of the message */
+#define GET_EXT_HEADER(msg) (msg & 0xffff)
+
/* K-codes for special symbols */
#define PD_SYNC1 0x18
#define PD_SYNC2 0x11
diff --git a/include/usb_pd_tcpm.h b/include/usb_pd_tcpm.h
index d46aef454d..bdd497b563 100644
--- a/include/usb_pd_tcpm.h
+++ b/include/usb_pd_tcpm.h
@@ -16,6 +16,9 @@
/* Time to wait for TCPC to complete transmit */
#define PD_T_TCPC_TX_TIMEOUT (100*MSEC)
+/* Number of valid Transmit Types */
+#define NUM_XMIT_TYPES (TCPC_TX_SOP_DEBUG_PRIME_PRIME + 1)
+
/* Detected resistor values of port partner */
enum tcpc_cc_voltage_status {
TYPEC_CC_VOLT_OPEN = 0,
@@ -32,6 +35,7 @@ enum tcpc_cc_pull {
TYPEC_CC_RP = 1,
TYPEC_CC_RD = 2,
TYPEC_CC_OPEN = 3,
+ TYPEC_CC_RA_RD = 4, /* Powered cable with Sink */
};
/* Pull-up values we apply as a SRC to advertise different current limits */
@@ -54,6 +58,7 @@ enum tcpm_transmit_type {
};
enum tcpc_transmit_complete {
+ TCPC_TX_UNSET = -1,
TCPC_TX_COMPLETE_SUCCESS = 0,
TCPC_TX_COMPLETE_DISCARDED = 1,
TCPC_TX_COMPLETE_FAILED = 2,
diff --git a/include/usb_pe_ctvpd_sm.h b/include/usb_pe_ctvpd_sm.h
new file mode 100644
index 0000000000..e2b2cde8c4
--- /dev/null
+++ b/include/usb_pe_ctvpd_sm.h
@@ -0,0 +1,239 @@
+/* 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 "common.h"
+#include "console.h"
+#include "task.h"
+#include "util.h"
+#include "usb_pd.h"
+#include "usb_pd_tcpm.h"
+#include "usb_pe_sm.h"
+#include "usb_prl_sm.h"
+#include "usb_tc_sm.h"
+#include "usb_emsg.h"
+#include "usb_sm.h"
+
+/* USB Policy Engine Charge-Through VCONN Powered Device module */
+
+#ifndef __CROS_EC_USB_PE_CTVPD_H
+#define __CROS_EC_USB_PE_CTVPD_H
+
+/* Policy Engine Flags */
+#define PE_FLAGS_MSG_RECEIVED (1 << 0)
+
+enum l_state {
+ PE_INIT,
+ PE_RUN,
+ PE_PAUSED
+};
+
+static enum l_state local_state = PE_INIT;
+
+/*
+ * PE_OBJ is a convenience macro to access struct sm_obj, which
+ * must be the first member of struct policy_engine.
+ */
+#define PE_OBJ(port) (SM_OBJ(pe[port]))
+
+/**
+ * This is the PE Port object that contains information needed to
+ * implement a VCONN and Charge-Through VCONN Powered Device.
+ */
+static struct policy_engine {
+ /*
+ * struct sm_obj must be first. This is the state machine
+ * object that keeps track of the current and last state
+ * of the state machine.
+ */
+ struct sm_obj obj;
+ /* port flags, see PE_FLAGS_* */
+ uint32_t flags;
+} pe[CONFIG_USB_PD_PORT_COUNT];
+
+static unsigned int pe_request(int port, enum signal sig);
+static unsigned int pe_request_entry(int port);
+static unsigned int pe_request_run(int port);
+
+static unsigned int do_nothing_exit(int port);
+static unsigned int get_super_state(int port);
+
+static const state_sig pe_request_sig[] = {
+ pe_request_entry,
+ pe_request_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+void pe_init(int port)
+{
+ pe[port].flags = 0;
+ init_state(port, PE_OBJ(port), pe_request);
+}
+
+void policy_engine(int port, int evt, int en)
+{
+ switch (local_state) {
+ case PE_INIT:
+ pe_init(port);
+ local_state = PE_RUN;
+ /* fall through */
+ case PE_RUN:
+ if (!en) {
+ local_state = PE_PAUSED;
+ break;
+ }
+
+ exe_state(port, PE_OBJ(port), RUN_SIG);
+ break;
+ case PE_PAUSED:
+ if (en)
+ local_state = PE_INIT;
+ break;
+ }
+}
+
+void pe_pass_up_message(int port)
+{
+ pe[port].flags |= PE_FLAGS_MSG_RECEIVED;
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0);
+}
+
+void pe_hard_reset_sent(int port)
+{
+ /* Do nothing */
+}
+
+void pe_got_hard_reset(int port)
+{
+ /* Do nothing */
+}
+
+void pe_report_error(int port, enum pe_error e)
+{
+ /* Do nothing */
+}
+
+void pe_got_soft_reset(int port)
+{
+ /* Do nothing */
+}
+
+void pe_message_sent(int port)
+{
+ /* Do nothing */
+}
+
+static unsigned int pe_request(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*pe_request_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int pe_request_entry(int port)
+{
+ return 0;
+}
+
+static unsigned int pe_request_run(int port)
+{
+ uint32_t *payload = (uint32_t *)emsg[port].buf;
+ uint32_t header = emsg[port].header;
+ uint32_t vdo = payload[0];
+
+ if (pe[port].flags & PE_FLAGS_MSG_RECEIVED) {
+ pe[port].flags &= ~PE_FLAGS_MSG_RECEIVED;
+
+ /*
+ * Only support Structured VDM Discovery
+ * Identity message
+ */
+
+ if (PD_HEADER_TYPE(header) != PD_DATA_VENDOR_DEF)
+ return 0;
+
+ if (PD_HEADER_CNT(header) == 0)
+ return 0;
+
+ if (!PD_VDO_SVDM(vdo))
+ return 0;
+
+ if (PD_VDO_CMD(vdo) != CMD_DISCOVER_IDENT)
+ return 0;
+
+#ifdef CONFIG_USB_TYPEC_CTVPD
+ /*
+ * We have a valid DISCOVER IDENTITY message.
+ * Attempt to reset support timer
+ */
+ tc_reset_support_timer(port);
+#endif
+ /* Prepare to send ACK */
+
+ /* VDM Header */
+ payload[0] = VDO(
+ USB_VID_GOOGLE,
+ 1, /* Structured VDM */
+ VDO_SVDM_VERS(1) |
+ VDO_CMDT(CMDT_RSP_ACK) |
+ CMD_DISCOVER_IDENT);
+
+ /* ID Header VDO */
+ payload[1] = 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);
+
+ /* Cert State VDO */
+ payload[2] = 0;
+
+ /* Product VDO */
+ payload[3] = VDO_PRODUCT(
+ CONFIG_USB_PID,
+ USB_BCD_DEVICE);
+
+ /* VPD VDO */
+ payload[4] = 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
+ );
+
+ /* 20 bytes, 5 data objects */
+ emsg[port].len = 20;
+
+ /* Set to highest revision supported by both ports. */
+ prl_set_rev(port, (PD_HEADER_REV(header) > PD_REV30) ?
+ PD_REV30 : PD_HEADER_REV(header));
+
+ /* Send the ACK */
+ prl_send_data_msg(port, TCPC_TX_SOP_PRIME,
+ PD_DATA_VENDOR_DEF);
+ }
+
+ return 0;
+}
+
+static unsigned int do_nothing_exit(int port)
+{
+ return 0;
+}
+
+static unsigned int get_super_state(int port)
+{
+ return RUN_SUPER;
+}
+
+#endif /* __CROS_EC_USB_PE_CTVPD_H */
diff --git a/include/usb_pe_sm.h b/include/usb_pe_sm.h
new file mode 100644
index 0000000000..a8fd59b08c
--- /dev/null
+++ b/include/usb_pe_sm.h
@@ -0,0 +1,79 @@
+/* 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.
+ */
+
+/* USB Policy Engine module */
+
+#ifndef __CROS_EC_USB_PE_H
+#define __CROS_EC_USB_PE_H
+
+enum pe_error {
+ ERR_RCH_CHUNKED,
+ ERR_RCH_MSG_REC,
+ ERR_TCH_CHUNKED,
+ ERR_TCH_XMIT,
+};
+
+/**
+ * Initialize the Policy Engine State Machine
+ *
+ * @param port USB-C port number
+ */
+void pe_init(int port);
+
+/**
+ * Runs the Policy Engine State Machine
+ *
+ * @param port USB-C port number
+ * @param evt system event, ie: PD_EVENT_RX
+ * @param en 0 to disable the machine, 1 to enable the machine
+ */
+void policy_engine(int port, int evt, int en);
+
+/**
+ * Informs the Policy Engine that a message was successfully sent
+ *
+ * @param port USB-C port number
+ */
+void pe_message_sent(int port);
+
+/**
+ * Informs the Policy Engine of an error.
+ *
+ * @param port USB-C port number
+ * @parm e error
+ */
+void pe_report_error(int port, enum pe_error e);
+
+/**
+ * Informs the Policy Engine that a message has been received
+ *
+ * @param port USB-C port number
+ * @parm e error
+ */
+void pe_pass_up_message(int port);
+
+/**
+ * Informs the Policy Engine that a hard reset was received.
+ *
+ * @param port USB-C port number
+ */
+void pe_got_hard_reset(int port);
+
+/**
+ * Informs the Policy Engine that a soft reset was received.
+ *
+ * @param port USB-C port number
+ */
+void pe_got_soft_reset(int port);
+
+/**
+ * Informs the Policy Engine that a hard reset was sent.
+ *
+ * @param port USB-C port number
+ */
+void pe_hard_reset_sent(int port);
+
+#endif /* __CROS_EC_USB_PE_H */
+
diff --git a/include/usb_prl_sm.h b/include/usb_prl_sm.h
new file mode 100644
index 0000000000..c02725baad
--- /dev/null
+++ b/include/usb_prl_sm.h
@@ -0,0 +1,213 @@
+/* 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.
+ */
+
+/* USB Protocol Layer module */
+
+#ifndef __CROS_EC_USB_PRL_H
+#define __CROS_EC_USB_PRL_H
+#include "common.h"
+#include "usb_pd.h"
+#include "usb_pd_tcpm.h"
+
+enum prl_tx_state_id {
+ PRL_TX_PHY_LAYER_RESET,
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST,
+ PRL_TX_LAYER_RESET_FOR_TRANSMIT,
+ PRL_TX_CONSTRUCT_MESSAGE,
+ PRL_TX_WAIT_FOR_PHY_RESPONSE,
+ PRL_TX_MATCH_MESSAGE_ID,
+ PRL_TX_MESSAGE_SENT,
+ PRL_TX_CHECK_RETRY_COUNTER,
+ PRL_TX_TRANSMISSION_ERROR,
+ PRL_TX_DISCARD_MESSAGE,
+
+ PRL_TX_SRC_SINK_TX,
+ PRL_TX_SRC_SOURCE_TX,
+ PRL_TX_SRC_PENDING,
+
+ PRL_TX_SNK_START_OF_AMS,
+ PRL_TX_SNK_PENDING,
+};
+
+enum prl_hr_state_id {
+ PRL_HR_WAIT_FOR_REQUEST,
+ PRL_HR_RESET_LAYER,
+ PRL_HR_INDICATE_HARD_RESET,
+ PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE,
+ PRL_HR_PHY_HARD_RESET_REQUESTED,
+ PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE,
+ PRL_HR_PE_HARD_RESET_COMPLETE,
+};
+
+enum rch_state_id {
+ RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER,
+ RCH_PASS_UP_MESSAGE,
+ RCH_PROCESSING_EXTENDED_MESSAGE,
+ RCH_REQUESTING_CHUNK,
+ RCH_WAITING_CHUNK,
+ RCH_REPORT_ERROR,
+};
+
+enum tch_state_id {
+ TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE,
+ TCH_PASS_DOWN_MESSAGE,
+ TCH_WAIT_FOR_TRANSMISSION_COMPLETE,
+ TCH_MESSAGE_SENT,
+ TCH_PREPARE_TO_SEND_CHUNKED_MESSAGE,
+ TCH_CONSTRUCT_CHUNKED_MESSAGE,
+ TCH_SENDING_CHUNKED_MESSAGE,
+ TCH_WAIT_CHUNK_REQUEST,
+ TCH_MESSAGE_RECEIVED,
+ TCH_REPORT_ERROR,
+};
+
+/*
+ * Number of times the Protocol Layer will try to transmit a message
+ * before giving up and signaling an error
+ */
+#define N_RETRY_COUNT 2
+
+/**
+ * Initialize the Protocol Layer State Machine
+ *
+ * @param port USB-C port number
+ */
+void prl_init(int port);
+
+/**
+ * Resets the Protocol Layer State Machine
+ *
+ * @param port USB-C port number
+ */
+void prl_reset(int port);
+
+/**
+ * Get Chunked Rx State Machine state id
+ *
+ * @param port USB-C port number
+ * @return id
+ */
+enum rch_state_id get_rch_state_id(int port);
+
+/**
+ * Get Chunked Tx State Machine state id
+ *
+ * @param port USB-C port number
+ * @return id
+ */
+enum tch_state_id get_tch_state_id(int port);
+
+/**
+ * Get Message Transmission State Machine state id
+ *
+ * @param port USB-C port number
+ * @return id
+ */
+enum prl_tx_state_id get_prl_tx_state_id(int port);
+
+/**
+ * Get Hard Reset State Machine state id
+ *
+ * @param port USB-C port number
+ * @return id
+ */
+enum prl_hr_state_id get_prl_hr_state_id(int port);
+
+/**
+ * Returns the state of the PRL state machine
+ * @return SM_INIT for initializing
+ * SM_RUN for running
+ * SM_PAUSED for paused
+ */
+enum sm_local_state prl_get_local_state(int port);
+
+/**
+ * Runs the Protocol Layer State Machine
+ *
+ * @param port USB-C port number
+ * @param evt system event, ie: PD_EVENT_RX
+ * @param en 0 to disable the machine, 1 to enable the machine
+ */
+void protocol_layer(int port, int evt, int en);
+
+/**
+ * Set the PD revision
+ *
+ * @param port USB-C port number
+ * @param rev revision
+ */
+void prl_set_rev(int port, enum pd_rev_type rev);
+
+/**
+ * Get the PD revision
+ *
+ * @param port USB-C port number
+ * @return pd rev
+ */
+enum pd_rev_type prl_get_rev(int port);
+
+/**
+ * Sends a PD control message
+ *
+ * @param port USB-C port number
+ * @param type Transmit type
+ * @param msg Control message type
+ * @return 0 on EC_SUCCESS, else EC_ERROR_BUSY
+ */
+void prl_send_ctrl_msg(int port, enum tcpm_transmit_type type,
+ enum pd_ctrl_msg_type msg);
+
+/**
+ * Sends a PD data message
+ *
+ * @param port USB-C port number
+ * @param type Transmit type
+ * @param msg Data message type
+ * @return 0 on EC_SUCCESS, else EC_ERROR_BUSY
+ */
+void prl_send_data_msg(int port, enum tcpm_transmit_type type,
+ enum pd_data_msg_type msg);
+
+/**
+ * Sends a PD extended data message
+ *
+ * @param port USB-C port number
+ * @param type Transmit type
+ * @param msg Extended data message type
+ * @return 0 on EC_SUCCESS, else EC_ERROR_BUSY
+ */
+void prl_send_ext_data_msg(int port, enum tcpm_transmit_type type,
+ enum pd_ext_msg_type msg);
+
+/**
+ * Informs the Protocol Layer that a hard reset has completed
+ *
+ * @param port USB-C port number
+ */
+void prl_hard_reset_complete(int port);
+
+/**
+ * Policy Engine calls this function to execute a hard reset.
+ *
+ * @param port USB-C port number
+ */
+void prl_execute_hard_reset(int port);
+
+/**
+ * Informs the Protocol Layer to start an Atomic Message Sequence
+ *
+ * @param port USB-C port number
+ */
+void prl_start_ams(int port);
+
+/**
+ * Informs the Protocol Layer to end an Atomic Message Sequence
+ *
+ * @param port USB-C port number
+ */
+void prl_end_ams(int port);
+
+#endif /* __CROS_EC_USB_PRL_H */
+
diff --git a/include/usb_sm.h b/include/usb_sm.h
new file mode 100644
index 0000000000..15de5f9ef4
--- /dev/null
+++ b/include/usb_sm.h
@@ -0,0 +1,67 @@
+/* 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.
+ */
+
+/* USB State Machine Framework */
+
+#ifndef __CROS_EC_USB_SM_H
+#define __CROS_EC_USB_SM_H
+
+#define SM_OBJ(smo) ((struct sm_obj *)&smo)
+#define SUPER(r, sig, s) ((((r) == 0) || ((sig) == ENTRY_SIG) || \
+ ((sig) == EXIT_SIG)) ? 0 : ((uintptr_t)(s)))
+#define RUN_SUPER 1
+
+/* Local state machine states */
+enum sm_local_state {
+ SM_INIT,
+ SM_RUN,
+ SM_PAUSED
+};
+
+/* State Machine signals */
+enum signal {
+ ENTRY_SIG = 0,
+ RUN_SIG,
+ EXIT_SIG,
+ SUPER_SIG,
+};
+
+typedef unsigned int (*state_sig)(int port);
+typedef unsigned int (*sm_state)(int port, enum signal sig);
+
+struct sm_obj {
+ sm_state task_state;
+ sm_state last_state;
+};
+
+/**
+ * Initialize a State Machine
+ *
+ * @param port USB-C port number
+ * @param obj State machine object
+ * @param target Initial state of state machine
+ */
+void init_state(int port, struct sm_obj *obj, sm_state target);
+
+/**
+ * Changes a state machines state
+ *
+ * @param port USB-C port number
+ * @param obj State machine object
+ * @param target State to transition to
+ * @return 0
+ */
+int set_state(int port, struct sm_obj *obj, sm_state target);
+
+/**
+ * Executes a state machine
+ *
+ * @param port USB-C port number
+ * @param obj State machine object
+ * @param sig State machine signal
+ */
+void exe_state(int port, struct sm_obj *obj, enum signal sig);
+
+#endif /* __CROS_EC_USB_SM_H */
diff --git a/include/usb_tc_ctvpd_sm.h b/include/usb_tc_ctvpd_sm.h
new file mode 100644
index 0000000000..3b47d35ffa
--- /dev/null
+++ b/include/usb_tc_ctvpd_sm.h
@@ -0,0 +1,2039 @@
+/* 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 "vpd_api.h"
+
+/* USB Type-C CTVPD module */
+
+#ifndef __CROS_EC_USB_TC_VPD_H
+#define __CROS_EC_USB_TC_VPD_H
+
+/* Type-C Layer Flags */
+#define TC_FLAGS_VCONN_ON (1 << 0)
+
+
+#undef PD_DEFAULT_STATE
+/* Port default state at startup */
+#define PD_DEFAULT_STATE(port) tc_state_unattached_snk
+
+#define TC_OBJ(port) (SM_OBJ(tc[port]))
+#define TC_TEST_OBJ(port) (SM_OBJ(tc[(port)].obj))
+
+#define SUPPORT_TIMER_RESET_INIT 0
+#define SUPPORT_TIMER_RESET_REQUEST 1
+#define SUPPORT_TIMER_RESET_COMPLETE 2
+
+static struct type_c {
+ /* struct sm_obj must be first */
+ struct sm_obj obj;
+ /* state id */
+ enum typec_state_id state_id;
+ /* current port power role (VPD, SOURCE or SINK) */
+ uint8_t power_role;
+ /* current port data role (DFP or UFP) */
+ uint8_t data_role;
+ /* enable power delivery state machines */
+ uint8_t pd_enable;
+ /* event timeout */
+ uint64_t evt_timeout;
+ /* state machine event */
+ int evt;
+ /* port flags, see TC_FLAGS_* */
+ uint32_t flags;
+ /*
+ * Time a charge-through port shall wait before it can determine it
+ * is attached
+ */
+ uint64_t cc_debounce;
+ /* Time a host port shall wait before it can determine it is attached */
+ uint64_t host_cc_debounce;
+ /* Time a Sink port shall wait before it can determine it is detached
+ * due to the potential for USB PD signaling on CC as described in
+ * the state definitions.
+ */
+ uint64_t pd_debounce;
+ /* Maintains state of billboard device */
+ int billboard_presented;
+ /*
+ * Time a port shall wait before it can determine it is
+ * re-attached during the try-wait process.
+ */
+ uint64_t try_wait_debounce;
+ /* charge-through support timer */
+ uint64_t support_timer;
+ /* reset the charge-through support timer */
+ uint8_t support_timer_reset;
+ /* VPD host port cc state */
+ enum pd_cc_states host_cc_state;
+ uint8_t ct_cc;
+ /* The cc state */
+ enum pd_cc_states cc_state;
+ uint64_t next_role_swap;
+} tc[CONFIG_USB_PD_PORT_COUNT];
+
+/* Type-C states */
+static unsigned int tc_state_disabled(int port, enum signal sig);
+static unsigned int tc_state_disabled_entry(int port);
+static unsigned int tc_state_disabled_run(int port);
+static unsigned int tc_state_disabled_exit(int port);
+
+static unsigned int tc_state_error_recovery(int port, enum signal sig);
+static unsigned int tc_state_error_recovery_entry(int port);
+static unsigned int tc_state_error_recovery_run(int port);
+
+static unsigned int tc_state_unattached_snk(int port, enum signal sig);
+static unsigned int tc_state_unattached_snk_entry(int port);
+static unsigned int tc_state_unattached_snk_run(int port);
+
+static unsigned int tc_state_attach_wait_snk(int port, enum signal sig);
+static unsigned int tc_state_attach_wait_snk_entry(int port);
+static unsigned int tc_state_attach_wait_snk_run(int port);
+
+static unsigned int tc_state_attached_snk(int port, enum signal sig);
+static unsigned int tc_state_attached_snk_entry(int port);
+static unsigned int tc_state_attached_snk_run(int port);
+static unsigned int tc_state_attached_snk_exit(int port);
+
+static unsigned int tc_state_try_snk(int port, enum signal sig);
+static unsigned int tc_state_try_snk_entry(int port);
+static unsigned int tc_state_try_snk_run(int port);
+
+static unsigned int tc_state_unattached_src(int port, enum signal sig);
+static unsigned int tc_state_unattached_src_entry(int port);
+static unsigned int tc_state_unattached_src_run(int port);
+
+static unsigned int tc_state_attach_wait_src(int port, enum signal sig);
+static unsigned int tc_state_attach_wait_src_entry(int port);
+static unsigned int tc_state_attach_wait_src_run(int port);
+
+static unsigned int tc_state_try_wait_src(int port, enum signal sig);
+static unsigned int tc_state_try_wait_src_entry(int port);
+static unsigned int tc_state_try_wait_src_run(int port);
+
+static unsigned int tc_state_attached_src(int port, enum signal sig);
+static unsigned int tc_state_attached_src_entry(int port);
+static unsigned int tc_state_attached_src_run(int port);
+
+static unsigned int tc_state_ct_try_snk(int port, enum signal sig);
+static unsigned int tc_state_ct_try_snk_entry(int port);
+static unsigned int tc_state_ct_try_snk_run(int port);
+static unsigned int tc_state_ct_try_snk_exit(int port);
+
+static unsigned int
+ tc_state_ct_attach_wait_unsupported(int port, enum signal sig);
+static unsigned int tc_state_ct_attach_wait_unsupported_entry(int port);
+static unsigned int tc_state_ct_attach_wait_unsupported_run(int port);
+static unsigned int tc_state_ct_attach_wait_unsupported_exit(int port);
+
+static unsigned int tc_state_ct_attached_unsupported(int port, enum signal sig);
+static unsigned int tc_state_ct_attached_unsupported_entry(int port);
+static unsigned int tc_state_ct_attached_unsupported_run(int port);
+static unsigned int tc_state_ct_attached_unsupported_exit(int port);
+
+static unsigned int
+ tc_state_ct_unattached_unsupported(int port, enum signal sig);
+static unsigned int tc_state_ct_unattached_unsupported_entry(int port);
+static unsigned int tc_state_ct_unattached_unsupported_run(int port);
+static unsigned int tc_state_ct_unattached_unsupported_exit(int port);
+
+static unsigned int tc_state_ct_unattached_vpd(int port, enum signal sig);
+static unsigned int tc_state_ct_unattached_vpd_entry(int port);
+static unsigned int tc_state_ct_unattached_vpd_run(int port);
+static unsigned int tc_state_ct_unattached_vpd_exit(int port);
+
+static unsigned int tc_state_ct_disabled_vpd(int port, enum signal sig);
+static unsigned int tc_state_ct_disabled_vpd_entry(int port);
+static unsigned int tc_state_ct_disabled_vpd_run(int port);
+
+static unsigned int tc_state_ct_attached_vpd(int port, enum signal sig);
+static unsigned int tc_state_ct_attached_vpd_entry(int port);
+static unsigned int tc_state_ct_attached_vpd_run(int port);
+
+static unsigned int tc_state_ct_attach_wait_vpd(int port, enum signal sig);
+static unsigned int tc_state_ct_attach_wait_vpd_entry(int port);
+static unsigned int tc_state_ct_attach_wait_vpd_run(int port);
+static unsigned int tc_state_ct_attach_wait_vpd_exit(int port);
+
+
+/* Super States */
+static unsigned int tc_state_host_rard_ct_rd(int port, enum signal sig);
+static unsigned int tc_state_host_rard_ct_rd_entry(int port);
+static unsigned int tc_state_host_rard_ct_rd_run(int port);
+
+static unsigned int tc_state_host_open_ct_open(int port, enum signal sig);
+static unsigned int tc_state_host_open_ct_open_entry(int port);
+static unsigned int tc_state_host_open_ct_open_run(int port);
+
+static unsigned int tc_state_vbus_cc_iso(int port, enum signal sig);
+static unsigned int tc_state_vbus_cc_iso_entry(int port);
+static unsigned int tc_state_vbus_cc_iso_run(int port);
+
+static unsigned int tc_state_host_rp3_ct_rd(int port, enum signal sig);
+static unsigned int tc_state_host_rp3_ct_rd_entry(int port);
+static unsigned int tc_state_host_rp3_ct_rd_run(int port);
+
+static unsigned int tc_state_host_rp3_ct_rpu(int port, enum signal sig);
+static unsigned int tc_state_host_rp3_ct_rpu_entry(int port);
+static unsigned int tc_state_host_rp3_ct_rpu_run(int port);
+
+static unsigned int tc_state_host_rpu_ct_rd(int port, enum signal sig);
+static unsigned int tc_state_host_rpu_ct_rd_entry(int port);
+static unsigned int tc_state_host_rpu_ct_rd_run(int port);
+
+static unsigned int do_nothing_exit(int port);
+static unsigned int get_super_state(int port);
+
+
+static const state_sig tc_state_disabled_sig[] = {
+ tc_state_disabled_entry,
+ tc_state_disabled_run,
+ tc_state_disabled_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_error_recovery_sig[] = {
+ tc_state_error_recovery_entry,
+ tc_state_error_recovery_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_unattached_snk_sig[] = {
+ tc_state_unattached_snk_entry,
+ tc_state_unattached_snk_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_attach_wait_snk_sig[] = {
+ tc_state_attach_wait_snk_entry,
+ tc_state_attach_wait_snk_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_attached_snk_sig[] = {
+ tc_state_attached_snk_entry,
+ tc_state_attached_snk_run,
+ tc_state_attached_snk_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_try_snk_sig[] = {
+ tc_state_try_snk_entry,
+ tc_state_try_snk_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_unattached_src_sig[] = {
+ tc_state_unattached_src_entry,
+ tc_state_unattached_src_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_attach_wait_src_sig[] = {
+ tc_state_attach_wait_src_entry,
+ tc_state_attach_wait_src_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_try_wait_src_sig[] = {
+ tc_state_try_wait_src_entry,
+ tc_state_try_wait_src_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_attached_src_sig[] = {
+ tc_state_attached_src_entry,
+ tc_state_attached_src_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_try_snk_sig[] = {
+ tc_state_ct_try_snk_entry,
+ tc_state_ct_try_snk_run,
+ tc_state_ct_try_snk_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_attach_wait_unsupported_sig[] = {
+ tc_state_ct_attach_wait_unsupported_entry,
+ tc_state_ct_attach_wait_unsupported_run,
+ tc_state_ct_attach_wait_unsupported_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_attached_unsupported_sig[] = {
+ tc_state_ct_attached_unsupported_entry,
+ tc_state_ct_attached_unsupported_run,
+ tc_state_ct_attached_unsupported_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_unattached_unsupported_sig[] = {
+ tc_state_ct_unattached_unsupported_entry,
+ tc_state_ct_unattached_unsupported_run,
+ tc_state_ct_unattached_unsupported_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_unattached_vpd_sig[] = {
+ tc_state_ct_unattached_vpd_entry,
+ tc_state_ct_unattached_vpd_run,
+ tc_state_ct_unattached_vpd_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_disabled_vpd_sig[] = {
+ tc_state_ct_disabled_vpd_entry,
+ tc_state_ct_disabled_vpd_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_attached_vpd_sig[] = {
+ tc_state_ct_attached_vpd_entry,
+ tc_state_ct_attached_vpd_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_attach_wait_vpd_sig[] = {
+ tc_state_ct_attach_wait_vpd_entry,
+ tc_state_ct_attach_wait_vpd_run,
+ tc_state_ct_attach_wait_vpd_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_rard_ct_rd_sig[] = {
+ tc_state_host_rard_ct_rd_entry,
+ tc_state_host_rard_ct_rd_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_open_ct_open_sig[] = {
+ tc_state_host_open_ct_open_entry,
+ tc_state_host_open_ct_open_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_vbus_cc_iso_sig[] = {
+ tc_state_vbus_cc_iso_entry,
+ tc_state_vbus_cc_iso_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_rp3_ct_rd_sig[] = {
+ tc_state_host_rp3_ct_rd_entry,
+ tc_state_host_rp3_ct_rd_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_rp3_ct_rpu_sig[] = {
+ tc_state_host_rp3_ct_rpu_entry,
+ tc_state_host_rp3_ct_rpu_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_rpu_ct_rd_sig[] = {
+ tc_state_host_rpu_ct_rd_entry,
+ tc_state_host_rpu_ct_rd_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+void tc_reset_support_timer(int port)
+{
+ tc[port].support_timer_reset |= SUPPORT_TIMER_RESET_REQUEST;
+}
+
+static void tc_state_init(int port)
+{
+ int res = 0;
+ sm_state this_state;
+
+ res = tc_restart_tcpc(port);
+
+ CPRINTS("TCPC p%d init %s", port, res ? "failed" : "ready");
+ this_state = res ? tc_state_disabled : PD_DEFAULT_STATE(port);
+
+ init_state(port, TC_OBJ(port), this_state);
+
+ /* Disable pd state machines */
+ tc[port].pd_enable = 0;
+ tc[port].evt_timeout = 10*MSEC;
+ tc[port].power_role = PD_PLUG_CABLE_VPD;
+ tc[port].data_role = 0; /* Reserved for VPD */
+ tc[port].billboard_presented = 0;
+ tc[port].flags = 0;
+}
+
+static void tc_event_check(int port, int evt)
+{
+ /* Do Nothing */
+}
+
+/**
+ * Disabled
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Remove the terminations from Host
+ * Remove the terminations from Charge-Through
+ */
+static unsigned int tc_state_disabled(int port, enum signal sig)
+{
+ int ret = 0;
+
+ ret = (*tc_state_disabled_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_open_ct_open);
+}
+
+static unsigned int tc_state_disabled_entry(int port)
+{
+ tc[port].state_id = DISABLED;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+ return 0;
+}
+
+static unsigned int tc_state_disabled_run(int port)
+{
+ task_wait_event(-1);
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_disabled_exit(int port)
+{
+#ifndef CONFIG_USB_PD_TCPC
+ if (tc_restart_tcpc(port) != 0) {
+ CPRINTS("TCPC p%d restart failed!", port);
+ return 0;
+ }
+#endif
+ CPRINTS("TCPC p%d resumed!", port);
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+
+ return 0;
+}
+
+/**
+ * ErrorRecovery
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Remove the terminations from Host
+ * Remove the terminations from Charge-Through
+ */
+static unsigned int tc_state_error_recovery(int port, enum signal sig)
+{
+ int ret = 0;
+
+ ret = (*tc_state_error_recovery_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_open_ct_open);
+}
+
+static unsigned int tc_state_error_recovery_entry(int port)
+{
+ tc[port].state_id = ERROR_RECOVERY;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+ /* Use cc_debounce state variable for error recovery timeout */
+ tc[port].cc_debounce = get_time().val + PD_T_ERROR_RECOVERY;
+ return 0;
+}
+
+static unsigned int tc_state_error_recovery_run(int port)
+{
+ if (get_time().val > tc[port].cc_debounce) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/**
+ * Unattached.SNK
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place Ra on VCONN and Rd on Host CC
+ * Place Rd on Charge-Through CCs
+ */
+static unsigned int tc_state_unattached_snk(int port, enum signal sig)
+{
+ int ret = 0;
+
+ ret = (*tc_state_unattached_snk_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rard_ct_rd);
+}
+
+static unsigned int tc_state_unattached_snk_entry(int port)
+{
+ tc[port].state_id = UNATTACHED_SNK;
+ if (tc[port].obj.last_state != tc_state_unattached_src)
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ tc[port].flags &= ~TC_FLAGS_VCONN_ON;
+ tc[port].cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_unattached_snk_run(int port)
+{
+ int host_cc;
+ int new_cc_state;
+ int cc1;
+ int cc2;
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ /*
+ * Transition to AttachWait.SNK when a Source connection is
+ * detected, as indicated by the SNK.Rp state on its Host-side
+ * port’s CC pin.
+ */
+ if (cc_is_rp(host_cc)) {
+ set_state(port, TC_OBJ(port), tc_state_attach_wait_snk);
+ return 0;
+ }
+
+ /* Check Charge-Through CCs for connection */
+ vpd_ct_get_cc(&cc1, &cc2);
+
+ if (cc_is_rp(cc1) != cc_is_rp(cc2))
+ new_cc_state = PD_CC_DFP_ATTACHED;
+ else
+ new_cc_state = PD_CC_NONE;
+
+ /* Debounce Charge-Through CC state */
+ if (tc[port].cc_state != new_cc_state) {
+ tc[port].cc_state = new_cc_state;
+ tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE;
+ }
+
+ /* If we are here, Host CC must be open */
+
+ /* Wait for Charge-Through CC debounce */
+ if (get_time().val < tc[port].cc_debounce)
+ return 0;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * Unattached.SRC when the state of the Host-side port’s CC pin is
+ * SNK.Open for tDRP − dcSRC.DRP ∙ tDRP and both of the following
+ * is detected on the Charge-Through port.
+ * 1) SNK.Rp state is detected on exactly one of the CC1 or CC2
+ * pins for at least tCCDebounce
+ * 2) VBUS is detected
+ */
+ if (vpd_is_ct_vbus_present() &&
+ tc[port].cc_state == PD_CC_DFP_ATTACHED) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_src);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/**
+ * AttachWait.SNK
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place Ra on VCONN and Rd on Host CC
+ * Place Rd on Charge-Through CCs
+ */
+static unsigned int tc_state_attach_wait_snk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_attach_wait_snk_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rard_ct_rd);
+}
+
+static unsigned int tc_state_attach_wait_snk_entry(int port)
+{
+ tc[port].state_id = ATTACH_WAIT_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+ tc[port].host_cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_attach_wait_snk_run(int port)
+{
+ int host_new_cc_state;
+ int host_cc;
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ if (cc_is_rp(host_cc))
+ host_new_cc_state = PD_CC_DFP_ATTACHED;
+ else
+ host_new_cc_state = PD_CC_NONE;
+
+ /* Debounce the Host CC state */
+ if (tc[port].host_cc_state != host_new_cc_state) {
+ tc[port].host_cc_state = host_new_cc_state;
+ if (host_new_cc_state == PD_CC_DFP_ATTACHED)
+ tc[port].host_cc_debounce = get_time().val +
+ PD_T_CC_DEBOUNCE;
+ else
+ tc[port].host_cc_debounce = get_time().val +
+ PD_T_PD_DEBOUNCE;
+ return 0;
+ }
+
+ /* Wait for Host CC debounce */
+ if (get_time().val < tc[port].host_cc_debounce)
+ return 0;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * Attached.SNK after the state of the Host-side port’s CC pin is
+ * SNK.Rp for at least tCCDebounce and either host-side VCONN or
+ * VBUS is detected.
+ *
+ * Transition to Unattached.SNK when the state of both the CC1 and
+ * CC2 pins is SNK.Open for at least tPDDebounce.
+ */
+ if (tc[port].host_cc_state == PD_CC_DFP_ATTACHED &&
+ (vpd_is_vconn_present() || vpd_is_host_vbus_present()))
+ set_state(port, TC_OBJ(port), tc_state_attached_snk);
+ else if (tc[port].host_cc_state == PD_CC_NONE)
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+
+ return 0;
+}
+
+/**
+ * Attached.SNK
+ */
+static unsigned int tc_state_attached_snk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_attached_snk_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tc_state_attached_snk_entry(int port)
+{
+ tc[port].state_id = ATTACHED_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ /*
+ * This state can only be entered from states AttachWait.SNK
+ * and Try.SNK. So the Host port is isolated from the
+ * Charge-Through port. We only need to High-Z the
+ * Charge-Through ports CC1 and CC2 pins.
+ */
+ vpd_ct_set_pull(TYPEC_CC_OPEN, 0);
+
+ tc[port].host_cc_state = PD_CC_UNSET;
+
+ /* Start Charge-Through support timer */
+ tc[port].support_timer_reset = SUPPORT_TIMER_RESET_INIT;
+ tc[port].support_timer = get_time().val + PD_T_AME;
+
+ /* Sample host CC every 2ms */
+ tc_set_timeout(port, 2*MSEC);
+
+ return 0;
+}
+
+static unsigned int tc_state_attached_snk_run(int port)
+{
+ int host_new_cc_state;
+ int host_cc;
+
+ /* Has host vbus and vconn been removed */
+ if (!vpd_is_host_vbus_present() && !vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ /*
+ * Reset the Charge-Through Support Timer when it first
+ * receives any USB PD Structured VDM Command it supports,
+ * which is the Discover Identity command. And this is only
+ * done one time.
+ */
+ if (tc[port].support_timer_reset == SUPPORT_TIMER_RESET_REQUEST) {
+ tc[port].support_timer_reset |= SUPPORT_TIMER_RESET_COMPLETE;
+ tc[port].support_timer = get_time().val + PD_T_AME;
+ }
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ if (cc_is_rp(host_cc))
+ host_new_cc_state = PD_CC_DFP_ATTACHED;
+ else
+ host_new_cc_state = PD_CC_NONE;
+
+ /* Debounce the Host CC state */
+ if (tc[port].host_cc_state != host_new_cc_state) {
+ tc[port].host_cc_state = host_new_cc_state;
+ tc[port].host_cc_debounce = get_time().val + PD_T_VPDCTDD;
+ return 0;
+ }
+
+ /* Wait for Host CC debounce */
+ if (get_time().val < tc[port].host_cc_debounce)
+ return 0;
+
+ if (vpd_is_vconn_present()) {
+ if (!(tc[port].flags & TC_FLAGS_VCONN_ON)) {
+ /* VCONN detected. Remove RA */
+ vpd_host_set_pull(TYPEC_CC_RD, 0);
+ tc[port].flags |= TC_FLAGS_VCONN_ON;
+ }
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition
+ * to CTUnattached.VPD if VCONN is present and the state of
+ * its Host-side port’s CC pin is SNK.Open for tVPDCTDD.
+ */
+ if (tc[port].host_cc_state == PD_CC_NONE) {
+ set_state(port, TC_OBJ(port),
+ tc_state_ct_unattached_vpd);
+ return 0;
+ }
+ }
+
+ /* Check the Support Timer */
+ if (get_time().val > tc[port].support_timer &&
+ !tc[port].billboard_presented) {
+ /*
+ * Present USB Billboard Device Class interface
+ * indicating that Charge-Through is not supported
+ */
+ tc[port].billboard_presented = 1;
+ vpd_present_billboard(BB_SNK);
+ }
+
+ return 0;
+}
+
+static unsigned int tc_state_attached_snk_exit(int port)
+{
+ /* Reset timeout value to 10ms */
+ tc_set_timeout(port, 10*MSEC);
+ tc[port].billboard_presented = 0;
+ vpd_present_billboard(BB_NONE);
+
+ return 0;
+}
+
+/**
+ * Super State HOST_RA_CT_RD
+ */
+static unsigned int tc_state_host_rard_ct_rd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_rard_ct_rd_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_rard_ct_rd_entry(int port)
+{
+ /* Place Ra on VCONN and Rd on Host CC */
+ vpd_host_set_pull(TYPEC_CC_RA_RD, 0);
+
+ /* Place Rd on Charge-Through CCs */
+ vpd_ct_set_pull(TYPEC_CC_RD, 0);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_rard_ct_rd_run(int port)
+{
+ return RUN_SUPER;
+}
+
+/**
+ * Super State HOST_OPEN_CT_OPEN
+ */
+static unsigned int tc_state_host_open_ct_open(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_open_ct_open_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_open_ct_open_entry(int port)
+{
+ /* Remove the terminations from Host */
+ vpd_host_set_pull(TYPEC_CC_OPEN, 0);
+
+ /* Remove the terminations from Charge-Through */
+ vpd_ct_set_pull(TYPEC_CC_OPEN, 0);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_open_ct_open_run(int port)
+{
+ return RUN_SUPER;
+}
+
+/**
+ * Super State VBUS_CC_ISO
+ */
+static unsigned int tc_state_vbus_cc_iso(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_vbus_cc_iso_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tc_state_vbus_cc_iso_entry(int port)
+{
+ /* Isolate the Host-side port from the Charge-Through port */
+ vpd_vbus_pass_en(0);
+
+ /* Remove Charge-Through side port CCs */
+ vpd_ct_cc_sel(CT_OPEN);
+
+ /* Enable mcu communication and cc */
+ vpd_mcu_cc_en(1);
+
+ return 0;
+}
+
+static unsigned int tc_state_vbus_cc_iso_run(int port)
+{
+ return 0;
+}
+
+/**
+ * Unattached.SRC
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place RpUSB on Host CC
+ * Place Rd on Charge-Through CCs
+ */
+static unsigned int tc_state_unattached_src(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_unattached_src_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rpu_ct_rd);
+}
+
+static unsigned int tc_state_unattached_src_entry(int port)
+{
+ tc[port].state_id = UNATTACHED_SRC;
+ if (tc[port].obj.last_state != tc_state_unattached_snk)
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Get power from VBUS */
+ vpd_vconn_pwr_sel_odl(PWR_VBUS);
+
+ /* Make sure it's the Charge-Through Port's VBUS */
+ if (!vpd_is_ct_vbus_present()) {
+ set_state(port, TC_OBJ(port), tc_state_error_recovery);
+ return 0;
+ }
+
+ tc[port].next_role_swap = get_time().val + PD_T_DRP_SRC;
+
+ return 0;
+}
+
+static unsigned int tc_state_unattached_src_run(int port)
+{
+ int host_cc;
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ /*
+ * Transition to AttachWait.SRC when host-side VBUS is
+ * vSafe0V and SRC.Rd state is detected on the Host-side
+ * port’s CC pin.
+ */
+ if (!vpd_is_host_vbus_present() && host_cc == TYPEC_CC_VOLT_RD) {
+ set_state(port, TC_OBJ(port), tc_state_attach_wait_src);
+ return 0;
+ }
+
+ /*
+ * Transition to Unattached.SNK within tDRPTransition or
+ * if Charge-Through VBUS is removed.
+ */
+ if (!vpd_is_ct_vbus_present() ||
+ get_time().val > tc[port].next_role_swap) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/**
+ * AttachWait.SRC
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place RpUSB on Host CC
+ * Place Rd on Charge-Through CCs
+ */
+static unsigned int tc_state_attach_wait_src(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_attach_wait_src_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rpu_ct_rd);
+}
+
+static unsigned int tc_state_attach_wait_src_entry(int port)
+{
+ tc[port].state_id = ATTACH_WAIT_SRC;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ tc[port].host_cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_attach_wait_src_run(int port)
+{
+ int host_new_cc_state;
+ int host_cc;
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ if (host_cc == TYPEC_CC_VOLT_RD)
+ host_new_cc_state = PD_CC_UFP_ATTACHED;
+ else
+ host_new_cc_state = PD_CC_NONE;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition
+ * to Unattached.SNK when the SRC.Open state is detected on the
+ * Host-side port’s CC or if Charge-Through VBUS falls below
+ * vSinkDisconnect. The Charge-Through VCONN-Powered USB Device
+ * shall detect the SRC.Open state within tSRCDisconnect, but
+ * should detect it as quickly as possible.
+ */
+ if (host_new_cc_state == PD_CC_NONE || !vpd_is_ct_vbus_present()) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ /* Debounce the Host CC state */
+ if (tc[port].host_cc_state != host_new_cc_state) {
+ tc[port].host_cc_state = host_new_cc_state;
+ tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE;
+ return 0;
+ }
+
+ /* Wait for Host CC debounce */
+ if (get_time().val < tc[port].cc_debounce)
+ return 0;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * Try.SNK when the host-side VBUS is at vSafe0V and the SRC.Rd
+ * state is on the Host-side port’s CC pin for at least tCCDebounce.
+ */
+ if (tc[port].host_cc_state == PD_CC_UFP_ATTACHED &&
+ !vpd_is_host_vbus_present()) {
+ set_state(port, TC_OBJ(port), tc_state_try_snk);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/**
+ * Attached.SRC
+ */
+static unsigned int tc_state_attached_src(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_attached_src_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tc_state_attached_src_entry(int port)
+{
+ tc[port].state_id = ATTACHED_SRC;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ /* Connect Charge-Through VBUS to Host VBUS */
+ vpd_vbus_pass_en(1);
+
+ /*
+ * Get power from VBUS. No need to test because
+ * the Host VBUS is connected to the Charge-Through
+ * VBUS
+ */
+ vpd_vconn_pwr_sel_odl(PWR_VBUS);
+
+ return 0;
+}
+
+static unsigned int tc_state_attached_src_run(int port)
+{
+ int host_cc;
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * Unattached.SNK when VBUS falls below vSinkDisconnect or the
+ * Host-side port’s CC pin is SRC.Open. The Charge-Through
+ * VCONNPowered USB Device shall detect the SRC.Open state within
+ * tSRCDisconnect, but should detect it as quickly as possible.
+ */
+ if (!vpd_is_ct_vbus_present() || host_cc == TYPEC_CC_VOLT_OPEN)
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+
+ return 0;
+}
+
+/**
+ * Super State HOST_RPU_CT_RD
+ */
+static unsigned int tc_state_host_rpu_ct_rd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_rpu_ct_rd_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_rpu_ct_rd_entry(int port)
+{
+ /* Place RpUSB on Host CC */
+ vpd_host_set_pull(TYPEC_CC_RP, TYPEC_RP_USB);
+
+ /* Place Rd on Charge-Through CCs */
+ vpd_ct_set_pull(TYPEC_CC_RD, 0);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_rpu_ct_rd_run(int port)
+{
+ return RUN_SUPER;
+}
+
+/**
+ * Try.SNK
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place Ra on VCONN and Rd on Host CC
+ * Place Rd on Charge-Through CCs
+ */
+static unsigned int tc_state_try_snk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_try_snk_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rard_ct_rd);
+}
+
+static unsigned int tc_state_try_snk_entry(int port)
+{
+ tc[port].state_id = TRY_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Get power from VBUS */
+ vpd_vconn_pwr_sel_odl(PWR_VBUS);
+
+ /* Make sure it's the Charge-Through Port's VBUS */
+ if (!vpd_is_ct_vbus_present()) {
+ set_state(port, TC_OBJ(port), tc_state_error_recovery);
+ return 0;
+ }
+
+ tc[port].host_cc_state = PD_CC_UNSET;
+
+ /* Using next_role_swap timer as try_src timer */
+ tc[port].next_role_swap = get_time().val + PD_T_DRP_TRY;
+
+ return 0;
+}
+
+static unsigned int tc_state_try_snk_run(int port)
+{
+ int host_new_cc_state;
+ int host_cc;
+
+ /*
+ * Wait for tDRPTry before monitoring the Charge-Through
+ * port’s CC pins for the SNK.Rp
+ */
+ if (get_time().val < tc[port].next_role_swap)
+ return 0;
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ if (cc_is_rp(host_cc))
+ host_new_cc_state = PD_CC_DFP_ATTACHED;
+ else
+ host_new_cc_state = PD_CC_NONE;
+
+ /* Debounce the Host CC state */
+ if (tc[port].host_cc_state != host_new_cc_state) {
+ tc[port].host_cc_state = host_new_cc_state;
+ tc[port].cc_debounce = get_time().val + PD_T_DEBOUNCE;
+ return 0;
+ }
+
+ /* Wait for Host CC debounce */
+ if (get_time().val < tc[port].cc_debounce)
+ return 0;
+
+ /*
+ * The Charge-Through VCONN-Powered USB Device shall then transition to
+ * Attached.SNK when the SNK.Rp state is detected on the Host-side
+ * port’s CC pin for at least tTryCCDebounce and VBUS or VCONN is
+ * detected on Host-side port.
+ *
+ * Alternatively, the Charge-Through VCONN-Powered USB Device shall
+ * transition to TryWait.SRC if Host-side SNK.Rp state is not detected
+ * for tTryCCDebounce.
+ */
+ if (tc[port].host_cc_state == PD_CC_DFP_ATTACHED &&
+ (vpd_is_host_vbus_present() || vpd_is_vconn_present()))
+ set_state(port, TC_OBJ(port), tc_state_attached_snk);
+ else if (tc[port].host_cc_state == PD_CC_NONE)
+ set_state(port, TC_OBJ(port), tc_state_try_wait_src);
+
+ return 0;
+}
+
+/**
+ * TryWait.SRC
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place RpUSB on Host CC
+ * Place Rd on Charge-Through CCs
+ */
+static unsigned int tc_state_try_wait_src(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_try_wait_src_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rpu_ct_rd);
+}
+
+static unsigned int tc_state_try_wait_src_entry(int port)
+{
+ tc[port].state_id = TRY_WAIT_SRC;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ tc[port].host_cc_state = PD_CC_UNSET;
+ tc[port].next_role_swap = get_time().val + PD_T_DRP_TRY;
+
+ return 0;
+}
+
+static unsigned int tc_state_try_wait_src_run(int port)
+{
+ int host_new_cc_state;
+ int host_cc;
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ if (host_cc == TYPEC_CC_VOLT_RD)
+ host_new_cc_state = PD_CC_UFP_ATTACHED;
+ else
+ host_new_cc_state = PD_CC_NONE;
+
+ /* Debounce the Host CC state */
+ if (tc[port].host_cc_state != host_new_cc_state) {
+ tc[port].host_cc_state = host_new_cc_state;
+ tc[port].host_cc_debounce =
+ get_time().val + PD_T_TRY_CC_DEBOUNCE;
+ return 0;
+ }
+
+ if (get_time().val > tc[port].host_cc_debounce) {
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition
+ * to Attached.SRC when host-side VBUS is at vSafe0V and the
+ * SRC.Rd state is detected on the Host-side port’s CC pin for
+ * at least tTryCCDebounce.
+ */
+ if (tc[port].host_cc_state == PD_CC_UFP_ATTACHED &&
+ !vpd_is_host_vbus_present()) {
+ set_state(port, TC_OBJ(port), tc_state_attached_src);
+ return 0;
+ }
+ }
+
+ if (get_time().val > tc[port].next_role_swap) {
+ /*
+ * The Charge-Through VCONN-Powered USB Device shall transition
+ * to Unattached.SNK after tDRPTry if the Host-side port’s CC
+ * pin is not in the SRC.Rd state.
+ */
+ if (tc[port].host_cc_state == PD_CC_NONE) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+ }
+
+ return RUN_SUPER;
+}
+
+/**
+ * CTTry.SNK
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place RP3A0 on Host CC
+ * Connect Charge-Through Rd
+ * Get power from VCONN
+ */
+static unsigned int tc_state_ct_try_snk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_try_snk_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rp3_ct_rd);
+}
+
+static unsigned int tc_state_ct_try_snk_entry(int port)
+{
+ tc[port].state_id = CTTRY_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ tc[port].cc_state = PD_CC_UNSET;
+ tc[port].next_role_swap = get_time().val + PD_T_DRP_TRY;
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_try_snk_run(int port)
+{
+ int new_cc_state;
+ int cc1;
+ int cc2;
+
+ /*
+ * Wait for tDRPTry before monitoring the Charge-Through
+ * port’s CC pins for the SNK.Rp
+ */
+ if (get_time().val < tc[port].next_role_swap)
+ return 0;
+
+ /* Check CT CC for connection */
+ vpd_ct_get_cc(&cc1, &cc2);
+
+ if (cc_is_rp(cc1) || cc_is_rp(cc2))
+ new_cc_state = PD_CC_DFP_ATTACHED;
+ else
+ new_cc_state = PD_CC_NONE;
+
+ /*
+ * The Charge-Through VCONN-Powered USB Device shall transition
+ * to Unattached.SNK if VCONN falls below vVCONNDisconnect.
+ */
+ if (!vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ /* Debounce the CT CC state */
+ if (tc[port].cc_state != new_cc_state) {
+ tc[port].cc_state = new_cc_state;
+ tc[port].cc_debounce = get_time().val + PD_T_DEBOUNCE;
+ tc[port].try_wait_debounce = get_time().val + PD_T_TRY_WAIT;
+
+ return 0;
+ }
+
+ if (get_time().val > tc[port].cc_debounce) {
+ /*
+ * The Charge-Through VCONN-Powered USB Device shall then
+ * transition to CTAttached.VPD when the SNK.Rp state is
+ * detected on the Charge-Through port’s CC pins for at
+ * least tTryCCDebounce and VBUS is detected on
+ * Charge-Through port.
+ */
+ if (tc[port].cc_state == PD_CC_DFP_ATTACHED &&
+ vpd_is_ct_vbus_present()) {
+ set_state(port, TC_OBJ(port), tc_state_ct_attached_vpd);
+ return 0;
+ }
+ }
+
+ if (get_time().val > tc[port].try_wait_debounce) {
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition
+ * to CTAttached.Unsupported if SNK.Rp state is not detected
+ * for tDRPTryWait.
+ */
+ if (tc[port].cc_state == PD_CC_NONE) {
+ set_state(port, TC_OBJ(port),
+ tc_state_ct_attached_unsupported);
+ return 0;
+ }
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_ct_try_snk_exit(int port)
+{
+ /* Disable PD */
+ tc[port].pd_enable = 0;
+
+ return 0;
+}
+
+/**
+ * CTAttachWait.Unsupported
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place RP3A0 on Host CC
+ * Place RPUSB on Charge-Through CC
+ * Get power from VCONN
+ */
+static unsigned int
+ tc_state_ct_attach_wait_unsupported(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_attach_wait_unsupported_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rp3_ct_rpu);
+}
+
+static unsigned int tc_state_ct_attach_wait_unsupported_entry(int port)
+{
+ tc[port].state_id = CTATTACH_WAIT_UNSUPPORTED;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ tc[port].cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_attach_wait_unsupported_run(int port)
+{
+ int new_cc_state;
+ int cc1;
+ int cc2;
+
+ /* Check CT CC for connection */
+ vpd_ct_get_cc(&cc1, &cc2);
+
+ if (cc1 == TYPEC_CC_VOLT_RD || cc2 == TYPEC_CC_VOLT_RD)
+ new_cc_state = PD_CC_DFP_ATTACHED;
+ else if (cc1 == TYPEC_CC_VOLT_RA && cc2 == TYPEC_CC_VOLT_RA)
+ new_cc_state = PD_CC_AUDIO_ACC;
+ else /* (cc1 == TYPEC_CC_VOLT_OPEN or cc2 == TYPEC_CC_VOLT_OPEN */
+ new_cc_state = PD_CC_NONE;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * Unattached.SNK if VCONN falls below vVCONNDisconnect.
+ */
+ if (!vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ /* Debounce the cc state */
+ if (tc[port].cc_state != new_cc_state) {
+ tc[port].cc_state = new_cc_state;
+ tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE;
+ return 0;
+ }
+
+ /* Wait for CC debounce */
+ if (get_time().val < tc[port].cc_debounce)
+ return 0;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTUnattached.VPD when the state of either the Charge-Through
+ * Port’s CC1 or CC2 pin is SRC.Open for at least tCCDebounce.
+ *
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTTry.SNK if the state of at least one of the Charge-Through
+ * port’s CC pins is SRC.Rd, or if the state of both the CC1 and CC2
+ * pins is SRC.Ra. for at least tCCDebounce.
+ */
+ if (new_cc_state == PD_CC_NONE)
+ set_state(port, TC_OBJ(port), tc_state_ct_unattached_vpd);
+ else /* PD_CC_DFP_ATTACHED or PD_CC_AUDIO_ACC */
+ set_state(port, TC_OBJ(port), tc_state_ct_try_snk);
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_attach_wait_unsupported_exit(int port)
+{
+ /* Disable PD */
+ tc[port].pd_enable = 0;
+
+ return 0;
+}
+
+/**
+ * CTAttached.Unsupported
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place RP3A0 on Host CC
+ * Place RPUSB on Charge-Through CC
+ * Get power from VCONN
+ */
+static unsigned int tc_state_ct_attached_unsupported(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_attached_unsupported_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rp3_ct_rpu);
+}
+
+static unsigned int tc_state_ct_attached_unsupported_entry(int port)
+{
+ tc[port].state_id = CTATTACHED_UNSUPPORTED;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Present Billboard device */
+ vpd_present_billboard(BB_SNK);
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_attached_unsupported_run(int port)
+{
+ int cc1;
+ int cc2;
+
+ /* Check CT CC for connection */
+ vpd_ct_get_cc(&cc1, &cc2);
+
+ if (!vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ /*
+ * The Charge-Through VCONN-Powered USB Device shall transition to
+ * CTUnattached.VPD when SRC.Open state is detected on both the
+ * Charge-Through port’s CC pins or the SRC.Open state is detected
+ * on one CC pin and SRC.Ra is detected on the other CC pin.
+ */
+ if ((cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_OPEN) ||
+ (cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_RA) ||
+ (cc1 == TYPEC_CC_VOLT_RA && cc2 == TYPEC_CC_VOLT_OPEN)) {
+ set_state(port, TC_OBJ(port), tc_state_ct_unattached_vpd);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_ct_attached_unsupported_exit(int port)
+{
+ vpd_present_billboard(BB_NONE);
+
+ return 0;
+}
+
+/**
+ * CTUnattached.Unsupported
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place RP3A0 on Host CC
+ * Place RPUSB on Charge-Through CC
+ * Get power from VCONN
+ */
+static unsigned int
+ tc_state_ct_unattached_unsupported(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_unattached_unsupported_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rp3_ct_rpu);
+}
+
+static unsigned int tc_state_ct_unattached_unsupported_entry(int port)
+{
+ tc[port].state_id = CTUNATTACHED_UNSUPPORTED;
+ if (tc[port].obj.last_state != tc_state_ct_unattached_vpd)
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ tc[port].next_role_swap = get_time().val + PD_T_DRP_SRC;
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_unattached_unsupported_run(int port)
+{
+ int cc1;
+ int cc2;
+
+ /* Check CT CC for connection */
+ vpd_ct_get_cc(&cc1, &cc2);
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTAttachWait.Unsupported when a Sink connection is detected on
+ * the Charge-Through port, as indicated by the SRC.Rd state on at
+ * least one of the Charge-Through port’s CC pins or SRC.Ra state
+ * on both the CC1 and CC2 pins.
+ */
+ if ((cc1 == TYPEC_CC_VOLT_RD || cc2 == TYPEC_CC_VOLT_RD) ||
+ (cc1 == TYPEC_CC_VOLT_RA &&
+ cc2 == TYPEC_CC_VOLT_RA)) {
+ set_state(port, TC_OBJ(port),
+ tc_state_ct_attach_wait_unsupported);
+ return 0;
+ }
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * Unattached.SNK if VCONN falls below vVCONNDisconnect.
+ */
+ if (!vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTUnattached.VPD within tDRPTransition after dcSRC.DRP ∙ tDRP.
+ */
+ if (get_time().val > tc[port].next_role_swap) {
+ set_state(port, TC_OBJ(port), tc_state_ct_unattached_vpd);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_ct_unattached_unsupported_exit(int port)
+{
+ /* Disable PD */
+ tc[port].pd_enable = 0;
+
+ return 0;
+}
+
+/**
+ * CTUnattached.VPD
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place RP3A0 on Host CC
+ * Connect Charge-Through Rd
+ * Get power from VCONN
+ */
+static unsigned int tc_state_ct_unattached_vpd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_unattached_vpd_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rp3_ct_rd);
+}
+
+static unsigned int tc_state_ct_unattached_vpd_entry(int port)
+{
+ tc[port].state_id = CTUNATTACHED_VPD;
+ if (tc[port].obj.last_state != tc_state_ct_unattached_unsupported)
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ tc[port].cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_unattached_vpd_run(int port)
+{
+ int new_cc_state;
+ int cc1;
+ int cc2;
+
+ /* Check CT CC for connection */
+ vpd_ct_get_cc(&cc1, &cc2);
+
+ if (cc_is_rp(cc1) != cc_is_rp(cc2))
+ new_cc_state = PD_CC_DFP_ATTACHED;
+ else if (!cc_is_rp(cc1) && !cc_is_rp(cc2))
+ new_cc_state = PD_CC_NONE;
+ else
+ new_cc_state = PD_CC_UNSET;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTAttachWait.VPD when a Source connection is detected on the
+ * Charge-Through port, as indicated by the SNK.Rp state on
+ * exactly one of the Charge-Through port’s CC pins.
+ */
+ if (new_cc_state == PD_CC_DFP_ATTACHED) {
+ set_state(port, TC_OBJ(port),
+ tc_state_ct_attach_wait_vpd);
+ return 0;
+ }
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * Unattached.SNK if VCONN falls below vVCONNDisconnect.
+ */
+ if (!vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port),
+ tc_state_unattached_snk);
+ return 0;
+ }
+
+ /* Debounce the cc state */
+ if (new_cc_state != tc[port].cc_state) {
+ tc[port].cc_state = new_cc_state;
+ tc[port].cc_debounce = get_time().val + PD_T_DRP_SRC;
+ return 0;
+ }
+
+ if (get_time().val < tc[port].cc_debounce)
+ return 0;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTUnattached.Unsupported within tDRPTransition after the state
+ * of both the Charge-Through port’s CC1 and CC2 pins is SNK.Open
+ * for tDRP-dcSRC.DRP ∙ tDRP, or if directed.
+ */
+ if (tc[port].cc_state == PD_CC_NONE) {
+ set_state(port, TC_OBJ(port),
+ tc_state_ct_unattached_unsupported);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_ct_unattached_vpd_exit(int port)
+{
+ /* Disable PD */
+ tc[port].pd_enable = 0;
+
+ return 0;
+}
+
+/**
+ * CTDisabled.VPD
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Remove the terminations from Host
+ * Remove the terminations from Charge-Through
+ */
+static unsigned int tc_state_ct_disabled_vpd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_disabled_vpd_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_open_ct_open);
+}
+
+static unsigned int tc_state_ct_disabled_vpd_entry(int port)
+{
+ tc[port].state_id = CTDISABLED_VPD;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Get power from VBUS */
+ vpd_vconn_pwr_sel_odl(PWR_VBUS);
+
+ tc[port].next_role_swap = get_time().val + PD_T_VPDDISABLE;
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_disabled_vpd_run(int port)
+{
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition
+ * to Unattached.SNK after tVPDDisable.
+ */
+ if (get_time().val > tc[port].next_role_swap)
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+
+ return 0;
+}
+
+/**
+ * CTAttached.VPD
+ */
+static unsigned int tc_state_ct_attached_vpd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_attached_vpd_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tc_state_ct_attached_vpd_entry(int port)
+{
+ int cc1;
+ int cc2;
+
+ tc[port].state_id = CTATTACHED_VPD;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Get power from VCONN */
+ vpd_vconn_pwr_sel_odl(PWR_VCONN);
+
+ /*
+ * Detect which of the Charge-Through port’s CC1 or CC2
+ * pins is connected through the cable
+ */
+ vpd_ct_get_cc(&cc1, &cc2);
+ tc[port].ct_cc = cc_is_rp(cc2) ? CT_CC2 : CT_CC1;
+
+ /*
+ * 1. Remove or reduce any additional capacitance on the
+ * Host-side CC port
+ */
+ vpd_mcu_cc_en(0);
+
+ /*
+ * 2. Disable the Rp termination advertising 3.0 A on the
+ * host port’s CC pin
+ */
+ vpd_host_set_pull(TYPEC_CC_OPEN, 0);
+
+ /*
+ * 3. Passively multiplex the detected Charge-Through port’s
+ * CC pin through to the host port’s CC
+ */
+ vpd_ct_cc_sel(tc[port].ct_cc);
+
+ /*
+ * 4. Disable the Rd on the Charge-Through port’s CC1 and CC2
+ * pins
+ */
+ vpd_ct_set_pull(TYPEC_CC_OPEN, 0);
+
+ /*
+ * 5. Connect the Charge-Through port’s VBUS through to the
+ * host port’s VBUS
+ */
+ vpd_vbus_pass_en(1);
+
+ tc[port].cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_attached_vpd_run(int port)
+{
+ int new_cc_state;
+ int cc1;
+ int cc2;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTDisabled.VPD if VCONN falls below vVCONNDisconnect.
+ */
+ if (!vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port), tc_state_ct_disabled_vpd);
+ return 0;
+ }
+
+ /* Check CT CC for connection */
+ vpd_ct_get_cc(&cc1, &cc2);
+ if ((tc[port].ct_cc ? cc2 : cc1) == TYPEC_CC_VOLT_OPEN)
+ new_cc_state = PD_CC_NONE;
+ else
+ new_cc_state = PD_CC_DFP_ATTACHED;
+
+ /* Debounce the cc state */
+ if (new_cc_state != tc[port].cc_state) {
+ tc[port].cc_state = new_cc_state;
+ tc[port].cc_debounce = get_time().val + PD_T_VPDCTDD;
+ return 0;
+ }
+
+ if (get_time().val < tc[port].pd_debounce)
+ return 0;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTUnattached.VPD when VBUS falls below vSinkDisconnect and the
+ * state of the passed-through CC pin is SNK.Open for tVPDCTDD.
+ */
+ if (tc[port].cc_state == PD_CC_NONE && !vpd_is_ct_vbus_present())
+ set_state(port, TC_OBJ(port), tc_state_ct_unattached_vpd);
+
+ return 0;
+}
+
+/**
+ * CTAttachWait.VPD
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place RP3A0 on Host CC
+ * Connect Charge-Through Rd
+ * Get power from VCONN
+ */
+static unsigned int tc_state_ct_attach_wait_vpd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_attach_wait_vpd_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rp3_ct_rd);
+}
+
+static unsigned int tc_state_ct_attach_wait_vpd_entry(int port)
+{
+ tc[port].state_id = CTATTACH_WAIT_VPD;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ tc[port].cc_state = PD_CC_UNSET;
+
+ /* Sample CCs every 2ms */
+ tc_set_timeout(port, 2 * MSEC);
+ return 0;
+}
+
+static unsigned int tc_state_ct_attach_wait_vpd_run(int port)
+{
+ int new_cc_state;
+ int cc1;
+ int cc2;
+
+ /* Check CT CC for connection */
+ vpd_ct_get_cc(&cc1, &cc2);
+
+ if (cc_is_rp(cc1) != cc_is_rp(cc2))
+ new_cc_state = PD_CC_DFP_ATTACHED;
+ else if (!cc_is_rp(cc1) && !cc_is_rp(cc2))
+ new_cc_state = PD_CC_NONE;
+ else
+ new_cc_state = PD_CC_UNSET;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTDisabled.VPD if VCONN falls below vVCONNDisconnect.
+ */
+ if (!vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port), tc_state_ct_disabled_vpd);
+ return 0;
+ }
+
+ /* Debounce the cc state */
+ if (new_cc_state != tc[port].cc_state) {
+ tc[port].cc_state = new_cc_state;
+ tc[port].cc_debounce = get_time().val +
+ PD_T_CC_DEBOUNCE;
+ tc[port].pd_debounce = get_time().val +
+ PD_T_PD_DEBOUNCE;
+ return 0;
+ }
+
+ if (get_time().val > tc[port].pd_debounce) {
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition
+ * to CTUnattached.VPD when the state of both the Charge-Through
+ * port’s CC1 and CC2 pins are SNK.Open for at least
+ * tPDDebounce.
+ */
+ if (tc[port].cc_state == PD_CC_NONE) {
+ set_state(port, TC_OBJ(port),
+ tc_state_ct_unattached_vpd);
+ return 0;
+ }
+ }
+
+ if (get_time().val > tc[port].cc_debounce) {
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTAttached.VPD after the state of only one of the
+ * Charge-Through port’s CC1 or CC2 pins is SNK.Rp for at
+ * least tCCDebounce and VBUS on the Charge-Through port is
+ * detected.
+ */
+ if (tc[port].cc_state == PD_CC_DFP_ATTACHED &&
+ vpd_is_ct_vbus_present()) {
+ set_state(port, TC_OBJ(port), tc_state_ct_attached_vpd);
+ return 0;
+ }
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_ct_attach_wait_vpd_exit(int port)
+{
+ /* Disable PD */
+ tc[port].pd_enable = 0;
+
+ /* Reset timeout value to 10ms */
+ tc_set_timeout(port, 10 * MSEC);
+
+ return 0;
+}
+
+/**
+ * Super State HOST_RP3_CT_RD
+ */
+static unsigned int tc_state_host_rp3_ct_rd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_rp3_ct_rd_sig[sig])(port);
+
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_rp3_ct_rd_entry(int port)
+{
+ /* Place RP3A0 on Host CC */
+ vpd_host_set_pull(TYPEC_CC_RP, TYPEC_RP_3A0);
+
+ /* Connect Charge-Through Rd */
+ vpd_ct_set_pull(TYPEC_CC_RD, 0);
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall
+ * ensure that it is powered by VCONN
+ */
+
+ /* Make sure vconn is on */
+ if (!vpd_is_vconn_present())
+ set_state(port, TC_OBJ(port), tc_state_error_recovery);
+
+ /* Get power from VCONN */
+ vpd_vconn_pwr_sel_odl(PWR_VCONN);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_rp3_ct_rd_run(int port)
+{
+ return 0;
+}
+
+/**
+ * Super State HOST_RP3_CT_RPU
+ */
+static unsigned int tc_state_host_rp3_ct_rpu(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_rp3_ct_rpu_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_rp3_ct_rpu_entry(int port)
+{
+ /* Place RP3A0 on Host CC */
+ vpd_host_set_pull(TYPEC_CC_RP, TYPEC_RP_3A0);
+
+ /* Place RPUSB on Charge-Through CC */
+ vpd_ct_set_pull(TYPEC_CC_RP, TYPEC_RP_USB);
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall
+ * ensure that it is powered by VCONN
+ */
+
+ /* Make sure vconn is on */
+ if (!vpd_is_vconn_present())
+ set_state(port, TC_OBJ(port), tc_state_error_recovery);
+
+ /* Get power from VCONN */
+ vpd_vconn_pwr_sel_odl(PWR_VCONN);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_rp3_ct_rpu_run(int port)
+{
+ return 0;
+}
+
+static unsigned int do_nothing_exit(int port)
+{
+ return 0;
+}
+
+static unsigned int get_super_state(int port)
+{
+ return RUN_SUPER;
+}
+
+#endif /* CONFIG_USB_TYPEC_CTVPD */
diff --git a/include/usb_tc_sm.h b/include/usb_tc_sm.h
new file mode 100644
index 0000000000..08e9ebbf54
--- /dev/null
+++ b/include/usb_tc_sm.h
@@ -0,0 +1,91 @@
+/* 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.
+ */
+
+/* USB Type-C module */
+
+#ifndef __CROS_EC_USB_TC_H
+#define __CROS_EC_USB_TC_H
+
+enum typec_state_id {
+ DISABLED,
+ UNATTACHED_SNK,
+ ATTACH_WAIT_SNK,
+ ATTACHED_SNK,
+#if !defined(CONFIG_USB_TYPEC_VPD)
+ ERROR_RECOVERY,
+ UNATTACHED_SRC,
+ ATTACH_WAIT_SRC,
+ ATTACHED_SRC,
+#endif
+#if !defined(CONFIG_USB_TYPEC_CTVPD) && !defined(CONFIG_USB_TYPEC_VPD)
+ AUDIO_ACCESSORY,
+ ORIENTED_DEBUG_ACCESSORY_SRC,
+ UNORIENTED_DEBUG_ACCESSORY_SRC,
+ DEBUG_ACCESSORY_SNK,
+ TRY_SRC,
+ TRY_WAIT_SNK,
+ CTUNATTACHED_SNK,
+ CTATTACHED_SNK,
+#endif
+#if defined(CONFIG_USB_TYPEC_CTVPD)
+ CTTRY_SNK,
+ CTATTACHED_UNSUPPORTED,
+ CTATTACH_WAIT_UNSUPPORTED,
+ CTUNATTACHED_UNSUPPORTED,
+ CTUNATTACHED_VPD,
+ CTATTACH_WAIT_VPD,
+ CTATTACHED_VPD,
+ CTDISABLED_VPD,
+ TRY_SNK,
+ TRY_WAIT_SRC,
+#endif
+ /* Number of states. Not an actual state. */
+ TC_STATE_COUNT,
+};
+
+/**
+ * Get the id of the current Type-C state
+ *
+ * @param port USB-C port number
+ */
+enum typec_state_id get_typec_state_id(int port);
+
+/**
+ * Get current data role
+ *
+ * @param port USB-C port number
+ * @return 0 for ufp, 1 for dfp, 2 for disconnected
+ */
+int tc_get_data_role(int port);
+
+/**
+ * Get current power role
+ *
+ * @param port USB-C port number
+ * @return 0 for sink, 1 for source or vpd
+ */
+int tc_get_power_role(int port);
+
+/**
+ * Set loop timeout value
+ *
+ * @param port USB-C port number
+ * @timeout time in ms
+ */
+void tc_set_timeout(int port, uint64_t timeout);
+
+#ifdef CONFIG_USB_TYPEC_CTVPD
+/**
+ * Resets the charge-through support timer. This can be
+ * called many times but the support timer will only
+ * reset once, while in the Attached.SNK state.
+ *
+ * @param port USB-C port number
+ */
+void tc_reset_support_timer(int port);
+
+#endif /* CONFIG_USB_TYPEC_CTVPD */
+#endif /* __CROS_EC_USB_TC_H */
+
diff --git a/include/usb_tc_vpd_sm.h b/include/usb_tc_vpd_sm.h
new file mode 100644
index 0000000000..c8a4dd48a3
--- /dev/null
+++ b/include/usb_tc_vpd_sm.h
@@ -0,0 +1,486 @@
+/* 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 "vpd_api.h"
+
+/* USB Type-C VCONN Powered Device module */
+
+#ifndef __CROS_EC_USB_TC_VPD_H
+#define __CROS_EC_USB_TC_VPD_H
+
+/* Type-C Layer Flags */
+#define TC_FLAGS_VCONN_ON BIT(0)
+
+#undef PD_DEFAULT_STATE
+/* Port default state at startup */
+#define PD_DEFAULT_STATE(port) tc_state_unattached_snk
+
+/*
+ * TC_OBJ is a convenience macro to access struct sm_obj, which
+ * must be the first member of struct type_c.
+ */
+#define TC_OBJ(port) (SM_OBJ(tc[port]))
+
+/**
+ * This is the Type-C Port object that contains information needed to
+ * implement a VCONN Powered Device.
+ */
+static struct type_c {
+ /*
+ * struct sm_obj must be first. This is the state machine
+ * object that keeps track of the current and last state
+ * of the state machine.
+ */
+ struct sm_obj obj;
+ /* state id */
+ enum typec_state_id state_id;
+ /* current port power role (VPD, SOURCE or SINK) */
+ uint8_t power_role;
+ /* current port data role (DFP or UFP) */
+ uint8_t data_role;
+ /* bool: enable power delivery state machines */
+ uint8_t pd_enable;
+ /* event timeout */
+ uint64_t evt_timeout;
+ /* state machine event */
+ int evt;
+ /* port flags, see TC_FLAGS_* */
+ uint32_t flags;
+ /* Time a port shall wait before it can determine it is attached */
+ uint64_t cc_debounce;
+ /* VPD host port cc state */
+ enum pd_cc_states host_cc_state;
+ uint8_t ct_cc;
+} tc[CONFIG_USB_PD_PORT_COUNT];
+
+/* Type-C states */
+static unsigned int tc_state_disabled(int port, enum signal sig);
+static unsigned int tc_state_disabled_entry(int port);
+static unsigned int tc_state_disabled_run(int port);
+static unsigned int tc_state_disabled_exit(int port);
+
+static unsigned int tc_state_unattached_snk(int port, enum signal sig);
+static unsigned int tc_state_unattached_snk_entry(int port);
+static unsigned int tc_state_unattached_snk_run(int port);
+static unsigned int tc_state_unattached_snk_exit(int port);
+
+static unsigned int tc_state_attach_wait_snk(int port, enum signal sig);
+static unsigned int tc_state_attach_wait_snk_entry(int port);
+static unsigned int tc_state_attach_wait_snk_run(int port);
+static unsigned int tc_state_attach_wait_snk_exit(int port);
+
+static unsigned int tc_state_attached_snk(int port, enum signal sig);
+static unsigned int tc_state_attached_snk_entry(int port);
+static unsigned int tc_state_attached_snk_run(int port);
+static unsigned int tc_state_attached_snk_exit(int port);
+
+/* Super States */
+static unsigned int tc_state_host_rard(int port, enum signal sig);
+static unsigned int tc_state_host_rard_entry(int port);
+static unsigned int tc_state_host_rard_run(int port);
+static unsigned int tc_state_host_rard_exit(int port);
+
+static unsigned int tc_state_host_open(int port, enum signal sig);
+static unsigned int tc_state_host_open_entry(int port);
+static unsigned int tc_state_host_open_run(int port);
+static unsigned int tc_state_host_open_exit(int port);
+
+static unsigned int tc_state_vbus_cc_iso(int port, enum signal sig);
+static unsigned int tc_state_vbus_cc_iso_entry(int port);
+static unsigned int tc_state_vbus_cc_iso_run(int port);
+static unsigned int tc_state_vbus_cc_iso_exit(int port);
+
+static unsigned int get_super_state(int port);
+
+static const state_sig tc_state_disabled_sig[] = {
+ tc_state_disabled_entry,
+ tc_state_disabled_run,
+ tc_state_disabled_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_unattached_snk_sig[] = {
+ tc_state_unattached_snk_entry,
+ tc_state_unattached_snk_run,
+ tc_state_unattached_snk_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_attach_wait_snk_sig[] = {
+ tc_state_attach_wait_snk_entry,
+ tc_state_attach_wait_snk_run,
+ tc_state_attach_wait_snk_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_attached_snk_sig[] = {
+ tc_state_attached_snk_entry,
+ tc_state_attached_snk_run,
+ tc_state_attached_snk_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_rard_sig[] = {
+ tc_state_host_rard_entry,
+ tc_state_host_rard_run,
+ tc_state_host_rard_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_open_sig[] = {
+ tc_state_host_open_entry,
+ tc_state_host_open_run,
+ tc_state_host_open_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_vbus_cc_iso_sig[] = {
+ tc_state_vbus_cc_iso_entry,
+ tc_state_vbus_cc_iso_run,
+ tc_state_vbus_cc_iso_exit,
+ get_super_state
+};
+
+static void tc_state_init(int port)
+{
+ int res = 0;
+ sm_state this_state;
+
+ res = tc_restart_tcpc(port);
+
+ CPRINTS("TCPC p%d init %s", port, res ? "failed" : "ready");
+ this_state = res ? tc_state_disabled : PD_DEFAULT_STATE(port);
+
+ /* Disable TCPC RX until connection is established */
+ tcpm_set_rx_enable(port, 0);
+
+ init_state(port, TC_OBJ(port), this_state);
+
+ /* Disable pd state machines */
+ tc[port].pd_enable = 0;
+ tc[port].evt_timeout = 10*MSEC;
+ tc[port].power_role = PD_PLUG_CABLE_VPD;
+ tc[port].data_role = 0; /* Reserved for VPD */
+ tc[port].flags = 0;
+}
+
+static void tc_event_check(int port, int evt)
+{
+ /* Do Nothing */
+}
+
+/**
+ * Disabled
+ *
+ * Super State Entries:
+ * Enable mcu communication
+ * Remove the terminations from Host CC
+ */
+static unsigned int tc_state_disabled(int port, enum signal sig)
+{
+ int ret = 0;
+
+ ret = (*tc_state_disabled_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_open);
+}
+
+static unsigned int tc_state_disabled_entry(int port)
+{
+ tc[port].state_id = DISABLED;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ return 0;
+}
+
+static unsigned int tc_state_disabled_run(int port)
+{
+ task_wait_event(-1);
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_disabled_exit(int port)
+{
+#ifndef CONFIG_USB_PD_TCPC
+ if (tc_restart_tcpc(port) != 0) {
+ CPRINTS("TCPC p%d restart failed!", port);
+ return 0;
+ }
+#endif
+ CPRINTS("TCPC p%d resumed!", port);
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+
+ return 0;
+}
+
+/**
+ * Unattached.SNK
+ *
+ * Super State Entry:
+ * Enable mcu communication
+ * Place Ra on VCONN and Rd on Host CC
+ */
+static unsigned int tc_state_unattached_snk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_unattached_snk_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rard);
+}
+
+static unsigned int tc_state_unattached_snk_entry(int port)
+{
+ tc[port].state_id = UNATTACHED_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ return 0;
+}
+
+static unsigned int tc_state_unattached_snk_run(int port)
+{
+ int host_cc;
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ /*
+ * Transition to AttachWait.SNK when a Source connection is
+ * detected, as indicated by the SNK.Rp state on its Host-side
+ * port’s CC pin.
+ */
+ if (cc_is_rp(host_cc)) {
+ set_state(port, TC_OBJ(port), tc_state_attach_wait_snk);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_unattached_snk_exit(int port)
+{
+ return 0;
+}
+
+/**
+ * AttachedWait.SNK
+ *
+ * Super State Entry:
+ * Enable mcu communication
+ * Place Ra on VCONN and Rd on Host CC
+ */
+static unsigned int tc_state_attach_wait_snk(int port, enum signal sig)
+{
+ int ret = 0;
+
+ ret = (*tc_state_attach_wait_snk_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rard);
+}
+
+static unsigned int tc_state_attach_wait_snk_entry(int port)
+{
+ tc[port].state_id = ATTACH_WAIT_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+ tc[port].host_cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_attach_wait_snk_run(int port)
+{
+ int host_new_cc_state;
+ int host_cc;
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ if (cc_is_rp(host_cc))
+ host_new_cc_state = PD_CC_DFP_ATTACHED;
+ else
+ host_new_cc_state = PD_CC_NONE;
+
+ /* Debounce the Host CC state */
+ if (tc[port].host_cc_state != host_new_cc_state) {
+ tc[port].host_cc_state = host_new_cc_state;
+ if (host_new_cc_state == PD_CC_DFP_ATTACHED)
+ tc[port].cc_debounce = get_time().val +
+ PD_T_CC_DEBOUNCE;
+ else
+ tc[port].cc_debounce = get_time().val +
+ PD_T_PD_DEBOUNCE;
+
+ return 0;
+ }
+
+ /* Wait for Host CC debounce */
+ if (get_time().val < tc[port].cc_debounce)
+ return 0;
+
+ /*
+ * A VCONN-Powered USB Device shall transition to
+ * Attached.SNK after the state of the Host-side port’s CC pin is
+ * SNK.Rp for at least tCCDebounce and either host-side VCONN or
+ * VBUS is detected.
+ *
+ * Transition to Unattached.SNK when the state of both the CC1 and
+ * CC2 pins is SNK.Open for at least tPDDebounce.
+ */
+ if (tc[port].host_cc_state == PD_CC_DFP_ATTACHED &&
+ (vpd_is_vconn_present() || vpd_is_host_vbus_present()))
+ set_state(port, TC_OBJ(port), tc_state_attached_snk);
+ else if (tc[port].host_cc_state == PD_CC_NONE)
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+
+ return 0;
+}
+
+static unsigned int tc_state_attach_wait_snk_exit(int port)
+{
+ return 0;
+}
+
+/**
+ * Attached.SNK
+ */
+static unsigned int tc_state_attached_snk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_attached_snk_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tc_state_attached_snk_entry(int port)
+{
+ tc[port].state_id = ATTACHED_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ return 0;
+}
+
+static unsigned int tc_state_attached_snk_run(int port)
+{
+ /* Has host vbus and vconn been removed */
+ if (!vpd_is_host_vbus_present() && !vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ if (vpd_is_vconn_present()) {
+ if (!(tc[port].flags & TC_FLAGS_VCONN_ON)) {
+ /* VCONN detected. Remove RA */
+ vpd_host_set_pull(TYPEC_CC_RD, 0);
+ tc[port].flags |= TC_FLAGS_VCONN_ON;
+ }
+ }
+
+ return 0;
+}
+
+static unsigned int tc_state_attached_snk_exit(int port)
+{
+ /* Disable PD */
+ tc[port].pd_enable = 0;
+ tc[port].flags &= ~TC_FLAGS_VCONN_ON;
+
+ return 0;
+}
+
+/**
+ * Super State HOST_RARD
+ */
+static unsigned int tc_state_host_rard(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_rard_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_rard_entry(int port)
+{
+ /* Place Ra on VCONN and Rd on Host CC */
+ vpd_host_set_pull(TYPEC_CC_RA_RD, 0);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_rard_run(int port)
+{
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_host_rard_exit(int port)
+{
+ return 0;
+}
+
+/**
+ * Super State HOST_OPEN
+ */
+static unsigned int tc_state_host_open(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_open_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_open_entry(int port)
+{
+ /* Remove the terminations from Host CC */
+ vpd_host_set_pull(TYPEC_CC_OPEN, 0);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_open_run(int port)
+{
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_host_open_exit(int port)
+{
+ return 0;
+}
+
+/**
+ * Super State VBUS_CC_ISO
+ */
+static unsigned int tc_state_vbus_cc_iso(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_vbus_cc_iso_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tc_state_vbus_cc_iso_entry(int port)
+{
+ /* Enable mcu communication and cc */
+ vpd_mcu_cc_en(1);
+
+ return 0;
+}
+
+static unsigned int tc_state_vbus_cc_iso_run(int port)
+{
+ return 0;
+}
+
+static unsigned int tc_state_vbus_cc_iso_exit(int port)
+{
+ return 0;
+}
+
+static unsigned int get_super_state(int port)
+{
+ return RUN_SUPER;
+}
+
+#endif /*__CROS_EC_USB_TC_VPD_H */
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 */