[PATCH 06/11] usb: tcpm: add setup_host_mode/device_mode/disable_src_vbus APIs

Peng Fan (OSS) peng.fan at oss.nxp.com
Wed Jun 17 10:23:05 CEST 2026


From: Peng Fan <peng.fan at nxp.com>

Add lightweight Type-C mode setup functions that bypass the TCPM state
machine and call TCPCI driver ops directly:

- tcpm_setup_host_mode(): set CC=Rp, detect device, set polarity,
  source VBUS
- tcpm_setup_device_mode(): set CC=Rd, detect host, set polarity
- tcpm_disable_src_vbus(): stop sourcing VBUS

These are used by board_usb_init/cleanup to configure the Type-C port
for USB host or device mode without running the full PD state machine.

Signed-off-by: Peng Fan <peng.fan at nxp.com>
---
 drivers/usb/tcpm/tcpm-uclass.c | 87 ++++++++++++++++++++++++++++++++++++++++++
 include/usb/tcpm.h             |  4 ++
 2 files changed, 91 insertions(+)

diff --git a/drivers/usb/tcpm/tcpm-uclass.c b/drivers/usb/tcpm/tcpm-uclass.c
index d4fe260e0db..e0f7ce62a79 100644
--- a/drivers/usb/tcpm/tcpm-uclass.c
+++ b/drivers/usb/tcpm/tcpm-uclass.c
@@ -8,6 +8,7 @@
 #include <dm/device.h>
 #include <dm/device_compat.h>
 #include <dm/uclass.h>
+#include <linux/delay.h>
 #include <linux/err.h>
 #include <usb/tcpm.h>
 #include "tcpm-internal.h"
@@ -140,6 +141,92 @@ static int tcpm_post_bind(struct udevice *dev)
 	return 0;
 }
 
+int tcpm_setup_host_mode(struct udevice *dev, enum typec_cc_polarity *polarity)
+{
+	const struct dm_tcpm_ops *ops = dev_get_driver_ops(dev);
+	enum typec_cc_status cc1, cc2;
+	int ret;
+
+	ops->set_vbus(dev, false, false);
+
+	ops->set_cc(dev, TYPEC_CC_RP_DEF);
+
+	mdelay(100);
+
+	ret = ops->get_cc(dev, &cc1, &cc2);
+	if (ret) {
+		dev_err(dev, "Failed to get cc for host mode: %d\n", ret);
+		return ret;
+	}
+
+	if (cc1 == TYPEC_CC_RD) {
+		*polarity = TYPEC_POLARITY_CC1;
+	} else if (cc2 == TYPEC_CC_RD) {
+		*polarity = TYPEC_POLARITY_CC2;
+	} else {
+		dev_err(dev, "no device detected (cc1=%d cc2=%d)\n", cc1, cc2);
+		return -ENODEV;
+	}
+
+	ret = ops->set_polarity(dev, *polarity);
+	if (ret)
+		return ret;
+
+	ops->set_vbus(dev, true, false);
+	mdelay(300);
+
+	return 0;
+}
+
+int tcpm_setup_device_mode(struct udevice *dev, enum typec_cc_polarity *polarity)
+{
+	const struct dm_tcpm_ops *ops = dev_get_driver_ops(dev);
+	enum typec_cc_status cc1, cc2;
+	int ret, retry;
+
+	ops->set_vbus(dev, false, false);
+
+	ops->set_cc(dev, TYPEC_CC_RD);
+
+	if (ops->start_toggling)
+		ops->start_toggling(dev, TYPEC_PORT_SNK, TYPEC_CC_RD);
+
+	for (retry = 0; retry < 20; retry++) {
+		mdelay(100);
+
+		ret = ops->get_cc(dev, &cc1, &cc2);
+		if (ret) {
+			dev_err(dev, "Failed to get cc for device mode: %d\n", ret);
+			return ret;
+		}
+
+		if (cc1 != TYPEC_CC_OPEN || cc2 != TYPEC_CC_OPEN)
+			break;
+	}
+
+	if (cc1 != TYPEC_CC_OPEN && cc2 == TYPEC_CC_OPEN) {
+		*polarity = TYPEC_POLARITY_CC1;
+	} else if (cc1 == TYPEC_CC_OPEN && cc2 != TYPEC_CC_OPEN) {
+		*polarity = TYPEC_POLARITY_CC2;
+	} else {
+		dev_err(dev, "no host detected (cc1=%d cc2=%d)\n", cc1, cc2);
+		return -ENODEV;
+	}
+
+	ret = ops->set_polarity(dev, *polarity);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int tcpm_disable_src_vbus(struct udevice *dev)
+{
+	const struct dm_tcpm_ops *ops = dev_get_driver_ops(dev);
+
+	return ops->set_vbus(dev, false, false);
+}
+
 UCLASS_DRIVER(tcpm) = {
 	.id		= UCLASS_TCPM,
 	.name		= "tcpm",
diff --git a/include/usb/tcpm.h b/include/usb/tcpm.h
index 10f0515fe12..caa67a5639d 100644
--- a/include/usb/tcpm.h
+++ b/include/usb/tcpm.h
@@ -96,4 +96,8 @@ enum typec_data_role tcpm_get_data_role(struct udevice *dev);
 bool tcpm_is_connected(struct udevice *dev);
 const char *tcpm_get_state(struct udevice *dev);
 
+int tcpm_setup_host_mode(struct udevice *dev, enum typec_cc_polarity *polarity);
+int tcpm_setup_device_mode(struct udevice *dev, enum typec_cc_polarity *polarity);
+int tcpm_disable_src_vbus(struct udevice *dev);
+
 #endif /* __LINUX_USB_TCPM_H */

-- 
2.51.0



More information about the U-Boot mailing list