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]

Reply via email to