ws63_hal/system.rs
1//! System control — clock enables, resets, and power management.
2
3use crate::peripherals::{CldoCrg, GlbCtlM, SysCtl0};
4
5/// System control handle.
6///
7/// Holds the SYS_CTL0, GLB_CTL_M, and CLDO_CRG peripherals for clock and
8/// reset configuration.
9pub struct System<'d> {
10 pub sys_ctl0: SysCtl0<'d>,
11 pub glb_ctl_m: GlbCtlM<'d>,
12 pub cldo_crg: CldoCrg<'d>,
13}
14
15impl<'d> System<'d> {
16 pub fn new(sys_ctl0: SysCtl0<'d>, glb_ctl_m: GlbCtlM<'d>, cldo_crg: CldoCrg<'d>) -> Self {
17 Self { sys_ctl0, glb_ctl_m, cldo_crg }
18 }
19}
20
21/// Reason for the last reset.
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum ResetReason {
24 /// Power-on reset (cold boot).
25 PowerOn,
26 /// External reset pin.
27 ExternalPin,
28 /// Watchdog timer reset.
29 Watchdog,
30 /// Software reset.
31 Software,
32 /// Brown-out reset.
33 BrownOut,
34 /// Unknown reset reason.
35 Unknown,
36}
37
38// Reset registers, from fbb_ws63 drivers/chips/ws63/porting/reboot/reboot_porting.c:
39// chip-reset trigger: GLB_CTL_M (0x4000_2000) + 0x110, set bit 2 (HAL_CHIP_RESET_REG)
40// reset-reason record: GLB_CTL (0x4000_0000) + 0xA0 (SYS_RST_RECORD_0)
41// reason-clear: GLB_CTL (0x4000_0000) + 0xA4 (SYS_DIAG_CLR_1)
42const CHIP_RESET_REG: *mut u32 = 0x4000_2110 as *mut u32;
43const CHIP_RESET_ENABLE_BIT: u32 = 1 << 2;
44const SYS_RST_RECORD_0: *mut u32 = 0x4000_00A0 as *mut u32;
45const SYS_DIAG_CLR_1: *mut u32 = 0x4000_00A4 as *mut u32;
46// History bits in SYS_RST_RECORD_0.
47const SYS_WDT_RST_HIS: u32 = 0x1;
48const SYS_SOFT_RST_HIS: u32 = 0x2;
49const POR_RST_FILTER_HIS: u32 = 0x8;
50
51impl System<'_> {
52 /// Read (and clear) the last reset reason from `SYS_RST_RECORD_0`.
53 ///
54 /// Decodes the WS63 reset-history record (`reboot_port_get_rst_reason`):
55 /// watchdog takes precedence over software over power-on. The matched bit is
56 /// cleared via `SYS_DIAG_CLR_1` so the next boot reports its own cause.
57 /// Reasons this SoC's record does not distinguish (`ExternalPin`, `BrownOut`)
58 /// are never returned here. An empty record reads back as [`ResetReason::Unknown`].
59 pub fn reset_reason(&self) -> ResetReason {
60 let val = unsafe { core::ptr::read_volatile(SYS_RST_RECORD_0) };
61 let (reason, clr) = if val & SYS_WDT_RST_HIS != 0 {
62 (ResetReason::Watchdog, SYS_WDT_RST_HIS)
63 } else if val & SYS_SOFT_RST_HIS != 0 {
64 (ResetReason::Software, SYS_SOFT_RST_HIS)
65 } else if val & POR_RST_FILTER_HIS != 0 {
66 (ResetReason::PowerOn, POR_RST_FILTER_HIS)
67 } else {
68 (ResetReason::Unknown, 0)
69 };
70 if clr != 0 {
71 unsafe { core::ptr::write_volatile(SYS_DIAG_CLR_1, clr) };
72 }
73 reason
74 }
75
76 /// Trigger a full software reset of the chip and never return.
77 ///
78 /// Sets the chip-reset enable bit (bit 2) of `GLB_CTL_M + 0x110`, the same
79 /// register `reboot_port_reboot_chip` uses. The CPU is reset before the
80 /// following spin loop completes.
81 pub fn software_reset(&self) -> ! {
82 unsafe {
83 let v = core::ptr::read_volatile(CHIP_RESET_REG);
84 core::ptr::write_volatile(CHIP_RESET_REG, v | CHIP_RESET_ENABLE_BIT);
85 }
86 loop {
87 core::hint::spin_loop();
88 }
89 }
90
91 /// Trigger a software reset and never return.
92 ///
93 /// WS63's porting layer exposes only a whole-chip reset, so this is an alias
94 /// of [`software_reset`](Self::software_reset).
95 pub fn software_reset_cpu(&self) -> ! {
96 self.software_reset()
97 }
98}
99
100/// Clocks after configuration.
101#[derive(Debug, Clone, Copy)]
102pub struct Clocks {
103 /// System (CPU) clock frequency in Hz.
104 pub sysclk: u32,
105 /// Peripheral bus clock frequency in Hz.
106 pub pclk: u32,
107}
108
109impl Default for Clocks {
110 fn default() -> Self {
111 Self { sysclk: crate::soc::ws63::SYSTEM_CLOCK_HZ, pclk: crate::soc::ws63::SYSTEM_CLOCK_HZ }
112 }
113}