summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAyushee <ayushee.shah@intel.com>2020-06-16 17:19:17 -0700
committerCommit Bot <commit-bot@chromium.org>2020-06-30 21:38:05 +0000
commita057167851e63be53e2de757361dc2e303b737e2 (patch)
tree3736a076490f16ad15a06c18406686e4694879fb
parent39ada1fd2bdadb8a319e7134e650c65332f0bc2e (diff)
downloadchrome-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.mk1
-rw-r--r--common/usbc/tbt_alt_mode.c241
-rw-r--r--common/usbc/usb_pd_dpm.c40
-rw-r--r--common/usbc/usb_pe_drp_sm.c5
-rw-r--r--include/usb_tbt_alt_mode.h65
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