From 7d1ef20acf69375cda756060b1de339b2e20a299 Mon Sep 17 00:00:00 2001 From: Haithem Rahmani Date: Wed, 22 Oct 2025 16:08:53 +0100 Subject: [PATCH 1/8] fix: "array out of bounds" error leading to memory corruption Fix description ---------------- add missing checks on the index of nor_flash -> lx_nor_flash_extended_cache[i] Fixes Issue#58 Signed-off-by: Haithem Rahmani --- common/src/lx_nor_flash_extended_cache_enable.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/lx_nor_flash_extended_cache_enable.c b/common/src/lx_nor_flash_extended_cache_enable.c index 86b3673..2b31afb 100644 --- a/common/src/lx_nor_flash_extended_cache_enable.c +++ b/common/src/lx_nor_flash_extended_cache_enable.c @@ -290,7 +290,7 @@ ULONG block_word; /* Loop through the memory supplied and assign to cache entries. */ i = 0; - while (cache_size >= LX_NOR_SECTOR_SIZE) + while ((cache_size >= LX_NOR_SECTOR_SIZE) && (i < LX_NOR_EXTENDED_CACHE_SIZE)) { /* Setup this cache entry. */ @@ -309,7 +309,7 @@ ULONG block_word; } /* Save the number of cache entries. */ - if(i > LX_NOR_EXTENDED_CACHE_SIZE) + if(i == LX_NOR_EXTENDED_CACHE_SIZE) { nor_flash -> lx_nor_flash_extended_cache_entries = LX_NOR_EXTENDED_CACHE_SIZE; From b5dcbff38c7c908a68bff2d4efbf88d855b8b270 Mon Sep 17 00:00:00 2001 From: Haithem Rahmani Date: Wed, 22 Oct 2025 16:08:53 +0100 Subject: [PATCH 2/8] fix: "array out of bounds" error leading to memory corruption Fix description ---------------- add missing checks on the index of nor_flash -> lx_nor_flash_extended_cache[i] Fixes Issue#58 Signed-off-by: Haithem Rahmani --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index db1b87d..b4c1e06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR) +cmake_minimum_required(VERSION 3.13 FATAL_ERROR) # Set up the project project(levelx From 0f7dd521e5949d14129fc0c1c23dd0e259a30a51 Mon Sep 17 00:00:00 2001 From: Haithem Rahmani Date: Tue, 4 Nov 2025 16:49:33 +0100 Subject: [PATCH 3/8] fix: Wrong out of range check in lx_nand_flash_block_find Fix issue 53 Signed-off-by: Haithem Rahmani --- common/src/lx_nand_flash_block_find.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/lx_nand_flash_block_find.c b/common/src/lx_nand_flash_block_find.c index bc37d2a..6af95c9 100644 --- a/common/src/lx_nand_flash_block_find.c +++ b/common/src/lx_nand_flash_block_find.c @@ -90,7 +90,7 @@ UINT _lx_nand_flash_block_find(LX_NAND_FLASH *nand_flash, ULONG logical_sector, block_mapping_index = logical_sector / nand_flash -> lx_nand_flash_pages_per_block; /* Check the address range. */ - if (block_mapping_index > nand_flash -> lx_nand_flash_block_mapping_table_size / sizeof(*nand_flash -> lx_nand_flash_block_mapping_table)) + if (block_mapping_index >= nand_flash -> lx_nand_flash_block_mapping_table_size / sizeof(*nand_flash -> lx_nand_flash_block_mapping_table)) { /* Out of range, return an error. */ From 47b2a17d4ba7c7ac1b7841d2c0353f5995504222 Mon Sep 17 00:00:00 2001 From: Haithem Rahmani Date: Tue, 7 Oct 2025 15:00:32 +0100 Subject: [PATCH 4/8] Add new levelx APIs New APIs -------- - lx_nor_flash_format(): erases nor flash blocks and setup block metadata - lx_nor_flash_open_extended(): same as lx_nor_flash_open() with user data pointer a last argument. - lx_nand_flash_open_extended(): same as lx_nand_flash_open() with user data pointer a last argument. Updates ------- - lx_nor_flash_open(): calls lx_nor_flash_open_extended() with a NULL as last argument - lx_nand_flash_open(): calls lx_nand_flash_open_extended() with a NULL as last argument - regression tests: to call the lx_nor_flash_format() Test results ------------- 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 6.27 sec 3/3 Test #1: standalone_free_sector_verify_build::levelx_nand_flash_test ........ Passed 6.29 sec 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 6.29 sec 3/3 Test #1: nor_obsolete_mapping_cache_build::levelx_nand_flash_test ........ Passed 6.58 sec 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 6.58 sec 3/3 Test #1: nor_obsolete_cache_build::levelx_nand_flash_test ........ Passed 6.67 sec 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 6.67 sec 3/3 Test #1: nor_mapping_cache_build::levelx_nand_flash_test ........ Passed 6.77 sec 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 6.78 sec 3/3 Test #1: full_build::levelx_nand_flash_test ........ Passed 6.91 sec 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 6.91 sec 3/3 Test #1: free_sector_verify_build::levelx_nand_flash_test ........ Passed 7.14 sec 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 7.14 sec 3/3 Test #1: standalone_full_build::levelx_nand_flash_test ........ Passed 7.21 sec 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 7.21 sec 3/3 Test #1: standalone_build::levelx_nand_flash_test ........ Passed 12.05 sec 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 12.06 sec 3/3 Test #1: default_build_coverage::levelx_nand_flash_test ........ Passed 48.78 sec 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 48.81 sec Add new levelx APIs New APIs -------- - lx_nor_flash_format(): erases nor flash blocks and setup block metadata - lx_nor_flash_open_extended(): same as lx_nor_flash_open() with user data pointer a last argument. - lx_nand_flash_open_extended(): same as lx_nand_flash_open() with user data pointer a last argument. Updates ------- - lx_nor_flash_open(): calls lx_nor_flash_open_extended() with a NULL as last argument - lx_nand_flash_open(): calls lx_nand_flash_open_extended() with a NULL as last argument - regression tests: to call the lx_nor_flash_format() Test results ------------- 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 6.27 sec 3/3 Test #1: standalone_free_sector_verify_build::levelx_nand_flash_test ........ Passed 6.29 sec 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 6.29 sec 3/3 Test #1: nor_obsolete_mapping_cache_build::levelx_nand_flash_test ........ Passed 6.58 sec 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 6.58 sec 3/3 Test #1: nor_obsolete_cache_build::levelx_nand_flash_test ........ Passed 6.67 sec 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 6.67 sec 3/3 Test #1: nor_mapping_cache_build::levelx_nand_flash_test ........ Passed 6.77 sec 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 6.78 sec 3/3 Test #1: full_build::levelx_nand_flash_test ........ Passed 6.91 sec 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 6.91 sec 3/3 Test #1: free_sector_verify_build::levelx_nand_flash_test ........ Passed 7.14 sec 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 7.14 sec 3/3 Test #1: standalone_full_build::levelx_nand_flash_test ........ Passed 7.21 sec 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 7.21 sec 3/3 Test #1: standalone_build::levelx_nand_flash_test ........ Passed 12.05 sec 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 12.06 sec 3/3 Test #1: default_build_coverage::levelx_nand_flash_test ........ Passed 48.78 sec 100% tests passed, 0 tests failed out of 3 Total Test time (real) = 48.81 sec fix: Write the correct meta data on the block after erase add missing nand_flash_format_extended API --- common/CMakeLists.txt | 4 + common/inc/lx_api.h | 23 +- common/src/lx_nand_flash_format.c | 216 +--- common/src/lx_nand_flash_format_extended.c | 279 ++++++ common/src/lx_nand_flash_open.c | 446 +-------- common/src/lx_nand_flash_open_extended.c | 536 ++++++++++ common/src/lx_nor_flash_format.c | 214 ++++ common/src/lx_nor_flash_open.c | 938 ++---------------- common/src/lx_nor_flash_open_extended.c | 914 +++++++++++++++++ test/regression/levelx_nor_flash_test.c | 14 +- test/regression/levelx_nor_flash_test_cache.c | 17 +- 11 files changed, 2059 insertions(+), 1542 deletions(-) create mode 100644 common/src/lx_nand_flash_format_extended.c create mode 100644 common/src/lx_nand_flash_open_extended.c create mode 100644 common/src/lx_nor_flash_format.c create mode 100644 common/src/lx_nor_flash_open_extended.c diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 7981774..f4cfbfe 100755 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -20,6 +20,7 @@ target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_erase_count_set.c ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_extended_cache_enable.c ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_format.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_format_extended.c ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_free_block_list_add.c ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_initialize.c ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_mapped_block_list_add.c @@ -30,6 +31,7 @@ target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_metadata_build.c ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_metadata_write.c ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_open.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_open_extended.c ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_page_ecc_check.c ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_page_ecc_compute.c ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_partial_defragment.c @@ -52,6 +54,8 @@ target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_logical_sector_find.c ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_next_block_to_erase_find.c ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_open.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_open_extended.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_format.c ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_partial_defragment.c ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_physical_sector_allocate.c ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_sector_mapping_cache_invalidate.c diff --git a/common/inc/lx_api.h b/common/inc/lx_api.h index b429456..05dab15 100644 --- a/common/inc/lx_api.h +++ b/common/inc/lx_api.h @@ -1,6 +1,7 @@ /*************************************************************************** - * Copyright (c) 2024 Microsoft Corporation - * + * Copyright (c) 2024 Microsoft Corporation + * Portion Copyright (c) 2025 STMicroelectronics + * * This program and the accompanying materials are made available under the * terms of the MIT License which is available at * https://opensource.org/licenses/MIT. @@ -528,7 +529,6 @@ typedef struct LX_NAND_FLASH_STRUCT #endif UCHAR *lx_nand_flash_page_buffer; UINT lx_nand_flash_page_buffer_size; - #ifdef LX_THREAD_SAFE_ENABLE /* When this conditional is used, the LevelX code utilizes a ThreadX mutex for thread @@ -537,6 +537,8 @@ typedef struct LX_NAND_FLASH_STRUCT TX_MUTEX lx_nand_flash_mutex; #endif + /* user data pointer optionally passed by the application to the driver via the lx_nand_flash_open_extended */ + VOID *lx_nand_flash_driver_info_ptr; /* Define the NAND flash control block open next/previous pointers. */ struct LX_NAND_FLASH_STRUCT *lx_nand_flash_open_next, *lx_nand_flash_open_previous; @@ -666,7 +668,8 @@ typedef struct LX_NOR_FLASH_STRUCT a higher layer. */ TX_MUTEX lx_nor_flash_mutex; #endif - + /* user data pointer optionally passed by the application to the driver via the lx_nor_flash_open_extended */ + VOID *lx_nor_flash_driver_info_ptr; /* Define the NOR flash control block open next/previous pointers. */ struct LX_NOR_FLASH_STRUCT *lx_nor_flash_open_next, *lx_nor_flash_open_previous; @@ -725,8 +728,10 @@ extern ULONG _lx_nor_flash_opened_cou #define lx_nand_flash_partial_defragment _lx_nand_flash_partial_defragment #define lx_nand_flash_extended_cache_enable _lx_nand_flash_extended_cache_enable #define lx_nand_flash_format _lx_nand_flash_format +#define lx_nand_flash_format_extended _lx_nand_flash_format_extended #define lx_nand_flash_initialize _lx_nand_flash_initialize #define lx_nand_flash_open _lx_nand_flash_open +#define lx_nand_flash_open_extended _lx_nand_flash_open_extended #define lx_nand_flash_page_ecc_check _lx_nand_flash_page_ecc_check #define lx_nand_flash_page_ecc_compute _lx_nand_flash_page_ecc_compute #define lx_nand_flash_sector_read _lx_nand_flash_sector_read @@ -743,7 +748,9 @@ extern ULONG _lx_nor_flash_opened_cou #define lx_nor_flash_partial_defragment _lx_nor_flash_partial_defragment #define lx_nor_flash_extended_cache_enable _lx_nor_flash_extended_cache_enable #define lx_nor_flash_initialize _lx_nor_flash_initialize +#define lx_nor_flash_format _lx_nor_flash_format #define lx_nor_flash_open _lx_nor_flash_open +#define lx_nor_flash_open_extended _lx_nor_flash_open_extended #define lx_nor_flash_sector_read _lx_nor_flash_sector_read #define lx_nor_flash_sector_release _lx_nor_flash_sector_release #define lx_nor_flash_sector_write _lx_nor_flash_sector_write @@ -759,7 +766,13 @@ UINT _lx_nand_flash_extended_cache_enable(LX_NAND_FLASH *nand_flash, VOID *m 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); +UINT _lx_nand_flash_format_extended(LX_NAND_FLASH* nand_flash, CHAR* name, + UINT(*nand_driver_initialize)(LX_NAND_FLASH*), VOID *nand_driver_info_ptr, + ULONG* memory_ptr, UINT memory_size); UINT _lx_nand_flash_open(LX_NAND_FLASH *nand_flash, CHAR *name, UINT (*nand_driver_initialize)(LX_NAND_FLASH *), ULONG* memory_ptr, UINT memory_size); +UINT _lx_nand_flash_open_extended(LX_NAND_FLASH *nand_flash, CHAR *name, + UINT (*nand_driver_initialize)(LX_NAND_FLASH *), VOID *nand_driver_info_ptr, + ULONG* memory_ptr, UINT memory_size); UINT _lx_nand_flash_page_ecc_check(LX_NAND_FLASH *nand_flash, UCHAR *page_buffer, UCHAR *ecc_buffer); UINT _lx_nand_flash_page_ecc_compute(LX_NAND_FLASH *nand_flash, UCHAR *page_buffer, UCHAR *ecc_buffer); UINT _lx_nand_flash_partial_defragment(LX_NAND_FLASH *nand_flash, UINT max_blocks); @@ -774,7 +787,9 @@ UINT _lx_nor_flash_close(LX_NOR_FLASH *nor_flash); UINT _lx_nor_flash_defragment(LX_NOR_FLASH *nor_flash); UINT _lx_nor_flash_extended_cache_enable(LX_NOR_FLASH *nor_flash, VOID *memory, ULONG size); UINT _lx_nor_flash_initialize(void); +UINT _lx_nor_flash_format(LX_NOR_FLASH *nor_flash, CHAR *name, UINT (*nor_driver_initialize)(LX_NOR_FLASH *), VOID *nor_driver_info_ptr); UINT _lx_nor_flash_open(LX_NOR_FLASH *nor_flash, CHAR *name, UINT (*nor_driver_initialize)(LX_NOR_FLASH *)); +UINT _lx_nor_flash_open_extended(LX_NOR_FLASH *nor_flash, CHAR *name, UINT (*nor_driver_initialize)(LX_NOR_FLASH *), VOID *nor_driver_info_ptr); UINT _lx_nor_flash_partial_defragment(LX_NOR_FLASH *nor_flash, UINT max_blocks); UINT _lx_nor_flash_sector_read(LX_NOR_FLASH *nor_flash, ULONG logical_sector, VOID *buffer); UINT _lx_nor_flash_sector_release(LX_NOR_FLASH *nor_flash, ULONG logical_sector); diff --git a/common/src/lx_nand_flash_format.c b/common/src/lx_nand_flash_format.c index 025a534..3835001 100644 --- a/common/src/lx_nand_flash_format.c +++ b/common/src/lx_nand_flash_format.c @@ -1,5 +1,6 @@ /*************************************************************************** * Copyright (c) 2024 Microsoft Corporation + * Copyright (c) 2025 STMicroelectronics * * This program and the accompanying materials are made available under the * terms of the MIT License which is available at @@ -39,7 +40,7 @@ /* FUNCTION RELEASE */ /* */ /* _lx_nand_flash_format PORTABLE C */ -/* 6.3.0 */ +/* */ /* AUTHOR */ /* */ /* Xiuwen Cai, Microsoft Corporation */ @@ -64,224 +65,21 @@ /* 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 */ -/* */ +/* _lx_nand_flash_format_extended */ +/* */ /* 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); - } +UINT status; - /* Is this block bad? */ - if (block_status != LX_NAND_GOOD_BLOCK) - { + status = _lx_nand_flash_format_extended(nand_flash, name, nand_driver_initialize, NULL, memory_ptr, memory_size); - /* 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); + return status; } - diff --git a/common/src/lx_nand_flash_format_extended.c b/common/src/lx_nand_flash_format_extended.c new file mode 100644 index 0000000..6598910 --- /dev/null +++ b/common/src/lx_nand_flash_format_extended.c @@ -0,0 +1,279 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * Copyright (c) 2025 STMicroelectronics + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** 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_extended PORTABLE C */ +/* */ +/* 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 */ +/* nand_driver_info_ptr Driver user data pointer */ +/* 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 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_format_extended(LX_NAND_FLASH* nand_flash, CHAR* name, + UINT(*nand_driver_initialize)(LX_NAND_FLASH*),VOID* nand_driver_info_ptr, + 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)); + + nand_flash -> lx_nand_flash_driver_info_ptr = nand_driver_info_ptr; + /* 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); +} + diff --git a/common/src/lx_nand_flash_open.c b/common/src/lx_nand_flash_open.c index 4e40309..9935bae 100644 --- a/common/src/lx_nand_flash_open.c +++ b/common/src/lx_nand_flash_open.c @@ -54,6 +54,7 @@ /* nand_flash NAND flash instance */ /* name Name of NAND flash instance */ /* nand_driver_initialize Driver initialize */ +/* nand_driver_info_ptr User data pointer */ /* memory_ptr Pointer to memory used by the */ /* LevelX for this NAND. */ /* memory_size Size of memory - must at least*/ @@ -66,15 +67,7 @@ /* */ /* CALLS */ /* */ -/* (nand_driver_initialize) Driver initialize */ -/* _lx_nand_flash_memory_initialize Initialize buffer */ -/* _lx_nand_flash_driver_block_status_get */ -/* Get block status */ -/* lx_nand_flash_driver_pages_read Read pages */ -/* _lx_nand_flash_free_block_list_add Add free block to list */ -/* _lx_nand_flash_mapped_block_list_add Add mapped block to list */ -/* _lx_nand_flash_system_error System error handler */ -/* tx_mutex_create Create thread-safe mutex */ +/* lx_nand_flash_open_extended Pass NULL as driver_info_ptr */ /* */ /* CALLED BY */ /* */ @@ -95,438 +88,9 @@ UINT _lx_nand_flash_open(LX_NAND_FLASH *nand_flash, CHAR *name, UINT (*nand_driver_initialize)(LX_NAND_FLASH *), ULONG* memory_ptr, UINT memory_size) { +UINT status = LX_ERROR; -ULONG block; -ULONG page; -UCHAR block_status; -ULONG block_count; -UINT status; -LX_NAND_FLASH *tail_ptr; -LX_NAND_DEVICE_INFO *nand_device_info_page; -UCHAR *spare_buffer_ptr; -UCHAR *page_buffer_ptr; -ULONG page_type; -UCHAR page_index; -LX_INTERRUPT_SAVE_AREA + status = _lx_nand_flash_open_extended(nand_flash, name, nand_driver_initialize, NULL, memory_ptr, memory_size); - 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 an error. */ - 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 an error. */ - return(LX_ERROR); - } - - /* Check the spare data length. */ - if (nand_flash -> lx_nand_flash_spare_data1_length < sizeof(ULONG)) - { - - /* Return an error. */ - 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_backup_metadata_block_number = LX_NAND_BLOCK_UNMAPPED; - - /* Setup page buffer and spare buffer pointers. */ - page_buffer_ptr = nand_flash -> lx_nand_flash_page_buffer; - spare_buffer_ptr = page_buffer_ptr + nand_flash -> lx_nand_flash_bytes_per_page; - - /* 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; - } - - /* Call driver read function to read page 0. */ -#ifdef LX_NAND_ENABLE_CONTROL_BLOCK_FOR_DRIVER_INTERFACE - status = (nand_flash -> lx_nand_flash_driver_pages_read)(nand_flash, block, 0, page_buffer_ptr, spare_buffer_ptr, 1); -#else - status = (nand_flash -> lx_nand_flash_driver_pages_read)(block, 0, page_buffer_ptr, spare_buffer_ptr, 1); -#endif - - /* Check for an error from flash driver. */ - if (status) - { - - /* 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); - } - } - - /* Get the page type. */ - page_type = LX_UTILITY_LONG_GET(&spare_buffer_ptr[nand_flash -> lx_nand_flash_spare_data1_offset]); - - /* Check if the type is device info. */ - if (page_type == LX_NAND_PAGE_TYPE_DEVICE_INFO) - { - - /* Get the device info page. */ - nand_device_info_page = (LX_NAND_DEVICE_INFO*)page_buffer_ptr; - - /* Check signature. */ - if (nand_device_info_page -> lx_nand_device_info_signature1 == LX_NAND_DEVICE_INFO_SIGNATURE1 && - nand_device_info_page -> lx_nand_device_info_signature2 == LX_NAND_DEVICE_INFO_SIGNATURE2) - { - - /* Save the block numbers. */ - nand_flash -> lx_nand_flash_metadata_block_number = nand_device_info_page -> lx_nand_device_info_metadata_block_number; - nand_flash -> lx_nand_flash_backup_metadata_block_number = nand_device_info_page -> lx_nand_device_info_backup_metadata_block_number; - break; - } - - } - - } - - /* Check if we have found the metadata block. */ - if (nand_flash -> lx_nand_flash_metadata_block_number == LX_NAND_BLOCK_UNMAPPED) - { - - /* Not found, return an error. */ - return (LX_ERROR); - } - - /* Initialize metadata block numbers and lists. */ - nand_flash -> lx_nand_flash_metadata_block_number_current = nand_flash -> lx_nand_flash_metadata_block_number; - nand_flash -> lx_nand_flash_backup_metadata_block_number_current = nand_flash -> lx_nand_flash_backup_metadata_block_number; - nand_flash -> lx_nand_flash_metadata_block[0] = (USHORT)nand_flash -> lx_nand_flash_metadata_block_number; - nand_flash -> lx_nand_flash_backup_metadata_block[0] = (USHORT)nand_flash -> lx_nand_flash_backup_metadata_block_number; - - /* Found one metadata block. */ - nand_flash -> lx_nand_flash_metadata_block_count = 1; - - /* Clear searched block count. */ - block_count = 0; - - do - { - - /* Initialize next block to unmapped. */ - nand_flash -> lx_nand_flash_metadata_block_number_next = LX_NAND_BLOCK_UNMAPPED; - nand_flash -> lx_nand_flash_backup_metadata_block_number_next = LX_NAND_BLOCK_UNMAPPED; - - /* Loop to read pages in the metadata block. */ - for (page = 0; page < nand_flash -> lx_nand_flash_pages_per_block ; page++) - { - - /* Call driver read function to read page. */ -#ifdef LX_NAND_ENABLE_CONTROL_BLOCK_FOR_DRIVER_INTERFACE - status = (nand_flash -> lx_nand_flash_driver_pages_read)(nand_flash, block, page, page_buffer_ptr, spare_buffer_ptr, 1); -#else - status = (nand_flash -> lx_nand_flash_driver_pages_read)(block, page, page_buffer_ptr, spare_buffer_ptr, 1); -#endif - - /* Check for an error from flash driver. */ - if (status) - { - - /* 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); - } - } - - /* Get page type and page index. */ - page_type = LX_UTILITY_LONG_GET(&spare_buffer_ptr[nand_flash -> lx_nand_flash_spare_data1_offset]) & (~LX_NAND_PAGE_TYPE_PAGE_NUMBER_MASK); - page_index = LX_UTILITY_LONG_GET(&spare_buffer_ptr[nand_flash -> lx_nand_flash_spare_data1_offset]) & LX_NAND_PAGE_TYPE_PAGE_NUMBER_MASK; - - /* Process metadata page by type. */ - switch (page_type) - { - case LX_NAND_PAGE_TYPE_DEVICE_INFO: - - /* This page is for device info. */ - nand_device_info_page = (LX_NAND_DEVICE_INFO*)page_buffer_ptr; - - /* Get the base erase count. */ - nand_flash -> lx_nand_flash_base_erase_count = nand_device_info_page -> lx_nand_device_info_base_erase_count; - break; - - case LX_NAND_PAGE_TYPE_ERASE_COUNT_TABLE: - - /* Check if page index is valid. */ - if (((ULONG)page_index + 1) * nand_flash -> lx_nand_flash_bytes_per_page > nand_flash -> lx_nand_flash_erase_count_table_size) - { - - /* Invalid page index. Return an error. */ - status = LX_ERROR; - break; - } - - /* Copy page data to erase count table. */ - LX_MEMCPY(nand_flash -> lx_nand_flash_erase_count_table + page_index * nand_flash -> lx_nand_flash_bytes_per_page, /* Use case of memcpy is verified. */ - page_buffer_ptr, nand_flash -> lx_nand_flash_bytes_per_page); - break; - - case LX_NAND_PAGE_TYPE_BLOCK_MAPPING_TABLE: - - /* Check if page index is valid. */ - if (((ULONG)page_index + 1) * nand_flash -> lx_nand_flash_bytes_per_page > nand_flash -> lx_nand_flash_block_mapping_table_size) - { - - /* Invalid page index. Return an error. */ - status = LX_ERROR; - break; - } - - /* Copy page data to block mapping table. */ - LX_MEMCPY(nand_flash -> lx_nand_flash_block_mapping_table + page_index * nand_flash -> lx_nand_flash_bytes_per_page / sizeof(*nand_flash -> lx_nand_flash_block_mapping_table), /* Use case of memcpy is verified. */ - page_buffer_ptr, nand_flash -> lx_nand_flash_bytes_per_page); - break; - - case LX_NAND_PAGE_TYPE_BLOCK_STATUS_TABLE: - - /* Check if page index is valid. */ - if (((ULONG)page_index + 1) * nand_flash -> lx_nand_flash_bytes_per_page > nand_flash -> lx_nand_flash_block_status_table_size) - { - - /* Invalid page index. Return an error. */ - status = LX_ERROR; - break; - } - - /* Copy page data to block status table. */ - LX_MEMCPY(nand_flash -> lx_nand_flash_block_status_table + page_index * nand_flash -> lx_nand_flash_bytes_per_page / sizeof(*nand_flash -> lx_nand_flash_block_status_table), /* Use case of memcpy is verified. */ - page_buffer_ptr, nand_flash -> lx_nand_flash_bytes_per_page); - break; - - case LX_NAND_PAGE_TYPE_FREE_PAGE: - - /* Found a free page. Update current page. */ - nand_flash -> lx_nand_flash_metadata_block_current_page = page; - nand_flash -> lx_nand_flash_backup_metadata_block_current_page = page; - - /* Skip all the remaining pages. */ - page = nand_flash -> lx_nand_flash_pages_per_block; - break; - - case LX_NAND_PAGE_TYPE_BLOCK_LINK: - - /* Found next blocks. Update next block numbers. */ - nand_flash -> lx_nand_flash_metadata_block_number_next = LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_MAIN_METADATA_OFFSET]); - nand_flash -> lx_nand_flash_backup_metadata_block_number_next = LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_BACKUP_METADATA_OFFSET]); - - /* Save block numbers in metadata block lists. */ - nand_flash -> lx_nand_flash_metadata_block[nand_flash -> lx_nand_flash_metadata_block_count] = (USHORT)LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_MAIN_METADATA_OFFSET]); - nand_flash -> lx_nand_flash_backup_metadata_block[nand_flash -> lx_nand_flash_metadata_block_count] = (USHORT)LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_BACKUP_METADATA_OFFSET]); - - /* Increase metadata block count. */ - nand_flash -> lx_nand_flash_metadata_block_count++; - - break; - - default: - - /* Unknown type, return error. */ - status = LX_ERROR; - } - - /* Check status. */ - if (status == LX_ERROR) - { - - /* Error, break the loop. */ - break; - } - - } - - /* Check if we have reached the last page. */ - if (page == nand_flash -> lx_nand_flash_pages_per_block) - { - - /* Move to next block. */ - nand_flash -> lx_nand_flash_metadata_block_number_current = nand_flash -> lx_nand_flash_metadata_block_number_next; - nand_flash -> lx_nand_flash_backup_metadata_block_number_current = nand_flash -> lx_nand_flash_backup_metadata_block_number_next; - - /* Make sure the block is valid. */ - if (nand_flash -> lx_nand_flash_metadata_block_number_current == LX_NAND_BLOCK_UNMAPPED) - { - - /* Error, break the loop. */ - break; - } - - /* Get the block to process. */ - block = nand_flash -> lx_nand_flash_metadata_block_number_current; - } - - /* Increase the processed block number. */ - block_count++; - - /* If block count is larger than total blocks, there is an error. */ - if (block_count >= nand_flash -> lx_nand_flash_total_blocks) - { - - /* Break the loop. */ - break; - } - } while (nand_flash -> lx_nand_flash_metadata_block_current_page == 0 && status != LX_ERROR); - - /* Check if metadata page is processed correctly. */ - if (nand_flash -> lx_nand_flash_metadata_block_current_page == 0 || status == LX_ERROR) - { - - /* Return an error. */ - return(LX_ERROR); - } - - /* Loop to build free and mapped block lists. */ - for (block = 0; block < nand_flash -> lx_nand_flash_total_blocks; block++) - { - - /* Check for free blocks. */ - if (nand_flash -> lx_nand_flash_block_status_table[block] == LX_NAND_BLOCK_STATUS_FREE) - { - - /* Add the block to free block list. */ - _lx_nand_flash_free_block_list_add(nand_flash, block); - } - - /* Check for mapped blocks. */ - if (nand_flash -> lx_nand_flash_block_mapping_table[block] != LX_NAND_BLOCK_UNMAPPED) - { - - /* Add the block to free block list. */ - _lx_nand_flash_mapped_block_list_add(nand_flash, block); - } - } - - -#ifdef LX_THREAD_SAFE_ENABLE - - /* If the thread safe option is enabled, create a ThreadX mutex that will be used in all external APIs - in order to provide thread-safe operation. */ - status = tx_mutex_create(&nand_flash -> lx_nand_flash_mutex, "NAND Flash Mutex", TX_NO_INHERIT); - - /* Determine if the mutex creation encountered an error. */ - if (status != LX_SUCCESS) - { - - /* Call system error handler, since this should not happen. */ - _lx_nand_flash_system_error(nand_flash, LX_SYSTEM_MUTEX_CREATE_FAILED, 0, 0); - - /* Return error to caller. */ - return(LX_ERROR); - } -#endif - - /* Lockout interrupts. */ - LX_DISABLE - - /* At this point, the NAND flash has been opened successfully. Place the - NAND flash control block on the linked list of currently opened NAND flashes. */ - - /* Set the NAND flash state to open. */ - nand_flash -> lx_nand_flash_state = LX_NAND_FLASH_OPENED; - - /* Place the NAND flash control block on the list of opened NAND flashes. First, - check for an empty list. */ - if (_lx_nand_flash_opened_count) - { - - /* List is not empty - other NAND flashes are open. */ - - /* Pickup tail pointer. */ - tail_ptr = _lx_nand_flash_opened_ptr -> lx_nand_flash_open_previous; - - /* Place the new NAND flash control block in the list. */ - _lx_nand_flash_opened_ptr -> lx_nand_flash_open_previous = nand_flash; - tail_ptr -> lx_nand_flash_open_next = nand_flash; - - /* Setup this NAND flash's opened links. */ - nand_flash -> lx_nand_flash_open_previous = tail_ptr; - nand_flash -> lx_nand_flash_open_next = _lx_nand_flash_opened_ptr; - } - else - { - - /* The opened NAND flash list is empty. Add the NAND flash to empty list. */ - _lx_nand_flash_opened_ptr = nand_flash; - nand_flash -> lx_nand_flash_open_next = nand_flash; - nand_flash -> lx_nand_flash_open_previous = nand_flash; - } - - /* Increment the opened NAND flash counter. */ - _lx_nand_flash_opened_count++; - - /* Restore interrupts. */ - LX_RESTORE - - /* Return a successful completion. */ - return(LX_SUCCESS); + return status; } - diff --git a/common/src/lx_nand_flash_open_extended.c b/common/src/lx_nand_flash_open_extended.c new file mode 100644 index 0000000..23ad31b --- /dev/null +++ b/common/src/lx_nand_flash_open_extended.c @@ -0,0 +1,536 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** 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_open PORTABLE C */ +/* 6.3.0 */ +/* AUTHOR */ +/* */ +/* Xiuwen Cai, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function opens a NAND flash instance and ensures the */ +/* NAND flash is in a coherent state. */ +/* */ +/* 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_nand_flash_memory_initialize Initialize buffer */ +/* _lx_nand_flash_driver_block_status_get */ +/* Get block status */ +/* lx_nand_flash_driver_pages_read Read pages */ +/* _lx_nand_flash_free_block_list_add Add free block to list */ +/* _lx_nand_flash_mapped_block_list_add Add mapped block to list */ +/* _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_open_extended(LX_NAND_FLASH *nand_flash, CHAR *name, UINT (*nand_driver_initialize)(LX_NAND_FLASH *), + VOID *nand_driver_info_ptr, ULONG* memory_ptr, UINT memory_size) +{ + +ULONG block; +ULONG page; +UCHAR block_status; +ULONG block_count; +UINT status; +LX_NAND_FLASH *tail_ptr; +LX_NAND_DEVICE_INFO *nand_device_info_page; +UCHAR *spare_buffer_ptr; +UCHAR *page_buffer_ptr; +ULONG page_type; +UCHAR page_index; +LX_INTERRUPT_SAVE_AREA + + 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)); + + /* Pass the driver_info_ptr */ + nand_flash -> lx_nand_flash_driver_info_ptr = nand_driver_info_ptr; + + /* 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 an error. */ + 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 an error. */ + return(LX_ERROR); + } + + /* Check the spare data length. */ + if (nand_flash -> lx_nand_flash_spare_data1_length < sizeof(ULONG)) + { + + /* Return an error. */ + 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_backup_metadata_block_number = LX_NAND_BLOCK_UNMAPPED; + + /* Setup page buffer and spare buffer pointers. */ + page_buffer_ptr = nand_flash -> lx_nand_flash_page_buffer; + spare_buffer_ptr = page_buffer_ptr + nand_flash -> lx_nand_flash_bytes_per_page; + + /* 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; + } + + /* Call driver read function to read page 0. */ +#ifdef LX_NAND_ENABLE_CONTROL_BLOCK_FOR_DRIVER_INTERFACE + status = (nand_flash -> lx_nand_flash_driver_pages_read)(nand_flash, block, 0, page_buffer_ptr, spare_buffer_ptr, 1); +#else + status = (nand_flash -> lx_nand_flash_driver_pages_read)(block, 0, page_buffer_ptr, spare_buffer_ptr, 1); +#endif + + /* Check for an error from flash driver. */ + if (status) + { + + /* 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); + } + } + + /* Get the page type. */ + page_type = LX_UTILITY_LONG_GET(&spare_buffer_ptr[nand_flash -> lx_nand_flash_spare_data1_offset]); + + /* Check if the type is device info. */ + if (page_type == LX_NAND_PAGE_TYPE_DEVICE_INFO) + { + + /* Get the device info page. */ + nand_device_info_page = (LX_NAND_DEVICE_INFO*)page_buffer_ptr; + + /* Check signature. */ + if (nand_device_info_page -> lx_nand_device_info_signature1 == LX_NAND_DEVICE_INFO_SIGNATURE1 && + nand_device_info_page -> lx_nand_device_info_signature2 == LX_NAND_DEVICE_INFO_SIGNATURE2) + { + + /* Save the block numbers. */ + nand_flash -> lx_nand_flash_metadata_block_number = nand_device_info_page -> lx_nand_device_info_metadata_block_number; + nand_flash -> lx_nand_flash_backup_metadata_block_number = nand_device_info_page -> lx_nand_device_info_backup_metadata_block_number; + break; + } + + } + + } + + /* Check if we have found the metadata block. */ + if (nand_flash -> lx_nand_flash_metadata_block_number == LX_NAND_BLOCK_UNMAPPED) + { + + /* Not found, return an error. */ + return (LX_ERROR); + } + + /* Initialize metadata block numbers and lists. */ + nand_flash -> lx_nand_flash_metadata_block_number_current = nand_flash -> lx_nand_flash_metadata_block_number; + nand_flash -> lx_nand_flash_backup_metadata_block_number_current = nand_flash -> lx_nand_flash_backup_metadata_block_number; + nand_flash -> lx_nand_flash_metadata_block[0] = (USHORT)nand_flash -> lx_nand_flash_metadata_block_number; + nand_flash -> lx_nand_flash_backup_metadata_block[0] = (USHORT)nand_flash -> lx_nand_flash_backup_metadata_block_number; + + /* Found one metadata block. */ + nand_flash -> lx_nand_flash_metadata_block_count = 1; + + /* Clear searched block count. */ + block_count = 0; + + do + { + + /* Initialize next block to unmapped. */ + nand_flash -> lx_nand_flash_metadata_block_number_next = LX_NAND_BLOCK_UNMAPPED; + nand_flash -> lx_nand_flash_backup_metadata_block_number_next = LX_NAND_BLOCK_UNMAPPED; + + /* Loop to read pages in the metadata block. */ + for (page = 0; page < nand_flash -> lx_nand_flash_pages_per_block ; page++) + { + + /* Call driver read function to read page. */ +#ifdef LX_NAND_ENABLE_CONTROL_BLOCK_FOR_DRIVER_INTERFACE + status = (nand_flash -> lx_nand_flash_driver_pages_read)(nand_flash, block, page, page_buffer_ptr, spare_buffer_ptr, 1); +#else + status = (nand_flash -> lx_nand_flash_driver_pages_read)(block, page, page_buffer_ptr, spare_buffer_ptr, 1); +#endif + + /* Check for an error from flash driver. */ + if (status) + { + + /* 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); + } + } + + /* Get page type and page index. */ + page_type = LX_UTILITY_LONG_GET(&spare_buffer_ptr[nand_flash -> lx_nand_flash_spare_data1_offset]) & (~LX_NAND_PAGE_TYPE_PAGE_NUMBER_MASK); + page_index = LX_UTILITY_LONG_GET(&spare_buffer_ptr[nand_flash -> lx_nand_flash_spare_data1_offset]) & LX_NAND_PAGE_TYPE_PAGE_NUMBER_MASK; + + /* Process metadata page by type. */ + switch (page_type) + { + case LX_NAND_PAGE_TYPE_DEVICE_INFO: + + /* This page is for device info. */ + nand_device_info_page = (LX_NAND_DEVICE_INFO*)page_buffer_ptr; + + /* Get the base erase count. */ + nand_flash -> lx_nand_flash_base_erase_count = nand_device_info_page -> lx_nand_device_info_base_erase_count; + break; + + case LX_NAND_PAGE_TYPE_ERASE_COUNT_TABLE: + + /* Check if page index is valid. */ + if (((ULONG)page_index + 1) * nand_flash -> lx_nand_flash_bytes_per_page > nand_flash -> lx_nand_flash_erase_count_table_size) + { + + /* Invalid page index. Return an error. */ + status = LX_ERROR; + break; + } + + /* Copy page data to erase count table. */ + LX_MEMCPY(nand_flash -> lx_nand_flash_erase_count_table + page_index * nand_flash -> lx_nand_flash_bytes_per_page, /* Use case of memcpy is verified. */ + page_buffer_ptr, nand_flash -> lx_nand_flash_bytes_per_page); + break; + + case LX_NAND_PAGE_TYPE_BLOCK_MAPPING_TABLE: + + /* Check if page index is valid. */ + if (((ULONG)page_index + 1) * nand_flash -> lx_nand_flash_bytes_per_page > nand_flash -> lx_nand_flash_block_mapping_table_size) + { + + /* Invalid page index. Return an error. */ + status = LX_ERROR; + break; + } + + /* Copy page data to block mapping table. */ + LX_MEMCPY(nand_flash -> lx_nand_flash_block_mapping_table + page_index * nand_flash -> lx_nand_flash_bytes_per_page / sizeof(*nand_flash -> lx_nand_flash_block_mapping_table), /* Use case of memcpy is verified. */ + page_buffer_ptr, nand_flash -> lx_nand_flash_bytes_per_page); + break; + + case LX_NAND_PAGE_TYPE_BLOCK_STATUS_TABLE: + + /* Check if page index is valid. */ + if (((ULONG)page_index + 1) * nand_flash -> lx_nand_flash_bytes_per_page > nand_flash -> lx_nand_flash_block_status_table_size) + { + + /* Invalid page index. Return an error. */ + status = LX_ERROR; + break; + } + + /* Copy page data to block status table. */ + LX_MEMCPY(nand_flash -> lx_nand_flash_block_status_table + page_index * nand_flash -> lx_nand_flash_bytes_per_page / sizeof(*nand_flash -> lx_nand_flash_block_status_table), /* Use case of memcpy is verified. */ + page_buffer_ptr, nand_flash -> lx_nand_flash_bytes_per_page); + break; + + case LX_NAND_PAGE_TYPE_FREE_PAGE: + + /* Found a free page. Update current page. */ + nand_flash -> lx_nand_flash_metadata_block_current_page = page; + nand_flash -> lx_nand_flash_backup_metadata_block_current_page = page; + + /* Skip all the remaining pages. */ + page = nand_flash -> lx_nand_flash_pages_per_block; + break; + + case LX_NAND_PAGE_TYPE_BLOCK_LINK: + + /* Found next blocks. Update next block numbers. */ + nand_flash -> lx_nand_flash_metadata_block_number_next = LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_MAIN_METADATA_OFFSET]); + nand_flash -> lx_nand_flash_backup_metadata_block_number_next = LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_BACKUP_METADATA_OFFSET]); + + /* Save block numbers in metadata block lists. */ + nand_flash -> lx_nand_flash_metadata_block[nand_flash -> lx_nand_flash_metadata_block_count] = (USHORT)LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_MAIN_METADATA_OFFSET]); + nand_flash -> lx_nand_flash_backup_metadata_block[nand_flash -> lx_nand_flash_metadata_block_count] = (USHORT)LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_BACKUP_METADATA_OFFSET]); + + /* Increase metadata block count. */ + nand_flash -> lx_nand_flash_metadata_block_count++; + + break; + + default: + + /* Unknown type, return error. */ + status = LX_ERROR; + } + + /* Check status. */ + if (status == LX_ERROR) + { + + /* Error, break the loop. */ + break; + } + + } + + /* Check if we have reached the last page. */ + if (page == nand_flash -> lx_nand_flash_pages_per_block) + { + + /* Move to next block. */ + nand_flash -> lx_nand_flash_metadata_block_number_current = nand_flash -> lx_nand_flash_metadata_block_number_next; + nand_flash -> lx_nand_flash_backup_metadata_block_number_current = nand_flash -> lx_nand_flash_backup_metadata_block_number_next; + + /* Make sure the block is valid. */ + if (nand_flash -> lx_nand_flash_metadata_block_number_current == LX_NAND_BLOCK_UNMAPPED) + { + + /* Error, break the loop. */ + break; + } + + /* Get the block to process. */ + block = nand_flash -> lx_nand_flash_metadata_block_number_current; + } + + /* Increase the processed block number. */ + block_count++; + + /* If block count is larger than total blocks, there is an error. */ + if (block_count >= nand_flash -> lx_nand_flash_total_blocks) + { + + /* Break the loop. */ + break; + } + } while (nand_flash -> lx_nand_flash_metadata_block_current_page == 0 && status != LX_ERROR); + + /* Check if metadata page is processed correctly. */ + if (nand_flash -> lx_nand_flash_metadata_block_current_page == 0 || status == LX_ERROR) + { + + /* Return an error. */ + return(LX_ERROR); + } + + /* Loop to build free and mapped block lists. */ + for (block = 0; block < nand_flash -> lx_nand_flash_total_blocks; block++) + { + + /* Check for free blocks. */ + if (nand_flash -> lx_nand_flash_block_status_table[block] == LX_NAND_BLOCK_STATUS_FREE) + { + + /* Add the block to free block list. */ + _lx_nand_flash_free_block_list_add(nand_flash, block); + } + + /* Check for mapped blocks. */ + if (nand_flash -> lx_nand_flash_block_mapping_table[block] != LX_NAND_BLOCK_UNMAPPED) + { + + /* Add the block to free block list. */ + _lx_nand_flash_mapped_block_list_add(nand_flash, block); + } + } + + +#ifdef LX_THREAD_SAFE_ENABLE + + /* If the thread safe option is enabled, create a ThreadX mutex that will be used in all external APIs + in order to provide thread-safe operation. */ + status = tx_mutex_create(&nand_flash -> lx_nand_flash_mutex, "NAND Flash Mutex", TX_NO_INHERIT); + + /* Determine if the mutex creation encountered an error. */ + if (status != LX_SUCCESS) + { + + /* Call system error handler, since this should not happen. */ + _lx_nand_flash_system_error(nand_flash, LX_SYSTEM_MUTEX_CREATE_FAILED, 0, 0); + + /* Return error to caller. */ + return(LX_ERROR); + } +#endif + + /* Lockout interrupts. */ + LX_DISABLE + + /* At this point, the NAND flash has been opened successfully. Place the + NAND flash control block on the linked list of currently opened NAND flashes. */ + + /* Set the NAND flash state to open. */ + nand_flash -> lx_nand_flash_state = LX_NAND_FLASH_OPENED; + + /* Place the NAND flash control block on the list of opened NAND flashes. First, + check for an empty list. */ + if (_lx_nand_flash_opened_count) + { + + /* List is not empty - other NAND flashes are open. */ + + /* Pickup tail pointer. */ + tail_ptr = _lx_nand_flash_opened_ptr -> lx_nand_flash_open_previous; + + /* Place the new NAND flash control block in the list. */ + _lx_nand_flash_opened_ptr -> lx_nand_flash_open_previous = nand_flash; + tail_ptr -> lx_nand_flash_open_next = nand_flash; + + /* Setup this NAND flash's opened links. */ + nand_flash -> lx_nand_flash_open_previous = tail_ptr; + nand_flash -> lx_nand_flash_open_next = _lx_nand_flash_opened_ptr; + } + else + { + + /* The opened NAND flash list is empty. Add the NAND flash to empty list. */ + _lx_nand_flash_opened_ptr = nand_flash; + nand_flash -> lx_nand_flash_open_next = nand_flash; + nand_flash -> lx_nand_flash_open_previous = nand_flash; + } + + /* Increment the opened NAND flash counter. */ + _lx_nand_flash_opened_count++; + + /* Restore interrupts. */ + LX_RESTORE + + /* Return a successful completion. */ + return(LX_SUCCESS); +} + diff --git a/common/src/lx_nor_flash_format.c b/common/src/lx_nor_flash_format.c new file mode 100644 index 0000000..393c6ae --- /dev/null +++ b/common/src/lx_nor_flash_format.c @@ -0,0 +1,214 @@ +/*************************************************************************** + * Copyright (c) 2025 Microsoft Corporation + * Copyright (c) 2025 STmicroelectronics + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR 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_nor_flash_format PORTABLE C */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function opens a NOR flash instance then erases all the non */ +/* erased blocks, then writes the erase count */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* name Name of NOR flash instance */ +/* nor_driver_initialize Driver initialize */ +/* nor_driver_info_ptr Drivier user data pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* (nor_driver_initialize) Driver initialize */ +/* _lx_nor_flash_driver_read Driver read */ +/* _lx_nor_flash_driver_write Driver write */ +/* (lx_nor_flash_driver_block_erased_verify) */ +/* NOR flash verify block erased */ +/* _lx_nor_flash_driver_block_erase Driver block erase */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/**************************************************************************/ + +UINT _lx_nor_flash_format(LX_NOR_FLASH *nor_flash, CHAR *name, UINT (*nor_driver_initialize)(LX_NOR_FLASH *), VOID *nor_driver_info_ptr) +{ + +ULONG l; +UINT status; + +ULONG *block_word_ptr; +ULONG bit_map_words; +ULONG sectors_per_block; +ULONG bit_map_mask; +ULONG block_word; + + LX_INTERRUPT_SAVE_AREA + + LX_PARAMETER_NOT_USED(name); + + /* Clear the NOR flash control block. User extension is not cleared. */ + LX_MEMSET(nor_flash, 0, (ULONG)((UCHAR*)&(nor_flash -> lx_nor_flash_open_previous) - (UCHAR*)nor_flash) + sizeof(nor_flash -> lx_nor_flash_open_previous)); + + nor_flash -> lx_nor_flash_driver_info_ptr = nor_driver_info_ptr; + + /* Call the flash driver's initialization function. */ + (nor_driver_initialize)(nor_flash); + +#ifndef LX_DIRECT_READ + + /* Determine if the driver supplied a RAM buffer for reading the NOR sector if direct read is not + supported. */ + if (nor_flash -> lx_nor_flash_sector_buffer == LX_NULL) + { + /* Return an error. */ + return(LX_NO_MEMORY); + } +#endif + + nor_flash -> lx_nor_flash_block_free_bit_map_offset = sizeof(LX_NOR_FLASH_BLOCK_HEADER)/sizeof(ULONG); + + LX_DISABLE + for (l = 0; l < nor_flash -> lx_nor_flash_total_blocks; l++) + { + /* Setup the initial erase count to 0, assuming that the block is already erased */ + block_word = ((ULONG) 0); + +#ifdef LX_NOR_ENABLE_CONTROL_BLOCK_FOR_DRIVER_INTERFACE + /* Check that the block is already erased */ + status = (nor_flash -> lx_nor_flash_driver_block_erased_verify)(nor_flash, l); +#else + status = (nor_flash -> lx_nor_flash_driver_block_erased_verify)(l); + +#endif + + /* the block is not erased, thus make sure to erase it */ + if (status) + { + /* the max erase count is not needed just pass the value 0 */ + status = _lx_nor_flash_driver_block_erase(nor_flash, l, 0); + /* Check for an error from flash driver */ + if (status) + { + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + LX_RESTORE + /* Return an error. */ + return(LX_ERROR); + } + + /* check that the sector was correctly erased */ +#ifdef LX_NOR_ENABLE_CONTROL_BLOCK_FOR_DRIVER_INTERFACE + status = (nor_flash -> lx_nor_flash_driver_block_erased_verify)(nor_flash, l); +#else + status = (nor_flash -> lx_nor_flash_driver_block_erased_verify)(l); +#endif + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + LX_RESTORE + /* Return an error. */ + return(LX_ERROR); + } + else + { + /* the block has been erased, thus incrememt its erase_count */ + block_word = ((ULONG) 1); + } + } + + /* Setup the block word pointer to the first word of the first block */ + block_word_ptr = nor_flash -> lx_nor_flash_base_address + l * nor_flash -> lx_nor_flash_words_per_block; + + /* Calculate the number of bits we need in the free physical sector bit map */ + sectors_per_block = (nor_flash -> lx_nor_flash_words_per_block / LX_NOR_SECTOR_SIZE) - 1; + + /* Calculate the number of words we need for the free physical sector bit map. */ + bit_map_words = (sectors_per_block + 31)/ 32; + + if ((sectors_per_block % 32) != 0) + { + bit_map_mask = (ULONG)(1 << (sectors_per_block % 32)); + bit_map_mask = bit_map_mask - 1; + } + else + { + + /* Exactly 32 sectors for the bit map mask. */ + bit_map_mask = LX_ALL_ONES; + } + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr + (nor_flash -> lx_nor_flash_block_free_bit_map_offset + (bit_map_words-1)) , &bit_map_mask, 1); + if (status) + { + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + LX_RESTORE + /* Return an error. */ + return(LX_ERROR); + } + + block_word = (block_word | LX_BLOCK_ERASED); + + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &block_word, 1); + if (status) + { + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + LX_RESTORE + /* Return an error. */ + return(LX_ERROR); + } + + } + + /* Restore interrupts. */ + LX_RESTORE + + return(LX_SUCCESS); +} diff --git a/common/src/lx_nor_flash_open.c b/common/src/lx_nor_flash_open.c index d12ffc1..edbff65 100644 --- a/common/src/lx_nor_flash_open.c +++ b/common/src/lx_nor_flash_open.c @@ -1,6 +1,7 @@ /*************************************************************************** * Copyright (c) 2024 Microsoft Corporation - * + * Portion Copyright (c) 2025 STMicroelectronics + * * This program and the accompanying materials are made available under the * terms of the MIT License which is available at * https://opensource.org/licenses/MIT. @@ -33,879 +34,74 @@ #include "lx_api.h" +/***************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nor_flash_open PORTABLE C */ +/* 6.3.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function opens a NOR flash instance and ensures the NOR flash */ +/* is in a coherent state. */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* name Name of NOR flash instance */ +/* nor_driver_initialize Driver initialize */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* lx_nor_flash_open_extended() */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 11-09-2020 William E. Lamie Modified comment(s), */ +/* fixed compiler warnings, */ +/* resulting in version 6.1.2 */ +/* 12-30-2020 William E. Lamie Modified comment(s), */ +/* fixed compiler warnings, */ +/* resulting in version 6.1.3 */ +/* 06-02-2021 Bhupendra Naphade Modified comment(s), and */ +/* updated product constants */ +/* resulting in version 6.1.7 */ +/* 03-08-2023 Xiuwen Cai Modified comment(s), */ +/* added new driver interface, */ +/* resulting in version 6.2.1 */ +/* 10-31-2023 Xiuwen Cai Modified comment(s), */ +/* added count for minimum */ +/* erased blocks, added */ +/* obsolete count cache, */ +/* avoided clearing user */ +/* extension in flash control */ +/* block, */ +/* resulting in version 6.3.0 */ +/* 10-21-2025 Modified comment(s) */ +/* call the extended equivalent */ +/* API */ -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _lx_nor_flash_open PORTABLE C */ -/* 6.3.0 */ -/* AUTHOR */ -/* */ -/* William E. Lamie, Microsoft Corporation */ -/* */ -/* DESCRIPTION */ -/* */ -/* This function opens a NOR flash instance and ensures the NOR flash */ -/* is in a coherent state. */ -/* */ -/* INPUT */ -/* */ -/* nor_flash NOR flash instance */ -/* name Name of NOR flash instance */ -/* nor_driver_initialize Driver initialize */ -/* */ -/* OUTPUT */ -/* */ -/* return status */ -/* */ -/* CALLS */ -/* */ -/* (nor_driver_initialize) Driver initialize */ -/* _lx_nor_flash_driver_read Driver read */ -/* _lx_nor_flash_driver_write Driver write */ -/* (lx_nor_flash_driver_block_erased_verify) */ -/* NOR flash verify block erased */ -/* _lx_nor_flash_driver_block_erase Driver block erase */ -/* _lx_nor_flash_logical_sector_find Find logical sector */ -/* _lx_nor_flash_system_error System error handler */ -/* tx_mutex_create Create thread-safe mutex */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ -/* DATE NAME DESCRIPTION */ -/* */ -/* 05-19-2020 William E. Lamie Initial Version 6.0 */ -/* 09-30-2020 William E. Lamie Modified comment(s), */ -/* resulting in version 6.1 */ -/* 11-09-2020 William E. Lamie Modified comment(s), */ -/* fixed compiler warnings, */ -/* resulting in version 6.1.2 */ -/* 12-30-2020 William E. Lamie Modified comment(s), */ -/* fixed compiler warnings, */ -/* resulting in version 6.1.3 */ -/* 06-02-2021 Bhupendra Naphade Modified comment(s), and */ -/* updated product constants */ -/* resulting in version 6.1.7 */ -/* 03-08-2023 Xiuwen Cai Modified comment(s), */ -/* added new driver interface, */ -/* resulting in version 6.2.1 */ -/* 10-31-2023 Xiuwen Cai Modified comment(s), */ -/* added count for minimum */ -/* erased blocks, added */ -/* obsolete count cache, */ -/* avoided clearing user */ -/* extension in flash control */ -/* block, */ -/* resulting in version 6.3.0 */ -/* */ -/**************************************************************************/ UINT _lx_nor_flash_open(LX_NOR_FLASH *nor_flash, CHAR *name, UINT (*nor_driver_initialize)(LX_NOR_FLASH *)) { -ULONG sectors_per_block; -ULONG sector_map_words; -ULONG bit_map_words; -ULONG bit_map_mask; -ULONG total_header_words; -ULONG header_sectors; -ULONG *block_word_ptr; -ULONG block_word; -ULONG temp; -ULONG free_sectors; -ULONG used_sectors; -ULONG *new_map_entry; -ULONG *new_sector_address; -ULONG erased_count, min_erased_count, max_erased_count, temp_erased_count, min_erased_blocks; -ULONG j, k, l; -UINT status; -#ifdef LX_FREE_SECTOR_DATA_VERIFY -ULONG *sector_word_ptr; -ULONG sector_word; -#endif -LX_NOR_FLASH *tail_ptr; -LX_INTERRUPT_SAVE_AREA - - LX_PARAMETER_NOT_USED(name); - - /* Clear the NOR flash control block. User extension is not cleared. */ - LX_MEMSET(nor_flash, 0, (ULONG)((UCHAR*)&(nor_flash -> lx_nor_flash_open_previous) - (UCHAR*)nor_flash) + sizeof(nor_flash -> lx_nor_flash_open_previous)); - - /* Call the flash driver's initialization function. */ - (nor_driver_initialize)(nor_flash); - -#ifndef LX_DIRECT_READ - - /* Determine if the driver supplied a RAM buffer for reading the NOR sector if direct read is not - supported. */ - if (nor_flash -> lx_nor_flash_sector_buffer == LX_NULL) - { - - /* Return an error. */ - return(LX_NO_MEMORY); - } -#endif - - /* Setup the offset to the free bit map. */ - nor_flash -> lx_nor_flash_block_free_bit_map_offset = sizeof(LX_NOR_FLASH_BLOCK_HEADER)/sizeof(ULONG); - - /* Calculate the number of bits we need in the free physical sector bit map. Subtract 1 to account for the - flash block header itself. The case where multiple physical sectors are needed for certain sized flash - devices is handled below. */ - sectors_per_block = (nor_flash -> lx_nor_flash_words_per_block / LX_NOR_SECTOR_SIZE) - 1; - - /* Calculate the number of words required for the sector map array. */ - sector_map_words = sectors_per_block; - - /* Calculate the number of words we need for the free physical sector bit map. */ - bit_map_words = (sectors_per_block + 31)/ 32; - - /* Save the number of bit map words. */ - nor_flash -> lx_nor_flash_block_bit_map_words = bit_map_words; - - /* Setup the offset (in words) to the array of physical sector mapping. */ - nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset = nor_flash -> lx_nor_flash_block_free_bit_map_offset + bit_map_words; - - /* Calculate the total number of words required for the flash block header. */ - total_header_words = sizeof(LX_NOR_FLASH_BLOCK_HEADER)/sizeof(ULONG) + bit_map_words + sector_map_words; - - /* Determine if more physical sectors are needed, which can happen on large devices. */ - if (total_header_words <= LX_NOR_SECTOR_SIZE) - { - - /* Round up to the size of 1 physical sector. */ - total_header_words = LX_NOR_SECTOR_SIZE; - } - else - { - - /* Otherwise calculate how many header sectors are necessary. */ - header_sectors = (total_header_words-1)/LX_NOR_SECTOR_SIZE; - - /* Round up to the next sector. */ - header_sectors++; - - /* Compute the total header words, rounding to the next sector. */ - total_header_words = header_sectors * LX_NOR_SECTOR_SIZE; - - /* Adjust the number of sectors per block. */ - sectors_per_block = sectors_per_block - (header_sectors - 1); - } - - /* Save the offset to the sector area. */ - nor_flash -> lx_nor_flash_block_physical_sector_offset = total_header_words; - - /* Save the physical sectors per block and total physical sectors. */ - nor_flash -> lx_nor_flash_physical_sectors_per_block = sectors_per_block; - nor_flash -> lx_nor_flash_total_physical_sectors = nor_flash -> lx_nor_flash_total_blocks * sectors_per_block; - - /* Build the free bit map mask, for the portion of the bit map that is less than 32 bits. */ - if ((sectors_per_block % 32) != 0) - { - bit_map_mask = (ULONG)(1 << (sectors_per_block % 32)); - bit_map_mask = bit_map_mask - 1; - } - else - { - - /* Exactly 32 sectors for the bit map mask. */ - bit_map_mask = LX_ALL_ONES; - } - - /* Save the free bit map mask in the control block. */ - nor_flash -> lx_nor_flash_block_bit_map_mask = bit_map_mask; - - /* Setup default values for the max/min erased counts. */ - min_erased_count = LX_ALL_ONES; - min_erased_blocks = 0; - max_erased_count = 0; - - /* Setup the block word pointer to the first word of the first block, which is effectively the - flash base address. */ - block_word_ptr = nor_flash -> lx_nor_flash_base_address; - - /* Loop through the blocks to determine the minimum and maximum erase count. */ - for (l = 0; l < nor_flash -> lx_nor_flash_total_blocks; l++) - { - - /* Pickup the first word of the block. If the flash manager has executed before, this word contains the - erase count for the block. Otherwise, if the word is 0xFFFFFFFF, this flash block was either erased - or this is the first time it was used. */ -#ifdef LX_DIRECT_READ - - /* Read the word directly. */ - block_word = *block_word_ptr; -#else - - - - status = _lx_nor_flash_driver_read(nor_flash, block_word_ptr, &block_word, 1); - - /* Check for an error from flash driver. Drivers should never return an error.. */ - if (status) - { - - /* Call system error handler. */ - _lx_nor_flash_system_error(nor_flash, status); - - /* Return an error. */ - return(LX_ERROR); - } -#endif - - /* Is the block erased? */ - if (((block_word & LX_BLOCK_ERASED) != LX_BLOCK_ERASED) && (block_word != LX_BLOCK_ERASE_STARTED)) - { - - /* No, valid block. Isolate the erased count. */ - erased_count = (block_word & LX_BLOCK_ERASE_COUNT_MASK); - - /* Is the erased count the minimum? */ - if (erased_count == min_erased_count) - { - - /* Yes, increment the minimum erased block count. */ - min_erased_blocks++; - } - - /* Is this the new minimum? */ - if (erased_count < min_erased_count) - { - - /* Yes, remember the new minimum. */ - min_erased_count = erased_count; - - /* Reset the minimum erased block count. */ - min_erased_blocks = 1; - } - - /* Is this the new maximum? */ - if (erased_count > max_erased_count) - { - - /* Yes, remember the new maximum. */ - max_erased_count = erased_count; - } - } - - /* Move to the next flash block. */ - block_word_ptr = block_word_ptr + (nor_flash -> lx_nor_flash_words_per_block); - } - - /* If we haven't found any erased counts, we can assume the flash is completely erased and needs to - be setup for the first time. */ - if (min_erased_count == LX_ALL_ONES) - { - - /* Indicate that this is the initial format. */ - nor_flash -> lx_nor_flash_diagnostic_initial_format = LX_TRUE; - - /* Setup the block word pointer to the first word of the first block, which is effectively the - flash base address. */ - block_word_ptr = nor_flash -> lx_nor_flash_base_address; - - /* Loop through the blocks to setup the flash the fist time. */ - for (l = 0; l < nor_flash -> lx_nor_flash_total_blocks; l++) - { - - /* Setup the free bit map that corresponds to the free physical sectors in this - block. Note that we only need to setup the portion of the free bit map that doesn't - have sectors associated with it. */ - status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr+(nor_flash -> lx_nor_flash_block_free_bit_map_offset + (bit_map_words-1)) , &bit_map_mask, 1); - - /* Check for an error from flash driver. Drivers should never return an error.. */ - if (status) - { - - /* Call system error handler. */ - _lx_nor_flash_system_error(nor_flash, status); - - /* Return an error. */ - return(LX_ERROR); - } - - /* Setup the initial erase count to 1. */ - block_word = ((ULONG) 1); - - /* Write the initial erase count for the block. */ - status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &block_word, 1); - - /* Check for an error from flash driver. Drivers should never return an error.. */ - if (status) - { - - /* Call system error handler. */ - _lx_nor_flash_system_error(nor_flash, status); - - /* Return an error. */ - return(LX_ERROR); - } - - /* Update the overall minimum and maximum erase count. */ - nor_flash -> lx_nor_flash_minimum_erase_count = 1; - nor_flash -> lx_nor_flash_minimum_erased_blocks = nor_flash -> lx_nor_flash_total_blocks; - nor_flash -> lx_nor_flash_maximum_erase_count = 1; - - /* Update the number of free physical sectors. */ - nor_flash -> lx_nor_flash_free_physical_sectors = nor_flash -> lx_nor_flash_free_physical_sectors + sectors_per_block; - - /* Move to the next flash block. */ - block_word_ptr = block_word_ptr + (nor_flash -> lx_nor_flash_words_per_block); - } - } - else - { - - /* At this point, we have a previously managed flash structure. This needs to be traversed to prepare for the - current flash operation. */ - - /* Default the flash free sector search to an invalid value. */ - nor_flash -> lx_nor_flash_free_block_search = nor_flash -> lx_nor_flash_total_blocks; - - /* Setup the block word pointer to the first word of the first block, which is effectively the - flash base address. */ - block_word_ptr = nor_flash -> lx_nor_flash_base_address; - - /* Loop through the blocks. */ - for (l = 0; l < nor_flash -> lx_nor_flash_total_blocks; l++) - { - - /* First, determine if this block has a valid erase count. */ -#ifdef LX_DIRECT_READ - - /* Read the word directly. */ - block_word = *block_word_ptr; -#else - status = _lx_nor_flash_driver_read(nor_flash, block_word_ptr, &block_word, 1); - - /* Check for an error from flash driver. Drivers should never return an error.. */ - if (status) - { - - /* Call system error handler. */ - _lx_nor_flash_system_error(nor_flash, status); - - /* Return an error. */ - return(LX_ERROR); - } -#endif - - /* Is the block erased? */ - if (((block_word & LX_BLOCK_ERASED) == LX_BLOCK_ERASED) || (block_word == LX_BLOCK_ERASE_STARTED)) - { - - /* This can happen if we were previously in the process of erasing the flash block and a - power interruption occurs. It should only occur once though. */ - - /* Is this the first time? */ - if (nor_flash -> lx_nor_flash_diagnostic_erased_block) - { - - /* No, this is a potential format error, since this should only happen once in a given - NOR flash format. */ - _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_BLOCK); - - /* Return an error. */ - return(LX_ERROR); - } - - /* Increment the erased block diagnostic. */ - nor_flash -> lx_nor_flash_diagnostic_erased_block++; - - /* Check to see if the block is erased. */ -#ifdef LX_NOR_ENABLE_CONTROL_BLOCK_FOR_DRIVER_INTERFACE - status = (nor_flash -> lx_nor_flash_driver_block_erased_verify)(nor_flash, l); -#else - status = (nor_flash -> lx_nor_flash_driver_block_erased_verify)(l); -#endif - - /* Is the block completely erased? */ - if (status != LX_SUCCESS) - { - - /* Is this the first time? */ - if (nor_flash -> lx_nor_flash_diagnostic_re_erase_block) - { - - /* No, this is a potential format error, since this should only happen once in a given - NOR flash format. */ - _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_BLOCK); - - /* Return an error. */ - return(LX_ERROR); - } - - /* Increment the erased block diagnostic. */ - nor_flash -> lx_nor_flash_diagnostic_re_erase_block++; - - /* No, the block is not fully erased, erase it again. */ - status = _lx_nor_flash_driver_block_erase(nor_flash, l, max_erased_count); - - /* Check for an error from flash driver. Drivers should never return an error.. */ - if (status) - { - - /* Call system error handler. */ - _lx_nor_flash_system_error(nor_flash, status); - - /* Return an error. */ - return(LX_ERROR); - } - } - - /* Setup the free bit map that corresponds to the free physical sectors in this - block. Note that we only need to setup the portion of the free bit map that doesn't - have sectors associated with it. */ - status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr+(nor_flash -> lx_nor_flash_block_free_bit_map_offset + (bit_map_words-1)) , &bit_map_mask, 1); - - /* Check for an error from flash driver. Drivers should never return an error.. */ - if (status) - { - - /* Call system error handler. */ - _lx_nor_flash_system_error(nor_flash, status); - - /* Return an error. */ - return(LX_ERROR); - } - - /* Write the initial erase count for the block with upper bit set. */ - temp_erased_count = (max_erased_count | LX_BLOCK_ERASED); - status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &temp_erased_count, 1); - - /* Check for an error from flash driver. Drivers should never return an error.. */ - if (status) - { - - /* Call system error handler. */ - _lx_nor_flash_system_error(nor_flash, status); - - /* Return an error. */ - return(LX_ERROR); - } - - /* Write the final initial erase count for the block. */ - status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &max_erased_count, 1); - - /* Check for an error from flash driver. Drivers should never return an error.. */ - if (status) - { - - /* Call system error handler. */ - _lx_nor_flash_system_error(nor_flash, status); - - /* Return an error. */ - return(LX_ERROR); - } - - /* Update the number of free physical sectors. */ - nor_flash -> lx_nor_flash_free_physical_sectors = nor_flash -> lx_nor_flash_free_physical_sectors + sectors_per_block; - } - else - { - - /* Calculate the number of free sectors from the free sector bit map. */ - free_sectors = 0; - for (j = 0; j < bit_map_words; j++) - { - - /* Read this word of the free sector bit map. */ -#ifdef LX_DIRECT_READ - - /* Read the word directly. */ - block_word = *(block_word_ptr + nor_flash -> lx_nor_flash_block_free_bit_map_offset + j); -#else - status = _lx_nor_flash_driver_read(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_free_bit_map_offset + j), &block_word, 1); - - /* Check for an error from flash driver. Drivers should never return an error.. */ - if (status) - { - - /* Call system error handler. */ - _lx_nor_flash_system_error(nor_flash, status); - - /* Return an error. */ - return(LX_ERROR); - } -#endif - - /* Count the number of set bits (free sectors). */ - for (k = 0; k < 32; k++) - { - - /* Is this sector free? */ - if (block_word & 1) - { - /* Yes, this sector is free, increment the free sectors count. */ - free_sectors++; - - /* Determine if we need to update the search pointer. */ - if (nor_flash -> lx_nor_flash_free_block_search == nor_flash -> lx_nor_flash_total_blocks) - { - - /* Remember the block with free sectors. */ - nor_flash -> lx_nor_flash_free_block_search = l; - } - } - - /* Shift down the free sector. */ - block_word = block_word >> 1; - } - } - - /* Update the number of free physical sectors. */ - nor_flash -> lx_nor_flash_free_physical_sectors = nor_flash -> lx_nor_flash_free_physical_sectors + free_sectors; - - /* We need to now examine the mapping list. */ - - /* Calculate how many non-free sectors there are - this includes valid and obsolete sectors. */ - used_sectors = sectors_per_block - free_sectors; - - /* Now walk the list of logical-physical sector mapping. */ - for (j = 0; j < sectors_per_block; j++) - { - - /* Read this word of the sector mapping list. */ -#ifdef LX_DIRECT_READ - - /* Read the word directly. */ - block_word = *(block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j); -#else - status = _lx_nor_flash_driver_read(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1); +UINT status = LX_ERROR; - /* Check for an error from flash driver. Drivers should never return an error.. */ - if (status) - { - - /* Call system error handler. */ - _lx_nor_flash_system_error(nor_flash, status); + status = _lx_nor_flash_open_extended(nor_flash, name, nor_driver_initialize, NULL); - /* Return an error. */ - return(LX_ERROR); - } -#endif - - /* Determine if we are expecting to find a used sector. */ - if (used_sectors) - { - - /* Yes, we expect this entry to be used. */ - - /* Is this sector in-use? */ - if ((block_word & LX_NOR_LOGICAL_SECTOR_MASK) != LX_NOR_LOGICAL_SECTOR_MASK) - { - - /* Determine if the valid bit is set and the superceded bit is clear. This indicates the block was - about to become obsolete. */ - if ((block_word & LX_NOR_PHYSICAL_SECTOR_VALID) && ((block_word & LX_NOR_PHYSICAL_SECTOR_SUPERCEDED) == 0)) - { - - - /* Increment the being obsoleted count. */ - nor_flash -> lx_nor_flash_diagnostic_sector_being_obsoleted++; - - /* Save the currently mapped physical sectors. */ - temp = nor_flash -> lx_nor_flash_mapped_physical_sectors; - - /* Indicate all the physical sectors are mapped for the purpose of this search. */ - nor_flash -> lx_nor_flash_mapped_physical_sectors = nor_flash -> lx_nor_flash_total_physical_sectors; - - /* Yes, this block was about to become obsolete. Perform a search for a logical sector entry that - has both of these bits set. */ - _lx_nor_flash_logical_sector_find(nor_flash, (block_word & LX_NOR_LOGICAL_SECTOR_MASK), LX_TRUE, &new_map_entry, &new_sector_address); - - /* Restore the number of mapped physical sectors. */ - nor_flash -> lx_nor_flash_mapped_physical_sectors = temp; - - /* Determine if the new logical sector entry is present. */ - if (new_map_entry) - { - - /* Yes, make the current entry obsolete in favor of the new entry. */ - block_word = block_word & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID); - status = _lx_nor_flash_driver_write(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1); - - /* Check for an error from flash driver. Drivers should never return an error.. */ - if (status) - { - - /* Call system error handler. */ - _lx_nor_flash_system_error(nor_flash, status); - - /* Return an error. */ - return(LX_ERROR); - } - - /* Is this the first time? */ - if (nor_flash -> lx_nor_flash_diagnostic_sector_obsoleted) - { - - /* No, this is a potential format error, since this should only happen once in a given - NOR flash format. */ - _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_FORMAT); - - /* Return an error. */ - return(LX_ERROR); - } - - /* Increment the obsoleted count. */ - nor_flash -> lx_nor_flash_diagnostic_sector_obsoleted++; - } - } - } - - /* Determine if the sector is free. */ - else if (block_word == LX_NOR_PHYSICAL_SECTOR_FREE) - { - - /* A free entry when there are still used sectors implies that the sector was allocated and a power interruption - took place prior to writing the new logical sector number into the list. */ - - /* Is this the first time? */ - if (nor_flash -> lx_nor_flash_diagnostic_mapping_invalidated) - { - - /* No, this is a potential format error, since this should only happen once in a given - NOR flash format. */ - _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_FORMAT); - - /* Return an error. */ - return(LX_ERROR); - } - - /* Write 0s out to this entry to invalidate the sector entry. */ - block_word = 0; - status = _lx_nor_flash_driver_write(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1); - - /* Check for an error from flash driver. Drivers should never return an error.. */ - if (status) - { - - /* Call system error handler. */ - _lx_nor_flash_system_error(nor_flash, status); - - /* Return an error. */ - return(LX_ERROR); - } - - /* Increment the number of mapping invalidates. */ - nor_flash -> lx_nor_flash_diagnostic_mapping_invalidated++; - } - - /* Yes, now determine if the sector is obsolete. */ - if ((block_word & LX_NOR_PHYSICAL_SECTOR_VALID) == 0) - { - - /* Increment the number of obsolete sectors. */ - nor_flash -> lx_nor_flash_obsolete_physical_sectors++; - } - - /* Determine if the mapping for this sector isn't yet valid. */ - else if (block_word & LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID) - { - - /* Yes, a power interruption or reset occurred while the sector mapping entry was being written. */ - - /* Increment the number of obsolete sectors. */ - nor_flash -> lx_nor_flash_obsolete_physical_sectors++; - - /* Increment the interrupted mapping counter. */ - nor_flash -> lx_nor_flash_diagnostic_mapping_write_interrupted++; - - /* Invalidate this entry - clearing valid bit, superceded bit and logical sector. */ - block_word = 0; - status = _lx_nor_flash_driver_write(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1); - - /* Check for an error from flash driver. Drivers should never return an error.. */ - if (status) - { - - /* Call system error handler. */ - _lx_nor_flash_system_error(nor_flash, status); - - /* Return an error. */ - return(LX_ERROR); - } - } - else - { - /* Increment the number of mapped physical sectors. */ - nor_flash -> lx_nor_flash_mapped_physical_sectors++; - } - - /* Decrease the number of used sectors. */ - used_sectors--; - } - else - { - - /* No more used sectors in this flash block. */ - - /* In this case the entry must be free or there is a serious NOR flash format error present. */ - if (block_word != LX_NOR_PHYSICAL_SECTOR_FREE) - { - - /* Increment the sector not free diagnostic. */ - nor_flash -> lx_nor_flash_diagnostic_sector_not_free++; - - /* NOR flash format. */ - _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_FORMAT); - - /* Write 0s out to this entry to invalidate the sector entry. */ - block_word = 0; - status = _lx_nor_flash_driver_write(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1); - - /* Check for an error from flash driver. Drivers should never return an error.. */ - if (status) - { - - /* Call system error handler. */ - _lx_nor_flash_system_error(nor_flash, status); - - /* Return an error. */ - return(LX_ERROR); - } - } - -#ifdef LX_FREE_SECTOR_DATA_VERIFY - - /* Pickup address of the free sector data area. */ - sector_word_ptr = block_word_ptr + (nor_flash -> lx_nor_flash_block_physical_sector_offset) + (j * LX_NOR_SECTOR_SIZE); - - /* Determine if the data for this sector is free. */ - for (k = 0; k < LX_NOR_SECTOR_SIZE; k++) - { - -#ifdef LX_DIRECT_READ - - /* Read the word directly. */ - sector_word = *(sector_word_ptr); -#else - status = _lx_nor_flash_driver_read(nor_flash, (sector_word_ptr), §or_word, 1); - - /* Check for an error from flash driver. Drivers should never return an error.. */ - if (status) - { - - /* Call system error handler. */ - _lx_nor_flash_system_error(nor_flash, status); - - /* Return an error. */ - return(LX_ERROR); - } -#endif - - /* Determine if this word is not available. */ - if (sector_word != LX_NOR_PHYSICAL_SECTOR_FREE) - { - - /* Increment the sector data not free diagnostic. */ - nor_flash -> lx_nor_flash_diagnostic_sector_data_not_free++; - - /* This is a format error. */ - _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_BLOCK); - - /* Return an error. */ - return(LX_ERROR); - } - - /* Move to the next word in the sector. */ - sector_word_ptr++; - } -#endif - } - } - } - - /* Move to the next flash block. */ - block_word_ptr = block_word_ptr + (nor_flash -> lx_nor_flash_words_per_block); - } - - /* Update the overall minimum and maximum erase count. */ - nor_flash -> lx_nor_flash_minimum_erase_count = min_erased_count; - nor_flash -> lx_nor_flash_minimum_erased_blocks = min_erased_blocks; - nor_flash -> lx_nor_flash_maximum_erase_count = max_erased_count; - - /* Determine if we need to update the free sector search pointer. */ - if (nor_flash -> lx_nor_flash_free_block_search == nor_flash -> lx_nor_flash_total_blocks) - { - - /* Just start at the beginning. */ - nor_flash -> lx_nor_flash_free_block_search = 0; - } - } - -#ifdef LX_THREAD_SAFE_ENABLE - - /* If the thread safe option is enabled, create a ThreadX mutex that will be used in all external APIs - in order to provide thread-safe operation. */ - status = tx_mutex_create(&nor_flash -> lx_nor_flash_mutex, "NOR Flash Mutex", TX_NO_INHERIT); - - /* Determine if the mutex creation encountered an error. */ - if (status != LX_SUCCESS) - { - - /* Call system error handler, since this should not happen. */ - _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_MUTEX_CREATE_FAILED); - - /* Return error to caller. */ - return(LX_ERROR); - } -#endif - - /* Enable the sector mapping cache. */ - nor_flash -> lx_nor_flash_sector_mapping_cache_enabled = LX_TRUE; - - /* Initialize the last found block and sector markers. */ - nor_flash -> lx_nor_flash_found_block_search = 0; - nor_flash -> lx_nor_flash_found_sector_search = 0; - - /* Lockout interrupts. */ - LX_DISABLE - - /* At this point, the NOR flash has been opened successfully. Place the - NOR flash control block on the linked list of currently opened NOR flashes. */ - - /* Set the NOR flash state to open. */ - nor_flash -> lx_nor_flash_state = LX_NOR_FLASH_OPENED; - - /* Place the NOR flash control block on the list of opened NOR flashes. First, - check for an empty list. */ - if (_lx_nor_flash_opened_count) - { - - /* List is not empty - other NOR flashes are open. */ - - /* Pickup tail pointer. */ - tail_ptr = _lx_nor_flash_opened_ptr -> lx_nor_flash_open_previous; - - /* Place the new NOR flash control block in the list. */ - _lx_nor_flash_opened_ptr -> lx_nor_flash_open_previous = nor_flash; - tail_ptr -> lx_nor_flash_open_next = nor_flash; - - /* Setup this NOR flash's opened links. */ - nor_flash -> lx_nor_flash_open_previous = tail_ptr; - nor_flash -> lx_nor_flash_open_next = _lx_nor_flash_opened_ptr; - } - else - { - - /* The opened NOR flash list is empty. Add the NOR flash to empty list. */ - _lx_nor_flash_opened_ptr = nor_flash; - nor_flash -> lx_nor_flash_open_next = nor_flash; - nor_flash -> lx_nor_flash_open_previous = nor_flash; - } - - /* Increment the opened NOR flash counter. */ - _lx_nor_flash_opened_count++; - - /* Restore interrupts. */ - LX_RESTORE - - /* Return a successful completion. */ - return(LX_SUCCESS); + return status; } - diff --git a/common/src/lx_nor_flash_open_extended.c b/common/src/lx_nor_flash_open_extended.c new file mode 100644 index 0000000..6b19d5d --- /dev/null +++ b/common/src/lx_nor_flash_open_extended.c @@ -0,0 +1,914 @@ +/*************************************************************************** + * Copyright (c) 2025 Microsoft Corporation + * Portion Copyright (c) 2025 STMicroelectronics + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR 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_nor_flash_open_extended PORTABLE C */ +/* 6.3.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function opens a NOR flash instance and ensures the NOR flash */ +/* is in a coherent state. */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* name Name of NOR flash instance */ +/* nor_driver_initialize Driver initialize */ +/* nor_driver_info_ptr Driver user data pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* (nor_driver_initialize) Driver initialize */ +/* _lx_nor_flash_driver_read Driver read */ +/* _lx_nor_flash_driver_write Driver write */ +/* (lx_nor_flash_driver_block_erased_verify) */ +/* NOR flash verify block erased */ +/* _lx_nor_flash_driver_block_erase Driver block erase */ +/* _lx_nor_flash_logical_sector_find Find logical sector */ +/* _lx_nor_flash_system_error System error handler */ +/* tx_mutex_create Create thread-safe mutex */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 11-09-2020 William E. Lamie Modified comment(s), */ +/* fixed compiler warnings, */ +/* resulting in version 6.1.2 */ +/* 12-30-2020 William E. Lamie Modified comment(s), */ +/* fixed compiler warnings, */ +/* resulting in version 6.1.3 */ +/* 06-02-2021 Bhupendra Naphade Modified comment(s), and */ +/* updated product constants */ +/* resulting in version 6.1.7 */ +/* 03-08-2023 Xiuwen Cai Modified comment(s), */ +/* added new driver interface, */ +/* resulting in version 6.2.1 */ +/* 10-31-2023 Xiuwen Cai Modified comment(s), */ +/* added count for minimum */ +/* erased blocks, added */ +/* obsolete count cache, */ +/* avoided clearing user */ +/* extension in flash control */ +/* block, */ +/* resulting in version 6.3.0 */ +/* */ +/**************************************************************************/ + +UINT _lx_nor_flash_open_extended(LX_NOR_FLASH *nor_flash, CHAR *name, UINT (*nor_driver_initialize)(LX_NOR_FLASH *), VOID *nor_driver_info_ptr) +{ +ULONG sectors_per_block; +ULONG sector_map_words; +ULONG bit_map_words; +ULONG bit_map_mask; +ULONG total_header_words; +ULONG header_sectors; +ULONG *block_word_ptr; +ULONG block_word; +ULONG temp; +ULONG free_sectors; +ULONG used_sectors; +ULONG *new_map_entry; +ULONG *new_sector_address; +ULONG erased_count, min_erased_count, max_erased_count, temp_erased_count, min_erased_blocks; +ULONG j, k, l; +UINT status; +#ifdef LX_FREE_SECTOR_DATA_VERIFY +ULONG *sector_word_ptr; +ULONG sector_word; +#endif +LX_NOR_FLASH *tail_ptr; +LX_INTERRUPT_SAVE_AREA + + LX_PARAMETER_NOT_USED(name); + + /* Clear the NOR flash control block. User extension is not cleared. */ + LX_MEMSET(nor_flash, 0, (ULONG)((UCHAR*)&(nor_flash -> lx_nor_flash_open_previous) - (UCHAR*)nor_flash) + sizeof(nor_flash -> lx_nor_flash_open_previous)); + + /* Pass the driver info ptr */ + nor_flash -> lx_nor_flash_driver_info_ptr = nor_driver_info_ptr; + + /* Call the flash driver's initialization function. */ + (nor_driver_initialize)(nor_flash); + +#ifndef LX_DIRECT_READ + + /* Determine if the driver supplied a RAM buffer for reading the NOR sector if direct read is not + supported. */ + if (nor_flash -> lx_nor_flash_sector_buffer == LX_NULL) + { + + /* Return an error. */ + return(LX_NO_MEMORY); + } +#endif + + /* Setup the offset to the free bit map. */ + nor_flash -> lx_nor_flash_block_free_bit_map_offset = sizeof(LX_NOR_FLASH_BLOCK_HEADER)/sizeof(ULONG); + + /* Calculate the number of bits we need in the free physical sector bit map. Subtract 1 to account for the + flash block header itself. The case where multiple physical sectors are needed for certain sized flash + devices is handled below. */ + sectors_per_block = (nor_flash -> lx_nor_flash_words_per_block / LX_NOR_SECTOR_SIZE) - 1; + + /* Calculate the number of words required for the sector map array. */ + sector_map_words = sectors_per_block; + + /* Calculate the number of words we need for the free physical sector bit map. */ + bit_map_words = (sectors_per_block + 31)/ 32; + + /* Save the number of bit map words. */ + nor_flash -> lx_nor_flash_block_bit_map_words = bit_map_words; + + /* Setup the offset (in words) to the array of physical sector mapping. */ + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset = nor_flash -> lx_nor_flash_block_free_bit_map_offset + bit_map_words; + + /* Calculate the total number of words required for the flash block header. */ + total_header_words = sizeof(LX_NOR_FLASH_BLOCK_HEADER)/sizeof(ULONG) + bit_map_words + sector_map_words; + + /* Determine if more physical sectors are needed, which can happen on large devices. */ + if (total_header_words <= LX_NOR_SECTOR_SIZE) + { + + /* Round up to the size of 1 physical sector. */ + total_header_words = LX_NOR_SECTOR_SIZE; + } + else + { + + /* Otherwise calculate how many header sectors are necessary. */ + header_sectors = (total_header_words-1)/LX_NOR_SECTOR_SIZE; + + /* Round up to the next sector. */ + header_sectors++; + + /* Compute the total header words, rounding to the next sector. */ + total_header_words = header_sectors * LX_NOR_SECTOR_SIZE; + + /* Adjust the number of sectors per block. */ + sectors_per_block = sectors_per_block - (header_sectors - 1); + } + + /* Save the offset to the sector area. */ + nor_flash -> lx_nor_flash_block_physical_sector_offset = total_header_words; + + /* Save the physical sectors per block and total physical sectors. */ + nor_flash -> lx_nor_flash_physical_sectors_per_block = sectors_per_block; + nor_flash -> lx_nor_flash_total_physical_sectors = nor_flash -> lx_nor_flash_total_blocks * sectors_per_block; + + /* Build the free bit map mask, for the portion of the bit map that is less than 32 bits. */ + if ((sectors_per_block % 32) != 0) + { + bit_map_mask = (ULONG)(1 << (sectors_per_block % 32)); + bit_map_mask = bit_map_mask - 1; + } + else + { + + /* Exactly 32 sectors for the bit map mask. */ + bit_map_mask = LX_ALL_ONES; + } + + /* Save the free bit map mask in the control block. */ + nor_flash -> lx_nor_flash_block_bit_map_mask = bit_map_mask; + + /* Setup default values for the max/min erased counts. */ + min_erased_count = LX_ALL_ONES; + min_erased_blocks = 0; + max_erased_count = 0; + + /* Setup the block word pointer to the first word of the first block, which is effectively the + flash base address. */ + block_word_ptr = nor_flash -> lx_nor_flash_base_address; + + /* Loop through the blocks to determine the minimum and maximum erase count. */ + for (l = 0; l < nor_flash -> lx_nor_flash_total_blocks; l++) + { + + /* Pickup the first word of the block. If the flash manager has executed before, this word contains the + erase count for the block. Otherwise, if the word is 0xFFFFFFFF, this flash block was either erased + or this is the first time it was used. */ +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + block_word = *block_word_ptr; +#else + + status = _lx_nor_flash_driver_read(nor_flash, block_word_ptr, &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } +#endif + + /* Is the block erased? */ + if (((block_word & LX_BLOCK_ERASED) != LX_BLOCK_ERASED) && (block_word != LX_BLOCK_ERASE_STARTED)) + { + + /* No, valid block. Isolate the erased count. */ + erased_count = (block_word & LX_BLOCK_ERASE_COUNT_MASK); + + /* Is the erased count the minimum? */ + if (erased_count == min_erased_count) + { + + /* Yes, increment the minimum erased block count. */ + min_erased_blocks++; + } + + /* Is this the new minimum? */ + if (erased_count < min_erased_count) + { + + /* Yes, remember the new minimum. */ + min_erased_count = erased_count; + + /* Reset the minimum erased block count. */ + min_erased_blocks = 1; + } + + /* Is this the new maximum? */ + if (erased_count > max_erased_count) + { + + /* Yes, remember the new maximum. */ + max_erased_count = erased_count; + } + } + + /* Move to the next flash block. */ + block_word_ptr = block_word_ptr + (nor_flash -> lx_nor_flash_words_per_block); + } + + /* If we haven't found any erased counts, we can assume the flash is completely erased and needs to + be setup for the first time. */ + if (min_erased_count == LX_ALL_ONES) + { + + /* Indicate that this is the initial format. */ + nor_flash -> lx_nor_flash_diagnostic_initial_format = LX_TRUE; + + /* Setup the block word pointer to the first word of the first block, which is effectively the + flash base address. */ + block_word_ptr = nor_flash -> lx_nor_flash_base_address; + + /* Loop through the blocks to setup the flash the fist time. */ + for (l = 0; l < nor_flash -> lx_nor_flash_total_blocks; l++) + { + + /* Setup the free bit map that corresponds to the free physical sectors in this + block. Note that we only need to setup the portion of the free bit map that doesn't + have sectors associated with it. */ + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr+(nor_flash -> lx_nor_flash_block_free_bit_map_offset + (bit_map_words-1)) , &bit_map_mask, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Setup the initial erase count to 1. */ + block_word = ((ULONG) 1); + + /* Write the initial erase count for the block. */ + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Update the overall minimum and maximum erase count. */ + nor_flash -> lx_nor_flash_minimum_erase_count = 1; + nor_flash -> lx_nor_flash_minimum_erased_blocks = nor_flash -> lx_nor_flash_total_blocks; + nor_flash -> lx_nor_flash_maximum_erase_count = 1; + + /* Update the number of free physical sectors. */ + nor_flash -> lx_nor_flash_free_physical_sectors = nor_flash -> lx_nor_flash_free_physical_sectors + sectors_per_block; + + /* Move to the next flash block. */ + block_word_ptr = block_word_ptr + (nor_flash -> lx_nor_flash_words_per_block); + } + } + else + { + + /* At this point, we have a previously managed flash structure. This needs to be traversed to prepare for the + current flash operation. */ + + /* Default the flash free sector search to an invalid value. */ + nor_flash -> lx_nor_flash_free_block_search = nor_flash -> lx_nor_flash_total_blocks; + + /* Setup the block word pointer to the first word of the first block, which is effectively the + flash base address. */ + block_word_ptr = nor_flash -> lx_nor_flash_base_address; + + /* Loop through the blocks. */ + for (l = 0; l < nor_flash -> lx_nor_flash_total_blocks; l++) + { + + /* First, determine if this block has a valid erase count. */ +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + block_word = *block_word_ptr; +#else + status = _lx_nor_flash_driver_read(nor_flash, block_word_ptr, &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } +#endif + + /* Is the block erased? */ + if (((block_word & LX_BLOCK_ERASED) == LX_BLOCK_ERASED) || (block_word == LX_BLOCK_ERASE_STARTED)) + { + + /* This can happen if we were previously in the process of erasing the flash block and a + power interruption occurs. It should only occur once though. */ + + /* Is this the first time? */ + if (nor_flash -> lx_nor_flash_diagnostic_erased_block) + { + + /* No, this is a potential format error, since this should only happen once in a given + NOR flash format. */ + _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_BLOCK); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Increment the erased block diagnostic. */ + nor_flash -> lx_nor_flash_diagnostic_erased_block++; + + /* Check to see if the block is erased. */ +#ifdef LX_NOR_ENABLE_CONTROL_BLOCK_FOR_DRIVER_INTERFACE + status = (nor_flash -> lx_nor_flash_driver_block_erased_verify)(nor_flash, l); +#else + status = (nor_flash -> lx_nor_flash_driver_block_erased_verify)(l); +#endif + + /* Is the block completely erased? */ + if (status != LX_SUCCESS) + { + + /* Is this the first time? */ + if (nor_flash -> lx_nor_flash_diagnostic_re_erase_block) + { + + /* No, this is a potential format error, since this should only happen once in a given + NOR flash format. */ + _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_BLOCK); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Increment the erased block diagnostic. */ + nor_flash -> lx_nor_flash_diagnostic_re_erase_block++; + + /* No, the block is not fully erased, erase it again. */ + status = _lx_nor_flash_driver_block_erase(nor_flash, l, max_erased_count); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + } + + /* Setup the free bit map that corresponds to the free physical sectors in this + block. Note that we only need to setup the portion of the free bit map that doesn't + have sectors associated with it. */ + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr+(nor_flash -> lx_nor_flash_block_free_bit_map_offset + (bit_map_words-1)) , &bit_map_mask, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Write the initial erase count for the block with upper bit set. */ + temp_erased_count = (max_erased_count | LX_BLOCK_ERASED); + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &temp_erased_count, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Write the final initial erase count for the block. */ + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &max_erased_count, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Update the number of free physical sectors. */ + nor_flash -> lx_nor_flash_free_physical_sectors = nor_flash -> lx_nor_flash_free_physical_sectors + sectors_per_block; + } + else + { + + /* Calculate the number of free sectors from the free sector bit map. */ + free_sectors = 0; + for (j = 0; j < bit_map_words; j++) + { + + /* Read this word of the free sector bit map. */ +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + block_word = *(block_word_ptr + nor_flash -> lx_nor_flash_block_free_bit_map_offset + j); +#else + status = _lx_nor_flash_driver_read(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_free_bit_map_offset + j), &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } +#endif + + /* Count the number of set bits (free sectors). */ + for (k = 0; k < 32; k++) + { + + /* Is this sector free? */ + if (block_word & 1) + { + /* Yes, this sector is free, increment the free sectors count. */ + free_sectors++; + + /* Determine if we need to update the search pointer. */ + if (nor_flash -> lx_nor_flash_free_block_search == nor_flash -> lx_nor_flash_total_blocks) + { + + /* Remember the block with free sectors. */ + nor_flash -> lx_nor_flash_free_block_search = l; + } + } + + /* Shift down the free sector. */ + block_word = block_word >> 1; + } + } + + /* Update the number of free physical sectors. */ + nor_flash -> lx_nor_flash_free_physical_sectors = nor_flash -> lx_nor_flash_free_physical_sectors + free_sectors; + + /* We need to now examine the mapping list. */ + + /* Calculate how many non-free sectors there are - this includes valid and obsolete sectors. */ + used_sectors = sectors_per_block - free_sectors; + + /* Now walk the list of logical-physical sector mapping. */ + for (j = 0; j < sectors_per_block; j++) + { + + /* Read this word of the sector mapping list. */ +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + block_word = *(block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j); +#else + status = _lx_nor_flash_driver_read(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } +#endif + + /* Determine if we are expecting to find a used sector. */ + if (used_sectors) + { + + /* Yes, we expect this entry to be used. */ + + /* Is this sector in-use? */ + if ((block_word & LX_NOR_LOGICAL_SECTOR_MASK) != LX_NOR_LOGICAL_SECTOR_MASK) + { + + /* Determine if the valid bit is set and the superceded bit is clear. This indicates the block was + about to become obsolete. */ + if ((block_word & LX_NOR_PHYSICAL_SECTOR_VALID) && ((block_word & LX_NOR_PHYSICAL_SECTOR_SUPERCEDED) == 0)) + { + + + /* Increment the being obsoleted count. */ + nor_flash -> lx_nor_flash_diagnostic_sector_being_obsoleted++; + + /* Save the currently mapped physical sectors. */ + temp = nor_flash -> lx_nor_flash_mapped_physical_sectors; + + /* Indicate all the physical sectors are mapped for the purpose of this search. */ + nor_flash -> lx_nor_flash_mapped_physical_sectors = nor_flash -> lx_nor_flash_total_physical_sectors; + + /* Yes, this block was about to become obsolete. Perform a search for a logical sector entry that + has both of these bits set. */ + _lx_nor_flash_logical_sector_find(nor_flash, (block_word & LX_NOR_LOGICAL_SECTOR_MASK), LX_TRUE, &new_map_entry, &new_sector_address); + + /* Restore the number of mapped physical sectors. */ + nor_flash -> lx_nor_flash_mapped_physical_sectors = temp; + + /* Determine if the new logical sector entry is present. */ + if (new_map_entry) + { + + /* Yes, make the current entry obsolete in favor of the new entry. */ + block_word = block_word & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID); + status = _lx_nor_flash_driver_write(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Is this the first time? */ + if (nor_flash -> lx_nor_flash_diagnostic_sector_obsoleted) + { + + /* No, this is a potential format error, since this should only happen once in a given + NOR flash format. */ + _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_FORMAT); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Increment the obsoleted count. */ + nor_flash -> lx_nor_flash_diagnostic_sector_obsoleted++; + } + } + } + + /* Determine if the sector is free. */ + else if (block_word == LX_NOR_PHYSICAL_SECTOR_FREE) + { + + /* A free entry when there are still used sectors implies that the sector was allocated and a power interruption + took place prior to writing the new logical sector number into the list. */ + + /* Is this the first time? */ + if (nor_flash -> lx_nor_flash_diagnostic_mapping_invalidated) + { + + /* No, this is a potential format error, since this should only happen once in a given + NOR flash format. */ + _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_FORMAT); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Write 0s out to this entry to invalidate the sector entry. */ + block_word = 0; + status = _lx_nor_flash_driver_write(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Increment the number of mapping invalidates. */ + nor_flash -> lx_nor_flash_diagnostic_mapping_invalidated++; + } + + /* Yes, now determine if the sector is obsolete. */ + if ((block_word & LX_NOR_PHYSICAL_SECTOR_VALID) == 0) + { + + /* Increment the number of obsolete sectors. */ + nor_flash -> lx_nor_flash_obsolete_physical_sectors++; + } + + /* Determine if the mapping for this sector isn't yet valid. */ + else if (block_word & LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID) + { + + /* Yes, a power interruption or reset occurred while the sector mapping entry was being written. */ + + /* Increment the number of obsolete sectors. */ + nor_flash -> lx_nor_flash_obsolete_physical_sectors++; + + /* Increment the interrupted mapping counter. */ + nor_flash -> lx_nor_flash_diagnostic_mapping_write_interrupted++; + + /* Invalidate this entry - clearing valid bit, superceded bit and logical sector. */ + block_word = 0; + status = _lx_nor_flash_driver_write(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + } + else + { + /* Increment the number of mapped physical sectors. */ + nor_flash -> lx_nor_flash_mapped_physical_sectors++; + } + + /* Decrease the number of used sectors. */ + used_sectors--; + } + else + { + + /* No more used sectors in this flash block. */ + + /* In this case the entry must be free or there is a serious NOR flash format error present. */ + if (block_word != LX_NOR_PHYSICAL_SECTOR_FREE) + { + + /* Increment the sector not free diagnostic. */ + nor_flash -> lx_nor_flash_diagnostic_sector_not_free++; + + /* NOR flash format. */ + _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_FORMAT); + + /* Write 0s out to this entry to invalidate the sector entry. */ + block_word = 0; + status = _lx_nor_flash_driver_write(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + } + +#ifdef LX_FREE_SECTOR_DATA_VERIFY + + /* Pickup address of the free sector data area. */ + sector_word_ptr = block_word_ptr + (nor_flash -> lx_nor_flash_block_physical_sector_offset) + (j * LX_NOR_SECTOR_SIZE); + + /* Determine if the data for this sector is free. */ + for (k = 0; k < LX_NOR_SECTOR_SIZE; k++) + { + +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + sector_word = *(sector_word_ptr); +#else + status = _lx_nor_flash_driver_read(nor_flash, (sector_word_ptr), §or_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } +#endif + + /* Determine if this word is not available. */ + if (sector_word != LX_NOR_PHYSICAL_SECTOR_FREE) + { + + /* Increment the sector data not free diagnostic. */ + nor_flash -> lx_nor_flash_diagnostic_sector_data_not_free++; + + /* This is a format error. */ + _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_BLOCK); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Move to the next word in the sector. */ + sector_word_ptr++; + } +#endif + } + } + } + + /* Move to the next flash block. */ + block_word_ptr = block_word_ptr + (nor_flash -> lx_nor_flash_words_per_block); + } + + /* Update the overall minimum and maximum erase count. */ + nor_flash -> lx_nor_flash_minimum_erase_count = min_erased_count; + nor_flash -> lx_nor_flash_minimum_erased_blocks = min_erased_blocks; + nor_flash -> lx_nor_flash_maximum_erase_count = max_erased_count; + + /* Determine if we need to update the free sector search pointer. */ + if (nor_flash -> lx_nor_flash_free_block_search == nor_flash -> lx_nor_flash_total_blocks) + { + + /* Just start at the beginning. */ + nor_flash -> lx_nor_flash_free_block_search = 0; + } + } + +#ifdef LX_THREAD_SAFE_ENABLE + + /* If the thread safe option is enabled, create a ThreadX mutex that will be used in all external APIs + in order to provide thread-safe operation. */ + status = tx_mutex_create(&nor_flash -> lx_nor_flash_mutex, "NOR Flash Mutex", TX_NO_INHERIT); + + /* Determine if the mutex creation encountered an error. */ + if (status != LX_SUCCESS) + { + + /* Call system error handler, since this should not happen. */ + _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_MUTEX_CREATE_FAILED); + + /* Return error to caller. */ + return(LX_ERROR); + } +#endif + + /* Enable the sector mapping cache. */ + nor_flash -> lx_nor_flash_sector_mapping_cache_enabled = LX_TRUE; + + /* Initialize the last found block and sector markers. */ + nor_flash -> lx_nor_flash_found_block_search = 0; + nor_flash -> lx_nor_flash_found_sector_search = 0; + + /* Lockout interrupts. */ + LX_DISABLE + + /* At this point, the NOR flash has been opened successfully. Place the + NOR flash control block on the linked list of currently opened NOR flashes. */ + + /* Set the NOR flash state to open. */ + nor_flash -> lx_nor_flash_state = LX_NOR_FLASH_OPENED; + + /* Place the NOR flash control block on the list of opened NOR flashes. First, + check for an empty list. */ + if (_lx_nor_flash_opened_count) + { + + /* List is not empty - other NOR flashes are open. */ + + /* Pickup tail pointer. */ + tail_ptr = _lx_nor_flash_opened_ptr -> lx_nor_flash_open_previous; + + /* Place the new NOR flash control block in the list. */ + _lx_nor_flash_opened_ptr -> lx_nor_flash_open_previous = nor_flash; + tail_ptr -> lx_nor_flash_open_next = nor_flash; + + /* Setup this NOR flash's opened links. */ + nor_flash -> lx_nor_flash_open_previous = tail_ptr; + nor_flash -> lx_nor_flash_open_next = _lx_nor_flash_opened_ptr; + } + else + { + + /* The opened NOR flash list is empty. Add the NOR flash to empty list. */ + _lx_nor_flash_opened_ptr = nor_flash; + nor_flash -> lx_nor_flash_open_next = nor_flash; + nor_flash -> lx_nor_flash_open_previous = nor_flash; + } + + /* Increment the opened NOR flash counter. */ + _lx_nor_flash_opened_count++; + + /* Restore interrupts. */ + LX_RESTORE + + /* Return a successful completion. */ + return(LX_SUCCESS); +} + diff --git a/test/regression/levelx_nor_flash_test.c b/test/regression/levelx_nor_flash_test.c index 6601263..60b17b7 100644 --- a/test/regression/levelx_nor_flash_test.c +++ b/test/regression/levelx_nor_flash_test.c @@ -21,7 +21,6 @@ ULONG readbuffer[128]; /* Define LevelX NOR flash simulator prototoypes. */ -UINT _lx_nor_flash_simulator_erase_all(VOID); UINT _lx_nor_flash_simulator_initialize(LX_NOR_FLASH *nor_flash); @@ -70,12 +69,13 @@ UINT status; ULONG *word_ptr; - /* Erase the simulated NOR flash. */ - _lx_nor_flash_simulator_erase_all(); + /* format the simulated NOR flash. */ /* Initialize LevelX. */ _lx_nor_flash_initialize(); + lx_nor_flash_format(&nor_sim_flash, "sim nor flash", _lx_nor_flash_simulator_initialize, NULL); + /* Test 1: Simple write 100 sectors and read 100 sectors. */ printf("Test 1: Simple write-read 100 sectors..........."); @@ -141,10 +141,10 @@ ULONG *word_ptr; printf("Test 2: Write same sector 120 times............."); /* Reinitialize... */ - _lx_nor_flash_simulator_erase_all(); - lx_nor_flash_initialize(); + lx_nor_flash_format(&nor_sim_flash, "sim nor flash", _lx_nor_flash_simulator_initialize, NULL); + lx_nor_flash_open(&nor_sim_flash, "sim nor flash", _lx_nor_flash_simulator_initialize); for (j = 0; j < 128; j++) @@ -1283,9 +1283,9 @@ ULONG *word_ptr; printf("Test 5: Randow write/read sector................"); - /* Erase the simulated NOR flash. */ - _lx_nor_flash_simulator_erase_all(); + /* format the simulated NOR flash. */ + lx_nor_flash_format(&nor_sim_flash, "sim nor flash", _lx_nor_flash_simulator_initialize, NULL); /* Open the flash. */ status = lx_nor_flash_open(&nor_sim_flash, "sim nor flash", _lx_nor_flash_simulator_initialize); diff --git a/test/regression/levelx_nor_flash_test_cache.c b/test/regression/levelx_nor_flash_test_cache.c index f0f111f..c06cd57 100644 --- a/test/regression/levelx_nor_flash_test_cache.c +++ b/test/regression/levelx_nor_flash_test_cache.c @@ -24,7 +24,6 @@ UCHAR nor_cache_memory2[8192]; /* Define LevelX NOR flash simulator prototoypes. */ -UINT _lx_nor_flash_simulator_erase_all(VOID); UINT _lx_nor_flash_simulator_initialize(LX_NOR_FLASH *nor_flash); @@ -74,15 +73,13 @@ UINT status; ULONG *word_ptr; - /* Erase the simulated NOR flash. */ - _lx_nor_flash_simulator_erase_all(); - /* Initialize LevelX. */ _lx_nor_flash_initialize(); /* Test 1: Simple write 100 sectors and read 100 sectors. */ printf("Test 1: Simple write-read 100 sectors..........."); + lx_nor_flash_format(&nor_sim_flash, "sim nor flash", _lx_nor_flash_simulator_initialize, NULL); lx_nor_flash_open(&nor_sim_flash, "sim nor flash", _lx_nor_flash_simulator_initialize); #ifndef LX_NOR_DISABLE_EXTENDED_CACHE lx_nor_flash_extended_cache_enable(&nor_sim_flash, nor_cache_memory, sizeof(nor_cache_memory)); @@ -267,10 +264,9 @@ lx_nor_flash_extended_cache_enable(&nor_sim_flash, nor_cache_memory, sizeof(nor_ printf("Test 2: Write same sector 120 times............."); /* Reinitialize... */ - _lx_nor_flash_simulator_erase_all(); - lx_nor_flash_initialize(); + lx_nor_flash_format(&nor_sim_flash, "sim nor flash", _lx_nor_flash_simulator_initialize, NULL); lx_nor_flash_open(&nor_sim_flash, "sim nor flash", _lx_nor_flash_simulator_initialize); #ifndef LX_NOR_DISABLE_EXTENDED_CACHE lx_nor_flash_extended_cache_enable(&nor_sim_flash, nor_cache_memory, sizeof(nor_cache_memory)); @@ -1430,10 +1426,11 @@ status += lx_nor_flash_extended_cache_enable(&nor_sim_flash, nor_cache_memory, s printf("Test 5: Randow write/read sector................"); - /* Erase the simulated NOR flash. */ - _lx_nor_flash_simulator_erase_all(); + /*format the simulated NOR flash. */ + /* Open the flash. */ + status = lx_nor_flash_format(&nor_sim_flash, "sim nor flash", _lx_nor_flash_simulator_initialize, NULL); status = lx_nor_flash_open(&nor_sim_flash, "sim nor flash", _lx_nor_flash_simulator_initialize); #ifndef LX_NOR_DISABLE_EXTENDED_CACHE status += lx_nor_flash_extended_cache_enable(&nor_sim_flash, nor_cache_memory, sizeof(nor_cache_memory)); @@ -1578,8 +1575,8 @@ status += lx_nor_flash_extended_cache_enable(&nor_sim_flash, nor_cache_memory, s printf("Test 6: Check lx_nor_flash_extended_cache_entries size................"); - /* Erase the simulated NOR flash. */ - _lx_nor_flash_simulator_erase_all(); + /* Format the simulated NOR flash. */ + status = lx_nor_flash_format(&nor_sim_flash, "sim nor flash", _lx_nor_flash_simulator_initialize, NULL); /* Open the flash. */ status = lx_nor_flash_open(&nor_sim_flash, "sim nor flash", _lx_nor_flash_simulator_initialize); From 4585b92f15440a311d53a9f46a9ca09b79b9036c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Tue, 25 Nov 2025 10:45:08 -0500 Subject: [PATCH 5/8] Update minimal CMake version in CMakeLists.txt to 3.13 --- test/cmake/samples/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cmake/samples/CMakeLists.txt b/test/cmake/samples/CMakeLists.txt index 8ee02a1..5d8e41d 100644 --- a/test/cmake/samples/CMakeLists.txt +++ b/test/cmake/samples/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR) +cmake_minimum_required(VERSION 3.13 FATAL_ERROR) cmake_policy(SET CMP0057 NEW) project(samples LANGUAGES C) From 6a534bed34a9917ac6ae1da4abe789a4bed8c3f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Tue, 25 Nov 2025 10:48:08 -0500 Subject: [PATCH 6/8] Update minimal CMake version in CMakeLists.txt to 3.13 --- test/cmake/regression/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cmake/regression/CMakeLists.txt b/test/cmake/regression/CMakeLists.txt index b11e4ad..f92c71a 100644 --- a/test/cmake/regression/CMakeLists.txt +++ b/test/cmake/regression/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR) +cmake_minimum_required(VERSION 3.13 FATAL_ERROR) cmake_policy(SET CMP0057 NEW) project(regression_test LANGUAGES C) From 9bf1c2424371b0686c3590cdc4bae481d78f7c2c Mon Sep 17 00:00:00 2001 From: Haithem Rahmani Date: Thu, 18 Dec 2025 11:05:36 +0100 Subject: [PATCH 7/8] fix: force initial erase count to 1 for each erased block write the word (1 | LX_NOR_ERASED) at the first word. this is actually equivalent to 0x80000001. When the lx_nor_flash_open_extended() is called, only the erase count will remain valid. Remove the LX_DISABLE/LX_RESTORE sequence. --- common/src/lx_nor_flash_format.c | 57 +++----------------------------- 1 file changed, 5 insertions(+), 52 deletions(-) diff --git a/common/src/lx_nor_flash_format.c b/common/src/lx_nor_flash_format.c index 393c6ae..f33969e 100644 --- a/common/src/lx_nor_flash_format.c +++ b/common/src/lx_nor_flash_format.c @@ -77,13 +77,8 @@ UINT _lx_nor_flash_format(LX_NOR_FLASH *nor_flash, CHAR *name, UINT (*nor_driv ULONG l; UINT status; -ULONG *block_word_ptr; -ULONG bit_map_words; -ULONG sectors_per_block; -ULONG bit_map_mask; ULONG block_word; - - LX_INTERRUPT_SAVE_AREA +ULONG *block_word_ptr; LX_PARAMETER_NOT_USED(name); @@ -106,14 +101,8 @@ ULONG block_word; } #endif - nor_flash -> lx_nor_flash_block_free_bit_map_offset = sizeof(LX_NOR_FLASH_BLOCK_HEADER)/sizeof(ULONG); - - LX_DISABLE for (l = 0; l < nor_flash -> lx_nor_flash_total_blocks; l++) { - /* Setup the initial erase count to 0, assuming that the block is already erased */ - block_word = ((ULONG) 0); - #ifdef LX_NOR_ENABLE_CONTROL_BLOCK_FOR_DRIVER_INTERFACE /* Check that the block is already erased */ status = (nor_flash -> lx_nor_flash_driver_block_erased_verify)(nor_flash, l); @@ -133,7 +122,6 @@ ULONG block_word; /* Call system error handler. */ _lx_nor_flash_system_error(nor_flash, status); - LX_RESTORE /* Return an error. */ return(LX_ERROR); } @@ -150,49 +138,18 @@ ULONG block_word; /* Call system error handler. */ _lx_nor_flash_system_error(nor_flash, status); - LX_RESTORE /* Return an error. */ return(LX_ERROR); } - else - { - /* the block has been erased, thus incrememt its erase_count */ - block_word = ((ULONG) 1); - } } /* Setup the block word pointer to the first word of the first block */ block_word_ptr = nor_flash -> lx_nor_flash_base_address + l * nor_flash -> lx_nor_flash_words_per_block; - /* Calculate the number of bits we need in the free physical sector bit map */ - sectors_per_block = (nor_flash -> lx_nor_flash_words_per_block / LX_NOR_SECTOR_SIZE) - 1; - - /* Calculate the number of words we need for the free physical sector bit map. */ - bit_map_words = (sectors_per_block + 31)/ 32; - - if ((sectors_per_block % 32) != 0) - { - bit_map_mask = (ULONG)(1 << (sectors_per_block % 32)); - bit_map_mask = bit_map_mask - 1; - } - else - { - - /* Exactly 32 sectors for the bit map mask. */ - bit_map_mask = LX_ALL_ONES; - } - status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr + (nor_flash -> lx_nor_flash_block_free_bit_map_offset + (bit_map_words-1)) , &bit_map_mask, 1); - if (status) - { - /* Call system error handler. */ - _lx_nor_flash_system_error(nor_flash, status); - - LX_RESTORE - /* Return an error. */ - return(LX_ERROR); - } - - block_word = (block_word | LX_BLOCK_ERASED); + /* Setup the initial erase_count to 1 and mark the block as erased. + * When the lx_nor_flash_open() gets called only the erase count will be kept. + */ + block_word = ((ULONG)1 | LX_BLOCK_ERASED); status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &block_word, 1); if (status) @@ -200,15 +157,11 @@ ULONG block_word; /* Call system error handler. */ _lx_nor_flash_system_error(nor_flash, status); - LX_RESTORE /* Return an error. */ return(LX_ERROR); } } - /* Restore interrupts. */ - LX_RESTORE - return(LX_SUCCESS); } From c27eb52bd24ada82f552e0ab7a526203b8aed7b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Mon, 12 Jan 2026 18:01:37 -0500 Subject: [PATCH 8/8] Updated version number constants --- common/inc/lx_api.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/inc/lx_api.h b/common/inc/lx_api.h index 05dab15..21b1950 100644 --- a/common/inc/lx_api.h +++ b/common/inc/lx_api.h @@ -204,7 +204,9 @@ typedef unsigned long long ULONG64; #define AZURE_RTOS_LEVELX #define LEVELX_MAJOR_VERSION 6 #define LEVELX_MINOR_VERSION 4 -#define LEVELX_PATCH_VERSION 1 +#define LEVELX_PATCH_VERSION 5 +#define LEVELX_BUILD_VERSION 202504 +#define LEVELX_HOTFIX_VERSION ' ' /* Define general LevelX Constants. */