1
//! Cache suspend and resume around ROM flash operations.
2
//!
3
//! Dropping cache lines for a flash range needs the MMU table and lives in
4
//! [`super::mmu`] instead.
5

            
6
use procmacros::ram;
7

            
8
/// Suspends the flash-backed caches for the duration of a ROM flash operation.
9
pub(super) struct CacheGuard {
10
    inner: Inner,
11
}
12

            
13
cfg_select! {
14
    esp32 => {
15
        struct Inner {
16
            pro: bool,
17
            app: bool,
18
        }
19
    }
20
    any(esp32s2, esp32s3) => {
21
        struct Inner {
22
            icache: u32,
23
            dcache: u32,
24
        }
25
    }
26
    esp32p4 => {
27
        // External flash/PSRAM is served by L2. L1 caches internal RAM; suspending
28
        // it stalls instruction fetch from IRAM (`#[ram]`).
29
        struct Inner {
30
            l2: u32,
31
        }
32
    }
33
    any(esp32h4, esp32s31) => {
34
        struct Inner {
35
            i0: u32,
36
            i1: u32,
37
            d: u32,
38
        }
39
    }
40
    _ => {
41
        struct Inner {
42
            autoload: u32,
43
        }
44
    }
45
}
46

            
47
impl CacheGuard {
48
    /// Disable the branch predictor (it issues cache requests) and suspend the
49
    /// external-memory caches.
50
    #[ram]
51
569
    pub(super) fn suspend() -> Self {
52
        // Runs while the cache is still on, so it may live in flash.
53
        #[cfg(soc_cpu_has_branch_predictor)]
54
        crate::soc::disable_branch_predictor();
55

            
56
569
        let inner = suspend_caches();
57
        Self { inner }
58
    }
59

            
60
    /// Flush every line. Must run while the caches are still off.
61
    #[cfg(esp32)]
62
    #[ram]
63
    pub(super) fn flush_while_off(&self) {
64
        unsafe {
65
            if self.inner.pro {
66
                Cache_Flush_rom(0);
67
            }
68
            if self.inner.app {
69
                Cache_Flush_rom(1);
70
            }
71
        }
72
    }
73
}
74

            
75
impl Drop for CacheGuard {
76
    #[ram]
77
569
    fn drop(&mut self) {
78
569
        resume_caches(&self.inner);
79

            
80
        // Runs once the cache is back on, so it may live in flash.
81
        #[cfg(soc_cpu_has_branch_predictor)]
82
        crate::soc::enable_branch_predictor();
83
    }
84
}
85

            
86
unsafe extern "C" {
87
    cfg_select! {
88
        esp32 => {
89
            fn Cache_Flush_rom(cpu: u32);
90
        }
91
        any(esp32s2, esp32s3) => {
92
            fn Cache_Suspend_ICache() -> u32;
93
            fn Cache_Suspend_DCache() -> u32;
94
            fn Cache_Resume_ICache(autoload: u32);
95
            fn Cache_Resume_DCache(autoload: u32);
96
        }
97
        esp32p4 => {
98
            fn Cache_Suspend_L2_Cache() -> u32;
99
            fn Cache_Resume_L2_Cache(autoload: u32);
100
        }
101
        esp32s31 => {
102
            fn Cache_Suspend_L1_CORE0_ICache() -> u32;
103
            fn Cache_Suspend_L1_CORE1_ICache() -> u32;
104
            fn Cache_Suspend_L1_DCache() -> u32;
105
            fn Cache_Resume_L1_CORE0_ICache(autoload: u32);
106
            fn Cache_Resume_L1_CORE1_ICache(autoload: u32);
107
            fn Cache_Resume_L1_DCache(autoload: u32);
108
        }
109
        // `map`: bit 0 is ICache0, bit 1 is ICache1, bit 4 is DCache, see
110
        // https://github.com/espressif/esp-idf/blob/188e3e55bb20505b5fa9dc49ee390bf42c58b4ee/components/esp_rom/esp32h4/include/esp32h4/rom/cache.h#L162-L164
111
        esp32h4 => {
112
            fn Cache_Suspend_Cache(map: u32) -> u32;
113
            fn Cache_Resume_Cache(map: u32, autoload: u32);
114
        }
115
        any(esp32c5, esp32c61) => {
116
            fn Cache_Suspend_Cache() -> u32;
117
            fn Cache_Resume_Cache(autoload: u32);
118
        }
119
        any(esp32c2, esp32c3, esp32c6, esp32h2) => {
120
            fn Cache_Suspend_ICache() -> u32;
121
            fn Cache_Resume_ICache(autoload: u32);
122
        }
123
        _ => {
124
            compile_error!("missing flash cache ROM bindings for this chip");
125
        }
126
    }
127
}
128

            
129
#[ram]
130
569
fn suspend_caches() -> Inner {
131
    cfg_select! {
132
        esp32 => {
133
            use crate::peripherals::DPORT;
134
            let dport = DPORT::regs();
135
            let pro = dport.pro_cache_ctrl().read().pro_cache_enable().bit();
136
            let app = dport.app_cache_ctrl().read().app_cache_enable().bit();
137
            // A cache must be idle before it is turned off, see `cache_ll_l1_disable_cache()`.
138
            if pro {
139
                while dport.pro_dcache_dbug0().read().pro_cache_state().bits() != 1 {}
140
                dport
141
                    .pro_cache_ctrl()
142
                    .modify(|_, w| w.pro_cache_enable().clear_bit());
143
            }
144
            if app {
145
                while dport.app_dcache_dbug0().read().app_cache_state().bits() != 1 {}
146
                dport
147
                    .app_cache_ctrl()
148
                    .modify(|_, w| w.app_cache_enable().clear_bit());
149
            }
150
            // Complete the writes above before the flash MMU is written.
151
            let _ = dport.pro_cache_ctrl().read();
152
            Inner { pro, app }
153
        }
154
        any(esp32s2, esp32s3) => {
155
            #[cfg(esp32s3)]
156
            use crate::peripherals::EXTMEM;
157
            unsafe {
158
                let icache = Cache_Suspend_ICache();
159
                // ROM `Cache_Suspend_I/DCache` on ESP32-S3 does not wait until
160
                // the cache FSM is idle (`ESP_ROM_HAS_CACHE_SUSPEND_WAITI_BUG`).
161
                #[cfg(esp32s3)]
162
                while EXTMEM::regs().cache_state().read().icache_state().bits() != 1 {}
163
                let dcache = Cache_Suspend_DCache();
164
                #[cfg(esp32s3)]
165
                while EXTMEM::regs().cache_state().read().dcache_state().bits() != 1 {}
166
                Inner { icache, dcache }
167
            }
168
        }
169
        esp32p4 => unsafe {
170
            Inner {
171
                l2: Cache_Suspend_L2_Cache(),
172
            }
173
        },
174
        esp32s31 => unsafe {
175
            Inner {
176
                i0: Cache_Suspend_L1_CORE0_ICache(),
177
                i1: Cache_Suspend_L1_CORE1_ICache(),
178
                d: Cache_Suspend_L1_DCache(),
179
            }
180
        },
181
        // Same order as `cache_ll_suspend_cache()`, see
182
        // https://github.com/espressif/esp-idf/blob/188e3e55bb20505b5fa9dc49ee390bf42c58b4ee/components/esp_hal_cache/esp32h4/include/hal/cache_ll.h#L295-L311
183
        esp32h4 => unsafe {
184
            Inner {
185
                i0: Cache_Suspend_Cache(1 << 0),
186
                i1: Cache_Suspend_Cache(1 << 1),
187
                d: Cache_Suspend_Cache(1 << 4),
188
            }
189
        },
190
        any(esp32c5, esp32c61) => unsafe {
191
            Inner {
192
                autoload: Cache_Suspend_Cache(),
193
            }
194
        },
195
        _ => unsafe {
196
            Inner {
197
569
                autoload: Cache_Suspend_ICache(),
198
            }
199
        },
200
    }
201
}
202

            
203
#[ram]
204
569
fn resume_caches(inner: &Inner) {
205
    cfg_select! {
206
        esp32 => {
207
            use crate::peripherals::DPORT;
208
            let dport = DPORT::regs();
209
            if inner.pro {
210
                dport
211
                    .pro_cache_ctrl()
212
                    .modify(|_, w| w.pro_cache_enable().set_bit());
213
            }
214
            if inner.app {
215
                dport
216
                    .app_cache_ctrl()
217
                    .modify(|_, w| w.app_cache_enable().set_bit());
218
            }
219
            // The caller returns to flash right away, so make sure that the cache is on again
220
            // before the next instruction is fetched: reading the register back completes the
221
            // write.
222
            let _ = dport.pro_cache_ctrl().read();
223
        }
224
        any(esp32s2, esp32s3) => unsafe {
225
            Cache_Resume_DCache(inner.dcache);
226
            Cache_Resume_ICache(inner.icache);
227
        },
228
        esp32p4 => unsafe { Cache_Resume_L2_Cache(inner.l2) },
229
        esp32s31 => unsafe {
230
            Cache_Resume_L1_DCache(inner.d);
231
            Cache_Resume_L1_CORE1_ICache(inner.i1);
232
            Cache_Resume_L1_CORE0_ICache(inner.i0);
233
        },
234
        esp32h4 => unsafe {
235
            Cache_Resume_Cache(1 << 4, inner.d);
236
            Cache_Resume_Cache(1 << 0, inner.i0);
237
            Cache_Resume_Cache(1 << 1, inner.i1);
238
        },
239
        any(esp32c5, esp32c61) => unsafe { Cache_Resume_Cache(inner.autoload) },
240
569
        _ => unsafe { Cache_Resume_ICache(inner.autoload) },
241
    }
242
}