summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/rainier/board.h1
-rw-r--r--board/reef_it8320/board.h1
-rw-r--r--board/scarlet/board.h1
-rw-r--r--chip/it83xx/intc.c6
-rw-r--r--common/usb_pd_protocol.c11
-rw-r--r--common/usb_pd_tcpc.c4
-rw-r--r--driver/tcpm/anx7447.c2
-rw-r--r--driver/tcpm/anx74xx.c71
-rw-r--r--driver/tcpm/anx7688.c4
-rw-r--r--driver/tcpm/fusb302.c20
-rw-r--r--driver/tcpm/it83xx.c13
-rw-r--r--driver/tcpm/mt6370.c2
-rw-r--r--driver/tcpm/ps8xxx.c2
-rw-r--r--driver/tcpm/stub.c12
-rw-r--r--driver/tcpm/tcpci.c164
-rw-r--r--driver/tcpm/tcpci.h2
-rw-r--r--driver/tcpm/tcpm.h38
-rw-r--r--include/usb_pd.h1
-rw-r--r--include/usb_pd_tcpc.h1
-rw-r--r--include/usb_pd_tcpm.h4
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, &reg))
- 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, &reg);
+ /* 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, &reg))
+ ++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, &reg);
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, &reg);
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