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}