apollo3/
flashctrl.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Flash Controller
6
7use core::cell::Cell;
8use core::ops::{Index, IndexMut};
9use kernel::deferred_call::{DeferredCall, DeferredCallClient};
10use kernel::hil;
11use kernel::utilities::cells::OptionalCell;
12use kernel::utilities::cells::TakeCell;
13use kernel::ErrorCode;
14
15pub const PAGE_SIZE: usize = 8 * 1024;
16pub const FLASH_INSTANCE_SIZE: usize = 512 * 1024;
17pub const FLASH_NUM_INSTANCES: usize = 2;
18pub const FLASH_PAGES_PER_INSTANCE: usize = FLASH_INSTANCE_SIZE / PAGE_SIZE;
19pub const FLASH_MAX_PAGES: usize = FLASH_NUM_INSTANCES * FLASH_PAGES_PER_INSTANCE;
20
21const FLASH_PROGRAM_KEY: u32 = 0x12344321;
22
23/// There are two flash instances, each is 512KiB.
24#[derive(PartialEq)]
25enum FlashInstance {
26    MAIN0 = 0,
27    MAIN1 = 1,
28}
29
30pub struct Apollo3Page(pub [u8; PAGE_SIZE]);
31
32impl Default for Apollo3Page {
33    fn default() -> Self {
34        Self([0; PAGE_SIZE])
35    }
36}
37
38impl Index<usize> for Apollo3Page {
39    type Output = u8;
40
41    fn index(&self, idx: usize) -> &u8 {
42        &self.0[idx]
43    }
44}
45
46impl IndexMut<usize> for Apollo3Page {
47    fn index_mut(&mut self, idx: usize) -> &mut u8 {
48        &mut self.0[idx]
49    }
50}
51
52impl AsMut<[u8]> for Apollo3Page {
53    fn as_mut(&mut self) -> &mut [u8] {
54        &mut self.0
55    }
56}
57
58/// This function can be used to read / write arbitrary flash memory, and thus
59/// arbitrary program code. As such, they are unsafe operations. We also can't
60/// confirm that the functions are safe in the context of Rust.
61///
62/// The below documentation is based on the official HAL documentation.
63///
64/// This function will call the chip ROM code to perform the below operation.
65///
66/// Use this function to safely read a value from peripheral or memory locations.
67///
68/// `addr` - The location to be read.
69///
70/// @return The value read from the given address.
71unsafe fn flash_util_read_word(addr: *mut u32) -> u32 {
72    // Call `uint32_t flash_util_read_word(uint32_t *)` in the ROM code.
73    let flash_util_read_word: unsafe extern "C" fn(*mut u32) -> u32 =
74        unsafe { core::mem::transmute(0x08000075 as *const ()) };
75
76    flash_util_read_word(addr)
77}
78
79/// This function can be used to read / write arbitrary flash memory, and thus
80/// arbitrary program code. As such, they are unsafe operations. We also can't
81/// confirm that the functions are safe in the context of Rust.
82///
83/// The below documentation is based on the official HAL documentation.
84///
85/// This function will call the chip ROM code to perform the below operation.
86///
87/// This function will program multiple words in main flash.
88///
89/// `program_key` - The programming key, AM_HAL_FLASH_PROGRAM_KEY.
90/// `src_addr` - Pointer to word aligned array of data to program into the flash instance.
91/// `dst_addr` - Pointer to the word aligned flash location where
92/// programming of the flash instance is to begin.
93/// `num_words` - The number of words to be programmed.
94///
95/// @return 0 for success, non-zero for failure.
96///     Failing return code indicates:
97///     1   ui32ProgramKey is invalid.
98///     2   pui32Dst is invalid.
99///     3   Flash addressing range would be exceeded.  That is, (pui32Dst +
100///         (ui32NumWords * 4)) is greater than the last valid address.
101///     4   pui32Src is invalid.
102///     5   Unused - will never be returned.
103///     6   Flash controller hardware timeout.
104unsafe fn flash_program_main(
105    program_key: u32,
106    src_addr: *mut u32,
107    dst_addr: *mut u32,
108    num_words: u32,
109) -> i32 {
110    use core::ffi::c_int;
111
112    // Call `int flash_program_main(uint32_t, uint32_t *, uint32_t *, uint32_t)` in the ROM code.
113    let flash_program_main: unsafe extern "C" fn(u32, *mut u32, *mut u32, u32) -> c_int =
114        unsafe { core::mem::transmute(0x08000055 as *const ()) };
115
116    flash_program_main(program_key, src_addr, dst_addr, num_words)
117}
118
119/// This function can be used to read / write arbitrary flash memory, and thus
120/// arbitrary program code. As such, they are unsafe operations. We also can't
121/// confirm that the functions are safe in the context of Rust.
122///
123/// The below documentation is based on the official HAL documentation.
124///
125/// This function will call the chip ROM code to perform the below operation.
126///
127/// This function will erase the desired flash page in the desired instance of
128/// flash.
129///
130/// `program_key` - The flash program key.
131/// `flash_instance` - The flash instance to reference the page number with.
132/// `page_num` - The flash page relative to the specified instance.
133///
134/// @return 0 for success, non-zero for failure.
135///     Failing return code indicates:
136///     1   ui32ProgramKey is invalid.
137///     2   ui32FlashInst is invalid.
138///     3   ui32PageNum is invalid.
139///     4   Flash controller hardware timeout.
140unsafe fn flash_page_erase(program_key: u32, flash_instance: u32, page_num: u32) -> i32 {
141    use core::ffi::c_int;
142
143    // Call `int flash_page_erase(uint32_t, uint32_t, uint32_t)` in the ROM code.
144    let flash_page_erase: unsafe extern "C" fn(u32, u32, u32) -> c_int =
145        unsafe { core::mem::transmute(0x08000051 as *const ()) };
146
147    flash_page_erase(program_key, flash_instance, page_num)
148}
149
150#[derive(Copy, Clone, PartialEq)]
151enum Operation {
152    None,
153    Read,
154    Write,
155    Erase,
156}
157
158pub struct FlashCtrl<'a> {
159    flash_client: OptionalCell<&'a dyn hil::flash::Client<FlashCtrl<'a>>>,
160    read_buf: TakeCell<'static, Apollo3Page>,
161    write_buf: TakeCell<'static, Apollo3Page>,
162
163    deferred_call: DeferredCall,
164    op: Cell<Operation>,
165}
166
167impl FlashCtrl<'_> {
168    pub fn new() -> Self {
169        FlashCtrl {
170            flash_client: OptionalCell::empty(),
171            read_buf: TakeCell::empty(),
172            write_buf: TakeCell::empty(),
173            deferred_call: DeferredCall::new(),
174            op: Cell::new(Operation::None),
175        }
176    }
177}
178
179impl<C: hil::flash::Client<Self>> hil::flash::HasClient<'static, C> for FlashCtrl<'_> {
180    fn set_client(&self, client: &'static C) {
181        self.flash_client.set(client);
182    }
183}
184
185impl hil::flash::Flash for FlashCtrl<'_> {
186    type Page = Apollo3Page;
187
188    fn read_page(
189        &self,
190        page_number: usize,
191        buf: &'static mut Self::Page,
192    ) -> Result<(), (ErrorCode, &'static mut Self::Page)> {
193        if page_number >= FLASH_MAX_PAGES {
194            return Err((ErrorCode::INVAL, buf));
195        }
196
197        if self.op.get() != Operation::None {
198            return Err((ErrorCode::BUSY, buf));
199        }
200
201        if self.deferred_call.is_pending() {
202            return Err((ErrorCode::BUSY, buf));
203        }
204
205        let addr = (page_number * PAGE_SIZE) as u32;
206        let addr_ptr = addr as *mut u32;
207
208        for i in 0..(PAGE_SIZE / 4) {
209            let val = unsafe { flash_util_read_word(addr_ptr.wrapping_add(i)).to_le_bytes() };
210            let offset = i * 4;
211
212            buf[offset] = val[0];
213            buf[offset + 1] = val[1];
214            buf[offset + 2] = val[2];
215            buf[offset + 3] = val[3];
216        }
217
218        self.read_buf.replace(buf);
219        self.op.set(Operation::Read);
220        self.deferred_call.set();
221        Ok(())
222    }
223
224    fn write_page(
225        &self,
226        page_number: usize,
227        buf: &'static mut Self::Page,
228    ) -> Result<(), (ErrorCode, &'static mut Self::Page)> {
229        if page_number >= FLASH_MAX_PAGES {
230            return Err((ErrorCode::INVAL, buf));
231        }
232
233        if self.op.get() != Operation::None {
234            return Err((ErrorCode::BUSY, buf));
235        }
236
237        if self.deferred_call.is_pending() {
238            return Err((ErrorCode::BUSY, buf));
239        }
240
241        let addr = (page_number * PAGE_SIZE) as u32;
242        let addr_ptr = addr as *mut u32;
243
244        let source_ptr = buf.0.as_mut_ptr() as *mut u32;
245
246        let ret = unsafe {
247            flash_program_main(
248                FLASH_PROGRAM_KEY,
249                source_ptr,
250                addr_ptr,
251                PAGE_SIZE as u32 / 4,
252            )
253        };
254
255        match ret {
256            0 => {
257                self.write_buf.replace(buf);
258                self.op.set(Operation::Write);
259                self.deferred_call.set();
260                Ok(())
261            }
262            1 => {
263                // ProgramKey is invalid
264                Err((ErrorCode::NOSUPPORT, buf))
265            }
266            2 => {
267                // Dst is invalid
268                Err((ErrorCode::INVAL, buf))
269            }
270            3 => {
271                // Flash addressing range would be exceeded
272                Err((ErrorCode::INVAL, buf))
273            }
274            4 => {
275                // Src is invalid.
276                Err((ErrorCode::INVAL, buf))
277            }
278            6 => {
279                // Flash controller hardware timeout.
280                Err((ErrorCode::BUSY, buf))
281            }
282            _ => Err((ErrorCode::FAIL, buf)),
283        }
284    }
285
286    fn erase_page(&self, page_number: usize) -> Result<(), ErrorCode> {
287        if page_number >= FLASH_MAX_PAGES {
288            return Err(ErrorCode::INVAL);
289        }
290
291        if self.op.get() != Operation::None {
292            return Err(ErrorCode::BUSY);
293        }
294
295        if self.deferred_call.is_pending() {
296            return Err(ErrorCode::BUSY);
297        }
298
299        let ret = if page_number <= FLASH_PAGES_PER_INSTANCE {
300            unsafe {
301                flash_page_erase(
302                    FLASH_PROGRAM_KEY,
303                    FlashInstance::MAIN0 as u32,
304                    page_number as u32,
305                )
306            }
307        } else {
308            unsafe {
309                flash_page_erase(
310                    FLASH_PROGRAM_KEY,
311                    FlashInstance::MAIN1 as u32,
312                    (page_number - FLASH_PAGES_PER_INSTANCE) as u32,
313                )
314            }
315        };
316
317        match ret {
318            0 => {
319                self.op.set(Operation::Erase);
320                self.deferred_call.set();
321                Ok(())
322            }
323            1 => {
324                // ProgramKey is invalid
325                Err(ErrorCode::NOSUPPORT)
326            }
327            2 => {
328                // FlashInst is invalid.
329                Err(ErrorCode::NOSUPPORT)
330            }
331            3 => {
332                // PageNum is invalid.
333                Err(ErrorCode::INVAL)
334            }
335            4 => {
336                // Flash controller hardware timeout.
337                Err(ErrorCode::BUSY)
338            }
339            _ => Err(ErrorCode::FAIL),
340        }
341    }
342}
343
344impl DeferredCallClient for FlashCtrl<'_> {
345    fn register(&'static self) {
346        self.deferred_call.register(self);
347    }
348
349    fn handle_deferred_call(&self) {
350        let prev_op = self.op.get();
351
352        self.op.set(Operation::None);
353
354        self.flash_client.map(|client| match prev_op {
355            Operation::None => unreachable!(),
356            Operation::Read => client.read_complete(self.read_buf.take().unwrap(), Ok(())),
357            Operation::Write => client.write_complete(self.write_buf.take().unwrap(), Ok(())),
358            Operation::Erase => client.erase_complete(Ok(())),
359        });
360    }
361}