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 ff2a5b26d6..c37e26a4aa 100644 --- a/board/rainier/board.h +++ b/board/rainier/board.h @@ -90,6 +90,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 ADC_VBUS -1 #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 a3c9683c7c..ba99004661 100644 --- a/board/scarlet/board.h +++ b/board/scarlet/board.h @@ -112,6 +112,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 ADC_VBUS -1 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 d7a215aa3b..275d04c7ab 100644 --- a/common/usb_pd_protocol.c +++ b/common/usb_pd_protocol.c @@ -2439,10 +2439,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 35789e69bd..47e9b56721 100644 --- a/driver/tcpm/anx7447.c +++ b/driver/tcpm/anx7447.c @@ -540,7 +540,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 ad86307862..b6abf718d1 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; @@ -818,7 +822,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; @@ -900,37 +904,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); @@ -939,7 +969,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); @@ -1036,7 +1067,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 6b73108739..6d75d94c51 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); @@ -194,7 +194,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 f89f47896e..de54b5304f 100644 --- a/driver/tcpm/fusb302.c +++ b/driver/tcpm/fusb302.c @@ -701,7 +701,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 @@ -714,10 +714,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; @@ -764,13 +760,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; } @@ -935,8 +924,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); @@ -973,7 +963,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 f34b9271f2..09edeb5d6c 100644 --- a/driver/tcpm/it83xx.c +++ b/driver/tcpm/it83xx.c @@ -104,7 +104,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)); @@ -470,15 +470,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, @@ -537,7 +528,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 a9881b34d1..cebb7ae348 100644 --- a/driver/tcpm/mt6370.c +++ b/driver/tcpm/mt6370.c @@ -114,7 +114,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 4dc601599e..7e81241614 100644 --- a/driver/tcpm/ps8xxx.c +++ b/driver/tcpm/ps8xxx.c @@ -175,7 +175,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 d4518ae608..411fd72569 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 */ @@ -225,7 +230,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; @@ -255,6 +260,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) { @@ -306,27 +394,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 */ @@ -345,21 +462,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 @@ -596,7 +712,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 c7cefc4ba4..2ddefba3e3 100644 --- a/driver/tcpm/tcpci.h +++ b/driver/tcpm/tcpci.h @@ -136,7 +136,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 091527e68a..00f753ecf2 100644 --- a/driver/tcpm/tcpm.h +++ b/driver/tcpm/tcpm.h @@ -132,13 +132,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); } @@ -298,17 +299,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 @@ -331,4 +321,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 0d464cc4cd..1f53fa7914 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 1b77e40450..35987e3965 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 |