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