Skip to main content

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}