1use core::ops::Range;
2
3use super::{EXTMEM_ORIGIN, PsramSize};
4use crate::peripherals::EXTMEM;
5
6#[derive(Copy, Clone, Debug, Default, PartialEq)]
8#[cfg_attr(feature = "defmt", derive(defmt::Format))]
9pub enum SpiRamFreq {
10 Freq20m = 20,
12 Freq26m = 26,
14 Freq40m = 40,
16 #[default]
18 Freq80m = 80,
19}
20
21impl SpiRamFreq {
22 fn divider(self) -> u32 {
23 match self {
24 Self::Freq80m => 1,
25 Self::Freq40m => 2,
26 Self::Freq26m => 3,
27 Self::Freq20m => 4,
28 }
29 }
30}
31
32#[derive(Copy, Clone, Debug, Default, PartialEq)]
34#[cfg_attr(feature = "defmt", derive(defmt::Format))]
35pub struct PsramConfig {
36 pub size: PsramSize,
38 pub ram_frequency: SpiRamFreq,
40}
41
42#[procmacros::ram]
44pub(crate) fn init_psram(config: &mut PsramConfig) -> bool {
45 let success = quad_spi_impl::psram_init(config);
46 if !success {
47 warn!(
48 "Failed to configure PSRAM. This may indicate a missing/inoperable PSRAM chip, or an incorrect PSRAM configuration. Check if the PSRAM chip is present and the configuration is correct."
49 );
50 }
51 success
52}
53
54#[procmacros::ram]
55pub(crate) fn map_psram(config: PsramConfig) -> Range<usize> {
56 const MMU_ACCESS_SPIRAM: u32 = 1 << 16;
57
58 unsafe extern "C" {
59 fn cache_dbus_mmu_set(
68 ext_ram: u32,
69 vaddr: u32,
70 paddr: u32,
71 psize: u32,
72 num: u32,
73 fixed: u32,
74 ) -> i32;
75 }
76
77 const START_PAGE: u32 = 0;
78
79 let cache_dbus_mmu_set_res = unsafe {
80 cache_dbus_mmu_set(
81 MMU_ACCESS_SPIRAM,
82 EXTMEM_ORIGIN as u32,
83 START_PAGE << 16,
84 64,
85 config.size.get() as u32 / 1024 / 64,
86 0,
87 )
88 };
89
90 if cache_dbus_mmu_set_res != 0 {
91 panic!("cache_dbus_mmu_set failed");
92 }
93
94 EXTMEM::regs().pro_dcache_ctrl1().modify(|_, w| {
95 w.pro_dcache_mask_bus0().clear_bit();
96 w.pro_dcache_mask_bus1().clear_bit();
97 w.pro_dcache_mask_bus2().clear_bit()
98 });
99
100 EXTMEM_ORIGIN..EXTMEM_ORIGIN + config.size.get()
101}
102
103pub(crate) mod quad_spi_impl {
104 use procmacros::ram;
105
106 use super::*;
107 use crate::psram::quad_xtensa;
108
109 #[ram]
110 pub(crate) fn psram_init(config: &mut PsramConfig) -> bool {
111 psram_gpio_config();
112
113 if config.size.is_auto() {
114 let Some(size) = quad_xtensa::detect_quad_size() else {
115 return false;
116 };
117 config.size = PsramSize::Size(size);
118 }
119
120 quad_xtensa::psram_reset_mode_spi1();
121 quad_xtensa::psram_enable_qio_mode_spi1();
122 quad_xtensa::config_psram_spi_phases();
123 mspi_timing_enter_high_speed_mode(config);
124
125 info!("PSRAM initialized successfully in Quad SPI mode");
126 true
127 }
128
129 #[ram]
130 fn mspi_timing_enter_high_speed_mode(config: &PsramConfig) {
131 let psram_div = config.ram_frequency.divider();
132 info!(
133 "PSRAM {} MHz, psram_div = {}",
134 config.ram_frequency as u32, psram_div
135 );
136 quad_xtensa::spi0_timing_config_set_psram_clock(psram_div);
137 }
138
139 #[repr(C)]
140 struct PsRamIo {
141 flash_clk_io: u8,
142 flash_cs_io: u8,
143 psram_clk_io: u8,
144 psram_cs_io: u8,
145 psram_spiq_sd0_io: u8,
146 psram_spid_sd1_io: u8,
147 psram_spiwp_sd3_io: u8,
148 psram_spihd_sd2_io: u8,
149 }
150
151 #[ram]
152 fn psram_gpio_config() {
153 unsafe extern "C" {
154 fn esp_rom_efuse_get_flash_gpio_info() -> u32;
155
156 fn esp_rom_spiflash_select_qio_pins(wp_gpio_num: u8, spiconfig: u32);
172 }
173
174 let psram_io = PsRamIo {
175 flash_clk_io: 30, flash_cs_io: 29, psram_clk_io: 30,
178 psram_cs_io: 26, psram_spiq_sd0_io: 31, psram_spid_sd1_io: 32, psram_spiwp_sd3_io: 28, psram_spihd_sd2_io: 27, };
184
185 const ESP_ROM_EFUSE_FLASH_DEFAULT_SPI: u32 = 0;
186
187 unsafe {
188 let spiconfig = esp_rom_efuse_get_flash_gpio_info();
189 if spiconfig == ESP_ROM_EFUSE_FLASH_DEFAULT_SPI {
190 } else {
193 panic!(
194 "Unsupported for now! The case 'FLASH pins are all configured via GPIO matrix in ROM.' is not yet supported."
195 );
196 }
197 esp_rom_spiflash_select_qio_pins(psram_io.psram_spiwp_sd3_io, spiconfig);
198 }
199 }
200}