diff options
author | Edward Hill <ecgh@chromium.org> | 2018-08-28 15:43:32 -0600 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-09-17 21:34:41 -0700 |
commit | cc641491fe1f60e9b65a414ab346754f8410e554 (patch) | |
tree | f6c112c4cf2101505426cd2307e7e88b37cea5ab | |
parent | 1a7e596cb98369771cf30a602e307e0cf26f2522 (diff) | |
download | chrome-ec-cc641491fe1f60e9b65a414ab346754f8410e554.tar.gz |
pd: Check for dual-role partner when TOGGLE_OFF/FORCE_SINK
Charging via hub with the ANX3429 TCPC sometimes fails when the system
is in S5/G3. Both the hub and the ANX3429 are auto-toggling, so source
and sink roles depend on the timing of the connection. Charging will
fail when the hub expects the ANX3429 to be a source, but the
drp_state in S5/G3 is TOGGLE_OFF/FORCE_SINK.
Ideally we wouldn't use auto-toggle when drp_state is
TOGGLE_OFF/FORCE_SINK, but with the ANX3429 TCPC, auto-toggle can't be
prevented in low power mode.
To fix this, try being a sink in case the connected device is dual-role.
100 ms is enough time for a dual-role partner to switch from sink to
source. If the connected device is sink-only, then we will attempt
SNK_DISCONNECTED twice (due to debounce time), then return to low power
mode (and stay there). After 200 ms, reset ready for a new connection.
Move the next_state selection out into a function since things were
getting very narrow inside PD_STATE_DRP_AUTO_TOGGLE.
BRANCH=none
BUG=b:72007056
TEST=sink device + ANX3429: low power mode
TEST=sink device + PS8751: low power mode
TEST=charger via hub + ANX3429: starts charging (20/20 tries)
TEST=charger via hub + PS8751: starts charging
(all tests with Grunt in G3)
Change-Id: I097dcace96bc6e6e9cfab279bcbded50ef9951e3
Signed-off-by: Edward Hill <ecgh@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1194678
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Reviewed-by: Jett Rink <jettrink@chromium.org>
-rw-r--r-- | common/usb_pd_protocol.c | 96 |
1 files changed, 67 insertions, 29 deletions
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c index 2d27d598e8..aa9313a9ad 100644 --- a/common/usb_pd_protocol.c +++ b/common/usb_pd_protocol.c @@ -193,6 +193,14 @@ static struct pd_protocol { int tasks_waiting_on_reset; #endif +#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE + /* + * Timer for handling TOGGLE_OFF/FORCE_SINK mode when auto-toggle + * enabled. See drp_auto_toggle_next_state() for details. + */ + uint64_t drp_sink_time; +#endif + /* PD state for Vendor Defined Messages */ enum vdm_states vdm_state; /* Timeout for the current vdm state. Set to 0 for no timeout. */ @@ -2204,6 +2212,64 @@ static void pd_partner_port_reset(int port) } #endif /* CONFIG_USB_PD_DUAL_ROLE */ +/** + * Returns whether the sink has detected a Rp resistor on the other side. + */ +static inline int cc_is_rp(int cc) +{ + return (cc == TYPEC_CC_VOLT_SNK_DEF) || (cc == TYPEC_CC_VOLT_SNK_1_5) || + (cc == TYPEC_CC_VOLT_SNK_3_0); +} + +#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE +static enum pd_states drp_auto_toggle_next_state(int port, int cc1, int cc2) +{ + enum pd_states next_state; + + /* Set to appropriate port state */ + if (cc1 == TYPEC_CC_VOLT_OPEN && + cc2 == TYPEC_CC_VOLT_OPEN) + /* nothing connected, keep toggling*/ + next_state = PD_STATE_DRP_AUTO_TOGGLE; + else if ((cc_is_rp(cc1) || cc_is_rp(cc2)) && + drp_state[port] != PD_DRP_FORCE_SOURCE) { + /* SNK allowed unless ForceSRC */ + next_state = PD_STATE_SNK_DISCONNECTED; + } else if ((cc1 == TYPEC_CC_VOLT_RD || + cc2 == TYPEC_CC_VOLT_RD) || + (cc1 == TYPEC_CC_VOLT_RA && + cc2 == TYPEC_CC_VOLT_RA)) { + /* + * SRC allowed unless ForceSNK or Toggle Off + * + * Ideally we wouldn't use auto-toggle when drp_state is + * TOGGLE_OFF/FORCE_SINK, but for some TCPCs, auto-toggle can't + * be prevented in low power mode. Try being a sink in case the + * connected device is dual-role (this ensures reliable charging + * from a hub, b/72007056). 100 ms is enough time for a + * dual-role partner to switch from sink to source. If the + * connected device is sink-only, then we will attempt + * SNK_DISCONNECTED twice (due to debounce time), then return to + * low power mode (and stay there). After 200 ms, reset ready + * for a new connection. + */ + if (drp_state[port] == PD_DRP_TOGGLE_OFF || + drp_state[port] == PD_DRP_FORCE_SINK) { + if (get_time().val > pd[port].drp_sink_time + 200*MSEC) + pd[port].drp_sink_time = get_time().val; + if (get_time().val < pd[port].drp_sink_time + 100*MSEC) + next_state = PD_STATE_SNK_DISCONNECTED; + else + next_state = PD_STATE_DRP_AUTO_TOGGLE; + } else + next_state = PD_STATE_SRC_DISCONNECTED; + } else + /* Anything else, keep toggling */ + next_state = PD_STATE_DRP_AUTO_TOGGLE; + return next_state; +} +#endif /* CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE */ + int pd_get_polarity(int port) { return pd[port].polarity; @@ -2246,15 +2312,6 @@ void pd_ping_enable(int port, int enable) pd[port].flags &= ~PD_FLAGS_PING_ENABLED; } -/** - * Returns whether the sink has detected a Rp resistor on the other side. - */ -static inline int cc_is_rp(int cc) -{ - return (cc == TYPEC_CC_VOLT_SNK_DEF) || (cc == TYPEC_CC_VOLT_SNK_1_5) || - (cc == TYPEC_CC_VOLT_SNK_3_0); -} - /* * CC values for regular sources and Debug sources (aka DTS) * @@ -3804,26 +3861,7 @@ void pd_task(void *u) /* Check for connection */ tcpm_get_cc(port, &cc1, &cc2); - /* Set to appropriate port state */ - if (cc1 == TYPEC_CC_VOLT_OPEN && - cc2 == TYPEC_CC_VOLT_OPEN) - /* nothing connected, keep toggling*/ - next_state = PD_STATE_DRP_AUTO_TOGGLE; - else if ((cc_is_rp(cc1) || cc_is_rp(cc2)) && - drp_state[port] != PD_DRP_FORCE_SOURCE) - /* SNK allowed unless ForceSRC */ - next_state = PD_STATE_SNK_DISCONNECTED; - else if (((cc1 == TYPEC_CC_VOLT_RD || - cc2 == TYPEC_CC_VOLT_RD) || - (cc1 == TYPEC_CC_VOLT_RA && - cc2 == TYPEC_CC_VOLT_RA)) && - (drp_state[port] != PD_DRP_TOGGLE_OFF && - drp_state[port] != PD_DRP_FORCE_SINK)) - /* SRC allowed unless ForceSNK or Toggle Off */ - next_state = PD_STATE_SRC_DISCONNECTED; - else - /* Anything else, keep toggling */ - next_state = PD_STATE_DRP_AUTO_TOGGLE; + next_state = drp_auto_toggle_next_state(port, cc1, cc2); if (next_state == PD_STATE_SNK_DISCONNECTED) { tcpm_set_cc(port, TYPEC_CC_RD); |