diff options
-rw-r--r-- | common/usb_pd_protocol.c | 26 | ||||
-rw-r--r-- | common/usb_pd_tcpc.c | 12 | ||||
-rw-r--r-- | driver/tcpm/anx74xx.c | 7 | ||||
-rw-r--r-- | driver/tcpm/stub.c | 5 | ||||
-rw-r--r-- | driver/tcpm/tcpci.c | 14 | ||||
-rw-r--r-- | driver/tcpm/tcpm.h | 7 | ||||
-rw-r--r-- | include/usb_pd.h | 6 | ||||
-rw-r--r-- | include/usb_pd_tcpc.h | 1 |
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 */ |