1 /***************************************************************************
2 * Copyright (c) 2024 Microsoft Corporation
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the MIT License which is available at
6 * https://opensource.org/licenses/MIT.
7 *
8 * SPDX-License-Identifier: MIT
9 **************************************************************************/
10
11
12 /**************************************************************************/
13 /**************************************************************************/
14 /** */
15 /** LevelX Component */
16 /** */
17 /** NAND Flash */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22 #define LX_SOURCE_CODE
23
24
25 /* Disable ThreadX error checking. */
26
27 #ifndef LX_DISABLE_ERROR_CHECKING
28 #define LX_DISABLE_ERROR_CHECKING
29 #endif
30
31
32 /* Include necessary system files. */
33
34 #include "lx_api.h"
35
36
37 /**************************************************************************/
38 /* */
39 /* FUNCTION RELEASE */
40 /* */
41 /* _lx_nand_flash_open PORTABLE C */
42 /* 6.3.0 */
43 /* AUTHOR */
44 /* */
45 /* Xiuwen Cai, Microsoft Corporation */
46 /* */
47 /* DESCRIPTION */
48 /* */
49 /* This function opens a NAND flash instance and ensures the */
50 /* NAND flash is in a coherent state. */
51 /* */
52 /* INPUT */
53 /* */
54 /* nand_flash NAND flash instance */
55 /* name Name of NAND flash instance */
56 /* nand_driver_initialize Driver initialize */
57 /* memory_ptr Pointer to memory used by the */
58 /* LevelX for this NAND. */
59 /* memory_size Size of memory - must at least*/
60 /* 7 * total block count + */
61 /* 2 * page size */
62 /* */
63 /* OUTPUT */
64 /* */
65 /* return status */
66 /* */
67 /* CALLS */
68 /* */
69 /* (nand_driver_initialize) Driver initialize */
70 /* _lx_nand_flash_memory_initialize Initialize buffer */
71 /* _lx_nand_flash_driver_block_status_get */
72 /* Get block status */
73 /* lx_nand_flash_driver_pages_read Read pages */
74 /* _lx_nand_flash_free_block_list_add Add free block to list */
75 /* _lx_nand_flash_mapped_block_list_add Add mapped block to list */
76 /* _lx_nand_flash_system_error System error handler */
77 /* tx_mutex_create Create thread-safe mutex */
78 /* */
79 /* CALLED BY */
80 /* */
81 /* Application Code */
82 /* */
83 /* RELEASE HISTORY */
84 /* */
85 /* DATE NAME DESCRIPTION */
86 /* */
87 /* 03-08-2023 Xiuwen Cai Initial Version 6.2.1 */
88 /* 10-31-2023 Xiuwen Cai Modified comment(s), */
89 /* avoided clearing user */
90 /* extension in flash control */
91 /* block, */
92 /* resulting in version 6.3.0 */
93 /* */
94 /**************************************************************************/
_lx_nand_flash_open(LX_NAND_FLASH * nand_flash,CHAR * name,UINT (* nand_driver_initialize)(LX_NAND_FLASH *),ULONG * memory_ptr,UINT memory_size)95 UINT _lx_nand_flash_open(LX_NAND_FLASH *nand_flash, CHAR *name, UINT (*nand_driver_initialize)(LX_NAND_FLASH *),
96 ULONG* memory_ptr, UINT memory_size)
97 {
98
99 ULONG block;
100 ULONG page;
101 UCHAR block_status;
102 ULONG block_count;
103 UINT status;
104 LX_NAND_FLASH *tail_ptr;
105 LX_NAND_DEVICE_INFO *nand_device_info_page;
106 UCHAR *spare_buffer_ptr;
107 UCHAR *page_buffer_ptr;
108 ULONG page_type;
109 UCHAR page_index;
110 LX_INTERRUPT_SAVE_AREA
111
112 LX_PARAMETER_NOT_USED(name);
113
114 /* Clear the NAND flash control block. User extension is not cleared. */
115 LX_MEMSET(nand_flash, 0, (ULONG)((UCHAR*)&(nand_flash -> lx_nand_flash_open_previous) - (UCHAR*)nand_flash) + sizeof(nand_flash -> lx_nand_flash_open_previous));
116
117 /* Call the flash driver's initialization function. */
118 (nand_driver_initialize)(nand_flash);
119
120 /* Determine if we can support this NAND flash size. */
121 if (nand_flash -> lx_nand_flash_pages_per_block > LX_NAND_MAX_PAGE_PER_BLOCK || nand_flash -> lx_nand_flash_total_blocks > LX_NAND_MAX_BLOCK_COUNT)
122 {
123
124 /* Return an error. */
125 return(LX_ERROR);
126 }
127
128 /* Check if it is new LevelX NAND driver. */
129 if (nand_flash -> lx_nand_flash_driver_pages_read == LX_NULL || nand_flash -> lx_nand_flash_driver_pages_write == LX_NULL || nand_flash -> lx_nand_flash_driver_pages_copy == LX_NULL)
130 {
131
132 /* Return an error. */
133 return(LX_ERROR);
134 }
135
136 /* Check the spare data length. */
137 if (nand_flash -> lx_nand_flash_spare_data1_length < sizeof(ULONG))
138 {
139
140 /* Return an error. */
141 return(LX_ERROR);
142 }
143
144 /* Calculate the number of words per block and per page. */
145 nand_flash -> lx_nand_flash_words_per_page = (nand_flash -> lx_nand_flash_bytes_per_page / sizeof(ULONG));
146 nand_flash -> lx_nand_flash_words_per_block = (nand_flash -> lx_nand_flash_words_per_page * nand_flash -> lx_nand_flash_pages_per_block);
147
148 /* Calculate the total pages. */
149 nand_flash -> lx_nand_flash_total_pages = nand_flash -> lx_nand_flash_total_blocks * nand_flash -> lx_nand_flash_pages_per_block;
150
151 /* Initialize memory buffer. */
152 status = _lx_nand_flash_memory_initialize(nand_flash, memory_ptr, memory_size);
153 if (status != LX_SUCCESS)
154 {
155 return(status);
156 }
157
158 /* Initialize block numbers. */
159 nand_flash -> lx_nand_flash_metadata_block_number = LX_NAND_BLOCK_UNMAPPED;
160 nand_flash -> lx_nand_flash_backup_metadata_block_number = LX_NAND_BLOCK_UNMAPPED;
161
162 /* Setup page buffer and spare buffer pointers. */
163 page_buffer_ptr = nand_flash -> lx_nand_flash_page_buffer;
164 spare_buffer_ptr = page_buffer_ptr + nand_flash -> lx_nand_flash_bytes_per_page;
165
166 /* Loop through the blocks to check for bad blocks and determine the minimum and maximum erase count for each good block. */
167 for (block = 0; block < nand_flash -> lx_nand_flash_total_blocks; block++)
168 {
169
170 /* First, check to make sure this block is good. */
171 status = _lx_nand_flash_driver_block_status_get(nand_flash, block, &block_status);
172
173 /* Check for an error from flash driver. */
174 if (status)
175 {
176
177 /* Call system error handler. */
178 _lx_nand_flash_system_error(nand_flash, status, block, 0);
179
180 /* Return an error. */
181 return(LX_ERROR);
182 }
183
184 /* Is this block bad? */
185 if (block_status != LX_NAND_GOOD_BLOCK)
186 {
187
188 /* Yes, this block is bad. */
189
190 /* Increment the number of bad blocks. */
191 nand_flash -> lx_nand_flash_bad_blocks++;
192
193 /* Save the block status. */
194 nand_flash -> lx_nand_flash_block_status_table[block] = LX_NAND_BLOCK_STATUS_BAD;
195
196 /* Continue to the next block. */
197 continue;
198 }
199
200 /* Call driver read function to read page 0. */
201 #ifdef LX_NAND_ENABLE_CONTROL_BLOCK_FOR_DRIVER_INTERFACE
202 status = (nand_flash -> lx_nand_flash_driver_pages_read)(nand_flash, block, 0, page_buffer_ptr, spare_buffer_ptr, 1);
203 #else
204 status = (nand_flash -> lx_nand_flash_driver_pages_read)(block, 0, page_buffer_ptr, spare_buffer_ptr, 1);
205 #endif
206
207 /* Check for an error from flash driver. */
208 if (status)
209 {
210
211 /* Call system error handler. */
212 _lx_nand_flash_system_error(nand_flash, status, block, 0);
213
214 /* Determine if the error is fatal. */
215 if (status != LX_NAND_ERROR_CORRECTED)
216 {
217
218 /* Return an error. */
219 return(LX_ERROR);
220 }
221 }
222
223 /* Get the page type. */
224 page_type = LX_UTILITY_LONG_GET(&spare_buffer_ptr[nand_flash -> lx_nand_flash_spare_data1_offset]);
225
226 /* Check if the type is device info. */
227 if (page_type == LX_NAND_PAGE_TYPE_DEVICE_INFO)
228 {
229
230 /* Get the device info page. */
231 nand_device_info_page = (LX_NAND_DEVICE_INFO*)page_buffer_ptr;
232
233 /* Check signature. */
234 if (nand_device_info_page -> lx_nand_device_info_signature1 == LX_NAND_DEVICE_INFO_SIGNATURE1 &&
235 nand_device_info_page -> lx_nand_device_info_signature2 == LX_NAND_DEVICE_INFO_SIGNATURE2)
236 {
237
238 /* Save the block numbers. */
239 nand_flash -> lx_nand_flash_metadata_block_number = nand_device_info_page -> lx_nand_device_info_metadata_block_number;
240 nand_flash -> lx_nand_flash_backup_metadata_block_number = nand_device_info_page -> lx_nand_device_info_backup_metadata_block_number;
241 break;
242 }
243
244 }
245
246 }
247
248 /* Check if we have found the metadata block. */
249 if (nand_flash -> lx_nand_flash_metadata_block_number == LX_NAND_BLOCK_UNMAPPED)
250 {
251
252 /* Not found, return an error. */
253 return (LX_ERROR);
254 }
255
256 /* Initialize metadata block numbers and lists. */
257 nand_flash -> lx_nand_flash_metadata_block_number_current = nand_flash -> lx_nand_flash_metadata_block_number;
258 nand_flash -> lx_nand_flash_backup_metadata_block_number_current = nand_flash -> lx_nand_flash_backup_metadata_block_number;
259 nand_flash -> lx_nand_flash_metadata_block[0] = (USHORT)nand_flash -> lx_nand_flash_metadata_block_number;
260 nand_flash -> lx_nand_flash_backup_metadata_block[0] = (USHORT)nand_flash -> lx_nand_flash_backup_metadata_block_number;
261
262 /* Found one metadata block. */
263 nand_flash -> lx_nand_flash_metadata_block_count = 1;
264
265 /* Clear searched block count. */
266 block_count = 0;
267
268 do
269 {
270
271 /* Initialize next block to unmapped. */
272 nand_flash -> lx_nand_flash_metadata_block_number_next = LX_NAND_BLOCK_UNMAPPED;
273 nand_flash -> lx_nand_flash_backup_metadata_block_number_next = LX_NAND_BLOCK_UNMAPPED;
274
275 /* Loop to read pages in the metadata block. */
276 for (page = 0; page < nand_flash -> lx_nand_flash_pages_per_block ; page++)
277 {
278
279 /* Call driver read function to read page. */
280 #ifdef LX_NAND_ENABLE_CONTROL_BLOCK_FOR_DRIVER_INTERFACE
281 status = (nand_flash -> lx_nand_flash_driver_pages_read)(nand_flash, block, page, page_buffer_ptr, spare_buffer_ptr, 1);
282 #else
283 status = (nand_flash -> lx_nand_flash_driver_pages_read)(block, page, page_buffer_ptr, spare_buffer_ptr, 1);
284 #endif
285
286 /* Check for an error from flash driver. */
287 if (status)
288 {
289
290 /* Call system error handler. */
291 _lx_nand_flash_system_error(nand_flash, status, block, 0);
292
293 /* Determine if the error is fatal. */
294 if (status != LX_NAND_ERROR_CORRECTED)
295 {
296
297 /* Return an error. */
298 return(LX_ERROR);
299 }
300 }
301
302 /* Get page type and page index. */
303 page_type = LX_UTILITY_LONG_GET(&spare_buffer_ptr[nand_flash -> lx_nand_flash_spare_data1_offset]) & (~LX_NAND_PAGE_TYPE_PAGE_NUMBER_MASK);
304 page_index = LX_UTILITY_LONG_GET(&spare_buffer_ptr[nand_flash -> lx_nand_flash_spare_data1_offset]) & LX_NAND_PAGE_TYPE_PAGE_NUMBER_MASK;
305
306 /* Process metadata page by type. */
307 switch (page_type)
308 {
309 case LX_NAND_PAGE_TYPE_DEVICE_INFO:
310
311 /* This page is for device info. */
312 nand_device_info_page = (LX_NAND_DEVICE_INFO*)page_buffer_ptr;
313
314 /* Get the base erase count. */
315 nand_flash -> lx_nand_flash_base_erase_count = nand_device_info_page -> lx_nand_device_info_base_erase_count;
316 break;
317
318 case LX_NAND_PAGE_TYPE_ERASE_COUNT_TABLE:
319
320 /* Check if page index is valid. */
321 if (((ULONG)page_index + 1) * nand_flash -> lx_nand_flash_bytes_per_page > nand_flash -> lx_nand_flash_erase_count_table_size)
322 {
323
324 /* Invalid page index. Return an error. */
325 status = LX_ERROR;
326 break;
327 }
328
329 /* Copy page data to erase count table. */
330 LX_MEMCPY(nand_flash -> lx_nand_flash_erase_count_table + page_index * nand_flash -> lx_nand_flash_bytes_per_page, /* Use case of memcpy is verified. */
331 page_buffer_ptr, nand_flash -> lx_nand_flash_bytes_per_page);
332 break;
333
334 case LX_NAND_PAGE_TYPE_BLOCK_MAPPING_TABLE:
335
336 /* Check if page index is valid. */
337 if (((ULONG)page_index + 1) * nand_flash -> lx_nand_flash_bytes_per_page > nand_flash -> lx_nand_flash_block_mapping_table_size)
338 {
339
340 /* Invalid page index. Return an error. */
341 status = LX_ERROR;
342 break;
343 }
344
345 /* Copy page data to block mapping table. */
346 LX_MEMCPY(nand_flash -> lx_nand_flash_block_mapping_table + page_index * nand_flash -> lx_nand_flash_bytes_per_page / sizeof(*nand_flash -> lx_nand_flash_block_mapping_table), /* Use case of memcpy is verified. */
347 page_buffer_ptr, nand_flash -> lx_nand_flash_bytes_per_page);
348 break;
349
350 case LX_NAND_PAGE_TYPE_BLOCK_STATUS_TABLE:
351
352 /* Check if page index is valid. */
353 if (((ULONG)page_index + 1) * nand_flash -> lx_nand_flash_bytes_per_page > nand_flash -> lx_nand_flash_block_status_table_size)
354 {
355
356 /* Invalid page index. Return an error. */
357 status = LX_ERROR;
358 break;
359 }
360
361 /* Copy page data to block status table. */
362 LX_MEMCPY(nand_flash -> lx_nand_flash_block_status_table + page_index * nand_flash -> lx_nand_flash_bytes_per_page / sizeof(*nand_flash -> lx_nand_flash_block_status_table), /* Use case of memcpy is verified. */
363 page_buffer_ptr, nand_flash -> lx_nand_flash_bytes_per_page);
364 break;
365
366 case LX_NAND_PAGE_TYPE_FREE_PAGE:
367
368 /* Found a free page. Update current page. */
369 nand_flash -> lx_nand_flash_metadata_block_current_page = page;
370 nand_flash -> lx_nand_flash_backup_metadata_block_current_page = page;
371
372 /* Skip all the remaining pages. */
373 page = nand_flash -> lx_nand_flash_pages_per_block;
374 break;
375
376 case LX_NAND_PAGE_TYPE_BLOCK_LINK:
377
378 /* Found next blocks. Update next block numbers. */
379 nand_flash -> lx_nand_flash_metadata_block_number_next = LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_MAIN_METADATA_OFFSET]);
380 nand_flash -> lx_nand_flash_backup_metadata_block_number_next = LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_BACKUP_METADATA_OFFSET]);
381
382 /* Save block numbers in metadata block lists. */
383 nand_flash -> lx_nand_flash_metadata_block[nand_flash -> lx_nand_flash_metadata_block_count] = (USHORT)LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_MAIN_METADATA_OFFSET]);
384 nand_flash -> lx_nand_flash_backup_metadata_block[nand_flash -> lx_nand_flash_metadata_block_count] = (USHORT)LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_BACKUP_METADATA_OFFSET]);
385
386 /* Increase metadata block count. */
387 nand_flash -> lx_nand_flash_metadata_block_count++;
388
389 break;
390
391 default:
392
393 /* Unknown type, return error. */
394 status = LX_ERROR;
395 }
396
397 /* Check status. */
398 if (status == LX_ERROR)
399 {
400
401 /* Error, break the loop. */
402 break;
403 }
404
405 }
406
407 /* Check if we have reached the last page. */
408 if (page == nand_flash -> lx_nand_flash_pages_per_block)
409 {
410
411 /* Move to next block. */
412 nand_flash -> lx_nand_flash_metadata_block_number_current = nand_flash -> lx_nand_flash_metadata_block_number_next;
413 nand_flash -> lx_nand_flash_backup_metadata_block_number_current = nand_flash -> lx_nand_flash_backup_metadata_block_number_next;
414
415 /* Make sure the block is valid. */
416 if (nand_flash -> lx_nand_flash_metadata_block_number_current == LX_NAND_BLOCK_UNMAPPED)
417 {
418
419 /* Error, break the loop. */
420 break;
421 }
422
423 /* Get the block to process. */
424 block = nand_flash -> lx_nand_flash_metadata_block_number_current;
425 }
426
427 /* Increase the processed block number. */
428 block_count++;
429
430 /* If block count is larger than total blocks, there is an error. */
431 if (block_count >= nand_flash -> lx_nand_flash_total_blocks)
432 {
433
434 /* Break the loop. */
435 break;
436 }
437 } while (nand_flash -> lx_nand_flash_metadata_block_current_page == 0 && status != LX_ERROR);
438
439 /* Check if metadata page is processed correctly. */
440 if (nand_flash -> lx_nand_flash_metadata_block_current_page == 0 || status == LX_ERROR)
441 {
442
443 /* Return an error. */
444 return(LX_ERROR);
445 }
446
447 /* Loop to build free and mapped block lists. */
448 for (block = 0; block < nand_flash -> lx_nand_flash_total_blocks; block++)
449 {
450
451 /* Check for free blocks. */
452 if (nand_flash -> lx_nand_flash_block_status_table[block] == LX_NAND_BLOCK_STATUS_FREE)
453 {
454
455 /* Add the block to free block list. */
456 _lx_nand_flash_free_block_list_add(nand_flash, block);
457 }
458
459 /* Check for mapped blocks. */
460 if (nand_flash -> lx_nand_flash_block_mapping_table[block] != LX_NAND_BLOCK_UNMAPPED)
461 {
462
463 /* Add the block to free block list. */
464 _lx_nand_flash_mapped_block_list_add(nand_flash, block);
465 }
466 }
467
468
469 #ifdef LX_THREAD_SAFE_ENABLE
470
471 /* If the thread safe option is enabled, create a ThreadX mutex that will be used in all external APIs
472 in order to provide thread-safe operation. */
473 status = tx_mutex_create(&nand_flash -> lx_nand_flash_mutex, "NAND Flash Mutex", TX_NO_INHERIT);
474
475 /* Determine if the mutex creation encountered an error. */
476 if (status != LX_SUCCESS)
477 {
478
479 /* Call system error handler, since this should not happen. */
480 _lx_nand_flash_system_error(nand_flash, LX_SYSTEM_MUTEX_CREATE_FAILED, 0, 0);
481
482 /* Return error to caller. */
483 return(LX_ERROR);
484 }
485 #endif
486
487 /* Lockout interrupts. */
488 LX_DISABLE
489
490 /* At this point, the NAND flash has been opened successfully. Place the
491 NAND flash control block on the linked list of currently opened NAND flashes. */
492
493 /* Set the NAND flash state to open. */
494 nand_flash -> lx_nand_flash_state = LX_NAND_FLASH_OPENED;
495
496 /* Place the NAND flash control block on the list of opened NAND flashes. First,
497 check for an empty list. */
498 if (_lx_nand_flash_opened_count)
499 {
500
501 /* List is not empty - other NAND flashes are open. */
502
503 /* Pickup tail pointer. */
504 tail_ptr = _lx_nand_flash_opened_ptr -> lx_nand_flash_open_previous;
505
506 /* Place the new NAND flash control block in the list. */
507 _lx_nand_flash_opened_ptr -> lx_nand_flash_open_previous = nand_flash;
508 tail_ptr -> lx_nand_flash_open_next = nand_flash;
509
510 /* Setup this NAND flash's opened links. */
511 nand_flash -> lx_nand_flash_open_previous = tail_ptr;
512 nand_flash -> lx_nand_flash_open_next = _lx_nand_flash_opened_ptr;
513 }
514 else
515 {
516
517 /* The opened NAND flash list is empty. Add the NAND flash to empty list. */
518 _lx_nand_flash_opened_ptr = nand_flash;
519 nand_flash -> lx_nand_flash_open_next = nand_flash;
520 nand_flash -> lx_nand_flash_open_previous = nand_flash;
521 }
522
523 /* Increment the opened NAND flash counter. */
524 _lx_nand_flash_opened_count++;
525
526 /* Restore interrupts. */
527 LX_RESTORE
528
529 /* Return a successful completion. */
530 return(LX_SUCCESS);
531 }
532
533