vesper/machine/src/platform/raspberrypi/device_driver/bcm/gpio.rs

441 lines
13 KiB
Rust

/*
* SPDX-License-Identifier: MIT OR BlueOak-1.0.0
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
* Original code distributed under MIT, additional changes are under BlueOak-1.0.0
*/
use {
crate::{
memory::{Address, Virtual},
platform::{
device_driver::{common::MMIODerefWrapper, IRQNumber},
BcmHost,
},
synchronization::{interface::Mutex, IRQSafeNullLock},
time,
},
core::{marker::PhantomData, time::Duration},
tock_registers::{
fields::FieldValue,
interfaces::{ReadWriteable, Readable, Writeable},
register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
},
};
// Descriptions taken from
// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
/// Generates `pub enums` with no variants for each `ident` passed in.
macro states($($name:ident),*) {
$(pub enum $name { })*
}
// Possible states for a GPIO pin.
states! {
Uninitialized, Input, Output, Alt
}
#[cfg(feature = "rpi3")]
register_structs! {
/// The offsets for each register.
/// From <https://wiki.osdev.org/Raspberry_Pi_Bare_Bones> and
/// <https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf>
#[allow(non_snake_case)]
RegisterBlock {
(0x00 => pub FunctionSelect: [ReadWrite<u32>; 6]), // function select
(0x18 => __reserved_1),
(0x1c => pub SetPin: [WriteOnly<u32>; 2]), // set output pin
(0x24 => __reserved_2),
(0x28 => pub ClearPin: [WriteOnly<u32>; 2]), // clear output pin
(0x30 => __reserved_3),
(0x34 => pub PinLevel: [ReadOnly<u32>; 2]), // get input pin level
(0x3c => __reserved_4),
// Everything below is unused atm!
// (0x40 => pub EDS: [ReadWrite<u32>; 2]),
// (0x48 => __reserved_5),
// (0x4c => pub REN: [ReadWrite<u32>; 2]),
// (0x54 => __reserved_6),
// (0x58 => pub FEN: [ReadWrite<u32>; 2]),
// (0x60 => __reserved_7),
// (0x64 => pub HEN: [ReadWrite<u32>; 2]),
// (0x6c => __reserved_8),
// (0x70 => pub LEN: [ReadWrite<u32>; 2]),
// (0x78 => __reserved_9),
// (0x7c => pub AREN: [ReadWrite<u32>; 2]),
// (0x84 => __reserved_10),
// (0x88 => pub AFEN: [ReadWrite<u32>; 2]),
// (0x90 => __reserved_11),
(0x94 => pub PullUpDown: ReadWrite<u32>),
(0x98 => pub PullUpDownEnableClock: [ReadWrite<u32>; 2]),
(0xa0 => @END),
}
}
#[cfg(feature = "rpi4")]
register_structs! {
/// The offsets for each register.
/// From <https://wiki.osdev.org/Raspberry_Pi_Bare_Bones> and
/// <https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf>
#[allow(non_snake_case)]
RegisterBlock {
(0x00 => pub FunctionSelect: [ReadWrite<u32>; 6]), // function select
(0x18 => __reserved_1),
(0x1c => pub SetPin: [WriteOnly<u32>; 2]), // set output pin
(0x24 => __reserved_2),
(0x28 => pub ClearPin: [WriteOnly<u32>; 2]), // clear output pin
(0x30 => __reserved_3),
(0x34 => pub PinLevel: [ReadOnly<u32>; 2]), // get input pin level
(0x3c => __reserved_4),
(0xe4 => PullUpDownControl: [ReadWrite<u32>; 4]),
(0xf4 => @END),
}
}
// Hide RegisterBlock from public api.
type Registers = MMIODerefWrapper<RegisterBlock>;
struct GPIOInner {
registers: Registers,
}
/// Public interface to the GPIO MMIO area
pub struct GPIO {
inner: IRQSafeNullLock<GPIOInner>,
}
impl GPIOInner {
pub const unsafe fn new(mmio_base_addr: Address<Virtual>) -> Self {
Self {
registers: Registers::new(mmio_base_addr),
}
}
#[cfg(feature = "rpi3")]
pub fn power_off(&self) {
// power off gpio pins (but not VCC pins)
for bank in 0..5 {
self.registers.FunctionSelect[bank].set(0);
}
self.registers.PullUpDown.set(0);
// The Linux 2837 GPIO driver waits 1 µs between the steps.
const DELAY: Duration = Duration::from_micros(1);
time::time_manager().spin_for(DELAY);
self.registers.PullUpDownEnableClock[0].set(0xffff_ffff);
self.registers.PullUpDownEnableClock[1].set(0xffff_ffff);
time::time_manager().spin_for(DELAY);
// flush GPIO setup
self.registers.PullUpDownEnableClock[0].set(0);
self.registers.PullUpDownEnableClock[1].set(0);
}
#[cfg(feature = "rpi4")]
pub fn power_off(&self) {
todo!()
}
#[cfg(feature = "rpi3")]
pub fn set_pull_up_down(&self, pin: usize, pull: PullUpDown) {
let bank = pin / 32;
let off = pin % 32;
self.registers.PullUpDown.set(0);
// The Linux 2837 GPIO driver waits 1 µs between the steps.
const DELAY: Duration = Duration::from_micros(1);
time::time_manager().spin_for(DELAY);
self.registers.PullUpDownEnableClock[bank].modify(FieldValue::<u32, ()>::new(
0b1,
off,
(pull == PullUpDown::Up).into(),
));
time::time_manager().spin_for(DELAY);
self.registers.PullUpDown.set(0);
self.registers.PullUpDownEnableClock[bank].set(0);
}
#[cfg(feature = "rpi4")]
pub fn set_pull_up_down(&self, pin: usize, pull: PullUpDown) {
let bank = pin / 16;
let off = pin % 16;
self.registers.PullUpDownControl[bank].modify(FieldValue::<u32, ()>::new(
0b11,
off * 2,
pull.into(),
));
}
pub fn to_alt(&self, pin: usize, function: Function) {
let bank = pin / 10;
let off = pin % 10;
self.registers.FunctionSelect[bank].modify(FieldValue::<u32, ()>::new(
0b111,
off * 3,
function.into(),
));
}
pub fn set_pin(&mut self, pin: usize) {
// Guarantees: pin number is between [0; 53] by construction.
let bank = pin / 32;
let shift = pin % 32;
self.registers.SetPin[bank].set(1 << shift);
}
pub fn clear_pin(&mut self, pin: usize) {
// Guarantees: pin number is between [0; 53] by construction.
let bank = pin / 32;
let shift = pin % 32;
self.registers.ClearPin[bank].set(1 << shift);
}
pub fn get_level(&self, pin: usize) -> Level {
// Guarantees: pin number is between [0; 53] by construction.
let bank = pin / 32;
let off = pin % 32;
self.registers.PinLevel[bank].matches_all(FieldValue::<u32, ()>::new(1, off, 1))
}
}
impl GPIO {
pub const COMPATIBLE: &'static str = "BCM GPIO";
/// # Safety
///
/// Unsafe, duh!
pub const unsafe fn new(mmio_base_addr: Address<Virtual>) -> Self {
Self {
inner: IRQSafeNullLock::new(GPIOInner::new(mmio_base_addr)),
}
}
pub fn get_pin(&self, pin: usize) -> Pin<Uninitialized> {
unsafe { Pin::new(pin, &self.inner) } // todo: expose only inner to avoid unlocked access
}
pub fn power_off(&self) {
self.inner.lock(|inner| inner.power_off());
}
}
/// An alternative GPIO function.
#[repr(u8)]
pub enum Function {
Input = 0b000,
Output = 0b001,
Alt0 = 0b100,
Alt1 = 0b101,
Alt2 = 0b110,
Alt3 = 0b111,
Alt4 = 0b011,
Alt5 = 0b010,
}
impl ::core::convert::From<Function> for u32 {
fn from(f: Function) -> Self {
f as u32
}
}
/// Pull up/down resistor setup.
#[repr(u8)]
#[derive(PartialEq, Eq)]
pub enum PullUpDown {
None = 0b00,
Up = 0b01,
Down = 0b10,
}
impl ::core::convert::From<PullUpDown> for u32 {
fn from(p: PullUpDown) -> Self {
p as u32
}
}
/// A GPIO pin in state `State`.
///
/// The `State` generic always corresponds to an un-instantiable type that is
/// used solely to mark and track the state of a given GPIO pin. A `Pin`
/// structure starts in the `Uninitialized` state and must be transitioned into
/// one of `Input`, `Output`, or `Alt` via the `into_input`, `into_output`, and
/// `into_alt` methods before it can be used.
pub struct Pin<'outer, State> {
pin: usize,
inner: &'outer IRQSafeNullLock<GPIOInner>,
_state: PhantomData<State>,
}
impl<'outer, State> Pin<'outer, State> {
/// Transitions `self` to state `NewState`, consuming `self` and returning a new
/// `Pin` instance in state `NewState`. This method should _never_ be exposed to
/// the public!
#[inline(always)]
fn transition<NewState>(self) -> Pin<'outer, NewState> {
Pin {
pin: self.pin,
inner: self.inner,
_state: PhantomData,
}
}
pub fn set_pull_up_down(&self, pull: PullUpDown) {
self.inner
.lock(|inner| inner.set_pull_up_down(self.pin, pull))
}
}
impl<'outer> Pin<'outer, Uninitialized> {
/// Returns a new GPIO `Pin` structure for pin number `pin`.
///
/// # Panics
///
/// Panics if `pin` > `53`.
unsafe fn new(
pin: usize,
inner: &'outer IRQSafeNullLock<GPIOInner>,
) -> Pin<'outer, Uninitialized> {
if pin > 53 {
panic!("gpio::Pin::new(): pin {pin} exceeds maximum of 53");
}
Pin {
inner,
pin,
_state: PhantomData,
}
}
/// Enables the alternative function `function` for `self`. Consumes self
/// and returns a `Pin` structure in the `Alt` state.
pub fn into_alt(self, function: Function) -> Pin<'outer, Alt> {
self.inner.lock(|inner| inner.to_alt(self.pin, function));
self.transition()
}
/// Sets this pin to be an _output_ pin. Consumes self and returns a `Pin`
/// structure in the `Output` state.
pub fn into_output(self) -> Pin<'outer, Output> {
self.into_alt(Function::Output).transition()
}
/// Sets this pin to be an _input_ pin. Consumes self and returns a `Pin`
/// structure in the `Input` state.
pub fn into_input(self) -> Pin<'outer, Input> {
self.into_alt(Function::Input).transition()
}
}
impl<'outer> Pin<'outer, Output> {
/// Sets (turns on) this pin.
pub fn set(&mut self) {
self.inner.lock(|inner| inner.set_pin(self.pin));
}
/// Clears (turns off) this pin.
pub fn clear(&mut self) {
self.inner.lock(|inner| inner.clear_pin(self.pin));
}
}
pub type Level = bool;
impl<'outer> Pin<'outer, Input> {
/// Reads the pin's value. Returns `true` if the level is high and `false`
/// if the level is low.
pub fn level(&self) -> Level {
self.inner.lock(|inner| inner.get_level(self.pin))
}
}
//--------------------------------------------------------------------------------------------------
// OS Interface Code
//--------------------------------------------------------------------------------------------------
impl crate::drivers::interface::DeviceDriver for GPIO {
type IRQNumberType = IRQNumber;
fn compatible(&self) -> &'static str {
Self::COMPATIBLE
}
}
//--------------------------------------------------------------------------------------------------
// Testing
//--------------------------------------------------------------------------------------------------
#[cfg(test)]
mod tests {
use super::*;
#[test_case]
fn test_pin_transitions() {
let mut reg = [0u32; 40];
let mmio_base_addr = Address::<Virtual>::new(&mut reg as *mut _ as usize);
let gpio = unsafe { GPIO::new(mmio_base_addr) };
let _out = gpio.get_pin(1).into_output();
assert_eq!(reg[0], 0b001_000);
let _inp = gpio.get_pin(12).into_input();
assert_eq!(reg[1], 0b000_000_000);
let _alt = gpio.get_pin(35).into_alt(Function::Alt1);
assert_eq!(reg[3], 0b101_000_000_000_000_000);
}
#[test_case]
fn test_pin_outputs() {
let mut reg = [0u32; 40];
let mmio_base_addr = Address::<Virtual>::new(&mut reg as *mut _ as usize);
let gpio = unsafe { GPIO::new(mmio_base_addr) };
let pin = gpio.get_pin(1);
let mut out = pin.into_output();
out.set();
assert_eq!(reg[7], 0b10); // SET pin 1 = 1 << 1
out.clear();
assert_eq!(reg[10], 0b10); // CLR pin 1 = 1 << 1
let pin = gpio.get_pin(35);
let mut out = pin.into_output();
out.set();
assert_eq!(reg[8], 0b1000); // SET pin 35 = 1 << (35 - 32)
out.clear();
assert_eq!(reg[11], 0b1000); // CLR pin 35 = 1 << (35 - 32)
}
#[test_case]
fn test_pin_inputs() {
let mut reg = [0u32; 40];
let mmio_base_addr = Address::<Virtual>::new(&mut reg as *mut _ as usize);
let gpio = unsafe { GPIO::new(mmio_base_addr) };
let pin = gpio.get_pin(1);
let inp = pin.into_input();
assert_eq!(inp.level(), false);
reg[13] = 0b10;
assert_eq!(inp.level(), true);
let pin = gpio.get_pin(35);
let inp = pin.into_input();
assert_eq!(inp.level(), false);
reg[14] = 0b1000;
assert_eq!(inp.level(), true);
}
}