From: Peng Fan <[email protected]>

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 <[email protected]>
---
 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

Reply via email to