mirror of
https://github.com/eclipse-threadx/levelx.git
synced 2026-09-14 03:59:44 +08:00
Initial commit
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
.git* export-ignore
|
||||
.hooks* export-ignore
|
||||
|
||||
# Custom attribute to mark sources as using our C code style.
|
||||
[attr]our-c-style whitespace=tab-in-indent eol=lf format.clang-format-6.0
|
||||
|
||||
# Custom attribute to mark sources as generated.
|
||||
# Do not perform whitespace checks. Do not format.
|
||||
[attr]generated whitespace=-tab-in-indent,-indent-with-non-tab -format.clang-format-6.0
|
||||
|
||||
bootstrap eol=lf
|
||||
configure eol=lf
|
||||
*.[1-9] eol=lf
|
||||
*.bash eol=lf
|
||||
*.sh eol=lf
|
||||
*.sh.in eol=lf
|
||||
|
||||
*.bat eol=crlf
|
||||
*.bat.in eol=crlf
|
||||
*.sln eol=crlf
|
||||
*.vcproj eol=crlf
|
||||
|
||||
*.pfx -text
|
||||
*.png -text
|
||||
*.png.in -text
|
||||
|
||||
*.c our-c-style
|
||||
*.cc our-c-style
|
||||
*.cpp our-c-style
|
||||
*.cu our-c-style
|
||||
*.cxx our-c-style
|
||||
*.h our-c-style
|
||||
*.hh our-c-style
|
||||
*.hpp our-c-style
|
||||
*.hxx our-c-style
|
||||
*.notcu our-c-style
|
||||
|
||||
*.cmake whitespace=tab-in-indent
|
||||
*.rst whitespace=tab-in-indent conflict-marker-size=79
|
||||
*.txt whitespace=tab-in-indent
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
.vscode/
|
||||
_deps/
|
||||
build/
|
||||
CMakeFiles/
|
||||
CMakeScripts/
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
Testing
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)
|
||||
|
||||
# Set up the project
|
||||
project(levelx
|
||||
VERSION 6.0.0
|
||||
LANGUAGES C ASM
|
||||
)
|
||||
|
||||
if(NOT DEFINED THREADX_ARCH)
|
||||
message(FATAL_ERROR "Error: THREADX_ARCH not defined")
|
||||
endif()
|
||||
if(NOT DEFINED THREADX_TOOLCHAIN)
|
||||
message(FATAL_ERROR "Error: THREADX_TOOLCHAIN not defined")
|
||||
endif()
|
||||
|
||||
|
||||
# Define our target library and an alias for consumers
|
||||
add_library(${PROJECT_NAME})
|
||||
add_library("azrtos::${PROJECT_NAME}" ALIAS ${PROJECT_NAME})
|
||||
|
||||
# Define any required dependencies between this library and others
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC
|
||||
"azrtos::threadx"
|
||||
)
|
||||
|
||||
# Pick up the common stuff
|
||||
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/common)
|
||||
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
MICROSOFT SOFTWARE LICENSE TERMS
|
||||
|
||||
MICROSOFT AZURE RTOS
|
||||
|
||||
Shape
|
||||
|
||||
These license terms are an agreement between you and Microsoft Corporation (or
|
||||
one of its affiliates). They apply to the software named above and any Microsoft
|
||||
services or software updates (except to the extent such services or updates are
|
||||
accompanied by new or additional terms, in which case those different terms
|
||||
apply prospectively and do not alter your or Microsoft’s rights relating to
|
||||
pre-updated software or services). IF YOU COMPLY WITH THESE LICENSE TERMS, YOU
|
||||
HAVE THE RIGHTS BELOW. BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS.
|
||||
|
||||
INSTALLATION AND USE RIGHTS.
|
||||
|
||||
General. You may install and use the software and the included Microsoft
|
||||
applications solely for internal development, testing and evaluation purposes.
|
||||
Any distribution or production use requires a separate license as set forth in
|
||||
Section 2.
|
||||
|
||||
Contributions. Microsoft welcomes contributions to this software. In the event
|
||||
that you make a contribution to this software you will be required to agree to a
|
||||
Contributor License Agreement (CLA) declaring that you have the right to, and
|
||||
actually do, grant Microsoft the rights to use your contribution. For details,
|
||||
visit https://cla.microsoft.com.
|
||||
|
||||
Included Microsoft Applications. The software includes other Microsoft
|
||||
applications which are governed by the licenses embedded in or made available
|
||||
with those applications.
|
||||
|
||||
Third Party Components. The software may include third party components with
|
||||
separate legal notices or governed by other agreements, as may be described
|
||||
within the software or in the ThirdPartyNotices file(s) accompanying the
|
||||
software.
|
||||
|
||||
Competitive Benchmarking. If you are a direct competitor, and you access or use
|
||||
the software for purposes of competitive benchmarking, analysis, or intelligence
|
||||
gathering, you waive as against Microsoft, its subsidiaries, and its affiliated
|
||||
companies (including prospectively) any competitive use, access, and
|
||||
benchmarking test restrictions in the terms governing your software to the
|
||||
extent your terms of use are, or purport to be, more restrictive than
|
||||
Microsoft’s terms. If you do not waive any such purported restrictions in the
|
||||
terms governing your software, you are not allowed to access or use this
|
||||
software, and will not do so.
|
||||
|
||||
DISTRIBUTION AND PRODUCTION USE. If you have obtained and/or are developing on
|
||||
microprocessor(s) and/or microcontroller(s) (“hardware”) listed in the file
|
||||
named “LICENSED-HARDWARE.txt” included in the repository and/or distributed with
|
||||
the software you have the following rights in and to the software solely when
|
||||
used in combination with the hardware. In the event hardware is not listed in
|
||||
the LICENSED-HARDWARE.txt file, you do not have the rights in this Section 2.
|
||||
|
||||
Distribution and Production Use Rights.
|
||||
|
||||
You may use the software in production (e.g. program the modified or unmodified
|
||||
software to devices you own or control) and distribute (i.e. make available to
|
||||
third parties) the modified or unmodified binary image produced from this code.
|
||||
|
||||
|
||||
You may permit your device distributors or developers to copy and distribute the
|
||||
binary image as programmed or to be programmed to your devices.
|
||||
|
||||
You may redistribute the unmodified or modified source to your device
|
||||
distributors or developers. Modifications must be clearly marked. Any
|
||||
redistribution in source code form must contain this license and any other
|
||||
licenses that accompany the software.
|
||||
|
||||
Requirements. For any code you distribute, you must:
|
||||
|
||||
when distributed in binary form, except as embedded in a device, include with
|
||||
such distribution the terms of this agreement;
|
||||
|
||||
when distributed in source code form to distributors or developers of your
|
||||
devices, include with such distribution the terms of this agreement; and
|
||||
|
||||
indemnify, defend and hold harmless Microsoft from any claims, including
|
||||
attorneys’ fees, related to the distribution or use of your devices, except to
|
||||
the extent that any claim is based solely on the unmodified software.
|
||||
|
||||
Restrictions. You may not:
|
||||
|
||||
use or modify the software to create a competing real time operating system
|
||||
software;
|
||||
|
||||
remove any copyright notices or licenses contained in the software;
|
||||
|
||||
use Microsoft’s trademarks or trade dress in your application in any way that
|
||||
suggests your device or application comes from or is endorsed by Microsoft;
|
||||
|
||||
transfer individual components, specific libraries, classes, functions or code
|
||||
fragments of the software separately for purposes unrelated to the software; or
|
||||
|
||||
use or distribute the software in any way that would subject the software or
|
||||
Microsoft’s intellectual property or technology to any other license terms.
|
||||
|
||||
SCOPE OF LICENSE. The software is licensed, not sold. Microsoft reserves all
|
||||
other rights. Unless applicable law gives you more rights despite this
|
||||
limitation, you will not (and have no right to):
|
||||
|
||||
remove, minimize, block, or modify any notices of Microsoft or its suppliers in
|
||||
the software;
|
||||
|
||||
use the software in any way that is against the law or to create or propagate
|
||||
malware; or
|
||||
|
||||
share, publish, distribute, or lease the software (except as permitted in
|
||||
Section 2 above), or provide the software as a stand-alone offering for others
|
||||
to use.
|
||||
|
||||
DATA. This software may interact with other Microsoft products that collect data
|
||||
that is transmitted to Microsoft. To learn more about how Microsoft processes
|
||||
personal data we collect, please see the Microsoft Privacy Statement at
|
||||
https://go.microsoft.com/fwlink/?LinkId=248681.
|
||||
|
||||
EXPORT RESTRICTIONS. You must comply with all domestic and international export
|
||||
laws and regulations that apply to the software, which include restrictions on
|
||||
destinations, end users, and end use. For further information on export
|
||||
restrictions, visit https://aka.ms/exporting.
|
||||
|
||||
SUPPORT SERVICES. Microsoft is not obligated under this agreement to provide any
|
||||
support services for the software. Any support provided is “as is”, “with all
|
||||
faults”, and without warranty of any kind.
|
||||
|
||||
UPDATES. Microsoft may periodically update the software. You may obtain updates
|
||||
only from Microsoft or Microsoft-authorized sources. Updates may not include or
|
||||
support all existing software features, services, or peripheral devices.
|
||||
|
||||
TERMINATION. Without prejudice to any other rights, Microsoft may terminate this
|
||||
agreement if you fail to comply with any of its terms or conditions. In such
|
||||
event, you must destroy all copies of the software and all of its component
|
||||
parts.
|
||||
|
||||
ENTIRE AGREEMENT. This agreement, and any other terms Microsoft may provide for
|
||||
supplements, updates, or third-party applications, is the entire agreement for
|
||||
the software. To the extent you have entered into a separate agreement with
|
||||
Microsoft relating specifically to the software, the terms in such agreement
|
||||
shall control.
|
||||
|
||||
APPLICABLE LAW AND PLACE TO RESOLVE DISPUTES. If you acquired the software in
|
||||
the United States or Canada, the laws of the state or province where you live
|
||||
(or, if a business, where your principal place of business is located) govern
|
||||
the interpretation of this agreement, claims for its breach, and all other
|
||||
claims (including consumer protection, unfair competition, and tort claims),
|
||||
regardless of conflict of laws principles. If you acquired the software in any
|
||||
other country, its laws apply. If U.S. federal jurisdiction exists, you and
|
||||
Microsoft consent to exclusive jurisdiction and venue in the federal court in
|
||||
King County, Washington for all disputes heard in court. If not, you and
|
||||
Microsoft consent to exclusive jurisdiction and venue in the Superior Court of
|
||||
King County, Washington for all disputes heard in court.
|
||||
|
||||
CONSUMER RIGHTS; REGIONAL VARIATIONS. This agreement describes certain legal
|
||||
rights. You may have other rights, including consumer rights, under the laws of
|
||||
your state or country. Separate and apart from your relationship with Microsoft,
|
||||
you may also have rights with respect to the party from which you acquired the
|
||||
software. This agreement does not change those other rights if the laws of your
|
||||
state or country do not permit it to do so. For example, if you acquired the
|
||||
software in one of the below regions, or mandatory country law applies, then the
|
||||
following provisions apply to you:
|
||||
|
||||
Australia. You have statutory guarantees under the Australian Consumer Law and
|
||||
nothing in this agreement is intended to affect those rights.
|
||||
|
||||
Germany and Austria.
|
||||
|
||||
i.Warranty. The properly licensed software will perform substantially as
|
||||
described in any Microsoft materials that accompany the software. However,
|
||||
Microsoft gives no contractual guarantee in relation to the licensed software.
|
||||
|
||||
ii.Limitation of Liability. In case of intentional conduct, gross negligence,
|
||||
claims based on the Product Liability Act, as well as, in case of death or
|
||||
personal or physical injury, Microsoft is liable according to the statutory law.
|
||||
|
||||
|
||||
Subject to the foregoing clause ii., Microsoft will only be liable for slight
|
||||
negligence if Microsoft is in breach of such material contractual obligations,
|
||||
the fulfillment of which facilitate the due performance of this agreement, the
|
||||
breach of which would endanger the purpose of this agreement and the compliance
|
||||
with which a party may constantly trust in (so-called "cardinal obligations").
|
||||
In other cases of slight negligence, Microsoft will not be liable for slight
|
||||
negligence.
|
||||
|
||||
DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED “AS IS.” YOU BEAR THE RISK OF
|
||||
USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES, OR CONDITIONS. TO
|
||||
THE EXTENT PERMITTED UNDER APPLICABLE LAWS, MICROSOFT EXCLUDES ALL IMPLIED
|
||||
WARRANTIES, INCLUDING MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
|
||||
NON-INFRINGEMENT.
|
||||
|
||||
LIMITATION ON AND EXCLUSION OF DAMAGES. IF YOU HAVE ANY BASIS FOR RECOVERING
|
||||
DAMAGES DESPITE THE PRECEDING DISCLAIMER OF WARRANTY, YOU CAN RECOVER FROM
|
||||
MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT
|
||||
RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL,
|
||||
INDIRECT, OR INCIDENTAL DAMAGES.
|
||||
|
||||
This limitation applies to (a) anything related to the software, services,
|
||||
content (including code) on third party Internet sites, or third party
|
||||
applications; and (b) claims for breach of contract, warranty, guarantee, or
|
||||
condition; strict liability, negligence, or other tort; or any other claim; in
|
||||
each case to the extent permitted by applicable law.
|
||||
|
||||
It also applies even if Microsoft knew or should have known about the
|
||||
possibility of the damages. The above limitation or exclusion may not apply to
|
||||
you because your state, province, or country may not allow the exclusion or
|
||||
limitation of incidental, consequential, or other damages.
|
||||
|
||||
|
||||
|
||||
Please note: As this software is distributed in Canada, some of the clauses in
|
||||
this agreement are provided below in French.
|
||||
|
||||
Remarque: Ce logiciel étant distribué au Canada, certaines des clauses dans ce
|
||||
contrat sont fournies ci-dessous en français.
|
||||
|
||||
EXONÉRATION DE GARANTIE. Le logiciel visé par une licence est offert « tel quel
|
||||
». Toute utilisation de ce logiciel est à votre seule risque et péril. Microsoft
|
||||
n’accorde aucune autre garantie expresse. Vous pouvez bénéficier de droits
|
||||
additionnels en vertu du droit local sur la protection des consommateurs, que ce
|
||||
contrat ne peut modifier. La ou elles sont permises par le droit locale, les
|
||||
garanties implicites de qualité marchande, d’adéquation à un usage particulier
|
||||
et d’absence de contrefaçon sont exclues.
|
||||
|
||||
LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES
|
||||
DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une
|
||||
indemnisation en cas de dommages directs uniquement à hauteur de 5,00 $ US. Vous
|
||||
ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris
|
||||
les dommages spéciaux, indirects ou accessoires et pertes de bénéfices.
|
||||
|
||||
Cette limitation concerne:
|
||||
|
||||
•tout ce qui est relié au logiciel, aux services ou au contenu (y compris le
|
||||
code) figurant sur des sites Internet tiers ou dans des programmes tiers; et
|
||||
|
||||
•les réclamations au titre de violation de contrat ou de garantie, ou au titre
|
||||
de responsabilité stricte, de négligence ou d’une autre faute dans la limite
|
||||
autorisée par la loi en vigueur.
|
||||
|
||||
Elle s’applique également, même si Microsoft connaissait ou devrait connaître
|
||||
l’éventualité d’un tel dommage. Si votre pays n’autorise pas l’exclusion ou la
|
||||
limitation de responsabilité pour les dommages indirects, accessoires ou de
|
||||
quelque nature que ce soit, il se peut que la limitation ou l’exclusion
|
||||
ci-dessus ne s’appliquera pas à votre égard.
|
||||
|
||||
EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous
|
||||
pourriez avoir d’autres droits prévus par les lois de votre pays. Le présent
|
||||
contrat ne modifie pas les droits que vous confèrent les lois de votre pays si
|
||||
celles-ci ne le permettent pas.
|
||||
@@ -0,0 +1,16 @@
|
||||
LICENSED HARDWARE LIST
|
||||
|
||||
Last Updated: 2020-05-08
|
||||
|
||||
Microsoft has entered into OEM Agreements with manufacturers of the following
|
||||
microprocessors and microcontrollers (the “hardware”) to enable those
|
||||
manufacturers to include and distribute Azure RTOS in certain hardware. If you
|
||||
have obtained and/or are developing on microprocessor(s) and/or
|
||||
microcontroller(s) (“hardware”) listed below you inherit the “Distribution and
|
||||
Production Use” rights in Section 2 of the Microsoft Software License Terms for
|
||||
Microsoft Azure RTOS. If hardware is not listed below, you do not have those
|
||||
rights.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
More coming soon. Please check back frequently for updates.
|
||||
Executable
+63
@@ -0,0 +1,63 @@
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
# {{BEGIN_TARGET_SOURCES}}
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fx_nand_flash_simulated_driver.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fx_nor_flash_simulator_driver.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_256byte_ecc_check.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_256byte_ecc_compute.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_block_full_update.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_block_obsoleted_check.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_block_reclaim.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_close.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_defragment.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_driver_block_erase.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_driver_block_erased_verify.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_driver_block_status_get.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_driver_block_status_set.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_driver_extra_bytes_get.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_driver_extra_bytes_set.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_driver_page_erased_verify.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_driver_read.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_driver_write.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_extended_cache_enable.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_initialize.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_logical_sector_find.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_next_block_to_erase_find.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_open.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
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_physical_page_allocate.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_sector_mapping_cache_invalidate.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_sector_read.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_sector_release.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_sector_write.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_simulator.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_system_error.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_block_reclaim.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_close.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_defragment.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_driver_block_erase.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_driver_read.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_driver_write.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_extended_cache_enable.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_initialize.c
|
||||
${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_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
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_sector_read.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_sector_release.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_sector_write.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_simulator.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_system_error.c
|
||||
|
||||
# {{END_TARGET_SOURCES}}
|
||||
)
|
||||
|
||||
# Add the Common/inc directory to the project include list
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/inc
|
||||
)
|
||||
|
||||
@@ -0,0 +1,530 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** Application Interface (API) */
|
||||
/** */
|
||||
/**************************************************************************/
|
||||
/**************************************************************************/
|
||||
|
||||
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* APPLICATION INTERFACE DEFINITION RELEASE */
|
||||
/* */
|
||||
/* lx_api.h PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This file defines the basic Application Interface (API) to the */
|
||||
/* high-performance LevelX. All service prototypes and data structure */
|
||||
/* definitions are defined in this file. */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef LX_API_H
|
||||
#define LX_API_H
|
||||
|
||||
|
||||
/* Include necessary system files. */
|
||||
|
||||
#include "tx_api.h"
|
||||
|
||||
/* Disable warning of parameter not used. */
|
||||
#ifndef LX_PARAMETER_NOT_USED
|
||||
#define LX_PARAMETER_NOT_USED(p) ((void)(p))
|
||||
#endif /* LX_PARAMETER_NOT_USED */
|
||||
|
||||
|
||||
/* Define general LevelX Constants. */
|
||||
|
||||
#define LX_FALSE 0
|
||||
#define LX_TRUE 1
|
||||
#define LX_NULL 0
|
||||
#define LX_BLOCK_ERASE_COUNT_MASK 0x7FFFFFFF
|
||||
#define LX_BLOCK_ERASE_COUNT_MAX 0x7FFFFFFF
|
||||
#define LX_BLOCK_ERASED 0x80000000
|
||||
#define LX_BLOCK_ERASE_STARTED 0
|
||||
#define LX_ALL_ONES 0xFFFFFFFF
|
||||
|
||||
|
||||
/* Define error codes. */
|
||||
|
||||
#define LX_SUCCESS 0
|
||||
#define LX_ERROR 1
|
||||
#define LX_NO_SECTORS 2
|
||||
#define LX_SECTOR_NOT_FOUND 3
|
||||
#define LX_NO_PAGES 4
|
||||
#define LX_INVALID_WRITE 5
|
||||
#define LX_NAND_ERROR_CORRECTED 6
|
||||
#define LX_NAND_ERROR_NOT_CORRECTED 7
|
||||
#define LX_NO_MEMORY 8
|
||||
#define LX_DISABLED 9
|
||||
#define LX_SYSTEM_INVALID_FORMAT 90
|
||||
#define LX_SYSTEM_INVALID_BLOCK 91
|
||||
#define LX_SYSTEM_ALLOCATION_FAILED 92
|
||||
#define LX_SYSTEM_MUTEX_CREATE_FAILED 93
|
||||
#define LX_SYSTEM_INVALID_SECTOR_MAP 94
|
||||
|
||||
|
||||
/* Define NOR flash constants. */
|
||||
|
||||
#define LX_NOR_FLASH_OPENED ((ULONG) 0x4E4F524F)
|
||||
#define LX_NOR_FLASH_CLOSED ((ULONG) 0x4E4F5244)
|
||||
#define LX_NOR_SECTOR_SIZE (512/sizeof(ULONG))
|
||||
#define LX_NOR_FLASH_MIN_LOGICAL_SECTOR_OFFSET 1
|
||||
#define LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET 2
|
||||
#ifndef LX_NOR_FLASH_MAX_ERASE_COUNT_DELTA
|
||||
#define LX_NOR_FLASH_MAX_ERASE_COUNT_DELTA 4
|
||||
#endif
|
||||
#define LX_NOR_SECTOR_MAPPING_CACHE_DEPTH 4
|
||||
#ifndef LX_NOR_SECTOR_MAPPING_CACHE_SIZE
|
||||
#define LX_NOR_SECTOR_MAPPING_CACHE_SIZE 16 /* Minimum value of 8, all sizes must be a power of 2. */
|
||||
#endif
|
||||
#ifndef LX_NOR_EXTENDED_CACHE_SIZE
|
||||
#define LX_NOR_EXTENDED_CACHE_SIZE 8 /* Maximum number of extended cache sectors. */
|
||||
#endif
|
||||
|
||||
|
||||
/* Define the mask for the hash index into the sector mapping cache table. The sector mapping cache is divided
|
||||
into 4 entry pieces that are indexed by the formula:
|
||||
|
||||
index = (sector & LX_NOR_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NOR_SECTOR_MAPPING_CACHE_DEPTH
|
||||
|
||||
The LX_NOR_SECTOR_MAPPING_CACHE_DEPTH define must not be changed unless the related source code is also changed. */
|
||||
|
||||
#define LX_NOR_SECTOR_MAPPING_CACHE_HASH_MASK ((LX_NOR_SECTOR_MAPPING_CACHE_SIZE/LX_NOR_SECTOR_MAPPING_CACHE_DEPTH)-1)
|
||||
#define LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_MASK 0x7FFFFFFF
|
||||
#define LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID 0x80000000
|
||||
|
||||
#define LX_NOR_PHYSICAL_SECTOR_VALID 0x80000000
|
||||
#define LX_NOR_PHYSICAL_SECTOR_SUPERCEDED 0x40000000
|
||||
#define LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID 0x20000000
|
||||
#define LX_NOR_LOGICAL_SECTOR_MASK 0x1FFFFFFF
|
||||
#define LX_NOR_PHYSICAL_SECTOR_FREE 0xFFFFFFFF
|
||||
|
||||
|
||||
/* Define NAND flash constants. */
|
||||
|
||||
#define LX_NAND_GOOD_BLOCK 0xFF
|
||||
#define LX_NAND_BAD_BLOCK 0x00
|
||||
#define LX_NAND_FLASH_OPENED ((ULONG) 0x4E4F524F)
|
||||
#define LX_NAND_FLASH_CLOSED ((ULONG) 0x4E4F5244)
|
||||
#ifndef LX_NAND_FLASH_MAX_ERASE_COUNT_DELTA
|
||||
#define LX_NAND_FLASH_MAX_ERASE_COUNT_DELTA 64
|
||||
#endif
|
||||
|
||||
/* Define the NAND sector mapping cache. */
|
||||
|
||||
#define LX_NAND_SECTOR_MAPPING_CACHE_DEPTH 4 /* Not required if LX_NAND_FLASH_DIRECT_MAPPING_CACHE is defined. */
|
||||
#ifndef LX_NAND_SECTOR_MAPPING_CACHE_SIZE
|
||||
#define LX_NAND_SECTOR_MAPPING_CACHE_SIZE 128 /* Minimum value of 8, all sizes must be a power of 2, unless direct
|
||||
mapping is defined, in which there is no power of 2 requirement. */
|
||||
#endif
|
||||
#ifndef LX_NAND_ERASE_COUNT_WRITE_SIZE
|
||||
#define LX_NAND_ERASE_COUNT_WRITE_SIZE (nand_flash -> lx_nand_flash_pages_per_block + 1)
|
||||
#endif
|
||||
|
||||
|
||||
/* Define the mask for the hash index into the NAND sector mapping cache table. The sector mapping cache is divided
|
||||
into 4 entry pieces that are indexed by the formula:
|
||||
|
||||
index = (sector & LX_NAND_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NAND_SECTOR_MAPPING_CACHE_DEPTH
|
||||
|
||||
The LX_NAND_SECTOR_MAPPING_CACHE_DEPTH define must not be changed unless the related source code is also changed. */
|
||||
|
||||
#define LX_NAND_SECTOR_MAPPING_CACHE_HASH_MASK ((LX_NAND_SECTOR_MAPPING_CACHE_SIZE/LX_NAND_SECTOR_MAPPING_CACHE_DEPTH)-1)
|
||||
#define LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_MASK 0x7FFFFFFF
|
||||
#define LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID 0x80000000
|
||||
|
||||
#define LX_NAND_BLOCK_VALID 0x80000000
|
||||
#define LX_NAND_BLOCK_EMPTY 0x40000000
|
||||
#define LX_NAND_BLOCK_FULL 0x20000000
|
||||
#define LX_NAND_PAGE_VALID 0x80000000
|
||||
#define LX_NAND_PAGE_SUPERCEDED 0x40000000
|
||||
#define LX_NAND_PAGE_MAPPING_NOT_VALID 0x20000000
|
||||
#define LX_NAND_LOGICAL_SECTOR_MASK 0x1FFFFFFF
|
||||
#define LX_NAND_PAGE_FREE 0xFFFFFFFF
|
||||
#define LX_NAND_PAGE_LIST_VALID 0xF0F0F0F0
|
||||
|
||||
|
||||
/* Define the NAND flash extra byte structure. This will be set in the spare area of each mapped physical page. */
|
||||
|
||||
typedef struct LX_NAND_PAGE_EXTRA_INFO_STRUCT
|
||||
{
|
||||
ULONG lx_nand_page_extra_info_logical_sector;
|
||||
} LX_NAND_PAGE_EXTRA_INFO;
|
||||
|
||||
|
||||
/* Define the NAND flash logical sector cache entry structure. */
|
||||
|
||||
typedef struct LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_STRUCT
|
||||
{
|
||||
#ifndef LX_NAND_FLASH_DIRECT_MAPPING_CACHE
|
||||
ULONG lx_nand_sector_mapping_cache_logical_sector;
|
||||
#endif
|
||||
USHORT lx_nand_sector_mapping_cache_block;
|
||||
USHORT lx_nand_sector_mapping_cache_page;
|
||||
} LX_NAND_SECTOR_MAPPING_CACHE_ENTRY;
|
||||
|
||||
|
||||
/* Define the NAND flash control block structure. */
|
||||
|
||||
typedef struct LX_NAND_FLASH_STRUCT
|
||||
{
|
||||
ULONG lx_nand_flash_state;
|
||||
ULONG lx_nand_flash_total_blocks;
|
||||
ULONG lx_nand_flash_pages_per_block;
|
||||
ULONG lx_nand_flash_bytes_per_page;
|
||||
ULONG lx_nand_flash_words_per_block;
|
||||
ULONG lx_nand_flash_words_per_page;
|
||||
ULONG lx_nand_flash_total_pages;
|
||||
|
||||
ULONG lx_nand_flash_bad_blocks;
|
||||
ULONG lx_nand_flash_free_pages;
|
||||
ULONG lx_nand_flash_mapped_pages;
|
||||
ULONG lx_nand_flash_obsolete_pages;
|
||||
ULONG lx_nand_flash_minimum_erase_count;
|
||||
ULONG lx_nand_flash_maximum_erase_count;
|
||||
ULONG lx_nand_flash_free_block_search;
|
||||
ULONG lx_nand_flash_found_block_search;
|
||||
ULONG lx_nand_flash_found_page_search;
|
||||
|
||||
ULONG lx_nand_flash_max_mapped_sector;
|
||||
ULONG lx_nand_flash_page_corrections;
|
||||
ULONG lx_nand_flash_last_block_correction;
|
||||
ULONG lx_nand_flash_last_page_correction;
|
||||
|
||||
ULONG lx_nand_flash_diagnostic_system_errors;
|
||||
ULONG lx_nand_flash_diagnostic_system_error;
|
||||
ULONG lx_nand_flash_diagnostic_sector_write_requests;
|
||||
ULONG lx_nand_flash_diagnostic_sector_read_requests;
|
||||
ULONG lx_nand_flash_diagnostic_sector_release_requests;
|
||||
ULONG lx_nand_flash_diagnostic_page_allocates;
|
||||
ULONG lx_nand_flash_diagnostic_page_allocate_errors;
|
||||
|
||||
ULONG lx_nand_flash_diagnostic_sector_mapping_cache_hits;
|
||||
ULONG lx_nand_flash_diagnostic_sector_mapping_cache_misses;
|
||||
ULONG lx_nand_flash_diagnostic_page_extra_bytes_cache_hits;
|
||||
ULONG lx_nand_flash_diagnostic_page_extra_bytes_cache_misses;
|
||||
ULONG lx_nand_flash_diagnostic_page_0_cache_hits;
|
||||
ULONG lx_nand_flash_diagnostic_page_0_cache_misses;
|
||||
ULONG lx_nand_flash_diagnostic_block_status_cache_hits;
|
||||
ULONG lx_nand_flash_diagnostic_block_status_cache_misses;
|
||||
|
||||
ULONG lx_nand_flash_diagnostic_block_reclaim_attempts;
|
||||
ULONG lx_nand_flash_diagnostic_block_erases;
|
||||
ULONG lx_nand_flash_diagnostic_block_status_gets;
|
||||
ULONG lx_nand_flash_diagnostic_block_status_sets;
|
||||
ULONG lx_nand_flash_diagnostic_page_extra_bytes_sets;
|
||||
ULONG lx_nand_flash_diagnostic_page_writes;
|
||||
ULONG lx_nand_flash_diagnostic_page_extra_bytes_gets;
|
||||
ULONG lx_nand_flash_diagnostic_page_reads;
|
||||
ULONG lx_nand_flash_diagnostic_moved_pages;
|
||||
ULONG lx_nand_flash_diagnostic_block_erased_verifies;
|
||||
ULONG lx_nand_flash_diagnostic_page_erased_verifies;
|
||||
|
||||
ULONG lx_nand_flash_diagnostic_initial_format;
|
||||
ULONG lx_nand_flash_diagnostic_erased_block;
|
||||
ULONG lx_nand_flash_diagnostic_re_erase_block;
|
||||
ULONG lx_nand_flash_diagnostic_page_being_obsoleted;
|
||||
ULONG lx_nand_flash_diagnostic_page_obsoleted;
|
||||
ULONG lx_nand_flash_diagnostic_mapping_invalid;
|
||||
ULONG lx_nand_flash_diagnostic_mapping_write_interrupted;
|
||||
ULONG lx_nand_flash_diagnostic_page_not_free;
|
||||
ULONG lx_nand_flash_diagnostic_page_data_not_free;
|
||||
|
||||
UINT (*lx_nand_flash_driver_read)(ULONG block, ULONG page, ULONG *destination, ULONG words);
|
||||
UINT (*lx_nand_flash_driver_write)(ULONG block, ULONG page, ULONG *source, ULONG words);
|
||||
UINT (*lx_nand_flash_driver_block_erase)(ULONG block, ULONG erase_count);
|
||||
UINT (*lx_nand_flash_driver_block_erased_verify)(ULONG block);
|
||||
UINT (*lx_nand_flash_driver_page_erased_verify)(ULONG block, ULONG page);
|
||||
UINT (*lx_nand_flash_driver_block_status_get)(ULONG block, UCHAR *bad_block_flag);
|
||||
UINT (*lx_nand_flash_driver_block_status_set)(ULONG block, UCHAR bad_block_flag);
|
||||
UINT (*lx_nand_flash_driver_extra_bytes_get)(ULONG block, ULONG page, UCHAR *destination, UINT size);
|
||||
UINT (*lx_nand_flash_driver_extra_bytes_set)(ULONG block, ULONG page, UCHAR *source, UINT size);
|
||||
UINT (*lx_nand_flash_driver_system_error)(UINT error_code, ULONG block, ULONG page);
|
||||
|
||||
ULONG *lx_nand_flash_page_buffer;
|
||||
UINT lx_nand_flash_sector_mapping_cache_enabled;
|
||||
LX_NAND_SECTOR_MAPPING_CACHE_ENTRY
|
||||
lx_nand_flash_sector_mapping_cache[LX_NAND_SECTOR_MAPPING_CACHE_SIZE];
|
||||
|
||||
|
||||
/* Define the extended cache structures for block status, page extra bytes, and block page 0 caches. The memory for these cache
|
||||
extensions is optionally supplied by the application after the lx_nand_flash_open call has been made. */
|
||||
|
||||
UCHAR *lx_nand_flash_block_status_cache;
|
||||
LX_NAND_PAGE_EXTRA_INFO *lx_nand_flash_page_extra_bytes_cache;
|
||||
ULONG *lx_nand_flash_page_0_cache;
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* When this conditional is used, the LevelX code utilizes a ThreadX mutex for thread
|
||||
safe operation. Generally, this is not required since FileX ensures thread safe operation at
|
||||
a higher layer. */
|
||||
TX_MUTEX lx_nand_flash_mutex;
|
||||
#endif
|
||||
|
||||
/* 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;
|
||||
|
||||
} LX_NAND_FLASH;
|
||||
|
||||
|
||||
|
||||
/* Define the NOR flash sector cache entry structure. */
|
||||
|
||||
typedef struct LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_STRUCT
|
||||
{
|
||||
ULONG lx_nor_sector_mapping_cache_logical_sector;
|
||||
ULONG *lx_nor_sector_mapping_cache_physical_sector_map_entry;
|
||||
ULONG *lx_nor_sector_mapping_cache_physical_sector_address;
|
||||
} LX_NOR_SECTOR_MAPPING_CACHE_ENTRY;
|
||||
|
||||
|
||||
/* Define the NOR flash extended cache entry structure. */
|
||||
|
||||
typedef struct LX_NOR_FLASH_EXTENDED_CACHE_ENTRY_STRUCT
|
||||
{
|
||||
ULONG *lx_nor_flash_extended_cache_entry_sector_address;
|
||||
ULONG *lx_nor_flash_extended_cache_entry_sector_memory;
|
||||
ULONG lx_nor_flash_extended_cache_entry_access_count;
|
||||
} LX_NOR_FLASH_EXTENDED_CACHE_ENTRY;
|
||||
|
||||
|
||||
/* Define the NOR flash control block structure. */
|
||||
|
||||
typedef struct LX_NOR_FLASH_STRUCT
|
||||
{
|
||||
ULONG lx_nor_flash_state;
|
||||
ULONG lx_nor_flash_total_blocks;
|
||||
ULONG lx_nor_flash_words_per_block;
|
||||
ULONG lx_nor_flash_total_physical_sectors;
|
||||
ULONG lx_nor_flash_physical_sectors_per_block;
|
||||
|
||||
ULONG *lx_nor_flash_base_address;
|
||||
ULONG lx_nor_flash_block_free_bit_map_offset;
|
||||
ULONG lx_nor_flash_block_bit_map_words;
|
||||
ULONG lx_nor_flash_block_bit_map_mask;
|
||||
ULONG lx_nor_flash_block_physical_sector_mapping_offset;
|
||||
ULONG lx_nor_flash_block_physical_sector_offset;
|
||||
|
||||
ULONG lx_nor_flash_free_physical_sectors;
|
||||
ULONG lx_nor_flash_mapped_physical_sectors;
|
||||
ULONG lx_nor_flash_obsolete_physical_sectors;
|
||||
ULONG lx_nor_flash_minimum_erase_count;
|
||||
ULONG lx_nor_flash_maximum_erase_count;
|
||||
|
||||
ULONG lx_nor_flash_free_block_search;
|
||||
ULONG lx_nor_flash_found_block_search;
|
||||
ULONG lx_nor_flash_found_sector_search;
|
||||
|
||||
ULONG lx_nor_flash_write_requests;
|
||||
ULONG lx_nor_flash_read_requests;
|
||||
ULONG lx_nor_flash_sector_mapping_cache_hits;
|
||||
ULONG lx_nor_flash_sector_mapping_cache_misses;
|
||||
ULONG lx_nor_flash_physical_block_allocates;
|
||||
ULONG lx_nor_flash_physical_block_allocate_errors;
|
||||
ULONG lx_nor_flash_diagnostic_system_errors;
|
||||
ULONG lx_nor_flash_diagnostic_system_error;
|
||||
ULONG lx_nor_flash_diagnostic_initial_format;
|
||||
ULONG lx_nor_flash_diagnostic_erased_block;
|
||||
ULONG lx_nor_flash_diagnostic_re_erase_block;
|
||||
ULONG lx_nor_flash_diagnostic_sector_being_obsoleted;
|
||||
ULONG lx_nor_flash_diagnostic_sector_obsoleted;
|
||||
ULONG lx_nor_flash_diagnostic_mapping_invalidated;
|
||||
ULONG lx_nor_flash_diagnostic_mapping_write_interrupted;
|
||||
ULONG lx_nor_flash_diagnostic_sector_not_free;
|
||||
ULONG lx_nor_flash_diagnostic_sector_data_not_free;
|
||||
|
||||
UINT (*lx_nor_flash_driver_read)(ULONG *flash_address, ULONG *destination, ULONG words);
|
||||
UINT (*lx_nor_flash_driver_write)(ULONG *flash_address, ULONG *source, ULONG words);
|
||||
UINT (*lx_nor_flash_driver_block_erase)(ULONG block, ULONG erase_count);
|
||||
UINT (*lx_nor_flash_driver_block_erased_verify)(ULONG block);
|
||||
UINT (*lx_nor_flash_driver_system_error)(UINT error_code);
|
||||
|
||||
ULONG *lx_nor_flash_sector_buffer;
|
||||
UINT lx_nor_flash_sector_mapping_cache_enabled;
|
||||
LX_NOR_SECTOR_MAPPING_CACHE_ENTRY
|
||||
lx_nor_flash_sector_mapping_cache[LX_NOR_SECTOR_MAPPING_CACHE_SIZE];
|
||||
|
||||
#ifndef LX_NOR_DISABLE_EXTENDED_CACHE
|
||||
|
||||
UINT lx_nor_flash_extended_cache_entries;
|
||||
LX_NOR_FLASH_EXTENDED_CACHE_ENTRY
|
||||
lx_nor_flash_extended_cache[LX_NOR_EXTENDED_CACHE_SIZE];
|
||||
ULONG lx_nor_flash_extended_cache_hits;
|
||||
ULONG lx_nor_flash_extended_cache_misses;
|
||||
#endif
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* When this conditional is used, the LevelX code utilizes a ThreadX mutex for thread
|
||||
safe operation. Generally, this is not required since FileX ensures thread safe operation at
|
||||
a higher layer. */
|
||||
TX_MUTEX lx_nor_flash_mutex;
|
||||
#endif
|
||||
|
||||
/* 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;
|
||||
|
||||
} LX_NOR_FLASH;
|
||||
|
||||
|
||||
/* Each physical NOR block has the following structure at the beginning of the block:
|
||||
|
||||
Offset Meaning
|
||||
|
||||
0 Erase count
|
||||
4 Minimum logical sector number - only when the block is full
|
||||
8 Maximum logical sector number - only when the block is full
|
||||
12 Free physical sector bit map, where a value of 1 indicates a
|
||||
free physical sector. The bit map is evenly divisible by 4
|
||||
. Array of physical sector mapping information (4 bytes per entry,
|
||||
one entry for each physical sector in block). Each entry looks
|
||||
like the following:
|
||||
|
||||
Bit(s) Meaning
|
||||
|
||||
0-29 Logical sector mapped if not 0x3FFFFFFF
|
||||
30 If 0, entry is being superceded
|
||||
31 If 1, entry is valid
|
||||
|
||||
Array of physical sectors, with each of size LX_NOR_SECTOR_SIZE
|
||||
*/
|
||||
|
||||
|
||||
typedef struct LX_NOR_FLASH_BLOCK_HEADER_STRUCT
|
||||
{
|
||||
ULONG lx_nor_flash_block_erase_count; /* Bit 31: 1 -> not used, 0 -> used */
|
||||
ULONG lx_nor_flash_block_min_logical_sector; /* On full block, minimum valid sector */
|
||||
ULONG lx_nor_flash_block_max_logical_sector; /* On full block, maximum valid sector */
|
||||
} LX_NOR_FLASH_BLOCK_HEADER;
|
||||
|
||||
|
||||
/* Define external structure references. */
|
||||
|
||||
extern LX_NAND_FLASH *_lx_nand_flash_opened_ptr;
|
||||
extern ULONG _lx_nand_flash_opened_count;
|
||||
extern LX_NOR_FLASH *_lx_nor_flash_opened_ptr;
|
||||
extern ULONG _lx_nor_flash_opened_count;
|
||||
|
||||
|
||||
/* Map internal functions. */
|
||||
|
||||
#ifndef LX_SOURCE_CODE
|
||||
#define lx_nand_flash_close _lx_nand_flash_close
|
||||
#define lx_nand_flash_defragment _lx_nand_flash_defragment
|
||||
#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_initialize _lx_nand_flash_initialize
|
||||
#define lx_nand_flash_open _lx_nand_flash_open
|
||||
#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
|
||||
#define lx_nand_flash_sector_release _lx_nand_flash_sector_release
|
||||
#define lx_nand_flash_sector_write _lx_nand_flash_sector_write
|
||||
#define lx_nand_flash_256byte_ecc_check _lx_nand_flash_256byte_ecc_check
|
||||
#define lx_nand_flash_256byte_ecc_compute _lx_nand_flash_256byte_ecc_compute
|
||||
|
||||
#define lx_nor_flash_close _lx_nor_flash_close
|
||||
#define lx_nor_flash_defragment _lx_nor_flash_defragment
|
||||
#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_open _lx_nor_flash_open
|
||||
#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
|
||||
#endif
|
||||
|
||||
|
||||
/* External LevelX API prototypes. */
|
||||
|
||||
UINT _lx_nand_flash_close(LX_NAND_FLASH *nand_flash);
|
||||
UINT _lx_nand_flash_defragment(LX_NAND_FLASH *nand_flash);
|
||||
UINT _lx_nand_flash_initialize(void);
|
||||
UINT _lx_nand_flash_extended_cache_enable(LX_NAND_FLASH *nand_flash, VOID *memory, ULONG size);
|
||||
UINT _lx_nand_flash_open(LX_NAND_FLASH *nand_flash, CHAR *name, UINT (*nand_driver_initialize)(LX_NAND_FLASH *));
|
||||
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);
|
||||
UINT _lx_nand_flash_sector_read(LX_NAND_FLASH *nand_flash, ULONG logical_sector, VOID *buffer);
|
||||
UINT _lx_nand_flash_sector_release(LX_NAND_FLASH *nand_flash, ULONG logical_sector);
|
||||
UINT _lx_nand_flash_sector_write(LX_NAND_FLASH *nand_flash, ULONG logical_sector, VOID *buffer);
|
||||
|
||||
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_open(LX_NOR_FLASH *nor_flash, CHAR *name, UINT (*nor_driver_initialize)(LX_NOR_FLASH *));
|
||||
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);
|
||||
UINT _lx_nor_flash_sector_write(LX_NOR_FLASH *nor_flash, ULONG logical_sector, VOID *buffer);
|
||||
|
||||
|
||||
/* Internal LevelX prototypes. */
|
||||
|
||||
UINT _lx_nand_flash_block_full_update(LX_NAND_FLASH *nand_flash, ULONG block, ULONG erase_count);
|
||||
VOID _lx_nand_flash_block_obsoleted_check(LX_NAND_FLASH *nand_flash, ULONG block);
|
||||
UINT _lx_nand_flash_block_reclaim(LX_NAND_FLASH *nand_flash);
|
||||
|
||||
UINT _lx_nand_flash_driver_read(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page, ULONG *destination, ULONG words);
|
||||
UINT _lx_nand_flash_driver_write(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page, ULONG *source, ULONG words);
|
||||
UINT _lx_nand_flash_driver_block_erase(LX_NAND_FLASH *nand_flash, ULONG block, ULONG erase_count);
|
||||
UINT _lx_nand_flash_driver_block_erased_verify(LX_NAND_FLASH *nand_flash, ULONG block);
|
||||
UINT _lx_nand_flash_driver_page_erased_verify(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page);
|
||||
UINT _lx_nand_flash_driver_block_status_get(LX_NAND_FLASH *nand_flash, ULONG block, UCHAR *bad_block_flag);
|
||||
UINT _lx_nand_flash_driver_block_status_set(LX_NAND_FLASH *nand_flash, ULONG block, UCHAR bad_block_flag);
|
||||
UINT _lx_nand_flash_driver_extra_bytes_get(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page, UCHAR *destination, UINT size);
|
||||
UINT _lx_nand_flash_driver_extra_bytes_set(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page, UCHAR *source, UINT size);
|
||||
|
||||
VOID _lx_nand_flash_internal_error(LX_NAND_FLASH *nand_flash, ULONG error_code);
|
||||
UINT _lx_nand_flash_logical_sector_find(LX_NAND_FLASH *nand_flash, ULONG logical_sector, ULONG superceded_check, ULONG *block, ULONG *page);
|
||||
UINT _lx_nand_flash_next_block_to_erase_find(LX_NAND_FLASH *nand_flash, ULONG *return_erase_block, ULONG *return_erase_count, ULONG *return_mapped_pages, ULONG *return_obsolete_pages);
|
||||
UINT _lx_nand_flash_physical_page_allocate(LX_NAND_FLASH *nand_flash, ULONG *block, ULONG *page, ULONG *erase_count);
|
||||
VOID _lx_nand_flash_sector_mapping_cache_invalidate(LX_NAND_FLASH *nand_flash, ULONG logical_sector);
|
||||
VOID _lx_nand_flash_system_error(LX_NAND_FLASH *nand_flash, UINT error_code, ULONG block, ULONG page);
|
||||
UINT _lx_nand_flash_256byte_ecc_check(UCHAR *page_buffer, UCHAR *ecc_buffer);
|
||||
UINT _lx_nand_flash_256byte_ecc_compute(UCHAR *page_buffer, UCHAR *ecc_buffer);
|
||||
|
||||
UINT _lx_nor_flash_block_reclaim(LX_NOR_FLASH *nor_flash);
|
||||
UINT _lx_nor_flash_driver_block_erase(LX_NOR_FLASH *nor_flash, ULONG block, ULONG erase_count);
|
||||
UINT _lx_nor_flash_driver_read(LX_NOR_FLASH *nor_flash, ULONG *flash_address, ULONG *destination, ULONG words);
|
||||
UINT _lx_nor_flash_driver_write(LX_NOR_FLASH *nor_flash, ULONG *flash_address, ULONG *source, ULONG words);
|
||||
VOID _lx_nor_flash_internal_error(LX_NOR_FLASH *nor_flash, ULONG error_code);
|
||||
UINT _lx_nor_flash_logical_sector_find(LX_NOR_FLASH *nor_flash, ULONG logical_sector, ULONG superceded_check, ULONG **physical_sector_map_entry, ULONG **physical_sector_address);
|
||||
UINT _lx_nor_flash_next_block_to_erase_find(LX_NOR_FLASH *nor_flash, ULONG *return_erase_block, ULONG *return_erase_count, ULONG *return_mapped_sectors, ULONG *return_obsolete_sectors);
|
||||
UINT _lx_nor_flash_physical_sector_allocate(LX_NOR_FLASH *nor_flash, ULONG logical_sector, ULONG **physical_sector_map_entry, ULONG **physical_sector_address);
|
||||
VOID _lx_nor_flash_sector_mapping_cache_invalidate(LX_NOR_FLASH *nor_flash, ULONG logical_sector);
|
||||
VOID _lx_nor_flash_system_error(LX_NOR_FLASH *nor_flash, UINT error_code);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,441 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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. */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
|
||||
|
||||
/**************************************************************************/
|
||||
/**************************************************************************/
|
||||
/** */
|
||||
/** FileX Component */
|
||||
/** */
|
||||
/** FileX NAND FLASH Simulator Driver */
|
||||
/** */
|
||||
/**************************************************************************/
|
||||
/**************************************************************************/
|
||||
|
||||
|
||||
/* Include necessary system files. */
|
||||
|
||||
#include "tx_api.h"
|
||||
#include "fx_api.h"
|
||||
#include "lx_api.h"
|
||||
|
||||
|
||||
/* Create a NAND flash control block. */
|
||||
|
||||
LX_NAND_FLASH nand_flash;
|
||||
|
||||
|
||||
/* Define the NAND flash simulation initialization function. */
|
||||
|
||||
UINT _lx_nand_flash_simulator_initialize(LX_NAND_FLASH *nand_flash);
|
||||
VOID _fx_nand_flash_read_sectors(ULONG logical_sector, ULONG sectors, UCHAR *destination_buffer);
|
||||
VOID _fx_nand_flash_write_sectors(ULONG logical_sector, ULONG sectors, UCHAR *source_buffer);
|
||||
|
||||
|
||||
/* The simulated NAND driver relies on the fx_media_format call to be made prior to
|
||||
the fx_media_open call.
|
||||
|
||||
fx_media_format(&ram_disk,
|
||||
_fx_nand_sim_driver, // Driver entry
|
||||
FX_NULL, // Unused
|
||||
media_memory, // Media buffer pointer
|
||||
sizeof(media_memory), // Media buffer size
|
||||
"MY_NAND_DISK", // Volume Name
|
||||
1, // Number of FATs
|
||||
32, // Directory Entries
|
||||
0, // Hidden sectors
|
||||
120, // Total sectors
|
||||
2048, // Sector size
|
||||
1, // Sectors per cluster
|
||||
1, // Heads
|
||||
1); // Sectors per track
|
||||
|
||||
*/
|
||||
|
||||
|
||||
VOID _fx_nand_flash_simulator_driver(FX_MEDIA *media_ptr);
|
||||
|
||||
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* FUNCTION RELEASE */
|
||||
/* */
|
||||
/* _fx_nand_simulator_driver PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function is the entry point to the generic NAND simulated */
|
||||
/* disk driver that is delivered with the flash wear leveling product */
|
||||
/* LevelX. */
|
||||
/* */
|
||||
/* This driver also serves as a template for developing other LevelX */
|
||||
/* NAND flash drivers for actual flash devices. Simply replace the */
|
||||
/* read/write sector logic with calls to read/write from the */
|
||||
/* appropriate physical device access functions. */
|
||||
/* */
|
||||
/* FileX NAND FLASH structures look like the following: */
|
||||
/* */
|
||||
/* Logical Sector Contents */
|
||||
/* */
|
||||
/* 0 Boot record */
|
||||
/* 1 FAT Area Start */
|
||||
/* +FAT Sectors Root Directory Start */
|
||||
/* +Directory Sectors Data Sector Start */
|
||||
/* */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* media_ptr Media control block pointer */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nand_flash_close Close NAND flash manager */
|
||||
/* _lx_nand_flash_open Open NAND flash manager */
|
||||
/* _lx_nand_flash_sector_read Read a NAND sector */
|
||||
/* _lx_nand_flash_sector_release Release a NAND sector */
|
||||
/* _lx_nand_flash_sector_write Write a NAND sector */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* FileX System Functions */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
VOID _fx_nand_flash_simulator_driver(FX_MEDIA *media_ptr)
|
||||
{
|
||||
|
||||
ULONG logical_sector;
|
||||
ULONG count;
|
||||
UCHAR *buffer;
|
||||
UINT status;
|
||||
|
||||
|
||||
/* There are several useful/important pieces of information contained in the media
|
||||
structure, some of which are supplied by FileX and others are for the driver to
|
||||
setup. The following is a summary of the necessary FX_MEDIA structure members:
|
||||
|
||||
FX_MEDIA Member Meaning
|
||||
|
||||
fx_media_driver_request FileX request type. Valid requests from FileX are
|
||||
as follows:
|
||||
|
||||
FX_DRIVER_READ
|
||||
FX_DRIVER_WRITE
|
||||
FX_DRIVER_FLUSH
|
||||
FX_DRIVER_ABORT
|
||||
FX_DRIVER_INIT
|
||||
FX_DRIVER_BOOT_READ
|
||||
FX_DRIVER_RELEASE_SECTORS
|
||||
FX_DRIVER_BOOT_WRITE
|
||||
FX_DRIVER_UNINIT
|
||||
|
||||
fx_media_driver_status This value is RETURNED by the driver. If the
|
||||
operation is successful, this field should be
|
||||
set to FX_SUCCESS for before returning. Otherwise,
|
||||
if an error occurred, this field should be set
|
||||
to FX_IO_ERROR.
|
||||
|
||||
fx_media_driver_buffer Pointer to buffer to read or write sector data.
|
||||
This is supplied by FileX.
|
||||
|
||||
fx_media_driver_logical_sector Logical sector FileX is requesting.
|
||||
|
||||
fx_media_driver_sectors Number of sectors FileX is requesting.
|
||||
|
||||
|
||||
The following is a summary of the optional FX_MEDIA structure members:
|
||||
|
||||
FX_MEDIA Member Meaning
|
||||
|
||||
fx_media_driver_info Pointer to any additional information or memory.
|
||||
This is optional for the driver use and is setup
|
||||
from the fx_media_open call. The RAM disk uses
|
||||
this pointer for the RAM disk memory itself.
|
||||
|
||||
fx_media_driver_write_protect The DRIVER sets this to FX_TRUE when media is write
|
||||
protected. This is typically done in initialization,
|
||||
but can be done anytime.
|
||||
|
||||
fx_media_driver_free_sector_update The DRIVER sets this to FX_TRUE when it needs to
|
||||
know when clusters are released. This is important
|
||||
for FLASH wear-leveling drivers.
|
||||
|
||||
fx_media_driver_system_write FileX sets this flag to FX_TRUE if the sector being
|
||||
written is a system sector, e.g., a boot, FAT, or
|
||||
directory sector. The driver may choose to use this
|
||||
to initiate error recovery logic for greater fault
|
||||
tolerance.
|
||||
|
||||
fx_media_driver_data_sector_read FileX sets this flag to FX_TRUE if the sector(s) being
|
||||
read are file data sectors, i.e., NOT system sectors.
|
||||
|
||||
fx_media_driver_sector_type FileX sets this variable to the specific type of
|
||||
sector being read or written. The following sector
|
||||
types are identified:
|
||||
|
||||
FX_UNKNOWN_SECTOR
|
||||
FX_BOOT_SECTOR
|
||||
FX_FAT_SECTOR
|
||||
FX_DIRECTORY_SECTOR
|
||||
FX_DATA_SECTOR
|
||||
*/
|
||||
|
||||
/* Process the driver request specified in the media control block. */
|
||||
switch(media_ptr -> fx_media_driver_request)
|
||||
{
|
||||
|
||||
case FX_DRIVER_READ:
|
||||
{
|
||||
|
||||
/* Read sector(s) from NAND flash. */
|
||||
logical_sector = media_ptr -> fx_media_driver_logical_sector;
|
||||
count = media_ptr -> fx_media_driver_sectors;
|
||||
buffer = (UCHAR *) media_ptr -> fx_media_driver_buffer;
|
||||
while (count)
|
||||
{
|
||||
|
||||
/* Call LevelX to read one flash sector. */
|
||||
status = _lx_nand_flash_sector_read(&nand_flash, logical_sector, buffer);
|
||||
|
||||
/* Determine if the read was successful. */
|
||||
if (status != LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Return an I/O error to FileX. */
|
||||
media_ptr -> fx_media_driver_status = FX_IO_ERROR;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Successful sector read. */
|
||||
count--;
|
||||
logical_sector++;
|
||||
buffer += media_ptr -> fx_media_bytes_per_sector;
|
||||
}
|
||||
/* Successful driver request. */
|
||||
media_ptr -> fx_media_driver_status = FX_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
case FX_DRIVER_WRITE:
|
||||
{
|
||||
|
||||
/* Write sector(s) to NAND flash. */
|
||||
logical_sector = media_ptr -> fx_media_driver_logical_sector;
|
||||
count = media_ptr -> fx_media_driver_sectors;
|
||||
buffer = (UCHAR *) media_ptr -> fx_media_driver_buffer;
|
||||
while (count)
|
||||
{
|
||||
|
||||
/* Call LevelX to write a sector. */
|
||||
status = _lx_nand_flash_sector_write(&nand_flash, logical_sector, buffer);
|
||||
|
||||
/* Determine if the write was successful. */
|
||||
if (status != LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Return an I/O error to FileX. */
|
||||
media_ptr -> fx_media_driver_status = FX_IO_ERROR;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Successful sector write. */
|
||||
count--;
|
||||
logical_sector++;
|
||||
buffer += media_ptr -> fx_media_bytes_per_sector;
|
||||
}
|
||||
|
||||
/* Successful driver request. */
|
||||
media_ptr -> fx_media_driver_status = FX_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
case FX_DRIVER_RELEASE_SECTORS:
|
||||
{
|
||||
|
||||
/* Release the mapping of this sector. */
|
||||
logical_sector = media_ptr -> fx_media_driver_logical_sector;
|
||||
count = media_ptr -> fx_media_driver_sectors;
|
||||
while (count)
|
||||
{
|
||||
|
||||
/* Call LevelX to release a sector mapping. */
|
||||
status = _lx_nand_flash_sector_release(&nand_flash, logical_sector);
|
||||
|
||||
/* Determine if the sector release was successful. */
|
||||
if (status != LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Return an I/O error to FileX. */
|
||||
media_ptr -> fx_media_driver_status = FX_IO_ERROR;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Successful sector release. */
|
||||
count--;
|
||||
logical_sector++;
|
||||
}
|
||||
|
||||
/* Successful driver request. */
|
||||
media_ptr -> fx_media_driver_status = FX_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
case FX_DRIVER_FLUSH:
|
||||
{
|
||||
|
||||
/* Return driver success. */
|
||||
media_ptr -> fx_media_driver_status = FX_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
case FX_DRIVER_ABORT:
|
||||
{
|
||||
|
||||
/* Return driver success. */
|
||||
media_ptr -> fx_media_driver_status = FX_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
case FX_DRIVER_INIT:
|
||||
{
|
||||
|
||||
/* FLASH drivers are responsible for setting several fields in the
|
||||
media structure, as follows:
|
||||
|
||||
media_ptr -> fx_media_driver_free_sector_update
|
||||
media_ptr -> fx_media_driver_write_protect
|
||||
|
||||
The fx_media_driver_free_sector_update flag is used to instruct
|
||||
FileX to inform the driver whenever sectors are not being used.
|
||||
This is especially useful for FLASH managers so they don't have
|
||||
maintain mapping for sectors no longer in use.
|
||||
|
||||
The fx_media_driver_write_protect flag can be set anytime by the
|
||||
driver to indicate the media is not writable. Write attempts made
|
||||
when this flag is set are returned as errors. */
|
||||
|
||||
/* Perform basic initialization here... since the boot record is going
|
||||
to be read subsequently and again for volume name requests. */
|
||||
|
||||
/* With flash wear leveling, FileX should tell wear leveling when sectors
|
||||
are no longer in use. */
|
||||
media_ptr -> fx_media_driver_free_sector_update = FX_TRUE;
|
||||
|
||||
/* Open the NAND flash simulation. */
|
||||
status = _lx_nand_flash_open(&nand_flash, "sim nand flash", _lx_nand_flash_simulator_initialize);
|
||||
|
||||
/* Determine if the flash open was successful. */
|
||||
if (status != LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Return an I/O error to FileX. */
|
||||
media_ptr -> fx_media_driver_status = FX_IO_ERROR;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Successful driver request. */
|
||||
media_ptr -> fx_media_driver_status = FX_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
case FX_DRIVER_UNINIT:
|
||||
{
|
||||
|
||||
/* There is nothing to do in this case for the RAM driver. For actual
|
||||
devices some shutdown processing may be necessary. */
|
||||
|
||||
/* Close the NAND flash simulation. */
|
||||
status = _lx_nand_flash_close(&nand_flash);
|
||||
|
||||
/* Determine if the flash close was successful. */
|
||||
if (status != LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Return an I/O error to FileX. */
|
||||
media_ptr -> fx_media_driver_status = FX_IO_ERROR;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Successful driver request. */
|
||||
media_ptr -> fx_media_driver_status = FX_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
case FX_DRIVER_BOOT_READ:
|
||||
{
|
||||
|
||||
/* Read the boot record and return to the caller. */
|
||||
status = _lx_nand_flash_sector_read(&nand_flash, 0, (UCHAR *) media_ptr -> fx_media_driver_buffer);
|
||||
|
||||
/* Determine if the read was successful. */
|
||||
if (status != LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Return an I/O error to FileX. */
|
||||
media_ptr -> fx_media_driver_status = FX_IO_ERROR;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Successful driver request. */
|
||||
media_ptr -> fx_media_driver_status = FX_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
case FX_DRIVER_BOOT_WRITE:
|
||||
{
|
||||
|
||||
/* Write the boot record and return to the caller. */
|
||||
status = _lx_nand_flash_sector_write(&nand_flash, 0, (UCHAR *) media_ptr -> fx_media_driver_buffer);
|
||||
|
||||
/* Determine if the write was successful. */
|
||||
if (status != LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Return an I/O error to FileX. */
|
||||
media_ptr -> fx_media_driver_status = FX_IO_ERROR;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Successful driver request. */
|
||||
media_ptr -> fx_media_driver_status = FX_SUCCESS;
|
||||
break ;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
|
||||
/* Invalid driver request. */
|
||||
media_ptr -> fx_media_driver_status = FX_IO_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,460 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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. */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
|
||||
|
||||
/**************************************************************************/
|
||||
/**************************************************************************/
|
||||
/** */
|
||||
/** FileX Component */
|
||||
/** */
|
||||
/** FileX NOR FLASH Simulator Driver */
|
||||
/** */
|
||||
/**************************************************************************/
|
||||
/**************************************************************************/
|
||||
|
||||
|
||||
/* Include necessary system files. */
|
||||
|
||||
#include "tx_api.h"
|
||||
#include "fx_api.h"
|
||||
#include "lx_api.h"
|
||||
|
||||
|
||||
/* Create a NOR flash control block. */
|
||||
|
||||
LX_NOR_FLASH nor_flash;
|
||||
|
||||
|
||||
/* The simulated NOR driver relies on the fx_media_format call to be made prior to
|
||||
the fx_media_open call.
|
||||
|
||||
fx_media_format(&ram_disk,
|
||||
_fx_nor_sim_driver, // Driver entry
|
||||
FX_NULL, // Unused
|
||||
media_memory, // Media buffer pointer
|
||||
sizeof(media_memory), // Media buffer size
|
||||
"MY_NOR_DISK", // Volume Name
|
||||
1, // Number of FATs
|
||||
32, // Directory Entries
|
||||
0, // Hidden sectors
|
||||
120, // Total sectors
|
||||
512, // Sector size
|
||||
1, // Sectors per cluster
|
||||
1, // Heads
|
||||
1); // Sectors per track
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/* Define prototypes. */
|
||||
|
||||
UINT _lx_nor_flash_simulator_initialize(LX_NOR_FLASH *nor_flash);
|
||||
VOID _fx_nor_flash_simulator_driver(FX_MEDIA *media_ptr);
|
||||
|
||||
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* FUNCTION RELEASE */
|
||||
/* */
|
||||
/* _fx_nor_flash_simulator_driver PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function is the entry point to the generic NOR simulated */
|
||||
/* disk driver that is delivered with the flash wear leveling product */
|
||||
/* LevelX. */
|
||||
/* */
|
||||
/* This driver also serves as a template for developing other LevelX */
|
||||
/* NOR flash drivers for actual flash devices. Simply replace the */
|
||||
/* read/write sector logic with calls to read/write from the */
|
||||
/* appropriate physical device access functions. */
|
||||
/* */
|
||||
/* FileX NOR FLASH structures look like the following: */
|
||||
/* */
|
||||
/* Logical Sector Contents */
|
||||
/* */
|
||||
/* 0 Boot record */
|
||||
/* 1 FAT Area Start */
|
||||
/* +FAT Sectors Root Directory Start */
|
||||
/* +Directory Sectors Data Sector Start */
|
||||
/* */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* media_ptr Media control block pointer */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* lx_nor_flash_close Close NOR flash manager */
|
||||
/* lx_nor_flash_open Open NOR flash manager */
|
||||
/* lx_nor_flash_sector_read Read a NOR sector */
|
||||
/* lx_nor_flash_sector_release Release a NOR sector */
|
||||
/* lx_nor_flash_sector_write Write a NOR sector */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* FileX System Functions */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
VOID _fx_nor_flash_simulator_driver(FX_MEDIA *media_ptr)
|
||||
{
|
||||
|
||||
UCHAR *source_buffer;
|
||||
UCHAR *destination_buffer;
|
||||
ULONG logical_sector;
|
||||
ULONG i;
|
||||
UINT status;
|
||||
|
||||
|
||||
/* There are several useful/important pieces of information contained in the media
|
||||
structure, some of which are supplied by FileX and others are for the driver to
|
||||
setup. The following is a summary of the necessary FX_MEDIA structure members:
|
||||
|
||||
FX_MEDIA Member Meaning
|
||||
|
||||
fx_media_driver_request FileX request type. Valid requests from FileX are
|
||||
as follows:
|
||||
|
||||
FX_DRIVER_READ
|
||||
FX_DRIVER_WRITE
|
||||
FX_DRIVER_FLUSH
|
||||
FX_DRIVER_ABORT
|
||||
FX_DRIVER_INIT
|
||||
FX_DRIVER_BOOT_READ
|
||||
FX_DRIVER_RELEASE_SECTORS
|
||||
FX_DRIVER_BOOT_WRITE
|
||||
FX_DRIVER_UNINIT
|
||||
|
||||
fx_media_driver_status This value is RETURNED by the driver. If the
|
||||
operation is successful, this field should be
|
||||
set to FX_SUCCESS for before returning. Otherwise,
|
||||
if an error occurred, this field should be set
|
||||
to FX_IO_ERROR.
|
||||
|
||||
fx_media_driver_buffer Pointer to buffer to read or write sector data.
|
||||
This is supplied by FileX.
|
||||
|
||||
fx_media_driver_logical_sector Logical sector FileX is requesting.
|
||||
|
||||
fx_media_driver_sectors Number of sectors FileX is requesting.
|
||||
|
||||
|
||||
The following is a summary of the optional FX_MEDIA structure members:
|
||||
|
||||
FX_MEDIA Member Meaning
|
||||
|
||||
fx_media_driver_info Pointer to any additional information or memory.
|
||||
This is optional for the driver use and is setup
|
||||
from the fx_media_open call. The RAM disk uses
|
||||
this pointer for the RAM disk memory itself.
|
||||
|
||||
fx_media_driver_write_protect The DRIVER sets this to FX_TRUE when media is write
|
||||
protected. This is typically done in initialization,
|
||||
but can be done anytime.
|
||||
|
||||
fx_media_driver_free_sector_update The DRIVER sets this to FX_TRUE when it needs to
|
||||
know when clusters are released. This is important
|
||||
for FLASH wear-leveling drivers.
|
||||
|
||||
fx_media_driver_system_write FileX sets this flag to FX_TRUE if the sector being
|
||||
written is a system sector, e.g., a boot, FAT, or
|
||||
directory sector. The driver may choose to use this
|
||||
to initiate error recovery logic for greater fault
|
||||
tolerance.
|
||||
|
||||
fx_media_driver_data_sector_read FileX sets this flag to FX_TRUE if the sector(s) being
|
||||
read are file data sectors, i.e., NOT system sectors.
|
||||
|
||||
fx_media_driver_sector_type FileX sets this variable to the specific type of
|
||||
sector being read or written. The following sector
|
||||
types are identified:
|
||||
|
||||
FX_UNKNOWN_SECTOR
|
||||
FX_BOOT_SECTOR
|
||||
FX_FAT_SECTOR
|
||||
FX_DIRECTORY_SECTOR
|
||||
FX_DATA_SECTOR
|
||||
*/
|
||||
|
||||
/* Process the driver request specified in the media control block. */
|
||||
switch(media_ptr -> fx_media_driver_request)
|
||||
{
|
||||
|
||||
case FX_DRIVER_READ:
|
||||
{
|
||||
|
||||
/* Setup the destination buffer and logical sector. */
|
||||
logical_sector = media_ptr -> fx_media_driver_logical_sector;
|
||||
destination_buffer = (UCHAR *) media_ptr -> fx_media_driver_buffer;
|
||||
|
||||
/* Loop to read sectors from flash. */
|
||||
for (i = 0; i < media_ptr -> fx_media_driver_sectors; i++)
|
||||
{
|
||||
|
||||
/* Read a sector from NOR flash. */
|
||||
status = lx_nor_flash_sector_read(&nor_flash, logical_sector, destination_buffer);
|
||||
|
||||
/* Determine if the read was successful. */
|
||||
if (status != LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Return an I/O error to FileX. */
|
||||
media_ptr -> fx_media_driver_status = FX_IO_ERROR;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Move to the next entries. */
|
||||
logical_sector++;
|
||||
destination_buffer = destination_buffer + 512;
|
||||
}
|
||||
|
||||
/* Successful driver request. */
|
||||
media_ptr -> fx_media_driver_status = FX_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
case FX_DRIVER_WRITE:
|
||||
{
|
||||
|
||||
/* Setup the source buffer and logical sector. */
|
||||
logical_sector = media_ptr -> fx_media_driver_logical_sector;
|
||||
source_buffer = (UCHAR *) media_ptr -> fx_media_driver_buffer;
|
||||
|
||||
/* Loop to write sectors to flash. */
|
||||
for (i = 0; i < media_ptr -> fx_media_driver_sectors; i++)
|
||||
{
|
||||
|
||||
/* Write a sector to NOR flash. */
|
||||
status = lx_nor_flash_sector_write(&nor_flash, logical_sector, source_buffer);
|
||||
|
||||
/* Determine if the write was successful. */
|
||||
if (status != LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Return an I/O error to FileX. */
|
||||
media_ptr -> fx_media_driver_status = FX_IO_ERROR;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Move to the next entries. */
|
||||
logical_sector++;
|
||||
source_buffer = source_buffer + 512;
|
||||
}
|
||||
|
||||
/* Successful driver request. */
|
||||
media_ptr -> fx_media_driver_status = FX_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
case FX_DRIVER_RELEASE_SECTORS:
|
||||
{
|
||||
|
||||
/* Setup the logical sector. */
|
||||
logical_sector = media_ptr -> fx_media_driver_logical_sector;
|
||||
|
||||
/* Release sectors. */
|
||||
for (i = 0; i < media_ptr -> fx_media_driver_sectors; i++)
|
||||
{
|
||||
|
||||
/* Release NOR flash sector. */
|
||||
status = lx_nor_flash_sector_release(&nor_flash, logical_sector);
|
||||
|
||||
/* Determine if the sector release was successful. */
|
||||
if (status != LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Return an I/O error to FileX. */
|
||||
media_ptr -> fx_media_driver_status = FX_IO_ERROR;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Move to the next entries. */
|
||||
logical_sector++;
|
||||
}
|
||||
|
||||
/* Successful driver request. */
|
||||
media_ptr -> fx_media_driver_status = FX_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
case FX_DRIVER_FLUSH:
|
||||
{
|
||||
|
||||
/* Return driver success. */
|
||||
media_ptr -> fx_media_driver_status = FX_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
case FX_DRIVER_ABORT:
|
||||
{
|
||||
|
||||
/* Return driver success. */
|
||||
media_ptr -> fx_media_driver_status = FX_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
case FX_DRIVER_INIT:
|
||||
{
|
||||
|
||||
/* FLASH drivers are responsible for setting several fields in the
|
||||
media structure, as follows:
|
||||
|
||||
media_ptr -> fx_media_driver_free_sector_update
|
||||
media_ptr -> fx_media_driver_write_protect
|
||||
|
||||
The fx_media_driver_free_sector_update flag is used to instruct
|
||||
FileX to inform the driver whenever sectors are not being used.
|
||||
This is especially useful for FLASH managers so they don't have
|
||||
maintain mapping for sectors no longer in use.
|
||||
|
||||
The fx_media_driver_write_protect flag can be set anytime by the
|
||||
driver to indicate the media is not writable. Write attempts made
|
||||
when this flag is set are returned as errors. */
|
||||
|
||||
/* Perform basic initialization here... since the boot record is going
|
||||
to be read subsequently and again for volume name requests. */
|
||||
|
||||
/* With flash wear leveling, FileX should tell wear leveling when sectors
|
||||
are no longer in use. */
|
||||
media_ptr -> fx_media_driver_free_sector_update = FX_TRUE;
|
||||
|
||||
/* Open the NOR flash simulation. */
|
||||
status = lx_nor_flash_open(&nor_flash, "sim nor flash", _lx_nor_flash_simulator_initialize);
|
||||
|
||||
/* Determine if the flash open was successful. */
|
||||
if (status != LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Return an I/O error to FileX. */
|
||||
media_ptr -> fx_media_driver_status = FX_IO_ERROR;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Successful driver request. */
|
||||
media_ptr -> fx_media_driver_status = FX_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
case FX_DRIVER_UNINIT:
|
||||
{
|
||||
|
||||
/* There is nothing to do in this case for the RAM driver. For actual
|
||||
devices some shutdown processing may be necessary. */
|
||||
|
||||
/* Close the NOR flash simulation. */
|
||||
status = lx_nor_flash_close(&nor_flash);
|
||||
|
||||
/* Determine if the flash close was successful. */
|
||||
if (status != LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Return an I/O error to FileX. */
|
||||
media_ptr -> fx_media_driver_status = FX_IO_ERROR;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Successful driver request. */
|
||||
media_ptr -> fx_media_driver_status = FX_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
case FX_DRIVER_BOOT_READ:
|
||||
{
|
||||
|
||||
/* Read the boot record and return to the caller. */
|
||||
|
||||
/* Setup the destination buffer. */
|
||||
destination_buffer = (UCHAR *) media_ptr -> fx_media_driver_buffer;
|
||||
|
||||
/* Read boot sector from NOR flash. */
|
||||
status = lx_nor_flash_sector_read(&nor_flash, 0, destination_buffer);
|
||||
|
||||
/* For NOR driver, determine if the boot record is valid. */
|
||||
if ((destination_buffer[0] != (UCHAR) 0xEB) ||
|
||||
(destination_buffer[1] != (UCHAR) 0x34) ||
|
||||
(destination_buffer[2] != (UCHAR) 0x90))
|
||||
{
|
||||
|
||||
/* Invalid boot record, return an error! */
|
||||
media_ptr -> fx_media_driver_status = FX_MEDIA_INVALID;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Determine if the boot read was successful. */
|
||||
if (status != LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Return an I/O error to FileX. */
|
||||
media_ptr -> fx_media_driver_status = FX_IO_ERROR;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Successful driver request. */
|
||||
media_ptr -> fx_media_driver_status = FX_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
case FX_DRIVER_BOOT_WRITE:
|
||||
{
|
||||
|
||||
/* Write the boot record and return to the caller. */
|
||||
|
||||
/* Setup the source buffer. */
|
||||
source_buffer = (UCHAR *) media_ptr -> fx_media_driver_buffer;
|
||||
|
||||
/* Write boot sector to NOR flash. */
|
||||
status = lx_nor_flash_sector_write(&nor_flash, 0, source_buffer);
|
||||
|
||||
/* Determine if the boot write was successful. */
|
||||
if (status != LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Return an I/O error to FileX. */
|
||||
media_ptr -> fx_media_driver_status = FX_IO_ERROR;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Successful driver request. */
|
||||
media_ptr -> fx_media_driver_status = FX_SUCCESS;
|
||||
break ;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
|
||||
/* Invalid driver request. */
|
||||
media_ptr -> fx_media_driver_status = FX_IO_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_256byte_ecc_check PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function checks 256 bytes of a NAND flash and ECC and */
|
||||
/* attempts to correct any single bit errors. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* page_buffer Page buffer */
|
||||
/* ecc_buffer Returned ECC buffer */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nand_flash_256byte_ecc_compute Compute ECC for 256 bytes */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* _lx_nand_flash_page_ecc_check NAND page check */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_256byte_ecc_check(UCHAR *page_buffer, UCHAR *ecc_buffer)
|
||||
{
|
||||
|
||||
INT i, j;
|
||||
UCHAR mask;
|
||||
UCHAR new_ecc_buffer[3];
|
||||
UCHAR ecc_errors[3];
|
||||
INT error_count;
|
||||
USHORT *data;
|
||||
USHORT byte;
|
||||
USHORT bit;
|
||||
ULONG correction_code;
|
||||
|
||||
|
||||
/* Clear the error count. */
|
||||
ecc_errors[0] = 0;
|
||||
ecc_errors[1] = 0;
|
||||
ecc_errors[2] = 0;
|
||||
|
||||
/* Calculate a new ECC for the 256 byte buffer. */
|
||||
_lx_nand_flash_256byte_ecc_compute(page_buffer, new_ecc_buffer);
|
||||
|
||||
/* Clear error count. */
|
||||
error_count = 0;
|
||||
|
||||
/* Loop through the ECC bytes to determine if there is an error in the page. */
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
|
||||
/* Check for differences in the ECCs. */
|
||||
ecc_errors[i] = new_ecc_buffer[i] ^ ecc_buffer[i];
|
||||
|
||||
/* Are there any errors? */
|
||||
if (ecc_errors[i])
|
||||
{
|
||||
|
||||
/* Accumulate the count of set bits. */
|
||||
mask = 1;
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
|
||||
/* Is this bit set? */
|
||||
if (ecc_errors[i] & mask)
|
||||
{
|
||||
|
||||
/* Yes, increment the count. */
|
||||
error_count++;
|
||||
}
|
||||
|
||||
/* Move mask to next bit. */
|
||||
mask = (UCHAR) ((mask << 1) & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if there are any errors. */
|
||||
if (error_count == 0)
|
||||
{
|
||||
|
||||
/* Everything is okay, return success. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
/* Was a correctable error discovered? */
|
||||
else if (error_count == 11)
|
||||
{
|
||||
|
||||
/* Initialize bit and byte offset values. */
|
||||
bit = 0;
|
||||
byte = 0;
|
||||
|
||||
/* Setup the data pointer. */
|
||||
data = (USHORT *) page_buffer;
|
||||
|
||||
/* Calculate the 32-bit correction code. */
|
||||
correction_code = (ULONG) (ecc_errors[2] << 16) | (ULONG)(ecc_errors[1] << 8) | (ULONG)ecc_errors[0];
|
||||
|
||||
/* Unpack the correction code. */
|
||||
byte = (USHORT) ((byte | ((correction_code >> (21+2)) & 1) << 6) & 0xFFFF);
|
||||
byte = (USHORT) ((byte | ((correction_code >> (19+2)) & 1) << 5) & 0xFFFF);
|
||||
byte = (USHORT) ((byte | ((correction_code >> (17+2)) & 1) << 4) & 0xFFFF);
|
||||
byte = (USHORT) ((byte | ((correction_code >> (15+2)) & 1) << 3) & 0xFFFF);
|
||||
byte = (USHORT) ((byte | ((correction_code >> (13+2)) & 1) << 2) & 0xFFFF);
|
||||
byte = (USHORT) ((byte | ((correction_code >> (11+2)) & 1) << 1) & 0xFFFF);
|
||||
byte = (USHORT) ((byte | ((correction_code >> (9+2)) & 1) << 0) & 0xFFFF);
|
||||
bit = (USHORT) ((bit | ((correction_code >> (7+2)) & 1) << 3) & 0xFFFF);
|
||||
bit = (USHORT) ((bit | ((correction_code >> (5+2)) & 1) << 2) & 0xFFFF);
|
||||
bit = (USHORT) ((bit | ((correction_code >> (3+2)) & 1) << 1) & 0xFFFF);
|
||||
bit = (USHORT) ((bit | ((correction_code >> (1+2)) & 1) << 0) & 0xFFFF);
|
||||
|
||||
/* Fix the error. */
|
||||
data[byte] = (USHORT) ((data[byte] ^ (1 << bit)) & 0xFFFF);
|
||||
|
||||
/* Return an error corrected status. */
|
||||
return(LX_NAND_ERROR_CORRECTED);
|
||||
}
|
||||
|
||||
/* Otherwise, an unrecoverable ECC or data error is present. */
|
||||
else
|
||||
{
|
||||
|
||||
/* Return an error. */
|
||||
return(LX_NAND_ERROR_NOT_CORRECTED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_256byte_ecc_compute PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function computes the ECC for 256 bytes of a NAND flash page. */
|
||||
/* The resulting ECC code is returned in 3 bytes. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* page_buffer Page buffer */
|
||||
/* ecc_buffer Returned ECC buffer */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* _lx_nand_flash_page_ecc_compute NAND page ECC compute */
|
||||
/* _lx_nand_flash_256byte_ecc_check Check 256 bytes and ECC */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_256byte_ecc_compute(UCHAR *page_buffer, UCHAR *ecc_buffer)
|
||||
{
|
||||
|
||||
USHORT i, j;
|
||||
USHORT *data;
|
||||
USHORT bits, mask;
|
||||
USHORT bit_parity;
|
||||
USHORT even_bit_parity;
|
||||
USHORT odd_bit_parity;
|
||||
USHORT even_byte_parity;
|
||||
USHORT odd_byte_parity;
|
||||
|
||||
|
||||
/* Initialize local variables. */
|
||||
bit_parity = 0;
|
||||
even_bit_parity = 0;
|
||||
odd_bit_parity = 0;
|
||||
even_byte_parity = 0;
|
||||
odd_byte_parity = 0;
|
||||
|
||||
/* Initialize the return ECC code area. */
|
||||
ecc_buffer[0]= 0;
|
||||
ecc_buffer[1]= 0;
|
||||
ecc_buffer[2]= 0;
|
||||
|
||||
/* Setup a 16-bit pointer to the buffer area. */
|
||||
data = (USHORT *) page_buffer;
|
||||
|
||||
/* Loop through the 256 byte buffer, 16 bits at a time. */
|
||||
for (i = 0; i < 128; i++)
|
||||
{
|
||||
|
||||
/* Compute the ECC value. */
|
||||
bit_parity = bit_parity ^ data[i];
|
||||
|
||||
/* Now count the bits in the current data word. */
|
||||
bits = 0;
|
||||
mask = 1;
|
||||
for (j = 0; j < 16; j++)
|
||||
{
|
||||
|
||||
/* Is the bit set? */
|
||||
if (data[i] & mask)
|
||||
{
|
||||
|
||||
/* Yes, increment the bit count. */
|
||||
bits++;
|
||||
}
|
||||
|
||||
/* Move the mask to the next bit. */
|
||||
mask = (USHORT) ((mask << 1) & 0xFFFF);
|
||||
}
|
||||
|
||||
/* Determine if the number of bits is odd. */
|
||||
if ((bits & 1) == 1)
|
||||
{
|
||||
|
||||
/* Odd number of bits. Adjust the odd/even byte parity. */
|
||||
even_byte_parity = (USHORT) ((even_byte_parity ^ (0xffff - i)) & 0xFFFF);
|
||||
odd_byte_parity = odd_byte_parity ^ i;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now look for bits set in the bit parity. */
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
|
||||
/* Is the bit set? */
|
||||
if (bit_parity & 1)
|
||||
{
|
||||
|
||||
/* Yes, adjust the odd even byte parity. */
|
||||
even_bit_parity = (USHORT) ((even_bit_parity ^ (15 - i)) & 0xFFFF);
|
||||
odd_bit_parity = odd_bit_parity ^ i;
|
||||
}
|
||||
|
||||
/* Look at next bit position. */
|
||||
bit_parity = bit_parity >> 1;
|
||||
}
|
||||
|
||||
/* At this point, we need to pack the 22 ECC bits into the 3 byte return area. */
|
||||
|
||||
/* Pack bit 21. */
|
||||
ecc_buffer[(21+2)/8] = ((UCHAR)(ecc_buffer[(21+2)/8] | ((odd_byte_parity >> 6) & 1) << (21+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 20. */
|
||||
ecc_buffer[(20+2)/8] = ((UCHAR)(ecc_buffer[(20+2)/8] | ((even_byte_parity >> 6) & 1) << (20+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 19. */
|
||||
ecc_buffer[(19+2)/8] = ((UCHAR)(ecc_buffer[(19+2)/8] | ((odd_byte_parity >> 5) & 1) << (19+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 18. */
|
||||
ecc_buffer[(18+2)/8] = ((UCHAR)(ecc_buffer[(18+2)/8] | ((even_byte_parity >> 5) & 1) << (18+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 17. */
|
||||
ecc_buffer[(17+2)/8] = ((UCHAR)(ecc_buffer[(17+2)/8] | ((odd_byte_parity >> 4) & 1) << (17+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 16. */
|
||||
ecc_buffer[(16+2)/8] = ((UCHAR)(ecc_buffer[(16+2)/8] | ((even_byte_parity >> 4) & 1) << (16+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 15. */
|
||||
ecc_buffer[(15+2)/8] = ((UCHAR)(ecc_buffer[(15+2)/8] | ((odd_byte_parity >> 3) & 1) << (15+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 14. */
|
||||
ecc_buffer[(14+2)/8] = ((UCHAR)(ecc_buffer[(14+2)/8] | ((even_byte_parity >> 3) & 1) << (14+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 13. */
|
||||
ecc_buffer[(13+2)/8] = ((UCHAR)(ecc_buffer[(13+2)/8] | ((odd_byte_parity >> 2) & 1) << (13+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 12. */
|
||||
ecc_buffer[(12+2)/8] = ((UCHAR)(ecc_buffer[(12+2)/8] | ((even_byte_parity >> 2) & 1) << (12+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 11. */
|
||||
ecc_buffer[(11+2)/8] = ((UCHAR)(ecc_buffer[(11+2)/8] | ((odd_byte_parity >> 1) & 1) << (11+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 10. */
|
||||
ecc_buffer[(10+2)/8] = ((UCHAR)(ecc_buffer[(10+2)/8] | ((even_byte_parity >> 1) & 1) << (10+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 9. */
|
||||
ecc_buffer[(9+2)/8] = ((UCHAR)(ecc_buffer[(9+2)/8] | ((odd_byte_parity >> 0) & 1) << (9+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 8. */
|
||||
ecc_buffer[(8+2)/8] = ((UCHAR)(ecc_buffer[(8+2)/8] | ((even_byte_parity >> 0) & 1) << (8+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 7. */
|
||||
ecc_buffer[(7+2)/8] = ((UCHAR)(ecc_buffer[(7+2)/8] | ((odd_bit_parity >> 3) & 1) << (7+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 6. */
|
||||
ecc_buffer[(6+2)/8] = ((UCHAR)(ecc_buffer[(6+2)/8] | ((even_bit_parity >> 3) & 1) << (6+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 5. */
|
||||
ecc_buffer[(5+2)/8] = ((UCHAR)(ecc_buffer[(5+2)/8] | ((odd_bit_parity >> 2) & 1) << (5+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 4. */
|
||||
ecc_buffer[(4+2)/8] = ((UCHAR)(ecc_buffer[(4+2)/8] | ((even_bit_parity >> 2) & 1) << (4+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 3. */
|
||||
ecc_buffer[(3+2)/8] = ((UCHAR)(ecc_buffer[(3+2)/8] | ((odd_bit_parity >> 1) & 1) << (3+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 2. */
|
||||
ecc_buffer[(2+2)/8] = ((UCHAR)(ecc_buffer[(2+2)/8] | ((even_bit_parity >> 1) & 1) << (2+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 1. */
|
||||
ecc_buffer[(1+2)/8] = ((UCHAR)(ecc_buffer[(1+2)/8] | ((odd_bit_parity >> 0) & 1) << (1+2)%8) & 0xFF);
|
||||
|
||||
/* Pack bit 0. */
|
||||
ecc_buffer[(0+2)/8] = ((UCHAR)(ecc_buffer[(0+2)/8] | ((even_bit_parity >> 0) & 1) << (0+2)%8) & 0xFF);
|
||||
|
||||
/* Return success! */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_block_full_update PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function writes the list of page to logical sector mapping */
|
||||
/* when the block is full. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* block Block that is full */
|
||||
/* erase_count Block erase count */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nand_flash_driver_write Driver flash page write */
|
||||
/* _lx_nand_flash_driver_extra_bytes_get Get extra bytes from spare */
|
||||
/* _lx_nand_flash_driver_extra_bytes_set Set extra bytes from spare */
|
||||
/* _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 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_block_full_update(LX_NAND_FLASH *nand_flash, ULONG block, ULONG erase_count)
|
||||
{
|
||||
|
||||
LX_NAND_PAGE_EXTRA_INFO extra_info;
|
||||
UINT status;
|
||||
ULONG i;
|
||||
ULONG *list_ptr;
|
||||
|
||||
|
||||
/* Setup pointer to internal buffer. */
|
||||
list_ptr = nand_flash -> lx_nand_flash_page_buffer;
|
||||
|
||||
/* Set the internal buffer of all ones. */
|
||||
for (i = 0; i < nand_flash -> lx_nand_flash_words_per_page; i++)
|
||||
{
|
||||
|
||||
/* Set the buffer to all ones. */
|
||||
list_ptr[i] = LX_ALL_ONES;
|
||||
}
|
||||
|
||||
/* Set the initial word to the erase count (this is what is already there!). */
|
||||
list_ptr[0] = erase_count;
|
||||
|
||||
/* Now walk through the pages in the block to build the list of mapped logical
|
||||
sectors to pages. */
|
||||
i = 1;
|
||||
while (i < nand_flash -> lx_nand_flash_pages_per_block)
|
||||
{
|
||||
|
||||
/* Read the extra bytes of each page to pickup the associated mapping. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, block, i, (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, i);
|
||||
|
||||
/* Return the error... no point in continuing. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Determine if the extra info indicates the page has not yet been allocated. */
|
||||
if (extra_info.lx_nand_page_extra_info_logical_sector == LX_NAND_PAGE_FREE)
|
||||
{
|
||||
|
||||
/* Block not full, get out of the loop. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Save the mapping. */
|
||||
list_ptr[i] = extra_info.lx_nand_page_extra_info_logical_sector;
|
||||
|
||||
/* Move to next entry. */
|
||||
i++;
|
||||
}
|
||||
|
||||
/* Check for a full block. */
|
||||
if (i == nand_flash -> lx_nand_flash_pages_per_block)
|
||||
{
|
||||
|
||||
/* Yes, the block is full. */
|
||||
|
||||
/* Place the list complete marker so we know the list is intact. */
|
||||
list_ptr[i] = LX_NAND_PAGE_LIST_VALID;
|
||||
|
||||
/* At this point we can write the buffer to page 0 of the block. */
|
||||
|
||||
/* Write the mapping list to page 0. */
|
||||
status = _lx_nand_flash_driver_write(nand_flash, block, 0, list_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);
|
||||
|
||||
/* Return the error... no point in continuing. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Write the extra bytes for page 0. */
|
||||
extra_info.lx_nand_page_extra_info_logical_sector = ((ULONG) LX_NAND_PAGE_FREE) & ~(((ULONG) LX_NAND_BLOCK_EMPTY) | ((ULONG) LX_NAND_BLOCK_FULL));
|
||||
status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, 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, block, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return status. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,369 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_block_obsoleted_check PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function checks to see if the specified block is completely */
|
||||
/* obsoleted. If so, the block is reclaimed (erased) and made */
|
||||
/* available. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* block Block number to check */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nand_flash_driver_block_status_get */
|
||||
/* Driver block status */
|
||||
/* _lx_nand_flash_driver_block_erase Driver block erase */
|
||||
/* _lx_nand_flash_driver_extra_bytes_get Driver get extra bytes */
|
||||
/* _lx_nand_flash_driver_block_status_set */
|
||||
/* Driver set extra bytes */
|
||||
/* _lx_nand_flash_driver_read Driver page read */
|
||||
/* _lx_nand_flash_driver_write Driver page write */
|
||||
/* _lx_nand_flash_block_reclaim Reclaim one block */
|
||||
/* _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 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
VOID _lx_nand_flash_block_obsoleted_check(LX_NAND_FLASH *nand_flash, ULONG block)
|
||||
{
|
||||
|
||||
LX_NAND_PAGE_EXTRA_INFO extra_info;
|
||||
UCHAR block_status;
|
||||
ULONG *page_word_ptr;
|
||||
ULONG erase_count;
|
||||
ULONG j;
|
||||
UINT status;
|
||||
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Simply return. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Is this block bad? */
|
||||
if (block_status != LX_NAND_GOOD_BLOCK)
|
||||
{
|
||||
|
||||
/* Yes, this block is bad. */
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, block, 0);
|
||||
|
||||
/* Simply return. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read the extra bytes of page 0. This will tell us if the page has any valid mappings. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, 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, block, 0);
|
||||
|
||||
/* Simply return. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Determine if the block is full, since it must be full first before it can be
|
||||
completely obsoleted. */
|
||||
if ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_BLOCK_FULL) != 0)
|
||||
{
|
||||
|
||||
/* This block is not full, therefore it cannot be obsoleted. */
|
||||
|
||||
/* Simply return. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Setup pointer to internal buffer. */
|
||||
page_word_ptr = nand_flash -> lx_nand_flash_page_buffer;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
|
||||
/* Simply return. */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if we have a valid logical sector mapping 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))
|
||||
{
|
||||
|
||||
/* Yes, we have a valid logical sector mapping list in page 0. */
|
||||
|
||||
/* Traverse the list to look for the logical sector. */
|
||||
for (j = 1; j < nand_flash -> lx_nand_flash_pages_per_block; j++)
|
||||
{
|
||||
|
||||
/* Is this entry valid? */
|
||||
if (page_word_ptr[j] & LX_NAND_PAGE_VALID)
|
||||
{
|
||||
|
||||
/* Read in the actual page entry to make sure this logical sector mapping is still valid. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, block, j, (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, j);
|
||||
|
||||
/* Simply return. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Determine if the actual page is still valid. */
|
||||
if (extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_PAGE_VALID)
|
||||
{
|
||||
|
||||
/* If so, we found a valid page so the block isn't completely obsoleted. */
|
||||
|
||||
/* Simply return. */
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Pickup the erase count. */
|
||||
erase_count = page_word_ptr[0];
|
||||
|
||||
/* Determine if we need to reclaim blocks to balance out the erase counts. */
|
||||
if ((erase_count == nand_flash -> lx_nand_flash_maximum_erase_count) &&
|
||||
((nand_flash -> lx_nand_flash_maximum_erase_count - nand_flash -> lx_nand_flash_minimum_erase_count) >= LX_NAND_FLASH_MAX_ERASE_COUNT_DELTA))
|
||||
{
|
||||
|
||||
/* Attempt to reclaim one block. */
|
||||
_lx_nand_flash_block_reclaim(nand_flash);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* At this point we can reclaim the block. */
|
||||
|
||||
/* Write the erased started indication. */
|
||||
page_word_ptr[0] = LX_BLOCK_ERASE_STARTED;
|
||||
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);
|
||||
}
|
||||
|
||||
/* Erase the entire block. */
|
||||
status = _lx_nand_flash_driver_block_erase(nand_flash, block, erase_count+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);
|
||||
}
|
||||
|
||||
/* Increment the bad block count. */
|
||||
nand_flash -> lx_nand_flash_bad_blocks++;
|
||||
|
||||
/* Update number of obsolete pages but not free pages since this block is now bad. */
|
||||
nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages - (nand_flash -> lx_nand_flash_pages_per_block - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Increment the erase count. */
|
||||
erase_count++;
|
||||
|
||||
/* Determine if the new erase count exceeds the maximum. */
|
||||
if (erase_count > ((ULONG) LX_BLOCK_ERASE_COUNT_MAX))
|
||||
{
|
||||
|
||||
/* Yes, erase count is in overflow. Stay at the maximum count. */
|
||||
erase_count = ((ULONG) LX_BLOCK_ERASE_COUNT_MAX);
|
||||
}
|
||||
|
||||
/* Determine if we need to update the maximum erase count. */
|
||||
if (erase_count > nand_flash -> lx_nand_flash_maximum_erase_count)
|
||||
{
|
||||
|
||||
/* Yes, a new maximum is present. */
|
||||
nand_flash -> lx_nand_flash_maximum_erase_count = erase_count;
|
||||
|
||||
/* Determine if the search pointer is on this block. */
|
||||
if (nand_flash -> lx_nand_flash_free_block_search == block)
|
||||
{
|
||||
|
||||
/* Move to next block. */
|
||||
nand_flash -> lx_nand_flash_free_block_search++;
|
||||
|
||||
/* Check for wrap condition. */
|
||||
if (nand_flash -> lx_nand_flash_free_block_search >= nand_flash -> lx_nand_flash_total_blocks)
|
||||
{
|
||||
|
||||
/* Reset to the beginning block. */
|
||||
nand_flash -> lx_nand_flash_free_block_search = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the buffer to all ones. */
|
||||
for (j = 0; j < nand_flash -> lx_nand_flash_words_per_page; j++)
|
||||
{
|
||||
|
||||
/* Set word to all ones. */
|
||||
page_word_ptr[j] = LX_ALL_ONES;
|
||||
}
|
||||
|
||||
/* Now store the erase count. */
|
||||
page_word_ptr[0] = (erase_count | LX_BLOCK_ERASED);
|
||||
|
||||
/* Write the 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 to see if the write was successful. */
|
||||
if (status == LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Now store the erase count. */
|
||||
page_word_ptr[0] = erase_count;
|
||||
|
||||
/* Write the 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);
|
||||
|
||||
/* Simply return. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Increment the bad block count. */
|
||||
nand_flash -> lx_nand_flash_bad_blocks++;
|
||||
|
||||
/* Update number of obsolete pages but not free pages since this block is now bad. */
|
||||
nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages - (nand_flash -> lx_nand_flash_pages_per_block - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Update parameters of this flash. */
|
||||
nand_flash -> lx_nand_flash_free_pages = nand_flash -> lx_nand_flash_free_pages + (nand_flash -> lx_nand_flash_pages_per_block - 1);
|
||||
nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages - (nand_flash -> lx_nand_flash_pages_per_block - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,631 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_block_reclaim PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function reclaims one block from the NAND flash. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nand_flash_driver_block_erase Driver erase block */
|
||||
/* _lx_nand_flash_driver_block_status_set */
|
||||
/* Set block status to bad */
|
||||
/* _lx_nand_flash_driver_extra_bytes_get NAND flash get extra bytes */
|
||||
/* _lx_nand_flash_driver_extra_bytes_set NAND flash set extra bytes */
|
||||
/* _lx_nand_flash_driver_write Driver flash page write */
|
||||
/* _lx_nand_flash_driver_read Driver flash page read */
|
||||
/* _lx_nand_flash_block_full_update Update page 0 with list of */
|
||||
/* mapped pages */
|
||||
/* _lx_nand_flash_next_block_to_erase_find */
|
||||
/* Find next block to erase */
|
||||
/* _lx_nand_flash_physical_page_allocate Allocate new page */
|
||||
/* _lx_nand_flash_sector_mapping_cache_invalidate */
|
||||
/* Invalidate cache entry */
|
||||
/* _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 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_block_reclaim(LX_NAND_FLASH *nand_flash)
|
||||
{
|
||||
|
||||
LX_NAND_PAGE_EXTRA_INFO old_extra_info;
|
||||
LX_NAND_PAGE_EXTRA_INFO new_extra_info;
|
||||
ULONG *page_word_ptr;
|
||||
ULONG i;
|
||||
ULONG erase_block;
|
||||
ULONG erase_count;
|
||||
ULONG mapped_pages;
|
||||
ULONG obsolete_pages;
|
||||
ULONG free_pages;
|
||||
ULONG logical_sector;
|
||||
ULONG new_block;
|
||||
ULONG new_page;
|
||||
ULONG new_erase_count;
|
||||
UINT status;
|
||||
|
||||
|
||||
/* Increment the reclaim attempts counter. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_block_reclaim_attempts++;
|
||||
|
||||
/* Determine the next block to erase. */
|
||||
_lx_nand_flash_next_block_to_erase_find(nand_flash, &erase_block, &erase_count, &mapped_pages, &obsolete_pages);
|
||||
|
||||
/* Setup pointer to page memory. */
|
||||
page_word_ptr = nand_flash -> lx_nand_flash_page_buffer;
|
||||
|
||||
/* Determine if the search pointer is set for this block. */
|
||||
if (nand_flash -> lx_nand_flash_free_block_search == erase_block)
|
||||
{
|
||||
|
||||
/* Ensure the search block is not the block we are trying to free. */
|
||||
nand_flash -> lx_nand_flash_free_block_search = erase_block + 1;
|
||||
|
||||
/* Check for wrap condition. */
|
||||
if (nand_flash -> lx_nand_flash_free_block_search >= nand_flash -> lx_nand_flash_total_blocks)
|
||||
nand_flash -> lx_nand_flash_free_block_search = 0;
|
||||
}
|
||||
|
||||
/* Determine if this block is completely obsolete. */
|
||||
if (obsolete_pages == nand_flash -> lx_nand_flash_pages_per_block)
|
||||
{
|
||||
|
||||
/* Read page 0 of the block, which has the erase count in the first 4 bytes. */
|
||||
status = _lx_nand_flash_driver_read(nand_flash, erase_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, erase_block, 0);
|
||||
}
|
||||
|
||||
/* Write the erased started indication. */
|
||||
page_word_ptr[0] = LX_BLOCK_ERASE_STARTED;
|
||||
status = _lx_nand_flash_driver_write(nand_flash, erase_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, erase_block, 0);
|
||||
}
|
||||
|
||||
/* Erase the entire block. */
|
||||
status = _lx_nand_flash_driver_block_erase(nand_flash, erase_block, erase_count+1);
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, erase_block, 0);
|
||||
|
||||
/* Attempt to mark this block as bad. */
|
||||
status = _lx_nand_flash_driver_block_status_set(nand_flash, erase_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, erase_block, 0);
|
||||
}
|
||||
|
||||
/* Increment the bad block count. */
|
||||
nand_flash -> lx_nand_flash_bad_blocks++;
|
||||
|
||||
/* Update number of obsolete pages but not free pages since this block is now bad. */
|
||||
nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages - obsolete_pages;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Increment the erase count. */
|
||||
erase_count++;
|
||||
|
||||
/* Determine if the new erase count exceeds the maximum. */
|
||||
if (erase_count > ((ULONG) LX_BLOCK_ERASE_COUNT_MAX))
|
||||
{
|
||||
|
||||
/* Yes, erase count is in overflow. Stay at the maximum count. */
|
||||
erase_count = ((ULONG) LX_BLOCK_ERASE_COUNT_MAX);
|
||||
}
|
||||
|
||||
/* Determine if we need to update the maximum erase count. */
|
||||
if (erase_count > nand_flash -> lx_nand_flash_maximum_erase_count)
|
||||
{
|
||||
|
||||
/* Yes, a new maximum is present. */
|
||||
nand_flash -> lx_nand_flash_maximum_erase_count = erase_count;
|
||||
}
|
||||
|
||||
/* Set the buffer to all ones. */
|
||||
for (i = 0; i < nand_flash -> lx_nand_flash_words_per_page; i++)
|
||||
{
|
||||
|
||||
/* Set word to all ones. */
|
||||
page_word_ptr[i] = LX_ALL_ONES;
|
||||
}
|
||||
|
||||
/* Now store the erase count. */
|
||||
page_word_ptr[0] = (erase_count | LX_BLOCK_ERASED);
|
||||
|
||||
/* Write the erase count for the block. */
|
||||
status = _lx_nand_flash_driver_write(nand_flash, erase_block, 0, page_word_ptr, LX_NAND_ERASE_COUNT_WRITE_SIZE);
|
||||
|
||||
/* Check the status. */
|
||||
if (status == LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Now store the erase count. */
|
||||
page_word_ptr[0] = erase_count;
|
||||
|
||||
/* Write the erase count for the block. */
|
||||
status = _lx_nand_flash_driver_write(nand_flash, erase_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, erase_block, 0);
|
||||
|
||||
/* Attempt to mark this block as bad. */
|
||||
status = _lx_nand_flash_driver_block_status_set(nand_flash, erase_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, erase_block, 0);
|
||||
}
|
||||
|
||||
/* Increment the bad block count. */
|
||||
nand_flash -> lx_nand_flash_bad_blocks++;
|
||||
|
||||
/* Update number of obsolete pages but not free pages since this block is now bad. */
|
||||
nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages - obsolete_pages;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Update parameters of this flash. */
|
||||
nand_flash -> lx_nand_flash_free_pages = nand_flash -> lx_nand_flash_free_pages + obsolete_pages;
|
||||
nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages - obsolete_pages;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Calculate the number of free pages in this block. */
|
||||
free_pages = nand_flash -> lx_nand_flash_pages_per_block - (obsolete_pages + mapped_pages);
|
||||
|
||||
/* Determine if there are enough free pages outside of this block to reclaim this block. */
|
||||
if (mapped_pages <= (nand_flash -> lx_nand_flash_free_pages - free_pages))
|
||||
{
|
||||
|
||||
/* Now search through the list to find mapped logical sectors to move. */
|
||||
for (i = 1; i < nand_flash -> lx_nand_flash_pages_per_block; i++)
|
||||
{
|
||||
|
||||
/* Read the logical sector mapping for this page. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, erase_block, i, (UCHAR *) &old_extra_info, sizeof(old_extra_info));
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, erase_block, i);
|
||||
|
||||
/* Return the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Determine if the entry hasn't been used. */
|
||||
if (old_extra_info.lx_nand_page_extra_info_logical_sector == LX_NAND_PAGE_FREE)
|
||||
{
|
||||
|
||||
/* Since allocations are done sequentially in the block, we know nothing
|
||||
else exists after this point. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Is this entry mapped? */
|
||||
if (old_extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_PAGE_VALID)
|
||||
{
|
||||
|
||||
/* Pickup the logical sector associated with this mapped page. */
|
||||
logical_sector = old_extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK;
|
||||
|
||||
/* Invalidate the old logical sector mapping cache entry. */
|
||||
_lx_nand_flash_sector_mapping_cache_invalidate(nand_flash, logical_sector);
|
||||
|
||||
/* Allocate a new page for this write. */
|
||||
_lx_nand_flash_physical_page_allocate(nand_flash, &new_block, &new_page, &new_erase_count);
|
||||
|
||||
/* Check to see if the new block is the same as the block we are trying to reclaim. */
|
||||
if (new_block == erase_block)
|
||||
{
|
||||
|
||||
/* Yes, the new page was found in the block to be erased. Simply move the search pointer
|
||||
to the block after the erase block and search for another page from there. */
|
||||
nand_flash -> lx_nand_flash_free_block_search = erase_block + 1;
|
||||
|
||||
/* Check for wrap condition. */
|
||||
if (nand_flash -> lx_nand_flash_free_block_search >= nand_flash -> lx_nand_flash_total_blocks)
|
||||
nand_flash -> lx_nand_flash_free_block_search = 0;
|
||||
|
||||
/* Allocate new page again. */
|
||||
_lx_nand_flash_physical_page_allocate(nand_flash, &new_block, &new_page, &new_erase_count);
|
||||
|
||||
/* Check again for the new page inside of the block to erase. This should be impossible, since
|
||||
we check previously if there are enough free pages outside of this block needed to reclaim
|
||||
this block. */
|
||||
if (new_block == erase_block)
|
||||
{
|
||||
|
||||
/* System error, a new page is not available outside of the erase block.
|
||||
Clear the new page so we fall through to the error handling. */
|
||||
new_page = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if the new page allocation was successful. */
|
||||
if (new_page)
|
||||
{
|
||||
|
||||
/* Yes, we were able to allocate a new page for the logical sector. */
|
||||
|
||||
/* Read the existing page into the internal buffer. */
|
||||
status = _lx_nand_flash_driver_read(nand_flash, erase_block, i, page_word_ptr, nand_flash -> lx_nand_flash_words_per_page);
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, erase_block, i);
|
||||
|
||||
/* Determine if the error is fatal. */
|
||||
if (status != LX_NAND_ERROR_CORRECTED)
|
||||
{
|
||||
|
||||
/* Return the error. */
|
||||
return(status);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the logical sector data to the new page. */
|
||||
status = _lx_nand_flash_driver_write(nand_flash, new_block, new_page, page_word_ptr, nand_flash -> lx_nand_flash_words_per_page);
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, new_block, new_page);
|
||||
|
||||
/* Return the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Now deprecate the old logical sector mapping. */
|
||||
|
||||
/* Clear bit 30, which indicates this page is being superceded. */
|
||||
old_extra_info.lx_nand_page_extra_info_logical_sector = old_extra_info.lx_nand_page_extra_info_logical_sector & ~((ULONG) LX_NAND_PAGE_SUPERCEDED);
|
||||
|
||||
/* Write the value back to the flash to clear bit 30. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, erase_block, i, (UCHAR *) &old_extra_info, sizeof(old_extra_info));
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, erase_block, i);
|
||||
|
||||
/* Return the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Now build the new mapping entry - with the not valid bit set initially. */
|
||||
new_extra_info.lx_nand_page_extra_info_logical_sector = ((ULONG) LX_NAND_PAGE_VALID) | ((ULONG) LX_NAND_PAGE_SUPERCEDED) | ((ULONG) LX_NAND_PAGE_MAPPING_NOT_VALID) | logical_sector;
|
||||
|
||||
/* Write out the new mapping entry. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, new_block, new_page, (UCHAR *) &new_extra_info, sizeof(new_extra_info));
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, new_block, new_page);
|
||||
|
||||
/* Return the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Now clear the not valid bit to make this sector mapping valid. This is done because the writing of the extra bytes itself can
|
||||
be interrupted and we need to make sure this can be detected when the flash is opened again. */
|
||||
new_extra_info.lx_nand_page_extra_info_logical_sector = new_extra_info.lx_nand_page_extra_info_logical_sector & ~((ULONG) LX_NAND_PAGE_MAPPING_NOT_VALID);
|
||||
|
||||
/* Clear the not valid bit. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, new_block, new_page, (UCHAR *) &new_extra_info, sizeof(new_extra_info));
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, new_block, new_page);
|
||||
|
||||
/* Return the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
#ifdef LX_NAND_FLASH_DIRECT_MAPPING_CACHE
|
||||
|
||||
/* 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) new_block;
|
||||
nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page = (USHORT) new_page;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Now clear bit 31, which indicates this sector is now obsoleted. */
|
||||
old_extra_info.lx_nand_page_extra_info_logical_sector = old_extra_info.lx_nand_page_extra_info_logical_sector & ~((ULONG) LX_NAND_PAGE_VALID);
|
||||
|
||||
/* Write the value back to the flash to clear bit 31. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, erase_block, i, (UCHAR *) &old_extra_info, sizeof(old_extra_info));
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, erase_block, i);
|
||||
|
||||
/* Return the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Increment the number of moved pages. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_moved_pages++;
|
||||
|
||||
/* Determine if the new page is the last page of the block. */
|
||||
if (new_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, new_block, new_erase_count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Call system error handler - the allocation should always succeed at this point. */
|
||||
_lx_nand_flash_system_error(nand_flash, LX_SYSTEM_ALLOCATION_FAILED, erase_block, i);
|
||||
|
||||
/* System error... break! */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Decrement the number of mapped pages. */
|
||||
mapped_pages--;
|
||||
|
||||
/* Determine if we are done. */
|
||||
if (mapped_pages == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read page 0 of the block, which has the erase count in the first 4 bytes. */
|
||||
status = _lx_nand_flash_driver_read(nand_flash, erase_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, erase_block, 0);
|
||||
}
|
||||
|
||||
/* Write the erased started indication. */
|
||||
page_word_ptr[0] = LX_BLOCK_ERASE_STARTED;
|
||||
status = _lx_nand_flash_driver_write(nand_flash, erase_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, erase_block, 0);
|
||||
}
|
||||
|
||||
/* Erase the entire block. */
|
||||
status = _lx_nand_flash_driver_block_erase(nand_flash, erase_block, erase_count+1);
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, erase_block, 0);
|
||||
|
||||
/* Attempt to mark this block as bad. */
|
||||
status = _lx_nand_flash_driver_block_status_set(nand_flash, erase_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, erase_block, 0);
|
||||
}
|
||||
|
||||
/* Increment the bad block count. */
|
||||
nand_flash -> lx_nand_flash_bad_blocks++;
|
||||
|
||||
/* Update number of obsolete pages but not free pages since this block is now bad. */
|
||||
nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages - obsolete_pages;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Increment the erase count. */
|
||||
erase_count++;
|
||||
|
||||
/* Determine if the new erase count exceeds the maximum. */
|
||||
if (erase_count > ((ULONG) LX_BLOCK_ERASE_COUNT_MAX))
|
||||
{
|
||||
|
||||
/* Yes, erase count is in overflow. Stay at the maximum count. */
|
||||
erase_count = ((ULONG) LX_BLOCK_ERASE_COUNT_MAX);
|
||||
}
|
||||
|
||||
/* Determine if we need to update the maximum erase count. */
|
||||
if (erase_count > nand_flash -> lx_nand_flash_maximum_erase_count)
|
||||
{
|
||||
|
||||
/* Yes, a new maximum is present. */
|
||||
nand_flash -> lx_nand_flash_maximum_erase_count = erase_count;
|
||||
}
|
||||
|
||||
/* Set the buffer to all ones. */
|
||||
for (i = 0; i < nand_flash -> lx_nand_flash_words_per_page; i++)
|
||||
{
|
||||
|
||||
/* Set word to all ones. */
|
||||
page_word_ptr[i] = LX_ALL_ONES;
|
||||
}
|
||||
|
||||
/* Now store the erase count. */
|
||||
page_word_ptr[0] = (erase_count | LX_BLOCK_ERASED);
|
||||
|
||||
/* Write the erase count for the block. */
|
||||
status = _lx_nand_flash_driver_write(nand_flash, erase_block, 0, page_word_ptr, LX_NAND_ERASE_COUNT_WRITE_SIZE);
|
||||
|
||||
/* Determine if the write was successful. */
|
||||
if (status == LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Now store the erase count. */
|
||||
page_word_ptr[0] = erase_count;
|
||||
|
||||
/* Write the erase count for the block. */
|
||||
status = _lx_nand_flash_driver_write(nand_flash, erase_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, erase_block, 0);
|
||||
|
||||
/* Attempt to mark this block as bad. */
|
||||
status = _lx_nand_flash_driver_block_status_set(nand_flash, erase_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, erase_block, 0);
|
||||
|
||||
/* Return the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Increment the bad block count. */
|
||||
nand_flash -> lx_nand_flash_bad_blocks++;
|
||||
|
||||
/* Update number of obsolete pages but not free pages since this block is now bad. */
|
||||
nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages - obsolete_pages;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Update parameters of this flash. */
|
||||
nand_flash -> lx_nand_flash_free_pages = nand_flash -> lx_nand_flash_free_pages + obsolete_pages;
|
||||
nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages - obsolete_pages;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return status. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_close PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function closes a NAND flash instance. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* tx_mutex_delete Delete 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_close(LX_NAND_FLASH *nand_flash)
|
||||
{
|
||||
|
||||
TX_INTERRUPT_SAVE_AREA
|
||||
|
||||
|
||||
/* Lockout interrupts for NAND flash close. */
|
||||
TX_DISABLE
|
||||
|
||||
/* See if the media is the only one on the media opened list. */
|
||||
if ((_lx_nand_flash_opened_ptr == nand_flash) &&
|
||||
(_lx_nand_flash_opened_ptr == nand_flash -> lx_nand_flash_open_next) &&
|
||||
(_lx_nand_flash_opened_ptr == nand_flash -> lx_nand_flash_open_previous))
|
||||
{
|
||||
|
||||
/* Only opened NAND flash, just set the opened list to NULL. */
|
||||
_lx_nand_flash_opened_ptr = LX_NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Otherwise, not the only opened NAND flash, link-up the neighbors. */
|
||||
(nand_flash -> lx_nand_flash_open_next) -> lx_nand_flash_open_previous =
|
||||
nand_flash -> lx_nand_flash_open_previous;
|
||||
(nand_flash -> lx_nand_flash_open_previous) -> lx_nand_flash_open_next =
|
||||
nand_flash -> lx_nand_flash_open_next;
|
||||
|
||||
/* See if we have to update the opened list head pointer. */
|
||||
if (_lx_nand_flash_opened_ptr == nand_flash)
|
||||
{
|
||||
|
||||
/* Yes, move the head pointer to the next opened NAND flash. */
|
||||
_lx_nand_flash_opened_ptr = nand_flash -> lx_nand_flash_open_next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Decrement the opened NAND flash counter. */
|
||||
_lx_nand_flash_opened_count--;
|
||||
|
||||
/* Finally, indicate that this NAND flash is closed. */
|
||||
nand_flash -> lx_nand_flash_state = LX_NAND_FLASH_CLOSED;
|
||||
|
||||
/* Restore interrupt posture. */
|
||||
TX_RESTORE
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Delete the thread safe mutex. */
|
||||
tx_mutex_delete(&nand_flash -> lx_nand_flash_mutex);
|
||||
#endif
|
||||
/* Return success. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_defragment PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function defragments the NAND flash. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nand_flash_block_reclaim Reclaim a NAND flash block */
|
||||
/* tx_mutex_get Get thread protection */
|
||||
/* tx_mutex_put Release thread protection */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Application Code */
|
||||
/* Internal LevelX */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_defragment(LX_NAND_FLASH *nand_flash)
|
||||
{
|
||||
|
||||
ULONG i;
|
||||
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Obtain the thread safe mutex. */
|
||||
tx_mutex_get(&nand_flash -> lx_nand_flash_mutex, TX_WAIT_FOREVER);
|
||||
#endif
|
||||
|
||||
/* Loop for max number of blocks, while there are obsolete count. */
|
||||
for (i = 0; i < nand_flash -> lx_nand_flash_total_blocks; i++)
|
||||
{
|
||||
|
||||
/* Determine if there is any more defragment work. */
|
||||
if (nand_flash -> lx_nand_flash_obsolete_pages == 0)
|
||||
break;
|
||||
|
||||
/* Call the block reclaim function to defragment. */
|
||||
_lx_nand_flash_block_reclaim(nand_flash);
|
||||
}
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return successful completion. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_driver_block_erase PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function calls the driver to erase a block. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* block Block number */
|
||||
/* erase_count Erase count for this block */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* Completion Status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* (lx_nand_flash_driver_block_erase) Driver erase block */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Internal LevelX */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_driver_block_erase(LX_NAND_FLASH *nand_flash, ULONG block, ULONG erase_count)
|
||||
{
|
||||
|
||||
ULONG cache_index;
|
||||
ULONG i;
|
||||
UINT status;
|
||||
|
||||
|
||||
/* Determine if the block status cache is enabled. */
|
||||
if (nand_flash -> lx_nand_flash_block_status_cache != LX_NULL)
|
||||
{
|
||||
|
||||
/* Save the block status value in the cache. */
|
||||
nand_flash -> lx_nand_flash_block_status_cache[block] = 0xFF;
|
||||
}
|
||||
|
||||
/* Determine if the page extra byte cache is enabled. */
|
||||
if (nand_flash -> lx_nand_flash_page_extra_bytes_cache != LX_NULL)
|
||||
{
|
||||
|
||||
/* Calculate the cache index. */
|
||||
cache_index = (block * nand_flash -> lx_nand_flash_pages_per_block);
|
||||
|
||||
/* Loop to clear the entries in the page extra bytes cache. */
|
||||
for (i = 0; i < nand_flash -> lx_nand_flash_pages_per_block; i++)
|
||||
{
|
||||
|
||||
/* Clear each cache entry. */
|
||||
nand_flash -> lx_nand_flash_page_extra_bytes_cache[cache_index+i].lx_nand_page_extra_info_logical_sector = (ULONG) 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if the page 0 cache is enabled. */
|
||||
if (nand_flash -> lx_nand_flash_page_0_cache != LX_NULL)
|
||||
{
|
||||
|
||||
/* Yes, the page 0 cache is enabled. */
|
||||
|
||||
/* Build index to page 0 cache. */
|
||||
cache_index = block * (nand_flash -> lx_nand_flash_pages_per_block + 1);
|
||||
|
||||
/* Clear the associated page 0 cache. */
|
||||
for (i = 0; i < (nand_flash -> lx_nand_flash_pages_per_block + 1); i++)
|
||||
{
|
||||
|
||||
/* Clear each cache entry. */
|
||||
nand_flash -> lx_nand_flash_page_0_cache[cache_index+i] = (ULONG) 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
/* Increment the block erases count. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_block_erases++;
|
||||
|
||||
/* Call driver erase block function. */
|
||||
status = (nand_flash -> lx_nand_flash_driver_block_erase)(block, erase_count);
|
||||
|
||||
/* Return status. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_driver_block_erased_verify PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function calls the driver to verify the block was erased. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* block Block number */
|
||||
/* page Page number */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* Completion Status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* (lx_nand_flash_driver_block_erased_verify) */
|
||||
/* Driver verify page erased */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Internal LevelX */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_driver_block_erased_verify(LX_NAND_FLASH *nand_flash, ULONG block)
|
||||
{
|
||||
|
||||
UINT status;
|
||||
|
||||
/* Increment the block erased verify count. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_block_erased_verifies++;
|
||||
|
||||
/* Call driver block erased verify function. */
|
||||
status = (nand_flash -> lx_nand_flash_driver_block_erased_verify)(block);
|
||||
|
||||
/* Return status. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_driver_block_status_get PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function calls the driver to get the block status and */
|
||||
/* updates the internal cache. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* block Block number */
|
||||
/* bad_block_flag Pointer to Bad block flag */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* Completion Status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* (lx_nand_flash_driver_block_status_get) */
|
||||
/* NAND flash block status get */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Internal LevelX */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_driver_block_status_get(LX_NAND_FLASH *nand_flash, ULONG block, UCHAR *bad_block_flag)
|
||||
{
|
||||
|
||||
UINT status;
|
||||
|
||||
|
||||
/* Determine if the block status cache is disabled. */
|
||||
if (nand_flash -> lx_nand_flash_block_status_cache == LX_NULL)
|
||||
{
|
||||
|
||||
/* Increment the block status get count. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_block_status_gets++;
|
||||
|
||||
/* Call driver block status get function. */
|
||||
status = (nand_flash -> lx_nand_flash_driver_block_status_get)(block, bad_block_flag);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Determine if the block status cache entry is valid. */
|
||||
if (nand_flash -> lx_nand_flash_block_status_cache[block] != 0)
|
||||
{
|
||||
|
||||
/* Simply return this value. */
|
||||
*bad_block_flag = nand_flash -> lx_nand_flash_block_status_cache[block];
|
||||
|
||||
/* Increment the number of status byte cache hits. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_block_status_cache_hits++;
|
||||
|
||||
/* Return successful status. */
|
||||
status = LX_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Increment the block status get count. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_block_status_gets++;
|
||||
|
||||
/* Call driver block status get function. */
|
||||
status = (nand_flash -> lx_nand_flash_driver_block_status_get)(block, bad_block_flag);
|
||||
|
||||
/* Increment the number of status byte cache misses. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_block_status_cache_misses++;
|
||||
|
||||
/* Save the block status value in the cache. */
|
||||
nand_flash -> lx_nand_flash_block_status_cache[block] = *bad_block_flag;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return status. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_driver_block_status_set PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function calls the driver to set the block status and */
|
||||
/* updates the internal cache. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* block Block number */
|
||||
/* bad_block_flag Bad block flag */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* Completion Status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* (lx_nand_flash_driver_block_status_set) */
|
||||
/* NAND flash block status set */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Internal LevelX */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_driver_block_status_set(LX_NAND_FLASH *nand_flash, ULONG block, UCHAR bad_block_flag)
|
||||
{
|
||||
|
||||
UINT status;
|
||||
|
||||
|
||||
/* Determine if the block status cache is enabled. */
|
||||
if (nand_flash -> lx_nand_flash_block_status_cache != LX_NULL)
|
||||
{
|
||||
|
||||
/* Save the block status value in the cache. */
|
||||
nand_flash -> lx_nand_flash_block_status_cache[block] = bad_block_flag;
|
||||
}
|
||||
|
||||
/* Increment the block status set count. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_block_status_sets++;
|
||||
|
||||
/* Call driver block status set function. */
|
||||
status = (nand_flash -> lx_nand_flash_driver_block_status_set)(block, bad_block_flag);
|
||||
|
||||
/* Return status. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_driver_extra_bytes_get PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function calls the driver to get the extra bytes of a NAND */
|
||||
/* page. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* block Block number */
|
||||
/* page Page number */
|
||||
/* destination Pointer to destination buffer */
|
||||
/* words Number of words to read */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* Completion Status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* (lx_nand_flash_driver_extra_bytes_get)Get extra bytes from spare */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Internal LevelX */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_driver_extra_bytes_get(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page, UCHAR *destination, UINT size)
|
||||
{
|
||||
|
||||
ULONG *source_ptr;
|
||||
ULONG *destination_ptr;
|
||||
ULONG cache_index;
|
||||
UINT status;
|
||||
|
||||
|
||||
/* Determine if the page extra bytes cache is disabled. */
|
||||
if (nand_flash -> lx_nand_flash_page_extra_bytes_cache == LX_NULL)
|
||||
{
|
||||
|
||||
/* Increment the page extra bytes get count. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_page_extra_bytes_gets++;
|
||||
|
||||
/* Call driver extra bytes get function. */
|
||||
status = (nand_flash -> lx_nand_flash_driver_extra_bytes_get)(block, page, destination, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Calculate the cache index. */
|
||||
cache_index = (block * nand_flash -> lx_nand_flash_pages_per_block) + page;
|
||||
|
||||
/* Setup the destination pointer. */
|
||||
destination_ptr = (ULONG *) destination;
|
||||
|
||||
/* Determine if this cache entry is valid. */
|
||||
if (nand_flash -> lx_nand_flash_page_extra_bytes_cache[cache_index].lx_nand_page_extra_info_logical_sector != 0)
|
||||
{
|
||||
|
||||
/* Simply return this value. */
|
||||
*destination_ptr = nand_flash -> lx_nand_flash_page_extra_bytes_cache[cache_index].lx_nand_page_extra_info_logical_sector;
|
||||
|
||||
/* Increment the number of page extra bytes cache hits. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_page_extra_bytes_cache_hits++;
|
||||
|
||||
/* Return successful status. */
|
||||
status = LX_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Increment the page extra bytes get count. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_page_extra_bytes_gets++;
|
||||
|
||||
/* Call driver extra bytes get function. */
|
||||
status = (nand_flash -> lx_nand_flash_driver_extra_bytes_get)(block, page, destination, size);
|
||||
|
||||
/* Increment the number of page extra bytes cache misses. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_page_extra_bytes_cache_misses++;
|
||||
|
||||
/* Setup destination pointer. */
|
||||
destination_ptr = &nand_flash -> lx_nand_flash_page_extra_bytes_cache[cache_index].lx_nand_page_extra_info_logical_sector;
|
||||
|
||||
/* Setup source pointer. */
|
||||
source_ptr = (ULONG *) destination;
|
||||
|
||||
/* Save the value in the page extra bytes cache. */
|
||||
*destination_ptr = *source_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return status. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_driver_extra_bytes_set PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function calls the driver extra bytes set operation and */
|
||||
/* updates the internal cache. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* block Block number */
|
||||
/* page Page number */
|
||||
/* source Pointer to source extra bytes */
|
||||
/* size Number of extra bytes */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* Completion Status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* (lx_nand_flash_driver_extra_bytes_set) */
|
||||
/* NAND flash set extra bytes */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Internal LevelX */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_driver_extra_bytes_set(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page, UCHAR *source, UINT size)
|
||||
{
|
||||
|
||||
ULONG cache_index;
|
||||
ULONG *destination_ptr;
|
||||
UINT status;
|
||||
|
||||
|
||||
/* Increment the page extra bytes set count. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_page_extra_bytes_sets++;
|
||||
|
||||
/* Call driver extra bytes set function. */
|
||||
status = (nand_flash -> lx_nand_flash_driver_extra_bytes_set)(block, page, source, size);
|
||||
|
||||
/* Determine if the page extra bytes set cache is enabled. */
|
||||
if (nand_flash -> lx_nand_flash_page_extra_bytes_cache != LX_NULL)
|
||||
{
|
||||
|
||||
/* Calculate the cache index. */
|
||||
cache_index = (block * nand_flash -> lx_nand_flash_pages_per_block) + page;
|
||||
|
||||
/* Build destination address. */
|
||||
destination_ptr = &nand_flash -> lx_nand_flash_page_extra_bytes_cache[cache_index].lx_nand_page_extra_info_logical_sector;
|
||||
|
||||
/* Now save this in the cache. */
|
||||
*destination_ptr = *((ULONG *) source);
|
||||
}
|
||||
|
||||
/* Return status. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_driver_page_erased_verify PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function calls the driver to verify the page was erased. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* block Block number */
|
||||
/* page Page number */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* Completion Status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* (lx_nand_flash_driver_page_erased_verify) */
|
||||
/* Driver verify page erased */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Internal LevelX */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_driver_page_erased_verify(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page)
|
||||
{
|
||||
|
||||
UINT status;
|
||||
|
||||
/* Increment the page erased verify count. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_page_erased_verifies++;
|
||||
|
||||
/* Call driver page erased verify function. */
|
||||
status = (nand_flash -> lx_nand_flash_driver_page_erased_verify)(block, page);
|
||||
|
||||
/* Return status. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_driver_read PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function calls the driver to read data from a page. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* block Block number */
|
||||
/* page Page number */
|
||||
/* destination Pointer to destination buffer */
|
||||
/* words Number of words to read */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* Completion Status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* (lx_nand_flash_driver_read) Driver page read */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Internal LevelX */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_driver_read(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page, ULONG *destination, ULONG words)
|
||||
{
|
||||
|
||||
ULONG cache_index;
|
||||
ULONG *source_ptr;
|
||||
ULONG *destination_ptr;
|
||||
ULONG i;
|
||||
UINT status;
|
||||
|
||||
|
||||
/* Determine if this is page 0. */
|
||||
if (page == 0)
|
||||
{
|
||||
|
||||
/* Determine if the page 0 cache is enabled. */
|
||||
if (nand_flash -> lx_nand_flash_page_0_cache != LX_NULL)
|
||||
{
|
||||
|
||||
/* Yes, the page 0 cache is enabled. */
|
||||
|
||||
/* Calculate the cache index. */
|
||||
cache_index = (block * (nand_flash -> lx_nand_flash_pages_per_block + 1));
|
||||
|
||||
/* Determine if this cache entry is valid. */
|
||||
if (nand_flash -> lx_nand_flash_page_0_cache[cache_index] != 0)
|
||||
{
|
||||
|
||||
/* Setup the destination pointer. */
|
||||
destination_ptr = (ULONG *) destination;
|
||||
|
||||
/* Setup the source pointer. */
|
||||
source_ptr = &nand_flash -> lx_nand_flash_page_0_cache[cache_index];
|
||||
|
||||
/* Simply copy the page 0 information to the destination. */
|
||||
i = 0;
|
||||
while (i < (nand_flash -> lx_nand_flash_pages_per_block + 1))
|
||||
{
|
||||
|
||||
/* Copy one word. */
|
||||
*destination_ptr++ = *source_ptr++;
|
||||
|
||||
/* Move to next word. */
|
||||
i++;
|
||||
}
|
||||
|
||||
/* Increment the number of page 0 cache hits. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_page_0_cache_hits++;
|
||||
|
||||
/* Return successful status. */
|
||||
status = LX_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Increment the page read count. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_page_reads++;
|
||||
|
||||
/* Increment the number of page 0 cache misses. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_page_0_cache_misses++;
|
||||
|
||||
/* Call driver read function. */
|
||||
status = (nand_flash -> lx_nand_flash_driver_read)(block, page, destination, words);
|
||||
|
||||
/* Setup destination pointer. */
|
||||
destination_ptr = &nand_flash -> lx_nand_flash_page_0_cache[cache_index];
|
||||
|
||||
/* Setup source pointer. */
|
||||
source_ptr = (ULONG *) destination;
|
||||
|
||||
/* Simply copy the page 0 information to the destination. */
|
||||
for (i = 0; i < (nand_flash -> lx_nand_flash_pages_per_block + 1); i++)
|
||||
{
|
||||
|
||||
/* Copy one word. */
|
||||
*destination_ptr++ = *source_ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Increment the page read count. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_page_reads++;
|
||||
|
||||
/* Call driver read function. */
|
||||
status = (nand_flash -> lx_nand_flash_driver_read)(block, page, destination, words);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Increment the page read count. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_page_reads++;
|
||||
|
||||
/* Call driver read function. */
|
||||
status = (nand_flash -> lx_nand_flash_driver_read)(block, page, destination, words);
|
||||
}
|
||||
|
||||
/* Return status. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_driver_write PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function calls the driver to write data to a NAND page. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* block Block number */
|
||||
/* page Page number */
|
||||
/* source Pointer to source buffer */
|
||||
/* words Number of words to write */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* Completion Status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* (lx_nand_flash_driver_write) Driver page write */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Internal LevelX */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_driver_write(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page, ULONG *source, ULONG words)
|
||||
{
|
||||
|
||||
ULONG cache_index;
|
||||
ULONG *source_ptr;
|
||||
ULONG *destination_ptr;
|
||||
ULONG i;
|
||||
UINT status;
|
||||
|
||||
|
||||
/* Determine if this is page 0. */
|
||||
if (page == 0)
|
||||
{
|
||||
|
||||
/* Determine if the page 0 cache is enabled. */
|
||||
if (nand_flash -> lx_nand_flash_page_0_cache != LX_NULL)
|
||||
{
|
||||
|
||||
/* Yes, the page 0 cache is enabled. */
|
||||
|
||||
/* Calculate the cache index. */
|
||||
cache_index = (block * (nand_flash -> lx_nand_flash_pages_per_block + 1));
|
||||
|
||||
/* Setup destination pointer. */
|
||||
destination_ptr = &nand_flash -> lx_nand_flash_page_0_cache[cache_index];
|
||||
|
||||
/* Setup source pointer. */
|
||||
source_ptr = (ULONG *) source;
|
||||
|
||||
/* Simply copy the page 0 information to the destination. */
|
||||
for (i = 0; i < (nand_flash -> lx_nand_flash_pages_per_block + 1); i++)
|
||||
{
|
||||
|
||||
/* Copy one word. */
|
||||
*destination_ptr++ = *source_ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Increment the page write count. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_page_writes++;
|
||||
|
||||
/* Call driver write function. */
|
||||
status = (nand_flash -> lx_nand_flash_driver_write)(block, page, source, words);
|
||||
|
||||
/* Return status. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_extended_cache_enable PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function sets up the extended NAND cache for block status, */
|
||||
/* page extra bytes, and page 0 contents. The routine will enable as */
|
||||
/* many cache capabilities as possible until the supplied memory is */
|
||||
/* exhausted. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* memory Pointer to memory for caches */
|
||||
/* size Size of memory in bytes */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* Completion Status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Internal LevelX */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_extended_cache_enable(LX_NAND_FLASH *nand_flash, VOID *memory, ULONG size)
|
||||
{
|
||||
|
||||
UCHAR *working_ptr;
|
||||
ULONG adjust_size;
|
||||
ULONG i;
|
||||
|
||||
|
||||
/* Setup the working pointer. */
|
||||
working_ptr = (UCHAR *) memory;
|
||||
|
||||
/* Check for a NULL pointer. If NULL, disable all the caches. */
|
||||
if (working_ptr == TX_NULL)
|
||||
{
|
||||
|
||||
/* Disable all extended caches. */
|
||||
nand_flash -> lx_nand_flash_block_status_cache = TX_NULL;
|
||||
nand_flash -> lx_nand_flash_page_extra_bytes_cache = TX_NULL;
|
||||
nand_flash -> lx_nand_flash_page_0_cache = TX_NULL;
|
||||
|
||||
/* Return success! */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
/* Determine if there is not enough memory for the block status cache. */
|
||||
if (size < nand_flash -> lx_nand_flash_total_blocks)
|
||||
{
|
||||
|
||||
/* Return an error since there is not enough memory for even the smallest
|
||||
cache. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* There is enough memory, setup the block status cache. */
|
||||
nand_flash -> lx_nand_flash_block_status_cache = working_ptr;
|
||||
|
||||
/* Clear the block status cache. */
|
||||
for (i = 0; i < nand_flash -> lx_nand_flash_total_blocks; i++)
|
||||
{
|
||||
|
||||
/* Clear the block status cache. */
|
||||
nand_flash -> lx_nand_flash_block_status_cache[i] = ((UCHAR) 0);
|
||||
}
|
||||
|
||||
/* Calculate the size to adjust to achieve ULONG alignment. */
|
||||
adjust_size = nand_flash -> lx_nand_flash_total_blocks + ((ULONG) (sizeof(ULONG) - 1));
|
||||
adjust_size = (adjust_size/sizeof(ULONG)) * sizeof(ULONG);
|
||||
|
||||
/* Adjust the size. */
|
||||
if (size >= adjust_size)
|
||||
{
|
||||
|
||||
/* Subtract the aligned size. */
|
||||
size = size - adjust_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Just set size to 0. */
|
||||
size = 0;
|
||||
}
|
||||
|
||||
/* Determine if there is enough memory for the page extra bytes cache for each page. */
|
||||
if (size >= (nand_flash -> lx_nand_flash_total_pages * sizeof(LX_NAND_PAGE_EXTRA_INFO)))
|
||||
{
|
||||
|
||||
/* Yes, there is enough memory for the extra bytes cache. */
|
||||
|
||||
/* First adjust the working pointer to the start of this memory. */
|
||||
working_ptr = working_ptr + adjust_size;
|
||||
|
||||
/* Setup the pointer to page extra bytes cache. */
|
||||
nand_flash -> lx_nand_flash_page_extra_bytes_cache = (LX_NAND_PAGE_EXTRA_INFO *) working_ptr;
|
||||
|
||||
/* Clear the page extra bytes cache. */
|
||||
for (i = 0; i < nand_flash -> lx_nand_flash_total_pages; i++)
|
||||
{
|
||||
|
||||
/* Clear the block status cache. */
|
||||
nand_flash -> lx_nand_flash_page_extra_bytes_cache[i].lx_nand_page_extra_info_logical_sector = ((ULONG) 0);
|
||||
}
|
||||
|
||||
/* Calculate the size to adjust to achieve ULONG alignment. */
|
||||
adjust_size = (nand_flash -> lx_nand_flash_total_pages * sizeof(LX_NAND_PAGE_EXTRA_INFO) + ((ULONG) (sizeof(ULONG) - 1)));
|
||||
adjust_size = (adjust_size/sizeof(ULONG)) * sizeof(ULONG);
|
||||
|
||||
/* Adjust the size. */
|
||||
if (size >= adjust_size)
|
||||
{
|
||||
|
||||
/* Subtract the aligned size. */
|
||||
size = size - adjust_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Just set size to 0. */
|
||||
size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if there is enough memory for the page 0 cache for each block. */
|
||||
if (size >= ((nand_flash -> lx_nand_flash_total_blocks * (nand_flash -> lx_nand_flash_pages_per_block + 1)) * sizeof(ULONG)))
|
||||
{
|
||||
|
||||
/* Yes, there is enough memory for the page 0 cache. */
|
||||
|
||||
/* First adjust the working pointer to the start of this memory. */
|
||||
working_ptr = working_ptr + adjust_size;
|
||||
|
||||
/* Setup the pointer to page 0 cache. */
|
||||
nand_flash -> lx_nand_flash_page_0_cache = (ULONG *) working_ptr;
|
||||
|
||||
/* Clear the page 0 cache. */
|
||||
for (i = 0; i < (nand_flash -> lx_nand_flash_total_blocks * (nand_flash -> lx_nand_flash_pages_per_block + 1)); i++)
|
||||
{
|
||||
|
||||
/* Clear the block status cache. */
|
||||
nand_flash -> lx_nand_flash_page_0_cache[i] = ((ULONG) 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return success. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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
|
||||
|
||||
|
||||
/* Include necessary system files. */
|
||||
|
||||
#include "lx_api.h"
|
||||
|
||||
|
||||
/* Define the NAND flash opened list head and opened count. */
|
||||
|
||||
LX_NAND_FLASH *_lx_nand_flash_opened_ptr;
|
||||
ULONG _lx_nand_flash_opened_count;
|
||||
|
||||
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* FUNCTION RELEASE */
|
||||
/* */
|
||||
/* _lx_nand_flash_initialize PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function initializes the NAND flash data structures. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Application Code */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_initialize(void)
|
||||
{
|
||||
|
||||
/* Clear open list head pointer and opened count. */
|
||||
_lx_nand_flash_opened_ptr = LX_NULL;
|
||||
_lx_nand_flash_opened_count = 0;
|
||||
|
||||
/* Return success! */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,837 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_logical_sector_find PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function attempts to find the specified logical sector in */
|
||||
/* the NAND flash. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* logical_sector Logical sector number */
|
||||
/* superceded_check Check for page being */
|
||||
/* superceded (can happen if */
|
||||
/* on interruptions of page */
|
||||
/* write) */
|
||||
/* block Destination for block */
|
||||
/* page Destination for page */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nand_flash_driver_block_status_get */
|
||||
/* Driver block status */
|
||||
/* _lx_nand_flash_driver_extra_bytes_get Driver get extra bytes */
|
||||
/* _lx_nand_flash_driver_extra_bytes_set NAND flash set extra bytes */
|
||||
/* _lx_nand_flash_driver_read Driver page read */
|
||||
/* _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 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_logical_sector_find(LX_NAND_FLASH *nand_flash, ULONG logical_sector, ULONG superceded_check, ULONG *block, ULONG *page)
|
||||
{
|
||||
|
||||
LX_NAND_PAGE_EXTRA_INFO extra_info;
|
||||
UCHAR block_status;
|
||||
ULONG *block_word_ptr;
|
||||
ULONG valid_pages;
|
||||
ULONG mapped_pages;
|
||||
ULONG total_blocks;
|
||||
ULONG total_pages;
|
||||
ULONG i, j;
|
||||
ULONG search_start;
|
||||
ULONG max_mapped_sector;
|
||||
#ifndef LX_NAND_FLASH_DIRECT_MAPPING_CACHE
|
||||
LX_NAND_SECTOR_MAPPING_CACHE_ENTRY *sector_mapping_cache_entry_ptr = LX_NULL;
|
||||
LX_NAND_SECTOR_MAPPING_CACHE_ENTRY temp_sector_mapping_cache_entry;
|
||||
#endif
|
||||
UINT status;
|
||||
|
||||
|
||||
/* Initialize the return parameters. */
|
||||
*block = (ULONG) 0;
|
||||
*page = (ULONG) 0;
|
||||
|
||||
/* Determine if there are any mapped pages. */
|
||||
if (nand_flash -> lx_nand_flash_mapped_pages == 0)
|
||||
{
|
||||
|
||||
/* No mapped pages so nothing can be found!. */
|
||||
return(LX_SECTOR_NOT_FOUND);
|
||||
}
|
||||
|
||||
/* Determine if this logical sector is greater than what has previously been mapped. */
|
||||
if (logical_sector > nand_flash -> lx_nand_flash_max_mapped_sector)
|
||||
{
|
||||
|
||||
/* Since this logical sector request is greater than what has been mapped previously,
|
||||
there is no point in searching any further. */
|
||||
|
||||
/* Return sector not found status. */
|
||||
return(LX_SECTOR_NOT_FOUND);
|
||||
}
|
||||
|
||||
/* Determine if the sector mapping cache is enabled. */
|
||||
if (nand_flash -> lx_nand_flash_sector_mapping_cache_enabled)
|
||||
{
|
||||
|
||||
#ifndef LX_NAND_FLASH_DIRECT_MAPPING_CACHE
|
||||
|
||||
/* Calculate the starting index of the sector cache for this sector entry. */
|
||||
i = (logical_sector & LX_NAND_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NAND_SECTOR_MAPPING_CACHE_DEPTH;
|
||||
|
||||
/* Build a pointer to the cache entry. */
|
||||
sector_mapping_cache_entry_ptr = &nand_flash -> lx_nand_flash_sector_mapping_cache[i];
|
||||
|
||||
/* Determine if the sector is in the sector mapping cache - assuming the depth of the sector
|
||||
mapping cache is LX_NAND_SECTOR_MAPPING_CACHE_DEPTH entries. */
|
||||
if ((sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_logical_sector) == (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID))
|
||||
{
|
||||
|
||||
/* Increment the sector mapping cache hit counter. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_sector_mapping_cache_hits++;
|
||||
|
||||
/* Yes, return the cached values associated with the sector. */
|
||||
*block = (ULONG) sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_block;
|
||||
*page = (ULONG) sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_page;
|
||||
|
||||
/* Don't move anything since we found the entry at the top. */
|
||||
|
||||
/* Return a successful status. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
else if (((sector_mapping_cache_entry_ptr + 1) -> lx_nand_sector_mapping_cache_logical_sector) == (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID))
|
||||
{
|
||||
|
||||
/* Increment the sector mapping cache hit counter. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_sector_mapping_cache_hits++;
|
||||
|
||||
/* Yes, return the cached values associated with the sector. */
|
||||
*block = (ULONG) (sector_mapping_cache_entry_ptr + 1) -> lx_nand_sector_mapping_cache_block;
|
||||
*page = (ULONG) (sector_mapping_cache_entry_ptr + 1) -> lx_nand_sector_mapping_cache_page;
|
||||
|
||||
/* Just swap the first and second entry. */
|
||||
temp_sector_mapping_cache_entry = *(sector_mapping_cache_entry_ptr);
|
||||
*(sector_mapping_cache_entry_ptr) = *(sector_mapping_cache_entry_ptr + 1);
|
||||
*(sector_mapping_cache_entry_ptr + 1) = temp_sector_mapping_cache_entry;
|
||||
|
||||
/* Return a successful status. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
else if (((sector_mapping_cache_entry_ptr + 2) -> lx_nand_sector_mapping_cache_logical_sector) == (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID))
|
||||
{
|
||||
|
||||
/* Increment the sector mapping cache hit counter. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_sector_mapping_cache_hits++;
|
||||
|
||||
/* Yes, return the cached value. */
|
||||
*block = (ULONG) (sector_mapping_cache_entry_ptr + 2) -> lx_nand_sector_mapping_cache_block;
|
||||
*page = (ULONG) (sector_mapping_cache_entry_ptr + 2) -> lx_nand_sector_mapping_cache_page;
|
||||
|
||||
/* Move the third entry to the top and the first two entries down. */
|
||||
temp_sector_mapping_cache_entry = *(sector_mapping_cache_entry_ptr);
|
||||
*(sector_mapping_cache_entry_ptr) = *(sector_mapping_cache_entry_ptr + 2);
|
||||
*(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1);
|
||||
*(sector_mapping_cache_entry_ptr + 1) = temp_sector_mapping_cache_entry;
|
||||
|
||||
/* Return a successful status. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
else if (((sector_mapping_cache_entry_ptr + 3) -> lx_nand_sector_mapping_cache_logical_sector) == (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID))
|
||||
{
|
||||
|
||||
/* Increment the sector mapping cache hit counter. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_sector_mapping_cache_hits++;
|
||||
|
||||
/* Yes, return the cached value. */
|
||||
*block = (ULONG) (sector_mapping_cache_entry_ptr + 3) -> lx_nand_sector_mapping_cache_block;
|
||||
*page = (ULONG) (sector_mapping_cache_entry_ptr + 3) -> lx_nand_sector_mapping_cache_page;
|
||||
|
||||
/* Move the last entry to the top and the first three entries down. */
|
||||
temp_sector_mapping_cache_entry = *(sector_mapping_cache_entry_ptr);
|
||||
*(sector_mapping_cache_entry_ptr) = *(sector_mapping_cache_entry_ptr + 3);
|
||||
*(sector_mapping_cache_entry_ptr + 3) = *(sector_mapping_cache_entry_ptr + 2);
|
||||
*(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1);
|
||||
*(sector_mapping_cache_entry_ptr + 1) = temp_sector_mapping_cache_entry;
|
||||
|
||||
/* Return a successful status. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
/* If we get here, we have a cache miss so increment the counter before we fall through to the search loop. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_sector_mapping_cache_misses++;
|
||||
#else
|
||||
|
||||
/* Direct mapping cache is defined. */
|
||||
|
||||
/* See if we are still opening the flash. */
|
||||
if (nand_flash -> lx_nand_flash_state == LX_NAND_FLASH_OPENED)
|
||||
{
|
||||
|
||||
/* Flash instance is opened. */
|
||||
|
||||
/* Determine if this logical sector fits in the logical sector direct cache mapping. */
|
||||
if (logical_sector < LX_NAND_SECTOR_MAPPING_CACHE_SIZE)
|
||||
{
|
||||
|
||||
/* Yes, invalidate the logical sector cache. */
|
||||
if ((nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_block == 0) &&
|
||||
(nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page == 0))
|
||||
{
|
||||
|
||||
/* Entry is not mapped. */
|
||||
|
||||
/* No, return sector not found status. */
|
||||
return(LX_SECTOR_NOT_FOUND);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Sector is mapped, return the block and page of the mapping. */
|
||||
*block = nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_block;
|
||||
*page = nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page;
|
||||
|
||||
/* Return a successful status. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* If we get here, we have a cache miss so increment the counter before we fall through to the search loop. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_sector_mapping_cache_misses++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Clear the maximum mapped sector. */
|
||||
max_mapped_sector = 0;
|
||||
|
||||
/* Setup the total number of mapped pages. */
|
||||
mapped_pages = nand_flash -> lx_nand_flash_mapped_pages;
|
||||
|
||||
/* Setup pointer to internal buffer. */
|
||||
block_word_ptr = nand_flash -> lx_nand_flash_page_buffer;
|
||||
|
||||
/* Start searching from the last found block. */
|
||||
i = nand_flash -> lx_nand_flash_found_block_search;
|
||||
|
||||
/* Setup the starting page to look at. */
|
||||
j = nand_flash -> lx_nand_flash_found_page_search;
|
||||
|
||||
/* Pickup the total number of blocks. */
|
||||
total_blocks = nand_flash -> lx_nand_flash_total_blocks;
|
||||
|
||||
/* Loop through the blocks to attempt to find the mapped logical sector. */
|
||||
while (total_blocks--)
|
||||
{
|
||||
|
||||
/* First, check to make sure this block is good. */
|
||||
status = _lx_nand_flash_driver_block_status_get(nand_flash, i, &block_status);
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, i, 0);
|
||||
|
||||
/* Return the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Is this block bad? */
|
||||
if (block_status != LX_NAND_GOOD_BLOCK)
|
||||
{
|
||||
|
||||
/* Yes, this block is bad. */
|
||||
|
||||
/* Move to the next block. */
|
||||
i++;
|
||||
|
||||
/* Check for wrap condition. */
|
||||
if (i >= nand_flash -> lx_nand_flash_total_blocks)
|
||||
{
|
||||
|
||||
/* Wrap to the first block. */
|
||||
i = 0;
|
||||
}
|
||||
|
||||
/* Start looking at the first page of the next block. */
|
||||
j = 1;
|
||||
|
||||
/* Continue to the next block. */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Read the extra bytes of page 0. This will tell us if the page has any valid mappings. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, i, 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, i, 0);
|
||||
|
||||
/* Return the error... no point in continuing. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Determine if there are any valid mappings. */
|
||||
if (((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_BLOCK_VALID) == 0) ||
|
||||
((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_BLOCK_EMPTY) != 0))
|
||||
{
|
||||
|
||||
/* Yes, this block is either no longer valid or is empty, i.e., no mappings. */
|
||||
|
||||
/* Move to the next block. */
|
||||
i++;
|
||||
|
||||
/* Check for wrap condition. */
|
||||
if (i >= nand_flash -> lx_nand_flash_total_blocks)
|
||||
{
|
||||
|
||||
/* Wrap to the first block. */
|
||||
i = 0;
|
||||
}
|
||||
|
||||
/* Reset to the first page of the next block. */
|
||||
j = 1;
|
||||
|
||||
/* 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, i, 0, block_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, i, 0);
|
||||
|
||||
/* Determine if the error is fatal. */
|
||||
if (status != LX_NAND_ERROR_CORRECTED)
|
||||
{
|
||||
|
||||
/* Return the error. */
|
||||
return(status);
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if we have a valid logical sector mapping list. */
|
||||
if ((block_word_ptr[1] != LX_NAND_PAGE_FREE) &&
|
||||
(block_word_ptr[nand_flash -> lx_nand_flash_pages_per_block] == LX_NAND_PAGE_LIST_VALID))
|
||||
{
|
||||
|
||||
/* Yes, we have a valid logical sector mapping list in page 0. */
|
||||
|
||||
/* Clear the valid entries counter. */
|
||||
valid_pages = 0;
|
||||
|
||||
/* Setup the total number of pages, less the metadata page. */
|
||||
total_pages = nand_flash -> lx_nand_flash_pages_per_block - 1;
|
||||
|
||||
/* Now search through the sector list to find a match. */
|
||||
while (total_pages--)
|
||||
{
|
||||
|
||||
/* Is this entry valid? */
|
||||
if ((block_word_ptr[j] & (LX_NAND_PAGE_VALID | LX_NAND_PAGE_MAPPING_NOT_VALID)) == LX_NAND_PAGE_VALID)
|
||||
{
|
||||
|
||||
/* Increment the valid entries counter. */
|
||||
valid_pages++;
|
||||
|
||||
/* Determine if this is a new maximum logical sector. */
|
||||
if ((block_word_ptr[j] & LX_NAND_LOGICAL_SECTOR_MASK) > max_mapped_sector)
|
||||
{
|
||||
|
||||
/* Yes, a new maximum mapped sectors - remember it! */
|
||||
max_mapped_sector = (block_word_ptr[j] & LX_NAND_LOGICAL_SECTOR_MASK);
|
||||
}
|
||||
|
||||
/* Do we have a valid sector match? */
|
||||
if ((block_word_ptr[j] & LX_NAND_LOGICAL_SECTOR_MASK) == logical_sector)
|
||||
{
|
||||
|
||||
/* Read in the actual page entry to make sure this logical sector mapping is still valid. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, i, j, (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, i, j);
|
||||
|
||||
/* Return the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Verify that the actual page still has a valid sector mapping. */
|
||||
if (((extra_info.lx_nand_page_extra_info_logical_sector & (LX_NAND_PAGE_VALID | LX_NAND_PAGE_MAPPING_NOT_VALID)) == LX_NAND_PAGE_VALID) &&
|
||||
((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK) == logical_sector))
|
||||
{
|
||||
|
||||
/* Decrement the number of mapped pages to examine. */
|
||||
mapped_pages--;
|
||||
|
||||
/* Determine if we care about the superceded bit. */
|
||||
if (superceded_check == LX_FALSE)
|
||||
{
|
||||
|
||||
/* Prepare the return information. */
|
||||
*block = i;
|
||||
*page = j;
|
||||
|
||||
/* Determine if the sector mapping cache is enabled. */
|
||||
if (nand_flash -> lx_nand_flash_sector_mapping_cache_enabled)
|
||||
{
|
||||
|
||||
#ifndef LX_NAND_FLASH_DIRECT_MAPPING_CACHE
|
||||
|
||||
/* Yes, update the cache with the logical sector mapping. */
|
||||
|
||||
/* Move all the cache entries down so the oldest is at the bottom. */
|
||||
*(sector_mapping_cache_entry_ptr + 3) = *(sector_mapping_cache_entry_ptr + 2);
|
||||
*(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1);
|
||||
*(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr);
|
||||
|
||||
/* Setup the new sector information in the cache. */
|
||||
sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_logical_sector = (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID);
|
||||
sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_block = (USHORT) i;
|
||||
sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_page = (USHORT) j;
|
||||
#else
|
||||
|
||||
/* Determine if this logical sector fits in the logical sector cache mapping. */
|
||||
if (logical_sector < LX_NAND_SECTOR_MAPPING_CACHE_SIZE)
|
||||
{
|
||||
|
||||
/* Yes, store the logical sector to block/page mapping in the cache. */
|
||||
nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_block = (USHORT) i;
|
||||
nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page = (USHORT) j;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Move to the next page. */
|
||||
j++;
|
||||
|
||||
/* Determine if the page has wrapped around. */
|
||||
if (j >= nand_flash -> lx_nand_flash_pages_per_block)
|
||||
{
|
||||
|
||||
/* Yes, page wrapped. Move to page 1 of the next block. */
|
||||
j = 1;
|
||||
}
|
||||
|
||||
/* Remember the last found block and page for next search. */
|
||||
nand_flash -> lx_nand_flash_found_block_search = i;
|
||||
nand_flash -> lx_nand_flash_found_page_search = j;
|
||||
|
||||
/* Return success! */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
/* Check for the superceded bit being clear, which means the sector was superceded. */
|
||||
else if (extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_PAGE_SUPERCEDED)
|
||||
{
|
||||
|
||||
/* Prepare the return information. */
|
||||
*block = i;
|
||||
*page = j;
|
||||
|
||||
/* No need to update the cache here, since this condition only happens during initialization. */
|
||||
|
||||
/* Move to the next page. */
|
||||
j++;
|
||||
|
||||
/* Determine if the page has wrapped around. */
|
||||
if (j >= nand_flash -> lx_nand_flash_pages_per_block)
|
||||
{
|
||||
|
||||
/* Yes, page wrapped. Move to page 1 of the next block. */
|
||||
j = 1;
|
||||
}
|
||||
|
||||
/* Remember the last found block and page for next search. */
|
||||
nand_flash -> lx_nand_flash_found_block_search = i;
|
||||
nand_flash -> lx_nand_flash_found_page_search = j;
|
||||
|
||||
/* Return success! */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* The entry is not really valid anymore, decrement the valid entries counter. */
|
||||
valid_pages--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Move to the next page. */
|
||||
j++;
|
||||
|
||||
/* Check for wrap condition. */
|
||||
if (j >= nand_flash -> lx_nand_flash_pages_per_block)
|
||||
{
|
||||
|
||||
/* Yes, page has wrapped, reset to page 1. */
|
||||
j = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* At this point we know that nothing matched in the mapping list. Now we need to check to see if there are any valid entries. */
|
||||
if (valid_pages)
|
||||
{
|
||||
|
||||
/* Reset the valid pages counter. */
|
||||
valid_pages = 0;
|
||||
|
||||
/* Setup the total number of pages, less the metadata page. */
|
||||
total_pages = nand_flash -> lx_nand_flash_pages_per_block - 1;
|
||||
|
||||
/* Now search through the sector list to find a match. */
|
||||
while (total_pages--)
|
||||
{
|
||||
|
||||
/* Is this entry valid? */
|
||||
if ((block_word_ptr[j] & (LX_NAND_PAGE_VALID | LX_NAND_PAGE_MAPPING_NOT_VALID)) == LX_NAND_PAGE_VALID)
|
||||
{
|
||||
|
||||
/* Read in the actual page entry to make sure this logical sector mapping is still valid. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, i, j, (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, i, j);
|
||||
|
||||
/* Return the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Verify that the actual page still has a valid sector mapping. */
|
||||
if ((extra_info.lx_nand_page_extra_info_logical_sector & (LX_NAND_PAGE_VALID | LX_NAND_PAGE_MAPPING_NOT_VALID)) == LX_NAND_PAGE_VALID)
|
||||
{
|
||||
|
||||
/* Set the valid pages counter to 1 and break the loop. */
|
||||
valid_pages = 1;
|
||||
|
||||
/* If one entry is valid, we can stop looking. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Move to the next page. */
|
||||
j++;
|
||||
|
||||
/* Check for wrap condition. */
|
||||
if (j >= nand_flash -> lx_nand_flash_pages_per_block)
|
||||
{
|
||||
|
||||
/* Yes, page has wrapped, reset to page 1. */
|
||||
j = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now, a final check for valid pages. */
|
||||
if (valid_pages == 0)
|
||||
{
|
||||
|
||||
/* There are no valid entries. We can now mark this block as not valid since there are no active
|
||||
mappings. This will prevent us from examining it again during the search process. */
|
||||
extra_info.lx_nand_page_extra_info_logical_sector = 0;
|
||||
status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, i, 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, i, 0);
|
||||
}
|
||||
|
||||
/* Reset the search page to 1. */
|
||||
j = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* No, we don't have a valid logical sector mapping list. */
|
||||
|
||||
/* Setup the total number of pages, less the metadata page. */
|
||||
total_pages = nand_flash -> lx_nand_flash_pages_per_block - 1;
|
||||
|
||||
/* Remember the start of the search. */
|
||||
search_start = j;
|
||||
|
||||
/* Now search through the sector list to find a match. */
|
||||
while (total_pages--)
|
||||
{
|
||||
|
||||
/* Read in the actual page entry to look for the logical sector mapping. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, i, j, (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, i, j);
|
||||
|
||||
/* Return the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Is there a logical page mapping? */
|
||||
if (extra_info.lx_nand_page_extra_info_logical_sector == LX_NAND_PAGE_FREE)
|
||||
{
|
||||
|
||||
/* Since the mapping is done sequentially in the block, we know nothing
|
||||
else exists after this point. */
|
||||
|
||||
/* Determine if the search started at the beginning of the block. */
|
||||
if (search_start == 1)
|
||||
{
|
||||
|
||||
/* Yes, we started at the beginning of the block. We are now done with this block. */
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Setup the new total to the search start. */
|
||||
total_pages = (search_start - 1);
|
||||
|
||||
/* Clear search start. */
|
||||
search_start = 1;
|
||||
|
||||
/* Start search over. */
|
||||
j = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Is this page mapping valid? */
|
||||
if ((extra_info.lx_nand_page_extra_info_logical_sector & (LX_NAND_PAGE_VALID | LX_NAND_PAGE_MAPPING_NOT_VALID)) == LX_NAND_PAGE_VALID)
|
||||
{
|
||||
|
||||
/* Yes, this page mapping is valid. */
|
||||
|
||||
/* Decrease the number of pages to examine. */
|
||||
mapped_pages--;
|
||||
|
||||
|
||||
/* Determine if this is a new maximum logical sector. */
|
||||
if ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK) > max_mapped_sector)
|
||||
{
|
||||
|
||||
/* Yes, a new maximum mapped sectors - remember it! */
|
||||
max_mapped_sector = (extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK);
|
||||
}
|
||||
|
||||
/* Do we have a valid sector match? */
|
||||
if ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK) == logical_sector)
|
||||
{
|
||||
|
||||
/* Determine if we care about the superceded bit. */
|
||||
if (superceded_check == LX_FALSE)
|
||||
{
|
||||
|
||||
/* Prepare the return information. */
|
||||
*block = i;
|
||||
*page = j;
|
||||
|
||||
/* Determine if the sector mapping cache is enabled. */
|
||||
if (nand_flash -> lx_nand_flash_sector_mapping_cache_enabled)
|
||||
{
|
||||
|
||||
/* Yes, update the cache with the logical sector mapping. */
|
||||
|
||||
#ifndef LX_NAND_FLASH_DIRECT_MAPPING_CACHE
|
||||
|
||||
/* Move all the cache entries down so the oldest is at the bottom. */
|
||||
*(sector_mapping_cache_entry_ptr + 3) = *(sector_mapping_cache_entry_ptr + 2);
|
||||
*(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1);
|
||||
*(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr);
|
||||
|
||||
/* Setup the new sector information in the cache. */
|
||||
sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_logical_sector = (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID);
|
||||
sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_block = (USHORT) i;
|
||||
sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_page = (USHORT) j;
|
||||
#else
|
||||
|
||||
/* Determine if this logical sector fits in the logical sector cache mapping. */
|
||||
if (logical_sector < LX_NAND_SECTOR_MAPPING_CACHE_SIZE)
|
||||
{
|
||||
|
||||
/* Yes, store the logical sector to block/page mapping in the cache. */
|
||||
nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_block = (USHORT) i;
|
||||
nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page = (USHORT) j;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Move to the next page. */
|
||||
j++;
|
||||
|
||||
/* Determine if the page has wrapped around. */
|
||||
if (j >= nand_flash -> lx_nand_flash_pages_per_block)
|
||||
{
|
||||
|
||||
/* Yes, page wrapped - move to page 1. */
|
||||
j = 1;
|
||||
}
|
||||
|
||||
/* Remember the last found block and page for next search. */
|
||||
nand_flash -> lx_nand_flash_found_block_search = i;
|
||||
nand_flash -> lx_nand_flash_found_page_search = j;
|
||||
|
||||
/* Return success! */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
/* Check for the superceded bit being clear, which means the sector was superceded. */
|
||||
else if (extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_PAGE_SUPERCEDED)
|
||||
{
|
||||
|
||||
/* Prepare the return information. */
|
||||
*block = i;
|
||||
*page = j;
|
||||
|
||||
/* No need to update the cache here, since this condition only happens during initialization. */
|
||||
|
||||
/* Move to the next page. */
|
||||
j++;
|
||||
|
||||
/* Determine if the page has wrapped around. */
|
||||
if (j >= nand_flash -> lx_nand_flash_pages_per_block)
|
||||
{
|
||||
|
||||
/* Yes, page wrapped - move to page 1. */
|
||||
j = 1;
|
||||
}
|
||||
|
||||
/* Remember the last found block and page for next search. */
|
||||
nand_flash -> lx_nand_flash_found_block_search = i;
|
||||
nand_flash -> lx_nand_flash_found_page_search = j;
|
||||
|
||||
/* Return success! */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Move to next page. */
|
||||
j++;
|
||||
|
||||
/* Determine if we have a wrap-around condition. */
|
||||
if (j >= nand_flash -> lx_nand_flash_pages_per_block)
|
||||
{
|
||||
|
||||
/* Yes, page wrapped - move to page 1. */
|
||||
j = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if there are any more mapped sectors. */
|
||||
if (mapped_pages == 0)
|
||||
break;
|
||||
|
||||
/* Move to the next block. */
|
||||
i++;
|
||||
|
||||
/* Check for wrap condition. */
|
||||
if (i >= nand_flash -> lx_nand_flash_total_blocks)
|
||||
{
|
||||
|
||||
/* Wrap to the first block. */
|
||||
i = 0;
|
||||
}
|
||||
|
||||
/* Start at the first page in the next block. */
|
||||
j = 1;
|
||||
}
|
||||
|
||||
/* Determine if we should update the maximum mapped sector. */
|
||||
if (nand_flash -> lx_nand_flash_max_mapped_sector != 0xFFFFFFFF)
|
||||
{
|
||||
|
||||
/* Yes, update the maximum mapped sector. */
|
||||
nand_flash -> lx_nand_flash_max_mapped_sector = max_mapped_sector;
|
||||
}
|
||||
|
||||
/* Return sector not found status. */
|
||||
return(LX_SECTOR_NOT_FOUND);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_next_block_to_erase_find PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function finds the next block to erase in the NAND flash. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* return_erase_block Returned block to erase */
|
||||
/* return_erase_count Returned erase count of block */
|
||||
/* return_mapped_pages Returned number of mapped */
|
||||
/* pages */
|
||||
/* return_obsolete_pages Returned number of obsolete */
|
||||
/* pages */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nand_flash_driver_extra_bytes_get Driver block extra bytes get */
|
||||
/* _lx_nand_flash_driver_block_status_get */
|
||||
/* Driver block status get */
|
||||
/* _lx_nand_flash_driver_read Driver page read */
|
||||
/* _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 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_next_block_to_erase_find(LX_NAND_FLASH *nand_flash, ULONG *return_erase_block, ULONG *return_erase_count, ULONG *return_mapped_pages, ULONG *return_obsolete_pages)
|
||||
{
|
||||
|
||||
LX_NAND_PAGE_EXTRA_INFO extra_info;
|
||||
UCHAR block_status;
|
||||
ULONG *block_word_ptr;
|
||||
ULONG block;
|
||||
ULONG page;
|
||||
ULONG mapped_pages;
|
||||
ULONG erase_count;
|
||||
ULONG obsolete_pages;
|
||||
ULONG min_block_erase = 0;
|
||||
ULONG min_block_erase_count = 0;
|
||||
ULONG min_block_obsolete_count = 0;
|
||||
ULONG min_block_mapped_count = 0;
|
||||
ULONG max_obsolete_pages = 0;
|
||||
ULONG max_obsolete_block = 0;
|
||||
ULONG max_obsolete_erase_count = 0;
|
||||
ULONG max_obsolete_mapped_count = 0;
|
||||
ULONG min_system_block_erase_count = 0;
|
||||
ULONG max_system_block_erase_count = 0;
|
||||
ULONG erase_count_threshold = 0;
|
||||
UINT status;
|
||||
|
||||
|
||||
/* Initialize the minimum erase count. */
|
||||
min_block_erase_count = LX_ALL_ONES;
|
||||
|
||||
/* Initialize the system minimum and maximum erase counts. */
|
||||
min_system_block_erase_count = LX_ALL_ONES;
|
||||
max_system_block_erase_count = 0;
|
||||
|
||||
/* Initialize the maximum obsolete page count. */
|
||||
max_obsolete_pages = 0;
|
||||
|
||||
/* Calculate the erase count threshold. */
|
||||
if (nand_flash -> lx_nand_flash_free_pages >= nand_flash -> lx_nand_flash_pages_per_block)
|
||||
{
|
||||
|
||||
/* Calculate erase count threshold by adding constant to the current minimum. */
|
||||
erase_count_threshold = nand_flash -> lx_nand_flash_minimum_erase_count + LX_NAND_FLASH_MAX_ERASE_COUNT_DELTA;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* When the number of free pages is low, simply pick the block that has the most number of obsolete sectors. */
|
||||
erase_count_threshold = LX_ALL_ONES;
|
||||
}
|
||||
|
||||
/* Set the block word pointer to the internal buffer. */
|
||||
block_word_ptr = nand_flash -> lx_nand_flash_page_buffer;
|
||||
|
||||
/* Loop through the blocks to attempt to find the block to erase. */
|
||||
for (block = 0; block < nand_flash -> lx_nand_flash_total_blocks; block++)
|
||||
{
|
||||
|
||||
/* First, check to see if 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 the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Determine if this block is bad. */
|
||||
if (block_status != LX_NAND_GOOD_BLOCK)
|
||||
{
|
||||
|
||||
/* Block is bad, move to the next block. */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Read page 0 of the block, the first word has the erase count. */
|
||||
status = _lx_nand_flash_driver_read(nand_flash, block, 0, block_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 the error. */
|
||||
return(status);
|
||||
}
|
||||
}
|
||||
|
||||
/* Pickup the erase count from the first word of the block. */
|
||||
erase_count = block_word_ptr[0];
|
||||
|
||||
/* Update the system minimum and maximum erase counts. */
|
||||
if (erase_count < min_system_block_erase_count)
|
||||
min_system_block_erase_count = erase_count;
|
||||
if (erase_count > max_system_block_erase_count)
|
||||
max_system_block_erase_count = erase_count;
|
||||
|
||||
/* Compute the number of obsolete and mapped pages for this block. */
|
||||
obsolete_pages = 0;
|
||||
mapped_pages = 0;
|
||||
|
||||
/* Loop through the pages for this block. */
|
||||
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, page);
|
||||
|
||||
/* Return the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Is this entry free? */
|
||||
if (extra_info.lx_nand_page_extra_info_logical_sector == LX_NAND_PAGE_FREE)
|
||||
{
|
||||
|
||||
/* Get out of the loop, we are done! */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Is this entry obsolete? */
|
||||
else if ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_PAGE_VALID) == 0)
|
||||
{
|
||||
|
||||
/* Increment the number of obsolete pages. */
|
||||
obsolete_pages++;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Increment the number of mapped pages. */
|
||||
mapped_pages++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if we have a block with a new maximum number of obsolete pages. */
|
||||
if ((obsolete_pages > max_obsolete_pages) && (erase_count <= erase_count_threshold))
|
||||
{
|
||||
|
||||
/* Update the new maximum obsolete pages and related information. */
|
||||
max_obsolete_pages = obsolete_pages;
|
||||
max_obsolete_block = block;
|
||||
max_obsolete_erase_count = erase_count;
|
||||
max_obsolete_mapped_count = mapped_pages;
|
||||
}
|
||||
else if ((max_obsolete_pages) && (obsolete_pages == max_obsolete_pages) && (erase_count <= erase_count_threshold))
|
||||
{
|
||||
|
||||
/* Another block has the same number of obsolete pages. Does this new block have a smaller erase
|
||||
count? */
|
||||
if (erase_count < max_obsolete_erase_count)
|
||||
{
|
||||
|
||||
/* Yes, choose the block with the smaller erase count. */
|
||||
max_obsolete_pages = obsolete_pages;
|
||||
max_obsolete_block = block;
|
||||
max_obsolete_erase_count = erase_count;
|
||||
max_obsolete_mapped_count = mapped_pages;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if we have a new minimum erase count. */
|
||||
if (erase_count < min_block_erase_count)
|
||||
{
|
||||
|
||||
/* Update the new minimum erase count and related information. */
|
||||
min_block_erase_count = erase_count;
|
||||
min_block_erase = block;
|
||||
min_block_obsolete_count = obsolete_pages;
|
||||
min_block_mapped_count = mapped_pages;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if we can erase the block with the most obsolete pages. */
|
||||
if (max_obsolete_pages)
|
||||
{
|
||||
|
||||
/* Erase the block with the most obsolete pages. */
|
||||
*return_erase_block = max_obsolete_block;
|
||||
*return_erase_count = max_obsolete_erase_count;
|
||||
*return_obsolete_pages = max_obsolete_pages;
|
||||
*return_mapped_pages = max_obsolete_mapped_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Otherwise, erase the block with the smallest erase count. */
|
||||
*return_erase_block = min_block_erase;
|
||||
*return_erase_count = min_block_erase_count;
|
||||
*return_obsolete_pages = min_block_obsolete_count;
|
||||
*return_mapped_pages = min_block_mapped_count;
|
||||
}
|
||||
|
||||
/* Update the overall minimum and maximum erase count. */
|
||||
nand_flash -> lx_nand_flash_minimum_erase_count = min_system_block_erase_count;
|
||||
nand_flash -> lx_nand_flash_maximum_erase_count = max_system_block_erase_count;
|
||||
|
||||
/* Return success. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_page_ecc_check PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function checks the NAND page and ECC for errors and */
|
||||
/* attempts to correct 1 bit errors. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* page_buffer Page buffer */
|
||||
/* ecc_buffer Returned ECC buffer */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nand_flash_256byte_ecc_check Check 256 bytes and ECC */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* NAND flash driver */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_page_ecc_check(LX_NAND_FLASH *nand_flash, UCHAR *page_buffer, UCHAR *ecc_buffer)
|
||||
{
|
||||
|
||||
UINT bytes_checked;
|
||||
UINT status;
|
||||
UINT return_status = LX_SUCCESS;
|
||||
|
||||
|
||||
/* Loop to check the entire NAND flash page. */
|
||||
bytes_checked = 0;
|
||||
while (bytes_checked < nand_flash -> lx_nand_flash_bytes_per_page)
|
||||
{
|
||||
|
||||
/* Check this 256 byte piece of the NAND page. */
|
||||
status = _lx_nand_flash_256byte_ecc_check(page_buffer, ecc_buffer);
|
||||
|
||||
/* Determine if there was an error. */
|
||||
if (status != LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Determine if a non-correctable error is present. */
|
||||
if (status == LX_NAND_ERROR_NOT_CORRECTED)
|
||||
{
|
||||
|
||||
/* Always return a non-correctable error, if present. */
|
||||
return_status = LX_NAND_ERROR_NOT_CORRECTED;
|
||||
break;
|
||||
}
|
||||
|
||||
/* A single-bit error was corrected, return this status
|
||||
if there is no LX_ERROR status already detected. */
|
||||
else if (return_status == LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Return a notice that the single bit error was corrected. */
|
||||
return_status = LX_NAND_ERROR_CORRECTED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Move to the next 256 byte portion of the page. */
|
||||
bytes_checked = bytes_checked + 256;
|
||||
|
||||
/* Move the page buffer forward. */
|
||||
page_buffer = page_buffer + 256;
|
||||
|
||||
/* Move the ECC buffer forward, note there are 3 bytes of ECC per page. */
|
||||
ecc_buffer = ecc_buffer + 3;
|
||||
}
|
||||
|
||||
/* Return status. */
|
||||
return(return_status);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_page_ecc_compute PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function computes the ECC for a NAND flash page. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* page_buffer Page buffer */
|
||||
/* ecc_buffer Returned ECC buffer */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nand_flash_256byte_ecc_compute Compute ECC for 256 bytes */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* NAND flash driver */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_page_ecc_compute(LX_NAND_FLASH *nand_flash, UCHAR *page_buffer, UCHAR *ecc_buffer)
|
||||
{
|
||||
|
||||
UINT bytes_computed;
|
||||
|
||||
|
||||
/* Loop to compute the ECC over the entire NAND flash page. */
|
||||
bytes_computed = 0;
|
||||
while (bytes_computed < nand_flash -> lx_nand_flash_bytes_per_page)
|
||||
{
|
||||
|
||||
/* Compute the ECC for this 256 byte piece of the page. */
|
||||
_lx_nand_flash_256byte_ecc_compute(page_buffer, ecc_buffer);
|
||||
|
||||
/* Move to the next 256 byte portion of the page. */
|
||||
bytes_computed = bytes_computed + 256;
|
||||
|
||||
/* Move the page buffer forward. */
|
||||
page_buffer = page_buffer + 256;
|
||||
|
||||
/* Move the ECC buffer forward, note there are 3 bytes of ECC per page. */
|
||||
ecc_buffer = ecc_buffer + 3;
|
||||
}
|
||||
|
||||
/* Return success. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_partial_defragment PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function defragments the NAND flash up to the specified */
|
||||
/* number of blocks. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* max_blocks Maximum number of blocks to */
|
||||
/* defragment */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nand_flash_block_reclaim Reclaim a NAND flash block */
|
||||
/* tx_mutex_get Get thread protection */
|
||||
/* tx_mutex_put Release thread protection */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Application Code */
|
||||
/* Internal LevelX */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_partial_defragment(LX_NAND_FLASH *nand_flash, UINT max_blocks)
|
||||
{
|
||||
|
||||
ULONG i;
|
||||
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Obtain the thread safe mutex. */
|
||||
tx_mutex_get(&nand_flash -> lx_nand_flash_mutex, TX_WAIT_FOREVER);
|
||||
#endif
|
||||
|
||||
/* Determine if the maximum number of blocks exceeds the total blocks in this flash instance. */
|
||||
if (max_blocks >= nand_flash -> lx_nand_flash_total_blocks)
|
||||
{
|
||||
|
||||
/* Adjust the maximum to the total number of blocks. */
|
||||
max_blocks = nand_flash -> lx_nand_flash_total_blocks;
|
||||
}
|
||||
|
||||
/* Loop for max number of blocks, while there are obsolete count. */
|
||||
for (i = 0; i < max_blocks; i++)
|
||||
{
|
||||
|
||||
/* Determine if there is any more defragment work. */
|
||||
if (nand_flash -> lx_nand_flash_obsolete_pages == 0)
|
||||
break;
|
||||
|
||||
/* Call the block reclaim function to defragment. */
|
||||
_lx_nand_flash_block_reclaim(nand_flash);
|
||||
}
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return successful completion. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,392 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_physical_page_allocate PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* 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 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_sector_mapping_cache_invalidate PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function invalidates the sector's entry in the NAND flash */
|
||||
/* cache. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* logical_sector Logical sector */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Internal LevelX */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
VOID _lx_nand_flash_sector_mapping_cache_invalidate(LX_NAND_FLASH *nand_flash, ULONG logical_sector)
|
||||
{
|
||||
|
||||
#ifndef LX_NAND_FLASH_DIRECT_MAPPING_CACHE
|
||||
ULONG i;
|
||||
LX_NAND_SECTOR_MAPPING_CACHE_ENTRY *sector_mapping_cache_entry_ptr;
|
||||
#endif
|
||||
|
||||
/* Determine if the sector mapping cache is enabled. */
|
||||
if (nand_flash -> lx_nand_flash_sector_mapping_cache_enabled)
|
||||
{
|
||||
|
||||
#ifndef LX_NAND_FLASH_DIRECT_MAPPING_CACHE
|
||||
|
||||
/* Calculate the starting index of the sector mapping cache for this sector entry. */
|
||||
i = (logical_sector & LX_NAND_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NAND_SECTOR_MAPPING_CACHE_DEPTH;
|
||||
|
||||
/* Build a pointer to the cache entry. */
|
||||
sector_mapping_cache_entry_ptr = &nand_flash -> lx_nand_flash_sector_mapping_cache[i];
|
||||
|
||||
/* Determine if the sector is in the sector mapping cache - assuming the depth of the sector
|
||||
mapping cache is LX_NAND_SECTOR_MAPPING_CACHE_DEPTH entries. */
|
||||
if ((sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_logical_sector) == (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID))
|
||||
{
|
||||
|
||||
/* Move all cache entries up and invalidate the last entry. */
|
||||
*(sector_mapping_cache_entry_ptr) = *(sector_mapping_cache_entry_ptr + 1);
|
||||
*(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr + 2);
|
||||
*(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 3);
|
||||
|
||||
/* Invalidate the last entry. */
|
||||
(sector_mapping_cache_entry_ptr + 3) -> lx_nand_sector_mapping_cache_logical_sector = 0;
|
||||
}
|
||||
else if (((sector_mapping_cache_entry_ptr + 1) -> lx_nand_sector_mapping_cache_logical_sector) == (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID))
|
||||
{
|
||||
|
||||
/* Move all subsequent cache entries up and invalidate the last entry. */
|
||||
*(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr + 2);
|
||||
*(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 3);
|
||||
|
||||
/* Invalidate the last entry. */
|
||||
(sector_mapping_cache_entry_ptr + 3) -> lx_nand_sector_mapping_cache_logical_sector = 0;
|
||||
}
|
||||
else if (((sector_mapping_cache_entry_ptr + 2) -> lx_nand_sector_mapping_cache_logical_sector) == (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID))
|
||||
{
|
||||
|
||||
/* Move all subsequent cache entries up and invalidate the last entry. */
|
||||
*(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 3);
|
||||
|
||||
/* Invalidate the last entry. */
|
||||
(sector_mapping_cache_entry_ptr + 3) -> lx_nand_sector_mapping_cache_logical_sector = 0;
|
||||
}
|
||||
else if (((sector_mapping_cache_entry_ptr + 3) -> lx_nand_sector_mapping_cache_logical_sector) == (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID))
|
||||
{
|
||||
|
||||
/* Simply invalidate the last entry. */
|
||||
(sector_mapping_cache_entry_ptr + 3) -> lx_nand_sector_mapping_cache_logical_sector = 0;
|
||||
}
|
||||
#else
|
||||
|
||||
/* Direct mapping cache is defined. */
|
||||
|
||||
/* Determine if this logical sector fits in the logical sector cache mapping. */
|
||||
if (logical_sector < LX_NAND_SECTOR_MAPPING_CACHE_SIZE)
|
||||
{
|
||||
|
||||
/* Yes, invalidate the logical sector cache. */
|
||||
nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_block = 0;
|
||||
nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_sector_read PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function reads a logical sector from NAND flash. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* logical_sector Logical sector number */
|
||||
/* buffer Pointer to buffer to read into*/
|
||||
/* (the size is number of */
|
||||
/* bytes in a page) */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nand_flash_driver_read Driver page read */
|
||||
/* _lx_nand_flash_logical_sector_find Find logical sector */
|
||||
/* _lx_nand_flash_system_error Internal system error handler */
|
||||
/* tx_mutex_get Get thread protection */
|
||||
/* tx_mutex_put Release thread protection */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Application Code */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_sector_read(LX_NAND_FLASH *nand_flash, ULONG logical_sector, VOID *buffer)
|
||||
{
|
||||
|
||||
UINT status;
|
||||
ULONG found_block;
|
||||
ULONG found_page;
|
||||
ULONG i;
|
||||
ULONG *word_ptr;
|
||||
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Obtain the thread safe mutex. */
|
||||
tx_mutex_get(&nand_flash -> lx_nand_flash_mutex, TX_WAIT_FOREVER);
|
||||
#endif
|
||||
|
||||
/* Increment the number of read requests. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_sector_read_requests++;
|
||||
|
||||
/* See if we can find the sector in the current mapping. */
|
||||
_lx_nand_flash_logical_sector_find(nand_flash, logical_sector, LX_FALSE, &found_block, &found_page);
|
||||
|
||||
/* Determine if the logical sector mapping was found. */
|
||||
if (found_page)
|
||||
{
|
||||
|
||||
/* Yes, we were able to find the logical sector to page mapping. */
|
||||
|
||||
/* Read the data from the page. */
|
||||
status = _lx_nand_flash_driver_read(nand_flash, found_block, found_page, buffer, nand_flash -> lx_nand_flash_words_per_page);
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, found_block, found_page);
|
||||
|
||||
/* Determine if the error was corrected. */
|
||||
if (status == LX_NAND_ERROR_CORRECTED)
|
||||
{
|
||||
|
||||
/* Yes, we were able to read this page successfully with error correction. */
|
||||
|
||||
/* Change the status to LX_SUCCESS. */
|
||||
status = LX_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Adjust return status. */
|
||||
status = LX_ERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Set the status to success. */
|
||||
status = LX_SUCCESS;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Sector hasn't been written. Simply fill the destination buffer with ones and return success. */
|
||||
|
||||
/* Setup pointer to users buffer. */
|
||||
word_ptr = (ULONG *) buffer;
|
||||
|
||||
/* Put all ones in he buffer. */
|
||||
for (i = 0; i < nand_flash -> lx_nand_flash_words_per_page; i++)
|
||||
{
|
||||
|
||||
/* Copy a word. */
|
||||
*word_ptr++ = LX_ALL_ONES;
|
||||
}
|
||||
|
||||
/* Set the status to success. */
|
||||
status = LX_SUCCESS;
|
||||
}
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return status. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_sector_release PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function releases a logical sector from being managed in the */
|
||||
/* NAND flash. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* logical_sector Logical sector number */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nand_flash_driver_read Driver flash sector read */
|
||||
/* _lx_nand_flash_driver_write Driver flash sector write */
|
||||
/* _lx_nand_flash_driver_extra_bytes_get Get extra bytes from spare */
|
||||
/* _lx_nand_flash_driver_extra_bytes_set Set extra bytes in spare */
|
||||
/* _lx_nand_flash_block_obsoleted_check Check for block obsoleted */
|
||||
/* _lx_nand_flash_block_reclaim Reclaim one flash block */
|
||||
/* _lx_nand_flash_sector_mapping_cache_invalidate */
|
||||
/* Invalidate cache entry */
|
||||
/* _lx_nand_flash_logical_sector_find Find logical sector */
|
||||
/* _lx_nand_flash_system_error Internal system error handler */
|
||||
/* tx_mutex_get Get thread protection */
|
||||
/* tx_mutex_put Release thread protection */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Application Code */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_sector_release(LX_NAND_FLASH *nand_flash, ULONG logical_sector)
|
||||
{
|
||||
|
||||
LX_NAND_PAGE_EXTRA_INFO extra_info;
|
||||
UINT status;
|
||||
ULONG found_block;
|
||||
ULONG found_page;
|
||||
ULONG *block_word_ptr;
|
||||
ULONG i;
|
||||
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Obtain the thread safe mutex. */
|
||||
tx_mutex_get(&nand_flash -> lx_nand_flash_mutex, TX_WAIT_FOREVER);
|
||||
#endif
|
||||
|
||||
/* Increment the number of release requests. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_sector_release_requests++;
|
||||
|
||||
/* See if we can find the logical sector in the current mapping. */
|
||||
_lx_nand_flash_logical_sector_find(nand_flash, logical_sector, LX_FALSE, &found_block, &found_page);
|
||||
|
||||
/* Determine if the logical sector to page mapping was found. */
|
||||
if (found_page)
|
||||
{
|
||||
|
||||
/* Yes, we were able to find the logical sector. */
|
||||
|
||||
/* Determine if this sector is the current maximum sector. */
|
||||
if (nand_flash -> lx_nand_flash_max_mapped_sector == logical_sector)
|
||||
{
|
||||
|
||||
/* Yes, this is the maximum sector. Reduce the maximum, if possible. */
|
||||
if (logical_sector)
|
||||
{
|
||||
|
||||
/* Decrement the logical sector. */
|
||||
nand_flash -> lx_nand_flash_max_mapped_sector--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the extra information from the page. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, found_block, found_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, found_block, found_page);
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return an error. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Mark this sector as invalid. */
|
||||
|
||||
/* Now clear bits 31 and 30, which indicates this sector is now obsoleted. */
|
||||
extra_info.lx_nand_page_extra_info_logical_sector = extra_info.lx_nand_page_extra_info_logical_sector &
|
||||
~(((ULONG) LX_NAND_PAGE_VALID) | ((ULONG) LX_NAND_PAGE_SUPERCEDED));
|
||||
|
||||
/* Write the value back to the flash to clear bits 31 & 30. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, found_block, found_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, found_block, found_page);
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return an error. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Increment the number of obsolete pages. */
|
||||
nand_flash -> lx_nand_flash_obsolete_pages++;
|
||||
|
||||
/* Decrement the number of mapped pages. */
|
||||
nand_flash -> lx_nand_flash_mapped_pages--;
|
||||
|
||||
/* Setup pointer to internal buffer. */
|
||||
block_word_ptr = nand_flash -> lx_nand_flash_page_buffer;
|
||||
|
||||
/* 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, found_block, 0, block_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, found_block, 0);
|
||||
|
||||
/* Determine if the error is fatal. */
|
||||
if (status != LX_NAND_ERROR_CORRECTED)
|
||||
{
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return the error. */
|
||||
return(status);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef LX_NAND_FLASH_MAPPING_LIST_UPDATE_DISABLE
|
||||
|
||||
/* Determine if the mapping list in page 0 is valid. */
|
||||
if ((block_word_ptr[1] != LX_NAND_PAGE_FREE) &&
|
||||
(block_word_ptr[nand_flash -> lx_nand_flash_pages_per_block] == LX_NAND_PAGE_LIST_VALID))
|
||||
{
|
||||
|
||||
/* Mark the entry as invalid. */
|
||||
block_word_ptr[found_page] &= ~LX_NAND_PAGE_VALID;
|
||||
|
||||
/* Write the invalid entry. */
|
||||
status = _lx_nand_flash_driver_write(nand_flash, found_block, 0, block_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Ensure the sector mapping cache no longer has this sector. */
|
||||
_lx_nand_flash_sector_mapping_cache_invalidate(nand_flash, logical_sector);
|
||||
|
||||
/* Call routine to see if this block is completely obsoleted. If so,
|
||||
we can reclaim it immediately. */
|
||||
_lx_nand_flash_block_obsoleted_check(nand_flash, found_block);
|
||||
|
||||
/* Determine if there are less than two block's worth of free pages. */
|
||||
i = 0;
|
||||
while (nand_flash -> lx_nand_flash_free_pages <= nand_flash -> lx_nand_flash_pages_per_block)
|
||||
{
|
||||
|
||||
/* Attempt to reclaim one block. */
|
||||
_lx_nand_flash_block_reclaim(nand_flash);
|
||||
|
||||
/* Increment the block count. */
|
||||
i++;
|
||||
|
||||
/* Have we exceeded the number of blocks in the system? */
|
||||
if (i >= nand_flash -> lx_nand_flash_total_blocks)
|
||||
{
|
||||
|
||||
/* Yes, break out of the loop. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Could not find the logical sector. */
|
||||
status = LX_SECTOR_NOT_FOUND;
|
||||
}
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return status. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,458 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_sector_write PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function writes a logical sector to the NAND flash page. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* logical_sector Logical sector number */
|
||||
/* buffer Pointer to buffer to write */
|
||||
/* (the size is number of */
|
||||
/* bytes in a page) */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nand_flash_driver_read Driver flash sector read */
|
||||
/* _lx_nand_flash_driver_write Driver flash sector write */
|
||||
/* _lx_nand_flash_driver_extra_bytes_get Get extra bytes from spare */
|
||||
/* _lx_nand_flash_driver_extra_bytes_set Set extra bytes in spare */
|
||||
/* _lx_nand_flash_block_full_update Update page 0 with list of */
|
||||
/* mapped pages */
|
||||
/* _lx_nand_flash_block_obsoleted_check Check for block obsoleted */
|
||||
/* _lx_nand_flash_block_reclaim Reclaim one flash block */
|
||||
/* _lx_nand_flash_logical_sector_find Find logical sector */
|
||||
/* _lx_nand_flash_physical_page_allocate Allocate new page */
|
||||
/* _lx_nand_flash_sector_mapping_cache_invalidate */
|
||||
/* Invalidate cache entry */
|
||||
/* _lx_nand_flash_system_error Internal system error handler */
|
||||
/* tx_mutex_get Get thread protection */
|
||||
/* tx_mutex_put Release thread protection */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Application Code */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nand_flash_sector_write(LX_NAND_FLASH *nand_flash, ULONG logical_sector, VOID *buffer)
|
||||
{
|
||||
|
||||
LX_NAND_PAGE_EXTRA_INFO old_extra_info;
|
||||
LX_NAND_PAGE_EXTRA_INFO new_extra_info;
|
||||
ULONG old_block;
|
||||
ULONG old_page;
|
||||
ULONG new_block;
|
||||
ULONG new_page;
|
||||
ULONG new_erase_count;
|
||||
ULONG i;
|
||||
#ifndef LX_NAND_FLASH_DIRECT_MAPPING_CACHE
|
||||
LX_NAND_SECTOR_MAPPING_CACHE_ENTRY *sector_mapping_cache_entry_ptr;
|
||||
#endif
|
||||
UINT status;
|
||||
ULONG *block_word_ptr;
|
||||
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Obtain the thread safe mutex. */
|
||||
tx_mutex_get(&nand_flash -> lx_nand_flash_mutex, TX_WAIT_FOREVER);
|
||||
#endif
|
||||
|
||||
/* Determine if there are less than two block's worth of free pages. */
|
||||
i = 0;
|
||||
while (nand_flash -> lx_nand_flash_free_pages <= nand_flash -> lx_nand_flash_pages_per_block)
|
||||
{
|
||||
|
||||
/* Attempt to reclaim one block. */
|
||||
_lx_nand_flash_block_reclaim(nand_flash);
|
||||
|
||||
/* Increment the block count. */
|
||||
i++;
|
||||
|
||||
/* Have we exceeded the number of blocks in the system? */
|
||||
if (i >= nand_flash -> lx_nand_flash_total_blocks)
|
||||
{
|
||||
|
||||
/* Yes, break out of the loop. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Increment the number of write requests. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_sector_write_requests++;
|
||||
|
||||
/* See if we can find the logical sector in the current mapping. */
|
||||
_lx_nand_flash_logical_sector_find(nand_flash, logical_sector, LX_FALSE, &old_block, &old_page);
|
||||
|
||||
/* Allocate a new page for this write. */
|
||||
_lx_nand_flash_physical_page_allocate(nand_flash, &new_block, &new_page, &new_erase_count);
|
||||
|
||||
/* Determine if the new page allocation was successful. */
|
||||
if (new_page)
|
||||
{
|
||||
|
||||
/* Yes, we were able to allocate a new page. */
|
||||
|
||||
/* Determine if this is the new maximum mapped sector. */
|
||||
if (logical_sector > nand_flash -> lx_nand_flash_max_mapped_sector)
|
||||
{
|
||||
|
||||
/* Remember this maximum mapped sector. */
|
||||
nand_flash -> lx_nand_flash_max_mapped_sector = logical_sector;
|
||||
}
|
||||
|
||||
/* Update the number of free pages. */
|
||||
nand_flash -> lx_nand_flash_free_pages--;
|
||||
|
||||
/* Write the logical sector data to the new page. */
|
||||
status = _lx_nand_flash_driver_write(nand_flash, new_block, new_page, buffer, nand_flash -> lx_nand_flash_words_per_page);
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, new_block, new_page);
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return an error. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Determine if there was an old mapping. */
|
||||
if (old_page)
|
||||
{
|
||||
|
||||
/* Read the extra info of the old page. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, old_block, old_page, (UCHAR *) &old_extra_info, sizeof(old_extra_info));
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, old_block, old_page);
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return an error. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Clear bit 30, which indicates this sector is superceded. */
|
||||
old_extra_info.lx_nand_page_extra_info_logical_sector = old_extra_info.lx_nand_page_extra_info_logical_sector & (~((ULONG) LX_NAND_PAGE_SUPERCEDED));
|
||||
|
||||
/* Write the extra info to old page. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, old_block, old_page, (UCHAR *) &old_extra_info, sizeof(old_extra_info));
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, old_block, old_page);
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return an error. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now build the new mapping entry - with the not valid bit set initially. */
|
||||
new_extra_info.lx_nand_page_extra_info_logical_sector = ((ULONG) LX_NAND_PAGE_VALID) | ((ULONG) LX_NAND_PAGE_SUPERCEDED) | ((ULONG) LX_NAND_PAGE_MAPPING_NOT_VALID) | logical_sector;
|
||||
|
||||
/* Write out the new mapping entry. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, new_block, new_page, (UCHAR *) &new_extra_info, sizeof(new_extra_info));
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, new_block, new_page);
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return an error. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Now clear the not valid bit to make this sector mapping valid. This is done because the writing of the extra bytes itself can
|
||||
be interrupted and we need to make sure this can be detected when the flash is opened again. */
|
||||
new_extra_info.lx_nand_page_extra_info_logical_sector = new_extra_info.lx_nand_page_extra_info_logical_sector & ~((ULONG) LX_NAND_PAGE_MAPPING_NOT_VALID);
|
||||
|
||||
/* Clear the not valid bit. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, new_block, new_page, (UCHAR *) &new_extra_info, sizeof(new_extra_info));
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, new_block, new_page);
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return an error. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
|
||||
/* Increment the number of mapped sectors. */
|
||||
nand_flash -> lx_nand_flash_mapped_pages++;
|
||||
|
||||
/* Was there a previously mapped sector? */
|
||||
if (old_page)
|
||||
{
|
||||
|
||||
/* Now clear bit 31, which indicates this sector is now obsoleted. */
|
||||
old_extra_info.lx_nand_page_extra_info_logical_sector = old_extra_info.lx_nand_page_extra_info_logical_sector & ~((ULONG) LX_NAND_PAGE_VALID);
|
||||
|
||||
/* Write the value back to the flash to clear bit 31. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, old_block, old_page, (UCHAR *) &old_extra_info, sizeof(old_extra_info));
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, old_block, old_page);
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return an error. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Setup pointer to internal buffer. */
|
||||
block_word_ptr = nand_flash -> lx_nand_flash_page_buffer;
|
||||
|
||||
/* 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, old_block, 0, block_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, old_block, 0);
|
||||
|
||||
/* Determine if the error is fatal. */
|
||||
if (status != LX_NAND_ERROR_CORRECTED)
|
||||
{
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return the error. */
|
||||
return(status);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef LX_NAND_FLASH_MAPPING_LIST_UPDATE_DISABLE
|
||||
|
||||
/* Determine if there is a valid mapping list. */
|
||||
if ((block_word_ptr[1] != LX_NAND_PAGE_FREE) &&
|
||||
(block_word_ptr[nand_flash -> lx_nand_flash_pages_per_block] == LX_NAND_PAGE_LIST_VALID))
|
||||
{
|
||||
|
||||
/* Mark the entry as invalid. */
|
||||
block_word_ptr[old_page] &= ~LX_NAND_PAGE_VALID;
|
||||
|
||||
/* Invalidate the page. */
|
||||
status = _lx_nand_flash_driver_write(nand_flash, old_block, 0, block_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Increment the number of obsolete pages. */
|
||||
nand_flash -> lx_nand_flash_obsolete_pages++;
|
||||
|
||||
/* Decrement the number of mapped pages. */
|
||||
nand_flash -> lx_nand_flash_mapped_pages--;
|
||||
|
||||
/* Invalidate the old sector mapping cache entry. */
|
||||
_lx_nand_flash_sector_mapping_cache_invalidate(nand_flash, logical_sector);
|
||||
|
||||
/* Call routine to see if this block is completely obsoleted. If so,
|
||||
we can reclaim it immediately. */
|
||||
_lx_nand_flash_block_obsoleted_check(nand_flash, old_block);
|
||||
}
|
||||
|
||||
/* Read the extra info of the old page. */
|
||||
status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, new_block, new_page, (UCHAR *) &new_extra_info, sizeof(new_extra_info));
|
||||
|
||||
/* Check for an error from flash driver. */
|
||||
if (status)
|
||||
{
|
||||
|
||||
/* Call system error handler. */
|
||||
_lx_nand_flash_system_error(nand_flash, status, old_block, old_page);
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return an error. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Check to see if the logical sector is still mapped to the same block/page. */
|
||||
if (((new_extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK) == logical_sector) &&
|
||||
(new_extra_info.lx_nand_page_extra_info_logical_sector & ((ULONG) LX_NAND_PAGE_VALID)))
|
||||
{
|
||||
|
||||
/* Determine if the new page is the last page of the block. */
|
||||
if (new_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, new_block, new_erase_count);
|
||||
}
|
||||
|
||||
/* Determine if the logical sector mapping cache is enabled. */
|
||||
if (nand_flash -> lx_nand_flash_sector_mapping_cache_enabled)
|
||||
{
|
||||
|
||||
/* Yes, sector mapping cache is enabled, place this logical sector mapping information in the cache. */
|
||||
|
||||
#ifndef LX_NAND_FLASH_DIRECT_MAPPING_CACHE
|
||||
|
||||
/* Calculate the starting index of the sector mapping cache for this sector entry. */
|
||||
i = (logical_sector & LX_NAND_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NAND_SECTOR_MAPPING_CACHE_DEPTH;
|
||||
|
||||
/* Build a pointer to the cache entry. */
|
||||
sector_mapping_cache_entry_ptr = &nand_flash -> lx_nand_flash_sector_mapping_cache[i];
|
||||
|
||||
/* Move all the cache entries down so the oldest is at the bottom. */
|
||||
*(sector_mapping_cache_entry_ptr + 3) = *(sector_mapping_cache_entry_ptr + 2);
|
||||
*(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1);
|
||||
*(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr);
|
||||
|
||||
/* Setup the new logical sector information in the cache. */
|
||||
sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_logical_sector = (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID);
|
||||
sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_block = (USHORT) new_block;
|
||||
sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_page = (USHORT) new_page;
|
||||
#else
|
||||
|
||||
/* Determine if this logical sector fits in the logical sector cache mapping. */
|
||||
if (logical_sector < LX_NAND_SECTOR_MAPPING_CACHE_SIZE)
|
||||
{
|
||||
|
||||
/* Yes, store the logical sector to block/page mapping in the cache. */
|
||||
nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_block = (USHORT) new_block;
|
||||
nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page = (USHORT) new_page;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Indicate the write was successful. */
|
||||
status = LX_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Indicate the write was unsuccessful. */
|
||||
status = LX_NO_SECTORS;
|
||||
}
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nand_flash -> lx_nand_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return the completion status. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,398 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 Simulator */
|
||||
/** */
|
||||
/**************************************************************************/
|
||||
/**************************************************************************/
|
||||
|
||||
|
||||
/* Include necessary files. */
|
||||
|
||||
#include "tx_api.h"
|
||||
#include "lx_api.h"
|
||||
|
||||
/* Define constants for the NAND flash simulation. */
|
||||
|
||||
#define TOTAL_BLOCKS 8
|
||||
#define PHYSICAL_PAGES_PER_BLOCK 16 /* Min value of 2 */
|
||||
#define BYTES_PER_PHYSICAL_PAGE 2048 /* 2048 bytes per page */
|
||||
#define WORDS_PER_PHYSICAL_PAGE 2048/4 /* Words per page */
|
||||
#define SPARE_BYTES_PER_PAGE 64 /* 64 "spare" bytes per page */
|
||||
/* For 2048 byte block spare area: */
|
||||
#define BAD_BLOCK_POSITION 0 /* 0 is the bad block byte postion */
|
||||
#define EXTRA_BYTE_POSITION 2 /* 2 is the extra bytes starting byte postion */
|
||||
#define ECC_BYTE_POSITION 40 /* 40 is the ECC starting byte position */
|
||||
|
||||
|
||||
/* Definition of the spare area is relative to the block size of the NAND part and perhaps manufactures of the NAND part.
|
||||
Here are some common definitions:
|
||||
|
||||
256 Byte Block
|
||||
|
||||
Bytes Meaning
|
||||
|
||||
0,1,2 ECC bytes
|
||||
3,4,6,7 Extra
|
||||
5 Bad block flag
|
||||
|
||||
512 Byte Block
|
||||
|
||||
Bytes Meaning
|
||||
|
||||
0,1,2,3,6,7 ECC bytes
|
||||
8-15 Extra
|
||||
5 Bad block flag
|
||||
|
||||
2048 Byte Block
|
||||
|
||||
Bytes Meaning
|
||||
|
||||
0 Bad block flag
|
||||
2-39 Extra
|
||||
40-63 ECC bytes
|
||||
*/
|
||||
|
||||
|
||||
typedef struct PHYSICAL_PAGE_STRUCT
|
||||
{
|
||||
unsigned long memory[WORDS_PER_PHYSICAL_PAGE];
|
||||
unsigned char spare[SPARE_BYTES_PER_PAGE];
|
||||
} PHYSICAL_PAGE;
|
||||
|
||||
typedef struct NAND_BLOCK_DIAG_STRUCT
|
||||
{
|
||||
unsigned long erases;
|
||||
unsigned long page_writes[PHYSICAL_PAGES_PER_BLOCK];
|
||||
unsigned long max_page_writes[PHYSICAL_PAGES_PER_BLOCK];
|
||||
} NAND_BLOCK_DIAG;
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct NAND_FLASH_BLOCK_STRUCT
|
||||
{
|
||||
PHYSICAL_PAGE physical_pages[PHYSICAL_PAGES_PER_BLOCK];
|
||||
} NAND_FLASH_BLOCK;
|
||||
|
||||
NAND_FLASH_BLOCK nand_memory_area[TOTAL_BLOCKS];
|
||||
|
||||
NAND_BLOCK_DIAG nand_block_diag[TOTAL_BLOCKS];
|
||||
|
||||
|
||||
/* Define NAND flash buffer for LevelX. */
|
||||
|
||||
ULONG nand_flash_simulator_buffer[WORDS_PER_PHYSICAL_PAGE];
|
||||
ULONG *nand_flash_memory;
|
||||
|
||||
|
||||
UINT _lx_nand_flash_simulator_initialize(LX_NAND_FLASH *nand_flash);
|
||||
UINT _lx_nand_flash_simulator_read(ULONG block, ULONG page, ULONG *destination, ULONG words);
|
||||
UINT _lx_nand_flash_simulator_write(ULONG block, ULONG page, ULONG *source, ULONG words);
|
||||
UINT _lx_nand_flash_simulator_block_erase(ULONG block, ULONG erase_count);
|
||||
UINT _lx_nand_flash_simulator_block_erased_verify(ULONG block);
|
||||
UINT _lx_nand_flash_simulator_page_erased_verify(ULONG block, ULONG page);
|
||||
UINT _lx_nand_flash_simulator_erase_all(VOID);
|
||||
UINT _lx_nand_flash_simulator_block_status_get(ULONG block, UCHAR *bad_block_byte);
|
||||
UINT _lx_nand_flash_simulator_block_status_set(ULONG block, UCHAR bad_block_byte);
|
||||
UINT _lx_nand_flash_simulator_extra_bytes_get(ULONG block, ULONG page, UCHAR *destination, UINT size);
|
||||
UINT _lx_nand_flash_simulator_extra_bytes_set(ULONG block, ULONG page, UCHAR *source, UINT size);
|
||||
UINT _lx_nand_flash_simulator_system_error(UINT error_code, ULONG block, ULONG page);
|
||||
|
||||
|
||||
|
||||
UINT _lx_nand_flash_simulator_initialize(LX_NAND_FLASH *nand_flash)
|
||||
{
|
||||
|
||||
/* Setup the buffer pointer. */
|
||||
nand_flash_memory = (ULONG *) &nand_memory_area[0];
|
||||
|
||||
/* Setup geometry of the NAND flash. */
|
||||
nand_flash -> lx_nand_flash_total_blocks = TOTAL_BLOCKS;
|
||||
nand_flash -> lx_nand_flash_pages_per_block = PHYSICAL_PAGES_PER_BLOCK;
|
||||
nand_flash -> lx_nand_flash_bytes_per_page = BYTES_PER_PHYSICAL_PAGE;
|
||||
|
||||
/* Setup function pointers for the NAND flash services. */
|
||||
nand_flash -> lx_nand_flash_driver_read = _lx_nand_flash_simulator_read;
|
||||
nand_flash -> lx_nand_flash_driver_write = _lx_nand_flash_simulator_write;
|
||||
nand_flash -> lx_nand_flash_driver_block_erase = _lx_nand_flash_simulator_block_erase;
|
||||
nand_flash -> lx_nand_flash_driver_block_erased_verify = _lx_nand_flash_simulator_block_erased_verify;
|
||||
nand_flash -> lx_nand_flash_driver_page_erased_verify = _lx_nand_flash_simulator_page_erased_verify;
|
||||
nand_flash -> lx_nand_flash_driver_block_status_get = _lx_nand_flash_simulator_block_status_get;
|
||||
nand_flash -> lx_nand_flash_driver_block_status_set = _lx_nand_flash_simulator_block_status_set;
|
||||
nand_flash -> lx_nand_flash_driver_extra_bytes_get = _lx_nand_flash_simulator_extra_bytes_get;
|
||||
nand_flash -> lx_nand_flash_driver_extra_bytes_set = _lx_nand_flash_simulator_extra_bytes_set;
|
||||
nand_flash -> lx_nand_flash_driver_system_error = _lx_nand_flash_simulator_system_error;
|
||||
|
||||
/* Setup local buffer for NAND flash operation. This buffer must be the page size of the NAND flash memory. */
|
||||
nand_flash -> lx_nand_flash_page_buffer = &nand_flash_simulator_buffer[0];
|
||||
|
||||
/* Return success. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
UINT _lx_nand_flash_simulator_read(ULONG block, ULONG page, ULONG *destination, ULONG words)
|
||||
{
|
||||
|
||||
ULONG *flash_address;
|
||||
|
||||
|
||||
/* Pickup the flash address. */
|
||||
flash_address = &(nand_memory_area[block].physical_pages[page].memory[0]);
|
||||
|
||||
/* Loop to read flash. */
|
||||
while (words--)
|
||||
{
|
||||
/* Copy word. */
|
||||
*destination++ = *flash_address++;
|
||||
}
|
||||
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
UINT _lx_nand_flash_simulator_write(ULONG block, ULONG page, ULONG *source, ULONG words)
|
||||
{
|
||||
|
||||
ULONG *flash_address;
|
||||
|
||||
|
||||
/* Increment the diag info. */
|
||||
nand_block_diag[block].page_writes[page]++;
|
||||
if (nand_block_diag[block].page_writes[page] > nand_block_diag[block].max_page_writes[page])
|
||||
nand_block_diag[block].max_page_writes[page] = nand_block_diag[block].page_writes[page];
|
||||
|
||||
/* Pickup the flash address. */
|
||||
flash_address = &(nand_memory_area[block].physical_pages[page].memory[0]);
|
||||
|
||||
/* Loop to write flash. */
|
||||
while (words--)
|
||||
{
|
||||
|
||||
/* Can the word be written? We can clear new bits, but just can't unclear
|
||||
in a NAND device. */
|
||||
if ((*source & *flash_address) != *source)
|
||||
return(LX_INVALID_WRITE);
|
||||
|
||||
/* Copy word. */
|
||||
*flash_address++ = *source++;
|
||||
}
|
||||
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
UINT _lx_nand_flash_simulator_block_erase(ULONG block, ULONG erase_count)
|
||||
{
|
||||
|
||||
ULONG *pointer;
|
||||
ULONG words;
|
||||
UINT i;
|
||||
|
||||
LX_PARAMETER_NOT_USED(erase_count);
|
||||
|
||||
/* Increment the diag info. */
|
||||
nand_block_diag[block].erases++;
|
||||
for (i = 0; i < PHYSICAL_PAGES_PER_BLOCK;i++)
|
||||
nand_block_diag[block].page_writes[i] = 0;
|
||||
|
||||
/* Setup pointer. */
|
||||
pointer = (ULONG *) &nand_memory_area[block];
|
||||
|
||||
/* Loop to erase block. */
|
||||
words = sizeof(NAND_FLASH_BLOCK)/sizeof(ULONG);
|
||||
while (words--)
|
||||
{
|
||||
|
||||
/* Erase word of block. */
|
||||
*pointer++ = (ULONG) 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
UINT _lx_nand_flash_simulator_erase_all(VOID)
|
||||
{
|
||||
|
||||
ULONG *pointer;
|
||||
ULONG words;
|
||||
UINT i, j;
|
||||
|
||||
|
||||
/* Increment the diag info. */
|
||||
for (i = 0; i < TOTAL_BLOCKS; i++)
|
||||
{
|
||||
nand_block_diag[i].erases = 0;
|
||||
for (j = 0; j < PHYSICAL_PAGES_PER_BLOCK;j++)
|
||||
nand_block_diag[i].page_writes[j] = 0;
|
||||
}
|
||||
|
||||
/* Setup pointer. */
|
||||
pointer = (ULONG *) &nand_memory_area[0];
|
||||
|
||||
/* Loop to erase block. */
|
||||
words = sizeof(nand_memory_area)/sizeof(ULONG);
|
||||
while (words--)
|
||||
{
|
||||
|
||||
/* Erase word of block. */
|
||||
*pointer++ = (ULONG) 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
UINT _lx_nand_flash_simulator_block_erased_verify(ULONG block)
|
||||
{
|
||||
|
||||
ULONG *word_ptr;
|
||||
ULONG words;
|
||||
|
||||
/* Determine if the block is completely erased. */
|
||||
|
||||
/* Pickup the pointer to the first word of the block. */
|
||||
word_ptr = (ULONG *) &nand_memory_area[block];
|
||||
|
||||
/* Calculate the number of words in a block. */
|
||||
words = sizeof(NAND_FLASH_BLOCK)/sizeof(ULONG);
|
||||
|
||||
/* Loop to check if the block is erased. */
|
||||
while (words--)
|
||||
{
|
||||
|
||||
/* Is this word erased? */
|
||||
if (*word_ptr++ != 0xFFFFFFFF)
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Return success. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
UINT _lx_nand_flash_simulator_page_erased_verify(ULONG block, ULONG page)
|
||||
{
|
||||
|
||||
ULONG *word_ptr;
|
||||
ULONG words;
|
||||
|
||||
/* Determine if the block is completely erased. */
|
||||
|
||||
/* Pickup the pointer to the first word of the block's page. */
|
||||
word_ptr = (ULONG *) &nand_memory_area[block].physical_pages[page];
|
||||
|
||||
/* Calculate the number of words in a block. */
|
||||
words = WORDS_PER_PHYSICAL_PAGE;
|
||||
|
||||
/* Loop to check if the page is erased. */
|
||||
while (words--)
|
||||
{
|
||||
|
||||
/* Is this word erased? */
|
||||
if (*word_ptr++ != 0xFFFFFFFF)
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Return success. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
UINT _lx_nand_flash_simulator_block_status_get(ULONG block, UCHAR *bad_block_byte)
|
||||
{
|
||||
|
||||
/* Pickup the bad block byte and return it. */
|
||||
*bad_block_byte = nand_memory_area[block].physical_pages[0].spare[BAD_BLOCK_POSITION];
|
||||
|
||||
/* Return success. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
UINT _lx_nand_flash_simulator_block_status_set(ULONG block, UCHAR bad_block_byte)
|
||||
{
|
||||
|
||||
/* Set the bad block byte. */
|
||||
nand_memory_area[block].physical_pages[0].spare[BAD_BLOCK_POSITION] = bad_block_byte;
|
||||
|
||||
/* Return success. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
UINT _lx_nand_flash_simulator_extra_bytes_get(ULONG block, ULONG page, UCHAR *destination, UINT size)
|
||||
{
|
||||
|
||||
UCHAR *source;
|
||||
|
||||
|
||||
/* Setup source pointer in the spare area. */
|
||||
source = (UCHAR *) &(nand_memory_area[block].physical_pages[page].spare[EXTRA_BYTE_POSITION]);
|
||||
|
||||
/* Loop to return the extra bytes requested. */
|
||||
while (size--)
|
||||
{
|
||||
|
||||
/* Retrieve an extra byte from the spare area. */
|
||||
*destination++ = *source++;
|
||||
}
|
||||
|
||||
/* Return success. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
UINT _lx_nand_flash_simulator_extra_bytes_set(ULONG block, ULONG page, UCHAR *source, UINT size)
|
||||
{
|
||||
|
||||
UCHAR *destination;
|
||||
|
||||
|
||||
/* Increment the diag info. */
|
||||
nand_block_diag[block].page_writes[page]++;
|
||||
if (nand_block_diag[block].page_writes[page] > nand_block_diag[block].max_page_writes[page])
|
||||
nand_block_diag[block].max_page_writes[page] = nand_block_diag[block].page_writes[page];
|
||||
|
||||
/* Setup destination pointer in the spare area. */
|
||||
destination = (UCHAR *) &(nand_memory_area[block].physical_pages[page].spare[EXTRA_BYTE_POSITION]);
|
||||
|
||||
/* Loop to set the extra bytes. */
|
||||
while (size--)
|
||||
{
|
||||
|
||||
/* Set an extra byte in the spare area. */
|
||||
*destination++ = *source++;
|
||||
}
|
||||
|
||||
/* Return success. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
UINT _lx_nand_flash_simulator_system_error(UINT error_code, ULONG block, ULONG page)
|
||||
{
|
||||
LX_PARAMETER_NOT_USED(error_code);
|
||||
LX_PARAMETER_NOT_USED(block);
|
||||
LX_PARAMETER_NOT_USED(page);
|
||||
|
||||
/* Custom processing goes here... all errors except for LX_NAND_ERROR_CORRECTED are fatal. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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_system_error PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function handles system errors in the NAND flash. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nand_flash NAND flash instance */
|
||||
/* error_code System error code */
|
||||
/* block Block where error occurred */
|
||||
/* page Page where error occurred */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* (lx_nand_flash_driver_system_error) Driver system error handler */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Internal LevelX */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
VOID _lx_nand_flash_system_error(LX_NAND_FLASH *nand_flash, UINT error_code, ULONG block, ULONG page)
|
||||
{
|
||||
|
||||
/* Increment the system error counter. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_system_errors++;
|
||||
|
||||
/* Save the most recent system error code. */
|
||||
nand_flash -> lx_nand_flash_diagnostic_system_error = error_code;
|
||||
|
||||
/* Determine if the system error is a NAND page corrected error. */
|
||||
if (error_code == LX_NAND_ERROR_CORRECTED)
|
||||
{
|
||||
|
||||
/* Yes, increment error correction information. */
|
||||
nand_flash -> lx_nand_flash_page_corrections++;
|
||||
|
||||
/* Remember the last block/page of corrected error. */
|
||||
nand_flash -> lx_nand_flash_last_block_correction = block;
|
||||
nand_flash -> lx_nand_flash_last_page_correction = page;
|
||||
}
|
||||
|
||||
/* Determine if the driver has setup a system error handler. */
|
||||
if (nand_flash -> lx_nand_flash_driver_system_error)
|
||||
{
|
||||
|
||||
/* Yes, call the driver's system error handler. */
|
||||
(nand_flash -> lx_nand_flash_driver_system_error)(error_code, block, page);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,562 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** NOR 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_nor_flash_block_reclaim PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function reclaims one block from the NOR flash. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nor_flash NOR flash instance */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nor_flash_driver_block_erase Driver erase block */
|
||||
/* _lx_nor_flash_driver_write Driver flash sector write */
|
||||
/* _lx_nor_flash_driver_read Driver flash sector read */
|
||||
/* _lx_nor_flash_next_block_to_erase_find */
|
||||
/* Find next block to erase */
|
||||
/* _lx_nor_flash_physical_sector_allocate */
|
||||
/* Allocate new logical sector */
|
||||
/* _lx_nor_flash_sector_mapping_cache_invalidate */
|
||||
/* Invalidate cache entry */
|
||||
/* _lx_nor_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 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nor_flash_block_reclaim(LX_NOR_FLASH *nor_flash)
|
||||
{
|
||||
|
||||
ULONG *block_word_ptr;
|
||||
ULONG *list_word_ptr;
|
||||
ULONG list_word;
|
||||
ULONG i;
|
||||
ULONG erase_block;
|
||||
ULONG erase_count;
|
||||
ULONG temp_erase_count;
|
||||
ULONG erase_started_value;
|
||||
ULONG mapped_sectors;
|
||||
ULONG obsolete_sectors;
|
||||
ULONG free_sectors;
|
||||
ULONG logical_sector;
|
||||
ULONG *new_mapping_address;
|
||||
ULONG *new_sector_address;
|
||||
ULONG new_mapping_entry;
|
||||
UINT status;
|
||||
|
||||
|
||||
/* Determine the next block to erase. */
|
||||
_lx_nor_flash_next_block_to_erase_find(nor_flash, &erase_block, &erase_count, &mapped_sectors, &obsolete_sectors);
|
||||
|
||||
/* Determine if the search pointer is set for this block. */
|
||||
if (nor_flash -> lx_nor_flash_free_block_search == erase_block)
|
||||
{
|
||||
|
||||
/* Ensure the search block is not the block we are trying to free. */
|
||||
nor_flash -> lx_nor_flash_free_block_search = erase_block + 1;
|
||||
|
||||
/* Check for wrap condition. */
|
||||
if (nor_flash -> lx_nor_flash_free_block_search >= nor_flash -> lx_nor_flash_total_blocks)
|
||||
nor_flash -> lx_nor_flash_free_block_search = 0;
|
||||
}
|
||||
|
||||
/* Setup the block word pointer to the first word of the search block. */
|
||||
block_word_ptr = nor_flash -> lx_nor_flash_base_address + (nor_flash -> lx_nor_flash_words_per_block * erase_block);
|
||||
|
||||
/* Determine if this block is completely obsolete. */
|
||||
if (obsolete_sectors == nor_flash -> lx_nor_flash_physical_sectors_per_block)
|
||||
{
|
||||
|
||||
/* Write the erased started indication. */
|
||||
erase_started_value = LX_BLOCK_ERASE_STARTED;
|
||||
status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_started_value, 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 the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Erase the entire block. */
|
||||
status = _lx_nor_flash_driver_block_erase(nor_flash, erase_block, erase_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 the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Increment the erase count. */
|
||||
erase_count++;
|
||||
|
||||
/* Determine if the new erase count exceeds the maximum. */
|
||||
if (erase_count > ((ULONG) LX_BLOCK_ERASE_COUNT_MAX))
|
||||
{
|
||||
|
||||
/* Yes, erase count is in overflow. Stay at the maximum count. */
|
||||
erase_count = ((ULONG) LX_BLOCK_ERASE_COUNT_MAX);
|
||||
}
|
||||
|
||||
/* Determine if we need to update the maximum erase count. */
|
||||
if (erase_count > nor_flash -> lx_nor_flash_maximum_erase_count)
|
||||
{
|
||||
|
||||
/* Yes, a new maximum is present. */
|
||||
nor_flash -> lx_nor_flash_maximum_erase_count = erase_count;
|
||||
}
|
||||
|
||||
/* 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 + (nor_flash -> lx_nor_flash_block_bit_map_words - 1)),
|
||||
&(nor_flash -> lx_nor_flash_block_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 the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Write the initial erase count for the block with upper bit set. */
|
||||
temp_erase_count = (erase_count | LX_BLOCK_ERASED);
|
||||
status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &temp_erase_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 the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Write the final initial erase count for the block. */
|
||||
status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_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 the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Update parameters of this flash. */
|
||||
nor_flash -> lx_nor_flash_free_physical_sectors = nor_flash -> lx_nor_flash_free_physical_sectors + obsolete_sectors;
|
||||
nor_flash -> lx_nor_flash_obsolete_physical_sectors = nor_flash -> lx_nor_flash_obsolete_physical_sectors - obsolete_sectors;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Calculate the number of free sectors in this block. */
|
||||
free_sectors = nor_flash -> lx_nor_flash_physical_sectors_per_block - (obsolete_sectors + mapped_sectors);
|
||||
|
||||
/* Determine if there are enough free sectors outside of this block to reclaim this block. */
|
||||
if (mapped_sectors <= (nor_flash -> lx_nor_flash_free_physical_sectors - free_sectors))
|
||||
{
|
||||
|
||||
/* Setup a pointer to the mapped list. */
|
||||
list_word_ptr = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset;
|
||||
|
||||
/* Now search through the list to find mapped sectors to move. */
|
||||
for (i = 0; i < nor_flash -> lx_nor_flash_physical_sectors_per_block; i++)
|
||||
{
|
||||
|
||||
/* Pickup the mapped sector list entry. */
|
||||
#ifdef LX_DIRECT_READ
|
||||
|
||||
/* Read the word directly. */
|
||||
list_word = *(list_word_ptr);
|
||||
#else
|
||||
status = _lx_nor_flash_driver_read(nor_flash, list_word_ptr, &list_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 the error. */
|
||||
return(status);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Determine if the entry hasn't been used. */
|
||||
if (list_word == LX_NOR_PHYSICAL_SECTOR_FREE)
|
||||
{
|
||||
|
||||
/* Since allocations are done sequentially in the block, we know nothing
|
||||
else exists after this point. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Is this entry mapped? */
|
||||
if (list_word & LX_NOR_PHYSICAL_SECTOR_VALID)
|
||||
{
|
||||
|
||||
/* Pickup the logical sector associated with this mapped physical sector. */
|
||||
logical_sector = list_word & LX_NOR_LOGICAL_SECTOR_MASK;
|
||||
|
||||
/* Invalidate the old sector mapping cache entry. */
|
||||
_lx_nor_flash_sector_mapping_cache_invalidate(nor_flash, logical_sector);
|
||||
|
||||
/* Allocate a new physical sector for this write. */
|
||||
_lx_nor_flash_physical_sector_allocate(nor_flash, logical_sector, &new_mapping_address, &new_sector_address);
|
||||
|
||||
/* Check to see if the new sector is also in the erase block. */
|
||||
if ((new_sector_address >= block_word_ptr) && (new_sector_address < (block_word_ptr + nor_flash -> lx_nor_flash_words_per_block)))
|
||||
{
|
||||
|
||||
/* Yes, the new sector was found in the block to be erased. Simply move the search pointer
|
||||
to the block after the erase block and search for another sector from there. */
|
||||
nor_flash -> lx_nor_flash_free_block_search = erase_block + 1;
|
||||
|
||||
/* Check for wrap condition. */
|
||||
if (nor_flash -> lx_nor_flash_free_block_search >= nor_flash -> lx_nor_flash_total_blocks)
|
||||
nor_flash -> lx_nor_flash_free_block_search = 0;
|
||||
|
||||
/* Allocate a new physical sector for this write. */
|
||||
_lx_nor_flash_physical_sector_allocate(nor_flash, logical_sector, &new_mapping_address, &new_sector_address);
|
||||
|
||||
/* Check again for the new sector inside of the block to erase. This should be impossible, since
|
||||
we check previously if there are enough free sectors outside of this block needed to reclaim
|
||||
this block. */
|
||||
if ((new_sector_address >= block_word_ptr) && (new_sector_address < (block_word_ptr + LX_NOR_SECTOR_SIZE)))
|
||||
{
|
||||
|
||||
/* System error, a new sector is not available outside of the erase block.
|
||||
Clear the new sector so we fall through to the error handling. */
|
||||
new_mapping_address = LX_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if the new sector allocation was successful. */
|
||||
if (new_mapping_address)
|
||||
{
|
||||
|
||||
/* Yes, we were able to allocate a new physical sector. */
|
||||
|
||||
#ifdef LX_DIRECT_READ
|
||||
/* First, write the sector data to the new physical sector. */
|
||||
status = _lx_nor_flash_driver_write(nor_flash, new_sector_address, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset) +
|
||||
(i * LX_NOR_SECTOR_SIZE), LX_NOR_SECTOR_SIZE);
|
||||
|
||||
/* 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 the error. */
|
||||
return(status);
|
||||
}
|
||||
#else
|
||||
|
||||
/* First, read the sector data into the internal memory of the NOR flash instance. This internal memory
|
||||
is supplied by the underlying driver during initialization. */
|
||||
status = _lx_nor_flash_driver_read(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset) +
|
||||
(i * LX_NOR_SECTOR_SIZE), nor_flash -> lx_nor_flash_sector_buffer,
|
||||
LX_NOR_SECTOR_SIZE);
|
||||
|
||||
/* 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 the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Next, write the sector data from the internal buffer to the new physical sector. */
|
||||
status = _lx_nor_flash_driver_write(nor_flash, new_sector_address, nor_flash -> lx_nor_flash_sector_buffer, LX_NOR_SECTOR_SIZE);
|
||||
|
||||
/* 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 the error. */
|
||||
return(status);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Now deprecate the old sector mapping. */
|
||||
|
||||
/* Clear bit 30, which indicates this sector is superceded. */
|
||||
list_word = list_word & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED);
|
||||
|
||||
/* Write the value back to the flash to clear bit 30. */
|
||||
status = _lx_nor_flash_driver_write(nor_flash, list_word_ptr, &list_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 the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Now build the new mapping entry - with the not valid bit set initially. */
|
||||
new_mapping_entry = ((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID) | ((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED) | (ULONG) LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID | logical_sector;
|
||||
|
||||
/* Write out the new mapping entry. */
|
||||
status = _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 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 the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Now clear the not valid bit to make this sector mapping valid. This is done because the writing of the extra bytes itself can
|
||||
be interrupted and we need to make sure this can be detected when the flash is opened again. */
|
||||
new_mapping_entry = new_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID);
|
||||
|
||||
/* Clear the not valid bit. */
|
||||
status = _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 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 the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Now clear bit 31, which indicates this sector is now obsoleted. */
|
||||
list_word = list_word & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID);
|
||||
|
||||
/* Write the value back to the flash to clear bit 31. */
|
||||
status = _lx_nor_flash_driver_write(nor_flash, list_word_ptr, &list_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 the error. */
|
||||
return(status);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Call system error handler - the allocation should always succeed at this point. */
|
||||
_lx_nor_flash_system_error(nor_flash, LX_SYSTEM_ALLOCATION_FAILED);
|
||||
|
||||
/* Return the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Decrement the number of mapped sectors. */
|
||||
mapped_sectors--;
|
||||
|
||||
/* Determine if we are done. */
|
||||
if (mapped_sectors == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Move the list pointer ahead. */
|
||||
list_word_ptr++;
|
||||
}
|
||||
|
||||
/* Write the erased started indication. */
|
||||
erase_started_value = LX_BLOCK_ERASE_STARTED;
|
||||
status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_started_value, 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 the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Erase the entire block. */
|
||||
status = _lx_nor_flash_driver_block_erase(nor_flash, erase_block, erase_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 the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Increment the erase count. */
|
||||
erase_count++;
|
||||
|
||||
/* Determine if the new erase count exceeds the maximum. */
|
||||
if (erase_count > ((ULONG) LX_BLOCK_ERASE_COUNT_MAX))
|
||||
{
|
||||
|
||||
/* Yes, erase count is in overflow. Stay at the maximum count. */
|
||||
erase_count = ((ULONG) LX_BLOCK_ERASE_COUNT_MAX);
|
||||
}
|
||||
|
||||
/* Determine if we need to update the maximum erase count. */
|
||||
if (erase_count > nor_flash -> lx_nor_flash_maximum_erase_count)
|
||||
{
|
||||
|
||||
/* Yes, a new maximum is present. */
|
||||
nor_flash -> lx_nor_flash_maximum_erase_count = erase_count;
|
||||
}
|
||||
|
||||
/* 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 + (nor_flash -> lx_nor_flash_block_bit_map_words - 1)) ,
|
||||
&(nor_flash -> lx_nor_flash_block_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 the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Write the initial erase count for the block with the upper bit set. */
|
||||
temp_erase_count = (erase_count | LX_BLOCK_ERASED);
|
||||
status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &temp_erase_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 the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Write the final initial erase count for the block. */
|
||||
status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_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 the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Update parameters of this flash. */
|
||||
nor_flash -> lx_nor_flash_free_physical_sectors = nor_flash -> lx_nor_flash_free_physical_sectors + obsolete_sectors;
|
||||
nor_flash -> lx_nor_flash_obsolete_physical_sectors = nor_flash -> lx_nor_flash_obsolete_physical_sectors - obsolete_sectors;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return status. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** NOR 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_nor_flash_close PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function closes a NOR flash instance. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nor_flash NOR flash instance */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* tx_mutex_delete Delete thread-safe mutex */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Application Code */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nor_flash_close(LX_NOR_FLASH *nor_flash)
|
||||
{
|
||||
|
||||
TX_INTERRUPT_SAVE_AREA
|
||||
|
||||
|
||||
/* Lockout interrupts for NOR flash close. */
|
||||
TX_DISABLE
|
||||
|
||||
/* See if the media is the only one on the media opened list. */
|
||||
if ((_lx_nor_flash_opened_ptr == nor_flash) &&
|
||||
(_lx_nor_flash_opened_ptr == nor_flash -> lx_nor_flash_open_next) &&
|
||||
(_lx_nor_flash_opened_ptr == nor_flash -> lx_nor_flash_open_previous))
|
||||
{
|
||||
|
||||
/* Only opened NOR flash, just set the opened list to NULL. */
|
||||
_lx_nor_flash_opened_ptr = LX_NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Otherwise, not the only opened NOR flash, link-up the neighbors. */
|
||||
(nor_flash -> lx_nor_flash_open_next) -> lx_nor_flash_open_previous =
|
||||
nor_flash -> lx_nor_flash_open_previous;
|
||||
(nor_flash -> lx_nor_flash_open_previous) -> lx_nor_flash_open_next =
|
||||
nor_flash -> lx_nor_flash_open_next;
|
||||
|
||||
/* See if we have to update the opened list head pointer. */
|
||||
if (_lx_nor_flash_opened_ptr == nor_flash)
|
||||
{
|
||||
|
||||
/* Yes, move the head pointer to the next opened NOR flash. */
|
||||
_lx_nor_flash_opened_ptr = nor_flash -> lx_nor_flash_open_next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Decrement the opened NOR flash counter. */
|
||||
_lx_nor_flash_opened_count--;
|
||||
|
||||
/* Finally, indicate that this NOR flash is closed. */
|
||||
nor_flash -> lx_nor_flash_state = LX_NOR_FLASH_CLOSED;
|
||||
|
||||
/* Restore interrupt posture. */
|
||||
TX_RESTORE
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Delete the thread safe mutex. */
|
||||
tx_mutex_delete(&nor_flash -> lx_nor_flash_mutex);
|
||||
#endif
|
||||
/* Return success. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** NOR 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_nor_flash_defragment PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function defragments the NOR flash. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nor_flash NOR flash instance */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nor_flash_block_reclaim Reclaim a NOR flash block */
|
||||
/* tx_mutex_get Get thread protection */
|
||||
/* tx_mutex_put Release thread protection */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Application Code */
|
||||
/* Internal LevelX */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nor_flash_defragment(LX_NOR_FLASH *nor_flash)
|
||||
{
|
||||
|
||||
ULONG i;
|
||||
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Obtain the thread safe mutex. */
|
||||
tx_mutex_get(&nor_flash -> lx_nor_flash_mutex, TX_WAIT_FOREVER);
|
||||
#endif
|
||||
|
||||
/* Loop for max number of blocks, while there are obsolete count. */
|
||||
for (i = 0; i < nor_flash -> lx_nor_flash_total_blocks; i++)
|
||||
{
|
||||
|
||||
/* Determine if there is any more defragment work. */
|
||||
if (nor_flash -> lx_nor_flash_obsolete_physical_sectors == 0)
|
||||
break;
|
||||
|
||||
/* Call the block reclaim function to defragment. */
|
||||
_lx_nor_flash_block_reclaim(nor_flash);
|
||||
}
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return successful completion. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** NOR 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_nor_flash_driver_block_erase PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function performs a NOR flash block erase. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nor_flash NOR flash instance */
|
||||
/* block Block number to erase */
|
||||
/* erase_count Erase count for this block */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* (lx_nor_flash_driver_block_erase) Actual driver block erase */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Application Code */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nor_flash_driver_block_erase(LX_NOR_FLASH *nor_flash, ULONG block, ULONG erase_count)
|
||||
{
|
||||
|
||||
UINT status;
|
||||
|
||||
#ifndef LX_NOR_DISABLE_EXTENDED_CACHE
|
||||
|
||||
UINT i;
|
||||
ULONG *block_start_address;
|
||||
ULONG *block_end_address;
|
||||
ULONG *cache_entry_start;
|
||||
ULONG *cache_entry_end;
|
||||
|
||||
|
||||
/* Calculate the block starting address. */
|
||||
block_start_address = nor_flash -> lx_nor_flash_base_address + (block * nor_flash -> lx_nor_flash_words_per_block);
|
||||
block_end_address = block_start_address + nor_flash -> lx_nor_flash_words_per_block;
|
||||
|
||||
/* Loop through the cache entries to see if there is a sector in cache. */
|
||||
for (i = 0; i < nor_flash -> lx_nor_flash_extended_cache_entries; i++)
|
||||
{
|
||||
|
||||
/* Search through the cache to see if this cache entry needs to be invalidated. */
|
||||
|
||||
/* Determine the cache entry addresses. */
|
||||
cache_entry_start = nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_sector_address;
|
||||
cache_entry_end = cache_entry_start + LX_NOR_SECTOR_SIZE;
|
||||
|
||||
/* Determine if the flash address in in the cache entry. */
|
||||
if ((cache_entry_start) && (block_start_address <= cache_entry_start) && (block_end_address > cache_entry_end))
|
||||
{
|
||||
|
||||
/* Yes, this cache entry is in the block to be erased so invalidate it. */
|
||||
nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_sector_address = LX_NULL;
|
||||
nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_access_count = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Call the actual driver block erase function. */
|
||||
status = (nor_flash -> lx_nor_flash_driver_block_erase)(block, erase_count);
|
||||
|
||||
/* Return completion status. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** NOR 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_nor_flash_extended_cache_read PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function performs a read of the NOR flash memory. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nor_flash NOR flash instance */
|
||||
/* flash_address Address of NOR flash to read */
|
||||
/* destination Destination for the read */
|
||||
/* words Number of words to read */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* (lx_nor_flash_driver_read) Actual driver read */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Application Code */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nor_flash_driver_read(LX_NOR_FLASH *nor_flash, ULONG *flash_address, ULONG *destination, ULONG words)
|
||||
{
|
||||
#ifndef LX_NOR_DISABLE_EXTENDED_CACHE
|
||||
|
||||
UINT status;
|
||||
UINT i;
|
||||
ULONG *cache_entry_start;
|
||||
ULONG *cache_entry_end;
|
||||
ULONG cache_offset;
|
||||
UINT least_used_cache_entry;
|
||||
|
||||
|
||||
/* Is the request a whole sector or a partial sector. */
|
||||
if ((words == 1) && (nor_flash -> lx_nor_flash_extended_cache_entries))
|
||||
{
|
||||
|
||||
/* One word request, which implies that it is a NOR flash metadata read. */
|
||||
|
||||
|
||||
/* Initialize the least used cache entry. */
|
||||
least_used_cache_entry = 0;
|
||||
|
||||
do
|
||||
{
|
||||
|
||||
/* Loop through the cache entries to see if there is a sector in cache. */
|
||||
for (i = 0; i < nor_flash -> lx_nor_flash_extended_cache_entries; i++)
|
||||
{
|
||||
|
||||
/* Search through the cache to find the entry. */
|
||||
|
||||
/* Determine the cache entry addresses. */
|
||||
cache_entry_start = nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_sector_address;
|
||||
cache_entry_end = cache_entry_start + LX_NOR_SECTOR_SIZE;
|
||||
|
||||
/* Determine if the flash address in in the cache entry. */
|
||||
if ((cache_entry_start) && (flash_address >= cache_entry_start) && (flash_address < cache_entry_end))
|
||||
{
|
||||
|
||||
/* Yes, we found the entry. */
|
||||
|
||||
/* Increment the accessed count. */
|
||||
nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_access_count++;
|
||||
|
||||
/* Calculate the offset into the cache entry. */
|
||||
cache_offset = (ULONG)(flash_address - cache_entry_start);
|
||||
|
||||
/* Copy the word from the cache. */
|
||||
*destination = *(nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_sector_memory + cache_offset);
|
||||
|
||||
/* Increment the number of cache hits. */
|
||||
nor_flash -> lx_nor_flash_extended_cache_hits++;
|
||||
|
||||
/* Return success. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Determine if we have a new least used sector. */
|
||||
if (i != least_used_cache_entry)
|
||||
{
|
||||
|
||||
/* Determine if this entry has a smaller accessed count. */
|
||||
if (nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_access_count <
|
||||
nor_flash -> lx_nor_flash_extended_cache[least_used_cache_entry].lx_nor_flash_extended_cache_entry_access_count)
|
||||
{
|
||||
|
||||
/* New least used entry. */
|
||||
least_used_cache_entry = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now read in the sector into the cache. */
|
||||
cache_offset = (ULONG)(flash_address - nor_flash -> lx_nor_flash_base_address);
|
||||
cache_offset = cache_offset & ~((ULONG) (LX_NOR_SECTOR_SIZE-1));
|
||||
cache_entry_start = nor_flash -> lx_nor_flash_base_address + cache_offset;
|
||||
|
||||
/* Call the actual driver read function. */
|
||||
status = (nor_flash -> lx_nor_flash_driver_read)(cache_entry_start,
|
||||
nor_flash -> lx_nor_flash_extended_cache[least_used_cache_entry].lx_nor_flash_extended_cache_entry_sector_memory,
|
||||
LX_NOR_SECTOR_SIZE);
|
||||
|
||||
/* Determine if there was an error. */
|
||||
if (status != LX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Return the error to the caller. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Setup the cache entry. */
|
||||
nor_flash -> lx_nor_flash_extended_cache[least_used_cache_entry].lx_nor_flash_extended_cache_entry_sector_address = cache_entry_start;
|
||||
nor_flash -> lx_nor_flash_extended_cache[least_used_cache_entry].lx_nor_flash_extended_cache_entry_access_count = 0;
|
||||
|
||||
/* Increment the number of cache misses. */
|
||||
nor_flash -> lx_nor_flash_extended_cache_misses++;
|
||||
|
||||
/* Decrement the number of cache hits, so that the increment that will happen next will be cancelled out. */
|
||||
nor_flash -> lx_nor_flash_extended_cache_hits--;
|
||||
|
||||
} while (status == LX_SUCCESS);
|
||||
|
||||
/* Return success. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Call the actual driver read function. */
|
||||
status = (nor_flash -> lx_nor_flash_driver_read)(flash_address, destination, words);
|
||||
|
||||
/* Return completion status. */
|
||||
return(status);
|
||||
}
|
||||
#else
|
||||
UINT status;
|
||||
|
||||
|
||||
/* Call the actual driver read function. */
|
||||
status = (nor_flash -> lx_nor_flash_driver_read)(flash_address, destination, words);
|
||||
|
||||
/* Return completion status. */
|
||||
return(status);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** NOR 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_nor_flash_driver_write PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function performs a write of the NOR flash memory. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nor_flash NOR flash instance */
|
||||
/* flash_address Address of NOR flash to write */
|
||||
/* source Source for the write */
|
||||
/* words Number of words to write */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* (lx_nor_flash_driver_write) Actual driver write */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Application Code */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nor_flash_driver_write(LX_NOR_FLASH *nor_flash, ULONG *flash_address, ULONG *source, ULONG words)
|
||||
{
|
||||
|
||||
#ifndef LX_NOR_DISABLE_EXTENDED_CACHE
|
||||
|
||||
UINT status;
|
||||
UINT i;
|
||||
ULONG *cache_entry_start;
|
||||
ULONG *cache_entry_end;
|
||||
ULONG cache_offset;
|
||||
|
||||
|
||||
/* Is the request a whole sector or a partial sector. */
|
||||
if ((words == 1) && (nor_flash -> lx_nor_flash_extended_cache_entries))
|
||||
{
|
||||
|
||||
/* One word request, which implies that it is a NOR flash metadata write. */
|
||||
|
||||
/* Loop through the cache entries to see if there is a sector in cache. */
|
||||
for (i = 0; i < nor_flash -> lx_nor_flash_extended_cache_entries; i++)
|
||||
{
|
||||
|
||||
/* Search through the cache to see if there is a cache entry. */
|
||||
|
||||
/* Determine the cache entry addresses. */
|
||||
cache_entry_start = nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_sector_address;
|
||||
cache_entry_end = cache_entry_start + LX_NOR_SECTOR_SIZE;
|
||||
|
||||
/* Determine if the flash address in in the cache entry. */
|
||||
if ((cache_entry_start) && (flash_address >= cache_entry_start) && (flash_address < cache_entry_end))
|
||||
{
|
||||
|
||||
/* Yes, we found the entry. */
|
||||
|
||||
/* Calculate the offset into the cache entry. */
|
||||
cache_offset = (ULONG)(flash_address - cache_entry_start);
|
||||
|
||||
/* Copy the word into the cache. */
|
||||
*(nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_sector_memory + cache_offset) = *source;
|
||||
|
||||
/* Get out of the loop. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* In any case, call the actual driver write function. */
|
||||
status = (nor_flash -> lx_nor_flash_driver_write)(flash_address, source, words);
|
||||
|
||||
/* Return completion status. */
|
||||
return(status);
|
||||
|
||||
#else
|
||||
UINT status;
|
||||
|
||||
|
||||
/* Call the actual driver write function. */
|
||||
status = (nor_flash -> lx_nor_flash_driver_write)(flash_address, source, words);
|
||||
|
||||
/* Return completion status. */
|
||||
return(status);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** NOR 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_nor_flash_extended_cache_enable PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function enables or disables the extended cache. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nor_flash NOR flash instance */
|
||||
/* memory Address of RAM for cache */
|
||||
/* size Size of the RAM for cache */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Application Code */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nor_flash_extended_cache_enable(LX_NOR_FLASH *nor_flash, VOID *memory, ULONG size)
|
||||
{
|
||||
#ifndef LX_NOR_DISABLE_EXTENDED_CACHE
|
||||
|
||||
UINT i;
|
||||
ULONG cache_size;
|
||||
ULONG *cache_memory;
|
||||
|
||||
|
||||
/* Determine if memory was specified but with an invalid size (less than one NOR sector). */
|
||||
if ((memory) && (size < LX_NOR_SECTOR_SIZE))
|
||||
{
|
||||
|
||||
/* Error in memory size supplied. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Obtain the thread safe mutex. */
|
||||
tx_mutex_get(&nor_flash -> lx_nor_flash_mutex, TX_WAIT_FOREVER);
|
||||
#endif
|
||||
|
||||
/* Initialize the internal NOR cache. */
|
||||
nor_flash -> lx_nor_flash_extended_cache_entries = 0;
|
||||
|
||||
/* Calculate cache size in words. */
|
||||
cache_size = size/sizeof(ULONG);
|
||||
|
||||
/* Setup cache memory pointer. */
|
||||
cache_memory = (ULONG *) memory;
|
||||
|
||||
/* Loop through the memory supplied and assign to cache entries. */
|
||||
i = 0;
|
||||
while (cache_size >= LX_NOR_SECTOR_SIZE)
|
||||
{
|
||||
|
||||
/* Setup this cache entry. */
|
||||
nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_sector_address = LX_NULL;
|
||||
nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_sector_memory = cache_memory;
|
||||
nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_access_count = 0;
|
||||
|
||||
/* Move the cache memory forward. */
|
||||
cache_memory = cache_memory + LX_NOR_SECTOR_SIZE;
|
||||
|
||||
/* Decrement the size. */
|
||||
cache_size = cache_size - LX_NOR_SECTOR_SIZE;
|
||||
|
||||
/* Move to next cache entry. */
|
||||
i++;
|
||||
}
|
||||
|
||||
/* Save the number of cache entries. */
|
||||
nor_flash -> lx_nor_flash_extended_cache_entries = i;
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return successful completion. */
|
||||
return(LX_SUCCESS);
|
||||
#else
|
||||
|
||||
/* Return disabled error message. */
|
||||
return(LX_DISABLED);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** NOR Flash */
|
||||
/** */
|
||||
/**************************************************************************/
|
||||
/**************************************************************************/
|
||||
|
||||
#define LX_SOURCE_CODE
|
||||
|
||||
|
||||
/* Include necessary system files. */
|
||||
|
||||
#include "lx_api.h"
|
||||
|
||||
|
||||
/* Define the NOR flash opened list head and opened count. */
|
||||
|
||||
LX_NOR_FLASH *_lx_nor_flash_opened_ptr;
|
||||
ULONG _lx_nor_flash_opened_count;
|
||||
|
||||
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* FUNCTION RELEASE */
|
||||
/* */
|
||||
/* _lx_nor_flash_initialize PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function initializes the NOR flash data structures. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Application Code */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nor_flash_initialize(void)
|
||||
{
|
||||
|
||||
/* Clear open list head pointer and opened count. */
|
||||
_lx_nor_flash_opened_ptr = LX_NULL;
|
||||
_lx_nor_flash_opened_count = 0;
|
||||
|
||||
/* Return success! */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,475 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** NOR 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_nor_flash_logical_sector_find PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function attempts to find the specified logical sector in */
|
||||
/* the NOR flash. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nor_flash NOR flash instance */
|
||||
/* logical_sector Logical sector number */
|
||||
/* superceded_check Check for sector being */
|
||||
/* superceded (can happen if */
|
||||
/* on interruptions of sector */
|
||||
/* write) */
|
||||
/* physical_sector_map_entry Destination for physical */
|
||||
/* sector map entry address */
|
||||
/* physical_sector_address Destination for physical */
|
||||
/* sector data */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nor_flash_driver_read Driver flash sector read */
|
||||
/* _lx_nor_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 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nor_flash_logical_sector_find(LX_NOR_FLASH *nor_flash, ULONG logical_sector, ULONG superceded_check, ULONG **physical_sector_map_entry, ULONG **physical_sector_address)
|
||||
{
|
||||
|
||||
ULONG *block_word_ptr;
|
||||
ULONG *list_word_ptr;
|
||||
ULONG list_word;
|
||||
ULONG min_logical_sector;
|
||||
ULONG max_logical_sector;
|
||||
ULONG mapped_sectors;
|
||||
ULONG total_blocks;
|
||||
ULONG total_sectors;
|
||||
ULONG i, j;
|
||||
ULONG search_start;
|
||||
LX_NOR_SECTOR_MAPPING_CACHE_ENTRY *sector_mapping_cache_entry_ptr = LX_NULL;
|
||||
LX_NOR_SECTOR_MAPPING_CACHE_ENTRY temp_sector_mapping_cache_entry;
|
||||
#ifndef LX_DIRECT_READ
|
||||
UINT status;
|
||||
#endif
|
||||
|
||||
|
||||
/* Initialize the return parameters. */
|
||||
*physical_sector_map_entry = (ULONG *) 0;
|
||||
*physical_sector_address = (ULONG *) 0;
|
||||
|
||||
/* Determine if there are any mapped physical sectors. */
|
||||
if (nor_flash -> lx_nor_flash_mapped_physical_sectors == 0)
|
||||
{
|
||||
|
||||
/* No mapped sector so nothing can be found!. */
|
||||
return(LX_SECTOR_NOT_FOUND);
|
||||
}
|
||||
|
||||
/* Determine if the sector mapping cache is enabled. */
|
||||
if (nor_flash -> lx_nor_flash_sector_mapping_cache_enabled)
|
||||
{
|
||||
|
||||
/* Calculate the starting index of the sector cache for this sector entry. */
|
||||
i = (logical_sector & LX_NOR_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NOR_SECTOR_MAPPING_CACHE_DEPTH;
|
||||
|
||||
/* Build a pointer to the cache entry. */
|
||||
sector_mapping_cache_entry_ptr = &nor_flash -> lx_nor_flash_sector_mapping_cache[i];
|
||||
|
||||
/* Determine if the sector is in the sector mapping cache - assuming the depth of the sector
|
||||
mapping cache is LX_NOR_SECTOR_MAPPING_CACHE_DEPTH entries. */
|
||||
if ((sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID))
|
||||
{
|
||||
|
||||
/* Increment the sector mapping cache hit counter. */
|
||||
nor_flash -> lx_nor_flash_sector_mapping_cache_hits++;
|
||||
|
||||
/* Yes, return the cached values associated with the sector. */
|
||||
*physical_sector_map_entry = sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_map_entry;
|
||||
*physical_sector_address = sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_address;
|
||||
|
||||
/* Don't move anything since we found the entry at the top. */
|
||||
|
||||
/* Return a successful status. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
else if (((sector_mapping_cache_entry_ptr + 1) -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID))
|
||||
{
|
||||
|
||||
/* Increment the sector mapping cache hit counter. */
|
||||
nor_flash -> lx_nor_flash_sector_mapping_cache_hits++;
|
||||
|
||||
/* Yes, return the cached values associated with the sector. */
|
||||
*physical_sector_map_entry = (sector_mapping_cache_entry_ptr + 1) -> lx_nor_sector_mapping_cache_physical_sector_map_entry;
|
||||
*physical_sector_address = (sector_mapping_cache_entry_ptr + 1) -> lx_nor_sector_mapping_cache_physical_sector_address;
|
||||
|
||||
/* Just swap the first and second entry. */
|
||||
temp_sector_mapping_cache_entry = *(sector_mapping_cache_entry_ptr);
|
||||
*(sector_mapping_cache_entry_ptr) = *(sector_mapping_cache_entry_ptr + 1);
|
||||
*(sector_mapping_cache_entry_ptr + 1) = temp_sector_mapping_cache_entry;
|
||||
|
||||
/* Return a successful status. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
else if (((sector_mapping_cache_entry_ptr + 2) -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID))
|
||||
{
|
||||
|
||||
/* Increment the sector mapping cache hit counter. */
|
||||
nor_flash -> lx_nor_flash_sector_mapping_cache_hits++;
|
||||
|
||||
/* Yes, return the cached value. */
|
||||
*physical_sector_map_entry = (sector_mapping_cache_entry_ptr + 2) -> lx_nor_sector_mapping_cache_physical_sector_map_entry;
|
||||
*physical_sector_address = (sector_mapping_cache_entry_ptr + 2) -> lx_nor_sector_mapping_cache_physical_sector_address;
|
||||
|
||||
/* Move the third entry to the top and the first two entries down. */
|
||||
temp_sector_mapping_cache_entry = *(sector_mapping_cache_entry_ptr);
|
||||
*(sector_mapping_cache_entry_ptr) = *(sector_mapping_cache_entry_ptr + 2);
|
||||
*(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1);
|
||||
*(sector_mapping_cache_entry_ptr + 1) = temp_sector_mapping_cache_entry;
|
||||
|
||||
/* Return a successful status. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
else if (((sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID))
|
||||
{
|
||||
|
||||
/* Increment the sector mapping cache hit counter. */
|
||||
nor_flash -> lx_nor_flash_sector_mapping_cache_hits++;
|
||||
|
||||
/* Yes, return the cached value. */
|
||||
*physical_sector_map_entry = (sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_physical_sector_map_entry;
|
||||
*physical_sector_address = (sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_physical_sector_address;
|
||||
|
||||
/* Move the last entry to the top and the first three entries down. */
|
||||
temp_sector_mapping_cache_entry = *(sector_mapping_cache_entry_ptr);
|
||||
*(sector_mapping_cache_entry_ptr) = *(sector_mapping_cache_entry_ptr + 3);
|
||||
*(sector_mapping_cache_entry_ptr + 3) = *(sector_mapping_cache_entry_ptr + 2);
|
||||
*(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1);
|
||||
*(sector_mapping_cache_entry_ptr + 1) = temp_sector_mapping_cache_entry;
|
||||
|
||||
/* Return a successful status. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
/* If we get here, we have a cache miss so increment the counter before we fall through the loop. */
|
||||
nor_flash -> lx_nor_flash_sector_mapping_cache_misses++;
|
||||
}
|
||||
|
||||
/* Setup the total number of mapped sectors. */
|
||||
mapped_sectors = nor_flash -> lx_nor_flash_mapped_physical_sectors;
|
||||
|
||||
/* Start searching from the last found block. */
|
||||
i = nor_flash -> lx_nor_flash_found_block_search;
|
||||
|
||||
/* Setup the starting sector to look at. */
|
||||
j = nor_flash -> lx_nor_flash_found_sector_search;
|
||||
|
||||
/* Pickup the total number of blocks. */
|
||||
total_blocks = nor_flash -> lx_nor_flash_total_blocks;
|
||||
|
||||
/* Loop through the blocks to attempt to find the mapped logical sector. */
|
||||
while (total_blocks--)
|
||||
{
|
||||
|
||||
/* Setup the block word pointer to the first word of the search block. */
|
||||
block_word_ptr = (nor_flash -> lx_nor_flash_base_address + (i * nor_flash -> lx_nor_flash_words_per_block));
|
||||
|
||||
/* Determine if the minimum and maximum logical sector values are present in the block header. If these are
|
||||
present, we can quickly skip blocks that don't have our sector. */
|
||||
|
||||
/* Read the minimum and maximum logical sector values in this block. */
|
||||
#ifdef LX_DIRECT_READ
|
||||
|
||||
/* Read the word directly. */
|
||||
min_logical_sector = *(block_word_ptr + LX_NOR_FLASH_MIN_LOGICAL_SECTOR_OFFSET);
|
||||
#else
|
||||
status = _lx_nor_flash_driver_read(nor_flash, block_word_ptr + LX_NOR_FLASH_MIN_LOGICAL_SECTOR_OFFSET, &min_logical_sector, 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 the error. */
|
||||
return(status);
|
||||
}
|
||||
#endif
|
||||
#ifdef LX_DIRECT_READ
|
||||
|
||||
/* Read the word directly. */
|
||||
max_logical_sector = *(block_word_ptr + LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET);
|
||||
#else
|
||||
status = _lx_nor_flash_driver_read(nor_flash, block_word_ptr + LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET, &max_logical_sector, 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 the error. */
|
||||
return(status);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Are the values valid? */
|
||||
if ((min_logical_sector != LX_ALL_ONES) && (max_logical_sector != LX_ALL_ONES))
|
||||
{
|
||||
|
||||
/* Now let's check to see if the search sector is within this range. */
|
||||
if ((logical_sector < min_logical_sector) || (logical_sector > max_logical_sector))
|
||||
{
|
||||
|
||||
/* Move to the next block. */
|
||||
i++;
|
||||
|
||||
/* Determine if we have wrapped. */
|
||||
if (i >= nor_flash -> lx_nor_flash_total_blocks)
|
||||
{
|
||||
|
||||
/* Yes, we have wrapped, set to block 0. */
|
||||
i = 0;
|
||||
}
|
||||
|
||||
/* Start at the first sector in the next block. */
|
||||
j = 0;
|
||||
|
||||
/* No point in looking further into this block, just continue the loop. */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup the total number of sectors. */
|
||||
total_sectors = nor_flash -> lx_nor_flash_physical_sectors_per_block;
|
||||
|
||||
/* Remember the start of the search. */
|
||||
search_start = j;
|
||||
|
||||
/* Now search through the sector list to find a match. */
|
||||
while (total_sectors--)
|
||||
{
|
||||
|
||||
/* Setup a pointer to the mapped list. */
|
||||
list_word_ptr = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j;
|
||||
|
||||
|
||||
/* Read in the mapped list for this block. */
|
||||
#ifdef LX_DIRECT_READ
|
||||
|
||||
/* Read the word directly. */
|
||||
list_word = *(list_word_ptr);
|
||||
#else
|
||||
status = _lx_nor_flash_driver_read(nor_flash, list_word_ptr, &list_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 the error. */
|
||||
return(status);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Determine if the entry hasn't been used. */
|
||||
if (list_word == LX_NOR_PHYSICAL_SECTOR_FREE)
|
||||
{
|
||||
|
||||
/* Since the mapping is done sequentially in the block, we know nothing
|
||||
else exists after this point. */
|
||||
|
||||
/* Determine if the search started at the beginning of the block. */
|
||||
if (search_start == 0)
|
||||
{
|
||||
|
||||
/* Yes, we started at the beginning of the block. We are now done with this block. */
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Setup the new total to the search start. */
|
||||
total_sectors = search_start;
|
||||
|
||||
/* Clear search start. */
|
||||
search_start = 0;
|
||||
|
||||
/* Start search over. */
|
||||
j = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Is this entry valid? */
|
||||
if ((list_word & (LX_NOR_PHYSICAL_SECTOR_VALID | LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID)) == LX_NOR_PHYSICAL_SECTOR_VALID)
|
||||
{
|
||||
|
||||
/* Decrement the number of mapped sectors. */
|
||||
mapped_sectors--;
|
||||
|
||||
/* Do we have a valid sector match? */
|
||||
if ((list_word & LX_NOR_LOGICAL_SECTOR_MASK) == logical_sector)
|
||||
{
|
||||
|
||||
/* Determine if we care about the superceded bit. */
|
||||
if (superceded_check == LX_FALSE)
|
||||
{
|
||||
|
||||
/* Prepare the return information. */
|
||||
*physical_sector_map_entry = list_word_ptr;
|
||||
*physical_sector_address = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset + (j * LX_NOR_SECTOR_SIZE);
|
||||
|
||||
/* Determine if the sector mapping cache is enabled. */
|
||||
if (nor_flash -> lx_nor_flash_sector_mapping_cache_enabled)
|
||||
{
|
||||
|
||||
/* Yes, update the cache with the sector mapping. */
|
||||
|
||||
/* Move all the cache entries down so the oldest is at the bottom. */
|
||||
*(sector_mapping_cache_entry_ptr + 3) = *(sector_mapping_cache_entry_ptr + 2);
|
||||
*(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1);
|
||||
*(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr);
|
||||
|
||||
/* Setup the new sector information in the cache. */
|
||||
sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_logical_sector = (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID);
|
||||
sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_map_entry = *physical_sector_map_entry;
|
||||
sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_address = *physical_sector_address;
|
||||
}
|
||||
|
||||
/* Remember the last found block for next search. */
|
||||
nor_flash -> lx_nor_flash_found_block_search = i;
|
||||
|
||||
/* Remember the last found sector. */
|
||||
nor_flash -> lx_nor_flash_found_sector_search = j+1;
|
||||
|
||||
/* Has this wrapped around? */
|
||||
if (nor_flash -> lx_nor_flash_found_sector_search >= nor_flash -> lx_nor_flash_physical_sectors_per_block)
|
||||
{
|
||||
|
||||
/* Reset to the beginning sector. */
|
||||
nor_flash -> lx_nor_flash_found_sector_search = 0;
|
||||
}
|
||||
|
||||
/* Return success! */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
/* Check for the superceded bit being clear, which means the sector was superceded. */
|
||||
else if (list_word & LX_NOR_PHYSICAL_SECTOR_SUPERCEDED)
|
||||
{
|
||||
|
||||
/* Prepare the return information. */
|
||||
*physical_sector_map_entry = list_word_ptr;
|
||||
*physical_sector_address = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset + (j * LX_NOR_SECTOR_SIZE);
|
||||
|
||||
/* No need to update the cache here, since this condition only happens during initialization. */
|
||||
|
||||
/* Remember the last found block for next search. */
|
||||
nor_flash -> lx_nor_flash_found_block_search = i;
|
||||
|
||||
/* Remember the last found sector. */
|
||||
nor_flash -> lx_nor_flash_found_sector_search = j+1;
|
||||
|
||||
/* Has this wrapped around? */
|
||||
if (nor_flash -> lx_nor_flash_found_sector_search >= nor_flash -> lx_nor_flash_physical_sectors_per_block)
|
||||
{
|
||||
|
||||
/* Reset to the beginning sector. */
|
||||
nor_flash -> lx_nor_flash_found_sector_search = 0;
|
||||
}
|
||||
|
||||
/* Return success! */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Move to the next list entry. */
|
||||
j++;
|
||||
|
||||
/* Check for wrap around. */
|
||||
if (j >= nor_flash -> lx_nor_flash_physical_sectors_per_block)
|
||||
{
|
||||
|
||||
/* Yes, wrap around, go back to the beginning. */
|
||||
j = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if there are any more mapped sectors. */
|
||||
if (mapped_sectors == 0)
|
||||
break;
|
||||
|
||||
/* Move to the next block. */
|
||||
i++;
|
||||
|
||||
/* Determine if we have wrapped. */
|
||||
if (i >= nor_flash -> lx_nor_flash_total_blocks)
|
||||
{
|
||||
|
||||
/* Yes, we have wrapped, set to block 0. */
|
||||
i = 0;
|
||||
}
|
||||
|
||||
/* Start at the first sector in the next block. */
|
||||
j = 0;
|
||||
}
|
||||
|
||||
/* Return sector not found status. */
|
||||
return(LX_SECTOR_NOT_FOUND);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** NOR 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_nor_flash_next_block_to_erase_find PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function finds the next block to erase in the NOR flash. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nor_flash NOR flash instance */
|
||||
/* return_erase_block Returned block to erase */
|
||||
/* return_erase_count Returned erase count of block */
|
||||
/* return_mapped_sectors Returned number of mapped */
|
||||
/* sectors */
|
||||
/* return_obsolete_sectors Returned number of obsolete */
|
||||
/* sectors */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nor_flash_driver_read Driver flash sector read */
|
||||
/* _lx_nor_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 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nor_flash_next_block_to_erase_find(LX_NOR_FLASH *nor_flash, ULONG *return_erase_block, ULONG *return_erase_count, ULONG *return_mapped_sectors, ULONG *return_obsolete_sectors)
|
||||
{
|
||||
|
||||
ULONG *block_word_ptr;
|
||||
ULONG *list_word_ptr;
|
||||
ULONG list_word;
|
||||
ULONG i, j;
|
||||
ULONG mapped_sectors;
|
||||
ULONG erase_count;
|
||||
ULONG obsolete_sectors;
|
||||
ULONG min_block_erase = 0;
|
||||
ULONG min_block_erase_count;
|
||||
ULONG min_block_obsolete_count = 0;
|
||||
ULONG min_block_mapped_count = 0;
|
||||
ULONG max_obsolete_sectors;
|
||||
ULONG max_obsolete_block = 0;
|
||||
ULONG max_obsolete_erase_count = 0;
|
||||
ULONG max_obsolete_mapped_count = 0;
|
||||
ULONG min_system_block_erase_count;
|
||||
ULONG max_system_block_erase_count;
|
||||
ULONG erase_count_threshold;
|
||||
#ifndef LX_DIRECT_READ
|
||||
UINT status;
|
||||
#endif
|
||||
|
||||
|
||||
/* Setup the block word pointer to the first word of the search block. */
|
||||
block_word_ptr = nor_flash -> lx_nor_flash_base_address;
|
||||
|
||||
/* Initialize the minimum erase count. */
|
||||
min_block_erase_count = LX_ALL_ONES;
|
||||
|
||||
/* Initialize the system minimum and maximum erase counts. */
|
||||
min_system_block_erase_count = LX_ALL_ONES;
|
||||
max_system_block_erase_count = 0;
|
||||
|
||||
/* Initialize the maximum obsolete sector count. */
|
||||
max_obsolete_sectors = 0;
|
||||
|
||||
/* Calculate the erase count threshold. */
|
||||
if (nor_flash -> lx_nor_flash_free_physical_sectors >= nor_flash -> lx_nor_flash_physical_sectors_per_block)
|
||||
{
|
||||
|
||||
/* Calculate erase count threshold by adding constant to the current minimum. */
|
||||
erase_count_threshold = nor_flash -> lx_nor_flash_minimum_erase_count + LX_NOR_FLASH_MAX_ERASE_COUNT_DELTA;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* When the number of free sectors is low, simply pick the block that has the most number of obsolete sectors. */
|
||||
erase_count_threshold = LX_ALL_ONES;
|
||||
}
|
||||
|
||||
/* Loop through the blocks to attempt to find the mapped logical sector. */
|
||||
for (i = 0; i < nor_flash -> lx_nor_flash_total_blocks; i++)
|
||||
{
|
||||
|
||||
/* Read the erase count of this block. */
|
||||
#ifdef LX_DIRECT_READ
|
||||
|
||||
/* Read the word directly. */
|
||||
erase_count = *(block_word_ptr);
|
||||
#else
|
||||
status = _lx_nor_flash_driver_read(nor_flash, block_word_ptr, &erase_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 the error. */
|
||||
return(status);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Update the system minimum and maximum erase counts. */
|
||||
if (erase_count < min_system_block_erase_count)
|
||||
min_system_block_erase_count = erase_count;
|
||||
if (erase_count > max_system_block_erase_count)
|
||||
max_system_block_erase_count = erase_count;
|
||||
|
||||
/* Compute the number of obsolete and mapped sectors for this block. */
|
||||
obsolete_sectors = 0;
|
||||
mapped_sectors = 0;
|
||||
|
||||
/* Setup a pointer to the mapped list. */
|
||||
list_word_ptr = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset;
|
||||
|
||||
/* Loop through the mapped list for this block. */
|
||||
for (j = 0; j < nor_flash -> lx_nor_flash_physical_sectors_per_block; j++)
|
||||
{
|
||||
|
||||
/* Read the current mapping entry. */
|
||||
#ifdef LX_DIRECT_READ
|
||||
|
||||
/* Read the word directly. */
|
||||
list_word = *(list_word_ptr);
|
||||
#else
|
||||
status = _lx_nor_flash_driver_read(nor_flash, list_word_ptr, &list_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 the error. */
|
||||
return(status);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Determine if the entry hasn't been used. */
|
||||
if (list_word == LX_NOR_PHYSICAL_SECTOR_FREE)
|
||||
{
|
||||
|
||||
/* Since allocations are done sequentially in the block, we know nothing
|
||||
else exists after this point. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Is this entry obsolete? */
|
||||
if ((list_word & LX_NOR_PHYSICAL_SECTOR_VALID) == 0)
|
||||
{
|
||||
|
||||
/* Increment the number of obsolete sectors. */
|
||||
obsolete_sectors++;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Increment the number of mapped sectors. */
|
||||
mapped_sectors++;
|
||||
}
|
||||
|
||||
/* Move the list pointer ahead. */
|
||||
list_word_ptr++;
|
||||
}
|
||||
|
||||
/* Determine if we have a block with a new maximum number of obsolete sectors. */
|
||||
if ((obsolete_sectors > max_obsolete_sectors) && (erase_count <= erase_count_threshold))
|
||||
{
|
||||
|
||||
/* Update the new maximum obsolete sectors and related information. */
|
||||
max_obsolete_sectors = obsolete_sectors;
|
||||
max_obsolete_block = i;
|
||||
max_obsolete_erase_count = erase_count;
|
||||
max_obsolete_mapped_count = mapped_sectors;
|
||||
}
|
||||
else if ((max_obsolete_sectors) && (obsolete_sectors == max_obsolete_sectors) && (erase_count <= erase_count_threshold))
|
||||
{
|
||||
|
||||
/* Another block has the same number of obsolete sectors. Does this new block have a smaller erase
|
||||
count? */
|
||||
if (erase_count < max_obsolete_erase_count)
|
||||
{
|
||||
|
||||
/* Yes, erase the block with the smaller erase count. */
|
||||
max_obsolete_sectors = obsolete_sectors;
|
||||
max_obsolete_block = i;
|
||||
max_obsolete_erase_count = erase_count;
|
||||
max_obsolete_mapped_count = mapped_sectors;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if we have a new minimum erase count. */
|
||||
if (erase_count < min_block_erase_count)
|
||||
{
|
||||
|
||||
/* Update the new minimum erase count and related information. */
|
||||
min_block_erase_count = erase_count;
|
||||
min_block_erase = i;
|
||||
min_block_obsolete_count = obsolete_sectors;
|
||||
min_block_mapped_count = mapped_sectors;
|
||||
}
|
||||
|
||||
/* Move to the next block. */
|
||||
block_word_ptr = block_word_ptr + nor_flash -> lx_nor_flash_words_per_block;
|
||||
}
|
||||
|
||||
/* Determine if we can erase the block with the most obsolete sectors. */
|
||||
if (max_obsolete_sectors)
|
||||
{
|
||||
|
||||
/* Erase the block with the most obsolete sectors. */
|
||||
*return_erase_block = max_obsolete_block;
|
||||
*return_erase_count = max_obsolete_erase_count;
|
||||
*return_obsolete_sectors = max_obsolete_sectors;
|
||||
*return_mapped_sectors = max_obsolete_mapped_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Otherwise, choose the block with the smallest erase count. */
|
||||
*return_erase_block = min_block_erase;
|
||||
*return_erase_count = min_block_erase_count;
|
||||
*return_obsolete_sectors = min_block_obsolete_count;
|
||||
*return_mapped_sectors = min_block_mapped_count;
|
||||
}
|
||||
|
||||
/* Update the overall minimum and maximum erase count. */
|
||||
nor_flash -> lx_nor_flash_minimum_erase_count = min_system_block_erase_count;
|
||||
nor_flash -> lx_nor_flash_maximum_erase_count = max_system_block_erase_count;
|
||||
|
||||
/* Return success. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,872 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** NOR 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_nor_flash_open PORTABLE C */
|
||||
/* 6.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 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
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;
|
||||
ULONG i, j, k;
|
||||
UINT status;
|
||||
#ifdef LX_FREE_SECTOR_DATA_VERIFY
|
||||
ULONG *sector_word_ptr;
|
||||
ULONG sector_word;
|
||||
#endif
|
||||
LX_NOR_FLASH *tail_ptr;
|
||||
TX_INTERRUPT_SAVE_AREA
|
||||
|
||||
LX_PARAMETER_NOT_USED(name);
|
||||
|
||||
/* Clear the NOR flash control block. */
|
||||
TX_MEMSET(nor_flash, 0, sizeof(LX_NOR_FLASH));
|
||||
|
||||
/* 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;
|
||||
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 (i = 0; i < nor_flash -> lx_nor_flash_total_blocks; i++)
|
||||
{
|
||||
|
||||
/* 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 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 (i = 0; i < nor_flash -> lx_nor_flash_total_blocks; i++)
|
||||
{
|
||||
|
||||
/* 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_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 (i = 0; i < nor_flash -> lx_nor_flash_total_blocks; i++)
|
||||
{
|
||||
|
||||
/* 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. */
|
||||
status = (nor_flash -> lx_nor_flash_driver_block_erased_verify)(i);
|
||||
|
||||
/* 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, i, 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 = i;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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((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_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 != TX_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. */
|
||||
TX_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. */
|
||||
TX_RESTORE
|
||||
|
||||
/* Return a successful completion. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** NOR 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_nor_flash_partial_defragment PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function defragments the NOR flash up to the specified */
|
||||
/* number of blocks. . */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nor_flash NOR flash instance */
|
||||
/* max_blocks Maximum number of blocks to */
|
||||
/* defragment */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nor_flash_block_reclaim Reclaim a NOR flash block */
|
||||
/* tx_mutex_get Get thread protection */
|
||||
/* tx_mutex_put Release thread protection */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Application Code */
|
||||
/* Internal LevelX */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nor_flash_partial_defragment(LX_NOR_FLASH *nor_flash, UINT max_blocks)
|
||||
{
|
||||
|
||||
ULONG i;
|
||||
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Obtain the thread safe mutex. */
|
||||
tx_mutex_get(&nor_flash -> lx_nor_flash_mutex, TX_WAIT_FOREVER);
|
||||
#endif
|
||||
|
||||
/* Determine if the maximum number of blocks exceeds the total blocks in this flash instance. */
|
||||
if (max_blocks >= nor_flash -> lx_nor_flash_total_blocks)
|
||||
{
|
||||
|
||||
/* Adjust the maximum to the total number of blocks. */
|
||||
max_blocks = nor_flash -> lx_nor_flash_total_blocks;
|
||||
}
|
||||
|
||||
/* Loop for max number of blocks, while there are obsolete count. */
|
||||
for (i = 0; i < max_blocks; i++)
|
||||
{
|
||||
|
||||
/* Determine if there is any more defragment work. */
|
||||
if (nor_flash -> lx_nor_flash_obsolete_physical_sectors == 0)
|
||||
break;
|
||||
|
||||
/* Call the block reclaim function to defragment. */
|
||||
_lx_nor_flash_block_reclaim(nor_flash);
|
||||
}
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return successful completion. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,337 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** NOR 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_nor_flash_physical_sector_allocate PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function allocates a free physical sector for mapping to a */
|
||||
/* logical sector. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nor_flash NOR flash instance */
|
||||
/* logical_sector Logical sector number */
|
||||
/* physical_sector_map_entry Pointer to sector map entry */
|
||||
/* physical_sector_address Address of physical sector */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nor_flash_driver_write Driver flash sector write */
|
||||
/* _lx_nor_flash_driver_read Driver flash sector read */
|
||||
/* _lx_nor_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 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nor_flash_physical_sector_allocate(LX_NOR_FLASH *nor_flash, ULONG logical_sector, ULONG **physical_sector_map_entry, ULONG **physical_sector_address)
|
||||
{
|
||||
|
||||
ULONG search_block;
|
||||
ULONG *block_word_ptr;
|
||||
ULONG block_word;
|
||||
ULONG min_logical_sector;
|
||||
ULONG max_logical_sector;
|
||||
ULONG *list_word_ptr;
|
||||
ULONG list_word;
|
||||
ULONG i, j, k, l;
|
||||
UINT status;
|
||||
|
||||
|
||||
/* Increment the number of physical sector allocation requests. */
|
||||
nor_flash -> lx_nor_flash_physical_block_allocates++;
|
||||
|
||||
/* Initialize the return parameters. */
|
||||
*physical_sector_map_entry = (ULONG *) 0;
|
||||
*physical_sector_address = (ULONG *) 0;
|
||||
|
||||
/* Determine if there are any free physical sectors. */
|
||||
if (nor_flash -> lx_nor_flash_free_physical_sectors == 0)
|
||||
{
|
||||
|
||||
/* Increment the number of failed allocations. */
|
||||
nor_flash -> lx_nor_flash_physical_block_allocate_errors++;
|
||||
|
||||
/* No free physical sectors, return . */
|
||||
return(LX_NO_SECTORS);
|
||||
}
|
||||
|
||||
/* Pickup the search for a free physical sector at the specified block. */
|
||||
search_block = nor_flash -> lx_nor_flash_free_block_search;
|
||||
|
||||
/* Loop through the blocks to find a free physical sector. */
|
||||
for (i = 0; i < nor_flash -> lx_nor_flash_total_blocks; i++)
|
||||
{
|
||||
|
||||
/* Setup the block word pointer to the first word of the search block. */
|
||||
block_word_ptr = nor_flash -> lx_nor_flash_base_address + (search_block * nor_flash -> lx_nor_flash_words_per_block);
|
||||
|
||||
/* Find the first free physical sector from the free sector bit map of this block. */
|
||||
for (j = 0; j < nor_flash -> lx_nor_flash_block_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 the error. */
|
||||
return(status);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Are there any free sectors in this word? */
|
||||
if (block_word)
|
||||
{
|
||||
|
||||
/* Yes, there are free sectors in this word. */
|
||||
for (k = 0; k < 32; k++)
|
||||
{
|
||||
|
||||
/* Is this sector free? */
|
||||
if (block_word & 1)
|
||||
{
|
||||
|
||||
/* Yes, this sector is free, clear the bit for this sector in the free sector map. */
|
||||
|
||||
/* Read this word of the free sector bit map again. */
|
||||
#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 the error. */
|
||||
return(status);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Clear the bit associated with the free sector to indicate it is not free. */
|
||||
block_word = block_word & ~(((ULONG) 1) << k);
|
||||
|
||||
/* Now write back free bit map word with the bit for this sector cleared. */
|
||||
status = _lx_nor_flash_driver_write(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 the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
/* Determine if this is the last entry available in this block. */
|
||||
if (((block_word >> 1) == 0) && (j == (nor_flash -> lx_nor_flash_block_bit_map_words - 1)))
|
||||
{
|
||||
|
||||
/* This is the last physical sector in the block. Now we need to calculate the minimum valid logical
|
||||
sector and the maximum valid logical sector. */
|
||||
|
||||
/* Setup the minimum and maximum logical sectors to the current logical sector. */
|
||||
min_logical_sector = logical_sector;
|
||||
max_logical_sector = logical_sector;
|
||||
|
||||
/* Setup a pointer to the mapped list. */
|
||||
list_word_ptr = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset;
|
||||
|
||||
/* Loop to search the mapped list. */
|
||||
for (l = 0; l < nor_flash -> lx_nor_flash_physical_sectors_per_block; l++)
|
||||
{
|
||||
|
||||
/* Read the mapped sector entry. */
|
||||
#ifdef LX_DIRECT_READ
|
||||
|
||||
/* Read the word directly. */
|
||||
list_word = *(list_word_ptr);
|
||||
#else
|
||||
status = _lx_nor_flash_driver_read(nor_flash, list_word_ptr, &list_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 the error. */
|
||||
return(status);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Is this entry valid? */
|
||||
if (list_word & LX_NOR_PHYSICAL_SECTOR_VALID)
|
||||
{
|
||||
|
||||
/* Isolate the logical sector. */
|
||||
list_word = list_word & LX_NOR_LOGICAL_SECTOR_MASK;
|
||||
|
||||
/* Determine if a new minimum has been found. */
|
||||
if (list_word < min_logical_sector)
|
||||
min_logical_sector = list_word;
|
||||
|
||||
/* Determine if a new maximum has been found. */
|
||||
if (list_word != LX_NOR_LOGICAL_SECTOR_MASK)
|
||||
{
|
||||
if (list_word > max_logical_sector)
|
||||
max_logical_sector = list_word;
|
||||
}
|
||||
}
|
||||
|
||||
/* Move the list pointer ahead. */
|
||||
list_word_ptr++;
|
||||
}
|
||||
|
||||
/* Move the search pointer forward, since we know this block is exhausted. */
|
||||
search_block++;
|
||||
|
||||
/* Check for wrap condition on the search block. */
|
||||
if (search_block >= nor_flash -> lx_nor_flash_total_blocks)
|
||||
{
|
||||
|
||||
/* Reset search block to the beginning. */
|
||||
search_block = 0;
|
||||
}
|
||||
|
||||
/* Now write the minimum and maximum logical sector in this block. */
|
||||
status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr + LX_NOR_FLASH_MIN_LOGICAL_SECTOR_OFFSET, &min_logical_sector, 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 the error. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr + LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET, &max_logical_sector, 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 the error. */
|
||||
return(status);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remember the block to search. */
|
||||
nor_flash -> lx_nor_flash_free_block_search = search_block;
|
||||
|
||||
/* Prepare the return information. */
|
||||
*physical_sector_map_entry = block_word_ptr + (nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + (j * 32)) + k;
|
||||
*physical_sector_address = block_word_ptr + (nor_flash -> lx_nor_flash_block_physical_sector_offset) + (((j * 32) + k) * LX_NOR_SECTOR_SIZE);
|
||||
|
||||
/* Return success! */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
/* Shift down the bit map. */
|
||||
block_word = block_word >> 1;
|
||||
|
||||
/* Determine if there are any more bits set? If not, we can break out of the search of this word. */
|
||||
if (block_word == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Move to the next flash block. */
|
||||
search_block++;
|
||||
|
||||
/* Determine if we have to wrap the search block. */
|
||||
if (search_block >= nor_flash -> lx_nor_flash_total_blocks)
|
||||
{
|
||||
|
||||
/* Set the search block to the beginning. */
|
||||
search_block = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Increment the number of failed allocations. */
|
||||
nor_flash -> lx_nor_flash_physical_block_allocate_errors++;
|
||||
|
||||
/* Return no sector completion. */
|
||||
return(LX_NO_SECTORS);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** NOR 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_nor_flash_sector_mapping_cache_invalidate PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function invalidates the sector's entry in the NOR flash */
|
||||
/* cache. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nor_flash NOR flash instance */
|
||||
/* logical_sector Logical sector */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Internal LevelX */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
VOID _lx_nor_flash_sector_mapping_cache_invalidate(LX_NOR_FLASH *nor_flash, ULONG logical_sector)
|
||||
{
|
||||
|
||||
ULONG i;
|
||||
LX_NOR_SECTOR_MAPPING_CACHE_ENTRY *sector_mapping_cache_entry_ptr;
|
||||
|
||||
|
||||
/* Determine if the sector mapping cache is enabled. */
|
||||
if (nor_flash -> lx_nor_flash_sector_mapping_cache_enabled)
|
||||
{
|
||||
|
||||
/* Calculate the starting index of the sector mapping cache for this sector entry. */
|
||||
i = (logical_sector & LX_NOR_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NOR_SECTOR_MAPPING_CACHE_DEPTH;
|
||||
|
||||
/* Build a pointer to the cache entry. */
|
||||
sector_mapping_cache_entry_ptr = &nor_flash -> lx_nor_flash_sector_mapping_cache[i];
|
||||
|
||||
/* Determine if the sector is in the sector mapping cache - assuming the depth of the sector
|
||||
mapping cache is LX_NOR_SECTOR_MAPPING_CACHE_DEPTH entries. */
|
||||
if ((sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID))
|
||||
{
|
||||
|
||||
/* Move all cache entries up and invalidate the last entry. */
|
||||
*(sector_mapping_cache_entry_ptr) = *(sector_mapping_cache_entry_ptr + 1);
|
||||
*(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr + 2);
|
||||
*(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 3);
|
||||
|
||||
/* Invalidate the last entry. */
|
||||
(sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_logical_sector = 0;
|
||||
}
|
||||
else if (((sector_mapping_cache_entry_ptr + 1) -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID))
|
||||
{
|
||||
|
||||
/* Move all subsequent cache entries up and invalidate the last entry. */
|
||||
*(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr + 2);
|
||||
*(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 3);
|
||||
|
||||
/* Invalidate the last entry. */
|
||||
(sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_logical_sector = 0;
|
||||
}
|
||||
else if (((sector_mapping_cache_entry_ptr + 2) -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID))
|
||||
{
|
||||
|
||||
/* Move all subsequent cache entries up and invalidate the last entry. */
|
||||
*(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 3);
|
||||
|
||||
/* Invalidate the last entry. */
|
||||
(sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_logical_sector = 0;
|
||||
}
|
||||
else if (((sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID))
|
||||
{
|
||||
|
||||
/* Simply invalidate the last entry. */
|
||||
(sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_logical_sector = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** NOR 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_nor_flash_sector_read PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function reads a logical sector from NOR flash. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nor_flash NOR flash instance */
|
||||
/* logical_sector Logical sector number */
|
||||
/* buffer Pointer to buffer to read into*/
|
||||
/* (the size is 512 bytes) */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nor_flash_driver_write Driver flash sector write */
|
||||
/* _lx_nor_flash_driver_read Driver flash sector read */
|
||||
/* _lx_nor_flash_logical_sector_find Find logical sector */
|
||||
/* _lx_nor_flash_physical_sector_allocate */
|
||||
/* Allocate new logical sector */
|
||||
/* _lx_nor_flash_system_error Internal system error handler */
|
||||
/* tx_mutex_get Get thread protection */
|
||||
/* tx_mutex_put Release thread protection */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Application Code */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nor_flash_sector_read(LX_NOR_FLASH *nor_flash, ULONG logical_sector, VOID *buffer)
|
||||
{
|
||||
|
||||
UINT status;
|
||||
ULONG *mapping_address;
|
||||
ULONG mapping_entry;
|
||||
ULONG *sector_address;
|
||||
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Obtain the thread safe mutex. */
|
||||
tx_mutex_get(&nor_flash -> lx_nor_flash_mutex, TX_WAIT_FOREVER);
|
||||
#endif
|
||||
|
||||
/* Increment the number of read requests. */
|
||||
nor_flash -> lx_nor_flash_read_requests++;
|
||||
|
||||
/* See if we can find the sector in the current mapping. */
|
||||
_lx_nor_flash_logical_sector_find(nor_flash, logical_sector, LX_FALSE, &mapping_address, §or_address);
|
||||
|
||||
/* Determine if the logical sector was found. */
|
||||
if (mapping_address)
|
||||
{
|
||||
|
||||
/* Yes, we were able to find the logical sector. */
|
||||
|
||||
/* Read the sector data from the physical sector. */
|
||||
status = _lx_nor_flash_driver_read(nor_flash, sector_address, buffer, LX_NOR_SECTOR_SIZE);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Adjust return status. */
|
||||
status = LX_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Set the status to success. */
|
||||
status = LX_SUCCESS;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Allocate a new physical sector for this write. */
|
||||
_lx_nor_flash_physical_sector_allocate(nor_flash, logical_sector, &mapping_address, §or_address);
|
||||
|
||||
/* Determine if the new sector allocation was successful. */
|
||||
if (mapping_address)
|
||||
{
|
||||
|
||||
/* Update the number of free physical sectors. */
|
||||
nor_flash -> lx_nor_flash_free_physical_sectors--;
|
||||
|
||||
/* Read the sector data from the physical sector. */
|
||||
status = _lx_nor_flash_driver_read(nor_flash, sector_address, buffer, LX_NOR_SECTOR_SIZE);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* Now build the new mapping entry. */
|
||||
mapping_entry = ((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID) | ((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED) | logical_sector;
|
||||
|
||||
/* Write out the new mapping entry. */
|
||||
status = _lx_nor_flash_driver_write(nor_flash, mapping_address, &mapping_entry, 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);
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return status. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Increment the number of mapped physical sectors. */
|
||||
nor_flash -> lx_nor_flash_mapped_physical_sectors++;
|
||||
|
||||
/* Set the status to success. */
|
||||
status = LX_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Could not find the logical sector. */
|
||||
status = LX_SECTOR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return status. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** NOR 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_nor_flash_sector_release PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function releases a logical sector from being managed in the */
|
||||
/* NOR flash. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nor_flash NOR flash instance */
|
||||
/* logical_sector Logical sector number */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nor_flash_driver_write Driver flash sector write */
|
||||
/* _lx_nor_flash_driver_read Driver flash sector read */
|
||||
/* _lx_nor_flash_block_reclaim Reclaim one flash block */
|
||||
/* _lx_nor_flash_sector_mapping_cache_invalidate */
|
||||
/* Invalidate cache entry */
|
||||
/* _lx_nor_flash_logical_sector_find Find logical sector */
|
||||
/* _lx_nor_flash_system_error Internal system error handler */
|
||||
/* tx_mutex_get Get thread protection */
|
||||
/* tx_mutex_put Release thread protection */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Application Code */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nor_flash_sector_release(LX_NOR_FLASH *nor_flash, ULONG logical_sector)
|
||||
{
|
||||
|
||||
UINT status;
|
||||
ULONG *mapping_address;
|
||||
ULONG mapping_entry;
|
||||
ULONG *sector_address;
|
||||
ULONG i;
|
||||
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Obtain the thread safe mutex. */
|
||||
tx_mutex_get(&nor_flash -> lx_nor_flash_mutex, TX_WAIT_FOREVER);
|
||||
#endif
|
||||
|
||||
/* Increment the number of read requests. */
|
||||
nor_flash -> lx_nor_flash_read_requests++;
|
||||
|
||||
/* See if we can find the sector in the current mapping. */
|
||||
_lx_nor_flash_logical_sector_find(nor_flash, logical_sector, LX_FALSE, &mapping_address, §or_address);
|
||||
|
||||
/* Determine if the logical sector was found. */
|
||||
if (mapping_address)
|
||||
{
|
||||
|
||||
/* Yes, we were able to find the logical sector. */
|
||||
|
||||
/* Read in the old sector mapping. */
|
||||
#ifdef LX_DIRECT_READ
|
||||
|
||||
/* Read the word directly. */
|
||||
mapping_entry = *(mapping_address);
|
||||
#else
|
||||
status = _lx_nor_flash_driver_read(nor_flash, mapping_address, &mapping_entry, 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);
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return status. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Mark this sector as invalid. */
|
||||
/* Now clear bits 31 and 30, which indicates this sector is now obsoleted. */
|
||||
mapping_entry = mapping_entry & ~(((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID) | ((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED));
|
||||
|
||||
/* Write the value back to the flash to clear bits 31 & 30. */
|
||||
status = _lx_nor_flash_driver_write(nor_flash, mapping_address, &mapping_entry, 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);
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return status. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Increment the number of obsolete physical sectors. */
|
||||
nor_flash -> lx_nor_flash_obsolete_physical_sectors++;
|
||||
|
||||
/* Decrement the number of mapped physical sectors. */
|
||||
nor_flash -> lx_nor_flash_mapped_physical_sectors--;
|
||||
|
||||
/* Ensure the sector mapping cache no longer has this sector. */
|
||||
_lx_nor_flash_sector_mapping_cache_invalidate(nor_flash, logical_sector);
|
||||
|
||||
/* Determine if there are less than two block's worth of free sectors. */
|
||||
i = 0;
|
||||
while (nor_flash -> lx_nor_flash_free_physical_sectors <= nor_flash -> lx_nor_flash_physical_sectors_per_block)
|
||||
{
|
||||
|
||||
/* Attempt to reclaim one physical block. */
|
||||
_lx_nor_flash_block_reclaim(nor_flash);
|
||||
|
||||
/* Increment the block count. */
|
||||
i++;
|
||||
|
||||
/* Have we exceeded the number of blocks in the system? */
|
||||
if (i >= nor_flash -> lx_nor_flash_total_blocks)
|
||||
{
|
||||
|
||||
/* Yes, break out of the loop. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the status to success. */
|
||||
status = LX_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Could not find the logical sector. */
|
||||
status = LX_SECTOR_NOT_FOUND;
|
||||
}
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return status. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,352 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** NOR 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_nor_flash_sector_write PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function writes a logical sector to the NOR flash. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nor_flash NOR flash instance */
|
||||
/* logical_sector Logical sector number */
|
||||
/* buffer Pointer to buffer to write */
|
||||
/* (the size is 512 bytes) */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* return status */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _lx_nor_flash_driver_write Driver flash sector write */
|
||||
/* _lx_nor_flash_driver_read Driver flash sector read */
|
||||
/* _lx_nor_flash_block_reclaim Reclaim one flash block */
|
||||
/* _lx_nor_flash_logical_sector_find Find logical sector */
|
||||
/* _lx_nor_flash_physical_sector_allocate */
|
||||
/* Allocate new physical sector */
|
||||
/* _lx_nor_flash_sector_mapping_cache_invalidate */
|
||||
/* Invalidate cache entry */
|
||||
/* _lx_nor_flash_system_error Internal system error handler */
|
||||
/* tx_mutex_get Get thread protection */
|
||||
/* tx_mutex_put Release thread protection */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Application Code */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
UINT _lx_nor_flash_sector_write(LX_NOR_FLASH *nor_flash, ULONG logical_sector, VOID *buffer)
|
||||
{
|
||||
|
||||
ULONG *old_mapping_address;
|
||||
ULONG *old_sector_address;
|
||||
ULONG old_mapping_entry;
|
||||
ULONG *new_mapping_address;
|
||||
ULONG *new_sector_address;
|
||||
ULONG new_mapping_entry;
|
||||
ULONG i;
|
||||
LX_NOR_SECTOR_MAPPING_CACHE_ENTRY *sector_mapping_cache_entry_ptr;
|
||||
UINT status;
|
||||
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Obtain the thread safe mutex. */
|
||||
tx_mutex_get(&nor_flash -> lx_nor_flash_mutex, TX_WAIT_FOREVER);
|
||||
#endif
|
||||
|
||||
/* Determine if there are less than two block's worth of free sectors. */
|
||||
i = 0;
|
||||
while (nor_flash -> lx_nor_flash_free_physical_sectors <= nor_flash -> lx_nor_flash_physical_sectors_per_block)
|
||||
{
|
||||
|
||||
/* Attempt to reclaim one physical block. */
|
||||
_lx_nor_flash_block_reclaim(nor_flash);
|
||||
|
||||
/* Increment the block count. */
|
||||
i++;
|
||||
|
||||
/* Have we exceeded the number of blocks in the system? */
|
||||
if (i >= nor_flash -> lx_nor_flash_total_blocks)
|
||||
{
|
||||
|
||||
/* Yes, break out of the loop. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Increment the number of write requests. */
|
||||
nor_flash -> lx_nor_flash_write_requests++;
|
||||
|
||||
/* See if we can find the sector in the current mapping. */
|
||||
_lx_nor_flash_logical_sector_find(nor_flash, logical_sector, LX_FALSE, &old_mapping_address, &old_sector_address);
|
||||
|
||||
/* Allocate a new physical sector for this write. */
|
||||
_lx_nor_flash_physical_sector_allocate(nor_flash, logical_sector, &new_mapping_address, &new_sector_address);
|
||||
|
||||
/* Determine if the new sector allocation was successful. */
|
||||
if (new_mapping_address)
|
||||
{
|
||||
|
||||
/* Yes, we were able to allocate a new physical sector. */
|
||||
|
||||
/* Update the number of free physical sectors. */
|
||||
nor_flash -> lx_nor_flash_free_physical_sectors--;
|
||||
|
||||
/* Write the sector data to the new physical sector. */
|
||||
status = _lx_nor_flash_driver_write(nor_flash, new_sector_address, buffer, LX_NOR_SECTOR_SIZE);
|
||||
|
||||
/* 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);
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return status. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Was there a previously mapped sector? */
|
||||
if (old_mapping_address)
|
||||
{
|
||||
|
||||
/* Now deprecate the old sector mapping. */
|
||||
|
||||
/* Read in the old sector mapping. */
|
||||
#ifdef LX_DIRECT_READ
|
||||
|
||||
/* Read the word directly. */
|
||||
old_mapping_entry = *(old_mapping_address);
|
||||
#else
|
||||
status = _lx_nor_flash_driver_read(nor_flash, old_mapping_address, &old_mapping_entry, 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);
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return status. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Clear bit 30, which indicates this sector is superceded. */
|
||||
old_mapping_entry = old_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED);
|
||||
|
||||
/* Write the value back to the flash to clear bit 30. */
|
||||
status = _lx_nor_flash_driver_write(nor_flash, old_mapping_address, &old_mapping_entry, 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);
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return status. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now build the new mapping entry - with the not valid bit set initially. */
|
||||
new_mapping_entry = ((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID) | ((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED) | ((ULONG) LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID) | logical_sector;
|
||||
|
||||
/* Write out the new mapping entry. */
|
||||
status = _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 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);
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return status. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Now clear the not valid bit to make this sector mapping valid. This is done because the writing of the extra bytes itself can
|
||||
be interrupted and we need to make sure this can be detected when the flash is opened again. */
|
||||
new_mapping_entry = new_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID);
|
||||
|
||||
/* Clear the not valid bit. */
|
||||
status = _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 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);
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return status. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Increment the number of mapped physical sectors. */
|
||||
nor_flash -> lx_nor_flash_mapped_physical_sectors++;
|
||||
|
||||
/* Was there a previously mapped sector? */
|
||||
if (old_mapping_address)
|
||||
{
|
||||
|
||||
/* Now clear bit 31, which indicates this sector is now obsoleted. */
|
||||
old_mapping_entry = old_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID);
|
||||
|
||||
/* Write the value back to the flash to clear bit 31. */
|
||||
status = _lx_nor_flash_driver_write(nor_flash, old_mapping_address, &old_mapping_entry, 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);
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return status. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Increment the number of obsolete physical sectors. */
|
||||
nor_flash -> lx_nor_flash_obsolete_physical_sectors++;
|
||||
|
||||
/* Decrement the number of mapped physical sectors. */
|
||||
nor_flash -> lx_nor_flash_mapped_physical_sectors--;
|
||||
|
||||
/* Invalidate the old sector mapping cache entry. */
|
||||
_lx_nor_flash_sector_mapping_cache_invalidate(nor_flash, logical_sector);
|
||||
}
|
||||
|
||||
/* Determine if the sector mapping cache is enabled. */
|
||||
if (nor_flash -> lx_nor_flash_sector_mapping_cache_enabled)
|
||||
{
|
||||
|
||||
/* Yes, sector mapping cache is enabled, place this sector information in the cache. */
|
||||
|
||||
/* Calculate the starting index of the sector mapping cache for this sector entry. */
|
||||
i = (logical_sector & LX_NOR_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NOR_SECTOR_MAPPING_CACHE_DEPTH;
|
||||
|
||||
/* Build a pointer to the cache entry. */
|
||||
sector_mapping_cache_entry_ptr = &nor_flash -> lx_nor_flash_sector_mapping_cache[i];
|
||||
|
||||
/* Move all the cache entries down so the oldest is at the bottom. */
|
||||
*(sector_mapping_cache_entry_ptr + 3) = *(sector_mapping_cache_entry_ptr + 2);
|
||||
*(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1);
|
||||
*(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr);
|
||||
|
||||
/* Setup the new sector information in the cache. */
|
||||
sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_logical_sector = (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID);
|
||||
sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_map_entry = new_mapping_address;
|
||||
sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_address = new_sector_address;
|
||||
}
|
||||
|
||||
/* Indicate the write was successful. */
|
||||
status = LX_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Indicate the write was unsuccessful. */
|
||||
status = LX_NO_SECTORS;
|
||||
}
|
||||
|
||||
#ifdef LX_THREAD_SAFE_ENABLE
|
||||
|
||||
/* Release the thread safe mutex. */
|
||||
tx_mutex_put(&nor_flash -> lx_nor_flash_mutex);
|
||||
#endif
|
||||
|
||||
/* Return the completion status. */
|
||||
return(status);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** NOR Flash Simulator */
|
||||
/** */
|
||||
/**************************************************************************/
|
||||
/**************************************************************************/
|
||||
|
||||
|
||||
/* Include necessary files. */
|
||||
|
||||
#include "tx_api.h"
|
||||
#include "lx_api.h"
|
||||
|
||||
/* Define constants for the NOR flash simulation. */
|
||||
|
||||
/* This configuration is for one physical sector of overhead. */
|
||||
|
||||
|
||||
#define TOTAL_BLOCKS 8
|
||||
#define PHYSICAL_SECTORS_PER_BLOCK 16 /* Min value of 2, max value of 120 for 1 sector of overhead. */
|
||||
#define WORDS_PER_PHYSICAL_SECTOR 128
|
||||
#define FREE_BIT_MAP_WORDS ((PHYSICAL_SECTORS_PER_BLOCK-1)/32)+1
|
||||
#define USABLE_SECTORS_PER_BLOCK (PHYSICAL_SECTORS_PER_BLOCK-1)
|
||||
#define UNUSED_METADATA_WORDS_PER_BLOCK (WORDS_PER_PHYSICAL_SECTOR-(3+FREE_BIT_MAP_WORDS+USABLE_SECTORS_PER_BLOCK))
|
||||
|
||||
|
||||
typedef struct PHYSICAL_SECTOR_STRUCT
|
||||
{
|
||||
unsigned long memory[WORDS_PER_PHYSICAL_SECTOR];
|
||||
} PHYSICAL_SECTOR;
|
||||
|
||||
|
||||
typedef struct FLASH_BLOCK_STRUCT
|
||||
{
|
||||
unsigned long erase_count;
|
||||
unsigned long min_log_sector;
|
||||
unsigned long max_log_sector;
|
||||
unsigned long free_bit_map[FREE_BIT_MAP_WORDS];
|
||||
unsigned long sector_metadata[USABLE_SECTORS_PER_BLOCK];
|
||||
unsigned long unused_words[UNUSED_METADATA_WORDS_PER_BLOCK];
|
||||
PHYSICAL_SECTOR physical_sectors[USABLE_SECTORS_PER_BLOCK];
|
||||
} FLASH_BLOCK;
|
||||
|
||||
FLASH_BLOCK nor_memory_area[TOTAL_BLOCKS];
|
||||
|
||||
ULONG nor_sector_memory[WORDS_PER_PHYSICAL_SECTOR];
|
||||
|
||||
UINT _lx_nor_flash_simulator_initialize(LX_NOR_FLASH *nor_flash);
|
||||
UINT _lx_nor_flash_simulator_read(ULONG *flash_address, ULONG *destination, ULONG words);
|
||||
UINT _lx_nor_flash_simulator_write(ULONG *flash_address, ULONG *source, ULONG words);
|
||||
UINT _lx_nor_flash_simulator_block_erase(ULONG block, ULONG erase_count);
|
||||
UINT _lx_nor_flash_simulator_block_erased_verify(ULONG block);
|
||||
UINT _lx_nor_flash_simulator_erase_all(VOID);
|
||||
UINT _lx_nor_flash_simulator_system_error(UINT error_code, ULONG block, ULONG sector);
|
||||
|
||||
|
||||
|
||||
UINT _lx_nor_flash_simulator_initialize(LX_NOR_FLASH *nor_flash)
|
||||
{
|
||||
|
||||
/* Setup the base address of the flash memory. */
|
||||
nor_flash -> lx_nor_flash_base_address = (ULONG *) &nor_memory_area[0];
|
||||
|
||||
/* Setup geometry of the flash. */
|
||||
nor_flash -> lx_nor_flash_total_blocks = TOTAL_BLOCKS;
|
||||
nor_flash -> lx_nor_flash_words_per_block = sizeof(FLASH_BLOCK)/sizeof(ULONG);
|
||||
|
||||
/* Setup function pointers for the NOR flash services. */
|
||||
nor_flash -> lx_nor_flash_driver_read = _lx_nor_flash_simulator_read;
|
||||
nor_flash -> lx_nor_flash_driver_write = _lx_nor_flash_simulator_write;
|
||||
nor_flash -> lx_nor_flash_driver_block_erase = _lx_nor_flash_simulator_block_erase;
|
||||
nor_flash -> lx_nor_flash_driver_block_erased_verify = _lx_nor_flash_simulator_block_erased_verify;
|
||||
|
||||
/* Setup local buffer for NOR flash operation. This buffer must be the sector size of the NOR flash memory. */
|
||||
nor_flash -> lx_nor_flash_sector_buffer = &nor_sector_memory[0];
|
||||
|
||||
/* Return success. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
UINT _lx_nor_flash_simulator_read(ULONG *flash_address, ULONG *destination, ULONG words)
|
||||
{
|
||||
|
||||
/* Loop to read flash. */
|
||||
while (words--)
|
||||
{
|
||||
/* Copy word. */
|
||||
*destination++ = *flash_address++;
|
||||
}
|
||||
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
UINT _lx_nor_flash_simulator_write(ULONG *flash_address, ULONG *source, ULONG words)
|
||||
{
|
||||
|
||||
/* Loop to write flash. */
|
||||
while (words--)
|
||||
{
|
||||
|
||||
/* Copy word. */
|
||||
*flash_address++ = *source++;
|
||||
}
|
||||
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
UINT _lx_nor_flash_simulator_block_erase(ULONG block, ULONG erase_count)
|
||||
{
|
||||
|
||||
ULONG *pointer;
|
||||
ULONG words;
|
||||
|
||||
LX_PARAMETER_NOT_USED(erase_count);
|
||||
|
||||
/* Setup pointer. */
|
||||
pointer = (ULONG *) &nor_memory_area[block];
|
||||
|
||||
/* Loop to erase block. */
|
||||
words = sizeof(FLASH_BLOCK)/sizeof(ULONG);
|
||||
while (words--)
|
||||
{
|
||||
|
||||
/* Erase word of block. */
|
||||
*pointer++ = (ULONG) 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
UINT _lx_nor_flash_simulator_erase_all(VOID)
|
||||
{
|
||||
|
||||
ULONG *pointer;
|
||||
ULONG words;
|
||||
|
||||
|
||||
/* Setup pointer. */
|
||||
pointer = (ULONG *) &nor_memory_area[0];
|
||||
|
||||
/* Loop to erase block. */
|
||||
words = sizeof(nor_memory_area)/sizeof(ULONG);
|
||||
while (words--)
|
||||
{
|
||||
|
||||
/* Erase word of block. */
|
||||
*pointer++ = (ULONG) 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
UINT _lx_nor_flash_simulator_block_erased_verify(ULONG block)
|
||||
{
|
||||
|
||||
ULONG *word_ptr;
|
||||
ULONG words;
|
||||
|
||||
/* Determine if the block is completely erased. */
|
||||
|
||||
/* Pickup the pointer to the first word of the block. */
|
||||
word_ptr = (ULONG *) &nor_memory_area[block].erase_count;
|
||||
|
||||
/* Calculate the number of words in a block. */
|
||||
words = sizeof(FLASH_BLOCK)/sizeof(ULONG);
|
||||
|
||||
/* Loop to check if the block is erased. */
|
||||
while (words--)
|
||||
{
|
||||
|
||||
/* Is this word erased? */
|
||||
if (*word_ptr++ != 0xFFFFFFFF)
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
/* Return success. */
|
||||
return(LX_SUCCESS);
|
||||
}
|
||||
|
||||
UINT _lx_nor_flash_simulator_system_error(UINT error_code, ULONG block, ULONG sector)
|
||||
{
|
||||
LX_PARAMETER_NOT_USED(error_code);
|
||||
LX_PARAMETER_NOT_USED(block);
|
||||
LX_PARAMETER_NOT_USED(sector);
|
||||
|
||||
/* Custom processing goes here... all errors are fatal. */
|
||||
return(LX_ERROR);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* 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 */
|
||||
/** */
|
||||
/** NOR 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_nor_flash_system_error PORTABLE C */
|
||||
/* 6.0 */
|
||||
/* AUTHOR */
|
||||
/* */
|
||||
/* William E. Lamie, Microsoft Corporation */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function handles system errors in the NOR flash. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* nor_flash NOR flash instance */
|
||||
/* error_code System error code */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* (lx_nor_flash_driver_system_error) Driver system error handler */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Internal LevelX */
|
||||
/* */
|
||||
/* RELEASE HISTORY */
|
||||
/* */
|
||||
/* DATE NAME DESCRIPTION */
|
||||
/* */
|
||||
/* 05-19-2020 William E. Lamie Initial Version 6.0 */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
VOID _lx_nor_flash_system_error(LX_NOR_FLASH *nor_flash, UINT error_code)
|
||||
{
|
||||
|
||||
/* Increment the system error counter. */
|
||||
nor_flash -> lx_nor_flash_diagnostic_system_errors++;
|
||||
|
||||
/* Save the most recent system error code. */
|
||||
nor_flash -> lx_nor_flash_diagnostic_system_error = error_code;
|
||||
|
||||
/* Determine if the driver has setup a system error handler. */
|
||||
if (nor_flash -> lx_nor_flash_driver_system_error)
|
||||
{
|
||||
|
||||
/* Yes, call the driver's system error handler. */
|
||||
(nor_flash -> lx_nor_flash_driver_system_error)(error_code);
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -0,0 +1,236 @@
|
||||
/* This is a small demo of the high-performance FileX FAT file system with LevelX
|
||||
and the NAND simulated driver. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "fx_api.h"
|
||||
#include "lx_api.h"
|
||||
|
||||
|
||||
#define DEMO_STACK_SIZE 4096
|
||||
|
||||
|
||||
/* Buffer for FileX FX_MEDIA sector cache. This must be large enough for at least one
|
||||
sector, which are typically 512 bytes in size. */
|
||||
|
||||
unsigned char media_memory[4096];
|
||||
|
||||
|
||||
/* Define NAND simulated device driver entry. */
|
||||
|
||||
VOID _fx_nand_flash_simulator_driver(FX_MEDIA *media_ptr);
|
||||
|
||||
|
||||
/* Define LevelX NAND simulated flash erase. */
|
||||
|
||||
UINT _lx_nand_flash_simulator_erase_all(VOID);
|
||||
|
||||
|
||||
/* Define thread prototypes. */
|
||||
|
||||
void thread_0_entry(ULONG thread_input);
|
||||
UCHAR thread_0_stack[DEMO_STACK_SIZE];
|
||||
|
||||
|
||||
/* Define FileX global data structures. */
|
||||
|
||||
FX_MEDIA nand_disk;
|
||||
FX_FILE my_file;
|
||||
FX_FILE my_file1;
|
||||
|
||||
|
||||
/* Define ThreadX global data structures. */
|
||||
|
||||
TX_THREAD thread_0;
|
||||
ULONG thread_0_counter;
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
|
||||
/* Enter the ThreadX kernel. */
|
||||
tx_kernel_enter();
|
||||
}
|
||||
|
||||
|
||||
/* Define what the initial system looks like. */
|
||||
|
||||
void tx_application_define(void *first_unused_memory)
|
||||
{
|
||||
|
||||
|
||||
/* Put system definition stuff in here, e.g. thread creates and other assorted
|
||||
create information. */
|
||||
|
||||
/* Create the main thread. */
|
||||
tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0,
|
||||
thread_0_stack, DEMO_STACK_SIZE,
|
||||
1, 1, TX_NO_TIME_SLICE, TX_AUTO_START);
|
||||
|
||||
/* Initialize NAND flash. */
|
||||
lx_nand_flash_initialize();
|
||||
|
||||
/* Initialize FileX. */
|
||||
fx_system_initialize();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void thread_0_entry(ULONG thread_input)
|
||||
{
|
||||
|
||||
UINT status;
|
||||
ULONG actual;
|
||||
CHAR local_buffer[30];
|
||||
|
||||
LX_PARAMETER_NOT_USED(thread_input);
|
||||
|
||||
/* Erase the simulated NAND flash. */
|
||||
_lx_nand_flash_simulator_erase_all();
|
||||
|
||||
/* Format the NAND disk - the memory for the NAND flash disk is setup in
|
||||
the NAND simulator. Note that for best performance, the format of the
|
||||
NAND flash should be less than one full NAND flash block of sectors. */
|
||||
fx_media_format(&nand_disk,
|
||||
_fx_nand_flash_simulator_driver, // Driver entry
|
||||
FX_NULL, // Unused
|
||||
media_memory, // Media buffer pointer
|
||||
sizeof(media_memory), // Media buffer size
|
||||
"MY_NAND_DISK", // Volume Name
|
||||
1, // Number of FATs
|
||||
32, // Directory Entries
|
||||
0, // Hidden sectors
|
||||
120, // Total sectors
|
||||
2048, // Sector size
|
||||
1, // Sectors per cluster
|
||||
1, // Heads
|
||||
1); // Sectors per track
|
||||
|
||||
/* Loop to repeat the demo over and over! */
|
||||
do
|
||||
{
|
||||
|
||||
/* Open the NAND disk. */
|
||||
status = fx_media_open(&nand_disk, "NAND DISK", _fx_nand_flash_simulator_driver, FX_NULL, media_memory, sizeof(media_memory));
|
||||
|
||||
/* Check the media open status. */
|
||||
if (status != FX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Error, break the loop! */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Create a file called TEST.TXT in the root directory. */
|
||||
status = fx_file_create(&nand_disk, "TEST.TXT");
|
||||
|
||||
/* Check the create status. */
|
||||
if (status != FX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Check for an already created status. This is expected on the
|
||||
second pass of this loop! */
|
||||
if (status != FX_ALREADY_CREATED)
|
||||
{
|
||||
|
||||
/* Create error, break the loop. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Open the test file. */
|
||||
status = fx_file_open(&nand_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE);
|
||||
|
||||
/* Check the file open status. */
|
||||
if (status != FX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Error opening file, break the loop. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Seek to the beginning of the test file. */
|
||||
status = fx_file_seek(&my_file, 0);
|
||||
|
||||
/* Check the file seek status. */
|
||||
if (status != FX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Error performing file seek, break the loop. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Write a string to the test file. */
|
||||
status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28);
|
||||
|
||||
/* Check the file write status. */
|
||||
if (status != FX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Error writing to a file, break the loop. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Seek to the beginning of the test file. */
|
||||
status = fx_file_seek(&my_file, 0);
|
||||
|
||||
/* Check the file seek status. */
|
||||
if (status != FX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Error performing file seek, break the loop. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read the first 28 bytes of the test file. */
|
||||
status = fx_file_read(&my_file, local_buffer, 28, &actual);
|
||||
|
||||
/* Check the file read status. */
|
||||
if ((status != FX_SUCCESS) || (actual != 28))
|
||||
{
|
||||
|
||||
/* Error reading file, break the loop. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Close the test file. */
|
||||
status = fx_file_close(&my_file);
|
||||
|
||||
/* Check the file close status. */
|
||||
if (status != FX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Error closing the file, break the loop. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Delete the file. */
|
||||
status = fx_file_delete(&nand_disk, "TEST.TXT");
|
||||
|
||||
/* Check the file delete status. */
|
||||
if (status != FX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Error deleting the file, break the loop. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Close the media. */
|
||||
status = fx_media_close(&nand_disk);
|
||||
|
||||
/* Check the media close status. */
|
||||
if (status != FX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Error closing the media, break the loop. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Increment the thread counter, which represents the number
|
||||
of successful passes through this loop. */
|
||||
thread_0_counter++;
|
||||
} while (1);
|
||||
|
||||
/* If we get here the FileX test failed! */
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
/* This is a small demo of the high-performance FileX FAT file system with LevelX
|
||||
and the NOR simulated driver. */
|
||||
|
||||
#include "fx_api.h"
|
||||
#include "lx_api.h"
|
||||
|
||||
|
||||
#define DEMO_STACK_SIZE 2048
|
||||
|
||||
|
||||
/* Buffer for FileX FX_MEDIA sector cache. This must be large enough for at least one
|
||||
sector, which are typically 512 bytes in size. */
|
||||
|
||||
unsigned char media_memory[512];
|
||||
|
||||
|
||||
/* Define NOR simulated device driver entry. */
|
||||
|
||||
VOID _fx_nor_flash_simulator_driver(FX_MEDIA *media_ptr);
|
||||
|
||||
|
||||
/* Define LevelX NOR simulated flash erase. */
|
||||
|
||||
UINT _lx_nor_flash_simulator_erase_all(VOID);
|
||||
|
||||
|
||||
/* Define thread prototypes. */
|
||||
|
||||
void thread_0_entry(ULONG thread_input);
|
||||
UCHAR thread_0_stack[DEMO_STACK_SIZE];
|
||||
|
||||
|
||||
/* Define FileX global data structures. */
|
||||
|
||||
FX_MEDIA nor_disk;
|
||||
FX_FILE my_file;
|
||||
|
||||
|
||||
|
||||
/* Define ThreadX global data structures. */
|
||||
|
||||
TX_THREAD thread_0;
|
||||
ULONG thread_0_counter;
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
|
||||
/* Enter the ThreadX kernel. */
|
||||
tx_kernel_enter();
|
||||
}
|
||||
|
||||
|
||||
/* Define what the initial system looks like. */
|
||||
|
||||
void tx_application_define(void *first_unused_memory)
|
||||
{
|
||||
|
||||
/* Put system definition stuff in here, e.g. thread creates and other assorted
|
||||
create information. */
|
||||
|
||||
/* Create the main thread. */
|
||||
tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0,
|
||||
thread_0_stack, DEMO_STACK_SIZE,
|
||||
1, 1, TX_NO_TIME_SLICE, TX_AUTO_START);
|
||||
|
||||
/* Initialize NOR flash. */
|
||||
lx_nor_flash_initialize();
|
||||
|
||||
/* Initialize FileX. */
|
||||
fx_system_initialize();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void thread_0_entry(ULONG thread_input)
|
||||
{
|
||||
|
||||
UINT status;
|
||||
ULONG actual;
|
||||
CHAR local_buffer[30];
|
||||
|
||||
LX_PARAMETER_NOT_USED(thread_input);
|
||||
|
||||
/* Erase the simulated NOR flash. */
|
||||
_lx_nor_flash_simulator_erase_all();
|
||||
|
||||
/* Format the NOR disk - the memory for the NOR flash disk is setup in
|
||||
the NOR simulator. Note that for best performance, the format of the
|
||||
NOR flash should be less than one full NOR flash block of sectors. */
|
||||
fx_media_format(&nor_disk,
|
||||
_fx_nor_flash_simulator_driver, // Driver entry
|
||||
FX_NULL, // Unused
|
||||
media_memory, // Media buffer pointer
|
||||
sizeof(media_memory), // Media buffer size
|
||||
"MY_NOR_DISK", // Volume Name
|
||||
1, // Number of FATs
|
||||
32, // Directory Entries
|
||||
0, // Hidden sectors
|
||||
120, // Total sectors
|
||||
512, // Sector size
|
||||
1, // Sectors per cluster
|
||||
1, // Heads
|
||||
1); // Sectors per track
|
||||
|
||||
/* Loop to repeat the demo over and over! */
|
||||
do
|
||||
{
|
||||
|
||||
/* Open the NOR disk. */
|
||||
status = fx_media_open(&nor_disk, "NOR DISK", _fx_nor_flash_simulator_driver, FX_NULL, media_memory, sizeof(media_memory));
|
||||
|
||||
/* Check the media open status. */
|
||||
if (status != FX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Error, break the loop! */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Create a file called TEST.TXT in the root directory. */
|
||||
status = fx_file_create(&nor_disk, "TEST.TXT");
|
||||
|
||||
/* Check the create status. */
|
||||
if (status != FX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Check for an already created status. This is expected on the
|
||||
second pass of this loop! */
|
||||
if (status != FX_ALREADY_CREATED)
|
||||
{
|
||||
|
||||
/* Create error, break the loop. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Open the test file. */
|
||||
status = fx_file_open(&nor_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE);
|
||||
|
||||
/* Check the file open status. */
|
||||
if (status != FX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Error opening file, break the loop. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Seek to the beginning of the test file. */
|
||||
status = fx_file_seek(&my_file, 0);
|
||||
|
||||
/* Check the file seek status. */
|
||||
if (status != FX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Error performing file seek, break the loop. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Write a string to the test file. */
|
||||
status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28);
|
||||
|
||||
/* Check the file write status. */
|
||||
if (status != FX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Error writing to a file, break the loop. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Seek to the beginning of the test file. */
|
||||
status = fx_file_seek(&my_file, 0);
|
||||
|
||||
/* Check the file seek status. */
|
||||
if (status != FX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Error performing file seek, break the loop. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read the first 28 bytes of the test file. */
|
||||
status = fx_file_read(&my_file, local_buffer, 28, &actual);
|
||||
|
||||
/* Check the file read status. */
|
||||
if ((status != FX_SUCCESS) || (actual != 28))
|
||||
{
|
||||
|
||||
/* Error reading file, break the loop. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Close the test file. */
|
||||
status = fx_file_close(&my_file);
|
||||
|
||||
/* Check the file close status. */
|
||||
if (status != FX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Error closing the file, break the loop. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Delete the file. */
|
||||
status = fx_file_delete(&nor_disk, "TEST.TXT");
|
||||
|
||||
/* Check the file delete status. */
|
||||
if (status != FX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Error deleting the file, break the loop. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Close the media. */
|
||||
status = fx_media_close(&nor_disk);
|
||||
|
||||
/* Check the media close status. */
|
||||
if (status != FX_SUCCESS)
|
||||
{
|
||||
|
||||
/* Error closing the media, break the loop. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Increment the thread counter, which represents the number
|
||||
of successful passes through this loop. */
|
||||
thread_0_counter++;
|
||||
|
||||
} while (1);
|
||||
|
||||
/* If we get here the FileX test failed! */
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user