Skip to content

usbc: USB-PD R3.2 policy SM + TCPC abstraction#2211

Open
b1nc0d3x wants to merge 1 commit into
freebsd:mainfrom
b1nc0d3x:submit/d3-usbc_pd
Open

usbc: USB-PD R3.2 policy SM + TCPC abstraction#2211
b1nc0d3x wants to merge 1 commit into
freebsd:mainfrom
b1nc0d3x:submit/d3-usbc_pd

Conversation

@b1nc0d3x
Copy link
Copy Markdown

Summary

Add a new in-tree framework for USB Power Delivery state-machine logic that is independent of any specific Type-C port controller chip. The framework lives at sys/dev/iicbus/usb/usbc/, builds as a kernel module (usbc.ko), and exposes four layers:

  1. usbc_pd.h — base types, enums, and timing constants from USB PD R3.2: SOP* packet types, control/data message numbers, header bit layout, PDO/RDO field encoders, protocol timers (tSenderResponse, tPSTransition, ...), VDM SVID / object position constants for DisplayPort altmode.

  2. usbc_pd_msg.{h,c} — hardware-independent build/parse for PD message headers and data objects. No I/O, no locking; callable from interrupt context:

    • usbc_pd_build_header() — pack header fields to a u16
    • usbc_pd_parse_header() — inverse, with bounds checking
    • usbc_pd_build_source_cap_pdo_fixed() — 5V fixed-supply PDO
    • usbc_pd_build_request_rdo() — fixed-supply request
    • usbc_pd_validate_pdo() — sanity-check before TX
  3. usbc_pd_policy.{h,c} — the protocol/policy engine. Implements the source-port policy state machine from PD chapter 8 (Power Negotiation), the soft-reset and hard-reset paths, the VDM exchange for Discover Identity / Discover SVIDs / Enter Mode / DP_Status, and the timer-driven retry/abort logic. Caller-facing API:

    • usbc_pd_policy_attach(sc, tcpc_ops)
    • usbc_pd_policy_detach(sc)
    • usbc_pd_policy_rx(sc, msg) — chip-driver IRQ handler entry
    • usbc_pd_policy_role_change(sc, ...)
    • usbc_pd_policy_enter_dp_altmode(sc, port_pin_assign)
  4. usbc_tcpc.h — the abstraction the chip driver implements. A struct of function pointers (transmit a PD msg, set CC state, set vbus, set role, etc.) that the policy SM invokes. This is what makes the framework hardware-independent — the FUSB302B driver in the companion (forthcoming) review supplies one implementation, and a future TUSB320 / TPS6598x / MT6360 driver supplies another without changing a line of policy code.

Why land this separately

  • Dependency-free against the rest of the tree — no other in-tree driver consumes it yet.
  • Reviewing the framework in isolation from any specific chip driver makes the abstraction boundary clearer.
  • The companion fusb302 review (forthcoming) is materially smaller once it can refer to "the policy SM" and "the TCPC interface" instead of containing both itself.

Not in this review

  • No chip driver. The fusb302(4) driver will be a separate, follow-on PR.
  • No userspace policy hook — the SM is currently autonomous; the intent is that a future tcpmctl(8) or libusbc consumer can drive policy choices from userspace, but that is out of scope here.
  • No PD R3.2 fast-role-swap or programmable-power-supply support. Source PDOs are limited to fixed-supply 5 V; extended-message handling is reserved for a follow-up.

Test plan

  • Buildkernel + GENERIC + device usbc: builds clean, no external symbol references, kldload / kldunload work.
  • Unit-level coverage: usbc_pd_build_header / parse_header round-trip 1000 random headers via a kgdb script; all field values preserved. PDO encoders verified by hand-decoding the resulting u32 against PD 6.4.1 Table 6-9.
  • Integration: with the companion fusb302 driver (forthcoming) attached to a Pine64 RockPro64 v2.1 + XYM W156F1 USB-C portable monitor, the policy SM drives a full source contract (PD_REQUESTACCEPTPS_RDY) and a DisplayPort altmode entry (Discover IdentityDiscover SVIDsEnter ModeDP_Status) within ~1 s of plug insertion. Subsequent unplug / replug cycles re-run the entire flow without leaking state.
  • Negative paths: forced PD soft-reset (sysctl) and hard-reset (peer TX fault) both return the SM to USBC_PD_SRC_STARTUP within tSenderResponseTimer + 50 ms.

Suggested reviewers

hps (USB stack), kib, manu, mhorne — based on git log of sys/dev/iicbus/usb/ and related arm64 work. Not @-mentioned to avoid spam; happy to ping on request.

Diff scope

File Lines
sys/dev/iicbus/usb/usbc/usbc_pd.h +179
sys/dev/iicbus/usb/usbc/usbc_pd_msg.c +211
sys/dev/iicbus/usb/usbc/usbc_pd_msg.h +174
sys/dev/iicbus/usb/usbc/usbc_pd_policy.c +1992
sys/dev/iicbus/usb/usbc/usbc_pd_policy.h +225
sys/dev/iicbus/usb/usbc/usbc_tcpc.h +128
sys/modules/usbc/Makefile +10
Total +2919

All files net-new; no existing FreeBSD code modified. License is BSD-2-Clause throughout; SPDX-License-Identifier headers present on every file.

Notes

Third in a series landing the RockPro64 USB-C DisplayPort + audio path. Predecessors:

Successor patches (fusb302 chip driver, rk_typec_phy DP altmode, rk_cdn_dp framer, DP audio + hdmi codec stub) will follow as their own PRs, paced to avoid swamping reviewer attention.

Author: Kyle Crenshaw <B1nc0d3x@gmail.com>

Add a new in-tree framework for USB Power Delivery state-machine
logic that is independent of any specific Type-C port controller
chip.  The framework lives at sys/dev/iicbus/usb/usbc/, builds as a
kernel module (usbc.ko), and exposes three layers:

1. usbc_pd.h -- base types, enums, and timing constants from USB
   PD R3.2: SOP* packet types, control/data message numbers, header
   bit layout, PDO/RDO field encoders, protocol timers
   (tSenderResponse, tPSTransition, ...), VDM SVID / object position
   constants for DisplayPort altmode.

2. usbc_pd_msg.{h,c} -- hardware-independent build/parse for PD
   message headers and data objects.  No I/O, no locking; callable
   from interrupt context:
     usbc_pd_build_header()     -- pack header fields to u16
     usbc_pd_parse_header()     -- inverse, with bounds checking
     usbc_pd_build_source_cap_pdo_fixed()
     usbc_pd_build_request_rdo()
     usbc_pd_validate_pdo()

3. usbc_pd_policy.{h,c} -- the protocol/policy engine.  Implements
   the source-port policy state machine from PD chapter 8 (Power
   Negotiation), the soft-reset and hard-reset paths, the VDM
   exchange for Discover Identity / Discover SVIDs / Enter Mode /
   DP_Status (the DP altmode prerequisites needed for cdn-dp on
   RockPro64), and the timer-driven retry/abort logic.
   Caller-facing API:
     usbc_pd_policy_attach(sc, tcpc_ops)
     usbc_pd_policy_detach(sc)
     usbc_pd_policy_rx(sc, msg)
     usbc_pd_policy_role_change(sc, ...)
     usbc_pd_policy_enter_dp_altmode(sc, port_pin_assign)

4. usbc_tcpc.h -- the abstraction the chip driver implements.  A
   struct of function pointers (transmit a PD msg, set CC state,
   set vbus, set role, etc.) that the policy SM invokes.  This is
   what makes the framework hardware-independent: the FUSB302B
   driver in the companion review supplies one implementation; a
   future TUSB320 / TPS6598x / MT6360 driver supplies another
   without touching policy code.

Why land this separately:
  - It is dependency-free against the rest of the tree -- no other
    in-tree driver consumes it yet.
  - Reviewing the framework in isolation from any specific chip
    driver makes the abstraction boundary clearer.
  - The companion fusb302 review is materially smaller once it can
    refer to "the policy SM" and "the TCPC interface" instead of
    containing both itself.

Not in this review:
  - No chip driver.  The fusb302(4) driver is a separate review.
  - No userspace policy hook -- SM is autonomous; the intent is
    that a future tcpmctl(8) or libusbc consumer can drive policy
    choices from userspace, but that is out of scope here.
  - No PD R3.2 fast-role-swap or programmable-power-supply support.
    Source PDOs are limited to fixed-supply 5 V; extended-message
    handling is reserved for a follow-up.

Tested on Pine64 RockPro64 v2.1 with the companion fusb302 driver
and an XYM W156F1 USB-C portable monitor: the policy SM drives a
full source contract (PD_REQUEST -> ACCEPT -> PS_RDY) and a
DisplayPort altmode entry (Discover Identity -> Discover SVIDs ->
Enter Mode -> DP_Status) within ~1 s of plug insertion.  Subsequent
unplug/replug cycles re-run the entire flow without leaking state.
Negative paths verified: forced PD soft-reset and hard-reset both
return the SM to USBC_PD_SRC_STARTUP within tSenderResponseTimer
+ 50 ms.

Signed-off-by: Kyle Crenshaw <B1nc0d3x@gmail.com>
@aokblast aokblast self-requested a review May 19, 2026 06:56
@bms
Copy link
Copy Markdown

bms commented May 19, 2026

There's the slight problem of hps having passed away many years ago. I had the pleasure of meeting him in Warsaw over 2012.

@b1nc0d3x
Copy link
Copy Markdown
Author

b1nc0d3x commented May 19, 2026 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants