summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--driver/tcpm/fusb302.c116
1 files changed, 75 insertions, 41 deletions
diff --git a/driver/tcpm/fusb302.c b/driver/tcpm/fusb302.c
index 3bf20f52d4..9bdde5e591 100644
--- a/driver/tcpm/fusb302.c
+++ b/driver/tcpm/fusb302.c
@@ -17,6 +17,9 @@
#include "usb_pd_tcpc.h"
#include "util.h"
+#define PACKET_IS_GOOD_CRC(head) (PD_HEADER_TYPE(head) == PD_CTRL_GOOD_CRC && \
+ PD_HEADER_CNT(head) == 0)
+
static struct fusb302_chip_state {
int cc_polarity;
int vconn_enabled;
@@ -27,12 +30,19 @@ static struct fusb302_chip_state {
uint8_t mdac_rd;
} state[CONFIG_USB_PD_PORT_COUNT];
-/* bring the FUSB302 out of reset after Hard Reset signaling */
+/*
+ * Bring the FUSB302 out of reset after Hard Reset signaling. This will
+ * automatically flush both the Rx and Tx FIFOs.
+ */
static void fusb302_pd_reset(int port)
{
tcpc_write(port, TCPC_REG_RESET, TCPC_REG_RESET_PD_RESET);
}
+/*
+ * Flush our Rx FIFO. To prevent packet framing issues, this function should
+ * only be called when Rx is disabled.
+ */
static void fusb302_flush_rx_fifo(int port)
{
/*
@@ -658,56 +668,84 @@ static int fusb302_tcpm_set_rx_enable(int port, int enable)
return 0;
}
+/* Return true if our Rx FIFO is empty */
+static int fusb302_rx_fifo_is_empty(int port)
+{
+ int reg;
+
+ return (!tcpc_read(port, TCPC_REG_STATUS1, &reg)) &&
+ (reg & TCPC_REG_STATUS1_RX_EMPTY);
+}
+
static int fusb302_tcpm_get_message(int port, uint32_t *payload, int *head)
{
/*
- * this is the buffer that will get the burst-read data
+ * This is the buffer that will get the burst-read data
* from the fusb302.
*
- * it's re-used in a couple different spots, the worst of which
+ * It's re-used in a couple different spots, the worst of which
* is the PD packet (not header) and CRC.
* maximum size necessary = 28 + 4 = 32
*/
uint8_t buf[32];
- int rv = 0;
- int len;
+ int rv, len;
- /* NOTE: Assuming enough memory has been allocated for payload. */
+ /* If our FIFO is empty then we have no packet */
+ if (fusb302_rx_fifo_is_empty(port))
+ return EC_ERROR_UNKNOWN;
- /*
- * PART 1 OF BURST READ: Write in register address.
- * Issue a START, no STOP.
- */
- tcpc_lock(port, 1);
- buf[0] = TCPC_REG_FIFOS;
- rv |= tcpc_xfer(port, buf, 1, 0, 0, I2C_XFER_START);
+ /* Read until we have a non-GoodCRC packet or an empty FIFO */
+ do {
+ buf[0] = TCPC_REG_FIFOS;
+ tcpc_lock(port, 1);
- /*
- * PART 2 OF BURST READ: Read up to the header.
- * Issue a repeated START, no STOP.
- * only grab three bytes so we can get the header
- * and determine how many more bytes we need to read.
- */
- rv |= tcpc_xfer(port, 0, 0, buf, 3, I2C_XFER_START);
+ /*
+ * PART 1 OF BURST READ: Write in register address.
+ * Issue a START, no STOP.
+ */
+ rv = tcpc_xfer(port, buf, 1, 0, 0, I2C_XFER_START);
- /* Grab the header */
- *head = (buf[1] & 0xFF);
- *head |= ((buf[2] << 8) & 0xFF00);
+ /*
+ * PART 2 OF BURST READ: Read up to the header.
+ * Issue a repeated START, no STOP.
+ * only grab three bytes so we can get the header
+ * and determine how many more bytes we need to read.
+ * TODO: Check token to ensure valid packet.
+ */
+ rv |= tcpc_xfer(port, 0, 0, buf, 3, I2C_XFER_START);
- /* figure out packet length, subtract header bytes */
- len = get_num_bytes(*head) - 2;
+ /* Grab the header */
+ *head = (buf[1] & 0xFF);
+ *head |= ((buf[2] << 8) & 0xFF00);
- /*
- * PART 3 OF BURST READ: Read everything else.
- * No START, but do issue a STOP at the end.
- * add 4 to len to read CRC out
- */
- rv |= tcpc_xfer(port, 0, 0, buf, len+4, I2C_XFER_STOP);
+ /* figure out packet length, subtract header bytes */
+ len = get_num_bytes(*head) - 2;
- tcpc_lock(port, 0);
+ /*
+ * PART 3 OF BURST READ: Read everything else.
+ * No START, but do issue a STOP at the end.
+ * add 4 to len to read CRC out
+ */
+ rv |= tcpc_xfer(port, 0, 0, buf, len+4, I2C_XFER_STOP);
- /* return the data */
- memcpy(payload, buf, len);
+ tcpc_lock(port, 0);
+ } while (!rv && PACKET_IS_GOOD_CRC(*head) &&
+ !fusb302_rx_fifo_is_empty(port));
+
+ if (!rv) {
+ /* Discard GoodCRC packets */
+ if (PACKET_IS_GOOD_CRC(*head))
+ rv = EC_ERROR_UNKNOWN;
+ else
+ 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;
}
@@ -800,8 +838,6 @@ void fusb302_tcpc_alert(int port)
int interrupt;
int interrupta;
int interruptb;
- int head;
- uint32_t payload[7];
/* reading interrupt registers clears them */
@@ -826,12 +862,10 @@ void fusb302_tcpc_alert(int port)
pd_transmit_complete(port, TCPC_TX_COMPLETE_FAILED);
}
+ /* GoodCRC was received, our FIFO is now non-empty */
if (interrupta & TCPC_REG_INTERRUPTA_TX_SUCCESS) {
- /*
- * Sent packet was acknowledged with a GoodCRC,
- * so remove GoodCRC message from FIFO.
- */
- tcpm_get_message(port, payload, &head);
+ task_set_event(PD_PORT_TO_TASK_ID(port),
+ PD_EVENT_RX, 0);
pd_transmit_complete(port, TCPC_TX_COMPLETE_SUCCESS);
}