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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! Peripheral Management
//!
//! Most peripherals are implemented as memory mapped I/O (MMIO).
//! Intrinsically, this means that accessing a peripheral requires
//! dereferencing a raw pointer that points to the peripheral's memory.
//!
//! Generally, Tock peripherals are modeled by two structures, such as:
//!
//! ```rust
//! # use kernel::utilities::cells::VolatileCell;
//! # use kernel::utilities::StaticRef;
//! # struct ChipSpecificPeripheralClock {};
//! /// The MMIO Structure.
//! #[repr(C)]
//! #[allow(dead_code)]
//! pub struct PeripheralRegisters {
//!     control: VolatileCell<u32>,
//!     interrupt_mask: VolatileCell<u32>,
//! }
//!
//! /// The Tock object that holds all information for this peripheral.
//! pub struct PeripheralHardware<'a> {
//!     mmio_address: StaticRef<PeripheralRegisters>,
//!     clock: &'a ChipSpecificPeripheralClock,
//! }
//! ```
//!
//! The first structure mirrors the MMIO specification. The second structure
//! holds a pointer to the actual address in memory. It also holds other
//! information for the peripheral. Kernel traits will be implemented for this
//! peripheral hardware structure. As the peripheral cannot derefence the raw
//! MMIO pointer safely, Tock provides the PeripheralManager interface:
//!
//! ```rust
//! # use kernel::utilities::cells::VolatileCell;
//! # use kernel::utilities::peripheral_management::PeripheralManager;
//! # use kernel::utilities::StaticRef;
//! # use kernel::ErrorCode;
//! # use kernel::hil;
//! # struct PeripheralRegisters { control: VolatileCell<u32> };
//! # struct PeripheralHardware { mmio_address: StaticRef<PeripheralRegisters> };
//! impl hil::uart::Configure for PeripheralHardware {
//!     fn configure(&self, params: hil::uart::Parameters) -> Result<(), ErrorCode> {
//!         let peripheral = &PeripheralManager::new(self);
//!         peripheral.registers.control.set(0x0);
//!         //         ^^^^^^^^^-- This is type &PeripheralRegisters
//!         Ok(())
//!     }
//! }
//! # use kernel::utilities::peripheral_management::PeripheralManagement;
//! # use kernel::platform::chip::NoClockControl;
//! # impl PeripheralManagement<NoClockControl> for PeripheralHardware {
//! #     type RegisterType = PeripheralRegisters;
//!
//! #     fn get_registers(&self) -> &PeripheralRegisters {
//! #         &*self.mmio_address
//! #     }
//! #     fn get_clock(&self) -> &NoClockControl { unsafe { &kernel::platform::chip::NO_CLOCK_CONTROL } }
//! #     fn before_peripheral_access(&self, _c: &NoClockControl, _r: &Self::RegisterType) {}
//! #     fn after_peripheral_access(&self, _c: &NoClockControl, _r: &Self::RegisterType) {}
//! # }
//! ```
//!
//! Each peripheral must tell the kernel where its registers live in memory:
//!
//! ```rust
//! # use kernel::utilities::peripheral_management::PeripheralManagement;
//! # use kernel::utilities::StaticRef;
//! # pub struct PeripheralRegisters {};
//! # pub struct PeripheralHardware { mmio_address: StaticRef<PeripheralRegisters> };
//! /// Teaching the kernel how to create PeripheralRegisters.
//! use kernel::platform::chip::NoClockControl;
//! impl PeripheralManagement<NoClockControl> for PeripheralHardware {
//!     type RegisterType = PeripheralRegisters;
//!
//!     fn get_registers(&self) -> &PeripheralRegisters {
//!         &*self.mmio_address
//!     }
//!     # fn get_clock(&self) -> &NoClockControl { unsafe { &kernel::platform::chip::NO_CLOCK_CONTROL } }
//!     # fn before_peripheral_access(&self, _c: &NoClockControl, _r: &Self::RegisterType) {}
//!     # fn after_peripheral_access(&self, _c: &NoClockControl, _r: &Self::RegisterType) {}
//! }
//! ```
//!
//! Note, this example kept the `mmio_address` in the `PeripheralHardware`
//! structure, which is useful when there are multiple copies of the same
//! peripheral (e.g. multiple UARTs). For single-instance peripherals, it's
//! fine to simply return the address directly from `get_registers`.
//!
//! Peripheral Clocks
//! -----------------
//!
//! To facilitate low-power operation, PeripheralManager captures the peripheral's
//! clock upon instantiation. The intention is to exploit
//! [Ownership Based Resource Management](https://doc.rust-lang.org/beta/nomicon/obrm.html)
//! to capture peripheral power state.
//!
//! To enable this, peripherals must inform the kernel which clock they use,
//! and when the clock should be enabled and disabled. Implementations of the
//! `before/after_mmio_access` methods must take care to not access hardware
//! without enabling clocks if needed if they use hardware for bookkeeping.
//!
//! ```rust
//! use kernel::utilities::peripheral_management::PeripheralManagement;
//! use kernel::utilities::StaticRef;
//! use kernel::platform::chip::ClockInterface;
//! // A dummy clock for this example.
//! // Real peripherals that do not have clocks should use NoClockControl from this module.
//! struct ExampleClock {};
//! impl ClockInterface for ExampleClock {
//!     fn is_enabled(&self) -> bool { true }
//!     fn enable(&self) { }
//!     fn disable(&self) { }
//! }
//!
//! // Dummy hardware for this example.
//! struct SpiRegisters {};
//! struct SpiHw<'a> {
//!     mmio_address: StaticRef<SpiRegisters>,
//!     clock: &'a ExampleClock,
//!     busy: bool,
//! };
//!
//! /// Teaching the kernel which clock controls SpiHw.
//! impl<'a> PeripheralManagement<ExampleClock> for SpiHw<'a> {
//!     type RegisterType = SpiRegisters;
//!
//!     fn get_registers(&self) -> &SpiRegisters { &*self.mmio_address }
//!
//!     fn get_clock(&self) -> &ExampleClock { self.clock }
//!
//!     fn before_peripheral_access(&self, clock: &ExampleClock, _registers: &SpiRegisters) {
//!         clock.enable();
//!     }
//!
//!     fn after_peripheral_access(&self, clock: &ExampleClock, _registers: &SpiRegisters) {
//!         if !self.busy {
//!             clock.disable();
//!         }
//!     }
//! }
//! ```

use crate::platform::chip::ClockInterface;

/// A structure encapsulating a peripheral should implement this trait.
pub trait PeripheralManagement<C>
where
    C: ClockInterface,
{
    type RegisterType;

    /// How to get a reference to the physical hardware registers (the MMIO struct).
    fn get_registers(&self) -> &Self::RegisterType;

    /// Which clock feeds this peripheral.
    ///
    /// For peripherals with no clock, use `&::kernel::platform::chip::NO_CLOCK_CONTROL`.
    fn get_clock(&self) -> &C;

    /// Called before peripheral access.
    ///
    /// Responsible for ensure the periphal can be safely accessed, e.g. that
    /// its clock is powered on.
    fn before_peripheral_access(&self, _: &C, _: &Self::RegisterType);

    /// Called after periphal access.
    ///
    /// Currently used primarily for power management to check whether the
    /// peripheral can be powered off.
    fn after_peripheral_access(&self, _: &C, _: &Self::RegisterType);
}

/// Structures encapsulating peripheral hardware (those implementing the
/// PeripheralManagement trait) should instantiate an instance of this
/// method to access memory mapped registers.
///
/// ```
/// # use kernel::utilities::cells::VolatileCell;
/// # use kernel::utilities::peripheral_management::PeripheralManager;
/// # use kernel::utilities::StaticRef;
/// # pub struct PeripheralRegisters { control: VolatileCell<u32> };
/// # pub struct PeripheralHardware { mmio_address: StaticRef<PeripheralRegisters> };
/// impl PeripheralHardware {
///     fn example(&self) {
///         let peripheral = &PeripheralManager::new(self);
///         peripheral.registers.control.set(0x1);
///     }
/// }
/// # use kernel::utilities::peripheral_management::PeripheralManagement;
/// # use kernel::platform::chip::NoClockControl;
/// # impl PeripheralManagement<NoClockControl> for PeripheralHardware {
/// #     type RegisterType = PeripheralRegisters;
///
/// #     fn get_registers(&self) -> &PeripheralRegisters {
/// #         &*self.mmio_address
/// #     }
/// #     fn get_clock(&self) -> &NoClockControl { unsafe { &kernel::platform::chip::NO_CLOCK_CONTROL } }
/// #     fn before_peripheral_access(&self, _c: &NoClockControl, _r: &Self::RegisterType) {}
/// #     fn after_peripheral_access(&self, _c: &NoClockControl, _r: &Self::RegisterType) {}
/// # }
/// ```
pub struct PeripheralManager<'a, H, C>
where
    H: 'a + PeripheralManagement<C>,
    C: 'a + ClockInterface,
{
    pub registers: &'a H::RegisterType,
    peripheral_hardware: &'a H,
    clock: &'a C,
}

impl<'a, H, C> PeripheralManager<'a, H, C>
where
    H: 'a + PeripheralManagement<C>,
    C: 'a + ClockInterface,
{
    pub fn new(peripheral_hardware: &'a H) -> PeripheralManager<'a, H, C> {
        let registers = peripheral_hardware.get_registers();
        let clock = peripheral_hardware.get_clock();
        peripheral_hardware.before_peripheral_access(clock, registers);
        PeripheralManager {
            registers,
            peripheral_hardware,
            clock,
        }
    }
}

impl<'a, H, C> Drop for PeripheralManager<'a, H, C>
where
    H: 'a + PeripheralManagement<C>,
    C: 'a + ClockInterface,
{
    fn drop(&mut self) {
        self.peripheral_hardware
            .after_peripheral_access(self.clock, self.registers);
    }
}