summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJett Rink <jettrink@chromium.org>2018-08-17 13:33:02 -0600
committerchrome-bot <chrome-bot@chromium.org>2018-09-07 18:36:36 -0700
commite8070b44b088e99e38ec741ed8091c7e4a033bef (patch)
treededc7bce6201fba05b92f4e48ec4405366fbc76b
parente6a13850e9b5a6b2889549c03caddf2944e7fc63 (diff)
downloadchrome-ec-e8070b44b088e99e38ec741ed8091c7e4a033bef.tar.gz
tcpm: add TCPC RX circular buffer in EC
The alert line for TCPC will stay asserted as long as there are RX messages for the TCPM (i.e. EC) to pull from the TCPC. We should clear all of the RX messages we know about during a single alert handling session. This CL can stand on its own, but it is a part of a CL stack that will tighten the critical section of time between received messages from the TCPC and sending follow up message out through the TCPC. See go/usb-pd-slow-response-time for more details. BRANCH=none BUG=b:112088135,b:112344286,b:111909282,b:112848644,b:113124761 BUG=b:113057273,b:112825261 TEST=Reduces reset issue in most cases for phaser, bobba. Does not seem to adversely affect state machine negotiation. Full CL stack consistently sends a REQUEST at 18ms after a SRC_CAP GoodCRC, which is well below the 24 ms threshold we need to be under for USB PD spec compliance. Also testing pd_suspend scenario manually and EC was responsive after port 1 suspend because of "bad behavior" Change-Id: I1654b46400e9881f2927a5f6d6ace589edd182de Signed-off-by: Jett Rink <jettrink@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1185727
-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 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, &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);
@@ -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, &reg);
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