sam4l/
gloc.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! Implementation of the SAM4L glue logic controller (GLOC).
//!
//! GLOC input and output pins must be selected appropriately from table 3-1 in
//! the SAM4l manual.

use crate::pm::{self, Clock, PBAClock};
use crate::scif::{self, ClockSource, GenericClock};
use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
use kernel::utilities::registers::{register_bitfields, ReadWrite};
use kernel::utilities::StaticRef;

#[repr(C)]
pub struct GlocRegisters {
    cr: ReadWrite<u32, Control::Register>,
    truth: ReadWrite<u32, Truth::Register>,
}

register_bitfields![u32,
    Control [
        /// Filter Enable
        FILTEN OFFSET(31) NUMBITS(1) [
            NoGlitchFilter = 0,
            GlitchFilter = 1
        ],
        /// Enable IN Inputs
        AEN OFFSET(0) NUMBITS(4) []
    ],

    Truth [
        /// Truth table values
        TRUTH OFFSET(0) NUMBITS(16) []
    ]
];

/// The GLOC's base addresses in memory (Section 7.1 of manual).
const GLOC_BASE_ADDR: usize = 0x40060000;

/// The number of bytes between each memory mapped GLOC LUT (Section 36.7).
const GLOC_LUT_SIZE: usize = 0x8;

/// Bitmasks for selecting the four GLOC inputs.
pub const IN_0_4: u8 = 0b0001; // IN0/IN4
pub const IN_1_5: u8 = 0b0010; // IN1/IN5
pub const IN_2_6: u8 = 0b0100; // IN2/IN6
pub const IN_3_7: u8 = 0b1000; // IN3/IN7

/// Available look up tables.
pub enum Lut {
    Lut0 = 0,
    Lut1 = 1,
}

pub struct Gloc {
    lut_regs: [StaticRef<GlocRegisters>; 2],
}

impl Gloc {
    pub const fn new() -> Self {
        Self {
            lut_regs: [get_lut_reg(Lut::Lut0), get_lut_reg(Lut::Lut1)],
        }
    }
}

/// Gets the memory location of the memory-mapped registers of a LUT.
const fn get_lut_reg(lut: Lut) -> StaticRef<GlocRegisters> {
    unsafe {
        StaticRef::new((GLOC_BASE_ADDR + (lut as usize) * GLOC_LUT_SIZE) as *const GlocRegisters)
    }
}

impl Gloc {
    /// Enables the GLOC by enabling its clock.
    pub fn enable(&self) {
        pm::enable_clock(Clock::PBA(PBAClock::GLOC));
    }

    /// Disables the GLOC by resetting the registers and disabling the clocks.
    pub fn disable(&self) {
        self.disable_lut(Lut::Lut0);
        self.disable_lut(Lut::Lut1);
        scif::generic_clock_disable(GenericClock::GCLK5);
        pm::disable_clock(Clock::PBA(PBAClock::GLOC));
    }

    /// Gets the memory-mapped registers associated with a LUT.
    fn lut_registers(&self, lut: Lut) -> &GlocRegisters {
        &self.lut_regs[lut as usize]
    }

    /// Set the truth table values.
    pub fn configure_lut(&self, lut: Lut, config: u16) {
        let registers = self.lut_registers(lut);
        registers.truth.write(Truth::TRUTH.val(config as u32));
    }

    /// Enable selected LUT inputs.
    pub fn enable_lut_inputs(&self, lut: Lut, inputs: u8) {
        let registers = self.lut_registers(lut);
        let aen: u32 = registers.cr.read(Control::AEN) | (inputs as u32);
        registers.cr.modify(Control::AEN.val(aen));
    }

    /// Disable selected LUT inputs.
    pub fn disable_lut_inputs(&self, lut: Lut, inputs: u8) {
        let registers = self.lut_registers(lut);
        let aen: u32 = registers.cr.read(Control::AEN) & !(inputs as u32);
        registers.cr.modify(Control::AEN.val(aen));
    }

    /// Disable LUT by resetting registers.
    pub fn disable_lut(&self, lut: Lut) {
        let registers = self.lut_registers(lut);
        registers.truth.write(Truth::TRUTH.val(0));
        registers.cr.modify(Control::AEN.val(0));
    }

    /// Enable filter on output to prevent glitches.  This will delay the given
    /// LUT's output by 3-4 GCLK cycles.
    pub fn enable_lut_filter(&self, lut: Lut) {
        scif::generic_clock_enable(GenericClock::GCLK5, ClockSource::CLK_CPU);
        let registers = self.lut_registers(lut);
        registers.cr.modify(Control::FILTEN::GlitchFilter);
    }

    /// Disable output filter.
    pub fn disable_lut_filter(&self, lut: Lut) {
        let registers = self.lut_registers(lut);
        registers.cr.modify(Control::FILTEN::NoGlitchFilter);
    }
}