parent
68b967b48e
commit
f3d47f4b02
@ -1,13 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "bcm2837"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["equation314 <equation618@gmail.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
zero_kernel_offset = []
|
|
||||||
use_generic_timer = ["aarch64"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
volatile = "0.2.4"
|
|
||||||
aarch64= { git = "https://github.com/equation314/aarch64", optional = true }
|
|
@ -1,65 +0,0 @@
|
|||||||
use super::raw;
|
|
||||||
use core::slice;
|
|
||||||
use core::str;
|
|
||||||
|
|
||||||
/// An ATAG.
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub enum Atag {
|
|
||||||
Core(raw::Core),
|
|
||||||
Mem(raw::Mem),
|
|
||||||
Cmd(&'static str),
|
|
||||||
Unknown(u32),
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Atag {
|
|
||||||
/// Returns `Some` if this is a `Core` ATAG. Otherwise returns `None`.
|
|
||||||
pub fn core(self) -> Option<raw::Core> {
|
|
||||||
match self {
|
|
||||||
Atag::Core(x) => Some(x),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `Some` if this is a `Mem` ATAG. Otherwise returns `None`.
|
|
||||||
pub fn mem(self) -> Option<raw::Mem> {
|
|
||||||
match self {
|
|
||||||
Atag::Mem(x) => Some(x),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `Some` with the command line string if this is a `Cmd` ATAG.
|
|
||||||
/// Otherwise returns `None`.
|
|
||||||
pub fn cmd(self) -> Option<&'static str> {
|
|
||||||
match self {
|
|
||||||
Atag::Cmd(x) => Some(x),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert between raw::* types and Atag wrapper.
|
|
||||||
impl<'a> From<&'a raw::Atag> for Atag {
|
|
||||||
fn from(atag: &raw::Atag) -> Atag {
|
|
||||||
unsafe {
|
|
||||||
match (atag.tag, &atag.kind) {
|
|
||||||
(raw::Atag::CORE, &raw::Kind { core }) => Atag::Core(core),
|
|
||||||
(raw::Atag::MEM, &raw::Kind { mem }) => Atag::Mem(mem),
|
|
||||||
(raw::Atag::CMDLINE, &raw::Kind { ref cmd }) => {
|
|
||||||
let cmd_ptr: *const u8 = &cmd.cmd as *const u8;
|
|
||||||
let mut len: usize = 0;
|
|
||||||
|
|
||||||
while *cmd_ptr.add(len) != 0 {
|
|
||||||
len += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cmd_slice = slice::from_raw_parts(cmd_ptr, len);
|
|
||||||
Atag::Cmd(str::from_utf8_unchecked(cmd_slice))
|
|
||||||
}
|
|
||||||
(raw::Atag::NONE, _) => Atag::None,
|
|
||||||
(id, _) => Atag::Unknown(id),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
mod atag;
|
|
||||||
mod raw;
|
|
||||||
|
|
||||||
use super::consts::KERNEL_OFFSET;
|
|
||||||
pub use self::atag::*;
|
|
||||||
pub use self::raw::{Cmd, Core, Mem};
|
|
||||||
|
|
||||||
/// The address at which the firmware loads the ATAGS.
|
|
||||||
const ATAG_BASE: usize = KERNEL_OFFSET + 0x100;
|
|
||||||
|
|
||||||
/// An iterator over the ATAGS on this system.
|
|
||||||
pub struct Atags {
|
|
||||||
ptr: &'static raw::Atag,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Atags {
|
|
||||||
/// Returns an instance of `Atags`, an iterator over ATAGS on this system.
|
|
||||||
pub fn get() -> Atags {
|
|
||||||
Atags {
|
|
||||||
ptr: unsafe { &*(ATAG_BASE as *const raw::Atag) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for Atags {
|
|
||||||
type Item = Atag;
|
|
||||||
|
|
||||||
/// Iterate over Atags. Returns a valid Atag until the iterator hits the
|
|
||||||
/// Atag::None.
|
|
||||||
fn next(&mut self) -> Option<Atag> {
|
|
||||||
let cur = self.ptr;
|
|
||||||
match cur.next() {
|
|
||||||
Some(next) => {
|
|
||||||
let result = Some(Atag::from(cur));
|
|
||||||
self.ptr = next;
|
|
||||||
result
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
/// A raw `ATAG` as laid out in memory.
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Atag {
|
|
||||||
pub dwords: u32,
|
|
||||||
pub tag: u32,
|
|
||||||
pub kind: Kind,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Atag {
|
|
||||||
pub const NONE: u32 = 0x00000000;
|
|
||||||
pub const CORE: u32 = 0x54410001;
|
|
||||||
pub const MEM: u32 = 0x54410002;
|
|
||||||
pub const VIDEOTEXT: u32 = 0x54410003;
|
|
||||||
pub const RAMDISK: u32 = 0x54410004;
|
|
||||||
pub const INITRD2: u32 = 0x54420005;
|
|
||||||
pub const SERIAL: u32 = 0x54410006;
|
|
||||||
pub const REVISION: u32 = 0x54410007;
|
|
||||||
pub const VIDEOLFB: u32 = 0x54410008;
|
|
||||||
pub const CMDLINE: u32 = 0x54410009;
|
|
||||||
|
|
||||||
/// Returns the ATAG following `self`, if there is one.
|
|
||||||
pub fn next(&self) -> Option<&Atag> {
|
|
||||||
if self.tag == Atag::NONE {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let current = self as *const Atag as *const u32;
|
|
||||||
let next: &Atag = unsafe { &*(current.add(self.dwords as usize) as *const Atag) };
|
|
||||||
|
|
||||||
Some(next)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The possible variant of an ATAG.
|
|
||||||
#[repr(C)]
|
|
||||||
pub union Kind {
|
|
||||||
pub core: Core,
|
|
||||||
pub mem: Mem,
|
|
||||||
pub cmd: Cmd,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `CORE` ATAG.
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct Core {
|
|
||||||
pub flags: u32,
|
|
||||||
pub page_size: u32,
|
|
||||||
pub root_dev: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `MEM` ATAG.
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct Mem {
|
|
||||||
pub size: u32,
|
|
||||||
pub start: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `CMDLINE` ATAG.
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct Cmd {
|
|
||||||
/// The first byte of the command line string.
|
|
||||||
pub cmd: u8,
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
#[cfg(feature = "zero_kernel_offset")]
|
|
||||||
pub const KERNEL_OFFSET: usize = 0;
|
|
||||||
#[cfg(not(feature = "zero_kernel_offset"))]
|
|
||||||
pub const KERNEL_OFFSET: usize = 0xFFFF_0000_0000_0000;
|
|
||||||
|
|
||||||
pub const RAW_IO_BASE: usize = 0x3F00_0000;
|
|
||||||
pub const IO_BASE: usize = KERNEL_OFFSET + RAW_IO_BASE;
|
|
@ -1,163 +0,0 @@
|
|||||||
use crate::consts::IO_BASE;
|
|
||||||
use crate::timer::delay;
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
use volatile::{ReadOnly, Volatile, WriteOnly};
|
|
||||||
|
|
||||||
/// The base address of the `GPIO` registers.
|
|
||||||
const GPIO_BASE: usize = IO_BASE + 0x200000;
|
|
||||||
|
|
||||||
/// An alternative GPIO function. (ref: peripherals 6.1, page 92)
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum Function {
|
|
||||||
Input = 0b000,
|
|
||||||
Output = 0b001,
|
|
||||||
Alt0 = 0b100,
|
|
||||||
Alt1 = 0b101,
|
|
||||||
Alt2 = 0b110,
|
|
||||||
Alt3 = 0b111,
|
|
||||||
Alt4 = 0b011,
|
|
||||||
Alt5 = 0b010,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// GPIO registers starting from `GPIO_BASE` (ref: peripherals 6.1, page 90)
|
|
||||||
#[repr(C)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
struct Registers {
|
|
||||||
FSEL: [Volatile<u32>; 6],
|
|
||||||
__reserved0: u32,
|
|
||||||
SET: [WriteOnly<u32>; 2],
|
|
||||||
__reserved1: u32,
|
|
||||||
CLR: [WriteOnly<u32>; 2],
|
|
||||||
__reserved2: u32,
|
|
||||||
LEV: [ReadOnly<u32>; 2],
|
|
||||||
__reserved3: u32,
|
|
||||||
EDS: [Volatile<u32>; 2],
|
|
||||||
__reserved4: u32,
|
|
||||||
REN: [Volatile<u32>; 2],
|
|
||||||
__reserved5: u32,
|
|
||||||
FEN: [Volatile<u32>; 2],
|
|
||||||
__reserved6: u32,
|
|
||||||
HEN: [Volatile<u32>; 2],
|
|
||||||
__reserved7: u32,
|
|
||||||
LEN: [Volatile<u32>; 2],
|
|
||||||
__reserved8: u32,
|
|
||||||
AREN: [Volatile<u32>; 2],
|
|
||||||
__reserved9: u32,
|
|
||||||
AFEN: [Volatile<u32>; 2],
|
|
||||||
__reserved10: u32,
|
|
||||||
PUD: Volatile<u32>,
|
|
||||||
PUDCLK: [Volatile<u32>; 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Possible states for a GPIO pin.
|
|
||||||
pub enum Uninitialized {}
|
|
||||||
pub enum Input {}
|
|
||||||
pub enum Output {}
|
|
||||||
pub enum Alt {}
|
|
||||||
|
|
||||||
/// A GPIO pin in state `State`.
|
|
||||||
///
|
|
||||||
/// The `State` generic always corresponds to an uninstantiatable type that is
|
|
||||||
/// use solely to mark and track the state of a given GPIO pin. A `Gpio`
|
|
||||||
/// structure starts in the `Uninitialized` state and must be transitions into
|
|
||||||
/// one of `Input`, `Output`, or `Alt` via the `into_input`, `into_output`, and
|
|
||||||
/// `into_alt` methods before it can be used.
|
|
||||||
pub struct Gpio<State> {
|
|
||||||
pin: u8,
|
|
||||||
registers: &'static mut Registers,
|
|
||||||
_state: PhantomData<State>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Gpio<T> {
|
|
||||||
/// Transitions `self` to state `S`, consuming `self` and returning a new
|
|
||||||
/// `Gpio` instance in state `S`. This method should _never_ be exposed to
|
|
||||||
/// the public!
|
|
||||||
#[inline(always)]
|
|
||||||
fn transition<S>(self) -> Gpio<S> {
|
|
||||||
Gpio {
|
|
||||||
pin: self.pin,
|
|
||||||
registers: self.registers,
|
|
||||||
_state: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the Gpio pull-up/pull-down state for values in `pin_value`
|
|
||||||
/// (ref: peripherals 6.1, page 101)
|
|
||||||
pub fn set_gpio_pd(&mut self, pud_value: u8) {
|
|
||||||
let index = if self.pin >= 32 { 1 } else { 0 };
|
|
||||||
|
|
||||||
self.registers.PUD.write(pud_value as u32);
|
|
||||||
delay(150);
|
|
||||||
self.registers.PUDCLK[index as usize].write((1 << self.pin) as u32);
|
|
||||||
delay(150);
|
|
||||||
self.registers.PUD.write(0);
|
|
||||||
self.registers.PUDCLK[index as usize].write(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Gpio<Uninitialized> {
|
|
||||||
/// Returns a new `GPIO` structure for pin number `pin`.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if `pin` > `53`.
|
|
||||||
pub fn new(pin: u8) -> Gpio<Uninitialized> {
|
|
||||||
if pin > 53 {
|
|
||||||
panic!("Gpio::new(): pin {} exceeds maximum of 53", pin);
|
|
||||||
}
|
|
||||||
|
|
||||||
Gpio {
|
|
||||||
registers: unsafe { &mut *(GPIO_BASE as *mut Registers) },
|
|
||||||
pin: pin,
|
|
||||||
_state: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enables the alternative function `function` for `self`. Consumes self
|
|
||||||
/// and returns a `Gpio` structure in the `Alt` state.
|
|
||||||
pub fn into_alt(self, function: Function) -> Gpio<Alt> {
|
|
||||||
let select = (self.pin / 10) as usize;
|
|
||||||
let offset = 3 * (self.pin % 10) as usize;
|
|
||||||
self.registers.FSEL[select].update(|value| {
|
|
||||||
*value &= !(0b111 << offset);
|
|
||||||
*value |= (function as u32) << offset;
|
|
||||||
});
|
|
||||||
self.transition()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets this pin to be an _output_ pin. Consumes self and returns a `Gpio`
|
|
||||||
/// structure in the `Output` state.
|
|
||||||
pub fn into_output(self) -> Gpio<Output> {
|
|
||||||
self.into_alt(Function::Output).transition()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets this pin to be an _input_ pin. Consumes self and returns a `Gpio`
|
|
||||||
/// structure in the `Input` state.
|
|
||||||
pub fn into_input(self) -> Gpio<Input> {
|
|
||||||
self.into_alt(Function::Input).transition()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Gpio<Output> {
|
|
||||||
/// Sets (turns on) the pin.
|
|
||||||
pub fn set(&mut self) {
|
|
||||||
let index = if self.pin >= 32 { 1 } else { 0 };
|
|
||||||
self.registers.SET[index as usize].write(1 << (self.pin - index * 32));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clears (turns off) the pin.
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
let index = if self.pin >= 32 { 1 } else { 0 };
|
|
||||||
self.registers.CLR[index as usize].write(1 << (self.pin - index * 32));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Gpio<Input> {
|
|
||||||
/// Reads the pin's value. Returns `true` if the level is high and `false`
|
|
||||||
/// if the level is low.
|
|
||||||
pub fn level(&mut self) -> bool {
|
|
||||||
let index = if self.pin >= 32 { 1 } else { 0 };
|
|
||||||
let high = 1 << (self.pin - index * 32);
|
|
||||||
(self.registers.LEV[index as usize].read() & high) == high
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
use crate::consts::IO_BASE;
|
|
||||||
use volatile::{ReadOnly, Volatile};
|
|
||||||
|
|
||||||
const INT_BASE: usize = IO_BASE + 0xB000 + 0x200;
|
|
||||||
|
|
||||||
/// Allowed interrupts (ref: peripherals 7.5, page 113)
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
|
||||||
pub enum Interrupt {
|
|
||||||
Timer1 = 1,
|
|
||||||
Timer3 = 3,
|
|
||||||
Usb = 9,
|
|
||||||
Aux = 29,
|
|
||||||
Gpio0 = 49,
|
|
||||||
Gpio1 = 50,
|
|
||||||
Gpio2 = 51,
|
|
||||||
Gpio3 = 52,
|
|
||||||
Uart = 57,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Interrupts registers starting from `INT_BASE` (ref: peripherals 7.5, page 112)
|
|
||||||
#[repr(C)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
struct Registers {
|
|
||||||
IRQBasicPending: ReadOnly<u32>,
|
|
||||||
IRQPending: [ReadOnly<u32>; 2],
|
|
||||||
FIQControl: Volatile<u32>,
|
|
||||||
EnableIRQ: [Volatile<u32>; 2],
|
|
||||||
EnableBasicIRQ: Volatile<u32>,
|
|
||||||
DisableIRQ: [Volatile<u32>; 2],
|
|
||||||
DisableBasicIRQ: Volatile<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pending interrupts
|
|
||||||
pub struct PendingInterrupts(u64);
|
|
||||||
|
|
||||||
impl Iterator for PendingInterrupts {
|
|
||||||
type Item = usize;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
let int = self.0.trailing_zeros();
|
|
||||||
if int < 64 {
|
|
||||||
self.0 &= !(1 << int);
|
|
||||||
Some(int as usize)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An interrupt controller. Used to enable and disable interrupts as well as to
|
|
||||||
/// check if an interrupt is pending.
|
|
||||||
pub struct Controller {
|
|
||||||
registers: &'static mut Registers,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Controller {
|
|
||||||
/// Returns a new handle to the interrupt controller.
|
|
||||||
#[inline]
|
|
||||||
pub fn new() -> Controller {
|
|
||||||
Controller {
|
|
||||||
registers: unsafe { &mut *(INT_BASE as *mut Registers) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enables the interrupt `int`.
|
|
||||||
pub fn enable(&mut self, int: Interrupt) {
|
|
||||||
self.registers.EnableIRQ[int as usize / 32].write(1 << (int as usize) % 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Disables the interrupt `int`.
|
|
||||||
pub fn disable(&mut self, int: Interrupt) {
|
|
||||||
self.registers.DisableIRQ[int as usize / 32].write(1 << (int as usize) % 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if `int` is pending. Otherwise, returns `false`.
|
|
||||||
pub fn is_pending(&self, int: Interrupt) -> bool {
|
|
||||||
self.registers.IRQPending[int as usize / 32].read() & (1 << (int as usize) % 32) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return all pending interrupts.
|
|
||||||
pub fn pending_interrupts(&self) -> PendingInterrupts {
|
|
||||||
let irq1 = self.registers.IRQPending[0].read() as u64;
|
|
||||||
let irq2 = self.registers.IRQPending[1].read() as u64;
|
|
||||||
PendingInterrupts((irq2 << 32) | irq1)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![feature(asm)]
|
|
||||||
|
|
||||||
extern crate volatile;
|
|
||||||
|
|
||||||
pub mod atags;
|
|
||||||
pub mod consts;
|
|
||||||
pub mod gpio;
|
|
||||||
pub mod interrupt;
|
|
||||||
pub mod mailbox;
|
|
||||||
pub mod mini_uart;
|
|
||||||
pub mod timer;
|
|
@ -1,80 +0,0 @@
|
|||||||
use crate::consts::IO_BASE;
|
|
||||||
use volatile::{ReadOnly, Volatile, WriteOnly};
|
|
||||||
|
|
||||||
/// The base address for the `MU` registers.
|
|
||||||
const MAILBOX_BASE: usize = IO_BASE + 0xB000 + 0x880;
|
|
||||||
|
|
||||||
/// Available mailbox channels
|
|
||||||
///
|
|
||||||
/// (ref: https://github.com/raspberrypi/firmware/wiki/Mailboxes)
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub enum MailboxChannel {
|
|
||||||
Framebuffer = 1,
|
|
||||||
Property = 8,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read from mailbox status register (MAILx_STA).
|
|
||||||
#[repr(u32)]
|
|
||||||
enum MailboxStatus {
|
|
||||||
MailboxEmpty = 1 << 30,
|
|
||||||
MailboxFull = 1 << 31,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mailbox registers. We basically only support mailbox 0 & 1. We
|
|
||||||
/// deliver to the VC in mailbox 1, it delivers to us in mailbox 0. See
|
|
||||||
/// BCM2835-ARM-Peripherals.pdf section 1.3 for an explanation about
|
|
||||||
/// the placement of memory barriers.
|
|
||||||
///
|
|
||||||
/// (ref: https://github.com/raspberrypi/firmware/wiki/Mailboxes)
|
|
||||||
#[repr(C)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
struct Registers {
|
|
||||||
MAIL0_RD: ReadOnly<u32>, // 0x00
|
|
||||||
__reserved0: [u32; 3],
|
|
||||||
MAIL0_POL: ReadOnly<u32>, // 0x10
|
|
||||||
MAIL0_SND: ReadOnly<u32>, // 0x14
|
|
||||||
MAIL0_STA: ReadOnly<u32>, // 0x18
|
|
||||||
MAIL0_CNF: Volatile<u32>, // 0x1c
|
|
||||||
|
|
||||||
MAIL1_WRT: WriteOnly<u32>, // 0x20
|
|
||||||
__reserved1: [u32; 3],
|
|
||||||
_MAIL1_POL: ReadOnly<u32>, // 0x30
|
|
||||||
_MAIL1_SND: ReadOnly<u32>, // 0x34
|
|
||||||
MAIL1_STA: ReadOnly<u32>, // 0x38
|
|
||||||
_MAIL1_CNF: Volatile<u32>, // 0x3c
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The Raspberry Pi's mailbox.
|
|
||||||
///
|
|
||||||
/// (ref: https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes)
|
|
||||||
pub struct Mailbox {
|
|
||||||
registers: &'static mut Registers,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mailbox {
|
|
||||||
/// Returns a new instance of `Mailbox`.
|
|
||||||
#[inline]
|
|
||||||
pub fn new() -> Mailbox {
|
|
||||||
Mailbox {
|
|
||||||
registers: unsafe { &mut *(MAILBOX_BASE as *mut Registers) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read from the requested channel of mailbox 0.
|
|
||||||
pub fn read(&self, channel: MailboxChannel) -> u32 {
|
|
||||||
loop {
|
|
||||||
while self.registers.MAIL0_STA.read() & (MailboxStatus::MailboxEmpty as u32) != 0 {}
|
|
||||||
let data = self.registers.MAIL0_RD.read();
|
|
||||||
if data & 0xF == channel as u32 {
|
|
||||||
return data & !0xF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write to the requested channel of mailbox 1.
|
|
||||||
pub fn write(&mut self, channel: MailboxChannel, data: u32) {
|
|
||||||
while self.registers.MAIL1_STA.read() & (MailboxStatus::MailboxFull as u32) != 0 {}
|
|
||||||
self.registers.MAIL1_WRT.write((data & !0xF) | (channel as u32));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,133 +0,0 @@
|
|||||||
use crate::consts::IO_BASE;
|
|
||||||
use crate::gpio::{Function, Gpio};
|
|
||||||
use volatile::{ReadOnly, Volatile};
|
|
||||||
|
|
||||||
/// The `AUXENB` register from page 9 of the BCM2837 documentation.
|
|
||||||
const AUX_ENABLES: *mut Volatile<u8> = (IO_BASE + 0x215004) as *mut Volatile<u8>;
|
|
||||||
|
|
||||||
/// The base address for the `MU` registers.
|
|
||||||
const MU_REG_BASE: usize = IO_BASE + 0x215040;
|
|
||||||
|
|
||||||
/// Enum representing bit fields of the `AUX_MU_IIR_REG` register.
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum MiniUartInterruptId {
|
|
||||||
Transmit = 0b010,
|
|
||||||
Recive = 0b100,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enum representing bit fields of the `AUX_MU_LSR_REG` register.
|
|
||||||
#[repr(u8)]
|
|
||||||
enum LsrStatus {
|
|
||||||
DataReady = 1,
|
|
||||||
TxAvailable = 1 << 5,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// MU registers starting from `MU_REG_BASE` (ref: peripherals 2.1, page 8)
|
|
||||||
#[repr(C)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
struct Registers {
|
|
||||||
AUX_MU_IO_REG: Volatile<u8>,
|
|
||||||
__r0: [u8; 3],
|
|
||||||
AUX_MU_IER_REG: Volatile<u8>,
|
|
||||||
__r1: [u8; 3],
|
|
||||||
AUX_MU_IIR_REG: Volatile<u8>,
|
|
||||||
__r2: [u8; 3],
|
|
||||||
AUX_MU_LCR_REG: Volatile<u8>,
|
|
||||||
__r3: [u8; 3],
|
|
||||||
AUX_MU_MCR_REG: Volatile<u8>,
|
|
||||||
__r4: [u8; 3],
|
|
||||||
AUX_MU_LSR_REG: ReadOnly<u8>,
|
|
||||||
__r5: [u8; 3],
|
|
||||||
AUX_MU_MSR_REG: ReadOnly<u8>,
|
|
||||||
__r6: [u8; 3],
|
|
||||||
AUX_MU_SCRATCH: Volatile<u8>,
|
|
||||||
__r7: [u8; 3],
|
|
||||||
AUX_MU_CNTL_REG: Volatile<u8>,
|
|
||||||
__r8: [u8; 3],
|
|
||||||
AUX_MU_STAT_REG: ReadOnly<u32>,
|
|
||||||
AUX_MU_BAUD_REG: Volatile<u16>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The Raspberry Pi's "mini UART".
|
|
||||||
pub struct MiniUart {
|
|
||||||
registers: &'static mut Registers,
|
|
||||||
timeout: Option<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MiniUart {
|
|
||||||
/// Returns a new instance of `MiniUart`.
|
|
||||||
#[inline]
|
|
||||||
pub fn new() -> MiniUart {
|
|
||||||
let registers = unsafe { &mut *(MU_REG_BASE as *mut Registers) };
|
|
||||||
|
|
||||||
MiniUart {
|
|
||||||
registers: registers,
|
|
||||||
timeout: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initializes the mini UART by enabling it as an auxiliary peripheral,
|
|
||||||
/// setting the data size to 8 bits, setting the BAUD rate to ~115200 (baud
|
|
||||||
/// divider of 270), setting GPIO pins 14 and 15 to alternative function 5
|
|
||||||
/// (TXD1/RDXD1), and finally enabling the UART transmitter and receiver.
|
|
||||||
///
|
|
||||||
/// By default, reads will never time out. To set a read timeout, use
|
|
||||||
/// `set_read_timeout()`.
|
|
||||||
pub fn init(&mut self) {
|
|
||||||
// Enable the mini UART as an auxiliary device.
|
|
||||||
unsafe { (*AUX_ENABLES).write(1) }
|
|
||||||
|
|
||||||
Gpio::new(14).into_alt(Function::Alt5).set_gpio_pd(0);
|
|
||||||
Gpio::new(15).into_alt(Function::Alt5).set_gpio_pd(0);
|
|
||||||
|
|
||||||
self.registers.AUX_MU_CNTL_REG.write(0); // Disable auto flow control and disable receiver and transmitter (for now)
|
|
||||||
self.registers.AUX_MU_IER_REG.write(1); // Enable receive interrupts and disable transmit interrupts
|
|
||||||
self.registers.AUX_MU_LCR_REG.write(3); // Enable 8 bit mode
|
|
||||||
self.registers.AUX_MU_MCR_REG.write(0); // Set RTS line to be always high
|
|
||||||
self.registers.AUX_MU_BAUD_REG.write(270); // Set baud rate to 115200
|
|
||||||
|
|
||||||
self.registers.AUX_MU_CNTL_REG.write(3); // Finally, enable transmitter and receiver
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the read timeout to `milliseconds` milliseconds.
|
|
||||||
pub fn set_read_timeout(&mut self, milliseconds: u32) {
|
|
||||||
self.timeout = Some(milliseconds)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write the byte `byte`. This method blocks until there is space available
|
|
||||||
/// in the output FIFO.
|
|
||||||
pub fn write_byte(&mut self, byte: u8) {
|
|
||||||
while self.registers.AUX_MU_LSR_REG.read() & (LsrStatus::TxAvailable as u8) == 0 {}
|
|
||||||
self.registers.AUX_MU_IO_REG.write(byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if there is at least one byte ready to be read. If this
|
|
||||||
/// method returns `true`, a subsequent call to `read_byte` is guaranteed to
|
|
||||||
/// return immediately. This method does not block.
|
|
||||||
pub fn has_byte(&self) -> bool {
|
|
||||||
self.registers.AUX_MU_LSR_REG.read() & (LsrStatus::DataReady as u8) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Blocks until there is a byte ready to read. If a read timeout is set,
|
|
||||||
/// this method blocks for at most that amount of time. Otherwise, this
|
|
||||||
/// method blocks indefinitely until there is a byte to read.
|
|
||||||
///
|
|
||||||
/// Returns `Ok(())` if a byte is ready to read. Returns `Err(())` if the
|
|
||||||
/// timeout expired while waiting for a byte to be ready. If this method
|
|
||||||
/// returns `Ok(())`, a subsequent call to `read_byte` is guaranteed to
|
|
||||||
/// return immediately.
|
|
||||||
pub fn wait_for_byte(&self) -> Result<(), ()> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads a byte. Blocks indefinitely until a byte is ready to be read.
|
|
||||||
pub fn read_byte(&self) -> u8 {
|
|
||||||
while !self.has_byte() {}
|
|
||||||
self.registers.AUX_MU_IO_REG.read()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read `AUX_MU_IIR_REG` and determine if the interrupt `id` is pending.
|
|
||||||
pub fn interrupt_is_pending(&self, id: MiniUartInterruptId) -> bool {
|
|
||||||
self.registers.AUX_MU_IIR_REG.read() & 0b110 == id as u8
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
extern crate aarch64;
|
|
||||||
|
|
||||||
use super::BasicTimer;
|
|
||||||
use crate::consts::KERNEL_OFFSET;
|
|
||||||
use aarch64::regs::*;
|
|
||||||
use volatile::*;
|
|
||||||
|
|
||||||
/// The base address for the ARM generic timer, IRQs, mailboxes
|
|
||||||
const GEN_TIMER_REG_BASE: usize = KERNEL_OFFSET + 0x4000_0000;
|
|
||||||
|
|
||||||
/// Core interrupt sources (ref: QA7 4.10, page 16)
|
|
||||||
#[repr(u8)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
|
||||||
enum CoreInterrupt {
|
|
||||||
CNTPSIRQ = 0,
|
|
||||||
CNTPNSIRQ = 1,
|
|
||||||
CNTHPIRQ = 2,
|
|
||||||
CNTVIRQ = 3,
|
|
||||||
Mailbox0 = 4,
|
|
||||||
Mailbox1 = 5,
|
|
||||||
Mailbox2 = 6,
|
|
||||||
Mailbox3 = 7,
|
|
||||||
Gpu = 8,
|
|
||||||
Pmu = 9,
|
|
||||||
AxiOutstanding = 10,
|
|
||||||
LocalTimer = 11,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Timer, IRQs, mailboxes registers (ref: QA7 chapter 4, page 7)
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[repr(C)]
|
|
||||||
struct Registers {
|
|
||||||
CONTROL: Volatile<u32>,
|
|
||||||
_unused1: [Volatile<u32>; 8],
|
|
||||||
LOCAL_IRQ: Volatile<u32>,
|
|
||||||
_unused2: [Volatile<u32>; 3],
|
|
||||||
LOCAL_TIMER_CTL: Volatile<u32>,
|
|
||||||
LOCAL_TIMER_FLAGS: Volatile<u32>,
|
|
||||||
_unused3: Volatile<u32>,
|
|
||||||
CORE_TIMER_IRQCNTL: [Volatile<u32>; 4],
|
|
||||||
CORE_MAILBOX_IRQCNTL: [Volatile<u32>; 4],
|
|
||||||
CORE_IRQ_SRC: [Volatile<u32>; 4],
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The ARM generic timer.
|
|
||||||
pub struct GenericTimer {
|
|
||||||
registers: &'static mut Registers,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BasicTimer for GenericTimer {
|
|
||||||
fn new() -> Self {
|
|
||||||
GenericTimer {
|
|
||||||
registers: unsafe { &mut *(GEN_TIMER_REG_BASE as *mut Registers) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(&mut self) {
|
|
||||||
self.registers.CORE_TIMER_IRQCNTL[0].write(1 << (CoreInterrupt::CNTPNSIRQ as u8));
|
|
||||||
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(&self) -> u64 {
|
|
||||||
let cntfrq = CNTFRQ_EL0.get(); // 62500000
|
|
||||||
(CNTPCT_EL0.get() * 1000000 / (cntfrq as u64)) as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tick_in(&mut self, us: u32) {
|
|
||||||
let cntfrq = CNTFRQ_EL0.get(); // 62500000
|
|
||||||
CNTP_TVAL_EL0.set(((cntfrq as f64) * (us as f64) / 1000000.0) as u32);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_pending(&self) -> bool {
|
|
||||||
self.registers.CORE_IRQ_SRC[0].read() & (1 << (CoreInterrupt::CNTPNSIRQ as u8)) != 0
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
#[cfg(feature = "use_generic_timer")]
|
|
||||||
mod generic_timer;
|
|
||||||
#[cfg(feature = "use_generic_timer")]
|
|
||||||
pub use self::generic_timer::GenericTimer as Timer;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "use_generic_timer"))]
|
|
||||||
mod system_timer;
|
|
||||||
#[cfg(not(feature = "use_generic_timer"))]
|
|
||||||
pub use self::system_timer::SystemTimer as Timer;
|
|
||||||
|
|
||||||
/// The Raspberry Pi timer.
|
|
||||||
pub trait BasicTimer {
|
|
||||||
/// Returns a new instance.
|
|
||||||
fn new() -> Self;
|
|
||||||
|
|
||||||
/// Initialization timer.
|
|
||||||
fn init(&mut self);
|
|
||||||
|
|
||||||
/// Reads the timer's counter and returns the 64-bit counter value.
|
|
||||||
/// The returned value is the number of elapsed microseconds.
|
|
||||||
fn read(&self) -> u64;
|
|
||||||
|
|
||||||
/// Sets up a match in timer 1 to occur `us` microseconds from now. If
|
|
||||||
/// interrupts for timer 1 are enabled and IRQs are unmasked, then a timer
|
|
||||||
/// interrupt will be issued in `us` microseconds.
|
|
||||||
fn tick_in(&mut self, us: u32);
|
|
||||||
|
|
||||||
/// Returns `true` if timer interruption is pending. Otherwise, returns `false`.
|
|
||||||
fn is_pending(&self) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// wait for `cycle` CPU cycles
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn delay(cycle: u32) {
|
|
||||||
for _ in 0..cycle {
|
|
||||||
unsafe { asm!("nop") }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
use super::BasicTimer;
|
|
||||||
use crate::consts::IO_BASE;
|
|
||||||
use crate::interrupt::{Controller, Interrupt};
|
|
||||||
use volatile::{ReadOnly, Volatile};
|
|
||||||
|
|
||||||
/// The base address for the ARM system timer registers.
|
|
||||||
const TIMER_REG_BASE: usize = IO_BASE + 0x3000;
|
|
||||||
|
|
||||||
/// System timer registers (ref: peripherals 12.1, page 172)
|
|
||||||
#[repr(C)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
struct Registers {
|
|
||||||
CS: Volatile<u32>,
|
|
||||||
CLO: ReadOnly<u32>,
|
|
||||||
CHI: ReadOnly<u32>,
|
|
||||||
COMPARE: [Volatile<u32>; 4],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
|
||||||
enum SystemTimerId {
|
|
||||||
Timer0 = 0,
|
|
||||||
Timer1 = 1,
|
|
||||||
Timer2 = 2,
|
|
||||||
Timer3 = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The Raspberry Pi ARM system timer.
|
|
||||||
pub struct SystemTimer {
|
|
||||||
registers: &'static mut Registers,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BasicTimer for SystemTimer {
|
|
||||||
fn new() -> Self {
|
|
||||||
SystemTimer {
|
|
||||||
registers: unsafe { &mut *(TIMER_REG_BASE as *mut Registers) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(&mut self) {
|
|
||||||
Controller::new().enable(Interrupt::Timer1);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(&self) -> u64 {
|
|
||||||
let low = self.registers.CLO.read();
|
|
||||||
let high = self.registers.CHI.read();
|
|
||||||
((high as u64) << 32) | (low as u64)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tick_in(&mut self, us: u32) {
|
|
||||||
let current_low = self.registers.CLO.read();
|
|
||||||
let compare = current_low.wrapping_add(us);
|
|
||||||
self.registers.COMPARE[SystemTimerId::Timer1 as usize].write(compare);
|
|
||||||
self.registers.CS.write(1 << (SystemTimerId::Timer1 as usize)); // unmask
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_pending(&self) -> bool {
|
|
||||||
let controller = Controller::new();
|
|
||||||
controller.is_pending(Interrupt::Timer1)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in new issue