Widens the #160 fix, which was scoped too narrowly to host role, and fixes
flare-edge#168.
Device role was deliberately left alone on the reasoning that the state
machine is only meaningless when the port is a host. That was wrong. On a
board with no extcon phandle the machine has no external role signal in
EITHER direction, and with bvalid pinned high by the VBUS bypass its charger
detection cannot be meaningful at all. In device role its verdict suspends
the phy underneath a bound gadget, so the USB recovery link dies.
Reproduced deterministically on warden-c8a3 with a host attached:
device role, host attached ccf=1 phy_sus=0x0 udc=configured works
-> host role ccf=1 phy_sus=0x0 udc=(none)
-> back to gadget ccf=0 phy_sus=0x1d1 udc=configured SUSPENDED
-> +15s / +30s ccf=0 phy_sus=0x1d1 udc=configured never recovers
and the 2207:0019 device disappears from the host for good.
Nothing notices, because every obvious signal lies: udc state reads
"configured", usb0 carrier reads 1, and DCTL RUN_STOP reads 1, so the gadget
really is asserting its pullup. It asserts into a phy whose analog front end
and 480M clock are off, so the SoC sees SE0 and the host sees no device at
all, not even an enumeration error.
It cannot self-heal either. bvalid is pinned high, so a host attaching later
produces no VBUS edge for the machine to trigger on: it ran once, powered the
phy off, and nothing re-evaluates. That is why a reboot appears to fix it and
why this read as a cable fault for weeks. It is not, and the cables were fine.
Gated on the extcon being self-allocated rather than on the board compatible,
so it describes the actual precondition: no external role signal exists.
Renamed from 29-usb2phy-no-otg-sm-in-host.patch, whose name now misstated the
scope.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MVGTC78dgCGfRANNKjoPea
Issue #160: the USB ethernet link on the OTG port stops passing traffic and
the host controller is later declared dead ("xHCI host not responding to stop
endpoint command", "HC died").
Root cause. The phy's OTG state machine and charger detection only make sense
while the port is a peripheral. This board has no ID source, so in host role
their inputs are meaningless: rockchip_usb2phy_otg_sm_work() reaches
OTG_STATE_B_IDLE with vbus asserted, schedules rockchip_chg_detect_work(), and
a DCP verdict calls rockchip_usb2phy_power_off(). That suspends the phy and
drops the 480M clkout the controller's core runs on, while a device is
enumerated and working.
The controller then stops clocking with its register block still readable, so
it reports itself healthy: USBCMD keeps RS set, USBSTS reads 0, PORTSC still
shows CCS and PED. Only MFINDEX gives it away by no longer advancing. Nothing
completes from that point, and the first transmit afterwards times out. The
recovery path cannot help either, because xhci_halt() needs the core clock to
update USBSTS.HCH, so it times out and xhci_reset() returns early without ever
writing USBCMD.HCRST.
dwc3 already calls phy_set_mode() on every role change, but this driver
implemented no .set_mode, so the phy never learned the role. Implement it,
track the role per port, and return early from both work functions in host
role. The check is inside the functions because both have direct callers
besides the scheduled work.
This keeps dr_mode "otg" and runtime role switching. Setting dr_mode to "host"
also avoids the fault, by skipping the same state machine, but compiles out
the gadget needed for flashing, recovery and pairing.
Measured on warden-c8a3, per arm, transmit packets and xhci interrupts
delivered after the stated idle:
idle 0 s +9, +10, +9 packets +170, +176, +188 interrupts
idle 120 s +0, +0, +0 packets +0, +0, +0 interrupts
Also drops 28-dwc3-otg-no-live-reinit.patch. It guarded dwc3_otg_host_init()
against re-entry on a live controller, but that path never executes here:
GSTS reads OTG IP clear and instrumentation logged no entries on either the
hardware irq or the guard site.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MVGTC78dgCGfRANNKjoPea
Replace every em dash with real punctuation (rewrites, not hyphen swaps)
in docs, code comments, scripts, configs, and the port records; convert
en dashes, curly quotes, ellipsis glyphs, arrows, and section signs to
ASCII; drop machine-writing tell phrases from living docs. ADR titles
now use a colon. The M2 bring-up DTS model string carried an em dash
into the patch series and its record echoes; fixed at both, and the full
series re-verified to apply cleanly onto pristine 6.18.46. One comment
in freshness.h deliberately names the em dash glyph the UI renders as
the unknown mark; that is data, kept as prose naming it.
Verified: cargo tests (sim, config-lint, rs485-bridge), shellcheck,
both driver MC/DC harnesses, patches-apply.
Second conciseness pass per review feedback: bullets and tables over
prose (vendor-SDK failings, repo layout, sim models, kernel-port
rationale, QEMU scenarios), duplicated content cut (provenance pointer,
scenario prose the qemu README already carries). Also fixes two stale
claims in architecture.md: the flared seam unification and real-image
boot both landed (flare-edge #110, PR #8) but were still marked pending.
The repo's documentation framed it as a support repo for one product
(WardenOS). Since going public the real audience is anyone with a Luckfox
Pico 86 Panel: a maintained 6.18 kernel, an off-device development loop,
and a device simulator that exist nowhere else for this board. Reframe the
README and top-level docs board-first, with WardenOS documented as the
downstream consumer it is (ADR-0008).
Also an editorial pass over the whole doc set:
- every H1/H2 is now a short title, not a sentence (ADRs, qemu/, patches/,
drivers/, architecture, NPU feasibility, config-lint, payload); workflow
flowchart titles fixed at the source in tools/flowgen.py and regenerated
with fresh bench numbers
- README Quick Start commands verified against the scripts; requirements
corrected (curl, bare python, gcc >= 14) and the MC/DC gate added as a
step (run green locally on gcc 14.2)
- dropped the 'needs python (not python3)' vendor dig: build-kernel.sh
inherited the same requirement (filed #10 to remove it)
- glossed MC/DC and HPMCU on first use; marked the tests/uboot-ab
reference as flare-edge; deduplicated the three-simulator list into the
root README table
Replaces the dual MIT/Apache-2.0 licensing (and its GPL carve-out) with a
single GPL-2.0-only license for the whole repository: coherent with the
kernel patch series it exists to carry, the embedded-Linux norm, and the
stack's openness principle — anyone shipping products on this SDK publishes
their changes. GPL-2.0 (not 3) deliberately: the Installation-Information
clause of GPLv3 sits badly with WardenOS's signed A/B firmware chain.
LICENSE is now the canonical GPLv2 text; crate manifests updated;
inbound = outbound noted in the README.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
Preparing the repo to go public (ADR-0007):
- CI: kernel-build moves from the self-hosted runner to ubuntu-latest
(installs its own cross toolchain + qemu, caches the pristine tarball).
On a public repo a registered self-hosted runner is reachable from
approved fork-PR workflows — i.e. arbitrary code on private
infrastructure — and the build never actually needed the SDK host.
ADR-0004 marked superseded-in-part; docs/ci-cd.md rewritten (site
specifics now live only in the private deployment log).
- Licensing: LICENSE gains the GPL-2.0 carve-out for patches/ and the
kernel source excerpts (Linux derivatives; per-driver provenance was
already tracked in PROVENANCE.md); patches/README.md states it too.
- Scrubbed from the tip: bench-unit dev credentials and its gadget IP
(m2-boot notes), the site AP SSID+BSSID and a neighboring AP's BSSID
and the device WLAN MAC (wifi bring-up evidence — BSSIDs are
geolocatable), the runner mesh IP. NOTE: these remain in git history;
decision on a pre-publication history rewrite is separate.
- Emoji cleanup across 21 tracked files (kernel port docs, review report,
enforce-mcdc.sh) per repo text conventions: status marks became
[x]/[wip]/[ ]/OK plain text.
- "[maintainer]-gated" process phrasing normalized to "maintainer-gated"
(attributions in dated evidence docs kept).
Verified: zero emojis tracked; scrub grep clean; patches carry no internal
references; ci.yml parses; shellcheck unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
- patches/: the pristine-6.18.46 -> WardenOS delta as a 13-patch, subsystem-split
series (223 files, ~136K lines: clk, pinctrl, DTs/mach, usb-phy, VOP/panel/rgb,
mailbox, pvtm, rknpu, rga, aic8800 wifi, audio codec, thermal/rtc/adc/gmac/touch).
Verified: every patch applies cleanly onto pristine (git apply --check), the full
series reproduces the hardware-verified tree, and the applied source configures +
builds the warden dtb + rockchip DRM drivers (rc=0).
- build/build-kernel.sh: fetch+verify pristine (sha256-pinned) -> apply series ->
warden_defconfig -> zImage + rv1106-warden.dtb. build/warden_defconfig captured.
- CI: `patches-apply` (GitHub-hosted, cached tarball) enforces the series applies;
`kernel-build` (self-hosted warden-sdk runner, dispatch-gated until registered)
runs the full build and uploads the image.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017wB8KB3MMQztRDXCMCkPrf