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}