rp2040/
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 Tock Contributors 2022.
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\n
17        /// This is used to save power by pausing the XOSC\n
18        /// On power-up this field is initialised to WAKE\n
19        /// An invalid write will also select WAKE\n
20        /// WARNING: stop the PLLs before selecting dormant mode\n
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        (0x010 => _reserved0),
26        /// A down counter running at the xosc frequency which counts to zero and stops.\n
27        /// To start the counter write a non-zero value.\n
28        /// Can be used for short software pauses when setting up time sensitive
29        (0x01C => count: ReadWrite<u32>),
30        (0x020 => @END),
31    }
32}
33
34register_bitfields![u32,
35    CTRL [
36        /// On power-up this field is initialised to DISABLE and the chip runs from the ROSC
37        /// If the chip has subsequently been programmed to run from the XOS
38        /// The 12-bit code is intended to give some protection against acci
39        ENABLE OFFSET(12) NUMBITS(12) [
40            ENABLE = 0xfab,
41            DISABLE = 0xd1e
42        ],
43        /// Frequency range. This resets to 0xAA0 and cannot be changed.
44        FREQ_RANGE OFFSET(0) NUMBITS(12) [
45
46            _1_15MHZ = 0xaa0
47        ]
48    ],
49    STATUS [
50        /// Oscillator is running and stable
51        STABLE OFFSET(31) NUMBITS(1) [],
52        /// An invalid value has been written to CTRL_ENABLE or CTRL_FREQ_RANGE or DORMANT
53        BADWRITE OFFSET(24) NUMBITS(1) [],
54        /// Oscillator is enabled but not necessarily running and stable, resets to 0
55        ENABLED OFFSET(12) NUMBITS(1) [],
56        /// The current frequency range setting, always reads 0
57        FREQ_RANGE OFFSET(0) NUMBITS(2) [
58
59            _1_15MHZ = 0
60        ]
61    ],
62    DORMANT [
63        VALUE OFFSET (0) NUMBITS (32) [
64            DORMANT = 0x636f6d61,
65            WAKE = 0x77616b65
66        ]
67    ],
68    STARTUP [
69        /// Multiplies the startup_delay by 4. This is of little value to the user given tha
70        X4 OFFSET(20) NUMBITS(1) [],
71        /// in multiples of 256*xtal_period
72        DELAY OFFSET(0) NUMBITS(14) []
73    ],
74    COUNT [
75
76        COUNT OFFSET(0) NUMBITS(8) []
77    ]
78];
79
80const XOSC_BASE: StaticRef<XoscRegisters> =
81    unsafe { StaticRef::new(0x40024000 as *const XoscRegisters) };
82
83pub struct Xosc {
84    registers: StaticRef<XoscRegisters>,
85}
86
87impl Xosc {
88    pub const fn new() -> Self {
89        Self {
90            registers: XOSC_BASE,
91        }
92    }
93
94    pub fn init(&self) {
95        // there is only one frequency range available
96        // RP2040 Manual https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf section 2.16.7
97        self.registers.ctrl.modify(CTRL::FREQ_RANGE::_1_15MHZ);
98        let startup_delay = (((12 * 1000000) / 1000) + 128) / 256;
99        self.registers
100            .startup
101            .modify(STARTUP::DELAY.val(startup_delay));
102        self.registers.ctrl.modify(CTRL::ENABLE::ENABLE);
103        // wait for the oscillator to become stable
104        while !self.registers.status.is_set(STATUS::STABLE) {}
105    }
106
107    pub fn disable(&self) {
108        self.registers.ctrl.modify(CTRL::ENABLE::DISABLE);
109    }
110
111    /// disable the oscillator until an interrupt arrives
112    pub fn dormant(&self) {
113        self.registers.dormant.modify(DORMANT::VALUE::DORMANT);
114        // wait for the oscillator to become stable
115        while !self.registers.status.is_set(STATUS::STABLE) {}
116    }
117}