diff --git a/Documentation/applications/testing/nand_sim/index.rst b/Documentation/applications/testing/nand_sim/index.rst new file mode 100644 index 00000000000..cf5fb2cee84 --- /dev/null +++ b/Documentation/applications/testing/nand_sim/index.rst @@ -0,0 +1,171 @@ +====================================== +``nand`` - NAND Flash Device Simulator +====================================== + +In order to test the filesystems that work with NAND flash devices in a +simulator, this exists to provide a virtual NAND flash device, along with its +driver, to allow manual (or scripted) testing, as well as to provide an +option to log the various actions performed under-the-hood along with the +state of the device, which includes the read, write and erase counts of each +page in the device. + +Structure of NAND Flash +======================= + +Most NAND flashes share a common interface, specified by the +`Open NAND Flash Interface (ONFI) `_. + +The important part from it, required in this context, is that a NAND Flash is +divided into a lot of blocks. And each blocks are divided into a lot of +pages. + +Here's the peculiar bit. If you want to erase a page, you need to erase +the *entire* block that it is part of, ie. blocks are the smallest erasable +units in a NAND flash. However, a page is the smallest unit to which you can +write data, or read data from. + +Why would you need erase operation? The Program/ Erase (P/E) cycle states +that a page (and thus its block) need to be erased first before data can be +written to it (Erase-Before-Write). + +Each page has a data area, and a spare area. Depending on the data area's +size, the spare area may have different structures (schemes). All the +required schemes are defined in ``/drivers/mtd/mtd_nandscheme.c`` (in +the ``g_nand_sparescheme*`` structures). + +Due to the nature of NAND flash, upon testing, a manufaturer may decide that +a certain block fails some test(s), and mark it as a **bad block** by +writing a certain value in a certain position in the spare area (depends on +data area's size, and thus, the spare area's scheme) of every page in it. + +.. NOTE:: + * While certain blocks may *still work* even if they are marked as bad, + it's inadvisable to store any data in it. + + * The spare data is the **only** record of a block being bad or not. + Please do not erase it. + + * Certain blocks may become bad after continuous usage, and would need + to be marked as such by either the filesystem or the driver. + +Currently, this simulator supports only 512 B sized pages, which means it +will follow the ``g_nand_sparescheme512`` scheme for its spare area, and +thus have a bad block marker at index ``5``. + +If a block is *not* bad, it contains a value of ``0xff`` in the place of a +bad block marker. Any other value denote it's a bad block. + +RAM to Device (Lower Half) +========================== + +Since this is an emulation, RAM of the host running the simulator is used +to create the device. While the speed of operations won't be even close to +the original, this being in the RAM, which works multitudes faster than +actual device, the functionality on the other hand has been kept consistent +to the utmost. + +First, ``/include/nuttx/mtd/nand.h`` has a structure ``struct nand_dev_s`` +defining a raw NAND MTD device (lowest level). Its field ``nand_dev_s->raw`` +is of type ``struct nand_raw_s *`` (defined in +``include/nuttx/mtd/nand_raw.h``), and this is what will hold the methods +for the raw device. There are primarily 3 methods that need to be looked +into: + +* eraseblock +* rawread +* rawwrite + +Conforming to the functionality of NAND flashes, these three were implemented +as ``nand_ram_*`` in ``/drivers/mtd/nand_ram.c`` to emulate RAM into a +virtual NAND flash. + +While in real devices, the spare area follows the data area (in most schemes) +, since this is virtual, we can get away with keeping the two into two +separate arrays, namely ``nand_ram_flash_data`` and ``nand_ram_flash_spare`` +for data and spare respectively. Each array has as many elements as number +of pages in the device. + +As the spare areas has some spare bytes we can use, some space is used as +counters for the reads/writes/erases each page faces, thus giving a clear +picture of wear of the virtual device to the tester. + +.. NOTE:: + ECC extension has not been implemented yet, so ECC bits in spare are + yet to be used or initialized properly. + +The method ``nand_ram_initialize()`` takes in a preallocated space for a +raw device (of type ``struct nand_raw_s`` as defined in +``include/nuttx/mtd/nand_raw.h``) and attaches these 3 custom methods as well +as device information like page size, block size, etc. to it. These form +the lower half of the driver. + +Upper Half +========== + +The method ``nand_ram_initialize()`` also initializes a +``struct mtd_dev_s *`` (defined in ``include/nuttx/mtd/mtd.h``), which it +returns. This structure contains a reference to our custom lower half in +``mtd_dev_s->raw``, as well as an upper half containing methods ``nand_*`` +(defined in ``drivers/mtd/mtd_nand.c``). + +Wrapper Over Upper Half +======================= + +The upper half, along with the lower half attached to it, received from +``nand_ram_initialize()`` contains these 5 methods for the upper half: + +* erase +* bread +* bwrite +* ioctl +* isbad +* markbad + +Each driver's upper half needs to be registered with NuttX before it can +appear in the list of devices (in ``/dev``). Instead of the previously +acquired upper-half, we'll be registering a wrapper over it, to improve +logging. The wrapper methods are defined as ``nand_wrapper_*`` in +``drivers/mtd/mtd_nandwrapper.c``. + +Here's a complicated bit. The actual upper half is an MTD device, but +more specifically, it is a NAND MTD device, which is represented by +``struct nand_dev_s``. Due to how it is defined, ``struct mtd_dev_s`` forms +the very start of ``struct nand_dev_s``, and hence they can be type-casted +to each other (provided required memory is accessible). Our wrapper, being a +wrapper over an MTD device, is an MTD device itself as well. MTD device +methods take in a ``struct mtd_dev_s *dev`` which describe the device +itself (which is the actual device that is registered using +``register_mtddriver``), which includes its methods. Our wrapper methods +receive such a device as well, which contains the wrapper device including +the wrapper functions. But, this way, there is no way of accessing the +methods of the actual upper half itself. Thus, instead of ``dev`` being +of type ``struct nand_dev_s``, it is actually of type +``struct nand_wrapper_dev_s`` which is a superset of ``struct nand_dev_s``, +who itself is a superset of ``struct mtd_dev_s``. Similar to previous case, +``struct mtd_dev_s`` forms the very start of ``struct nand_wrapper_dev_s``, +and thus the types are inter-changeable. + +The methods ``nand_wrapper_*`` in ``drivers/mtd/mtd_nandwrapper.c`` all +pass the parameters to corresponding method of the actual upper half +after logging it. *But, the device passed on to the actual upper half +is still the wrapper, not the actual upper half, as the upper half methods +may call the other methods as well internally and we might want to log +them as well*. + +Registering Device & Daemon +=========================== + +This wrapper is then registered using ``register_mtddriver``, and this +whole thing is converted to be a daemon, so that the device can keep running +in the background. + +Making it a daemon is achieved by using ``fork()``, killing the parent, and +using ``daemon()`` in child. + +Known Issues +============ + +* ECC is not implemented yet. +* MLC NAND Flash is not implemented yet. +* Due to the fixed name of the device, there can't be more than one instance + of this virtual device. \ No newline at end of file diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 0cf5f9de08f..5d75e849729 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -271,6 +271,71 @@ config MTD_NAND_EMBEDDEDECC only one supported) is Micron, 4-bit ECC, device size = 1Gb or 2Gb or 4Gb. +config MTD_NAND_RAM + bool "Enable virtual NAND Flash" + default n + ---help--- + Enable a virtual NAND Flash device emulated from RAM in the simulator. + +if MTD_NAND_RAM + +config MTD_NAND_RAM_SIZE + int "Size of the virtual NAND Flash in MB." + default 2 + ---help--- + Size of the virtual NAND Flash in megabytes. + +config MTD_NAND_RAM_DEBUG + bool "Enable debugging of virtual NAND Flash." + default n + ---help--- + Enables debug info being written to syslog for virtual NAND Flash + device emulated from RAM in the simulator. + +config MTD_NAND_RAM_DEBUG_LEVEL + int "Debugging level of virtual NAND Flash raw lower half operations." + depends on MTD_NAND_RAM_DEBUG + default 1 + range 1 3 + ---help--- + 1 - Log every instruction. + 2 - Log every 5 instructions. + 3 - Log every 10 instructions. + +config MTD_NAND_RAM_STATUS + int "Log status of virtual NAND Flash." + depends on MTD_NAND_RAM_DEBUG + default 3 + range 1 8 + ---help--- + 1 - Log every instruction. + 2 - Log every 5 instructions. + 3 - Log every 10 instructions. + 4 - Log every 50 instructions. + 5 - Log every 100 instructions. + 6 - Log every 500 instructions. + 7 - Log every 1000 instructions. + 8 - Log every 5000 instructions. + +endif #MTD_NAND_RAM + +config MTD_NAND_WRAPPER + bool "Enable logging wrapper for NAND flash upper half operations." + default n + +if MTD_NAND_WRAPPER + +config MTD_NAND_WRAPPER_DEBUG_LEVEL + int "Log level of upper half of virtual NAND Flash." + default 1 + range 1 3 + ---help--- + 1 - Log every instruction. + 2 - Log every 5 instructions. + 3 - Log every 10 instructions. + +endif # MTD_NAND_WRAPPER + endif # MTD_NAND config RPMSGMTD diff --git a/drivers/mtd/Make.defs b/drivers/mtd/Make.defs index 549b6c3fb89..6956610c3cd 100644 --- a/drivers/mtd/Make.defs +++ b/drivers/mtd/Make.defs @@ -60,6 +60,15 @@ CSRCS += mtd_nand.c mtd_onfi.c mtd_nandscheme.c mtd_nandmodel.c mtd_modeltab.c ifeq ($(CONFIG_MTD_NAND_SWECC),y) CSRCS += mtd_nandecc.c hamming.c endif + +ifeq ($(CONFIG_MTD_NAND_RAM), y) +CSRCS += mtd_nandram.c +endif + +ifeq ($(CONFIG_MTD_NAND_WRAPPER), y) +CSRCS += mtd_nandwrapper.c +endif + endif ifeq ($(CONFIG_RAMMTD),y) diff --git a/drivers/mtd/mtd_nand.c b/drivers/mtd/mtd_nand.c index f4776d8a6f2..88175faceef 100644 --- a/drivers/mtd/mtd_nand.c +++ b/drivers/mtd/mtd_nand.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include diff --git a/drivers/mtd/mtd_nandram.c b/drivers/mtd/mtd_nandram.c new file mode 100644 index 00000000000..cfaaef7366e --- /dev/null +++ b/drivers/mtd/mtd_nandram.c @@ -0,0 +1,475 @@ +/**************************************************************************** + * drivers/mtd/mtd_nandram.c + * This file deals with the raw lower half of the device driver, and manages + * reading and writing to the actual NAND Flash device that has been emulated + * from RAM. + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_MTD_NAND_RAM_DEBUG + +#define NAND_RAM_DEBUG_1 1 +#define NAND_RAM_DEBUG_2 5 +#define NAND_RAM_DEBUG_3 10 + +#define NAND_RAM_STATUS_1 1 +#define NAND_RAM_STATUS_2 5 +#define NAND_RAM_STATUS_3 10 +#define NAND_RAM_STATUS_4 50 +#define NAND_RAM_STATUS_5 100 +#define NAND_RAM_STATUS_6 500 +#define NAND_RAM_STATUS_7 1000 +#define NAND_RAM_STATUS_8 5000 + +#if CONFIG_MTD_NAND_RAM_DEBUG_LEVEL == 1 +#define NAND_RAM_DEBUG_LEVEL NAND_RAM_DEBUG_1 +#elif CONFIG_MTD_NAND_RAM_DEBUG_LEVEL == 2 +#define NAND_RAM_DEBUG_LEVEL NAND_RAM_DEBUG_2 +#elif CONFIG_MTD_NAND_RAM_DEBUG_LEVEL == 3 +#define NAND_RAM_DEBUG_LEVEL NAND_RAM_DEBUG_3 +#endif /* CONFIG_MTD_NAND_RAM_DEBUG_LEVEL */ + +#if CONFIG_MTD_NAND_RAM_STATUS == 1 +#define NAND_RAM_STATUS_LEVEL NAND_RAM_STATUS_1 +#elif CONFIG_MTD_NAND_RAM_STATUS == 2 +#define NAND_RAM_STATUS_LEVEL NAND_RAM_STATUS_2 +#elif CONFIG_MTD_NAND_RAM_STATUS == 3 +#define NAND_RAM_STATUS_LEVEL NAND_RAM_STATUS_3 +#elif CONFIG_MTD_NAND_RAM_STATUS == 4 +#define NAND_RAM_STATUS_LEVEL NAND_RAM_STATUS_4 +#elif CONFIG_MTD_NAND_RAM_STATUS == 5 +#define NAND_RAM_STATUS_LEVEL NAND_RAM_STATUS_5 +#elif CONFIG_MTD_NAND_RAM_STATUS == 6 +#define NAND_RAM_STATUS_LEVEL NAND_RAM_STATUS_6 +#elif CONFIG_MTD_NAND_RAM_STATUS == 7 +#define NAND_RAM_STATUS_LEVEL NAND_RAM_STATUS_7 +#elif CONFIG_MTD_NAND_RAM_STATUS == 8 +#define NAND_RAM_STATUS_LEVEL NAND_RAM_STATUS_8 +#endif /* CONFIG_MTD_NAND_RAM_STATUS */ + +#define NAND_RAM_LOG(str, ...) \ + { \ + if (nand_ram_ins_i % NAND_RAM_DEBUG_LEVEL == 0) \ + { \ + syslog(LOG_DEBUG, "nand_ram: " str, __VA_ARGS__); \ + } \ + } \ + +#define NAND_RAM_STATUS_LOG(str, ...) \ + syslog(LOG_DEBUG, "nand_ram_status: " str, __VA_ARGS__); + +#else + +#define NAND_RAM_LOG +#define NAND_RAM_STATUS_LOG + +#endif /* CONFIG_MTD_NAND_RAM_DEBUG */ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct nand_ram_data_s +{ + uint8_t page[NAND_RAM_PAGE_SIZE / 8]; +}; + +/* 512 B page spare scheme */ + +struct nand_ram_spare_s +{ + uint8_t ecc_0; /* 0 */ + uint8_t ecc_1; + uint8_t ecc_2; + uint8_t ecc_3; + uint8_t __res1; + uint8_t bad; /* 5 */ /* NAND_RAM_BLOCK_* */ + uint8_t ecc_4; + uint8_t ecc_5; + + /* Using reserved (8 bytes) */ + + uint16_t n_read; + uint16_t n_write; /* 10 */ + uint16_t n_erase; + uint8_t free; /* Erased page: NAND_RAM_PAGE_* */ + uint8_t __res2; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static uint64_t nand_ram_ins_i = 0; /* Instruction counter */ +static mutex_t nand_ram_dev_mut; +static struct nand_ram_data_s nand_ram_flash_data[NAND_RAM_N_PAGES]; +static struct nand_ram_spare_s nand_ram_flash_spare[NAND_RAM_N_PAGES]; + +/* Hard coded array for bad block indexes */ + +static int g_nand_ram_rand_bad_blk_indx[] = + { + 4, 14, 19, 21, 28, 30, 107, + 108, 164, 173, 179, 229, 268, + 362, 377, 382, 396, 410, 412, + 419, 428, 456, 500, 0 + }; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * External Functions + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nand_ram_storage_status + * + * Description: + * Writes per-page status of virtual NAND Flash. + * + ****************************************************************************/ + +static void nand_ram_storage_status(void) +{ + uint32_t i; + uint16_t reads; + uint16_t writes; + uint16_t erases; + uint8_t bad; + + /* Wear */ + + for (i = 0; i < NAND_RAM_N_PAGES; i++) + { + reads = nand_ram_flash_spare[i].n_read; + writes = nand_ram_flash_spare[i].n_write; + erases = nand_ram_flash_spare[i].n_erase; + bad = (nand_ram_flash_spare[i].bad != NAND_RAM_BLOCK_GOOD); + + NAND_RAM_STATUS_LOG( + "Block %3d, Page %6d, Bad: %1d |" + " Reads: %6d, Writes: %6d, Erases: %6d\n", + i >> NAND_RAM_LOG_PAGES_PER_BLOCK, i, bad, + reads, writes, erases); + } + + return; +} + +static inline void nand_ram_status(void) +{ + if (nand_ram_ins_i % NAND_RAM_STATUS_LEVEL == 0) + { + nand_ram_storage_status(); + } +} + +/**************************************************************************** + * Name: nand_ram_storage_init + * + * Description: + * Initializes the actual NAND Device that is emulated from RAM. + * + ****************************************************************************/ + +static void nand_ram_storage_init(void) +{ + int i; + + memset(nand_ram_flash_data, 0xff, + sizeof(struct nand_ram_data_s) * NAND_RAM_N_PAGES); + memset(nand_ram_flash_spare, 0, + sizeof(struct nand_ram_spare_s) * NAND_RAM_N_PAGES); + + for (i = 0; i < NAND_RAM_N_PAGES; i++) + { + nand_ram_flash_spare[i].free = NAND_RAM_PAGE_FREE; + nand_ram_flash_spare[i].bad = NAND_RAM_BLOCK_GOOD; + } + + /* Bad blocks */ + + for (i = 0; + g_nand_ram_rand_bad_blk_indx[i] != 0 && + g_nand_ram_rand_bad_blk_indx[i] < NAND_RAM_N_BLOCKS; + i++) + { + int j; + + for (j = 0; j < NAND_RAM_PAGES_PER_BLOCK; j++) + { + int page = (g_nand_ram_rand_bad_blk_indx[i] << + NAND_RAM_LOG_PAGES_PER_BLOCK)+j; + + /* Set bad block marker to Anything but NAND_RAM_BLOCK_GOOD */ + + nand_ram_flash_spare[page].bad = 0; + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nand_ram_eraseblock + * + * Description: + * Erases a block on the device. + * + * Input Parameters: + * raw: NAND MTD Device raw structure. + * block: Block number (0 indexing) to erase + * + * Returned Value: + * 0: Successful + * < 0: Error + * + ****************************************************************************/ + +int nand_ram_eraseblock(FAR struct nand_raw_s *raw, off_t block) +{ + int i; + uint32_t start_page; + uint32_t end_page; + + start_page = block << NAND_RAM_LOG_PAGES_PER_BLOCK; + end_page = start_page + NAND_RAM_PAGES_PER_BLOCK; + + nxmutex_lock(&nand_ram_dev_mut); + nand_ram_ins_i++; + + NAND_RAM_LOG( + "[LOWER %lu | %s] Block %d, Start Page: %d, Last Page: %d", + nand_ram_ins_i, "eraseblock", block, start_page, end_page - 1 + ); + nand_ram_status(); + + /* [start_page, end_page) is cleared (all bits are set) */ + + memset(nand_ram_flash_data + start_page, 0xff, + (end_page - start_page) * sizeof(struct nand_ram_data_s)); + for (i = start_page; i < end_page; i++) + { + nand_ram_flash_spare[i].n_erase++; + nand_ram_flash_spare[i].free = 1; + } + + NAND_RAM_LOG("[LOWER %lu | %s] Done\n", nand_ram_ins_i, "eraseblock"); + + nxmutex_unlock(&nand_ram_dev_mut); + + return OK; +} + +/**************************************************************************** + * Name: nand_ram_rawread + * + * Description: + * Reads a page from the device. + * + * Input Parameters: + * raw: NAND MTD Device raw structure. + * block: Block number (0 indexing) to erase + * page: Page number (0 indexing) in (relative to) that block + * data: Preallocated memory where the data will be copied to + * spare: Preallocated memory where the spare data will be copied to + * + * Returned Value: + * 0: Successful + * + ****************************************************************************/ + +int nand_ram_rawread(FAR struct nand_raw_s *raw, off_t block, + unsigned int page, FAR void *data, FAR void *spare) +{ + int ret; + uint32_t read_page; + struct nand_ram_data_s *read_page_data; + struct nand_ram_spare_s *read_page_spare; + + read_page = (block << NAND_RAM_LOG_PAGES_PER_BLOCK) + page; + read_page_data = nand_ram_flash_data + read_page; + read_page_spare = nand_ram_flash_spare + read_page; + + ret = OK; + + nxmutex_lock(&nand_ram_dev_mut); + nand_ram_ins_i++; + + NAND_RAM_LOG("[LOWER %lu | %s] Page %d\n", + nand_ram_ins_i, "rawread", read_page); + nand_ram_status(); + + if (nand_ram_flash_spare[read_page].bad != NAND_RAM_BLOCK_GOOD) + { + ret = -EFAULT; + NAND_RAM_LOG("[LOWER %lu | %s] Failed: %s\n", + nand_ram_ins_i, "rawread", EFAULT_STR); + goto errout; + } + + nand_ram_flash_spare[read_page].n_read++; + + if (data != NULL) + { + memcpy(data, (const void *)read_page_data, NAND_RAM_PAGE_SIZE); + } + + if (spare != NULL) + { + memcpy(spare, (const void *)read_page_spare, NAND_RAM_PAGE_SIZE); + } + + NAND_RAM_LOG("[LOWER %lu | %s] Done\n", nand_ram_ins_i, "rawread"); + +errout: + nxmutex_unlock(&nand_ram_dev_mut); + + return ret; +} + +/**************************************************************************** + * Name: nand_ram_rawread + * + * Description: + * Writes a page to the device. + * + * Input Parameters: + * raw: NAND MTD Device raw structure. + * block: Block number (0 indexing) to erase + * page: Page number (0 indexing) in (relative to) that block + * data: Preallocated memory where the data will be copied to + * spare: Preallocated memory where the spare data will be copied to + * + * Returned Value: + * 0: Successful + * -EACCESS: The page's block needs to be erased first before writing to it + * + ****************************************************************************/ + +int nand_ram_rawwrite(FAR struct nand_raw_s *raw, off_t block, + unsigned int page, FAR const void *data, + FAR const void *spare) +{ + int ret; + uint32_t write_page; + struct nand_ram_data_s *write_page_data; + struct nand_ram_spare_s *write_page_spare; + + write_page = (block << NAND_RAM_LOG_PAGES_PER_BLOCK) + page; + write_page_data = nand_ram_flash_data + write_page; + write_page_spare = nand_ram_flash_spare + write_page; + + ret = OK; + + nxmutex_lock(&nand_ram_dev_mut); + nand_ram_ins_i++; + + NAND_RAM_LOG("[LOWER %lu | %s] Page %d\n", + nand_ram_ins_i, "rawwrite", write_page); + nand_ram_status(); + + if (nand_ram_flash_spare[write_page].free != NAND_RAM_PAGE_FREE) + { + ret = -EACCES; + NAND_RAM_LOG("[LOWER %lu | %s] Failed: %s\n", + nand_ram_ins_i, "rawwrite", EACCES_STR); + goto errout; + } + + nand_ram_flash_spare[write_page].n_write++; + + if (data != NULL) + { + memcpy((void *)write_page_data, data, NAND_RAM_PAGE_SIZE); + } + + if (spare != NULL) + { + memcpy((void *)write_page_spare, data, NAND_RAM_PAGE_SIZE); + } + + NAND_RAM_LOG("[LOWER %lu | %s] Done\n", nand_ram_ins_i, "rawwrite"); + +errout: + nxmutex_unlock(&nand_ram_dev_mut); + + return ret; +} + +/**************************************************************************** + * Name: nand_ram_init + * + * Description: + * Driver init. + * + * Input Parameters: + * raw: NAND MTD Device raw structure. + * + * Returned Value: + * A non-NULL MTD driver instance is returned on success. NULL is + * returned on any failure. + * + ****************************************************************************/ + +FAR struct mtd_dev_s *nand_ram_initialize(struct nand_raw_s *raw) +{ + NAND_RAM_LOG("[LOWER | %s]\n", "initialize"); + + nand_ram_storage_init(); + nxmutex_init(&nand_ram_dev_mut); + + raw->model.devid = 123; + raw->model.pagesize = NAND_RAM_PAGE_SIZE; + raw->model.sparesize = NAND_RAM_SPARE_SIZE; + raw->model.devsize = NAND_RAM_SIZE / (1024 * 1024); + raw->model.blocksize = NAND_RAM_BLOCK_SIZE / 1024; + raw->model.scheme = &g_nand_sparescheme512; + + raw->eraseblock = nand_ram_eraseblock; + raw->rawread = nand_ram_rawread; + raw->rawwrite = nand_ram_rawwrite; + + return nand_raw_initialize(raw); +} diff --git a/drivers/mtd/mtd_nandwrapper.c b/drivers/mtd/mtd_nandwrapper.c new file mode 100644 index 00000000000..83e6bbc9862 --- /dev/null +++ b/drivers/mtd/mtd_nandwrapper.c @@ -0,0 +1,418 @@ +/**************************************************************************** + * drivers/mtd/mtd_nandwrapper.c + * This deals with the wrapper over the upper half of the driver, to enable + * logging for debugging, and essentially passes the parameters right to the + * actual upper half of the NAND Flash device driver without changing them. + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define NAND_WRAPPER_DEBUG_1 1 +#define NAND_WRAPPER_DEBUG_2 5 +#define NAND_WRAPPER_DEBUG_3 10 + +#if CONFIG_MTD_NAND_WRAPPER_DEBUG_LEVEL == 1 +#define NAND_WRAPPER_DEBUG_LEVEL NAND_WRAPPER_DEBUG_1 +#elif CONFIG_MTD_NAND_WRAPPER_DEBUG_LEVEL == 2 +#define NAND_WRAPPER_DEBUG_LEVEL NAND_WRAPPER_DEBUG_2 +#elif CONFIG_MTD_NAND_WRAPPER_DEBUG_LEVEL == 3 +#define NAND_WRAPPER_DEBUG_LEVEL NAND_WRAPPER_DEBUG_3 +#endif /* CONFIG_MTD_NAND_WRAPPER_DEBUG_LEVEL */ + +#define NAND_WRAPPER_LOG(str, ...) \ + { \ + if(nand_wrapper_ins_i % NAND_WRAPPER_DEBUG_LEVEL == 0) \ + { \ + syslog(LOG_DEBUG, "nand_wrapper: " str, __VA_ARGS__); \ + } \ + } + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static uint64_t nand_wrapper_ins_i = 0; /* Instruction counter */ +static mutex_t nand_wrapper_dev_mut; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * External Functions + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nand_wrapper_erase + * + * Description: + * Wrapper for NAND MTD erase method. + * + * Input Parameters: + * dev: NAND MTD Device (with an actual type of `nand_wrapper_dev_s` + * startblock: Block number (0-indexing) to start erasing + * nblocks: Number of blocks to erase + * + * Returned Value: + * 0: Successful + * < 0: Error + * + * Assumptions/Limitations: + * This assumes `dev` is specifically of type `struct nand_wrapper_dev_s *` + * + ****************************************************************************/ + +int nand_wrapper_erase(FAR struct mtd_dev_s *dev, off_t startblock, + size_t nblocks) +{ + int ret; + FAR struct nand_wrapper_dev_s *nand_dev; + + nand_dev = (struct nand_wrapper_dev_s *)dev; + + nxmutex_lock(&nand_wrapper_dev_mut); + nand_wrapper_ins_i++; + NAND_WRAPPER_LOG("[UPPER %lu | %s] Startblock: %d, N Blocks: %ld\n", + nand_wrapper_ins_i, "erase", startblock, nblocks); + DEBUGASSERT(nand_dev && nand_dev->under.mtd.erase); + + ret = nand_dev->under.mtd.erase(dev, startblock, nblocks); + + if (ret >= 0) + { + NAND_WRAPPER_LOG("[UPPER %lu | %s] Done\n", + nand_wrapper_ins_i, "erase"); + } + else + { + NAND_WRAPPER_LOG("[UPPER %lu | %s] Failed: %d!\n", + nand_wrapper_ins_i, "erase", ret); + } + + nxmutex_unlock(&nand_wrapper_dev_mut); + + return ret; +} + +/**************************************************************************** + * Name: nand_wrapper_bread + * + * Description: + * Wrapper for NAND MTD bread method. + * + * Input Parameters: + * dev: MTD Device + * startpage: Page number (0-indexing) to start reading + * npages: Number of pages to read. + * buffer: Preallocated memory where the data will be copied to + * + * Returned Value: + * 0: Successful + * < 0: Error + * + * Assumptions/Limitations: + * This assumes `dev` is specifically of type `struct nand_wrapper_dev_s *` + * + ****************************************************************************/ + +ssize_t nand_wrapper_bread(FAR struct mtd_dev_s *dev, off_t startpage, + size_t npages, FAR uint8_t *buffer) +{ + int ret; + FAR struct nand_wrapper_dev_s *nand_dev; + + nand_dev = (struct nand_wrapper_dev_s *)dev; + + nxmutex_lock(&nand_wrapper_dev_mut); + nand_wrapper_ins_i++; + NAND_WRAPPER_LOG("[UPPER %lu | %s] " + "Startblock: %d, N Pages: %ld, Buffer: %p\n", + nand_wrapper_ins_i, "bread", startpage, npages, buffer); + DEBUGASSERT(nand_dev && nand_dev->under.mtd.bread); + + ret = nand_dev->under.mtd.bread(dev, startpage, npages, buffer); + + if (ret >= 0) + { + NAND_WRAPPER_LOG("[UPPER %lu | %s] Done\n", + nand_wrapper_ins_i, "bread"); + } + else + { + NAND_WRAPPER_LOG("[UPPER %lu | %s] Failed: %d!\n", + nand_wrapper_ins_i, "bread", ret); + } + + nxmutex_unlock(&nand_wrapper_dev_mut); + + return ret; +} + +/**************************************************************************** + * Name: nand_wrapper_bwrite + * + * Description: + * Wrapper for NAND MTD bwrite method. + * + * Input Parameters: + * dev: MTD Device + * startpage: Page number (0-indexing) to start reading + * npages: Number of pages to read. + * buffer: Data which will be written to the device + * + * Returned Value: + * 0: Successful + * < 0: Error + * + * Assumptions/Limitations: + * This assumes the length of `buffer` would be the same as the size of + * `npages * block_size`. This also assumes `dev` is specifically of + * type `struct nand_wrapper_dev_s *` + * + ****************************************************************************/ + +ssize_t nand_wrapper_bwrite(FAR struct mtd_dev_s *dev, off_t startpage, + size_t npages, FAR const uint8_t *buffer) +{ + int ret; + FAR struct nand_wrapper_dev_s *nand_dev; + + nand_dev = (struct nand_wrapper_dev_s *)dev; + + nxmutex_lock(&nand_wrapper_dev_mut); + nand_wrapper_ins_i++; + NAND_WRAPPER_LOG("[UPPER %lu | %s] " + "Startblock: %d, N Pages: %ld, Buffer: %p \n", + nand_wrapper_ins_i, "bwrite", startpage, npages, buffer); + DEBUGASSERT(nand_dev && nand_dev->under.mtd.bwrite); + + ret = nand_dev->under.mtd.bwrite(dev, startpage, npages, buffer); + + if (ret >= 0) + { + NAND_WRAPPER_LOG("[UPPER %lu | %s] Done\n", + nand_wrapper_ins_i, "bwrite"); + } + else + { + NAND_WRAPPER_LOG("[UPPER %lu | %s] Failed: %d!\n", + nand_wrapper_ins_i, "bwrite", ret); + } + + nxmutex_unlock(&nand_wrapper_dev_mut); + + return ret; +} + +/**************************************************************************** + * Name: nand_wrapper_ioctl + * + * Description: + * Wrapper for NAND MTD ioctl method. + * + * Input Parameters: + * dev: MTD Device + * cmd: Command for IOCTL + * arg: Any argument required by command + * + * Returned Value: + * 0: Successful + * < 0: Error + * + * Assumptions/Limitations: + * This assumes `dev` is specifically of type `struct nand_wrapper_dev_s *` + * + ****************************************************************************/ + +int nand_wrapper_ioctl(FAR struct mtd_dev_s *dev, int cmd, + unsigned long arg) +{ + int ret; + FAR struct nand_wrapper_dev_s *nand_dev; + + nand_dev = (struct nand_wrapper_dev_s *)dev; + + nxmutex_lock(&nand_wrapper_dev_mut); + nand_wrapper_ins_i++; + NAND_WRAPPER_LOG("[UPPER %lu | %s] Command: %d, Arg : %ld\n", + nand_wrapper_ins_i, "ioctl", cmd, arg); + DEBUGASSERT(nand_dev && nand_dev->under.mtd.ioctl); + + ret = nand_dev->under.mtd.ioctl(dev, cmd, arg); + + if (ret >= 0) + { + NAND_WRAPPER_LOG("[UPPER %lu | %s] Done\n", + nand_wrapper_ins_i, "ioctl"); + } + else + { + NAND_WRAPPER_LOG("[UPPER %lu | %s] Failed: %d!\n", + nand_wrapper_ins_i, "ioctl", ret); + } + + nxmutex_unlock(&nand_wrapper_dev_mut); + + return ret; +} + +/**************************************************************************** + * Name: nand_wrapper_isbad + * + * Description: + * Wrapper for NAND MTD isbad method. + * + * Input Parameters: + * dev: MTD Device + * block: Block number (0-indexing) to check if it is bad + * + * Returned Value: + * 0: Successful + * < 0: Error + * + * Assumptions/Limitations: + * This assumes `dev` is specifically of type `struct nand_wrapper_dev_s *` + * + ****************************************************************************/ + +int nand_wrapper_isbad(FAR struct mtd_dev_s *dev, off_t block) +{ + int ret; + FAR struct nand_wrapper_dev_s *nand_dev; + + nand_dev = (struct nand_wrapper_dev_s *)dev; + + nxmutex_lock(&nand_wrapper_dev_mut); + nand_wrapper_ins_i++; + NAND_WRAPPER_LOG("[UPPER %lu | %s] Blocks: %d\n", + nand_wrapper_ins_i, "isbad", block); + DEBUGASSERT(nand_dev && nand_dev->under.mtd.isbad); + + ret = nand_dev->under.mtd.isbad(dev, block); + + if (ret >= 0) + { + NAND_WRAPPER_LOG("[UPPER %lu | %s] Done\n", + nand_wrapper_ins_i, "isbad"); + } + else + { + NAND_WRAPPER_LOG("[UPPER %lu | %s] Failed: %d!\n", + nand_wrapper_ins_i, "isbad", ret); + } + + nxmutex_unlock(&nand_wrapper_dev_mut); + + return ret; +} + +/**************************************************************************** + * Name: nand_wrapper_markbad + * + * Description: + * Wrapper for NAND MTD markbad method. + * + * Input Parameters: + * dev: MTD Device + * block: Block number (0-indexing) to mark it as bad + * + * Returned Value: + * 0: Successful + * < 0: Error + * + * Assumptions/Limitations: + * This assumes `dev` is specifically of type `struct nand_wrapper_dev_s *` + * + ****************************************************************************/ + +int nand_wrapper_markbad(FAR struct mtd_dev_s *dev, off_t block) +{ + int ret; + FAR struct nand_wrapper_dev_s *nand_dev; + + nand_dev = (struct nand_wrapper_dev_s *)dev; + + nxmutex_lock(&nand_wrapper_dev_mut); + nand_wrapper_ins_i++; + NAND_WRAPPER_LOG("[UPPER %lu | %s] Blocks: %d\n", + nand_wrapper_ins_i, "markbad", block); + DEBUGASSERT(nand_dev && nand_dev->under.mtd.markbad); + + ret = nand_dev->under.mtd.markbad(dev, block); + + if (ret >= 0) + { + NAND_WRAPPER_LOG("[UPPER %lu | %s] Done\n", + nand_wrapper_ins_i, "markbad"); + } + else + { + NAND_WRAPPER_LOG("[UPPER %lu | %s] Failed: %d!\n", + nand_wrapper_ins_i, "markbad", ret); + } + + nxmutex_unlock(&nand_wrapper_dev_mut); + + return ret; +} + +/**************************************************************************** + * Name: nand_wrapper_initialize + * + * Description: + * Initializes wrapper. + * + * Returned Value: + * 0: Successful + * < 0: Error + * + ****************************************************************************/ + +void nand_wrapper_initialize(void) +{ + nxmutex_init(&nand_wrapper_dev_mut); +} diff --git a/include/nuttx/compiler.h b/include/nuttx/compiler.h index 8a6b5ec615b..91cc8ee6def 100644 --- a/include/nuttx/compiler.h +++ b/include/nuttx/compiler.h @@ -207,8 +207,8 @@ /* Branch prediction */ -# define predict_true(x) __builtin_expect(!!(x), 1) -# define predict_false(x) __builtin_expect((x), 0) +# define predict_true(x) __builtin_expect(!!(x), 1) +# define predict_false(x) __builtin_expect(!!(x), 0) /* Code locate */ diff --git a/include/nuttx/mtd/nand.h b/include/nuttx/mtd/nand.h index 08ad677dc9e..4be864ee1e1 100644 --- a/include/nuttx/mtd/nand.h +++ b/include/nuttx/mtd/nand.h @@ -98,7 +98,7 @@ extern "C" * * Returned Value: * A non-NULL MTD driver instance is returned on success. NULL is - * returned on any failaure. + * returned on any failure. * ****************************************************************************/ diff --git a/include/nuttx/mtd/nand_ram.h b/include/nuttx/mtd/nand_ram.h new file mode 100644 index 00000000000..5229882e42b --- /dev/null +++ b/include/nuttx/mtd/nand_ram.h @@ -0,0 +1,101 @@ +/**************************************************************************** + * include/nuttx/mtd/nand_ram.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ +#ifndef __INCLUDE_NUTTX_MTD_NAND_RAM_H +#define __INCLUDE_NUTTX_MTD_NAND_RAM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define NAND_RAM_DEBUG CONFIG_MTD_NAND_RAM_DEBUG + +#define NAND_RAM_B(x) (x) +#define NAND_RAM_KB(x) (NAND_RAM_B(x) << 10) +#define NAND_RAM_MB(x) (NAND_RAM_KB(x) << 10) + +#define NAND_RAM_SIZE NAND_RAM_MB(CONFIG_MTD_NAND_RAM_SIZE) + +#define NAND_RAM_LOG_PAGES_PER_BLOCK ((uint32_t) 7) +#define NAND_RAM_PAGE_SIZE ((uint32_t) (1 << 9)) /* 512 B */ +#define NAND_RAM_SPARE_SIZE ((uint32_t) (1 << 4)) /* 16 B */ +#define NAND_RAM_N_PAGES ((uint32_t) NAND_RAM_SIZE / NAND_RAM_PAGE_SIZE) +#define NAND_RAM_TOTAL_PAGE_SIZE ((uint32_t) (NAND_RAM_PAGE_SIZE + NAND_RAM_SPARE_SIZE)) +#define NAND_RAM_PAGES_PER_BLOCK ((uint32_t) (NAND_RAM_BLOCK_SIZE / NAND_RAM_PAGE_SIZE)) +#define NAND_RAM_N_BLOCKS ((uint32_t) (NAND_RAM_N_PAGES / NAND_RAM_PAGES_PER_BLOCK)) +#define NAND_RAM_BLOCK_SIZE ((uint32_t) ((1 << NAND_RAM_LOG_PAGES_PER_BLOCK) * NAND_RAM_PAGE_SIZE)) + +#define NAND_RAM_PAGE_WRITTEN 0 +#define NAND_RAM_PAGE_FREE 1 + +#define NAND_RAM_BLOCK_GOOD 0xff + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +EXTERN FAR struct mtd_dev_s *g_nand_ram_mtd_wrapper; +EXTERN FAR struct mtd_dev_s *g_nand_ram_mtd_under; +EXTERN FAR struct nand_raw_s *g_nand_mtd_raw; + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int nand_ram_eraseblock(FAR struct nand_raw_s *raw, off_t block); +int nand_ram_rawread(FAR struct nand_raw_s *raw, off_t block, + unsigned int page, FAR void *data, FAR void *spare); +int nand_ram_rawwrite(FAR struct nand_raw_s *raw, off_t block, + unsigned int page, FAR const void *data, + FAR const void *spare); +FAR struct mtd_dev_s *nand_ram_initialize(struct nand_raw_s *raw); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __TESTING_NAND_RAM_NAND_RAM_H */ \ No newline at end of file diff --git a/include/nuttx/mtd/nand_wrapper.h b/include/nuttx/mtd/nand_wrapper.h new file mode 100644 index 00000000000..28304d107be --- /dev/null +++ b/include/nuttx/mtd/nand_wrapper.h @@ -0,0 +1,84 @@ +/**************************************************************************** + * include/nuttx/mtd/nand_wrapper.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ +#ifndef __INCLUDE_NUTTX_MTD_NAND_WRAPPER_H +#define __INCLUDE_NUTTX_MTD_NAND_WRAPPER_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct nand_wrapper_dev_s +{ + struct nand_dev_s wrapper; /* Wrapper device */ + struct nand_dev_s under; /* Underlying actuall upper half device */ +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int nand_wrapper_erase(FAR struct mtd_dev_s *dev, off_t startblock, + size_t nblocks); +ssize_t nand_wrapper_bread(FAR struct mtd_dev_s *dev, off_t startpage, + size_t npages, FAR uint8_t *buffer); +ssize_t nand_wrapper_bwrite(FAR struct mtd_dev_s *dev, off_t startpage, + size_t npages, FAR const uint8_t *buffer); +int nand_wrapper_ioctl(FAR struct mtd_dev_s *dev, int cmd, + unsigned long arg); +int nand_wrapper_isbad(FAR struct mtd_dev_s *dev, off_t block); +int nand_wrapper_markbad(FAR struct mtd_dev_s *dev, off_t block); +void nand_wrapper_initialize(void); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __TESTING_NAND_RAM_NAND_RAM_H */ \ No newline at end of file