[PATCH v5 1/6] usb: tcpm: add core framework
Jonas Karlman
jonas at kwiboo.se
Wed Sep 4 09:28:38 CEST 2024
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.wang at rock-chips.com>
> Signed-off-by: Wang Jie <dave.wang at rock-chips.com>
> Signed-off-by: Sebastian Reichel <sebastian.reichel at 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]
More information about the U-Boot
mailing list