| From foo@baz Fri Mar 16 15:43:17 CET 2018 |
| From: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> |
| Date: Tue, 21 Nov 2017 14:12:12 +0000 |
| Subject: typec: tcpm: fusb302: Resolve out of order messaging events |
| |
| From: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> |
| |
| |
| [ Upstream commit ab69f61321140ff632d560775bc226259a78dfa2 ] |
| |
| The expectation in the FUSB302 driver is that a TX_SUCCESS event |
| should occur after a message has been sent, but before a GCRCSENT |
| event is raised to indicate successful receipt of a message from |
| the partner. However in some circumstances it is possible to see |
| the hardware raise a GCRCSENT event before a TX_SUCCESS event |
| is raised. The upshot of this is that the GCRCSENT handling portion |
| of code ends up reporting the GoodCRC message to TCPM because the |
| TX_SUCCESS event hasn't yet arrived to trigger a consumption of it. |
| When TX_SUCCESS is then raised by the chip it ends up consuming the |
| actual message that was meant for TCPM, and this incorrect sequence |
| results in a hard reset from TCPM. |
| |
| To avoid this problem, this commit updates the message reading |
| code to check whether a GoodCRC message was received or not. Based |
| on this check it will either report that the previous transmission |
| has completed or it will pass the msg data to TCPM for futher |
| processing. This way the incorrect ordering of the events no longer |
| matters. |
| |
| Signed-off-by: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> |
| Reviewed-by: Guenter Roeck <linux@roeck-us.net> |
| Acked-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Sasha Levin <alexander.levin@microsoft.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/staging/typec/fusb302/fusb302.c | 21 +++++++++++++++++---- |
| 1 file changed, 17 insertions(+), 4 deletions(-) |
| |
| --- a/drivers/staging/typec/fusb302/fusb302.c |
| +++ b/drivers/staging/typec/fusb302/fusb302.c |
| @@ -1552,6 +1552,21 @@ static int fusb302_pd_read_message(struc |
| fusb302_log(chip, "PD message header: %x", msg->header); |
| fusb302_log(chip, "PD message len: %d", len); |
| |
| + /* |
| + * Check if we've read off a GoodCRC message. If so then indicate to |
| + * TCPM that the previous transmission has completed. Otherwise we pass |
| + * the received message over to TCPM for processing. |
| + * |
| + * We make this check here instead of basing the reporting decision on |
| + * the IRQ event type, as it's possible for the chip to report the |
| + * TX_SUCCESS and GCRCSENT events out of order on occasion, so we need |
| + * to check the message type to ensure correct reporting to TCPM. |
| + */ |
| + if ((!len) && (pd_header_type_le(msg->header) == PD_CTRL_GOOD_CRC)) |
| + tcpm_pd_transmit_complete(chip->tcpm_port, TCPC_TX_SUCCESS); |
| + else |
| + tcpm_pd_receive(chip->tcpm_port, msg); |
| + |
| return ret; |
| } |
| |
| @@ -1659,13 +1674,12 @@ static irqreturn_t fusb302_irq_intn(int |
| |
| if (interrupta & FUSB_REG_INTERRUPTA_TX_SUCCESS) { |
| fusb302_log(chip, "IRQ: PD tx success"); |
| - /* read out the received good CRC */ |
| ret = fusb302_pd_read_message(chip, &pd_msg); |
| if (ret < 0) { |
| - fusb302_log(chip, "cannot read in GCRC, ret=%d", ret); |
| + fusb302_log(chip, |
| + "cannot read in PD message, ret=%d", ret); |
| goto done; |
| } |
| - tcpm_pd_transmit_complete(chip->tcpm_port, TCPC_TX_SUCCESS); |
| } |
| |
| if (interrupta & FUSB_REG_INTERRUPTA_HARDRESET) { |
| @@ -1686,7 +1700,6 @@ static irqreturn_t fusb302_irq_intn(int |
| "cannot read in PD message, ret=%d", ret); |
| goto done; |
| } |
| - tcpm_pd_receive(chip->tcpm_port, &pd_msg); |
| } |
| done: |
| mutex_unlock(&chip->lock); |