Skip to main content

esp_hal/soc/esp32p4/
clocks.rs

1//! Clock tree for ESP32-P4X (chip revision v3.x / eco5).
2//!
3//! CPLL: 400 MHz (eco5 default), 360 MHz (conservative)
4//! SPLL: 480 MHz (peripheral clocks)
5//! MPLL: 400 MHz (PSRAM, media)
6//! XTAL: 40 MHz
7//!
8//! Clock hierarchy: XTAL -> CPLL -> CPU_ROOT -> CPU/APB dividers
9//! SPLL -> PLL_F240M/160M/120M/80M/20M (peripheral clocks)
10#![allow(dead_code, reason = "Clock functions called from generated macro code")]
11#![allow(
12    missing_docs,
13    reason = "Clock-tree types come from a generated macro that does not emit doc strings"
14)]
15#![allow(
16    clippy::enum_variant_names,
17    reason = "CPU frequency variant names follow the chip-spec MHz convention"
18)]
19
20use core::sync::atomic::{AtomicBool, Ordering};
21
22use esp_rom_sys::rom::ets_update_cpu_frequency_rom;
23
24use crate::{
25    peripherals::{HP_SYS_CLKRST, LP_AON_CLKRST, PMU},
26    soc::regi2c,
27    time::Rate,
28};
29
30define_clock_tree_types!();
31
32/// CPU clock frequency presets for ESP32-P4.
33#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
34#[cfg_attr(feature = "defmt", derive(defmt::Format))]
35#[non_exhaustive]
36pub enum CpuClock {
37    /// 400 MHz CPU clock (eco5 / v3.x maximum)
38    /// CPLL -> CPU_ROOT -> CPU/1, APB divider /4 = 100MHz
39    _400MHz = 400,
40
41    /// 200 MHz CPU clock (low power)
42    /// CPLL -> CPU_ROOT -> CPU/2, APB /2 = 100MHz
43    _200MHz = 200,
44
45    /// 100 MHz CPU clock (ultra low power)
46    /// CPLL -> CPU_ROOT -> CPU/4, APB /1 = 100MHz
47    #[default]
48    _100MHz = 100,
49}
50
51impl CpuClock {
52    // Preset: 400 MHz CPU, 100 MHz APB
53    const PRESET_400: ClockConfig = ClockConfig {
54        cpu_root_clk: Some(CpuRootClkConfig::Cpll),
55        cpll_clk: Some(CpllClkConfig::_400),
56        mpll_clk: Some(MpllClkConfig::_500),
57        cpu_clk: Some(CpuClkConfig::new(0)), // /1 = 400 MHz
58        mem_clk: Some(MemClkConfig::new(1)), // /2 = 200 MHz
59        sys_clk: Some(SysClkConfig::new(0)), // /1 = 200 MHz
60        apb_clk: Some(ApbClkConfig::new(1)), // /2 = 100 MHz
61        iomux_function_clock: Some(IomuxFunctionClockConfig::new(
62            IomuxFunctionClockSource::PllF80m,
63            0,
64        )),
65        lp_fast_clk: Some(LpFastClkConfig::RcFast),
66        lp_slow_clk: Some(LpSlowClkConfig::RcSlow),
67        timg_calibration_clock: None,
68    };
69
70    // TODO: 360 MHz preset
71
72    const PRESET_200: ClockConfig = ClockConfig {
73        cpu_root_clk: Some(CpuRootClkConfig::Cpll),
74        cpll_clk: Some(CpllClkConfig::_400),
75        mpll_clk: Some(MpllClkConfig::_500),
76        cpu_clk: Some(CpuClkConfig::new(1)), // /2 = 200 MHz
77        mem_clk: Some(MemClkConfig::new(0)), // /1 = 200 MHz
78        sys_clk: Some(SysClkConfig::new(0)), // /1 = 200 MHz
79        apb_clk: Some(ApbClkConfig::new(1)), // /2 = 100 MHz
80        iomux_function_clock: Some(IomuxFunctionClockConfig::new(
81            IomuxFunctionClockSource::PllF80m,
82            0,
83        )),
84        lp_fast_clk: Some(LpFastClkConfig::RcFast),
85        lp_slow_clk: Some(LpSlowClkConfig::RcSlow),
86        timg_calibration_clock: None,
87    };
88
89    const PRESET_100: ClockConfig = ClockConfig {
90        cpu_root_clk: Some(CpuRootClkConfig::Cpll),
91        cpll_clk: Some(CpllClkConfig::_400),
92        mpll_clk: Some(MpllClkConfig::_500),
93        cpu_clk: Some(CpuClkConfig::new(3)), // /4 = 100 MHz
94        mem_clk: Some(MemClkConfig::new(0)), // /1 = 100 MHz
95        sys_clk: Some(SysClkConfig::new(0)), // /1 = 100 MHz
96        apb_clk: Some(ApbClkConfig::new(0)), // /1 = 100 MHz
97        iomux_function_clock: Some(IomuxFunctionClockConfig::new(
98            IomuxFunctionClockSource::PllF80m,
99            0,
100        )),
101        lp_fast_clk: Some(LpFastClkConfig::RcFast),
102        lp_slow_clk: Some(LpSlowClkConfig::RcSlow),
103        timg_calibration_clock: None,
104    };
105}
106
107impl From<CpuClock> for ClockConfig {
108    fn from(value: CpuClock) -> ClockConfig {
109        match value {
110            CpuClock::_400MHz => CpuClock::PRESET_400,
111            CpuClock::_200MHz => CpuClock::PRESET_200,
112            CpuClock::_100MHz => CpuClock::PRESET_100,
113        }
114    }
115}
116
117impl Default for ClockConfig {
118    fn default() -> Self {
119        Self::from(CpuClock::default())
120    }
121}
122
123impl ClockConfig {
124    pub(crate) fn try_get_preset(self) -> Option<CpuClock> {
125        match self {
126            v if v == CpuClock::PRESET_400 => Some(CpuClock::_400MHz),
127            v if v == CpuClock::PRESET_200 => Some(CpuClock::_200MHz),
128            v if v == CpuClock::PRESET_100 => Some(CpuClock::_100MHz),
129            _ => None,
130        }
131    }
132
133    pub(crate) fn configure(self, clocks: &mut ClockTree) {
134        // The CPU, memory, system, and APB dividers share one update signal.
135        // Write the complete configuration before latching it; applying each
136        // change separately can temporarily overclock the buses.
137        DIVIDER_UPDATE_DEFERRED.store(true, Ordering::Relaxed);
138        self.apply(clocks);
139        DIVIDER_UPDATE_DEFERRED.store(false, Ordering::Relaxed);
140
141        update_divider();
142        ets_update_cpu_frequency_rom(Rate::from_hz(cpu_clk_frequency()).as_mhz());
143    }
144}
145
146// CPLL_CLK
147
148fn enable_cpll_clk_impl(_clocks: &mut ClockTree, _en: bool) {
149    // PLLs are always on, for now
150}
151
152fn configure_cpll_clk_impl(
153    _clocks: &mut ClockTree,
154    _old_config: Option<CpllClkConfig>,
155    new_config: CpllClkConfig,
156) {
157    // Calibration starts before the analog configuration is written, and stops
158    // after the PLL reports that calibration ended.
159    // Ref: IDF rtc_clk_cpll_configure (esp32p4).
160    HP_SYS_CLKRST::regs()
161        .ana_pll_ctrl0()
162        .modify(|_, w| w.cpu_pll_cal_stop().clear_bit());
163
164    // div7_0 = (freq_mhz / 40) - 1
165    let div7_0: u8 = match new_config {
166        CpllClkConfig::_360 => 9,
167        CpllClkConfig::_400 => 10,
168    };
169
170    let lref: u8 = 0x50; // dchgp=5, div_ref=0, oc_enb_fcal=0
171    let dcur: u8 = 0x73; // dlref_sel=1, dhref_sel=3, dcur=3
172
173    regi2c::I2C_CPLL_OC_REF_DIV.write_reg(lref);
174    regi2c::I2C_CPLL_OC_DIV_7_0.write_reg(div7_0);
175    regi2c::I2C_CPLL_OC_DCUR.write_reg(dcur);
176
177    // Wait for calibration to complete
178    while !HP_SYS_CLKRST::regs()
179        .ana_pll_ctrl0()
180        .read()
181        .cpu_pll_cal_end()
182        .bit_is_set()
183    {
184        core::hint::spin_loop();
185    }
186
187    // Wait for true stop
188    crate::rom::ets_delay_us(10);
189
190    HP_SYS_CLKRST::regs()
191        .ana_pll_ctrl0()
192        .modify(|_, w| w.cpu_pll_cal_stop().set_bit());
193}
194
195// SPLL_CLK
196
197fn enable_spll_clk_impl(_clocks: &mut ClockTree, _en: bool) {
198    // PLLs are always on, for now
199}
200
201// MPLL_CLK
202
203fn enable_mpll_clk_impl(clocks: &mut ClockTree, en: bool) {
204    PMU::regs().rf_pwc().modify(|_, w| w.mspi_phy_xpd().bit(en));
205    LP_AON_CLKRST::regs()
206        .lp_aonclkrst_hp_clk_ctrl()
207        .modify(|_, w| w.lp_aonclkrst_hp_mpll_500m_clk_en().bit(en));
208
209    if !en {
210        return;
211    }
212
213    // Calibration starts before the analog configuration is written, and stops
214    // after the PLL reports that calibration ended.
215    // Ref: IDF clk_ll_mpll_set_config_v1 (esp32p4).
216    HP_SYS_CLKRST::regs()
217        .ana_pll_ctrl0()
218        .modify(|_, w| w.mspi_cal_stop().clear_bit());
219
220    regi2c::I2C_MPLL_DHREF_DHREF.write_field(3);
221
222    let rstb = regi2c::I2C_MPLL_IR_CAL_RSTB.read();
223    regi2c::I2C_MPLL_IR_CAL_RSTB.write_reg(rstb & 0xDF);
224    regi2c::I2C_MPLL_IR_CAL_RSTB.write_reg(rstb | (1 << 5));
225
226    // div = freq_mhz/20 - 1, ref_div = 1 -> MPLL = XTAL(40) * (div+1) / (ref_div+1)
227    let ref_div: u8 = 1;
228    let div: u8 = match unwrap!(clocks.mpll_clk) {
229        MpllClkConfig::_320 => 15,
230        MpllClkConfig::_400 => 19,
231        MpllClkConfig::_500 => 24,
232    };
233    let div_val: u8 = (div << 3) | ref_div;
234    regi2c::I2C_MPLL_DIV_REG_ADDR.write_reg(div_val);
235
236    while HP_SYS_CLKRST::regs()
237        .ana_pll_ctrl0()
238        .read()
239        .mspi_cal_end()
240        .bit_is_clear()
241    {
242        core::hint::spin_loop();
243    }
244    HP_SYS_CLKRST::regs()
245        .ana_pll_ctrl0()
246        .modify(|_, w| w.mspi_cal_stop().set_bit());
247
248    crate::rom::ets_delay_us(10);
249}
250
251fn configure_mpll_clk_impl(
252    _clocks: &mut ClockTree,
253    _old_config: Option<MpllClkConfig>,
254    _new_config: MpllClkConfig,
255) {
256    // Configuration applied in enable_mpll_clk_impl
257}
258
259// CPU_ROOT_CLK (mux: XTAL / CPLL / RC_FAST)
260fn configure_cpu_root_clk_impl(
261    _clocks: &mut ClockTree,
262    _old_selector: Option<CpuRootClkConfig>,
263    new_selector: CpuRootClkConfig,
264) {
265    //      LP_AON_CLKRST.hp_clk_ctrl.hp_root_clk_src_sel: 0=XTAL, 1=CPLL, 2=RC_FAST
266    let sel = match new_selector {
267        CpuRootClkConfig::Xtal => 0,
268        CpuRootClkConfig::Cpll => 1,
269        CpuRootClkConfig::RcFast => 2,
270    };
271    LP_AON_CLKRST::regs()
272        .lp_aonclkrst_hp_clk_ctrl()
273        .modify(|_, w| unsafe { w.lp_aonclkrst_hp_root_clk_src_sel().bits(sel) });
274}
275
276// CPU_CLK divider
277fn enable_cpu_clk_impl(_clocks: &mut ClockTree, _en: bool) {}
278fn configure_cpu_clk_impl(
279    _clocks: &mut ClockTree,
280    _old_config: Option<CpuClkConfig>,
281    new_config: CpuClkConfig,
282) {
283    HP_SYS_CLKRST::regs()
284        .root_clk_ctrl0()
285        .modify(|_, w| unsafe {
286            w.cpu_clk_div_num().bits(new_config.divisor as u8);
287            w.cpu_clk_div_numerator().bits(0);
288            w.cpu_clk_div_denominator().bits(0)
289        });
290
291    // Trigger divider update
292    update_divider();
293
294    if !DIVIDER_UPDATE_DEFERRED.load(Ordering::Relaxed) {
295        ets_update_cpu_frequency_rom(Rate::from_hz(cpu_clk_frequency()).as_mhz());
296    }
297}
298
299static DIVIDER_UPDATE_DEFERRED: AtomicBool = AtomicBool::new(false);
300
301fn update_divider() {
302    if DIVIDER_UPDATE_DEFERRED.load(Ordering::Relaxed) {
303        return;
304    }
305
306    HP_SYS_CLKRST::regs()
307        .root_clk_ctrl0()
308        .modify(|_, w| w.soc_clk_div_update().set_bit());
309    while HP_SYS_CLKRST::regs()
310        .root_clk_ctrl0()
311        .read()
312        .soc_clk_div_update()
313        .bit_is_set()
314    {
315        core::hint::spin_loop();
316    }
317}
318
319// MEM_CLK
320
321fn enable_mem_clk_impl(_clocks: &mut ClockTree, _en: bool) {
322    // Nothing to do.
323}
324
325fn configure_mem_clk_impl(
326    _clocks: &mut ClockTree,
327    _old_config: Option<MemClkConfig>,
328    new_config: MemClkConfig,
329) {
330    HP_SYS_CLKRST::regs()
331        .root_clk_ctrl1()
332        .modify(|_, w| unsafe {
333            w.mem_clk_div_num().bits(new_config.divisor as u8);
334            w.mem_clk_div_numerator().bits(0);
335            w.mem_clk_div_denominator().bits(0)
336        });
337
338    // Trigger divider update
339    update_divider();
340}
341
342// SYS_CLK
343
344fn enable_sys_clk_impl(_clocks: &mut ClockTree, _en: bool) {
345    // Nothing to do.
346}
347
348fn configure_sys_clk_impl(
349    _clocks: &mut ClockTree,
350    _old_config: Option<SysClkConfig>,
351    new_config: SysClkConfig,
352) {
353    HP_SYS_CLKRST::regs()
354        .root_clk_ctrl1()
355        .modify(|_, w| unsafe { w.sys_clk_div_num().bits(new_config.divisor as u8) });
356    HP_SYS_CLKRST::regs()
357        .root_clk_ctrl2()
358        .modify(|_, w| unsafe {
359            w.sys_clk_div_numerator().bits(0);
360            w.sys_clk_div_denominator().bits(0)
361        });
362
363    // Trigger divider update
364    update_divider();
365}
366
367// APB_CLK
368
369fn enable_apb_clk_impl(_clocks: &mut ClockTree, _en: bool) {
370    // Nothing to do.
371}
372
373fn configure_apb_clk_impl(
374    _clocks: &mut ClockTree,
375    _old_config: Option<ApbClkConfig>,
376    new_config: ApbClkConfig,
377) {
378    HP_SYS_CLKRST::regs()
379        .root_clk_ctrl2()
380        .modify(|_, w| unsafe {
381            w.apb_clk_div_num().bits(new_config.divisor as u8);
382            w.apb_clk_div_numerator().bits(0)
383        });
384    HP_SYS_CLKRST::regs()
385        .root_clk_ctrl3()
386        .modify(|_, w| unsafe { w.apb_clk_div_denominator().bits(0) });
387
388    // Trigger divider update
389    update_divider();
390}
391
392// IOMUX_FUNCTION_CLOCK
393
394fn configure_iomux_function_clock_impl(
395    _clocks: &mut ClockTree,
396    _old_config: Option<IomuxFunctionClockConfig>,
397    new_config: IomuxFunctionClockConfig,
398) {
399    HP_SYS_CLKRST::regs()
400        .peri_clk_ctrl26()
401        .modify(|_, w| unsafe {
402            w.iomux_clk_src_sel().bit(matches!(
403                new_config.source,
404                IomuxFunctionClockSource::PllF80m
405            ));
406            w.iomux_clk_div_num().bits(new_config.div_num as u8)
407        });
408}
409
410// LP_FAST_CLK mux
411fn configure_lp_fast_clk_impl(
412    _clocks: &mut ClockTree,
413    _old_selector: Option<LpFastClkConfig>,
414    new_selector: LpFastClkConfig,
415) {
416    LP_AON_CLKRST::regs()
417        .lp_aonclkrst_lp_clk_conf()
418        .modify(|_, w| unsafe {
419            w.lp_aonclkrst_fast_clk_sel().bits(match new_selector {
420                LpFastClkConfig::RcFast => 0,
421                LpFastClkConfig::XtalD2 => 1,
422            })
423        });
424}
425
426// LP_SLOW_CLK mux
427fn configure_lp_slow_clk_impl(
428    _clocks: &mut ClockTree,
429    _old_selector: Option<LpSlowClkConfig>,
430    new_selector: LpSlowClkConfig,
431) {
432    LP_AON_CLKRST::regs()
433        .lp_aonclkrst_lp_clk_conf()
434        .modify(|_, w| unsafe {
435            w.lp_aonclkrst_slow_clk_sel().bits(match new_selector {
436                LpSlowClkConfig::RcSlow => 0,
437                LpSlowClkConfig::Xtal32k => 1,
438                // LpSlowClkConfig::Rc32k => 2,
439                LpSlowClkConfig::OscSlow => 3,
440            })
441        });
442}
443
444// Per-instance clock impl for TIMG
445
446impl SdmInstance {
447    // SDM_FUNCTION_CLOCK
448
449    fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) {
450        crate::peripherals::GPIO_SD::regs()
451            .sigmadelta_misc()
452            .modify(|_, w| w.function_clk_en().bit(en));
453    }
454}
455impl TimgInstance {
456    fn enable_function_clock_impl(self, _clocks: &mut ClockTree, _en: bool) {
457        // TIMG function clock is managed by peripheral clock gates
458    }
459
460    fn configure_function_clock_impl(
461        self,
462        _clocks: &mut ClockTree,
463        _old_config: Option<TimgFunctionClockConfig>,
464        _new_config: TimgFunctionClockConfig,
465    ) {
466        // TODO: Configure TIMG clock source
467        // HP_SYS_CLKRST PERI_CLK_CTRL20/21
468    }
469
470    fn enable_wdt_clock_impl(self, _clocks: &mut ClockTree, _en: bool) {}
471
472    fn configure_wdt_clock_impl(
473        self,
474        _clocks: &mut ClockTree,
475        _old_config: Option<TimgWdtClockConfig>,
476        _new_config: TimgWdtClockConfig,
477    ) {
478        // TODO: Configure TIMG WDT clock source
479    }
480}
481
482impl RmtInstance {
483    // RMT_SCLK
484
485    fn enable_sclk_impl(self, _clocks: &mut ClockTree, en: bool) {
486        HP_SYS_CLKRST::regs()
487            .peri_clk_ctrl22()
488            .modify(|_, w| w.rmt_clk_en().bit(en));
489    }
490
491    fn configure_sclk_impl(
492        self,
493        _clocks: &mut ClockTree,
494        _old_config: Option<RmtSclkConfig>,
495        new_config: RmtSclkConfig,
496    ) {
497        // Register values: 0 = XTAL, 1 = RC_FAST, 2 = PLL_F80M.
498        HP_SYS_CLKRST::regs()
499            .peri_clk_ctrl22()
500            .modify(|_, w| unsafe {
501                w.rmt_clk_src_sel().bits(match new_config {
502                    RmtSclkConfig::XtalClk => 0,
503                    RmtSclkConfig::RcFastClk => 1,
504                    RmtSclkConfig::PllF80m => 2,
505                })
506            });
507    }
508}
509
510// System clock impl functions
511
512// Mux enable stubs (mux nodes need enable functions too)
513fn enable_cpu_root_clk_impl(_clocks: &mut ClockTree, _en: bool) {}
514fn enable_lp_fast_clk_impl(_clocks: &mut ClockTree, _en: bool) {}
515fn enable_lp_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) {}
516
517// Source clock enable/disable stubs (PLLs, oscillators)
518fn enable_rc_fast_clk_impl(_clocks: &mut ClockTree, _en: bool) {}
519fn enable_xtal32k_clk_impl(_clocks: &mut ClockTree, _en: bool) {}
520fn enable_osc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) {}
521fn enable_rc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) {}
522fn enable_rc32k_clk_impl(_clocks: &mut ClockTree, _en: bool) {}
523fn enable_pll_f20m_impl(_clocks: &mut ClockTree, _en: bool) {}
524fn enable_pll_f80m_impl(_clocks: &mut ClockTree, _en: bool) {}
525fn enable_pll_f120m_impl(_clocks: &mut ClockTree, _en: bool) {}
526fn enable_pll_f160m_impl(_clocks: &mut ClockTree, _en: bool) {}
527fn enable_pll_f240m_impl(_clocks: &mut ClockTree, _en: bool) {}
528fn enable_pll_f25m_impl(_clocks: &mut ClockTree, _en: bool) {}
529fn enable_pll_f50m_impl(_clocks: &mut ClockTree, _en: bool) {}
530fn enable_xtal_d2_clk_impl(_clocks: &mut ClockTree, _en: bool) {}
531
532// TIMG_CALIBRATION_CLOCK
533
534fn enable_timg_calibration_clock_impl(_clocks: &mut ClockTree, _en: bool) {
535    // Nothing to do here.
536}
537
538impl MipiDsiInstance {
539    // MIPI_DSI_DPI_CLK
540    // HP_SYS_CLKRST.peri_clk_ctrl03: mipi_dsi_dpiclk_{src_sel,div_num,en}
541
542    fn enable_dpi_clk_impl(self, _clocks: &mut ClockTree, en: bool) {
543        HP_SYS_CLKRST::regs()
544            .peri_clk_ctrl03()
545            .modify(|_, w| w.mipi_dsi_dpiclk_en().bit(en));
546    }
547
548    fn configure_dpi_clk_impl(
549        self,
550        _clocks: &mut ClockTree,
551        _old_config: Option<MipiDsiDpiClkConfig>,
552        new_config: MipiDsiDpiClkConfig,
553    ) {
554        // Register values: 0 = XTAL, 1 = PLL_F240M, 2 = PLL_F160M.
555        let src_sel = match new_config.sclk() {
556            MipiDsiDpiClkSclk::Xtal => 0,
557            MipiDsiDpiClkSclk::PllF240m => 1,
558            MipiDsiDpiClkSclk::PllF160m => 2,
559        };
560        HP_SYS_CLKRST::regs()
561            .peri_clk_ctrl03()
562            .modify(|_, w| unsafe {
563                w.mipi_dsi_dpiclk_src_sel().bits(src_sel);
564                w.mipi_dsi_dpiclk_div_num().bits(new_config.div_num() as u8)
565            });
566    }
567
568    // MIPI_DSI_PHY_PLL_REFCLK
569    // HP_SYS_CLKRST.peri_clk_ctrl03: mipi_dsi_dphy_pll_refclk_{src_sel,div_num,en}
570
571    fn enable_phy_pll_refclk_impl(self, _clocks: &mut ClockTree, en: bool) {
572        HP_SYS_CLKRST::regs()
573            .peri_clk_ctrl03()
574            .modify(|_, w| w.mipi_dsi_dphy_pll_refclk_en().bit(en));
575    }
576
577    fn configure_phy_pll_refclk_impl(
578        self,
579        _clocks: &mut ClockTree,
580        _old_config: Option<MipiDsiPhyPllRefclkConfig>,
581        new_config: MipiDsiPhyPllRefclkConfig,
582    ) {
583        // Hardware register: 0=XTAL, 1=APLL (not modelled), 2=CPLL, 3=SPLL, 4=MPLL.
584        let src_sel = match new_config.sclk() {
585            MipiDsiPhyPllRefclkSclk::Xtal => 0u8,
586            MipiDsiPhyPllRefclkSclk::Cpll => 2,
587            MipiDsiPhyPllRefclkSclk::Spll => 3,
588            MipiDsiPhyPllRefclkSclk::Mpll => 4,
589        };
590        HP_SYS_CLKRST::regs()
591            .peri_clk_ctrl03()
592            .modify(|_, w| unsafe {
593                w.mipi_dsi_dphy_pll_refclk_src_sel().bits(src_sel);
594                w.mipi_dsi_dphy_pll_refclk_div_num()
595                    .bits(new_config.div_num() as u8)
596            });
597    }
598
599    // MIPI_DSI_PHY_CFG_CLK
600    // HP_SYS_CLKRST.peri_clk_ctrl02: mipi_dsi_dphy_clk_src_sel
601    // HP_SYS_CLKRST.peri_clk_ctrl03: mipi_dsi_dphy_cfg_clk_en
602
603    fn enable_phy_cfg_clk_impl(self, _clocks: &mut ClockTree, en: bool) {
604        HP_SYS_CLKRST::regs()
605            .peri_clk_ctrl03()
606            .modify(|_, w| w.mipi_dsi_dphy_cfg_clk_en().bit(en));
607    }
608
609    fn configure_phy_cfg_clk_impl(
610        self,
611        _clocks: &mut ClockTree,
612        _old_config: Option<MipiDsiPhyCfgClkConfig>,
613        new_config: MipiDsiPhyCfgClkConfig,
614    ) {
615        // Register values: 0 = PLL_F20M, 1 = RC_FAST, 2 = PLL_F25M.
616        let src_sel = match new_config {
617            MipiDsiPhyCfgClkConfig::PllF20m => 0,
618            MipiDsiPhyCfgClkConfig::RcFast => 1,
619            MipiDsiPhyCfgClkConfig::PllF25m => 2,
620        };
621        HP_SYS_CLKRST::regs()
622            .peri_clk_ctrl02()
623            .modify(|_, w| unsafe { w.mipi_dsi_dphy_clk_src_sel().bits(src_sel) });
624    }
625}
626
627fn configure_timg_calibration_clock_impl(
628    _clocks: &mut ClockTree,
629    _old_config: Option<TimgCalibrationClockConfig>,
630    new_config: TimgCalibrationClockConfig,
631) {
632    HP_SYS_CLKRST::regs()
633        .peri_clk_ctrl21()
634        .modify(|_, w| unsafe {
635            w.timergrp0_tgrt_clk_src_sel().bits(match new_config {
636                TimgCalibrationClockConfig::MpllClk => 0,
637                TimgCalibrationClockConfig::SpllClk => 1,
638                TimgCalibrationClockConfig::CpllClk => 2,
639                TimgCalibrationClockConfig::RcFastDivClk => 7,
640                TimgCalibrationClockConfig::RcSlowClk => 8,
641                TimgCalibrationClockConfig::Xtal32kClk => 10,
642            })
643        });
644}
645
646impl PsramInstance {
647    // PSRAM_FUNCTION_CLOCK
648
649    fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) {
650        HP_SYS_CLKRST::regs().peri_clk_ctrl00().modify(|_, w| {
651            w.psram_pll_clk_en().bit(en);
652            w.psram_core_clk_en().bit(en)
653        });
654    }
655
656    fn configure_function_clock_impl(
657        self,
658        _clocks: &mut ClockTree,
659        _old_config: Option<PsramFunctionClockConfig>,
660        new_config: PsramFunctionClockConfig,
661    ) {
662        HP_SYS_CLKRST::regs()
663            .peri_clk_ctrl00()
664            .modify(|_, w| unsafe {
665                w.psram_clk_src_sel().bits(match new_config {
666                    PsramFunctionClockConfig::Xtal => 0,
667                    PsramFunctionClockConfig::Mpll => 1,
668                    PsramFunctionClockConfig::Spll => 2,
669                    PsramFunctionClockConfig::Cpll => 3,
670                })
671            });
672    }
673}