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}