summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJett Rink <jettrink@chromium.org>2018-08-17 13:33:02 -0600
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2019-04-15 17:06:41 +0000
commitd69d3ff853ead2b2475239f683c65a617b9ab7f6 (patch)
treebf493cf945c39b6392de23998467a2e8fc8a7955
parentc129add2a2711af4322622b0db7c419ac1739de6 (diff)
downloadchrome-ec-d69d3ff853ead2b2475239f683c65a617b9ab7f6.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 BUG=b/127896055 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 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1566036 Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org> Commit-Queue: Daisuke Nojiri <dnojiri@chromium.org> Tested-by: Daisuke Nojiri <dnojiri@chromium.org>
-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