kernel/hil/gpio.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//! HIL for General Purpose Input-Output (GPIO) pins.
6
7use core::cell::Cell;
8
9use crate::utilities::cells::OptionalCell;
10use crate::ErrorCode;
11
12/// Enum for configuring any pull-up or pull-down resistors on the GPIO pin.
13#[derive(Clone, Copy, Debug)]
14pub enum FloatingState {
15 PullUp,
16 PullDown,
17 PullNone,
18}
19
20/// Enum for selecting which edge to trigger interrupts on.
21#[derive(Clone, Copy, Debug)]
22pub enum InterruptEdge {
23 RisingEdge,
24 FallingEdge,
25 EitherEdge,
26}
27
28/// Enum for which state the pin is in.
29///
30/// Some MCUs can support Input/Output pins,
31/// so this is a valid option. `Function` means the pin has been configured to
32/// a special function. Determining which function it outside the scope of the HIL,
33/// and should instead use a chip-specific API.
34#[derive(Clone, Copy, Debug)]
35pub enum Configuration {
36 /// Cannot be read or written or used; effectively inactive.
37 LowPower,
38 /// Calls to the `Input` trait are valid.
39 Input,
40 /// Calls to the `Output` trait are valid.
41 Output,
42 /// Calls to both the `Input` and `Output` traits are valid.
43 InputOutput,
44 /// Chip-specific, requires chip-specific API for more detail,
45 Function,
46 /// In a state not covered by other values.
47 Other,
48}
49
50/// Some GPIOs can be semantically active or not.
51/// For example:
52/// - a LED is active when emitting light,
53/// - a button GPIO is active when pressed.
54#[derive(Clone, Copy, PartialEq, Eq)]
55pub enum ActivationState {
56 Inactive = 0,
57 Active = 1,
58}
59
60/// Whether a GPIO is in the `ActivationState::Active` when the signal is high
61/// or low.
62#[derive(Clone, Copy)]
63pub enum ActivationMode {
64 ActiveHigh,
65 ActiveLow,
66}
67
68/// The Pin trait allows a pin to be used as either input
69/// or output and to be configured.
70pub trait Pin: Input + Output + Configure {}
71
72/// The InterruptPin trait allows a pin to be used as either
73/// input or output and also to source interrupts.
74pub trait InterruptPin<'a>: Pin + Interrupt<'a> {}
75
76/// The InterruptValuePin trait allows a pin to be used as
77/// either input or output and also to source interrupts which
78/// pass a value.
79pub trait InterruptValuePin<'a>: Pin + InterruptWithValue<'a> {}
80
81// Provide blanket implementations for all trait groups
82impl<T: Input + Output + Configure> Pin for T {}
83impl<'a, T: Pin + Interrupt<'a>> InterruptPin<'a> for T {}
84impl<'a, T: Pin + InterruptWithValue<'a>> InterruptValuePin<'a> for T {}
85
86/// Control and configure a GPIO pin.
87pub trait Configure {
88 /// Return the current pin configuration.
89 fn configuration(&self) -> Configuration;
90
91 /// Make the pin an output, returning the current configuration,
92 /// which should be either `Configuration::Output` or
93 /// `Configuration::InputOutput`.
94 fn make_output(&self) -> Configuration;
95 /// Disable the pin as an output, returning the current configuration.
96 fn disable_output(&self) -> Configuration;
97
98 /// Make the pin an input, returning the current configuration,
99 /// which should be ither `Configuration::Input` or
100 /// `Configuration::InputOutput`.
101 fn make_input(&self) -> Configuration;
102 /// Disable the pin as an input, returning the current configuration.
103 fn disable_input(&self) -> Configuration;
104
105 /// Put a pin into its lowest power state, with no guarantees on
106 /// if it is enabled or not. Implementations are free to use any
107 /// state (e.g. input, output, disable, etc.) the hardware pin
108 /// supports to ensure the pin is as low power as possible.
109 /// Re-enabling the pin requires reconfiguring it (i.e. the state
110 /// of its enabled configuration is not stored).
111 fn deactivate_to_low_power(&self);
112
113 /// Set the floating state of the pin.
114 fn set_floating_state(&self, state: FloatingState);
115 /// Return the current floating state of the pin.
116 fn floating_state(&self) -> FloatingState;
117
118 /// Return whether the pin is an input (reading from
119 /// the Input trait will return valid results). Returns
120 /// true if the pin is in Configuration::Input or
121 /// Configuration::InputOutput.
122 fn is_input(&self) -> bool {
123 match self.configuration() {
124 Configuration::Input | Configuration::InputOutput => true,
125 _ => false,
126 }
127 }
128
129 /// Return whether the pin is an output (writing to
130 /// the Output trait will change the output of the pin).
131 /// Returns true if the pin is in Configuration::Output or
132 /// Configuration::InputOutput.
133 fn is_output(&self) -> bool {
134 match self.configuration() {
135 Configuration::Output | Configuration::InputOutput => true,
136 _ => false,
137 }
138 }
139}
140
141/// Configuration trait for pins that can be simultaneously
142/// input and output. Having this trait allows an implementation
143/// to statically verify this is possible.
144pub trait ConfigureInputOutput: Configure {
145 /// Make the pin a simultaneously input and output; should always
146 /// return `Configuration::InputOutput`.
147 fn make_input_output(&self) -> Configuration;
148 fn is_input_output(&self) -> bool;
149}
150
151pub trait Output {
152 /// Set the GPIO pin high. If the pin is not an output or
153 /// input/output, this call is ignored.
154 fn set(&self);
155
156 /// Set the GPIO pin low. If the pin is not an output or
157 /// input/output, this call is ignored.
158 fn clear(&self);
159
160 /// Toggle the GPIO pin. If the pin was high, set it low. If
161 /// the pin was low, set it high. If the pin is not an output or
162 /// input/output, this call is ignored. Return the new value
163 /// of the pin.
164 fn toggle(&self) -> bool;
165
166 /// Activate or deactivate a GPIO pin, for a given activation mode.
167 fn write_activation(&self, state: ActivationState, mode: ActivationMode) {
168 match (state, mode) {
169 (ActivationState::Active, ActivationMode::ActiveHigh)
170 | (ActivationState::Inactive, ActivationMode::ActiveLow) => {
171 self.set();
172 }
173 (ActivationState::Active, ActivationMode::ActiveLow)
174 | (ActivationState::Inactive, ActivationMode::ActiveHigh) => {
175 self.clear();
176 }
177 }
178 }
179}
180
181pub trait Input {
182 /// Get the current state of an input GPIO pin. For an output
183 /// pin, return the output; for an input pin, return the input;
184 /// for disabled or function pins the value is undefined.
185 fn read(&self) -> bool;
186
187 /// Get the current state of a GPIO pin, for a given activation mode.
188 fn read_activation(&self, mode: ActivationMode) -> ActivationState {
189 let value = self.read();
190 match (mode, value) {
191 (ActivationMode::ActiveHigh, true) | (ActivationMode::ActiveLow, false) => {
192 ActivationState::Active
193 }
194 (ActivationMode::ActiveLow, true) | (ActivationMode::ActiveHigh, false) => {
195 ActivationState::Inactive
196 }
197 }
198 }
199}
200
201pub trait Interrupt<'a>: Input {
202 /// Set the client for interrupt events.
203 fn set_client(&self, client: &'a dyn Client);
204
205 /// Enable an interrupt on the GPIO pin. This does not
206 /// configure the pin except to enable an interrupt: it
207 /// should be separately configured as an input, etc.
208 fn enable_interrupts(&self, mode: InterruptEdge);
209
210 /// Disable interrupts for the GPIO pin.
211 fn disable_interrupts(&self);
212
213 /// Return whether this interrupt is pending
214 fn is_pending(&self) -> bool;
215}
216
217/// Interface for users of synchronous GPIO interrupts. In order
218/// to receive interrupts, the user must implement
219/// this `Client` interface.
220pub trait Client {
221 /// Called when an interrupt occurs. The `identifier` will
222 /// be the same value that was passed to `enable_interrupt()`
223 /// when the interrupt was configured.
224 fn fired(&self);
225}
226
227/// Interface that wraps an interrupt to pass a value when it
228/// triggers.
229///
230/// The standard use case for this trait is when several interrupts
231/// call the same callback function and it needs to distinguish which
232/// one is calling it by giving each one a unique value.
233pub trait InterruptWithValue<'a>: Input {
234 /// Set the client for interrupt events.
235 fn set_client(&self, client: &'a dyn ClientWithValue);
236
237 /// Enable an interrupt on the GPIO pin. This does not
238 /// configure the pin except to enable an interrupt: it
239 /// should be separately configured as an input, etc.
240 /// Returns:
241 /// Ok(()) - the interrupt was set up properly
242 /// FAIL - the interrupt was not set up properly; this is due to
243 /// not having an underlying interrupt source yet, i.e.
244 /// the struct is not yet fully initialized.
245 fn enable_interrupts(&self, mode: InterruptEdge) -> Result<(), ErrorCode>;
246
247 /// Disable interrupts for the GPIO pin.
248 fn disable_interrupts(&self);
249
250 /// Return whether this interrupt is pending
251 fn is_pending(&self) -> bool;
252
253 /// Set the value that will be passed to clients on an
254 /// interrupt.
255 fn set_value(&self, value: u32);
256
257 /// Return the value that is passed to clients on an
258 /// interrupt.
259 fn value(&self) -> u32;
260}
261
262/// Interfaces for users of GPIO interrupts who handle many interrupts
263/// with the same function. The value passed in the callback allows the
264/// callback to distinguish which interrupt fired.
265pub trait ClientWithValue {
266 fn fired(&self, value: u32);
267}
268
269/// Standard implementation of InterruptWithValue: handles an
270/// `gpio::Client::fired` and passes it up as a
271/// `gpio::ClientWithValue::fired`.
272pub struct InterruptValueWrapper<'a, IP: InterruptPin<'a>> {
273 value: Cell<u32>,
274 client: OptionalCell<&'a dyn ClientWithValue>,
275 source: &'a IP,
276}
277
278impl<'a, IP: InterruptPin<'a>> InterruptValueWrapper<'a, IP> {
279 pub fn new(pin: &'a IP) -> Self {
280 Self {
281 value: Cell::new(0),
282 client: OptionalCell::empty(),
283 source: pin,
284 }
285 }
286
287 pub fn finalize(&'static self) -> &'static Self {
288 self.source.set_client(self);
289 self
290 }
291}
292
293impl<'a, IP: InterruptPin<'a>> InterruptWithValue<'a> for InterruptValueWrapper<'a, IP> {
294 fn set_value(&self, value: u32) {
295 self.value.set(value);
296 }
297
298 fn value(&self) -> u32 {
299 self.value.get()
300 }
301
302 fn set_client(&self, client: &'a dyn ClientWithValue) {
303 self.client.replace(client);
304 }
305
306 fn is_pending(&self) -> bool {
307 self.source.is_pending()
308 }
309
310 fn enable_interrupts(&self, edge: InterruptEdge) -> Result<(), ErrorCode> {
311 self.source.enable_interrupts(edge);
312 Ok(())
313 }
314
315 fn disable_interrupts(&self) {
316 self.source.disable_interrupts();
317 }
318}
319
320impl<'a, IP: InterruptPin<'a>> Input for InterruptValueWrapper<'a, IP> {
321 fn read(&self) -> bool {
322 self.source.read()
323 }
324}
325
326impl<'a, IP: InterruptPin<'a>> Configure for InterruptValueWrapper<'a, IP> {
327 fn configuration(&self) -> Configuration {
328 self.source.configuration()
329 }
330
331 fn make_output(&self) -> Configuration {
332 self.source.make_output()
333 }
334
335 fn disable_output(&self) -> Configuration {
336 self.source.disable_output()
337 }
338
339 fn make_input(&self) -> Configuration {
340 self.source.make_input()
341 }
342
343 fn disable_input(&self) -> Configuration {
344 self.source.disable_input()
345 }
346
347 fn deactivate_to_low_power(&self) {
348 self.source.deactivate_to_low_power();
349 }
350
351 fn set_floating_state(&self, state: FloatingState) {
352 self.source.set_floating_state(state);
353 }
354
355 fn floating_state(&self) -> FloatingState {
356 self.source.floating_state()
357 }
358
359 fn is_input(&self) -> bool {
360 self.source.is_input()
361 }
362
363 fn is_output(&self) -> bool {
364 self.source.is_output()
365 }
366}
367
368impl<'a, IP: InterruptPin<'a>> Output for InterruptValueWrapper<'a, IP> {
369 fn set(&self) {
370 self.source.set();
371 }
372
373 fn clear(&self) {
374 self.source.clear();
375 }
376
377 fn toggle(&self) -> bool {
378 self.source.toggle()
379 }
380}
381
382impl<'a, IP: InterruptPin<'a>> Client for InterruptValueWrapper<'a, IP> {
383 fn fired(&self) {
384 self.client.map(|c| c.fired(self.value()));
385 }
386}