summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Brandmeyer <jbrandmeyer@chromium.org>2018-10-03 09:16:33 -0600
committerchrome-bot <chrome-bot@chromium.org>2018-10-04 17:09:01 -0700
commit31fe290061fdf9d1b0e8a634c77cb7a30b2f012b (patch)
tree4c86c978f86dce2e5868f71a0e756bb38cbaf7af
parent356083b7f0b36ffd5b8d387e332ff488810ed47e (diff)
downloadchrome-ec-31fe290061fdf9d1b0e8a634c77cb7a30b2f012b.tar.gz
tcpc: Resume suspended ports after an interval
When it appears that the TCPC queue handling logic may be stuck in an infinite loop, the EC will break the loop by disabling the port in order to avoid task starvation. However, nothing attempts to resume the port, and it therefore remains disabled until the next EC reset. Wake back up after a reasonable interval in order to automatically retry. On port suspend, completely drain the TCPM message queue. BUG=chromium:891713 TEST=On Careena hardware, with analogix TCPC firmware v1.5 and an electronically marked cable that sends SOP' messages, verify that the EC auto-retries every second. After removing the offending cable, and PD power supply will work. BRANCH=grunt Change-Id: I563b763501333eb36ee1f76e6f484ebb3245c82a Signed-off-by: Jonathan Brandmeyer <jbrandmeyer@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1258571 Reviewed-by: Jett Rink <jettrink@chromium.org>
-rw-r--r--common/usb_pd_protocol.c26
-rw-r--r--common/usb_pd_tcpc.c12
-rw-r--r--driver/tcpm/anx74xx.c7
-rw-r--r--driver/tcpm/stub.c5
-rw-r--r--driver/tcpm/tcpci.c14
-rw-r--r--driver/tcpm/tcpm.h7
-rw-r--r--include/usb_pd.h6
-rw-r--r--include/usb_pd_tcpc.h1
8 files changed, 71 insertions, 7 deletions
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c
index 8cefc0cfc9..23c88d7a85 100644
--- a/common/usb_pd_protocol.c
+++ b/common/usb_pd_protocol.c
@@ -3246,6 +3246,9 @@ void pd_task(void *u)
if (rstatus != 0 && rstatus != EC_ERROR_UNIMPLEMENTED)
CPRINTS("TCPC p%d release failed!", port);
#endif
+ /* Drain any outstanding software message queues. */
+ tcpm_clear_pending_messages(port);
+
/* Wait for resume */
while (pd[port].task_state == PD_STATE_SUSPENDED)
task_wait_event(-1);
@@ -4110,6 +4113,29 @@ void pd_set_suspend(int port, int enable)
}
}
+#ifdef CONFIG_USB_PD_TCPM_TCPCI
+static uint32_t pd_ports_to_resume;
+static void resume_pd_port(void)
+{
+ uint32_t port;
+ uint32_t suspended_ports = atomic_read_clear(&pd_ports_to_resume);
+
+ while (suspended_ports) {
+ port = __builtin_ctz(suspended_ports);
+ suspended_ports &= ~(1 << port);
+ pd_set_suspend(port, 0);
+ }
+}
+DECLARE_DEFERRED(resume_pd_port);
+
+void pd_deferred_resume(int port)
+{
+ atomic_or(&pd_ports_to_resume, 1 << port);
+ hook_call_deferred(&resume_pd_port_data, SECOND);
+}
+
+#endif /* CONFIG_USB_PD_DEFERRED_RESUME */
+
int pd_is_port_enabled(int port)
{
switch (pd[port].task_state) {
diff --git a/common/usb_pd_tcpc.c b/common/usb_pd_tcpc.c
index 152775bbc0..11bc399b5e 100644
--- a/common/usb_pd_tcpc.c
+++ b/common/usb_pd_tcpc.c
@@ -261,7 +261,12 @@ static struct pd_port_controller {
static int rx_buf_is_full(int port)
{
- /* Buffer is full if the tail is 1 ahead of head */
+ /*
+ * TODO: Refactor these to use the incrementing-counter idiom instead of
+ * the wrapping-counter idiom to reclaim the last buffer entry.
+ *
+ * Buffer is full if the tail is 1 ahead of head.
+ */
int diff = pd[port].rx_buf_tail - pd[port].rx_buf_head;
return (diff == 1) || (diff == -RX_BUFFER_SIZE);
}
@@ -272,6 +277,11 @@ int rx_buf_is_empty(int port)
return pd[port].rx_buf_tail == pd[port].rx_buf_head;
}
+void rx_buf_clear(int port)
+{
+ pd[port].rx_buf_tail = pd[port].rx_buf_head;
+}
+
static void rx_buf_increment(int port, int *buf_ptr)
{
*buf_ptr = *buf_ptr == RX_BUFFER_SIZE ? 0 : *buf_ptr + 1;
diff --git a/driver/tcpm/anx74xx.c b/driver/tcpm/anx74xx.c
index fd114f7385..e0c01f52dc 100644
--- a/driver/tcpm/anx74xx.c
+++ b/driver/tcpm/anx74xx.c
@@ -931,13 +931,14 @@ void anx74xx_tcpc_alert(int port)
/* Ensure we don't loop endlessly */
if (failed_attempts >= MAX_ALLOW_FAILED_RX_READS) {
CPRINTF("C%d Cannot consume RX buffer after %d failed "
- "attempts!",
- failed_attempts);
+ "attempts!", port, failed_attempts);
/*
* The port is in a bad state, we don't want to consume
- * all EC resources so suspend port forever.
+ * all EC resources so suspend the port for a little
+ * while.
*/
pd_set_suspend(port, 1);
+ pd_deferred_resume(port);
return;
}
}
diff --git a/driver/tcpm/stub.c b/driver/tcpm/stub.c
index 1d748de6b1..23529b5185 100644
--- a/driver/tcpm/stub.c
+++ b/driver/tcpm/stub.c
@@ -103,6 +103,11 @@ int tcpm_dequeue_message(int port, uint32_t *payload, int *head)
return ret;
}
+void tcpm_clear_pending_messages(int port)
+{
+ rx_buf_clear(port);
+}
+
int tcpm_transmit(int port, enum tcpm_transmit_type type, uint16_t header,
const uint32_t *data)
{
diff --git a/driver/tcpm/tcpci.c b/driver/tcpm/tcpci.c
index 1c1c04868d..00067c18d6 100644
--- a/driver/tcpm/tcpci.c
+++ b/driver/tcpm/tcpci.c
@@ -446,6 +446,13 @@ int tcpm_dequeue_message(const int port, uint32_t *const payload,
return EC_SUCCESS;
}
+void tcpm_clear_pending_messages(int port)
+{
+ struct queue *const q = &cached_messages[port];
+
+ q->tail = q->head;
+}
+
int tcpci_tcpm_transmit(int port, enum tcpm_transmit_type type,
uint16_t header, const uint32_t *data)
{
@@ -528,13 +535,14 @@ void tcpci_tcpc_alert(int port)
/* Ensure we don't loop endlessly */
if (failed_attempts >= MAX_ALLOW_FAILED_RX_READS) {
CPRINTF("C%d Cannot consume RX buffer after %d failed "
- "attempts!",
- failed_attempts);
+ "attempts!", port, failed_attempts);
/*
* The port is in a bad state, we don't want to consume
- * all EC resources so suspend port forever.
+ * all EC resources so suspend the port for a little
+ * while.
*/
pd_set_suspend(port, 1);
+ pd_deferred_resume(port);
return;
}
}
diff --git a/driver/tcpm/tcpm.h b/driver/tcpm/tcpm.h
index fc0cd2791d..66792d34c8 100644
--- a/driver/tcpm/tcpm.h
+++ b/driver/tcpm/tcpm.h
@@ -372,4 +372,11 @@ int tcpm_dequeue_message(int port, uint32_t *payload, int *header);
*/
int tcpm_has_pending_message(int port);
+/**
+ * Clear any pending messages in the RX queue. This function must be
+ * called from the same context as the caller of tcpm_dequeue_message to avoid
+ * race conditions.
+ */
+void tcpm_clear_pending_messages(int port);
+
#endif
diff --git a/include/usb_pd.h b/include/usb_pd.h
index f27c10544d..c5f6799a26 100644
--- a/include/usb_pd.h
+++ b/include/usb_pd.h
@@ -1637,6 +1637,12 @@ int pd_rx_started(int port);
void pd_set_suspend(int port, int enable);
/**
+ * Resume the PD task for a port after a period of time has elapsed.
+ * @param port USB-C port number
+ */
+void pd_deferred_resume(int port);
+
+/**
* Check if the port has been initialized and PD task has not been
* suspended.
*
diff --git a/include/usb_pd_tcpc.h b/include/usb_pd_tcpc.h
index 2c2da66c84..812a194a85 100644
--- a/include/usb_pd_tcpc.h
+++ b/include/usb_pd_tcpc.h
@@ -58,5 +58,6 @@ int tcpc_get_message(int port, uint32_t *payload, int *head);
int tcpc_transmit(int port, enum tcpm_transmit_type type, uint16_t header,
const uint32_t *data);
int rx_buf_is_empty(int port);
+void rx_buf_clear(int port);
#endif /* __CROS_EC_USB_PD_TCPC_H */