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

