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