esp-hal 1.1.0

Bare-metal HAL for Espressif devices
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
use super::{TimerWakeupSource, WakeSource, WakeTriggers, WakeupLevel};
use crate::{
    gpio::{RtcFunction, RtcPinWithResistors},
    peripherals::{APB_CTRL, BB, EXTMEM, GPIO, IO_MUX, LPWR, SPI0, SPI1, SYSTEM},
    rtc_cntl::{Rtc, sleep::RtcioWakeupSource},
    soc::regi2c,
};

// Approximate mapping of voltages to RTC_CNTL_DBIAS_WAK, RTC_CNTL_DBIAS_SLP,
// RTC_CNTL_DIG_DBIAS_WAK, RTC_CNTL_DIG_DBIAS_SLP values.
// Valid if RTC_CNTL_DBG_ATTEN is 0.
/// Digital bias voltage level of 0.90V.
pub const RTC_CNTL_DBIAS_0V90: u8 = 13;
/// Digital bias voltage level of 0.95V.
pub const RTC_CNTL_DBIAS_0V95: u8 = 16;
/// Digital bias voltage level of 1.00V.
pub const RTC_CNTL_DBIAS_1V00: u8 = 18;
/// Digital bias voltage level of 1.05V.
pub const RTC_CNTL_DBIAS_1V05: u8 = 20;
/// Digital bias voltage level of 1.10V.
pub const RTC_CNTL_DBIAS_1V10: u8 = 23;
/// Digital bias voltage level of 1.15V.
pub const RTC_CNTL_DBIAS_1V15: u8 = 25;
/// Digital bias voltage level of 1.20V.
pub const RTC_CNTL_DBIAS_1V20: u8 = 28;
/// Digital bias voltage level of 1.25V.
pub const RTC_CNTL_DBIAS_1V25: u8 = 30;
/// Digital bias voltage level of approximately 1.34V.
pub const RTC_CNTL_DBIAS_1V30: u8 = 31;

/// Default attenuation setting during light sleep, with a voltage drop.
pub const RTC_CNTL_DBG_ATTEN_LIGHTSLEEP_DEFAULT: u8 = 5;
/// No attenuation (no voltage drop) during light sleep.
pub const RTC_CNTL_DBG_ATTEN_LIGHTSLEEP_NODROP: u8 = 0;
/// Default attenuation setting during deep sleep, with maximum voltage drop.
pub const RTC_CNTL_DBG_ATTEN_DEEPSLEEP_DEFAULT: u8 = 15;
/// No attenuation (no voltage drop) during deep sleep.
pub const RTC_CNTL_DBG_ATTEN_DEEPSLEEP_NODROP: u8 = 0;

/// Default bias setting during sleep mode.
pub const RTC_CNTL_BIASSLP_SLEEP_DEFAULT: u8 = 1;
/// Keeps the bias for ultra-low power sleep mode always on.
pub const RTC_CNTL_BIASSLP_SLEEP_ON: u8 = 0;

/// Default power-down current setting during sleep mode.
pub const RTC_CNTL_PD_CUR_SLEEP_DEFAULT: u8 = 1;
/// Keeps the power-down current setting for sleep mode always on.
pub const RTC_CNTL_PD_CUR_SLEEP_ON: u8 = 0;

/// Default driver bias setting for the digital domain during sleep mode.
pub const RTC_CNTL_DG_VDD_DRV_B_SLP_DEFAULT: u8 = 254;

/// Default debug attenuation setting for the monitor mode.
pub const RTC_CNTL_DBG_ATTEN_MONITOR_DEFAULT: u8 = 0;
/// Default bias setting for sleep mode in the monitor mode.
pub const RTC_CNTL_BIASSLP_MONITOR_DEFAULT: bool = false;
/// Default power-down current setting for the monitor mode.
pub const RTC_CNTL_PD_CUR_MONITOR_DEFAULT: bool = false;

/// Default number of cycles to wait for the PLL buffer to stabilize.
pub const RTC_CNTL_PLL_BUF_WAIT_DEFAULT: u8 = 20;
/// Default number of cycles to wait for the XTL buffer to stabilize.
pub const RTC_CNTL_XTL_BUF_WAIT_DEFAULT: u8 = 100;
/// Default number of cycles to wait for the internal 8MHz clock to stabilize.
pub const RTC_CNTL_CK8M_WAIT_DEFAULT: u8 = 20;
/// Default number of cycles required to enable the internal 8MHz clock.
pub const RTC_CK8M_ENABLE_WAIT_DEFAULT: u8 = 5;

/// Minimum number of cycles for sleep duration.
pub const RTC_CNTL_MIN_SLP_VAL_MIN: u8 = 2;

/// Power-up cycles for other hardware blocks.
pub const OTHER_BLOCKS_POWERUP: u8 = 1;
/// Wait cycles for other hardware blocks to stabilize.
pub const OTHER_BLOCKS_WAIT: u16 = 1;

/// Disables GPIO interrupt.
pub const GPIO_INTR_DISABLE: u8 = 0;
/// Sets GPIO interrupt to trigger on a low level signal.
pub const GPIO_INTR_LOW_LEVEL: u8 = 4;
/// Sets GPIO interrupt to trigger on a high level signal.
pub const GPIO_INTR_HIGH_LEVEL: u8 = 5;

/// Specifies the function configuration for GPIO pins.
pub const PIN_FUNC_GPIO: u8 = 1;
/// Index for signaling GPIO output.
pub const SIG_GPIO_OUT_IDX: u32 = 128;
/// Maximum number of GPIO pins supported.
pub const GPIO_NUM_MAX: usize = 22;

impl WakeSource for TimerWakeupSource {
    fn apply(
        &self,
        rtc: &Rtc<'_>,
        triggers: &mut WakeTriggers,
        _sleep_config: &mut RtcSleepConfig,
    ) {
        triggers.set_timer(true);
        let rtc_cntl = LPWR::regs();
        // TODO: maybe add check to prevent overflow?
        let ticks = crate::clock::us_to_rtc_ticks(self.duration.as_micros() as u64);
        // "alarm" time in slow rtc ticks
        let now = rtc.time_since_boot_raw();
        let time_in_ticks = now + ticks;
        unsafe {
            rtc_cntl
                .slp_timer0()
                .write(|w| w.slp_val_lo().bits((time_in_ticks & 0xffffffff) as u32));

            rtc_cntl
                .int_clr()
                .write(|w| w.main_timer().clear_bit_by_one());

            rtc_cntl.slp_timer1().write(|w| {
                w.slp_val_hi().bits(((time_in_ticks >> 32) & 0xffff) as u16);
                w.main_timer_alarm_en().set_bit()
            });
        }
    }
}

impl RtcioWakeupSource<'_, '_> {
    fn apply_pin(&self, pin: &mut dyn RtcPinWithResistors, level: WakeupLevel) {
        // The pullup/pulldown part is like in gpio_deep_sleep_wakeup_prepare
        let level = match level {
            WakeupLevel::High => {
                pin.rtcio_pullup(false);
                pin.rtcio_pulldown(true);
                GPIO_INTR_HIGH_LEVEL
            }
            WakeupLevel::Low => {
                pin.rtcio_pullup(true);
                pin.rtcio_pulldown(false);
                GPIO_INTR_LOW_LEVEL
            }
        };
        pin.rtcio_pad_hold(true);

        // apply_wakeup does the same as idf's esp_deep_sleep_enable_gpio_wakeup
        unsafe {
            pin.apply_wakeup(true, level);
        }
    }
}

fn isolate_digital_gpio() {
    // like esp_sleep_isolate_digital_gpio
    let rtc_cntl = LPWR::regs();
    let io_mux = IO_MUX::regs();
    let gpio = GPIO::regs();

    let dig_iso = &rtc_cntl.dig_iso().read();
    let deep_sleep_hold_is_en =
        !dig_iso.dg_pad_force_unhold().bit() && dig_iso.dg_pad_autohold_en().bit();
    if !deep_sleep_hold_is_en {
        return;
    }

    // TODO: assert that the task stack is not in external ram

    for pin_num in 0..GPIO_NUM_MAX {
        let pin_hold = rtc_cntl.dig_pad_hold().read().bits() & (1 << pin_num) != 0;
        if !pin_hold {
            // input disable, like gpio_ll_input_disable
            io_mux.gpio(pin_num).modify(|_, w| w.fun_ie().clear_bit());
            // output disable, like gpio_ll_output_disable
            unsafe {
                gpio.func_out_sel_cfg(pin_num)
                    .modify(|_, w| w.bits(SIG_GPIO_OUT_IDX));
            }

            // disable pull-up and pull-down
            io_mux.gpio(pin_num).modify(|_, w| w.fun_wpu().clear_bit());
            io_mux.gpio(pin_num).modify(|_, w| w.fun_wpd().clear_bit());

            // make pad work as gpio (otherwise, deep_sleep bottom current will rise)
            io_mux
                .gpio(pin_num)
                .modify(|_, w| unsafe { w.mcu_sel().bits(RtcFunction::Digital as u8) });
        }
    }
}

impl WakeSource for RtcioWakeupSource<'_, '_> {
    fn apply(
        &self,
        _rtc: &Rtc<'_>,
        triggers: &mut WakeTriggers,
        sleep_config: &mut RtcSleepConfig,
    ) {
        let mut pins = self.pins.borrow_mut();

        if pins.is_empty() {
            return;
        }

        triggers.set_gpio(true);

        // If deep sleep is enabled, esp_start_sleep calls
        // gpio_deep_sleep_wakeup_prepare which sets these pullup and
        // pulldown values. But later in esp_start_sleep it calls
        // esp_sleep_isolate_digital_gpio, which disables the pullup and pulldown (but
        // only if it isn't held).
        // But it looks like gpio_deep_sleep_wakeup_prepare enables hold for all pins
        // in the wakeup mask.
        //
        // So: all pins in the wake mask should get this treatment here, and all pins
        // not in the wake mask should get
        // - pullup and pulldowns disabled
        // - input and output disabled, and
        // - their func should get set to GPIO.
        // But this last block of things gets skipped if hold is disabled globally (see
        // gpio_ll_deep_sleep_hold_is_en)

        LPWR::regs()
            .cntl_gpio_wakeup()
            .modify(|_, w| w.gpio_pin_clk_gate().set_bit());

        LPWR::regs()
            .ext_wakeup_conf()
            .modify(|_, w| w.gpio_wakeup_filter().set_bit());

        if sleep_config.deep_slp() {
            for (pin, level) in pins.iter_mut() {
                self.apply_pin(*pin, *level);
            }

            isolate_digital_gpio();
        }

        // like rtc_cntl_ll_gpio_clear_wakeup_status, as called from
        // gpio_deep_sleep_wakeup_prepare
        LPWR::regs()
            .cntl_gpio_wakeup()
            .modify(|_, w| w.gpio_wakeup_status_clr().set_bit());
        LPWR::regs()
            .cntl_gpio_wakeup()
            .modify(|_, w| w.gpio_wakeup_status_clr().clear_bit());
    }
}

// impl Drop for RtcioWakeupSource<'_, '_> {
// fn drop(&mut self) {
// should we have saved the pin configuration first?
// set pin back to IO_MUX (input_enable and func have no effect when pin is sent
// to IO_MUX)
// let mut pins = self.pins.borrow_mut();
// for (pin, _level) in pins.iter_mut() {
// pin.rtc_set_config(true, false, RtcFunction::Rtc);
// }
// }
// }

bitfield::bitfield! {
    #[derive(Clone, Copy)]
    /// RTC Configuration.
    pub struct RtcConfig(u32);
    impl Debug;
    /// Number of rtc_fast_clk cycles to wait for 8M clock to be ready
    pub u8, ck8m_wait, set_ck8m_wait: 7, 0;
    /// Number of rtc_fast_clk cycles to wait for XTAL clock to be ready
    pub u8, xtal_wait, set_xtal_wait: 15, 8;
    /// Number of rtc_fast_clk cycles to wait for PLL clock to be ready
    pub u8, pll_wait, set_pll_wait: 23, 16;
    /// Perform clock control related initialization.
    pub clkctl_init, set_clkctl_init: 24;
    /// Perform power control related initialization.
    pub pwrctl_init, set_pwrctl_init: 25;
    /// Force power down RTC_DBOOST
    pub rtc_dboost_fpd, set_rtc_dboost_fpd: 26;
    /// Keep the XTAL oscillator powered up in sleep.
    pub xtal_fpu, set_xtal_fpu: 27;
    /// Keep the BBPLL oscillator powered up in sleep.
    pub bbpll_fpu, set_bbpll_fpu: 28;
    /// Enable clock gating when the CPU is in wait-for-interrupt state.
    pub cpu_waiti_clk_gate, set_cpu_waiti_clk_gate: 29;
    /// Calibrate Ocode to make bandgap voltage more precise.
    pub cali_ocode, set_cali_ocode: 30;
}

impl Default for RtcConfig {
    fn default() -> Self {
        let mut cfg = Self(Default::default());
        cfg.set_ck8m_wait(RTC_CNTL_CK8M_WAIT_DEFAULT);
        cfg.set_xtal_wait(RTC_CNTL_XTL_BUF_WAIT_DEFAULT);
        cfg.set_pll_wait(RTC_CNTL_PLL_BUF_WAIT_DEFAULT);
        cfg.set_clkctl_init(true);
        cfg.set_pwrctl_init(true);
        cfg.set_rtc_dboost_fpd(true);
        cfg.set_cpu_waiti_clk_gate(true);
        cfg.set_bbpll_fpu(true);
        cfg
    }
}

bitfield::bitfield! {
    #[derive(Clone, Copy)]
    /// Configuration for RTC initialization.
    pub struct RtcInitConfig(u128);
    impl Debug;
    /// Number of cycles required to power up WiFi
    pub u8, wifi_powerup_cycles, set_wifi_powerup_cycles: 6, 0;
    /// Number of wait cycles for WiFi to stabilize
    pub u16, wifi_wait_cycles, set_wifi_wait_cycles: 15, 7;
    /// Number of cycles required to power up Bluetooth
    pub u8, bt_powerup_cycles, set_bt_powerup_cycles: 22, 16;
    /// Number of wait cycles for Bluetooth to stabilize
    pub u16, bt_wait_cycles, set_bt_wait_cycles: 31, 23;
    /// Number of cycles required to power up the top CPU
    pub u8, cpu_top_powerup_cycles, set_cpu_top_powerup_cycles: 38, 32;
    /// Number of wait cycles for the top CPU to stabilize
    pub u16, cpu_top_wait_cycles, set_cpu_top_wait_cycles: 47, 39;
    /// Number of cycles required to power up the digital wrapper
    pub u8, dg_wrap_powerup_cycles, set_dg_wrap_powerup_cycles: 54, 48;
    /// Number of wait cycles for the digital wrapper to stabilize
    pub u16, dg_wrap_wait_cycles, set_dg_wrap_wait_cycles: 63, 55;
    /// Number of cycles required to power up the digital peripherals
    pub u8, dg_peri_powerup_cycles, set_dg_peri_powerup_cycles: 70, 64;
    /// Number of wait cycles for the digital peripherals to stabilize
    pub u16, dg_peri_wait_cycles, set_dg_peri_wait_cycles: 79, 71;
}

impl Default for RtcInitConfig {
    fn default() -> Self {
        let mut cfg = Self(Default::default());
        cfg.set_wifi_powerup_cycles(OTHER_BLOCKS_POWERUP);
        cfg.set_bt_powerup_cycles(OTHER_BLOCKS_POWERUP);
        cfg.set_cpu_top_powerup_cycles(OTHER_BLOCKS_POWERUP);
        cfg.set_dg_wrap_powerup_cycles(OTHER_BLOCKS_POWERUP);
        cfg.set_dg_peri_powerup_cycles(OTHER_BLOCKS_POWERUP);
        cfg.set_wifi_wait_cycles(OTHER_BLOCKS_WAIT);
        cfg.set_bt_wait_cycles(OTHER_BLOCKS_WAIT);
        cfg.set_cpu_top_wait_cycles(OTHER_BLOCKS_WAIT);
        cfg.set_dg_wrap_wait_cycles(OTHER_BLOCKS_WAIT);
        cfg.set_dg_peri_wait_cycles(OTHER_BLOCKS_WAIT);
        cfg
    }
}

bitfield::bitfield! {
    #[derive(Clone, Copy)]
    /// Configuration for RTC sleep mode.
    pub struct RtcSleepConfig(u64);
    impl Debug;
    /// force normal voltage in sleep mode (digital domain memory)
    pub lslp_mem_inf_fpu, set_lslp_mem_inf_fpu: 0;
    /// keep low voltage in sleep mode (even if ULP/touch is used)
    pub rtc_mem_inf_follow_cpu, set_rtc_mem_inf_follow_cpu: 1;
    /// power down RTC fast memory
    pub rtc_fastmem_pd_en, set_rtc_fastmem_pd_en: 2;
    /// power down RTC slow memory
    pub rtc_slowmem_pd_en, set_rtc_slowmem_pd_en: 3;
    /// power down RTC peripherals
    pub rtc_peri_pd_en, set_rtc_peri_pd_en: 4;
    /// power down WiFi
    pub wifi_pd_en, set_wifi_pd_en: 5;
    /// power down BT
    pub bt_pd_en, set_bt_pd_en: 6;
    /// power down CPU, but not restart when lightsleep.
    pub cpu_pd_en, set_cpu_pd_en: 7;
    /// Power down Internal 8M oscillator
    pub int_8m_pd_en, set_int_8m_pd_en: 8;
    /// power down digital peripherals
    pub dig_peri_pd_en, set_dig_peri_pd_en: 9;
    /// power down digital domain
    pub deep_slp, set_deep_slp: 10;
    /// enable WDT flashboot mode
    pub wdt_flashboot_mod_en, set_wdt_flashboot_mod_en: 11;
    /// set bias for digital domain, in sleep mode
    pub u8, dig_dbias_slp, set_dig_dbias_slp: 16, 12;
    /// set bias for RTC domain, in sleep mode
    pub u8, rtc_dbias_slp, set_rtc_dbias_slp: 21, 17;
    /// voltage parameter, in sleep mode
    pub u8, dbg_atten_slp, set_dbg_atten_slp: 25, 22;
    /// circuit control parameter, in monitor mode
    pub bias_sleep_monitor, set_bias_sleep_monitor: 26;
    /// circuit control parameter, in sleep mode
    pub bias_sleep_slp, set_bias_sleep_slp: 27;
    /// circuit control parameter, in monitor mode
    pub pd_cur_slp, set_pd_cur_slp: 28;
    /// power down VDDSDIO regulator
    pub vddsdio_pd_en, set_vddsdio_pd_en: 29;
    /// keep main XTAL powered up in sleep
    pub xtal_fpu, set_xtal_fpu: 30;
    /// keep rtc regulator powered up in sleep
    pub rtc_regulator_fpu, set_rtc_regulator_fpu: 31;
    /// enable deep sleep reject
    pub deep_slp_reject, set_deep_slp_reject: 32;
    /// enable light sleep reject
    pub light_slp_reject, set_light_slp_reject: 33;
}

impl Default for RtcSleepConfig {
    fn default() -> Self {
        let mut cfg = Self(Default::default());
        cfg.set_deep_slp_reject(true);
        cfg.set_light_slp_reject(true);
        cfg.set_rtc_dbias_slp(RTC_CNTL_DBIAS_1V10);
        cfg.set_dig_dbias_slp(RTC_CNTL_DBIAS_1V10);
        cfg
    }
}

const DR_REG_NRX_BASE: u32 = 0x6001CC00;
const DR_REG_FE_BASE: u32 = 0x60006000;
const DR_REG_FE2_BASE: u32 = 0x60005000;

const NRXPD_CTRL: u32 = DR_REG_NRX_BASE + 0x00d4;
const FE_GEN_CTRL: u32 = DR_REG_FE_BASE + 0x0090;
const FE2_TX_INTERP_CTRL: u32 = DR_REG_FE2_BASE + 0x00f0;

const SYSCON_SRAM_POWER_UP: u8 = 0x0000000F;
const SYSCON_ROM_POWER_UP: u8 = 0x00000003;

const NRX_RX_ROT_FORCE_PU: u32 = 1 << 5;
const NRX_VIT_FORCE_PU: u32 = 1 << 3;
const NRX_DEMAP_FORCE_PU: u32 = 1 << 1;

const FE_IQ_EST_FORCE_PU: u32 = 1 << 5;
const FE2_TX_INF_FORCE_PU: u32 = 1 << 10;

fn modify_register(reg: u32, mask: u32, value: u32) {
    let reg = reg as *mut u32;

    unsafe { reg.write_volatile((reg.read_volatile() & !mask) | value) };
}

fn register_modify_bits(reg: u32, bits: u32, set: bool) {
    if set {
        modify_register(reg, bits, bits);
    } else {
        modify_register(reg, bits, 0);
    }
}

fn rtc_sleep_pu(val: bool) {
    LPWR::regs()
        .dig_pwc()
        .modify(|_, w| w.lslp_mem_force_pu().bit(val));

    APB_CTRL::regs().front_end_mem_pd().modify(|_r, w| {
        w.dc_mem_force_pu()
            .bit(val)
            .pbus_mem_force_pu()
            .bit(val)
            .agc_mem_force_pu()
            .bit(val)
    });

    BB::regs()
        .bbpd_ctrl()
        .modify(|_r, w| w.fft_force_pu().bit(val).dc_est_force_pu().bit(val));

    register_modify_bits(
        NRXPD_CTRL,
        NRX_RX_ROT_FORCE_PU | NRX_VIT_FORCE_PU | NRX_DEMAP_FORCE_PU,
        val,
    );

    register_modify_bits(FE_GEN_CTRL, FE_IQ_EST_FORCE_PU, val);

    register_modify_bits(FE2_TX_INTERP_CTRL, FE2_TX_INF_FORCE_PU, val);

    APB_CTRL::regs().mem_power_up().modify(|_r, w| unsafe {
        w.sram_power_up()
            .bits(if val { SYSCON_SRAM_POWER_UP } else { 0 });
        w.rom_power_up()
            .bits(if val { SYSCON_ROM_POWER_UP } else { 0 })
    });
}

impl RtcSleepConfig {
    /// Configures the RTC for deep sleep mode.
    pub fn deep() -> Self {
        // Set up for ultra-low power sleep. Wakeup sources may modify these settings.
        let mut cfg = Self::default();

        cfg.set_lslp_mem_inf_fpu(false);
        cfg.set_rtc_mem_inf_follow_cpu(true); // ?
        cfg.set_rtc_fastmem_pd_en(true);
        cfg.set_rtc_slowmem_pd_en(true);
        cfg.set_rtc_peri_pd_en(true);
        cfg.set_wifi_pd_en(true);
        cfg.set_bt_pd_en(true);
        cfg.set_cpu_pd_en(true);
        cfg.set_int_8m_pd_en(true);

        cfg.set_dig_peri_pd_en(true);
        cfg.set_dig_dbias_slp(0); // because of dig_peri_pd_en

        cfg.set_deep_slp(true);
        cfg.set_wdt_flashboot_mod_en(false);
        cfg.set_vddsdio_pd_en(true);
        cfg.set_xtal_fpu(false);
        cfg.set_deep_slp_reject(true);
        cfg.set_light_slp_reject(true);
        cfg.set_rtc_dbias_slp(RTC_CNTL_DBIAS_1V10);

        // because of dig_peri_pd_en
        cfg.set_rtc_regulator_fpu(false);
        cfg.set_dbg_atten_slp(RTC_CNTL_DBG_ATTEN_DEEPSLEEP_DEFAULT);

        // because of xtal_fpu
        cfg.set_bias_sleep_monitor(true);
        cfg.set_bias_sleep_slp(true);
        cfg.set_pd_cur_slp(true);

        cfg
    }

    pub(crate) fn base_settings(_rtc: &Rtc<'_>) {
        let cfg = RtcConfig::default();

        // settings derived from esp_clk_init -> rtc_init
        let rtc_cntl = LPWR::regs();
        let extmem = EXTMEM::regs();
        let system = SYSTEM::regs();

        regi2c::I2C_DIG_REG_XPD_RTC_REG.write_field(0);
        regi2c::I2C_DIG_REG_XPD_DIG_REG.write_field(0);

        unsafe {
            rtc_cntl.timer1().modify(|_, w| {
                w.pll_buf_wait().bits(cfg.pll_wait());
                w.ck8m_wait().bits(cfg.ck8m_wait())
            });

            // Moved from rtc sleep to rtc init to save sleep function running time
            // set shortest possible sleep time limit

            rtc_cntl
                .timer5()
                .modify(|_, w| w.min_slp_val().bits(RTC_CNTL_MIN_SLP_VAL_MIN));

            // TODO: something about cali_ocode

            // Reset RTC bias to default value (needed if waking up from deep sleep)
            regi2c::I2C_DIG_REG_EXT_RTC_DREG_SLEEP.write_field(RTC_CNTL_DBIAS_1V10);

            // LDO dbias initialization
            // TODO: this modifies g_rtc_dbias_pvt_non_240m and g_dig_dbias_pvt_non_240m.
            //       We're using a high enough default but we should read from the efuse.
            // set_rtc_dig_dbias();

            regi2c::I2C_DIG_REG_EXT_RTC_DREG.write_field(RTC_CNTL_DBIAS_1V25);
            regi2c::I2C_DIG_REG_EXT_DIG_DREG.write_field(RTC_CNTL_DBIAS_1V25);

            if cfg.clkctl_init() {
                // clear CMMU clock force on

                extmem
                    .cache_mmu_power_ctrl()
                    .modify(|_, w| w.cache_mmu_mem_force_on().clear_bit());
                // clear tag clock force on

                extmem
                    .icache_tag_power_ctrl()
                    .modify(|_, w| w.icache_tag_mem_force_on().clear_bit());

                // clear register clock force on
                SPI0::regs()
                    .clock_gate()
                    .modify(|_, w| w.clk_en().clear_bit());
                SPI1::regs()
                    .clock_gate()
                    .modify(|_, w| w.clk_en().clear_bit());
            }

            if cfg.pwrctl_init() {
                rtc_cntl
                    .clk_conf()
                    .modify(|_, w| w.ck8m_force_pu().clear_bit());

                rtc_cntl
                    .options0()
                    .modify(|_, w| w.xtl_force_pu().bit(cfg.xtal_fpu() || cfg.bbpll_fpu()));

                // cancel bbpll force pu if setting no force power up

                rtc_cntl.options0().modify(|_, w| {
                    w.bbpll_force_pu().bit(cfg.bbpll_fpu());
                    w.bbpll_i2c_force_pu().bit(cfg.bbpll_fpu());
                    w.bb_i2c_force_pu().bit(cfg.bbpll_fpu())
                });

                rtc_cntl
                    .rtc_cntl()
                    .modify(|_, w| w.regulator_force_pu().clear_bit());

                // If this mask is enabled, all soc memories cannot enter power down mode
                // We should control soc memory power down mode from RTC, so we will not touch
                // this register any more

                system
                    .mem_pd_mask()
                    .modify(|_, w| w.lslp_mem_pd_mask().clear_bit());

                // If this pd_cfg is set to 1, all memory won't enter low power mode during
                // light sleep If this pd_cfg is set to 0, all memory will enter low
                // power mode during light sleep
                rtc_sleep_pu(false);

                rtc_cntl
                    .dig_pwc()
                    .modify(|_, w| w.dg_wrap_force_pu().clear_bit());

                rtc_cntl
                    .dig_iso()
                    .modify(|_, w| w.dg_wrap_force_noiso().clear_bit());

                // if SYSTEM_CPU_WAIT_MODE_FORCE_ON == 0 , the cpu clk will be closed when cpu
                // enter WAITI mode

                system
                    .cpu_per_conf()
                    .modify(|_, w| w.cpu_wait_mode_force_on().bit(!cfg.cpu_waiti_clk_gate()));

                // cancel digital PADS force no iso

                rtc_cntl.dig_iso().modify(|_, w| {
                    w.dg_pad_force_unhold().clear_bit();
                    w.dg_pad_force_noiso().clear_bit()
                });
            }

            rtc_cntl.int_ena().write(|w| w.bits(0));
            rtc_cntl.int_clr().write(|w| w.bits(u32::MAX));

            regi2c::I2C_ULP_IR_FORCE_XPD_CK.write_field(1);
        }
    }

    pub(crate) fn apply(&self) {
        // like esp-idf rtc_sleep_init()
        let rtc_cntl = LPWR::regs();

        if self.lslp_mem_inf_fpu() {
            rtc_sleep_pu(true);
        }

        unsafe {
            assert!(!self.pd_cur_slp() || self.bias_sleep_slp());

            regi2c::I2C_DIG_REG_EXT_RTC_DREG_SLEEP.write_field(self.rtc_dbias_slp());
            regi2c::I2C_DIG_REG_EXT_DIG_DREG_SLEEP.write_field(self.dig_dbias_slp());

            rtc_cntl.bias_conf().modify(|_, w| {
                w.dbg_atten_monitor()
                    .bits(RTC_CNTL_DBG_ATTEN_MONITOR_DEFAULT);
                // We have config values for this in self, so I don't know why we're setting
                // hardcoded defaults for these next two. It's what IDF does...
                w.bias_sleep_monitor().bit(RTC_CNTL_BIASSLP_MONITOR_DEFAULT);
                w.pd_cur_monitor().bit(RTC_CNTL_PD_CUR_MONITOR_DEFAULT)
            });

            rtc_cntl.bias_conf().modify(|_, w| {
                w.dbg_atten_deep_slp().bits(self.dbg_atten_slp());
                w.bias_sleep_deep_slp().bit(self.bias_sleep_slp());
                w.pd_cur_deep_slp().bit(self.pd_cur_slp())
            });

            if self.deep_slp() {
                regi2c::I2C_ULP_IR_FORCE_XPD_CK.write_field(0);

                rtc_cntl
                    .dig_pwc()
                    .modify(|_, w| w.dg_wrap_pd_en().set_bit());

                rtc_cntl.ana_conf().modify(|_, w| {
                    w.ckgen_i2c_pu().clear_bit();
                    w.pll_i2c_pu().clear_bit();
                    w.rfrx_pbus_pu().clear_bit();
                    w.txrf_i2c_pu().clear_bit()
                });

                rtc_cntl
                    .options0()
                    .modify(|_, w| w.bb_i2c_force_pu().clear_bit());
            } else {
                rtc_cntl.bias_conf().modify(|_, w| {
                    w.dg_vdd_drv_b_slp_en().set_bit();
                    w.dg_vdd_drv_b_slp().bits(RTC_CNTL_DG_VDD_DRV_B_SLP_DEFAULT)
                });

                rtc_cntl
                    .dig_pwc()
                    .modify(|_, w| w.dg_wrap_pd_en().clear_bit());
            }

            rtc_cntl
                .rtc_cntl()
                .modify(|_, w| w.regulator_force_pu().bit(self.rtc_regulator_fpu()));

            rtc_cntl.clk_conf().modify(|_, w| {
                w.ck8m_force_pu().bit(!self.int_8m_pd_en());
                w.ck8m_force_nogating().bit(!self.int_8m_pd_en())
            });

            // enable VDDSDIO control by state machine

            rtc_cntl.dig_pwc().modify(|_, w| {
                w.vdd_spi_pwr_force().clear_bit();
                w.vdd_spi_pd_en().bit(self.vddsdio_pd_en())
            });

            rtc_cntl.slp_reject_conf().modify(|_, w| {
                w.deep_slp_reject_en().bit(self.deep_slp_reject());
                w.light_slp_reject_en().bit(self.light_slp_reject())
            });

            rtc_cntl
                .options0()
                .modify(|_, w| w.xtl_force_pu().bit(self.xtal_fpu()));

            rtc_cntl
                .clk_conf()
                .modify(|_, w| w.xtal_global_force_nogating().bit(self.xtal_fpu()));
        }
    }

    pub(crate) fn start_sleep(&self, wakeup_triggers: WakeTriggers) {
        // set bits for what can wake us up
        LPWR::regs()
            .wakeup_state()
            .modify(|_, w| unsafe { w.wakeup_ena().bits(wakeup_triggers.0.into()) });

        LPWR::regs()
            .state0()
            .write(|w| w.sleep_en().set_bit().slp_wakeup().set_bit());
    }

    pub(crate) fn finish_sleep(&self) {
        // In deep sleep mode, we never get here

        LPWR::regs().int_clr().write(|w| {
            w.slp_reject().clear_bit_by_one();
            w.slp_wakeup().clear_bit_by_one()
        });

        // restore config if it is a light sleep
        if self.lslp_mem_inf_fpu() {
            rtc_sleep_pu(true);
        }
    }
}