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