lpc55s6x/
flexcomm.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//! Flexible Communication (Flexcomm) peripheral driver for the LPC55S6x family.
6//!
7//! The Flexcomm block is a multi‑protocol serial interface that can be
8//! configured at runtime to operate as one of several functions:
9//! - **USART** (Universal Synchronous/Asynchronous Receiver/Transmitter)
10//! - **SPI** (Serial Peripheral Interface)
11//! - **I²C** (Inter‑Integrated Circuit)
12//! - **I²S** (Inter‑IC Sound, transmit or receive)
13//!
14//! Each LPC55S6x device includes up to 8 Flexcomm instances (Flexcomm0–7),
15//! each with its own base address. The `PSELID` register selects the active
16//! function and can be locked to prevent accidental reconfiguration.
17//!
18//! This module provides:
19//! - Strongly‑typed register mappings for `PSELID` and `PID`
20//! - Safe constructors for accessing Flexcomm instances by base address or ID
21//! - Convenience methods for configuring a Flexcomm as a UART
22//!
23//! Reference: *LPC55S6x/LPC55S2x/LPC552x User Manual* (NXP).
24
25use kernel::utilities::registers::interfaces::Writeable;
26use kernel::utilities::registers::{register_bitfields, register_structs, ReadOnly, ReadWrite};
27use kernel::utilities::StaticRef;
28
29use crate::flexcomm::PSELID::{LOCK, PERSEL};
30
31const FLEXCOMM0_BASE: StaticRef<FlexcommRegisters> =
32    unsafe { StaticRef::new(0x40086000 as *const FlexcommRegisters) };
33const FLEXCOMM1_BASE: StaticRef<FlexcommRegisters> =
34    unsafe { StaticRef::new(0x40087000 as *const FlexcommRegisters) };
35const FLEXCOMM2_BASE: StaticRef<FlexcommRegisters> =
36    unsafe { StaticRef::new(0x40088000 as *const FlexcommRegisters) };
37const FLEXCOMM3_BASE: StaticRef<FlexcommRegisters> =
38    unsafe { StaticRef::new(0x40089000 as *const FlexcommRegisters) };
39const FLEXCOMM4_BASE: StaticRef<FlexcommRegisters> =
40    unsafe { StaticRef::new(0x4008A000 as *const FlexcommRegisters) };
41const FLEXCOMM5_BASE: StaticRef<FlexcommRegisters> =
42    unsafe { StaticRef::new(0x40096000 as *const FlexcommRegisters) };
43const FLEXCOMM6_BASE: StaticRef<FlexcommRegisters> =
44    unsafe { StaticRef::new(0x40097000 as *const FlexcommRegisters) };
45const FLEXCOMM7_BASE: StaticRef<FlexcommRegisters> =
46    unsafe { StaticRef::new(0x40098000 as *const FlexcommRegisters) };
47
48register_structs! {
49    /// Flexcomm serial communication
50    FlexcommRegisters {
51        (0x000 => _reserved0),
52        /// Peripheral Select and Flexcomm ID register.
53        (0xFF8 => pselid: ReadWrite<u32, PSELID::Register>),
54        /// Peripheral identification register.
55        (0xFFC => pid: ReadOnly<u32, PID::Register>),
56        (0x1000 => @END),
57    }
58}
59register_bitfields![u32,
60pub PSELID [
61    /// Peripheral Select. This field is writable by software.
62    PERSEL OFFSET(0) NUMBITS(3) [
63        /// No peripheral selected.
64        NoPeripheralSelected = 0,
65        /// USART function selected.
66        USARTFunctionSelected = 1,
67        /// SPI function selected.
68        SPIFunctionSelected = 2,
69        /// I2C function selected.
70        I2CFunctionSelected = 3,
71        /// I2S transmit function selected.
72        I2STransmitFunctionSelected = 4,
73        /// I2S receive function selected.
74        I2SReceiveFunctionSelected = 5
75    ],
76    /// Lock the peripheral select. This field is writable by software.
77    LOCK OFFSET(3) NUMBITS(1) [
78        /// Peripheral select can be changed by software.
79        PeripheralSelectCanBeChangedBySoftware = 0,
80        /// Peripheral select is locked and cannot be changed until this Flexcomm or the ent
81        LOCKED = 1
82    ],
83    /// USART present indicator. This field is Read-only.
84    USARTPRESENT OFFSET(4) NUMBITS(1) [
85        /// This Flexcomm does not include the USART function.
86        ThisFlexcommDoesNotIncludeTheUSARTFunction = 0,
87        /// This Flexcomm includes the USART function.
88        ThisFlexcommIncludesTheUSARTFunction = 1
89    ],
90    /// SPI present indicator. This field is Read-only.
91    SPIPRESENT OFFSET(5) NUMBITS(1) [
92        /// This Flexcomm does not include the SPI function.
93        ThisFlexcommDoesNotIncludeTheSPIFunction = 0,
94        /// This Flexcomm includes the SPI function.
95        ThisFlexcommIncludesTheSPIFunction = 1
96    ],
97    /// I2C present indicator. This field is Read-only.
98    I2CPRESENT OFFSET(6) NUMBITS(1) [
99        /// This Flexcomm does not include the I2C function.
100        ThisFlexcommDoesNotIncludeTheI2CFunction = 0,
101        /// This Flexcomm includes the I2C function.
102        ThisFlexcommIncludesTheI2CFunction = 1
103    ],
104    /// I 2S present indicator. This field is Read-only.
105    I2SPRESENT OFFSET(7) NUMBITS(1) [
106        /// This Flexcomm does not include the I2S function.
107        ThisFlexcommDoesNotIncludeTheI2SFunction = 0,
108        /// This Flexcomm includes the I2S function.
109        ThisFlexcommIncludesTheI2SFunction = 1
110    ],
111    /// Flexcomm ID.
112    ID OFFSET(12) NUMBITS(20) []
113],
114PID [
115    /// size aperture for the register port on the bus (APB or AHB).
116    APERTURE OFFSET(0) NUMBITS(8) [],
117    /// Minor revision of module implementation.
118    MINOR_REV OFFSET(8) NUMBITS(4) [],
119    /// Major revision of module implementation.
120    MAJOR_REV OFFSET(12) NUMBITS(4) [],
121    /// Module identifier for the selected function.
122    ID OFFSET(16) NUMBITS(16) []
123]
124];
125
126/// A driver for a generic Flexcomm peripheral.
127pub struct Flexcomm {
128    regs: StaticRef<FlexcommRegisters>,
129}
130
131impl Flexcomm {
132    pub const fn new(base_addr: usize) -> Self {
133        Flexcomm {
134            regs: unsafe { StaticRef::new(base_addr as *const FlexcommRegisters) },
135        }
136    }
137
138    pub const fn new_id(id: u32) -> Option<Self> {
139        let base_addr = match id {
140            0 => FLEXCOMM0_BASE,
141            1 => FLEXCOMM1_BASE,
142            2 => FLEXCOMM2_BASE,
143            3 => FLEXCOMM3_BASE,
144            4 => FLEXCOMM4_BASE,
145            5 => FLEXCOMM5_BASE,
146            6 => FLEXCOMM6_BASE,
147            7 => FLEXCOMM7_BASE,
148            _ => return None,
149        };
150
151        Some(Flexcomm {
152            regs: { base_addr },
153        })
154    }
155
156    /// Configures this Flexcomm to be a UART and locks the selection.
157    pub fn configure_for_uart(&self) {
158        self.regs
159            .pselid
160            .write(PERSEL::USARTFunctionSelected + LOCK::SET);
161    }
162}