Skip to main content

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