diff options
author | Shawn Nematbakhsh <shawnn@chromium.org> | 2016-12-09 12:25:20 -0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2016-12-14 20:14:38 -0800 |
commit | 94d27d9779de6fe18c33945768abf1e97383c814 (patch) | |
tree | f2befee4ad58d1b812b5982b552008ddd0925d91 /driver | |
parent | 8a8af6c10ee6973d17b818f65311fc6f12925f09 (diff) | |
download | chrome-ec-94d27d9779de6fe18c33945768abf1e97383c814.tar.gz |
tcpm: fusb302: Don't mistake meaningful packets for GOOD_CRC
If a partner port sends a packet at approximately the same time as we
send a packet, we may end up with the initial packet followed by the
GOOD_CRC reply in our HW FIFO. Don't automatically discard the first
packet in the FIFO. Instead, discard the packet only if it's a GOOD_CRC
packet. And, modify our get_message function to automatically discard
GOOD_CRC in search of a meaningful packet.
In addition, due to interrupt latency, we can't rely on receiving one
interrupt per incoming packet. If our Rx FIFO is non-empty, assume that
it contains at least one packet.
BUG=chrome-os-partner:60242
BRANCH=gru
TEST=Manual on kevin, attach Apple dongle with no inputs. Attach zinger,
verify we negotiate to ~20V. Repeat 10x and verify negotiation is
successful each time.
Signed-off-by: Shawn Nematbakhsh <shawnn@chromium.org>
Change-Id: I7e4430fc2b7ed44b1aa4b561d72c8e1e964b245a
Reviewed-on: https://chromium-review.googlesource.com/414927
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
(cherry picked from commit 373f5dfd75e8ea5074d26fc6392eafe83de4f905)
Reviewed-on: https://chromium-review.googlesource.com/419186
Commit-Ready: Shawn N <shawnn@chromium.org>
Tested-by: Shawn N <shawnn@chromium.org>
Reviewed-by: Shawn N <shawnn@chromium.org>
Diffstat (limited to 'driver')
-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); } |