As of today, there are several SBCs on the market powered exclusively via a full-featured USB-C port, using USB-PD. The Radxa ROCK 5B is perhaps the best known example. These boards typically use a "thin" USB-C controller and PD PHY chip such as the Fairchild/Onsemi FUSB302B, requiring the USB-PD state machine to be implemented in software. Bringing up the USB-C port on such boards presents unique challenges: - For full USB, PD & alternate mode functionality, the state machine needs to be implemented in the HLOS (typically the Linux kernel). However, the USB-PD specification allows power sources to have a timeout (typically 5 seconds) within which a newly attached sink must initiate PD communications - depending on the boot process, this may be difficult to meet reliably. Power supplies tend to respond to "late" PD communication in rather problematic ways (not responding at all, forcing a hard reset, just straight up cutting power, or even cycling power 3 times in the case of one of my power supplies), so the 5 second timeout is not safe to just ignore. - To ensure timely PD communication start on boot, it's tempting to implement a PD driver & state machine in the bootloader or firmware. This, however, requires either implementing some kind of runtime firmware<->OS interface (so the firmware can control the PD PHY, while the OS can control the PD state machine & policy engine in the firmware), or abandoning full functionality of the port, and downgrading it to a power-only USB-C port. - In theory, one can get the best of both worlds by implementing a simpler PD driver in the firmware, and then handing off to a more fully functional driver in the operating system, once it boots fully. This is analogous to how display outputs are typically managed. Unfortunately, this only results in further problems, as the OS-side driver needs to discover the state of the USB-C connection and the existing PD contract without resetting the port (as a reset could involve a VBUS cut, and thus cause a boot loop, because the VBUS is the board's sole source of power). The USB-PD standard seems to cater to this situation using the "soft reset" feature - unfortunately, in my testing, few (if any) PD power supplies implement the soft reset mechanism correctly. Most supplies will accept and ACK a soft reset, only to then issue a hard reset of their own in response. Others send a garbled PD packet in response, and then cut power. The alternative to a soft reset would be just retrying communication with different parameters (e.g. sequence number), until a valid reply is received. This works with some power supplies, which correctly ignore out-of-sequence PD communications, but in others, it causes a hard reset or VBUS cut, just like a proper soft reset packet would. This leaves no reliable way to have the OS driver take over without assistance from the firmware driver. To facilitate such assisted handovers, a standardized way is needed for the firmware driver to inform the OS driver of the state of each PD-capable USB-C port on the system. For a successful handoff, at lest 8 bits of data needs to be passed up to the OS: - Sequence number: 3 bits - Spec version: 2 bits - Current port data role: 1 bit - Current port power role: 1 bit (may be omitted if the only goal is to support batteryless systems with USB-PD as the sole power source, as in this case, this will always be "sink", but systems with a battery may want to do a handoff for faster boot as well, and they would want to indicate "source" here if that's what was negotiated) * Port orientation: 1 bit (not needed for FUSB302-based systems as one can probe port orientation passively without the port partner detecting a reset, but other PD chips may not offer this option) Additionally, passing the active PDO to the OS as part of the handoff data could also be useful. Considering the market for SBCs with the 3 most popular architectures (x86, ARM64 and RISC-V), they all seem to fall into 3 categories as far as their boot process goes (u-boot or vendor bootloader + DT, UEFI + DT, UEFI + ACPI). Each of these boot processes potentially require a different solution to the PD handoff problem. I propose the following handoff mechanisms: - For Device Tree systems with non-UEFI bootloaders (e.g. u-boot without bootefi support), a new DT parameter could be defined on the USB-C port device. e.g. of the form "pd-state = 0x8b;" which the bootloader/firmware would update before passing control to the OS kernel. This requires the bootloader to parse and modify the FDT blob, but has no other dependencies, e.g. no need for runtime variable support. - For bootloaders implementing UEFI but not ACPI, FDT blob modification can be avoided by passing the handoff data in a UEFI variable. The device tree can then reference this variable by name (e.g. pd-handoff-variable = "Pd0001" - different for each port on a system with multiple PD-capable Type C ports), so when the operating system boots, the kernel driver can retrieve this variable and continue PD communications seamlessly. - Finally, for bootloaders implementing ACPI, the DSDT entry for the Type C port could include a new ACPI control method, e.g. "Method(PDST, 0, NotSerialized)". This method, when called by the operating system, returns the USB-PD state of the port at handoff, so the kernel driver can pick up PD communication where the firmware has left off. Unlike in the DT case, there is no need for separate UEFI vs non-UEFI mechanisms here, because the ACPI Source Language is versatile enough to enable firmware vendors to implement this control method in a variety of ways (e.g. by reading a firmware variable, reading a fixed memory location, calling a runtime service, or even just by having the firmware patch the value returned by the method in the DSDT AML itself). Additionally, it would be useful for the OS-side driver to know if a PD-enabled Type-C port is critical for the power supply of the system, so it can e.g. avoid triggering hard resets on that port. For Device Tree systems, the easy solution may be a boolean property, however I wonder if it would be more descriptive to actually have the Type-C port declare a regulator it produces (representing VBUS), and then have the rest of the system's power regulators depend on it. The ACPI equivalent to this would be having the port produce a power resource, and have the rest of the system consume it. Ideas, comments, etc. welcome. Sincerely, Gábor Stefanik