diff options
author | Sam Hurst <shurst@google.com> | 2019-11-05 18:47:47 +0000 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2019-11-06 21:49:53 +0000 |
commit | 1369d55f5e3cb9f0389c452b054d62fc74255c18 (patch) | |
tree | 4bfeacef7e4ecea4a126f9798b364e1b2f12417b /common | |
parent | bdc0f13235d05c411a3ebd96eeb58f338cbff4da (diff) | |
download | chrome-ec-1369d55f5e3cb9f0389c452b054d62fc74255c18.tar.gz |
Revert "usbc: remove unnecessary tcpc CC reads"
This reverts commit bec03d91bc9f954682c02d122a0500d10cc102c2.
Reason for revert: Some TCPC's bugs will generate an attach event when
nothing is connected, triggering the state machine to enter an attached
state. Example output with nothing connected to the port:
2019-10-28 10:23:20 [0.044393 C1: Unattached.SNK]
2019-10-28 10:23:20 [0.101264 p1: PPC init'd.]
2019-10-28 10:23:20 [0.106488 C1: Unattached.SRC]
2019-10-28 10:23:20 [0.116072 C1: AttachWait.SRC]
2019-10-28 10:23:20 [0.220006 C1: Attached.SRC]
TOT has basic TypeC/PD functionality but issues still exist that will be
fixed in subsequent CLs.
Original change's description:
> usbc: remove unnecessary tcpc CC reads
>
> We only need to read the CC pins when the CC evt is fired otherwise the
> CC pins should have the same value. It is actually incorrect/undesirable
> that our old PD stack queried the tpcp cc driver over i2c so frequently
> for the CC pins status.
>
> Also single source code that interprets the CC lines values into a UFP
> or DFP state.
>
> Lastly, remove extraneous timers for cc debouncing, we only need one.
>
> BRANCH=none
> BUG=none
> TEST=builds
>
> Change-Id: I65baa2e6fb92d7ab5ca12daa76225cd13b9ab974
> Signed-off-by: Jett Rink <jettrink@chromium.org>
> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1825504
> Reviewed-by: Denis Brockus <dbrockus@chromium.org>
> Reviewed-by: Sam Hurst <shurst@google.com>
> Commit-Queue: Denis Brockus <dbrockus@chromium.org>
Bug: none
Change-Id: Ie9bd366dd85df9a33934e06e4208543f6e7aaa9b
Signed-off-by: Sam Hurst <shurst@google.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1900058
Reviewed-by: Aseda Aboagye <aaboagye@chromium.org>
Diffstat (limited to 'common')
-rw-r--r-- | common/usb_common.c | 1 | ||||
-rw-r--r-- | common/usbc/usb_tc_ctvpd_sm.c | 3 | ||||
-rw-r--r-- | common/usbc/usb_tc_drp_acc_trysrc_sm.c | 645 |
3 files changed, 458 insertions, 191 deletions
diff --git a/common/usb_common.c b/common/usb_common.c index 85e81b8042..eab88e0e5e 100644 --- a/common/usb_common.c +++ b/common/usb_common.c @@ -107,7 +107,6 @@ enum pd_cc_states pd_get_cc_state( */ return PD_CC_NONE; } - /* * Zinger implements a board specific usb policy that does not define * PD_MAX_VOLTAGE_MV and PD_OPERATING_POWER_MW. And in turn, does not diff --git a/common/usbc/usb_tc_ctvpd_sm.c b/common/usbc/usb_tc_ctvpd_sm.c index 996b38b6fb..1747a8c0ab 100644 --- a/common/usbc/usb_tc_ctvpd_sm.c +++ b/common/usbc/usb_tc_ctvpd_sm.c @@ -29,9 +29,6 @@ #define SUPPORT_TIMER_RESET_REQUEST 1 #define SUPPORT_TIMER_RESET_COMPLETE 2 -/* Constant used to force an initial debounce cycle */ -#define PD_CC_UNSET -1 - /** * This is the Type-C Port object that contains information needed to * implement a Charge Through VCONN Powered Device. diff --git a/common/usbc/usb_tc_drp_acc_trysrc_sm.c b/common/usbc/usb_tc_drp_acc_trysrc_sm.c index 2accd67d6e..a6c58ce843 100644 --- a/common/usbc/usb_tc_drp_acc_trysrc_sm.c +++ b/common/usbc/usb_tc_drp_acc_trysrc_sm.c @@ -50,7 +50,7 @@ #define TC_FLAGS_PARTNER_EXTPOWER BIT(13) #define TC_FLAGS_PARTNER_DR_DATA BIT(14) #define TC_FLAGS_PARTNER_DR_POWER BIT(15) -#define TC_FLAGS_PARTNER_PD_CAPABLE BIT(16) +#define TC_FLAGS_PARTNER_PD_CAPABLE BIT(16) #define TC_FLAGS_HARD_RESET BIT(17) #define TC_FLAGS_PARTNER_USB_COMM BIT(18) #define TC_FLAGS_PR_SWAP_IN_PROGRESS BIT(19) @@ -112,9 +112,7 @@ static const char * const tc_state_names[] = { /* Generate a compiler error if invalid states are referenced */ #ifndef CONFIG_USB_PD_TRY_SRC -extern int TC_TRY_SRC_UNDEFINED; #define TC_TRY_SRC TC_TRY_SRC_UNDEFINED -extern int TC_TRY_WAIT_SNK_UNDEFINED; #define TC_TRY_WAIT_SNK TC_TRY_WAIT_SNK_UNDEFINED #endif @@ -135,22 +133,39 @@ static struct type_c { uint8_t polarity; /* port flags, see TC_FLAGS_* */ uint32_t flags; + /* event timeout */ + uint64_t evt_timeout; + /* Time a port shall wait before it can determine it is attached */ + uint64_t cc_debounce; + /* + * Time a Sink port shall wait before it can determine it is detached + * due to the potential for USB PD signaling on CC as described in + * the state definitions. + */ + uint64_t pd_debounce; +#ifdef CONFIG_USB_PD_TRY_SRC + /* + * Time a port shall wait before it can determine it is + * re-attached during the try-wait process. + */ + uint64_t try_wait_debounce; +#endif + /* The cc state */ + enum pd_cc_states cc_state; /* Role toggle timer */ uint64_t next_role_swap; /* Generic timer */ uint64_t timeout; +#ifdef CONFIG_USB_PD_TCPC_LOW_POWER /* Time to enter low power mode */ uint64_t low_power_time; /* Tasks to notify after TCPC has been reset */ int tasks_waiting_on_reset; /* Tasks preventing TCPC from entering low power mode */ int tasks_preventing_lpm; - /* The last time the cc1 or cc2 line changed. */ - uint64_t cc_last_change; - /* Current voltage on CC pins */ - enum tcpc_cc_voltage_status cc1, cc2; - /* Interpreted PD state of above cc1 and cc2 lines */ - enum pd_cc_states cc_state; +#endif + /* Voltage on CC pin */ + enum tcpc_cc_voltage_status cc_voltage; /* Type-C current */ typec_current_t typec_curr; /* Type-C current change */ @@ -162,7 +177,6 @@ static struct type_c { } tc[CONFIG_USB_PD_PORT_MAX_COUNT]; /* Port dual-role state */ -static __maybe_unused enum pd_dual_role_states drp_state[CONFIG_USB_PD_PORT_MAX_COUNT] = { [0 ... (CONFIG_USB_PD_PORT_MAX_COUNT - 1)] = CONFIG_USB_PD_INITIAL_DRP_STATE}; @@ -171,19 +185,13 @@ enum pd_dual_role_states drp_state[CONFIG_USB_PD_PORT_MAX_COUNT] = { static void set_vconn(int port, int enable); #endif -/* Forward declare common, private functions */ -static void exit_low_power_mode(int port); -static void handle_device_access(int port); -static void handle_new_power_state(int port); -static int reset_device_and_notify(int port); -static void pd_update_dual_role_config(int port); -static int pd_device_in_low_power(int port); -static void pd_wait_for_wakeup(int port); +#ifdef CONFIG_USB_PE_SM +#ifdef CONFIG_USB_PD_ALT_MODE_DFP /* Tracker for which task is waiting on sysjump prep to finish */ static volatile task_id_t sysjump_task_waiting = TASK_ID_INVALID; +#endif -#ifdef CONFIG_USB_PE_SM /* * 4 entry rw_hash table of type-C devices that AP has firmware updates for. */ @@ -192,15 +200,31 @@ static volatile task_id_t sysjump_task_waiting = TASK_ID_INVALID; static struct ec_params_usb_pd_rw_hash_entry rw_hash_table[RW_HASH_ENTRIES]; #endif +/* Forward declare common, private functions */ +#ifdef CONFIG_USB_PD_TCPC_LOW_POWER +static void exit_low_power_mode(int port); +static void handle_device_access(int port); +static int pd_device_in_low_power(int port); +static void pd_wait_for_wakeup(int port); +static int reset_device_and_notify(int port); +#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ + +#ifdef CONFIG_POWER_COMMON +static void handle_new_power_state(int port); +#endif + +static void pd_update_dual_role_config(int port); #endif /* CONFIG_USB_PE_SM */ /* Forward declare common, private functions */ static void set_state_tc(const int port, const enum usb_tc_state new_state); test_export_static enum usb_tc_state get_state_tc(const int port); +#ifdef CONFIG_USB_PD_TRY_SRC /* Enable variable for Try.SRC states */ -STATIC_IF(CONFIG_USB_PD_TRY_SRC) uint8_t pd_try_src_enable; -STATIC_IF(CONFIG_USB_PD_TRY_SRC) void pd_update_try_source(void); +static uint8_t pd_try_src_enable; +static void pd_update_try_source(void); +#endif static void sink_stop_drawing_current(int port); @@ -718,11 +742,19 @@ test_export_static enum usb_tc_state get_state_tc(const int port) return tc[port].ctx.current - &tc_states[0]; } +/* Get the previous TypeC state. */ +static enum usb_tc_state get_last_state_tc(const int port) +{ + return tc[port].ctx.previous - &tc_states[0]; +} + static void print_current_state(const int port) { CPRINTS("C%d: %s", port, tc_state_names[get_state_tc(port)]); } +#ifdef CONFIG_USB_PE_SM +#ifdef CONFIG_USB_PD_TCPC_LOW_POWER /* This is only called from the PD tasks that owns the port. */ static void exit_low_power_mode(int port) { @@ -735,25 +767,12 @@ static void exit_low_power_mode(int port) else TC_CLR_FLAG(port, TC_FLAGS_LPM_REQUESTED); } +#endif +#endif void tc_event_check(int port, int evt) { - /* Update the cc variables if there was a change */ - if (evt & PD_EVENT_CC) { - enum tcpc_cc_voltage_status cc1, cc2; - - tcpm_get_cc(port, &cc1, &cc2); - if (cc1 != tc[port].cc1 || cc2 != tc[port].cc2) { - tc[port].cc_state = pd_get_cc_state(cc1, cc2); - tc[port].cc1 = cc1; - tc[port].cc2 = cc2; - tc[port].cc_last_change = get_time().val; - } - } - - if (!IS_ENABLED(CONFIG_USB_PE_SM)) - return; - +#ifdef CONFIG_USB_PE_SM if (IS_ENABLED(CONFIG_USB_PD_TCPC_LOW_POWER)) { if (evt & PD_EXIT_LOW_POWER_EVENT_MASK) exit_low_power_mode(port); @@ -762,20 +781,25 @@ void tc_event_check(int port, int evt) handle_device_access(port); } +#ifdef CONFIG_POWER_COMMON if (IS_ENABLED(CONFIG_POWER_COMMON)) { if (evt & PD_EVENT_POWER_STATE_CHANGE) handle_new_power_state(port); } +#endif +#ifdef CONFIG_USB_PD_ALT_MODE_DFP if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) { if (evt & PD_EVENT_SYSJUMP) { pe_exit_dp_mode(port); notify_sysjump_ready(&sysjump_task_waiting); } } +#endif if (evt & PD_EVENT_UPDATE_DUAL_ROLE) pd_update_dual_role_config(port); +#endif } /* @@ -924,6 +948,8 @@ void pd_deferred_resume(int port) } #endif /* CONFIG_USB_PD_DEFERRED_RESUME */ +#ifdef CONFIG_USB_PE_SM + /* This must only be called from the PD task */ static void pd_update_dual_role_config(int port) { @@ -952,6 +978,7 @@ static void pd_update_dual_role_config(int port) } } +#ifdef CONFIG_POWER_COMMON static void handle_new_power_state(int port) { if (IS_ENABLED(CONFIG_POWER_COMMON) && @@ -967,8 +994,8 @@ static void handle_new_power_state(int port) set_usb_mux_with_current_data_role(port); } } +#endif /* CONFIG_POWER_COMMON */ -#ifdef CONFIG_USB_PE_SM /* * HOST COMMANDS */ @@ -1418,6 +1445,7 @@ void pd_handle_overcurrent(int port) } #endif /* defined(CONFIG_USBC_PPC) */ +#ifdef CONFIG_USB_PD_TCPC_LOW_POWER /* 10 ms is enough time for any TCPC transaction to complete. */ #define PD_LPM_DEBOUNCE_US (10 * MSEC) @@ -1598,16 +1626,43 @@ void pd_prevent_low_power_mode(int port, int prevent) else atomic_clear(&tc[port].tasks_preventing_lpm, current_task_mask); } +#endif /* CONFIG_USB_PD_TCPC_LOW_POWER */ static void sink_power_sub_states(int port) { - /* Debounce cc lines for at least Rp value change timer */ - if (get_time().val < tc[port].cc_last_change + PD_T_RP_VALUE_CHANGE) + enum tcpc_cc_voltage_status cc1, cc2, cc; + enum tcpc_cc_voltage_status new_cc_voltage; + + tcpm_get_cc(port, &cc1, &cc2); + + cc = tc[port].polarity ? cc2 : cc1; + + if (cc == TYPEC_CC_VOLT_RP_DEF) + new_cc_voltage = TYPEC_CC_VOLT_RP_DEF; + else if (cc == TYPEC_CC_VOLT_RP_1_5) + new_cc_voltage = TYPEC_CC_VOLT_RP_1_5; + else if (cc == TYPEC_CC_VOLT_RP_3_0) + new_cc_voltage = TYPEC_CC_VOLT_RP_3_0; + else + new_cc_voltage = TYPEC_CC_VOLT_OPEN; + + /* Debounce the cc state */ + if (new_cc_voltage != tc[port].cc_voltage) { + tc[port].cc_voltage = new_cc_voltage; + tc[port].cc_debounce = + get_time().val + PD_T_RP_VALUE_CHANGE; + return; + } + + if (tc[port].cc_debounce == 0 || + get_time().val < tc[port].cc_debounce) return; + tc[port].cc_debounce = 0; + if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { tc[port].typec_curr = usb_get_typec_current_limit( - tc[port].polarity, tc[port].cc1, tc[port].cc2); + tc[port].polarity, cc1, cc2); typec_set_input_current_limit(port, tc[port].typec_curr, TYPE_C_VOLTAGE); @@ -1671,21 +1726,6 @@ static void tc_error_recovery_run(const int port) } } -/* Set the CC resistors to Rd and update the TCPC power role header */ -static void set_rd(const int port) -{ - /* - * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rd. Reset last cc change time. - */ - tcpm_set_cc(port, TYPEC_CC_RD); - tc[port].cc_last_change = get_time().val; - - /* Set power role to sink */ - tc_set_power_role(port, PD_ROLE_SINK); - tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role); -} - /** * Unattached.SNK * @@ -1693,14 +1733,23 @@ static void set_rd(const int port) */ static void tc_unattached_snk_entry(const int port) { - /* Set Rd since we are not in the Rd superstate */ - set_rd(port); + if (get_last_state_tc(port) != TC_UNATTACHED_SRC) + print_current_state(port); if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) charge_manager_update_dualrole(port, CAP_UNKNOWN); + /* + * Indicate that the port is disconnected so the board + * can restore state from any previous data swap. + */ + pd_execute_data_swap(port, PD_ROLE_DISCONNECTED); tc[port].next_role_swap = get_time().val + PD_T_DRP_SNK; + if (IS_ENABLED(CONFIG_USBC_SS_MUX)) + usb_mux_set(port, TYPEC_MUX_NONE, + USB_SWITCH_DISCONNECT, tc[port].polarity); + if (IS_ENABLED(CONFIG_USB_PE_SM)) { tc[port].flags = 0; tc[port].pd_enable = 0; @@ -1709,6 +1758,8 @@ static void tc_unattached_snk_entry(const int port) static void tc_unattached_snk_run(const int port) { + enum tcpc_cc_voltage_status cc1, cc2; + /* * TODO(b/137498392): Add wait before sampling the CC * status after role changes @@ -1723,6 +1774,9 @@ static void tc_unattached_snk_run(const int port) } } + /* Check for connection */ + tcpm_get_cc(port, &cc1, &cc2); + /* * The port shall transition to AttachWait.SNK when a Source * connection is detected, as indicated by the SNK.Rp state @@ -1732,13 +1786,15 @@ static void tc_unattached_snk_run(const int port) * after the state of both CC pins is SNK.Open for * tDRP − dcSRC.DRP ∙ tDRP. */ - if (tc[port].cc_state == PD_CC_DFP_DEBUG_ACC || - tc[port].cc_state == PD_CC_DFP_ATTACHED) + if (cc_is_rp(cc1) || cc_is_rp(cc2)) { /* Connection Detected */ set_state_tc(port, TC_ATTACH_WAIT_SNK); - else if (get_time().val > tc[port].next_role_swap) + } else if (get_time().val > tc[port].next_role_swap) { /* DRP Toggle */ set_state_tc(port, TC_UNATTACHED_SRC); + } + + return; } /** @@ -1752,19 +1808,43 @@ static void tc_unattached_snk_run(const int port) static void tc_attach_wait_snk_entry(const int port) { print_current_state(port); + + tc[port].cc_state = PD_CC_UNSET; } static void tc_attach_wait_snk_run(const int port) { + enum tcpc_cc_voltage_status cc1, cc2; + enum pd_cc_states new_cc_state; + + /* Check for connection */ + tcpm_get_cc(port, &cc1, &cc2); + + if (cc_is_rp(cc1) && cc_is_rp(cc2)) + new_cc_state = PD_CC_DFP_DEBUG_ACC; + else if (cc_is_rp(cc1) || cc_is_rp(cc2)) + new_cc_state = PD_CC_DFP_ATTACHED; + else + new_cc_state = PD_CC_NONE; + + /* Debounce the cc state */ + if (new_cc_state != tc[port].cc_state) { + tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE; + tc[port].pd_debounce = get_time().val + PD_T_PD_DEBOUNCE; + tc[port].cc_state = new_cc_state; + return; + } + /* * A DRP shall transition to Unattached.SNK when the state of both * the CC1 and CC2 pins is SNK.Open for at least tPDDebounce. */ - if (get_time().val > tc[port].cc_last_change + PD_T_PD_DEBOUNCE && - tc[port].cc_state == PD_CC_NONE) { + if (new_cc_state == PD_CC_NONE && + get_time().val > tc[port].pd_debounce) { if (IS_ENABLED(CONFIG_USB_PE_SM) && - IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) + IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) { pd_dfp_exit_mode(port, 0, 0); + } /* We are detached */ set_state_tc(port, TC_UNATTACHED_SRC); @@ -1772,7 +1852,7 @@ static void tc_attach_wait_snk_run(const int port) } /* Wait for CC debounce */ - if (get_time().val < tc[port].cc_last_change + PD_T_CC_DEBOUNCE) + if (get_time().val < tc[port].cc_debounce) return; /* @@ -1790,22 +1870,23 @@ static void tc_attach_wait_snk_run(const int port) * SNK.Rp for at least tCCDebounce and VBUS is detected. */ if (pd_is_vbus_present(port)) { - if (tc[port].cc_state == PD_CC_DFP_ATTACHED) { - if (IS_ENABLED(CONFIG_USB_PD_TRY_SRC) && - pd_try_src_enable) + if (new_cc_state == PD_CC_DFP_ATTACHED) { +#ifdef CONFIG_USB_PD_TRY_SRC + if (pd_try_src_enable) set_state_tc(port, TC_TRY_SRC); else +#endif set_state_tc(port, TC_ATTACHED_SNK); } else { - /* cc_state is PD_CC_DFP_DEBUG_ACC */ + /* new_cc_state is PD_CC_DFP_DEBUG_ACC */ TC_SET_FLAG(port, TC_FLAGS_TS_DTS_PARTNER); set_state_tc(port, TC_DBG_ACC_SNK); } if (IS_ENABLED(CONFIG_USB_PE_SM) && - IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) { - hook_call_deferred( - &pd_usb_billboard_deferred_data, PD_T_AME); + IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) { + hook_call_deferred(&pd_usb_billboard_deferred_data, + PD_T_AME); } } } @@ -1815,12 +1896,33 @@ static void tc_attach_wait_snk_run(const int port) */ static void tc_attached_snk_entry(const int port) { + enum tcpc_cc_voltage_status cc1, cc2; + print_current_state(port); - if (!TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { +#ifdef CONFIG_USB_PE_SM + 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 +#endif + { /* Get connector orientation */ - tc[port].polarity = get_snk_polarity( - tc[port].cc1, tc[port].cc2); + tcpm_get_cc(port, &cc1, &cc2); + tc[port].polarity = get_snk_polarity(cc1, cc2); set_polarity(port, tc[port].polarity); /* @@ -1831,14 +1933,19 @@ static void tc_attached_snk_entry(const int port) if (IS_ENABLED(CONFIG_CHARGE_MANAGER)) { tc[port].typec_curr = - usb_get_typec_current_limit( - tc[port].polarity, tc[port].cc1, tc[port].cc2); - typec_set_input_current_limit( - port, tc[port].typec_curr, TYPE_C_VOLTAGE); + 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, CAP_DEDICATED); } } + /* Apply Rd */ + tcpm_set_cc(port, TYPEC_CC_RD); + + tc[port].cc_debounce = 0; + /* Enable PD */ if (IS_ENABLED(CONFIG_USB_PE_SM)) tc[port].pd_enable = 1; @@ -2003,12 +2110,16 @@ static void tc_unoriented_dbg_acc_src_entry(const int port) static void tc_unoriented_dbg_acc_src_run(const int port) { + enum tcpc_cc_voltage_status cc1, cc2; + + /* Check for connection */ + tcpm_get_cc(port, &cc1, &cc2); + /* * A DRP, the port shall transition to Unattached.SNK when the * SRC.Open state is detected on either the CC1 or CC2 pin. */ - if (tc[port].cc1 == TYPEC_CC_VOLT_OPEN || - tc[port].cc2 == TYPEC_CC_VOLT_OPEN) { + if (cc1 == TYPEC_CC_VOLT_OPEN || cc2 == TYPEC_CC_VOLT_OPEN) { /* Remove VBUS */ pd_power_supply_reset(port); @@ -2042,7 +2153,7 @@ 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)) { + IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) { pd_dfp_exit_mode(port, 0, 0); } @@ -2050,22 +2161,6 @@ static void tc_dbg_acc_snk_run(const int port) } } -/* Set the CC resistors to Rp and update the TCPC power role header */ -static void set_rp(const int port) -{ - /* - * Both CC1 and CC2 pins shall be independently pulled - * up through Rp. Reset last cc change time. - */ - tcpm_select_rp_value(port, CONFIG_USB_PD_PULLUP); - tcpm_set_cc(port, TYPEC_CC_RP); - tc[port].cc_last_change = get_time().val; - - /* Set power role to source */ - tc_set_power_role(port, PD_ROLE_SOURCE); - tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role); -} - /** * Unattached.SRC * @@ -2073,8 +2168,8 @@ static void set_rp(const int port) */ static void tc_unattached_src_entry(const int port) { - /* Set Rd since we are not in the Rd superstate */ - set_rp(port); + if (get_last_state_tc(port) != TC_UNATTACHED_SNK) + print_current_state(port); if (IS_ENABLED(CONFIG_USBC_PPC)) { /* There is no sink connected. */ @@ -2100,6 +2195,8 @@ static void tc_unattached_src_entry(const int port) static void tc_unattached_src_run(const int port) { + enum tcpc_cc_voltage_status cc1, cc2; + if (IS_ENABLED(CONFIG_USB_PE_SM)) { if (TC_CHK_FLAG(port, TC_FLAGS_HARD_RESET)) { TC_CLR_FLAG(port, TC_FLAGS_HARD_RESET); @@ -2118,6 +2215,9 @@ static void tc_unattached_src_run(const int port) return; } + /* Check for connection */ + tcpm_get_cc(port, &cc1, &cc2); + /* * Transition to AttachWait.SRC when VBUS is vSafe0V and: * 1) The SRC.Rd state is detected on either CC1 or CC2 pin or @@ -2126,8 +2226,7 @@ static void tc_unattached_src_run(const int port) * A DRP shall transition to Unattached.SNK within tDRPTransition * after dcSRC.DRP ∙ tDRP */ - if (cc_is_at_least_one_rd(tc[port].cc1, tc[port].cc2) || - cc_is_audio_acc(tc[port].cc1, tc[port].cc2)) + if (cc_is_at_least_one_rd(cc1, cc2) || cc_is_audio_acc(cc1, cc2)) set_state_tc(port, TC_ATTACH_WAIT_SRC); else if (get_time().val > tc[port].next_role_swap) set_state_tc(port, TC_UNATTACHED_SNK); @@ -2144,18 +2243,43 @@ static void tc_unattached_src_run(const int port) static void tc_attach_wait_src_entry(const int port) { print_current_state(port); + + tc[port].cc_state = PD_CC_UNSET; } static void tc_attach_wait_src_run(const int port) { - /* Immediate transition without debounce if we detect Vopen */ - if (tc[port].cc_state == PD_CC_NONE) { + enum tcpc_cc_voltage_status cc1, cc2; + enum pd_cc_states new_cc_state; + + /* Check for connection */ + tcpm_get_cc(port, &cc1, &cc2); + + /* Debug accessory */ + if (cc_is_snk_dbg_acc(cc1, cc2)) { + /* Debug accessory */ + new_cc_state = PD_CC_UFP_DEBUG_ACC; + } else if (cc_is_at_least_one_rd(cc1, cc2)) { + /* UFP attached */ + new_cc_state = PD_CC_UFP_ATTACHED; + } else if (cc_is_audio_acc(cc1, cc2)) { + /* AUDIO Accessory not supported. Just ignore */ + new_cc_state = PD_CC_UFP_AUDIO_ACC; + } else { + /* No UFP */ set_state_tc(port, TC_UNATTACHED_SNK); return; } + /* Debounce the cc state */ + if (new_cc_state != tc[port].cc_state) { + tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE; + tc[port].cc_state = new_cc_state; + return; + } + /* Wait for CC debounce */ - if (get_time().val < tc[port].cc_last_change + PD_T_CC_DEBOUNCE) + if (get_time().val < tc[port].cc_debounce) return; /* @@ -2169,10 +2293,13 @@ static void tc_attach_wait_src_run(const int port) * tCCDebounce. */ if (!pd_is_vbus_present(port)) { - if (tc[port].cc_state == PD_CC_UFP_ATTACHED) + if (new_cc_state == PD_CC_UFP_ATTACHED) { set_state_tc(port, TC_ATTACHED_SRC); - else if (tc[port].cc_state == PD_CC_UFP_DEBUG_ACC) + return; + } else if (new_cc_state == PD_CC_UFP_DEBUG_ACC) { set_state_tc(port, TC_UNORIENTED_DBG_ACC_SRC); + return; + } } } @@ -2182,12 +2309,23 @@ static void tc_attach_wait_src_run(const int port) */ static void tc_attached_src_entry(const int port) { - print_current_state(port); + enum tcpc_cc_voltage_status cc1, cc2; - /* Run function relies on timeout being 0 or meaningful */ - tc[port].timeout = 0; + print_current_state(port); +#if defined(CONFIG_USB_PE_SM) if (TC_CHK_FLAG(port, TC_FLAGS_PR_SWAP_IN_PROGRESS)) { + /* Change role to source */ + tc_set_power_role(port, PD_ROLE_SOURCE); + tcpm_set_msg_header(port, + tc[port].power_role, tc[port].data_role); + + /* + * Both CC1 and CC2 pins shall be independently terminated to + * ground through Rp. + */ + tcpm_select_rp_value(port, CONFIG_USB_PD_PULLUP); + /* Enable VBUS */ pd_set_power_supply_ready(port); @@ -2214,7 +2352,9 @@ static void tc_attached_src_entry(const int port) USB_SWITCH_DISCONNECT, tc[port].polarity); } - tc[port].polarity = (tc[port].cc1 != TYPEC_CC_VOLT_RD); + /* Get connector orientation */ + tcpm_get_cc(port, &cc1, &cc2); + tc[port].polarity = (cc1 != TYPEC_CC_VOLT_RD); set_polarity(port, tc[port].polarity); /* @@ -2227,6 +2367,42 @@ static void tc_attached_src_entry(const int port) tc[port].timeout = get_time().val + MAX(PD_POWER_SUPPLY_TURN_ON_DELAY, PD_T_VCONN_STABLE); } +#else + /* Get connector orientation */ + tcpm_get_cc(port, &cc1, &cc2); + tc[port].polarity = (cc1 != TYPEC_CC_VOLT_RD); + set_polarity(port, tc[port].polarity); + + /* + * Initial data role for sink is DFP + * This also sets the usb mux + */ + tc_set_data_role(port, PD_ROLE_DFP); + + /* + * Start sourcing Vconn before Vbus to ensure + * we are within USB Type-C Spec 1.4 tVconnON + */ + if (IS_ENABLED(CONFIG_USBC_VCONN)) + set_vconn(port, 1); + + /* Enable VBUS */ + if (pd_set_power_supply_ready(port)) { + /* Stop sourcing Vconn if Vbus failed */ + if (IS_ENABLED(CONFIG_USBC_VCONN)) + set_vconn(port, 0); + + if (IS_ENABLED(CONFIG_USBC_SS_MUX)) + usb_mux_set(port, TYPEC_MUX_NONE, + USB_SWITCH_DISCONNECT, tc[port].polarity); + } +#endif /* CONFIG_USB_PE_SM */ + + /* Apply Rp */ + tcpm_set_cc(port, TYPEC_CC_RP); + + tc[port].cc_debounce = 0; + tc[port].cc_state = PD_CC_NONE; /* Inform PPC that a sink is connected. */ if (IS_ENABLED(CONFIG_USBC_PPC)) @@ -2235,9 +2411,14 @@ static void tc_attached_src_entry(const int port) static void tc_attached_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) { + if (tc[port].pd_enable == 0 && + get_time().val > tc[port].timeout) { + tc[port].pd_enable = 1; tc[port].timeout = 0; } @@ -2299,6 +2480,26 @@ static void tc_attached_src_run(const int 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; + /* * When the SRC.Open state is detected on the monitored CC pin, a DRP * shall transition to Unattached.SNK unless it strongly prefers the @@ -2320,7 +2521,6 @@ static void tc_attached_src_run(const int port) tc[port].pd_enable = 0; set_state_tc(port, IS_ENABLED(CONFIG_USB_PD_TRY_SRC) ? TC_TRY_WAIT_SNK : TC_UNATTACHED_SNK); - return; } #ifdef CONFIG_USB_PE_SM @@ -2332,9 +2532,8 @@ static void tc_attached_src_run(const int port) * Power Role Swap Request */ if (TC_CHK_FLAG(port, TC_FLAGS_DO_PR_SWAP)) { - /* Clear PR_SWAP flag in exit */ - set_state_tc(port, TC_ATTACHED_SNK); - return; + TC_CLR_FLAG(port, TC_FLAGS_DO_PR_SWAP); + return set_state_tc(port, TC_ATTACHED_SNK); } /* @@ -2425,31 +2624,53 @@ static void tc_try_src_entry(const int port) { print_current_state(port); + tc[port].cc_state = PD_CC_UNSET; + tc[port].try_wait_debounce = get_time().val + PD_T_DRP_TRY; tc[port].timeout = get_time().val + PD_T_TRY_TIMEOUT; } static void tc_try_src_run(const int port) { - const uint64_t now = get_time().val; + enum tcpc_cc_voltage_status cc1, cc2; + enum pd_cc_states new_cc_state; + + /* Check for connection */ + tcpm_get_cc(port, &cc1, &cc2); + + if ((cc1 == TYPEC_CC_VOLT_RD && cc2 != TYPEC_CC_VOLT_RD) || + (cc1 != TYPEC_CC_VOLT_RD && cc2 == TYPEC_CC_VOLT_RD)) + new_cc_state = PD_CC_UFP_ATTACHED; + else + new_cc_state = PD_CC_NONE; + + /* 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_CC_DEBOUNCE; + } /* * The port shall transition to Attached.SRC when the SRC.Rd state is * detected on exactly one of the CC1 or CC2 pins for at least * tTryCCDebounce. */ - if (now > tc[port].cc_last_change + PD_T_TRY_CC_DEBOUNCE && - tc[port].cc_state == PD_CC_UFP_ATTACHED) - set_state_tc(port, TC_ATTACHED_SRC); + if (get_time().val > tc[port].cc_debounce) { + if (new_cc_state == PD_CC_UFP_ATTACHED) + set_state_tc(port, TC_ATTACHED_SRC); + } + /* * The port shall transition to TryWait.SNK after tDRPTry and the * SRC.Rd state has not been detected and VBUS is within vSafe0V, * or after tTryTimeout and the SRC.Rd state has not been detected. */ - else if (now > tc[port].cc_last_change + PD_T_DRP_TRY && - !pd_is_vbus_present(port)) - set_state_tc(port, TC_TRY_WAIT_SNK); - else if (now > tc[port].timeout) - set_state_tc(port, TC_TRY_WAIT_SNK); + if (new_cc_state == PD_CC_NONE) { + if ((get_time().val > tc[port].try_wait_debounce && + !pd_is_vbus_present(port)) || + get_time().val > tc[port].timeout) { + set_state_tc(port, TC_TRY_WAIT_SNK); + } + } } /** @@ -2463,26 +2684,48 @@ static void tc_try_src_run(const int port) static void tc_try_wait_snk_entry(const int port) { print_current_state(port); + + tc[port].cc_state = PD_CC_UNSET; + tc[port].try_wait_debounce = get_time().val + PD_T_CC_DEBOUNCE; } static void tc_try_wait_snk_run(const int port) { - const uint64_t now = get_time().val; + enum tcpc_cc_voltage_status cc1, cc2; + enum pd_cc_states new_cc_state; + + /* Check for connection */ + tcpm_get_cc(port, &cc1, &cc2); + + /* We only care about CCs being open */ + if (cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_OPEN) + new_cc_state = PD_CC_NONE; + else + new_cc_state = PD_CC_UNSET; + + /* Debounce the cc state */ + if (new_cc_state != tc[port].cc_state) { + tc[port].cc_state = new_cc_state; + tc[port].pd_debounce = get_time().val + PD_T_PD_DEBOUNCE; + } - /* - * The port shall transition to Attached.SNK after tCCDebounce if or - * when VBUS is detected. - */ - if (now > tc[port].cc_last_change + PD_T_CC_DEBOUNCE && - pd_is_vbus_present(port)) - set_state_tc(port, TC_ATTACHED_SNK); /* * The port shall transition to Unattached.SNK when the state of both * of the CC1 and CC2 pins is SNK.Open for at least tPDDebounce. */ - else if (now > tc[port].cc_last_change + PD_T_PD_DEBOUNCE && - tc[port].cc_state == PD_CC_NONE) + if ((get_time().val > tc[port].pd_debounce) && + (new_cc_state == PD_CC_NONE)) { set_state_tc(port, TC_UNATTACHED_SNK); + return; + } + + /* + * The port shall transition to Attached.SNK after tCCDebounce if or + * when VBUS is detected. + */ + if (get_time().val > tc[port].try_wait_debounce && + pd_is_vbus_present(port)) + set_state_tc(port, TC_ATTACHED_SNK); } #endif @@ -2497,11 +2740,11 @@ static void tc_ct_unattached_snk_entry(int port) /* * Both CC1 and CC2 pins shall be independently terminated to - * ground through Rd. Reset last cc change time. + * ground through Rd. */ tcpm_select_rp_value(port, CONFIG_USB_PD_PULLUP); tcpm_set_cc(port, TYPEC_CC_RD); - tc[port].cc_last_change = get_time().val; + tc[port].cc_state = PD_CC_UNSET; /* Set power role to sink */ tc_set_power_role(port, PD_ROLE_SINK); @@ -2518,6 +2761,10 @@ static void tc_ct_unattached_snk_entry(int port) static void tc_ct_unattached_snk_run(int port) { + enum tcpc_cc_voltage_status cc1; + enum tcpc_cc_voltage_status cc2; + enum pd_cc_states new_cc_state; + if (tc[port].timeout > 0 && get_time().val > tc[port].timeout) { tc[port].pd_enable = 1; tc[port].timeout = 0; @@ -2540,22 +2787,40 @@ static void tc_ct_unattached_snk_run(int port) pe_ps_reset_complete(port); } - /* - * The port shall transition to CTAttached.SNK when VBUS is detected. - */ - if (pd_is_vbus_present(port)) { - set_state_tc(port, TC_CT_ATTACHED_SNK); + /* Check for connection */ + tcpm_get_cc(port, &cc1, &cc2); + + /* We only care about CCs being open */ + if (cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_OPEN) + new_cc_state = PD_CC_NONE; + else + new_cc_state = PD_CC_UNSET; + + /* 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_VPDDETACH; + } + /* * The port shall transition to Unattached.SNK if the state of * the CC pin is SNK.Open for tVPDDetach after VBUS is vSafe0V. */ - } else if (get_time().val > tc[port].cc_last_change + PD_T_VPDDETACH && - tc[port].cc_state == PD_CC_NONE) { - if (IS_ENABLED(CONFIG_USB_PD_ALT_MODE_DFP)) - pd_dfp_exit_mode(port, 0, 0); + if (get_time().val > tc[port].cc_debounce) { + if (new_cc_state == PD_CC_NONE && !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); + set_state_tc(port, TC_UNATTACHED_SNK); + return; + } } + + /* + * The port shall transition to CTAttached.SNK when VBUS is detected. + */ + if (pd_is_vbus_present(port)) + set_state_tc(port, TC_CT_ATTACHED_SNK); } /** @@ -2610,6 +2875,48 @@ static void tc_ct_attached_snk_exit(int port) #endif /* CONFIG_USB_PE_SM */ /** + * Super State CC_RD + */ +static void tc_cc_rd_entry(const int port) +{ + /* Disable VCONN */ + if (IS_ENABLED(CONFIG_USBC_VCONN)) + set_vconn(port, 0); + + /* + * Both CC1 and CC2 pins shall be independently terminated to + * ground through Rd. + */ + tcpm_set_cc(port, TYPEC_CC_RD); + + /* Set power role to sink */ + tc_set_power_role(port, PD_ROLE_SINK); + tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role); +} + + +/** + * Super State CC_RP + */ +static void tc_cc_rp_entry(const int port) +{ + /* Disable VCONN */ + if (IS_ENABLED(CONFIG_USBC_VCONN)) + set_vconn(port, 0); + + /* Set power role to source */ + tc_set_power_role(port, PD_ROLE_SOURCE); + tcpm_set_msg_header(port, tc[port].power_role, tc[port].data_role); + + /* + * Both CC1 and CC2 pins shall be independently pulled + * up through Rp. + */ + tcpm_select_rp_value(port, CONFIG_USB_PD_PULLUP); + tcpm_set_cc(port, TYPEC_CC_RP); +} + +/** * Super State CC_OPEN */ static void tc_cc_open_entry(const int port) @@ -2636,38 +2943,6 @@ static void tc_cc_open_entry(const int port) } } -/** - * Super State CC_RD - */ -static void tc_cc_rd_entry(const int port) -{ - set_rd(port); -} - -/** - * Super State CC_RP - */ -static void tc_cc_rp_entry(const int port) -{ - set_rp(port); -} - -/** - * Super State Unattached - * - * Ensures that any time we unattached we can perform an action without - * repeating it during DRP toggle - */ -static void tc_unattached_entry(const int port) -{ - /* This only prints the first time we enter a unattached state */ - print_current_state(port); - - /* This disables the mux when we disconnect on a port */ - if (IS_ENABLED(CONFIG_USBC_SS_MUX)) - set_usb_mux_with_current_data_role(port); -} - void tc_run(const int port) { run_state(port, &tc[port].ctx); @@ -2704,7 +2979,6 @@ int pd_is_debug_acc(int port) * | TC_ATTACH_WAIT_SNK | | TC_ATTACH_WAIT_SRC | * | TC_TRY_WAIT_SNK | | TC_TRY_SRC | * | TC_DBG_ACC_SNK | | TC_UNORIENTED_DBG_ACC_SRC | - * | TC_ATTACHED_SNK | | TC_ATTACHED_SRC | * |-----------------------| |---------------------------------| * * |TC_CC_OPEN -----------| @@ -2713,6 +2987,8 @@ int pd_is_debug_acc(int port) * | TC_ERROR_RECOVERY | * |----------------------| * + * TC_ATTACHED_SNK TC_ATTACHED_SRC + * */ static const struct usb_state tc_states[] = { /* Super States */ @@ -2725,9 +3001,6 @@ static const struct usb_state tc_states[] = { [TC_CC_RP] = { .entry = tc_cc_rp_entry, }, - [TC_UNATTACHED] = { - .entry = tc_unattached_entry, - }, /* Normal States */ [TC_DISABLED] = { .entry = tc_disabled_entry, @@ -2743,7 +3016,7 @@ static const struct usb_state tc_states[] = { [TC_UNATTACHED_SNK] = { .entry = tc_unattached_snk_entry, .run = tc_unattached_snk_run, - .parent = &tc_states[TC_UNATTACHED], + .parent = &tc_states[TC_CC_RD], }, [TC_ATTACH_WAIT_SNK] = { .entry = tc_attach_wait_snk_entry, @@ -2754,7 +3027,6 @@ static const struct usb_state tc_states[] = { .entry = tc_attached_snk_entry, .run = tc_attached_snk_run, .exit = tc_attached_snk_exit, - .parent = &tc_states[TC_CC_RD], }, [TC_UNORIENTED_DBG_ACC_SRC] = { .entry = tc_unoriented_dbg_acc_src_entry, @@ -2769,7 +3041,7 @@ static const struct usb_state tc_states[] = { [TC_UNATTACHED_SRC] = { .entry = tc_unattached_src_entry, .run = tc_unattached_src_run, - .parent = &tc_states[TC_UNATTACHED], + .parent = &tc_states[TC_CC_RP], }, [TC_ATTACH_WAIT_SRC] = { .entry = tc_attach_wait_src_entry, @@ -2780,7 +3052,6 @@ static const struct usb_state tc_states[] = { .entry = tc_attached_src_entry, .run = tc_attached_src_run, .exit = tc_attached_src_exit, - .parent = &tc_states[TC_CC_RP], }, #ifdef CONFIG_USB_PD_TRY_SRC [TC_TRY_SRC] = { |