// stm32u5xx-hal/src/rcc.rs
//! Reset and Clock Control for STM32U575 (initial/minimal port)
//!
//! Scope: only features that clearly overlap between stm32l4xx-hal rcc.rs
//! and the STM32U5 HAL C (stm32u5xx_hal_rcc.c/.h).
//!
//! Skipped (present in U5 HAL C, not here yet):
//! - MSIK (kernel MSI), SHSI (secure HSI)
//! - PLL2 / PLL3
//! - PLL FRACN and MBOOST/EPOD handling
//! - LSE glitch filter, LSE CSS, LSESYS
//! - LSI divider (LSIPREDIV)
//! - STOP wake-up clock selection (STOPWUCK / STOPKERWUCK)
//! - Security/privileged attributes
//! - APB3 (PCLK3) prescaler & clocks
//! - MCO configuration
//! - Per-peripheral Enable/Reset tables (normally in rcc/enable.rs)

use crate::pac::{rcc, RCC};
use crate::flash::ACR;
use crate::pwr::Pwr;
use crate::time::Hertz;
use fugit::RateExtU32;

// Uncomment when you add bus-peripheral enable/reset mapping like in L4
// mod enable;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MsiFreq {
    // Order and names reflect STM32U5 MSIRANGE mapping (RCC_MSIRANGE_x):
    // 0..15 encode the specific frequencies per stm32u5xx_hal_rcc.h
    // We provide exact frequencies in Hz.
    MSI48,      // 48_000_000
    MSI24,      // 24_000_000
    MSI16,      // 16_000_000
    MSI12,      // 12_000_000
    MSI4,       // 4_000_000
    MSI2,       // 2_000_000
    MSI1_33,    // 1_330_000 (approx 1.33 MHz)
    MSI1,       // 1_000_000
    MSI3_072,   // 3_072_000
    MSI1_536,   // 1_536_000
    MSI1_024,   // 1_024_000
    MSI_768K,   // 768_000
    MSI400K,    // 400_000
    MSI200K,    // 200_000
    MSI133K,    // 133_000
    MSI100K,    // 100_000
}

impl MsiFreq {
    fn to_hertz(self) -> Hertz {
        let hz = match self {
            Self::MSI48 => 48_000_000,
            Self::MSI24 => 24_000_000,
            Self::MSI16 => 16_000_000,
            Self::MSI12 => 12_000_000,
            Self::MSI4 => 4_000_000,
            Self::MSI2 => 2_000_000,
            Self::MSI1_33 => 1_330_000,
            Self::MSI1 => 1_000_000,
            Self::MSI3_072 => 3_072_000,
            Self::MSI1_536 => 1_536_000,
            Self::MSI1_024 => 1_024_000,
            Self::MSI_768K => 768_000,
            Self::MSI400K => 400_000,
            Self::MSI200K => 200_000,
            Self::MSI133K => 133_000,
            Self::MSI100K => 100_000,
        };
        hz.Hz()
    }

    // Map to ICSCR1.MSISRANGE field encoding (0..15)
    fn to_range_bits(self) -> u8 {
        match self {
            Self::MSI48 => 0,
            Self::MSI24 => 1,
            Self::MSI16 => 2,
            Self::MSI12 => 3,
            Self::MSI4 => 4,
            Self::MSI2 => 5,
            Self::MSI1_33 => 6,
            Self::MSI1 => 7,
            Self::MSI3_072 => 8,
            Self::MSI1_536 => 9,
            Self::MSI1_024 => 10,
            Self::MSI_768K => 11,
            Self::MSI400K => 12,
            Self::MSI200K => 13,
            Self::MSI133K => 14,
            Self::MSI100K => 15,
        }
    }
}

/// Extension trait that constrains the `RCC` peripheral
pub trait RccExt {
    /// Constrains the `RCC` peripheral so it plays nicely with the other abstractions
    fn constrain(self) -> Rcc;
}

impl RccExt for RCC {
    fn constrain(self) -> Rcc {
        Rcc {
            ahb1: AHB1::new(),
            ahb2_1: AHB2_1::new(),
            ahb2_2: AHB2_2::new(),
            ahb3: AHB3::new(),
            apb1r1: APB1R1::new(),
            apb1r2: APB1R2::new(),
            apb2: APB2::new(),
            apb3: APB3::new(),
            cfgr: CFGR {
                hse: None,
                lse: None,
                msi: None,
                hsi48: false,
                lsi: false,
                hclk: None,
                pclk1: None,
                pclk2: None,
                sysclk: None,
                pll_source: None,
                pll_config: None,
            },
        }
    }
}

/// Constrained RCC peripheral
pub struct Rcc {
    pub ahb1: AHB1,
    pub ahb2_1: AHB2_1,
    pub ahb2_2: AHB2_2,
    pub ahb3: AHB3,
    pub apb1r1: APB1R1,
    pub apb1r2: APB1R2,
    pub apb2: APB2,
    pub apb3: APB3,
    pub cfgr: CFGR,
}

/// Backup domain control register proxy
pub struct BDCR {
    _0: (),
}

impl BDCR {
    #[allow(dead_code)]
    pub(crate) fn bdcr(&mut self) -> &rcc::BDCR {
        unsafe { &(*RCC::ptr()).bdcr }
    }
}

/// Control/Status Register proxy
pub struct CSR {
    _0: (),
}

impl CSR {
    #[allow(dead_code)]
    pub(crate) fn csr(&mut self) -> &rcc::CSR {
        unsafe { &(*RCC::ptr()).csr }
    }
}

macro_rules! bus_struct_1 {
    ($busX:ident => ($EN:ident, $en:ident, $SMEN:ident, $smen:ident, $RST:ident, $rst:ident, $doc:literal),) => {
        #[doc = $doc]
        pub struct $busX {
            _0: (),
        }
        impl $busX {
            pub(crate) fn new() -> Self {
                Self { _0: () }
            }
            #[allow(unused)]
            pub(crate) fn enr(&self) -> &rcc::$EN {
                unsafe { &(*RCC::ptr()).$en }
            }
            #[allow(unused)]
            pub(crate) fn smenr(&self) -> &rcc::$SMEN {
                unsafe { &(*RCC::ptr()).$smen }
            }
            #[allow(unused)]
            pub(crate) fn rstr(&self) -> &rcc::$RST {
                unsafe { &(*RCC::ptr()).$rst }
            }
        }
    };
}

// U5 splits AHB2/APB1 into *1/*2 register banks; we model both.
bus_struct_1! {
    AHB1 => (AHB1ENR, ahb1enr, AHB1SMENR, ahb1smenr, AHB1RSTR, ahb1rstr, "AHB1 registers"),
}
bus_struct_1! {
    AHB2_1 => (AHB2ENR1, ahb2enr1, AHB2SMENR1, ahb2smenr1, AHB2RSTR1, ahb2rstr1, "AHB2 part 1 registers"),
}
bus_struct_1! {
    AHB2_2 => (AHB2ENR2, ahb2enr2, AHB2SMENR2, ahb2smenr2, AHB2RSTR2, ahb2rstr2, "AHB2 part 2 registers"),
}
bus_struct_1! {
    AHB3 => (AHB3ENR, ahb3enr, AHB3SMENR, ahb3smenr, AHB3RSTR, ahb3rstr, "AHB3 registers"),
}
bus_struct_1! {
    APB1R1 => (APB1ENR1, apb1enr1, APB1SMENR1, apb1smenr1, APB1RSTR1, apb1rstr1, "APB1 register set 1"),
}
bus_struct_1! {
    APB1R2 => (APB1ENR2, apb1enr2, APB1SMENR2, apb1smenr2, APB1RSTR2, apb1rstr2, "APB1 register set 2"),
}
bus_struct_1! {
    APB2 => (APB2ENR, apb2enr, APB2SMENR, apb2smenr, APB2RSTR, apb2rstr, "APB2 registers"),
}
bus_struct_1! {
    APB3 => (APB3ENR, apb3enr, APB3SMENR, apb3smenr, APB3RSTR, apb3rstr, "APB3 registers"),
}

/// Bus associated to peripheral
pub trait RccBus: crate::Sealed {
    type Bus;
}

/// Enable/disable peripheral
pub trait Enable: RccBus {
    fn enable(bus: &mut Self::Bus);
    fn disable(bus: &mut Self::Bus);
    fn is_enabled() -> bool;
    fn is_disabled() -> bool;
    unsafe fn enable_unchecked();
    unsafe fn disable_unchecked();
}

/// Enable/disable peripheral in sleep mode
pub trait SMEnable: RccBus {
    fn enable_in_sleep_mode(bus: &mut Self::Bus);
    fn disable_in_sleep_mode(bus: &mut Self::Bus);
    fn is_enabled_in_sleep_mode() -> bool;
    fn is_disabled_in_sleep_mode() -> bool;
    unsafe fn enable_in_sleep_mode_unchecked();
    unsafe fn disable_in_sleep_mode_unchecked();
}

/// Reset peripheral
pub trait Reset: RccBus {
    fn reset(bus: &mut Self::Bus);
    unsafe fn reset_unchecked();
}

#[derive(Debug, PartialEq)]
struct HseConfig {
    speed: u32,
    bypass: CrystalBypass,
    css: ClockSecuritySystem,
}

#[derive(Debug, PartialEq)]
struct LseConfig {
    bypass: CrystalBypass,
    // Skipped: LSE CSS / glitch filter / LSESYS
    css: ClockSecuritySystem, // kept in struct for future, not applied
}

/// Crystal bypass selector
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum CrystalBypass {
    Enable,
    Disable,
}

/// Clock Security System
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ClockSecuritySystem {
    Enable,
    Disable,
}

const HSI: u32 = 16_000_000; // Hz (HSI16)

/// PLL Source (PLL1)
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PllSource {
    MSI,
    HSI16,
    HSE,
}

impl PllSource {
    // Map to RCC_PLL1CFGR PLL1SRC field bits (00: none, 01: MSI, 10: HSI, 11: HSE)
    fn to_pllsrc_bits(self) -> u8 {
        match self {
            Self::MSI => 0b01,
            Self::HSI16 => 0b10,
            Self::HSE => 0b11,
        }
    }
}

#[derive(Clone, Copy, Debug)]
pub struct PllConfig {
    // Main PLL division factor M (1..16) -> register stores (M-1)
    m: u8,
    // Main PLL multiplication factor N (4..512) -> register stores (N-1)
    n: u16,
    // Main PLL division factor for PLLR (system clock) (1..128) -> reg stores (R-1)
    r: u8,
    // Skipped: P, Q outputs, MBOOST, FRACN, RGE beyond minimal
}

impl PllConfig {
    pub fn new(m: u8, n: u16, r: u8) -> Self {
        assert!(m >= 1 && m <= 16);
        assert!(n >= 4 && n <= 512);
        assert!(r >= 1 && r <= 128);
        Self { m, n, r }
    }
}

/// Clock configuration builder
pub struct CFGR {
    hse: Option<HseConfig>,
    lse: Option<LseConfig>,
    msi: Option<MsiFreq>,
    hsi48: bool,
    lsi: bool,
    hclk: Option<u32>,
    pclk1: Option<u32>,
    pclk2: Option<u32>,
    sysclk: Option<u32>,
    pll_source: Option<PllSource>,
    pll_config: Option<PllConfig>,
}

impl CFGR {
    pub fn hse(mut self, freq: Hertz, bypass: CrystalBypass, css: ClockSecuritySystem) -> Self {
        self.hse = Some(HseConfig {
            speed: freq.raw(),
            bypass,
            css,
        });
        self
    }

    pub fn lse(mut self, bypass: CrystalBypass, css: ClockSecuritySystem) -> Self {
        self.lse = Some(LseConfig { bypass, css });
        self
    }

    pub fn msi(mut self, range: MsiFreq) -> Self {
        self.msi = Some(range);
        self
    }

    pub fn lsi(mut self, on: bool) -> Self {
        self.lsi = on;
        self
    }

    pub fn hsi48(mut self, on: bool) -> Self {
        self.hsi48 = on;
        self
    }

    pub fn hclk(mut self, freq: Hertz) -> Self {
        self.hclk = Some(freq.raw());
        self
    }

    pub fn pclk1(mut self, freq: Hertz) -> Self {
        self.pclk1 = Some(freq.raw());
        self
    }

    pub fn pclk2(mut self, freq: Hertz) -> Self {
        self.pclk2 = Some(freq.raw());
        self
    }

    pub fn sysclk(mut self, freq: Hertz) -> Self {
        self.sysclk = Some(freq.raw());
        self
    }

    pub fn sysclk_with_pll(mut self, freq: Hertz, cfg: PllConfig) -> Self {
        self.sysclk = Some(freq.raw());
        self.pll_config = Some(cfg);
        self
    }

    pub fn pll_source(mut self, source: PllSource) -> Self {
        self.pll_source = Some(source);
        self
    }

    pub fn freeze(&self, acr: &mut ACR, pwr: &mut Pwr) -> Clocks {
        let rcc = unsafe { &*RCC::ptr() };

        // Ensure MSI is ON and selected first (safe base)
        if rcc.cr.read().msison().bit_is_clear() {
            rcc.cr.modify(|_, w| w.msison().set_bit());
            while rcc.cr.read().msisrdy().bit_is_clear() {}
        }

        // Program MSI to 4 MHz as default base (MSIRGSEL=1, MSISRANGE=4MHz)
        unsafe {
            rcc.icscr1.modify(|_, w| {
                w.msirgsel().set_bit();
                w.msisrange().bits(MsiFreq::MSI4.to_range_bits());
                w
            });
        }

        // If SYSCLK not MSI, force switch to MSI (SW=00) and wait
        if rcc.cfgr1.read().sws().bits() != 0 {
            // Reset CFGR1.SW to MSI
            rcc.cfgr1.modify(|_, w| unsafe { w.sw().bits(0b00) });
            while rcc.cfgr1.read().sws().bits() != 0b00 {}
        }

        //
        // 1) Oscillators
        //

        // LSI if requested (or if we needed by LSE CSS in future)
        let lsi_used = if self.lsi {
            rcc.bdcr.modify(|_, w| w.lsion().set_bit());
            while rcc.bdcr.read().lsirdy().bit_is_clear() {}
            true
        } else {
            false
        };

        // LSE if requested
        if let Some(lse_cfg) = &self.lse {
            // Enable backup domain write access
            // NOTE: depends on your Pwr wrapper; mirrors L4 HAL style
            pwr.cr1.reg().modify(|_, w| w.dbp().set_bit());
            // Enable LSE (bypass if set)
            if lse_cfg.bypass == CrystalBypass::Enable {
                rcc.bdcr.modify(|_, w| w.lsebyp().set_bit());
            }
            rcc.bdcr.modify(|_, w| w.lseon().set_bit());
            while rcc.bdcr.read().lserdy().bit_is_clear() {}
            // Skipped: LSESYS and LSE CSS/glitch filter
        }

        // HSE if requested
        if let Some(hse_cfg) = &self.hse {
            rcc.cr.modify(|_, w| {
                if hse_cfg.bypass == CrystalBypass::Enable {
                    w.hsebyp().set_bit();
                }
                w.hseon().set_bit()
            });
            while rcc.cr.read().hserdy().bit_is_clear() {}

            if hse_cfg.css == ClockSecuritySystem::Enable {
                // Enable CSS on HSE (CR.CSSON)
                rcc.cr.modify(|_, w| w.csson().set_bit());
            }
        }

        // MSI range if set explicitly
        if let Some(msi) = self.msi {
            unsafe {
                rcc.icscr1.modify(|_, w| {
                    w.msirgsel().set_bit();
                    w.msisrange().bits(msi.to_range_bits());
                    w
                })
            };
            while rcc.cr.read().msisrdy().bit_is_clear() {}
        }

        // HSI48
        if self.hsi48 {
            rcc.cr.modify(|_, w| w.hsi48on().set_bit());
            while rcc.cr.read().hsi48rdy().bit_is_clear() {}
        }

        //
        // 2) PLL1 setup (optional)
        //
        // Decide PLL1 source
        let (src_freq, pll_src) = if let Some(src) = self.pll_source {
            match src {
                PllSource::HSE => {
                    let hse = self
                        .hse
                        .as_ref()
                        .expect("HSE selected as PLL source, but not enabled");
                    (hse.speed, src)
                }
                PllSource::HSI16 => (HSI, src),
                PllSource::MSI => {
                    let msi = self.msi.expect("MSI selected as PLL source, but not enabled");
                    (msi.to_hertz().raw(), src)
                }
            }
        } else {
            if let Some(hse) = &self.hse {
                (hse.speed, PllSource::HSE)
            } else if let Some(msi) = self.msi {
                (msi.to_hertz().raw(), PllSource::MSI)
            } else {
                (HSI, PllSource::HSI16)
            }
        };

        // If we need HSI for PLL1 and it's not on, turn it on
        if pll_src == PllSource::HSI16 {
            rcc.cr.modify(|_, w| w.hsion().set_bit());
            while rcc.cr.read().hsirdy().bit_is_clear() {}
        }

        let want_sysclk = match (self.sysclk, self.msi) {
            (Some(sysclk), _) => sysclk,
            (None, Some(msi)) => msi.to_hertz().raw(),
            (None, None) => MsiFreq::MSI4.to_hertz().raw(),
        };

        assert!(want_sysclk <= 80_000_000);

        // Auto PLL config if not provided (simple best-effort: M=1, R=2)
        let pllconf = if self.pll_config.is_none() {
            if self.sysclk.is_some() {
                let m = 1u8;
                let r = 2u8;
                // want_sysclk = (src/m)*N / r => N = want_sysclk * r / (src/m)
                let n = ((want_sysclk as u64) * (r as u64) / (src_freq as u64)) as u16;
                Some(PllConfig::new(m, n, r))
            } else {
                None
            }
        } else {
            self.pll_config
        };

        // Compute prescalers from requested hclk/pclk1/pclk2
        // HPRE (CFGR2.HPRE), PPRE1/PPRE2 (CFGR2.PPRE1/PPRE2)
        let (hpre_bits, hpre_div) = self
            .hclk
            .map(|hclk| match want_sysclk / hclk {
                0 => unreachable!(),
                1 => (0b0000, 1),
                2 => (0b1000, 2),
                3..=5 => (0b1001, 4),
                6..=11 => (0b1010, 8),
                12..=39 => (0b1011, 16),
                40..=95 => (0b1100, 64),
                96..=191 => (0b1101, 128),
                192..=383 => (0b1110, 256),
                _ => (0b1111, 512),
            })
            .unwrap_or((0b0000, 1));
        let hclk = want_sysclk / hpre_div;

        let (ppre1_bits, ppre1_div) = self
            .pclk1
            .map(|pclk1| match hclk / pclk1 {
                0 => unreachable!(),
                1 => (0b000, 1),
                2 => (0b100, 2),
                3..=5 => (0b101, 4),
                6..=11 => (0b110, 8),
                _ => (0b111, 16),
            })
            .unwrap_or((0b000, 1));
        let pclk1 = hclk / (ppre1_div as u32);

        let (ppre2_bits, ppre2_div) = self
            .pclk2
            .map(|pclk2| match hclk / pclk2 {
                0 => unreachable!(),
                1 => (0b000, 1),
                2 => (0b100, 2),
                3..=5 => (0b101, 4),
                6..=11 => (0b110, 8),
                _ => (0b111, 16),
            })
            .unwrap_or((0b000, 1));
        let pclk2 = hclk / (ppre2_div as u32);

        // Timer clocks double if APB prescaler != 1
        let timclk1 = if ppre1_div == 1 { pclk1 } else { 2 * pclk1 };
        let timclk2 = if ppre2_div == 1 { pclk2 } else { 2 * pclk2 };

        // Adjust Flash latency (simple L4-like thresholds for <=80 MHz)
        unsafe {
            acr.acr().write(|w| {
                w.latency().bits(if hclk <= 16_000_000 {
                    0b000
                } else if hclk <= 32_000_000 {
                    0b001
                } else if hclk <= 48_000_000 {
                    0b010
                } else if hclk <= 64_000_000 {
                    0b011
                } else {
                    0b100
                })
            })
        }

        // Program prescalers first (CFGR2: HPRE/PPRE1/PPRE2)
        rcc.cfgr2.modify(|_, w| unsafe {
            w.hpre().bits(hpre_bits);
            w.ppre1().bits(ppre1_bits);
            w.ppre2().bits(ppre2_bits)
        });

        let mut used_msi = self.msi;
        let sysclk_src_bits;

        if let Some(pll) = pllconf {
            // Basic checks for VCO ranges (very simplified; FRACN/MBOOST ignored)
            let vco_in = (src_freq as u32) / (pll.m as u32);
            assert!(vco_in >= 1_000_000 && vco_in <= 16_000_000);
            let vco = (vco_in as u64) * (pll.n as u64);
            assert!(vco >= 128_000_000 && vco <= 544_000_000);
            let sysclk_calc = (vco / (pll.r as u64)) as u32;
            assert!(sysclk_calc <= 80_000_000);

            // Disable PLL1 before reconfig
            rcc.cr.modify(|_, w| w.pll1on().clear_bit());
            while rcc.cr.read().pll1rdy().bit_is_set() {}

            // Set PLL source
            let src_bits = pll_src.to_pllsrc_bits();
            rcc.pll1cfgr.modify(|_, w| unsafe {
                w.pll1src().bits(src_bits);
                // VCI range selection (very rough): 4..8 => range0, 8..16 => range1
                let rge = if vco_in < 8_000_000 { 0b00 } else { 0b11 };
                w.pll1rge().bits(rge);
                // M
                w.pll1m().bits(pll.m - 1);
                // Enable only R output
                w.pll1ren().clear_bit(); // clear first; will set after lock
                w
            });

            // Program N, R in PLL1DIVR
            rcc.pll1divr.modify(|_, w| unsafe {
                w.pll1n().bits((pll.n - 1) as u16);
                w.pll1r().bits(pll.r - 1);
                // Leave P/Q untouched/disabled (skipped)
                w
            });

            // Enable PLL1
            rcc.cr.modify(|_, w| w.pll1on().set_bit());
            while rcc.cr.read().pll1rdy().bit_is_clear() {}

            // Enable PLL1 R output
            rcc.pll1cfgr.modify(|_, w| w.pll1ren().set_bit());

            // Switch SYSCLK to PLL1 (CFGR1.SW = 11)
            sysclk_src_bits = 0b11;
            rcc.cfgr1.modify(|_, w| unsafe { w.sw().bits(sysclk_src_bits) });
        } else {
            // Keep MSI as SYSCLK (CFGR1.SW = 00)
            sysclk_src_bits = 0b00;
            if used_msi.is_none() {
                used_msi = Some(MsiFreq::MSI4);
            }
            rcc.cfgr1.modify(|_, w| unsafe { w.sw().bits(sysclk_src_bits) });
        }

        // Wait for SWS
        while rcc.cfgr1.read().sws().bits() != sysclk_src_bits {}

        // If we ended up on PLL, and MSI wasn't requested for other purposes, we can switch MSI off
        if used_msi.is_none() && sysclk_src_bits == 0b11 {
            rcc.cr.modify(|_, w| w.msison().clear_bit());
        }

        Clocks {
            hclk: hclk.Hz(),
            lsi: lsi_used,
            lse: self.lse.is_some(),
            msi: used_msi,
            hsi48: self.hsi48,
            pclk1: pclk1.Hz(),
            pclk2: pclk2.Hz(),
            ppre1: ppre1_div as u8,
            ppre2: ppre2_div as u8,
            sysclk: want_sysclk.Hz(),
            timclk1: timclk1.Hz(),
            timclk2: timclk2.Hz(),
            pll_source: if pllconf.is_some() { Some(pll_src) } else { None },
        }
    }
}

/// Frozen clock frequencies (indicate RCC config is locked)
#[derive(Clone, Copy, Debug)]
pub struct Clocks {
    hclk: Hertz,
    hsi48: bool,
    msi: Option<MsiFreq>,
    lsi: bool,
    lse: bool,
    pclk1: Hertz,
    pclk2: Hertz,
    ppre1: u8,
    ppre2: u8,
    sysclk: Hertz,
    timclk1: Hertz,
    timclk2: Hertz,
    pll_source: Option<PllSource>,
}

impl Clocks {
    pub fn hclk(&self) -> Hertz {
        self.hclk
    }
    pub fn hsi48(&self) -> bool {
        self.hsi48
    }
    pub fn msi(&self) -> Option<MsiFreq> {
        self.msi
    }
    pub fn lsi(&self) -> bool {
        self.lsi
    }
    pub fn lse(&self) -> bool {
        self.lse
    }
    pub fn pclk1(&self) -> Hertz {
        self.pclk1
    }
    pub fn pclk2(&self) -> Hertz {
        self.pclk2
    }
    pub fn pll_source(&self) -> Option<PllSource> {
        self.pll_source
    }
    #[allow(dead_code)]
    pub(crate) fn ppre1(&self) -> u8 {
        self.ppre1
    }
    #[allow(dead_code)]
    pub(crate) fn ppre2(&self) -> u8 {
        self.ppre2
    }
    pub fn sysclk(&self) -> Hertz {
        self.sysclk
    }
    pub fn timclk1(&self) -> Hertz {
        self.timclk1
    }
    pub fn timclk2(&self) -> Hertz {
        self.timclk2
    }
}
