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