Skip to main content

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}