lpc55s6x/
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 2025.
4
5//! General‑Purpose Input/Output (GPIO) driver for the LPC55S6x family.
6//!
7//! Features supported:
8//! - 64 GPIO pins across Port0 and Port1
9//! - Direction control (input/output) with atomic set/clear/not registers
10//! - Pin read/write/toggle operations
11//! - Integration with IOCON, InputMux, and PINT for flexible pin configuration
12//! - Interrupt support on rising, falling, or both edges
13//! - Safe abstractions for pin initialization and peripheral setup
14//!
15//! The `Pins` struct bundles all pins and ensures they are initialized with
16//! the required InputMux, IOCON, and PINT connections. Each `GpioPin` can be
17//! retrieved by its `LPCPin` enum value and used directly in drivers.
18//!
19//! Reference: *LPC55S6x/LPC55S2x/LPC552x User Manual* (NXP).
20
21use kernel::hil::gpio;
22use kernel::utilities::cells::OptionalCell;
23use kernel::utilities::registers::interfaces::{Readable, Writeable};
24use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite, WriteOnly};
25use kernel::utilities::StaticRef;
26
27register_structs! {
28    pub GpioRegisters {
29        (0x0000 => _reserved0: [u8; 0x2000]),
30        (0x2000 => dir_0: ReadWrite<u32, DIR::Register>),
31        (0x2004 => dir_1: ReadWrite<u32, DIR::Register>),
32        (0x2008 => _reserved1: [u8; 0x78]),
33        (0x2080 => mask_0: ReadWrite<u32, MASK::Register>),
34        (0x2084 => mask_1: ReadWrite<u32, MASK::Register>),
35        (0x2088 => _reserved2: [u8; 0x78]),
36        (0x2100 => pin_0: ReadWrite<u32, PIN::Register>),
37        (0x2104 => pin_1: ReadWrite<u32, PIN::Register>),
38        (0x2108 => _reserved3: [u8; 0x78]),
39        (0x2180 => mpin_0: ReadWrite<u32, MPIN::Register>),
40        (0x2184 => mpin_1: ReadWrite<u32, MPIN::Register>),
41        (0x2188 => _reserved4: [u8; 0x78]),
42        (0x2200 => set_0: WriteOnly<u32, SET::Register>),
43        (0x2204 => set_1: WriteOnly<u32, SET::Register>),
44        (0x2208 => _reserved5: [u8; 0x78]),
45        (0x2280 => clr_0: WriteOnly<u32, CLR::Register>),
46        (0x2284 => clr_1: WriteOnly<u32, CLR::Register>),
47        (0x2288 => _reserved6: [u8; 0x78]),
48        (0x2300 => not_0: WriteOnly<u32, NOT::Register>),
49        (0x2304 => not_1: WriteOnly<u32, NOT::Register>),
50        (0x2308 => _reserved7: [u8; 0x78]),
51        (0x2380 => dirset_0: WriteOnly<u32, DIRSET::Register>),
52        (0x2384 => dirset_1: WriteOnly<u32, DIRSET::Register>),
53        (0x2388 => _reserved8: [u8; 0x78]),
54        (0x2400 => dirclr_0: WriteOnly<u32, DIRCLR::Register>),
55        (0x2404 => dirclr_1: WriteOnly<u32, DIRCLR::Register>),
56        (0x2408 => _reserved9: [u8; 0x78]),
57        (0x2480 => dirnot_0: WriteOnly<u32, DIRNOT::Register>),
58        (0x2484 => dirnot_1: WriteOnly<u32, DIRNOT::Register>),
59        (0x2488 => @END),
60    }
61}
62
63register_bitfields![u32,
64    DIR [ DIRP OFFSET(0) NUMBITS(32) [] ], MASK [ MASKP OFFSET(0) NUMBITS(32) [] ],
65    PIN [ PORT OFFSET(0) NUMBITS(32) [] ], MPIN [ MPORTP OFFSET(0) NUMBITS(32) [] ],
66    SET [ SETP OFFSET(0) NUMBITS(32) [] ], CLR [ CLRP OFFSET(0) NUMBITS(32) [] ],
67    NOT [ NOTP OFFSET(0) NUMBITS(32) [] ], DIRSET [ DIRSETP OFFSET(0) NUMBITS(32) [] ],
68    DIRCLR [ DIRCLRP OFFSET(0) NUMBITS(32) [] ], DIRNOT [ DIRNOTP OFFSET(0) NUMBITS(32) [] ]
69];
70
71pub(crate) const GPIO_BASE: StaticRef<GpioRegisters> =
72    unsafe { StaticRef::new(0x5008_C000 as *const GpioRegisters) };
73
74#[derive(Clone, Copy, Debug, PartialEq)]
75#[allow(non_camel_case_types)]
76pub enum LPCPin {
77    P0_0 = 0,
78    P0_1 = 1,
79    P0_2 = 2,
80    P0_3 = 3,
81    P0_4 = 4,
82    P0_5 = 5,
83    P0_6 = 6,
84    P0_7 = 7,
85    P0_8 = 8,
86    P0_9 = 9,
87    P0_10 = 10,
88    P0_11 = 11,
89    P0_12 = 12,
90    P0_13 = 13,
91    P0_14 = 14,
92    P0_15 = 15,
93    P0_16 = 16,
94    P0_17 = 17,
95    P0_18 = 18,
96    P0_19 = 19,
97    P0_20 = 20,
98    P0_21 = 21,
99    P0_22 = 22,
100    P0_23 = 23,
101    P0_24 = 24,
102    P0_25 = 25,
103    P0_26 = 26,
104    P0_27 = 27,
105    P0_28 = 28,
106    P0_29 = 29,
107    P0_30 = 30,
108    P0_31 = 31,
109    P1_0 = 32,
110    P1_1 = 33,
111    P1_2 = 34,
112    P1_3 = 35,
113    P1_4 = 36,
114    P1_5 = 37,
115    P1_6 = 38,
116    P1_7 = 39,
117    P1_8 = 40,
118    P1_9 = 41,
119    P1_10 = 42,
120    P1_11 = 43,
121    P1_12 = 44,
122    P1_13 = 45,
123    P1_14 = 46,
124    P1_15 = 47,
125    P1_16 = 48,
126    P1_17 = 49,
127    P1_18 = 50,
128    P1_19 = 51,
129    P1_20 = 52,
130    P1_21 = 53,
131    P1_22 = 54,
132    P1_23 = 55,
133    P1_24 = 56,
134    P1_25 = 57,
135    P1_26 = 58,
136    P1_27 = 59,
137    P1_28 = 60,
138    P1_29 = 61,
139    P1_30 = 62,
140    P1_31 = 63,
141}
142
143#[repr(u8)]
144#[derive(Copy, Clone, Debug, PartialEq, Eq)]
145pub enum Port {
146    Port0 = 0,
147    Port1 = 1,
148}
149
150pub struct Pins<'a> {
151    pub pins: [GpioPin<'a>; 64],
152    pub inputmux: Inputmux,
153    pub iocon: Iocon,
154    pub pint: Pint<'a>,
155}
156
157impl<'a> Pins<'a> {
158    pub const fn new() -> Self {
159        let inputmux = Inputmux::new();
160        let iocon = Iocon::new();
161        let pint = Pint::new();
162        Self {
163            pins: [
164                GpioPin::new(LPCPin::P0_0),
165                GpioPin::new(LPCPin::P0_1),
166                GpioPin::new(LPCPin::P0_2),
167                GpioPin::new(LPCPin::P0_3),
168                GpioPin::new(LPCPin::P0_4),
169                GpioPin::new(LPCPin::P0_5),
170                GpioPin::new(LPCPin::P0_6),
171                GpioPin::new(LPCPin::P0_7),
172                GpioPin::new(LPCPin::P0_8),
173                GpioPin::new(LPCPin::P0_9),
174                GpioPin::new(LPCPin::P0_10),
175                GpioPin::new(LPCPin::P0_11),
176                GpioPin::new(LPCPin::P0_12),
177                GpioPin::new(LPCPin::P0_13),
178                GpioPin::new(LPCPin::P0_14),
179                GpioPin::new(LPCPin::P0_15),
180                GpioPin::new(LPCPin::P0_16),
181                GpioPin::new(LPCPin::P0_17),
182                GpioPin::new(LPCPin::P0_18),
183                GpioPin::new(LPCPin::P0_19),
184                GpioPin::new(LPCPin::P0_20),
185                GpioPin::new(LPCPin::P0_21),
186                GpioPin::new(LPCPin::P0_22),
187                GpioPin::new(LPCPin::P0_23),
188                GpioPin::new(LPCPin::P0_24),
189                GpioPin::new(LPCPin::P0_25),
190                GpioPin::new(LPCPin::P0_26),
191                GpioPin::new(LPCPin::P0_27),
192                GpioPin::new(LPCPin::P0_28),
193                GpioPin::new(LPCPin::P0_29),
194                GpioPin::new(LPCPin::P0_30),
195                GpioPin::new(LPCPin::P0_31),
196                GpioPin::new(LPCPin::P1_0),
197                GpioPin::new(LPCPin::P1_1),
198                GpioPin::new(LPCPin::P1_2),
199                GpioPin::new(LPCPin::P1_3),
200                GpioPin::new(LPCPin::P1_4),
201                GpioPin::new(LPCPin::P1_5),
202                GpioPin::new(LPCPin::P1_6),
203                GpioPin::new(LPCPin::P1_7),
204                GpioPin::new(LPCPin::P1_8),
205                GpioPin::new(LPCPin::P1_9),
206                GpioPin::new(LPCPin::P1_10),
207                GpioPin::new(LPCPin::P1_11),
208                GpioPin::new(LPCPin::P1_12),
209                GpioPin::new(LPCPin::P1_13),
210                GpioPin::new(LPCPin::P1_14),
211                GpioPin::new(LPCPin::P1_15),
212                GpioPin::new(LPCPin::P1_16),
213                GpioPin::new(LPCPin::P1_17),
214                GpioPin::new(LPCPin::P1_18),
215                GpioPin::new(LPCPin::P1_19),
216                GpioPin::new(LPCPin::P1_20),
217                GpioPin::new(LPCPin::P1_21),
218                GpioPin::new(LPCPin::P1_22),
219                GpioPin::new(LPCPin::P1_23),
220                GpioPin::new(LPCPin::P1_24),
221                GpioPin::new(LPCPin::P1_25),
222                GpioPin::new(LPCPin::P1_26),
223                GpioPin::new(LPCPin::P1_27),
224                GpioPin::new(LPCPin::P1_28),
225                GpioPin::new(LPCPin::P1_29),
226                GpioPin::new(LPCPin::P1_30),
227                GpioPin::new(LPCPin::P1_31),
228            ],
229            inputmux,
230            iocon,
231            pint,
232        }
233    }
234    /// Returns a reference to the GPIO pin corresponding to the given `LPCPin`.
235    ///
236    /// # Panics
237    /// This function will **never panic** because:
238    /// - `searched_pin` is an `LPCPin` enum, which only has valid, in‑range discriminants.
239    /// - Each valid `LPCPin` maps to a slot in `self.pins`, so the index is always within bounds.
240    /// - During initialization, all entries in `self.pins` are guaranteed to be populated (`Some`),
241    ///   so calling `.unwrap()` is safe.
242    ///
243    /// Therefore, both the array indexing and the `unwrap()` are guaranteed not to fail.
244    pub fn get_pin(&self, searched_pin: LPCPin) -> &GpioPin<'a> {
245        &self.pins[searched_pin as usize]
246    }
247
248    pub fn handle_interrupt(&self) {
249        self.pint.handle_interrupt();
250
251        for pin in self.pins.iter() {
252            pin.handle_interrupt();
253        }
254    }
255
256    pub fn set_inputmux(&'a self) {
257        for pin in self.pins.iter() {
258            pin.set_inputmux(&self.inputmux);
259        }
260    }
261
262    pub fn set_iocon(&'a self) {
263        for pin in self.pins.iter() {
264            pin.set_iocon(&self.iocon);
265        }
266    }
267
268    pub fn set_pint(&'a self) {
269        for pin in self.pins.iter() {
270            pin.set_pint(&self.pint);
271        }
272    }
273
274    pub fn init(&'a self) {
275        self.set_inputmux();
276        self.set_iocon();
277        self.set_pint();
278    }
279}
280
281pub struct GpioPin<'a> {
282    registers: StaticRef<GpioRegisters>,
283    port: Port,
284    pin: u8,
285    client: OptionalCell<&'a dyn gpio::Client>,
286    inputmux: OptionalCell<&'a Inputmux>,
287    iocon: OptionalCell<&'a Iocon>,
288    pint: OptionalCell<&'a Pint<'a>>,
289}
290
291pub use kernel::hil::gpio::{Configure, Input, Interrupt, Output, Pin};
292
293use crate::inputmux::Inputmux;
294use crate::iocon::Iocon;
295use crate::pint::{Edge, Pint};
296
297impl<'a> GpioPin<'a> {
298    pub const fn new(pin_name: LPCPin) -> Self {
299        let pin_num = pin_name as u8;
300        let port = match pin_num / 32 {
301            0 => Port::Port0,
302            1 => Port::Port1,
303            _ => panic!("Invalid pin number for LPCPin"),
304        };
305        Self {
306            registers: GPIO_BASE,
307            port,
308            pin: pin_num % 32,
309            client: OptionalCell::empty(),
310            inputmux: OptionalCell::empty(),
311            iocon: OptionalCell::empty(),
312            pint: OptionalCell::empty(),
313        }
314    }
315
316    fn pin_mask(&self) -> u32 {
317        1 << self.pin
318    }
319
320    fn is_output(&self) -> bool {
321        match self.port {
322            Port::Port0 => (self.registers.dir_0.get() & self.pin_mask()) != 0,
323            Port::Port1 => (self.registers.dir_1.get() & self.pin_mask()) != 0,
324        }
325    }
326
327    pub fn get_pin_num(&self) -> usize {
328        (self.port as usize * 32) + self.pin as usize
329    }
330
331    pub fn handle_interrupt(&self) {
332        self.pint.map(|pint| {
333            pint.handle_interrupt();
334        });
335    }
336
337    pub fn set_inputmux(&self, inputmux: &'a Inputmux) {
338        self.inputmux.set(inputmux);
339    }
340    pub fn set_iocon(&self, iocon: &'a Iocon) {
341        self.iocon.set(iocon);
342    }
343    pub fn set_pint(&self, pint: &'a Pint<'a>) {
344        self.pint.set(pint);
345    }
346}
347
348impl gpio::Output for GpioPin<'_> {
349    fn set(&self) {
350        match self.port {
351            Port::Port0 => self.registers.set_0.write(SET::SETP.val(self.pin_mask())),
352            Port::Port1 => self.registers.set_1.write(SET::SETP.val(self.pin_mask())),
353        }
354    }
355
356    fn clear(&self) {
357        match self.port {
358            Port::Port0 => self.registers.clr_0.write(CLR::CLRP.val(self.pin_mask())),
359            Port::Port1 => self.registers.clr_1.write(CLR::CLRP.val(self.pin_mask())),
360        }
361    }
362
363    fn toggle(&self) -> bool {
364        match self.port {
365            Port::Port0 => self.registers.not_0.write(NOT::NOTP.val(self.pin_mask())),
366            Port::Port1 => self.registers.not_1.write(NOT::NOTP.val(self.pin_mask())),
367        }
368        self.read()
369    }
370}
371
372impl gpio::Input for GpioPin<'_> {
373    fn read(&self) -> bool {
374        match self.port {
375            Port::Port0 => self.registers.pin_0.get() & self.pin_mask() != 0,
376            Port::Port1 => self.registers.pin_1.get() & self.pin_mask() != 0,
377        }
378    }
379}
380
381impl gpio::Configure for GpioPin<'_> {
382    fn make_output(&self) -> gpio::Configuration {
383        match self.port {
384            Port::Port0 => self
385                .registers
386                .dirset_0
387                .write(DIRSET::DIRSETP.val(self.pin_mask())),
388            Port::Port1 => self
389                .registers
390                .dirset_1
391                .write(DIRSET::DIRSETP.val(self.pin_mask())),
392        }
393        gpio::Configuration::Output
394    }
395
396    fn make_input(&self) -> gpio::Configuration {
397        match self.port {
398            Port::Port0 => self
399                .registers
400                .dirclr_0
401                .write(DIRCLR::DIRCLRP.val(self.pin_mask())),
402            Port::Port1 => self
403                .registers
404                .dirclr_1
405                .write(DIRCLR::DIRCLRP.val(self.pin_mask())),
406        }
407        gpio::Configuration::Input
408    }
409
410    fn configuration(&self) -> gpio::Configuration {
411        if self.is_output() {
412            gpio::Configuration::Output
413        } else {
414            gpio::Configuration::Input
415        }
416    }
417
418    fn set_floating_state(&self, state: kernel::hil::gpio::FloatingState) {
419        let pins = [
420            LPCPin::P0_0,
421            LPCPin::P0_1,
422            LPCPin::P0_2,
423            LPCPin::P0_3,
424            LPCPin::P0_4,
425            LPCPin::P0_5,
426            LPCPin::P0_6,
427            LPCPin::P0_7,
428            LPCPin::P0_8,
429            LPCPin::P0_9,
430            LPCPin::P0_10,
431            LPCPin::P0_11,
432            LPCPin::P0_12,
433            LPCPin::P0_13,
434            LPCPin::P0_14,
435            LPCPin::P0_15,
436            LPCPin::P0_16,
437            LPCPin::P0_17,
438            LPCPin::P0_18,
439            LPCPin::P0_19,
440            LPCPin::P0_20,
441            LPCPin::P0_21,
442            LPCPin::P0_22,
443            LPCPin::P0_23,
444            LPCPin::P0_24,
445            LPCPin::P0_25,
446            LPCPin::P0_26,
447            LPCPin::P0_27,
448            LPCPin::P0_28,
449            LPCPin::P0_29,
450            LPCPin::P0_30,
451            LPCPin::P0_31,
452            LPCPin::P1_0,
453            LPCPin::P1_1,
454            LPCPin::P1_2,
455            LPCPin::P1_3,
456            LPCPin::P1_4,
457            LPCPin::P1_5,
458            LPCPin::P1_6,
459            LPCPin::P1_7,
460            LPCPin::P1_8,
461            LPCPin::P1_9,
462            LPCPin::P1_10,
463            LPCPin::P1_11,
464            LPCPin::P1_12,
465            LPCPin::P1_13,
466            LPCPin::P1_14,
467            LPCPin::P1_15,
468            LPCPin::P1_16,
469            LPCPin::P1_17,
470            LPCPin::P1_18,
471            LPCPin::P1_19,
472            LPCPin::P1_20,
473            LPCPin::P1_21,
474            LPCPin::P1_22,
475            LPCPin::P1_23,
476            LPCPin::P1_24,
477            LPCPin::P1_25,
478            LPCPin::P1_26,
479            LPCPin::P1_27,
480            LPCPin::P1_28,
481            LPCPin::P1_29,
482            LPCPin::P1_30,
483            LPCPin::P1_31,
484        ];
485
486        for pin in pins.iter() {
487            match state {
488                gpio::FloatingState::PullNone => {
489                    self.iocon.map(|iocon| {
490                        iocon.set_pull_none(*pin);
491                    });
492                }
493                gpio::FloatingState::PullUp => {
494                    self.iocon.map(|iocon| {
495                        iocon.set_pull_up(*pin);
496                    });
497                }
498                gpio::FloatingState::PullDown => {
499                    self.iocon.map(|iocon| {
500                        iocon.set_pull_down(*pin);
501                    });
502                }
503            }
504        }
505    }
506    fn floating_state(&self) -> gpio::FloatingState {
507        gpio::FloatingState::PullNone
508    }
509    fn disable_input(&self) -> gpio::Configuration {
510        self.make_output()
511    }
512    fn disable_output(&self) -> gpio::Configuration {
513        self.make_input()
514    }
515    fn deactivate_to_low_power(&self) {
516        let _state = gpio::FloatingState::PullNone;
517        self.make_input();
518    }
519}
520
521impl<'a> gpio::Interrupt<'a> for GpioPin<'a> {
522    fn set_client(&self, client: &'a dyn gpio::Client) {
523        self.client.set(client);
524        self.pint.map(|pint| {
525            pint.set_client(0, client);
526        });
527    }
528
529    fn enable_interrupts(&self, mode: gpio::InterruptEdge) {
530        match mode {
531            gpio::InterruptEdge::RisingEdge => {
532                self.pint.map(|pint| {
533                    pint.configure_interrupt(0, Edge::Rising);
534                });
535            }
536            gpio::InterruptEdge::FallingEdge => {
537                self.pint.map(|pint| {
538                    pint.configure_interrupt(0, Edge::Falling);
539                });
540            }
541            gpio::InterruptEdge::EitherEdge => {
542                self.pint.map(|pint| {
543                    pint.configure_interrupt(0, Edge::Both);
544                });
545            }
546        }
547    }
548
549    fn disable_interrupts(&self) {
550        self.pint.map(|pint| {
551            pint.disable_interrupt(0);
552        });
553    }
554
555    fn is_pending(&self) -> bool {
556        self.pint.map_or(false, |pint| {
557            let channel = 0;
558            pint.is_pending(channel)
559        })
560    }
561}