Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
esp32c3: Add support for PLL clock configuration
Signed-off-by: Gustavo Henrique Nihei <[email protected]>
  • Loading branch information
gustavonihei committed Aug 8, 2022
commit 80c21fc3e6ca241445dbb1a7d2d20f529bfe1bcc
11 changes: 9 additions & 2 deletions esp-hal-common/src/clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,20 @@ impl ClockControl {
/// Configure the CPU clock speed.
#[allow(unused)]
pub fn configure(clock_control: SystemClockControl, cpu_clock_speed: CpuClock) -> ClockControl {
clocks_ll::set_cpu_clock(cpu_clock_speed);
let apb_freq = clocks_ll::ApbFrequency::ApbFreq80MHz;
let xtal_freq = clocks_ll::XtalFrequency::RtcXtalFreq40M;
let pll_freq = clocks_ll::PllFrequency::Pll480MHz;

clocks_ll::esp32c3_rtc_bbpll_enable();
clocks_ll::esp32c3_rtc_bbpll_configure(xtal_freq, pll_freq);
clocks_ll::esp32c3_rtc_freq_to_pll_mhz(cpu_clock_speed);
clocks_ll::esp32c3_rtc_apb_freq_update(apb_freq);

ClockControl {
_private: (),
desired_rates: RawClocks {
cpu_clock: cpu_clock_speed.frequency(),
apb_clock: MegahertzU32::MHz(80),
apb_clock: MegahertzU32::MHz(apb_freq.mhz()),
xtal_clock: MegahertzU32::MHz(40),
i2c_clock: MegahertzU32::MHz(40),
},
Expand Down
280 changes: 270 additions & 10 deletions esp-hal-common/src/clocks_ll/esp32c3.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,280 @@
use paste::paste;

use crate::clock::CpuClock;

pub(crate) fn set_cpu_clock(cpu_clock_speed: CpuClock) {
let system_control = unsafe { &*crate::pac::SYSTEM::PTR };
use crate::rom::{ets_update_cpu_frequency, regi2c_ctrl_write_reg, regi2c_ctrl_write_reg_mask};
use crate::{regi2c_write, regi2c_write_mask};

const MHZ: u32 = 1_000_000;

const I2C_BBPLL: u32 = 0x66;
const I2C_BBPLL_HOSTID: u32 = 0;

const I2C_BBPLL_MODE_HF: u32 = 4;

const I2C_BBPLL_OC_REF_DIV: u32 = 2;
const I2C_BBPLL_OC_DCHGP_LSB: u32 = 4;
const I2C_BBPLL_OC_DIV_7_0: u32 = 3;

const I2C_BBPLL_OC_DR1: u32 = 5;
const I2C_BBPLL_OC_DR1_MSB: u32 = 2;
const I2C_BBPLL_OC_DR1_LSB: u32 = 0;

const I2C_BBPLL_OC_DR3: u32 = 5;
const I2C_BBPLL_OC_DR3_MSB: u32 = 6;
const I2C_BBPLL_OC_DR3_LSB: u32 = 4;

const I2C_BBPLL_OC_DCUR: u32 = 6;

const I2C_BBPLL_OC_VCO_DBIAS: u32 = 9;
const I2C_BBPLL_OC_VCO_DBIAS_MSB: u32 = 1;
const I2C_BBPLL_OC_VCO_DBIAS_LSB: u32 = 0;

const I2C_BBPLL_OC_DHREF_SEL: u32 = 6;
const I2C_BBPLL_OC_DHREF_SEL_MSB: u32 = 5;
const I2C_BBPLL_OC_DHREF_SEL_LSB: u32 = 4;

const I2C_BBPLL_OC_DLREF_SEL: u32 = 6;
const I2C_BBPLL_OC_DLREF_SEL_MSB: u32 = 7;
const I2C_BBPLL_OC_DLREF_SEL_LSB: u32 = 6;

const I2C_MST_ANA_CONF0_REG: u32 = 0x6000_e040;
const I2C_MST_BBPLL_STOP_FORCE_HIGH: u32 = 1 << 3;
const I2C_MST_BBPLL_STOP_FORCE_LOW: u32 = 1 << 2;

#[allow(unused)]
#[derive(Debug, Clone, Copy)]
pub(crate) enum XtalFrequency {
RtcXtalFreq40M,
RtcXtalFreq32M,
RtcXtalFreqOther(u32),
}

impl XtalFrequency {
pub(crate) fn mhz(&self) -> u32 {
match self {
XtalFrequency::RtcXtalFreq40M => 40,
XtalFrequency::RtcXtalFreq32M => 32,
XtalFrequency::RtcXtalFreqOther(mhz) => *mhz,
}
}
}

#[allow(unused)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum PllFrequency {
Pll320MHz,
Pll480MHz,
}

#[allow(unused)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ApbFrequency {
ApbFreq80MHz,
ApbFreqOther(u32),
}

impl ApbFrequency {
pub(crate) fn mhz(&self) -> u32 {
match self {
ApbFrequency::ApbFreq80MHz => 80,
ApbFrequency::ApbFreqOther(mhz) => *mhz,
}
}

fn hz(&self) -> u32 {
self.mhz() * MHZ
}
}

pub(crate) fn esp32c3_rtc_bbpll_configure(xtal_freq: XtalFrequency, pll_freq: PllFrequency) {
let system = unsafe { &*crate::pac::SYSTEM::ptr() };

unsafe {
let div_ref: u32;
let div7_0: u32;
let dr1: u32;
let dr3: u32;
let dchgp: u32;
let dcur: u32;
let dbias: u32;
let i2c_bbpll_lref: u32;
let i2c_bbpll_div_7_0: u32;
let i2c_bbpll_dcur: u32;

let clear_reg_mask = |reg, mask: u32| {
(reg as *mut u32).write_volatile((reg as *mut u32).read_volatile() & !mask)
};
let set_reg_mask = |reg, mask: u32| {
(reg as *mut u32).write_volatile((reg as *mut u32).read_volatile() | mask)
};

clear_reg_mask(I2C_MST_ANA_CONF0_REG, I2C_MST_BBPLL_STOP_FORCE_HIGH);
set_reg_mask(I2C_MST_ANA_CONF0_REG, I2C_MST_BBPLL_STOP_FORCE_LOW);

if pll_freq == PllFrequency::Pll480MHz {
// Set this register to let the digital part know 480M PLL is used
system
.cpu_per_conf
.modify(|_, w| w.pll_freq_sel().set_bit());

// Configure 480M PLL
match xtal_freq {
XtalFrequency::RtcXtalFreq40M => {
div_ref = 0;
div7_0 = 8;
dr1 = 0;
dr3 = 0;
dchgp = 5;
dcur = 3;
dbias = 2;
}

XtalFrequency::RtcXtalFreq32M => {
div_ref = 1;
div7_0 = 26;
dr1 = 1;
dr3 = 1;
dchgp = 4;
dcur = 0;
dbias = 2;
}

XtalFrequency::RtcXtalFreqOther(_) => {
div_ref = 0;
div7_0 = 8;
dr1 = 0;
dr3 = 0;
dchgp = 5;
dcur = 3;
dbias = 2;
}
}

regi2c_write!(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x6b);
} else {
// Clear this register to let the digital part know 320M PLL is used
system
.cpu_per_conf
.modify(|_, w| w.pll_freq_sel().clear_bit());

// Configure 320M PLL
match xtal_freq {
XtalFrequency::RtcXtalFreq40M => {
div_ref = 0;
div7_0 = 4;
dr1 = 0;
dr3 = 0;
dchgp = 5;
dcur = 3;
dbias = 2;
}

XtalFrequency::RtcXtalFreq32M => {
div_ref = 1;
div7_0 = 6;
dr1 = 0;
dr3 = 0;
dchgp = 5;
dcur = 3;
dbias = 2;
}

XtalFrequency::RtcXtalFreqOther(_) => {
div_ref = 0;
div7_0 = 4;
dr1 = 0;
dr3 = 0;
dchgp = 5;
dcur = 3;
dbias = 2;
}
}

regi2c_write!(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x69);
}

i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | div_ref;
i2c_bbpll_div_7_0 = div7_0;
i2c_bbpll_dcur =
(2 << I2C_BBPLL_OC_DLREF_SEL_LSB) | (1 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur;

regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_REF_DIV, i2c_bbpll_lref);

regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0);

regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR1, dr1);

regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DR3, dr3);

regi2c_write!(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur);

regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_VCO_DBIAS, dbias);

regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DHREF_SEL, 2);

regi2c_write_mask!(I2C_BBPLL, I2C_BBPLL_OC_DLREF_SEL, 1);
}
}

pub(crate) fn esp32c3_rtc_bbpll_enable() {
let rtc_cntl = unsafe { &*crate::pac::RTC_CNTL::ptr() };

rtc_cntl.options0.modify(|_, w| {
w.bb_i2c_force_pd()
.clear_bit()
.bbpll_force_pd()
.clear_bit()
.bbpll_i2c_force_pd()
.clear_bit()
});
}

pub(crate) fn esp32c3_rtc_update_to_xtal(freq: XtalFrequency, _div: u32) {
let system_control = unsafe { &*crate::pac::SYSTEM::ptr() };

unsafe {
ets_update_cpu_frequency(freq.mhz());
// Set divider from XTAL to APB clock. Need to set divider to 1 (reg. value 0) first.
system_control.sysclk_conf.modify(|_, w| {
w.pre_div_cnt()
.bits(0)
.pre_div_cnt()
.bits((_div - 1) as u16)
});

// No need to adjust the REF_TICK

// Switch clock source
system_control
.sysclk_conf
.modify(|_, w| w.soc_clk_sel().bits(1));
.modify(|_, w| w.soc_clk_sel().bits(0));
}
}

pub(crate) fn esp32c3_rtc_freq_to_pll_mhz(cpu_clock_speed: CpuClock) {
let system_control = unsafe { &*crate::pac::SYSTEM::ptr() };

unsafe {
system_control
.sysclk_conf
.modify(|_, w| w.pre_div_cnt().bits(0).soc_clk_sel().bits(1));
system_control.cpu_per_conf.modify(|_, w| {
w.pll_freq_sel()
.set_bit()
.cpuperiod_sel()
.bits(match cpu_clock_speed {
CpuClock::Clock80MHz => 0,
CpuClock::Clock160MHz => 1,
})
w.cpuperiod_sel().bits(match cpu_clock_speed {
CpuClock::Clock80MHz => 0,
CpuClock::Clock160MHz => 1,
})
});
ets_update_cpu_frequency(cpu_clock_speed.mhz());
}
}

pub(crate) fn esp32c3_rtc_apb_freq_update(apb_freq: ApbFrequency) {
let rtc_cntl = unsafe { &*crate::pac::RTC_CNTL::ptr() };
let value = ((apb_freq.hz() >> 12) & u16::MAX as u32)
| (((apb_freq.hz() >> 12) & u16::MAX as u32) << 16);

rtc_cntl
.store5
.modify(|_, w| unsafe { w.rtc_scratch5().bits(value) });
}
1 change: 1 addition & 0 deletions esp-hal-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub mod ledc;
pub mod prelude;
pub mod pulse_control;
pub mod rng;
pub mod rom;
pub mod rtc_cntl;
pub mod serial;
pub mod spi;
Expand Down
Loading