diff options
-rw-r--r-- | driver/tcpm/fusb302.c | 116 |
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 & 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); } |