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_logical_sector_find PORTABLE C */
43 /* 6.3.0 */
44 /* AUTHOR */
45 /* */
46 /* William E. Lamie, Microsoft Corporation */
47 /* */
48 /* DESCRIPTION */
49 /* */
50 /* This function attempts to find the specified logical sector in */
51 /* the NOR flash. */
52 /* */
53 /* INPUT */
54 /* */
55 /* nor_flash NOR flash instance */
56 /* logical_sector Logical sector number */
57 /* superceded_check Check for sector being */
58 /* superceded (can happen if */
59 /* on interruptions of sector */
60 /* write) */
61 /* physical_sector_map_entry Destination for physical */
62 /* sector map entry address */
63 /* physical_sector_address Destination for physical */
64 /* sector data */
65 /* */
66 /* OUTPUT */
67 /* */
68 /* return status */
69 /* */
70 /* CALLS */
71 /* */
72 /* _lx_nor_flash_driver_read Driver flash sector read */
73 /* _lx_nor_flash_driver_write Driver flash sector write */
74 /* _lx_nor_flash_system_error Internal system error handler */
75 /* */
76 /* CALLED BY */
77 /* */
78 /* Internal LevelX */
79 /* */
80 /* RELEASE HISTORY */
81 /* */
82 /* DATE NAME DESCRIPTION */
83 /* */
84 /* 05-19-2020 William E. Lamie Initial Version 6.0 */
85 /* 09-30-2020 William E. Lamie Modified comment(s), */
86 /* resulting in version 6.1 */
87 /* 06-02-2021 Bhupendra Naphade Modified comment(s), */
88 /* resulting in version 6.1.7 */
89 /* 10-31-2023 Xiuwen Cai Modified comment(s), */
90 /* added mapping bitmap cache, */
91 /* added obsolete count cache, */
92 /* optimized full obsoleted */
93 /* block searching logic, */
94 /* resulting in version 6.3.0 */
95 /* */
96 /**************************************************************************/
_lx_nor_flash_logical_sector_find(LX_NOR_FLASH * nor_flash,ULONG logical_sector,ULONG superceded_check,ULONG ** physical_sector_map_entry,ULONG ** physical_sector_address)97 UINT _lx_nor_flash_logical_sector_find(LX_NOR_FLASH *nor_flash, ULONG logical_sector, ULONG superceded_check, ULONG **physical_sector_map_entry, ULONG **physical_sector_address)
98 {
99
100 ULONG *block_word_ptr;
101 ULONG *list_word_ptr;
102 ULONG list_word;
103 ULONG min_logical_sector;
104 ULONG max_logical_sector;
105 ULONG mapped_sectors;
106 ULONG total_blocks;
107 ULONG total_sectors;
108 ULONG i, j;
109 ULONG search_start;
110 LX_NOR_SECTOR_MAPPING_CACHE_ENTRY *sector_mapping_cache_entry_ptr = LX_NULL;
111 LX_NOR_SECTOR_MAPPING_CACHE_ENTRY temp_sector_mapping_cache_entry;
112 #ifndef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
113 ULONG valid_sector_found;
114 #endif
115 #if !defined(LX_DIRECT_READ) || !defined(LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE)
116 UINT status;
117 #endif
118
119
120 /* Initialize the return parameters. */
121 *physical_sector_map_entry = (ULONG *) 0;
122 *physical_sector_address = (ULONG *) 0;
123
124 /* Determine if there are any mapped physical sectors. */
125 if (nor_flash -> lx_nor_flash_mapped_physical_sectors == 0)
126 {
127
128 /* No mapped sector so nothing can be found!. */
129 return(LX_SECTOR_NOT_FOUND);
130 }
131
132 #ifndef LX_NOR_DISABLE_EXTENDED_CACHE
133 #ifdef LX_NOR_ENABLE_MAPPING_BITMAP
134
135 /* Determine if the logical sector is in the range of mapping bitmap cache. */
136 if (logical_sector < nor_flash -> lx_nor_flash_extended_cache_mapping_bitmap_max_logical_sector)
137 {
138
139 /* Determine if the logical sector is mapped. */
140 if ((nor_flash -> lx_nor_flash_extended_cache_mapping_bitmap[logical_sector >> 5] & (ULONG)(1 << (logical_sector & 31))) == 0)
141 {
142
143 /* Not mapped, return not found. */
144 return(LX_SECTOR_NOT_FOUND);
145 }
146 }
147 #endif
148 #endif
149
150 /* Determine if the sector mapping cache is enabled. */
151 if (nor_flash -> lx_nor_flash_sector_mapping_cache_enabled)
152 {
153
154 /* Calculate the starting index of the sector cache for this sector entry. */
155 i = (logical_sector & LX_NOR_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NOR_SECTOR_MAPPING_CACHE_DEPTH;
156
157 /* Build a pointer to the cache entry. */
158 sector_mapping_cache_entry_ptr = &nor_flash -> lx_nor_flash_sector_mapping_cache[i];
159
160 /* Determine if the sector is in the sector mapping cache - assuming the depth of the sector
161 mapping cache is LX_NOR_SECTOR_MAPPING_CACHE_DEPTH entries. */
162 if ((sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID))
163 {
164
165 /* Increment the sector mapping cache hit counter. */
166 nor_flash -> lx_nor_flash_sector_mapping_cache_hits++;
167
168 /* Yes, return the cached values associated with the sector. */
169 *physical_sector_map_entry = sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_map_entry;
170 *physical_sector_address = sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_address;
171
172 /* Don't move anything since we found the entry at the top. */
173
174 /* Return a successful status. */
175 return(LX_SUCCESS);
176 }
177 else if (((sector_mapping_cache_entry_ptr + 1) -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID))
178 {
179
180 /* Increment the sector mapping cache hit counter. */
181 nor_flash -> lx_nor_flash_sector_mapping_cache_hits++;
182
183 /* Yes, return the cached values associated with the sector. */
184 *physical_sector_map_entry = (sector_mapping_cache_entry_ptr + 1) -> lx_nor_sector_mapping_cache_physical_sector_map_entry;
185 *physical_sector_address = (sector_mapping_cache_entry_ptr + 1) -> lx_nor_sector_mapping_cache_physical_sector_address;
186
187 /* Just swap the first and second entry. */
188 temp_sector_mapping_cache_entry = *(sector_mapping_cache_entry_ptr);
189 *(sector_mapping_cache_entry_ptr) = *(sector_mapping_cache_entry_ptr + 1);
190 *(sector_mapping_cache_entry_ptr + 1) = temp_sector_mapping_cache_entry;
191
192 /* Return a successful status. */
193 return(LX_SUCCESS);
194 }
195 else if (((sector_mapping_cache_entry_ptr + 2) -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID))
196 {
197
198 /* Increment the sector mapping cache hit counter. */
199 nor_flash -> lx_nor_flash_sector_mapping_cache_hits++;
200
201 /* Yes, return the cached value. */
202 *physical_sector_map_entry = (sector_mapping_cache_entry_ptr + 2) -> lx_nor_sector_mapping_cache_physical_sector_map_entry;
203 *physical_sector_address = (sector_mapping_cache_entry_ptr + 2) -> lx_nor_sector_mapping_cache_physical_sector_address;
204
205 /* Move the third entry to the top and the first two entries down. */
206 temp_sector_mapping_cache_entry = *(sector_mapping_cache_entry_ptr);
207 *(sector_mapping_cache_entry_ptr) = *(sector_mapping_cache_entry_ptr + 2);
208 *(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1);
209 *(sector_mapping_cache_entry_ptr + 1) = temp_sector_mapping_cache_entry;
210
211 /* Return a successful status. */
212 return(LX_SUCCESS);
213 }
214 else if (((sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID))
215 {
216
217 /* Increment the sector mapping cache hit counter. */
218 nor_flash -> lx_nor_flash_sector_mapping_cache_hits++;
219
220 /* Yes, return the cached value. */
221 *physical_sector_map_entry = (sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_physical_sector_map_entry;
222 *physical_sector_address = (sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_physical_sector_address;
223
224 /* Move the last entry to the top and the first three entries down. */
225 temp_sector_mapping_cache_entry = *(sector_mapping_cache_entry_ptr);
226 *(sector_mapping_cache_entry_ptr) = *(sector_mapping_cache_entry_ptr + 3);
227 *(sector_mapping_cache_entry_ptr + 3) = *(sector_mapping_cache_entry_ptr + 2);
228 *(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1);
229 *(sector_mapping_cache_entry_ptr + 1) = temp_sector_mapping_cache_entry;
230
231 /* Return a successful status. */
232 return(LX_SUCCESS);
233 }
234
235 /* If we get here, we have a cache miss so increment the counter before we fall through the loop. */
236 nor_flash -> lx_nor_flash_sector_mapping_cache_misses++;
237 }
238
239 /* Setup the total number of mapped sectors. */
240 mapped_sectors = nor_flash -> lx_nor_flash_mapped_physical_sectors;
241
242 /* Start searching from the last found block. */
243 i = nor_flash -> lx_nor_flash_found_block_search;
244
245 /* Setup the starting sector to look at. */
246 j = nor_flash -> lx_nor_flash_found_sector_search;
247
248 /* Pickup the total number of blocks. */
249 total_blocks = nor_flash -> lx_nor_flash_total_blocks;
250
251 /* Loop through the blocks to attempt to find the mapped logical sector. */
252 while (total_blocks--)
253 {
254
255 #ifdef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
256 /* Determine if the obsolete sector count is available in the cache. */
257 if (i < nor_flash -> lx_nor_flash_extended_cache_obsolete_count_max_block)
258 {
259
260 /* Check if the block contains obsolete sectors only. */
261 if ((ULONG)nor_flash -> lx_nor_flash_extended_cache_obsolete_count[i] == nor_flash -> lx_nor_flash_physical_sectors_per_block)
262 {
263
264 /* Move to the next block. */
265 i++;
266
267 /* Determine if we have wrapped. */
268 if (i >= nor_flash -> lx_nor_flash_total_blocks)
269 {
270
271 /* Yes, we have wrapped, set to block 0. */
272 i = 0;
273 }
274
275 /* Start at the first sector in the next block. */
276 j = 0;
277
278 /* No point in looking further into this block, just continue the loop. */
279 continue;
280
281 }
282 }
283 #endif
284
285 /* Setup the block word pointer to the first word of the search block. */
286 block_word_ptr = (nor_flash -> lx_nor_flash_base_address + (i * nor_flash -> lx_nor_flash_words_per_block));
287
288 /* Determine if the minimum and maximum logical sector values are present in the block header. If these are
289 present, we can quickly skip blocks that don't have our sector. */
290
291 /* Read the minimum and maximum logical sector values in this block. */
292 #ifdef LX_DIRECT_READ
293
294 /* Read the word directly. */
295 min_logical_sector = *(block_word_ptr + LX_NOR_FLASH_MIN_LOGICAL_SECTOR_OFFSET);
296 #else
297 status = _lx_nor_flash_driver_read(nor_flash, block_word_ptr + LX_NOR_FLASH_MIN_LOGICAL_SECTOR_OFFSET, &min_logical_sector, 1);
298
299 /* Check for an error from flash driver. Drivers should never return an error.. */
300 if (status)
301 {
302
303 /* Call system error handler. */
304 _lx_nor_flash_system_error(nor_flash, status);
305
306 /* Return the error. */
307 return(status);
308 }
309 #endif
310
311 /* Is the value valid? */
312 if (min_logical_sector != LX_ALL_ONES)
313 {
314 #ifdef LX_DIRECT_READ
315
316 /* Read the word directly. */
317 max_logical_sector = *(block_word_ptr + LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET);
318 #else
319 status = _lx_nor_flash_driver_read(nor_flash, block_word_ptr + LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET, &max_logical_sector, 1);
320
321 /* Check for an error from flash driver. Drivers should never return an error.. */
322 if (status)
323 {
324
325 /* Call system error handler. */
326 _lx_nor_flash_system_error(nor_flash, status);
327
328 /* Return the error. */
329 return(status);
330 }
331 #endif
332
333 /* Is the value valid? */
334 if (max_logical_sector != LX_ALL_ONES)
335 {
336
337 /* Now let's check to see if the search sector is within this range. */
338 if ((logical_sector < min_logical_sector) || (logical_sector > max_logical_sector))
339 {
340
341 /* Move to the next block. */
342 i++;
343
344 /* Determine if we have wrapped. */
345 if (i >= nor_flash -> lx_nor_flash_total_blocks)
346 {
347
348 /* Yes, we have wrapped, set to block 0. */
349 i = 0;
350 }
351
352 /* Start at the first sector in the next block. */
353 j = 0;
354
355 /* No point in looking further into this block, just continue the loop. */
356 continue;
357 }
358 }
359 }
360 else
361 {
362
363 /* Set the max logical sector to all ones. */
364 max_logical_sector = LX_ALL_ONES;
365 }
366 #ifndef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
367
368 /* Clear the valid sector found flag. */
369 valid_sector_found = LX_FALSE;
370 #endif
371
372 /* Setup the total number of sectors. */
373 total_sectors = nor_flash -> lx_nor_flash_physical_sectors_per_block;
374
375 /* Remember the start of the search. */
376 search_start = j;
377
378 /* Now search through the sector list to find a match. */
379 while (total_sectors--)
380 {
381
382 /* Setup a pointer to the mapped list. */
383 list_word_ptr = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j;
384
385
386 /* Read in the mapped list for this block. */
387 #ifdef LX_DIRECT_READ
388
389 /* Read the word directly. */
390 list_word = *(list_word_ptr);
391 #else
392 status = _lx_nor_flash_driver_read(nor_flash, list_word_ptr, &list_word, 1);
393
394 /* Check for an error from flash driver. Drivers should never return an error.. */
395 if (status)
396 {
397
398 /* Call system error handler. */
399 _lx_nor_flash_system_error(nor_flash, status);
400
401 /* Return the error. */
402 return(status);
403 }
404 #endif
405
406 /* Determine if the entry hasn't been used. */
407 if (list_word == LX_NOR_PHYSICAL_SECTOR_FREE)
408 {
409
410 /* Since the mapping is done sequentially in the block, we know nothing
411 else exists after this point. */
412
413 /* Determine if the search started at the beginning of the block. */
414 if (search_start == 0)
415 {
416
417 /* Yes, we started at the beginning of the block. We are now done with this block. */
418 break;
419 }
420 else
421 {
422
423 /* Setup the new total to the search start. */
424 total_sectors = search_start;
425
426 /* Clear search start. */
427 search_start = 0;
428
429 /* Start search over. */
430 j = 0;
431 continue;
432 }
433 }
434
435 /* Is this entry valid? */
436 if ((list_word & (LX_NOR_PHYSICAL_SECTOR_VALID | LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID)) == LX_NOR_PHYSICAL_SECTOR_VALID)
437 {
438
439 /* Decrement the number of mapped sectors. */
440 mapped_sectors--;
441
442 /* Do we have a valid sector match? */
443 if ((list_word & LX_NOR_LOGICAL_SECTOR_MASK) == logical_sector)
444 {
445
446 /* Determine if we care about the superceded bit. */
447 if (superceded_check == LX_FALSE)
448 {
449
450 /* Prepare the return information. */
451 *physical_sector_map_entry = list_word_ptr;
452 *physical_sector_address = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset + (j * LX_NOR_SECTOR_SIZE);
453
454 /* Determine if the sector mapping cache is enabled. */
455 if (nor_flash -> lx_nor_flash_sector_mapping_cache_enabled)
456 {
457
458 /* Yes, update the cache with the sector mapping. */
459
460 /* Move all the cache entries down so the oldest is at the bottom. */
461 *(sector_mapping_cache_entry_ptr + 3) = *(sector_mapping_cache_entry_ptr + 2);
462 *(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1);
463 *(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr);
464
465 /* Setup the new sector information in the cache. */
466 sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_logical_sector = (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID);
467 sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_map_entry = *physical_sector_map_entry;
468 sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_address = *physical_sector_address;
469 }
470
471 /* Remember the last found block for next search. */
472 nor_flash -> lx_nor_flash_found_block_search = i;
473
474 /* Remember the last found sector. */
475 nor_flash -> lx_nor_flash_found_sector_search = j+1;
476
477 /* Has this wrapped around? */
478 if (nor_flash -> lx_nor_flash_found_sector_search >= nor_flash -> lx_nor_flash_physical_sectors_per_block)
479 {
480
481 /* Reset to the beginning sector. */
482 nor_flash -> lx_nor_flash_found_sector_search = 0;
483 }
484
485 /* Return success! */
486 return(LX_SUCCESS);
487 }
488
489 /* Check for the superceded bit being clear, which means the sector was superceded. */
490 else if (list_word & LX_NOR_PHYSICAL_SECTOR_SUPERCEDED)
491 {
492
493 /* Prepare the return information. */
494 *physical_sector_map_entry = list_word_ptr;
495 *physical_sector_address = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset + (j * LX_NOR_SECTOR_SIZE);
496
497 /* No need to update the cache here, since this condition only happens during initialization. */
498
499 /* Remember the last found block for next search. */
500 nor_flash -> lx_nor_flash_found_block_search = i;
501
502 /* Remember the last found sector. */
503 nor_flash -> lx_nor_flash_found_sector_search = j+1;
504
505 /* Has this wrapped around? */
506 if (nor_flash -> lx_nor_flash_found_sector_search >= nor_flash -> lx_nor_flash_physical_sectors_per_block)
507 {
508
509 /* Reset to the beginning sector. */
510 nor_flash -> lx_nor_flash_found_sector_search = 0;
511 }
512
513 /* Return success! */
514 return(LX_SUCCESS);
515 }
516 }
517 #ifndef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
518
519 /* Set the valid sector found flag. */
520 valid_sector_found = LX_TRUE;
521 #endif
522 }
523
524 /* Move to the next list entry. */
525 j++;
526
527 /* Check for wrap around. */
528 if (j >= nor_flash -> lx_nor_flash_physical_sectors_per_block)
529 {
530
531 /* Yes, wrap around, go back to the beginning. */
532 j = 0;
533 }
534 }
535 #ifndef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
536 /* Check if the block contains no valid sectors. */
537 if ((valid_sector_found == LX_FALSE) && (max_logical_sector != LX_ALL_ONES))
538 {
539
540 /* Clear max logical sector to indicate sectors are all obsoleted. */
541 max_logical_sector = 0;
542
543 /* Write the max logical sector to the block header. */
544 status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr + LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET, &max_logical_sector, 1);
545
546 /* Check for an error from flash driver. Drivers should never return an error.. */
547 if (status)
548 {
549
550 /* Call system error handler. */
551 _lx_nor_flash_system_error(nor_flash, status);
552
553 /* Return the error. */
554 return(status);
555 }
556 }
557 #endif
558
559 /* Determine if there are any more mapped sectors. */
560 if (mapped_sectors == 0)
561 break;
562
563 /* Move to the next block. */
564 i++;
565
566 /* Determine if we have wrapped. */
567 if (i >= nor_flash -> lx_nor_flash_total_blocks)
568 {
569
570 /* Yes, we have wrapped, set to block 0. */
571 i = 0;
572 }
573
574 /* Start at the first sector in the next block. */
575 j = 0;
576 }
577
578 /* Return sector not found status. */
579 return(LX_SECTOR_NOT_FOUND);
580 }
581
582