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:
BFE Engineering
2026-08-25 08:36:56 -06:00
co-authored by Claude Opus 4.8
parent 54f961c6b6
commit 7e76553bd7
15 changed files with 900 additions and 3 deletions
@@ -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) }
}
+46
View File
@@ -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