kernel/utilities/peripheral_management.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Peripheral Management
6//!
7//! Most peripherals are implemented as memory mapped I/O (MMIO). Intrinsically,
8//! this means that accessing a peripheral requires dereferencing a raw pointer
9//! that points to the peripheral's memory.
10//!
11//! Generally, Tock peripherals are modeled by two structures, such as:
12//!
13//! ```rust
14//! # use kernel::utilities::cells::VolatileCell;
15//! # use kernel::utilities::StaticRef;
16//! # struct ChipSpecificPeripheralClock {};
17//! /// The MMIO Structure.
18//! #[repr(C)]
19//! #[allow(dead_code)]
20//! pub struct PeripheralRegisters {
21//! control: VolatileCell<u32>,
22//! interrupt_mask: VolatileCell<u32>,
23//! }
24//!
25//! /// The Tock object that holds all information for this peripheral.
26//! pub struct PeripheralHardware<'a> {
27//! mmio_address: StaticRef<PeripheralRegisters>,
28//! clock: &'a ChipSpecificPeripheralClock,
29//! }
30//! ```
31//!
32//! The first structure mirrors the MMIO specification. The second structure
33//! holds a pointer to the actual address in memory. It also holds other
34//! information for the peripheral. Kernel traits will be implemented for this
35//! peripheral hardware structure. As the peripheral cannot derefence the raw
36//! MMIO pointer safely, Tock provides the PeripheralManager interface:
37//!
38//! ```rust
39//! # use kernel::utilities::cells::VolatileCell;
40//! # use kernel::utilities::peripheral_management::PeripheralManager;
41//! # use kernel::utilities::StaticRef;
42//! # use kernel::ErrorCode;
43//! # use kernel::hil;
44//! # struct PeripheralRegisters { control: VolatileCell<u32> };
45//! # struct PeripheralHardware { mmio_address: StaticRef<PeripheralRegisters> };
46//! impl hil::uart::Configure for PeripheralHardware {
47//! fn configure(&self, params: hil::uart::Parameters) -> Result<(), ErrorCode> {
48//! let peripheral = &PeripheralManager::new(self);
49//! peripheral.registers.control.set(0x0);
50//! // ^^^^^^^^^-- This is type &PeripheralRegisters
51//! Ok(())
52//! }
53//! }
54//! # use kernel::utilities::peripheral_management::PeripheralManagement;
55//! # use kernel::platform::chip::NoClockControl;
56//! # impl PeripheralManagement<NoClockControl> for PeripheralHardware {
57//! # type RegisterType = PeripheralRegisters;
58//!
59//! # fn get_registers(&self) -> &PeripheralRegisters {
60//! # &*self.mmio_address
61//! # }
62//! # fn get_clock(&self) -> &NoClockControl { unsafe { &kernel::platform::chip::NO_CLOCK_CONTROL } }
63//! # fn before_peripheral_access(&self, _c: &NoClockControl, _r: &Self::RegisterType) {}
64//! # fn after_peripheral_access(&self, _c: &NoClockControl, _r: &Self::RegisterType) {}
65//! # }
66//! ```
67//!
68//! Each peripheral must tell the kernel where its registers live in memory:
69//!
70//! ```rust
71//! # use kernel::utilities::peripheral_management::PeripheralManagement;
72//! # use kernel::utilities::StaticRef;
73//! # pub struct PeripheralRegisters {};
74//! # pub struct PeripheralHardware { mmio_address: StaticRef<PeripheralRegisters> };
75//! /// Teaching the kernel how to create PeripheralRegisters.
76//! use kernel::platform::chip::NoClockControl;
77//! impl PeripheralManagement<NoClockControl> for PeripheralHardware {
78//! type RegisterType = PeripheralRegisters;
79//!
80//! fn get_registers(&self) -> &PeripheralRegisters {
81//! &*self.mmio_address
82//! }
83//! # fn get_clock(&self) -> &NoClockControl { unsafe { &kernel::platform::chip::NO_CLOCK_CONTROL } }
84//! # fn before_peripheral_access(&self, _c: &NoClockControl, _r: &Self::RegisterType) {}
85//! # fn after_peripheral_access(&self, _c: &NoClockControl, _r: &Self::RegisterType) {}
86//! }
87//! ```
88//!
89//! Note, this example kept the `mmio_address` in the `PeripheralHardware`
90//! structure, which is useful when there are multiple copies of the same
91//! peripheral (e.g. multiple UARTs). For single-instance peripherals, it's fine
92//! to simply return the address directly from `get_registers`.
93//!
94//! Peripheral Clocks
95//! -----------------
96//!
97//! To facilitate low-power operation, PeripheralManager captures the
98//! peripheral's clock upon instantiation. The intention is to exploit
99//! [Ownership Based Resource
100//! Management](https://doc.rust-lang.org/beta/nomicon/obrm.html) to capture
101//! peripheral power state.
102//!
103//! To enable this, peripherals must inform the kernel which clock they use, and
104//! when the clock should be enabled and disabled. Implementations of the
105//! `before/after_mmio_access` methods must take care to not access hardware
106//! without enabling clocks if needed if they use hardware for bookkeeping.
107//!
108//! ```rust
109//! use kernel::utilities::peripheral_management::PeripheralManagement;
110//! use kernel::utilities::StaticRef;
111//! use kernel::platform::chip::ClockInterface;
112//! // A dummy clock for this example.
113//! // Real peripherals that do not have clocks should use NoClockControl from this module.
114//! struct ExampleClock {};
115//! impl ClockInterface for ExampleClock {
116//! fn is_enabled(&self) -> bool { true }
117//! fn enable(&self) { }
118//! fn disable(&self) { }
119//! }
120//!
121//! // Dummy hardware for this example.
122//! struct SpiRegisters {};
123//! struct SpiHw<'a> {
124//! mmio_address: StaticRef<SpiRegisters>,
125//! clock: &'a ExampleClock,
126//! busy: bool,
127//! };
128//!
129//! /// Teaching the kernel which clock controls SpiHw.
130//! impl<'a> PeripheralManagement<ExampleClock> for SpiHw<'a> {
131//! type RegisterType = SpiRegisters;
132//!
133//! fn get_registers(&self) -> &SpiRegisters { &*self.mmio_address }
134//!
135//! fn get_clock(&self) -> &ExampleClock { self.clock }
136//!
137//! fn before_peripheral_access(&self, clock: &ExampleClock, _registers: &SpiRegisters) {
138//! clock.enable();
139//! }
140//!
141//! fn after_peripheral_access(&self, clock: &ExampleClock, _registers: &SpiRegisters) {
142//! if !self.busy {
143//! clock.disable();
144//! }
145//! }
146//! }
147//! ```
148
149use crate::platform::chip::ClockInterface;
150
151/// A structure encapsulating a peripheral should implement this trait.
152pub trait PeripheralManagement<C>
153where
154 C: ClockInterface,
155{
156 type RegisterType;
157
158 /// How to get a reference to the physical hardware registers (the MMIO
159 /// struct).
160 fn get_registers(&self) -> &Self::RegisterType;
161
162 /// Which clock feeds this peripheral.
163 ///
164 /// For peripherals with no clock, use
165 /// `&::kernel::platform::chip::NO_CLOCK_CONTROL`.
166 fn get_clock(&self) -> &C;
167
168 /// Called before peripheral access.
169 ///
170 /// Responsible for ensure the peripheral can be safely accessed, e.g. that
171 /// its clock is powered on.
172 fn before_peripheral_access(&self, _: &C, _: &Self::RegisterType);
173
174 /// Called after peripheral access.
175 ///
176 /// Currently used primarily for power management to check whether the
177 /// peripheral can be powered off.
178 fn after_peripheral_access(&self, _: &C, _: &Self::RegisterType);
179}
180
181/// Object used to access memory mapped registers with the
182/// [`PeripheralManagement`] interface.
183///
184/// Structures encapsulating peripheral hardware (those implementing the
185/// [`PeripheralManagement`] trait) should instantiate an instance of this
186/// object.
187///
188/// ```
189/// # use kernel::utilities::cells::VolatileCell;
190/// # use kernel::utilities::peripheral_management::PeripheralManager;
191/// # use kernel::utilities::StaticRef;
192/// # pub struct PeripheralRegisters { control: VolatileCell<u32> };
193/// # pub struct PeripheralHardware { mmio_address: StaticRef<PeripheralRegisters> };
194/// impl PeripheralHardware {
195/// fn example(&self) {
196/// let peripheral = &PeripheralManager::new(self);
197/// peripheral.registers.control.set(0x1);
198/// }
199/// }
200/// # use kernel::utilities::peripheral_management::PeripheralManagement;
201/// # use kernel::platform::chip::NoClockControl;
202/// # impl PeripheralManagement<NoClockControl> for PeripheralHardware {
203/// # type RegisterType = PeripheralRegisters;
204///
205/// # fn get_registers(&self) -> &PeripheralRegisters {
206/// # &*self.mmio_address
207/// # }
208/// # fn get_clock(&self) -> &NoClockControl { unsafe { &kernel::platform::chip::NO_CLOCK_CONTROL } }
209/// # fn before_peripheral_access(&self, _c: &NoClockControl, _r: &Self::RegisterType) {}
210/// # fn after_peripheral_access(&self, _c: &NoClockControl, _r: &Self::RegisterType) {}
211/// # }
212/// ```
213pub struct PeripheralManager<'a, H, C>
214where
215 H: 'a + PeripheralManagement<C>,
216 C: 'a + ClockInterface,
217{
218 pub registers: &'a H::RegisterType,
219 peripheral_hardware: &'a H,
220 clock: &'a C,
221}
222
223impl<'a, H, C> PeripheralManager<'a, H, C>
224where
225 H: 'a + PeripheralManagement<C>,
226 C: 'a + ClockInterface,
227{
228 pub fn new(peripheral_hardware: &'a H) -> PeripheralManager<'a, H, C> {
229 let registers = peripheral_hardware.get_registers();
230 let clock = peripheral_hardware.get_clock();
231 peripheral_hardware.before_peripheral_access(clock, registers);
232 PeripheralManager {
233 registers,
234 peripheral_hardware,
235 clock,
236 }
237 }
238}
239
240impl<'a, H, C> Drop for PeripheralManager<'a, H, C>
241where
242 H: 'a + PeripheralManagement<C>,
243 C: 'a + ClockInterface,
244{
245 fn drop(&mut self) {
246 self.peripheral_hardware
247 .after_peripheral_access(self.clock, self.registers);
248 }
249}