From 9c2269ef66bb3001b42b78cdd67200a9fd871eaa Mon Sep 17 00:00:00 2001 From: Sam Hurst Date: Fri, 8 Nov 2019 13:48:55 -0800 Subject: 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 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1906993 Commit-Queue: Denis Brockus Reviewed-by: Denis Brockus Reviewed-by: Jett Rink --- common/usbc/usb_pe_drp_sm.c | 16 +- common/usbc/usb_tc_drp_acc_trysrc_sm.c | 419 +++++++++++++++++++++++++-------- include/usb_pe_sm.h | 7 + 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 */ -- cgit v1.2.1