RFC/Proposal: USB-PD state handoff via DT or ACPI, primarily for batteryless systems

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux