sam4l/
bpm.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
5//! Implementation of the BPM peripheral.
6
7use kernel::utilities::registers::interfaces::{Readable, Writeable};
8use kernel::utilities::registers::{register_bitfields, ReadOnly, ReadWrite, WriteOnly};
9use kernel::utilities::StaticRef;
10
11#[repr(C)]
12struct BpmRegisters {
13    ier: WriteOnly<u32, Interrupt::Register>,
14    idr: WriteOnly<u32, Interrupt::Register>,
15    imr: ReadOnly<u32, Interrupt::Register>,
16    isr: ReadOnly<u32, Interrupt::Register>,
17    icr: WriteOnly<u32, Interrupt::Register>,
18    sr: ReadOnly<u32, Status::Register>,
19    unlock: ReadWrite<u32, Unlock::Register>,
20    pmcon: ReadWrite<u32, PowerModeControl::Register>,
21    _reserved0: [u32; 2],
22    bkupwcause: ReadOnly<u32, BackupWakeup::Register>,
23    bkupwen: ReadWrite<u32, BackupWakeup::Register>,
24    bkuppmux: ReadWrite<u32, BackupPinMuxing::Register>,
25    ioret: ReadWrite<u32, InputOutputRetention::Register>,
26}
27
28register_bitfields![u32,
29    Interrupt [
30        /// Access Error
31        AE 31,
32        /// Power Scaling OK
33        PSOK 0
34    ],
35
36    Status [
37        /// Access Error
38        AE 31,
39        /// Power Scaling OK
40        PSOK 0
41    ],
42
43    Unlock [
44        /// Unlock Key
45        KEY OFFSET(24) NUMBITS(8) [],
46        /// Unlock Address
47        ADDR OFFSET(0) NUMBITS(10) []
48    ],
49
50    PowerModeControl [
51        /// Fast Wakeup
52        FASTWKUP OFFSET(24) NUMBITS(1) [
53            NormalWakeup = 0,
54            FastWakeup = 1
55        ],
56        /// 32kHz-1kHz Clock Source Selection
57        CK32S OFFSET(16) NUMBITS(1) [
58            Osc32k = 0,
59            Rc32k = 1
60        ],
61        /// SLEEP mode Configuration
62        SLEEP OFFSET(12) NUMBITS(2) [
63            CpuStopped = 0,
64            CpuAhbStopped = 1,
65            CpuAhbPbGclkStopped = 2,
66            CpuAhbPbGclkClockStopped = 3
67        ],
68        /// Retention Mode
69        RET OFFSET(9) NUMBITS(1) [
70            NoPowerSave = 0,
71            PowerSave = 1
72        ],
73        /// Backup Mode
74        BKUP OFFSET(8) NUMBITS(1) [
75            NoPowerSave = 0,
76            PowerSave = 1
77        ],
78        /// WARN: Undocumented!
79        ///
80        /// According to the datasheet (sec 6.2, p57) changing power scaling
81        /// requires waiting for an interrupt (presumably because flash is
82        /// inaccessible during the transition). However, the ASF code sets
83        /// bit 3 ('PSCM' bit) of the PMCON register, which is *blank* (not a '-')
84        /// in the datasheet with supporting comments that this allows a change
85        /// 'without CPU halt'
86        PSCM OFFSET(3) NUMBITS(1) [
87            WithCpuHalt = 0,
88            WithoutCpuHalt = 1
89        ],
90        /// Power Scaling Change Request
91        PSCREQ OFFSET(2) NUMBITS(1) [
92            PowerScalingNotRequested = 0,
93            PowerScalingRequested = 1
94        ],
95        /// Power Scaling Configuration Value
96        PS OFFSET(0) NUMBITS(2) []
97    ],
98
99    BackupWakeup [
100        BKUP OFFSET(0) NUMBITS(32) [
101            Eic =      0b000001,
102            Ast =      0b000010,
103            Wdt =      0b000100,
104            Bod33 =    0b001000,
105            Bod18 =    0b010000,
106            Picouart = 0b100000
107        ]
108    ],
109
110    BackupPinMuxing [
111        /// Backup Pin Muxing
112        BKUPPMUX OFFSET(0) NUMBITS(9) [
113            Pb01 = 0b000000001,
114            Pa06 = 0b000000010,
115            Pa04 = 0b000000100,
116            Pa05 = 0b000001000,
117            Pa07 = 0b000010000,
118            Pc03 = 0b000100000,
119            Pc04 = 0b001000000,
120            Pc05 = 0b010000000,
121            Pc06 = 0b100000000
122        ]
123    ],
124
125    InputOutputRetention [
126        /// Retention on I/O lines after waking up from the BACKUP mode
127        RET OFFSET(0) NUMBITS(1) [
128            IoLinesNotHeld = 0,
129            IoLinesHeld = 1
130        ]
131    ]
132];
133
134const BPM_UNLOCK_KEY: u32 = 0xAA;
135
136const BPM: StaticRef<BpmRegisters> = unsafe { StaticRef::new(0x400F0000 as *const BpmRegisters) };
137
138/// Which power scaling mode the chip should use for internal voltages
139///
140/// See Tables 42-6 and 42-8 (page 1125) for information of energy usage
141/// of different power scaling modes
142pub enum PowerScaling {
143    /// Mode 0: Default out of reset
144    ///
145    ///   - Maximum system clock frequency is 32MHz
146    ///   - Normal flash speed
147    PS0,
148
149    /// Mode 1: Reduced voltage
150    ///
151    ///   - Maximum system clock frequency is 12MHz
152    ///   - Normal flash speed
153    ///   - These peripherals are not available in Mode 1:
154    ///      - USB
155    ///      - DFLL
156    ///      - PLL
157    ///      - Programming/Erasing Flash
158    PS1,
159
160    /// Mode 2:
161    ///
162    ///   - Maximum system clock frequency is 48MHz
163    ///   - High speed flash
164    PS2,
165}
166
167pub enum CK32Source {
168    OSC32K = 0,
169    RC32K = 1,
170}
171
172#[inline(never)]
173pub unsafe fn set_ck32source(source: CK32Source) {
174    let control = BPM.pmcon.extract();
175    unlock_register(0x1c); // Control
176    BPM.pmcon
177        .modify_no_read(control, PowerModeControl::CK32S.val(source as u32));
178}
179
180unsafe fn unlock_register(register_offset: u32) {
181    BPM.unlock
182        .write(Unlock::KEY.val(BPM_UNLOCK_KEY) + Unlock::ADDR.val(register_offset));
183}
184
185unsafe fn power_scaling_ok() -> bool {
186    BPM.sr.is_set(Status::PSOK)
187}
188
189// This approach based on `bpm_power_scaling_cpu` from ASF
190pub unsafe fn set_power_scaling(ps_value: PowerScaling) {
191    // The datasheet says to spin on this before doing anything, ASF
192    // doesn't as far as I can tell, but it seems like a good idea
193    while !power_scaling_ok() {}
194
195    let control = BPM.pmcon.extract();
196
197    // Unlock PMCON register
198    unlock_register(0x1c); // Control
199
200    // Actually change power scaling
201    BPM.pmcon.modify_no_read(
202        control,
203        PowerModeControl::PS.val(ps_value as u32)
204            + PowerModeControl::PSCM::WithoutCpuHalt
205            + PowerModeControl::PSCREQ::PowerScalingRequested,
206    );
207}