nrf52/
nvmc.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//! Non-Volatile Memory Controller
6//!
7//! Used in order read and write to internal flash.
8
9use core::cell::Cell;
10use core::ops::{Index, IndexMut};
11use kernel::deferred_call::{DeferredCall, DeferredCallClient};
12use kernel::hil;
13use kernel::utilities::cells::OptionalCell;
14use kernel::utilities::cells::TakeCell;
15use kernel::utilities::cells::VolatileCell;
16use kernel::utilities::registers::interfaces::{Readable, Writeable};
17use kernel::utilities::registers::{register_bitfields, ReadOnly, ReadWrite};
18use kernel::utilities::StaticRef;
19use kernel::ErrorCode;
20
21const NVMC_BASE: StaticRef<NvmcRegisters> =
22    unsafe { StaticRef::new(0x4001E400 as *const NvmcRegisters) };
23
24#[repr(C)]
25struct NvmcRegisters {
26    /// Ready flag
27    /// Address 0x400 - 0x404
28    pub ready: ReadOnly<u32, Ready::Register>,
29    _reserved0: [u32; 4],
30    /// Ready flag
31    /// Address 0x408 - 0x40C
32    pub ready_next: ReadOnly<u32, Ready::Register>,
33    /// Reserved
34    _reserved1: [u32; 59],
35    /// Configuration register
36    /// Address: 0x504 - 0x508
37    pub config: ReadWrite<u32, Configuration::Register>,
38    /// Register for erasing a page in Code area
39    /// Address: 0x508 - 0x50C
40    pub erasepage: ReadWrite<u32, ErasePage::Register>,
41    /// Register for erasing all non-volatile user memory
42    /// Address: 0x50C - 0x510
43    pub eraseall: ReadWrite<u32, EraseAll::Register>,
44    _reserved2: u32,
45    /// Register for erasing User Information Configuration Registers
46    /// Address: 0x514 - 0x518
47    pub eraseuicr: ReadWrite<u32, EraseUicr::Register>,
48    /// Reserved
49    _reserved3: [u32; 10],
50    /// Configuration register
51    /// Address: 0x540 - 0x544
52    pub icachecnf: ReadWrite<u32, CacheConfiguration::Register>,
53    /// Reserved
54    _reserved4: u32,
55    /// Configuration register
56    /// Address: 0x548 - 0x54c
57    pub ihit: ReadWrite<u32, CacheHit::Register>,
58    /// Configuration register
59    /// Address: 0x54C - 0x550
60    pub imiss: ReadWrite<u32, CacheMiss::Register>,
61}
62
63register_bitfields! [u32,
64    /// Ready flag
65    Ready [
66        /// NVMC is ready or busy
67        READY OFFSET(0) NUMBITS(1) [
68            /// NVMC is busy (on-going write or erase operation)
69            BUSY = 0,
70            /// NVMC is ready
71            READY = 1
72        ]
73    ],
74    /// Configuration register
75    Configuration [
76        /// Program memory access mode. It is strongly recommended
77        /// to only activate erase and write modes when they are actively
78        /// used. Enabling write or erase will invalidate the cache and keep
79        /// it invalidated.
80        WEN OFFSET(0) NUMBITS(2) [
81            /// Read only access
82            Ren = 0,
83            /// Write Enabled
84            Wen = 1,
85            /// Erase enabled
86            Een = 2
87        ]
88    ],
89    /// Register for erasing a page in Code area
90    ErasePage [
91        /// Register for starting erase of a page in Code area
92        ERASEPAGE OFFSET(0) NUMBITS(32) []
93    ],
94    /// Register for erasing all non-volatile user memory
95    EraseAll [
96        /// Erase all non-volatile memory including UICR registers. Note
97        /// that code erase has to be enabled by CONFIG.EEN before the
98        /// UICR can be erased
99        ERASEALL OFFSET(0) NUMBITS(1) [
100            /// No operation
101            NOOPERATION = 0,
102            /// Start chip erase
103            ERASE = 1
104        ]
105    ],
106    /// Register for erasing User Information Configuration Registers
107    EraseUicr [
108        /// Register starting erase of all User Information Configuration Registers.
109        /// Note that code erase has to be enabled by CONFIG.EEN before the UICR can be erased
110        ERASEUICR OFFSET(0) NUMBITS(1) [
111            /// No operation
112            NOOPERATION = 0,
113            /// Start erase of UICR
114            ERASE = 1
115        ]
116    ],
117    /// I-Code cache configuration register
118    CacheConfiguration [
119        /// Cache enabled
120        CACHEEN OFFSET(0) NUMBITS(1) [
121            /// Disable cache. Invalidates all cache entries
122            DISABLED = 0,
123            /// Enable cache
124            ENABLED = 1
125        ],
126        /// Cache profiling enable
127        CACHEPROFEN OFFSET(8) NUMBITS(1) [
128            /// Disable cache profiling
129            DISABLED = 0,
130            /// Enable cache profiling
131            ENABLED = 1
132        ]
133    ],
134    /// I-Code cache hit counter
135    CacheHit [
136        /// Number of cache hits
137        HITS OFFSET(0) NUMBITS(32) []
138    ],
139    /// I-Code cache miss counter
140    CacheMiss [
141        /// Number of cache misses
142        MISSES OFFSET(0) NUMBITS(32) []
143    ]
144];
145
146const PAGE_SIZE: usize = 4096;
147
148/// This is a wrapper around a u8 array that is sized to a single page for the
149/// nrf. Users of this module must pass an object of this type to use the
150/// `hil::flash::Flash` interface.
151///
152/// An example looks like:
153///
154/// ```rust
155/// # extern crate nrf52;
156/// # use nrf52::nvmc::NrfPage;
157/// # use kernel::static_init;
158///
159/// let pagebuffer = unsafe { static_init!(NrfPage, NrfPage::default()) };
160/// ```
161pub struct NrfPage(pub [u8; PAGE_SIZE]);
162
163impl Default for NrfPage {
164    fn default() -> Self {
165        Self([0; PAGE_SIZE])
166    }
167}
168impl NrfPage {
169    fn len(&self) -> usize {
170        self.0.len()
171    }
172}
173
174impl Index<usize> for NrfPage {
175    type Output = u8;
176
177    fn index(&self, idx: usize) -> &u8 {
178        &self.0[idx]
179    }
180}
181
182impl IndexMut<usize> for NrfPage {
183    fn index_mut(&mut self, idx: usize) -> &mut u8 {
184        &mut self.0[idx]
185    }
186}
187
188impl AsMut<[u8]> for NrfPage {
189    fn as_mut(&mut self) -> &mut [u8] {
190        &mut self.0
191    }
192}
193
194/// FlashState is used to track the current state and command of the flash.
195#[derive(Clone, Copy, PartialEq)]
196pub enum FlashState {
197    Ready, // Flash is ready to complete a command.
198    Read,  // Performing a read operation.
199    Write, // Performing a write operation.
200    Erase, // Performing an erase operation.
201}
202
203pub struct Nvmc {
204    registers: StaticRef<NvmcRegisters>,
205    client: OptionalCell<&'static dyn hil::flash::Client<Nvmc>>,
206    buffer: TakeCell<'static, NrfPage>,
207    state: Cell<FlashState>,
208    deferred_call: DeferredCall,
209}
210
211impl Nvmc {
212    pub fn new() -> Self {
213        Self {
214            registers: NVMC_BASE,
215            client: OptionalCell::empty(),
216            buffer: TakeCell::empty(),
217            state: Cell::new(FlashState::Ready),
218            deferred_call: DeferredCall::new(),
219        }
220    }
221
222    /// Configure the NVMC to allow writes to flash.
223    pub fn configure_writeable(&self) {
224        self.registers.config.write(Configuration::WEN::Wen);
225    }
226
227    pub fn configure_eraseable(&self) {
228        self.registers.config.write(Configuration::WEN::Een);
229    }
230
231    pub fn erase_uicr(&self) {
232        self.registers.config.write(Configuration::WEN::Een);
233        while !self.is_ready() {}
234        self.registers
235            .erasepage
236            .write(ErasePage::ERASEPAGE.val(0x10001000));
237        while !self.is_ready() {}
238    }
239
240    /// Check if there is an ongoing operation with the NVMC peripheral.
241    pub fn is_ready(&self) -> bool {
242        self.registers.ready.is_set(Ready::READY)
243    }
244
245    pub fn handle_interrupt(&self) {
246        let state = self.state.get();
247        self.state.set(FlashState::Ready);
248
249        match state {
250            FlashState::Read => {
251                self.client.map(|client| {
252                    self.buffer.take().map(|buffer| {
253                        client.read_complete(buffer, Ok(()));
254                    });
255                });
256            }
257            FlashState::Write => {
258                self.client.map(|client| {
259                    self.buffer.take().map(|buffer| {
260                        client.write_complete(buffer, Ok(()));
261                    });
262                });
263            }
264            FlashState::Erase => {
265                self.client.map(|client| {
266                    client.erase_complete(Ok(()));
267                });
268            }
269            _ => {}
270        }
271    }
272
273    fn erase_page_helper(&self, page_number: usize) {
274        // Put the NVMC in erase mode.
275        self.registers.config.write(Configuration::WEN::Een);
276
277        // Tell the NVMC to erase the correct page by passing in the correct
278        // address.
279        self.registers
280            .erasepage
281            .write(ErasePage::ERASEPAGE.val((page_number * PAGE_SIZE) as u32));
282
283        // Make sure that the NVMC is done. The CPU should be blocked while the
284        // erase is happening, but it doesn't hurt to check too.
285        while !self.registers.ready.is_set(Ready::READY) {}
286    }
287
288    fn read_range(
289        &self,
290        page_number: usize,
291        buffer: &'static mut NrfPage,
292    ) -> Result<(), (ErrorCode, &'static mut NrfPage)> {
293        // Actually do a copy from flash into the buffer.
294        let mut byte: *const u8 = (page_number * PAGE_SIZE) as *const u8;
295        unsafe {
296            for i in 0..buffer.len() {
297                buffer[i] = *byte;
298                byte = byte.offset(1);
299            }
300        }
301
302        // Hold on to the buffer for the callback.
303        self.buffer.replace(buffer);
304
305        // Mark the need for an interrupt so we can call the read done
306        // callback.
307        self.state.set(FlashState::Read);
308        self.deferred_call.set();
309
310        Ok(())
311    }
312
313    fn write_page(
314        &self,
315        page_number: usize,
316        data: &'static mut NrfPage,
317    ) -> Result<(), (ErrorCode, &'static mut NrfPage)> {
318        // Need to erase the page first.
319        self.erase_page_helper(page_number);
320
321        // Put the NVMC in write mode.
322        self.registers.config.write(Configuration::WEN::Wen);
323
324        for i in (0..data.len()).step_by(4) {
325            let word: u32 = (data[i + 0] as u32) << 0
326                | (data[i + 1] as u32) << 8
327                | (data[i + 2] as u32) << 16
328                | (data[i + 3] as u32) << 24;
329
330            let address = ((page_number * PAGE_SIZE) + i) as u32;
331            let location = unsafe { &*(address as *const VolatileCell<u32>) };
332            location.set(word);
333            while !self.registers.ready.is_set(Ready::READY) {}
334        }
335
336        // Make sure that the NVMC is done. The CPU should be blocked while the
337        // write is happening, but it doesn't hurt to check too.
338        while !self.registers.ready.is_set(Ready::READY) {}
339
340        // Save the buffer so we can return it with the callback.
341        self.buffer.replace(data);
342
343        // Mark the need for an interrupt so we can call the write done
344        // callback.
345        self.state.set(FlashState::Write);
346        self.deferred_call.set();
347
348        Ok(())
349    }
350
351    fn erase_page(&self, page_number: usize) -> Result<(), ErrorCode> {
352        // Do the basic erase.
353        self.erase_page_helper(page_number);
354
355        // Mark that we want to trigger a pseudo interrupt so that we can issue
356        // the callback even though the NVMC is completely blocking.
357        self.state.set(FlashState::Erase);
358        self.deferred_call.set();
359
360        Ok(())
361    }
362}
363
364impl<C: hil::flash::Client<Self>> hil::flash::HasClient<'static, C> for Nvmc {
365    fn set_client(&self, client: &'static C) {
366        self.client.set(client);
367    }
368}
369
370impl hil::flash::Flash for Nvmc {
371    type Page = NrfPage;
372
373    fn read_page(
374        &self,
375        page_number: usize,
376        buf: &'static mut Self::Page,
377    ) -> Result<(), (ErrorCode, &'static mut Self::Page)> {
378        self.read_range(page_number, buf)
379    }
380
381    fn write_page(
382        &self,
383        page_number: usize,
384        buf: &'static mut Self::Page,
385    ) -> Result<(), (ErrorCode, &'static mut Self::Page)> {
386        self.write_page(page_number, buf)
387    }
388
389    fn erase_page(&self, page_number: usize) -> Result<(), ErrorCode> {
390        self.erase_page(page_number)
391    }
392}
393
394impl DeferredCallClient for Nvmc {
395    fn handle_deferred_call(&self) {
396        self.handle_interrupt();
397    }
398
399    fn register(&'static self) {
400        self.deferred_call.register(self);
401    }
402}