[PATCH v1 4/4] usb: tcpm: improve data role mismatch error recovery

Sebastian Reichel sebastian.reichel at collabora.com
Thu Oct 31 18:50:33 CET 2024


On Radxa ROCK 5B I managed to get U-Boot into an endless loop of
printing

fusb302 usb-typec at 22: TCPM: data role mismatch, initiating error recovery

messages by changing the data role in Linux and then rebooting the
system. This is happening because the external device (A cheap USB-C hub
powered through a USB-C PD power-supply) kept its state and the error
recovery path for non self-powered devices is not enough to change it.

Avoid this by swapping our own data role when the error recovery stage
is reached for a port, which is not self-powered. Right now data support
is limited anyways and once proper support is added we can use the data
role swap request to get the desired data direction after the initial
negotiation completed.

Fixes: 1db4c0ac77e3 ("usb: tcpm: add core framework")
Signed-off-by: Sebastian Reichel <sebastian.reichel at collabora.com>
---
 drivers/usb/tcpm/tcpm.c | 30 +++++++++++++++++++++++++++---
 1 file changed, 27 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/tcpm/tcpm.c b/drivers/usb/tcpm/tcpm.c
index 909fe2ef4fcb..12d66d470f9a 100644
--- a/drivers/usb/tcpm/tcpm.c
+++ b/drivers/usb/tcpm/tcpm.c
@@ -833,6 +833,28 @@ static void tcpm_pd_ctrl_request(struct udevice *dev,
 	}
 }
 
+static void tcpm_recover_data_role_mismatch(struct udevice *dev)
+{
+	struct tcpm_port *port = dev_get_uclass_plat(dev);
+
+	dev_err(dev, "TCPM: data role mismatch, initiating error recovery\n");
+	if (port->self_powered) {
+		tcpm_set_state(dev, ERROR_RECOVERY, 0);
+		return;
+	}
+
+	/*
+	 * The error recovery will not help for devices, which are not
+	 * self-powered because the error recovery avoids killing the board
+	 * power. Since this can happen early on sending
+	 * a DR_SWAP request is not sensible. Instead let's change our own
+	 * data role. It can be swapped back once USB-PD reached the ready
+	 * state.
+	 */
+	tcpm_set_roles(dev, true, port->pwr_role,
+		       port->data_role == TYPEC_HOST ? TYPEC_DEVICE : TYPEC_HOST);
+}
+
 static void tcpm_pd_rx_handler(struct udevice *dev,
 			       const struct pd_message *msg)
 {
@@ -867,9 +889,11 @@ static void tcpm_pd_rx_handler(struct udevice *dev,
 		remote_is_host = !!(le16_to_cpu(msg->header) & PD_HEADER_DATA_ROLE);
 		local_is_host = port->data_role == TYPEC_HOST;
 		if (remote_is_host == local_is_host) {
-			dev_err(dev, "TCPM: data role mismatch, initiating error recovery\n");
-			tcpm_set_state(dev, ERROR_RECOVERY, 0);
-		} else {
+			tcpm_recover_data_role_mismatch(dev);
+			local_is_host = port->data_role == TYPEC_HOST;
+		}
+
+		if (remote_is_host != local_is_host) {
 			if (cnt)
 				tcpm_pd_data_request(dev, msg);
 			else
-- 
2.45.2



More information about the U-Boot mailing list