diff options
author | Ayushee <ayushee.shah@intel.com> | 2020-06-16 17:19:17 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-06-30 21:38:05 +0000 |
commit | a057167851e63be53e2de757361dc2e303b737e2 (patch) | |
tree | 3736a076490f16ad15a06c18406686e4694879fb | |
parent | 39ada1fd2bdadb8a319e7134e650c65332f0bc2e (diff) | |
download | chrome-ec-a057167851e63be53e2de757361dc2e303b737e2.tar.gz |
TCPMv2: Add Entry flow for Thunderbolt mode
Create a separate module for representing Thunderbolt mode which decides
whether and how we can enter into Thunderbolt alternate mode.
Modify the Device Policy Manger (usb_pd_dpm.c) to first check if the
port partner and cable is a Thunderbolt Compatible. If not, continue the
rest of the PD flow undisturbed.
BUG=b:148528713
BRANCH=none
TEST=1. Attach Thunderbolt dock, DUT should enter Thunderbolt mode
2. Attach Type-C dock, DUT should enter DP mode
3. Attach DP dongle, DUT should enter DP mode
Change-Id: I369eb6337144676996c9d94a412eec060eacd273
Signed-off-by: Ayushee <ayushee.shah@intel.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2250488
Reviewed-by: Abe Levkoy <alevkoy@chromium.org>
-rw-r--r-- | common/usbc/build.mk | 1 | ||||
-rw-r--r-- | common/usbc/tbt_alt_mode.c | 241 | ||||
-rw-r--r-- | common/usbc/usb_pd_dpm.c | 40 | ||||
-rw-r--r-- | common/usbc/usb_pe_drp_sm.c | 5 | ||||
-rw-r--r-- | include/usb_tbt_alt_mode.h | 65 |
5 files changed, 344 insertions, 8 deletions
diff --git a/common/usbc/build.mk b/common/usbc/build.mk index b2e36cf81e..b440c69a21 100644 --- a/common/usbc/build.mk +++ b/common/usbc/build.mk @@ -30,6 +30,7 @@ all-obj-$(CONFIG_USB_CTVPD)+=$(_usbc_dir)usb_pe_ctvpd_sm.o all-obj-$(CONFIG_USB_DRP_ACC_TRYSRC)+=$(_usbc_dir)usb_pe_drp_sm.o all-obj-$(CONFIG_USB_DRP_ACC_TRYSRC)+=$(_usbc_dir)usb_pd_dpm.o all-obj-$(CONFIG_USB_DRP_ACC_TRYSRC)+=$(_usbc_dir)dp_alt_mode.o +all-obj-$(CONFIG_USB_PD_TBT_COMPAT_MODE)+=$(_usbc_dir)tbt_alt_mode.o all-obj-$(CONFIG_CMD_PD)+=$(_usbc_dir)usb_pd_console.o endif # CONFIG_USB_PE_SM diff --git a/common/usbc/tbt_alt_mode.c b/common/usbc/tbt_alt_mode.c new file mode 100644 index 0000000000..c3d4953c7b --- /dev/null +++ b/common/usbc/tbt_alt_mode.c @@ -0,0 +1,241 @@ +/* Copyright 2020 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. + */ + +/* + * Thunderbolt alternate mode support + * Refer to USB Type-C Cable and Connector Specification Release 2.0 Section F + */ + +#include <stdbool.h> +#include <stdint.h> +#include "compile_time_macros.h" +#include "console.h" +#include "tcpm.h" +#include "usb_mux.h" +#include "usb_pd.h" +#include "usb_pd_dpm.h" +#include "usb_pd_tbt.h" +#include "usb_pe_sm.h" +#include "usb_tbt_alt_mode.h" + +#ifdef CONFIG_COMMON_RUNTIME +#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) +#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) +#else +#define CPRINTF(format, args...) +#define CPRINTS(format, args...) +#endif + +static int tbt_prints(const char *string, int port) +{ + return CPRINTS("C%d: TBT %s", port, string); +} + +/* The states of Thunderbolt negotiation */ +enum tbt_states { + TBT_START = 0, + TBT_ENTER_SOP_SENT, + TBT_ENTER_SOP_NACKED, + TBT_ACTIVE, + TBT_EXIT_SOP_SENT, + TBT_EXIT_SOP_RETRY_SENT, + TBT_ENTER_SOP_RETRY, + TBT_ENTER_SOP_RETRY_SENT, + TBT_INACTIVE, + TBT_STATE_COUNT, +}; +static enum tbt_states tbt_state[CONFIG_USB_PD_PORT_MAX_COUNT]; + +static const uint8_t state_vdm_cmd[TBT_STATE_COUNT] = { + [TBT_ENTER_SOP_SENT] = CMD_ENTER_MODE, + [TBT_ACTIVE] = CMD_EXIT_MODE, + [TBT_EXIT_SOP_SENT] = CMD_EXIT_MODE, + [TBT_EXIT_SOP_RETRY_SENT] = CMD_EXIT_MODE, + [TBT_ENTER_SOP_RETRY_SENT] = CMD_ENTER_MODE, +}; + +void tbt_init(int port) +{ + tbt_state[port] = TBT_START; +} + +void tbt_teardown(int port) +{ + tbt_prints("teardown", port); + tbt_state[port] = TBT_INACTIVE; +} + +static void tbt_entry_failed(int port) +{ + tbt_prints("alt mode protocol failed!", port); + tbt_state[port] = TBT_INACTIVE; + dpm_set_mode_entry_done(port); +} + +static bool tbt_response_valid(int port, enum tcpm_transmit_type type, + char *cmdt, int vdm_cmd) +{ + enum tbt_states st = tbt_state[port]; + + /* + * Check for an unexpected response. + * If Thunderbolt is inactive, ignore the command. + */ + if ((st != TBT_INACTIVE && state_vdm_cmd[st] != vdm_cmd) || + (get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE && + type != TCPC_TX_SOP)) { + tbt_entry_failed(port); + return false; + } + return true; +} + +void intel_vdm_acked(int port, enum tcpm_transmit_type type, int vdo_count, + uint32_t *vdm) +{ + const uint8_t vdm_cmd = PD_VDO_CMD(vdm[0]); + + if (!tbt_response_valid(port, type, "ACK", vdm_cmd)) + return; + + switch (tbt_state[port]) { + case TBT_ENTER_SOP_SENT: + case TBT_ENTER_SOP_RETRY_SENT: + set_tbt_compat_mode_ready(port); + dpm_set_mode_entry_done(port); + tbt_state[port] = TBT_ACTIVE; + tbt_prints("enter mode SOP", port); + break; + case TBT_EXIT_SOP_SENT: + /* + * Request to exit mode successful, so put it in + * inactive state. + */ + tbt_prints("exit mode SOP", port); + tbt_state[port] = TBT_INACTIVE; + break; + case TBT_EXIT_SOP_RETRY_SENT: + /* + * The request to exit the mode was successful, + * so try to enter the mode again. + */ + tbt_state[port] = TBT_ENTER_SOP_RETRY; + break; + case TBT_INACTIVE: + /* + * This can occur if the mode is shutdown because + * the CPU is being turned off, and an exit mode + * command has been sent. + */ + break; + default: + /* Invalid or unexpected negotiation state */ + CPRINTF("%s called with invalid state %d\n", + __func__, tbt_state[port]); + tbt_entry_failed(port); + break; + } +} + +void intel_vdm_naked(int port, enum tcpm_transmit_type type, uint8_t vdm_cmd) +{ + if (!tbt_response_valid(port, type, "NACK", vdm_cmd)) + return; + + switch (tbt_state[port]) { + case TBT_ENTER_SOP_SENT: + /* + * If a request to enter Thunderbolt mode is NAK'ed, this + * likely means the partner is already in Thunderbolt alt mode, + * so request to exit the mode first before retrying the enter + * command. This can happen if the EC is restarted + */ + tbt_state[port] = TBT_ENTER_SOP_NACKED; + break; + case TBT_ENTER_SOP_RETRY_SENT: + /* + * Another NAK on the second attempt to enter Thunderbolt mode. + * Give up. + */ + tbt_entry_failed(port); + break; + default: + CPRINTS("C%d: NAK for cmd %d in state %d", port, + vdm_cmd, tbt_state[port]); + tbt_entry_failed(port); + break; + } +} + +int tbt_setup_next_vdm(int port, int vdo_count, uint32_t *vdm) +{ + const struct pd_discovery *disc = + pd_get_am_discovery(port, TCPC_TX_SOP); + struct svdm_amode_data *modep; + int vdo_count_ret = 0; + + if (vdo_count < VDO_MAX_SIZE || + !disc->identity.idh.modal_support || + !is_tbt_cable_superspeed(port)) { + return -1; + } + + switch (tbt_state[port]) { + case TBT_START: + case TBT_ENTER_SOP_RETRY: + if (tbt_state[port] == TBT_START) + tbt_prints("attempt to enter mode", port); + /* + * Note: If it's not a Passive cable, the tbt_setup_next_vdm() + * function will return zero + */ + if (get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE) { + vdo_count_ret = + enter_tbt_compat_mode(port, TCPC_TX_SOP, vdm); + if (tbt_state[port] == TBT_START) + tbt_state[port] = TBT_ENTER_SOP_SENT; + else + tbt_state[port] = TBT_ENTER_SOP_RETRY_SENT; + break; + } + /* + * TODO(b/148528713): Add support for Thunderbolt active cable. + */ + case TBT_ENTER_SOP_NACKED: + case TBT_ACTIVE: + /* + * Called to exit Thunderbolt alt mode, either when the mode is + * active and the system is shutting down, or when an initial + * request to enter the mode is NAK'ed. This can happen if EC + * is restarted while Thunderbolt mode is active. + */ + if (get_usb_pd_cable_type(port) == IDH_PTYPE_PCABLE) { + modep = pd_get_amode_data(port, + TCPC_TX_SOP, USB_VID_INTEL); + if (!(modep && modep->opos)) + return -1; + + vdm[0] = VDO(USB_VID_INTEL, 1, CMD_EXIT_MODE) | + VDO_OPOS(modep->opos) | + VDO_CMDT(CMDT_INIT) | + VDO_SVDM_VERS( + pd_get_vdo_ver(port, TCPC_TX_SOP)); + vdo_count_ret = 1; + tbt_state[port] = (tbt_state[port] == TBT_ACTIVE) ? + TBT_EXIT_SOP_SENT : + TBT_EXIT_SOP_RETRY_SENT; + } + break; + case TBT_INACTIVE: + /* Thunderbolt mode is inactive */ + return -1; + default: + CPRINTF("%s called with invalid state %d\n", + __func__, tbt_state[port]); + return -1; + } + + return vdo_count_ret; +} diff --git a/common/usbc/usb_pd_dpm.c b/common/usbc/usb_pd_dpm.c index e7136f41bc..af72b74b5a 100644 --- a/common/usbc/usb_pd_dpm.c +++ b/common/usbc/usb_pd_dpm.c @@ -15,6 +15,7 @@ #include "usb_pd.h" #include "usb_pd_dpm.h" #include "usb_pe_sm.h" +#include "usb_tbt_alt_mode.h" #include "tcpm.h" #ifdef CONFIG_COMMON_RUNTIME @@ -50,6 +51,11 @@ void dpm_vdm_acked(int port, enum tcpm_transmit_type type, int vdo_count, case USB_SID_DISPLAYPORT: dp_vdm_acked(port, type, vdo_count, vdm); break; + case USB_VID_INTEL: + if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE)) { + intel_vdm_acked(port, type, vdo_count, vdm); + break; + } default: CPRINTS("C%d: Received unexpected VDM ACK for SVID %d", port, svid); @@ -63,6 +69,11 @@ void dpm_vdm_naked(int port, enum tcpm_transmit_type type, uint16_t svid, case USB_SID_DISPLAYPORT: dp_vdm_naked(port, type, vdm_cmd); break; + case USB_VID_INTEL: + if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE)) { + intel_vdm_naked(port, type, vdm_cmd); + break; + } default: CPRINTS("C%d: Received unexpected VDM NAK for SVID %d", port, svid); @@ -71,7 +82,7 @@ void dpm_vdm_naked(int port, enum tcpm_transmit_type type, uint16_t svid, void dpm_attempt_mode_entry(int port) { - int vdo_count; + int vdo_count = 0; uint32_t vdm[VDO_MAX_SIZE]; if (dpm[port].mode_entry_done) @@ -96,21 +107,34 @@ void dpm_attempt_mode_entry(int port) pd_get_modes_discovery(port, TCPC_TX_SOP) != PD_DISC_COMPLETE) return; + /* Check if we discovered a Thunderbot-Compatible mode */ + if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE) && + pd_is_mode_discovered_for_svid(port, TCPC_TX_SOP, + USB_VID_INTEL)) + vdo_count = tbt_setup_next_vdm(port, ARRAY_SIZE(vdm), vdm); + + /* + * IF thunderbolt mode is not discovered or if the device/cable is not + * thunderbolt compatible, Check if we discovered a DisplayPort mode + */ + if (vdo_count == 0 && !dpm[port].mode_entry_done && + pd_is_mode_discovered_for_svid(port, TCPC_TX_SOP, + USB_SID_DISPLAYPORT)) + vdo_count = dp_setup_next_vdm(port, ARRAY_SIZE(vdm), vdm); + /* - * Check if we even discovered a DisplayPort mode; if not, just - * mark discovery done and get out of here. + * If we did not enter any alternate mode, just mark discovery done + * and get out of here. */ - if (!pd_is_mode_discovered_for_svid(port, TCPC_TX_SOP, - USB_SID_DISPLAYPORT)) { - CPRINTF("C%d: No DP mode discovered\n", port); + if (vdo_count == 0 && !dpm[port].mode_entry_done) { + CPRINTF("C%d: No supported ALT mode discovered\n", port); dpm_set_mode_entry_done(port); return; } - vdo_count = dp_setup_next_vdm(port, ARRAY_SIZE(vdm), vdm); if (vdo_count < 0) { dpm_set_mode_entry_done(port); - CPRINTF("C%d: Couldn't set up DP VDM\n", port); + CPRINTF("C%d: Couldn't set up ALT VDM\n", port); return; } diff --git a/common/usbc/usb_pe_drp_sm.c b/common/usbc/usb_pe_drp_sm.c index 533a6c1f8b..aed9528c10 100644 --- a/common/usbc/usb_pe_drp_sm.c +++ b/common/usbc/usb_pe_drp_sm.c @@ -22,6 +22,7 @@ #include "usb_pd.h" #include "usb_pd_tcpm.h" #include "usb_pe_sm.h" +#include "usb_tbt_alt_mode.h" #include "usb_prl_sm.h" #include "usb_tc_sm.h" #include "usb_emsg.h" @@ -1053,6 +1054,7 @@ void pd_send_vdm(int port, uint32_t vid, int cmd, const uint32_t *data, task_wake(PD_PORT_TO_TASK_ID(port)); } +/* TODO: Add a common exit routine for all the alternate modes */ void pe_exit_dp_mode(int port) { if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) { @@ -5395,6 +5397,9 @@ void pd_dfp_discovery_init(int port) /* Reset the DPM and DP modules to enable alternate mode entry. */ dpm_init(port); dp_init(port); + + if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE)) + tbt_init(port); } #ifdef CONFIG_USB_PD_ALT_MODE_DFP diff --git a/include/usb_tbt_alt_mode.h b/include/usb_tbt_alt_mode.h new file mode 100644 index 0000000000..186c2f8139 --- /dev/null +++ b/include/usb_tbt_alt_mode.h @@ -0,0 +1,65 @@ +/* Copyright 2020 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. + */ + +/* + * Thunderbolt alternate mode support + * Refer to USB Type-C Cable and Connector Specification Release 2.0 Section F + */ + +#ifndef __CROS_EC_USB_TBT_ALT_MODE_H +#define __CROS_EC_USB_TBT_ALT_MODE_H + +#include <stdint.h> + +#include "tcpm.h" + +/* + * Initialize Thunderbolt state for the specified port. + * + * @param port USB-C port number + */ +void tbt_init(int port); + +/* + * Handles received Thunderbolt VDM ACKs. + * + * @param port USB-C port number + * @param type Transmit type (SOP, SOP') for received ACK + * @param vdo_count The number of VDOs in the ACK VDM + * @param vdm VDM from ACK + */ +void intel_vdm_acked(int port, enum tcpm_transmit_type type, int vdo_count, + uint32_t *vdm); + +/* + * Handles NAKed (or Not Supported or timed out) Thunderbolt VDM requests. + * + * @param port USB-C port number + * @param type Transmit type (SOP, SOP') for request + * @param svid The SVID of the request + * @param vdm_cmd The VDM command of the request + */ +void intel_vdm_naked(int port, enum tcpm_transmit_type type, uint8_t vdm_cmd); + +/* + * Reset the Thunderbolt mode VDM state for the specified port, as when exiting + * Thunderbolt mode. + * + * @param port USB-C port number + */ +void tbt_teardown(int port); + +/* + * Construct the next Thunderbolt VDM that should be sent. + * + * @param port USB-C port number + * @param vdo_count The number of VDOs in vdm; must be at least VDO_MAX_SIZE + * @param vdm The VDM payload to be sent; output; must point to at least + * VDO_MAX_SIZE elements + * @return The number of VDOs written to VDM or -1 to indicate error + */ +int tbt_setup_next_vdm(int port, int vdo_count, uint32_t *vdm); + +#endif |