Skip to main content

esp_hal/psram/
esp32s3.rs

1use core::ops::Range;
2
3use procmacros::ram;
4
5use super::{EXTMEM_ORIGIN, PsramSize};
6use crate::peripherals::{EXTMEM, IO_MUX, MMU_TABLE, SPI0, SPI1};
7
8/// PSRAM interface mode
9#[derive(Copy, Clone, Debug, Default, PartialEq)]
10#[cfg_attr(feature = "defmt", derive(defmt::Format))]
11pub enum PsramMode {
12    /// Tries to detect the PSRAM mode. While convenient, not entirely reliable.
13    #[default]
14    Auto,
15
16    /// The PSRAM is connected via Quad SPI.
17    QuadSpi,
18
19    /// The PSRAM is connected via Octal SPI.
20    OctalSpi,
21}
22
23/// Frequency of flash memory
24#[derive(Copy, Clone, Debug, Default, PartialEq)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub enum FlashFreq {
27    /// Flash frequency 20 MHz
28    FlashFreq20m  = 20,
29    /// Flash frequency 40 MHz
30    FlashFreq40m  = 40,
31    /// Flash frequency 80 MHz
32    #[default]
33    FlashFreq80m  = 80,
34    /// Flash frequency 120 MHz
35    /// This is not recommended, see <https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-guides/flash_psram_config.html>
36    FlashFreq120m = 120,
37}
38
39/// Frequency of PSRAM memory
40#[derive(Copy, Clone, Debug, Default, PartialEq)]
41#[cfg_attr(feature = "defmt", derive(defmt::Format))]
42pub enum SpiRamFreq {
43    /// PSRAM frequency 40 MHz
44    Freq40m  = 40,
45    /// PSRAM frequency 80 MHz. Default.
46    #[default]
47    Freq80m  = 80,
48    /// PSRAM frequency 120 MHz
49    /// This is not recommended, see <https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-guides/flash_psram_config.html>
50    Freq120m = 120,
51}
52
53/// Core timing configuration
54#[derive(Copy, Clone, Debug, Default, PartialEq)]
55#[cfg_attr(feature = "defmt", derive(defmt::Format))]
56pub enum SpiTimingConfigCoreClock {
57    /// Core clock 80 MHz
58    #[default]
59    SpiTimingConfigCoreClock80m  = 80,
60    /// Core clock 120 MHz
61    SpiTimingConfigCoreClock120m = 120,
62    /// Core clock 160 MHz
63    SpiTimingConfigCoreClock160m = 160,
64    /// Core clock 240 MHz
65    SpiTimingConfigCoreClock240m = 240,
66}
67
68/// PSRAM configuration
69#[derive(Copy, Clone, Debug, Default, PartialEq)]
70#[cfg_attr(feature = "defmt", derive(defmt::Format))]
71pub struct PsramConfig {
72    /// PSRAM interface mode
73    pub mode: PsramMode,
74    /// PSRAM size
75    pub size: PsramSize,
76    /// Core timing configuration
77    pub core_clock: Option<SpiTimingConfigCoreClock>,
78    /// Frequency of flash memory
79    pub flash_frequency: FlashFreq,
80    /// Frequency of PSRAM memory
81    pub ram_frequency: SpiRamFreq,
82}
83
84/// Initializes PSRAM to be used for data.
85#[procmacros::ram]
86pub(crate) fn init_psram(config: &mut PsramConfig) -> bool {
87    let success = match config.mode {
88        PsramMode::Auto => {
89            let mut success = octal_spi_impl::psram_init(config);
90
91            if !success {
92                success = quad_spi_impl::psram_init(config);
93            }
94
95            success
96        }
97        PsramMode::QuadSpi => quad_spi_impl::psram_init(config),
98        PsramMode::OctalSpi => octal_spi_impl::psram_init(config),
99    };
100
101    if !success {
102        warn!(
103            "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."
104        );
105    }
106
107    success
108}
109
110#[procmacros::ram]
111pub(crate) fn map_psram(config: PsramConfig) -> Range<usize> {
112    const MMU_ACCESS_SPIRAM: u32 = 1 << 15;
113    const START_PAGE: u32 = 0;
114
115    unsafe extern "C" {
116        fn Cache_Suspend_DCache() -> u32;
117
118        fn Cache_Resume_DCache(param: u32);
119
120        /// Sets DCache mmu mapping.
121        ///
122        /// [`ext_ram`]: u32 DPORT_MMU_ACCESS_FLASH for flash, DPORT_MMU_ACCESS_SPIRAM for spiram, DPORT_MMU_INVALID for invalid.
123        /// [`vaddr`]: u32 Virtual address in CPU address space.
124        /// [`paddr`]: u32 Physical address in external memory. Should be aligned by psize.
125        /// [`psize`]: u32 Page size of DCache, in kilobytes. Should be 64 here.
126        /// [`num`]: u32 Pages to be set.
127        /// [`fixes`]: u32 0 for physical pages grow with virtual pages, other for virtual pages map to same physical page.
128        fn cache_dbus_mmu_set(
129            ext_ram: u32,
130            vaddr: u32,
131            paddr: u32,
132            psize: u32,
133            num: u32,
134            fixed: u32,
135        ) -> i32;
136    }
137
138    const MMU_PAGE_SIZE: u32 = property!("mmu.page_size");
139
140    fn mmu_entry_is_valid(entry_id: usize) -> bool {
141        !MMU_TABLE::regs().entry(entry_id).read().invalid().bit()
142    }
143
144    // calculate the PSRAM start address to map
145    // the linker scripts can produce a gap between mapped IROM and DROM segments
146    // bigger than a flash page - i.e. we will see an unmapped memory slot
147    // start from the end and find the last mapped flash page
148    //
149    // More general information about the MMU can be found here:
150    // https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/system/mm.html#introduction
151    let mut mapped_pages = 0;
152
153    // the bootloader is using the last page to access flash internally
154    // (e.g. to read the app descriptor) so we just skip that
155    let flash_mmu_table_size: usize = MMU_TABLE::regs().entry_iter().count();
156    for i in (0..(flash_mmu_table_size - 1)).rev() {
157        if mmu_entry_is_valid(i) {
158            mapped_pages = (i + 1) as u32;
159            break;
160        }
161    }
162    let start = EXTMEM_ORIGIN as u32 + (MMU_PAGE_SIZE * mapped_pages);
163    debug!("PSRAM start address = {:x}", start);
164
165    // If we need use SPIRAM, we should use data cache.
166    unsafe { Cache_Suspend_DCache() };
167
168    let cache_dbus_mmu_set_res = unsafe {
169        cache_dbus_mmu_set(
170            MMU_ACCESS_SPIRAM,
171            start,
172            START_PAGE << 16,
173            64,
174            config.size.get() as u32 / 1024 / 64, // number of pages to map
175            0,
176        )
177    };
178
179    EXTMEM::regs().dcache_ctrl1().modify(|_, w| {
180        w.dcache_shut_core0_bus().clear_bit();
181        w.dcache_shut_core1_bus().clear_bit()
182    });
183
184    unsafe { Cache_Resume_DCache(0) };
185
186    // panic AFTER resuming the cache
187    if cache_dbus_mmu_set_res != 0 {
188        panic!("cache_dbus_mmu_set failed");
189    }
190
191    start as usize..start as usize + config.size.get()
192}
193
194pub(crate) mod quad_spi_impl {
195    use super::*;
196    use crate::psram::quad_xtensa;
197
198    const PSRAM_CS_IO: u8 = 26;
199    const SPI_CS1_GPIO_NUM: u8 = 26;
200    const FUNC_SPICS1_SPICS1: u8 = 0;
201    const PIN_FUNC_GPIO: u8 = 2;
202    const PSRAM_SPIWP_SD3_IO: u8 = 10;
203    const ESP_ROM_EFUSE_FLASH_DEFAULT_SPI: u32 = 0;
204    const SPICS1_OUT_IDX: u8 = 6;
205
206    #[ram]
207    pub(crate) fn psram_init(config: &mut PsramConfig) -> bool {
208        psram_gpio_config();
209        psram_set_cs_timing();
210
211        if config.size.is_auto() {
212            let Some(size) = quad_xtensa::detect_quad_size() else {
213                return false;
214            };
215            config.size = PsramSize::Size(size);
216        }
217
218        if config.core_clock.is_none() {
219            config.core_clock = Some(if config.ram_frequency == SpiRamFreq::Freq120m {
220                SpiTimingConfigCoreClock::SpiTimingConfigCoreClock120m
221            } else {
222                SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m
223            });
224        }
225
226        quad_xtensa::psram_reset_mode_spi1();
227        quad_xtensa::psram_enable_qio_mode_spi1();
228
229        // Timing tuning is not implemented. Quad 80 MHz does not require it.
230        // See IDF `mspi_timing_psram_tuning`.
231        mspi_timing_psram_tuning();
232
233        quad_xtensa::config_psram_spi_phases();
234        mspi_timing_enter_high_speed_mode(true, config);
235
236        info!("PSRAM initialized successfully in Quad SPI mode");
237        true
238    }
239
240    unsafe extern "C" {
241        fn esp_rom_efuse_get_flash_gpio_info() -> u32;
242
243        fn esp_rom_efuse_get_flash_wp_gpio() -> u8;
244
245        fn esp_rom_gpio_connect_out_signal(
246            gpio_num: u8,
247            signal_idx: u8,
248            out_inv: bool,
249            oen_inv: bool,
250        );
251
252        /// Enables Quad I/O pin functions.
253        ///
254        /// Sets the HD & WP pin functions for Quad I/O modes, based on the
255        /// efuse SPI pin configuration.
256        ///
257        /// [`wp_gpio_num`]: u8 Number of the WP pin to reconfigure for quad I/O
258        /// [`spiconfig`]: u32 Pin configuration, as returned from ets_efuse_get_spiconfig().
259        /// - If this parameter is 0, default SPI pins are used and wp_gpio_num parameter is
260        ///   ignored.
261        /// - If this parameter is 1, default HSPI pins are used and wp_gpio_num parameter is
262        ///   ignored.
263        /// - For other values, this parameter encodes the HD pin number and also the CLK pin
264        ///   number. CLK pin selection is used to determine if HSPI or SPI peripheral will be used
265        ///   (use HSPI if CLK pin is the HSPI clock pin, otherwise use SPI).
266        //   Both HD & WP pins are configured via GPIO matrix to map to the selected peripheral.
267        fn esp_rom_spiflash_select_qio_pins(wp_gpio_num: u8, spiconfig: u32);
268    }
269
270    #[ram]
271    fn mspi_timing_psram_tuning() {
272        // currently we only support !SPI_TIMING_PSRAM_NEEDS_TUNING
273        // see https://github.com/espressif/esp-idf/blob/4e24516ee2731eb55687182d4e061b5b93a9e33f/components/esp_hw_support/mspi_timing_tuning.c#L391-L415
274    }
275
276    /// Sets SPI0 FLASH and PSRAM module clock, din_num, din_mode and extra
277    /// dummy, according to the configuration got from timing tuning
278    /// function (`calculate_best_flash_tuning_config`). iF control_spi1 ==
279    /// 1, will also update SPI1 timing registers. Should only be set to 1 when
280    /// do tuning.
281    ///
282    /// Must always be called after `mspi_timing_flash_tuning` or
283    /// `calculate_best_flash_tuning_config`
284    #[ram]
285    fn mspi_timing_enter_high_speed_mode(control_spi1: bool, config: &PsramConfig) {
286        let core_clock: SpiTimingConfigCoreClock = mspi_core_clock(config);
287        let flash_div: u32 = flash_clock_divider(config);
288        let psram_div: u32 = psram_clock_divider(config);
289
290        info!(
291            "PSRAM {} MHz, flash {} MHz, core_clock {:?}, flash_div = {}, psram_div = {}",
292            config.ram_frequency as u32,
293            config.flash_frequency as u32,
294            core_clock,
295            flash_div,
296            psram_div
297        );
298
299        // Set SPI01 core clock
300        // SPI0 and SPI1 share the register for core clock. So we only set SPI0 here.
301        // Set FLASH module clock
302        spi0_timing_config_set_core_clock(core_clock);
303
304        spi0_timing_config_set_flash_clock(flash_div);
305        if control_spi1 {
306            spi1_timing_config_set_flash_clock(flash_div);
307        }
308        quad_xtensa::spi0_timing_config_set_psram_clock(psram_div);
309    }
310
311    #[ram]
312    fn spi0_timing_config_set_core_clock(core_clock: SpiTimingConfigCoreClock) {
313        unsafe {
314            SPI0::regs().core_clk_sel().modify(|_, w| {
315                w.core_clk_sel().bits(match core_clock {
316                    SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m => 0,
317                    SpiTimingConfigCoreClock::SpiTimingConfigCoreClock120m => 1,
318                    SpiTimingConfigCoreClock::SpiTimingConfigCoreClock160m => 2,
319                    SpiTimingConfigCoreClock::SpiTimingConfigCoreClock240m => 3,
320                })
321            });
322        }
323    }
324
325    #[ram]
326    fn mspi_core_clock(config: &PsramConfig) -> SpiTimingConfigCoreClock {
327        config.core_clock.unwrap_or_default()
328    }
329
330    #[ram]
331    fn flash_clock_divider(config: &PsramConfig) -> u32 {
332        config.core_clock.unwrap_or_default() as u32 / config.flash_frequency as u32
333    }
334
335    #[ram]
336    fn psram_clock_divider(config: &PsramConfig) -> u32 {
337        config.core_clock.unwrap_or_default() as u32 / config.ram_frequency as u32
338    }
339
340    #[ram]
341    fn psram_set_cs_timing() {
342        // SPI0/1 share the cs_hold / cs_setup, cd_hold_time / cd_setup_time registers
343        // for PSRAM, so we only need to set SPI0 related registers here
344        SPI0::regs().spi_smem_ac().modify(|_, w| unsafe {
345            w.spi_smem_cs_hold_time().bits(0);
346            w.spi_smem_cs_setup_time().bits(0);
347            w.spi_smem_cs_hold().set_bit();
348            w.spi_smem_cs_setup().set_bit()
349        });
350    }
351
352    #[ram]
353    fn psram_gpio_config() {
354        // CS1
355        let cs1_io: u8 = PSRAM_CS_IO;
356        let mcu_sel = if cs1_io == SPI_CS1_GPIO_NUM {
357            FUNC_SPICS1_SPICS1
358        } else {
359            unsafe {
360                esp_rom_gpio_connect_out_signal(cs1_io, SPICS1_OUT_IDX, false, false);
361                PIN_FUNC_GPIO
362            }
363        };
364
365        IO_MUX::regs()
366            .gpio(cs1_io as usize)
367            .modify(|_, w| unsafe { w.mcu_sel().bits(mcu_sel) });
368
369        // WP HD
370        let mut wp_io: u8 = PSRAM_SPIWP_SD3_IO;
371        let spiconfig = unsafe { esp_rom_efuse_get_flash_gpio_info() };
372        if spiconfig == ESP_ROM_EFUSE_FLASH_DEFAULT_SPI {
373            // MSPI pins (except wp / hd) are all configured via IO_MUX in 1st
374            // bootloader.
375        } else {
376            // MSPI pins (except wp / hd) are all configured via GPIO matrix in 1st
377            // bootloader.
378            wp_io = unsafe { esp_rom_efuse_get_flash_wp_gpio() };
379        }
380        // This ROM function will init both WP and HD pins.
381        unsafe {
382            esp_rom_spiflash_select_qio_pins(wp_io, spiconfig);
383        }
384    }
385}
386
387pub(crate) mod octal_spi_impl {
388    use super::*;
389    use crate::psram::quad_xtensa;
390
391    const OPI_PSRAM_SYNC_READ: u16 = 0x0000;
392    const OPI_PSRAM_SYNC_WRITE: u16 = 0x8080;
393    const OPI_PSRAM_REG_READ: u16 = 0x4040;
394    const OPI_PSRAM_REG_WRITE: u16 = 0xC0C0;
395    const OCT_PSRAM_RD_CMD_BITLEN: u8 = 16;
396    const OCT_PSRAM_WR_CMD_BITLEN: u8 = 16;
397    const OCT_PSRAM_ADDR_BITLEN: u8 = 32;
398    const OCT_PSRAM_RD_DUMMY_BITLEN: u8 = 2 * (10 - 1);
399    const OCT_PSRAM_WR_DUMMY_BITLEN: u8 = 2 * (5 - 1);
400    const OCT_PSRAM_CS1_IO: u8 = SPI_CS1_GPIO_NUM;
401    const OCT_PSRAM_VENDOR_ID: u8 = 0xD;
402
403    const OCT_PSRAM_CS_SETUP_TIME: u8 = 3;
404    const OCT_PSRAM_CS_HOLD_TIME: u8 = 3;
405    const OCT_PSRAM_CS_HOLD_DELAY: u8 = 2;
406
407    const SPI_CS1_GPIO_NUM: u8 = 26;
408    const FUNC_SPICS1_SPICS1: u8 = 0;
409
410    const ESP_ROM_SPIFLASH_OPI_DTR_MODE: u8 = 7;
411
412    unsafe extern "C" {
413        // @brief To execute a flash operation command
414        // @param spi_num spi port
415        // @param mode Flash Read Mode
416        // @param cmd data to send in command field
417        // @param cmd_bit_len bit length of command field
418        // @param addr data to send in address field
419        // @param addr_bit_len bit length of address field
420        // @param dummy_bits bit length of dummy field
421        // @param mosi_data data buffer to be sent in mosi field
422        // @param mosi_bit_len bit length of data buffer to be sent in mosi field
423        // @param miso_data data buffer to accept data in miso field
424        // @param miso_bit_len bit length of data buffer to accept data in miso field
425        // @param cs_mark decide which cs pin to use. 0: cs0, 1: cs1
426        // @param is_write_erase_operation to indicate whether this a write or erase
427        // flash operation
428        fn esp_rom_opiflash_exec_cmd(
429            spi_num: u32,
430            mode: u8,
431            cmd: u32,
432            cmd_bit_len: u32,
433            addr: u32,
434            addr_bit_len: u32,
435            dummy_bits: u32,
436            mosi_data: *const u8,
437            mosi_bit_len: u32,
438            miso_data: *mut u8,
439            miso_bit_len: u32,
440            cs_mask: u32,
441            is_write_erase_operation: bool,
442        );
443
444        // @brief Set data swap mode in DTR(DDR) mode
445        // @param spi_num spi port
446        // @param wr_swap to decide whether to swap fifo data in dtr write operation
447        // @param rd_swap to decide whether to swap fifo data in dtr read operation
448        fn esp_rom_spi_set_dtr_swap_mode(spi: u32, wr_swap: bool, rd_swap: bool);
449
450        fn esp_rom_opiflash_pin_config();
451    }
452
453    /// Represents the operational mode registers of an OPI PSRAM.
454    #[derive(Default)]
455    #[repr(C)]
456    struct OpiPsramModeReg {
457        // Mode register 0 (MR0).
458        pub mr0: u8,
459        // Mode register 1 (MR1).
460        pub mr1: u8,
461        // Mode register 2 (MR2).
462        pub mr2: u8,
463        // Mode register 3 (MR3).
464        pub mr3: u8,
465        // Mode register 4 (MR4).
466        pub mr4: u8,
467        // Mode register 8 (MR8).
468        pub mr8: u8,
469    }
470
471    #[allow(unused)]
472    impl OpiPsramModeReg {
473        fn drive_str(&self) -> u8 {
474            self.mr0 & 0b11
475        }
476
477        fn set_drive_str(&mut self, value: u8) {
478            self.mr0 &= !0b11;
479            self.mr0 |= value & 0b11;
480        }
481
482        fn read_latency(&self) -> u8 {
483            (self.mr0 >> 2) & 0b111
484        }
485
486        fn set_read_latency(&mut self, value: u8) {
487            self.mr0 &= !(0b111 << 2);
488            self.mr0 |= (value & 0b111) << 2;
489        }
490
491        fn lt(&self) -> u8 {
492            (self.mr0 >> 5) & 0b1
493        }
494
495        fn set_lt(&mut self, value: u8) {
496            self.mr0 &= !(0b1 << 5);
497            self.mr0 |= (value & 0b1) << 5;
498        }
499
500        fn rsvd0_1(&self) -> u8 {
501            (self.mr0 >> 6) & 0b11
502        }
503
504        fn set_rsvd0_1(&mut self, value: u8) {
505            self.mr0 &= !(0b11 << 6);
506            self.mr0 |= (value & 0b11) << 6;
507        }
508
509        fn vendor_id(&self) -> u8 {
510            self.mr1 & 0b11111
511        }
512
513        fn set_vendor_id(&mut self, value: u8) {
514            self.mr1 &= !0b11111;
515            self.mr1 |= value & 0b11111;
516        }
517
518        fn rsvd0_2(&self) -> u8 {
519            (self.mr1 >> 5) & 0b111
520        }
521
522        fn set_rsvd0_2(&mut self, value: u8) {
523            self.mr1 &= !(0b111 << 5);
524            self.mr1 |= (value & 0b111) << 5;
525        }
526
527        fn density(&self) -> u8 {
528            self.mr2 & 0b111
529        }
530
531        fn set_density(&mut self, value: u8) {
532            self.mr2 &= !0b111;
533            self.mr2 |= value & 0b111;
534        }
535
536        fn dev_id(&self) -> u8 {
537            (self.mr2 >> 3) & 0b11
538        }
539
540        fn set_dev_id(&mut self, value: u8) {
541            self.mr2 &= !(0b11 << 3);
542            self.mr2 |= (value & 0b11) << 3;
543        }
544
545        fn rsvd1_2(&self) -> u8 {
546            (self.mr2 >> 5) & 0b11
547        }
548
549        fn set_rsvd1_2(&mut self, value: u8) {
550            self.mr2 &= !(0b11 << 5);
551            self.mr2 |= (value & 0b11) << 5;
552        }
553
554        fn gb(&self) -> u8 {
555            (self.mr2 >> 7) & 0b1
556        }
557
558        fn set_gb(&mut self, value: u8) {
559            self.mr2 &= !(0b1 << 7);
560            self.mr2 |= (value & 0b1) << 7;
561        }
562
563        fn rsvd3_7(&self) -> u8 {
564            self.mr3 & 0b11111
565        }
566
567        fn set_rsvd3_7(&mut self, value: u8) {
568            self.mr3 &= !0b11111;
569            self.mr3 |= value & 0b11111;
570        }
571
572        fn srf(&self) -> u8 {
573            (self.mr3 >> 5) & 0b1
574        }
575
576        fn set_srf(&mut self, value: u8) {
577            self.mr3 &= !(0b1 << 5);
578            self.mr3 |= (value & 0b1) << 5;
579        }
580
581        fn vcc(&self) -> u8 {
582            (self.mr3 >> 6) & 0b1
583        }
584
585        fn set_vcc(&mut self, value: u8) {
586            self.mr3 &= !(0b1 << 6);
587            self.mr3 |= (value & 0b1) << 6;
588        }
589
590        fn rsvd0(&self) -> u8 {
591            (self.mr3 >> 7) & 0b1
592        }
593
594        fn set_rsvd0(&mut self, value: u8) {
595            self.mr3 &= !(0b1 << 7);
596            self.mr3 |= (value & 0b1) << 7;
597        }
598
599        fn pasr(&self) -> u8 {
600            self.mr4 & 0b111
601        }
602
603        fn set_pasr(&mut self, value: u8) {
604            self.mr4 &= !0b111;
605            self.mr4 |= value & 0b111;
606        }
607
608        fn rf(&self) -> u8 {
609            (self.mr4 >> 3) & 0b1
610        }
611
612        fn set_rf(&mut self, value: u8) {
613            self.mr4 &= !(0b1 << 3);
614            self.mr4 |= (value & 0b1) << 3;
615        }
616
617        fn rsvd3(&self) -> u8 {
618            (self.mr4 >> 4) & 0b1
619        }
620
621        fn set_rsvd3(&mut self, value: u8) {
622            self.mr4 &= !(0b1 << 4);
623            self.mr4 |= (value & 0b1) << 4;
624        }
625
626        fn wr_latency(&self) -> u8 {
627            (self.mr4 >> 5) & 0b111
628        }
629
630        fn set_wr_latency(&mut self, value: u8) {
631            self.mr4 &= !(0b111 << 5);
632            self.mr4 |= (value & 0b111) << 5;
633        }
634
635        fn bl(&self) -> u8 {
636            self.mr8 & 0b11
637        }
638
639        fn set_bl(&mut self, value: u8) {
640            self.mr8 &= !0b11;
641            self.mr8 |= value & 0b11;
642        }
643
644        fn bt(&self) -> u8 {
645            (self.mr8 >> 2) & 0b1
646        }
647
648        fn set_bt(&mut self, value: u8) {
649            self.mr8 &= !(0b1 << 2);
650            self.mr8 |= (value & 0b1) << 2;
651        }
652
653        fn rsvd0_4(&self) -> u8 {
654            (self.mr8 >> 3) & 0b11111
655        }
656
657        fn set_rsvd0_4(&mut self, value: u8) {
658            self.mr8 &= !(0b11111 << 3);
659            self.mr8 |= (value & 0b11111) << 3;
660        }
661    }
662
663    #[ram]
664    pub(crate) fn psram_init(config: &mut PsramConfig) -> bool {
665        mspi_pin_init();
666        set_psram_cs_timing();
667
668        // for now we don't support ECC
669        // "s_configure_psram_ecc();"
670
671        // enter MSPI slow mode to init PSRAM device registers
672        spi_timing_enter_mspi_low_speed_mode(true);
673
674        unsafe {
675            // set to variable dummy mode
676            SPI1::regs()
677                .ddr()
678                .modify(|_, w| w.spi_fmem_var_dummy().set_bit());
679            esp_rom_spi_set_dtr_swap_mode(1, false, false);
680        }
681
682        // Set PSRAM read latency and drive strength
683        let mut mode_reg = OpiPsramModeReg::default();
684        mode_reg.set_lt(1);
685        mode_reg.set_read_latency(2);
686        mode_reg.set_drive_str(0);
687        mode_reg.set_bl(3);
688        mode_reg.set_bt(0);
689
690        init_psram_mode_reg(1, &mode_reg);
691        // Print PSRAM info
692        psram_mode_reg(1, &mut mode_reg);
693
694        print_psram_info(&mode_reg);
695
696        if mode_reg.vendor_id() != OCT_PSRAM_VENDOR_ID {
697            debug!(
698                "Unknown PSRAM vendor ID: {:x}. PSRAM chip not found or not supported. Check if the interface mode is configured correctly.",
699                mode_reg.vendor_id()
700            );
701            return false;
702        }
703
704        let psram_size = match mode_reg.density() {
705            0x0 => 2 * 1024 * 1024,
706            0x1 => 4 * 1024 * 1024,
707            0x3 => 8 * 1024 * 1024,
708            0x5 => 16 * 1024 * 1024,
709            0x7 => 32 * 1024 * 1024,
710            _ => 0,
711        };
712        info!("{} bytes of PSRAM", psram_size);
713
714        if config.core_clock.is_none() {
715            config.core_clock = Some(if config.ram_frequency == SpiRamFreq::Freq80m {
716                SpiTimingConfigCoreClock::SpiTimingConfigCoreClock160m
717            } else if config.ram_frequency == SpiRamFreq::Freq120m {
718                SpiTimingConfigCoreClock::SpiTimingConfigCoreClock240m
719            } else {
720                SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m
721            });
722        }
723        if config.size.is_auto() {
724            config.size = PsramSize::Size(psram_size);
725        }
726
727        // Do PSRAM timing tuning, we use SPI1 to do the tuning, and set the
728        // SPI0 PSRAM timing related registers accordingly
729        // this is unsupported for now
730        // spi_timing_psram_tuning();
731
732        // Back to the high speed mode. Flash/PSRAM clocks are set to the clock
733        // that user selected. SPI0/1 registers are all set correctly
734        spi_timing_enter_mspi_high_speed_mode(true, config);
735
736        // Tuning may change SPI1 regs, whereas legacy spi_flash APIs rely on
737        // these regs. This function is to restore SPI1 init state.
738        spi_flash_set_rom_required_regs();
739
740        // Flash chip requires MSPI specifically, call this function to set them
741        // this is unsupported for now
742        // spi_flash_set_vendor_required_regs();
743
744        config_psram_spi_phases();
745
746        info!("PSRAM initialized successfully in Octal SPI mode");
747        true
748    }
749
750    // Configure PSRAM SPI0 phase related registers here according to the PSRAM chip
751    // requirement
752    fn config_psram_spi_phases() {
753        SPI0::regs().cache_sctrl().modify(|_, w| unsafe {
754            w.sram_oct().set_bit();
755
756            w.cache_sram_usr_rcmd().set_bit();
757            w.cache_sram_usr_wcmd().set_bit();
758
759            // Config ADDR phase
760            w.sram_addr_bitlen().bits(OCT_PSRAM_ADDR_BITLEN - 1);
761            w.cache_usr_scmd_4byte().set_bit();
762
763            // Config RD/WR Dummy phase
764            w.usr_rd_sram_dummy().set_bit();
765            w.usr_wr_sram_dummy().set_bit();
766            w.sram_rdummy_cyclelen().bits(OCT_PSRAM_RD_DUMMY_BITLEN - 1);
767            w.sram_wdummy_cyclelen().bits(OCT_PSRAM_WR_DUMMY_BITLEN - 1)
768        });
769
770        // Config Write CMD phase for SPI0 to access PSRAM
771        SPI0::regs().sram_dwr_cmd().modify(|_, w| unsafe {
772            w.cache_sram_usr_wr_cmd_value().bits(OPI_PSRAM_SYNC_WRITE);
773            w.cache_sram_usr_wr_cmd_bitlen()
774                .bits(OCT_PSRAM_WR_CMD_BITLEN - 1)
775        });
776
777        // Config Read CMD phase for SPI0 to access PSRAM
778        SPI0::regs().sram_drd_cmd().modify(|_, w| unsafe {
779            w.cache_sram_usr_rd_cmd_bitlen()
780                .bits(OCT_PSRAM_RD_CMD_BITLEN - 1);
781            w.cache_sram_usr_rd_cmd_value().bits(OPI_PSRAM_SYNC_READ)
782        });
783
784        SPI0::regs().spi_smem_ddr().modify(|_, w| {
785            w.spi_smem_var_dummy().set_bit();
786            w.wdat_swp().clear_bit();
787            w.rdat_swp().clear_bit();
788            w.en().set_bit()
789        });
790
791        SPI0::regs().sram_cmd().modify(|_, w| {
792            w.sdummy_out().set_bit();
793            w.scmd_oct().set_bit();
794            w.saddr_oct().set_bit();
795            w.sdout_oct().set_bit();
796            w.sdin_oct().set_bit()
797        });
798    }
799
800    #[ram]
801    fn spi_flash_set_rom_required_regs() {
802        // Disable the variable dummy mode when doing timing tuning
803        SPI1::regs()
804            .ddr()
805            .modify(|_, w| w.spi_fmem_var_dummy().clear_bit());
806        // STR /DTR mode setting is done every time when
807        // `esp_rom_opiflash_exec_cmd` is called
808        //
809        // Add any registers that are not set in ROM SPI flash functions here in
810        // the future
811    }
812
813    #[ram]
814    fn mspi_pin_init() {
815        unsafe { esp_rom_opiflash_pin_config() };
816        // Set F4R4 board pin drive strength. TODO: IDF-3663
817
818        // For now, set them all to 3. Need to check after QVL test results are out.
819        // TODO: IDF-3663 Set default clk
820        SPI0::regs().date().modify(|_, w| unsafe {
821            w.spi_spiclk_pad_drv_ctl_en().set_bit();
822            w.spi_smem_spiclk_fun_drv().bits(3);
823            w.spi_fmem_spiclk_fun_drv().bits(3)
824        });
825
826        // Set default mspi d0 ~ d7, dqs pin drive strength
827        let pins = [
828            OCT_PSRAM_CS1_IO as usize,
829            27,
830            28,
831            31,
832            32,
833            33,
834            34,
835            35,
836            36,
837            37,
838        ];
839        for pin in pins {
840            IO_MUX::regs()
841                .gpio(pin)
842                .modify(|_, w| unsafe { w.fun_drv().bits(3) });
843        }
844
845        IO_MUX::regs()
846            .gpio(OCT_PSRAM_CS1_IO as usize)
847            .modify(|_, w| unsafe { w.mcu_sel().bits(FUNC_SPICS1_SPICS1) });
848    }
849
850    fn spi_timing_enter_mspi_low_speed_mode(control_spi1: bool) {
851        // Here we are going to slow the SPI1 frequency to 20Mhz, so we need to
852        // set SPI1 din_num and din_mode regs.
853        //
854        // Because SPI0 and SPI1 share the din_num and din_mode regs, so if we
855        // clear SPI1 din_num and din_mode to 0, if the SPI0 flash
856        // module clock is still in high freq, it may not work correctly.
857        //
858        // Therefore, here we need to slow both the SPI0 and SPI1 and related
859        // timing tuning regs to 20Mhz configuration.
860        // Switch SPI1 and SPI0 clock as 20MHz, set its SPIMEM core clock as 80M and set
861        // clock division as 4
862        spi0_timing_config_set_core_clock(SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m); // SPI0 and SPI1 share the register for core clock. So we only set SPI0 here.
863        spi0_timing_config_set_flash_clock(4);
864        if control_spi1 {
865            // After tuning, won't touch SPI1 again
866            spi1_timing_config_set_flash_clock(4);
867        }
868
869        // Set PSRAM module clock
870        quad_xtensa::spi0_timing_config_set_psram_clock(4);
871
872        // for now we don't support tuning the timing
873        // "clear_timing_tuning_regs(control_spi1);"
874    }
875
876    // Set SPI0 FLASH and PSRAM module clock, din_num, din_mode and extra dummy,
877    // according to the configuration got from timing tuning function
878    // (`calculate_best_flash_tuning_config`). iF control_spi1 == 1, will also
879    // update SPI1 timing registers. Should only be set to 1 when do tuning.
880    //
881    // This function should always be called after `spi_timing_flash_tuning` or
882    // `calculate_best_flash_tuning_config`
883    fn spi_timing_enter_mspi_high_speed_mode(control_spi1: bool, config: &PsramConfig) {
884        let flash_div: u32 = flash_clock_divider(config);
885        let psram_div: u32 = psram_clock_divider(config);
886
887        // Set SPI01 core clock
888        spi0_timing_config_set_core_clock(config.core_clock.unwrap_or_default()); // SPI0 and SPI1 share the register for core clock. So we only set SPI0 here.
889        // Set FLASH module clock
890        spi0_timing_config_set_flash_clock(flash_div);
891        if control_spi1 {
892            spi1_timing_config_set_flash_clock(flash_div);
893        }
894        // Set PSRAM module clock
895        quad_xtensa::spi0_timing_config_set_psram_clock(psram_div);
896
897        // for now we don't support tuning the timing
898        // "set_timing_tuning_regs_as_required(true);"
899    }
900
901    fn set_psram_cs_timing() {
902        // SPI0/1 share the cs_hold / cs_setup, cd_hold_time / cd_setup_time,
903        // cs_hold_delay registers for PSRAM, so we only need to set SPI0 related
904        // registers here
905        SPI0::regs().spi_smem_ac().modify(|_, w| unsafe {
906            w.spi_smem_cs_hold().set_bit();
907            w.spi_smem_cs_setup().set_bit();
908            w.spi_smem_cs_hold_time().bits(OCT_PSRAM_CS_HOLD_TIME);
909            w.spi_smem_cs_setup_time().bits(OCT_PSRAM_CS_SETUP_TIME);
910
911            // CONFIG_SPIRAM_ECC_ENABLE unsupported for now
912            // CS1 high time
913            w.spi_smem_cs_hold_delay().bits(OCT_PSRAM_CS_HOLD_DELAY)
914        });
915    }
916
917    fn psram_mode_reg(spi_num: u32, out_reg: &mut OpiPsramModeReg) {
918        let mode = ESP_ROM_SPIFLASH_OPI_DTR_MODE;
919        let cmd_len: u32 = 16;
920        let addr_bit_len: u32 = 32;
921        let dummy: u32 = OCT_PSRAM_RD_DUMMY_BITLEN as u32;
922        let mut data_bit_len: u32 = 16;
923
924        unsafe {
925            // Read MR0~1 register
926            esp_rom_opiflash_exec_cmd(
927                spi_num,
928                mode,
929                OPI_PSRAM_REG_READ as u32,
930                cmd_len,
931                0x0,
932                addr_bit_len,
933                dummy,
934                core::ptr::null(),
935                0,
936                &mut out_reg.mr0,
937                data_bit_len,
938                1 << 1,
939                false,
940            );
941            // Read MR2~3 register
942            esp_rom_opiflash_exec_cmd(
943                spi_num,
944                mode,
945                OPI_PSRAM_REG_READ as u32,
946                cmd_len,
947                0x2,
948                addr_bit_len,
949                dummy,
950                core::ptr::null(),
951                0,
952                &mut out_reg.mr2,
953                data_bit_len,
954                1 << 1,
955                false,
956            );
957            data_bit_len = 8;
958            // Read MR4 register
959            esp_rom_opiflash_exec_cmd(
960                spi_num,
961                mode,
962                OPI_PSRAM_REG_READ as u32,
963                cmd_len,
964                0x4,
965                addr_bit_len,
966                dummy,
967                core::ptr::null(),
968                0,
969                &mut out_reg.mr4,
970                data_bit_len,
971                1 << 1,
972                false,
973            );
974            // Read MR8 register
975            esp_rom_opiflash_exec_cmd(
976                spi_num,
977                mode,
978                OPI_PSRAM_REG_READ as u32,
979                cmd_len,
980                0x8,
981                addr_bit_len,
982                dummy,
983                core::ptr::null(),
984                0,
985                &mut out_reg.mr8,
986                data_bit_len,
987                1 << 1,
988                false,
989            );
990        }
991    }
992
993    // Initialise mode registers of the PSRAM
994    fn init_psram_mode_reg(spi_num: u32, mode_reg_config: &OpiPsramModeReg) {
995        let mode = ESP_ROM_SPIFLASH_OPI_DTR_MODE;
996        let cmd_len: u32 = 16;
997        let addr: u32 = 0x0; // 0x0 is the MR0 register
998        let addr_bit_len: u32 = 32;
999        let dummy = OCT_PSRAM_RD_DUMMY_BITLEN as u32;
1000        let mut mode_reg = OpiPsramModeReg::default();
1001        let data_bit_len: u32 = 16;
1002
1003        // read
1004        unsafe {
1005            esp_rom_opiflash_exec_cmd(
1006                spi_num,
1007                mode,
1008                OPI_PSRAM_REG_READ as u32,
1009                cmd_len,
1010                addr,
1011                addr_bit_len,
1012                dummy,
1013                core::ptr::null(),
1014                0,
1015                &mut mode_reg.mr0,
1016                data_bit_len,
1017                1 << 1,
1018                false,
1019            );
1020        }
1021
1022        // modify
1023        mode_reg.set_lt(mode_reg_config.lt());
1024        mode_reg.set_read_latency(mode_reg_config.read_latency());
1025        mode_reg.set_drive_str(mode_reg_config.drive_str());
1026
1027        // write
1028        unsafe {
1029            esp_rom_opiflash_exec_cmd(
1030                spi_num,
1031                mode,
1032                OPI_PSRAM_REG_WRITE as u32,
1033                cmd_len,
1034                addr,
1035                addr_bit_len,
1036                0,
1037                &mode_reg.mr0,
1038                16,
1039                core::ptr::null_mut(),
1040                0,
1041                1 << 1,
1042                false,
1043            );
1044        }
1045
1046        // CONFIG_SPIRAM_ECC_ENABLE not yet supported
1047    }
1048
1049    fn print_psram_info(reg_val: &OpiPsramModeReg) {
1050        info!(
1051            "vendor id    : {:02x} ({})",
1052            reg_val.vendor_id(),
1053            if reg_val.vendor_id() == 0x0d {
1054                "AP"
1055            } else {
1056                "UNKNOWN"
1057            }
1058        );
1059        info!(
1060            "dev id       : {:02x} (generation {})",
1061            reg_val.dev_id(),
1062            reg_val.dev_id() + 1
1063        );
1064        info!(
1065            "density      : {:02x} ({} Mbit)",
1066            reg_val.density(),
1067            if reg_val.density() == 0x1 {
1068                32
1069            } else if reg_val.density() == 0x3 {
1070                64
1071            } else if reg_val.density() == 0x5 {
1072                128
1073            } else if reg_val.density() == 0x7 {
1074                256
1075            } else {
1076                0
1077            }
1078        );
1079        info!(
1080            "good-die     : {:02x} ({})",
1081            reg_val.gb(),
1082            if reg_val.gb() == 1 { "Pass" } else { "Fail" }
1083        );
1084        info!(
1085            "Latency      : {:02x} ({})",
1086            reg_val.lt(),
1087            if reg_val.lt() == 1 {
1088                "Fixed"
1089            } else {
1090                "Variable"
1091            }
1092        );
1093        info!(
1094            "VCC          : {:02x} ({})",
1095            reg_val.vcc(),
1096            if reg_val.vcc() == 1 { "3V" } else { "1.8V" }
1097        );
1098        info!(
1099            "SRF          : {:02x} ({} Refresh)",
1100            reg_val.srf(),
1101            if reg_val.srf() == 0x1 { "Fast" } else { "Slow" }
1102        );
1103        info!(
1104            "BurstType    : {:02x} ({} Wrap)",
1105            reg_val.bt(),
1106            if reg_val.bt() == 1 && reg_val.bl() != 3 {
1107                "Hybrid"
1108            } else {
1109                ""
1110            }
1111        );
1112        info!(
1113            "BurstLen     : {:02x} ({} Byte)",
1114            reg_val.bl(),
1115            if reg_val.bl() == 0x00 {
1116                16
1117            } else if reg_val.bl() == 0x01 {
1118                32
1119            } else if reg_val.bl() == 0x10 {
1120                64
1121            } else {
1122                1024
1123            }
1124        );
1125        info!(
1126            "Readlatency  : {:02x} ({} cycles@{})",
1127            reg_val.read_latency(),
1128            reg_val.read_latency() * 2 + 6,
1129            if reg_val.lt() == 1 {
1130                "Fixed"
1131            } else {
1132                "Variable"
1133            }
1134        );
1135        info!(
1136            "DriveStrength: {:02x} (1/{})",
1137            reg_val.drive_str(),
1138            if reg_val.drive_str() == 0x00 {
1139                1
1140            } else if reg_val.drive_str() == 0x01 {
1141                2
1142            } else if reg_val.drive_str() == 0x02 {
1143                4
1144            } else {
1145                8
1146            }
1147        );
1148    }
1149
1150    #[ram]
1151    fn spi0_timing_config_set_core_clock(core_clock: SpiTimingConfigCoreClock) {
1152        unsafe {
1153            SPI0::regs().core_clk_sel().modify(|_, w| {
1154                w.core_clk_sel().bits(match core_clock {
1155                    SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m => 0,
1156                    SpiTimingConfigCoreClock::SpiTimingConfigCoreClock120m => 1,
1157                    SpiTimingConfigCoreClock::SpiTimingConfigCoreClock160m => 2,
1158                    SpiTimingConfigCoreClock::SpiTimingConfigCoreClock240m => 3,
1159                })
1160            });
1161        }
1162    }
1163
1164    #[ram]
1165    fn flash_clock_divider(config: &PsramConfig) -> u32 {
1166        config.core_clock.unwrap_or_default() as u32 / config.flash_frequency as u32
1167    }
1168
1169    #[ram]
1170    fn psram_clock_divider(config: &PsramConfig) -> u32 {
1171        config.core_clock.unwrap_or_default() as u32 / config.ram_frequency as u32
1172    }
1173}
1174
1175#[ram]
1176fn spi0_timing_config_set_flash_clock(freqdiv: u32) {
1177    if freqdiv == 1 {
1178        SPI0::regs().clock().write(|w| unsafe {
1179            w.clk_equ_sysclk().set_bit();
1180            w.clkcnt_h().bits(0);
1181            w.clkcnt_n().bits(0);
1182            w.clkcnt_l().bits(0)
1183        });
1184    } else {
1185        SPI0::regs().clock().write(|w| unsafe {
1186            w.clk_equ_sysclk().clear_bit();
1187            w.clkcnt_h().bits((freqdiv / 2 - 1) as u8);
1188            w.clkcnt_n().bits((freqdiv - 1) as u8);
1189            w.clkcnt_l().bits((freqdiv - 1) as u8)
1190        });
1191    }
1192}
1193
1194#[ram]
1195fn spi1_timing_config_set_flash_clock(freqdiv: u32) {
1196    if freqdiv == 1 {
1197        SPI1::regs().clock().write(|w| unsafe {
1198            w.clk_equ_sysclk().set_bit();
1199            w.clkcnt_h().bits(0);
1200            w.clkcnt_n().bits(0);
1201            w.clkcnt_l().bits(0)
1202        });
1203    } else {
1204        SPI1::regs().clock().write(|w| unsafe {
1205            w.clk_equ_sysclk().clear_bit();
1206            w.clkcnt_h().bits((freqdiv / 2 - 1) as u8);
1207            w.clkcnt_n().bits((freqdiv - 1) as u8);
1208            w.clkcnt_l().bits((freqdiv - 1) as u8)
1209        });
1210    }
1211}