Skip to main content

esp_hal/soc/esp32c2/
clocks.rs

1//! Clock tree definitions and implementations for ESP32-C2.
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: using external 32K oscillator, divider for
12//!   RC_FAST and possibly more.
13#![allow(dead_code, reason = "Some of this is bound to be unused")]
14#![allow(missing_docs, reason = "Experimental")]
15
16// TODO: This is a temporary place for this, should probably be moved into clocks_ll.
17
18use esp_rom_sys::rom::{ets_delay_us, ets_update_cpu_frequency_rom};
19
20use crate::{
21    clock::Clocks,
22    peripherals::{I2C_ANA_MST, LPWR, SYSTEM, TIMG0, UART0, UART1},
23    rtc_cntl::Rtc,
24    soc::regi2c,
25    time::Rate,
26};
27
28define_clock_tree_types!();
29
30/// Clock configuration options.
31#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
32#[cfg_attr(feature = "defmt", derive(defmt::Format))]
33#[allow(
34    clippy::enum_variant_names,
35    reason = "MHz suffix indicates physical unit."
36)]
37#[non_exhaustive]
38pub enum CpuClock {
39    /// 80 MHz CPU clock
40    #[default]
41    _80MHz  = 80,
42
43    /// 120 MHz CPU clock
44    _120MHz = 120,
45}
46
47impl CpuClock {
48    const PRESET_80: ClockConfig = ClockConfig {
49        xtal_clk: None,
50        system_pre_div: None,
51        cpu_pll_div: Some(CpuPllDivConfig::new(CpuPllDivDivisor::_6)),
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_120: ClockConfig = ClockConfig {
60        xtal_clk: None,
61        system_pre_div: None,
62        cpu_pll_div: Some(CpuPllDivConfig::new(CpuPllDivDivisor::_4)),
63        cpu_clk: Some(CpuClkConfig::Pll),
64        rc_fast_clk_div_n: Some(RcFastClkDivNConfig::new(0)),
65        rtc_slow_clk: Some(RtcSlowClkConfig::RcSlow),
66        rtc_fast_clk: Some(RtcFastClkConfig::Rc),
67        low_power_clk: Some(LowPowerClkConfig::RtcSlow),
68        timg_calibration_clock: None,
69    };
70}
71
72impl From<CpuClock> for ClockConfig {
73    fn from(value: CpuClock) -> ClockConfig {
74        match value {
75            CpuClock::_80MHz => CpuClock::PRESET_80,
76            CpuClock::_120MHz => CpuClock::PRESET_120,
77        }
78    }
79}
80
81impl Default for ClockConfig {
82    fn default() -> Self {
83        Self::from(CpuClock::default())
84    }
85}
86
87impl ClockConfig {
88    pub(crate) fn try_get_preset(self) -> Option<CpuClock> {
89        match self {
90            v if v == CpuClock::PRESET_80 => Some(CpuClock::_80MHz),
91            v if v == CpuClock::PRESET_120 => Some(CpuClock::_120MHz),
92            _ => None,
93        }
94    }
95
96    pub(crate) fn configure(mut self) {
97        // Switch CPU to XTAL before reconfiguring PLL.
98        ClockTree::with(|clocks| {
99            configure_xtal_clk(clocks, XtalClkConfig::_40);
100            configure_system_pre_div(clocks, SystemPreDivConfig::new(0));
101            configure_cpu_clk(clocks, CpuClkConfig::Xtal);
102        });
103
104        // Detect XTAL if unset.
105        // FIXME: this doesn't support running from RC_FAST_CLK. We should rework detection to
106        // only run when requesting XTAL.
107        if self.xtal_clk.is_none() {
108            // While the bootloader stores a crystal frequency in a retention register,
109            // that comes from a constant that we should not trust. If the user did not
110            // provide a crystal frequency, we should detect it.
111            let xtal = ClockTree::with(detect_xtal_freq);
112            debug!("Auto-detected XTAL frequency: {}", xtal.value());
113            self.xtal_clk = Some(xtal);
114        }
115
116        self.apply();
117    }
118}
119
120fn detect_xtal_freq(clocks: &mut ClockTree) -> XtalClkConfig {
121    // Estimate XTAL frequency using RC_FAST/256 as the calibration clock. RC_SLOW is too
122    // imprecise for reliable detection.
123    const CALIBRATION_CYCLES: u32 = 10;
124
125    // The digital path for RC_FAST_D256 must be enabled for TIMG calibration to work.
126    LPWR::regs()
127        .clk_conf()
128        .modify(|_, w| w.dig_clk8m_d256_en().set_bit());
129
130    let (xtal_cycles, calibration_clock_frequency) = Clocks::measure_rtc_clock(
131        clocks,
132        TimgCalibrationClockConfig::RcFastDivClk,
133        TimgFunctionClockConfig::XtalClk,
134        CALIBRATION_CYCLES,
135    );
136
137    LPWR::regs()
138        .clk_conf()
139        .modify(|_, w| w.dig_clk8m_d256_en().clear_bit());
140
141    let mhz = (calibration_clock_frequency * xtal_cycles / CALIBRATION_CYCLES).as_mhz();
142
143    if mhz.abs_diff(40) < mhz.abs_diff(26) {
144        XtalClkConfig::_40
145    } else {
146        XtalClkConfig::_26
147    }
148}
149
150// XTAL_CLK
151
152fn configure_xtal_clk_impl(
153    _clocks: &mut ClockTree,
154    _old_config: Option<XtalClkConfig>,
155    config: XtalClkConfig,
156) {
157    // The stored configuration affects PLL settings instead. We save the value in a register
158    // similar to ESP-IDF, just in case something relies on that, or, if we can in the future read
159    // back the value instead of wasting RAM on it.
160
161    // Used by `rtc_clk_xtal_freq_get` patched ROM function.
162    let freq_mhz = config.value() / 1_000_000;
163    LPWR::regs().store4().modify(|r, w| unsafe {
164        // The data is stored in two copies of 16-bit values. The first bit overwrites the LSB of
165        // the frequency value with DISABLE_ROM_LOG.
166
167        // Copy the DISABLE_ROM_LOG bit
168        let disable_rom_log_bit = r.bits() & Rtc::RTC_DISABLE_ROM_LOG;
169        let half = (freq_mhz & (0xFFFF & !Rtc::RTC_DISABLE_ROM_LOG)) | disable_rom_log_bit;
170        w.data().bits(half | (half << 16))
171    });
172}
173
174// PLL_CLK
175
176fn enable_pll_clk_impl(clocks: &mut ClockTree, en: bool) {
177    const I2C_BBPLL_OC_DCHGP_LSB: u32 = 4;
178    const I2C_BBPLL_OC_DHREF_SEL_LSB: u32 = 4;
179    const I2C_BBPLL_OC_DLREF_SEL_LSB: u32 = 6;
180
181    // regi2c_ctrl_ll_i2c_bbpll_enable
182    I2C_ANA_MST::regs()
183        .ana_config()
184        .modify(|_, w| w.bbpll_pd().bit(!en));
185
186    LPWR::regs().options0().modify(|_, w| {
187        let power_down = !en;
188        w.bb_i2c_force_pd().bit(power_down);
189        w.bbpll_force_pd().bit(power_down);
190        w.bbpll_i2c_force_pd().bit(power_down)
191    });
192
193    if !en {
194        return;
195    }
196
197    // Digital part
198
199    SYSTEM::regs()
200        .cpu_per_conf()
201        .modify(|_, w| w.pll_freq_sel().set_bit()); // Undocumented, selects 480MHz PLL.
202
203    // Analog part
204
205    // Start BBPLL self-calibration
206    I2C_ANA_MST::regs().ana_conf0().modify(|_, w| {
207        w.bbpll_stop_force_high().clear_bit();
208        w.bbpll_stop_force_low().set_bit()
209    });
210
211    let div_ref: u8;
212    let div7_0: u8;
213    let dr1: u8;
214    let dr3: u8;
215    let dchgp: u8;
216    let dcur: u8;
217    let dbias: u8;
218    // Configure 480M PLL
219    match unwrap!(clocks.xtal_clk) {
220        XtalClkConfig::_26 => {
221            // Divide by 13 -> reference = 2MHz
222            div_ref = 12;
223            // Multiply by 236 + 4 -> PLL output = 480MHz
224            div7_0 = 236;
225            dr1 = 4;
226            dr3 = 4;
227            dchgp = 0;
228            dcur = 0;
229            dbias = 2;
230        }
231        XtalClkConfig::_40 => {
232            // Divide by 1 -> reference = 40MHz
233            div_ref = 0;
234            // Multiply by 8 + 4 -> PLL output = 480MHz
235            div7_0 = 8;
236            dr1 = 0;
237            dr3 = 0;
238            dchgp = 5;
239            dcur = 3;
240            dbias = 2;
241        }
242    }
243
244    regi2c::I2C_BBPLL_REG4.write_reg(0x6b);
245
246    let i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | div_ref;
247    let i2c_bbpll_dcur =
248        (1 << I2C_BBPLL_OC_DLREF_SEL_LSB) | (3 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur;
249
250    regi2c::I2C_BBPLL_OC_REF.write_reg(i2c_bbpll_lref);
251    regi2c::I2C_BBPLL_OC_DIV_REG.write_reg(div7_0);
252    regi2c::I2C_BBPLL_OC_DR1.write_field(dr1);
253    regi2c::I2C_BBPLL_OC_DR3.write_field(dr3);
254    regi2c::I2C_BBPLL_REG6.write_reg(i2c_bbpll_dcur);
255    regi2c::I2C_BBPLL_OC_VCO_DBIAS.write_field(dbias);
256
257    while I2C_ANA_MST::regs()
258        .ana_conf0()
259        .read()
260        .bbpll_cal_done()
261        .bit_is_clear()
262    {}
263
264    ets_delay_us(10);
265
266    // Stop BBPLL self-calibration
267    I2C_ANA_MST::regs().ana_conf0().modify(|_, w| {
268        w.bbpll_stop_force_high().set_bit();
269        w.bbpll_stop_force_low().clear_bit()
270    });
271}
272
273// RC_FAST_CLK
274
275fn enable_rc_fast_clk_impl(_clocks: &mut ClockTree, en: bool) {
276    // XPD_RC_OSCILLATOR exists but we'll manage that separately
277    const RTC_CNTL_FOSC_DFREQ_DEFAULT: u8 = 172;
278    LPWR::regs().clk_conf().modify(|_, w| {
279        // Confusing CK8M naming inherited from ESP32?
280
281        // CK8M_DFREQ value controls tuning of 8M clock.
282        unsafe { w.ck8m_dfreq().bits(RTC_CNTL_FOSC_DFREQ_DEFAULT) };
283
284        w.enb_ck8m().bit(!en);
285        w.dig_clk8m_en().bit(en); // digital system clock gate
286        // Do not force the clock either way.
287        w.ck8m_force_pd().clear_bit();
288        w.ck8m_force_pu().clear_bit()
289    });
290    LPWR::regs()
291        .timer1()
292        .modify(|_, w| unsafe { w.ck8m_wait().bits(if en { 5 } else { 20 }) });
293}
294
295// OSC_SLOW_CLK
296
297fn enable_osc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) {
298    // Nothing to do.
299}
300
301// RC_SLOW_CLK
302
303fn enable_rc_slow_clk_impl(_clocks: &mut ClockTree, en: bool) {
304    if !en {
305        return;
306    }
307
308    // SCK_DCAP value controls tuning of 136k clock. The higher the value of DCAP, the lower the
309    // frequency. There is no separate enable bit, just make sure the calibration value is set.
310    const RTC_CNTL_SCK_DCAP_DEFAULT: u8 = 255;
311    LPWR::regs()
312        .rtc_cntl()
313        .modify(|_, w| unsafe { w.sck_dcap().bits(RTC_CNTL_SCK_DCAP_DEFAULT) });
314
315    // Also configure the divider here to its usual value of 1.
316
317    // Updating the divider should be part of the RC_SLOW_CLK divider config:
318    let slow_clk_conf = LPWR::regs().slow_clk_conf();
319    // Invalidate
320    let new_value = slow_clk_conf.modify(|_, w| w.ana_clk_div_vld().clear_bit());
321    // Update divider
322    let new_value = slow_clk_conf.write(|w| unsafe {
323        w.bits(new_value);
324        w.ana_clk_div().bits(0)
325    });
326    // Re-synchronize
327    slow_clk_conf.write(|w| {
328        unsafe { w.bits(new_value) };
329        w.ana_clk_div_vld().set_bit()
330    });
331}
332
333// RC_FAST_DIV_CLK
334
335fn enable_rc_fast_div_clk_impl(_clocks: &mut ClockTree, en: bool) {
336    LPWR::regs()
337        .clk_conf()
338        .modify(|_, w| w.enb_ck8m_div().bit(!en));
339}
340
341// SYSTEM_PRE_DIV_IN
342
343// Not an actual MUX, used to allow configuring the DIV divider as one block.
344// Related to CPU clock source configuration.
345fn enable_system_pre_div_in_impl(_clocks: &mut ClockTree, _en: bool) {
346    // Nothing to do.
347}
348
349fn configure_system_pre_div_in_impl(
350    _clocks: &mut ClockTree,
351    _old_config: Option<SystemPreDivInConfig>,
352    _new_config: SystemPreDivInConfig,
353) {
354    // Nothing to do.
355}
356
357// SYSTEM_PRE_DIV
358
359fn enable_system_pre_div_impl(_clocks: &mut ClockTree, _en: bool) {
360    // Nothing to do.
361}
362
363fn configure_system_pre_div_impl(
364    _clocks: &mut ClockTree,
365    _old_config: Option<SystemPreDivConfig>,
366    new_config: SystemPreDivConfig,
367) {
368    SYSTEM::regs()
369        .sysclk_conf()
370        .modify(|_, w| unsafe { w.pre_div_cnt().bits(new_config.divisor() as u16 & 0x3FF) });
371}
372
373// CPU_PLL_DIV
374
375fn enable_cpu_pll_div_impl(_clocks: &mut ClockTree, _en: bool) {
376    // Nothing to do.
377}
378
379fn configure_cpu_pll_div_impl(
380    _clocks: &mut ClockTree,
381    _old_config: Option<CpuPllDivConfig>,
382    _new_config: CpuPllDivConfig,
383) {
384    // Nothing to do.
385}
386
387// APB_CLK
388
389fn enable_apb_clk_impl(_clocks: &mut ClockTree, _en: bool) {
390    // Nothing to do.
391}
392
393fn configure_apb_clk_impl(
394    _clocks: &mut ClockTree,
395    _old_config: Option<ApbClkConfig>,
396    _new_config: ApbClkConfig,
397) {
398    // Nothing to do.
399}
400
401// CRYPTO_CLK
402
403fn enable_crypto_clk_impl(_clocks: &mut ClockTree, _en: bool) {
404    // Nothing to do.
405}
406
407fn configure_crypto_clk_impl(
408    _clocks: &mut ClockTree,
409    _old_config: Option<CryptoClkConfig>,
410    _new_config: CryptoClkConfig,
411) {
412    // Nothing to do, determined by CPU clock.
413}
414
415// MSPI_CLK
416
417fn enable_mspi_clk_impl(_clocks: &mut ClockTree, _en: bool) {
418    // Nothing to do.
419}
420
421fn configure_mspi_clk_impl(
422    _clocks: &mut ClockTree,
423    _old_config: Option<MspiClkConfig>,
424    _new_config: MspiClkConfig,
425) {
426    // Nothing to do, determined by CPU clock.
427}
428
429// CPU_CLK
430
431fn configure_cpu_clk_impl(
432    clocks: &mut ClockTree,
433    _old_config: Option<CpuClkConfig>,
434    new_config: CpuClkConfig,
435) {
436    // Based on TRM Table 6.2-2
437    if new_config == CpuClkConfig::Pll {
438        SYSTEM::regs().cpu_per_conf().modify(|_, w| unsafe {
439            w.cpuperiod_sel()
440                .bits(match unwrap!(clocks.cpu_pll_div).divisor {
441                    CpuPllDivDivisor::_4 => 1,
442                    CpuPllDivDivisor::_6 => 0,
443                })
444        });
445    }
446
447    SYSTEM::regs().sysclk_conf().modify(|_, w| unsafe {
448        w.soc_clk_sel().bits(match new_config {
449            CpuClkConfig::Xtal => 0,
450            CpuClkConfig::RcFast => 2,
451            CpuClkConfig::Pll => 1,
452        })
453    });
454
455    let apb_freq = Rate::from_hz(apb_clk_frequency(clocks));
456    update_apb_frequency(apb_freq);
457
458    let cpu_freq = Rate::from_hz(cpu_clk_frequency(clocks));
459    ets_update_cpu_frequency_rom(cpu_freq.as_mhz());
460}
461
462fn update_apb_frequency(freq: Rate) {
463    let freq_shifted = (freq.as_hz() >> 12) & 0xFFFF;
464    let value = freq_shifted | (freq_shifted << 16);
465    LPWR::regs()
466        .store5()
467        .modify(|_, w| unsafe { w.data().bits(value) });
468}
469
470// PLL_40M
471
472fn enable_pll_40m_impl(_clocks: &mut ClockTree, _en: bool) {
473    // Nothing to do.
474}
475
476// PLL_60M
477
478fn enable_pll_60m_impl(_clocks: &mut ClockTree, _en: bool) {
479    // Nothing to do.
480}
481
482// PLL_80M
483
484fn enable_pll_80m_impl(_clocks: &mut ClockTree, _en: bool) {
485    // Nothing to do.
486}
487
488// CPU_DIV2
489
490fn enable_cpu_div2_impl(_clocks: &mut ClockTree, _en: bool) {
491    // Nothing to do.
492}
493
494// RC_FAST_CLK_DIV_N
495
496fn enable_rc_fast_clk_div_n_impl(_clocks: &mut ClockTree, _en: bool) {
497    // Nothing to do.
498}
499
500fn configure_rc_fast_clk_div_n_impl(
501    _clocks: &mut ClockTree,
502    _old_config: Option<RcFastClkDivNConfig>,
503    new_config: RcFastClkDivNConfig,
504) {
505    let clk_conf = LPWR::regs().clk_conf();
506    // Invalidate because we may be changing the divider from some other value
507    let new_value = clk_conf.modify(|_, w| w.ck8m_div_sel_vld().clear_bit());
508    // Update divider
509    let new_value = clk_conf.write(|w| unsafe {
510        w.bits(new_value);
511        w.ck8m_div_sel().bits(new_config.divisor() as u8)
512    });
513    // Re-synchronize
514    clk_conf.write(|w| {
515        unsafe { w.bits(new_value) };
516        w.ck8m_div_sel_vld().set_bit()
517    });
518}
519
520// XTAL_DIV_CLK
521
522fn enable_xtal_div_clk_impl(_clocks: &mut ClockTree, _en: bool) {
523    // Nothing to do.
524}
525
526// RTC_SLOW_CLK
527
528fn enable_rtc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) {
529    // Nothing to do.
530}
531
532fn configure_rtc_slow_clk_impl(
533    _clocks: &mut ClockTree,
534    _old_config: Option<RtcSlowClkConfig>,
535    new_config: RtcSlowClkConfig,
536) {
537    LPWR::regs().clk_conf().modify(|_, w| unsafe {
538        // TODO: variants should be in PAC
539        w.ana_clk_rtc_sel().bits(match new_config {
540            RtcSlowClkConfig::OscSlow => 1,
541            RtcSlowClkConfig::RcSlow => 0,
542            RtcSlowClkConfig::RcFast => 2,
543        })
544    });
545
546    ets_delay_us(300);
547}
548
549// RTC_FAST_CLK
550
551fn enable_rtc_fast_clk_impl(_clocks: &mut ClockTree, _en: bool) {
552    // Nothing to do.
553}
554
555fn configure_rtc_fast_clk_impl(
556    _clocks: &mut ClockTree,
557    _old_config: Option<RtcFastClkConfig>,
558    new_config: RtcFastClkConfig,
559) {
560    // TODO: variants should be fixed in PAC
561    LPWR::regs().clk_conf().modify(|_, w| match new_config {
562        RtcFastClkConfig::Xtal => w.fast_clk_rtc_sel().clear_bit(),
563        RtcFastClkConfig::Rc => w.fast_clk_rtc_sel().set_bit(),
564    });
565    ets_delay_us(3);
566}
567
568// LOW_POWER_CLK
569
570fn enable_low_power_clk_impl(_clocks: &mut ClockTree, _en: bool) {
571    // Nothing in esp-idf does this - is this managed by hardware, or the radio blobs?
572    // SYSTEM::regs()
573    //     .bt_lpck_div_frac()
574    //     .modify(|_, w| w.lpclk_rtc_en().bit(en));
575}
576
577fn configure_low_power_clk_impl(
578    _clocks: &mut ClockTree,
579    _old_config: Option<LowPowerClkConfig>,
580    new_config: LowPowerClkConfig,
581) {
582    SYSTEM::regs().bt_lpck_div_frac().modify(|_, w| {
583        w.lpclk_sel_8m()
584            .bit(new_config == LowPowerClkConfig::RcFast);
585        w.lpclk_sel_rtc_slow()
586            .bit(new_config == LowPowerClkConfig::RtcSlow);
587        w.lpclk_sel_xtal()
588            .bit(new_config == LowPowerClkConfig::Xtal);
589        w.lpclk_sel_xtal32k()
590            .bit(new_config == LowPowerClkConfig::OscSlow)
591    });
592}
593
594// UART_MEM_CLK
595
596fn enable_uart_mem_clk_impl(_clocks: &mut ClockTree, en: bool) {
597    // TODO: these functions (peripheral bus clock control) should be generated,
598    // replacing current PeripheralClockControl code.
599    // Enabling clock should probably not reset the peripheral.
600    let regs = SYSTEM::regs();
601
602    if en {
603        regs.perip_rst_en0()
604            .modify(|_, w| w.uart_mem_rst().bit(true));
605        regs.perip_rst_en0()
606            .modify(|_, w| w.uart_mem_rst().bit(false));
607    }
608
609    regs.perip_clk_en0()
610        .modify(|_, w| w.uart_mem_clk_en().bit(en));
611}
612
613// TIMG_CALIBRATION_CLOCK
614
615fn enable_timg_calibration_clock_impl(_clocks: &mut ClockTree, _en: bool) {
616    // Nothing to do, calibration clocks can only be selected. They are gated by the CALI_START
617    // bit, which is managed by the calibration process.
618}
619
620fn configure_timg_calibration_clock_impl(
621    _clocks: &mut ClockTree,
622    _old_config: Option<TimgCalibrationClockConfig>,
623    new_config: TimgCalibrationClockConfig,
624) {
625    TIMG0::regs().rtccalicfg().modify(|_, w| unsafe {
626        w.rtc_cali_clk_sel().bits(match new_config {
627            TimgCalibrationClockConfig::RcSlowClk => 0,
628            TimgCalibrationClockConfig::RcFastDivClk => 1,
629            TimgCalibrationClockConfig::Osc32kClk => 2,
630        })
631    });
632}
633
634impl TimgInstance {
635    // TIMG_FUNCTION_CLOCK
636
637    fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) {
638        // TODO: should we model T0_DIVIDER, too?
639        TIMG0::regs()
640            .regclk()
641            .modify(|_, w| w.timer_clk_is_active().bit(en));
642    }
643
644    fn configure_function_clock_impl(
645        self,
646        _clocks: &mut ClockTree,
647        _old_config: Option<TimgFunctionClockConfig>,
648        new_config: TimgFunctionClockConfig,
649    ) {
650        TIMG0::regs().t(0).config().modify(|_, w| {
651            w.use_xtal()
652                .bit(new_config == TimgFunctionClockConfig::XtalClk)
653        });
654    }
655
656    // TIMG_WDT_CLOCK
657
658    fn enable_wdt_clock_impl(self, _clocks: &mut ClockTree, _en: bool) {
659        // No separate clock control enable bit.
660    }
661
662    fn configure_wdt_clock_impl(
663        self,
664        _clocks: &mut ClockTree,
665        _old_config: Option<TimgWdtClockConfig>,
666        new_config: TimgWdtClockConfig,
667    ) {
668        TIMG0::regs().wdtconfig0().modify(|_, w| {
669            w.wdt_use_xtal()
670                .bit(new_config == TimgWdtClockConfig::XtalClk)
671        });
672    }
673}
674
675impl UartInstance {
676    // UART_FUNCTION_CLOCK
677
678    fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) {
679        let regs = match self {
680            UartInstance::Uart0 => UART0::regs(),
681            UartInstance::Uart1 => UART1::regs(),
682        };
683        regs.clk_conf().modify(|_, w| w.sclk_en().bit(en));
684    }
685
686    fn configure_function_clock_impl(
687        self,
688        _clocks: &mut ClockTree,
689        _old_config: Option<UartFunctionClockConfig>,
690        new_config: UartFunctionClockConfig,
691    ) {
692        let regs = match self {
693            UartInstance::Uart0 => UART0::regs(),
694            UartInstance::Uart1 => UART1::regs(),
695        };
696        regs.clk_conf().modify(|_, w| unsafe {
697            w.sclk_sel().bits(match new_config.sclk {
698                UartFunctionClockSclk::PllF40m => 1,
699                UartFunctionClockSclk::RcFast => 2,
700                UartFunctionClockSclk::Xtal => 3,
701            });
702            w.sclk_div_num().bits(new_config.div_num as _)
703        });
704    }
705
706    // UART_MEM_CLOCK
707
708    fn enable_mem_clock_impl(self, _clocks: &mut ClockTree, _en: bool) {
709        // Nothing to do.
710    }
711
712    fn configure_mem_clock_impl(
713        self,
714        _clocks: &mut ClockTree,
715        _old_config: Option<UartMemClockConfig>,
716        _new_config: UartMemClockConfig,
717    ) {
718        // Nothing to do.
719    }
720
721    // UART_BAUD_RATE_GENERATOR
722
723    fn enable_baud_rate_generator_impl(self, _clocks: &mut ClockTree, _en: bool) {
724        // Nothing to do.
725    }
726
727    fn configure_baud_rate_generator_impl(
728        self,
729        _clocks: &mut ClockTree,
730        _old_config: Option<UartBaudRateGeneratorConfig>,
731        new_config: UartBaudRateGeneratorConfig,
732    ) {
733        let regs = match self {
734            UartInstance::Uart0 => UART0::regs(),
735            UartInstance::Uart1 => UART1::regs(),
736        };
737        regs.clkdiv().write(|w| unsafe {
738            w.clkdiv().bits(new_config.integral as _);
739            w.frag().bits(new_config.fractional as _)
740        });
741    }
742}