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