summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 */