nuttx/drivers/mtd/mtd_nand.c

1055 lines
29 KiB
C
Raw Permalink Normal View History

2013-11-16 18:46:35 +01:00
/****************************************************************************
2013-11-16 19:22:43 +01:00
* drivers/mtd/mtd_nand.c
2013-11-16 18:46:35 +01:00
*
* Copyright (c) 2011, 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/mtd/nand_config.h>
#include <inttypes.h>
2013-11-16 18:46:35 +01:00
#include <string.h>
#include <assert.h>
2013-11-17 01:26:07 +01:00
#include <errno.h>
2013-11-16 18:46:35 +01:00
#include <debug.h>
#include <stdio.h>
2013-11-16 18:46:35 +01:00
2013-11-17 01:26:07 +01:00
#include <nuttx/kmalloc.h>
#include <nuttx/fs/ioctl.h>
2013-11-16 18:46:35 +01:00
#include <nuttx/mtd/nand.h>
#include <nuttx/mtd/onfi.h>
#include <nuttx/mtd/nand_scheme.h>
2013-11-17 21:35:57 +01:00
#include <nuttx/mtd/nand_ecc.h>
2013-11-16 18:46:35 +01:00
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
2013-11-17 19:22:09 +01:00
/* Success Values returned by the nand_checkblock function */
2013-11-17 21:35:57 +01:00
#define GOODBLOCK 254
#define BADBLOCK 255
/* Bad block marker */
#define NAND_BLOCKSTATUS_BAD 0xba
2013-11-16 18:46:35 +01:00
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Bad block checking */
2013-11-17 19:22:09 +01:00
static int nand_markblock(FAR struct nand_dev_s *nand, off_t block);
2013-11-17 19:22:09 +01:00
static int nand_checkblock(FAR struct nand_dev_s *nand, off_t block);
#if defined(CONFIG_MTD_NAND_BLOCKCHECK) && defined(CONFIG_DEBUG_INFO) && \
defined(CONFIG_DEBUG_FS)
2013-11-17 19:22:09 +01:00
static int nand_devscan(FAR struct nand_dev_s *nand);
#endif
2013-11-16 18:46:35 +01:00
/* Misc. NAND helpers */
static uint32_t nand_chipid(FAR struct nand_raw_s *raw);
2013-11-17 21:35:57 +01:00
static int nand_eraseblock(FAR struct nand_dev_s *nand,
off_t block, bool scrub);
static int nand_readpage(FAR struct nand_dev_s *nand, off_t block,
unsigned int page, FAR uint8_t *data);
static int nand_writepage(FAR struct nand_dev_s *nand, off_t block,
unsigned int page, FAR const void *data);
2013-11-17 21:35:57 +01:00
2013-11-17 01:26:07 +01:00
/* MTD driver methods */
static int nand_erase(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks);
static ssize_t nand_bread(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, uint8_t *buffer);
static ssize_t nand_bwrite(FAR struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, const uint8_t *buffer);
static int nand_ioctl(FAR struct mtd_dev_s *dev, int cmd,
unsigned long arg);
static int nand_isbad(FAR struct mtd_dev_s *dev, off_t block);
static int nand_markbad(FAR struct mtd_dev_s *dev, off_t block);
2013-11-17 01:26:07 +01:00
2013-11-16 18:46:35 +01:00
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: nand_markblock
*
* Description:
* Mark a block as bad.
*
* Input Parameters:
* nand - Pointer to a struct nand_dev_s instance.
* block - Number of block to mark.
*
* Returned Value:
* Returns negated errno value on any failure.
*
****************************************************************************/
static int nand_markblock(FAR struct nand_dev_s *nand, off_t block)
{
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;
/* 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);
ret = NAND_WRITEPAGE(nand->raw, block, 0, 0, spare);
if (ret < 0)
{
ferr("ERROR: Failed bo marke block %" PRIdOFF " as BAD\n", block);
}
return ret;
}
2013-11-17 19:22:09 +01:00
/****************************************************************************
* Name: nand_checkblock
*
* Description:
* Read and check for a bad block.
*
* Input Parameters:
* nand - Pointer to a struct nand_dev_s instance.
* block - Number of block to check.
*
* Returned Value:
* Returns BADBLOCK if the given block of a nandflash device is bad;
* returns GOODBLOCK if the block is good; or returns negated errno
* value on any failure.
*
****************************************************************************/
static int nand_checkblock(FAR struct nand_dev_s *nand, off_t block)
{
uint8_t spare[CONFIG_MTD_NAND_MAXPAGESPARESIZE];
2013-11-17 21:35:57 +01:00
FAR const struct nand_scheme_s *scheme;
FAR struct nand_raw_s *raw;
FAR struct nand_model_s *model;
2013-11-17 19:22:09 +01:00
uint8_t marker;
int ret;
DEBUGASSERT(nand && nand->raw);
2013-11-17 21:35:57 +01:00
/* Retrieve the model and scheme */
2013-11-17 19:22:09 +01:00
raw = nand->raw;
model = &raw->model;
scheme = nandmodel_getscheme(model);
/* Read spare area of first page of block */
2013-11-19 15:50:12 +01:00
ret = NAND_RAWREAD(raw, block, 0, 0, spare);
2013-11-17 19:22:09 +01:00
if (ret < 0)
{
ferr("ERROR: Failed to read page 0 of block %" PRIdOFF "\n", block);
2013-11-17 19:22:09 +01:00
return ret;
}
nandscheme_readbadblockmarker(scheme, spare, &marker);
if (marker != 0xff)
{
finfo("Page 0 block %" PRIdOFF " marker=%02x\n", block, marker);
2013-11-17 19:22:09 +01:00
return BADBLOCK;
}
/* Read spare area of second page of block */
2013-11-19 15:50:12 +01:00
ret = NAND_RAWREAD(raw, block, 1, 0, spare);
2013-11-17 19:22:09 +01:00
if (ret < 0)
{
ferr("ERROR: Failed to read page 1 of block %" PRIdOFF "\n", block);
2013-11-17 19:22:09 +01:00
return ret;
}
nandscheme_readbadblockmarker(scheme, spare, &marker);
2013-11-19 15:50:12 +01:00
if (marker != 0xff)
2013-11-17 19:22:09 +01:00
{
finfo("Page 1 block %" PRIdOFF " marker=%02x\n", block, marker);
2013-11-17 19:22:09 +01:00
return BADBLOCK;
}
return GOODBLOCK;
}
/****************************************************************************
* Name: nand_devscan
*
* Description:
* Scans the device to retrieve or create block status information.
*
* Currently, this function does nothing but scan the NAND and eat up time.
* This is a goot thing to do if you are debugging NAND, but otherwise,
* just a waste of time. This logic could, however, be integrated into
* some bad block checking logic at sometime in the future.
*
2013-11-17 19:22:09 +01:00
* Input Parameters:
* nand - Pointer to a struct nand_dev_s instance.
*
* Returned Value:
2013-11-25 20:53:58 +01:00
* OK (always)
2013-11-17 19:22:09 +01:00
*
****************************************************************************/
#if defined(CONFIG_MTD_NAND_BLOCKCHECK) && defined(CONFIG_DEBUG_INFO) && \
defined(CONFIG_DEBUG_FS)
2013-11-17 19:22:09 +01:00
static int nand_devscan(FAR struct nand_dev_s *nand)
{
2013-11-17 21:35:57 +01:00
FAR struct nand_raw_s *raw;
FAR struct nand_model_s *model;
off_t nblocks;
2013-11-17 19:22:09 +01:00
off_t block;
#if defined(CONFIG_DEBUG_INFO) && defined(CONFIG_DEBUG_FS)
off_t good = 0;
unsigned int ngood = 0;
2013-11-25 20:53:58 +01:00
#endif
2013-11-17 19:22:09 +01:00
int ret;
DEBUGASSERT(nand && nand->raw);
/* Retrieve model information */
2013-11-17 21:35:57 +01:00
raw = nand->raw;
model = &raw->model;
2013-11-17 19:22:09 +01:00
2013-11-17 21:35:57 +01:00
nblocks = nandmodel_getdevblocks(model);
2013-11-17 19:22:09 +01:00
/* Initialize block statuses */
finfo("Retrieving bad block information. nblocks=%" PRIdOFF "\n", nblocks);
2013-11-17 19:22:09 +01:00
/* Retrieve block status from their first page spare area */
2013-11-17 21:35:57 +01:00
for (block = 0; block < nblocks; block++)
2013-11-17 19:22:09 +01:00
{
/* Read spare of first page */
ret = nand_checkblock(nand, block);
if (ret != GOODBLOCK)
{
#if defined(CONFIG_DEBUG_INFO) && defined(CONFIG_DEBUG_FS)
2013-11-25 20:53:58 +01:00
if (ngood > 0)
{
finfo("Good blocks: %u - %u\n", good, good + ngood);
2013-11-25 20:53:58 +01:00
ngood = 0;
}
2013-11-25 20:53:58 +01:00
#endif
2013-11-17 19:22:09 +01:00
if (ret == BADBLOCK)
{
finfo("Block %u is bad\n", (unsigned int)block);
2013-11-17 19:22:09 +01:00
}
else
{
ferr("ERROR: Cannot retrieve info from block %u: %d\n",
2013-11-17 19:22:09 +01:00
(unsigned int)block, ret);
}
}
#if defined(CONFIG_DEBUG_INFO) && defined(CONFIG_DEBUG_FS)
2013-11-25 20:53:58 +01:00
else
{
if (ngood == 0)
{
good = block;
}
2013-11-25 20:53:58 +01:00
ngood++;
2013-11-25 20:53:58 +01:00
}
#endif
2013-11-17 19:22:09 +01:00
}
#if defined(CONFIG_DEBUG_INFO) && defined(CONFIG_DEBUG_FS)
2013-11-25 20:53:58 +01:00
if (ngood > 0)
{
finfo("Good blocks: %u - %u\n", good, good + ngood);
2013-11-25 20:53:58 +01:00
}
#endif
2013-11-17 19:22:09 +01:00
return OK;
}
#endif /* CONFIG_MTD_NAND_BLOCKCHECK && CONFIG_DEBUG_INFO && CONFIG_DEBUG_FS */
2013-11-17 19:22:09 +01:00
/****************************************************************************
* Name: nand_chipid
*
* Description:
* Reads and returns the identifiers of a NAND FLASH chip
*
* Input Parameters:
* raw - Pointer to a struct nand_raw_s instance.
*
* Returned Value:
* id1|(id2<<8)|(id3<<16)|(id4<<24)
*
****************************************************************************/
static uint32_t nand_chipid(FAR struct nand_raw_s *raw)
{
uint8_t id[5];
DEBUGASSERT(raw);
WRITE_COMMAND8(raw, COMMAND_READID);
WRITE_ADDRESS8(raw, 0);
id[0] = READ_DATA8(raw);
id[1] = READ_DATA8(raw);
id[2] = READ_DATA8(raw);
id[3] = READ_DATA8(raw);
id[4] = READ_DATA8(raw);
finfo("Chip ID: %02x %02x %02x %02x %02x\n",
id[0], id[1], id[2], id[3], id[4]);
return (uint32_t)id[0] |
((uint32_t)id[1] << 8) |
((uint32_t)id[2] << 16) |
((uint32_t)id[3] << 24);
}
2013-11-17 21:35:57 +01:00
/****************************************************************************
* 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:
2013-11-17 21:35:57 +01:00
* OK on success; a negated errno value on failure.
*
****************************************************************************/
static int nand_eraseblock(FAR struct nand_dev_s *nand, off_t block,
bool scrub)
{
int ret;
/* finfo("Block %d\n", block); */
2013-11-17 21:35:57 +01:00
DEBUGASSERT(nand && nand->raw);
#ifdef CONFIG_MTD_NAND_BLOCKCHECK
if (!scrub)
2013-11-17 21:35:57 +01:00
{
/* Check block status */
if (nand_checkblock(nand, block) != GOODBLOCK)
{
finfo("Block is BAD\n");
2013-11-17 21:35:57 +01:00
return -EAGAIN;
}
}
#endif
/* Erase block */
ret = NAND_ERASEBLOCK(nand->raw, block);
if (ret < 0)
{
ferr("ERROR: Cannot erase block %" PRIdOFF "\n", block);
2013-11-17 21:35:57 +01:00
/* Try to mark the block as BAD */
ret = nand_markblock(nand, block);
2013-11-17 21:35:57 +01:00
}
return ret;
}
/****************************************************************************
* Name: nandecc_readpage
*
* Description:
* Reads the data area (only) of a page of a NAND FLASH into the
* provided buffer.
*
* Input Parameters:
2013-11-17 21:35:57 +01:00
* 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.
2013-11-28 19:21:33 +01:00
* data - Buffer where the data area will be stored.
2013-11-17 21:35:57 +01:00
*
* Returned Value:
2013-11-17 21:35:57 +01:00
* 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,
2013-11-28 19:21:33 +01:00
unsigned int page, FAR uint8_t *data)
2013-11-17 21:35:57 +01:00
{
finfo("block=%d page=%d data=%p\n", (int)block, page, data);
2013-11-28 19:21:33 +01:00
2013-11-17 21:35:57 +01:00
#ifdef CONFIG_MTD_NAND_BLOCKCHECK
/* Check that the block is not BAD if data is requested */
if (nand_checkblock(nand, block) != GOODBLOCK)
{
ferr("ERROR: Block is BAD\n");
2013-11-17 21:35:57 +01:00
return -EAGAIN;
}
2013-11-28 19:21:33 +01:00
#endif
2013-11-17 21:35:57 +01:00
#ifdef CONFIG_MTD_NAND_SWECC
/* nandecc_readpage will handle the software ECC case */
2013-11-17 21:35:57 +01:00
DEBUGASSERT(nand && nand->raw);
2013-11-18 18:42:17 +01:00
if (nand->raw->ecctype == NANDECC_SWECC)
{
/* Read data with software ECC verification */
2013-11-28 19:21:33 +01:00
return nandecc_readpage(nand, block, page, data, NULL);
}
/* The lower half will handle the No ECC and all hardware assisted
* ECC calculations.
*/
else
#endif
{
2013-11-28 19:21:33 +01:00
return NAND_READPAGE(nand->raw, block, page, data, NULL);
}
2013-11-17 21:35:57 +01:00
}
/****************************************************************************
* Name: nand_writepage
*
* Description:
* Writes the data area (only) of a page into NAND FLASH from the
* provided buffer.
*
* Input Parameters:
2013-11-17 21:35:57 +01:00
* 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.
2013-11-28 19:21:33 +01:00
* data - Buffer where the data area will be stored.
2013-11-17 21:35:57 +01:00
*
* Returned Value:
2013-11-17 21:35:57 +01:00
* 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,
2013-11-28 19:21:33 +01:00
unsigned int page, FAR const void *data)
2013-11-17 21:35:57 +01:00
{
#ifdef CONFIG_MTD_NAND_BLOCKCHECK
/* Check that the block is good */
if (nand_checkblock(nand, block) != GOODBLOCK)
{
ferr("ERROR: Block is BAD\n");
2013-11-17 21:35:57 +01:00
return -EAGAIN;
}
2013-12-02 04:06:07 +01:00
#endif
2013-11-17 21:35:57 +01:00
#ifdef CONFIG_MTD_NAND_SWECC
/* nandecc_writepage will handle the software ECC case */
DEBUGASSERT(nand && nand->raw);
2013-11-18 18:42:17 +01:00
if (nand->raw->ecctype == NANDECC_SWECC)
{
/* Write data with software ECC calculation */
2013-11-28 19:21:33 +01:00
return nandecc_writepage(nand, block, page, data, NULL);
}
2013-11-17 21:35:57 +01:00
/* The lower half will handle the No ECC and all hardware assisted
* ECC calculations.
*/
2015-10-04 23:04:00 +02:00
else
#endif
{
2013-11-28 19:21:33 +01:00
return NAND_WRITEPAGE(nand->raw, block, page, data, NULL);
}
2013-11-17 21:35:57 +01:00
}
2013-11-17 01:26:07 +01:00
/****************************************************************************
* Name: nand_erase
*
* Description:
* Erase several blocks, each of the size previously reported.
*
****************************************************************************/
static int nand_erase(FAR struct mtd_dev_s *dev, off_t startblock,
2013-11-17 01:26:07 +01:00
size_t nblocks)
{
2013-11-17 21:35:57 +01:00
FAR struct nand_dev_s *nand = (FAR struct nand_dev_s *)dev;
size_t blocksleft = nblocks;
int ret;
2013-11-17 01:26:07 +01:00
finfo("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
2013-11-17 01:26:07 +01:00
2013-11-17 21:35:57 +01:00
/* Lock access to the NAND until we complete the erase */
2013-11-17 01:26:07 +01:00
nxmutex_lock(&nand->lock);
2013-11-17 21:35:57 +01:00
while (blocksleft-- > 0)
{
/* Erase each sector */
ret = nand_eraseblock(nand, startblock, false);
if (ret < 0)
{
ferr("ERROR: nand_eraseblock failed on block %ld: %d\n",
2013-11-17 21:35:57 +01:00
(long)startblock, ret);
nxmutex_unlock(&nand->lock);
2013-11-17 21:35:57 +01:00
return ret;
}
startblock++;
}
nxmutex_unlock(&nand->lock);
2013-11-17 21:35:57 +01:00
return (int)nblocks;
2013-11-17 01:26:07 +01:00
}
/****************************************************************************
* Name: nand_bread
*
* Description:
* Read the specified number of blocks into the user provided buffer.
*
****************************************************************************/
static ssize_t nand_bread(FAR struct mtd_dev_s *dev, off_t startpage,
2013-11-28 19:21:33 +01:00
size_t npages, FAR uint8_t *buffer)
2013-11-17 01:26:07 +01:00
{
2013-11-17 21:35:57 +01:00
FAR struct nand_dev_s *nand = (FAR struct nand_dev_s *)dev;
FAR struct nand_raw_s *raw;
FAR struct nand_model_s *model;
bool fixedecc = false;
2013-11-17 21:35:57 +01:00
unsigned int pagesperblock;
unsigned int page;
uint16_t pagesize;
size_t remaining;
off_t maxblock;
off_t block;
int ret;
2013-11-17 01:26:07 +01:00
finfo("startpage: %" PRIdOFF " npages: %zu\n", startpage, npages);
2013-11-17 21:35:57 +01:00
DEBUGASSERT(nand && nand->raw);
/* Retrieve the model */
raw = nand->raw;
model = &raw->model;
2013-11-17 01:26:07 +01:00
2013-11-17 21:35:57 +01:00
/* Get the number of pages in one block, the size of one page, and
* the number of blocks on the device.
2013-11-17 01:26:07 +01:00
*/
2013-11-17 21:35:57 +01:00
pagesperblock = nandmodel_pagesperblock(model);
pagesize = nandmodel_getpagesize(model);
maxblock = nandmodel_getdevblocks(model);
/* Get the block and page offset associated with the startpage */
block = startpage / pagesperblock;
page = startpage % pagesperblock;
2013-11-17 21:35:57 +01:00
/* Lock access to the NAND until we complete the read */
nxmutex_lock(&nand->lock);
2013-11-17 21:35:57 +01:00
/* Then read every page from NAND */
for (remaining = npages; remaining > 0; remaining--)
{
/* Check for attempt to read beyond the end of NAND */
if (block > maxblock)
{
ferr("ERROR: Read beyond the end of FLASH, block=%ld\n",
(long)block);
ret = -ESPIPE;
goto errout_with_lock;
}
2013-11-17 21:35:57 +01:00
/* Read the next page from NAND */
2013-11-28 19:21:33 +01:00
ret = nand_readpage(nand, block, page, buffer);
if (ret == -EUCLEAN)
{
fixedecc = true;
}
else if (ret < 0)
2013-11-17 21:35:57 +01:00
{
ferr("ERROR: nand_readpage failed block=%" PRIdOFF
" page=%d: %d\n", block, page, ret);
2013-11-17 21:35:57 +01:00
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;
block++;
2013-11-17 21:35:57 +01:00
}
/* Increment the buffer point by the size of one page */
2013-11-28 19:21:33 +01:00
buffer += pagesize;
2013-11-17 21:35:57 +01:00
}
nxmutex_unlock(&nand->lock);
return fixedecc ? -EUCLEAN : npages;
2013-11-17 21:35:57 +01:00
errout_with_lock:
nxmutex_unlock(&nand->lock);
2013-11-17 21:35:57 +01:00
return ret;
2013-11-17 01:26:07 +01:00
}
/****************************************************************************
* Name: nand_bwrite
*
* Description:
* Write the specified number of blocks from the user provided buffer.
*
****************************************************************************/
static ssize_t nand_bwrite(FAR struct mtd_dev_s *dev, off_t startpage,
2013-11-28 19:21:33 +01:00
size_t npages, const uint8_t *buffer)
2013-11-17 01:26:07 +01:00
{
2013-11-17 21:35:57 +01:00
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;
2013-11-17 01:26:07 +01:00
finfo("startpage: %" PRIdOFF " npages: %zu\n", startpage, npages);
2013-11-17 21:35:57 +01:00
DEBUGASSERT(nand && nand->raw);
/* Retrieve the model */
2013-11-17 01:26:07 +01:00
raw = nand->raw;
model = &raw->model;
2013-11-17 21:35:57 +01:00
/* Get the number of pages in one block, the size of one page, and
* the number of blocks on the device.
2013-11-17 01:26:07 +01:00
*/
2013-11-17 21:35:57 +01:00
pagesperblock = nandmodel_pagesperblock(model);
pagesize = nandmodel_getpagesize(model);
maxblock = nandmodel_getdevblocks(model);
/* Get the block and page offset associated with the startpage */
block = startpage / pagesperblock;
page = startpage % pagesperblock;
2013-11-17 21:35:57 +01:00
/* Lock access to the NAND until we complete the write */
nxmutex_lock(&nand->lock);
2013-11-17 21:35:57 +01:00
/* Then write every page into NAND */
for (remaining = npages; remaining > 0; remaining--)
{
/* Check for attempt to write beyond the end of NAND */
if (block > maxblock)
{
ferr("ERROR: Write beyond the end of FLASH, block=%ld\n",
(long)block);
ret = -ESPIPE;
goto errout_with_lock;
}
2013-11-17 21:35:57 +01:00
/* Write the next page into NAND */
2013-11-28 19:21:33 +01:00
ret = nand_writepage(nand, block, page, buffer);
2013-11-17 21:35:57 +01:00
if (ret < 0)
{
ferr("ERROR: nand_writepage failed block=%ld page=%d: %d\n",
2013-11-17 21:35:57 +01:00
(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;
block++;
2013-11-17 21:35:57 +01:00
}
/* Increment the buffer point by the size of one page */
2013-11-28 19:21:33 +01:00
buffer += pagesize;
2013-11-17 21:35:57 +01:00
}
nxmutex_unlock(&nand->lock);
2013-11-17 21:35:57 +01:00
return npages;
errout_with_lock:
nxmutex_unlock(&nand->lock);
2013-11-17 21:35:57 +01:00
return ret;
2013-11-17 01:26:07 +01:00
}
/****************************************************************************
* Name: nand_ioctl
****************************************************************************/
static int nand_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
2013-11-17 01:26:07 +01:00
{
2013-11-17 21:35:57 +01:00
FAR struct nand_dev_s *nand = (FAR struct nand_dev_s *)dev;
FAR struct nand_raw_s *raw;
FAR struct nand_model_s *model;
2013-11-17 01:26:07 +01:00
int ret = -EINVAL; /* Assume good command with bad parameters */
DEBUGASSERT(nand && nand->raw);
raw = nand->raw;
model = &raw->model;
2013-11-17 01:26:07 +01:00
switch (cmd)
{
case MTDIOC_GEOMETRY:
{
FAR struct mtd_geometry_s *geo = (FAR struct mtd_geometry_s *)arg;
2013-11-17 01:26:07 +01:00
if (geo)
{
memset(geo, 0, sizeof(*geo));
/* Populate the geometry structure with information needed to
* know the capacity and how to access the device. Returns:
2013-11-17 01:26:07 +01:00
*
* blocksize Size of one read/write block in bytes
* erasesize Size of one erase block in bytes
* neraseblocks The number of erase blocks in the device
2013-11-17 01:26:07 +01:00
*/
geo->blocksize = model->pagesize;
geo->erasesize = nandmodel_getbyteblocksize(model);
geo->neraseblocks = nandmodel_getdevblocks(model);
2013-11-17 01:26:07 +01:00
ret = OK;
}
}
break;
case BIOC_PARTINFO:
{
FAR struct partition_info_s *info =
(FAR struct partition_info_s *)arg;
if (info != NULL)
{
info->numsectors = nandmodel_getdevpagesize(model);
info->sectorsize = model->pagesize;
info->startsector = 0;
info->parent[0] = '\0';
ret = OK;
}
}
break;
2013-11-17 01:26:07 +01:00
case MTDIOC_BULKERASE:
{
/* Erase the entire device */
ret = nand_erase(dev, 0, nandmodel_getdevblocks(model));
2013-11-17 01:26:07 +01:00
}
break;
case MTDIOC_ERASESTATE:
{
FAR uint8_t *result = (FAR uint8_t *)arg;
*result = 0xff;
ret = OK;
}
break;
case MTDIOC_ERASESECTORS:
{
FAR struct mtd_erase_s *erase = (FAR struct mtd_erase_s *)arg;
ret = nand_erase(dev, erase->startblock, erase->nblocks);
}
break;
2013-11-17 01:26:07 +01:00
default:
ret = -ENOTTY; /* Bad command */
break;
}
return ret;
}
/****************************************************************************
* Name: nand_isbad
****************************************************************************/
static int nand_isbad(FAR struct mtd_dev_s *dev, off_t block)
{
FAR struct nand_dev_s *nand = (FAR struct nand_dev_s *)dev;
int ret;
nxmutex_lock(&nand->lock);
ret = nand_checkblock(nand, block);
nxmutex_unlock(&nand->lock);
if (ret == GOODBLOCK)
{
ret = 0;
}
else if (ret == BADBLOCK)
{
ret = 1;
}
return ret;
}
/****************************************************************************
* Name: nand_markbad
****************************************************************************/
static int nand_markbad(FAR struct mtd_dev_s *dev, off_t block)
{
FAR struct nand_dev_s *nand = (FAR struct nand_dev_s *)dev;
int ret;
nxmutex_lock(&nand->lock);
ret = nand_markblock(nand, block);
nxmutex_unlock(&nand->lock);
return ret;
}
2013-11-16 18:46:35 +01:00
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nand_raw_initialize
*
* Description:
* Initialize NAND without probing.
*
* Input Parameters:
* raw - Lower-half, raw NAND FLASH interface
*
* Returned Value:
* A non-NULL MTD driver instance is returned on success. NULL is
* returned on any failaure.
*
****************************************************************************/
FAR struct mtd_dev_s *nand_raw_initialize(FAR struct nand_raw_s *raw)
{
FAR struct nand_dev_s *nand;
/* Allocate an NAND MTD device structure */
nand = kmm_zalloc(sizeof(struct nand_dev_s));
if (!nand)
{
ferr("ERROR: Failed to allocate the NAND MTD device structure\n");
return NULL;
}
/* Initialize the NAND MTD device structure */
nand->mtd.erase = nand_erase;
nand->mtd.bread = nand_bread;
nand->mtd.bwrite = nand_bwrite;
nand->mtd.ioctl = nand_ioctl;
nand->mtd.isbad = nand_isbad;
nand->mtd.markbad = nand_markbad;
nand->raw = raw;
nxmutex_init(&nand->lock);
#if defined(CONFIG_MTD_NAND_BLOCKCHECK) && defined(CONFIG_DEBUG_INFO) && \
defined(CONFIG_DEBUG_FS)
/* Scan the device for bad blocks */
nand_devscan(nand);
#endif
/* Return the implementation-specific state structure as the MTD device */
return &nand->mtd;
}
2013-11-16 18:46:35 +01:00
/****************************************************************************
* Name: nand_initialize
*
* Description:
* Probe and initialize NAND.
*
* Input Parameters:
2013-11-17 15:56:30 +01:00
* raw - Lower-half, raw NAND FLASH interface
2013-11-16 18:46:35 +01:00
*
* Returned Value:
* A non-NULL MTD driver instance is returned on success. NULL is
2013-11-17 01:26:07 +01:00
* returned on any failaure.
2013-11-16 18:46:35 +01:00
*
****************************************************************************/
2013-11-17 15:56:30 +01:00
FAR struct mtd_dev_s *nand_initialize(FAR struct nand_raw_s *raw)
2013-11-16 18:46:35 +01:00
{
struct onfi_pgparam_s onfi;
int ret;
finfo("cmdaddr=%p addraddr=%p dataaddr=%p\n",
2013-11-17 15:56:30 +01:00
(FAR void *)raw->cmdaddr, (FAR void *)raw->addraddr,
(FAR void *)raw->dataaddr);
2013-11-16 18:46:35 +01:00
/* Check if there is NAND connected on the EBI */
2013-11-17 15:56:30 +01:00
if (!onfi_ebidetect(raw->cmdaddr, raw->addraddr, raw->dataaddr))
2013-11-16 18:46:35 +01:00
{
ferr("ERROR: No NAND device detected at: %p %p %p\n",
2013-11-17 15:56:30 +01:00
(FAR void *)raw->cmdaddr, (FAR void *)raw->addraddr,
(FAR void *)raw->dataaddr);
2013-11-17 01:26:07 +01:00
return NULL;
2013-11-16 18:46:35 +01:00
}
/* Read the ONFI page parameters from the NAND device */
2013-11-17 15:56:30 +01:00
ret = onfi_read(raw->cmdaddr, raw->addraddr, raw->dataaddr, &onfi);
2013-11-16 18:46:35 +01:00
if (ret < 0)
{
2013-11-17 15:56:30 +01:00
uint32_t chipid;
finfo("Failed to get ONFI page parameters: %d\n", ret);
2013-11-17 15:56:30 +01:00
/* If the ONFI model is not supported, determine the NAND
* model from a lookup of known FLASH parts.
*/
chipid = nand_chipid(raw);
if (nandmodel_find(g_nandmodels, NAND_NMODELS, chipid,
&raw->model))
{
ferr("ERROR: Could not determine NAND model\n");
2013-11-17 15:56:30 +01:00
return NULL;
}
2013-11-16 18:46:35 +01:00
}
else
{
2013-11-17 15:56:30 +01:00
FAR struct nand_model_s *model = &raw->model;
2013-11-16 18:46:35 +01:00
uint64_t size;
finfo("Found ONFI compliant NAND FLASH\n");
2013-11-16 18:46:35 +01:00
/* Construct the NAND model structure */
2013-11-17 01:26:07 +01:00
model->devid = onfi.manufacturer;
model->options = onfi.buswidth ? NANDMODEL_DATAWIDTH16 :
NANDMODEL_DATAWIDTH8;
2013-11-17 01:26:07 +01:00
model->pagesize = onfi.pagesize;
model->sparesize = onfi.sparesize;
2013-11-16 18:46:35 +01:00
2013-11-17 01:26:07 +01:00
size = (uint64_t)onfi.pagesperblock *
(uint64_t)onfi.blocksperlun *
(uint64_t)onfi.pagesize;
2013-11-16 18:46:35 +01:00
DEBUGASSERT(size < ((uint64_t)1 << 36));
2013-11-17 01:26:07 +01:00
model->devsize = (uint16_t)(size >> 20);
2013-11-16 18:46:35 +01:00
2013-11-17 01:26:07 +01:00
size = (uint64_t)onfi.pagesperblock *
(uint64_t)onfi.pagesize;
2013-11-16 18:46:35 +01:00
DEBUGASSERT(size < ((uint64_t)1 << 26));
2013-11-17 01:26:07 +01:00
model->blocksize = (uint16_t)(size >> 10);
2013-11-16 18:46:35 +01:00
switch (onfi.pagesize)
{
case 256:
2013-11-17 01:26:07 +01:00
model->scheme = &g_nand_sparescheme256;
2013-11-16 18:46:35 +01:00
break;
case 512:
2013-11-17 01:26:07 +01:00
model->scheme = &g_nand_sparescheme512;
2013-11-16 18:46:35 +01:00
break;
case 2048:
2013-11-17 01:26:07 +01:00
model->scheme = &g_nand_sparescheme2048;
2013-11-16 18:46:35 +01:00
break;
case 4096:
2013-11-17 01:26:07 +01:00
model->scheme = &g_nand_sparescheme4096;
2013-11-16 18:46:35 +01:00
break;
}
/* Enable any internal, embedded ECC function */
2013-11-16 18:46:35 +01:00
onfi_embeddedecc(&onfi, raw->cmdaddr, raw->addraddr, raw->dataaddr,
true);
2013-11-16 18:46:35 +01:00
}
return nand_raw_initialize(raw);
2013-11-16 18:46:35 +01:00
}