commit 6f4ea0561f0ad9153b1ffd3b4f82ad9e3975969d Author: PProvost Date: Mon May 11 08:49:43 2020 -0600 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ee9315d --- /dev/null +++ b/.gitattributes @@ -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 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9c33dc7 --- /dev/null +++ b/.gitignore @@ -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 + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..111bc45 --- /dev/null +++ b/CMakeLists.txt @@ -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) + diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..76974a3 --- /dev/null +++ b/LICENSE.txt @@ -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. \ No newline at end of file diff --git a/LICENSED-HARDWARE.txt b/LICENSED-HARDWARE.txt new file mode 100644 index 0000000..77dd1ab --- /dev/null +++ b/LICENSED-HARDWARE.txt @@ -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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ef87e9c --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# LevelX \ No newline at end of file diff --git a/TODO b/TODO new file mode 100644 index 0000000..10ea7f7 --- /dev/null +++ b/TODO @@ -0,0 +1,3 @@ +TODO: + + diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100755 index 0000000..a08604e --- /dev/null +++ b/common/CMakeLists.txt @@ -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 +) + diff --git a/common/inc/lx_api.h b/common/inc/lx_api.h new file mode 100644 index 0000000..622790b --- /dev/null +++ b/common/inc/lx_api.h @@ -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 + diff --git a/common/src/fx_nand_flash_simulated_driver.c b/common/src/fx_nand_flash_simulated_driver.c new file mode 100644 index 0000000..21df7ce --- /dev/null +++ b/common/src/fx_nand_flash_simulated_driver.c @@ -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; + } + } +} + diff --git a/common/src/fx_nor_flash_simulator_driver.c b/common/src/fx_nor_flash_simulator_driver.c new file mode 100644 index 0000000..7658979 --- /dev/null +++ b/common/src/fx_nor_flash_simulator_driver.c @@ -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; + } + } +} + diff --git a/common/src/lx_nand_flash_256byte_ecc_check.c b/common/src/lx_nand_flash_256byte_ecc_check.c new file mode 100644 index 0000000..4a9d0d7 --- /dev/null +++ b/common/src/lx_nand_flash_256byte_ecc_check.c @@ -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); + } +} + diff --git a/common/src/lx_nand_flash_256byte_ecc_compute.c b/common/src/lx_nand_flash_256byte_ecc_compute.c new file mode 100644 index 0000000..0a8a78c --- /dev/null +++ b/common/src/lx_nand_flash_256byte_ecc_compute.c @@ -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); +} + diff --git a/common/src/lx_nand_flash_block_full_update.c b/common/src/lx_nand_flash_block_full_update.c new file mode 100644 index 0000000..e63a4d5 --- /dev/null +++ b/common/src/lx_nand_flash_block_full_update.c @@ -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); +} + diff --git a/common/src/lx_nand_flash_block_obsoleted_check.c b/common/src/lx_nand_flash_block_obsoleted_check.c new file mode 100644 index 0000000..77a53d7 --- /dev/null +++ b/common/src/lx_nand_flash_block_obsoleted_check.c @@ -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); + } + } + } + } +} + diff --git a/common/src/lx_nand_flash_block_reclaim.c b/common/src/lx_nand_flash_block_reclaim.c new file mode 100644 index 0000000..5dd5340 --- /dev/null +++ b/common/src/lx_nand_flash_block_reclaim.c @@ -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); +} + diff --git a/common/src/lx_nand_flash_close.c b/common/src/lx_nand_flash_close.c new file mode 100644 index 0000000..db32145 --- /dev/null +++ b/common/src/lx_nand_flash_close.c @@ -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); +} + + diff --git a/common/src/lx_nand_flash_defragment.c b/common/src/lx_nand_flash_defragment.c new file mode 100644 index 0000000..18b7bfa --- /dev/null +++ b/common/src/lx_nand_flash_defragment.c @@ -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); +} + + diff --git a/common/src/lx_nand_flash_driver_block_erase.c b/common/src/lx_nand_flash_driver_block_erase.c new file mode 100644 index 0000000..e0e7dbb --- /dev/null +++ b/common/src/lx_nand_flash_driver_block_erase.c @@ -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); +} + + diff --git a/common/src/lx_nand_flash_driver_block_erased_verify.c b/common/src/lx_nand_flash_driver_block_erased_verify.c new file mode 100644 index 0000000..92958b4 --- /dev/null +++ b/common/src/lx_nand_flash_driver_block_erased_verify.c @@ -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); +} + + diff --git a/common/src/lx_nand_flash_driver_block_status_get.c b/common/src/lx_nand_flash_driver_block_status_get.c new file mode 100644 index 0000000..3138759 --- /dev/null +++ b/common/src/lx_nand_flash_driver_block_status_get.c @@ -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); +} + + diff --git a/common/src/lx_nand_flash_driver_block_status_set.c b/common/src/lx_nand_flash_driver_block_status_set.c new file mode 100644 index 0000000..2bdd4d0 --- /dev/null +++ b/common/src/lx_nand_flash_driver_block_status_set.c @@ -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); +} + + diff --git a/common/src/lx_nand_flash_driver_extra_bytes_get.c b/common/src/lx_nand_flash_driver_extra_bytes_get.c new file mode 100644 index 0000000..df481f6 --- /dev/null +++ b/common/src/lx_nand_flash_driver_extra_bytes_get.c @@ -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); +} + + diff --git a/common/src/lx_nand_flash_driver_extra_bytes_set.c b/common/src/lx_nand_flash_driver_extra_bytes_set.c new file mode 100644 index 0000000..778ced3 --- /dev/null +++ b/common/src/lx_nand_flash_driver_extra_bytes_set.c @@ -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); +} + + diff --git a/common/src/lx_nand_flash_driver_page_erased_verify.c b/common/src/lx_nand_flash_driver_page_erased_verify.c new file mode 100644 index 0000000..7129e92 --- /dev/null +++ b/common/src/lx_nand_flash_driver_page_erased_verify.c @@ -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); +} + diff --git a/common/src/lx_nand_flash_driver_read.c b/common/src/lx_nand_flash_driver_read.c new file mode 100644 index 0000000..28ef74b --- /dev/null +++ b/common/src/lx_nand_flash_driver_read.c @@ -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); +} + diff --git a/common/src/lx_nand_flash_driver_write.c b/common/src/lx_nand_flash_driver_write.c new file mode 100644 index 0000000..e2eb34a --- /dev/null +++ b/common/src/lx_nand_flash_driver_write.c @@ -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); +} + diff --git a/common/src/lx_nand_flash_extended_cache_enable.c b/common/src/lx_nand_flash_extended_cache_enable.c new file mode 100644 index 0000000..f838167 --- /dev/null +++ b/common/src/lx_nand_flash_extended_cache_enable.c @@ -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); +} + diff --git a/common/src/lx_nand_flash_initialize.c b/common/src/lx_nand_flash_initialize.c new file mode 100644 index 0000000..ef64285 --- /dev/null +++ b/common/src/lx_nand_flash_initialize.c @@ -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); +} + diff --git a/common/src/lx_nand_flash_logical_sector_find.c b/common/src/lx_nand_flash_logical_sector_find.c new file mode 100644 index 0000000..28abf18 --- /dev/null +++ b/common/src/lx_nand_flash_logical_sector_find.c @@ -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); +} + diff --git a/common/src/lx_nand_flash_next_block_to_erase_find.c b/common/src/lx_nand_flash_next_block_to_erase_find.c new file mode 100644 index 0000000..78b43fc --- /dev/null +++ b/common/src/lx_nand_flash_next_block_to_erase_find.c @@ -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); +} + diff --git a/common/src/lx_nand_flash_open.c b/common/src/lx_nand_flash_open.c new file mode 100644 index 0000000..b538e4f --- /dev/null +++ b/common/src/lx_nand_flash_open.c @@ -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); +} + diff --git a/common/src/lx_nand_flash_page_ecc_check.c b/common/src/lx_nand_flash_page_ecc_check.c new file mode 100644 index 0000000..27fb9dc --- /dev/null +++ b/common/src/lx_nand_flash_page_ecc_check.c @@ -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); +} + diff --git a/common/src/lx_nand_flash_page_ecc_compute.c b/common/src/lx_nand_flash_page_ecc_compute.c new file mode 100644 index 0000000..0cbdfd7 --- /dev/null +++ b/common/src/lx_nand_flash_page_ecc_compute.c @@ -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); +} + diff --git a/common/src/lx_nand_flash_partial_defragment.c b/common/src/lx_nand_flash_partial_defragment.c new file mode 100644 index 0000000..1f5c7ae --- /dev/null +++ b/common/src/lx_nand_flash_partial_defragment.c @@ -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); +} + + diff --git a/common/src/lx_nand_flash_physical_page_allocate.c b/common/src/lx_nand_flash_physical_page_allocate.c new file mode 100644 index 0000000..6028a86 --- /dev/null +++ b/common/src/lx_nand_flash_physical_page_allocate.c @@ -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); +} + diff --git a/common/src/lx_nand_flash_sector_mapping_cache_invalidate.c b/common/src/lx_nand_flash_sector_mapping_cache_invalidate.c new file mode 100644 index 0000000..d90236f --- /dev/null +++ b/common/src/lx_nand_flash_sector_mapping_cache_invalidate.c @@ -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 + } +} + diff --git a/common/src/lx_nand_flash_sector_read.c b/common/src/lx_nand_flash_sector_read.c new file mode 100644 index 0000000..0b6541d --- /dev/null +++ b/common/src/lx_nand_flash_sector_read.c @@ -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); +} + diff --git a/common/src/lx_nand_flash_sector_release.c b/common/src/lx_nand_flash_sector_release.c new file mode 100644 index 0000000..e299527 --- /dev/null +++ b/common/src/lx_nand_flash_sector_release.c @@ -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); +} + diff --git a/common/src/lx_nand_flash_sector_write.c b/common/src/lx_nand_flash_sector_write.c new file mode 100644 index 0000000..c602087 --- /dev/null +++ b/common/src/lx_nand_flash_sector_write.c @@ -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); +} + + diff --git a/common/src/lx_nand_flash_simulator.c b/common/src/lx_nand_flash_simulator.c new file mode 100644 index 0000000..8368daa --- /dev/null +++ b/common/src/lx_nand_flash_simulator.c @@ -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); +} + + + diff --git a/common/src/lx_nand_flash_system_error.c b/common/src/lx_nand_flash_system_error.c new file mode 100644 index 0000000..f942323 --- /dev/null +++ b/common/src/lx_nand_flash_system_error.c @@ -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); + } +} + diff --git a/common/src/lx_nor_flash_block_reclaim.c b/common/src/lx_nor_flash_block_reclaim.c new file mode 100644 index 0000000..c0553f9 --- /dev/null +++ b/common/src/lx_nor_flash_block_reclaim.c @@ -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); +} + diff --git a/common/src/lx_nor_flash_close.c b/common/src/lx_nor_flash_close.c new file mode 100644 index 0000000..ea2981f --- /dev/null +++ b/common/src/lx_nor_flash_close.c @@ -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); +} + + diff --git a/common/src/lx_nor_flash_defragment.c b/common/src/lx_nor_flash_defragment.c new file mode 100644 index 0000000..e709efe --- /dev/null +++ b/common/src/lx_nor_flash_defragment.c @@ -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); +} + + diff --git a/common/src/lx_nor_flash_driver_block_erase.c b/common/src/lx_nor_flash_driver_block_erase.c new file mode 100644 index 0000000..01cc309 --- /dev/null +++ b/common/src/lx_nor_flash_driver_block_erase.c @@ -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); +} + + diff --git a/common/src/lx_nor_flash_driver_read.c b/common/src/lx_nor_flash_driver_read.c new file mode 100644 index 0000000..1ee317e --- /dev/null +++ b/common/src/lx_nor_flash_driver_read.c @@ -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 +} + + diff --git a/common/src/lx_nor_flash_driver_write.c b/common/src/lx_nor_flash_driver_write.c new file mode 100644 index 0000000..358ebef --- /dev/null +++ b/common/src/lx_nor_flash_driver_write.c @@ -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 +} + diff --git a/common/src/lx_nor_flash_extended_cache_enable.c b/common/src/lx_nor_flash_extended_cache_enable.c new file mode 100644 index 0000000..f94a199 --- /dev/null +++ b/common/src/lx_nor_flash_extended_cache_enable.c @@ -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 +} + + diff --git a/common/src/lx_nor_flash_initialize.c b/common/src/lx_nor_flash_initialize.c new file mode 100644 index 0000000..4e94e88 --- /dev/null +++ b/common/src/lx_nor_flash_initialize.c @@ -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); +} + diff --git a/common/src/lx_nor_flash_logical_sector_find.c b/common/src/lx_nor_flash_logical_sector_find.c new file mode 100644 index 0000000..25d0269 --- /dev/null +++ b/common/src/lx_nor_flash_logical_sector_find.c @@ -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); +} + diff --git a/common/src/lx_nor_flash_next_block_to_erase_find.c b/common/src/lx_nor_flash_next_block_to_erase_find.c new file mode 100644 index 0000000..6ca0b3d --- /dev/null +++ b/common/src/lx_nor_flash_next_block_to_erase_find.c @@ -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); +} + diff --git a/common/src/lx_nor_flash_open.c b/common/src/lx_nor_flash_open.c new file mode 100644 index 0000000..a7c3f6b --- /dev/null +++ b/common/src/lx_nor_flash_open.c @@ -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); +} + diff --git a/common/src/lx_nor_flash_partial_defragment.c b/common/src/lx_nor_flash_partial_defragment.c new file mode 100644 index 0000000..15c1ca6 --- /dev/null +++ b/common/src/lx_nor_flash_partial_defragment.c @@ -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); +} + + diff --git a/common/src/lx_nor_flash_physical_sector_allocate.c b/common/src/lx_nor_flash_physical_sector_allocate.c new file mode 100644 index 0000000..5ed33b4 --- /dev/null +++ b/common/src/lx_nor_flash_physical_sector_allocate.c @@ -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); +} + diff --git a/common/src/lx_nor_flash_sector_mapping_cache_invalidate.c b/common/src/lx_nor_flash_sector_mapping_cache_invalidate.c new file mode 100644 index 0000000..47af9e1 --- /dev/null +++ b/common/src/lx_nor_flash_sector_mapping_cache_invalidate.c @@ -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; + } + } +} + diff --git a/common/src/lx_nor_flash_sector_read.c b/common/src/lx_nor_flash_sector_read.c new file mode 100644 index 0000000..5fbe478 --- /dev/null +++ b/common/src/lx_nor_flash_sector_read.c @@ -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); +} + diff --git a/common/src/lx_nor_flash_sector_release.c b/common/src/lx_nor_flash_sector_release.c new file mode 100644 index 0000000..87268c4 --- /dev/null +++ b/common/src/lx_nor_flash_sector_release.c @@ -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); +} + diff --git a/common/src/lx_nor_flash_sector_write.c b/common/src/lx_nor_flash_sector_write.c new file mode 100644 index 0000000..c88ae2c --- /dev/null +++ b/common/src/lx_nor_flash_sector_write.c @@ -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); +} + + diff --git a/common/src/lx_nor_flash_simulator.c b/common/src/lx_nor_flash_simulator.c new file mode 100644 index 0000000..634b1f8 --- /dev/null +++ b/common/src/lx_nor_flash_simulator.c @@ -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); +} + diff --git a/common/src/lx_nor_flash_system_error.c b/common/src/lx_nor_flash_system_error.c new file mode 100644 index 0000000..e82bb51 --- /dev/null +++ b/common/src/lx_nor_flash_system_error.c @@ -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); + } +} + diff --git a/docs/LevelX_User_Guide.docx b/docs/LevelX_User_Guide.docx new file mode 100644 index 0000000..0d10c75 Binary files /dev/null and b/docs/LevelX_User_Guide.docx differ diff --git a/docs/LevelX_User_Guide.pdf b/docs/LevelX_User_Guide.pdf new file mode 100755 index 0000000..fc7eaea Binary files /dev/null and b/docs/LevelX_User_Guide.pdf differ diff --git a/samples/demo_filex_nand_flash.c b/samples/demo_filex_nand_flash.c new file mode 100644 index 0000000..da9f801 --- /dev/null +++ b/samples/demo_filex_nand_flash.c @@ -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 +#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; +} + diff --git a/samples/demo_filex_nor_flash.c b/samples/demo_filex_nor_flash.c new file mode 100644 index 0000000..2125bbd --- /dev/null +++ b/samples/demo_filex_nor_flash.c @@ -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; +} +