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
bfe-core1106-sdk
A modern, open development environment for the Luckfox Pico 86 Panel (Rockchip RV1106), replacing the vendor SDK, and honest about what runs on real silicon versus what is simulated.
| Vendor SDK | This repo | |
|---|---|---|
| Kernel | 5.10.160, twice-forked, frozen | 6.18.46: a reviewable, subsystem-split patch series onto pristine upstream; full peripheral set (display, touch, wifi, audio, NPU, ...) hardware-verified on a bench panel |
| Build | ~2 GB tree, absolute paths baked in, Kconfig options silently dropped | one hermetic script: sha256-pinned source fetch, fail-closed patch apply and config fragments |
| Off-device testing | none; every change means flashing a panel | register-level hardware models (sim/) plus a QEMU device VM booting the real kernel, real daemons, and the real UI with display + touch |
| Config safety | memory-map mistakes reach hardware (one bricked a bench unit) | static gates (tools/config-lint) catch them before any flash |
| CI | none | hosted pipeline: tests, coverage, benchmarks, patch-apply gate, kernel build with an in-CI QEMU boot smoke |
| Flashing tools | closed (upgrade_tool) |
open (rkdeveloptool) |
| License | mixed | GPL-2.0-only, with a per-driver provenance ledger |
Quick Start
Requirements: gcc-arm-linux-gnueabihf, qemu-system-arm, curl, cpio,
mkfs.ext4, a bare python on PATH (Debian/Ubuntu: python-is-python3),
gcc >= 14 (driver harnesses), and Rust (for the simulators' tests).
# 1. Build the kernel: fetch pinned pristine 6.18.46, apply patches/, emit
# zImage + rv1106-warden.dtb. WORK must sit outside any git checkout.
WORK=$HOME/kbuild-out CROSS_COMPILE=arm-linux-gnueabihf- bash build/build-kernel.sh
# 2. Boot it in the QEMU device simulator (no hardware needed):
bash qemu/mkinitramfs.sh
bash qemu/mkimage.sh
bash qemu/run.sh --kernel $HOME/kbuild-out/linux-6.18.46/arch/arm/boot/zImage --shell
# 3. Run the test suites:
for d in sim tools/config-lint qemu/rs485-bridge; do
(cd "$d" && cargo test)
done
for d in drivers/*/test; do make -C "$d" check; done # driver harnesses (gcc >= 14)
Add WARDEN_KCONFIG_FRAGMENT=qemu/configs/virt.fragment to step 1 for the
kernel variant with the simulator's extra devices; qemu/README.md has the
scenario tests (portal, OTA apply, display + touch, watchdog).
Layout
| Directory | Contents |
|---|---|
patches/ |
the RV1106 forward-port onto pristine linux-6.18.46, subsystem-split |
build/ |
hermetic kernel build: pinned fetch -> apply patches -> zImage + dtb; vendor.manifest pins the third-party trees this platform builds against (LVGL, the vendor RV1106 SDK) to exact commits, and fetch-vendor.sh obtains and verifies them |
qemu/ |
device simulator: QEMU -M virt boots the real kernel and real userspace |
sim/ |
register-level hardware models (Rust): membus, HPMCU, CRU, Modbus, RGA, NPU |
drivers/ |
hardened hardware-facing drivers: HAL seams, test harnesses |
kernel/ |
forward-port provenance and bring-up records (patches/ is canonical) |
tools/ |
config-lint (static memory-map gates) and dev tooling |
docs/ |
architecture, ADRs (decisions/), CI/CD |
Architecture
One thin hardware abstraction seam per block (a trait in Rust, a function
table in C): firmware logic talks to the seam; the seam binds a real backend
on the device or a simulated backend on the host. The driver test harnesses
measure against the same seam the simulator implements, so the two reinforce
each other. Full detail: docs/architecture.md.
| Simulator | Runs | Proves |
|---|---|---|
sim/ |
register-level Rust models | driver and supervisor logic, with fault injection |
qemu/ |
the real kernel + userspace on -M virt |
boot, init, daemons, networking, OTA, watchdog, display + touch |
lvglsim (downstream) |
the LVGL UI on SDL | rendering and UI flows |
With the production UI binary in qemu/payload/, run.sh --display on opens
the panel's 720x720 screen in a window, mouse clicks landing as touch:
device and UI in one VM. Emulation results are never on-silicon evidence;
the simulators narrow which claims need a panel.
Principles
- Open: open tools over closed ones; GPL-2.0-only.
- Hard: every seam has a fault-injection path; recovery code is tested against failure, not just success.
- Modern: the newest kernel the hardware can run, current toolchains, Rust for new host-testable code, reproducible builds.
License
GPL-2.0-only, repo-wide (LICENSE; a per-file SPDX identifier governs
where present). patches/ and kernel/ are derivative of the Linux kernel
and GPL-2.0 vendor code; per-driver origin is tracked in
kernel/rv1106-enablement/PROVENANCE.md. Contributions are accepted under
the same license (inbound = outbound).