summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Hurst <shurst@google.com>2019-11-08 13:48:55 -0800
committerCommit Bot <commit-bot@chromium.org>2019-11-22 23:15:44 +0000
commit9c2269ef66bb3001b42b78cdd67200a9fd871eaa (patch)
treef6ca81a23966d8eee8cba58c50eab29b9f1aa06d
parent044f15584125158c867f581004551b7d4e82b9ac (diff)
downloadchrome-ec-9c2269ef66bb3001b42b78cdd67200a9fd871eaa.tar.gz
TCPMv2: ServoV4 is not recognized appropriately
Configure the port as a SNK with PD in DebugAccessory.SNK state BUG=chromium:1020752 BRANCH=none TEST=make -j buildall Manual Test: 1: Connect Servo v4 with NeckTek charger pluged in DUT power port The DUT negotiates to 20V, and starts charging. Change-Id: Id44d566024b5016965f996435d11befdc1c53e98 Signed-off-by: Sam Hurst <shurst@google.com> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1906993 Commit-Queue: Denis Brockus <dbrockus@chromium.org> Reviewed-by: Denis Brockus <dbrockus@chromium.org> Reviewed-by: Jett Rink <jettrink@chromium.org>
-rw-r--r--common/usbc/usb_pe_drp_sm.c16
-rw-r--r--common/usbc/usb_tc_drp_acc_trysrc_sm.c419
-rw-r--r--include/usb_pe_sm.h7
3 files changed, 347 insertions, 95 deletions
diff --git a/common/usbc/usb_pe_drp_sm.c b/common/usbc/usb_pe_drp_sm.c
index 727e1cd220..36921f7e73 100644
--- a/common/usbc/usb_pe_drp_sm.c
+++ b/common/usbc/usb_pe_drp_sm.c
@@ -99,6 +99,10 @@
#define PE_FLAGS_FAST_ROLE_SWAP_ENABLED BIT(21)
/* Flag to note TCPC passed on FRS signal from port partner */
#define PE_FLAGS_FAST_ROLE_SWAP_SIGNALED BIT(22)
+/* For PD2.0, triggers a DR SWAP from UFP to DFP before sending a DiscID msg */
+#define PE_FLAGS_DR_SWAP_TO_DFP BIT(23)
+/* FLAG to track if port partner is dualrole capable */
+#define PE_FLAGS_PORT_PARTNER_IS_DUALROLE BIT(24)
/* 6.7.3 Hard Reset Counter */
#define N_HARD_RESET_COUNT 2
@@ -899,13 +903,21 @@ static void pe_update_pdo_flags(int port, uint32_t pdo)
* charging white-list.
*/
if (!(pdo & PDO_FIXED_DUAL_ROLE) || (pdo & PDO_FIXED_EXTERNAL) ||
- charge_whitelisted)
+ charge_whitelisted) {
+ PE_CLR_FLAG(port, PE_FLAGS_PORT_PARTNER_IS_DUALROLE);
charge_manager_update_dualrole(port, CAP_DEDICATED);
- else
+ } else {
+ PE_SET_FLAG(port, PE_FLAGS_PORT_PARTNER_IS_DUALROLE);
charge_manager_update_dualrole(port, CAP_DUALROLE);
+ }
#endif
}
+int pd_is_port_partner_dualrole(int port)
+{
+ return PE_CHK_FLAG(port, PE_FLAGS_PORT_PARTNER_IS_DUALROLE);
+}
+
int pd_board_check_request(uint32_t rdo, int pdo_cnt)
{
int idx = RDO_POS(rdo);
diff --git a/common/usbc/usb_tc_drp_acc_trysrc_sm.c b/common/usbc/usb_tc_drp_acc_trysrc_sm.c
index 9ec21cdd26..6aa39b520b 100644
--- a/common/usbc/usb_tc_drp_acc_trysrc_sm.c
+++ b/common/usbc/usb_tc_drp_acc_trysrc_sm.c
@@ -370,11 +370,14 @@ void pd_request_power_swap(int port)
{
if (IS_ENABLED(CONFIG_USB_PE_SM)) {
/*
- * Must be in Attached.SRC or Attached.SNK when this function
- * is called
+ * Must be in Attached.SRC, Attached.SNK, UnorientedDbgAcc.SRC,
+ * or DbgAcc.SNK, when this function is called.
*/
if (get_state_tc(port) == TC_ATTACHED_SRC ||
- get_state_tc(port) == TC_ATTACHED_SNK) {
+ get_state_tc(port) == TC_ATTACHED_SNK ||
+ get_state_tc(port) == TC_DBG_ACC_SNK ||
+ get_state_tc(port) ==
+ TC_UNORIENTED_DBG_ACC_SRC) {
TC_SET_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS);
}
}
@@ -601,7 +604,8 @@ void tc_disc_ident_complete(int port)
void tc_snk_power_off(int port)
{
- if (get_state_tc(port) == TC_ATTACHED_SNK) {
+ if (get_state_tc(port) == TC_ATTACHED_SNK ||
+ get_state_tc(port) == TC_DBG_ACC_SNK) {
TC_SET_FLAG(port, TC_FLAGS_POWER_OFF_SNK);
sink_stop_drawing_current(port);
}
@@ -617,7 +621,8 @@ int tc_src_power_on(int port)
void tc_src_power_off(int port)
{
- if (get_state_tc(port) == TC_ATTACHED_SRC) {
+ if (get_state_tc(port) == TC_ATTACHED_SRC ||
+ get_state_tc(port) == TC_UNORIENTED_DBG_ACC_SRC) {
/* Remove VBUS */
pd_power_supply_reset(port);
@@ -710,6 +715,70 @@ void pd_prepare_sysjump(void)
}
#endif
+#ifdef CONFIG_USB_PE_SM
+static void tc_perform_src_hard_reset(int port)
+{
+ switch (tc[port].ps_reset_state) {
+ case PS_STATE0:
+ /* Remove VBUS */
+ tc_src_power_off(port);
+
+ /* Turn off VCONN */
+ set_vconn(port, 0);
+
+ /* Set role to DFP */
+ tc_set_data_role(port, PD_ROLE_DFP);
+
+ tc[port].ps_reset_state = PS_STATE1;
+ tc[port].timeout = get_time().val + PD_T_SRC_RECOVER;
+ return;
+ case PS_STATE1:
+ /* Enable VBUS */
+ pd_set_power_supply_ready(port);
+
+ /* Turn off VCONN */
+ set_vconn(port, 1);
+
+ tc[port].ps_reset_state = PS_STATE3;
+ tc[port].timeout = get_time().val +
+ PD_POWER_SUPPLY_TURN_ON_DELAY;
+ return;
+ case PS_STATE2:
+ case PS_STATE3:
+ /* Tell Policy Engine Hard Reset is complete */
+ pe_ps_reset_complete(port);
+
+ TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET);
+ tc[port].ps_reset_state = PS_STATE0;
+ return;
+ }
+}
+#endif
+
+static void tc_perform_snk_hard_reset(int port)
+{
+ tc_set_data_role(port, PD_ROLE_UFP);
+
+ /* Clear the input current limit */
+ sink_stop_drawing_current(port);
+
+ /*
+ * When VCONN is supported, the Hard Reset Shall cause
+ * the Port with the Rd resistor asserted to turn off
+ * VCONN.
+ */
+#ifdef CONFIG_USBC_VCONN
+ if (TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON))
+ set_vconn(port, 0);
+#endif
+
+ /*
+ * Inform policy engine that power supply
+ * reset is complete
+ */
+ pe_ps_reset_complete(port);
+}
+
void tc_start_error_recovery(int port)
{
/*
@@ -1956,7 +2025,9 @@ static void tc_attached_snk_entry(const int port)
cc1, cc2);
typec_set_input_current_limit(port,
tc[port].typec_curr, TYPE_C_VOLTAGE);
- charge_manager_update_dualrole(port, CAP_DEDICATED);
+ charge_manager_update_dualrole(port,
+ pd_is_port_partner_dualrole(port) ?
+ CAP_DUALROLE : CAP_DEDICATED);
}
}
@@ -1978,24 +2049,7 @@ static void tc_attached_snk_run(const int port)
*/
if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET)) {
TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET);
-
- tc_set_data_role(port, PD_ROLE_UFP);
- /* Clear the input current limit */
- sink_stop_drawing_current(port);
-
- /*
- * When VCONN is supported, the Hard Reset Shall cause
- * the Port with the Rd resistor asserted to turn off
- * VCONN.
- */
- if (TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON))
- set_vconn(port, 0);
-
- /*
- * Inform policy engine that power supply
- * reset is complete
- */
- pe_ps_reset_complete(port);
+ tc_perform_snk_hard_reset(port);
}
/*
@@ -2113,41 +2167,158 @@ static void tc_attached_snk_exit(const int port)
* UnorientedDebugAccessory.SRC
*
* Super State Entry Actions:
- * Vconn Off
* Place Rp on CC
* Set power role to SOURCE
*/
static void tc_unoriented_dbg_acc_src_entry(const int port)
{
+ enum tcpc_cc_voltage_status cc1, cc2;
+
print_current_state(port);
- /* Enable VBUS */
- pd_set_power_supply_ready(port);
+ /* Run function relies on timeout being 0 or meaningful */
+ tc[port].timeout = 0;
+
+ /* Clear Low Power Mode Request */
+ TC_CLR_FLAG(port, TC_FLAGS_LPM_REQUESTED);
+
+ if (TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) {
+ /* Enable VBUS */
+ pd_set_power_supply_ready(port);
+
+ /*
+ * Maintain VCONN supply state, whether ON or OFF, and its
+ * data role / usb mux connections.
+ */
+ } else {
+ /* Get connector orientation */
+ tcpm_get_cc(port, &cc1, &cc2);
+ tc[port].polarity = (cc1 != TYPEC_CC_VOLT_RD);
+ set_polarity(port, tc[port].polarity);
- /* Any board specific unoriented debug setup should be added below */
+ /*
+ * Initial data role for sink is DFP
+ * This also sets the usb mux
+ */
+ tc_set_data_role(port, PD_ROLE_DFP);
+
+ /* Enable VBUS */
+ if (pd_set_power_supply_ready(port)) {
+ if (IS_ENABLED(CONFIG_USBC_SS_MUX))
+ usb_mux_set(port, TYPEC_MUX_NONE,
+ USB_SWITCH_DISCONNECT, tc[port].polarity);
+ }
+
+#ifdef CONFIG_USB_PE_SM
+ tc[port].pd_enable = 0;
+ tc[port].timeout = get_time().val +
+ PD_POWER_SUPPLY_TURN_ON_DELAY;
+#endif
+ }
+
+ /* Inform PPC that a sink is connected. */
+ if (IS_ENABLED(CONFIG_USBC_PPC))
+ ppc_sink_is_connected(port, 1);
}
static void tc_unoriented_dbg_acc_src_run(const int port)
{
enum tcpc_cc_voltage_status cc1, cc2;
+ enum pd_cc_states new_cc_state;
+
+#ifdef CONFIG_USB_PE_SM
+ /* Enable PD communications after power supply has fully turned on */
+ if (tc[port].pd_enable == 0 &&
+ get_time().val > tc[port].timeout) {
+
+ tc[port].pd_enable = 1;
+ tc[port].timeout = 0;
+ }
+
+ if (tc[port].pd_enable == 0)
+ return;
+
+ /*
+ * Handle Hard Reset from Policy Engine
+ */
+ if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET)) {
+ if (get_time().val < tc[port].timeout)
+ return;
+
+ /*
+ * This function clears TC_FLAGS_HARD_RESET
+ * when the hard reset is complete.
+ */
+ tc_perform_src_hard_reset(port);
+ }
+#endif
/* Check for connection */
tcpm_get_cc(port, &cc1, &cc2);
+ if (tc[port].polarity)
+ cc1 = cc2;
+
+ if (cc1 == TYPEC_CC_VOLT_OPEN)
+ new_cc_state = PD_CC_NONE;
+ else
+ new_cc_state = PD_CC_UFP_ATTACHED;
+
+ /* Debounce the cc state */
+ if (new_cc_state != tc[port].cc_state) {
+ tc[port].cc_state = new_cc_state;
+ tc[port].cc_debounce = get_time().val + PD_T_SRC_DISCONNECT;
+ }
+
+ if (get_time().val < tc[port].cc_debounce)
+ return;
+
+ if (tc[port].cc_state == PD_CC_NONE &&
+ !TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS) &&
+ !TC_CHK_FLAG(port, TC_FLAGS_DISC_IDENT_IN_PROGRESS)) {
+
+ tc[port].pd_enable = 0;
+ set_state_tc(port, TC_UNATTACHED_SNK);
+ }
+
+#ifdef CONFIG_USB_PE_SM
/*
- * A DRP, the port shall transition to Unattached.SNK when the
- * SRC.Open state is detected on either the CC1 or CC2 pin.
+ * PD swap commands
*/
- if (cc1 == TYPEC_CC_VOLT_OPEN || cc2 == TYPEC_CC_VOLT_OPEN) {
- /* Remove VBUS */
- pd_power_supply_reset(port);
+ if (tc[port].pd_enable) {
+ /*
+ * Power Role Swap Request
+ */
+ if (TC_CHK_FLAG(port, TC_FLAGS_DO_PR_SWAP)) {
+ /* Clear TC_FLAGS_DO_PR_SWAP on exit */
+ return set_state_tc(port, TC_DBG_ACC_SNK);
+ }
- if (IS_ENABLED(CONFIG_CHARGE_MANAGER))
- charge_manager_set_ceil(port, CEIL_REQUESTOR_PD,
- CHARGE_CEIL_NONE);
+ /*
+ * Data Role Swap Request
+ */
+ if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP)) {
+ TC_CLR_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP);
- set_state_tc(port, TC_UNATTACHED_SNK);
+ /* Perform Data Role Swap */
+ tc_set_data_role(port,
+ tc[port].data_role == PD_ROLE_DFP ?
+ PD_ROLE_UFP : PD_ROLE_DFP);
+ }
}
+#endif
+}
+
+static void tc_unoriented_dbg_acc_src_exit(const int port)
+{
+ /*
+ * A port shall cease to supply VBUS within tVBUSOFF of exiting
+ * UnorientedDbg.SRC.
+ */
+ tc_src_power_off(port);
+
+ /* Clear PR swap flag */
+ TC_CLR_FLAG(port, TC_FLAGS_DO_PR_SWAP);
}
/**
@@ -2160,26 +2331,128 @@ static void tc_unoriented_dbg_acc_src_run(const int port)
*/
static void tc_dbg_acc_snk_entry(const int port)
{
+ enum tcpc_cc_voltage_status cc1, cc2;
+
print_current_state(port);
- /*
- * TODO(b/137759869): Board specific debug accessory setup should
- * be add here.
- */
+ if (TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) {
+ /*
+ * Both CC1 and CC2 pins shall be independently terminated to
+ * ground through Rd.
+ */
+ tcpm_set_cc(port, TYPEC_CC_RD);
+
+ /* Change role to sink */
+ tc_set_power_role(port, PD_ROLE_SINK);
+ tcpm_set_msg_header(port, tc[port].power_role,
+ tc[port].data_role);
+
+ /*
+ * Maintain VCONN supply state, whether ON or OFF, and its
+ * data role / usb mux connections.
+ */
+ } else {
+ /* Get connector orientation */
+ tcpm_get_cc(port, &cc1, &cc2);
+ tc[port].polarity = get_snk_polarity(cc1, cc2);
+ set_polarity(port, tc[port].polarity);
+
+ /*
+ * Initial data role for sink is UFP
+ * This also sets the usb mux
+ */
+ tc_set_data_role(port, PD_ROLE_UFP);
+
+ if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) {
+ tc[port].typec_curr =
+ usb_get_typec_current_limit(tc[port].polarity,
+ cc1, cc2);
+ typec_set_input_current_limit(port,
+ tc[port].typec_curr, TYPE_C_VOLTAGE);
+ charge_manager_update_dualrole(port,
+ pd_is_port_partner_dualrole(port) ?
+ CAP_DUALROLE : CAP_DEDICATED);
+ }
+ }
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
}
static void tc_dbg_acc_snk_run(const int port)
{
- if (!pd_is_vbus_present(port)) {
- if (IS_ENABLED(CONFIG_USB_PE_SM) &&
- IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) {
- pd_dfp_exit_mode(port, 0, 0);
+
+ if (!IS_ENABLED(CONFIG_USB_PE_SM)) {
+ /* Detach detection */
+ if (!pd_is_vbus_present(port)) {
+ set_state_tc(port, TC_UNATTACHED_SNK);
+ return;
}
- set_state_tc(port, TC_UNATTACHED_SNK);
+ /* Run Sink Power Sub-State */
+ sink_power_sub_states(port);
+
+ return;
+ }
+
+ /*
+ * Perform Hard Reset
+ */
+ if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET)) {
+ TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET);
+ tc_perform_snk_hard_reset(port);
+ }
+
+ /*
+ * The sink will be powered off during a power role swap but we
+ * don't want to trigger a disconnect
+ */
+ if (!TC_CHK_FLAG(port, TC_FLAGS_POWER_OFF_SNK) &&
+ !TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) {
+ /* Detach detection */
+ if (!pd_is_vbus_present(port)) {
+ if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP))
+ pd_dfp_exit_mode(port, 0, 0);
+
+ set_state_tc(port, TC_UNATTACHED_SNK);
+ return;
+ }
+
+ if (!pe_is_explicit_contract(port))
+ sink_power_sub_states(port);
+ }
+
+ /* PD swap commands */
+
+ /*
+ * Power Role Swap
+ */
+ if (TC_CHK_FLAG(port, TC_FLAGS_DO_PR_SWAP)) {
+ /* Clear PR_SWAP flag in exit */
+ set_state_tc(port, TC_UNORIENTED_DBG_ACC_SRC);
+ return;
+ }
+
+ /*
+ * Data Role Swap
+ */
+ if (TC_CHK_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP)) {
+ TC_CLR_FLAG(port, TC_FLAGS_REQUEST_DR_SWAP);
+
+ /* Perform Data Role Swap */
+ tc_set_data_role(port, tc[port].data_role == PD_ROLE_UFP ?
+ PD_ROLE_DFP : PD_ROLE_UFP);
}
}
+static void tc_dbg_acc_snk_exit(const int port)
+{
+ TC_CLR_FLAG(port, TC_FLAGS_DO_PR_SWAP | TC_FLAGS_POWER_OFF_SNK);
+
+ /* Stop drawing power */
+ sink_stop_drawing_current(port);
+}
+
/**
* Unattached.SRC
*
@@ -2468,51 +2741,11 @@ static void tc_attached_src_run(const int port)
if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET)) {
if (get_time().val < tc[port].timeout)
return;
-
- switch (tc[port].ps_reset_state) {
- case PS_STATE0:
- /* Remove VBUS */
- tc_src_power_off(port);
-
- /* Set role to DFP */
- tc_set_data_role(port, PD_ROLE_DFP);
-
- /* Turn off VCONN */
- if (TC_CHK_FLAG(port, TC_FLAGS_VCONN_ON))
- set_vconn(port, 0);
-
- /* Remove Rp */
- tcpm_set_cc(port, TYPEC_CC_OPEN);
-
- tc[port].ps_reset_state = PS_STATE1;
- tc[port].timeout = get_time().val + PD_T_SRC_RECOVER;
- return;
- case PS_STATE1:
- /* Turn VCONN on before Vbus to meet tVconnON */
- if (IS_ENABLED(CONFIG_USBC_VCONN))
- set_vconn(port, 1);
-
- tc[port].ps_reset_state = PS_STATE2;
- return;
- case PS_STATE2:
- /* Enable VBUS */
- pd_set_power_supply_ready(port);
-
- /* Apply Rp */
- tcpm_set_cc(port, TYPEC_CC_RP);
-
- tc[port].ps_reset_state = PS_STATE3;
- tc[port].timeout = get_time().val +
- PD_POWER_SUPPLY_TURN_ON_DELAY;
- return;
- case PS_STATE3:
- /* Tell Policy Engine Hard Reset is complete */
- pe_ps_reset_complete(port);
-
- TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET);
- tc[port].ps_reset_state = PS_STATE0;
- return;
- }
+ /*
+ * This function clears TC_FLAGS_HARD_RESET
+ * when the hard reset is complete.
+ */
+ tc_perform_src_hard_reset(port);
}
#endif
@@ -3039,8 +3272,6 @@ static void tc_cc_rd_entry(const int port)
* ground through Rd.
*/
tcpm_set_cc(port, TYPEC_CC_RD);
-
-
}
@@ -3241,11 +3472,13 @@ static const struct usb_state tc_states[] = {
[TC_UNORIENTED_DBG_ACC_SRC] = {
.entry = tc_unoriented_dbg_acc_src_entry,
.run = tc_unoriented_dbg_acc_src_run,
+ .exit = tc_unoriented_dbg_acc_src_exit,
.parent = &tc_states[TC_CC_RP],
},
[TC_DBG_ACC_SNK] = {
.entry = tc_dbg_acc_snk_entry,
.run = tc_dbg_acc_snk_run,
+ .exit = tc_dbg_acc_snk_exit,
.parent = &tc_states[TC_CC_RD],
},
[TC_UNATTACHED_SRC] = {
diff --git a/include/usb_pe_sm.h b/include/usb_pe_sm.h
index 2806f0702d..c786a31ebc 100644
--- a/include/usb_pe_sm.h
+++ b/include/usb_pe_sm.h
@@ -163,5 +163,12 @@ int pe_is_explicit_contract(int port);
*/
void pe_dpm_request(int port, enum pe_dpm_request req);
+/*
+ * Return true if port partner is dualrole capable
+ *
+ * @param port USB-C port number
+ */
+int pd_is_port_partner_dualrole(int port);
+
#endif /* __CROS_EC_USB_PE_H */