Hi Sebastian, On 2024-09-03 20:16, Sebastian Reichel wrote: > This adds TCPM framework in preparation for fusb302 support, which can > handle USB power delivery messages. This is needed to solve issues with > devices, that are running from a USB-C port supporting USB-PD, but not > having a battery. > > Such a device currently boots to the kernel without interacting with > the power-supply at all. If there are no USB-PD message replies within > 5 seconds, the power-supply assumes the peripheral is not capable of > USB-PD. It usually takes more than 5 seconds for the system to reach > the kernel and probe the I2C based fusb302 chip driver. Thus the > system always runs into this state. The power-supply's solution to > fix this error state is a hard reset, which involves removing the > power from VBUS. Boards without a battery (or huge capacitors) will > reset at this point resulting in a boot loop. > > This imports the TCPM framework from the kernel. The porting has > originally been done by Rockchip using hardware timers and the Linux > kernel's TCPM code from some years ago. > > I had a look at upgrading to the latest TCPM kernel code, but that > beast became a lot more complex due to adding more USB-C features. > I believe these features are not needed in U-Boot and with multiple > kthreads and hrtimers being involved it is non-trivial to port them. > Instead I worked on stripping down features from the Rockchip port > to an even more basic level. Also the TCPM code has been reworked > to avoid complete use of any timers (Rockchip used SoC specific > hardware timers + IRQ to implement delayed work mechanism). Instead > the delayed state changes are handled directly from the poll loop. > > Note, that (in contrast to the original Rockchip port) the state > machine has the same hard reset quirk, that the kernel has - i.e. > it avoids disabling the CC pin resistors for devices that are not > self-powered. Without that quirk, the Radxa Rock 5B will not just > end up doing a machine reset when a hard reset is triggered, but will > not even recover, because the CPU will loose power and the FUSB302 > will keep this state because of leak voltage arriving through the RX > serial pin (assuming a serial adapter is connected). > > This also includes a 'tcpm' command, which can be used to get > information about the current state and the negotiated voltage > and current. > > Co-developed-by: Wang Jie <dave.w...@rock-chips.com> > Signed-off-by: Wang Jie <dave.w...@rock-chips.com> > Signed-off-by: Sebastian Reichel <sebastian.reic...@collabora.com> > --- > Makefile | 1 + > cmd/Kconfig | 7 + > cmd/Makefile | 1 + > cmd/tcpm.c | 136 ++ > doc/usage/cmd/tcpm.rst | 66 + > doc/usage/index.rst | 1 + > drivers/usb/Kconfig | 2 + > drivers/usb/tcpm/Kconfig | 8 + > drivers/usb/tcpm/Makefile | 3 + > drivers/usb/tcpm/tcpm-internal.h | 173 +++ > drivers/usb/tcpm/tcpm-uclass.c | 145 ++ > drivers/usb/tcpm/tcpm.c | 2288 ++++++++++++++++++++++++++++++ > include/dm/uclass-id.h | 1 + > include/usb/pd.h | 516 +++++++ > include/usb/tcpm.h | 99 ++ > 15 files changed, 3447 insertions(+) > create mode 100644 cmd/tcpm.c > create mode 100644 doc/usage/cmd/tcpm.rst > create mode 100644 drivers/usb/tcpm/Kconfig > create mode 100644 drivers/usb/tcpm/Makefile > create mode 100644 drivers/usb/tcpm/tcpm-internal.h > create mode 100644 drivers/usb/tcpm/tcpm-uclass.c > create mode 100644 drivers/usb/tcpm/tcpm.c > create mode 100644 include/usb/pd.h > create mode 100644 include/usb/tcpm.h >
[snip] > diff --git a/drivers/usb/tcpm/tcpm-uclass.c b/drivers/usb/tcpm/tcpm-uclass.c > new file mode 100644 > index 000000000000..738ab9b2b695 > --- /dev/null > +++ b/drivers/usb/tcpm/tcpm-uclass.c > @@ -0,0 +1,145 @@ [snip] > + > +static int tcpm_post_bind(struct udevice *dev) > +{ > + const struct dm_tcpm_ops *drvops = dev_get_driver_ops(dev); > + const char *cap_str; > + ofnode node; > + int ret; > + > + /* > + * USB Power Delivery (USB PD) specification requires, that > communication > + * with a sink happens within roughly 5 seconds. Otherwise the source > + * might assume that the sink does not support USB PD. Starting to do > + * USB PD communication after that results in a hard reset, which > briefly > + * removes any power from the USB-C port. > + * > + * On systems with alternative power supplies this is not an issue, but > + * systems, which get soleley powered through their USB-C port will end > + * up losing their power supply and doing a board level reset. The hard > + * reset will also restart the 5 second timeout. That means a operating > + * system initializing USB PD will put the system into a boot loop when > + * it takes more than 5 seconds from cold boot to the operating system > + * starting to transmit USB PD messages. > + * > + * The issue can be avoided by doing the initial USB PD communication > + * in U-Boot. The operating system can then re-negotiate by doing a > + * soft reset, which does not trigger removal of the supply voltage. > + * > + * Since the TCPM state machine is quite complex and depending on the > + * remote side can take quite some time to finish, this tries to limit > + * the automatic probing to systems probably relying on power being > + * provided by the USB-C port(s): > + * > + * 1. self-powered devices won't reset when the USB-C port looses power > + * 2. if the power is allowed to go into anything else than sink mode > + * it is not the only power source > + */ > + ret = drvops->get_connector_node(dev, &node); > + if (ret) > + return ret; > + > + if (ofnode_read_bool(node, "self-powered")) This could possible also check for the pd-disable property. Beside that I think this look good. Regards, Jonas > + return 0; > + > + cap_str = ofnode_read_string(node, "power-role"); > + if (!cap_str) > + return -EINVAL; > + > + if (strcmp("sink", cap_str)) > + return 0; > + > + dev_info(dev, "probing Type-C port manager..."); > + > + dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND); > + > + return 0; > +} > + > +UCLASS_DRIVER(tcpm) = { > + .id = UCLASS_TCPM, > + .name = "tcpm", > + .per_device_plat_auto = sizeof(struct tcpm_port), > + .post_bind = tcpm_post_bind, > + .post_probe = tcpm_post_probe, > +}; [snip]