use esp_rom_sys::rom::ets_delay_us;
use crate::{
efuse::ChipRevision,
ethernet::{RmiiClkIn, RmiiClkOut, RmiiClockConfig},
peripherals::{EMAC_EXT, LPWR},
private::Sealed,
soc::regi2c,
};
pub(super) const PHY_INTF_RMII: u8 = 4;
pub(super) const PHY_INTF_MII: u8 = 0;
pub struct ExternalRefClock<P>(P);
impl<P> ExternalRefClock<P> {
pub fn new(pin: P) -> Self {
Self(pin)
}
}
impl<P> Sealed for ExternalRefClock<P> {}
impl<P: RmiiClkIn> RmiiClockConfig for ExternalRefClock<P> {
fn configure(self) {
self.0.configure_iomux();
EMAC_EXT::regs()
.ex_phyinf_conf()
.modify(|_, w| unsafe { w.phy_intf_sel().bits(PHY_INTF_RMII) });
EMAC_EXT::regs().ex_oscclk_conf().modify(|_, w| {
w.clk_sel().set_bit()
});
EMAC_EXT::regs().ex_clk_ctrl().modify(|_, w| {
w.ext_en().set_bit();
w.int_en().clear_bit()
});
}
}
pub struct ApllClock<P>(P);
impl<P> ApllClock<P> {
pub fn new(pin: P) -> Self {
Self(pin)
}
}
impl<P> Sealed for ApllClock<P> {}
impl<P: RmiiClkOut> RmiiClockConfig for ApllClock<P> {
fn configure(self) {
self.0.configure_iomux();
const APLL_ODIV_50MHZ: u8 = 2;
let f_xtal = crate::clock::ll::xtal_clk_frequency();
let f_xtal_mhz = f_xtal / 1_000_000;
let sdm = ((400 << 16) / f_xtal_mhz) - (4 << 16);
let sdm2 = (sdm >> 16) as u8;
let sdm1 = ((sdm >> 8) & 0xff) as u8;
let sdm0 = (sdm & 0xff) as u8;
trace!("SDM2: {}, SDM1: {}, SDM0: {}", sdm2, sdm1, sdm0);
LPWR::regs().ana_conf().modify(|_, w| {
w.plla_force_pd().clear_bit();
w.plla_force_pu().set_bit()
});
regi2c::I2C_APLL_DSDM2.write_field(sdm2);
regi2c::I2C_APLL_DSDM1.write_field(sdm1);
regi2c::I2C_APLL_DSDM0.write_field(sdm0);
const CLK_LL_APLL_SDM_STOP_VAL_1: u8 = 0x09;
const CLK_LL_APLL_SDM_STOP_VAL_2_REV0: u8 = 0x69;
const CLK_LL_APLL_SDM_STOP_VAL_2_REV1: u8 = 0x49;
regi2c::I2C_APLL_SDM_CTRL.write_reg(CLK_LL_APLL_SDM_STOP_VAL_1);
if crate::soc::chip_revision_above(ChipRevision::from_combined(100)) {
regi2c::I2C_APLL_SDM_CTRL.write_reg(CLK_LL_APLL_SDM_STOP_VAL_2_REV1);
} else {
regi2c::I2C_APLL_SDM_CTRL.write_reg(CLK_LL_APLL_SDM_STOP_VAL_2_REV0);
}
regi2c::I2C_APLL_OR_OUTPUT_DIV.write_field(APLL_ODIV_50MHZ);
const APLL_CALIBRATION_DELAY: u8 = 0x0F;
const APLL_CALIBRATION_RSTB: u8 = 0x10;
const APLL_CALIBRATION_START: u8 = 0x20;
regi2c::I2C_APLL_IR_CAL.write_reg(APLL_CALIBRATION_DELAY);
regi2c::I2C_APLL_IR_CAL
.write_reg(APLL_CALIBRATION_DELAY | APLL_CALIBRATION_RSTB | APLL_CALIBRATION_START);
regi2c::I2C_APLL_IR_CAL.write_reg(APLL_CALIBRATION_DELAY | APLL_CALIBRATION_RSTB);
while regi2c::I2C_APLL_OR_CAL_END.read() == 0 {
ets_delay_us(1);
}
EMAC_EXT::regs()
.ex_phyinf_conf()
.modify(|_, w| unsafe { w.phy_intf_sel().bits(PHY_INTF_RMII) });
EMAC_EXT::regs().ex_clkout_conf().modify(|_, w| unsafe {
w.div_num().bits(0);
w.h_div_num().bits(0)
});
EMAC_EXT::regs().ex_oscclk_conf().modify(|_, w| {
w.clk_sel().clear_bit()
});
EMAC_EXT::regs().ex_clk_ctrl().modify(|_, w| {
w.int_en().set_bit();
w.ext_en().clear_bit()
});
}
}
pub(crate) struct MiiClock;
impl MiiClock {
pub(super) fn configure(&self) {
EMAC_EXT::regs()
.ex_phyinf_conf()
.modify(|_, w| unsafe { w.phy_intf_sel().bits(PHY_INTF_MII) });
EMAC_EXT::regs().ex_clk_ctrl().modify(|_, w| {
w.mii_clk_tx_en().set_bit();
w.mii_clk_rx_en().set_bit()
});
}
}