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