esp-hal/esp-hal-common/src/soc/esp32/psram.rs
Dániel Buga 7866896b70
Add defmt support, make log optional (#773)
* Executor related touchups

* Make log optional

* Add defmt feature and derive on Debug structs

* Test both log drivers

* Update esp-println

* Document defmt msrv
2023-09-04 11:29:44 +01:00

1353 lines
50 KiB
Rust

//! # PSRAM "virtual peripheral" driver (ESP32)
//!
//! ## Overview
//!
//! The `PSRAM` module provides support for accessing and controlling
//! the `Pseudo Static Random Access Memory (PSRAM)` on the `ESP32`.
//!
//! The `PSRAM` module enables users to interface with the `PSRAM` memory
//! present on the `ESP32` chip. `PSRAM` provides additional external memory to
//! supplement the internal memory of the `ESP32`, allowing for increased
//! storage capacity and improved performance in certain applications.
//!
//! The `PSRAM` module is accessed through a virtual address, defined as
//! `PSRAM_VADDR`. The starting virtual address for the PSRAM module is
//! 0x3F800000. The `PSRAM` module size depends on the configuration specified
//! during the compilation process. The available `PSRAM` sizes are `2MB`,
//! `4MB`, and `8MB`.
//!
//! NOTE: If you want to use `PSRAM` on `ESP32` or `ESP32-S3`, it'll work only
//! in `release` mode.
const PSRAM_VADDR: u32 = 0x3F800000;
pub fn psram_vaddr_start() -> usize {
PSRAM_VADDR_START
}
cfg_if::cfg_if! {
if #[cfg(feature = "psram_2m")] {
const PSRAM_SIZE: u32 = 2;
} else if #[cfg(feature = "psram_4m")] {
const PSRAM_SIZE: u32 = 4;
} else if #[cfg(feature = "psram_8m")] {
const PSRAM_SIZE: u32 = 8;
} else {
const PSRAM_SIZE: u32 = 0;
}
}
pub const PSRAM_BYTES: usize = PSRAM_SIZE as usize * 1024 * 1024;
pub const PSRAM_VADDR_START: usize = PSRAM_VADDR as usize;
#[cfg(any(feature = "psram_2m", feature = "psram_4m", feature = "psram_8m"))]
pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral<P = crate::peripherals::PSRAM>) {
utils::psram_init();
utils::s_mapping(PSRAM_VADDR, PSRAM_BYTES as u32);
}
#[cfg(any(feature = "psram_2m", feature = "psram_4m", feature = "psram_8m"))]
pub(crate) mod utils {
use procmacros::ram;
pub(crate) fn s_mapping(v_start: u32, size: u32) {
// Enable external RAM in MMU
cache_sram_mmu_set(0, 0, v_start, 0, 32, size / 1024 / 32);
// Flush and enable icache for APP CPU
unsafe {
let dport = &*esp32::DPORT::PTR;
dport
.app_cache_ctrl1
.modify(|_, w| w.app_cache_mask_dram1().clear_bit());
}
cache_sram_mmu_set(1, 0, v_start, 0, 32, size / 1024 / 32);
}
// we can use the ROM version of this: it works well enough and keeps the size
// of the binary down.
fn cache_sram_mmu_set(
cpu_no: u32,
pid: u32,
vaddr: u32,
paddr: u32,
psize: u32,
num: u32,
) -> i32 {
unsafe { cache_sram_mmu_set_rom(cpu_no, pid, vaddr, paddr, psize, num) }
}
// PSRAM clock and cs IO should be configured based on hardware design.
// For ESP32-WROVER or ESP32-WROVER-B module, the clock IO is IO17, the cs IO is
// IO16, they are the default value for these two configs.
const D0WD_PSRAM_CLK_IO: u8 = 17;
const D0WD_PSRAM_CS_IO: u8 = 16;
const D2WD_PSRAM_CLK_IO: u8 = 9; // Default value is 9
const D2WD_PSRAM_CS_IO: u8 = 10; // Default value is 10
// For ESP32-PICO chip, the psram share clock with flash. The flash clock pin is
// fixed, which is IO6.
const PICO_PSRAM_CLK_IO: u8 = 6;
const PICO_PSRAM_CS_IO: u8 = 10; // Default value is 10
const ESP_ROM_EFUSE_FLASH_DEFAULT_SPI: u32 = 0;
const ESP_ROM_EFUSE_FLASH_DEFAULT_HSPI: u32 = 1;
const SPI_IOMUX_PIN_NUM_CLK: u8 = 6;
const SPI_IOMUX_PIN_NUM_CS: u8 = 11;
// IO-pins for PSRAM.
// WARNING: PSRAM shares all but the CS and CLK pins with the flash, so these
// defines hardcode the flash pins as well, making this code incompatible
// with either a setup that has the flash on non-standard pins or ESP32s
// with built-in flash.
const PSRAM_SPIQ_SD0_IO: u8 = 7;
const PSRAM_SPID_SD1_IO: u8 = 8;
const PSRAM_SPIWP_SD3_IO: u8 = 10;
const PSRAM_SPIHD_SD2_IO: u8 = 9;
const FLASH_HSPI_CLK_IO: u8 = 14;
const FLASH_HSPI_CS_IO: u8 = 15;
const PSRAM_HSPI_SPIQ_SD0_IO: u8 = 12;
const PSRAM_HSPI_SPID_SD1_IO: u8 = 13;
const PSRAM_HSPI_SPIWP_SD3_IO: u8 = 2;
const PSRAM_HSPI_SPIHD_SD2_IO: u8 = 4;
const DR_REG_SPI1_BASE: u32 = 0x3ff42000;
const SPI1_USER_REG: u32 = DR_REG_SPI1_BASE + 0x1C;
const SPI1_SLAVE_REG: u32 = DR_REG_SPI1_BASE + 0x038;
const SPI1_PIN_REG: u32 = DR_REG_SPI1_BASE + 0x34;
const SPI1_CTRL_REG: u32 = DR_REG_SPI1_BASE + 0x8;
const SPI1_USER1_REG: u32 = DR_REG_SPI1_BASE + 0x20;
const SPI1_W0_REG: u32 = DR_REG_SPI1_BASE + 0x80;
const SPI1_CTRL2_REG: u32 = DR_REG_SPI1_BASE + 0x14;
const SPI1_CMD_REG: u32 = DR_REG_SPI1_BASE + 0x0;
const SPI1_USER2_REG: u32 = DR_REG_SPI1_BASE + 0x24;
const SPI1_MOSI_DLEN_REG: u32 = DR_REG_SPI1_BASE + 0x28;
const SPI1_MISO_DLEN_REG: u32 = DR_REG_SPI1_BASE + 0x2C;
const SPI1_ADDR_REG: u32 = DR_REG_SPI1_BASE + 0x4;
const DR_REG_SPI0_BASE: u32 = 0x3ff43000;
const SPI0_EXT2_REG: u32 = DR_REG_SPI0_BASE + 0xf8;
const SPI0_EXT3_REG: u32 = DR_REG_SPI0_BASE + 0xfc;
const SPI0_USER_REG: u32 = DR_REG_SPI0_BASE + 0x1C;
const SPI0_PIN_REG: u32 = DR_REG_SPI0_BASE + 0x34;
const SPI0_CTRL_REG: u32 = DR_REG_SPI0_BASE + 0x8;
const SPI0_USER1_REG: u32 = DR_REG_SPI0_BASE + 0x20;
const SPI0_CTRL2_REG: u32 = DR_REG_SPI0_BASE + 0x14;
const SPI0_DATE_REG: u32 = DR_REG_SPI0_BASE + 0x3FC;
const SPI0_CLOCK_REG: u32 = DR_REG_SPI0_BASE + 0x18;
const SPI0_CACHE_SCTRL_REG: u32 = DR_REG_SPI0_BASE + 0x54;
const SPI0_SRAM_DRD_CMD_REG: u32 = DR_REG_SPI0_BASE + 0x5C;
const SPI0_SRAM_DWR_CMD_REG: u32 = DR_REG_SPI0_BASE + 0x60;
const SPI_USR_PREP_HOLD_M: u32 = 1 << 23;
const SPI_TRANS_DONE: u32 = 1 << 4;
const SPI_CK_IDLE_EDGE: u32 = 1 << 29;
const SPI_CK_OUT_EDGE: u32 = 1 << 7;
const SPI_WR_BIT_ORDER: u32 = 1 << 26;
const SPI_RD_BIT_ORDER: u32 = 1 << 25;
const SPI_DOUTDIN: u32 = 1 << 0;
const SPI_SLAVE_MODE: u32 = 1 << 30;
const SPI_CS_HOLD_M: u32 = 1 << 4;
const SPI_CS_SETUP_M: u32 = 1 << 5;
const SPI_HOLD_TIME_V: u32 = 0xF;
const fn psram_cs_hold_time_from_psram_speed(speed: PsramCacheSpeed) -> u32 {
match speed {
PsramCacheSpeed::PsramCacheF80mS40m => 0,
PsramCacheSpeed::PsramCacheF40mS40m => 0,
PsramCacheSpeed::PsramCacheF80mS80m => 1,
}
}
const SPI_HOLD_TIME_S: u32 = 4;
const SPI_SETUP_TIME_V: u32 = 0xF;
const SPI_SETUP_TIME_S: u32 = 0;
const SPI_USR_DUMMY: u32 = 1 << 29;
const PSRAM_INTERNAL_IO_28: u32 = 28;
const PSRAM_INTERNAL_IO_29: u32 = 29;
const SIG_GPIO_OUT_IDX: u32 = 256;
const SPICLK_OUT_IDX: u32 = 0;
const SIG_IN_FUNC224_IDX: u32 = 224;
const SIG_IN_FUNC225_IDX: u32 = 225;
const SPICS0_OUT_IDX: u32 = 5;
const SPICS1_OUT_IDX: u32 = 6;
const SPIQ_OUT_IDX: u32 = 1;
const SPIQ_IN_IDX: u32 = 1;
const SPID_OUT_IDX: u32 = 2;
const SPID_IN_IDX: u32 = 2;
const SPIWP_OUT_IDX: u32 = 4;
const SPIWP_IN_IDX: u32 = 4;
const SPIHD_OUT_IDX: u32 = 3;
const SPIHD_IN_IDX: u32 = 3;
const FUNC_SD_CLK_SPICLK: u32 = 1;
const PIN_FUNC_GPIO: u32 = 2;
const FUN_DRV_V: u32 = 0x3;
const FUN_DRV_S: u32 = 10;
const FUN_DRV: u32 = 0x3;
const SPI_CLK_EQU_SYSCLK_M: u32 = 1 << 31;
const SPI_CLKDIV_PRE_V: u32 = 0x1FFF;
const SPI_CLKDIV_PRE_S: u32 = 18;
const SPI_CLKCNT_N: u32 = 0x0000003F;
const SPI_CLKCNT_N_S: u32 = 12;
const SPI_CLKCNT_H: u32 = 0x0000003F;
const SPI_CLKCNT_H_S: u32 = 6;
const SPI_CLKCNT_L: u32 = 0x0000003F;
const SPI_CLKCNT_L_S: u32 = 0;
const SPI_USR_SRAM_DIO_M: u32 = 1 << 1;
const SPI_USR_SRAM_QIO_M: u32 = 1 << 2;
const SPI_CACHE_SRAM_USR_RCMD_M: u32 = 1 << 5;
const SPI_CACHE_SRAM_USR_WCMD_M: u32 = 1 << 28;
const SPI_SRAM_ADDR_BITLEN_V: u32 = 0x3F;
const SPI_USR_RD_SRAM_DUMMY_M: u32 = 1 << 4;
const SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_V: u32 = 0xF;
const SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_S: u32 = 28;
const SPI_CACHE_SRAM_USR_RD_CMD_VALUE_V: u32 = 0xFFFF;
const SPI_CACHE_SRAM_USR_RD_CMD_VALUE_S: u32 = 0;
const SPI_CACHE_SRAM_USR_WR_CMD_BITLEN: u32 = 0x0000000F;
const SPI_CACHE_SRAM_USR_WR_CMD_BITLEN_S: u32 = 28;
const SPI_CACHE_SRAM_USR_WR_CMD_VALUE: u32 = 0x0000FFFF;
const PSRAM_QUAD_WRITE: u32 = 0x38;
const SPI_CACHE_SRAM_USR_WR_CMD_VALUE_S: u32 = 0;
const SPI_SRAM_DUMMY_CYCLELEN_V: u32 = 0xFF;
const PSRAM_FAST_READ_QUAD_DUMMY: u32 = 0x5;
const SPI_SRAM_DUMMY_CYCLELEN_S: u32 = 14;
const SPI_SRAM_ADDR_BITLEN_S: u32 = 22;
const PSRAM_FAST_READ_QUAD: u32 = 0xEB;
const SPI_USR: u32 = 1 << 18;
const SPI_USR_COMMAND_BITLEN: u32 = 0x0000000F;
const SPI_USR_COMMAND_BITLEN_S: u32 = 28;
const SPI_USR_COMMAND: u32 = 1 << 31;
const SPI_USR_COMMAND_VALUE: u32 = 0x0000FFFF;
const SPI_USR_COMMAND_VALUE_S: u32 = 0;
const SPI_USR_ADDR_BITLEN: u32 = 0x0000003F;
const SPI_USR_ADDR: u32 = 1 << 30;
const SPI_USR_MOSI: u32 = 1 << 27;
const SPI_USR_MISO_DBITLEN: u32 = 0x00FFFFFF;
const SPI_USR_MOSI_DBITLEN: u32 = 0x00FFFFFF;
const SPI_USR_MOSI_DBITLEN_S: u32 = 0;
const SPI_USR_MISO_DBITLEN_S: u32 = 0;
const SPI_USR_MISO: u32 = 1 << 28;
const SPI_FWRITE_DUAL_S: u32 = 12;
const SPI_FWRITE_DUAL_M: u32 = 1 << 12;
const SPI_CS1_DIS_M: u32 = 1 << 1;
const SPI_CS0_DIS_M: u32 = 1 << 0;
const SPI_FWRITE_QIO: u32 = 1 << 15;
const SPI_FWRITE_DIO: u32 = 1 << 14;
const SPI_FWRITE_QUAD: u32 = 1 << 13;
const SPI_FWRITE_DUAL: u32 = 1 << 12;
const SPI_FREAD_QIO: u32 = 1 << 24;
const SPI_FREAD_QUAD: u32 = 1 << 20;
const SPI_FREAD_DUAL: u32 = 1 << 14;
const SPI_FREAD_DIO: u32 = 1 << 23;
const SPI_FREAD_QIO_M: u32 = 1 << 24;
const SPI0_R_QIO_DUMMY_CYCLELEN: u32 = 3;
const SPI_FREAD_DIO_M: u32 = 1 << 23;
const SPI0_R_DIO_DUMMY_CYCLELEN: u32 = 1;
const SPI_USR_ADDR_BITLEN_V: u32 = 0x3F;
const SPI0_R_DIO_ADDR_BITSLEN: u32 = 27;
const SPI_USR_ADDR_BITLEN_S: u32 = 26;
const SPI_FREAD_QUAD_M: u32 = 1 << 20;
const SPI_FREAD_DUAL_M: u32 = 1 << 14;
const SPI0_R_FAST_DUMMY_CYCLELEN: u32 = 7;
const PSRAM_IO_MATRIX_DUMMY_40M: u8 = 1;
const PSRAM_IO_MATRIX_DUMMY_80M: u8 = 2;
const _SPI_CACHE_PORT: u8 = 0;
const _SPI_FLASH_PORT: u8 = 1;
const SPI_USR_DUMMY_CYCLELEN_V: u32 = 0xFF;
const SPI_USR_DUMMY_CYCLELEN_S: u32 = 0;
const _SPI_80M_CLK_DIV: u8 = 1;
const _SPI_40M_CLK_DIV: u8 = 2;
const FLASH_ID_GD25LQ32C: u32 = 0xC86016;
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[allow(unused)]
enum PsramCacheSpeed {
PsramCacheF80mS40m = 0,
PsramCacheF40mS40m,
PsramCacheF80mS80m,
}
#[derive(PartialEq, Eq, Debug, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct PsramIo {
flash_clk_io: u8,
flash_cs_io: u8,
psram_clk_io: u8,
psram_cs_io: u8,
psram_spiq_sd0_io: u8,
psram_spid_sd1_io: u8,
psram_spiwp_sd3_io: u8,
psram_spihd_sd2_io: u8,
}
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
enum PsramClkMode {
PsramClkModeNorm = 0, // Normal SPI mode
PsramClkModeDclk = 1, // Two extra clock cycles after CS is set high level
}
#[repr(C)]
struct EspRomSpiflashChip {
device_id: u32,
chip_size: u32, // chip size in bytes
block_size: u32,
sector_size: u32,
page_size: u32,
status_mask: u32,
}
extern "C" {
fn esp_rom_efuse_get_flash_gpio_info() -> u32;
fn esp_rom_gpio_connect_out_signal(
gpio_num: u32,
signal_idx: u32,
out_inv: bool,
oen_inv: bool,
);
fn esp_rom_gpio_connect_in_signal(gpio_num: u32, signal_idx: u32, inv: bool);
fn esp_rom_spiflash_config_clk(freqdiv: u8, spi: u8) -> i32;
static g_rom_spiflash_dummy_len_plus: u8;
static g_rom_flashchip: EspRomSpiflashChip;
fn cache_sram_mmu_set_rom(
cpu_no: u32,
pid: u32,
vaddr: u32,
paddr: u32,
psize: u32,
num: u32,
) -> i32;
}
pub(crate) fn psram_init() {
let chip = crate::efuse::Efuse::get_chip_type();
let mode = PsramCacheSpeed::PsramCacheF40mS40m; // How to make this configurable
let mut psram_io = PsramIo::default();
let clk_mode;
match chip {
crate::efuse::ChipType::Esp32D0wdq6 | crate::efuse::ChipType::Esp32D0wdq5 => {
clk_mode = PsramClkMode::PsramClkModeNorm;
psram_io.psram_clk_io = D0WD_PSRAM_CLK_IO;
psram_io.psram_cs_io = D0WD_PSRAM_CS_IO;
}
crate::efuse::ChipType::Esp32D2wdq5 => {
clk_mode = PsramClkMode::PsramClkModeDclk;
psram_io.psram_clk_io = D2WD_PSRAM_CLK_IO;
psram_io.psram_cs_io = D2WD_PSRAM_CS_IO;
}
crate::efuse::ChipType::Esp32Picod2 => {
clk_mode = PsramClkMode::PsramClkModeNorm;
psram_io.psram_clk_io = PICO_PSRAM_CLK_IO;
psram_io.psram_cs_io = PICO_PSRAM_CS_IO;
}
crate::efuse::ChipType::Esp32Picod4 => {
panic!("PSRAM is unsupported on this chip");
}
crate::efuse::ChipType::Unknown => {
panic!("Unknown chip type. PSRAM is not supported");
}
}
let spiconfig = unsafe { esp_rom_efuse_get_flash_gpio_info() };
if spiconfig == ESP_ROM_EFUSE_FLASH_DEFAULT_SPI {
psram_io.flash_clk_io = SPI_IOMUX_PIN_NUM_CLK;
psram_io.flash_cs_io = SPI_IOMUX_PIN_NUM_CS;
psram_io.psram_spiq_sd0_io = PSRAM_SPIQ_SD0_IO;
psram_io.psram_spid_sd1_io = PSRAM_SPID_SD1_IO;
psram_io.psram_spiwp_sd3_io = PSRAM_SPIWP_SD3_IO;
psram_io.psram_spihd_sd2_io = PSRAM_SPIHD_SD2_IO;
} else if spiconfig == ESP_ROM_EFUSE_FLASH_DEFAULT_HSPI {
psram_io.flash_clk_io = FLASH_HSPI_CLK_IO;
psram_io.flash_cs_io = FLASH_HSPI_CS_IO;
psram_io.psram_spiq_sd0_io = PSRAM_HSPI_SPIQ_SD0_IO;
psram_io.psram_spid_sd1_io = PSRAM_HSPI_SPID_SD1_IO;
psram_io.psram_spiwp_sd3_io = PSRAM_HSPI_SPIWP_SD3_IO;
psram_io.psram_spihd_sd2_io = PSRAM_HSPI_SPIHD_SD2_IO;
} else {
panic!("Getting Flash/PSRAM pins from efuse is not supported");
// psram_io.flash_clk_io = EFUSE_SPICONFIG_RET_SPICLK(spiconfig);
// psram_io.flash_cs_io = EFUSE_SPICONFIG_RET_SPICS0(spiconfig);
// psram_io.psram_spiq_sd0_io = EFUSE_SPICONFIG_RET_SPIQ(spiconfig);
// psram_io.psram_spid_sd1_io = EFUSE_SPICONFIG_RET_SPID(spiconfig);
// psram_io.psram_spihd_sd2_io =
// EFUSE_SPICONFIG_RET_SPIHD(spiconfig);
// psram_io.psram_spiwp_sd3_io = bootloader_flash_get_wp_pin();
}
info!("PS-RAM pins {:?}", &psram_io);
write_peri_reg(SPI0_EXT3_REG, 0x1);
clear_peri_reg_mask(SPI1_USER_REG, SPI_USR_PREP_HOLD_M);
psram_spi_init(mode, clk_mode);
match mode {
PsramCacheSpeed::PsramCacheF80mS80m => unsafe {
esp_rom_gpio_connect_out_signal(
psram_io.psram_clk_io as u32,
SPICLK_OUT_IDX,
false,
false,
);
},
_ => unsafe {
if clk_mode == PsramClkMode::PsramClkModeDclk {
// We need to delay CLK to the PSRAM with respect to the clock signal as output
// by the SPI peripheral. We do this by routing it signal to
// signal 224/225, which are used as a loopback; the extra run through
// the GPIO matrix causes the delay. We use GPIO20 (which is not in any package
// but has pad logic in silicon) as a temporary pad for
// this. So the signal path is: SPI CLK --> GPIO28 -->
// signal224(in then out) --> internal GPIO29 --> signal225(in then out) -->
// GPIO17(PSRAM CLK)
esp_rom_gpio_connect_out_signal(
PSRAM_INTERNAL_IO_28,
SPICLK_OUT_IDX,
false,
false,
);
esp_rom_gpio_connect_in_signal(PSRAM_INTERNAL_IO_28, SIG_IN_FUNC224_IDX, false);
esp_rom_gpio_connect_out_signal(
PSRAM_INTERNAL_IO_29,
SIG_IN_FUNC224_IDX,
false,
false,
);
esp_rom_gpio_connect_in_signal(PSRAM_INTERNAL_IO_29, SIG_IN_FUNC225_IDX, false);
esp_rom_gpio_connect_out_signal(
psram_io.psram_clk_io as u32,
SIG_IN_FUNC225_IDX,
false,
false,
);
} else {
esp_rom_gpio_connect_out_signal(
psram_io.psram_clk_io as u32,
SPICLK_OUT_IDX,
false,
false,
);
}
},
}
let extra_dummy = psram_gpio_config(&psram_io, mode);
// psram_is_32mbit_ver0 would need special handling here
unsafe {
esp_rom_gpio_connect_out_signal(PSRAM_INTERNAL_IO_28, SIG_GPIO_OUT_IDX, false, false);
esp_rom_gpio_connect_out_signal(PSRAM_INTERNAL_IO_29, SIG_GPIO_OUT_IDX, false, false);
esp_rom_gpio_connect_out_signal(
psram_io.psram_clk_io as u32,
SPICLK_OUT_IDX,
false,
false,
);
}
// Update cs timing according to psram driving method.
psram_set_cs_timing_spi1(mode, clk_mode);
psram_set_cs_timing_spi0(mode, clk_mode); // SPI_CACHE_PORT
psram_enable_qio_mode_spi1(clk_mode, mode);
info!(
"PS-RAM vaddrmode = {:?}",
PsramVaddrMode::PsramVaddrModeLowhigh
);
psram_cache_init(
mode,
PsramVaddrMode::PsramVaddrModeLowhigh,
clk_mode,
extra_dummy,
);
}
#[allow(unused)]
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum PsramVaddrMode {
/// App and pro CPU use their own flash cache for external RAM access
PsramVaddrModeNormal = 0,
/// App and pro CPU share external RAM caches: pro CPU has low * 2M, app
/// CPU has high 2M
PsramVaddrModeLowhigh,
/// App and pro CPU share external RAM caches: pro CPU does even 32yte
/// ranges, app does odd ones.
PsramVaddrModeEvenodd,
}
// register initialization for sram cache params and r/w commands
fn psram_cache_init(
psram_cache_mode: PsramCacheSpeed,
vaddrmode: PsramVaddrMode,
clk_mode: PsramClkMode,
extra_dummy: u32,
) {
info!(
"PS-RAM cache_init, psram_cache_mode={:?}, extra_dummy={}, clk_mode={:?}",
psram_cache_mode, extra_dummy, clk_mode
);
match psram_cache_mode {
PsramCacheSpeed::PsramCacheF80mS80m => {
// flash 1 div clk,80+40;
clear_peri_reg_mask(SPI0_DATE_REG, 1 << 31);
// pre clk div , ONLY IF SPI/ SRAM@ DIFFERENT SPEED,JUST FOR SPI0. FLASH DIV
// 2+SRAM DIV4
clear_peri_reg_mask(SPI0_DATE_REG, 1 << 30);
}
PsramCacheSpeed::PsramCacheF80mS40m => {
clear_peri_reg_mask(SPI0_CLOCK_REG, SPI_CLK_EQU_SYSCLK_M);
set_peri_reg_bits(SPI0_CLOCK_REG, SPI_CLKDIV_PRE_V, 0, SPI_CLKDIV_PRE_S);
set_peri_reg_bits(SPI0_CLOCK_REG, SPI_CLKCNT_N, 1, SPI_CLKCNT_N_S);
set_peri_reg_bits(SPI0_CLOCK_REG, SPI_CLKCNT_H, 0, SPI_CLKCNT_H_S);
set_peri_reg_bits(SPI0_CLOCK_REG, SPI_CLKCNT_L, 1, SPI_CLKCNT_L_S);
// flash 1 div clk
set_peri_reg_mask(SPI0_DATE_REG, 1 << 31);
// pre clk div ONLY IF SPI/SRAM@ DIFFERENT SPEED,JUST FOR SPI0.
clear_peri_reg_mask(SPI0_DATE_REG, 1 << 30);
}
_ => {
clear_peri_reg_mask(SPI0_DATE_REG, 1 << 31); // flash 1 div clk
clear_peri_reg_mask(SPI0_DATE_REG, 1 << 30); // pre clk div
}
}
clear_peri_reg_mask(SPI0_CACHE_SCTRL_REG, SPI_USR_SRAM_DIO_M); // disable dio mode for cache command
set_peri_reg_mask(SPI0_CACHE_SCTRL_REG, SPI_USR_SRAM_QIO_M); // enable qio mode for cache command
set_peri_reg_mask(SPI0_CACHE_SCTRL_REG, SPI_CACHE_SRAM_USR_RCMD_M); // enable cache read command
set_peri_reg_mask(SPI0_CACHE_SCTRL_REG, SPI_CACHE_SRAM_USR_WCMD_M); // enable cache write command
set_peri_reg_bits(
SPI0_CACHE_SCTRL_REG,
SPI_SRAM_ADDR_BITLEN_V,
23,
SPI_SRAM_ADDR_BITLEN_S,
); // write address for cache command.
set_peri_reg_mask(SPI0_CACHE_SCTRL_REG, SPI_USR_RD_SRAM_DUMMY_M); // enable cache read dummy
// config sram cache r/w command
set_peri_reg_bits(
SPI0_SRAM_DRD_CMD_REG,
SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_V,
7,
SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_S,
);
set_peri_reg_bits(
SPI0_SRAM_DRD_CMD_REG,
SPI_CACHE_SRAM_USR_RD_CMD_VALUE_V,
PSRAM_FAST_READ_QUAD,
SPI_CACHE_SRAM_USR_RD_CMD_VALUE_S,
); // 0xEB
set_peri_reg_bits(
SPI0_SRAM_DWR_CMD_REG,
SPI_CACHE_SRAM_USR_WR_CMD_BITLEN,
7,
SPI_CACHE_SRAM_USR_WR_CMD_BITLEN_S,
);
set_peri_reg_bits(
SPI0_SRAM_DWR_CMD_REG,
SPI_CACHE_SRAM_USR_WR_CMD_VALUE,
PSRAM_QUAD_WRITE,
SPI_CACHE_SRAM_USR_WR_CMD_VALUE_S,
); // 0x38
set_peri_reg_bits(
SPI0_CACHE_SCTRL_REG,
SPI_SRAM_DUMMY_CYCLELEN_V,
PSRAM_FAST_READ_QUAD_DUMMY + extra_dummy,
SPI_SRAM_DUMMY_CYCLELEN_S,
); // dummy, psram cache : 40m--+1dummy; 80m--+2dummy
match psram_cache_mode {
PsramCacheSpeed::PsramCacheF80mS80m => (), // in this mode , no delay is needed
_ => {
if clk_mode == PsramClkMode::PsramClkModeDclk {
set_peri_reg_bits(
SPI0_SRAM_DRD_CMD_REG,
SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_V,
15,
SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_S,
); // read command length, 2 bytes(1byte for delay),sending in qio mode in cache
set_peri_reg_bits(
SPI0_SRAM_DRD_CMD_REG,
SPI_CACHE_SRAM_USR_RD_CMD_VALUE_V,
(PSRAM_FAST_READ_QUAD) << 8,
SPI_CACHE_SRAM_USR_RD_CMD_VALUE_S,
); // 0xEB, read command value,(0x00 for delay,0xeb for cmd)
set_peri_reg_bits(
SPI0_SRAM_DWR_CMD_REG,
SPI_CACHE_SRAM_USR_WR_CMD_BITLEN,
15,
SPI_CACHE_SRAM_USR_WR_CMD_BITLEN_S,
); // write command length,2 bytes(1byte for delay,send in qio mode in cache)
set_peri_reg_bits(
SPI0_SRAM_DWR_CMD_REG,
SPI_CACHE_SRAM_USR_WR_CMD_VALUE,
(PSRAM_QUAD_WRITE) << 8,
SPI_CACHE_SRAM_USR_WR_CMD_VALUE_S,
); // 0x38, write command value,(0x00 for delay)
set_peri_reg_bits(
SPI0_CACHE_SCTRL_REG,
SPI_SRAM_DUMMY_CYCLELEN_V,
PSRAM_FAST_READ_QUAD_DUMMY + extra_dummy,
SPI_SRAM_DUMMY_CYCLELEN_S,
); // dummy, psram cache : 40m--+1dummy; 80m--+2dummy
}
}
}
unsafe {
let dport = &*esp32::DPORT::PTR;
dport
.pro_cache_ctrl
.modify(|_, w| w.pro_dram_hl().clear_bit().pro_dram_split().clear_bit());
dport
.app_cache_ctrl
.modify(|_, w| w.app_dram_hl().clear_bit().app_dram_split().clear_bit());
if vaddrmode == PsramVaddrMode::PsramVaddrModeLowhigh {
dport
.pro_cache_ctrl
.modify(|_, w| w.pro_dram_hl().set_bit());
dport
.app_cache_ctrl
.modify(|_, w| w.app_dram_hl().set_bit());
} else if vaddrmode == PsramVaddrMode::PsramVaddrModeEvenodd {
dport
.pro_cache_ctrl
.modify(|_, w| w.pro_dram_split().set_bit());
dport
.app_cache_ctrl
.modify(|_, w| w.app_dram_split().set_bit());
}
// use Dram1 to visit ext sram. cache page mode : 1 -->16k 4 -->2k
// 0-->32k,(accord with the settings in cache_sram_mmu_set)
dport.pro_cache_ctrl1.modify(|_, w| {
w.pro_cache_mask_dram1()
.clear_bit()
.pro_cache_mask_opsdram()
.clear_bit()
});
dport
.pro_cache_ctrl1
.modify(|_, w| w.pro_cmmu_sram_page_mode().variant(0));
// use Dram1 to visit ext sram. cache page mode : 1 -->16k 4 -->2k
// 0-->32k,(accord with the settings in cache_sram_mmu_set)
dport.app_cache_ctrl1.modify(|_, w| {
w.app_cache_mask_dram1()
.clear_bit()
.app_cache_mask_opsdram()
.clear_bit()
});
dport
.app_cache_ctrl1
.modify(|_, w| w.app_cmmu_sram_page_mode().variant(0));
}
// ENABLE SPI0 CS1 TO PSRAM(CS0--FLASH; CS1--SRAM)
clear_peri_reg_mask(SPI0_PIN_REG, SPI_CS1_DIS_M);
}
// spi param init for psram
fn psram_spi_init(
// psram_spi_num_t spi_num = PSRAM_SPI_1,
mode: PsramCacheSpeed,
clk_mode: PsramClkMode,
) {
clear_peri_reg_mask(SPI1_SLAVE_REG, SPI_TRANS_DONE << 5);
// SPI_CPOL & SPI_CPHA
clear_peri_reg_mask(SPI1_PIN_REG, SPI_CK_IDLE_EDGE);
clear_peri_reg_mask(SPI1_USER_REG, SPI_CK_OUT_EDGE);
// SPI bit order
clear_peri_reg_mask(SPI1_CTRL_REG, SPI_WR_BIT_ORDER);
clear_peri_reg_mask(SPI1_CTRL_REG, SPI_RD_BIT_ORDER);
// SPI bit order
clear_peri_reg_mask(SPI1_USER_REG, SPI_DOUTDIN);
// May be not must to do.
write_peri_reg(SPI1_USER1_REG, 0);
// SPI mode type
clear_peri_reg_mask(SPI1_SLAVE_REG, SPI_SLAVE_MODE);
unsafe {
let ptr = SPI1_W0_REG as *mut u32;
for i in 0..16 {
ptr.offset(i).write_volatile(0);
}
}
psram_set_cs_timing_spi1(mode, clk_mode);
}
fn psram_set_cs_timing_spi1(psram_cache_mode: PsramCacheSpeed, clk_mode: PsramClkMode) {
if clk_mode == PsramClkMode::PsramClkModeNorm {
set_peri_reg_mask(SPI1_USER_REG, SPI_CS_HOLD_M | SPI_CS_SETUP_M);
// Set cs time.
set_peri_reg_bits(
SPI1_CTRL2_REG,
SPI_HOLD_TIME_V,
psram_cs_hold_time_from_psram_speed(psram_cache_mode),
SPI_HOLD_TIME_S,
);
set_peri_reg_bits(SPI1_CTRL2_REG, SPI_SETUP_TIME_V, 0, SPI_SETUP_TIME_S);
} else {
clear_peri_reg_mask(SPI1_USER_REG, SPI_CS_HOLD_M | SPI_CS_SETUP_M);
}
}
fn psram_set_cs_timing_spi0(psram_cache_mode: PsramCacheSpeed, clk_mode: PsramClkMode) {
if clk_mode == PsramClkMode::PsramClkModeNorm {
set_peri_reg_mask(SPI0_USER_REG, SPI_CS_HOLD_M | SPI_CS_SETUP_M);
// Set cs time.
set_peri_reg_bits(
SPI0_CTRL2_REG,
SPI_HOLD_TIME_V,
psram_cs_hold_time_from_psram_speed(psram_cache_mode),
SPI_HOLD_TIME_S,
);
set_peri_reg_bits(SPI0_CTRL2_REG, SPI_SETUP_TIME_V, 0, SPI_SETUP_TIME_S);
} else {
clear_peri_reg_mask(SPI0_USER_REG, SPI_CS_HOLD_M | SPI_CS_SETUP_M);
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
struct PsramCmd {
cmd: u16, // Command value
cmd_bit_len: u16, // Command byte length
addr: *const u32, // Point to address value
addr_bit_len: u16, // Address byte length
tx_data: *const u32, // Point to send data buffer
tx_data_bit_len: u16, // Send data byte length.
rx_data: *mut u32, // Point to recevie data buffer
rx_data_bit_len: u16, // Recevie Data byte length.
dummy_bit_len: u32,
}
impl Default for PsramCmd {
fn default() -> Self {
Self {
cmd: Default::default(),
cmd_bit_len: Default::default(),
addr: core::ptr::null(),
addr_bit_len: Default::default(),
tx_data: core::ptr::null(),
tx_data_bit_len: Default::default(),
rx_data: core::ptr::null_mut(),
rx_data_bit_len: Default::default(),
dummy_bit_len: Default::default(),
}
}
}
const PSRAM_ENTER_QMODE: u32 = 0x35;
// enter QPI mode
#[ram]
fn psram_enable_qio_mode_spi1(clk_mode: PsramClkMode, psram_mode: PsramCacheSpeed) {
let mut ps_cmd: PsramCmd = PsramCmd::default();
let addr: u32 = (PSRAM_ENTER_QMODE << 24) | 0;
ps_cmd.cmd_bit_len = 0;
if clk_mode == PsramClkMode::PsramClkModeDclk {
match psram_mode {
PsramCacheSpeed::PsramCacheF80mS80m => (),
_ => {
ps_cmd.cmd_bit_len = 2;
}
}
}
ps_cmd.cmd = 0;
ps_cmd.addr = &addr;
ps_cmd.addr_bit_len = 8;
ps_cmd.tx_data = core::ptr::null();
ps_cmd.tx_data_bit_len = 0;
ps_cmd.rx_data = core::ptr::null_mut();
ps_cmd.rx_data_bit_len = 0;
ps_cmd.dummy_bit_len = 0;
let (backup_usr, backup_usr1, backup_usr2) = psram_cmd_config_spi1(&ps_cmd);
psram_cmd_recv_start_spi1(core::ptr::null_mut(), 0, PsramCmdMode::PsramCmdSpi);
psram_cmd_end_spi1(backup_usr, backup_usr1, backup_usr2);
}
#[ram]
fn psram_cmd_end_spi1(backup_usr: u32, backup_usr1: u32, backup_usr2: u32) {
loop {
if read_peri_reg(SPI1_CMD_REG) & SPI_USR == 0 {
break;
}
}
write_peri_reg(SPI1_USER_REG, backup_usr);
write_peri_reg(SPI1_USER1_REG, backup_usr1);
write_peri_reg(SPI1_USER2_REG, backup_usr2);
}
// setup spi command/addr/data/dummy in user mode
#[ram]
fn psram_cmd_config_spi1(p_in_data: &PsramCmd) -> (u32, u32, u32) {
loop {
if read_peri_reg(SPI1_CMD_REG) & SPI_USR == 0 {
break;
}
}
let backup_usr = read_peri_reg(SPI1_USER_REG);
let backup_usr1 = read_peri_reg(SPI1_USER1_REG);
let backup_usr2 = read_peri_reg(SPI1_USER2_REG);
// Set command by user.
if p_in_data.cmd_bit_len != 0 {
// Max command length 16 bits.
set_peri_reg_bits(
SPI1_USER2_REG,
SPI_USR_COMMAND_BITLEN,
(p_in_data.cmd_bit_len - 1) as u32,
SPI_USR_COMMAND_BITLEN_S,
);
// Enable command
set_peri_reg_mask(SPI1_USER_REG, SPI_USR_COMMAND);
// Load command,bit15-0 is cmd value.
set_peri_reg_bits(
SPI1_USER2_REG,
SPI_USR_COMMAND_VALUE,
p_in_data.cmd as u32,
SPI_USR_COMMAND_VALUE_S,
);
} else {
clear_peri_reg_mask(SPI1_USER_REG, SPI_USR_COMMAND);
set_peri_reg_bits(
SPI1_USER2_REG,
SPI_USR_COMMAND_BITLEN,
0,
SPI_USR_COMMAND_BITLEN_S,
);
}
// Set Address by user.
if p_in_data.addr_bit_len != 0 {
set_peri_reg_bits(
SPI1_USER1_REG,
SPI_USR_ADDR_BITLEN,
(p_in_data.addr_bit_len - 1) as u32,
SPI_USR_ADDR_BITLEN_S,
);
// Enable address
set_peri_reg_mask(SPI1_USER_REG, SPI_USR_ADDR);
// Set address
write_peri_reg(SPI1_ADDR_REG, unsafe { p_in_data.addr.read_volatile() });
} else {
clear_peri_reg_mask(SPI1_USER_REG, SPI_USR_ADDR);
set_peri_reg_bits(
SPI1_USER1_REG,
SPI_USR_ADDR_BITLEN,
0,
SPI_USR_ADDR_BITLEN_S,
);
}
// Set data by user.
let p_tx_val = p_in_data.tx_data;
if p_in_data.tx_data_bit_len != 0 {
// Enable MOSI
clear_peri_reg_mask(SPI1_USER_REG, SPI_USR_MOSI);
// Load send buffer
let len = (p_in_data.tx_data_bit_len + 31) / 32;
if !p_tx_val.is_null() {
for i in 0..len {
write_peri_reg(SPI1_W0_REG, unsafe {
p_tx_val.offset(i as isize).read_volatile()
});
}
}
// Set data send buffer length.Max data length 64 bytes.
set_peri_reg_bits(
SPI1_MOSI_DLEN_REG,
SPI_USR_MOSI_DBITLEN,
(p_in_data.tx_data_bit_len - 1) as u32,
SPI_USR_MOSI_DBITLEN_S,
);
} else {
clear_peri_reg_mask(SPI1_USER_REG, SPI_USR_MOSI);
set_peri_reg_bits(
SPI1_MOSI_DLEN_REG,
SPI_USR_MOSI_DBITLEN,
0,
SPI_USR_MOSI_DBITLEN_S,
);
}
// Set rx data by user.
if p_in_data.rx_data_bit_len != 0 {
// Enable MOSI
set_peri_reg_mask(SPI1_USER_REG, SPI_USR_MISO);
// Set data send buffer length.Max data length 64 bytes.
set_peri_reg_bits(
SPI1_MISO_DLEN_REG,
SPI_USR_MISO_DBITLEN,
(p_in_data.rx_data_bit_len - 1) as u32,
SPI_USR_MISO_DBITLEN_S,
);
} else {
clear_peri_reg_mask(SPI1_USER_REG, SPI_USR_MISO);
set_peri_reg_bits(
SPI1_MISO_DLEN_REG,
SPI_USR_MISO_DBITLEN,
0,
SPI_USR_MISO_DBITLEN_S,
);
}
if p_in_data.dummy_bit_len != 0 {
set_peri_reg_mask(SPI1_USER_REG, SPI_USR_DUMMY); // dummy en
set_peri_reg_bits(
SPI1_USER1_REG,
SPI_USR_DUMMY_CYCLELEN_V,
p_in_data.dummy_bit_len - 1,
SPI_USR_DUMMY_CYCLELEN_S,
); // DUMMY
} else {
clear_peri_reg_mask(SPI1_USER_REG, SPI_USR_DUMMY); // dummy en
set_peri_reg_bits(
SPI1_USER1_REG,
SPI_USR_DUMMY_CYCLELEN_V,
0,
SPI_USR_DUMMY_CYCLELEN_S,
); // DUMMY
}
(backup_usr, backup_usr1, backup_usr2)
}
#[derive(Debug, Clone, Copy, PartialEq)]
enum PsramCmdMode {
PsramCmdQpi,
PsramCmdSpi,
}
// start sending cmd/addr and optionally, receiving data
#[ram]
fn psram_cmd_recv_start_spi1(p_rx_data: *mut u32, rx_byte_len: u16, cmd_mode: PsramCmdMode) {
// get cs1
clear_peri_reg_mask(SPI1_PIN_REG, SPI_CS1_DIS_M);
set_peri_reg_mask(SPI1_PIN_REG, SPI_CS0_DIS_M);
let mode_backup: u32 = (read_peri_reg(SPI1_USER_REG) >> SPI_FWRITE_DUAL_S) & 0xf;
let rd_mode_backup: u32 = read_peri_reg(SPI1_CTRL_REG)
& (SPI_FREAD_DIO_M | SPI_FREAD_DUAL_M | SPI_FREAD_QUAD_M | SPI_FREAD_QIO_M);
if cmd_mode == PsramCmdMode::PsramCmdSpi {
psram_set_basic_write_mode_spi1();
psram_set_basic_read_mode_spi1();
} else if cmd_mode == PsramCmdMode::PsramCmdQpi {
psram_set_qio_write_mode_spi1();
psram_set_qio_read_mode_spi1();
}
// Wait for SPI0 to idle
loop {
if read_peri_reg(SPI0_EXT2_REG) == 0 {
break;
}
}
unsafe {
// DPORT_SET_PERI_REG_MASK(DPORT_HOST_INF_SEL_REG, 1 << 14);
let dport = &*esp32::DPORT::PTR;
dport.host_inf_sel.modify(|r, w| w.bits(r.bits() | 1 << 14));
}
// Start send data
set_peri_reg_mask(SPI1_CMD_REG, SPI_USR);
loop {
if read_peri_reg(SPI1_CMD_REG) & SPI_USR == 0 {
break;
}
}
unsafe {
// DPORT_CLEAR_PERI_REG_MASK(DPORT_HOST_INF_SEL_REG, 1 << 14);
let dport = &*esp32::DPORT::PTR;
dport
.host_inf_sel
.modify(|r, w| w.bits(r.bits() & !(1 << 14)));
}
// recover spi mode
set_peri_reg_bits(
SPI1_USER_REG,
if !p_rx_data.is_null() {
SPI_FWRITE_DUAL_M
} else {
0xf
},
mode_backup,
SPI_FWRITE_DUAL_S,
);
clear_peri_reg_mask(
SPI1_CTRL_REG,
SPI_FREAD_DIO_M | SPI_FREAD_DUAL_M | SPI_FREAD_QUAD_M | SPI_FREAD_QIO_M,
);
set_peri_reg_mask(SPI1_CTRL_REG, rd_mode_backup);
// return cs to cs0
set_peri_reg_mask(SPI1_PIN_REG, SPI_CS1_DIS_M);
clear_peri_reg_mask(SPI1_PIN_REG, SPI_CS0_DIS_M);
if !p_rx_data.is_null() {
let mut idx = 0;
// Read data out
loop {
unsafe {
p_rx_data
.offset(idx)
.write_volatile(read_peri_reg(SPI1_W0_REG + ((idx as u32) << 2)));
}
idx += 1;
if idx > ((rx_byte_len / 4) + if (rx_byte_len % 4) != 0 { 1 } else { 0 }) as isize {
break;
}
}
}
}
// set basic SPI write mode
fn psram_set_basic_write_mode_spi1() {
clear_peri_reg_mask(SPI1_USER_REG, SPI_FWRITE_QIO);
clear_peri_reg_mask(SPI1_USER_REG, SPI_FWRITE_DIO);
clear_peri_reg_mask(SPI1_USER_REG, SPI_FWRITE_QUAD);
clear_peri_reg_mask(SPI1_USER_REG, SPI_FWRITE_DUAL);
}
// set QPI write mode
fn psram_set_qio_write_mode_spi1() {
set_peri_reg_mask(SPI1_USER_REG, SPI_FWRITE_QIO);
clear_peri_reg_mask(SPI1_USER_REG, SPI_FWRITE_DIO);
clear_peri_reg_mask(SPI1_USER_REG, SPI_FWRITE_QUAD);
clear_peri_reg_mask(SPI1_USER_REG, SPI_FWRITE_DUAL);
}
// set QPI read mode
fn psram_set_qio_read_mode_spi1() {
set_peri_reg_mask(SPI1_CTRL_REG, SPI_FREAD_QIO);
clear_peri_reg_mask(SPI1_CTRL_REG, SPI_FREAD_QUAD);
clear_peri_reg_mask(SPI1_CTRL_REG, SPI_FREAD_DUAL);
clear_peri_reg_mask(SPI1_CTRL_REG, SPI_FREAD_DIO);
}
// set SPI read mode
fn psram_set_basic_read_mode_spi1() {
clear_peri_reg_mask(SPI1_CTRL_REG, SPI_FREAD_QIO);
clear_peri_reg_mask(SPI1_CTRL_REG, SPI_FREAD_QUAD);
clear_peri_reg_mask(SPI1_CTRL_REG, SPI_FREAD_DUAL);
clear_peri_reg_mask(SPI1_CTRL_REG, SPI_FREAD_DIO);
}
// psram gpio init , different working frequency we have different solutions
fn psram_gpio_config(psram_io: &PsramIo, mode: PsramCacheSpeed) -> u32 {
let g_rom_spiflash_dummy_len_plus_ptr: *mut u8 =
unsafe { core::mem::transmute(&g_rom_spiflash_dummy_len_plus) };
fn gpio_pin_mux_reg(gpio: u8) -> u32 {
crate::gpio::get_io_mux_reg(gpio).as_ptr() as u32
}
fn gpio_hal_iomux_func_sel(reg: u32, function: u32) {
unsafe {
let ptr = reg as *mut u32;
let old = ptr.read_volatile();
ptr.write_volatile((old & !(0b111 << 12)) | (function << 12));
}
}
let spi_cache_dummy;
let rd_mode_reg = read_peri_reg(SPI0_CTRL_REG);
if (rd_mode_reg & SPI_FREAD_QIO_M) != 0 {
spi_cache_dummy = SPI0_R_QIO_DUMMY_CYCLELEN;
} else if (rd_mode_reg & SPI_FREAD_DIO_M) != 0 {
spi_cache_dummy = SPI0_R_DIO_DUMMY_CYCLELEN;
set_peri_reg_bits(
SPI0_USER1_REG,
SPI_USR_ADDR_BITLEN_V,
SPI0_R_DIO_ADDR_BITSLEN,
SPI_USR_ADDR_BITLEN_S,
);
} else if (rd_mode_reg & (SPI_FREAD_QUAD_M | SPI_FREAD_DUAL_M)) != 0 {
spi_cache_dummy = SPI0_R_FAST_DUMMY_CYCLELEN;
} else {
spi_cache_dummy = SPI0_R_FAST_DUMMY_CYCLELEN;
}
let extra_dummy;
match mode {
PsramCacheSpeed::PsramCacheF80mS40m => {
extra_dummy = PSRAM_IO_MATRIX_DUMMY_40M;
unsafe {
g_rom_spiflash_dummy_len_plus_ptr
.offset(_SPI_CACHE_PORT as isize)
.write_volatile(PSRAM_IO_MATRIX_DUMMY_80M);
g_rom_spiflash_dummy_len_plus_ptr
.offset(_SPI_FLASH_PORT as isize)
.write_volatile(PSRAM_IO_MATRIX_DUMMY_40M);
}
set_peri_reg_bits(
SPI0_USER1_REG,
SPI_USR_DUMMY_CYCLELEN_V,
spi_cache_dummy + PSRAM_IO_MATRIX_DUMMY_80M as u32,
SPI_USR_DUMMY_CYCLELEN_S,
); // DUMMY
unsafe {
esp_rom_spiflash_config_clk(_SPI_80M_CLK_DIV, _SPI_CACHE_PORT);
esp_rom_spiflash_config_clk(_SPI_40M_CLK_DIV, _SPI_FLASH_PORT);
}
// set drive ability for clock
set_peri_reg_bits(
gpio_pin_mux_reg(psram_io.flash_clk_io),
FUN_DRV,
3,
FUN_DRV_S,
);
set_peri_reg_bits(
gpio_pin_mux_reg(psram_io.psram_clk_io),
FUN_DRV,
2,
FUN_DRV_S,
);
}
PsramCacheSpeed::PsramCacheF80mS80m => {
extra_dummy = PSRAM_IO_MATRIX_DUMMY_80M;
unsafe {
g_rom_spiflash_dummy_len_plus_ptr
.offset(_SPI_CACHE_PORT as isize)
.write_volatile(PSRAM_IO_MATRIX_DUMMY_80M);
g_rom_spiflash_dummy_len_plus_ptr
.offset(_SPI_FLASH_PORT as isize)
.write_volatile(PSRAM_IO_MATRIX_DUMMY_80M);
}
set_peri_reg_bits(
SPI0_USER1_REG,
SPI_USR_DUMMY_CYCLELEN_V,
spi_cache_dummy + PSRAM_IO_MATRIX_DUMMY_80M as u32,
SPI_USR_DUMMY_CYCLELEN_S,
); // DUMMY
unsafe {
esp_rom_spiflash_config_clk(_SPI_80M_CLK_DIV, _SPI_CACHE_PORT);
esp_rom_spiflash_config_clk(_SPI_80M_CLK_DIV, _SPI_FLASH_PORT);
}
// set drive ability for clock
set_peri_reg_bits(
gpio_pin_mux_reg(psram_io.flash_clk_io),
FUN_DRV,
3,
FUN_DRV_S,
);
set_peri_reg_bits(
gpio_pin_mux_reg(psram_io.psram_clk_io),
FUN_DRV,
3,
FUN_DRV_S,
);
}
PsramCacheSpeed::PsramCacheF40mS40m => {
extra_dummy = PSRAM_IO_MATRIX_DUMMY_40M;
unsafe {
g_rom_spiflash_dummy_len_plus_ptr
.offset(_SPI_CACHE_PORT as isize)
.write_volatile(PSRAM_IO_MATRIX_DUMMY_40M);
g_rom_spiflash_dummy_len_plus_ptr
.offset(_SPI_FLASH_PORT as isize)
.write_volatile(PSRAM_IO_MATRIX_DUMMY_40M);
}
set_peri_reg_bits(
SPI0_USER1_REG,
SPI_USR_DUMMY_CYCLELEN_V,
spi_cache_dummy + PSRAM_IO_MATRIX_DUMMY_40M as u32,
SPI_USR_DUMMY_CYCLELEN_S,
); // DUMMY
unsafe {
esp_rom_spiflash_config_clk(_SPI_40M_CLK_DIV, _SPI_CACHE_PORT);
esp_rom_spiflash_config_clk(_SPI_40M_CLK_DIV, _SPI_FLASH_PORT);
}
// set drive ability for clock
set_peri_reg_bits(
gpio_pin_mux_reg(psram_io.flash_clk_io),
FUN_DRV,
2,
FUN_DRV_S,
);
set_peri_reg_bits(
gpio_pin_mux_reg(psram_io.psram_clk_io),
FUN_DRV,
2,
FUN_DRV_S,
);
}
}
set_peri_reg_mask(SPI0_USER_REG, SPI_USR_DUMMY); // dummy enable
// In bootloader, all the signals are already configured,
// We keep the following code in case the bootloader is some older version.
unsafe {
esp_rom_gpio_connect_out_signal(
psram_io.flash_cs_io as u32,
SPICS0_OUT_IDX,
false,
false,
);
esp_rom_gpio_connect_out_signal(
psram_io.psram_cs_io as u32,
SPICS1_OUT_IDX,
false,
false,
);
esp_rom_gpio_connect_out_signal(
psram_io.psram_spiq_sd0_io as u32,
SPIQ_OUT_IDX,
false,
false,
);
esp_rom_gpio_connect_in_signal(psram_io.psram_spiq_sd0_io as u32, SPIQ_IN_IDX, false);
esp_rom_gpio_connect_out_signal(
psram_io.psram_spid_sd1_io as u32,
SPID_OUT_IDX,
false,
false,
);
esp_rom_gpio_connect_in_signal(psram_io.psram_spid_sd1_io as u32, SPID_IN_IDX, false);
esp_rom_gpio_connect_out_signal(
psram_io.psram_spiwp_sd3_io as u32,
SPIWP_OUT_IDX,
false,
false,
);
esp_rom_gpio_connect_in_signal(psram_io.psram_spiwp_sd3_io as u32, SPIWP_IN_IDX, false);
esp_rom_gpio_connect_out_signal(
psram_io.psram_spihd_sd2_io as u32,
SPIHD_OUT_IDX,
false,
false,
);
esp_rom_gpio_connect_in_signal(psram_io.psram_spihd_sd2_io as u32, SPIHD_IN_IDX, false);
}
// select pin function gpio
if (psram_io.flash_clk_io == SPI_IOMUX_PIN_NUM_CLK)
&& (psram_io.flash_clk_io != psram_io.psram_clk_io)
{
// flash clock signal should come from IO MUX.
gpio_hal_iomux_func_sel(gpio_pin_mux_reg(psram_io.flash_clk_io), FUNC_SD_CLK_SPICLK);
} else {
// flash clock signal should come from GPIO matrix.
gpio_hal_iomux_func_sel(gpio_pin_mux_reg(psram_io.flash_clk_io), PIN_FUNC_GPIO);
}
gpio_hal_iomux_func_sel(gpio_pin_mux_reg(psram_io.flash_cs_io), PIN_FUNC_GPIO);
gpio_hal_iomux_func_sel(gpio_pin_mux_reg(psram_io.psram_cs_io), PIN_FUNC_GPIO);
gpio_hal_iomux_func_sel(gpio_pin_mux_reg(psram_io.psram_clk_io), PIN_FUNC_GPIO);
gpio_hal_iomux_func_sel(gpio_pin_mux_reg(psram_io.psram_spiq_sd0_io), PIN_FUNC_GPIO);
gpio_hal_iomux_func_sel(gpio_pin_mux_reg(psram_io.psram_spid_sd1_io), PIN_FUNC_GPIO);
gpio_hal_iomux_func_sel(gpio_pin_mux_reg(psram_io.psram_spihd_sd2_io), PIN_FUNC_GPIO);
gpio_hal_iomux_func_sel(gpio_pin_mux_reg(psram_io.psram_spiwp_sd3_io), PIN_FUNC_GPIO);
let flash_id: u32 = unsafe { g_rom_flashchip.device_id };
info!("Flash-ID = {}", flash_id);
if flash_id == FLASH_ID_GD25LQ32C {
// Set drive ability for 1.8v flash in 80Mhz.
set_peri_reg_bits(
gpio_pin_mux_reg(psram_io.flash_cs_io),
FUN_DRV_V,
3,
FUN_DRV_S,
);
set_peri_reg_bits(
gpio_pin_mux_reg(psram_io.flash_clk_io),
FUN_DRV_V,
3,
FUN_DRV_S,
);
set_peri_reg_bits(
gpio_pin_mux_reg(psram_io.psram_cs_io),
FUN_DRV_V,
3,
FUN_DRV_S,
);
set_peri_reg_bits(
gpio_pin_mux_reg(psram_io.psram_clk_io),
FUN_DRV_V,
3,
FUN_DRV_S,
);
set_peri_reg_bits(
gpio_pin_mux_reg(psram_io.psram_spiq_sd0_io),
FUN_DRV_V,
3,
FUN_DRV_S,
);
set_peri_reg_bits(
gpio_pin_mux_reg(psram_io.psram_spid_sd1_io),
FUN_DRV_V,
3,
FUN_DRV_S,
);
set_peri_reg_bits(
gpio_pin_mux_reg(psram_io.psram_spihd_sd2_io),
FUN_DRV_V,
3,
FUN_DRV_S,
);
set_peri_reg_bits(
gpio_pin_mux_reg(psram_io.psram_spiwp_sd3_io),
FUN_DRV_V,
3,
FUN_DRV_S,
);
}
extra_dummy as u32
}
fn clear_peri_reg_mask(reg: u32, mask: u32) {
unsafe {
(reg as *mut u32).write_volatile((reg as *mut u32).read_volatile() & !mask);
}
}
fn set_peri_reg_mask(reg: u32, mask: u32) {
unsafe {
(reg as *mut u32).write_volatile((reg as *mut u32).read_volatile() | mask);
}
}
fn set_peri_reg_bits(reg: u32, bitmap: u32, value: u32, shift: u32) {
unsafe {
(reg as *mut u32).write_volatile(
((reg as *mut u32).read_volatile() & !(bitmap << shift))
| ((value & bitmap) << shift),
);
}
}
fn write_peri_reg(reg: u32, val: u32) {
unsafe {
(reg as *mut u32).write_volatile(val);
}
}
fn read_peri_reg(reg: u32) -> u32 {
unsafe { (reg as *mut u32).read_volatile() }
}
}