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