stm32_fmc/nand/
device.rs

1//! Management of external NAND Flash through the STM32 FMC peripheral
2//!
3//! Commands and parameters are referenced to the Open NAND Flash Interface
4//! (ONFI) Specification Revision 5.1 3 May 2022
5//!
6//! Addressing supports up to 64Gb / 4GByte (8-bit data) or 128Gb / 8Gbyte (16-bit data).
7
8use core::convert::TryInto;
9use core::sync::atomic::{fence, Ordering};
10use core::{fmt, ptr, str};
11
12/// NAND Commands defined in ONFI Specification 5.1
13#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14#[derive(Clone, Copy, Debug, PartialEq)]
15#[allow(unused)]
16enum Command {
17    /// 0xFF Reset: ONFI Section 5.3
18    Reset = 0xFF,
19    /// 0x90 Read ID: ONFI Section 5.6
20    ReadID = 0x90,
21    /// 0xEC Read Parameter Page: ONFI Section 5.7
22    ReadParameterPage = 0xEC,
23    /// 0xED Read Unique ID: ONFI Section 5.8
24    ReadUniqueID = 0xED,
25    /// Block Erase: ONFI Section 5.9
26    BlockErase = 0x60,
27    /// 0x70 Read Status: ONFI Section 5.10
28    ReadStatus = 0x70,
29}
30
31/// Status returned from 0x70 Read Status: ONFI Section 5.10
32#[cfg_attr(feature = "defmt", derive(defmt::Format))]
33#[derive(Clone, Copy, Debug, PartialEq)]
34pub enum Status {
35    /// Status Register indicated Pass
36    Success(u8),
37    /// Status Register indicates Fail
38    Fail(u8),
39}
40impl Status {
41    fn from_register(reg: u8) -> Self {
42        match reg & 1 {
43            1 => Self::Fail(reg),
44            _ => Self::Success(reg),
45        }
46    }
47}
48
49/// Identifier returned from 0x90 Read ID: ONFI Section 5.6
50#[cfg_attr(feature = "defmt", derive(defmt::Format))]
51#[derive(Clone, Copy, Debug, PartialEq)]
52pub struct ID {
53    manufacturer_jedec: u8,
54    device_jedec: u8,
55    internal_chip_count: usize,
56    page_size: usize,
57}
58
59/// Parameter Page returned from 0xEC Read Parameter Page: ONFI Section 5.7
60#[cfg_attr(feature = "defmt", derive(defmt::Format))]
61#[derive(Clone, Copy, PartialEq)]
62pub struct ParameterPage {
63    signature: [u8; 4],
64    onfi_revision: u16,
65    manufacturer: [u8; 12],
66    model: [u8; 20],
67    date_code: u16,
68    data_bytes_per_page: u32,
69    spare_bytes_per_page: u16,
70    pages_per_block: u32,
71    blocks_per_lun: u32,
72    lun_count: u8,
73    ecc_bits: u8,
74}
75impl ParameterPage {
76    /// Manufacturer of the device
77    pub fn manufacturer(&self) -> &str {
78        str::from_utf8(&self.manufacturer).unwrap_or("<ERR>")
79    }
80    /// Model number of the deviceo
81    pub fn model(&self) -> &str {
82        str::from_utf8(&self.model).unwrap_or("<ERR>")
83    }
84}
85impl fmt::Debug for ParameterPage {
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        f.debug_struct("ONFI Parameter Page")
88            .field("ONFI Revision", &self.onfi_revision)
89            .field("Manufacturer", &self.manufacturer())
90            .field("Model", &self.model())
91            .field("Date Code", &self.date_code)
92            .field("Data bytes per Page", &self.data_bytes_per_page)
93            .field("Spare bytes per Page", &self.spare_bytes_per_page)
94            .field("Pages per Block", &self.pages_per_block)
95            .field("Blocks per LUN", &self.blocks_per_lun)
96            .field("LUN Count", &self.lun_count)
97            .field("ECC Bits Correctability", &self.ecc_bits)
98            .finish()
99    }
100}
101
102/// NAND Device
103#[derive(Clone, Debug, PartialEq)]
104#[allow(missing_copy_implementations)]
105pub struct NandDevice {
106    common_command: *mut u8,
107    common_address: *mut u8,
108    attribute_command: *mut u8,
109    common_data: *mut u8,
110
111    /// Number of address bits C that are used for the column address. The
112    /// number of data bytes per page is typically 2^C
113    column_bits: Option<usize>,
114}
115
116unsafe fn write_volatile_sync<T>(dest: *mut T, src: T) {
117    ptr::write_volatile(dest, src);
118
119    // Ensure that the write is committed before continuing. In the default
120    // ARMv7-M address map the space 0x8000_0000-0x9FFF_FFFF is Normal Memory
121    // with write-though cache attribute.
122    fence(Ordering::SeqCst);
123}
124
125impl NandDevice {
126    /// Create a `NandDevice` from a bank pointer
127    ///
128    /// # Safety
129    ///
130    /// The FMC controller must have been initialized as NAND controller and
131    /// enabled for this bank, with the correct pin settings. The bank pointer
132    /// must be a singleton.
133    pub(crate) unsafe fn init(ptr: *mut u8, column_bits: usize) -> NandDevice {
134        let mut nand = NandDevice {
135            common_command: ptr.add(0x1_0000),
136            common_address: ptr.add(0x2_0000),
137            attribute_command: ptr.add(0x801_0000),
138            common_data: ptr,
139            column_bits: Some(column_bits),
140        };
141
142        // Reset Command. May be specifically required by some devices and there
143        // seems to be no disadvantage of sending it always
144        nand.reset();
145
146        nand
147    }
148    /// 0xFF Reset: ONFI Section 5.3
149    pub fn reset(&mut self) {
150        unsafe {
151            write_volatile_sync(self.common_command, 0xFF);
152        }
153    }
154    /// Generic Command
155    fn command(&mut self, cmd: Command, address: u8, buffer: &mut [u8]) {
156        unsafe {
157            write_volatile_sync(self.common_command, cmd as u8);
158            write_volatile_sync(self.common_address, address);
159            for x in buffer {
160                *x = ptr::read_volatile(self.common_data);
161            }
162        }
163    }
164    /// Generic Address
165    ///
166    /// column_bits must be set first!
167    fn address(&mut self, address: usize, spare: bool) {
168        let column_bits = self
169            .column_bits
170            .expect("Number of column bits must be configured first");
171        let column = (address & ((1 << column_bits) - 1))
172            + if spare { 1 << column_bits } else { 0 };
173        let row = address >> column_bits;
174
175        let mut addr_cycles = [0u8; 5];
176
177        // Assuming 5-cycle address
178        addr_cycles[0] = (column & 0xFF) as u8;
179        addr_cycles[1] = ((column >> 8) & 0xFF) as u8;
180        addr_cycles[2] = (row & 0xFF) as u8;
181        addr_cycles[3] = ((row >> 8) & 0xFF) as u8;
182        addr_cycles[4] = ((row >> 16) & 0xFF) as u8;
183
184        for a in addr_cycles {
185            unsafe {
186                write_volatile_sync(self.common_address, a);
187            }
188        }
189    }
190
191    /// 0x90 Read ID: ONFI Section 5.6
192    pub fn read_id(&mut self) -> ID {
193        let mut id = [0u8; 5];
194        self.command(Command::ReadID, 0, &mut id);
195
196        let internal_chip_count = match id[2] & 3 {
197            1 => 2,
198            2 => 4,
199            3 => 8,
200            _ => 1,
201        };
202        let page_size = match id[3] & 3 {
203            1 => 2048,
204            2 => 4096,
205            _ => 0,
206        };
207        ID {
208            manufacturer_jedec: id[0],
209            device_jedec: id[1],
210            internal_chip_count,
211            page_size,
212        }
213    }
214    /// 0xEC Read Parameter Page: ONFI Section 5.7
215    pub fn read_parameter_page(&mut self) -> ParameterPage {
216        let mut page = [0u8; 115];
217        self.command(Command::ReadParameterPage, 0, &mut page);
218
219        ParameterPage {
220            signature: page[0..4].try_into().unwrap(),
221            onfi_revision: u16::from_le_bytes(page[4..6].try_into().unwrap()),
222            manufacturer: page[32..44].try_into().unwrap(),
223            model: page[44..64].try_into().unwrap(),
224            date_code: u16::from_le_bytes(page[65..67].try_into().unwrap()),
225            data_bytes_per_page: u32::from_le_bytes(
226                page[80..84].try_into().unwrap(),
227            ),
228            spare_bytes_per_page: u16::from_le_bytes(
229                page[84..86].try_into().unwrap(),
230            ),
231            pages_per_block: u32::from_le_bytes(
232                page[92..96].try_into().unwrap(),
233            ),
234            blocks_per_lun: u32::from_le_bytes(
235                page[96..100].try_into().unwrap(),
236            ),
237            lun_count: page[100],
238            ecc_bits: page[112],
239        }
240    }
241    /// 0xED Read Unique ID: ONFI Section 5.8
242    pub fn read_unique_id(&mut self) -> u128 {
243        let mut unique = [0u8; 16];
244        self.command(Command::ReadUniqueID, 0, &mut unique);
245        u128::from_le_bytes(unique)
246    }
247    /// 0x60 Block Erase: ONFI Section 5.9
248    pub fn block_erase(&mut self, address: usize) -> Status {
249        unsafe {
250            write_volatile_sync(self.common_command, 0x60); // auto block erase setup
251        }
252
253        let column_bits = self
254            .column_bits
255            .expect("Number of column bits must be configured first!");
256        let row = address >> column_bits;
257        unsafe {
258            // write block address
259            write_volatile_sync(self.common_address, (row & 0xFF) as u8);
260            write_volatile_sync(self.common_address, ((row >> 8) & 0xFF) as u8);
261            write_volatile_sync(
262                self.common_address,
263                ((row >> 16) & 0xFF) as u8,
264            );
265
266            // erase command
267            write_volatile_sync(self.attribute_command, 0xD0); // t_WB
268            write_volatile_sync(self.common_command, Command::ReadStatus as u8);
269            let status_register = ptr::read_volatile(self.common_data);
270            Status::from_register(status_register)
271        }
272    }
273
274    /// Page Read: ONFI Section 5.14
275    ///
276    /// This method starts a Page Read operation but does not include the data
277    /// phase. This method is useful when DMA is used for the data phase.
278    ///
279    /// For a method that completes the entire transaction see
280    /// [`page_read`](Self::page_read).
281    pub fn start_page_read(&mut self, address: usize, spare: bool) {
282        unsafe {
283            write_volatile_sync(self.common_command, 0x00);
284            self.address(address, spare);
285            write_volatile_sync(self.attribute_command, 0x30); // t_WB
286        }
287    }
288    /// Page Read: ONFI Section 5.14
289    ///
290    /// Executes a Page Read operation from the specified address. Data is
291    /// copied to the slice `page`. The length of `page` determines the read
292    /// length. The read length should not exceed the number of bytes between
293    /// the specified address and the end of the page. Reading beyond the end of
294    /// the page results in indeterminate values being returned.
295    ///
296    /// If `spare` is true, then the read occours from the spare area. The
297    /// address offset from the start of the page plus the slice length should
298    /// not exceed the spare area size.
299    pub fn page_read(&mut self, address: usize, spare: bool, page: &mut [u8]) {
300        self.start_page_read(address, spare);
301        for x in page {
302            unsafe {
303                *x = ptr::read_volatile(self.common_data);
304            }
305        }
306    }
307
308    /// Page Program: ONFI Section 5.16
309    ///
310    /// Executes a page program to the specified address and waits for it to
311    /// complete. The length of `page` determines the write length. The write
312    /// length should not exceed the number of bytes between the specified
313    /// address and the end of the page. Writing beyond this length is
314    /// undefined.
315    pub fn page_program(
316        &mut self,
317        address: usize,
318        spare: bool,
319        page: &[u8],
320    ) -> Status {
321        unsafe {
322            write_volatile_sync(self.common_command, 0x80); // data input
323            self.address(address, spare);
324            for x in page {
325                write_volatile_sync(self.common_data, *x); // write page
326            }
327            write_volatile_sync(self.attribute_command, 0x10); // program command, t_WB
328            let mut status_register;
329            while {
330                write_volatile_sync(
331                    self.common_command,
332                    Command::ReadStatus as u8,
333                );
334                status_register = ptr::read_volatile(self.common_data);
335
336                status_register & 0x20 == 0 // program in progress
337            } {}
338
339            Status::from_register(status_register)
340        }
341    }
342}
343
344/// Methods to allow users to implement their own commands using `unsafe`.
345///
346impl NandDevice {
347    /// Return a Raw Pointer to the common command space. This memory-mapped
348    /// address is used to write command phase of NAND device transactions.
349    ///
350    /// It is recommended to use
351    /// [`ptr::write_volatile`](https://doc.rust-lang.org/std/ptr/fn.write_volatile.html)
352    /// to write to this pointer. Depending on the memory map in use, you may
353    /// need to ensure the write is committed by using
354    /// [`core::atomic::sync::fence`](https://doc.rust-lang.org/core/sync/atomic/fn.fence.html).
355    pub fn common_command(&mut self) -> *mut u8 {
356        self.common_command
357    }
358    /// Return a Raw Pointer to the attribute command space. This memory-mapped
359    /// address is used to write command phase of NAND device transactions.
360    ///
361    /// It is recommended to use
362    /// [`ptr::write_volatile`](https://doc.rust-lang.org/std/ptr/fn.write_volatile.html)
363    /// to write to this pointer. Depending on the memory map in use, you may
364    /// need to ensure the write is committed by using
365    /// [`core::atomic::sync::fence`](https://doc.rust-lang.org/core/sync/atomic/fn.fence.html).
366    pub fn attribute_command(&mut self) -> *mut u8 {
367        self.attribute_command
368    }
369    /// Return a Raw Pointer to the common address space. This memory-mapped
370    /// address is used to write the address phase of NAND device transactions.
371    ///
372    /// It is recommended to use
373    /// [`ptr::write_volatile`](https://doc.rust-lang.org/std/ptr/fn.write_volatile.html)
374    /// to write to this pointer. Depending on the memory map in use, you may
375    /// need to ensure the write is committed by using
376    /// [`core::atomic::sync::fence`](https://doc.rust-lang.org/core/sync/atomic/fn.fence.html).
377    pub fn common_address(&mut self) -> *mut u8 {
378        self.common_address
379    }
380    /// Return a Raw Pointer to the common data space. This memory-mapped
381    /// address is used to write or read the data phase of NAND device
382    /// transactions.
383    ///
384    /// It is recommended to use
385    /// [`ptr::write_volatile`](https://doc.rust-lang.org/std/ptr/fn.write_volatile.html)
386    /// to write to this pointer. Depending on the memory map in use, you may
387    /// need to ensure the write is committed by using
388    /// [`core::atomic::sync::fence`](https://doc.rust-lang.org/core/sync/atomic/fn.fence.html).
389    pub fn common_data(&mut self) -> *mut u8 {
390        self.common_data
391    }
392}