esp_hal/timer/
timg.rs

1//! # Timer Group (TIMG)
2//!
3//! ## Overview
4//!
5//! The Timer Group (TIMG) peripherals contain one or more general-purpose
6//! timers, plus one or more watchdog timers.
7//!
8//! The general-purpose timers are based on a 16-bit pre-scaler and a 54-bit
9//! auto-reload-capable up-down counter.
10//!
11//! ## Configuration
12//!
13//! The timers have configurable alarms, which are triggered when the internal
14//! counter of the timers reaches a specific target value. The timers are
15//! clocked using the APB clock source.
16//!
17//! Typically, a general-purpose timer can be used in scenarios such as:
18//!
19//! - Generate period alarms; trigger events periodically
20//! - Generate one-shot alarms; trigger events once
21//! - Free-running; fetching a high-resolution timestamp on demand
22//!
23//! ## Examples
24//!
25//! ### General-purpose Timer
26//!
27//! ```rust, no_run
28#![doc = crate::before_snippet!()]
29//! use esp_hal::timer::timg::TimerGroup;
30//! use esp_hal::timer::Timer;
31//!
32//! let timg0 = TimerGroup::new(peripherals.TIMG0);
33//! let timer0 = timg0.timer0;
34//!
35//! // Get the current timestamp, in microseconds:
36//! let now = timer0.now();
37//!
38//! // Wait for timeout:
39//! timer0.load_value(Duration::from_secs(1));
40//! timer0.start();
41//!
42//! while !timer0.is_interrupt_set() {
43//!     // Wait
44//! }
45//!
46//! timer0.clear_interrupt();
47//! # Ok(())
48//! # }
49//! ```
50//! 
51//! ### Watchdog Timer
52//! ```rust, no_run
53#![doc = crate::before_snippet!()]
54//! use esp_hal::timer::timg::TimerGroup;
55//! use esp_hal::timer::timg::MwdtStage;
56//! use esp_hal::timer::Timer;
57//!
58//! let timg0 = TimerGroup::new(peripherals.TIMG0);
59//! let mut wdt = timg0.wdt;
60//!
61//! wdt.set_timeout(MwdtStage::Stage0, Duration::from_millis(5_000));
62//! wdt.enable();
63//!
64//! loop {
65//!     wdt.feed();
66//! }
67//! # }
68//! ```
69use core::marker::PhantomData;
70
71use super::Error;
72#[cfg(timg1)]
73use crate::peripherals::TIMG1;
74#[cfg(any(esp32c6, esp32h2))]
75use crate::soc::constants::TIMG_DEFAULT_CLK_SRC;
76use crate::{
77    asynch::AtomicWaker,
78    clock::Clocks,
79    interrupt::{self, InterruptConfigurable, InterruptHandler},
80    pac::timg0::RegisterBlock,
81    peripherals::{Interrupt, TIMG0},
82    private::Sealed,
83    system::PeripheralClockControl,
84    time::{Duration, Instant, Rate},
85};
86
87const NUM_TIMG: usize = 1 + cfg!(timg1) as usize;
88
89cfg_if::cfg_if! {
90    // We need no locks when a TIMG has a single timer, and we don't need locks for ESP32
91    // and S2 where the effective interrupt enable register (config) is not shared between
92    // the timers.
93    if #[cfg(all(timg_timer1, not(any(esp32, esp32s2))))] {
94        use crate::sync::{lock, RawMutex};
95        static INT_ENA_LOCK: [RawMutex; NUM_TIMG] = [const { RawMutex::new() }; NUM_TIMG];
96    }
97}
98
99/// A timer group consisting of
100#[cfg_attr(not(timg_timer1), doc = "a general purpose timer")]
101#[cfg_attr(timg_timer1, doc = "2 timers")]
102/// and a watchdog timer.
103pub struct TimerGroup<'d, T>
104where
105    T: TimerGroupInstance + 'd,
106{
107    _timer_group: PhantomData<T>,
108    /// Timer 0
109    pub timer0: Timer<'d>,
110    /// Timer 1
111    #[cfg(timg_timer1)]
112    pub timer1: Timer<'d>,
113    /// Watchdog timer
114    pub wdt: Wdt<T>,
115}
116
117#[doc(hidden)]
118pub trait TimerGroupInstance {
119    fn id() -> u8;
120    fn register_block() -> *const RegisterBlock;
121    fn configure_src_clk();
122    fn enable_peripheral();
123    fn reset_peripheral();
124    fn configure_wdt_src_clk();
125    fn wdt_interrupt() -> Interrupt;
126}
127
128impl TimerGroupInstance for TIMG0<'_> {
129    fn id() -> u8 {
130        0
131    }
132
133    #[inline(always)]
134    fn register_block() -> *const RegisterBlock {
135        Self::regs()
136    }
137
138    fn configure_src_clk() {
139        cfg_if::cfg_if! {
140            if #[cfg(esp32)] {
141                // ESP32 has only APB clock source, do nothing
142            } else if #[cfg(any(esp32c2, esp32c3, esp32s2, esp32s3))] {
143                unsafe {
144                    (*<Self as TimerGroupInstance>::register_block())
145                        .t(0)
146                        .config()
147                        .modify(|_, w| w.use_xtal().clear_bit());
148                }
149            } else if #[cfg(any(esp32c6, esp32h2))] {
150                crate::peripherals::PCR::regs()
151                    .timergroup0_timer_clk_conf()
152                    .modify(|_, w| unsafe { w.tg0_timer_clk_sel().bits(TIMG_DEFAULT_CLK_SRC) });
153            }
154        }
155    }
156
157    fn enable_peripheral() {
158        PeripheralClockControl::enable(crate::system::Peripheral::Timg0);
159    }
160
161    fn reset_peripheral() {
162        // FIXME: for TIMG0 do nothing for now because the reset breaks
163        // `time::Instant::now`
164    }
165
166    fn configure_wdt_src_clk() {
167        cfg_if::cfg_if! {
168            if #[cfg(any(esp32, esp32s2, esp32s3))] {
169                // ESP32, ESP32-S2, and ESP32-S3 use only ABP, do nothing
170            } else if #[cfg(any(esp32c2, esp32c3))] {
171                unsafe {
172                    (*<Self as TimerGroupInstance>::register_block())
173                        .wdtconfig0()
174                        .modify(|_, w| w.wdt_use_xtal().clear_bit());
175                }
176            } else if #[cfg(any(esp32c6, esp32h2))] {
177                crate::peripherals::PCR::regs()
178                    .timergroup0_wdt_clk_conf()
179                    .modify(|_, w| unsafe { w.tg0_wdt_clk_sel().bits(1) });
180            }
181        }
182    }
183
184    fn wdt_interrupt() -> Interrupt {
185        Interrupt::TG0_WDT_LEVEL
186    }
187}
188
189#[cfg(timg1)]
190impl TimerGroupInstance for crate::peripherals::TIMG1<'_> {
191    fn id() -> u8 {
192        1
193    }
194
195    #[inline(always)]
196    fn register_block() -> *const RegisterBlock {
197        Self::regs()
198    }
199
200    fn configure_src_clk() {
201        cfg_if::cfg_if! {
202            if #[cfg(any(esp32, esp32c2, esp32c3))] {
203                // ESP32 has only APB clock source, do nothing
204                // ESP32-C2 and ESP32-C3 don't have t1config only t0config, do nothing
205            } else if #[cfg(any(esp32c6, esp32h2))] {
206                crate::peripherals::PCR::regs()
207                    .timergroup1_timer_clk_conf()
208                    .modify(|_, w| unsafe { w.tg1_timer_clk_sel().bits(TIMG_DEFAULT_CLK_SRC) });
209            } else if #[cfg(any(esp32s2, esp32s3))] {
210                unsafe {
211                    (*<Self as TimerGroupInstance>::register_block())
212                        .t(1)
213                        .config()
214                        .modify(|_, w| w.use_xtal().clear_bit());
215                }
216            }
217        }
218    }
219
220    fn enable_peripheral() {
221        PeripheralClockControl::enable(crate::system::Peripheral::Timg1);
222    }
223
224    fn reset_peripheral() {
225        PeripheralClockControl::reset(crate::system::Peripheral::Timg1)
226    }
227
228    fn configure_wdt_src_clk() {
229        cfg_if::cfg_if! {
230            if #[cfg(any(esp32, esp32s2, esp32s3, esp32c2, esp32c3))] {
231                // ESP32-C2 and ESP32-C3 don't have t1config only t0config, do nothing
232                // ESP32, ESP32-S2, and ESP32-S3 use only ABP, do nothing
233            } else if #[cfg(any(esp32c6, esp32h2))] {
234                crate::peripherals::PCR::regs()
235                    .timergroup1_wdt_clk_conf()
236                    .modify(|_, w| unsafe { w.tg1_wdt_clk_sel().bits(TIMG_DEFAULT_CLK_SRC) });
237            }
238        }
239    }
240
241    fn wdt_interrupt() -> Interrupt {
242        Interrupt::TG1_WDT_LEVEL
243    }
244}
245
246impl<'d, T> TimerGroup<'d, T>
247where
248    T: TimerGroupInstance + 'd,
249{
250    /// Construct a new instance of [`TimerGroup`] in blocking mode
251    pub fn new(_timer_group: T) -> Self {
252        T::reset_peripheral();
253        T::enable_peripheral();
254
255        T::configure_src_clk();
256
257        Self {
258            _timer_group: PhantomData,
259            timer0: Timer {
260                timer: 0,
261                tg: T::id(),
262                register_block: T::register_block(),
263                _lifetime: PhantomData,
264            },
265            #[cfg(timg_timer1)]
266            timer1: Timer {
267                timer: 1,
268                tg: T::id(),
269                register_block: T::register_block(),
270                _lifetime: PhantomData,
271            },
272            wdt: Wdt::new(),
273        }
274    }
275}
276
277impl super::Timer for Timer<'_> {
278    fn start(&self) {
279        self.set_counter_active(false);
280        self.set_alarm_active(false);
281
282        self.reset_counter();
283        self.set_counter_decrementing(false);
284
285        self.set_counter_active(true);
286        self.set_alarm_active(true);
287    }
288
289    fn stop(&self) {
290        self.set_counter_active(false);
291    }
292
293    fn reset(&self) {
294        self.reset_counter()
295    }
296
297    fn is_running(&self) -> bool {
298        self.is_counter_active()
299    }
300
301    fn now(&self) -> Instant {
302        self.now()
303    }
304
305    fn load_value(&self, value: Duration) -> Result<(), Error> {
306        self.load_value(value)
307    }
308
309    fn enable_auto_reload(&self, auto_reload: bool) {
310        self.set_auto_reload(auto_reload)
311    }
312
313    fn enable_interrupt(&self, state: bool) {
314        self.set_interrupt_enabled(state);
315    }
316
317    fn clear_interrupt(&self) {
318        self.clear_interrupt()
319    }
320
321    fn is_interrupt_set(&self) -> bool {
322        self.is_interrupt_set()
323    }
324
325    fn async_interrupt_handler(&self) -> InterruptHandler {
326        match (self.timer_group(), self.timer_number()) {
327            (0, 0) => asynch::timg0_timer0_handler,
328            #[cfg(timg_timer1)]
329            (0, 1) => asynch::timg0_timer1_handler,
330            #[cfg(timg1)]
331            (1, 0) => asynch::timg1_timer0_handler,
332            #[cfg(all(timg_timer1, timg1))]
333            (1, 1) => asynch::timg1_timer1_handler,
334            _ => unreachable!(),
335        }
336    }
337
338    fn peripheral_interrupt(&self) -> Interrupt {
339        match (self.timer_group(), self.timer_number()) {
340            (0, 0) => Interrupt::TG0_T0_LEVEL,
341            #[cfg(timg_timer1)]
342            (0, 1) => Interrupt::TG0_T1_LEVEL,
343            #[cfg(timg1)]
344            (1, 0) => Interrupt::TG1_T0_LEVEL,
345            #[cfg(all(timg_timer1, timg1))]
346            (1, 1) => Interrupt::TG1_T1_LEVEL,
347            _ => unreachable!(),
348        }
349    }
350
351    fn set_interrupt_handler(&self, handler: InterruptHandler) {
352        self.set_interrupt_handler(handler)
353    }
354
355    fn waker(&self) -> &AtomicWaker {
356        asynch::waker(self)
357    }
358}
359
360/// A timer within a Timer Group.
361#[derive(Debug)]
362#[cfg_attr(feature = "defmt", derive(defmt::Format))]
363pub struct Timer<'d> {
364    register_block: *const RegisterBlock,
365    _lifetime: PhantomData<&'d mut ()>,
366    timer: u8,
367    tg: u8,
368}
369
370impl Sealed for Timer<'_> {}
371unsafe impl Send for Timer<'_> {}
372
373/// Timer peripheral instance
374impl Timer<'_> {
375    /// Unsafely clone this peripheral reference.
376    ///
377    /// # Safety
378    ///
379    /// You must ensure that you're only using one instance of this type at a
380    /// time.
381    pub unsafe fn clone_unchecked(&self) -> Self {
382        Self {
383            register_block: self.register_block,
384            timer: self.timer,
385            tg: self.tg,
386            _lifetime: PhantomData,
387        }
388    }
389
390    /// Creates a new peripheral reference with a shorter lifetime.
391    ///
392    /// Use this method if you would like to keep working with the peripheral
393    /// after you dropped the driver that consumes this.
394    pub fn reborrow(&mut self) -> Timer<'_> {
395        unsafe { self.clone_unchecked() }
396    }
397
398    pub(crate) fn set_interrupt_handler(&self, handler: InterruptHandler) {
399        let interrupt = match (self.timer_group(), self.timer_number()) {
400            (0, 0) => Interrupt::TG0_T0_LEVEL,
401            #[cfg(timg_timer1)]
402            (0, 1) => Interrupt::TG0_T1_LEVEL,
403            #[cfg(timg1)]
404            (1, 0) => Interrupt::TG1_T0_LEVEL,
405            #[cfg(all(timg_timer1, timg1))]
406            (1, 1) => Interrupt::TG1_T1_LEVEL,
407            _ => unreachable!(),
408        };
409
410        for core in crate::system::Cpu::other() {
411            crate::interrupt::disable(core, interrupt);
412        }
413        unsafe { interrupt::bind_interrupt(interrupt, handler.handler()) };
414        unwrap!(interrupt::enable(interrupt, handler.priority()));
415    }
416
417    fn register_block(&self) -> &RegisterBlock {
418        unsafe { &*self.register_block }
419    }
420
421    fn timer_group(&self) -> u8 {
422        self.tg
423    }
424
425    fn timer_number(&self) -> u8 {
426        self.timer
427    }
428
429    fn t(&self) -> &crate::pac::timg0::T {
430        self.register_block().t(self.timer_number().into())
431    }
432
433    fn reset_counter(&self) {
434        let t = self.t();
435
436        t.loadlo().write(|w| unsafe { w.load_lo().bits(0) });
437        t.loadhi().write(|w| unsafe { w.load_hi().bits(0) });
438
439        t.load().write(|w| unsafe { w.load().bits(1) });
440    }
441
442    fn set_counter_active(&self, state: bool) {
443        self.t().config().modify(|_, w| w.en().bit(state));
444    }
445
446    fn is_counter_active(&self) -> bool {
447        self.t().config().read().en().bit_is_set()
448    }
449
450    fn set_counter_decrementing(&self, decrementing: bool) {
451        self.t()
452            .config()
453            .modify(|_, w| w.increase().bit(!decrementing));
454    }
455
456    fn set_auto_reload(&self, auto_reload: bool) {
457        self.t()
458            .config()
459            .modify(|_, w| w.autoreload().bit(auto_reload));
460    }
461
462    fn set_alarm_active(&self, state: bool) {
463        self.t().config().modify(|_, w| w.alarm_en().bit(state));
464    }
465
466    fn load_value(&self, value: Duration) -> Result<(), Error> {
467        cfg_if::cfg_if! {
468            if #[cfg(esp32h2)] {
469                // ESP32-H2 is using PLL_48M_CLK source instead of APB_CLK
470                let clk_src = Clocks::get().pll_48m_clock;
471            } else {
472                let clk_src = Clocks::get().apb_clock;
473            }
474        }
475        let Some(ticks) = timeout_to_ticks(value, clk_src, self.divider()) else {
476            return Err(Error::InvalidTimeout);
477        };
478
479        // The counter is 54-bits wide, so we must ensure that the provided
480        // value is not too wide:
481        if (ticks & !0x3F_FFFF_FFFF_FFFF) != 0 {
482            return Err(Error::InvalidTimeout);
483        }
484
485        let high = (ticks >> 32) as u32;
486        let low = (ticks & 0xFFFF_FFFF) as u32;
487
488        let t = self.t();
489
490        t.alarmlo().write(|w| unsafe { w.alarm_lo().bits(low) });
491        t.alarmhi().write(|w| unsafe { w.alarm_hi().bits(high) });
492
493        Ok(())
494    }
495
496    fn clear_interrupt(&self) {
497        self.register_block()
498            .int_clr()
499            .write(|w| w.t(self.timer).clear_bit_by_one());
500        let periodic = self.t().config().read().autoreload().bit_is_set();
501        self.set_alarm_active(periodic);
502    }
503
504    fn now(&self) -> Instant {
505        let t = self.t();
506
507        t.update().write(|w| w.update().set_bit());
508        while t.update().read().update().bit_is_set() {
509            // Wait for the update to complete
510        }
511
512        let value_lo = t.lo().read().bits() as u64;
513        let value_hi = t.hi().read().bits() as u64;
514
515        let ticks = (value_hi << 32) | value_lo;
516        cfg_if::cfg_if! {
517            if #[cfg(esp32h2)] {
518                // ESP32-H2 is using PLL_48M_CLK source instead of APB_CLK
519                let clk_src = Clocks::get().pll_48m_clock;
520            } else {
521                let clk_src = Clocks::get().apb_clock;
522            }
523        }
524        let micros = ticks_to_timeout(ticks, clk_src, self.divider());
525
526        Instant::from_ticks(micros)
527    }
528
529    fn divider(&self) -> u32 {
530        let t = self.t();
531
532        // From the ESP32 TRM, "11.2.1 16­-bit Prescaler and Clock Selection":
533        //
534        // "The prescaler can divide the APB clock by a factor from 2 to 65536.
535        // Specifically, when TIMGn_Tx_DIVIDER is either 1 or 2, the clock divisor is 2;
536        // when TIMGn_Tx_DIVIDER is 0, the clock divisor is 65536. Any other value will
537        // cause the clock to be divided by exactly that value."
538        match t.config().read().divider().bits() {
539            0 => 65536,
540            1 | 2 => 2,
541            n => n as u32,
542        }
543    }
544
545    fn is_interrupt_set(&self) -> bool {
546        self.register_block()
547            .int_raw()
548            .read()
549            .t(self.timer)
550            .bit_is_set()
551    }
552
553    fn set_interrupt_enabled(&self, state: bool) {
554        cfg_if::cfg_if! {
555            if #[cfg(any(esp32, esp32s2))] {
556                // On ESP32 and S2, the `int_ena` register is ineffective - interrupts fire even
557                // without int_ena enabling them. We use level interrupts so that we have a status
558                // bit available.
559                self.register_block()
560                    .t(self.timer as usize)
561                    .config()
562                    .modify(|_, w| w.level_int_en().bit(state));
563            } else if #[cfg(timg_timer1)] {
564                lock(&INT_ENA_LOCK[self.timer_group() as usize], || {
565                    self.register_block()
566                        .int_ena()
567                        .modify(|_, w| w.t(self.timer_number()).bit(state));
568                });
569            } else {
570                self.register_block()
571                    .int_ena()
572                    .modify(|_, w| w.t(0).bit(state));
573            }
574        }
575    }
576}
577
578fn ticks_to_timeout(ticks: u64, clock: Rate, divider: u32) -> u64 {
579    // 1_000_000 is used to get rid of `float` calculations
580    let period: u64 = 1_000_000 * 1_000_000 / (clock.as_hz() as u64 / divider as u64);
581
582    ticks * period / 1_000_000
583}
584
585fn timeout_to_ticks(timeout: Duration, clock: Rate, divider: u32) -> Option<u64> {
586    let micros = timeout.as_micros();
587    let ticks_per_sec = (clock.as_hz() / divider) as u64;
588
589    micros.checked_mul(ticks_per_sec).map(|n| n / 1_000_000)
590}
591
592/// Behavior of the MWDT stage if it times out.
593#[allow(unused)]
594#[derive(Debug, Clone, Copy)]
595pub enum MwdtStageAction {
596    /// No effect on the system.
597    Off         = 0,
598    /// Trigger an interrupt.
599    Interrupt   = 1,
600    /// Reset the CPU core.
601    ResetCpu    = 2,
602    /// Reset the main system, power management unit and RTC peripherals.
603    ResetSystem = 3,
604}
605
606/// MWDT stages.
607///
608/// Timer stages allow for a timer to have a series of different timeout values
609/// and corresponding expiry action.
610#[derive(Debug, Clone, Copy)]
611pub enum MwdtStage {
612    /// MWDT stage 0.
613    Stage0,
614    /// MWDT stage 1.
615    Stage1,
616    /// MWDT stage 2.
617    Stage2,
618    /// MWDT stage 3.
619    Stage3,
620}
621
622/// Watchdog timer
623pub struct Wdt<TG> {
624    phantom: PhantomData<TG>,
625}
626
627/// Watchdog driver
628impl<TG> Wdt<TG>
629where
630    TG: TimerGroupInstance,
631{
632    /// Construct a new instance of [`Wdt`]
633    pub fn new() -> Self {
634        TG::configure_wdt_src_clk();
635
636        Self {
637            phantom: PhantomData,
638        }
639    }
640
641    /// Enable the watchdog timer instance
642    pub fn enable(&mut self) {
643        // SAFETY: The `TG` instance being modified is owned by `self`, which is behind
644        //         a mutable reference.
645        unsafe { self.set_wdt_enabled(true) };
646    }
647
648    /// Disable the watchdog timer instance
649    pub fn disable(&mut self) {
650        // SAFETY: The `TG` instance being modified is owned by `self`, which is behind
651        //         a mutable reference.
652        unsafe { self.set_wdt_enabled(false) };
653    }
654
655    /// Forcibly enable or disable the watchdog timer
656    ///
657    /// # Safety
658    ///
659    /// This bypasses the usual ownership rules for the peripheral, so users
660    /// must take care to ensure that no driver instance is active for the
661    /// timer.
662    pub unsafe fn set_wdt_enabled(&mut self, enabled: bool) {
663        let reg_block = unsafe { &*TG::register_block() };
664
665        self.set_write_protection(false);
666
667        if !enabled {
668            reg_block.wdtconfig0().write(|w| unsafe { w.bits(0) });
669        } else {
670            reg_block.wdtconfig0().write(|w| w.wdt_en().bit(true));
671
672            reg_block
673                .wdtconfig0()
674                .write(|w| w.wdt_flashboot_mod_en().bit(false));
675
676            #[cfg_attr(esp32, allow(unused_unsafe))]
677            reg_block.wdtconfig0().write(|w| unsafe {
678                w.wdt_en()
679                    .bit(true)
680                    .wdt_stg0()
681                    .bits(MwdtStageAction::ResetSystem as u8)
682                    .wdt_cpu_reset_length()
683                    .bits(7)
684                    .wdt_sys_reset_length()
685                    .bits(7)
686                    .wdt_stg1()
687                    .bits(MwdtStageAction::Off as u8)
688                    .wdt_stg2()
689                    .bits(MwdtStageAction::Off as u8)
690                    .wdt_stg3()
691                    .bits(MwdtStageAction::Off as u8)
692            });
693
694            #[cfg(any(esp32c2, esp32c3, esp32c6))]
695            reg_block
696                .wdtconfig0()
697                .modify(|_, w| w.wdt_conf_update_en().set_bit());
698        }
699
700        self.set_write_protection(true);
701    }
702
703    /// Feed the watchdog timer
704    pub fn feed(&mut self) {
705        let reg_block = unsafe { &*TG::register_block() };
706
707        self.set_write_protection(false);
708
709        reg_block.wdtfeed().write(|w| unsafe { w.bits(1) });
710
711        self.set_write_protection(true);
712    }
713
714    fn set_write_protection(&mut self, enable: bool) {
715        let reg_block = unsafe { &*TG::register_block() };
716
717        let wkey = if enable { 0u32 } else { 0x50D8_3AA1u32 };
718
719        reg_block
720            .wdtwprotect()
721            .write(|w| unsafe { w.wdt_wkey().bits(wkey) });
722    }
723
724    /// Set the timeout, in microseconds, of the watchdog timer
725    pub fn set_timeout(&mut self, stage: MwdtStage, timeout: Duration) {
726        let timeout_raw = (timeout.as_micros() * 10_000 / 125) as u32;
727
728        let reg_block = unsafe { &*TG::register_block() };
729
730        self.set_write_protection(false);
731
732        reg_block
733            .wdtconfig1()
734            .write(|w| unsafe { w.wdt_clk_prescale().bits(1) });
735
736        unsafe {
737            match stage {
738                MwdtStage::Stage0 => reg_block
739                    .wdtconfig2()
740                    .write(|w| w.wdt_stg0_hold().bits(timeout_raw)),
741                MwdtStage::Stage1 => reg_block
742                    .wdtconfig3()
743                    .write(|w| w.wdt_stg1_hold().bits(timeout_raw)),
744                MwdtStage::Stage2 => reg_block
745                    .wdtconfig4()
746                    .write(|w| w.wdt_stg2_hold().bits(timeout_raw)),
747                MwdtStage::Stage3 => reg_block
748                    .wdtconfig5()
749                    .write(|w| w.wdt_stg3_hold().bits(timeout_raw)),
750            };
751        }
752
753        #[cfg(any(esp32c2, esp32c3, esp32c6))]
754        reg_block
755            .wdtconfig0()
756            .modify(|_, w| w.wdt_conf_update_en().set_bit());
757
758        self.set_write_protection(true);
759    }
760
761    /// Set the stage action of the MWDT for a specific stage.
762    ///
763    /// This function modifies MWDT behavior only if a custom bootloader with
764    /// the following modifications is used:
765    /// - `ESP_TASK_WDT_EN` parameter **disabled**
766    /// - `ESP_INT_WDT` parameter **disabled**
767    pub fn set_stage_action(&mut self, stage: MwdtStage, action: MwdtStageAction) {
768        let reg_block = unsafe { &*TG::register_block() };
769
770        self.set_write_protection(false);
771
772        match stage {
773            MwdtStage::Stage0 => {
774                reg_block
775                    .wdtconfig0()
776                    .modify(|_, w| unsafe { w.wdt_stg0().bits(action as u8) });
777            }
778            MwdtStage::Stage1 => {
779                reg_block
780                    .wdtconfig0()
781                    .modify(|_, w| unsafe { w.wdt_stg1().bits(action as u8) });
782            }
783            MwdtStage::Stage2 => {
784                reg_block
785                    .wdtconfig0()
786                    .modify(|_, w| unsafe { w.wdt_stg2().bits(action as u8) });
787            }
788            MwdtStage::Stage3 => {
789                reg_block
790                    .wdtconfig0()
791                    .modify(|_, w| unsafe { w.wdt_stg3().bits(action as u8) });
792            }
793        }
794
795        self.set_write_protection(true);
796    }
797}
798
799impl<TG> crate::private::Sealed for Wdt<TG> where TG: TimerGroupInstance {}
800
801impl<TG> InterruptConfigurable for Wdt<TG>
802where
803    TG: TimerGroupInstance,
804{
805    fn set_interrupt_handler(&mut self, handler: interrupt::InterruptHandler) {
806        let interrupt = TG::wdt_interrupt();
807        unsafe {
808            interrupt::bind_interrupt(interrupt, handler.handler());
809            interrupt::enable(interrupt, handler.priority()).unwrap();
810        }
811    }
812}
813
814impl<TG> Default for Wdt<TG>
815where
816    TG: TimerGroupInstance,
817{
818    fn default() -> Self {
819        Self::new()
820    }
821}
822
823// Async functionality of the timer groups.
824mod asynch {
825    use procmacros::handler;
826
827    use super::*;
828    use crate::asynch::AtomicWaker;
829
830    const NUM_WAKERS: usize = {
831        let timer_per_group = 1 + cfg!(timg_timer1) as usize;
832        NUM_TIMG * timer_per_group
833    };
834
835    static WAKERS: [AtomicWaker; NUM_WAKERS] = [const { AtomicWaker::new() }; NUM_WAKERS];
836
837    pub(super) fn waker(timer: &Timer<'_>) -> &'static AtomicWaker {
838        let index = (timer.timer_number() << 1) | timer.timer_group();
839        &WAKERS[index as usize]
840    }
841
842    #[inline]
843    fn handle_irq(timer: Timer<'_>) {
844        timer.set_interrupt_enabled(false);
845        waker(&timer).wake();
846    }
847
848    // INT_ENA means that when the interrupt occurs, it will show up in the
849    // INT_ST. Clearing INT_ENA that it won't show up on INT_ST but if
850    // interrupt is already there, it won't clear it - that's why we need to
851    // clear the INT_CLR as well.
852    #[handler]
853    pub(crate) fn timg0_timer0_handler() {
854        handle_irq(Timer {
855            register_block: TIMG0::regs(),
856            _lifetime: PhantomData,
857            timer: 0,
858            tg: 0,
859        });
860    }
861
862    #[cfg(timg1)]
863    #[handler]
864    pub(crate) fn timg1_timer0_handler() {
865        handle_irq(Timer {
866            register_block: TIMG1::regs(),
867            _lifetime: PhantomData,
868            timer: 0,
869            tg: 1,
870        });
871    }
872
873    #[cfg(timg_timer1)]
874    #[handler]
875    pub(crate) fn timg0_timer1_handler() {
876        handle_irq(Timer {
877            register_block: TIMG0::regs(),
878            _lifetime: PhantomData,
879            timer: 1,
880            tg: 0,
881        });
882    }
883
884    #[cfg(all(timg1, timg_timer1))]
885    #[handler]
886    pub(crate) fn timg1_timer1_handler() {
887        handle_irq(Timer {
888            register_block: TIMG1::regs(),
889            _lifetime: PhantomData,
890            timer: 1,
891            tg: 1,
892        });
893    }
894}
895
896/// Event Task Matrix
897#[cfg(soc_etm)]
898pub mod etm {
899    use super::*;
900    use crate::etm::{EtmEvent, EtmTask};
901
902    /// Event Task Matrix event for a timer.
903    pub struct Event {
904        id: u8,
905    }
906
907    /// Event Task Matrix task for a timer.
908    pub struct Task {
909        id: u8,
910    }
911
912    impl EtmEvent for Event {
913        fn id(&self) -> u8 {
914            self.id
915        }
916    }
917
918    impl Sealed for Event {}
919
920    impl EtmTask for Task {
921        fn id(&self) -> u8 {
922            self.id
923        }
924    }
925
926    impl Sealed for Task {}
927
928    /// General purpose timer ETM events.
929    pub trait Events {
930        /// ETM event triggered on alarm
931        fn on_alarm(&self) -> Event;
932    }
933
934    /// General purpose timer ETM tasks
935    pub trait Tasks {
936        /// ETM task to start the counter
937        fn cnt_start(&self) -> Task;
938
939        /// ETM task to start the alarm
940        fn cnt_stop(&self) -> Task;
941
942        /// ETM task to stop the counter
943        fn cnt_reload(&self) -> Task;
944
945        /// ETM task to reload the counter
946        fn cnt_cap(&self) -> Task;
947
948        /// ETM task to load the counter with the value stored when the last
949        /// `now()` was called
950        fn alarm_start(&self) -> Task;
951    }
952
953    impl Events for Timer<'_> {
954        fn on_alarm(&self) -> Event {
955            Event {
956                id: 48 + self.timer_group(),
957            }
958        }
959    }
960
961    impl Tasks for Timer<'_> {
962        fn cnt_start(&self) -> Task {
963            Task {
964                id: 88 + self.timer_group(),
965            }
966        }
967
968        fn alarm_start(&self) -> Task {
969            Task {
970                id: 90 + self.timer_group(),
971            }
972        }
973
974        fn cnt_stop(&self) -> Task {
975            Task {
976                id: 92 + self.timer_group(),
977            }
978        }
979
980        fn cnt_reload(&self) -> Task {
981            Task {
982                id: 94 + self.timer_group(),
983            }
984        }
985
986        fn cnt_cap(&self) -> Task {
987            Task {
988                id: 96 + self.timer_group(),
989            }
990        }
991    }
992}