1
//! The hardware wakeup-enable mask, and the hooks that run around a sleep.
2
//!
3
//! The driver that owns the hardware enables a wakeup source. The caller of a sleep function does
4
//! not. The hardware wakeup-enable mask is the record of the request. It is a register in the
5
//! always-on domain, and it keeps its value while the chip is awake, through a light sleep, and
6
//! through a deep-sleep wake. Sleep entry reads the mask back, and calculates everything else from
7
//! it.
8
//!
9
//! A driver can also register hooks in the call that sets its mask bit. Use them to do work at
10
//! sleep entry, or to restore state after a light sleep.
11

            
12
use enumset::EnumSet;
13
use esp_sync::NonReentrantMutex;
14

            
15
use crate::{
16
    rtc_cntl::{WakeupSource, sleep::RtcSleepConfig},
17
    soc::clocks::ClockSource,
18
    time::Duration,
19
};
20

            
21
/// Which sleep the chip is entering.
22
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23
pub(crate) enum SleepKind {
24
    /// The digital domain keeps its state, and the sleep returns to the caller.
25
    Light,
26
    /// The chip resets when it wakes.
27
    Deep,
28
}
29

            
30
/// A power domain that a wakeup source needs powered while the chip sleeps.
31
///
32
/// The names are the same for all chips. A request for a domain that the target chip does not
33
/// have, or that it cannot power down, does nothing.
34
///
35
/// A clock is not a domain. Ask for one with [`WrappedSleepConfig::keep_clock_running`].
36
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37
// The names are the same for all chips, and each chip uses a different subset.
38
#[allow(dead_code, reason = "the names are the same for all chips")]
39
pub(crate) enum SleepResource {
40
    /// The low-power peripherals, including the RTC IO pads.
41
    LpPeripherals,
42
    /// The low-power memory, which holds the program and the data of a low-power core.
43
    LpMemory,
44
    /// The high-performance peripherals, including the digital GPIO pads.
45
    HpPeripherals,
46
}
47

            
48
/// The sleep configuration, as the entry hook of a wakeup source can see it.
49
///
50
/// A hook can only relax the sleep. It can keep a power domain powered, keep a clock running,
51
/// refuse a light sleep, or shorten the sleep. The caller of the sleep function selects how much
52
/// of the chip to power down, and it arms the wake timer. A wakeup source only adds the resources
53
/// that it needs, refuses the sleep, or sets a shorter limit. It cannot request a power-down, stop
54
/// a clock, cancel a refusal, or lengthen a limit.
55
///
56
/// Each request is idempotent. The order of the hooks cannot change the result. The source with
57
/// the strongest request wins. One refusal is enough. The shortest limit wins.
58
///
59
/// Keep this property. A method that requests a power-down, stops a clock, cancels a refusal, or
60
/// lengthens a limit removes it.
61
#[instability::unstable]
62
pub struct WrappedSleepConfig<'a> {
63
    config: &'a mut RtcSleepConfig,
64
    clocks: EnumSet<ClockSource>,
65
    refused: bool,
66
    limit: Option<Duration>,
67
}
68

            
69
impl<'a> WrappedSleepConfig<'a> {
70
    pub(crate) fn new(config: &'a mut RtcSleepConfig) -> Self {
71
        Self {
72
            config,
73
            clocks: EnumSet::empty(),
74
            refused: false,
75
            limit: None,
76
        }
77
    }
78

            
79
    /// Returns whether the chip is entering deep sleep, which resets it when it wakes.
80
    #[instability::unstable]
81
    pub fn is_deep_sleep(&self) -> bool {
82
        self.config.is_deep_sleep()
83
    }
84

            
85
    /// Keeps `resource` powered during the sleep.
86
    pub(crate) fn keep_alive(&mut self, resource: SleepResource) {
87
        // One function for all chips. It keeps the code of one resource together for all chips, so
88
        // that a reader can compare the chips, and a new resource needs one change only.
89
        let _config = &mut self.config;
90
        match resource {
91
            SleepResource::LpPeripherals => {
92
                cfg_select! {
93
                    // esp32h2 powers its low-power peripherals from the top domain, so it has no
94
                    // separate flag to clear.
95
                    esp32h2 => {}
96
                    soc_has_pmu => _config.pd_flags.set_pd_lp_periph(false),
97
                    _ => _config.set_rtc_peri_pd_en(false),
98
                }
99
            }
100
            SleepResource::LpMemory => {
101
                cfg_select! {
102
                    // The PMU chips keep the contents of the low-power memory through a sleep in
103
                    // retention mode, so there is no power-down to prevent.
104
                    any(esp32, esp32s2, esp32s3) => _config.set_rtc_slowmem_pd_en(false),
105
                    _ => {}
106
                }
107
            }
108
            SleepResource::HpPeripherals => {
109
                cfg_select! {
110
                    // esp32, esp32s2 and esp32h2 cannot power down the high-performance peripherals
111
                    // separately, so there is nothing to prevent.
112
                    any(esp32, esp32s2, esp32h2) => {}
113
                    soc_has_pmu => _config.pd_flags.set_pd_hp_periph(false),
114
                    _ => _config.set_dig_peri_pd_en(false),
115
                }
116
            }
117
        }
118
    }
119

            
120
    /// Keeps `source` running during the sleep.
121
    ///
122
    /// Ask a clock tree node which source it runs on, and name the answer. A source that the chip
123
    /// keeps running anyway, or that it cannot power down, needs nothing.
124
    #[instability::unstable]
125
    pub fn keep_clock_running(&mut self, source: ClockSource) {
126
        self.clocks.insert(source);
127
    }
128

            
129
    /// Refuses a light sleep.
130
    ///
131
    /// Call this when software state makes the sleep unsafe and the hardware cannot see that
132
    /// state. One call is enough. A later hook cannot cancel it.
133
    ///
134
    /// A deep sleep ignores the call. The wake resets the chip, so there is no caller to report
135
    /// the refusal to. Read [`Self::is_deep_sleep`] when other work in the hook depends on the
136
    /// kind of sleep.
137
    ///
138
    /// The hardware also rejects a sleep when an enabled source is already asserted at sleep
139
    /// entry. This call is the software refusal.
140
    #[instability::unstable]
141
    pub fn reject_sleep(&mut self) {
142
        self.refused = true;
143
    }
144

            
145
    /// Limits this sleep to `duration`.
146
    ///
147
    /// The duration starts when the driver programs the wake timer, after every hook has run. The
148
    /// shortest request wins. A later hook cannot make it longer.
149
    ///
150
    /// The driver clamps the wake timer to the limit. When the timer wakeup source is not enabled,
151
    /// the limit enables it for this sleep. Nothing else wakes the chip at the limit. After a
152
    /// light sleep the driver restores the timer. A deadline the caller armed stays armed. A timer
153
    /// that only the limit enabled is disabled again.
154
    ///
155
    /// The limit applies to deep sleep as well. The wake resets the chip. Read
156
    /// [`Self::is_deep_sleep`] when the hook should bound only one kind of sleep.
157
    ///
158
    /// A duration that the sleep transition cannot catch does not start the sleep. A light sleep
159
    /// then returns as if it had ended. A deep sleep without rejection panics, because it cannot
160
    /// return and the chip does not wake. This is not a refusal. Use [`Self::reject_sleep`] to
161
    /// refuse a light sleep. A duration of zero is too short to sleep.
162
    #[instability::unstable]
163
    pub fn limit_sleep(&mut self, duration: Duration) {
164
        let already_shorter = match self.limit {
165
            Some(current) => current.as_micros() <= duration.as_micros(),
166
            None => false,
167
        };
168
        if !already_shorter {
169
            self.limit = Some(duration);
170
        }
171
    }
172

            
173
    /// Prevents the power-down of every clock source a hook asked for.
174
    ///
175
    /// The set is what makes the requests of the hooks independent of their order.
176
    fn apply_clock_requests(&mut self) {
177
        // One function for all chips, like `keep_alive`.
178
        let _config = &mut self.config;
179
        for source in self.clocks {
180
            match source {
181
                ClockSource::XtalClk => {
182
                    cfg_select! {
183
                        soc_has_pmu => _config.pd_flags.set_pd_xtal(false),
184
                        // This flag has the opposite sense. It forces the crystal on.
185
                        _ => _config.set_xtal_fpu(true),
186
                    }
187
                }
188
                ClockSource::RcFastClk => {
189
                    cfg_select! {
190
                        soc_has_pmu => _config.pd_flags.set_pd_rc_fast(false),
191
                        _ => _config.set_int_8m_pd_en(false),
192
                    }
193
                }
194
                #[cfg(use_xtal32k)]
195
                ClockSource::Xtal32kClk => {
196
                    cfg_select! {
197
                        soc_has_pmu => _config.pd_flags.set_pd_xtal32k(false),
198
                        _ => {}
199
                    }
200
                }
201
                #[cfg(soc_has_clock_node_rc32k_clk)]
202
                ClockSource::Rc32kClk => {
203
                    cfg_select! {
204
                        // esp32h2 has no separate flag for the 32 kHz RC oscillator.
205
                        esp32h2 => {}
206
                        _ => _config.pd_flags.set_pd_rc32k(false),
207
                    }
208
                }
209
                // The slow RC oscillator runs in the always-on domain, and the external 32 kHz
210
                // oscillator arrives on a pad, so neither has a power-down to prevent.
211
                #[cfg(soc_has_clock_node_osc_slow_clk)]
212
                ClockSource::OscSlowClk => {}
213
                ClockSource::RcSlowClk => {}
214
            }
215
        }
216
    }
217
}
218

            
219
/// Runs at sleep entry, before the sleep configuration reaches hardware.
220
///
221
/// The configuration already holds the kind of the sleep, so a hook that needs it asks
222
/// [`WrappedSleepConfig::is_deep_sleep`]. A hook keeps a clock running with
223
/// [`WrappedSleepConfig::keep_clock_running`]. A hook refuses a light sleep with
224
/// [`WrappedSleepConfig::reject_sleep`].
225
///
226
/// The hook can run with interrupts disabled, for example from automatic light sleep. Do not
227
/// allocate, take a blocking lock, or log in it.
228
#[instability::unstable]
229
pub type SleepEntryHook = fn(&mut WrappedSleepConfig<'_>);
230

            
231
/// Runs after a light sleep.
232
///
233
/// It also runs when a hook refuses a light sleep. An entry hook may already have changed a pad,
234
/// and the exit hook puts that pad back. A deep sleep resets the chip, which runs the
235
/// initialization again.
236
#[instability::unstable]
237
pub type SleepExitHook = fn();
238

            
239
for_each_wakeup_source! {
240
    (all $( ($variant:ident, $bit:literal) ),*) => {
241
        /// One slot for each bit of the mask, so that a source can use its bit as the index.
242
        const HOOK_SLOTS: usize = {
243
            let mut highest = 0;
244
            $(
245
                if $bit > highest {
246
                    highest = $bit;
247
                }
248
            )*
249
            highest + 1
250
        };
251
    };
252
}
253

            
254
struct Hooks {
255
    entry: [Option<SleepEntryHook>; HOOK_SLOTS],
256
    exit: [Option<SleepExitHook>; HOOK_SLOTS],
257
}
258

            
259
/// The same lock protects the hooks and the mask bits, so the two always agree.
260
static HOOKS: NonReentrantMutex<Hooks> = NonReentrantMutex::new(Hooks {
261
    entry: [None; HOOK_SLOTS],
262
    exit: [None; HOOK_SLOTS],
263
});
264

            
265
impl WakeupSource {
266
    /// Enables this source, so that it can end a sleep.
267
    ///
268
    /// The source stays enabled until [`Self::disable`] is called. It also stays enabled through a
269
    /// sleep and through a deep-sleep wake. While the chip is awake, an enabled source does
270
    /// nothing.
271
    // On a chip where all the implemented sources need hooks, nothing calls this function.
272
    #[allow(dead_code, reason = "not every chip has such a source yet")]
273
    pub(crate) fn enable(self) {
274
        self.enable_with_hooks(None, None)
275
    }
276

            
277
    /// Enables this source and registers the hooks that run around a sleep.
278
    ///
279
    /// A source has one slot for each kind of hook, so a second call replaces the hooks of the
280
    /// first call. One driver owns each source, so only that driver can replace its own hooks.
281
    /// A call for a source that is already enabled does this.
282
    ///
283
    /// Both hooks run with the flash accessible, so they need no [`ram`][crate::ram] attribute. The
284
    /// entry hook runs before esp-hal writes the sleep configuration to hardware, and the exit hook
285
    /// runs after the wake sequence restores it. Both hooks are part of sleep entry, so keep them
286
    /// short.
287
    ///
288
    /// Only the driver that owns the source calls this. esp-hal owns the sources of its own
289
    /// drivers, for example the timer source. A call for such a source replaces the hooks of that
290
    /// driver, and the driver then does not work through a sleep.
291
    #[instability::unstable]
292
3
    pub fn enable_with_hooks(self, entry: Option<SleepEntryHook>, exit: Option<SleepExitHook>) {
293
        HOOKS.with(|hooks| {
294
45
            hooks.entry[self as usize] = entry;
295
45
            hooks.exit[self as usize] = exit;
296

            
297
            set_mask_bit(self, true);
298
        })
299
3
    }
300

            
301
    /// Disables this source, and removes its hooks.
302
    ///
303
    /// Only the driver that owns the source calls this, as for [`Self::enable_with_hooks`].
304
    #[instability::unstable]
305
3
    pub fn disable(self) {
306
        HOOKS.with(|hooks| {
307
3
            hooks.entry[self as usize] = None;
308
3
            hooks.exit[self as usize] = None;
309

            
310
            set_mask_bit(self, false);
311
        })
312
3
    }
313
}
314

            
315
/// Returns the enabled wakeup sources, as the hardware mask records them.
316
pub(crate) fn enabled_sources() -> enumset::EnumSet<WakeupSource> {
317
    enumset::EnumSet::from_u32_truncated(mask())
318
}
319

            
320
/// Returns the sources that reject the next sleep.
321
///
322
/// A source rejects a sleep if the source is already asserted when the sleep starts. Without the
323
/// rejection, the chip sleeps through the event that the caller wants to wake on. The reject
324
/// sources are the enabled wake sources that the chip can also reject on. A chip cannot reject on
325
/// every source that it can wake from. esp32 rejects on GPIO and SDIO only, and esp32c2, esp32c3,
326
/// esp32s2 and esp32s3 do not reject on a UART.
327
pub(crate) fn reject_mask() -> u32 {
328
    mask() & property!("sleep.rejectable_mask")
329
}
330

            
331
/// What the entry hooks asked for, other than power domains and clocks.
332
///
333
/// The clock requests reach the configuration before this value is returned, and so does the
334
/// wake timer. The refusal does not, because only the caller knows what to do with it.
335
pub(crate) struct SleepEntry {
336
    /// A hook called [`WrappedSleepConfig::reject_sleep`].
337
    pub(crate) refused: bool,
338

            
339
    /// How [`WrappedSleepConfig::limit_sleep`] changed the wake timer.
340
    ///
341
    /// `None` when no hook set a limit. The caller restores the timer on every path that does not
342
    /// sleep, and after a light sleep.
343
    #[cfg(sleep_has_wakeup_source_timer)]
344
    pub(crate) clamp: Option<super::timer::LimitClamp>,
345
}
346

            
347
/// Runs the sleep-entry hook of every enabled source.
348
///
349
/// One [`WrappedSleepConfig::reject_sleep`] is enough. The caller ignores the refusal for a deep
350
/// sleep. The wake resets the chip, so there is no caller to report the refusal to. The shortest
351
/// [`WrappedSleepConfig::limit_sleep`] wins.
352
///
353
/// The mask as read at sleep entry selects the hooks. A hook can enable another source. The GPIO
354
/// hook does this, because it selects between the `ext0`, `ext1` and per-pin paths. The caller
355
/// therefore reads the mask again after the hooks. It does not run the hooks again until the mask
356
/// stops to change.
357
///
358
/// The caller writes the kind of the sleep to the configuration before this call, so that the hooks
359
/// can read it.
360
pub(crate) fn run_entry_hooks(config: &mut RtcSleepConfig) -> SleepEntry {
361
    let mut wrapped = WrappedSleepConfig::new(config);
362

            
363
    for source in enabled_sources() {
364
        let hook = HOOKS.with(|hooks| hooks.entry[source as usize]);
365
        if let Some(hook) = hook {
366
            hook(&mut wrapped);
367
        }
368
    }
369

            
370
    // The clamp writes the comparator, so it runs after every hook has asked for its limit. A
371
    // source that the clamp enables has not run its entry hook. It runs here, where the requests
372
    // of a hook still reach the configuration.
373
    #[cfg(sleep_has_wakeup_source_timer)]
374
    let clamp = wrapped.limit.map(|limit| {
375
        let clamp = super::timer::clamp_to_limit(limit);
376
        clamp.apply_entry_hook(&mut wrapped);
377
        clamp
378
    });
379

            
380
    wrapped.apply_clock_requests();
381

            
382
    SleepEntry {
383
        refused: wrapped.refused,
384
        #[cfg(sleep_has_wakeup_source_timer)]
385
        clamp,
386
    }
387
}
388

            
389
/// Runs the post-wake hook of every enabled source.
390
///
391
/// A light sleep calls this after the wake. A refused light sleep calls it too, so an entry hook
392
/// can undo a pad change. A sleep that a limit ends before it starts calls it for the same reason.
393
/// A deep sleep that resets the chip does not call this.
394
pub(crate) fn run_exit_hooks() {
395
    for source in enabled_sources() {
396
        let hook = HOOKS.with(|hooks| hooks.exit[source as usize]);
397
        if let Some(hook) = hook {
398
            hook();
399
        }
400
    }
401
}
402

            
403
/// Reads the wakeup-enable mask back from hardware.
404
pub(crate) fn mask() -> u32 {
405
    let reg = cfg_select! {
406
        soc_has_pmu => crate::peripherals::PMU::regs().slp_wakeup_cntl2(),
407
        _ => crate::peripherals::LPWR::regs().wakeup_state(),
408
    };
409
    reg.read().wakeup_ena().bits() as _
410
}
411

            
412
/// Writes the wakeup-enable mask.
413
///
414
/// The function writes the field and not the register, because on esp32 the mask shares its
415
/// register with `gpio_wakeup_filter` and with the read-only `wakeup_cause`.
416
pub(crate) fn set_mask(mask: u32) {
417
    let reg = cfg_select! {
418
        soc_has_pmu => crate::peripherals::PMU::regs().slp_wakeup_cntl2(),
419
        _ => crate::peripherals::LPWR::regs().wakeup_state(),
420
    };
421
    reg.modify(|_, w| unsafe { w.wakeup_ena().bits(mask as _) });
422
}
423

            
424
fn set_mask_bit(source: WakeupSource, enable: bool) {
425
3
    let bit = 1 << source as u32;
426
    let current = mask();
427

            
428
3
    set_mask(if enable {
429
        current | bit
430
    } else {
431
        current & !bit
432
    });
433
}