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_sector_write                          PORTABLE C      */
43 /*                                                           6.3.0        */
44 /*  AUTHOR                                                                */
45 /*                                                                        */
46 /*    William E. Lamie, Microsoft Corporation                             */
47 /*                                                                        */
48 /*  DESCRIPTION                                                           */
49 /*                                                                        */
50 /*    This function writes a logical sector to the NOR flash.             */
51 /*                                                                        */
52 /*  INPUT                                                                 */
53 /*                                                                        */
54 /*    nor_flash                             NOR flash instance            */
55 /*    logical_sector                        Logical sector number         */
56 /*    buffer                                Pointer to buffer to write    */
57 /*                                            (the size is 512 bytes)     */
58 /*                                                                        */
59 /*  OUTPUT                                                                */
60 /*                                                                        */
61 /*    return status                                                       */
62 /*                                                                        */
63 /*  CALLS                                                                 */
64 /*                                                                        */
65 /*    _lx_nor_flash_driver_write            Driver flash sector write     */
66 /*    _lx_nor_flash_driver_read             Driver flash sector read      */
67 /*    _lx_nor_flash_block_reclaim           Reclaim one flash block       */
68 /*    _lx_nor_flash_logical_sector_find     Find logical sector           */
69 /*    _lx_nor_flash_physical_sector_allocate                              */
70 /*                                          Allocate new physical sector  */
71 /*    _lx_nor_flash_sector_mapping_cache_invalidate                       */
72 /*                                          Invalidate cache entry        */
73 /*    _lx_nor_flash_system_error            Internal system error handler */
74 /*    tx_mutex_get                          Get thread protection         */
75 /*    tx_mutex_put                          Release thread protection     */
76 /*                                                                        */
77 /*  CALLED BY                                                             */
78 /*                                                                        */
79 /*    Application Code                                                    */
80 /*                                                                        */
81 /*  RELEASE HISTORY                                                       */
82 /*                                                                        */
83 /*    DATE              NAME                      DESCRIPTION             */
84 /*                                                                        */
85 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
86 /*  09-30-2020     William E. Lamie         Modified comment(s),          */
87 /*                                            resulting in version 6.1    */
88 /*  06-02-2021     Bhupendra Naphade        Modified comment(s),          */
89 /*                                            resulting in version 6.1.7  */
90 /*  10-31-2023     Xiuwen Cai               Modified comment(s),          */
91 /*                                            added mapping bitmap cache, */
92 /*                                            added obsolete count cache, */
93 /*                                            resulting in version 6.3.0  */
94 /*                                                                        */
95 /**************************************************************************/
_lx_nor_flash_sector_write(LX_NOR_FLASH * nor_flash,ULONG logical_sector,VOID * buffer)96 UINT  _lx_nor_flash_sector_write(LX_NOR_FLASH *nor_flash, ULONG logical_sector, VOID *buffer)
97 {
98 
99 ULONG                           *old_mapping_address;
100 ULONG                           *old_sector_address;
101 ULONG                           old_mapping_entry;
102 ULONG                           *new_mapping_address;
103 ULONG                           *new_sector_address;
104 ULONG                           new_mapping_entry;
105 ULONG                           i;
106 LX_NOR_SECTOR_MAPPING_CACHE_ENTRY  *sector_mapping_cache_entry_ptr;
107 UINT                            status;
108 #ifdef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
109 ULONG                           block;
110 #endif
111 
112 #ifdef LX_THREAD_SAFE_ENABLE
113 
114     /* Obtain the thread safe mutex.  */
115     tx_mutex_get(&nor_flash -> lx_nor_flash_mutex, TX_WAIT_FOREVER);
116 #endif
117 
118     /* Determine if there are less than two block's worth of free sectors.  */
119     i =  0;
120     while (nor_flash -> lx_nor_flash_free_physical_sectors <= nor_flash -> lx_nor_flash_physical_sectors_per_block)
121     {
122 
123         /* Attempt to reclaim one physical block.  */
124         _lx_nor_flash_block_reclaim(nor_flash);
125 
126         /* Increment the block count.  */
127         i++;
128 
129         /* Have we exceeded the number of blocks in the system?  */
130         if (i >= nor_flash -> lx_nor_flash_total_blocks)
131         {
132 
133             /* Yes, break out of the loop.  */
134             break;
135         }
136     }
137 
138     /* Increment the number of write requests.  */
139     nor_flash -> lx_nor_flash_write_requests++;
140 
141     /* See if we can find the sector in the current mapping.  */
142     _lx_nor_flash_logical_sector_find(nor_flash, logical_sector, LX_FALSE, &old_mapping_address, &old_sector_address);
143 
144     /* Allocate a new physical sector for this write.  */
145     _lx_nor_flash_physical_sector_allocate(nor_flash, logical_sector, &new_mapping_address, &new_sector_address);
146 
147     /* Determine if the new sector allocation was successful.  */
148     if (new_mapping_address)
149     {
150 
151         /* Yes, we were able to allocate a new physical sector.  */
152 
153         /* Update the number of free physical sectors.  */
154         nor_flash -> lx_nor_flash_free_physical_sectors--;
155 
156         /* Write the sector data to the new physical sector.  */
157         status =  _lx_nor_flash_driver_write(nor_flash, new_sector_address, buffer, LX_NOR_SECTOR_SIZE);
158 
159         /* Check for an error from flash driver. Drivers should never return an error..  */
160         if (status)
161         {
162 
163             /* Call system error handler.  */
164             _lx_nor_flash_system_error(nor_flash, status);
165 
166 #ifdef LX_THREAD_SAFE_ENABLE
167 
168             /* Release the thread safe mutex.  */
169             tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
170 #endif
171 
172             /* Return status.  */
173             return(LX_ERROR);
174         }
175 
176         /* Was there a previously mapped sector?  */
177         if (old_mapping_address)
178         {
179 
180             /* Now deprecate the old sector mapping.  */
181 
182             /* Read in the old sector mapping.  */
183 #ifdef LX_DIRECT_READ
184 
185             /* Read the word directly.  */
186             old_mapping_entry =  *(old_mapping_address);
187 #else
188             status =  _lx_nor_flash_driver_read(nor_flash, old_mapping_address, &old_mapping_entry, 1);
189 
190             /* Check for an error from flash driver. Drivers should never return an error..  */
191             if (status)
192             {
193 
194                 /* Call system error handler.  */
195                 _lx_nor_flash_system_error(nor_flash, status);
196 
197 #ifdef LX_THREAD_SAFE_ENABLE
198 
199                 /* Release the thread safe mutex.  */
200                 tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
201 #endif
202 
203                 /* Return status.  */
204                 return(LX_ERROR);
205             }
206 #endif
207 
208             /* Clear bit 30, which indicates this sector is superceded.  */
209             old_mapping_entry =  old_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED);
210 
211             /* Write the value back to the flash to clear bit 30.  */
212             status =  _lx_nor_flash_driver_write(nor_flash, old_mapping_address, &old_mapping_entry, 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 #ifdef LX_THREAD_SAFE_ENABLE
222 
223                 /* Release the thread safe mutex.  */
224                 tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
225 #endif
226 
227                 /* Return status.  */
228                 return(LX_ERROR);
229             }
230         }
231 
232         /* Now build the new mapping entry - with the not valid bit set initially.  */
233         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;
234 
235         /* Write out the new mapping entry.  */
236         status =  _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 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 #ifdef LX_THREAD_SAFE_ENABLE
246 
247             /* Release the thread safe mutex.  */
248             tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
249 #endif
250 
251             /* Return status.  */
252             return(LX_ERROR);
253         }
254 
255         /* Now clear the not valid bit to make this sector mapping valid.  This is done because the writing of the extra bytes itself can
256            be interrupted and we need to make sure this can be detected when the flash is opened again.  */
257         new_mapping_entry =  new_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID);
258 
259         /* Clear the not valid bit.  */
260         status =  _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 1);
261 
262         /* Check for an error from flash driver. Drivers should never return an error..  */
263         if (status)
264         {
265 
266             /* Call system error handler.  */
267             _lx_nor_flash_system_error(nor_flash, status);
268 
269 #ifdef LX_THREAD_SAFE_ENABLE
270 
271             /* Release the thread safe mutex.  */
272             tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
273 #endif
274 
275             /* Return status.  */
276             return(LX_ERROR);
277         }
278 #ifndef LX_NOR_DISABLE_EXTENDED_CACHE
279 #ifdef LX_NOR_ENABLE_MAPPING_BITMAP
280 
281         /* Determine if the logical sector is within the mapping bitmap.  */
282         if (logical_sector < nor_flash -> lx_nor_flash_extended_cache_mapping_bitmap_max_logical_sector)
283         {
284 
285             /* Set the bit in the mapping bitmap.  */
286             nor_flash -> lx_nor_flash_extended_cache_mapping_bitmap[logical_sector >> 5] |= (ULONG)(1 << (logical_sector & 31));
287         }
288 #endif
289 #endif
290 
291         /* Increment the number of mapped physical sectors.  */
292         nor_flash -> lx_nor_flash_mapped_physical_sectors++;
293 
294         /* Was there a previously mapped sector?  */
295         if (old_mapping_address)
296         {
297 
298             /* Now clear bit 31, which indicates this sector is now obsoleted.  */
299             old_mapping_entry =  old_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID);
300 
301             /* Write the value back to the flash to clear bit 31.  */
302             status =  _lx_nor_flash_driver_write(nor_flash, old_mapping_address, &old_mapping_entry, 1);
303 
304             /* Check for an error from flash driver. Drivers should never return an error..  */
305             if (status)
306             {
307 
308                 /* Call system error handler.  */
309                 _lx_nor_flash_system_error(nor_flash, status);
310 
311 #ifdef LX_THREAD_SAFE_ENABLE
312 
313                 /* Release the thread safe mutex.  */
314                 tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
315 #endif
316 
317               /* Return status.  */
318                 return(LX_ERROR);
319             }
320 
321             /* Increment the number of obsolete physical sectors.  */
322             nor_flash -> lx_nor_flash_obsolete_physical_sectors++;
323 
324 #ifdef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
325 
326             /* Get the block number from mapping address.  */
327             block = (ULONG)(old_mapping_address - nor_flash -> lx_nor_flash_base_address) / nor_flash -> lx_nor_flash_words_per_block;
328 
329             /* Determine if this block is within the range of the obsolete count cache.  */
330             if (block < nor_flash -> lx_nor_flash_extended_cache_obsolete_count_max_block)
331             {
332 
333                 /* Increment the obsolete count for this block.  */
334                 nor_flash -> lx_nor_flash_extended_cache_obsolete_count[block] ++;
335             }
336 #endif
337 
338             /* Decrement the number of mapped physical sectors.  */
339             nor_flash -> lx_nor_flash_mapped_physical_sectors--;
340 
341             /* Invalidate the old sector mapping cache entry.  */
342             _lx_nor_flash_sector_mapping_cache_invalidate(nor_flash, logical_sector);
343         }
344 
345         /* Determine if the sector mapping cache is enabled.  */
346         if (nor_flash -> lx_nor_flash_sector_mapping_cache_enabled)
347         {
348 
349             /* Yes, sector mapping cache is enabled, place this sector information in the cache.  */
350 
351             /* Calculate the starting index of the sector mapping cache for this sector entry.  */
352             i =  (logical_sector & LX_NOR_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NOR_SECTOR_MAPPING_CACHE_DEPTH;
353 
354             /* Build a pointer to the cache entry.  */
355             sector_mapping_cache_entry_ptr =  &nor_flash -> lx_nor_flash_sector_mapping_cache[i];
356 
357             /* Move all the cache entries down so the oldest is at the bottom.  */
358             *(sector_mapping_cache_entry_ptr + 3) =  *(sector_mapping_cache_entry_ptr + 2);
359             *(sector_mapping_cache_entry_ptr + 2) =  *(sector_mapping_cache_entry_ptr + 1);
360             *(sector_mapping_cache_entry_ptr + 1) =  *(sector_mapping_cache_entry_ptr);
361 
362             /* Setup the new sector information in the cache.  */
363             sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_logical_sector =             (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID);
364             sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_map_entry =  new_mapping_address;
365             sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_address =    new_sector_address;
366         }
367 
368         /* Indicate the write was successful.  */
369         status =  LX_SUCCESS;
370     }
371     else
372     {
373 
374         /* Indicate the write was unsuccessful.  */
375         status =  LX_NO_SECTORS;
376     }
377 
378 #ifdef LX_THREAD_SAFE_ENABLE
379 
380     /* Release the thread safe mutex.  */
381     tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
382 #endif
383 
384     /* Return the completion status.  */
385     return(status);
386 }
387 
388 
389