Refactor tutorial X1
parent
48e4d135c2
commit
326b43b2be
Binary file not shown.
Binary file not shown.
@ -0,0 +1,22 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! Architectural symmetric multiprocessing.
|
||||
|
||||
use cortex_a::regs::*;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Code
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// Return the executing core's id.
|
||||
#[inline(always)]
|
||||
pub fn core_id<T>() -> T
|
||||
where
|
||||
T: From<u8>,
|
||||
{
|
||||
const CORE_MASK: u64 = 0b11;
|
||||
|
||||
T::from((MPIDR_EL1.get() & CORE_MASK) as u8)
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! Conditional exporting of processor architecture code.
|
||||
|
||||
#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
|
||||
mod aarch64;
|
||||
|
||||
#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
|
||||
pub use aarch64::*;
|
@ -1,53 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! Synchronization primitives.
|
||||
|
||||
use crate::interface;
|
||||
use core::cell::UnsafeCell;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Arch-public
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// A pseudo-lock for teaching purposes.
|
||||
///
|
||||
/// Used to introduce [interior mutability].
|
||||
///
|
||||
/// In contrast to a real Mutex implementation, does not protect against concurrent access to the
|
||||
/// contained data. This part is preserved for later lessons.
|
||||
///
|
||||
/// The lock will only be used as long as it is safe to do so, i.e. as long as the kernel is
|
||||
/// executing single-threaded, aka only running on a single core with interrupts disabled.
|
||||
///
|
||||
/// [interior mutability]: https://doc.rust-lang.org/std/cell/index.html
|
||||
pub struct NullLock<T: ?Sized> {
|
||||
data: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized + Send> Send for NullLock<T> {}
|
||||
unsafe impl<T: ?Sized + Send> Sync for NullLock<T> {}
|
||||
|
||||
impl<T> NullLock<T> {
|
||||
/// Wraps `data` into a new `NullLock`.
|
||||
pub const fn new(data: T) -> NullLock<T> {
|
||||
NullLock {
|
||||
data: UnsafeCell::new(data),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// OS interface implementations
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
impl<T> interface::sync::Mutex for &NullLock<T> {
|
||||
type Data = T;
|
||||
|
||||
fn lock<R>(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R {
|
||||
// In a real lock, there would be code encapsulating this line that ensures that this
|
||||
// mutable reference will ever only be given out once at a time.
|
||||
f(unsafe { &mut *self.data.get() })
|
||||
}
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! GPIO Driver.
|
||||
|
||||
use crate::{cpu, driver, synchronization, synchronization::NullLock};
|
||||
use core::ops;
|
||||
use register::{mmio::*, register_bitfields, register_structs};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Private Definitions
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
// GPIO registers.
|
||||
//
|
||||
// Descriptions taken from
|
||||
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
|
||||
register_bitfields! {
|
||||
u32,
|
||||
|
||||
/// GPIO Function Select 1
|
||||
GPFSEL1 [
|
||||
/// Pin 15
|
||||
FSEL15 OFFSET(15) NUMBITS(3) [
|
||||
Input = 0b000,
|
||||
Output = 0b001,
|
||||
AltFunc0 = 0b100 // PL011 UART RX
|
||||
|
||||
],
|
||||
|
||||
/// Pin 14
|
||||
FSEL14 OFFSET(12) NUMBITS(3) [
|
||||
Input = 0b000,
|
||||
Output = 0b001,
|
||||
AltFunc0 = 0b100 // PL011 UART TX
|
||||
]
|
||||
],
|
||||
|
||||
/// GPIO Pull-up/down Clock Register 0
|
||||
GPPUDCLK0 [
|
||||
/// Pin 15
|
||||
PUDCLK15 OFFSET(15) NUMBITS(1) [
|
||||
NoEffect = 0,
|
||||
AssertClock = 1
|
||||
],
|
||||
|
||||
/// Pin 14
|
||||
PUDCLK14 OFFSET(14) NUMBITS(1) [
|
||||
NoEffect = 0,
|
||||
AssertClock = 1
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
RegisterBlock {
|
||||
(0x00 => GPFSEL0: ReadWrite<u32>),
|
||||
(0x04 => GPFSEL1: ReadWrite<u32, GPFSEL1::Register>),
|
||||
(0x08 => GPFSEL2: ReadWrite<u32>),
|
||||
(0x0C => GPFSEL3: ReadWrite<u32>),
|
||||
(0x10 => GPFSEL4: ReadWrite<u32>),
|
||||
(0x14 => GPFSEL5: ReadWrite<u32>),
|
||||
(0x18 => _reserved1),
|
||||
(0x94 => GPPUD: ReadWrite<u32>),
|
||||
(0x98 => GPPUDCLK0: ReadWrite<u32, GPPUDCLK0::Register>),
|
||||
(0x9C => GPPUDCLK1: ReadWrite<u32>),
|
||||
(0xA0 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct GPIOInner {
|
||||
base_addr: usize,
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Definitions
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// Representation of the GPIO HW.
|
||||
pub struct GPIO {
|
||||
inner: NullLock<GPIOInner>,
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Private Code
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
impl ops::Deref for GPIOInner {
|
||||
type Target = RegisterBlock;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl GPIOInner {
|
||||
const fn new(base_addr: usize) -> Self {
|
||||
Self { base_addr }
|
||||
}
|
||||
|
||||
/// Return a pointer to the associated MMIO register block.
|
||||
fn ptr(&self) -> *const RegisterBlock {
|
||||
self.base_addr as *const _
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Code
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
impl GPIO {
|
||||
/// Create an instance.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The user must ensure to provide the correct `base_addr`.
|
||||
pub const unsafe fn new(base_addr: usize) -> Self {
|
||||
Self {
|
||||
inner: NullLock::new(GPIOInner::new(base_addr)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Map PL011 UART as standard output.
|
||||
///
|
||||
/// TX to pin 14
|
||||
/// RX to pin 15
|
||||
pub fn map_pl011_uart(&self) {
|
||||
let mut r = &self.inner;
|
||||
r.lock(|inner| {
|
||||
// Map to pins.
|
||||
inner
|
||||
.GPFSEL1
|
||||
.modify(GPFSEL1::FSEL14::AltFunc0 + GPFSEL1::FSEL15::AltFunc0);
|
||||
|
||||
// Enable pins 14 and 15.
|
||||
inner.GPPUD.set(0);
|
||||
cpu::spin_for_cycles(150);
|
||||
|
||||
inner
|
||||
.GPPUDCLK0
|
||||
.write(GPPUDCLK0::PUDCLK14::AssertClock + GPPUDCLK0::PUDCLK15::AssertClock);
|
||||
cpu::spin_for_cycles(150);
|
||||
|
||||
inner.GPPUDCLK0.set(0);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// OS Interface Code
|
||||
//------------------------------------------------------------------------------
|
||||
use synchronization::interface::Mutex;
|
||||
|
||||
impl driver::interface::DeviceDriver for GPIO {
|
||||
fn compatible(&self) -> &str {
|
||||
"BCM GPIO"
|
||||
}
|
||||
}
|
@ -1,258 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! GPIO driver.
|
||||
|
||||
use crate::{arch, arch::sync::NullLock, interface};
|
||||
use core::ops;
|
||||
use register::{mmio::ReadWrite, register_bitfields, register_structs};
|
||||
|
||||
// GPIO registers.
|
||||
//
|
||||
// Descriptions taken from
|
||||
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
|
||||
register_bitfields! {
|
||||
u32,
|
||||
|
||||
/// GPIO Function Select 1
|
||||
GPFSEL1 [
|
||||
/// Pin 15
|
||||
FSEL15 OFFSET(15) NUMBITS(3) [
|
||||
Input = 0b000,
|
||||
Output = 0b001,
|
||||
AltFunc0 = 0b100 // PL011 UART RX
|
||||
|
||||
],
|
||||
|
||||
/// Pin 14
|
||||
FSEL14 OFFSET(12) NUMBITS(3) [
|
||||
Input = 0b000,
|
||||
Output = 0b001,
|
||||
AltFunc0 = 0b100 // PL011 UART TX
|
||||
]
|
||||
],
|
||||
|
||||
/// GPIO Function Select 2
|
||||
GPFSEL2 [
|
||||
/// Pin 27
|
||||
FSEL27 OFFSET(21) NUMBITS(3)[
|
||||
Input = 0b000,
|
||||
Output = 0b001,
|
||||
AltFunc4 = 0b011 // JTAG TMS
|
||||
],
|
||||
|
||||
/// Pin 26
|
||||
FSEL26 OFFSET(18) NUMBITS(3)[
|
||||
Input = 0b000,
|
||||
Output = 0b001,
|
||||
AltFunc4 = 0b011 // JTAG TDI
|
||||
],
|
||||
|
||||
/// Pin 25
|
||||
FSEL25 OFFSET(15) NUMBITS(3)[
|
||||
Input = 0b000,
|
||||
Output = 0b001,
|
||||
AltFunc4 = 0b011 // JTAG TCK
|
||||
],
|
||||
|
||||
/// Pin 24
|
||||
FSEL24 OFFSET(12) NUMBITS(3)[
|
||||
Input = 0b000,
|
||||
Output = 0b001,
|
||||
AltFunc4 = 0b011 // JTAG TDO
|
||||
],
|
||||
|
||||
/// Pin 23
|
||||
FSEL23 OFFSET(9) NUMBITS(3)[
|
||||
Input = 0b000,
|
||||
Output = 0b001,
|
||||
AltFunc4 = 0b011 // JTAG RTCK
|
||||
],
|
||||
|
||||
/// Pin 22
|
||||
FSEL22 OFFSET(6) NUMBITS(3)[
|
||||
Input = 0b000,
|
||||
Output = 0b001,
|
||||
AltFunc4 = 0b011 // JTAG TRST
|
||||
]
|
||||
],
|
||||
|
||||
/// GPIO Pull-up/down Clock Register 0
|
||||
GPPUDCLK0 [
|
||||
/// Pin 27
|
||||
PUDCLK27 OFFSET(27) NUMBITS(1) [
|
||||
NoEffect = 0,
|
||||
AssertClock = 1
|
||||
],
|
||||
/// Pin 26
|
||||
PUDCLK26 OFFSET(26) NUMBITS(1) [
|
||||
NoEffect = 0,
|
||||
AssertClock = 1
|
||||
],
|
||||
|
||||
/// Pin 25
|
||||
PUDCLK25 OFFSET(25) NUMBITS(1) [
|
||||
NoEffect = 0,
|
||||
AssertClock = 1
|
||||
],
|
||||
|
||||
/// Pin 24
|
||||
PUDCLK24 OFFSET(24) NUMBITS(1) [
|
||||
NoEffect = 0,
|
||||
AssertClock = 1
|
||||
],
|
||||
|
||||
/// Pin 23
|
||||
PUDCLK23 OFFSET(23) NUMBITS(1) [
|
||||
NoEffect = 0,
|
||||
AssertClock = 1
|
||||
],
|
||||
|
||||
/// Pin 22
|
||||
PUDCLK22 OFFSET(22) NUMBITS(1) [
|
||||
NoEffect = 0,
|
||||
AssertClock = 1
|
||||
],
|
||||
|
||||
/// Pin 15
|
||||
PUDCLK15 OFFSET(15) NUMBITS(1) [
|
||||
NoEffect = 0,
|
||||
AssertClock = 1
|
||||
],
|
||||
|
||||
/// Pin 14
|
||||
PUDCLK14 OFFSET(14) NUMBITS(1) [
|
||||
NoEffect = 0,
|
||||
AssertClock = 1
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
RegisterBlock {
|
||||
(0x00 => GPFSEL0: ReadWrite<u32>),
|
||||
(0x04 => GPFSEL1: ReadWrite<u32, GPFSEL1::Register>),
|
||||
(0x08 => GPFSEL2: ReadWrite<u32, GPFSEL2::Register>),
|
||||
(0x0C => GPFSEL3: ReadWrite<u32>),
|
||||
(0x10 => GPFSEL4: ReadWrite<u32>),
|
||||
(0x14 => GPFSEL5: ReadWrite<u32>),
|
||||
(0x18 => _reserved1),
|
||||
(0x94 => GPPUD: ReadWrite<u32>),
|
||||
(0x98 => GPPUDCLK0: ReadWrite<u32, GPPUDCLK0::Register>),
|
||||
(0x9C => GPPUDCLK1: ReadWrite<u32>),
|
||||
(0xA0 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
/// The driver's private data.
|
||||
struct GPIOInner {
|
||||
base_addr: usize,
|
||||
}
|
||||
|
||||
/// Deref to RegisterBlock.
|
||||
impl ops::Deref for GPIOInner {
|
||||
type Target = RegisterBlock;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl GPIOInner {
|
||||
const fn new(base_addr: usize) -> GPIOInner {
|
||||
GPIOInner { base_addr }
|
||||
}
|
||||
|
||||
/// Return a pointer to the register block.
|
||||
fn ptr(&self) -> *const RegisterBlock {
|
||||
self.base_addr as *const _
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// BSP-public
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
use interface::sync::Mutex;
|
||||
|
||||
/// The driver's main struct.
|
||||
pub struct GPIO {
|
||||
inner: NullLock<GPIOInner>,
|
||||
}
|
||||
|
||||
impl GPIO {
|
||||
pub const unsafe fn new(base_addr: usize) -> GPIO {
|
||||
GPIO {
|
||||
inner: NullLock::new(GPIOInner::new(base_addr)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Map PL011 UART as standard output.
|
||||
///
|
||||
/// TX to pin 14
|
||||
/// RX to pin 15
|
||||
pub fn map_pl011_uart(&self) {
|
||||
let mut r = &self.inner;
|
||||
r.lock(|inner| {
|
||||
// Map to pins.
|
||||
inner
|
||||
.GPFSEL1
|
||||
.modify(GPFSEL1::FSEL14::AltFunc0 + GPFSEL1::FSEL15::AltFunc0);
|
||||
|
||||
// Enable pins 14 and 15.
|
||||
inner.GPPUD.set(0);
|
||||
arch::spin_for_cycles(150);
|
||||
|
||||
inner
|
||||
.GPPUDCLK0
|
||||
.write(GPPUDCLK0::PUDCLK14::AssertClock + GPPUDCLK0::PUDCLK15::AssertClock);
|
||||
arch::spin_for_cycles(150);
|
||||
|
||||
inner.GPPUDCLK0.set(0);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// OS interface implementations
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
impl interface::driver::DeviceDriver for GPIO {
|
||||
fn compatible(&self) -> &str {
|
||||
"GPIO"
|
||||
}
|
||||
}
|
||||
|
||||
impl interface::driver::JTAGOps for GPIO {
|
||||
fn enable(&self) -> interface::driver::Result {
|
||||
let mut r = &self.inner;
|
||||
r.lock(|inner| {
|
||||
inner.GPFSEL2.modify(
|
||||
GPFSEL2::FSEL22::AltFunc4
|
||||
+ GPFSEL2::FSEL23::AltFunc4
|
||||
+ GPFSEL2::FSEL24::AltFunc4
|
||||
+ GPFSEL2::FSEL25::AltFunc4
|
||||
+ GPFSEL2::FSEL26::AltFunc4
|
||||
+ GPFSEL2::FSEL27::AltFunc4,
|
||||
);
|
||||
|
||||
inner.GPPUD.set(0);
|
||||
arch::spin_for_cycles(150);
|
||||
|
||||
inner.GPPUDCLK0.write(
|
||||
GPPUDCLK0::PUDCLK22::AssertClock
|
||||
+ GPPUDCLK0::PUDCLK23::AssertClock
|
||||
+ GPPUDCLK0::PUDCLK24::AssertClock
|
||||
+ GPPUDCLK0::PUDCLK25::AssertClock
|
||||
+ GPPUDCLK0::PUDCLK26::AssertClock
|
||||
+ GPPUDCLK0::PUDCLK27::AssertClock,
|
||||
);
|
||||
arch::spin_for_cycles(150);
|
||||
|
||||
inner.GPPUDCLK0.set(0);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! Top-level BSP file for the Raspberry Pi 3 and 4.
|
||||
|
||||
pub mod console;
|
||||
pub mod cpu;
|
||||
pub mod driver;
|
||||
pub mod memory;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Global instances
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
use super::device_driver;
|
||||
|
||||
static GPIO: device_driver::GPIO =
|
||||
unsafe { device_driver::GPIO::new(memory::map::mmio::GPIO_BASE) };
|
||||
|
||||
static PL011_UART: device_driver::PL011Uart =
|
||||
unsafe { device_driver::PL011Uart::new(memory::map::mmio::PL011_UART_BASE) };
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Code
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// Board identification.
|
||||
pub fn board_name() -> &'static str {
|
||||
#[cfg(feature = "bsp_rpi3")]
|
||||
{
|
||||
"Raspberry Pi 3"
|
||||
}
|
||||
|
||||
#[cfg(feature = "bsp_rpi4")]
|
||||
{
|
||||
"Raspberry Pi 4"
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! BSP console facilities.
|
||||
|
||||
use super::{super::device_driver, memory::map};
|
||||
use crate::console;
|
||||
use core::fmt;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Code
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// In case of a panic, the panic handler uses this function to take a last shot at printing
|
||||
/// something before the system is halted.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - Use only for printing during a panic.
|
||||
pub unsafe fn panic_console_out() -> impl fmt::Write {
|
||||
let mut uart = device_driver::PanicUart::new(map::mmio::PL011_UART_BASE);
|
||||
uart.init();
|
||||
uart
|
||||
}
|
||||
|
||||
/// Return a reference to the console.
|
||||
pub fn console() -> &'static impl console::interface::All {
|
||||
&super::PL011_UART
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! BSP Processor code.
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Definitions
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// Used by `arch` code to find the early boot core.
|
||||
pub const BOOT_CORE_ID: usize = 0;
|
||||
|
||||
/// The early boot core's stack address.
|
||||
pub const BOOT_CORE_STACK_START: u64 = 0x80_000;
|
@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! BSP driver support.
|
||||
|
||||
use crate::driver;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Definitions
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// Device Driver Manager type.
|
||||
pub struct BSPDriverManager {
|
||||
device_drivers: [&'static (dyn DeviceDriver + Sync); 2],
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Global instances
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
static BSP_DRIVER_MANAGER: BSPDriverManager = BSPDriverManager {
|
||||
device_drivers: [&super::GPIO, &super::PL011_UART],
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Code
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// Return a reference to the driver manager.
|
||||
pub fn driver_manager() -> &'static impl driver::interface::DriverManager {
|
||||
&BSP_DRIVER_MANAGER
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// OS Interface Code
|
||||
//------------------------------------------------------------------------------
|
||||
use driver::interface::DeviceDriver;
|
||||
|
||||
impl driver::interface::DriverManager for BSPDriverManager {
|
||||
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)] {
|
||||
&self.device_drivers[..]
|
||||
}
|
||||
|
||||
fn post_device_driver_init(&self) {
|
||||
// Configure PL011Uart's output pins.
|
||||
super::GPIO.map_pl011_uart();
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! BSP Memory Management.
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Definitions
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// The board's memory map.
|
||||
#[rustfmt::skip]
|
||||
pub(super) mod map {
|
||||
pub const GPIO_OFFSET: usize = 0x0020_0000;
|
||||
pub const UART_OFFSET: usize = 0x0020_1000;
|
||||
|
||||
/// Physical devices.
|
||||
#[cfg(feature = "bsp_rpi3")]
|
||||
pub mod mmio {
|
||||
use super::*;
|
||||
|
||||
pub const BASE: usize = 0x3F00_0000;
|
||||
pub const GPIO_BASE: usize = BASE + GPIO_OFFSET;
|
||||
pub const PL011_UART_BASE: usize = BASE + UART_OFFSET;
|
||||
}
|
||||
|
||||
/// Physical devices.
|
||||
#[cfg(feature = "bsp_rpi4")]
|
||||
pub mod mmio {
|
||||
use super::*;
|
||||
|
||||
pub const BASE: usize = 0xFE00_0000;
|
||||
pub const GPIO_BASE: usize = BASE + GPIO_OFFSET;
|
||||
pub const PL011_UART_BASE: usize = BASE + UART_OFFSET;
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! Board Support Package for the Raspberry Pi.
|
||||
|
||||
mod memory_map;
|
||||
|
||||
use super::driver;
|
||||
use crate::interface;
|
||||
use core::fmt;
|
||||
|
||||
/// Used by `arch` code to find the early boot core.
|
||||
pub const BOOT_CORE_ID: u64 = 0;
|
||||
|
||||
/// The early boot core's stack address.
|
||||
pub const BOOT_CORE_STACK_START: u64 = 0x80_000;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Global BSP driver instances
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
static GPIO: driver::GPIO = unsafe { driver::GPIO::new(memory_map::mmio::GPIO_BASE) };
|
||||
static PL011_UART: driver::PL011Uart =
|
||||
unsafe { driver::PL011Uart::new(memory_map::mmio::PL011_UART_BASE) };
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Implementation of the kernel's BSP calls
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// Board identification.
|
||||
#[allow(dead_code)]
|
||||
pub fn board_name() -> &'static str {
|
||||
#[cfg(feature = "bsp_rpi3")]
|
||||
{
|
||||
"Raspberry Pi 3"
|
||||
}
|
||||
|
||||
#[cfg(feature = "bsp_rpi4")]
|
||||
{
|
||||
"Raspberry Pi 4"
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a reference to a `console::All` implementation.
|
||||
pub fn console() -> &'static impl interface::console::All {
|
||||
&PL011_UART
|
||||
}
|
||||
|
||||
/// In case of a panic, the panic handler uses this function to take a last shot at printing
|
||||
/// something before the system is halted.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - Use only for printing during a panic.
|
||||
pub unsafe fn panic_console_out() -> impl fmt::Write {
|
||||
let uart = driver::PanicUart::new(memory_map::mmio::PL011_UART_BASE);
|
||||
uart.init();
|
||||
uart
|
||||
}
|
||||
|
||||
/// Return an array of references to all `DeviceDriver` compatible `BSP` drivers.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The order of devices is the order in which `DeviceDriver::init()` is called.
|
||||
pub fn device_drivers() -> [&'static dyn interface::driver::DeviceDriver; 2] {
|
||||
[&GPIO, &PL011_UART]
|
||||
}
|
||||
|
||||
/// BSP initialization code that runs after driver init.
|
||||
pub fn post_driver_init() {
|
||||
// Configure PL011Uart's output pins.
|
||||
GPIO.map_pl011_uart();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn jtag() -> &'static impl interface::driver::JTAGOps {
|
||||
&GPIO
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! The board's memory map.
|
||||
|
||||
/// Physical devices.
|
||||
#[rustfmt::skip]
|
||||
pub mod mmio {
|
||||
#[cfg(feature = "bsp_rpi3")]
|
||||
pub const BASE: usize = 0x3F00_0000;
|
||||
|
||||
#[cfg(feature = "bsp_rpi4")]
|
||||
pub const BASE: usize = 0xFE00_0000;
|
||||
|
||||
pub const GPIO_BASE: usize = BASE + 0x0020_0000;
|
||||
pub const PL011_UART_BASE: usize = BASE + 0x0020_1000;
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! System console.
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Definitions
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// Console interfaces.
|
||||
pub mod interface {
|
||||
use core::fmt;
|
||||
|
||||
/// Console write functions.
|
||||
pub trait Write {
|
||||
/// Write a single character.
|
||||
fn write_char(&self, c: char);
|
||||
|
||||
/// Write a Rust format string.
|
||||
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
|
||||
|
||||
/// Block execution until the last character has been physically put on the TX wire
|
||||
/// (draining TX buffers/FIFOs, if any).
|
||||
fn flush(&self);
|
||||
}
|
||||
|
||||
/// Console read functions.
|
||||
pub trait Read {
|
||||
/// Read a single character.
|
||||
fn read_char(&self) -> char {
|
||||
' '
|
||||
}
|
||||
|
||||
/// Clear RX buffers, if any.
|
||||
fn clear(&self);
|
||||
}
|
||||
|
||||
/// Console statistics.
|
||||
pub trait Statistics {
|
||||
/// Return the number of characters written.
|
||||
fn chars_written(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
/// Return the number of characters read.
|
||||
fn chars_read(&self) -> usize {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait alias for a full-fledged console.
|
||||
pub trait All = Write + Read + Statistics;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2020 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! Processor code.
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[path = "_arch/aarch64/cpu.rs"]
|
||||
mod arch_cpu;
|
||||
pub use arch_cpu::*;
|
||||
|
||||
pub mod smp;
|
@ -0,0 +1,10 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! Symmetric multiprocessing.
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[path = "../_arch/aarch64/cpu/smp.rs"]
|
||||
mod arch_cpu_smp;
|
||||
pub use arch_cpu_smp::*;
|
@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! Driver support.
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Definitions
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// Driver interfaces.
|
||||
pub mod interface {
|
||||
|
||||
/// Device Driver functions.
|
||||
pub trait DeviceDriver {
|
||||
/// Return a compatibility string for identifying the driver.
|
||||
fn compatible(&self) -> &str;
|
||||
|
||||
/// Called by the kernel to bring up the device.
|
||||
fn init(&self) -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Device driver management functions.
|
||||
///
|
||||
/// The `BSP` is supposed to supply one global instance.
|
||||
pub trait DriverManager {
|
||||
/// Return a slice of references to all `BSP`-instantiated drivers.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The order of devices is the order in which `DeviceDriver::init()` is called.
|
||||
fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)];
|
||||
|
||||
/// Initialization code that runs after driver init.
|
||||
///
|
||||
/// For example, device driver code that depends on other drivers already being online.
|
||||
fn post_device_driver_init(&self);
|
||||
}
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2018-2020 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! Trait definitions for coupling `kernel` and `BSP` code.
|
||||
//!
|
||||
//! ```
|
||||
//! +-------------------+
|
||||
//! | Interface (Trait) |
|
||||
//! | |
|
||||
//! +--+-------------+--+
|
||||
//! ^ ^
|
||||
//! | |
|
||||
//! | |
|
||||
//! +----------+--+ +--+----------+
|
||||
//! | Kernel code | | BSP Code |
|
||||
//! | | | |
|
||||
//! +-------------+ +-------------+
|
||||
//! ```
|
||||
|
||||
/// System console operations.
|
||||
pub mod console {
|
||||
use core::fmt;
|
||||
|
||||
/// Console write functions.
|
||||
pub trait Write {
|
||||
/// Write a single character.
|
||||
fn write_char(&self, c: char);
|
||||
|
||||
/// Write a Rust format string.
|
||||
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
|
||||
|
||||
/// Block execution until the last character has been physically put on the TX wire
|
||||
/// (draining TX buffers/FIFOs, if any).
|
||||
fn flush(&self);
|
||||
}
|
||||
|
||||
/// Console read functions.
|
||||
pub trait Read {
|
||||
/// Read a single character.
|
||||
fn read_char(&self) -> char {
|
||||
' '
|
||||
}
|
||||
|
||||
/// Clear RX buffers, if any.
|
||||
fn clear(&self);
|
||||
}
|
||||
|
||||
/// Console statistics.
|
||||
pub trait Statistics {
|
||||
/// Return the number of characters written.
|
||||
fn chars_written(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
/// Return the number of characters read.
|
||||
fn chars_read(&self) -> usize {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait alias for a full-fledged console.
|
||||
pub trait All = Write + Read + Statistics;
|
||||
}
|
||||
|
||||
/// Synchronization primitives.
|
||||
pub mod sync {
|
||||
/// Any object implementing this trait guarantees exclusive access to the data contained within
|
||||
/// the mutex for the duration of the lock.
|
||||
///
|
||||
/// The trait follows the [Rust embedded WG's
|
||||
/// proposal](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md) and therefore
|
||||
/// provides some goodness such as [deadlock
|
||||
/// prevention](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md#design-decisions-and-compatibility).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Since the lock function takes an `&mut self` to enable deadlock-prevention, the trait is
|
||||
/// best implemented **for a reference to a container struct**, and has a usage pattern that
|
||||
/// might feel strange at first:
|
||||
///
|
||||
/// ```
|
||||
/// static MUT: Mutex<RefCell<i32>> = Mutex::new(RefCell::new(0));
|
||||
///
|
||||
/// fn foo() {
|
||||
/// let mut r = &MUT; // Note that r is mutable
|
||||
/// r.lock(|data| *data += 1);
|
||||
/// }
|
||||
/// ```
|
||||
pub trait Mutex {
|
||||
/// Type of data encapsulated by the mutex.
|
||||
type Data;
|
||||
|
||||
/// Creates a critical section and grants temporary mutable access to the encapsulated data.
|
||||
fn lock<R>(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R;
|
||||
}
|
||||
}
|
||||
|
||||
/// Driver interfaces.
|
||||
pub mod driver {
|
||||
/// Driver result type, e.g. for indicating successful driver init.
|
||||
pub type Result = core::result::Result<(), ()>;
|
||||
|
||||
/// Device Driver functions.
|
||||
pub trait DeviceDriver {
|
||||
/// Return a compatibility string for identifying the driver.
|
||||
fn compatible(&self) -> &str;
|
||||
|
||||
/// Called by the kernel to bring up the device.
|
||||
fn init(&self) -> Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// JTAG driver operations
|
||||
pub trait JTAGOps {
|
||||
/// Enable JTAG.
|
||||
fn enable(&self) -> Result {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Timekeeping interfaces.
|
||||
pub mod time {
|
||||
use core::time::Duration;
|
||||
|
||||
/// Timer functions.
|
||||
pub trait Timer {
|
||||
/// The timer's resolution.
|
||||
fn resolution(&self) -> Duration;
|
||||
|
||||
/// The uptime since power-on of the device.
|
||||
///
|
||||
/// This includes time consumed by firmware and bootloaders.
|
||||
fn uptime(&self) -> Duration;
|
||||
|
||||
/// Spin for a given duration.
|
||||
fn spin_for(&self, duration: Duration);
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2020 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! Synchronization primitives.
|
||||
|
||||
use core::cell::UnsafeCell;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Definitions
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// Synchronization interfaces.
|
||||
pub mod interface {
|
||||
|
||||
/// Any object implementing this trait guarantees exclusive access to the data contained within
|
||||
/// the Mutex for the duration of the provided closure.
|
||||
///
|
||||
/// The trait follows the [Rust embedded WG's
|
||||
/// proposal](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md) and therefore
|
||||
/// provides some goodness such as [deadlock
|
||||
/// prevention](https://github.com/korken89/wg/blob/master/rfcs/0377-mutex-trait.md#design-decisions-and-compatibility).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Since the lock function takes an `&mut self` to enable deadlock-prevention, the trait is
|
||||
/// best implemented **for a reference to a container struct**, and has a usage pattern that
|
||||
/// might feel strange at first:
|
||||
///
|
||||
/// ```
|
||||
/// static MUT: Mutex<RefCell<i32>> = Mutex::new(RefCell::new(0));
|
||||
///
|
||||
/// fn foo() {
|
||||
/// let mut r = &MUT; // Note that r is mutable
|
||||
/// r.lock(|data| *data += 1);
|
||||
/// }
|
||||
/// ```
|
||||
pub trait Mutex {
|
||||
/// The type of encapsulated data.
|
||||
type Data;
|
||||
|
||||
/// Creates a critical section and grants temporary mutable access to the encapsulated data.
|
||||
fn lock<R>(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R;
|
||||
}
|
||||
}
|
||||
|
||||
/// A pseudo-lock for teaching purposes.
|
||||
///
|
||||
/// Used to introduce [interior mutability].
|
||||
///
|
||||
/// In contrast to a real Mutex implementation, does not protect against concurrent access from
|
||||
/// other cores to the contained data. This part is preserved for later lessons.
|
||||
///
|
||||
/// The lock will only be used as long as it is safe to do so, i.e. as long as the kernel is
|
||||
/// executing single-threaded, aka only running on a single core with interrupts disabled.
|
||||
///
|
||||
/// [interior mutability]: https://doc.rust-lang.org/std/cell/index.html
|
||||
pub struct NullLock<T: ?Sized> {
|
||||
data: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Code
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
unsafe impl<T: ?Sized> Sync for NullLock<T> {}
|
||||
|
||||
impl<T> NullLock<T> {
|
||||
/// Wraps `data` into a new `NullLock`.
|
||||
pub const fn new(data: T) -> Self {
|
||||
Self {
|
||||
data: UnsafeCell::new(data),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// OS Interface Code
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
impl<T> interface::Mutex for &NullLock<T> {
|
||||
type Data = T;
|
||||
|
||||
fn lock<R>(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R {
|
||||
// In a real lock, there would be code encapsulating this line that ensures that this
|
||||
// mutable reference will ever only be given out once at a time.
|
||||
let data = unsafe { &mut *self.data.get() };
|
||||
|
||||
f(data)
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
//
|
||||
// Copyright (c) 2020 Andre Richter <andre.o.richter@gmail.com>
|
||||
|
||||
//! Timer primitives.
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[path = "_arch/aarch64/time.rs"]
|
||||
mod arch_time;
|
||||
pub use arch_time::*;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Public Definitions
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/// Timekeeping interfaces.
|
||||
pub mod interface {
|
||||
use core::time::Duration;
|
||||
|
||||
/// Time management functions.
|
||||
///
|
||||
/// The `BSP` is supposed to supply one global instance.
|
||||
pub trait TimeManager {
|
||||
/// The timer's resolution.
|
||||
fn resolution(&self) -> Duration;
|
||||
|
||||
/// The uptime since power-on of the device.
|
||||
///
|
||||
/// This includes time consumed by firmware and bootloaders.
|
||||
fn uptime(&self) -> Duration;
|
||||
|
||||
/// Spin for a given duration.
|
||||
fn spin_for(&self, duration: Duration);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue