1use super::PsramSize;
2use crate::peripherals::{EXTMEM, SPI0, SPI1};
3
4const EXTMEM_ORIGIN: usize = 0x3f500000;
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]
33pub(crate) fn init_psram(config: PsramConfig) {
34 let mut config = config;
35 utils::psram_init(&mut config);
36
37 #[allow(unused)]
38 enum CacheLayout {
39 Invalid = 0,
40 ICacheLow = 1 << 0,
41 ICacheHigh = 1 << 1,
42 DCacheLow = 1 << 2,
43 DCacheHigh = 1 << 3,
44 }
45
46 const MMU_ACCESS_SPIRAM: u32 = 1 << 16;
47
48 const CACHE_SIZE_8KB: u32 = 0;
49 const CACHE_4WAYS_ASSOC: u32 = 0;
50 const CACHE_LINE_SIZE_16B: u32 = 0;
51
52 unsafe extern "C" {
53 fn Cache_Allocate_SRAM(
64 sram0_layout: u32,
65 sram1_layout: u32,
66 sram2_layout: u32,
67 sram3_layout: u32,
68 );
69
70 fn cache_dbus_mmu_set(
79 ext_ram: u32,
80 vaddr: u32,
81 paddr: u32,
82 psize: u32,
83 num: u32,
84 fixed: u32,
85 ) -> i32;
86
87 fn Cache_Set_DCache_Mode(cache_size: u32, ways: u32, cache_line_size: u32);
93
94 fn Cache_Invalidate_DCache_All();
96 }
97
98 unsafe {
99 Cache_Allocate_SRAM(
100 CacheLayout::ICacheLow as u32,
101 CacheLayout::DCacheLow as u32,
102 CacheLayout::Invalid as u32,
103 CacheLayout::Invalid as u32,
104 );
105 Cache_Set_DCache_Mode(CACHE_SIZE_8KB, CACHE_4WAYS_ASSOC, CACHE_LINE_SIZE_16B);
106 Cache_Invalidate_DCache_All();
107
108 const START_PAGE: u32 = 0;
109
110 if cache_dbus_mmu_set(
111 MMU_ACCESS_SPIRAM,
112 EXTMEM_ORIGIN as u32,
113 START_PAGE << 16,
114 64,
115 config.size.get() as u32 / 1024 / 64, 0,
117 ) != 0
118 {
119 panic!("cache_dbus_mmu_set failed");
120 }
121
122 EXTMEM::regs().pro_dcache_ctrl1().modify(|_, w| {
123 w.pro_dcache_mask_bus0().clear_bit();
124 w.pro_dcache_mask_bus1().clear_bit();
125 w.pro_dcache_mask_bus2().clear_bit()
126 });
127 }
128
129 unsafe {
130 super::MAPPED_PSRAM.memory_range = EXTMEM_ORIGIN..EXTMEM_ORIGIN + config.size.get();
131 }
132}
133
134pub(crate) mod utils {
135 use super::*;
136
137 const PSRAM_RESET_EN: u16 = 0x66;
138 const PSRAM_RESET: u16 = 0x99;
139 const PSRAM_DEVICE_ID: u16 = 0x9F;
140 const CS_PSRAM_SEL: u8 = 1 << 1;
141
142 #[derive(PartialEq, Eq, Debug, Copy, Clone, Default)]
144 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
145 #[allow(unused)]
146 pub enum PsramVaddrMode {
147 #[default]
149 Normal = 0,
150 Lowhigh,
153 Evenodd,
156 }
157
158 pub(crate) fn psram_init(config: &mut PsramConfig) {
162 psram_gpio_config();
163
164 if config.size.is_auto() {
165 psram_disable_qio_mode();
166
167 let mut dev_id = 0u32;
169 psram_exec_cmd(
170 CommandMode::PsramCmdSpi,
171 PSRAM_DEVICE_ID,
172 8, 0,
174 24, 0, core::ptr::null(),
177 0, &mut dev_id as *mut _ as *mut u8,
179 24, CS_PSRAM_SEL, false,
182 );
183 info!("chip id = {:x}", dev_id);
184
185 let size = if dev_id != 0xffffff {
186 const PSRAM_ID_EID_S: u32 = 16;
187 const PSRAM_ID_EID_M: u32 = 0xff;
188 const PSRAM_EID_SIZE_M: u32 = 0x07;
189 const PSRAM_EID_SIZE_S: u32 = 5;
190
191 let size_id = (((dev_id >> PSRAM_ID_EID_S) & PSRAM_ID_EID_M) >> PSRAM_EID_SIZE_S)
192 & PSRAM_EID_SIZE_M;
193
194 const PSRAM_EID_SIZE_32MBITS: u32 = 1;
195 const PSRAM_EID_SIZE_64MBITS: u32 = 2;
196
197 match size_id {
198 PSRAM_EID_SIZE_64MBITS => 8 * 1024 * 1024,
199 PSRAM_EID_SIZE_32MBITS => 4 * 1024 * 1024,
200 _ => 2 * 1024 * 1024,
201 }
202 } else {
203 0
204 };
205
206 info!("size is {}", size);
207
208 config.size = PsramSize::Size(size);
209 }
210
211 psram_reset_mode();
212 psram_enable_qio_mode();
213
214 psram_cache_init(config.speed, PsramVaddrMode::Normal);
215 }
216
217 fn psram_reset_mode() {
219 psram_exec_cmd(
220 CommandMode::PsramCmdSpi,
221 PSRAM_RESET_EN,
222 8, 0,
224 0, 0, core::ptr::null(),
227 0, core::ptr::null_mut(),
229 0, CS_PSRAM_SEL, false,
232 ); psram_exec_cmd(
235 CommandMode::PsramCmdSpi,
236 PSRAM_RESET,
237 8, 0,
239 0, 0, core::ptr::null(),
242 0, core::ptr::null_mut(),
244 0, CS_PSRAM_SEL, false,
247 ); }
249
250 fn psram_enable_qio_mode() {
252 const PSRAM_ENTER_QMODE: u16 = 0x35;
253 const CS_PSRAM_SEL: u8 = 1 << 1;
254
255 psram_exec_cmd(
256 CommandMode::PsramCmdSpi,
257 PSRAM_ENTER_QMODE,
258 8, 0,
260 0, 0, core::ptr::null(),
263 0, core::ptr::null_mut(),
265 0, CS_PSRAM_SEL, false, );
269 }
270
271 fn psram_disable_qio_mode() {
273 const PSRAM_EXIT_QMODE: u16 = 0xF5;
274 const CS_PSRAM_SEL: u8 = 1 << 1;
275
276 psram_exec_cmd(
277 CommandMode::PsramCmdQpi,
278 PSRAM_EXIT_QMODE,
279 8, 0,
281 0, 0, core::ptr::null(),
284 0, core::ptr::null_mut(),
286 0, CS_PSRAM_SEL, false, );
290 }
291
292 #[derive(PartialEq)]
293 #[allow(unused)]
294 enum CommandMode {
295 PsramCmdQpi = 0,
296 PsramCmdSpi = 1,
297 }
298
299 #[expect(clippy::too_many_arguments)]
300 #[inline(always)]
301 fn psram_exec_cmd(
302 mode: CommandMode,
303 cmd: u16,
304 cmd_bit_len: u16,
305 addr: u32,
306 addr_bit_len: u32,
307 dummy_bits: u32,
308 mosi_data: *const u8,
309 mosi_bit_len: u32,
310 miso_data: *mut u8,
311 miso_bit_len: u32,
312 cs_mask: u8,
313 is_write_erase_operation: bool,
314 ) {
315 unsafe extern "C" {
316 fn esp_rom_spi_cmd_start(
324 spi_num: u32,
325 rx_buf: *const u8,
326 rx_len: u16,
327 cs_en_mask: u8,
328 is_write_erase: bool,
329 );
330 }
331
332 unsafe {
333 let spi1 = SPI1::regs();
334 let backup_usr = spi1.user().read().bits();
335 let backup_usr1 = spi1.user1().read().bits();
336 let backup_usr2 = spi1.user2().read().bits();
337 let backup_ctrl = spi1.ctrl().read().bits();
338 psram_set_op_mode(mode);
339 _psram_exec_cmd(
340 cmd,
341 cmd_bit_len,
342 &addr,
343 addr_bit_len,
344 dummy_bits,
345 mosi_data,
346 mosi_bit_len,
347 miso_data,
348 miso_bit_len,
349 );
350 esp_rom_spi_cmd_start(
351 1,
352 miso_data,
353 (miso_bit_len / 8) as u16,
354 cs_mask,
355 is_write_erase_operation,
356 );
357
358 spi1.user().write(|w| w.bits(backup_usr));
359 spi1.user1().write(|w| w.bits(backup_usr1));
360 spi1.user2().write(|w| w.bits(backup_usr2));
361 spi1.ctrl().write(|w| w.bits(backup_ctrl));
362 }
363 }
364
365 #[expect(clippy::too_many_arguments)]
366 #[inline(always)]
367 fn _psram_exec_cmd(
368 cmd: u16,
369 cmd_bit_len: u16,
370 addr: *const u32,
371 addr_bit_len: u32,
372 dummy_bits: u32,
373 mosi_data: *const u8,
374 mosi_bit_len: u32,
375 miso_data: *mut u8,
376 miso_bit_len: u32,
377 ) {
378 #[repr(C)]
379 #[allow(non_camel_case_types)]
380 struct esp_rom_spi_cmd_t {
381 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,
390 }
391
392 unsafe extern "C" {
393 fn esp_rom_spi_cmd_config(spi_num: u32, pcmd: *const esp_rom_spi_cmd_t);
397 }
398
399 let conf = esp_rom_spi_cmd_t {
400 cmd,
401 cmd_bit_len,
402 addr,
403 addr_bit_len,
404 tx_data: mosi_data as *const u32,
405 tx_data_bit_len: mosi_bit_len,
406 rx_data: miso_data as *mut u32,
407 rx_data_bit_len: miso_bit_len,
408 dummy_bit_len: dummy_bits,
409 };
410
411 unsafe {
412 esp_rom_spi_cmd_config(1, &conf);
413 }
414 }
415
416 fn psram_set_op_mode(mode: CommandMode) {
417 unsafe extern "C" {
418 fn esp_rom_spi_set_op_mode(spi: u32, mode: u32);
419 }
420
421 const ESP_ROM_SPIFLASH_QIO_MODE: u32 = 0;
422 const ESP_ROM_SPIFLASH_SLOWRD_MODE: u32 = 5;
423
424 unsafe {
425 match mode {
426 CommandMode::PsramCmdQpi => {
427 esp_rom_spi_set_op_mode(1, ESP_ROM_SPIFLASH_QIO_MODE);
428 SPI1::regs().ctrl().modify(|_, w| w.fcmd_quad().set_bit());
429 }
430 CommandMode::PsramCmdSpi => {
431 esp_rom_spi_set_op_mode(1, ESP_ROM_SPIFLASH_SLOWRD_MODE);
432 }
433 }
434 }
435 }
436
437 #[repr(C)]
438 struct PsRamIo {
439 flash_clk_io: u8,
440 flash_cs_io: u8,
441 psram_clk_io: u8,
442 psram_cs_io: u8,
443 psram_spiq_sd0_io: u8,
444 psram_spid_sd1_io: u8,
445 psram_spiwp_sd3_io: u8,
446 psram_spihd_sd2_io: u8,
447 }
448
449 fn psram_gpio_config() {
450 unsafe extern "C" {
451 fn esp_rom_efuse_get_flash_gpio_info() -> u32;
452
453 fn esp_rom_spiflash_select_qio_pins(wp_gpio_num: u8, spiconfig: u32);
469 }
470
471 let psram_io = PsRamIo {
472 flash_clk_io: 30, flash_cs_io: 29, psram_clk_io: 30,
475 psram_cs_io: 26, psram_spiq_sd0_io: 31, psram_spid_sd1_io: 32, psram_spiwp_sd3_io: 28, psram_spihd_sd2_io: 27, };
481
482 const ESP_ROM_EFUSE_FLASH_DEFAULT_SPI: u32 = 0;
483
484 unsafe {
485 let spiconfig = esp_rom_efuse_get_flash_gpio_info();
486 if spiconfig == ESP_ROM_EFUSE_FLASH_DEFAULT_SPI {
487 } else {
490 panic!(
492 "Unsupported for now! The case 'FLASH pins are all configured via GPIO matrix in ROM.' is not yet supported."
493 );
494
495 }
508 esp_rom_spiflash_select_qio_pins(psram_io.psram_spiwp_sd3_io, spiconfig);
509 }
511 }
512
513 const PSRAM_IO_MATRIX_DUMMY_20M: u32 = 0;
514 const PSRAM_IO_MATRIX_DUMMY_40M: u32 = 0;
515 const PSRAM_IO_MATRIX_DUMMY_80M: u32 = 0;
516
517 fn psram_cache_init(psram_cache_mode: PsramCacheSpeed, _vaddrmode: PsramVaddrMode) {
519 let mut extra_dummy = 0;
520 match psram_cache_mode {
521 PsramCacheSpeed::PsramCacheS80m => {
522 psram_clock_set(1);
523 extra_dummy = PSRAM_IO_MATRIX_DUMMY_80M;
524 }
525 PsramCacheSpeed::PsramCacheS40m => {
526 psram_clock_set(2);
527 extra_dummy = PSRAM_IO_MATRIX_DUMMY_40M;
528 }
529 PsramCacheSpeed::PsramCacheS26m => {
530 psram_clock_set(3);
531 extra_dummy = PSRAM_IO_MATRIX_DUMMY_20M;
532 }
533 PsramCacheSpeed::PsramCacheS20m => {
534 psram_clock_set(4);
535 extra_dummy = PSRAM_IO_MATRIX_DUMMY_20M;
536 }
537 _ => {
538 psram_clock_set(2);
539 }
540 }
541
542 const PSRAM_QUAD_WRITE: u32 = 0x38;
543 const PSRAM_FAST_READ_QUAD: u32 = 0xEB;
544 const PSRAM_FAST_READ_QUAD_DUMMY: u32 = 0x5;
545
546 unsafe {
547 let spi = SPI0::regs();
548
549 spi.cache_sctrl()
550 .modify(|_, w| w.usr_sram_dio().clear_bit()); spi.cache_sctrl().modify(|_, w| w.usr_sram_qio().set_bit()); spi.cache_sctrl()
555 .modify(|_, w| w.cache_sram_usr_rcmd().set_bit()); spi.cache_sctrl()
558 .modify(|_, w| w.cache_sram_usr_wcmd().set_bit()); spi.cache_sctrl()
562 .modify(|_, w| w.sram_addr_bitlen().bits(23));
563
564 spi.cache_sctrl()
565 .modify(|_, w| w.usr_rd_sram_dummy().set_bit()); spi.sram_dwr_cmd()
569 .modify(|_, w| w.cache_sram_usr_wr_cmd_bitlen().bits(7));
570
571 spi.sram_dwr_cmd().modify(|_, w| {
572 w.cache_sram_usr_wr_cmd_value()
573 .bits(PSRAM_QUAD_WRITE as u16)
574 });
575
576 spi.sram_drd_cmd()
577 .modify(|_, w| w.cache_sram_usr_rd_cmd_bitlen().bits(7));
578
579 spi.sram_drd_cmd().modify(|_, w| {
580 w.cache_sram_usr_rd_cmd_value()
581 .bits(PSRAM_FAST_READ_QUAD as u16)
582 });
583
584 spi.cache_sctrl().modify(|_, w| {
586 w.sram_rdummy_cyclelen()
587 .bits((PSRAM_FAST_READ_QUAD_DUMMY + extra_dummy) as u8)
588 });
589
590 spi.misc().modify(|_, w| w.cs1_dis().clear_bit());
595 }
596 }
597
598 fn psram_clock_set(freqdiv: i8) {
599 const SPI_MEM_SCLKCNT_N_S: u32 = 16;
600 const SPI_MEM_SCLKCNT_H_S: u32 = 8;
601 const SPI_MEM_SCLKCNT_L_S: u32 = 0;
602
603 if 1 >= freqdiv {
604 SPI0::regs()
605 .sram_clk()
606 .modify(|_, w| w.sclk_equ_sysclk().set_bit());
607 } else {
608 let freqbits: u32 = (((freqdiv - 1) as u32) << SPI_MEM_SCLKCNT_N_S)
609 | (((freqdiv / 2 - 1) as u32) << SPI_MEM_SCLKCNT_H_S)
610 | (((freqdiv - 1) as u32) << SPI_MEM_SCLKCNT_L_S);
611 unsafe {
612 SPI0::regs().sram_clk().modify(|_, w| w.bits(freqbits));
613 }
614 }
615 }
616}