[Yaffs] no erasedblocks available

Top Page
Attachments:
Message as email
+ (text/plain)
+ nand.c (text/x-csrc)
Delete this message
Reply to this message
Author: bandwidthcrunch
Date:  
To: yaffs
Subject: [Yaffs] no erasedblocks available
Hi all,
        I have a samsung nand device K9F1208UOM which is being mounted
perfectly and shows me erasedblocks when i check the proc entry. But
when i try to create/copy etc a file on to the partition i run out of
memory . Below is the log and my nand.c file which i believe tries to
create the  directory but ends up writing to all blocks cause after
the mkdir command the proc entry shows me only 5 eraseblocks .
[root@manav /]#mount -t yaffs /dev/mtdblock3 /mnt/yaffs/
yaffs: dev is 7939 name is "unknown-char(31,3)"
yaffs: dev is 7939 name is "unknown-char(31,3)"
[root@manav /]#df
Filesystem           1k-blocks      Used Available Use% Mounted on
rootfs                   31488      9312     22176  30% /
/dev/mtdblock2           31488      9312     22176  30% /
/dev/mtdblock3           16368       308     16060   2% /mnt/yaffs
[root@manav /]#cat /proc/yaffs
YAFFS built:Nov  4 2004 14:36:33
$Id: yaffs_fs.c,v 1.1.6.2 2003/11/08 10:33:14 bushi Exp $
$Id: yaffs_guts.c,v 1.1.6.4 2004/03/29 12:09:30 bushi Exp $


Device yaffs
startBlock......... 1
endBlock........... 1023
chunkGroupBits..... 0
chunkGroupSize..... 1
nErasedBlocks...... 1003
nTnodesCreated..... 100
nFreeTnodes........ 87
nObjectsCreated.... 100
nFreeObjects....... 90
nFreeChunks........ 32316
nPageWrites........ 0
nPageReads......... 0
nBlockErasures..... 0
nGCCopies.......... 0
garbageCollections. 0
passiveGCs......... 0
nRetriedWrites..... 0
nRetireBlocks...... 0
eccFixed........... 0
eccUnfixed......... 6
tagsEccFixed....... 0
tagsEccUnfixed..... 1018
cacheHits.......... 0
nDeletedFiles...... 0
nUnlinkedFiles..... 0
nBackgroudDeletions 0
useNANDECC......... 0
cd /mnt/yaffs/

[root@manav yaffs]#ls
lost+found
[root@manav yaffs]#mkdir test
Writing data without ECC to NAND-FLASH is not recommended ........
:
:
Writing data without ECC to NAND-FLASH is not recommended
mkdir: Cannot create directory `test': Cannot allocate memory
[root@manav yaffs]#cat /proc/yaffs
YAFFS built:Nov 4 2004 14:36:33
$Id: yaffs_fs.c,v 1.1.6.2 2003/11/08 10:33:14 bushi Exp $
$Id: yaffs_guts.c,v 1.1.6.4 2004/03/29 12:09:30 bushi Exp $

Device yaffs
startBlock......... 1
endBlock........... 1023
chunkGroupBits..... 0
chunkGroupSize..... 1
nErasedBlocks...... 5
nTnodesCreated..... 100
nFreeTnodes........ 87
nObjectsCreated.... 100
nFreeObjects....... 90
nFreeChunks........ 32316
nPageWrites........ 65586
nPageReads......... 31655
nBlockErasures..... 0
nGCCopies.......... 0
garbageCollections. 0
passiveGCs......... 0
nRetriedWrites..... 31934
nRetireBlocks...... 998
eccFixed........... 0
eccUnfixed......... 6
tagsEccFixed....... 0
tagsEccUnfixed..... 1018
cacheHits.......... 0
nDeletedFiles...... 0
nUnlinkedFiles..... 0
nBackgroudDeletions 0
useNANDECC......... 0


I have disabled both printk (KERN_WARNING "Writing data without ECC
to NAND-FLASH is not recommended\n");
and printk (KERN_WARNING "Reading data from NAND FLASH without ECC is
not recommended\n"); in nand.c but to no avail . My kernel is 2.4.19
and i have tried enabling both -DCONFIG_YAFFS_USE_NANDECC and
DCONFIG_YAFFS_USE_OLD_MTD in all combo's. Any suggestions would be
great .
/*
 *  drivers/mtd/nand.c
 *
 *  Overview:
 *   This is the generic MTD driver for NAND flash devices. It should be
 *   capable of working with almost all NAND chips currently available.
 *   
 *    Additional technical information is available on
 *    http://www.linux-mtd.infradead.org/tech/nand.html
 *    
 *  Copyright (C) 2000 Steven J. Hill ()
 *           2002 Thomas Gleixner ()
 *
 *  10-29-2001  Thomas Gleixner ()
 *         - Changed nand_chip structure for controlline function to
 *        support different hardware structures (Access to
 *        controllines ALE,CLE,NCE via hardware specific function. 
 *        - exit out of "failed erase block" changed, to avoid
 *        driver hangup
 *        - init_waitqueue_head added in function nand_scan !!
 *
 *  01-30-2002  Thomas Gleixner ()
 *        change in nand_writev to block invalid vecs entries
 *
 *  02-11-2002  Thomas Gleixner ()
 *        - major rewrite to avoid duplicated code
 *          common nand_write_page function  
 *          common get_chip function 
 *        - added oob_config structure for out of band layouts
 *        - write_oob changed for partial programming
 *        - read cache for faster access for subsequent reads
 *        from the same page.
 *        - support for different read/write address
 *        - support for device ready/busy line
 *        - read oob for more than one page enabled
 *
 *  02-27-2002    Thomas Gleixner ()
 *        - command-delay can be programmed
 *        - fixed exit from erase with callback-function enabled
 *
 *  03-21-2002  Thomas Gleixner ()
 *        - DEBUG improvements provided by Elizabeth Clarke 
 *        ()
 *        - added zero check for this->chip_delay
 *
 *  04-03-2002  Thomas Gleixner ()
 *        - added added hw-driver supplied command and wait functions
 *        - changed blocking for erase (erase suspend enabled)
 *        - check pointers before accessing flash provided by
 *        John Hall ()
 *
 *  04-09-2002  Thomas Gleixner ()
 *        - nand_wait repaired
 *
 *  04-28-2002  Thomas Gleixner ()    
 *        - OOB config defines moved to nand.h 
 *
 *  08-01-2002  Thomas Gleixner ()    
 *        - changed my mailaddress, added pointer to tech/nand.html
 *
 *  08-07-2002     Thomas Gleixner ()
 *        forced bad block location to byte 5 of OOB, even if
 *        CONFIG_MTD_NAND_ECC_JFFS2 is not set, to prevent
 *        erase /dev/mtdX from erasing bad blocks and destroying
 *        bad block info
 *
 *  08-10-2002     Thomas Gleixner ()
 *        Fixed writing tail of data. Thanks to Alice Hennessy
 *        <>.
 *
 *  08-10-2002     Thomas Gleixner ()
 *        nand_read_ecc and nand_write_page restructured to support
 *        hardware ECC. Thanks to Steven Hein ()
 *        for basic implementation and suggestions.
 *        3 new pointers in nand_chip structure:
 *        calculate_ecc, correct_data, enabled_hwecc                      
 *        forcing all hw-drivers to support page cache
 *        eccvalid_pos is now mandatory
 *
 *  08-17-2002    tglx: fixed signed/unsigned missmatch in write.c
 *        Thanks to Ken Offer <>     
 *
 *  08-29-2002  tglx: use buffered read/write only for non pagealigned 
 *        access, speed up the aligned path by using the fs-buffer
 *        reset chip removed from nand_select(), implicit done
 *        only, when erase is interrupted
 *        waitfuntion use yield, instead of schedule_timeout
 *        support for 6byte/512byte hardware ECC
 *        read_ecc, write_ecc extended for different oob-layout
 *        selections: Implemented NAND_NONE_OOB, NAND_JFFS2_OOB,
 *        NAND_YAFFS_OOB. fs-driver gives one of these constants
 *        to select the oob-layout fitting the filesystem.
 *        oobdata can be read together with the raw data, when
 *        the fs-driver supplies a big enough buffer.
 *        size = 12 * number of pages to read (256B pagesize)
 *               24 * number of pages to read (512B pagesize)
 *        the buffer contains 8/16 byte oobdata and 4/8 byte
 *        returncode from calculate_ecc
 *        oobdata can be given from filesystem to program them
 *        in one go together with the raw data. ECC codes are
 *        filled in at the place selected by oobsel.
 *
 *  09-04-2002  tglx: fixed write_verify (John Hall ())
 *
 *  11-11-2002  tglx: fixed debug output in nand_write_page 
 *        (John Hall ())
 *
 *  11-25-2002  tglx: Moved device ID/ manufacturer ID from nand_ids.h
 *        Splitted device ID and manufacturer ID table. 
 *        Removed CONFIG_MTD_NAND_ECC, as it defaults to ECC_NONE for
 *        mtd->read / mtd->write and is controllable by the fs driver
 *        for mtd->read_ecc / mtd->write_ecc
 *        some minor cleanups
 *
 *  12-05-2002  tglx: Dave Ellis (DGE@sixnetio) provided the fix for
 *        WRITE_VERIFY long time ago. Thanks for remembering me.    
 *
 *  02-14-2003  tglx: Reject non page aligned writes     
 *        Fixed ecc select in nand_write_page to match semantics. 
 *
 *  02-18-2003    tglx: Changed oobsel to pointer. Added a default oob-selector
 *            
 *  02-18-2003    tglx: Implemented oobsel again. Now it uses a pointer to
 +        a structure, which will be supplied by a filesystem driver
 *        If NULL is given, then the defaults (none or defaults
 *        supplied by ioctl (MEMSETOOBSEL) are used.
 *        For partitions the partition defaults are used (mtdpart.c)
 *
 *  06-04-2003  tglx: fix compile errors and fix write verify problem for
 *        some chips, which need either a delay between the readback
 *        and the next write command or have the CE removed. The
 *        CE disable/enable is much faster than a 20us delay and
 *        it should work on all available chips.
 *    
 * $Id: nand.c,v 1.57 2003/10/15 13:23:56 dwmw2 Exp $
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */


#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/compatmac.h>
#include <linux/interrupt.h>
#include <asm/io.h>

/*
* NAND low-level MTD interface functions
*/
static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len);
static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len);
static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len);

static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
              size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel);
static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf);
static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
               size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel);
static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf);
static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs,
            unsigned long count, loff_t to, size_t * retlen);
static int nand_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs,
            unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
static void nand_sync (struct mtd_info *mtd);
static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf,  struct nand_oobinfo *oobsel);


static u_char nand_read_byte(struct mtd_info *mtd)
{
    struct nand_chip *this = mtd->priv;
    return readb(this->IO_ADDR_R);
}


static void nand_write_byte(struct mtd_info *mtd, u_char byte)
{
    struct nand_chip *this = mtd->priv;
    writeb(byte, this->IO_ADDR_W);
}


static void nand_select_chip(struct mtd_info *mtd, int chip)
{
    struct nand_chip *this = mtd->priv;
    switch(chip) {
    case -1:
        this->hwcontrol(mtd, NAND_CTL_CLRNCE);    
        break;
    case 0:
        this->hwcontrol(mtd, NAND_CTL_SETNCE);
        break;


    default:
        BUG();
    }
}


static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
    int i;
    struct nand_chip *this = mtd->priv;


    for (i=0; i<len; i++)
        writeb(buf[i], this->IO_ADDR_W);
}


static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
    int i;
    struct nand_chip *this = mtd->priv;


    for (i=0; i<len; i++)
        buf[i] = readb(this->IO_ADDR_R);
}


static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
    int i;
    struct nand_chip *this = mtd->priv;


    for (i=0; i<len; i++)
        if (buf[i] != readb(this->IO_ADDR_R))
            return i;


    return 0;
}


/* Appropriate chip should already be selected */
static int nand_block_bad(struct mtd_info *mtd, unsigned long page)
{
    struct nand_chip *this = mtd->priv;

    
    this->cmdfunc (mtd, NAND_CMD_READOOB, NAND_BADBLOCK_POS, page);
    if (this->read_byte(mtd) != 0xff)
        return 1;


    return 0;
}


/*
 * Send command to NAND device
 */
static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
{
    register struct nand_chip *this = mtd->priv;


    /* Begin command latch cycle */
    this->hwcontrol(mtd, NAND_CTL_SETCLE);
    /*
     * Write out the command to the device.
     */
    if (command == NAND_CMD_SEQIN) {
        int readcmd;


        if (column >= mtd->oobblock) {
            /* OOB area */
            column -= mtd->oobblock;
            readcmd = NAND_CMD_READOOB;
        } else if (column < 256) {
            /* First 256 bytes --> READ0 */
            readcmd = NAND_CMD_READ0;
        } else {
            column -= 256;
            readcmd = NAND_CMD_READ1;
        }
        this->write_byte(mtd, readcmd);
    }
    this->write_byte(mtd, command);


    /* Set ALE and clear CLE to start address cycle */
    this->hwcontrol(mtd, NAND_CTL_CLRCLE);


    if (column != -1 || page_addr != -1) {
        this->hwcontrol(mtd, NAND_CTL_SETALE);


        /* Serially input address */
        if (column != -1)
            this->write_byte(mtd, column);
        if (page_addr != -1) {
            this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
            this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
            /* One more address cycle for higher density devices */
            if (mtd->size & 0x0c000000) 
                this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
        }
        /* Latch in address */
        this->hwcontrol(mtd, NAND_CTL_CLRALE);
    }

    
    /* 
     * program and erase have their own busy handlers 
     * status and sequential in needs no delay
    */
    switch (command) {

            
    case NAND_CMD_PAGEPROG:
    case NAND_CMD_ERASE1:
    case NAND_CMD_ERASE2:
    case NAND_CMD_SEQIN:
    case NAND_CMD_STATUS:
        return;


    case NAND_CMD_RESET:
        if (this->dev_ready)    
            break;
        udelay(this->chip_delay);
        this->hwcontrol(mtd, NAND_CTL_SETCLE);
        this->write_byte(mtd, NAND_CMD_STATUS);
        this->hwcontrol(mtd, NAND_CTL_CLRCLE);
        while ( !(this->read_byte(mtd) & 0x40));
        return;


    /* This applies to read commands */    
    default:
        /* 
         * If we don't have access to the busy pin, we apply the given
         * command delay
        */
        if (!this->dev_ready) {
            udelay (this->chip_delay);
            return;
        }    
    }

    
    /* wait until command is processed */
    while (!this->dev_ready(mtd));
}


/*
 *    Get chip for selected access
 */
static inline void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state, int *erase_state)
{


    DECLARE_WAITQUEUE (wait, current);


    /* 
     * Grab the lock and see if the device is available 
     * For erasing, we keep the spinlock until the
     * erase command is written. 
    */
retry:
    spin_lock_bh (&this->chip_lock);


    if (this->state == FL_READY) {
        this->state = new_state;
        if (new_state != FL_ERASING)
            spin_unlock_bh (&this->chip_lock);
        return;
    }
#if 0 /* This was broken. And of dubious utility */
    if (this->state == FL_ERASING) {
        if (new_state != FL_ERASING) {
            this->state = new_state;
            spin_unlock_bh (&this->chip_lock);
            this->select_chip(mtd, 0);    /* select in any case */
            this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
            return;
        }
    }
#endif
    set_current_state (TASK_UNINTERRUPTIBLE);
    add_wait_queue (&this->wq, &wait);
    spin_unlock_bh (&this->chip_lock);
    schedule ();
    remove_wait_queue (&this->wq, &wait);
    goto retry;
}


/*
* Wait for command done. This applies to erase and program only
* Erase can take up to 400ms and program up to 20ms according to
* general NAND and SmartMedia specs
*
*/
static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
{

    unsigned long    timeo = jiffies;
    int    status;

    
    if (state == FL_ERASING)
         timeo += (HZ * 400) / 1000;
    else
         timeo += (HZ * 20) / 1000;


    spin_lock_bh (&this->chip_lock);
    this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);


    while (time_before(jiffies, timeo)) {        
        /* Check, if we were interrupted */
        if (this->state != state) {
            spin_unlock_bh (&this->chip_lock);
            return 0;
        }
        if (this->dev_ready) {
            if (this->dev_ready(mtd))
                break;
        }
        if (this->read_byte(mtd) & 0x40)
            break;

                        
        spin_unlock_bh (&this->chip_lock);
        yield ();
        spin_lock_bh (&this->chip_lock);
    }
    status = (int) this->read_byte(mtd);
    spin_unlock_bh (&this->chip_lock);


    return status;
}


/*
 *    Nand_page_program function is used for write and writev !
 *    This function will always program a full page of data
 *    If you call it with a non page aligned buffer, you're lost :)
 */
static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf,  struct nand_oobinfo *oobsel)
{
    int     i, status;
    u_char    ecc_code[6], *oob_data;
    int    eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
    int      *oob_config = oobsel->eccpos;

    
    /* pad oob area, if we have no oob buffer from fs-driver */
    if (!oob_buf) {
        oob_data = &this->data_buf[mtd->oobblock];
        for (i = 0; i < mtd->oobsize; i++)
            oob_data[i] = 0xff;
    } else 
        oob_data = oob_buf;

    
    /* Send command to begin auto page programming */
    this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page);


    /* Write out complete page of data, take care of eccmode */
    switch (eccmode) {
    /* No ecc and software ecc 3/256, write all */
    case NAND_ECC_NONE:
//printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n");
        this->write_buf(mtd, this->data_poi, mtd->oobblock);
        break;
    case NAND_ECC_SOFT:
        this->calculate_ecc(mtd, &this->data_poi[0], &(ecc_code[0]));
        for (i = 0; i < 3; i++)
            oob_data[oob_config[i]] = ecc_code[i];
        /* Calculate and write the second ECC for 512 Byte page size */
        if (mtd->oobblock == 512) {
            this->calculate_ecc(mtd, &this->data_poi[256], &(ecc_code[3]));
            for (i = 3; i < 6; i++)
                oob_data[oob_config[i]] = ecc_code[i];
        } 
        this->write_buf(mtd, this->data_poi, mtd->oobblock);
        break;

        
    /* Hardware ecc 3 byte / 256 data, write first half, get ecc, then second, if 512 byte pagesize */    
    case NAND_ECC_HW3_256:        
        this->enable_hwecc(mtd, NAND_ECC_WRITE);    /* enable hardware ecc logic for write */
        this->write_buf(mtd, this->data_poi, mtd->eccsize);

        
        this->calculate_ecc(mtd, NULL, &(ecc_code[0]));
        for (i = 0; i < 3; i++)
            oob_data[oob_config[i]] = ecc_code[i];

            
        if (mtd->oobblock == 512) {
            this->enable_hwecc(mtd, NAND_ECC_WRITE);    /* enable hardware ecc logic for write*/
            this->write_buf(mtd, &this->data_poi[mtd->eccsize], mtd->oobblock - mtd->eccsize);
            this->calculate_ecc(mtd, NULL, &(ecc_code[3]));
            for (i = 3; i < 6; i++)
                oob_data[oob_config[i]] = ecc_code[i];
        }
        break;

                
    /* Hardware ecc 3 byte / 512 byte data, write full page */    
    case NAND_ECC_HW3_512:    
        this->enable_hwecc(mtd, NAND_ECC_WRITE);    /* enable hardware ecc logic */
        this->write_buf(mtd, this->data_poi, mtd->oobblock);
        this->calculate_ecc(mtd, NULL, &(ecc_code[0]));
        for (i = 0; i < 3; i++)
            oob_data[oob_config[i]] = ecc_code[i];
        break;


    /* Hardware ecc 6 byte / 512 byte data, write full page */    
    case NAND_ECC_HW6_512:    
        this->enable_hwecc(mtd, NAND_ECC_WRITE);    /* enable hardware ecc logic */
        this->write_buf(mtd, this->data_poi, mtd->oobblock);
        this->calculate_ecc(mtd, NULL, &(ecc_code[0]));
        for (i = 0; i < 6; i++)
            oob_data[oob_config[i]] = ecc_code[i];
        break;

        
    default:
        printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
        BUG();    
    }    

    
    /* Write out OOB data */
    this->write_buf(mtd, oob_data, mtd->oobsize);


    /* Send command to actually program the data */
    this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);


    /* call wait ready function */
    status = this->waitfunc (mtd, this, FL_WRITING);


    /* See if device thinks it succeeded */
    if (status & 0x01) {
        DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page);
        return -EIO;
    }


#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
    /*
     * The NAND device assumes that it is always writing to
     * a cleanly erased page. Hence, it performs its internal
     * write verification only on bits that transitioned from
     * 1 to 0. The device does NOT verify the whole page on a
     * byte by byte basis. It is possible that the page was
     * not completely erased or the page is becoming unusable
     * due to wear. The read with ECC would catch the error
     * later when the ECC page check fails, but we would rather
     * catch it early in the page write stage. Better to write
     * no data than invalid data.
     */


    /* Send command to read back the page */
    this->cmdfunc (mtd, NAND_CMD_READ0, 0, page);
    /* Loop through and verify the data */
    if (this->verify_buf(mtd, this->data_poi, mtd->oobblock)) {
        DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
        return -EIO;
    }


    /* check, if we have a fs-supplied oob-buffer */
    if (oob_buf) {
        if (this->verify_buf(mtd, oob_data, mtd->oobsize)) {
            DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
            return -EIO;
        }
    } else {
        if (eccmode != NAND_ECC_NONE) {
            int ecc_bytes = 0;


            switch (this->eccmode) {
            case NAND_ECC_SOFT:
            case NAND_ECC_HW3_256: ecc_bytes = (mtd->oobblock == 512) ? 6 : 3; break;
            case NAND_ECC_HW3_512: ecc_bytes = 3; break;
            case NAND_ECC_HW6_512: ecc_bytes = 6; break;
            }


            this->read_buf(mtd, oob_data, mtd->oobsize);


            for (i = 0; i < ecc_bytes; i++) {
                if (oob_data[oob_config[i]] != ecc_code[i]) {
                    DEBUG (MTD_DEBUG_LEVEL0,
                           "%s: Failed ECC write "
                       "verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i);
                return -EIO;
                }
            }
        }
    }
    /* 
     * Terminate the read command. This is faster than sending a reset command or 
     * applying a 20us delay before issuing the next programm sequence.
     * This is not a problem for all chips, but I have found a bunch of them.
     */
    this->select_chip(mtd, -1);
    this->select_chip(mtd, 0);
#endif
    return 0;
}


/*
*    Use NAND read ECC
*/
static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
{
    return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL);
}               



/*
 * NAND read with ECC
 */
static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
              size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
{
    int j, col, page, end, ecc;
    int erase_state = 0;
    int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;
    struct nand_chip *this = mtd->priv;
    u_char *data_poi, *oob_data = oob_buf;
    u_char ecc_calc[6];
    u_char ecc_code[6];
    int     eccmode;
    int    *oob_config;


    // use chip default if zero
    if (oobsel == NULL)
        oobsel = &mtd->oobinfo;

        
    eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
    oob_config = oobsel->eccpos;


    DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);


    /* Do not allow reads past end of device */
    if ((from + len) > mtd->size) {
        DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n");
        *retlen = 0;
        return -EINVAL;
    }


    /* Grab the lock and see if the device is available */
    nand_get_chip (this, mtd ,FL_READING, &erase_state);


    /* Select the NAND device */
    this->select_chip(mtd, 0);


    /* First we calculate the starting page */
    page = from >> this->page_shift;


    /* Get raw starting column */
    col = from & (mtd->oobblock - 1);


    end = mtd->oobblock;
    ecc = mtd->eccsize;


    /* Send the read command */
    this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);

    
    /* Loop until all data read */
    while (read < len) {

        
        /* If we have consequent page reads, apply delay or wait for ready/busy pin */
        if (read) {
            if (!this->dev_ready) 
                udelay (this->chip_delay);
            else
                while (!this->dev_ready(mtd));    
        }


        /* 
         * If the read is not page aligned, we have to read into data buffer
         * due to ecc, else we read into return buffer direct
         */
        if (!col && (len - read) >= end)  
            data_poi = &buf[read];
        else 
            data_poi = this->data_buf;


        /* get oob area, if we have no oob buffer from fs-driver */
        if (!oob_buf) {
            oob_data = &this->data_buf[end];
            oob = 0;
        }     

            
        j = 0;
        switch (eccmode) {
        case NAND_ECC_NONE: {    /* No ECC, Read in a page */
            static unsigned long lastwhinge = 0;
            if ((lastwhinge / HZ) != (jiffies / HZ)) {
        //        printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n");
                lastwhinge = jiffies;
            }
            this->read_buf(mtd, data_poi, end);
            break;
        }

            
        case NAND_ECC_SOFT:    /* Software ECC 3/256: Read in a page + oob data */
            this->read_buf(mtd, data_poi, end);
            this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]);
            if (mtd->oobblock == 512)
                this->calculate_ecc(mtd, &data_poi[256], &ecc_calc[3]);
            break;    

            
        case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data: Read in first 256 byte, get ecc, */
            this->enable_hwecc(mtd, NAND_ECC_READ);    
            this->read_buf(mtd, data_poi, ecc);
            this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]);    /* read from hardware */

            
            if (mtd->oobblock == 512) { /* read second, if pagesize = 512 */
                this->enable_hwecc(mtd, NAND_ECC_READ);    
                this->read_buf(mtd, &data_poi[ecc], end-ecc);
                this->calculate_ecc(mtd, &data_poi[256], &ecc_calc[3]); /* read from hardware */
            }                    
            break;                        

                
        case NAND_ECC_HW3_512:    
        case NAND_ECC_HW6_512: /* Hardware ECC 3/6 byte / 512 byte data : Read in a page  */
            this->enable_hwecc(mtd, NAND_ECC_READ);    
            this->read_buf(mtd, data_poi, end);
            this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]);    /* read from hardware */
            break;


        default:
            printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
            BUG();    
        }


        /* read oobdata */
        for (j = 0; j <  mtd->oobsize; j++) 
            oob_data[oob + j] = this->read_byte(mtd);

        
        /* Skip ECC, if not active */
        if (eccmode == NAND_ECC_NONE)
            goto readdata;    

        
        /* Pick the ECC bytes out of the oob data */
        for (j = 0; j < 6; j++)
            ecc_code[j] = oob_data[oob + oob_config[j]];


        /* correct data, if neccecary */
        ecc_status = this->correct_data(mtd, &data_poi[0], &ecc_code[0], &ecc_calc[0]);
        /* check, if we have a fs supplied oob-buffer */
        if (oob_buf) { 
            oob += mtd->oobsize;
            *((int *)&oob_data[oob]) = ecc_status;
            oob += sizeof(int);
        }
        if (ecc_status == -1) {    
            DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);
            ecc_failed++;
        }

        
        if (mtd->oobblock == 512 && eccmode != NAND_ECC_HW3_512) {
            ecc_status = this->correct_data(mtd, &data_poi[256], &ecc_code[3], &ecc_calc[3]);
            if (oob_buf) {
                *((int *)&oob_data[oob]) = ecc_status;
                oob += sizeof(int);
            }
            if (ecc_status == -1) {
                DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);
                ecc_failed++;
            }
        }
readdata:
        if (col || (len - read) < end) { 
            for (j = col; j < end && read < len; j++)
                buf[read++] = data_poi[j];
        } else        
            read += mtd->oobblock;
        /* For subsequent reads align to page boundary. */
        col = 0;
        /* Increment page address */
        page++;
    }


    /* De-select the NAND device */
    this->select_chip(mtd, -1);


    /* Wake up anyone waiting on the device */
    spin_lock_bh (&this->chip_lock);
    this->state = FL_READY;
    wake_up (&this->wq);
    spin_unlock_bh (&this->chip_lock);


    /*
     * Return success, if no ECC failures, else -EIO
     * fs driver will take care of that, because
     * retlen == desired len and result == -EIO
     */
    *retlen = read;
    return ecc_failed ? -EIO : 0;
}


/*
 * NAND read out-of-band
 */
static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
{
    int i, col, page;
    int erase_state = 0;
    struct nand_chip *this = mtd->priv;


    DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);


    /* Shift to get page */
    page = ((int) from) >> this->page_shift;


    /* Mask to get column */
    col = from & 0x0f;


    /* Initialize return length value */
    *retlen = 0;


    /* Do not allow reads past end of device */
    if ((from + len) > mtd->size) {
        DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n");
        *retlen = 0;
        return -EINVAL;
    }


    /* Grab the lock and see if the device is available */
    nand_get_chip (this, mtd , FL_READING, &erase_state);


    /* Select the NAND device */
    this->select_chip(mtd, 0);


    /* Send the read command */
    this->cmdfunc (mtd, NAND_CMD_READOOB, col, page);
    /* 
     * Read the data, if we read more than one page
     * oob data, let the device transfer the data !
     */
    i = 0;
    while (i < len) {
        int thislen = (mtd->oobsize - col) & (mtd->oobsize - 1);
        if (!thislen)
            thislen = mtd->oobsize;
        thislen = min_t(int, thislen, len);
        this->read_buf(mtd, &buf[i], thislen);
        i += thislen;
        col += thislen;
        /* Delay between pages */
        udelay (this->chip_delay);
    }
    /* De-select the NAND device */
    this->select_chip(mtd, -1);


    /* Wake up anyone waiting on the device */
    spin_lock_bh (&this->chip_lock);
    this->state = FL_READY;
    wake_up (&this->wq);
    spin_unlock_bh (&this->chip_lock);


    /* Return happy */
    *retlen = len;
    return 0;
}


#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0

/*
*    Use NAND write ECC
*/
static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
{
    return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL));
}               
/*
 * NAND write with ECC
 */
static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
               size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel)
{
    int page, ret = 0, oob = 0, written = 0;
    struct nand_chip *this = mtd->priv;


    DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);


    /* Do not allow write past end of device */
    if ((to + len) > mtd->size) {
        DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n");
        return -EINVAL;
    }


    /* reject writes, which are not page aligned */    
    if (NOTALIGNED (to) || NOTALIGNED(len)) {
        printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
        return -EINVAL;
    }


    // if oobsel is NULL, use chip defaults
    if (oobsel == NULL) 
        oobsel = &mtd->oobinfo;        


    /* Shift to get page */
    page = ((int) to) >> this->page_shift;


    /* Grab the lock and see if the device is available */
    nand_get_chip (this, mtd, FL_WRITING, NULL);


    /* Select the NAND device */
    this->select_chip(mtd, 0);


    /* Check the WP bit */
    this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
    if (!(this->read_byte(mtd) & 0x80)) {
        DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Device is write protected!!!\n");
        ret = -EIO;
        goto out;
    }


    /* Loop until all data is written */
    while (written < len) {
        int cnt = mtd->oobblock;
        this->data_poi = (u_char*) &buf[written];
        /* We use the same function for write and writev */
        if (eccbuf) {
            ret = nand_write_page (mtd, this, page, &eccbuf[oob], oobsel);
            oob += mtd->oobsize;
        } else 
            ret = nand_write_page (mtd, this, page, NULL, oobsel);    

        
        if (ret)
            goto out;


        /* Update written bytes count */
        written += cnt;
        /* Increment page address */
        page++;
    }


out:
    /* De-select the NAND device */
    this->select_chip(mtd, -1);


    /* Wake up anyone waiting on the device */
    spin_lock_bh (&this->chip_lock);
    this->state = FL_READY;
    wake_up (&this->wq);
    spin_unlock_bh (&this->chip_lock);


    *retlen = written;
    return ret;
}


static u_char ffchars[] = {
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};


/*
 * NAND write out-of-band
 */
static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
{
    int column, page, status, ret = 0;
    struct nand_chip *this = mtd->priv;
#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
    int i;
#endif


    DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);


    /* Shift to get page */
    page = ((int) to) >> this->page_shift;


    /* Mask to get column */
    column = to & 0x1f;


    /* Initialize return length value */
    *retlen = 0;


    /* Do not allow write past end of page */
    if ((column + len) > mtd->oobsize) {
        DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n");
        return -EINVAL;
    }


    /* Grab the lock and see if the device is available */
    nand_get_chip (this, mtd, FL_WRITING, NULL);


    /* Select the NAND device */
    this->select_chip(mtd, 0);


    /* Reset the chip. Some chips (like the Toshiba TC5832DC found
       in one of my DiskOnChip 2000 test units) will clear the whole
       data page too if we don't do this. I have no clue why, but
       I seem to have 'fixed' it in the doc2000 driver in
       August 1999.  dwmw2. */
    this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);


    /* Check the WP bit */
    this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
    if (!(this->read_byte(mtd) & 0x80)) {
        DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Device is write protected!!!\n");
        ret = -EIO;
        goto out;
    }
    /* Write out desired data */
    this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page);


    /* prepad 0xff for partial programming */
    this->write_buf(mtd, ffchars, column);
    /* write data */
    this->write_buf(mtd, buf, len);
    /* postpad 0xff for partial programming */
    this->write_buf(mtd, ffchars, mtd->oobsize - (len+column));


    /* Send command to program the OOB data */
    this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);


    status = this->waitfunc (mtd, this, FL_WRITING);


    /* See if device thinks it succeeded */
    if (status & 0x01) {
        DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page);
        ret = -EIO;
        goto out;
    }
    /* Return happy */
    *retlen = len;


#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
    /* Send command to read back the data */
    this->cmdfunc (mtd, NAND_CMD_READOOB, column, page);


    /* Loop through and verify the data */
    for (i = 0; i < len; i++) {
        if (buf[i] != this->read_byte(mtd)) {
            DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page);
            ret = -EIO;
            goto out;
        }
    }
#endif


out:
    /* De-select the NAND device */
    this->select_chip(mtd, -1);


    /* Wake up anyone waiting on the device */
    spin_lock_bh (&this->chip_lock);
    this->state = FL_READY;
    wake_up (&this->wq);
    spin_unlock_bh (&this->chip_lock);


    return ret;
}



/*
 * NAND write with iovec
 */
static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, 
        loff_t to, size_t * retlen)
{
    return (nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, 0));    
}


static int nand_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, 
        loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel)
{
    int i, page, len, total_len, ret = 0, written = 0;
    struct nand_chip *this = mtd->priv;


    /* Calculate total length of data */
    total_len = 0;
    for (i = 0; i < count; i++)
        total_len += (int) vecs[i].iov_len;


    DEBUG (MTD_DEBUG_LEVEL3,
           "nand_writev: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count);


    /* Do not allow write past end of page */
    if ((to + total_len) > mtd->size) {
        DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Attempted write past end of device\n");
        return -EINVAL;
    }


    /* reject writes, which are not page aligned */    
    if (NOTALIGNED (to) || NOTALIGNED(total_len)) {
        printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
        return -EINVAL;
    }


    // if oobsel is NULL, use chip defaults
    if (oobsel == NULL) 
        oobsel = &mtd->oobinfo;        


    /* Shift to get page */
    page = ((int) to) >> this->page_shift;


    /* Grab the lock and see if the device is available */
    nand_get_chip (this, mtd, FL_WRITING, NULL);


    /* Select the NAND device */
    this->select_chip(mtd, 0);


    /* Check the WP bit */
    this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
    if (!(this->read_byte(mtd) & 0x80)) {
        DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Device is write protected!!!\n");
        ret = -EIO;
        goto out;
    }


    /* Loop until all iovecs' data has been written */
    len = 0;
    while (count) {
        /* 
         *  Check, if the tuple gives us not enough data for a 
         *  full page write. Then we can use the iov direct, 
         *  else we have to copy into data_buf.        
         */
        if ((vecs->iov_len - len) >= mtd->oobblock) {
            this->data_poi = (u_char *) vecs->iov_base;
            this->data_poi += len;
            len += mtd->oobblock; 
            /* Check, if we have to switch to the next tuple */
            if (len >= (int) vecs->iov_len) {
                vecs++;
                len = 0;
                count--;
            }
        } else {
            /*
             * Read data out of each tuple until we have a full page
             * to write or we've read all the tuples.
             */
            int cnt = 0;
            while ((cnt < mtd->oobblock) && count) {
                if (vecs->iov_base != NULL && vecs->iov_len) {
                    this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++];
                }
                /* Check, if we have to switch to the next tuple */
                if (len >= (int) vecs->iov_len) {
                    vecs++;
                    len = 0;
                    count--;
                }
            }    
            this->data_poi = this->data_buf;    
        }

        
        /* We use the same function for write and writev !) */
        ret = nand_write_page (mtd, this, page, NULL, oobsel);
        if (ret)
            goto out;


        /* Update written bytes count */
        written += mtd->oobblock;;


        /* Increment page address */
        page++;
    }


out:
    /* De-select the NAND device */
    this->select_chip(mtd, -1);


    /* Wake up anyone waiting on the device */
    spin_lock_bh (&this->chip_lock);
    this->state = FL_READY;
    wake_up (&this->wq);
    spin_unlock_bh (&this->chip_lock);


    *retlen = written;
    return ret;
}


/*
 * NAND erase a block
 */
static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
{
    int page, len, status, pages_per_block, ret;
    struct nand_chip *this = mtd->priv;
    DECLARE_WAITQUEUE (wait, current);


    DEBUG (MTD_DEBUG_LEVEL3,
           "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len);


    /* Start address must align on block boundary */
    if (instr->addr & (mtd->erasesize - 1)) {
        DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
        return -EINVAL;
    }


    /* Length must align on block boundary */
    if (instr->len & (mtd->erasesize - 1)) {
        DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n");
        return -EINVAL;
    }


    /* Do not allow erase past end of device */
    if ((instr->len + instr->addr) > mtd->size) {
        DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n");
        return -EINVAL;
    }


    /* Grab the lock and see if the device is available */
    nand_get_chip (this, mtd, FL_ERASING, NULL);


    /* Shift to get first page */
    page = (int) (instr->addr >> this->page_shift);


    /* Calculate pages in each block */
    pages_per_block = mtd->erasesize / mtd->oobblock;


    /* Select the NAND device */
    this->select_chip(mtd, 0);


    /* Check the WP bit */
    this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
    if (!(this->read_byte(mtd) & 0x80)) {
        DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n");
        instr->state = MTD_ERASE_FAILED;
        goto erase_exit;
    }


    /* Loop through the pages */
    len = instr->len;


    instr->state = MTD_ERASING;


    while (len) {
        /* Check if we have a bad block, we do not erase bad blocks ! */
        if (this->block_bad(mtd, page)) {
            printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page);


//#ifndef CONFIG_MIZI // BUSHI
            instr->state = MTD_ERASE_FAILED;
            goto erase_exit;
//#endif /* CONFIG_MIZI */
        }


        /* Send commands to erase a page */
        this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
        this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);


        spin_unlock_bh (&this->chip_lock);
        status = this->waitfunc (mtd, this, FL_ERASING);


        /* Get spinlock, in case we exit */
        spin_lock_bh (&this->chip_lock);
        /* See if block erase succeeded */
        if (status & 0x01) {
            DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page);
            instr->state = MTD_ERASE_FAILED;
            goto erase_exit;
        }

        
        /* Check, if we were interupted */
        if (this->state == FL_ERASING) {
            /* Increment page address and decrement length */
            len -= mtd->erasesize;
            page += pages_per_block;
        }
        /* Release the spin lock */
        spin_unlock_bh (&this->chip_lock);
erase_retry:
        spin_lock_bh (&this->chip_lock);
        /* Check the state and sleep if it changed */
        if (this->state == FL_ERASING || this->state == FL_READY) {
            /* Select the NAND device again, if we were interrupted */
            this->state = FL_ERASING;
            this->select_chip(mtd, 0);
            continue;
        } else {
            set_current_state (TASK_UNINTERRUPTIBLE);
            add_wait_queue (&this->wq, &wait);
            spin_unlock_bh (&this->chip_lock);
            schedule ();
            remove_wait_queue (&this->wq, &wait);
            goto erase_retry;
        }
    }
    instr->state = MTD_ERASE_DONE;


erase_exit:
    /* De-select the NAND device */
    this->select_chip(mtd, -1);
    spin_unlock_bh (&this->chip_lock);


    ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;;
    /* Do call back function */
    if (!ret && instr->callback)
        instr->callback (instr);


    /* The device is ready */
    spin_lock_bh (&this->chip_lock);
    this->state = FL_READY;
    wake_up (&this->wq);
    spin_unlock_bh (&this->chip_lock);


    /* Return more or less happy */
    return ret;
}


/*
 * NAND sync
 */
static void nand_sync (struct mtd_info *mtd)
{
    struct nand_chip *this = mtd->priv;
    DECLARE_WAITQUEUE (wait, current);


    DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");


retry:
    /* Grab the spinlock */
    spin_lock_bh (&this->chip_lock);


    /* See what's going on */
    switch (this->state) {
    case FL_READY:
    case FL_SYNCING:
        this->state = FL_SYNCING;
        spin_unlock_bh (&this->chip_lock);
        break;


    default:
        /* Not an idle state */
        add_wait_queue (&this->wq, &wait);
        spin_unlock_bh (&this->chip_lock);
        schedule ();


        remove_wait_queue (&this->wq, &wait);
        goto retry;
    }


    /* Lock the device */
    spin_lock_bh (&this->chip_lock);


    /* Set the device to be ready again */
    if (this->state == FL_SYNCING) {
        this->state = FL_READY;
        wake_up (&this->wq);
    }


    /* Unlock the device */
    spin_unlock_bh (&this->chip_lock);
}


/*
 * Scan for the NAND device
 */
int nand_scan (struct mtd_info *mtd, int maxchips)
{
    int i, nand_maf_id, nand_dev_id;
    struct nand_chip *this = mtd->priv;


    /* check for proper chip_delay setup, set 20us if not */
    if (!this->chip_delay)
        this->chip_delay = 20;


    /* check, if a user supplied command function given */
    if (this->cmdfunc == NULL)
        this->cmdfunc = nand_command;


    /* check, if a user supplied wait function given */
    if (this->waitfunc == NULL)
        this->waitfunc = nand_wait;


    if (!this->block_bad)
        this->block_bad = nand_block_bad;
    if (!this->select_chip)
        this->select_chip = nand_select_chip;
    if (!this->write_byte)
        this->write_byte = nand_write_byte;
    if (!this->read_byte)
        this->read_byte = nand_read_byte;
    if (!this->write_buf)
        this->write_buf = nand_write_buf;
    if (!this->read_buf)
        this->read_buf = nand_read_buf;
    if (!this->verify_buf)
        this->verify_buf = nand_verify_buf;


    /* Select the device */
    this->select_chip(mtd, 0);


    /* Send the command for reading device ID */
    this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);


    /* Read manufacturer and device IDs */
    nand_maf_id = this->read_byte(mtd);
    nand_dev_id = this->read_byte(mtd);


    /* Print and store flash device information */
    for (i = 0; nand_flash_ids[i].name != NULL; i++) {
        if (nand_dev_id == nand_flash_ids[i].id && !mtd->size) {
            mtd->name = nand_flash_ids[i].name;
            mtd->erasesize = nand_flash_ids[i].erasesize;
            mtd->eccsize = 256;
            this->chipshift = nand_flash_ids[i].chipshift;
            if (nand_flash_ids[i].page256) {
                mtd->oobblock = 256;
                mtd->oobsize = 8;
                this->page_shift = 8;
            } else {
                mtd->oobblock = 512;
                mtd->oobsize = 16;
                this->page_shift = 9;
            }
            /* Try to identify manufacturer */
            for (i = 0; nand_manuf_ids[i].id != 0x0; i++) {
                if (nand_manuf_ids[i].id == nand_maf_id)
                    break;
            }    
            printk (KERN_INFO "NAND device: Manufacturer ID:"
                " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, 
                nand_manuf_ids[i].name , mtd->name);
            break;
        }
    }


    if (!mtd->name) {
        printk (KERN_WARNING "No NAND device found!!!\n");
        return 1;
    }


    for (i=1; i < maxchips; i++) {
        this->select_chip(mtd, i);


        /* Send the command for reading device ID */
        this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);


        /* Read manufacturer and device IDs */
        if (nand_maf_id != this->read_byte(mtd) ||
            nand_dev_id != this->read_byte(mtd))
            break;
    }
    if (i > 1)
        printk(KERN_INFO "%d NAND chips detected\n", i);


    mtd->size = (1 << this->chipshift) /* * i when we fix the rest of the code */;


    /* 
     * check ECC mode, default to software
     * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize
     * fallback to software ECC 
    */
    this->eccsize = 256;    /* set default eccsize */    


    switch (this->eccmode) {


    case NAND_ECC_HW3_512: 
    case NAND_ECC_HW6_512: 
        if (mtd->oobblock == 256) {
            printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n");
            this->eccmode = NAND_ECC_SOFT;
            this->calculate_ecc = nand_calculate_ecc;
            this->correct_data = nand_correct_data;
            break;        
        } else 
            this->eccsize = 512; /* set eccsize to 512 and fall through for function check */


    case NAND_ECC_HW3_256:
        if (this->calculate_ecc && this->correct_data && this->enable_hwecc)
            break;
        printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n");
        BUG();    


    case NAND_ECC_NONE: 
        printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");
        this->eccmode = NAND_ECC_NONE;
        break;


    case NAND_ECC_SOFT:    
        this->calculate_ecc = nand_calculate_ecc;
        this->correct_data = nand_correct_data;
        break;


    default:
        printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
        BUG();    
    }    

    
    /* Initialize state, waitqueue and spinlock */
    this->state = FL_READY;
    init_waitqueue_head (&this->wq);
    spin_lock_init (&this->chip_lock);


    /* De-select the device */
    this->select_chip(mtd, -1);


    /* Fill in remaining MTD driver data */
    mtd->type = MTD_NANDFLASH;
    mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
    mtd->ecctype = MTD_ECC_SW;
    mtd->erase = nand_erase;
    mtd->point = NULL;
    mtd->unpoint = NULL;
    mtd->read = nand_read;
    mtd->write = nand_write;
    mtd->read_ecc = nand_read_ecc;
    mtd->write_ecc = nand_write_ecc;
    mtd->read_oob = nand_read_oob;
    mtd->write_oob = nand_write_oob;
    mtd->readv = NULL;
    mtd->writev = nand_writev;
    mtd->writev_ecc = nand_writev_ecc;
    mtd->sync = nand_sync;
    mtd->lock = NULL;
    mtd->unlock = NULL;
    mtd->suspend = NULL;
    mtd->resume = NULL;
    mtd->owner = THIS_MODULE;


    /* Return happy */
    return 0;
}


EXPORT_SYMBOL (nand_scan);

MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("Steven J. Hill <>, Thomas Gleixner <>");
MODULE_DESCRIPTION ("Generic NAND flash driver code");