diff options
-rw-r--r-- | board/rainier/board.h | 1 | ||||
-rw-r--r-- | board/reef_it8320/board.h | 1 | ||||
-rw-r--r-- | board/scarlet/board.h | 1 | ||||
-rw-r--r-- | chip/it83xx/intc.c | 6 | ||||
-rw-r--r-- | common/usb_pd_protocol.c | 11 | ||||
-rw-r--r-- | common/usb_pd_tcpc.c | 4 | ||||
-rw-r--r-- | driver/tcpm/anx7447.c | 2 | ||||
-rw-r--r-- | driver/tcpm/anx74xx.c | 71 | ||||
-rw-r--r-- | driver/tcpm/anx7688.c | 4 | ||||
-rw-r--r-- | driver/tcpm/fusb302.c | 20 | ||||
-rw-r--r-- | driver/tcpm/it83xx.c | 13 | ||||
-rw-r--r-- | driver/tcpm/mt6370.c | 2 | ||||
-rw-r--r-- | driver/tcpm/ps8xxx.c | 2 | ||||
-rw-r--r-- | driver/tcpm/stub.c | 12 | ||||
-rw-r--r-- | driver/tcpm/tcpci.c | 164 | ||||
-rw-r--r-- | driver/tcpm/tcpci.h | 2 | ||||
-rw-r--r-- | driver/tcpm/tcpm.h | 38 | ||||
-rw-r--r-- | include/usb_pd.h | 1 | ||||
-rw-r--r-- | include/usb_pd_tcpc.h | 1 | ||||
-rw-r--r-- | include/usb_pd_tcpm.h | 4 |
20 files changed, 252 insertions, 108 deletions
diff --git a/board/rainier/board.h b/board/rainier/board.h index 7e3dc17330..1c5e9b6a03 100644 --- a/board/rainier/board.h +++ b/board/rainier/board.h @@ -91,6 +91,7 @@ #define CONFIG_USB_PD_DUAL_ROLE #define CONFIG_USB_PD_PORT_COUNT 1 #define CONFIG_USB_PD_TCPM_FUSB302 +#define CONFIG_USB_PD_TCPM_TCPCI #define CONFIG_USB_PD_VBUS_DETECT_TCPC #define CONFIG_USB_PD_VBUS_MEASURE_NOT_PRESENT #define CONFIG_USBC_SS_MUX diff --git a/board/reef_it8320/board.h b/board/reef_it8320/board.h index 84377e717a..9469481f8b 100644 --- a/board/reef_it8320/board.h +++ b/board/reef_it8320/board.h @@ -76,6 +76,7 @@ #define CONFIG_USB_PD_PORT_COUNT 2 #define CONFIG_USB_PD_VBUS_DETECT_CHARGER #define CONFIG_USB_PD_TCPM_ITE83XX +#define CONFIG_USB_PD_TCPM_TCPCI #define CONFIG_USB_PD_TRY_SRC #define CONFIG_USB_POWER_DELIVERY #define CONFIG_USB_PD_COMM_LOCKED diff --git a/board/scarlet/board.h b/board/scarlet/board.h index 24fd88de4f..d6110c5ba3 100644 --- a/board/scarlet/board.h +++ b/board/scarlet/board.h @@ -110,6 +110,7 @@ #define CONFIG_USB_PD_LOGGING #define CONFIG_USB_PD_PORT_COUNT 1 #define CONFIG_USB_PD_TCPM_FUSB302 +#define CONFIG_USB_PD_TCPM_TCPCI #define CONFIG_USB_PD_VBUS_DETECT_TCPC #define CONFIG_USB_PD_5V_CHARGER_CTRL #define CONFIG_USBC_SS_MUX diff --git a/chip/it83xx/intc.c b/chip/it83xx/intc.c index 2d1f06b2db..ae52a5d5a2 100644 --- a/chip/it83xx/intc.c +++ b/chip/it83xx/intc.c @@ -9,6 +9,7 @@ #include "kmsc_chip.h" #include "registers.h" #include "task.h" +#include "tcpm.h" #include "usb_pd.h" #ifdef CONFIG_USB_PD_TCPM_ITE83XX @@ -24,12 +25,9 @@ static void chip_pd_irq(enum usbpd_port port) PD_EVENT_TCPC_RESET, 0); } else { if (USBPD_IS_RX_DONE(port)) { - /* mask RX done interrupt */ - IT83XX_USBPD_IMR(port) |= USBPD_REG_MASK_MSG_RX_DONE; + tcpm_enqueue_message(port); /* clear RX done interrupt */ IT83XX_USBPD_ISR(port) = USBPD_REG_MASK_MSG_RX_DONE; - task_set_event(PD_PORT_TO_TASK_ID(port), - PD_EVENT_RX, 0); } if (USBPD_IS_TX_DONE(port)) { /* clear TX done interrupt */ diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c index 878d2e64f7..d3ea27c8a6 100644 --- a/common/usb_pd_protocol.c +++ b/common/usb_pd_protocol.c @@ -2613,10 +2613,15 @@ void pd_task(void *u) #endif /* process any potential incoming message */ - incoming_packet = evt & PD_EVENT_RX; + incoming_packet = tcpm_has_pending_message(port); if (incoming_packet) { - if (!tcpm_get_message(port, payload, &head)) - handle_request(port, head, payload); + tcpm_dequeue_message(port, payload, &head); + handle_request(port, head, payload); + + /* Check if there are any more messages */ + if (tcpm_has_pending_message(port)) + task_set_event(PD_PORT_TO_TASK_ID(port), + TASK_EVENT_WAKE, 0); } if (pd[port].req_suspend_state) diff --git a/common/usb_pd_tcpc.c b/common/usb_pd_tcpc.c index 9feb0ef2b1..152775bbc0 100644 --- a/common/usb_pd_tcpc.c +++ b/common/usb_pd_tcpc.c @@ -266,7 +266,7 @@ static int rx_buf_is_full(int port) return (diff == 1) || (diff == -RX_BUFFER_SIZE); } -static int rx_buf_is_empty(int port) +int rx_buf_is_empty(int port) { /* Buffer is empty if the head and tail are the same */ return pd[port].rx_buf_tail == pd[port].rx_buf_head; @@ -884,7 +884,7 @@ void pd_task(void *u) void pd_rx_event(int port) { - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_RX, 0); + task_set_event(PD_PORT_TO_TASK_ID(port), TASK_EVENT_WAKE, 0); } int tcpc_alert_status(int port, int *alert) diff --git a/driver/tcpm/anx7447.c b/driver/tcpm/anx7447.c index a6cfb75937..cd06e83b07 100644 --- a/driver/tcpm/anx7447.c +++ b/driver/tcpm/anx7447.c @@ -565,7 +565,7 @@ const struct tcpm_drv anx7447_tcpm_drv = { .set_vconn = &tcpci_tcpm_set_vconn, .set_msg_header = &tcpci_tcpm_set_msg_header, .set_rx_enable = &tcpci_tcpm_set_rx_enable, - .get_message = &tcpci_tcpm_get_message, + .get_message_raw = &tcpci_tcpm_get_message_raw, .transmit = &tcpci_tcpm_transmit, .tcpc_alert = &anx7447_tcpc_alert, #ifdef CONFIG_USB_PD_DISCHARGE_TCPC diff --git a/driver/tcpm/anx74xx.c b/driver/tcpm/anx74xx.c index 669966ddee..9164b9a313 100644 --- a/driver/tcpm/anx74xx.c +++ b/driver/tcpm/anx74xx.c @@ -7,6 +7,7 @@ /* Type-C port manager for Analogix's anx74xx chips */ +#include "console.h" #include "anx74xx.h" #include "task.h" #include "tcpci.h" @@ -23,6 +24,9 @@ #error "Please upgrade your board configuration" #endif +#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) +#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) + struct anx_state { int polarity; int vconn_en; @@ -828,7 +832,7 @@ static int anx74xx_tcpm_get_vbus_level(int port) } #endif -static int anx74xx_tcpm_get_message(int port, uint32_t *payload, int *head) +static int anx74xx_tcpm_get_message_raw(int port, uint32_t *payload, int *head) { int reg = 0, rv = EC_SUCCESS; int len = 0; @@ -910,37 +914,63 @@ static int anx74xx_tcpm_transmit(int port, enum tcpm_transmit_type type, return ret; } +/* + * Don't let the TCPC try to pull from the RX buffer forever. We typical only + * have 1 or 2 messages waiting. + */ +#define MAX_ALLOW_FAILED_RX_READS 10 + void anx74xx_tcpc_alert(int port) { int reg; + int failed_attempts; /* Clear soft irq bit */ tcpc_write(port, ANX74XX_REG_IRQ_EXT_SOURCE_3, - ANX74XX_REG_CLEAR_SOFT_IRQ); - - if (tcpc_read(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG, ®)) - return; - - /* Don't clear msg received bit, until read it is by TCPM */ - tcpc_write(port, ANX74XX_REG_RECVD_MSG_INT, (reg & 0xFE)); - - if (reg & ANX74XX_REG_IRQ_CC_MSG_INT) - /* Set a PD_EVENT_RX */ - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_RX, 0); + ANX74XX_REG_CLEAR_SOFT_IRQ); - if (reg & ANX74XX_REG_IRQ_CC_STATUS_INT) - /* CC status changed, wake task */ - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_CC, 0); + /* Read main alert register for pending alerts */ + reg = 0; + tcpc_read(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG, ®); + /* Prioritize TX completion because PD state machine is waiting */ if (reg & ANX74XX_REG_IRQ_GOOD_CRC_INT) - /* Inform PD about this TX success */ pd_transmit_complete(port, TCPC_TX_COMPLETE_SUCCESS); if (reg & ANX74XX_REG_IRQ_TX_FAIL_INT) - /* let PD does not wait for this */ pd_transmit_complete(port, TCPC_TX_COMPLETE_FAILED); - /* Read and Clear register1 */ + /* Pull all RX messages from TCPC into EC memory */ + failed_attempts = 0; + while (reg & ANX74XX_REG_IRQ_CC_MSG_INT) { + if (tcpm_enqueue_message(port)) + ++failed_attempts; + if (tcpc_read(port, ANX74XX_REG_IRQ_SOURCE_RECV_MSG, ®)) + ++failed_attempts; + + /* 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); + /* + * The port is in a bad state, we don't want to consume + * all EC resources so suspend port forever. + */ + pd_set_suspend(port, 1); + return; + } + } + + /* Clear all pending alerts */ + tcpc_write(port, ANX74XX_REG_RECVD_MSG_INT, reg); + + if (reg & ANX74XX_REG_IRQ_CC_STATUS_INT) + /* CC status changed, wake task */ + task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_CC, 0); + + /* Read and clear extended alert register 1 */ + reg = 0; tcpc_read(port, ANX74XX_REG_IRQ_EXT_SOURCE_1, ®); tcpc_write(port, ANX74XX_REG_IRQ_EXT_SOURCE_1, reg); @@ -949,7 +979,8 @@ void anx74xx_tcpc_alert(int port) /* ANX hardware clears the request bit */ pd_transmit_complete(port, TCPC_TX_COMPLETE_SUCCESS); - /* Read and Clear TCPC Alert register2 */ + /* Read and clear TCPC extended alert register 2 */ + reg = 0; tcpc_read(port, ANX74XX_REG_IRQ_EXT_SOURCE_2, ®); tcpc_write(port, ANX74XX_REG_IRQ_EXT_SOURCE_2, reg); @@ -1054,7 +1085,7 @@ const struct tcpm_drv anx74xx_tcpm_drv = { .set_vconn = &anx74xx_tcpm_set_vconn, .set_msg_header = &anx74xx_tcpm_set_msg_header, .set_rx_enable = &anx74xx_tcpm_set_rx_enable, - .get_message = &anx74xx_tcpm_get_message, + .get_message_raw = &anx74xx_tcpm_get_message_raw, .transmit = &anx74xx_tcpm_transmit, .tcpc_alert = &anx74xx_tcpc_alert, #ifdef CONFIG_USB_PD_DISCHARGE_TCPC diff --git a/driver/tcpm/anx7688.c b/driver/tcpm/anx7688.c index 6cbfba9dcf..c9d6045b44 100644 --- a/driver/tcpm/anx7688.c +++ b/driver/tcpm/anx7688.c @@ -132,7 +132,7 @@ static void anx7688_tcpc_alert(int port) rv = tcpc_read16(port, TCPC_REG_ALERT, &alert); /* process and clear alert status */ - tcpci_tcpm_drv.tcpc_alert(port); + tcpci_tcpc_alert(port); if (!rv && (alert & ANX7688_VENDOR_ALERT)) anx7688_update_hpd_enable(port); @@ -193,7 +193,7 @@ const struct tcpm_drv anx7688_tcpm_drv = { .set_vconn = &tcpci_tcpm_set_vconn, .set_msg_header = &tcpci_tcpm_set_msg_header, .set_rx_enable = &tcpci_tcpm_set_rx_enable, - .get_message = &tcpci_tcpm_get_message, + .get_message_raw = &tcpci_tcpm_get_message_raw, .transmit = &tcpci_tcpm_transmit, .tcpc_alert = &anx7688_tcpc_alert, }; diff --git a/driver/tcpm/fusb302.c b/driver/tcpm/fusb302.c index da5d32e206..68cf14774d 100644 --- a/driver/tcpm/fusb302.c +++ b/driver/tcpm/fusb302.c @@ -699,7 +699,7 @@ static int fusb302_rx_fifo_is_empty(int port) (reg & TCPC_REG_STATUS1_RX_EMPTY); } -static int fusb302_tcpm_get_message(int port, uint32_t *payload, int *head) +static int fusb302_tcpm_get_message_raw(int port, uint32_t *payload, int *head) { /* * This is the buffer that will get the burst-read data @@ -712,10 +712,6 @@ static int fusb302_tcpm_get_message(int port, uint32_t *payload, int *head) uint8_t buf[32]; int rv, len; - /* If our FIFO is empty then we have no packet */ - if (fusb302_rx_fifo_is_empty(port)) - return EC_ERROR_UNKNOWN; - /* Read until we have a non-GoodCRC packet or an empty FIFO */ do { buf[0] = TCPC_REG_FIFOS; @@ -762,13 +758,6 @@ static int fusb302_tcpm_get_message(int port, uint32_t *payload, int *head) memcpy(payload, buf, len); } - /* - * If our FIFO is non-empty then we may have a packet, we may get - * fewer interrupts than packets due to interrupt latency. - */ - if (!fusb302_rx_fifo_is_empty(port)) - task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_RX, 0); - return rv; } @@ -933,8 +922,9 @@ void fusb302_tcpc_alert(int port) /* Packet received and GoodCRC sent */ /* (this interrupt fires after the GoodCRC finishes) */ if (state[port].rx_enable) { - task_set_event(PD_PORT_TO_TASK_ID(port), - PD_EVENT_RX, 0); + /* Pull all RX messages from TCPC into EC memory */ + while (!fusb302_rx_fifo_is_empty(port)) + tcpm_enqueue_message(port); } else { /* flush rx fifo if rx isn't enabled */ fusb302_flush_rx_fifo(port); @@ -971,7 +961,7 @@ const struct tcpm_drv fusb302_tcpm_drv = { .set_vconn = &fusb302_tcpm_set_vconn, .set_msg_header = &fusb302_tcpm_set_msg_header, .set_rx_enable = &fusb302_tcpm_set_rx_enable, - .get_message = &fusb302_tcpm_get_message, + .get_message_raw = &fusb302_tcpm_get_message_raw, .transmit = &fusb302_tcpm_transmit, .tcpc_alert = &fusb302_tcpc_alert, }; diff --git a/driver/tcpm/it83xx.c b/driver/tcpm/it83xx.c index 23f88fe980..24097e1199 100644 --- a/driver/tcpm/it83xx.c +++ b/driver/tcpm/it83xx.c @@ -125,7 +125,7 @@ static enum tcpc_cc_voltage_status it83xx_get_cc( return cc_state; } -static int it83xx_rx_data(enum usbpd_port port, int *head, uint32_t *buf) +static int it83xx_tcpm_get_message_raw(int port, uint32_t *buf, int *head) { int cnt = PD_HEADER_CNT(IT83XX_USBPD_RMH(port)); @@ -518,15 +518,6 @@ static int it83xx_tcpm_set_rx_enable(int port, int enable) return EC_SUCCESS; } -static int it83xx_tcpm_get_message(int port, uint32_t *payload, int *head) -{ - int ret = it83xx_rx_data(port, head, payload); - /* un-mask RX done interrupt */ - IT83XX_USBPD_IMR(port) &= ~USBPD_REG_MASK_MSG_RX_DONE; - - return ret; -} - static int it83xx_tcpm_transmit(int port, enum tcpm_transmit_type type, uint16_t header, @@ -595,7 +586,7 @@ const struct tcpm_drv it83xx_tcpm_drv = { .set_vconn = &it83xx_tcpm_set_vconn, .set_msg_header = &it83xx_tcpm_set_msg_header, .set_rx_enable = &it83xx_tcpm_set_rx_enable, - .get_message = &it83xx_tcpm_get_message, + .get_message_raw = &it83xx_tcpm_get_message_raw, .transmit = &it83xx_tcpm_transmit, .get_chip_info = &it83xx_tcpm_get_chip_info, }; diff --git a/driver/tcpm/mt6370.c b/driver/tcpm/mt6370.c index c7414601a1..d79ef0b691 100644 --- a/driver/tcpm/mt6370.c +++ b/driver/tcpm/mt6370.c @@ -116,7 +116,7 @@ const struct tcpm_drv mt6370_tcpm_drv = { .set_vconn = &tcpci_tcpm_set_vconn, .set_msg_header = &tcpci_tcpm_set_msg_header, .set_rx_enable = &tcpci_tcpm_set_rx_enable, - .get_message = &tcpci_tcpm_get_message, + .get_message_raw = &tcpci_tcpm_get_message_raw, .transmit = &tcpci_tcpm_transmit, .tcpc_alert = &tcpci_tcpc_alert, #ifdef CONFIG_USB_PD_DISCHARGE_TCPC diff --git a/driver/tcpm/ps8xxx.c b/driver/tcpm/ps8xxx.c index 2caceacde8..d58f3cde35 100644 --- a/driver/tcpm/ps8xxx.c +++ b/driver/tcpm/ps8xxx.c @@ -149,7 +149,7 @@ const struct tcpm_drv ps8xxx_tcpm_drv = { .set_vconn = &tcpci_tcpm_set_vconn, .set_msg_header = &tcpci_tcpm_set_msg_header, .set_rx_enable = &tcpci_tcpm_set_rx_enable, - .get_message = &tcpci_tcpm_get_message, + .get_message_raw = &tcpci_tcpm_get_message_raw, .transmit = &ps8xxx_tcpm_transmit, .tcpc_alert = &tcpci_tcpc_alert, #ifdef CONFIG_USB_PD_DISCHARGE_TCPC diff --git a/driver/tcpm/stub.c b/driver/tcpm/stub.c index 5df10dddc6..1d748de6b1 100644 --- a/driver/tcpm/stub.c +++ b/driver/tcpm/stub.c @@ -88,7 +88,12 @@ int tcpm_set_rx_enable(int port, int enable) return tcpc_set_rx_enable(port, enable); } -int tcpm_get_message(int port, uint32_t *payload, int *head) +int tcpm_has_pending_message(int port) +{ + return !rx_buf_is_empty(port); +} + +int tcpm_dequeue_message(int port, uint32_t *payload, int *head) { int ret = tcpc_get_message(port, payload, head); @@ -126,9 +131,8 @@ void tcpc_alert(int port) if (status & TCPC_REG_ALERT_RX_STATUS) { /* * message received. since TCPC is compiled in, we - * already received PD_EVENT_RX from phy layer in - * pd_rx_event(), so we don't need to set another - * event. + * already woke the PD task up from the phy layer via + * pd_rx_event(), so we don't need to wake it again. */ } if (status & TCPC_REG_ALERT_RX_HARD_RST) { diff --git a/driver/tcpm/tcpci.c b/driver/tcpm/tcpci.c index c81d62d8ac..c22d995d71 100644 --- a/driver/tcpm/tcpci.c +++ b/driver/tcpm/tcpci.c @@ -5,7 +5,9 @@ /* Type-C port manager */ +#include "atomic.h" #include "anx74xx.h" +#include "console.h" #include "ec_commands.h" #include "ps8xxx.h" #include "task.h" @@ -18,6 +20,9 @@ #include "usb_pd_tcpc.h" #include "util.h" +#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) +#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) + static int tcpc_vbus[CONFIG_USB_PD_PORT_COUNT]; /* Save the selected rp value */ @@ -338,7 +343,7 @@ int tcpci_tcpm_get_vbus_level(int port) } #endif -int tcpci_tcpm_get_message(int port, uint32_t *payload, int *head) +int tcpci_tcpm_get_message_raw(int port, uint32_t *payload, int *head) { int rv, cnt, reg = TCPC_REG_RX_DATA; @@ -364,6 +369,89 @@ clear: return rv; } +struct cached_tcpm_message { + uint32_t header; + uint32_t payload[7]; +}; + +/* Cache depth needs to be power of 2 */ +#define CACHE_DEPTH (1 << 2) +#define CACHE_DEPTH_MASK (CACHE_DEPTH - 1) + +struct queue { + /* + * Head points to the index of the first empty slot to put a new RX + * message. Must be masked before used in lookup. + */ + uint32_t head; + /* + * Tail points to the index of the first message for the PD task to + * consume. Must be masked before used in lookup. + */ + uint32_t tail; + struct cached_tcpm_message buffer[CACHE_DEPTH]; +}; +static struct queue cached_messages[CONFIG_USB_PD_PORT_COUNT]; + +/* Note this method can be called from an interrupt context. */ +int tcpm_enqueue_message(const int port) +{ + int rv; + struct queue *const q = &cached_messages[port]; + struct cached_tcpm_message *const head = + &q->buffer[q->head & CACHE_DEPTH_MASK]; + + if (q->head - q->tail == CACHE_DEPTH) { + CPRINTS("C%d RX EC Buffer full!", port); + return EC_ERROR_OVERFLOW; + } + + /* Call the raw driver without caching */ + rv = tcpc_config[port].drv->get_message_raw(port, head->payload, + &head->header); + if (rv) { + CPRINTS("C%d: Could not retrieve RX message (%d)", port, rv); + return rv; + } + + /* Increment atomically to ensure get_message_raw happens-before */ + atomic_add(&q->head, 1); + + /* Wake PD task up so it can process incoming RX messages */ + task_set_event(PD_PORT_TO_TASK_ID(port), TASK_EVENT_WAKE, 0); + + return EC_SUCCESS; +} + +int tcpm_has_pending_message(const int port) +{ + const struct queue *const q = &cached_messages[port]; + + return q->head != q->tail; +} + +int tcpm_dequeue_message(const int port, uint32_t *const payload, + int *const header) +{ + struct queue *const q = &cached_messages[port]; + struct cached_tcpm_message *const tail = + &q->buffer[q->tail & CACHE_DEPTH_MASK]; + + if (!tcpm_has_pending_message(port)) { + CPRINTS("C%d No message in RX buffer!"); + return EC_ERROR_BUSY; + } + + /* Copy cache data in to parameters */ + *header = tail->header; + memcpy(payload, tail->payload, sizeof(tail->payload)); + + /* Increment atomically to ensure memcpy happens-before */ + atomic_add(&q->tail, 1); + + return EC_SUCCESS; +} + int tcpci_tcpm_transmit(int port, enum tcpm_transmit_type type, uint16_t header, const uint32_t *data) { @@ -410,27 +498,56 @@ static int register_mask_reset(int port) return 0; } +/* + * Don't let the TCPC try to pull from the RX buffer forever. We typical only + * have 1 or 2 messages waiting. + */ +#define MAX_ALLOW_FAILED_RX_READS 10 + void tcpci_tcpc_alert(int port) { - int status; + int status = 0; + int failed_attempts; uint32_t pd_event = 0; /* Read the Alert register from the TCPC */ tcpm_alert_status(port, &status); - /* - * Check registers to see if we can tell that the TCPC has reset. If - * so, perform tcpc_init inline. - */ - if (register_mask_reset(port)) - pd_event |= PD_EVENT_TCPC_RESET; /* - * Clear alert status for everything except RX_STATUS, which shouldn't - * be cleared until we have successfully retrieved message. + * Check for TX complete first b/c PD state machine waits on TX + * completion events. This will send an event to the PD tasks + * immediately */ - if (status & ~TCPC_REG_ALERT_RX_STATUS) - tcpc_write16(port, TCPC_REG_ALERT, - status & ~TCPC_REG_ALERT_RX_STATUS); + if (status & TCPC_REG_ALERT_TX_COMPLETE) + pd_transmit_complete(port, status & TCPC_REG_ALERT_TX_SUCCESS ? + TCPC_TX_COMPLETE_SUCCESS : + TCPC_TX_COMPLETE_FAILED); + + /* Pull all RX messages from TCPC into EC memory */ + failed_attempts = 0; + while (status & TCPC_REG_ALERT_RX_STATUS) { + if (tcpm_enqueue_message(port)) + ++failed_attempts; + if (tcpm_alert_status(port, &status)) + ++failed_attempts; + + /* 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); + /* + * The port is in a bad state, we don't want to consume + * all EC resources so suspend port forever. + */ + pd_set_suspend(port, 1); + return; + } + } + + /* Clear all pending alert bits */ + if (status) + tcpc_write16(port, TCPC_REG_ALERT, status); if (status & TCPC_REG_ALERT_CC_STATUS) { /* CC status changed, wake task */ @@ -449,21 +566,20 @@ void tcpci_tcpc_alert(int port) pd_event |= TASK_EVENT_WAKE; #endif /* CONFIG_USB_PD_VBUS_DETECT_TCPC && CONFIG_USB_CHARGER */ } - if (status & TCPC_REG_ALERT_RX_STATUS) { - /* message received */ - pd_event |= PD_EVENT_RX; - } if (status & TCPC_REG_ALERT_RX_HARD_RST) { /* hard reset received */ pd_execute_hard_reset(port); pd_event |= TASK_EVENT_WAKE; } - if (status & TCPC_REG_ALERT_TX_COMPLETE) { - /* transmit complete */ - pd_transmit_complete(port, status & TCPC_REG_ALERT_TX_SUCCESS ? - TCPC_TX_COMPLETE_SUCCESS : - TCPC_TX_COMPLETE_FAILED); - } + + /* + * TODO(crbug.com/875274): Remove this check + * + * Check registers to see if we can tell that the TCPC has reset. If + * so, perform a tcpc_init. + */ + if (register_mask_reset(port)) + pd_event |= PD_EVENT_TCPC_RESET; /* * Wait until all possible TCPC accesses in this function are complete @@ -732,7 +848,7 @@ const struct tcpm_drv tcpci_tcpm_drv = { .set_vconn = &tcpci_tcpm_set_vconn, .set_msg_header = &tcpci_tcpm_set_msg_header, .set_rx_enable = &tcpci_tcpm_set_rx_enable, - .get_message = &tcpci_tcpm_get_message, + .get_message_raw = &tcpci_tcpm_get_message_raw, .transmit = &tcpci_tcpm_transmit, .tcpc_alert = &tcpci_tcpc_alert, #ifdef CONFIG_USB_PD_DISCHARGE_TCPC diff --git a/driver/tcpm/tcpci.h b/driver/tcpm/tcpci.h index 1af01d9b60..3b58657f1f 100644 --- a/driver/tcpm/tcpci.h +++ b/driver/tcpm/tcpci.h @@ -137,7 +137,7 @@ int tcpci_tcpm_set_polarity(int port, int polarity); int tcpci_tcpm_set_vconn(int port, int enable); int tcpci_tcpm_set_msg_header(int port, int power_role, int data_role); int tcpci_tcpm_set_rx_enable(int port, int enable); -int tcpci_tcpm_get_message(int port, uint32_t *payload, int *head); +int tcpci_tcpm_get_message_raw(int port, uint32_t *payload, int *head); int tcpci_tcpm_transmit(int port, enum tcpm_transmit_type type, uint16_t header, const uint32_t *data); int tcpci_tcpm_release(int port); diff --git a/driver/tcpm/tcpm.h b/driver/tcpm/tcpm.h index 0ea0bf47a7..a25ef6508b 100644 --- a/driver/tcpm/tcpm.h +++ b/driver/tcpm/tcpm.h @@ -160,13 +160,14 @@ static inline int tcpm_set_rx_enable(int port, int enable) return tcpc_config[port].drv->set_rx_enable(port, enable); } -static inline int tcpm_get_message(int port, uint32_t *payload, int *head) -{ - return tcpc_config[port].drv->get_message(port, payload, head); -} +/** + * Reads a message using get_message_raw driver method and puts it into EC's + * cache. + */ +int tcpm_enqueue_message(int port); static inline int tcpm_transmit(int port, enum tcpm_transmit_type type, - uint16_t header, const uint32_t *data) + uint16_t header, const uint32_t *data) { return tcpc_config[port].drv->transmit(port, type, header, data); } @@ -333,17 +334,6 @@ int tcpm_set_msg_header(int port, int power_role, int data_role); int tcpm_set_rx_enable(int port, int enable); /** - * Read last received PD message. - * - * @param port Type-C port number - * @param payload Pointer to location to copy payload of message - * @param header of message - * - * @return EC_SUCCESS or error - */ -int tcpm_get_message(int port, uint32_t *payload, int *head); - -/** * Transmit PD message * * @param port Type-C port number @@ -366,4 +356,20 @@ void tcpc_alert(int port); #endif +/** + * Gets the next waiting RX message. + * + * @param port Type-C port number + * @param payload Pointer to location to copy payload of PD message + * @param header The header of PD message + * + * @return EC_SUCCESS or error + */ +int tcpm_dequeue_message(int port, uint32_t *payload, int *header); + +/** + * Returns true if the tcpm has RX messages waiting to be consumed. + */ +int tcpm_has_pending_message(int port); + #endif diff --git a/include/usb_pd.h b/include/usb_pd.h index 5675bac4ef..643ed309aa 100644 --- a/include/usb_pd.h +++ b/include/usb_pd.h @@ -38,7 +38,6 @@ enum pd_rx_errors { }; /* Events for USB PD task */ -#define PD_EVENT_RX (1<<2) /* Incoming packet event */ #define PD_EVENT_TX (1<<3) /* Outgoing packet event */ #define PD_EVENT_CC (1<<4) /* CC line change event */ #define PD_EVENT_TCPC_RESET (1<<5) /* TCPC has reset */ diff --git a/include/usb_pd_tcpc.h b/include/usb_pd_tcpc.h index 1b93cd822c..2c2da66c84 100644 --- a/include/usb_pd_tcpc.h +++ b/include/usb_pd_tcpc.h @@ -57,5 +57,6 @@ int tcpc_set_rx_enable(int port, int enable); 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); #endif /* __CROS_EC_USB_PD_TCPC_H */ diff --git a/include/usb_pd_tcpm.h b/include/usb_pd_tcpm.h index 08402dda07..1c600c8d7e 100644 --- a/include/usb_pd_tcpm.h +++ b/include/usb_pd_tcpm.h @@ -158,7 +158,7 @@ struct tcpm_drv { int (*set_rx_enable)(int port, int enable); /** - * Read last received PD message. + * Read received PD message from the TCPC * * @param port Type-C port number * @param payload Pointer to location to copy payload of message @@ -166,7 +166,7 @@ struct tcpm_drv { * * @return EC_SUCCESS or error */ - int (*get_message)(int port, uint32_t *payload, int *head); + int (*get_message_raw)(int port, uint32_t *payload, int *head); /** * Transmit PD message |