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> {
70 pub(crate) capacity: usize,
71 unlocked: bool,
72 #[cfg(multi_core)]
73 pub(crate) multi_core_strategy: MultiCoreStrategy,
74 _flash: Flash<'d>,
75}
76
77impl<'d> FlashStorage<'d> {
78 pub const WORD_SIZE: u32 = 4;
80 pub const SECTOR_SIZE: u32 = 4096;
82
83 pub fn new(flash: Flash<'d>) -> Self {
89 #[cfg(not(any(feature = "esp32", feature = "esp32s2")))]
90 const ADDR: u32 = 0x0000;
91 #[cfg(any(feature = "esp32", feature = "esp32s2"))]
92 const ADDR: u32 = 0x1000;
93
94 let mut storage = Self {
95 capacity: 0,
96 unlocked: false,
97 #[cfg(multi_core)]
98 multi_core_strategy: MultiCoreStrategy::Error,
99 _flash: flash,
100 };
101
102 let mut buffer = crate::buffer::FlashWordBuffer::uninit();
103 storage.internal_read(ADDR, buffer.as_bytes_mut()).unwrap();
104
105 let buffer = unsafe { buffer.assume_init_bytes() };
106 let mb = match buffer[3] & 0xf0 {
107 0x00 => 1,
108 0x10 => 2,
109 0x20 => 4,
110 0x30 => 8,
111 0x40 => 16,
112 0x50 => 32,
113 _ => 0,
114 };
115 storage.capacity = mb * 1024 * 1024;
116
117 storage
118 }
119
120 #[inline(always)]
121 pub(crate) fn check_alignment<const ALIGN: u32>(
122 &self,
123 offset: u32,
124 length: usize,
125 ) -> Result<(), FlashStorageError> {
126 let offset = offset as usize;
127 if offset % ALIGN as usize != 0 || length % ALIGN as usize != 0 {
128 return Err(FlashStorageError::NotAligned);
129 }
130 Ok(())
131 }
132
133 #[inline(always)]
134 pub(crate) fn check_bounds(&self, offset: u32, length: usize) -> Result<(), FlashStorageError> {
135 let offset = offset as usize;
136 if length > self.capacity || offset > self.capacity - length {
137 return Err(FlashStorageError::OutOfBounds);
138 }
139 Ok(())
140 }
141
142 pub(crate) fn internal_read(
143 &mut self,
144 offset: u32,
145 bytes: &mut [MaybeUninit<u8>],
146 ) -> Result<(), FlashStorageError> {
147 check_rc(chip_specific::spiflash_read(
148 offset,
149 bytes.as_mut_ptr() as *mut u32,
150 bytes.len() as u32,
151 ))
152 }
153
154 #[inline(always)]
155 fn unlock_once(&mut self) -> Result<(), FlashStorageError> {
156 if !self.unlocked {
157 if chip_specific::spiflash_unlock() != 0 {
158 return Err(FlashStorageError::CantUnlock);
159 }
160 self.unlocked = true;
161 }
162 Ok(())
163 }
164
165 pub(crate) fn internal_erase(&mut self, sector: u32) -> Result<(), FlashStorageError> {
166 #[cfg(multi_core)]
167 let unpark = self.multi_core_strategy.pre_write()?;
168
169 self.unlock_once()?;
170 check_rc(chip_specific::spiflash_erase_sector(sector))?;
171
172 #[cfg(multi_core)]
173 self.multi_core_strategy.post_write(unpark);
174
175 Ok(())
176 }
177
178 pub(crate) fn internal_write(
179 &mut self,
180 offset: u32,
181 bytes: &[u8],
182 ) -> Result<(), FlashStorageError> {
183 #[cfg(multi_core)]
184 let unpark = self.multi_core_strategy.pre_write()?;
185
186 self.unlock_once()?;
187 check_rc(chip_specific::spiflash_write(
188 offset,
189 bytes.as_ptr() as *const u32,
190 bytes.len() as u32,
191 ))?;
192
193 #[cfg(multi_core)]
194 self.multi_core_strategy.post_write(unpark);
195
196 Ok(())
197 }
198}
199
200#[derive(Clone, Copy, PartialEq, Eq, Debug)]
203#[cfg_attr(feature = "defmt", derive(defmt::Format))]
204#[cfg(multi_core)]
205pub(crate) enum MultiCoreStrategy {
206 Error,
209 AutoPark,
211 Ignore,
215}
216
217#[cfg(multi_core)]
218impl<'d> FlashStorage<'d> {
219 pub fn multicore_auto_park(mut self) -> FlashStorage<'d> {
222 self.multi_core_strategy = MultiCoreStrategy::AutoPark;
223 self
224 }
225
226 pub unsafe fn multicore_ignore(mut self) -> FlashStorage<'d> {
232 self.multi_core_strategy = MultiCoreStrategy::Ignore;
233 self
234 }
235}
236
237#[cfg(multi_core)]
238impl MultiCoreStrategy {
239 pub(crate) fn pre_write(&self) -> Result<bool, FlashStorageError> {
245 let mut cpu_ctrl = CpuControl::new(unsafe { CPU_CTRL::steal() });
246 match self {
247 MultiCoreStrategy::Error => {
248 for other_cpu in Cpu::other() {
249 if is_running(other_cpu) {
250 return Err(FlashStorageError::OtherCoreRunning);
251 }
252 }
253 Ok(false)
254 }
255 MultiCoreStrategy::AutoPark => {
256 for other_cpu in Cpu::other() {
257 if is_running(other_cpu) {
258 unsafe { cpu_ctrl.park_core(other_cpu) };
259 return Ok(true);
260 }
261 }
262 Ok(false)
263 }
264 MultiCoreStrategy::Ignore => Ok(false),
265 }
266 }
267
268 pub(crate) fn post_write(&self, unpark: bool) {
274 let mut cpu_ctrl = CpuControl::new(unsafe { CPU_CTRL::steal() });
275 if let MultiCoreStrategy::AutoPark = self {
276 if unpark {
277 for other_cpu in Cpu::other() {
278 cpu_ctrl.unpark_core(other_cpu);
279 }
280 }
281 }
282 }
283}