summaryrefslogtreecommitdiff
path: root/common/usbc
diff options
context:
space:
mode:
Diffstat (limited to 'common/usbc')
-rw-r--r--common/usbc/build.mk1
-rw-r--r--common/usbc/dp_alt_mode.c103
-rw-r--r--common/usbc/tbt_alt_mode.c104
-rw-r--r--common/usbc/usb_mode.c5
-rw-r--r--common/usbc/usb_pd_dpm.c65
-rw-r--r--common/usbc/usb_pd_timer.c99
-rw-r--r--common/usbc/usb_pe_drp_sm.c3
-rw-r--r--common/usbc/usb_tc_drp_acc_trysrc_sm.c43
8 files changed, 294 insertions, 129 deletions
diff --git a/common/usbc/build.mk b/common/usbc/build.mk
index 48ab5351b8..60e2347741 100644
--- a/common/usbc/build.mk
+++ b/common/usbc/build.mk
@@ -46,6 +46,7 @@ all-obj-$(CONFIG_USB_PD_ALT_MODE_UFP_DP)+=$(_usbc_dir)usb_pd_dp_ufp.o
endif # CONFIG_USB_PD_TCPMV2
# For testing
+all-obj-$(CONFIG_TEST_USB_PD_TIMER)+=$(_usbc_dir)usb_pd_timer.o
all-obj-$(CONFIG_TEST_USB_PE_SM)+=$(_usbc_dir)usbc_pd_policy.o
all-obj-$(CONFIG_TEST_USB_PE_SM)+=$(_usbc_dir)usb_pe_drp_sm.o
all-obj-$(CONFIG_TEST_SM)+=$(_usbc_dir)usb_sm.o
diff --git a/common/usbc/dp_alt_mode.c b/common/usbc/dp_alt_mode.c
index 9a3493c6e1..2a532466ac 100644
--- a/common/usbc/dp_alt_mode.c
+++ b/common/usbc/dp_alt_mode.c
@@ -12,6 +12,7 @@
#include <stdbool.h>
#include <stdint.h>
#include "assert.h"
+#include "atomic.h"
#include "usb_common.h"
#include "usb_dp_alt_mode.h"
#include "usb_pd.h"
@@ -31,8 +32,10 @@ enum dp_states {
DP_ENTER_ACKED,
DP_ENTER_NAKED,
DP_STATUS_ACKED,
+ DP_PREPARE_CONFIG,
DP_ACTIVE,
DP_ENTER_RETRY,
+ DP_PREPARE_EXIT,
DP_INACTIVE,
DP_STATE_COUNT
};
@@ -45,20 +48,31 @@ static enum dp_states dp_state[CONFIG_USB_PD_PORT_MAX_COUNT];
static const uint8_t state_vdm_cmd[DP_STATE_COUNT] = {
[DP_START] = CMD_ENTER_MODE,
[DP_ENTER_ACKED] = CMD_DP_STATUS,
- [DP_STATUS_ACKED] = CMD_DP_CONFIG,
- [DP_ACTIVE] = CMD_EXIT_MODE,
- [DP_ENTER_NAKED] = CMD_EXIT_MODE,
+ [DP_PREPARE_CONFIG] = CMD_DP_CONFIG,
+ [DP_PREPARE_EXIT] = CMD_EXIT_MODE,
[DP_ENTER_RETRY] = CMD_ENTER_MODE,
};
+/*
+ * Track if we're retrying due to an Enter Mode NAK
+ */
+#define DP_FLAG_RETRY BIT(0)
+
+static uint32_t dpm_dp_flags[CONFIG_USB_PD_PORT_MAX_COUNT];
+
+#define DP_SET_FLAG(port, flag) atomic_or(&dpm_dp_flags[port], (flag))
+#define DP_CLR_FLAG(port, flag) atomic_clear_bits(&dpm_dp_flags[port], (flag))
+#define DP_CHK_FLAG(port, flag) (dpm_dp_flags[port] & (flag))
+
bool dp_is_active(int port)
{
- return dp_state[port] == DP_ACTIVE;
+ return dp_state[port] == DP_ACTIVE || dp_state[port] == DP_PREPARE_EXIT;
}
void dp_init(int port)
{
dp_state[port] = DP_START;
+ dpm_dp_flags[port] = 0;
}
bool dp_entry_is_done(int port)
@@ -71,6 +85,7 @@ static void dp_entry_failed(int port)
{
CPRINTS("C%d: DP alt mode protocol failed!", port);
dp_state[port] = DP_INACTIVE;
+ dpm_dp_flags[port] = 0;
}
static bool dp_response_valid(int port, enum tcpci_msg_type type,
@@ -135,25 +150,23 @@ void dp_vdm_acked(int port, enum tcpci_msg_type type, int vdo_count,
dfp_consume_attention(port, vdm);
dp_state[port] = DP_STATUS_ACKED;
break;
- case DP_STATUS_ACKED:
+ case DP_PREPARE_CONFIG:
if (modep && modep->opos && modep->fx->post_config)
modep->fx->post_config(port);
dp_state[port] = DP_ACTIVE;
CPRINTS("C%d: Entered DP mode", port);
break;
- case DP_ACTIVE:
+ case DP_PREPARE_EXIT:
/*
* Request to exit mode successful, so put the module in an
- * inactive state.
+ * inactive state or give entry another shot.
*/
- dp_exit_to_usb_mode(port);
- break;
- case DP_ENTER_NAKED:
- /*
- * The request to exit the mode was successful,
- * so try to enter the mode again.
- */
- dp_state[port] = DP_ENTER_RETRY;
+ if (DP_CHK_FLAG(port, DP_FLAG_RETRY)) {
+ dp_state[port] = DP_ENTER_RETRY;
+ DP_CLR_FLAG(port, DP_FLAG_RETRY);
+ } else {
+ dp_exit_to_usb_mode(port);
+ }
break;
case DP_INACTIVE:
/*
@@ -195,7 +208,7 @@ void dp_vdm_naked(int port, enum tcpci_msg_type type, uint8_t vdm_cmd)
*/
dp_entry_failed(port);
break;
- case DP_ACTIVE:
+ case DP_PREPARE_EXIT:
/* Treat an Exit Mode NAK the same as an Exit Mode ACK. */
dp_exit_to_usb_mode(port);
break;
@@ -207,14 +220,15 @@ void dp_vdm_naked(int port, enum tcpci_msg_type type, uint8_t vdm_cmd)
}
}
-int dp_setup_next_vdm(int port, int vdo_count, uint32_t *vdm)
+enum dpm_msg_setup_status dp_setup_next_vdm(int port, int *vdo_count,
+ uint32_t *vdm)
{
const struct svdm_amode_data *modep = pd_get_amode_data(port,
TCPCI_MSG_SOP, USB_SID_DISPLAYPORT);
int vdo_count_ret;
- if (vdo_count < VDO_MAX_SIZE)
- return -1;
+ if (*vdo_count < VDO_MAX_SIZE)
+ return MSG_SETUP_ERROR;
switch (dp_state[port]) {
case DP_START:
@@ -223,7 +237,7 @@ int dp_setup_next_vdm(int port, int vdo_count, uint32_t *vdm)
vdm[0] = pd_dfp_enter_mode(port, TCPCI_MSG_SOP,
USB_SID_DISPLAYPORT, 0);
if (vdm[0] == 0)
- return -1;
+ return MSG_SETUP_ERROR;
/* CMDT_INIT is 0, so this is a no-op */
vdm[0] |= VDO_CMDT(CMDT_INIT);
vdm[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP));
@@ -233,26 +247,48 @@ int dp_setup_next_vdm(int port, int vdo_count, uint32_t *vdm)
break;
case DP_ENTER_ACKED:
if (!(modep && modep->opos))
- return -1;
+ return MSG_SETUP_ERROR;
vdo_count_ret = modep->fx->status(port, vdm);
if (vdo_count_ret == 0)
- return -1;
+ return MSG_SETUP_ERROR;
vdm[0] |= PD_VDO_OPOS(modep->opos);
vdm[0] |= VDO_CMDT(CMDT_INIT);
vdm[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP));
break;
case DP_STATUS_ACKED:
if (!(modep && modep->opos))
- return -1;
+ return MSG_SETUP_ERROR;
+
+ if (!get_dp_pin_mode(port))
+ return MSG_SETUP_ERROR;
+ dp_state[port] = DP_PREPARE_CONFIG;
+
+ /*
+ * Place the USB Type-C pins that are to be re-configured to
+ * DisplayPort Configuration into the Safe state. For
+ * USB_PD_MUX_DOCK, the superspeed signals can remain
+ * connected. For USB_PD_MUX_DP_ENABLED, disconnect the
+ * superspeed signals here, before the pins are re-configured
+ * to DisplayPort (in svdm_dp_post_config, when we receive
+ * the config ack).
+ */
+ if (svdm_dp_get_mux_mode(port) == USB_PD_MUX_DP_ENABLED) {
+ usb_mux_set_safe_mode(port);
+ return MSG_SETUP_MUX_WAIT;
+ }
+ /* Fall through if no mux set is needed */
+ case DP_PREPARE_CONFIG:
vdo_count_ret = modep->fx->config(port, vdm);
if (vdo_count_ret == 0)
- return -1;
+ return MSG_SETUP_ERROR;
vdm[0] |= VDO_CMDT(CMDT_INIT);
vdm[0] |= VDO_SVDM_VERS(pd_get_vdo_ver(port, TCPCI_MSG_SOP));
break;
case DP_ENTER_NAKED:
+ DP_SET_FLAG(port, DP_FLAG_RETRY);
+ /* Fall through to send exit mode */
case DP_ACTIVE:
/*
* Called to exit DP alt mode, either when the mode
@@ -265,10 +301,13 @@ int dp_setup_next_vdm(int port, int vdo_count, uint32_t *vdm)
* TODO(b/159856063): Clean up the API to the fx functions.
*/
if (!(modep && modep->opos))
- return -1;
+ return MSG_SETUP_ERROR;
usb_mux_set_safe_mode_exit(port);
-
+ dp_state[port] = DP_PREPARE_EXIT;
+ return MSG_SETUP_MUX_WAIT;
+ case DP_PREPARE_EXIT:
+ /* DPM should call setup only after safe state is set */
vdm[0] = VDO(USB_SID_DISPLAYPORT,
1, /* structured */
CMD_EXIT_MODE);
@@ -282,11 +321,17 @@ int dp_setup_next_vdm(int port, int vdo_count, uint32_t *vdm)
/*
* DP mode is inactive.
*/
- return -1;
+ return MSG_SETUP_ERROR;
default:
CPRINTF("%s called with invalid state %d\n",
__func__, dp_state[port]);
- return -1;
+ return MSG_SETUP_ERROR;
}
- return vdo_count_ret;
+
+ if (vdo_count_ret) {
+ *vdo_count = vdo_count_ret;
+ return MSG_SETUP_SUCCESS;
+ }
+
+ return MSG_SETUP_UNSUPPORTED;
}
diff --git a/common/usbc/tbt_alt_mode.c b/common/usbc/tbt_alt_mode.c
index 73e2796345..5baf9d1a73 100644
--- a/common/usbc/tbt_alt_mode.c
+++ b/common/usbc/tbt_alt_mode.c
@@ -90,6 +90,8 @@ enum tbt_states {
TBT_START = 0,
TBT_ENTER_SOP,
TBT_ACTIVE,
+ /* Set to force Exit mode from non-Active states */
+ TBT_PREPARE_EXIT_MODE,
TBT_EXIT_SOP,
TBT_INACTIVE,
/* Active cable only */
@@ -103,7 +105,6 @@ 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] = CMD_ENTER_MODE,
- [TBT_ACTIVE] = CMD_EXIT_MODE,
[TBT_EXIT_SOP] = CMD_EXIT_MODE,
/* Active cable only */
[TBT_ENTER_SOP_PRIME] = CMD_ENTER_MODE,
@@ -299,27 +300,18 @@ void intel_vdm_acked(int port, enum tcpci_msg_type type, int vdo_count,
/* Indicate to PE layer that alt mode is active */
pd_set_dfp_enter_mode_flag(port, true);
break;
- case TBT_ACTIVE:
+ case TBT_EXIT_SOP:
tbt_prints("exit mode SOP", port);
opos_sop = pd_alt_mode(port, TCPCI_MSG_SOP, USB_VID_INTEL);
/* Clear Thunderbolt related signals */
- pd_dfp_exit_mode(port, TCPCI_MSG_SOP, USB_VID_INTEL, opos_sop);
- set_usb_mux_with_current_data_role(port);
+ if (opos_sop > 0)
+ pd_dfp_exit_mode(port, TCPCI_MSG_SOP, USB_VID_INTEL,
+ opos_sop);
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_exit_done(port);
- }
- break;
- case TBT_EXIT_SOP:
- set_usb_mux_with_current_data_role(port);
- if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE)
- tbt_active_cable_exit_mode(port);
- else {
+ set_usb_mux_with_current_data_role(port);
if (TBT_CHK_FLAG(port, TBT_FLAG_RETRY_DONE))
/* retried enter mode, still failed, give up */
tbt_exit_done(port);
@@ -330,7 +322,6 @@ void intel_vdm_acked(int port, enum tcpci_msg_type type, int vdo_count,
case TBT_EXIT_SOP_PRIME_PRIME:
tbt_prints("exit mode SOP''", port);
tbt_state[port] = TBT_EXIT_SOP_PRIME;
- set_usb_mux_with_current_data_role(port);
break;
case TBT_EXIT_SOP_PRIME:
tbt_prints("exit mode SOP'", port);
@@ -382,26 +373,15 @@ void intel_vdm_naked(int port, enum tcpci_msg_type type, uint8_t vdm_cmd)
* so request to exit the mode first before retrying the enter
* command. This can happen if the EC is restarted
*/
- tbt_state[port] = TBT_EXIT_SOP;
- break;
- case TBT_ACTIVE:
- /* Exit SOP got NAK'ed */
- set_usb_mux_with_current_data_role(port);
- 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;
- TBT_CLR_FLAG(port, TBT_FLAG_RETRY_DONE);
- }
+ tbt_state[port] = TBT_PREPARE_EXIT_MODE;
break;
case TBT_EXIT_SOP:
/* Exit SOP got NAK'ed */
tbt_prints("exit mode SOP failed", port);
- set_usb_mux_with_current_data_role(port);
if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE)
tbt_active_cable_exit_mode(port);
else {
+ set_usb_mux_with_current_data_role(port);
if (TBT_CHK_FLAG(port, TBT_FLAG_RETRY_DONE))
/* Retried enter mode, still failed, give up */
tbt_exit_done(port);
@@ -410,7 +390,6 @@ void intel_vdm_naked(int port, enum tcpci_msg_type type, uint8_t vdm_cmd)
}
break;
case TBT_EXIT_SOP_PRIME_PRIME:
- set_usb_mux_with_current_data_role(port);
tbt_prints("exit mode SOP'' failed", port);
tbt_state[port] = TBT_EXIT_SOP_PRIME;
break;
@@ -458,8 +437,9 @@ static bool tbt_mode_is_supported(int port, int vdo_count)
return true;
}
-int tbt_setup_next_vdm(int port, int vdo_count, uint32_t *vdm,
- enum tcpci_msg_type *tx_type)
+enum dpm_msg_setup_status tbt_setup_next_vdm(int port, int *vdo_count,
+ uint32_t *vdm,
+ enum tcpci_msg_type *tx_type)
{
struct svdm_amode_data *modep;
int vdo_count_ret = 0;
@@ -467,36 +447,39 @@ int tbt_setup_next_vdm(int port, int vdo_count, uint32_t *vdm,
*tx_type = TCPCI_MSG_SOP;
- if (vdo_count < VDO_MAX_SIZE)
- return -1;
+ if (*vdo_count < VDO_MAX_SIZE)
+ return MSG_SETUP_ERROR;
switch (tbt_state[port]) {
case TBT_START:
- if (!tbt_mode_is_supported(port, vdo_count))
- return 0;
+ if (!tbt_mode_is_supported(port, *vdo_count))
+ return MSG_SETUP_UNSUPPORTED;
if (!TBT_CHK_FLAG(port, TBT_FLAG_RETRY_DONE))
tbt_prints("attempt to enter mode", port);
else
tbt_prints("retry to enter mode", port);
+ /*
+ * Enter safe mode before sending Enter mode SOP/SOP'/SOP''
+ * Ref: Tiger Lake Platform PD Controller Interface
+ * Requirements for Integrated USB C, section A.1.2 TBT as DFP.
+ */
+ usb_mux_set_safe_mode(port);
+
cable_mode_resp.raw_value =
pd_get_tbt_mode_vdo(port, TCPCI_MSG_SOP_PRIME);
/* Active cable and LRD cables send Enter Mode SOP' first */
if (get_usb_pd_cable_type(port) == IDH_PTYPE_ACABLE ||
cable_mode_resp.tbt_active_passive == TBT_CABLE_ACTIVE) {
- vdo_count_ret = enter_tbt_compat_mode(port,
- TCPCI_MSG_SOP_PRIME, vdm);
- *tx_type = TCPCI_MSG_SOP_PRIME;
tbt_state[port] = TBT_ENTER_SOP_PRIME;
} else {
/* Passive cable send Enter Mode SOP */
- vdo_count_ret =
- enter_tbt_compat_mode(port, TCPCI_MSG_SOP, vdm);
tbt_state[port] = TBT_ENTER_SOP;
}
- break;
+
+ return MSG_SETUP_MUX_WAIT;
case TBT_ENTER_SOP_PRIME:
vdo_count_ret =
enter_tbt_compat_mode(port, TCPCI_MSG_SOP_PRIME, vdm);
@@ -512,20 +495,30 @@ int tbt_setup_next_vdm(int port, int vdo_count, uint32_t *vdm,
vdo_count_ret =
enter_tbt_compat_mode(port, TCPCI_MSG_SOP, vdm);
break;
- case TBT_EXIT_SOP:
case TBT_ACTIVE:
/*
+ * Since we had successfully entered mode, consider ourselves
+ * done with any retires.
+ */
+ TBT_SET_FLAG(port, TBT_FLAG_RETRY_DONE);
+ /* Fall through */
+ case TBT_PREPARE_EXIT_MODE:
+ /*
* 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.
*/
+ usb_mux_set_safe_mode_exit(port);
+
+ tbt_state[port] = TBT_EXIT_SOP;
+ return MSG_SETUP_MUX_WAIT;
+ case TBT_EXIT_SOP:
+ /* DPM will only call this after safe state set is done */
modep = pd_get_amode_data(port,
TCPCI_MSG_SOP, USB_VID_INTEL);
if (!(modep && modep->opos))
- return -1;
-
- usb_mux_set_safe_mode_exit(port);
+ return MSG_SETUP_ERROR;
vdm[0] = VDO(USB_VID_INTEL, 1, CMD_EXIT_MODE) |
VDO_OPOS(modep->opos) |
@@ -538,9 +531,7 @@ int tbt_setup_next_vdm(int port, int vdo_count, uint32_t *vdm,
modep = pd_get_amode_data(port,
TCPCI_MSG_SOP_PRIME, USB_VID_INTEL);
if (!(modep && modep->opos))
- return -1;
-
- usb_mux_set_safe_mode_exit(port);
+ return MSG_SETUP_ERROR;
vdm[0] = VDO(USB_VID_INTEL, 1, CMD_EXIT_MODE) |
VDO_OPOS(modep->opos) |
@@ -554,9 +545,7 @@ int tbt_setup_next_vdm(int port, int vdo_count, uint32_t *vdm,
modep = pd_get_amode_data(port,
TCPCI_MSG_SOP_PRIME, USB_VID_INTEL);
if (!(modep && modep->opos))
- return -1;
-
- usb_mux_set_safe_mode_exit(port);
+ return MSG_SETUP_ERROR;
vdm[0] = VDO(USB_VID_INTEL, 1, CMD_EXIT_MODE) |
VDO_OPOS(modep->opos) |
@@ -568,12 +557,17 @@ int tbt_setup_next_vdm(int port, int vdo_count, uint32_t *vdm,
break;
case TBT_INACTIVE:
/* Thunderbolt mode is inactive */
- return 0;
+ return MSG_SETUP_UNSUPPORTED;
default:
CPRINTF("%s called with invalid state %d\n",
__func__, tbt_state[port]);
- return -1;
+ return MSG_SETUP_ERROR;
+ }
+
+ if (vdo_count_ret) {
+ *vdo_count = vdo_count_ret;
+ return MSG_SETUP_SUCCESS;
}
- return vdo_count_ret;
+ return MSG_SETUP_UNSUPPORTED;
}
diff --git a/common/usbc/usb_mode.c b/common/usbc/usb_mode.c
index b9dc4973bc..7a79ec7a7b 100644
--- a/common/usbc/usb_mode.c
+++ b/common/usbc/usb_mode.c
@@ -21,6 +21,7 @@
#include "usb_pd_dpm.h"
#include "usb_pd_tcpm.h"
#include "usb_pe_sm.h"
+#include "usb_tbt_alt_mode.h"
#include "usbc_ppc.h"
#ifdef CONFIG_COMMON_RUNTIME
@@ -122,7 +123,9 @@ void usb4_exit_mode_request(int port)
{
usb4_state[port] = USB4_START;
usb_mux_set_safe_mode_exit(port);
- set_usb_mux_with_current_data_role(port);
+ /* If TBT mode is active, leave safe state for mode exit VDMs */
+ if (!tbt_is_active(port))
+ set_usb_mux_with_current_data_role(port);
}
void enter_usb_init(int port)
diff --git a/common/usbc/usb_pd_dpm.c b/common/usbc/usb_pd_dpm.c
index eb2dfc52c0..36cfdf0f75 100644
--- a/common/usbc/usb_pd_dpm.c
+++ b/common/usbc/usb_pd_dpm.c
@@ -18,9 +18,11 @@
#include "tcpm/tcpm.h"
#include "usb_dp_alt_mode.h"
#include "usb_mode.h"
+#include "usb_mux.h"
#include "usb_pd.h"
#include "usb_pd_dpm.h"
#include "usb_pd_tcpm.h"
+#include "usb_pd_pdo.h"
#include "usb_tbt_alt_mode.h"
#ifdef CONFIG_COMMON_RUNTIME
@@ -234,6 +236,7 @@ static void dpm_attempt_mode_entry(int port)
enum tcpci_msg_type tx_type = TCPCI_MSG_SOP;
bool enter_mode_requested =
IS_ENABLED(CONFIG_USB_PD_REQUIRE_AP_MODE_ENTRY) ? false : true;
+ enum dpm_msg_setup_status status = MSG_SETUP_UNSUPPORTED;
if (pd_get_data_role(port) != PD_ROLE_DFP) {
if (DPM_CHK_FLAG(port, DPM_FLAG_ENTER_DP |
@@ -276,6 +279,14 @@ static void dpm_attempt_mode_entry(int port)
return;
}
+ /*
+ * If muxes are still settling, then wait on our next VDM. We must
+ * ensure we correctly sequence actions such as USB safe state with TBT
+ * entry or DP configuration.
+ */
+ if (IS_ENABLED(CONFIG_USBC_SS_MUX) && !usb_mux_set_completed(port))
+ return;
+
/* Check if port, port partner and cable support USB4. */
if (IS_ENABLED(CONFIG_USB_PD_USB4) &&
board_is_tbt_usb4_port(port) &&
@@ -287,8 +298,9 @@ static void dpm_attempt_mode_entry(int port)
* cable and USB4 mode with the port partner.
*/
if (tbt_cable_entry_required_for_usb4(port)) {
- vdo_count = tbt_setup_next_vdm(port,
- ARRAY_SIZE(vdm), vdm, &tx_type);
+ vdo_count = ARRAY_SIZE(vdm);
+ status = tbt_setup_next_vdm(port, &vdo_count, vdm,
+ &tx_type);
} else {
pd_dpm_request(port, DPM_REQUEST_ENTER_USB);
return;
@@ -302,24 +314,32 @@ static void dpm_attempt_mode_entry(int port)
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);
+ vdo_count = ARRAY_SIZE(vdm);
+ status = tbt_setup_next_vdm(port, &vdo_count, vdm,
+ &tx_type);
}
/* If not, check if they support DisplayPort alt mode. */
- if (vdo_count == 0 && !DPM_CHK_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE) &&
+ if (status == MSG_SETUP_UNSUPPORTED &&
+ !DPM_CHK_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE) &&
pd_is_mode_discovered_for_svid(port, TCPCI_MSG_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);
+ vdo_count = ARRAY_SIZE(vdm);
+ status = dp_setup_next_vdm(port, &vdo_count, vdm);
}
+ /* Not ready to send a VDM, check again next cycle */
+ if (status == MSG_SETUP_MUX_WAIT)
+ return;
+
/*
* 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_CHK_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE)) {
+ if (status != MSG_SETUP_SUCCESS &&
+ !DPM_CHK_FLAG(port, DPM_FLAG_MODE_ENTRY_DONE)) {
if (enter_mode_requested) {
/*
* TODO(b/168030639): Notify the AP that mode entry
@@ -335,7 +355,7 @@ static void dpm_attempt_mode_entry(int port)
return;
}
- if (vdo_count < 0) {
+ if (status != MSG_SETUP_SUCCESS) {
dpm_set_mode_entry_done(port);
CPRINTS("C%d: Couldn't construct alt mode VDM", port);
return;
@@ -355,8 +375,9 @@ static void dpm_attempt_mode_entry(int port)
static void dpm_attempt_mode_exit(int port)
{
- uint32_t vdm = 0;
- int vdo_count = 0;
+ uint32_t vdm[VDO_MAX_SIZE];
+ int vdo_count = ARRAY_SIZE(vdm);
+ enum dpm_msg_setup_status status = MSG_SETUP_ERROR;
enum tcpci_msg_type tx_type = TCPCI_MSG_SOP;
if (IS_ENABLED(CONFIG_USB_PD_USB4) &&
@@ -364,6 +385,15 @@ static void dpm_attempt_mode_exit(int port)
CPRINTS("C%d: USB4 teardown", port);
usb4_exit_mode_request(port);
}
+
+ /*
+ * If muxes are still settling, then wait on our next VDM. We must
+ * ensure we correctly sequence actions such as USB safe state with TBT
+ * or DP mode exit.
+ */
+ if (IS_ENABLED(CONFIG_USBC_SS_MUX) && !usb_mux_set_completed(port))
+ return;
+
if (IS_ENABLED(CONFIG_USB_PD_TBT_COMPAT_MODE) &&
tbt_is_active(port)) {
/*
@@ -373,18 +403,21 @@ static void dpm_attempt_mode_exit(int port)
*/
CPRINTS("C%d: TBT teardown", port);
tbt_exit_mode_request(port);
- vdo_count = tbt_setup_next_vdm(port, VDO_MAX_SIZE, &vdm,
- &tx_type);
+ status = tbt_setup_next_vdm(port, &vdo_count, vdm, &tx_type);
} else if (dp_is_active(port)) {
CPRINTS("C%d: DP teardown", port);
- vdo_count = dp_setup_next_vdm(port, VDO_MAX_SIZE, &vdm);
+ status = dp_setup_next_vdm(port, &vdo_count, vdm);
} else {
/* Clear exit mode request */
dpm_clear_mode_exit_request(port);
return;
}
- if (!pd_setup_vdm_request(port, tx_type, &vdm, vdo_count)) {
+ /* This covers error, wait mux, and unsupported cases */
+ if (status != MSG_SETUP_SUCCESS)
+ return;
+
+ if (!pd_setup_vdm_request(port, tx_type, vdm, vdo_count)) {
dpm_clear_mode_exit_request(port);
return;
}
@@ -467,7 +500,7 @@ static void balance_source_ports(void)
uint32_t removed_ports, new_ports;
static bool deferred_waiting;
- if (task_get_current() == TASK_ID_HOOKS)
+ if (in_deferred_context())
deferred_waiting = false;
/*
diff --git a/common/usbc/usb_pd_timer.c b/common/usbc/usb_pd_timer.c
index 67a574904f..97aa699737 100644
--- a/common/usbc/usb_pd_timer.c
+++ b/common/usbc/usb_pd_timer.c
@@ -4,32 +4,54 @@
*/
#include "assert.h"
+#include "atomic.h"
#include "common.h"
#include "console.h"
#include "limits.h"
+#include "math_util.h"
#include "system.h"
#include "usb_pd_timer.h"
#include "usb_tc_sm.h"
#define MAX_PD_PORTS CONFIG_USB_PD_PORT_MAX_COUNT
#define MAX_PD_TIMERS PD_TIMER_COUNT
-#define PD_TIMERS_ALL_MASK ((uint32_t)(((uint64_t)1 << PD_TIMER_COUNT) - 1))
+#define PD_TIMERS_ALL_MASK (UINT64_MAX >> (64 - PD_TIMER_COUNT))
#define MAX_EXPIRE (0x7FFFFFFF)
#define NO_TIMEOUT (-1)
#define EXPIRE_NOW (0)
-#define PD_SET_ACTIVE(p, m) atomic_or(&timer_active[p], (m))
-#define PD_CLR_ACTIVE(p, m) atomic_clear_bits(&timer_active[p], (m))
-#define PD_CHK_ACTIVE(p, m) (timer_active[p] & (m))
-
-#define PD_SET_DISABLED(p, m) atomic_or(&timer_disabled[p], (m))
-#define PD_CLR_DISABLED(p, m) atomic_clear_bits(&timer_disabled[p], (m))
-#define PD_CHK_DISABLED(p, m) (timer_disabled[p] & (m))
-
-static uint32_t timer_active[MAX_PD_PORTS];
-static uint32_t timer_disabled[MAX_PD_PORTS];
+#define PD_SET_ACTIVE(p, m) pd_timer_atomic_op( \
+ atomic_or, \
+ timer_active[p], \
+ (m))
+#define PD_CLR_ACTIVE(p, m) pd_timer_atomic_op( \
+ atomic_clear_bits, \
+ timer_active[p], \
+ (m))
+#define PD_CHK_ACTIVE(p, m) ((timer_active[p][0] & ((m) >> 32)) | \
+ (timer_active[p][1] & (m)))
+
+#define PD_SET_DISABLED(p, m) pd_timer_atomic_op( \
+ atomic_or, \
+ timer_disabled[p], \
+ (m))
+#define PD_CLR_DISABLED(p, m) pd_timer_atomic_op( \
+ atomic_clear_bits, \
+ timer_disabled[p], \
+ (m))
+#define PD_CHK_DISABLED(p, m) ((timer_disabled[p][0] & ((m) >> 32)) | \
+ (timer_disabled[p][1] & (m)))
+
+#define TIMER_FIELD_NUM_UINT32S 2
+
+test_mockable_static
+uint32_t timer_active[MAX_PD_PORTS][TIMER_FIELD_NUM_UINT32S];
+test_mockable_static
+uint32_t timer_disabled[MAX_PD_PORTS][TIMER_FIELD_NUM_UINT32S];
static uint64_t timer_expires[MAX_PD_PORTS][MAX_PD_TIMERS];
+BUILD_ASSERT(sizeof(timer_active[0]) * CHAR_BIT >= PD_TIMER_COUNT);
+BUILD_ASSERT(sizeof(timer_disabled[0]) * CHAR_BIT >= PD_TIMER_COUNT);
/*
* CONFIG_CMD_PD_TIMER debug variables
@@ -82,9 +104,52 @@ __maybe_unused static __const_data const char * const pd_timer_names[] = {
* already and will always return that it is still expired. This timer state
* will not adjust the task scheduling timeout value.
*/
+
+/*
+ * Performs an atomic operation on a PD timer bit field. Atomic operations
+ * require 32-bit operands, but there are more than 32 timers, so choose the
+ * correct operand and modify the mask accordingly.
+ *
+ * @param op Atomic operation function to call
+ * @param timer_field Array of timer fields to operate on
+ * @param mask_val 64-bit mask to apply to the timer field
+ */
+test_mockable_static void pd_timer_atomic_op(
+ atomic_val_t (*op)(atomic_t*, atomic_val_t),
+ uint32_t *const timer_field, const uint64_t mask_val)
+{
+ uint32_t *atomic_timer_field;
+ union mask64_t {
+ struct {
+#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+ uint32_t lo;
+ uint32_t hi;
+#elif (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+ uint32_t hi;
+ uint32_t lo;
+#endif
+ };
+ uint64_t val;
+ } mask;
+
+ /*
+ * High-order mask bits correspond to field [0]. Low-order mask bits
+ * correspond to field [1].
+ */
+ mask.val = mask_val;
+ if (mask.hi) {
+ atomic_timer_field = timer_field;
+ (void)op(atomic_timer_field, mask.hi);
+ }
+ if (mask.lo) {
+ atomic_timer_field = timer_field + 1;
+ (void)op(atomic_timer_field, mask.lo);
+ }
+}
+
static void pd_timer_inactive(int port, enum pd_task_timer timer)
{
- uint32_t mask = 1 << timer;
+ uint64_t mask = bitmask_uint64(timer);
if (PD_CHK_ACTIVE(port, mask)) {
PD_CLR_ACTIVE(port, mask);
@@ -97,14 +162,14 @@ static void pd_timer_inactive(int port, enum pd_task_timer timer)
static bool pd_timer_is_active(int port, enum pd_task_timer timer)
{
- uint32_t mask = 1 << timer;
+ uint64_t mask = bitmask_uint64(timer);
return PD_CHK_ACTIVE(port, mask);
}
static bool pd_timer_is_inactive(int port, enum pd_task_timer timer)
{
- uint32_t mask = 1 << timer;
+ uint64_t mask = bitmask_uint64(timer);
return !PD_CHK_ACTIVE(port, mask) && !PD_CHK_DISABLED(port, mask);
}
@@ -123,7 +188,7 @@ void pd_timer_init(int port)
void pd_timer_enable(int port, enum pd_task_timer timer, uint32_t expires_us)
{
- uint32_t mask = 1 << timer;
+ uint64_t mask = bitmask_uint64(timer);
if (!PD_CHK_ACTIVE(port, mask)) {
PD_SET_ACTIVE(port, mask);
@@ -140,7 +205,7 @@ void pd_timer_enable(int port, enum pd_task_timer timer, uint32_t expires_us)
void pd_timer_disable(int port, enum pd_task_timer timer)
{
- uint32_t mask = 1 << timer;
+ uint64_t mask = bitmask_uint64(timer);
if (PD_CHK_ACTIVE(port, mask)) {
PD_CLR_ACTIVE(port, mask);
@@ -179,7 +244,7 @@ void pd_timer_disable_range(int port, enum pd_timer_range range)
bool pd_timer_is_disabled(int port, enum pd_task_timer timer)
{
- uint32_t mask = 1 << timer;
+ uint64_t mask = bitmask_uint64(timer);
return PD_CHK_DISABLED(port, mask);
}
diff --git a/common/usbc/usb_pe_drp_sm.c b/common/usbc/usb_pe_drp_sm.c
index c86819d0f7..431dcdc9af 100644
--- a/common/usbc/usb_pe_drp_sm.c
+++ b/common/usbc/usb_pe_drp_sm.c
@@ -1373,9 +1373,8 @@ static void pe_clear_port_data(int port)
pd_set_src_caps(port, 0, NULL);
pe_set_snk_caps(port, 0, NULL);
- /* Clear any stored modes and discovery data */
+ /* Clear any stored discovery data, but leave modes for alt mode exit */
pd_dfp_discovery_init(port);
- pd_dfp_mode_init(port);
dpm_remove_sink(port);
dpm_remove_source(port);
diff --git a/common/usbc/usb_tc_drp_acc_trysrc_sm.c b/common/usbc/usb_tc_drp_acc_trysrc_sm.c
index 182ea686ec..3fa9528699 100644
--- a/common/usbc/usb_tc_drp_acc_trysrc_sm.c
+++ b/common/usbc/usb_tc_drp_acc_trysrc_sm.c
@@ -700,9 +700,11 @@ __maybe_unused static void tc_enable_try_src(int en)
}
/*
- * Exit all modes due to a detach event
+ * Exit all modes due to a detach event or hard reset
+ *
* Note: this skips the ExitMode VDM steps in the PE because it is assumed the
- * partner is not present to receive them, and the PE will no longer be running.
+ * partner is not present to receive them, and the PE will no longer be running,
+ * or we've forced an abrupt mode exit through a hard reset.
*/
static void tc_set_modes_exit(int port)
{
@@ -1304,6 +1306,13 @@ static bool tc_perform_src_hard_reset(int port)
/* Set role to DFP */
tc_set_data_role(port, PD_ROLE_DFP);
+ /*
+ * USB PD Rev 3.0 Ver 2.0 6.8.3.2: "A Hard Reset Shall cause
+ * all Active Modes to be exited by both Port Partners and any
+ * Cable Plugs"
+ */
+ tc_set_modes_exit(port);
+
tc[port].ps_reset_state = PS_STATE1;
pd_timer_enable(port, TC_TIMER_TIMEOUT, PD_T_SRC_RECOVER);
return false;
@@ -1349,6 +1358,13 @@ static bool tc_perform_snk_hard_reset(int port)
tc_set_data_role(port, PD_ROLE_UFP);
/*
+ * USB PD Rev 3.0 Ver 2.0 6.8.3.2: "A Hard Reset Shall cause
+ * all Active Modes to be exited by both Port Partners and any
+ * Cable Plugs"
+ */
+ tc_set_modes_exit(port);
+
+ /*
* When VCONN is supported, the Hard Reset Shall cause
* the Port with the Rd resistor asserted to turn off
* VCONN.
@@ -1574,9 +1590,9 @@ void tc_state_init(int port)
}
/*
- * If this is non-EFS2 device, battery is not present and EC RO doesn't
- * keep power-on reset flag after reset caused by H1, then don't apply
- * CC open because it will cause brown out.
+ * If this is non-EFS2 device, battery is not present or at some minimum
+ * voltage and EC RO doesn't keep power-on reset flag after reset caused
+ * by H1, then don't apply CC open because it will cause brown out.
*
* Please note that we are checking if CONFIG_BOARD_RESET_AFTER_POWER_ON
* is defined now, but actually we need to know if it was enabled in
@@ -1585,7 +1601,7 @@ void tc_state_init(int port)
*/
if (!IS_ENABLED(CONFIG_BOARD_RESET_AFTER_POWER_ON) &&
!IS_ENABLED(CONFIG_VBOOT_EFS2) && IS_ENABLED(CONFIG_BATTERY) &&
- (battery_is_present() == BP_NO)) {
+ !pd_is_battery_capable()) {
first_state = TC_UNATTACHED_SNK;
}
@@ -1745,8 +1761,14 @@ void tc_event_check(int port, int evt)
}
}
- if (evt & PD_EVENT_UPDATE_DUAL_ROLE)
+ if (evt & PD_EVENT_UPDATE_DUAL_ROLE) {
+ /* If TCPC is idle, start the wake process */
+ if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER) &&
+ get_state_tc(port) == TC_LOW_POWER_MODE)
+ tcpm_wake_low_power_mode(port);
+
pd_update_dual_role_config(port);
+ }
}
/*
@@ -1893,11 +1915,14 @@ __maybe_unused static void handle_new_power_state(int port)
/*
* If the sink port was sourcing Vconn, and can no longer, request a
- * hard reset on this port to restore Vconn to the source.
+ * hard reset on this port to restore Vconn to the source. If we do not
+ * have sufficient battery to withstand Vbus loss, then continue with
+ * the inconsistent Vconn state in order to keep the board powered.
*/
if (IS_ENABLED(CONFIG_USB_PE_SM)) {
if (tc_is_vconn_src(port) && tc_is_attached_snk(port) &&
- !pd_check_vconn_swap(port))
+ !pd_check_vconn_swap(port) &&
+ pd_is_battery_capable())
pd_dpm_request(port, DPM_REQUEST_HARD_RESET_SEND);
}