nuttx/drivers/mtd/mtd_nand.c

980 lines
27 KiB
C
Raw 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) 2013 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
2013-11-16 19:22:43 +01:00
* This logic was based largely on Atmel sample code with modifications for
* better integration with NuttX. The Atmel sample code has a BSD
* compatible license that requires this copyright notice:
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 <sys/types.h>
#include <stdint.h>
#include <string.h>
2013-11-17 21:35:57 +01:00
#include <semaphore.h>
2013-11-16 18:46:35 +01:00
#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>
2013-11-17 01:26:07 +01:00
#include <nuttx/kmalloc.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/mtd/mtd.h>
2013-11-16 18:46:35 +01:00
#include <nuttx/mtd/nand.h>
#include <nuttx/mtd/onfi.h>
2013-11-17 15:56:30 +01:00
#include <nuttx/mtd/nand_raw.h>
2013-11-16 18:46:35 +01:00
#include <nuttx/mtd/nand_scheme.h>
#include <nuttx/mtd/nand_model.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
****************************************************************************/
2013-11-17 21:35:57 +01:00
/* NAND locking */
static int nand_lock(FAR struct nand_dev_s *nand);
#define nand_unlock(n) sem_post(&(n)->exclsem)
/* Bad block checking */
2013-11-17 19:22:09 +01:00
#ifdef CONFIG_MTD_NAND_BLOCKCHECK
static int nand_checkblock(FAR struct nand_dev_s *nand, off_t block);
static int nand_devscan(FAR struct nand_dev_s *nand);
#else
# define nand_checkblock(n,b) (GOODBLOCK)
# define nand_devscan(n) (0)
2013-11-17 19:22:09 +01:00
#endif
2013-11-16 18:46:35 +01:00
/* Misc. NAND helpers */
static uint32_t nand_chipid(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,
2013-11-28 19:21:33 +01:00
unsigned int page, FAR uint8_t *data);
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
2013-11-17 01:26:07 +01:00
/* MTD driver methods */
static int nand_erase(struct mtd_dev_s *dev, off_t startblock,
size_t nblocks);
static ssize_t nand_bread(struct mtd_dev_s *dev, off_t startblock,
2013-11-28 19:21:33 +01:00
size_t nblocks, uint8_t *buffer);
2013-11-17 01:26:07 +01:00
static ssize_t nand_bwrite(struct mtd_dev_s *dev, off_t startblock,
2013-11-28 19:21:33 +01:00
size_t nblocks, const uint8_t *buffer);
2013-11-17 01:26:07 +01:00
static int nand_ioctl(struct mtd_dev_s *dev, int cmd,
unsigned long arg);
2013-11-16 18:46:35 +01:00
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
2013-11-17 21:35:57 +01:00
/****************************************************************************
* 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;
}
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.
*
****************************************************************************/
#ifdef CONFIG_MTD_NAND_BLOCKCHECK
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)
{
2013-11-25 20:53:58 +01:00
fdbg("ERROR: Failed to read page 0 of block %d\n", block);
2013-11-17 19:22:09 +01:00
return ret;
}
nandscheme_readbadblockmarker(scheme, spare, &marker);
if (marker != 0xff)
{
2013-11-25 20:53:58 +01:00
fvdbg("Page 0 block %d 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)
{
2013-11-25 20:53:58 +01:00
fdbg("ERROR: Failed to read page 1 of block %d\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
{
2013-11-25 20:53:58 +01:00
fvdbg("Page 1 block %d marker=%02x\n", block, marker);
2013-11-17 19:22:09 +01:00
return BADBLOCK;
}
return GOODBLOCK;
}
#endif /* CONFIG_MTD_NAND_BLOCKCHECK */
/****************************************************************************
* Name: nand_devscan
*
* Description:
* Scans the device to retrieve or create block status information.
*
* Currently, this functin 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
*
****************************************************************************/
//#ifdef CONFIG_MTD_NAND_BLOCKCHECK
#if defined(CONFIG_MTD_NAND_BLOCKCHECK) && defined(CONFIG_DEBUG_VERBOSE) && 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;
2013-11-25 20:53:58 +01:00
#if defined(CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
off_t good;
unsigned int ngood;
#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 */
2013-11-25 20:53:58 +01:00
fvdbg("Retrieving bad block information. nblocks=%d\n", nblocks);
2013-11-17 19:22:09 +01:00
/* Retrieve block status from their first page spare area */
2013-11-25 20:53:58 +01:00
#if defined(CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
ngood = 0;
#endif
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)
{
2013-11-25 20:53:58 +01:00
#if defined(CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
if (ngood > 0)
{
fvdbg("Good blocks: %u - %u\n", good, good + ngood);
ngood = 0;
}
#endif
2013-11-17 19:22:09 +01:00
if (ret == BADBLOCK)
{
fvdbg("Block %u is bad\n", (unsigned int)block);
}
else
{
fdbg("ERROR: Cannot retrieve info from block %u: %d\n",
(unsigned int)block, ret);
}
}
2013-11-25 20:53:58 +01:00
#if defined(CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
else
{
if (ngood == 0)
{
good = block;
}
ngood++;
}
#endif
2013-11-17 19:22:09 +01:00
}
2013-11-25 20:53:58 +01:00
#if defined(CONFIG_DEBUG_VERBOSE) && defined(CONFIG_DEBUG_FS)
if (ngood > 0)
{
fvdbg("Good blocks: %u - %u\n", good, good + ngood);
}
#endif
2013-11-17 19:22:09 +01:00
return OK;
}
#endif /* CONFIG_MTD_NAND_BLOCKCHECK */
/****************************************************************************
* 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(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);
fvdbg("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
* 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.
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.
* 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
{
2013-11-28 19:21:33 +01:00
fvdbg("block=%d page=%d data=%p\n", (int)block, page, data);
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)
{
fdbg("ERROR: Block is BAD\n");
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:
* 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.
* 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)
{
fdbg("ERROR: Block is BAD\n");
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.
*/
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(struct mtd_dev_s *dev, off_t startblock,
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
2013-11-17 21:35:57 +01:00
fvdbg("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
2013-11-17 21:35:57 +01:00
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;
2013-11-17 01:26:07 +01:00
}
/****************************************************************************
* Name: nand_bread
*
* Description:
* Read the specified number of blocks into the user provided buffer.
*
****************************************************************************/
2013-11-17 21:35:57 +01:00
static ssize_t nand_bread(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;
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
fvdbg("startpage: %ld npages: %d\n", (long)startpage, (int)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 */
nand_lock(nand);
/* 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)
{
fdbg("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);
2013-11-17 21:35:57 +01:00
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;
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
}
nand_unlock(nand);
return npages;
errout_with_lock:
nand_unlock(nand);
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.
*
****************************************************************************/
2013-11-17 21:35:57 +01:00
static ssize_t nand_bwrite(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
2013-11-17 21:35:57 +01:00
fvdbg("startpage: %08lx npages: %d\n", (long)startpage, (int)npages);
DEBUGASSERT(nand && nand->raw);
/* Retrieve the model */
2013-11-17 01:26:07 +01:00
2013-11-17 21:35:57 +01:00
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.
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 */
nand_lock(nand);
/* 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)
{
fdbg("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)
{
fdbg("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
}
nand_unlock(nand);
return npages;
errout_with_lock:
nand_unlock(nand);
return ret;
2013-11-17 01:26:07 +01:00
}
/****************************************************************************
* Name: nand_ioctl
****************************************************************************/
static int nand_ioctl(struct mtd_dev_s *dev, int cmd, unsigned long arg)
{
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:
{
struct mtd_geometry_s *geo = (struct mtd_geometry_s *)arg;
if (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 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_XIPBASE:
default:
ret = -ENOTTY; /* Bad command */
break;
}
return ret;
}
2013-11-16 18:46:35 +01:00
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* 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
* cmdaddr - NAND command address base
* addraddr - NAND address address base
* dataaddr - NAND data address
2013-11-17 01:26:07 +01:00
* model - A pointer to the model data (probably in the raw MTD
* driver instance.
2013-11-16 18:46:35 +01:00
*
* Returned value.
2013-11-17 01:26:07 +01:00
* A non-NULL MTD driver intstance is returned on success. NULL is
* 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
{
2013-11-17 19:22:09 +01:00
FAR struct nand_dev_s *nand;
2013-11-16 18:46:35 +01:00
struct onfi_pgparam_s onfi;
int ret;
fvdbg("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
{
fdbg("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;
fvdbg("Failed to get ONFI page parameters: %d\n", ret);
/* 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))
{
fdbg("ERROR: Could not determine NAND model\n");
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;
fvdbg("Found ONFI compliant NAND FLASH\n");
/* 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;
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;
}
/* Disable any internal, embedded ECC function */
(void)onfi_embeddedecc(&onfi, cmdaddr, addraddr, dataaddr, false);
}
2013-11-17 01:26:07 +01:00
/* Allocate an NAND MTD device structure */
nand = (FAR struct nand_dev_s *)kmm_zalloc(sizeof(struct nand_dev_s));
2013-11-17 19:22:09 +01:00
if (!nand)
2013-11-17 01:26:07 +01:00
{
fdbg("ERROR: Failed to allocate the NAND MTD device structure\n");
return NULL;
}
/* Initialize the NAND MTD device structure */
2013-11-17 19:22:09 +01:00
nand->mtd.erase = nand_erase;
nand->mtd.bread = nand_bread;
nand->mtd.bwrite = nand_bwrite;
nand->mtd.ioctl = nand_ioctl;
nand->raw = raw;
2013-11-17 01:26:07 +01:00
2013-11-17 21:35:57 +01:00
sem_init(&nand->exclsem, 0, 1);
2013-11-17 19:22:09 +01:00
/* Scan the device for bad blocks */
2013-11-25 20:53:58 +01:00
(void)nand_devscan(nand);
2013-11-16 18:46:35 +01:00
/* Return the implementation-specific state structure as the MTD device */
2013-11-17 19:22:09 +01:00
return &nand->mtd;
2013-11-16 18:46:35 +01:00
}