mango-hal 0.2.1

Hardware Abstraction Layer for the mango operationg system.
Documentation
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2023 Andre Richter <andre.o.richter@gmail.com>

//! GPIO Driver.

use crate::systems::rpi::{
  bsp::device_driver::common::MMIODerefWrapper,
  driver,
  exception::asynchronous::IRQNumber,
  memory::{Address, Virtual},
  synchronization,
  synchronization::IRQSafeNullLock,
};
use tock_registers::{
  interfaces::{ReadWriteable, Writeable},
  register_bitfields, register_structs,
  registers::ReadWrite,
};

//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------

// GPIO registers.
//
// Descriptions taken from
// - https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
// - https://datasheets.raspberrypi.org/bcm2711/bcm2711-peripherals.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 Register
    ///
    /// BCM2837 only.
    GPPUD [
        /// Controls the actuation of the internal pull-up/down control line to ALL the GPIO pins.
        PUD OFFSET(0) NUMBITS(2) [
            Off = 0b00,
            PullDown = 0b01,
            PullUp = 0b10
        ]
    ],

    /// GPIO Pull-up/down Clock Register 0
    ///
    /// BCM2837 only.
    GPPUDCLK0 [
        /// Pin 15
        PUDCLK15 OFFSET(15) NUMBITS(1) [
            NoEffect = 0,
            AssertClock = 1
        ],

        /// Pin 14
        PUDCLK14 OFFSET(14) NUMBITS(1) [
            NoEffect = 0,
            AssertClock = 1
        ]
    ],

    /// GPIO Pull-up / Pull-down Register 0
    ///
    /// BCM2711 only.
    GPIO_PUP_PDN_CNTRL_REG0 [
        /// Pin 15
        GPIO_PUP_PDN_CNTRL15 OFFSET(30) NUMBITS(2) [
            NoResistor = 0b00,
            PullUp = 0b01
        ],

        /// Pin 14
        GPIO_PUP_PDN_CNTRL14 OFFSET(28) NUMBITS(2) [
            NoResistor = 0b00,
            PullUp = 0b01
        ]
    ]
}

register_structs! {
    #[allow(non_snake_case)]
    RegisterBlock {
        (0x00 => _reserved1),
        (0x04 => GPFSEL1: ReadWrite<u32, GPFSEL1::Register>),
        (0x08 => _reserved2),
        (0x94 => GPPUD: ReadWrite<u32, GPPUD::Register>),
        (0x98 => GPPUDCLK0: ReadWrite<u32, GPPUDCLK0::Register>),
        (0x9C => _reserved3),
        (0xE4 => GPIO_PUP_PDN_CNTRL_REG0: ReadWrite<u32, GPIO_PUP_PDN_CNTRL_REG0::Register>),
        (0xE8 => @END),
    }
}

/// Abstraction for the associated MMIO registers.
type Registers = MMIODerefWrapper<RegisterBlock>;

struct GPIOInner
{
  registers: Registers,
}

//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------

/// Representation of the GPIO HW.
pub struct GPIO
{
  inner: IRQSafeNullLock<GPIOInner>,
}

//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------

impl GPIOInner
{
  /// Create an instance.
  ///
  /// # Safety
  ///
  /// - The user must ensure to provide a correct MMIO start address.
  pub const unsafe fn new(mmio_start_addr: Address<Virtual>) -> Self
  {
    Self {
      registers: Registers::new(mmio_start_addr),
    }
  }

  /// Disable pull-up/down on pins 14 and 15.
  #[cfg(feature = "rpi3")]
  fn disable_pud_14_15_bcm2837(&mut self)
  {
    use crate::systems::rpi::time;
    use core::time::Duration;

    // The Linux 2837 GPIO driver waits 1 µs between the steps.
    const DELAY: Duration = Duration::from_micros(1);

    self.registers.GPPUD.write(GPPUD::PUD::Off);
    time::time_manager().spin_for(DELAY);

    self
      .registers
      .GPPUDCLK0
      .write(GPPUDCLK0::PUDCLK15::AssertClock + GPPUDCLK0::PUDCLK14::AssertClock);
    time::time_manager().spin_for(DELAY);

    self.registers.GPPUD.write(GPPUD::PUD::Off);
    self.registers.GPPUDCLK0.set(0);
  }

  /// Disable pull-up/down on pins 14 and 15.
  #[cfg(feature = "rpi4")]
  fn disable_pud_14_15_bcm2711(&mut self)
  {
    self.registers.GPIO_PUP_PDN_CNTRL_REG0.write(
      GPIO_PUP_PDN_CNTRL_REG0::GPIO_PUP_PDN_CNTRL15::PullUp
        + GPIO_PUP_PDN_CNTRL_REG0::GPIO_PUP_PDN_CNTRL14::PullUp,
    );
  }

  /// Map PL011 UART as standard output.
  ///
  /// TX to pin 14
  /// RX to pin 15
  pub fn map_pl011_uart(&mut self)
  {
    // Select the UART on pins 14 and 15.
    self
      .registers
      .GPFSEL1
      .modify(GPFSEL1::FSEL15::AltFunc0 + GPFSEL1::FSEL14::AltFunc0);

    // Disable pull-up/down on pins 14 and 15.
    #[cfg(feature = "rpi3")]
    self.disable_pud_14_15_bcm2837();

    #[cfg(feature = "rpi4")]
    self.disable_pud_14_15_bcm2711();
  }
}

//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------

impl GPIO
{
  pub const COMPATIBLE: &'static str = "BCM GPIO";

  /// Create an instance.
  ///
  /// # Safety
  ///
  /// - The user must ensure to provide a correct MMIO start address.
  pub const unsafe fn new(mmio_start_addr: Address<Virtual>) -> Self
  {
    Self {
      inner: IRQSafeNullLock::new(GPIOInner::new(mmio_start_addr)),
    }
  }

  /// Concurrency safe version of `GPIOInner.map_pl011_uart()`
  pub fn map_pl011_uart(&self)
  {
    self.inner.lock(|inner| inner.map_pl011_uart())
  }
}

//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
use synchronization::interface::Mutex;

impl driver::interface::DeviceDriver for GPIO
{
  type IRQNumberType = IRQNumber;

  fn compatible(&self) -> &'static str
  {
    Self::COMPATIBLE
  }
}