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}