//! The Raspberry Pi 3 (BCM2837, four Cortex-A53 cores): a **skeleton**. //! Nothing here is used or tested yet; it writes down where things are so //! the next stages have a place to start (see "Raspberry Pi roadmap" in //! the README). QEMU can emulate it too: `qemu-system-aarch64 -M raspi3b`. //! //! How it differs from QEMU's virt: //! //! ```text //! 0x0000_0000 RAM (1 GiB minus what the GPU keeps; the firmware loads //! kernel8.img at 0x8_0000 and passes the DTB in x0) //! 0x3f00_0000 peripherals ("0x7e..." in Broadcom's manual = 0x3f... here) //! 0x3f00_b200 the BCM2835 interrupt controller (not a GIC!) //! 0x3f00_b880 the mailbox (ask the GPU for a framebuffer) //! 0x3f20_1000 PL011 UART0 (same driver as virt) //! 0x3f21_5040 the mini-UART (UART1, a different, simpler chip) //! 0x3f30_0000 EMMC SD card controller (0x3f20_2000: SDHOST, the other one) //! 0x3f98_0000 USB (DWC2); networking is a LAN9514 on that USB bus //! 0x4000_0000 the "ARM local" block: per-core timer interrupts, mailboxes //! ``` //! //! So RAM and devices share the first GiB: the 1 GiB blocks paging.rs //! uses for virt aren't fine enough, and the Pi needs 2 MiB blocks (TODO). use super::pl011::Pl011; use super::{Board, IrqController, Kind, Uart}; use crate::arch::aarch64::dtb::Fdt; use alloc::string::String; use alloc::vec::Vec; pub const PERIPHERAL_BASE: usize = 0x3f00_0000; pub const UART0_BASE: usize = PERIPHERAL_BASE + 0x20_1000; pub const MINI_UART_BASE: usize = PERIPHERAL_BASE + 0x21_5040; pub const IRQ_BASE: usize = PERIPHERAL_BASE + 0xb200; pub const MAILBOX_BASE: usize = PERIPHERAL_BASE + 0xb880; pub const EMMC_BASE: usize = PERIPHERAL_BASE + 0x30_0000; pub const SDHOST_BASE: usize = PERIPHERAL_BASE + 0x20_2000; pub const USB_BASE: usize = PERIPHERAL_BASE + 0x98_0000; pub const LOCAL_BASE: usize = 0x4000_0000; /// ARM local block: core 0's timer interrupt control (CNTVIRQ is bit 3). pub const LOCAL_TIMER_INT_CTRL0: usize = LOCAL_BASE + 0x40; /// ARM local block: core 0's interrupt source register. pub const LOCAL_IRQ_SOURCE0: usize = LOCAL_BASE + 0x60; /// The BCM2835 interrupt controller plus the ARM local block. TODO: the /// timer goes through the local block (set bit 3 of LOCAL_TIMER_INT_CTRL0, /// read LOCAL_IRQ_SOURCE0); devices through the BCM2835 pending registers. pub struct Bcm2835Irq; impl IrqController for Bcm2835Irq { fn init(&self) { // TODO(Pi 3): disable everything (IRQ_BASE + 0x1c/0x20/0x24). } fn enable(&self, _irq: u32) { // TODO(Pi 3): local timer -> LOCAL_TIMER_INT_CTRL0; devices -> enable registers. } fn ack(&self) -> Option { None // TODO(Pi 3): read LOCAL_IRQ_SOURCE0, then the pending registers } fn eoi(&self, _irq: u32) { // Nothing to do on this controller: the device clears its own line. } } pub struct Raspi3 { uart: Pl011, irq: Bcm2835Irq, } /// Not selected by `board::current()` yet. pub static RASPI3: Raspi3 = Raspi3 { uart: Pl011::new(UART0_BASE), irq: Bcm2835Irq }; /// TODO(Pi 3): needs 2 MiB granularity (RAM up to 0x3f00_0000, devices after). static MAP: [(u64, Kind); 1] = [(0, Kind::Normal)]; impl Board for Raspi3 { fn name(&self) -> &'static str { "Raspberry Pi 3 (skeleton)" } fn uart(&self) -> &dyn Uart { &self.uart } fn irq(&self) -> &dyn IrqController { &self.irq } fn timer_irq(&self) -> u32 { 3 // CNTVIRQ's bit in LOCAL_IRQ_SOURCE0 } fn memory_map(&self) -> &'static [(u64, Kind)] { &MAP } fn configure(&self, _dtb: Option<&Fdt>) -> Vec { Vec::from([String::from("raspi3: skeleton only (TODO: read the DTB the firmware passes in x0)")]) } }