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 true
34}
35
36#[procmacros::ram]
37pub(crate) fn map_psram(config: PsramConfig) -> Range<usize> {
38 const MMU_ACCESS_SPIRAM: u32 = 1 << 16;
39
40 unsafe extern "C" {
41 fn cache_dbus_mmu_set(
50 ext_ram: u32,
51 vaddr: u32,
52 paddr: u32,
53 psize: u32,
54 num: u32,
55 fixed: u32,
56 ) -> i32;
57 }
58
59 const START_PAGE: u32 = 0;
60
61 let cache_dbus_mmu_set_res = unsafe {
62 cache_dbus_mmu_set(
63 MMU_ACCESS_SPIRAM,
64 EXTMEM_ORIGIN as u32,
65 START_PAGE << 16,
66 64,
67 config.size.get() as u32 / 1024 / 64, 0,
69 )
70 };
71
72 if cache_dbus_mmu_set_res != 0 {
73 panic!("cache_dbus_mmu_set failed");
74 }
75
76 EXTMEM::regs().pro_dcache_ctrl1().modify(|_, w| {
77 w.pro_dcache_mask_bus0().clear_bit();
78 w.pro_dcache_mask_bus1().clear_bit();
79 w.pro_dcache_mask_bus2().clear_bit()
80 });
81
82 EXTMEM_ORIGIN..EXTMEM_ORIGIN + config.size.get()
83}
84
85pub(crate) mod utils {
86 use super::*;
87
88 const PSRAM_RESET_EN: u16 = 0x66;
89 const PSRAM_RESET: u16 = 0x99;
90 const PSRAM_DEVICE_ID: u16 = 0x9F;
91 const CS_PSRAM_SEL: u8 = 1 << 1;
92
93 #[derive(PartialEq, Eq, Debug, Copy, Clone, Default)]
95 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
96 #[allow(unused)]
97 pub enum PsramVaddrMode {
98 #[default]
100 Normal = 0,
101 Lowhigh,
104 Evenodd,
107 }
108
109 pub(crate) fn psram_init(config: &mut PsramConfig) {
113 psram_gpio_config();
114
115 if config.size.is_auto() {
116 psram_disable_qio_mode();
117
118 let mut dev_id = 0u32;
120 psram_exec_cmd(
121 CommandMode::PsramCmdSpi,
122 PSRAM_DEVICE_ID,
123 8, 0,
125 24, 0, core::ptr::null(),
128 0, &mut dev_id as *mut _ as *mut u8,
130 24, CS_PSRAM_SEL, false,
133 );
134 info!("chip id = {:x}", dev_id);
135
136 let size = if dev_id != 0xffffff {
137 const PSRAM_ID_EID_S: u32 = 16;
138 const PSRAM_ID_EID_M: u32 = 0xff;
139 const PSRAM_EID_SIZE_M: u32 = 0x07;
140 const PSRAM_EID_SIZE_S: u32 = 5;
141
142 let size_id = (((dev_id >> PSRAM_ID_EID_S) & PSRAM_ID_EID_M) >> PSRAM_EID_SIZE_S)
143 & PSRAM_EID_SIZE_M;
144
145 const PSRAM_EID_SIZE_32MBITS: u32 = 1;
146 const PSRAM_EID_SIZE_64MBITS: u32 = 2;
147
148 match size_id {
149 PSRAM_EID_SIZE_64MBITS => 8 * 1024 * 1024,
150 PSRAM_EID_SIZE_32MBITS => 4 * 1024 * 1024,
151 _ => 2 * 1024 * 1024,
152 }
153 } else {
154 0
155 };
156
157 info!("size is {}", size);
158
159 config.size = PsramSize::Size(size);
160 }
161
162 psram_reset_mode();
163 psram_enable_qio_mode();
164
165 psram_cache_init(config.speed, PsramVaddrMode::Normal);
166 }
167
168 fn psram_reset_mode() {
170 psram_exec_cmd(
171 CommandMode::PsramCmdSpi,
172 PSRAM_RESET_EN,
173 8, 0,
175 0, 0, core::ptr::null(),
178 0, core::ptr::null_mut(),
180 0, CS_PSRAM_SEL, false,
183 ); psram_exec_cmd(
186 CommandMode::PsramCmdSpi,
187 PSRAM_RESET,
188 8, 0,
190 0, 0, core::ptr::null(),
193 0, core::ptr::null_mut(),
195 0, CS_PSRAM_SEL, false,
198 ); }
200
201 fn psram_enable_qio_mode() {
203 const PSRAM_ENTER_QMODE: u16 = 0x35;
204 const CS_PSRAM_SEL: u8 = 1 << 1;
205
206 psram_exec_cmd(
207 CommandMode::PsramCmdSpi,
208 PSRAM_ENTER_QMODE,
209 8, 0,
211 0, 0, core::ptr::null(),
214 0, core::ptr::null_mut(),
216 0, CS_PSRAM_SEL, false, );
220 }
221
222 fn psram_disable_qio_mode() {
224 const PSRAM_EXIT_QMODE: u16 = 0xF5;
225 const CS_PSRAM_SEL: u8 = 1 << 1;
226
227 psram_exec_cmd(
228 CommandMode::PsramCmdQpi,
229 PSRAM_EXIT_QMODE,
230 8, 0,
232 0, 0, core::ptr::null(),
235 0, core::ptr::null_mut(),
237 0, CS_PSRAM_SEL, false, );
241 }
242
243 #[derive(PartialEq)]
244 #[allow(unused)]
245 enum CommandMode {
246 PsramCmdQpi = 0,
247 PsramCmdSpi = 1,
248 }
249
250 #[expect(clippy::too_many_arguments)]
251 #[inline(always)]
252 fn psram_exec_cmd(
253 mode: CommandMode,
254 cmd: u16,
255 cmd_bit_len: u16,
256 addr: u32,
257 addr_bit_len: u32,
258 dummy_bits: u32,
259 mosi_data: *const u8,
260 mosi_bit_len: u32,
261 miso_data: *mut u8,
262 miso_bit_len: u32,
263 cs_mask: u8,
264 is_write_erase_operation: bool,
265 ) {
266 unsafe extern "C" {
267 fn esp_rom_spi_cmd_start(
275 spi_num: u32,
276 rx_buf: *const u8,
277 rx_len: u16,
278 cs_en_mask: u8,
279 is_write_erase: bool,
280 );
281 }
282
283 unsafe {
284 let spi1 = SPI1::regs();
285 let backup_usr = spi1.user().read().bits();
286 let backup_usr1 = spi1.user1().read().bits();
287 let backup_usr2 = spi1.user2().read().bits();
288 let backup_ctrl = spi1.ctrl().read().bits();
289 psram_set_op_mode(mode);
290 _psram_exec_cmd(
291 cmd,
292 cmd_bit_len,
293 &addr,
294 addr_bit_len,
295 dummy_bits,
296 mosi_data,
297 mosi_bit_len,
298 miso_data,
299 miso_bit_len,
300 );
301 esp_rom_spi_cmd_start(
302 1,
303 miso_data,
304 (miso_bit_len / 8) as u16,
305 cs_mask,
306 is_write_erase_operation,
307 );
308
309 spi1.user().write(|w| w.bits(backup_usr));
310 spi1.user1().write(|w| w.bits(backup_usr1));
311 spi1.user2().write(|w| w.bits(backup_usr2));
312 spi1.ctrl().write(|w| w.bits(backup_ctrl));
313 }
314 }
315
316 #[expect(clippy::too_many_arguments)]
317 #[inline(always)]
318 fn _psram_exec_cmd(
319 cmd: u16,
320 cmd_bit_len: u16,
321 addr: *const u32,
322 addr_bit_len: u32,
323 dummy_bits: u32,
324 mosi_data: *const u8,
325 mosi_bit_len: u32,
326 miso_data: *mut u8,
327 miso_bit_len: u32,
328 ) {
329 #[repr(C)]
330 #[allow(non_camel_case_types)]
331 struct esp_rom_spi_cmd_t {
332 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,
341 }
342
343 unsafe extern "C" {
344 fn esp_rom_spi_cmd_config(spi_num: u32, pcmd: *const esp_rom_spi_cmd_t);
348 }
349
350 let conf = esp_rom_spi_cmd_t {
351 cmd,
352 cmd_bit_len,
353 addr,
354 addr_bit_len,
355 tx_data: mosi_data as *const u32,
356 tx_data_bit_len: mosi_bit_len,
357 rx_data: miso_data as *mut u32,
358 rx_data_bit_len: miso_bit_len,
359 dummy_bit_len: dummy_bits,
360 };
361
362 unsafe {
363 esp_rom_spi_cmd_config(1, &conf);
364 }
365 }
366
367 fn psram_set_op_mode(mode: CommandMode) {
368 unsafe extern "C" {
369 fn esp_rom_spi_set_op_mode(spi: u32, mode: u32);
370 }
371
372 const ESP_ROM_SPIFLASH_QIO_MODE: u32 = 0;
373 const ESP_ROM_SPIFLASH_SLOWRD_MODE: u32 = 5;
374
375 unsafe {
376 match mode {
377 CommandMode::PsramCmdQpi => {
378 esp_rom_spi_set_op_mode(1, ESP_ROM_SPIFLASH_QIO_MODE);
379 SPI1::regs().ctrl().modify(|_, w| w.fcmd_quad().set_bit());
380 }
381 CommandMode::PsramCmdSpi => {
382 esp_rom_spi_set_op_mode(1, ESP_ROM_SPIFLASH_SLOWRD_MODE);
383 }
384 }
385 }
386 }
387
388 #[repr(C)]
389 struct PsRamIo {
390 flash_clk_io: u8,
391 flash_cs_io: u8,
392 psram_clk_io: u8,
393 psram_cs_io: u8,
394 psram_spiq_sd0_io: u8,
395 psram_spid_sd1_io: u8,
396 psram_spiwp_sd3_io: u8,
397 psram_spihd_sd2_io: u8,
398 }
399
400 fn psram_gpio_config() {
401 unsafe extern "C" {
402 fn esp_rom_efuse_get_flash_gpio_info() -> u32;
403
404 fn esp_rom_spiflash_select_qio_pins(wp_gpio_num: u8, spiconfig: u32);
420 }
421
422 let psram_io = PsRamIo {
423 flash_clk_io: 30, flash_cs_io: 29, psram_clk_io: 30,
426 psram_cs_io: 26, psram_spiq_sd0_io: 31, psram_spid_sd1_io: 32, psram_spiwp_sd3_io: 28, psram_spihd_sd2_io: 27, };
432
433 const ESP_ROM_EFUSE_FLASH_DEFAULT_SPI: u32 = 0;
434
435 unsafe {
436 let spiconfig = esp_rom_efuse_get_flash_gpio_info();
437 if spiconfig == ESP_ROM_EFUSE_FLASH_DEFAULT_SPI {
438 } else {
441 panic!(
443 "Unsupported for now! The case 'FLASH pins are all configured via GPIO matrix in ROM.' is not yet supported."
444 );
445
446 }
459 esp_rom_spiflash_select_qio_pins(psram_io.psram_spiwp_sd3_io, spiconfig);
460 }
462 }
463
464 const PSRAM_IO_MATRIX_DUMMY_20M: u32 = 0;
465 const PSRAM_IO_MATRIX_DUMMY_40M: u32 = 0;
466 const PSRAM_IO_MATRIX_DUMMY_80M: u32 = 0;
467
468 fn psram_cache_init(psram_cache_mode: PsramCacheSpeed, _vaddrmode: PsramVaddrMode) {
470 let mut extra_dummy = 0;
471 match psram_cache_mode {
472 PsramCacheSpeed::PsramCacheS80m => {
473 psram_clock_set(1);
474 extra_dummy = PSRAM_IO_MATRIX_DUMMY_80M;
475 }
476 PsramCacheSpeed::PsramCacheS40m => {
477 psram_clock_set(2);
478 extra_dummy = PSRAM_IO_MATRIX_DUMMY_40M;
479 }
480 PsramCacheSpeed::PsramCacheS26m => {
481 psram_clock_set(3);
482 extra_dummy = PSRAM_IO_MATRIX_DUMMY_20M;
483 }
484 PsramCacheSpeed::PsramCacheS20m => {
485 psram_clock_set(4);
486 extra_dummy = PSRAM_IO_MATRIX_DUMMY_20M;
487 }
488 _ => {
489 psram_clock_set(2);
490 }
491 }
492
493 const PSRAM_QUAD_WRITE: u32 = 0x38;
494 const PSRAM_FAST_READ_QUAD: u32 = 0xEB;
495 const PSRAM_FAST_READ_QUAD_DUMMY: u32 = 0x5;
496
497 unsafe {
498 let spi = SPI0::regs();
499
500 spi.cache_sctrl()
501 .modify(|_, w| w.usr_sram_dio().clear_bit()); spi.cache_sctrl().modify(|_, w| w.usr_sram_qio().set_bit()); spi.cache_sctrl()
506 .modify(|_, w| w.cache_sram_usr_rcmd().set_bit()); spi.cache_sctrl()
509 .modify(|_, w| w.cache_sram_usr_wcmd().set_bit()); spi.cache_sctrl()
513 .modify(|_, w| w.sram_addr_bitlen().bits(23));
514
515 spi.cache_sctrl()
516 .modify(|_, w| w.usr_rd_sram_dummy().set_bit()); spi.sram_dwr_cmd()
520 .modify(|_, w| w.cache_sram_usr_wr_cmd_bitlen().bits(7));
521
522 spi.sram_dwr_cmd().modify(|_, w| {
523 w.cache_sram_usr_wr_cmd_value()
524 .bits(PSRAM_QUAD_WRITE as u16)
525 });
526
527 spi.sram_drd_cmd()
528 .modify(|_, w| w.cache_sram_usr_rd_cmd_bitlen().bits(7));
529
530 spi.sram_drd_cmd().modify(|_, w| {
531 w.cache_sram_usr_rd_cmd_value()
532 .bits(PSRAM_FAST_READ_QUAD as u16)
533 });
534
535 spi.cache_sctrl().modify(|_, w| {
537 w.sram_rdummy_cyclelen()
538 .bits((PSRAM_FAST_READ_QUAD_DUMMY + extra_dummy) as u8)
539 });
540
541 spi.misc().modify(|_, w| w.cs1_dis().clear_bit());
546 }
547 }
548
549 fn psram_clock_set(freqdiv: i8) {
550 const SPI_MEM_SCLKCNT_N_S: u32 = 16;
551 const SPI_MEM_SCLKCNT_H_S: u32 = 8;
552 const SPI_MEM_SCLKCNT_L_S: u32 = 0;
553
554 if 1 >= freqdiv {
555 SPI0::regs()
556 .sram_clk()
557 .modify(|_, w| w.sclk_equ_sysclk().set_bit());
558 } else {
559 let freqbits: u32 = (((freqdiv - 1) as u32) << SPI_MEM_SCLKCNT_N_S)
560 | (((freqdiv / 2 - 1) as u32) << SPI_MEM_SCLKCNT_H_S)
561 | (((freqdiv - 1) as u32) << SPI_MEM_SCLKCNT_L_S);
562 unsafe {
563 SPI0::regs().sram_clk().modify(|_, w| w.bits(freqbits));
564 }
565 }
566 }
567}