diff options
author | Sam Hurst <shurst@google.com> | 2018-09-13 09:27:08 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2019-04-08 22:30:19 -0700 |
commit | 0fe6147a9d8d9feef5049aa6c6c4a6ad30d12b26 (patch) | |
tree | 05d4509bcfe68a248ec3fa58168f3de2536c2d9c /include | |
parent | e097feb8b2db20cd2435a483517356defa222db1 (diff) | |
download | chrome-ec-0fe6147a9d8d9feef5049aa6c6c4a6ad30d12b26.tar.gz |
chocodile_vpdmcu: Firmware for chocodile mcu
Implement Chocodile Charge-Through Vconn Powered firmware for mcu
using new Type-C/PD State machine stack.
BUG=b:115626873
BRANCH=none
TEST=manual
Charge-Through was tested on an Atlas running a DRP USB-C/PD state
machine with CTUnattached.SNK and CTAttached.SNK states.
Signed-off-by: Sam Hurst <shurst@chromium.org>
Change-Id: I847f1bcd2fc3ce41e66edd133a10c943d5e8c819
Reviewed-on: https://chromium-review.googlesource.com/1225250
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: Sam Hurst <shurst@google.com>
Reviewed-by: Stefan Reinauer <reinauer@google.com>
Diffstat (limited to 'include')
-rw-r--r-- | include/config.h | 33 | ||||
-rw-r--r-- | include/usb_emsg.h | 23 | ||||
-rw-r--r-- | include/usb_pd.h | 63 | ||||
-rw-r--r-- | include/usb_pd_tcpm.h | 5 | ||||
-rw-r--r-- | include/usb_pe_ctvpd_sm.h | 239 | ||||
-rw-r--r-- | include/usb_pe_sm.h | 79 | ||||
-rw-r--r-- | include/usb_prl_sm.h | 213 | ||||
-rw-r--r-- | include/usb_sm.h | 67 | ||||
-rw-r--r-- | include/usb_tc_ctvpd_sm.h | 2039 | ||||
-rw-r--r-- | include/usb_tc_sm.h | 91 | ||||
-rw-r--r-- | include/usb_tc_vpd_sm.h | 486 |
11 files changed, 3335 insertions, 3 deletions
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 */ |