]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
usb: typec: fusb302: cache PD RX state
authorSebastian Reichel <sebastian.reichel@collabora.com>
Fri, 4 Jul 2025 17:55:06 +0000 (19:55 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 21 Jul 2025 14:32:49 +0000 (16:32 +0200)
This patch fixes a race condition communication error, which ends up in
PD hard resets when losing the race. Some systems, like the Radxa ROCK
5B are powered through USB-C without any backup power source and use a
FUSB302 chip to do the PD negotiation. This means it is quite important
to avoid hard resets, since that effectively kills the system's
power-supply.

I've found the following race condition while debugging unplanned power
loss during booting the board every now and then:

1. lots of TCPM/FUSB302/PD initialization stuff
2. TCPM ends up in SNK_WAIT_CAPABILITIES (tcpm_set_pd_rx is enabled here)
3. the remote PD source does not send anything, so TCPM does a SOFT RESET
4. TCPM ends up in SNK_WAIT_CAPABILITIES for the second time
   (tcpm_set_pd_rx is enabled again, even though it is still on)

At this point I've seen broken CRC good messages being send by the
FUSB302 with a logic analyzer sniffing the CC lines. Also it looks like
messages are being lost and things generally going haywire with one of
the two sides doing a hard reset once a broken CRC good message was send
to the bus.

I think the system is running into a race condition, that the FIFOs are
being cleared and/or the automatic good CRC message generation flag is
being updated while a message is already arriving.

Let's avoid this by caching the PD RX enabled state, as we have already
processed anything in the FIFOs and are in a good state. As a side
effect that this also optimizes I2C bus usage :)

As far as I can tell the problem theoretically also exists when TCPM
enters SNK_WAIT_CAPABILITIES the first time, but I believe this is less
critical for the following reason:

On devices like the ROCK 5B, which are powered through a TCPM backed
USB-C port, the bootloader must have done some prior PD communication
(initial communication must happen within 5 seconds after plugging the
USB-C plug). This means the first time the kernel TCPM state machine
reaches SNK_WAIT_CAPABILITIES, the remote side is not sending messages
actively. On other devices a hard reset simply adds some extra delay and
things should be good afterwards.

Fixes: c034a43e72dda ("staging: typec: Fairchild FUSB302 Type-c chip driver")
Cc: stable <stable@kernel.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20250704-fusb302-race-condition-fix-v1-1-239012c0e27a@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/typec/tcpm/fusb302.c

index f2801279c4b5dd04e1332347af3aa1af1d3bf5e8..a4ff2403ddd66fb7aaa27fd890533c7aff1dc163 100644 (file)
@@ -104,6 +104,7 @@ struct fusb302_chip {
        bool vconn_on;
        bool vbus_on;
        bool charge_on;
+       bool pd_rx_on;
        bool vbus_present;
        enum typec_cc_polarity cc_polarity;
        enum typec_cc_status cc1;
@@ -841,6 +842,11 @@ static int tcpm_set_pd_rx(struct tcpc_dev *dev, bool on)
        int ret = 0;
 
        mutex_lock(&chip->lock);
+       if (chip->pd_rx_on == on) {
+               fusb302_log(chip, "pd is already %s", str_on_off(on));
+               goto done;
+       }
+
        ret = fusb302_pd_rx_flush(chip);
        if (ret < 0) {
                fusb302_log(chip, "cannot flush pd rx buffer, ret=%d", ret);
@@ -863,6 +869,8 @@ static int tcpm_set_pd_rx(struct tcpc_dev *dev, bool on)
                            str_on_off(on), ret);
                goto done;
        }
+
+       chip->pd_rx_on = on;
        fusb302_log(chip, "pd := %s", str_on_off(on));
 done:
        mutex_unlock(&chip->lock);