[PATCH v2 05/10] usb: tcpm: add setup_host_mode/device_mode/disable_src_vbus APIs
Peng Fan (OSS)
peng.fan at oss.nxp.com
Sun Jun 21 04:06:41 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