Skip to main content

esp_hal/soc/esp32c3/
clocks.rs

1//! Clock tree definitions and implementations for ESP32-C3.
2//!
3//! Remarks:
4//! - Enabling a clock node assumes it has first been configured. Some fixed clock nodes don't need
5//!   to be configured.
6//! - Some information may be assumed, e.g. the possibility to disable watchdog timers before clock
7//!   configuration.
8//! - Internal RC oscillators (136k RC_SLOW and 17.5M RC_FAST) are not calibrated here, this system
9//!   can only give a rough estimate of their frequency. They can be calibrated separately using a
10//!   known crystal frequency.
11//! - Some of the SOC capabilities are not implemented.
12#![allow(dead_code, reason = "Some of this is bound to be unused")]
13#![allow(missing_docs, reason = "Experimental")]
14
15// TODO: This is a temporary place for this, should probably be moved into clocks_ll.
16
17use esp_rom_sys::rom::{ets_delay_us, ets_update_cpu_frequency_rom};
18
19use crate::{
20    peripherals::{APB_CTRL, I2C_ANA_MST, LPWR, SYSTEM, TIMG0, TIMG1},
21    soc::regi2c,
22    time::Rate,
23};
24
25define_clock_tree_types!();
26
27/// Clock configuration options.
28#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
29#[cfg_attr(feature = "defmt", derive(defmt::Format))]
30#[allow(
31    clippy::enum_variant_names,
32    reason = "MHz suffix indicates physical unit."
33)]
34#[non_exhaustive]
35pub enum CpuClock {
36    /// 80 MHz CPU clock
37    #[default]
38    _80MHz  = 80,
39
40    /// 160 MHz CPU clock
41    _160MHz = 160,
42}
43
44impl CpuClock {
45    // The presets use 480MHz PLL by default, because that is the default value the chip boots
46    // with, and changing it breaks USB Serial/JTAG.
47    const PRESET_80: ClockConfig = ClockConfig {
48        xtal_clk: None,
49        system_pre_div: None,
50        pll_clk: Some(PllClkConfig::_480),
51        cpu_pll_div_out: Some(CpuPllDivOutConfig::_80),
52        cpu_clk: Some(CpuClkConfig::Pll),
53        rc_fast_clk_div_n: Some(RcFastClkDivNConfig::new(0)),
54        rtc_slow_clk: Some(RtcSlowClkConfig::RcSlow),
55        rtc_fast_clk: Some(RtcFastClkConfig::Rc),
56        low_power_clk: Some(LowPowerClkConfig::RtcSlow),
57        timg_calibration_clock: None,
58    };
59    const PRESET_160: ClockConfig = ClockConfig {
60        xtal_clk: None,
61        system_pre_div: None,
62        pll_clk: Some(PllClkConfig::_480),
63        cpu_pll_div_out: Some(CpuPllDivOutConfig::_160),
64        cpu_clk: Some(CpuClkConfig::Pll),
65        rc_fast_clk_div_n: Some(RcFastClkDivNConfig::new(0)),
66        rtc_slow_clk: Some(RtcSlowClkConfig::RcSlow),
67        rtc_fast_clk: Some(RtcFastClkConfig::Rc),
68        low_power_clk: Some(LowPowerClkConfig::RtcSlow),
69        timg_calibration_clock: None,
70    };
71}
72
73impl From<CpuClock> for ClockConfig {
74    fn from(value: CpuClock) -> ClockConfig {
75        match value {
76            CpuClock::_80MHz => CpuClock::PRESET_80,
77            CpuClock::_160MHz => CpuClock::PRESET_160,
78        }
79    }
80}
81
82impl Default for ClockConfig {
83    fn default() -> Self {
84        Self::from(CpuClock::default())
85    }
86}
87
88impl ClockConfig {
89    pub(crate) fn try_get_preset(self) -> Option<CpuClock> {
90        match self {
91            v if v == CpuClock::PRESET_80 => Some(CpuClock::_80MHz),
92            v if v == CpuClock::PRESET_160 => Some(CpuClock::_160MHz),
93            _ => None,
94        }
95    }
96
97    pub(crate) fn configure(mut self, clocks: &mut ClockTree) {
98        if self.xtal_clk.is_none() {
99            // TODO: support multiple crystal frequencies (esp-idf supports 32M).
100            self.xtal_clk = Some(XtalClkConfig::_40);
101        }
102
103        // Switch CPU to XTAL before reconfiguring PLL.
104        configure_xtal_clk(clocks, XtalClkConfig::_40);
105        configure_system_pre_div(clocks, SystemPreDivConfig::new(0));
106        configure_cpu_clk(clocks, CpuClkConfig::Xtal);
107
108        self.apply(clocks);
109    }
110}
111
112// XTAL_CLK
113
114fn configure_xtal_clk_impl(
115    _clocks: &mut ClockTree,
116    _old_config: Option<XtalClkConfig>,
117    _config: XtalClkConfig,
118) {
119    // The stored configuration affects PLL settings instead.
120}
121
122// PLL_CLK
123
124fn enable_pll_clk_impl(clocks: &mut ClockTree, en: bool) {
125    // regi2c_ctrl_ll_i2c_bbpll_enable
126    I2C_ANA_MST::regs()
127        .ana_config()
128        .modify(|_, w| w.bbpll_pd().bit(!en));
129
130    LPWR::regs().options0().modify(|_, w| {
131        let power_down = !en;
132        w.bb_i2c_force_pd().bit(power_down);
133        w.bbpll_force_pd().bit(power_down);
134        w.bbpll_i2c_force_pd().bit(power_down)
135    });
136
137    if !en {
138        return;
139    }
140
141    // Digital part
142    let pll_freq = unwrap!(clocks.pll_clk);
143    let xtal_freq = unwrap!(clocks.xtal_clk);
144    SYSTEM::regs()
145        .cpu_per_conf()
146        .modify(|_, w| w.pll_freq_sel().bit(pll_freq == PllClkConfig::_480));
147
148    // Analog part
149
150    // Start BBPLL self-calibration
151    I2C_ANA_MST::regs().ana_conf0().modify(|_, w| {
152        w.bbpll_stop_force_high().clear_bit();
153        w.bbpll_stop_force_low().set_bit()
154    });
155
156    let div_ref: u8;
157    let div7_0: u8;
158    let dr1: u8;
159    let dr3: u8;
160    let dchgp: u8;
161    let dcur: u8;
162    let dbias: u8;
163    match pll_freq {
164        PllClkConfig::_480 => {
165            // Configure 480M PLL
166            match xtal_freq {
167                XtalClkConfig::_40 => {
168                    div_ref = 0;
169                    // Will multiply by 8 + 4 = 12
170                    div7_0 = 8;
171                    dr1 = 0;
172                    dr3 = 0;
173                    dchgp = 5;
174                    dcur = 3;
175                    dbias = 2;
176                }
177            }
178
179            // Set the MODE_HF bit
180            regi2c::I2C_BBPLL_REG4.write_reg(0x6b);
181        }
182        PllClkConfig::_320 => {
183            // Configure 320M PLL
184            match xtal_freq {
185                XtalClkConfig::_40 => {
186                    div_ref = 0;
187                    // Will multiply by 4 + 4 = 8
188                    div7_0 = 4;
189                    dr1 = 0;
190                    dr3 = 0;
191                    dchgp = 5;
192                    dcur = 3;
193                    dbias = 2;
194                }
195            }
196
197            // Clear the MODE_HF bit
198            regi2c::I2C_BBPLL_REG4.write_reg(0x69);
199        }
200    }
201
202    const I2C_BBPLL_OC_DCHGP_LSB: u32 = 4;
203    const I2C_BBPLL_OC_DLREF_SEL_LSB: u32 = 6;
204    const I2C_BBPLL_OC_DHREF_SEL_LSB: u32 = 4;
205
206    let i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | div_ref;
207
208    // Weird, that the last two writes flip these values...
209    let i2c_bbpll_dcur =
210        (2 << I2C_BBPLL_OC_DLREF_SEL_LSB) | (1 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur;
211
212    regi2c::I2C_BBPLL_OC_REF.write_reg(i2c_bbpll_lref);
213    regi2c::I2C_BBPLL_OC_DIV_REG.write_reg(div7_0);
214    regi2c::I2C_BBPLL_OC_DR1.write_field(dr1);
215    regi2c::I2C_BBPLL_OC_DR3.write_field(dr3);
216    regi2c::I2C_BBPLL_REG6.write_reg(i2c_bbpll_dcur);
217    regi2c::I2C_BBPLL_OC_VCO_DBIAS.write_field(dbias);
218    regi2c::I2C_BBPLL_OC_DHREF_SEL.write_field(2);
219    regi2c::I2C_BBPLL_OC_DLREF_SEL.write_field(1);
220}
221
222fn configure_pll_clk_impl(
223    _clocks: &mut ClockTree,
224    _old_config: Option<PllClkConfig>,
225    _config: PllClkConfig,
226) {
227    // Nothing to do. The PLL may still be powered down. We'll configure it in
228    // `enable_pll_clk_impl`.
229}
230
231// RC_FAST_CLK
232
233fn enable_rc_fast_clk_impl(_clocks: &mut ClockTree, en: bool) {
234    // XPD_RC_OSCILLATOR exists but we'll manage that separately
235    const RTC_CNTL_FOSC_DFREQ_DEFAULT: u8 = 172;
236    LPWR::regs().clk_conf().modify(|_, w| {
237        // Confusing CK8M naming inherited from ESP32?
238
239        // CK8M_DFREQ value controls tuning of 8M clock.
240        unsafe { w.ck8m_dfreq().bits(RTC_CNTL_FOSC_DFREQ_DEFAULT) };
241
242        w.enb_ck8m().bit(!en);
243        w.dig_clk8m_en().bit(en); // digital system clock gate
244        // Do not force the clock either way.
245        w.ck8m_force_pd().clear_bit();
246        w.ck8m_force_pu().clear_bit()
247    });
248    LPWR::regs()
249        .timer1()
250        .modify(|_, w| unsafe { w.ck8m_wait().bits(if en { 5 } else { 20 }) });
251}
252
253// XTAL32K_CLK
254
255#[cfg(use_xtal32k)]
256fn enable_xtal32k_clk_impl(_clocks: &mut ClockTree, en: bool) {
257    // RTCIO could be configured to allow an external oscillator to be used. We could model this
258    // with a MUX, probably, but this is omitted for now for simplicity.
259
260    const CLK_LL_XTAL_32K_DAC_VAL: u8 = 3;
261    const CLK_LL_XTAL_32K_DRES_VAL: u8 = 3;
262    const CLK_LL_XTAL_32K_DGM_VAL: u8 = 3;
263    const CLK_LL_XTAL_32K_DBUF_VAL: bool = true; // differential buffer
264    LPWR::regs().ext_xtl_conf().modify(|_, w| unsafe {
265        w.xtal32k_gpio_sel().bit(false);
266
267        w.dac_xtal_32k().bits(CLK_LL_XTAL_32K_DAC_VAL);
268        w.dres_xtal_32k().bits(CLK_LL_XTAL_32K_DRES_VAL);
269        w.dgm_xtal_32k().bits(CLK_LL_XTAL_32K_DGM_VAL);
270        w.dbuf_xtal_32k().bit(CLK_LL_XTAL_32K_DBUF_VAL);
271
272        w.xpd_xtal_32k().bit(en)
273    });
274
275    // Enable for digital part
276    LPWR::regs()
277        .clk_conf()
278        .modify(|_, w| w.dig_xtal32k_en().bit(en));
279}
280
281// RC_SLOW_CLK
282
283fn enable_rc_slow_clk_impl(_clocks: &mut ClockTree, en: bool) {
284    if en {
285        // SCK_DCAP value controls tuning of 136k clock. The higher the value of DCAP, the lower the
286        // frequency. There is no separate enable bit, just make sure the calibration value is set.
287        const RTC_CNTL_SCK_DCAP_DEFAULT: u8 = 255;
288        LPWR::regs()
289            .rtc_cntl()
290            .modify(|_, w| unsafe { w.sck_dcap().bits(RTC_CNTL_SCK_DCAP_DEFAULT) });
291
292        // Also configure the divider here to its usual value of 1.
293
294        // Updating the divider should be part of the RC_SLOW_CLK divider config:
295        let slow_clk_conf = LPWR::regs().slow_clk_conf();
296        // Invalidate
297        let new_value = slow_clk_conf.modify(|_, w| w.ana_clk_div_vld().clear_bit());
298        // Update divider
299        let new_value = slow_clk_conf.write(|w| unsafe {
300            w.bits(new_value);
301            w.ana_clk_div().bits(0)
302        });
303        // Re-synchronize
304        slow_clk_conf.write(|w| {
305            unsafe { w.bits(new_value) };
306            w.ana_clk_div_vld().set_bit()
307        });
308    }
309}
310
311// RC_FAST_DIV_CLK
312
313fn enable_rc_fast_div_clk_impl(_clocks: &mut ClockTree, en: bool) {
314    LPWR::regs()
315        .clk_conf()
316        .modify(|_, w| w.enb_ck8m_div().bit(!en));
317}
318
319// SYSTEM_PRE_DIV_IN
320
321fn enable_system_pre_div_in_impl(_clocks: &mut ClockTree, _en: bool) {
322    // Nothing to do.
323}
324
325fn configure_system_pre_div_in_impl(
326    _clocks: &mut ClockTree,
327    _old_config: Option<SystemPreDivInConfig>,
328    _new_config: SystemPreDivInConfig,
329) {
330    // Nothing to do.
331}
332
333// SYSTEM_PRE_DIV
334
335fn enable_system_pre_div_impl(_clocks: &mut ClockTree, _en: bool) {
336    // Nothing to do.
337}
338
339fn configure_system_pre_div_impl(
340    _clocks: &mut ClockTree,
341    _old_config: Option<SystemPreDivConfig>,
342    new_config: SystemPreDivConfig,
343) {
344    APB_CTRL::regs()
345        .sysclk_conf()
346        .modify(|_, w| unsafe { w.pre_div_cnt().bits(new_config.divisor() as u16 & 0x3FF) });
347}
348
349// CPU_PLL_DIV_OUT
350
351fn enable_cpu_pll_div_out_impl(_clocks: &mut ClockTree, _en: bool) {
352    // Nothing to do.
353}
354
355fn configure_cpu_pll_div_out_impl(
356    _clocks: &mut ClockTree,
357    _old_config: Option<CpuPllDivOutConfig>,
358    _config: CpuPllDivOutConfig,
359) {
360    // Nothing to do.
361}
362
363// APB_CLK
364
365fn enable_apb_clk_impl(_clocks: &mut ClockTree, _en: bool) {
366    // Nothing to do.
367}
368
369fn configure_apb_clk_impl(
370    _clocks: &mut ClockTree,
371    _old_config: Option<ApbClkConfig>,
372    _new_config: ApbClkConfig,
373) {
374    // Nothing to do.
375}
376
377// CRYPTO_CLK
378
379fn enable_crypto_clk_impl(_clocks: &mut ClockTree, _en: bool) {
380    // Nothing to do.
381}
382
383fn configure_crypto_clk_impl(
384    _clocks: &mut ClockTree,
385    _old_config: Option<CryptoClkConfig>,
386    _new_config: CryptoClkConfig,
387) {
388    // Nothing to do.
389}
390
391// CPU_CLK
392
393fn configure_cpu_clk_impl(
394    clocks: &mut ClockTree,
395    _old_config: Option<CpuClkConfig>,
396    new_config: CpuClkConfig,
397) {
398    // Based on TRM Table 6.2-2
399    if new_config == CpuClkConfig::Pll {
400        SYSTEM::regs().cpu_per_conf().modify(|_, w| unsafe {
401            w.cpuperiod_sel()
402                .bits(match unwrap!(clocks.cpu_pll_div_out) {
403                    CpuPllDivOutConfig::_80 => 0,
404                    CpuPllDivOutConfig::_160 => 1,
405                })
406        });
407    }
408
409    SYSTEM::regs().sysclk_conf().modify(|_, w| unsafe {
410        w.pre_div_cnt().bits(0);
411        w.soc_clk_sel().bits(match new_config {
412            CpuClkConfig::Xtal => 0,
413            CpuClkConfig::RcFast => 2,
414            CpuClkConfig::Pll => 1,
415        })
416    });
417
418    let apb_freq = Rate::from_hz(apb_clk_frequency());
419    update_apb_frequency(apb_freq);
420
421    let cpu_freq = Rate::from_hz(cpu_clk_frequency());
422    ets_update_cpu_frequency_rom(cpu_freq.as_mhz());
423}
424
425fn update_apb_frequency(freq: Rate) {
426    let freq_shifted = (freq.as_hz() >> 12) & 0xFFFF;
427    let value = freq_shifted | (freq_shifted << 16);
428    LPWR::regs()
429        .store5()
430        .modify(|_, w| unsafe { w.data().bits(value) });
431}
432
433// PLL_80M
434
435fn enable_pll_80m_impl(_clocks: &mut ClockTree, _en: bool) {
436    // Nothing to do.
437}
438
439// PLL_160M
440
441fn enable_pll_160m_impl(_clocks: &mut ClockTree, _en: bool) {
442    // Nothing to do.
443}
444
445// RC_FAST_CLK_DIV_N
446
447fn enable_rc_fast_clk_div_n_impl(_clocks: &mut ClockTree, _en: bool) {
448    // Nothing to do.
449}
450
451fn configure_rc_fast_clk_div_n_impl(
452    _clocks: &mut ClockTree,
453    _old_config: Option<RcFastClkDivNConfig>,
454    new_config: RcFastClkDivNConfig,
455) {
456    let clk_conf = LPWR::regs().clk_conf();
457    // Invalidate because we may be changing the divider from some other value
458    let new_value = clk_conf.modify(|_, w| w.ck8m_div_sel_vld().clear_bit());
459    // Update divider
460    let new_value = clk_conf.write(|w| unsafe {
461        w.bits(new_value);
462        w.ck8m_div_sel().bits(new_config.divisor() as u8)
463    });
464    // Re-synchronize
465    clk_conf.write(|w| {
466        unsafe { w.bits(new_value) };
467        w.ck8m_div_sel_vld().set_bit()
468    });
469}
470
471// XTAL_DIV_CLK
472
473fn enable_xtal_div_clk_impl(_clocks: &mut ClockTree, _en: bool) {
474    // Nothing to do.
475}
476
477// RTC_SLOW_CLK
478
479fn enable_rtc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) {
480    // Nothing to do.
481}
482
483fn configure_rtc_slow_clk_impl(
484    _clocks: &mut ClockTree,
485    _old_config: Option<RtcSlowClkConfig>,
486    new_config: RtcSlowClkConfig,
487) {
488    LPWR::regs().clk_conf().modify(|_, w| unsafe {
489        // TODO: variants should be in PAC
490        w.ana_clk_rtc_sel().bits(match new_config {
491            #[cfg(use_xtal32k)]
492            RtcSlowClkConfig::Xtal32k => 1,
493            RtcSlowClkConfig::RcSlow => 0,
494            RtcSlowClkConfig::RcFast => 2,
495        })
496    });
497    ets_delay_us(300);
498}
499
500// RTC_FAST_CLK
501
502fn enable_rtc_fast_clk_impl(_clocks: &mut ClockTree, _en: bool) {
503    // Nothing to do.
504}
505
506fn configure_rtc_fast_clk_impl(
507    _clocks: &mut ClockTree,
508    _old_config: Option<RtcFastClkConfig>,
509    new_config: RtcFastClkConfig,
510) {
511    // TODO: variants should be fixed in PAC
512    LPWR::regs().clk_conf().modify(|_, w| match new_config {
513        RtcFastClkConfig::Xtal => w.fast_clk_rtc_sel().clear_bit(),
514        RtcFastClkConfig::Rc => w.fast_clk_rtc_sel().set_bit(),
515    });
516    ets_delay_us(3);
517}
518
519// LOW_POWER_CLK
520
521fn enable_low_power_clk_impl(_clocks: &mut ClockTree, _en: bool) {
522    // Nothing in esp-idf does this - is this managed by hardware, or the radio blobs?
523    // SYSTEM::regs()
524    //     .bt_lpck_div_frac()
525    //     .modify(|_, w| w.lpclk_rtc_en().bit(en));
526}
527
528fn configure_low_power_clk_impl(
529    _clocks: &mut ClockTree,
530    _old_config: Option<LowPowerClkConfig>,
531    new_config: LowPowerClkConfig,
532) {
533    SYSTEM::regs().bt_lpck_div_frac().modify(|_, w| {
534        w.lpclk_sel_8m()
535            .bit(new_config == LowPowerClkConfig::RcFast);
536        w.lpclk_sel_rtc_slow()
537            .bit(new_config == LowPowerClkConfig::RtcSlow);
538        w.lpclk_sel_xtal()
539            .bit(new_config == LowPowerClkConfig::Xtal);
540        w.lpclk_sel_xtal32k().bit({
541            cfg_select! {
542                use_xtal32k => new_config == LowPowerClkConfig::Xtal32k,
543                _ => false,
544            }
545        })
546    });
547}
548
549// UART_MEM_CLK
550
551fn enable_uart_mem_clk_impl(_clocks: &mut ClockTree, en: bool) {
552    // TODO: these functions (peripheral bus clock control) should be generated,
553    // replacing current PeripheralClockControl code.
554    // Enabling clock should probably not reset the peripheral.
555    let regs = SYSTEM::regs();
556
557    if en {
558        regs.perip_rst_en0()
559            .modify(|_, w| w.uart_mem_rst().bit(true));
560        regs.perip_rst_en0()
561            .modify(|_, w| w.uart_mem_rst().bit(false));
562    }
563
564    regs.perip_clk_en0()
565        .modify(|_, w| w.uart_mem_clk_en().bit(en));
566}
567
568// TIMG_CALIBRATION_CLOCK
569
570fn enable_timg_calibration_clock_impl(_clocks: &mut ClockTree, _en: bool) {
571    // Nothing to do, calibration clocks can only be selected. They are gated by the CALI_START
572    // bit, which is managed by the calibration process.
573}
574
575fn configure_timg_calibration_clock_impl(
576    _clocks: &mut ClockTree,
577    _old_config: Option<TimgCalibrationClockConfig>,
578    new_config: TimgCalibrationClockConfig,
579) {
580    TIMG0::regs().rtccalicfg().modify(|_, w| unsafe {
581        w.rtc_cali_clk_sel().bits(match new_config {
582            TimgCalibrationClockConfig::RcSlowClk => 0,
583            TimgCalibrationClockConfig::RcFastDivClk => 1,
584            #[cfg(use_xtal32k)]
585            TimgCalibrationClockConfig::Xtal32kClk => 2,
586        })
587    });
588}
589
590impl SdmInstance {
591    // SDM_FUNCTION_CLOCK
592
593    fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) {
594        crate::peripherals::GPIO_SD::regs()
595            .sigmadelta_misc()
596            .modify(|_, w| w.function_clk_en().bit(en));
597    }
598}
599impl RmtInstance {
600    // RMT_SCLK
601
602    fn enable_sclk_impl(self, _clocks: &mut ClockTree, en: bool) {
603        crate::peripherals::RMT::regs()
604            .sys_conf()
605            .modify(|_, w| w.sclk_active().bit(en));
606    }
607
608    fn configure_sclk_impl(
609        self,
610        _clocks: &mut ClockTree,
611        _old_config: Option<RmtSclkConfig>,
612        new_config: RmtSclkConfig,
613    ) {
614        crate::peripherals::RMT::regs()
615            .sys_conf()
616            .modify(|_, w| unsafe {
617                w.clk_en().clear_bit();
618                w.sclk_sel().bits(match new_config {
619                    RmtSclkConfig::ApbClk => 1,
620                    RmtSclkConfig::RcFastClk => 2,
621                    RmtSclkConfig::XtalClk => 3,
622                })
623            });
624    }
625}
626
627impl TimgInstance {
628    // TIMG_FUNCTION_CLOCK
629    // Note that the function clock is a pre-requisite of the timer, but does not enable the
630    // counter.
631
632    fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) {
633        let regs = match self {
634            TimgInstance::Timg0 => TIMG0::regs(),
635            TimgInstance::Timg1 => TIMG1::regs(),
636        };
637        regs.regclk().modify(|_, w| {
638            w.timer_clk_is_active().bit(en);
639            w.clk_en().bit(en)
640        });
641    }
642
643    fn configure_function_clock_impl(
644        self,
645        _clocks: &mut ClockTree,
646        _old_config: Option<TimgFunctionClockConfig>,
647        new_config: TimgFunctionClockConfig,
648    ) {
649        let regs = match self {
650            TimgInstance::Timg0 => TIMG0::regs(),
651            TimgInstance::Timg1 => TIMG1::regs(),
652        };
653        regs.t(0).config().modify(|_, w| {
654            w.use_xtal()
655                .bit(new_config == TimgFunctionClockConfig::XtalClk)
656        });
657    }
658
659    // TIMG_WDT_CLOCK
660
661    fn enable_wdt_clock_impl(self, _clocks: &mut ClockTree, _en: bool) {
662        // No separate clock control enable bit.
663    }
664
665    fn configure_wdt_clock_impl(
666        self,
667        _clocks: &mut ClockTree,
668        _old_config: Option<TimgWdtClockConfig>,
669        new_config: TimgWdtClockConfig,
670    ) {
671        let regs = match self {
672            TimgInstance::Timg0 => TIMG0::regs(),
673            TimgInstance::Timg1 => TIMG1::regs(),
674        };
675        regs.wdtconfig0().modify(|_, w| {
676            w.wdt_use_xtal()
677                .bit(new_config == TimgWdtClockConfig::XtalClk)
678        });
679    }
680}