e310x_hal/pmu.rs
1//! PMU Extension
2#![allow(missing_docs)]
3use e310x::{Backup, Pmu, Rtc};
4
5/// Backup register size in bytes
6const BACKUP_REGISTER_BYTES: usize = 4;
7
8const BACKUP_LEN: usize = 16;
9
10/// value required written to pmukey register before writing to other PMU registers
11pub const PMU_KEY_VAL: u32 = 0x51F15E;
12
13// HiFive1 (Rev A) programs
14#[cfg(not(feature = "g002"))]
15const DEFAULT_SLEEP_PROGRAM: [u32; 8] = [
16 0x0F0, // assert corerst
17 0x1F0, // assert hfclkrst
18 0x1D0, // deassert pmu_out_1
19 0x1C0, // deassert pmu_out_0
20 0x1C0, // repeats
21 0x1C0, 0x1C0, 0x1C0,
22];
23
24#[cfg(not(feature = "g002"))]
25const DEFAULT_WAKE_PROGRAM: [u32; 8] = [
26 0x1F0, // assert all resets and enable all power supplies
27 0x0F8, // idle 2^8 cycles, then deassert hfclkrst
28 0x030, // deassert corerst and padrst
29 0x030, // repeats
30 0x030, 0x030, 0x030, 0x030,
31];
32
33// HiFive1 Rev B programs
34#[cfg(feature = "g002")]
35const DEFAULT_SLEEP_PROGRAM: [u32; 8] = [
36 0x2F0, // assert corerst
37 0x3F0, // assert hfclkrst
38 0x3D0, // deassert pmu_out_1
39 0x3C0, // deassert pmu_out_0
40 0x3C0, // repeats
41 0x3C0, 0x3C0, 0x3C0,
42];
43
44#[cfg(feature = "g002")]
45const DEFAULT_WAKE_PROGRAM: [u32; 8] = [
46 0x3F0, // assert all resets and enable all power supplies
47 0x2F8, // idle 2^8 cycles, then deassert hfclkrst
48 0x030, // deassert corerst and padrst
49 0x030, // repeats
50 0x030, 0x030, 0x030, 0x030,
51];
52
53///
54/// Enumeration of device reset causes
55///
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum ResetCause {
58 /// Reset due to power on
59 PowerOn,
60 /// Reset due to external input (button)
61 External,
62 /// Reset due to watchdog
63 WatchDog,
64}
65
66///
67/// Enumeration of device wakeup causes
68///
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum WakeupCause {
71 /// Wake up due to reset (see ResetCause)
72 Reset(ResetCause),
73 /// Wake up due to RTC clock
74 RTC,
75 /// Wake up due to digital input (button)
76 Digital,
77}
78
79///
80/// Errors for user data backup procedures
81///
82#[derive(Debug)]
83pub enum BackupError {
84 /// Emitted when user data is larger than backup registers capacity
85 DataTooLarge,
86 /// Emitted when user data size is not divisible by 4 bytes
87 DataSizeInvalid,
88}
89
90///
91/// Wakeup/Reset cause errors
92///
93#[derive(Debug)]
94pub enum CauseError {
95 /// Emitted if an unknown wakeup or reset cause is encountered
96 InvalidCause,
97}
98
99pub trait PMUExt {
100 ///
101 /// Resets SLEEP and WAKE programs on the PMU to defaults
102 ///
103 fn load_default_programs(&self);
104
105 ///
106 /// Puts device to sleep for N seconds, allowing wake-up button to wake it up as well
107 ///
108 /// # Arguments
109 ///
110 /// *sleep_time* - the amount of time to sleep for in seconds
111 ///
112 /// # Notes
113 ///
114 /// - enables RTC to be always on
115 /// - sets RTC scale to 1/s
116 ///
117 fn sleep(self, sleep_time: u32);
118
119 ///
120 /// Returns an enumified version of the Wakeup and Reset causes from the pmucause register
121 ///
122 /// # Returns
123 ///
124 /// * `Result<WakeupCause, CauseError>` - the cause enum is returned on success
125 ///
126 /// # Errors
127 ///
128 /// * `CauseError::InvalidCause` - returned if an unknown wakeup or reset cause is encountered
129 ///
130 fn wakeup_cause(&self) -> Result<WakeupCause, CauseError>;
131
132 ///
133 /// Stores user data `UD` to backup registers.
134 ///
135 /// # Safety
136 ///
137 /// `user_data` value must not contain un-serializable types such as pointers or references.
138 ///
139 /// `user_data` size must be divisible by 4 bytes.
140 ///
141 /// **The data is only guaranteed to be consistent when program is compiled with the same version of the compiler on store/restore.**
142 ///
143 /// `#[repr(align(4))]` can be used to enforce a minimum alignment of 4 bytes for `user_data`
144 ///
145 /// # Arguments
146 ///
147 /// * `user_data` - reference to the user data to store. `user_data` size must by divisible by 4 bytes
148 ///
149 /// # Returns
150 ///
151 /// * `Result<(), BackupError>` - `()` is returned on success
152 ///
153 /// # Errors
154 ///
155 /// * `BackupError::DataTooLarge` - returned if `user_data` cannot fit into backup registers
156 /// * `BackupError::DataSizeInvalid` - returned if `user_data` size is not divisible by 4 bytes
157 ///
158 unsafe fn store_backup<UD>(&self, user_data: &UD) -> Result<(), BackupError>;
159
160 ///
161 /// Restores user data `UD` from backup registers.
162 ///
163 /// # Safety
164 ///
165 /// `user_data` value must not contain un-serializable types such as pointers or references.
166 ///
167 /// `user_data` size must be divisible by 4 bytes.
168 ///
169 /// **The data is only guaranteed to be consistent when program is compiled with the same version of the compiler on store/restore.**
170 ///
171 /// `#[repr(align(4))]` can be used to enforce a minimum alignment of 4 bytes for `user_data`
172 ///
173 /// # Arguments
174 ///
175 /// * `user_data` - the user data to restore to. `user_data` size must by divisible by 4 bytes
176 ///
177 /// # Returns
178 ///
179 /// * `Result<(), BackupError>` - `()` is returned on success
180 ///
181 /// # Errors
182 ///
183 /// * `BackupError::DataTooLarge` - returned if `user_data` cannot fit into backup registers
184 /// * `BackupError::DataSizeInvalid` - returned if `user_data` size is not divisible by 4 bytes ///
185 ///
186 unsafe fn restore_backup<UD>(&self, user_data: &mut UD) -> Result<(), BackupError>;
187
188 ///
189 /// Clears all backup registers by setting each to zero
190 ///
191 fn clear_backup(&self);
192}
193
194impl PMUExt for Pmu {
195 fn load_default_programs(&self) {
196 unsafe {
197 for i in 0..8 {
198 self.pmukey().write(|w| w.bits(PMU_KEY_VAL));
199 self.pmusleeppm(i)
200 .write(|w| w.bits(DEFAULT_SLEEP_PROGRAM[i]));
201
202 self.pmukey().write(|w| w.bits(PMU_KEY_VAL));
203 self.pmuwakepm(i).write(|w| w.bits(DEFAULT_WAKE_PROGRAM[i]));
204 }
205 }
206 }
207
208 fn sleep(self, sleep_time: u32) {
209 unsafe {
210 let rtc = Rtc::steal();
211
212 // set interrupt source to RTC enabled, each pmu register needs key set before write
213 self.pmukey().write(|w| w.bits(PMU_KEY_VAL));
214 self.pmuie()
215 .write(|w| w.rtc().set_bit().dwakeup().set_bit());
216 // set RTC clock scale to once per second for easy calculation
217 rtc.rtccfg()
218 .write(|w| w.enalways().set_bit().scale().bits(15));
219 // get current RTC clock value scaled
220 let rtc_now = rtc.rtcs().read().bits();
221 // set RTC clock comparator
222 rtc.rtccmp().write(|w| w.bits(rtc_now + sleep_time));
223 // go to sleep for sleep_time seconds, need to set pmukey here as well
224 self.pmukey().write(|w| w.bits(PMU_KEY_VAL));
225 self.pmusleep().write(|w| w.sleep().set_bit());
226 }
227 }
228
229 fn wakeup_cause(&self) -> Result<WakeupCause, CauseError> {
230 let pmu_cause = self.pmucause().read();
231 let wakeup_cause = pmu_cause.wakeupcause();
232 if wakeup_cause.is_rtc() {
233 return Ok(WakeupCause::RTC);
234 } else if wakeup_cause.is_digital() {
235 return Ok(WakeupCause::Digital);
236 } else if wakeup_cause.is_reset() {
237 let reset_cause = pmu_cause.resetcause();
238
239 if reset_cause.is_power_on() {
240 return Ok(WakeupCause::Reset(ResetCause::PowerOn));
241 } else if reset_cause.is_external() {
242 return Ok(WakeupCause::Reset(ResetCause::External));
243 } else if reset_cause.is_watchdog() {
244 return Ok(WakeupCause::Reset(ResetCause::WatchDog));
245 }
246 }
247
248 Err(CauseError::InvalidCause)
249 }
250
251 unsafe fn store_backup<UD>(&self, user_data: &UD) -> Result<(), BackupError>
252 where
253 UD: Sized,
254 {
255 let backup = Backup::steal();
256 let ud_size = core::mem::size_of::<UD>();
257
258 if ud_size > BACKUP_LEN * BACKUP_REGISTER_BYTES {
259 return Err(BackupError::DataTooLarge);
260 }
261
262 if ud_size % BACKUP_REGISTER_BYTES != 0 {
263 return Err(BackupError::DataSizeInvalid);
264 }
265
266 let reg_count = ud_size / BACKUP_REGISTER_BYTES;
267
268 let ptr = user_data as *const _;
269 let ptr_u32 = ptr as *const u32;
270 let sliced = core::slice::from_raw_parts(ptr_u32, reg_count);
271
272 backup.backup_iter().enumerate().for_each(|(i, backup_r)| {
273 backup_r.write(|w| w.bits(sliced[i]));
274 });
275
276 Ok(())
277 }
278
279 unsafe fn restore_backup<UD>(&self, user_data: &mut UD) -> Result<(), BackupError>
280 where
281 UD: Sized,
282 {
283 let backup = Backup::steal();
284 let ud_size = core::mem::size_of::<UD>();
285
286 if ud_size > BACKUP_LEN * BACKUP_REGISTER_BYTES {
287 return Err(BackupError::DataTooLarge);
288 }
289
290 if ud_size % BACKUP_REGISTER_BYTES != 0 {
291 return Err(BackupError::DataSizeInvalid);
292 }
293
294 let reg_count = ud_size / BACKUP_REGISTER_BYTES;
295
296 let ptr = user_data as *const _;
297 let ptr_u32 = ptr as *mut u32;
298 let sliced = core::slice::from_raw_parts_mut(ptr_u32, reg_count);
299
300 backup.backup_iter().enumerate().for_each(|(i, backup_r)| {
301 sliced[i] = backup_r.read().bits();
302 });
303
304 Ok(())
305 }
306
307 fn clear_backup(&self) {
308 unsafe {
309 let backup = Backup::steal();
310
311 for backup_r in backup.backup_iter() {
312 backup_r.write(|w| w.bits(0u32));
313 }
314 }
315 }
316}