* Executor related touchups * Make log optional * Add defmt feature and derive on Debug structs * Test both log drivers * Update esp-println * Document defmt msrv
1353 lines
50 KiB
Rust
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() }
|
|
}
|
|
}
|