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