Skip to main content

esp_hal/psram/
esp32c5_c61.rs

1use core::ops::Range;
2
3use super::{EXTMEM_ORIGIN, PsramSize};
4use crate::{
5    peripherals::{CACHE, SPI0, SPI1},
6    psram::implem::quad::MspiTimingSpeedMode,
7};
8
9mod quad;
10
11use procmacros::ram;
12use quad::CommandMode;
13
14const FUNC_SPICS1_SPICS1: u8 = 0;
15const PIN_FUNC_GPIO: u8 = 1;
16
17cfg_select! {
18    esp32c61 => {
19        const PSRAM_CS_IO: u8 = 14;
20        const SPI_CS1_GPIO_NUM: u8 = 14;
21        const SPICS1_OUT_IDX: u8 = 102;
22        const PSRAM_SPIWP_SD3_IO: u8 = 17;
23    }
24    esp32c5 => {
25        const PSRAM_CS_IO: u8 = 15;
26        const SPI_CS1_GPIO_NUM: u8 = 15;
27        const SPICS1_OUT_IDX: u8 = 101;
28        const PSRAM_SPIWP_SD3_IO: u8 = 18;
29    }
30    _ => {}
31}
32
33#[derive(Copy, Clone, Debug, PartialEq, Eq)]
34pub(crate) enum PsramLlCsIdT {
35    PsramLlCsId0,
36    PsramLlCsId1,
37}
38
39/// Frequency of flash memory
40#[derive(Copy, Clone, Debug, Default, PartialEq)]
41#[cfg_attr(feature = "defmt", derive(defmt::Format))]
42pub enum FlashFreq {
43    /// Flash frequency 40 MHz
44    FlashFreq40m = 40,
45    /// Flash frequency 80 MHz
46    #[default]
47    FlashFreq80m = 80,
48}
49
50impl FlashFreq {
51    fn mhz(&self) -> u32 {
52        match self {
53            FlashFreq::FlashFreq40m => 40,
54            FlashFreq::FlashFreq80m => 80,
55        }
56    }
57}
58
59/// Frequency of PSRAM memory
60#[derive(Copy, Clone, Debug, Default, PartialEq)]
61#[cfg_attr(feature = "defmt", derive(defmt::Format))]
62pub enum SpiRamFreq {
63    /// PSRAM frequency 40 MHz
64    #[default]
65    Freq40m = 40,
66    /// PSRAM frequency 80 MHz
67    Freq80m = 80,
68}
69
70impl SpiRamFreq {
71    pub(crate) fn need_timing_tuning(&self) -> bool {
72        *self != SpiRamFreq::Freq40m
73    }
74
75    fn mhz(&self) -> u32 {
76        match self {
77            SpiRamFreq::Freq40m => 40,
78            SpiRamFreq::Freq80m => 80,
79        }
80    }
81}
82
83/// MSPI timing tuning parameters.
84#[derive(Copy, Clone, Debug, Default, PartialEq)]
85#[cfg_attr(feature = "defmt", derive(defmt::Format))]
86pub struct MspiTimingTuningParam {
87    /// Input signal delay mode
88    pub spi_din_mode: u8,
89    /// Input signal delay number
90    pub spi_din_num: u8,
91    /// Extra dummy length
92    pub extra_dummy_len: u8,
93}
94
95/// PSRAM configuration
96#[derive(Copy, Clone, Debug, PartialEq)]
97#[cfg_attr(feature = "defmt", derive(defmt::Format))]
98pub struct PsramConfig {
99    /// PSRAM size
100    pub size: PsramSize,
101    /// Frequency of flash memory
102    pub flash_frequency: FlashFreq,
103    /// Flash timing tuning configuration.
104    /// Used as needed.
105    pub flash_tuning: MspiTimingTuningParam,
106    /// Frequency of PSRAM memory
107    pub ram_frequency: SpiRamFreq,
108    /// PSRAM timing tuning configuration.
109    /// Used as needed.
110    pub ram_tuning: MspiTimingTuningParam,
111}
112
113impl Default for PsramConfig {
114    fn default() -> Self {
115        Self {
116            size: Default::default(),
117            flash_frequency: Default::default(),
118            flash_tuning: MspiTimingTuningParam {
119                spi_din_mode: 3,
120                spi_din_num: 1,
121                extra_dummy_len: 2,
122            },
123            ram_frequency: Default::default(),
124            ram_tuning: MspiTimingTuningParam {
125                spi_din_mode: 3,
126                spi_din_num: 1,
127                extra_dummy_len: 2,
128            },
129        }
130    }
131}
132
133/// Initialize PSRAM to be used for data.
134#[procmacros::ram]
135pub(crate) fn init_psram(config: &mut PsramConfig) -> bool {
136    quad::psram_init(config);
137    true
138}
139
140#[procmacros::ram]
141pub(crate) fn map_psram(config: PsramConfig) -> Range<usize> {
142    const MMU_PAGE_SIZE: u32 = property!("mmu.page_size");
143    const FLASH_MMU_TABLE_SIZE: u32 = property!("mmu.entry_num");
144
145    fn select_mmu_entry(entry_id: u32) {
146        SPI0::regs()
147            .mmu_item_index()
148            .write(|w| unsafe { w.mmu_item_index().bits(entry_id) });
149    }
150
151    fn mmu_entry_is_valid(entry_id: u32) -> bool {
152        select_mmu_entry(entry_id);
153        SPI0::regs().mmu_item_content().read().valid().bit()
154    }
155
156    fn write_psram_mmu_entry(entry_id: u32, page: u16) {
157        select_mmu_entry(entry_id);
158        SPI0::regs().mmu_item_content().write(|w| {
159            unsafe { w.paddr().bits(page) };
160            w.access_spiram().set_bit();
161            w.valid().set_bit()
162        });
163    }
164
165    // calculate the PSRAM start address to map
166    // the linker scripts can produce a gap between mapped IROM and DROM segments
167    // bigger than a flash page - i.e. we will see an unmapped memory slot
168    // start from the end and find the last mapped flash page
169    let mut mapped_pages = 0;
170
171    // the bootloader is using the last page to access flash internally
172    // (e.g. to read the app descriptor) so we just skip that
173    for i in (0..(FLASH_MMU_TABLE_SIZE - 1)).rev() {
174        if mmu_entry_is_valid(i) {
175            mapped_pages = i + 1;
176            break;
177        }
178    }
179    let start = EXTMEM_ORIGIN as u32 + (MMU_PAGE_SIZE * mapped_pages);
180    debug!("PSRAM start address = {:x}", start);
181
182    for i in 0..config.size.get() as u32 / MMU_PAGE_SIZE {
183        write_psram_mmu_entry(i + mapped_pages, i as u16);
184    }
185
186    // enable busses
187    CACHE::regs().cache_ctrl().modify(|_, w| {
188        w.cache_shut_bus0().clear_bit();
189        w.cache_shut_bus1().clear_bit()
190    });
191
192    start as usize..start as usize + config.size.get()
193}
194
195fn core_clock_for_mode(_speed_mode: MspiTimingSpeedMode) -> u32 {
196    // in all cases (low and high speed) we use an MSPI clock of 80 MHz on this target
197    80
198}
199
200mod ctrlr_ll {
201    use super::*;
202
203    pub(crate) const PSRAM_CTRLR_LL_MSPI_ID_0: u32 = 0;
204
205    #[inline(always)]
206    pub(crate) fn psram_ctrlr_ll_set_cs_hold(_mspi_id: u32, hold_n: u32) {
207        assert!(hold_n > 0);
208
209        SPI0::regs().smem_ac().modify(|_, w| {
210            w.smem_cs_hold().set_bit();
211            unsafe {
212                w.smem_cs_hold_time().bits(hold_n as u8 - 1);
213            }
214            w
215        });
216    }
217
218    #[inline(always)]
219    pub(crate) fn psram_ctrlr_ll_set_cs_setup(_mspi_id: u32, setup_n: u32) {
220        assert!(setup_n > 0);
221
222        SPI0::regs().smem_ac().modify(|_, w| {
223            w.smem_cs_setup().set_bit();
224            unsafe {
225                w.smem_cs_setup_time().bits(setup_n as u8 - 1);
226            }
227            w
228        });
229    }
230
231    #[inline(always)]
232    pub(crate) fn psram_ctrlr_ll_set_read_mode(mspi_id: u32, read_mode: CommandMode) {
233        assert!(mspi_id == 0);
234
235        SPI0::regs().cache_sctrl().modify(|_, w| {
236            w.usr_sram_dio().bit(read_mode == CommandMode::PsramCmdSpi);
237            w.usr_sram_qio().bit(read_mode == CommandMode::PsramCmdQpi);
238            w
239        });
240    }
241
242    /// Set PSRAM write cmd
243    #[inline(always)]
244    pub(crate) fn psram_ctrlr_ll_set_wr_cmd(_mspi_id: u32, cmd_bitlen: u32, cmd_val: u32) {
245        assert!(cmd_bitlen > 0);
246        SPI0::regs()
247            .cache_sctrl()
248            .modify(|_, w| w.cache_sram_usr_wcmd().set_bit());
249
250        SPI0::regs().sram_dwr_cmd().modify(|_, w| {
251            unsafe { w.cache_sram_usr_wr_cmd_bitlen().bits(cmd_bitlen as u8 - 1) };
252            unsafe { w.cache_sram_usr_wr_cmd_value().bits(cmd_val as u16) };
253            w
254        });
255    }
256
257    /// Set PSRAM read cmd
258    #[inline(always)]
259    pub(crate) fn psram_ctrlr_ll_set_rd_cmd(_mspi_id: u32, cmd_bitlen: u32, cmd_val: u32) {
260        assert!(cmd_bitlen > 0);
261
262        SPI0::regs()
263            .cache_sctrl()
264            .modify(|_, w| w.cache_sram_usr_rcmd().set_bit());
265
266        SPI0::regs().sram_drd_cmd().modify(|_, w| {
267            unsafe { w.cache_sram_usr_rd_cmd_bitlen().bits(cmd_bitlen as u8 - 1) };
268            unsafe { w.cache_sram_usr_rd_cmd_value().bits(cmd_val as u16) };
269            w
270        });
271    }
272
273    /// Set PSRAM addr bitlen
274    #[inline(always)]
275    pub(crate) fn psram_ctrlr_ll_set_addr_bitlen(_mspi_id: u32, addr_bitlen: u32) {
276        assert!(addr_bitlen > 0);
277
278        SPI0::regs()
279            .cache_sctrl()
280            .modify(|_, w| unsafe { w.sram_addr_bitlen().bits(addr_bitlen as u8 - 1) });
281    }
282
283    /// Set PSRAM read dummy
284    #[inline(always)]
285    pub(crate) fn psram_ctrlr_ll_set_rd_dummy(_mspi_id: u32, dummy_n: u32) {
286        assert!(dummy_n > 0);
287
288        SPI0::regs().cache_sctrl().modify(|_, w| {
289            w.usr_rd_sram_dummy().set_bit();
290            unsafe { w.sram_rdummy_cyclelen().bits(dummy_n as u8 - 1) };
291            w
292        });
293    }
294
295    /// Select which pin to use for the psram
296    #[inline(always)]
297    pub(crate) fn psram_ctrlr_ll_set_cs_pin(_mspi_id: u32, cs_id: PsramLlCsIdT) {
298        SPI1::regs().misc().modify(|_, w| {
299            w.cs0_dis().bit(cs_id == PsramLlCsIdT::PsramLlCsId0);
300            w.cs1_dis().bit(cs_id == PsramLlCsIdT::PsramLlCsId1);
301            w
302        });
303    }
304}
305
306#[ram]
307fn mspi_timing_config_set_flash_clock(flash_freq_mhz: u32, speed_mode: quad::MspiTimingSpeedMode) {
308    let core_clock_mhz = core_clock_for_mode(speed_mode);
309    // SPI0 and SPI1 share the register for core clock. So we only set SPI0 here.
310    mspi_timing_ll_set_core_clock(core_clock_mhz);
311
312    let freqdiv: u32 = core_clock_mhz / flash_freq_mhz;
313    debug!("flash freqdiv: {}", freqdiv);
314    assert!(freqdiv > 0);
315    let reg_val: u32 = mspi_timing_ll_calculate_clock_reg(freqdiv);
316    mspi_timing_ll_set_flash_clock(0, reg_val);
317    mspi_timing_ll_set_flash_clock(1, reg_val);
318}
319
320/// Set MSPI_FAST_CLK's high-speed divider (valid when SOC_ROOT clock source is PLL)
321#[inline(always)]
322pub(crate) fn mspi_timing_ll_set_core_clock(core_clk_mhz: u32) {
323    let divider = match core_clk_mhz {
324        80 => 6,
325        _ => panic!("Invalid core clock"),
326    };
327
328    crate::peripherals::PCR::regs()
329        .mspi_clk_conf()
330        .modify(|_, w| unsafe { w.mspi_fast_div_num().bits(divider - 1) });
331}
332
333/// Calculate spi_flash clock frequency division parameters for register.
334#[inline(always)]
335fn mspi_timing_ll_calculate_clock_reg(clkdiv: u32) -> u32 {
336    if clkdiv == 1 {
337        1 << 31
338    } else {
339        (clkdiv - 1) | ((((clkdiv - 1) / 2) & 0xff) << 8) | (((clkdiv - 1) & 0xff) << 16)
340    }
341}
342
343/// Set Flash clock
344#[inline(always)]
345fn mspi_timing_ll_set_flash_clock(mspi_id: u32, clock_conf: u32) {
346    if mspi_id == 0 {
347        SPI0::regs()
348            .clock()
349            .write(|w| unsafe { w.bits(clock_conf) });
350    } else {
351        SPI1::regs()
352            .clock()
353            .write(|w| unsafe { w.bits(clock_conf) });
354    }
355}
356
357#[ram]
358fn mspi_timing_config_set_psram_clock(psram_freq_mhz: u32, speed_mode: quad::MspiTimingSpeedMode) {
359    // bool control_both_mspi = true
360    let core_clock_mhz = core_clock_for_mode(speed_mode);
361
362    // SPI0 and SPI1 share the register for core clock. So we only set SPI0 here.
363    mspi_timing_ll_set_core_clock(core_clock_mhz);
364
365    let freqdiv: u32 = core_clock_mhz / psram_freq_mhz;
366    debug!("psram freqdiv: {}", freqdiv);
367    assert!(freqdiv > 0);
368    let reg_val: u32 = mspi_timing_ll_calculate_clock_reg(freqdiv);
369    mspi_timing_ll_set_psram_clock(reg_val);
370}
371
372/// Set PSRAM clock
373#[inline(always)]
374fn mspi_timing_ll_set_psram_clock(clock_conf: u32) {
375    SPI0::regs()
376        .sram_clk()
377        .write(|w| unsafe { w.bits(clock_conf) });
378}
379
380/// Set MSPI Flash din mode
381#[inline(always)]
382pub(crate) fn mspi_timing_ll_set_flash_din_mode(mspi_id: u8, din_mode: u8) {
383    assert!(mspi_id == 0);
384    assert!(din_mode <= 7);
385
386    SPI0::regs().din_mode().modify(|_, w| {
387        unsafe {
388            w.din0_mode().bits(din_mode);
389            w.din1_mode().bits(din_mode);
390            w.din2_mode().bits(din_mode);
391            w.din3_mode().bits(din_mode);
392            w.din4_mode().bits(din_mode);
393            w.din5_mode().bits(din_mode);
394            w.din6_mode().bits(din_mode);
395            w.din7_mode().bits(din_mode);
396            w.dins_mode().bits(din_mode);
397        }
398
399        w
400    });
401
402    SPI0::regs()
403        .timing_cali()
404        .modify(|_, w| w.update().set_bit());
405}
406
407/// Set MSPI Flash din num
408#[inline(always)]
409pub(crate) fn mspi_timing_ll_set_flash_din_num(mspi_id: u8, din_num: u8) {
410    assert!(mspi_id == 0);
411    assert!(din_num <= 3);
412
413    SPI0::regs().din_num().modify(|_, w| {
414        unsafe {
415            w.din0_num().bits(din_num);
416            w.din1_num().bits(din_num);
417            w.din2_num().bits(din_num);
418            w.din3_num().bits(din_num);
419            w.din4_num().bits(din_num);
420            w.din5_num().bits(din_num);
421            w.din6_num().bits(din_num);
422            w.din7_num().bits(din_num);
423            w.dins_num().bits(din_num);
424        }
425
426        w
427    });
428
429    SPI0::regs()
430        .timing_cali()
431        .modify(|_, w| w.update().set_bit());
432}
433
434/// Set MSPI Flash extra dummy
435#[inline(always)]
436pub(crate) fn mspi_timing_ll_set_flash_extra_dummy(mspi_id: u8, extra_dummy: u8) {
437    if mspi_id == 0 {
438        SPI0::regs().timing_cali().modify(|_, w| {
439            w.timing_cali().bit(extra_dummy > 0);
440            unsafe { w.extra_dummy_cyclelen().bits(extra_dummy) };
441            w
442        });
443    } else {
444        SPI1::regs().timing_cali().modify(|_, w| {
445            w.timing_cali().bit(extra_dummy > 0);
446            unsafe { w.extra_dummy_cyclelen().bits(extra_dummy) };
447            w
448        });
449    }
450
451    SPI0::regs()
452        .timing_cali()
453        .modify(|_, w| w.update().set_bit());
454}
455
456/// Set MSPI PSRAM din mode
457#[inline(always)]
458pub(crate) fn mspi_timing_ll_set_psram_din_mode(mspi_id: u8, din_mode: u8) {
459    assert!(mspi_id == 0);
460    assert!(din_mode <= 7);
461
462    SPI0::regs().smem_din_mode().modify(|_, w| {
463        unsafe {
464            w.smem_din0_mode().bits(din_mode);
465            w.smem_din1_mode().bits(din_mode);
466            w.smem_din2_mode().bits(din_mode);
467            w.smem_din3_mode().bits(din_mode);
468            w.smem_din4_mode().bits(din_mode);
469            w.smem_din5_mode().bits(din_mode);
470            w.smem_din6_mode().bits(din_mode);
471            w.smem_din7_mode().bits(din_mode);
472            w.smem_dins_mode().bits(din_mode);
473        }
474
475        w
476    });
477
478    SPI0::regs()
479        .timing_cali()
480        .modify(|_, w| w.update().set_bit());
481}
482
483/// Set MSPI PSRAM din num
484#[inline(always)]
485pub(crate) fn mspi_timing_ll_set_psram_din_num(mspi_id: u8, din_num: u8) {
486    assert!(mspi_id == 0);
487    assert!(din_num <= 3);
488
489    SPI0::regs().smem_din_num().modify(|_, w| {
490        unsafe {
491            w.smem_din0_num().bits(din_num);
492            w.smem_din1_num().bits(din_num);
493            w.smem_din2_num().bits(din_num);
494            w.smem_din3_num().bits(din_num);
495            w.smem_din4_num().bits(din_num);
496            w.smem_din5_num().bits(din_num);
497            w.smem_din6_num().bits(din_num);
498            w.smem_din7_num().bits(din_num);
499            w.smem_dins_num().bits(din_num);
500        }
501
502        w
503    });
504
505    SPI0::regs()
506        .timing_cali()
507        .modify(|_, w| w.update().set_bit());
508}
509
510/// Set MSPI PSRAM extra dummy
511#[inline(always)]
512pub(crate) fn mspi_timing_ll_set_psram_extra_dummy(mspi_id: u8, extra_dummy: u8) {
513    assert!(mspi_id == 0);
514
515    SPI0::regs().smem_timing_cali().modify(|_, w| {
516        w.smem_timing_cali().bit(extra_dummy > 0);
517        unsafe { w.smem_extra_dummy_cyclelen().bits(extra_dummy) };
518        w
519    });
520
521    SPI0::regs()
522        .timing_cali()
523        .modify(|_, w| w.update().set_bit());
524}
525
526/// Get MSPI flash dummy info
527#[inline(always)]
528pub(crate) fn mspi_timing_ll_get_flash_dummy(mspi_id: u8) -> (u8, u8) {
529    assert!(mspi_id <= 1);
530
531    if mspi_id == 0 {
532        let usr_dummy = SPI0::regs().user1().read().usr_dummy_cyclelen().bits();
533        let extra_dummy = SPI0::regs()
534            .timing_cali()
535            .read()
536            .extra_dummy_cyclelen()
537            .bits();
538        (usr_dummy, extra_dummy)
539    } else {
540        let usr_dummy = SPI1::regs().user1().read().usr_dummy_cyclelen().bits();
541        let extra_dummy = SPI1::regs()
542            .timing_cali()
543            .read()
544            .extra_dummy_cyclelen()
545            .bits();
546        (usr_dummy, extra_dummy)
547    }
548}
549
550/// Get MSPI PSRAM dummy info
551#[inline(always)]
552pub(crate) fn mspi_timing_ll_get_psram_dummy(mspi_id: u8) -> (u8, u8) {
553    assert!(mspi_id == 0);
554
555    let usr_dummy = SPI0::regs()
556        .cache_sctrl()
557        .read()
558        .sram_rdummy_cyclelen()
559        .bits();
560    let extra_dummy = SPI0::regs()
561        .smem_timing_cali()
562        .read()
563        .smem_extra_dummy_cyclelen()
564        .bits();
565    (usr_dummy, extra_dummy)
566}