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> Reviewed-on: https://chromium-review.googlesource.com/1231363 Reviewed-by: Martin Roth <martinroth@chromium.org> Commit-Queue: Martin Roth <martinroth@chromium.org> Tested-by: Martin Roth <martinroth@chromium.org>
This commit is contained in:
parent
6836d4a0c8
commit
bf4aea7fb4
|
@ -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. */
|
||||
|
@ -2199,6 +2207,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;
|
||||
|
@ -2241,15 +2307,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)
|
||||
*
|
||||
|
@ -3799,26 +3856,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);
|
||||
|
|
Loading…
Reference in New Issue