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