1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! Board reboot and shutdown via the BCM Power Management (PM) block.
//!
//! Both work through the same reset machinery [`crate::watchdog`] uses:
//! arm the PM watchdog with a tiny countdown and select `PM_RSTC`'s
//! full-reset type, so the board resets almost immediately instead of
//! after a timeout. [`shutdown`] additionally writes a
//! "halt" sentinel
//! into `PM_RSTS` first — the boot-partition field set to 63, which the
//! firmware (`bootcode.bin`) reads on the way back up and, seeing that
//! reserved value, halts instead of booting. That's the closest this
//! hardware has to a power-off: the board stays powered but idle until it
//! is physically power-cycled, since nothing on it can gate its own
//! supply.
//!
//! Like [`crate::watchdog`] and [`crate::rng`], the PM block isn't modeled
//! in `bcm2837-lpa`, so this pokes its known physical addresses directly.
//! Register layout, the `PM_PASSWORD` requirement, and the halt encoding
//! follow Linux's `bcm2835_wdt` driver (`drivers/watchdog/bcm2835_wdt.c`),
//! which registers the restart and power-off handlers alongside the
//! watchdog itself — the same reference [`crate::watchdog`] follows, and
//! the standard one for this undocumented-by-Broadcom corner of the PM
//! block. The constants are intentionally re-declared here rather than
//! shared with [`crate::watchdog`], keeping each PM-touching module
//! self-contained the way `rng`/`watchdog` already are.
/// PM block base address: the peripheral base plus the block's
/// `0x0010_0000` offset.
///
/// Taken from [`crate::soc`] rather than written out, which is not
/// tidiness — it was `0x3f10_0000`, the BCM2836/2837 value, with no chip
/// selection at all. On a BCM2835, whose peripherals are at
/// `0x2000_0000`, that address is nothing: [`reboot`] wrote to it and
/// took a data abort, so a Pi Zero could be told to reboot exactly once
/// and what it did instead was fault. The MMU maps the peripheral region
/// the chip actually has, which is what turns a wrong address here into
/// a translation fault rather than a silent write to whatever is there.
const PM_BASE: usize = cratePERIPHERAL_BASE as usize + 0x0010_0000;
/// Reset controller register: bits [5:4] select the reset type applied
/// when the watchdog countdown reaches zero.
const PM_RSTC: *mut u32 = as *mut u32;
/// Reset status register. Beyond latched reset-cause flags, its
/// even-numbered low bits ([0], [2], … [10]) encode the boot-partition
/// number the firmware reads on the next reset — the field [`shutdown`]
/// sets to 63 to request a halt.
const PM_RSTS: *mut u32 = as *mut u32;
/// Watchdog register: bits [19:0] are the countdown, in ticks of a fixed
/// 65536 Hz clock.
const PM_WDOG: *mut u32 = as *mut u32;
/// Password required in the top byte of every `PM_RSTC`/`PM_RSTS`/`PM_WDOG`
/// write for the hardware to accept it — any other top byte is silently
/// ignored (see [`crate::watchdog`]'s module doc for the full rationale).
const PM_PASSWORD: u32 = 0x5a00_0000;
/// `PM_WDOG`'s 20-bit countdown field.
const PM_WDOG_TIME_MASK: u32 = 0x000f_ffff;
/// `PM_RSTC`'s reset-type field (bits [5:4]).
const PM_RSTC_WRCFG_MASK: u32 = 0x0000_0030;
/// `PM_RSTC` reset-type value that arms a full board reset once the
/// watchdog countdown reaches zero.
const PM_RSTC_WRCFG_FULL_RESET: u32 = 0x0000_0020;
/// Boot-partition 63 encoded across `PM_RSTS`'s even low bits ([0], [2],
/// … [10]): `0x1 | 0x4 | 0x10 | 0x40 | 0x100 | 0x400`. 63 is the reserved
/// "halt" partition — the firmware treats it as "do not boot" rather than
/// a real partition to load, which is how [`shutdown`] stops the board
/// from rebooting after the reset. This is the same magic value Linux
/// writes to power a Pi off.
const PM_RSTS_HALT_PARTITION: u32 = 0x0000_0555;
/// A short watchdog countdown for a deliberate reset — long enough for the
/// register writes to take effect, short enough to be indistinguishable
/// from immediate (~150 µs at the 65536 Hz clock). Mirrors the count
/// Linux's driver loads for a software-requested restart.
const RESET_WDOG_TICKS: u32 = 10;
/// Reboots the board immediately.
///
/// Arms the PM watchdog with a tiny countdown and a full-reset type, then
/// parks the core; the reset fires within roughly 150 µs, equivalent to a
/// power cycle. Does not return.
///
/// Any pending UART/peripheral output should be flushed *before* calling
/// this — the reset does not wait for in-flight transmissions to drain.
!
/// Shuts the board down (halts it).
///
/// Sets `PM_RSTS`'s boot-partition field to the reserved "halt" value
/// (63), then triggers the same reset [`reboot`] does. On the way back up
/// the firmware sees the halt sentinel and stops instead of booting, so
/// the board goes idle and stays that way until it is physically power-
/// cycled — this hardware can't actually cut its own power. Does not
/// return.
///
/// As with [`reboot`], flush any pending output first.
!
/// Loads the short reset countdown into `PM_WDOG` and arms `PM_RSTC`'s
/// full-reset type — the reset trigger shared by [`reboot`] and
/// [`shutdown`], which differ only in whether the halt sentinel is written
/// first.
///
/// # Safety
///
/// Writes the PM reset registers, resetting the board once the countdown
/// (~150 µs) elapses.
unsafe