Files
levelx/common/src/lx_nand_flash_physical_page_allocate.c
T
2021-06-02 06:46:59 +00:00

397 lines
17 KiB
C

/**************************************************************************/
/* */
/* Copyright (c) Microsoft Corporation. All rights reserved. */
/* */
/* This software is licensed under the Microsoft Software License */
/* Terms for Microsoft Azure RTOS. Full text of the license can be */
/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
/* and in the root directory of this software. */
/* */
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/** */
/** LevelX Component */
/** */
/** NAND Flash */
/** */
/**************************************************************************/
/**************************************************************************/
#define LX_SOURCE_CODE
/* Disable ThreadX error checking. */
#ifndef LX_DISABLE_ERROR_CHECKING
#define LX_DISABLE_ERROR_CHECKING
#endif
/* Include necessary system files. */
#include "lx_api.h"
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _lx_nand_flash_physical_page_allocate PORTABLE C */
/* 6.1.7 */
/* AUTHOR */
/* */
/* William E. Lamie, Microsoft Corporation */
/* */
/* DESCRIPTION */
/* */
/* This function allocates a free page for mapping to a */
/* logical sector. */
/* */
/* INPUT */
/* */
/* nand_flash NAND flash instance */
/* block Destination for block */
/* page Destination for page */
/* erase_count Destination for erase count */
/* */
/* OUTPUT */
/* */
/* return status */
/* */
/* CALLS */
/* */
/* _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_block_status_get */
/* Driver block status get */
/* _lx_nand_flash_driver_page_erased_verify */
/* Driver verify page erased */
/* _lx_nand_flash_driver_read Driver page read */
/* _lx_nand_flash_block_full_update Update page 0 with list of */
/* mapped pages */
/* _lx_nand_flash_system_error Internal system error handler */
/* */
/* CALLED BY */
/* */
/* Internal LevelX */
/* */
/* 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 */
/* 06-02-2021 Bhupendra Naphade Modified comment(s), */
/* resulting in version 6.1.7 */
/* */
/**************************************************************************/
UINT _lx_nand_flash_physical_page_allocate(LX_NAND_FLASH *nand_flash, ULONG *block, ULONG *page, ULONG *erase_count)
{
LX_NAND_PAGE_EXTRA_INFO extra_info;
UCHAR block_status;
ULONG search_block;
ULONG search_page;
ULONG *word_ptr;
ULONG i, j;
UINT status;
/* Increment the number of page allocation requests. */
nand_flash -> lx_nand_flash_diagnostic_page_allocates++;
/* Initialize the return parameters. */
*block = (ULONG) 0;
*page = (ULONG) 0;
*erase_count = nand_flash -> lx_nand_flash_minimum_erase_count;
/* Determine if there are any free pages. */
if (nand_flash -> lx_nand_flash_free_pages == 0)
{
/* Increment the number of failed allocations. */
nand_flash -> lx_nand_flash_diagnostic_page_allocate_errors++;
/* No free pages, return . */
return(LX_NO_PAGES);
}
/* Pickup the search for a free page at the specified block. */
search_block = nand_flash -> lx_nand_flash_free_block_search;
/* Loop through the blocks to find a free page. */
for (i = 0; i < nand_flash -> lx_nand_flash_total_blocks; i++)
{
/* First, check to see if this block is good. */
status = _lx_nand_flash_driver_block_status_get(nand_flash, search_block, &block_status);
/* Check for an error from flash driver. */
if (status)
{
/* Call system error handler. */
_lx_nand_flash_system_error(nand_flash, status, search_block, 0);
/* Return the error. */
return(status);
}
/* Determine if this block is bad. */
if (block_status != LX_NAND_GOOD_BLOCK)
{
/* Move to next search block. */
search_block++;
/* Have we wrapped around yet? */
if (search_block >= nand_flash -> lx_nand_flash_total_blocks)
{
/* Reset the search block to the beginning. */
search_block = 0;
}
/* Block is bad, move to the next block. */
continue;
}
/* Read the extra bytes of page 0. This will tell us if the page has any free entries. */
status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, search_block, 0, (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, search_block, 0);
/* Return the error... no point in continuing. */
return(status);
}
/* Determine if there are any free entries. */
if ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_BLOCK_FULL) == 0)
{
/* This block is full... go to the next block. */
search_block++;
/* Have we wrapped around yet? */
if (search_block >= nand_flash -> lx_nand_flash_total_blocks)
{
/* Reset the search block to the beginning. */
search_block = 0;
}
/* Block is bad, move to the next block. */
continue;
}
/* Now check to see if page 0 has been written to. When the block is full, page 0 is written with the mapping of pages
to logical sectors in this block - only when the block becomes full. If the block has additional pages free, page 0 is all 0xFFs. */
status = _lx_nand_flash_driver_read(nand_flash, search_block, 0, nand_flash -> lx_nand_flash_page_buffer, nand_flash -> lx_nand_flash_words_per_page);
/* Check for an error... an error here indicates the block does not have any free pages. */
if (status)
{
/* Call system error handler. */
_lx_nand_flash_system_error(nand_flash, status, search_block, 0);
/* Determine if the error is fatal. */
if (status != LX_NAND_ERROR_CORRECTED)
{
/* Return the error. */
return(status);
}
}
/* Setup pointer to the page 0 data. */
word_ptr = nand_flash -> lx_nand_flash_page_buffer;
/* Pickup the block erase count (first word in the page 0 data area). */
if (*word_ptr != LX_ALL_ONES)
*erase_count = *word_ptr;
/* Now determine if the remaining words are all ones, i.e. haven't been written to. */
j = 1;
while (j < (nand_flash -> lx_nand_flash_pages_per_block + 1))
{
/* Has this word been written to? */
if (word_ptr[j] != LX_ALL_ONES)
break;
/* Move to next word. */
j++;
}
/* Does this block have any free pages? */
if (j < (nand_flash -> lx_nand_flash_pages_per_block + 1))
{
/* Move to next search block. */
search_block++;
/* Have we wrapped around yet? */
if (search_block >= nand_flash -> lx_nand_flash_total_blocks)
{
/* Reset the search block to the beginning. */
search_block = 0;
}
/* No, the data area has been written to. Simply move to the next block. */
continue;
}
/* Now search for an available page within the block. */
search_page = 1;
while (search_page < nand_flash -> lx_nand_flash_pages_per_block)
{
/* Read the extra information for this page to see if there is a sector mapping. */
status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, search_block, search_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, search_block, search_page);
/* Return the error. */
return(status);
}
/* Now determine if this page is mapped or free. */
if (extra_info.lx_nand_page_extra_info_logical_sector == LX_NAND_PAGE_FREE)
{
/* Now make sure the data area of this page is free. */
status = _lx_nand_flash_driver_page_erased_verify(nand_flash, search_block, search_page);
/* Is the page free? */
if (status != LX_SUCCESS)
{
/* No, the page is not actually free. For now, simply mark the extra info area to indicate
the page is not available and continue our search. */
extra_info.lx_nand_page_extra_info_logical_sector = 0;
/* Decrease the number of free pages. */
nand_flash -> lx_nand_flash_free_pages--;
/* Increase the number of obsolete pages. */
nand_flash -> lx_nand_flash_obsolete_pages++;
/* Write out the extra info for this page. */
status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, search_block, search_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, search_block, search_page);
/* Return the error. */
return(status);
}
/* Determine if this is the last entry of the block. */
if (search_page == (nand_flash -> lx_nand_flash_pages_per_block - 1))
{
/* Yes, we need to update page 0 of the block with the list of mapped
pages for this block. */
_lx_nand_flash_block_full_update(nand_flash, search_block, *erase_count);
}
}
else
{
/* Yes, we have found a free page in this block. Get out of the page search loop. */
break;
}
}
/* Move to next search page. */
search_page++;
}
/* Determine if we have found the free block. */
if (search_page < nand_flash -> lx_nand_flash_pages_per_block)
{
/* Remember the block to search. */
nand_flash -> lx_nand_flash_free_block_search = search_block;
/* Determine if the allocated page is the last one in this block. */
if (search_page == (nand_flash -> lx_nand_flash_pages_per_block - 1))
{
/* Yes, move to the next block. */
nand_flash -> lx_nand_flash_free_block_search++;
/* Have we wrapped around yet? */
if (nand_flash -> lx_nand_flash_free_block_search >= nand_flash -> lx_nand_flash_total_blocks)
{
/* Wrap condition - reset the search block to the beginning. */
nand_flash -> lx_nand_flash_free_block_search = 0;
}
}
/* Prepare the return information. */
*block = search_block;
*page = search_page;
/* Determine if this is the first data page of the block. */
if (search_page == 1)
{
/* Yes, we need to update the extra bytes of page 0 to indicate the block is no longer empty. */
extra_info.lx_nand_page_extra_info_logical_sector = ((ULONG) LX_NAND_PAGE_FREE) & ~((ULONG) LX_NAND_BLOCK_EMPTY);
status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, search_block, 0, (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, search_block, 0);
}
}
/* Return success! */
return(LX_SUCCESS);
}
/* Move to next search block. */
search_block++;
/* Have we wrapped around yet? */
if (search_block >= nand_flash -> lx_nand_flash_total_blocks)
{
/* Reset the search block to the beginning. */
search_block = 0;
}
}
/* Increment the number of failed allocations. */
nand_flash -> lx_nand_flash_diagnostic_page_allocate_errors++;
/* Return error. */
return(LX_NO_PAGES);
}