Quantcast
Channel: Raspberry Pi Forums
Viewing all articles
Browse latest Browse all 7503

Bare metal, Assembly language • [Proof of Concept] VPU Suspend-to-RAM for BCM2710A1 (Pi Zero 2W)

$
0
0
Summary

I've implemented VPU-level suspend-to-RAM for the BCM2710A1 (Pi Zero 2W) as a custom bootcode.bin based on
librerpi/rpi-open-firmware. This addresses the long-standing suspend gap discussed in raspberrypi/linux#1281.

Target: ~2–5 mA suspend with sub-second GPIO3 wake.

Status: Code complete (compiles clean with -Wall -Werror), awaiting first hardware boot.

---
Background

As documented in the canonical suspend issue, the fundamental blocker is that the VPU (VideoCore IV) is the system
supervisor. For suspend-to-RAM to work, the VPU must:
1. Save its own state to SDRAM
2. Put SDRAM into self-refresh
3. Power-gate the ARM cores
4. Enter a low-power state
5. On wake: relock PLLs, exit self-refresh, restore state, reboot ARM

The stock firmware has none of this. I've written it.

---
Implementation

The firmware implements the full suspend/resume cycle in ~1,900 lines of C and VC4 assembly:

### Suspend Orchestration
- **File:** `suspend.c` (1240 LOC)
- **Responsibilities:**
- State save
- SDRAM self-refresh entry/exit
- ARM power-gate
- GPIO3 wake config
- VPU sleep

---

### Wake Handler
- **File:** `resume.S` (306 LOC)
- **Responsibilities:**
- Assembly register save/restore
- Magic validation
- Watchdog fallback

---

### State Layout
- **File:** `state.h` (216 LOC)
- **Responsibilities:**
- Struct definitions
- 12 compile-time offset assertions (asm/C agreement)

---

### Boot Integration
- **File:** `boot_suspend_hook.c`
- **Responsibilities:**
- Hook into `romstage.c`
- Resume check before SDRAM init
- Suspend trigger after boot

---
Suspend Sequence

enter_suspend_mode()
└── suspend_enter()
├── 1. _save_vpu_regs() [assembly] — r2-r31, SP, LR, SR, PC
├── 2. Save peripheral state (PLL, GPIO, IC0/IC1, SDRAM config)
├── 3. Configure GPIO3 rising-edge wake + VPU IC0 routing
├── 4. power_gate_arm() — PM_PROC sequence
├── 5. Wait for SDRAM IDLE
├── 6. sdram_enter_selfrefresh()
│ ├── SD_CS |= STBY, wait for SDUP clear
│ ├── Gate SDRAM clock (CM_SDCCTL)
│ └── Power down DDR PLL (APHY)
├── 7. VPU `sleep` instruction ← halts here (~2-5 mA target) ←

├── [GPIO3 interrupt wakes VPU]

├── 8. sdram_exit_selfrefresh()
│ ├── DDR PLL relock (3-retry with power-cycle)
│ ├── PHY DLL reset (OMAP3-style kick, 3 retries)
│ ├── Un-gate SDRAM clock
│ ├── Clear STBY, restart controller, wait SDUP
│ ├── ZQ calibration + PVT compensation (JEDEC)
│ └── 8-pattern SDRAM integrity test
├── 9. power_ungate_arm() — full PM_PROC sequence with CFG retry
└── 10. Return 0 (success)

---
Key Technical Details

Register addresses verified against Broadcom headers:

All 22+ register addresses and bit positions were verified against the auto-generated Broadcom headers in
common/broadcom/bcm2708_chip/:
- sdc_ctrl.h — SD_CS, SD_SA bit positions
- cpr_powman.h — PM_PROC at offset 0x110
- cpr_clkman.h — CM_SDCCTL at 0x1A8, UPDATE=bit17, ACCPT=bit16
- sdc_addr_front.h — APHY DDR PLL registers
- sdc_dq_front.h — DPHY DLL registers

Several bugs were found in initial guesses (e.g., CM_SDCCTL UPDATE was assumed bit 5, actual is bit 17 — would have
written KILL instead). Full audit in docs/register-map.md.

VPU interrupt routing for GPIO3 wake:

From upstream interrupt.c:
- GPIO bank 0 (pins 0-27) → VPU IC0 hw intno 49
- Routing: offset = 0x10 + ((intno >> 3) << 2), slot = 0xF << ((intno & 7) << 2)
- For GPIO3: IC0+0x28, bits [7:4] = 0xF0

VPU sleep instruction:

Discovered in upstream arm_monitor.c — the VPU has a sleep instruction that halts until interrupt:
__asm__ __volatile__("sleep" :::);

Assembly register save:

C can't reliably capture caller-saved registers (function prologue clobbers r0-r5). The assembly version in resume.S
stores r2-r31 directly via st instructions before any compiler code runs.

---
Retry Logic (Lessons from OMAP3 / Exynos)

- PLL lock: 3 attempts with full power-cycle between each
- PHY DLL lock: 3 "kicks" (reset → stall → de-assert → wait)
- ARM power-on: CFG 0,1,2,3 retry if POWOK fails (from upstream BCM2708PowerManagement.cc)

If all else fails, watchdog reset for clean recovery.

---
Build & Flash

git clone --recurse-submodules https://github.com/bhoot1234567890/vc4-suspend.git
cd vc4-suspend

# Docker build (recommended)
docker build -t rpi-firmware .
mkdir -p build
docker run --rm -v "$(pwd)/build:/out" rpi-firmware

# Flash to SD
cp build/bootcode.bin /path/to/sdcard/

---
What's Next

1. First hardware boot — I need to test on actual Pi Zero 2W hardware (UART cable on order)
2. Power measurements — Verify ~2-5 mA target
3. Linux integration — Mailbox message from Linux → VPU "suspend now"
4. Reliability testing — 100-cycle stress test

---
Questions for the Community

1. Has anyone attempted VPU-level suspend before? I couldn't find any prior art.
2. Are there any undocumented gotchas with the DDR PLL or PHY DLL on BCM2710A1 specifically?
3. Would anyone with a Zero 2W and UART cable be willing to test?

---
References

- GitHub repo
- Broadcom Patent US8452997B2 — describes the suspend-to-SDRAM technique
- librerpi/rpi-open-firmware — upstream open firmware
- hermanhermitage/videocoreiv — VC4 documentation

---
License: GPLv2+ (matching upstream rpi-open-firmware)

---

Statistics: Posted by bhoot1234567890 — Tue Mar 03, 2026 10:45 pm — Replies 0 — Views 19



Viewing all articles
Browse latest Browse all 7503

Trending Articles