Files
levelx/common/src/lx_nand_flash_metadata_allocate.c
T
Xiuwen Cai a8bc412907 Release 6.2.1 on 08 Mar 2023. Expand to see details.
075bb4c Update patch version.
a30e30b Update version and date for release.
a6b42b2 Update owners
6a3e60b Add user extension in flash control block
2023-03-08 08:32:05 +00:00

375 lines
15 KiB
C

/**************************************************************************/
/* */
/* Copyright (c) Microsoft Corporation. All rights reserved. */
/* */
/* This software is licensed under the Microsoft Software License */
/* Terms for Microsoft Azure RTOS. Full text of the license can be */
/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
/* and in the root directory of this software. */
/* */
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/** */
/** LevelX Component */
/** */
/** NAND Flash */
/** */
/**************************************************************************/
/**************************************************************************/
#define LX_SOURCE_CODE
/* Disable ThreadX error checking. */
#ifndef LX_DISABLE_ERROR_CHECKING
#define LX_DISABLE_ERROR_CHECKING
#endif
/* Include necessary system files. */
#include "lx_api.h"
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _lx_nand_flash_metadata_allocate PORTABLE C */
/* 6.2.1 */
/* AUTHOR */
/* */
/* Xiuwen Cai, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function allocates new blocks for metadata if current metadata */
/* block is full. This function also frees metadata blocks if the chain*/
/* is too long. */
/* */
/* INPUT */
/* */
/* nand_flash NAND flash instance */
/* */
/* OUTPUT */
/* */
/* return status */
/* */
/* CALLS */
/* */
/* _lx_nand_flash_metadata_build Build metadata */
/* _lx_nand_flash_driver_block_erase Erase block */
/* _lx_nand_flash_block_data_move Move block data */
/* _lx_nand_flash_free_block_list_add Add free block list */
/* _lx_nand_flash_block_allocate Allocate block */
/* _lx_nand_flash_block_status_set Set block status */
/* _lx_nand_flash_metadata_write Write metadata */
/* _lx_nand_flash_system_error Internal system error handler */
/* */
/* CALLED BY */
/* */
/* Internal LevelX */
/* */
/* RELEASE HISTORY */
/* */
/* DATE NAME DESCRIPTION */
/* */
/* 03-08-2023 Xiuwen Cai Initial Version 6.2.1 */
/* */
/**************************************************************************/
UINT _lx_nand_flash_metadata_allocate(LX_NAND_FLASH *nand_flash)
{
ULONG block;
ULONG page;
UINT status;
UCHAR *page_buffer_ptr;
UINT j;
UCHAR min_erase_count;
/* Get current page for metadata block. */
page = nand_flash -> lx_nand_flash_metadata_block_current_page;
/* Check if new metadata block is required. */
if (page < nand_flash -> lx_nand_flash_pages_per_block)
{
/* No new block is required. Just return success. */
return(LX_SUCCESS);
}
/* Advance to next metadata block. */
nand_flash -> lx_nand_flash_metadata_block_number_current = nand_flash -> lx_nand_flash_metadata_block_number_next;
/* Reset current page number. */
nand_flash -> lx_nand_flash_metadata_block_current_page = 0;
/* Advance to next backup metadata block. */
nand_flash -> lx_nand_flash_backup_metadata_block_number_current = nand_flash -> lx_nand_flash_backup_metadata_block_number_next;
/* Reset current page number for backup metadata block. */
nand_flash -> lx_nand_flash_backup_metadata_block_current_page = 0;
/* Check if number of allocated blocks reaches maximum. */
if (nand_flash -> lx_nand_flash_metadata_block_count == LX_NAND_FLASH_MAX_METADATA_BLOCKS)
{
/* Loop to mark metadata blocks as free. */
for (j = 0; j < LX_NAND_FLASH_MAX_METADATA_BLOCKS - 1; j++)
{
/* Free metadata block. */
nand_flash -> lx_nand_flash_block_status_table[nand_flash -> lx_nand_flash_metadata_block[j]] = LX_NAND_BLOCK_STATUS_FREE;
/* Free backup metadata block. */
nand_flash -> lx_nand_flash_block_status_table[nand_flash -> lx_nand_flash_backup_metadata_block[j]] = LX_NAND_BLOCK_STATUS_FREE;
/* Increase erase count. */
nand_flash -> lx_nand_flash_erase_count_table[nand_flash -> lx_nand_flash_metadata_block[j]]++;
/* Increase erase count for backup metadata block. */
nand_flash -> lx_nand_flash_erase_count_table[nand_flash -> lx_nand_flash_backup_metadata_block[j]]++;
}
/* Set new metadata block head. */
nand_flash -> lx_nand_flash_metadata_block_number = nand_flash -> lx_nand_flash_metadata_block_number_current;
/* Set new backup metadata block head. */
nand_flash -> lx_nand_flash_backup_metadata_block_number = nand_flash -> lx_nand_flash_backup_metadata_block_number_current;
/* Set erase count to maximum. */
min_erase_count = 255;
/* Loop to find the minimum erase count. */
for (j = 0; j < nand_flash -> lx_nand_flash_total_blocks; j++)
{
/* Exclude erase count of bad block. */
if(nand_flash -> lx_nand_flash_block_status_table[j] != LX_NAND_BLOCK_STATUS_BAD)
{
/* Check if it has less erase count. */
if (nand_flash -> lx_nand_flash_erase_count_table[j] < min_erase_count)
{
/* Update the minimum erase count. */
min_erase_count = nand_flash -> lx_nand_flash_erase_count_table[j];
}
}
}
/* Check if the minimum erase count is larger than zero. */
if (min_erase_count > 0)
{
/* Loop to update erase count. */
for (j = 0; j < nand_flash -> lx_nand_flash_total_blocks; j++)
{
/* Skip bad blocks. */
if (nand_flash -> lx_nand_flash_block_status_table[j] != LX_NAND_BLOCK_STATUS_BAD)
{
/* Update erase count. */
nand_flash -> lx_nand_flash_erase_count_table[j] = (UCHAR)(nand_flash -> lx_nand_flash_erase_count_table[j] - min_erase_count);
}
}
/* Update base erase count. */
nand_flash -> lx_nand_flash_base_erase_count += (ULONG)min_erase_count;
}
/* Rebuild metadata pages. */
status = _lx_nand_flash_metadata_build(nand_flash);
/* Check return status. */
if (status != LX_SUCCESS)
{
/* Return error status. */
return(status);
}
/* Loop to erase freed blocks. */
for (j = 0; j < LX_NAND_FLASH_MAX_METADATA_BLOCKS - 1; j++)
{
/* Get the block number to erase. */
block = nand_flash -> lx_nand_flash_metadata_block[j];
/* Erase the block. */
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]);
/* Check for an error from flash driver. */
if (status)
{
/* Call system error handler. */
_lx_nand_flash_system_error(nand_flash, status, block, 0);
#ifdef LX_THREAD_SAFE_ENABLE
/* Release the thread safe mutex. */
tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
#endif
/* Return an error. */
return(LX_ERROR);
}
/* Check if the block has too many erases. */
if (nand_flash -> lx_nand_flash_erase_count_table[block] > LX_NAND_FLASH_MAX_ERASE_COUNT_DELTA)
{
/* Move data from less worn block. */
_lx_nand_flash_block_data_move(nand_flash, block);
}
else
{
/* Add the block to free block list. */
_lx_nand_flash_free_block_list_add(nand_flash, block);
}
/* Get the block number from backup metadata blocks. */
block = nand_flash -> lx_nand_flash_backup_metadata_block[j];
/* Erase the block. */
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]);
/* Check for an error from flash driver. */
if (status)
{
/* Call system error handler. */
_lx_nand_flash_system_error(nand_flash, status, block, 0);
#ifdef LX_THREAD_SAFE_ENABLE
/* Release the thread safe mutex. */
tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
#endif
/* Return an error. */
return(LX_ERROR);
}
/* Check if the block has too many erases. */
if (nand_flash -> lx_nand_flash_erase_count_table[block] > LX_NAND_FLASH_MAX_ERASE_COUNT_DELTA)
{
/* Move data from less worn block. */
_lx_nand_flash_block_data_move(nand_flash, block);
}
else
{
/* Add the block to free block list. */
_lx_nand_flash_free_block_list_add(nand_flash, block);
}
}
/* Update metadata block list. */
nand_flash -> lx_nand_flash_metadata_block[0] = (USHORT)nand_flash -> lx_nand_flash_metadata_block_number_current;
/* Update metadata block list. */
nand_flash -> lx_nand_flash_backup_metadata_block[0] = (USHORT)nand_flash -> lx_nand_flash_backup_metadata_block_number_current;
/* Set metadata block count to one. */
nand_flash -> lx_nand_flash_metadata_block_count = 1;
}
/* Allocate new block for metadata. */
status = _lx_nand_flash_block_allocate(nand_flash, &block);
/* Check return status. */
if (status != LX_SUCCESS)
{
/* Call system error handler. */
_lx_nand_flash_system_error(nand_flash, status, block, 0);
/* Determine if the error is fatal. */
if (status != LX_NAND_ERROR_CORRECTED)
{
/* Return an error. */
return(LX_ERROR);
}
}
/* Update next metadata block number. */
nand_flash -> lx_nand_flash_metadata_block_number_next = block;
/* Save the new block in metadata block list. */
nand_flash -> lx_nand_flash_metadata_block[nand_flash -> lx_nand_flash_metadata_block_count] = (USHORT)block;
/* Set the block status to allocated. */
status = _lx_nand_flash_block_status_set(nand_flash, block, LX_NAND_BLOCK_STATUS_ALLOCATED);
/* Check return status. */
if (status != LX_SUCCESS)
{
/* Return status. */
return (status);
}
/* Allocate new block for backup metadata. */
status = _lx_nand_flash_block_allocate(nand_flash, &block);
/* Check return status. */
if (status != LX_SUCCESS)
{
/* Call system error handler. */
_lx_nand_flash_system_error(nand_flash, status, block, 0);
/* Determine if the error is fatal. */
if (status != LX_NAND_ERROR_CORRECTED)
{
/* Return an error. */
return(LX_ERROR);
}
}
/* Update next backup metadata block number. */
nand_flash -> lx_nand_flash_backup_metadata_block_number_next = block;
/* Save the new block in metadata block list. */
nand_flash -> lx_nand_flash_backup_metadata_block[nand_flash -> lx_nand_flash_metadata_block_count] = (USHORT)block;
/* Set the block status to allocated. */
status = _lx_nand_flash_block_status_set(nand_flash, block, LX_NAND_BLOCK_STATUS_ALLOCATED);
if (status != LX_SUCCESS)
{
return (status);
}
/* Setup page buffer. */
page_buffer_ptr = nand_flash -> lx_nand_flash_page_buffer;
/* Initialize page data. */
LX_MEMSET(page_buffer_ptr, 0xFF, nand_flash -> lx_nand_flash_bytes_per_page);
/* Save next block number. */
LX_UTILITY_LONG_SET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_MAIN_METADATA_OFFSET], nand_flash -> lx_nand_flash_metadata_block_number_next);
/* Save next block number for backup metadata. */
LX_UTILITY_LONG_SET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_BACKUP_METADATA_OFFSET], nand_flash -> lx_nand_flash_backup_metadata_block_number_next);
/* Write metadata. */
status = _lx_nand_flash_metadata_write(nand_flash, page_buffer_ptr, LX_NAND_PAGE_TYPE_BLOCK_LINK);
/* Increase metadata block count. */
nand_flash -> lx_nand_flash_metadata_block_count++;
/* Return sector not found status. */
return(status);
}