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 /** NOR 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_nor_flash_block_reclaim PORTABLE C */
43 /* 6.3.0 */
44 /* AUTHOR */
45 /* */
46 /* William E. Lamie, Microsoft Corporation */
47 /* */
48 /* DESCRIPTION */
49 /* */
50 /* This function reclaims one block from the NOR flash. */
51 /* */
52 /* INPUT */
53 /* */
54 /* nor_flash NOR flash instance */
55 /* */
56 /* OUTPUT */
57 /* */
58 /* return status */
59 /* */
60 /* CALLS */
61 /* */
62 /* _lx_nor_flash_driver_block_erase Driver erase block */
63 /* _lx_nor_flash_driver_write Driver flash sector write */
64 /* _lx_nor_flash_driver_read Driver flash sector read */
65 /* _lx_nor_flash_next_block_to_erase_find */
66 /* Find next block to erase */
67 /* _lx_nor_flash_physical_sector_allocate */
68 /* Allocate new logical sector */
69 /* _lx_nor_flash_sector_mapping_cache_invalidate */
70 /* Invalidate cache entry */
71 /* _lx_nor_flash_system_error Internal system error handler */
72 /* */
73 /* CALLED BY */
74 /* */
75 /* Internal LevelX */
76 /* */
77 /* RELEASE HISTORY */
78 /* */
79 /* DATE NAME DESCRIPTION */
80 /* */
81 /* 05-19-2020 William E. Lamie Initial Version 6.0 */
82 /* 09-30-2020 William E. Lamie Modified comment(s), */
83 /* resulting in version 6.1 */
84 /* 06-02-2021 Bhupendra Naphade Modified comment(s), */
85 /* resulting in version 6.1.7 */
86 /* 10-31-2023 Xiuwen Cai Modified comment(s), */
87 /* added count for minimum */
88 /* erased blocks, added */
89 /* obsolete count cache, */
90 /* resulting in version 6.3.0 */
91 /* */
92 /**************************************************************************/
_lx_nor_flash_block_reclaim(LX_NOR_FLASH * nor_flash)93 UINT _lx_nor_flash_block_reclaim(LX_NOR_FLASH *nor_flash)
94 {
95
96 ULONG *block_word_ptr;
97 ULONG *list_word_ptr;
98 ULONG list_word;
99 ULONG i;
100 ULONG erase_block;
101 ULONG erase_count;
102 ULONG temp_erase_count;
103 ULONG erase_started_value;
104 ULONG mapped_sectors;
105 ULONG obsolete_sectors;
106 ULONG free_sectors;
107 ULONG logical_sector;
108 ULONG *new_mapping_address;
109 ULONG *new_sector_address;
110 ULONG new_mapping_entry;
111 UINT status;
112
113
114 /* Determine the next block to erase. */
115 _lx_nor_flash_next_block_to_erase_find(nor_flash, &erase_block, &erase_count, &mapped_sectors, &obsolete_sectors);
116
117 /* Determine if the search pointer is set for this block. */
118 if (nor_flash -> lx_nor_flash_free_block_search == erase_block)
119 {
120
121 /* Ensure the search block is not the block we are trying to free. */
122 nor_flash -> lx_nor_flash_free_block_search = erase_block + 1;
123
124 /* Check for wrap condition. */
125 if (nor_flash -> lx_nor_flash_free_block_search >= nor_flash -> lx_nor_flash_total_blocks)
126 nor_flash -> lx_nor_flash_free_block_search = 0;
127 }
128
129 /* Setup the block word pointer to the first word of the search block. */
130 block_word_ptr = nor_flash -> lx_nor_flash_base_address + (nor_flash -> lx_nor_flash_words_per_block * erase_block);
131
132 /* Determine if this block is completely obsolete. */
133 if (obsolete_sectors == nor_flash -> lx_nor_flash_physical_sectors_per_block)
134 {
135
136 /* Write the erased started indication. */
137 erase_started_value = LX_BLOCK_ERASE_STARTED;
138 status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_started_value, 1);
139
140 /* Check for an error from flash driver. Drivers should never return an error.. */
141 if (status)
142 {
143
144 /* Call system error handler. */
145 _lx_nor_flash_system_error(nor_flash, status);
146
147 /* Return the error. */
148 return(status);
149 }
150
151 /* Erase the entire block. */
152 status = _lx_nor_flash_driver_block_erase(nor_flash, erase_block, erase_count+1);
153
154 /* Check for an error from flash driver. Drivers should never return an error.. */
155 if (status)
156 {
157
158 /* Call system error handler. */
159 _lx_nor_flash_system_error(nor_flash, status);
160
161 /* Return the error. */
162 return(status);
163 }
164
165 /* Determine if the erase count is at the minimum. */
166 if (erase_count == nor_flash -> lx_nor_flash_minimum_erase_count)
167 {
168
169 /* Yes, decrement the minimum erased block count. */
170 nor_flash -> lx_nor_flash_minimum_erased_blocks--;
171 }
172
173 /* Increment the erase count. */
174 erase_count++;
175
176 /* Determine if the new erase count exceeds the maximum. */
177 if (erase_count > ((ULONG) LX_BLOCK_ERASE_COUNT_MAX))
178 {
179
180 /* Yes, erase count is in overflow. Stay at the maximum count. */
181 erase_count = ((ULONG) LX_BLOCK_ERASE_COUNT_MAX);
182 }
183
184 /* Determine if we need to update the maximum erase count. */
185 if (erase_count > nor_flash -> lx_nor_flash_maximum_erase_count)
186 {
187
188 /* Yes, a new maximum is present. */
189 nor_flash -> lx_nor_flash_maximum_erase_count = erase_count;
190 }
191
192 /* Setup the free bit map that corresponds to the free physical sectors in this
193 block. Note that we only need to setup the portion of the free bit map that doesn't
194 have sectors associated with it. */
195 status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr+(nor_flash -> lx_nor_flash_block_free_bit_map_offset + (nor_flash -> lx_nor_flash_block_bit_map_words - 1)),
196 &(nor_flash -> lx_nor_flash_block_bit_map_mask), 1);
197
198 /* Check for an error from flash driver. Drivers should never return an error.. */
199 if (status)
200 {
201
202 /* Call system error handler. */
203 _lx_nor_flash_system_error(nor_flash, status);
204
205 /* Return the error. */
206 return(status);
207 }
208
209 /* Write the initial erase count for the block with upper bit set. */
210 temp_erase_count = (erase_count | LX_BLOCK_ERASED);
211 status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &temp_erase_count, 1);
212
213 /* Check for an error from flash driver. Drivers should never return an error.. */
214 if (status)
215 {
216
217 /* Call system error handler. */
218 _lx_nor_flash_system_error(nor_flash, status);
219
220 /* Return the error. */
221 return(status);
222 }
223
224 /* Write the final initial erase count for the block. */
225 status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_count, 1);
226
227 /* Check for an error from flash driver. Drivers should never return an error.. */
228 if (status)
229 {
230
231 /* Call system error handler. */
232 _lx_nor_flash_system_error(nor_flash, status);
233
234 /* Return the error. */
235 return(status);
236 }
237
238 /* Update parameters of this flash. */
239 nor_flash -> lx_nor_flash_free_physical_sectors = nor_flash -> lx_nor_flash_free_physical_sectors + obsolete_sectors;
240 nor_flash -> lx_nor_flash_obsolete_physical_sectors = nor_flash -> lx_nor_flash_obsolete_physical_sectors - obsolete_sectors;
241 #ifdef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
242
243 /* Check if the block is cached by obsolete count cache. */
244 if (erase_block < nor_flash -> lx_nor_flash_extended_cache_obsolete_count_max_block)
245 {
246
247 /* Yes, clear the obsolete count for this block. */
248 nor_flash -> lx_nor_flash_extended_cache_obsolete_count[erase_block] = 0;
249 }
250 #endif
251 }
252 else
253 {
254
255 /* Calculate the number of free sectors in this block. */
256 free_sectors = nor_flash -> lx_nor_flash_physical_sectors_per_block - (obsolete_sectors + mapped_sectors);
257
258 /* Determine if there are enough free sectors outside of this block to reclaim this block. */
259 if (mapped_sectors <= (nor_flash -> lx_nor_flash_free_physical_sectors - free_sectors))
260 {
261
262 /* Setup a pointer to the mapped list. */
263 list_word_ptr = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset;
264
265 /* Now search through the list to find mapped sectors to move. */
266 for (i = 0; i < nor_flash -> lx_nor_flash_physical_sectors_per_block; i++)
267 {
268
269 /* Pickup the mapped sector list entry. */
270 #ifdef LX_DIRECT_READ
271
272 /* Read the word directly. */
273 list_word = *(list_word_ptr);
274 #else
275 status = _lx_nor_flash_driver_read(nor_flash, list_word_ptr, &list_word, 1);
276
277 /* Check for an error from flash driver. Drivers should never return an error.. */
278 if (status)
279 {
280
281 /* Call system error handler. */
282 _lx_nor_flash_system_error(nor_flash, status);
283
284 /* Return the error. */
285 return(status);
286 }
287 #endif
288
289 /* Determine if the entry hasn't been used. */
290 if (list_word == LX_NOR_PHYSICAL_SECTOR_FREE)
291 {
292
293 /* Since allocations are done sequentially in the block, we know nothing
294 else exists after this point. */
295 break;
296 }
297
298 /* Is this entry mapped? */
299 if (list_word & LX_NOR_PHYSICAL_SECTOR_VALID)
300 {
301
302 /* Pickup the logical sector associated with this mapped physical sector. */
303 logical_sector = list_word & LX_NOR_LOGICAL_SECTOR_MASK;
304
305 /* Invalidate the old sector mapping cache entry. */
306 _lx_nor_flash_sector_mapping_cache_invalidate(nor_flash, logical_sector);
307
308 /* Allocate a new physical sector for this write. */
309 _lx_nor_flash_physical_sector_allocate(nor_flash, logical_sector, &new_mapping_address, &new_sector_address);
310
311 /* Check to see if the new sector is also in the erase block. */
312 if ((new_sector_address >= block_word_ptr) && (new_sector_address < (block_word_ptr + nor_flash -> lx_nor_flash_words_per_block)))
313 {
314
315 /* Yes, the new sector was found in the block to be erased. Simply move the search pointer
316 to the block after the erase block and search for another sector from there. */
317 nor_flash -> lx_nor_flash_free_block_search = erase_block + 1;
318
319 /* Check for wrap condition. */
320 if (nor_flash -> lx_nor_flash_free_block_search >= nor_flash -> lx_nor_flash_total_blocks)
321 nor_flash -> lx_nor_flash_free_block_search = 0;
322
323 /* Allocate a new physical sector for this write. */
324 _lx_nor_flash_physical_sector_allocate(nor_flash, logical_sector, &new_mapping_address, &new_sector_address);
325
326 /* Check again for the new sector inside of the block to erase. This should be impossible, since
327 we check previously if there are enough free sectors outside of this block needed to reclaim
328 this block. */
329 if ((new_sector_address >= block_word_ptr) && (new_sector_address < (block_word_ptr + LX_NOR_SECTOR_SIZE)))
330 {
331
332 /* System error, a new sector is not available outside of the erase block.
333 Clear the new sector so we fall through to the error handling. */
334 new_mapping_address = LX_NULL;
335 }
336 }
337
338 /* Determine if the new sector allocation was successful. */
339 if (new_mapping_address)
340 {
341
342 /* Yes, we were able to allocate a new physical sector. */
343
344 #ifdef LX_DIRECT_READ
345 /* First, write the sector data to the new physical sector. */
346 status = _lx_nor_flash_driver_write(nor_flash, new_sector_address, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset) +
347 (i * LX_NOR_SECTOR_SIZE), LX_NOR_SECTOR_SIZE);
348
349 /* Check for an error from flash driver. Drivers should never return an error.. */
350 if (status)
351 {
352
353 /* Call system error handler. */
354 _lx_nor_flash_system_error(nor_flash, status);
355
356 /* Return the error. */
357 return(status);
358 }
359 #else
360
361 /* First, read the sector data into the internal memory of the NOR flash instance. This internal memory
362 is supplied by the underlying driver during initialization. */
363 status = _lx_nor_flash_driver_read(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset) +
364 (i * LX_NOR_SECTOR_SIZE), nor_flash -> lx_nor_flash_sector_buffer,
365 LX_NOR_SECTOR_SIZE);
366
367 /* Check for an error from flash driver. Drivers should never return an error.. */
368 if (status)
369 {
370
371 /* Call system error handler. */
372 _lx_nor_flash_system_error(nor_flash, status);
373
374 /* Return the error. */
375 return(status);
376 }
377
378 /* Next, write the sector data from the internal buffer to the new physical sector. */
379 status = _lx_nor_flash_driver_write(nor_flash, new_sector_address, nor_flash -> lx_nor_flash_sector_buffer, LX_NOR_SECTOR_SIZE);
380
381 /* Check for an error from flash driver. Drivers should never return an error.. */
382 if (status)
383 {
384
385 /* Call system error handler. */
386 _lx_nor_flash_system_error(nor_flash, status);
387
388 /* Return the error. */
389 return(status);
390 }
391 #endif
392
393 /* Now deprecate the old sector mapping. */
394
395 /* Clear bit 30, which indicates this sector is superceded. */
396 list_word = list_word & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED);
397
398 /* Write the value back to the flash to clear bit 30. */
399 status = _lx_nor_flash_driver_write(nor_flash, list_word_ptr, &list_word, 1);
400
401 /* Check for an error from flash driver. Drivers should never return an error.. */
402 if (status)
403 {
404
405 /* Call system error handler. */
406 _lx_nor_flash_system_error(nor_flash, status);
407
408 /* Return the error. */
409 return(status);
410 }
411
412 /* Now build the new mapping entry - with the not valid bit set initially. */
413 new_mapping_entry = ((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID) | ((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED) | (ULONG) LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID | logical_sector;
414
415 /* Write out the new mapping entry. */
416 status = _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 1);
417
418 /* Check for an error from flash driver. Drivers should never return an error.. */
419 if (status)
420 {
421
422 /* Call system error handler. */
423 _lx_nor_flash_system_error(nor_flash, status);
424
425 /* Return the error. */
426 return(status);
427 }
428
429 /* Now clear the not valid bit to make this sector mapping valid. This is done because the writing of the extra bytes itself can
430 be interrupted and we need to make sure this can be detected when the flash is opened again. */
431 new_mapping_entry = new_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID);
432
433 /* Clear the not valid bit. */
434 status = _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 1);
435
436 /* Check for an error from flash driver. Drivers should never return an error.. */
437 if (status)
438 {
439
440 /* Call system error handler. */
441 _lx_nor_flash_system_error(nor_flash, status);
442
443 /* Return the error. */
444 return(status);
445 }
446
447 /* Now clear bit 31, which indicates this sector is now obsoleted. */
448 list_word = list_word & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID);
449
450 /* Write the value back to the flash to clear bit 31. */
451 status = _lx_nor_flash_driver_write(nor_flash, list_word_ptr, &list_word, 1);
452
453 /* Check for an error from flash driver. Drivers should never return an error.. */
454 if (status)
455 {
456
457 /* Call system error handler. */
458 _lx_nor_flash_system_error(nor_flash, status);
459
460 /* Return the error. */
461 return(status);
462 }
463 }
464 else
465 {
466
467 /* Call system error handler - the allocation should always succeed at this point. */
468 _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_ALLOCATION_FAILED);
469
470 /* Return the error. */
471 return(status);
472 }
473
474 /* Decrement the number of mapped sectors. */
475 mapped_sectors--;
476
477 /* Determine if we are done. */
478 if (mapped_sectors == 0)
479 break;
480 }
481
482 /* Move the list pointer ahead. */
483 list_word_ptr++;
484 }
485
486 /* Write the erased started indication. */
487 erase_started_value = LX_BLOCK_ERASE_STARTED;
488 status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_started_value, 1);
489
490 /* Check for an error from flash driver. Drivers should never return an error.. */
491 if (status)
492 {
493
494 /* Call system error handler. */
495 _lx_nor_flash_system_error(nor_flash, status);
496
497 /* Return the error. */
498 return(status);
499 }
500
501 /* Erase the entire block. */
502 status = _lx_nor_flash_driver_block_erase(nor_flash, erase_block, erase_count+1);
503
504 /* Check for an error from flash driver. Drivers should never return an error.. */
505 if (status)
506 {
507
508 /* Call system error handler. */
509 _lx_nor_flash_system_error(nor_flash, status);
510
511 /* Return the error. */
512 return(status);
513 }
514
515 /* Determine if the erase count is at the minimum. */
516 if (erase_count == nor_flash -> lx_nor_flash_minimum_erase_count)
517 {
518
519 /* Yes, decrement the minimum erased block count. */
520 nor_flash -> lx_nor_flash_minimum_erased_blocks--;
521 }
522
523 /* Increment the erase count. */
524 erase_count++;
525
526 /* Determine if the new erase count exceeds the maximum. */
527 if (erase_count > ((ULONG) LX_BLOCK_ERASE_COUNT_MAX))
528 {
529
530 /* Yes, erase count is in overflow. Stay at the maximum count. */
531 erase_count = ((ULONG) LX_BLOCK_ERASE_COUNT_MAX);
532 }
533
534 /* Determine if we need to update the maximum erase count. */
535 if (erase_count > nor_flash -> lx_nor_flash_maximum_erase_count)
536 {
537
538 /* Yes, a new maximum is present. */
539 nor_flash -> lx_nor_flash_maximum_erase_count = erase_count;
540 }
541
542 /* Setup the free bit map that corresponds to the free physical sectors in this
543 block. Note that we only need to setup the portion of the free bit map that doesn't
544 have sectors associated with it. */
545 status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr+(nor_flash -> lx_nor_flash_block_free_bit_map_offset + (nor_flash -> lx_nor_flash_block_bit_map_words - 1)) ,
546 &(nor_flash -> lx_nor_flash_block_bit_map_mask), 1);
547
548 /* Check for an error from flash driver. Drivers should never return an error.. */
549 if (status)
550 {
551
552 /* Call system error handler. */
553 _lx_nor_flash_system_error(nor_flash, status);
554
555 /* Return the error. */
556 return(status);
557 }
558
559 /* Write the initial erase count for the block with the upper bit set. */
560 temp_erase_count = (erase_count | LX_BLOCK_ERASED);
561 status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &temp_erase_count, 1);
562
563 /* Check for an error from flash driver. Drivers should never return an error.. */
564 if (status)
565 {
566
567 /* Call system error handler. */
568 _lx_nor_flash_system_error(nor_flash, status);
569
570 /* Return the error. */
571 return(status);
572 }
573
574 /* Write the final initial erase count for the block. */
575 status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_count, 1);
576
577 /* Check for an error from flash driver. Drivers should never return an error.. */
578 if (status)
579 {
580
581 /* Call system error handler. */
582 _lx_nor_flash_system_error(nor_flash, status);
583
584 /* Return the error. */
585 return(status);
586 }
587
588 /* Update parameters of this flash. */
589 nor_flash -> lx_nor_flash_free_physical_sectors = nor_flash -> lx_nor_flash_free_physical_sectors + obsolete_sectors;
590 nor_flash -> lx_nor_flash_obsolete_physical_sectors = nor_flash -> lx_nor_flash_obsolete_physical_sectors - obsolete_sectors;
591 #ifdef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
592
593 /* Check if the block is cached by obsolete count cache. */
594 if (erase_block < nor_flash -> lx_nor_flash_extended_cache_obsolete_count_max_block)
595 {
596
597 /* Yes, clear the obsolete count for this block. */
598 nor_flash -> lx_nor_flash_extended_cache_obsolete_count[erase_block] = 0;
599 }
600 #endif
601 }
602 }
603
604 /* Return status. */
605 return(LX_SUCCESS);
606 }
607
608