Skip to main content

esp_hal/psram/
esp32s2.rs

1use core::ops::Range;
2
3use super::{EXTMEM_ORIGIN, PsramSize};
4use crate::peripherals::EXTMEM;
5
6/// Frequency of PSRAM memory
7#[derive(Copy, Clone, Debug, Default, PartialEq)]
8#[cfg_attr(feature = "defmt", derive(defmt::Format))]
9pub enum SpiRamFreq {
10    /// PSRAM frequency 20 MHz
11    Freq20m = 20,
12    /// PSRAM frequency 26 MHz
13    Freq26m = 26,
14    /// PSRAM frequency 40 MHz
15    Freq40m = 40,
16    /// PSRAM frequency 80 MHz. Default for Espressif modules.
17    #[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/// PSRAM configuration
33#[derive(Copy, Clone, Debug, Default, PartialEq)]
34#[cfg_attr(feature = "defmt", derive(defmt::Format))]
35pub struct PsramConfig {
36    /// PSRAM size
37    pub size: PsramSize,
38    /// Frequency of PSRAM memory
39    pub ram_frequency: SpiRamFreq,
40}
41
42/// Initializes PSRAM to be used for data.
43#[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        /// Sets DCache mmu mapping.
60        ///
61        /// [`ext_ram`]: u32 DPORT_MMU_ACCESS_FLASH for flash, DPORT_MMU_ACCESS_SPIRAM for spiram, DPORT_MMU_INVALID for invalid.
62        /// [`vaddr`]: u32 Virtual address in CPU address space.
63        /// [`paddr`]: u32 Physical address in external memory. Should be aligned by psize.
64        /// [`psize`]: u32 Page size of DCache, in kilobytes. Should be 64 here.
65        /// [`num`]: u32 Pages to be set.
66        /// [`fixes`]: u32 0 for physical pages grow with virtual pages, other for virtual pages map to same physical page.
67        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            /// Enables Quad I/O pin functions.
157            ///
158            /// Sets the HD & WP pin functions for Quad I/O modes, based on the
159            /// efuse SPI pin configuration.
160            ///
161            /// [`wp_gpio_num`]: u8 Number of the WP pin to reconfigure for quad I/O
162            /// [`spiconfig`]: u32 Pin configuration, as returned from ets_efuse_get_spiconfig().
163            /// - If this parameter is 0, default SPI pins are used and wp_gpio_num parameter is
164            ///   ignored.
165            /// - If this parameter is 1, default HSPI pins are used and wp_gpio_num parameter is
166            ///   ignored.
167            /// - For other values, this parameter encodes the HD pin number and also the CLK pin
168            ///   number. CLK pin selection is used to determine if HSPI or SPI peripheral will be
169            ///   used (use HSPI if CLK pin is the HSPI clock pin, otherwise use SPI). Both HD & WP
170            ///   pins are configured via GPIO matrix to map to the selected peripheral.
171            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, // SPI_CLK_GPIO_NUM
176            flash_cs_io: 29,  // SPI_CS0_GPIO_NUM
177            psram_clk_io: 30,
178            psram_cs_io: 26,        // SPI_CS1_GPIO_NUM
179            psram_spiq_sd0_io: 31,  // SPI_Q_GPIO_NUM
180            psram_spid_sd1_io: 32,  // SPI_D_GPIO_NUM
181            psram_spiwp_sd3_io: 28, // SPI_WP_GPIO_NUM
182            psram_spihd_sd2_io: 27, // SPI_HD_GPIO_NUM
183        };
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                // FLASH pins(except wp / hd) are all configured via IO_MUX in
191                // rom.
192            } 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}