diff options
author | li feng <li1.feng@intel.com> | 2020-06-30 17:02:14 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-07-18 05:04:29 +0000 |
commit | d9cf2c2ffa2b88c04544d32e26ed1beff1eb5e40 (patch) | |
tree | f963498b2331b764a01a639db62204629fdac4da | |
parent | a3c994ccc1cda5450d8da2c568653ca10b7b8439 (diff) | |
download | chrome-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.c | 294 | ||||
-rw-r--r-- | common/usbc/usb_pd_dpm.c | 6 | ||||
-rw-r--r-- | include/usb_tbt_alt_mode.h | 4 |
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 |