lx_nor_flash_sector_write.c
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /*************************************************************************** | ||
| 2 | * Copyright (c) 2024 Microsoft Corporation | ||
| 3 | * Copyright (c) 2026-present Eclipse ThreadX contributors | ||
| 4 | * | ||
| 5 | * This program and the accompanying materials are made available under the | ||
| 6 | * terms of the MIT License which is available at | ||
| 7 | * https://opensource.org/licenses/MIT. | ||
| 8 | * | ||
| 9 | * SPDX-License-Identifier: MIT | ||
| 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 | /**************************************************************************/ | ||
| 82 | 27560 | UINT _lx_nor_flash_sector_write(LX_NOR_FLASH *nor_flash, ULONG logical_sector, VOID *buffer) | |
| 83 | { | ||
| 84 | |||
| 85 | ULONG *old_mapping_address; | ||
| 86 | ULONG *old_sector_address; | ||
| 87 | ULONG old_mapping_entry; | ||
| 88 | ULONG *new_mapping_address; | ||
| 89 | ULONG *new_sector_address; | ||
| 90 | ULONG new_mapping_entry; | ||
| 91 | ULONG i; | ||
| 92 | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY *sector_mapping_cache_entry_ptr; | ||
| 93 | UINT status; | ||
| 94 | #ifdef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE | ||
| 95 | ULONG block; | ||
| 96 | #endif | ||
| 97 | |||
| 98 | #ifdef LX_THREAD_SAFE_ENABLE | ||
| 99 | |||
| 100 | /* Obtain the thread safe mutex. */ | ||
| 101 | 2756 | tx_mutex_get(&nor_flash -> lx_nor_flash_mutex, TX_WAIT_FOREVER); | |
| 102 | #endif | ||
| 103 | |||
| 104 | /* Determine if there are less than two block's worth of free sectors. */ | ||
| 105 | 27560 | i = 0; | |
| 106 |
4/4✓ Branch 7 → 3 taken 10260 times.
✓ Branch 7 → 8 taken 24804 times.
✓ Branch 8 → 4 taken 1140 times.
✓ Branch 8 → 9 taken 2756 times.
|
38960 | while (nor_flash -> lx_nor_flash_free_physical_sectors <= nor_flash -> lx_nor_flash_physical_sectors_per_block) |
| 107 | { | ||
| 108 | |||
| 109 | /* Attempt to reclaim one physical block. */ | ||
| 110 | 11400 | _lx_nor_flash_block_reclaim(nor_flash); | |
| 111 | |||
| 112 | /* Increment the block count. */ | ||
| 113 | 11400 | i++; | |
| 114 | |||
| 115 | /* Have we exceeded the number of blocks in the system? */ | ||
| 116 |
2/4✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 10260 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 1140 times.
|
11400 | if (i >= nor_flash -> lx_nor_flash_total_blocks) |
| 117 | { | ||
| 118 | |||
| 119 | /* Yes, break out of the loop. */ | ||
| 120 | ✗ | break; | |
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | /* Increment the number of write requests. */ | ||
| 125 | 27560 | nor_flash -> lx_nor_flash_write_requests++; | |
| 126 | |||
| 127 | /* See if we can find the sector in the current mapping. */ | ||
| 128 | 27560 | _lx_nor_flash_logical_sector_find(nor_flash, logical_sector, LX_FALSE, &old_mapping_address, &old_sector_address); | |
| 129 | |||
| 130 | /* Allocate a new physical sector for this write. */ | ||
| 131 | 27560 | _lx_nor_flash_physical_sector_allocate(nor_flash, logical_sector, &new_mapping_address, &new_sector_address); | |
| 132 | |||
| 133 | /* Determine if the new sector allocation was successful. */ | ||
| 134 |
2/7✓ Branch 10 → 11 taken 24804 times.
✗ Branch 10 → 37 not taken.
✗ Branch 10 → 41 not taken.
✗ Branch 10 → 43 not taken.
✗ Branch 10 → 45 not taken.
✓ Branch 11 → 12 taken 2756 times.
✗ Branch 11 → 43 not taken.
|
27560 | if (new_mapping_address) |
| 135 | { | ||
| 136 | |||
| 137 | /* Yes, we were able to allocate a new physical sector. */ | ||
| 138 | |||
| 139 | /* Update the number of free physical sectors. */ | ||
| 140 | 27560 | nor_flash -> lx_nor_flash_free_physical_sectors--; | |
| 141 | |||
| 142 | /* Write the sector data to the new physical sector. */ | ||
| 143 | 27560 | status = _lx_nor_flash_driver_write(nor_flash, new_sector_address, buffer, LX_NOR_SECTOR_SIZE); | |
| 144 | |||
| 145 | /* Check for an error from flash driver. Drivers should never return an error.. */ | ||
| 146 |
2/4✗ Branch 12 → 13 not taken.
✓ Branch 12 → 15 taken 24804 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 17 taken 2756 times.
|
27560 | if (status) |
| 147 | { | ||
| 148 | |||
| 149 | /* Call system error handler. */ | ||
| 150 | ✗ | _lx_nor_flash_system_error(nor_flash, status); | |
| 151 | |||
| 152 | #ifdef LX_THREAD_SAFE_ENABLE | ||
| 153 | |||
| 154 | /* Release the thread safe mutex. */ | ||
| 155 | ✗ | tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); | |
| 156 | #endif | ||
| 157 | |||
| 158 | /* Return status. */ | ||
| 159 | ✗ | return(LX_ERROR); | |
| 160 | } | ||
| 161 | |||
| 162 | /* Was there a previously mapped sector? */ | ||
| 163 |
5/5✓ Branch 15 → 16 taken 20142 times.
✓ Branch 15 → 20 taken 518 times.
✓ Branch 15 → 24 taken 4144 times.
✓ Branch 17 → 18 taken 2238 times.
✓ Branch 17 → 23 taken 518 times.
|
27560 | if (old_mapping_address) |
| 164 | { | ||
| 165 | |||
| 166 | /* Now deprecate the old sector mapping. */ | ||
| 167 | |||
| 168 | /* Read in the old sector mapping. */ | ||
| 169 | #ifdef LX_DIRECT_READ | ||
| 170 | |||
| 171 | /* Read the word directly. */ | ||
| 172 | 4476 | old_mapping_entry = *(old_mapping_address); | |
| 173 | #else | ||
| 174 | 17904 | status = _lx_nor_flash_driver_read(nor_flash, old_mapping_address, &old_mapping_entry, 1); | |
| 175 | |||
| 176 | /* Check for an error from flash driver. Drivers should never return an error.. */ | ||
| 177 |
1/2✗ Branch 17 → 18 not taken.
✓ Branch 17 → 20 taken 17904 times.
|
17904 | if (status) |
| 178 | { | ||
| 179 | |||
| 180 | /* Call system error handler. */ | ||
| 181 | ✗ | _lx_nor_flash_system_error(nor_flash, status); | |
| 182 | |||
| 183 | #ifdef LX_THREAD_SAFE_ENABLE | ||
| 184 | |||
| 185 | /* Release the thread safe mutex. */ | ||
| 186 | tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); | ||
| 187 | #endif | ||
| 188 | |||
| 189 | /* Return status. */ | ||
| 190 | ✗ | return(LX_ERROR); | |
| 191 | } | ||
| 192 | #endif | ||
| 193 | |||
| 194 | /* Clear bit 30, which indicates this sector is superceded. */ | ||
| 195 | 22380 | old_mapping_entry = old_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED); | |
| 196 | |||
| 197 | /* Write the value back to the flash to clear bit 30. */ | ||
| 198 | 22380 | status = _lx_nor_flash_driver_write(nor_flash, old_mapping_address, &old_mapping_entry, 1); | |
| 199 | |||
| 200 | /* Check for an error from flash driver. Drivers should never return an error.. */ | ||
| 201 |
3/6✗ Branch 17 → 18 not taken.
✓ Branch 17 → 20 taken 2238 times.
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 23 taken 2238 times.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 24 taken 17904 times.
|
22380 | if (status) |
| 202 | { | ||
| 203 | |||
| 204 | /* Call system error handler. */ | ||
| 205 | ✗ | _lx_nor_flash_system_error(nor_flash, status); | |
| 206 | |||
| 207 | #ifdef LX_THREAD_SAFE_ENABLE | ||
| 208 | |||
| 209 | /* Release the thread safe mutex. */ | ||
| 210 | ✗ | tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); | |
| 211 | #endif | ||
| 212 | |||
| 213 | /* Return status. */ | ||
| 214 | ✗ | return(LX_ERROR); | |
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | /* Now build the new mapping entry - with the not valid bit set initially. */ | ||
| 219 | 27560 | 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; | |
| 220 | |||
| 221 | /* Write out the new mapping entry. */ | ||
| 222 | 27560 | status = _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 1); | |
| 223 | |||
| 224 | /* Check for an error from flash driver. Drivers should never return an error.. */ | ||
| 225 |
3/6✗ Branch 21 → 22 not taken.
✓ Branch 21 → 24 taken 2756 times.
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 28 taken 2756 times.
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 28 taken 22048 times.
|
27560 | if (status) |
| 226 | { | ||
| 227 | |||
| 228 | /* Call system error handler. */ | ||
| 229 | ✗ | _lx_nor_flash_system_error(nor_flash, status); | |
| 230 | |||
| 231 | #ifdef LX_THREAD_SAFE_ENABLE | ||
| 232 | |||
| 233 | /* Release the thread safe mutex. */ | ||
| 234 | ✗ | tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); | |
| 235 | #endif | ||
| 236 | |||
| 237 | /* Return status. */ | ||
| 238 | ✗ | return(LX_ERROR); | |
| 239 | } | ||
| 240 | |||
| 241 | /* Now clear the not valid bit to make this sector mapping valid. This is done because the writing of the extra bytes itself can | ||
| 242 | be interrupted and we need to make sure this can be detected when the flash is opened again. */ | ||
| 243 | 27560 | new_mapping_entry = new_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID); | |
| 244 | |||
| 245 | /* Clear the not valid bit. */ | ||
| 246 | 27560 | status = _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 1); | |
| 247 | |||
| 248 | /* Check for an error from flash driver. Drivers should never return an error.. */ | ||
| 249 |
3/5✗ Branch 25 → 26 not taken.
✓ Branch 25 → 28 taken 2756 times.
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 32 taken 22048 times.
✓ Branch 29 → 33 taken 2756 times.
|
27560 | if (status) |
| 250 | { | ||
| 251 | |||
| 252 | /* Call system error handler. */ | ||
| 253 | ✗ | _lx_nor_flash_system_error(nor_flash, status); | |
| 254 | |||
| 255 | #ifdef LX_THREAD_SAFE_ENABLE | ||
| 256 | |||
| 257 | /* Release the thread safe mutex. */ | ||
| 258 | ✗ | tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); | |
| 259 | #endif | ||
| 260 | |||
| 261 | /* Return status. */ | ||
| 262 | ✗ | return(LX_ERROR); | |
| 263 | } | ||
| 264 | #ifndef LX_NOR_DISABLE_EXTENDED_CACHE | ||
| 265 | #ifdef LX_NOR_ENABLE_MAPPING_BITMAP | ||
| 266 | |||
| 267 | /* Determine if the logical sector is within the mapping bitmap. */ | ||
| 268 |
2/2✓ Branch 32 → 33 taken 2856 times.
✓ Branch 32 → 34 taken 2656 times.
|
5512 | if (logical_sector < nor_flash -> lx_nor_flash_extended_cache_mapping_bitmap_max_logical_sector) |
| 269 | { | ||
| 270 | |||
| 271 | /* Set the bit in the mapping bitmap. */ | ||
| 272 | 2856 | nor_flash -> lx_nor_flash_extended_cache_mapping_bitmap[logical_sector >> 5] |= (ULONG)(1 << (logical_sector & 31)); | |
| 273 | } | ||
| 274 | #endif | ||
| 275 | #endif | ||
| 276 | |||
| 277 | /* Increment the number of mapped physical sectors. */ | ||
| 278 | 27560 | nor_flash -> lx_nor_flash_mapped_physical_sectors++; | |
| 279 | |||
| 280 | /* Was there a previously mapped sector? */ | ||
| 281 |
10/10✓ Branch 28 → 29 taken 2238 times.
✓ Branch 28 → 34 taken 518 times.
✓ Branch 32 → 33 taken 13428 times.
✓ Branch 32 → 38 taken 2590 times.
✓ Branch 32 → 40 taken 518 times.
✓ Branch 33 → 34 taken 2238 times.
✓ Branch 33 → 40 taken 518 times.
✓ Branch 34 → 35 taken 4476 times.
✓ Branch 34 → 40 taken 518 times.
✓ Branch 34 → 42 taken 518 times.
|
27560 | if (old_mapping_address) |
| 282 | { | ||
| 283 | |||
| 284 | /* Now clear bit 31, which indicates this sector is now obsoleted. */ | ||
| 285 | 22380 | old_mapping_entry = old_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID); | |
| 286 | |||
| 287 | /* Write the value back to the flash to clear bit 31. */ | ||
| 288 | 22380 | status = _lx_nor_flash_driver_write(nor_flash, old_mapping_address, &old_mapping_entry, 1); | |
| 289 | |||
| 290 | /* Check for an error from flash driver. Drivers should never return an error.. */ | ||
| 291 |
4/8✗ Branch 30 → 31 not taken.
✓ Branch 30 → 33 taken 2238 times.
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 37 taken 13428 times.
✗ Branch 35 → 36 not taken.
✓ Branch 35 → 39 taken 2238 times.
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 39 taken 4476 times.
|
22380 | if (status) |
| 292 | { | ||
| 293 | |||
| 294 | /* Call system error handler. */ | ||
| 295 | ✗ | _lx_nor_flash_system_error(nor_flash, status); | |
| 296 | |||
| 297 | #ifdef LX_THREAD_SAFE_ENABLE | ||
| 298 | |||
| 299 | /* Release the thread safe mutex. */ | ||
| 300 | ✗ | tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); | |
| 301 | #endif | ||
| 302 | |||
| 303 | /* Return status. */ | ||
| 304 | ✗ | return(LX_ERROR); | |
| 305 | } | ||
| 306 | |||
| 307 | /* Increment the number of obsolete physical sectors. */ | ||
| 308 | 22380 | nor_flash -> lx_nor_flash_obsolete_physical_sectors++; | |
| 309 | |||
| 310 | #ifdef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE | ||
| 311 | |||
| 312 | /* Get the block number from mapping address. */ | ||
| 313 | 4476 | block = (ULONG)(old_mapping_address - nor_flash -> lx_nor_flash_base_address) / nor_flash -> lx_nor_flash_words_per_block; | |
| 314 | |||
| 315 | /* Determine if this block is within the range of the obsolete count cache. */ | ||
| 316 |
4/4✓ Branch 37 → 38 taken 1119 times.
✓ Branch 37 → 39 taken 1119 times.
✓ Branch 39 → 40 taken 1119 times.
✓ Branch 39 → 41 taken 1119 times.
|
4476 | if (block < nor_flash -> lx_nor_flash_extended_cache_obsolete_count_max_block) |
| 317 | { | ||
| 318 | |||
| 319 | /* Increment the obsolete count for this block. */ | ||
| 320 | 2238 | nor_flash -> lx_nor_flash_extended_cache_obsolete_count[block] ++; | |
| 321 | } | ||
| 322 | #endif | ||
| 323 | |||
| 324 | /* Decrement the number of mapped physical sectors. */ | ||
| 325 | 22380 | nor_flash -> lx_nor_flash_mapped_physical_sectors--; | |
| 326 | |||
| 327 | /* Invalidate the old sector mapping cache entry. */ | ||
| 328 | 22380 | _lx_nor_flash_sector_mapping_cache_invalidate(nor_flash, logical_sector); | |
| 329 | } | ||
| 330 | |||
| 331 | /* Determine if the sector mapping cache is enabled. */ | ||
| 332 |
4/8✓ Branch 34 → 35 taken 2756 times.
✗ Branch 34 → 36 not taken.
✓ Branch 38 → 39 taken 13780 times.
✗ Branch 38 → 40 not taken.
✓ Branch 40 → 41 taken 8268 times.
✗ Branch 40 → 42 not taken.
✓ Branch 42 → 43 taken 2756 times.
✗ Branch 42 → 44 not taken.
|
27560 | if (nor_flash -> lx_nor_flash_sector_mapping_cache_enabled) |
| 333 | { | ||
| 334 | |||
| 335 | /* Yes, sector mapping cache is enabled, place this sector information in the cache. */ | ||
| 336 | |||
| 337 | /* Calculate the starting index of the sector mapping cache for this sector entry. */ | ||
| 338 | 27560 | i = (logical_sector & LX_NOR_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NOR_SECTOR_MAPPING_CACHE_DEPTH; | |
| 339 | |||
| 340 | /* Build a pointer to the cache entry. */ | ||
| 341 | 27560 | sector_mapping_cache_entry_ptr = &nor_flash -> lx_nor_flash_sector_mapping_cache[i]; | |
| 342 | |||
| 343 | /* Move all the cache entries down so the oldest is at the bottom. */ | ||
| 344 | 27560 | *(sector_mapping_cache_entry_ptr + 3) = *(sector_mapping_cache_entry_ptr + 2); | |
| 345 | 27560 | *(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1); | |
| 346 | 27560 | *(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr); | |
| 347 | |||
| 348 | /* Setup the new sector information in the cache. */ | ||
| 349 | 27560 | sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_logical_sector = (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID); | |
| 350 | 27560 | sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_map_entry = new_mapping_address; | |
| 351 | 27560 | sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_address = new_sector_address; | |
| 352 | } | ||
| 353 | |||
| 354 | /* Indicate the write was successful. */ | ||
| 355 | 27560 | status = LX_SUCCESS; | |
| 356 | } | ||
| 357 | else | ||
| 358 | { | ||
| 359 | |||
| 360 | /* Indicate the write was unsuccessful. */ | ||
| 361 | ✗ | status = LX_NO_SECTORS; | |
| 362 | } | ||
| 363 | |||
| 364 | #ifdef LX_THREAD_SAFE_ENABLE | ||
| 365 | |||
| 366 | /* Release the thread safe mutex. */ | ||
| 367 | 2756 | tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); | |
| 368 | #endif | ||
| 369 | |||
| 370 | /* Return the completion status. */ | ||
| 371 | 27560 | return(status); | |
| 372 | } | ||
| 373 | |||
| 374 | |||
| 375 |