diff options
author | Abe Levkoy <alevkoy@chromium.org> | 2020-10-15 10:52:50 -0600 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-10-22 19:38:09 +0000 |
commit | e1728abb335acf11e2c9512ae88181a770935850 (patch) | |
tree | 75e3d109394193178e2552bf41f0492d44f5db01 | |
parent | 67645100a8dd165840c65f4bb73343ec1e53300f (diff) | |
download | chrome-ec-e1728abb335acf11e2c9512ae88181a770935850.tar.gz |
TCPMv2: Add typeccontrol enter-mode subcommand
Define and implement TYPEC_CONTROL_COMMAND_ENTER_MODE. Allow DPM state
to be accessed asynchronously by host commands. Add support for this
command to ectool.
BUG=b:168030639
TEST=Attach DP dongle; discovers but does not enter
TEST=ectool typeccontrol 1 2 0; enters DP
TEST=Attach TBT dock and TBT active cable; discovers but does not enter
TEST=ectool typeccontrol 1 2 1; enters TBT
BRANCH=none
Change-Id: I218c4b9a92004ef1efe9a27b2a920031961b33f3
Signed-off-by: Abe Levkoy <alevkoy@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2454538
Reviewed-by: Diana Z <dzigterman@chromium.org>
-rw-r--r-- | common/usbc/usb_pd_dpm.c | 148 | ||||
-rw-r--r-- | common/usbc/usb_pd_host.c | 3 | ||||
-rw-r--r-- | include/config.h | 6 | ||||
-rw-r--r-- | include/ec_commands.h | 9 | ||||
-rw-r--r-- | include/usb_pd.h | 12 | ||||
-rw-r--r-- | include/usb_pd_dpm.h | 3 | ||||
-rw-r--r-- | util/ectool.c | 26 |
7 files changed, 182 insertions, 25 deletions
diff --git a/common/usbc/usb_pd_dpm.c b/common/usbc/usb_pd_dpm.c index 87516e4390..c131bc9a2a 100644 --- a/common/usbc/usb_pd_dpm.c +++ b/common/usbc/usb_pd_dpm.c @@ -11,12 +11,14 @@ #include "charge_state.h" #include "compile_time_macros.h" #include "console.h" +#include "ec_commands.h" +#include "system.h" +#include "tcpm.h" #include "usb_dp_alt_mode.h" #include "usb_mode.h" #include "usb_pd.h" #include "usb_pd_dpm.h" #include "usb_tbt_alt_mode.h" -#include "tcpm.h" #ifdef CONFIG_COMMON_RUNTIME #define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) @@ -27,29 +29,100 @@ #endif static struct { - bool mode_entry_done; - bool mode_exit_request; + uint32_t flags; } dpm[CONFIG_USB_PD_PORT_MAX_COUNT]; +#define DPM_SET_FLAG(port, flag) \ + deprecated_atomic_or(&dpm[(port)].flags, (flag)) +#define DPM_CLR_FLAG(port, flag) \ + deprecated_atomic_clear_bits(&dpm[(port)].flags, (flag)) +#define DPM_CHK_FLAG(port, flag) (dpm[(port)].flags & (flag)) + +/* Flags for internal DPM state */ +#define DPM_FLAG_MODE_ENTRY_DONE BIT(0) +#define DPM_FLAG_EXIT_REQUEST BIT(1) +#define DPM_FLAG_ENTER_DP BIT(2) +#define DPM_FLAG_ENTER_TBT BIT(3) +#define DPM_FLAG_ENTER_USB4 BIT(4) + +enum ec_status pd_request_enter_mode(int port, enum typec_mode mode) +{ + if (port >= board_get_usb_pd_port_count()) + return EC_RES_INVALID_PARAM; + + /* Only one enter request may be active at a time. */ + if (DPM_CHK_FLAG(port, DPM_FLAG_ENTER_DP | + DPM_FLAG_ENTER_TBT | + DPM_FLAG_ENTER_USB4)) + return EC_RES_BUSY; + + switch (mode) { + case TYPEC_MODE_DP: + DPM_SET_FLAG(port, DPM_FLAG_ENTER_DP); + break; +#ifdef CONFIG_USB_PD_TBT_COMPAT_MODE + case TYPEC_MODE_TBT: + DPM_SET_FLAG(port, DPM_FLAG_ENTER_TBT); + break; +#endif /* CONFIG_USB_PD_TBT_COMPAT_MODE */ +#ifdef CONFIG_USB_PD_USB4 + case TYPEC_MODE_USB4: + DPM_SET_FLAG(port, DPM_FLAG_ENTER_USB4); + break; +#endif + default: + return EC_RES_INVALID_PARAM; + } + + DPM_CLR_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE); + DPM_CLR_FLAG(port, DPM_FLAG_EXIT_REQUEST); + + return EC_RES_SUCCESS; +} + void dpm_init(int port) { - dpm[port].mode_entry_done = false; - dpm[port].mode_exit_request = false; + dpm[port].flags = 0; } static void dpm_set_mode_entry_done(int port) { - dpm[port].mode_entry_done = true; + DPM_SET_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE); + DPM_CLR_FLAG(port, DPM_FLAG_ENTER_DP | DPM_FLAG_ENTER_TBT | + DPM_FLAG_ENTER_USB4); } void dpm_set_mode_exit_request(int port) { - dpm[port].mode_exit_request = true; + DPM_SET_FLAG(port, DPM_FLAG_EXIT_REQUEST); } static void dpm_clear_mode_exit_request(int port) { - dpm[port].mode_exit_request = false; + DPM_CLR_FLAG(port, DPM_FLAG_EXIT_REQUEST); +} + +/* + * Returns true if the current policy requests that the EC try to enter this + * mode on this port. If the EC is in charge of policy, the answer is always + * yes. + */ +static bool dpm_mode_entry_requested(int port, enum typec_mode mode) +{ + /* If the AP isn't controlling policy, the EC is. */ + if (!IS_ENABLED(CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY)) + return true; + + switch (mode) { + case TYPEC_MODE_DP: + return !!DPM_CHK_FLAG(port, DPM_FLAG_ENTER_DP); + case TYPEC_MODE_TBT: + return !!DPM_CHK_FLAG(port, DPM_FLAG_ENTER_TBT); + case TYPEC_MODE_USB4: + return !!DPM_CHK_FLAG(port, DPM_FLAG_ENTER_USB4); + default: + return false; + } } void dpm_vdm_acked(int port, enum tcpm_transmit_type type, int vdo_count, @@ -93,18 +166,32 @@ void dpm_vdm_naked(int port, enum tcpm_transmit_type type, uint16_t svid, } /* - * The call to this function requests that the PE send one VDM, whichever is - * next in the mode entry sequence. This only happens if preconditions for mode - * entry are met. + * Requests that the PE send one VDM, whichever is next in the mode entry + * sequence. This only happens if preconditions for mode entry are met. If + * CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY is enabled, this function waits for the + * AP to direct mode entry. */ 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; + bool enter_mode_requested = + IS_ENABLED(CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY) ? false : true; - if (pd_get_data_role(port) != PD_ROLE_DFP) + if (pd_get_data_role(port) != PD_ROLE_DFP) { + if (DPM_CHK_FLAG(port, DPM_FLAG_ENTER_DP | + DPM_FLAG_ENTER_TBT | + DPM_FLAG_ENTER_USB4)) + DPM_CLR_FLAG(port, DPM_FLAG_ENTER_DP | + DPM_FLAG_ENTER_TBT | + DPM_FLAG_ENTER_USB4); + /* + * TODO(b/168030639): Notify the AP that the enter mode request + * failed. + */ return; + } /* * Do not try to enter mode while CPU is off. * CPU transitions (e.g b/158634281) can occur during the discovery @@ -131,29 +218,46 @@ static void dpm_attempt_mode_entry(int port) } /* Check if the device and cable support USB4. */ - if (IS_ENABLED(CONFIG_USB_PD_USB4) && enter_usb_is_capable(port)) { + if (IS_ENABLED(CONFIG_USB_PD_USB4) && enter_usb_is_capable(port) && + dpm_mode_entry_requested(port, TYPEC_MODE_USB4)) { pd_dpm_request(port, DPM_REQUEST_ENTER_USB); return; } /* If not, check if they support Thunderbolt alt mode. */ if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE) && - pd_is_mode_discovered_for_svid(port, TCPC_TX_SOP, USB_VID_INTEL)) + pd_is_mode_discovered_for_svid(port, TCPC_TX_SOP, USB_VID_INTEL) && + dpm_mode_entry_requested(port, TYPEC_MODE_TBT)) { + enter_mode_requested = true; vdo_count = tbt_setup_next_vdm(port, ARRAY_SIZE(vdm), vdm, &tx_type); + } /* If not, check if they support DisplayPort alt mode. */ - if (vdo_count == 0 && !dpm[port].mode_entry_done && + if (vdo_count == 0 && !DPM_CHK_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE) && pd_is_mode_discovered_for_svid(port, TCPC_TX_SOP, - USB_SID_DISPLAYPORT)) + USB_SID_DISPLAYPORT) && + dpm_mode_entry_requested(port, TYPEC_MODE_DP)) { + enter_mode_requested = true; vdo_count = dp_setup_next_vdm(port, ARRAY_SIZE(vdm), vdm); + } /* - * If the PE didn't discover any supported alternate mode, just mark - * setup done and get out of here. + * If the PE didn't discover any supported (requested) alternate mode, + * just mark setup done and get out of here. */ - if (vdo_count == 0 && !dpm[port].mode_entry_done) { - CPRINTS("C%d: No supported alt mode discovered", port); + if (vdo_count == 0 && !DPM_CHK_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE)) { + if (enter_mode_requested) { + /* + * TODO(b/168030639): Notify the AP that mode entry + * failed. + */ + CPRINTS("C%d: No supported alt mode discovered", port); + } + /* + * If the AP did not request mode entry, it may do so in the + * future, but the DPM is done trying for now. + */ dpm_set_mode_entry_done(port); return; } @@ -208,8 +312,8 @@ static void dpm_attempt_mode_exit(int port) void dpm_run(int port) { - if (dpm[port].mode_exit_request) + if (DPM_CHK_FLAG(port, DPM_FLAG_EXIT_REQUEST)) dpm_attempt_mode_exit(port); - else if (!dpm[port].mode_entry_done) + else if (!DPM_CHK_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE)) dpm_attempt_mode_entry(port); } diff --git a/common/usbc/usb_pd_host.c b/common/usbc/usb_pd_host.c index 61dfa8bf49..8f084a0cb9 100644 --- a/common/usbc/usb_pd_host.c +++ b/common/usbc/usb_pd_host.c @@ -111,6 +111,9 @@ static enum ec_status hc_typec_control(struct host_cmd_handler_args *args) case TYPEC_CONTROL_COMMAND_CLEAR_EVENTS: pd_clear_events(p->port, p->clear_events_mask); break; + case TYPEC_CONTROL_COMMAND_ENTER_MODE: { + return pd_request_enter_mode(p->port, p->mode_to_enter); + } default: return EC_RES_INVALID_PARAM; } diff --git a/include/config.h b/include/config.h index 8966f0e0e8..4c3ec5289e 100644 --- a/include/config.h +++ b/include/config.h @@ -3905,6 +3905,12 @@ /* Support for USB PD alternate mode of Downward Facing Port */ #undef CONFIG_USB_PD_ALT_MODE_DFP +/* + * Do not enter USB PD alternate modes or USB4 automatically. Wait for the AP to + * direct the EC to enter a mode. This requires AP software support. + */ +#undef CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY + /* HPD is sent to the GPU from the EC via a GPIO */ #undef CONFIG_USB_PD_DP_HPD_GPIO diff --git a/include/ec_commands.h b/include/ec_commands.h index 226571b005..c378847904 100644 --- a/include/ec_commands.h +++ b/include/ec_commands.h @@ -6371,6 +6371,14 @@ struct ec_response_typec_discovery { enum typec_control_command { TYPEC_CONTROL_COMMAND_EXIT_MODES, TYPEC_CONTROL_COMMAND_CLEAR_EVENTS, + TYPEC_CONTROL_COMMAND_ENTER_MODE, +}; + +/* Modes (USB or alternate) that a type-C port may enter. */ +enum typec_mode { + TYPEC_MODE_DP, + TYPEC_MODE_TBT, + TYPEC_MODE_USB4, }; struct ec_params_typec_control { @@ -6385,6 +6393,7 @@ struct ec_params_typec_control { */ union { uint32_t clear_events_mask; + uint8_t mode_to_enter; /* enum typec_mode */ uint8_t placeholder[128]; }; } __ec_align1; diff --git a/include/usb_pd.h b/include/usb_pd.h index df5f785135..6c9de3307e 100644 --- a/include/usb_pd.h +++ b/include/usb_pd.h @@ -2609,6 +2609,18 @@ uint32_t pd_get_events(int port); */ void pd_clear_events(int port, uint32_t clear_mask); +/* + * Requests that the port enter the specified mode. A successful result just + * means that the request was received, not that the mode has been entered yet. + * + * @param port USB-C port number + * @param mode The mode to enter + * @return EC_RES_SUCCESS if the request was made + * EC_RES_INVALID_PARAM for an invalid port or mode; + * EC_RES_BUSY if another mode entry request is already in progress + */ +enum ec_status pd_request_enter_mode(int port, enum typec_mode mode); + /** * Get port partner data swap capable status * diff --git a/include/usb_pd_dpm.h b/include/usb_pd_dpm.h index d1c029bc03..ccc7c57ff7 100644 --- a/include/usb_pd_dpm.h +++ b/include/usb_pd_dpm.h @@ -11,6 +11,9 @@ #ifndef __CROS_EC_USB_DPM_H #define __CROS_EC_USB_DPM_H +#include "ec_commands.h" +#include "usb_pd_tcpm.h" + /* * Initializes DPM state for a port. * diff --git a/util/ectool.c b/util/ectool.c index 3220f3fd98..78f7a89473 100644 --- a/util/ectool.c +++ b/util/ectool.c @@ -9479,6 +9479,7 @@ int cmd_pd_write_log(int argc, char *argv[]) int cmd_typec_control(int argc, char *argv[]) { struct ec_params_typec_control p; + long conversion_result; char *endptr; int rv; @@ -9486,9 +9487,13 @@ int cmd_typec_control(int argc, char *argv[]) fprintf(stderr, "Usage: %s <port> <command> [args]\n" " <port> is the type-c port to query\n" - " <type> is one of:\n" + " <command> is one of:\n" " 0: Exit modes\n" - " 1: Clear events\n", argv[0]); + " 1: Clear events\n" + " args: <event mask>\n" + " 2: Enter mode\n" + " args: <0: DP, 1:TBT, 2:USB4>\n", + argv[0]); return -1; } @@ -9504,7 +9509,8 @@ int cmd_typec_control(int argc, char *argv[]) return -1; } - if (p.command == TYPEC_CONTROL_COMMAND_CLEAR_EVENTS) { + switch (p.command) { + case TYPEC_CONTROL_COMMAND_CLEAR_EVENTS: if (argc < 4) { fprintf(stderr, "Missing event mask\n"); return -1; @@ -9515,6 +9521,20 @@ int cmd_typec_control(int argc, char *argv[]) fprintf(stderr, "Bad event mask\n"); return -1; } + break; + case TYPEC_CONTROL_COMMAND_ENTER_MODE: + if (argc < 4) { + fprintf(stderr, "Missing mode\n"); + return -1; + } + + conversion_result = strtol(argv[3], &endptr, 0); + if ((endptr && *endptr) || conversion_result > UINT8_MAX || + conversion_result < 0) { + fprintf(stderr, "Bad mode\n"); + return -1; + } + p.mode_to_enter = conversion_result; } rv = ec_command(EC_CMD_TYPEC_CONTROL, 0, &p, sizeof(p), |