docs: ASCII typography and style normalization across all repo text
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.
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# Policy (mirrors flare-edge): only GitHub-owned actions get the repo token; the one
|
# Policy (mirrors flare-edge): only GitHub-owned actions get the repo token; the one
|
||||||
# third-party helper (taiki-e/install-action) is pinned and never handed a token.
|
# third-party helper (taiki-e/install-action) is pinned and never handed a token.
|
||||||
# Every job runs on GitHub-hosted runners — no self-hosted runner may be reachable
|
# Every job runs on GitHub-hosted runners: no self-hosted runner may be reachable
|
||||||
# from this repo's workflows (ADR-0007: public-repo fork PRs would otherwise be
|
# from this repo's workflows (ADR-0007: public-repo fork PRs would otherwise be
|
||||||
# able to run code on private infrastructure). kernel-build is dispatch-only.
|
# able to run code on private infrastructure). kernel-build is dispatch-only.
|
||||||
name: ci
|
name: ci
|
||||||
@@ -146,7 +146,7 @@ jobs:
|
|||||||
# Automatic artifact GC (mirrors flare-edge). An exceeded account-wide Actions
|
# Automatic artifact GC (mirrors flare-edge). An exceeded account-wide Actions
|
||||||
# storage quota blocks ALL new runs (startup_failure), not just uploads, so
|
# storage quota blocks ALL new runs (startup_failure), not just uploads, so
|
||||||
# before a new kernel artifact is uploaded, drop older kernel-rv1106 artifacts
|
# before a new kernel artifact is uploaded, drop older kernel-rv1106 artifacts
|
||||||
# beyond the newest few — storage stays bounded across dispatches. gh + the
|
# beyond the newest few: storage stays bounded across dispatches. gh + the
|
||||||
# built-in token (no third-party action); needs actions:write to delete.
|
# built-in token (no third-party action); needs actions:write to delete.
|
||||||
# Best-effort: it never fails the run, so it can't block the build that needs it.
|
# Best-effort: it never fails the run, so it can't block the build that needs it.
|
||||||
prune-artifacts:
|
prune-artifacts:
|
||||||
@@ -177,8 +177,8 @@ jobs:
|
|||||||
|
|
||||||
kernel-build:
|
kernel-build:
|
||||||
# Full hermetic build on a GitHub-hosted runner (ADR-0007; supersedes the
|
# Full hermetic build on a GitHub-hosted runner (ADR-0007; supersedes the
|
||||||
# self-hosted half of ADR-0004 — a self-hosted runner must never be reachable
|
# self-hosted half of ADR-0004: a self-hosted runner must never be reachable
|
||||||
# from a public repo's workflows). Manual-dispatch by design — a full kernel
|
# from a public repo's workflows). Manual-dispatch by design: a full kernel
|
||||||
# build is heavy; trigger via `gh workflow run ci.yml` / the Actions UI.
|
# build is heavy; trigger via `gh workflow run ci.yml` / the Actions UI.
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
needs: [prune-artifacts]
|
needs: [prune-artifacts]
|
||||||
@@ -191,7 +191,7 @@ jobs:
|
|||||||
sudo apt-get update -qq
|
sudo apt-get update -qq
|
||||||
sudo apt-get install -y -qq gcc-arm-linux-gnueabihf qemu-system-arm \
|
sudo apt-get install -y -qq gcc-arm-linux-gnueabihf qemu-system-arm \
|
||||||
cpio bc bison flex libssl-dev ccache
|
cpio bc bison flex libssl-dev ccache
|
||||||
- name: provision `python` (SDK quirk — build calls bare python)
|
- name: provision `python` (SDK quirk: build calls bare python)
|
||||||
run: |
|
run: |
|
||||||
mkdir -p "$RUNNER_TEMP/bin"
|
mkdir -p "$RUNNER_TEMP/bin"
|
||||||
ln -sf "$(command -v python3)" "$RUNNER_TEMP/bin/python"
|
ln -sf "$(command -v python3)" "$RUNNER_TEMP/bin/python"
|
||||||
@@ -225,7 +225,7 @@ jobs:
|
|||||||
# (zImage alone emits no Module.symvers); only these dirs' .ko files
|
# (zImage alone emits no Module.symvers); only these dirs' .ko files
|
||||||
# are collected/uploaded.
|
# are collected/uploaded.
|
||||||
WARDEN_MODULES_COLLECT: drivers/net/wireless/aic8800
|
WARDEN_MODULES_COLLECT: drivers/net/wireless/aic8800
|
||||||
# KERNEL_TARBALL is exported from the SHELL so $HOME expands — a literal
|
# KERNEL_TARBALL is exported from the SHELL so $HOME expands. A literal
|
||||||
# `~` in a YAML env: value is never tilde-expanded and broke every
|
# `~` in a YAML env: value is never tilde-expanded and broke every
|
||||||
# dispatch until caught in review.
|
# dispatch until caught in review.
|
||||||
run: |
|
run: |
|
||||||
@@ -242,7 +242,7 @@ jobs:
|
|||||||
"$RUNNER_TEMP/kbuild-out/linux-6.18.46/arch/arm/boot/zImage"
|
"$RUNNER_TEMP/kbuild-out/linux-6.18.46/arch/arm/boot/zImage"
|
||||||
# Best-effort: the build IS the gate. Uploading the zImage/dtb to GitHub
|
# Best-effort: the build IS the gate. Uploading the zImage/dtb to GitHub
|
||||||
# artifact storage can fail on an account-wide storage-quota hit (recalculated
|
# artifact storage can fail on an account-wide storage-quota hit (recalculated
|
||||||
# every 6-12h) that has nothing to do with this build — don't red-X a good
|
# every 6-12h) that has nothing to do with this build. Don't red-X a good
|
||||||
# kernel build over it.
|
# kernel build over it.
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|||||||
@@ -6,14 +6,14 @@
|
|||||||

|

|
||||||
|
|
||||||
A modern, open development environment for the **Luckfox Pico 86 Panel**
|
A modern, open development environment for the **Luckfox Pico 86 Panel**
|
||||||
(Rockchip RV1106), replacing the vendor SDK — and honest about what runs on
|
(Rockchip RV1106), replacing the vendor SDK, and honest about what runs on
|
||||||
real silicon versus what is simulated.
|
real silicon versus what is simulated.
|
||||||
|
|
||||||
| | Vendor SDK | This repo |
|
| | 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 |
|
| **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 |
|
| **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 |
|
| **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 |
|
| **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 |
|
| **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`) |
|
| **Flashing tools** | closed (`upgrade_tool`) | open (`rkdeveloptool`) |
|
||||||
@@ -51,7 +51,7 @@ scenario tests (portal, OTA apply, display + touch, watchdog).
|
|||||||
| Directory | Contents |
|
| Directory | Contents |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `patches/` | the RV1106 forward-port onto pristine linux-6.18.46, subsystem-split |
|
| `patches/` | the RV1106 forward-port onto pristine linux-6.18.46, subsystem-split |
|
||||||
| `build/` | hermetic kernel build: pinned fetch → apply patches → `zImage` + dtb |
|
| `build/` | hermetic kernel build: pinned fetch -> apply patches -> `zImage` + dtb |
|
||||||
| `qemu/` | device simulator: QEMU `-M virt` boots the real kernel and real userspace |
|
| `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 |
|
| `sim/` | register-level hardware models (Rust): membus, HPMCU, CRU, Modbus, RGA, NPU |
|
||||||
| `drivers/` | hardened hardware-facing drivers: HAL seams, test harnesses |
|
| `drivers/` | hardened hardware-facing drivers: HAL seams, test harnesses |
|
||||||
@@ -74,24 +74,24 @@ each other. Full detail: `docs/architecture.md`.
|
|||||||
| `lvglsim` (downstream) | the LVGL UI on SDL | rendering and UI flows |
|
| `lvglsim` (downstream) | the LVGL UI on SDL | rendering and UI flows |
|
||||||
|
|
||||||
With the production UI binary in `qemu/payload/`, `run.sh --display on` opens
|
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 —
|
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;
|
device and UI in one VM. Emulation results are never on-silicon evidence;
|
||||||
the simulators narrow which claims need a panel.
|
the simulators narrow which claims need a panel.
|
||||||
|
|
||||||
## Principles
|
## Principles
|
||||||
|
|
||||||
- **Open** — open tools over closed ones; GPL-2.0-only.
|
- **Open**: open tools over closed ones; GPL-2.0-only.
|
||||||
- **Hard** — every seam has a fault-injection path; recovery code is tested
|
- **Hard**: every seam has a fault-injection path; recovery code is tested
|
||||||
against failure, not just success.
|
against failure, not just success.
|
||||||
- **Modern** — the newest kernel the hardware can run, current toolchains,
|
- **Modern**: the newest kernel the hardware can run, current toolchains,
|
||||||
Rust for new host-testable code, reproducible builds.
|
Rust for new host-testable code, reproducible builds.
|
||||||
|
|
||||||
## Downstream
|
## Downstream
|
||||||
|
|
||||||
A private firmware repo (flare-edge) consumes this SDK; issue references and
|
A private firmware repo (flare-edge) consumes this SDK; issue references and
|
||||||
checkout paths pointing there are engineering context, not reachable links.
|
checkout paths pointing there are engineering context, not reachable links.
|
||||||
The QEMU simulator runs its production binaries unmodified — real
|
The QEMU simulator runs its production binaries unmodified,
|
||||||
over-the-air updates included.
|
real over-the-air updates included.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
+11
-11
@@ -26,8 +26,8 @@
|
|||||||
# Module.symvers, so per-dir M= builds cannot link) and
|
# Module.symvers, so per-dir M= builds cannot link) and
|
||||||
# collects the listed dirs' modules into $WORK/modules-out.
|
# collects the listed dirs' modules into $WORK/modules-out.
|
||||||
#
|
#
|
||||||
# Requires: `python` (not python3) on PATH — the SDK quirk; the CI runner provides
|
# Requires: `python` (not python3) on PATH: the SDK quirk; the CI runner provides
|
||||||
# a project-local venv. Builds are SERIAL on the shared SDK box — never run two.
|
# a project-local venv. Builds are SERIAL on the shared SDK box: never run two.
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
KVER=6.18.46
|
KVER=6.18.46
|
||||||
@@ -51,7 +51,7 @@ trap '[ "${WORK_OWNED:-0}" = 1 ] && rm -rf "$WORK"' EXIT
|
|||||||
|
|
||||||
log() { printf '\033[36m== %s\033[0m\n' "$*"; }
|
log() { printf '\033[36m== %s\033[0m\n' "$*"; }
|
||||||
|
|
||||||
command -v python >/dev/null || { echo "need 'python' (not python3) on PATH — SDK quirk" >&2; exit 1; }
|
command -v python >/dev/null || { echo "need 'python' (not python3) on PATH: SDK quirk" >&2; exit 1; }
|
||||||
|
|
||||||
# 1. obtain + verify the pristine tarball
|
# 1. obtain + verify the pristine tarball
|
||||||
mkdir -p "$WORK"
|
mkdir -p "$WORK"
|
||||||
@@ -66,7 +66,7 @@ rm -rf "$SRC"
|
|||||||
log "extracting pristine"
|
log "extracting pristine"
|
||||||
tar -C "$WORK" -xf "$TB"
|
tar -C "$WORK" -xf "$TB"
|
||||||
|
|
||||||
# 3. apply the patch series in order (fail loudly — never echo a lie)
|
# 3. apply the patch series in order (fail loudly: never echo a lie)
|
||||||
log "applying patch series"
|
log "applying patch series"
|
||||||
for p in "$PATCHES"/*.patch; do
|
for p in "$PATCHES"/*.patch; do
|
||||||
if git -C "$SRC" apply --whitespace=nowarn "$p" 2>/dev/null; then
|
if git -C "$SRC" apply --whitespace=nowarn "$p" 2>/dev/null; then
|
||||||
@@ -89,7 +89,7 @@ SENTINEL="$SRC/arch/arm/boot/dts/rockchip/rv1106-warden.dts"
|
|||||||
[ -f "$SENTINEL" ] || {
|
[ -f "$SENTINEL" ] || {
|
||||||
echo "FATAL: patch series did not apply (missing $SENTINEL)." >&2
|
echo "FATAL: patch series did not apply (missing $SENTINEL)." >&2
|
||||||
echo " Is \$WORK inside a git repo? git apply silently ignores out-of-subdir" >&2
|
echo " Is \$WORK inside a git repo? git apply silently ignores out-of-subdir" >&2
|
||||||
echo " paths there — point WORK at a dir outside any checkout (e.g. \$RUNNER_TEMP)." >&2
|
echo " paths there: point WORK at a dir outside any checkout (e.g. \$RUNNER_TEMP)." >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
log "patch series applied ($(basename "$SENTINEL") present)"
|
log "patch series applied ($(basename "$SENTINEL") present)"
|
||||||
@@ -98,7 +98,7 @@ log "patch series applied ($(basename "$SENTINEL") present)"
|
|||||||
log "configuring (warden_defconfig)"
|
log "configuring (warden_defconfig)"
|
||||||
cp "$HERE/warden_defconfig" "$SRC/.config"
|
cp "$HERE/warden_defconfig" "$SRC/.config"
|
||||||
# Optional kconfig fragment overlay (e.g. qemu/configs/virt.fragment for the
|
# Optional kconfig fragment overlay (e.g. qemu/configs/virt.fragment for the
|
||||||
# QEMU -M virt device-sim variant). Fail closed if set but unreadable — never
|
# QEMU -M virt device-sim variant). Fail closed if set but unreadable: never
|
||||||
# silently build the wrong kernel. Unset => the canonical RV1106 build,
|
# silently build the wrong kernel. Unset => the canonical RV1106 build,
|
||||||
# byte-identical to a build without this hook.
|
# byte-identical to a build without this hook.
|
||||||
if [ -n "${WARDEN_KCONFIG_FRAGMENT:-}" ]; then
|
if [ -n "${WARDEN_KCONFIG_FRAGMENT:-}" ]; then
|
||||||
@@ -112,7 +112,7 @@ if [ -n "${WARDEN_KCONFIG_FRAGMENT:-}" ]; then
|
|||||||
fi
|
fi
|
||||||
# CROSS_COMPILE defaults to the Luckfox SDK uclibc prefix (set SDK_TC to its bin/),
|
# CROSS_COMPILE defaults to the Luckfox SDK uclibc prefix (set SDK_TC to its bin/),
|
||||||
# but the kernel is freestanding, so a caller may override with a generic arm cross
|
# but the kernel is freestanding, so a caller may override with a generic arm cross
|
||||||
# toolchain instead — e.g. CROSS_COMPILE=arm-linux-gnueabihf- (in Debian's
|
# toolchain instead: e.g. CROSS_COMPILE=arm-linux-gnueabihf- (in Debian's
|
||||||
# gcc-arm-linux-gnueabihf), which the CI runner already has on PATH.
|
# gcc-arm-linux-gnueabihf), which the CI runner already has on PATH.
|
||||||
export ARCH=arm
|
export ARCH=arm
|
||||||
export CROSS_COMPILE="${CROSS_COMPILE:-arm-rockchip830-linux-uclibcgnueabihf-}"
|
export CROSS_COMPILE="${CROSS_COMPILE:-arm-rockchip830-linux-uclibcgnueabihf-}"
|
||||||
@@ -122,7 +122,7 @@ command -v "${CROSS_COMPILE}gcc" >/dev/null \
|
|||||||
make -C "$SRC" ARCH=arm CROSS_COMPILE="$CROSS_COMPILE" olddefconfig >/dev/null
|
make -C "$SRC" ARCH=arm CROSS_COMPILE="$CROSS_COMPILE" olddefconfig >/dev/null
|
||||||
|
|
||||||
# Fragment took-effect assertion: merge_config -m only pastes text, and
|
# Fragment took-effect assertion: merge_config -m only pastes text, and
|
||||||
# olddefconfig silently resolves any symbol whose dependencies are unmet —
|
# olddefconfig silently resolves any symbol whose dependencies are unmet:
|
||||||
# a fragment option could be dropped without a word. Verify every explicit
|
# a fragment option could be dropped without a word. Verify every explicit
|
||||||
# request in the fragment survived into the final .config; fail loud if not.
|
# request in the fragment survived into the final .config; fail loud if not.
|
||||||
if [ -n "${WARDEN_KCONFIG_FRAGMENT:-}" ]; then
|
if [ -n "${WARDEN_KCONFIG_FRAGMENT:-}" ]; then
|
||||||
@@ -137,7 +137,7 @@ if [ -n "${WARDEN_KCONFIG_FRAGMENT:-}" ]; then
|
|||||||
"# CONFIG_"*" is not set")
|
"# CONFIG_"*" is not set")
|
||||||
# A disable succeeded if the symbol is NOT set: Kconfig writes either
|
# A disable succeeded if the symbol is NOT set: Kconfig writes either
|
||||||
# the literal "is not set" line or (when dependencies gate the symbol
|
# the literal "is not set" line or (when dependencies gate the symbol
|
||||||
# out) nothing at all — both are valid outcomes. Only "still =value"
|
# out) nothing at all: both are valid outcomes. Only "still =value"
|
||||||
# is a failed disable. (A typo'd symbol disables nothing and is
|
# is a failed disable. (A typo'd symbol disables nothing and is
|
||||||
# harmless by construction.)
|
# harmless by construction.)
|
||||||
opt="${line#\# }"; opt="${opt% is not set}"
|
opt="${line#\# }"; opt="${opt% is not set}"
|
||||||
@@ -165,13 +165,13 @@ make -C "$SRC" ARCH=arm CROSS_COMPILE="$CROSS_COMPILE" CC="$KCC" -j"$JOBS" \
|
|||||||
|
|
||||||
# Optional module set (issue #4: the panel loaded a stale 5.10 .ko because
|
# Optional module set (issue #4: the panel loaded a stale 5.10 .ko because
|
||||||
# this build never produced matched 6.18 modules). Full `make modules` is
|
# this build never produced matched 6.18 modules). Full `make modules` is
|
||||||
# required — zImage alone emits no Module.symvers, so a per-directory M=
|
# required: zImage alone emits no Module.symvers, so a per-directory M=
|
||||||
# build cannot resolve even core symbols. FAILS CLOSED if a listed dir
|
# build cannot resolve even core symbols. FAILS CLOSED if a listed dir
|
||||||
# yields no modules.
|
# yields no modules.
|
||||||
if [ -n "${WARDEN_MODULES_COLLECT:-}" ]; then
|
if [ -n "${WARDEN_MODULES_COLLECT:-}" ]; then
|
||||||
MODOUT="$WORK/modules-out"
|
MODOUT="$WORK/modules-out"
|
||||||
rm -rf "$MODOUT"; mkdir -p "$MODOUT"
|
rm -rf "$MODOUT"; mkdir -p "$MODOUT"
|
||||||
log "building modules (full set — needed for Module.symvers)"
|
log "building modules (full set: needed for Module.symvers)"
|
||||||
make -C "$SRC" ARCH=arm CROSS_COMPILE="$CROSS_COMPILE" CC="$KCC" -j"$JOBS" modules
|
make -C "$SRC" ARCH=arm CROSS_COMPILE="$CROSS_COMPILE" CC="$KCC" -j"$JOBS" modules
|
||||||
for d in $WARDEN_MODULES_COLLECT; do
|
for d in $WARDEN_MODULES_COLLECT; do
|
||||||
[ -d "$SRC/$d" ] || { echo "FATAL: WARDEN_MODULES_COLLECT dir '$d' not in tree" >&2; exit 1; }
|
[ -d "$SRC/$d" ] || { echo "FATAL: WARDEN_MODULES_COLLECT dir '$d' not in tree" >&2; exit 1; }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Fetch (with retries) and sha256-verify the pristine kernel tarball into $1.
|
# Fetch (with retries) and sha256-verify the pristine kernel tarball into $1.
|
||||||
# Single source of truth for the URL + verification used by build-kernel.sh
|
# Single source of truth for the URL + verification used by build-kernel.sh
|
||||||
# and both CI jobs (patches-apply, kernel-build) — a KVER bump edits this file
|
# and both CI jobs (patches-apply, kernel-build): a KVER bump edits this file
|
||||||
# and build-kernel.sh only. FAILS CLOSED: a missing pin refuses to proceed.
|
# and build-kernel.sh only. FAILS CLOSED: a missing pin refuses to proceed.
|
||||||
#
|
#
|
||||||
# Usage: fetch-kernel-tarball.sh <destination-path>
|
# Usage: fetch-kernel-tarball.sh <destination-path>
|
||||||
@@ -17,7 +17,7 @@ TB="${1:?usage: fetch-kernel-tarball.sh <destination-path>}"
|
|||||||
# Pin first: a forgotten pin on a KVER bump should refuse BEFORE burning a
|
# Pin first: a forgotten pin on a KVER bump should refuse BEFORE burning a
|
||||||
# 140MB download it will then reject anyway.
|
# 140MB download it will then reject anyway.
|
||||||
[ -f "$SHA_FILE" ] || {
|
[ -f "$SHA_FILE" ] || {
|
||||||
echo "FATAL: no pinned sha256 for linux-$KVER (expected $SHA_FILE) — refusing an unverified tarball" >&2
|
echo "FATAL: no pinned sha256 for linux-$KVER (expected $SHA_FILE): refusing an unverified tarball" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
if [ ! -f "$TB" ]; then
|
if [ ! -f "$TB" ]; then
|
||||||
|
|||||||
+36
-36
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
How the SDK makes the 86 Panel buildable, testable, and hardenable without a
|
How the SDK makes the 86 Panel buildable, testable, and hardenable without a
|
||||||
board in the loop. The seam inventory below comes from a full survey of the
|
board in the loop. The seam inventory below comes from a full survey of the
|
||||||
downstream WardenOS firmware — the SDK's first consumer — not aspiration; the
|
downstream WardenOS firmware (the SDK's first consumer), not aspiration; the
|
||||||
file paths in it point into that (private) codebase and serve as engineering
|
file paths in it point into that (private) codebase and serve as engineering
|
||||||
context.
|
context.
|
||||||
|
|
||||||
@@ -13,19 +13,19 @@ tested (or not) differently. Today:
|
|||||||
|
|
||||||
| Block | Where | Access | Test seam today | Fails on host by |
|
| Block | Where | Access | Test seam today | Fails on host by |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| Registers / SRAM (CRU reset, HPMCU mailbox) | `flared/src/devmem.rs`, `hpmcu.rs` | `/dev/mem` mmap `peek/poke32` | **none** — zero tests | (would fault; not exercised) |
|
| Registers / SRAM (CRU reset, HPMCU mailbox) | `flared/src/devmem.rs`, `hpmcu.rs` | `/dev/mem` mmap `peek/poke32` | **none**, zero tests | (would fault; not exercised) |
|
||||||
| HPMCU / RISC-V coproc | `flared/src/hpmcu.rs` | via devmem + firmware blob load | `WARDEN_HPMCU_FW` redirects the blob path only | env gate disables it |
|
| HPMCU / RISC-V coproc | `flared/src/hpmcu.rs` | via devmem + firmware blob load | `WARDEN_HPMCU_FW` redirects the blob path only | env gate disables it |
|
||||||
| NPU load | `ui-src/.../sysmon.c` | read `/proc/rknpu/load` | **none** — literal path | file absent → "NPU absent" |
|
| NPU load | `ui-src/.../sysmon.c` | read `/proc/rknpu/load` | **none**, literal path | file absent -> "NPU absent" |
|
||||||
| RGA (2D blit) | `ui-src/.../warden_rga.c` | `librga improcess` + dma-heap ioctl | compile-time `#if WARDEN_USE_RGA` | `#if` off → LVGL software path |
|
| RGA (2D blit) | `ui-src/.../warden_rga.c` | `librga improcess` + dma-heap ioctl | compile-time `#if WARDEN_USE_RGA` | `#if` off -> LVGL software path |
|
||||||
| RS485 daemon | `warden-modbus/modbus_engine.c` | `open("/dev/ttyS4")` | recompile `-DRS485_PORT=<pty>` | (recompiled for a pty) |
|
| RS485 daemon | `warden-modbus/modbus_engine.c` | `open("/dev/ttyS4")` | recompile `-DRS485_PORT=<pty>` | (recompiled for a pty) |
|
||||||
| RS485 panel client | `ui-src/.../modbus.c` | `AF_UNIX` socket | `WARDEN_MODBUS_SOCK` env override | socket absent → "unavailable" |
|
| RS485 panel client | `ui-src/.../modbus.c` | `AF_UNIX` socket | `WARDEN_MODBUS_SOCK` env override | socket absent -> "unavailable" |
|
||||||
| Relays / GPIO | `ui-src/.../relays.c` | `/sys/class/gpio` sysfs | **none** — literal paths | path absent → "unavailable" |
|
| Relays / GPIO | `ui-src/.../relays.c` | `/sys/class/gpio` sysfs | **none**, literal paths | path absent -> "unavailable" |
|
||||||
| Slot metadata | `flared/src/slotctl.rs` | `misc` partition + `/proc/cmdline` | `WARDEN_MISC_DEV`, `WARDEN_CMDLINE_FILE` env overrides | (redirected to scratch files) |
|
| Slot metadata | `flared/src/slotctl.rs` | `misc` partition + `/proc/cmdline` | `WARDEN_MISC_DEV`, `WARDEN_CMDLINE_FILE` env overrides | (redirected to scratch files) |
|
||||||
|
|
||||||
Three patterns coexist: **compile-time `#if`** (RGA), **env-override**
|
Three patterns coexist: **compile-time `#if`** (RGA), **env-override**
|
||||||
(modbus socket, misc dev, cmdline, hpmcu fw), and
|
(modbus socket, misc dev, cmdline, hpmcu fw), and
|
||||||
**fails-soft-when-the-path-is-absent** (NPU, relays, devmem-would-fault). The
|
**fails-soft-when-the-path-is-absent** (NPU, relays, devmem-would-fault). The
|
||||||
last is not a test seam — you can inject "absent", never "relay 1 is ON" or
|
last is not a test seam: you can inject "absent", never "relay 1 is ON" or
|
||||||
"NPU at 80%". The SDK's job: **one deliberate seam per block**, each with a
|
"NPU at 80%". The SDK's job: **one deliberate seam per block**, each with a
|
||||||
real backend and a sim backend.
|
real backend and a sim backend.
|
||||||
|
|
||||||
@@ -33,14 +33,14 @@ real backend and a sim backend.
|
|||||||
|
|
||||||
Two seam kinds cover everything above:
|
Two seam kinds cover everything above:
|
||||||
|
|
||||||
- **Register/SRAM seam → a trait.** `MemBus` (`sim/src/membus.rs`):
|
- **Register/SRAM seam -> a trait.** `MemBus` (`sim/src/membus.rs`):
|
||||||
`peek32/poke32` at a physical address. Real backend = flared `devmem.rs`
|
`peek32/poke32` at a physical address. Real backend = flared `devmem.rs`
|
||||||
mmap; sim backend = `SimBus`. The HPMCU watchdog and the CRU reset ladder
|
mmap; sim backend = `SimBus`. The HPMCU watchdog and the CRU reset ladder
|
||||||
both ride this. **Built.**
|
both ride this. **Built.**
|
||||||
- **Resource-path seam → env-override + injection.** Every device/proc/sys
|
- **Resource-path seam -> env-override + injection.** Every device/proc/sys
|
||||||
path a driver opens resolves through one indirection
|
path a driver opens resolves through one indirection
|
||||||
(`warden_hw_path("npu.load")` in C, an env-overridable const in Rust) —
|
(`warden_hw_path("npu.load")` in C, an env-overridable const in Rust),
|
||||||
generalizing the existing `WARDEN_MISC_DEV`/`WARDEN_MODBUS_SOCK` pattern —
|
generalizing the existing `WARDEN_MISC_DEV`/`WARDEN_MODBUS_SOCK` pattern,
|
||||||
so a test points it at a fake file/fifo the sim writes. No LD_PRELOAD, no
|
so a test points it at a fake file/fifo the sim writes. No LD_PRELOAD, no
|
||||||
fake mounts.
|
fake mounts.
|
||||||
|
|
||||||
@@ -58,36 +58,36 @@ and supervisor logic runs in CI with no panel. All models are **done**:
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `membus` | `MemBus` trait + `SimBus` in-memory bus (`Clone`, so two "cores" alias shared memory) |
|
| `membus` | `MemBus` trait + `SimBus` in-memory bus (`Clone`, so two "cores" alias shared memory) |
|
||||||
| `hpmcu` | the watchdog coprocessor's state machine (boot-grace, heartbeat-timeout, disarm, fire) on a `SimBus` mailbox with a virtual clock; 7 tests incl. the arm-within-grace no-boot-loop safety property |
|
| `hpmcu` | the watchdog coprocessor's state machine (boot-grace, heartbeat-timeout, disarm, fire) on a `SimBus` mailbox with a virtual clock; 7 tests incl. the arm-within-grace no-boot-loop safety property |
|
||||||
| `cru` | reset ladder against the known glb_srst_fst / DW-watchdog registers, plus boot-mode register semantics (survives warm reset, cleared by POR — the MaskRom recovery maneuver) |
|
| `cru` | reset ladder against the known glb_srst_fst / DW-watchdog registers, plus boot-mode register semantics (survives warm reset, cleared by POR, the MaskRom recovery maneuver) |
|
||||||
| `modbus` | byte-in/byte-out RTU slave: CRC16 byte-identical to the master, FC 0x01–0x06/0x0F/0x10/0x11, exception replies, silent-drop and forced-NAK fault injection; doubles as the QEMU sim's field bus via `qemu/rs485-bridge/` (§7) |
|
| `modbus` | byte-in/byte-out RTU slave: CRC16 byte-identical to the master, FC 0x01-0x06/0x0F/0x10/0x11, exception replies, silent-drop and forced-NAK fault injection; doubles as the QEMU sim's field bus via `qemu/rs485-bridge/` (section 7) |
|
||||||
| `npu` | `/proc/rknpu/load` text model behind the path seam — the load-readout UI is host-testable; NPU *compute* is out of scope |
|
| `npu` | `/proc/rknpu/load` text model behind the path seam; the load-readout UI is host-testable; NPU *compute* is out of scope |
|
||||||
| `rga` | recording `improcess` fake with programmable `IM_STATUS`, exercising offload-dispatch and CPU-fallback; wired into the `rga_improcess` benchmark |
|
| `rga` | recording `improcess` fake with programmable `IM_STATUS`, exercising offload-dispatch and CPU-fallback; wired into the `rga_improcess` benchmark |
|
||||||
|
|
||||||
**Next:** MEI (0x2B/0x0E) Modbus identification; the Tier-2 driver *sources*
|
**Next:** MEI (0x2B/0x0E) Modbus identification; the Tier-2 driver *sources*
|
||||||
migrate in with the flare-edge unification (ADR-0005) — their hardware ends
|
migrate in with the flare-edge unification (ADR-0005); their hardware ends
|
||||||
are already modelled here.
|
are already modelled here.
|
||||||
|
|
||||||
**Integration with flare-edge — landed 2026-08-31** (flare-edge PR #110):
|
**Integration with flare-edge, landed 2026-08-31** (flare-edge PR #110):
|
||||||
`warden-sim` is a flared dev-dependency (through the vendored submodule), and
|
`warden-sim` is a flared dev-dependency (through the vendored submodule), and
|
||||||
unification tests in flared's own suite pin its real arm/beat and
|
unification tests in flared's own suite pin its real arm/beat and
|
||||||
reset-ladder logic to `HpmcuSim`/`CruSim`.
|
reset-ladder logic to `HpmcuSim`/`CruSim`.
|
||||||
|
|
||||||
## 4. Driver Hardening
|
## 4. Driver Hardening
|
||||||
|
|
||||||
"100% MC/DC on 100% of drivers" is infeasible literally — ~97% of driver LOC
|
"100% MC/DC on 100% of drivers" is infeasible literally (~97% of driver LOC
|
||||||
is vendor blobs (AIC8800 wifi alone is 88.5K lines) — so the target is
|
is vendor blobs; AIC8800 wifi alone is 88.5K lines), so the target is
|
||||||
tiered:
|
tiered:
|
||||||
|
|
||||||
- **Tier 1 — our own hardware code → real 100% MC/DC.** Extract the unit
|
- **Tier 1, our own hardware code -> real 100% MC/DC.** Extract the unit
|
||||||
behind a small injectable seam, mock its world, build
|
behind a small injectable seam, mock its world, build
|
||||||
`-fcondition-coverage`, enforce via the shared `drivers/enforce-mcdc.sh`
|
`-fcondition-coverage`, enforce via the shared `drivers/enforce-mcdc.sh`
|
||||||
(gcc-14 `gcov --conditions`) in the CI `mcdc` job — the proven flare-edge
|
(gcc-14 `gcov --conditions`) in the CI `mcdc` job (the proven flare-edge
|
||||||
`tests/uboot-ab` method. **Done:** `relays.c` (40/40 conditions) and
|
`tests/uboot-ab` method). **Done:** `relays.c` (40/40 conditions) and
|
||||||
`freshness.c` (66/66). **Next in:** the modbus master and the RGA
|
`freshness.c` (66/66). **Next in:** the modbus master and the RGA
|
||||||
dispatch, with the ADR-0005 unification (`drivers/README.md`).
|
dispatch, with the ADR-0005 unification (`drivers/README.md`).
|
||||||
- **Tier 2 — near-mainline small drivers → branch coverage + fault
|
- **Tier 2, near-mainline small drivers -> branch coverage + fault
|
||||||
injection.**
|
injection.**
|
||||||
- **Tier 3 — vendor blobs (AIC8800, MPP/ISP/RGA libs) → fault-injection
|
- **Tier 3, vendor blobs (AIC8800, MPP/ISP/RGA libs) -> fault-injection
|
||||||
hardening behind the seam**, not MC/DC: e.g. the SDIO-wedge recovery is
|
hardening behind the seam**, not MC/DC: e.g. the SDIO-wedge recovery is
|
||||||
tested against an injected wedge; the blob itself is untestable.
|
tested against an injected wedge; the blob itself is untestable.
|
||||||
|
|
||||||
@@ -99,11 +99,11 @@ against failure, not just the happy path.
|
|||||||
|
|
||||||
The brick was a *memory-map* fault: the boot-loaded MCU's load address
|
The brick was a *memory-map* fault: the boot-loaded MCU's load address
|
||||||
(`0x40000`) is a reserved carve-out on Thunder-Boot boards but plain kernel
|
(`0x40000`) is a reserved carve-out on Thunder-Boot boards but plain kernel
|
||||||
RAM on the 86 Panel. No behavioural sim catches that — it takes a **static
|
RAM on the 86 Panel. No behavioural sim catches that; it takes a **static
|
||||||
check against the target devicetree**, owned here as CI gates so mistakes
|
check against the target devicetree**, owned here as CI gates so mistakes
|
||||||
are caught before a flash, not on the bench.
|
are caught before a flash, not on the bench.
|
||||||
|
|
||||||
- **Built:** `tools/config-lint` — every `LOADERn=Hpmcu` `LOAD_ADDR` in the
|
- **Built:** `tools/config-lint`: every `LOADERn=Hpmcu` `LOAD_ADDR` in the
|
||||||
rkbin loader `.ini` must land inside a DT `reserved-memory` range. Its
|
rkbin loader `.ini` must land inside a DT `reserved-memory` range. Its
|
||||||
test suite encodes the c8a3 brick itself (see its README).
|
test suite encodes the c8a3 brick itself (see its README).
|
||||||
- **Next:** partition-table-vs-image-size; vermagic-vs-kernel.
|
- **Next:** partition-table-vs-image-size; vermagic-vs-kernel.
|
||||||
@@ -116,11 +116,11 @@ in the README's "What Works" boots.
|
|||||||
- **Base:** a direct forward-port of the vendor 5.10.160 tree onto pristine
|
- **Base:** a direct forward-port of the vendor 5.10.160 tree onto pristine
|
||||||
6.18.46, keeping the Buildroot LTS/uClibc userspace.
|
6.18.46, keeping the Buildroot LTS/uClibc userspace.
|
||||||
- **Not plan44/OpenWrt 6.6** (ADR-0001): that fork swaps Buildroot for
|
- **Not plan44/OpenWrt 6.6** (ADR-0001): that fork swaps Buildroot for
|
||||||
OpenWrt/musl and ships no AIC8800 kmod — a platform swap, not a port.
|
OpenWrt/musl and ships no AIC8800 kmod; a platform swap, not a port.
|
||||||
- **Not mainline alone:** no RV1106 DT/clk/display/RGA/NPU/flash-boot
|
- **Not mainline alone:** no RV1106 DT/clk/display/RGA/NPU/flash-boot
|
||||||
upstream; already-mainline rv1126 register data is reused where it
|
upstream; already-mainline rv1126 register data is reused where it
|
||||||
matches.
|
matches.
|
||||||
- **Dominant risk** was the struct-ABI break (the VLAN saga) — mitigated by
|
- **Dominant risk** was the struct-ABI break (the VLAN saga), mitigated by
|
||||||
shipping kernel moves as one matched boot+oem image, never a partial
|
shipping kernel moves as one matched boot+oem image, never a partial
|
||||||
reflash.
|
reflash.
|
||||||
- **Kept honest** by the hermetic `build/build-kernel.sh` and the
|
- **Kept honest** by the hermetic `build/build-kernel.sh` and the
|
||||||
@@ -129,7 +129,7 @@ in the README's "What Works" boots.
|
|||||||
|
|
||||||
## 7. Device Emulation
|
## 7. Device Emulation
|
||||||
|
|
||||||
A QEMU VM (`-M virt,highmem=off`, one Cortex-A7, 256M — the RV1106G3's
|
A QEMU VM (`-M virt,highmem=off`, one Cortex-A7, 256M: the RV1106G3's
|
||||||
shape) boots the real forward-ported kernel and real userspace, entering at
|
shape) boots the real forward-ported kernel and real userspace, entering at
|
||||||
`-kernel zImage`: everything below is closed rkbin blobs plus mask ROM.
|
`-kernel zImage`: everything below is closed rkbin blobs plus mask ROM.
|
||||||
Details, scenarios, and the emulated-vs-not table: `qemu/README.md` and
|
Details, scenarios, and the emulated-vs-not table: `qemu/README.md` and
|
||||||
@@ -146,21 +146,21 @@ ADR-0006.
|
|||||||
`sim/`, and the VM consumes it.
|
`sim/`, and the VM consumes it.
|
||||||
- Division of labour: NPU/RGA/HPMCU *behavior* stays `sim/`; UI *rendering
|
- Division of labour: NPU/RGA/HPMCU *behavior* stays `sim/`; UI *rendering
|
||||||
development* stays `lvglsim`; the VM is where processes, the kernel, and
|
development* stays `lvglsim`; the VM is where processes, the kernel, and
|
||||||
the network meet. §5 still applies — no behavioural sim catches memory-map
|
the network meet. section 5 still applies (no behavioural sim catches memory-map
|
||||||
faults — and "boots under emulation" is never on-silicon evidence.
|
faults), and "boots under emulation" is never on-silicon evidence.
|
||||||
|
|
||||||
## 8. Order of Work
|
## 8. Order of Work
|
||||||
|
|
||||||
1. **Simulator core** — `membus`, `hpmcu`, `cru`, `modbus`, `rga`, `npu`.
|
1. **Simulator core**: `membus`, `hpmcu`, `cru`, `modbus`, `rga`, `npu`.
|
||||||
**Done.**
|
**Done.**
|
||||||
2. **C-driver MC/DC harnesses** — `relays.c` + `freshness.c` at 100%,
|
2. **C-driver MC/DC harnesses**: `relays.c` + `freshness.c` at 100%,
|
||||||
CI-gated. **Done.**
|
CI-gated. **Done.**
|
||||||
3. **flared devmem/hpmcu seam + tests** — unified with `sim/`. **Done**
|
3. **flared devmem/hpmcu seam + tests**: unified with `sim/`. **Done**
|
||||||
(2026-08-31, flare-edge PR #110).
|
(2026-08-31, flare-edge PR #110).
|
||||||
4. **Config-lint CI gates** (§5) — the brick class of bug. **Done.**
|
4. **Config-lint CI gates** (section 5): the brick class of bug. **Done.**
|
||||||
5. **Hermetic kernel build** + the `patches-apply` gate. **Done.**
|
5. **Hermetic kernel build** + the `patches-apply` gate. **Done.**
|
||||||
6. **Kernel 5.10 → 6.18.46 forward-port** (§6, ADR-0001). **Done**
|
6. **Kernel 5.10 -> 6.18.46 forward-port** (section 6, ADR-0001). **Done**
|
||||||
(hardware-verified).
|
(hardware-verified).
|
||||||
7. **QEMU device sim** (§7, ADR-0006) — boot smoke, A/B disk harness, RS485
|
7. **QEMU device sim** (section 7, ADR-0006): boot smoke, A/B disk harness, RS485
|
||||||
bridge, portal/watchdog/clock/OTA scenarios, display+touch, real-image
|
bridge, portal/watchdog/clock/OTA scenarios, display+touch, real-image
|
||||||
boot. **Done** (emulation-verified).
|
boot. **Done** (emulation-verified).
|
||||||
|
|||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
# CI/CD
|
# CI/CD
|
||||||
|
|
||||||
`.github/workflows/ci.yml` — every job runs on GitHub-hosted `ubuntu-latest`.
|
`.github/workflows/ci.yml`: every job runs on GitHub-hosted `ubuntu-latest`.
|
||||||
No self-hosted runner is (or may be) reachable from this repo's workflows —
|
No self-hosted runner is (or may be) reachable from this repo's workflows:
|
||||||
on a public repo, a fork PR that gets one approved run could otherwise
|
on a public repo, a fork PR that gets one approved run could otherwise
|
||||||
execute code on private infrastructure (ADR-0007).
|
execute code on private infrastructure (ADR-0007).
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ execute code on private infrastructure (ADR-0007).
|
|||||||
| `bench` | ubuntu-latest | Smoke-runs the sim + rs485-bridge micro-benchmarks; emits ns/op trend JSON. |
|
| `bench` | ubuntu-latest | Smoke-runs the sim + rs485-bridge micro-benchmarks; emits ns/op trend JSON. |
|
||||||
| `patches-apply` | ubuntu-latest | Fetches pristine linux-6.18.46 (cached, sha256-verified) and applies `patches/*` in order. |
|
| `patches-apply` | ubuntu-latest | Fetches pristine linux-6.18.46 (cached, sha256-verified) and applies `patches/*` in order. |
|
||||||
| `qemu-tools` | ubuntu-latest | shellcheck on `qemu/**.sh`; builds the initramfs (pinned busybox) and the A/B disk image. |
|
| `qemu-tools` | ubuntu-latest | shellcheck on `qemu/**.sh`; builds the initramfs (pinned busybox) and the A/B disk image. |
|
||||||
| `kernel-build` | ubuntu-latest, **dispatch-only** | apt-installs the cross toolchain + qemu, `build/build-kernel.sh` → `zImage` + `rv1106-warden.dtb`, QEMU `-M virt` boot smoke (fail-closed), artifact upload (best-effort). Trigger: `gh workflow run ci.yml`. |
|
| `kernel-build` | ubuntu-latest, **dispatch-only** | apt-installs the cross toolchain + qemu, `build/build-kernel.sh` -> `zImage` + `rv1106-warden.dtb`, QEMU `-M virt` boot smoke (fail-closed), artifact upload (best-effort). Trigger: `gh workflow run ci.yml`. |
|
||||||
| `prune-artifacts` | ubuntu-latest, dispatch-only | Deletes `kernel-rv1106` artifacts beyond the newest 3. |
|
| `prune-artifacts` | ubuntu-latest, dispatch-only | Deletes `kernel-rv1106` artifacts beyond the newest 3. |
|
||||||
| `badges` | ubuntu-latest | Renders loc/tests/coverage shields on push to `main` (`[skip ci]` + `paths-ignore` loop guard). |
|
| `badges` | ubuntu-latest | Renders loc/tests/coverage shields on push to `main` (`[skip ci]` + `paths-ignore` loop guard). |
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ADR 0001 — Kernel Forward-Port
|
# ADR 0001: Kernel Forward-Port
|
||||||
|
|
||||||
**Status:** Accepted (2026-08-25). Supersedes the README's original plan44/6.6 goal.
|
**Status:** Accepted (2026-08-25). Supersedes the README's original plan44/6.6 goal.
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ deltas as a reviewable patch series in `patches/`.
|
|||||||
- Done and **hardware-verified on `warden-c8a3`**: clk, pinctrl, eMMC, GMAC, TRNG,
|
- Done and **hardware-verified on `warden-c8a3`**: clk, pinctrl, eMMC, GMAC, TRNG,
|
||||||
OTP, SARADC/TSADC, RTC, USB host, PWM/backlight, VOP display, GT911 touch, AIC8800
|
OTP, SARADC/TSADC, RTC, USB host, PWM/backlight, VOP display, GT911 touch, AIC8800
|
||||||
wifi, RGA, I2S audio, HPMCU mailbox, open NPU driver, PVTM.
|
wifi, RGA, I2S audio, HPMCU mailbox, open NPU driver, PVTM.
|
||||||
- **uClibc stays load-bearing** — RGA/MPP/ISP/NPU userspace ship as uClibc-only
|
- **uClibc stays required**: RGA/MPP/ISP/NPU userspace ship as uClibc-only
|
||||||
blobs; a glibc swap breaks media (flare-edge #51, wontfix). Any kernel bump
|
blobs; a glibc swap breaks media (flare-edge #51, wontfix). Any kernel bump
|
||||||
inherits this.
|
inherits this.
|
||||||
- Kernel bumps risk struct-ABI breaks for out-of-tree modules (the AIC8800/VLAN
|
- Kernel bumps risk struct-ABI breaks for out-of-tree modules (the AIC8800/VLAN
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ADR 0002 — Tiered MC/DC
|
# ADR 0002: Tiered MC/DC
|
||||||
|
|
||||||
**Status:** Accepted (2026-08-25).
|
**Status:** Accepted (2026-08-25).
|
||||||
|
|
||||||
@@ -11,23 +11,23 @@ that code would be theatre, not assurance.
|
|||||||
## Decision
|
## Decision
|
||||||
A **two-tier** policy, measured against the Hardware Abstraction Seam:
|
A **two-tier** policy, measured against the Hardware Abstraction Seam:
|
||||||
|
|
||||||
- **Tier 1 — our own hardware-facing code → real 100% MC/DC.** `modbus_engine.c`,
|
- **Tier 1, our own hardware-facing code -> real 100% MC/DC.** `modbus_engine.c`,
|
||||||
`relays.c`, `warden_rga.c` (wrapper), `hpmcu.rs`, `devmem.rs`, `freshness.c`, plus
|
`relays.c`, `warden_rga.c` (wrapper), `hpmcu.rs`, `devmem.rs`, `freshness.c`, plus
|
||||||
the two smallest near-mainline drivers where feasible. Enforced in CI
|
the two smallest near-mainline drivers where feasible. Enforced in CI
|
||||||
(`gcc-14 -fcondition-coverage` + `gcov-14 --conditions`). Rust uses
|
(`gcc-14 -fcondition-coverage` + `gcov-14 --conditions`). Rust uses
|
||||||
`cargo-llvm-cov` line/region coverage for now — true `--mcdc` needs a nightly
|
`cargo-llvm-cov` line/region coverage for now; true `--mcdc` needs a nightly
|
||||||
toolchain (`-Z coverage-options=condition`) and is deferred on that tooling skew;
|
toolchain (`-Z coverage-options=condition`) and is deferred on that tooling skew;
|
||||||
the C drivers carry the literal MC/DC gate.
|
the C drivers carry the literal MC/DC gate.
|
||||||
- **Tier 2 — ported/vendor drivers → fault-injection + branch coverage + benchmarks**
|
- **Tier 2, ported/vendor drivers -> fault-injection + branch coverage + benchmarks**
|
||||||
against the simulator, behind a stable seam. Explicitly NOT literal MC/DC.
|
against the simulator, behind a stable seam. Explicitly NOT literal MC/DC.
|
||||||
|
|
||||||
## Consequences
|
## Consequences
|
||||||
- Matches the user's framing: "as many drivers as possible at 100% MC/DC; for the
|
- Matches the user's framing: "as many drivers as possible at 100% MC/DC; for the
|
||||||
rest, a very serious testing and benchmarking system."
|
rest, a very serious testing and benchmarking system."
|
||||||
- The seam is the shared object: the same thing MC/DC is measured against and the
|
- The seam is the shared object: the same thing MC/DC is measured against and the
|
||||||
simulator implements — the two goals reinforce, not duplicate.
|
simulator implements; the two goals reinforce, not duplicate.
|
||||||
- Every Tier-1 file gets a `drivers/<name>/test/` host harness (a `Makefile` +
|
- Every Tier-1 file gets a `drivers/<name>/test/` host harness (a `Makefile` +
|
||||||
`test_<name>.c`) that calls the one shared `drivers/enforce-mcdc.sh` — which derives
|
`test_<name>.c`) that calls the one shared `drivers/enforce-mcdc.sh`, which derives
|
||||||
the driver name from the `.gcov` file, so there is a single gate to maintain, not a
|
the driver name from the `.gcov` file, so there is a single gate to maintain, not a
|
||||||
per-driver copy. The CI `mcdc` job auto-discovers any `drivers/*/test/Makefile` and
|
per-driver copy. The CI `mcdc` job auto-discovers any `drivers/*/test/Makefile` and
|
||||||
fails below 100%.
|
fails below 100%.
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
# ADR 0003 — Standalone Repo
|
# ADR 0003: Standalone Repo
|
||||||
|
|
||||||
**Status:** Accepted (2026-08-25). Repo-visibility half superseded by ADR-0007
|
**Status:** Accepted (2026-08-25). Repo-visibility half superseded by ADR-0007
|
||||||
(2026-08-30) — warden-sdk went public; the "private for now" consequence below
|
(2026-08-30): warden-sdk went public; the "private for now" consequence below
|
||||||
no longer holds. Original decision kept for the record.
|
no longer holds. Original decision kept for the record.
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
Our real SDK changes lived as uncommitted edits in a 2GB opaque vendor fork, with
|
Our real SDK changes lived as uncommitted edits in a 2GB opaque vendor fork, with
|
||||||
no CI, tests, or versioning of their own. The SDK requirement (future-features-2
|
no CI, tests, or versioning of their own. The SDK requirement (future-features-2,
|
||||||
§SDK) calls for "its own repo, held to firmware standards."
|
SDK section) calls for "its own repo, held to firmware standards."
|
||||||
|
|
||||||
## Decision
|
## Decision
|
||||||
A **private** `warden-sdk` GitHub repo (now `blueflare-energy/bfe-core1106-sdk` and public
|
A **private** `warden-sdk` GitHub repo (now `blueflare-energy/bfe-core1106-sdk` and public
|
||||||
@@ -17,7 +17,7 @@ on a passing review run, green CI, and the maintainer's fresh explicit go-ahead.
|
|||||||
|
|
||||||
## Consequences
|
## Consequences
|
||||||
- flare-edge consumes this repo later (flared depending on `warden-sim`, drivers
|
- flare-edge consumes this repo later (flared depending on `warden-sim`, drivers
|
||||||
built from here) — a separate, maintainer-gated integration step; flare-edge is not
|
built from here), a separate, maintainer-gated integration step; flare-edge is not
|
||||||
edited by the SDK-completion effort.
|
edited by the SDK-completion effort.
|
||||||
- Private for now (references bench devices / in-progress hardening); can be opened
|
- Private for now (references bench devices / in-progress hardening); can be opened
|
||||||
later once scrubbed, matching how `flare-deployment` is handled.
|
later once scrubbed, matching how `flare-deployment` is handled.
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
# ADR 0004 — Self-Hosted Runner
|
# ADR 0004: Self-Hosted Runner
|
||||||
|
|
||||||
**Status:** Superseded in part by ADR-0007 (2026-08-30) — `kernel-build` moved
|
**Status:** Superseded in part by ADR-0007 (2026-08-30): `kernel-build` moved
|
||||||
to GitHub-hosted runners for the public repo and the self-hosted registration
|
to GitHub-hosted runners for the public repo and the self-hosted registration
|
||||||
is retired. Original decision below, kept for the record. (2026-08-25.)
|
is retired. Original decision below, kept for the record. (2026-08-25.)
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
The heavy kernel/firmware build needs the SDK toolchain and Buildroot's baked-in
|
The heavy kernel/firmware build needs the SDK toolchain and Buildroot's baked-in
|
||||||
absolute paths — impractical on GitHub-hosted runners. flare-edge already builds on
|
absolute paths, impractical on GitHub-hosted runners. flare-edge already builds on
|
||||||
a repo-scoped self-hosted runner on `bfe-mpc-0640` (label `flare-edge`); a repo-scoped
|
a repo-scoped self-hosted runner on `bfe-mpc-0640` (label `flare-edge`); a repo-scoped
|
||||||
registration cannot be shared across repos by label alone.
|
registration cannot be shared across repos by label alone.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ADR 0005 — Driver Source of Truth
|
# ADR 0005: Driver Source of Truth
|
||||||
|
|
||||||
**Status:** Accepted (2026-08-25).
|
**Status:** Accepted (2026-08-25).
|
||||||
|
|
||||||
@@ -18,4 +18,4 @@ formalized as a patch series in `patches/`. flare-edge consumes this repo later
|
|||||||
- Realizes the seam architecture (ADR-referenced in `docs/architecture.md`).
|
- Realizes the seam architecture (ADR-referenced in `docs/architecture.md`).
|
||||||
- flare-edge is not edited now; a later integration step points flare-edge at these.
|
- flare-edge is not edited now; a later integration step points flare-edge at these.
|
||||||
- Risk: temporary duplication of shared constants between the two repos until the
|
- Risk: temporary duplication of shared constants between the two repos until the
|
||||||
integration lands — tracked, acceptable for the bring-up window.
|
integration lands; tracked, acceptable for the bring-up window.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ADR 0006 — QEMU Device Simulator
|
# ADR 0006: QEMU Device Simulator
|
||||||
|
|
||||||
**Status:** Accepted (2026-08-29).
|
**Status:** Accepted (2026-08-29).
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
The two existing simulators cannot test the *device*: `lvglsim` (flare-edge)
|
The two existing simulators cannot test the *device*: `lvglsim` (flare-edge)
|
||||||
is an SDL rendering harness, and `sim/` models registers behind driver seams.
|
is an SDL rendering harness, and `sim/` models registers behind driver seams.
|
||||||
Init ordering, the daemons as real processes, networking/enrollment against
|
Init ordering, the daemons as real processes, networking/enrollment against
|
||||||
FLARE, OTA, and the watchdog were testable only on a bench panel — flare-edge's
|
FLARE, OTA, and the watchdog were testable only on a bench panel; flare-edge's
|
||||||
fault suite marks five scenarios "HIL, human prompts", its Playwright e2e needs
|
fault suite marks five scenarios "HIL, human prompts", its Playwright e2e needs
|
||||||
a live panel on the LAN, and its OTA desk test stops at "reached APPLYING".
|
a live panel on the LAN, and its OTA desk test stops at "reached APPLYING".
|
||||||
|
|
||||||
@@ -14,12 +14,12 @@ Two ways to emulate the panel were considered:
|
|||||||
|
|
||||||
1. **A custom RV1106 QEMU board model.** Nothing exists upstream or in the
|
1. **A custom RV1106 QEMU board model.** Nothing exists upstream or in the
|
||||||
community, so this means writing VOP/CRU/GRF/eMMC/HPMCU device models from
|
community, so this means writing VOP/CRU/GRF/eMMC/HPMCU device models from
|
||||||
scratch and maintaining them against QEMU — months of work that duplicates
|
scratch and maintaining them against QEMU: months of work that duplicates
|
||||||
what `sim/` already models in Rust. It still could not run the boot chain:
|
what `sim/` already models in Rust. It still could not run the boot chain:
|
||||||
BootROM is mask ROM and the DDR-init/idblock stages are closed rkbin blobs.
|
BootROM is mask ROM and the DDR-init/idblock stages are closed rkbin blobs.
|
||||||
2. **The generic `-M virt` machine, entering at `-kernel zImage`.** The
|
2. **The generic `-M virt` machine, entering at `-kernel zImage`.** The
|
||||||
forward-ported 6.18.46 config is multi_v7-derived and already carries
|
forward-ported 6.18.46 config is multi_v7-derived and already carries
|
||||||
`ARCH_VIRT` plus the full virtio set — the canonical RV1106 zImage boots
|
`ARCH_VIRT` plus the full virtio set; the canonical RV1106 zImage boots
|
||||||
virt **unmodified** (verified 2026-08-29). Peripherals become virtio
|
virt **unmodified** (verified 2026-08-29). Peripherals become virtio
|
||||||
substitutes; SoC-block behavior stays in `sim/`, bridged in (the RS485
|
substitutes; SoC-block behavior stays in `sim/`, bridged in (the RS485
|
||||||
chardev bridge) rather than re-modeled.
|
chardev bridge) rather than re-modeled.
|
||||||
@@ -28,11 +28,11 @@ Two ways to emulate the panel were considered:
|
|||||||
Option 2. `qemu/` holds the harness: `qemu-system-arm -M virt,highmem=off
|
Option 2. `qemu/` holds the harness: `qemu-system-arm -M virt,highmem=off
|
||||||
-cpu cortex-a7 -smp 1 -m 256M` (the RV1106G3's shape), one canonical kernel
|
-cpu cortex-a7 -smp 1 -m 256M` (the RV1106G3's shape), one canonical kernel
|
||||||
image plus an optional additive config fragment (`qemu/configs/virt.fragment`
|
image plus an optional additive config fragment (`qemu/configs/virt.fragment`
|
||||||
via the `WARDEN_KCONFIG_FRAGMENT` hook — PCI/pci-serial/i6300esb/WireGuard/
|
via the `WARDEN_KCONFIG_FRAGMENT` hook: PCI/pci-serial/i6300esb/WireGuard/
|
||||||
virtio-gpu/virtio-input; the RV1106 build is byte-identical with the variable
|
virtio-gpu/virtio-input; the RV1106 build is byte-identical with the variable
|
||||||
unset). The VM carries the device's real 12-partition `blkdevparts=` A/B
|
unset). The VM carries the device's real 12-partition `blkdevparts=` A/B
|
||||||
layout on a virtio disk and populates the `/dev/block/by-name/` contract.
|
layout on a virtio disk and populates the `/dev/block/by-name/` contract.
|
||||||
The name is `qemu/`, not any variant of "sim" — the wikis already warn that
|
The name is `qemu/`, not any variant of "sim"; the wikis already warn that
|
||||||
"sim" is two different things.
|
"sim" is two different things.
|
||||||
|
|
||||||
Notable mechanics: `highmem=off` because the non-LPAE 32-bit kernel cannot
|
Notable mechanics: `highmem=off` because the non-LPAE 32-bit kernel cannot
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ADR 0007 — Hosted-Only CI
|
# ADR 0007: Hosted-Only CI
|
||||||
|
|
||||||
**Status:** Accepted (2026-08-30). Supersedes the runner half of ADR-0004.
|
**Status:** Accepted (2026-08-30). Supersedes the runner half of ADR-0004.
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ alignment with the stack philosophy). Two facts change the ADR-0004 calculus:
|
|||||||
|
|
||||||
1. **A self-hosted runner on a public repo is a standing hazard.** A fork PR
|
1. **A self-hosted runner on a public repo is a standing hazard.** A fork PR
|
||||||
can modify workflow files; once any run of theirs is approved, workflows
|
can modify workflow files; once any run of theirs is approved, workflows
|
||||||
can target the repo's registered self-hosted runners — i.e. arbitrary code
|
can target the repo's registered self-hosted runners, i.e. arbitrary code
|
||||||
on the private host, which also serves production. GitHub's own guidance is
|
on the private host, which also serves production. GitHub's own guidance is
|
||||||
to never attach self-hosted runners to public repos, and personal-account
|
to never attach self-hosted runners to public repos, and personal-account
|
||||||
repos have no runner groups to scope the risk away.
|
repos have no runner groups to scope the risk away.
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# ADR 0008 — Panel Development Environment
|
# ADR 0008: Panel Development Environment
|
||||||
|
|
||||||
**Status:** Accepted (2026-08-30).
|
**Status:** Accepted (2026-08-30).
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
warden-sdk was written as the SDK for WardenOS, and its documentation framed
|
warden-sdk was written as the SDK for WardenOS, and its documentation framed
|
||||||
it that way: a support repo for one product. Since going public (ADR-0007,
|
it that way: a support repo for one product. Since going public (ADR-0007,
|
||||||
GPL-2.0-only), the actual audience is wider — anyone with a Luckfox Pico 86
|
GPL-2.0-only), the actual audience is wider: anyone with a Luckfox Pico 86
|
||||||
Panel gets a maintained 6.18 kernel, an off-device development loop, and a
|
Panel gets a maintained 6.18 kernel, an off-device development loop, and a
|
||||||
device simulator out of this repo, none of which exists elsewhere for this
|
device simulator out of this repo, none of which exists elsewhere for this
|
||||||
board. The product-first framing undersold that and confused the entry point
|
board. The product-first framing undersold that and confused the entry point
|
||||||
@@ -16,8 +16,8 @@ Position warden-sdk as **a modern, open development environment for the
|
|||||||
Luckfox Pico 86 Panel (RV1106)**. WardenOS is documented as the downstream
|
Luckfox Pico 86 Panel (RV1106)**. WardenOS is documented as the downstream
|
||||||
consumer it is, not the purpose. Documentation follows three rules: lead with
|
consumer it is, not the purpose. Documentation follows three rules: lead with
|
||||||
the board, not the product; keep private-repo references clearly marked as
|
the board, not the product; keep private-repo references clearly marked as
|
||||||
context; keep titles short — a heading names a section, it does not summarize
|
context; keep titles short (a heading names a section,, it does not summarize
|
||||||
it.
|
it).
|
||||||
|
|
||||||
## Consequences
|
## Consequences
|
||||||
- README and top-level docs lead with the hardware and the developer loop
|
- README and top-level docs lead with the hardware and the developer loop
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# NPU Graphics Feasibility
|
# NPU Graphics Feasibility
|
||||||
|
|
||||||
> Point-in-time engineering study, written while scoping the `rknpu.ko` 6.18
|
> Point-in-time engineering study, written while scoping the `rknpu.ko` 6.18
|
||||||
> port for the downstream WardenOS firmware — "our boards" / "this product"
|
> port for the downstream WardenOS firmware; "our boards" / "this product"
|
||||||
> below refer to that context. The hardware conclusions apply to any 86 Panel.
|
> below refer to that context. The hardware conclusions apply to any 86 Panel.
|
||||||
|
|
||||||
**Bottom line: no, not for 3D rendering — not "slower," but genuinely not how the
|
**Bottom line: no, not for 3D rendering; not "slower," but genuinely not how the
|
||||||
hardware works past the first pipeline stage. The RKNPU on RV1106 is a
|
hardware works past the first pipeline stage. The RKNPU on RV1106 is a
|
||||||
fixed-function, INT8-only, command-stream tensor accelerator with no rasterizer,
|
fixed-function, INT8-only, command-stream tensor accelerator with no rasterizer,
|
||||||
no texture unit, no per-pixel programmability, and no framebuffer output; only
|
no texture unit, no per-pixel programmability, and no framebuffer output; only
|
||||||
@@ -20,7 +20,7 @@ non-visual inference, not for graphics.**
|
|||||||
|
|
||||||
This document answers a single question raised while planning that port: given
|
This document answers a single question raised while planning that port: given
|
||||||
we're about to carry `rknpu.ko` forward to a new kernel, could the NPU pull any
|
we're about to carry `rknpu.ko` forward to a new kernel, could the NPU pull any
|
||||||
graphics weight on a GPU-less SoC? Facts are cited to their source — the
|
graphics weight on a GPU-less SoC? Facts are cited to their source: the
|
||||||
hardware wiki (`luckfox-pico-86-panel/`), the product wiki
|
hardware wiki (`luckfox-pico-86-panel/`), the product wiki
|
||||||
(`flare-edge-construction/`), the vendored SDK kernel driver source
|
(`flare-edge-construction/`), the vendored SDK kernel driver source
|
||||||
(`flare-edge/sdk/sysdrv/source/kernel/drivers/rknpu/`), the RV1106 devicetree,
|
(`flare-edge/sdk/sysdrv/source/kernel/drivers/rknpu/`), the RV1106 devicetree,
|
||||||
@@ -32,7 +32,7 @@ or flagged as general knowledge / needing TRM confirmation.
|
|||||||
|
|
||||||
### Identity and Generation
|
### Identity and Generation
|
||||||
|
|
||||||
- It is the **RKNPU** — Rockchip's 4th-generation NPU IP, exposed to tooling as
|
- It is the **RKNPU**, Rockchip's 4th-generation NPU IP, exposed to tooling as
|
||||||
the "RKNPU2" software generation (same toolchain family as RK3566/68/88), but
|
the "RKNPU2" software generation (same toolchain family as RK3566/68/88), but
|
||||||
RV1103/RV1106 sit in that family's **INT8-only tier**: RKNN-Toolkit2 conversion
|
RV1103/RV1106 sit in that family's **INT8-only tier**: RKNN-Toolkit2 conversion
|
||||||
requires `quantize=8`, with no mixed-precision or FP path available on this
|
requires `quantize=8`, with no mixed-precision or FP path available on this
|
||||||
@@ -40,7 +40,7 @@ or flagged as general knowledge / needing TRM confirmation.
|
|||||||
(`luckfox-pico-86-panel/npu.md:7`, `soc-rv1106.md`)
|
(`luckfox-pico-86-panel/npu.md:7`, `soc-rv1106.md`)
|
||||||
- The chip's own datasheet lists it as supporting mixed **INT4/INT8/INT16**
|
- The chip's own datasheet lists it as supporting mixed **INT4/INT8/INT16**
|
||||||
precision at the IP-family level, and states it "supports creating simple
|
precision at the IP-family level, and states it "supports creating simple
|
||||||
custom operators" (the RKNN custom-op extension) — but this is an extension
|
custom operators" (the RKNN custom-op extension), but this is an extension
|
||||||
mechanism for adding a new *operator* to the compiled-graph model, not general
|
mechanism for adding a new *operator* to the compiled-graph model, not general
|
||||||
programmability (see below). (`luckfox-pico-86-panel/npu.md:7`,
|
programmability (see below). (`luckfox-pico-86-panel/npu.md:7`,
|
||||||
`raw/web-camera-isp-npu.md:9`)
|
`raw/web-camera-isp-npu.md:9`)
|
||||||
@@ -52,7 +52,7 @@ or flagged as general knowledge / needing TRM confirmation.
|
|||||||
*older* 0.5/1.0 split. Which figure applies to a given 86-Panel unit also
|
*older* 0.5/1.0 split. Which figure applies to a given 86-Panel unit also
|
||||||
depends on its SKU (0208/0408 = G2, 1208/1408 = G3), which isn't confirmed for
|
depends on its SKU (0208/0408 = G2, 1208/1408 = G3), which isn't confirmed for
|
||||||
our boards. **Any TOPS number in this document should be read as "half to one
|
our boards. **Any TOPS number in this document should be read as "half to one
|
||||||
TOPS, disputed" — not a precise spec.** (`luckfox-pico-86-panel/npu.md:8`,
|
TOPS, disputed", not a precise spec.** (`luckfox-pico-86-panel/npu.md:8`,
|
||||||
`soc-rv1106.md:31-45`)
|
`soc-rv1106.md:31-45`)
|
||||||
- One real measured clock point exists at all: a leaked-but-mirrored Rockchip
|
- One real measured clock point exists at all: a leaked-but-mirrored Rockchip
|
||||||
internal power-test report states a "typical IPC workload" corner of **NPU
|
internal power-test report states a "typical IPC workload" corner of **NPU
|
||||||
@@ -62,7 +62,7 @@ or flagged as general knowledge / needing TRM confirmation.
|
|||||||
|
|
||||||
### Architecture
|
### Architecture
|
||||||
|
|
||||||
This is the load-bearing fact for everything downstream. Reading the vendored
|
Everything downstream rests on this fact. Reading the vendored
|
||||||
kernel driver source directly (`flare-edge/sdk/sysdrv/source/kernel/drivers/rknpu/`):
|
kernel driver source directly (`flare-edge/sdk/sysdrv/source/kernel/drivers/rknpu/`):
|
||||||
|
|
||||||
- The driver's register offsets (`rknpu_ioctl.h`) center on a **"PC" (program
|
- The driver's register offsets (`rknpu_ioctl.h`) center on a **"PC" (program
|
||||||
@@ -74,38 +74,38 @@ kernel driver source directly (`flare-edge/sdk/sysdrv/source/kernel/drivers/rknp
|
|||||||
the hardware executes that command stream to completion, raising an interrupt
|
the hardware executes that command stream to completion, raising an interrupt
|
||||||
the driver waits on (`wait_event_timeout(...msecs_to_jiffies(args->timeout))`
|
the driver waits on (`wait_event_timeout(...msecs_to_jiffies(args->timeout))`
|
||||||
in `rknpu_job.c`). This is **not** a shader core fetching and executing
|
in `rknpu_job.c`). This is **not** a shader core fetching and executing
|
||||||
arbitrary instructions per invocation — it's closer to a DMA-fed
|
arbitrary instructions per invocation; it's closer to a DMA-fed
|
||||||
fixed-function pipeline being told "run this pre-built op sequence over these
|
fixed-function pipeline being told "run this pre-built op sequence over these
|
||||||
buffers."
|
buffers."
|
||||||
- **The actual op-code semantics of that regcmd stream are not publicly
|
- **The actual op-code semantics of that regcmd stream are not publicly
|
||||||
documented.** The hardware wiki is explicit: register-level detail (TRM Part 2)
|
documented.** The hardware wiki is explicit: register-level detail (TRM Part 2)
|
||||||
"does not exist publicly — confirmed by exhausting all known Rockchip doc
|
"does not exist publicly, confirmed by exhausting all known Rockchip doc
|
||||||
mirrors," including Rockchip's own NPU SDK guide, which is "pure userspace-API
|
mirrors," including Rockchip's own NPU SDK guide, which is "pure userspace-API
|
||||||
reference with zero register offsets." NPU access on this SoC is
|
reference with zero register offsets." NPU access on this SoC is
|
||||||
"architecturally gated behind the RKNN userspace API stack, not just
|
"architecturally gated behind the RKNN userspace API stack, not just
|
||||||
under-documented." (`luckfox-pico-86-panel/npu.md:14`) This means: **no one
|
under-documented." (`luckfox-pico-86-panel/npu.md:14`) This means: **no one
|
||||||
outside Rockchip's compiler team can hand-write a regcmd stream that does
|
outside Rockchip's compiler team can hand-write a regcmd stream that does
|
||||||
something novel** (e.g., a rasterizer) — only the closed RKNN-Toolkit2
|
something novel** (e.g., a rasterizer); only the closed RKNN-Toolkit2
|
||||||
compiler emits valid ones, by lowering a supported ONNX graph (conv, pool,
|
compiler emits valid ones, by lowering a supported ONNX graph (conv, pool,
|
||||||
elementwise, activation, and similar tensor ops) into that command format.
|
elementwise, activation, and similar tensor ops) into that command format.
|
||||||
What "operators" the compiler can lower is itself the real ISA surface, and it
|
What "operators" the compiler can lower is itself the real ISA surface, and it
|
||||||
is a CNN operator set, not a general instruction set. *(Flag: this document's
|
is a CNN operator set, not a general instruction set. *(Flag: this document's
|
||||||
claim that no rasterization/gather/sampling primitive exists in the regcmd ISA
|
claim that no rasterization/gather/sampling primitive exists in the regcmd ISA
|
||||||
is reasoned from the RKNN operator taxonomy and general 4th-gen RKNPU
|
is reasoned from the RKNN operator taxonomy and general 4th-gen RKNPU
|
||||||
architecture, not from register-level ground truth — the TRM that would settle
|
architecture, not from register-level ground truth; the TRM that would settle
|
||||||
it definitively does not exist publicly. Treat as high-confidence, not
|
it definitively does not exist publicly. Treat as high-confidence, not
|
||||||
certain.)*
|
certain.)*
|
||||||
- **Is there a lower-level submit path than the RKNN runtime?** Technically yes
|
- **Is there a lower-level submit path than the RKNN runtime?** Technically yes:
|
||||||
— `DRM_IOCTL_RKNPU_SUBMIT` / `IOCTL_RKNPU_SUBMIT` accepts a raw
|
`DRM_IOCTL_RKNPU_SUBMIT` / `IOCTL_RKNPU_SUBMIT` accepts a raw
|
||||||
`regcfg_obj_addr`/`regcmd_addr` task list directly; nothing in the kernel
|
`regcfg_obj_addr`/`regcmd_addr` task list directly; nothing in the kernel
|
||||||
driver *requires* going through `librknnrt`. But this is the same interface
|
driver *requires* going through `librknnrt`. But this is the same interface
|
||||||
the RKNN runtime itself calls internally — the driver has no knowledge of
|
the RKNN runtime itself calls internally; the driver has no knowledge of
|
||||||
"operators" at all, only "a command buffer and some memory handles." Since the
|
"operators" at all, only "a command buffer and some memory handles." Since the
|
||||||
command-buffer format is closed, this ioctl is not a usable "write your own
|
command-buffer format is closed, this ioctl is not a usable "write your own
|
||||||
compute kernel" door for us; it's an implementation detail of the vendor
|
compute kernel" door for us; it's an implementation detail of the vendor
|
||||||
runtime we'd be building on top of via the RKNN C API regardless.
|
runtime we'd be building on top of via the RKNN C API regardless.
|
||||||
- Custom operators (per the datasheet's "creating simple custom operators")
|
- Custom operators (per the datasheet's "creating simple custom operators")
|
||||||
extend the *model graph* with a new node type — still compiled by the RKNN
|
extend the *model graph* with a new node type, still compiled by the RKNN
|
||||||
toolchain into the same regcmd format, still constrained to whatever
|
toolchain into the same regcmd format, still constrained to whatever
|
||||||
primitive operations the hardware's fixed-function units implement
|
primitive operations the hardware's fixed-function units implement
|
||||||
underneath. It is not a route to arbitrary per-element or per-pixel code.
|
underneath. It is not a route to arbitrary per-element or per-pixel code.
|
||||||
@@ -113,75 +113,75 @@ kernel driver source directly (`flare-edge/sdk/sysdrv/source/kernel/drivers/rknp
|
|||||||
### Data Types and Memory
|
### Data Types and Memory
|
||||||
|
|
||||||
- **INT8-only quantization tier** for RV1106/RV1103 (`quantize=8` mandatory at
|
- **INT8-only quantization tier** for RV1106/RV1103 (`quantize=8` mandatory at
|
||||||
conversion time) — inputs/outputs must be int8 and strictly 4-D.
|
conversion time); inputs/outputs must be int8 and strictly 4-D.
|
||||||
LayerNormalization and ReduceL2 aren't supported by the RKNN parser at all and
|
LayerNormalization and ReduceL2 aren't supported by the RKNN parser at all and
|
||||||
must run on the Cortex-A7 before/after the NPU call.
|
must run on the Cortex-A7 before/after the NPU call.
|
||||||
(`luckfox-pico-86-panel/npu.md:35`, `raw/web-camera-isp-npu.md:43`)
|
(`luckfox-pico-86-panel/npu.md:35`, `raw/web-camera-isp-npu.md:43`)
|
||||||
- **No dedicated VRAM.** The NPU shares the same in-package DDR3L as everything
|
- **No dedicated VRAM.** The NPU shares the same in-package DDR3L as everything
|
||||||
else — 128MB (G2) or 256MB (G3) total, shared with Linux, any RGA/ISP buffers,
|
else: 128MB (G2) or 256MB (G3) total, shared with Linux, any RGA/ISP buffers,
|
||||||
and NPU weights/activations. There is a small on-chip scratch: `NPU_CBUF`
|
and NPU weights/activations. There is a small on-chip scratch: `NPU_CBUF`
|
||||||
256KB SRAM plus an optional SRAM/NBUF allocation path in the driver
|
256KB SRAM plus an optional SRAM/NBUF allocation path in the driver
|
||||||
(`rknpu_gem.c`, `RKNPU_MEM_TRY_ALLOC_SRAM`/`_NBUF`) — a cache for
|
(`rknpu_gem.c`, `RKNPU_MEM_TRY_ALLOC_SRAM`/`_NBUF`), a cache for
|
||||||
weights/activations, not a general framebuffer-sized memory.
|
weights/activations, not a general framebuffer-sized memory.
|
||||||
(`luckfox-pico-86-panel/npu.md:10`, `soc-rv1106.md:47-98`)
|
(`luckfox-pico-86-panel/npu.md:10`, `soc-rv1106.md:47-98`)
|
||||||
- **No IOMMU wired on this board.** Boot log shows `"rknpu iommu device-tree
|
- **No IOMMU wired on this board.** Boot log shows `"rknpu iommu device-tree
|
||||||
entry not found!, using non-iommu mode"` — every buffer the NPU touches today
|
entry not found!, using non-iommu mode"`; every buffer the NPU touches today
|
||||||
must be physically-contiguous DMA memory, the same constraint RGA has on this
|
must be physically-contiguous DMA memory, the same constraint RGA has on this
|
||||||
chip. (`luckfox-pico-86-panel/npu.md:26`)
|
chip. (`luckfox-pico-86-panel/npu.md:26`)
|
||||||
- **Real-world binding constraint is RAM, not TOPS**: forum/GitHub evidence
|
- **Real-world binding constraint is RAM, not TOPS**: forum/GitHub evidence
|
||||||
shows YOLOv5s running but YOLOv8/YOLOv10 hitting memory errors on RV1106 —
|
shows YOLOv5s running but YOLOv8/YOLOv10 hitting memory errors on RV1106;
|
||||||
the 128–256MB shared-DDR budget is the practical ceiling well before compute
|
the 128-256MB shared-DDR budget is the practical ceiling well before compute
|
||||||
is. (`luckfox-pico-86-panel/npu.md:38`)
|
is. (`luckfox-pico-86-panel/npu.md:38`)
|
||||||
|
|
||||||
### Software Stack
|
### Software Stack
|
||||||
|
|
||||||
1. **Kernel driver** (`rknpu.ko`, currently v0.9.2 on our shipped firmware) —
|
1. **Kernel driver** (`rknpu.ko`, currently v0.9.2 on our shipped firmware):
|
||||||
exposes `/dev/rknpu` (a DRM device or a misc device, selectable at build time
|
exposes `/dev/rknpu` (a DRM device or a misc device, selectable at build time
|
||||||
via `ROCKCHIP_RKNPU_DRM_GEM` vs `ROCKCHIP_RKNPU_DMA_HEAP`, see
|
via `ROCKCHIP_RKNPU_DRM_GEM` vs `ROCKCHIP_RKNPU_DMA_HEAP`, see
|
||||||
`drivers/rknpu/Kconfig`), handles memory allocation (`RKNPU_MEM_CREATE` /
|
`drivers/rknpu/Kconfig`), handles memory allocation (`RKNPU_MEM_CREATE` /
|
||||||
`_MAP` / `_DESTROY` / `_SYNC` ioctls), job submission
|
`_MAP` / `_DESTROY` / `_SYNC` ioctls), job submission
|
||||||
(`RKNPU_SUBMIT`), and misc actions (frequency/voltage/power, bandwidth
|
(`RKNPU_SUBMIT`), and misc actions (frequency/voltage/power, bandwidth
|
||||||
priority — `RKNPU_ACTION` ioctl enum in `rknpu_ioctl.h`).
|
priority; `RKNPU_ACTION` ioctl enum in `rknpu_ioctl.h`).
|
||||||
2. **RKNN userspace runtime** (on-device C API — the *only* supported on-target
|
2. **RKNN userspace runtime** (on-device C API, the *only* supported on-target
|
||||||
API on RV1106/RV1103; the Python API is PC-side verification only): `rknn_init()`
|
API on RV1106/RV1103; the Python API is PC-side verification only): `rknn_init()`
|
||||||
→ `rknn_query()` → `rknn_create_mem()` → `rknn_set_io_mem()` → `memcpy()` input
|
-> `rknn_query()` -> `rknn_create_mem()` -> `rknn_set_io_mem()` -> `memcpy()` input
|
||||||
→ `rknn_run()` → dequantize `float = (int_output - zero_point) * scale`.
|
-> `rknn_run()` -> dequantize `float = (int_output - zero_point) * scale`.
|
||||||
(`luckfox-pico-86-panel/npu.md:34`)
|
(`luckfox-pico-86-panel/npu.md:34`)
|
||||||
3. **RKNN-Toolkit2** (PC-side, x86 only, Python, Ubuntu-only wheels) — the
|
3. **RKNN-Toolkit2** (PC-side, x86 only, Python, Ubuntu-only wheels): the
|
||||||
offline compiler: train (PyTorch/TF) → export ONNX → convert/quantize to a
|
offline compiler: train (PyTorch/TF) -> export ONNX -> convert/quantize to a
|
||||||
`.rknn` file. This step is where the regcmd command stream actually gets
|
`.rknn` file. This step is where the regcmd command stream actually gets
|
||||||
generated; it happens once, offline, not per-frame. (`luckfox-pico-86-panel/npu.md:33`)
|
generated; it happens once, offline, not per-frame. (`luckfox-pico-86-panel/npu.md:33`)
|
||||||
|
|
||||||
**On our product today**: `rknpu.ko` is loaded (`S24npu` init script) and
|
**On our product today**: `rknpu.ko` is loaded (`S24npu` init script) and
|
||||||
`/proc/rknpu/load` is polled purely to drive the Monitor page's NPU-utilization
|
`/proc/rknpu/load` is polled purely to drive the Monitor page's NPU-load
|
||||||
graph — there is no evidence anywhere in the platform wiki's raw notes of an
|
graph; there is no evidence anywhere in the platform wiki's raw notes of an
|
||||||
actual `rknn_init()`/`rknn_run()` inference workload ever having been run on
|
actual `rknn_init()`/`rknn_run()` inference workload ever having been run on
|
||||||
this product. The NPU is live and idle from a compute standpoint.
|
this product. The NPU is live and idle from a compute standpoint.
|
||||||
(`luckfox-pico-86-panel/npu.md:28`) `/proc/rknpu/volt` is a confirmed SIGSEGV
|
(`luckfox-pico-86-panel/npu.md:28`) `/proc/rknpu/volt` is a confirmed SIGSEGV
|
||||||
footgun on this board (no regulator wired) — never poll it.
|
footgun on this board (no regulator wired): never poll it.
|
||||||
(`luckfox-pico-86-panel/npu.md:27`)
|
(`luckfox-pico-86-panel/npu.md:27`)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. 3D Rendering, Stage by Stage
|
## 2. 3D Rendering, Stage by Stage
|
||||||
|
|
||||||
A conventional 3D pipeline: **vertex transform → primitive assembly →
|
A conventional 3D pipeline: **vertex transform -> primitive assembly ->
|
||||||
rasterization → depth test → texture sampling → per-pixel shading →
|
rasterization -> depth test -> texture sampling -> per-pixel shading ->
|
||||||
framebuffer write.** Verdict per stage, given everything in §1:
|
framebuffer write.** Verdict per stage, given everything in section 1:
|
||||||
|
|
||||||
| Stage | Maps to NPU? | Why / how |
|
| Stage | Maps to NPU? | Why / how |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **Vertex transform** (MVP matrix × vertices) | **Yes, in principle** | This is exactly GEMM/matmul — the NPU's actual strength as a tensor accelerator. A batch of vertices as an input tensor, a weight-like MVP matrix, one matmul op. This is the *only* stage that's hardware-shape-compatible. |
|
| **Vertex transform** (MVP matrix x vertices) | **Yes, in principle** | This is exactly GEMM/matmul, the NPU's actual strength as a tensor accelerator. A batch of vertices as an input tensor, a weight-like MVP matrix, one matmul op. This is the *only* stage that's hardware-shape-compatible. |
|
||||||
| **Primitive assembly** (grouping vertices into triangles) | **No** | Not a tensor op at all — it's index-buffer bookkeeping/control flow. Trivial on a CPU, meaningless to express as a conv/pool/elementwise graph. |
|
| **Primitive assembly** (grouping vertices into triangles) | **No** | Not a tensor op at all; it's index-buffer bookkeeping/control flow. Trivial on a CPU, meaningless to express as a conv/pool/elementwise graph. |
|
||||||
| **Rasterization** (triangle scan-conversion, edge functions, coverage) | **No — not how the hardware works** | There is no scan-conversion primitive in the RKNN operator set or (as far as the undocumented regcmd ISA can be inferred) the hardware's fixed-function units. A CNN accelerator computes dense/windowed reductions over a tensor; it has no per-primitive geometric test. This is not "slow," it's absent. |
|
| **Rasterization** (triangle scan-conversion, edge functions, coverage) | **No, not how the hardware works** | There is no scan-conversion primitive in the RKNN operator set or (as far as the undocumented regcmd ISA can be inferred) the hardware's fixed-function units. A CNN accelerator computes dense/windowed reductions over a tensor; it has no per-primitive geometric test. This is not "slow," it's absent. |
|
||||||
| **Depth test** (per-pixel z-buffer compare-and-write) | **No** | Requires a read-compare-conditional-write per pixel against arbitrary prior state — not an operation in the conv/pool/elementwise/activation vocabulary, and there's no depth-buffer-shaped hardware resource on this IP. |
|
| **Depth test** (per-pixel z-buffer compare-and-write) | **No** | Requires a read-compare-conditional-write per pixel against arbitrary prior state; not an operation in the conv/pool/elementwise/activation vocabulary, and there's no depth-buffer-shaped hardware resource on this IP. |
|
||||||
| **Texture sampling** (bilinear/nearest fetch from an image by UV) | **No (high confidence, needs TRM to fully settle)** | No "gather/sample" op appears anywhere in the documented RKNN operator taxonomy for this tier. Convolution can *read* a spatial neighborhood, but that's not the same primitive as an arbitrary-address texture fetch with wrapping/filtering. |
|
| **Texture sampling** (bilinear/nearest fetch from an image by UV) | **No (high confidence, needs TRM to fully settle)** | No "gather/sample" op appears anywhere in the documented RKNN operator taxonomy for this tier. Convolution can *read* a spatial neighborhood, but that's not the same primitive as an arbitrary-address texture fetch with wrapping/filtering. |
|
||||||
| **Per-pixel shading** (arbitrary per-fragment program) | **No** | The NPU executes one fixed, precompiled graph over a whole tensor — it cannot run per-pixel conditional/arbitrary code. You could contrive a *specific* visual effect that literally is a small CNN (see §3), but that's not "shading" in the pipeline sense — it's a different, narrower thing wearing the name. |
|
| **Per-pixel shading** (arbitrary per-fragment program) | **No** | The NPU executes one fixed, precompiled graph over a whole tensor; it cannot run per-pixel conditional/arbitrary code. You could contrive a *specific* visual effect that literally is a small CNN (see section 3), but that's not "shading" in the pipeline sense; it's a different, narrower thing wearing the name. |
|
||||||
| **Framebuffer write** (write final pixels to the display's scanout buffer) | **No** | The NPU has no display/scanout connection at all — no DRM plane, no VOP link. Its only output path is writing tensor data to a DDR buffer, which is not a display pixel format. Something else (CPU or RGA) has to dequantize (`int8 → float → pixel`) and repack it into an actual framebuffer format before it's visible — and even RGA doesn't consume NPU tensor layouts directly (see §4). |
|
| **Framebuffer write** (write final pixels to the display's scanout buffer) | **No** | The NPU has no display/scanout connection at all: no DRM plane, no VOP link. Its only output path is writing tensor data to a DDR buffer, which is not a display pixel format. Something else (CPU or RGA) has to dequantize (`int8 -> float -> pixel`) and repack it into an actual framebuffer format before it's visible, and even RGA doesn't consume NPU tensor layouts directly (see section 4). |
|
||||||
|
|
||||||
### Verdict
|
### Verdict
|
||||||
|
|
||||||
**No full pipeline is possible on this hardware — five of six stages have no
|
**No full pipeline is possible on this hardware: five of six stages have no
|
||||||
mapping at all, not a slow one.** A "hybrid" design where only vertex transform
|
mapping at all, not a slow one.** A "hybrid" design where only vertex transform
|
||||||
runs on the NPU and everything else (rasterize, depth-test, texture, shade,
|
runs on the NPU and everything else (rasterize, depth-test, texture, shade,
|
||||||
write) runs on the Cortex-A7 is the only thing even worth evaluating, and it
|
write) runs on the Cortex-A7 is the only thing even worth evaluating, and it
|
||||||
@@ -189,60 +189,60 @@ doesn't clear the bar either:
|
|||||||
|
|
||||||
- **Dispatch cost is real and not free.** Submission goes through an ioctl,
|
- **Dispatch cost is real and not free.** Submission goes through an ioctl,
|
||||||
a DMA of the command/data buffers, and a **blocking wait on a hardware
|
a DMA of the command/data buffers, and a **blocking wait on a hardware
|
||||||
interrupt** (`wait_event_timeout` in `rknpu_job.c`) — this is a job-queue
|
interrupt** (`wait_event_timeout` in `rknpu_job.c`); this is a job-queue
|
||||||
round trip through the kernel, not a same-cycle instruction. No on-hardware
|
round trip through the kernel, not a same-cycle instruction. No on-hardware
|
||||||
latency number exists in the wiki or SDK for this board (flagged as needing
|
latency number exists in the wiki or SDK for this board (flagged as needing
|
||||||
measurement, not asserted here), but the *shape* of the interface — ioctl +
|
measurement, not asserted here), but the *shape* of the interface (ioctl +
|
||||||
DMA + IRQ wait — is categorically heavier than a same-thread function call.
|
DMA + IRQ wait) is categorically heavier than a same-thread function call.
|
||||||
- **The scale doesn't justify it.** This is a 720×720 wall-panel UI rendering
|
- **The scale doesn't justify it.** This is a 720x720 wall-panel UI rendering
|
||||||
simple chrome, not a game engine — any "3D" element (an isometric icon, a
|
simple chrome, not a game engine; any "3D" element (an isometric icon, a
|
||||||
rotating gauge) involves a handful to a few hundred vertices per frame. A
|
rotating gauge) involves a handful to a few hundred vertices per frame. A
|
||||||
Cortex-A7 with NEON does a few-hundred-vertex 4×4 matrix transform in low
|
Cortex-A7 with NEON does a few-hundred-vertex 4x4 matrix transform in low
|
||||||
single-digit microseconds; there is no plausible world where paying an
|
single-digit microseconds; there is no plausible world where paying an
|
||||||
ioctl/DMA/IRQ round trip to a shared-DDR accelerator beats that, even before
|
ioctl/DMA/IRQ round trip to a shared-DDR accelerator beats that, even before
|
||||||
accounting for the INT8 quantization step (packing/unpacking float vertex and
|
accounting for the INT8 quantization step (packing/unpacking float vertex and
|
||||||
matrix data to/from int8 with scale/zero-point, and the *dynamic range*
|
matrix data to/from int8 with scale/zero-point, and the *dynamic range*
|
||||||
problem — an MVP matrix spans near-to-far-plane depth ranges that quantize
|
problem: an MVP matrix spans near-to-far-plane depth ranges that quantize
|
||||||
very poorly to INT8 without per-frame requantization).
|
very poorly to INT8 without per-frame requantization).
|
||||||
- **The offline-compile step doesn't fit a live camera-driven UI anyway.** RKNN
|
- **The offline-compile step doesn't fit a live camera-driven UI anyway.** RKNN
|
||||||
models are compiled ahead-of-time by RKNN-Toolkit2 on a PC; while the runtime
|
models are compiled ahead-of-time by RKNN-Toolkit2 on a PC; while the runtime
|
||||||
*can* accept different input tensor values per `rknn_run()` call (so a
|
*can* accept different input tensor values per `rknn_run()` call (so a
|
||||||
"run this fixed matmul graph on today's MVP matrix" model is technically
|
"run this fixed matmul graph on today's MVP matrix" model is technically
|
||||||
legitimate), any change to the graph shape itself is a PC-side recompile, not
|
legitimate), any change to the graph shape itself is a PC-side recompile, not
|
||||||
a runtime option — a real constraint for anything beyond the most rigid,
|
a runtime option, a real constraint for anything beyond the most rigid,
|
||||||
pre-planned use of the matmul stage.
|
pre-planned use of the matmul stage.
|
||||||
|
|
||||||
**Net: don't chase this.** Even the one stage that's shape-compatible isn't a
|
**Net: don't chase this.** Even the one stage that's shape-compatible isn't a
|
||||||
net win at this UI's scale, and the other five stages are not partial-credit —
|
net win at this UI's scale, and the other five stages are not partial-credit;
|
||||||
they are the wrong tool, full stop.
|
they are the wrong tool, full stop.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. CNN-Shaped Image Tasks
|
## 3. CNN-Shaped Image Tasks
|
||||||
|
|
||||||
Setting 3D aside — a CNN accelerator's real strength is convolution, which
|
Setting 3D aside: a CNN accelerator's real strength is convolution, which
|
||||||
*does* map to some classic image-processing tasks. Evaluated against this
|
*does* map to some classic image-processing tasks. Evaluated against this
|
||||||
specific 0.5–1 TOPS-class, 128–256MB-shared-DDR, no-camera product:
|
specific 0.5-1 TOPS-class, 128-256MB-shared-DDR, no-camera product:
|
||||||
|
|
||||||
| Task | Technically fits an NPU? | Realistic on this product? |
|
| Task | Technically fits an NPU? | Realistic on this product? |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **Blur / sharpen / edge detection** (convolution kernels) | Yes — this is literally what conv2d does | **No — RKIVE already exists for this, and is a better fit.** RKIVE (Rockchip Intelligent Video Engine) is a *separate*, fixed-function classic-CV block at `0xFFAD0000` — Canny edge, Sobel, morphology (erode/dilate), histogram, connected-components, optical flow, block matching — that sits **completely idle** on this board today, needs no model-compile step, and is architecturally the intended hardware for exactly this class of filter. (`soc-rv1106.md:108`, `raw/web-camera-isp-npu.md:13`) Reaching for the NPU (compile a model, quantize INT8, pay job-submit overhead) to do a blur that RKIVE or even plain CPU already does more directly is solving an already-solved problem the hard way. This product also has direct, recorded evidence that *any* blur is expensive without a GPU: a 42px soft "flare" shadow effect measurably tanked LVGL's DRM-backend performance and was replaced with a cheap 2px border (`flare-edge-construction/design-system.md:22`) — the fix that shipped was "don't blur," not "blur on a different accelerator." |
|
| **Blur / sharpen / edge detection** (convolution kernels) | Yes; this is literally what conv2d does | **No; RKIVE already exists for this, and is a better fit.** RKIVE (Rockchip Intelligent Video Engine) is a *separate*, fixed-function classic-CV block at `0xFFAD0000` (Canny edge, Sobel, morphology (erode/dilate), histogram, connected-components, optical flow, block matching) that sits **completely idle** on this board today, needs no model-compile step, and is architecturally the intended hardware for exactly this class of filter. (`soc-rv1106.md:108`, `raw/web-camera-isp-npu.md:13`) Reaching for the NPU (compile a model, quantize INT8, pay job-submit overhead) to do a blur that RKIVE or even plain CPU already does more directly is solving an already-solved problem the hard way. This product also has direct, recorded evidence that *any* blur is expensive without a GPU: a 42px soft "flare" shadow effect measurably tanked LVGL's DRM-backend performance and was replaced with a cheap 2px border (`flare-edge-construction/design-system.md:22`); the fix that shipped was "don't blur," not "blur on a different accelerator." |
|
||||||
| **Super-resolution / upscaling** | Yes — small SR CNNs (ESPCN-class) exist and run on comparable RKNPU2-family chips | **No use case.** The panel renders its own UI natively at its native 720×720 resolution — there is no lower-resolution source content needing upscaling, and no camera feed to upscale (the 86-Panel has zero camera hardware — confirmed by schematic inspection, `luckfox-pico-86-panel/modernization-roadmap.md:76`). Dead on arrival for lack of an input, not for lack of hardware capability. |
|
| **Super-resolution / upscaling** | Yes; small SR CNNs (ESPCN-class) exist and run on comparable RKNPU2-family chips | **No use case.** The panel renders its own UI natively at its native 720x720 resolution; there is no lower-resolution source content needing upscaling, and no camera feed to upscale (the 86-Panel has zero camera hardware, confirmed by schematic inspection, `luckfox-pico-86-panel/modernization-roadmap.md:76`). Dead on arrival for lack of an input, not for lack of hardware capability. |
|
||||||
| **Style transfer** | Yes — it literally is a CNN | **No — oversized and pointless.** Fast-neural-style-class networks are typically larger than YOLOv5s, which is already near this board's practical RAM ceiling (YOLOv8/v10 already error out on RV1106's shared DDR — `npu.md:38`). There's also no product need for a stylized-UI-render feature. |
|
| **Style transfer** | Yes; it literally is a CNN | **No, oversized and pointless.** Fast-neural-style-class networks are typically larger than YOLOv5s, which is already near this board's practical RAM ceiling (YOLOv8/v10 already error out on RV1106's shared DDR, `npu.md:38`). There's also no product need for a stylized-UI-render feature. |
|
||||||
| **Segmentation-driven UI effects** | Yes, in principle | **Moot — no camera, no visual input of any kind to segment.** |
|
| **Segmentation-driven UI effects** | Yes, in principle | **Moot: no camera, no visual input of any kind to segment.** |
|
||||||
| **2D affine transforms** (rotate/scale/skew as matrix math) | Yes, technically a small matmul | **No — RGA already does this natively, in fixed-function hardware, cheaper.** RGA2-Enhance on this board already does scale (bicubic up / averaging down, to 16× either direction), rotate (90/180/270° on input windows), crop, and color/format conversion as dedicated blit-engine operations — no model compile, no INT8 quantization, no job-submit-and-IRQ-wait round trip, just a register-programmed blit. It is already wired into LVGL (the Monitor-page double-buffer-sync offload, verified 20%→8% CPU on real hardware) and proven in production. (`luckfox-pico-86-panel/rga.md`) |
|
| **2D affine transforms** (rotate/scale/skew as matrix math) | Yes, technically a small matmul | **No; RGA already does this natively, in fixed-function hardware, cheaper.** RGA2-Enhance on this board already does scale (bicubic up / averaging down, to 16x either direction), rotate (90/180/270° on input windows), crop, and color/format conversion as dedicated blit-engine operations: no model compile, no INT8 quantization, no job-submit-and-IRQ-wait round trip, just a register-programmed blit. It is already wired into LVGL (the Monitor-page double-buffer-sync offload, verified 20%->8% CPU on real hardware) and proven in production. (`luckfox-pico-86-panel/rga.md`) |
|
||||||
|
|
||||||
### Prior Rejection
|
### Prior Rejection
|
||||||
|
|
||||||
The product wiki records that the keyboard's touch-bias correction (snapping an
|
The product wiki records that the keyboard's touch-bias correction (snapping an
|
||||||
ambiguous tap to the nearest key) was **explicitly evaluated for NPU
|
ambiguous tap to the nearest key) was **explicitly evaluated for NPU
|
||||||
acceleration and not taken** — the team asked "Is the key bias implementation
|
acceleration and not taken**: the team asked "Is the key bias implementation
|
||||||
feasible using the NPU on this device?" and shipped plain nearest-key-rectangle
|
feasible using the NPU on this device?" and shipped plain nearest-key-rectangle
|
||||||
geometry instead, using LVGL's own buttonmatrix internals.
|
geometry instead, using LVGL's own buttonmatrix internals.
|
||||||
(`flare-edge-construction/design-system.md:73`) That is exactly the right call
|
(`flare-edge-construction/design-system.md:73`) That is exactly the right call
|
||||||
for the reasons in this document: a tiny, cheap, well-defined 2D geometric
|
for the reasons in this document: a tiny, cheap, well-defined 2D geometric
|
||||||
problem has no business going through a tensor accelerator's compile-and-submit
|
problem has no business going through a tensor accelerator's compile-and-submit
|
||||||
pipeline. Nothing in this research changes that conclusion — if anything it
|
pipeline. Nothing in this research changes that conclusion; if anything it
|
||||||
generalizes it.
|
generalizes it.
|
||||||
|
|
||||||
### Realistic Verdict
|
### Realistic Verdict
|
||||||
@@ -250,11 +250,11 @@ generalizes it.
|
|||||||
None of the CNN-shaped graphical tasks clear the bar for this specific product.
|
None of the CNN-shaped graphical tasks clear the bar for this specific product.
|
||||||
Where a hardware assist genuinely helps (2D blit/scale/rotate/blend, classic
|
Where a hardware assist genuinely helps (2D blit/scale/rotate/blend, classic
|
||||||
CV filters), this SoC already has two purpose-built, cheaper, proven-or-idle
|
CV filters), this SoC already has two purpose-built, cheaper, proven-or-idle
|
||||||
accelerators (RGA, RKIVE) that are the architecturally correct answer — not the
|
accelerators (RGA, RKIVE) that are the architecturally correct answer, not the
|
||||||
NPU. The NPU's actual realistic value on this product remains what the platform
|
NPU. The NPU's actual realistic value on this product remains what the platform
|
||||||
wiki already concluded independent of this research: **small, non-visual
|
wiki already concluded independent of this research: **small, non-visual
|
||||||
inference** (audio classification off the on-die codec, RS-485/sensor anomaly
|
inference** (audio classification off the on-die codec, RS-485/sensor anomaly
|
||||||
detection, touch-gesture-pattern classification) — not graphics of any kind.
|
detection, touch-gesture-pattern classification), not graphics of any kind.
|
||||||
(`luckfox-pico-86-panel/npu.md:46-50`)
|
(`luckfox-pico-86-panel/npu.md:46-50`)
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -265,54 +265,54 @@ detection, touch-gesture-pattern classification) — not graphics of any kind.
|
|||||||
|
|
||||||
- **This is a forward-port of Rockchip's out-of-tree vendor driver, not a
|
- **This is a forward-port of Rockchip's out-of-tree vendor driver, not a
|
||||||
from-scratch write.** The driver already carries version-gated compatibility
|
from-scratch write.** The driver already carries version-gated compatibility
|
||||||
shims for kernel APIs that changed across versions — e.g. `rknpu_iommu.c` has
|
shims for kernel APIs that changed across versions, e.g. `rknpu_iommu.c` has
|
||||||
`#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE` / `#if KERNEL_VERSION(5, 10, 0)
|
`#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE` / `#if KERNEL_VERSION(5, 10, 0)
|
||||||
<= LINUX_VERSION_CODE` branches for IOVA/dma_limit API differences — showing
|
<= LINUX_VERSION_CODE` branches for IOVA/dma_limit API differences, showing
|
||||||
Rockchip's own driver source is written to track multiple kernel versions,
|
Rockchip's own driver source is written to track multiple kernel versions,
|
||||||
which is a good sign for portability in principle but confirms real API-level
|
which is a good sign for portability in principle but confirms real API-level
|
||||||
work is needed, not a recompile.
|
work is needed, not a recompile.
|
||||||
- **No mainline path exists to lean on.** The open-source "Rocket" NPU driver
|
- **No mainline path exists to lean on.** The open-source "Rocket" NPU driver
|
||||||
(`accel/rocket`) covers RK3588 and (as of a 2026-07-15 reverse-engineering
|
(`accel/rocket`) covers RK3588 and (as of a 2026-07-15 reverse-engineering
|
||||||
effort) RK3576 — but RV1106's 4th-generation NPU IP is a **different
|
effort) RK3576, but RV1106's 4th-generation NPU IP is a **different
|
||||||
generation** and is not covered by Rocket, and neither active mainline RV1106
|
generation** and is not covered by Rocket, and neither active mainline RV1106
|
||||||
patch series (Simon Glass's SoC/clk/pinctrl series, Vladislav Leonov's
|
patch series (Simon Glass's SoC/clk/pinctrl series, Vladislav Leonov's
|
||||||
peripheral series) touches NPU, RGA, ISP, VENC, or display at all.
|
peripheral series) touches NPU, RGA, ISP, VENC, or display at all.
|
||||||
(`luckfox-pico-86-panel/mainline-kernel.md:38`, `modernization-roadmap.md:48,96`)
|
(`luckfox-pico-86-panel/mainline-kernel.md:38`, `modernization-roadmap.md:48,96`)
|
||||||
**NPU use on this chip requires the proprietary RKNPU2 vendor runtime
|
**NPU use on this chip requires the proprietary RKNPU2 vendor runtime
|
||||||
indefinitely** — there is no future where an open driver + open compiler
|
indefinitely**: there is no future where an open driver + open compiler
|
||||||
replaces it.
|
replaces it.
|
||||||
- **Memory manager choice matters for the port.** The driver's Kconfig offers
|
- **Memory manager choice matters for the port.** The driver's Kconfig offers
|
||||||
two mutually exclusive memory managers: `ROCKCHIP_RKNPU_DRM_GEM` (needs the
|
two mutually exclusive memory managers: `ROCKCHIP_RKNPU_DRM_GEM` (needs the
|
||||||
DRM subsystem — DRM GEM/fence APIs have moved substantially between 5.10 and
|
DRM subsystem; DRM GEM/fence APIs have moved substantially between 5.10 and
|
||||||
6.18) or `ROCKCHIP_RKNPU_DMA_HEAP` (needs `DMABUF_HEAPS_ROCKCHIP_CMA_HEAP`).
|
6.18) or `ROCKCHIP_RKNPU_DMA_HEAP` (needs `DMABUF_HEAPS_ROCKCHIP_CMA_HEAP`).
|
||||||
Whichever is chosen inherits whatever DRM/dma-buf/dma-fence API churn exists
|
Whichever is chosen inherits whatever DRM/dma-buf/dma-fence API churn exists
|
||||||
across that kernel gap — the same class of surface RGA's port would also have
|
across that kernel gap, the same class of surface RGA's port would also have
|
||||||
to cross.
|
to cross.
|
||||||
- **IOMMU status is a live design choice, not a given.** Today this board runs
|
- **IOMMU status is a live design choice, not a given.** Today this board runs
|
||||||
the NPU in **non-IOMMU mode** (no DT entry) — same physically-contiguous-only
|
the NPU in **non-IOMMU mode** (no DT entry), same physically-contiguous-only
|
||||||
memory constraint RGA has on this chip. The kernel driver does support an
|
memory constraint RGA has on this chip. The kernel driver does support an
|
||||||
IOMMU path (`rknpu_iommu.c`), so wiring it up is possible but is new scope,
|
IOMMU path (`rknpu_iommu.c`), so wiring it up is possible but is new scope,
|
||||||
not something the port inherits for free.
|
not something the port inherits for free.
|
||||||
- **Reset/clock plumbing**: `SRST_A_RKNN`/`SRST_H_RKNN` resets and
|
- **Reset/clock plumbing**: `SRST_A_RKNN`/`SRST_H_RKNN` resets and
|
||||||
`ACLK_RKNN`/`HCLK_RKNN` clocks off the shared CRU "matrix" clock ladder, node
|
`ACLK_RKNN`/`HCLK_RKNN` clocks off the shared CRU "matrix" clock ladder, node
|
||||||
`npu@ff660000` in `rv1106.dtsi` (`status = "disabled"` at the base dtsi level;
|
`npu@ff660000` in `rv1106.dtsi` (`status = "disabled"` at the base dtsi level;
|
||||||
our board enables it downstream) — unremarkable, same pattern as every other
|
our board enables it downstream); unremarkable, same pattern as every other
|
||||||
RV1106 peripheral node.
|
RV1106 peripheral node.
|
||||||
- **Bottom line on effort class**: this is the same class of work already
|
- **Bottom line on effort class**: this is the same class of work already
|
||||||
scoped for RGA in the modernization roadmap — "carrying Rockchip's out-of-tree
|
scoped for RGA in the modernization roadmap ("carrying Rockchip's out-of-tree
|
||||||
driver forward against a newer kernel ABI" — bounded, evidence-backed, but
|
driver forward against a newer kernel ABI"): bounded, evidence-backed, but
|
||||||
real engineering, not a version-string bump. (`luckfox-pico-86-panel/rga.md:86`)
|
real engineering, not a version-string bump. (`luckfox-pico-86-panel/rga.md:86`)
|
||||||
|
|
||||||
### Direct Submit Path
|
### Direct Submit Path
|
||||||
|
|
||||||
**It needs the full toolchain.** As established in §1, the raw
|
**It needs the full toolchain.** As established in section 1, the raw
|
||||||
`DRM_IOCTL_RKNPU_SUBMIT` path exists at the kernel-ioctl level, but the
|
`DRM_IOCTL_RKNPU_SUBMIT` path exists at the kernel-ioctl level, but the
|
||||||
command-buffer format it consumes is generated exclusively by the closed
|
command-buffer format it consumes is generated exclusively by the closed
|
||||||
RKNN-Toolkit2 compiler and is not publicly documented at the register level.
|
RKNN-Toolkit2 compiler and is not publicly documented at the register level.
|
||||||
There is no supported "hand-roll a compute kernel" door here — any real
|
There is no supported "hand-roll a compute kernel" door here; any real
|
||||||
workload (an inference model, or a hypothetical matmul-as-graphics use) has to
|
workload (an inference model, or a hypothetical matmul-as-graphics use) has to
|
||||||
go: train/define → ONNX → RKNN-Toolkit2 compile (PC, offline) → ship the
|
go: train/define -> ONNX -> RKNN-Toolkit2 compile (PC, offline) -> ship the
|
||||||
`.rknn` blob → RKNN C API (`rknn_init`/`rknn_run`) on-device. This is a heavier,
|
`.rknn` blob -> RKNN C API (`rknn_init`/`rknn_run`) on-device. This is a heavier,
|
||||||
slower-to-iterate loop than driving RGA (which is a direct, synchronous
|
slower-to-iterate loop than driving RGA (which is a direct, synchronous
|
||||||
`im2d`-style C API call with no offline compile step at all) or writing plain
|
`im2d`-style C API call with no offline compile step at all) or writing plain
|
||||||
CPU code.
|
CPU code.
|
||||||
@@ -321,18 +321,18 @@ CPU code.
|
|||||||
|
|
||||||
Worth restating plainly since it's the thing the NPU would be compared against:
|
Worth restating plainly since it's the thing the NPU would be compared against:
|
||||||
**RGA2-Enhance already does everything this panel's UI plausibly needs from 2D
|
**RGA2-Enhance already does everything this panel's UI plausibly needs from 2D
|
||||||
hardware acceleration** — blit/copy, scale (bicubic, up to 16×), rotate
|
hardware acceleration**: blit/copy, scale (bicubic, up to 16x), rotate
|
||||||
(90/180/270° on input), full CSC (BT601/BT709), blend (Porter-Duff), colorkey,
|
(90/180/270° on input), full CSC (BT601/BT709), blend (Porter-Duff), colorkey,
|
||||||
ROP, fill, dither, mosaic, and a purpose-built OSD compositing path — and it's
|
ROP, fill, dither, mosaic, and a purpose-built OSD compositing path, and it's
|
||||||
already integrated into LVGL with a measured, shipped production win (Monitor
|
already integrated into LVGL with a measured, shipped production win (Monitor
|
||||||
page 20%→8% CPU). (`luckfox-pico-86-panel/rga.md`) There is no 2D graphics gap
|
page 20%->8% CPU). (`luckfox-pico-86-panel/rga.md`) There is no 2D graphics gap
|
||||||
on this product that would motivate reaching for the NPU instead. One small,
|
on this product that would motivate reaching for the NPU instead. One small,
|
||||||
suggestive detail: RGA's feature bitmask includes `RGA_NN_QUANTIZE` — a hint
|
suggestive detail: RGA's feature bitmask includes `RGA_NN_QUANTIZE`, a hint
|
||||||
that RGA's real intended role in Rockchip's own IPC/camera reference designs is
|
that RGA's real intended role in Rockchip's own IPC/camera reference designs is
|
||||||
*feeding* the NPU (resize/convert/quantize a frame before inference), not the
|
*feeding* the NPU (resize/convert/quantize a frame before inference), not the
|
||||||
NPU feeding graphics back to RGA. That's the direction Rockchip designed this
|
NPU feeding graphics back to RGA. That's the direction Rockchip designed this
|
||||||
silicon to work in, and it lines up with everything else in this document: RGA
|
silicon to work in, and it lines up with everything else in this document: RGA
|
||||||
→ pixels, NPU → tensors, and the two don't trade places.
|
-> pixels, NPU -> tensors, and the two don't trade places.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -341,11 +341,11 @@ silicon to work in, and it lines up with everything else in this document: RGA
|
|||||||
| | Worth prototyping? | Why |
|
| | Worth prototyping? | Why |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **NPU for any part of 3D rendering** | **No** | Not "how the hardware works" past vertex-transform, and even that stage isn't a net win at this UI's vertex counts once job-submit overhead and INT8 quantization of matrices/vertices are accounted for. |
|
| **NPU for any part of 3D rendering** | **No** | Not "how the hardware works" past vertex-transform, and even that stage isn't a net win at this UI's vertex counts once job-submit overhead and INT8 quantization of matrices/vertices are accounted for. |
|
||||||
| **NPU for 2D image filters (blur/sharpen/edge)** | **No** | RKIVE is idle, purpose-built, and needs no model-compile step — strictly better fit if this is ever wanted at all. |
|
| **NPU for 2D image filters (blur/sharpen/edge)** | **No** | RKIVE is idle, purpose-built, and needs no model-compile step; strictly better fit if this is ever wanted at all. |
|
||||||
| **NPU for super-resolution / style transfer / segmentation effects** | **No** | No input source (no camera) or no product need; also RAM-marginal on this SKU regardless. |
|
| **NPU for super-resolution / style transfer / segmentation effects** | **No** | No input source (no camera) or no product need; also RAM-marginal on this SKU regardless. |
|
||||||
| **NPU for 2D affine transforms (rotate/scale) as "graphics"** | **No** | RGA already does this in fixed-function hardware, cheaper, already proven in production. |
|
| **NPU for 2D affine transforms (rotate/scale) as "graphics"** | **No** | RGA already does this in fixed-function hardware, cheaper, already proven in production. |
|
||||||
| **Porting `rknpu.ko` to 6.18** | **Conditionally yes — but scope it for inference, not graphics** | Same bounded, evidence-backed effort class as the RGA port; keeps the door open for the platform wiki's actual identified NPU opportunity (a small non-visual classifier: audio, RS-485/sensor anomaly detection, touch-gesture patterns). Do not justify or scope the port around a graphics capability — it doesn't unlock one. |
|
| **Porting `rknpu.ko` to 6.18** | **Conditionally yes, but scope it for inference, not graphics** | Same bounded, evidence-backed effort class as the RGA port; keeps the door open for the platform wiki's actual identified NPU opportunity (a small non-visual classifier: audio, RS-485/sensor anomaly detection, touch-gesture patterns). Do not justify or scope the port around a graphics capability; it doesn't unlock one. |
|
||||||
| **A first NPU spike, if one is wanted for team familiarity** | **Only the already-identified real use case** | A tiny non-visual model (e.g. an RS-485 anomaly classifier) — not a graphics stunt. This is the same conclusion the platform wiki already reached independent of this research. |
|
| **A first NPU spike, if one is wanted for team familiarity** | **Only the already-identified real use case** | A tiny non-visual model (e.g. an RS-485 anomaly classifier), not a graphics stunt. This is the same conclusion the platform wiki already reached independent of this research. |
|
||||||
|
|
||||||
### Falsifiers
|
### Falsifiers
|
||||||
|
|
||||||
@@ -353,20 +353,20 @@ Flagging explicitly, per the instruction to distinguish settled facts from
|
|||||||
things needing verification:
|
things needing verification:
|
||||||
|
|
||||||
- **TRM Part 2 (register-level RKNPU documentation) does not exist publicly.**
|
- **TRM Part 2 (register-level RKNPU documentation) does not exist publicly.**
|
||||||
Everything in §2's rasterization/texture/per-pixel-shading "no" verdicts is
|
Everything in section 2's rasterization/texture/per-pixel-shading "no" verdicts is
|
||||||
reasoned from the RKNN operator taxonomy and general 4th-generation RKNPU
|
reasoned from the RKNN operator taxonomy and general 4th-generation RKNPU
|
||||||
architecture knowledge, not from a register-level ground truth — because that
|
architecture knowledge, not from a register-level ground truth, because that
|
||||||
ground truth isn't published anywhere, including to Luckfox's own engineers
|
ground truth isn't published anywhere, including to Luckfox's own engineers
|
||||||
per a direct forum admission. (`soc-rv1106.md:114`) If Rockchip ever publishes
|
per a direct forum admission. (`soc-rv1106.md:114`) If Rockchip ever publishes
|
||||||
register-level detail, or if a full RKNN supported-operator list surfaces with
|
register-level detail, or if a full RKNN supported-operator list surfaces with
|
||||||
a gather/sample-style op this research didn't find, revisit.
|
a gather/sample-style op this research didn't find, revisit.
|
||||||
- **No on-hardware NPU job-submission latency number exists anywhere in the
|
- **No on-hardware NPU job-submission latency number exists anywhere in the
|
||||||
wiki or SDK for this board.** The "dispatch overhead beats any small win"
|
wiki or SDK for this board.** The "dispatch overhead beats any small win"
|
||||||
argument in §2 is architectural (ioctl + DMA + blocking IRQ wait, vs. a
|
argument in section 2 is architectural (ioctl + DMA + blocking IRQ wait, vs. a
|
||||||
same-thread NEON call) and is high-confidence, but a real measured number
|
same-thread NEON call) and is high-confidence, but a real measured number
|
||||||
would strengthen or could in principle narrow it. Not asserted as measured
|
would strengthen or could in principle narrow it. Not asserted as measured
|
||||||
here.
|
here.
|
||||||
- **Which SKU (G2 vs G3) our boards actually carry is unconfirmed**, which
|
- **Which SKU (G2 vs G3) our boards actually carry is unconfirmed**, which
|
||||||
leaves the exact TOPS ceiling and RAM budget open — doesn't change any verdict
|
leaves the exact TOPS ceiling and RAM budget open; doesn't change any verdict
|
||||||
in this document (nothing here turns on the TOPS number), but is worth closing
|
in this document (nothing here turns on the TOPS number), but is worth closing
|
||||||
out anyway during the driver port.
|
out anyway during the driver port.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> **Outcome tested:** The UI never shows a stale number: it holds briefly, then marks unknown.
|
> **Outcome tested:** The UI never shows a stale number: it holds briefly, then marks unknown.
|
||||||
|
|
||||||
**Coverage**: freshness.c — 66/66 conditions, 100% MC/DC (CI-enforced)
|
**Coverage**: freshness.c: 66/66 conditions, 100% MC/DC (CI-enforced)
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> **Outcome tested:** A relay is exported transparently and driven without disturbing a held contact.
|
> **Outcome tested:** A relay is exported transparently and driven without disturbing a held contact.
|
||||||
|
|
||||||
**Coverage**: relays.c — 40/40 conditions, 100% MC/DC (CI-enforced)
|
**Coverage**: relays.c: 40/40 conditions, 100% MC/DC (CI-enforced)
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
|
|||||||
+4
-4
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
Per **ADR-0002** (tiered MC/DC) and **ADR-0005** (source-of-truth), our own
|
Per **ADR-0002** (tiered MC/DC) and **ADR-0005** (source-of-truth), our own
|
||||||
hardware-facing code migrates here behind a HAL seam and is hardened. "100% MC/DC on
|
hardware-facing code migrates here behind a HAL seam and is hardened. "100% MC/DC on
|
||||||
100% of drivers" is infeasible (≈97% of kernel-driver LOC is vendor blobs — AIC8800
|
100% of drivers" is infeasible (~97% of kernel-driver LOC is vendor blobs; AIC8800
|
||||||
alone is 88.5K lines); the realistic, honest target is tiered.
|
alone is 88.5K lines); the realistic, honest target is tiered.
|
||||||
|
|
||||||
## Tier 1 — 100% MC/DC
|
## Tier 1: 100% MC/DC
|
||||||
|
|
||||||
Self-contained logic with a clean seam, measured to **100% MC/DC** (gcc-14
|
Self-contained logic with a clean seam, measured to **100% MC/DC** (gcc-14
|
||||||
`-fcondition-coverage`) by the CI `mcdc` job (`make -C drivers/*/test check`):
|
`-fcondition-coverage`) by the CI `mcdc` job (`make -C drivers/*/test check`):
|
||||||
@@ -18,12 +18,12 @@ Self-contained logic with a clean seam, measured to **100% MC/DC** (gcc-14
|
|||||||
**Adding a Tier-1 driver:** copy `<name>.{c,h}` here, put the hardware/OS calls behind
|
**Adding a Tier-1 driver:** copy `<name>.{c,h}` here, put the hardware/OS calls behind
|
||||||
a small injectable seam, then mirror `relays/test/` (a fake backend for the logic
|
a small injectable seam, then mirror `relays/test/` (a fake backend for the logic
|
||||||
branches + a real backend over a scratch tree for the plumbing). Reuse the shared
|
branches + a real backend over a scratch tree for the plumbing). Reuse the shared
|
||||||
gate — the Makefile calls `bash ../../enforce-mcdc.sh <gcov.log> build/<name>.c.gcov
|
gate: the Makefile calls `bash ../../enforce-mcdc.sh <gcov.log> build/<name>.c.gcov
|
||||||
build/test.rc` (it derives the driver name from the `.gcov` file, so there is no
|
build/test.rc` (it derives the driver name from the `.gcov` file, so there is no
|
||||||
per-driver copy to keep in sync). The CI `mcdc` job picks up any
|
per-driver copy to keep in sync). The CI `mcdc` job picks up any
|
||||||
`drivers/*/test/Makefile` automatically.
|
`drivers/*/test/Makefile` automatically.
|
||||||
|
|
||||||
## Tier 2 — Fault Injection
|
## Tier 2: Fault Injection
|
||||||
|
|
||||||
Drivers too large or too vendor/UI-coupled for literal MC/DC get fault-injection,
|
Drivers too large or too vendor/UI-coupled for literal MC/DC get fault-injection,
|
||||||
branch coverage, and benchmarks against the simulator instead. Their **hardware side
|
branch coverage, and benchmarks against the simulator instead. Their **hardware side
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* The UI Freshness Contract engine (flare-edge ADR-0004) — core, LVGL-free.
|
* The UI Freshness Contract engine (flare-edge ADR-0004): core, LVGL-free.
|
||||||
* See freshness.h for the contract. LVGL binding lives in freshness_lv.c.
|
* See freshness.h for the contract. LVGL binding lives in freshness_lv.c.
|
||||||
*/
|
*/
|
||||||
#include "freshness.h"
|
#include "freshness.h"
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
/* Max simultaneous live bindings. Bindings belong to visible pages; the whole
|
/* Max simultaneous live bindings. Bindings belong to visible pages; the whole
|
||||||
* navigable set of a screen is small, so this is generous. A full table drops
|
* navigable set of a screen is small, so this is generous. A full table drops
|
||||||
* the binding (returns NULL) rather than silently overflowing — the LVGL layer
|
* the binding (returns NULL) rather than silently overflowing: the LVGL layer
|
||||||
* turns that into a visible fault, never a stale value. */
|
* turns that into a visible fault, never a stale value. */
|
||||||
#ifndef FRESH_MAX
|
#ifndef FRESH_MAX
|
||||||
#define FRESH_MAX 96
|
#define FRESH_MAX 96
|
||||||
@@ -40,7 +40,7 @@ struct warden_fresh {
|
|||||||
/* A fixed table scanned in full: bindings are torn down all at once by
|
/* A fixed table scanned in full: bindings are torn down all at once by
|
||||||
* warden_fresh_reset (like the screen timers), never individually, so a running
|
* warden_fresh_reset (like the screen timers), never individually, so a running
|
||||||
* high-water bound would only hide the free-slot arms from tests without saving
|
* high-water bound would only hide the free-slot arms from tests without saving
|
||||||
* real work — the visible set per screen is a handful. */
|
* real work: the visible set per screen is a handful. */
|
||||||
static struct warden_fresh s_vals[FRESH_MAX];
|
static struct warden_fresh s_vals[FRESH_MAX];
|
||||||
|
|
||||||
warden_fresh_render_t warden_fresh_decide(warden_fresh_result_t produced,
|
warden_fresh_render_t warden_fresh_decide(warden_fresh_result_t produced,
|
||||||
@@ -122,7 +122,7 @@ warden_fresh_t *warden_fresh_bind(void *page, warden_fresh_produce_cb produce,
|
|||||||
v->used = true;
|
v->used = true;
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
return NULL; /* table full — caller surfaces a fault, never a stale value */
|
return NULL; /* table full: caller surfaces a fault, never a stale value */
|
||||||
}
|
}
|
||||||
|
|
||||||
void warden_fresh_set_visible(void *page, bool visible)
|
void warden_fresh_set_visible(void *page, bool visible)
|
||||||
@@ -175,7 +175,7 @@ uint32_t warden_fresh_count(void)
|
|||||||
|
|
||||||
uint32_t warden_fresh_min_budget_ms(void)
|
uint32_t warden_fresh_min_budget_ms(void)
|
||||||
{
|
{
|
||||||
/* `seen` — not `best == 0` — marks "nothing scanned yet", so a legitimate
|
/* `seen` (not `best == 0`) marks "nothing scanned yet", so a legitimate
|
||||||
* zero-tolerance binding (max_stale_ms == 0, "must be fresh every tick") wins
|
* zero-tolerance binding (max_stale_ms == 0, "must be fresh every tick") wins
|
||||||
* the minimum instead of being mistaken for the empty sentinel and widened. */
|
* the minimum instead of being mistaken for the empty sentinel and widened. */
|
||||||
uint32_t best = 0;
|
uint32_t best = 0;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* The UI Freshness Contract engine (flare-edge ADR-0004) — core, LVGL-free.
|
* The UI Freshness Contract engine (flare-edge ADR-0004): core, LVGL-free.
|
||||||
*
|
*
|
||||||
* The panel is read on-site to judge whether hardware is healthy, so a silently
|
* The panel is read on-site to judge whether hardware is healthy, so a silently
|
||||||
* *stale* number is worse than a missing one: a stale IP or hashrate reads as
|
* *stale* number is worse than a missing one: a stale IP or hashrate reads as
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
* sanctioned way to show a live value. It guarantees a bound value is refreshed
|
* sanctioned way to show a live value. It guarantees a bound value is refreshed
|
||||||
* (a) the instant its page becomes visible, (b) periodically while visible
|
* (a) the instant its page becomes visible, (b) periodically while visible
|
||||||
* within a declared max-staleness, and (c) promptly when a declared source
|
* within a declared max-staleness, and (c) promptly when a declared source
|
||||||
* changes — and it renders a value whose source cannot be evaluated as an
|
* changes, and it renders a value whose source cannot be evaluated as an
|
||||||
* explicit UNKNOWN, never as its confident last-known number.
|
* explicit UNKNOWN, never as its confident last-known number.
|
||||||
*
|
*
|
||||||
* This header is deliberately LVGL-free so the engine and every producer are
|
* This header is deliberately LVGL-free so the engine and every producer are
|
||||||
@@ -21,14 +21,14 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
/* The mark shown when a value's source cannot be evaluated (em dash). The LVGL
|
/* The mark shown when a value's source cannot be evaluated (em dash). The LVGL
|
||||||
* layer additionally dims the widget. Public so the label wrapper and tests can
|
* layer also dims the widget. Public so the label wrapper and tests can
|
||||||
* reference the same literal. */
|
* reference the same literal. */
|
||||||
#define WARDEN_FRESH_UNKNOWN_MARK "\xE2\x80\x94"
|
#define WARDEN_FRESH_UNKNOWN_MARK "\xE2\x80\x94"
|
||||||
|
|
||||||
/* What a producer reports after being asked to produce the current value. */
|
/* What a producer reports after being asked to produce the current value. */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
FRESH_OK = 0, /* wrote the current value into buf */
|
FRESH_OK = 0, /* wrote the current value into buf */
|
||||||
FRESH_UNKNOWN, /* source unavailable — no value can be produced now */
|
FRESH_UNKNOWN, /* source unavailable: no value can be produced now */
|
||||||
FRESH_SAME, /* source read fine; value unchanged (cheap re-render) */
|
FRESH_SAME, /* source read fine; value unchanged (cheap re-render) */
|
||||||
} warden_fresh_result_t;
|
} warden_fresh_result_t;
|
||||||
|
|
||||||
@@ -40,11 +40,11 @@ typedef warden_fresh_result_t (*warden_fresh_produce_cb)(char *buf, size_t n,
|
|||||||
/* What the engine decided the widget should show this cycle. */
|
/* What the engine decided the widget should show this cycle. */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
FRESH_RENDER_VALUE = 0, /* show the produced/last-good value */
|
FRESH_RENDER_VALUE = 0, /* show the produced/last-good value */
|
||||||
FRESH_RENDER_UNKNOWN, /* show the explicit-unknown mark ("—", dimmed) */
|
FRESH_RENDER_UNKNOWN, /* show the explicit-unknown mark (an em dash, dimmed) */
|
||||||
FRESH_RENDER_NOCHANGE, /* leave the widget exactly as it is */
|
FRESH_RENDER_NOCHANGE, /* leave the widget exactly as it is */
|
||||||
} warden_fresh_render_t;
|
} warden_fresh_render_t;
|
||||||
|
|
||||||
/* The one pure decision at the heart of the contract — no state, no I/O, no
|
/* The one pure decision at the heart of the contract: no state, no I/O, no
|
||||||
* LVGL, so every branch is unit-testable. `showing_unknown` is whether the
|
* LVGL, so every branch is unit-testable. `showing_unknown` is whether the
|
||||||
* widget is currently displaying the UNKNOWN mark (so recovery from a stale
|
* widget is currently displaying the UNKNOWN mark (so recovery from a stale
|
||||||
* blip re-renders the value even when the producer reports it unchanged). */
|
* blip re-renders the value even when the producer reports it unchanged). */
|
||||||
@@ -80,13 +80,13 @@ void warden_fresh_tick(uint32_t now_ms);
|
|||||||
/* A producer of change fired: refresh every visible value bound to `source`. */
|
/* A producer of change fired: refresh every visible value bound to `source`. */
|
||||||
void warden_fresh_invalidate(const char *source, uint32_t now_ms);
|
void warden_fresh_invalidate(const char *source, uint32_t now_ms);
|
||||||
|
|
||||||
/* Drop all bindings — called on a theme/screen rebuild, like the screen timers. */
|
/* Drop all bindings: called on a theme/screen rebuild, like the screen timers. */
|
||||||
void warden_fresh_reset(void);
|
void warden_fresh_reset(void);
|
||||||
|
|
||||||
/* Number of live bindings (introspection / tests). */
|
/* Number of live bindings (introspection / tests). */
|
||||||
uint32_t warden_fresh_count(void);
|
uint32_t warden_fresh_count(void);
|
||||||
|
|
||||||
/* Smallest max_stale_ms among visible bindings, or 0 if none — lets the LVGL
|
/* Smallest max_stale_ms among visible bindings, or 0 if none: lets the LVGL
|
||||||
* layer size the shared tick to the tightest budget actually on screen. */
|
* layer size the shared tick to the tightest budget actually on screen. */
|
||||||
uint32_t warden_fresh_min_budget_ms(void);
|
uint32_t warden_fresh_min_budget_ms(void);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# MC/DC unit harness for drivers/freshness/freshness.c (the UI stale-number guard).
|
# MC/DC unit harness for drivers/freshness/freshness.c (the UI stale-number guard).
|
||||||
#
|
#
|
||||||
# make check — build, run, FAIL unless freshness.c hits 100% MC/DC + all checks.
|
# make check : build, run, FAIL unless freshness.c hits 100% MC/DC + all checks.
|
||||||
# make report — per-condition gcov annotation.
|
# make report : per-condition gcov annotation.
|
||||||
# make clean
|
# make clean
|
||||||
#
|
#
|
||||||
# FRESH_MAX is forced to 2 so the "binding table full -> NULL" path is reachable
|
# FRESH_MAX is forced to 2 so the "binding table full -> NULL" path is reachable
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* MC/DC harness for drivers/freshness/freshness.c (built with -DFRESH_MAX=2).
|
/* MC/DC harness for drivers/freshness/freshness.c (built with -DFRESH_MAX=2).
|
||||||
*
|
*
|
||||||
* freshness.c is pure logic with produce/render callbacks — no hardware seam
|
* freshness.c is pure logic with produce/render callbacks: no hardware seam
|
||||||
* needed, the callbacks ARE the seam. We drive the decision function directly and
|
* needed, the callbacks ARE the seam. We drive the decision function directly and
|
||||||
* the bind/tick/invalidate state machine through programmable fakes, covering
|
* the bind/tick/invalidate state machine through programmable fakes, covering
|
||||||
* every decision (incl. the compound `used && visible`, `!produce || !render`,
|
* every decision (incl. the compound `used && visible`, `!produce || !render`,
|
||||||
@@ -45,7 +45,7 @@ static void test_decide(void) {
|
|||||||
EXPECT(warden_fresh_decide(FRESH_UNKNOWN, true, false, 200, 100) == FRESH_RENDER_UNKNOWN);
|
EXPECT(warden_fresh_decide(FRESH_UNKNOWN, true, false, 200, 100) == FRESH_RENDER_UNKNOWN);
|
||||||
EXPECT(warden_fresh_decide(FRESH_UNKNOWN, true, false, 50, 100) == FRESH_RENDER_NOCHANGE);
|
EXPECT(warden_fresh_decide(FRESH_UNKNOWN, true, false, 50, 100) == FRESH_RENDER_NOCHANGE);
|
||||||
/* boundary: age == max_stale is NOT stale (guards a `>`->`>=` regression that
|
/* boundary: age == max_stale is NOT stale (guards a `>`->`>=` regression that
|
||||||
* MC/DC alone would not catch — both outcomes are already covered above). */
|
* MC/DC alone would not catch: both outcomes are already covered above). */
|
||||||
EXPECT(warden_fresh_decide(FRESH_UNKNOWN, true, false, 100, 100) == FRESH_RENDER_NOCHANGE);
|
EXPECT(warden_fresh_decide(FRESH_UNKNOWN, true, false, 100, 100) == FRESH_RENDER_NOCHANGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ bool warden_relay_get(uint32_t idx);
|
|||||||
/** Drive the output. Exports and sets the direction on first use. */
|
/** Drive the output. Exports and sets the direction on first use. */
|
||||||
void warden_relay_set(uint32_t idx, bool on);
|
void warden_relay_set(uint32_t idx, bool on);
|
||||||
|
|
||||||
/** True if the GPIO is exported and usable — the page says so if it is not. */
|
/** True if the GPIO is exported and usable: the page says so if it is not. */
|
||||||
bool warden_relay_available(uint32_t idx);
|
bool warden_relay_available(uint32_t idx);
|
||||||
|
|
||||||
/* --- Hardware-abstraction seam ---------------------------------------------
|
/* --- Hardware-abstraction seam ---------------------------------------------
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# MC/DC unit harness for drivers/relays/relays.c.
|
# MC/DC unit harness for drivers/relays/relays.c.
|
||||||
#
|
#
|
||||||
# make check — build, run, and FAIL unless relays.c hits 100% MC/DC
|
# make check : build, run, and FAIL unless relays.c hits 100% MC/DC
|
||||||
# (condition) coverage and every unit check passes.
|
# (condition) coverage and every unit check passes.
|
||||||
# make report — per-condition gcov annotation (build/relays.c.gcov).
|
# make report : per-condition gcov annotation (build/relays.c.gcov).
|
||||||
# make clean
|
# make clean
|
||||||
#
|
#
|
||||||
# Requires gcc >= 14 (for -fcondition-coverage) and its matching gcov.
|
# Requires gcc >= 14 (for -fcondition-coverage) and its matching gcov.
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
/* MC/DC harness for drivers/relays/relays.c.
|
/* MC/DC harness for drivers/relays/relays.c.
|
||||||
*
|
*
|
||||||
* Two layers, one binary, so the combined run covers every decision in relays.c:
|
* Two layers, one binary, so the combined run covers every decision in relays.c:
|
||||||
* 1. unit tests through a FAKE relay_io — exercise the decision logic, incl.
|
* 1. unit tests through a FAKE relay_io: exercise the decision logic, incl.
|
||||||
* the export->node-appears path a passive tree cannot model.
|
* the export->node-appears path a passive tree cannot model.
|
||||||
* 2. integration tests through the real sysfs backend + $WARDEN_GPIO_ROOT
|
* 2. integration tests through the real sysfs backend + $WARDEN_GPIO_ROOT
|
||||||
* pointed at a scratch tree — exercise the backend's fopen/stat branches.
|
* pointed at a scratch tree: exercise the backend's fopen/stat branches.
|
||||||
*/
|
*/
|
||||||
#include "../relays.h"
|
#include "../relays.h"
|
||||||
|
|
||||||
|
|||||||
+19
-19
@@ -1,4 +1,4 @@
|
|||||||
# RV1106 → Linux 6.18 forward-port (self-built, from vendor 5.10)
|
# RV1106 -> Linux 6.18 forward-port (self-built, from vendor 5.10)
|
||||||
|
|
||||||
**Decision (2026-08-23):** forward-port the RV1106 SoC enablement from the
|
**Decision (2026-08-23):** forward-port the RV1106 SoC enablement from the
|
||||||
Rockchip **vendor 5.10** tree straight to **Linux 6.18 LTS**, ourselves, using **no
|
Rockchip **vendor 5.10** tree straight to **Linux 6.18 LTS**, ourselves, using **no
|
||||||
@@ -6,7 +6,7 @@ plan44 code**, built on **our Buildroot 2025.02 LTS + uClibc**. We own the tree.
|
|||||||
|
|
||||||
## The reality this rests on (verified, not assumed)
|
## The reality this rests on (verified, not assumed)
|
||||||
|
|
||||||
Vanilla mainline (6.6.93 and 6.18) has **zero** RV1106 support — no devicetree, no
|
Vanilla mainline (6.6.93 and 6.18) has **zero** RV1106 support: no devicetree, no
|
||||||
clock driver, no pinctrl data, not one `rv1106` reference in `arch/arm` or
|
clock driver, no pinctrl data, not one `rv1106` reference in `arch/arm` or
|
||||||
`drivers/`. So "the newest kernel" is a generic ARM kernel that cannot boot this
|
`drivers/`. So "the newest kernel" is a generic ARM kernel that cannot boot this
|
||||||
SoC. What we forward-port is the **entire RV1106 BSP**: ~120 files in the vendor
|
SoC. What we forward-port is the **entire RV1106 BSP**: ~120 files in the vendor
|
||||||
@@ -16,48 +16,48 @@ it is delivered incrementally, console-first.
|
|||||||
|
|
||||||
## Method (how each piece is ported)
|
## Method (how each piece is ported)
|
||||||
|
|
||||||
- **Framework drivers → sibling-delta.** clk and pinctrl are driven by the shared
|
- **Framework drivers -> sibling-delta.** clk and pinctrl are driven by the shared
|
||||||
rockchip framework (`drivers/clk/rockchip/clk.c`, `pinctrl-rockchip.c`). RV1106's
|
rockchip framework (`drivers/clk/rockchip/clk.c`, `pinctrl-rockchip.c`). RV1106's
|
||||||
siblings **rv1126 and rv1108 exist in BOTH vendor-5.10 and mainline-6.18**, so
|
siblings **rv1126 and rv1108 exist in BOTH vendor-5.10 and mainline-6.18**, so
|
||||||
their 5.10→6.18 delta is the exact recipe for how the framework API changed —
|
their 5.10->6.18 delta is the exact recipe for how the framework API changed.
|
||||||
we apply that recipe to rv1106's data (`clk-rv1106.c` = 1284 lines;
|
We apply that recipe to rv1106's data (`clk-rv1106.c` = 1284 lines;
|
||||||
`rv1106-cru.h` clock IDs; the rv1106 pinctrl table). This turns "adapt to APIs
|
`rv1106-cru.h` clock IDs; the rv1106 pinctrl table). This turns "adapt to APIs
|
||||||
we have to guess" into "copy a diff a sibling already proves."
|
we have to guess" into "copy a diff a sibling already proves."
|
||||||
- **SoC-unique code → forward-port + build-fix-build.** `mach-rockchip`
|
- **SoC-unique code -> forward-port + build-fix-build.** `mach-rockchip`
|
||||||
(machine, `rockchip_hptimer`, `rv1106_pm`/`rv1106_sleep.S`), the CRU/GRF glue,
|
(machine, `rockchip_hptimer`, `rv1106_pm`/`rv1106_sleep.S`), the CRU/GRF glue,
|
||||||
and the board DT have no sibling; port directly and iterate on the 6.18 build.
|
and the board DT have no sibling; port directly and iterate on the 6.18 build.
|
||||||
- **Mainline-present drivers → just wire DT + clocks.** 8250-dw UART, dw_mmc
|
- **Mainline-present drivers -> just wire DT + clocks.** 8250-dw UART, dw_mmc
|
||||||
(SDIO/eMMC), dw-apb i2c, rockchip gpio/pinctrl core, pwm, dw-wdt watchdog are all
|
(SDIO/eMMC), dw-apb i2c, rockchip gpio/pinctrl core, pwm, dw-wdt watchdog are all
|
||||||
in mainline 6.18 — we do **not** port them, we supply the DT nodes + clock/reset
|
in mainline 6.18. We do **not** port them, we supply the DT nodes + clock/reset
|
||||||
phandles and let the mainline drivers bind.
|
phandles and let the mainline drivers bind.
|
||||||
- **Out-of-tree drivers → port the driver.** AIC8800 wifi (plan44 has none; ours),
|
- **Out-of-tree drivers -> port the driver.** AIC8800 wifi (plan44 has none; ours),
|
||||||
RGA/MPP kernel bits (assess vs need — the 86-Panel has no camera), carrying our
|
RGA/MPP kernel bits (assess vs need: the 86-Panel has no camera), carrying our
|
||||||
Tier-1 SDIO-wakeup fix forward.
|
Tier-1 SDIO-wakeup fix forward.
|
||||||
- **Our 4 kernel patches** (panel-mcu-reset/logo, watchdog-enable, usb-otg
|
- **Our 4 kernel patches** (panel-mcu-reset/logo, watchdog-enable, usb-otg
|
||||||
dual-role, recovery-splash) → re-apply against 6.18 (line offsets + some DT
|
dual-role, recovery-splash) -> re-apply against 6.18 (line offsets + some DT
|
||||||
bindings changed).
|
bindings changed).
|
||||||
|
|
||||||
## Bring-up milestones (console-first order)
|
## Bring-up milestones (console-first order)
|
||||||
|
|
||||||
- **M0 — buildable base.** 6.18.46 tree + our Buildroot/uClibc toolchain builds a
|
- **M0: buildable base.** 6.18.46 tree + our Buildroot/uClibc toolchain builds a
|
||||||
generic ARM kernel; a `rv1106_defconfig` forward-ported from vendor 5.10.
|
generic ARM kernel; a `rv1106_defconfig` forward-ported from vendor 5.10.
|
||||||
- **M1 — SoC compiles.** `mach-rockchip` RV1106 select + `clk-rv1106` +
|
- **M1: SoC compiles.** `mach-rockchip` RV1106 select + `clk-rv1106` +
|
||||||
rv1106 pinctrl data + `rv1106-cru.h` compile clean against 6.18 (no boot yet).
|
rv1106 pinctrl data + `rv1106-cru.h` compile clean against 6.18 (no boot yet).
|
||||||
- **M2 — earlycon boot.** DT (`rv1106.dtsi` core) + clk + pinctrl + 8250-dw →
|
- **M2: earlycon boot.** DT (`rv1106.dtsi` core) + clk + pinctrl + 8250-dw ->
|
||||||
kernel prints to the RV1106 UART. The first "it's alive."
|
kernel prints to the RV1106 UART. The first "it's alive."
|
||||||
- **M3 — rootfs boot.** dw_mmc + DT → eMMC/SD rootfs, userspace up on our Buildroot.
|
- **M3: rootfs boot.** dw_mmc + DT -> eMMC/SD rootfs, userspace up on our Buildroot.
|
||||||
- **M4 — display.** VOP2 + the RGB666 720×720 panel + GT911 touch + PWM backlight →
|
- **M4: display.** VOP2 + the RGB666 720x720 panel + GT911 touch + PWM backlight ->
|
||||||
the LVGL UI renders.
|
the LVGL UI renders.
|
||||||
- **M5 — wifi + our patches.** AIC8800 SDIO driver ported (+ Tier-1 fix); the 4
|
- **M5: wifi + our patches.** AIC8800 SDIO driver ported (+ Tier-1 fix); the 4
|
||||||
custom kernel patches re-applied.
|
custom kernel patches re-applied.
|
||||||
- **M6 — the rest.** RGA (UI accel), watchdog, HPMCU coprocessor, USB-OTG dual role.
|
- **M6: the rest.** RGA (UI accel), watchdog, HPMCU coprocessor, USB-OTG dual role.
|
||||||
|
|
||||||
## Regression (so a 6.18 port doesn't reintroduce the struct-ABI bug class)
|
## Regression (so a 6.18 port doesn't reintroduce the struct-ABI bug class)
|
||||||
|
|
||||||
The warden-sdk simulator + MC/DC harness are how we hold the line: `HpmcuSim` and
|
The warden-sdk simulator + MC/DC harness are how we hold the line: `HpmcuSim` and
|
||||||
the register/`MemBus` seam test the supervisor/reset logic off the ported kernel;
|
the register/`MemBus` seam test the supervisor/reset logic off the ported kernel;
|
||||||
the `relays-mcdc` pattern extends to each ported driver we own; and a
|
the `relays-mcdc` pattern extends to each ported driver we own; and a
|
||||||
**config-lint CI gate** (the class that bricked c8a3 — a load address in unreserved
|
**config-lint CI gate** (the class that bricked c8a3: a load address in unreserved
|
||||||
kernel RAM) checks the DT reservations against the drivers on every build.
|
kernel RAM) checks the DT reservations against the drivers on every build.
|
||||||
|
|
||||||
## Layout (this repo)
|
## Layout (this repo)
|
||||||
|
|||||||
@@ -3,39 +3,39 @@
|
|||||||
Booting the self-built 6.18 kernel on real hardware (warden-c8a3), 2026-08-24.
|
Booting the self-built 6.18 kernel on real hardware (warden-c8a3), 2026-08-24.
|
||||||
The build side (zImage + dtb) is in `../rv1106-enablement/`; this is the on-target
|
The build side (zImage + dtb) is in `../rv1106-enablement/`; this is the on-target
|
||||||
half. **The safe-test framework and the boot-image format below are the reusable
|
half. **The safe-test framework and the boot-image format below are the reusable
|
||||||
findings — they worked on the first hardware try.**
|
findings: they worked on the first hardware try.**
|
||||||
|
|
||||||
## The safe test path (A/B slot _b, never touch _a)
|
## The safe test path (A/B slot _b, never touch _a)
|
||||||
|
|
||||||
c8a3 is the desk-connected bench Warden: reachable over its USB-gadget ethernet
|
c8a3 is the desk-connected bench Warden: reachable over its USB-gadget ethernet
|
||||||
(dev-build dropbear; address and bench credentials live in the private
|
(dev-build dropbear; address and bench credentials live in the private
|
||||||
deployment notes — use `scp -O`, no sftp-server), plus the serial console at
|
deployment notes: use `scp -O`, no sftp-server), plus the serial console at
|
||||||
115200 and remotely switchable power. It runs an A/B
|
115200 and remotely switchable power. It runs an A/B
|
||||||
firmware (boot_a=mmcblk0p5 / boot_b=mmcblk0p6, 32 MiB each; rootfs_a/_b; AvbABData
|
firmware (boot_a=mmcblk0p5 / boot_b=mmcblk0p6, 32 MiB each; rootfs_a/_b; AvbABData
|
||||||
in `misc` sector 4 / byte 2048).
|
in `misc` sector 4 / byte 2048).
|
||||||
|
|
||||||
The test never risks the working system:
|
The test never risks the working system:
|
||||||
1. Back up `boot_b` (`dd .../by-name/boot_b -> /userdata/boot_b.bak`).
|
1. Back up `boot_b` (`dd .../by-name/boot_b -> /userdata/boot_b.bak`).
|
||||||
2. Write the test `boot.img` to **boot_b only** — `_a` (the shipped WardenOS) is
|
2. Write the test `boot.img` to **boot_b only**. `_a` (the shipped WardenOS) is
|
||||||
untouched.
|
untouched.
|
||||||
3. Flip AvbABData to a **one-shot _b**: `craft_ab.py 14 0 1 15 1 0 1` → A(prio14,
|
3. Flip AvbABData to a **one-shot _b**: `craft_ab.py 14 0 1 15 1 0 1` -> A(prio14,
|
||||||
tries0,ok1) B(prio15,tries1,ok0); write to `misc` sector 4. U-Boot's SPL then
|
tries0,ok1) B(prio15,tries1,ok0); write to `misc` sector 4. U-Boot's SPL then
|
||||||
picks _b once, decrements tries→0, and boots it.
|
picks _b once, decrements tries->0, and boots it.
|
||||||
4. If the test kernel fails/hangs, U-Boot auto-reverts to `_a` (SPL: "slot boot
|
4. If the test kernel fails/hangs, U-Boot auto-reverts to `_a` (SPL: "slot boot
|
||||||
failed, resetting" → next boot `A/B-slot: _a, successful: 1`). A hung kernel
|
failed, resetting" -> next boot `A/B-slot: _a, successful: 1`). A hung kernel
|
||||||
just needs a Zigbee power-cycle; `_a` boots WardenOS back. **Zero manual
|
just needs a Zigbee power-cycle; `_a` boots WardenOS back. **Zero manual
|
||||||
recovery needed — verified across three failed attempts.**
|
recovery needed: verified across three failed attempts.**
|
||||||
|
|
||||||
`earlycon=uart8250,mmio32,0xff4c0000` is confirmed correct: it is in the live
|
`earlycon=uart8250,mmio32,0xff4c0000` is confirmed correct: it is in the live
|
||||||
5.10 cmdline, and U-Boot's own DDR probe reports `Adding bank: 0x0 - 0x10000000`
|
5.10 cmdline, and U-Boot's own DDR probe reports `Adding bank: 0x0 - 0x10000000`
|
||||||
(256 MiB) — so the M2 DT's memory node and earlycon are right.
|
(256 MiB), so the M2 DT's memory node and earlycon are right.
|
||||||
|
|
||||||
## The boot-image format (this was the whole fight)
|
## The boot-image format (this was the whole fight)
|
||||||
|
|
||||||
rockchip U-Boot (this build) needs a very specific `boot.img`, NOT a plain FIT:
|
rockchip U-Boot (this build) needs a very specific `boot.img`, NOT a plain FIT:
|
||||||
|
|
||||||
- **External-data FIT.** `mkimage -f its -E -p 0x800`. The FDT metadata stays tiny
|
- **External-data FIT.** `mkimage -f its -E -p 0x800`. The FDT metadata stays tiny
|
||||||
(totalsize ≈ 1536 B, matching boot_a); the kernel/fdt/resource **data is
|
(totalsize ~ 1536 B, matching boot_a); the kernel/fdt/resource **data is
|
||||||
appended** after it. A normal embedded-data FIT (totalsize = whole image) is
|
appended** after it. A normal embedded-data FIT (totalsize = whole image) is
|
||||||
rejected with `FIT: No fit blob` / `No FIT image`.
|
rejected with `FIT: No fit blob` / `No FIT image`.
|
||||||
- **A `resource` (multi) sub-image is mandatory.** rockchip reads the DTB via the
|
- **A `resource` (multi) sub-image is mandatory.** rockchip reads the DTB via the
|
||||||
@@ -43,62 +43,62 @@ rockchip U-Boot (this build) needs a very specific `boot.img`, NOT a plain FIT:
|
|||||||
with `resource_tool --pack`. Without it: `Failed to load DTB, ret=-19`.
|
with `resource_tool --pack`. Without it: `Failed to load DTB, ret=-19`.
|
||||||
- **Sysmem sentinel load addresses.** fdt `load=0xffffff00`, kernel
|
- **Sysmem sentinel load addresses.** fdt `load=0xffffff00`, kernel
|
||||||
`load=entry=0xffffff01`; U-Boot's sysmem places them (it chose kernel@0x8000,
|
`load=entry=0xffffff01`; U-Boot's sysmem places them (it chose kernel@0x8000,
|
||||||
fdt@0xc00000). Real low addresses collided → "No fit blob".
|
fdt@0xc00000). Real low addresses collided -> "No fit blob".
|
||||||
- `CONFIG_FIT_SIGNATURE` is **off** in this U-Boot, so the image need not be
|
- `CONFIG_FIT_SIGNATURE` is **off** in this U-Boot, so the image need not be
|
||||||
signed. Template: `sdk/sysdrv/source/kernel/boot.its`; builder recipe:
|
signed. Template: `sdk/sysdrv/source/kernel/boot.its`; builder recipe:
|
||||||
`sdk/project/scripts/mk-fitimage.sh` (`mkimage -E -p 0x800`).
|
`sdk/project/scripts/mk-fitimage.sh` (`mkimage -E -p 0x800`).
|
||||||
|
|
||||||
With the correct format, U-Boot loaded my kernel + my DTB and printed my DT model
|
With the correct format, U-Boot loaded my kernel + my DTB and printed my DT model
|
||||||
string (`Model: WardenOS 86-Panel (RV1106) — M2 earlycon bring-up`), then
|
string (`Model: WardenOS 86-Panel (RV1106), M2 earlycon bring-up`), then
|
||||||
`Starting kernel ...`.
|
`Starting kernel ...`.
|
||||||
|
|
||||||
## Result: [x] M2 achieved — the 6.18 kernel boots on hardware
|
## Result: [x] M2 achieved, the 6.18 kernel boots on hardware
|
||||||
|
|
||||||
Six attempts, each auto-recovering to `_a`, then a clean boot:
|
Six attempts, each auto-recovering to `_a`, then a clean boot:
|
||||||
|
|
||||||
```
|
```
|
||||||
[0.000000] Linux version 6.18.46 … #2 SMP
|
[0.000000] Linux version 6.18.46 ... #2 SMP
|
||||||
[0.000000] CPU: ARMv7 Processor [410fc075]
|
[0.000000] CPU: ARMv7 Processor [410fc075]
|
||||||
[0.000000] OF: fdt: Machine model: WardenOS 86-Panel (RV1106) — M2 earlycon bring-up
|
[0.000000] OF: fdt: Machine model: WardenOS 86-Panel (RV1106), M2 earlycon bring-up
|
||||||
[0.000000] earlycon: uart8250 at MMIO32 0xff4c0000
|
[0.000000] earlycon: uart8250 at MMIO32 0xff4c0000
|
||||||
[0.000000] cma: Reserved 64 MiB at 0x0c000000
|
[0.000000] cma: Reserved 64 MiB at 0x0c000000
|
||||||
[0.040693] Calibrating delay loop … 48.00 BogoMIPS ← arch timer up
|
[0.040693] Calibrating delay loop ... 48.00 BogoMIPS <- arch timer up
|
||||||
[0.343810] pinctrl core: initialized pinctrl subsystem
|
[0.343810] pinctrl core: initialized pinctrl subsystem
|
||||||
[1.968703] ff4c0000.serial: ttyS2 … is a 16550A ← 8250 on our CRU clock
|
[1.968703] ff4c0000.serial: ttyS2 ... is a 16550A <- 8250 on our CRU clock
|
||||||
```
|
```
|
||||||
|
|
||||||
Two more bugs, found via the DEBUG_LL rebuild (`DEBUG_LL_UART_8250`, PHYS
|
Two more bugs, found via the DEBUG_LL rebuild (`DEBUG_LL_UART_8250`, PHYS
|
||||||
0xff4c0000, shift 2, 32-bit word + `earlyprintk` — the decompressor prints
|
0xff4c0000, shift 2, 32-bit word + `earlyprintk`, the decompressor prints
|
||||||
pre-MMU), then fixed:
|
pre-MMU), then fixed:
|
||||||
|
|
||||||
- **DTB overrun → `r2=0` / `invalid dtb`.** The multi_v7 zImage decompresses to
|
- **DTB overrun -> `r2=0` / `invalid dtb`.** The multi_v7 zImage decompresses to
|
||||||
~20 MiB from 0x8000, overrunning the DTB at 0xc00000, so the decompressor handed
|
~20 MiB from 0x8000, overrunning the DTB at 0xc00000, so the decompressor handed
|
||||||
the kernel a null DTB pointer. Fix: place the fdt high — `load=0x08000000` in the
|
the kernel a null DTB pointer. Fix: place the fdt high (`load=0x08000000`) in the
|
||||||
FIT `.its` (see `boot5.its`). The real fix is a lean defconfig; multi_v7 is bloat.
|
FIT `.its` (see `boot5.its`). The real fix is a lean defconfig; multi_v7 is bloat.
|
||||||
- **`grf_ctx` NULL deref in clk-rv1106.** `rockchip_clk_register_branches(grf_ctx,…)`
|
- **`grf_ctx` NULL deref in clk-rv1106.** `rockchip_clk_register_branches(grf_ctx,...)`
|
||||||
crashed because the minimal DT had no `rockchip,rv1106-grf-cru` node to set
|
crashed because the minimal DT had no `rockchip,rv1106-grf-cru` node to set
|
||||||
`grf_ctx`. Fix: add the `grf-clock-controller` child to the grf syscon (now in
|
`grf_ctx`. Fix: add the `grf-clock-controller` child to the grf syscon (now in
|
||||||
`dts/rv1106-warden-m2.dts`).
|
`dts/rv1106-warden-m2.dts`).
|
||||||
|
|
||||||
**Console baud gotcha:** `console=ttyS2,115200`. earlycon is readable at 115200
|
**Console baud gotcha:** `console=ttyS2,115200`. earlycon is readable at 115200
|
||||||
(U-Boot leaves uart2 there), but the vendor's 1.5M console rate is garbage on the
|
(U-Boot leaves uart2 there), but the vendor's 1.5M console rate is garbage on the
|
||||||
CP2102 bench adapter — so the M2 DT pins 115200 for readable bring-up; production
|
CP2102 bench adapter, so the M2 DT pins 115200 for readable bring-up; production
|
||||||
overrides to 1.5M.
|
overrides to 1.5M.
|
||||||
|
|
||||||
## M3 (same session): [x] the full WardenOS runs on the 6.18 kernel
|
## M3 (same session): [x] the full WardenOS runs on the 6.18 kernel
|
||||||
|
|
||||||
Adding the eMMC `dw_mmc` node (`mmc@ffa90000`, clocks from cru + grf_cru) was the
|
Adding the eMMC `dw_mmc` node (`mmc@ffa90000`, clocks from cru + grf_cru) was the
|
||||||
only change M3 needed — the mmc/ext4 drivers are already in-config. The kernel
|
only change M3 needed: the mmc/ext4 drivers are already in-config. The kernel
|
||||||
enumerated the eMMC at HS200, mounted the ext4 rootfs, ran `/sbin/init`, and
|
enumerated the eMMC at HS200, mounted the ext4 rootfs, ran `/sbin/init`, and
|
||||||
started every WardenOS daemon. A serial root login confirms `uname -a` →
|
started every WardenOS daemon. A serial root login confirms `uname -a` ->
|
||||||
`Linux warden-c8a3 6.18.46 armv7l`, with `warden-flared/-modbus/-mikrotik/-asic/
|
`Linux warden-c8a3 6.18.46 armv7l`, with `warden-flared/-modbus/-mikrotik/-asic/
|
||||||
-ui/-flight` all running. Expected M4/M5 gaps show cleanly: the 5.10 aic8800 `.ko`
|
-ui/-flight` all running. Expected M4/M5 gaps show cleanly: the 5.10 aic8800 `.ko`
|
||||||
won't load (vermagic → M5), and there's no backlight/framebuffer yet (→ M4).
|
won't load (vermagic -> M5), and there's no backlight/framebuffer yet (-> M4).
|
||||||
|
|
||||||
**Console lesson applied:** with `console=ttyS2,115200` the whole boot is readable
|
**Console lesson applied:** with `console=ttyS2,115200` the whole boot is readable
|
||||||
on the CP2102 (the 1.5M vendor rate is garbage on it). Serial login uses the same
|
on the CP2102 (the 1.5M vendor rate is garbage on it). Serial login uses the same
|
||||||
`c8a3_run.py` helper (dev-build bench credentials, see private deployment notes)
|
`c8a3_run.py` helper (dev-build bench credentials, see private deployment notes)
|
||||||
as the 5.10 firmware — the userspace is unchanged.
|
as the 5.10 firmware: the userspace is unchanged.
|
||||||
|
|
||||||
Next: M4 (VOP2 display + panel + touch), M5 (AIC8800 SDIO port + our 4 patches),
|
Next: M4 (VOP2 display + panel + touch), M5 (AIC8800 SDIO port + our 4 patches),
|
||||||
M6 (RGA/watchdog/HPMCU/USB-OTG); plus a lean defconfig + Buildroot-on-6.18 cleanup.
|
M6 (RGA/watchdog/HPMCU/USB-OTG); plus a lean defconfig + Buildroot-on-6.18 cleanup.
|
||||||
|
|||||||
@@ -1,52 +1,52 @@
|
|||||||
# RV1106 hardware-capabilities audit — nothing left on the table
|
# RV1106 hardware-capabilities audit: nothing left on the table
|
||||||
|
|
||||||
Every block in the vendor SoC DT (`rv1106.dtsi`), classified. Goal: a 6.18 driver
|
Every block in the vendor SoC DT (`rv1106.dtsi`), classified. Goal: a 6.18 driver
|
||||||
for every capability the hardware actually has, open-source, verified. Camera/ISP
|
for every capability the hardware actually has, open-source, verified. Camera/ISP
|
||||||
is the only whole class deliberately skipped — the 86-Panel has no camera.
|
is the only whole class deliberately skipped: the 86-Panel has no camera.
|
||||||
|
|
||||||
## Done / at parity (verified on warden-c8a3)
|
## Done / at parity (verified on warden-c8a3)
|
||||||
CRU clk · pinctrl (+ioc/pmuioc) · GIC · arch timer · pl330 DMA · 8250 uart (×3) ·
|
CRU clk · pinctrl (+ioc/pmuioc) · GIC · arch timer · pl330 DMA · 8250 uart (x3) ·
|
||||||
dw_mmc eMMC · i2c · dw-wdt · **RTC** · **tsadc** · **RGA** (rga2, hw 3.3.87975) ·
|
dw_mmc eMMC · i2c · dw-wdt · **RTC** · **tsadc** · **RGA** (rga2, hw 3.3.87975) ·
|
||||||
PWM backlight · **USB host** (dwc3/xhci) + usb2phy · grf/pmu syscons.
|
PWM backlight · **USB host** (dwc3/xhci) + usb2phy · grf/pmu syscons.
|
||||||
|
|
||||||
## Verified this run (2026-08-25) — all on warden-c8a3, self-built 6.18.46
|
## Verified this run (2026-08-25), all on warden-c8a3, self-built 6.18.46
|
||||||
- **AIC8800 wifi** — wlan0 up, scanned the site AP at −43 dBm (modules; `wifi/VERIFIED-on-c8a3.md`).
|
- **AIC8800 wifi**: wlan0 up, scanned the site AP at -43 dBm (modules; `wifi/VERIFIED-on-c8a3.md`).
|
||||||
- **TRNG** — /dev/hwrng, real HW entropy (`rng-otp/`).
|
- **TRNG**: /dev/hwrng, real HW entropy (`rng-otp/`).
|
||||||
- **OTP/nvmem** — rockchip-otp0 reads chip id (`rng-otp/`).
|
- **OTP/nvmem**: rockchip-otp0 reads chip id (`rng-otp/`).
|
||||||
- **GMAC** — eth0 Link Up 100 Mbps/Full (`gmac/`).
|
- **GMAC**: eth0 Link Up 100 Mbps/Full (`gmac/`).
|
||||||
- **SARADC** — iio:device0 reads 2 ch; the −22 was vref, not clk (`adc/SARADC-FIX.md`).
|
- **SARADC**: iio:device0 reads 2 ch; the -22 was vref, not clk (`adc/SARADC-FIX.md`).
|
||||||
|
|
||||||
## VOP display — VERIFIED this run (2026-08-25)
|
## VOP display: VERIFIED this run (2026-08-25)
|
||||||
Full WardenOS Dashboard renders on the 86-Panel on 6.18 (`_b`), webcam-verified,
|
Full WardenOS Dashboard renders on the 86-Panel on 6.18 (`_b`), webcam-verified,
|
||||||
pixel-identical to stock `_a`. Two VOP driver bugs were the final black-screen
|
pixel-identical to stock `_a`. Two VOP driver bugs were the final black-screen
|
||||||
cause: `rgb_dclk_pol` hardcoded inverted (panel needs 0), and the wrong primary
|
cause: `rgb_dclk_pol` hardcoded inverted (panel needs 0), and the wrong primary
|
||||||
scanout window (rv1106 uses **WIN1**, not rv1126's WIN2). Full chain + `_a`-vs-`_b`
|
scanout window (rv1106 uses **WIN1**, not rv1126's WIN2). Full chain + `_a`-vs-`_b`
|
||||||
register diff in `display/VERIFIED.md`.
|
register diff in `display/VERIFIED.md`.
|
||||||
|
|
||||||
## GT911 touch — VERIFIED this run (2026-08-25)
|
## GT911 touch: VERIFIED this run (2026-08-25)
|
||||||
UI responds to taps/swipes on the panel (confirmed by hand at the panel); GT911 detected
|
UI responds to taps/swipes on the panel (confirmed by hand at the panel); GT911 detected
|
||||||
(`ID 911, version 1060`), `/dev/input/event0` held by warden-ui. Fix:
|
(`ID 911, version 1060`), `/dev/input/event0` held by warden-ui. Fix:
|
||||||
`CONFIG_TOUCHSCREEN_GOODIX=y` (built-in — the rootfs `goodix.ko` is a 5.10 build
|
`CONFIG_TOUCHSCREEN_GOODIX=y` (built-in, the rootfs `goodix.ko` is a 5.10 build
|
||||||
that can't load on 6.18) + GT911 node on `&i2c3`. Details in `touch/VERIFIED.md`.
|
that can't load on 6.18) + GT911 node on `&i2c3`. Details in `touch/VERIFIED.md`.
|
||||||
|
|
||||||
## In flight
|
## In flight
|
||||||
- **i2s-tdm** — DAI builds; needs the codec + card (below).
|
- **i2s-tdm**: DAI builds; needs the codec + card (below).
|
||||||
- **AIC8800 BT** — module built (6.18 vermagic); HCI bring-up not yet exercised.
|
- **AIC8800 BT**: module built (6.18 vermagic); HCI bring-up not yet exercised.
|
||||||
|
|
||||||
## audio — VERIFIED this run
|
## audio: VERIFIED this run
|
||||||
card `rv1106-acodec` + pcmC0D0p/c (`audio/`); audible test @ bench with display.
|
card `rv1106-acodec` + pcmC0D0p/c (`audio/`); audible test @ bench with display.
|
||||||
|
|
||||||
## Remaining blocks — final status (all real capabilities now verified)
|
## Remaining blocks: final status (all real capabilities now verified)
|
||||||
| Block | verdict | note |
|
| Block | verdict | note |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **mailbox** (HPMCU) | VERIFIED | A7↔RISC-V-SCR1 round-trip, 5/5 exact echoes (`mailbox/VERIFIED.md`). Took 3 hardware-found fixes: rv1106 num_chans=1 (1 shared IRQ, not 4), CLK_CORE_MCU IGNORE_UNUSED (6.18 was gating the coprocessor clock), + an open SCR1 echo firmware with A2B_INTEN. The /dev/mem SRAM watchdog stays as a separate dead-man's-switch. |
|
| **mailbox** (HPMCU) | VERIFIED | A7<->RISC-V-SCR1 round-trip, 5/5 exact echoes (`mailbox/VERIFIED.md`). Took 3 hardware-found fixes: rv1106 num_chans=1 (1 shared IRQ, not 4), CLK_CORE_MCU IGNORE_UNUSED (6.18 was gating the coprocessor clock), + an open SCR1 echo firmware with A2B_INTEN. The /dev/mem SRAM watchdog stays as a separate dead-man's-switch. |
|
||||||
| **NPU** (rknpu) | [x] open driver VERIFIED; compute deferred | open GPL rknpu 0.9.2 driver, /dev/dri/card1, version ioctl PASS (`npu/VERIFIED.md`). Open *compute* (a regcmd compiler) is a from-scratch ~person-year register-RE project — no RV1106 prior art, no public TRM Part 2, mainline accel/rocket+Teflon are RK3588-only. Ship the driver, no blob. |
|
| **NPU** (rknpu) | [x] open driver VERIFIED; compute deferred | open GPL rknpu 0.9.2 driver, /dev/dri/card1, version ioctl PASS (`npu/VERIFIED.md`). Open *compute* (a regcmd compiler) is a from-scratch ~person-year register-RE project: no RV1106 prior art, no public TRM Part 2, mainline accel/rocket+Teflon are RK3588-only. Ship the driver, no blob. |
|
||||||
| **pvtm** | VERIFIED | both core+pmu PVT monitors probe; debugfs ring-osc reads (`pvtm/PORT-DONE.md`). |
|
| **pvtm** | VERIFIED | both core+pmu PVT monitors probe; debugfs ring-osc reads (`pvtm/PORT-DONE.md`). |
|
||||||
| **crypto-v3** (accel) | deferred (documented) | ~100 KB whole-subsystem replacement of mainline's rk3288 crypto + heavy crypto-API deltas; the **CPU crypto extensions (AES/SHA, batch2 =y) already cover the functional need** — an offload optimization, not a capability gap. |
|
| **crypto-v3** (accel) | deferred (documented) | ~100 KB whole-subsystem replacement of mainline's rk3288 crypto + heavy crypto-API deltas; the **CPU crypto extensions (AES/SHA, batch2 =y) already cover the functional need**, an offload optimization, not a capability gap. |
|
||||||
| camera/ISP, SPI | N/A | no such hardware on the 86-Panel. |
|
| camera/ISP, SPI | N/A | no such hardware on the 86-Panel. |
|
||||||
|
|
||||||
**Conclusion:** every RV1106 block with real, exercisable value on the panel is
|
**Conclusion:** every RV1106 block with real, exercisable value on the panel is
|
||||||
ported + verified on 6.18 — including the mailbox and the open NPU driver. The only
|
ported + verified on 6.18, including the mailbox and the open NPU driver. The only
|
||||||
deferred item is the crypto *accelerator* (CPU crypto already covers it) and open
|
deferred item is the crypto *accelerator* (CPU crypto already covers it) and open
|
||||||
NPU *compute* (a person-year RE effort, scoped in `npu/OPEN-NPU-PLAN.md`). No
|
NPU *compute* (a person-year RE effort, scoped in `npu/OPEN-NPU-PLAN.md`). No
|
||||||
unexplored gap remains.
|
unexplored gap remains.
|
||||||
@@ -55,5 +55,5 @@ unexplored gap remains.
|
|||||||
cif · csi2-dphy · mipi-csi2 · rkisp (all camera/ISP) · SPI (no on-board SPI device).
|
cif · csi2-dphy · mipi-csi2 · rkisp (all camera/ISP) · SPI (no on-board SPI device).
|
||||||
|
|
||||||
## Order (after wifi)
|
## Order (after wifi)
|
||||||
GMAC → trngv1 → crypto-v3 → OTP → audio (codec/dsm) → NPU → mailbox → pvtm.
|
GMAC -> trngv1 -> crypto-v3 -> OTP -> audio (codec/dsm) -> NPU -> mailbox -> pvtm.
|
||||||
GMAC + trngv1 first: highest real value (wired net + hardware entropy).
|
GMAC + trngv1 first: highest real value (wired net + hardware entropy).
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ c8a3, not just compiled.
|
|||||||
|
|
||||||
| Driver / node | 5.10 evidence | mainline? | 6.18 status |
|
| Driver / node | 5.10 evidence | mainline? | 6.18 status |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| CRU clock (clk-rv1106) | — | ported | [x] M2 |
|
| CRU clock (clk-rv1106) | n/a | ported | [x] M2 |
|
||||||
| pinctrl-rockchip (rv1106) | — | ported | [x] M2 |
|
| pinctrl-rockchip (rv1106) | n/a | ported | [x] M2 |
|
||||||
| GIC-400 / arch_timer | arch_timer | mainline | [x] M2 |
|
| GIC-400 / arch_timer | arch_timer | mainline | [x] M2 |
|
||||||
| dw_mmc (eMMC) | dw-mci | mainline | [x] M3 |
|
| dw_mmc (eMMC) | dw-mci | mainline | [x] M3 |
|
||||||
| 8250 uart2 (console) | ttyS2 | mainline | [x] M2 |
|
| 8250 uart2 (console) | ttyS2 | mainline | [x] M2 |
|
||||||
| GPIO (rockchip, ×5 banks) | gpio-rockchip | mainline | [x] batch1 (chips 0–4) |
|
| GPIO (rockchip, x5 banks) | gpio-rockchip | mainline | [x] batch1 (chips 0-4) |
|
||||||
| DMA (pl330, ff420000) | ff420000.dma-controller | mainline | [x] batch1 |
|
| DMA (pl330, ff420000) | ff420000.dma-controller | mainline | [x] batch1 |
|
||||||
| uart1 / uart4 | ttyS1, ttyS4 | mainline | [x] batch1 |
|
| uart1 / uart4 | ttyS1, ttyS4 | mainline | [x] batch1 |
|
||||||
| I2C (dw-apb, ff460000=i2c3) | ff460000.i2c | mainline | [x] batch1 (i2c-3) |
|
| I2C (dw-apb, ff460000=i2c3) | ff460000.i2c | mainline | [x] batch1 (i2c-3) |
|
||||||
@@ -23,22 +23,22 @@ c8a3, not just compiled.
|
|||||||
| TRNG (rng@ff448000) | rockchip,trngv1 | mainline (rk3588 IP) | [x] /dev/hwrng, real entropy (`rng-otp/`) |
|
| TRNG (rng@ff448000) | rockchip,trngv1 | mainline (rk3588 IP) | [x] /dev/hwrng, real entropy (`rng-otp/`) |
|
||||||
| OTP/nvmem (ff3d0000) | rockchip,rv1106-otp | ported (px30_otp_read) | [x] rockchip-otp0, reads chip id |
|
| OTP/nvmem (ff3d0000) | rockchip,rv1106-otp | ported (px30_otp_read) | [x] rockchip-otp0, reads chip id |
|
||||||
| GMAC (ffa80000) | rockchip,rv1106-gmac | ported (dwmac-rk rv1106_ops) | [x] eth0 Link Up 100M/Full (`gmac/`) |
|
| GMAC (ffa80000) | rockchip,rv1106-gmac | ported (dwmac-rk rv1106_ops) | [x] eth0 Link Up 100M/Full (`gmac/`) |
|
||||||
| GPIO_SYSFS (legacy /sys/class/gpio) | — | mainline (config) | [ ] goodix script needs it |
|
| GPIO_SYSFS (legacy /sys/class/gpio) | n/a | mainline (config) | [ ] goodix script needs it |
|
||||||
| PWM (rockchip) | — | mainline (=m) | [ ] batch2 =y (backlight) |
|
| PWM (rockchip) | n/a | mainline (=m) | [ ] batch2 =y (backlight) |
|
||||||
| RTC (rv1106-rtc) | — | ported (vendor driver) | [x] /dev/rtc0 registers + reads |
|
| RTC (rv1106-rtc) | n/a | ported (vendor driver) | [x] /dev/rtc0 registers + reads |
|
||||||
| USB2 phy (inno, rv1106) | rockchip_usb2phy_* | ported (data, no tuning) | [x] probes → USB up |
|
| USB2 phy (inno, rv1106) | rockchip_usb2phy_* | ported (data, no tuning) | [x] probes -> USB up |
|
||||||
| USB host (DWC3→xhci, ffb00000) | xhci-hcd:usb1 | mainline | [x] xhci host registered |
|
| USB host (DWC3->xhci, ffb00000) | xhci-hcd:usb1 | mainline | [x] xhci host registered |
|
||||||
| USB OTG gadget (DWC3, eth0) | eth0 | mainline dwc3 | [wip] host works; eth0 needs dr_mode=peripheral |
|
| USB OTG gadget (DWC3, eth0) | eth0 | mainline dwc3 | [wip] host works; eth0 needs dr_mode=peripheral |
|
||||||
| crypto (aes/ccm/ctr/arc4) | modules | mainline | [ ] batch2 (config =y) |
|
| crypto (aes/ccm/ctr/arc4) | modules | mainline | [ ] batch2 (config =y) |
|
||||||
| PSCI node (removed) | — | — | [x] deleted (no secure monitor → SMC fault) |
|
| PSCI node (removed) | n/a | n/a | [x] deleted (no secure monitor -> SMC fault) |
|
||||||
| VOP display (ff990000) | ff990000.vop | ported (rv1126 sibling) | [wip] binds+DRM+card0; connector WIP |
|
| VOP display (ff990000) | ff990000.vop | ported (rv1126 sibling) | [wip] binds+DRM+card0; connector WIP |
|
||||||
| PWM backlight (pwm1) | — | mainline (rk3328 fallback) | [x] backlight up (brightness) |
|
| PWM backlight (pwm1) | n/a | mainline (rk3328 fallback) | [x] backlight up (brightness) |
|
||||||
| RGB666 720×720 panel | — | panel-dpi | [wip] probes; bus_format + connector WIP |
|
| RGB666 720x720 panel | n/a | panel-dpi | [wip] probes; bus_format + connector WIP |
|
||||||
| GT911 touch (goodix) | goodix, gt911 | mainline | [ ] M4 (needs GPIO_SYSFS [x] + node) |
|
| GT911 touch (goodix) | goodix, gt911 | mainline | [ ] M4 (needs GPIO_SYSFS [x] + node) |
|
||||||
| GPIO_SYSFS / crypto / CFG80211 | — | mainline (config) | [x] =y (batch2) |
|
| GPIO_SYSFS / crypto / CFG80211 | n/a | mainline (config) | [x] =y (batch2) |
|
||||||
| AIC8800 wifi (bsp/fdrv) | aic8800_* | **out-of-tree** | [x] M5 — wlan0 up, scanned the site AP at −43dBm (modules, `wifi/VERIFIED-on-c8a3.md`) |
|
| AIC8800 wifi (bsp/fdrv) | aic8800_* | **out-of-tree** | [x] M5: wlan0 up, scanned the site AP at -43dBm (modules, `wifi/VERIFIED-on-c8a3.md`) |
|
||||||
| AIC8800 BT (btlpm) | aic8800_btlpm | **out-of-tree** | [wip] module built (6.18 vermagic); HCI bring-up not yet exercised |
|
| AIC8800 BT (btlpm) | aic8800_btlpm | **out-of-tree** | [wip] module built (6.18 vermagic); HCI bring-up not yet exercised |
|
||||||
| NPU (rknpu, ff660000) | rknpu, ff660000.npu | **out-of-tree** | [x] open GPL driver VERIFIED on hardware — `/dev/dri/card1`, `rknpu_version_test` PASS (power/clock/reset path exercised); open *compute* (regcmd) remains a from-scratch RE project (`npu/VERIFIED.md`, `npu/OPEN-NPU-PLAN.md`) |
|
| NPU (rknpu, ff660000) | rknpu, ff660000.npu | **out-of-tree** | [x] open GPL driver VERIFIED on hardware: `/dev/dri/card1`, `rknpu_version_test` PASS (power/clock/reset path exercised); open *compute* (regcmd) remains a from-scratch RE project (`npu/VERIFIED.md`, `npu/OPEN-NPU-PLAN.md`) |
|
||||||
| RGA 2D (rga2) | rga2 | ported (vendor char-dev) | [x] /dev/rga, hw 3.3.87975 |
|
| RGA 2D (rga2) | rga2 | ported (vendor char-dev) | [x] /dev/rga, hw 3.3.87975 |
|
||||||
| I2S audio (i2s-tdm) | i2s | rv1126 fallback (=y) | [x] cpu DAI registers (part of the card below) |
|
| I2S audio (i2s-tdm) | i2s | rv1126 fallback (=y) | [x] cpu DAI registers (part of the card below) |
|
||||||
| Audio codec (acodec) | rockchip,rv1106-codec | ported (rv1106_codec.c) | [x] card `rv1106-acodec`, pcmC0D0p/c (`audio/`); audible test @ bench |
|
| Audio codec (acodec) | rockchip,rv1106-codec | ported (rv1106_codec.c) | [x] card `rv1106-acodec`, pcmC0D0p/c (`audio/`); audible test @ bench |
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
# Overnight autonomous kernel-completion workflow
|
# Overnight autonomous kernel-completion workflow
|
||||||
|
|
||||||
**Directive (2026-08-24 night):** port/enable **every** remaining RV1106
|
**Directive (2026-08-24 night):** port/enable **every** remaining RV1106
|
||||||
hardware capability on the self-built 6.18 kernel, **open-source-first** — we want
|
hardware capability on the self-built 6.18 kernel, **open-source-first**: we want
|
||||||
*source* we can read, harden, and bend to our needs (vendor SDK source, upstream,
|
*source* we can read, harden, and bend to our needs (vendor SDK source, upstream,
|
||||||
community repos, or reverse-engineering), never binary blobs. Not just ports —
|
community repos, or reverse-engineering), never binary blobs. Not just ports,
|
||||||
**new drivers if necessary**. Leave zero hardware capability or customization on
|
**new drivers if necessary**. Leave zero hardware capability or customization on
|
||||||
the table. Fully autonomous, runs overnight; the display's last mile is deferred
|
the table. Fully autonomous, runs overnight; the display's last mile is deferred
|
||||||
to tomorrow morning (needs eyes on the panel).
|
to tomorrow morning (needs eyes on the panel).
|
||||||
|
|
||||||
## Open-source sourcing policy (in order of preference)
|
## Open-source sourcing policy (in order of preference)
|
||||||
1. **Vendor SDK source** — `flare-edge/sdk/sysdrv/source/kernel/drivers/…`. Every
|
1. **Vendor SDK source**: `flare-edge/sdk/sysdrv/source/kernel/drivers/...`. Every
|
||||||
driver below ships as C source there (aic8800, rknpu, rga, rtc, tsadc, i2s all
|
driver below ships as C source there (aic8800, rknpu, rga, rtc, tsadc, i2s all
|
||||||
have source — the *only* closed pieces are userspace runtimes like RKNN, not
|
have source: the *only* closed pieces are userspace runtimes like RKNN, not
|
||||||
the kernel drivers). Port 5.10 → 6.18.
|
the kernel drivers). Port 5.10 -> 6.18.
|
||||||
2. **Mainline / sibling** — reuse rv1126/px30 data where the IP matches (clk,
|
2. **Mainline / sibling**: reuse rv1126/px30 data where the IP matches (clk,
|
||||||
pinctrl, vop, saradc, usb2phy all used this).
|
pinctrl, vop, saradc, usb2phy all used this).
|
||||||
3. **Community open drivers** — deep web research (AICSemi upstream/github, the
|
3. **Community open drivers**: deep web research (AICSemi upstream/github, the
|
||||||
NPU reverse-engineering efforts, etc.) for a cleaner or better-understood base.
|
NPU reverse-engineering efforts, etc.) for a cleaner or better-understood base.
|
||||||
4. **Reverse-engineering** — register maps from the TRM + the vendor source, as a
|
4. **Reverse-engineering**: register maps from the TRM + the vendor source, as a
|
||||||
last resort or to harden.
|
last resort or to harden.
|
||||||
For each: understand it, prefer the most upstream-aligned source, harden it, and
|
For each: understand it, prefer the most upstream-aligned source, harden it, and
|
||||||
add a regression check where feasible.
|
add a regression check where feasible.
|
||||||
@@ -36,17 +36,17 @@ add a regression check where feasible.
|
|||||||
| 8 | **eth0 USB gadget** | dr_mode + configfs | `eth0` to xps |
|
| 8 | **eth0 USB gadget** | dr_mode + configfs | `eth0` to xps |
|
||||||
| 9 | **Capabilities audit** | TRM + SDK sweep | enable/port everything else |
|
| 9 | **Capabilities audit** | TRM + SDK sweep | enable/port everything else |
|
||||||
|
|
||||||
### Capabilities audit (#9) — nothing left on the table
|
### Capabilities audit (#9): nothing left on the table
|
||||||
Enumerate every RV1106 block and confirm a 6.18 driver: crypto accelerator + TRNG,
|
Enumerate every RV1106 block and confirm a 6.18 driver: crypto accelerator + TRNG,
|
||||||
mailbox/HPMCU integration, DSMC/flexbus, SFC/SPI-NOR, remaining SPI, CAN, PWM
|
mailbox/HPMCU integration, DSMC/flexbus, SFC/SPI-NOR, remaining SPI, CAN, PWM
|
||||||
(fan/other), DMA2, DDR monitor, the second/other UARTs, GMAC (if wired), pvtm,
|
(fan/other), DMA2, DDR monitor, the second/other UARTs, GMAC (if wired), pvtm,
|
||||||
otp/nvmem, dsmc. Anything with hardware present and no driver → port or write it.
|
otp/nvmem, dsmc. Anything with hardware present and no driver -> port or write it.
|
||||||
|
|
||||||
## Execution model
|
## Execution model
|
||||||
Pipeline: research (Sonnet subagents, in background) ∥ port (me) → build → flash to
|
Pipeline: research (Sonnet subagents, in background) ∥ port (me) -> build -> flash to
|
||||||
c8a3 slot _b → verify on hardware → commit to warden-sdk. Serial bottleneck is
|
c8a3 slot _b -> verify on hardware -> commit to warden-sdk. Serial bottleneck is
|
||||||
c8a3; research/porting overlaps verification. **Resilience:** krecover is hardened
|
c8a3; research/porting overlaps verification. **Resilience:** krecover is hardened
|
||||||
(serial-primary + Pi/rkdeveloptool bootcount-reset fallback); on a port that won't
|
(serial-primary + Pi/rkdeveloptool bootcount-reset fallback); on a port that won't
|
||||||
land, capture the state, mark it in DRIVER-PARITY.md, and move on — never leave
|
land, capture the state, mark it in DRIVER-PARITY.md, and move on; never leave
|
||||||
c8a3 stuck, never block the whole run on one driver. Commit after every landed
|
c8a3 stuck, never block the whole run on one driver. Commit after every landed
|
||||||
driver so progress is durable.
|
driver so progress is durable.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Overnight kernel-enablement run — results (2026-08-24 night → 08-25)
|
# Overnight kernel-enablement run: results (2026-08-24 night -> 08-25)
|
||||||
|
|
||||||
Goal: port/enable **every** remaining RV1106 hardware capability on the
|
Goal: port/enable **every** remaining RV1106 hardware capability on the
|
||||||
self-built Linux 6.18.46, open-source-first, verify on hardware, so the display's
|
self-built Linux 6.18.46, open-source-first, verify on hardware, so the display's
|
||||||
@@ -7,37 +7,37 @@ last mile can start in the morning. Runs on warden-c8a3 (`_b` slot = our 6.18).
|
|||||||
## Verified on hardware this run
|
## Verified on hardware this run
|
||||||
| Driver | Evidence |
|
| Driver | Evidence |
|
||||||
|---|---|
|
|---|---|
|
||||||
| **AIC8800 wifi** (M5) | wlan0 up, `iw scan` found the site AP at −43 dBm + others. Modules (built-in deadlocks the two-stage SDIO bring-up). |
|
| **AIC8800 wifi** (M5) | wlan0 up, `iw scan` found the site AP at -43 dBm + others. Modules (built-in deadlocks the two-stage SDIO bring-up). |
|
||||||
| **TRNG** | `/dev/hwrng`, `rng_current=rockchip-rng`, real entropy. |
|
| **TRNG** | `/dev/hwrng`, `rng_current=rockchip-rng`, real entropy. |
|
||||||
| **OTP/nvmem** | `rockchip-otp0` reads chip id ("MR1"). |
|
| **OTP/nvmem** | `rockchip-otp0` reads chip id ("MR1"). |
|
||||||
| **GMAC** (wired eth) | `eth0: Link is Up - 100 Mbps/Full`. |
|
| **GMAC** (wired eth) | `eth0: Link is Up - 100 Mbps/Full`. |
|
||||||
| **SARADC** | `iio:device0` reads 2 channels (adc-keys). |
|
| **SARADC** | `iio:device0` reads 2 channels (adc-keys). |
|
||||||
| **Audio** | card `rv1106-acodec`, `/dev/snd/pcmC0D0p`+`pcmC0D0c`. Audible test → bench. |
|
| **Audio** | card `rv1106-acodec`, `/dev/snd/pcmC0D0p`+`pcmC0D0c`. Audible test -> bench. |
|
||||||
|
|
||||||
(These join the earlier-verified set: clk, pinctrl, GIC, timer, DMA, eMMC, uart×3,
|
(These join the earlier-verified set: clk, pinctrl, GIC, timer, DMA, eMMC, uartx3,
|
||||||
i2c, wdt, RTC, tsadc, RGA, USB host, PWM backlight. VOP display binds — connector
|
i2c, wdt, RTC, tsadc, RGA, USB host, PWM backlight. VOP display binds: connector
|
||||||
is the morning task.)
|
is the morning task.)
|
||||||
|
|
||||||
## Cross-cutting fixes made this run
|
## Cross-cutting fixes made this run
|
||||||
- **XZ compression** (`CONFIG_KERNEL_XZ`): the growing kernel overran U-Boot's
|
- **XZ compression** (`CONFIG_KERNEL_XZ`): the growing kernel overran U-Boot's
|
||||||
DTB-at-0xc00000 load boundary (`Sysmem Error`, FLARE-AB fell to _a). zImage
|
DTB-at-0xc00000 load boundary (`Sysmem Error`, FLARE-AB fell to _a). zImage
|
||||||
12.1→8.3 MB, ~4 MB headroom. See `kernel-618-btest-cycle` memory + wifi doc.
|
12.1->8.3 MB, ~4 MB headroom. See `kernel-618-btest-cycle` memory + wifi doc.
|
||||||
- **Module vs built-in**: wifi must be modules (SDIO two-stage bring-up races the
|
- **Module vs built-in**: wifi must be modules (SDIO two-stage bring-up races the
|
||||||
mmc probe when built-in — sequential initcall deadlock). Audio/GMAC/etc. are
|
mmc probe when built-in: sequential initcall deadlock). Audio/GMAC/etc. are
|
||||||
built-in and fine.
|
built-in and fine.
|
||||||
- Hardened the c8a3 `_b`-test → `_a`-recover cycle (compact-hex AVB serial write +
|
- Hardened the c8a3 `_b`-test -> `_a`-recover cycle (compact-hex AVB serial write +
|
||||||
readback while stable; Pi golden-env fallback for bootcount→loader).
|
readback while stable; Pi golden-env fallback for bootcount->loader).
|
||||||
|
|
||||||
## Not done — deferred with reasons (see CAPABILITIES-AUDIT.md)
|
## Not done: deferred with reasons (see CAPABILITIES-AUDIT.md)
|
||||||
mailbox (binds via rk3368 fallback but no client to exercise), crypto-v3 (CPU
|
mailbox (binds via rk3368 fallback but no client to exercise), crypto-v3 (CPU
|
||||||
crypto extensions already cover it; 100 KB port), NPU (no open userspace),
|
crypto extensions already cover it; 100 KB port), NPU (no open userspace),
|
||||||
pvtm (DVFS-only). Camera/ISP/SPI: no hardware. **No unexplored capability gap.**
|
pvtm (DVFS-only). Camera/ISP/SPI: no hardware. **No unexplored capability gap.**
|
||||||
|
|
||||||
## Morning (needs a human at the bench)
|
## Morning (needs a human at the bench)
|
||||||
1. **Display connector** — VOP binds + DRM card0 + panel probes, but no connector
|
1. **Display connector**: VOP binds + DRM card0 + panel probes, but no connector
|
||||||
link yet; needs eyes on the panel (pixels can't be verified over serial).
|
link yet; needs eyes on the panel (pixels can't be verified over serial).
|
||||||
2. **Audible audio** — `speaker-test`/`aplay` through the acodec.
|
2. **Audible audio**: `speaker-test`/`aplay` through the acodec.
|
||||||
3. Wifi **boot-time auto-load** (modules currently insmod'd by hand) — a loader
|
3. Wifi **boot-time auto-load** (modules currently insmod'd by hand): a loader
|
||||||
that inserts aic8800_bsp→fdrv(→btlpm) from the rootfs.
|
that inserts aic8800_bsp->fdrv(->btlpm) from the rootfs.
|
||||||
|
|
||||||
Provenance: every ported driver is GPL-2.0 kernel source (`PROVENANCE.md`).
|
Provenance: every ported driver is GPL-2.0 kernel source (`PROVENANCE.md`).
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# RV1106 → 6.18 forward-port — live status
|
# RV1106 -> 6.18 forward-port: live status
|
||||||
|
|
||||||
> **CANONICAL SOURCE.** This directory is a point-in-time **port-provenance layer** —
|
> **CANONICAL SOURCE.** This directory is a point-in-time **port-provenance layer**:
|
||||||
> what was ported, the milestones, verification evidence, and standalone test programs
|
> what was ported, the milestones, verification evidence, and standalone test programs
|
||||||
> (e.g. `npu/rknpu_version_test.c`). It is **not** the source of truth for the kernel
|
> (e.g. `npu/rknpu_version_test.c`). It is **not** the source of truth for the kernel
|
||||||
> delta. The canonical, current delta is **`../../patches/*.patch`** (applied by
|
> delta. The canonical, current delta is **`../../patches/*.patch`** (applied by
|
||||||
@@ -8,125 +8,125 @@
|
|||||||
> `flare-edge/research/linux-6.18.46/`. The `.c`/`.dts`/`.frag` snapshots here are
|
> `flare-edge/research/linux-6.18.46/`. The `.c`/`.dts`/`.frag` snapshots here are
|
||||||
> historical port evidence and may **lag** the final series (e.g. `clk/clk-rv1106.c`
|
> historical port evidence and may **lag** the final series (e.g. `clk/clk-rv1106.c`
|
||||||
> predates the last HPMCU-clock `CLK_IGNORE_UNUSED` fix that is in `patches/10-clk-rv1106.patch`).
|
> predates the last HPMCU-clock `CLK_IGNORE_UNUSED` fix that is in `patches/10-clk-rv1106.patch`).
|
||||||
> Do not hand-edit them or treat them as current — change kernel behaviour in `patches/`.
|
> Do not hand-edit them or treat them as current: change kernel behaviour in `patches/`.
|
||||||
|
|
||||||
Target: **Linux 6.18.46** (vanilla, `flare-edge/research/linux-6.18.46/`), forward-ported
|
Target: **Linux 6.18.46** (vanilla, `flare-edge/research/linux-6.18.46/`), forward-ported
|
||||||
from the **vendor 5.10.160** tree, no plan44 code, built with our
|
from the **vendor 5.10.160** tree, no plan44 code, built with our
|
||||||
`arm-rockchip830-...-gcc 8.3` (confirmed: **gcc 8.3 builds 6.18 fine**).
|
`arm-rockchip830-...-gcc 8.3` (confirmed: **gcc 8.3 builds 6.18 fine**).
|
||||||
|
|
||||||
## Confirmed feasibility facts
|
## Confirmed feasibility facts
|
||||||
- gcc 8.3 (our SDK toolchain) compiles the 6.18 kernel — no toolchain bump needed to start.
|
- gcc 8.3 (our SDK toolchain) compiles the 6.18 kernel: no toolchain bump needed to start.
|
||||||
- `multi_v7_defconfig` (ARM + rockchip) configures; `CLK_RV1106=y` wires in cleanly.
|
- `multi_v7_defconfig` (ARM + rockchip) configures; `CLK_RV1106=y` wires in cleanly.
|
||||||
- rv1106's siblings **rv1126/rv1108 exist in both trees**, so their 5.10→6.18 delta is a
|
- rv1106's siblings **rv1126/rv1108 exist in both trees**, so their 5.10->6.18 delta is a
|
||||||
working template for the framework API changes.
|
working template for the framework API changes.
|
||||||
|
|
||||||
## M1 — clock driver (`clk-rv1106.c`, 1294 lines): [x] COMPILES CLEAN on 6.18
|
## M1, clock driver (`clk-rv1106.c`, 1294 lines): [x] COMPILES CLEAN on 6.18
|
||||||
Build-fix loop against 6.18 — `clk-rv1106.o` (85732 bytes) builds with no errors.
|
Build-fix loop against 6.18: `clk-rv1106.o` (85732 bytes) builds with no errors.
|
||||||
Fixed (captured in `clk/`):
|
Fixed (captured in `clk/`):
|
||||||
1. **Kconfig + Makefile hooks** — added `CONFIG_CLK_RV1106` (mirrors CLK_RV1126).
|
1. **Kconfig + Makefile hooks**: added `CONFIG_CLK_RV1106` (mirrors CLK_RV1126).
|
||||||
2. **CRU register macros** — ported all 55 `RV1106_*` register-accessor `#define`s from the
|
2. **CRU register macros**: ported all 55 `RV1106_*` register-accessor `#define`s from the
|
||||||
vendor `clk.h` into 6.18's `clk.h` (mainline has none).
|
vendor `clk.h` into 6.18's `clk.h` (mainline has none).
|
||||||
3. **Header split** — `panic_notifier_list` moved to `<linux/panic_notifier.h>` (kernel
|
3. **Header split**: `panic_notifier_list` moved to `<linux/panic_notifier.h>` (kernel
|
||||||
5.18); added the include.
|
5.18); added the include.
|
||||||
4. **`rockchip_clk_register_armclk` signature change** — 5.10 took
|
4. **`rockchip_clk_register_armclk` signature change**: 5.10 took
|
||||||
`(num_parents, parent_clk, alt_parent_clk)`; 6.18 takes `(parent_names[], num_parents)`
|
`(num_parents, parent_clk, alt_parent_clk)`; 6.18 takes `(parent_names[], num_parents)`
|
||||||
and drives the mux from `reg_data.mux_core_main/alt`. Adapted the call to the 6.18 form
|
and drives the mux from `reg_data.mux_core_main/alt`. Adapted the call to the 6.18 form
|
||||||
using a parent-names array (sibling-delta from rv1126). **PORT-VERIFY**: the mux input
|
using a parent-names array (sibling-delta from rv1126). **PORT-VERIFY**: the mux input
|
||||||
list `{ "gpll","cpll","apll" }` and the `mux_core_main/alt=2` mapping are a best-effort
|
list `{ "gpll","cpll","apll" }` and the `mux_core_main/alt=2` mapping are a best-effort
|
||||||
from the 5.10 intent (main=apll) — they set the **CPU clock source**, so they must be
|
from the 5.10 intent (main=apll); they set the **CPU clock source**, so they must be
|
||||||
checked against the RV1106 `CORECLKSEL_CON` register map (TRM) and validated on hardware
|
checked against the RV1106 `CORECLKSEL_CON` register map (TRM) and validated on hardware
|
||||||
before trusting a boot. A wrong mux silently breaks boot.
|
before trusting a boot. A wrong mux silently breaks boot.
|
||||||
|
|
||||||
5. **`CLK_FRAC_DIVIDER_NO_LIMIT`** — Rockchip downstream-only frac-divider flag (6 uses on
|
5. **`CLK_FRAC_DIVIDER_NO_LIMIT`**: Rockchip downstream-only frac-divider flag (6 uses on
|
||||||
the UART frac clocks); mainline has no min/max opt-out, mapped to 0 (default limit).
|
the UART frac clocks); mainline has no min/max opt-out, mapped to 0 (default limit).
|
||||||
**PORT-VERIFY**: UART fractional baud accuracy.
|
**PORT-VERIFY**: UART fractional baud accuracy.
|
||||||
|
|
||||||
## M1 — pinctrl (`pinctrl-rockchip.c/.h`): [x] COMPILES CLEAN on 6.18
|
## M1, pinctrl (`pinctrl-rockchip.c/.h`): [x] COMPILES CLEAN on 6.18
|
||||||
`pinctrl-rockchip.o` (173688 bytes) builds no-errors. Transplanted from vendor 5.10 (effort S,
|
`pinctrl-rockchip.o` (173688 bytes) builds no-errors. Transplanted from vendor 5.10 (effort S,
|
||||||
zero API drift — the survey's assessment held): added `RV1106` to the type enum; a 159-line
|
zero API drift, the survey's assessment held): added `RV1106` to the type enum; a 159-line
|
||||||
block of `RV1106_DRV/PULL/SMT_*` macros + 3 `rv1106_calc_*_reg_and_bit()` functions; `case
|
block of `RV1106_DRV/PULL/SMT_*` macros + 3 `rv1106_calc_*_reg_and_bit()` functions; `case
|
||||||
RV1106:` in the 3 pull functions + the RK3568 drive-strength group; `rv1106_pin_banks[]` +
|
RV1106:` in the 3 pull functions + the RK3568 drive-strength group; `rv1106_pin_banks[]` +
|
||||||
`rv1106_pin_ctrl`; and the `rockchip,rv1106-pinctrl` of_device_id (dropping the vendor's
|
`rv1106_pin_ctrl`; and the `rockchip,rv1106-pinctrl` of_device_id (dropping the vendor's
|
||||||
`#ifdef CONFIG_CPU_RV1106` guard — mainline compiles all SoCs unconditionally). Captured as
|
`#ifdef CONFIG_CPU_RV1106` guard: mainline compiles all SoCs unconditionally). Captured as
|
||||||
`pinctrl/0001-rv1106-pinctrl.patch`.
|
`pinctrl/0001-rv1106-pinctrl.patch`.
|
||||||
- **PORT-VERIFY RESOLVED (iomux offsets):** the DRV/PULL/SMT per-bank offsets are confirmed by
|
- **PORT-VERIFY RESOLVED (iomux offsets):** the DRV/PULL/SMT per-bank offsets are confirmed by
|
||||||
a THIRD independent source — the upstream Simon Glass v3 pinctrl patch. Its
|
a THIRD independent source: the upstream Simon Glass v3 pinctrl patch. Its
|
||||||
`rv1106_{drv,pull,smt}_offsets[]` match ours exactly once the 0x10000-strided per-bank IOC
|
`rv1106_{drv,pull,smt}_offsets[]` match ours exactly once the 0x10000-strided per-bank IOC
|
||||||
base is applied (e.g. vendor GPIO2 DRV `0x100C0` = `0x10000 + 0xc0`). Retires the "do not
|
base is applied (e.g. vendor GPIO2 DRV `0x100C0` = `0x10000 + 0xc0`). Retires the "do not
|
||||||
guess" iomux risk without hardware. (Note upstream uses per-bank IOC regmaps + low offsets;
|
guess" iomux risk without hardware. (Note upstream uses per-bank IOC regmaps + low offsets;
|
||||||
we use the vendor's full-offset form — same absolute register, both compile.)
|
we use the vendor's full-offset form: same absolute register, both compile.)
|
||||||
- **Still PORT-VERIFY:** GPIO4 bank pin-count (`pin_banks` says 24, DT `gpio-ranges` says 32) —
|
- **Still PORT-VERIFY:** GPIO4 bank pin-count (`pin_banks` says 24, DT `gpio-ranges` says 32),
|
||||||
carried from vendor unchanged; needs TRM/hardware.
|
carried from vendor unchanged; needs TRM/hardware.
|
||||||
|
|
||||||
## M1 — mach: DONE
|
## M1, mach: DONE
|
||||||
`mach-rockchip` RV1106/RV1103 SoC recognition added as a DT-compat entry (no
|
`mach-rockchip` RV1106/RV1103 SoC recognition added as a DT-compat entry (no
|
||||||
`CPU_RV1106` symbol recreated). Captured as `mach/0001-rv1106-soc-recognition.patch`.
|
`CPU_RV1106` symbol recreated). Captured as `mach/0001-rv1106-soc-recognition.patch`.
|
||||||
**M1 is complete: clk + pinctrl + mach all compile clean on 6.18.**
|
**M1 is complete: clk + pinctrl + mach all compile clean on 6.18.**
|
||||||
|
|
||||||
## M2 — earlycon build: DONE (boot pending hardware)
|
## M2, earlycon build: DONE (boot pending hardware)
|
||||||
The first full kernel build with our SoC drivers, 2026-08-24:
|
The first full kernel build with our SoC drivers, 2026-08-24:
|
||||||
- **`multi_v7_defconfig` + `configs/m2-earlycon.fragment` builds an 11.8 MB zImage**
|
- **`multi_v7_defconfig` + `configs/m2-earlycon.fragment` builds an 11.8 MB zImage**
|
||||||
with `clk-rv1106.o` (85732 B) and `pinctrl-rockchip.o` (173688 B, our rv1106 data)
|
with `clk-rv1106.o` (85732 B) and `pinctrl-rockchip.o` (173688 B, our rv1106 data)
|
||||||
compiled *into the full tree* — no rv1106 warnings/errors. Reproducible via
|
compiled *into the full tree*: no rv1106 warnings/errors. Reproducible via
|
||||||
`build-m2.sh`.
|
`build-m2.sh`.
|
||||||
- **`dts/rv1106-warden-m2.dts` → `rv1106-warden-m2.dtb` compiles clean** (W=1, no dtc
|
- **`dts/rv1106-warden-m2.dts` -> `rv1106-warden-m2.dtb` compiles clean** (W=1, no dtc
|
||||||
warnings). A deliberately minimal DT — CPU (cortex-a7), GIC-400, arch timer, 256 MiB
|
warnings). A deliberately minimal DT: CPU (cortex-a7), GIC-400, arch timer, 256 MiB
|
||||||
RAM, GRF, the CRU (our clk-rv1106), and uart2 (`snps,dw-apb-uart`, the console) —
|
RAM, GRF, the CRU (our clk-rv1106), and uart2 (`snps,dw-apb-uart`, the console),
|
||||||
with `earlycon=uart8250,mmio32,0xff4c0000` so the first print reuses the loader's
|
with `earlycon=uart8250,mmio32,0xff4c0000` so the first print reuses the loader's
|
||||||
divisor before any clock/pinctrl probe.
|
divisor before any clock/pinctrl probe.
|
||||||
|
|
||||||
**Still PORT-VERIFY before a boot is trusted:** memory size/base (256 MiB @ 0x0 assumed
|
**Still PORT-VERIFY before a boot is trusted:** memory size/base (256 MiB @ 0x0 assumed,
|
||||||
— the loader usually patches this); the CPU clock mux in clk-rv1106 (§M1 item 4); the
|
the loader usually patches this); the CPU clock mux in clk-rv1106 (section M1 item 4); the
|
||||||
console baud (1.5M assumed). A wrong DDR/clock value silently hangs before or just after
|
console baud (1.5M assumed). A wrong DDR/clock value silently hangs before or just after
|
||||||
earlycon.
|
earlycon.
|
||||||
|
|
||||||
## M2 — boot: DONE — "it's alive" on warden-c8a3 (2026-08-24)
|
## M2, boot: DONE: "it's alive" on warden-c8a3 (2026-08-24)
|
||||||
The self-built **Linux 6.18.46 boots on real RV1106 hardware**, through our ported
|
The self-built **Linux 6.18.46 boots on real RV1106 hardware**, through our ported
|
||||||
drivers, verified over the serial console. It reaches earlycon, the arch timer
|
drivers, verified over the serial console. It reaches earlycon, the arch timer
|
||||||
(BogoMIPS calibrated), **our `clk-rv1106` CRU driver**, pinctrl, and the mainline
|
(BogoMIPS calibrated), **our `clk-rv1106` CRU driver**, pinctrl, and the mainline
|
||||||
8250 bound to uart2 **clocked by our CRU** (`ttyS2 … 16550A`), then hands off to the
|
8250 bound to uart2 **clocked by our CRU** (`ttyS2 ... 16550A`), then hands off to the
|
||||||
real console and mounts a rootfs. See `../docs/m2-boot-on-c8a3.md` for the method
|
real console and mounts a rootfs. See `../docs/m2-boot-on-c8a3.md` for the method
|
||||||
(the A/B `_b`-slot safe-test framework worked first try) and the boot-image format
|
(the A/B `_b`-slot safe-test framework worked first try) and the boot-image format
|
||||||
(external-data FIT + resource, `mkimage -E -p 0x800`).
|
(external-data FIT + resource, `mkimage -E -p 0x800`).
|
||||||
|
|
||||||
Three bring-up bugs were found and fixed on hardware, all captured in the DT:
|
Three bring-up bugs were found and fixed on hardware, all captured in the DT:
|
||||||
1. **boot.img format** — rockchip U-Boot needs the external-data FIT + a `resource`
|
1. **boot.img format**: rockchip U-Boot needs the external-data FIT + a `resource`
|
||||||
(multi) sub-image with `rk-kernel.dtb`, else `No fit blob` / `Failed to load DTB`.
|
(multi) sub-image with `rk-kernel.dtb`, else `No fit blob` / `Failed to load DTB`.
|
||||||
2. **DTB overrun** — the bloated multi_v7 zImage decompresses to ~20 MiB and overran
|
2. **DTB overrun**: the bloated multi_v7 zImage decompresses to ~20 MiB and overran
|
||||||
the DTB at 0xc00000, so the kernel got `r2=0` (`invalid dtb`). Fix: place the fdt
|
the DTB at 0xc00000, so the kernel got `r2=0` (`invalid dtb`). Fix: place the fdt
|
||||||
high (`load=0x08000000`). A lean RV1106 defconfig would also fix this and is the
|
high (`load=0x08000000`). A lean RV1106 defconfig would also fix this and is the
|
||||||
right long-term move.
|
right long-term move.
|
||||||
3. **grf-cru NULL deref** — `clk-rv1106` registers a second branch set against a GRF
|
3. **grf-cru NULL deref**: `clk-rv1106` registers a second branch set against a GRF
|
||||||
clock-controller (`grf_ctx`), set only by a `rockchip,rv1106-grf-cru` node. The
|
clock-controller (`grf_ctx`), set only by a `rockchip,rv1106-grf-cru` node. The
|
||||||
minimal DT omitted it → `grf_ctx` NULL → panic in `rockchip_clk_register_branches`.
|
minimal DT omitted it -> `grf_ctx` NULL -> panic in `rockchip_clk_register_branches`.
|
||||||
Fix: add the `grf-clock-controller` child to the grf syscon.
|
Fix: add the `grf-clock-controller` child to the grf syscon.
|
||||||
|
|
||||||
**clk-rv1106 PORT-VERIFY (armclk mux, PLL rates) is now partially retired**: the CRU
|
**clk-rv1106 PORT-VERIFY (armclk mux, PLL rates) is now partially retired**: the CRU
|
||||||
comes up far enough to clock the UART and the arch timer on hardware. A wrong CPU
|
comes up far enough to clock the UART and the arch timer on hardware. A wrong CPU
|
||||||
mux/PLL would show later (cpufreq / peripheral rates), still to be checked.
|
mux/PLL would show later (cpufreq / peripheral rates), still to be checked.
|
||||||
|
|
||||||
## M3 — rootfs boot: DONE — the full WardenOS runs on the 6.18 kernel (2026-08-24)
|
## M3, rootfs boot: DONE: the full WardenOS runs on the 6.18 kernel (2026-08-24)
|
||||||
Adding the eMMC (`dw_mmc`) node to the DT was all M3 needed — the drivers are already
|
Adding the eMMC (`dw_mmc`) node to the DT was all M3 needed: the drivers are already
|
||||||
in the config. On hardware:
|
in the config. On hardware:
|
||||||
```
|
```
|
||||||
dwmmc_rockchip ffa90000.mmc: Version ID is 270a ← our dw_mmc bound
|
dwmmc_rockchip ffa90000.mmc: Version ID is 270a <- our dw_mmc bound
|
||||||
mmc0: new HS200 MMC card ... mmcblk0: 8GTF4R 7.28 GiB ← eMMC at HS200 (198 MHz)
|
mmc0: new HS200 MMC card ... mmcblk0: 8GTF4R 7.28 GiB <- eMMC at HS200 (198 MHz)
|
||||||
mmcblk0: p1(env)...p10(rootfs_b)...p12(userdata) ← partitions enumerated
|
mmcblk0: p1(env)...p10(rootfs_b)...p12(userdata) <- partitions enumerated
|
||||||
EXT4-fs (mmcblk0p10): mounted ... VFS: Mounted root ← ext4 rootfs mounted
|
EXT4-fs (mmcblk0p10): mounted ... VFS: Mounted root <- ext4 rootfs mounted
|
||||||
Run /sbin/init as init process
|
Run /sbin/init as init process
|
||||||
Starting warden-modbus / -mikrotik / -asic / -flared / Warden UI
|
Starting warden-modbus / -mikrotik / -asic / -flared / Warden UI
|
||||||
```
|
```
|
||||||
A root shell over the serial console confirms it: `uname -a` → **`Linux warden-c8a3
|
A root shell over the serial console confirms it: `uname -a` -> **`Linux warden-c8a3
|
||||||
6.18.46 armv7l`**, with `warden-flared`, `warden-modbus`, `warden-mikrotik`,
|
6.18.46 armv7l`**, with `warden-flared`, `warden-modbus`, `warden-mikrotik`,
|
||||||
`warden-asic`, `warden-ui`, `warden-flight` all in `ps`. The eMMC node's ciu-drive/
|
`warden-asic`, `warden-ui`, `warden-flight` all in `ps`. The eMMC node's ciu-drive/
|
||||||
ciu-sample clocks come from `grf_cru`, so the M2 grf-cru fix was a prerequisite.
|
ciu-sample clocks come from `grf_cru`, so the M2 grf-cru fix was a prerequisite.
|
||||||
|
|
||||||
The expected M4/M5 gaps show as clean failures: the out-of-tree 5.10 `aic8800`
|
The expected M4/M5 gaps show as clean failures: the out-of-tree 5.10 `aic8800`
|
||||||
wifi/BT `.ko` won't load on 6.18 (vermagic — needs the driver ported, **M5**), and
|
wifi/BT `.ko` won't load on 6.18 (vermagic, needs the driver ported, **M5**), and
|
||||||
`warden-ui: no backlight device` (no display/VOP node yet — **M4**).
|
`warden-ui: no backlight device` (no display/VOP node yet, **M4**).
|
||||||
|
|
||||||
## M4/M5/M6 — NEXT
|
## M4/M5/M6: NEXT
|
||||||
- **M4 display**: VOP2 + the RGB666 720×720 panel + GT911 touch + PWM backlight →
|
- **M4 display**: VOP2 + the RGB666 720x720 panel + GT911 touch + PWM backlight ->
|
||||||
the LVGL UI renders (the UI process already runs, it just has no framebuffer).
|
the LVGL UI renders (the UI process already runs, it just has no framebuffer).
|
||||||
- **M5 wifi**: port the AIC8800 SDIO driver to 6.18 (+ the Tier-1 fix) so wlan0/usb0
|
- **M5 wifi**: port the AIC8800 SDIO driver to 6.18 (+ the Tier-1 fix) so wlan0/usb0
|
||||||
come back; re-apply our 4 kernel patches.
|
come back; re-apply our 4 kernel patches.
|
||||||
@@ -137,16 +137,16 @@ wifi/BT `.ko` won't load on 6.18 (vermagic — needs the driver ported, **M5**),
|
|||||||
|
|
||||||
## Upstream tracking (decision 2026-08-23)
|
## Upstream tracking (decision 2026-08-23)
|
||||||
Base stays the vendor forward-port (applies to 6.18; we control it). The unmerged upstream
|
Base stays the vendor forward-port (applies to 6.18; we control it). The unmerged upstream
|
||||||
RV1106 series (~35 patches, "New" state, does NOT apply cleanly to 6.18 — pinctrl v3 failed at
|
RV1106 series (~35 patches, "New" state, does NOT apply cleanly to 6.18, pinctrl v3 failed at
|
||||||
`pinctrl-rockchip.c:3390`) is used as a **correctness oracle** for boot-critical PORT-VERIFY
|
`pinctrl-rockchip.c:3390`) is used as a **correctness oracle** for boot-critical PORT-VERIFY
|
||||||
values (already retired the iomux offsets) and tracked for eventual convergence when it merges.
|
values (already retired the iomux offsets) and tracked for eventual convergence when it merges.
|
||||||
|
|
||||||
## Honest scope
|
## Honest scope
|
||||||
This is the first driver of ~120 in the RV1106 BSP. The clk port alone is a multi-cycle
|
This is the first driver of ~120 in the RV1106 BSP. The clk port alone is a multi-cycle
|
||||||
effort of the four delta classes above; the whole bring-up (M1–M6 in `../docs/bringup.md`)
|
effort of the four delta classes above; the whole bring-up (M1-M6 in `../docs/bringup.md`)
|
||||||
is a multi-month kernel effort. Progress is real and the method is proven; correctness of
|
is a multi-month kernel effort. Progress is real and the method is proven; correctness of
|
||||||
boot-critical pieces (clock mux, PLL rates, DDR, pinctrl iomux) requires the TRM and
|
boot-critical pieces (clock mux, PLL rates, DDR, pinctrl iomux) requires the TRM and
|
||||||
on-hardware validation — which waits on c8a3's recovery and, ultimately, careful bring-up.
|
on-hardware validation: which waits on c8a3's recovery and, ultimately, careful bring-up.
|
||||||
|
|
||||||
## Layout
|
## Layout
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
# Driver provenance & openness ledger
|
# Driver provenance & openness ledger
|
||||||
|
|
||||||
Standing directive: **every** driver we run on the self-built 6.18 kernel must be
|
Standing directive: **every** driver we run on the self-built 6.18 kernel must be
|
||||||
open source we can read, harden, and bend — not a binary blob — and this applies
|
open source we can read, harden, and bend (not a binary blob) and this applies
|
||||||
to the drivers *already* ported, not just the new ones. This ledger records, for
|
to the drivers *already* ported, not just the new ones. This ledger records, for
|
||||||
each, where the source came from and under what license. Every entry is GPL-2.0
|
each, where the source came from and under what license. Every entry is GPL-2.0
|
||||||
kernel source; nothing here is a binary-only kernel module.
|
kernel source; nothing here is a binary-only kernel module.
|
||||||
|
|
||||||
## Legend
|
## Legend
|
||||||
- **vendor-src** — C source from the Rockchip 5.10 vendor SDK
|
- **vendor-src**: C source from the Rockchip 5.10 vendor SDK
|
||||||
(`flare-edge/sdk/sysdrv/source/kernel/`), forward-ported 5.10 → 6.18.
|
(`flare-edge/sdk/sysdrv/source/kernel/`), forward-ported 5.10 -> 6.18.
|
||||||
- **mainline-sibling** — 6.18 mainline driver, extended with an rv1106 data table
|
- **mainline-sibling**: 6.18 mainline driver, extended with an rv1106 data table
|
||||||
derived from a sibling SoC (rv1126/px30/rk3562) whose IP block matches.
|
derived from a sibling SoC (rv1126/px30/rk3562) whose IP block matches.
|
||||||
- All Rockchip vendor kernel code is a GPL-2.0 fork of mainline Linux → GPL-clean.
|
- All Rockchip vendor kernel code is a GPL-2.0 fork of mainline Linux -> GPL-clean.
|
||||||
|
|
||||||
## Already-ported drivers (audited for openness per directive)
|
## Already-ported drivers (audited for openness per directive)
|
||||||
| Driver | Origin | License | Openness notes |
|
| Driver | Origin | License | Openness notes |
|
||||||
@@ -30,18 +30,18 @@ kernel source; nothing here is a binary-only kernel module.
|
|||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| AIC8800 wifi/BT | vendor-src (AICSemi) | `MODULE_LICENSE("GPL")` | full full-MAC source; 50 files use GPL-only kernel/cfg80211/sdio symbols; **no** proprietary/redistribution restriction in-tree |
|
| AIC8800 wifi/BT | vendor-src (AICSemi) | `MODULE_LICENSE("GPL")` | full full-MAC source; 50 files use GPL-only kernel/cfg80211/sdio symbols; **no** proprietary/redistribution restriction in-tree |
|
||||||
|
|
||||||
## The one unavoidable blob — and why it is *not* kernel code
|
## The one unavoidable blob, and why it is *not* kernel code
|
||||||
- **`aic8800dc_fw`** (`/oem/usr/ko/`) is device firmware that the GPL host driver
|
- **`aic8800dc_fw`** (`/oem/usr/ko/`) is device firmware that the GPL host driver
|
||||||
*uploads to the wifi chip's on-die processor*. It never runs on the A7; it is not
|
*uploads to the wifi chip's on-die processor*. It never runs on the A7; it is not
|
||||||
linked into the kernel. This is the standard Linux firmware split (cf. every
|
linked into the kernel. This is the standard Linux firmware split (cf. every
|
||||||
ath10k/mt76 device) and is the only binary in the wifi path — the driver itself
|
ath10k/mt76 device) and is the only binary in the wifi path: the driver itself
|
||||||
is source. No open re-implementation of the AIC on-die MAC firmware exists;
|
is source. No open re-implementation of the AIC on-die MAC firmware exists;
|
||||||
reverse-engineering it is out of scope for the panel and buys nothing (the host
|
reverse-engineering it is out of scope for the panel and buys nothing (the host
|
||||||
driver is where our control + hardening lives).
|
driver is where our control + hardening lives).
|
||||||
|
|
||||||
## NPU — the openness line we will not cross
|
## NPU: the openness line we will not cross
|
||||||
The **kernel** rknpu driver is portable GPL source (`npu/PORT-PLAN.md`). What is
|
The **kernel** rknpu driver is portable GPL source (`npu/PORT-PLAN.md`). What is
|
||||||
closed is the *userspace* RKNN runtime + the regcmd stream format — a binary blob.
|
closed is the *userspace* RKNN runtime + the regcmd stream format, a binary blob.
|
||||||
Per directive we do **not** ship that blob; if NPU compute is ever wanted, the open
|
Per directive we do **not** ship that blob; if NPU compute is ever wanted, the open
|
||||||
path is a from-scratch/reverse-engineered regcmd encoder, tracked separately, never
|
path is a from-scratch/reverse-engineered regcmd encoder, tracked separately, never
|
||||||
a vendored binary runtime. (See `npu/PORT-PLAN.md` and the graphics investigation.)
|
a vendored binary runtime. (See `npu/PORT-PLAN.md` and the graphics investigation.)
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
# Remaining ports — concrete execution steps (scoped from source)
|
# Remaining ports: concrete execution steps (scoped from source)
|
||||||
|
|
||||||
Scoped against mainline 6.18 + vendor 5.10 source while the wifi transplant builds.
|
Scoped against mainline 6.18 + vendor 5.10 source while the wifi transplant builds.
|
||||||
Execute in this order once the tree is free (after wifi lands + verifies). Each:
|
Execute in this order once the tree is free (after wifi lands + verifies). Each:
|
||||||
port in tree → build → flash c8a3 _b → verify (self, no visual) → commit.
|
port in tree -> build -> flash c8a3 _b -> verify (self, no visual) -> commit.
|
||||||
|
|
||||||
## 1. TRNG (hardware RNG) — TRIVIAL (~2 lines), HIGH value
|
## 1. TRNG (hardware RNG): TRIVIAL (~2 lines), HIGH value
|
||||||
**Finding:** rv1106 `rockchip,trngv1` registers (CTRL 0x0000, STAT 0x0004, MODE
|
**Finding:** rv1106 `rockchip,trngv1` registers (CTRL 0x0000, STAT 0x0004, MODE
|
||||||
0x0008, ISTAT 0x0014, RAND0–7 0x0020–0x3C) are **byte-identical** to rk3588, which
|
0x0008, ISTAT 0x0014, RAND0-7 0x0020-0x3C) are **byte-identical** to rk3588, which
|
||||||
mainline `hw_random/rockchip-rng.c` already drives via `rk3588_rng_read` +
|
mainline `hw_random/rockchip-rng.c` already drives via `rk3588_rng_read` +
|
||||||
`rk3588_soc_data`.
|
`rk3588_soc_data`.
|
||||||
**Port:** add `{ .compatible = "rockchip,rv1106-rng", .data = &rk3588_soc_data }`
|
**Port:** add `{ .compatible = "rockchip,rv1106-rng", .data = &rk3588_soc_data }`
|
||||||
@@ -16,11 +16,11 @@ to the OF table; give the DT node `compatible = "rockchip,rv1106-rng",
|
|||||||
**Verify:** `/dev/hwrng` present; `dd if=/dev/hwrng bs=32 count=1 | xxd` non-zero;
|
**Verify:** `/dev/hwrng` present; `dd if=/dev/hwrng bs=32 count=1 | xxd` non-zero;
|
||||||
`cat /sys/class/misc/hw_random/rng_current` = rockchip.
|
`cat /sys/class/misc/hw_random/rng_current` = rockchip.
|
||||||
|
|
||||||
## 2. GMAC (wired ethernet) — MEDIUM, HIGH value
|
## 2. GMAC (wired ethernet): MEDIUM, HIGH value
|
||||||
**Finding:** node `ethernet@ffa80000` = `snps,dwmac-4.20a` + `rockchip,rv1106-gmac`,
|
**Finding:** node `ethernet@ffa80000` = `snps,dwmac-4.20a` + `rockchip,rv1106-gmac`,
|
||||||
**phy-mode rmii**, `phy-handle=&rmii_phy` (ethernet-phy@2). The **integrated EPHY
|
**phy-mode rmii**, `phy-handle=&rmii_phy` (ethernet-phy@2). The **integrated EPHY
|
||||||
(0x1234d400) is ALREADY in mainline** `net/phy/rockchip.c` → no PHY port needed.
|
(0x1234d400) is ALREADY in mainline** `net/phy/rockchip.c` -> no PHY port needed.
|
||||||
Bandgap trim `macphy_bgs` comes from OTP (soft dep — PHY works without it, slightly
|
Bandgap trim `macphy_bgs` comes from OTP (soft dep: PHY works without it, slightly
|
||||||
worse analog). Vendor glue lives in `dwmac-rk.c`; mainline is `dwmac-rockchip.c`.
|
worse analog). Vendor glue lives in `dwmac-rk.c`; mainline is `dwmac-rockchip.c`.
|
||||||
**Port:** add an rv1106 `struct rockchip_gmac_ops` to mainline `dwmac-rockchip.c`
|
**Port:** add an rv1106 `struct rockchip_gmac_ops` to mainline `dwmac-rockchip.c`
|
||||||
(GRF bits for RMII mode + `set_to_rmii`/`set_rmii_speed` from vendor `dwmac-rk.c`
|
(GRF bits for RMII mode + `set_to_rmii`/`set_rmii_speed` from vendor `dwmac-rk.c`
|
||||||
@@ -29,30 +29,30 @@ above). Kconfig: `STMMAC_ETH=y`, `DWMAC_ROCKCHIP=y`, `ROCKCHIP_PHY=y`.
|
|||||||
**Verify:** `ip link` shows the MAC; `ethtool eth?`; link-up on a patch cable if the
|
**Verify:** `ip link` shows the MAC; `ethtool eth?`; link-up on a patch cable if the
|
||||||
bench has one, else MDIO read of PHY ID via the driver's probe log.
|
bench has one, else MDIO read of PHY ID via the driver's probe log.
|
||||||
|
|
||||||
## 3. OTP / nvmem — SMALL, MEDIUM value (also feeds GMAC bandgap + chip-id)
|
## 3. OTP / nvmem: SMALL, MEDIUM value (also feeds GMAC bandgap + chip-id)
|
||||||
**Finding:** vendor `nvmem/rockchip-otp.c` has `rv1106_otp_clocks[]` + an rv1106
|
**Finding:** vendor `nvmem/rockchip-otp.c` has `rv1106_otp_clocks[]` + an rv1106
|
||||||
read path; mainline has px30/rk3308/rk3576/rk3588 but no rv1106.
|
read path; mainline has px30/rk3308/rk3576/rk3588 but no rv1106.
|
||||||
**Port:** add `rv1106_data` (reg_read + clocks/offset/size) from vendor to mainline
|
**Port:** add `rv1106_data` (reg_read + clocks/offset/size) from vendor to mainline
|
||||||
`rockchip-otp.c` + compatible `rockchip,rv1106-otp`. Kconfig `NVMEM_ROCKCHIP_OTP=y`.
|
`rockchip-otp.c` + compatible `rockchip,rv1106-otp`. Kconfig `NVMEM_ROCKCHIP_OTP=y`.
|
||||||
**Verify:** nvmem device in `/sys/bus/nvmem/devices/`; read chip-id cell.
|
**Verify:** nvmem device in `/sys/bus/nvmem/devices/`; read chip-id cell.
|
||||||
|
|
||||||
## 4. Audio (codec + DSM + card) — MEDIUM, MEDIUM value
|
## 4. Audio (codec + DSM + card): MEDIUM, MEDIUM value
|
||||||
i2s-tdm DAI already builds (rv1126 fallback). Port vendor `rv1106_codec.c` (acodec)
|
i2s-tdm DAI already builds (rv1126 fallback). Port vendor `rv1106_codec.c` (acodec)
|
||||||
+ `rk_dsm.c` (digital speaker) + a `simple-audio-card`/`rockchip,rv1106-codec` node.
|
+ `rk_dsm.c` (digital speaker) + a `simple-audio-card`/`rockchip,rv1106-codec` node.
|
||||||
**Verify:** `aplay -l` shows a card; `speaker-test` writes without error (audible
|
**Verify:** `aplay -l` shows a card; `speaker-test` writes without error (audible
|
||||||
check is tomorrow's on-panel item).
|
check is tomorrow's on-panel item).
|
||||||
|
|
||||||
## 5. crypto-v3 (accelerator) — LARGER, MEDIUM value
|
## 5. crypto-v3 (accelerator): LARGER, MEDIUM value
|
||||||
Vendor `crypto/rockchip/rk_crypto_v3*.c` (v3 core + ahash + skcipher); mainline has
|
Vendor `crypto/rockchip/rk_crypto_v3*.c` (v3 core + ahash + skcipher); mainline has
|
||||||
only v1 (rk3288). Port the v3 files + `rockchip,crypto-v3` compatible. Larger
|
only v1 (rk3288). Port the v3 files + `rockchip,crypto-v3` compatible. Larger
|
||||||
surface; do after the quick wins. **Verify:** `/proc/crypto` lists rk hw entries;
|
surface; do after the quick wins. **Verify:** `/proc/crypto` lists rk hw entries;
|
||||||
`tcrypt` or an openssl-engine smoke test.
|
`tcrypt` or an openssl-engine smoke test.
|
||||||
|
|
||||||
## 6. NPU kernel driver — MEDIUM, LOW-MED value (`npu/PORT-PLAN.md`)
|
## 6. NPU kernel driver: MEDIUM, LOW-MED value (`npu/PORT-PLAN.md`)
|
||||||
Kernel driver only; no open userspace regcmd encoder (openness line — no blob).
|
Kernel driver only; no open userspace regcmd encoder (openness line, no blob).
|
||||||
|
|
||||||
## 7. mailbox (HPMCU) — SMALL, MED value — proper coproc mbox vs /dev/mem hack.
|
## 7. mailbox (HPMCU): SMALL, MED value, proper coproc mbox vs /dev/mem hack.
|
||||||
## 8. pvtm — SMALL, LOW value — DVFS monitors; only if DVFS is pursued.
|
## 8. pvtm: SMALL, LOW value, DVFS monitors; only if DVFS is pursued.
|
||||||
|
|
||||||
Order rationale: TRNG (near-free, high value) → GMAC (high value, PHY already
|
Order rationale: TRNG (near-free, high value) -> GMAC (high value, PHY already
|
||||||
mainline) → OTP (small, feeds GMAC) → audio → crypto → NPU → mailbox → pvtm.
|
mainline) -> OTP (small, feeds GMAC) -> audio -> crypto -> NPU -> mailbox -> pvtm.
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
# SARADC — VERIFIED on warden-c8a3 (2026-08-25); the -22 was vref, not clk
|
# SARADC: VERIFIED on warden-c8a3 (2026-08-25); the -22 was vref, not clk
|
||||||
|
|
||||||
The rockchip_saradc probe failed `-22` NOT at clk_set_rate (no "failed to set
|
The rockchip_saradc probe failed `-22` NOT at clk_set_rate (no "failed to set
|
||||||
adc clk rate" ever printed) but at `regulator_get_voltage(info->vref)` — with no
|
adc clk rate" ever printed) but at `regulator_get_voltage(info->vref)`: with no
|
||||||
`vref-supply` in DT the driver got a **dummy** regulator, and
|
`vref-supply` in DT the driver got a **dummy** regulator, and
|
||||||
`regulator_get_voltage(dummy)` returns -EINVAL, which probe returns directly (so
|
`regulator_get_voltage(dummy)` returns -EINVAL, which probe returns directly (so
|
||||||
only the generic "probe failed with error -22" showed). The earlier
|
only the generic "probe failed with error -22" showed). The earlier
|
||||||
clk-rv1106-divider theory was wrong (the divider is HIWORD settable, xin24m is
|
clk-rv1106-divider theory was wrong (the divider is HIWORD settable, xin24m is
|
||||||
registered; clk_set_rate would clamp, not fail).
|
registered; clk_set_rate would clamp, not fail).
|
||||||
|
|
||||||
**Fix (DT only):** add the 1.8 V reference the vendor 86-panel uses —
|
**Fix (DT only):** add the 1.8 V reference the vendor 86-panel uses:
|
||||||
```dts
|
```dts
|
||||||
/ { vcc_1v8: vcc-1v8 { compatible = "regulator-fixed"; regulator-name = "vcc_1v8";
|
/ { vcc_1v8: vcc-1v8 { compatible = "regulator-fixed"; regulator-name = "vcc_1v8";
|
||||||
regulator-always-on; regulator-boot-on;
|
regulator-always-on; regulator-boot-on;
|
||||||
@@ -16,4 +16,4 @@ registered; clk_set_rate would clamp, not fail).
|
|||||||
&saradc { vref-supply = <&vcc_1v8>; status = "okay"; };
|
&saradc { vref-supply = <&vcc_1v8>; status = "okay"; };
|
||||||
```
|
```
|
||||||
**Evidence:** `iio:device0` (`ff3c0000.saradc`), `in_voltage0_raw=1023`,
|
**Evidence:** `iio:device0` (`ff3c0000.saradc`), `in_voltage0_raw=1023`,
|
||||||
`in_voltage1_raw=246` — both channels read real analog values (the adc-keys path).
|
`in_voltage1_raw=246`, both channels read real analog values (the adc-keys path).
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
Status: **zImage + rockchip/rv1106-warden.dtb build cleanly, 0 errors, 0 warnings**, with
|
Status: **zImage + rockchip/rv1106-warden.dtb build cleanly, 0 errors, 0 warnings**, with
|
||||||
`SND_SOC_RV1106=y` (acodec) and `SND_SOC_RK_DSM=y` (dsm) both built in, and the
|
`SND_SOC_RV1106=y` (acodec) and `SND_SOC_RK_DSM=y` (dsm) both built in, and the
|
||||||
`simple-audio-card` sound card wired in the board DT. Verified against `System.map` (host
|
`simple-audio-card` sound card wired in the board DT. Verified against `System.map` (host
|
||||||
`grep`, not cross-`nm` — see Verification below). **Not flashed or run on hardware** — this is
|
`grep`, not cross-`nm`: see Verification below). **Not flashed or run on hardware**: this is
|
||||||
a build-only port; on-target audio verification is explicitly deferred to the parent
|
a build-only port; on-target audio verification is explicitly deferred to the parent
|
||||||
session / bench.
|
session / bench.
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ Trees involved:
|
|||||||
speaker/headphone, tied together by a mainline `simple-audio-card`. The digital speaker
|
speaker/headphone, tied together by a mainline `simple-audio-card`. The digital speaker
|
||||||
modulator (`dsm`) was also ported and builds cleanly, but is left **disabled** in the board DT,
|
modulator (`dsm`) was also ported and builds cleanly, but is left **disabled** in the board DT,
|
||||||
matching the vendor 86-Panel board file (`rv1106-luckfox-pico-86panel-ipc.dtsi`), which also
|
matching the vendor 86-Panel board file (`rv1106-luckfox-pico-86panel-ipc.dtsi`), which also
|
||||||
ships `dsm` and `dsm_sound` disabled — the panel's actual audio path is acodec-only.
|
ships `dsm` and `dsm_sound` disabled: the panel's actual audio path is acodec-only.
|
||||||
|
|
||||||
## Files copied (verbatim from vendor, then patched in place)
|
## Files copied (verbatim from vendor, then patched in place)
|
||||||
|
|
||||||
@@ -25,10 +25,10 @@ From `sdk/sysdrv/source/kernel/sound/soc/codecs/` to the 6.18 tree's `sound/soc/
|
|||||||
|
|
||||||
| File | Lines | Notes |
|
| File | Lines | Notes |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `rv1106_codec.c` | 2317 | Analog codec (acodec) driver — the priority target |
|
| `rv1106_codec.c` | 2317 | Analog codec (acodec) driver: the priority target |
|
||||||
| `rv1106_codec.h` | — | Register/bitfield definitions, copied unmodified |
|
| `rv1106_codec.h` | n/a | Register/bitfield definitions, copied unmodified |
|
||||||
| `rk_dsm.c` | 653 | Digital speaker modulator (DSM) driver |
|
| `rk_dsm.c` | 653 | Digital speaker modulator (DSM) driver |
|
||||||
| `rk_dsm.h` | — | Register/bitfield definitions, copied unmodified |
|
| `rk_dsm.h` | n/a | Register/bitfield definitions, copied unmodified |
|
||||||
|
|
||||||
## Kconfig / Makefile wiring
|
## Kconfig / Makefile wiring
|
||||||
|
|
||||||
@@ -39,11 +39,11 @@ From `sdk/sysdrv/source/kernel/sound/soc/codecs/` to the 6.18 tree's `sound/soc/
|
|||||||
`select REGMAP_MMIO`) after `SND_SOC_RTQ9128`, before `SND_SOC_SDW_MOCKUP` (alphabetical
|
`select REGMAP_MMIO`) after `SND_SOC_RTQ9128`, before `SND_SOC_SDW_MOCKUP` (alphabetical
|
||||||
slot for "RV").
|
slot for "RV").
|
||||||
- Neither entry restricts to `ARM64` (unlike `SND_SOC_RK3308`'s existing
|
- Neither entry restricts to `ARM64` (unlike `SND_SOC_RK3308`'s existing
|
||||||
`depends on ARM64 || COMPILE_TEST` in this tree) — RV1106 is Cortex-A7 / ARM32-only, so an
|
`depends on ARM64 || COMPILE_TEST` in this tree): RV1106 is Cortex-A7 / ARM32-only, so an
|
||||||
ARM64 dependency would make the symbol unselectable on this board's `ARCH=arm` build.
|
ARM64 dependency would make the symbol unselectable on this board's `ARCH=arm` build.
|
||||||
|
|
||||||
`sound/soc/codecs/Makefile` (6.18 uses `snd-soc-<name>-y := <objs>.o` instead of the vendor's
|
`sound/soc/codecs/Makefile` (6.18 uses `snd-soc-<name>-y := <objs>.o` instead of the vendor's
|
||||||
`snd-soc-<name>-objs := <objs>.o` — same semantics, current tree's naming convention followed):
|
`snd-soc-<name>-objs := <objs>.o`, same semantics, current tree's naming convention followed):
|
||||||
- `snd-soc-rk-dsm-y := rk_dsm.o` + `obj-$(CONFIG_SND_SOC_RK_DSM) += snd-soc-rk-dsm.o`
|
- `snd-soc-rk-dsm-y := rk_dsm.o` + `obj-$(CONFIG_SND_SOC_RK_DSM) += snd-soc-rk-dsm.o`
|
||||||
- `snd-soc-rv1106-y := rv1106_codec.o` + `obj-$(CONFIG_SND_SOC_RV1106) += snd-soc-rv1106.o`
|
- `snd-soc-rv1106-y := rv1106_codec.o` + `obj-$(CONFIG_SND_SOC_RV1106) += snd-soc-rv1106.o`
|
||||||
|
|
||||||
@@ -60,12 +60,12 @@ CONFIG_SND_SOC_RV1106=y
|
|||||||
CONFIG_SND_SOC_RK_DSM=y
|
CONFIG_SND_SOC_RK_DSM=y
|
||||||
CONFIG_SND_SIMPLE_CARD=y
|
CONFIG_SND_SIMPLE_CARD=y
|
||||||
CONFIG_SND_SIMPLE_CARD_UTILS=y
|
CONFIG_SND_SIMPLE_CARD_UTILS=y
|
||||||
CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM=y (already y — DMA glue for the DAI)
|
CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM=y (already y: DMA glue for the DAI)
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: `SOUND`/`SND`/`SND_SOC` were `=m` in the pre-port `.config` (the whole ALSA/ASoC
|
Note: `SOUND`/`SND`/`SND_SOC` were `=m` in the pre-port `.config` (the whole ALSA/ASoC
|
||||||
subsystem was module-only). They had to be flipped to `=y` first — a tristate symbol can't be
|
subsystem was module-only). They had to be flipped to `=y` first: a tristate symbol can't be
|
||||||
`y` while its dependency chain is capped at `m` — otherwise `SND_SOC_RV1106=y` would silently
|
`y` while its dependency chain is capped at `m`; otherwise `SND_SOC_RV1106=y` would silently
|
||||||
clamp to `m` under `olddefconfig`, and `System.map` (built-in symbols only) would show nothing
|
clamp to `m` under `olddefconfig`, and `System.map` (built-in symbols only) would show nothing
|
||||||
even with `grep -c rv1106_codec` returning 0, which is exactly the silent-drop failure mode the
|
even with `grep -c rv1106_codec` returning 0, which is exactly the silent-drop failure mode the
|
||||||
task asked to guard against. `REGMAP_MMIO` and `MFD_SYSCON` were already `=y` (pulled in by
|
task asked to guard against. `REGMAP_MMIO` and `MFD_SYSCON` were already `=y` (pulled in by
|
||||||
@@ -80,14 +80,14 @@ actual delta surface was small. Six fixes total, all found by iterating single-o
|
|||||||
|
|
||||||
| # | File | Change |
|
| # | File | Change |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 1 | `rv1106_codec.c` | Dropped `#include <linux/rockchip/grf.h>` — vendor-BSP-only header, not present in mainline. Nothing in the file actually needs symbols from it: `PERI_GRF_PERI_CON1` is defined locally in the driver and the GRF is accessed generically via `syscon_regmap_lookup_by_phandle()` + `regmap_write()`. Documented in a comment at the top of the include block. |
|
| 1 | `rv1106_codec.c` | Dropped `#include <linux/rockchip/grf.h>`: vendor-BSP-only header, not present in mainline. Nothing in the file actually needs symbols from it: `PERI_GRF_PERI_CON1` is defined locally in the driver and the GRF is accessed generically via `syscon_regmap_lookup_by_phandle()` + `regmap_write()`. Documented in a comment at the top of the include block. |
|
||||||
| 2 | `rv1106_codec.c` | Replaced `#include <linux/of_gpio.h>` with `#include <linux/gpio/consumer.h>`. The driver calls `devm_gpiod_get_optional()` / `gpiod_direction_output()` (the `gpiod_*` consumer API), which live in `gpio/consumer.h`, not `of_gpio.h` (legacy integer-GPIO header) — this only happened to resolve on 5.10 via a transitive include chain that no longer holds in 6.18. |
|
| 2 | `rv1106_codec.c` | Replaced `#include <linux/of_gpio.h>` with `#include <linux/gpio/consumer.h>`. The driver calls `devm_gpiod_get_optional()` / `gpiod_direction_output()` (the `gpiod_*` consumer API), which live in `gpio/consumer.h`, not `of_gpio.h` (legacy integer-GPIO header): this only happened to resolve on 5.10 via a transitive include chain that no longer holds in 6.18. |
|
||||||
| 3 | `rv1106_codec.c` | Added `#include <linux/of_device.h>` — `of_match_device()` is declared there, not pulled in transitively by `of_platform.h` any more; without it the call was an implicit-function-declaration error (`-Werror`) plus a pointer-from-int warning on the assignment. |
|
| 3 | `rv1106_codec.c` | Added `#include <linux/of_device.h>`: `of_match_device()` is declared there, not pulled in transitively by `of_platform.h` any more; without it the call was an implicit-function-declaration error (`-Werror`) plus a pointer-from-int warning on the assignment. |
|
||||||
| 4 | `rv1106_codec.c` | `snd_soc_get_volsw_range()` / `snd_soc_put_volsw_range()` (used in `rv1106_codec_mic_gain_get/put` and `rv1106_codec_hpmix_gain_get/put`) no longer exist as separate mainline entry points — they were folded away when `SOC_SINGLE_RANGE`/`SOC_SINGLE_RANGE_TLV` were made simple aliases over `SOC_SINGLE_VALUE`. The unified `snd_soc_get_volsw()`/`snd_soc_put_volsw()` already honour the `xmin` field of `struct soc_mixer_control`, so they're a drop-in replacement; confirmed by checking how the affected controls are declared (`SOC_SINGLE_EXT_TLV`, min=0) — behavior is identical. 4 call sites fixed. |
|
| 4 | `rv1106_codec.c` | `snd_soc_get_volsw_range()` / `snd_soc_put_volsw_range()` (used in `rv1106_codec_mic_gain_get/put` and `rv1106_codec_hpmix_gain_get/put`) no longer exist as separate mainline entry points: they were folded away when `SOC_SINGLE_RANGE`/`SOC_SINGLE_RANGE_TLV` were made simple aliases over `SOC_SINGLE_VALUE`. The unified `snd_soc_get_volsw()`/`snd_soc_put_volsw()` already honour the `xmin` field of `struct soc_mixer_control`, so they're a drop-in replacement; confirmed by checking how the affected controls are declared (`SOC_SINGLE_EXT_TLV`, min=0): behavior is identical. 4 call sites fixed. |
|
||||||
| 5 | `rv1106_codec.c` | `SND_SOC_DAIFMT_CBS_CFS` -> `SND_SOC_DAIFMT_CBC_CFC`, `SND_SOC_DAIFMT_CBM_CFM` -> `SND_SOC_DAIFMT_CBP_CFP` in `rv1106_set_dai_fmt()`'s clock-role switch. Mainline renamed the old master/slave (`CBM`/`CBS`) constants to the consumer/provider naming (`CBC`/`CBP`) some releases back; `SND_SOC_DAIFMT_MASTER_MASK` itself still exists as a compat alias for `CLOCK_PROVIDER_MASK`, so only the two case labels needed renaming. Verified the semantic mapping against the branch bodies (codec-slave -> `IO_MODE_SLAVE`/`MODE_SLAVE` register bits = consumer = `CBC_CFC`; codec-master -> `..._MASTER` bits = provider = `CBP_CFP`). |
|
| 5 | `rv1106_codec.c` | `SND_SOC_DAIFMT_CBS_CFS` -> `SND_SOC_DAIFMT_CBC_CFC`, `SND_SOC_DAIFMT_CBM_CFM` -> `SND_SOC_DAIFMT_CBP_CFP` in `rv1106_set_dai_fmt()`'s clock-role switch. Mainline renamed the old master/slave (`CBM`/`CBS`) constants to the consumer/provider naming (`CBC`/`CBP`) some releases back; `SND_SOC_DAIFMT_MASTER_MASK` itself still exists as a compat alias for `CLOCK_PROVIDER_MASK`, so only the two case labels needed renaming. Verified the semantic mapping against the branch bodies (codec-slave -> `IO_MODE_SLAVE`/`MODE_SLAVE` register bits = consumer = `CBC_CFC`; codec-master -> `..._MASTER` bits = provider = `CBP_CFP`). |
|
||||||
| 6 | `rv1106_codec.c` + `rk_dsm.c` | `struct platform_driver.remove` is `void (*remove)(struct platform_device *)` in 6.18 (was `int (*remove)(struct platform_device *)`, transitional `.remove_new` before that). Changed `rv1106_platform_remove()` and `rk_dsm_platform_remove()` from `static int ... { ...; return 0; }` to `static void ...` (dropped the trailing `return 0;`). This was an `-Werror=incompatible-pointer-types` build error, not a warning. |
|
| 6 | `rv1106_codec.c` + `rk_dsm.c` | `struct platform_driver.remove` is `void (*remove)(struct platform_device *)` in 6.18 (was `int (*remove)(struct platform_device *)`, transitional `.remove_new` before that). Changed `rv1106_platform_remove()` and `rk_dsm_platform_remove()` from `static int ... { ...; return 0; }` to `static void ...` (dropped the trailing `return 0;`). This was an `-Werror=incompatible-pointer-types` build error, not a warning. |
|
||||||
|
|
||||||
That's the complete fix list — nothing else needed touching. No vendor-only headers other than
|
That's the complete fix list: nothing else needed touching. No vendor-only headers other than
|
||||||
`rockchip/grf.h` were pulled in; `linux/mfd/syscon.h`, `linux/reset.h`, `linux/clk.h`,
|
`rockchip/grf.h` were pulled in; `linux/mfd/syscon.h`, `linux/reset.h`, `linux/clk.h`,
|
||||||
`linux/regulator/consumer.h`, `sound/pcm_params.h`, `sound/soc.h`, `sound/tlv.h`,
|
`linux/regulator/consumer.h`, `sound/pcm_params.h`, `sound/soc.h`, `sound/tlv.h`,
|
||||||
`sound/dmaengine_pcm.h` all exist unchanged in mainline and needed no stubbing.
|
`sound/dmaengine_pcm.h` all exist unchanged in mainline and needed no stubbing.
|
||||||
@@ -95,13 +95,13 @@ That's the complete fix list — nothing else needed touching. No vendor-only he
|
|||||||
## rk_dsm (digital speaker modulator) decision
|
## rk_dsm (digital speaker modulator) decision
|
||||||
|
|
||||||
**Kept in, not dropped.** It only needed fix #6 above (the same `platform_driver.remove` void
|
**Kept in, not dropped.** It only needed fix #6 above (the same `platform_driver.remove` void
|
||||||
signature change as the acodec) — a one-line-class fix, well under the "drop if it fights the
|
signature change as the acodec), a one-line-class fix, well under the "drop if it fights the
|
||||||
API a lot" threshold in the task brief. `SND_SOC_RK_DSM=y` is set and `rk_dsm.c` links cleanly
|
API a lot" threshold in the task brief. `SND_SOC_RK_DSM=y` is set and `rk_dsm.c` links cleanly
|
||||||
into `zImage` (17 `rk_dsm`-prefixed symbols in `System.map`, including
|
into `zImage` (17 `rk_dsm`-prefixed symbols in `System.map`, including
|
||||||
`rk_dsm_platform_remove`, `rk_dsm_hw_params`, `rk_dsm_set_dai_fmt`).
|
`rk_dsm_platform_remove`, `rk_dsm_hw_params`, `rk_dsm_set_dai_fmt`).
|
||||||
|
|
||||||
However, the DSM node (`dsm: codec-digital@ff340000`) and its companion `dsm_sound` machine
|
However, the DSM node (`dsm: codec-digital@ff340000`) and its companion `dsm_sound` machine
|
||||||
card are **not wired up** in `rv1106-warden.dts` — `&dsm { status = "disabled"; };` is set
|
card are **not wired up** in `rv1106-warden.dts`: `&dsm { status = "disabled"; };` is set
|
||||||
explicitly, matching the vendor `rv1106-luckfox-pico-86panel-ipc.dtsi`, which itself ships `dsm`
|
explicitly, matching the vendor `rv1106-luckfox-pico-86panel-ipc.dtsi`, which itself ships `dsm`
|
||||||
disabled and `dsm_sound` as `status = "disabled"`. The 86-Panel's speaker/headphone path is the
|
disabled and `dsm_sound` as `status = "disabled"`. The 86-Panel's speaker/headphone path is the
|
||||||
acodec, not the DSM Class-D path; DSM is present on other RV1106 boards (bare speaker modules)
|
acodec, not the DSM Class-D path; DSM is present on other RV1106 boards (bare speaker modules)
|
||||||
@@ -111,7 +111,7 @@ turn it on) but is not part of this board's active audio graph.
|
|||||||
## Device tree changes (`arch/arm/boot/dts/rockchip/rv1106-warden.dts`)
|
## Device tree changes (`arch/arm/boot/dts/rockchip/rv1106-warden.dts`)
|
||||||
|
|
||||||
`i2s0_8ch` was already `status = "okay"` pre-port (from an earlier "sweep" commit) and already
|
`i2s0_8ch` was already `status = "okay"` pre-port (from an earlier "sweep" commit) and already
|
||||||
carries `#sound-dai-cells = <0>` in the base `rv1106.dtsi` node — no change needed there.
|
carries `#sound-dai-cells = <0>` in the base `rv1106.dtsi` node: no change needed there.
|
||||||
|
|
||||||
The base `rv1106.dtsi` `acodec: acodec@ff480000` node (mirrored from the vendor's own
|
The base `rv1106.dtsi` `acodec: acodec@ff480000` node (mirrored from the vendor's own
|
||||||
`arch/arm/boot/dts/rv1106.dtsi`, which has the same gap) does **not** carry
|
`arch/arm/boot/dts/rv1106.dtsi`, which has the same gap) does **not** carry
|
||||||
@@ -149,7 +149,7 @@ Verified against the decompiled built `.dtb` (`dtc -I dtb -O dts`):
|
|||||||
- `acodec@ff480000`: `status = "okay"`, `#sound-dai-cells = <0x00>`, `phandle = <0x5a>`
|
- `acodec@ff480000`: `status = "okay"`, `#sound-dai-cells = <0x00>`, `phandle = <0x5a>`
|
||||||
- `i2s@ffae0000` (i2s0_8ch): `status = "okay"`, `#sound-dai-cells = <0x00>`, `phandle = <0x59>`
|
- `i2s@ffae0000` (i2s0_8ch): `status = "okay"`, `#sound-dai-cells = <0x00>`, `phandle = <0x59>`
|
||||||
- `codec-digital@ff340000` (dsm): `status = "disabled"`
|
- `codec-digital@ff340000` (dsm): `status = "disabled"`
|
||||||
- `acodec-sound` node present with `sound-dai = <0x59>` (cpu) / `sound-dai = <0x5a>` (codec) —
|
- `acodec-sound` node present with `sound-dai = <0x59>` (cpu) / `sound-dai = <0x5a>` (codec):
|
||||||
phandles correctly resolve to the i2s0_8ch/acodec nodes above.
|
phandles correctly resolve to the i2s0_8ch/acodec nodes above.
|
||||||
|
|
||||||
## Build status
|
## Build status
|
||||||
@@ -168,10 +168,10 @@ both the touched-file single-object builds and the final combined `zImage`+dtb b
|
|||||||
nothing). `arch/arm/boot/zImage` and `arch/arm/boot/dts/rockchip/rv1106-warden.dtb` both
|
nothing). `arch/arm/boot/zImage` and `arch/arm/boot/dts/rockchip/rv1106-warden.dtb` both
|
||||||
produced. XZ compression and the existing console/earlycon config were left untouched.
|
produced. XZ compression and the existing console/earlycon config were left untouched.
|
||||||
|
|
||||||
## Verification (build-only — see hardware watch-list below for what's deferred)
|
## Verification (build-only, see hardware watch-list below for what's deferred)
|
||||||
|
|
||||||
Per the task instructions, `System.map` (produced by the **host** toolchain's `nm`, ground
|
Per the task instructions, `System.map` (produced by the **host** toolchain's `nm`, ground
|
||||||
truth for built-in linkage — not the cross-`nm`, which mis-lists symbols) was checked directly
|
truth for built-in linkage, not the cross-`nm`, which mis-lists symbols) was checked directly
|
||||||
with `grep`:
|
with `grep`:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -184,23 +184,23 @@ $ grep -c soc_codec_dev_rv1106 System.map
|
|||||||
```
|
```
|
||||||
|
|
||||||
54 `rv1106_codec`-prefixed symbols and 17 `rk_dsm`-prefixed symbols are linked into the kernel
|
54 `rv1106_codec`-prefixed symbols and 17 `rk_dsm`-prefixed symbols are linked into the kernel
|
||||||
image — both drivers are genuinely built in (`=y`), not silently dropped to module or excluded.
|
image: both drivers are genuinely built in (`=y`), not silently dropped to module or excluded.
|
||||||
`soc_codec_dev_rv1106` (the `snd_soc_component_driver` struct) is present, confirming the
|
`soc_codec_dev_rv1106` (the `snd_soc_component_driver` struct) is present, confirming the
|
||||||
component registration path is compiled in.
|
component registration path is compiled in.
|
||||||
|
|
||||||
## Hardware-verify watch-list (deferred to the parent session / bench — NOT done here)
|
## Hardware-verify watch-list (deferred to the parent session / bench, NOT done here)
|
||||||
|
|
||||||
This session did not flash or touch any hardware, per the task boundary. When the parent
|
This session did not flash or touch any hardware, per the task boundary. When the parent
|
||||||
session verifies on a real 86-Panel:
|
session verifies on a real 86-Panel:
|
||||||
|
|
||||||
- `aplay -l` should list a card named **`rv1106-acodec`** (from
|
- `aplay -l` should list a card named **`rv1106-acodec`** (from
|
||||||
`simple-audio-card,name = "rv1106-acodec"` in the DT) — this is the ALSA card name to look
|
`simple-audio-card,name = "rv1106-acodec"` in the DT): this is the ALSA card name to look
|
||||||
for, not a device path.
|
for, not a device path.
|
||||||
- Expect one playback + one capture PCM stream under that card (`rv1106-hifi` DAI: playback 1-2ch,
|
- Expect one playback + one capture PCM stream under that card (`rv1106-hifi` DAI: playback 1-2ch,
|
||||||
capture 1-4ch, 8kHz-192kHz, S16_LE/S20_3LE/S24_LE/S32_LE).
|
capture 1-4ch, 8kHz-192kHz, S16_LE/S20_3LE/S24_LE/S32_LE).
|
||||||
- `amixer -c <card> controls` should surface the vendor mixer controls carried over unchanged
|
- `amixer -c <card> controls` should surface the vendor mixer controls carried over unchanged
|
||||||
from the BSP driver (ADC MIC Left/Right Gain, ADC ALC Left/Right Volume, HPF cutoff, DAC/lineout
|
from the BSP driver (ADC MIC Left/Right Gain, ADC ALC Left/Right Volume, HPF cutoff, DAC/lineout
|
||||||
gain, mic bias, etc.) — these are pure regmap register pokes, unaffected by this port's API-shim
|
gain, mic bias, etc.): these are pure regmap register pokes, unaffected by this port's API-shim
|
||||||
fixes, so if the card enumerates at all they should already work correctly.
|
fixes, so if the card enumerates at all they should already work correctly.
|
||||||
- Watch dmesg for probe-order issues: `acodec` depends on `PCLK_ACODEC`, `MCLK_ACODEC_TX`,
|
- Watch dmesg for probe-order issues: `acodec` depends on `PCLK_ACODEC`, `MCLK_ACODEC_TX`,
|
||||||
`MCLK_I2S0_8CH_TX` clocks and `SRST_P_ACODEC` reset all resolving before probe; a probe
|
`MCLK_I2S0_8CH_TX` clocks and `SRST_P_ACODEC` reset all resolving before probe; a probe
|
||||||
@@ -211,20 +211,20 @@ session verifies on a real 86-Panel:
|
|||||||
"ASoC: ... error getting cpu/codec dai info" at parse time) before suspecting the codec driver
|
"ASoC: ... error getting cpu/codec dai info" at parse time) before suspecting the codec driver
|
||||||
itself, since the DT wiring is the newest/least-proven part of this port.
|
itself, since the DT wiring is the newest/least-proven part of this port.
|
||||||
- The actual audible test (does sound come out of the speaker/headphone jack) is explicitly
|
- The actual audible test (does sound come out of the speaker/headphone jack) is explicitly
|
||||||
**deferred to the bench** — this session only proves the driver builds, links, and the DT
|
**deferred to the bench**: this session only proves the driver builds, links, and the DT
|
||||||
graph resolves; it does not and cannot prove the analog output path works on real silicon.
|
graph resolves; it does not and cannot prove the analog output path works on real silicon.
|
||||||
|
|
||||||
---
|
---
|
||||||
## Parent-session verify + fix (2026-08-25)
|
## Parent-session verify + fix (2026-08-25)
|
||||||
**Gap found:** the subagent set `SND_SOC_RV1106=y` + `SND_SIMPLE_CARD=y` but left
|
**Gap found:** the subagent set `SND_SOC_RV1106=y` + `SND_SIMPLE_CARD=y` but left
|
||||||
the **cpu DAI** `CONFIG_SND_SOC_ROCKCHIP_I2S_TDM=m` (module). On the bare-kernel
|
the **cpu DAI** `CONFIG_SND_SOC_ROCKCHIP_I2S_TDM=m` (module). On the bare-kernel
|
||||||
_b boot no modules load, so i2s0_8ch never registered a DAI → the simple-audio-card
|
_b boot no modules load, so i2s0_8ch never registered a DAI -> the simple-audio-card
|
||||||
stuck in `deferred probe pending: asoc-simple-card: parse error`, no card.
|
stuck in `deferred probe pending: asoc-simple-card: parse error`, no card.
|
||||||
**Fix:** `CONFIG_SND_SOC_ROCKCHIP=y` + `CONFIG_SND_SOC_ROCKCHIP_I2S_TDM=y` (the
|
**Fix:** `CONFIG_SND_SOC_ROCKCHIP=y` + `CONFIG_SND_SOC_ROCKCHIP_I2S_TDM=y` (the
|
||||||
i2s0_8ch node already carries the `rockchip,rv1126-i2s-tdm` fallback compatible +
|
i2s0_8ch node already carries the `rockchip,rv1126-i2s-tdm` fallback compatible +
|
||||||
`#sound-dai-cells=<0>`, so the mainline driver binds it).
|
`#sound-dai-cells=<0>`, so the mainline driver binds it).
|
||||||
|
|
||||||
**VERIFIED on c8a3:** `/proc/asound/cards` → `0 [rv1106acodec]: simple-card -
|
**VERIFIED on c8a3:** `/proc/asound/cards` -> `0 [rv1106acodec]: simple-card -
|
||||||
rv1106-acodec`; `aplay -l` → `card 0: rv1106acodec, device 0:
|
rv1106-acodec`; `aplay -l` -> `card 0: rv1106acodec, device 0:
|
||||||
ffae0000.i2s-rv1106-hifi`; `/dev/snd/` has `controlC0 pcmC0D0p pcmC0D0c` (playback
|
ffae0000.i2s-rv1106-hifi`; `/dev/snd/` has `controlC0 pcmC0D0p pcmC0D0c` (playback
|
||||||
+ capture). Audible speaker test deferred to the bench (with the display).
|
+ capture). Audible speaker test deferred to the bench (with the display).
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# build-m2.sh — reproducible M2 (earlycon) build of the RV1106 → 6.18 forward-port.
|
# build-m2.sh: reproducible M2 (earlycon) build of the RV1106 -> 6.18 forward-port.
|
||||||
#
|
#
|
||||||
# Produces: a zImage with our ported SoC drivers, and rv1106-warden-m2.dtb.
|
# Produces: a zImage with our ported SoC drivers, and rv1106-warden-m2.dtb.
|
||||||
# Boot is a separate on-hardware step (see PORT-STATUS.md "M2 boot").
|
# Boot is a separate on-hardware step (see PORT-STATUS.md "M2 boot").
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# M2 earlycon config fragment for the RV1106 → Linux 6.18 forward-port.
|
# M2 earlycon config fragment for the RV1106 -> Linux 6.18 forward-port.
|
||||||
#
|
#
|
||||||
# Base: multi_v7_defconfig (PORT-STATUS confirms it configures and CLK_RV1106
|
# Base: multi_v7_defconfig (PORT-STATUS confirms it configures and CLK_RV1106
|
||||||
# wires in cleanly). Apply this fragment on top, then `make olddefconfig`:
|
# wires in cleanly). Apply this fragment on top, then `make olddefconfig`:
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
# make ARCH=arm CROSS_COMPILE=... olddefconfig zImage
|
# make ARCH=arm CROSS_COMPILE=... olddefconfig zImage
|
||||||
#
|
#
|
||||||
# This builds an 11.8 MB zImage with our ported SoC drivers compiled in
|
# This builds an 11.8 MB zImage with our ported SoC drivers compiled in
|
||||||
# (clk-rv1106.o, pinctrl-rockchip.o with the rv1106 data) — verified 2026-08-24.
|
# (clk-rv1106.o, pinctrl-rockchip.o with the rv1106 data), verified 2026-08-24.
|
||||||
|
|
||||||
# --- our ported RV1106 SoC drivers ---
|
# --- our ported RV1106 SoC drivers ---
|
||||||
CONFIG_ARCH_ROCKCHIP=y
|
CONFIG_ARCH_ROCKCHIP=y
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
# M4 — display (VOP) port to 6.18
|
# M4: display (VOP) port to 6.18
|
||||||
|
|
||||||
The RV1106 VOP is the older RV-series "lite" VOP (`rockchip,rv1106-vop`, VOP_VERSION
|
The RV1106 VOP is the older RV-series "lite" VOP (`rockchip,rv1106-vop`, VOP_VERSION
|
||||||
2.0xc), driven by mainline's `rockchip_drm_vop.c` (VOP1), **not** VOP2. Its sibling
|
2.0xc), driven by mainline's `rockchip_drm_vop.c` (VOP1), **not** VOP2. Its sibling
|
||||||
**rv1126** is already in mainline 6.18, so the register data is a small reuse — like
|
**rv1126** is already in mainline 6.18, so the register data is a small reuse, like
|
||||||
the clk/pinctrl ports.
|
the clk/pinctrl ports.
|
||||||
|
|
||||||
## The driver delta (`rockchip_vop_reg.c`)
|
## The driver delta (`rockchip_vop_reg.c`)
|
||||||
|
|
||||||
`rv1106_vop_data.c.frag` — add this `vop_data` (reuses rv1126's common/modeset/
|
`rv1106_vop_data.c.frag`: add this `vop_data` (reuses rv1126's common/modeset/
|
||||||
output/misc/intr/win sub-structs; only the version, the smaller 1280² raster, and
|
output/misc/intr/win sub-structs; only the version, the smaller 1280² raster, and
|
||||||
the **`VOP_FEATURE_INTERNAL_RGB` feature** differ), plus the match entry:
|
the **`VOP_FEATURE_INTERNAL_RGB` feature** differ), plus the match entry:
|
||||||
|
|
||||||
@@ -15,18 +15,18 @@ the **`VOP_FEATURE_INTERNAL_RGB` feature** differ), plus the match entry:
|
|||||||
{ .compatible = "rockchip,rv1106-vop", .data = &rv1106_vop },
|
{ .compatible = "rockchip,rv1106-vop", .data = &rv1106_vop },
|
||||||
```
|
```
|
||||||
|
|
||||||
`VOP_FEATURE_INTERNAL_RGB` is essential — it is what makes `vop_bind` call
|
`VOP_FEATURE_INTERNAL_RGB` is essential: it is what makes `vop_bind` call
|
||||||
`rockchip_rgb_init()` for the parallel-RGB output (rv1126 routes through MIPI and
|
`rockchip_rgb_init()` for the parallel-RGB output (rv1126 routes through MIPI and
|
||||||
omits it).
|
omits it).
|
||||||
|
|
||||||
## The DT (in `../dts/rv1106-warden.dts`)
|
## The DT (in `../dts/rv1106-warden.dts`)
|
||||||
|
|
||||||
- `&vop` enabled + **named resets** `resets = <&cru SRST_H_VOP>, <&cru SRST_D_VOP>;
|
- `&vop` enabled + **named resets** `resets = <&cru SRST_H_VOP>, <&cru SRST_D_VOP>;
|
||||||
reset-names = "ahb", "dclk";` — the mainline driver requires these (the vendor
|
reset-names = "ahb", "dclk";`, the mainline driver requires these (the vendor
|
||||||
node omits them; without them: `vop_bind: failed to get ahb reset`).
|
node omits them; without them: `vop_bind: failed to get ahb reset`).
|
||||||
- `&vop_out_rgb` repointed straight to the panel (mainline has no separate rgb
|
- `&vop_out_rgb` repointed straight to the panel (mainline has no separate rgb
|
||||||
node — `rockchip_rgb_init` is a helper the VOP calls).
|
node: `rockchip_rgb_init` is a helper the VOP calls).
|
||||||
- `panel` (`panel-dpi`, 720×720, 30 MHz) + `pwm-backlight` on `&pwm1`.
|
- `panel` (`panel-dpi`, 720x720, 30 MHz) + `pwm-backlight` on `&pwm1`.
|
||||||
|
|
||||||
## Verified on warden-c8a3 (2026-08-24)
|
## Verified on warden-c8a3 (2026-08-24)
|
||||||
|
|
||||||
@@ -36,14 +36,14 @@ rockchip-drm display-subsystem: bound ff990000.vop
|
|||||||
[drm] Initialized rockchip 1.0.0 for display-subsystem on minor 0
|
[drm] Initialized rockchip 1.0.0 for display-subsystem on minor 0
|
||||||
```
|
```
|
||||||
`/dev/dri/card0` is present and the **PWM backlight is up** (`/sys/class/backlight`,
|
`/dev/dri/card0` is present and the **PWM backlight is up** (`/sys/class/backlight`,
|
||||||
brightness settable). **The VOP driver port is validated** — the register data,
|
brightness settable). **The VOP driver port is validated**: the register data,
|
||||||
version, feature, and resets are right.
|
version, feature, and resets are right.
|
||||||
|
|
||||||
## RESOLVED — full UI renders on the panel (2026-08-25)
|
## RESOLVED: full UI renders on the panel (2026-08-25)
|
||||||
|
|
||||||
The connector *and* the deeper black-screen chain that followed it are fixed; the
|
The connector *and* the deeper black-screen chain that followed it are fixed; the
|
||||||
86-Panel now draws the full WardenOS Dashboard on 6.18 (`_b`), verified by webcam.
|
86-Panel now draws the full WardenOS Dashboard on 6.18 (`_b`), verified by webcam.
|
||||||
The two final root causes were VOP driver bugs — `rgb_dclk_pol` hardcoded inverted,
|
The two final root causes were VOP driver bugs: `rgb_dclk_pol` hardcoded inverted,
|
||||||
and the wrong primary scanout window (rv1106 scans out via **WIN1**, not WIN2).
|
and the wrong primary scanout window (rv1106 scans out via **WIN1**, not WIN2).
|
||||||
See **`VERIFIED.md`** for the complete bring-up chain, the `_a`-vs-`_b` register
|
See **`VERIFIED.md`** for the complete bring-up chain, the `_a`-vs-`_b` register
|
||||||
diff that pinned it down, and every file changed.
|
diff that pinned it down, and every file changed.
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# Display (VOP + RGB panel) — VERIFIED on warden-c8a3 (2026-08-25)
|
# Display (VOP + RGB panel): VERIFIED on warden-c8a3 (2026-08-25)
|
||||||
|
|
||||||
The 86-Panel renders the **full WardenOS Dashboard UI** on our self-built Linux
|
The 86-Panel renders the **full WardenOS Dashboard UI** on our self-built Linux
|
||||||
6.18.46 (`_b` slot), pixel-identical to the stock 5.10 `_a` slot. Verified by
|
6.18.46 (`_b` slot), pixel-identical to the stock 5.10 `_a` slot. Verified by
|
||||||
webcam pointed at the physical panel (pixels can't be checked over serial/ssh) —
|
webcam pointed at the physical panel (pixels can't be checked over serial/ssh);
|
||||||
`warden-ui -b DRM` drawing the Dashboard: nav sidebar, "Wellhead 03" site card,
|
`warden-ui -b DRM` drawing the Dashboard: nav sidebar, "Wellhead 03" site card,
|
||||||
Mesh/WAN/LAN/RS485 status rows, correct colors, no banding, no colour swap.
|
Mesh/WAN/LAN/RS485 status rows, correct colors, no banding, no colour swap.
|
||||||
|
|
||||||
@@ -13,10 +13,10 @@ black-screen chain that followed it).
|
|||||||
|
|
||||||
1. **Connector never created** (`Cannot find any crtc`): `CONFIG_ROCKCHIP_RGB`
|
1. **Connector never created** (`Cannot find any crtc`): `CONFIG_ROCKCHIP_RGB`
|
||||||
was not set, AND the vendor `&rgb` bridge node's dangling endpoint
|
was not set, AND the vendor `&rgb` bridge node's dangling endpoint
|
||||||
(`rgb_in_vop -> vop_out_rgb`) read to fw_devlink as a vop↔panel dependency
|
(`rgb_in_vop -> vop_out_rgb`) read to fw_devlink as a vop<->panel dependency
|
||||||
cycle, so the VOP probed before the panel. Fix: enable `ROCKCHIP_RGB` +
|
cycle, so the VOP probed before the panel. Fix: enable `ROCKCHIP_RGB` +
|
||||||
`/delete-node/ &rgb;` so `vop_out_rgb <-> panel_in_vop` is the only graph link
|
`/delete-node/ &rgb;` so `vop_out_rgb <-> panel_in_vop` is the only graph link
|
||||||
(rockchip_rgb_init then defers + retries and finds the panel → `LVDS-1`).
|
(rockchip_rgb_init then defers + retries and finds the panel -> `LVDS-1`).
|
||||||
2. **Backlight dark**: `&pwm1` had `pinctrl-names = "active"`; mainline
|
2. **Backlight dark**: `&pwm1` had `pinctrl-names = "active"`; mainline
|
||||||
pwm-rockchip relies on the driver core auto-applying the **`"default"`** state,
|
pwm-rockchip relies on the driver core auto-applying the **`"default"`** state,
|
||||||
so the PWM pin was never muxed. Fix: rename to `"default"`.
|
so the PWM pin was never muxed. Fix: rename to `"default"`.
|
||||||
@@ -37,22 +37,22 @@ black-screen chain that followed it).
|
|||||||
## The two root causes of the final "backlit-black" (VOP driver bugs)
|
## The two root causes of the final "backlit-black" (VOP driver bugs)
|
||||||
|
|
||||||
With connector + backlight + pins + panel-init all correct, the screen was still
|
With connector + backlight + pins + panel-init all correct, the screen was still
|
||||||
physically black (backlight on, `LVDS-1` connected, VOP streaming, fb0 720×720,
|
physically black (backlight on, `LVDS-1` connected, VOP streaming, fb0 720x720,
|
||||||
`modetest`/splash written) — the classic "everything healthy, glass dark" state.
|
`modetest`/splash written): the classic "everything healthy, glass dark" state.
|
||||||
Found by dumping the VOP register block `0xff990000` on the **working `_a` slot**
|
Found by dumping the VOP register block `0xff990000` on the **working `_a` slot**
|
||||||
and diffing against `_b` (same SoC, same register map):
|
and diffing against `_b` (same SoC, same register map):
|
||||||
|
|
||||||
**Bug 1 — DCLK polarity inverted.** `rockchip_drm_vop.c` hardcodes
|
**Bug 1: DCLK polarity inverted.** `rockchip_drm_vop.c` hardcodes
|
||||||
`rgb_dclk_pol = 1` for the LVDS/RGB output. The panel latches pixel data on the
|
`rgb_dclk_pol = 1` for the LVDS/RGB output. The panel latches pixel data on the
|
||||||
**non-inverted** edge — `_a` reads `PX30_DSP_CTRL0` (0xff990020) = `0x1`
|
**non-inverted** edge: `_a` reads `PX30_DSP_CTRL0` (0xff990020) = `0x1`
|
||||||
(rgb_dclk_pol bit1 = **0**). The vendor 5.10 derives it as
|
(rgb_dclk_pol bit1 = **0**). The vendor 5.10 derives it as
|
||||||
`(bus_flags & PIXDATA_DRIVE_NEGEDGE) ? 1 : 0`, which is 0 for this panel. With the
|
`(bus_flags & PIXDATA_DRIVE_NEGEDGE) ? 1 : 0`, which is 0 for this panel. With the
|
||||||
inverted clock the panel samples RGB on the wrong edge → black. **Fix:** set
|
inverted clock the panel samples RGB on the wrong edge -> black. **Fix:** set
|
||||||
`rgb_dclk_pol` to 0 in the `DRM_MODE_CONNECTOR_LVDS` case.
|
`rgb_dclk_pol` to 0 in the `DRM_MODE_CONNECTOR_LVDS` case.
|
||||||
|
|
||||||
**Bug 2 — wrong primary scanout window.** The rv1106 VOP scans out through
|
**Bug 2: wrong primary scanout window.** The rv1106 VOP scans out through
|
||||||
**WIN1**, but our port reused rv1126's win table (`win0`-overlay + **`win2`**-primary).
|
**WIN1**, but our port reused rv1126's win table (`win0`-overlay + **`win2`**-primary).
|
||||||
The vendor `rv1106_vop_win_data` is `{ NULL-win0, rk3366_lit_win1_data-primary }` —
|
The vendor `rv1106_vop_win_data` is `{ NULL-win0, rk3366_lit_win1_data-primary }`,
|
||||||
i.e. WIN1 (`PX30_WIN1_*` at 0x090). Mainline was configuring WIN2, which never
|
i.e. WIN1 (`PX30_WIN1_*` at 0x090). Mainline was configuring WIN2, which never
|
||||||
reaches this SoC's RGB interface, so WIN1 stayed all-zero and nothing scanned out.
|
reaches this SoC's RGB interface, so WIN1 stayed all-zero and nothing scanned out.
|
||||||
Register evidence (`_a` working vs `_b` broken, before the fix):
|
Register evidence (`_a` working vs `_b` broken, before the fix):
|
||||||
@@ -62,26 +62,26 @@ Register evidence (`_a` working vs `_b` broken, before the fix):
|
|||||||
| 0x090 | WIN1_CTRL0 (enable) | `0x00000001` | `0x00000000` |
|
| 0x090 | WIN1_CTRL0 (enable) | `0x00000001` | `0x00000000` |
|
||||||
| 0x098 | WIN1_VIR (stride) | `0x000002D0` (720) | `0x00000000` |
|
| 0x098 | WIN1_VIR (stride) | `0x000002D0` (720) | `0x00000000` |
|
||||||
| 0x0a0 | WIN1_MST (fb addr) | `0x0F900000` | `0x00000000` |
|
| 0x0a0 | WIN1_MST (fb addr) | `0x0F900000` | `0x00000000` |
|
||||||
| 0x0a4 | WIN1_DSP_INFO (size)| `0x02CF02CF` (720×720) | `0x00EF013F` (stale) |
|
| 0x0a4 | WIN1_DSP_INFO (size)| `0x02CF02CF` (720x720) | `0x00EF013F` (stale) |
|
||||||
|
|
||||||
**Fix:** `rv1106_vop_win_data[] = { { .phy = &px30_win1_data, PRIMARY } }`
|
**Fix:** `rv1106_vop_win_data[] = { { .phy = &px30_win1_data, PRIMARY } }`
|
||||||
(`px30_win1_data` already exists in mainline over `PX30_WIN1_*` — just wasn't wired
|
(`px30_win1_data` already exists in mainline over `PX30_WIN1_*`, just wasn't wired
|
||||||
for rv1106). After the fix `_b` reads WIN1_CTRL0=1, WIN1_DSP_INFO=0x02CF02CF —
|
for rv1106). After the fix `_b` reads WIN1_CTRL0=1, WIN1_DSP_INFO=0x02CF02CF:
|
||||||
matching `_a`.
|
matching `_a`.
|
||||||
|
|
||||||
## Files changed (in research/linux-6.18.46)
|
## Files changed (in research/linux-6.18.46)
|
||||||
|
|
||||||
- `drivers/gpu/drm/rockchip/rockchip_drm_vop.c` — `rgb_dclk_pol` 1 → 0 (LVDS case).
|
- `drivers/gpu/drm/rockchip/rockchip_drm_vop.c`: `rgb_dclk_pol` 1 -> 0 (LVDS case).
|
||||||
- `drivers/gpu/drm/rockchip/rockchip_vop_reg.c` — `rv1106_vop_win_data` uses
|
- `drivers/gpu/drm/rockchip/rockchip_vop_reg.c`: `rv1106_vop_win_data` uses
|
||||||
`px30_win1_data` as the single PRIMARY window (was rv1126's win0+win2).
|
`px30_win1_data` as the single PRIMARY window (was rv1126's win0+win2).
|
||||||
- `drivers/gpu/drm/panel/panel-simple.c` — `panel_dpi_probe()` reads DT `bus-format`.
|
- `drivers/gpu/drm/panel/panel-simple.c`: `panel_dpi_probe()` reads DT `bus-format`.
|
||||||
- `arch/arm/boot/dts/rockchip/rv1106-warden.dts` — `&vop` pinctrl `<&lcd_pins>`;
|
- `arch/arm/boot/dts/rockchip/rv1106-warden.dts`: `&vop` pinctrl `<&lcd_pins>`;
|
||||||
`&pwm1` pinctrl "default"; `/delete-node/ &rgb`; panel `bus-format` RGB666,
|
`&pwm1` pinctrl "default"; `/delete-node/ &rgb`; panel `bus-format` RGB666,
|
||||||
no reset-gpios.
|
no reset-gpios.
|
||||||
|
|
||||||
## Known separate issue (NOT display)
|
## Known separate issue (NOT display)
|
||||||
|
|
||||||
`_b` still cold-reboots periodically (pre-existing `_b` general-stability issue,
|
`_b` still cold-reboots periodically (pre-existing `_b` general-stability issue,
|
||||||
independent of the display — the panel renders correctly the whole time it is up).
|
independent of the display: the panel renders correctly the whole time it is up).
|
||||||
Tracked separately; the high-bootlimit env (`bootlimit=10000`) keeps these reboots
|
Tracked separately; the high-bootlimit env (`bootlimit=10000`) keeps these reboots
|
||||||
from tripping `altbootcmd=download` into the loader during bring-up.
|
from tripping `altbootcmd=download` into the loader during bring-up.
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# RV1106 6.18 devicetree
|
# RV1106 6.18 devicetree
|
||||||
|
|
||||||
- **`rv1106-warden.dts`** — our board DT. `#include`s the vendor `rv1106.dtsi`
|
- **`rv1106-warden.dts`**: our board DT. `#include`s the vendor `rv1106.dtsi`
|
||||||
(the full SoC: pinctrl, gpio×5, dmac, cru+grf_cru, uart, emmc, i2c, saradc,
|
(the full SoC: pinctrl, gpiox5, dmac, cru+grf_cru, uart, emmc, i2c, saradc,
|
||||||
watchdog, usb, vop, npu, rga, …), then enables only what the wall-HMI needs.
|
watchdog, usb, vop, npu, rga, ...), then enables only what the wall-HMI needs.
|
||||||
No camera/ISP/CSI. Grows one driver batch at a time (see `../DRIVER-PARITY.md`).
|
No camera/ISP/CSI. Grows one driver batch at a time (see `../DRIVER-PARITY.md`).
|
||||||
- **`rv1106-warden-m2.dts`** — the earlier standalone minimal DT used to bring up
|
- **`rv1106-warden-m2.dts`**: the earlier standalone minimal DT used to bring up
|
||||||
M2/M3 before the full-SoC transplant; kept for reference.
|
M2/M3 before the full-SoC transplant; kept for reference.
|
||||||
|
|
||||||
## Transplanting the vendor SoC DT onto 6.18 (recipe)
|
## Transplanting the vendor SoC DT onto 6.18 (recipe)
|
||||||
@@ -18,9 +18,9 @@ with the vendor's (it has extra BOOT_CHARGING/UMS/PANIC/WATCHDOG constants the
|
|||||||
DT uses). Then the two hardware deltas:
|
DT uses). Then the two hardware deltas:
|
||||||
|
|
||||||
1. **Delete the `psci` node** from `rv1106.dtsi`. RV1106 has no secure monitor in
|
1. **Delete the `psci` node** from `rv1106.dtsi`. RV1106 has no secure monitor in
|
||||||
our boot chain, so `arm,psci-1.0` + `method="smc"` makes the SMC call fault →
|
our boot chain, so `arm,psci-1.0` + `method="smc"` makes the SMC call fault ->
|
||||||
`Oops - bad mode` → `Attempted to kill the idle task`. (Confirmed on c8a3.)
|
`Oops - bad mode` -> `Attempted to kill the idle task`. (Confirmed on c8a3.)
|
||||||
2. Enable the peripherals in the board DTS (`&uart2`, `&emmc`, …).
|
2. Enable the peripherals in the board DTS (`&uart2`, `&emmc`, ...).
|
||||||
|
|
||||||
Verified on warden-c8a3: pinctrl, gpio0–4, pl330 DMA, uart1/2/4, i2c3, dw-wdt all
|
Verified on warden-c8a3: pinctrl, gpio0-4, pl330 DMA, uart1/2/4, i2c3, dw-wdt all
|
||||||
probe; full WardenOS userspace boots.
|
probe; full WardenOS userspace boots.
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||||
/*
|
/*
|
||||||
* Minimal RV1106 devicetree for the WardenOS 86-Panel — M2 "earlycon" bring-up
|
* Minimal RV1106 devicetree for the WardenOS 86-Panel, M2 "earlycon" bring-up
|
||||||
* of the self-built Linux 6.18 forward-port (warden-sdk kernel/docs/bringup.md).
|
* of the self-built Linux 6.18 forward-port (warden-sdk kernel/docs/bringup.md).
|
||||||
*
|
*
|
||||||
* Deliberately NOT the full vendor rv1106.dtsi: just the core needed to reach
|
* Deliberately NOT the full vendor rv1106.dtsi: just the core needed to reach
|
||||||
* "it's alive" on the debug UART — CPU, GIC, arch timer, RAM, the CRU (our
|
* "it's alive" on the debug UART: CPU, GIC, arch timer, RAM, the CRU (our
|
||||||
* ported clk-rv1106), GRF, and uart2 (the console, driven by mainline 8250_dw).
|
* ported clk-rv1106), GRF, and uart2 (the console, driven by mainline 8250_dw).
|
||||||
* earlycon writes uart2's MMIO directly at the bootloader's divisor, so it prints
|
* earlycon writes uart2's MMIO directly at the bootloader's divisor, so it prints
|
||||||
* before the clock/pinctrl drivers probe; the real ttyS console then needs the
|
* before the clock/pinctrl drivers probe; the real ttyS console then needs the
|
||||||
* CRU to hand out SCLK_UART2, which is exactly what this validates on hardware.
|
* CRU to hand out SCLK_UART2, which is exactly what this validates on hardware.
|
||||||
*
|
*
|
||||||
* PORT-VERIFY (must be checked against the TRM / on hardware before trusting a
|
* PORT-VERIFY (must be checked against the TRM / on hardware before trusting a
|
||||||
* boot — a wrong value here silently hangs the board):
|
* boot; a wrong value here silently hangs the board):
|
||||||
* - memory size/base (256 MiB @ 0x0 assumed for the 86-Panel; the loader
|
* - memory size/base (256 MiB @ 0x0 assumed for the 86-Panel; the loader
|
||||||
* usually patches this — confirm against the real DDR init);
|
* usually patches this; confirm against the real DDR init);
|
||||||
* - uart2 is the console at 0xff4c0000 (matches the vendor ttyFIQ0 base);
|
* - uart2 is the console at 0xff4c0000 (matches the vendor ttyFIQ0 base);
|
||||||
* - CRU needs no assigned-clocks here (PLLs keep their loader rates) — fine for
|
* - CRU needs no assigned-clocks here (PLLs keep their loader rates), fine for
|
||||||
* earlycon/console, revisit for the full clock tree.
|
* earlycon/console, revisit for the full clock tree.
|
||||||
*/
|
*/
|
||||||
/dts-v1/;
|
/dts-v1/;
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
#include <dt-bindings/clock/rv1106-cru.h>
|
#include <dt-bindings/clock/rv1106-cru.h>
|
||||||
|
|
||||||
/ {
|
/ {
|
||||||
model = "WardenOS 86-Panel (RV1106) — M2 earlycon bring-up";
|
model = "WardenOS 86-Panel (RV1106), M2 earlycon bring-up";
|
||||||
compatible = "rockchip,rv1106";
|
compatible = "rockchip,rv1106";
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
#size-cells = <1>;
|
#size-cells = <1>;
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
|
|
||||||
memory@0 {
|
memory@0 {
|
||||||
device_type = "memory";
|
device_type = "memory";
|
||||||
reg = <0x00000000 0x10000000>; /* 256 MiB — PORT-VERIFY */
|
reg = <0x00000000 0x10000000>; /* 256 MiB: PORT-VERIFY */
|
||||||
};
|
};
|
||||||
|
|
||||||
xin24m: oscillator {
|
xin24m: oscillator {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* WardenOS 86-Panel board DT for the self-built Linux 6.18 forward-port.
|
* WardenOS 86-Panel board DT for the self-built Linux 6.18 forward-port.
|
||||||
*
|
*
|
||||||
* Includes the full vendor rv1106.dtsi (SoC nodes + pinctrl + gpio + dmac), then
|
* Includes the full vendor rv1106.dtsi (SoC nodes + pinctrl + gpio + dmac), then
|
||||||
* enables only what our wall-HMI needs — no camera/ISP/CSI. Grows one driver
|
* enables only what our wall-HMI needs: no camera/ISP/CSI. Grows one driver
|
||||||
* batch at a time toward full parity with the 5.10 kernel (DRIVER-PARITY.md).
|
* batch at a time toward full parity with the 5.10 kernel (DRIVER-PARITY.md).
|
||||||
*/
|
*/
|
||||||
/dts-v1/;
|
/dts-v1/;
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
reset-gpios = <&gpio1 RK_PA2 GPIO_ACTIVE_LOW>;
|
reset-gpios = <&gpio1 RK_PA2 GPIO_ACTIVE_LOW>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* SARADC voltage reference (1.8V) — the driver reads this for scale;
|
/* SARADC voltage reference (1.8V): the driver reads this for scale;
|
||||||
without it saradc probes -22 (regulator_get_voltage on the dummy). */
|
without it saradc probes -22 (regulator_get_voltage on the dummy). */
|
||||||
vcc_1v8: vcc-1v8 {
|
vcc_1v8: vcc-1v8 {
|
||||||
compatible = "regulator-fixed";
|
compatible = "regulator-fixed";
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
status = "okay";
|
status = "okay";
|
||||||
};
|
};
|
||||||
|
|
||||||
/* --- M5: AIC8800DC wifi/BT SDIO (on &sdmmc, mmc@ffaa0000 — NOT &sdio) --- */
|
/* --- M5: AIC8800DC wifi/BT SDIO (on &sdmmc, mmc@ffaa0000; NOT &sdio) --- */
|
||||||
&sdmmc {
|
&sdmmc {
|
||||||
max-frequency = <50000000>;
|
max-frequency = <50000000>;
|
||||||
bus-width = <4>;
|
bus-width = <4>;
|
||||||
@@ -95,7 +95,7 @@
|
|||||||
status = "okay";
|
status = "okay";
|
||||||
};
|
};
|
||||||
|
|
||||||
/* --- M4: display (VOP + RGB → 720x720 panel + PWM backlight) --- */
|
/* --- M4: display (VOP + RGB -> 720x720 panel + PWM backlight) --- */
|
||||||
&display_subsystem {
|
&display_subsystem {
|
||||||
status = "okay";
|
status = "okay";
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# GMAC (wired 10/100 ethernet) — VERIFIED on warden-c8a3 (2026-08-25)
|
# GMAC (wired 10/100 ethernet): VERIFIED on warden-c8a3 (2026-08-25)
|
||||||
|
|
||||||
**Result: `eth0: Link is Up - 100Mbps/Full - flow control rx/tx`** on our
|
**Result: `eth0: Link is Up - 100Mbps/Full - flow control rx/tx`** on our
|
||||||
self-built 6.18.46. The 86-Panel's RMII MAC + on-die 10/100 FEPHY works; a real
|
self-built 6.18.46. The 86-Panel's RMII MAC + on-die 10/100 FEPHY works; a real
|
||||||
@@ -31,17 +31,17 @@ static const struct rk_gmac_ops rv1106_ops = {
|
|||||||
|
|
||||||
## Adaptations vs the vendor 5.10 ops
|
## Adaptations vs the vendor 5.10 ops
|
||||||
- Mainline `rk_gmac_ops` uses `.set_speed(bsp_priv, interface, speed)` returning
|
- Mainline `rk_gmac_ops` uses `.set_speed(bsp_priv, interface, speed)` returning
|
||||||
int (vendor: `.set_rmii_speed(bsp_priv, speed)` void) — adapted.
|
int (vendor: `.set_rmii_speed(bsp_priv, speed)` void), adapted.
|
||||||
- Split `.integrated_phy_power(up)` into mainline's `.integrated_phy_powerup` /
|
- Split `.integrated_phy_power(up)` into mainline's `.integrated_phy_powerup` /
|
||||||
`.integrated_phy_powerdown`, each calling mainline's single-reg
|
`.integrated_phy_powerdown`, each calling mainline's single-reg
|
||||||
`rk_gmac_integrated_fephy_power{up,down}(priv, CON0)`.
|
`rk_gmac_integrated_fephy_power{up,down}(priv, CON0)`.
|
||||||
- **Bandgap trim OMITTED**: the vendor also wrote an OTP-derived bandgap value to
|
- **Bandgap trim OMITTED**: the vendor also wrote an OTP-derived bandgap value to
|
||||||
MACPHY_CON1; that's an analog optimisation the FEPHY runs without, and mainline's
|
MACPHY_CON1; that's an analog optimisation the FEPHY runs without, and mainline's
|
||||||
fephy helper doesn't carry it. Link came up 100M/Full without it. (If signal
|
fephy helper doesn't carry it. Link came up 100M/Full without it. (If signal
|
||||||
integrity ever needs it, the `bgs` OTP cell now reads — see rng-otp/.)
|
integrity ever needs it, the `bgs` OTP cell now reads: see rng-otp/.)
|
||||||
|
|
||||||
## DT / config
|
## DT / config
|
||||||
- `&gmac { status = "okay"; }` — the dtsi `ethernet@ffa80000` node already has
|
- `&gmac { status = "okay"; }`: the dtsi `ethernet@ffa80000` node already has
|
||||||
clocks/resets/`phy-mode="rmii"`/`phy-handle=&rmii_phy` + the integrated
|
clocks/resets/`phy-mode="rmii"`/`phy-handle=&rmii_phy` + the integrated
|
||||||
`ethernet-phy@2` (`phy-is-integrated`). `rockchip,grf=<&grf>` (the big syscon
|
`ethernet-phy@2` (`phy-is-integrated`). `rockchip,grf=<&grf>` (the big syscon
|
||||||
covers the 0x60xxx VOGRF offsets). The `bgs`/`txlevel` nvmem-cells are present
|
covers the 0x60xxx VOGRF offsets). The `bgs`/`txlevel` nvmem-cells are present
|
||||||
@@ -49,8 +49,8 @@ static const struct rk_gmac_ops rv1106_ops = {
|
|||||||
- Kconfig: `STMMAC_ETH`, `STMMAC_PLATFORM`, `DWMAC_ROCKCHIP`, `ROCKCHIP_PHY` (=y).
|
- Kconfig: `STMMAC_ETH`, `STMMAC_PLATFORM`, `DWMAC_ROCKCHIP`, `ROCKCHIP_PHY` (=y).
|
||||||
|
|
||||||
## Evidence
|
## Evidence
|
||||||
`rk_gmac-dwmac ffa80000.ethernet eth0: configuring for phy/rmii link mode` →
|
`rk_gmac-dwmac ffa80000.ethernet eth0: configuring for phy/rmii link mode` ->
|
||||||
`eth0: Link is Up - 100Mbps/Full - flow control rx/tx`. PHY bound at
|
`eth0: Link is Up - 100Mbps/Full - flow control rx/tx`. PHY bound at
|
||||||
`stmmac-0:02` (integrated). The internal FEPHY reports id 0044.1400; it bound to
|
`stmmac-0:02` (integrated). The internal FEPHY reports id 0044.1400; it bound to
|
||||||
the Generic PHY (mainline `net/phy/rockchip.c` INTERNAL_EPHY_ID is 0x1234d400) —
|
the Generic PHY (mainline `net/phy/rockchip.c` INTERNAL_EPHY_ID is 0x1234d400):
|
||||||
`phy-is-integrated` + c22 was sufficient for a full-duplex 100M link.
|
`phy-is-integrated` + c22 was sufficient for a full-duplex 100M link.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# HPMCU mailbox to 100% — plan, feasibility, hardware round-trip verify
|
# HPMCU mailbox to 100%: plan, feasibility, hardware round-trip verify
|
||||||
|
|
||||||
**Goal:** a working, fully-open A7 ↔ HPMCU (SCR1) mailbox link on our self-built
|
**Goal:** a working, fully-open A7 ↔ HPMCU (SCR1) mailbox link on our self-built
|
||||||
Linux 6.18.46, **verified by a message round-trip over serial on hardware.**
|
Linux 6.18.46, **verified by a message round-trip over serial on hardware.**
|
||||||
@@ -11,21 +11,21 @@ the SCR1 core**. The only real work is: (1) enable the controller in DT + config
|
|||||||
(2) write a ~dozen-line SCR1 echo handler, (3) write a small Linux client, and
|
(2) write a ~dozen-line SCR1 echo handler, (3) write a small Linux client, and
|
||||||
(4) run the round-trip on `warden-c8a3` (the recovery-capable rig) over serial.
|
(4) run the round-trip on `warden-c8a3` (the recovery-capable rig) over serial.
|
||||||
|
|
||||||
The one substantive design decision — **hardware mailbox IP vs. our current
|
The one substantive design decision (**hardware mailbox IP vs. our current
|
||||||
`/dev/mem` polled-SRAM scheme** — is settled below: build the real mailbox link
|
`/dev/mem` polled-SRAM scheme**) is settled below: build the real mailbox link
|
||||||
(it is the open, IRQ-driven, general-purpose answer), but keep the proven SRAM
|
(it is the open, IRQ-driven, general-purpose answer), but keep the proven SRAM
|
||||||
watchdog exactly as-is (different threat model, different job).
|
watchdog exactly as-is (different threat model, different job).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Feasibility — honest assessment
|
## 1. Feasibility: honest assessment
|
||||||
|
|
||||||
**Fully feasible. Two halves, both open, both with working reference code to copy.**
|
**Fully feasible. Two halves, both open, both with working reference code to copy.**
|
||||||
|
|
||||||
- **Controller (Linux side): non-issue.** `drivers/mailbox/rockchip-mailbox.c` is
|
- **Controller (Linux side): non-issue.** `drivers/mailbox/rockchip-mailbox.c` is
|
||||||
upstream in mainline 6.18 and **already binds on our exact kernel** via the
|
upstream in mainline 6.18 and **already binds on our exact kernel** via the
|
||||||
generic `rockchip,rk3368-mailbox` **fallback compatible** with **zero patching**
|
generic `rockchip,rk3368-mailbox` **fallback compatible** with **zero patching**:
|
||||||
— recorded in `CAPABILITIES-AUDIT.md`'s Remaining-blocks table (mailbox row), confirmed by source read. RV1106's DT
|
recorded in `CAPABILITIES-AUDIT.md`'s Remaining-blocks table (mailbox row), confirmed by source read. RV1106's DT
|
||||||
declares both instances with that fallback string. Gated today only by
|
declares both instances with that fallback string. Gated today only by
|
||||||
`status="disabled"` + `CONFIG_ROCKCHIP_MBOX` being absent from the defconfig.
|
`status="disabled"` + `CONFIG_ROCKCHIP_MBOX` being absent from the defconfig.
|
||||||
- **HPMCU firmware (MCU side): we already do the hard part.** WardenOS has a
|
- **HPMCU firmware (MCU side): we already do the hard part.** WardenOS has a
|
||||||
@@ -33,30 +33,30 @@ watchdog exactly as-is (different threat model, different job).
|
|||||||
the vendor's own `sysdrv/source/mcu/` toolchain (xPack `riscv-none-embed-gcc`
|
the vendor's own `sysdrv/source/mcu/` toolchain (xPack `riscv-none-embed-gcc`
|
||||||
10.2.0, `-march=rv32imc`), loaded via `/dev/mem`-mmap by `flared/src/hpmcu.rs`,
|
10.2.0, `-march=rv32imc`), loaded via `/dev/mem`-mmap by `flared/src/hpmcu.rs`,
|
||||||
running a real state machine and firing a real CRU reset. Adding a mailbox echo
|
running a real state machine and firing a real CRU reset. Adding a mailbox echo
|
||||||
handler to that firmware is small, additive work — and the vendor ships a
|
handler to that firmware is small, additive work, and the vendor ships a
|
||||||
register-level template for the MCU side (`hal_mbox.c`) plus a working two-ended
|
register-level template for the MCU side (`hal_mbox.c`) plus a working two-ended
|
||||||
example (`battery-ipc/stream.c` + `rockchip_thunderboot_service.c`).
|
example (`battery-ipc/stream.c` + `rockchip_thunderboot_service.c`).
|
||||||
- **The only genuinely new/greenfield piece** is that no general Linux-side mailbox
|
- **The only genuinely new/greenfield piece** is that no general Linux-side mailbox
|
||||||
_client_ for RV1106 exists in-tree (the one real client,
|
_client_ for RV1106 exists in-tree (the one real client,
|
||||||
`rockchip_thunderboot_service.c`, is a hardwired one-shot `{0xf00d,0xdeadbeef}`
|
`rockchip_thunderboot_service.c`, is a hardwired one-shot `{0xf00d,0xdeadbeef}`
|
||||||
"MCU done" signal, and isn't in our board's DT). But a mailbox client using the
|
"MCU done" signal, and isn't in our board's DT). But a mailbox client using the
|
||||||
stock upstream `mailbox_client.h` API is a small, well-understood piece of code —
|
stock upstream `mailbox_client.h` API is a small, well-understood piece of code,
|
||||||
not a driver port. `mailbox-test.c` (stock kernel debugfs exerciser, present in
|
not a driver port. `mailbox-test.c` (stock kernel debugfs exerciser, present in
|
||||||
our tree, `compatible="mailbox-test"`) lets us prove the Linux→controller path
|
our tree, `compatible="mailbox-test"`) lets us prove the Linux->controller path
|
||||||
**before writing any client at all.**
|
**before writing any client at all.**
|
||||||
|
|
||||||
**Confidence in a verifiable open link: high.** The only area flagged risky is
|
**Confidence in a verifiable open link: high.** The only area flagged risky is
|
||||||
_starting the SCR1 with our own entry point_ — and we have already solved and
|
_starting the SCR1 with our own entry point_, and we have already solved and
|
||||||
hardware-proven exactly that (the `hpmcu.rs` load/release sequence), so it is a
|
hardware-proven exactly that (the `hpmcu.rs` load/release sequence), so it is a
|
||||||
known quantity here, not the open problem it would be for a team starting cold.
|
known quantity here, not the open problem it would be for a team starting cold.
|
||||||
|
|
||||||
**Decision — hardware mailbox vs. `/dev/mem` SRAM:** build the **hardware mailbox**
|
**Decision, hardware mailbox vs. `/dev/mem` SRAM:** build the **hardware mailbox**
|
||||||
as the open, general, IRQ-driven bidirectional channel (this is "the real mailbox
|
as the open, general, IRQ-driven bidirectional channel (this is "the real mailbox
|
||||||
client" the task wants). **Keep the existing SRAM polled-word watchdog untouched** —
|
client" the task wants). **Keep the existing SRAM polled-word watchdog untouched**:
|
||||||
ADR-0002 deliberately chose it as a dead-man's-switch (no IRQ, no dependency on the
|
ADR-0002 deliberately chose it as a dead-man's-switch (no IRQ, no dependency on the
|
||||||
mailbox controller being up, survives A7 hangs by design). They coexist: the SRAM
|
mailbox controller being up, survives A7 hangs by design). They coexist: the SRAM
|
||||||
word is the safety watchdog; the mailbox is the general IPC channel. Do **not** rip
|
word is the safety watchdog; the mailbox is the general IPC channel. Do **not** rip
|
||||||
out `hpmcu.rs`'s watchdog to route it through the mailbox — that would trade a
|
out `hpmcu.rs`'s watchdog to route it through the mailbox: that would trade a
|
||||||
proven fail-safe for a more complex path with no safety gain.
|
proven fail-safe for a more complex path with no safety gain.
|
||||||
|
|
||||||
**Do NOT (for a first cut):** port `rockchip_rpmsg.c` or NXP `rpmsg-lite`. Both are
|
**Do NOT (for a first cut):** port `rockchip_rpmsg.c` or NXP `rpmsg-lite`. Both are
|
||||||
@@ -65,7 +65,7 @@ files only for RK3308/RK3568; `rockchip_rpmsg.c` matches only `rk3562/rk3568-rpm
|
|||||||
neither is upstream in mainline; `CONFIG_RPMSG_ROCKCHIP` is `# not set` on every
|
neither is upstream in mainline; `CONFIG_RPMSG_ROCKCHIP` is `# not set` on every
|
||||||
RV1106 defconfig). They add virtio/vring machinery we don't need to prove a link.
|
RV1106 defconfig). They add virtio/vring machinery we don't need to prove a link.
|
||||||
If a richer multi-message channel is later wanted, layer a small hand-rolled ring
|
If a richer multi-message channel is later wanted, layer a small hand-rolled ring
|
||||||
buffer in `hpmcu_sram` on top of the doorbell — that's the natural next step, not
|
buffer in `hpmcu_sram` on top of the doorbell: that's the natural next step, not
|
||||||
a full rpmsg port.
|
a full rpmsg port.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -76,13 +76,13 @@ a full rpmsg port.
|
|||||||
|
|
||||||
| Node | Base | IRQ | Clock | Consumer | Use |
|
| Node | Base | IRQ | Clock | Consumer | Use |
|
||||||
|---|---|---|---|---|---|
|
|---|---|---|---|---|---|
|
||||||
| `mailbox@ff5c0000` | `0xff5c0000` (`reg` size `0x200`) | `GIC_SPI 1` | `PCLK_MAILBOX` | `thunder-boot-service` (ch1, `"amp-rx"`) — not on our board | **HPMCU-connected — use this one** |
|
| `mailbox@ff5c0000` | `0xff5c0000` (`reg` size `0x200`) | `GIC_SPI 1` | `PCLK_MAILBOX` | `thunder-boot-service` (ch1, `"amp-rx"`), not on our board | **HPMCU-connected: use this one** |
|
||||||
| `pmu_mailbox@ff378000` | `0xff378000` (`0x200`) | `GIC_SPI 114` | `PCLK_PMU_MAILBOX` | none anywhere | PMU-domain, undocumented purpose — **avoid** |
|
| `pmu_mailbox@ff378000` | `0xff378000` (`0x200`) | `GIC_SPI 114` | `PCLK_PMU_MAILBOX` | none anywhere | PMU-domain, undocumented purpose: **avoid** |
|
||||||
|
|
||||||
**`@ff5c0000` is the HPMCU-connected instance, proven from both ends:** the Linux
|
**`@ff5c0000` is the HPMCU-connected instance, proven from both ends:** the Linux
|
||||||
`thunder_boot_service` node's `resets` are literally named `SRST_CORE_MCU*`; the
|
`thunder_boot_service` node's `resets` are literally named `SRST_CORE_MCU*`; the
|
||||||
MCU-side vendor CMSIS header hard-codes `#define MBOX_BASE 0xFF5C0000U`
|
MCU-side vendor CMSIS header hard-codes `#define MBOX_BASE 0xFF5C0000U`
|
||||||
(`.../hal/lib/CMSIS/Device/RV1106/Include/rv1106.h:815`) — the same physical
|
(`.../hal/lib/CMSIS/Device/RV1106/Include/rv1106.h:815`): the same physical
|
||||||
address the A7 sees. Both clock IDs are real CRU gates (`PCLK_MAILBOX` at
|
address the A7 sees. Both clock IDs are real CRU gates (`PCLK_MAILBOX` at
|
||||||
`clk-rv1106.c:343`, `PCLK_PMU_MAILBOX` at `:737`), so the fallback-compatible match
|
`clk-rv1106.c:343`, `PCLK_PMU_MAILBOX` at `:737`), so the fallback-compatible match
|
||||||
is trustworthy: the register block is IP-identical to rk3368's.
|
is trustworthy: the register block is IP-identical to rk3368's.
|
||||||
@@ -101,113 +101,113 @@ MAILBOX_B2A_DAT(x) 0x34 + x*8
|
|||||||
```
|
```
|
||||||
|
|
||||||
**4 channels, 32-bit `{cmd,data}` per message per direction. Doorbell + 8-byte
|
**4 channels, 32-bit `{cmd,data}` per message per direction. Doorbell + 8-byte
|
||||||
payload — NOT a bulk channel.** Sender writes CMD then DAT (two `writel_relaxed`,
|
payload, NOT a bulk channel.** Sender writes CMD then DAT (two `writel_relaxed`,
|
||||||
fire-and-forget, no ack/poll — `rockchip-mailbox.c:46-70`); the peer's STATUS bit
|
fire-and-forget, no ack/poll: `rockchip-mailbox.c:46-70`); the peer's STATUS bit
|
||||||
raises an IRQ; receiver reads CMD/DAT, dispatches to the registered client via
|
raises an IRQ; receiver reads CMD/DAT, dispatches to the registered client via
|
||||||
`mbox_chan_received_data`, clears the STATUS bit to ack. `#mbox-cells = <1>`
|
`mbox_chan_received_data`, clears the STATUS bit to ack. `#mbox-cells = <1>`
|
||||||
(channel index). Exported `rockchip_mbox_read_msg()` (`EXPORT_SYMBOL_GPL`,
|
(channel index). Exported `rockchip_mbox_read_msg()` (`EXPORT_SYMBOL_GPL`,
|
||||||
`rockchip-mailbox.c:108-125`) pulls the last `{cmd,data}` pair out for a client.
|
`rockchip-mailbox.c:108-125`) pulls the last `{cmd,data}` pair out for a client.
|
||||||
Only OF match in the driver: `"rockchip,rk3368-mailbox"` → `.num_chans = 4`.
|
Only OF match in the driver: `"rockchip,rk3368-mailbox"` -> `.num_chans = 4`.
|
||||||
|
|
||||||
### HPMCU / SCR1 facts (from our own hardware-validated work)
|
### HPMCU / SCR1 facts (from our own hardware-validated work)
|
||||||
|
|
||||||
- SCR1 = Syntacore RV32IMC, machine-mode only, 16KB unified cache, "HPMCU".
|
- SCR1 = Syntacore RV32IMC, machine-mode only, 16KB unified cache, "HPMCU".
|
||||||
- **On our board the SCR1 is idle from reset** (non-TB `RV1106MINIALL.ini`, no
|
- **On our board the SCR1 is idle from reset** (non-TB `RV1106MINIALL.ini`, no
|
||||||
`Hpmcu=` loader stage) — a clean slate, not even the stock camera-AE blob.
|
`Hpmcu=` loader stage), a clean slate, not even the stock camera-AE blob.
|
||||||
- **Usable MCU code SRAM is a hard 8KB**: `hpmcu_sram` at offset `0x3e000` inside
|
- **Usable MCU code SRAM is a hard 8KB**: `hpmcu_sram` at offset `0x3e000` inside
|
||||||
`system_sram@ff6c0000` → absolute **`0xFF6FE000`, 8KB** (`reg=<0x3e000 0x2000>`,
|
`system_sram@ff6c0000` -> absolute **`0xFF6FE000`, 8KB** (`reg=<0x3e000 0x2000>`,
|
||||||
`rv1106.dtsi:1149-1151`), shared region with 248KB `rkisp_sram`. Our firmware +
|
`rv1106.dtsi:1149-1151`), shared region with 248KB `rkisp_sram`. Our firmware +
|
||||||
mailbox echo handler must fit this.
|
mailbox echo handler must fit this.
|
||||||
- **Proven load/release sequence** (`flared/src/hpmcu.rs:26-76`, hardware-validated):
|
- **Proven load/release sequence** (`flared/src/hpmcu.rs:26-76`, hardware-validated):
|
||||||
`CORECRU_CORESOFTRST_CON01` (`0xff3b8000+0xa04`) hold `0x1e001e` → write firmware
|
`CORECRU_CORESOFTRST_CON01` (`0xff3b8000+0xa04`) hold `0x1e001e` -> write firmware
|
||||||
to `0xff6fe000` → `SGRF_HPMCU_BOOT_ADDR` (`0xff076000+0x44`) = load addr →
|
to `0xff6fe000` -> `SGRF_HPMCU_BOOT_ADDR` (`0xff076000+0x44`) = load addr ->
|
||||||
release CORECRU to `0x1e0000`. This is the WiFi-independent way we start the core
|
release CORECRU to `0x1e0000`. This is the WiFi-independent way we start the core
|
||||||
with our own entry point — the piece source-level analysis flagged as unknown is
|
with our own entry point: the piece source-level analysis flagged as unknown is
|
||||||
already solved and proven here.
|
already solved and proven here.
|
||||||
- **MCU-side reference to copy:** `hal_mbox.c`/`hal_mbox.h` (generic 4-channel
|
- **MCU-side reference to copy:** `hal_mbox.c`/`hal_mbox.h` (generic 4-channel
|
||||||
register driver, `HAL_MBOX_Init`/send/ack) + working example
|
register driver, `HAL_MBOX_Init`/send/ack) + working example
|
||||||
`battery-ipc/stream.c:380-425` (sends `struct MBOX_CMD_DAT {cmd,data}` over
|
`battery-ipc/stream.c:380-425` (sends `struct MBOX_CMD_DAT {cmd,data}` over
|
||||||
`MBOX_CH_1`, client name `"mcu-status"` — exactly matches the Linux
|
`MBOX_CH_1`, client name `"mcu-status"`: exactly matches the Linux
|
||||||
`struct rockchip_mbox_msg`).
|
`struct rockchip_mbox_msg`).
|
||||||
|
|
||||||
### Current comms mechanism (what exists today, keep it)
|
### Current comms mechanism (what exists today, keep it)
|
||||||
|
|
||||||
WardenOS's watchdog uses a **software mailbox in shared SRAM**, not the mailbox IP:
|
WardenOS's watchdog uses a **software mailbox in shared SRAM**, not the mailbox IP:
|
||||||
last 256 bytes of `hpmcu_sram` at absolute base **`0xFF6FFF00`** — `+0x00 magic`
|
last 256 bytes of `hpmcu_sram` at absolute base **`0xFF6FFF00`**, `+0x00 magic`
|
||||||
(Linux: `WARD`/`DISA`), `+0x04 counter` (heartbeat), `+0x08 mcu_state`
|
(Linux: `WARD`/`DISA`), `+0x04 counter` (heartbeat), `+0x08 mcu_state`
|
||||||
(`BOOT/ARMD/DISA/FIRE`), `+0x0c`/`+0x10` debug. 5s heartbeat, fires after 90s
|
(`BOOT/ARMD/DISA/FIRE`), `+0x0c`/`+0x10` debug. 5s heartbeat, fires after 90s
|
||||||
no-advance. CRU reset via `GLB_SRST_FST` at **`0xff3b0c08`** magic `0xfdb9` (the
|
no-advance. CRU reset via `GLB_SRST_FST` at **`0xff3b0c08`** magic `0xfdb9` (the
|
||||||
only working whole-SoC reset — `reboot -f` is a no-op: no PSCI/restart handler).
|
only working whole-SoC reset, `reboot -f` is a no-op: no PSCI/restart handler).
|
||||||
Reset constant is CI-guarded across 4 sites. **`dd of=/dev/mem` faults for the SRAM
|
Reset constant is CI-guarded across 4 sites. **`dd of=/dev/mem` faults for the SRAM
|
||||||
region on this ARM kernel — only the `mmap()` path writes** (busybox `devmem` or
|
region on this ARM kernel: only the `mmap()` path writes** (busybox `devmem` or
|
||||||
`libc::mmap`); `STRICT_DEVMEM` is off in our config.
|
`libc::mmap`); `STRICT_DEVMEM` is off in our config.
|
||||||
|
|
||||||
### The `0x40000` boot-load hazard (load-bearing — for any _boot-time_ MCU path only)
|
### The `0x40000` boot-load hazard (boot-time MCU paths only)
|
||||||
|
|
||||||
Boot-loading firmware to the DDR carve-out `0x40000` (240KB) **bricked warden-c8a3
|
Boot-loading firmware to the DDR carve-out `0x40000` (240KB) **bricked warden-c8a3
|
||||||
on 2026-08-23** because our non-TB kernel DT does **not** reserve `0x40000` — it
|
on 2026-08-23** because our non-TB kernel DT does **not** reserve `0x40000`: it
|
||||||
collides with kernel RAM → early-boot hang before eth0. **This mailbox plan avoids
|
collides with kernel RAM -> early-boot hang before eth0. **This mailbox plan avoids
|
||||||
the hazard entirely** by using the runtime SRAM-load path (`0xff6fe000`), not a
|
the hazard entirely** by using the runtime SRAM-load path (`0xff6fe000`), not a
|
||||||
boot-time DDR load. If a boot-time path is ever pursued, a `reserved-memory` DT node
|
boot-time DDR load. If a boot-time path is ever pursued, a `reserved-memory` DT node
|
||||||
for `0x40000/0x3c000` must be added and verified via `/proc/iomem` **before**
|
for `0x40000/0x3c000` must be added and verified via `/proc/iomem` **before**
|
||||||
flashing the idblock. (Standing memory: `boot-loaded-mcu-0x40000-hazard.md` —
|
flashing the idblock. (Standing memory: `boot-loaded-mcu-0x40000-hazard.md`,
|
||||||
worth promoting into `riscv-mcu.md` open-questions; not yet captured there.)
|
worth promoting into `riscv-mcu.md` open-questions; not yet captured there.)
|
||||||
|
|
||||||
### URLs / upstream status
|
### URLs / upstream status
|
||||||
|
|
||||||
- `drivers/mailbox/rockchip-mailbox.c` — upstream mainline v6.18 (Bootlin Elixir
|
- `drivers/mailbox/rockchip-mailbox.c`: upstream mainline v6.18 (Bootlin Elixir
|
||||||
confirms), binds via `rk3368-mailbox` fallback, zero patch.
|
confirms), binds via `rk3368-mailbox` fallback, zero patch.
|
||||||
- No `drivers/rpmsg/rockchip*` and no `drivers/remoteproc/rockchip*` in mainline
|
- No `drivers/rpmsg/rockchip*` and no `drivers/remoteproc/rockchip*` in mainline
|
||||||
(GitHub API enumeration of both dirs — none Rockchip). Vendor `rockchip_rpmsg.c`
|
(GitHub API enumeration of both dirs, none Rockchip). Vendor `rockchip_rpmsg.c`
|
||||||
never merged; `lore.kernel.org` 403-walled, submission history unpinnable.
|
never merged; `lore.kernel.org` 403-walled, submission history unpinnable.
|
||||||
- Community RV1106 HPMCU prior art beyond LED-blink: **~zero** (GitHub search
|
- Community RV1106 HPMCU prior art beyond LED-blink: **~zero** (GitHub search
|
||||||
`rv1106 hpmcu` / `rv1106 scr1 coprocessor` → only SDK/rkbin mirrors). ADR-0002:
|
`rv1106 hpmcu` / `rv1106 scr1 coprocessor` -> only SDK/rkbin mirrors). ADR-0002:
|
||||||
"expect to be first." Our PR #25 firmware is the only known custom SCR1 code.
|
"expect to be first." Our PR #25 firmware is the only known custom SCR1 code.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. The simplest fully-open, hardware-verifiable path (milestones)
|
## 3. The simplest fully-open, hardware-verifiable path (milestones)
|
||||||
|
|
||||||
### M-MBOX-1 — Controller wiring proof (Tier 1: today, ZERO HPMCU risk, no firmware)
|
### M-MBOX-1, Controller wiring proof (Tier 1: today, ZERO HPMCU risk, no firmware)
|
||||||
|
|
||||||
Proves DT status/clock/IRQ/probe/send all work on real hardware without touching
|
Proves DT status/clock/IRQ/probe/send all work on real hardware without touching
|
||||||
the SCR1 at all.
|
the SCR1 at all.
|
||||||
|
|
||||||
1. Kernel config: `CONFIG_ROCKCHIP_MBOX=y` + `CONFIG_MAILBOX_TEST=y` (mainline
|
1. Kernel config: `CONFIG_ROCKCHIP_MBOX=y` + `CONFIG_MAILBOX_TEST=y` (mainline
|
||||||
driver, **no patch** — `rockchip-mailbox.c` binds on the fallback compatible).
|
driver, **no patch**, `rockchip-mailbox.c` binds on the fallback compatible).
|
||||||
2. Board DTS: override `&mailbox { status = "okay"; };` (the `@ff5c0000` instance —
|
2. Board DTS: override `&mailbox { status = "okay"; };` (the `@ff5c0000` instance,
|
||||||
main CRU clock, `GIC_SPI 1`, no PMU-domain complications). Add a
|
main CRU clock, `GIC_SPI 1`, no PMU-domain complications). Add a
|
||||||
`compatible = "mailbox-test"` node with `mboxes = <&mailbox N>` on a **free
|
`compatible = "mailbox-test"` node with `mboxes = <&mailbox N>` on a **free
|
||||||
channel — avoid channel 1** (vendor reserves it for `"amp-rx"` semantics).
|
channel: avoid channel 1** (vendor reserves it for `"amp-rx"` semantics).
|
||||||
3. **Verify on serial console** (via the `warden-c8a3` `_b`-slot one-shot loop):
|
3. **Verify on serial console** (via the `warden-c8a3` `_b`-slot one-shot loop):
|
||||||
confirm `rockchip-mailbox` probes clean in `dmesg`; write an 8-byte `{cmd,data}`
|
confirm `rockchip-mailbox` probes clean in `dmesg`; write an 8-byte `{cmd,data}`
|
||||||
pair to the debugfs `message` file (`mailbox-test.c`); then read back the
|
pair to the debugfs `message` file (`mailbox-test.c`); then read back the
|
||||||
physical `A2B_CMD(N)`/`A2B_DAT(N)` registers directly with busybox `devmem` at
|
physical `A2B_CMD(N)`/`A2B_DAT(N)` registers directly with busybox `devmem` at
|
||||||
`0xff5c0008+8N` / `0xff5c000c+8N` and confirm the value landed. **Proves the AP→
|
`0xff5c0008+8N` / `0xff5c000c+8N` and confirm the value landed. **Proves the AP->
|
||||||
controller send path end-to-end, serial-only, no risk to the HPMCU.**
|
controller send path end-to-end, serial-only, no risk to the HPMCU.**
|
||||||
|
|
||||||
### M-MBOX-2 — Real round-trip: SCR1 echo + Linux client (Tier 2: the deliverable)
|
### M-MBOX-2, Real round-trip: SCR1 echo + Linux client (Tier 2: the deliverable)
|
||||||
|
|
||||||
The actual "message round-trip over serial on hardware" the task asks for.
|
The actual "message round-trip over serial on hardware" the task asks for.
|
||||||
|
|
||||||
1. **SCR1 echo firmware** (additive to our existing bare-metal firmware, must fit
|
1. **SCR1 echo firmware** (additive to our existing bare-metal firmware, must fit
|
||||||
the 8KB `hpmcu_sram` budget): on A2B IRQ (or a tight poll of `A2B_STATUS` — poll
|
the 8KB `hpmcu_sram` budget): on A2B IRQ (or a tight poll of `A2B_STATUS`, poll
|
||||||
is simpler and safe for a bring-up echo, avoids MCU IRQ-controller setup), read
|
is simpler and safe for a bring-up echo, avoids MCU IRQ-controller setup), read
|
||||||
`A2B_CMD(x)`/`A2B_DAT(x)`, write them back to `B2A_CMD(x)`/`B2A_DAT(x)`, set the
|
`A2B_CMD(x)`/`A2B_DAT(x)`, write them back to `B2A_CMD(x)`/`B2A_DAT(x)`, set the
|
||||||
`B2A_STATUS` bit, clear `A2B_STATUS`. Copy the register sequence from vendor
|
`B2A_STATUS` bit, clear `A2B_STATUS`. Copy the register sequence from vendor
|
||||||
`hal_mbox.c` / `battery-ipc/stream.c`. Load and start the core with the
|
`hal_mbox.c` / `battery-ipc/stream.c`. Load and start the core with the
|
||||||
**proven `hpmcu.rs` load/release sequence** (SRAM path, `0xff6fe000` — **not**
|
**proven `hpmcu.rs` load/release sequence** (SRAM path, `0xff6fe000`, **not**
|
||||||
the `0x40000` boot path, hazard §2).
|
the `0x40000` boot path, hazard section 2).
|
||||||
2. **Linux client**: a minimal in-tree `mbox_client` (stock `mailbox_client.h`:
|
2. **Linux client**: a minimal in-tree `mbox_client` (stock `mailbox_client.h`:
|
||||||
`mbox_request_channel_byname` / `mbox_send_message` + an rx callback reading via
|
`mbox_request_channel_byname` / `mbox_send_message` + an rx callback reading via
|
||||||
the exported `rockchip_mbox_read_msg()`), or — to avoid writing a kernel client
|
the exported `rockchip_mbox_read_msg()`), or (to avoid writing a kernel client
|
||||||
for the first proof — reuse `mailbox-test`'s debugfs send and read the B2A
|
for the first proof) reuse `mailbox-test`'s debugfs send and read the B2A
|
||||||
registers from userspace with `devmem`. Prefer the `mailbox-test` route for
|
registers from userspace with `devmem`. Prefer the `mailbox-test` route for
|
||||||
M-MBOX-2's first light; promote to a real `mbox_client` once the round-trip is
|
M-MBOX-2's first light; promote to a real `mbox_client` once the round-trip is
|
||||||
green.
|
green.
|
||||||
3. **Verify on serial console** (`warden-c8a3`, `_b`-slot): send a known
|
3. **Verify on serial console** (`warden-c8a3`, `_b`-slot): send a known
|
||||||
`{cmd,data}` (e.g. `{0x1234, 0xcafef00d}`) from Linux; confirm the SCR1 echoed
|
`{cmd,data}` (e.g. `{0x1234, 0xcafef00d}`) from Linux; confirm the SCR1 echoed
|
||||||
it back — either via the client's rx callback logging the received pair, or by
|
it back, either via the client's rx callback logging the received pair, or by
|
||||||
`devmem` reading `B2A_CMD(N)`/`B2A_DAT(N)` (`0xff5c0030+8N`/`0xff5c0034+8N`) and
|
`devmem` reading `B2A_CMD(N)`/`B2A_DAT(N)` (`0xff5c0030+8N`/`0xff5c0034+8N`) and
|
||||||
matching it byte-for-byte against what was sent. Capture the serial transcript
|
matching it byte-for-byte against what was sent. Capture the serial transcript
|
||||||
as the evidence. **This is the 100%, verified, fully-open mailbox link.**
|
as the evidence. **This is the 100%, verified, fully-open mailbox link.**
|
||||||
@@ -217,7 +217,7 @@ the working `_a` slot, auto-reverts on hang). The SRAM-load path sidesteps the
|
|||||||
`0x40000` brick hazard. The existing SRAM watchdog keeps running throughout,
|
`0x40000` brick hazard. The existing SRAM watchdog keeps running throughout,
|
||||||
independent of the mailbox, as it did during the 2026-08-14 validation.
|
independent of the mailbox, as it did during the 2026-08-14 validation.
|
||||||
|
|
||||||
### M-MBOX-3 — (Optional, later) richer channel
|
### M-MBOX-3: (Optional, later) richer channel
|
||||||
|
|
||||||
Only if a real multi-message need appears: layer a small ring buffer in
|
Only if a real multi-message need appears: layer a small ring buffer in
|
||||||
`hpmcu_sram`, mailbox used as the doorbell ("look at address X"). Still **do not**
|
`hpmcu_sram`, mailbox used as the doorbell ("look at address X"). Still **do not**
|
||||||
@@ -227,21 +227,21 @@ adopt rpmsg/virtio unless the payload complexity genuinely demands it.
|
|||||||
|
|
||||||
## 4. Risks / open questions
|
## 4. Risks / open questions
|
||||||
|
|
||||||
- **SCR1 IRQ setup** — for M-MBOX-2 a polled echo avoids configuring the MCU's
|
- **SCR1 IRQ setup**: for M-MBOX-2 a polled echo avoids configuring the MCU's
|
||||||
interrupt controller; fine for bring-up. IRQ-driven B2A on the Linux side works
|
interrupt controller; fine for bring-up. IRQ-driven B2A on the Linux side works
|
||||||
regardless (that's the controller's job). Note as a simplification, not a gap.
|
regardless (that's the controller's job). Note as a simplification, not a gap.
|
||||||
- **Channel choice** — use a channel ≠ 1 (ch1 = vendor `"amp-rx"` reservation).
|
- **Channel choice**: use a channel ≠ 1 (ch1 = vendor `"amp-rx"` reservation).
|
||||||
- **`pmu_mailbox@ff378000` purpose** — undocumented, no consumer anywhere; leave
|
- **`pmu_mailbox@ff378000` purpose**: undocumented, no consumer anywhere; leave
|
||||||
disabled, do not use, until a TRM section or Rockchip engineer clarifies.
|
disabled, do not use, until a TRM section or Rockchip engineer clarifies.
|
||||||
- **RS-485 UART reachability from the HPMCU** — never checked (pinmux vs Linux
|
- **RS-485 UART reachability from the HPMCU**: never checked (pinmux vs Linux
|
||||||
ownership); irrelevant to the mailbox link but open for future MCU apps.
|
ownership); irrelevant to the mailbox link but open for future MCU apps.
|
||||||
- **Whether Rockchip-official (non-RE) MCU/mailbox docs exist under NDA/partner
|
- **Whether Rockchip-official (non-RE) MCU/mailbox docs exist under NDA/partner
|
||||||
access** — never asked Luckfox support directly; worth a query.
|
access**: never asked Luckfox support directly; worth a query.
|
||||||
- **`mcutool.c` exact `/dev/mem` sequence** — inferred, not read line-by-line;
|
- **`mcutool.c` exact `/dev/mem` sequence**: inferred, not read line-by-line;
|
||||||
moot, since our `hpmcu.rs` is an independent hardware-validated reimplementation.
|
moot, since our `hpmcu.rs` is an independent hardware-validated reimplementation.
|
||||||
|
|
||||||
---
|
---
|
||||||
_Cross-refs: `../CAPABILITIES-AUDIT.md`, `../REMAINING-PORTS.md §7`,
|
_Cross-refs: `../CAPABILITIES-AUDIT.md`, `../REMAINING-PORTS.md section 7`,
|
||||||
`../../luckfox-pico-86-panel/riscv-mcu.md`,
|
`../../luckfox-pico-86-panel/riscv-mcu.md`,
|
||||||
`.../raw/followup-riscv-mcu.md`,
|
`.../raw/followup-riscv-mcu.md`,
|
||||||
`flare-edge/major-app-additions/docs/decisions/0002-hpmcu-watchdog.md`,
|
`flare-edge/major-app-additions/docs/decisions/0002-hpmcu-watchdog.md`,
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# HPMCU mailbox — [x] 100% VERIFIED on warden-c8a3 (2026-08-25)
|
# HPMCU mailbox: [x] 100% VERIFIED on warden-c8a3 (2026-08-25)
|
||||||
|
|
||||||
A fully-open A7 ↔ HPMCU (RISC-V SCR1) hardware-mailbox round-trip on our self-built
|
A fully-open A7 <-> HPMCU (RISC-V SCR1) hardware-mailbox round-trip on our self-built
|
||||||
Linux 6.18.46. Open kernel driver + open SCR1 firmware, **zero blobs**.
|
Linux 6.18.46. Open kernel driver + open SCR1 firmware, **zero blobs**.
|
||||||
|
|
||||||
## Evidence (serial, _b slot = our 6.18)
|
## Evidence (serial, _b slot = our 6.18)
|
||||||
SCR1 echo firmware running: `DBG_STATE = 0x584F424D` ("MBOX"). Five round-trips,
|
SCR1 echo firmware running: `DBG_STATE = 0x584F424D` ("MBOX"). Five round-trips,
|
||||||
Linux → mailbox → SCR1 → mailbox → Linux, **all exact**:
|
Linux -> mailbox -> SCR1 -> mailbox -> Linux, **all exact**:
|
||||||
```
|
```
|
||||||
sent 0x0000beef/0x600df00d -> B2A 0x0000BEEF/0x600DF00D e=4 OK
|
sent 0x0000beef/0x600df00d -> B2A 0x0000BEEF/0x600DF00D e=4 OK
|
||||||
sent 0x0000c0de/0x12345678 -> B2A 0x0000C0DE/0x12345678 e=5 OK
|
sent 0x0000c0de/0x12345678 -> B2A 0x0000C0DE/0x12345678 e=5 OK
|
||||||
@@ -15,7 +15,7 @@ sent 0x0000aa55/0x55aa55aa -> B2A 0x0000AA55/0x55AA55AA e=8 OK
|
|||||||
```
|
```
|
||||||
Echo counter increments 1:1 with sends; both CMD and DAT echo back verbatim.
|
Echo counter increments 1:1 with sends; both CMD and DAT echo back verbatim.
|
||||||
|
|
||||||
## The three fixes it took (none in the research plan — found on hardware)
|
## The three fixes it took (none in the research plan, found on hardware)
|
||||||
1. **Controller IRQ count** (`rockchip-mailbox.c`): the rv1106 mailbox has ONE
|
1. **Controller IRQ count** (`rockchip-mailbox.c`): the rv1106 mailbox has ONE
|
||||||
shared IRQ (GIC_SPI 1), but the rk3368 driver-data assumes one IRQ per channel
|
shared IRQ (GIC_SPI 1), but the rk3368 driver-data assumes one IRQ per channel
|
||||||
(num_chans=4) and probe failed `IRQ index 1 not found`. Added an
|
(num_chans=4) and probe failed `IRQ index 1 not found`. Added an
|
||||||
@@ -27,7 +27,7 @@ Echo counter increments 1:1 with sends; both CMD and DAT echo back verbatim.
|
|||||||
released core never executed (DBG_STATE stayed 0). Marked it `CLK_IGNORE_UNUSED`.
|
released core never executed (DBG_STATE stayed 0). Marked it `CLK_IGNORE_UNUSED`.
|
||||||
(5.10 happened to leave it on.)
|
(5.10 happened to leave it on.)
|
||||||
3. **A2B doorbell semantics** (SCR1 firmware + send order): the MCU-side receiver
|
3. **A2B doorbell semantics** (SCR1 firmware + send order): the MCU-side receiver
|
||||||
must set `A2B_INTEN` or an A7 write to A2B_CMD never raises A2B_STATUS — the echo
|
must set `A2B_INTEN` or an A7 write to A2B_CMD never raises A2B_STATUS: the echo
|
||||||
firmware now sets `A2B_INTEN` at init. And the A2B_DAT write is the doorbell, so
|
firmware now sets `A2B_INTEN` at init. And the A2B_DAT write is the doorbell, so
|
||||||
the sender writes CMD first, then DAT (the mainline order), so both are current
|
the sender writes CMD first, then DAT (the mainline order), so both are current
|
||||||
when the SCR1 reads them.
|
when the SCR1 reads them.
|
||||||
@@ -35,8 +35,8 @@ Echo counter increments 1:1 with sends; both CMD and DAT echo back verbatim.
|
|||||||
## Loading the firmware (no reflash, no brick)
|
## Loading the firmware (no reflash, no brick)
|
||||||
The SCR1 echo firmware (`scr1-echo/`, 154 B) is loaded at runtime into hpmcu_sram
|
The SCR1 echo firmware (`scr1-echo/`, 154 B) is loaded at runtime into hpmcu_sram
|
||||||
(0xFF6FE000) via the proven `flared/hpmcu.rs` sequence (`load-echo-fw.sh`): GRF
|
(0xFF6FE000) via the proven `flared/hpmcu.rs` sequence (`load-echo-fw.sh`): GRF
|
||||||
uncached peripheral window (0xff040024/28) → CORECRU hold → firmware to SRAM →
|
uncached peripheral window (0xff040024/28) -> CORECRU hold -> firmware to SRAM ->
|
||||||
SGRF boot addr → CORECRU release. **Do NOT kill warden-flared** (it one-shot-loads
|
SGRF boot addr -> CORECRU release. **Do NOT kill warden-flared** (it one-shot-loads
|
||||||
at boot then just beats + pets the dw-wdt; killing it resets the board). We reset
|
at boot then just beats + pets the dw-wdt; killing it resets the board). We reset
|
||||||
+ reload the SCR1; flared does not re-load. The SRAM-load path avoids the 0x40000
|
+ reload the SCR1; flared does not re-load. The SRAM-load path avoids the 0x40000
|
||||||
boot-load brick hazard entirely.
|
boot-load brick hazard entirely.
|
||||||
@@ -47,7 +47,7 @@ HPMCU-connected instance). The existing /dev/mem SRAM watchdog is untouched (a
|
|||||||
separate dead-man's-switch); the mailbox is the general open IPC channel.
|
separate dead-man's-switch); the mailbox is the general open IPC channel.
|
||||||
|
|
||||||
## Files
|
## Files
|
||||||
- `scr1-echo/main.c` + `start.S` + `link.lds` + `Makefile` — the open SCR1 echo fw.
|
- `scr1-echo/main.c` + `start.S` + `link.lds` + `Makefile`: the open SCR1 echo fw.
|
||||||
- `scr1-echo/load-echo-fw.sh`, `mbox-roundtrip.sh`, `echo-fw-words.txt` — load + test.
|
- `scr1-echo/load-echo-fw.sh`, `mbox-roundtrip.sh`, `echo-fw-words.txt`: load + test.
|
||||||
- Kernel deltas: `rockchip-mailbox.c` (rv1106 num_chans=1), `clk-rv1106.c`
|
- Kernel deltas: `rockchip-mailbox.c` (rv1106 num_chans=1), `clk-rv1106.c`
|
||||||
(CLK_CORE_MCU IGNORE_UNUSED), `rv1106-warden.dts` (&mailbox okay).
|
(CLK_CORE_MCU IGNORE_UNUSED), `rv1106-warden.dts` (&mailbox okay).
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# HPMCU mailbox-echo firmware — bare-metal RV32IMC for the RV1106 SCR1 core.
|
# HPMCU mailbox-echo firmware: bare-metal RV32IMC for the RV1106 SCR1 core.
|
||||||
# Same xPack riscv-none-embed-gcc 10.2.0 + flags as the watchdog firmware.
|
# Same xPack riscv-none-embed-gcc 10.2.0 + flags as the watchdog firmware.
|
||||||
|
|
||||||
# Point XPACK at the xpack riscv toolchain bin/ inside a flare-edge SDK checkout.
|
# Point XPACK at the xpack riscv toolchain bin/ inside a flare-edge SDK checkout.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* HPMCU watchdog supervisor — linked into the hpmcu_sram carve-out.
|
* HPMCU watchdog supervisor: linked into the hpmcu_sram carve-out.
|
||||||
*
|
*
|
||||||
* The RV1106's 256K on-chip SRAM at 0xFF6C0000 is split by the kernel DT
|
* The RV1106's 256K on-chip SRAM at 0xFF6C0000 is split by the kernel DT
|
||||||
* (rv1106.dtsi sram@ff6c0000) into rkisp_sram (0x0..0x3e000) and hpmcu_sram
|
* (rv1106.dtsi sram@ff6c0000) into rkisp_sram (0x0..0x3e000) and hpmcu_sram
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
FW=${1:-/userdata/echo-fw-words.txt}
|
FW=${1:-/userdata/echo-fw-words.txt}
|
||||||
|
|
||||||
# GRF uncached peripheral window (covers CRU + this SRAM + the mailbox
|
# GRF uncached peripheral window (covers CRU + this SRAM + the mailbox
|
||||||
# 0xff5c0000) — WITHOUT this the MCU's peripheral/SRAM accesses are cached and
|
# 0xff5c0000): WITHOUT this the MCU's peripheral/SRAM accesses are cached and
|
||||||
# invisible to Linux. hpmcu.rs: GRF_BASE 0xff040000 +0x24/+0x28 = 0xff000/0xffc00.
|
# invisible to Linux. hpmcu.rs: GRF_BASE 0xff040000 +0x24/+0x28 = 0xff000/0xffc00.
|
||||||
devmem 0xff040024 32 0xff000
|
devmem 0xff040024 32 0xff000
|
||||||
devmem 0xff040028 32 0xffc00
|
devmem 0xff040028 32 0xffc00
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
* watchdog SRAM mailbox relies on).
|
* watchdog SRAM mailbox relies on).
|
||||||
*
|
*
|
||||||
* Loaded + started by the proven flared/hpmcu.rs SRAM sequence
|
* Loaded + started by the proven flared/hpmcu.rs SRAM sequence
|
||||||
* (0xFF6FE000 load addr, CORECRU reset hold/release) — NOT the 0x40000
|
* (0xFF6FE000 load addr, CORECRU reset hold/release), NOT the 0x40000
|
||||||
* boot-load path (that bricks a non-TB board; see boot-loaded-mcu-0x40000
|
* boot-load path (that bricks a non-TB board; see boot-loaded-mcu-0x40000
|
||||||
* hazard). Fits the 8K hpmcu_sram budget (this is a few hundred bytes).
|
* hazard). Fits the 8K hpmcu_sram budget (this is a few hundred bytes).
|
||||||
*/
|
*/
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
#define REG32(a) (*(volatile uint32_t *)(uintptr_t)(a))
|
#define REG32(a) (*(volatile uint32_t *)(uintptr_t)(a))
|
||||||
|
|
||||||
/* Mailbox IP (HPMCU-connected instance) — offsets match rockchip-mailbox.c. */
|
/* Mailbox IP (HPMCU-connected instance): offsets match rockchip-mailbox.c. */
|
||||||
#define MBOX_BASE 0xFF5C0000u
|
#define MBOX_BASE 0xFF5C0000u
|
||||||
#define A2B_INTEN REG32(MBOX_BASE + 0x00)
|
#define A2B_INTEN REG32(MBOX_BASE + 0x00)
|
||||||
#define A2B_STATUS REG32(MBOX_BASE + 0x04)
|
#define A2B_STATUS REG32(MBOX_BASE + 0x04)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Prove the A7<->HPMCU mailbox round-trip on channel 0. Write A2B_CMD first, then
|
# Prove the A7<->HPMCU mailbox round-trip on channel 0. Write A2B_CMD first, then
|
||||||
# A2B_DAT — the A2B_DAT write is the doorbell (raises A2B_STATUS on the MCU side,
|
# A2B_DAT: the A2B_DAT write is the doorbell (raises A2B_STATUS on the MCU side,
|
||||||
# whose echo firmware has enabled A2B_INTEN), so both CMD and DAT are current when
|
# whose echo firmware has enabled A2B_INTEN), so both CMD and DAT are current when
|
||||||
# the SCR1 reads them. The SCR1 echoes {cmd,dat} verbatim into B2A_CMD/B2A_DAT.
|
# the SCR1 reads them. The SCR1 echoes {cmd,dat} verbatim into B2A_CMD/B2A_DAT.
|
||||||
CMD=${1:-0x00001234}
|
CMD=${1:-0x00001234}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
# Open NPU to 100% — plan, feasibility, and the honest ceiling
|
# Open NPU to 100%: plan, feasibility, and the honest ceiling
|
||||||
|
|
||||||
**Goal as stated:** take the RV1106 NPU to "100% — open source, verified on
|
**Goal as stated:** take the RV1106 NPU to "100%, open source, verified on
|
||||||
hardware." **Bottom line up front:** two very different things live under that
|
hardware." **Bottom line up front:** two very different things live under that
|
||||||
sentence, and only one of them is reachable soon.
|
sentence, and only one of them is reachable soon.
|
||||||
|
|
||||||
1. **Open, on-hardware-verified _kernel driver_** (`/dev/dri/cardN` binds, answers
|
1. **Open, on-hardware-verified _kernel driver_** (`/dev/dri/cardN` binds, answers
|
||||||
a version-query ioctl on our self-built Linux 6.18.46). **Achievable now**,
|
a version-query ioctl on our self-built Linux 6.18.46). **Achievable now**,
|
||||||
low-single-digit engineer-days, license-clean GPL forward-port. This is the
|
low-single-digit engineer-days, license-clean GPL forward-port. This is the
|
||||||
`PORT-PLAN.md` work in this directory — a real, bounded milestone (M6).
|
`PORT-PLAN.md` work in this directory, a real, bounded milestone (M6).
|
||||||
2. **Open _userspace_ that runs a real model** (an open compiler/encoder emitting a
|
2. **Open _userspace_ that runs a real model** (an open compiler/encoder emitting a
|
||||||
valid `regcmd` stream this driver can submit, no closed `librknnrt`, no `.rknn`
|
valid `regcmd` stream this driver can submit, no closed `librknnrt`, no `.rknn`
|
||||||
blob). **Not achievable on any near horizon.** It is a from-scratch,
|
blob). **Not achievable on any near horizon.** It is a from-scratch,
|
||||||
per-SoC register reverse-engineering project with **zero existing public prior
|
per-SoC register reverse-engineering project with **zero existing public prior
|
||||||
art for RV1106/RV1103** — the least-covered tier of the entire RKNPU family.
|
art for RV1106/RV1103**, the least-covered tier of the entire RKNPU family.
|
||||||
|
|
||||||
This document scopes both, states the most ambitious end state that is actually
|
This document scopes both, states the most ambitious end state that is actually
|
||||||
achievable, and defines the concrete first milestone toward open compute.
|
achievable, and defines the concrete first milestone toward open compute.
|
||||||
@@ -23,7 +23,7 @@ document is the strategic wrapper and the item-(2) reality check. Read both.
|
|||||||
|
|
||||||
## 1. Brutally honest feasibility
|
## 1. Brutally honest feasibility
|
||||||
|
|
||||||
### 1.1 "Port the GPL kernel driver" — TRACTABLE (do it)
|
### 1.1 "Port the GPL kernel driver": TRACTABLE (do it)
|
||||||
|
|
||||||
- The vendor driver (`flare-edge/sdk/sysdrv/source/kernel/drivers/rknpu/`, v0.9.2,
|
- The vendor driver (`flare-edge/sdk/sysdrv/source/kernel/drivers/rknpu/`, v0.9.2,
|
||||||
`DRIVER_DATE 20230825`) is **SPDX GPL-2.0 at the file level**, `MODULE_LICENSE("GPL v2")`
|
`DRIVER_DATE 20230825`) is **SPDX GPL-2.0 at the file level**, `MODULE_LICENSE("GPL v2")`
|
||||||
@@ -35,26 +35,26 @@ document is the strategic wrapper and the item-(2) reality check. Read both.
|
|||||||
`dma_mask=32`, `pc_data_amount_scale=2`, `pc_task_number_bits=16`,
|
`dma_mask=32`, `pc_data_amount_scale=2`, `pc_task_number_bits=16`,
|
||||||
`pc_task_status_offset=0x3c`, single-core `irqs`/`resets` arrays,
|
`pc_task_status_offset=0x3c`, single-core `irqs`/`resets` arrays,
|
||||||
`nbuf_phyaddr=0/nbuf_size=0`).
|
`nbuf_phyaddr=0/nbuf_size=0`).
|
||||||
- The DT node is **already fully specified** in the base tree, only disabled —
|
- The DT node is **already fully specified** in the base tree, only disabled:
|
||||||
`npu@ff660000`, `reg=<0xff660000 0x10000>`, `GIC_SPI 109`, clocks
|
`npu@ff660000`, `reg=<0xff660000 0x10000>`, `GIC_SPI 109`, clocks
|
||||||
`ACLK_RKNN`/`HCLK_RKNN`, `assigned-clock-rates=<420000000>`, resets
|
`ACLK_RKNN`/`HCLK_RKNN`, `assigned-clock-rates=<420000000>`, resets
|
||||||
`SRST_A_RKNN`/`SRST_H_RKNN`, `status="disabled"` (`rv1106.dtsi:1127-1138`). One
|
`SRST_A_RKNN`/`SRST_H_RKNN`, `status="disabled"` (`rv1106.dtsi:1127-1138`). One
|
||||||
board-DTS override (`&npu { status="okay"; }`) enables it.
|
board-DTS override (`&npu { status="okay"; }`) enables it.
|
||||||
- **No IOMMU** on this board (`"rknpu iommu device-tree entry not found!, using
|
- **No IOMMU** on this board (`"rknpu iommu device-tree entry not found!, using
|
||||||
non-iommu mode"` — CMA/contiguous DMA only), **no power-domains** (single-rail),
|
non-iommu mode"`, CMA/contiguous DMA only), **no power-domains** (single-rail),
|
||||||
**no OPP table** (fixed clock). This _shrinks_ the port: the multi-domain/DVFS/
|
**no OPP table** (fixed clock). This _shrinks_ the port: the multi-domain/DVFS/
|
||||||
thermal code is all provably dead for RV1106's DT.
|
thermal code is all provably dead for RV1106's DT.
|
||||||
- The one real build blocker is four vendor-only downstream headers
|
- The one real build blocker is four vendor-only downstream headers
|
||||||
(`soc/rockchip/rockchip_{iommu,opp_select,system_monitor,ipa}.h`) absent from
|
(`soc/rockchip/rockchip_{iommu,opp_select,system_monitor,ipa}.h`) absent from
|
||||||
mainline 6.18 — all four back **dead-code call sites** for this SoC, fixable with
|
mainline 6.18: all four back **dead-code call sites** for this SoC, fixable with
|
||||||
small local compat-shim stubs (same pattern already used for `clk-rv1106.c`'s
|
small local compat-shim stubs (same pattern already used for `clk-rv1106.c`'s
|
||||||
`panic_notifier_list` move). Full delta in `PORT-PLAN.md §2.4`.
|
`panic_notifier_list` move). Full delta in `PORT-PLAN.md section 2.4`.
|
||||||
|
|
||||||
**Verdict: real, bounded, worth doing.** Same class as the RGA port. Ends at a
|
**Verdict: real, bounded, worth doing.** Same class as the RGA port. Ends at a
|
||||||
clean probe + `RKNPU_GET_DRV_VERSION`/`RKNPU_GET_HW_VERSION` ioctl round-trip on
|
clean probe + `RKNPU_GET_DRV_VERSION`/`RKNPU_GET_HW_VERSION` ioctl round-trip on
|
||||||
hardware — **and stops there**, because of §1.2.
|
hardware, **and stops there**, because of section 1.2.
|
||||||
|
|
||||||
### 1.2 "Open userspace that runs a model" — HARD → effectively BLOCKED for RV1106
|
### 1.2 "Open userspace that runs a model": HARD -> effectively BLOCKED for RV1106
|
||||||
|
|
||||||
The kernel driver's entire hardware contract is: power/clock/reset, DMA/GEM buffer
|
The kernel driver's entire hardware contract is: power/clock/reset, DMA/GEM buffer
|
||||||
management, then it drops a **pointer + length to a userspace-authored `regcmd`
|
management, then it drops a **pointer + length to a userspace-authored `regcmd`
|
||||||
@@ -68,20 +68,20 @@ block, from `rknpu_ioctl.h:21-39` used via `REG_WRITE` in `rknpu_job.c`):
|
|||||||
| Offset | Name | Meaning |
|
| Offset | Name | Meaning |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `0x00`/`0x04` | VERSION / VERSION_NUM | read-only HW version |
|
| `0x00`/`0x04` | VERSION / VERSION_NUM | read-only HW version |
|
||||||
| `0x08` | PC_OP_EN | pulse 1→0 kicks the job; `0x1`=slave mode before commit |
|
| `0x08` | PC_OP_EN | pulse 1->0 kicks the job; `0x1`=slave mode before commit |
|
||||||
| `0x10` | PC_DATA_ADDR | device address of the regcmd buffer |
|
| `0x10` | PC_DATA_ADDR | device address of the regcmd buffer |
|
||||||
| `0x14` | PC_DATA_AMOUNT | `(regcfg_amount + 4 + scale-1)/scale - 1` |
|
| `0x14` | PC_DATA_AMOUNT | `(regcfg_amount + 4 + scale-1)/scale - 1` |
|
||||||
| `0x20`/`0x24`/`0x28`/`0x2c` | INT_MASK / INT_CLEAR / INT_STATUS / INT_RAW_STATUS | interrupt handshake |
|
| `0x20`/`0x24`/`0x28`/`0x2c` | INT_MASK / INT_CLEAR / INT_STATUS / INT_RAW_STATUS | interrupt handshake |
|
||||||
| `0x30` | PC_TASK_CONTROL | `((0x6\|pingpong)<<pc_task_number_bits)\|task_number` (the `0x6` is an undecoded fixed mode field) |
|
| `0x30` | PC_TASK_CONTROL | `((0x6\|pingpong)<<pc_task_number_bits)\|task_number` (the `0x6` is an undecoded fixed mode field) |
|
||||||
| `0x34` | PC_DMA_BASE_ADDR | `task_base_addr` |
|
| `0x34` | PC_DMA_BASE_ADDR | `task_base_addr` |
|
||||||
|
|
||||||
**This is the entire openly-documented register map** — the wrapper/sequencer
|
**This is the entire openly-documented register map**: the wrapper/sequencer
|
||||||
only. **None of it describes the compute engine** (convolution/pooling/activation
|
only. **None of it describes the compute engine** (convolution/pooling/activation
|
||||||
config, feature/weight DMA descriptors). Those live _inside_ the opaque regcmd
|
config, feature/weight DMA descriptors). Those live _inside_ the opaque regcmd
|
||||||
buffer. The op-code semantics of that stream:
|
buffer. The op-code semantics of that stream:
|
||||||
|
|
||||||
- **Have no public register-level documentation anywhere.** TRM Part 2 does not
|
- **Have no public register-level documentation anywhere.** TRM Part 2 does not
|
||||||
exist publicly — confirmed by exhausting Rockchip doc mirrors and confirmed
|
exist publicly, confirmed by exhausting Rockchip doc mirrors and confirmed
|
||||||
directly by a Luckfox engineer that **Luckfox itself was never given one.** Even
|
directly by a Luckfox engineer that **Luckfox itself was never given one.** Even
|
||||||
Rockchip's own 90-page ROCKIVA SDK guide is pure userspace-API reference with
|
Rockchip's own 90-page ROCKIVA SDK guide is pure userspace-API reference with
|
||||||
zero register offsets. This is a deliberate architectural gate, not a
|
zero register offsets. This is a deliberate architectural gate, not a
|
||||||
@@ -89,21 +89,21 @@ buffer. The op-code semantics of that stream:
|
|||||||
- **Only the closed RKNN-Toolkit2 compiler emits valid regcmd streams.** The
|
- **Only the closed RKNN-Toolkit2 compiler emits valid regcmd streams.** The
|
||||||
on-device closed `librknnrt` parses `.rknn` blobs and issues the same
|
on-device closed `librknnrt` parses `.rknn` blobs and issues the same
|
||||||
`RKNPU_MEM_*`/`RKNPU_SUBMIT` ioctls. `DRM_IOCTL_RKNPU_SUBMIT` technically accepts
|
`RKNPU_MEM_*`/`RKNPU_SUBMIT` ioctls. `DRM_IOCTL_RKNPU_SUBMIT` technically accepts
|
||||||
a raw command buffer directly — nothing _requires_ `librknnrt` — but since the
|
a raw command buffer directly (nothing _requires_ `librknnrt`), but since the
|
||||||
buffer format is closed, that ioctl is **not** a "write your own kernel" door.
|
buffer format is closed, that ioctl is **not** a "write your own kernel" door.
|
||||||
|
|
||||||
### 1.3 Why there is no shortcut through the existing open efforts
|
### 1.3 Why there is no shortcut through the existing open efforts
|
||||||
|
|
||||||
| Open effort | Chip(s) | Reaches RV1106? |
|
| Open effort | Chip(s) | Reaches RV1106? |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Mainline `accel/rocket` (merged 2025-07, in our `research/linux-6.18.46/`) | RK3588 only, as shipped | **No.** `Kconfig`: `depends on (ARCH_ROCKCHIP && ARM64)`. RV1106 is 32-bit `arch/arm` — excluded before the register question. `of_match` is hardcoded `"rockchip,rk3588-rknn-core"`. |
|
| Mainline `accel/rocket` (merged 2025-07, in our `research/linux-6.18.46/`) | RK3588 only, as shipped | **No.** `Kconfig`: `depends on (ARCH_ROCKCHIP && ARM64)`. RV1106 is 32-bit `arch/arm`: excluded before the register question. `of_match` is hardcoded `"rockchip,rk3588-rknn-core"`. |
|
||||||
| Mesa **Teflon** (TFLite delegate) | Only etnaviv or rocket backends | **No.** Entirely downstream of Rocket — its Rockchip reach _is_ Rocket's reach: RK3588 only. |
|
| Mesa **Teflon** (TFLite delegate) | Only etnaviv or rocket backends | **No.** Entirely downstream of Rocket, its Rockchip reach _is_ Rocket's reach: RK3588 only. |
|
||||||
| `gahingwoo/linux-rk3576-npu` + `charsiu` (active, days-old as of 2026-08) | RK3576 | **No — wrong generation.** MobileNetV1 end-to-end + a Llama-3.2-1B INT4 open runtime (`charsiu`, opens `/dev/accel/accel0`, emits its own register stream) reportedly work byte-exact — but on RK3576's NPU, a **newer generation** than RV1106's. Unmerged (LKML "[PATCH v9] accel/rocket: RK3576" in review; unverifiable by second source — Anubis-walled). |
|
| `gahingwoo/linux-rk3576-npu` + `charsiu` (active, days-old as of 2026-08) | RK3576 | **No, wrong generation.** MobileNetV1 end-to-end + a Llama-3.2-1B INT4 open runtime (`charsiu`, opens `/dev/accel/accel0`, emits its own register stream) reportedly work byte-exact, but on RK3576's NPU, a **newer generation** than RV1106's. Unmerged (LKML "[PATCH v9] accel/rocket: RK3576" in review; unverifiable by second source, Anubis-walled). |
|
||||||
| RK3568/RK3566 Armbian fork | RK3568 | **No.** Built by byte-level diff against captured vendor command streams; multi-task still imperfect; unmerged. |
|
| RK3568/RK3566 Armbian fork | RK3568 | **No.** Built by byte-level diff against captured vendor command streams; multi-task still imperfect; unmerged. |
|
||||||
| `phhusson/rknpu-reverse-engineering` | RK3588 only, dormant since 2024-03, no license | **No.** Exploratory `strings`/`strace`/GEM-dump notes; **no compiler, no encoder, no runtime.** ~1 person-year got RK3588 to "structures identified." |
|
| `phhusson/rknpu-reverse-engineering` | RK3588 only, dormant since 2024-03, no license | **No.** Exploratory `strings`/`strace`/GEM-dump notes; **no compiler, no encoder, no runtime.** ~1 person-year got RK3588 to "structures identified." |
|
||||||
| **RV1106/RV1103** | — | **Zero hits of any kind.** No driver, no fork, no RE writeup, no captured traces. Least-covered tier in the family. |
|
| **RV1106/RV1103** | n/a | **Zero hits of any kind.** No driver, no fork, no RE writeup, no captured traces. Least-covered tier in the family. |
|
||||||
|
|
||||||
RV1106 sits in the RKNPU2 **"v1" family** (RK3566/68, RV1109/26, RV1103/06 —
|
RV1106 sits in the RKNPU2 **"v1" family** (RK3566/68, RV1109/26, RV1103/06,
|
||||||
INT8-only, `quantize=8` mandatory). RK3576/RK3588 are a **different, newer**
|
INT8-only, `quantize=8` mandatory). RK3576/RK3588 are a **different, newer**
|
||||||
generation. Precedent (RK3568 needing byte-level RE off RK3588) shows this is a
|
generation. Precedent (RK3568 needing byte-level RE off RK3588) shows this is a
|
||||||
**bespoke per-SoC RE project even between adjacent generations, not a recompile.**
|
**bespoke per-SoC RE project even between adjacent generations, not a recompile.**
|
||||||
@@ -112,36 +112,36 @@ generation. Precedent (RK3568 needing byte-level RE off RK3588) shows this is a
|
|||||||
|
|
||||||
Ranked by realism:
|
Ranked by realism:
|
||||||
|
|
||||||
- **Tier A — ACHIEVABLE NOW (commit to this):** open GPL kernel driver, statically
|
- **Tier A, ACHIEVABLE NOW (commit to this):** open GPL kernel driver, statically
|
||||||
built into our 6.18.46, `/dev/dri/cardN` binds, `/proc/rknpu/load` live for the
|
built into our 6.18.46, `/dev/dri/cardN` binds, `/proc/rknpu/load` live for the
|
||||||
Monitor page, `RKNPU_GET_HW_VERSION`/`RKNPU_GET_DRV_VERSION` verified on
|
Monitor page, `RKNPU_GET_HW_VERSION`/`RKNPU_GET_DRV_VERSION` verified on
|
||||||
hardware. **No blob shipped.** This is item (1); it is the honest "100% open" for
|
hardware. **No blob shipped.** This is item (1); it is the honest "100% open" for
|
||||||
the _driver_.
|
the _driver_.
|
||||||
- **Tier B — ACHIEVABLE AS A RESEARCH SPIKE, bounded, low-value:** a **single
|
- **Tier B, ACHIEVABLE AS A RESEARCH SPIKE, bounded, low-value:** a **single
|
||||||
hand-written op** (one conv or one matmul) executed via a `regcmd` **captured**
|
hand-written op** (one conv or one matmul) executed via a `regcmd` **captured**
|
||||||
from the closed stack and **replayed** through our open `DRM_IOCTL_RKNPU_SUBMIT`,
|
from the closed stack and **replayed** through our open `DRM_IOCTL_RKNPU_SUBMIT`,
|
||||||
byte-for-byte, with an open host tool — proving the open submit path can drive
|
byte-for-byte, with an open host tool: proving the open submit path can drive
|
||||||
real compute. This does **not** require understanding the regcmd ISA; it requires
|
real compute. This does **not** require understanding the regcmd ISA; it requires
|
||||||
capturing one. It is the concrete first milestone toward open compute (§3, M-NPU-2)
|
capturing one. It is the concrete first milestone toward open compute (section 3, M-NPU-2)
|
||||||
and the only way to get an on-hardware compute number without the RE project.
|
and the only way to get an on-hardware compute number without the RE project.
|
||||||
- **Tier C — NOT ACHIEVABLE on any near horizon:** an open **compiler/encoder** that
|
- **Tier C, NOT ACHIEVABLE on any near horizon:** an open **compiler/encoder** that
|
||||||
emits novel regcmd streams for arbitrary models. This is the ~person-year+
|
emits novel regcmd streams for arbitrary models. This is the ~person-year+
|
||||||
from-scratch RE project (§3, M-NPU-3+). We should **scope it, not staff it**,
|
from-scratch RE project (section 3, M-NPU-3+). We should **scope it, not staff it**,
|
||||||
unless a strategic reason to own open NPU compute emerges.
|
unless a strategic reason to own open NPU compute emerges.
|
||||||
|
|
||||||
**Honest recommendation:** ship Tier A. Do Tier B only as a time-boxed spike _if_ a
|
**Honest recommendation:** ship Tier A. Do Tier B only as a time-boxed spike _if_ a
|
||||||
real non-visual inference use case (audio/RS-485/touch-gesture classifier — the
|
real non-visual inference use case (audio/RS-485/touch-gesture classifier, the
|
||||||
only sensible NPU use here; no camera exists on this board) is actually wanted.
|
only sensible NPU use here; no camera exists on this board) is actually wanted.
|
||||||
Treat Tier C as closed-vendor-runtime-only for the foreseeable future. This matches
|
Treat Tier C as closed-vendor-runtime-only for the foreseeable future. This matches
|
||||||
the standing `PROVENANCE.md` policy: **we do not ship the closed runtime blob**;
|
the standing `PROVENANCE.md` policy: **we do not ship the closed runtime blob**;
|
||||||
the open path is a from-scratch regcmd encoder tracked separately, never a vendored
|
the open path is a from-scratch regcmd encoder tracked separately, never a vendored
|
||||||
binary — and until that encoder exists, open NPU _compute_ does not exist for us.
|
binary, and until that encoder exists, open NPU _compute_ does not exist for us.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Key technical facts (for the implementer)
|
## 2. Key technical facts (for the implementer)
|
||||||
|
|
||||||
### Register / memory map (TRM Part1 — Part2 does not exist publicly)
|
### Register / memory map (TRM Part1: Part2 does not exist publicly)
|
||||||
|
|
||||||
| Block | Base | Size |
|
| Block | Base | Size |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -154,13 +154,13 @@ binary — and until that encoder exists, open NPU _compute_ does not exist for
|
|||||||
IRQ `GIC_SPI 109 LEVEL_HIGH`, node `ff660000.npu`. Clocks `ACLK_RKNN`/`HCLK_RKNN`
|
IRQ `GIC_SPI 109 LEVEL_HIGH`, node `ff660000.npu`. Clocks `ACLK_RKNN`/`HCLK_RKNN`
|
||||||
off the CRU matrix; only measured clock point anywhere is **NPU 500MHz** (Rockchip
|
off the CRU matrix; only measured clock point anywhere is **NPU 500MHz** (Rockchip
|
||||||
internal power-test xlsx, "typical IPC workload" corner). A commented-out board-DTS
|
internal power-test xlsx, "typical IPC workload" corner). A commented-out board-DTS
|
||||||
override would set 700MHz — never applied.
|
override would set 700MHz, never applied.
|
||||||
|
|
||||||
### UAPI (the seam an open userspace must target)
|
### UAPI (the seam an open userspace must target)
|
||||||
|
|
||||||
Two memory-manager paths, Kconfig-selected. **RV1106's shipping defconfig picks
|
Two memory-manager paths, Kconfig-selected. **RV1106's shipping defconfig picks
|
||||||
`CONFIG_ROCKCHIP_RKNPU_DMA_HEAP=y`** (`/dev/rknpu` misc device, bare `IOCTL_RKNPU_*`
|
`CONFIG_ROCKCHIP_RKNPU_DMA_HEAP=y`** (`/dev/rknpu` misc device, bare `IOCTL_RKNPU_*`
|
||||||
codes, magic `'r'`) — but `PORT-PLAN.md` deliberately chooses **DRM_GEM** for the
|
codes, magic `'r'`), but `PORT-PLAN.md` deliberately chooses **DRM_GEM** for the
|
||||||
6.18 port (`/dev/dri/cardN`, `DRM_IOCTL_RKNPU_*`, `DRM_RENDER_ALLOW`), because M4
|
6.18 port (`/dev/dri/cardN`, `DRM_IOCTL_RKNPU_*`, `DRM_RENDER_ALLOW`), because M4
|
||||||
already pulls DRM in for the panel and DRM_GEM is the vendor default. Same struct
|
already pulls DRM in for the panel and DRM_GEM is the vendor default. Same struct
|
||||||
payloads either way. Six ioctls: `RKNPU_ACTION`, `RKNPU_SUBMIT`,
|
payloads either way. Six ioctls: `RKNPU_ACTION`, `RKNPU_SUBMIT`,
|
||||||
@@ -182,16 +182,16 @@ struct rknpu_submit { // rknpu_ioctl.h:260-274
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
An open userspace must produce: **(a) the `struct rknpu_task[]` list — fully
|
An open userspace must produce: **(a) the `struct rknpu_task[]` list, fully
|
||||||
inferable from the kernel driver alone, trivial;** and **(b) the regcmd byte stream
|
inferable from the kernel driver alone, trivial;** and **(b) the regcmd byte stream
|
||||||
each task points to — NOT inferable from the driver or any published TRM.** (b) is
|
each task points to, NOT inferable from the driver or any published TRM.** (b) is
|
||||||
the whole problem.
|
the whole problem.
|
||||||
|
|
||||||
### `/proc/rknpu` — safety (hardware-confirmed)
|
### `/proc/rknpu`: safety (hardware-confirmed)
|
||||||
|
|
||||||
- `/proc/rknpu/load` — **safe** (50 clean reads verified); the Monitor page polls it.
|
- `/proc/rknpu/load`: **safe** (50 clean reads verified); the Monitor page polls it.
|
||||||
- `/proc/rknpu/freq`, `/power` — exposed, unverified.
|
- `/proc/rknpu/freq`, `/power`: exposed, unverified.
|
||||||
- `/proc/rknpu/volt` — **CONFIRMED SIGSEGV** (null deref — no regulator wired). Never poll.
|
- `/proc/rknpu/volt`: **CONFIRMED SIGSEGV** (null deref, no regulator wired). Never poll.
|
||||||
|
|
||||||
### Provenance / policy
|
### Provenance / policy
|
||||||
|
|
||||||
@@ -215,26 +215,26 @@ open encoder exists.
|
|||||||
|
|
||||||
## 3. Milestones
|
## 3. Milestones
|
||||||
|
|
||||||
### M-NPU-1 — Open kernel driver, verified on hardware (ACHIEVABLE — the real deliverable)
|
### M-NPU-1: Open kernel driver, verified on hardware (ACHIEVABLE, the real deliverable)
|
||||||
|
|
||||||
Follows `PORT-PLAN.md` in full. Summary of the gates:
|
Follows `PORT-PLAN.md` in full. Summary of the gates:
|
||||||
|
|
||||||
1. **Build**: `CONFIG_ROCKCHIP_RKNPU=y` + `_DRM_GEM=y` + `_DEBUG_FS`/`_PROC_FS`;
|
1. **Build**: `CONFIG_ROCKCHIP_RKNPU=y` + `_DRM_GEM=y` + `_DEBUG_FS`/`_PROC_FS`;
|
||||||
compile `rknpu_{drv,job,gem,reset,iommu,debugger}.o` clean into `built-in.a`.
|
compile `rknpu_{drv,job,gem,reset,iommu,debugger}.o` clean into `built-in.a`.
|
||||||
Prove the four §2.4 compat shims (`compat/soc/rockchip/rockchip_{iommu,opp_select,
|
Prove the four section 2.4 compat shims (`compat/soc/rockchip/rockchip_{iommu,opp_select,
|
||||||
system_monitor,ipa}.h`) against real 6.18 headers — this is where they get
|
system_monitor,ipa}.h`) against real 6.18 headers: this is where they get
|
||||||
verified, not just inspected.
|
verified, not just inspected.
|
||||||
2. **DT**: board override `&npu { status="okay"; }`; `dtc -W` clean; re-check the
|
2. **DT**: board override `&npu { status="okay"; }`; `dtc -W` clean; re-check the
|
||||||
"place the fdt high" DTB-placement fix (bigger `built-in.a`).
|
"place the fdt high" DTB-placement fix (bigger `built-in.a`).
|
||||||
3. **Boot** via the `warden-c8a3` `_b`-slot one-shot loop (never touches `_a`,
|
3. **Boot** via the `warden-c8a3` `_b`-slot one-shot loop (never touches `_a`,
|
||||||
auto-reverts on hang): `dmesg | grep -i rknpu` shows clean probe — clock/reset/
|
auto-reverts on hang): `dmesg | grep -i rknpu` shows clean probe, clock/reset/
|
||||||
IRQ acquired, no panic, no `-EPROBE_DEFER` stall.
|
IRQ acquired, no panic, no `-EPROBE_DEFER` stall.
|
||||||
4. **Node**: `/dev/dri/cardN` (or `renderD1xx`) appears — classic DRM node, **not**
|
4. **Node**: `/dev/dri/cardN` (or `renderD1xx`) appears, classic DRM node, **not**
|
||||||
`/dev/accel/`.
|
`/dev/accel/`.
|
||||||
5. **Verify ioctl**: host-buildable C program opens the node, issues
|
5. **Verify ioctl**: host-buildable C program opens the node, issues
|
||||||
`DRM_IOCTL_RKNPU_ACTION {.flags=RKNPU_GET_DRV_VERSION}`, checks `value` decodes
|
`DRM_IOCTL_RKNPU_ACTION {.flags=RKNPU_GET_DRV_VERSION}`, checks `value` decodes
|
||||||
to `0.9.2`; `RKNPU_GET_HW_VERSION` returns something plausible. Exercises full
|
to `0.9.2`; `RKNPU_GET_HW_VERSION` returns something plausible. Exercises full
|
||||||
dispatch → power-get/put → clock/reset with no regcmd dependency.
|
dispatch -> power-get/put -> clock/reset with no regcmd dependency.
|
||||||
6. **Capture the durable delta** as a patch series in this dir (compat shims + DT
|
6. **Capture the durable delta** as a patch series in this dir (compat shims + DT
|
||||||
fragment + config fragment); vendor source stays in `research/linux-6.18.46/`.
|
fragment + config fragment); vendor source stays in `research/linux-6.18.46/`.
|
||||||
|
|
||||||
@@ -242,32 +242,32 @@ Follows `PORT-PLAN.md` in full. Summary of the gates:
|
|||||||
not surfaced by the read-through; DTB placement. **This is "NPU driver = 100% open,
|
not surfaced by the read-through; DTB placement. **This is "NPU driver = 100% open,
|
||||||
verified."** Done here for the driver half.
|
verified."** Done here for the driver half.
|
||||||
|
|
||||||
### M-NPU-2 — First open compute: capture-and-replay one op (SPIKE, only if wanted)
|
### M-NPU-2, First open compute: capture-and-replay one op (SPIKE, only if wanted)
|
||||||
|
|
||||||
The concrete first milestone toward open _compute_, and the honest Tier-B ceiling.
|
The concrete first milestone toward open _compute_, and the honest Tier-B ceiling.
|
||||||
It does **not** require decoding the ISA.
|
It does **not** require decoding the ISA.
|
||||||
|
|
||||||
1. On a unit with the closed stack available (build host / a dev panel with the
|
1. On a unit with the closed stack available (build host / a dev panel with the
|
||||||
closed `librknnrt` + a trivial single-conv or single-matmul `.rknn`), capture
|
closed `librknnrt` + a trivial single-conv or single-matmul `.rknn`), capture
|
||||||
the regcmd buffer(s) the runtime DMAs in — via `strace` of the `RKNPU_MEM_*`/
|
the regcmd buffer(s) the runtime DMAs in: via `strace` of the `RKNPU_MEM_*`/
|
||||||
`RKNPU_SUBMIT` ioctls + `GEM_FLINK`/`GEM_OPEN` buffer dumps (phhusson's method),
|
`RKNPU_SUBMIT` ioctls + `GEM_FLINK`/`GEM_OPEN` buffer dumps (phhusson's method),
|
||||||
plus the input/output tensor buffers.
|
plus the input/output tensor buffers.
|
||||||
2. Write an **open host tool** that: allocates the same GEM buffers via
|
2. Write an **open host tool** that: allocates the same GEM buffers via
|
||||||
`RKNPU_MEM_CREATE`, writes the captured regcmd bytes + captured input tensor,
|
`RKNPU_MEM_CREATE`, writes the captured regcmd bytes + captured input tensor,
|
||||||
builds the `struct rknpu_task[]` (fully open, §2), and submits via
|
builds the `struct rknpu_task[]` (fully open, section 2), and submits via
|
||||||
`DRM_IOCTL_RKNPU_SUBMIT` on our M-NPU-1 driver.
|
`DRM_IOCTL_RKNPU_SUBMIT` on our M-NPU-1 driver.
|
||||||
3. **Verify**: output tensor matches the closed runtime's output **byte-for-byte**
|
3. **Verify**: output tensor matches the closed runtime's output **byte-for-byte**
|
||||||
on hardware, and matches a CPU/NEON reference for the same op. Record the
|
on hardware, and matches a CPU/NEON reference for the same op. Record the
|
||||||
**measured job-submission latency** (ioctl+DMA+blocking-IRQ) — a number that
|
**measured job-submission latency** (ioctl+DMA+blocking-IRQ), a number that
|
||||||
exists nowhere today and settles the "NPU vs NEON dispatch overhead" argument.
|
exists nowhere today and settles the "NPU vs NEON dispatch overhead" argument.
|
||||||
|
|
||||||
**Deliverable:** proof the open submit path drives real compute + first latency
|
**Deliverable:** proof the open submit path drives real compute + first latency
|
||||||
number. **Not** a general runtime — the regcmd is a fixed captured blob for one op
|
number. **Not** a general runtime: the regcmd is a fixed captured blob for one op
|
||||||
shape. **Value:** low; do only as a time-boxed research spike behind a real
|
shape. **Value:** low; do only as a time-boxed research spike behind a real
|
||||||
use case. **Never ship a captured Rockchip regcmd as a product artifact** (it is
|
use case. **Never ship a captured Rockchip regcmd as a product artifact** (it is
|
||||||
their compiler's output) — this stays in `research/`.
|
their compiler's output), this stays in `research/`.
|
||||||
|
|
||||||
### M-NPU-3+ — Open regcmd encoder (SCOPE ONLY — do not staff without a strategic reason)
|
### M-NPU-3+: Open regcmd encoder (SCOPE ONLY, do not staff without a strategic reason)
|
||||||
|
|
||||||
The Tier-C from-scratch RE project. Method (the only demonstrated one):
|
The Tier-C from-scratch RE project. Method (the only demonstrated one):
|
||||||
differentially decode captured regcmd streams across many hand-built minimal ONNX
|
differentially decode captured regcmd streams across many hand-built minimal ONNX
|
||||||
@@ -286,14 +286,14 @@ ever begun; otherwise this is the documented reason open NPU compute is deferred
|
|||||||
jobs; it delivers **no open way to produce a valid job.** Guard this in every
|
jobs; it delivers **no open way to produce a valid job.** Guard this in every
|
||||||
status line.
|
status line.
|
||||||
- Additional vendor-only symbols beyond the four headers may surface only at
|
- Additional vendor-only symbols beyond the four headers may surface only at
|
||||||
compile — M-NPU-1 gate 1 is the real test.
|
compile: M-NPU-1 gate 1 is the real test.
|
||||||
- **SKU unconfirmed** (G2 128MB / 0.5-TOPS vs G3 256MB / 1.0-TOPS; datasheet Rev 2.0
|
- **SKU unconfirmed** (G2 128MB / 0.5-TOPS vs G3 256MB / 1.0-TOPS; datasheet Rev 2.0
|
||||||
retracted the split to "both 1.0 TOPS"). Determines the compute ceiling if
|
retracted the split to "both 1.0 TOPS"). Determines the compute ceiling if
|
||||||
M-NPU-2/3 are ever pursued; needs an on-hardware chip-ID/TOPS register read.
|
M-NPU-2/3 are ever pursued; needs an on-hardware chip-ID/TOPS register read.
|
||||||
- **No open regcmd project for RV1106 exists** (re-confirm periodically — community
|
- **No open regcmd project for RV1106 exists** (re-confirm periodically, community
|
||||||
moves fast, esp. the `gahingwoo` RK3576 work; but it is the wrong generation).
|
moves fast, esp. the `gahingwoo` RK3576 work; but it is the wrong generation).
|
||||||
- **No NPU use case is currently established for this board** (no camera; RGA2 +
|
- **No NPU use case is currently established for this board** (no camera; RGA2 +
|
||||||
RKIVE already cover the 2D/CNN-shaped image tasks better — see
|
RKIVE already cover the 2D/CNN-shaped image tasks better, see
|
||||||
`npu-graphics-feasibility.md`). Absent a concrete non-visual classifier need,
|
`npu-graphics-feasibility.md`). Absent a concrete non-visual classifier need,
|
||||||
M-NPU-2 and beyond have no pull, and M-NPU-1 alone (driver binds, load graph
|
M-NPU-2 and beyond have no pull, and M-NPU-1 alone (driver binds, load graph
|
||||||
works, no blob) is the correct stopping point.
|
works, no blob) is the correct stopping point.
|
||||||
@@ -301,4 +301,4 @@ ever begun; otherwise this is the documented reason open NPU compute is deferred
|
|||||||
---
|
---
|
||||||
_Cross-refs: `PORT-PLAN.md` (authoritative file-by-file kernel port),
|
_Cross-refs: `PORT-PLAN.md` (authoritative file-by-file kernel port),
|
||||||
`../../docs/npu-graphics-feasibility.md`, `../CAPABILITIES-AUDIT.md`,
|
`../../docs/npu-graphics-feasibility.md`, `../CAPABILITIES-AUDIT.md`,
|
||||||
`../PROVENANCE.md`, `../DRIVER-PARITY.md:41`, `../REMAINING-PORTS.md §6`._
|
`../PROVENANCE.md`, `../DRIVER-PARITY.md:41`, `../REMAINING-PORTS.md section 6`._
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
# RKNPU kernel driver → 6.18 — port plan (M6 class)
|
# RKNPU kernel driver -> 6.18: port plan (M6 class)
|
||||||
|
|
||||||
Scope: port the **kernel driver only** (`rknpu.ko`'s source, statically built into
|
Scope: port the **kernel driver only** (`rknpu.ko`'s source, statically built into
|
||||||
our tree) so `/dev/dri/cardN` (or `renderD1xx`) binds on the RV1106 NPU and answers
|
our tree) so `/dev/dri/cardN` (or `renderD1xx`) binds on the RV1106 NPU and answers
|
||||||
a version-query ioctl. This does **not** run a model — see §3 for why that's a
|
a version-query ioctl. This does **not** run a model. See section 3 for why that's a
|
||||||
separate, much bigger, and largely closed problem. Written against the same
|
separate, much bigger, and largely closed problem. Written against the same
|
||||||
target as the rest of this port: **Linux 6.18.46 vanilla**
|
target as the rest of this port: **Linux 6.18.46 vanilla**
|
||||||
(`flare-edge/research/linux-6.18.46/`), forward-ported from vendor 5.10.160, built
|
(`flare-edge/research/linux-6.18.46/`), forward-ported from vendor 5.10.160, built
|
||||||
with our `arm-rockchip830-...-gcc 8.3` toolchain — see `../PORT-STATUS.md` and
|
with our `arm-rockchip830-...-gcc 8.3` toolchain. See `../PORT-STATUS.md` and
|
||||||
`../../docs/bringup.md` for the method and milestones this slots into (M6, listed
|
`../../docs/bringup.md` for the method and milestones this slots into (M6, listed
|
||||||
in `../DRIVER-PARITY.md` as "NPU (rknpu, ff660000) | out-of-tree | [ ] M6").
|
in `../DRIVER-PARITY.md` as "NPU (rknpu, ff660000) | out-of-tree | [ ] M6").
|
||||||
|
|
||||||
Builds on `warden-sdk/docs/npu-graphics-feasibility.md`, which already read this
|
Builds on `warden-sdk/docs/npu-graphics-feasibility.md`, which already read this
|
||||||
same driver source to answer a narrower question (can the NPU do graphics — no).
|
same driver source to answer a narrower question (can the NPU do graphics: no).
|
||||||
This document answers the porting question that doc explicitly deferred.
|
This document answers the porting question that doc explicitly deferred.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. What we're forward-porting (vendor SDK source)
|
## 1. What we're forward-porting (vendor SDK source)
|
||||||
|
|
||||||
Source: `flare-edge/sdk/sysdrv/source/kernel/drivers/rknpu/` — vendor version
|
Source: `flare-edge/sdk/sysdrv/source/kernel/drivers/rknpu/`, vendor version
|
||||||
**0.9.2** (`DRIVER_MAJOR/MINOR/PATCHLEVEL` in `include/rknpu_drv.h:34-36`,
|
**0.9.2** (`DRIVER_MAJOR/MINOR/PATCHLEVEL` in `include/rknpu_drv.h:34-36`,
|
||||||
`DRIVER_DATE "20230825"`), currently loaded on the shipping 5.10.160 kernel as
|
`DRIVER_DATE "20230825"`), currently loaded on the shipping 5.10.160 kernel as
|
||||||
`rknpu.ko`, IRQ `ff660000.npu`.
|
`rknpu.ko`, IRQ `ff660000.npu`.
|
||||||
|
|
||||||
### The driver already targets RV1106 natively — this is not a from-scratch adaptation
|
### The driver already targets RV1106 natively: this is not a from-scratch adaptation
|
||||||
|
|
||||||
The vendor driver is a **single multi-SoC codebase**, not something written for
|
The vendor driver is a **single multi-SoC codebase**, not something written for
|
||||||
RK3588 that we'd have to adapt. `rknpu_drv.c`'s `of_match` table already carries a
|
RK3588 that we'd have to adapt. `rknpu_drv.c`'s `of_match` table already carries a
|
||||||
@@ -49,7 +49,7 @@ static const struct rknpu_config rv1106_rknpu_config = {
|
|||||||
```
|
```
|
||||||
|
|
||||||
And the devicetree side is **already complete** in `rv1106.dtsi` (base tree,
|
And the devicetree side is **already complete** in `rv1106.dtsi` (base tree,
|
||||||
`status = "disabled"`) — nothing to author, only enable:
|
`status = "disabled"`), nothing to author, only enable:
|
||||||
|
|
||||||
```dts
|
```dts
|
||||||
// arch/arm/boot/dts/rv1106.dtsi:1127-1138
|
// arch/arm/boot/dts/rv1106.dtsi:1127-1138
|
||||||
@@ -67,17 +67,17 @@ npu: npu@ff660000 {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
No `power-domains` property (RV1106's NPU is single-core, single-rail — unlike
|
No `power-domains` property (RV1106's NPU is single-core, single-rail, unlike
|
||||||
RK3588's 3-core NPU which needs `genpd_dev_npu0/1/2`) and no `iommus` property
|
RK3588's 3-core NPU which needs `genpd_dev_npu0/1/2`) and no `iommus` property
|
||||||
(matches the 5.10 boot-log finding already on record: `"rknpu iommu device-tree
|
(matches the 5.10 boot-log finding already on record: `"rknpu iommu device-tree
|
||||||
entry not found!, using non-iommu mode"`, `npu-graphics-feasibility.md:123-126`).
|
entry not found!, using non-iommu mode"`, `npu-graphics-feasibility.md:123-126`).
|
||||||
Both facts materially shrink the v1 port's scope — see §2.3 and §2.4.
|
Both facts materially shrink the v1 port's scope. See section 2.3 and section 2.4.
|
||||||
|
|
||||||
### The uapi/job model (for context — already characterized in the feasibility doc)
|
### The uapi/job model (for context, already characterized in the feasibility doc)
|
||||||
|
|
||||||
`include/rknpu_ioctl.h`: `struct rknpu_task` (`regcfg_amount`, `regcfg_offset`,
|
`include/rknpu_ioctl.h`: `struct rknpu_task` (`regcfg_amount`, `regcfg_offset`,
|
||||||
`regcmd_addr`) + `struct rknpu_submit` (`task_obj_addr`, `regcfg_obj_addr`,
|
`regcmd_addr`) + `struct rknpu_submit` (`task_obj_addr`, `regcfg_obj_addr`,
|
||||||
`core_mask`, `fence_fd`) — a **register command-list (regcmd) task-queue model**,
|
`core_mask`, `fence_fd`), a **register command-list (regcmd) task-queue model**,
|
||||||
submitted via `DRM_IOCTL_RKNPU_SUBMIT` and executed to completion with the driver
|
submitted via `DRM_IOCTL_RKNPU_SUBMIT` and executed to completion with the driver
|
||||||
blocking on a hardware IRQ (`rknpu_job.c`, `wait_event_timeout`). Six ioctls total:
|
blocking on a hardware IRQ (`rknpu_job.c`, `wait_event_timeout`). Six ioctls total:
|
||||||
`RKNPU_ACTION`, `RKNPU_SUBMIT`, `RKNPU_MEM_{CREATE,MAP,DESTROY,SYNC}`
|
`RKNPU_ACTION`, `RKNPU_SUBMIT`, `RKNPU_MEM_{CREATE,MAP,DESTROY,SYNC}`
|
||||||
@@ -88,19 +88,19 @@ verify step uses: `RKNPU_GET_HW_VERSION = 0`, `RKNPU_GET_DRV_VERSION = 1`
|
|||||||
### Memory manager choice: DRM GEM (not DMA-heap)
|
### Memory manager choice: DRM GEM (not DMA-heap)
|
||||||
|
|
||||||
`Kconfig` offers a mutually-exclusive choice: `ROCKCHIP_RKNPU_DRM_GEM` (default)
|
`Kconfig` offers a mutually-exclusive choice: `ROCKCHIP_RKNPU_DRM_GEM` (default)
|
||||||
vs `ROCKCHIP_RKNPU_DMA_HEAP`. **Pick DRM_GEM** — M4 (VOP2 display) already pulls
|
vs `ROCKCHIP_RKNPU_DMA_HEAP`. **Pick DRM_GEM**: M4 (VOP2 display) already pulls
|
||||||
the DRM core into this kernel for the panel, so there's no new subsystem cost, and
|
the DRM core into this kernel for the panel, so there's no new subsystem cost, and
|
||||||
DRM_GEM is the vendor's default/most-tested path. This also means the node that
|
DRM_GEM is the vendor's default/most-tested path. This also means the node that
|
||||||
appears is a classic DRM char device (`/dev/dri/cardN` / `renderD1xx` via
|
appears is a classic DRM char device (`/dev/dri/cardN` / `renderD1xx` via
|
||||||
`drm_dev_alloc`/`drm_dev_register`, `rknpu_drv.c:725,730`) — **not** the newer
|
`drm_dev_alloc`/`drm_dev_register`, `rknpu_drv.c:725,730`), **not** the newer
|
||||||
`/dev/accel/` framework mainline's own `rocket` driver uses (§3). Don't confuse
|
`/dev/accel/` framework mainline's own `rocket` driver uses (section 3). Don't confuse
|
||||||
the two node namespaces when verifying.
|
the two node namespaces when verifying.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Files, config, and the concrete 5.10→6.18 API deltas
|
## 2. Files, config, and the concrete 5.10->6.18 API deltas
|
||||||
|
|
||||||
### 2.1 File list — v1 minimal port
|
### 2.1 File list: v1 minimal port
|
||||||
|
|
||||||
| File | Bring in? | Why |
|
| File | Bring in? | Why |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -108,15 +108,15 @@ the two node namespaces when verifying.
|
|||||||
| `rknpu_job.c` / `include/rknpu_job.h` | **Yes** | job submit, IRQ handler, PC task-list execution |
|
| `rknpu_job.c` / `include/rknpu_job.h` | **Yes** | job submit, IRQ handler, PC task-list execution |
|
||||||
| `rknpu_gem.c` / `include/rknpu_gem.h` | **Yes** | GEM memory manager (DRM_GEM path) |
|
| `rknpu_gem.c` / `include/rknpu_gem.h` | **Yes** | GEM memory manager (DRM_GEM path) |
|
||||||
| `rknpu_reset.c` / `include/rknpu_reset.h` | **Yes** | `SRST_A_RKNN`/`SRST_H_RKNN` reset control |
|
| `rknpu_reset.c` / `include/rknpu_reset.h` | **Yes** | `SRST_A_RKNN`/`SRST_H_RKNN` reset control |
|
||||||
| `rknpu_iommu.c` / `include/rknpu_iommu.h` | Yes, but dead code path | 61 lines, self-contained, already version-gated to 6.1; harmless to carry even though `iommu_en` stays false on our non-IOMMU DT (§2.4) |
|
| `rknpu_iommu.c` / `include/rknpu_iommu.h` | Yes, but dead code path | 61 lines, self-contained, already version-gated to 6.1; harmless to carry even though `iommu_en` stays false on our non-IOMMU DT (section 2.4) |
|
||||||
| `rknpu_debugger.c` / `include/rknpu_debugger.h` | Yes (optional) | `/proc/rknpu/load` — the Monitor page already polls this on 5.10 (`npu-graphics-feasibility.md:151-158`); keep for continuity even though wiring the UI back up is out of scope here |
|
| `rknpu_debugger.c` / `include/rknpu_debugger.h` | Yes (optional) | `/proc/rknpu/load`: the Monitor page already polls this on 5.10 (`npu-graphics-feasibility.md:151-158`); keep for continuity even though wiring the UI back up is out of scope here |
|
||||||
| `rknpu_mem.c` | **No** | only for `ROCKCHIP_RKNPU_DMA_HEAP` — we're not using that memory manager |
|
| `rknpu_mem.c` | **No** | only for `ROCKCHIP_RKNPU_DMA_HEAP`: we're not using that memory manager |
|
||||||
| `rknpu_mm.c` / `include/rknpu_mm.h` | **No** | SRAM/NBUF allocator (`ROCKCHIP_RKNPU_SRAM`, needs `NO_GKI`); `rv1106_rknpu_config` has `nbuf_phyaddr=0, nbuf_size=0` — dead weight on this SoC |
|
| `rknpu_mm.c` / `include/rknpu_mm.h` | **No** | SRAM/NBUF allocator (`ROCKCHIP_RKNPU_SRAM`, needs `NO_GKI`); `rv1106_rknpu_config` has `nbuf_phyaddr=0, nbuf_size=0`, dead weight on this SoC |
|
||||||
| `rknpu_fence.c` / `include/rknpu_fence.h` | **No (v1)** | `ROCKCHIP_RKNPU_FENCE`/`SYNC_FILE` — dma-fence cross-driver sync, not needed to prove basic binding; revisit if a real workload needs fenced submission later |
|
| `rknpu_fence.c` / `include/rknpu_fence.h` | **No (v1)** | `ROCKCHIP_RKNPU_FENCE`/`SYNC_FILE`: dma-fence cross-driver sync, not needed to prove basic binding; revisit if a real workload needs fenced submission later |
|
||||||
| `Kconfig`, `Makefile` | **Yes, trimmed** | drop the `rknpu_mem.o`/`rknpu_mm.o`/`rknpu_fence.o` conditional lines' configs (leave the `Makefile` structure as-is — it's already `obj-$(CONFIG_...)`-gated per file, so simply not enabling those Kconfig symbols is sufficient; no Makefile edit required) |
|
| `Kconfig`, `Makefile` | **Yes, trimmed** | drop the `rknpu_mem.o`/`rknpu_mm.o`/`rknpu_fence.o` conditional lines' configs (leave the `Makefile` structure as-is; it's already `obj-$(CONFIG_...)`-gated per file, so simply not enabling those Kconfig symbols is sufficient; no Makefile edit required) |
|
||||||
|
|
||||||
### 2.2 Config symbols (built-in, matching this port's established pattern —
|
### 2.2 Config symbols (built-in, matching this port's established pattern:
|
||||||
M1–M3 build everything statically to avoid the vermagic class of bug that
|
M1-M3 build everything statically to avoid the vermagic class of bug that
|
||||||
blocks `aic8800.ko` today, `PORT-STATUS.md:114-115`)
|
blocks `aic8800.ko` today, `PORT-STATUS.md:114-115`)
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -131,7 +131,7 @@ CONFIG_DRM=y # already required by M4 (VOP2)
|
|||||||
### 2.3 DT change
|
### 2.3 DT change
|
||||||
|
|
||||||
One-line status flip on the board DT (the same pattern M2/M3 used for
|
One-line status flip on the board DT (the same pattern M2/M3 used for
|
||||||
`grf-clock-controller` and the eMMC node — override in the board file, don't
|
`grf-clock-controller` and the eMMC node: override in the board file, don't
|
||||||
touch the base `rv1106.dtsi`):
|
touch the base `rv1106.dtsi`):
|
||||||
|
|
||||||
```dts
|
```dts
|
||||||
@@ -140,19 +140,19 @@ touch the base `rv1106.dtsi`):
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
No new properties needed — `compatible`/`reg`/`interrupts`/`clocks`/`resets` are
|
No new properties needed: `compatible`/`reg`/`interrupts`/`clocks`/`resets` are
|
||||||
already correct and match the driver's own `rv1106_rknpu_config` exactly (§1).
|
already correct and match the driver's own `rv1106_rknpu_config` exactly (section 1).
|
||||||
**Do not add an `iommus =` property for v1** — see §2.4.
|
**Do not add an `iommus =` property for v1**. See section 2.4.
|
||||||
|
|
||||||
### 2.4 The real build blocker: four vendor-only `soc/rockchip/*.h` headers
|
### 2.4 The real build blocker: four vendor-only `soc/rockchip/*.h` headers
|
||||||
|
|
||||||
This is the one item in this plan that isn't "already handled by the vendor's own
|
This is the one item in this plan that isn't "already handled by the vendor's own
|
||||||
version gates" — verified directly against our `flare-edge/research/linux-6.18.46/`
|
version gates", verified directly against our `flare-edge/research/linux-6.18.46/`
|
||||||
tree, not assumed:
|
tree, not assumed:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
// rknpu_drv.c:37-40 (inside #ifndef FPGA_PLATFORM, which is never defined for
|
// rknpu_drv.c:37-40 (inside #ifndef FPGA_PLATFORM, which is never defined for
|
||||||
// our build — grep of Makefile/Kconfig shows no FPGA_PLATFORM define anywhere)
|
// our build: grep of Makefile/Kconfig shows no FPGA_PLATFORM define anywhere)
|
||||||
#include <soc/rockchip/rockchip_iommu.h>
|
#include <soc/rockchip/rockchip_iommu.h>
|
||||||
#include <soc/rockchip/rockchip_opp_select.h>
|
#include <soc/rockchip/rockchip_opp_select.h>
|
||||||
#include <soc/rockchip/rockchip_system_monitor.h>
|
#include <soc/rockchip/rockchip_system_monitor.h>
|
||||||
@@ -160,33 +160,33 @@ tree, not assumed:
|
|||||||
```
|
```
|
||||||
|
|
||||||
`find flare-edge/research/linux-6.18.46/include -iname 'rockchip_{iommu,opp_select,
|
`find flare-edge/research/linux-6.18.46/include -iname 'rockchip_{iommu,opp_select,
|
||||||
system_monitor,ipa}.h'` returns **nothing** — all four are Rockchip downstream-BSP
|
system_monitor,ipa}.h'` returns **nothing**. All four are Rockchip downstream-BSP
|
||||||
convenience headers (DVFS/OPP-table selection, thermal/system-monitor
|
convenience headers (DVFS/OPP-table selection, thermal/system-monitor
|
||||||
registration, IPA power-model, and a vendor wrapper around the IOMMU-core API)
|
registration, IPA power-model, and a vendor wrapper around the IOMMU-core API)
|
||||||
that were never upstreamed. `rknpu_drv.h:21-23` pulls in `rockchip_opp_select.h`
|
that were never upstreamed. `rknpu_drv.h:21-23` pulls in `rockchip_opp_select.h`
|
||||||
unconditionally too, gated only by `KERNEL_VERSION(5,10,0) <= LINUX_VERSION_CODE`
|
unconditionally too, gated only by `KERNEL_VERSION(5,10,0) <= LINUX_VERSION_CODE`,
|
||||||
— true for 6.18, so it's compiled by default, not something a Kconfig toggle
|
true for 6.18, so it's compiled by default, not something a Kconfig toggle
|
||||||
avoids.
|
avoids.
|
||||||
|
|
||||||
**All four are genuinely dead code for RV1106 at runtime**, which is what makes
|
**All four are genuinely dead code for RV1106 at runtime**, which is what makes
|
||||||
this a small, well-scoped fix rather than a real feature to build:
|
this a small, well-scoped fix rather than a real feature to build:
|
||||||
- `rockchip_iommu_is_enabled()` (`rknpu_drv.c:902`) is only called inside
|
- `rockchip_iommu_is_enabled()` (`rknpu_drv.c:902`) is only called inside
|
||||||
`if (rknpu_dev->multiple_domains)` — true only for RK3588's 3-core NPU; RV1106
|
`if (rknpu_dev->multiple_domains)`: true only for RK3588's 3-core NPU; RV1106
|
||||||
never sets it (no `power-domains` property, §1).
|
never sets it (no `power-domains` property, section 1).
|
||||||
- The OPP/system-monitor/IPA calls drive dynamic frequency/voltage scaling and
|
- The OPP/system-monitor/IPA calls drive dynamic frequency/voltage scaling and
|
||||||
thermal cooling-device registration against an OPP table — RV1106's DT pins a
|
thermal cooling-device registration against an OPP table. RV1106's DT pins a
|
||||||
single fixed clock rate (`assigned-clock-rates = <420000000>`) and has no
|
single fixed clock rate (`assigned-clock-rates = <420000000>`) and has no
|
||||||
`operating-points-v2` table; none of this is exercised today either.
|
`operating-points-v2` table; none of this is exercised today either.
|
||||||
|
|
||||||
**Fix**: add small local compat shim headers (in this port's own include path,
|
**Fix**: add small local compat shim headers (in this port's own include path,
|
||||||
ahead of the vendor source's include search path) providing just the symbols
|
ahead of the vendor source's include search path) providing just the symbols
|
||||||
these call sites reference — `rockchip_iommu_is_enabled()` returning `false`, and
|
these call sites reference, `rockchip_iommu_is_enabled()` returning `false`, and
|
||||||
no-op/`-ENOTSUPP` stand-ins for the opp/monitor/ipa registration calls actually
|
no-op/`-ENOTSUPP` stand-ins for the opp/monitor/ipa registration calls actually
|
||||||
referenced in `rknpu_drv.c`. This is the same "compat shim for a header that moved
|
referenced in `rknpu_drv.c`. This is the same "compat shim for a header that moved
|
||||||
or doesn't exist upstream" pattern already used for `clk-rv1106.c`'s
|
or doesn't exist upstream" pattern already used for `clk-rv1106.c`'s
|
||||||
`panic_notifier_list` move (`../PORT-STATUS.md:18-20`) — same class of fix,
|
`panic_notifier_list` move (`../PORT-STATUS.md:18-20`), same class of fix,
|
||||||
same low risk, because the code behind it is provably dead for this SoC's DT.
|
same low risk, because the code behind it is provably dead for this SoC's DT.
|
||||||
**Do not** reach for `#define FPGA_PLATFORM` as a shortcut — that macro also
|
**Do not** reach for `#define FPGA_PLATFORM` as a shortcut: that macro also
|
||||||
guards the reset-control logic in `rknpu_reset.c` (nearly the whole file is
|
guards the reset-control logic in `rknpu_reset.c` (nearly the whole file is
|
||||||
`#ifndef FPGA_PLATFORM`), which we need live; it's too blunt an instrument here.
|
`#ifndef FPGA_PLATFORM`), which we need live; it's too blunt an instrument here.
|
||||||
|
|
||||||
@@ -199,73 +199,73 @@ actually still true at 6.18:
|
|||||||
|
|
||||||
| Vendor gate (`rknpu_drv.c`) | 6.18 status (verified) |
|
| Vendor gate (`rknpu_drv.c`) | 6.18 status (verified) |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `#if KERNEL_VERSION(6,1,0) > LINUX_VERSION_CODE` around `.gem_free_object_unlocked` | Correctly **skipped** — that field is gone from `struct drm_driver` in 6.18's `include/drm/drm_drv.h` (grepped, zero hits), and the driver's `#else` branch already uses the modern `struct drm_gem_object_funcs` (`.free`, `.export`, `.get_sg_table`, `.vmap`, `.vunmap`, `.mmap` — `rknpu_gem.c:352-358`) |
|
| `#if KERNEL_VERSION(6,1,0) > LINUX_VERSION_CODE` around `.gem_free_object_unlocked` | Correctly **skipped**: that field is gone from `struct drm_driver` in 6.18's `include/drm/drm_drv.h` (grepped, zero hits), and the driver's `#else` branch already uses the modern `struct drm_gem_object_funcs` (`.free`, `.export`, `.get_sg_table`, `.vmap`, `.vunmap`, `.mmap`, `rknpu_gem.c:352-358`) |
|
||||||
| `DEFINE_DRM_GEM_FOPS(...)` (6.1+) vs hand-rolled `file_operations` | 6.1+ branch applies; macro is a standard DRM-core helper, present in 6.18 |
|
| `DEFINE_DRM_GEM_FOPS(...)` (6.1+) vs hand-rolled `file_operations` | 6.1+ branch applies; macro is a standard DRM-core helper, present in 6.18 |
|
||||||
| `.gem_prime_mmap = drm_gem_prime_mmap` (6.1+) vs a custom `rknpu_gem_prime_mmap` | 6.1+ branch applies |
|
| `.gem_prime_mmap = drm_gem_prime_mmap` (6.1+) vs a custom `rknpu_gem_prime_mmap` | 6.1+ branch applies |
|
||||||
| `struct drm_driver` fields the vendor initializer sets (`major`, `minor`, `patchlevel`, `driver_features`, `dumb_create`, `dumb_map_offset`) | All still present in 6.18's `drm_drv.h` (line-checked) |
|
| `struct drm_driver` fields the vendor initializer sets (`major`, `minor`, `patchlevel`, `driver_features`, `dumb_create`, `dumb_map_offset`) | All still present in 6.18's `drm_drv.h` (line-checked) |
|
||||||
| `DRM_IOCTL_DEF_DRV(...)` macro (ioctl table) | Still defined in 6.18's `include/drm/drm_ioctl.h:151` |
|
| `DRM_IOCTL_DEF_DRV(...)` macro (ioctl table) | Still defined in 6.18's `include/drm/drm_ioctl.h:151` |
|
||||||
| `iommu_map()` / `iommu_unmap()` / `iommu_get_domain_for_dev()` / `iommu_attach_device()` / `iommu_detach_device()` (`rknpu_gem.c`, `rknpu_reset.c`) | `iommu_map()`'s extern signature is unchanged in 6.18's `include/linux/iommu.h:914` (mainline did add a newer `iommu_map_nosync()` alongside it, but didn't remove the classic call) — moot anyway since this path is dead on our non-IOMMU DT (§2.4) |
|
| `iommu_map()` / `iommu_unmap()` / `iommu_get_domain_for_dev()` / `iommu_attach_device()` / `iommu_detach_device()` (`rknpu_gem.c`, `rknpu_reset.c`) | `iommu_map()`'s extern signature is unchanged in 6.18's `include/linux/iommu.h:914` (mainline did add a newer `iommu_map_nosync()` alongside it, but didn't remove the classic call), moot anyway since this path is dead on our non-IOMMU DT (section 2.4) |
|
||||||
| `devm_reset_control_get`, `clk_bulk_data`, `pm_runtime_get_sync`/`put_sync`/`resume_and_get` | Stable mainline APIs across the whole 5.10→6.18 span; no gate needed |
|
| `devm_reset_control_get`, `clk_bulk_data`, `pm_runtime_get_sync`/`put_sync`/`resume_and_get` | Stable mainline APIs across the whole 5.10->6.18 span; no gate needed |
|
||||||
|
|
||||||
Net: outside the four-header fix in §2.4, this is expected to be a **build-fix-build
|
Net: outside the four-header fix in section 2.4, this is expected to be a **build-fix-build
|
||||||
pass**, not a rewrite — confirm by actually compiling into the tree (the checks
|
pass**, not a rewrite. Confirm by actually compiling into the tree (the checks
|
||||||
above are header-presence/signature verification, not a build).
|
above are header-presence/signature verification, not a build).
|
||||||
|
|
||||||
### 2.6 Explicitly deferred (not required to prove the driver binds)
|
### 2.6 Explicitly deferred (not required to prove the driver binds)
|
||||||
|
|
||||||
- **IOMMU enablement.** Stays off — matches current 5.10 runtime behavior and
|
- **IOMMU enablement.** Stays off: matches current 5.10 runtime behavior and
|
||||||
avoids the newer-kernel IOMMU-core churn entirely (mainline replaced
|
avoids the newer-kernel IOMMU-core churn entirely (mainline replaced
|
||||||
`iommu_domain_alloc(bus)` with device-based `iommu_paging_domain_alloc(dev)`
|
`iommu_domain_alloc(bus)` with device-based `iommu_paging_domain_alloc(dev)`
|
||||||
somewhere in the 6.x series — confirmed by grepping 6.18.46's `iommu.h`, which
|
somewhere in the 6.x series, confirmed by grepping 6.18.46's `iommu.h`, which
|
||||||
has the new call and no bus-based `iommu_domain_alloc`). Since our DT carries no
|
has the new call and no bus-based `iommu_domain_alloc`). Since our DT carries no
|
||||||
`iommus=` property, this churn never gets compiled against in the first place.
|
`iommus=` property, this churn never gets compiled against in the first place.
|
||||||
- **dma-fence / `ROCKCHIP_RKNPU_FENCE`.** Cross-driver sync primitive, not needed
|
- **dma-fence / `ROCKCHIP_RKNPU_FENCE`.** Cross-driver sync primitive, not needed
|
||||||
to answer a version-query ioctl.
|
to answer a version-query ioctl.
|
||||||
- **DVFS / thermal cooling / multi-power-domain.** RV1106-inapplicable per §2.4;
|
- **DVFS / thermal cooling / multi-power-domain.** RV1106-inapplicable per section 2.4;
|
||||||
stubbed out, not implemented.
|
stubbed out, not implemented.
|
||||||
- **SRAM/NBUF allocator.** Dead weight on this SoC's config table (§2.1).
|
- **SRAM/NBUF allocator.** Dead weight on this SoC's config table (section 2.1).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Verify steps (driver binding only — no model, no RKNN runtime)
|
## 3. Verify steps (driver binding only, no model, no RKNN runtime)
|
||||||
|
|
||||||
Uses the same proven safe-test loop as M2/M3: the A/B `_b`-slot one-shot boot on
|
Uses the same proven safe-test loop as M2/M3: the A/B `_b`-slot one-shot boot on
|
||||||
`warden-c8a3` (`../../docs/m2-boot-on-c8a3.md`) — never touches the working `_a`
|
`warden-c8a3` (`../../docs/m2-boot-on-c8a3.md`), never touches the working `_a`
|
||||||
slot, auto-reverts on hang.
|
slot, auto-reverts on hang.
|
||||||
|
|
||||||
1. **Build**: `CONFIG_ROCKCHIP_RKNPU=y` (+ the symbols in §2.2) added to the
|
1. **Build**: `CONFIG_ROCKCHIP_RKNPU=y` (+ the symbols in section 2.2) added to the
|
||||||
defconfig fragment; `rknpu_drv.o`/`rknpu_job.o`/`rknpu_gem.o`/`rknpu_reset.o`/
|
defconfig fragment; `rknpu_drv.o`/`rknpu_job.o`/`rknpu_gem.o`/`rknpu_reset.o`/
|
||||||
`rknpu_iommu.o`/`rknpu_debugger.o` compile clean into `built-in.a` — this is
|
`rknpu_iommu.o`/`rknpu_debugger.o` compile clean into `built-in.a`; this is
|
||||||
where the §2.4 shim headers get proven, not just inspected.
|
where the section 2.4 shim headers get proven, not just inspected.
|
||||||
2. **DT**: `npu@ff660000` flipped to `okay`; `dtc -W` clean, no warnings, no
|
2. **DT**: `npu@ff660000` flipped to `okay`; `dtc -W` clean, no warnings, no
|
||||||
overrun of the existing DTB-placement fix from M2 (`PORT-STATUS.md`'s "place
|
overrun of the existing DTB-placement fix from M2 (`PORT-STATUS.md`'s "place
|
||||||
the fdt high" note — a bigger built-in.a makes this worth re-checking).
|
the fdt high" note; a bigger built-in.a makes this worth re-checking).
|
||||||
3. **Boot** (via the `_b`-slot loop): `dmesg | grep -i rknpu` shows the
|
3. **Boot** (via the `_b`-slot loop): `dmesg | grep -i rknpu` shows the
|
||||||
`platform_driver` probing without error — clock/reset/IRQ acquired, no panic,
|
`platform_driver` probing without error, clock/reset/IRQ acquired, no panic,
|
||||||
no `-EPROBE_DEFER` stall. Compare against the 5.10 baseline probe log for the
|
no `-EPROBE_DEFER` stall. Compare against the 5.10 baseline probe log for the
|
||||||
same node if available.
|
same node if available.
|
||||||
4. **Node appears**: `ls -la /dev/dri/` shows a new `cardN`/`renderD1xx` for the
|
4. **Node appears**: `ls -la /dev/dri/` shows a new `cardN`/`renderD1xx` for the
|
||||||
npu — classic DRM char device (not `/dev/accel/`, see §1).
|
npu, classic DRM char device (not `/dev/accel/`, see section 1).
|
||||||
5. **Trivial ioctl, not a model**: a small host-buildable C program opens the DRM
|
5. **Trivial ioctl, not a model**: a small host-buildable C program opens the DRM
|
||||||
node and issues `DRM_IOCTL_RKNPU_ACTION` with `{.flags = RKNPU_GET_DRV_VERSION}`
|
node and issues `DRM_IOCTL_RKNPU_ACTION` with `{.flags = RKNPU_GET_DRV_VERSION}`
|
||||||
(`rknpu_ioctl.h:114`), checks the returned `value` decodes to `0.9.2`
|
(`rknpu_ioctl.h:114`), checks the returned `value` decodes to `0.9.2`
|
||||||
(`RKNPU_GET_DRV_VERSION_MAJOR/MINOR/PATCHLEVEL` macros,
|
(`RKNPU_GET_DRV_VERSION_MAJOR/MINOR/PATCHLEVEL` macros,
|
||||||
`rknpu_ioctl.h:52-54`) — and/or `RKNPU_GET_HW_VERSION` returns something
|
`rknpu_ioctl.h:52-54`), and/or `RKNPU_GET_HW_VERSION` returns something
|
||||||
plausible. This exercises the full ioctl-dispatch → power-get/put →
|
plausible. This exercises the full ioctl-dispatch -> power-get/put ->
|
||||||
clock/reset path with zero dependency on a regcmd buffer or the RKNN runtime.
|
clock/reset path with zero dependency on a regcmd buffer or the RKNN runtime.
|
||||||
6. **Explicitly not required for "done" here**: `DRM_IOCTL_RKNPU_SUBMIT`, any
|
6. **Explicitly not required for "done" here**: `DRM_IOCTL_RKNPU_SUBMIT`, any
|
||||||
`.rknn` model, `librknnrt`. That's the userspace question — §4.
|
`.rknn` model, `librknnrt`. That's the userspace question: section 4.
|
||||||
|
|
||||||
### Effort estimate
|
### Effort estimate
|
||||||
|
|
||||||
Smaller than M1 (clk/pinctrl — required inferring an unknown CPU-clock mux from a
|
Smaller than M1 (clk/pinctrl: required inferring an unknown CPU-clock mux from a
|
||||||
sibling diff) and smaller than the display work ahead in M4 (register-map
|
sibling diff) and smaller than the display work ahead in M4 (register-map
|
||||||
guesswork against RV1126/RK3568 siblings). This one is closer in shape to M3 ("the
|
guesswork against RV1126/RK3568 siblings). This one is closer in shape to M3 ("the
|
||||||
eMMC node was all M3 needed" — `PORT-STATUS.md:99`): the DT is already fully
|
eMMC node was all M3 needed", `PORT-STATUS.md:99`): the DT is already fully
|
||||||
specified upstream, the driver's C source already has a dedicated, tested RV1106
|
specified upstream, the driver's C source already has a dedicated, tested RV1106
|
||||||
config table and of_match entry (§1), and the GEM/DRM surface is already correctly
|
config table and of_match entry (section 1), and the GEM/DRM surface is already correctly
|
||||||
version-gated past 6.1 (§2.5). The concentrated risk is (a) actually compiling
|
version-gated past 6.1 (section 2.5). The concentrated risk is (a) actually compiling
|
||||||
the §2.4 shims against real 6.18 headers rather than trusting the header-presence
|
the section 2.4 shims against real 6.18 headers rather than trusting the header-presence
|
||||||
check above, and (b) the possibility of additional vendor-only symbols not
|
check above, and (b) the possibility of additional vendor-only symbols not
|
||||||
surfaced by this read-through. Realistic order of magnitude: low-single-digit
|
surfaced by this read-through. Realistic order of magnitude: low-single-digit
|
||||||
engineer-days to a clean probe + version-ioctl round trip on hardware, assuming
|
engineer-days to a clean probe + version-ioctl round trip on hardware, assuming
|
||||||
@@ -273,29 +273,29 @@ no surprise blocks the way M2's boot-image format did.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. The userspace-runtime reality: what porting the kernel driver does — and does NOT — unlock
|
## 4. The userspace-runtime reality: what porting the kernel driver does (and does NOT) unlock
|
||||||
|
|
||||||
The instruction that motivated this document was explicit: don't let "the driver
|
The instruction that motivated this document was explicit: don't let "the driver
|
||||||
is open source" imply the NPU becomes open-source-usable. It doesn't. This section
|
is open source" imply the NPU becomes open-source-usable. It doesn't. This section
|
||||||
updates `npu-graphics-feasibility.md §4`'s conclusion with the current
|
updates `npu-graphics-feasibility.md section 4`'s conclusion with the current
|
||||||
(2026-08-24) state of every open effort found.
|
(2026-08-24) state of every open effort found.
|
||||||
|
|
||||||
### 4.1 The kernel driver itself: genuinely open, and this is a real forward-port of Rockchip's own code
|
### 4.1 The kernel driver itself: genuinely open, and this is a real forward-port of Rockchip's own code
|
||||||
|
|
||||||
`rknpu_drv.c` et al. are SPDX `GPL-2.0`, authored by Rockchip
|
`rknpu_drv.c` et al. are SPDX `GPL-2.0`, authored by Rockchip
|
||||||
(`Felix Zeng <felix.zeng@rock-chips.com>`), and are the actual vendor driver — the
|
(`Felix Zeng <felix.zeng@rock-chips.com>`), and are the actual vendor driver: the
|
||||||
same driver every Rockchip Linux SDK ships, mirrored at
|
same driver every Rockchip Linux SDK ships, mirrored at
|
||||||
[`github.com/airockchip/rknpu`](https://github.com/airockchip/rknpu) /
|
[`github.com/airockchip/rknpu`](https://github.com/airockchip/rknpu) /
|
||||||
[`github.com/rockchip-linux/rknpu`](https://github.com/rockchip-linux/rknpu).
|
[`github.com/rockchip-linux/rknpu`](https://github.com/rockchip-linux/rknpu).
|
||||||
Porting it forward is legitimate, license-clean work, not a workaround. **This is
|
Porting it forward is legitimate, license-clean work, not a workaround. **This is
|
||||||
a different codebase from mainline's own driver** (§4.2) — don't conflate "port
|
a different codebase from mainline's own driver** (section 4.2): don't conflate "port
|
||||||
the vendor driver" with "adopt mainline's `accel/rocket`"; they are unrelated
|
the vendor driver" with "adopt mainline's `accel/rocket`"; they are unrelated
|
||||||
implementations of the same hardware class, and only one of them (the vendor one)
|
implementations of the same hardware class, and only one of them (the vendor one)
|
||||||
covers RV1106 at all.
|
covers RV1106 at all.
|
||||||
|
|
||||||
### 4.2 Mainline `accel/rocket`: real, merged, and does not reach RV1106
|
### 4.2 Mainline `accel/rocket`: real, merged, and does not reach RV1106
|
||||||
|
|
||||||
- Merged into mainline Linux and Mesa in **2025-07** — Tomeu Vizoso, ["Rockchip NPU
|
- Merged into mainline Linux and Mesa in **2025-07**, Tomeu Vizoso, ["Rockchip NPU
|
||||||
update 6: We are in mainline!"](https://blog.tomeuvizoso.net/2025/07/rockchip-npu-update-6-we-are-in-mainline.html),
|
update 6: We are in mainline!"](https://blog.tomeuvizoso.net/2025/07/rockchip-npu-update-6-we-are-in-mainline.html),
|
||||||
following the LKML series
|
following the LKML series
|
||||||
["[PATCH v2 0/7] New DRM accel driver for Rockchip's RKNN NPU"](https://lkml.iu.edu/hypermail/linux/kernel/2502.3/02497.html).
|
["[PATCH v2 0/7] New DRM accel driver for Rockchip's RKNN NPU"](https://lkml.iu.edu/hypermail/linux/kernel/2502.3/02497.html).
|
||||||
@@ -304,33 +304,33 @@ covers RV1106 at all.
|
|||||||
question**: `depends on (ARCH_ROCKCHIP && ARM64) || COMPILE_TEST`
|
question**: `depends on (ARCH_ROCKCHIP && ARM64) || COMPILE_TEST`
|
||||||
(`drivers/accel/rocket/Kconfig` in our 6.18.46 tree). RV1106 is a 32-bit
|
(`drivers/accel/rocket/Kconfig` in our 6.18.46 tree). RV1106 is a 32-bit
|
||||||
Cortex-A7 (`arch/arm`, confirmed on our own hardware:
|
Cortex-A7 (`arch/arm`, confirmed on our own hardware:
|
||||||
`PORT-STATUS.md`'s boot log — `CPU: ARMv7 Processor`). This is a Kconfig
|
`PORT-STATUS.md`'s boot log, `CPU: ARMv7 Processor`). This is a Kconfig
|
||||||
dependency, not necessarily an unfixable technical wall on its own — but it
|
dependency, not necessarily an unfixable technical wall on its own, but it
|
||||||
signals no one has done the 32-bit validation work, on top of the register-level
|
signals no one has done the 32-bit validation work, on top of the register-level
|
||||||
work below.
|
work below.
|
||||||
- **Hardware coverage, per the driver's own docs**
|
- **Hardware coverage, per the driver's own docs**
|
||||||
(`Documentation/accel/rocket/index.rst` in our tree): *"Hardware currently
|
(`Documentation/accel/rocket/index.rst` in our tree): *"Hardware currently
|
||||||
supported: * RK3588."* Nothing else, as shipped in 6.18.46.
|
supported: * RK3588."* Nothing else, as shipped in 6.18.46.
|
||||||
- **RK3576** — active, but incomplete, and not in our tree. A 2026-07-15
|
- **RK3576**: active, but incomplete, and not in our tree. A 2026-07-15
|
||||||
reverse-engineering effort (
|
reverse-engineering effort (
|
||||||
[CNX Software](https://www.cnx-software.com/2026/07/15/reverse-engineering-brings-rk3576-npu-support-to-open-source-rocket-driver-for-mainline-linux/),
|
[CNX Software](https://www.cnx-software.com/2026/07/15/reverse-engineering-brings-rk3576-npu-support-to-open-source-rocket-driver-for-mainline-linux/),
|
||||||
code at [`gahingwoo/linux-rk3576-npu`](https://github.com/gahingwoo/linux-rk3576-npu))
|
code at [`gahingwoo/linux-rk3576-npu`](https://github.com/gahingwoo/linux-rk3576-npu))
|
||||||
got single-task inference working end-to-end on a Radxa ROCK 4D running Linux
|
got single-task inference working end-to-end on a Radxa ROCK 4D running Linux
|
||||||
7.1-rc5 — but **multi-task chained inference (any real multi-layer network)
|
7.1-rc5, but **multi-task chained inference (any real multi-layer network)
|
||||||
fails: only the first task per NPU power session actually computes.** Not
|
fails: only the first task per NPU power session actually computes.** Not
|
||||||
merged into the kernel we're building against. Cited here because it's the
|
merged into the kernel we're building against. Cited here because it's the
|
||||||
closest active precedent to "port Rocket to a new RKNPU generation," and even
|
closest active precedent to "port Rocket to a new RKNPU generation," and even
|
||||||
that isn't production-usable yet.
|
that isn't production-usable yet.
|
||||||
- **RK3568/RK3566** — an out-of-tree community fork exists (Armbian forum,
|
- **RK3568/RK3566**: an out-of-tree community fork exists (Armbian forum,
|
||||||
["ODROID-M1: RK3568 NPU on the open stack"](https://forum.armbian.com/topic/61651-odroid-m1-rk3568-npu-on-the-open-stack-rocket-kernel-driver-mesa-teflon/)),
|
["ODROID-M1: RK3568 NPU on the open stack"](https://forum.armbian.com/topic/61651-odroid-m1-rk3568-npu-on-the-open-stack-rocket-kernel-driver-mesa-teflon/)),
|
||||||
built on `accel/rocket` "with local fixes" atop the RK3588 Mesa merge request,
|
built on `accel/rocket` "with local fixes" atop the RK3588 Mesa merge request,
|
||||||
requiring **byte-level comparison against captured vendor command streams** to
|
requiring **byte-level comparison against captured vendor command streams** to
|
||||||
work out weight-layout and CBUF differences from RK3588. Not merged upstream.
|
work out weight-layout and CBUF differences from RK3588. Not merged upstream.
|
||||||
Confirms the general pattern: **porting Rocket to a new RKNPU generation is a
|
Confirms the general pattern: **porting Rocket to a new RKNPU generation is a
|
||||||
bespoke reverse-engineering project per SoC, not a recompile** — the same
|
bespoke reverse-engineering project per SoC, not a recompile**, the same
|
||||||
conclusion `npu-graphics-feasibility.md` already reached, now with two more
|
conclusion `npu-graphics-feasibility.md` already reached, now with two more
|
||||||
data points (RK3576, RK3568) supporting it.
|
data points (RK3576, RK3568) supporting it.
|
||||||
- **RV1106/RV1103** — zero hits in this research. No mainline coverage, no known
|
- **RV1106/RV1103**: zero hits in this research. No mainline coverage, no known
|
||||||
public fork, no known RE project targeting it specifically (unlike RK3568 and
|
public fork, no known RE project targeting it specifically (unlike RK3568 and
|
||||||
RK3576, which both have named, in-progress efforts). This is the least-covered
|
RK3576, which both have named, in-progress efforts). This is the least-covered
|
||||||
tier of the RKNPU family in the open-source world today.
|
tier of the RKNPU family in the open-source world today.
|
||||||
@@ -341,7 +341,7 @@ Merged into Mesa 24.1 ([Phoronix](https://www.phoronix.com/news/Gallium3D-Teflon
|
|||||||
[docs.mesa3d.org/teflon.html](https://docs.mesa3d.org/teflon.html)) as a Gallium3D
|
[docs.mesa3d.org/teflon.html](https://docs.mesa3d.org/teflon.html)) as a Gallium3D
|
||||||
frontend for TensorFlow Lite. Per its own docs: **"Teflon only works with etnaviv
|
frontend for TensorFlow Lite. Per its own docs: **"Teflon only works with etnaviv
|
||||||
or rocket gallium drivers."** There is no Teflon path independent of a working
|
or rocket gallium drivers."** There is no Teflon path independent of a working
|
||||||
Rocket kernel driver underneath it — so Teflon's real-world Rockchip coverage is
|
Rocket kernel driver underneath it, so Teflon's real-world Rockchip coverage is
|
||||||
exactly Rocket's: solid on RK3588, experimental/WIP on RK3576 and (unofficially)
|
exactly Rocket's: solid on RK3588, experimental/WIP on RK3576 and (unofficially)
|
||||||
RK3568, absent for RV1106.
|
RK3568, absent for RV1106.
|
||||||
|
|
||||||
@@ -353,7 +353,7 @@ exploratory/documentation stage (structures like `regcfg_amount`/`regcmd_addr`
|
|||||||
identified, DRM device enumerated) with **no compiled tool, compiler, or runtime
|
identified, DRM device enumerated) with **no compiled tool, compiler, or runtime
|
||||||
output**, and does not touch RV1106. This matches
|
output**, and does not touch RV1106. This matches
|
||||||
`npu-graphics-feasibility.md`'s existing finding that the regcmd ISA is
|
`npu-graphics-feasibility.md`'s existing finding that the regcmd ISA is
|
||||||
undocumented outside Rockchip and reasoned-about only, not published — no new
|
undocumented outside Rockchip and reasoned-about only, not published. No new
|
||||||
project has changed that for any SoC generation, let alone this one.
|
project has changed that for any SoC generation, let alone this one.
|
||||||
|
|
||||||
### 4.5 Net conclusion (updated, still holds)
|
### 4.5 Net conclusion (updated, still holds)
|
||||||
@@ -366,8 +366,8 @@ RK3588 (has mainline Rocket + Teflon) or even RK3576/RK3568 (have active,
|
|||||||
imperfect RE efforts), **there is no open compiler, no open runtime, and no known
|
imperfect RE efforts), **there is no open compiler, no open runtime, and no known
|
||||||
public reverse-engineering project of any kind.** Every real inference workload on
|
public reverse-engineering project of any kind.** Every real inference workload on
|
||||||
this NPU has to go through the closed pipeline
|
this NPU has to go through the closed pipeline
|
||||||
(`npu-graphics-feasibility.md §1`: RKNN-Toolkit2 on a PC → `.rknn` blob →
|
(`npu-graphics-feasibility.md section 1`: RKNN-Toolkit2 on a PC -> `.rknn` blob ->
|
||||||
`librknnrt`/RKNN C API on-device) for the foreseeable future — porting the kernel
|
`librknnrt`/RKNN C API on-device) for the foreseeable future; porting the kernel
|
||||||
driver is worth doing (it's real, bounded, evidence-backed work, same class as
|
driver is worth doing (it's real, bounded, evidence-backed work, same class as
|
||||||
RGA), but it does not change that reality, and shouldn't be scoped or sold as if
|
RGA), but it does not change that reality, and shouldn't be scoped or sold as if
|
||||||
it does.
|
it does.
|
||||||
@@ -379,14 +379,14 @@ it does.
|
|||||||
```
|
```
|
||||||
npu/
|
npu/
|
||||||
PORT-PLAN.md this document
|
PORT-PLAN.md this document
|
||||||
compat/ (to add) local stub headers for §2.4:
|
compat/ (to add) local stub headers for section 2.4:
|
||||||
soc/rockchip/rockchip_iommu.h
|
soc/rockchip/rockchip_iommu.h
|
||||||
soc/rockchip/rockchip_opp_select.h
|
soc/rockchip/rockchip_opp_select.h
|
||||||
soc/rockchip/rockchip_system_monitor.h
|
soc/rockchip/rockchip_system_monitor.h
|
||||||
soc/rockchip/rockchip_ipa.h
|
soc/rockchip/rockchip_ipa.h
|
||||||
```
|
```
|
||||||
The vendor driver source itself is not duplicated here (same convention as
|
The vendor driver source itself is not duplicated here (same convention as
|
||||||
`clk/`, `pinctrl/`, `mach/` — vendor source stays forward-ported in
|
`clk/`, `pinctrl/`, `mach/`: vendor source stays forward-ported in
|
||||||
`flare-edge/research/linux-6.18.46/` as a scratch tree; only the durable delta —
|
`flare-edge/research/linux-6.18.46/` as a scratch tree; only the durable delta (
|
||||||
compat shims, DT fragment, config fragment — belongs in this repo, captured as a
|
compat shims, DT fragment, config fragment) belongs in this repo, captured as a
|
||||||
patch series once M6 actually lands).
|
patch series once M6 actually lands).
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
Status: **zImage + rockchip/rv1106-warden.dtb build cleanly, 0 errors, 0 warnings**,
|
Status: **zImage + rockchip/rv1106-warden.dtb build cleanly, 0 errors, 0 warnings**,
|
||||||
with `CONFIG_ROCKCHIP_RKNPU=y` (+ `_DRM_GEM`/`_DEBUG_FS`/`_PROC_FS=y`) built in, 99
|
with `CONFIG_ROCKCHIP_RKNPU=y` (+ `_DRM_GEM`/`_DEBUG_FS`/`_PROC_FS=y`) built in, 99
|
||||||
`rknpu`-prefixed symbols linked into `System.map`, and `&npu { status = "okay"; }`
|
`rknpu`-prefixed symbols linked into `System.map`, and `&npu { status = "okay"; }`
|
||||||
in the board dtb. **Not flashed or probed on hardware** — per this task's explicit
|
in the board dtb. **Not flashed or probed on hardware**, per this task's explicit
|
||||||
boundary, this is a build-only port; on-target verification (dmesg probe log,
|
boundary, this is a build-only port; on-target verification (dmesg probe log,
|
||||||
`/dev/dri/cardN`, the version-query ioctl round trip) is deferred to the parent
|
`/dev/dri/cardN`, the version-query ioctl round trip) is deferred to the parent
|
||||||
session. GPL source port only — no closed blob, same class of work as the RGA and
|
session. GPL source port only: no closed blob, same class of work as the RGA and
|
||||||
audio ports in this series (`../rga/PORT-DONE.md`, `../audio/PORT-PROGRESS.md`).
|
audio ports in this series (`../rga/PORT-DONE.md`, `../audio/PORT-PROGRESS.md`).
|
||||||
|
|
||||||
Trees involved:
|
Trees involved:
|
||||||
@@ -16,27 +16,27 @@ Trees involved:
|
|||||||
(v0.9.2, `DRIVER_DATE "20230825"`)
|
(v0.9.2, `DRIVER_DATE "20230825"`)
|
||||||
|
|
||||||
Scope and strategic framing are in `PORT-PLAN.md` (file-by-file plan, authoritative)
|
Scope and strategic framing are in `PORT-PLAN.md` (file-by-file plan, authoritative)
|
||||||
and `OPEN-NPU-PLAN.md` (the "open driver, closed userspace" reality — this port
|
and `OPEN-NPU-PLAN.md` (the "open driver, closed userspace" reality, this port
|
||||||
delivers exactly Tier A there: an open, on-hardware-verifiable *kernel* driver, and
|
delivers exactly Tier A there: an open, on-hardware-verifiable *kernel* driver, and
|
||||||
nothing more; it does not and cannot unlock running a model).
|
nothing more; it does not and cannot unlock running a model).
|
||||||
|
|
||||||
## File set copied
|
## File set copied
|
||||||
|
|
||||||
Per `PORT-PLAN.md` §2.1's "Yes" column, copied verbatim from the vendor tree into
|
Per `PORT-PLAN.md` section 2.1's "Yes" column, copied verbatim from the vendor tree into
|
||||||
`drivers/rknpu/` (patched in place afterward — see API-delta table below):
|
`drivers/rknpu/` (patched in place afterward, see API-delta table below):
|
||||||
|
|
||||||
| File | Bring in | Notes |
|
| File | Bring in | Notes |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `rknpu_drv.c` / `include/rknpu_drv.h` | yes | probe/remove, of_match table (incl. the RV1106 entry + `rv1106_rknpu_config`), DRM driver registration, power get/put |
|
| `rknpu_drv.c` / `include/rknpu_drv.h` | yes | probe/remove, of_match table (incl. the RV1106 entry + `rv1106_rknpu_config`), DRM driver registration, power get/put |
|
||||||
| `rknpu_job.c` / `include/rknpu_job.h` | yes | job submit, IRQ handler, PC task-list execution, `rknpu_get_hw_version()`/`rknpu_get_drv_version()` |
|
| `rknpu_job.c` / `include/rknpu_job.h` | yes | job submit, IRQ handler, PC task-list execution, `rknpu_get_hw_version()`/`rknpu_get_drv_version()` |
|
||||||
| `rknpu_gem.c` / `include/rknpu_gem.h` | yes | GEM memory manager (DRM_GEM path) |
|
| `rknpu_gem.c` / `include/rknpu_gem.h` | yes | GEM memory manager (DRM_GEM path) |
|
||||||
| `rknpu_reset.c` / `include/rknpu_reset.h` | yes | `SRST_A_RKNN`/`SRST_H_RKNN` reset control — compiled unmodified, zero API drift |
|
| `rknpu_reset.c` / `include/rknpu_reset.h` | yes | `SRST_A_RKNN`/`SRST_H_RKNN` reset control: compiled unmodified, zero API drift |
|
||||||
| `rknpu_iommu.c` / `include/rknpu_iommu.h` | yes (dead code path) | 61 lines; `iommu_en` stays false on our non-IOMMU DT — compiled unmodified |
|
| `rknpu_iommu.c` / `include/rknpu_iommu.h` | yes (dead code path) | 61 lines; `iommu_en` stays false on our non-IOMMU DT: compiled unmodified |
|
||||||
| `rknpu_debugger.c` / `include/rknpu_debugger.h` | yes | `/proc/rknpu/load` continuity target for the Monitor page — compiled unmodified |
|
| `rknpu_debugger.c` / `include/rknpu_debugger.h` | yes | `/proc/rknpu/load` continuity target for the Monitor page: compiled unmodified |
|
||||||
| `rknpu_mem.c`, `rknpu_mm.c`, `rknpu_fence.c` | **no** (headers only) | DMA_HEAP / SRAM / FENCE variants — not enabled for this port (§2.2). Their headers (`rknpu_mem.h`, `rknpu_mm.h`, `rknpu_fence.h`) *are* copied because `rknpu_drv.h`/`rknpu_gem.h` include them unconditionally for struct/prototype declarations, but zero object code results: every call site into these three is either `#ifdef CONFIG_ROCKCHIP_RKNPU_{DMA_HEAP,SRAM,FENCE}` (compiled out, all three configs left off) or `if (IS_ENABLED(CONFIG_ROCKCHIP_RKNPU_SRAM) && ...)` (compile-time-constant-folded to dead code and dropped by the optimizer — confirmed: none of `rknpu_mm_*`/`rknpu_mem_*` appear in `System.map`) |
|
| `rknpu_mem.c`, `rknpu_mm.c`, `rknpu_fence.c` | **no** (headers only) | DMA_HEAP / SRAM / FENCE variants: not enabled for this port (section 2.2). Their headers (`rknpu_mem.h`, `rknpu_mm.h`, `rknpu_fence.h`) *are* copied because `rknpu_drv.h`/`rknpu_gem.h` include them unconditionally for struct/prototype declarations, but zero object code results: every call site into these three is either `#ifdef CONFIG_ROCKCHIP_RKNPU_{DMA_HEAP,SRAM,FENCE}` (compiled out, all three configs left off) or `if (IS_ENABLED(CONFIG_ROCKCHIP_RKNPU_SRAM) && ...)` (compile-time-constant-folded to dead code and dropped by the optimizer, confirmed: none of `rknpu_mm_*`/`rknpu_mem_*` appear in `System.map`) |
|
||||||
|
|
||||||
UAPI header: `rknpu_ioctl.h` was split the way `drivers/accel/rocket/` (the mainline
|
UAPI header: `rknpu_ioctl.h` was split the way `drivers/accel/rocket/` (the mainline
|
||||||
Rocket driver, already vendored in this tree) does it — the real content lives at
|
Rocket driver, already vendored in this tree) does it; the real content lives at
|
||||||
`include/uapi/drm/rknpu_ioctl.h` (mainline convention for DRM ioctl UAPI headers),
|
`include/uapi/drm/rknpu_ioctl.h` (mainline convention for DRM ioctl UAPI headers),
|
||||||
and `drivers/rknpu/include/rknpu_ioctl.h` is a one-line redirect (`#include
|
and `drivers/rknpu/include/rknpu_ioctl.h` is a one-line redirect (`#include
|
||||||
<drm/rknpu_ioctl.h>`) so the vendor source's unmodified `#include "rknpu_ioctl.h"`
|
<drm/rknpu_ioctl.h>`) so the vendor source's unmodified `#include "rknpu_ioctl.h"`
|
||||||
@@ -45,33 +45,33 @@ keeps working. This also makes the header directly usable by
|
|||||||
|
|
||||||
## Kconfig / Makefile wiring
|
## Kconfig / Makefile wiring
|
||||||
|
|
||||||
`drivers/rknpu/Kconfig` (new) — trimmed from the vendor's own `Kconfig`: drops the
|
`drivers/rknpu/Kconfig` (new), trimmed from the vendor's own `Kconfig`: drops the
|
||||||
`ROCKCHIP_RKNPU_SRAM`/`_FENCE` options and the `DRM_GEM` vs `DMA_HEAP` `choice`
|
`ROCKCHIP_RKNPU_SRAM`/`_FENCE` options and the `DRM_GEM` vs `DMA_HEAP` `choice`
|
||||||
block entirely (DRM_GEM is the only memory manager this port wires up — see
|
block entirely (DRM_GEM is the only memory manager this port wires up, see
|
||||||
`PORT-PLAN.md` §1 "Memory manager choice"). Sourced from `drivers/Kconfig` right
|
`PORT-PLAN.md` section 1 "Memory manager choice"). Sourced from `drivers/Kconfig` right
|
||||||
after `source "drivers/accel/Kconfig"` (RKNPU is a classic DRM device, not
|
after `source "drivers/accel/Kconfig"` (RKNPU is a classic DRM device, not
|
||||||
`drivers/accel/` — see §1 of `PORT-PLAN.md`, "not `/dev/accel/`").
|
`drivers/accel/`, see section 1 of `PORT-PLAN.md`, "not `/dev/accel/`").
|
||||||
|
|
||||||
`drivers/rknpu/Makefile` (new) — trimmed from the vendor's own `Makefile`: only the
|
`drivers/rknpu/Makefile` (new), trimmed from the vendor's own `Makefile`: only the
|
||||||
five always-on objects plus GEM (`rknpu_drv.o`, `rknpu_reset.o`, `rknpu_job.o`,
|
five always-on objects plus GEM (`rknpu_drv.o`, `rknpu_reset.o`, `rknpu_job.o`,
|
||||||
`rknpu_debugger.o`, `rknpu_iommu.o`, `rknpu_gem.o` gated on
|
`rknpu_debugger.o`, `rknpu_iommu.o`, `rknpu_gem.o` gated on
|
||||||
`CONFIG_ROCKCHIP_RKNPU_DRM_GEM`); no edit needed for the never-built
|
`CONFIG_ROCKCHIP_RKNPU_DRM_GEM`); no edit needed for the never-built
|
||||||
`rknpu_mem.o`/`rknpu_mm.o`/`rknpu_fence.o` lines because they're simply omitted
|
`rknpu_mem.o`/`rknpu_mm.o`/`rknpu_fence.o` lines because they're simply omitted
|
||||||
(matches `PORT-PLAN.md` §2.1's note that no Makefile edit is required, just not
|
(matches `PORT-PLAN.md` section 2.1's note that no Makefile edit is required, just not
|
||||||
setting their Kconfig symbols). `ccflags-y` adds `compat/` to the include path
|
setting their Kconfig symbols). `ccflags-y` adds `compat/` to the include path
|
||||||
**ahead of** `include/` — see the compat-shim section below for why the ordering
|
**ahead of** `include/`. See the compat-shim section below for why the ordering
|
||||||
matters.
|
matters.
|
||||||
|
|
||||||
Wired into `drivers/Makefile`: `obj-$(CONFIG_ROCKCHIP_RKNPU) += rknpu/` placed
|
Wired into `drivers/Makefile`: `obj-$(CONFIG_ROCKCHIP_RKNPU) += rknpu/` placed
|
||||||
immediately after `obj-y += gpu/` (rknpu registers a classic DRM device, so — like
|
immediately after `obj-y += gpu/` (rknpu registers a classic DRM device, so (like
|
||||||
`gpu/` itself — it must come after `char/` and `iommu/` per the existing comments
|
`gpu/` itself) it must come after `char/` and `iommu/` per the existing comments
|
||||||
there, and building after `gpu/` specifically ensures the DRM core is ready first).
|
there, and building after `gpu/` specifically ensures the DRM core is ready first).
|
||||||
|
|
||||||
## Config symbols set
|
## Config symbols set
|
||||||
|
|
||||||
Via `./scripts/config --enable <SYMBOL>` then `make ARCH=arm CROSS_COMPILE=...
|
Via `./scripts/config --enable <SYMBOL>` then `make ARCH=arm CROSS_COMPILE=...
|
||||||
olddefconfig`, confirmed `=y` in `.config` afterward (no silent Kconfig-dependency
|
olddefconfig`, confirmed `=y` in `.config` afterward (no silent Kconfig-dependency
|
||||||
drop — all four symbols round-tripped through `olddefconfig` unchanged):
|
drop, all four symbols round-tripped through `olddefconfig` unchanged):
|
||||||
|
|
||||||
```
|
```
|
||||||
CONFIG_ROCKCHIP_RKNPU=y
|
CONFIG_ROCKCHIP_RKNPU=y
|
||||||
@@ -81,52 +81,52 @@ CONFIG_ROCKCHIP_RKNPU_PROC_FS=y
|
|||||||
```
|
```
|
||||||
|
|
||||||
Prerequisites already satisfied pre-port: `CONFIG_DRM=y`, `CONFIG_ARCH_ROCKCHIP=y`
|
Prerequisites already satisfied pre-port: `CONFIG_DRM=y`, `CONFIG_ARCH_ROCKCHIP=y`
|
||||||
(both already on from the M4 display work). Left off per `PORT-PLAN.md` §2.2/§2.6:
|
(both already on from the M4 display work). Left off per `PORT-PLAN.md` section 2.2/section 2.6:
|
||||||
`ROCKCHIP_RKNPU_DMA_HEAP`, `ROCKCHIP_RKNPU_SRAM`, `ROCKCHIP_RKNPU_FENCE` — RV1106
|
`ROCKCHIP_RKNPU_DMA_HEAP`, `ROCKCHIP_RKNPU_SRAM`, `ROCKCHIP_RKNPU_FENCE`; RV1106
|
||||||
needs none of them (single-core NPU, no NBUF, no OPP table, no multi-domain
|
needs none of them (single-core NPU, no NBUF, no OPP table, no multi-domain
|
||||||
fencing).
|
fencing).
|
||||||
|
|
||||||
## The four compat-shim headers (`drivers/rknpu/compat/soc/rockchip/`)
|
## The four compat-shim headers (`drivers/rknpu/compat/soc/rockchip/`)
|
||||||
|
|
||||||
All four vendor-only headers from `PORT-PLAN.md` §2.4 (absent from mainline) are
|
All four vendor-only headers from `PORT-PLAN.md` section 2.4 (absent from mainline) are
|
||||||
addressed, but **not uniformly** — one of the four needed a genuinely fresh shim
|
addressed, but **not uniformly**, one of the four needed a genuinely fresh shim
|
||||||
rather than a vendor-verbatim copy, for a reason the plan didn't anticipate:
|
rather than a vendor-verbatim copy, for a reason the plan didn't anticipate:
|
||||||
|
|
||||||
### `rockchip_iommu.h` — written fresh, NOT a copy of the vendor header
|
### `rockchip_iommu.h`: written fresh, NOT a copy of the vendor header
|
||||||
|
|
||||||
This is the one real surprise of the port. The vendor header itself already
|
This is the one real surprise of the port. The vendor header itself already
|
||||||
carries a working `#if IS_ENABLED(CONFIG_ROCKCHIP_IOMMU) ... #else <stub> #endif`
|
carries a working `#if IS_ENABLED(CONFIG_ROCKCHIP_IOMMU) ... #else <stub> #endif`
|
||||||
split, so naively copying it looked safe. **It is not**, in this specific tree:
|
split, so naively copying it looked safe. **It is not**, in this specific tree:
|
||||||
`CONFIG_ROCKCHIP_IOMMU` is not a vacant symbol here — it's `=y` in our `.config`
|
`CONFIG_ROCKCHIP_IOMMU` is not a vacant symbol here: it's `=y` in our `.config`
|
||||||
already, for the *real*, unrelated mainline IOMMU driver
|
already, for the *real*, unrelated mainline IOMMU driver
|
||||||
(`drivers/iommu/rockchip-iommu.c`, pulled in by the M4 display/VOP2 work). That
|
(`drivers/iommu/rockchip-iommu.c`, pulled in by the M4 display/VOP2 work). That
|
||||||
driver implements the standard `struct iommu_ops` and does **not** export a
|
driver implements the standard `struct iommu_ops` and does **not** export a
|
||||||
function called `rockchip_iommu_is_enabled()` — confirmed by grepping
|
function called `rockchip_iommu_is_enabled()`, confirmed by grepping
|
||||||
`drivers/iommu/rockchip-iommu.c` for the symbol (zero hits). Had the vendor
|
`drivers/iommu/rockchip-iommu.c` for the symbol (zero hits). Had the vendor
|
||||||
header been copied as-is, `IS_ENABLED(CONFIG_ROCKCHIP_IOMMU)` would have evaluated
|
header been copied as-is, `IS_ENABLED(CONFIG_ROCKCHIP_IOMMU)` would have evaluated
|
||||||
true (not false, as the plan assumed) and selected the `extern bool
|
true (not false, as the plan assumed) and selected the `extern bool
|
||||||
rockchip_iommu_is_enabled(struct device *dev);` declaration with **no definition
|
rockchip_iommu_is_enabled(struct device *dev);` declaration with **no definition
|
||||||
anywhere in the tree** — a link failure that would only show up at the very end of
|
anywhere in the tree**: a link failure that would only show up at the very end of
|
||||||
a full kernel build, not at this file's own compile step.
|
a full kernel build, not at this file's own compile step.
|
||||||
|
|
||||||
Fix: `drivers/rknpu/compat/soc/rockchip/rockchip_iommu.h` is a fresh 25-line file,
|
Fix: `drivers/rknpu/compat/soc/rockchip/rockchip_iommu.h` is a fresh 25-line file,
|
||||||
unconditional (no `#if IS_ENABLED(...)` at all), providing only the one symbol
|
unconditional (no `#if IS_ENABLED(...)` at all), providing only the one symbol
|
||||||
`rknpu_drv.c` actually calls: `rockchip_iommu_is_enabled()` (used once, in
|
`rknpu_drv.c` actually calls: `rockchip_iommu_is_enabled()` (used once, in
|
||||||
`rknpu_power_off()`, inside `if (rknpu_dev->multiple_domains)` — true only for
|
`rknpu_power_off()`, inside `if (rknpu_dev->multiple_domains)`; true only for
|
||||||
RK3588's 3-core NPU; RV1106's `rv1106_rknpu_config` never sets it, and the board DT
|
RK3588's 3-core NPU; RV1106's `rv1106_rknpu_config` never sets it, and the board DT
|
||||||
carries no `iommus=` property, so this is genuinely dead code, exactly as
|
carries no `iommus=` property, so this is genuinely dead code, exactly as
|
||||||
`PORT-PLAN.md` §2.4 predicted — the fix just couldn't be "copy the vendor header,"
|
`PORT-PLAN.md` section 2.4 predicted: the fix just couldn't be "copy the vendor header,"
|
||||||
it had to be "write an unconditional one that doesn't shadow-collide with this
|
it had to be "write an unconditional one that doesn't shadow-collide with this
|
||||||
tree's real `CONFIG_ROCKCHIP_IOMMU`."
|
tree's real `CONFIG_ROCKCHIP_IOMMU`."
|
||||||
|
|
||||||
### `rockchip_opp_select.h`, `rockchip_system_monitor.h`, `rockchip_ipa.h` — copied verbatim, safe as-is
|
### `rockchip_opp_select.h`, `rockchip_system_monitor.h`, `rockchip_ipa.h`: copied verbatim, safe as-is
|
||||||
|
|
||||||
Unlike `ROCKCHIP_IOMMU`, none of `CONFIG_ROCKCHIP_OPP`, `CONFIG_ROCKCHIP_SYSTEM_MONITOR`,
|
Unlike `ROCKCHIP_IOMMU`, none of `CONFIG_ROCKCHIP_OPP`, `CONFIG_ROCKCHIP_SYSTEM_MONITOR`,
|
||||||
`CONFIG_ROCKCHIP_IPA` exist anywhere in mainline (grepped every `Kconfig` in the
|
`CONFIG_ROCKCHIP_IPA` exist anywhere in mainline (grepped every `Kconfig` in the
|
||||||
tree and the `.config` — zero hits for all three), so these three vendor headers'
|
tree and the `.config`, zero hits for all three), so these three vendor headers'
|
||||||
own `#if IS_ENABLED(...)`/`#if IS_REACHABLE(...)` gates always evaluate false and
|
own `#if IS_ENABLED(...)`/`#if IS_REACHABLE(...)` gates always evaluate false and
|
||||||
their static-inline stub branches (`-EOPNOTSUPP`/`ERR_PTR(-ENOTSUPP)`/no-ops) are
|
their static-inline stub branches (`-EOPNOTSUPP`/`ERR_PTR(-ENOTSUPP)`/no-ops) are
|
||||||
always selected — no collision risk, copied byte-for-byte from
|
always selected, no collision risk, copied byte-for-byte from
|
||||||
`sdk/sysdrv/source/kernel/include/soc/rockchip/`, with only an explanatory header
|
`sdk/sysdrv/source/kernel/include/soc/rockchip/`, with only an explanatory header
|
||||||
comment added to each. The full struct definitions (`struct rockchip_opp_info`,
|
comment added to each. The full struct definitions (`struct rockchip_opp_info`,
|
||||||
`struct monitor_dev_info`, `struct ipa_power_model_data`) are still required
|
`struct monitor_dev_info`, `struct ipa_power_model_data`) are still required
|
||||||
@@ -137,7 +137,7 @@ pointers dereferenced in the (dead-for-RV1106, but still compiled) DVFS code pat
|
|||||||
|
|
||||||
`compat/` is listed **first** in `ccflags-y` (ahead of `include/`), specifically so
|
`compat/` is listed **first** in `ccflags-y` (ahead of `include/`), specifically so
|
||||||
these four shadow anything a future in-tree `soc/rockchip/` addition might
|
these four shadow anything a future in-tree `soc/rockchip/` addition might
|
||||||
introduce — `rockchip_iommu.h` in particular must never resolve to a different,
|
introduce. `rockchip_iommu.h` in particular must never resolve to a different,
|
||||||
unrelated header of the same name.
|
unrelated header of the same name.
|
||||||
|
|
||||||
### Devfreq/OPP/monitor/IPA code: compiles, but is entirely unreferenced for our build
|
### Devfreq/OPP/monitor/IPA code: compiles, but is entirely unreferenced for our build
|
||||||
@@ -149,18 +149,18 @@ different implementations, one per era). For 6.18 the `#else` (>= 6.1) branch is
|
|||||||
what's textually compiled, and it references file-scope statics (`npu_mdevp`,
|
what's textually compiled, and it references file-scope statics (`npu_mdevp`,
|
||||||
`npu_devfreq_profile`, `npu_cooling_power`) that a naive manual read of the
|
`npu_devfreq_profile`, `npu_cooling_power`) that a naive manual read of the
|
||||||
surrounding `#if` nesting suggested might only be declared under the sibling
|
surrounding `#if` nesting suggested might only be declared under the sibling
|
||||||
`< 6.1` branch. **This did not manifest as a build error** — `rknpu_drv.o` compiled
|
`< 6.1` branch. **This did not manifest as a build error**: `rknpu_drv.o` compiled
|
||||||
clean on the first fixed pass, which is the authoritative answer (per this task's
|
clean on the first fixed pass, which is the authoritative answer (per this task's
|
||||||
own "prove by compiling, not by inspection" instruction — a manual preprocessor
|
own "prove by compiling, not by inspection" instruction, a manual preprocessor
|
||||||
trace over ~700 lines of nested version gates is exactly the kind of thing to
|
trace over ~700 lines of nested version gates is exactly the kind of thing to
|
||||||
distrust vs. the compiler). Confirmed after the fact: **none** of
|
distrust vs. the compiler). Confirmed after the fact: **none** of
|
||||||
`rknpu_devfreq_init`, `npu_devfreq_target`, `npu_devfreq_profile`, `npu_mdevp`, or
|
`rknpu_devfreq_init`, `npu_devfreq_target`, `npu_devfreq_profile`, `npu_mdevp`, or
|
||||||
`npu_cooling_power` appear in `System.map` — the call site into
|
`npu_cooling_power` appear in `System.map`; the call site into
|
||||||
`rknpu_devfreq_init()` (in `rknpu_probe()`) is itself gated by the same `#if
|
`rknpu_devfreq_init()` (in `rknpu_probe()`) is itself gated by the same `#if
|
||||||
KERNEL_VERSION(6,1,0) > LINUX_VERSION_CODE`, false for 6.18, so the whole devfreq
|
KERNEL_VERSION(6,1,0) > LINUX_VERSION_CODE`, false for 6.18, so the whole devfreq
|
||||||
init path is unreachable and GCC drops the unused `static` functions entirely.
|
init path is unreachable and GCC drops the unused `static` functions entirely.
|
||||||
Net effect matches `PORT-PLAN.md`'s framing exactly — RV1106 has no OPP table and
|
Net effect matches `PORT-PLAN.md`'s framing exactly: RV1106 has no OPP table and
|
||||||
no DVFS — just via straightforward dead-code elimination rather than anything this
|
no DVFS, just via straightforward dead-code elimination rather than anything this
|
||||||
port had to force.
|
port had to force.
|
||||||
|
|
||||||
## 5.10 -> 6.18 API-delta fixes (all mechanical, all found by iterating single-object builds)
|
## 5.10 -> 6.18 API-delta fixes (all mechanical, all found by iterating single-object builds)
|
||||||
@@ -169,34 +169,34 @@ port had to force.
|
|||||||
|
|
||||||
| # | Change |
|
| # | Change |
|
||||||
|---|---|
|
|---|---|
|
||||||
| 1 | `struct drm_driver` has no `.gem_prime_mmap` member any more (the legacy driver-level `gem_prime_*` fallback vtable was removed). Dropped the `.gem_prime_mmap = drm_gem_prime_mmap,` initializer for the `KERNEL_VERSION(6,1,0) <= LINUX_VERSION_CODE` branch — not a functionality loss, because per-object mmap is already wired via `drm_gem_object_funcs.mmap = rknpu_gem_mmap_obj` in `rknpu_gem.c`'s `rknpu_gem_object_funcs` (the mechanism this field used to be a fallback *from*, per `PORT-PLAN.md` §2.5). |
|
| 1 | `struct drm_driver` has no `.gem_prime_mmap` member any more (the legacy driver-level `gem_prime_*` fallback vtable was removed). Dropped the `.gem_prime_mmap = drm_gem_prime_mmap,` initializer for the `KERNEL_VERSION(6,1,0) <= LINUX_VERSION_CODE` branch: not a functionality loss, because per-object mmap is already wired via `drm_gem_object_funcs.mmap = rknpu_gem_mmap_obj` in `rknpu_gem.c`'s `rknpu_gem_object_funcs` (the mechanism this field used to be a fallback *from*, per `PORT-PLAN.md` section 2.5). |
|
||||||
| 2 | `struct drm_driver` has no `.date` member any more (dropped from mainline DRM). Removed the `.date = DRIVER_DATE,` initializer; `.major`/`.minor`/`.patchlevel` already carry the version. |
|
| 2 | `struct drm_driver` has no `.date` member any more (dropped from mainline DRM). Removed the `.date = DRIVER_DATE,` initializer; `.major`/`.minor`/`.patchlevel` already carry the version. |
|
||||||
| 3 | `hrtimer_init(&t, clock, mode)` + separate `t.function = fn` assignment -> combined `hrtimer_setup(&t, fn, clock, mode)` (same fix class as the RGA port's hrtimer change, `../rga/PORT-DONE.md` item 2). |
|
| 3 | `hrtimer_init(&t, clock, mode)` + separate `t.function = fn` assignment -> combined `hrtimer_setup(&t, fn, clock, mode)` (same fix class as the RGA port's hrtimer change, `../rga/PORT-DONE.md` item 2). |
|
||||||
| 4 | `platform_driver.remove`: `int (*)(struct platform_device *)` -> `void (*)(struct platform_device *)`. Changed `rknpu_remove()` from `static int ... { ...; return 0; }` to `static void ...` (dropped the trailing `return 0;`) — same fix as the RGA and audio ports. |
|
| 4 | `platform_driver.remove`: `int (*)(struct platform_device *)` -> `void (*)(struct platform_device *)`. Changed `rknpu_remove()` from `static int ... { ...; return 0; }` to `static void ...` (dropped the trailing `return 0;`), same fix as the RGA and audio ports. |
|
||||||
| 5 | `MODULE_IMPORT_NS(DMA_BUF)` -> `MODULE_IMPORT_NS("DMA_BUF")` (quoted-string form; same fix as the RGA port item 6). |
|
| 5 | `MODULE_IMPORT_NS(DMA_BUF)` -> `MODULE_IMPORT_NS("DMA_BUF")` (quoted-string form; same fix as the RGA port item 6). |
|
||||||
|
|
||||||
### `rknpu_gem.c`
|
### `rknpu_gem.c`
|
||||||
|
|
||||||
| # | Change |
|
| # | Change |
|
||||||
|---|---|
|
|---|---|
|
||||||
| 1 | `<linux/pfn_t.h>` and the `pfn_t` wrapper type (`__pfn_to_pfn_t()`, `PFN_DEV`) were removed entirely from mainline. Dropped the include; `vmf_insert_mixed()` (the only call site reachable at `KERNEL_VERSION(4,15,0) <= LINUX_VERSION_CODE`, which is our branch) now takes a plain `unsigned long pfn` directly — `pfn = page_to_pfn(...)` was already computing that raw value, so the fix is just passing `pfn` instead of `__pfn_to_pfn_t(pfn, PFN_DEV)`. |
|
| 1 | `<linux/pfn_t.h>` and the `pfn_t` wrapper type (`__pfn_to_pfn_t()`, `PFN_DEV`) were removed entirely from mainline. Dropped the include; `vmf_insert_mixed()` (the only call site reachable at `KERNEL_VERSION(4,15,0) <= LINUX_VERSION_CODE`, which is our branch) now takes a plain `unsigned long pfn` directly: `pfn = page_to_pfn(...)` was already computing that raw value, so the fix is just passing `pfn` instead of `__pfn_to_pfn_t(pfn, PFN_DEV)`. |
|
||||||
| 2 | `vmap()`/`vunmap()`/`VM_MAP` used to be pulled in transitively; 6.18 needs `<linux/vmalloc.h>` included explicitly. Added it. |
|
| 2 | `vmap()`/`vunmap()`/`VM_MAP` used to be pulled in transitively; 6.18 needs `<linux/vmalloc.h>` included explicitly. Added it. |
|
||||||
| 3 | `%zu` format specifier for `rknpu_obj->size` (`-Werror=format=`): the field is declared `unsigned long`, not `size_t`, on this target — changed to `%lu`. (Pure `-Wformat` pickiness, not a real 5.10-vs-6.18 delta; the vendor's own type just doesn't match `%zu` on this ABI and newer GCC/kernel `-Werror` catches it.) |
|
| 3 | `%zu` format specifier for `rknpu_obj->size` (`-Werror=format=`): the field is declared `unsigned long`, not `size_t`, on this target, changed to `%lu`. (Pure `-Wformat` pickiness, not a real 5.10-vs-6.18 delta; the vendor's own type just doesn't match `%zu` on this ABI and newer GCC/kernel `-Werror` catches it.) |
|
||||||
| 4 | `iommu_map()` gained a trailing `gfp_t gfp` argument (`iommu_map(domain, iova, paddr, size, prot)` -> `iommu_map(domain, iova, paddr, size, prot, gfp)`). Added `GFP_KERNEL` at both call sites (cache-buffer path and the per-sg-entry DDR path). |
|
| 4 | `iommu_map()` gained a trailing `gfp_t gfp` argument (`iommu_map(domain, iova, paddr, size, prot)` -> `iommu_map(domain, iova, paddr, size, prot, gfp)`). Added `GFP_KERNEL` at both call sites (cache-buffer path and the per-sg-entry DDR path). |
|
||||||
| 5 | `vma->vm_flags` is a read-only field now (direct assignment is a compile error, not just deprecated) — replaced all four sites with `vm_flags_set()`/`vm_flags_clear()`: `rknpu_gem_mmap_pages()` (`VM_MIXEDMAP`), `rknpu_gem_mmap_cache()` (`VM_MIXEDMAP`), `rknpu_gem_mmap_buffer()` (`VM_DONTCOPY\|VM_DONTEXPAND\|VM_DONTDUMP\|VM_IO` set, `VM_PFNMAP` cleared). The three read-only accesses (`vm_get_page_prot(vma->vm_flags)` in `rknpu_gem_mmap`) needed no change — only assignment is blocked. |
|
| 5 | `vma->vm_flags` is a read-only field now (direct assignment is a compile error, not just deprecated), replaced all four sites with `vm_flags_set()`/`vm_flags_clear()`: `rknpu_gem_mmap_pages()` (`VM_MIXEDMAP`), `rknpu_gem_mmap_cache()` (`VM_MIXEDMAP`), `rknpu_gem_mmap_buffer()` (`VM_DONTCOPY\|VM_DONTEXPAND\|VM_DONTDUMP\|VM_IO` set, `VM_PFNMAP` cleared). The three read-only accesses (`vm_get_page_prot(vma->vm_flags)` in `rknpu_gem_mmap`) needed no change: only assignment is blocked. |
|
||||||
|
|
||||||
`rknpu_reset.c`, `rknpu_iommu.c`, `rknpu_debugger.c`, `rknpu_job.c` needed **zero**
|
`rknpu_reset.c`, `rknpu_iommu.c`, `rknpu_debugger.c`, `rknpu_job.c` needed **zero**
|
||||||
changes — compiled clean against 6.18 unmodified, confirming `PORT-PLAN.md` §2.5's
|
changes: compiled clean against 6.18 unmodified, confirming `PORT-PLAN.md` section 2.5's
|
||||||
assessment that most of the surface was already correctly version-gated by the
|
assessment that most of the surface was already correctly version-gated by the
|
||||||
vendor.
|
vendor.
|
||||||
|
|
||||||
## Device tree change
|
## Device tree change
|
||||||
|
|
||||||
One board-DTS override appended to `arch/arm/boot/dts/rockchip/rv1106-warden.dts`
|
One board-DTS override appended to `arch/arm/boot/dts/rockchip/rv1106-warden.dts`
|
||||||
(base `rv1106.dtsi`'s `npu@ff660000` node — `compatible`, `reg`, `interrupts`
|
(base `rv1106.dtsi`'s `npu@ff660000` node: `compatible`, `reg`, `interrupts`
|
||||||
(`GIC_SPI 109`), `clocks` (`ACLK_RKNN`/`HCLK_RKNN`), `assigned-clock-rates
|
(`GIC_SPI 109`), `clocks` (`ACLK_RKNN`/`HCLK_RKNN`), `assigned-clock-rates
|
||||||
= <420000000>`, `resets` (`SRST_A_RKNN`/`SRST_H_RKNN`) — left untouched, per
|
= <420000000>`, `resets` (`SRST_A_RKNN`/`SRST_H_RKNN`), left untouched, per
|
||||||
instructions, exactly as `PORT-PLAN.md` §2.3 specified):
|
instructions, exactly as `PORT-PLAN.md` section 2.3 specified):
|
||||||
|
|
||||||
```dts
|
```dts
|
||||||
&npu {
|
&npu {
|
||||||
@@ -225,7 +225,7 @@ make ARCH=arm CROSS_COMPILE=$CROSS_COMPILE zImage rockchip/rv1106-warden.dtb -j"
|
|||||||
```
|
```
|
||||||
|
|
||||||
Result: **exit 0**. `grep -iE "error|warn"` over the full build log returns exactly
|
Result: **exit 0**. `grep -iE "error|warn"` over the full build log returns exactly
|
||||||
one line — the pre-existing, NPU-unrelated dtc warning noted above. Zero errors,
|
one line: the pre-existing, NPU-unrelated dtc warning noted above. Zero errors,
|
||||||
zero rknpu-related warnings. `arch/arm/boot/zImage` (8.7 MB) and
|
zero rknpu-related warnings. `arch/arm/boot/zImage` (8.7 MB) and
|
||||||
`arch/arm/boot/dts/rockchip/rv1106-warden.dtb` (36.9 KB) both produced. XZ kernel
|
`arch/arm/boot/dts/rockchip/rv1106-warden.dtb` (36.9 KB) both produced. XZ kernel
|
||||||
compression and the existing console/earlycon config were left untouched, per
|
compression and the existing console/earlycon config were left untouched, per
|
||||||
@@ -235,7 +235,7 @@ etc.) was reverted or altered.
|
|||||||
## Verification: rknpu is genuinely linked in (not silently dropped to a module)
|
## Verification: rknpu is genuinely linked in (not silently dropped to a module)
|
||||||
|
|
||||||
Per the task's explicit instruction, checked with **host** `grep` on `System.map`
|
Per the task's explicit instruction, checked with **host** `grep` on `System.map`
|
||||||
(ground truth for built-in linkage — cross-`nm` mis-lists symbols on this
|
(ground truth for built-in linkage, cross-`nm` mis-lists symbols on this
|
||||||
toolchain, same caveat as every other port in this series):
|
toolchain, same caveat as every other port in this series):
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -259,7 +259,7 @@ symbol presence the task asked to confirm.
|
|||||||
## Hardware test program: `rknpu_version_test.c`
|
## Hardware test program: `rknpu_version_test.c`
|
||||||
|
|
||||||
Written to `warden-sdk/kernel/rv1106-enablement/npu/rknpu_version_test.c` per the
|
Written to `warden-sdk/kernel/rv1106-enablement/npu/rknpu_version_test.c` per the
|
||||||
task's spec. Dependency-free beyond the kernel tree's own UAPI headers — no
|
task's spec. Dependency-free beyond the kernel tree's own UAPI headers, no
|
||||||
libdrm, no target sysroot headers:
|
libdrm, no target sysroot headers:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -271,11 +271,11 @@ arm-rockchip830-linux-uclibcgnueabihf-gcc \
|
|||||||
<warden-sdk>/kernel/rv1106-enablement/npu/rknpu_version_test.c
|
<warden-sdk>/kernel/rv1106-enablement/npu/rknpu_version_test.c
|
||||||
```
|
```
|
||||||
|
|
||||||
**This exact command was run in this session** (build-only — the resulting binary
|
**This exact command was run in this session** (build-only, the resulting binary
|
||||||
was not copied to or executed on any target) and produced a clean ARM EABI5 static
|
was not copied to or executed on any target) and produced a clean ARM EABI5 static
|
||||||
ELF binary with exit code 0. One expected, harmless warning appears:
|
ELF binary with exit code 0. One expected, harmless warning appears:
|
||||||
`#warning "Attempt to use kernel headers from user space"` from
|
`#warning "Attempt to use kernel headers from user space"` from
|
||||||
`include/uapi/linux/types.h` — the standard notice every raw-kernel-uapi-header
|
`include/uapi/linux/types.h`, the standard notice every raw-kernel-uapi-header
|
||||||
userspace build gets; it does not affect correctness (`__u32` etc. are still
|
userspace build gets; it does not affect correctness (`__u32` etc. are still
|
||||||
correctly defined with `__KERNEL__` undefined).
|
correctly defined with `__KERNEL__` undefined).
|
||||||
|
|
||||||
@@ -285,13 +285,13 @@ then again with `.flags = RKNPU_GET_HW_VERSION`, and prints both. Traced against
|
|||||||
driver source to get the exact semantics right:
|
driver source to get the exact semantics right:
|
||||||
- `RKNPU_GET_DRV_VERSION` returns `RKNPU_GET_DRV_VERSION_CODE(DRIVER_MAJOR,
|
- `RKNPU_GET_DRV_VERSION` returns `RKNPU_GET_DRV_VERSION_CODE(DRIVER_MAJOR,
|
||||||
DRIVER_MINOR, DRIVER_PATCHLEVEL)` = `MAJOR*10000 + MINOR*100 + PATCHLEVEL`
|
DRIVER_MINOR, DRIVER_PATCHLEVEL)` = `MAJOR*10000 + MINOR*100 + PATCHLEVEL`
|
||||||
(`rknpu_drv.c:rknpu_get_drv_version()`) — for this port's unmodified
|
(`rknpu_drv.c:rknpu_get_drv_version()`), for this port's unmodified
|
||||||
`DRIVER_MAJOR/MINOR/PATCHLEVEL = 0/9/2`, that's raw code `902`, which the test
|
`DRIVER_MAJOR/MINOR/PATCHLEVEL = 0/9/2`, that's raw code `902`, which the test
|
||||||
program decodes back to `"0.9.2"` via the UAPI header's own
|
program decodes back to `"0.9.2"` via the UAPI header's own
|
||||||
`RKNPU_GET_DRV_VERSION_{MAJOR,MINOR,PATCHLEVEL}()` macros.
|
`RKNPU_GET_DRV_VERSION_{MAJOR,MINOR,PATCHLEVEL}()` macros.
|
||||||
- `RKNPU_GET_HW_VERSION` returns a raw value read directly off the NPU core's
|
- `RKNPU_GET_HW_VERSION` returns a raw value read directly off the NPU core's
|
||||||
`VERSION`/`VERSION_NUM` registers (`rknpu_job.c:rknpu_get_hw_version()`) — no
|
`VERSION`/`VERSION_NUM` registers (`rknpu_job.c:rknpu_get_hw_version()`): no
|
||||||
published decode table exists for it (per `PORT-PLAN.md` §3 step 5, "checks...
|
published decode table exists for it (per `PORT-PLAN.md` section 3 step 5, "checks...
|
||||||
returns something plausible"); the test program just prints it in hex.
|
returns something plausible"); the test program just prints it in hex.
|
||||||
|
|
||||||
**Expected output on a successful hardware run** (parent session):
|
**Expected output on a successful hardware run** (parent session):
|
||||||
@@ -305,7 +305,7 @@ and clock/reset all exercised.
|
|||||||
```
|
```
|
||||||
|
|
||||||
A driver version that decodes to anything other than `0.9.2` would indicate a stale
|
A driver version that decodes to anything other than `0.9.2` would indicate a stale
|
||||||
build or a stub/mock intercepting the ioctl, not a real driver response — that's
|
build or a stub/mock intercepting the ioctl, not a real driver response: that's
|
||||||
the value of checking the decoded string, not just the ioctl return code.
|
the value of checking the decoded string, not just the ioctl return code.
|
||||||
|
|
||||||
## Explicitly deferred to the parent session (not done here, per this task's boundary)
|
## Explicitly deferred to the parent session (not done here, per this task's boundary)
|
||||||
@@ -314,23 +314,23 @@ the value of checking the decoded string, not just the ioctl return code.
|
|||||||
- Boot-time probe verification: `dmesg | grep -i rknpu` should show clean
|
- Boot-time probe verification: `dmesg | grep -i rknpu` should show clean
|
||||||
clock/reset/IRQ acquisition, no panic, no permanent `-EPROBE_DEFER` (a single
|
clock/reset/IRQ acquisition, no panic, no permanent `-EPROBE_DEFER` (a single
|
||||||
deferral early at boot, before other clock/reset providers are up, would be
|
deferral early at boot, before other clock/reset providers are up, would be
|
||||||
normal — same caveat class as the audio port's acodec probe-order note).
|
normal, same caveat class as the audio port's acodec probe-order note).
|
||||||
Compare against the 5.10 baseline probe log if available (`PORT-PLAN.md` §3
|
Compare against the 5.10 baseline probe log if available (`PORT-PLAN.md` section 3
|
||||||
step 3).
|
step 3).
|
||||||
- `ls -la /dev/dri/` should show a new `cardN`/`renderD1xx` — classic DRM node
|
- `ls -la /dev/dri/` should show a new `cardN`/`renderD1xx`: classic DRM node
|
||||||
(this port intentionally does **not** produce a `/dev/accel/` node; see
|
(this port intentionally does **not** produce a `/dev/accel/` node; see
|
||||||
`PORT-PLAN.md` §1 and `OPEN-NPU-PLAN.md` §1.3 for why mainline's own
|
`PORT-PLAN.md` section 1 and `OPEN-NPU-PLAN.md` section 1.3 for why mainline's own
|
||||||
`drivers/accel/rocket/` driver — RK3588/ARM64-only — is a different codebase
|
`drivers/accel/rocket/` driver (RK3588/ARM64-only) is a different codebase
|
||||||
that doesn't reach RV1106 at all).
|
that doesn't reach RV1106 at all).
|
||||||
- Run `rknpu_version_test` (built above) against the real node; confirm the
|
- Run `rknpu_version_test` (built above) against the real node; confirm the
|
||||||
decoded driver version prints `0.9.2` and the hw version is non-zero/plausible.
|
decoded driver version prints `0.9.2` and the hw version is non-zero/plausible.
|
||||||
- **Not attempted, not required for this milestone**: `DRM_IOCTL_RKNPU_SUBMIT`,
|
- **Not attempted, not required for this milestone**: `DRM_IOCTL_RKNPU_SUBMIT`,
|
||||||
any `.rknn` model, `librknnrt` — that's the closed-userspace question
|
any `.rknn` model, `librknnrt`, that's the closed-userspace question
|
||||||
`OPEN-NPU-PLAN.md` §1.2–1.4 covers; out of scope here by design (no blob is
|
`OPEN-NPU-PLAN.md` section 1.2-1.4 covers; out of scope here by design (no blob is
|
||||||
shipped by this port, and none is needed to prove the kernel driver itself).
|
shipped by this port, and none is needed to prove the kernel driver itself).
|
||||||
|
|
||||||
## Also updated this session
|
## Also updated this session
|
||||||
|
|
||||||
`../DRIVER-PARITY.md`'s NPU row: `[ ] M6 — plan: npu/PORT-PLAN.md` -> `[wip] M6 built,
|
`../DRIVER-PARITY.md`'s NPU row: `[ ] M6, plan: npu/PORT-PLAN.md` -> `[wip] M6 built,
|
||||||
0 errors/0 warnings, 99 rknpu-prefixed symbols in System.map, &npu
|
0 errors/0 warnings, 99 rknpu-prefixed symbols in System.map, &npu
|
||||||
{status="okay"} in the dtb — not yet flashed/probed on hardware`.
|
{status="okay"} in the dtb, not yet flashed/probed on hardware`.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# NPU (rknpu) open kernel driver — VERIFIED on warden-c8a3 (2026-08-25)
|
# NPU (rknpu) open kernel driver: VERIFIED on warden-c8a3 (2026-08-25)
|
||||||
|
|
||||||
The open GPL rknpu kernel driver runs on our self-built Linux 6.18.46. This is the
|
The open GPL rknpu kernel driver runs on our self-built Linux 6.18.46. This is the
|
||||||
achievable open end state (Tier A in `OPEN-NPU-PLAN.md`); open *compute* remains a
|
achievable open end state (Tier A in `OPEN-NPU-PLAN.md`); open *compute* remains a
|
||||||
@@ -17,7 +17,7 @@ $ rknpu_version_test
|
|||||||
power-get/put, and clock/reset all exercised.
|
power-get/put, and clock/reset all exercised.
|
||||||
```
|
```
|
||||||
The `RKNPU_GET_DRV_VERSION`/`RKNPU_GET_HW_VERSION` ioctls exercise the full
|
The `RKNPU_GET_DRV_VERSION`/`RKNPU_GET_HW_VERSION` ioctls exercise the full
|
||||||
dispatch → power-domain get/put → clock/reset path, so a clean answer confirms the
|
dispatch -> power-domain get/put -> clock/reset path, so a clean answer confirms the
|
||||||
whole driver bring-up, not just registration.
|
whole driver bring-up, not just registration.
|
||||||
|
|
||||||
## The fix that took it from probe-fail to PASS
|
## The fix that took it from probe-fail to PASS
|
||||||
@@ -25,16 +25,16 @@ The base dtsi `npu@ff660000` node declares its interrupt but has **no
|
|||||||
`interrupt-names`**; the rknpu driver requests its IRQ by name (`"npu_irq"`), so
|
`interrupt-names`**; the rknpu driver requests its IRQ by name (`"npu_irq"`), so
|
||||||
probe bailed `error -ENXIO: IRQ npu_irq not found` and never registered its DRM
|
probe bailed `error -ENXIO: IRQ npu_irq not found` and never registered its DRM
|
||||||
device. Board DTS override adds `interrupt-names = "npu_irq";` (+ `status="okay"`).
|
device. Board DTS override adds `interrupt-names = "npu_irq";` (+ `status="okay"`).
|
||||||
(The rest of the port — GPL source, 4 compat-shim headers for dead-code vendor
|
(The rest of the port (GPL source, 4 compat-shim headers for dead-code vendor
|
||||||
headers, 10 mechanical 6.18 API deltas — is in `PORT-PROGRESS.md`.)
|
headers, 10 mechanical 6.18 API deltas) is in `PORT-PROGRESS.md`.)
|
||||||
|
|
||||||
Also: the version test must iterate DRM cards and keep the one that ANSWERS the
|
Also: the version test must iterate DRM cards and keep the one that ANSWERS the
|
||||||
ioctl — the display card (card0) opens fine but returns EINVAL. Fixed in
|
ioctl. The display card (card0) opens fine but returns EINVAL. Fixed in
|
||||||
`rknpu_version_test.c`.
|
`rknpu_version_test.c`.
|
||||||
|
|
||||||
## The honest ceiling (why "100% open NPU" stops at the driver)
|
## The honest ceiling (why "100% open NPU" stops at the driver)
|
||||||
The kernel driver only DMAs an opaque userspace-authored `regcmd` blob into the PC
|
The kernel driver only DMAs an opaque userspace-authored `regcmd` blob into the PC
|
||||||
registers and pulses go — it never inspects the compute stream. The compute-engine
|
registers and pulses go. It never inspects the compute stream. The compute-engine
|
||||||
register map (TRM Part 2) is not public for RV1106, there is **zero open RE prior
|
register map (TRM Part 2) is not public for RV1106, there is **zero open RE prior
|
||||||
art** for this NPU generation, and only the closed RKNN-Toolkit2 compiler emits
|
art** for this NPU generation, and only the closed RKNN-Toolkit2 compiler emits
|
||||||
valid regcmd. Mainline `accel/rocket` + Mesa Teflon are RK3588-only (64-bit). So an
|
valid regcmd. Mainline `accel/rocket` + Mesa Teflon are RK3588-only (64-bit). So an
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
* RKNPU_GET_HW_VERSION, and prints the decoded results. This exercises the
|
* RKNPU_GET_HW_VERSION, and prints the decoded results. This exercises the
|
||||||
* full ioctl-dispatch -> power-get/put -> clock/reset path with zero
|
* full ioctl-dispatch -> power-get/put -> clock/reset path with zero
|
||||||
* dependency on a regcmd buffer or the (closed) RKNN runtime -- see
|
* dependency on a regcmd buffer or the (closed) RKNN runtime -- see
|
||||||
* OPEN-NPU-PLAN.md §1.4 Tier A and PORT-PLAN.md §3 step 5.
|
* OPEN-NPU-PLAN.md section 1.4 Tier A and PORT-PLAN.md section 3 step 5.
|
||||||
*
|
*
|
||||||
* Dependency-free beyond the UAPI header: build with
|
* Dependency-free beyond the UAPI header: build with
|
||||||
* -I<kernel-tree>/include/uapi
|
* -I<kernel-tree>/include/uapi
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
* value read directly off the NPU core's VERSION/VERSION_NUM registers
|
* value read directly off the NPU core's VERSION/VERSION_NUM registers
|
||||||
* (rknpu_job.c:rknpu_get_hw_version()) -- any non-zero, non-0xffffffff
|
* (rknpu_job.c:rknpu_get_hw_version()) -- any non-zero, non-0xffffffff
|
||||||
* value is a plausible "the register block is alive" signal; there is no
|
* value is a plausible "the register block is alive" signal; there is no
|
||||||
* published decode table for it beyond that (PORT-PLAN.md §3 step 5).
|
* published decode table for it beyond that (PORT-PLAN.md section 3 step 5).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
# PVTM (Process-Voltage-Temperature Monitor) — VERIFIED on warden-c8a3 (2026-08-25)
|
# PVTM (Process-Voltage-Temperature Monitor): VERIFIED on warden-c8a3 (2026-08-25)
|
||||||
|
|
||||||
Whole-driver port: mainline 6.18 has **no** rockchip pvtm driver; the vendor
|
Whole-driver port: mainline 6.18 has **no** rockchip pvtm driver; the vendor
|
||||||
`drivers/soc/rockchip/rockchip_pvtm.c` (GPL-2.0, 1046L) supports rv1106. Copied it in
|
`drivers/soc/rockchip/rockchip_pvtm.c` (GPL-2.0, 1046L) supports rv1106. Copied it in
|
||||||
+ `include/linux/soc/rockchip/pvtm.h`; Kconfig `ROCKCHIP_PVTM` + Makefile; `=y`.
|
+ `include/linux/soc/rockchip/pvtm.h`; Kconfig `ROCKCHIP_PVTM` + Makefile; `=y`.
|
||||||
|
|
||||||
## API-delta fixes (6.18)
|
## API-delta fixes (6.18)
|
||||||
- `struct thermal_zone_device` is now opaque → replaced `pvtm->tz->ops->get_temp(...)`
|
- `struct thermal_zone_device` is now opaque -> replaced `pvtm->tz->ops->get_temp(...)`
|
||||||
with the public `thermal_zone_get_temp(pvtm->tz, &cur_temp)`.
|
with the public `thermal_zone_get_temp(pvtm->tz, &cur_temp)`.
|
||||||
- added `#include <linux/of_device.h>` for `of_match_device()`.
|
- added `#include <linux/of_device.h>` for `of_match_device()`.
|
||||||
- copied the vendor-only header `linux/soc/rockchip/pvtm.h`.
|
- copied the vendor-only header `linux/soc/rockchip/pvtm.h`.
|
||||||
|
|
||||||
## The real blocker (why it first probed silently)
|
## The real blocker (why it first probed silently)
|
||||||
The vendor of_match_table wraps the rv1106 entries in `#ifdef CONFIG_CPU_RV1106` — a
|
The vendor of_match_table wraps the rv1106 entries in `#ifdef CONFIG_CPU_RV1106`: a
|
||||||
vendor per-SoC symbol that does **not exist** in mainline. Result: devices
|
vendor per-SoC symbol that does **not exist** in mainline. Result: devices
|
||||||
(`ff240000.pvtm`, `ff390000.pvtm`) were created + the driver registered, but the
|
(`ff240000.pvtm`, `ff390000.pvtm`) were created + the driver registered, but the
|
||||||
compatibles were compiled out of the match table, so nothing bound and probe never
|
compatibles were compiled out of the match table, so nothing bound and probe never
|
||||||
ran (no dmesg at all). **Fix: drop the `#ifdef CONFIG_CPU_RV1106` guard** — our tree
|
ran (no dmesg at all). **Fix: drop the `#ifdef CONFIG_CPU_RV1106` guard**; our tree
|
||||||
only builds for rv1106, so the entries are unconditional. (Watch for this guard in any
|
only builds for rv1106, so the entries are unconditional. (Watch for this guard in any
|
||||||
other vendor driver ported by verbatim copy.)
|
other vendor driver ported by verbatim copy.)
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
# RGA port — DONE, verified on hardware (2026-08-24)
|
# RGA port: DONE, verified on hardware (2026-08-24)
|
||||||
|
|
||||||
Ported the vendor char-dev RGA driver (`drivers/video/rockchip/rga3/`, the
|
Ported the vendor char-dev RGA driver (`drivers/video/rockchip/rga3/`, the
|
||||||
multicore `CONFIG_ROCKCHIP_MULTI_RGA` driver — WardenOS's librga uses `/dev/rga`,
|
multicore `CONFIG_ROCKCHIP_MULTI_RGA` driver: WardenOS's librga uses `/dev/rga`,
|
||||||
not V4L2) to 6.18. Open source (vendor C source). On warden-c8a3:
|
not V4L2) to 6.18. Open source (vendor C source). On warden-c8a3:
|
||||||
```
|
```
|
||||||
rga: rga2, irq = 55, match scheduler
|
rga: rga2, irq = 55, match scheduler
|
||||||
rga: rga2 hardware loaded successfully, hw_version:3.3.87975
|
rga: rga2 hardware loaded successfully, hw_version:3.3.87975
|
||||||
rga: rga2 probe successfully → /dev/rga present, /dev/dma_heap present
|
rga: rga2 probe successfully -> /dev/rga present, /dev/dma_heap present
|
||||||
```
|
```
|
||||||
|
|
||||||
## Build tree wiring
|
## Build tree wiring
|
||||||
`drivers/video/rockchip/{Kconfig,Makefile}` created (source rga3/); wired into
|
`drivers/video/rockchip/{Kconfig,Makefile}` created (source rga3/); wired into
|
||||||
`drivers/video/{Kconfig,Makefile}`. `CONFIG_ROCKCHIP_MULTI_RGA=y`.
|
`drivers/video/{Kconfig,Makefile}`. `CONFIG_ROCKCHIP_MULTI_RGA=y`.
|
||||||
`&rga2 { status="okay"; }`. For `/dev/dma_heap/cma`: use the kernel's default 64 MiB
|
`&rga2 { status="okay"; }`. For `/dev/dma_heap/cma`: use the kernel's default 64 MiB
|
||||||
CMA + `CONFIG_DMABUF_HEAPS{,_CMA,_SYSTEM}=y` — do NOT add a `linux,cma` DT node
|
CMA + `CONFIG_DMABUF_HEAPS{,_CMA,_SYSTEM}=y`; do NOT add a `linux,cma` DT node
|
||||||
(a mis-aligned/duplicate one hangs the boot at reserved-memory init).
|
(a mis-aligned/duplicate one hangs the boot at reserved-memory init).
|
||||||
|
|
||||||
## 5.10 → 6.18 API deltas fixed (all mechanical)
|
## 5.10 -> 6.18 API deltas fixed (all mechanical)
|
||||||
1. `platform_driver.remove`: `int` → `void` (`rga_drv_remove`, drop the returns).
|
1. `platform_driver.remove`: `int` -> `void` (`rga_drv_remove`, drop the returns).
|
||||||
2. `hrtimer_init(&t,…)` + `t.function=fn` → `hrtimer_setup(&t, fn, CLOCK_MONOTONIC,
|
2. `hrtimer_init(&t,...)` + `t.function=fn` -> `hrtimer_setup(&t, fn, CLOCK_MONOTONIC,
|
||||||
HRTIMER_MODE_REL)` — the scheduler tick; verified under the live probe.
|
HRTIMER_MODE_REL)`: the scheduler tick; verified under the live probe.
|
||||||
3. `iommu_map` / `iommu_map_sg`: added the new `gfp` arg (`GFP_KERNEL`).
|
3. `iommu_map` / `iommu_map_sg`: added the new `gfp` arg (`GFP_KERNEL`).
|
||||||
4. `get_user_pages_remote`: dropped the removed `vmas` arg.
|
4. `get_user_pages_remote`: dropped the removed `vmas` arg.
|
||||||
5. `MAX_ORDER` (removed) → compat `#define MAX_ORDER (MAX_PAGE_ORDER + 1)` in
|
5. `MAX_ORDER` (removed) -> compat `#define MAX_ORDER (MAX_PAGE_ORDER + 1)` in
|
||||||
`rga3/include/rga_drv.h` (preserves the old exclusive semantics everywhere).
|
`rga3/include/rga_drv.h` (preserves the old exclusive semantics everywhere).
|
||||||
6. `MODULE_IMPORT_NS(<token>)` → `MODULE_IMPORT_NS("DMA_BUF")` (string form).
|
6. `MODULE_IMPORT_NS(<token>)` -> `MODULE_IMPORT_NS("DMA_BUF")` (string form).
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# M6 — RGA 2D accelerator port to 6.18
|
# M6: RGA 2D accelerator port to 6.18
|
||||||
|
|
||||||
Target: `/dev/rga` present and working on the self-built Linux 6.18.46 RV1106 port
|
Target: `/dev/rga` present and working on the self-built Linux 6.18.46 RV1106 port
|
||||||
(`../PORT-STATUS.md`, `../DRIVER-PARITY.md` row "RGA 2D (rga2)"), at parity with the
|
(`../PORT-STATUS.md`, `../DRIVER-PARITY.md` row "RGA 2D (rga2)"), at parity with the
|
||||||
@@ -6,42 +6,42 @@ running vendor 5.10 kernel and with zero WardenOS UI-side code changes.
|
|||||||
|
|
||||||
## Decision: port the vendor char-dev driver. Do not touch mainline V4L2 rga.
|
## Decision: port the vendor char-dev driver. Do not touch mainline V4L2 rga.
|
||||||
|
|
||||||
**WardenOS depends on the vendor `/dev/rga` ioctl uapi via librga's IM2D API — not
|
**WardenOS depends on the vendor `/dev/rga` ioctl uapi via librga's IM2D API, not
|
||||||
V4L2.** `ui-src/src/warden/warden_rga.c` (`flare-edge/major-app-additions/`) includes
|
V4L2.** `ui-src/src/warden/warden_rga.c` (`flare-edge/major-app-additions/`) includes
|
||||||
`<rga/rga.h>` and `<rga/im2d.h>` and calls `wrapbuffer_fd_t()`, `improcess(..., IM_SYNC)`
|
`<rga/rga.h>` and `<rga/im2d.h>` and calls `wrapbuffer_fd_t()`, `improcess(..., IM_SYNC)`
|
||||||
and `querystring(RGA_VERSION)` (warden_rga.c:46-48, 377-390, 458, 614-631) — the
|
and `querystring(RGA_VERSION)` (warden_rga.c:46-48, 377-390, 458, 614-631): the
|
||||||
Rockchip **im2d/librga** user-space API, which talks to the kernel purely through the
|
Rockchip **im2d/librga** user-space API, which talks to the kernel purely through the
|
||||||
vendor char-device ioctl protocol (`RGA_IOC_REQUEST_SUBMIT` etc., see below). There is
|
vendor char-device ioctl protocol (`RGA_IOC_REQUEST_SUBMIT` etc., see below). There is
|
||||||
no V4L2 (`/dev/videoN`, `VIDIOC_*`) code anywhere in WardenOS's RGA path, and no
|
no V4L2 (`/dev/videoN`, `VIDIOC_*`) code anywhere in WardenOS's RGA path, and no
|
||||||
V4L2-backed librga build exists upstream to switch to even if we wanted one. Porting the
|
V4L2-backed librga build exists upstream to switch to even if we wanted one. Porting the
|
||||||
vendor driver is therefore not a preference, it is the only path that keeps the existing
|
vendor driver is therefore not a preference, it is the only path that keeps the existing
|
||||||
UI code (and its measured 20%→8% CPU win, warden_rga.c:419) working unmodified.
|
UI code (and its measured 20%->8% CPU win, warden_rga.c:419) working unmodified.
|
||||||
|
|
||||||
The candidate alternative — adding an `rv1106` compatible string to mainline's
|
The candidate alternative (adding an `rv1106` compatible string to mainline's
|
||||||
`drivers/media/platform/rockchip/rga/` V4L2 M2M driver — is rejected on both counts the
|
`drivers/media/platform/rockchip/rga/` V4L2 M2M driver) is rejected on both counts the
|
||||||
task asked to check:
|
task asked to check:
|
||||||
|
|
||||||
1. **Register incompatibility.** Mainline's driver (current `torvalds/linux` master,
|
1. **Register incompatibility.** Mainline's driver (current `torvalds/linux` master,
|
||||||
representative of 6.18) matches only `rockchip,rk3288-rga`, `rockchip,rk3399-rga`
|
representative of 6.18) matches only `rockchip,rk3288-rga`, `rockchip,rk3399-rga`
|
||||||
(both mapped to one `rga2_hw` struct, `.features = FLIP | ROTATE | BG_COLOR` only —
|
(both mapped to one `rga2_hw` struct, `.features = FLIP | ROTATE | BG_COLOR` only:
|
||||||
no scaling, no format conversion) and `rockchip,rk3588-rga3`. Per Rockchip's own FAQ
|
no scaling, no format conversion) and `rockchip,rk3588-rga3`. Per Rockchip's own FAQ
|
||||||
("Although RGA on both RK3399 and RV1126 is RGA2-ENHANCE, their sub versions are
|
("Although RGA on both RK3399 and RV1126 is RGA2-ENHANCE, their sub versions are
|
||||||
different" — `librga/docs/Rockchip_FAQ_RGA_EN.md`, Q2.10) RK3399's RGA is already
|
different"; `librga/docs/Rockchip_FAQ_RGA_EN.md`, Q2.10) RK3399's RGA is already
|
||||||
**RGA2-ENHANCE**, a newer/richer core than RK3288's baseline RGA2 that mainline's
|
**RGA2-ENHANCE**, a newer/richer core than RK3288's baseline RGA2 that mainline's
|
||||||
`rga2_hw` was written against — so mainline's claim of rk3399 compatibility is itself
|
`rga2_hw` was written against, so mainline's claim of rk3399 compatibility is itself
|
||||||
the reduced-feature subset, not proof of a shared register map. RV1106 is a *third*
|
the reduced-feature subset, not proof of a shared register map. RV1106 is a *third*
|
||||||
point in that family: our own vendor driver (`rga_drv.c:1402-1409`) special-cases the
|
point in that family: our own vendor driver (`rga_drv.c:1402-1409`) special-cases the
|
||||||
exact hardware version string `"3.3.87975"` to a distinct `rga2e_1106_data` table,
|
exact hardware version string `"3.3.87975"` to a distinct `rga2e_1106_data` table,
|
||||||
separate from the generic `rga2e_data` and from the IOMMU-capable `rga2e_iommu_data`
|
separate from the generic `rga2e_data` and from the IOMMU-capable `rga2e_iommu_data`
|
||||||
used by other RGA2-ENHANCE chips — i.e. even Rockchip's own driver does not treat
|
used by other RGA2-ENHANCE chips, i.e. even Rockchip's own driver does not treat
|
||||||
RV1106 as register-identical to its closest relatives, let alone to RK3288/RK3399's
|
RV1106 as register-identical to its closest relatives, let alone to RK3288/RK3399's
|
||||||
older baseline core. A pending upstream series (Jianfeng Liu,
|
older baseline core. A pending upstream series (Jianfeng Liu,
|
||||||
`20240322052915.3507937-1-liujianfeng1994@gmail.com`, "media: rockchip: rga: Add
|
`20240322052915.3507937-1-liujianfeng1994@gmail.com`, "media: rockchip: rga: Add
|
||||||
rk3568 support") explicitly states "RGA2 on rk3568 is the same core as RGA2 on
|
rk3568 support") explicitly states "RGA2 on rk3568 is the same core as RGA2 on
|
||||||
rk3288" — confirming mainline's whole `rga2_hw` lineage targets the *old* core, not
|
rk3288", confirming mainline's whole `rga2_hw` lineage targets the *old* core, not
|
||||||
the ENHANCE family RV1106 belongs to.
|
the ENHANCE family RV1106 belongs to.
|
||||||
2. **Feature/uapi mismatch.** Even where mainline's driver runs, it only implements
|
2. **Feature/uapi mismatch.** Even where mainline's driver runs, it only implements
|
||||||
flip/rotate/solid-fill via V4L2 M2M — no im2d, no `/dev/rga` char device, no
|
flip/rotate/solid-fill via V4L2 M2M: no im2d, no `/dev/rga` char device, no
|
||||||
`wrapbuffer_fd_t`/`improcess` surface. Adopting it would mean rewriting
|
`wrapbuffer_fd_t`/`improcess` surface. Adopting it would mean rewriting
|
||||||
`warden_rga.c`'s draw-unit and buffer-sync-copy paths against `VIDIOC_*` ioctls from
|
`warden_rga.c`'s draw-unit and buffer-sync-copy paths against `VIDIOC_*` ioctls from
|
||||||
scratch, on hardware nobody has shown is even electrically the same core.
|
scratch, on hardware nobody has shown is even electrically the same core.
|
||||||
@@ -51,30 +51,30 @@ task asked to check:
|
|||||||
Real path: `flare-edge/sdk/sysdrv/source/kernel/drivers/video/rockchip/rga3/`
|
Real path: `flare-edge/sdk/sysdrv/source/kernel/drivers/video/rockchip/rga3/`
|
||||||
(`sdk` is a symlink to `<flare-edge>/sdk`). This is Rockchip's
|
(`sdk` is a symlink to `<flare-edge>/sdk`). This is Rockchip's
|
||||||
**"multicore" RGA driver** (`CONFIG_ROCKCHIP_MULTI_RGA`, module name `rga3.ko`/built-in
|
**"multicore" RGA driver** (`CONFIG_ROCKCHIP_MULTI_RGA`, module name `rga3.ko`/built-in
|
||||||
`rga3.o`) — open source, `SPDX-License-Identifier: GPL-2.0`, driver version **1.3.1**
|
`rga3.o`): open source, `SPDX-License-Identifier: GPL-2.0`, driver version **1.3.1**
|
||||||
(`include/rga_drv.h:88-94`; the current `airockchip/librga` upstream ships a matching
|
(`include/rga_drv.h:88-94`; the current `airockchip/librga` upstream ships a matching
|
||||||
**1.3.3** for RK3588 on 6.1/6.4 Armbian kernels — same lineage, already proven building
|
**1.3.3** for RK3588 on 6.1/6.4 Armbian kernels, same lineage, already proven building
|
||||||
against 6.x elsewhere). There are three other RGA driver trees in the SDK
|
against 6.x elsewhere). There are three other RGA driver trees in the SDK
|
||||||
(`drivers/video/rockchip/rga/`, `rga2/`, and the mainline-style
|
(`drivers/video/rockchip/rga/`, `rga2/`, and the mainline-style
|
||||||
`drivers/media/platform/rockchip/rga/`) — **none of these are built** for RV1106
|
`drivers/media/platform/rockchip/rga/`). **None of these are built** for RV1106
|
||||||
(`grep CONFIG_ROCKCHIP_MULTI_RGA arch/arm/configs/*rv1106*defconfig` is the only RGA
|
(`grep CONFIG_ROCKCHIP_MULTI_RGA arch/arm/configs/*rv1106*defconfig` is the only RGA
|
||||||
symbol set; `drivers/media/platform/rockchip/rga` isn't referenced by any rv1106
|
symbol set; `drivers/media/platform/rockchip/rga` isn't referenced by any rv1106
|
||||||
defconfig or DT). Ignore them; `rga3/` is the one true source.
|
defconfig or DT). Ignore them; `rga3/` is the one true source.
|
||||||
|
|
||||||
| File | Role |
|
| File | Role |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `rga_drv.c` | probe/remove, misc char-dev (`"rga"` → `/dev/rga`), ioctl dispatch, IRQ, clocks, hrtimer scheduler tick |
|
| `rga_drv.c` | probe/remove, misc char-dev (`"rga"` -> `/dev/rga`), ioctl dispatch, IRQ, clocks, hrtimer scheduler tick |
|
||||||
| `rga_common.c`, `rga_job.c`, `rga_mm.c` | job/request lifecycle, buffer-descriptor management |
|
| `rga_common.c`, `rga_job.c`, `rga_mm.c` | job/request lifecycle, buffer-descriptor management |
|
||||||
| `rga2_reg_info.c`, `rga3_reg_info.c`, `rga_hw_config.c` | per-core register programming (the actual "how to talk to the silicon" — this is what a mainline single-core driver does NOT have for RGA2-ENHANCE) |
|
| `rga2_reg_info.c`, `rga3_reg_info.c`, `rga_hw_config.c` | per-core register programming (the actual "how to talk to the silicon"; this is what a mainline single-core driver does NOT have for RGA2-ENHANCE) |
|
||||||
| `rga_dma_buf.c` | dma-buf import/map (`dma_buf_attach` + `dma_buf_map_attachment`, `iosys_map`) |
|
| `rga_dma_buf.c` | dma-buf import/map (`dma_buf_attach` + `dma_buf_map_attachment`, `iosys_map`) |
|
||||||
| `rga_iommu.c` | IOMMU attach — **not exercised on RV1106** (see below) |
|
| `rga_iommu.c` | IOMMU attach: **not exercised on RV1106** (see below) |
|
||||||
| `rga_fence.c` | `dma_fence`/`sync_file` for the async ioctl path — **not needed**, WardenOS is sync-only (see Scope cuts) |
|
| `rga_fence.c` | `dma_fence`/`sync_file` for the async ioctl path: **not needed**, WardenOS is sync-only (see Scope cuts) |
|
||||||
| `rga_debugger.c` | optional procfs/debugfs introspection |
|
| `rga_debugger.c` | optional procfs/debugfs introspection |
|
||||||
| `include/rga.h` | uapi: ioctl numbers or `RGA_IOC_MAGIC='r'` — `RGA_IOC_GET_DRVIER_VERSION`, `RGA_IOC_GET_HW_VERSION`, `RGA_IOC_IMPORT_BUFFER`, `RGA_IOC_REQUEST_CREATE/SUBMIT/CONFIG/CANCEL`, plus the legacy `RGA_BLIT_SYNC`(0x5017)/`RGA_BLIT_ASYNC`/`RGA_GET_VERSION` numeric ioctls. **This is the exact uapi `librga.so` (userspace) calls** — nothing here changes; we port the kernel side only. |
|
| `include/rga.h` | uapi: ioctl numbers or `RGA_IOC_MAGIC='r'`, `RGA_IOC_GET_DRVIER_VERSION`, `RGA_IOC_GET_HW_VERSION`, `RGA_IOC_IMPORT_BUFFER`, `RGA_IOC_REQUEST_CREATE/SUBMIT/CONFIG/CANCEL`, plus the legacy `RGA_BLIT_SYNC`(0x5017)/`RGA_BLIT_ASYNC`/`RGA_GET_VERSION` numeric ioctls. **This is the exact uapi `librga.so` (userspace) calls**: nothing here changes; we port the kernel side only. |
|
||||||
|
|
||||||
**DT match for our chip:** `rga2_dt_ids[]` (`rga_drv.c:1249-1258`) matches
|
**DT match for our chip:** `rga2_dt_ids[]` (`rga_drv.c:1249-1258`) matches
|
||||||
`compatible = "rockchip,rga2_core0"` → `rk3588_rga2_match_data` (clock names
|
`compatible = "rockchip,rga2_core0"` -> `rk3588_rga2_match_data` (clock names
|
||||||
`"aclk_rga2","hclk_rga2","clk_rga2"`, `rga_drv.c:1195-1199`) — this is **exactly** our
|
`"aclk_rga2","hclk_rga2","clk_rga2"`, `rga_drv.c:1195-1199`); this is **exactly** our
|
||||||
vendor DT node:
|
vendor DT node:
|
||||||
```c
|
```c
|
||||||
// rv1106.dtsi:1154-1161
|
// rv1106.dtsi:1154-1161
|
||||||
@@ -87,25 +87,25 @@ rga2: rga@ff980000 {
|
|||||||
status = "disabled";
|
status = "disabled";
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
No `resets =`, no `iommus =` on this node — confirmed correct: `rga_drv.c` never calls
|
No `resets =`, no `iommus =` on this node; confirmed correct: `rga_drv.c` never calls
|
||||||
`reset_control_*` at all (unlike VOP, which needed named resets added for mainline,
|
`reset_control_*` at all (unlike VOP, which needed named resets added for mainline,
|
||||||
`../display/README.md`), and probe only calls `rga_iommu_probe()` when
|
`../display/README.md`), and probe only calls `rga_iommu_probe()` when
|
||||||
`scheduler->data->mmu == RGA_IOMMU` (`rga_drv.c:1420-1425`) — for RV1106's matched
|
`scheduler->data->mmu == RGA_IOMMU` (`rga_drv.c:1420-1425`), for RV1106's matched
|
||||||
`rga2e_1106_data` (selected by exact HW-version-string match `"3.3.87975"`,
|
`rga2e_1106_data` (selected by exact HW-version-string match `"3.3.87975"`,
|
||||||
`rga_drv.c:1402-1409`) that table has no IOMMU, so the branch never runs. **RV1106's RGA2
|
`rga_drv.c:1402-1409`) that table has no IOMMU, so the branch never runs. **RV1106's RGA2
|
||||||
is physically-contiguous-only**, which is exactly why `warden_rga.c` allocates its
|
is physically-contiguous-only**, which is exactly why `warden_rga.c` allocates its
|
||||||
canvas/scanout buffers from `/dev/dma_heap/cma` (CMA dma-buf heap) rather than any
|
canvas/scanout buffers from `/dev/dma_heap/cma` (CMA dma-buf heap) rather than any
|
||||||
generic malloc — no driver-side change needed here, the existing WardenOS allocation
|
generic malloc: no driver-side change needed here, the existing WardenOS allocation
|
||||||
strategy already matches the hardware constraint.
|
strategy already matches the hardware constraint.
|
||||||
|
|
||||||
**IRQ name:** `dev_driver_string(dev)` = the platform_driver's `.driver.name`, which for
|
**IRQ name:** `dev_driver_string(dev)` = the platform_driver's `.driver.name`, which for
|
||||||
the `rga2_dt_ids` match is literally `"rga2"` (`rga_drv.c:1478-1481`) — this is the exact
|
the `rga2_dt_ids` match is literally `"rga2"` (`rga_drv.c:1478-1481`); this is the exact
|
||||||
string that shows up as `rga2` in `/proc/interrupts` on the running 5.10 image, confirming
|
string that shows up as `rga2` in `/proc/interrupts` on the running 5.10 image, confirming
|
||||||
the task's framing and that we're looking at the right driver.
|
the task's framing and that we're looking at the right driver.
|
||||||
|
|
||||||
**DT enable status (5.10, for parity):** the shipped 86-Panel board DT already turns this
|
**DT enable status (5.10, for parity):** the shipped 86-Panel board DT already turns this
|
||||||
node on — `rv1106g-luckfox-pico-86panel.dts` → `rv1106-luckfox-pico-86panel-ipc.dtsi` →
|
node on, `rv1106g-luckfox-pico-86panel.dts` -> `rv1106-luckfox-pico-86panel-ipc.dtsi` ->
|
||||||
`#include "rv1106-evb.dtsi"` → `&rga2 { status = "okay"; };` (`rv1106-evb.dtsi:59-61`).
|
`#include "rv1106-evb.dtsi"` -> `&rga2 { status = "okay"; };` (`rv1106-evb.dtsi:59-61`).
|
||||||
`sdk-patches/kernel/configs/flare-edge.config:21-29` (flare-edge repo) is the config
|
`sdk-patches/kernel/configs/flare-edge.config:21-29` (flare-edge repo) is the config
|
||||||
fragment: `CONFIG_ROCKCHIP_MULTI_RGA=y` (built-in, not `=m`, so devtmpfs creates
|
fragment: `CONFIG_ROCKCHIP_MULTI_RGA=y` (built-in, not `=m`, so devtmpfs creates
|
||||||
`/dev/rga` with no insmod step) + `CONFIG_DMABUF_HEAPS=y` + `CONFIG_DMABUF_HEAPS_CMA=y`.
|
`/dev/rga` with no insmod step) + `CONFIG_DMABUF_HEAPS=y` + `CONFIG_DMABUF_HEAPS_CMA=y`.
|
||||||
@@ -117,9 +117,9 @@ fragment: `CONFIG_ROCKCHIP_MULTI_RGA=y` (built-in, not `=m`, so devtmpfs creates
|
|||||||
(Apache-2.0 licensed headers, `rga.h`/`im2d.h`). It links against the kernel uapi in
|
(Apache-2.0 licensed headers, `rga.h`/`im2d.h`). It links against the kernel uapi in
|
||||||
`include/rga.h` above, which is unchanged by this port. Once `/dev/rga` exists with the
|
`include/rga.h` above, which is unchanged by this port. Once `/dev/rga` exists with the
|
||||||
same ioctl numbers, the existing `librga.so` and the existing `warden_rga.c` binary/object
|
same ioctl numbers, the existing `librga.so` and the existing `warden_rga.c` binary/object
|
||||||
need **no changes** — this is a pure kernel-side port.
|
need **no changes**: this is a pure kernel-side port.
|
||||||
|
|
||||||
## API-delta checklist, 5.10 → 6.18 (checked against the real target tree)
|
## API-delta checklist, 5.10 -> 6.18 (checked against the real target tree)
|
||||||
|
|
||||||
Checked directly against `flare-edge/research/linux-6.18.46/` (the tree M1-M3 already
|
Checked directly against `flare-edge/research/linux-6.18.46/` (the tree M1-M3 already
|
||||||
build and boot on), not guessed:
|
build and boot on), not guessed:
|
||||||
@@ -127,12 +127,12 @@ build and boot on), not guessed:
|
|||||||
| # | Delta | Evidence | Fix |
|
| # | Delta | Evidence | Fix |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| 1 | **`platform_driver.remove` is now `void`, not `int`.** | `linux-6.18.46/include/linux/platform_device.h:233`: `void (*remove)(struct platform_device *);`. Vendor `rga_drv_remove()` (`rga_drv.c`) returns `int`. Used by all 3 `platform_driver` structs (rga3_core0/1, rga2). | Change signature to `void`, drop the `return ret;`/`return 0;`, keep the body. Mechanical, 1 function + 3 struct references. |
|
| 1 | **`platform_driver.remove` is now `void`, not `int`.** | `linux-6.18.46/include/linux/platform_device.h:233`: `void (*remove)(struct platform_device *);`. Vendor `rga_drv_remove()` (`rga_drv.c`) returns `int`. Used by all 3 `platform_driver` structs (rga3_core0/1, rga2). | Change signature to `void`, drop the `return ret;`/`return 0;`, keep the body. Mechanical, 1 function + 3 struct references. |
|
||||||
| 2 | **`hrtimer_init()` is gone; `hrtimer_setup()` merges init+callback.** | `grep hrtimer_init` on `linux-6.18.46/include/linux/hrtimer.h` returns nothing callable — only `hrtimer_setup()`/`hrtimer_setup_on_stack()` (`hrtimer.h:230-235`). Vendor `rga_drv.c:362-366` does the old split form: `hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); timer.function = hrtimer_handler;`. | `hrtimer_setup(&timer, hrtimer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL);` — one call site, but it's the RGA scheduler tick (`rga_drv.c:327-371`), so build-clean is necessary but not sufficient: exercise it under load (the blit test below) since a hrtimer regression shows up as stalled/duplicate completions, not a compile error. **This is the delta most likely to bite in a way the compiler won't catch — treat it as the highest-scrutiny item.** |
|
| 2 | **`hrtimer_init()` is gone; `hrtimer_setup()` merges init+callback.** | `grep hrtimer_init` on `linux-6.18.46/include/linux/hrtimer.h` returns nothing callable: only `hrtimer_setup()`/`hrtimer_setup_on_stack()` (`hrtimer.h:230-235`). Vendor `rga_drv.c:362-366` does the old split form: `hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); timer.function = hrtimer_handler;`. | `hrtimer_setup(&timer, hrtimer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL);`, one call site, but it's the RGA scheduler tick (`rga_drv.c:327-371`), so build-clean is necessary but not sufficient: exercise it under load (the blit test below) since a hrtimer regression shows up as stalled/duplicate completions, not a compile error. **This is the delta most likely to bite in a way the compiler won't catch: treat it as the highest-scrutiny item.** |
|
||||||
| 3 | `dma_buf_attach()` / `dma_buf_map_attachment()` | `linux-6.18.46/include/linux/dma-buf.h:571,588` — signatures (`dmabuf, dev`) / (`attach, dir`) → `sg_table` are **unchanged** from what `rga_dma_buf.c:443,450,494,501` already calls. Vendor code already uses `struct iosys_map` (post-5.18 API) at `rga_dma_buf.c:398`, so it's already ahead of 5.10 baseline here. | No change needed; compile-verify only. |
|
| 3 | `dma_buf_attach()` / `dma_buf_map_attachment()` | `linux-6.18.46/include/linux/dma-buf.h:571,588`: signatures (`dmabuf, dev`) / (`attach, dir`) -> `sg_table` are **unchanged** from what `rga_dma_buf.c:443,450,494,501` already calls. Vendor code already uses `struct iosys_map` (post-5.18 API) at `rga_dma_buf.c:398`, so it's already ahead of 5.10 baseline here. | No change needed; compile-verify only. |
|
||||||
| 4 | `class_create()` losing its `owner` arg (6.4) | Not applicable — `rga3/` has **zero** `class_create` calls (grep across the whole dir + `include/`); it registers `/dev/rga` via `misc_register()` (`rga_drv.c:1520`), which needs no class. | Nothing to fix. (Flagged in the task brief as a risk; verified moot for this driver.) |
|
| 4 | `class_create()` losing its `owner` arg (6.4) | Not applicable: `rga3/` has **zero** `class_create` calls (grep across the whole dir + `include/`); it registers `/dev/rga` via `misc_register()` (`rga_drv.c:1520`), which needs no class. | Nothing to fix. (Flagged in the task brief as a risk; verified moot for this driver.) |
|
||||||
| 5 | IOMMU API drift (`iommu_domain_alloc`, `iommu_attach_device`, rockchip IOMMU v2) | `rga_iommu.c` uses `iommu_group_get`/`iommu_get_domain_for_dev` — but as established above, **RV1106 never calls `rga_iommu_probe()`** (its match_data has no `RGA_IOMMU` flag). | Out of scope for RV1106; don't even need to build-fix `rga_iommu.c`'s IOMMU-attach path correctness, only that it compiles (dead code on our chip). |
|
| 5 | IOMMU API drift (`iommu_domain_alloc`, `iommu_attach_device`, rockchip IOMMU v2) | `rga_iommu.c` uses `iommu_group_get`/`iommu_get_domain_for_dev`, but as established above, **RV1106 never calls `rga_iommu_probe()`** (its match_data has no `RGA_IOMMU` flag). | Out of scope for RV1106; don't even need to build-fix `rga_iommu.c`'s IOMMU-attach path correctness, only that it compiles (dead code on our chip). |
|
||||||
| 6 | `proc_ops`/`debugfs_create_file` | `rga_debugger.c:499,553,597,605,633,686` already uses `struct proc_ops` (post-5.6) and plain `debugfs_create_file`/`proc_create_data` — both stable, unchanged APIs in 6.18. | No change needed. Optional subsystem anyway (see Scope cuts). |
|
| 6 | `proc_ops`/`debugfs_create_file` | `rga_debugger.c:499,553,597,605,633,686` already uses `struct proc_ops` (post-5.6) and plain `debugfs_create_file`/`proc_create_data`, both stable, unchanged APIs in 6.18. | No change needed. Optional subsystem anyway (see Scope cuts). |
|
||||||
| 7 | GRF/syscon regmap lookups | None — `rga3/` has zero `syscon`/`regmap`/`rockchip,grf` references, unlike VOP which needed the `grf_ctx` fix in `PORT-STATUS.md` M2 item 3. | Nothing to fix; simpler than the VOP port in this respect. |
|
| 7 | GRF/syscon regmap lookups | None: `rga3/` has zero `syscon`/`regmap`/`rockchip,grf` references, unlike VOP which needed the `grf_ctx` fix in `PORT-STATUS.md` M2 item 3. | Nothing to fix; simpler than the VOP port in this respect. |
|
||||||
|
|
||||||
## Scope cuts (reduce port surface to exactly what WardenOS uses)
|
## Scope cuts (reduce port surface to exactly what WardenOS uses)
|
||||||
|
|
||||||
@@ -140,15 +140,15 @@ build and boot on), not guessed:
|
|||||||
explicit: "The RGA blit uses the synchronous path. No fences" (warden_rga.h:23), and
|
explicit: "The RGA blit uses the synchronous path. No fences" (warden_rga.h:23), and
|
||||||
every call site uses `improcess(..., IM_SYNC)` (warden_rga.c:388,629). The
|
every call site uses `improcess(..., IM_SYNC)` (warden_rga.c:388,629). The
|
||||||
`Makefile`'s `rga3-$(CONFIG_ROCKCHIP_RGA_ASYNC) += rga_fence.o` means leaving this
|
`Makefile`'s `rga3-$(CONFIG_ROCKCHIP_RGA_ASYNC) += rga_fence.o` means leaving this
|
||||||
config off **excludes `rga_fence.c` (dma_fence/sync_file) from the build entirely** —
|
config off **excludes `rga_fence.c` (dma_fence/sync_file) from the build entirely**:
|
||||||
removing an entire API-delta surface (dma_fence context allocation, sync_file
|
removing an entire API-delta surface (dma_fence context allocation, sync_file
|
||||||
lifetime) that WardenOS never exercises. Matches the vendor default (`ROCKCHIP_RGA_ASYNC`
|
lifetime) that WardenOS never exercises. Matches the vendor default (`ROCKCHIP_RGA_ASYNC`
|
||||||
defaults to `y` in Kconfig but is safe to turn off; verify nothing else in the SDK
|
defaults to `y` in Kconfig but is safe to turn off; verify nothing else in the SDK
|
||||||
userspace on this image needs it — nothing does; MPP/ISP aren't used on the 86-Panel).
|
userspace on this image needs it: nothing does; MPP/ISP aren't used on the 86-Panel).
|
||||||
- **`CONFIG_ROCKCHIP_RGA_PROC_FS`/`_DEBUG_FS`/`_DEBUGGER`: leave off initially.** Useful
|
- **`CONFIG_ROCKCHIP_RGA_PROC_FS`/`_DEBUG_FS`/`_DEBUGGER`: leave off initially.** Useful
|
||||||
for bring-up (dumps registered buffers/jobs) but not required for `/dev/rga` to exist
|
for bring-up (dumps registered buffers/jobs) but not required for `/dev/rga` to exist
|
||||||
or work; add later if debugging needs it.
|
or work; add later if debugging needs it.
|
||||||
- **Only `rga3/` (`CONFIG_ROCKCHIP_MULTI_RGA`) is ported** — not `drivers/video/rockchip/rga/`
|
- **Only `rga3/` (`CONFIG_ROCKCHIP_MULTI_RGA`) is ported**, not `drivers/video/rockchip/rga/`
|
||||||
or `rga2/` (the older single-core drivers), matching the vendor 5.10 build exactly.
|
or `rga2/` (the older single-core drivers), matching the vendor 5.10 build exactly.
|
||||||
|
|
||||||
## File / config / DT plan
|
## File / config / DT plan
|
||||||
@@ -164,7 +164,7 @@ drivers/video/rockchip/rga3/ # forward-ported rga_drv.c, rga_common
|
|||||||
# rga3_reg_info.c, rga2_reg_info.c,
|
# rga3_reg_info.c, rga2_reg_info.c,
|
||||||
# rga_hw_config.c, rga_mm.c, rga_dma_buf.c,
|
# rga_hw_config.c, rga_mm.c, rga_dma_buf.c,
|
||||||
# rga_iommu.c, rga_policy.c, Kconfig, Makefile
|
# rga_iommu.c, rga_policy.c, Kconfig, Makefile
|
||||||
# (rga_fence.c, rga_debugger.c omitted —
|
# (rga_fence.c, rga_debugger.c omitted,
|
||||||
# see Scope cuts)
|
# see Scope cuts)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -184,8 +184,8 @@ obj-$(CONFIG_ROCKCHIP_MULTI_RGA) += rga3.o
|
|||||||
(drops the `rga3-$(CONFIG_ROCKCHIP_RGA_ASYNC) += rga_fence.o` and
|
(drops the `rga3-$(CONFIG_ROCKCHIP_RGA_ASYNC) += rga_fence.o` and
|
||||||
`rga3-$(CONFIG_ROCKCHIP_RGA_DEBUGGER) += rga_debugger.o` lines from the vendor Makefile.)
|
`rga3-$(CONFIG_ROCKCHIP_RGA_DEBUGGER) += rga_debugger.o` lines from the vendor Makefile.)
|
||||||
|
|
||||||
Apply the two API-delta fixes from the table above (`platform_driver.remove` → void,
|
Apply the two API-delta fixes from the table above (`platform_driver.remove` -> void,
|
||||||
`hrtimer_init`+`.function=` → `hrtimer_setup()`) during the transplant, same
|
`hrtimer_init`+`.function=` -> `hrtimer_setup()`) during the transplant, same
|
||||||
build-fix-build loop already used for `clk-rv1106.c`/`pinctrl-rockchip.c`
|
build-fix-build loop already used for `clk-rv1106.c`/`pinctrl-rockchip.c`
|
||||||
(`PORT-STATUS.md` M1).
|
(`PORT-STATUS.md` M1).
|
||||||
|
|
||||||
@@ -197,34 +197,34 @@ CONFIG_DMABUF_HEAPS=y
|
|||||||
CONFIG_DMABUF_HEAPS_CMA=y
|
CONFIG_DMABUF_HEAPS_CMA=y
|
||||||
```
|
```
|
||||||
(`CONFIG_DMABUF_HEAPS` is currently `# CONFIG_DMABUF_HEAPS is not set` in
|
(`CONFIG_DMABUF_HEAPS` is currently `# CONFIG_DMABUF_HEAPS is not set` in
|
||||||
`linux-6.18.46/.config` — confirmed by grep — so this is a required addition, not
|
`linux-6.18.46/.config` (confirmed by grep), so this is a required addition, not
|
||||||
already-on. `CONFIG_ARCH_ROCKCHIP=y` is already set.) `=y` not `=m`, matching the
|
already-on. `CONFIG_ARCH_ROCKCHIP=y` is already set.) `=y` not `=m`, matching the
|
||||||
vendor fragment's own rationale (`flare-edge.config:23`: builtin so devtmpfs creates
|
vendor fragment's own rationale (`flare-edge.config:23`: builtin so devtmpfs creates
|
||||||
the node with no insmod step).
|
the node with no insmod step).
|
||||||
|
|
||||||
### 3. Devicetree — two additions to `dts/rv1106-warden.dts`
|
### 3. Devicetree: two additions to `dts/rv1106-warden.dts`
|
||||||
|
|
||||||
**(a) Enable the node.** `rv1106.dtsi` is already `#include`d wholesale by
|
**(a) Enable the node.** `rv1106.dtsi` is already `#include`d wholesale by
|
||||||
`rv1106-warden.dts` (`dts/README.md`), and it already carries the `rga2` node
|
`rv1106-warden.dts` (`dts/README.md`), and it already carries the `rga2` node
|
||||||
byte-for-byte (clocks, IRQ, compatible) — just disabled. Add the same one-line override
|
byte-for-byte (clocks, IRQ, compatible), just disabled. Add the same one-line override
|
||||||
the vendor board DT uses:
|
the vendor board DT uses:
|
||||||
```dts
|
```dts
|
||||||
&rga2 {
|
&rga2 {
|
||||||
status = "okay";
|
status = "okay";
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
No clock/reset/iommu properties to add — all three RGA2E clocks
|
No clock/reset/iommu properties to add: all three RGA2E clocks
|
||||||
(`HCLK_RGA2E`=269, `ACLK_RGA2E`=270, `CLK_CORE_RGA2E`=271) are **already wired** in the
|
(`HCLK_RGA2E`=269, `ACLK_RGA2E`=270, `CLK_CORE_RGA2E`=271) are **already wired** in the
|
||||||
ported `clk-rv1106.c` (`clk/clk-rv1106.c:924-930`, landed in M1, `PORT-STATUS.md`) off
|
ported `clk-rv1106.c` (`clk/clk-rv1106.c:924-930`, landed in M1, `PORT-STATUS.md`) off
|
||||||
the same `hclk_vo_root`/`aclk_vo_root` parents that VOP already uses successfully on
|
the same `hclk_vo_root`/`aclk_vo_root` parents that VOP already uses successfully on
|
||||||
hardware (M4, verified) — this is the lowest-risk clock story of any M6 driver.
|
hardware (M4, verified). This is the lowest-risk clock story of any M6 driver.
|
||||||
|
|
||||||
**(b) Add the CMA reserved-memory pool — currently missing from the ported DT.**
|
**(b) Add the CMA reserved-memory pool, currently missing from the ported DT.**
|
||||||
`warden_rga.c`'s canvas allocator opens `/dev/dma_heap/cma`
|
`warden_rga.c`'s canvas allocator opens `/dev/dma_heap/cma`
|
||||||
(`warden_rga.c:70,269-274`), which requires a `linux,cma`/`shared-dma-pool`
|
(`warden_rga.c:70,269-274`), which requires a `linux,cma`/`shared-dma-pool`
|
||||||
reserved-memory node to exist. On the vendor 5.10 board DT this lives in
|
reserved-memory node to exist. On the vendor 5.10 board DT this lives in
|
||||||
`rv1106-luckfox-pico-86panel-ipc.dtsi` (**not** `rv1106.dtsi`, and confirmed **not yet
|
`rv1106-luckfox-pico-86panel-ipc.dtsi` (**not** `rv1106.dtsi`, and confirmed **not yet
|
||||||
present** in `dts/rv1106-warden.dts` — grep for `reserved-memory`/`linux,cma` there
|
present** in `dts/rv1106-warden.dts`; grep for `reserved-memory`/`linux,cma` there
|
||||||
returns nothing):
|
returns nothing):
|
||||||
```dts
|
```dts
|
||||||
// rv1106-luckfox-pico-86panel-ipc.dtsi:91-107
|
// rv1106-luckfox-pico-86panel-ipc.dtsi:91-107
|
||||||
@@ -246,15 +246,15 @@ reserved_memory: reserved-memory {
|
|||||||
Port just the `linux,cma` child (the sibling `drm-logo`/`mmc_ecsd` reserved regions
|
Port just the `linux,cma` child (the sibling `drm-logo`/`mmc_ecsd` reserved regions
|
||||||
belong to the display/boot-logo and eMMC-ECSD milestones respectively, not RGA) into
|
belong to the display/boot-logo and eMMC-ECSD milestones respectively, not RGA) into
|
||||||
`rv1106-warden.dts`. Without this the RGA kernel driver itself will still probe and
|
`rv1106-warden.dts`. Without this the RGA kernel driver itself will still probe and
|
||||||
`/dev/rga` will still appear (the driver has no CMA dependency of its own — DMA-BUF
|
`/dev/rga` will still appear (the driver has no CMA dependency of its own: DMA-BUF
|
||||||
import works from any dma-buf exporter), but WardenOS's own canvas/scanout allocator
|
import works from any dma-buf exporter), but WardenOS's own canvas/scanout allocator
|
||||||
will fail open and the UI falls back to the CPU path silently (`warden_rga.c:269-274`
|
will fail open and the UI falls back to the CPU path silently (`warden_rga.c:269-274`
|
||||||
returns `-1` on `open()` failure) — so this step is required for the *offload* to be
|
returns `-1` on `open()` failure), so this step is required for the *offload* to be
|
||||||
observably working, even though it isn't required for `/dev/rga` to exist.
|
observably working, even though it isn't required for `/dev/rga` to exist.
|
||||||
|
|
||||||
### 4. Rootfs / userspace
|
### 4. Rootfs / userspace
|
||||||
|
|
||||||
None needed — `librga.so`/`.a` for `rv1106_arm-rockchip830-linux-uclibcgnueabihf`
|
None needed: `librga.so`/`.a` for `rv1106_arm-rockchip830-linux-uclibcgnueabihf`
|
||||||
already ships in the SDK (`sdk/media/rga/release_rga_rv1106_.../lib/`) and is already
|
already ships in the SDK (`sdk/media/rga/release_rga_rv1106_.../lib/`) and is already
|
||||||
what the current Buildroot overlay installs and what `warden_rga.c` already links
|
what the current Buildroot overlay installs and what `warden_rga.c` already links
|
||||||
against with `WARDEN_USE_RGA=1`. No rebuild of librga or of WardenOS's C code is
|
against with `WARDEN_USE_RGA=1`. No rebuild of librga or of WardenOS's C code is
|
||||||
@@ -263,11 +263,11 @@ implied by this kernel port.
|
|||||||
## Verify steps
|
## Verify steps
|
||||||
|
|
||||||
1. **Build:** `rga3.o` compiles with zero warnings/errors against `linux-6.18.46`
|
1. **Build:** `rga3.o` compiles with zero warnings/errors against `linux-6.18.46`
|
||||||
(same evidentiary bar as `PORT-STATUS.md`'s M1 entries — "COMPILES CLEAN").
|
(same evidentiary bar as `PORT-STATUS.md`'s M1 entries, "COMPILES CLEAN").
|
||||||
2. **Boot + node:** on warden-c8a3 (A/B `_b`-slot loop, `../docs/m2-boot-on-c8a3.md`),
|
2. **Boot + node:** on warden-c8a3 (A/B `_b`-slot loop, `../docs/m2-boot-on-c8a3.md`),
|
||||||
confirm dmesg shows `rga2, irq = 87, match scheduler` and
|
confirm dmesg shows `rga2, irq = 87, match scheduler` and
|
||||||
`rga2 hardware loaded successfully, hw_version:3.3.87975.` (the exact version string
|
`rga2 hardware loaded successfully, hw_version:3.3.87975.` (the exact version string
|
||||||
`rga_drv_probe` selects `rga2e_1106_data` on, `rga_drv.c:1403`) — a different version
|
`rga_drv_probe` selects `rga2e_1106_data` on, `rga_drv.c:1403`): a different version
|
||||||
string here would mean the wrong match-data table and is a stop-ship signal.
|
string here would mean the wrong match-data table and is a stop-ship signal.
|
||||||
`ls -l /dev/rga` exists with no manual `mknod`/`insmod`.
|
`ls -l /dev/rga` exists with no manual `mknod`/`insmod`.
|
||||||
3. **CMA heap:** `ls /dev/dma_heap/cma` exists (needs step 3(b) above).
|
3. **CMA heap:** `ls /dev/dma_heap/cma` exists (needs step 3(b) above).
|
||||||
@@ -275,24 +275,24 @@ implied by this kernel port.
|
|||||||
check is `librga`'s own CLI/test harness if the SDK ships one, or a ~20-line C
|
check is `librga`'s own CLI/test harness if the SDK ships one, or a ~20-line C
|
||||||
program using the already-present `librga.so`: allocate two small dma-buf CMA
|
program using the already-present `librga.so`: allocate two small dma-buf CMA
|
||||||
buffers via `/dev/dma_heap/cma`, fill one with a known pattern, call
|
buffers via `/dev/dma_heap/cma`, fill one with a known pattern, call
|
||||||
`improcess(src, dst, ..., IM_SYNC)` for a straight copy, and memcmp the result —
|
`improcess(src, dst, ..., IM_SYNC)` for a straight copy, and memcmp the result.
|
||||||
this exercises exactly the ioctl path (`RGA_IOC_REQUEST_SUBMIT`/`RGA_BLIT_SYNC`) and
|
This exercises exactly the ioctl path (`RGA_IOC_REQUEST_SUBMIT`/`RGA_BLIT_SYNC`) and
|
||||||
the hrtimer-driven completion path (API-delta #2) that WardenOS's real usage
|
the hrtimer-driven completion path (API-delta #2) that WardenOS's real usage
|
||||||
exercises, without needing DRM/VOP (M4) to be finished first.
|
exercises, without needing DRM/VOP (M4) to be finished first.
|
||||||
5. **On-target UI evidence (once M4/display lands):** boot the real WardenOS build with
|
5. **On-target UI evidence (once M4/display lands):** boot the real WardenOS build with
|
||||||
`WARDEN_USE_RGA=1`, open the graph/Monitor page, confirm `warden_rga_available()`
|
`WARDEN_USE_RGA=1`, open the graph/Monitor page, confirm `warden_rga_available()`
|
||||||
reports true (`querystring(RGA_VERSION)` succeeds — `warden_rga.c:458-466`) and the
|
reports true (`querystring(RGA_VERSION)` succeeds, `warden_rga.c:458-466`) and the
|
||||||
Monitor page's "RGA" load metric moves during graph scroll (`warden_rga_load_pct()`,
|
Monitor page's "RGA" load metric moves during graph scroll (`warden_rga_load_pct()`,
|
||||||
`warden_rga.c:226-241`) — the same on-target evidence bar as every other change in
|
`warden_rga.c:226-241`); the same on-target evidence bar as every other change in
|
||||||
this repo (repo policy: "UI/daemon changes are verified on a real panel").
|
this repo (repo policy: "UI/daemon changes are verified on a real panel").
|
||||||
|
|
||||||
## Summary of residual risk (PORT-VERIFY-class items)
|
## Summary of residual risk (PORT-VERIFY-class items)
|
||||||
|
|
||||||
- **hrtimer_setup() correctness under load** (delta #2) — compiles clean is not enough;
|
- **hrtimer_setup() correctness under load** (delta #2): compiles clean is not enough;
|
||||||
needs the blit-test loop run repeatedly/concurrently to rule out a scheduler-tick
|
needs the blit-test loop run repeatedly/concurrently to rule out a scheduler-tick
|
||||||
regression.
|
regression.
|
||||||
- **CMA pool sizing** — 10 MiB was sized against the 5.10 image's actual usage (graph
|
- **CMA pool sizing**: 10 MiB was sized against the 5.10 image's actual usage (graph
|
||||||
canvas + scanout mirror at 720×720×4B ≈ 2 MiB each); carry the same size unless a
|
canvas + scanout mirror at 720x720x4B ~ 2 MiB each); carry the same size unless a
|
||||||
future accounting shows it's tight.
|
future accounting shows it's tight.
|
||||||
- **Driver-parity table** (`../DRIVER-PARITY.md`) should move `RGA 2D (rga2)` from [ ] to
|
- **Driver-parity table** (`../DRIVER-PARITY.md`) should move `RGA 2D (rga2)` from [ ] to
|
||||||
[wip]/[x] as these steps land, same convention as every other M-milestone row
|
[wip]/[x] as these steps land, same convention as every other M-milestone row
|
||||||
@@ -311,10 +311,10 @@ implied by this kernel port.
|
|||||||
- Target kernel tree (ground truth for the API-delta table):
|
- Target kernel tree (ground truth for the API-delta table):
|
||||||
`flare-edge/research/linux-6.18.46/{include/linux/platform_device.h,hrtimer.h,dma-buf.h}`,
|
`flare-edge/research/linux-6.18.46/{include/linux/platform_device.h,hrtimer.h,dma-buf.h}`,
|
||||||
`.config`
|
`.config`
|
||||||
- [airockchip/librga](https://github.com/airockchip/librga) — upstream userspace IM2D
|
- [airockchip/librga](https://github.com/airockchip/librga): upstream userspace IM2D
|
||||||
library (Apache-2.0), RV1106 target support, 1.3.3 driver version on RK3588/6.1-6.4
|
library (Apache-2.0), RV1106 target support, 1.3.3 driver version on RK3588/6.1-6.4
|
||||||
- [librga FAQ — RGA hardware family notes](https://github.com/airockchip/librga/blob/main/docs/Rockchip_FAQ_RGA_EN.md) (Q2.10: RK3399/RV1126 both RGA2-ENHANCE, differing sub-versions, ROP cut on RV1126)
|
- [librga FAQ: RGA hardware family notes](https://github.com/airockchip/librga/blob/main/docs/Rockchip_FAQ_RGA_EN.md) (Q2.10: RK3399/RV1126 both RGA2-ENHANCE, differing sub-versions, ROP cut on RV1126)
|
||||||
- [torvalds/linux — drivers/media/platform/rockchip/rga/rga.c](https://raw.githubusercontent.com/torvalds/linux/master/drivers/media/platform/rockchip/rga/rga.c) — mainline V4L2 driver's `of_device_id` table (rk3288-rga, rk3399-rga → `rga2_hw`; rk3588-rga3 → `rga3_hw`)
|
- [torvalds/linux: drivers/media/platform/rockchip/rga/rga.c](https://raw.githubusercontent.com/torvalds/linux/master/drivers/media/platform/rockchip/rga/rga.c); mainline V4L2 driver's `of_device_id` table (rk3288-rga, rk3399-rga -> `rga2_hw`; rk3588-rga3 -> `rga3_hw`)
|
||||||
- [LWN — "media: platform: rga: Add RGA3 support"](https://lwn.net/Articles/1041152/) — RK3588 RGA3 mainlining, one `/dev/video` per core, no multicore scheduling in-kernel
|
- [LWN, "media: platform: rga: Add RGA3 support"](https://lwn.net/Articles/1041152/): RK3588 RGA3 mainlining, one `/dev/video` per core, no multicore scheduling in-kernel
|
||||||
- [lore.kernel.org — "media: rockchip: rga: Add rk3568 support" (Jianfeng Liu)](https://lore.kernel.org/lkml/20240322052915.3507937-1-liujianfeng1994@gmail.com/) — "RGA2 on rk3568 is the same core as RGA2 on rk3288" (confirms mainline's RGA2 lineage is the older/baseline core, not RGA2-ENHANCE)
|
- [lore.kernel.org, "media: rockchip: rga: Add rk3568 support" (Jianfeng Liu)](https://lore.kernel.org/lkml/20240322052915.3507937-1-liujianfeng1994@gmail.com/): "RGA2 on rk3568 is the same core as RGA2 on rk3288" (confirms mainline's RGA2 lineage is the older/baseline core, not RGA2-ENHANCE)
|
||||||
- CNX Software, [Rockchip RK3588 mainline Linux support](https://www.cnx-software.com/2024/12/21/rockchip-rk3588-mainline-linux-support-current-status-and-future-work-for-2025/) — RGA2 V4L2 mainlining timeline context
|
- CNX Software, [Rockchip RK3588 mainline Linux support](https://www.cnx-software.com/2024/12/21/rockchip-rk3588-mainline-linux-support-current-status-and-future-work-for-2025/): RGA2 V4L2 mainlining timeline context
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# TRNG + OTP port (batch A) — 6.18
|
# TRNG + OTP port (batch A): 6.18
|
||||||
|
|
||||||
## TRNG (hardware RNG) — VERIFIED on warden-c8a3 (2026-08-25)
|
## TRNG (hardware RNG): VERIFIED on warden-c8a3 (2026-08-25)
|
||||||
rv1106's `rockchip,trngv1` is the same standalone TRNG_V1 IP as rk3588 (identical
|
rv1106's `rockchip,trngv1` is the same standalone TRNG_V1 IP as rk3588 (identical
|
||||||
register map). Mainline `drivers/char/hw_random/rockchip-rng.c` already drives it.
|
register map). Mainline `drivers/char/hw_random/rockchip-rng.c` already drives it.
|
||||||
|
|
||||||
@@ -12,18 +12,18 @@ register map). Mainline `drivers/char/hw_random/rockchip-rng.c` already drives i
|
|||||||
rv1106's clock/reset names need no special handling.) Kconfig
|
rv1106's clock/reset names need no special handling.) Kconfig
|
||||||
`HW_RANDOM_ROCKCHIP=y` (already set).
|
`HW_RANDOM_ROCKCHIP=y` (already set).
|
||||||
|
|
||||||
**DT** (`rv1106-warden.dts`): override the dtsi `rng@ff448000` node —
|
**DT** (`rv1106-warden.dts`): override the dtsi `rng@ff448000` node:
|
||||||
```dts
|
```dts
|
||||||
&rng { compatible = "rockchip,rv1106-rng"; status = "okay"; };
|
&rng { compatible = "rockchip,rv1106-rng"; status = "okay"; };
|
||||||
```
|
```
|
||||||
|
|
||||||
**Evidence:** `/dev/hwrng` present, `rng_current = rockchip-rng`,
|
**Evidence:** `/dev/hwrng` present, `rng_current = rockchip-rng`,
|
||||||
`dd if=/dev/hwrng bs=16` → `c697 503d f9db 6b84 50e4 e1ee f232 b2ae` (real HW
|
`dd if=/dev/hwrng bs=16` -> `c697 503d f9db 6b84 50e4 e1ee f232 b2ae` (real HW
|
||||||
entropy, non-zero). Hardware entropy source for the panel's crypto/keys.
|
entropy, non-zero). Hardware entropy source for the panel's crypto/keys.
|
||||||
|
|
||||||
## OTP / nvmem — VERIFIED on warden-c8a3 (2026-08-25)
|
## OTP / nvmem: VERIFIED on warden-c8a3 (2026-08-25)
|
||||||
Reads real data: `dd .../rockchip-otp0/nvmem bs=1 count=16 | xxd` →
|
Reads real data: `dd .../rockchip-otp0/nvmem bs=1 count=16 | xxd` ->
|
||||||
`5211 02fe 084d 5231 0000 0000 3b15 0000` (contains "MR1" chip id) — no timeout.
|
`5211 02fe 084d 5231 0000 0000 3b15 0000` (contains "MR1" chip id), no timeout.
|
||||||
Mainline `drivers/nvmem/rockchip-otp.c` gains an `rv1106_data` + compatible.
|
Mainline `drivers/nvmem/rockchip-otp.c` gains an `rv1106_data` + compatible.
|
||||||
|
|
||||||
**Delta:**
|
**Delta:**
|
||||||
@@ -42,7 +42,7 @@ static const struct rockchip_data rv1106_data = {
|
|||||||
Kconfig `NVMEM_ROCKCHIP_OTP=y`. **DT:** `&otp { status = "okay"; };` (the dtsi
|
Kconfig `NVMEM_ROCKCHIP_OTP=y`. **DT:** `&otp { status = "okay"; };` (the dtsi
|
||||||
`otp@ff3d0000` node already carries compatible + the 6 clocks/resets).
|
`otp@ff3d0000` node already carries compatible + the 6 clocks/resets).
|
||||||
|
|
||||||
**First try** used `.reg_read = rk3588_otp_read` → `timeout during read setup`
|
**First try** used `.reg_read = rk3588_otp_read` -> `timeout during read setup`
|
||||||
(rk3588 uses a different addressing path). Corrected to `px30_otp_read` — the
|
(rk3588 uses a different addressing path). Corrected to `px30_otp_read`: the
|
||||||
mainline user-mode OTPC_USER read, the same sequence the vendor 5.10 driver used
|
mainline user-mode OTPC_USER read, the same sequence the vendor 5.10 driver used
|
||||||
for rv1106 (its `rk3568_otp_read`). Confirmed: cell reads return real data.
|
for rv1106 (its `rk3568_otp_read`). Confirmed: cell reads return real data.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# RTC — rockchip,rv1106-rtc
|
# RTC: rockchip,rv1106-rtc
|
||||||
|
|
||||||
`rtc-rockchip.c` — the vendor internal-RTC driver (open source, from the SDK),
|
`rtc-rockchip.c`: the vendor internal-RTC driver (open source, from the SDK),
|
||||||
ported to 6.18. Only 5.10→6.18 API delta: `rtc_register_device` →
|
ported to 6.18. Only 5.10->6.18 API delta: `rtc_register_device` ->
|
||||||
`devm_rtc_register_device` (paired with the existing `devm_rtc_allocate_device`).
|
`devm_rtc_register_device` (paired with the existing `devm_rtc_allocate_device`).
|
||||||
Enable `CONFIG_RTC_DRV_ROCKCHIP=y` + `&rtc { status="okay"; }`. Verified on
|
Enable `CONFIG_RTC_DRV_ROCKCHIP=y` + `&rtc { status="okay"; }`. Verified on
|
||||||
warden-c8a3: `/dev/rtc0` registers and reads.
|
warden-c8a3: `/dev/rtc0` registers and reads.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# arch-timer / vDSO clock fix — plan (issue #3)
|
# arch-timer / vDSO clock fix: plan (issue #3)
|
||||||
|
|
||||||
Status: DIAGNOSED off-board, fix gated on two bench measurements.
|
Status: DIAGNOSED off-board, fix gated on two bench measurements.
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ The same kernel family under `qemu-system-arm -M virt` gives musl
|
|||||||
measurement, `qemu/tests/clockprobe`). The generic 6.18 armv7 vDSO is
|
measurement, `qemu/tests/clockprobe`). The generic 6.18 armv7 vDSO is
|
||||||
therefore CORRECT; the board symptom (musl reads ~12% high, kernel time
|
therefore CORRECT; the board symptom (musl reads ~12% high, kernel time
|
||||||
right) is RV1106-specific. The boot chain runs in the secure world and is
|
right) is RV1106-specific. The boot chain runs in the secure world and is
|
||||||
closed rkbin — the NS view of the CPU timer registers (CNTFRQ, CNTVOFF) is
|
closed rkbin: the NS view of the CPU timer registers (CNTFRQ, CNTVOFF) is
|
||||||
whatever it left behind, and only the arch-counter path (vDSO,
|
whatever it left behind, and only the arch-counter path (vDSO,
|
||||||
`arch_sys_counter`) trusts them.
|
`arch_sys_counter`) trusts them.
|
||||||
|
|
||||||
@@ -28,14 +28,14 @@ distinguish these.)
|
|||||||
|
|
||||||
## The fix (both cases, one DT override)
|
## The fix (both cases, one DT override)
|
||||||
|
|
||||||
Append to the BOARD dts (`rv1106-warden.dts` — never the vendor dtsi) an
|
Append to the BOARD dts (`rv1106-warden.dts`, never the vendor dtsi) an
|
||||||
override on the armv7-timer node:
|
override on the armv7-timer node:
|
||||||
|
|
||||||
arm,cpu-registers-not-fw-configured;
|
arm,cpu-registers-not-fw-configured;
|
||||||
clock-frequency = <MEASURED_HZ>;
|
clock-frequency = <MEASURED_HZ>;
|
||||||
|
|
||||||
The property makes the driver use the physical counter, ignore CNTVOFF, and
|
The property makes the driver use the physical counter, ignore CNTVOFF, and
|
||||||
take the frequency from DT — the documented remedy for firmware that does
|
take the frequency from DT: the documented remedy for firmware that does
|
||||||
not configure the CPU timer registers. `MEASURED_HZ` comes from bench
|
not configure the CPU timer registers. `MEASURED_HZ` comes from bench
|
||||||
answer 1 (do NOT guess; a wrong value makes every clock wrong instead of
|
answer 1 (do NOT guess; a wrong value makes every clock wrong instead of
|
||||||
one path). Ship as an update to the arch/dts patch in `patches/`.
|
one path). Ship as an update to the arch/dts patch in `patches/`.
|
||||||
@@ -50,5 +50,5 @@ one path). Ship as an update to the arch/dts patch in `patches/`.
|
|||||||
## Bench access note
|
## Bench access note
|
||||||
|
|
||||||
2026-08-30: c8a3 is physically dark (CP2102 console silent through two
|
2026-08-30: c8a3 is physically dark (CP2102 console silent through two
|
||||||
remote power cycles; both network paths down) — needs hands at the bench
|
remote power cycles; both network paths down), needs hands at the bench
|
||||||
before the measurements can run.
|
before the measurements can run.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# GT911 capacitive touch — VERIFIED on warden-c8a3 (2026-08-25)
|
# GT911 capacitive touch: VERIFIED on warden-c8a3 (2026-08-25)
|
||||||
|
|
||||||
Touch works on our self-built 6.18.46 (`_b`): the WardenOS UI responds to
|
Touch works on our self-built 6.18.46 (`_b`): the WardenOS UI responds to
|
||||||
taps/swipes (confirmed by hand on the physical panel). Objective evidence:
|
taps/swipes (confirmed by hand on the physical panel). Objective evidence:
|
||||||
@@ -8,7 +8,7 @@ Goodix-TS 3-0014: ID 911, version: 1060
|
|||||||
input: Goodix Capacitive TouchScreen as .../i2c-3/3-0014/input/input0
|
input: Goodix Capacitive TouchScreen as .../i2c-3/3-0014/input/input0
|
||||||
```
|
```
|
||||||
`/dev/input/event0` is created and held open by `warden-ui` (pid 517, exclusive
|
`/dev/input/event0` is created and held open by `warden-ui` (pid 517, exclusive
|
||||||
EVIOCGRAB — which is why a second reader sees 0 bytes; the events go to the UI).
|
EVIOCGRAB: which is why a second reader sees 0 bytes; the events go to the UI).
|
||||||
|
|
||||||
## Root cause on `_b`
|
## Root cause on `_b`
|
||||||
|
|
||||||
@@ -20,12 +20,12 @@ built struct module size at run time
|
|||||||
The **stock rootfs ships `goodix.ko` built for the 5.10 kernel**; it cannot load
|
The **stock rootfs ships `goodix.ko` built for the 5.10 kernel**; it cannot load
|
||||||
on our 6.18 (`struct module` layout mismatch). Our 6.18 `.config` did **not** have
|
on our 6.18 (`struct module` layout mismatch). Our 6.18 `.config` did **not** have
|
||||||
the driver at all (`# CONFIG_TOUCHSCREEN_GOODIX is not set`), and the DTS had no
|
the driver at all (`# CONFIG_TOUCHSCREEN_GOODIX is not set`), and the DTS had no
|
||||||
GT911 node — so nothing drove the GT911.
|
GT911 node, so nothing drove the GT911.
|
||||||
|
|
||||||
## Fix
|
## Fix
|
||||||
|
|
||||||
1. **`CONFIG_TOUCHSCREEN_GOODIX=y`** — build the mainline Goodix driver *into* the
|
1. **`CONFIG_TOUCHSCREEN_GOODIX=y`**: build the mainline Goodix driver *into* the
|
||||||
kernel (no module → no vermagic/struct mismatch; the stale rootfs `.ko` still
|
kernel (no module -> no vermagic/struct mismatch; the stale rootfs `.ko` still
|
||||||
fails to insmod but is now harmless).
|
fails to insmod but is now harmless).
|
||||||
2. **GT911 DT node** under `&i2c3` (matches the vendor 86-panel wiring):
|
2. **GT911 DT node** under `&i2c3` (matches the vendor 86-panel wiring):
|
||||||
```
|
```
|
||||||
@@ -43,15 +43,15 @@ GT911 node — so nothing drove the GT911.
|
|||||||
|
|
||||||
Polarity note: mainline goodix drives reset **logical 0 = hold, 1 = release**, and
|
Polarity note: mainline goodix drives reset **logical 0 = hold, 1 = release**, and
|
||||||
the GT911 reset is physically active-low, so `reset-gpios` is **ACTIVE_HIGH**
|
the GT911 reset is physically active-low, so `reset-gpios` is **ACTIVE_HIGH**
|
||||||
(logical==physical) — NOT the ACTIVE_LOW the vendor 5.10 driver used. Confirmed
|
(logical==physical), NOT the ACTIVE_LOW the vendor 5.10 driver used. Confirmed
|
||||||
against mainline gt911 DT examples (sun7i-a20-wexler-tab7200 etc.): both irq-gpios
|
against mainline gt911 DT examples (sun7i-a20-wexler-tab7200 etc.): both irq-gpios
|
||||||
and reset-gpios are ACTIVE_HIGH; reg 0x14 needs irq-gpios for address select.
|
and reset-gpios are ACTIVE_HIGH; reg 0x14 needs irq-gpios for address select.
|
||||||
|
|
||||||
Non-fatal: `Direct firmware load for goodix_911_cfg.bin failed (-2)` — the GT911
|
Non-fatal: `Direct firmware load for goodix_911_cfg.bin failed (-2)`; the GT911
|
||||||
uses its flashed internal config; touch works without a cfg.bin.
|
uses its flashed internal config; touch works without a cfg.bin.
|
||||||
|
|
||||||
## Files changed (research/linux-6.18.46)
|
## Files changed (research/linux-6.18.46)
|
||||||
|
|
||||||
- `.config` — `CONFIG_TOUCHSCREEN_GOODIX=y`.
|
- `.config`: `CONFIG_TOUCHSCREEN_GOODIX=y`.
|
||||||
- `arch/arm/boot/dts/rockchip/rv1106-warden.dts` — GT911 node on `&i2c3`,
|
- `arch/arm/boot/dts/rockchip/rv1106-warden.dts`: GT911 node on `&i2c3`,
|
||||||
`tp_rst`/`tp_irq` pin groups under `&pinctrl`.
|
`tp_rst`/`tp_irq` pin groups under `&pinctrl`.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
* RV1106 USB2 PHY: single OTG port at 0xff3e0000 (ported from the vendor driver;
|
* RV1106 USB2 PHY: single OTG port at 0xff3e0000 (ported from the vendor driver;
|
||||||
* fields map 1:1 to 6.18 except utmi_iddig -> utmi_id, and the 5.10-only
|
* fields map 1:1 to 6.18 except utmi_iddig -> utmi_id, and the 5.10-only
|
||||||
* iddig_output/iddig_en/bvalid_grf_sel are dropped). Signal-quality phy_tuning is
|
* iddig_output/iddig_en/bvalid_grf_sel are dropped). Signal-quality phy_tuning is
|
||||||
* left off for now (guarded, so NULL is safe) — the phy is functional without it.
|
* left off for now (guarded, so NULL is safe): the phy is functional without it.
|
||||||
*/
|
*/
|
||||||
static const struct rockchip_usb2phy_cfg rv1106_phy_cfgs[] = {
|
static const struct rockchip_usb2phy_cfg rv1106_phy_cfgs[] = {
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,50 +1,50 @@
|
|||||||
# AIC8800 WiFi+BT SDIO driver — 5.10 → 6.18 port plan
|
# AIC8800 WiFi+BT SDIO driver: 5.10 -> 6.18 port plan
|
||||||
|
|
||||||
Target: the M5 milestone of `../PORT-STATUS.md` / `../../docs/bringup.md` — port
|
Target: the M5 milestone of `../PORT-STATUS.md` / `../../docs/bringup.md`, port
|
||||||
the AIC8800 SDIO WiFi+BT driver onto the self-built **Linux 6.18.46** kernel that
|
the AIC8800 SDIO WiFi+BT driver onto the self-built **Linux 6.18.46** kernel that
|
||||||
already boots WardenOS on `warden-c8a3` (M0–M3 done; M4 display in progress). No
|
already boots WardenOS on `warden-c8a3` (M0-M3 done; M4 display in progress). No
|
||||||
plan44 code (per the 2026-08-23 decision in `bringup.md`) — this is a direct
|
plan44 code (per the 2026-08-23 decision in `bringup.md`): this is a direct
|
||||||
forward-port of our own vendor source, same method already proven for
|
forward-port of our own vendor source, same method already proven for
|
||||||
clk/pinctrl/mach/mmc.
|
clk/pinctrl/mach/mmc.
|
||||||
|
|
||||||
## 0. Source recommendation
|
## 0. Source recommendation
|
||||||
|
|
||||||
**Port from our own vendor SDK source**, not from any external fork:
|
**Port from our own vendor SDK source**, not from any external fork:
|
||||||
`flare-edge/sdk/sysdrv/drv_ko/wifi/aic8800dc/` (§1 below is the full map). This
|
`flare-edge/sdk/sysdrv/drv_ko/wifi/aic8800dc/` (section 1 below is the full map). This
|
||||||
is not a default-to-what-we-have choice — it's the correct one on the evidence:
|
is not a default-to-what-we-have choice; it's the correct one on the evidence:
|
||||||
|
|
||||||
- **Nothing more upstream exists.** AICSemi has no public upstream driver repo
|
- **Nothing more upstream exists.** AICSemi has no public upstream driver repo
|
||||||
with a stable, discoverable URL, and there is no aic8800 code in Linux
|
with a stable, discoverable URL, and there is no aic8800 code in Linux
|
||||||
`staging` or any LKML patch series (mainline/staging status: **absent** —
|
`staging` or any LKML patch series (mainline/staging status: **absent**,
|
||||||
confirmed by a dedicated web-research pass; no counter-evidence found).
|
confirmed by a dedicated web-research pass; no counter-evidence found).
|
||||||
There is nothing more "canonical" to port from than the vendor source we
|
There is nothing more "canonical" to port from than the vendor source we
|
||||||
already have.
|
already have.
|
||||||
- **Our tree already carries two hardware-verified fixes newer forks don't
|
- **Our tree already carries two hardware-verified fixes newer forks don't
|
||||||
have**: the `queue_sz`-zero console-flood clamp
|
have**: the `queue_sz`-zero console-flood clamp
|
||||||
(`flare-edge/*/sdk-patches/wifi/patches/0001-aic8800dc-no-zero-timeout-spin-and-ratelimit.patch`)
|
(`flare-edge/*/sdk-patches/wifi/patches/0001-aic8800dc-no-zero-timeout-spin-and-ratelimit.patch`)
|
||||||
and the SDIO-wakeup busy-spin→sleep Tier-1 hardening (live in the vendor tree
|
and the SDIO-wakeup busy-spin->sleep Tier-1 hardening (live in the vendor tree
|
||||||
today per `luckfox-pico-86-panel/wifi-bluetooth-aic8800.md`, tracked as
|
today per `luckfox-pico-86-panel/wifi-bluetooth-aic8800.md`, tracked as
|
||||||
`future-features-2/sdk-patches/wifi/patches/0002-aic8800dc-sdio-wakeup-sleep-not-spin.patch`
|
`future-features-2/sdk-patches/wifi/patches/0002-aic8800dc-sdio-wakeup-sleep-not-spin.patch`,
|
||||||
— **not yet copied into this branch's `sdk-patches/`; carry both forward into
|
**not yet copied into this branch's `sdk-patches/`; carry both forward into
|
||||||
the ported tree, and file the housekeeping gap separately**). Starting from a
|
the ported tree, and file the housekeeping gap separately**). Starting from a
|
||||||
third-party fork would mean re-discovering and re-fixing both bugs on
|
third-party fork would mean re-discovering and re-fixing both bugs on
|
||||||
hardware.
|
hardware.
|
||||||
- **The vendor source already anticipates part of the delta.** `rwnx_compat.h`
|
- **The vendor source already anticipates part of the delta.** `rwnx_compat.h`
|
||||||
has `#if LINUX_VERSION_CODE` shims up to `KERNEL_VERSION(5, 15, 60)` — e.g.
|
has `#if LINUX_VERSION_CODE` shims up to `KERNEL_VERSION(5, 15, 60)`, e.g.
|
||||||
`rwnx_main.c`'s `net_device_ops` already switches between `.ndo_do_ioctl` and
|
`rwnx_main.c`'s `net_device_ops` already switches between `.ndo_do_ioctl` and
|
||||||
`.ndo_siocdevprivate` at the 5.15 boundary (rwnx_main.c:1457–1474). That
|
`.ndo_siocdevprivate` at the 5.15 boundary (rwnx_main.c:1457-1474). That
|
||||||
removes one whole item from the delta checklist below at zero cost.
|
removes one whole item from the delta checklist below at zero cost.
|
||||||
- **A cleaner community fork exists but is not itself a base — it's a reference.**
|
- **A cleaner community fork exists but is not itself a base: it's a reference.**
|
||||||
`radxa-pkg/aic8800` (github.com/radxa-pkg/aic8800) is an actively maintained,
|
`radxa-pkg/aic8800` (github.com/radxa-pkg/aic8800) is an actively maintained,
|
||||||
GPL-3.0, DKMS-packaged build of the same AICSemi driver family, explicitly
|
GPL-3.0, DKMS-packaged build of the same AICSemi driver family, explicitly
|
||||||
patched for kernel **6.12/6.13** on Rockchip SDIO boards (Rock 3C/5C), and is
|
patched for kernel **6.12/6.13** on Rockchip SDIO boards (Rock 3C/5C), and is
|
||||||
the only community effort found that has already absorbed the post-5.15
|
the only community effort found that has already absorbed the post-5.15
|
||||||
cfg80211/netdev/timer churn this port needs. **Use it the same way the
|
cfg80211/netdev/timer churn this port needs. **Use it the same way the
|
||||||
pinctrl port used the upstream RV1106 patch series — as a correctness
|
pinctrl port used the upstream RV1106 patch series, as a correctness
|
||||||
oracle to diff against**, not as code we vendor: it targets AIC8800D80 SDIO/USB/PCIe
|
oracle to diff against**, not as code we vendor: it targets AIC8800D80 SDIO/USB/PCIe
|
||||||
variants generically, not our AIC8800DC + our two local hardening patches,
|
variants generically, not our AIC8800DC + our two local hardening patches,
|
||||||
and an Armbian forum thread reports open regressions on it at the 6.12+
|
and an Armbian forum thread reports open regressions on it at the 6.12+
|
||||||
boundary — so treat its fixes as a second opinion on the wiphy-lock and
|
boundary, so treat its fixes as a second opinion on the wiphy-lock and
|
||||||
timer-rename hunks, verify each independently.
|
timer-rename hunks, verify each independently.
|
||||||
- **No mainline/staging path exists to lean on instead.** Confirmed by the
|
- **No mainline/staging path exists to lean on instead.** Confirmed by the
|
||||||
research pass: no aic8800 in `drivers/net/wireless/`, `staging/`, or any
|
research pass: no aic8800 in `drivers/net/wireless/`, `staging/`, or any
|
||||||
@@ -72,7 +72,7 @@ aic8800dc/
|
|||||||
Makefile # obj-$(CONFIG_AIC8800_BTLPM_SUPPORT) += aic8800_btlpm/
|
Makefile # obj-$(CONFIG_AIC8800_BTLPM_SUPPORT) += aic8800_btlpm/
|
||||||
# obj-$(CONFIG_AIC8800_WLAN_SUPPORT) += aic8800_fdrv/
|
# obj-$(CONFIG_AIC8800_WLAN_SUPPORT) += aic8800_fdrv/
|
||||||
# obj-$(CONFIG_AIC_WLAN_SUPPORT) += aic8800_bsp/
|
# obj-$(CONFIG_AIC_WLAN_SUPPORT) += aic8800_bsp/
|
||||||
# (link order = bsp, fdrv, btlpm — matches the insmod order below)
|
# (link order = bsp, fdrv, btlpm, matches the insmod order below)
|
||||||
aic8800_bsp/ # SDIO bus glue + firmware bootstrap ("bsp" = board support package)
|
aic8800_bsp/ # SDIO bus glue + firmware bootstrap ("bsp" = board support package)
|
||||||
aic_bsp_main.c # module_init/exit; per-chip firmware filename tables (fw_u02,
|
aic_bsp_main.c # module_init/exit; per-chip firmware filename tables (fw_u02,
|
||||||
# fw_8800dc_u01, fw_8800dc_u02, ...); aicbsp_probe_semaphore
|
# fw_8800dc_u01, fw_8800dc_u02, ...); aicbsp_probe_semaphore
|
||||||
@@ -80,7 +80,7 @@ aic8800dc/
|
|||||||
# wildcard match + internal vendor/device ID probe:
|
# wildcard match + internal vendor/device ID probe:
|
||||||
# SDIO_VENDOR_ID_AIC8800DC=0xc8a1, SDIO_DEVICE_ID_AIC8800DC=0xc08d
|
# SDIO_VENDOR_ID_AIC8800DC=0xc8a1, SDIO_DEVICE_ID_AIC8800DC=0xc08d
|
||||||
# (aicsdio.c:75,80); calls rockchip_wifi_power()/
|
# (aicsdio.c:75,80); calls rockchip_wifi_power()/
|
||||||
# rockchip_wifi_set_carddetect() (aicsdio.c:515-580) — see §3.7
|
# rockchip_wifi_set_carddetect() (aicsdio.c:515-580), see section 3.7
|
||||||
aic8800dc_compat.c/.h, aic8800d80_compat.c/.h # per-chip-variant glue
|
aic8800dc_compat.c/.h, aic8800d80_compat.c/.h # per-chip-variant glue
|
||||||
aicwf_txq_prealloc.c, md5.c, aic_bsp_driver.c/.h, aicwf_firmware_array.c/.h
|
aicwf_txq_prealloc.c, md5.c, aic_bsp_driver.c/.h, aicwf_firmware_array.c/.h
|
||||||
aic8800_fdrv/ # the actual cfg80211 full-MAC driver ("fdrv" = fullmac driver)
|
aic8800_fdrv/ # the actual cfg80211 full-MAC driver ("fdrv" = fullmac driver)
|
||||||
@@ -88,51 +88,51 @@ aic8800dc/
|
|||||||
# (5732), wiphy_register() (6035); struct net_device_ops
|
# (5732), wiphy_register() (6035); struct net_device_ops
|
||||||
# rwnx_netdev_ops (1457) / rwnx_netdev_monitor_ops (1477);
|
# rwnx_netdev_ops (1457) / rwnx_netdev_monitor_ops (1477);
|
||||||
# 3 rtnl_lock()/rtnl_unlock() pairs (5429,6063,6097)
|
# 3 rtnl_lock()/rtnl_unlock() pairs (5429,6063,6097)
|
||||||
rwnx_msg_rx.c # firmware→driver event handling incl. cfg80211_connect_result()
|
rwnx_msg_rx.c # firmware->driver event handling incl. cfg80211_connect_result()
|
||||||
# (line 958) and cfg80211_roamed() (1006,1010)
|
# (line 958) and cfg80211_roamed() (1006,1010)
|
||||||
rwnx_cfgfile.c, rwnx_tx.c, rwnx_rx.c, rwnx_txq.c # datapath
|
rwnx_cfgfile.c, rwnx_tx.c, rwnx_rx.c, rwnx_txq.c # datapath
|
||||||
aicwf_sdio.c # the actual SDIO transport (`aicwf_sdio_driver`, line 1216) —
|
aicwf_sdio.c # the actual SDIO transport (`aicwf_sdio_driver`, line 1216),
|
||||||
# THIS is the file the task's "aicwf_sdio" driver name refers to;
|
# THIS is the file the task's "aicwf_sdio" driver name refers to;
|
||||||
# it lives inside aic8800_fdrv/, not a separate directory. Also
|
# it lives inside aic8800_fdrv/, not a separate directory. Also
|
||||||
# calls rockchip_wifi_power()/set_carddetect() (1260-1331) and
|
# calls rockchip_wifi_power()/set_carddetect() (1260-1331) and
|
||||||
# contains the Tier-1 wakeup-sleep hardening (§0) around the
|
# contains the Tier-1 wakeup-sleep hardening (section 0) around the
|
||||||
# `aicwf_sdio_wakeup()` retry loop (~line 1363 in the older
|
# `aicwf_sdio_wakeup()` retry loop (~line 1363 in the older
|
||||||
# line numbering cited by the wiki; grep for `usleep_range`).
|
# line numbering cited by the wiki; grep for `usleep_range`).
|
||||||
aicwf_tcp_ack.c, aicwf_rx_prealloc.c, aic_priv_cmd.c, aic_vendor.c
|
aicwf_tcp_ack.c, aicwf_rx_prealloc.c, aic_priv_cmd.c, aic_vendor.c
|
||||||
rwnx_compat.h # vendor compat shim layer, #if LINUX_VERSION_CODE guards up to
|
rwnx_compat.h # vendor compat shim layer, #if LINUX_VERSION_CODE guards up to
|
||||||
# 5.15.60 ONLY — everything past that (timer renames, wiphy
|
# 5.15.60 ONLY: everything past that (timer renames, wiphy
|
||||||
# locking, netif_rx_ni removal) is new territory, not covered.
|
# locking, netif_rx_ni removal) is new territory, not covered.
|
||||||
usb_host.c/.h, rwnx_pci.*, rwnx_mesh.* # dead code on this board (no USB/PCI variant used)
|
usb_host.c/.h, rwnx_pci.*, rwnx_mesh.* # dead code on this board (no USB/PCI variant used)
|
||||||
aic8800_btlpm/ # Bluetooth low-power-mode / HCI wake companion module
|
aic8800_btlpm/ # Bluetooth low-power-mode / HCI wake companion module
|
||||||
aic8800_btlpm.c, aic_bluetooth_main.c, lpm.c, rfkill.c # standard rfkill_register(), no
|
aic8800_btlpm.c, aic_bluetooth_main.c, lpm.c, rfkill.c # standard rfkill_register(), no
|
||||||
# Rockchip-specific RFKILL_RK dependency (checked, none found)
|
# Rockchip-specific RFKILL_RK dependency (checked, none found)
|
||||||
aic8800dc_fw/ # firmware blobs, see §5
|
aic8800dc_fw/ # firmware blobs, see section 5
|
||||||
```
|
```
|
||||||
|
|
||||||
**Modules actually `insmod`ed on the running 5.10 panel** (from
|
**Modules actually `insmod`ed on the running 5.10 panel** (from
|
||||||
`sdk/sysdrv/drv_ko/wifi/insmod_wifi.sh:109-124`, the `#aic8800` stanza gated on
|
`sdk/sysdrv/drv_ko/wifi/insmod_wifi.sh:109-124`, the `#aic8800` stanza gated on
|
||||||
`/proc/device-tree/model` containing `"W"` or an SDIO uevent match):
|
`/proc/device-tree/model` containing `"W"` or an SDIO uevent match):
|
||||||
```
|
```
|
||||||
cfg80211.ko → libarc4.ko → ctr.ko → ccm.ko → libaes.ko → aes_generic.ko
|
cfg80211.ko -> libarc4.ko -> ctr.ko -> ccm.ko -> libaes.ko -> aes_generic.ko
|
||||||
→ aic8800_bsp.ko (sleep 0.2s) → aic8800_fdrv.ko (sleep 2s) → aic8800_btlpm.ko (sleep 0.1s)
|
-> aic8800_bsp.ko (sleep 0.2s) -> aic8800_fdrv.ko (sleep 2s) -> aic8800_btlpm.ko (sleep 0.1s)
|
||||||
```
|
```
|
||||||
`rkwifi_server` is deliberately never started (WardenOS owns `wlan0` directly —
|
`rkwifi_server` is deliberately never started (WardenOS owns `wlan0` directly,
|
||||||
see `insmod_wifi.sh:126-137` and `wifi-bluetooth-aic8800.md`); nothing to
|
see `insmod_wifi.sh:126-137` and `wifi-bluetooth-aic8800.md`); nothing to
|
||||||
replicate there. The crypto modules (arc4/ctr/ccm/aes) are dependencies of the
|
replicate there. The crypto modules (arc4/ctr/ccm/aes) are dependencies of the
|
||||||
driver's internal key-handling, not aic8800-specific — confirm they're already
|
driver's internal key-handling, not aic8800-specific, confirm they're already
|
||||||
`=y`/reachable in the 6.18 config (crypto is currently listed as "[ ] batch2" in
|
`=y`/reachable in the 6.18 config (crypto is currently listed as "[ ] batch2" in
|
||||||
`../DRIVER-PARITY.md`; flip alongside this work).
|
`../DRIVER-PARITY.md`; flip alongside this work).
|
||||||
|
|
||||||
**DT node — correction to the task's framing.** The task description assumed
|
**DT node: correction to the task's framing.** The task description assumed
|
||||||
`sdio: mmc@ff9a0000`. **That is wrong for this board.** The AIC8800 is wired to
|
`sdio: mmc@ff9a0000`. **That is wrong for this board.** The AIC8800 is wired to
|
||||||
**`sdmmc: mmc@ffaa0000`** (mmc1), not `sdio: mmc@ff9a0000` (mmc2) — confirmed
|
**`sdmmc: mmc@ffaa0000`** (mmc1), not `sdio: mmc@ff9a0000` (mmc2), confirmed
|
||||||
directly in `rv1106g-luckfox-pico-86panel.dts:83-97` (comment literally reads
|
directly in `rv1106g-luckfox-pico-86panel.dts:83-97` (comment literally reads
|
||||||
`/**********SDIO-WIFI**********/` over the `&sdmmc` node) and independently in
|
`/**********SDIO-WIFI**********/` over the `&sdmmc` node) and independently in
|
||||||
`luckfox-pico-86-panel/wifi-bluetooth-aic8800.md:11`/`hardware-86-panel.md:69`.
|
`luckfox-pico-86-panel/wifi-bluetooth-aic8800.md:11`/`hardware-86-panel.md:69`.
|
||||||
`&sdio` (mmc@ff9a0000) stays `status = "disabled"` and is unused on this board.
|
`&sdio` (mmc@ff9a0000) stays `status = "disabled"` and is unused on this board.
|
||||||
There is **no separate DT child node** for the aic8800 chip itself — it's
|
There is **no separate DT child node** for the aic8800 chip itself: it's
|
||||||
discovered purely by SDIO bus-scan + vendor/device ID match inside the driver
|
discovered purely by SDIO bus-scan + vendor/device ID match inside the driver
|
||||||
(`aicsdio.c`, no `of_match_table` anywhere in the tree — checked, none found);
|
(`aicsdio.c`, no `of_match_table` anywhere in the tree, checked, none found);
|
||||||
the only DT surface is the MMC controller node plus the power-sequencing node:
|
the only DT surface is the MMC controller node plus the power-sequencing node:
|
||||||
```dts
|
```dts
|
||||||
sdio_pwrseq: sdio-pwrseq { /* rv1106g-luckfox-pico-86panel.dts:20-23 */
|
sdio_pwrseq: sdio-pwrseq { /* rv1106g-luckfox-pico-86panel.dts:20-23 */
|
||||||
@@ -156,50 +156,50 @@ sdio_pwrseq: sdio-pwrseq { /* rv1106g-luckfox-pico-86panel.
|
|||||||
```
|
```
|
||||||
The `sdmmc0_{clk,cmd,bus4,det}` pin groups are defined in
|
The `sdmmc0_{clk,cmd,bus4,det}` pin groups are defined in
|
||||||
`sdk/sysdrv/source/kernel/arch/arm/boot/dts/rv1106-pinctrl.dtsi:700-731` (a
|
`sdk/sysdrv/source/kernel/arch/arm/boot/dts/rv1106-pinctrl.dtsi:700-731` (a
|
||||||
separate include from `rv1106.dtsi`) — port that block, it isn't in the DT
|
separate include from `rv1106.dtsi`): port that block, it isn't in the DT
|
||||||
files the M1–M3 work already ported.
|
files the M1-M3 work already ported.
|
||||||
|
|
||||||
## 2. Open-source landscape (web research)
|
## 2. Open-source landscape (web research)
|
||||||
|
|
||||||
- **AICSemi upstream**: no discoverable, stable public GitHub org/repo carrying
|
- **AICSemi upstream**: no discoverable, stable public GitHub org/repo carrying
|
||||||
aic8800_bsp/fdrv/btlpm as canonical upstream — vendor releases exist only
|
aic8800_bsp/fdrv/btlpm as canonical upstream, vendor releases exist only
|
||||||
as SoC-vendor SDK drops (Rockchip's, in our case). Nothing more upstream to
|
as SoC-vendor SDK drops (Rockchip's, in our case). Nothing more upstream to
|
||||||
point at than what we have.
|
point at than what we have.
|
||||||
- **Mainline/staging**: **absent.** No aic8800 anywhere in `drivers/net/wireless/`,
|
- **Mainline/staging**: **absent.** No aic8800 anywhere in `drivers/net/wireless/`,
|
||||||
`drivers/staging/`, or any LKML/patchwork series as of this research pass.
|
`drivers/staging/`, or any LKML/patchwork series as of this research pass.
|
||||||
- **Cleanest newer-kernel fork found**: `radxa-pkg/aic8800`
|
- **Cleanest newer-kernel fork found**: `radxa-pkg/aic8800`
|
||||||
(github.com/radxa-pkg/aic8800) — actively maintained, GPL-3.0, DKMS-packaged,
|
(github.com/radxa-pkg/aic8800), actively maintained, GPL-3.0, DKMS-packaged,
|
||||||
patched for kernel **6.12/6.13** on Rockchip SDIO boards (Radxa Rock 3C/5C),
|
patched for kernel **6.12/6.13** on Rockchip SDIO boards (Radxa Rock 3C/5C),
|
||||||
covering SDIO/USB/PCIe AIC8800D80 variants. Firmware ships as a companion
|
covering SDIO/USB/PCIe AIC8800D80 variants. Firmware ships as a companion
|
||||||
`aic8800-firmware` package into `/lib/firmware/aic8800_fw/`. Treat as a
|
`aic8800-firmware` package into `/lib/firmware/aic8800_fw/`. Treat as a
|
||||||
**reference/oracle for the API-delta hunks** (§3), not a vendoring source —
|
**reference/oracle for the API-delta hunks** (section 3), not a vendoring source,
|
||||||
see §0 for why. An Armbian forum thread ("AIC8800 wifi sdio module not
|
see section 0 for why. An Armbian forum thread ("AIC8800 wifi sdio module not
|
||||||
working with kernel 6.12+") reports it has its own open regressions at that
|
working with kernel 6.12+") reports it has its own open regressions at that
|
||||||
boundary, so cross-check rather than trust each hunk blindly.
|
boundary, so cross-check rather than trust each hunk blindly.
|
||||||
- **Other community efforts** (Armbian, LuckFox's own OpenWrt branch, generic
|
- **Other community efforts** (Armbian, LuckFox's own OpenWrt branch, generic
|
||||||
BananaPi/OpenWrt feeds): no aic8800 kmod bundled by default even where the DT
|
BananaPi/OpenWrt feeds): no aic8800 kmod bundled by default even where the DT
|
||||||
wiring exists — `luckfox-pico-86-panel/alternative-bsps.md:20` documents this
|
wiring exists, `luckfox-pico-86-panel/alternative-bsps.md:20` documents this
|
||||||
exact gap for LuckFox's own OpenWrt target (`cortexa7.mk` ships `kmod-rknpu-rockchip`
|
exact gap for LuckFox's own OpenWrt target (`cortexa7.mk` ships `kmod-rknpu-rockchip`
|
||||||
only, no `kmod-aic8800`). Confirms there is no ready-made newer-kernel package
|
only, no `kmod-aic8800`). Confirms there is no ready-made newer-kernel package
|
||||||
to pull instead of porting.
|
to pull instead of porting.
|
||||||
- **plan44's OpenWrt RV1106 fork** (`flare-edge/research/plan44-openwrt/`,
|
- **plan44's OpenWrt RV1106 fork** (`flare-edge/research/plan44-openwrt/`,
|
||||||
Linux 6.6, 152 RV1106 patches): checked directly — **contains no aic8800
|
Linux 6.6, 152 RV1106 patches): checked directly, **contains no aic8800
|
||||||
code at all** (`find ... -ipath '*aic8800*'` empty). Confirms
|
code at all** (`find ... -ipath '*aic8800*'` empty). Confirms
|
||||||
`bringup.md`'s "AIC8800 wifi (plan44 has none; ours)" and the "no plan44
|
`bringup.md`'s "AIC8800 wifi (plan44 has none; ours)" and the "no plan44
|
||||||
code" decision doesn't cost us anything here — there's nothing to take.
|
code" decision doesn't cost us anything here: there's nothing to take.
|
||||||
|
|
||||||
## 3. 5.10 → 6.18 API-delta checklist
|
## 3. 5.10 -> 6.18 API-delta checklist
|
||||||
|
|
||||||
Grounded in two passes: (a) direct kernel.org/bootlin/LWN research on 5.10→6.18
|
Grounded in two passes: (a) direct kernel.org/bootlin/LWN research on 5.10->6.18
|
||||||
API history, (b) `grep` evidence from the actual vendor source (file:line cited
|
API history, (b) `grep` evidence from the actual vendor source (file:line cited
|
||||||
below) so this is a checklist against *our* code, not a generic survey.
|
below) so this is a checklist against *our* code, not a generic survey.
|
||||||
|
|
||||||
### 3.1 Timers — confirmed hard breaks, exact call sites found
|
### 3.1 Timers: confirmed hard breaks, exact call sites found
|
||||||
`del_timer_sync`/`del_timer` → **`timer_delete_sync`/`timer_delete`**: renamed
|
`del_timer_sync`/`del_timer` -> **`timer_delete_sync`/`timer_delete`**: renamed
|
||||||
in `9b13df3fb64e` (landed v6.2-rc1) as a **compat-wrapped** rename; **the
|
in `9b13df3fb64e` (landed v6.2-rc1) as a **compat-wrapped** rename; **the
|
||||||
compat wrapper was removed in v6.15-rc1**, so by 6.18 the old names **do not
|
compat wrapper was removed in v6.15-rc1**, so by 6.18 the old names **do not
|
||||||
exist**. `from_timer()` → **`timer_container_of()`**: a real mainline rename
|
exist**. `from_timer()` -> **`timer_container_of()`**: a real mainline rename
|
||||||
(~6.14–6.15 treewide timer-API cleanup; exact tag unconfirmed, verify against
|
(~6.14-6.15 treewide timer-API cleanup; exact tag unconfirmed, verify against
|
||||||
the actual 6.18.46 headers already unpacked at
|
the actual 6.18.46 headers already unpacked at
|
||||||
`flare-edge/research/linux-6.18.46/include/linux/timer.h`). Every call site in
|
`flare-edge/research/linux-6.18.46/include/linux/timer.h`). Every call site in
|
||||||
the driver, found by direct grep (not estimated):
|
the driver, found by direct grep (not estimated):
|
||||||
@@ -209,11 +209,11 @@ the driver, found by direct grep (not estimated):
|
|||||||
| `from_timer()` | `aic8800_bsp/aicsdio.c:1442`; `aic8800_fdrv/aicwf_sdio.c:279,317,2933`; `aic8800_fdrv/rwnx_main.c:1724`; `aic8800_fdrv/rwnx_rx.c:1751,2027` |
|
| `from_timer()` | `aic8800_bsp/aicsdio.c:1442`; `aic8800_fdrv/aicwf_sdio.c:279,317,2933`; `aic8800_fdrv/rwnx_main.c:1724`; `aic8800_fdrv/rwnx_rx.c:1751,2027` |
|
||||||
| `del_timer_sync()` | `aic8800_bsp/aicsdio.c:1677`; `aic8800_fdrv/aicwf_sdio.c:1048,1054,1297,1303,3152`; `aic8800_fdrv/rwnx_main.c:2181`; `aic8800_fdrv/rwnx_rx.c:1501,1526` |
|
| `del_timer_sync()` | `aic8800_bsp/aicsdio.c:1677`; `aic8800_fdrv/aicwf_sdio.c:1048,1054,1297,1303,3152`; `aic8800_fdrv/rwnx_main.c:2181`; `aic8800_fdrv/rwnx_rx.c:1501,1526` |
|
||||||
| `del_timer()` (non-sync) | `aic8800_fdrv/aicwf_tcp_ack.c:108,367,401,462`; `aic8800_fdrv/rwnx_rx.c:1921,2607`; `aic8800_btlpm/aic8800_btlpm.c:580,603,951`; `aic8800_btlpm/lpm.c:558,581,935` |
|
| `del_timer()` (non-sync) | `aic8800_fdrv/aicwf_tcp_ack.c:108,367,401,462`; `aic8800_fdrv/rwnx_rx.c:1921,2607`; `aic8800_btlpm/aic8800_btlpm.c:580,603,951`; `aic8800_btlpm/lpm.c:558,581,935` |
|
||||||
| `setup_timer()` (pre-4.14 dead branch) | `aic8800_fdrv/aicwf_tcp_ack.c:84` — already `#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)` guarded against a live `timer_setup()` branch; **delete the dead `#if` branch**, don't port it |
|
| `setup_timer()` (pre-4.14 dead branch) | `aic8800_fdrv/aicwf_tcp_ack.c:84`: already `#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)` guarded against a live `timer_setup()` branch; **delete the dead `#if` branch**, don't port it |
|
||||||
| `timer_setup()` calls (unaffected, just listed for completeness) | `aicsdio.c:2059`; `aicwf_sdio.c:3564,3589,3590`; `rwnx_main.c:1841,6118`; `rwnx_rx.c:1433,2510`; `aicwf_tcp_ack.c:87`; both btlpm files:1104/1054 |
|
| `timer_setup()` calls (unaffected, just listed for completeness) | `aicsdio.c:2059`; `aicwf_sdio.c:3564,3589,3590`; `rwnx_main.c:1841,6118`; `rwnx_rx.c:1433,2510`; `aicwf_tcp_ack.c:87`; both btlpm files:1104/1054 |
|
||||||
|
|
||||||
Mechanical fix: sed-rename `from_timer`→`timer_container_of`, `del_timer_sync`→
|
Mechanical fix: sed-rename `from_timer`->`timer_container_of`, `del_timer_sync`->
|
||||||
`timer_delete_sync`, `del_timer`→`timer_delete` across the ~20 call sites above.
|
`timer_delete_sync`, `del_timer`->`timer_delete` across the ~20 call sites above.
|
||||||
`timer_setup()` itself is unchanged.
|
`timer_setup()` itself is unchanged.
|
||||||
|
|
||||||
### 3.2 netdev
|
### 3.2 netdev
|
||||||
@@ -221,25 +221,25 @@ Mechanical fix: sed-rename `from_timer`→`timer_container_of`, `del_timer_sync`
|
|||||||
around 5.14/5.15)**: **already handled** by the vendor. `rwnx_main.c:1457-1474`
|
around 5.14/5.15)**: **already handled** by the vendor. `rwnx_main.c:1457-1474`
|
||||||
already has `#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0)` /
|
already has `#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0)` /
|
||||||
`#else .ndo_siocdevprivate = rwnx_do_ioctl,` for both `rwnx_netdev_ops` and
|
`#else .ndo_siocdevprivate = rwnx_do_ioctl,` for both `rwnx_netdev_ops` and
|
||||||
`rwnx_netdev_monitor_ops`. Zero work needed — the guard picks the 6.18-correct
|
`rwnx_netdev_monitor_ops`. Zero work needed: the guard picks the 6.18-correct
|
||||||
member automatically. **Verify only** that `rwnx_do_ioctl`'s body (line 1368)
|
member automatically. **Verify only** that `rwnx_do_ioctl`'s body (line 1368)
|
||||||
still compiles against the `ndo_siocdevprivate` signature
|
still compiles against the `ndo_siocdevprivate` signature
|
||||||
(`int (*)(struct net_device *, struct ifreq *, void __user *, int)` vs the
|
(`int (*)(struct net_device *, struct ifreq *, void __user *, int)` vs the
|
||||||
old ioctl signature) — check on the actual 6.18.46 headers.
|
old ioctl signature), check on the actual 6.18.46 headers.
|
||||||
- **`netif_rx_ni()` removed (5.18, merged into plain `netif_rx()`, safe from any
|
- **`netif_rx_ni()` removed (5.18, merged into plain `netif_rx()`, safe from any
|
||||||
context)**: three call sites, **not** version-guarded: `rwnx_rx.c:404,598,1651`.
|
context)**: three call sites, **not** version-guarded: `rwnx_rx.c:404,598,1651`.
|
||||||
Mechanical fix: `netif_rx_ni(rx_skb)` → `netif_rx(rx_skb)`.
|
Mechanical fix: `netif_rx_ni(rx_skb)` -> `netif_rx(rx_skb)`.
|
||||||
- **`netif_napi_add()` weight arg dropped (~6.1)**: **not used** anywhere in
|
- **`netif_napi_add()` weight arg dropped (~6.1)**: **not used** anywhere in
|
||||||
this driver (checked, no `netif_napi_add`/NAPI in the tree — the driver does
|
this driver (checked, no `netif_napi_add`/NAPI in the tree, the driver does
|
||||||
its own kthread-based RX processing, not NAPI polling). No action.
|
its own kthread-based RX processing, not NAPI polling). No action.
|
||||||
|
|
||||||
### 3.3 cfg80211 — the hard, non-mechanical part
|
### 3.3 cfg80211: the hard, non-mechanical part
|
||||||
**Verified directly against `flare-edge/research/linux-6.18.46/include/net/cfg80211.h`**
|
**Verified directly against `flare-edge/research/linux-6.18.46/include/net/cfg80211.h`**
|
||||||
(the actual target tree, not a guess from release notes):
|
(the actual target tree, not a guess from release notes):
|
||||||
- **`cfg80211_connect_result()`**: **confirmed unchanged and safe.** In 6.18.46
|
- **`cfg80211_connect_result()`**: **confirmed unchanged and safe.** In 6.18.46
|
||||||
it's a `static inline` that just forwards to `cfg80211_connect_bss()`
|
it's a `static inline` that just forwards to `cfg80211_connect_bss()`
|
||||||
(`cfg80211.h:8654-8661`), with the exact same 8-argument signature the driver
|
(`cfg80211.h:8654-8661`), with the exact same 8-argument signature the driver
|
||||||
already calls at `rwnx_msg_rx.c:958`. `cfg80211_roamed()` — also **confirmed
|
already calls at `rwnx_msg_rx.c:958`. `cfg80211_roamed()`, also **confirmed
|
||||||
unchanged**: 6.18.46 signature is
|
unchanged**: 6.18.46 signature is
|
||||||
`cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, gfp_t gfp)`
|
`cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, gfp_t gfp)`
|
||||||
(`cfg80211.h:8748`), matching the driver's call at `rwnx_msg_rx.c:1006,1010`
|
(`cfg80211.h:8748`), matching the driver's call at `rwnx_msg_rx.c:1006,1010`
|
||||||
@@ -248,98 +248,98 @@ Mechanical fix: sed-rename `from_timer`→`timer_container_of`, `del_timer_sync`
|
|||||||
`cfg80211_connect_bss()` directly is optional cleanup, not a requirement.
|
`cfg80211_connect_bss()` directly is optional cleanup, not a requirement.
|
||||||
- **`wiphy_new()`/`wiphy_new_nm()`**: **confirmed unchanged.**
|
- **`wiphy_new()`/`wiphy_new_nm()`**: **confirmed unchanged.**
|
||||||
`wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)`
|
`wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)`
|
||||||
(`cfg80211.h:6250-6253`, inline wrapper over `wiphy_new_nm(ops, sizeof_priv, NULL)`)
|
(`cfg80211.h:6250-6253`, inline wrapper over `wiphy_new_nm(ops, sizeof_priv, NULL)`):
|
||||||
— matches `rwnx_main.c:5732`'s `wiphy_new(&rwnx_cfg80211_ops, sizeof(struct rwnx_hw))`
|
matches `rwnx_main.c:5732`'s `wiphy_new(&rwnx_cfg80211_ops, sizeof(struct rwnx_hw))`
|
||||||
exactly. No signature-driven work needed.
|
exactly. No signature-driven work needed.
|
||||||
- **Wiphy locking overhaul — real, confirmed present, and now precisely scoped
|
- **Wiphy locking overhaul, real, confirmed present, and now precisely scoped
|
||||||
(not a guess).** `cfg80211.h` (~6266, 6325-6362) confirms `wiphy_lock()`/
|
(not a guess).** `cfg80211.h` (~6266, 6325-6362) confirms `wiphy_lock()`/
|
||||||
`wiphy_unlock()`/`lockdep_assert_wiphy()` and a `struct wiphy_work` deferred-work
|
`wiphy_unlock()`/`lockdep_assert_wiphy()` and a `struct wiphy_work` deferred-work
|
||||||
mechanism all exist in 6.18.46, and — the load-bearing sentence, directly
|
mechanism all exist in 6.18.46, and (the decisive sentence, directly
|
||||||
from the `wiphy_lock()` doc comment — **"When cfg80211 ops are called, the
|
from the `wiphy_lock()` doc comment) **"When cfg80211 ops are called, the
|
||||||
wiphy is already locked."** That means:
|
wiphy is already locked."** That means:
|
||||||
- The driver's `cfg80211_ops` callbacks themselves (`rwnx_cfg80211_scan`,
|
- The driver's `cfg80211_ops` callbacks themselves (`rwnx_cfg80211_scan`,
|
||||||
`_connect`, `_disconnect`, `_add_key`, `_mgmt_tx`, etc., `rwnx_main.c:5365-5382`)
|
`_connect`, `_disconnect`, `_add_key`, `_mgmt_tx`, etc., `rwnx_main.c:5365-5382`)
|
||||||
need **no new locking** — cfg80211 core now takes the wiphy mutex before
|
need **no new locking**: cfg80211 core now takes the wiphy mutex before
|
||||||
calling into any of them, where 5.10-era cfg80211 relied on the caller
|
calling into any of them, where 5.10-era cfg80211 relied on the caller
|
||||||
holding RTNL instead.
|
holding RTNL instead.
|
||||||
- The real risk is the **other direction**: this driver calls
|
- The real risk is the **other direction**: this driver calls
|
||||||
`cfg80211_scan_done()` / `cfg80211_connect_result()` / `cfg80211_roamed()`
|
`cfg80211_scan_done()` / `cfg80211_connect_result()` / `cfg80211_roamed()`
|
||||||
from **firmware-event handling, not from inside an ops callback** —
|
from **firmware-event handling, not from inside an ops callback**,
|
||||||
`rwnx_msg_rx.c` (async, driven by SDIO RX) and `rwnx_main.c:1212,2082,2168`
|
`rwnx_msg_rx.c` (async, driven by SDIO RX) and `rwnx_main.c:1212,2082,2168`
|
||||||
(also async paths, not the ops entry points). Multiple `cfg80211.h` doc
|
(also async paths, not the ops entry points). Multiple `cfg80211.h` doc
|
||||||
comments for adjacent notification APIs state "the caller must hold ...
|
comments for adjacent notification APIs state "the caller must hold ...
|
||||||
wiphy mutex" (e.g. `cfg80211.h:9428`: "Caller must hold wiphy mutex,
|
wiphy mutex" (e.g. `cfg80211.h:9428`: "Caller must hold wiphy mutex,
|
||||||
therefore must only be called from sleepable context") — the pattern that
|
therefore must only be called from sleepable context"), the pattern that
|
||||||
replaced the old "hold RTNL" requirement. **Concretely: every
|
replaced the old "hold RTNL" requirement. **Concretely: every
|
||||||
`cfg80211_*` notification call reached from `rwnx_msg_rx.c` /
|
`cfg80211_*` notification call reached from `rwnx_msg_rx.c` /
|
||||||
the async paths in `rwnx_main.c` needs `wiphy_lock(wiphy)` /
|
the async paths in `rwnx_main.c` needs `wiphy_lock(wiphy)` /
|
||||||
`wiphy_unlock(wiphy)` wrapped around it that wasn't there before** (5.10
|
`wiphy_unlock(wiphy)` wrapped around it that wasn't there before** (5.10
|
||||||
only needed `rtnl_lock()`, which this driver's 3 existing
|
only needed `rtnl_lock()`, which this driver's 3 existing
|
||||||
`rtnl_lock()`/`rtnl_unlock()` sites at `rwnx_main.c:5429/5433,
|
`rtnl_lock()`/`rtnl_unlock()` sites at `rwnx_main.c:5429/5433,
|
||||||
6063/6075, 6097/6102` already show it knows how to take defensively — the
|
6063/6075, 6097/6102` already show it knows how to take defensively: the
|
||||||
fix is adding the wiphy-mutex equivalent at the async notification sites,
|
fix is adding the wiphy-mutex equivalent at the async notification sites,
|
||||||
not at those 3 sites, which are netdev-registration paths and likely stay
|
not at those 3 sites, which are netdev-registration paths and likely stay
|
||||||
RTNL-only).
|
RTNL-only).
|
||||||
- This is still the single highest-effort item in the port — not because the
|
- This is still the single highest-effort item in the port, not because the
|
||||||
contract is unknown (it's now confirmed above), but because applying it
|
contract is unknown (it's now confirmed above), but because applying it
|
||||||
correctly means auditing every async→cfg80211 call site in `rwnx_msg_rx.c`
|
correctly means auditing every async->cfg80211 call site in `rwnx_msg_rx.c`
|
||||||
and the async branches of `rwnx_main.c` (1212, 2082, 2168, 2445-2645) one
|
and the async branches of `rwnx_main.c` (1212, 2082, 2168, 2445-2645) one
|
||||||
by one, and because a wrong lock order (wiphy mutex vs. RTNL vs. this
|
by one, and because a wrong lock order (wiphy mutex vs. RTNL vs. this
|
||||||
driver's own internal locks/semaphores) produces lockdep splats or
|
driver's own internal locks/semaphores) produces lockdep splats or
|
||||||
deadlocks that only show up under real traffic, not at compile time. Cross-
|
deadlocks that only show up under real traffic, not at compile time. Cross-
|
||||||
check each hunk against how `radxa-pkg/aic8800` (§0) handled the same
|
check each hunk against how `radxa-pkg/aic8800` (section 0) handled the same
|
||||||
transition on its 6.12/6.13 port — it hit this exact wall first.
|
transition on its 6.12/6.13 port: it hit this exact wall first.
|
||||||
|
|
||||||
### 3.4 SDIO/MMC
|
### 3.4 SDIO/MMC
|
||||||
No breaking changes found in `sdio_driver`, `sdio_claim_host`/`release_host`,
|
No breaking changes found in `sdio_driver`, `sdio_claim_host`/`release_host`,
|
||||||
`sdio_readb`/`writesb`, `sdio_set_block_size` between 5.10 and 6.18 (low
|
`sdio_readb`/`writesb`, `sdio_set_block_size` between 5.10 and 6.18 (low
|
||||||
research depth on this axis — treat as low-risk, smoke-test rather than
|
research depth on this axis, treat as low-risk, smoke-test rather than
|
||||||
line-audit). The mainline `dw_mmc`/`dw_mmc-rockchip` host driver is already
|
line-audit). The mainline `dw_mmc`/`dw_mmc-rockchip` host driver is already
|
||||||
proven on 6.18 for eMMC (`../DRIVER-PARITY.md`: `dw_mmc (eMMC) | mainline | [x] M3`)
|
proven on 6.18 for eMMC (`../DRIVER-PARITY.md`: `dw_mmc (eMMC) | mainline | [x] M3`)
|
||||||
and `CONFIG_MMC_DW_ROCKCHIP=y` is already in the live `.config` — the SDIO
|
and `CONFIG_MMC_DW_ROCKCHIP=y` is already in the live `.config`, the SDIO
|
||||||
*controller* side of this port is de-risked; only the AIC8800 *card driver*
|
*controller* side of this port is de-risked; only the AIC8800 *card driver*
|
||||||
above the `sdmmc` bus is new work.
|
above the `sdmmc` bus is new work.
|
||||||
|
|
||||||
### 3.5 proc_ops
|
### 3.5 proc_ops
|
||||||
`file_operations`→`proc_ops` for procfs landed in **5.6** — already true at the
|
`file_operations`->`proc_ops` for procfs landed in **5.6**: already true at the
|
||||||
5.10 baseline this driver was written against, and no further proc_ops changes
|
5.10 baseline this driver was written against, and no further proc_ops changes
|
||||||
were found 5.10→6.18. The three files using `proc_ops`/`proc_create`
|
were found 5.10->6.18. The three files using `proc_ops`/`proc_create`
|
||||||
(`aicwf_sdio.c`, `aic8800_btlpm.c`, `lpm.c`) should need no change here.
|
(`aicwf_sdio.c`, `aic8800_btlpm.c`, `lpm.c`) should need no change here.
|
||||||
|
|
||||||
### 3.6 DMA
|
### 3.6 DMA
|
||||||
No breaking coherent/streaming DMA API changes found 5.10→6.18 relevant to this
|
No breaking coherent/streaming DMA API changes found 5.10->6.18 relevant to this
|
||||||
driver; SDIO drivers ride MMC-core DMA rather than calling
|
driver; SDIO drivers ride MMC-core DMA rather than calling
|
||||||
`dma_alloc_coherent`/`dma_map_single` directly for the card-side data path.
|
`dma_alloc_coherent`/`dma_map_single` directly for the card-side data path.
|
||||||
Low risk, not independently line-audited — flag if the build surfaces anything.
|
Low risk, not independently line-audited: flag if the build surfaces anything.
|
||||||
|
|
||||||
### 3.7 Vendor/Rockchip-only helpers — real mainline gap, not a version delta
|
### 3.7 Vendor/Rockchip-only helpers: real mainline gap, not a version delta
|
||||||
`rockchip_wifi_power()` / `rockchip_wifi_set_carddetect()` (declared in
|
`rockchip_wifi_power()` / `rockchip_wifi_set_carddetect()` (declared in
|
||||||
`include/linux/rfkill-wlan.h`, implemented in `net/rfkill/rfkill-wlan.c` in the
|
`include/linux/rfkill-wlan.h`, implemented in `net/rfkill/rfkill-wlan.c` in the
|
||||||
**vendor 5.10 tree only** — this is a Rockchip-BSP-vendor subsystem, not
|
**vendor 5.10 tree only**: this is a Rockchip-BSP-vendor subsystem, not
|
||||||
mainline Linux, and does not exist in `flare-edge/research/linux-6.18.46/`).
|
mainline Linux, and does not exist in `flare-edge/research/linux-6.18.46/`).
|
||||||
Call sites: `aic8800_bsp/aicsdio.c:515,517,556,580` and
|
Call sites: `aic8800_bsp/aicsdio.c:515,517,556,580` and
|
||||||
`aic8800_fdrv/aicwf_sdio.c:1260,1262,1329,1331` (all under
|
`aic8800_fdrv/aicwf_sdio.c:1260,1262,1329,1331` (all under
|
||||||
`#ifdef CONFIG_PLATFORM_ROCKCHIP`, which is true for this board). **This is not
|
`#ifdef CONFIG_PLATFORM_ROCKCHIP`, which is true for this board). **This is not
|
||||||
a rename — there is nothing to rename to.** Recommended fix: **stub these
|
a rename: there is nothing to rename to.** Recommended fix: **stub these
|
||||||
calls out entirely** rather than port `rfkill-wlan.c`. The DT already declares
|
calls out entirely** rather than port `rfkill-wlan.c`. The DT already declares
|
||||||
a standard mainline `mmc-pwrseq-simple` (`sdio_pwrseq`, §1) bound via
|
a standard mainline `mmc-pwrseq-simple` (`sdio_pwrseq`, section 1) bound via
|
||||||
`mmc-pwrseq = <&sdio_pwrseq>`, which the mainline MMC core already drives at
|
`mmc-pwrseq = <&sdio_pwrseq>`, which the mainline MMC core already drives at
|
||||||
bus-scan/power-up time through `drivers/mmc/core/pwrseq_simple.c` — a real
|
bus-scan/power-up time through `drivers/mmc/core/pwrseq_simple.c`, a real
|
||||||
mainline mechanism doing the same job (GPIO power/reset sequencing) these
|
mainline mechanism doing the same job (GPIO power/reset sequencing) these
|
||||||
vendor calls were a pre-DT-pwrseq-era stand-in for. `rockchip_wifi_set_carddetect()`
|
vendor calls were a pre-DT-pwrseq-era stand-in for. `rockchip_wifi_set_carddetect()`
|
||||||
is likewise redundant for a `non-removable` MMC device, which mainline already
|
is likewise redundant for a `non-removable` MMC device, which mainline already
|
||||||
auto-rescans. Treat every call site as `#if 0`/deleted, not ported.
|
auto-rescans. Treat every call site as `#if 0`/deleted, not ported.
|
||||||
`get_cpu_version`/`rockchip_soc_id`-style helpers: **not called anywhere** in
|
`get_cpu_version`/`rockchip_soc_id`-style helpers: **not called anywhere** in
|
||||||
this driver (checked, no hits) — the task's assumption that this driver calls
|
this driver (checked, no hits), the task's assumption that this driver calls
|
||||||
such a helper does not hold; no action needed. `module_param`, `kthread_run`/
|
such a helper does not hold; no action needed. `module_param`, `kthread_run`/
|
||||||
`kthread_should_stop`: stable, unaffected — used extensively (RX/TX kthreads in
|
`kthread_should_stop`: stable, unaffected, used extensively (RX/TX kthreads in
|
||||||
`aicwf_sdio.c`), no changes needed.
|
`aicwf_sdio.c`), no changes needed.
|
||||||
|
|
||||||
### 3.8 Firmware loading
|
### 3.8 Firmware loading
|
||||||
`request_firmware`/`request_firmware_nowait`/`release_firmware`: stable
|
`request_firmware`/`request_firmware_nowait`/`release_firmware`: stable
|
||||||
5.10→6.18, no signature changes found. Note this driver's actual firmware load
|
5.10->6.18, no signature changes found. Note this driver's actual firmware load
|
||||||
path is `AIC_FW_PATH`-relative direct file read via its own loader
|
path is `AIC_FW_PATH`-relative direct file read via its own loader
|
||||||
(`aic_load_fw`/`CONFIG_USE_FW_REQUEST` is `?= n` in the Makefile — the vendor
|
(`aic_load_fw`/`CONFIG_USE_FW_REQUEST` is `?= n` in the Makefile, the vendor
|
||||||
driver does **not** use the standard `request_firmware()` API by default, it
|
driver does **not** use the standard `request_firmware()` API by default, it
|
||||||
reads firmware files from a configurable path itself). Confirm this stays true
|
reads firmware files from a configurable path itself). Confirm this stays true
|
||||||
after the port; it's a deliberate vendor choice, not a version-driven gap.
|
after the port; it's a deliberate vendor choice, not a version-driven gap.
|
||||||
@@ -347,31 +347,31 @@ after the port; it's a deliberate vendor choice, not a version-driven gap.
|
|||||||
## 4. File / config / DT port plan
|
## 4. File / config / DT port plan
|
||||||
|
|
||||||
### 4.1 Files to bring into the 6.18 tree
|
### 4.1 Files to bring into the 6.18 tree
|
||||||
Copy `aic8800dc/{aic8800_bsp,aic8800_fdrv,aic8800_btlpm}/*.{c,h}` (source only —
|
Copy `aic8800dc/{aic8800_bsp,aic8800_fdrv,aic8800_btlpm}/*.{c,h}` (source only:
|
||||||
exclude every generated `.o`/`.ko`/`.mod.*`/`.cmd`/`Module.symvers`/
|
exclude every generated `.o`/`.ko`/`.mod.*`/`.cmd`/`Module.symvers`/
|
||||||
`modules.order` artifact already sitting in the vendor tree from prior
|
`modules.order` artifact already sitting in the vendor tree from prior
|
||||||
out-of-tree builds) into the 6.18 tree at:
|
out-of-tree builds) into the 6.18 tree at:
|
||||||
```
|
```
|
||||||
flare-edge/research/linux-6.18.46/drivers/net/wireless/aic8800/
|
flare-edge/research/linux-6.18.46/drivers/net/wireless/aic8800/
|
||||||
Kconfig # new: top-level "source" wrapper, see 4.2
|
Kconfig # new: top-level "source" wrapper, see 4.2
|
||||||
Makefile # obj-y for the 3 subdirs, preserving bsp→fdrv→btlpm link order
|
Makefile # obj-y for the 3 subdirs, preserving bsp->fdrv->btlpm link order
|
||||||
aic8800_bsp/ (from aic8800dc/aic8800_bsp/)
|
aic8800_bsp/ (from aic8800dc/aic8800_bsp/)
|
||||||
aic8800_fdrv/ (from aic8800dc/aic8800_fdrv/, minus usb_host.c/rwnx_pci.*/rwnx_mesh.* — dead
|
aic8800_fdrv/ (from aic8800dc/aic8800_fdrv/, minus usb_host.c/rwnx_pci.*/rwnx_mesh.*: dead
|
||||||
code on this board's SDIO-only, non-mesh config; keep out unless a build
|
code on this board's SDIO-only, non-mesh config; keep out unless a build
|
||||||
error proves them referenced elsewhere)
|
error proves them referenced elsewhere)
|
||||||
aic8800_btlpm/ (from aic8800dc/aic8800_btlpm/)
|
aic8800_btlpm/ (from aic8800dc/aic8800_btlpm/)
|
||||||
```
|
```
|
||||||
This is exactly the path the vendor's own `Kconfig` already assumes
|
This is exactly the path the vendor's own `Kconfig` already assumes
|
||||||
(`source "drivers/net/wireless/aic8800/aic8800_fdrv/Kconfig"`, §1) — no path
|
(`source "drivers/net/wireless/aic8800/aic8800_fdrv/Kconfig"`, section 1), no path
|
||||||
rewriting needed inside the sub-Kconfigs. Firmware blobs (§5) go to
|
rewriting needed inside the sub-Kconfigs. Firmware blobs (section 5) go to
|
||||||
`aic8800dc_fw/` under the rootfs firmware path, not into the kernel tree.
|
`aic8800dc_fw/` under the rootfs firmware path, not into the kernel tree.
|
||||||
|
|
||||||
Apply, in this order, on top of the copied source: the `0001` queue_sz clamp
|
Apply, in this order, on top of the copied source: the `0001` queue_sz clamp
|
||||||
patch, and the Tier-1 SDIO-wakeup-sleep patch (currently only tracked under
|
patch, and the Tier-1 SDIO-wakeup-sleep patch (currently only tracked under
|
||||||
`flare-edge/future-features-2/sdk-patches/wifi/patches/0002-aic8800dc-sdio-wakeup-sleep-not-spin.patch`
|
`flare-edge/future-features-2/sdk-patches/wifi/patches/0002-aic8800dc-sdio-wakeup-sleep-not-spin.patch`:
|
||||||
— pull it from there; it is *not* in this branch's own `sdk-patches/wifi/`, a
|
pull it from there; it is *not* in this branch's own `sdk-patches/wifi/`, a
|
||||||
separate housekeeping gap worth closing regardless of this port). Then apply
|
separate housekeeping gap worth closing regardless of this port). Then apply
|
||||||
the API-delta fixes from §3.
|
the API-delta fixes from section 3.
|
||||||
|
|
||||||
### 4.2 Kconfig wiring
|
### 4.2 Kconfig wiring
|
||||||
Add one line to `flare-edge/research/linux-6.18.46/drivers/net/wireless/Kconfig`
|
Add one line to `flare-edge/research/linux-6.18.46/drivers/net/wireless/Kconfig`
|
||||||
@@ -380,13 +380,13 @@ list, `Kconfig:22-38`):
|
|||||||
```
|
```
|
||||||
source "drivers/net/wireless/aic8800/Kconfig"
|
source "drivers/net/wireless/aic8800/Kconfig"
|
||||||
```
|
```
|
||||||
New `drivers/net/wireless/aic8800/Kconfig` — carry the vendor's `aic8800dc/Kconfig`
|
New `drivers/net/wireless/aic8800/Kconfig`, carry the vendor's `aic8800dc/Kconfig`
|
||||||
content forward nearly verbatim (it already has the right shape: an
|
content forward nearly verbatim (it already has the right shape: an
|
||||||
`AIC_WLAN_SUPPORT` bool gate, an `AIC_FW_PATH` string default, and `source`
|
`AIC_WLAN_SUPPORT` bool gate, an `AIC_FW_PATH` string default, and `source`
|
||||||
lines for the fdrv/btlpm sub-Kconfigs) but change the two `tristate` symbols in
|
lines for the fdrv/btlpm sub-Kconfigs) but change the two `tristate` symbols in
|
||||||
`aic8800_fdrv/Kconfig` (`AIC8800_WLAN_SUPPORT`) and `aic8800_btlpm/Kconfig`
|
`aic8800_fdrv/Kconfig` (`AIC8800_WLAN_SUPPORT`) and `aic8800_btlpm/Kconfig`
|
||||||
(`AIC8800_BTLPM_SUPPORT`) to **default `y`**, and set `AIC_WLAN_SUPPORT`
|
(`AIC8800_BTLPM_SUPPORT`) to **default `y`**, and set `AIC_WLAN_SUPPORT`
|
||||||
default `y` — per the task's requirement, everything built in, not modular,
|
default `y`, per the task's requirement, everything built in, not modular,
|
||||||
since the 6.18 rootfs has no working module-loading pipeline yet
|
since the 6.18 rootfs has no working module-loading pipeline yet
|
||||||
(`../PORT-STATUS.md` M3 note: the old 5.10 `.ko`s already fail on 6.18 from
|
(`../PORT-STATUS.md` M3 note: the old 5.10 `.ko`s already fail on 6.18 from
|
||||||
vermagic mismatch, and a rebuilt 6.18 module tree for Buildroot doesn't exist
|
vermagic mismatch, and a rebuilt 6.18 module tree for Buildroot doesn't exist
|
||||||
@@ -395,7 +395,7 @@ yet either).
|
|||||||
### 4.3 Config symbols (`=y`, built-in)
|
### 4.3 Config symbols (`=y`, built-in)
|
||||||
```
|
```
|
||||||
CONFIG_WIRELESS=y # already =y in the live 6.18 .config
|
CONFIG_WIRELESS=y # already =y in the live 6.18 .config
|
||||||
CONFIG_CFG80211=y # already =y — flipped in "batch2" per ../DRIVER-PARITY.md
|
CONFIG_CFG80211=y # already =y, flipped in "batch2" per ../DRIVER-PARITY.md
|
||||||
CONFIG_WLAN=y # already =y
|
CONFIG_WLAN=y # already =y
|
||||||
CONFIG_MMC=y # already =y
|
CONFIG_MMC=y # already =y
|
||||||
CONFIG_MMC_DW_ROCKCHIP=y # already =y
|
CONFIG_MMC_DW_ROCKCHIP=y # already =y
|
||||||
@@ -403,67 +403,67 @@ CONFIG_RFKILL=y # already =y
|
|||||||
CONFIG_AIC_WLAN_SUPPORT=y # new
|
CONFIG_AIC_WLAN_SUPPORT=y # new
|
||||||
CONFIG_AIC8800_WLAN_SUPPORT=y # new
|
CONFIG_AIC8800_WLAN_SUPPORT=y # new
|
||||||
CONFIG_AIC8800_BTLPM_SUPPORT=y # new
|
CONFIG_AIC8800_BTLPM_SUPPORT=y # new
|
||||||
CONFIG_BT=y # currently =m in the live .config — flip to =y for the same
|
CONFIG_BT=y # currently =m in the live .config, flip to =y for the same
|
||||||
# no-working-module-pipeline reason as the aic8800 pieces
|
# no-working-module-pipeline reason as the aic8800 pieces
|
||||||
CONFIG_BT_HCIUART=y # currently =m — flip alongside CONFIG_BT
|
CONFIG_BT_HCIUART=y # currently =m, flip alongside CONFIG_BT
|
||||||
CONFIG_BT_HCIUART_H4=y # already =y (H4 is the transport this board's BT actually uses,
|
CONFIG_BT_HCIUART_H4=y # already =y (H4 is the transport this board's BT actually uses,
|
||||||
# per hardware-86-panel.md: UART1/ttyS1, hciattach -s 1500000
|
# per hardware-86-panel.md: UART1/ttyS1, hciattach -s 1500000
|
||||||
# ... any 1500000 flow nosleep)
|
# ... any 1500000 flow nosleep)
|
||||||
CONFIG_CRYPTO_ARC4=y CONFIG_CRYPTO_CTR=y CONFIG_CRYPTO_CCM=y CONFIG_CRYPTO_AES=y # driver's
|
CONFIG_CRYPTO_ARC4=y CONFIG_CRYPTO_CTR=y CONFIG_CRYPTO_CCM=y CONFIG_CRYPTO_AES=y # driver's
|
||||||
# internal key-handling deps, currently "[ ] batch2" in
|
# internal key-handling deps, currently "[ ] batch2" in
|
||||||
# ../DRIVER-PARITY.md — confirm =y, not =m, alongside this work
|
# ../DRIVER-PARITY.md, confirm =y, not =m, alongside this work
|
||||||
```
|
```
|
||||||
Do **not** enable `CONFIG_MAC80211` for this driver — confirmed by source
|
Do **not** enable `CONFIG_MAC80211` for this driver: confirmed by source
|
||||||
inspection (§3.3 note, no `ieee80211_hw`/mac80211 symbol usage anywhere in
|
inspection (section 3.3 note, no `ieee80211_hw`/mac80211 symbol usage anywhere in
|
||||||
`aic8800_fdrv/`) that this is a pure `cfg80211_ops` full-MAC driver; mac80211
|
`aic8800_fdrv/`) that this is a pure `cfg80211_ops` full-MAC driver; mac80211
|
||||||
was only ever needed for the *other* vendor WiFi chips in the shared
|
was only ever needed for the *other* vendor WiFi chips in the shared
|
||||||
`sdk/sysdrv/drv_ko/wifi/` tree (RTL8189FS etc.), not this one. Leaving it out
|
`sdk/sysdrv/drv_ko/wifi/` tree (RTL8189FS etc.), not this one. Leaving it out
|
||||||
avoids pulling in a subsystem this port doesn't need. Do **not** carry
|
avoids pulling in a subsystem this port doesn't need. Do **not** carry
|
||||||
`CONFIG_RFKILL_RK` — confirmed unused (`aic8800_btlpm/rfkill.c` calls the
|
`CONFIG_RFKILL_RK`: confirmed unused (`aic8800_btlpm/rfkill.c` calls the
|
||||||
standard mainline `rfkill_register()`, no Rockchip-specific rfkill hook).
|
standard mainline `rfkill_register()`, no Rockchip-specific rfkill hook).
|
||||||
|
|
||||||
### 4.4 DT changes
|
### 4.4 DT changes
|
||||||
On the board DT that M4/M5 work extends
|
On the board DT that M4/M5 work extends
|
||||||
(`warden-sdk/kernel/rv1106-enablement/dts/rv1106-warden.dts` or its successor —
|
(`warden-sdk/kernel/rv1106-enablement/dts/rv1106-warden.dts` or its successor,
|
||||||
follow the pattern of the M3 `&emmc` addition in `rv1106-warden-m2.dts:126-142`):
|
follow the pattern of the M3 `&emmc` addition in `rv1106-warden-m2.dts:126-142`):
|
||||||
1. Port the `sdmmc0_{clk,cmd,bus4,det}` pinctrl group from
|
1. Port the `sdmmc0_{clk,cmd,bus4,det}` pinctrl group from
|
||||||
`sdk/sysdrv/source/kernel/arch/arm/boot/dts/rv1106-pinctrl.dtsi:700-731`
|
`sdk/sysdrv/source/kernel/arch/arm/boot/dts/rv1106-pinctrl.dtsi:700-731`
|
||||||
(not yet in the ported tree — M1–M3 only needed the eMMC/uart2/eth pin
|
(not yet in the ported tree: M1-M3 only needed the eMMC/uart2/eth pin
|
||||||
groups).
|
groups).
|
||||||
2. Add the `sdmmc: mmc@ffaa0000` node (clocks `HCLK_SDMMC`/`CCLK_SRC_SDMMC` off
|
2. Add the `sdmmc: mmc@ffaa0000` node (clocks `HCLK_SDMMC`/`CCLK_SRC_SDMMC` off
|
||||||
`&cru`, `SCLK_SDMMC_DRV`/`SCLK_SDMMC_SAMPLE` off `&grf_cru` — the same
|
`&cru`, `SCLK_SDMMC_DRV`/`SCLK_SDMMC_SAMPLE` off `&grf_cru`, the same
|
||||||
`grf_cru` dependency the M2 eMMC fix already established, so no new
|
`grf_cru` dependency the M2 eMMC fix already established, so no new
|
||||||
prerequisite) and the `sdio_pwrseq` node, both transplanted verbatim from
|
prerequisite) and the `sdio_pwrseq` node, both transplanted verbatim from
|
||||||
§1's block (interrupt `GIC_SPI 52`, per `rv1106.dtsi:1403-1412` in the
|
section 1's block (interrupt `GIC_SPI 52`, per `rv1106.dtsi:1403-1412` in the
|
||||||
vendor tree).
|
vendor tree).
|
||||||
3. `status = "okay"` on `&sdmmc`, matching the vendor board DT exactly (§1).
|
3. `status = "okay"` on `&sdmmc`, matching the vendor board DT exactly (section 1).
|
||||||
4. **Do not** touch `&sdio` (mmc@ff9a0000) — leave `disabled`, it's genuinely
|
4. **Do not** touch `&sdio` (mmc@ff9a0000): leave `disabled`, it's genuinely
|
||||||
unused hardware on this board (§1 correction).
|
unused hardware on this board (section 1 correction).
|
||||||
|
|
||||||
### 4.5 Driver init-order note
|
### 4.5 Driver init-order note
|
||||||
The 5.10 module load order (`insmod_wifi.sh`, §1) is
|
The 5.10 module load order (`insmod_wifi.sh`, section 1) is
|
||||||
`aic8800_bsp → (200ms) → aic8800_fdrv → (2s!) → aic8800_btlpm`, with real sleep
|
`aic8800_bsp -> (200ms) -> aic8800_fdrv -> (2s!) -> aic8800_btlpm`, with real sleep
|
||||||
delays between each. Built-in (`=y`), there is no equivalent explicit delay —
|
delays between each. Built-in (`=y`), there is no equivalent explicit delay:
|
||||||
initcall order is controlled by link order (the vendor Makefile's `obj-y` list
|
initcall order is controlled by link order (the vendor Makefile's `obj-y` list
|
||||||
is already `aic8800_btlpm/ aic8800_fdrv/ aic8800_bsp/` in Makefile-declaration
|
is already `aic8800_btlpm/ aic8800_fdrv/ aic8800_bsp/` in Makefile-declaration
|
||||||
order but Kbuild links `obj-y` in Makefile order regardless of which appears
|
order but Kbuild links `obj-y` in Makefile order regardless of which appears
|
||||||
first in the `ifeq` block — **preserve `aic8800_bsp` before `aic8800_fdrv`
|
first in the `ifeq` block, **preserve `aic8800_bsp` before `aic8800_fdrv`
|
||||||
before `aic8800_btlpm` in the new `drivers/net/wireless/aic8800/Makefile`'s
|
before `aic8800_btlpm` in the new `drivers/net/wireless/aic8800/Makefile`'s
|
||||||
`obj-y` list**, mirroring the working insmod order) and by each subsystem's
|
`obj-y` list**, mirroring the working insmod order) and by each subsystem's
|
||||||
declared `module_init()`/initcall level (all three currently use plain
|
declared `module_init()`/initcall level (all three currently use plain
|
||||||
`module_init()`, which becomes `device_initcall` level when built-in — same
|
`module_init()`, which becomes `device_initcall` level when built-in, same
|
||||||
level for all three, so link order is what decides relative sequencing among
|
level for all three, so link order is what decides relative sequencing among
|
||||||
them). The observed 2-second gap between `aic8800_bsp` and `aic8800_fdrv` on
|
them). The observed 2-second gap between `aic8800_bsp` and `aic8800_fdrv` on
|
||||||
the running 5.10 system is suspicious enough (firmware download + chip
|
the running 5.10 system is suspicious enough (firmware download + chip
|
||||||
bring-up time) that if the built-in probe races ahead of firmware readiness,
|
bring-up time) that if the built-in probe races ahead of firmware readiness,
|
||||||
watch for it specifically during bring-up (§6) — this is a real risk the
|
watch for it specifically during bring-up (section 6): this is a real risk the
|
||||||
static-link change introduces that modular loading didn't have, not just a
|
static-link change introduces that modular loading didn't have, not just a
|
||||||
formality.
|
formality.
|
||||||
|
|
||||||
## 5. Firmware notes
|
## 5. Firmware notes
|
||||||
|
|
||||||
Firmware is not open source (binary blobs, as expected for WiFi/BT RF/PHY
|
Firmware is not open source (binary blobs, as expected for WiFi/BT RF/PHY
|
||||||
patches) but is freely redistributable — no export-control/NDA marking found on
|
patches) but is freely redistributable: no export-control/NDA marking found on
|
||||||
the files themselves or in the vendor tree's licensing. 21 files,
|
the files themselves or in the vendor tree's licensing. 21 files,
|
||||||
`sdk/sysdrv/drv_ko/wifi/aic8800dc/aic8800dc_fw/` (484 KB total):
|
`sdk/sysdrv/drv_ko/wifi/aic8800dc/aic8800dc_fw/` (484 KB total):
|
||||||
```
|
```
|
||||||
@@ -476,25 +476,25 @@ fw_patch_{8800dc_u02,8800dc_u02_ext0,8800dc_u02h}.bin # BT patch firmware
|
|||||||
fw_patch_table_8800dc_{u02,u02h}.bin # BT patch tables
|
fw_patch_table_8800dc_{u02,u02h}.bin # BT patch tables
|
||||||
lmacfw_rf_8800dc.bin # RF test-mode firmware
|
lmacfw_rf_8800dc.bin # RF test-mode firmware
|
||||||
```
|
```
|
||||||
Per `aic_bsp_main.c`'s `fw_8800dc_u02[]` table (§1), the exact subset loaded at
|
Per `aic_bsp_main.c`'s `fw_8800dc_u02[]` table (section 1), the exact subset loaded at
|
||||||
runtime depends on `AICBSP_CPMODE_WORK` vs `_TEST` and on `CONFIG_SDIO_BT`
|
runtime depends on `AICBSP_CPMODE_WORK` vs `_TEST` and on `CONFIG_SDIO_BT`
|
||||||
(=n in the vendor Makefile — WiFi and BT firmware are loaded/attached
|
(=n in the vendor Makefile: WiFi and BT firmware are loaded/attached
|
||||||
separately, not as one combo blob, despite the combo chip). **Firmware load
|
separately, not as one combo blob, despite the combo chip). **Firmware load
|
||||||
path**: `AIC_FW_PATH` Kconfig default is `/oem/usr/ko/aic8800dc_fw` — this is a
|
path**: `AIC_FW_PATH` Kconfig default is `/oem/usr/ko/aic8800dc_fw`, this is a
|
||||||
5.10-era Buildroot-rootfs-layout artifact (the `/oem` partition), not a kernel
|
5.10-era Buildroot-rootfs-layout artifact (the `/oem` partition), not a kernel
|
||||||
concept; on the 6.18 rootfs, point this at wherever WardenOS's 6.18 Buildroot
|
concept; on the 6.18 rootfs, point this at wherever WardenOS's 6.18 Buildroot
|
||||||
userspace places firmware (likely `/lib/firmware/aic8800dc/` if following
|
userspace places firmware (likely `/lib/firmware/aic8800dc/` if following
|
||||||
standard mainline convention, or keep `/oem/usr/ko/aic8800dc_fw` if the 6.18
|
standard mainline convention, or keep `/oem/usr/ko/aic8800dc_fw` if the 6.18
|
||||||
rootfs partition layout is unchanged from 5.10 — confirm against whatever the
|
rootfs partition layout is unchanged from 5.10, confirm against whatever the
|
||||||
M4/M5 rootfs build actually produces, this is a rootfs-layout decision, not a
|
M4/M5 rootfs build actually produces, this is a rootfs-layout decision, not a
|
||||||
kernel one). Not found in `linux-firmware.git` (the mainline firmware
|
kernel one). Not found in `linux-firmware.git` (the mainline firmware
|
||||||
project) — AIC8800 firmware has not been upstreamed there; continue shipping
|
project): AIC8800 firmware has not been upstreamed there; continue shipping
|
||||||
it the way the vendor tree already does (bundled alongside the driver, loaded
|
it the way the vendor tree already does (bundled alongside the driver, loaded
|
||||||
by direct file read per §3.8, not `request_firmware()`).
|
by direct file read per section 3.8, not `request_firmware()`).
|
||||||
|
|
||||||
**Chip variant**: **AIC8800DC**, confirmed authoritative by the board config
|
**Chip variant**: **AIC8800DC**, confirmed authoritative by the board config
|
||||||
(`RK_ENABLE_WIFI_CHIP=AIC8800DC`) and the SDIO device ID match in source
|
(`RK_ENABLE_WIFI_CHIP=AIC8800DC`) and the SDIO device ID match in source
|
||||||
(`aicsdio.c:75,80`: vendor `0xc8a1`, device `0xc08d`) — not the dual-band
|
(`aicsdio.c:75,80`: vendor `0xc8a1`, device `0xc08d`), not the dual-band
|
||||||
AIC8800D80 seen on other LuckFox boards. 2.4 GHz only in practice (confirmed
|
AIC8800D80 seen on other LuckFox boards. 2.4 GHz only in practice (confirmed
|
||||||
on hardware per `wifi-bluetooth-aic8800.md:10`: `iw phy` shows 1 band, 0
|
on hardware per `wifi-bluetooth-aic8800.md:10`: `iw phy` shows 1 band, 0
|
||||||
5 GHz channels, despite AIC8800DC being marketed dual-band-capable elsewhere).
|
5 GHz channels, despite AIC8800DC being marketed dual-band-capable elsewhere).
|
||||||
@@ -504,34 +504,34 @@ is authoritative.
|
|||||||
## 6. Verify steps
|
## 6. Verify steps
|
||||||
|
|
||||||
Follow the existing A/B `_b`-slot hardware-verification loop
|
Follow the existing A/B `_b`-slot hardware-verification loop
|
||||||
(`warden-sdk/kernel/docs/m2-boot-on-c8a3.md`), same method M1–M3 already used:
|
(`warden-sdk/kernel/docs/m2-boot-on-c8a3.md`), same method M1-M3 already used:
|
||||||
1. **Build**: driver compiles clean into the 6.18.46 tree (`make ... modules`
|
1. **Build**: driver compiles clean into the 6.18.46 tree (`make ... modules`
|
||||||
is not the target — it's `=y`, so this is just `make zImage`/whatever M2's
|
is not the target, it's `=y`, so this is just `make zImage`/whatever M2's
|
||||||
`build-m2.sh` wraps, succeeding with the new `drivers/net/wireless/aic8800/`
|
`build-m2.sh` wraps, succeeding with the new `drivers/net/wireless/aic8800/`
|
||||||
objects linked into `vmlinux`/the zImage).
|
objects linked into `vmlinux`/the zImage).
|
||||||
2. **Probe**: boot on `warden-c8a3`, confirm in `dmesg`: the `sdmmc` MMC host
|
2. **Probe**: boot on `warden-c8a3`, confirm in `dmesg`: the `sdmmc` MMC host
|
||||||
binds (`dwmmc_rockchip ffaa0000.mmc: ...`, same pattern M3 already proved
|
binds (`dwmmc_rockchip ffaa0000.mmc: ...`, same pattern M3 already proved
|
||||||
for `ffa90000.mmc`/eMMC), then an SDIO card enumerates on it, then
|
for `ffa90000.mmc`/eMMC), then an SDIO card enumerates on it, then
|
||||||
`aic8800_bsp`'s probe fires (vendor/device ID match, firmware file opens
|
`aic8800_bsp`'s probe fires (vendor/device ID match, firmware file opens
|
||||||
succeed — watch specifically for `AIC_FW_PATH` resolution failures, §5),
|
succeed, watch specifically for `AIC_FW_PATH` resolution failures, section 5),
|
||||||
then `aic8800_fdrv` attaches and **`wlan0` appears** in `ip link`.
|
then `aic8800_fdrv` attaches and **`wlan0` appears** in `ip link`.
|
||||||
3. **cfg80211 sanity**: `iw phy` shows the expected single 2.4 GHz band/14
|
3. **cfg80211 sanity**: `iw phy` shows the expected single 2.4 GHz band/14
|
||||||
channels (matching the known-good 5.10 baseline in
|
channels (matching the known-good 5.10 baseline in
|
||||||
`wifi-bluetooth-aic8800.md:10` — a mismatch here is a signal something in
|
`wifi-bluetooth-aic8800.md:10`, a mismatch here is a signal something in
|
||||||
the cfg80211/wiphy port is wrong, not a chip regression).
|
the cfg80211/wiphy port is wrong, not a chip regression).
|
||||||
4. **Scan**: `iw dev wlan0 scan` returns nearby APs — exercises
|
4. **Scan**: `iw dev wlan0 scan` returns nearby APs, exercises
|
||||||
`rwnx_cfg80211_scan`/`cfg80211_scan_done()` and, indirectly, whether the
|
`rwnx_cfg80211_scan`/`cfg80211_scan_done()` and, indirectly, whether the
|
||||||
wiphy-locking port (§3.3) is functioning rather than deadlocking.
|
wiphy-locking port (section 3.3) is functioning rather than deadlocking.
|
||||||
5. **Connect**: associate to a real AP (`wpa_supplicant`/`wpa_cli`, matching
|
5. **Connect**: associate to a real AP (`wpa_supplicant`/`wpa_cli`, matching
|
||||||
WardenOS's own `wlan0` ownership model — do **not** start `rkwifi_server`,
|
WardenOS's own `wlan0` ownership model, do **not** start `rkwifi_server`,
|
||||||
§1) and confirm `COMPLETED` state plus a DHCP lease — exercises
|
section 1) and confirm `COMPLETED` state plus a DHCP lease, exercises
|
||||||
`rwnx_cfg80211_connect`/`cfg80211_connect_result()`.
|
`rwnx_cfg80211_connect`/`cfg80211_connect_result()`.
|
||||||
6. **Known-regression checks** (carry forward, don't re-discover): confirm the
|
6. **Known-regression checks** (carry forward, don't re-discover): confirm the
|
||||||
two hardening patches (§0/§4.1) are actually effective — no console-flood
|
two hardening patches (section 0/section 4.1) are actually effective, no console-flood
|
||||||
"cmd timed-out" spam under load, and no CPU-pinning busy-spin if the SDIO
|
"cmd timed-out" spam under load, and no CPU-pinning busy-spin if the SDIO
|
||||||
link is stressed (`aicwf_bustx_thr` should sleep, not spin, on a wakeup
|
link is stressed (`aicwf_bustx_thr` should sleep, not spin, on a wakeup
|
||||||
failure). Confirm `iw dev wlan0 set power_save off` still behaves as
|
failure). Confirm `iw dev wlan0 set power_save off` still behaves as
|
||||||
expected (a wall-powered panel, no reason to want power-save —
|
expected (a wall-powered panel, no reason to want power-save:
|
||||||
`wifi-bluetooth-aic8800.md:41`).
|
`wifi-bluetooth-aic8800.md:41`).
|
||||||
7. **BT** (secondary to WiFi but same milestone): confirm `aic8800_btlpm`
|
7. **BT** (secondary to WiFi but same milestone): confirm `aic8800_btlpm`
|
||||||
attaches and `hciattach -s 1500000 /dev/ttyS1 any 1500000 flow nosleep`
|
attaches and `hciattach -s 1500000 /dev/ttyS1 any 1500000 flow nosleep`
|
||||||
@@ -551,12 +551,12 @@ Follow the existing A/B `_b`-slot hardware-verification loop
|
|||||||
`flare-edge/research/linux-6.18.46/.config` (live, post-M3, batch2 CFG80211=y),
|
`flare-edge/research/linux-6.18.46/.config` (live, post-M3, batch2 CFG80211=y),
|
||||||
`flare-edge/research/linux-6.18.46/include/net/cfg80211.h` (directly read to
|
`flare-edge/research/linux-6.18.46/include/net/cfg80211.h` (directly read to
|
||||||
verify `wiphy_new`, `cfg80211_connect_result`, `wiphy_lock`/`lockdep_assert_wiphy`
|
verify `wiphy_new`, `cfg80211_connect_result`, `wiphy_lock`/`lockdep_assert_wiphy`
|
||||||
against the actual target tree, not release notes — §3.3),
|
against the actual target tree, not release notes, section 3.3),
|
||||||
`flare-edge/research/plan44-openwrt/` (checked, no aic8800),
|
`flare-edge/research/plan44-openwrt/` (checked, no aic8800),
|
||||||
`warden-sdk/kernel/rv1106-enablement/{PORT-STATUS.md,DRIVER-PARITY.md,OVERNIGHT-PLAN.md}`,
|
`warden-sdk/kernel/rv1106-enablement/{PORT-STATUS.md,DRIVER-PARITY.md,OVERNIGHT-PLAN.md}`,
|
||||||
`warden-sdk/kernel/docs/{bringup.md,m2-boot-on-c8a3.md}`,
|
`warden-sdk/kernel/docs/{bringup.md,m2-boot-on-c8a3.md}`,
|
||||||
`luckfox-pico-86-panel/{wifi-bluetooth-aic8800.md,hardware-86-panel.md,alternative-bsps.md,sdk-patches.md,boot-chain.md}`.
|
`luckfox-pico-86-panel/{wifi-bluetooth-aic8800.md,hardware-86-panel.md,alternative-bsps.md,sdk-patches.md,boot-chain.md}`.
|
||||||
- Web (via research pass, see §0/§2): github.com/radxa-pkg/aic8800 +
|
- Web (via research pass, see section 0/section 2): github.com/radxa-pkg/aic8800 +
|
||||||
deepwiki.com/radxa-pkg/aic8800; forum.armbian.com topic 50332 ("AIC8800 wifi
|
deepwiki.com/radxa-pkg/aic8800; forum.armbian.com topic 50332 ("AIC8800 wifi
|
||||||
sdio module not working with kernel 6.12+"); kernel.org/patchwork commits
|
sdio module not working with kernel 6.12+"); kernel.org/patchwork commits
|
||||||
`9b13df3fb64e` (timer_delete rename), `2655926aea9b` (netif_rx_ni removal),
|
`9b13df3fb64e` (timer_delete rename), `2655926aea9b` (netif_rx_ni removal),
|
||||||
@@ -564,6 +564,6 @@ Follow the existing A/B `_b`-slot hardware-verification loop
|
|||||||
LKML/lkml.iu.edu mirrors for the timer-rename and wiphy-guard series.
|
LKML/lkml.iu.edu mirrors for the timer-rename and wiphy-guard series.
|
||||||
Patchwork/LWN direct fetches were partially blocked by anti-bot walls during
|
Patchwork/LWN direct fetches were partially blocked by anti-bot walls during
|
||||||
research, so the *exact commit/version* the wiphy-lock migration landed in
|
research, so the *exact commit/version* the wiphy-lock migration landed in
|
||||||
is not pinned precisely — but its **presence and current contract in the
|
is not pinned precisely, but its **presence and current contract in the
|
||||||
actual 6.18.46 tree we're porting to is directly confirmed** (previous
|
actual 6.18.46 tree we're porting to is directly confirmed** (previous
|
||||||
paragraph), which is the fact that actually matters for this port.
|
paragraph), which is the fact that actually matters for this port.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# AIC8800 wifi+BT port — M5 progress (2026-08-24)
|
# AIC8800 wifi+BT port: M5 progress (2026-08-24)
|
||||||
|
|
||||||
Status: **DONE — zImage + rv1106-warden.dtb build clean with the driver
|
Status: **DONE, zImage + rv1106-warden.dtb build clean with the driver
|
||||||
built in.** Not flashed/booted (parent session verifies on hardware).
|
built in.** Not flashed/booted (parent session verifies on hardware).
|
||||||
|
|
||||||
## Build status
|
## Build status
|
||||||
@@ -15,14 +15,14 @@ Clean rebuild (all aic8800 sources touched to force recompilation): **0
|
|||||||
errors**, 104 warnings, all benign vendor-code style (`101
|
errors**, 104 warnings, all benign vendor-code style (`101
|
||||||
-Wmissing-prototypes` on internal non-`static` helper functions that were
|
-Wmissing-prototypes` on internal non-`static` helper functions that were
|
||||||
never prototyped in headers, `3 -Wempty-body` on an existing `if
|
never prototyped in headers, `3 -Wempty-body` on an existing `if
|
||||||
(...);`-with-no-body debug macro expansion) — none are correctness issues
|
(...);`-with-no-body debug macro expansion), none are correctness issues
|
||||||
and none were introduced by version-delta fixes. `zImage is ready`;
|
and none were introduced by version-delta fixes. `zImage is ready`;
|
||||||
`rv1106-warden.dtb` compiles with only the pre-existing, unrelated `&rgb`
|
`rv1106-warden.dtb` compiles with only the pre-existing, unrelated `&rgb`
|
||||||
graph-endpoint warning (already known from M4 display work, not
|
graph-endpoint warning (already known from M4 display work, not
|
||||||
wifi-related). Confirmed via `nm vmlinux.unstripped` that the driver is
|
wifi-related). Confirmed via `nm vmlinux.unstripped` that the driver is
|
||||||
actually linked in, not silently dropped: `aicbsp_sdio_driver`,
|
actually linked in, not silently dropped: `aicbsp_sdio_driver`,
|
||||||
`aicwf_sdio_driver`, `rwnx_cfg80211_ops` all present, and the three
|
`aicwf_sdio_driver`, `rwnx_cfg80211_ops` all present, and the three
|
||||||
`module_init()`s appear as `device_initcall`s in the correct link order —
|
`module_init()`s appear as `device_initcall`s in the correct link order,
|
||||||
`__initcall__kmod_aic8800_fdrv__...rwnx_mod_init` after bsp's init and
|
`__initcall__kmod_aic8800_fdrv__...rwnx_mod_init` after bsp's init and
|
||||||
`__initcall__kmod_aic8800_btlpm__...aic_bluetooth_mod_init` last.
|
`__initcall__kmod_aic8800_btlpm__...aic_bluetooth_mod_init` last.
|
||||||
|
|
||||||
@@ -37,57 +37,57 @@ Dropped as dead code for this board (SDIO-only, no USB/PCIe, `CONFIG_SDIO_BT=n`)
|
|||||||
`usb_host.{c,h}`, `rwnx_pci.{c,h}`, `aicwf_usb.c`, `btsdio.c`, `aic_btsdio.c`.
|
`usb_host.{c,h}`, `rwnx_pci.{c,h}`, `aicwf_usb.c`, `btsdio.c`, `aic_btsdio.c`.
|
||||||
|
|
||||||
**One exception the plan flagged correctly**: `rwnx_mesh.{c,h}` was
|
**One exception the plan flagged correctly**: `rwnx_mesh.{c,h}` was
|
||||||
initially dropped on the same "dead code" theory but had to be **restored**
|
initially dropped on the same "dead code" theory but had to be **restored**,
|
||||||
— `rwnx_tx.c`'s `NL80211_IFTYPE_MESH_POINT` switch-case block is not
|
`rwnx_tx.c`'s `NL80211_IFTYPE_MESH_POINT` switch-case block is not
|
||||||
preprocessor-gated, so `rwnx_mesh.h`'s types (`struct rwnx_mesh_path`,
|
preprocessor-gated, so `rwnx_mesh.h`'s types (`struct rwnx_mesh_path`,
|
||||||
`struct rwnx_mesh_proxy`) and prototypes are needed to compile even though
|
`struct rwnx_mesh_proxy`) and prototypes are needed to compile even though
|
||||||
this board never creates a mesh-type interface. Restored both files
|
this board never creates a mesh-type interface. Restored both files
|
||||||
(1KB each) and added `rwnx_mesh.o` back to the fdrv `Makefile` object list.
|
(1KB each) and added `rwnx_mesh.o` back to the fdrv `Makefile` object list.
|
||||||
`usb_host.c`/`rwnx_pci.c` did NOT need restoring — no build error ever
|
`usb_host.c`/`rwnx_pci.c` did NOT need restoring: no build error ever
|
||||||
referenced them.
|
referenced them.
|
||||||
|
|
||||||
New in-tree Kbuild wiring (not copied from vendor, vendor Makefiles are
|
New in-tree Kbuild wiring (not copied from vendor, vendor Makefiles are
|
||||||
out-of-tree `KDIR=` external-module style and don't apply directly):
|
out-of-tree `KDIR=` external-module style and don't apply directly):
|
||||||
- `drivers/net/wireless/aic8800/Kconfig` — top-level `AIC_WLAN_SUPPORT`
|
- `drivers/net/wireless/aic8800/Kconfig`, top-level `AIC_WLAN_SUPPORT`
|
||||||
bool + `AIC_FW_PATH` string, `source`s the fdrv/btlpm sub-Kconfigs.
|
bool + `AIC_FW_PATH` string, `source`s the fdrv/btlpm sub-Kconfigs.
|
||||||
Wired into `drivers/net/wireless/Kconfig` (new `source` line, alongside
|
Wired into `drivers/net/wireless/Kconfig` (new `source` line, alongside
|
||||||
the existing vendor Kconfig list).
|
the existing vendor Kconfig list).
|
||||||
- `drivers/net/wireless/aic8800/Makefile` — `obj-y` list preserving
|
- `drivers/net/wireless/aic8800/Makefile`, `obj-y` list preserving
|
||||||
bsp → fdrv → btlpm link order (per PORT-PLAN.md §4.5, since these are
|
bsp -> fdrv -> btlpm link order (per PORT-PLAN.md section 4.5, since these are
|
||||||
built-in `=y` now and initcall order follows link order). Wired into
|
built-in `=y` now and initcall order follows link order). Wired into
|
||||||
`drivers/net/wireless/Makefile` (`obj-$(CONFIG_AIC_WLAN_SUPPORT) += aic8800/`).
|
`drivers/net/wireless/Makefile` (`obj-$(CONFIG_AIC_WLAN_SUPPORT) += aic8800/`).
|
||||||
- `aic8800_fdrv/Kconfig`, `aic8800_btlpm/Kconfig` — vendor's `tristate`
|
- `aic8800_fdrv/Kconfig`, `aic8800_btlpm/Kconfig`, vendor's `tristate`
|
||||||
symbols changed to `default y` (built-in, not modular; the 6.18 rootfs
|
symbols changed to `default y` (built-in, not modular; the 6.18 rootfs
|
||||||
has no working module pipeline yet).
|
has no working module pipeline yet).
|
||||||
- Three new in-tree `Makefile`s (`aic8800_bsp/`, `aic8800_fdrv/`,
|
- Three new in-tree `Makefile`s (`aic8800_bsp/`, `aic8800_fdrv/`,
|
||||||
`aic8800_btlpm/`) translating the vendor's `ccflags-$(CONFIG_X) += -DX`
|
`aic8800_btlpm/`) translating the vendor's `ccflags-$(CONFIG_X) += -DX`
|
||||||
pattern into fixed `-D` flags for the one build configuration this board
|
pattern into fixed `-D` flags for the one build configuration this board
|
||||||
actually uses (SDIO, Rockchip, no USB/PCI/mesh-in-practice, TCP-ACK
|
actually uses (SDIO, Rockchip, no USB/PCI/mesh-in-practice, TCP-ACK
|
||||||
filter, preallocated TXQ, RF test support — see each Makefile's ccflags
|
filter, preallocated TXQ, RF test support, see each Makefile's ccflags
|
||||||
for the full list, matching the vendor Makefile defaults). `CONFIG_AIC_FW_PATH`
|
for the full list, matching the vendor Makefile defaults). `CONFIG_AIC_FW_PATH`
|
||||||
needs no `-D`: it's now a real Kconfig string symbol, so
|
needs no `-D`: it's now a real Kconfig string symbol, so
|
||||||
`include/generated/autoconf.h` already defines the `CONFIG_AIC_FW_PATH`
|
`include/generated/autoconf.h` already defines the `CONFIG_AIC_FW_PATH`
|
||||||
C-string macro the driver expects (`aicsdio.c:59`, `aic_bsp_driver.h:348`).
|
C-string macro the driver expects (`aicsdio.c:59`, `aic_bsp_driver.h:348`).
|
||||||
|
|
||||||
## API-delta fixes (file → change)
|
## API-delta fixes (file -> change)
|
||||||
|
|
||||||
### Plan-anticipated mechanical renames (applied via scoped `sed`, all call sites)
|
### Plan-anticipated mechanical renames (applied via scoped `sed`, all call sites)
|
||||||
- `from_timer()` → `timer_container_of()` — 7 sites: `aicsdio.c`,
|
- `from_timer()` -> `timer_container_of()`, 7 sites: `aicsdio.c`,
|
||||||
`aicwf_sdio.c` (×3), `rwnx_main.c`, `rwnx_rx.c` (×2).
|
`aicwf_sdio.c` (x3), `rwnx_main.c`, `rwnx_rx.c` (x2).
|
||||||
- `del_timer_sync()` → `timer_delete_sync()` — 10 sites: `aicsdio.c`,
|
- `del_timer_sync()` -> `timer_delete_sync()`, 10 sites: `aicsdio.c`,
|
||||||
`aicwf_sdio.c` (×5), `rwnx_main.c` (×2, one more than the plan's count —
|
`aicwf_sdio.c` (x5), `rwnx_main.c` (x2, one more than the plan's count,
|
||||||
`rwnx_main.c:6177` wasn't in the plan's list but grep found it),
|
`rwnx_main.c:6177` wasn't in the plan's list but grep found it),
|
||||||
`rwnx_rx.c` (×2).
|
`rwnx_rx.c` (x2).
|
||||||
- `del_timer()` → `timer_delete()` — 9 sites: `aicwf_tcp_ack.c` (×4),
|
- `del_timer()` -> `timer_delete()`, 9 sites: `aicwf_tcp_ack.c` (x4),
|
||||||
`rwnx_rx.c` (×2), `aic8800_btlpm/aic8800_btlpm.c` (×3, dead — not in this
|
`rwnx_rx.c` (x2), `aic8800_btlpm/aic8800_btlpm.c` (x3, dead, not in this
|
||||||
build's object list, fixed anyway for tree hygiene), `aic8800_btlpm/lpm.c`
|
build's object list, fixed anyway for tree hygiene), `aic8800_btlpm/lpm.c`
|
||||||
(×3, same — `lpm.c` isn't compiled, `CONFIG_SUPPORT_LPM=n`).
|
(x3, same, `lpm.c` isn't compiled, `CONFIG_SUPPORT_LPM=n`).
|
||||||
- `netif_rx_ni()` → `netif_rx()` — 3 sites, all `rwnx_rx.c`.
|
- `netif_rx_ni()` -> `netif_rx()`: 3 sites, all `rwnx_rx.c`.
|
||||||
|
|
||||||
### Deltas beyond the plan's list (found by the compiler, fixed against 6.18 headers)
|
### Deltas beyond the plan's list (found by the compiler, fixed against 6.18 headers)
|
||||||
- **`cfg80211_rx_spurious_frame()` / `cfg80211_rx_unexpected_4addr_frame()`**
|
- **`cfg80211_rx_spurious_frame()` / `cfg80211_rx_unexpected_4addr_frame()`**
|
||||||
(`rwnx_rx.c`, 2 sites): gained a `link_id` param before `gfp`; passed
|
(`rwnx_rx.c`, 2 sites): gained a `link_id` param before `gfp`; passed
|
||||||
`-1` ("not applicable", per the header's own doc comment — this driver
|
`-1` ("not applicable", per the header's own doc comment, this driver
|
||||||
has no MLO).
|
has no MLO).
|
||||||
- **`cfg80211_ch_switch_notify()` / `cfg80211_ch_switch_started_notify()`**
|
- **`cfg80211_ch_switch_notify()` / `cfg80211_ch_switch_started_notify()`**
|
||||||
(`rwnx_main.c`, 2 sites): the vendor's own version-gated compat shim
|
(`rwnx_main.c`, 2 sites): the vendor's own version-gated compat shim
|
||||||
@@ -100,12 +100,12 @@ out-of-tree `KDIR=` external-module style and don't apply directly):
|
|||||||
edit the historical shim; `link_id = 0` (mandatory for non-MLO per the
|
edit the historical shim; `link_id = 0` (mandatory for non-MLO per the
|
||||||
header).
|
header).
|
||||||
- **`cfg80211_ops` struct-initializer type mismatches** (`rwnx_main.c`,
|
- **`cfg80211_ops` struct-initializer type mismatches** (`rwnx_main.c`,
|
||||||
7 callbacks — all gained new params, all effectively "not applicable" for
|
7 callbacks, all gained new params, all effectively "not applicable" for
|
||||||
this single-radio, non-MLO, SDIO full-MAC driver, so the new params are
|
this single-radio, non-MLO, SDIO full-MAC driver, so the new params are
|
||||||
accepted and ignored):
|
accepted and ignored):
|
||||||
- `change_beacon`: now takes `struct cfg80211_ap_update *` (wraps the old
|
- `change_beacon`: now takes `struct cfg80211_ap_update *` (wraps the old
|
||||||
`cfg80211_beacon_data` as `.beacon`, plus FILS/S1G/unsol fields unused
|
`cfg80211_beacon_data` as `.beacon`, plus FILS/S1G/unsol fields unused
|
||||||
here) — unwrapped with `&update->beacon` so `rwnx_build_bcn()` and its
|
here); unwrapped with `&update->beacon` so `rwnx_build_bcn()` and its
|
||||||
other 3 callers stay untouched.
|
other 3 callers stay untouched.
|
||||||
- `set_monitor_channel`: gained a `struct net_device *dev` param. Rather
|
- `set_monitor_channel`: gained a `struct net_device *dev` param. Rather
|
||||||
than thread an unused `dev` through the well-used internal 2-arg
|
than thread an unused `dev` through the well-used internal 2-arg
|
||||||
@@ -123,41 +123,41 @@ out-of-tree `KDIR=` external-module style and don't apply directly):
|
|||||||
`unsigned int link_id`; passed `0` (mandatory for non-MLO per doc comment).
|
`unsigned int link_id`; passed `0` (mandatory for non-MLO per doc comment).
|
||||||
- **`wakeup_source_create()`/`_add()`/`_remove()`/`_destroy()`** (`rwnx_wakelock.c`):
|
- **`wakeup_source_create()`/`_add()`/`_remove()`/`_destroy()`** (`rwnx_wakelock.c`):
|
||||||
removed from the public API entirely (still exist internally in
|
removed from the public API entirely (still exist internally in
|
||||||
`drivers/base/power/wakeup.c` but are no longer `EXPORT_SYMBOL`'d) —
|
`drivers/base/power/wakeup.c` but are no longer `EXPORT_SYMBOL`'d);
|
||||||
`wakeup_source_register(dev, name)` / `wakeup_source_unregister(ws)` is
|
`wakeup_source_register(dev, name)` / `wakeup_source_unregister(ws)` is
|
||||||
now the only supported entry point (confirmed: `wakeup_source_register()`'s
|
now the only supported entry point (confirmed: `wakeup_source_register()`'s
|
||||||
own implementation is literally `create()` + conditional sysfs +
|
own implementation is literally `create()` + conditional sysfs +
|
||||||
`add()`). Rewrote `rwnx_wakeup_init()`/`_deinit()` to call
|
`add()`). Rewrote `rwnx_wakeup_init()`/`_deinit()` to call
|
||||||
`wakeup_source_register(NULL, name)` / `wakeup_source_unregister(ws)` —
|
`wakeup_source_register(NULL, name)` / `wakeup_source_unregister(ws)`:
|
||||||
`dev=NULL` registers an anonymous source not tied to a `struct device`,
|
`dev=NULL` registers an anonymous source not tied to a `struct device`,
|
||||||
matching the old `wakeup_source_create()` behavior used here. (The file
|
matching the old `wakeup_source_create()` behavior used here. (The file
|
||||||
already had a correct modern `rwnx_wakeup_register()`/`_unregister()`
|
already had a correct modern `rwnx_wakeup_register()`/`_unregister()`
|
||||||
pair for a different call path — this fix makes `rwnx_wakeup_init()`
|
pair for a different call path; this fix makes `rwnx_wakeup_init()`
|
||||||
consistent with it.)
|
consistent with it.)
|
||||||
- **`MODULE_IMPORT_NS(bare_token)` → `MODULE_IMPORT_NS("string")`** — 3
|
- **`MODULE_IMPORT_NS(bare_token)` -> `MODULE_IMPORT_NS("string")`**: 3
|
||||||
sites (`aic_bsp_driver.c`, `rwnx_platform.c`, `rwnx_main.c`), all
|
sites (`aic_bsp_driver.c`, `rwnx_platform.c`, `rwnx_main.c`), all
|
||||||
importing the same non-mainline `VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver`
|
importing the same non-mainline `VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver`
|
||||||
namespace token (an Android-GKI-kernel artifact — confirmed absent from
|
namespace token (an Android-GKI-kernel artifact, confirmed absent from
|
||||||
our mainline `fs/` tree, so this is now inert modinfo metadata, not a
|
our mainline `fs/` tree, so this is now inert modinfo metadata, not a
|
||||||
real namespace gate). `MODULE_INFO()`/`MODULE_IMPORT_NS()` now require a
|
real namespace gate). `MODULE_INFO()`/`MODULE_IMPORT_NS()` now require a
|
||||||
quoted string per current `include/linux/module.h`.
|
quoted string per current `include/linux/module.h`.
|
||||||
- **`rwnx_platform.c`: unconditional `#include "rwnx_pci.h"`** — the header
|
- **`rwnx_platform.c`: unconditional `#include "rwnx_pci.h"`**, the header
|
||||||
was dropped with `rwnx_pci.c` (dead code, no PCIe on this board), but this
|
was dropped with `rwnx_pci.c` (dead code, no PCIe on this board), but this
|
||||||
one `#include` wasn't behind any guard. Guarded it behind
|
one `#include` wasn't behind any guard. Guarded it behind
|
||||||
`AICWF_PCIE_SUPPORT` (matching the guard its only two callers,
|
`AICWF_PCIE_SUPPORT` (matching the guard its only two callers,
|
||||||
`rwnx_platform_{,un}register_drv()`, already use at their call sites in
|
`rwnx_platform_{,un}register_drv()`, already use at their call sites in
|
||||||
`rwnx_main.c`) and guarded the two functions' `rwnx_pci_{,un}register_drv()`
|
`rwnx_main.c`) and guarded the two functions' `rwnx_pci_{,un}register_drv()`
|
||||||
bodies the same way, returning `0`/no-op for the SDIO-only build instead.
|
bodies the same way, returning `0`/no-op for the SDIO-only build instead.
|
||||||
- **`<linux/rfkill-wlan.h>` (vendor-only, PORT-PLAN.md §3.7)** — removed
|
- **`<linux/rfkill-wlan.h>` (vendor-only, PORT-PLAN.md section 3.7)**: removed
|
||||||
from 3 files (`aicsdio.c`, `aicwf_sdio.c`, `rwnx_main.c`). Traced every
|
from 3 files (`aicsdio.c`, `aicwf_sdio.c`, `rwnx_main.c`). Traced every
|
||||||
real `rockchip_wifi_power()`/`_set_carddetect()` call site first: all of
|
real `rockchip_wifi_power()`/`_set_carddetect()` call site first: all of
|
||||||
them are either already `#if 0`'d out in the vendor source
|
them are either already `#if 0`'d out in the vendor source
|
||||||
(`aicwf_sdio.c`) or gated behind `CONFIG_PLATFORM_ROCKCHIP2` (a symbol
|
(`aicwf_sdio.c`) or gated behind `CONFIG_PLATFORM_ROCKCHIP2` (a symbol
|
||||||
this build never defines — only plain `ROCKCHIP`), so none needed
|
this build never defines, only plain `ROCKCHIP`), so none needed
|
||||||
stubbing individually; only the unconditional header `#include`s needed
|
stubbing individually; only the unconditional header `#include`s needed
|
||||||
removing. The DT's `mmc-pwrseq-simple` node does the power/reset
|
removing. The DT's `mmc-pwrseq-simple` node does the power/reset
|
||||||
sequencing instead, as planned.
|
sequencing instead, as planned.
|
||||||
- **`CONFIG_RFTEST` added to `aic8800_fdrv`'s ccflags** — not itself a
|
- **`CONFIG_RFTEST` added to `aic8800_fdrv`'s ccflags**: not itself a
|
||||||
version delta, but needed: `aic_priv_cmd.c`'s RF-test-mode enum
|
version delta, but needed: `aic_priv_cmd.c`'s RF-test-mode enum
|
||||||
(`SET_TX`, `RDWR_EFUSE_*`, ~50 constants) and struct typedefs
|
(`SET_TX`, `RDWR_EFUSE_*`, ~50 constants) and struct typedefs
|
||||||
(`cmd_rf_settx_t` etc.) are defined inline in the file under `#ifdef
|
(`cmd_rf_settx_t` etc.) are defined inline in the file under `#ifdef
|
||||||
@@ -172,31 +172,31 @@ The vendor `aic8800_bsp` and `aic8800_fdrv` were always built as two
|
|||||||
**independent `.ko` modules**, each with its own private symbol namespace.
|
**independent `.ko` modules**, each with its own private symbol namespace.
|
||||||
Built in-tree as `=y`, both link into one `vmlinux`, and it turns out
|
Built in-tree as `=y`, both link into one `vmlinux`, and it turns out
|
||||||
`aic8800_bsp` carries a **complete second, self-contained copy** of large
|
`aic8800_bsp` carries a **complete second, self-contained copy** of large
|
||||||
parts of `aic8800_fdrv`'s machinery — its own SDIO byte/frame transport
|
parts of `aic8800_fdrv`'s machinery: its own SDIO byte/frame transport
|
||||||
(`aicwf_sdio_*`, `aicwf_bus_*`, `aicwf_frame_*`, `aicwf_tx_*`,
|
(`aicwf_sdio_*`, `aicwf_bus_*`, `aicwf_frame_*`, `aicwf_tx_*`,
|
||||||
`aicwf_rx_*`, `crc8_ponl_107`), its own message/debug-command layer
|
`aicwf_rx_*`, `crc8_ponl_107`), its own message/debug-command layer
|
||||||
(`rwnx_send_dbg_*_req`, `rwnx_cmd_mgr_{init,deinit}`,
|
(`rwnx_send_dbg_*_req`, `rwnx_cmd_mgr_{init,deinit}`,
|
||||||
`rwnx_rx_handle_msg`), and a few globals (`chip_mcu_id`, `chip_sub_id`,
|
`rwnx_rx_handle_msg`), and a few globals (`chip_mcu_id`, `chip_sub_id`,
|
||||||
`aic_fw_path`, `testmode`) — used for the bsp-side firmware bring-up phase
|
`aic_fw_path`, `testmode`), used for the bsp-side firmware bring-up phase
|
||||||
before fdrv's own driver instance re-probes and takes over the SDIO
|
before fdrv's own driver instance re-probes and takes over the SDIO
|
||||||
function for real runtime operation. **49 symbols total**, `ld` caught
|
function for real runtime operation. **49 symbols total**, `ld` caught
|
||||||
every one as "multiple definition" at the final `vmlinux.o` link (the
|
every one as "multiple definition" at the final `vmlinux.o` link (the
|
||||||
per-subdirectory `built-in.a` build had already succeeded — this class of
|
per-subdirectory `built-in.a` build had already succeeded: this class of
|
||||||
error only surfaces at the whole-image link, which is worth knowing for
|
error only surfaces at the whole-image link, which is worth knowing for
|
||||||
anyone repeating this kind of port).
|
anyone repeating this kind of port).
|
||||||
|
|
||||||
Verified via `aic_bsp_export.h` (the real, narrow, intentional
|
Verified via `aic_bsp_export.h` (the real, narrow, intentional
|
||||||
`aicbsp_*`-prefixed bsp→fdrv API surface) that none of the 49 are part of
|
`aicbsp_*`-prefixed bsp->fdrv API surface) that none of the 49 are part of
|
||||||
the actual cross-module contract, and via `grep -rlw` that `fdrv` has its
|
the actual cross-module contract, and via `grep -rlw` that `fdrv` has its
|
||||||
own **complete** definition of every one (not an `extern` pointing at
|
own **complete** definition of every one (not an `extern` pointing at
|
||||||
bsp's copy) — confirming these are two truly independent implementations,
|
bsp's copy), confirming these are two truly independent implementations,
|
||||||
not one legitimately shared. Fix: renamed all 49 symbols with an
|
not one legitimately shared. Fix: renamed all 49 symbols with an
|
||||||
`aicbsp_priv_` prefix, scoped strictly to `aic8800_bsp/*.{c,h}` (both
|
`aicbsp_priv_` prefix, scoped strictly to `aic8800_bsp/*.{c,h}` (both
|
||||||
definitions and bsp-internal call sites via whole-word `sed`); `fdrv`'s
|
definitions and bsp-internal call sites via whole-word `sed`); `fdrv`'s
|
||||||
copies are untouched. One of the 49, `md5.c`'s MD5 functions, got a
|
copies are untouched. One of the 49, `md5.c`'s MD5 functions, got a
|
||||||
different (smaller-footprint) fix: `aic8800_bsp/md5.c` and
|
different (smaller-footprint) fix: `aic8800_bsp/md5.c` and
|
||||||
`aic8800_fdrv/md5.c` are **byte-identical** files, so `md5.o` was simply
|
`aic8800_fdrv/md5.c` are **byte-identical** files, so `md5.o` was simply
|
||||||
dropped from `aic8800_fdrv`'s object list instead of renamed — `fdrv`'s
|
dropped from `aic8800_fdrv`'s object list instead of renamed, `fdrv`'s
|
||||||
calls resolve against `bsp`'s copy at link time since bsp links first.
|
calls resolve against `bsp`'s copy at link time since bsp links first.
|
||||||
|
|
||||||
## Config symbols set
|
## Config symbols set
|
||||||
@@ -208,11 +208,11 @@ CONFIG_AIC8800_BTLPM_SUPPORT=y
|
|||||||
CONFIG_AIC_FW_PATH="/oem/usr/ko/aic8800dc_fw"
|
CONFIG_AIC_FW_PATH="/oem/usr/ko/aic8800dc_fw"
|
||||||
CONFIG_WIRELESS=y CONFIG_CFG80211=y CONFIG_WLAN=y (already y pre-port)
|
CONFIG_WIRELESS=y CONFIG_CFG80211=y CONFIG_WLAN=y (already y pre-port)
|
||||||
CONFIG_MMC=y CONFIG_MMC_DW_ROCKCHIP=y CONFIG_RFKILL=y (already y pre-port)
|
CONFIG_MMC=y CONFIG_MMC_DW_ROCKCHIP=y CONFIG_RFKILL=y (already y pre-port)
|
||||||
CONFIG_BT=y (was =m, flipped to =y — no module pipeline yet)
|
CONFIG_BT=y (was =m, flipped to =y, no module pipeline yet)
|
||||||
CONFIG_BT_HCIUART=y (was =m, flipped to =y)
|
CONFIG_BT_HCIUART=y (was =m, flipped to =y)
|
||||||
CONFIG_BT_HCIUART_H4=y (already y)
|
CONFIG_BT_HCIUART_H4=y (already y)
|
||||||
CONFIG_CRYPTO_ARC4=y CONFIG_CRYPTO_CTR=y CONFIG_CRYPTO_CCM=y CONFIG_CRYPTO_AES=y
|
CONFIG_CRYPTO_ARC4=y CONFIG_CRYPTO_CTR=y CONFIG_CRYPTO_CCM=y CONFIG_CRYPTO_AES=y
|
||||||
# CONFIG_MAC80211 is not set (explicitly left off — pure cfg80211 full-MAC
|
# CONFIG_MAC80211 is not set (explicitly left off, pure cfg80211 full-MAC
|
||||||
driver, confirmed no mac80211/ieee80211_hw
|
driver, confirmed no mac80211/ieee80211_hw
|
||||||
symbol usage anywhere in aic8800_fdrv)
|
symbol usage anywhere in aic8800_fdrv)
|
||||||
```
|
```
|
||||||
@@ -231,12 +231,12 @@ ported drivers' symbols (`CONFIG_CLK_RV1106`, `CONFIG_PINCTRL_ROCKCHIP`,
|
|||||||
`non-removable`, `rockchip,default-sample-phase = <90>`,
|
`non-removable`, `rockchip,default-sample-phase = <90>`,
|
||||||
`supports-sdio`, `mmc-pwrseq = <&sdio_pwrseq>`, pinctrl
|
`supports-sdio`, `mmc-pwrseq = <&sdio_pwrseq>`, pinctrl
|
||||||
`sdmmc0_clk`/`_cmd`/`_bus4`/`_det`, `status = "okay"`.
|
`sdmmc0_clk`/`_cmd`/`_bus4`/`_det`, `status = "okay"`.
|
||||||
- `&sdio` (mmc@ff9a0000) left untouched — stays `disabled`, genuinely
|
- `&sdio` (mmc@ff9a0000) left untouched: stays `disabled`, genuinely
|
||||||
unused hardware on this board.
|
unused hardware on this board.
|
||||||
|
|
||||||
**Note**: the `&sdmmc` node itself (`mmc@ffaa0000`) in `rv1106.dtsi` and
|
**Note**: the `&sdmmc` node itself (`mmc@ffaa0000`) in `rv1106.dtsi` and
|
||||||
the `sdmmc0_*` pinctrl groups in `rv1106-pinctrl.dtsi` were **already
|
the `sdmmc0_*` pinctrl groups in `rv1106-pinctrl.dtsi` were **already
|
||||||
present** in this tree from earlier M1–M3 work (not added by this task) —
|
present** in this tree from earlier M1-M3 work (not added by this task),
|
||||||
this task only added the board-DTS *enablement* (`status = "okay"` +
|
this task only added the board-DTS *enablement* (`status = "okay"` +
|
||||||
properties) and the new `sdio_pwrseq` node. Verified in the compiled
|
properties) and the new `sdio_pwrseq` node. Verified in the compiled
|
||||||
`.dtb` (decompiled with `dtc -I dtb -O dts`) that both nodes carry the
|
`.dtb` (decompiled with `dtc -I dtb -O dts`) that both nodes carry the
|
||||||
@@ -245,7 +245,7 @@ correct phandles/properties.
|
|||||||
## Hardening patches
|
## Hardening patches
|
||||||
|
|
||||||
Both hardware-verified patches from the plan are **already present in the
|
Both hardware-verified patches from the plan are **already present in the
|
||||||
vendor SDK source** we copied from (not merely trackable-but-unapplied) —
|
vendor SDK source** we copied from (not merely trackable-but-unapplied),
|
||||||
confirmed two ways: `patch -p5 --dry-run` on both reported "Reversed (or
|
confirmed two ways: `patch -p5 --dry-run` on both reported "Reversed (or
|
||||||
previously applied) patch detected" against the freshly-copied tree, and
|
previously applied) patch detected" against the freshly-copied tree, and
|
||||||
`grep` found their exact markers already in place:
|
`grep` found their exact markers already in place:
|
||||||
@@ -260,7 +260,7 @@ previously applied) patch detected" against the freshly-copied tree, and
|
|||||||
No action needed beyond copying the source. **Known housekeeping gap
|
No action needed beyond copying the source. **Known housekeeping gap
|
||||||
(pre-existing, not fixed by this task, flagged by the plan)**: patch 0002
|
(pre-existing, not fixed by this task, flagged by the plan)**: patch 0002
|
||||||
is still only tracked under `future-features-2/sdk-patches/wifi/patches/`,
|
is still only tracked under `future-features-2/sdk-patches/wifi/patches/`,
|
||||||
not this branch's own `sdk-patches/wifi/patches/` — worth closing
|
not this branch's own `sdk-patches/wifi/patches/`, worth closing
|
||||||
separately since the fix is live in the vendor tree either way.
|
separately since the fix is live in the vendor tree either way.
|
||||||
|
|
||||||
## Constraints honored
|
## Constraints honored
|
||||||
@@ -269,35 +269,35 @@ Only touched: the wifi driver tree (`drivers/net/wireless/aic8800/`), its
|
|||||||
Kconfig/Makefile wiring (`drivers/net/wireless/{Kconfig,Makefile}`), the
|
Kconfig/Makefile wiring (`drivers/net/wireless/{Kconfig,Makefile}`), the
|
||||||
board DTS wifi nodes (`rv1106-warden.dts`, additive only), and
|
board DTS wifi nodes (`rv1106-warden.dts`, additive only), and
|
||||||
wifi/BT/crypto-related `.config` symbols. Did not touch any other driver,
|
wifi/BT/crypto-related `.config` symbols. Did not touch any other driver,
|
||||||
DT node, or unrelated config symbol — spot-checked after the port that
|
DT node, or unrelated config symbol: spot-checked after the port that
|
||||||
`CONFIG_CLK_RV1106`, `CONFIG_PINCTRL_ROCKCHIP`, `CONFIG_DRM_ROCKCHIP`,
|
`CONFIG_CLK_RV1106`, `CONFIG_PINCTRL_ROCKCHIP`, `CONFIG_DRM_ROCKCHIP`,
|
||||||
`CONFIG_MMC_DW_ROCKCHIP` are all still `=y`, and the `&rgb`/VOP-related dtc
|
`CONFIG_MMC_DW_ROCKCHIP` are all still `=y`, and the `&rgb`/VOP-related dtc
|
||||||
warning is the same pre-existing one from M4 (not new). Never touched
|
warning is the same pre-existing one from M4 (not new). Never touched
|
||||||
hardware — no kflash, no reboot, no `/dev/ttyUSB2`, no Pi.
|
hardware: no kflash, no reboot, no `/dev/ttyUSB2`, no Pi.
|
||||||
|
|
||||||
## What the parent should watch for on hardware
|
## What the parent should watch for on hardware
|
||||||
|
|
||||||
Per PORT-PLAN.md §6's verify sequence:
|
Per PORT-PLAN.md section 6's verify sequence:
|
||||||
1. `dmesg`: `dwmmc_rockchip ffaa0000.mmc: ...` binding (mirrors the M3
|
1. `dmesg`: `dwmmc_rockchip ffaa0000.mmc: ...` binding (mirrors the M3
|
||||||
eMMC pattern on `ffa90000.mmc`), then an SDIO card enumerating on it
|
eMMC pattern on `ffa90000.mmc`), then an SDIO card enumerating on it
|
||||||
(vendor/device ID `0xc8a1`/`0xc08d`), then `aic8800_bsp`'s probe firing.
|
(vendor/device ID `0xc8a1`/`0xc08d`), then `aic8800_bsp`'s probe firing.
|
||||||
2. **Firmware path**: `AIC_FW_PATH` defaults to `/oem/usr/ko/aic8800dc_fw`
|
2. **Firmware path**: `AIC_FW_PATH` defaults to `/oem/usr/ko/aic8800dc_fw`,
|
||||||
— a 5.10-era Buildroot `/oem` partition path. Confirm the 6.18 rootfs
|
a 5.10-era Buildroot `/oem` partition path. Confirm the 6.18 rootfs
|
||||||
actually has firmware there (or update `CONFIG_AIC_FW_PATH` to wherever
|
actually has firmware there (or update `CONFIG_AIC_FW_PATH` to wherever
|
||||||
it landed) — watch specifically for firmware-open failures in dmesg as
|
it landed), watch specifically for firmware-open failures in dmesg as
|
||||||
the first failure mode, before assuming a driver/DT bug.
|
the first failure mode, before assuming a driver/DT bug.
|
||||||
3. `wlan0` should appear in `ip link` once `aic8800_fdrv` attaches.
|
3. `wlan0` should appear in `ip link` once `aic8800_fdrv` attaches.
|
||||||
4. `iw phy` should show one 2.4 GHz band, 14 channels (matches the known
|
4. `iw phy` should show one 2.4 GHz band, 14 channels (matches the known
|
||||||
5.10 baseline — a mismatch signals a cfg80211/wiphy port bug, not a
|
5.10 baseline: a mismatch signals a cfg80211/wiphy port bug, not a
|
||||||
chip issue).
|
chip issue).
|
||||||
5. `iw dev wlan0 scan` and a real AP association exercise the
|
5. `iw dev wlan0 scan` and a real AP association exercise the
|
||||||
wiphy-locking behavior (§3.3 of the plan) — this is the area with the
|
wiphy-locking behavior (section 3.3 of the plan): this is the area with the
|
||||||
least direct verification in this port (the driver's own ops callbacks
|
least direct verification in this port (the driver's own ops callbacks
|
||||||
need no new locking per the plan's analysis, since cfg80211 core now
|
need no new locking per the plan's analysis, since cfg80211 core now
|
||||||
holds the wiphy mutex before calling in; the async-context notification
|
holds the wiphy mutex before calling in; the async-context notification
|
||||||
calls from `rwnx_msg_rx.c` were flagged as the highest-residual-risk
|
calls from `rwnx_msg_rx.c` were flagged as the highest-residual-risk
|
||||||
area and were NOT touched by this build-fix pass beyond the signature
|
area and were NOT touched by this build-fix pass beyond the signature
|
||||||
changes above — if scan/connect hang or lockdep splats appear, look
|
changes above, if scan/connect hang or lockdep splats appear, look
|
||||||
there first).
|
there first).
|
||||||
6. Confirm the two hardening patches are actually effective under load: no
|
6. Confirm the two hardening patches are actually effective under load: no
|
||||||
"cmd timed-out" console flood, no CPU-pinning busy-spin on a stressed/
|
"cmd timed-out" console flood, no CPU-pinning busy-spin on a stressed/
|
||||||
@@ -307,6 +307,6 @@ Per PORT-PLAN.md §6's verify sequence:
|
|||||||
8. Given the scale of the bsp/fdrv duplicate-symbol rename (49 symbols),
|
8. Given the scale of the bsp/fdrv duplicate-symbol rename (49 symbols),
|
||||||
if anything behaves subtly wrong in the bsp-side firmware bring-up
|
if anything behaves subtly wrong in the bsp-side firmware bring-up
|
||||||
phase specifically (vs. fdrv's runtime path), double-check the rename
|
phase specifically (vs. fdrv's runtime path), double-check the rename
|
||||||
didn't miss a cross-file reference within `aic8800_bsp/` — it was done
|
didn't miss a cross-file reference within `aic8800_bsp/`: it was done
|
||||||
with whole-word `sed` scoped to that directory and re-verified by a
|
with whole-word `sed` scoped to that directory and re-verified by a
|
||||||
clean rebuild, but it touched every `.c`/`.h` in that subtree.
|
clean rebuild, but it touched every `.c`/`.h` in that subtree.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# AIC8800 wifi — VERIFIED on warden-c8a3 (self-built 6.18.46), 2026-08-25
|
# AIC8800 wifi: VERIFIED on warden-c8a3 (self-built 6.18.46), 2026-08-25
|
||||||
|
|
||||||
**Result: wifi works end-to-end on our self-built Linux 6.18.46.** Modules built
|
**Result: wifi works end-to-end on our self-built Linux 6.18.46.** Modules built
|
||||||
from the ported source (vermagic `6.18.46 SMP mod_unload ARMv7 p2v8`), loaded on
|
from the ported source (vermagic `6.18.46 SMP mod_unload ARMv7 p2v8`), loaded on
|
||||||
@@ -6,39 +6,39 @@ the panel, downloaded firmware to the AIC8800DC, created `wlan0`, and completed
|
|||||||
live RF scan.
|
live RF scan.
|
||||||
|
|
||||||
## Evidence (serial console, _b slot = our 6.18 kernel)
|
## Evidence (serial console, _b slot = our 6.18 kernel)
|
||||||
- `insmod aic8800_bsp.ko` → firmware download OK: `aicwf_patch_config_8800dc done`,
|
- `insmod aic8800_bsp.ko` -> firmware download OK: `aicwf_patch_config_8800dc done`,
|
||||||
`Start app: 00120000`, BSP_RC=0.
|
`Start app: 00120000`, BSP_RC=0.
|
||||||
- `insmod aic8800_fdrv.ko` → `ieee80211 phy0: HT supp 1, VHT supp 1, HE supp 1`,
|
- `insmod aic8800_fdrv.ko` -> `ieee80211 phy0: HT supp 1, VHT supp 1, HE supp 1`,
|
||||||
FDRV_RC=0.
|
FDRV_RC=0.
|
||||||
- `wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> ... link/ether <device MAC>`
|
- `wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> ... link/ether <device MAC>`
|
||||||
- `iw dev wlan0 scan` found real APs (SSIDs/BSSIDs redacted for publication):
|
- `iw dev wlan0 scan` found real APs (SSIDs/BSSIDs redacted for publication):
|
||||||
- **the site AP at 2412 MHz, −43 dBm**
|
- **the site AP at 2412 MHz, -43 dBm**
|
||||||
- a neighboring guest AP at 2412 MHz, −73 dBm
|
- a neighboring guest AP at 2412 MHz, -73 dBm
|
||||||
- +several more, correct signal strengths → RF path fully functional.
|
- +several more, correct signal strengths -> RF path fully functional.
|
||||||
|
|
||||||
## Why MODULES, not built-in (=y)
|
## Why MODULES, not built-in (=y)
|
||||||
Built-in device_initcalls run BEFORE the dw_mmc/SDIO controller probes. Initcalls
|
Built-in device_initcalls run BEFORE the dw_mmc/SDIO controller probes. Initcalls
|
||||||
are sequential: `aicbsp_init` blocked 3.3–7.5 s doing the eager chip bring-up, and
|
are sequential: `aicbsp_init` blocked 3.3-7.5 s doing the eager chip bring-up, and
|
||||||
the mmc controller only probed at 7.9 s (SDIO card at 8.2 s) — AFTER aicbsp had
|
the mmc controller only probed at 7.9 s (SDIO card at 8.2 s), AFTER aicbsp had
|
||||||
already given up (`aicsdio.c:597` 2 s `down_timeout` → `sdio_unregister_driver`).
|
already given up (`aicsdio.c:597` 2 s `down_timeout` -> `sdio_unregister_driver`).
|
||||||
Extending the timeout can't help (aicbsp blocks the very mmc probe that would
|
Extending the timeout can't help (aicbsp blocks the very mmc probe that would
|
||||||
enumerate the card — a deadlock). Loaded as modules AFTER boot (mmc up, card at
|
enumerate the card, a deadlock). Loaded as modules AFTER boot (mmc up, card at
|
||||||
4 s), `insmod aic8800_bsp` registers the SDIO driver against an already-present
|
4 s), `insmod aic8800_bsp` registers the SDIO driver against an already-present
|
||||||
card → probe fires immediately → firmware download → fdrv → wlan0. This is the
|
card -> probe fires immediately -> firmware download -> fdrv -> wlan0. This is the
|
||||||
vendor-proven flow.
|
vendor-proven flow.
|
||||||
|
|
||||||
## Kernel-size fix (needed to boot the wifi kernel at all)
|
## Kernel-size fix (needed to boot the wifi kernel at all)
|
||||||
The wifi kernel grew the gzip zImage to 12.12 MB; rockchip U-Boot loads the kernel
|
The wifi kernel grew the gzip zImage to 12.12 MB; rockchip U-Boot loads the kernel
|
||||||
blob at 0x8000 and relocates the DTB to 0xc00000 (12 MB), so a >~11.95 MB zImage
|
blob at 0x8000 and relocates the DTB to 0xc00000 (12 MB), so a >~11.95 MB zImage
|
||||||
overruns the FDT at U-Boot load time (`Sysmem Error: KERNEL overlap with FDT`) and
|
overruns the FDT at U-Boot load time (`Sysmem Error: KERNEL overlap with FDT`) and
|
||||||
FLARE-AB falls back to _a. Switched `CONFIG_KERNEL_GZIP` → `CONFIG_KERNEL_XZ`:
|
FLARE-AB falls back to _a. Switched `CONFIG_KERNEL_GZIP` -> `CONFIG_KERNEL_XZ`:
|
||||||
zImage 12.12 MB → 8.15 MB (module build), ~4 MB headroom under the FDT. Also the
|
zImage 12.12 MB -> 8.15 MB (module build), ~4 MB headroom under the FDT. Also the
|
||||||
right call for a firmware kernel. (Uncompressed Image is ~30 MB; the ARM
|
right call for a firmware kernel. (Uncompressed Image is ~30 MB; the ARM
|
||||||
decompressor relocates the FDT at runtime, so only the U-Boot LOAD-time overlap
|
decompressor relocates the FDT at runtime, so only the U-Boot LOAD-time overlap
|
||||||
mattered — proven by the kernel booting once the zImage fit.)
|
mattered: proven by the kernel booting once the zImage fit.)
|
||||||
|
|
||||||
## Boot-time auto-load (follow-up, deployment layer — not the kernel port)
|
## Boot-time auto-load (follow-up, deployment layer, not the kernel port)
|
||||||
Modules were insmod'd manually for this verify. Production auto-load needs a
|
Modules were insmod'd manually for this verify. Production auto-load needs a
|
||||||
loader that inserts `aic8800_bsp.ko` → `aic8800_fdrv.ko` (→ `aic8800_btlpm.ko`) in
|
loader that inserts `aic8800_bsp.ko` -> `aic8800_fdrv.ko` (-> `aic8800_btlpm.ko`) in
|
||||||
order from wherever they're staged; the vendor `insmod_wifi.sh` references a
|
order from wherever they're staged; the vendor `insmod_wifi.sh` references a
|
||||||
different variant (`aic_load_fw.ko`/`bcmdhd.ko`, absent here). Track in the rootfs.
|
different variant (`aic_load_fw.ko`/`bcmdhd.ko`, absent here). Track in the rootfs.
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
# AICSemi AIC8800DC SDIO WiFi+BT driver (WardenOS M5 port).
|
# AICSemi AIC8800DC SDIO WiFi+BT driver (WardenOS M5 port).
|
||||||
#
|
#
|
||||||
# Ported from the vendor 5.10.160 out-of-tree source
|
# Ported from the vendor 5.10.160 out-of-tree source
|
||||||
# (flare-edge/sdk/sysdrv/drv_ko/wifi/aic8800dc/) — see
|
# (flare-edge/sdk/sysdrv/drv_ko/wifi/aic8800dc/), see
|
||||||
# warden-sdk/kernel/rv1106-enablement/wifi/PORT-PLAN.md and PORT-PROGRESS.md.
|
# warden-sdk/kernel/rv1106-enablement/wifi/PORT-PLAN.md and PORT-PROGRESS.md.
|
||||||
# Modules (=m), loaded late by /oem/usr/ko/insmod_wifi.sh: the AIC8800 SDIO
|
# Modules (=m), loaded late by /oem/usr/ko/insmod_wifi.sh: the AIC8800 SDIO
|
||||||
# bring-up is two-stage (bsp downloads firmware, chip re-enumerates, fdrv
|
# bring-up is two-stage (bsp downloads firmware, chip re-enumerates, fdrv
|
||||||
# attaches). Built-in device_initcalls run BEFORE the dw_mmc/SDIO controller
|
# attaches). Built-in device_initcalls run BEFORE the dw_mmc/SDIO controller
|
||||||
# probes — initcalls are sequential, so aicbsp_init blocks [3.3-7.5]s while the
|
# probes: initcalls are sequential, so aicbsp_init blocks [3.3-7.5]s while the
|
||||||
# mmc controller only probes at [7.9]s afterward, and the eager bring-up
|
# mmc controller only probes at [7.9]s afterward, and the eager bring-up
|
||||||
# deadlocks (it waits for a card the blocked mmc probe hasn't enumerated).
|
# deadlocks (it waits for a card the blocked mmc probe hasn't enumerated).
|
||||||
# Late module load, after the mmc-pwrseq enumerates the card, is the
|
# Late module load, after the mmc-pwrseq enumerates the card, is the
|
||||||
@@ -28,7 +28,7 @@ config AIC_FW_PATH
|
|||||||
default "/oem/usr/ko/aic8800dc_fw"
|
default "/oem/usr/ko/aic8800dc_fw"
|
||||||
help
|
help
|
||||||
Path to the firmware & config files. The driver reads these
|
Path to the firmware & config files. The driver reads these
|
||||||
directly (not via request_firmware()) — see PORT-PLAN.md §3.8.
|
directly (not via request_firmware()), see PORT-PLAN.md section 3.8.
|
||||||
|
|
||||||
if AIC_WLAN_SUPPORT
|
if AIC_WLAN_SUPPORT
|
||||||
source "drivers/net/wireless/aic8800/aic8800_fdrv/Kconfig"
|
source "drivers/net/wireless/aic8800/aic8800_fdrv/Kconfig"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# Link order matters: bsp before fdrv before btlpm, mirroring the working
|
# Link order matters: bsp before fdrv before btlpm, mirroring the working
|
||||||
# insmod order (insmod_wifi.sh) now that these are built-in (=y) instead of
|
# insmod order (insmod_wifi.sh) now that these are built-in (=y) instead of
|
||||||
# modules — see PORT-PLAN.md §4.5.
|
# modules, see PORT-PLAN.md section 4.5.
|
||||||
obj-$(CONFIG_AIC_WLAN_SUPPORT) += aic8800_bsp/
|
obj-$(CONFIG_AIC_WLAN_SUPPORT) += aic8800_bsp/
|
||||||
obj-$(CONFIG_AIC8800_WLAN_SUPPORT) += aic8800_fdrv/
|
obj-$(CONFIG_AIC8800_WLAN_SUPPORT) += aic8800_fdrv/
|
||||||
obj-$(CONFIG_AIC8800_BTLPM_SUPPORT) += aic8800_btlpm/
|
obj-$(CONFIG_AIC8800_BTLPM_SUPPORT) += aic8800_btlpm/
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0-only
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
#
|
#
|
||||||
# aic8800_bsp — SDIO bus glue + firmware bootstrap, ported in-tree from the
|
# aic8800_bsp: SDIO bus glue + firmware bootstrap, ported in-tree from the
|
||||||
# vendor out-of-tree Makefile (flare-edge/sdk/sysdrv/drv_ko/wifi/aic8800dc/
|
# vendor out-of-tree Makefile (flare-edge/sdk/sysdrv/drv_ko/wifi/aic8800dc/
|
||||||
# aic8800_bsp/Makefile). CONFIG_AIC_FW_PATH is now a real Kconfig string
|
# aic8800_bsp/Makefile). CONFIG_AIC_FW_PATH is now a real Kconfig string
|
||||||
# symbol (see ../Kconfig) so it needs no -D here — the driver picks it up
|
# symbol (see ../Kconfig) so it needs no -D here; the driver picks it up
|
||||||
# from include/generated/autoconf.h directly (aicsdio.c, aic_bsp_driver.h).
|
# from include/generated/autoconf.h directly (aicsdio.c, aic_bsp_driver.h).
|
||||||
#
|
#
|
||||||
# Vendor CONFIG_* build-time knobs below are preserved as fixed -D flags
|
# Vendor CONFIG_* build-time knobs below are preserved as fixed -D flags
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ config AIC8800_BTLPM_SUPPORT
|
|||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
This is support for the aic8800 bluetooth low-power-mode / HCI
|
This is support for the aic8800 bluetooth low-power-mode / HCI
|
||||||
wake companion module (aic8800_btlpm). Built-in (y) by default —
|
wake companion module (aic8800_btlpm). Built-in (y) by default,
|
||||||
see the aic8800 port notes in warden-sdk/kernel/rv1106-enablement/wifi/.
|
see the aic8800 port notes in warden-sdk/kernel/rv1106-enablement/wifi/.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0-only
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
#
|
#
|
||||||
# aic8800_btlpm — Bluetooth low-power-mode / HCI wake companion module,
|
# aic8800_btlpm: Bluetooth low-power-mode / HCI wake companion module,
|
||||||
# ported in-tree from the vendor out-of-tree Makefile
|
# ported in-tree from the vendor out-of-tree Makefile
|
||||||
# (flare-edge/sdk/sysdrv/drv_ko/wifi/aic8800dc/aic8800_btlpm/Makefile).
|
# (flare-edge/sdk/sysdrv/drv_ko/wifi/aic8800dc/aic8800_btlpm/Makefile).
|
||||||
# lpm.c stays out of the build (CONFIG_SUPPORT_LPM was already off in the
|
# lpm.c stays out of the build (CONFIG_SUPPORT_LPM was already off in the
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ config AIC8800_WLAN_SUPPORT
|
|||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
This is support for the aic8800 cfg80211 full-MAC wifi driver
|
This is support for the aic8800 cfg80211 full-MAC wifi driver
|
||||||
(aic8800_fdrv). Built-in (y) by default — see the aic8800 port
|
(aic8800_fdrv). Built-in (y) by default, see the aic8800 port
|
||||||
notes in warden-sdk/kernel/rv1106-enablement/wifi/.
|
notes in warden-sdk/kernel/rv1106-enablement/wifi/.
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0-only
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
#
|
#
|
||||||
# aic8800_fdrv — the cfg80211 full-MAC driver ("fdrv"), ported in-tree from
|
# aic8800_fdrv: the cfg80211 full-MAC driver ("fdrv"), ported in-tree from
|
||||||
# the vendor out-of-tree Makefile (flare-edge/sdk/sysdrv/drv_ko/wifi/
|
# the vendor out-of-tree Makefile (flare-edge/sdk/sysdrv/drv_ko/wifi/
|
||||||
# aic8800dc/aic8800_fdrv/Makefile). SDIO-only build (this board has no
|
# aic8800dc/aic8800_fdrv/Makefile). SDIO-only build (this board has no
|
||||||
# USB/PCI variant) — usb_host.c and rwnx_pci.c are dropped from the tree
|
# USB/PCI variant): usb_host.c and rwnx_pci.c are dropped from the tree
|
||||||
# entirely (PORT-PLAN.md §4.1 flagged both as candidates to drop "unless a
|
# entirely (PORT-PLAN.md section 4.1 flagged both as candidates to drop "unless a
|
||||||
# build error proves them referenced elsewhere" — that held for these two).
|
# build error proves them referenced elsewhere", that held for these two).
|
||||||
# rwnx_mesh.c was ALSO dropped initially on the same theory but had to be
|
# rwnx_mesh.c was ALSO dropped initially on the same theory but had to be
|
||||||
# restored: rwnx_tx.c's NL80211_IFTYPE_MESH_POINT switch-case is not
|
# restored: rwnx_tx.c's NL80211_IFTYPE_MESH_POINT switch-case is not
|
||||||
# preprocessor-gated, so rwnx_mesh.h's types/prototypes are needed to
|
# preprocessor-gated, so rwnx_mesh.h's types/prototypes are needed to
|
||||||
# compile even though this board never creates a mesh-type interface.
|
# compile even though this board never creates a mesh-type interface.
|
||||||
# md5.o: fdrv carries its OWN copy (vendor ships md5.c in both aic8800_bsp/ and
|
# md5.o: fdrv carries its OWN copy (vendor ships md5.c in both aic8800_bsp/ and
|
||||||
# aic8800_fdrv/). As separate modules each .ko needs its own MD5 — bsp does not
|
# aic8800_fdrv/). As separate modules each .ko needs its own MD5: bsp does not
|
||||||
# EXPORT_SYMBOL(MD5Init/...), so fdrv.ko cannot resolve them from aic8800_bsp.ko
|
# EXPORT_SYMBOL(MD5Init/...), so fdrv.ko cannot resolve them from aic8800_bsp.ko
|
||||||
# (modpost: "MD5Final undefined"). The two copies are byte-identical and live in
|
# (modpost: "MD5Final undefined"). The two copies are byte-identical and live in
|
||||||
# separate module namespaces, so there is no collision. (When these were briefly
|
# separate module namespaces, so there is no collision. (When these were briefly
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
status = "okay";
|
status = "okay";
|
||||||
};
|
};
|
||||||
|
|
||||||
/* --- M5: AIC8800DC wifi/BT SDIO (on &sdmmc, mmc@ffaa0000 — NOT &sdio) --- */
|
/* --- M5: AIC8800DC wifi/BT SDIO (on &sdmmc, mmc@ffaa0000, NOT &sdio) --- */
|
||||||
&sdmmc {
|
&sdmmc {
|
||||||
max-frequency = <50000000>;
|
max-frequency = <50000000>;
|
||||||
bus-width = <4>;
|
bus-width = <4>;
|
||||||
|
|||||||
@@ -1835,7 +1835,7 @@ index 000000000..418652552
|
|||||||
+#include <dt-bindings/clock/rv1106-cru.h>
|
+#include <dt-bindings/clock/rv1106-cru.h>
|
||||||
+
|
+
|
||||||
+/ {
|
+/ {
|
||||||
+ model = "WardenOS 86-Panel (RV1106) — M2 earlycon bring-up";
|
+ model = "WardenOS 86-Panel (RV1106), M2 earlycon bring-up";
|
||||||
+ compatible = "rockchip,rv1106";
|
+ compatible = "rockchip,rv1106";
|
||||||
+ #address-cells = <1>;
|
+ #address-cells = <1>;
|
||||||
+ #size-cells = <1>;
|
+ #size-cells = <1>;
|
||||||
|
|||||||
+3
-3
@@ -6,8 +6,8 @@ patch applies cleanly onto pristine 6.18.46 (`git apply --check`), and the whole
|
|||||||
reproduces the 223-file / ~136K-line delta that boots and is hardware-verified on
|
reproduces the 223-file / ~136K-line delta that boots and is hardware-verified on
|
||||||
`warden-c8a3` (see `../kernel/rv1106-enablement/`).
|
`warden-c8a3` (see `../kernel/rv1106-enablement/`).
|
||||||
|
|
||||||
Build it with `../build/build-kernel.sh` (fetch pristine → apply these → configure
|
Build it with `../build/build-kernel.sh` (fetch pristine -> apply these -> configure
|
||||||
with `../build/warden_defconfig` → `zImage` + `rv1106-warden.dtb`).
|
with `../build/warden_defconfig` -> `zImage` + `rv1106-warden.dtb`).
|
||||||
|
|
||||||
## The Series
|
## The Series
|
||||||
|
|
||||||
@@ -43,4 +43,4 @@ subsystem. Licensing/origin of each block is tracked in
|
|||||||
|
|
||||||
Whitespace: `60-wifi-aic8800.patch` carries the vendor blob's trailing-whitespace
|
Whitespace: `60-wifi-aic8800.patch` carries the vendor blob's trailing-whitespace
|
||||||
verbatim; `git apply --whitespace=nowarn` (the build wrapper) accepts it. Do not
|
verbatim; `git apply --whitespace=nowarn` (the build wrapper) accepts it. Do not
|
||||||
re-flow vendor code — it must stay diffable against future vendor drops.
|
re-flow vendor code; it must stay diffable against future vendor drops.
|
||||||
|
|||||||
+13
-13
@@ -1,17 +1,17 @@
|
|||||||
# The Device Simulator
|
# The Device Simulator
|
||||||
|
|
||||||
A QEMU virtual machine that boots the real forward-ported kernel and real
|
A QEMU virtual machine that boots the real forward-ported kernel and real
|
||||||
userspace: the 86 Panel — init, daemons, networking, OTA, watchdog, display —
|
userspace: the 86 Panel (init, daemons, networking, OTA, watchdog, display)
|
||||||
developed and tested with no board attached. The third simulator in the stack
|
developed and tested with no board attached. The third simulator in the stack
|
||||||
(three-way split: root README), deliberately not named "sim": it runs the
|
(three-way split: root README), deliberately not named "sim": it runs the
|
||||||
whole machine above the kernel entry point on real binaries — bring your own,
|
whole machine above the kernel entry point on real binaries: bring your own,
|
||||||
or drop prebuilt payloads in `payload/`. Decision record: ADR-0006.
|
or drop prebuilt payloads in `payload/`. Decision record: ADR-0006.
|
||||||
|
|
||||||
## The Boundary
|
## The Boundary
|
||||||
|
|
||||||
There is no RV1106 machine model in QEMU and everything below the kernel is
|
There is no RV1106 machine model in QEMU and everything below the kernel is
|
||||||
closed rkbin blobs plus mask ROM, so the VM **enters at `-kernel zImage`** on
|
closed rkbin blobs plus mask ROM, so the VM **enters at `-kernel zImage`** on
|
||||||
`-M virt,highmem=off` (single Cortex-A7, 256M — the RV1106G3's shape).
|
`-M virt,highmem=off` (single Cortex-A7, 256M: the RV1106G3's shape).
|
||||||
|
|
||||||
| Emulated / substituted | Not emulated (stays bench / `sim/` territory) |
|
| Emulated / substituted | Not emulated (stays bench / `sim/` territory) |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -28,7 +28,7 @@ closed rkbin blobs plus mask ROM, so the VM **enters at `-kernel zImage`** on
|
|||||||
**"Boots/works under emulation" is never evidence of "works on silicon."**
|
**"Boots/works under emulation" is never evidence of "works on silicon."**
|
||||||
The VM narrows which claims need a panel; on-device claims still need
|
The VM narrows which claims need a panel; on-device claims still need
|
||||||
on-device evidence. Conversely, the VM is the first environment that runs
|
on-device evidence. Conversely, the VM is the first environment that runs
|
||||||
production binaries on a non-RV1106 memory map — it found flare-edge #106
|
production binaries on a non-RV1106 memory map: it found flare-edge #106
|
||||||
(fatal SIGBUS in flared's HPMCU probe) and #107 (Y2038 time_t truncation)
|
(fatal SIGBUS in flared's HPMCU probe) and #107 (Y2038 time_t truncation)
|
||||||
on its first two boots of real userspace.
|
on its first two boots of real userspace.
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ bash qemu/run.sh --kernel $HOME/kbuild-out/linux-6.18.46/arch/arm/boot/zImage --
|
|||||||
```
|
```
|
||||||
|
|
||||||
Payload: drop static musl armv7 binaries into `qemu/payload/` (see its
|
Payload: drop static musl armv7 binaries into `qemu/payload/` (see its
|
||||||
README) — `warden-flared`, `warden-modbus`, and `warden-ui` (the LVGL
|
README); `warden-flared`, `warden-modbus`, and `warden-ui` (the LVGL
|
||||||
fbdev+evdev build from flare-edge `tools/build-ui-vm.sh`) are started by
|
fbdev+evdev build from flare-edge `tools/build-ui-vm.sh`) are started by
|
||||||
stage-2 init when present.
|
stage-2 init when present.
|
||||||
|
|
||||||
@@ -64,12 +64,12 @@ All take the virt-fragment `<zImage>`; `FLARE_EDGE=<checkout>` where noted.
|
|||||||
|
|
||||||
| Scenario | Needs | Proves |
|
| Scenario | Needs | Proves |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `boot-smoke.sh` | — | sentinel-asserting boot; runs in CI inside kernel-build |
|
| `boot-smoke.sh` | - | sentinel-asserting boot; runs in CI inside kernel-build |
|
||||||
| `portal-scenario.sh` | `FLARE_EDGE` | real flared against the desk mock portal: authenticated check-in, desired-state pull, signed tier-1 `.wfw` download; verify/stage/APPLYING as a dry run (no `WARDEN_FW_ALLOW_APPLY`) |
|
| `portal-scenario.sh` | `FLARE_EDGE` | real flared against the desk mock portal: authenticated check-in, desired-state pull, signed tier-1 `.wfw` download; verify/stage/APPLYING as a dry run (no `WARDEN_FW_ALLOW_APPLY`) |
|
||||||
| `ota-apply.sh` | `FLARE_EDGE` | the FULL apply: the `.wfw`'s bootable rootfs payload is written to rootfs_b (`run.sh --allow-apply` gates it per boot), the AvbABData in `misc` flips, and slot `_b` boots the applied version |
|
| `ota-apply.sh` | `FLARE_EDGE` | the FULL apply: the `.wfw`'s bootable rootfs payload is written to rootfs_b (`run.sh --allow-apply` gates it per boot), the AvbABData in `misc` flips, and slot `_b` boots the applied version |
|
||||||
| `ui-shot.sh` | — | display+touch, headless: QMP-screendumps the 720x720 UI, taps the Metrics tab via `input-send-event`, asserts the frame changed (`qmp.py` is the QMP client) |
|
| `ui-shot.sh` | - | display+touch, headless: QMP-screendumps the 720x720 UI, taps the Metrics tab via `input-send-event`, asserts the frame changed (`qmp.py` is the QMP client) |
|
||||||
| `real-image-boot.sh` | matched `rootfs.img` + `oem.img` | an ACTUAL flare-edge build (placed by `mkimage.sh --rootfs-image/--oem-image`) boots its own init chain to getty; binaries predating known fixes reproduce their bugs faithfully — a time machine for field issues |
|
| `real-image-boot.sh` | matched `rootfs.img` + `oem.img` | an ACTUAL flare-edge build (placed by `mkimage.sh --rootfs-image/--oem-image`) boots its own init chain to getty; binaries predating known fixes reproduce their bugs faithfully, a time machine for field issues |
|
||||||
| watchdog (`run.sh --watchdog`) | — | arm `/dev/watchdog`, don't pet: the VM resets ~30 s later (verified) |
|
| watchdog (`run.sh --watchdog`) | - | arm `/dev/watchdog`, don't pet: the VM resets ~30 s later (verified) |
|
||||||
|
|
||||||
Scenario fine print:
|
Scenario fine print:
|
||||||
|
|
||||||
@@ -77,18 +77,18 @@ Scenario fine print:
|
|||||||
harness (ADR-0006 boundary); the VM exports `WARDEN_HARD_RESET=0` so
|
harness (ADR-0006 boundary); the VM exports `WARDEN_HARD_RESET=0` so
|
||||||
flared's post-apply reset surfaces as a reported error, not a /dev/mem
|
flared's post-apply reset surfaces as a reported error, not a /dev/mem
|
||||||
fault.
|
fault.
|
||||||
- Touch injection holds 200 ms — an instantaneous press+release lands inside
|
- Touch injection holds 200 ms: an instantaneous press+release lands inside
|
||||||
one LVGL poll and never clicks.
|
one LVGL poll and never clicks.
|
||||||
- Watchdog + a flared payload don't mix: flared pets only while the UI
|
- Watchdog + a flared payload don't mix: flared pets only while the UI
|
||||||
heartbeat is fresh.
|
heartbeat is fresh.
|
||||||
|
|
||||||
## Gotchas
|
## Gotchas
|
||||||
|
|
||||||
- AF_UNIX socket paths cap at ~108 chars — keep `--rs485`/`--qmp` paths short.
|
- AF_UNIX socket paths cap at ~108 chars; keep `--rs485`/`--qmp` paths short.
|
||||||
- A serial port that is closed discards incoming bytes: hold ONE fd open
|
- A serial port that is closed discards incoming bytes: hold ONE fd open
|
||||||
across write and read when scripting the guest side of the RS485 bridge.
|
across write and read when scripting the guest side of the RS485 bridge.
|
||||||
- `highmem=off` and `-global virtio-mmio.force-legacy=false` are load-bearing
|
- `highmem=off` and `-global virtio-mmio.force-legacy=false` are required
|
||||||
(32-bit ECAM reach; virtio-1-only gpu/input) — both live ONLY in run.sh,
|
(32-bit ECAM reach; virtio-1-only gpu/input); both live ONLY in run.sh,
|
||||||
which every script (boot smoke included) delegates to.
|
which every script (boot smoke included) delegates to.
|
||||||
- Never pass `earlyprintk`: DEBUG_UART_PHYS is the RV1106's 0xff4c0000.
|
- Never pass `earlyprintk`: DEBUG_UART_PHYS is the RV1106's 0xff4c0000.
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# The device's canonical 12-partition A/B layout, expressed for the VM's
|
# The device's canonical 12-partition A/B layout, expressed for the VM's
|
||||||
# virtio disk (vda). On hardware the same string names mmcblk0 and is baked
|
# virtio disk (vda). On hardware the same string names mmcblk0 and is baked
|
||||||
# into the U-Boot env — source: flare-edge docs/decisions/0003-partition-layout.md.
|
# into the U-Boot env; source: flare-edge docs/decisions/0003-partition-layout.md.
|
||||||
# There is no MBR/GPT anywhere: U-Boot and Linux both parse this string, which
|
# There is no MBR/GPT anywhere: U-Boot and Linux both parse this string, which
|
||||||
# is why handing it to the VM kernel on the cmdline reproduces the exact
|
# is why handing it to the VM kernel on the cmdline reproduces the exact
|
||||||
# partition map (vda9 = rootfs_a = hardware mmcblk0p9).
|
# partition map (vda9 = rootfs_a = hardware mmcblk0p9).
|
||||||
#
|
#
|
||||||
# Sourced by qemu/mkimage.sh (computes byte offsets from it) and qemu/run.sh
|
# Sourced by qemu/mkimage.sh (computes byte offsets from it) and qemu/run.sh
|
||||||
# (passes it verbatim in -append). Single source of truth — edit only here.
|
# (passes it verbatim in -append). Single source of truth: edit only here.
|
||||||
WARDEN_BLKDEVPARTS='vda:32K(env),512K@32K(idblock),512K(uboot),512K(misc),32M(boot_a),32M(boot_b),128M(oem_a),128M(oem_b),1G(rootfs_a),1G(rootfs_b),32M(recovery),1G(userdata)'
|
WARDEN_BLKDEVPARTS='vda:32K(env),512K@32K(idblock),512K(uboot),512K(misc),32M(boot_a),32M(boot_b),128M(oem_a),128M(oem_b),1G(rootfs_a),1G(rootfs_b),32M(recovery),1G(userdata)'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# QEMU -M virt kernel variant — merged onto build/warden_defconfig via
|
# QEMU -M virt kernel variant, merged onto build/warden_defconfig via
|
||||||
# WARDEN_KCONFIG_FRAGMENT (see build/build-kernel.sh). The RV1106 zImage stays
|
# WARDEN_KCONFIG_FRAGMENT (see build/build-kernel.sh). The RV1106 zImage stays
|
||||||
# canonical and byte-identical when the variable is unset.
|
# canonical and byte-identical when the variable is unset.
|
||||||
#
|
#
|
||||||
@@ -18,7 +18,7 @@ CONFIG_PCI_HOST_GENERIC=y
|
|||||||
# shows up as ttyS0; stage-2 init aliases it to the device's /dev/ttyS4).
|
# shows up as ttyS0; stage-2 init aliases it to the device's /dev/ttyS4).
|
||||||
CONFIG_SERIAL_8250_PCI=y
|
CONFIG_SERIAL_8250_PCI=y
|
||||||
|
|
||||||
# /dev/watchdog for flared's watchdog_loop() — untestable on both existing
|
# /dev/watchdog for flared's watchdog_loop(), untestable on both existing
|
||||||
# sims. i6300esb is the watchdog QEMU offers on arm virt (PCI device):
|
# sims. i6300esb is the watchdog QEMU offers on arm virt (PCI device):
|
||||||
# -device i6300esb -action watchdog=reset.
|
# -device i6300esb -action watchdog=reset.
|
||||||
CONFIG_WATCHDOG=y
|
CONFIG_WATCHDOG=y
|
||||||
@@ -28,7 +28,7 @@ CONFIG_I6300ESB_WDT=y
|
|||||||
CONFIG_WIREGUARD=y
|
CONFIG_WIREGUARD=y
|
||||||
|
|
||||||
# Display + touch: virtio-gpu scanout with fbdev emulation (the VM UI build
|
# Display + touch: virtio-gpu scanout with fbdev emulation (the VM UI build
|
||||||
# uses LVGL's fbdev backend — no libdrm needed in the guest), virtio-tablet
|
# uses LVGL's fbdev backend; no libdrm needed in the guest), virtio-tablet
|
||||||
# for absolute-coordinate touch injection via QMP.
|
# for absolute-coordinate touch injection via QMP.
|
||||||
CONFIG_FB=y
|
CONFIG_FB=y
|
||||||
CONFIG_DRM_VIRTIO_GPU=y
|
CONFIG_DRM_VIRTIO_GPU=y
|
||||||
|
|||||||
+2
-2
@@ -9,7 +9,7 @@ qemu_log() { printf '\033[36m== %s\033[0m\n' "$*"; }
|
|||||||
|
|
||||||
# Fetch (or accept via $BUSYBOX) the pinned static armv7 busybox and verify it
|
# Fetch (or accept via $BUSYBOX) the pinned static armv7 busybox and verify it
|
||||||
# against qemu/busybox.sha256. FAILS CLOSED: a missing pin refuses to build,
|
# against qemu/busybox.sha256. FAILS CLOSED: a missing pin refuses to build,
|
||||||
# never silently skips verification — mirroring build/build-kernel.sh's
|
# never silently skips verification, mirroring build/build-kernel.sh's
|
||||||
# tarball handling. Sets $BB to the verified binary's path.
|
# tarball handling. Sets $BB to the verified binary's path.
|
||||||
qemu_get_busybox() {
|
qemu_get_busybox() {
|
||||||
local sha_file="$QEMU_DIR/busybox.sha256"
|
local sha_file="$QEMU_DIR/busybox.sha256"
|
||||||
@@ -19,7 +19,7 @@ qemu_get_busybox() {
|
|||||||
# Pin first: a missing pin refuses BEFORE downloading, same ordering as
|
# Pin first: a missing pin refuses BEFORE downloading, same ordering as
|
||||||
# build/fetch-kernel-tarball.sh.
|
# build/fetch-kernel-tarball.sh.
|
||||||
[ -f "$sha_file" ] || {
|
[ -f "$sha_file" ] || {
|
||||||
echo "FATAL: no pinned sha256 for busybox (expected $sha_file) — refusing to build from an unverified binary" >&2
|
echo "FATAL: no pinned sha256 for busybox (expected $sha_file): refusing to build from an unverified binary" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
if [ ! -f "$BB" ]; then
|
if [ ! -f "$BB" ]; then
|
||||||
|
|||||||
+4
-4
@@ -1,10 +1,10 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Build the VM's virtio disk image carrying the device's canonical 12-partition
|
# Build the VM's virtio disk image carrying the device's canonical 12-partition
|
||||||
# A/B layout (qemu/blkdevparts.conf — the same string U-Boot and Linux parse on
|
# A/B layout (qemu/blkdevparts.conf: the same string U-Boot and Linux parse on
|
||||||
# hardware; there is no MBR/GPT). Every partition is placed at the exact offset
|
# hardware; there is no MBR/GPT). Every partition is placed at the exact offset
|
||||||
# the cmdline string declares; rootfs_a/rootfs_b/oem_a/oem_b/userdata get ext4,
|
# the cmdline string declares; rootfs_a/rootfs_b/oem_a/oem_b/userdata get ext4,
|
||||||
# the boot-chain partitions (env/idblock/uboot/misc/boot_a/boot_b/recovery)
|
# the boot-chain partitions (env/idblock/uboot/misc/boot_a/boot_b/recovery)
|
||||||
# stay zeroed — the VM enters at -kernel and never reads them.
|
# stay zeroed: the VM enters at -kernel and never reads them.
|
||||||
#
|
#
|
||||||
# Built entirely UNPRIVILEGED: per-partition mkfs.ext4 -d (no loop mounts, no
|
# Built entirely UNPRIVILEGED: per-partition mkfs.ext4 -d (no loop mounts, no
|
||||||
# sudo), then dd'd into a sparse raw image.
|
# sudo), then dd'd into a sparse raw image.
|
||||||
@@ -72,7 +72,7 @@ for p in "$QEMU_DIR"/payload/*; do
|
|||||||
install -m 0755 "$p" "$ROOT/usr/bin/$(basename "$p")"
|
install -m 0755 "$p" "$ROOT/usr/bin/$(basename "$p")"
|
||||||
done
|
done
|
||||||
|
|
||||||
# Firmware version stamp — same path the device build writes; flared reads its
|
# Firmware version stamp: same path the device build writes; flared reads its
|
||||||
# running version here (downgrade rules key off it).
|
# running version here (downgrade rules key off it).
|
||||||
printf '%s\n' "$FW_VERSION" > "$ROOT/etc/warden-firmware-version"
|
printf '%s\n' "$FW_VERSION" > "$ROOT/etc/warden-firmware-version"
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ place_partition() {
|
|||||||
*) echo "FATAL: unknown partition name '$name' in blkdevparts.conf" >&2; exit 1 ;;
|
*) echo "FATAL: unknown partition name '$name' in blkdevparts.conf" >&2; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
DISK_END_TRACK "$off" "$size"
|
DISK_END_TRACK "$off" "$size"
|
||||||
# dd in 4K blocks — every offset in the canonical layout is 4K-aligned;
|
# dd in 4K blocks: every offset in the canonical layout is 4K-aligned;
|
||||||
# assert rather than assume, a misaligned write would corrupt a neighbor.
|
# assert rather than assume, a misaligned write would corrupt a neighbor.
|
||||||
if [ $((off % 4096)) -ne 0 ] || [ $((size % 4096)) -ne 0 ]; then
|
if [ $((off % 4096)) -ne 0 ] || [ $((size % 4096)) -ne 0 ]; then
|
||||||
echo "FATAL: partition $name not 4K-aligned (off=$off size=$size)" >&2
|
echo "FATAL: partition $name not 4K-aligned (off=$off size=$size)" >&2
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Build the QEMU device-sim initramfs: the pinned static busybox + qemu/rootfs/.
|
# Build the QEMU device-sim initramfs: the pinned static busybox + qemu/rootfs/.
|
||||||
# The busybox binary is the ONLY external input (sha256-pinned, fail-closed —
|
# The busybox binary is the ONLY external input (sha256-pinned, fail-closed:
|
||||||
# see qemu/lib.sh).
|
# see qemu/lib.sh).
|
||||||
#
|
#
|
||||||
# Env:
|
# Env:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Guest Payloads
|
# Guest Payloads
|
||||||
|
|
||||||
Drop **static musl armv7** binaries in this directory (contents are
|
Drop **static musl armv7** binaries in this directory (contents are
|
||||||
gitignored — binaries are never committed); `qemu/mkimage.sh` copies everything
|
gitignored; binaries are never committed); `qemu/mkimage.sh` copies everything
|
||||||
in this directory (except this README) into `/usr/bin/` of both rootfs slots.
|
in this directory (except this README) into `/usr/bin/` of both rootfs slots.
|
||||||
Static musl is the same target the device uses for its Rust daemons, so the
|
Static musl is the same target the device uses for its Rust daemons, so the
|
||||||
exact production binaries run unmodified in the VM.
|
exact production binaries run unmodified in the VM.
|
||||||
@@ -21,6 +21,6 @@ cp <flare-edge>/target/armv7-unknown-linux-musleabihf/release/warden-flared qemu
|
|||||||
```
|
```
|
||||||
|
|
||||||
Stage-2 init starts `warden-flared`, `warden-modbus`, and `warden-ui` (the
|
Stage-2 init starts `warden-flared`, `warden-modbus`, and `warden-ui` (the
|
||||||
UI additionally needs `--display on|headless` + the virt.fragment kernel for
|
UI also needs `--display on|headless` + the virt.fragment kernel for
|
||||||
/dev/fb0) automatically when present (logs land in `/tmp/<name>.log` inside
|
/dev/fb0) automatically when present (logs land in `/tmp/<name>.log` inside
|
||||||
the guest). An empty payload is valid — the image boots busybox-only.
|
the guest). An empty payload is valid: the image boots busybox-only.
|
||||||
|
|||||||
+6
-6
@@ -1,13 +1,13 @@
|
|||||||
#!/bin/busybox sh
|
#!/bin/busybox sh
|
||||||
# shellcheck shell=dash
|
# shellcheck shell=dash
|
||||||
# Stage-1 rc: sourced by /init (still PID 1, initramfs root) when a virtio
|
# Stage-1 rc: sourced by /init (still PID 1, initramfs root) when a virtio
|
||||||
# disk is present. Emulates U-Boot's slot choice — mount the validated slot's
|
# disk is present. Emulates U-Boot's slot choice: mount the validated slot's
|
||||||
# rootfs and switch_root into it. This is an EMULATION of the A/B selection
|
# rootfs and switch_root into it. This is an EMULATION of the A/B selection
|
||||||
# outcome, not the BCB/bootcount mechanism itself.
|
# outcome, not the BCB/bootcount mechanism itself.
|
||||||
#
|
#
|
||||||
# Every guarded failure path `return`s to /init (valid in a sourced script;
|
# Every guarded failure path `return`s to /init (valid in a sourced script;
|
||||||
# /init then falls through to shell/poweroff). The final exec is the one
|
# /init then falls through to shell/poweroff). The final exec is the one
|
||||||
# unguardable step: if switch_root itself fails to launch, the shell — PID 1 —
|
# unguardable step: if switch_root itself fails to launch, the shell (PID 1)
|
||||||
# exits and the kernel panics; the applet-existence check below catches the
|
# exits and the kernel panics; the applet-existence check below catches the
|
||||||
# only preventable variant of that.
|
# only preventable variant of that.
|
||||||
|
|
||||||
@@ -19,22 +19,22 @@ slot="$(warden_slot)"
|
|||||||
|
|
||||||
root="/dev/block/by-name/rootfs${slot}"
|
root="/dev/block/by-name/rootfs${slot}"
|
||||||
if [ ! -e "$root" ]; then
|
if [ ! -e "$root" ]; then
|
||||||
echo "rc: $root missing — staying in initramfs"
|
echo "rc: $root missing: staying in initramfs"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p /mnt
|
mkdir -p /mnt
|
||||||
if ! mount -t ext4 "$root" /mnt; then
|
if ! mount -t ext4 "$root" /mnt; then
|
||||||
echo "rc: mount of $root failed — staying in initramfs"
|
echo "rc: mount of $root failed: staying in initramfs"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
if [ ! -x /mnt/sbin/init ]; then
|
if [ ! -x /mnt/sbin/init ]; then
|
||||||
echo "rc: $root has no /sbin/init — staying in initramfs"
|
echo "rc: $root has no /sbin/init: staying in initramfs"
|
||||||
umount /mnt
|
umount /mnt
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
if ! command -v switch_root >/dev/null; then
|
if ! command -v switch_root >/dev/null; then
|
||||||
echo "rc: busybox lacks switch_root — staying in initramfs"
|
echo "rc: busybox lacks switch_root: staying in initramfs"
|
||||||
umount /mnt
|
umount /mnt
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# shellcheck shell=sh
|
# shellcheck shell=sh
|
||||||
# Shared helpers for the VM's stage-1 (/init + /etc/rc, initramfs) and stage-2
|
# Shared helpers for the VM's stage-1 (/init + /etc/rc, initramfs) and stage-2
|
||||||
# (/sbin/init, disk rootfs) boot scripts. Present in both filesystems because
|
# (/sbin/init, disk rootfs) boot scripts. Present in both filesystems because
|
||||||
# both are staged from the same qemu/rootfs/ skeleton. ONE copy of each rule —
|
# both are staged from the same qemu/rootfs/ skeleton. ONE copy of each rule:
|
||||||
# the slot-validation drift between two hand-copied parsers was a real
|
# the slot-validation drift between two hand-copied parsers was a real
|
||||||
# review finding.
|
# review finding.
|
||||||
|
|
||||||
# Populate /dev/block/by-name/<PARTNAME> symlinks from sysfs uevents — the
|
# Populate /dev/block/by-name/<PARTNAME> symlinks from sysfs uevents: the
|
||||||
# contract flare-edge's slotctl.rs relies on. blkdevparts= gives every vda
|
# contract flare-edge's slotctl.rs relies on. blkdevparts= gives every vda
|
||||||
# partition a PARTNAME.
|
# partition a PARTNAME.
|
||||||
warden_populate_by_name() {
|
warden_populate_by_name() {
|
||||||
@@ -26,7 +26,7 @@ warden_populate_by_name() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Parse warden.slot= from the cmdline (whole-token, never substring) and
|
# Parse warden.slot= from the cmdline (whole-token, never substring) and
|
||||||
# VALIDATE it — echoes "_a" or "_b", falling back to _a with a warning.
|
# VALIDATE it: echoes "_a" or "_b", falling back to _a with a warning.
|
||||||
warden_slot() {
|
warden_slot() {
|
||||||
slot="_a"
|
slot="_a"
|
||||||
# shellcheck disable=SC2013 # cmdline TOKENS are the unit here, not lines
|
# shellcheck disable=SC2013 # cmdline TOKENS are the unit here, not lines
|
||||||
|
|||||||
+2
-2
@@ -2,7 +2,7 @@
|
|||||||
# shellcheck shell=dash
|
# shellcheck shell=dash
|
||||||
# WardenOS QEMU device sim: initramfs /init (PID 1).
|
# WardenOS QEMU device sim: initramfs /init (PID 1).
|
||||||
#
|
#
|
||||||
# Phase-1 duty: prove the kernel booted on -M virt — print the sentinel the
|
# Phase-1 duty: prove the kernel booted on -M virt; print the sentinel the
|
||||||
# smoke test greps for, then power off (PSCI SYSTEM_OFF, so qemu exits).
|
# smoke test greps for, then power off (PSCI SYSTEM_OFF, so qemu exits).
|
||||||
# `warden.shell` on the kernel cmdline drops to an interactive shell instead.
|
# `warden.shell` on the kernel cmdline drops to an interactive shell instead.
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ mount -t sysfs sysfs /sys
|
|||||||
echo "WARDEN-QEMU-BOOT-OK"
|
echo "WARDEN-QEMU-BOOT-OK"
|
||||||
|
|
||||||
# With a virtio disk attached, hand over to the stage-1 rc (by-name symlinks,
|
# With a virtio disk attached, hand over to the stage-1 rc (by-name symlinks,
|
||||||
# slot select, switch_root). It only returns on failure — then fall through to
|
# slot select, switch_root). It only returns on failure; then fall through to
|
||||||
# the diskless shell/poweroff behavior below.
|
# the diskless shell/poweroff behavior below.
|
||||||
if [ -b /dev/vda ]; then
|
if [ -b /dev/vda ]; then
|
||||||
# shellcheck source=qemu/rootfs/etc/rc disable=SC1091
|
# shellcheck source=qemu/rootfs/etc/rc disable=SC1091
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# shellcheck shell=dash
|
# shellcheck shell=dash
|
||||||
# Stage-2 init: PID 1 on the disk rootfs (rootfs_a or rootfs_b), reached via
|
# Stage-2 init: PID 1 on the disk rootfs (rootfs_a or rootfs_b), reached via
|
||||||
# switch_root from the initramfs. Brings up the minimum a WardenOS userspace
|
# switch_root from the initramfs. Brings up the minimum a WardenOS userspace
|
||||||
# needs — mounts, by-name symlinks, network, serial alias — then starts any
|
# needs (mounts, by-name symlinks, network, serial alias), then starts any
|
||||||
# payload daemons and holds. This stands in for the device's BusyBox SysV
|
# payload daemons and holds. This stands in for the device's BusyBox SysV
|
||||||
# /etc/init.d/S* sequence; it is deliberately tiny, not a model of it.
|
# /etc/init.d/S* sequence; it is deliberately tiny, not a model of it.
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ mount -t tmpfs tmpfs /tmp
|
|||||||
# shellcheck source=qemu/rootfs/etc/warden-lib.sh disable=SC1091
|
# shellcheck source=qemu/rootfs/etc/warden-lib.sh disable=SC1091
|
||||||
. /etc/warden-lib.sh
|
. /etc/warden-lib.sh
|
||||||
|
|
||||||
# Fresh devtmpfs — repopulate the by-name contract; same VALIDATED slot rule
|
# Fresh devtmpfs: repopulate the by-name contract; same VALIDATED slot rule
|
||||||
# as stage 1 (shared helper, so the two can never drift).
|
# as stage 1 (shared helper, so the two can never drift).
|
||||||
warden_populate_by_name
|
warden_populate_by_name
|
||||||
slot="$(warden_slot)"
|
slot="$(warden_slot)"
|
||||||
@@ -45,7 +45,7 @@ mkdir -p /userdata/warden
|
|||||||
[ -c /dev/ttyS0 ] && ln -sf /dev/ttyS0 /dev/ttyS4
|
[ -c /dev/ttyS0 ] && ln -sf /dev/ttyS0 /dev/ttyS4
|
||||||
|
|
||||||
# Network: slirp user-mode net on eth0 (DHCP, fallback to QEMU's static map).
|
# Network: slirp user-mode net on eth0 (DHCP, fallback to QEMU's static map).
|
||||||
# The fallback keys off the interface actually having an address — udhcpc
|
# The fallback keys off the interface actually having an address: udhcpc
|
||||||
# exiting 0 only proves a lease, not that the hook script applied it.
|
# exiting 0 only proves a lease, not that the hook script applied it.
|
||||||
ip link set lo up
|
ip link set lo up
|
||||||
if [ -e /sys/class/net/eth0 ]; then
|
if [ -e /sys/class/net/eth0 ]; then
|
||||||
@@ -64,7 +64,7 @@ echo "WARDEN-QEMU-ROOTFS-OK slot=${slot}"
|
|||||||
|
|
||||||
# Payload daemons (dropped into /usr/bin by qemu/mkimage.sh from qemu/payload/).
|
# Payload daemons (dropped into /usr/bin by qemu/mkimage.sh from qemu/payload/).
|
||||||
# WARDEN_FLARE_INSECURE=1: the VM's portal is the desk mock over plain HTTP.
|
# WARDEN_FLARE_INSECURE=1: the VM's portal is the desk mock over plain HTTP.
|
||||||
# This is a dev instrument — a production device build never sets it.
|
# This is a dev instrument: a production device build never sets it.
|
||||||
export WARDEN_FLARE_INSECURE=1
|
export WARDEN_FLARE_INSECURE=1
|
||||||
# No HPMCU on -M virt: the mailbox SRAM (0xff6fff00) is unmapped bus space
|
# No HPMCU on -M virt: the mailbox SRAM (0xff6fff00) is unmapped bus space
|
||||||
# here, and flared's /dev/mem poke dies with an external abort (SIGBUS). The
|
# here, and flared's /dev/mem poke dies with an external abort (SIGBUS). The
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name = "warden-rs485-bridge"
|
name = "warden-rs485-bridge"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Bridges a QEMU serial chardev (unix socket) to the warden-sim Modbus RTU slave, so the VM guest's RS-485 master polls the same simulated field bus the unit tests do — fault injection included."
|
description = "Bridges a QEMU serial chardev (unix socket) to the warden-sim Modbus RTU slave, so the VM guest's RS-485 master polls the same simulated field bus the unit tests do, fault injection included."
|
||||||
license = "GPL-2.0-only"
|
license = "GPL-2.0-only"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Micro-benchmarks for the RS-485 bridge dispatch path — same dependency-free
|
//! Micro-benchmarks for the RS-485 bridge dispatch path: same dependency-free
|
||||||
//! fixed-iteration pattern as sim/benches/sim_bench.rs: human timings to
|
//! fixed-iteration pattern as sim/benches/sim_bench.rs: human timings to
|
||||||
//! stdout, one JSON line per benchmark to stderr for CI trend capture.
|
//! stdout, one JSON line per benchmark to stderr for CI trend capture.
|
||||||
//!
|
//!
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
//! what it believes is /dev/ttyS4); this bridge is the wire and every slave on
|
//! what it believes is /dev/ttyS4); this bridge is the wire and every slave on
|
||||||
//! it. Frames are delimited by an inter-frame gap of silence: RTU's 3.5-char
|
//! it. Frames are delimited by an inter-frame gap of silence: RTU's 3.5-char
|
||||||
//! rule cannot survive a socket transport, so a wall-clock gap stands in for it.
|
//! rule cannot survive a socket transport, so a wall-clock gap stands in for it.
|
||||||
//! A mis-split frame fails CRC inside `handle_frame`, which answers `None` —
|
//! A mis-split frame fails CRC inside `handle_frame`, which answers `None`
|
||||||
//! exactly a real slave staying silent — and the master already treats silence
|
//! (exactly a real slave staying silent), and the master already treats silence
|
||||||
//! as a timeout, so the failure mode degrades to a dropped poll, never a
|
//! as a timeout, so the failure mode degrades to a dropped poll, never a
|
||||||
//! phantom reply.
|
//! phantom reply.
|
||||||
//!
|
//!
|
||||||
@@ -26,7 +26,7 @@ pub const DEFAULT_GAP: Duration = Duration::from_millis(10);
|
|||||||
|
|
||||||
/// Accumulation cap: a Modbus RTU ADU is at most 256 bytes, so anything past
|
/// Accumulation cap: a Modbus RTU ADU is at most 256 bytes, so anything past
|
||||||
/// 2x that without an inter-frame gap is a misbehaving master streaming
|
/// 2x that without an inter-frame gap is a misbehaving master streaming
|
||||||
/// continuously — drop the buffer instead of growing without bound.
|
/// continuously. Drop the buffer instead of growing without bound.
|
||||||
const MAX_PENDING: usize = 512;
|
const MAX_PENDING: usize = 512;
|
||||||
|
|
||||||
/// The shared bus: the slave plus its declared dimensions. The sim's register
|
/// The shared bus: the slave plus its declared dimensions. The sim's register
|
||||||
@@ -75,11 +75,11 @@ pub fn pump_serial(
|
|||||||
if buf.len() > MAX_PENDING {
|
if buf.len() > MAX_PENDING {
|
||||||
// Rate-limit the log and back off for one gap so a master
|
// Rate-limit the log and back off for one gap so a master
|
||||||
// streaming continuously cannot peg a core and flood
|
// streaming continuously cannot peg a core and flood
|
||||||
// stderr — mirroring the accept-loop backoff.
|
// stderr, mirroring the accept-loop backoff.
|
||||||
discards += 1;
|
discards += 1;
|
||||||
if discards == 1 || discards.is_multiple_of(256) {
|
if discards == 1 || discards.is_multiple_of(256) {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"rs485: {} bytes buffered with no inter-frame gap — \
|
"rs485: {} bytes buffered with no inter-frame gap, \
|
||||||
discarding (misbehaving master? {} discards so far)",
|
discarding (misbehaving master? {} discards so far)",
|
||||||
buf.len(),
|
buf.len(),
|
||||||
discards
|
discards
|
||||||
@@ -152,7 +152,7 @@ pub fn handle_control_line(line: &str, bus: &Bus) -> String {
|
|||||||
return format!("err trailing arguments after '{cmd}'");
|
return format!("err trailing arguments after '{cmd}'");
|
||||||
}
|
}
|
||||||
// Each arm states its own bound (bus.regs for register space, bus.bits for
|
// Each arm states its own bound (bus.regs for register space, bus.bits for
|
||||||
// bit space) INLINE — a previous string-keyed lookup defaulted silently to
|
// bit space) INLINE: a previous string-keyed lookup defaulted silently to
|
||||||
// the bit bound, which would have handed a future `get-input` command the
|
// the bit bound, which would have handed a future `get-input` command the
|
||||||
// wrong range and reintroduced the out-of-range panic this check prevents.
|
// wrong range and reintroduced the out-of-range panic this check prevents.
|
||||||
let mut s = bus.slave.lock().unwrap();
|
let mut s = bus.slave.lock().unwrap();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//! control listener.
|
//! control listener.
|
||||||
//!
|
//!
|
||||||
//! Typical use (matches qemu/run.sh --rs485). Put the sockets in a private
|
//! Typical use (matches qemu/run.sh --rs485). Put the sockets in a private
|
||||||
//! per-run directory (mktemp -d) — short (AF_UNIX caps paths at ~108 chars)
|
//! per-run directory (mktemp -d): short (AF_UNIX caps paths at ~108 chars)
|
||||||
//! and not guessable/pre-creatable by other local users, unlike a fixed
|
//! and not guessable/pre-creatable by other local users, unlike a fixed
|
||||||
//! /tmp name:
|
//! /tmp name:
|
||||||
//!
|
//!
|
||||||
@@ -40,7 +40,7 @@ fn main() {
|
|||||||
eprintln!("{name} needs a value");
|
eprintln!("{name} needs a value");
|
||||||
usage()
|
usage()
|
||||||
});
|
});
|
||||||
// A following flag means the value was omitted — report the real
|
// A following flag means the value was omitted. Report the real
|
||||||
// problem instead of swallowing the flag as a bogus value.
|
// problem instead of swallowing the flag as a bogus value.
|
||||||
if v.starts_with("--") {
|
if v.starts_with("--") {
|
||||||
eprintln!("{name} needs a value, got flag '{v}'");
|
eprintln!("{name} needs a value, got flag '{v}'");
|
||||||
@@ -70,7 +70,7 @@ fn main() {
|
|||||||
if let Some(path) = control {
|
if let Some(path) = control {
|
||||||
// Clear a stale socket from a previous run. A failure here that is not
|
// Clear a stale socket from a previous run. A failure here that is not
|
||||||
// "nothing to remove" (e.g. someone else's file behind /tmp's sticky
|
// "nothing to remove" (e.g. someone else's file behind /tmp's sticky
|
||||||
// bit) will make the bind below fail — surface both errors.
|
// bit) will make the bind below fail. Surface both errors.
|
||||||
let removed = std::fs::remove_file(&path);
|
let removed = std::fs::remove_file(&path);
|
||||||
let listener = UnixListener::bind(&path).unwrap_or_else(|e| {
|
let listener = UnixListener::bind(&path).unwrap_or_else(|e| {
|
||||||
eprintln!("FATAL: cannot bind control socket {path}: {e}");
|
eprintln!("FATAL: cannot bind control socket {path}: {e}");
|
||||||
@@ -89,7 +89,7 @@ fn main() {
|
|||||||
let conn = match conn {
|
let conn = match conn {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("rs485: control accept failed: {e} — backing off");
|
eprintln!("rs485: control accept failed: {e}, backing off");
|
||||||
std::thread::sleep(Duration::from_millis(200));
|
std::thread::sleep(Duration::from_millis(200));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-7
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Launch the WardenOS device VM (qemu-system-arm -M virt, single Cortex-A7,
|
# Launch the WardenOS device VM (qemu-system-arm -M virt, single Cortex-A7,
|
||||||
# 256M — the RV1106G3's shape). See qemu/README.md for what this does and does
|
# 256M: the RV1106G3's shape). See qemu/README.md for what this does and does
|
||||||
# not emulate.
|
# not emulate.
|
||||||
#
|
#
|
||||||
# Usage: run.sh --kernel <zImage> [options] [-- <extra qemu args>]
|
# Usage: run.sh --kernel <zImage> [options] [-- <extra qemu args>]
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
# when present; pass --no-disk for a diskless initramfs boot)
|
# when present; pass --no-disk for a diskless initramfs boot)
|
||||||
# --no-disk boot without a disk (initramfs shell/smoke behavior)
|
# --no-disk boot without a disk (initramfs shell/smoke behavior)
|
||||||
# --slot _a|_b rootfs slot to boot (default _a)
|
# --slot _a|_b rootfs slot to boot (default _a)
|
||||||
# --rtc DATE guest RTC base, e.g. 2021-01-01 — reproduces the no-RTC
|
# --rtc DATE guest RTC base, e.g. 2021-01-01: reproduces the no-RTC
|
||||||
# "device boots believing 2021" incident class
|
# "device boots believing 2021" incident class
|
||||||
# --rs485 SOCK unix socket chardev for the RS485/Modbus bridge
|
# --rs485 SOCK unix socket chardev for the RS485/Modbus bridge
|
||||||
# (pci-serial: needs the virt.fragment kernel)
|
# (pci-serial: needs the virt.fragment kernel)
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
# --api-port N hostfwd 127.0.0.1:N -> guest :28443 (default 28443; 0 disables)
|
# --api-port N hostfwd 127.0.0.1:N -> guest :28443 (default 28443; 0 disables)
|
||||||
# --shell interactive shell in the guest instead of daemon hold
|
# --shell interactive shell in the guest instead of daemon hold
|
||||||
# --allow-apply let flared ACTUALLY apply OTA firmware (writes rootfs_b
|
# --allow-apply let flared ACTUALLY apply OTA firmware (writes rootfs_b
|
||||||
# inside disk.img — safe in the VM, never the default)
|
# inside disk.img, safe in the VM, never the default)
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
QEMU_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
QEMU_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
@@ -63,7 +63,7 @@ if [ -z "$KERNEL" ] || [ ! -f "$KERNEL" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
[ -f "$INITRD" ] || {
|
[ -f "$INITRD" ] || {
|
||||||
echo "FATAL: initramfs not found at $INITRD — run qemu/mkinitramfs.sh" >&2
|
echo "FATAL: initramfs not found at $INITRD: run qemu/mkinitramfs.sh" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
case "$SLOT" in _a|_b) ;; *) echo "FATAL: --slot must be _a or _b" >&2; exit 1 ;; esac
|
case "$SLOT" in _a|_b) ;; *) echo "FATAL: --slot must be _a or _b" >&2; exit 1 ;; esac
|
||||||
@@ -72,14 +72,14 @@ if [ "$NO_DISK" -eq 0 ] && [ -z "$DISK" ] && [ -f "$OUT/disk.img" ]; then
|
|||||||
DISK="$OUT/disk.img"
|
DISK="$OUT/disk.img"
|
||||||
fi
|
fi
|
||||||
if [ -n "$DISK" ] && [ ! -f "$DISK" ]; then
|
if [ -n "$DISK" ] && [ ! -f "$DISK" ]; then
|
||||||
echo "FATAL: disk image $DISK not found — run qemu/mkimage.sh (or pass --no-disk)" >&2
|
echo "FATAL: disk image $DISK not found: run qemu/mkimage.sh (or pass --no-disk)" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# NOTE: never add `earlyprintk` — the config's DEBUG_UART_PHYS is the RV1106's
|
# NOTE: never add `earlyprintk`: the config's DEBUG_UART_PHYS is the RV1106's
|
||||||
# 0xff4c0000, which does not exist on -M virt.
|
# 0xff4c0000, which does not exist on -M virt.
|
||||||
APPEND="console=ttyAMA0 rdinit=/init"
|
APPEND="console=ttyAMA0 rdinit=/init"
|
||||||
# Port 0 disables a forward — a boot smoke needs no host ports and must not
|
# Port 0 disables a forward. A boot smoke needs no host ports and must not
|
||||||
# fail on a busy default port.
|
# fail on a busy default port.
|
||||||
NETDEV="user,id=n0"
|
NETDEV="user,id=n0"
|
||||||
[ "$SSH_PORT" != 0 ] && NETDEV="$NETDEV,hostfwd=tcp:127.0.0.1:${SSH_PORT}-:22"
|
[ "$SSH_PORT" != 0 ] && NETDEV="$NETDEV,hostfwd=tcp:127.0.0.1:${SSH_PORT}-:22"
|
||||||
|
|||||||
@@ -11,16 +11,16 @@ QDIR="$(cd "$HERE/.." && pwd)" # qemu/
|
|||||||
|
|
||||||
ZIMAGE="${1:-}"
|
ZIMAGE="${1:-}"
|
||||||
if [ -z "$ZIMAGE" ] || [ ! -f "$ZIMAGE" ]; then
|
if [ -z "$ZIMAGE" ] || [ ! -f "$ZIMAGE" ]; then
|
||||||
echo "FATAL: usage: $0 <zImage> [initramfs] — zImage missing or not a file: '${ZIMAGE:-}'" >&2
|
echo "FATAL: usage: $0 <zImage> [initramfs]: zImage missing or not a file: '${ZIMAGE:-}'" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
INITRD="${2:-$QDIR/out/initramfs.cpio.gz}"
|
INITRD="${2:-$QDIR/out/initramfs.cpio.gz}"
|
||||||
[ -f "$INITRD" ] || {
|
[ -f "$INITRD" ] || {
|
||||||
echo "FATAL: initramfs not found at $INITRD — run qemu/mkinitramfs.sh first" >&2
|
echo "FATAL: initramfs not found at $INITRD: run qemu/mkinitramfs.sh first" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
command -v qemu-system-arm >/dev/null || {
|
command -v qemu-system-arm >/dev/null || {
|
||||||
echo "FATAL: qemu-system-arm not on PATH (apt-get install qemu-system-arm) — see qemu/README.md" >&2
|
echo "FATAL: qemu-system-arm not on PATH (apt-get install qemu-system-arm): see qemu/README.md" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ trap 'rm -f "$LOG"' EXIT
|
|||||||
|
|
||||||
# Delegate the qemu invocation to run.sh (--no-disk) so the machine shape
|
# Delegate the qemu invocation to run.sh (--no-disk) so the machine shape
|
||||||
# (-M virt,highmem=off, cpu, memory, virtio topology) lives in exactly one
|
# (-M virt,highmem=off, cpu, memory, virtio topology) lives in exactly one
|
||||||
# place — the two hand-copied invocations had already drifted once.
|
# place: the two hand-copied invocations had already drifted once.
|
||||||
# timeout -k: a wedged qemu that ignores SIGTERM gets SIGKILLed 10s later
|
# timeout -k: a wedged qemu that ignores SIGTERM gets SIGKILLed 10s later
|
||||||
# instead of holding the job until the workflow-level timeout.
|
# instead of holding the job until the workflow-level timeout.
|
||||||
timeout -k 10 180 bash "$QDIR/run.sh" \
|
timeout -k 10 180 bash "$QDIR/run.sh" \
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# Clock-sanity scenario (issue #3 regression guard): boot the VM, run the
|
# Clock-sanity scenario (issue #3 regression guard): boot the VM, run the
|
||||||
# musl-static clockprobe in the guest, and assert the vDSO monotonic RATE
|
# musl-static clockprobe in the guest, and assert the vDSO monotonic RATE
|
||||||
# matches the kernel's /proc/uptime within 1%. Under QEMU -M virt this passes
|
# matches the kernel's /proc/uptime within 1%. Under QEMU -M virt this passes
|
||||||
# on the current kernel (measured 0.99963) — a regression here means the
|
# on the current kernel (measured 0.99963): a regression here means the
|
||||||
# generic vDSO path broke. The RV1106 *board* leg of issue #3 is a separate,
|
# generic vDSO path broke. The RV1106 *board* leg of issue #3 is a separate,
|
||||||
# bench-only measurement; this scenario cannot see board-specific CNTFRQ or
|
# bench-only measurement; this scenario cannot see board-specific CNTFRQ or
|
||||||
# CNTVOFF misprogramming.
|
# CNTVOFF misprogramming.
|
||||||
@@ -21,7 +21,7 @@ if [ -z "$ZIMAGE" ] || [ ! -f "$ZIMAGE" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
command -v qemu-system-arm >/dev/null || {
|
command -v qemu-system-arm >/dev/null || {
|
||||||
echo "FATAL: qemu-system-arm not on PATH — see qemu/README.md" >&2
|
echo "FATAL: qemu-system-arm not on PATH: see qemu/README.md" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
command -v arm-linux-gnueabihf-gcc >/dev/null || {
|
command -v arm-linux-gnueabihf-gcc >/dev/null || {
|
||||||
@@ -58,7 +58,7 @@ for _attempt in 1 2 3; do
|
|||||||
sleep 3
|
sleep 3
|
||||||
kill -0 "$QEMU_PID" 2>/dev/null && break
|
kill -0 "$QEMU_PID" 2>/dev/null && break
|
||||||
if grep -aq 'Could not set up host forwarding' "$WORK/console.log"; then
|
if grep -aq 'Could not set up host forwarding' "$WORK/console.log"; then
|
||||||
echo "== hostfwd port collision on base $PORT — retrying"
|
echo "== hostfwd port collision on base $PORT, retrying"
|
||||||
QEMU_PID=""
|
QEMU_PID=""
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# FULL OTA apply scenario — the loop the desk e2e stops short of: the real
|
# FULL OTA apply scenario (the loop the desk e2e stops short of): the real
|
||||||
# flared inside the VM downloads a real signed tier-1 .wfw whose payload is a
|
# flared inside the VM downloads a real signed tier-1 .wfw whose payload is a
|
||||||
# bootable rootfs, verifies it, and ACTUALLY WRITES rootfs_b (safe: it is a
|
# bootable rootfs, verifies it, and ACTUALLY WRITES rootfs_b (safe: it is a
|
||||||
# region inside disk.img); the harness then reboots into slot _b and asserts
|
# region inside disk.img); the harness then reboots into slot _b and asserts
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
#
|
#
|
||||||
# Documented emulation gaps (ADR-0006): the BCB slot CHOICE and the physical
|
# Documented emulation gaps (ADR-0006): the BCB slot CHOICE and the physical
|
||||||
# reset are performed by the harness (cmdline slot + a fresh qemu boot), not
|
# reset are performed by the harness (cmdline slot + a fresh qemu boot), not
|
||||||
# by U-Boot/CRU — those stay bench territory.
|
# by U-Boot/CRU. Those stay bench territory.
|
||||||
#
|
#
|
||||||
# FAILS CLOSED on missing prerequisites.
|
# FAILS CLOSED on missing prerequisites.
|
||||||
#
|
#
|
||||||
@@ -22,7 +22,7 @@ PATH="$PATH:/usr/sbin:/sbin"
|
|||||||
|
|
||||||
ZIMAGE="${1:-}"
|
ZIMAGE="${1:-}"
|
||||||
if [ -z "$ZIMAGE" ] || [ ! -f "$ZIMAGE" ]; then
|
if [ -z "$ZIMAGE" ] || [ ! -f "$ZIMAGE" ]; then
|
||||||
echo "FATAL: usage: $0 <zImage> — the virt.fragment kernel variant" >&2
|
echo "FATAL: usage: $0 <zImage>: the virt.fragment kernel variant" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ -z "${FLARE_EDGE:-}" ] || [ ! -f "$FLARE_EDGE/tools/mock-flare-portal.py" ]; then
|
if [ -z "${FLARE_EDGE:-}" ] || [ ! -f "$FLARE_EDGE/tools/mock-flare-portal.py" ]; then
|
||||||
@@ -34,7 +34,7 @@ fi
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
command -v qemu-system-arm >/dev/null || {
|
command -v qemu-system-arm >/dev/null || {
|
||||||
echo "FATAL: qemu-system-arm not on PATH — see qemu/README.md" >&2
|
echo "FATAL: qemu-system-arm not on PATH: see qemu/README.md" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ API_KEY="$(python3 -c 'import secrets; print(secrets.token_hex(24))')"
|
|||||||
PORT=$((20000 + RANDOM % 20000))
|
PORT=$((20000 + RANDOM % 20000))
|
||||||
|
|
||||||
# 0. The offer's payload is a REAL bootable rootfs: the same skeleton the
|
# 0. The offer's payload is a REAL bootable rootfs: the same skeleton the
|
||||||
# disk uses, stamped with the NEW version — booting it is the proof.
|
# disk uses, stamped with the NEW version: booting it is the proof.
|
||||||
QEMU_DIR="$QDIR"
|
QEMU_DIR="$QDIR"
|
||||||
OUT="$QDIR/out"
|
OUT="$QDIR/out"
|
||||||
# shellcheck source=qemu/lib.sh disable=SC1091
|
# shellcheck source=qemu/lib.sh disable=SC1091
|
||||||
@@ -105,7 +105,7 @@ for _attempt in 1 2 3; do
|
|||||||
sleep 3
|
sleep 3
|
||||||
kill -0 "$QEMU_PID" 2>/dev/null && break
|
kill -0 "$QEMU_PID" 2>/dev/null && break
|
||||||
if grep -aq 'Could not set up host forwarding' "$WORK/console.log"; then
|
if grep -aq 'Could not set up host forwarding' "$WORK/console.log"; then
|
||||||
echo "== hostfwd port collision on base $VMBASE — retrying"
|
echo "== hostfwd port collision on base $VMBASE, retrying"
|
||||||
QEMU_PID=""
|
QEMU_PID=""
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
@@ -117,7 +117,7 @@ if [ -z "$QEMU_PID" ] || ! kill -0 "$QEMU_PID" 2>/dev/null; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# 4. wait for the apply to conclude. flared logs to its in-guest file, not
|
# 4. wait for the apply to conclude. flared logs to its in-guest file, not
|
||||||
# the console — but the next check-in REPORTS the outcome to the portal:
|
# the console, but the next check-in REPORTS the outcome to the portal:
|
||||||
# detail "hard reset failed after slot flip" is the exact post-apply state
|
# detail "hard reset failed after slot flip" is the exact post-apply state
|
||||||
# under the gated reset (write done, AvbABData flipped, reboot refused).
|
# under the gated reset (write done, AvbABData flipped, reboot refused).
|
||||||
deadline=$((SECONDS + 420))
|
deadline=$((SECONDS + 420))
|
||||||
@@ -140,7 +140,7 @@ grep -aq "GET /api/v1/devices/$DEVICE_ID/firmware/assets/.* -> 200" "$WORK/mock.
|
|||||||
echo "FATAL: staged without a portal asset download?!" >&2
|
echo "FATAL: staged without a portal asset download?!" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
echo "== apply staged (asset downloaded, rootfs_b written, reset gated) — rebooting into _b"
|
echo "== apply staged (asset downloaded, rootfs_b written, reset gated), rebooting into _b"
|
||||||
kill "$QEMU_PID" 2>/dev/null || true
|
kill "$QEMU_PID" 2>/dev/null || true
|
||||||
wait "$QEMU_PID" 2>/dev/null || true
|
wait "$QEMU_PID" 2>/dev/null || true
|
||||||
QEMU_PID=""
|
QEMU_PID=""
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# End-to-end device scenario: the REAL warden-flared, running inside the VM,
|
# End-to-end device scenario: the REAL warden-flared, running inside the VM,
|
||||||
# checks in to flare-edge's mock FLARE portal on the host and pulls its
|
# checks in to flare-edge's mock FLARE portal on the host and pulls its
|
||||||
# firmware desired-state — the exact device-initiated HTTPS(-shaped) flow a
|
# firmware desired-state: the exact device-initiated HTTPS(-shaped) flow a
|
||||||
# panel performs, with zero flare-edge code changes (the portal URL is a state
|
# panel performs, with zero flare-edge code changes (the portal URL is a state
|
||||||
# file; 10.0.2.2 is slirp's host alias).
|
# file; 10.0.2.2 is slirp's host alias).
|
||||||
#
|
#
|
||||||
# FAILS CLOSED on every missing prerequisite — never a soft skip.
|
# FAILS CLOSED on every missing prerequisite: never a soft skip.
|
||||||
#
|
#
|
||||||
# Usage: portal-scenario.sh <zImage-virt>
|
# Usage: portal-scenario.sh <zImage-virt>
|
||||||
# Env: FLARE_EDGE path to a flare-edge checkout (provides mock-flare-portal.py)
|
# Env: FLARE_EDGE path to a flare-edge checkout (provides mock-flare-portal.py)
|
||||||
@@ -16,7 +16,7 @@ QDIR="$(cd "$HERE/.." && pwd)" # qemu/
|
|||||||
|
|
||||||
ZIMAGE="${1:-}"
|
ZIMAGE="${1:-}"
|
||||||
if [ -z "$ZIMAGE" ] || [ ! -f "$ZIMAGE" ]; then
|
if [ -z "$ZIMAGE" ] || [ ! -f "$ZIMAGE" ]; then
|
||||||
echo "FATAL: usage: $0 <zImage> — the virt.fragment kernel variant" >&2
|
echo "FATAL: usage: $0 <zImage>: the virt.fragment kernel variant" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ -z "${FLARE_EDGE:-}" ] || [ ! -f "$FLARE_EDGE/tools/mock-flare-portal.py" ]; then
|
if [ -z "${FLARE_EDGE:-}" ] || [ ! -f "$FLARE_EDGE/tools/mock-flare-portal.py" ]; then
|
||||||
@@ -24,11 +24,11 @@ if [ -z "${FLARE_EDGE:-}" ] || [ ! -f "$FLARE_EDGE/tools/mock-flare-portal.py" ]
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
[ -x "$QDIR/payload/warden-flared" ] || {
|
[ -x "$QDIR/payload/warden-flared" ] || {
|
||||||
echo "FATAL: no qemu/payload/warden-flared — build a static musl armv7 flared (see qemu/payload/README.md)" >&2
|
echo "FATAL: no qemu/payload/warden-flared: build a static musl armv7 flared (see qemu/payload/README.md)" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
command -v qemu-system-arm >/dev/null || {
|
command -v qemu-system-arm >/dev/null || {
|
||||||
echo "FATAL: qemu-system-arm not on PATH — see qemu/README.md" >&2
|
echo "FATAL: qemu-system-arm not on PATH: see qemu/README.md" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ FW_SIGNING_KEY_FILE="$FLARE_EDGE/tools/testdata/fw-dev-key.seed" \
|
|||||||
WARDEN_UBOOT_VERSION=2017.09 \
|
WARDEN_UBOOT_VERSION=2017.09 \
|
||||||
bash "$FLARE_EDGE/tools/mk-wfw.sh" "$WORK/rootfs-payload.img" 1 0.0.2 "$WORK/offer.wfw"
|
bash "$FLARE_EDGE/tools/mk-wfw.sh" "$WORK/rootfs-payload.img" 1 0.0.2 "$WORK/offer.wfw"
|
||||||
|
|
||||||
# 1. mock portal on the host, our device pre-registered (no pairing needed —
|
# 1. mock portal on the host, our device pre-registered (no pairing needed:
|
||||||
# the same credential-seeding shortcut fw-e2e-test.sh uses), offering the .wfw.
|
# the same credential-seeding shortcut fw-e2e-test.sh uses), offering the .wfw.
|
||||||
python3 "$FLARE_EDGE/tools/mock-flare-portal.py" \
|
python3 "$FLARE_EDGE/tools/mock-flare-portal.py" \
|
||||||
--port "$PORT" --device "$DEVICE_ID:$API_KEY" \
|
--port "$PORT" --device "$DEVICE_ID:$API_KEY" \
|
||||||
@@ -79,7 +79,7 @@ echo "== mock portal on :$PORT, device $DEVICE_ID"
|
|||||||
|
|
||||||
# 2. image seeded with the portal URL + credentials.
|
# 2. image seeded with the portal URL + credentials.
|
||||||
bash "$QDIR/mkinitramfs.sh"
|
bash "$QDIR/mkinitramfs.sh"
|
||||||
# All four enrolment keys — flare::enrolment() returns None (and the report
|
# All four enrolment keys: flare::enrolment() returns None (and the report
|
||||||
# loop parks forever) unless flare.site is present too.
|
# loop parks forever) unless flare.site is present too.
|
||||||
bash "$QDIR/mkimage.sh" \
|
bash "$QDIR/mkimage.sh" \
|
||||||
--portal-url "http://10.0.2.2:$PORT" \
|
--portal-url "http://10.0.2.2:$PORT" \
|
||||||
@@ -88,7 +88,7 @@ bash "$QDIR/mkimage.sh" \
|
|||||||
--state "flare.site=qemu-devsim"
|
--state "flare.site=qemu-devsim"
|
||||||
|
|
||||||
# 3. boot the VM headless (daemons run; console log to file). Random hostfwd
|
# 3. boot the VM headless (daemons run; console log to file). Random hostfwd
|
||||||
# ports can collide with another process — detect the early qemu bind
|
# ports can collide with another process. Detect the early qemu bind
|
||||||
# failure and retry with a fresh base rather than failing spuriously.
|
# failure and retry with a fresh base rather than failing spuriously.
|
||||||
QEMU_PID=""
|
QEMU_PID=""
|
||||||
for _attempt in 1 2 3; do
|
for _attempt in 1 2 3; do
|
||||||
@@ -103,7 +103,7 @@ for _attempt in 1 2 3; do
|
|||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
if grep -aq 'Could not set up host forwarding' "$WORK/console.log"; then
|
if grep -aq 'Could not set up host forwarding' "$WORK/console.log"; then
|
||||||
echo "== hostfwd port collision on base $VMBASE — retrying"
|
echo "== hostfwd port collision on base $VMBASE, retrying"
|
||||||
QEMU_PID=""
|
QEMU_PID=""
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
@@ -116,7 +116,7 @@ if [ -z "$QEMU_PID" ] || ! kill -0 "$QEMU_PID" 2>/dev/null; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 4. assert: rootfs up, and the portal saw — from OUR device id — an
|
# 4. assert: rootfs up, and the portal saw (from OUR device id) an
|
||||||
# authenticated check-in, the firmware desired-state pull, and the signed
|
# authenticated check-in, the firmware desired-state pull, and the signed
|
||||||
# .wfw asset download (i.e. flared accepted the offer and fetched it; the
|
# .wfw asset download (i.e. flared accepted the offer and fetched it; the
|
||||||
# verify+stage+APPLYING that follow are a dry run without
|
# verify+stage+APPLYING that follow are a dry run without
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
# RV1106-only init steps degrade on virt (backlight, goodix, npu, the 5.10
|
# RV1106-only init steps degrade on virt (backlight, goodix, npu, the 5.10
|
||||||
# /oem modules fail vermagic), and binaries older than the flare-edge #106
|
# /oem modules fail vermagic), and binaries older than the flare-edge #106
|
||||||
# fix reproduce that crash faithfully. Interactive login uses the image's own
|
# fix reproduce that crash faithfully. Interactive login uses the image's own
|
||||||
# credentials — deliberately not recorded here.
|
# credentials: deliberately not recorded here.
|
||||||
#
|
#
|
||||||
# FAILS CLOSED on missing prerequisites.
|
# FAILS CLOSED on missing prerequisites.
|
||||||
#
|
#
|
||||||
@@ -21,12 +21,12 @@ QDIR="$(cd "$HERE/.." && pwd)" # qemu/
|
|||||||
ZIMAGE="${1:-}"; ROOTFS="${2:-}"; OEM="${3:-}"
|
ZIMAGE="${1:-}"; ROOTFS="${2:-}"; OEM="${3:-}"
|
||||||
for f in "$ZIMAGE" "$ROOTFS" "$OEM"; do
|
for f in "$ZIMAGE" "$ROOTFS" "$OEM"; do
|
||||||
if [ -z "$f" ] || [ ! -f "$f" ]; then
|
if [ -z "$f" ] || [ ! -f "$f" ]; then
|
||||||
echo "FATAL: usage: $0 <zImage> <rootfs.img> <oem.img> — '$f' missing" >&2
|
echo "FATAL: usage: $0 <zImage> <rootfs.img> <oem.img>: '$f' missing" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
command -v qemu-system-arm >/dev/null || {
|
command -v qemu-system-arm >/dev/null || {
|
||||||
echo "FATAL: qemu-system-arm not on PATH — see qemu/README.md" >&2
|
echo "FATAL: qemu-system-arm not on PATH: see qemu/README.md" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ for _attempt in 1 2 3; do
|
|||||||
sleep 3
|
sleep 3
|
||||||
kill -0 "$QEMU_PID" 2>/dev/null && break
|
kill -0 "$QEMU_PID" 2>/dev/null && break
|
||||||
if grep -aq 'Could not set up host forwarding' "$WORK/console.log"; then
|
if grep -aq 'Could not set up host forwarding' "$WORK/console.log"; then
|
||||||
echo "== hostfwd port collision on base $PORT — retrying"
|
echo "== hostfwd port collision on base $PORT, retrying"
|
||||||
QEMU_PID=""
|
QEMU_PID=""
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Display + touch scenario: boot the VM headless with virtio-gpu, wait for the
|
# Display + touch scenario: boot the VM headless with virtio-gpu, wait for the
|
||||||
# LVGL UI (fbdev build) to render a real frame, then inject an absolute touch
|
# LVGL UI (fbdev build) to render a real frame, then inject an absolute touch
|
||||||
# tap on the Metrics tab (virtio-tablet) and ASSERT the frame changed — the
|
# tap on the Metrics tab (virtio-tablet) and ASSERT the frame changed: the
|
||||||
# device-level analogue of flare-edge's Xvfb/xdotool sim-test.sh. Readiness is
|
# device-level analogue of flare-edge's Xvfb/xdotool sim-test.sh. Readiness is
|
||||||
# polled from screendumps on bounded deadlines, never guessed with fixed
|
# polled from screendumps on bounded deadlines, never guessed with fixed
|
||||||
# sleeps: TCG renders CPU-bound and a loaded host can be arbitrarily slow.
|
# sleeps: TCG renders CPU-bound and a loaded host can be arbitrarily slow.
|
||||||
@@ -17,15 +17,15 @@ QDIR="$(cd "$HERE/.." && pwd)" # qemu/
|
|||||||
ZIMAGE="${1:-}"
|
ZIMAGE="${1:-}"
|
||||||
OUTDIR="${2:-$QDIR/out}"
|
OUTDIR="${2:-$QDIR/out}"
|
||||||
if [ -z "$ZIMAGE" ] || [ ! -f "$ZIMAGE" ]; then
|
if [ -z "$ZIMAGE" ] || [ ! -f "$ZIMAGE" ]; then
|
||||||
echo "FATAL: usage: $0 <zImage> [out-dir] — the virt.fragment kernel variant" >&2
|
echo "FATAL: usage: $0 <zImage> [out-dir]: the virt.fragment kernel variant" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
[ -x "$QDIR/payload/warden-ui" ] || {
|
[ -x "$QDIR/payload/warden-ui" ] || {
|
||||||
echo "FATAL: no qemu/payload/warden-ui — build it with flare-edge tools/build-ui-vm.sh" >&2
|
echo "FATAL: no qemu/payload/warden-ui: build it with flare-edge tools/build-ui-vm.sh" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
command -v qemu-system-arm >/dev/null || {
|
command -v qemu-system-arm >/dev/null || {
|
||||||
echo "FATAL: qemu-system-arm not on PATH — see qemu/README.md" >&2
|
echo "FATAL: qemu-system-arm not on PATH: see qemu/README.md" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ trap cleanup EXIT
|
|||||||
bash "$QDIR/mkinitramfs.sh"
|
bash "$QDIR/mkinitramfs.sh"
|
||||||
bash "$QDIR/mkimage.sh"
|
bash "$QDIR/mkimage.sh"
|
||||||
|
|
||||||
# Random hostfwd ports can collide — detect qemu's early bind failure and
|
# Random hostfwd ports can collide. Detect qemu's early bind failure and
|
||||||
# retry with a fresh base rather than failing spuriously.
|
# retry with a fresh base rather than failing spuriously.
|
||||||
for _attempt in 1 2 3; do
|
for _attempt in 1 2 3; do
|
||||||
PORT=$((21000 + RANDOM % 20000))
|
PORT=$((21000 + RANDOM % 20000))
|
||||||
@@ -53,7 +53,7 @@ for _attempt in 1 2 3; do
|
|||||||
sleep 3
|
sleep 3
|
||||||
kill -0 "$QEMU_PID" 2>/dev/null && break
|
kill -0 "$QEMU_PID" 2>/dev/null && break
|
||||||
if grep -aq 'Could not set up host forwarding' "$WORK/console.log"; then
|
if grep -aq 'Could not set up host forwarding' "$WORK/console.log"; then
|
||||||
echo "== hostfwd port collision on base $PORT — retrying"
|
echo "== hostfwd port collision on base $PORT, retrying"
|
||||||
QEMU_PID=""
|
QEMU_PID=""
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
@@ -96,7 +96,7 @@ EOF
|
|||||||
|
|
||||||
# The VM can die mid-poll (OOM, crash): check liveness before every QMP
|
# The VM can die mid-poll (OOM, crash): check liveness before every QMP
|
||||||
# call so the failure is OUR message + console evidence, not a python
|
# call so the failure is OUR message + console evidence, not a python
|
||||||
# traceback — and preserve the console log before the trap removes $WORK.
|
# traceback, and preserve the console log before the trap removes $WORK.
|
||||||
vm_alive_or_die() {
|
vm_alive_or_die() {
|
||||||
kill -0 "$QEMU_PID" 2>/dev/null && return 0
|
kill -0 "$QEMU_PID" 2>/dev/null && return 0
|
||||||
echo "FATAL: VM exited during the screendump poll" >&2
|
echo "FATAL: VM exited during the screendump poll" >&2
|
||||||
@@ -121,7 +121,7 @@ done
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Tap the "Metrics" tab: pixel (373,40) of 720x720 scaled to the QMP absolute
|
# Tap the "Metrics" tab: pixel (373,40) of 720x720 scaled to the QMP absolute
|
||||||
# range 0..32767 — switching tabs must repaint the content area. Poll for the
|
# range 0..32767. Switching tabs must repaint the content area. Poll for the
|
||||||
# repaint rather than guessing a delay.
|
# repaint rather than guessing a delay.
|
||||||
vm_alive_or_die
|
vm_alive_or_die
|
||||||
qmp tap 16975 1820
|
qmp tap 16975 1820
|
||||||
@@ -142,7 +142,7 @@ cp "$WORK/shot1.ppm" "$OUTDIR/ui-shot1.ppm"
|
|||||||
cp "$WORK/shot2.ppm" "$OUTDIR/ui-shot2.ppm" 2>/dev/null || true
|
cp "$WORK/shot2.ppm" "$OUTDIR/ui-shot2.ppm" 2>/dev/null || true
|
||||||
|
|
||||||
[ "$changed" = 1 ] || {
|
[ "$changed" = 1 ] || {
|
||||||
echo "FATAL: tapping the Metrics tab did not change the frame within 90s — touch is not reaching the UI" >&2
|
echo "FATAL: tapping the Metrics tab did not change the frame within 90s: touch is not reaching the UI" >&2
|
||||||
cp "$WORK/console.log" "$OUTDIR/ui-shot-console.log" || true
|
cp "$WORK/console.log" "$OUTDIR/ui-shot-console.log" || true
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//! Dependency-free (`harness = false`): a fixed-iteration timing loop, so the sim
|
//! Dependency-free (`harness = false`): a fixed-iteration timing loop, so the sim
|
||||||
//! crate keeps zero deps and CI can capture a stable ns/op number per model with no
|
//! crate keeps zero deps and CI can capture a stable ns/op number per model with no
|
||||||
//! criterion tree to compile. Human-readable timings go to stdout; one JSON line per
|
//! criterion tree to compile. Human-readable timings go to stdout; one JSON line per
|
||||||
//! benchmark goes to stderr for CI trend capture (`bench: … ns_per_op …`).
|
//! benchmark goes to stderr for CI trend capture (`bench: ... ns_per_op ...`).
|
||||||
//!
|
//!
|
||||||
//! Run: `cargo bench` (or `cargo run --release --bench sim_bench`).
|
//! Run: `cargo bench` (or `cargo run --release --bench sim_bench`).
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user