use core::ops::Range;
use super::{EXTMEM_ORIGIN, PsramSize};
use crate::{
peripherals::{CACHE, SPI0, SPI1},
psram::implem::quad::MspiTimingSpeedMode,
};
mod quad;
use procmacros::ram;
use quad::CommandMode;
const FUNC_SPICS1_SPICS1: u8 = 0;
const PIN_FUNC_GPIO: u8 = 1;
cfg_if::cfg_if! {
if #[cfg(esp32c61)] {
const PSRAM_CS_IO: u8 = 14;
const SPI_CS1_GPIO_NUM: u8 = 14;
const SPICS1_OUT_IDX: u8 = 102;
const PSRAM_SPIWP_SD3_IO: u8 = 17;
} else if #[cfg(esp32c5)] {
const PSRAM_CS_IO: u8 = 15;
const SPI_CS1_GPIO_NUM: u8 = 15;
const SPICS1_OUT_IDX: u8 = 101;
const PSRAM_SPIWP_SD3_IO: u8 = 18;
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum PsramLlCsIdT {
PsramLlCsId0,
PsramLlCsId1,
}
#[derive(Copy, Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FlashFreq {
FlashFreq40m = 40,
#[default]
FlashFreq80m = 80,
}
impl FlashFreq {
fn mhz(&self) -> u32 {
match self {
FlashFreq::FlashFreq40m => 40,
FlashFreq::FlashFreq80m => 80,
}
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SpiRamFreq {
#[default]
Freq40m = 40,
Freq80m = 80,
}
impl SpiRamFreq {
pub(crate) fn need_timing_tuning(&self) -> bool {
*self != SpiRamFreq::Freq40m
}
fn mhz(&self) -> u32 {
match self {
SpiRamFreq::Freq40m => 40,
SpiRamFreq::Freq80m => 80,
}
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct MspiTimingTuningParam {
pub spi_din_mode: u8,
pub spi_din_num: u8,
pub extra_dummy_len: u8,
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PsramConfig {
pub size: PsramSize,
pub flash_frequency: FlashFreq,
pub flash_tuning: MspiTimingTuningParam,
pub ram_frequency: SpiRamFreq,
pub ram_tuning: MspiTimingTuningParam,
}
impl Default for PsramConfig {
fn default() -> Self {
Self {
size: Default::default(),
flash_frequency: Default::default(),
flash_tuning: MspiTimingTuningParam {
spi_din_mode: 3,
spi_din_num: 1,
extra_dummy_len: 2,
},
ram_frequency: Default::default(),
ram_tuning: MspiTimingTuningParam {
spi_din_mode: 3,
spi_din_num: 1,
extra_dummy_len: 2,
},
}
}
}
#[procmacros::ram]
pub(crate) fn init_psram(config: &mut PsramConfig) -> bool {
quad::psram_init(config);
true
}
#[procmacros::ram]
pub(crate) fn map_psram(config: PsramConfig) -> Range<usize> {
const MMU_ACCESS_SPIRAM: u32 = 1 << 9;
const MMU_PAGE_SIZE: u32 = 0x10000;
const FLASH_MMU_TABLE_SIZE: u32 = 512;
const MMU_VALID: u32 = 1 << 10;
fn read_mmu_entry(i: u32) -> u32 {
SPI0::regs()
.mmu_item_index()
.write(|w| unsafe { w.mmu_item_index().bits(i) });
SPI0::regs()
.mmu_item_content()
.read()
.mmu_item_content()
.bits()
}
fn write_mmu_entry(i: u32, entry: u32) {
SPI0::regs()
.mmu_item_index()
.write(|w| unsafe { w.mmu_item_index().bits(i) });
SPI0::regs()
.mmu_item_content()
.write(|w| unsafe { w.mmu_item_content().bits(entry) });
}
let mut mapped_pages = 0;
for i in (0..(FLASH_MMU_TABLE_SIZE - 1)).rev() {
if (read_mmu_entry(i) & MMU_VALID) != 0 {
mapped_pages = i + 1;
break;
}
}
let start = EXTMEM_ORIGIN as u32 + (MMU_PAGE_SIZE * mapped_pages);
debug!("PSRAM start address = {:x}", start);
for i in 0..config.size.get() as u32 / MMU_PAGE_SIZE {
write_mmu_entry(i + mapped_pages, MMU_VALID + MMU_ACCESS_SPIRAM + i)
}
CACHE::regs().cache_ctrl().modify(|_, w| {
w.cache_shut_bus0().clear_bit();
w.cache_shut_bus1().clear_bit()
});
start as usize..start as usize + config.size.get()
}
fn core_clock_for_mode(_speed_mode: MspiTimingSpeedMode) -> u32 {
80
}
mod ctrlr_ll {
use super::*;
pub(crate) const PSRAM_CTRLR_LL_MSPI_ID_0: u32 = 0;
#[inline(always)]
pub(crate) fn psram_ctrlr_ll_set_cs_hold(_mspi_id: u32, hold_n: u32) {
assert!(hold_n > 0);
SPI0::regs().smem_ac().modify(|_, w| {
w.smem_cs_hold().set_bit();
unsafe {
w.smem_cs_hold_time().bits(hold_n as u8 - 1);
}
w
});
}
#[inline(always)]
pub(crate) fn psram_ctrlr_ll_set_cs_setup(_mspi_id: u32, setup_n: u32) {
assert!(setup_n > 0);
SPI0::regs().smem_ac().modify(|_, w| {
w.smem_cs_setup().set_bit();
unsafe {
w.smem_cs_setup_time().bits(setup_n as u8 - 1);
}
w
});
}
#[inline(always)]
pub(crate) fn psram_ctrlr_ll_set_read_mode(mspi_id: u32, read_mode: CommandMode) {
assert!(mspi_id == 0);
SPI0::regs().cache_sctrl().modify(|_, w| {
w.usr_sram_dio().bit(read_mode == CommandMode::PsramCmdSpi);
w.usr_sram_qio().bit(read_mode == CommandMode::PsramCmdQpi);
w
});
}
#[inline(always)]
pub(crate) fn psram_ctrlr_ll_set_wr_cmd(_mspi_id: u32, cmd_bitlen: u32, cmd_val: u32) {
assert!(cmd_bitlen > 0);
SPI0::regs()
.cache_sctrl()
.modify(|_, w| w.cache_sram_usr_wcmd().set_bit());
SPI0::regs().sram_dwr_cmd().modify(|_, w| {
unsafe { w.cache_sram_usr_wr_cmd_bitlen().bits(cmd_bitlen as u8 - 1) };
unsafe { w.cache_sram_usr_wr_cmd_value().bits(cmd_val as u16) };
w
});
}
#[inline(always)]
pub(crate) fn psram_ctrlr_ll_set_rd_cmd(_mspi_id: u32, cmd_bitlen: u32, cmd_val: u32) {
assert!(cmd_bitlen > 0);
SPI0::regs()
.cache_sctrl()
.modify(|_, w| w.cache_sram_usr_rcmd().set_bit());
SPI0::regs().sram_drd_cmd().modify(|_, w| {
unsafe { w.cache_sram_usr_rd_cmd_bitlen().bits(cmd_bitlen as u8 - 1) };
unsafe { w.cache_sram_usr_rd_cmd_value().bits(cmd_val as u16) };
w
});
}
#[inline(always)]
pub(crate) fn psram_ctrlr_ll_set_addr_bitlen(_mspi_id: u32, addr_bitlen: u32) {
assert!(addr_bitlen > 0);
SPI0::regs()
.cache_sctrl()
.modify(|_, w| unsafe { w.sram_addr_bitlen().bits(addr_bitlen as u8 - 1) });
}
#[inline(always)]
pub(crate) fn psram_ctrlr_ll_set_rd_dummy(_mspi_id: u32, dummy_n: u32) {
assert!(dummy_n > 0);
SPI0::regs().cache_sctrl().modify(|_, w| {
w.usr_rd_sram_dummy().set_bit();
unsafe { w.sram_rdummy_cyclelen().bits(dummy_n as u8 - 1) };
w
});
}
#[inline(always)]
pub(crate) fn psram_ctrlr_ll_set_cs_pin(_mspi_id: u32, cs_id: PsramLlCsIdT) {
SPI1::regs().misc().modify(|_, w| {
w.cs0_dis().bit(cs_id == PsramLlCsIdT::PsramLlCsId0);
w.cs1_dis().bit(cs_id == PsramLlCsIdT::PsramLlCsId1);
w
});
}
}
#[ram]
fn mspi_timing_config_set_flash_clock(flash_freq_mhz: u32, speed_mode: quad::MspiTimingSpeedMode) {
let core_clock_mhz = core_clock_for_mode(speed_mode);
mspi_timing_ll_set_core_clock(core_clock_mhz);
let freqdiv: u32 = core_clock_mhz / flash_freq_mhz;
debug!("flash freqdiv: {}", freqdiv);
assert!(freqdiv > 0);
let reg_val: u32 = mspi_timing_ll_calculate_clock_reg(freqdiv);
mspi_timing_ll_set_flash_clock(0, reg_val);
mspi_timing_ll_set_flash_clock(1, reg_val);
}
#[inline(always)]
pub(crate) fn mspi_timing_ll_set_core_clock(core_clk_mhz: u32) {
let divider = match core_clk_mhz {
80 => 6,
_ => panic!("Invalid core clock"),
};
crate::peripherals::PCR::regs()
.mspi_clk_conf()
.modify(|_, w| unsafe { w.mspi_fast_div_num().bits(divider - 1) });
}
#[inline(always)]
fn mspi_timing_ll_calculate_clock_reg(clkdiv: u32) -> u32 {
if clkdiv == 1 {
1 << 31
} else {
(clkdiv - 1) | ((((clkdiv - 1) / 2) & 0xff) << 8) | (((clkdiv - 1) & 0xff) << 16)
}
}
#[inline(always)]
fn mspi_timing_ll_set_flash_clock(mspi_id: u32, clock_conf: u32) {
if mspi_id == 0 {
SPI0::regs()
.clock()
.write(|w| unsafe { w.bits(clock_conf) });
} else {
SPI1::regs()
.clock()
.write(|w| unsafe { w.bits(clock_conf) });
}
}
#[ram]
fn mspi_timing_config_set_psram_clock(psram_freq_mhz: u32, speed_mode: quad::MspiTimingSpeedMode) {
let core_clock_mhz = core_clock_for_mode(speed_mode);
mspi_timing_ll_set_core_clock(core_clock_mhz);
let freqdiv: u32 = core_clock_mhz / psram_freq_mhz;
debug!("psram freqdiv: {}", freqdiv);
assert!(freqdiv > 0);
let reg_val: u32 = mspi_timing_ll_calculate_clock_reg(freqdiv);
mspi_timing_ll_set_psram_clock(reg_val);
}
#[inline(always)]
fn mspi_timing_ll_set_psram_clock(clock_conf: u32) {
SPI0::regs()
.sram_clk()
.write(|w| unsafe { w.bits(clock_conf) });
}
#[inline(always)]
pub(crate) fn mspi_timing_ll_set_flash_din_mode(mspi_id: u8, din_mode: u8) {
assert!(mspi_id == 0);
assert!(din_mode <= 7);
SPI0::regs().din_mode().modify(|_, w| {
unsafe {
w.din0_mode().bits(din_mode);
w.din1_mode().bits(din_mode);
w.din2_mode().bits(din_mode);
w.din3_mode().bits(din_mode);
w.din4_mode().bits(din_mode);
w.din5_mode().bits(din_mode);
w.din6_mode().bits(din_mode);
w.din7_mode().bits(din_mode);
w.dins_mode().bits(din_mode);
}
w
});
SPI0::regs()
.timing_cali()
.modify(|_, w| w.update().set_bit());
}
#[inline(always)]
pub(crate) fn mspi_timing_ll_set_flash_din_num(mspi_id: u8, din_num: u8) {
assert!(mspi_id == 0);
assert!(din_num <= 3);
SPI0::regs().din_num().modify(|_, w| {
unsafe {
w.din0_num().bits(din_num);
w.din1_num().bits(din_num);
w.din2_num().bits(din_num);
w.din3_num().bits(din_num);
w.din4_num().bits(din_num);
w.din5_num().bits(din_num);
w.din6_num().bits(din_num);
w.din7_num().bits(din_num);
w.dins_num().bits(din_num);
}
w
});
SPI0::regs()
.timing_cali()
.modify(|_, w| w.update().set_bit());
}
#[inline(always)]
pub(crate) fn mspi_timing_ll_set_flash_extra_dummy(mspi_id: u8, extra_dummy: u8) {
if mspi_id == 0 {
SPI0::regs().timing_cali().modify(|_, w| {
w.timing_cali().bit(extra_dummy > 0);
unsafe { w.extra_dummy_cyclelen().bits(extra_dummy) };
w
});
} else {
SPI1::regs().timing_cali().modify(|_, w| {
w.timing_cali().bit(extra_dummy > 0);
unsafe { w.extra_dummy_cyclelen().bits(extra_dummy) };
w
});
}
SPI0::regs()
.timing_cali()
.modify(|_, w| w.update().set_bit());
}
#[inline(always)]
pub(crate) fn mspi_timing_ll_set_psram_din_mode(mspi_id: u8, din_mode: u8) {
assert!(mspi_id == 0);
assert!(din_mode <= 7);
SPI0::regs().smem_din_mode().modify(|_, w| {
unsafe {
w.smem_din0_mode().bits(din_mode);
w.smem_din1_mode().bits(din_mode);
w.smem_din2_mode().bits(din_mode);
w.smem_din3_mode().bits(din_mode);
w.smem_din4_mode().bits(din_mode);
w.smem_din5_mode().bits(din_mode);
w.smem_din6_mode().bits(din_mode);
w.smem_din7_mode().bits(din_mode);
w.smem_dins_mode().bits(din_mode);
}
w
});
SPI0::regs()
.timing_cali()
.modify(|_, w| w.update().set_bit());
}
#[inline(always)]
pub(crate) fn mspi_timing_ll_set_psram_din_num(mspi_id: u8, din_num: u8) {
assert!(mspi_id == 0);
assert!(din_num <= 3);
SPI0::regs().smem_din_num().modify(|_, w| {
unsafe {
w.smem_din0_num().bits(din_num);
w.smem_din1_num().bits(din_num);
w.smem_din2_num().bits(din_num);
w.smem_din3_num().bits(din_num);
w.smem_din4_num().bits(din_num);
w.smem_din5_num().bits(din_num);
w.smem_din6_num().bits(din_num);
w.smem_din7_num().bits(din_num);
w.smem_dins_num().bits(din_num);
}
w
});
SPI0::regs()
.timing_cali()
.modify(|_, w| w.update().set_bit());
}
#[inline(always)]
pub(crate) fn mspi_timing_ll_set_psram_extra_dummy(mspi_id: u8, extra_dummy: u8) {
assert!(mspi_id == 0);
SPI0::regs().smem_timing_cali().modify(|_, w| {
w.smem_timing_cali().bit(extra_dummy > 0);
unsafe { w.smem_extra_dummy_cyclelen().bits(extra_dummy) };
w
});
SPI0::regs()
.timing_cali()
.modify(|_, w| w.update().set_bit());
}
#[inline(always)]
pub(crate) fn mspi_timing_ll_get_flash_dummy(mspi_id: u8) -> (u8, u8) {
assert!(mspi_id <= 1);
if mspi_id == 0 {
let usr_dummy = SPI0::regs().user1().read().usr_dummy_cyclelen().bits();
let extra_dummy = SPI0::regs()
.timing_cali()
.read()
.extra_dummy_cyclelen()
.bits();
(usr_dummy, extra_dummy)
} else {
let usr_dummy = SPI1::regs().user1().read().usr_dummy_cyclelen().bits();
let extra_dummy = SPI1::regs()
.timing_cali()
.read()
.extra_dummy_cyclelen()
.bits();
(usr_dummy, extra_dummy)
}
}
#[inline(always)]
pub(crate) fn mspi_timing_ll_get_psram_dummy(mspi_id: u8) -> (u8, u8) {
assert!(mspi_id == 0);
let usr_dummy = SPI0::regs()
.cache_sctrl()
.read()
.sram_rdummy_cyclelen()
.bits();
let extra_dummy = SPI0::regs()
.smem_timing_cali()
.read()
.smem_extra_dummy_cyclelen()
.bits();
(usr_dummy, extra_dummy)
}