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