summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorli feng <li1.feng@intel.com>2020-06-30 17:02:14 -0700
committerCommit Bot <commit-bot@chromium.org>2020-07-18 05:04:29 +0000
commitd9cf2c2ffa2b88c04544d32e26ed1beff1eb5e40 (patch)
treef963498b2331b764a01a639db62204629fdac4da
parenta3c994ccc1cda5450d8da2c568653ca10b7b8439 (diff)
downloadchrome-ec-d9cf2c2ffa2b88c04544d32e26ed1beff1eb5e40.tar.gz
TCPMv2: Add thunderbolt entry flow for active cable
Order of Enter Mode: SOP', SOP''(if supported), SOP Order of Exit Mode: SOP, SOP''(if supported), SOP' Entry flow start from TBT_START, state changes to TBT_ACTIVE if succeed; changes to TBT_INACTIVE if fail. Handling of NAK If any enter mode SOP* is NAK'ed, exit modes of all SOP* and retry enter. If any enter mode in retry is NAK'ed again, will exit modes of all SOP*, TBT entry failed. Special case: Exit in TBT_ACTIVE: start from TBT_ACTIVE, exit all modes of SOP*, then chagne state to TBT_INACTIVE. BUG=b:148528713 BRANCH=none TEST=1. Attach Thunderbolt dock and active cable, DUT enter Thunderbolt mode 2. Attach Thunderbolt dock and passive cable, DUT enter Thunderbolt mode Signed-off-by: li feng <li1.feng@intel.com> Change-Id: Iaaa63f70c4abc9c269cdbb2a504214f924155901 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2277487 Reviewed-by: Abe Levkoy <alevkoy@chromium.org>
-rw-r--r--common/usbc/tbt_alt_mode.c294
-rw-r--r--common/usbc/usb_pd_dpm.c6
-rw-r--r--include/usb_tbt_alt_mode.h4
3 files changed, 248 insertions, 56 deletions
diff --git a/common/usbc/tbt_alt_mode.c b/common/usbc/tbt_alt_mode.c
index ee4c78285e..5c763ea287 100644
--- a/common/usbc/tbt_alt_mode.c
+++ b/common/usbc/tbt_alt_mode.c
@@ -20,6 +20,40 @@
#include "usb_pe_sm.h"
#include "usb_tbt_alt_mode.h"
+/*
+ * Enter/Exit TBT mode with active cable
+ *
+ *
+ * TBT_START |------------
+ * retry_done = false | |
+ * | v |
+ * |<------------------| Exit Mode SOP |
+ * | retry_done = true | | |
+ * v | | ACK/NAK |
+ * Enter Mode SOP' | --------|--------- |
+ * ACK | NAK | Exit Mode SOP'' |
+ * |------|------| | | |
+ * | | | | ACK/NAK |
+ * v | | --------|--------- |
+ * Enter Mode SOP'' | | Exit Mode SOP' |
+ * | | | | |
+ * ACK | NAK | | | ACK/NAK |
+ * |------|------| | | ------------------ |
+ * | | | | retry_done == true? |
+ * v | | | | |
+ * Enter Mode SOP | | | No | |
+ * | | | |----------- |
+ * ACK | NAK | | |Yes |
+ * |-------|------| | | v |
+ * | | | | TBT_INACTIVE |
+ * v | | | retry_done = false |
+ * TBT_ACTIVE | | | |
+ * retry_done = true | | | |
+ * | | | | |
+ * v v v v |
+ * -----------------------------------------------------------------|
+ */
+
#ifdef CONFIG_COMMON_RUNTIME
#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args)
@@ -28,6 +62,14 @@
#define CPRINTS(format, args...)
#endif
+/*
+ * If a partner sends an Enter Mode NAK, Exit Mode and try again. This has
+ * happened when the EC loses state after previously entering an alt mode
+ * with a partner. It may be fixed in b/159495742, in which case this
+ * logic is unneeded.
+ */
+static bool retry_done;
+
static int tbt_prints(const char *string, int port)
{
return CPRINTS("C%d: TBT %s", port, string);
@@ -36,24 +78,34 @@ static int tbt_prints(const char *string, int port)
/* The states of Thunderbolt negotiation */
enum tbt_states {
TBT_START = 0,
- TBT_ENTER_SOP_NACKED,
+ TBT_ENTER_SOP,
TBT_ACTIVE,
- TBT_ENTER_SOP_RETRY,
+ TBT_EXIT_SOP,
TBT_INACTIVE,
+ /* Active cable only */
+ TBT_ENTER_SOP_PRIME,
+ TBT_ENTER_SOP_PRIME_PRIME,
+ TBT_EXIT_SOP_PRIME,
+ TBT_EXIT_SOP_PRIME_PRIME,
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_START] = CMD_ENTER_MODE,
+ [TBT_ENTER_SOP] = CMD_ENTER_MODE,
[TBT_ACTIVE] = CMD_EXIT_MODE,
- [TBT_ENTER_SOP_NACKED] = CMD_EXIT_MODE,
- [TBT_ENTER_SOP_RETRY] = CMD_ENTER_MODE,
+ [TBT_EXIT_SOP] = CMD_EXIT_MODE,
+ /* Active cable only */
+ [TBT_ENTER_SOP_PRIME] = CMD_ENTER_MODE,
+ [TBT_ENTER_SOP_PRIME_PRIME] = CMD_ENTER_MODE,
+ [TBT_EXIT_SOP_PRIME] = CMD_EXIT_MODE,
+ [TBT_EXIT_SOP_PRIME_PRIME] = CMD_EXIT_MODE,
};
void tbt_init(int port)
{
tbt_state[port] = TBT_START;
+ retry_done = false;
}
bool tbt_is_active(int port)
@@ -65,12 +117,14 @@ void tbt_teardown(int port)
{
tbt_prints("teardown", port);
tbt_state[port] = TBT_INACTIVE;
+ retry_done = false;
}
static void tbt_entry_failed(int port)
{
tbt_prints("alt mode protocol failed!", port);
tbt_state[port] = TBT_INACTIVE;
+ retry_done = false;
dpm_set_mode_entry_done(port);
}
@@ -81,6 +135,8 @@ static bool tbt_response_valid(int port, enum tcpm_transmit_type type,
/*
* Check for an unexpected response.
+ * 1. invalid command
+ * 2. invalid Tx type for passive cable
* If Thunderbolt is inactive, ignore the command.
*/
if ((st != TBT_INACTIVE && state_vdm_cmd[st] != vdm_cmd) ||
@@ -92,36 +148,90 @@ static bool tbt_response_valid(int port, enum tcpm_transmit_type type,
return true;
}
+/* Exit Mode process is complete, but retry Enter Mode process */
+static void tbt_retry_enter_mode(int port)
+{
+ tbt_state[port] = TBT_START;
+ retry_done = true;
+}
+
+/* Send Exit Mode to SOP''(if supported), or SOP' */
+static void tbt_active_cable_exit_mode(int port)
+{
+ struct pd_discovery *disc;
+
+ disc = pd_get_am_discovery(port, TCPC_TX_SOP_PRIME);
+
+ if (disc->identity.product_t1.a_rev20.sop_p_p)
+ tbt_state[port] = TBT_EXIT_SOP_PRIME_PRIME;
+ else
+ tbt_state[port] = TBT_EXIT_SOP_PRIME;
+}
+
void intel_vdm_acked(int port, enum tcpm_transmit_type type, int vdo_count,
uint32_t *vdm)
{
+ struct pd_discovery *disc;
const uint8_t vdm_cmd = PD_VDO_CMD(vdm[0]);
if (!tbt_response_valid(port, type, "ACK", vdm_cmd))
return;
+ disc = pd_get_am_discovery(port, TCPC_TX_SOP_PRIME);
+
switch (tbt_state[port]) {
- case TBT_START:
- case TBT_ENTER_SOP_RETRY:
+ case TBT_ENTER_SOP_PRIME:
+ if (disc->identity.product_t1.a_rev20.sop_p_p)
+ tbt_state[port] = TBT_ENTER_SOP_PRIME_PRIME;
+ else
+ tbt_state[port] = TBT_ENTER_SOP;
+ break;
+ case TBT_ENTER_SOP_PRIME_PRIME:
+ tbt_state[port] = TBT_ENTER_SOP;
+ break;
+ case TBT_ENTER_SOP:
set_tbt_compat_mode_ready(port);
dpm_set_mode_entry_done(port);
tbt_state[port] = TBT_ACTIVE;
+ retry_done = true;
tbt_prints("enter mode SOP", port);
break;
case TBT_ACTIVE:
- /*
- * Request to exit mode successful, so put it in
- * inactive state.
- */
- tbt_prints("exit mode SOP", port);
- tbt_state[port] = TBT_INACTIVE;
+ if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE)
+ tbt_active_cable_exit_mode(port);
+ else {
+ /*
+ * Exit Mode process is complete; go to inactive state.
+ */
+ tbt_prints("exit mode SOP", port);
+ tbt_state[port] = TBT_INACTIVE;
+ retry_done = false;
+ }
break;
- case TBT_ENTER_SOP_NACKED:
- /*
- * The request to exit the mode was successful,
- * so try to enter the mode again.
- */
- tbt_state[port] = TBT_ENTER_SOP_RETRY;
+ case TBT_EXIT_SOP:
+ if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE)
+ tbt_active_cable_exit_mode(port);
+ else {
+ if (retry_done)
+ /* retried enter mode, still failed, give up */
+ tbt_entry_failed(port);
+ else
+ tbt_retry_enter_mode(port);
+ }
+ break;
+ case TBT_EXIT_SOP_PRIME_PRIME:
+ tbt_state[port] = TBT_EXIT_SOP_PRIME;
+ break;
+ case TBT_EXIT_SOP_PRIME:
+ if (retry_done) {
+ /*
+ * Exit mode process is complete; go to inactive state.
+ */
+ tbt_prints("exit mode SOP'", port);
+ tbt_entry_failed(port);
+ } else {
+ tbt_retry_enter_mode(port);
+ }
break;
case TBT_INACTIVE:
/*
@@ -141,25 +251,57 @@ void intel_vdm_acked(int port, enum tcpm_transmit_type type, int vdo_count,
void intel_vdm_naked(int port, enum tcpm_transmit_type type, uint8_t vdm_cmd)
{
- if (!tbt_response_valid(port, type, "NACK", vdm_cmd))
+ if (!tbt_response_valid(port, type, "NAK", vdm_cmd))
return;
switch (tbt_state[port]) {
- case TBT_START:
+ case TBT_ENTER_SOP_PRIME:
+ case TBT_ENTER_SOP_PRIME_PRIME:
+ case TBT_ENTER_SOP:
/*
* 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;
+ tbt_state[port] = TBT_EXIT_SOP;
break;
- case TBT_ENTER_SOP_RETRY:
- /*
- * Another NAK on the second attempt to enter Thunderbolt mode.
- * Give up.
- */
- tbt_entry_failed(port);
+ case TBT_ACTIVE:
+ /* Exit SOP got NAK'ed */
+ if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE)
+ tbt_active_cable_exit_mode(port);
+ else {
+ tbt_prints("exit mode SOP failed", port);
+ tbt_state[port] = TBT_INACTIVE;
+ retry_done = false;
+ }
+ break;
+ case TBT_EXIT_SOP:
+ /* Exit SOP got NAK'ed */
+ if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE)
+ tbt_active_cable_exit_mode(port);
+ else {
+ if (retry_done)
+ /* Retried enter mode, still failed, give up */
+ tbt_entry_failed(port);
+ else
+ tbt_retry_enter_mode(port);
+ }
+ break;
+ case TBT_EXIT_SOP_PRIME_PRIME:
+ tbt_prints("exit mode SOP'' failed", port);
+ tbt_state[port] = TBT_EXIT_SOP_PRIME;
+ break;
+ case TBT_EXIT_SOP_PRIME:
+ if (retry_done) {
+ /*
+ * Exit mode process is complete; go to inactive state.
+ */
+ tbt_prints("exit mode SOP' failed", port);
+ tbt_entry_failed(port);
+ } else {
+ tbt_retry_enter_mode(port);
+ }
break;
default:
CPRINTS("C%d: NAK for cmd %d in state %d", port,
@@ -169,7 +311,8 @@ void intel_vdm_naked(int port, enum tcpm_transmit_type type, uint8_t vdm_cmd)
}
}
-int tbt_setup_next_vdm(int port, int vdo_count, uint32_t *vdm)
+int tbt_setup_next_vdm(int port, int vdo_count, uint32_t *vdm,
+ enum tcpm_transmit_type *tx_type)
{
const struct pd_discovery *disc =
pd_get_am_discovery(port, TCPC_TX_SOP);
@@ -183,24 +326,44 @@ int tbt_setup_next_vdm(int port, int vdo_count, uint32_t *vdm)
return -1;
}
+ *tx_type = TCPC_TX_SOP;
+
switch (tbt_state[port]) {
case TBT_START:
- case TBT_ENTER_SOP_RETRY:
- if (tbt_state[port] == TBT_START)
+ if (!retry_done)
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) {
+ else
+ tbt_prints("retry to enter mode", port);
+ /* Active cable send Enter Mode SOP' first */
+ if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE) {
+ vdo_count_ret =
+ enter_tbt_compat_mode(
+ port, TCPC_TX_SOP_PRIME, vdm);
+ *tx_type = TCPC_TX_SOP_PRIME;
+ tbt_state[port] = TBT_ENTER_SOP_PRIME;
+ } else {
+ /* Passive cable send Enter Mode SOP */
vdo_count_ret =
enter_tbt_compat_mode(port, TCPC_TX_SOP, vdm);
+ tbt_state[port] = TBT_ENTER_SOP;
}
- /*
- * TODO(b/148528713): Add support for Thunderbolt active cable.
- */
break;
- case TBT_ENTER_SOP_NACKED:
+ case TBT_ENTER_SOP_PRIME:
+ vdo_count_ret =
+ enter_tbt_compat_mode(port, TCPC_TX_SOP_PRIME, vdm);
+ *tx_type = TCPC_TX_SOP_PRIME;
+ break;
+ case TBT_ENTER_SOP_PRIME_PRIME:
+ vdo_count_ret =
+ enter_tbt_compat_mode(
+ port, TCPC_TX_SOP_PRIME_PRIME, vdm);
+ *tx_type = TCPC_TX_SOP_PRIME_PRIME;
+ break;
+ case TBT_ENTER_SOP:
+ vdo_count_ret =
+ enter_tbt_compat_mode(port, TCPC_TX_SOP, vdm);
+ break;
+ case TBT_EXIT_SOP:
case TBT_ACTIVE:
/*
* Called to exit Thunderbolt alt mode, either when the mode is
@@ -208,19 +371,44 @@ int tbt_setup_next_vdm(int port, int vdo_count, uint32_t *vdm)
* 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;
- }
+ 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;
+ break;
+ case TBT_EXIT_SOP_PRIME_PRIME:
+ modep = pd_get_amode_data(port,
+ TCPC_TX_SOP_PRIME, 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_PRIME_PRIME));
+ vdo_count_ret = 1;
+ *tx_type = TCPC_TX_SOP_PRIME_PRIME;
+ break;
+ case TBT_EXIT_SOP_PRIME:
+ modep = pd_get_amode_data(port,
+ TCPC_TX_SOP_PRIME, 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_PRIME));
+ vdo_count_ret = 1;
+ *tx_type = TCPC_TX_SOP_PRIME;
break;
case TBT_INACTIVE:
/* Thunderbolt mode is inactive */
diff --git a/common/usbc/usb_pd_dpm.c b/common/usbc/usb_pd_dpm.c
index b84b5b075f..968fd13844 100644
--- a/common/usbc/usb_pd_dpm.c
+++ b/common/usbc/usb_pd_dpm.c
@@ -102,6 +102,7 @@ static void dpm_attempt_mode_entry(int port)
{
int vdo_count = 0;
uint32_t vdm[VDO_MAX_SIZE];
+ enum tcpm_transmit_type tx_type = TCPC_TX_SOP;
if (pd_get_data_role(port) != PD_ROLE_DFP)
return;
@@ -135,7 +136,8 @@ static void dpm_attempt_mode_entry(int port)
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);
+ vdo_count = tbt_setup_next_vdm(port,
+ ARRAY_SIZE(vdm), vdm, &tx_type);
/*
* IF thunderbolt mode is not discovered or if the device/cable is not
@@ -166,7 +168,7 @@ static void dpm_attempt_mode_entry(int port)
* TODO(b/155890173): Provide a host command to request that the PE send
* an arbitrary VDM via this mechanism.
*/
- if (!pd_setup_vdm_request(port, TCPC_TX_SOP, vdm, vdo_count)) {
+ if (!pd_setup_vdm_request(port, tx_type, vdm, vdo_count)) {
dpm_set_mode_entry_done(port);
return;
}
diff --git a/include/usb_tbt_alt_mode.h b/include/usb_tbt_alt_mode.h
index c46c471035..fb3aadb481 100644
--- a/include/usb_tbt_alt_mode.h
+++ b/include/usb_tbt_alt_mode.h
@@ -67,8 +67,10 @@ void tbt_teardown(int port);
* @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
+ * @param tx_type Transmit type(SOP, SOP', SOP'') for next VDM to be sent
* @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);
+int tbt_setup_next_vdm(int port, int vdo_count, uint32_t *vdm,
+ enum tcpm_transmit_type *tx_type);
#endif