1use core::ops::Range;
2
3use super::{EXTMEM_ORIGIN, PsramSize};
4use crate::peripherals::{EXTMEM, SPI0, SPI1};
5
6#[derive(PartialEq, Eq, Debug, Copy, Clone, Default)]
8#[cfg_attr(feature = "defmt", derive(defmt::Format))]
9#[allow(missing_docs)]
10pub enum PsramCacheSpeed {
11 PsramCacheS80m = 1,
12 PsramCacheS40m,
13 PsramCacheS26m,
14 PsramCacheS20m,
15 #[default]
16 PsramCacheMax,
17}
18
19#[derive(Copy, Clone, Debug, Default)]
21#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22pub struct PsramConfig {
23 pub size: PsramSize,
25 pub speed: PsramCacheSpeed,
27}
28
29#[procmacros::ram]
31pub(crate) fn init_psram(config: &mut PsramConfig) -> bool {
32 utils::psram_init(config)
33}
34
35#[procmacros::ram]
36pub(crate) fn map_psram(config: PsramConfig) -> Range<usize> {
37 const MMU_ACCESS_SPIRAM: u32 = 1 << 16;
38
39 unsafe extern "C" {
40 fn cache_dbus_mmu_set(
49 ext_ram: u32,
50 vaddr: u32,
51 paddr: u32,
52 psize: u32,
53 num: u32,
54 fixed: u32,
55 ) -> i32;
56 }
57
58 const START_PAGE: u32 = 0;
59
60 let cache_dbus_mmu_set_res = unsafe {
61 cache_dbus_mmu_set(
62 MMU_ACCESS_SPIRAM,
63 EXTMEM_ORIGIN as u32,
64 START_PAGE << 16,
65 64,
66 config.size.get() as u32 / 1024 / 64, 0,
68 )
69 };
70
71 if cache_dbus_mmu_set_res != 0 {
72 panic!("cache_dbus_mmu_set failed");
73 }
74
75 EXTMEM::regs().pro_dcache_ctrl1().modify(|_, w| {
76 w.pro_dcache_mask_bus0().clear_bit();
77 w.pro_dcache_mask_bus1().clear_bit();
78 w.pro_dcache_mask_bus2().clear_bit()
79 });
80
81 EXTMEM_ORIGIN..EXTMEM_ORIGIN + config.size.get()
82}
83
84pub(crate) mod utils {
85 use super::*;
86
87 const PSRAM_RESET_EN: u16 = 0x66;
88 const PSRAM_RESET: u16 = 0x99;
89 const PSRAM_DEVICE_ID: u16 = 0x9F;
90 const CS_PSRAM_SEL: u8 = 1 << 1;
91
92 #[derive(PartialEq, Eq, Debug, Copy, Clone, Default)]
94 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
95 #[allow(unused)]
96 pub enum PsramVaddrMode {
97 #[default]
99 Normal = 0,
100 Lowhigh,
103 Evenodd,
106 }
107
108 pub(crate) fn psram_init(config: &mut PsramConfig) -> bool {
112 psram_gpio_config();
113
114 if config.size.is_auto() {
115 psram_disable_qio_mode();
116
117 let mut dev_id = 0u32;
119 psram_exec_cmd(
120 CommandMode::PsramCmdSpi,
121 PSRAM_DEVICE_ID,
122 8, 0,
124 24, 0, core::ptr::null(),
127 0, &mut dev_id as *mut _ as *mut u8,
129 24, CS_PSRAM_SEL, false,
132 );
133
134 if dev_id == 0xffffff {
135 debug!(
136 "Unknown PSRAM chip ID: {:x}. PSRAM chip not found or not supported.",
137 dev_id
138 );
139 return false;
140 }
141
142 info!("chip id = {:x}", dev_id);
143
144 const PSRAM_ID_EID_S: u32 = 16;
145 const PSRAM_ID_EID_M: u32 = 0xff;
146 const PSRAM_EID_SIZE_M: u32 = 0x07;
147 const PSRAM_EID_SIZE_S: u32 = 5;
148
149 let size_id = (((dev_id >> PSRAM_ID_EID_S) & PSRAM_ID_EID_M) >> PSRAM_EID_SIZE_S)
150 & PSRAM_EID_SIZE_M;
151
152 const PSRAM_EID_SIZE_32MBITS: u32 = 1;
153 const PSRAM_EID_SIZE_64MBITS: u32 = 2;
154
155 let size = match size_id {
156 PSRAM_EID_SIZE_64MBITS => 8 * 1024 * 1024,
157 PSRAM_EID_SIZE_32MBITS => 4 * 1024 * 1024,
158 _ => 2 * 1024 * 1024,
159 };
160
161 info!("size is {}", size);
162
163 config.size = PsramSize::Size(size);
164 }
165
166 psram_reset_mode();
167 psram_enable_qio_mode();
168
169 psram_cache_init(config.speed, PsramVaddrMode::Normal);
170
171 true
172 }
173
174 fn psram_reset_mode() {
176 psram_exec_cmd(
177 CommandMode::PsramCmdSpi,
178 PSRAM_RESET_EN,
179 8, 0,
181 0, 0, core::ptr::null(),
184 0, core::ptr::null_mut(),
186 0, CS_PSRAM_SEL, false,
189 ); psram_exec_cmd(
192 CommandMode::PsramCmdSpi,
193 PSRAM_RESET,
194 8, 0,
196 0, 0, core::ptr::null(),
199 0, core::ptr::null_mut(),
201 0, CS_PSRAM_SEL, false,
204 ); }
206
207 fn psram_enable_qio_mode() {
209 const PSRAM_ENTER_QMODE: u16 = 0x35;
210 const CS_PSRAM_SEL: u8 = 1 << 1;
211
212 psram_exec_cmd(
213 CommandMode::PsramCmdSpi,
214 PSRAM_ENTER_QMODE,
215 8, 0,
217 0, 0, core::ptr::null(),
220 0, core::ptr::null_mut(),
222 0, CS_PSRAM_SEL, false, );
226 }
227
228 fn psram_disable_qio_mode() {
230 const PSRAM_EXIT_QMODE: u16 = 0xF5;
231 const CS_PSRAM_SEL: u8 = 1 << 1;
232
233 psram_exec_cmd(
234 CommandMode::PsramCmdQpi,
235 PSRAM_EXIT_QMODE,
236 8, 0,
238 0, 0, core::ptr::null(),
241 0, core::ptr::null_mut(),
243 0, CS_PSRAM_SEL, false, );
247 }
248
249 #[derive(PartialEq)]
250 #[allow(unused)]
251 enum CommandMode {
252 PsramCmdQpi = 0,
253 PsramCmdSpi = 1,
254 }
255
256 #[expect(clippy::too_many_arguments)]
257 #[inline(always)]
258 fn psram_exec_cmd(
259 mode: CommandMode,
260 cmd: u16,
261 cmd_bit_len: u16,
262 addr: u32,
263 addr_bit_len: u32,
264 dummy_bits: u32,
265 mosi_data: *const u8,
266 mosi_bit_len: u32,
267 miso_data: *mut u8,
268 miso_bit_len: u32,
269 cs_mask: u8,
270 is_write_erase_operation: bool,
271 ) {
272 unsafe extern "C" {
273 fn esp_rom_spi_cmd_start(
281 spi_num: u32,
282 rx_buf: *const u8,
283 rx_len: u16,
284 cs_en_mask: u8,
285 is_write_erase: bool,
286 );
287 }
288
289 unsafe {
290 let spi1 = SPI1::regs();
291 let backup_usr = spi1.user().read().bits();
292 let backup_usr1 = spi1.user1().read().bits();
293 let backup_usr2 = spi1.user2().read().bits();
294 let backup_ctrl = spi1.ctrl().read().bits();
295 psram_set_op_mode(mode);
296 _psram_exec_cmd(
297 cmd,
298 cmd_bit_len,
299 &addr,
300 addr_bit_len,
301 dummy_bits,
302 mosi_data,
303 mosi_bit_len,
304 miso_data,
305 miso_bit_len,
306 );
307 esp_rom_spi_cmd_start(
308 1,
309 miso_data,
310 (miso_bit_len / 8) as u16,
311 cs_mask,
312 is_write_erase_operation,
313 );
314
315 spi1.user().write(|w| w.bits(backup_usr));
316 spi1.user1().write(|w| w.bits(backup_usr1));
317 spi1.user2().write(|w| w.bits(backup_usr2));
318 spi1.ctrl().write(|w| w.bits(backup_ctrl));
319 }
320 }
321
322 #[expect(clippy::too_many_arguments)]
323 #[inline(always)]
324 fn _psram_exec_cmd(
325 cmd: u16,
326 cmd_bit_len: u16,
327 addr: *const u32,
328 addr_bit_len: u32,
329 dummy_bits: u32,
330 mosi_data: *const u8,
331 mosi_bit_len: u32,
332 miso_data: *mut u8,
333 miso_bit_len: u32,
334 ) {
335 #[repr(C)]
336 #[allow(non_camel_case_types)]
337 struct esp_rom_spi_cmd_t {
338 cmd: u16, cmd_bit_len: u16, addr: *const u32, addr_bit_len: u32, tx_data: *const u32, tx_data_bit_len: u32, rx_data: *mut u32, rx_data_bit_len: u32, dummy_bit_len: u32,
347 }
348
349 unsafe extern "C" {
350 fn esp_rom_spi_cmd_config(spi_num: u32, pcmd: *const esp_rom_spi_cmd_t);
354 }
355
356 let conf = esp_rom_spi_cmd_t {
357 cmd,
358 cmd_bit_len,
359 addr,
360 addr_bit_len,
361 tx_data: mosi_data as *const u32,
362 tx_data_bit_len: mosi_bit_len,
363 rx_data: miso_data as *mut u32,
364 rx_data_bit_len: miso_bit_len,
365 dummy_bit_len: dummy_bits,
366 };
367
368 unsafe {
369 esp_rom_spi_cmd_config(1, &conf);
370 }
371 }
372
373 fn psram_set_op_mode(mode: CommandMode) {
374 unsafe extern "C" {
375 fn esp_rom_spi_set_op_mode(spi: u32, mode: u32);
376 }
377
378 const ESP_ROM_SPIFLASH_QIO_MODE: u32 = 0;
379 const ESP_ROM_SPIFLASH_SLOWRD_MODE: u32 = 5;
380
381 unsafe {
382 match mode {
383 CommandMode::PsramCmdQpi => {
384 esp_rom_spi_set_op_mode(1, ESP_ROM_SPIFLASH_QIO_MODE);
385 SPI1::regs().ctrl().modify(|_, w| w.fcmd_quad().set_bit());
386 }
387 CommandMode::PsramCmdSpi => {
388 esp_rom_spi_set_op_mode(1, ESP_ROM_SPIFLASH_SLOWRD_MODE);
389 }
390 }
391 }
392 }
393
394 #[repr(C)]
395 struct PsRamIo {
396 flash_clk_io: u8,
397 flash_cs_io: u8,
398 psram_clk_io: u8,
399 psram_cs_io: u8,
400 psram_spiq_sd0_io: u8,
401 psram_spid_sd1_io: u8,
402 psram_spiwp_sd3_io: u8,
403 psram_spihd_sd2_io: u8,
404 }
405
406 fn psram_gpio_config() {
407 unsafe extern "C" {
408 fn esp_rom_efuse_get_flash_gpio_info() -> u32;
409
410 fn esp_rom_spiflash_select_qio_pins(wp_gpio_num: u8, spiconfig: u32);
426 }
427
428 let psram_io = PsRamIo {
429 flash_clk_io: 30, flash_cs_io: 29, psram_clk_io: 30,
432 psram_cs_io: 26, psram_spiq_sd0_io: 31, psram_spid_sd1_io: 32, psram_spiwp_sd3_io: 28, psram_spihd_sd2_io: 27, };
438
439 const ESP_ROM_EFUSE_FLASH_DEFAULT_SPI: u32 = 0;
440
441 unsafe {
442 let spiconfig = esp_rom_efuse_get_flash_gpio_info();
443 if spiconfig == ESP_ROM_EFUSE_FLASH_DEFAULT_SPI {
444 } else {
447 panic!(
449 "Unsupported for now! The case 'FLASH pins are all configured via GPIO matrix in ROM.' is not yet supported."
450 );
451
452 }
465 esp_rom_spiflash_select_qio_pins(psram_io.psram_spiwp_sd3_io, spiconfig);
466 }
468 }
469
470 const PSRAM_IO_MATRIX_DUMMY_20M: u32 = 0;
471 const PSRAM_IO_MATRIX_DUMMY_40M: u32 = 0;
472 const PSRAM_IO_MATRIX_DUMMY_80M: u32 = 0;
473
474 fn psram_cache_init(psram_cache_mode: PsramCacheSpeed, _vaddrmode: PsramVaddrMode) {
476 let mut extra_dummy = 0;
477 match psram_cache_mode {
478 PsramCacheSpeed::PsramCacheS80m => {
479 psram_clock_set(1);
480 extra_dummy = PSRAM_IO_MATRIX_DUMMY_80M;
481 }
482 PsramCacheSpeed::PsramCacheS40m => {
483 psram_clock_set(2);
484 extra_dummy = PSRAM_IO_MATRIX_DUMMY_40M;
485 }
486 PsramCacheSpeed::PsramCacheS26m => {
487 psram_clock_set(3);
488 extra_dummy = PSRAM_IO_MATRIX_DUMMY_20M;
489 }
490 PsramCacheSpeed::PsramCacheS20m => {
491 psram_clock_set(4);
492 extra_dummy = PSRAM_IO_MATRIX_DUMMY_20M;
493 }
494 _ => {
495 psram_clock_set(2);
496 }
497 }
498
499 const PSRAM_QUAD_WRITE: u32 = 0x38;
500 const PSRAM_FAST_READ_QUAD: u32 = 0xEB;
501 const PSRAM_FAST_READ_QUAD_DUMMY: u32 = 0x5;
502
503 unsafe {
504 let spi = SPI0::regs();
505
506 spi.cache_sctrl()
507 .modify(|_, w| w.usr_sram_dio().clear_bit()); spi.cache_sctrl().modify(|_, w| w.usr_sram_qio().set_bit()); spi.cache_sctrl()
512 .modify(|_, w| w.cache_sram_usr_rcmd().set_bit()); spi.cache_sctrl()
515 .modify(|_, w| w.cache_sram_usr_wcmd().set_bit()); spi.cache_sctrl()
519 .modify(|_, w| w.sram_addr_bitlen().bits(23));
520
521 spi.cache_sctrl()
522 .modify(|_, w| w.usr_rd_sram_dummy().set_bit()); spi.sram_dwr_cmd()
526 .modify(|_, w| w.cache_sram_usr_wr_cmd_bitlen().bits(7));
527
528 spi.sram_dwr_cmd().modify(|_, w| {
529 w.cache_sram_usr_wr_cmd_value()
530 .bits(PSRAM_QUAD_WRITE as u16)
531 });
532
533 spi.sram_drd_cmd()
534 .modify(|_, w| w.cache_sram_usr_rd_cmd_bitlen().bits(7));
535
536 spi.sram_drd_cmd().modify(|_, w| {
537 w.cache_sram_usr_rd_cmd_value()
538 .bits(PSRAM_FAST_READ_QUAD as u16)
539 });
540
541 spi.cache_sctrl().modify(|_, w| {
543 w.sram_rdummy_cyclelen()
544 .bits((PSRAM_FAST_READ_QUAD_DUMMY + extra_dummy) as u8)
545 });
546
547 spi.misc().modify(|_, w| w.cs1_dis().clear_bit());
552 }
553 }
554
555 fn psram_clock_set(freqdiv: i8) {
556 const SPI_MEM_SCLKCNT_N_S: u32 = 16;
557 const SPI_MEM_SCLKCNT_H_S: u32 = 8;
558 const SPI_MEM_SCLKCNT_L_S: u32 = 0;
559
560 if 1 >= freqdiv {
561 SPI0::regs()
562 .sram_clk()
563 .modify(|_, w| w.sclk_equ_sysclk().set_bit());
564 } else {
565 let freqbits: u32 = (((freqdiv - 1) as u32) << SPI_MEM_SCLKCNT_N_S)
566 | (((freqdiv / 2 - 1) as u32) << SPI_MEM_SCLKCNT_H_S)
567 | (((freqdiv - 1) as u32) << SPI_MEM_SCLKCNT_L_S);
568 unsafe {
569 SPI0::regs().sram_clk().modify(|_, w| w.bits(freqbits));
570 }
571 }
572 }
573}