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_next_block_to_erase_find              PORTABLE C      */
43 /*                                                           6.3.0        */
44 /*  AUTHOR                                                                */
45 /*                                                                        */
46 /*    William E. Lamie, Microsoft Corporation                             */
47 /*                                                                        */
48 /*  DESCRIPTION                                                           */
49 /*                                                                        */
50 /*    This function finds the next block to erase in the NOR flash.       */
51 /*                                                                        */
52 /*  INPUT                                                                 */
53 /*                                                                        */
54 /*    nor_flash                             NOR flash instance            */
55 /*    return_erase_block                    Returned block to erase       */
56 /*    return_erase_count                    Returned erase count of block */
57 /*    return_mapped_sectors                 Returned number of mapped     */
58 /*                                            sectors                     */
59 /*    return_obsolete_sectors               Returned number of obsolete   */
60 /*                                            sectors                     */
61 /*                                                                        */
62 /*  OUTPUT                                                                */
63 /*                                                                        */
64 /*    return status                                                       */
65 /*                                                                        */
66 /*  CALLS                                                                 */
67 /*                                                                        */
68 /*    _lx_nor_flash_driver_read             Driver flash sector read      */
69 /*    _lx_nor_flash_system_error            Internal system error handler */
70 /*                                                                        */
71 /*  CALLED BY                                                             */
72 /*                                                                        */
73 /*    Internal LevelX                                                     */
74 /*                                                                        */
75 /*  RELEASE HISTORY                                                       */
76 /*                                                                        */
77 /*    DATE              NAME                      DESCRIPTION             */
78 /*                                                                        */
79 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
80 /*  09-30-2020     William E. Lamie         Modified comment(s),          */
81 /*                                            resulting in version 6.1    */
82 /*  06-02-2021     Bhupendra Naphade        Modified comment(s),          */
83 /*                                            resulting in version 6.1.7  */
84 /*  10-31-2023     Xiuwen Cai               Modified comment(s),          */
85 /*                                            added mapping bitmap cache, */
86 /*                                            added obsolete count cache, */
87 /*                                            optimized full obsoleted    */
88 /*                                            block searching logic,      */
89 /*                                            resulting in version 6.3.0  */
90 /*                                                                        */
91 /**************************************************************************/
_lx_nor_flash_next_block_to_erase_find(LX_NOR_FLASH * nor_flash,ULONG * return_erase_block,ULONG * return_erase_count,ULONG * return_mapped_sectors,ULONG * return_obsolete_sectors)92 UINT  _lx_nor_flash_next_block_to_erase_find(LX_NOR_FLASH *nor_flash, ULONG *return_erase_block, ULONG *return_erase_count, ULONG *return_mapped_sectors, ULONG *return_obsolete_sectors)
93 {
94 
95 ULONG   *block_word_ptr;
96 ULONG   *list_word_ptr;
97 ULONG   list_word;
98 ULONG   i, j;
99 ULONG   mapped_sectors;
100 ULONG   erase_count;
101 ULONG   obsolete_sectors;
102 ULONG   min_block_erase = 0;
103 ULONG   min_block_erase_count;
104 ULONG   min_block_obsolete_count = 0;
105 ULONG   min_block_mapped_count = 0;
106 ULONG   min_block_mapped_count_available = LX_FALSE;
107 ULONG   max_obsolete_sectors;
108 ULONG   max_obsolete_block = 0;
109 ULONG   max_obsolete_erase_count = 0;
110 ULONG   max_obsolete_mapped_count = 0;
111 ULONG   max_obsolete_mapped_count_available = LX_FALSE;
112 ULONG   min_system_block_erase_count;
113 ULONG   system_min_erased_blocks;
114 ULONG   max_system_block_erase_count;
115 ULONG   erase_count_threshold;
116 ULONG   min_logical_sector;
117 ULONG   max_logical_sector;
118 #ifndef LX_DIRECT_READ
119 UINT    status;
120 #endif
121 UINT    obsolete_sectors_available;
122 UINT    mapped_sectors_available;
123 
124 
125     /* Setup the block word pointer to the first word of the search block.  */
126     block_word_ptr =  nor_flash -> lx_nor_flash_base_address;
127 
128     /* Initialize the minimum erase count.  */
129     min_block_erase_count =  LX_ALL_ONES;
130 
131     /* Initialize the system minimum and maximum erase counts.  */
132     min_system_block_erase_count =  LX_ALL_ONES;
133     system_min_erased_blocks = 0;
134     max_system_block_erase_count =  0;
135 
136     /* Initialize the maximum obsolete sector count.  */
137     max_obsolete_sectors =  0;
138 
139     /* Calculate the erase count threshold.  */
140     if (nor_flash -> lx_nor_flash_free_physical_sectors >= nor_flash -> lx_nor_flash_physical_sectors_per_block)
141     {
142 
143         /* Calculate erase count threshold by adding constant to the current minimum.  */
144         erase_count_threshold =  nor_flash -> lx_nor_flash_minimum_erase_count + LX_NOR_FLASH_MAX_ERASE_COUNT_DELTA;
145     }
146     else
147     {
148 
149         /* When the number of free sectors is low, simply pick the block that has the most number of obsolete sectors.  */
150         erase_count_threshold =  LX_ALL_ONES;
151     }
152 
153     /* Loop through the blocks to attempt to find the mapped logical sector.  */
154     for (i = 0; i < nor_flash -> lx_nor_flash_total_blocks; i++)
155     {
156 
157         /* Read the erase count of this block.  */
158 #ifdef LX_DIRECT_READ
159 
160         /* Read the word directly.  */
161         erase_count =  *(block_word_ptr);
162 #else
163         status =  _lx_nor_flash_driver_read(nor_flash, block_word_ptr, &erase_count, 1);
164 
165         /* Check for an error from flash driver. Drivers should never return an error..  */
166         if (status)
167         {
168 
169             /* Call system error handler.  */
170             _lx_nor_flash_system_error(nor_flash, status);
171 
172             /* Return the error.  */
173             return(status);
174         }
175 #endif
176 
177         /* Update the system minimum and maximum erase counts.  */
178         if (erase_count == min_system_block_erase_count)
179         {
180             system_min_erased_blocks ++;
181         }
182         if (erase_count < min_system_block_erase_count)
183         {
184             min_system_block_erase_count =  erase_count;
185             system_min_erased_blocks = 1;
186         }
187         if (erase_count > max_system_block_erase_count)
188             max_system_block_erase_count =  erase_count;
189 
190         /* Initialize the obsolete and mapped sector count available flags.  */
191         obsolete_sectors_available =  LX_FALSE;
192         mapped_sectors_available =  LX_FALSE;
193 
194 #ifdef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
195 
196         /* Determine if the obsolete sector count is available in the cache.  */
197         if (i < nor_flash -> lx_nor_flash_extended_cache_obsolete_count_max_block)
198         {
199 
200             /* Yes, the obsolete sector count is available.  */
201             obsolete_sectors_available =  LX_TRUE;
202 
203             /* Pickup the obsolete sector count from the cache.  */
204             obsolete_sectors = (ULONG)nor_flash -> lx_nor_flash_extended_cache_obsolete_count[i];
205         }
206         else
207         {
208 #endif
209         /* Read the minimum and maximum logical sector values in this block.  */
210 #ifdef LX_DIRECT_READ
211 
212         /* Read the word directly.  */
213         min_logical_sector =  *(block_word_ptr + LX_NOR_FLASH_MIN_LOGICAL_SECTOR_OFFSET);
214 #else
215         status =  _lx_nor_flash_driver_read(nor_flash, block_word_ptr + LX_NOR_FLASH_MIN_LOGICAL_SECTOR_OFFSET, &min_logical_sector, 1);
216 
217         /* Check for an error from flash driver. Drivers should never return an error..  */
218         if (status)
219         {
220 
221             /* Call system error handler.  */
222             _lx_nor_flash_system_error(nor_flash, status);
223 
224             /* Return the error.  */
225             return(status);
226         }
227 #endif
228 
229         /* Determine if the minimum logical sector is valid.  */
230         if (min_logical_sector != LX_ALL_ONES)
231         {
232 #ifdef LX_DIRECT_READ
233 
234             /* Read the word directly.  */
235             max_logical_sector =  *(block_word_ptr + LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET);
236 #else
237             status =  _lx_nor_flash_driver_read(nor_flash, block_word_ptr + LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET, &max_logical_sector, 1);
238 
239             /* Check for an error from flash driver. Drivers should never return an error..  */
240             if (status)
241             {
242 
243                 /* Call system error handler.  */
244                 _lx_nor_flash_system_error(nor_flash, status);
245 
246                 /* Return the error.  */
247                 return(status);
248             }
249 #endif
250 
251             /* Are the values valid?  */
252             /* Now let's check to see if all the sector are obsoleted.  */
253             if ((max_logical_sector != LX_ALL_ONES) && (max_logical_sector < min_logical_sector))
254             {
255 
256                 obsolete_sectors_available =  LX_TRUE;
257                 obsolete_sectors =  nor_flash -> lx_nor_flash_physical_sectors_per_block;
258             }
259         }
260 #ifdef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
261         }
262 #endif
263 
264         /* Determine if the mapped sector count is available.  */
265         if (obsolete_sectors_available == LX_FALSE)
266         {
267 
268             /* Compute the number of obsolete and mapped sectors for this block.  */
269 
270             /* Initialize the obsolete and mapped sector counts.  */
271             obsolete_sectors =  0;
272             mapped_sectors =    0;
273 
274             /* Set the mapped sector count and obsolete sector count available flags.  */
275             mapped_sectors_available =  LX_TRUE;
276             obsolete_sectors_available =  LX_TRUE;
277 
278             /* Setup a pointer to the mapped list.  */
279             list_word_ptr =  block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset;
280 
281             /* Loop through the mapped list for this block.  */
282             for (j = 0; j < nor_flash -> lx_nor_flash_physical_sectors_per_block; j++)
283             {
284 
285                 /* Read the current mapping entry.  */
286 #ifdef LX_DIRECT_READ
287 
288                 /* Read the word directly.  */
289                 list_word =  *(list_word_ptr);
290 #else
291                 status =  _lx_nor_flash_driver_read(nor_flash, list_word_ptr, &list_word, 1);
292 
293                 /* Check for an error from flash driver. Drivers should never return an error..  */
294                 if (status)
295                 {
296 
297                     /* Call system error handler.  */
298                     _lx_nor_flash_system_error(nor_flash, status);
299 
300                     /* Return the error.  */
301                     return(status);
302                 }
303 #endif
304 
305                 /* Determine if the entry hasn't been used.  */
306                 if (list_word == LX_NOR_PHYSICAL_SECTOR_FREE)
307                 {
308 
309                     /* Since allocations are done sequentially in the block, we know nothing
310                        else exists after this point.  */
311                     break;
312                 }
313 
314                 /* Is this entry obsolete?  */
315                 if ((list_word & LX_NOR_PHYSICAL_SECTOR_VALID) == 0)
316                 {
317 
318                     /* Increment the number of obsolete sectors.  */
319                     obsolete_sectors++;
320                 }
321                 else
322                 {
323 
324                     /* Increment the number of mapped sectors.  */
325                     mapped_sectors++;
326                 }
327 
328                 /* Move the list pointer ahead.  */
329                 list_word_ptr++;
330             }
331         }
332 
333         /* Determine if this block contains full obsoleted sectors and the erase count is minimum.  */
334         if ((obsolete_sectors == nor_flash -> lx_nor_flash_physical_sectors_per_block) &&
335             (erase_count == nor_flash -> lx_nor_flash_minimum_erase_count) &&
336             (nor_flash -> lx_nor_flash_minimum_erased_blocks > 0))
337         {
338 
339             /* Yes, we have a full obsoleted block with minimum erase count.  */
340             *return_erase_block =       i;
341             *return_erase_count =       erase_count;
342             *return_obsolete_sectors =  obsolete_sectors;
343             *return_mapped_sectors =    mapped_sectors;
344 
345             break;
346         }
347 
348 
349         /* Determine if we have a block with a new maximum number of obsolete sectors.  */
350         if ((obsolete_sectors > max_obsolete_sectors) && (erase_count <= erase_count_threshold))
351         {
352 
353             /* Update the new maximum obsolete sectors and related information.  */
354             max_obsolete_sectors =      obsolete_sectors;
355             max_obsolete_block =        i;
356             max_obsolete_erase_count =  erase_count;
357             max_obsolete_mapped_count = mapped_sectors;
358             max_obsolete_mapped_count_available = mapped_sectors_available;
359 
360         }
361         else if ((max_obsolete_sectors) && (obsolete_sectors == max_obsolete_sectors) && (erase_count <= erase_count_threshold))
362         {
363 
364             /* Another block has the same number of obsolete sectors.  Does this new block have a smaller erase
365                count?  */
366             if (erase_count < max_obsolete_erase_count)
367             {
368 
369                 /* Yes, erase the block with the smaller erase count.  */
370                 max_obsolete_sectors =      obsolete_sectors;
371                 max_obsolete_block =        i;
372                 max_obsolete_erase_count =  erase_count;
373                 max_obsolete_mapped_count = mapped_sectors;
374                 max_obsolete_mapped_count_available = mapped_sectors_available;
375             }
376         }
377 
378         /* Determine if we have a new minimum erase count.  */
379         if (erase_count < min_block_erase_count)
380         {
381 
382             /* Update the new minimum erase count and related information.  */
383             min_block_erase_count =     erase_count;
384             min_block_erase =           i;
385             min_block_obsolete_count =  obsolete_sectors;
386             min_block_mapped_count =    mapped_sectors;
387             min_block_mapped_count_available = mapped_sectors_available;
388         }
389 
390         /* Move to the next block.  */
391         block_word_ptr =  block_word_ptr + nor_flash -> lx_nor_flash_words_per_block;
392     }
393 
394     /* Determine if we found a block with full obsoleted sector and the erase count is minimum.  */
395     if (i == nor_flash -> lx_nor_flash_total_blocks)
396     {
397 
398         /* Determine if we can erase the block with the most obsolete sectors.  */
399         if (max_obsolete_sectors)
400         {
401 
402             /* Erase the block with the most obsolete sectors.  */
403             *return_erase_block =       max_obsolete_block;
404             *return_erase_count =       max_obsolete_erase_count;
405             *return_obsolete_sectors =  max_obsolete_sectors;
406             *return_mapped_sectors =    max_obsolete_mapped_count;
407             mapped_sectors_available =  max_obsolete_mapped_count_available;
408         }
409         else
410         {
411 
412             /* Otherwise, choose the block with the smallest erase count.  */
413             *return_erase_block =       min_block_erase;
414             *return_erase_count =       min_block_erase_count;
415             *return_obsolete_sectors =  min_block_obsolete_count;
416             *return_mapped_sectors =    min_block_mapped_count;
417             mapped_sectors_available =  min_block_mapped_count_available;
418         }
419 
420         /* Update the overall minimum and maximum erase count.  */
421         nor_flash -> lx_nor_flash_minimum_erase_count =  min_system_block_erase_count;
422         nor_flash -> lx_nor_flash_minimum_erased_blocks =  system_min_erased_blocks;
423         nor_flash -> lx_nor_flash_maximum_erase_count =  max_system_block_erase_count;
424     }
425 
426     /* Determine if the mapped sector count is available.  */
427     if (mapped_sectors_available == LX_FALSE)
428     {
429 
430         /* Compute the number of obsolete and mapped sectors for this block.  */
431         mapped_sectors =  0;
432 
433         /* Setup a pointer to the mapped list.  */
434         block_word_ptr =  nor_flash -> lx_nor_flash_base_address + *return_erase_block * nor_flash -> lx_nor_flash_words_per_block;
435         list_word_ptr =  block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset;
436 
437         /* Loop through the mapped list for this block.  */
438         for (j = 0; j < nor_flash -> lx_nor_flash_physical_sectors_per_block; j++)
439         {
440 
441             /* Read the current mapping entry.  */
442 #ifdef LX_DIRECT_READ
443 
444             /* Read the word directly.  */
445             list_word =  *(list_word_ptr);
446 #else
447             status =  _lx_nor_flash_driver_read(nor_flash, list_word_ptr, &list_word, 1);
448 
449             /* Check for an error from flash driver. Drivers should never return an error..  */
450             if (status)
451             {
452 
453                 /* Call system error handler.  */
454                 _lx_nor_flash_system_error(nor_flash, status);
455 
456                 /* Return the error.  */
457                 return(status);
458             }
459 #endif
460 
461             /* Determine if the entry hasn't been used.  */
462             if (list_word == LX_NOR_PHYSICAL_SECTOR_FREE)
463             {
464 
465                 /* Since allocations are done sequentially in the block, we know nothing
466                        else exists after this point.  */
467                 break;
468             }
469 
470             /* Is this entry mapped?  */
471             if ((list_word & LX_NOR_PHYSICAL_SECTOR_VALID) != 0)
472             {
473 
474                 /* Increment the number of mapped sectors.  */
475                 mapped_sectors++;
476             }
477 
478             /* Move the list pointer ahead.  */
479             list_word_ptr++;
480         }
481 
482         /* Return the mapped sector count.  */
483         *return_mapped_sectors = mapped_sectors;
484 
485     }
486     /* Return success.  */
487     return(LX_SUCCESS);
488 }
489 
490