rustberry 0.1.0

All-purpose Raspberry Pi library for Rust
Documentation
// Copyright (C) 2018  Adam Gausmann
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

//! Frontend for the Clock Manager register map.

use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
use std::thread;
use std::time::Duration;

use error::{Error, ErrorKind};
use peripherals::consts::*;
use system::System;
use util::mem::RegisterMap;

static RESERVED: AtomicBool = ATOMIC_BOOL_INIT; //false

/// Low-level (but safe) access to the Clock Manager register segment.
pub struct ClockManager {
    registers: RegisterMap,
}

impl ClockManager {

    /// Attempts to reserve the Clock Manager register segment, returning it
    /// upon success.
    ///
    /// # Errors
    ///
    /// This struct is a singleton object to enforce single ownership of
    /// clocks. Returns `Error::Reserved` if another object exists.
    pub fn new() -> Result<ClockManager, Error> {
        if RESERVED.fetch_or(true, Ordering::SeqCst) {
            return Err(Error::new(ErrorKind::Reserved));
        }

        Ok(ClockManager {
            registers: unsafe { RegisterMap::map(
                System::detect()?.peripheral_offset() + CM_PAGE_OFFSET,
                PAGE_SIZE,
            )}?,
        })
    }

    /// Sets the source for the given clock, the most commonly used value
    /// being `Oscillator`.
    pub fn set_source(&mut self, clock: Clock, source: ClockSource) {
        assert!(!self.is_busy(clock));

        let mut reg = self.registers.load(CM_CTL[clock.id()]);
        reg &= !CM_CTL_SRC_MASK;
        reg |= source.value() | CM_CTL_PASSWD;
        self.registers.store(CM_CTL[clock.id()], reg);
    }

    /// Enables output for this clock by setting `ENAB` in the `CM_xxxCTL`
    /// register.
    pub fn enable(&mut self, clock: Clock) {
        let mut reg = self.registers.load(CM_CTL[clock.id()]);
        reg |= CM_CTL_ENAB | CM_CTL_PASSWD;
        self.registers.store(CM_CTL[clock.id()], reg);
    }

    /// Disables output for this clock by clearing `ENAB` in the `CM_xxxCTL`
    /// register.
    pub fn disable(&mut self, clock: Clock) {
        let mut reg = self.registers.load(CM_CTL[clock.id()]);
        reg &= !CM_CTL_ENAB;
        reg |= CM_CTL_PASSWD;
        self.registers.store(CM_CTL[clock.id()], reg);

        thread::sleep(Duration::from_micros(110));
        while self.is_busy(clock) {
            thread::sleep(Duration::from_micros(1));
        }
    }

    /// Immediately kills this clock by setting `KILL` in the `CM_xxxCTL`
    /// register. This should only be used for testing since  it can cause
    /// output glitches.
    pub fn kill(&mut self, clock: Clock) {
        let mut reg = self.registers.load(CM_CTL[clock.id()]);
        reg |= CM_CTL_KILL | CM_CTL_PASSWD;
        self.registers.store(CM_CTL[clock.id()], reg);
    }

    /// Returns `true` if the clock is currently outputting a cycle, according
    /// to the `BUSY` field in `CM_xxxCTL`.
    pub fn is_busy(&self, clock: Clock) -> bool {
        let reg = self.registers.load(CM_CTL[clock.id()]);
        reg & CM_CTL_BUSY != 0
    }

    /// Inverts the clock output by setting `FLIP` in the `CM_xxxCTL` register.
    /// This may be called while the clock is busy, but it will cause glitches
    /// if it is.
    pub fn enable_flip(&mut self, clock: Clock) {
        let mut reg = self.registers.load(CM_CTL[clock.id()]);
        reg |= CM_CTL_FLIP | CM_CTL_PASSWD;
        self.registers.store(CM_CTL[clock.id()], reg);
    }

    /// Disables inversion of the clock output by clearing `FLIP` in the
    /// `CM_xxxCTL` register. This may be called while the clock is busy, but
    /// it will cause glitches if it is.
    pub fn disable_flip(&mut self, clock: Clock) {
        let mut reg = self.registers.load(CM_CTL[clock.id()]);
        reg &= !CM_CTL_FLIP;
        reg |= CM_CTL_PASSWD;
        self.registers.store(CM_CTL[clock.id()], reg);
    }

    /// Sets the MASH divider level by assigning the value to `MASH` in the
    /// `CM_xxxCTL` register. Valid values are 0,1,2,3 corresponding to the
    /// number of stages.
    ///
    /// # Panics
    ///
    /// This function will panic if called while the clock is busy because such
    /// a state is likely to cause lock-ups and/or glitches more severe than
    /// the modification of the other controls. The caller should ensure the
    /// clock is disabled and wait for it to finish before setting this field.
    pub fn set_mash(&mut self, clock: Clock, value: u32) {
        assert!(!self.is_busy(clock));
        let value = (value << CM_CTL_MASH_SHIFT) & CM_CTL_MASH_MASK;

        let mut reg = self.registers.load(CM_CTL[clock.id()]);
        reg &= !CM_CTL_MASH_MASK;
        reg |= value | CM_CTL_PASSWD;
        self.registers.store(CM_CTL[clock.id()], reg);
    }

    /// Sets the base frequency divider for this clock by assigning it to the
    /// `CM_xxxDIV` register. Divider values are unsigned, fixed-point decimal,
    /// with both the integer `int` and fractional `frac` part being truncated
    /// to 12 bits.
    pub fn set_divider(&mut self, clock: Clock, int: u32, frac: u32) {
        assert!(!self.is_busy(clock));
        let int = (int << CM_DIV_DIVI_SHIFT) & CM_DIV_DIVI_MASK;
        let frac = (frac << CM_DIV_DIVF_SHIFT) & CM_DIV_DIVF_MASK;

        let reg = int | frac | CM_DIV_PASSWD;
        self.registers.store(CM_DIV[clock.id()], reg);
    }
}

/// The clocks that are supported.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[allow(missing_docs)]
pub enum Clock {
    Gp0,
    Gp1,
    Gp2,
    Pwm,
}

impl Clock {
    /// Maps the variant to the ID used internally by the constant tables.
    fn id(self) -> usize {
        match self {
            Clock::Gp0 => CM_CLOCK_GP0,
            Clock::Gp1 => CM_CLOCK_GP1,
            Clock::Gp2 => CM_CLOCK_GP2,
            Clock::Pwm => CM_CLOCK_PWM,
        }
    }
}

/// Supported sources for the clock generators. Default is `Oscillator`.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum ClockSource {
    Gnd,
    Oscillator,
    PllaPer,
    PllcPer,
    PlldPer,
    HdmiAux,
}

impl ClockSource {
    /// The numeric representation used internally.
    fn value(self) -> u32 {
        match self {
            ClockSource::Gnd        => CM_CTL_SRC_GND,
            ClockSource::Oscillator => CM_CTL_SRC_OSC,
            ClockSource::PllaPer    => CM_CTL_SRC_PLLA_PER,
            ClockSource::PllcPer    => CM_CTL_SRC_PLLC_PER,
            ClockSource::PlldPer    => CM_CTL_SRC_PLLD_PER,
            ClockSource::HdmiAux    => CM_CTL_SRC_HDMI_AUX,
        }
    }
}