use super::{EXTMEM_ORIGIN, PsramSize};
use crate::{
clock::ll::{ClockTree, PsramFunctionClockConfig, PsramInstance},
efuse,
peripherals::{HP_SYS, HP_SYS_CLKRST, MEMSPI2, PMU},
};
mod oct_hex;
#[derive(Copy, Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub enum PsramMode {
#[default]
Hex,
}
#[derive(Copy, Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub struct PsramConfig {
pub mode: PsramMode,
pub size: PsramSize,
pub timing: PsramTimingParams,
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PsramTimingParams {
pub clock_source: PsramFunctionClockConfig,
pub clock: u32,
pub mr0_rl: u8,
pub mr4_wl: u8,
pub rd_dummy_bits: u8,
pub wr_dummy_bits: u8,
pub reg_dummy_bits: u32,
}
impl Default for PsramTimingParams {
fn default() -> Self {
Self::MHZ_250
}
}
impl PsramTimingParams {
pub const MHZ_20: Self = Self {
clock_source: PsramFunctionClockConfig::Mpll,
clock: 20,
mr0_rl: 2,
mr4_wl: 2,
rd_dummy_bits: 17,
wr_dummy_bits: 7,
reg_dummy_bits: 8,
};
pub const MHZ_80: Self = Self {
clock_source: PsramFunctionClockConfig::Mpll,
clock: 80,
mr0_rl: 2,
mr4_wl: 2,
rd_dummy_bits: 17,
wr_dummy_bits: 7,
reg_dummy_bits: 8,
};
pub const MHZ_125: Self = Self {
clock_source: PsramFunctionClockConfig::Mpll,
clock: 125,
mr0_rl: 2,
mr4_wl: 2,
rd_dummy_bits: 17,
wr_dummy_bits: 7,
reg_dummy_bits: 8,
};
pub const MHZ_200: Self = Self {
clock_source: PsramFunctionClockConfig::Mpll,
clock: 200,
mr0_rl: 4,
mr4_wl: 1,
rd_dummy_bits: 25,
wr_dummy_bits: 11,
reg_dummy_bits: 12,
};
pub const MHZ_250: Self = Self {
clock_source: PsramFunctionClockConfig::Mpll,
clock: 250,
mr0_rl: 6,
mr4_wl: 3,
rd_dummy_bits: 33,
wr_dummy_bits: 15,
reg_dummy_bits: 16,
};
}
#[crate::ram]
pub(crate) fn init_psram(config: &mut PsramConfig) -> bool {
psram_phy_ldo_init();
enable_psram_mspi();
reset_psram_mspi();
ClockTree::with(|clocks| {
PsramInstance::Psram.configure_function_clock(clocks, config.timing.clock_source);
PsramInstance::Psram.request_function_clock(clocks);
});
if !oct_hex::set_bus_clock(config.timing.clock) {
return false;
}
let is_hex = matches!(config.mode, PsramMode::Hex);
oct_hex::enable_dll();
oct_hex::psram_pad_init(is_hex); oct_hex::set_cs_timing();
oct_hex::init_mr_registers(&config.timing, is_hex);
if config.size.is_auto() {
config.size = PsramSize::Size(oct_hex::psram_detect_size(&config.timing));
}
oct_hex::configure_psram_mspi(&config.timing, is_hex);
if efuse::chip_revision() == efuse::ChipRevision::from_combined(300) {
debug!("Applying P4 v3.0 PSRAM workaround");
p4_rev3_psram_workaround();
}
oct_hex::mmu_map_psram(config.size.get());
true
}
pub(crate) fn map_psram(config: PsramConfig) -> core::ops::Range<usize> {
let start = EXTMEM_ORIGIN;
start..start + config.size.get()
}
fn psram_phy_ldo_init() {
PMU::regs()
.ext_ldo_p0_0p1a()
.write(|w| unsafe { w.bits(0x4020_0100) });
PMU::regs()
.ext_ldo_p0_0p1a_ana()
.write(|w| unsafe { w.bits(0xB100_0000) });
PMU::regs()
.ext_ldo_p0_0p2a()
.write(|w| unsafe { w.bits(0x4020_0000) });
PMU::regs()
.ext_ldo_p0_0p2a_ana()
.write(|w| unsafe { w.bits(0xA000_0000) });
PMU::regs()
.ext_ldo_p0_0p3a()
.write(|w| unsafe { w.bits(0x4020_0000) });
PMU::regs()
.ext_ldo_p0_0p3a_ana()
.write(|w| unsafe { w.bits(0xA000_0000) });
PMU::regs()
.ext_ldo_p1_0p1a()
.write(|w| unsafe { w.bits(0x4020_0180) });
PMU::regs()
.ext_ldo_p1_0p1a_ana()
.write(|w| unsafe { w.bits(0x5700_0000) });
PMU::regs()
.ext_ldo_p1_0p2a()
.write(|w| unsafe { w.bits(0x4020_0000) });
PMU::regs()
.ext_ldo_p1_0p2a_ana()
.write(|w| unsafe { w.bits(0xA000_0000) });
PMU::regs()
.ext_ldo_p1_0p3a()
.write(|w| unsafe { w.bits(0x4020_0000) });
PMU::regs()
.ext_ldo_p1_0p3a_ana()
.write(|w| unsafe { w.bits(0xA000_0000) });
crate::rom::ets_delay_us(50);
}
fn enable_psram_mspi() {
HP_SYS_CLKRST::regs()
.soc_clk_ctrl0()
.modify(|_, w| w.psram_sys_clk_en().set_bit());
}
fn reset_psram_mspi() {
HP_SYS_CLKRST::regs().hp_rst_en0().modify(|_, w| {
w.rst_en_dual_mspi_axi().set_bit();
w.rst_en_dual_mspi_apb().set_bit()
});
HP_SYS_CLKRST::regs().hp_rst_en0().modify(|_, w| {
w.rst_en_dual_mspi_axi().clear_bit();
w.rst_en_dual_mspi_apb().clear_bit()
});
}
fn p4_rev3_psram_workaround() {
let cache_fctrl = MEMSPI2::regs().cache_fctrl().read().bits();
let cache_sctrl = MEMSPI2::regs().cache_sctrl().read().bits();
let sram_cmd = MEMSPI2::regs().sram_cmd().read().bits();
let sram_drd_cmd = MEMSPI2::regs().sram_drd_cmd().read().bits();
let sram_dwr_cmd = MEMSPI2::regs().sram_dwr_cmd().read().bits();
let sram_clk = MEMSPI2::regs().sram_clk().read().bits();
let ctrl1 = MEMSPI2::regs().ctrl1().read().bits();
let smem_ddr = MEMSPI2::regs().smem_ddr().read().bits();
let timing_cali = MEMSPI2::regs().timing_cali().read().bits();
let smem_timing_cali = MEMSPI2::regs().smem_timing_cali().read().bits();
let smem_ac = MEMSPI2::regs().smem_ac().read().bits();
HP_SYS::regs()
.core_err_resp_dis()
.write(|w| unsafe { w.bits(0x7) });
unsafe {
oct_hex::write_psram_mmu_entry(0, 0);
let _ = core::ptr::read_volatile(0x8800_0000u32 as *const u32);
let _ = core::ptr::read_volatile(0x8800_0080u32 as *const u32);
crate::rom::ets_delay_us(1);
}
reset_psram_mspi();
HP_SYS::regs()
.core_err_resp_dis()
.write(|w| unsafe { w.bits(0) });
unsafe {
MEMSPI2::regs().cache_fctrl().write(|w| w.bits(cache_fctrl));
MEMSPI2::regs().cache_sctrl().write(|w| w.bits(cache_sctrl));
MEMSPI2::regs().sram_cmd().write(|w| w.bits(sram_cmd));
MEMSPI2::regs()
.sram_drd_cmd()
.write(|w| w.bits(sram_drd_cmd));
MEMSPI2::regs()
.sram_dwr_cmd()
.write(|w| w.bits(sram_dwr_cmd));
MEMSPI2::regs().sram_clk().write(|w| w.bits(sram_clk));
MEMSPI2::regs().ctrl1().write(|w| w.bits(ctrl1));
MEMSPI2::regs().smem_ddr().write(|w| w.bits(smem_ddr));
MEMSPI2::regs().timing_cali().write(|w| w.bits(timing_cali));
MEMSPI2::regs()
.smem_timing_cali()
.write(|w| w.bits(smem_timing_cali));
MEMSPI2::regs().smem_ac().write(|w| w.bits(smem_ac));
}
}