diff --git a/ChangeLog b/ChangeLog index 4a414666d3..7f9e246ece 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6080,4 +6080,5 @@ * drivers/mtd/mtd_rawnand.c and include/nuttx/mtd/nand_raw.h: More NAND support (2013-11-17). * drivers/mtd/mtd_nandscheme.c: More NAND support (2013-11-17). + * include/nuttx/mtd/nand_ecc.h: More NAND (2013-11-17). diff --git a/drivers/mtd/mtd_nand.c b/drivers/mtd/mtd_nand.c index ada091b86b..d0f8c8c202 100755 --- a/drivers/mtd/mtd_nand.c +++ b/drivers/mtd/mtd_nand.c @@ -49,7 +49,7 @@ #include #include #include -#include +#include #include #include #include @@ -62,14 +62,19 @@ #include #include #include +#include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* Success Values returned by the nand_checkblock function */ -#define BADBLOCK 255 -#define GOODBLOCK 254 +#define GOODBLOCK 254 +#define BADBLOCK 255 + +/* Bad block marker */ + +#define NAND_BLOCKSTATUS_BAD 0xba /**************************************************************************** * Private Types @@ -78,6 +83,11 @@ /**************************************************************************** * Private Function Prototypes ****************************************************************************/ +/* NAND locking */ + +static int nand_lock(FAR struct nand_dev_s *nand); +#define nand_unlock(n) sem_post(&(n)->exclsem) + /* Sparing logic */ #ifdef CONFIG_MTD_NAND_BLOCKCHECK @@ -88,6 +98,9 @@ static int nand_devscan(FAR struct nand_dev_s *nand); # define nand_devscan(n) #endif +static int nand_eraseblock(FAR struct nand_dev_s *nand, + off_t block, bool scrub); + /* MTD driver methods */ static int nand_erase(struct mtd_dev_s *dev, off_t startblock, @@ -107,6 +120,39 @@ static int nand_ioctl(struct mtd_dev_s *dev, int cmd, * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: nand_lock + * + * Description: + * Get exclusive access to the nand. + * + * Input Parameters: + * nand - Pointer to a struct nand_dev_s instance. + * block - Number of block to check. + * + * Returned Value: + * OK on success; a negater errno value on failure. + * + ****************************************************************************/ + +static int nand_lock(FAR struct nand_dev_s *nand) +{ + int errcode; + int ret; + + ret = sem_wait(&nand->exclsem); + if (ret < 0) + { + errcode = errno; + DEBUGASSERT(errcode != OK); + + fdbg("sem_wait failed: %d\n", errcode); + return -errcode; + } + + return OK; +} + /**************************************************************************** * Name: nand_checkblock * @@ -128,15 +174,15 @@ static int nand_ioctl(struct mtd_dev_s *dev, int cmd, static int nand_checkblock(FAR struct nand_dev_s *nand, off_t block) { uint8_t spare[CONFIG_MTD_NAND_MAXPAGESPARESIZE]; - const struct nand_raw_s *raw; - const struct nand_model_s *model; - const struct nand_scheme_s *scheme; + FAR const struct nand_scheme_s *scheme; + FAR struct nand_raw_s *raw; + FAR struct nand_model_s *model; uint8_t marker; int ret; DEBUGASSERT(nand && nand->raw); - /* Retrieve model scheme */ + /* Retrieve the model and scheme */ raw = nand->raw; model = &raw->model; @@ -193,9 +239,9 @@ static int nand_checkblock(FAR struct nand_dev_s *nand, off_t block) #ifdef CONFIG_MTD_NAND_BLOCKCHECK static int nand_devscan(FAR struct nand_dev_s *nand) { - FAR const struct nand_raw_s *raw; - FAR const struct nand_model_s *model; - off_t numBlocks; + FAR struct nand_raw_s *raw; + FAR struct nand_model_s *model; + off_t nblocks; off_t block; int ret; @@ -203,10 +249,10 @@ static int nand_devscan(FAR struct nand_dev_s *nand) /* Retrieve model information */ - raw = nand->raw; - model = &raw->model; + raw = nand->raw; + model = &raw->model; - numBlocks = nandmodel_getdevblocksize(model); + nblocks = nandmodel_getdevblocks(model); /* Initialize block statuses */ @@ -214,7 +260,7 @@ static int nand_devscan(FAR struct nand_dev_s *nand) /* Retrieve block status from their first page spare area */ - for (block = 0; block < numBlocks; block++) + for (block = 0; block < nblocks; block++) { /* Read spare of first page */ @@ -237,6 +283,150 @@ static int nand_devscan(FAR struct nand_dev_s *nand) } #endif /* CONFIG_MTD_NAND_BLOCKCHECK */ +/**************************************************************************** + * Name: nand_eraseblock + * + * Description: + * Erase one block. + * + * Input Parameters: + * nand Pointer to a struct nand_dev_s instance + * block Number of block to erase + * scrub True: Erase bad blocks + * + * Returned Value + * OK on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int nand_eraseblock(FAR struct nand_dev_s *nand, off_t block, + bool scrub) +{ + uint8_t spare[CONFIG_MTD_NAND_MAXPAGESPARESIZE]; + FAR const struct nand_scheme_s *scheme; + FAR struct nand_model_s *model; + FAR struct nand_raw_s *raw; + int ret; + + /* fvdbg("Block %d\n", block); */ + DEBUGASSERT(nand && nand->raw); + +#ifdef CONFIG_MTD_NAND_BLOCKCHECK + if (scrub) + { + /* Check block status */ + + if (nand_checkblock(nand, block) != GOODBLOCK) + { + fvdbg("Block is BAD\n"); + return -EAGAIN; + } + } +#endif + + /* Erase block */ + + ret = NAND_ERASEBLOCK(nand->raw, block); + if (ret < 0) + { + int tmp; + + fdbg("ERROR: Cannot erase block %ld\n", (long)block); + + /* Retrieve the model and scheme */ + + raw = nand->raw; + model = &raw->model; + scheme = nandmodel_getscheme(model); + + /* Try to mark the block as BAD */ + + memset(spare, 0xff, CONFIG_MTD_NAND_MAXPAGESPARESIZE); + nandscheme_writebadblockmarker(scheme, spare, NAND_BLOCKSTATUS_BAD); + tmp = NAND_WRITEPAGE(nand->raw, block, 0, 0, spare); + if (tmp < 0) + { + fdbg("ERROR: Failed bo marke block %ld as BAD\n", (long)block); + } + } + + return ret; +} + +/**************************************************************************** + * Name: nandecc_readpage + * + * Description: + * Reads the data area (only) of a page of a NAND FLASH into the + * provided buffer. + * + * Input parameters: + * nand - Upper-half, NAND FLASH interface + * block - Number of the block where the page to read resides. + * page - Number of the page to read inside the given block. + * buf - Buffer where the data area will be stored. + * + * Returned value. + * OK is returned in success; a negated errno value is returned on failure. + * + ****************************************************************************/ + +static int nand_readpage(FAR struct nand_dev_s *nand, off_t block, + unsigned int page, FAR uint8_t *buf) +{ +#ifdef CONFIG_MTD_NAND_BLOCKCHECK + /* Check that the block is not BAD if data is requested */ + + if (nand_checkblock(nand, block) != GOODBLOCK) + { + fdbg("ERROR: Block is BAD\n"); + return -EAGAIN; + } + + /* Read data with ECC verification */ + + return nandecc_readpage(nand, block, page, buf, NULL); +#else + return NAND_READPAGE(nand->raw, block, page, buf, NULL); +#endif +} + +/**************************************************************************** + * Name: nand_writepage + * + * Description: + * Writes the data area (only) of a page into NAND FLASH from the + * provided buffer. + * + * Input parameters: + * nand - Upper-half, NAND FLASH interface + * block - Number of the block where the page to read resides. + * page - Number of the page to read inside the given block. + * buf - Buffer where the data area will be stored. + * + * Returned value. + * OK is returned in success; a negated errno value is returned on failure. + * + ****************************************************************************/ + +static int nand_writepage(FAR struct nand_dev_s *nand, off_t block, + unsigned int page, FAR const void *buf) +{ +#ifdef CONFIG_MTD_NAND_BLOCKCHECK + /* Check that the block is good */ + + if (nand_checkblock(nand, block) != GOODBLOCK) + { + fdbg("ERROR: Block is BAD\n"); + return -EAGAIN; + } +#endif + + /* Write data with ECC calculation */ + + return nandecc_writepage(nand, block, page, buf, NULL); +} + /**************************************************************************** * Name: nand_erase * @@ -248,17 +438,33 @@ static int nand_devscan(FAR struct nand_dev_s *nand) static int nand_erase(struct mtd_dev_s *dev, off_t startblock, size_t nblocks) { - struct nand_raw_s *nand = (struct nand_raw_s *)dev; + FAR struct nand_dev_s *nand = (FAR struct nand_dev_s *)dev; + size_t blocksleft = nblocks; + int ret; - /* The interface definition assumes that all erase blocks are the same size. - * If that is not true for this particular device, then transform the - * start block and nblocks as necessary. - */ -#warning Missing logic + fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks); - /* Erase the specified blocks and return status (OK or a negated errno) */ + /* Lock access to the NAND until we complete the erase */ - return OK; + nand_lock(nand); + while (blocksleft-- > 0) + { + /* Erase each sector */ + + ret = nand_eraseblock(nand, startblock, false); + if (ret < 0) + { + fdbg("nand_eraseblock failed on block %ld: %d\n", + (long)startblock, ret); + nand_unlock(nand); + return ret; + } + + startblock++; + } + + nand_unlock(nand); + return (int)nblocks; } /**************************************************************************** @@ -269,22 +475,87 @@ static int nand_erase(struct mtd_dev_s *dev, off_t startblock, * ****************************************************************************/ -static ssize_t nand_bread(struct mtd_dev_s *dev, off_t startblock, - size_t nblocks, uint8_t *buf) +static ssize_t nand_bread(struct mtd_dev_s *dev, off_t startpage, + size_t npages, FAR uint8_t *buf) { - struct nand_raw_s *nand = (struct nand_raw_s *)dev; + FAR struct nand_dev_s *nand = (FAR struct nand_dev_s *)dev; + FAR struct nand_raw_s *raw; + FAR struct nand_model_s *model; + unsigned int pagesperblock; + unsigned int page; + uint16_t pagesize; + size_t remaining; + off_t maxblock; + off_t block; + int ret; - /* The interface definition assumes that all read/write blocks are the same size. - * If that is not true for this particular device, then transform the - * start block and nblocks as necessary. + fvdbg("startpage: %08lx npages: %d\n", (long)startpage, (int)npages); + DEBUGASSERT(nand && nand->raw); + + /* Retrieve the model */ + + raw = nand->raw; + model = &raw->model; + + /* Get the number of pages in one block, the size of one page, and + * the number of blocks on the device. */ - /* Read the specified blocks into the provided user buffer and return status - * (The positive, number of blocks actually read or a negated errno). - */ -#warning Missing logic + pagesperblock = nandmodel_pagesperblock(model); + pagesize = nandmodel_getpagesize(model); + maxblock = nandmodel_getdevblocks(model); - return 0; + /* Get the block and page offset associated with the startpage */ + + block = startpage / pagesperblock; + page = pagesperblock % pagesperblock; + + /* Lock access to the NAND until we complete the read */ + + nand_lock(nand); + + /* Then read every page from NAND */ + + for (remaining = npages; remaining > 0; remaining--) + { + /* Read the next page from NAND */ + + ret = nand_readpage(nand, block, page, buf); + if (ret < 0) + { + fdbg("ERROR: nand_readpage failed block=%ld page=%d: %d\n", + (long)block, page, ret); + goto errout_with_lock; + } + + /* Increment the page number. If we exceed the number of + * pages per block, then reset the page number and bump up + * the block number. + */ + + if (++page >= pagesperblock) + { + page = 0; + if (++block > maxblock) + { + fdbg("ERROR: Read beyond the end of FLASH, block=%d\n", + block); + ret = -ESPIPE; + goto errout_with_lock; + } + } + + /* Increment the buffer point by the size of one page */ + + buf += pagesize; + } + + nand_unlock(nand); + return npages; + +errout_with_lock: + nand_unlock(nand); + return ret; } /**************************************************************************** @@ -295,22 +566,87 @@ static ssize_t nand_bread(struct mtd_dev_s *dev, off_t startblock, * ****************************************************************************/ -static ssize_t nand_bwrite(struct mtd_dev_s *dev, off_t startblock, - size_t nblocks, const uint8_t *buf) +static ssize_t nand_bwrite(struct mtd_dev_s *dev, off_t startpage, + size_t npages, const uint8_t *buf) { - struct nand_raw_s *nand = (struct nand_raw_s *)dev; + FAR struct nand_dev_s *nand = (FAR struct nand_dev_s *)dev; + FAR struct nand_raw_s *raw; + FAR struct nand_model_s *model; + unsigned int pagesperblock; + unsigned int page; + uint16_t pagesize; + size_t remaining; + off_t maxblock; + off_t block; + int ret; - /* The interface definition assumes that all read/write blocks are the same size. - * If that is not true for this particular device, then transform the - * start block and nblocks as necessary. + fvdbg("startpage: %08lx npages: %d\n", (long)startpage, (int)npages); + DEBUGASSERT(nand && nand->raw); + + /* Retrieve the model */ + + raw = nand->raw; + model = &raw->model; + + /* Get the number of pages in one block, the size of one page, and + * the number of blocks on the device. */ - /* Write the specified blocks from the provided user buffer and return status - * (The positive, number of blocks actually written or a negated errno) - */ -#warning Missing logic + pagesperblock = nandmodel_pagesperblock(model); + pagesize = nandmodel_getpagesize(model); + maxblock = nandmodel_getdevblocks(model); - return 0; + /* Get the block and page offset associated with the startpage */ + + block = startpage / pagesperblock; + page = pagesperblock % pagesperblock; + + /* Lock access to the NAND until we complete the write */ + + nand_lock(nand); + + /* Then write every page into NAND */ + + for (remaining = npages; remaining > 0; remaining--) + { + /* Write the next page into NAND */ + + ret = nand_writepage(nand, block, page, buf); + if (ret < 0) + { + fdbg("ERROR: nand_readpage failed block=%ld page=%d: %d\n", + (long)block, page, ret); + goto errout_with_lock; + } + + /* Increment the page number. If we exceed the number of + * pages per block, then reset the page number and bump up + * the block number. + */ + + if (++page >= pagesperblock) + { + page = 0; + if (++block > maxblock) + { + fdbg("ERROR: Write beyond the end of FLASH, block=%d\n", + block); + ret = -ESPIPE; + goto errout_with_lock; + } + } + + /* Increment the buffer point by the size of one page */ + + buf += pagesize; + } + + nand_unlock(nand); + return npages; + +errout_with_lock: + nand_unlock(nand); + return ret; } /**************************************************************************** @@ -319,7 +655,7 @@ static ssize_t nand_bwrite(struct mtd_dev_s *dev, off_t startblock, static int nand_ioctl(struct mtd_dev_s *dev, int cmd, unsigned long arg) { - struct nand_raw_s *nand = (struct nand_raw_s *)dev; + FAR struct nand_dev_s *nand = (FAR struct nand_dev_s *)dev; int ret = -EINVAL; /* Assume good command with bad parameters */ switch (cmd) @@ -496,6 +832,8 @@ FAR struct mtd_dev_s *nand_initialize(FAR struct nand_raw_s *raw) nand->mtd.ioctl = nand_ioctl; nand->raw = raw; + sem_init(&nand->exclsem, 0, 1); + /* Scan the device for bad blocks */ ret = nand_devscan(nand); diff --git a/include/nuttx/mtd/nand.h b/include/nuttx/mtd/nand.h index 6ac8f14458..0854b34bb1 100644 --- a/include/nuttx/mtd/nand.h +++ b/include/nuttx/mtd/nand.h @@ -50,6 +50,7 @@ #include #include +#include #include #include @@ -71,6 +72,7 @@ struct nand_dev_s { struct mtd_dev_s mtd; /* Externally visible part of the driver */ FAR struct nand_raw_s *raw; /* Retained reference to the lower half */ + sem_t exclsem; /* For exclusive access to the NAND flas */ }; /**************************************************************************** diff --git a/include/nuttx/mtd/nand_ecc.h b/include/nuttx/mtd/nand_ecc.h new file mode 100644 index 0000000000..a5ce0ca869 --- /dev/null +++ b/include/nuttx/mtd/nand_ecc.h @@ -0,0 +1,130 @@ +/**************************************************************************** + * include/nuttx/mtd/nand_ecc.h + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * This logic was based largely on Atmel sample code with modifications for + * better integration with NuttX. The Atmel sample code has a BSD + * compatibile license that requires this copyright notice: + * + * Copyright (c) 2012, Atmel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the names NuttX nor Atmel nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_MTD_ECC_H +#define __INCLUDE_NUTTX_MTD_ECC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include + +/**************************************************************************** + * Pre-Processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: nandecc_readpage + * + * Description: + * Reads the data and/or the spare areas of a page of a NAND FLASH into the + * provided buffers. + * + * Input parameters: + * nand - Upper-half, NAND FLASH interface + * block - Number of the block where the page to read resides. + * page - Number of the page to read inside the given block. + * data - Buffer where the data area will be stored. + * spare - Buffer where the spare area will be stored. + * + * Returned value. + * OK is returned in success; a negated errno value is returned on failure. + * + ****************************************************************************/ + +int nandecc_readpage(FAR struct nand_dev_s *nand, off_t block, + unsigned int page, FAR void *data, FAR void *spare); + +/**************************************************************************** + * Name: nandecc_writepage + * + * Description: + * Writes the data and/or the spare area of a page on a NAND FLASH chip. + * + * Input parameters: + * nand - Upper-half, NAND FLASH interface + * block - Number of the block where the page to write resides. + * page - Number of the page to write inside the given block. + * data - Buffer containing the data to be writting + * spare - Buffer conatining the spare data to be written. + * + * Returned value. + * OK is returned in success; a negated errno value is returned on failure. + * + ****************************************************************************/ + +int nandecc_writepage(FAR struct nand_dev_s *nand, off_t block, + unsigned int page, FAR const void *data, + FAR const void *spare); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __INCLUDE_NUTTX_MTD_ECC_H */ diff --git a/include/nuttx/mtd/nand_model.h b/include/nuttx/mtd/nand_model.h index cca3414a15..71a3037967 100644 --- a/include/nuttx/mtd/nand_model.h +++ b/include/nuttx/mtd/nand_model.h @@ -193,7 +193,7 @@ int nandmodel_translate(FAR const struct nand_model_s *model, off_t address, #define nandmodel_getdevid(m) ((m)->devid) /**************************************************************************** - * Name: nandmodel_getdevblocksize + * Name: nandmodel_getdevblocks * * Description: * Returns the number of blocks in the entire device. @@ -206,7 +206,7 @@ int nandmodel_translate(FAR const struct nand_model_s *model, off_t address, * ****************************************************************************/ -#define nandmodel_getdevblocksize(m) \ +#define nandmodel_getdevblocks(m) \ ((off_t)((m)->devsize << 10) / (m)->blocksize) /**************************************************************************** @@ -244,23 +244,7 @@ int nandmodel_translate(FAR const struct nand_model_s *model, off_t address, #define nandmodel_getdevmbsize(m) ((uint32_t)((m)->devsize)) /**************************************************************************** - * Name: nandmodel_getblocksize - * - * Description: - * Returns the number of pages in one single block of a device. - * - * Input Parameters: - * model Pointer to a nand_model_s instance. - * - * Returned Values: - * - * - ****************************************************************************/ - -unsigned int nandmodel_getblocksize(FAR const struct nand_model_s *model); - -/**************************************************************************** - * Name: nandmodel_getpageblocksize + * Name: nandmodel_pagesperblock * * Description: * Returns the number of pages in one single block of a device. @@ -273,7 +257,7 @@ unsigned int nandmodel_getblocksize(FAR const struct nand_model_s *model); * ****************************************************************************/ -#define nandmodel_getpageblocksize(m) \ +#define nandmodel_pagesperblock(m) \ ((uint32_t)((m)->blocksize << 10) / model->pagesize) /**************************************************************************** @@ -291,7 +275,7 @@ unsigned int nandmodel_getblocksize(FAR const struct nand_model_s *model); ****************************************************************************/ #define nandmodel_getdevpagesize(m) \ - ((uint32_t)nandmodel_getdevblocksize(m) * nandmodel_getpageblocksize(m)) + ((uint32_t)nandmodel_getdevblocks(m) * nandmodel_pagesperblock(m)) /**************************************************************************** * Name: nandmodel_getbyteblocksize