Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Removed

- The `ESP_HAL_CONFIG_XTAL_FREQUENCY` configuration option has been removed (#4517)

## [v1.0.0] - 2025-10-30

Expand Down
35 changes: 0 additions & 35 deletions esp-hal/esp_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,41 +27,6 @@ options:
default:
- value: false

# Ideally, we should be able to set any clock frequency for any chip. However,
# currently only the 32 and C2 implements any sort of configurability, and
# the rest have a fixed clock frequency.
- name: xtal-frequency
description: "The frequency of the crystal oscillator, in MHz. Set to `auto` to
automatically detect the frequency. `auto` may not be able to identify the clock
frequency in some cases. Also, configuring a specific frequency may increase
performance slightly."
default:
- if: 'chip == "esp32" || chip == "esp32c2"'
value: '"auto"'
- if: 'chip == "esp32c3" || chip == "esp32c6" || chip == "esp32s2" || chip == "esp32s3"'
value: '"40"'
- if: 'chip == "esp32h2"'
value: '"32"'
constraints:
- if: 'chip == "esp32" || chip == "esp32c2"'
type:
validator: enumeration
value:
- 'auto'
- '26'
- '40'
- if: 'chip == "esp32c3" || chip == "esp32c6" || chip == "esp32s2" || chip == "esp32s3"'
type:
validator: enumeration
value:
- '40'
- if: 'chip == "esp32h2"'
type:
validator: enumeration
value:
- '32'
active: 'chip == "esp32" || chip == "esp32c2"'

- name: spi-address-workaround
description: "Enables a workaround for the issue where SPI in
half-duplex mode incorrectly transmits the address on a single line if the
Expand Down
131 changes: 2 additions & 129 deletions esp-hal/src/clock/clocks_ll/esp32c2.rs
Original file line number Diff line number Diff line change
@@ -1,135 +1,8 @@
use crate::{
clock::{ApbClock, Clock, CpuClock, PllClock, RtcClock, XtalClock},
peripherals::{APB_CTRL, I2C_ANA_MST, LPWR, MODEM_CLKRST, SYSTEM},
soc::regi2c,
clock::{RtcClock, XtalClock},
peripherals::{APB_CTRL, MODEM_CLKRST},
};

const I2C_BBPLL_OC_DCHGP_LSB: u32 = 4;
const I2C_BBPLL_OC_DHREF_SEL_LSB: u32 = 4;
const I2C_BBPLL_OC_DLREF_SEL_LSB: u32 = 6;

pub(crate) fn esp32c2_rtc_bbpll_configure(xtal_freq: XtalClock, _pll_freq: PllClock) {
let div_ref: u8;
let div7_0: u8;
let dr1: u8;
let dr3: u8;
let dchgp: u8;
let dcur: u8;
let dbias: u8;

// Set this register to let the digital part know 480M PLL is used
SYSTEM::regs()
.cpu_per_conf()
.modify(|_, w| w.pll_freq_sel().set_bit());

I2C_ANA_MST::regs().ana_conf0().modify(|_, w| {
w.bbpll_stop_force_high().clear_bit();
w.bbpll_stop_force_low().set_bit()
});

// Configure 480M PLL
match xtal_freq {
XtalClock::_26M => {
div_ref = 12;
div7_0 = 236;
dr1 = 4;
dr3 = 4;
dchgp = 0;
dcur = 0;
dbias = 2;
}
XtalClock::_40M => {
div_ref = 0;
div7_0 = 8;
dr1 = 0;
dr3 = 0;
dchgp = 5;
dcur = 3;
dbias = 2;
}
}

regi2c::I2C_BBPLL_REG4.write_reg(0x6b);

let i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | div_ref;
let i2c_bbpll_dcur =
(1 << I2C_BBPLL_OC_DLREF_SEL_LSB) | (3 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur;

regi2c::I2C_BBPLL_OC_REF.write_reg(i2c_bbpll_lref);
regi2c::I2C_BBPLL_OC_DIV_REG.write_reg(div7_0);
regi2c::I2C_BBPLL_OC_DR1.write_field(dr1);
regi2c::I2C_BBPLL_OC_DR3.write_field(dr3);
regi2c::I2C_BBPLL_REG6.write_reg(i2c_bbpll_dcur);
regi2c::I2C_BBPLL_OC_VCO_DBIAS.write_field(dbias);

// WAIT CALIBRATION DONE
while I2C_ANA_MST::regs()
.ana_conf0()
.read()
.bbpll_cal_done()
.bit_is_clear()
{}

// workaround bbpll calibration might stop early
crate::rom::ets_delay_us(10);

// Stop BBPLL self-calibration
I2C_ANA_MST::regs().ana_conf0().modify(|_, w| {
w.bbpll_stop_force_high().set_bit();
w.bbpll_stop_force_low().clear_bit()
});
}

pub(crate) fn esp32c2_rtc_bbpll_enable() {
LPWR::regs().options0().modify(|_, w| {
w.bb_i2c_force_pd().clear_bit();
w.bbpll_force_pd().clear_bit();
w.bbpll_i2c_force_pd().clear_bit()
});
}

pub(crate) fn esp32c2_rtc_update_to_xtal(freq: XtalClock, div: u32) {
crate::rom::ets_update_cpu_frequency_rom(freq.mhz());

// Set divider from XTAL to APB clock. Need to set divider to 1 (reg. value 0)
// first.
SYSTEM::regs().sysclk_conf().modify(|_, w| unsafe {
w.pre_div_cnt().bits(0);
w.pre_div_cnt().bits((div - 1) as u16)
});

// No need to adjust the REF_TICK

// Switch clock source
SYSTEM::regs()
.sysclk_conf()
.modify(|_, w| unsafe { w.soc_clk_sel().bits(0) });
}

pub(crate) fn esp32c2_rtc_freq_to_pll_mhz(cpu_clock_speed: CpuClock) {
SYSTEM::regs().sysclk_conf().modify(|_, w| unsafe {
w.pre_div_cnt().bits(0);
w.soc_clk_sel().bits(1)
});
SYSTEM::regs().cpu_per_conf().modify(|_, w| unsafe {
w.cpuperiod_sel().bits(match cpu_clock_speed {
CpuClock::_80MHz => 0,
CpuClock::_120MHz => 1,
})
});

crate::rom::ets_update_cpu_frequency_rom(cpu_clock_speed.mhz());
}

pub(crate) fn esp32c2_rtc_apb_freq_update(apb_freq: ApbClock) {
let value = ((apb_freq.hz() >> 12) & u16::MAX as u32)
| (((apb_freq.hz() >> 12) & u16::MAX as u32) << 16);

LPWR::regs()
.store5()
.modify(|_, w| unsafe { w.data().bits(value) });
}

// Mask for clock bits used by both WIFI and Bluetooth, 0, 1, 2, 3, 7, 8, 9, 10,
// 19, 20, 21, 22, 23
const SYSTEM_WIFI_CLK_WIFI_BT_COMMON_M: u32 = 0x78078F;
Expand Down
100 changes: 37 additions & 63 deletions esp-hal/src/clock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ impl RtcClock {
}

/// Select source for RTC_SLOW_CLK.
#[cfg(not(any(esp32c6, esp32h2)))]
#[cfg(not(any(esp32c2, esp32c6, esp32h2)))]
pub(crate) fn set_slow_freq(slow_freq: RtcSlowClock) {
LPWR::regs().clk_conf().modify(|_, w| {
unsafe {
Expand All @@ -455,7 +455,7 @@ impl RtcClock {
}

/// Select source for RTC_FAST_CLK.
#[cfg(not(any(esp32c6, esp32h2)))]
#[cfg(not(any(esp32c2, esp32c6, esp32h2)))]
pub(crate) fn set_fast_freq(fast_freq: RtcFastClock) {
LPWR::regs().clk_conf().modify(|_, w| {
w.fast_clk_rtc_sel().bit(match fast_freq {
Expand Down Expand Up @@ -1093,7 +1093,9 @@ impl Clocks {
let config = Self::configure(cpu_clock_speed);
RtcClock::update_xtal_freq_mhz(config.xtal_clock.as_mhz());
unsafe { ACTIVE_CLOCKS = Some(config) };
})
});

crate::rtc_cntl::rtc::configure_clock();
}

fn try_get<'a>() -> Option<&'a Clocks> {
Expand All @@ -1116,51 +1118,30 @@ impl Clocks {
#[cfg(systimer)]
#[inline]
pub(crate) fn xtal_freq() -> Rate {
if esp_config::esp_config_str!("ESP_HAL_CONFIG_XTAL_FREQUENCY") == "auto"
&& let Some(clocks) = Self::try_get()
{
if let Some(clocks) = Self::try_get() {
return clocks.xtal_clock;
}

Self::measure_xtal_frequency().frequency()
}

#[cfg(not(esp32))] // unused
const fn xtal_frequency_from_config() -> Option<XtalClock> {
let frequency_conf = esp_config::esp_config_str!("ESP_HAL_CONFIG_XTAL_FREQUENCY");

for_each_soc_xtal_options!(
(all $( ($freq:literal) ),*) => {
paste::paste! {
return match frequency_conf.as_bytes() {
b"auto" => None,

// If the frequency is a pre-set value for the chip, return the associated enum variant.
$( _ if esp_config::esp_config_int_parse!(u32, frequency_conf) == $freq => Some(XtalClock::[<_ $freq M>]), )*
#[cfg(not(esp32))]
fn measure_xtal_frequency() -> XtalClock {
cfg_if::cfg_if! {
if #[cfg(esp32h2)] {
XtalClock::_32M
} else if #[cfg(not(esp32c2))] {
XtalClock::_40M
} else {
// TODO: we should be able to read from a retention register, but probe-rs flashes a
// bootloader that assumes a frequency, instead of choosing a matching one.
let mhz = RtcClock::estimate_xtal_frequency();

_ => None,
};
}
};
);
}
debug!("Working with a {}MHz crystal", mhz);

#[cfg(not(esp32))] // unused - the build-time config can be removed in favour of explicit configuration via esp_hal::init
fn measure_xtal_frequency() -> XtalClock {
if let Some(clock) = const { Self::xtal_frequency_from_config() } {
// Use the configured frequency
clock
} else if esp_config::esp_config_str!("ESP_HAL_CONFIG_XTAL_FREQUENCY") == "auto" {
// TODO: we should be able to read from a retention register, but probe-rs flashes a
// bootloader that assumes a frequency, instead of choosing a matching one.
let mhz = RtcClock::estimate_xtal_frequency();

debug!("Working with a {}MHz crystal", mhz);

// Try to guess the closest possible crystal value.
XtalClock::closest_from_mhz(mhz)
} else {
unreachable!("Invalid crystal frequency configured, this should not be possible.")
// Try to guess the closest possible crystal value.
XtalClock::closest_from_mhz(mhz)
}
}
}
}
Expand Down Expand Up @@ -1199,32 +1180,25 @@ impl Clocks {
impl Clocks {
/// Configure the CPU clock speed.
pub(crate) fn configure(cpu_clock_speed: CpuClock) -> Self {
let xtal_freq = Self::measure_xtal_frequency();
use crate::soc::clocks::{ClockTree, request_low_power_clk};

let apb_freq;
if cpu_clock_speed != CpuClock::default() {
let pll_freq = PllClock::Pll480MHz;

if cpu_clock_speed.mhz() <= xtal_freq.mhz() {
apb_freq = ApbClock::ApbFreqOther(cpu_clock_speed.mhz());
clocks_ll::esp32c2_rtc_update_to_xtal(xtal_freq, 1);
clocks_ll::esp32c2_rtc_apb_freq_update(apb_freq);
} else {
apb_freq = ApbClock::ApbFreq40MHz;
clocks_ll::esp32c2_rtc_bbpll_enable();
clocks_ll::esp32c2_rtc_bbpll_configure(xtal_freq, pll_freq);
clocks_ll::esp32c2_rtc_freq_to_pll_mhz(cpu_clock_speed);
clocks_ll::esp32c2_rtc_apb_freq_update(apb_freq);
}
} else {
apb_freq = ApbClock::ApbFreq40MHz;
// TODO: expose the whole new enum for custom options
match cpu_clock_speed {
CpuClock::_80MHz => crate::soc::clocks::CpuClock::_80MHz,
CpuClock::_120MHz => crate::soc::clocks::CpuClock::_120MHz,
}
.configure();

Self {
cpu_clock: cpu_clock_speed.frequency(),
apb_clock: apb_freq.frequency(),
xtal_clock: xtal_freq.frequency(),
}
ClockTree::with(|clocks| {
// TODO: this should be managed by esp-radio. The actual clock is probably managed by
// the hardware, but we need to make sure the upstream clocks are running.
request_low_power_clk(clocks);
Self {
cpu_clock: Rate::from_hz(crate::soc::clocks::cpu_clk_frequency(clocks)),
apb_clock: Rate::from_hz(crate::soc::clocks::apb_clk_frequency(clocks)),
xtal_clock: Rate::from_hz(crate::soc::clocks::xtl_clk_frequency(clocks)),
}
})
}
}

Expand Down
2 changes: 0 additions & 2 deletions esp-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,8 +689,6 @@ pub fn init(config: Config) -> Peripherals {

Clocks::init(config.cpu_clock());

crate::rtc_cntl::rtc::configure_clock();

// RTC domain must be enabled before we try to disable
let mut rtc = crate::rtc_cntl::Rtc::new(peripherals.LPWR.reborrow());

Expand Down
23 changes: 6 additions & 17 deletions esp-hal/src/rtc_cntl/rtc/esp32c2.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use strum::FromRepr;

use crate::{
clock::{RtcClock, RtcFastClock, RtcSlowClock},
clock::RtcClock,
peripherals::{APB_CTRL, EXTMEM, LPWR, SPI0, SPI1, SYSTEM},
rtc_cntl::RtcCalSel,
soc::regi2c,
Expand All @@ -14,9 +14,10 @@ pub(crate) fn init() {
regi2c::I2C_DIG_REG_XPD_RTC_REG.write_field(0);

unsafe {
rtc_cntl
.timer1()
.modify(|_, w| w.pll_buf_wait().bits(20u8).ck8m_wait().bits(20u8));
rtc_cntl.timer1().modify(|_, w| {
w.pll_buf_wait().bits(20u8);
w.ck8m_wait().bits(20u8)
});

rtc_cntl.timer5().modify(|_, w| w.min_slp_val().bits(2u8));
}
Expand All @@ -35,28 +36,16 @@ pub(crate) fn init() {
}

regi2c::I2C_ULP_IR_FORCE_XPD_CK.write_field(0);

// from esp_clk_init:
// clk_ll_rc_fast_enable();
rtc_cntl.clk_conf().modify(|_, w| w.enb_ck8m().clear_bit());
rtc_cntl
.timer1()
.modify(|_, w| unsafe { w.ck8m_wait().bits(5) });
// esp_rom_delay_us(SOC_DELAY_RC_FAST_ENABLE);
crate::rom::ets_delay_us(50);

RtcClock::set_fast_freq(RtcFastClock::RcFast);
RtcClock::set_slow_freq(RtcSlowClock::RcSlow);
}

pub(crate) fn configure_clock() {
// TODO: this needs to be re-done, too. "RtcMux" is just RC_SLOW_CLK.
let cal_val = loop {
let res = RtcClock::calibrate(RtcCalSel::RtcMux, 1024);
if res != 0 {
break res;
}
};

unsafe { LPWR::regs().store1().write(|w| w.bits(cal_val)) };
}

Expand Down
Loading
Loading