summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorCaveh Jalali <caveh@chromium.org>2019-06-12 00:59:17 -0700
committerCommit Bot <commit-bot@chromium.org>2019-10-31 07:18:25 +0000
commitb0828291b501289f4c1d3726ab545cf3efd06d13 (patch)
tree0709d8d4262fbdacb6b2897b08b9e405d3ebe8c8 /common
parent85ae91a99c04bc77b9c4fd3159e04fa4e9d0af16 (diff)
downloadchrome-ec-b0828291b501289f4c1d3726ab545cf3efd06d13.tar.gz
pd_protocol: add hard_reset_complete_timer
certain chargers have noisy CC lines which can prevent overly sensitive TCPCs from detecting a bus idle state. this means the TCPC will not send out messages such as hard_reset. this condition may not clear which means our pd_task() will retry sending hard_resets. although we specify a "timeout" for the event loop in this state, the timeout can be ignored by the event wait when there are pending events. this gets us into a tight event processing loop that starves the watchdog! the solution is to add an explicit timeout timer when processing the PD_STATE_HARD_RESET_SEND state. BUG=b:134702480 BRANCH=none TEST=no more EC watchdog on affected systems Change-Id: I1ae871f5d8fc99f6906ddd18741bbf68dcb6e935 Signed-off-by: Caveh Jalali <caveh@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1889431 Tested-by: Nitin Kolluru <nkolluru@google.com> Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org>
Diffstat (limited to 'common')
-rw-r--r--common/usb_pd_protocol.c68
1 files changed, 49 insertions, 19 deletions
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c
index 77cb652a19..ad4ff19fa0 100644
--- a/common/usb_pd_protocol.c
+++ b/common/usb_pd_protocol.c
@@ -267,6 +267,11 @@ static struct pd_protocol {
* of our own.
*/
uint64_t ready_state_holdoff_timer;
+ /*
+ * PD 2.0 spec, section 6.5.11.1
+ * When we can give up on a HARD_RESET transmission.
+ */
+ uint64_t hard_reset_complete_timer;
} pd[CONFIG_USB_PD_PORT_COUNT];
#ifdef CONFIG_COMMON_RUNTIME
@@ -4452,8 +4457,10 @@ void pd_task(void *u)
break;
case PD_STATE_HARD_RESET_SEND:
hard_reset_count++;
- if (pd[port].last_state != pd[port].task_state)
+ if (pd[port].last_state != pd[port].task_state) {
hard_reset_sent = 0;
+ pd[port].hard_reset_complete_timer = 0;
+ }
#ifdef CONFIG_CHARGE_MANAGER
if (pd[port].last_state == PD_STATE_SNK_DISCOVERY ||
(pd[port].last_state == PD_STATE_SOFT_RESET &&
@@ -4473,29 +4480,52 @@ void pd_task(void *u)
}
#endif
- /* try sending hard reset until it succeeds */
- if (!hard_reset_sent) {
- if (pd_transmit(port, TCPC_TX_HARD_RESET,
- 0, NULL) < 0) {
- timeout = 10*MSEC;
+ if (hard_reset_sent)
+ break;
+
+ if (pd_transmit(port, TCPC_TX_HARD_RESET, 0, NULL) <
+ 0) {
+ /*
+ * likely a non-idle channel
+ * TCPCI r2.0 v1.0 4.4.15:
+ * the TCPC does not retry HARD_RESET
+ * but we can try periodically until the timer
+ * expires.
+ */
+ now = get_time();
+ if (pd[port].hard_reset_complete_timer == 0) {
+ pd[port].hard_reset_complete_timer =
+ now.val +
+ PD_T_HARD_RESET_COMPLETE;
+ timeout = PD_T_HARD_RESET_RETRY;
+ break;
+ }
+ if (now.val <
+ pd[port].hard_reset_complete_timer) {
+ CPRINTS("C%d: Retrying hard reset",
+ port);
+ timeout = PD_T_HARD_RESET_RETRY;
break;
}
-
- /* successfully sent hard reset */
- hard_reset_sent = 1;
/*
- * If we are source, delay before cutting power
- * to allow sink time to get hard reset.
+ * PD 2.0 spec, section 6.5.11.1
+ * Pretend TX_HARD_RESET succeeded after
+ * timeout.
*/
- if (pd[port].power_role == PD_ROLE_SOURCE) {
- set_state_timeout(port,
- get_time().val + PD_T_PS_HARD_RESET,
- PD_STATE_HARD_RESET_EXECUTE);
- } else {
- set_state(port,
+ }
+
+ hard_reset_sent = 1;
+ /*
+ * If we are source, delay before cutting power
+ * to allow sink time to get hard reset.
+ */
+ if (pd[port].power_role == PD_ROLE_SOURCE) {
+ set_state_timeout(port,
+ get_time().val + PD_T_PS_HARD_RESET,
PD_STATE_HARD_RESET_EXECUTE);
- timeout = 10*MSEC;
- }
+ } else {
+ set_state(port, PD_STATE_HARD_RESET_EXECUTE);
+ timeout = 10 * MSEC;
}
break;
case PD_STATE_HARD_RESET_EXECUTE: