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}