components/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//! Components for GPIO pins.
6//!
7//! Usage
8//! -----
9//!
10//! The `gpio_component_helper!` macro takes 'static references to GPIO pins.
11//! When GPIO instances are owned values, the `gpio_component_helper_owned!` can
12//! be used, indicating that the passed values are owned values. This macro will
13//! perform static allocation of the passed in GPIO pins internally.
14//!
15//! ```rust
16//! let gpio = components::gpio::GpioComponent::new(
17//! board_kernel,
18//! components::gpio_component_helper!(
19//! nrf52840::gpio::GPIOPin,
20//! // left side of the USB plug
21//! 0 => &nrf52840::gpio::PORT[Pin::P0_13],
22//! 1 => &nrf52840::gpio::PORT[Pin::P0_15],
23//! 2 => &nrf52840::gpio::PORT[Pin::P0_17],
24//! 3 => &nrf52840::gpio::PORT[Pin::P0_20],
25//! 4 => &nrf52840::gpio::PORT[Pin::P0_22],
26//! 5 => &nrf52840::gpio::PORT[Pin::P0_24],
27//! 6 => &nrf52840::gpio::PORT[Pin::P1_00],
28//! 7 => &nrf52840::gpio::PORT[Pin::P0_09],
29//! 8 => &nrf52840::gpio::PORT[Pin::P0_10],
30//! // right side of the USB plug
31//! 9 => &nrf52840::gpio::PORT[Pin::P0_31],
32//! 10 => &nrf52840::gpio::PORT[Pin::P0_29],
33//! 11 => &nrf52840::gpio::PORT[Pin::P0_02],
34//! 12 => &nrf52840::gpio::PORT[Pin::P1_15],
35//! 13 => &nrf52840::gpio::PORT[Pin::P1_13],
36//! 14 => &nrf52840::gpio::PORT[Pin::P1_10],
37//! // Below the PCB
38//! 15 => &nrf52840::gpio::PORT[Pin::P0_26],
39//! 16 => &nrf52840::gpio::PORT[Pin::P0_04],
40//! 17 => &nrf52840::gpio::PORT[Pin::P0_11],
41//! 18 => &nrf52840::gpio::PORT[Pin::P0_14],
42//! 19 => &nrf52840::gpio::PORT[Pin::P1_11],
43//! 20 => &nrf52840::gpio::PORT[Pin::P1_07],
44//! 21 => &nrf52840::gpio::PORT[Pin::P1_01],
45//! 22 => &nrf52840::gpio::PORT[Pin::P1_04],
46//! 23 => &nrf52840::gpio::PORT[Pin::P1_02]
47//! ),
48//! ).finalize(components::gpio_component_static!(nrf52840::gpio::GPIOPin));
49//! ```
50
51use capsules_core::gpio::GPIO;
52use core::mem::MaybeUninit;
53use kernel::capabilities;
54use kernel::component::Component;
55use kernel::create_capability;
56use kernel::hil::gpio;
57use kernel::hil::gpio::InterruptWithValue;
58
59#[macro_export]
60macro_rules! gpio_component_helper_max_pin {
61 () => { 0usize };
62 ($a:expr, $b:expr, $($tail:expr),* $(,)?) => { $crate::gpio_component_helper_max_pin! (max ($a, $b), $($tail,)*) };
63 ($a:expr $(,)?) => { $a };
64}
65
66#[macro_export]
67macro_rules! gpio_component_helper_owned {
68 (
69 $Pin:ty,
70 $($nr:literal => $pin:expr),* $(,)?
71 ) => {
72 $crate::gpio_component_helper!(
73 $Pin,
74 $(
75 $nr => static_init!($Pin, $pin),
76 )*
77 )
78 };
79}
80
81#[macro_export]
82/// Pins are declared using the following format:
83/// number => pin
84///
85/// Any pin numbers that are skipped will be declared as None
86/// and using them from user space will return NODEVICE
87macro_rules! gpio_component_helper {
88 (
89 $Pin:ty,
90 $($nr:literal => $pin:expr),* $(,)?
91 ) => {{
92 use kernel::count_expressions;
93 use kernel::hil::gpio::InterruptValueWrapper;
94 use kernel::static_init;
95
96 const fn max (a: usize, b: usize) -> usize {
97 [a, b][(a < b) as usize]
98 }
99
100 const NUM_PINS: usize = $crate::gpio_component_helper_max_pin! ($($nr,)*) + 1;
101
102 let mut pins = static_init!(
103 [Option<&'static InterruptValueWrapper<'static, $Pin>>; NUM_PINS],
104 [None; NUM_PINS]
105 );
106
107 $(
108 pins[$nr] = Some(static_init!(InterruptValueWrapper<$Pin>, InterruptValueWrapper::new($pin)).finalize());
109 )*
110
111 pins
112 };};
113}
114
115#[macro_export]
116macro_rules! gpio_component_static {
117 ($Pin:ty $(,)?) => {{
118 kernel::static_buf!(capsules_core::gpio::GPIO<'static, $Pin>)
119 };};
120}
121
122pub type GpioComponentType<IP> = GPIO<'static, IP>;
123
124pub struct GpioComponent<IP: 'static + gpio::InterruptPin<'static>> {
125 board_kernel: &'static kernel::Kernel,
126 driver_num: usize,
127 gpio_pins: &'static [Option<&'static gpio::InterruptValueWrapper<'static, IP>>],
128}
129
130impl<IP: 'static + gpio::InterruptPin<'static>> GpioComponent<IP> {
131 pub fn new(
132 board_kernel: &'static kernel::Kernel,
133 driver_num: usize,
134 gpio_pins: &'static [Option<&'static gpio::InterruptValueWrapper<'static, IP>>],
135 ) -> Self {
136 Self {
137 board_kernel,
138 driver_num,
139 gpio_pins,
140 }
141 }
142}
143
144impl<IP: 'static + gpio::InterruptPin<'static>> Component for GpioComponent<IP> {
145 type StaticInput = &'static mut MaybeUninit<GPIO<'static, IP>>;
146 type Output = &'static GPIO<'static, IP>;
147
148 fn finalize(self, static_buffer: Self::StaticInput) -> Self::Output {
149 let grant_cap = create_capability!(capabilities::MemoryAllocationCapability);
150 let gpio = static_buffer.write(GPIO::new(
151 self.gpio_pins,
152 self.board_kernel.create_grant(self.driver_num, &grant_cap),
153 ));
154 for maybe_pin in self.gpio_pins.iter() {
155 if let Some(pin) = maybe_pin {
156 pin.set_client(gpio);
157 }
158 }
159
160 gpio
161 }
162}