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.1.7 */
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 /* */
87 /**************************************************************************/
_lx_nor_flash_block_reclaim(LX_NOR_FLASH * nor_flash)88 UINT _lx_nor_flash_block_reclaim(LX_NOR_FLASH *nor_flash)
89 {
90
91 ULONG *block_word_ptr;
92 ULONG *list_word_ptr;
93 ULONG list_word;
94 ULONG i;
95 ULONG erase_block;
96 ULONG erase_count;
97 ULONG temp_erase_count;
98 ULONG erase_started_value;
99 ULONG mapped_sectors;
100 ULONG obsolete_sectors;
101 ULONG free_sectors;
102 ULONG logical_sector;
103 ULONG *new_mapping_address;
104 ULONG *new_sector_address;
105 ULONG new_mapping_entry;
106 UINT status;
107
108
109 /* Determine the next block to erase. */
110 _lx_nor_flash_next_block_to_erase_find(nor_flash, &erase_block, &erase_count, &mapped_sectors, &obsolete_sectors);
111
112 /* Determine if the search pointer is set for this block. */
113 if (nor_flash -> lx_nor_flash_free_block_search == erase_block)
114 {
115
116 /* Ensure the search block is not the block we are trying to free. */
117 nor_flash -> lx_nor_flash_free_block_search = erase_block + 1;
118
119 /* Check for wrap condition. */
120 if (nor_flash -> lx_nor_flash_free_block_search >= nor_flash -> lx_nor_flash_total_blocks)
121 nor_flash -> lx_nor_flash_free_block_search = 0;
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 + (nor_flash -> lx_nor_flash_words_per_block * erase_block);
126
127 /* Determine if this block is completely obsolete. */
128 if (obsolete_sectors == nor_flash -> lx_nor_flash_physical_sectors_per_block)
129 {
130
131 /* Write the erased started indication. */
132 erase_started_value = LX_BLOCK_ERASE_STARTED;
133 status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_started_value, 1);
134
135 /* Check for an error from flash driver. Drivers should never return an error.. */
136 if (status)
137 {
138
139 /* Call system error handler. */
140 _lx_nor_flash_system_error(nor_flash, status);
141
142 /* Return the error. */
143 return(status);
144 }
145
146 /* Erase the entire block. */
147 status = _lx_nor_flash_driver_block_erase(nor_flash, erase_block, erase_count+1);
148
149 /* Check for an error from flash driver. Drivers should never return an error.. */
150 if (status)
151 {
152
153 /* Call system error handler. */
154 _lx_nor_flash_system_error(nor_flash, status);
155
156 /* Return the error. */
157 return(status);
158 }
159
160 /* Increment the erase count. */
161 erase_count++;
162
163 /* Determine if the new erase count exceeds the maximum. */
164 if (erase_count > ((ULONG) LX_BLOCK_ERASE_COUNT_MAX))
165 {
166
167 /* Yes, erase count is in overflow. Stay at the maximum count. */
168 erase_count = ((ULONG) LX_BLOCK_ERASE_COUNT_MAX);
169 }
170
171 /* Determine if we need to update the maximum erase count. */
172 if (erase_count > nor_flash -> lx_nor_flash_maximum_erase_count)
173 {
174
175 /* Yes, a new maximum is present. */
176 nor_flash -> lx_nor_flash_maximum_erase_count = erase_count;
177 }
178
179 /* Setup the free bit map that corresponds to the free physical sectors in this
180 block. Note that we only need to setup the portion of the free bit map that doesn't
181 have sectors associated with it. */
182 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)),
183 &(nor_flash -> lx_nor_flash_block_bit_map_mask), 1);
184
185 /* Check for an error from flash driver. Drivers should never return an error.. */
186 if (status)
187 {
188
189 /* Call system error handler. */
190 _lx_nor_flash_system_error(nor_flash, status);
191
192 /* Return the error. */
193 return(status);
194 }
195
196 /* Write the initial erase count for the block with upper bit set. */
197 temp_erase_count = (erase_count | LX_BLOCK_ERASED);
198 status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &temp_erase_count, 1);
199
200 /* Check for an error from flash driver. Drivers should never return an error.. */
201 if (status)
202 {
203
204 /* Call system error handler. */
205 _lx_nor_flash_system_error(nor_flash, status);
206
207 /* Return the error. */
208 return(status);
209 }
210
211 /* Write the final initial erase count for the block. */
212 status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_count, 1);
213
214 /* Check for an error from flash driver. Drivers should never return an error.. */
215 if (status)
216 {
217
218 /* Call system error handler. */
219 _lx_nor_flash_system_error(nor_flash, status);
220
221 /* Return the error. */
222 return(status);
223 }
224
225 /* Update parameters of this flash. */
226 nor_flash -> lx_nor_flash_free_physical_sectors = nor_flash -> lx_nor_flash_free_physical_sectors + obsolete_sectors;
227 nor_flash -> lx_nor_flash_obsolete_physical_sectors = nor_flash -> lx_nor_flash_obsolete_physical_sectors - obsolete_sectors;
228 }
229 else
230 {
231
232 /* Calculate the number of free sectors in this block. */
233 free_sectors = nor_flash -> lx_nor_flash_physical_sectors_per_block - (obsolete_sectors + mapped_sectors);
234
235 /* Determine if there are enough free sectors outside of this block to reclaim this block. */
236 if (mapped_sectors <= (nor_flash -> lx_nor_flash_free_physical_sectors - free_sectors))
237 {
238
239 /* Setup a pointer to the mapped list. */
240 list_word_ptr = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset;
241
242 /* Now search through the list to find mapped sectors to move. */
243 for (i = 0; i < nor_flash -> lx_nor_flash_physical_sectors_per_block; i++)
244 {
245
246 /* Pickup the mapped sector list entry. */
247 #ifdef LX_DIRECT_READ
248
249 /* Read the word directly. */
250 list_word = *(list_word_ptr);
251 #else
252 status = _lx_nor_flash_driver_read(nor_flash, list_word_ptr, &list_word, 1);
253
254 /* Check for an error from flash driver. Drivers should never return an error.. */
255 if (status)
256 {
257
258 /* Call system error handler. */
259 _lx_nor_flash_system_error(nor_flash, status);
260
261 /* Return the error. */
262 return(status);
263 }
264 #endif
265
266 /* Determine if the entry hasn't been used. */
267 if (list_word == LX_NOR_PHYSICAL_SECTOR_FREE)
268 {
269
270 /* Since allocations are done sequentially in the block, we know nothing
271 else exists after this point. */
272 break;
273 }
274
275 /* Is this entry mapped? */
276 if (list_word & LX_NOR_PHYSICAL_SECTOR_VALID)
277 {
278
279 /* Pickup the logical sector associated with this mapped physical sector. */
280 logical_sector = list_word & LX_NOR_LOGICAL_SECTOR_MASK;
281
282 /* Invalidate the old sector mapping cache entry. */
283 _lx_nor_flash_sector_mapping_cache_invalidate(nor_flash, logical_sector);
284
285 /* Allocate a new physical sector for this write. */
286 _lx_nor_flash_physical_sector_allocate(nor_flash, logical_sector, &new_mapping_address, &new_sector_address);
287
288 /* Check to see if the new sector is also in the erase block. */
289 if ((new_sector_address >= block_word_ptr) && (new_sector_address < (block_word_ptr + nor_flash -> lx_nor_flash_words_per_block)))
290 {
291
292 /* Yes, the new sector was found in the block to be erased. Simply move the search pointer
293 to the block after the erase block and search for another sector from there. */
294 nor_flash -> lx_nor_flash_free_block_search = erase_block + 1;
295
296 /* Check for wrap condition. */
297 if (nor_flash -> lx_nor_flash_free_block_search >= nor_flash -> lx_nor_flash_total_blocks)
298 nor_flash -> lx_nor_flash_free_block_search = 0;
299
300 /* Allocate a new physical sector for this write. */
301 _lx_nor_flash_physical_sector_allocate(nor_flash, logical_sector, &new_mapping_address, &new_sector_address);
302
303 /* Check again for the new sector inside of the block to erase. This should be impossible, since
304 we check previously if there are enough free sectors outside of this block needed to reclaim
305 this block. */
306 if ((new_sector_address >= block_word_ptr) && (new_sector_address < (block_word_ptr + LX_NOR_SECTOR_SIZE)))
307 {
308
309 /* System error, a new sector is not available outside of the erase block.
310 Clear the new sector so we fall through to the error handling. */
311 new_mapping_address = LX_NULL;
312 }
313 }
314
315 /* Determine if the new sector allocation was successful. */
316 if (new_mapping_address)
317 {
318
319 /* Yes, we were able to allocate a new physical sector. */
320
321 #ifdef LX_DIRECT_READ
322 /* First, write the sector data to the new physical sector. */
323 status = _lx_nor_flash_driver_write(nor_flash, new_sector_address, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset) +
324 (i * LX_NOR_SECTOR_SIZE), LX_NOR_SECTOR_SIZE);
325
326 /* Check for an error from flash driver. Drivers should never return an error.. */
327 if (status)
328 {
329
330 /* Call system error handler. */
331 _lx_nor_flash_system_error(nor_flash, status);
332
333 /* Return the error. */
334 return(status);
335 }
336 #else
337
338 /* First, read the sector data into the internal memory of the NOR flash instance. This internal memory
339 is supplied by the underlying driver during initialization. */
340 status = _lx_nor_flash_driver_read(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset) +
341 (i * LX_NOR_SECTOR_SIZE), nor_flash -> lx_nor_flash_sector_buffer,
342 LX_NOR_SECTOR_SIZE);
343
344 /* Check for an error from flash driver. Drivers should never return an error.. */
345 if (status)
346 {
347
348 /* Call system error handler. */
349 _lx_nor_flash_system_error(nor_flash, status);
350
351 /* Return the error. */
352 return(status);
353 }
354
355 /* Next, write the sector data from the internal buffer to the new physical sector. */
356 status = _lx_nor_flash_driver_write(nor_flash, new_sector_address, nor_flash -> lx_nor_flash_sector_buffer, LX_NOR_SECTOR_SIZE);
357
358 /* Check for an error from flash driver. Drivers should never return an error.. */
359 if (status)
360 {
361
362 /* Call system error handler. */
363 _lx_nor_flash_system_error(nor_flash, status);
364
365 /* Return the error. */
366 return(status);
367 }
368 #endif
369
370 /* Now deprecate the old sector mapping. */
371
372 /* Clear bit 30, which indicates this sector is superceded. */
373 list_word = list_word & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED);
374
375 /* Write the value back to the flash to clear bit 30. */
376 status = _lx_nor_flash_driver_write(nor_flash, list_word_ptr, &list_word, 1);
377
378 /* Check for an error from flash driver. Drivers should never return an error.. */
379 if (status)
380 {
381
382 /* Call system error handler. */
383 _lx_nor_flash_system_error(nor_flash, status);
384
385 /* Return the error. */
386 return(status);
387 }
388
389 /* Now build the new mapping entry - with the not valid bit set initially. */
390 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;
391
392 /* Write out the new mapping entry. */
393 status = _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 1);
394
395 /* Check for an error from flash driver. Drivers should never return an error.. */
396 if (status)
397 {
398
399 /* Call system error handler. */
400 _lx_nor_flash_system_error(nor_flash, status);
401
402 /* Return the error. */
403 return(status);
404 }
405
406 /* Now clear the not valid bit to make this sector mapping valid. This is done because the writing of the extra bytes itself can
407 be interrupted and we need to make sure this can be detected when the flash is opened again. */
408 new_mapping_entry = new_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID);
409
410 /* Clear the not valid bit. */
411 status = _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 1);
412
413 /* Check for an error from flash driver. Drivers should never return an error.. */
414 if (status)
415 {
416
417 /* Call system error handler. */
418 _lx_nor_flash_system_error(nor_flash, status);
419
420 /* Return the error. */
421 return(status);
422 }
423
424 /* Now clear bit 31, which indicates this sector is now obsoleted. */
425 list_word = list_word & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID);
426
427 /* Write the value back to the flash to clear bit 31. */
428 status = _lx_nor_flash_driver_write(nor_flash, list_word_ptr, &list_word, 1);
429
430 /* Check for an error from flash driver. Drivers should never return an error.. */
431 if (status)
432 {
433
434 /* Call system error handler. */
435 _lx_nor_flash_system_error(nor_flash, status);
436
437 /* Return the error. */
438 return(status);
439 }
440 }
441 else
442 {
443
444 /* Call system error handler - the allocation should always succeed at this point. */
445 _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_ALLOCATION_FAILED);
446
447 /* Return the error. */
448 return(status);
449 }
450
451 /* Decrement the number of mapped sectors. */
452 mapped_sectors--;
453
454 /* Determine if we are done. */
455 if (mapped_sectors == 0)
456 break;
457 }
458
459 /* Move the list pointer ahead. */
460 list_word_ptr++;
461 }
462
463 /* Write the erased started indication. */
464 erase_started_value = LX_BLOCK_ERASE_STARTED;
465 status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_started_value, 1);
466
467 /* Check for an error from flash driver. Drivers should never return an error.. */
468 if (status)
469 {
470
471 /* Call system error handler. */
472 _lx_nor_flash_system_error(nor_flash, status);
473
474 /* Return the error. */
475 return(status);
476 }
477
478 /* Erase the entire block. */
479 status = _lx_nor_flash_driver_block_erase(nor_flash, erase_block, erase_count+1);
480
481 /* Check for an error from flash driver. Drivers should never return an error.. */
482 if (status)
483 {
484
485 /* Call system error handler. */
486 _lx_nor_flash_system_error(nor_flash, status);
487
488 /* Return the error. */
489 return(status);
490 }
491
492 /* Increment the erase count. */
493 erase_count++;
494
495 /* Determine if the new erase count exceeds the maximum. */
496 if (erase_count > ((ULONG) LX_BLOCK_ERASE_COUNT_MAX))
497 {
498
499 /* Yes, erase count is in overflow. Stay at the maximum count. */
500 erase_count = ((ULONG) LX_BLOCK_ERASE_COUNT_MAX);
501 }
502
503 /* Determine if we need to update the maximum erase count. */
504 if (erase_count > nor_flash -> lx_nor_flash_maximum_erase_count)
505 {
506
507 /* Yes, a new maximum is present. */
508 nor_flash -> lx_nor_flash_maximum_erase_count = erase_count;
509 }
510
511 /* Setup the free bit map that corresponds to the free physical sectors in this
512 block. Note that we only need to setup the portion of the free bit map that doesn't
513 have sectors associated with it. */
514 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)) ,
515 &(nor_flash -> lx_nor_flash_block_bit_map_mask), 1);
516
517 /* Check for an error from flash driver. Drivers should never return an error.. */
518 if (status)
519 {
520
521 /* Call system error handler. */
522 _lx_nor_flash_system_error(nor_flash, status);
523
524 /* Return the error. */
525 return(status);
526 }
527
528 /* Write the initial erase count for the block with the upper bit set. */
529 temp_erase_count = (erase_count | LX_BLOCK_ERASED);
530 status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &temp_erase_count, 1);
531
532 /* Check for an error from flash driver. Drivers should never return an error.. */
533 if (status)
534 {
535
536 /* Call system error handler. */
537 _lx_nor_flash_system_error(nor_flash, status);
538
539 /* Return the error. */
540 return(status);
541 }
542
543 /* Write the final initial erase count for the block. */
544 status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_count, 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 /* Update parameters of this flash. */
558 nor_flash -> lx_nor_flash_free_physical_sectors = nor_flash -> lx_nor_flash_free_physical_sectors + obsolete_sectors;
559 nor_flash -> lx_nor_flash_obsolete_physical_sectors = nor_flash -> lx_nor_flash_obsolete_physical_sectors - obsolete_sectors;
560 }
561 }
562
563 /* Return status. */
564 return(LX_SUCCESS);
565 }
566
567