ws63_hal/clock.rs
1//! Clock definitions for WS63.
2//!
3//! The WS63 uses a CLDO_CRG (Clock and Reset Generator) for peripheral clock
4//! gating. Clocks default to **enabled** out of reset, so the drivers do not
5//! gate them; this module keeps the [`Peripheral`] enum and its CKEN bit map
6//! ([`Peripheral::cken_info`]) as a peripheral → clock-gate reference, used by
7//! `safety.rs`'s drift checks and available to future clock-gating code.
8//!
9//! CKEN-bit provenance (audited against fbb_ws63 porting + the WS63 SVD):
10//! - **SDK/SVD-confirmed**: PWM `CKEN_CTL0` bits [10:2] (base 2; `pwm_porting.c`),
11//! I2S `CKEN_CTL0` bit 11 (bus) + bit 12 (clk) (`sio_porting.c`), UART0/1/2
12//! `CKEN_CTL1` bits 18/19/20 (`clock_init.c` + SVD `uart_cken[20:18]`), SPI
13//! `CKEN_CTL1` bit 25 (`spi_porting.c` + SVD `spi_cken[25]`).
14//! - **Not individually gated by the SDK** (rely on the reset-default clock; the
15//! bit is not attested by the SVD or porting code): I2C, Timer, LSADC, Tsensor,
16//! TRNG, Security, DMA, SDMA, SFC, SPI1 — `cken_info` returns `None` for these
17//! rather than fabricating a bit. WiFi/BT entry gates (`CKEN_CTL1` 13 / 8–12 /
18//! 29) are owned by the radio blobs and are not in this enum.
19//!
20//! The earlier `ClockControl` / `PeripheralGuard` RAII layer was removed: it had
21//! zero consumers (the drivers rely on the reset-default clocks) and was dead
22//! scaffolding. Re-introduce a clock-gating API alongside a real consumer if one
23//! is needed, deriving the gate bits from [`Peripheral::cken_info`].
24
25/// Enumeration of all peripheral clocks.
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum Peripheral {
28 Uart0,
29 Uart1,
30 Uart2,
31 I2c0,
32 I2c1,
33 Spi0,
34 Spi1,
35 Pwm,
36 Timer,
37 Lsadc,
38 Tsensor,
39 I2s,
40 Dma,
41 Sdma,
42 Sfc,
43 Trng,
44 SecurityGroup,
45}
46
47impl Peripheral {
48 /// The CLDO_CRG clock-gate register index (0 = `CKEN_CTL0`, 1 = `CKEN_CTL1`)
49 /// and bit position for this peripheral.
50 ///
51 /// The CLDO_CRG clock-gate register index (0 = `CKEN_CTL0`, 1 = `CKEN_CTL1`)
52 /// and bit position for this peripheral, or `None` if the SDK does **not**
53 /// individually gate it (it relies on the reset-default clock; no bit is
54 /// attested by the SVD or porting code).
55 ///
56 /// PWM occupies 9 contiguous gates (`CKEN_CTL0` bits 2..=10); this returns its
57 /// base bit (2). I2S returns its clk gate (bit 12); it also has a bus gate at
58 /// bit 11. See the module docs for the provenance of each.
59 pub fn cken_info(&self) -> Option<(u8, u8)> {
60 match self {
61 // ── SDK/SVD-confirmed gates ──
62 Peripheral::Pwm => Some((0, 2)), // CKEN_CTL0 [10:2], base bit 2 (pwm_porting.c)
63 Peripheral::I2s => Some((0, 12)), // CKEN_CTL0 bit 12 (clk); bit 11 = bus (sio_porting.c)
64 Peripheral::Uart0 => Some((1, 18)), // SVD uart_cken[20:18] + clock_init.c
65 Peripheral::Uart1 => Some((1, 19)),
66 Peripheral::Uart2 => Some((1, 20)),
67 Peripheral::Spi0 => Some((1, 25)), // SVD spi_cken[25] + spi_porting.c
68 // ── Not individually gated by the SDK (default-on) — no authoritative bit ──
69 Peripheral::I2c0
70 | Peripheral::I2c1
71 | Peripheral::Timer
72 | Peripheral::Lsadc
73 | Peripheral::Tsensor
74 | Peripheral::Trng
75 | Peripheral::SecurityGroup
76 | Peripheral::Dma
77 | Peripheral::Sdma
78 | Peripheral::Sfc
79 | Peripheral::Spi1 => None,
80 }
81 }
82}
83
84/// Number of [`Peripheral`] enum variants. Update when adding variants.
85pub const PERIPHERAL_COUNT: usize = 17;
86
87// ── Tests ──────────────────────────────────────────────────────
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use crate::soc::ws63::SYSTEM_CLOCK_HZ;
93
94 #[test]
95 fn test_system_clock_240mhz() {
96 assert_eq!(SYSTEM_CLOCK_HZ, 240_000_000);
97 }
98
99 #[test]
100 fn test_peripheral_count() {
101 assert_eq!(PERIPHERAL_COUNT, 17);
102 }
103
104 #[test]
105 fn test_peripheral_cken_info_bounds_and_gating() {
106 // SDK/SVD-confirmed gates return Some with in-range (reg, bit).
107 let gated = [
108 Peripheral::Pwm,
109 Peripheral::I2s,
110 Peripheral::Uart0,
111 Peripheral::Uart1,
112 Peripheral::Uart2,
113 Peripheral::Spi0,
114 ];
115 for p in &gated {
116 let (reg, bit) = p.cken_info().unwrap_or_else(|| panic!("{:?} should be gated", p));
117 assert!(reg <= 1, "Peripheral {:?} has invalid reg={}", p, reg);
118 assert!(bit < 32, "Peripheral {:?} has invalid bit={}", p, bit);
119 }
120 // Peripherals the SDK does not individually gate return None (not a fake bit).
121 let ungated = [
122 Peripheral::I2c0,
123 Peripheral::I2c1,
124 Peripheral::Timer,
125 Peripheral::Lsadc,
126 Peripheral::Tsensor,
127 Peripheral::Trng,
128 Peripheral::SecurityGroup,
129 Peripheral::Dma,
130 Peripheral::Sdma,
131 Peripheral::Sfc,
132 Peripheral::Spi1,
133 ];
134 for p in &ungated {
135 assert_eq!(p.cken_info(), None, "Peripheral {:?} should not be gated", p);
136 }
137 }
138
139 #[test]
140 fn test_pwm_cken_info_returns_base_bit() {
141 assert_eq!(Peripheral::Pwm.cken_info(), Some((0, 2)));
142 }
143
144 #[test]
145 fn test_i2s_cken_info_is_clk_gate() {
146 // I2S clk gate is CKEN_CTL0 bit 12 (was wrongly bit 24 before the SDK audit).
147 assert_eq!(Peripheral::I2s.cken_info(), Some((0, 12)));
148 }
149
150 #[test]
151 fn test_peripheral_variants_are_unique() {
152 let variants: [Peripheral; 17] = [
153 Peripheral::Uart0,
154 Peripheral::Uart1,
155 Peripheral::Uart2,
156 Peripheral::I2c0,
157 Peripheral::I2c1,
158 Peripheral::Spi0,
159 Peripheral::Spi1,
160 Peripheral::Pwm,
161 Peripheral::Timer,
162 Peripheral::Lsadc,
163 Peripheral::Tsensor,
164 Peripheral::I2s,
165 Peripheral::Dma,
166 Peripheral::Sdma,
167 Peripheral::Sfc,
168 Peripheral::Trng,
169 Peripheral::SecurityGroup,
170 ];
171 for i in 0..variants.len() {
172 for j in (i + 1)..variants.len() {
173 assert_ne!(variants[i], variants[j]);
174 }
175 }
176 }
177}