diff options
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 */ |