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