summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Hill <ecgh@chromium.org>2018-08-28 15:43:32 -0600
committerchrome-bot <chrome-bot@chromium.org>2018-09-17 21:34:41 -0700
commitcc641491fe1f60e9b65a414ab346754f8410e554 (patch)
treef6c112c4cf2101505426cd2307e7e88b37cea5ab
parent1a7e596cb98369771cf30a602e307e0cf26f2522 (diff)
downloadchrome-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.c96
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);