Initial commit

This commit is contained in:
PProvost
2020-05-11 08:49:43 -06:00
commit 6f4ea0561f
65 changed files with 14775 additions and 0 deletions
+972
View File
@@ -0,0 +1,972 @@
/**************************************************************************/
/* */
/* Copyright (c) Microsoft Corporation. All rights reserved. */
/* */
/* This software is licensed under the Microsoft Software License */
/* Terms for Microsoft Azure RTOS. Full text of the license can be */
/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
/* and in the root directory of this software. */
/* */
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/** */
/** LevelX Component */
/** */
/** NAND Flash */
/** */
/**************************************************************************/
/**************************************************************************/
#define LX_SOURCE_CODE
/* Disable ThreadX error checking. */
#ifndef TX_DISABLE_ERROR_CHECKING
#define TX_DISABLE_ERROR_CHECKING
#endif
/* Include necessary system files. */
#include "lx_api.h"
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _lx_nand_flash_open PORTABLE C */
/* 6.0 */
/* AUTHOR */
/* */
/* William E. Lamie, 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 */
/* */
/* OUTPUT */
/* */
/* return status */
/* */
/* CALLS */
/* */
/* (nand_driver_initialize) Driver initialize */
/* _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_driver_extra_bytes_get Driver get extra bytes */
/* _lx_nand_flash_driver_extra_bytes_set Driver set extra bytes */
/* _lx_nand_flash_driver_read Driver page read */
/* _lx_nand_flash_driver_write Driver page write */
/* _lx_nand_flash_driver_block_erased_verify */
/* Driver verify block erased */
/* _lx_nand_flash_driver_block_erase Driver block erase */
/* _lx_nand_flash_driver_page_erased_verify */
/* Driver verify page erased */
/* _lx_nand_flash_block_full_update Write mapping to page 0 */
/* _lx_nand_flash_logical_sector_find Find logical sector */
/* _lx_nand_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 */
/* */
/**************************************************************************/
UINT _lx_nand_flash_open(LX_NAND_FLASH *nand_flash, CHAR *name, UINT (*nand_driver_initialize)(LX_NAND_FLASH *))
{
LX_NAND_PAGE_EXTRA_INFO extra_info;
ULONG block;
ULONG page;
ULONG found_block, found_page;
UCHAR block_status;
ULONG *page_word_ptr;
ULONG temp;
ULONG free_pages;
ULONG obsolete_pages;
ULONG mapped_pages;
ULONG max_mapped_sector;
ULONG erased_count, min_erased_count, max_erased_count;
UINT status;
LX_NAND_FLASH *tail_ptr;
#ifdef LX_NAND_FLASH_DIRECT_MAPPING_CACHE
ULONG logical_sector;
#endif
TX_INTERRUPT_SAVE_AREA
LX_PARAMETER_NOT_USED(name);
/* Clear the NAND flash control block. */
TX_MEMSET(nand_flash, 0, sizeof(LX_NAND_FLASH));
/* 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 * sizeof(ULONG)) + 8) > nand_flash -> lx_nand_flash_bytes_per_page)
{
/* Not enough room in page 0 to contain the erase count and logical sector mapping list. */
/* Return error. */
return(LX_ERROR);
}
/* Determine if the driver supplied a RAM buffer for reading the page. */
if (nand_flash -> lx_nand_flash_page_buffer == LX_NULL)
{
/* Return an error. */
return(LX_NO_MEMORY);
}
/* 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;
/* Setup default values for the max/min erased counts. */
min_erased_count = LX_ALL_ONES;
max_erased_count = 0;
/* Default the max mapped sector to 0. */
max_mapped_sector = 0;
/* Setup the block word pointer to the internal buffer area. */
page_word_ptr = nand_flash -> lx_nand_flash_page_buffer;
/* Initialize the last found block and page search markers. */
nand_flash -> lx_nand_flash_found_block_search = 0;
nand_flash -> lx_nand_flash_found_page_search = 1;
/* 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++;
/* Continue to the next block. */
continue;
}
/* Now read page 0 of the block, which has the erase count in the first 4 bytes. */
status = _lx_nand_flash_driver_read(nand_flash, block, 0, page_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1));
/* 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);
}
}
/* Is the block erased? */
if (((page_word_ptr[0] & LX_BLOCK_ERASED) != LX_BLOCK_ERASED) && (page_word_ptr[0] != LX_BLOCK_ERASE_STARTED))
{
/* No, valid block. Isolate the erased count. */
erased_count = (page_word_ptr[0] & LX_BLOCK_ERASE_COUNT_MASK);
/* Is this the new minimum? */
if (erased_count < min_erased_count)
{
/* Yes, remember the new minimum. */
min_erased_count = erased_count;
}
/* Is this the new maximum? */
if (erased_count > max_erased_count)
{
/* Yes, remember the new maximum. */
max_erased_count = erased_count;
}
}
}
/* 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. */
nand_flash -> lx_nand_flash_diagnostic_initial_format = LX_TRUE;
/* Loop through the blocks to setup the flash the first time. */
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++;
/* Continue to the next block. */
continue;
}
/* Setup the initial erase count to 1. */
page_word_ptr[0] = (((ULONG) 1) | LX_BLOCK_ERASED);
/* Write the initial erase count for the block. */
status = _lx_nand_flash_driver_write(nand_flash, block, 0, page_word_ptr, LX_NAND_ERASE_COUNT_WRITE_SIZE);
/* Check for status after the block erase. */
if (status == LX_SUCCESS)
{
/* Setup the initial erase count to 1. */
page_word_ptr[0] = ((ULONG) 1);
/* Write the initial erase count for the block. */
status = _lx_nand_flash_driver_write(nand_flash, block, 0, page_word_ptr, LX_NAND_ERASE_COUNT_WRITE_SIZE);
}
/* 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);
/* Return an error. */
return(LX_ERROR);
}
/* Increment the bad block count. */
nand_flash -> lx_nand_flash_bad_blocks++;
}
else
{
/* Update the number of free pages. Subtract 1, since page 0 is used for the erase count and list of mapped pages. */
nand_flash -> lx_nand_flash_free_pages = nand_flash -> lx_nand_flash_free_pages +
nand_flash -> lx_nand_flash_pages_per_block - 1;
}
}
/* Update the overall minimum and maximum erase count. */
nand_flash -> lx_nand_flash_minimum_erase_count = 1;
nand_flash -> lx_nand_flash_maximum_erase_count = 1;
}
else
{
/* At this point, we have a previously managed flash structure. This needs to be traversed to prepare for the
current flash operation. */
/* Setup the maximum mapped sector value so all searches are valid. */
nand_flash -> lx_nand_flash_max_mapped_sector = 0xFFFFFFFF;
/* Default the flash block search to an invalid value. */
nand_flash -> lx_nand_flash_free_block_search = nand_flash -> lx_nand_flash_total_blocks;
/* Loop through the blocks. */
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++;
/* Continue to the next block. */
continue;
}
/* Now read page 0 of the block, which has the erase count in the first 4 bytes. */
status = _lx_nand_flash_driver_read(nand_flash, block, 0, page_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1));
/* 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);
}
}
/* Is the block erased? */
if (((page_word_ptr[0] & LX_BLOCK_ERASED) == LX_BLOCK_ERASED) || (page_word_ptr[0] == 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 (nand_flash -> lx_nand_flash_diagnostic_erased_block)
{
/* No, this is a potential format error, since this should only happen once in a given
NAND flash format. */
_lx_nand_flash_system_error(nand_flash, LX_SYSTEM_INVALID_BLOCK, block, 0);
}
/* Increment the erased block count. */
nand_flash -> lx_nand_flash_diagnostic_erased_block++;
/* Check to see if the block is erased. */
status = _lx_nand_flash_driver_block_erased_verify(nand_flash, block);
/* Is the block completely erased? */
if (status != LX_SUCCESS)
{
/* Is this the first time? */
if (nand_flash -> lx_nand_flash_diagnostic_re_erase_block)
{
/* No, this is a potential format error, since this should only happen once in a given
NAND flash format. */
_lx_nand_flash_system_error(nand_flash, LX_SYSTEM_INVALID_BLOCK, block, 0);
}
/* Increment the erased block diagnostic. */
nand_flash -> lx_nand_flash_diagnostic_re_erase_block++;
/* No, the block is not fully erased, erase it again. */
status = _lx_nand_flash_driver_block_erase(nand_flash, block, max_erased_count);
/* 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);
/* Return an error. */
return(LX_ERROR);
}
/* Increment the bad block count. */
nand_flash -> lx_nand_flash_bad_blocks++;
/* Continue with next block. */
continue;
}
}
/* Now read page 0 of the block, which is all ones at this point. */
status = _lx_nand_flash_driver_read(nand_flash, block, 0, page_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1));
/* 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);
/* Return an error. */
return(LX_ERROR);
}
/* Increment the bad block count. */
nand_flash -> lx_nand_flash_bad_blocks++;
/* Continue with next block. */
continue;
}
/* Write the initial erase count for the block. */
page_word_ptr[0] = (max_erased_count | LX_BLOCK_ERASED);
status = _lx_nand_flash_driver_write(nand_flash, block, 0, page_word_ptr, LX_NAND_ERASE_COUNT_WRITE_SIZE);
/* Determine if the write was successful. */
if (status == LX_SUCCESS)
{
/* Write the initial erase count for the block. */
page_word_ptr[0] = max_erased_count;
status = _lx_nand_flash_driver_write(nand_flash, block, 0, page_word_ptr, LX_NAND_ERASE_COUNT_WRITE_SIZE);
}
/* 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);
/* Return an error. */
return(LX_ERROR);
}
/* Increment the bad block count. */
nand_flash -> lx_nand_flash_bad_blocks++;
/* Continue with next block. */
continue;
}
/* Update the number of free pages. */
nand_flash -> lx_nand_flash_free_pages = nand_flash -> lx_nand_flash_free_pages +
nand_flash -> lx_nand_flash_pages_per_block - 1;
}
else
{
/* Check for a format error, an interruption while writing the mapped sector list. */
if ((page_word_ptr[1] != LX_NAND_PAGE_FREE) &&
(page_word_ptr[nand_flash -> lx_nand_flash_pages_per_block] != LX_NAND_PAGE_LIST_VALID))
{
/* Increment the invalid mapping counter. */
nand_flash -> lx_nand_flash_diagnostic_mapping_invalid++;
}
/* Calculate the number of free pages by traversing the pages in the block. */
free_pages = 0;
obsolete_pages = 0;
mapped_pages = 0;
for (page = 1; page < nand_flash -> lx_nand_flash_pages_per_block; page++)
{
/* Read the logical sector mapping for this page. */
status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, block, page, (UCHAR *) &extra_info, sizeof(extra_info));
/* 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);
}
/* Determine if the page is free. */
if (extra_info.lx_nand_page_extra_info_logical_sector == LX_NAND_PAGE_FREE)
{
/* The page is free and we know that the pages are allocated sequentially
so there is no need to go further. */
/* Increment the free pages counter. */
free_pages = free_pages + (nand_flash -> lx_nand_flash_pages_per_block - page);
/* Verify that the same position in the mapped list from page 0 is also free. */
if (page_word_ptr[page] != LX_NAND_PAGE_FREE)
{
/* Increment the invalid mapping counter. */
nand_flash -> lx_nand_flash_diagnostic_mapping_invalid++;
/* Call system error handler. */
_lx_nand_flash_system_error(nand_flash, LX_SYSTEM_INVALID_FORMAT, block, page);
}
/* Determine if the associated page is really free... it could be the case that the
page data area was in the process of being written or just completed when a power
interruption occurs before the extra bytes are setup with the logical sector. */
/* Read verify the page is erased. */
status = _lx_nand_flash_driver_page_erased_verify(nand_flash, block, page);
/* Check for an error from flash driver. */
if (status == LX_ERROR)
{
/* Increment the page data not free diagnostic counter. */
nand_flash -> lx_nand_flash_diagnostic_page_data_not_free++;
/* Decrement the free page counter and increment the obsolete page counter. */
free_pages--;
obsolete_pages++;
/* Now setup the extra info to show this page is obsolete. */
extra_info.lx_nand_page_extra_info_logical_sector = 0;
status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, block, page, (UCHAR *) &extra_info, sizeof(extra_info));
/* Check for an error from flash driver. */
if (status)
{
/* Call system error handler. */
_lx_nand_flash_system_error(nand_flash, status, block, page);
/* Return an error. */
return(LX_ERROR);
}
}
/* At this point we can break out of the page traversal loop, since nothing else can exist after this page. */
break;
}
else
{
/* Determine if the mapping list is not present and the value doesn't match the
logical sector value. */
if ((page_word_ptr[nand_flash -> lx_nand_flash_pages_per_block] == LX_NAND_PAGE_LIST_VALID) &&
((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK) !=
(page_word_ptr[page] & LX_NAND_LOGICAL_SECTOR_MASK)))
{
/* Increment the invalid mapping counter. */
nand_flash -> lx_nand_flash_diagnostic_mapping_invalid++;
/* Call system error handler. */
_lx_nand_flash_system_error(nand_flash, LX_SYSTEM_INVALID_FORMAT, block, page);
}
/* Determine if this mapped page is obsolete. */
if ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK) ==
extra_info.lx_nand_page_extra_info_logical_sector)
{
/* Yes, page mapping is obsolete. Increment the obsolete count. */
obsolete_pages++;
/* Continue looking at next page. */
continue;
}
/* Determine if the mapping for this page isn't yet valid. */
if (extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_PAGE_MAPPING_NOT_VALID)
{
/* Yes, a power interruption or reset occurred while the sector mapping entry was being written. */
/* Increment the obsolete count. */
obsolete_pages++;
/* Increment the interrupted mapping counter. */
nand_flash -> lx_nand_flash_diagnostic_mapping_write_interrupted++;
/* Invalidate this entry - clearing valid bit, superceded bit and logical sector. */
extra_info.lx_nand_page_extra_info_logical_sector = 0;
/* Write it out the page extra information. */
status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, block, page, (UCHAR *) &extra_info, sizeof(extra_info));
/* Check for an error from flash driver. */
if (status)
{
/* Call system error handler. */
_lx_nand_flash_system_error(nand_flash, status, block, page);
/* Return an error. */
return(LX_ERROR);
}
/* Continue looking at next page. */
continue;
}
/* Determine if this page is still mapped. */
if ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_PAGE_VALID) &&
(extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_PAGE_SUPERCEDED))
{
/* Yes, sector is mapped. Increment the mapped count. */
mapped_pages++;
/* Determine if this is a new maximum mapped sector. */
if ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK) > max_mapped_sector)
{
/* Remember the maximum mapped logical sector. */
max_mapped_sector = (extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK);
}
#ifdef LX_NAND_FLASH_DIRECT_MAPPING_CACHE
/* Logical sector is mapped, setup the direct cache. */
logical_sector = extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK;
/* Determine if this logical sector fits in the logical sector direct cache mapping. */
if (logical_sector < LX_NAND_SECTOR_MAPPING_CACHE_SIZE)
{
/* Remember the mapping for this logical sector. */
nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_block = (USHORT) block;
nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page = (USHORT) page;
}
#endif
/* Continue looking at next page. */
continue;
}
/* At this point, the page was in the process of being superceded. */
/* Determine if this is a new maximum mapped sector. */
if ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK) > max_mapped_sector)
{
/* Remember the maximum mapped logical sector. */
max_mapped_sector = (extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK);
}
/* Increment the being obsoleted count. */
nand_flash -> lx_nand_flash_diagnostic_page_being_obsoleted++;
/* Save the currently mapped pages. */
temp = nand_flash -> lx_nand_flash_mapped_pages;
/* Indicate all the pages are mapped for the purpose of this search. */
nand_flash -> lx_nand_flash_mapped_pages = nand_flash -> lx_nand_flash_total_pages;
/* Yes, this block was about to become obsolete. Perform a search for a logical sector entry that
has both of these bits set. */
_lx_nand_flash_logical_sector_find(nand_flash, (extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK),
LX_TRUE, &found_block, &found_page);
/* Read page 0 data again. */
status = _lx_nand_flash_driver_read(nand_flash, block, 0, page_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1));
/* 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);
}
}
/* Restore the number of mapped pages. */
nand_flash -> lx_nand_flash_mapped_pages = temp;
/* Determine if the new logical sector entry is present. */
if (found_page)
{
/* Yes, make the current entry obsolete in favor of the new entry. */
/* Increment the obsolete count. */
obsolete_pages++;
/* Clear the valid bit. */
extra_info.lx_nand_page_extra_info_logical_sector = extra_info.lx_nand_page_extra_info_logical_sector & ~((ULONG) LX_NAND_PAGE_VALID);
/* Write it out the page extra information. */
status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, block, page, (UCHAR *) &extra_info, sizeof(extra_info));
/* Check for an error from flash driver. */
if (status)
{
/* Call system error handler. */
_lx_nand_flash_system_error(nand_flash, status, block, page);
/* Return an error. */
return(LX_ERROR);
}
/* Is this the first time? */
if (nand_flash -> lx_nand_flash_diagnostic_page_obsoleted)
{
/* No, this is a potential format error, since this should only happen once in a given
NAND flash format. */
_lx_nand_flash_system_error(nand_flash, LX_SYSTEM_INVALID_SECTOR_MAP, block, page);
}
/* Increment the obsoleted count. */
nand_flash -> lx_nand_flash_diagnostic_page_obsoleted++;
#ifdef LX_NAND_FLASH_DIRECT_MAPPING_CACHE
/* Logical sector is mapped, setup the direct cache. */
logical_sector = extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK;
/* Determine if this logical sector fits in the logical sector direct cache mapping. */
if (logical_sector < LX_NAND_SECTOR_MAPPING_CACHE_SIZE)
{
/* Remember the mapping for this logical sector. */
nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_block = (USHORT) found_block;
nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page = (USHORT) found_page;
}
#endif
}
else
{
/* A new page was not found. Increment the number of mapped pages. */
mapped_pages++;
#ifdef LX_NAND_FLASH_DIRECT_MAPPING_CACHE
/* Logical sector is mapped, setup the direct cache. */
logical_sector = extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK;
/* Determine if this logical sector fits in the logical sector direct cache mapping. */
if (logical_sector < LX_NAND_SECTOR_MAPPING_CACHE_SIZE)
{
/* Remember the mapping for this logical sector. */
nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_block = (USHORT) block;
nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page = (USHORT) page;
}
#endif
}
}
}
/* Update the number of free, obsolete, and mapped pages. */
nand_flash -> lx_nand_flash_free_pages = nand_flash -> lx_nand_flash_free_pages + free_pages;
nand_flash -> lx_nand_flash_mapped_pages = nand_flash -> lx_nand_flash_mapped_pages + mapped_pages;
nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages + obsolete_pages;
/* Determine if the mapping list needs to be written to the flash. */
if ((free_pages == 0) && (page_word_ptr[1] == LX_ALL_ONES))
{
/* Yes, the mapping list should be written to page 0. */
_lx_nand_flash_block_full_update(nand_flash, block, page_word_ptr[0]);
/* Read page 0 data again. */
status = _lx_nand_flash_driver_read(nand_flash, block, 0, page_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1));
/* 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);
}
}
}
}
}
/* Update the overall minimum and maximum erase count. */
nand_flash -> lx_nand_flash_minimum_erase_count = min_erased_count;
nand_flash -> lx_nand_flash_maximum_erase_count = max_erased_count;
/* Determine if we need to update the free sector search pointer. */
if (nand_flash -> lx_nand_flash_free_block_search == nand_flash -> lx_nand_flash_total_blocks)
{
/* Just start at the beginning. */
nand_flash -> lx_nand_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(&nand_flash -> lx_nand_flash_mutex, "NAND Flash Mutex", TX_NO_INHERIT);
/* Determine if the mutex creation encountered an error. */
if (status != TX_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
/* Enable the sector mapping cache. */
nand_flash -> lx_nand_flash_sector_mapping_cache_enabled = LX_TRUE;
/* Initialize the last found block and page search markers. */
nand_flash -> lx_nand_flash_found_block_search = 0;
nand_flash -> lx_nand_flash_found_page_search = 1;
/* Remember the maximum mapped sector. */
nand_flash -> lx_nand_flash_max_mapped_sector = max_mapped_sector;
/* Lockout interrupts. */
TX_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. */
TX_RESTORE
/* Return a successful completion. */
return(LX_SUCCESS);
}