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:
+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
|
||||
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)
|
||||
|
||||
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
|
||||
`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
|
||||
@@ -16,48 +16,48 @@ it is delivered incrementally, console-first.
|
||||
|
||||
## 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
|
||||
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 —
|
||||
we apply that recipe to rv1106's data (`clk-rv1106.c` = 1284 lines;
|
||||
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;
|
||||
`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."
|
||||
- **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,
|
||||
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
|
||||
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.
|
||||
- **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
|
||||
- **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
|
||||
Tier-1 SDIO-wakeup fix forward.
|
||||
- **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).
|
||||
|
||||
## 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.
|
||||
- **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).
|
||||
- **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."
|
||||
- **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 →
|
||||
- **M3: rootfs boot.** dw_mmc + DT -> eMMC/SD rootfs, userspace up on our Buildroot.
|
||||
- **M4: display.** VOP2 + the RGB666 720x720 panel + GT911 touch + PWM backlight ->
|
||||
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.
|
||||
- **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)
|
||||
|
||||
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 `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.
|
||||
|
||||
## Layout (this repo)
|
||||
|
||||
@@ -3,39 +3,39 @@
|
||||
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
|
||||
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)
|
||||
|
||||
c8a3 is the desk-connected bench Warden: reachable over its USB-gadget ethernet
|
||||
(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
|
||||
firmware (boot_a=mmcblk0p5 / boot_b=mmcblk0p6, 32 MiB each; rootfs_a/_b; AvbABData
|
||||
in `misc` sector 4 / byte 2048).
|
||||
|
||||
The test never risks the working system:
|
||||
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.
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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)
|
||||
|
||||
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
|
||||
(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
|
||||
rejected with `FIT: No fit blob` / `No FIT image`.
|
||||
- **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`.
|
||||
- **Sysmem sentinel load addresses.** fdt `load=0xffffff00`, kernel
|
||||
`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
|
||||
signed. Template: `sdk/sysdrv/source/kernel/boot.its`; builder recipe:
|
||||
`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
|
||||
string (`Model: WardenOS 86-Panel (RV1106) — M2 earlycon bring-up`), then
|
||||
string (`Model: WardenOS 86-Panel (RV1106), M2 earlycon bring-up`), then
|
||||
`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:
|
||||
|
||||
```
|
||||
[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] 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] 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
|
||||
[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
|
||||
0xff4c0000, shift 2, 32-bit word + `earlyprintk` — the decompressor prints
|
||||
0xff4c0000, shift 2, 32-bit word + `earlyprintk`, the decompressor prints
|
||||
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
|
||||
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.
|
||||
- **`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
|
||||
`grf_ctx`. Fix: add the `grf-clock-controller` child to the grf syscon (now in
|
||||
`dts/rv1106-warden-m2.dts`).
|
||||
|
||||
**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
|
||||
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.
|
||||
|
||||
## 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
|
||||
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
|
||||
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/
|
||||
-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
|
||||
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)
|
||||
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),
|
||||
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
|
||||
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)
|
||||
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) ·
|
||||
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
|
||||
- **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/`).
|
||||
- **OTP/nvmem** — rockchip-otp0 reads chip id (`rng-otp/`).
|
||||
- **GMAC** — eth0 Link Up 100 Mbps/Full (`gmac/`).
|
||||
- **SARADC** — iio:device0 reads 2 ch; the −22 was vref, not clk (`adc/SARADC-FIX.md`).
|
||||
## 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`).
|
||||
- **TRNG**: /dev/hwrng, real HW entropy (`rng-otp/`).
|
||||
- **OTP/nvmem**: rockchip-otp0 reads chip id (`rng-otp/`).
|
||||
- **GMAC**: eth0 Link Up 100 Mbps/Full (`gmac/`).
|
||||
- **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,
|
||||
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
|
||||
scanout window (rv1106 uses **WIN1**, not rv1126's WIN2). Full chain + `_a`-vs-`_b`
|
||||
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
|
||||
(`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`.
|
||||
|
||||
## In flight
|
||||
- **i2s-tdm** — DAI builds; needs the codec + card (below).
|
||||
- **AIC8800 BT** — module built (6.18 vermagic); HCI bring-up not yet exercised.
|
||||
- **i2s-tdm**: DAI builds; needs the codec + card (below).
|
||||
- **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.
|
||||
|
||||
## Remaining blocks — final status (all real capabilities now verified)
|
||||
## Remaining blocks: final status (all real capabilities now verified)
|
||||
| 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. |
|
||||
| **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. |
|
||||
| **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. |
|
||||
| **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. |
|
||||
|
||||
**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
|
||||
NPU *compute* (a person-year RE effort, scoped in `npu/OPEN-NPU-PLAN.md`). No
|
||||
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).
|
||||
|
||||
## 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).
|
||||
|
||||
@@ -8,12 +8,12 @@ c8a3, not just compiled.
|
||||
|
||||
| Driver / node | 5.10 evidence | mainline? | 6.18 status |
|
||||
|---|---|---|---|
|
||||
| CRU clock (clk-rv1106) | — | ported | [x] M2 |
|
||||
| pinctrl-rockchip (rv1106) | — | ported | [x] M2 |
|
||||
| CRU clock (clk-rv1106) | n/a | ported | [x] M2 |
|
||||
| pinctrl-rockchip (rv1106) | n/a | ported | [x] M2 |
|
||||
| GIC-400 / arch_timer | arch_timer | mainline | [x] M2 |
|
||||
| dw_mmc (eMMC) | dw-mci | mainline | [x] M3 |
|
||||
| 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 |
|
||||
| uart1 / uart4 | ttyS1, ttyS4 | mainline | [x] batch1 |
|
||||
| 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/`) |
|
||||
| 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/`) |
|
||||
| GPIO_SYSFS (legacy /sys/class/gpio) | — | mainline (config) | [ ] goodix script needs it |
|
||||
| PWM (rockchip) | — | mainline (=m) | [ ] batch2 =y (backlight) |
|
||||
| RTC (rv1106-rtc) | — | ported (vendor driver) | [x] /dev/rtc0 registers + reads |
|
||||
| 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 |
|
||||
| GPIO_SYSFS (legacy /sys/class/gpio) | n/a | mainline (config) | [ ] goodix script needs it |
|
||||
| PWM (rockchip) | n/a | mainline (=m) | [ ] batch2 =y (backlight) |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| PWM backlight (pwm1) | — | mainline (rk3328 fallback) | [x] backlight up (brightness) |
|
||||
| RGB666 720×720 panel | — | panel-dpi | [wip] probes; bus_format + connector WIP |
|
||||
| PWM backlight (pwm1) | n/a | mainline (rk3328 fallback) | [x] backlight up (brightness) |
|
||||
| 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) |
|
||||
| GPIO_SYSFS / crypto / CFG80211 | — | 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`) |
|
||||
| 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 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 |
|
||||
| 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 |
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
# Overnight autonomous kernel-completion workflow
|
||||
|
||||
**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,
|
||||
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
|
||||
the table. Fully autonomous, runs overnight; the display's last mile is deferred
|
||||
to tomorrow morning (needs eyes on the panel).
|
||||
|
||||
## 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
|
||||
have source — the *only* closed pieces are userspace runtimes like RKNN, not
|
||||
the kernel drivers). Port 5.10 → 6.18.
|
||||
2. **Mainline / sibling** — reuse rv1126/px30 data where the IP matches (clk,
|
||||
have source: the *only* closed pieces are userspace runtimes like RKNN, not
|
||||
the kernel drivers). Port 5.10 -> 6.18.
|
||||
2. **Mainline / sibling**: reuse rv1126/px30 data where the IP matches (clk,
|
||||
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.
|
||||
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.
|
||||
For each: understand it, prefer the most upstream-aligned source, harden it, and
|
||||
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 |
|
||||
| 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,
|
||||
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,
|
||||
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
|
||||
Pipeline: research (Sonnet subagents, in background) ∥ port (me) → build → flash to
|
||||
c8a3 slot _b → verify on hardware → commit to warden-sdk. Serial bottleneck is
|
||||
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; research/porting overlaps verification. **Resilience:** krecover is hardened
|
||||
(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
|
||||
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
|
||||
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
|
||||
| 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. |
|
||||
| **OTP/nvmem** | `rockchip-otp0` reads chip id ("MR1"). |
|
||||
| **GMAC** (wired eth) | `eth0: Link is Up - 100 Mbps/Full`. |
|
||||
| **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,
|
||||
i2c, wdt, RTC, tsadc, RGA, USB host, PWM backlight. VOP display binds — connector
|
||||
(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
|
||||
is the morning task.)
|
||||
|
||||
## Cross-cutting fixes made this run
|
||||
- **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
|
||||
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
|
||||
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.
|
||||
- Hardened the c8a3 `_b`-test → `_a`-recover cycle (compact-hex AVB serial write +
|
||||
readback while stable; Pi golden-env fallback for bootcount→loader).
|
||||
- Hardened the c8a3 `_b`-test -> `_a`-recover cycle (compact-hex AVB serial write +
|
||||
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
|
||||
crypto extensions already cover it; 100 KB port), NPU (no open userspace),
|
||||
pvtm (DVFS-only). Camera/ISP/SPI: no hardware. **No unexplored capability gap.**
|
||||
|
||||
## 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).
|
||||
2. **Audible audio** — `speaker-test`/`aplay` through the acodec.
|
||||
3. Wifi **boot-time auto-load** (modules currently insmod'd by hand) — a loader
|
||||
that inserts aic8800_bsp→fdrv(→btlpm) from the rootfs.
|
||||
2. **Audible audio**: `speaker-test`/`aplay` through the acodec.
|
||||
3. Wifi **boot-time auto-load** (modules currently insmod'd by hand): a loader
|
||||
that inserts aic8800_bsp->fdrv(->btlpm) from the rootfs.
|
||||
|
||||
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
|
||||
> (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
|
||||
@@ -8,125 +8,125 @@
|
||||
> `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`
|
||||
> 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
|
||||
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**).
|
||||
|
||||
## 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.
|
||||
- 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.
|
||||
|
||||
## 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.
|
||||
## 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.
|
||||
Fixed (captured in `clk/`):
|
||||
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
|
||||
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
|
||||
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.
|
||||
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)`
|
||||
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
|
||||
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
|
||||
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).
|
||||
**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,
|
||||
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
|
||||
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
|
||||
`#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`.
|
||||
- **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
|
||||
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;
|
||||
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) —
|
||||
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),
|
||||
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
|
||||
`CPU_RV1106` symbol recreated). Captured as `mach/0001-rv1106-soc-recognition.patch`.
|
||||
**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:
|
||||
- **`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)
|
||||
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`.
|
||||
- **`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
|
||||
RAM, GRF, the CRU (our clk-rv1106), and uart2 (`snps,dw-apb-uart`, the console) —
|
||||
- **`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
|
||||
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
|
||||
divisor before any clock/pinctrl probe.
|
||||
|
||||
**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
|
||||
**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 (section M1 item 4); the
|
||||
console baud (1.5M assumed). A wrong DDR/clock value silently hangs before or just after
|
||||
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
|
||||
drivers, verified over the serial console. It reaches earlycon, the arch timer
|
||||
(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
|
||||
(the A/B `_b`-slot safe-test framework worked first try) and the boot-image format
|
||||
(external-data FIT + resource, `mkimage -E -p 0x800`).
|
||||
|
||||
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`.
|
||||
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
|
||||
high (`load=0x08000000`). A lean RV1106 defconfig would also fix this and is the
|
||||
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
|
||||
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.
|
||||
|
||||
**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
|
||||
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)
|
||||
Adding the eMMC (`dw_mmc`) node to the DT was all M3 needed — the drivers are already
|
||||
## 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
|
||||
in the config. On hardware:
|
||||
```
|
||||
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)
|
||||
mmcblk0: p1(env)...p10(rootfs_b)...p12(userdata) ← partitions enumerated
|
||||
EXT4-fs (mmcblk0p10): mounted ... VFS: Mounted root ← ext4 rootfs mounted
|
||||
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)
|
||||
mmcblk0: p1(env)...p10(rootfs_b)...p12(userdata) <- partitions enumerated
|
||||
EXT4-fs (mmcblk0p10): mounted ... VFS: Mounted root <- ext4 rootfs mounted
|
||||
Run /sbin/init as init process
|
||||
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`,
|
||||
`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.
|
||||
|
||||
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
|
||||
`warden-ui: no backlight device` (no display/VOP node yet — **M4**).
|
||||
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**).
|
||||
|
||||
## M4/M5/M6 — NEXT
|
||||
- **M4 display**: VOP2 + the RGB666 720×720 panel + GT911 touch + PWM backlight →
|
||||
## M4/M5/M6: NEXT
|
||||
- **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).
|
||||
- **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.
|
||||
@@ -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)
|
||||
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
|
||||
values (already retired the iomux offsets) and tracked for eventual convergence when it merges.
|
||||
|
||||
## Honest scope
|
||||
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
|
||||
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
|
||||
```
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
# Driver provenance & openness ledger
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
## Legend
|
||||
- **vendor-src** — C source from the Rockchip 5.10 vendor SDK
|
||||
(`flare-edge/sdk/sysdrv/source/kernel/`), forward-ported 5.10 → 6.18.
|
||||
- **mainline-sibling** — 6.18 mainline driver, extended with an rv1106 data table
|
||||
- **vendor-src**: C source from the Rockchip 5.10 vendor SDK
|
||||
(`flare-edge/sdk/sysdrv/source/kernel/`), forward-ported 5.10 -> 6.18.
|
||||
- **mainline-sibling**: 6.18 mainline driver, extended with an rv1106 data table
|
||||
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)
|
||||
| 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 |
|
||||
|
||||
## 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
|
||||
*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
|
||||
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;
|
||||
reverse-engineering it is out of scope for the panel and buys nothing (the host
|
||||
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
|
||||
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
|
||||
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.)
|
||||
|
||||
@@ -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.
|
||||
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
|
||||
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` +
|
||||
`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;
|
||||
`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`,
|
||||
**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.
|
||||
Bandgap trim `macphy_bgs` comes from OTP (soft dep — PHY works without it, slightly
|
||||
(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
|
||||
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`
|
||||
(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
|
||||
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
|
||||
read path; mainline has px30/rk3308/rk3576/rk3588 but no rv1106.
|
||||
**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`.
|
||||
**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)
|
||||
+ `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
|
||||
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
|
||||
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;
|
||||
`tcrypt` or an openssl-engine smoke test.
|
||||
|
||||
## 6. NPU kernel driver — MEDIUM, LOW-MED value (`npu/PORT-PLAN.md`)
|
||||
Kernel driver only; no open userspace regcmd encoder (openness line — no blob).
|
||||
## 6. NPU kernel driver: MEDIUM, LOW-MED value (`npu/PORT-PLAN.md`)
|
||||
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.
|
||||
## 8. pvtm — SMALL, LOW value — DVFS monitors; only if DVFS is pursued.
|
||||
## 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.
|
||||
|
||||
Order rationale: TRNG (near-free, high value) → GMAC (high value, PHY already
|
||||
mainline) → OTP (small, feeds GMAC) → audio → crypto → NPU → mailbox → pvtm.
|
||||
Order rationale: TRNG (near-free, high value) -> GMAC (high value, PHY already
|
||||
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
|
||||
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
|
||||
`regulator_get_voltage(dummy)` returns -EINVAL, which probe returns directly (so
|
||||
only the generic "probe failed with error -22" showed). The earlier
|
||||
clk-rv1106-divider theory was wrong (the divider is HIWORD settable, xin24m is
|
||||
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
|
||||
/ { vcc_1v8: vcc-1v8 { compatible = "regulator-fixed"; regulator-name = "vcc_1v8";
|
||||
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"; };
|
||||
```
|
||||
**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
|
||||
`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
|
||||
`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
|
||||
session / bench.
|
||||
|
||||
@@ -17,7 +17,7 @@ Trees involved:
|
||||
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,
|
||||
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)
|
||||
|
||||
@@ -25,10 +25,10 @@ From `sdk/sysdrv/source/kernel/sound/soc/codecs/` to the 6.18 tree's `sound/soc/
|
||||
|
||||
| File | Lines | Notes |
|
||||
|---|---|---|
|
||||
| `rv1106_codec.c` | 2317 | Analog codec (acodec) driver — the priority target |
|
||||
| `rv1106_codec.h` | — | Register/bitfield definitions, copied unmodified |
|
||||
| `rv1106_codec.c` | 2317 | Analog codec (acodec) driver: the priority target |
|
||||
| `rv1106_codec.h` | n/a | Register/bitfield definitions, copied unmodified |
|
||||
| `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
|
||||
|
||||
@@ -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
|
||||
slot for "RV").
|
||||
- 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.
|
||||
|
||||
`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-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_SIMPLE_CARD=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
|
||||
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
|
||||
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
|
||||
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
|
||||
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 |
|
||||
|---|---|---|
|
||||
| 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. |
|
||||
| 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. |
|
||||
| 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. |
|
||||
| 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. |
|
||||
| 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. |
|
||||
|
||||
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`,
|
||||
`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.
|
||||
@@ -95,13 +95,13 @@ That's the complete fix list — nothing else needed touching. No vendor-only he
|
||||
## rk_dsm (digital speaker modulator) decision
|
||||
|
||||
**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
|
||||
into `zImage` (17 `rk_dsm`-prefixed symbols in `System.map`, including
|
||||
`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
|
||||
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`
|
||||
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)
|
||||
@@ -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`)
|
||||
|
||||
`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
|
||||
`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>`
|
||||
- `i2s@ffae0000` (i2s0_8ch): `status = "okay"`, `#sound-dai-cells = <0x00>`, `phandle = <0x59>`
|
||||
- `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.
|
||||
|
||||
## 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
|
||||
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
|
||||
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`:
|
||||
|
||||
```
|
||||
@@ -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
|
||||
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
|
||||
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
|
||||
session verifies on a real 86-Panel:
|
||||
|
||||
- `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.
|
||||
- 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).
|
||||
- `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
|
||||
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.
|
||||
- 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
|
||||
@@ -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
|
||||
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
|
||||
**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.
|
||||
|
||||
---
|
||||
## Parent-session verify + fix (2026-08-25)
|
||||
**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
|
||||
_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.
|
||||
**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 +
|
||||
`#sound-dai-cells=<0>`, so the mainline driver binds it).
|
||||
|
||||
**VERIFIED on c8a3:** `/proc/asound/cards` → `0 [rv1106acodec]: simple-card -
|
||||
rv1106-acodec`; `aplay -l` → `card 0: rv1106acodec, device 0:
|
||||
**VERIFIED on c8a3:** `/proc/asound/cards` -> `0 [rv1106acodec]: simple-card -
|
||||
rv1106-acodec`; `aplay -l` -> `card 0: rv1106acodec, device 0:
|
||||
ffae0000.i2s-rv1106-hifi`; `/dev/snd/` has `controlC0 pcmC0D0p pcmC0D0c` (playback
|
||||
+ capture). Audible speaker test deferred to the bench (with the display).
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/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.
|
||||
# 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
|
||||
# wires in cleanly). Apply this fragment on top, then `make olddefconfig`:
|
||||
@@ -8,7 +8,7 @@
|
||||
# make ARCH=arm CROSS_COMPILE=... olddefconfig zImage
|
||||
#
|
||||
# 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 ---
|
||||
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
|
||||
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 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
|
||||
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 },
|
||||
```
|
||||
|
||||
`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
|
||||
omits it).
|
||||
|
||||
## The DT (in `../dts/rv1106-warden.dts`)
|
||||
|
||||
- `&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`).
|
||||
- `&vop_out_rgb` repointed straight to the panel (mainline has no separate rgb
|
||||
node — `rockchip_rgb_init` is a helper the VOP calls).
|
||||
- `panel` (`panel-dpi`, 720×720, 30 MHz) + `pwm-backlight` on `&pwm1`.
|
||||
node: `rockchip_rgb_init` is a helper the VOP calls).
|
||||
- `panel` (`panel-dpi`, 720x720, 30 MHz) + `pwm-backlight` on `&pwm1`.
|
||||
|
||||
## 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
|
||||
```
|
||||
`/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.
|
||||
|
||||
## 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
|
||||
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).
|
||||
See **`VERIFIED.md`** for the complete bring-up chain, the `_a`-vs-`_b` register
|
||||
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
|
||||
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,
|
||||
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`
|
||||
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` +
|
||||
`/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
|
||||
pwm-rockchip relies on the driver core auto-applying the **`"default"`** state,
|
||||
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)
|
||||
|
||||
With connector + backlight + pins + panel-init all correct, the screen was still
|
||||
physically black (backlight on, `LVDS-1` connected, VOP streaming, fb0 720×720,
|
||||
`modetest`/splash written) — the classic "everything healthy, glass dark" state.
|
||||
physically black (backlight on, `LVDS-1` connected, VOP streaming, fb0 720x720,
|
||||
`modetest`/splash written): the classic "everything healthy, glass dark" state.
|
||||
Found by dumping the VOP register block `0xff990000` on the **working `_a` slot**
|
||||
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
|
||||
**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
|
||||
`(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.
|
||||
|
||||
**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).
|
||||
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
|
||||
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):
|
||||
@@ -62,26 +62,26 @@ Register evidence (`_a` working vs `_b` broken, before the fix):
|
||||
| 0x090 | WIN1_CTRL0 (enable) | `0x00000001` | `0x00000000` |
|
||||
| 0x098 | WIN1_VIR (stride) | `0x000002D0` (720) | `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 } }`
|
||||
(`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 —
|
||||
(`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:
|
||||
matching `_a`.
|
||||
|
||||
## 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_vop_reg.c` — `rv1106_vop_win_data` uses
|
||||
- `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
|
||||
`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`.
|
||||
- `arch/arm/boot/dts/rockchip/rv1106-warden.dts` — `&vop` pinctrl `<&lcd_pins>`;
|
||||
- `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>`;
|
||||
`&pwm1` pinctrl "default"; `/delete-node/ &rgb`; panel `bus-format` RGB666,
|
||||
no reset-gpios.
|
||||
|
||||
## Known separate issue (NOT display)
|
||||
|
||||
`_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
|
||||
from tripping `altbootcmd=download` into the loader during bring-up.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# RV1106 6.18 devicetree
|
||||
|
||||
- **`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,
|
||||
watchdog, usb, vop, npu, rga, …), then enables only what the wall-HMI needs.
|
||||
- **`rv1106-warden.dts`**: our board DT. `#include`s the vendor `rv1106.dtsi`
|
||||
(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.
|
||||
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.
|
||||
|
||||
## 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:
|
||||
|
||||
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 →
|
||||
`Oops - bad mode` → `Attempted to kill the idle task`. (Confirmed on c8a3.)
|
||||
2. Enable the peripherals in the board DTS (`&uart2`, `&emmc`, …).
|
||||
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.)
|
||||
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.
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
// 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).
|
||||
*
|
||||
* 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).
|
||||
* 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
|
||||
* 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
|
||||
* 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
|
||||
* 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);
|
||||
* - 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.
|
||||
*/
|
||||
/dts-v1/;
|
||||
@@ -25,7 +25,7 @@
|
||||
#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";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
@@ -47,7 +47,7 @@
|
||||
|
||||
memory@0 {
|
||||
device_type = "memory";
|
||||
reg = <0x00000000 0x10000000>; /* 256 MiB — PORT-VERIFY */
|
||||
reg = <0x00000000 0x10000000>; /* 256 MiB: PORT-VERIFY */
|
||||
};
|
||||
|
||||
xin24m: oscillator {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* 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
|
||||
* 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).
|
||||
*/
|
||||
/dts-v1/;
|
||||
@@ -31,7 +31,7 @@
|
||||
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). */
|
||||
vcc_1v8: vcc-1v8 {
|
||||
compatible = "regulator-fixed";
|
||||
@@ -57,7 +57,7 @@
|
||||
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 {
|
||||
max-frequency = <50000000>;
|
||||
bus-width = <4>;
|
||||
@@ -95,7 +95,7 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
/* --- M4: display (VOP + RGB → 720x720 panel + PWM backlight) --- */
|
||||
/* --- M4: display (VOP + RGB -> 720x720 panel + PWM backlight) --- */
|
||||
&display_subsystem {
|
||||
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
|
||||
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
|
||||
- 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` /
|
||||
`.integrated_phy_powerdown`, each calling mainline's single-reg
|
||||
`rk_gmac_integrated_fephy_power{up,down}(priv, CON0)`.
|
||||
- **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
|
||||
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
|
||||
- `&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
|
||||
`ethernet-phy@2` (`phy-is-integrated`). `rockchip,grf=<&grf>` (the big syscon
|
||||
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).
|
||||
|
||||
## 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
|
||||
`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.
|
||||
|
||||
@@ -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
|
||||
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
|
||||
(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
|
||||
`/dev/mem` polled-SRAM scheme** — is settled below: build the real mailbox link
|
||||
The one substantive design decision (**hardware mailbox IP vs. our current
|
||||
`/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
|
||||
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.**
|
||||
|
||||
- **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
|
||||
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
|
||||
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
|
||||
declares both instances with that fallback string. Gated today only by
|
||||
`status="disabled"` + `CONFIG_ROCKCHIP_MBOX` being absent from the defconfig.
|
||||
- **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`
|
||||
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
|
||||
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
|
||||
example (`battery-ipc/stream.c` + `rockchip_thunderboot_service.c`).
|
||||
- **The only genuinely new/greenfield piece** is that no general Linux-side mailbox
|
||||
_client_ for RV1106 exists in-tree (the one real client,
|
||||
`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
|
||||
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
|
||||
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.**
|
||||
|
||||
**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
|
||||
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
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
**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
|
||||
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
|
||||
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.
|
||||
|
||||
---
|
||||
@@ -76,13 +76,13 @@ a full rpmsg port.
|
||||
|
||||
| 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** |
|
||||
| `pmu_mailbox@ff378000` | `0xff378000` (`0x200`) | `GIC_SPI 114` | `PCLK_PMU_MAILBOX` | none anywhere | PMU-domain, undocumented purpose — **avoid** |
|
||||
| `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** |
|
||||
|
||||
**`@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
|
||||
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
|
||||
`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.
|
||||
@@ -101,113 +101,113 @@ MAILBOX_B2A_DAT(x) 0x34 + x*8
|
||||
```
|
||||
|
||||
**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`,
|
||||
fire-and-forget, no ack/poll — `rockchip-mailbox.c:46-70`); the peer's STATUS bit
|
||||
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
|
||||
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>`
|
||||
(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.
|
||||
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)
|
||||
|
||||
- SCR1 = Syntacore RV32IMC, machine-mode only, 16KB unified cache, "HPMCU".
|
||||
- **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
|
||||
`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 +
|
||||
mailbox echo handler must fit this.
|
||||
- **Proven load/release sequence** (`flared/src/hpmcu.rs:26-76`, hardware-validated):
|
||||
`CORECRU_CORESOFTRST_CON01` (`0xff3b8000+0xa04`) hold `0x1e001e` → write firmware
|
||||
to `0xff6fe000` → `SGRF_HPMCU_BOOT_ADDR` (`0xff076000+0x44`) = load addr →
|
||||
`CORECRU_CORESOFTRST_CON01` (`0xff3b8000+0xa04`) hold `0x1e001e` -> write firmware
|
||||
to `0xff6fe000` -> `SGRF_HPMCU_BOOT_ADDR` (`0xff076000+0x44`) = load addr ->
|
||||
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.
|
||||
- **MCU-side reference to copy:** `hal_mbox.c`/`hal_mbox.h` (generic 4-channel
|
||||
register driver, `HAL_MBOX_Init`/send/ack) + working example
|
||||
`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`).
|
||||
|
||||
### Current comms mechanism (what exists today, keep it)
|
||||
|
||||
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`
|
||||
(`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
|
||||
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
|
||||
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.
|
||||
|
||||
### 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
|
||||
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
|
||||
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
|
||||
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
|
||||
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.)
|
||||
|
||||
### 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.
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
the SCR1 at all.
|
||||
|
||||
1. Kernel config: `CONFIG_ROCKCHIP_MBOX=y` + `CONFIG_MAILBOX_TEST=y` (mainline
|
||||
driver, **no patch** — `rockchip-mailbox.c` binds on the fallback compatible).
|
||||
2. Board DTS: override `&mailbox { status = "okay"; };` (the `@ff5c0000` instance —
|
||||
driver, **no patch**, `rockchip-mailbox.c` binds on the fallback compatible).
|
||||
2. Board DTS: override `&mailbox { status = "okay"; };` (the `@ff5c0000` instance,
|
||||
main CRU clock, `GIC_SPI 1`, no PMU-domain complications). Add a
|
||||
`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):
|
||||
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
|
||||
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.**
|
||||
|
||||
### 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.
|
||||
|
||||
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
|
||||
`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
|
||||
`hal_mbox.c` / `battery-ipc/stream.c`. Load and start the core with the
|
||||
**proven `hpmcu.rs` load/release sequence** (SRAM path, `0xff6fe000` — **not**
|
||||
the `0x40000` boot path, hazard §2).
|
||||
**proven `hpmcu.rs` load/release sequence** (SRAM path, `0xff6fe000`, **not**
|
||||
the `0x40000` boot path, hazard section 2).
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
green.
|
||||
3. **Verify on serial console** (`warden-c8a3`, `_b`-slot): send a known
|
||||
`{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
|
||||
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.**
|
||||
@@ -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,
|
||||
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
|
||||
`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
|
||||
|
||||
- **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
|
||||
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).
|
||||
- **`pmu_mailbox@ff378000` purpose** — undocumented, no consumer anywhere; leave
|
||||
- **Channel choice**: use a channel ≠ 1 (ch1 = vendor `"amp-rx"` reservation).
|
||||
- **`pmu_mailbox@ff378000` purpose**: undocumented, no consumer anywhere; leave
|
||||
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.
|
||||
- **Whether Rockchip-official (non-RE) MCU/mailbox docs exist under NDA/partner
|
||||
access** — never asked Luckfox support directly; worth a query.
|
||||
- **`mcutool.c` exact `/dev/mem` sequence** — inferred, not read line-by-line;
|
||||
access**: never asked Luckfox support directly; worth a query.
|
||||
- **`mcutool.c` exact `/dev/mem` sequence**: inferred, not read line-by-line;
|
||||
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`,
|
||||
`.../raw/followup-riscv-mcu.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**.
|
||||
|
||||
## Evidence (serial, _b slot = our 6.18)
|
||||
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 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.
|
||||
|
||||
## 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
|
||||
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
|
||||
@@ -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`.
|
||||
(5.10 happened to leave it on.)
|
||||
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
|
||||
the sender writes CMD first, then DAT (the mainline order), so both are current
|
||||
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)
|
||||
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
|
||||
uncached peripheral window (0xff040024/28) → CORECRU hold → firmware to SRAM →
|
||||
SGRF boot addr → CORECRU release. **Do NOT kill warden-flared** (it one-shot-loads
|
||||
uncached peripheral window (0xff040024/28) -> CORECRU hold -> firmware to SRAM ->
|
||||
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
|
||||
+ reload the SCR1; flared does not re-load. The SRAM-load path avoids the 0x40000
|
||||
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.
|
||||
|
||||
## Files
|
||||
- `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/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.
|
||||
- Kernel deltas: `rockchip-mailbox.c` (rv1106 num_chans=1), `clk-rv1106.c`
|
||||
(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.
|
||||
|
||||
# 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
|
||||
* (rv1106.dtsi sram@ff6c0000) into rkisp_sram (0x0..0x3e000) and hpmcu_sram
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
FW=${1:-/userdata/echo-fw-words.txt}
|
||||
|
||||
# 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.
|
||||
devmem 0xff040024 32 0xff000
|
||||
devmem 0xff040028 32 0xffc00
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* watchdog SRAM mailbox relies on).
|
||||
*
|
||||
* 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
|
||||
* 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))
|
||||
|
||||
/* 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 A2B_INTEN REG32(MBOX_BASE + 0x00)
|
||||
#define A2B_STATUS REG32(MBOX_BASE + 0x04)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
# 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
|
||||
# the SCR1 reads them. The SCR1 echoes {cmd,dat} verbatim into B2A_CMD/B2A_DAT.
|
||||
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
|
||||
sentence, and only one of them is reachable soon.
|
||||
|
||||
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**,
|
||||
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
|
||||
valid `regcmd` stream this driver can submit, no closed `librknnrt`, no `.rknn`
|
||||
blob). **Not achievable on any near horizon.** It is a from-scratch,
|
||||
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
|
||||
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.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,
|
||||
`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`,
|
||||
`pc_task_status_offset=0x3c`, single-core `irqs`/`resets` arrays,
|
||||
`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
|
||||
`ACLK_RKNN`/`HCLK_RKNN`, `assigned-clock-rates=<420000000>`, resets
|
||||
`SRST_A_RKNN`/`SRST_H_RKNN`, `status="disabled"` (`rv1106.dtsi:1127-1138`). One
|
||||
board-DTS override (`&npu { status="okay"; }`) enables it.
|
||||
- **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/
|
||||
thermal code is all provably dead for RV1106's DT.
|
||||
- The one real build blocker is four vendor-only downstream headers
|
||||
(`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
|
||||
`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
|
||||
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
|
||||
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 |
|
||||
|---|---|---|
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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` |
|
||||
|
||||
**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
|
||||
config, feature/weight DMA descriptors). Those live _inside_ the opaque regcmd
|
||||
buffer. The op-code semantics of that stream:
|
||||
|
||||
- **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
|
||||
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
|
||||
@@ -89,21 +89,21 @@ buffer. The op-code semantics of that stream:
|
||||
- **Only the closed RKNN-Toolkit2 compiler emits valid regcmd streams.** The
|
||||
on-device closed `librknnrt` parses `.rknn` blobs and issues the same
|
||||
`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.
|
||||
|
||||
### 1.3 Why there is no shortcut through the existing open efforts
|
||||
|
||||
| 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"`. |
|
||||
| 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). |
|
||||
| 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. |
|
||||
| `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. |
|
||||
| `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**
|
||||
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.**
|
||||
@@ -112,36 +112,36 @@ generation. Precedent (RK3568 needing byte-level RE off RK3588) shows this is a
|
||||
|
||||
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
|
||||
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
|
||||
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**
|
||||
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
|
||||
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.
|
||||
- **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+
|
||||
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.
|
||||
|
||||
**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.
|
||||
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 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)
|
||||
|
||||
### Register / memory map (TRM Part1 — Part2 does not exist publicly)
|
||||
### Register / memory map (TRM Part1: Part2 does not exist publicly)
|
||||
|
||||
| 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`
|
||||
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
|
||||
override would set 700MHz — never applied.
|
||||
override would set 700MHz, never applied.
|
||||
|
||||
### UAPI (the seam an open userspace must target)
|
||||
|
||||
Two memory-manager paths, Kconfig-selected. **RV1106's shipping defconfig picks
|
||||
`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
|
||||
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`,
|
||||
@@ -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
|
||||
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.
|
||||
|
||||
### `/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/freq`, `/power` — exposed, unverified.
|
||||
- `/proc/rknpu/volt` — **CONFIRMED SIGSEGV** (null deref — no regulator wired). Never poll.
|
||||
- `/proc/rknpu/load`: **safe** (50 clean reads verified); the Monitor page polls it.
|
||||
- `/proc/rknpu/freq`, `/power`: exposed, unverified.
|
||||
- `/proc/rknpu/volt`: **CONFIRMED SIGSEGV** (null deref, no regulator wired). Never poll.
|
||||
|
||||
### Provenance / policy
|
||||
|
||||
@@ -215,26 +215,26 @@ open encoder exists.
|
||||
|
||||
## 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:
|
||||
|
||||
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`.
|
||||
Prove the four §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
|
||||
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
|
||||
verified, not just inspected.
|
||||
2. **DT**: board override `&npu { status="okay"; }`; `dtc -W` clean; re-check the
|
||||
"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`,
|
||||
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.
|
||||
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/`.
|
||||
5. **Verify ioctl**: host-buildable C program opens the node, issues
|
||||
`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
|
||||
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
|
||||
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,
|
||||
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.
|
||||
It does **not** require decoding the ISA.
|
||||
|
||||
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
|
||||
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),
|
||||
plus the input/output tensor buffers.
|
||||
2. Write an **open host tool** that: allocates the same GEM buffers via
|
||||
`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.
|
||||
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
|
||||
**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.
|
||||
|
||||
**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
|
||||
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):
|
||||
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
|
||||
status line.
|
||||
- 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
|
||||
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.
|
||||
- **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).
|
||||
- **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,
|
||||
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.
|
||||
@@ -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),
|
||||
`../../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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## 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`,
|
||||
`DRIVER_DATE "20230825"`), currently loaded on the shipping 5.10.160 kernel as
|
||||
`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
|
||||
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,
|
||||
`status = "disabled"`) — nothing to author, only enable:
|
||||
`status = "disabled"`), nothing to author, only enable:
|
||||
|
||||
```dts
|
||||
// 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
|
||||
(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`).
|
||||
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`,
|
||||
`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
|
||||
blocking on a hardware IRQ (`rknpu_job.c`, `wait_event_timeout`). Six ioctls total:
|
||||
`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)
|
||||
|
||||
`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
|
||||
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
|
||||
`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
|
||||
`drm_dev_alloc`/`drm_dev_register`, `rknpu_drv.c:725,730`), **not** the newer
|
||||
`/dev/accel/` framework mainline's own `rocket` driver uses (section 3). Don't confuse
|
||||
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 |
|
||||
|---|---|---|
|
||||
@@ -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_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_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_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_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 |
|
||||
| `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) |
|
||||
| `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_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_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) |
|
||||
|
||||
### 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
|
||||
### 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
|
||||
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
|
||||
|
||||
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`):
|
||||
|
||||
```dts
|
||||
@@ -140,19 +140,19 @@ touch the base `rv1106.dtsi`):
|
||||
};
|
||||
```
|
||||
|
||||
No new properties needed — `compatible`/`reg`/`interrupts`/`clocks`/`resets` are
|
||||
already correct and match the driver's own `rv1106_rknpu_config` exactly (§1).
|
||||
**Do not add an `iommus =` property for v1** — see §2.4.
|
||||
No new properties needed: `compatible`/`reg`/`interrupts`/`clocks`/`resets` are
|
||||
already correct and match the driver's own `rv1106_rknpu_config` exactly (section 1).
|
||||
**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
|
||||
|
||||
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:
|
||||
|
||||
```c
|
||||
// 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_opp_select.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,
|
||||
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
|
||||
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`
|
||||
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
|
||||
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
|
||||
avoids.
|
||||
|
||||
**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:
|
||||
- `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
|
||||
never sets it (no `power-domains` property, §1).
|
||||
`if (rknpu_dev->multiple_domains)`: true only for RK3588's 3-core NPU; RV1106
|
||||
never sets it (no `power-domains` property, section 1).
|
||||
- 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
|
||||
`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,
|
||||
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
|
||||
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
|
||||
`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.
|
||||
**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
|
||||
`#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) |
|
||||
|---|---|
|
||||
| `#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 |
|
||||
| `.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) |
|
||||
| `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) |
|
||||
| `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 |
|
||||
| `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 |
|
||||
|
||||
Net: outside the four-header fix in §2.4, this is expected to be a **build-fix-build
|
||||
pass**, not a rewrite — confirm by actually compiling into the tree (the checks
|
||||
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
|
||||
above are header-presence/signature verification, not a build).
|
||||
|
||||
### 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
|
||||
`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
|
||||
`iommus=` property, this churn never gets compiled against in the first place.
|
||||
- **dma-fence / `ROCKCHIP_RKNPU_FENCE`.** Cross-driver sync primitive, not needed
|
||||
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.
|
||||
- **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
|
||||
`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.
|
||||
|
||||
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`/
|
||||
`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.
|
||||
`rknpu_iommu.o`/`rknpu_debugger.o` compile clean into `built-in.a`; this is
|
||||
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
|
||||
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
|
||||
`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
|
||||
same node if available.
|
||||
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
|
||||
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_GET_DRV_VERSION_MAJOR/MINOR/PATCHLEVEL` macros,
|
||||
`rknpu_ioctl.h:52-54`) — and/or `RKNPU_GET_HW_VERSION` returns something
|
||||
plausible. This exercises the full ioctl-dispatch → power-get/put →
|
||||
`rknpu_ioctl.h:52-54`), and/or `RKNPU_GET_HW_VERSION` returns something
|
||||
plausible. This exercises the full ioctl-dispatch -> power-get/put ->
|
||||
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
|
||||
`.rknn` model, `librknnrt`. That's the userspace question — §4.
|
||||
`.rknn` model, `librknnrt`. That's the userspace question: section 4.
|
||||
|
||||
### 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
|
||||
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
|
||||
config table and of_match entry (§1), and the GEM/DRM surface is already correctly
|
||||
version-gated past 6.1 (§2.5). The concentrated risk is (a) actually compiling
|
||||
the §2.4 shims against real 6.18 headers rather than trusting the header-presence
|
||||
config table and of_match entry (section 1), and the GEM/DRM surface is already correctly
|
||||
version-gated past 6.1 (section 2.5). The concentrated risk is (a) actually compiling
|
||||
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
|
||||
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
|
||||
@@ -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
|
||||
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.
|
||||
|
||||
### 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
|
||||
(`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
|
||||
[`github.com/airockchip/rknpu`](https://github.com/airockchip/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
|
||||
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
|
||||
implementations of the same hardware class, and only one of them (the vendor one)
|
||||
covers RV1106 at all.
|
||||
|
||||
### 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),
|
||||
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).
|
||||
@@ -304,33 +304,33 @@ covers RV1106 at all.
|
||||
question**: `depends on (ARCH_ROCKCHIP && ARM64) || COMPILE_TEST`
|
||||
(`drivers/accel/rocket/Kconfig` in our 6.18.46 tree). RV1106 is a 32-bit
|
||||
Cortex-A7 (`arch/arm`, confirmed on our own hardware:
|
||||
`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
|
||||
`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
|
||||
signals no one has done the 32-bit validation work, on top of the register-level
|
||||
work below.
|
||||
- **Hardware coverage, per the driver's own docs**
|
||||
(`Documentation/accel/rocket/index.rst` in our tree): *"Hardware currently
|
||||
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 (
|
||||
[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))
|
||||
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
|
||||
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
|
||||
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/)),
|
||||
built on `accel/rocket` "with local fixes" atop the RK3588 Mesa merge request,
|
||||
requiring **byte-level comparison against captured vendor command streams** to
|
||||
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
|
||||
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
|
||||
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
|
||||
RK3576, which both have named, in-progress efforts). This is the least-covered
|
||||
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
|
||||
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
|
||||
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)
|
||||
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
|
||||
output**, and does not touch RV1106. This matches
|
||||
`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.
|
||||
|
||||
### 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
|
||||
public reverse-engineering project of any kind.** Every real inference workload on
|
||||
this NPU has to go through the closed pipeline
|
||||
(`npu-graphics-feasibility.md §1`: RKNN-Toolkit2 on a PC → `.rknn` blob →
|
||||
`librknnrt`/RKNN C API on-device) for the foreseeable future — porting the kernel
|
||||
(`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
|
||||
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
|
||||
it does.
|
||||
@@ -379,14 +379,14 @@ it does.
|
||||
```
|
||||
npu/
|
||||
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_opp_select.h
|
||||
soc/rockchip/rockchip_system_monitor.h
|
||||
soc/rockchip/rockchip_ipa.h
|
||||
```
|
||||
The vendor driver source itself is not duplicated here (same convention as
|
||||
`clk/`, `pinctrl/`, `mach/` — vendor source stays forward-ported in
|
||||
`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
|
||||
`clk/`, `pinctrl/`, `mach/`: vendor source stays forward-ported in
|
||||
`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
|
||||
patch series once M6 actually lands).
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
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
|
||||
`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,
|
||||
`/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`).
|
||||
|
||||
Trees involved:
|
||||
@@ -16,27 +16,27 @@ Trees involved:
|
||||
(v0.9.2, `DRIVER_DATE "20230825"`)
|
||||
|
||||
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
|
||||
nothing more; it does not and cannot unlock running a model).
|
||||
|
||||
## File set copied
|
||||
|
||||
Per `PORT-PLAN.md` §2.1's "Yes" column, copied verbatim from the vendor tree into
|
||||
`drivers/rknpu/` (patched in place afterward — see API-delta table below):
|
||||
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):
|
||||
|
||||
| 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_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_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_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_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_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 (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
|
||||
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),
|
||||
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"`
|
||||
@@ -45,33 +45,33 @@ keeps working. This also makes the header directly usable by
|
||||
|
||||
## 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`
|
||||
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
|
||||
block entirely (DRM_GEM is the only memory manager this port wires up, see
|
||||
`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
|
||||
`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`,
|
||||
`rknpu_debugger.o`, `rknpu_iommu.o`, `rknpu_gem.o` gated on
|
||||
`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
|
||||
(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
|
||||
**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.
|
||||
|
||||
Wired into `drivers/Makefile`: `obj-$(CONFIG_ROCKCHIP_RKNPU) += rknpu/` placed
|
||||
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
|
||||
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
|
||||
there, and building after `gpu/` specifically ensures the DRM core is ready first).
|
||||
|
||||
## Config symbols set
|
||||
|
||||
Via `./scripts/config --enable <SYMBOL>` then `make ARCH=arm CROSS_COMPILE=...
|
||||
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
|
||||
@@ -81,52 +81,52 @@ CONFIG_ROCKCHIP_RKNPU_PROC_FS=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:
|
||||
`ROCKCHIP_RKNPU_DMA_HEAP`, `ROCKCHIP_RKNPU_SRAM`, `ROCKCHIP_RKNPU_FENCE` — RV1106
|
||||
(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
|
||||
needs none of them (single-core NPU, no NBUF, no OPP table, no multi-domain
|
||||
fencing).
|
||||
|
||||
## 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
|
||||
addressed, but **not uniformly** — one of the four needed a genuinely fresh shim
|
||||
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
|
||||
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
|
||||
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:
|
||||
`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
|
||||
(`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
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
`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
|
||||
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
|
||||
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`,
|
||||
`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
|
||||
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
|
||||
comment added to each. The full struct definitions (`struct rockchip_opp_info`,
|
||||
`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
|
||||
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.
|
||||
|
||||
### 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`,
|
||||
`npu_devfreq_profile`, `npu_cooling_power`) that a naive manual read of the
|
||||
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
|
||||
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
|
||||
distrust vs. the compiler). Confirmed after the fact: **none** of
|
||||
`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
|
||||
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.
|
||||
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
|
||||
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
|
||||
port had to force.
|
||||
|
||||
## 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 |
|
||||
|---|---|
|
||||
| 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. |
|
||||
| 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). |
|
||||
|
||||
### `rknpu_gem.c`
|
||||
|
||||
| # | 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. |
|
||||
| 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). |
|
||||
| 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**
|
||||
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
|
||||
vendor.
|
||||
|
||||
## Device tree change
|
||||
|
||||
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
|
||||
= <420000000>`, `resets` (`SRST_A_RKNN`/`SRST_H_RKNN`) — left untouched, per
|
||||
instructions, exactly as `PORT-PLAN.md` §2.3 specified):
|
||||
= <420000000>`, `resets` (`SRST_A_RKNN`/`SRST_H_RKNN`), left untouched, per
|
||||
instructions, exactly as `PORT-PLAN.md` section 2.3 specified):
|
||||
|
||||
```dts
|
||||
&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
|
||||
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
|
||||
`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
|
||||
@@ -235,7 +235,7 @@ etc.) was reverted or altered.
|
||||
## 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`
|
||||
(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):
|
||||
|
||||
```
|
||||
@@ -259,7 +259,7 @@ symbol presence the task asked to confirm.
|
||||
## Hardware test program: `rknpu_version_test.c`
|
||||
|
||||
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:
|
||||
|
||||
```sh
|
||||
@@ -271,11 +271,11 @@ arm-rockchip830-linux-uclibcgnueabihf-gcc \
|
||||
<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
|
||||
ELF binary with exit code 0. One expected, harmless warning appears:
|
||||
`#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
|
||||
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:
|
||||
- `RKNPU_GET_DRV_VERSION` returns `RKNPU_GET_DRV_VERSION_CODE(DRIVER_MAJOR,
|
||||
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
|
||||
program decodes back to `"0.9.2"` via the UAPI header's own
|
||||
`RKNPU_GET_DRV_VERSION_{MAJOR,MINOR,PATCHLEVEL}()` macros.
|
||||
- `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
|
||||
published decode table exists for it (per `PORT-PLAN.md` §3 step 5, "checks...
|
||||
`VERSION`/`VERSION_NUM` registers (`rknpu_job.c:rknpu_get_hw_version()`): no
|
||||
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.
|
||||
|
||||
**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
|
||||
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.
|
||||
|
||||
## 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
|
||||
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
|
||||
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
|
||||
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` section 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
|
||||
`PORT-PLAN.md` §1 and `OPEN-NPU-PLAN.md` §1.3 for why mainline's own
|
||||
`drivers/accel/rocket/` driver — RK3588/ARM64-only — is a different codebase
|
||||
`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
|
||||
that doesn't reach RV1106 at all).
|
||||
- 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.
|
||||
- **Not attempted, not required for this milestone**: `DRM_IOCTL_RKNPU_SUBMIT`,
|
||||
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
|
||||
any `.rknn` model, `librknnrt`, that's the closed-userspace question
|
||||
`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).
|
||||
|
||||
## 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
|
||||
{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
|
||||
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.
|
||||
```
|
||||
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.
|
||||
|
||||
## 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
|
||||
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"`).
|
||||
(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`.)
|
||||
(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`.)
|
||||
|
||||
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`.
|
||||
|
||||
## 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
|
||||
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
|
||||
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
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* RKNPU_GET_HW_VERSION, and prints the decoded results. This exercises the
|
||||
* full ioctl-dispatch -> power-get/put -> clock/reset path with zero
|
||||
* 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
|
||||
* -I<kernel-tree>/include/uapi
|
||||
@@ -41,7 +41,7 @@
|
||||
* value read directly off the NPU core's VERSION/VERSION_NUM registers
|
||||
* (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
|
||||
* 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>
|
||||
|
||||
@@ -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
|
||||
`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`.
|
||||
|
||||
## 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)`.
|
||||
- added `#include <linux/of_device.h>` for `of_match_device()`.
|
||||
- copied the vendor-only header `linux/soc/rockchip/pvtm.h`.
|
||||
|
||||
## 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
|
||||
(`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
|
||||
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
|
||||
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
|
||||
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:
|
||||
```
|
||||
rga: rga2, irq = 55, match scheduler
|
||||
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
|
||||
`drivers/video/rockchip/{Kconfig,Makefile}` created (source rga3/); wired into
|
||||
`drivers/video/{Kconfig,Makefile}`. `CONFIG_ROCKCHIP_MULTI_RGA=y`.
|
||||
`&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).
|
||||
|
||||
## 5.10 → 6.18 API deltas fixed (all mechanical)
|
||||
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,
|
||||
HRTIMER_MODE_REL)` — the scheduler tick; verified under the live probe.
|
||||
## 5.10 -> 6.18 API deltas fixed (all mechanical)
|
||||
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,
|
||||
HRTIMER_MODE_REL)`: the scheduler tick; verified under the live probe.
|
||||
3. `iommu_map` / `iommu_map_sg`: added the new `gfp` arg (`GFP_KERNEL`).
|
||||
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).
|
||||
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
|
||||
(`../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.
|
||||
|
||||
**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
|
||||
`<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
|
||||
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
|
||||
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
|
||||
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
|
||||
`drivers/media/platform/rockchip/rga/` V4L2 M2M driver — is rejected on both counts the
|
||||
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
|
||||
task asked to check:
|
||||
|
||||
1. **Register incompatibility.** Mainline's driver (current `torvalds/linux` master,
|
||||
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
|
||||
("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_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*
|
||||
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,
|
||||
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
|
||||
older baseline core. A pending upstream series (Jianfeng Liu,
|
||||
`20240322052915.3507937-1-liujianfeng1994@gmail.com`, "media: rockchip: rga: Add
|
||||
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.
|
||||
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
|
||||
`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.
|
||||
@@ -51,30 +51,30 @@ task asked to check:
|
||||
Real path: `flare-edge/sdk/sysdrv/source/kernel/drivers/video/rockchip/rga3/`
|
||||
(`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
|
||||
`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
|
||||
**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
|
||||
(`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
|
||||
symbol set; `drivers/media/platform/rockchip/rga` isn't referenced by any rv1106
|
||||
defconfig or DT). Ignore them; `rga3/` is the one true source.
|
||||
|
||||
| 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 |
|
||||
| `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_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_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_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
|
||||
`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
|
||||
`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
|
||||
vendor DT node:
|
||||
```c
|
||||
// rv1106.dtsi:1154-1161
|
||||
@@ -87,25 +87,25 @@ rga2: rga@ff980000 {
|
||||
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,
|
||||
`../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"`,
|
||||
`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
|
||||
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.
|
||||
|
||||
**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
|
||||
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
|
||||
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`).
|
||||
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`).
|
||||
`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
|
||||
`/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
|
||||
`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
|
||||
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
|
||||
build and boot on), not guessed:
|
||||
@@ -127,12 +127,12 @@ build and boot on), not guessed:
|
||||
| # | 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. |
|
||||
| 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. |
|
||||
| 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). |
|
||||
| 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. |
|
||||
| 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. |
|
||||
| 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). |
|
||||
| 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. |
|
||||
|
||||
## 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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
for bring-up (dumps registered buffers/jobs) but not required for `/dev/rga` to exist
|
||||
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.
|
||||
|
||||
## 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,
|
||||
# rga_hw_config.c, rga_mm.c, rga_dma_buf.c,
|
||||
# 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)
|
||||
```
|
||||
|
||||
@@ -184,8 +184,8 @@ obj-$(CONFIG_ROCKCHIP_MULTI_RGA) += rga3.o
|
||||
(drops the `rga3-$(CONFIG_ROCKCHIP_RGA_ASYNC) += rga_fence.o` and
|
||||
`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,
|
||||
`hrtimer_init`+`.function=` → `hrtimer_setup()`) during the transplant, same
|
||||
Apply the two API-delta fixes from the table above (`platform_driver.remove` -> void,
|
||||
`hrtimer_init`+`.function=` -> `hrtimer_setup()`) during the transplant, same
|
||||
build-fix-build loop already used for `clk-rv1106.c`/`pinctrl-rockchip.c`
|
||||
(`PORT-STATUS.md` M1).
|
||||
|
||||
@@ -197,34 +197,34 @@ CONFIG_DMABUF_HEAPS=y
|
||||
CONFIG_DMABUF_HEAPS_CMA=y
|
||||
```
|
||||
(`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
|
||||
vendor fragment's own rationale (`flare-edge.config:23`: builtin so devtmpfs creates
|
||||
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
|
||||
`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:
|
||||
```dts
|
||||
&rga2 {
|
||||
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
|
||||
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
|
||||
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: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
|
||||
`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):
|
||||
```dts
|
||||
// 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
|
||||
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
|
||||
`/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
|
||||
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.
|
||||
|
||||
### 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
|
||||
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
|
||||
@@ -263,11 +263,11 @@ implied by this kernel port.
|
||||
## Verify steps
|
||||
|
||||
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`),
|
||||
confirm dmesg shows `rga2, irq = 87, match scheduler` and
|
||||
`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.
|
||||
`ls -l /dev/rga` exists with no manual `mknod`/`insmod`.
|
||||
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
|
||||
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
|
||||
`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
|
||||
`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
|
||||
the hrtimer-driven completion path (API-delta #2) that WardenOS's real usage
|
||||
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
|
||||
`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()`,
|
||||
`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").
|
||||
|
||||
## 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
|
||||
regression.
|
||||
- **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
|
||||
- **CMA pool sizing**: 10 MiB was sized against the 5.10 image's actual usage (graph
|
||||
canvas + scanout mirror at 720x720x4B ~ 2 MiB each); carry the same size unless a
|
||||
future accounting shows it's tight.
|
||||
- **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
|
||||
@@ -311,10 +311,10 @@ implied by this kernel port.
|
||||
- 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}`,
|
||||
`.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
|
||||
- [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`)
|
||||
- [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)
|
||||
- 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
|
||||
- [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`)
|
||||
- [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)
|
||||
- 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
|
||||
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
|
||||
`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
|
||||
&rng { compatible = "rockchip,rv1106-rng"; status = "okay"; };
|
||||
```
|
||||
|
||||
**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.
|
||||
|
||||
## OTP / nvmem — VERIFIED on warden-c8a3 (2026-08-25)
|
||||
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.
|
||||
## OTP / nvmem: VERIFIED on warden-c8a3 (2026-08-25)
|
||||
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.
|
||||
Mainline `drivers/nvmem/rockchip-otp.c` gains an `rv1106_data` + compatible.
|
||||
|
||||
**Delta:**
|
||||
@@ -42,7 +42,7 @@ static const struct rockchip_data rv1106_data = {
|
||||
Kconfig `NVMEM_ROCKCHIP_OTP=y`. **DT:** `&otp { status = "okay"; };` (the dtsi
|
||||
`otp@ff3d0000` node already carries compatible + the 6 clocks/resets).
|
||||
|
||||
**First try** used `.reg_read = rk3588_otp_read` → `timeout during read setup`
|
||||
(rk3588 uses a different addressing path). Corrected to `px30_otp_read` — the
|
||||
**First try** used `.reg_read = rk3588_otp_read` -> `timeout during read setup`
|
||||
(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
|
||||
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),
|
||||
ported to 6.18. Only 5.10→6.18 API delta: `rtc_register_device` →
|
||||
`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` ->
|
||||
`devm_rtc_register_device` (paired with the existing `devm_rtc_allocate_device`).
|
||||
Enable `CONFIG_RTC_DRV_ROCKCHIP=y` + `&rtc { status="okay"; }`. Verified on
|
||||
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.
|
||||
|
||||
@@ -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
|
||||
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
|
||||
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,
|
||||
`arch_sys_counter`) trusts them.
|
||||
|
||||
@@ -28,14 +28,14 @@ distinguish these.)
|
||||
|
||||
## 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:
|
||||
|
||||
arm,cpu-registers-not-fw-configured;
|
||||
clock-frequency = <MEASURED_HZ>;
|
||||
|
||||
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
|
||||
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/`.
|
||||
@@ -50,5 +50,5 @@ one path). Ship as an update to the arch/dts patch in `patches/`.
|
||||
## Bench access note
|
||||
|
||||
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.
|
||||
|
||||
@@ -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
|
||||
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
|
||||
```
|
||||
`/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`
|
||||
|
||||
@@ -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
|
||||
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
|
||||
GT911 node — so nothing drove the GT911.
|
||||
GT911 node, so nothing drove the GT911.
|
||||
|
||||
## Fix
|
||||
|
||||
1. **`CONFIG_TOUCHSCREEN_GOODIX=y`** — build the mainline Goodix driver *into* the
|
||||
kernel (no module → no vermagic/struct mismatch; the stale rootfs `.ko` still
|
||||
1. **`CONFIG_TOUCHSCREEN_GOODIX=y`**: build the mainline Goodix driver *into* the
|
||||
kernel (no module -> no vermagic/struct mismatch; the stale rootfs `.ko` still
|
||||
fails to insmod but is now harmless).
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
## Files changed (research/linux-6.18.46)
|
||||
|
||||
- `.config` — `CONFIG_TOUCHSCREEN_GOODIX=y`.
|
||||
- `arch/arm/boot/dts/rockchip/rv1106-warden.dts` — GT911 node on `&i2c3`,
|
||||
- `.config`: `CONFIG_TOUCHSCREEN_GOODIX=y`.
|
||||
- `arch/arm/boot/dts/rockchip/rv1106-warden.dts`: GT911 node on `&i2c3`,
|
||||
`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;
|
||||
* 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
|
||||
* 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[] = {
|
||||
{
|
||||
|
||||
@@ -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
|
||||
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
|
||||
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
|
||||
forward-port of our own vendor source, same method already proven for
|
||||
clk/pinctrl/mach/mmc.
|
||||
|
||||
## 0. Source recommendation
|
||||
|
||||
**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
|
||||
is not a default-to-what-we-have choice — it's the correct one on the evidence:
|
||||
`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:
|
||||
|
||||
- **Nothing more upstream exists.** AICSemi has no public upstream driver repo
|
||||
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).
|
||||
There is nothing more "canonical" to port from than the vendor source we
|
||||
already have.
|
||||
- **Our tree already carries two hardware-verified fixes newer forks don't
|
||||
have**: the `queue_sz`-zero console-flood clamp
|
||||
(`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
|
||||
`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
|
||||
`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
|
||||
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
|
||||
hardware.
|
||||
- **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
|
||||
`.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.
|
||||
- **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,
|
||||
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
|
||||
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
|
||||
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
|
||||
variants generically, not our AIC8800DC + our two local hardening patches,
|
||||
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.
|
||||
- **No mainline/staging path exists to lean on instead.** Confirmed by the
|
||||
research pass: no aic8800 in `drivers/net/wireless/`, `staging/`, or any
|
||||
@@ -72,7 +72,7 @@ aic8800dc/
|
||||
Makefile # obj-$(CONFIG_AIC8800_BTLPM_SUPPORT) += aic8800_btlpm/
|
||||
# obj-$(CONFIG_AIC8800_WLAN_SUPPORT) += aic8800_fdrv/
|
||||
# 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)
|
||||
aic_bsp_main.c # module_init/exit; per-chip firmware filename tables (fw_u02,
|
||||
# fw_8800dc_u01, fw_8800dc_u02, ...); aicbsp_probe_semaphore
|
||||
@@ -80,7 +80,7 @@ aic8800dc/
|
||||
# wildcard match + internal vendor/device ID probe:
|
||||
# SDIO_VENDOR_ID_AIC8800DC=0xc8a1, SDIO_DEVICE_ID_AIC8800DC=0xc08d
|
||||
# (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
|
||||
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)
|
||||
@@ -88,51 +88,51 @@ aic8800dc/
|
||||
# (5732), wiphy_register() (6035); struct net_device_ops
|
||||
# rwnx_netdev_ops (1457) / rwnx_netdev_monitor_ops (1477);
|
||||
# 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)
|
||||
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;
|
||||
# it lives inside aic8800_fdrv/, not a separate directory. Also
|
||||
# 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
|
||||
# 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
|
||||
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.
|
||||
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.c, aic_bluetooth_main.c, lpm.c, rfkill.c # standard rfkill_register(), no
|
||||
# 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
|
||||
`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):
|
||||
```
|
||||
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)
|
||||
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)
|
||||
```
|
||||
`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
|
||||
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
|
||||
`../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
|
||||
**`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
|
||||
`/**********SDIO-WIFI**********/` over the `&sdmmc` node) and independently in
|
||||
`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.
|
||||
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
|
||||
(`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:
|
||||
```dts
|
||||
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
|
||||
`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
|
||||
files the M1–M3 work already ported.
|
||||
separate include from `rv1106.dtsi`): port that block, it isn't in the DT
|
||||
files the M1-M3 work already ported.
|
||||
|
||||
## 2. Open-source landscape (web research)
|
||||
|
||||
- **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
|
||||
point at than what we have.
|
||||
- **Mainline/staging**: **absent.** No aic8800 anywhere in `drivers/net/wireless/`,
|
||||
`drivers/staging/`, or any LKML/patchwork series as of this research pass.
|
||||
- **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),
|
||||
covering SDIO/USB/PCIe AIC8800D80 variants. Firmware ships as a companion
|
||||
`aic8800-firmware` package into `/lib/firmware/aic8800_fw/`. Treat as a
|
||||
**reference/oracle for the API-delta hunks** (§3), not a vendoring source —
|
||||
see §0 for why. An Armbian forum thread ("AIC8800 wifi sdio module not
|
||||
**reference/oracle for the API-delta hunks** (section 3), not a vendoring source,
|
||||
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
|
||||
boundary, so cross-check rather than trust each hunk blindly.
|
||||
- **Other community efforts** (Armbian, LuckFox's own OpenWrt branch, generic
|
||||
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`
|
||||
only, no `kmod-aic8800`). Confirms there is no ready-made newer-kernel package
|
||||
to pull instead of porting.
|
||||
- **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
|
||||
`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
|
||||
below) so this is a checklist against *our* code, not a generic survey.
|
||||
|
||||
### 3.1 Timers — confirmed hard breaks, exact call sites found
|
||||
`del_timer_sync`/`del_timer` → **`timer_delete_sync`/`timer_delete`**: renamed
|
||||
### 3.1 Timers: confirmed hard breaks, exact call sites found
|
||||
`del_timer_sync`/`del_timer` -> **`timer_delete_sync`/`timer_delete`**: renamed
|
||||
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
|
||||
exist**. `from_timer()` → **`timer_container_of()`**: a real mainline rename
|
||||
(~6.14–6.15 treewide timer-API cleanup; exact tag unconfirmed, verify against
|
||||
exist**. `from_timer()` -> **`timer_container_of()`**: a real mainline rename
|
||||
(~6.14-6.15 treewide timer-API cleanup; exact tag unconfirmed, verify against
|
||||
the actual 6.18.46 headers already unpacked at
|
||||
`flare-edge/research/linux-6.18.46/include/linux/timer.h`). Every call site in
|
||||
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` |
|
||||
| `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` |
|
||||
| `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 |
|
||||
|
||||
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.
|
||||
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_setup()` itself is unchanged.
|
||||
|
||||
### 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`
|
||||
already has `#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0)` /
|
||||
`#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)
|
||||
still compiles against the `ndo_siocdevprivate` signature
|
||||
(`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
|
||||
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
|
||||
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.
|
||||
|
||||
### 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`**
|
||||
(the actual target tree, not a guess from release notes):
|
||||
- **`cfg80211_connect_result()`**: **confirmed unchanged and safe.** In 6.18.46
|
||||
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
|
||||
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
|
||||
`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`
|
||||
@@ -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.
|
||||
- **`wiphy_new()`/`wiphy_new_nm()`**: **confirmed unchanged.**
|
||||
`wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)`
|
||||
(`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))`
|
||||
(`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))`
|
||||
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()`/
|
||||
`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
|
||||
from the `wiphy_lock()` doc comment — **"When cfg80211 ops are called, the
|
||||
mechanism all exist in 6.18.46, and (the decisive sentence, directly
|
||||
from the `wiphy_lock()` doc comment) **"When cfg80211 ops are called, the
|
||||
wiphy is already locked."** That means:
|
||||
- The driver's `cfg80211_ops` callbacks themselves (`rwnx_cfg80211_scan`,
|
||||
`_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
|
||||
holding RTNL instead.
|
||||
- The real risk is the **other direction**: this driver calls
|
||||
`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`
|
||||
(also async paths, not the ops entry points). Multiple `cfg80211.h` doc
|
||||
comments for adjacent notification APIs state "the caller must hold ...
|
||||
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
|
||||
`cfg80211_*` notification call reached from `rwnx_msg_rx.c` /
|
||||
the async paths in `rwnx_main.c` needs `wiphy_lock(wiphy)` /
|
||||
`wiphy_unlock(wiphy)` wrapped around it that wasn't there before** (5.10
|
||||
only needed `rtnl_lock()`, which this driver's 3 existing
|
||||
`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,
|
||||
not at those 3 sites, which are netdev-registration paths and likely stay
|
||||
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
|
||||
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
|
||||
by one, and because a wrong lock order (wiphy mutex vs. RTNL vs. this
|
||||
driver's own internal locks/semaphores) produces lockdep splats or
|
||||
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
|
||||
transition on its 6.12/6.13 port — it hit this exact wall first.
|
||||
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.
|
||||
|
||||
### 3.4 SDIO/MMC
|
||||
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
|
||||
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
|
||||
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*
|
||||
above the `sdmmc` bus is new work.
|
||||
|
||||
### 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
|
||||
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.
|
||||
|
||||
### 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
|
||||
`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
|
||||
`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/`).
|
||||
Call sites: `aic8800_bsp/aicsdio.c:515,517,556,580` and
|
||||
`aic8800_fdrv/aicwf_sdio.c:1260,1262,1329,1331` (all under
|
||||
`#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
|
||||
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
|
||||
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
|
||||
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
|
||||
auto-rescans. Treat every call site as `#if 0`/deleted, not ported.
|
||||
`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`/
|
||||
`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.
|
||||
|
||||
### 3.8 Firmware loading
|
||||
`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
|
||||
(`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
|
||||
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.
|
||||
@@ -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.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`/
|
||||
`modules.order` artifact already sitting in the vendor tree from prior
|
||||
out-of-tree builds) into the 6.18 tree at:
|
||||
```
|
||||
flare-edge/research/linux-6.18.46/drivers/net/wireless/aic8800/
|
||||
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_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
|
||||
error proves them referenced elsewhere)
|
||||
aic8800_btlpm/ (from aic8800dc/aic8800_btlpm/)
|
||||
```
|
||||
This is exactly the path the vendor's own `Kconfig` already assumes
|
||||
(`source "drivers/net/wireless/aic8800/aic8800_fdrv/Kconfig"`, §1) — no path
|
||||
rewriting needed inside the sub-Kconfigs. Firmware blobs (§5) go to
|
||||
(`source "drivers/net/wireless/aic8800/aic8800_fdrv/Kconfig"`, section 1), no path
|
||||
rewriting needed inside the sub-Kconfigs. Firmware blobs (section 5) go to
|
||||
`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
|
||||
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`
|
||||
— pull it from there; it is *not* in this branch's own `sdk-patches/wifi/`, a
|
||||
`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
|
||||
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
|
||||
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"
|
||||
```
|
||||
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
|
||||
`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
|
||||
`aic8800_fdrv/Kconfig` (`AIC8800_WLAN_SUPPORT`) and `aic8800_btlpm/Kconfig`
|
||||
(`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
|
||||
(`../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
|
||||
@@ -395,7 +395,7 @@ yet either).
|
||||
### 4.3 Config symbols (`=y`, built-in)
|
||||
```
|
||||
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_MMC=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_AIC8800_WLAN_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
|
||||
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,
|
||||
# per hardware-86-panel.md: UART1/ttyS1, hciattach -s 1500000
|
||||
# ... any 1500000 flow nosleep)
|
||||
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
|
||||
# ../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
|
||||
inspection (§3.3 note, no `ieee80211_hw`/mac80211 symbol usage anywhere in
|
||||
Do **not** enable `CONFIG_MAC80211` for this driver: confirmed by source
|
||||
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
|
||||
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
|
||||
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).
|
||||
|
||||
### 4.4 DT changes
|
||||
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`):
|
||||
1. Port the `sdmmc0_{clk,cmd,bus4,det}` pinctrl group from
|
||||
`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).
|
||||
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
|
||||
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).
|
||||
3. `status = "okay"` on `&sdmmc`, matching the vendor board DT exactly (§1).
|
||||
4. **Do not** touch `&sdio` (mmc@ff9a0000) — leave `disabled`, it's genuinely
|
||||
unused hardware on this board (§1 correction).
|
||||
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
|
||||
unused hardware on this board (section 1 correction).
|
||||
|
||||
### 4.5 Driver init-order note
|
||||
The 5.10 module load order (`insmod_wifi.sh`, §1) is
|
||||
`aic8800_bsp → (200ms) → aic8800_fdrv → (2s!) → aic8800_btlpm`, with real sleep
|
||||
delays between each. Built-in (`=y`), there is no equivalent explicit delay —
|
||||
The 5.10 module load order (`insmod_wifi.sh`, section 1) is
|
||||
`aic8800_bsp -> (200ms) -> aic8800_fdrv -> (2s!) -> aic8800_btlpm`, with real sleep
|
||||
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
|
||||
is already `aic8800_btlpm/ aic8800_fdrv/ aic8800_bsp/` in Makefile-declaration
|
||||
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
|
||||
`obj-y` list**, mirroring the working insmod order) and by each subsystem's
|
||||
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
|
||||
them). The observed 2-second gap between `aic8800_bsp` and `aic8800_fdrv` on
|
||||
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,
|
||||
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
|
||||
formality.
|
||||
|
||||
## 5. Firmware notes
|
||||
|
||||
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,
|
||||
`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
|
||||
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`
|
||||
(=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
|
||||
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
|
||||
concept; on the 6.18 rootfs, point this at wherever WardenOS's 6.18 Buildroot
|
||||
userspace places firmware (likely `/lib/firmware/aic8800dc/` if following
|
||||
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
|
||||
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
|
||||
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
|
||||
(`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
|
||||
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).
|
||||
@@ -504,34 +504,34 @@ is authoritative.
|
||||
## 6. Verify steps
|
||||
|
||||
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`
|
||||
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/`
|
||||
objects linked into `vmlinux`/the zImage).
|
||||
2. **Probe**: boot on `warden-c8a3`, confirm in `dmesg`: the `sdmmc` MMC host
|
||||
binds (`dwmmc_rockchip ffaa0000.mmc: ...`, same pattern M3 already proved
|
||||
for `ffa90000.mmc`/eMMC), then an SDIO card enumerates on it, then
|
||||
`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`.
|
||||
3. **cfg80211 sanity**: `iw phy` shows the expected single 2.4 GHz band/14
|
||||
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).
|
||||
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
|
||||
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
|
||||
WardenOS's own `wlan0` ownership model — do **not** start `rkwifi_server`,
|
||||
§1) and confirm `COMPLETED` state plus a DHCP lease — exercises
|
||||
WardenOS's own `wlan0` ownership model, do **not** start `rkwifi_server`,
|
||||
section 1) and confirm `COMPLETED` state plus a DHCP lease, exercises
|
||||
`rwnx_cfg80211_connect`/`cfg80211_connect_result()`.
|
||||
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
|
||||
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
|
||||
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`).
|
||||
7. **BT** (secondary to WiFi but same milestone): confirm `aic8800_btlpm`
|
||||
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/include/net/cfg80211.h` (directly read to
|
||||
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),
|
||||
`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}`,
|
||||
`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
|
||||
sdio module not working with kernel 6.12+"); kernel.org/patchwork commits
|
||||
`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.
|
||||
Patchwork/LWN direct fetches were partially blocked by anti-bot walls during
|
||||
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
|
||||
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).
|
||||
|
||||
## 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
|
||||
-Wmissing-prototypes` on internal non-`static` helper functions that were
|
||||
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`;
|
||||
`rv1106-warden.dtb` compiles with only the pre-existing, unrelated `&rgb`
|
||||
graph-endpoint warning (already known from M4 display work, not
|
||||
wifi-related). Confirmed via `nm vmlinux.unstripped` that the driver is
|
||||
actually linked in, not silently dropped: `aicbsp_sdio_driver`,
|
||||
`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_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`.
|
||||
|
||||
**One exception the plan flagged correctly**: `rwnx_mesh.{c,h}` was
|
||||
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
|
||||
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
|
||||
preprocessor-gated, so `rwnx_mesh.h`'s types (`struct rwnx_mesh_path`,
|
||||
`struct rwnx_mesh_proxy`) and prototypes are needed to compile even though
|
||||
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.
|
||||
`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.
|
||||
|
||||
New in-tree Kbuild wiring (not copied from vendor, vendor Makefiles are
|
||||
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.
|
||||
Wired into `drivers/net/wireless/Kconfig` (new `source` line, alongside
|
||||
the existing vendor Kconfig list).
|
||||
- `drivers/net/wireless/aic8800/Makefile` — `obj-y` list preserving
|
||||
bsp → fdrv → btlpm link order (per PORT-PLAN.md §4.5, since these are
|
||||
- `drivers/net/wireless/aic8800/Makefile`, `obj-y` list preserving
|
||||
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
|
||||
`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
|
||||
has no working module pipeline yet).
|
||||
- Three new in-tree `Makefile`s (`aic8800_bsp/`, `aic8800_fdrv/`,
|
||||
`aic8800_btlpm/`) translating the vendor's `ccflags-$(CONFIG_X) += -DX`
|
||||
pattern into fixed `-D` flags for the one build configuration this board
|
||||
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`
|
||||
needs no `-D`: it's now a real Kconfig string symbol, so
|
||||
`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`).
|
||||
|
||||
## API-delta fixes (file → change)
|
||||
## API-delta fixes (file -> change)
|
||||
|
||||
### Plan-anticipated mechanical renames (applied via scoped `sed`, all call sites)
|
||||
- `from_timer()` → `timer_container_of()` — 7 sites: `aicsdio.c`,
|
||||
`aicwf_sdio.c` (×3), `rwnx_main.c`, `rwnx_rx.c` (×2).
|
||||
- `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 —
|
||||
- `from_timer()` -> `timer_container_of()`, 7 sites: `aicsdio.c`,
|
||||
`aicwf_sdio.c` (x3), `rwnx_main.c`, `rwnx_rx.c` (x2).
|
||||
- `del_timer_sync()` -> `timer_delete_sync()`, 10 sites: `aicsdio.c`,
|
||||
`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_rx.c` (×2).
|
||||
- `del_timer()` → `timer_delete()` — 9 sites: `aicwf_tcp_ack.c` (×4),
|
||||
`rwnx_rx.c` (×2), `aic8800_btlpm/aic8800_btlpm.c` (×3, dead — not in this
|
||||
`rwnx_rx.c` (x2).
|
||||
- `del_timer()` -> `timer_delete()`, 9 sites: `aicwf_tcp_ack.c` (x4),
|
||||
`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`
|
||||
(×3, same — `lpm.c` isn't compiled, `CONFIG_SUPPORT_LPM=n`).
|
||||
- `netif_rx_ni()` → `netif_rx()` — 3 sites, all `rwnx_rx.c`.
|
||||
(x3, same, `lpm.c` isn't compiled, `CONFIG_SUPPORT_LPM=n`).
|
||||
- `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)
|
||||
- **`cfg80211_rx_spurious_frame()` / `cfg80211_rx_unexpected_4addr_frame()`**
|
||||
(`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).
|
||||
- **`cfg80211_ch_switch_notify()` / `cfg80211_ch_switch_started_notify()`**
|
||||
(`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
|
||||
header).
|
||||
- **`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
|
||||
accepted and ignored):
|
||||
- `change_beacon`: now takes `struct cfg80211_ap_update *` (wraps the old
|
||||
`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.
|
||||
- `set_monitor_channel`: gained a `struct net_device *dev` param. Rather
|
||||
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).
|
||||
- **`wakeup_source_create()`/`_add()`/`_remove()`/`_destroy()`** (`rwnx_wakelock.c`):
|
||||
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
|
||||
now the only supported entry point (confirmed: `wakeup_source_register()`'s
|
||||
own implementation is literally `create()` + conditional sysfs +
|
||||
`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`,
|
||||
matching the old `wakeup_source_create()` behavior used here. (The file
|
||||
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.)
|
||||
- **`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
|
||||
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
|
||||
real namespace gate). `MODULE_INFO()`/`MODULE_IMPORT_NS()` now require a
|
||||
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
|
||||
one `#include` wasn't behind any guard. Guarded it behind
|
||||
`AICWF_PCIE_SUPPORT` (matching the guard its only two callers,
|
||||
`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()`
|
||||
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
|
||||
real `rockchip_wifi_power()`/`_set_carddetect()` call site first: all of
|
||||
them are either already `#if 0`'d out in the vendor source
|
||||
(`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
|
||||
removing. The DT's `mmc-pwrseq-simple` node does the power/reset
|
||||
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
|
||||
(`SET_TX`, `RDWR_EFUSE_*`, ~50 constants) and struct typedefs
|
||||
(`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.
|
||||
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
|
||||
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_rx_*`, `crc8_ponl_107`), its own message/debug-command layer
|
||||
(`rwnx_send_dbg_*_req`, `rwnx_cmd_mgr_{init,deinit}`,
|
||||
`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
|
||||
function for real runtime operation. **49 symbols total**, `ld` caught
|
||||
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
|
||||
anyone repeating this kind of port).
|
||||
|
||||
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
|
||||
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
|
||||
`aicbsp_priv_` prefix, scoped strictly to `aic8800_bsp/*.{c,h}` (both
|
||||
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
|
||||
different (smaller-footprint) fix: `aic8800_bsp/md5.c` and
|
||||
`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.
|
||||
|
||||
## Config symbols set
|
||||
@@ -208,11 +208,11 @@ CONFIG_AIC8800_BTLPM_SUPPORT=y
|
||||
CONFIG_AIC_FW_PATH="/oem/usr/ko/aic8800dc_fw"
|
||||
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_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_H4=y (already 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
|
||||
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>`,
|
||||
`supports-sdio`, `mmc-pwrseq = <&sdio_pwrseq>`, pinctrl
|
||||
`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.
|
||||
|
||||
**Note**: the `&sdmmc` node itself (`mmc@ffaa0000`) in `rv1106.dtsi` and
|
||||
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"` +
|
||||
properties) and the new `sdio_pwrseq` node. Verified in the compiled
|
||||
`.dtb` (decompiled with `dtc -I dtb -O dts`) that both nodes carry the
|
||||
@@ -245,7 +245,7 @@ correct phandles/properties.
|
||||
## Hardening patches
|
||||
|
||||
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
|
||||
previously applied) patch detected" against the freshly-copied tree, and
|
||||
`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
|
||||
(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/`,
|
||||
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.
|
||||
|
||||
## 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
|
||||
board DTS wifi nodes (`rv1106-warden.dts`, additive only), and
|
||||
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_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
|
||||
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
|
||||
|
||||
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
|
||||
eMMC pattern on `ffa90000.mmc`), then an SDIO card enumerating on it
|
||||
(vendor/device ID `0xc8a1`/`0xc08d`), then `aic8800_bsp`'s probe firing.
|
||||
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
|
||||
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
|
||||
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.
|
||||
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
|
||||
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).
|
||||
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
|
||||
need no new locking per the plan's analysis, since cfg80211 core now
|
||||
holds the wiphy mutex before calling in; the async-context notification
|
||||
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
|
||||
changes above — if scan/connect hang or lockdep splats appear, look
|
||||
changes above, if scan/connect hang or lockdep splats appear, look
|
||||
there first).
|
||||
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/
|
||||
@@ -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),
|
||||
if anything behaves subtly wrong in the bsp-side firmware bring-up
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
## 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.
|
||||
- `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.
|
||||
- `wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> ... link/ether <device MAC>`
|
||||
- `iw dev wlan0 scan` found real APs (SSIDs/BSSIDs redacted for publication):
|
||||
- **the site AP at 2412 MHz, −43 dBm**
|
||||
- a neighboring guest AP at 2412 MHz, −73 dBm
|
||||
- +several more, correct signal strengths → RF path fully functional.
|
||||
- **the site AP at 2412 MHz, -43 dBm**
|
||||
- a neighboring guest AP at 2412 MHz, -73 dBm
|
||||
- +several more, correct signal strengths -> RF path fully functional.
|
||||
|
||||
## Why MODULES, not built-in (=y)
|
||||
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
|
||||
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`).
|
||||
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
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
## 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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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).
|
||||
#
|
||||
# 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.
|
||||
# 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
|
||||
# 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
|
||||
# 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
|
||||
@@ -28,7 +28,7 @@ config AIC_FW_PATH
|
||||
default "/oem/usr/ko/aic8800dc_fw"
|
||||
help
|
||||
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
|
||||
source "drivers/net/wireless/aic8800/aic8800_fdrv/Kconfig"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# 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
|
||||
# modules — see PORT-PLAN.md §4.5.
|
||||
# modules, see PORT-PLAN.md section 4.5.
|
||||
obj-$(CONFIG_AIC_WLAN_SUPPORT) += aic8800_bsp/
|
||||
obj-$(CONFIG_AIC8800_WLAN_SUPPORT) += aic8800_fdrv/
|
||||
obj-$(CONFIG_AIC8800_BTLPM_SUPPORT) += aic8800_btlpm/
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# 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/
|
||||
# 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).
|
||||
#
|
||||
# Vendor CONFIG_* build-time knobs below are preserved as fixed -D flags
|
||||
|
||||
@@ -3,5 +3,5 @@ config AIC8800_BTLPM_SUPPORT
|
||||
default y
|
||||
help
|
||||
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/.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 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
|
||||
# (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
|
||||
|
||||
@@ -3,5 +3,5 @@ config AIC8800_WLAN_SUPPORT
|
||||
default y
|
||||
help
|
||||
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/.
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
# 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/
|
||||
# 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
|
||||
# entirely (PORT-PLAN.md §4.1 flagged both as candidates to drop "unless a
|
||||
# build error proves them referenced elsewhere" — that held for these two).
|
||||
# USB/PCI variant): usb_host.c and rwnx_pci.c are dropped from the tree
|
||||
# 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).
|
||||
# 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
|
||||
# preprocessor-gated, so rwnx_mesh.h's types/prototypes are needed to
|
||||
# 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
|
||||
# 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
|
||||
# (modpost: "MD5Final undefined"). The two copies are byte-identical and live in
|
||||
# separate module namespaces, so there is no collision. (When these were briefly
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
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 {
|
||||
max-frequency = <50000000>;
|
||||
bus-width = <4>;
|
||||
|
||||
Reference in New Issue
Block a user