/**************************************************************************/ /* */ /* 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_format PORTABLE C */ /* 6.3.0 */ /* AUTHOR */ /* */ /* Xiuwen Cai, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ /* This function erases and formats a NAND flash. */ /* */ /* INPUT */ /* */ /* nand_flash NAND flash instance */ /* name Name of NAND flash instance */ /* nand_driver_initialize Driver initialize */ /* memory_ptr Pointer to memory used by the */ /* LevelX for this NAND. */ /* memory_size Size of memory - must at least*/ /* 7 * total block count + */ /* 2 * page size */ /* */ /* OUTPUT */ /* */ /* return status */ /* */ /* CALLS */ /* */ /* (nand_driver_initialize) Driver initialize */ /* LX_MEMSET Initialize memory */ /* _lx_nand_flash_memory_initialize Initialize buffer */ /* _lx_nand_flash_driver_block_status_get */ /* Driver block status get */ /* _lx_nand_flash_driver_block_status_set */ /* Driver block status set */ /* _lx_nand_flash_metadata_build Build metadata */ /* _lx_nand_flash_metadata_write Write metadata */ /* _lx_nand_flash_driver_block_erase Driver block erase */ /* _lx_nand_flash_system_error System error handler */ /* tx_mutex_create Create thread-safe mutex */ /* */ /* CALLED BY */ /* */ /* Application Code */ /* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ /* */ /* 03-08-2023 Xiuwen Cai Initial Version 6.2.1 */ /* 10-31-2023 Xiuwen Cai Modified comment(s), */ /* avoided clearing user */ /* extension in flash control */ /* block, */ /* resulting in version 6.3.0 */ /* */ /**************************************************************************/ UINT _lx_nand_flash_format(LX_NAND_FLASH* nand_flash, CHAR* name, UINT(*nand_driver_initialize)(LX_NAND_FLASH*), ULONG* memory_ptr, UINT memory_size) { ULONG block; UCHAR block_status; UINT status; UCHAR *page_buffer_ptr; LX_PARAMETER_NOT_USED(name); /* Clear the NAND flash control block. User extension is not cleared. */ LX_MEMSET(nand_flash, 0, (ULONG)((UCHAR*)&(nand_flash -> lx_nand_flash_open_previous) - (UCHAR*)nand_flash) + sizeof(nand_flash -> lx_nand_flash_open_previous)); /* Call the flash driver's initialization function. */ (nand_driver_initialize)(nand_flash); /* Determine if we can support this NAND flash size. */ if (nand_flash -> lx_nand_flash_pages_per_block > LX_NAND_MAX_PAGE_PER_BLOCK || nand_flash -> lx_nand_flash_total_blocks > LX_NAND_MAX_BLOCK_COUNT) { return(LX_ERROR); } /* Check if it is new LevelX NAND driver. */ if (nand_flash -> lx_nand_flash_driver_pages_read == LX_NULL || nand_flash -> lx_nand_flash_driver_pages_write == LX_NULL || nand_flash -> lx_nand_flash_driver_pages_copy == LX_NULL) { return(LX_ERROR); } /* Check the spare data length. */ if (nand_flash -> lx_nand_flash_spare_data1_length < sizeof(ULONG)) { return(LX_ERROR); } /* Calculate the number of words per block and per page. */ nand_flash -> lx_nand_flash_words_per_page = (nand_flash -> lx_nand_flash_bytes_per_page / sizeof(ULONG)); nand_flash -> lx_nand_flash_words_per_block = (nand_flash -> lx_nand_flash_words_per_page * nand_flash -> lx_nand_flash_pages_per_block); /* Calculate the total pages. */ nand_flash -> lx_nand_flash_total_pages = nand_flash -> lx_nand_flash_total_blocks * nand_flash -> lx_nand_flash_pages_per_block; /* Initialize memory buffer. */ status = _lx_nand_flash_memory_initialize(nand_flash, memory_ptr, memory_size); if (status != LX_SUCCESS) { return(status); } /* Initialize block numbers. */ nand_flash -> lx_nand_flash_metadata_block_number = LX_NAND_BLOCK_UNMAPPED; nand_flash -> lx_nand_flash_metadata_block_number_next = LX_NAND_BLOCK_UNMAPPED; nand_flash -> lx_nand_flash_backup_metadata_block_number = LX_NAND_BLOCK_UNMAPPED; nand_flash -> lx_nand_flash_backup_metadata_block_number_next = LX_NAND_BLOCK_UNMAPPED; /* Initialize the block status buffer. */ LX_MEMSET(nand_flash -> lx_nand_flash_block_status_table, 0xFF, nand_flash -> lx_nand_flash_block_status_table_size); /* Loop through the blocks to check for bad blocks and determine the minimum and maximum erase count for each good block. */ for (block = 0; block < nand_flash -> lx_nand_flash_total_blocks; block++) { /* First, check to make sure this block is good. */ status = _lx_nand_flash_driver_block_status_get(nand_flash, block, &block_status); /* Check for an error from flash driver. */ if (status) { /* Call system error handler. */ _lx_nand_flash_system_error(nand_flash, status, block, 0); /* Return an error. */ return(LX_ERROR); } /* Is this block bad? */ if (block_status != LX_NAND_GOOD_BLOCK) { /* Yes, this block is bad. */ /* Increment the number of bad blocks. */ nand_flash -> lx_nand_flash_bad_blocks++; /* Save the block status. */ nand_flash -> lx_nand_flash_block_status_table[block] = LX_NAND_BLOCK_STATUS_BAD; /* Continue to the next block. */ continue; } /* Erase the block. */ status = _lx_nand_flash_driver_block_erase(nand_flash, block, 0); /* Check for an error from flash driver. */ if (status) { /* Call system error handler. */ _lx_nand_flash_system_error(nand_flash, status, block, 0); /* Attempt to mark this block as bad. */ status = _lx_nand_flash_driver_block_status_set(nand_flash, block, LX_NAND_BAD_BLOCK); /* Check for error in setting the block status. */ if (status) { /* Call system error handler. */ _lx_nand_flash_system_error(nand_flash, status, block, 0); } /* Increment the bad block count. */ nand_flash -> lx_nand_flash_bad_blocks++; /* Save the block status. */ nand_flash -> lx_nand_flash_block_status_table[block] = LX_NAND_BLOCK_STATUS_BAD; } else { /* Allocate blocks for metadata. */ if (nand_flash -> lx_nand_flash_metadata_block_number == LX_NAND_BLOCK_UNMAPPED) { nand_flash -> lx_nand_flash_metadata_block_number = block; nand_flash -> lx_nand_flash_metadata_block_number_current = block; } else if (nand_flash -> lx_nand_flash_backup_metadata_block_number == LX_NAND_BLOCK_UNMAPPED) { nand_flash -> lx_nand_flash_backup_metadata_block_number = block; nand_flash -> lx_nand_flash_backup_metadata_block_number_current = block; } else if (nand_flash -> lx_nand_flash_metadata_block_number_next == LX_NAND_BLOCK_UNMAPPED) { nand_flash -> lx_nand_flash_metadata_block_number_next = block; } else if (nand_flash -> lx_nand_flash_backup_metadata_block_number_next == LX_NAND_BLOCK_UNMAPPED) { nand_flash -> lx_nand_flash_backup_metadata_block_number_next = block; } } } /* There should be enough blocks for metadata. */ if (nand_flash -> lx_nand_flash_backup_metadata_block_number_next == LX_NAND_BLOCK_UNMAPPED) { return (LX_NO_BLOCKS); } /* Save the block status for metadata. */ nand_flash -> lx_nand_flash_block_status_table[nand_flash -> lx_nand_flash_backup_metadata_block_number_next] = LX_NAND_BLOCK_STATUS_ALLOCATED; nand_flash -> lx_nand_flash_block_status_table[nand_flash -> lx_nand_flash_metadata_block_number_next] = LX_NAND_BLOCK_STATUS_ALLOCATED; nand_flash -> lx_nand_flash_block_status_table[nand_flash -> lx_nand_flash_backup_metadata_block_number] = (USHORT)nand_flash -> lx_nand_flash_backup_metadata_block_number_next; nand_flash -> lx_nand_flash_block_status_table[nand_flash -> lx_nand_flash_metadata_block_number] = (USHORT)nand_flash -> lx_nand_flash_metadata_block_number_next; /* Initialize the mapping table. */ LX_MEMSET(nand_flash -> lx_nand_flash_block_mapping_table, 0xFF, nand_flash -> lx_nand_flash_block_mapping_table_size); /* Build initial metadata. */ status = _lx_nand_flash_metadata_build(nand_flash); if (status != LX_SUCCESS) { /* Return error status. */ return(status); } /* Get buffer for page data. */ page_buffer_ptr = nand_flash -> lx_nand_flash_page_buffer; /* Initialize the page buffer. */ LX_MEMSET(page_buffer_ptr, 0xFF, nand_flash -> lx_nand_flash_bytes_per_page); /* Set the next block numbers. */ LX_UTILITY_LONG_SET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_MAIN_METADATA_OFFSET], nand_flash -> lx_nand_flash_metadata_block_number_next); LX_UTILITY_LONG_SET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_BACKUP_METADATA_OFFSET], nand_flash -> lx_nand_flash_backup_metadata_block_number_next); /* Save the next block numbers to metadata block. */ status = _lx_nand_flash_metadata_write(nand_flash, page_buffer_ptr, LX_NAND_PAGE_TYPE_BLOCK_LINK); if (status != LX_SUCCESS) { /* Return error status. */ return(status); } /* Return a successful completion. */ return(LX_SUCCESS); }