lx_nand_flash_metadata_allocate.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 | /** NAND 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_nand_flash_metadata_allocate PORTABLE C */ | ||
| 43 | /* 6.2.1 */ | ||
| 44 | /* AUTHOR */ | ||
| 45 | /* */ | ||
| 46 | /* Xiuwen Cai, Microsoft Corporation */ | ||
| 47 | /* */ | ||
| 48 | /* DESCRIPTION */ | ||
| 49 | /* */ | ||
| 50 | /* This function allocates new blocks for metadata if current metadata */ | ||
| 51 | /* block is full. This function also frees metadata blocks if the chain*/ | ||
| 52 | /* is too long. */ | ||
| 53 | /* */ | ||
| 54 | /* INPUT */ | ||
| 55 | /* */ | ||
| 56 | /* nand_flash NAND flash instance */ | ||
| 57 | /* */ | ||
| 58 | /* OUTPUT */ | ||
| 59 | /* */ | ||
| 60 | /* return status */ | ||
| 61 | /* */ | ||
| 62 | /* CALLS */ | ||
| 63 | /* */ | ||
| 64 | /* _lx_nand_flash_metadata_build Build metadata */ | ||
| 65 | /* _lx_nand_flash_driver_block_erase Erase block */ | ||
| 66 | /* _lx_nand_flash_block_data_move Move block data */ | ||
| 67 | /* _lx_nand_flash_free_block_list_add Add free block list */ | ||
| 68 | /* _lx_nand_flash_block_allocate Allocate block */ | ||
| 69 | /* _lx_nand_flash_block_status_set Set block status */ | ||
| 70 | /* _lx_nand_flash_metadata_write Write metadata */ | ||
| 71 | /* _lx_nand_flash_system_error Internal system error handler */ | ||
| 72 | /* */ | ||
| 73 | /* CALLED BY */ | ||
| 74 | /* */ | ||
| 75 | /* Internal LevelX */ | ||
| 76 | /* */ | ||
| 77 | /**************************************************************************/ | ||
| 78 | 369460 | UINT _lx_nand_flash_metadata_allocate(LX_NAND_FLASH *nand_flash) | |
| 79 | { | ||
| 80 | |||
| 81 | ULONG block; | ||
| 82 | ULONG page; | ||
| 83 | UINT status; | ||
| 84 | UCHAR *page_buffer_ptr; | ||
| 85 | UINT j; | ||
| 86 | UCHAR min_erase_count; | ||
| 87 | |||
| 88 | |||
| 89 | /* Get current page for metadata block. */ | ||
| 90 | 369460 | page = nand_flash -> lx_nand_flash_metadata_block_current_page; | |
| 91 | |||
| 92 | /* Check if new metadata block is required. */ | ||
| 93 |
2/2✓ Branch 2 → 3 taken 368030 times.
✓ Branch 2 → 4 taken 1430 times.
|
369460 | if (page < nand_flash -> lx_nand_flash_pages_per_block) |
| 94 | { | ||
| 95 | |||
| 96 | /* No new block is required. Just return success. */ | ||
| 97 | 368030 | return(LX_SUCCESS); | |
| 98 | } | ||
| 99 | |||
| 100 | /* Advance to next metadata block. */ | ||
| 101 | 1430 | nand_flash -> lx_nand_flash_metadata_block_number_current = nand_flash -> lx_nand_flash_metadata_block_number_next; | |
| 102 | |||
| 103 | /* Reset current page number. */ | ||
| 104 | 1430 | nand_flash -> lx_nand_flash_metadata_block_current_page = 0; | |
| 105 | |||
| 106 | /* Advance to next backup metadata block. */ | ||
| 107 | 1430 | nand_flash -> lx_nand_flash_backup_metadata_block_number_current = nand_flash -> lx_nand_flash_backup_metadata_block_number_next; | |
| 108 | |||
| 109 | /* Reset current page number for backup metadata block. */ | ||
| 110 | 1430 | nand_flash -> lx_nand_flash_backup_metadata_block_current_page = 0; | |
| 111 | |||
| 112 | /* Check if number of allocated blocks reaches maximum. */ | ||
| 113 |
3/3✓ Branch 4 → 5 taken 460 times.
✓ Branch 4 → 42 taken 873 times.
✓ Branch 4 → 44 taken 97 times.
|
1430 | if (nand_flash -> lx_nand_flash_metadata_block_count == LX_NAND_FLASH_MAX_METADATA_BLOCKS) |
| 114 | { | ||
| 115 | |||
| 116 | /* Loop to mark metadata blocks as free. */ | ||
| 117 |
2/2✓ Branch 7 → 6 taken 1380 times.
✓ Branch 7 → 8 taken 460 times.
|
1840 | for (j = 0; j < LX_NAND_FLASH_MAX_METADATA_BLOCKS - 1; j++) |
| 118 | { | ||
| 119 | |||
| 120 | /* Free metadata block. */ | ||
| 121 | 1380 | nand_flash -> lx_nand_flash_block_status_table[nand_flash -> lx_nand_flash_metadata_block[j]] = LX_NAND_BLOCK_STATUS_FREE; | |
| 122 | |||
| 123 | /* Free backup metadata block. */ | ||
| 124 | 1380 | nand_flash -> lx_nand_flash_block_status_table[nand_flash -> lx_nand_flash_backup_metadata_block[j]] = LX_NAND_BLOCK_STATUS_FREE; | |
| 125 | |||
| 126 | /* Increase erase count. */ | ||
| 127 | 1380 | nand_flash -> lx_nand_flash_erase_count_table[nand_flash -> lx_nand_flash_metadata_block[j]]++; | |
| 128 | |||
| 129 | /* Increase erase count for backup metadata block. */ | ||
| 130 | 1380 | nand_flash -> lx_nand_flash_erase_count_table[nand_flash -> lx_nand_flash_backup_metadata_block[j]]++; | |
| 131 | } | ||
| 132 | |||
| 133 | /* Set new metadata block head. */ | ||
| 134 | 460 | nand_flash -> lx_nand_flash_metadata_block_number = nand_flash -> lx_nand_flash_metadata_block_number_current; | |
| 135 | |||
| 136 | /* Set new backup metadata block head. */ | ||
| 137 | 460 | nand_flash -> lx_nand_flash_backup_metadata_block_number = nand_flash -> lx_nand_flash_backup_metadata_block_number_current; | |
| 138 | |||
| 139 | /* Set erase count to maximum. */ | ||
| 140 | 460 | min_erase_count = 255; | |
| 141 | |||
| 142 | /* Loop to find the minimum erase count. */ | ||
| 143 |
2/2✓ Branch 13 → 9 taken 471040 times.
✓ Branch 13 → 14 taken 460 times.
|
471500 | for (j = 0; j < nand_flash -> lx_nand_flash_total_blocks; j++) |
| 144 | { | ||
| 145 | |||
| 146 | /* Exclude erase count of bad block. */ | ||
| 147 |
1/2✓ Branch 9 → 10 taken 471040 times.
✗ Branch 9 → 12 not taken.
|
471040 | if(nand_flash -> lx_nand_flash_block_status_table[j] != LX_NAND_BLOCK_STATUS_BAD) |
| 148 | { | ||
| 149 | |||
| 150 | /* Check if it has less erase count. */ | ||
| 151 |
2/2✓ Branch 10 → 11 taken 920 times.
✓ Branch 10 → 12 taken 470120 times.
|
471040 | if (nand_flash -> lx_nand_flash_erase_count_table[j] < min_erase_count) |
| 152 | { | ||
| 153 | |||
| 154 | /* Update the minimum erase count. */ | ||
| 155 | 920 | min_erase_count = nand_flash -> lx_nand_flash_erase_count_table[j]; | |
| 156 | } | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | /* Check if the minimum erase count is larger than zero. */ | ||
| 161 |
1/2✗ Branch 14 → 15 not taken.
✓ Branch 14 → 21 taken 460 times.
|
460 | if (min_erase_count > 0) |
| 162 | { | ||
| 163 | |||
| 164 | /* Loop to update erase count. */ | ||
| 165 | ✗ | for (j = 0; j < nand_flash -> lx_nand_flash_total_blocks; j++) | |
| 166 | { | ||
| 167 | |||
| 168 | /* Skip bad blocks. */ | ||
| 169 | ✗ | if (nand_flash -> lx_nand_flash_block_status_table[j] != LX_NAND_BLOCK_STATUS_BAD) | |
| 170 | { | ||
| 171 | |||
| 172 | /* Update erase count. */ | ||
| 173 | ✗ | nand_flash -> lx_nand_flash_erase_count_table[j] = (UCHAR)(nand_flash -> lx_nand_flash_erase_count_table[j] - min_erase_count); | |
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | /* Update base erase count. */ | ||
| 178 | ✗ | nand_flash -> lx_nand_flash_base_erase_count += (ULONG)min_erase_count; | |
| 179 | } | ||
| 180 | |||
| 181 | /* Rebuild metadata pages. */ | ||
| 182 | 460 | status = _lx_nand_flash_metadata_build(nand_flash); | |
| 183 | |||
| 184 | /* Check return status. */ | ||
| 185 |
1/2✗ Branch 22 → 23 not taken.
✓ Branch 22 → 24 taken 460 times.
|
460 | if (status != LX_SUCCESS) |
| 186 | { | ||
| 187 | |||
| 188 | /* Return error status. */ | ||
| 189 | ✗ | return(status); | |
| 190 | } | ||
| 191 | |||
| 192 | /* Loop to erase freed blocks. */ | ||
| 193 |
4/4✓ Branch 40 → 25 taken 1242 times.
✓ Branch 40 → 41 taken 414 times.
✓ Branch 42 → 25 taken 138 times.
✓ Branch 42 → 43 taken 46 times.
|
1840 | for (j = 0; j < LX_NAND_FLASH_MAX_METADATA_BLOCKS - 1; j++) |
| 194 | { | ||
| 195 | |||
| 196 | /* Get the block number to erase. */ | ||
| 197 | 1380 | block = nand_flash -> lx_nand_flash_metadata_block[j]; | |
| 198 | |||
| 199 | /* Erase the block. */ | ||
| 200 | 1380 | status = _lx_nand_flash_driver_block_erase(nand_flash, block, nand_flash -> lx_nand_flash_base_erase_count + nand_flash -> lx_nand_flash_erase_count_table[block]); | |
| 201 | |||
| 202 | /* Check for an error from flash driver. */ | ||
| 203 |
2/3✗ Branch 26 → 27 not taken.
✓ Branch 26 → 29 taken 1242 times.
✓ Branch 26 → 30 taken 138 times.
|
1380 | if (status) |
| 204 | { | ||
| 205 | |||
| 206 | /* Call system error handler. */ | ||
| 207 | ✗ | _lx_nand_flash_system_error(nand_flash, status, block, 0); | |
| 208 | #ifdef LX_THREAD_SAFE_ENABLE | ||
| 209 | |||
| 210 | /* Release the thread safe mutex. */ | ||
| 211 | ✗ | tx_mutex_put(&nand_flash -> lx_nand_flash_mutex); | |
| 212 | #endif | ||
| 213 | |||
| 214 | /* Return an error. */ | ||
| 215 | ✗ | return(LX_ERROR); | |
| 216 | } | ||
| 217 | |||
| 218 | /* Check if the block has too many erases. */ | ||
| 219 |
2/4✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 1242 times.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 138 times.
|
1380 | if (nand_flash -> lx_nand_flash_erase_count_table[block] > LX_NAND_FLASH_MAX_ERASE_COUNT_DELTA) |
| 220 | { | ||
| 221 | |||
| 222 | /* Move data from less worn block. */ | ||
| 223 | ✗ | _lx_nand_flash_block_data_move(nand_flash, block); | |
| 224 | } | ||
| 225 | else | ||
| 226 | { | ||
| 227 | |||
| 228 | /* Add the block to free block list. */ | ||
| 229 | 1380 | _lx_nand_flash_free_block_list_add(nand_flash, block); | |
| 230 | } | ||
| 231 | |||
| 232 | /* Get the block number from backup metadata blocks. */ | ||
| 233 | 1380 | block = nand_flash -> lx_nand_flash_backup_metadata_block[j]; | |
| 234 | |||
| 235 | /* Erase the block. */ | ||
| 236 | 1380 | status = _lx_nand_flash_driver_block_erase(nand_flash, block, nand_flash -> lx_nand_flash_base_erase_count + nand_flash -> lx_nand_flash_erase_count_table[block]); | |
| 237 | |||
| 238 | /* Check for an error from flash driver. */ | ||
| 239 |
2/4✗ Branch 33 → 34 not taken.
✓ Branch 33 → 36 taken 1242 times.
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 38 taken 138 times.
|
1380 | if (status) |
| 240 | { | ||
| 241 | |||
| 242 | /* Call system error handler. */ | ||
| 243 | ✗ | _lx_nand_flash_system_error(nand_flash, status, block, 0); | |
| 244 | #ifdef LX_THREAD_SAFE_ENABLE | ||
| 245 | |||
| 246 | /* Release the thread safe mutex. */ | ||
| 247 | ✗ | tx_mutex_put(&nand_flash -> lx_nand_flash_mutex); | |
| 248 | #endif | ||
| 249 | |||
| 250 | /* Return an error. */ | ||
| 251 | ✗ | return(LX_ERROR); | |
| 252 | } | ||
| 253 | |||
| 254 | /* Check if the block has too many erases. */ | ||
| 255 |
2/4✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 1242 times.
✗ Branch 38 → 39 not taken.
✓ Branch 38 → 40 taken 138 times.
|
1380 | if (nand_flash -> lx_nand_flash_erase_count_table[block] > LX_NAND_FLASH_MAX_ERASE_COUNT_DELTA) |
| 256 | { | ||
| 257 | |||
| 258 | /* Move data from less worn block. */ | ||
| 259 | ✗ | _lx_nand_flash_block_data_move(nand_flash, block); | |
| 260 | } | ||
| 261 | else | ||
| 262 | { | ||
| 263 | |||
| 264 | /* Add the block to free block list. */ | ||
| 265 | 1380 | _lx_nand_flash_free_block_list_add(nand_flash, block); | |
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | /* Update metadata block list. */ | ||
| 270 | 460 | nand_flash -> lx_nand_flash_metadata_block[0] = (USHORT)nand_flash -> lx_nand_flash_metadata_block_number_current; | |
| 271 | |||
| 272 | /* Update metadata block list. */ | ||
| 273 | 460 | nand_flash -> lx_nand_flash_backup_metadata_block[0] = (USHORT)nand_flash -> lx_nand_flash_backup_metadata_block_number_current; | |
| 274 | |||
| 275 | /* Set metadata block count to one. */ | ||
| 276 | 460 | nand_flash -> lx_nand_flash_metadata_block_count = 1; | |
| 277 | } | ||
| 278 | |||
| 279 | /* Allocate new block for metadata. */ | ||
| 280 | 1430 | status = _lx_nand_flash_block_allocate(nand_flash, &block); | |
| 281 | |||
| 282 | /* Check return status. */ | ||
| 283 |
2/4✗ Branch 43 → 44 not taken.
✓ Branch 43 → 47 taken 1287 times.
✗ Branch 45 → 46 not taken.
✓ Branch 45 → 49 taken 143 times.
|
1430 | if (status != LX_SUCCESS) |
| 284 | { | ||
| 285 | |||
| 286 | /* Call system error handler. */ | ||
| 287 | ✗ | _lx_nand_flash_system_error(nand_flash, status, block, 0); | |
| 288 | |||
| 289 | /* Determine if the error is fatal. */ | ||
| 290 | ✗ | if (status != LX_NAND_ERROR_CORRECTED) | |
| 291 | { | ||
| 292 | |||
| 293 | /* Return an error. */ | ||
| 294 | ✗ | return(LX_ERROR); | |
| 295 | } | ||
| 296 | } | ||
| 297 | |||
| 298 | /* Update next metadata block number. */ | ||
| 299 | 1430 | nand_flash -> lx_nand_flash_metadata_block_number_next = block; | |
| 300 | |||
| 301 | /* Save the new block in metadata block list. */ | ||
| 302 | 1430 | nand_flash -> lx_nand_flash_metadata_block[nand_flash -> lx_nand_flash_metadata_block_count] = (USHORT)block; | |
| 303 | |||
| 304 | /* Set the block status to allocated. */ | ||
| 305 | 1430 | status = _lx_nand_flash_block_status_set(nand_flash, block, LX_NAND_BLOCK_STATUS_ALLOCATED); | |
| 306 | |||
| 307 | /* Check return status. */ | ||
| 308 |
2/4✗ Branch 48 → 49 not taken.
✓ Branch 48 → 50 taken 1287 times.
✗ Branch 50 → 51 not taken.
✓ Branch 50 → 52 taken 143 times.
|
1430 | if (status != LX_SUCCESS) |
| 309 | { | ||
| 310 | |||
| 311 | /* Return status. */ | ||
| 312 | ✗ | return (status); | |
| 313 | } | ||
| 314 | |||
| 315 | /* Allocate new block for backup metadata. */ | ||
| 316 | 1430 | status = _lx_nand_flash_block_allocate(nand_flash, &block); | |
| 317 | |||
| 318 | /* Check return status. */ | ||
| 319 |
2/4✗ Branch 51 → 52 not taken.
✓ Branch 51 → 55 taken 1287 times.
✗ Branch 53 → 54 not taken.
✓ Branch 53 → 57 taken 143 times.
|
1430 | if (status != LX_SUCCESS) |
| 320 | { | ||
| 321 | |||
| 322 | /* Call system error handler. */ | ||
| 323 | ✗ | _lx_nand_flash_system_error(nand_flash, status, block, 0); | |
| 324 | |||
| 325 | /* Determine if the error is fatal. */ | ||
| 326 | ✗ | if (status != LX_NAND_ERROR_CORRECTED) | |
| 327 | { | ||
| 328 | |||
| 329 | /* Return an error. */ | ||
| 330 | ✗ | return(LX_ERROR); | |
| 331 | } | ||
| 332 | } | ||
| 333 | |||
| 334 | /* Update next backup metadata block number. */ | ||
| 335 | 1430 | nand_flash -> lx_nand_flash_backup_metadata_block_number_next = block; | |
| 336 | |||
| 337 | /* Save the new block in metadata block list. */ | ||
| 338 | 1430 | nand_flash -> lx_nand_flash_backup_metadata_block[nand_flash -> lx_nand_flash_metadata_block_count] = (USHORT)block; | |
| 339 | |||
| 340 | /* Set the block status to allocated. */ | ||
| 341 | 1430 | status = _lx_nand_flash_block_status_set(nand_flash, block, LX_NAND_BLOCK_STATUS_ALLOCATED); | |
| 342 |
2/4✗ Branch 56 → 57 not taken.
✓ Branch 56 → 58 taken 1287 times.
✗ Branch 58 → 59 not taken.
✓ Branch 58 → 60 taken 143 times.
|
1430 | if (status != LX_SUCCESS) |
| 343 | { | ||
| 344 | ✗ | return (status); | |
| 345 | } | ||
| 346 | |||
| 347 | /* Setup page buffer. */ | ||
| 348 | 1430 | page_buffer_ptr = nand_flash -> lx_nand_flash_page_buffer; | |
| 349 | |||
| 350 | /* Initialize page data. */ | ||
| 351 |
4/4✓ Branch 60 → 59 taken 439296 times.
✓ Branch 60 → 61 taken 858 times.
✓ Branch 62 → 61 taken 73216 times.
✓ Branch 62 → 63 taken 143 times.
|
513942 | LX_MEMSET(page_buffer_ptr, 0xFF, nand_flash -> lx_nand_flash_bytes_per_page); |
| 352 | |||
| 353 | /* Save next block number. */ | ||
| 354 | 1430 | LX_UTILITY_LONG_SET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_MAIN_METADATA_OFFSET], nand_flash -> lx_nand_flash_metadata_block_number_next); | |
| 355 | |||
| 356 | /* Save next block number for backup metadata. */ | ||
| 357 | 1430 | LX_UTILITY_LONG_SET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_BACKUP_METADATA_OFFSET], nand_flash -> lx_nand_flash_backup_metadata_block_number_next); | |
| 358 | |||
| 359 | /* Write metadata. */ | ||
| 360 | 1430 | status = _lx_nand_flash_metadata_write(nand_flash, page_buffer_ptr, LX_NAND_PAGE_TYPE_BLOCK_LINK); | |
| 361 | |||
| 362 | /* Increase metadata block count. */ | ||
| 363 | 1430 | nand_flash -> lx_nand_flash_metadata_block_count++; | |
| 364 | |||
| 365 | /* Return sector not found status. */ | ||
| 366 | 1430 | return(status); | |
| 367 | } | ||
| 368 | |||
| 369 |