#![allow(dead_code, reason = "Some of this is bound to be unused")]
#![allow(missing_docs, reason = "Experimental")]
use esp_rom_sys::rom::{ets_delay_us, ets_update_cpu_frequency_rom};
use crate::{
clock::Clocks,
efuse::VOL_LEVEL_HP_INV,
peripherals::{APB_CTRL, LPWR, RTC_IO, SYSTEM, TIMG0, UART0, UART1, UART2},
rtc_cntl::Rtc,
soc::regi2c,
time::Rate,
};
define_clock_tree_types!();
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(
clippy::enum_variant_names,
reason = "MHz suffix indicates physical unit."
)]
#[non_exhaustive]
pub enum CpuClock {
#[default]
_80MHz = 80,
_160MHz = 160,
_240MHz = 240,
}
impl CpuClock {
const PRESET_80: ClockConfig = ClockConfig {
xtal_clk: None,
pll_clk: Some(PllClkConfig::_320),
apll_clk: None,
cpu_pll_div: Some(CpuPllDivConfig::new(CpuPllDivDivisor::_4)),
syscon_pre_div: None,
cpu_clk: Some(CpuClkConfig::Pll),
rtc_slow_clk: Some(RtcSlowClkConfig::RcSlow),
rtc_fast_clk: Some(RtcFastClkConfig::Rc),
timg_calibration_clock: None,
};
const PRESET_160: ClockConfig = ClockConfig {
xtal_clk: None,
pll_clk: Some(PllClkConfig::_320),
apll_clk: None,
cpu_pll_div: Some(CpuPllDivConfig::new(CpuPllDivDivisor::_2)),
syscon_pre_div: None,
cpu_clk: Some(CpuClkConfig::Pll),
rtc_slow_clk: Some(RtcSlowClkConfig::RcSlow),
rtc_fast_clk: Some(RtcFastClkConfig::Rc),
timg_calibration_clock: None,
};
const PRESET_240: ClockConfig = ClockConfig {
xtal_clk: None,
pll_clk: Some(PllClkConfig::_480),
apll_clk: None,
cpu_pll_div: Some(CpuPllDivConfig::new(CpuPllDivDivisor::_2)),
syscon_pre_div: None,
cpu_clk: Some(CpuClkConfig::Pll),
rtc_slow_clk: Some(RtcSlowClkConfig::RcSlow),
rtc_fast_clk: Some(RtcFastClkConfig::Rc),
timg_calibration_clock: None,
};
}
impl From<CpuClock> for ClockConfig {
fn from(value: CpuClock) -> ClockConfig {
match value {
CpuClock::_80MHz => CpuClock::PRESET_80,
CpuClock::_160MHz => CpuClock::PRESET_160,
CpuClock::_240MHz => CpuClock::PRESET_240,
}
}
}
impl Default for ClockConfig {
fn default() -> Self {
Self::from(CpuClock::default())
}
}
impl ClockConfig {
pub(crate) fn try_get_preset(self) -> Option<CpuClock> {
match self {
v if v == CpuClock::PRESET_80 => Some(CpuClock::_80MHz),
v if v == CpuClock::PRESET_160 => Some(CpuClock::_160MHz),
v if v == CpuClock::PRESET_240 => Some(CpuClock::_240MHz),
_ => None,
}
}
pub(crate) fn configure(mut self) {
ClockTree::with(|clocks| {
configure_xtal_clk(clocks, XtalClkConfig::_40);
configure_syscon_pre_div(clocks, SysconPreDivConfig::new(0));
configure_cpu_clk(clocks, CpuClkConfig::Xtal);
});
if self.xtal_clk.is_none() {
let xtal = ClockTree::with(detect_xtal_freq);
debug!("Auto-detected XTAL frequency: {}", xtal.value());
self.xtal_clk = Some(xtal);
}
self.apply();
}
}
fn detect_xtal_freq(clocks: &mut ClockTree) -> XtalClkConfig {
const CALIBRATION_CYCLES: u32 = 10;
LPWR::regs()
.clk_conf()
.modify(|_, w| w.dig_clk8m_d256_en().set_bit());
let (xtal_cycles, calibration_clock_frequency) = Clocks::measure_rtc_clock(
clocks,
TimgCalibrationClockConfig::RcFastDivClk,
CALIBRATION_CYCLES,
);
LPWR::regs()
.clk_conf()
.modify(|_, w| w.dig_clk8m_d256_en().clear_bit());
let mhz = (calibration_clock_frequency * xtal_cycles / CALIBRATION_CYCLES).as_mhz();
if mhz.abs_diff(40) < mhz.abs_diff(26) {
XtalClkConfig::_40
} else {
XtalClkConfig::_26
}
}
const BBPLL_IR_CAL_DELAY_VAL: u8 = 0x18;
const BBPLL_IR_CAL_EXT_CAP_VAL: u8 = 0x20;
const BBPLL_OC_ENB_FCAL_VAL: u8 = 0x9a;
const BBPLL_OC_ENB_VCON_VAL: u8 = 0x00;
const BBPLL_BBADC_CAL_7_0_VAL: u8 = 0x00;
const RTC_CNTL_DBIAS_1V10: u8 = 4;
const RTC_CNTL_DBIAS_1V25: u8 = 7;
const BBPLL_ENDIV5_VAL_320M: u8 = 0x43;
const BBPLL_BBADC_DSMP_VAL_320M: u8 = 0x84;
const BBPLL_ENDIV5_VAL_480M: u8 = 0xc3;
const BBPLL_BBADC_DSMP_VAL_480M: u8 = 0x74;
fn configure_xtal_clk_impl(
_clocks: &mut ClockTree,
_old_config: Option<XtalClkConfig>,
config: XtalClkConfig,
) {
let freq_mhz = config.value() / 1_000_000;
LPWR::regs().store4().modify(|r, w| unsafe {
let disable_rom_log_bit = r.bits() & Rtc::RTC_DISABLE_ROM_LOG;
let half = (freq_mhz & (0xFFFF & !Rtc::RTC_DISABLE_ROM_LOG)) | disable_rom_log_bit;
w.data().bits(half | (half << 16))
});
}
fn enable_pll_clk_impl(clocks: &mut ClockTree, en: bool) {
LPWR::regs().options0().modify(|_, w| {
let power_down = !en;
w.bias_i2c_force_pd().bit(power_down);
w.bb_i2c_force_pd().bit(power_down);
w.bbpll_force_pd().bit(power_down);
w.bbpll_i2c_force_pd().bit(power_down)
});
if !en {
return;
}
regi2c::I2C_BBPLL_IR_CAL_DELAY.write_reg(BBPLL_IR_CAL_DELAY_VAL);
regi2c::I2C_BBPLL_IR_CAL_EXT_CAP.write_reg(BBPLL_IR_CAL_EXT_CAP_VAL);
regi2c::I2C_BBPLL_OC_ENB_FCAL.write_reg(BBPLL_OC_ENB_FCAL_VAL);
regi2c::I2C_BBPLL_OC_ENB_VCON.write_reg(BBPLL_OC_ENB_VCON_VAL);
regi2c::I2C_BBPLL_BBADC_CAL_REG.write_reg(BBPLL_BBADC_CAL_7_0_VAL);
let xtal_cfg = unwrap!(clocks.xtal_clk);
let pll_cfg = unwrap!(clocks.pll_clk);
struct PllParams {
div_ref: u8,
div10_8: u8,
lref: u8,
dcur: u8,
bw: u8,
div7_0: u8,
}
impl PllParams {
fn apply(&self) {
regi2c::I2C_BBPLL_OC_LREF
.write_reg((self.lref << 7) | (self.div10_8 << 4) | self.div_ref);
regi2c::I2C_BBPLL_OC_DIV_REG.write_reg(self.div7_0);
regi2c::I2C_BBPLL_OC_DCUR.write_reg((self.bw << 6) | self.dcur);
}
}
struct VoltageParams {
endiv5: u8,
bbadc_dsmp: u8,
dbias: u8,
}
impl VoltageParams {
fn apply(&self) {
LPWR::regs()
.reg()
.modify(|_, w| unsafe { w.dig_dbias_wak().bits(self.dbias) });
regi2c::I2C_BBPLL_ENDIV5.write_reg(self.endiv5);
regi2c::I2C_BBPLL_BBADC_DSMP.write_reg(self.bbadc_dsmp);
}
}
let voltage_params = match pll_cfg {
PllClkConfig::_320 => VoltageParams {
dbias: RTC_CNTL_DBIAS_1V10,
endiv5: BBPLL_ENDIV5_VAL_320M,
bbadc_dsmp: BBPLL_BBADC_DSMP_VAL_320M,
},
PllClkConfig::_480 => VoltageParams {
dbias: RTC_CNTL_DBIAS_1V25 - crate::efuse::read_field_le::<u8>(VOL_LEVEL_HP_INV),
endiv5: BBPLL_ENDIV5_VAL_480M,
bbadc_dsmp: BBPLL_BBADC_DSMP_VAL_480M,
},
};
let pll_params = match xtal_cfg {
XtalClkConfig::_40 => PllParams {
div_ref: 0, lref: 0,
dcur: 6,
bw: 3,
div10_8: 0,
div7_0: match pll_cfg {
PllClkConfig::_320 => 32,
PllClkConfig::_480 => 28,
},
},
XtalClkConfig::_26 => PllParams {
div_ref: 12, lref: 1,
dcur: 0,
bw: 1,
div10_8: 4,
div7_0: match pll_cfg {
PllClkConfig::_320 => 224,
PllClkConfig::_480 => 144,
},
},
};
voltage_params.apply();
if matches!(pll_cfg, PllClkConfig::_480) {
ets_delay_us(3);
}
pll_params.apply();
const SOC_DELAY_PLL_ENABLE_WITH_32K: u32 = 160;
ets_delay_us(SOC_DELAY_PLL_ENABLE_WITH_32K);
}
fn configure_pll_clk_impl(
_clocks: &mut ClockTree,
_old_config: Option<PllClkConfig>,
_new_config: PllClkConfig,
) {
}
fn enable_apll_clk_impl(_clocks: &mut ClockTree, en: bool) {
LPWR::regs().ana_conf().modify(|_, w| {
w.plla_force_pd().bit(!en);
w.plla_force_pu().bit(en)
});
if en {
todo!(
"Implement APLL configuration and calibration here. See esp-idf `clk_ll_apll_set_config`"
);
}
}
fn configure_apll_clk_impl(
_clocks: &mut ClockTree,
_old_config: Option<ApllClkConfig>,
_config: ApllClkConfig,
) {
}
fn enable_rc_fast_clk_impl(_clocks: &mut ClockTree, en: bool) {
const RTC_CNTL_CK8M_DFREQ_DEFAULT: u8 = 172;
LPWR::regs().clk_conf().modify(|_, w| {
unsafe {
w.ck8m_div_sel().bits(0);
w.ck8m_dfreq().bits(RTC_CNTL_CK8M_DFREQ_DEFAULT);
}
let power_down = !en;
w.ck8m_force_pd().bit(power_down);
w.ck8m_force_pu().bit(!power_down)
});
}
fn enable_pll_f160m_clk_impl(_clocks: &mut ClockTree, _en: bool) {
}
fn enable_cpu_pll_div_in_impl(_clocks: &mut ClockTree, _en: bool) {
}
fn configure_cpu_pll_div_in_impl(
_clocks: &mut ClockTree,
_old_config: Option<CpuPllDivInConfig>,
_new_config: CpuPllDivInConfig,
) {
}
fn enable_cpu_pll_div_impl(_clocks: &mut ClockTree, _en: bool) {
}
fn configure_cpu_pll_div_impl(
_clocks: &mut ClockTree,
_old_config: Option<CpuPllDivConfig>,
_new_config: CpuPllDivConfig,
) {
}
fn enable_syscon_pre_div_in_impl(_: &mut ClockTree, _: bool) {
}
fn configure_syscon_pre_div_in_impl(
_clocks: &mut ClockTree,
_old_config: Option<SysconPreDivInConfig>,
_new_config: SysconPreDivInConfig,
) {
}
fn enable_syscon_pre_div_impl(_clocks: &mut ClockTree, _en: bool) {
}
fn configure_syscon_pre_div_impl(
_clocks: &mut ClockTree,
_old_config: Option<SysconPreDivConfig>,
new_config: SysconPreDivConfig,
) {
APB_CTRL::regs()
.sysclk_conf()
.modify(|_, w| unsafe { w.pre_div_cnt().bits(new_config.divisor() as u16 & 0x3FF) });
}
fn enable_apb_clk_impl(_clocks: &mut ClockTree, _en: bool) {
}
fn configure_apb_clk_impl(
_clocks: &mut ClockTree,
_old_config: Option<ApbClkConfig>,
_new_config: ApbClkConfig,
) {
}
fn enable_ref_tick_impl(_clocks: &mut ClockTree, _en: bool) {
}
fn configure_ref_tick_impl(
_clocks: &mut ClockTree,
_old_config: Option<RefTickConfig>,
_new_config: RefTickConfig,
) {
}
fn enable_ref_tick_xtal_impl(_clocks: &mut ClockTree, _en: bool) {
}
fn configure_ref_tick_xtal_impl(
_clocks: &mut ClockTree,
_old_config: Option<RefTickXtalConfig>,
new_config: RefTickXtalConfig,
) {
APB_CTRL::regs()
.xtal_tick_conf()
.write(|w| unsafe { w.xtal_tick_num().bits(new_config.divisor() as u8) });
}
fn enable_ref_tick_fosc_impl(_clocks: &mut ClockTree, _en: bool) {
}
fn configure_ref_tick_fosc_impl(
_clocks: &mut ClockTree,
_old_config: Option<RefTickFoscConfig>,
new_config: RefTickFoscConfig,
) {
APB_CTRL::regs()
.ck8m_tick_conf()
.write(|w| unsafe { w.ck8m_tick_num().bits(new_config.divisor() as u8) });
}
fn enable_ref_tick_apll_impl(_clocks: &mut ClockTree, _en: bool) {
}
fn configure_ref_tick_apll_impl(
_clocks: &mut ClockTree,
_old_config: Option<RefTickApllConfig>,
new_config: RefTickApllConfig,
) {
APB_CTRL::regs()
.apll_tick_conf()
.write(|w| unsafe { w.apll_tick_num().bits(new_config.divisor() as u8) });
}
fn enable_ref_tick_pll_impl(_clocks: &mut ClockTree, _en: bool) {
}
fn configure_ref_tick_pll_impl(
_clocks: &mut ClockTree,
_old_config: Option<RefTickPllConfig>,
new_config: RefTickPllConfig,
) {
APB_CTRL::regs()
.pll_tick_conf()
.write(|w| unsafe { w.pll_tick_num().bits(new_config.divisor() as u8) });
}
fn configure_cpu_clk_impl(
clocks: &mut ClockTree,
_old_config: Option<CpuClkConfig>,
new_config: CpuClkConfig,
) {
let pll_selector = clocks.cpu_pll_div_in;
let bbpll_config = clocks.pll_clk.map(|pll| pll.value()).unwrap_or_default();
let is_240mhz = pll_selector == Some(CpuPllDivInConfig::Pll) && bbpll_config == 480_000_000;
let clock_source_sel1_bit = match clocks.cpu_pll_div {
Some(CpuPllDivConfig {
divisor: CpuPllDivDivisor::_2,
}) => {
if is_240mhz {
2
} else {
1
}
}
_ => 0, };
SYSTEM::regs()
.cpu_per_conf()
.modify(|_, w| unsafe { w.cpuperiod_sel().bits(clock_source_sel1_bit) });
LPWR::regs().clk_conf().modify(|_, w| match new_config {
CpuClkConfig::Xtal => w.soc_clk_sel().xtal(),
CpuClkConfig::RcFast => w.soc_clk_sel().ck8m(),
CpuClkConfig::Apll => w.soc_clk_sel().apll(),
CpuClkConfig::Pll => w.soc_clk_sel().pll(),
});
let cpu_freq = Rate::from_hz(cpu_clk_frequency(clocks));
ets_update_cpu_frequency_rom(cpu_freq.as_mhz());
let apb_freq = Rate::from_hz(apb_clk_frequency(clocks));
update_apb_frequency(apb_freq);
}
fn update_apb_frequency(freq: Rate) {
let freq_shifted = (freq.as_hz() >> 12) & 0xFFFF;
let value = freq_shifted | (freq_shifted << 16);
LPWR::regs()
.store5()
.modify(|_, w| unsafe { w.data().bits(value) });
}
fn enable_apb_clk_cpu_div2_impl(_clocks: &mut ClockTree, _en: bool) {
}
fn enable_apb_clk_80m_impl(_clocks: &mut ClockTree, _en: bool) {
}
fn enable_xtal32k_clk_impl(_clocks: &mut ClockTree, en: bool) {
const CLK_LL_XTAL_32K_DAC_VAL: u8 = 1;
const CLK_LL_XTAL_32K_DRES_VAL: u8 = 3;
const CLK_LL_XTAL_32K_DBIAS_VAL: u8 = 0;
RTC_IO::regs().xtal_32k_pad().modify(|_, w| unsafe {
w.dac_xtal_32k().bits(CLK_LL_XTAL_32K_DAC_VAL);
w.dres_xtal_32k().bits(CLK_LL_XTAL_32K_DRES_VAL);
w.dbias_xtal_32k().bits(CLK_LL_XTAL_32K_DBIAS_VAL);
if en {
w.x32n_rde().clear_bit();
w.x32n_rue().clear_bit();
w.x32n_fun_ie().clear_bit();
w.x32p_rde().clear_bit();
w.x32p_rue().clear_bit();
w.x32p_fun_ie().clear_bit();
}
w.x32n_mux_sel().bit(en);
w.x32p_mux_sel().bit(en);
w.xpd_xtal_32k().bit(en)
});
LPWR::regs()
.clk_conf()
.modify(|_, w| w.dig_xtal32k_en().bit(en));
}
fn enable_rc_slow_clk_impl(_clocks: &mut ClockTree, en: bool) {
if en {
const RTC_CNTL_SCK_DCAP_DEFAULT: u8 = 255;
LPWR::regs()
.reg()
.modify(|_, w| unsafe { w.sck_dcap().bits(RTC_CNTL_SCK_DCAP_DEFAULT) });
}
}
fn enable_rc_fast_div_clk_impl(_clocks: &mut ClockTree, en: bool) {
LPWR::regs().clk_conf().modify(|_, w| {
w.enb_ck8m_div().bit(!en);
w.ck8m_div().div256()
});
}
fn enable_xtal_div_clk_impl(_clocks: &mut ClockTree, _en: bool) {
}
fn enable_rtc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) {
}
fn configure_rtc_slow_clk_impl(
_clocks: &mut ClockTree,
_old_config: Option<RtcSlowClkConfig>,
new_config: RtcSlowClkConfig,
) {
LPWR::regs().clk_conf().modify(|_, w| match new_config {
RtcSlowClkConfig::RcSlow => w.ana_clk_rtc_sel().slow_ck(),
RtcSlowClkConfig::Xtal32k => w.ana_clk_rtc_sel().ck_xtal_32k(),
RtcSlowClkConfig::RcFast => w.ana_clk_rtc_sel().ck8m_d256_out(),
});
}
fn enable_rtc_fast_clk_impl(_clocks: &mut ClockTree, _en: bool) {
}
fn configure_rtc_fast_clk_impl(
_clocks: &mut ClockTree,
_old_config: Option<RtcFastClkConfig>,
new_config: RtcFastClkConfig,
) {
LPWR::regs().clk_conf().modify(|_, w| match new_config {
RtcFastClkConfig::Xtal => w.fast_clk_rtc_sel().xtal_div_4(),
RtcFastClkConfig::Rc => w.fast_clk_rtc_sel().ck8m(),
});
}
fn enable_uart_mem_clk_impl(_clocks: &mut ClockTree, en: bool) {
let regs = SYSTEM::regs();
if en {
regs.perip_rst_en()
.modify(|_, w| w.uart_mem_rst().bit(true));
regs.perip_rst_en()
.modify(|_, w| w.uart_mem_rst().bit(false));
}
regs.perip_clk_en()
.modify(|_, w| w.uart_mem_clk_en().bit(en));
}
fn enable_timg_calibration_clock_impl(_clocks: &mut ClockTree, _en: bool) {
}
fn configure_timg_calibration_clock_impl(
_clocks: &mut ClockTree,
_old_config: Option<TimgCalibrationClockConfig>,
new_config: TimgCalibrationClockConfig,
) {
TIMG0::regs().rtccalicfg().modify(|_, w| unsafe {
w.rtc_cali_clk_sel().bits(match new_config {
TimgCalibrationClockConfig::RcSlowClk => 0,
TimgCalibrationClockConfig::RcFastDivClk => 1,
TimgCalibrationClockConfig::Xtal32kClk => 2,
})
});
}
impl McpwmInstance {
fn enable_function_clock_impl(self, _clocks: &mut ClockTree, _en: bool) {
}
fn configure_function_clock_impl(
self,
_clocks: &mut ClockTree,
_old_config: Option<McpwmFunctionClockConfig>,
_new_config: McpwmFunctionClockConfig,
) {
}
}
impl UartInstance {
fn enable_function_clock_impl(self, _clocks: &mut ClockTree, _en: bool) {
}
fn configure_function_clock_impl(
self,
_clocks: &mut ClockTree,
_old_config: Option<UartFunctionClockConfig>,
new_config: UartFunctionClockConfig,
) {
let regs = match self {
Self::Uart0 => UART0::regs(),
Self::Uart1 => UART1::regs(),
Self::Uart2 => UART2::regs(),
};
regs.conf0().modify(|_, w| {
w.tick_ref_always_on()
.bit(new_config.sclk == UartFunctionClockSclk::Apb)
});
}
fn enable_mem_clock_impl(self, _clocks: &mut ClockTree, _en: bool) {
}
fn configure_mem_clock_impl(
self,
_clocks: &mut ClockTree,
_old_config: Option<UartMemClockConfig>,
_new_config: UartMemClockConfig,
) {
}
fn enable_baud_rate_generator_impl(self, _clocks: &mut ClockTree, _en: bool) {
}
fn configure_baud_rate_generator_impl(
self,
_clocks: &mut ClockTree,
_old_config: Option<UartBaudRateGeneratorConfig>,
new_config: UartBaudRateGeneratorConfig,
) {
let regs = match self {
Self::Uart0 => UART0::regs(),
Self::Uart1 => UART1::regs(),
Self::Uart2 => UART2::regs(),
};
regs.clkdiv().write(|w| unsafe {
w.clkdiv().bits(new_config.integral as _);
w.frag().bits(new_config.fractional as _)
});
}
}