rp2350/xosc.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 OxidOS Automotive 2025.
4
5use kernel::utilities::registers::interfaces::{ReadWriteable, Readable};
6use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite};
7use kernel::utilities::StaticRef;
8
9register_structs! {
10 /// Controls the crystal oscillator
11 XoscRegisters {
12 /// Crystal Oscillator Control
13 (0x000 => ctrl: ReadWrite<u32, CTRL::Register>),
14 /// Crystal Oscillator Status
15 (0x004 => status: ReadWrite<u32, STATUS::Register>),
16 /// Crystal Oscillator pause control.
17 /// This is used to save power by pausing the XOSC.
18 /// On power-up this field is initialised to WAKE.
19 /// An invalid write will also select WAKE.
20 /// WARNING: stop the PLLs before selecting dormant mode.
21 /// WARNING: setup the irq before selecting dormant mode
22 (0x008 => dormant: ReadWrite<u32, DORMANT::Register>),
23 /// Controls the startup delay
24 (0x00C => startup: ReadWrite<u32, STARTUP::Register>),
25 /// A down counter running at the xosc frequency which counts to zero and stops..
26 /// To start the counter write a non-zero value.
27 /// Can be used for short software pauses when setting up time sensitive
28 (0x010 => count: ReadWrite<u32>),
29 (0x014 => @END),
30 }
31}
32
33register_bitfields![u32,
34CTRL [
35 /// On power-up this field is initialised to DISABLE and the chip runs from the ROSC.
36/// If the chip has subsequently been programmed to run from the XOSC then setting this field to DISABLE may lock-up the chip. If this is a concern then run the clk_ref from the ROSC and enable the clk_sys RESUS feature.
37/// The 12-bit code is intended to give some protection against accidental writes. An invalid setting will retain the previous value. The actual value being used can be read from STATUS_ENABLED
38 ENABLE OFFSET(12) NUMBITS(12) [
39 DISABLE = 0xd1e,
40 ENABLE = 0xfab,
41 ],
42 /// The 12-bit code is intended to give some protection against accidental writes. An invalid setting will retain the previous value. The actual value being used can be read from STATUS_FREQ_RANGE
43 FREQ_RANGE OFFSET(0) NUMBITS(12) [
44 FREQ_1_15MHZ = 0xaa0,
45 FREQ_10_30MHZ = 0xaa1,
46 FREQ_25_60MHZ = 0xaa2,
47 FREQ_40_100MHZ = 0xaa3,
48 ]
49],
50STATUS [
51 /// Oscillator is running and stable
52 STABLE OFFSET(31) NUMBITS(1) [],
53 /// An invalid value has been written to CTRL_ENABLE or CTRL_FREQ_RANGE or DORMANT
54 BADWRITE OFFSET(24) NUMBITS(1) [],
55 /// Oscillator is enabled but not necessarily running and stable, resets to 0
56 ENABLED OFFSET(12) NUMBITS(1) [],
57 /// The current frequency range setting
58 FREQ_RANGE OFFSET(0) NUMBITS(2) [
59 FREQ_1_15MHZ = 0x0,
60 FREQ_10_30MHZ = 0x1,
61 FREQ_25_60MHZ = 0x2,
62 FREQ_40_100MHZ = 0x3,
63 ]
64],
65DORMANT [
66 /// This is used to save power by pausing the XOSC
67/// On power-up this field is initialised to WAKE
68/// An invalid write will also select WAKE
69/// Warning: stop the PLLs before selecting dormant mode
70/// Warning: setup the irq before selecting dormant mode
71 VALUE OFFSET(0) NUMBITS(32) [
72 DORMANT = 0x636f6d61,
73 WAKE = 0x77616b65,
74 ]
75],
76STARTUP [
77 /// Multiplies the startup_delay by 4, just in case. The reset value is controlled by a mask-programmable tiecell and is provided in case we are booting from XOSC and the default startup delay is insufficient. The reset value is 0x0.
78 X4 OFFSET(20) NUMBITS(1) [],
79 /// in multiples of 256*xtal_period. The reset value of 0xc4 corresponds to approx 50 000 cycles.
80 DELAY OFFSET(0) NUMBITS(14) []
81],
82COUNT [
83
84 COUNT OFFSET(0) NUMBITS(16) []
85]
86];
87
88const XOSC_BASE: StaticRef<XoscRegisters> =
89 unsafe { StaticRef::new(0x40048000 as *const XoscRegisters) };
90
91pub struct Xosc {
92 registers: StaticRef<XoscRegisters>,
93}
94
95impl Xosc {
96 pub const fn new() -> Self {
97 Self {
98 registers: XOSC_BASE,
99 }
100 }
101
102 pub fn init(&self) {
103 self.registers.ctrl.modify(CTRL::FREQ_RANGE::FREQ_1_15MHZ);
104 // This delay is from the RP2350 manual, page 552, section 8.2.4, and from the Pico SDK
105 // implementation of the XOSC driver.
106 let startup_delay = (((12 * 1000000) / 1000) + 128) / 256;
107 self.registers
108 .startup
109 .modify(STARTUP::DELAY.val(startup_delay));
110 self.registers.ctrl.modify(CTRL::ENABLE::ENABLE);
111 // wait for the oscillator to become stable
112 while !self.registers.status.is_set(STATUS::STABLE) {}
113 }
114
115 pub fn disable(&self) {
116 self.registers.ctrl.modify(CTRL::ENABLE::DISABLE);
117 }
118
119 /// disable the oscillator until an interrupt arrives
120 pub fn dormant(&self) {
121 self.registers.dormant.modify(DORMANT::VALUE::DORMANT);
122 // wait for the oscillator to become stable
123 while !self.registers.status.is_set(STATUS::STABLE) {}
124 }
125}