mirror of
https://github.com/eclipse-threadx/levelx.git
synced 2026-09-14 04:06:00 +08:00
Merge pull request #60 from rahmanih/add_new_level_apis_pr
New APIs: - lx_nor_flash_format(): setup the NOR Flash for a new and fresh filesystem. - lx_nor_flash_open_extended(): adds a user data pointer that could be passed to the underlying low-level NOR driver. - lx_nand_flash_open_extended(): adds a user data pointer that could be passed to the underlying low-level NAND driver. Updated APIs: - lx_nor_flash_open(): call the lx_nor_flash_open_extended() with a NULL as user data pointer. - lx_nand_flash_open(): call the lx_nand_flash_open_extended() with a NULL as user data pointer.
This commit is contained in:
@@ -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
|
||||
|
||||
+19
-4
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
UINT status;
|
||||
|
||||
LX_PARAMETER_NOT_USED(name);
|
||||
status = _lx_nand_flash_format_extended(nand_flash, name, nand_driver_initialize, NULL, memory_ptr, memory_size);
|
||||
|
||||
/* Clear the NAND flash control block. User extension is not cleared. */
|
||||
LX_MEMSET(nand_flash, 0, (ULONG)((UCHAR*)&(nand_flash -> lx_nand_flash_open_previous) - (UCHAR*)nand_flash) + sizeof(nand_flash -> lx_nand_flash_open_previous));
|
||||
|
||||
/* Call the flash driver's initialization function. */
|
||||
(nand_driver_initialize)(nand_flash);
|
||||
|
||||
/* Determine if we can support this NAND flash size. */
|
||||
if (nand_flash -> lx_nand_flash_pages_per_block > LX_NAND_MAX_PAGE_PER_BLOCK || nand_flash -> lx_nand_flash_total_blocks > LX_NAND_MAX_BLOCK_COUNT)
|
||||
{
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Check if it is new LevelX NAND driver. */
|
||||
if (nand_flash -> lx_nand_flash_driver_pages_read == LX_NULL || nand_flash -> lx_nand_flash_driver_pages_write == LX_NULL || nand_flash -> lx_nand_flash_driver_pages_copy == LX_NULL)
|
||||
{
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Check the spare data length. */
|
||||
if (nand_flash -> lx_nand_flash_spare_data1_length < sizeof(ULONG))
|
||||
{
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Calculate the number of words per block and per page. */
|
||||
nand_flash -> lx_nand_flash_words_per_page = (nand_flash -> lx_nand_flash_bytes_per_page / sizeof(ULONG));
|
||||
nand_flash -> lx_nand_flash_words_per_block = (nand_flash -> lx_nand_flash_words_per_page * nand_flash -> lx_nand_flash_pages_per_block);
|
||||
|
||||
/* Calculate the total pages. */
|
||||
nand_flash -> lx_nand_flash_total_pages = nand_flash -> lx_nand_flash_total_blocks * nand_flash -> lx_nand_flash_pages_per_block;
|
||||
|
||||
/* Initialize memory buffer. */
|
||||
status = _lx_nand_flash_memory_initialize(nand_flash, memory_ptr, memory_size);
|
||||
if (status != LX_SUCCESS)
|
||||
{
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Initialize block numbers. */
|
||||
nand_flash -> lx_nand_flash_metadata_block_number = LX_NAND_BLOCK_UNMAPPED;
|
||||
nand_flash -> lx_nand_flash_metadata_block_number_next = LX_NAND_BLOCK_UNMAPPED;
|
||||
nand_flash -> lx_nand_flash_backup_metadata_block_number = LX_NAND_BLOCK_UNMAPPED;
|
||||
nand_flash -> lx_nand_flash_backup_metadata_block_number_next = LX_NAND_BLOCK_UNMAPPED;
|
||||
|
||||
/* Initialize the block status buffer. */
|
||||
LX_MEMSET(nand_flash -> lx_nand_flash_block_status_table, 0xFF, nand_flash -> lx_nand_flash_block_status_table_size);
|
||||
|
||||
/* Loop through the blocks to check for bad blocks and determine the minimum and maximum erase count for each good block. */
|
||||
for (block = 0; block < nand_flash -> lx_nand_flash_total_blocks; block++)
|
||||
{
|
||||
|
||||
/* First, check to make sure this block is good. */
|
||||
status = _lx_nand_flash_driver_block_status_get(nand_flash, block, &block_status);
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, block, 0);
|
||||
|
||||
/* Return an error. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Is this block bad? */
|
||||
if (block_status != LX_NAND_GOOD_BLOCK)
|
||||
{
|
||||
|
||||
/* Yes, this block is bad. */
|
||||
|
||||
/* Increment the number of bad blocks. */
|
||||
nand_flash -> lx_nand_flash_bad_blocks++;
|
||||
|
||||
/* Save the block status. */
|
||||
nand_flash -> lx_nand_flash_block_status_table[block] = LX_NAND_BLOCK_STATUS_BAD;
|
||||
|
||||
/* Continue to the next block. */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Erase the block. */
|
||||
status = _lx_nand_flash_driver_block_erase(nand_flash, block, 0);
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, block, 0);
|
||||
|
||||
/* Attempt to mark this block as bad. */
|
||||
status = _lx_nand_flash_driver_block_status_set(nand_flash, block, LX_NAND_BAD_BLOCK);
|
||||
|
||||
/* Check for error in setting the block status. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, block, 0);
|
||||
}
|
||||
|
||||
/* Increment the bad block count. */
|
||||
nand_flash -> lx_nand_flash_bad_blocks++;
|
||||
|
||||
/* Save the block status. */
|
||||
nand_flash -> lx_nand_flash_block_status_table[block] = LX_NAND_BLOCK_STATUS_BAD;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Allocate blocks for metadata. */
|
||||
if (nand_flash -> lx_nand_flash_metadata_block_number == LX_NAND_BLOCK_UNMAPPED)
|
||||
{
|
||||
nand_flash -> lx_nand_flash_metadata_block_number = block;
|
||||
nand_flash -> lx_nand_flash_metadata_block_number_current = block;
|
||||
}
|
||||
else if (nand_flash -> lx_nand_flash_backup_metadata_block_number == LX_NAND_BLOCK_UNMAPPED)
|
||||
{
|
||||
nand_flash -> lx_nand_flash_backup_metadata_block_number = block;
|
||||
nand_flash -> lx_nand_flash_backup_metadata_block_number_current = block;
|
||||
}
|
||||
else if (nand_flash -> lx_nand_flash_metadata_block_number_next == LX_NAND_BLOCK_UNMAPPED)
|
||||
{
|
||||
nand_flash -> lx_nand_flash_metadata_block_number_next = block;
|
||||
}
|
||||
else if (nand_flash -> lx_nand_flash_backup_metadata_block_number_next == LX_NAND_BLOCK_UNMAPPED)
|
||||
{
|
||||
nand_flash -> lx_nand_flash_backup_metadata_block_number_next = block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* There should be enough blocks for metadata. */
|
||||
if (nand_flash -> lx_nand_flash_backup_metadata_block_number_next == LX_NAND_BLOCK_UNMAPPED)
|
||||
{
|
||||
return (LX_NO_BLOCKS);
|
||||
}
|
||||
|
||||
/* Save the block status for metadata. */
|
||||
nand_flash -> lx_nand_flash_block_status_table[nand_flash -> lx_nand_flash_backup_metadata_block_number_next] = LX_NAND_BLOCK_STATUS_ALLOCATED;
|
||||
nand_flash -> lx_nand_flash_block_status_table[nand_flash -> lx_nand_flash_metadata_block_number_next] = LX_NAND_BLOCK_STATUS_ALLOCATED;
|
||||
nand_flash -> lx_nand_flash_block_status_table[nand_flash -> lx_nand_flash_backup_metadata_block_number] = (USHORT)nand_flash -> lx_nand_flash_backup_metadata_block_number_next;
|
||||
nand_flash -> lx_nand_flash_block_status_table[nand_flash -> lx_nand_flash_metadata_block_number] = (USHORT)nand_flash -> lx_nand_flash_metadata_block_number_next;
|
||||
|
||||
/* Initialize the mapping table. */
|
||||
LX_MEMSET(nand_flash -> lx_nand_flash_block_mapping_table, 0xFF, nand_flash -> lx_nand_flash_block_mapping_table_size);
|
||||
|
||||
/* Build initial metadata. */
|
||||
status = _lx_nand_flash_metadata_build(nand_flash);
|
||||
if (status != LX_SUCCESS)
|
||||
{
|
||||
/* Return error status. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Get buffer for page data. */
|
||||
page_buffer_ptr = nand_flash -> lx_nand_flash_page_buffer;
|
||||
|
||||
/* Initialize the page buffer. */
|
||||
LX_MEMSET(page_buffer_ptr, 0xFF, nand_flash -> lx_nand_flash_bytes_per_page);
|
||||
|
||||
/* Set the next block numbers. */
|
||||
LX_UTILITY_LONG_SET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_MAIN_METADATA_OFFSET], nand_flash -> lx_nand_flash_metadata_block_number_next);
|
||||
LX_UTILITY_LONG_SET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_BACKUP_METADATA_OFFSET], nand_flash -> lx_nand_flash_backup_metadata_block_number_next);
|
||||
|
||||
/* Save the next block numbers to metadata block. */
|
||||
status = _lx_nand_flash_metadata_write(nand_flash, page_buffer_ptr, LX_NAND_PAGE_TYPE_BLOCK_LINK);
|
||||
|
||||
if (status != LX_SUCCESS)
|
||||
{
|
||||
/* Return error status. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Return a successful completion. */
|
||||
return(LX_SUCCESS);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
+67
-871
@@ -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
|
||||
UINT status = LX_ERROR;
|
||||
|
||||
LX_PARAMETER_NOT_USED(name);
|
||||
status = _lx_nor_flash_open_extended(nor_flash, name, nor_driver_initialize, NULL);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user