use crate::{
clock::ll::PsramInstance,
pac::{generic::Reg, iomux_mspi_pin::psram_d_pin0::PSRAM_D_PIN0_SPEC},
peripherals::{IOMUX_MSPI_PIN, MEMSPI2, MEMSPI3},
psram::{EXTMEM_ORIGIN, PsramTimingParams},
};
pub(super) fn psram_detect_size(speed_params: &PsramTimingParams) -> usize {
let (mr2, _mr3) = psram_mr_read(speed_params, MR_ADDR_MR2_MR3);
match mr2 & 0x7 {
0x1 => 4 * 1024 * 1024, 0x3 => 8 * 1024 * 1024, 0x5 => 16 * 1024 * 1024, 0x6 => 64 * 1024 * 1024, 0x7 => 32 * 1024 * 1024, _ => {
warn!("Unknown PSRAM size: mr2={:#x}", mr2);
32 * 1024 * 1024
}
}
}
pub(super) fn write_psram_mmu_entry(entry_id: u32, page: u16) {
MEMSPI2::regs()
.mmu_item_index()
.write(|w| unsafe { w.mmu_item_index().bits(entry_id) });
MEMSPI2::regs().mmu_item_content().write(|w| unsafe {
w.paddr().bits(page);
w.access_spiram().set_bit();
w.spiram_valid().set_bit()
});
}
pub(super) fn mmu_map_psram(size: usize) {
const MMU_PAGE_SIZE: usize = property!("mmu.page_size");
let page_count = size / MMU_PAGE_SIZE;
for page in 0..page_count {
write_psram_mmu_entry(page as u32, page as u16);
}
unsafe { crate::soc::cache_invalidate_addr(EXTMEM_ORIGIN as u32, size as u32) };
}
pub(super) fn configure_psram_mspi(timing: &PsramTimingParams, is_hex: bool) {
MEMSPI2::regs().cache_sctrl().modify(|_, w| unsafe {
w.cache_usr_saddr_4byte().set_bit();
w.usr_wr_sram_dummy().set_bit();
w.usr_rd_sram_dummy().set_bit();
w.cache_sram_usr_rcmd().set_bit();
w.sram_rdummy_cyclelen().bits(timing.rd_dummy_bits);
w.sram_wdummy_cyclelen().bits(timing.wr_dummy_bits);
w.sram_addr_bitlen().bits(31);
w.cache_sram_usr_wcmd().set_bit();
w.sram_oct().set_bit();
w
});
MEMSPI2::regs().sram_cmd().modify(|_, w| {
w.sdin_oct().set_bit();
w.sdout_oct().set_bit();
w.saddr_oct().set_bit();
w.scmd_oct().set_bit();
w.sdummy_wout().set_bit();
w.sdin_hex().bit(is_hex);
w.sdout_hex().bit(is_hex)
});
MEMSPI2::regs().sram_drd_cmd().write(|w| unsafe {
w.cache_sram_usr_rd_cmd_bitlen().bits(15);
w.cache_sram_usr_rd_cmd_value().bits(0)
});
MEMSPI2::regs().sram_dwr_cmd().write(|w| unsafe {
w.cache_sram_usr_wr_cmd_bitlen().bits(15);
w.cache_sram_usr_wr_cmd_value().bits(0x8080)
});
MEMSPI2::regs().ctrl1().modify(|_, w| {
w.ar_splice_en().set_bit();
w.aw_splice_en().set_bit()
});
MEMSPI2::regs().smem_ddr().modify(|_, w| {
w.var_dummy().set_bit();
w.rdat_swp().clear_bit();
w.wdat_swp().clear_bit();
w.en().set_bit()
});
MEMSPI2::regs().cache_fctrl().modify(|_, w| {
w.axi_req_en().set_bit();
w.close_axi_inf_en().clear_bit()
});
}
pub(super) fn set_bus_clock(clock: u32) -> bool {
let source_mhz = PsramInstance::Psram.function_clock_frequency() / 1_000_000;
if !source_mhz.is_multiple_of(clock) {
error!(
"PSRAM source clock frequency must be an integer multiple of PSRAM frequency. Source={} MHz, PSRAM={} MHz",
source_mhz, clock
);
return false;
}
let div = (source_mhz / clock) as u8;
if div <= 1 {
MEMSPI2::regs().sram_clk().write(|w| unsafe {
w.sclk_equ_sysclk().set_bit();
w.sclkcnt_n().bits(0);
w.sclkcnt_h().bits(0);
w.sclkcnt_l().bits(0)
});
MEMSPI3::regs().clock().write(|w| unsafe {
w.clk_equ_sysclk().set_bit();
w.clkcnt_n().bits(0);
w.clkcnt_h().bits(0);
w.clkcnt_l().bits(0)
});
} else {
MEMSPI2::regs().sram_clk().write(|w| unsafe {
w.sclkcnt_n().bits(div - 1);
w.sclkcnt_h().bits(div / 2 - 1);
w.sclkcnt_l().bits(div - 1)
});
MEMSPI3::regs().clock().write(|w| unsafe {
w.clkcnt_n().bits(div - 1);
w.clkcnt_h().bits(div / 2 - 1);
w.clkcnt_l().bits(div - 1)
});
};
true
}
pub(super) fn enable_dll() {
MEMSPI2::regs()
.timing_cali()
.modify(|_, w| w.dll_timing_cali().set_bit());
MEMSPI2::regs()
.smem_timing_cali()
.modify(|_, w| w.dll_timing_cali().set_bit());
}
pub(super) fn psram_pad_init(_is_hex: bool) {
fn init_pin_drv(reg: &Reg<PSRAM_D_PIN0_SPEC>) {
reg.modify(|_, w| unsafe { w.drv().bits(2) });
}
init_pin_drv(IOMUX_MSPI_PIN::regs().psram_d_pin0());
init_pin_drv(IOMUX_MSPI_PIN::regs().psram_q_pin0());
init_pin_drv(IOMUX_MSPI_PIN::regs().psram_wp_pin0());
init_pin_drv(IOMUX_MSPI_PIN::regs().psram_hold_pin0());
init_pin_drv(IOMUX_MSPI_PIN::regs().psram_dq4_pin0());
init_pin_drv(IOMUX_MSPI_PIN::regs().psram_dq5_pin0());
init_pin_drv(IOMUX_MSPI_PIN::regs().psram_dq6_pin0());
init_pin_drv(IOMUX_MSPI_PIN::regs().psram_dq7_pin0());
IOMUX_MSPI_PIN::regs()
.psram_dqs_0_pin0()
.modify(|_, w| unsafe {
w.drv().bits(2);
w.xpd().set_bit()
});
init_pin_drv(IOMUX_MSPI_PIN::regs().psram_ck_pin0());
init_pin_drv(IOMUX_MSPI_PIN::regs().psram_cs_pin0());
#[cfg(psram_hex_spi)]
if _is_hex {
init_pin_drv(IOMUX_MSPI_PIN::regs().psram_dq8_pin0());
init_pin_drv(IOMUX_MSPI_PIN::regs().psram_dq9_pin0());
init_pin_drv(IOMUX_MSPI_PIN::regs().psram_dq10_pin0());
init_pin_drv(IOMUX_MSPI_PIN::regs().psram_dq11_pin0());
init_pin_drv(IOMUX_MSPI_PIN::regs().psram_dq12_pin0());
init_pin_drv(IOMUX_MSPI_PIN::regs().psram_dq13_pin0());
init_pin_drv(IOMUX_MSPI_PIN::regs().psram_dq14_pin0());
init_pin_drv(IOMUX_MSPI_PIN::regs().psram_dq15_pin0());
IOMUX_MSPI_PIN::regs()
.psram_dqs_1_pin0()
.modify(|_, w| unsafe {
w.drv().bits(2);
w.xpd().set_bit()
});
}
}
pub(super) fn set_cs_timing() {
const AP_CS_SETUP_TIME: u8 = 4;
const AP_CS_HOLD_TIME: u8 = 4;
const AP_CS_HOLD_DELAY: u8 = 3;
MEMSPI2::regs().smem_ac().modify(|_, w| unsafe {
w.cs_setup().set_bit();
w.cs_hold().set_bit();
w.split_trans_en().set_bit();
w.cs_setup_time().bits(AP_CS_SETUP_TIME - 1);
w.cs_hold_time().bits(AP_CS_HOLD_TIME - 1);
w.cs_hold_delay().bits(AP_CS_HOLD_DELAY - 1);
w
});
}
const MR_ADDR_MR0_MR1: u32 = 0x0;
const MR_ADDR_MR2_MR3: u32 = 0x2;
const MR_ADDR_MR4_MR5: u32 = 0x4;
#[allow(dead_code)] const MR_ADDR_MR6_MR7: u32 = 0x6;
const MR_ADDR_MR8_MR9: u32 = 0x8;
fn psram_mr_read(timing: &PsramTimingParams, mr_addr: u32) -> (u8, u8) {
let pair = mspi1_reg_read16(timing, mr_addr);
((pair & 0xFF) as u8, ((pair >> 8) & 0xFF) as u8)
}
fn psram_mr_write(mr_addr: u32, low: u8, high: u8) {
let pair = (low as u16) | ((high as u16) << 8);
mspi1_reg_write16(mr_addr, pair)
}
pub(super) fn init_mr_registers(timing: &PsramTimingParams, is_hex: bool) {
let (mut mr0, mr1) = psram_mr_read(timing, MR_ADDR_MR0_MR1);
mr0 &= !(0x3 | (0x7 << 2) | (1 << 5));
mr0 |= ((timing.mr0_rl & 0x7) << 2) | (1 << 5);
psram_mr_write(MR_ADDR_MR0_MR1, mr0, mr1);
let (mut mr4, mr5) = psram_mr_read(timing, MR_ADDR_MR4_MR5);
mr4 &= !(0x7 << 5);
mr4 |= (timing.mr4_wl & 0x7) << 5;
psram_mr_write(MR_ADDR_MR4_MR5, mr4, mr5);
let (mut mr8, mr9) = psram_mr_read(timing, MR_ADDR_MR8_MR9); mr8 &= !(0x3 | (1 << 2) | (1 << 3) | (1 << 6));
mr8 |= 3 | (1 << 3); if is_hex {
mr8 |= 1 << 6; }
psram_mr_write(MR_ADDR_MR8_MR9, mr8, mr9); }
#[repr(C)]
struct EspRomSpiCmd {
cmd: u16,
cmd_bit_len: u16,
addr: *mut u32,
addr_bit_len: u32,
tx_data: *mut u32,
tx_data_bit_len: u32,
rx_data: *mut u32,
rx_data_bit_len: u32,
dummy_bit_len: u32,
}
const ESP_ROM_SPIFLASH_OPI_DTR_MODE: u32 = 7;
const ROM_SPI_PSRAM_CMD_NUM: i32 = 3;
unsafe extern "C" {
fn esp_rom_spi_set_op_mode(spi_num: i32, mode: u32);
fn esp_rom_spi_cmd_config(spi_num: i32, pcmd: *mut EspRomSpiCmd);
}
fn mspi1_kick_and_collect(rx: &mut [u8]) -> Result<(), ()> {
const SPI_USR_TRIGGER: u32 = 1 << 18;
const MAX_ITERS: u32 = 1_000_000;
MEMSPI3::regs().misc().modify(|_, w| {
w.cs1_dis().clear_bit();
w.cs0_dis().set_bit()
});
MEMSPI3::regs()
.cmd()
.write(|w| unsafe { w.bits(SPI_USR_TRIGGER) });
let mut t = MAX_ITERS;
while MEMSPI3::regs().cmd().read().bits() & SPI_USR_TRIGGER != 0 {
t -= 1;
if t == 0 {
return Err(());
}
core::hint::spin_loop();
}
if !rx.is_empty() {
let n_bytes = rx.len();
let n_words = n_bytes.div_ceil(4);
for i in 0..n_words {
let word = MEMSPI3::regs().w(i).read().bits();
for b in 0..4 {
let off = i * 4 + b;
if off >= n_bytes {
break;
}
rx[off] = ((word >> (b * 8)) & 0xFF) as u8;
}
}
}
Ok(())
}
fn mspi1_reg_read16(timing: &PsramTimingParams, addr: u32) -> u16 {
const REG_READ_CMD: u16 = 0x4040;
const CMD_BITLEN: u16 = 16;
const ADDR_BITLEN: u32 = 32;
const DATA_BITLEN: u32 = 16;
let mut addr_local = addr;
let mut rx: [u8; 2] = [0; 2];
let mut conf = EspRomSpiCmd {
cmd: REG_READ_CMD,
cmd_bit_len: CMD_BITLEN,
addr: &raw mut addr_local,
addr_bit_len: ADDR_BITLEN,
tx_data: core::ptr::null_mut(),
tx_data_bit_len: 0,
rx_data: rx.as_mut_ptr().cast::<u32>(),
rx_data_bit_len: DATA_BITLEN,
dummy_bit_len: timing.reg_dummy_bits,
};
unsafe {
esp_rom_spi_set_op_mode(ROM_SPI_PSRAM_CMD_NUM, ESP_ROM_SPIFLASH_OPI_DTR_MODE);
esp_rom_spi_cmd_config(ROM_SPI_PSRAM_CMD_NUM, &raw mut conf);
}
let _ = mspi1_kick_and_collect(&mut rx);
u16::from_le_bytes(rx)
}
fn mspi1_reg_write16(addr: u32, data: u16) {
const REG_WRITE_CMD: u16 = 0xC0C0;
const CMD_BITLEN: u16 = 16;
const ADDR_BITLEN: u32 = 32;
const DATA_BITLEN: u32 = 16;
let mut addr_local = addr;
let mut tx = data.to_le_bytes();
let mut conf = EspRomSpiCmd {
cmd: REG_WRITE_CMD,
cmd_bit_len: CMD_BITLEN,
addr: &raw mut addr_local,
addr_bit_len: ADDR_BITLEN,
tx_data: tx.as_mut_ptr().cast::<u32>(),
tx_data_bit_len: DATA_BITLEN,
rx_data: core::ptr::null_mut(),
rx_data_bit_len: 0,
dummy_bit_len: 0,
};
unsafe {
esp_rom_spi_set_op_mode(ROM_SPI_PSRAM_CMD_NUM, ESP_ROM_SPIFLASH_OPI_DTR_MODE);
esp_rom_spi_cmd_config(ROM_SPI_PSRAM_CMD_NUM, &raw mut conf);
}
let mut empty: [u8; 0] = [];
let _ = mspi1_kick_and_collect(&mut empty);
}