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}