1
#[cfg_attr(gpio_version = "1", path = "v1.rs")]
2
#[cfg_attr(gpio_version = "2", path = "v2.rs")]
3
#[cfg_attr(gpio_version = "3", path = "v3.rs")]
4
mod version;
5

            
6
use portable_atomic::{AtomicU32, Ordering};
7
use strum::EnumCount;
8
pub(crate) use version::{
9
    enable_interrupt,
10
    gpio_intr_enable,
11
    prepare_pin_pull,
12
    set_interrupt_priority,
13
};
14

            
15
use crate::peripherals::GPIO;
16

            
17
#[doc(hidden)]
18
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash, EnumCount)]
19
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20
pub enum GpioBank {
21
    _0,
22
    #[cfg(gpio_has_bank_1)]
23
    _1,
24
}
25

            
26
/// A set of pins, in the register words that the GPIO peripheral groups them into.
27
///
28
/// The peripheral has one register word for each bank, so a set of pins also has one word for each
29
/// bank. [`Self::word`] returns the word of a bank, which the caller can compare with the mask of
30
/// the register of that bank.
31
pub(crate) struct PadMask([AtomicU32; GpioBank::COUNT]);
32

            
33
impl PadMask {
34
    pub(crate) const fn new() -> Self {
35
        Self([const { AtomicU32::new(0) }; GpioBank::COUNT])
36
    }
37

            
38
    pub(crate) fn word(&self, bank: GpioBank) -> &AtomicU32 {
39
        &self.0[bank as usize]
40
    }
41

            
42
    #[cfg(sleep_driver_supported)]
43
    pub(crate) fn set(&self, gpio: u8, member: bool) {
44
        let bank = bank(gpio);
45
        let pin = 1 << (gpio - bank.offset());
46

            
47
        if member {
48
            self.word(bank).fetch_or(pin, Ordering::Relaxed);
49
        } else {
50
            self.word(bank).fetch_and(!pin, Ordering::Relaxed);
51
        }
52
    }
53

            
54
    #[cfg(sleep_driver_supported)]
55
    pub(crate) fn contains(&self, gpio: u8) -> bool {
56
        let bank = bank(gpio);
57

            
58
        self.word(bank).load(Ordering::Relaxed) & (1 << (gpio - bank.offset())) != 0
59
    }
60
}
61

            
62
impl GpioBank {
63
    /// All the banks, so that a caller can read the pins one word at a time.
64
    #[cfg(sleep_driver_supported)]
65
    pub(crate) const ALL: [Self; Self::COUNT] = cfg_select! {
66
        gpio_has_bank_1 => [Self::_0, Self::_1],
67
        _ => [Self::_0],
68
    };
69

            
70
    /// The pins of this bank that an async wait owns.
71
    ///
72
    /// The interrupt handler clears the bit of a pin to report that its wait is complete. The bit
73
    /// therefore tells the future that the wait is complete.
74
    pub(crate) fn async_operations(self) -> &'static AtomicU32 {
75
        static FLAGS: PadMask = PadMask::new();
76

            
77
        FLAGS.word(self)
78
    }
79

            
80
    /// The pins of this bank that listen for an interrupt.
81
    ///
82
    /// The same bits also give the pins that can end a light sleep, because one write sets the
83
    /// interrupt enable and the wakeup enable of a pad. Sleep entry reads these bits, and thus
84
    /// needs no register read for each pin. The default interrupt handler clears only `int_ena`.
85
    pub(crate) fn listening(self) -> &'static AtomicU32 {
86
        static FLAGS: PadMask = PadMask::new();
87

            
88
        FLAGS.word(self)
89
    }
90

            
91
    pub(crate) fn offset(self) -> u8 {
92
        match self {
93
            Self::_0 => 0,
94
            #[cfg(gpio_has_bank_1)]
95
            Self::_1 => 32,
96
        }
97
    }
98

            
99
728
    pub(crate) fn write_out_en(self, word: u32, enable: bool) {
100
        if enable {
101
            self.write_out_en_set(word);
102
        } else {
103
            self.write_out_en_clear(word);
104
        }
105
560
    }
106

            
107
    pub(crate) fn write_out_en_clear(self, word: u32) {
108
        match self {
109
            Self::_0 => GPIO::regs()
110
                .enable_w1tc()
111
                .write(|w| unsafe { w.bits(word) }),
112
            #[cfg(gpio_has_bank_1)]
113
            Self::_1 => GPIO::regs()
114
                .enable1_w1tc()
115
                .write(|w| unsafe { w.bits(word) }),
116
        };
117
    }
118

            
119
    pub(crate) fn write_out_en_set(self, word: u32) {
120
        match self {
121
            Self::_0 => GPIO::regs()
122
                .enable_w1ts()
123
                .write(|w| unsafe { w.bits(word) }),
124
            #[cfg(gpio_has_bank_1)]
125
            Self::_1 => GPIO::regs()
126
                .enable1_w1ts()
127
                .write(|w| unsafe { w.bits(word) }),
128
        };
129
    }
130

            
131
550
    pub(crate) fn read_input(self) -> u32 {
132
        match self {
133
            Self::_0 => GPIO::regs().in_().read().bits(),
134
            #[cfg(gpio_has_bank_1)]
135
            Self::_1 => GPIO::regs().in1().read().bits(),
136
        }
137
550
    }
138

            
139
45
    pub(crate) fn read_output(self) -> u32 {
140
        match self {
141
            Self::_0 => GPIO::regs().out().read().bits(),
142
            #[cfg(gpio_has_bank_1)]
143
            Self::_1 => GPIO::regs().out1().read().bits(),
144
        }
145
45
    }
146

            
147
40
    pub(crate) fn read_interrupt_status(self) -> u32 {
148
        version::read_bank_interrupt_status(self)
149
40
    }
150

            
151
    pub(crate) fn read_interrupt_status_of_current_cpu(self) -> u32 {
152
        version::read_interrupt_status_of_current_cpu(self)
153
    }
154

            
155
9
    pub(crate) fn write_interrupt_status_clear(self, word: u32) {
156
        match self {
157
            Self::_0 => GPIO::regs()
158
                .status_w1tc()
159
                .write(|w| unsafe { w.bits(word) }),
160
            #[cfg(gpio_has_bank_1)]
161
            Self::_1 => GPIO::regs()
162
                .status1_w1tc()
163
                .write(|w| unsafe { w.bits(word) }),
164
        };
165
9
    }
166

            
167
1761
    pub(crate) fn write_output(self, word: u32, set: bool) {
168
        if set {
169
            self.write_output_set(word);
170
        } else {
171
            self.write_output_clear(word);
172
        }
173
970
    }
174

            
175
    pub(crate) fn write_output_set(self, word: u32) {
176
        match self {
177
            Self::_0 => GPIO::regs().out_w1ts().write(|w| unsafe { w.bits(word) }),
178
            #[cfg(gpio_has_bank_1)]
179
            Self::_1 => GPIO::regs().out1_w1ts().write(|w| unsafe { w.bits(word) }),
180
        };
181
    }
182

            
183
    pub(crate) fn write_output_clear(self, word: u32) {
184
        match self {
185
            Self::_0 => GPIO::regs().out_w1tc().write(|w| unsafe { w.bits(word) }),
186
            #[cfg(gpio_has_bank_1)]
187
            Self::_1 => GPIO::regs().out1_w1tc().write(|w| unsafe { w.bits(word) }),
188
        };
189
    }
190
}
191

            
192
pub(crate) fn bank(_gpio_num: u8) -> GpioBank {
193
    #[cfg(gpio_has_bank_1)]
194
    if _gpio_num >= 32 {
195
        return GpioBank::_1;
196
    }
197

            
198
    GpioBank::_0
199
}
200

            
201
/// Sets GPIO event listening.
202
///
203
/// - `gpio_num`: the pin to configure
204
/// - `int_ena`: maskable and non-maskable CPU interrupt bits. None to leave unchanged.
205
/// - `int_type`: interrupt type, see [super::Event] (or 0 to disable)
206
/// - `wake_up_from_light_sleep`: whether to wake up from light sleep
207
pub(crate) fn set_int_enable(
208
    gpio_num: u8,
209
    int_ena: Option<u8>,
210
    int_type: u8,
211
    wake_up_from_light_sleep: bool,
212
) {
213
    let mut listening = false;
214
    GPIO::regs().pin(gpio_num as usize).modify(|r, w| unsafe {
215
        let enabled = int_ena.unwrap_or_else(|| r.int_ena().bits());
216
        listening = enabled != 0;
217

            
218
        w.int_ena().bits(enabled);
219
        w.int_type().bits(int_type);
220
        w.wakeup_enable().bit(wake_up_from_light_sleep)
221
    });
222

            
223
    let bank = bank(gpio_num);
224
11
    let pin = 1 << (gpio_num - bank.offset());
225
2
    if listening {
226
        bank.listening().fetch_or(pin, Ordering::Relaxed);
227
    } else {
228
30
        bank.listening().fetch_and(!pin, Ordering::Relaxed);
229
    }
230
2
}
231

            
232
/// Clears the CPU interrupt enable of a pad, and keeps its wake condition.
233
#[cfg(feature = "rt")]
234
pub(crate) fn disable_cpu_interrupt(gpio_num: u8) {
235
    GPIO::regs()
236
        .pin(gpio_num as usize)
237
        .modify(|_, w| unsafe { w.int_ena().bits(0) });
238
}
239

            
240
pub(crate) fn is_int_enabled(gpio_num: u8) -> bool {
241
454
    GPIO::regs().pin(gpio_num as usize).read().int_ena().bits() != 0
242
454
}