Skip to main content

rpi_hal/
lib.rs

1//! Hardware abstraction layer for bare-metal Raspberry Pi.
2//!
3//! Built around the BCM2836/BCM2837 (Pi 2, Pi 3), with preliminary
4//! support for the BCM2711 (Pi 4), the BCM2835 (Pi 1, Pi Zero) and the
5//! BCM2712 (Pi 5) — see the crate's README for what is verified on each.
6//! Exactly one of the `bcm2837`/`bcm2711`/`bcm2835`/`bcm2712` features
7//! selects the chip; none is a default, since there is no sensible default
8//! target chip.
9
10#![no_std]
11#![deny(missing_docs)]
12// docs.rs builds with `--cfg docsrs` (see `[package.metadata.docs.rs]` in
13// Cargo.toml), which turns on rustdoc labelling every feature-gated item
14// with the feature that provides it -- without it a reader sees modules
15// like `gpio::asynch` with no indication that a feature is needed to get
16// them. Gated on the cfg rather than unconditional because it's a
17// nightly-only rustdoc feature.
18#![cfg_attr(docsrs, feature(doc_cfg))]
19
20// The BCM2712 (Pi 5) builds only a handful of the modules below, and the
21// `not(feature = "bcm2712")` on the rest is that list. Each one left out
22// drives a peripheral the BCM2712 does not have where the driver expects
23// it: most of the I/O moved onto the RP1 south bridge as different IP
24// (RP1's GPIO, for one, shares nothing with the BCM283x block), and what
25// stayed on the SoC moved address. What remains is CPU state (caches, MMU,
26// FPU, PMU, exception vectors), the System Timer, and the two GPIO drivers
27// written for this chip. A module rejoins once it has been checked against
28// the BCM2712's own block, not before: one quietly built against the wrong
29// register map would compile and then hang on its first access.
30
31/// Audio out through the VideoCore firmware's `ril.audio_render` MMAL
32/// component — HDMI, or the analog jack.
33#[cfg(feature = "mmal")]
34pub mod audio_render;
35/// Blocking driver for the auxiliary SPI controllers SPI1 and SPI2.
36#[cfg(not(feature = "bcm2712"))]
37pub mod aux_spi;
38mod barrier;
39/// HCI bring-up for the on-board BCM43438 Bluetooth controller (Pi 3):
40/// H4 transport, `.hcd` patchram firmware load, and local
41/// version/address readback.
42#[cfg(not(feature = "bcm2712"))]
43pub mod bluetooth;
44#[cfg(feature = "rt")]
45mod boot;
46mod cache;
47#[cfg(not(feature = "bcm2712"))]
48mod clock_manager;
49/// Identity of the core executing this code — see the module's own doc
50/// comment for why this is not part of `multicore`.
51pub mod cpu;
52#[cfg(feature = "rt")]
53mod critical_section;
54/// Blocking driver for the DMA controller.
55#[cfg(not(feature = "bcm2712"))]
56pub mod dma;
57mod emmc;
58// Installs `__unhandled_exception` rather than offering an API, so there
59// is nothing here to make public -- see the module's own documentation.
60#[cfg(feature = "fault-report")]
61mod fault;
62/// Enabling the hardware floating-point / SIMD unit (VFP + NEON).
63pub mod fpu;
64/// Per-core ARM generic (architected) timer: monotonic counter, blocking
65/// delays, and interrupt-driven deadlines.
66///
67/// Not built for ARMv6 (BCM2835): the generic timer is an ARMv7-A
68/// extension, and the per-core control and routing registers this driver
69/// programs live in an ARM-local peripheral block the BCM2835 does not
70/// have. [`timer`] — the BCM System Timer, a peripheral rather than CPU
71/// state — is the monotonic clock there.
72///
73/// Not built for BCM2712 yet either: the counter itself is CPU state, but
74/// the per-core control registers this driver programs sit in the
75/// BCM2836's ARM-local block, which the BCM2712 does not have.
76#[cfg(not(any(armv6, feature = "bcm2712")))]
77pub mod generic_timer;
78/// The BCM2712's always-on GPIO block, which carries the Pi 5's ACT LED.
79#[cfg(feature = "bcm2712")]
80pub mod gio_aon;
81/// Typestated GPIO pin wrapper with `embedded-hal` `digital` trait
82/// implementations.
83#[cfg(not(feature = "bcm2712"))]
84pub mod gpio;
85/// Typestated GPIO pin wrapper with `embedded-hal` `digital` trait
86/// implementations, over the header pins on the Pi 5's RP1.
87#[cfg(feature = "bcm2712")]
88#[path = "gpio_rp1.rs"]
89pub mod gpio;
90/// Transport-independent HID report descriptor parser, shared by the USB
91/// and Bluetooth HID hosts — see [`hid_report::ReportDescriptor`].
92#[cfg(not(feature = "bcm2712"))]
93pub mod hid_report;
94/// Blocking driver for I2C1 (BSC1).
95#[cfg(not(feature = "bcm2712"))]
96pub mod i2c;
97/// Blocking driver for the header's I2C buses, on the Pi 5's RP1.
98#[cfg(feature = "bcm2712")]
99#[path = "i2c_rp1.rs"]
100pub mod i2c;
101#[cfg(feature = "rt")]
102/// CPU-level IRQ enable/disable and the exception vector table.
103pub mod irq;
104/// Safe wrapper around the BCM2836/2837 legacy interrupt controller.
105///
106/// Not built under `bcm2711`: BCM2711 wires a different set of
107/// peripherals through the surviving legacy IC block (its PAC's bit
108/// names for the shared pending/enable registers genuinely differ, not
109/// just cosmetically), and it's superseded there by GIC-400 anyway --
110/// a real, separate driver this crate doesn't have yet. No interrupt
111/// controller is available under `bcm2711` until that lands, so nothing
112/// interrupt-driven works there either.
113#[cfg(not(any(feature = "bcm2711", feature = "bcm2712")))]
114pub mod lic;
115/// Blocking driver for the VideoCore mailbox property interface.
116pub mod mailbox;
117/// Where the loaded image ends and free RAM begins — see
118/// [`mem::heap_region`], which is how a program sizes a heap without
119/// hardcoding a board's memory split.
120///
121/// Needs the `rt` feature, since the boundary it reports is the one this
122/// crate's linker script places. A program supplying its own script owns
123/// that layout instead.
124#[cfg(all(feature = "rt", not(feature = "bcm2712")))]
125pub mod mem;
126/// Blocking driver for the mini UART (UART1).
127#[cfg(not(feature = "bcm2712"))]
128pub mod mini_uart;
129/// Client for the VideoCore firmware's MMAL multimedia framework, over
130/// [`vchiq`] — components, ports, parameters, and buffer exchange.
131#[cfg(feature = "mmal")]
132pub mod mmal;
133/// Identity-mapped MMU bring-up, plus the one thing a driver may need to
134/// change about the map afterwards: taking a region of RAM out of the
135/// caches ([`mmu::set_uncached`]), for memory a bus master writes
136/// concurrently with this core.
137#[cfg(feature = "mmu")]
138pub mod mmu;
139#[cfg(all(feature = "rt", feature = "multicore", not(armv6)))]
140/// Bringing up secondary cores (1-3) — see the module's own doc
141/// comment for the wake-up handoff this builds on.
142///
143/// Not built for ARMv6 (BCM2835): that chip has one core, and no
144/// ARM-local mailbox registers for a second one to be woken through. The
145/// `multicore` feature is rejected outright there rather than quietly
146/// producing a build without this module — see the `compile_error!`
147/// below.
148pub mod multicore;
149// A consumer who asked for `multicore` on a uniprocessor chip asked for
150// something that cannot be delivered, and the failure to be loud about is
151// the silent one: without this, the feature would simply compile away and
152// the first sign would be `rpi_hal::multicore` not resolving.
153#[cfg(all(feature = "multicore", armv6))]
154compile_error!(
155    "the `multicore` feature is not available on ARMv6: the BCM2835 (Pi 1, \
156     Pi Zero) is a single-core part. Drop the feature -- note that it is \
157     implied by nothing else, so nothing else has to change."
158);
159// The same loud failure for the features whose subsystems the BCM2712 does
160// not have yet. Each is a feature a consumer opts into by name, so it is
161// asking for something specific, and quietly compiling it away would leave
162// them a missing module or a link error instead of a reason.
163#[cfg(all(feature = "bcm2712", feature = "multicore"))]
164compile_error!(
165    "the `multicore` feature is not available on the BCM2712 (Pi 5) yet: its \
166     secondary cores are started through PSCI calls to the boot firmware, \
167     not the ARM-local mailboxes this crate's `multicore` uses."
168);
169#[cfg(all(feature = "bcm2712", feature = "fault-report"))]
170compile_error!(
171    "the `fault-report` feature is not available on the BCM2712 (Pi 5) yet: \
172     its handler re-initialises UART0 from inside an exception, which has \
173     not been checked against that chip's RP1-hosted UART."
174);
175#[cfg(all(feature = "bcm2712", any(feature = "vchiq", feature = "v3d")))]
176compile_error!(
177    "the `vchiq`, `mmal` and `v3d` features are not available on the BCM2712 \
178     (Pi 5): they drive the VideoCore IV-era firmware interfaces and GPU."
179);
180/// Reference I2C driver for the OV5647 (Raspberry Pi Camera v1) image
181/// sensor — a third-party device (paired with [`unicam`]), not a SoC
182/// peripheral.
183#[cfg(not(feature = "bcm2712"))]
184pub mod ov5647;
185/// Peripheral access for the BCM2712 (Pi 5): the hand-written stand-in for
186/// the generated PAC the other chips re-export under this name.
187#[cfg(feature = "bcm2712")]
188#[path = "pac_bcm2712.rs"]
189pub mod pac;
190/// Blocking bring-up for the PCM / I2S peripheral (digital audio out to an
191/// external I2S DAC).
192#[cfg(not(feature = "bcm2712"))]
193pub mod pcm;
194/// The PMU cycle counter — a cheap per-core CPU-cycle clock for profiling
195/// work too short for the System Timer to measure without disturbing it.
196///
197/// Not built for ARMv6 (BCM2835): the ARM1176 has performance counters,
198/// but behind entirely different CP15 registers from the ARMv7-A PMU this
199/// driver programs. [`timer`] is the clock available there.
200#[cfg(not(armv6))]
201pub mod pmu;
202/// Board reboot and shutdown via the Power Management (PM) block.
203#[cfg(not(feature = "bcm2712"))]
204pub mod power;
205/// Blocking driver for the PWM controller.
206#[cfg(not(feature = "bcm2712"))]
207pub mod pwm;
208/// Blocking driver for the hardware random number generator.
209#[cfg(not(feature = "bcm2712"))]
210pub mod rng;
211/// Blocking driver for the on-board SD card slot (Arasan EMMC host
212/// controller, or the SDHCI that replaced it on the Pi 4 and Pi 5).
213pub mod sd;
214/// Blocking driver for the SD card slot over the *other* host
215/// controller, SDHOST — the one that leaves the Arasan controller free
216/// for [`sdio`], so a board can drive its card and its wireless chip at
217/// the same time.
218///
219/// Not built for BCM2711: a Pi 4 drives its card from EMMC2, which is a
220/// separate block again, and GPIO48-53 there are the Ethernet PHY's
221/// RGMII interface rather than the card slot — so routing them here
222/// would be actively wrong rather than merely useless.
223#[cfg(not(any(feature = "bcm2711", feature = "bcm2712")))]
224pub mod sdhost;
225/// Blocking SDIO driver for the on-board BCM43438 wireless chip (Wi-Fi
226/// side), via the same Arasan EMMC host controller.
227#[cfg(not(feature = "bcm2712"))]
228pub mod sdio;
229#[cfg(not(feature = "bcm2712"))]
230mod soc;
231/// Blocking driver for SPI0.
232#[cfg(not(feature = "bcm2712"))]
233pub mod spi;
234/// Extent of the main stack and how much of it is left — see
235/// [`stack::headroom`].
236///
237/// Needs the `rt` feature, since the region it reports on is the one
238/// this crate's linker script reserves and its boot code points `sp`
239/// at. A program supplying its own boot code and linker script owns
240/// that layout instead.
241#[cfg(feature = "rt")]
242pub mod stack;
243/// BCM System Timer driver: free-running microsecond counter and delays.
244pub mod timer;
245/// Driver for the DSI touchscreen's touch input (the firmware-mediated
246/// shared touch buffer, not I2C).
247#[cfg(not(feature = "bcm2712"))]
248pub mod touch;
249/// Blocking driver for UART0 (PL011) — on the Pi 5, RP1's UART0, on the
250/// same GPIO14/15 header pins.
251pub mod uart;
252/// Blocking driver for the Unicam CSI-2 camera receiver.
253#[cfg(not(feature = "bcm2712"))]
254pub mod unicam;
255/// USB host support for the built-in DWC2 OTG controller.
256#[cfg(not(feature = "bcm2712"))]
257pub mod usb;
258/// Bring-up for the V3D 3D pipeline (VideoCore IV's QPU shader cores).
259/// Pi 3 (BCM2836/BCM2837) only.
260#[cfg(feature = "v3d")]
261pub mod v3d;
262/// VCHIQ: the shared-memory, doorbell-signalled message transport to the
263/// VideoCore firmware — everything the simple request/response
264/// [`mailbox`] can't carry.
265#[cfg(feature = "vchiq")]
266pub mod vchiq;
267/// Hardware H.264 video decode, driven through the VideoCore firmware's
268/// `ril.video_decode` MMAL component.
269#[cfg(feature = "mmal")]
270pub mod video_decode;
271/// Blocking driver for the PM block's watchdog timer.
272#[cfg(not(feature = "bcm2712"))]
273pub mod watchdog;
274/// Host control protocol (SDPCM/CDC) for the on-board BCM43430 Wi-Fi
275/// chip, on top of `sdio`.
276#[cfg(not(feature = "bcm2712"))]
277pub mod wifi;
278
279/// Re-export of the underlying peripheral access crate, for direct
280/// register access alongside this crate's higher-level wrappers.
281/// Whichever of `bcm2837`/`bcm2711`/`bcm2835` is enabled (see
282/// `Cargo.toml`) -- with more than one on, the precedence is
283/// `bcm2712` > `bcm2711` > `bcm2837` > `bcm2835`, newest chip first, and
284/// the same order every other chip branch in this crate follows. The
285/// `bcm2712` arm is the `pac` module above rather than a re-export, since
286/// that chip has no generated PAC.
287#[cfg(all(feature = "bcm2711", not(feature = "bcm2712")))]
288pub use bcm2711_lpa as pac;
289#[cfg(all(
290    not(feature = "bcm2712"),
291    not(feature = "bcm2711"),
292    not(feature = "bcm2837"),
293    feature = "bcm2835"
294))]
295pub use bcm2835_lpa as pac;
296#[cfg(all(
297    not(feature = "bcm2712"),
298    not(feature = "bcm2711"),
299    feature = "bcm2837"
300))]
301pub use bcm2837_lpa as pac;
302// With neither feature on there is no `pac` at all, and every module that
303// reaches for a register fails to resolve it -- 18 errors naming
304// `crate::pac` and none of them saying a chip has to be chosen. This is
305// the third arm of the re-export above, and it puts that sentence in
306// front of them: expansion runs before name resolution, so this is the
307// first error printed, and rustc gives up early enough afterwards that
308// the tail shrinks to two (the unresolved `crate::pac`, and an
309// `i2c.rs`/`init` duplicate-definition that only looks like one because
310// the two impls' distinguishing types come from `pac`). Not silenced
311// entirely -- that would mean stubbing out a fake `pac`, a worse trade
312// than a short cascade under a clear headline.
313#[cfg(not(any(
314    feature = "bcm2837",
315    feature = "bcm2711",
316    feature = "bcm2835",
317    feature = "bcm2712"
318)))]
319compile_error!(
320    "no chip selected: rpi-hal needs exactly one of the `bcm2837` (Pi 2, Pi 3), \
321     `bcm2711` (Pi 4), `bcm2835` (Pi 1, Pi Zero) or `bcm2712` (Pi 5) features. \
322     None is a default, since there is no sensible default target chip -- add \
323     one to this crate's dependency entry, e.g. \
324     `rpi-hal = { version = \"0.10\", features = [\"bcm2837\"] }`."
325);
326// The Pi 5's boot firmware starts 64-bit kernels only, so a 32-bit build
327// would produce an image nothing can run -- and `pac`'s addresses, all
328// above 4 GiB, would not even fit a pointer.
329#[cfg(all(feature = "bcm2712", not(target_arch = "aarch64")))]
330compile_error!(
331    "the `bcm2712` feature (Pi 5) needs an AArch64 target, e.g. \
332     `--target aarch64-unknown-none-softfloat`: the Pi 5 only boots 64-bit \
333     kernels."
334);
335
336/// Halts the calling core forever, parking it in a low-power
337/// wait-for-event loop.
338///
339/// Useful as the terminal state of a bare-metal `kmain` or as the tail
340/// of a `#[panic_handler]`, where there is nothing left to do and no OS
341/// to return to.
342pub fn halt() -> ! {
343    loop {
344        unsafe { core::arch::asm!("wfe") };
345    }
346}