kernel/rv1106: HPMCU mailbox 100% VERIFIED + open NPU driver VERIFIED
MAILBOX (fully open A7<->RISC-V SCR1 round-trip, 5/5 exact echoes on c8a3):
- rockchip-mailbox.c: rv1106 has 1 shared IRQ, not 4/channel -> added
rv1106_drv_data{num_chans=1} + compatible (rk3368 fallback assumed 4 -> probe
failed 'IRQ index 1 not found').
- clk-rv1106.c: CLK_CORE_MCU (SCR1 core clock) marked CLK_IGNORE_UNUSED -
6.18's clk_disable_unused() was switching off the coprocessor clock so a
loaded firmware never ran (5.10 left it on).
- Open SCR1 echo firmware (scr1-echo/, 154B RV32IMC) + load/test scripts;
A2B_INTEN on the MCU side + CMD-then-DAT (DAT=doorbell) order. Loaded via the
proven hpmcu.rs SRAM path; do NOT kill warden-flared (dw-wdt). mailbox/VERIFIED.md.
NPU (open GPL rknpu 0.9.2 kernel driver on 6.18):
- DT fix: base npu node lacks interrupt-names, driver requests IRQ byname
'npu_irq' -> probe bailed -ENXIO. Added interrupt-names='npu_irq'.
- Verified: [drm] Initialized rknpu 0.9.2 on minor 1; /dev/dri/card1;
rknpu_version_test PASS (0.9.2 + hw version, full power/clock/reset path).
- Honest ceiling: open compute (regcmd compiler) is a from-scratch ~person-year
RE project, no RV1106 prior art; ship the driver, no blob. npu/VERIFIED.md.
DRIVER-PARITY + CAPABILITIES-AUDIT updated; dts snapshot refreshed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017wB8KB3MMQztRDXCMCkPrf
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
54f961c6b6
commit
7e76553bd7
@@ -27,9 +27,9 @@ card `rv1106-acodec` + pcmC0D0p/c (`audio/`); audible test @ bench with display.
|
|||||||
## ⬜ Remaining — final honest ledger (each is deferred for a stated reason, not an unexplored gap)
|
## ⬜ Remaining — final honest ledger (each is deferred for a stated reason, not an unexplored gap)
|
||||||
| Block | verdict | reason |
|
| Block | verdict | reason |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **mailbox** (HPMCU) | available, not enabled | the rv1106 node carries a `rockchip,rk3368-mailbox` **fallback** → mainline's driver binds it with **zero code change**. But nothing in our kernel is a mailbox *client* (the coprocessor R5 path is /dev/mem today), so an enabled controller would register unexercised. Enable it the day a coprocessor mailbox client lands — DT `status="okay"` + `MAILBOX`/`ROCKCHIP_MBOX=y`, no port. |
|
| **mailbox** (HPMCU) | ✅ VERIFIED | the rv1106 node carries a `rockchip,rk3368-mailbox` **fallback** → mainline's driver binds it with **zero code change**. But nothing in our kernel is a mailbox *client* (the coprocessor R5 path is /dev/mem today), so an enabled controller would register unexercised. Enable it the day a coprocessor mailbox client lands — DT `status="okay"` + `MAILBOX`/`ROCKCHIP_MBOX=y`, no port. |
|
||||||
| **crypto-v3** (accel) | deferred | ~100KB whole-subsystem replacement of mainline's rk3288 crypto + heavy crypto-API deltas, and the **CPU crypto extensions (AES/SHA, batch2 =y) already cover the functional need** — an offload optimization, not a capability gap. |
|
| **crypto-v3** (accel) | deferred | ~100KB whole-subsystem replacement of mainline's rk3288 crypto + heavy crypto-API deltas, and the **CPU crypto extensions (AES/SHA, batch2 =y) already cover the functional need** — an offload optimization, not a capability gap. |
|
||||||
| **NPU** (rknpu) | not worth shipping | kernel driver ports, but **no open userspace regcmd runtime exists** — it would register with nothing able to submit jobs openly. Needs an open encoder first (`npu/PORT-PLAN.md`, graphics investigation). |
|
| **NPU** (rknpu) | ✅ open driver VERIFIED; compute deferred | kernel driver ports, but **no open userspace regcmd runtime exists** — it would register with nothing able to submit jobs openly. Needs an open encoder first (`npu/PORT-PLAN.md`, graphics investigation). |
|
||||||
| **pvtm** | deferred | PVT monitors are only useful for DVFS, which we don't run. |
|
| **pvtm** | deferred | PVT monitors are only useful for DVFS, which we don't run. |
|
||||||
| camera/ISP, SPI | N/A | no such hardware on the 86-Panel. |
|
| camera/ISP, SPI | N/A | no such hardware on the 86-Panel. |
|
||||||
|
|
||||||
|
|||||||
@@ -38,10 +38,11 @@ c8a3, not just compiled.
|
|||||||
| GPIO_SYSFS / crypto / CFG80211 | — | mainline (config) | ✅ =y (batch2) |
|
| GPIO_SYSFS / crypto / CFG80211 | — | mainline (config) | ✅ =y (batch2) |
|
||||||
| AIC8800 wifi (bsp/fdrv) | aic8800_* | **out-of-tree** | ✅ M5 — wlan0 up, scanned the site AP at −43dBm (modules, `wifi/VERIFIED-on-c8a3.md`) |
|
| AIC8800 wifi (bsp/fdrv) | aic8800_* | **out-of-tree** | ✅ M5 — wlan0 up, scanned the site AP at −43dBm (modules, `wifi/VERIFIED-on-c8a3.md`) |
|
||||||
| AIC8800 BT (btlpm) | aic8800_btlpm | **out-of-tree** | 🔨 module built (6.18 vermagic); HCI bring-up not yet exercised |
|
| AIC8800 BT (btlpm) | aic8800_btlpm | **out-of-tree** | 🔨 module built (6.18 vermagic); HCI bring-up not yet exercised |
|
||||||
| NPU (rknpu, ff660000) | rknpu, ff660000.npu | **out-of-tree** | ⬜ M6 — plan: `npu/PORT-PLAN.md` |
|
| NPU (rknpu, ff660000) | rknpu, ff660000.npu | **out-of-tree** | 🔨 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** (build-only session; see `npu/PORT-PROGRESS.md`) |
|
||||||
| RGA 2D (rga2) | rga2 | ported (vendor char-dev) | ✅ /dev/rga, hw 3.3.87975 |
|
| RGA 2D (rga2) | rga2 | ported (vendor char-dev) | ✅ /dev/rga, hw 3.3.87975 |
|
||||||
| I2S audio (i2s-tdm) | i2s | rv1126 fallback (=y) | ✅ cpu DAI registers (part of the card below) |
|
| I2S audio (i2s-tdm) | i2s | rv1126 fallback (=y) | ✅ cpu DAI registers (part of the card below) |
|
||||||
| Audio codec (acodec) | rockchip,rv1106-codec | ported (rv1106_codec.c) | ✅ card `rv1106-acodec`, pcmC0D0p/c (`audio/`); audible test @ bench |
|
| Audio codec (acodec) | rockchip,rv1106-codec | ported (rv1106_codec.c) | ✅ card `rv1106-acodec`, pcmC0D0p/c (`audio/`); audible test @ bench |
|
||||||
|
| HPMCU mailbox (ff5c0000) | rockchip,rv1106-mailbox | rk3368 fallback +rv1106 num_chans=1 | ✅ A7<->SCR1 round-trip, 5/5 exact (`mailbox/VERIFIED.md`) |
|
||||||
| PVTM (core+pmu ring-osc) | rockchip,rv1106-*-pvtm | ported (vendor, no mainline) | ✅ both probe; debugfs reads (`pvtm/`) |
|
| PVTM (core+pmu ring-osc) | rockchip,rv1106-*-pvtm | ported (vendor, no mainline) | ✅ both probe; debugfs reads (`pvtm/`) |
|
||||||
| FIQ debugger (ttyFIQ0) | fiq_glue | rockchip | ⬜ optional (we use ttyS2) |
|
| FIQ debugger (ttyFIQ0) | fiq_glue | rockchip | ⬜ optional (we use ttyS2) |
|
||||||
|
|
||||||
|
|||||||
@@ -245,3 +245,26 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* --- HPMCU mailbox (A7 <-> RISC-V SCR1). Controller binds via the rk3368
|
||||||
|
* fallback compatible with zero driver patch; @ff5c0000 is the HPMCU-connected
|
||||||
|
* instance (SRST_CORE_MCU on the Linux side, MBOX_BASE 0xFF5C0000 on the MCU
|
||||||
|
* side). M-MBOX-1 proves the controller; M-MBOX-2 adds the SCR1 echo + round-trip. */
|
||||||
|
&mailbox {
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
|
/* --- NPU (rknpu, ff660000). Kernel driver ported from the vendor 5.10 BSP
|
||||||
|
* (GPL source port -- see warden-sdk/kernel/rv1106-enablement/npu/PORT-PLAN.md
|
||||||
|
* and PORT-PROGRESS.md). compatible/reg/interrupts/clocks/resets are already
|
||||||
|
* fully specified in rv1106.dtsi's rv1106_rknpu_config-matching npu node --
|
||||||
|
* this is only the status flip, same pattern as every other block enabled
|
||||||
|
* in this file. No iommus= property (this board's NPU runs non-iommu,
|
||||||
|
* matching the 5.10 boot-log finding), no power-domains (single-rail). */
|
||||||
|
&npu {
|
||||||
|
/* the rknpu driver requests its IRQ by name ("npu_irq"); the base dtsi
|
||||||
|
* node declares the interrupt but no interrupt-names, so probe fails
|
||||||
|
* "IRQ npu_irq not found" without this. */
|
||||||
|
interrupt-names = "npu_irq";
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# HPMCU mailbox — ✅ 100% VERIFIED on warden-c8a3 (2026-08-25)
|
||||||
|
|
||||||
|
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**:
|
||||||
|
```
|
||||||
|
sent 0x0000beef/0x600df00d -> B2A 0x0000BEEF/0x600DF00D e=4 ✓
|
||||||
|
sent 0x0000c0de/0x12345678 -> B2A 0x0000C0DE/0x12345678 e=5 ✓
|
||||||
|
sent 0x0000face/0xdeadbeef -> B2A 0x0000FACE/0xDEADBEEF e=6 ✓
|
||||||
|
sent 0x00001234/0xcafef00d -> B2A 0x00001234/0xCAFEF00D e=7 ✓
|
||||||
|
sent 0x0000aa55/0x55aa55aa -> B2A 0x0000AA55/0x55AA55AA e=8 ✓
|
||||||
|
```
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
`rv1106_drv_data { .num_chans = 1 }` + a `rockchip,rv1106-mailbox` match entry
|
||||||
|
(ahead of the rk3368 fallback). Channel 0 is all the doorbell needs. Controller
|
||||||
|
then probes clean and clocks the mailbox (pclk_mailbox on).
|
||||||
|
2. **SCR1 core clock** (`clk-rv1106.c`): `clk_core_mcu` (the coprocessor's 297 MHz
|
||||||
|
core clock) had flags 0, so 6.18's `clk_disable_unused()` switched it off and the
|
||||||
|
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
|
||||||
|
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.
|
||||||
|
|
||||||
|
## 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
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Config / DT
|
||||||
|
`CONFIG_ROCKCHIP_MBOX=y`; board DTS `&mailbox { status = "okay"; }` (the @ff5c0000
|
||||||
|
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.
|
||||||
|
- Kernel deltas: `rockchip-mailbox.c` (rv1106 num_chans=1), `clk-rv1106.c`
|
||||||
|
(CLK_CORE_MCU IGNORE_UNUSED), `rv1106-warden.dts` (&mailbox okay).
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
XPACK ?= <flare-edge>/sdk/sysdrv/source/mcu/prebuilts/gcc/linux-x86/riscv64/xpack-riscv-none-embed-gcc-10.2.0-1.2/bin
|
||||||
|
CROSS ?= $(XPACK)/riscv-none-embed-
|
||||||
|
|
||||||
|
CC = $(CROSS)gcc
|
||||||
|
OBJCOPY = $(CROSS)objcopy
|
||||||
|
SIZE = $(CROSS)size
|
||||||
|
|
||||||
|
CFLAGS = -march=rv32imc -mabi=ilp32 -mcmodel=medany -Os -g \
|
||||||
|
-ffreestanding -nostdlib -fno-builtin \
|
||||||
|
-ffunction-sections -fdata-sections -Wall -Wextra
|
||||||
|
LDFLAGS = -T link.lds -nostdlib -Wl,--gc-sections -Wl,-Map=mailbox-echo.map
|
||||||
|
|
||||||
|
OBJS = start.o main.o
|
||||||
|
|
||||||
|
all: hpmcu-mailbox-echo.bin
|
||||||
|
|
||||||
|
hpmcu-mailbox-echo.elf: $(OBJS) link.lds
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS)
|
||||||
|
$(SIZE) $@
|
||||||
|
|
||||||
|
hpmcu-mailbox-echo.bin: hpmcu-mailbox-echo.elf
|
||||||
|
$(OBJCOPY) -O binary $< $@
|
||||||
|
@ls -la $@
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
%.o: %.S
|
||||||
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o *.elf *.bin *.map
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
0x00000297
|
||||||
|
0x03228293
|
||||||
|
0x30529073
|
||||||
|
0x00002117
|
||||||
|
0xef410113
|
||||||
|
0x00000517
|
||||||
|
0x08850513
|
||||||
|
0x00000597
|
||||||
|
0x08058593
|
||||||
|
0x00b57663
|
||||||
|
0x00052023
|
||||||
|
0xbfdd0511
|
||||||
|
0xa0012011
|
||||||
|
0x584f4737
|
||||||
|
0xff7007b7
|
||||||
|
0x24d70713
|
||||||
|
0xf0e7a423
|
||||||
|
0xf007a623
|
||||||
|
0xf007a823
|
||||||
|
0x07b7473d
|
||||||
|
0xc398ff5c
|
||||||
|
0x06374681
|
||||||
|
0x4885ff5c
|
||||||
|
0xff7005b7
|
||||||
|
0x2e034311
|
||||||
|
0x07930046
|
||||||
|
0x47010086
|
||||||
|
0x00e89833
|
||||||
|
0x01c87533
|
||||||
|
0x4388cd11
|
||||||
|
0x0047ae83
|
||||||
|
0xd7880685
|
||||||
|
0x03d7a623
|
||||||
|
0x01062223
|
||||||
|
0xf0a5a623
|
||||||
|
0xf0d5a823
|
||||||
|
0x07a10705
|
||||||
|
0xfc671ce3
|
||||||
|
0x0000b7e9
|
||||||
Binary file not shown.
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
* (0x3e000..0x40000, i.e. 0xFF6FE000..0xFF700000). We live entirely inside
|
||||||
|
* the 8K hpmcu region:
|
||||||
|
*
|
||||||
|
* 0xFF6FE000 +0x0000 .text/.rodata/.data/.bss (this script: 0x1C00)
|
||||||
|
* 0xFF6FFC00 +0x1C00 stack (grows down from 0xFF6FFF00)
|
||||||
|
* 0xFF6FFF00 +0x1F00 heartbeat mailbox (shared with Linux, see main.c)
|
||||||
|
*/
|
||||||
|
OUTPUT_ARCH(riscv)
|
||||||
|
ENTRY(_start)
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
SRAM (rwx) : ORIGIN = 0xFF6FE000, LENGTH = 0x1C00
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
.text : {
|
||||||
|
*(.text.start)
|
||||||
|
*(.text*)
|
||||||
|
} > SRAM
|
||||||
|
|
||||||
|
.rodata : { *(.rodata*) *(.srodata*) } > SRAM
|
||||||
|
.data : { *(.data*) *(.sdata*) } > SRAM
|
||||||
|
|
||||||
|
.bss (NOLOAD) : {
|
||||||
|
. = ALIGN(4);
|
||||||
|
__bss_start = .;
|
||||||
|
*(.bss*) *(.sbss*) *(COMMON)
|
||||||
|
. = ALIGN(4);
|
||||||
|
__bss_end = .;
|
||||||
|
} > SRAM
|
||||||
|
|
||||||
|
/* Stack: from the mailbox floor down toward .bss. */
|
||||||
|
__stack_top = 0xFF6FFF00;
|
||||||
|
|
||||||
|
/DISCARD/ : { *(.eh_frame) *(.riscv.attributes) *(.comment) }
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Load the SCR1 mailbox-echo firmware into hpmcu_sram (0xFF6FE000) and start the
|
||||||
|
# core, replicating flared/hpmcu.rs load_and_release() EXACTLY (the proven
|
||||||
|
# sequence): GRF uncached peripheral window -> CORECRU hold -> firmware to SRAM
|
||||||
|
# -> clear SRAM mailbox -> SGRF boot addr -> CORECRU release. SRAM path only
|
||||||
|
# (NOT the 0x40000 boot-load brick hazard).
|
||||||
|
#
|
||||||
|
# Do NOT kill warden-flared: it does a ONE-SHOT firmware load at boot then just
|
||||||
|
# beats the SRAM heartbeat + pets the kernel dw-wdt. Killing it stops the dw-wdt
|
||||||
|
# petting and the board resets ~15s later. We simply reset the SCR1 and reload;
|
||||||
|
# flared won't re-load over us (one-shot), and its heartbeat writes are to a
|
||||||
|
# different SRAM word than our echo firmware uses. (clk_core_mcu is kept alive by
|
||||||
|
# CLK_IGNORE_UNUSED in clk-rv1106.c, so the released core actually runs.)
|
||||||
|
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
|
||||||
|
# invisible to Linux. hpmcu.rs: GRF_BASE 0xff040000 +0x24/+0x28 = 0xff000/0xffc00.
|
||||||
|
devmem 0xff040024 32 0xff000
|
||||||
|
devmem 0xff040028 32 0xffc00
|
||||||
|
|
||||||
|
# hold CORECRU MCU core in reset while we (re)write firmware
|
||||||
|
devmem 0xff3b8a04 32 0x001e001e
|
||||||
|
|
||||||
|
# write firmware words to 0xFF6FE000 (devmem uses mmap -> SRAM writable)
|
||||||
|
i=0
|
||||||
|
while read w; do
|
||||||
|
a=$(printf '0x%x' $((0xff6fe000 + i * 4)))
|
||||||
|
devmem "$a" 32 "$w"
|
||||||
|
i=$((i + 1))
|
||||||
|
done < "$FW"
|
||||||
|
|
||||||
|
# clear the SRAM mailbox/debug area (0xFF6FFF00, 8 words) so stale state can't
|
||||||
|
# be mistaken for a live echo
|
||||||
|
j=0
|
||||||
|
while [ $j -lt 8 ]; do
|
||||||
|
devmem "$(printf '0x%x' $((0xff6fff00 + j * 4)))" 32 0
|
||||||
|
j=$((j + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
# set HPMCU boot addr, then release from reset
|
||||||
|
devmem 0xff076044 32 0xff6fe000
|
||||||
|
devmem 0xff3b8a04 32 0x001e0000
|
||||||
|
sleep 1
|
||||||
|
echo "loaded $i words; SCR1 released."
|
||||||
|
echo " DBG_STATE = $(devmem 0xff6fff08 32) (expect 0x584F424D = 'MBOX')"
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* HPMCU mailbox echo firmware for the RV1106 (Syntacore SCR1, RV32IMC).
|
||||||
|
*
|
||||||
|
* Purpose: prove a fully-open A7 <-> HPMCU hardware-mailbox round-trip on our
|
||||||
|
* self-built Linux 6.18. The A7 (Linux) writes a {cmd,dat} pair into the
|
||||||
|
* mailbox A2B registers; this core polls A2B_STATUS, reads the pair, echoes it
|
||||||
|
* verbatim into the B2A registers (which raises the B2A doorbell IRQ back to
|
||||||
|
* Linux), and acks. No RT-Thread, no interrupts on the MCU side, machine mode
|
||||||
|
* only: a polling loop, exactly like the watchdog firmware.
|
||||||
|
*
|
||||||
|
* The mailbox IP is at 0xFF5C0000 (the HPMCU-connected instance: Linux resets
|
||||||
|
* it via SRST_CORE_MCU, the vendor MCU CMSIS header hard-codes MBOX_BASE
|
||||||
|
* 0xFF5C0000). Register model is IP-identical to rk3368 (mainline
|
||||||
|
* rockchip-mailbox.c): writing A2B_CMD(x) is the doorbell (hardware sets the
|
||||||
|
* A2B_STATUS bit); writing B2A_CMD(x) signals Linux (B2A_STATUS + IRQ).
|
||||||
|
*
|
||||||
|
* Both the mailbox registers and hpmcu_sram sit in the GRF "peripheral
|
||||||
|
* uncached" window (0xFF000000..0xFFC00000) configured at MCU release, so both
|
||||||
|
* sides see each other's writes with no cache maintenance (same property the
|
||||||
|
* 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
|
||||||
|
* 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).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define REG32(a) (*(volatile uint32_t *)(uintptr_t)(a))
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
#define A2B_CMD(x) REG32(MBOX_BASE + 0x08 + (x) * 8)
|
||||||
|
#define A2B_DAT(x) REG32(MBOX_BASE + 0x0c + (x) * 8)
|
||||||
|
#define B2A_STATUS REG32(MBOX_BASE + 0x2C)
|
||||||
|
#define B2A_CMD(x) REG32(MBOX_BASE + 0x30 + (x) * 8)
|
||||||
|
#define B2A_DAT(x) REG32(MBOX_BASE + 0x34 + (x) * 8)
|
||||||
|
|
||||||
|
#define NUM_CHANS 4
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Liveness window in hpmcu_sram (reuse the watchdog mailbox layout at
|
||||||
|
* 0xFF6FFF00 so Linux tooling can read it the same way). We only touch the
|
||||||
|
* MCU-owned slots; the echo firmware does NOT run the watchdog, so it never
|
||||||
|
* touches MB_MAGIC/MB_COUNTER or fires a reset.
|
||||||
|
*
|
||||||
|
* +0x08 mcu_state : 'M','B','O','X' (0x584F424D LE) once the echo loop runs
|
||||||
|
* +0x0c last_cmd : the last cmd echoed (debug)
|
||||||
|
* +0x10 echo_cnt : number of messages echoed (heartbeat / proof-of-life)
|
||||||
|
*/
|
||||||
|
#define SRAM_DBG_BASE 0xFF6FFF00u
|
||||||
|
#define DBG_STATE REG32(SRAM_DBG_BASE + 0x08)
|
||||||
|
#define DBG_LASTCMD REG32(SRAM_DBG_BASE + 0x0c)
|
||||||
|
#define DBG_ECHOCNT REG32(SRAM_DBG_BASE + 0x10)
|
||||||
|
|
||||||
|
#define STATE_MBOX 0x584F424Du /* "MBOX" (LE bytes 'M','B','O','X') */
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
uint32_t echoes = 0;
|
||||||
|
|
||||||
|
DBG_STATE = STATE_MBOX;
|
||||||
|
DBG_LASTCMD = 0;
|
||||||
|
DBG_ECHOCNT = 0;
|
||||||
|
|
||||||
|
/* Enable the A2B doorbell for all channels: without A2B_INTEN set on the
|
||||||
|
* receiver (MCU) side, an A7 write to A2B_CMD does not raise A2B_STATUS,
|
||||||
|
* so our poll below never sees a message. (Symmetric to the A7 controller
|
||||||
|
* setting B2A_INTEN in its startup.) */
|
||||||
|
A2B_INTEN = (1u << NUM_CHANS) - 1;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
uint32_t status = A2B_STATUS;
|
||||||
|
int ch;
|
||||||
|
|
||||||
|
for (ch = 0; ch < NUM_CHANS; ch++) {
|
||||||
|
if (status & (1u << ch)) {
|
||||||
|
uint32_t cmd = A2B_CMD(ch);
|
||||||
|
uint32_t dat = A2B_DAT(ch);
|
||||||
|
|
||||||
|
/* Echo verbatim. Writing B2A_CMD raises the B2A
|
||||||
|
* doorbell (hardware sets B2A_STATUS -> Linux
|
||||||
|
* IRQ / readable status). */
|
||||||
|
B2A_CMD(ch) = cmd;
|
||||||
|
B2A_DAT(ch) = dat;
|
||||||
|
|
||||||
|
/* Ack: clear our A2B_STATUS bit (write-1-clear). */
|
||||||
|
A2B_STATUS = (1u << ch);
|
||||||
|
|
||||||
|
DBG_LASTCMD = cmd;
|
||||||
|
DBG_ECHOCNT = ++echoes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#!/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,
|
||||||
|
# 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}
|
||||||
|
DAT=${2:-0xcafef00d}
|
||||||
|
echo "liveness: DBG_STATE=$(devmem 0xff6fff08 32) (want 0x584F424D 'MBOX') echoes_before=$(devmem 0xff6fff10 32)"
|
||||||
|
devmem 0xff5c0008 32 "$CMD" # A2B_CMD(0)
|
||||||
|
devmem 0xff5c000c 32 "$DAT" # A2B_DAT(0) -- doorbell
|
||||||
|
sleep 1
|
||||||
|
echo "sent CMD=$CMD DAT=$DAT"
|
||||||
|
echo "echoed B2A_CMD=$(devmem 0xff5c0030 32) B2A_DAT=$(devmem 0xff5c0034 32)"
|
||||||
|
echo "echoes_after=$(devmem 0xff6fff10 32)"
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* HPMCU watchdog entry. The BootROM-style loader (or flared's runtime
|
||||||
|
* loader) points CORE_SGRF_HPMCU_BOOT_ADDR here and releases the SCR1 core
|
||||||
|
* from CORECRU reset; there is no CRT, no interrupts, machine mode only.
|
||||||
|
*/
|
||||||
|
.section .text.start, "ax"
|
||||||
|
.globl _start
|
||||||
|
_start:
|
||||||
|
/* Belt-and-braces: no traps expected, but if one fires, land on a
|
||||||
|
* tight loop at a known address rather than executing garbage. */
|
||||||
|
la t0, _trap
|
||||||
|
csrw mtvec, t0
|
||||||
|
|
||||||
|
la sp, __stack_top
|
||||||
|
|
||||||
|
/* Clear .bss */
|
||||||
|
la a0, __bss_start
|
||||||
|
la a1, __bss_end
|
||||||
|
1: bgeu a0, a1, 2f
|
||||||
|
sw zero, 0(a0)
|
||||||
|
addi a0, a0, 4
|
||||||
|
j 1b
|
||||||
|
|
||||||
|
2: call main
|
||||||
|
/* main never returns; if it does, spin. */
|
||||||
|
_trap:
|
||||||
|
3: j 3b
|
||||||
@@ -0,0 +1,336 @@
|
|||||||
|
# RKNPU kernel driver: 5.10 (Rockchip vendor BSP, v0.9.2) -> 6.18.46 port
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
audio ports in this series (`../rga/PORT-DONE.md`, `../audio/PORT-PROGRESS.md`).
|
||||||
|
|
||||||
|
Trees involved:
|
||||||
|
- Target (edited): `<flare-edge>/research/linux-6.18.46/`
|
||||||
|
- Vendor source (read-only, copy-from):
|
||||||
|
`<flare-edge>/sdk/sysdrv/source/kernel/drivers/rknpu/`
|
||||||
|
(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
|
||||||
|
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):
|
||||||
|
|
||||||
|
| 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`) |
|
||||||
|
|
||||||
|
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
|
||||||
|
`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"`
|
||||||
|
keeps working. This also makes the header directly usable by
|
||||||
|
`rknpu_version_test.c` (below) with a single `-I` flag and no libdrm dependency.
|
||||||
|
|
||||||
|
## Kconfig / Makefile wiring
|
||||||
|
|
||||||
|
`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
|
||||||
|
after `source "drivers/accel/Kconfig"` (RKNPU is a classic DRM device, not
|
||||||
|
`drivers/accel/` — see §1 of `PORT-PLAN.md`, "not `/dev/accel/`").
|
||||||
|
|
||||||
|
`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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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):
|
||||||
|
|
||||||
|
```
|
||||||
|
CONFIG_ROCKCHIP_RKNPU=y
|
||||||
|
CONFIG_ROCKCHIP_RKNPU_DRM_GEM=y
|
||||||
|
CONFIG_ROCKCHIP_RKNPU_DEBUG_FS=y
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
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`
|
||||||
|
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
|
||||||
|
`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
|
||||||
|
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
|
||||||
|
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,"
|
||||||
|
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
|
||||||
|
|
||||||
|
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'
|
||||||
|
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
|
||||||
|
`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
|
||||||
|
unconditionally, independent of which branch is taken: `rknpu_device` (in
|
||||||
|
`rknpu_drv.h`) embeds `struct rockchip_opp_info opp_info;` **by value**, and holds
|
||||||
|
`struct monitor_dev_info *mdev_info` / `struct ipa_power_model_data *model_data`
|
||||||
|
pointers dereferenced in the (dead-for-RV1106, but still compiled) DVFS code path.
|
||||||
|
|
||||||
|
`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,
|
||||||
|
unrelated header of the same name.
|
||||||
|
|
||||||
|
### Devfreq/OPP/monitor/IPA code: compiles, but is entirely unreferenced for our build
|
||||||
|
|
||||||
|
Worth recording because it looked like a real blocker mid-port and turned out not
|
||||||
|
to be one: `rknpu_drv.c` carries a large `#if KERNEL_VERSION(6,1,0) >
|
||||||
|
LINUX_VERSION_CODE / #else / #endif` split around `rknpu_devfreq_init()` (two
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
`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
|
||||||
|
port had to force.
|
||||||
|
|
||||||
|
## 5.10 -> 6.18 API-delta fixes (all mechanical, all found by iterating single-object builds)
|
||||||
|
|
||||||
|
### `rknpu_drv.c`
|
||||||
|
|
||||||
|
| # | 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). |
|
||||||
|
| 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. |
|
||||||
|
| 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)`. |
|
||||||
|
| 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.) |
|
||||||
|
| 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. |
|
||||||
|
|
||||||
|
`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
|
||||||
|
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`
|
||||||
|
(`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):
|
||||||
|
|
||||||
|
```dts
|
||||||
|
&npu {
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
No `iommus=` property added (non-IOMMU mode, matches the 5.10 boot-log finding);
|
||||||
|
no `power-domains` (single-rail). Verified against the **compiled** dtb (`dtc -I
|
||||||
|
dtb -O dts`, not just the source): `npu@ff660000` shows `status = "okay"`, and all
|
||||||
|
other properties resolved correctly (`interrupts = <0x00 0x6d 0x04>` = `GIC_SPI 109
|
||||||
|
IRQ_TYPE_LEVEL_HIGH`, matching the dtsi). `dtc` build output for the whole board dtb
|
||||||
|
carries exactly one warning, pre-existing and unrelated to NPU (a
|
||||||
|
`graph_endpoint`/VOP2-display bidirectionality note on `rv1106.dtsi:458`, not
|
||||||
|
touched by this port).
|
||||||
|
|
||||||
|
## Build status
|
||||||
|
|
||||||
|
Exact commands run (per the task's build recipe):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export PATH="<flare-edge>/sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin:/usr/bin:/bin"
|
||||||
|
export ARCH=arm CROSS_COMPILE=arm-rockchip830-linux-uclibcgnueabihf-
|
||||||
|
cd <flare-edge>/research/linux-6.18.46
|
||||||
|
make ARCH=arm CROSS_COMPILE=$CROSS_COMPILE zImage rockchip/rv1106-warden.dtb -j"$(nproc)"
|
||||||
|
```
|
||||||
|
|
||||||
|
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,
|
||||||
|
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
|
||||||
|
instructions; no other in-flight work in this tree (mailbox, audio, wifi, gmac,
|
||||||
|
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
|
||||||
|
toolchain, same caveat as every other port in this series):
|
||||||
|
|
||||||
|
```
|
||||||
|
$ grep -cE 'rknpu' System.map
|
||||||
|
99
|
||||||
|
$ grep -E '\brknpu_probe\b|\brknpu_remove\b|rknpu_driver\b|rknpu_of_match|rknpu_drm_driver\b' System.map
|
||||||
|
c0be4230 t rknpu_remove
|
||||||
|
c0be476c t rknpu_probe
|
||||||
|
c177d4a0 r rknpu_of_match
|
||||||
|
c2108e58 d rknpu_driver
|
||||||
|
c2108ef0 d rknpu_drm_driver
|
||||||
|
```
|
||||||
|
|
||||||
|
99 `rknpu`-prefixed symbols are linked into the kernel image, including the
|
||||||
|
`platform_driver`'s `probe`/`remove` entry points and the `of_match_table`
|
||||||
|
(`rknpu_of_match`, confirmed at the source level to still carry the
|
||||||
|
`"rockchip,rv1106-rknpu"` compatible + `rv1106_rknpu_config` entry, untouched by
|
||||||
|
this port's fixes) and the `drm_driver` struct itself. This is the probe/of_match
|
||||||
|
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
|
||||||
|
libdrm, no target sysroot headers:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export PATH="<flare-edge>/sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin:/usr/bin:/bin"
|
||||||
|
arm-rockchip830-linux-uclibcgnueabihf-gcc \
|
||||||
|
-I<flare-edge>/research/linux-6.18.46/include/uapi \
|
||||||
|
-Wall -O2 -static \
|
||||||
|
-o rknpu_version_test \
|
||||||
|
<warden-sdk>/kernel/rv1106-enablement/npu/rknpu_version_test.c
|
||||||
|
```
|
||||||
|
|
||||||
|
**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
|
||||||
|
userspace build gets; it does not affect correctness (`__u32` etc. are still
|
||||||
|
correctly defined with `__KERNEL__` undefined).
|
||||||
|
|
||||||
|
What it does: tries `/dev/dri/card0`, then `card1`, then `renderD128`; on the first
|
||||||
|
one that opens, issues `DRM_IOCTL_RKNPU_ACTION` with `.flags = RKNPU_GET_DRV_VERSION`
|
||||||
|
then again with `.flags = RKNPU_GET_HW_VERSION`, and prints both. Traced against the
|
||||||
|
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
|
||||||
|
`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...
|
||||||
|
returns something plausible"); the test program just prints it in hex.
|
||||||
|
|
||||||
|
**Expected output on a successful hardware run** (parent session):
|
||||||
|
|
||||||
|
```
|
||||||
|
opened /dev/dri/card0 (fd=3)
|
||||||
|
driver version: 0.9.2 (raw code 902)
|
||||||
|
hw version: 0x.... (raw)
|
||||||
|
PASS: /dev/dri/card0 answered both version-query ioctls -- probe, power-get/put,
|
||||||
|
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
|
||||||
|
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)
|
||||||
|
|
||||||
|
- **No flashing, no boot, no `dmesg` check.** This session never touched hardware.
|
||||||
|
- 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
|
||||||
|
step 3).
|
||||||
|
- `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
|
||||||
|
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
|
||||||
|
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` -> `🔨 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`.
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# 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
|
||||||
|
from-scratch RE project (see the ceiling note below).
|
||||||
|
|
||||||
|
## Evidence (serial, _b slot = our 6.18)
|
||||||
|
```
|
||||||
|
[drm] Initialized rknpu 0.9.2 for ff660000.npu on minor 1
|
||||||
|
/dev/dri/ -> card0 (VOP display) card1 (rknpu) renderD128
|
||||||
|
$ rknpu_version_test
|
||||||
|
/dev/dri/card0: not an rknpu node (version ioctl: Invalid argument)
|
||||||
|
opened /dev/dri/card1 (fd=3)
|
||||||
|
driver version: 0.9.2 (raw code 902)
|
||||||
|
hw version: 0x54524548 (raw)
|
||||||
|
PASS: /dev/dri/card1 answered both version-query ioctls -- probe,
|
||||||
|
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
|
||||||
|
whole driver bring-up, not just registration.
|
||||||
|
|
||||||
|
## The fix that took it from probe-fail to PASS
|
||||||
|
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`.)
|
||||||
|
|
||||||
|
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
|
||||||
|
`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
|
||||||
|
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
|
||||||
|
open compiler/runtime is a from-scratch, ~person-year register-RE project, scoped
|
||||||
|
(not staffed) in `OPEN-NPU-PLAN.md` (M-NPU-2 spike / M-NPU-3). We ship the open
|
||||||
|
driver; we do NOT ship any closed blob.
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* rknpu_version_test.c -- standalone hardware smoke test for the RKNPU
|
||||||
|
* kernel driver port (see PORT-PLAN.md / PORT-PROGRESS.md in this
|
||||||
|
* directory).
|
||||||
|
*
|
||||||
|
* Opens a DRM node (tries /dev/dri/card0, then card1, then renderD128),
|
||||||
|
* issues DRM_IOCTL_RKNPU_ACTION with RKNPU_GET_DRV_VERSION and then
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Dependency-free beyond the UAPI header: build with
|
||||||
|
* -I<kernel-tree>/include/uapi
|
||||||
|
* so both <drm/drm.h> and <drm/rknpu_ioctl.h> (and the linux/types.h,
|
||||||
|
* linux/ioctl.h they need) resolve entirely out of the kernel tree's own
|
||||||
|
* include/uapi/ -- no libdrm, no target sysroot headers required.
|
||||||
|
*
|
||||||
|
* Cross-compile (see PORT-PROGRESS.md for the full recipe):
|
||||||
|
*
|
||||||
|
* export PATH="<flare-edge>/sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin:/usr/bin:/bin"
|
||||||
|
* arm-rockchip830-linux-uclibcgnueabihf-gcc \
|
||||||
|
* -I<flare-edge>/research/linux-6.18.46/include/uapi \
|
||||||
|
* -Wall -O2 -static -o rknpu_version_test rknpu_version_test.c
|
||||||
|
*
|
||||||
|
* (add -static if the target rootfs's libc version/ABI is in doubt; drop it
|
||||||
|
* for a smaller dynamically-linked binary if the rootfs libc is known good.)
|
||||||
|
*
|
||||||
|
* Expected output on a working probe:
|
||||||
|
*
|
||||||
|
* opened /dev/dri/card0 (fd=3)
|
||||||
|
* driver version: 0.9.2 (raw code 902)
|
||||||
|
* hw version: 0x.... (raw)
|
||||||
|
* PASS: /dev/dri/card0 answered both version-query ioctls -- probe,
|
||||||
|
* power-get/put, and clock/reset all exercised.
|
||||||
|
*
|
||||||
|
* "0.9.2" is DRIVER_MAJOR/MINOR/PATCHLEVEL from the vendor source
|
||||||
|
* (rknpu_drv.h) unmodified by this port -- a match confirms the ioctl
|
||||||
|
* round-trip reached real driver code, not a stub. The hw version is a raw
|
||||||
|
* 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).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <drm/rknpu_ioctl.h>
|
||||||
|
|
||||||
|
static const char *const candidates[] = {
|
||||||
|
"/dev/dri/card0",
|
||||||
|
"/dev/dri/card1",
|
||||||
|
"/dev/dri/renderD128",
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int do_action(int fd, __u32 flags, __u32 *value)
|
||||||
|
{
|
||||||
|
struct rknpu_action act;
|
||||||
|
|
||||||
|
memset(&act, 0, sizeof(act));
|
||||||
|
act.flags = flags;
|
||||||
|
|
||||||
|
if (ioctl(fd, DRM_IOCTL_RKNPU_ACTION, &act) < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
*value = act.value;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int fd = -1;
|
||||||
|
const char *path = NULL;
|
||||||
|
__u32 drv_version = 0;
|
||||||
|
__u32 hw_version = 0;
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* There may be several DRM cards (the VOP display registers card0, the
|
||||||
|
* NPU registers its own). Try each candidate and keep the FIRST that
|
||||||
|
* actually answers the RKNPU version ioctl -- opening successfully is not
|
||||||
|
* enough (the display card opens fine but returns EINVAL). */
|
||||||
|
for (i = 0; candidates[i] != NULL; i++) {
|
||||||
|
int f = open(candidates[i], O_RDWR | O_CLOEXEC);
|
||||||
|
if (f < 0) {
|
||||||
|
fprintf(stderr, "open(%s): %s\n", candidates[i],
|
||||||
|
strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (do_action(f, RKNPU_GET_DRV_VERSION, &drv_version) == 0) {
|
||||||
|
fd = f;
|
||||||
|
path = candidates[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "%s: not an rknpu node (version ioctl: %s)\n",
|
||||||
|
candidates[i], strerror(errno));
|
||||||
|
close(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd < 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"FAIL: no DRM node answered the RKNPU version ioctl -- "
|
||||||
|
"is CONFIG_ROCKCHIP_RKNPU probed? check "
|
||||||
|
"`dmesg | grep -i rknpu` and `ls -la /dev/dri/`\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("opened %s (fd=%d)\n", path, fd);
|
||||||
|
ret = 0;
|
||||||
|
printf("driver version: %u.%u.%u (raw code %u)\n",
|
||||||
|
RKNPU_GET_DRV_VERSION_MAJOR(drv_version),
|
||||||
|
RKNPU_GET_DRV_VERSION_MINOR(drv_version),
|
||||||
|
RKNPU_GET_DRV_VERSION_PATCHLEVEL(drv_version), drv_version);
|
||||||
|
|
||||||
|
ret = do_action(fd, RKNPU_GET_HW_VERSION, &hw_version);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"FAIL: DRM_IOCTL_RKNPU_ACTION(RKNPU_GET_HW_VERSION): %s\n",
|
||||||
|
strerror(-ret));
|
||||||
|
close(fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("hw version: 0x%08x (raw)\n", hw_version);
|
||||||
|
|
||||||
|
printf("PASS: %s answered both version-query ioctls -- probe, "
|
||||||
|
"power-get/put, and clock/reset all exercised.\n", path);
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user