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