Skip to main content

esp_hal/psram/
esp32s31.rs

1//! PSRAM driver for ESP32-S31.
2
3use super::{EXTMEM_ORIGIN, PsramSize};
4use crate::{
5    clock::ll::{ClockTree, PsramFunctionClockConfig, PsramInstance},
6    peripherals::HP_SYS_CLKRST,
7};
8
9mod oct_hex;
10
11/// PSRAM configuration.
12#[derive(Copy, Clone, Debug, Default, PartialEq)]
13#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14#[instability::unstable]
15pub struct PsramConfig {
16    /// Size of PSRAM to map. Default: `AutoDetect` via MR2 density.
17    pub size: PsramSize,
18
19    /// PSRAM timing parameters. Default: 250 MHz.
20    pub timing: PsramTimingParams,
21    // TODO: ECC enable.
22    // The MSPI0 controller has a ECC engine.
23    // pub ecc: bool, // or any other enum.
24}
25
26/// PSRAM timing parameters.
27///
28/// These values should be tuned together with the source clock frequency. The source clock
29/// frequency must be an integer multiple of the PSRAM frequency.
30#[derive(Copy, Clone, Debug, PartialEq)]
31#[cfg_attr(feature = "defmt", derive(defmt::Format))]
32pub struct PsramTimingParams {
33    /// PSRAM clock source.
34    pub clock_source: PsramFunctionClockConfig,
35
36    /// Bus clock frequency in MHz. Must divide the source clock evenly.
37    pub clock: u32,
38
39    /// MR0.read_latency field value (cycles = 2 * value + 6).
40    pub mr0_rl: u8,
41
42    /// MR4.wr_latency field value.
43    pub mr4_wl: u8,
44
45    /// Read dummy length in bits for sync data reads (cache path).
46    ///
47    /// For `N` dummy bits, configure `reg_dummy_bits` to `N - 1`.
48    pub rd_dummy_bits: u8,
49
50    /// Write dummy length in bits for sync data writes (cache path).
51    ///
52    /// For `N` dummy bits, configure `reg_dummy_bits` to `N - 1`.
53    pub wr_dummy_bits: u8,
54
55    /// Register-read dummy length for direct command path (MSPI3).
56    pub reg_dummy_bits: u32,
57}
58
59impl Default for PsramTimingParams {
60    fn default() -> Self {
61        Self::MHZ_250
62    }
63}
64
65impl PsramTimingParams {
66    /// Preset for 20 MHz clock speed.
67    pub const MHZ_20: Self = Self {
68        clock_source: PsramFunctionClockConfig::Mpll,
69        clock: 20,
70        mr0_rl: 2,
71        mr4_wl: 2,
72        rd_dummy_bits: 17,
73        wr_dummy_bits: 7,
74        reg_dummy_bits: 8,
75    };
76
77    /// Preset for 80 MHz clock speed.
78    pub const MHZ_80: Self = Self {
79        clock_source: PsramFunctionClockConfig::Mpll,
80        clock: 80,
81        mr0_rl: 2,
82        mr4_wl: 2,
83        rd_dummy_bits: 17,
84        wr_dummy_bits: 7,
85        reg_dummy_bits: 8,
86    };
87
88    /// Preset for 125 MHz clock speed.
89    pub const MHZ_125: Self = Self {
90        clock_source: PsramFunctionClockConfig::Mpll,
91        clock: 125,
92        mr0_rl: 2,
93        mr4_wl: 2,
94        rd_dummy_bits: 17,
95        wr_dummy_bits: 7,
96        reg_dummy_bits: 8,
97    };
98
99    /// Preset for 200 MHz clock speed.
100    pub const MHZ_200: Self = Self {
101        clock_source: PsramFunctionClockConfig::Mpll,
102        clock: 200,
103        mr0_rl: 4,
104        mr4_wl: 1,
105        rd_dummy_bits: 25,
106        wr_dummy_bits: 11,
107        reg_dummy_bits: 12,
108    };
109
110    /// Preset for 250 MHz clock speed.
111    pub const MHZ_250: Self = Self {
112        clock_source: PsramFunctionClockConfig::Mpll,
113        clock: 250,
114        mr0_rl: 6,
115        mr4_wl: 3,
116        rd_dummy_bits: 33,
117        wr_dummy_bits: 15,
118        reg_dummy_bits: 16,
119    };
120}
121
122/// Initialize PSRAM.
123#[crate::ram]
124pub(crate) fn init_psram(config: &mut PsramConfig) -> bool {
125    // Module clock + clock source
126    enable_psram_mspi();
127    reset_psram_mspi();
128
129    ClockTree::with(|clocks| {
130        PsramInstance::Psram.configure_function_clock(clocks, config.timing.clock_source);
131        PsramInstance::Psram.request_function_clock(clocks);
132    });
133
134    // Controller + PHY pad bring-up.
135    if !oct_hex::set_bus_clock(config.timing.clock) {
136        return false;
137    }
138    oct_hex::enable_dll();
139    oct_hex::psram_pad_init(false); // required for DDR strobe latch
140    oct_hex::set_cs_timing();
141
142    // SoC MR init (via MSPI3 SPI direct)
143    oct_hex::init_mr_registers(&config.timing, false);
144
145    if config.size.is_auto() {
146        config.size = PsramSize::Size(oct_hex::psram_detect_size(&config.timing));
147    }
148
149    // basic AXI configuration here
150    oct_hex::configure_psram_mspi(&config.timing, false);
151    oct_hex::mmu_map_psram(config.size.get());
152
153    true
154}
155
156pub(crate) fn map_psram(config: PsramConfig) -> core::ops::Range<usize> {
157    let start = EXTMEM_ORIGIN;
158    start..start + config.size.get()
159}
160
161fn enable_psram_mspi() {
162    HP_SYS_CLKRST::regs()
163        .psram_ctrl0()
164        .modify(|_, w| w.sys_clk_en().set_bit());
165}
166
167fn reset_psram_mspi() {
168    HP_SYS_CLKRST::regs().psram_ctrl0().modify(|_, w| {
169        w.axi_rst_en().set_bit();
170        w.apb_rst_en().set_bit()
171    });
172    HP_SYS_CLKRST::regs().psram_ctrl0().modify(|_, w| {
173        w.axi_rst_en().clear_bit();
174        w.apb_rst_en().clear_bit()
175    });
176}