1use core::mem::MaybeUninit;
2
3#[cfg(multi_core)]
4use esp_hal::peripherals::CPU_CTRL;
5#[cfg(not(feature = "emulation"))]
6pub use esp_hal::peripherals::FLASH as Flash;
7#[cfg(multi_core)]
8use esp_hal::system::Cpu;
9#[cfg(multi_core)]
10use esp_hal::system::CpuControl;
11#[cfg(multi_core)]
12use esp_hal::system::is_running;
13
14use crate::chip_specific;
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16#[non_exhaustive]
17#[cfg_attr(feature = "defmt", derive(defmt::Format))]
18pub enum FlashStorageError {
20 IoError,
22 IoTimeout,
24 CantUnlock,
26 NotAligned,
28 OutOfBounds,
30 #[cfg(multi_core)]
35 OtherCoreRunning,
36 Other(i32),
38}
39
40#[inline(always)]
41pub fn check_rc(rc: i32) -> Result<(), FlashStorageError> {
43 match rc {
44 0 => Ok(()),
45 1 => Err(FlashStorageError::IoError),
46 2 => Err(FlashStorageError::IoTimeout),
47 _ => Err(FlashStorageError::Other(rc)),
48 }
49}
50
51#[cfg(feature = "emulation")]
52#[derive(Debug)]
53pub struct Flash<'d> {
54 _phantom: core::marker::PhantomData<&'d ()>,
55}
56
57#[cfg(feature = "emulation")]
58impl<'d> Flash<'d> {
59 pub fn new() -> Self {
60 Flash {
61 _phantom: core::marker::PhantomData,
62 }
63 }
64}
65
66#[derive(Debug)]
67#[cfg_attr(feature = "defmt", derive(defmt::Format))]
68pub struct FlashStorage<'d> {
80 pub(crate) capacity: usize,
81 unlocked: bool,
82 #[cfg(multi_core)]
83 pub(crate) multi_core_strategy: MultiCoreStrategy,
84 _flash: Flash<'d>,
85}
86
87impl<'d> FlashStorage<'d> {
88 pub const WORD_SIZE: u32 = 4;
90 pub const SECTOR_SIZE: u32 = 4096;
92 pub const BLOCK_SIZE: u32 = 65536;
94
95 pub fn new(flash: Flash<'d>) -> Self {
101 const ADDR: u32 = if cfg!(feature = "esp32c5") {
105 0x2000
106 } else if cfg!(any(feature = "esp32", feature = "esp32s2")) {
107 0x1000
108 } else {
109 0x0000
110 };
111
112 let mut storage = Self {
113 capacity: 0,
114 unlocked: false,
115 #[cfg(multi_core)]
116 multi_core_strategy: MultiCoreStrategy::Error,
117 _flash: flash,
118 };
119
120 let mut buffer = crate::buffer::FlashWordBuffer::uninit();
121 storage.internal_read(ADDR, buffer.as_bytes_mut()).unwrap();
122
123 let buffer = unsafe { buffer.assume_init_bytes() };
124 let mb = match buffer[3] & 0xf0 {
125 0x00 => 1,
126 0x10 => 2,
127 0x20 => 4,
128 0x30 => 8,
129 0x40 => 16,
130 0x50 => 32,
131 _ => 0,
132 };
133 storage.capacity = mb * 1024 * 1024;
134
135 storage
136 }
137
138 #[inline(always)]
139 pub(crate) fn check_alignment<const ALIGN: u32>(
140 &self,
141 offset: u32,
142 length: usize,
143 ) -> Result<(), FlashStorageError> {
144 let offset = offset as usize;
145 if offset % ALIGN as usize != 0 || length % ALIGN as usize != 0 {
146 return Err(FlashStorageError::NotAligned);
147 }
148 Ok(())
149 }
150
151 #[inline(always)]
152 pub(crate) fn check_bounds(&self, offset: u32, length: usize) -> Result<(), FlashStorageError> {
153 let offset = offset as usize;
154 if length > self.capacity || offset > self.capacity - length {
155 return Err(FlashStorageError::OutOfBounds);
156 }
157 Ok(())
158 }
159
160 pub(crate) fn internal_read(
161 &mut self,
162 offset: u32,
163 bytes: &mut [MaybeUninit<u8>],
164 ) -> Result<(), FlashStorageError> {
165 check_rc(chip_specific::spiflash_read(
166 offset,
167 bytes.as_mut_ptr() as *mut u32,
168 bytes.len() as u32,
169 ))
170 }
171
172 #[inline(always)]
173 fn unlock_once(&mut self) -> Result<(), FlashStorageError> {
174 if !self.unlocked {
175 if chip_specific::spiflash_unlock() != 0 {
176 return Err(FlashStorageError::CantUnlock);
177 }
178 self.unlocked = true;
179 }
180 Ok(())
181 }
182
183 pub(crate) fn internal_erase_sector(&mut self, sector: u32) -> Result<(), FlashStorageError> {
184 #[cfg(multi_core)]
185 let unpark = self.multi_core_strategy.pre_write()?;
186
187 self.unlock_once()?;
188 check_rc(chip_specific::spiflash_erase_sector(sector))?;
189
190 #[cfg(multi_core)]
191 self.multi_core_strategy.post_write(unpark);
192
193 Ok(())
194 }
195
196 pub(crate) fn internal_erase_block(&mut self, block: u32) -> Result<(), FlashStorageError> {
197 #[cfg(multi_core)]
198 let unpark = self.multi_core_strategy.pre_write()?;
199
200 self.unlock_once()?;
201 check_rc(chip_specific::spiflash_erase_block(block))?;
202
203 #[cfg(multi_core)]
204 self.multi_core_strategy.post_write(unpark);
205
206 Ok(())
207 }
208
209 pub(crate) fn internal_write(
210 &mut self,
211 offset: u32,
212 bytes: &[u8],
213 ) -> Result<(), FlashStorageError> {
214 #[cfg(multi_core)]
215 let unpark = self.multi_core_strategy.pre_write()?;
216
217 self.unlock_once()?;
218 check_rc(chip_specific::spiflash_write(
219 offset,
220 bytes.as_ptr() as *const u32,
221 bytes.len() as u32,
222 ))?;
223
224 #[cfg(multi_core)]
225 self.multi_core_strategy.post_write(unpark);
226
227 Ok(())
228 }
229}
230
231#[derive(Clone, Copy, PartialEq, Eq, Debug)]
234#[cfg_attr(feature = "defmt", derive(defmt::Format))]
235#[cfg(multi_core)]
236pub(crate) enum MultiCoreStrategy {
237 Error,
240 AutoPark,
242 Ignore,
246}
247
248#[cfg(multi_core)]
249impl<'d> FlashStorage<'d> {
250 pub fn multicore_auto_park(mut self) -> FlashStorage<'d> {
253 self.multi_core_strategy = MultiCoreStrategy::AutoPark;
254 self
255 }
256
257 pub unsafe fn multicore_ignore(mut self) -> FlashStorage<'d> {
263 self.multi_core_strategy = MultiCoreStrategy::Ignore;
264 self
265 }
266}
267
268#[cfg(multi_core)]
269impl MultiCoreStrategy {
270 pub(crate) fn pre_write(&self) -> Result<bool, FlashStorageError> {
276 let mut cpu_ctrl = CpuControl::new(unsafe { CPU_CTRL::steal() });
277 match self {
278 MultiCoreStrategy::Error => {
279 for other_cpu in Cpu::other() {
280 if is_running(other_cpu) {
281 return Err(FlashStorageError::OtherCoreRunning);
282 }
283 }
284 Ok(false)
285 }
286 MultiCoreStrategy::AutoPark => {
287 for other_cpu in Cpu::other() {
288 if is_running(other_cpu) {
289 unsafe { cpu_ctrl.park_core(other_cpu) };
290 return Ok(true);
291 }
292 }
293 Ok(false)
294 }
295 MultiCoreStrategy::Ignore => Ok(false),
296 }
297 }
298
299 pub(crate) fn post_write(&self, unpark: bool) {
305 let mut cpu_ctrl = CpuControl::new(unsafe { CPU_CTRL::steal() });
306 if let MultiCoreStrategy::AutoPark = self {
307 if unpark {
308 for other_cpu in Cpu::other() {
309 cpu_ctrl.unpark_core(other_cpu);
310 }
311 }
312 }
313 }
314}