[Yaffs] no erasedblocks available

bandwidthcrunch bandwidthcrunch <bandwidthcrunch@gmail.com>
Thu, 4 Nov 2004 15:11:26 +0530


------=_Part_2020_19033482.1099561286164
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

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 .

------=_Part_2020_19033482.1099561286164
Content-Type: text/x-csrc; name="nand.c"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment; filename="nand.c"

/*
 *  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.
 *  =20
 *=09Additional technical information is available on
 *=09http://www.linux-mtd.infradead.org/tech/nand.html
 *=09
 *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
 * =09=09  2002 Thomas Gleixner (tglx@linutronix.de)
 *
 *  10-29-2001  Thomas Gleixner (tglx@linutronix.de)
 * =09=09- Changed nand_chip structure for controlline function to
 *=09=09support different hardware structures (Access to
 *=09=09controllines ALE,CLE,NCE via hardware specific function.=20
 *=09=09- exit out of "failed erase block" changed, to avoid
 *=09=09driver hangup
 *=09=09- init_waitqueue_head added in function nand_scan !!
 *
 *  01-30-2002  Thomas Gleixner (tglx@linutronix.de)
 *=09=09change in nand_writev to block invalid vecs entries
 *
 *  02-11-2002  Thomas Gleixner (tglx@linutronix.de)
 *=09=09- major rewrite to avoid duplicated code
 *=09=09  common nand_write_page function =20
 *=09=09  common get_chip function=20
 *=09=09- added oob_config structure for out of band layouts
 *=09=09- write_oob changed for partial programming
 *=09=09- read cache for faster access for subsequent reads
 *=09=09from the same page.
 *=09=09- support for different read/write address
 *=09=09- support for device ready/busy line
 *=09=09- read oob for more than one page enabled
 *
 *  02-27-2002=09Thomas Gleixner (tglx@linutronix.de)
 *=09=09- command-delay can be programmed
 *=09=09- fixed exit from erase with callback-function enabled
 *
 *  03-21-2002  Thomas Gleixner (tglx@linutronix.de)
 *=09=09- DEBUG improvements provided by Elizabeth Clarke=20
 *=09=09(eclarke@aminocom.com)
 *=09=09- added zero check for this->chip_delay
 *
 *  04-03-2002  Thomas Gleixner (tglx@linutronix.de)
 *=09=09- added added hw-driver supplied command and wait functions
 *=09=09- changed blocking for erase (erase suspend enabled)
 *=09=09- check pointers before accessing flash provided by
 *=09=09John Hall (john.hall@optionexist.co.uk)
 *
 *  04-09-2002  Thomas Gleixner (tglx@linutronix.de)
 *=09=09- nand_wait repaired
 *
 *  04-28-2002  Thomas Gleixner (tglx@linutronix.de)=09
 *=09=09- OOB config defines moved to nand.h=20
 *
 *  08-01-2002  Thomas Gleixner (tglx@linutronix.de)=09
 *=09=09- changed my mailaddress, added pointer to tech/nand.html
 *
 *  08-07-2002 =09Thomas Gleixner (tglx@linutronix.de)
 *=09=09forced bad block location to byte 5 of OOB, even if
 *=09=09CONFIG_MTD_NAND_ECC_JFFS2 is not set, to prevent
 *=09=09erase /dev/mtdX from erasing bad blocks and destroying
 *=09=09bad block info
 *
 *  08-10-2002 =09Thomas Gleixner (tglx@linutronix.de)
 *=09=09Fixed writing tail of data. Thanks to Alice Hennessy
 *=09=09<ahennessy@mvista.com>.
 *
 *  08-10-2002 =09Thomas Gleixner (tglx@linutronix.de)
 *=09=09nand_read_ecc and nand_write_page restructured to support
 *=09=09hardware ECC. Thanks to Steven Hein (ssh@sgi.com)
 *=09=09for basic implementation and suggestions.
 *=09=093 new pointers in nand_chip structure:
 *=09=09calculate_ecc, correct_data, enabled_hwecc =09=09=09=09=09=20
 *=09=09forcing all hw-drivers to support page cache
 *=09=09eccvalid_pos is now mandatory
 *
 *  08-17-2002=09tglx: fixed signed/unsigned missmatch in write.c
 *=09=09Thanks to Ken Offer <koffer@arlut.utexas.edu> =09
 *
 *  08-29-2002  tglx: use buffered read/write only for non pagealigned=20
 *=09=09access, speed up the aligned path by using the fs-buffer
 *=09=09reset chip removed from nand_select(), implicit done
 *=09=09only, when erase is interrupted
 *=09=09waitfuntion use yield, instead of schedule_timeout
 *=09=09support for 6byte/512byte hardware ECC
 *=09=09read_ecc, write_ecc extended for different oob-layout
 *=09=09selections: Implemented NAND_NONE_OOB, NAND_JFFS2_OOB,
 *=09=09NAND_YAFFS_OOB. fs-driver gives one of these constants
 *=09=09to select the oob-layout fitting the filesystem.
 *=09=09oobdata can be read together with the raw data, when
 *=09=09the fs-driver supplies a big enough buffer.
 *=09=09size =3D 12 * number of pages to read (256B pagesize)
 *=09=09       24 * number of pages to read (512B pagesize)
 *=09=09the buffer contains 8/16 byte oobdata and 4/8 byte
 *=09=09returncode from calculate_ecc
 *=09=09oobdata can be given from filesystem to program them
 *=09=09in one go together with the raw data. ECC codes are
 *=09=09filled in at the place selected by oobsel.
 *
 *  09-04-2002  tglx: fixed write_verify (John Hall (john.hall@optionexist.=
co.uk))
 *
 *  11-11-2002  tglx: fixed debug output in nand_write_page=20
 *=09=09(John Hall (john.hall@optionexist.co.uk))
 *
 *  11-25-2002  tglx: Moved device ID/ manufacturer ID from nand_ids.h
 *=09=09Splitted device ID and manufacturer ID table.=20
 *=09=09Removed CONFIG_MTD_NAND_ECC, as it defaults to ECC_NONE for
 *=09=09mtd->read / mtd->write and is controllable by the fs driver
 *=09=09for mtd->read_ecc / mtd->write_ecc
 *=09=09some minor cleanups
 *
 *  12-05-2002  tglx: Dave Ellis (DGE@sixnetio) provided the fix for
 *=09=09WRITE_VERIFY long time ago. Thanks for remembering me.=09
 *
 *  02-14-2003  tglx: Reject non page aligned writes =09
 *=09=09Fixed ecc select in nand_write_page to match semantics.=20
 *
 *  02-18-2003=09tglx: Changed oobsel to pointer. Added a default oob-selec=
tor
 *=09=09=09
 *  02-18-2003=09tglx: Implemented oobsel again. Now it uses a pointer to
 +=09=09a structure, which will be supplied by a filesystem driver
 *=09=09If NULL is given, then the defaults (none or defaults
 *=09=09supplied by ioctl (MEMSETOOBSEL) are used.
 *=09=09For partitions the partition defaults are used (mtdpart.c)
 *
 *  06-04-2003  tglx: fix compile errors and fix write verify problem for
 *=09=09some chips, which need either a delay between the readback
 *=09=09and the next write command or have the CE removed. The
 *=09=09CE disable/enable is much faster than a 20us delay and
 *=09=09it should work on all available chips.
 *=09
 * $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,
=09=09=09  size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobi=
nfo *oobsel);
static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, si=
ze_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,
=09=09=09   size_t * retlen, const u_char * buf, u_char * eccbuf, struct na=
nd_oobinfo *oobsel);
static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, siz=
e_t * retlen, const u_char *buf);
static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs,
=09=09=09unsigned long count, loff_t to, size_t * retlen);
static int nand_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs,
=09=09=09unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, s=
truct 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, i=
nt page, u_char *oob_buf,  struct nand_oobinfo *oobsel);

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

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

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

=09default:
=09=09BUG();
=09}
}

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

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

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

=09for (i=3D0; i<len; i++)
=09=09buf[i] =3D readb(this->IO_ADDR_R);
}

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

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

=09return 0;
}

/* Appropriate chip should already be selected */
static int nand_block_bad(struct mtd_info *mtd, unsigned long page)
{
=09struct nand_chip *this =3D mtd->priv;
=09
=09this->cmdfunc (mtd, NAND_CMD_READOOB, NAND_BADBLOCK_POS, page);
=09if (this->read_byte(mtd) !=3D 0xff)
=09=09return 1;

=09return 0;
}

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

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

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

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

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

=09=09/* Serially input address */
=09=09if (column !=3D -1)
=09=09=09this->write_byte(mtd, column);
=09=09if (page_addr !=3D -1) {
=09=09=09this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
=09=09=09this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
=09=09=09/* One more address cycle for higher density devices */
=09=09=09if (mtd->size & 0x0c000000)=20
=09=09=09=09this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f=
));
=09=09}
=09=09/* Latch in address */
=09=09this->hwcontrol(mtd, NAND_CTL_CLRALE);
=09}
=09
=09/*=20
=09 * program and erase have their own busy handlers=20
=09 * status and sequential in needs no delay
=09*/
=09switch (command) {
=09=09=09
=09case NAND_CMD_PAGEPROG:
=09case NAND_CMD_ERASE1:
=09case NAND_CMD_ERASE2:
=09case NAND_CMD_SEQIN:
=09case NAND_CMD_STATUS:
=09=09return;

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

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

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

=09DECLARE_WAITQUEUE (wait, current);

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

=09if (this->state =3D=3D FL_READY) {
=09=09this->state =3D new_state;
=09=09if (new_state !=3D FL_ERASING)
=09=09=09spin_unlock_bh (&this->chip_lock);
=09=09return;
=09}
#if 0 /* This was broken. And of dubious utility */
=09if (this->state =3D=3D FL_ERASING) {
=09=09if (new_state !=3D FL_ERASING) {
=09=09=09this->state =3D new_state;
=09=09=09spin_unlock_bh (&this->chip_lock);
=09=09=09this->select_chip(mtd, 0);=09/* select in any case */
=09=09=09this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
=09=09=09return;
=09=09}
=09}
#endif
=09set_current_state (TASK_UNINTERRUPTIBLE);
=09add_wait_queue (&this->wq, &wait);
=09spin_unlock_bh (&this->chip_lock);
=09schedule ();
=09remove_wait_queue (&this->wq, &wait);
=09goto 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=20
 * general NAND and SmartMedia specs
 *
*/
static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int stat=
e)
{

=09unsigned long=09timeo =3D jiffies;
=09int=09status;
=09
=09if (state =3D=3D FL_ERASING)
=09=09 timeo +=3D (HZ * 400) / 1000;
=09else
=09=09 timeo +=3D (HZ * 20) / 1000;

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

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

=09return status;
}

/*
 *=09Nand_page_program function is used for write and writev !
 *=09This function will always program a full page of data
 *=09If 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, i=
nt page, u_char *oob_buf,  struct nand_oobinfo *oobsel)
{
=09int =09i, status;
=09u_char=09ecc_code[6], *oob_data;
=09int=09eccmode =3D oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
=09int  =09*oob_config =3D oobsel->eccpos;
=09
=09/* pad oob area, if we have no oob buffer from fs-driver */
=09if (!oob_buf) {
=09=09oob_data =3D &this->data_buf[mtd->oobblock];
=09=09for (i =3D 0; i < mtd->oobsize; i++)
=09=09=09oob_data[i] =3D 0xff;
=09} else=20
=09=09oob_data =3D oob_buf;
=09
=09/* Send command to begin auto page programming */
=09this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page);

=09/* Write out complete page of data, take care of eccmode */
=09switch (eccmode) {
=09/* No ecc and software ecc 3/256, write all */
=09case NAND_ECC_NONE:
//printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recom=
mended\n");
=09=09this->write_buf(mtd, this->data_poi, mtd->oobblock);
=09=09break;
=09case NAND_ECC_SOFT:
=09=09this->calculate_ecc(mtd, &this->data_poi[0], &(ecc_code[0]));
=09=09for (i =3D 0; i < 3; i++)
=09=09=09oob_data[oob_config[i]] =3D ecc_code[i];
=09=09/* Calculate and write the second ECC for 512 Byte page size */
=09=09if (mtd->oobblock =3D=3D 512) {
=09=09=09this->calculate_ecc(mtd, &this->data_poi[256], &(ecc_code[3]));
=09=09=09for (i =3D 3; i < 6; i++)
=09=09=09=09oob_data[oob_config[i]] =3D ecc_code[i];
=09=09}=20
=09=09this->write_buf(mtd, this->data_poi, mtd->oobblock);
=09=09break;
=09=09
=09/* Hardware ecc 3 byte / 256 data, write first half, get ecc, then secon=
d, if 512 byte pagesize */=09
=09case NAND_ECC_HW3_256:=09=09
=09=09this->enable_hwecc(mtd, NAND_ECC_WRITE);=09/* enable hardware ecc log=
ic for write */
=09=09this->write_buf(mtd, this->data_poi, mtd->eccsize);
=09=09
=09=09this->calculate_ecc(mtd, NULL, &(ecc_code[0]));
=09=09for (i =3D 0; i < 3; i++)
=09=09=09oob_data[oob_config[i]] =3D ecc_code[i];
=09=09=09
=09=09if (mtd->oobblock =3D=3D 512) {
=09=09=09this->enable_hwecc(mtd, NAND_ECC_WRITE);=09/* enable hardware ecc =
logic for write*/
=09=09=09this->write_buf(mtd, &this->data_poi[mtd->eccsize], mtd->oobblock =
- mtd->eccsize);
=09=09=09this->calculate_ecc(mtd, NULL, &(ecc_code[3]));
=09=09=09for (i =3D 3; i < 6; i++)
=09=09=09=09oob_data[oob_config[i]] =3D ecc_code[i];
=09=09}
=09=09break;
=09=09=09=09
=09/* Hardware ecc 3 byte / 512 byte data, write full page */=09
=09case NAND_ECC_HW3_512:=09
=09=09this->enable_hwecc(mtd, NAND_ECC_WRITE);=09/* enable hardware ecc log=
ic */
=09=09this->write_buf(mtd, this->data_poi, mtd->oobblock);
=09=09this->calculate_ecc(mtd, NULL, &(ecc_code[0]));
=09=09for (i =3D 0; i < 3; i++)
=09=09=09oob_data[oob_config[i]] =3D ecc_code[i];
=09=09break;

=09/* Hardware ecc 6 byte / 512 byte data, write full page */=09
=09case NAND_ECC_HW6_512:=09
=09=09this->enable_hwecc(mtd, NAND_ECC_WRITE);=09/* enable hardware ecc log=
ic */
=09=09this->write_buf(mtd, this->data_poi, mtd->oobblock);
=09=09this->calculate_ecc(mtd, NULL, &(ecc_code[0]));
=09=09for (i =3D 0; i < 6; i++)
=09=09=09oob_data[oob_config[i]] =3D ecc_code[i];
=09=09break;
=09=09
=09default:
=09=09printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
=09=09BUG();=09
=09}=09
=09
=09/* Write out OOB data */
=09this->write_buf(mtd, oob_data, mtd->oobsize);

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

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

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

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

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

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

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

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

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

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


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

=09// use chip default if zero
=09if (oobsel =3D=3D NULL)
=09=09oobsel =3D &mtd->oobinfo;
=09=09
=09eccmode =3D oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
=09oob_config =3D oobsel->eccpos;

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

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

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

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

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

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

=09end =3D mtd->oobblock;
=09ecc =3D mtd->eccsize;

=09/* Send the read command */
=09this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
=09
=09/* Loop until all data read */
=09while (read < len) {
=09=09
=09=09/* If we have consequent page reads, apply delay or wait for ready/bu=
sy pin */
=09=09if (read) {
=09=09=09if (!this->dev_ready)=20
=09=09=09=09udelay (this->chip_delay);
=09=09=09else
=09=09=09=09while (!this->dev_ready(mtd));=09
=09=09}

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

=09=09/* get oob area, if we have no oob buffer from fs-driver */
=09=09if (!oob_buf) {
=09=09=09oob_data =3D &this->data_buf[end];
=09=09=09oob =3D 0;
=09=09} =09
=09=09=09
=09=09j =3D 0;
=09=09switch (eccmode) {
=09=09case NAND_ECC_NONE: {=09/* No ECC, Read in a page */
=09=09=09static unsigned long lastwhinge =3D 0;
=09=09=09if ((lastwhinge / HZ) !=3D (jiffies / HZ)) {
=09=09//=09=09printk (KERN_WARNING "Reading data from NAND FLASH without EC=
C is not recommended\n");
=09=09=09=09lastwhinge =3D jiffies;
=09=09=09}
=09=09=09this->read_buf(mtd, data_poi, end);
=09=09=09break;
=09=09}
=09=09=09
=09=09case NAND_ECC_SOFT:=09/* Software ECC 3/256: Read in a page + oob dat=
a */
=09=09=09this->read_buf(mtd, data_poi, end);
=09=09=09this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]);
=09=09=09if (mtd->oobblock =3D=3D 512)
=09=09=09=09this->calculate_ecc(mtd, &data_poi[256], &ecc_calc[3]);
=09=09=09break;=09
=09=09=09
=09=09case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data: Read in=
 first 256 byte, get ecc, */
=09=09=09this->enable_hwecc(mtd, NAND_ECC_READ);=09
=09=09=09this->read_buf(mtd, data_poi, ecc);
=09=09=09this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]);=09/* read fr=
om hardware */
=09=09=09
=09=09=09if (mtd->oobblock =3D=3D 512) { /* read second, if pagesize =3D 51=
2 */
=09=09=09=09this->enable_hwecc(mtd, NAND_ECC_READ);=09
=09=09=09=09this->read_buf(mtd, &data_poi[ecc], end-ecc);
=09=09=09=09this->calculate_ecc(mtd, &data_poi[256], &ecc_calc[3]); /* read=
 from hardware */
=09=09=09}=09=09=09=09=09
=09=09=09break;=09=09=09=09=09=09
=09=09=09=09
=09=09case NAND_ECC_HW3_512:=09
=09=09case NAND_ECC_HW6_512: /* Hardware ECC 3/6 byte / 512 byte data : Rea=
d in a page  */
=09=09=09this->enable_hwecc(mtd, NAND_ECC_READ);=09
=09=09=09this->read_buf(mtd, data_poi, end);
=09=09=09this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]);=09/* read fr=
om hardware */
=09=09=09break;

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

=09=09/* read oobdata */
=09=09for (j =3D 0; j <  mtd->oobsize; j++)=20
=09=09=09oob_data[oob + j] =3D this->read_byte(mtd);
=09=09
=09=09/* Skip ECC, if not active */
=09=09if (eccmode =3D=3D NAND_ECC_NONE)
=09=09=09goto readdata;=09
=09=09
=09=09/* Pick the ECC bytes out of the oob data */
=09=09for (j =3D 0; j < 6; j++)
=09=09=09ecc_code[j] =3D oob_data[oob + oob_config[j]];

=09=09/* correct data, if neccecary */
=09=09ecc_status =3D this->correct_data(mtd, &data_poi[0], &ecc_code[0], &e=
cc_calc[0]);
=09=09/* check, if we have a fs supplied oob-buffer */
=09=09if (oob_buf) {=20
=09=09=09oob +=3D mtd->oobsize;
=09=09=09*((int *)&oob_data[oob]) =3D ecc_status;
=09=09=09oob +=3D sizeof(int);
=09=09}
=09=09if (ecc_status =3D=3D -1) {=09
=09=09=09DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page =
0x%08x\n", page);
=09=09=09ecc_failed++;
=09=09}
=09=09
=09=09if (mtd->oobblock =3D=3D 512 && eccmode !=3D NAND_ECC_HW3_512) {
=09=09=09ecc_status =3D this->correct_data(mtd, &data_poi[256], &ecc_code[3=
], &ecc_calc[3]);
=09=09=09if (oob_buf) {
=09=09=09=09*((int *)&oob_data[oob]) =3D ecc_status;
=09=09=09=09oob +=3D sizeof(int);
=09=09=09}
=09=09=09if (ecc_status =3D=3D -1) {
=09=09=09=09DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, pa=
ge 0x%08x\n", page);
=09=09=09=09ecc_failed++;
=09=09=09}
=09=09}
readdata:
=09=09if (col || (len - read) < end) {=20
=09=09=09for (j =3D col; j < end && read < len; j++)
=09=09=09=09buf[read++] =3D data_poi[j];
=09=09} else=09=09
=09=09=09read +=3D mtd->oobblock;
=09=09/* For subsequent reads align to page boundary. */
=09=09col =3D 0;
=09=09/* Increment page address */
=09=09page++;
=09}

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

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

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

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

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

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

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

=09/* Initialize return length value */
=09*retlen =3D 0;

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

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

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

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

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

=09/* Return happy */
=09*retlen =3D len;
=09return 0;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

=09*retlen =3D written;
=09return ret;
}

static u_char ffchars[] =3D {
=090xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
=090xff, 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, siz=
e_t * retlen, const u_char * buf)
{
=09int column, page, status, ret =3D 0;
=09struct nand_chip *this =3D mtd->priv;
#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
=09int i;
#endif

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

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

=09/* Mask to get column */
=09column =3D to & 0x1f;

=09/* Initialize return length value */
=09*retlen =3D 0;

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

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

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

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

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

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

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

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

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

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

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

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

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

=09return ret;
}


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

=09*retlen =3D written;
=09return ret;
}

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

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

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

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

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

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

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

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

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

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

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

=09instr->state =3D MTD_ERASING;

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

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

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

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

=09=09/* Get spinlock, in case we exit */
=09=09spin_lock_bh (&this->chip_lock);
=09=09/* See if block erase succeeded */
=09=09if (status & 0x01) {
=09=09=09DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x=
\n", page);
=09=09=09instr->state =3D MTD_ERASE_FAILED;
=09=09=09goto erase_exit;
=09=09}
=09=09
=09=09/* Check, if we were interupted */
=09=09if (this->state =3D=3D FL_ERASING) {
=09=09=09/* Increment page address and decrement length */
=09=09=09len -=3D mtd->erasesize;
=09=09=09page +=3D pages_per_block;
=09=09}
=09=09/* Release the spin lock */
=09=09spin_unlock_bh (&this->chip_lock);
erase_retry:
=09=09spin_lock_bh (&this->chip_lock);
=09=09/* Check the state and sleep if it changed */
=09=09if (this->state =3D=3D FL_ERASING || this->state =3D=3D FL_READY) {
=09=09=09/* Select the NAND device again, if we were interrupted */
=09=09=09this->state =3D FL_ERASING;
=09=09=09this->select_chip(mtd, 0);
=09=09=09continue;
=09=09} else {
=09=09=09set_current_state (TASK_UNINTERRUPTIBLE);
=09=09=09add_wait_queue (&this->wq, &wait);
=09=09=09spin_unlock_bh (&this->chip_lock);
=09=09=09schedule ();
=09=09=09remove_wait_queue (&this->wq, &wait);
=09=09=09goto erase_retry;
=09=09}
=09}
=09instr->state =3D MTD_ERASE_DONE;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

=09switch (this->eccmode) {

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

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

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

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

=09default:
=09=09printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
=09=09BUG();=09
=09}=09
=09
=09/* Initialize state, waitqueue and spinlock */
=09this->state =3D FL_READY;
=09init_waitqueue_head (&this->wq);
=09spin_lock_init (&this->chip_lock);

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

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

=09/* Return happy */
=09return 0;
}

EXPORT_SYMBOL (nand_scan);

MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner=
 <tglx@linutronix.de>");
MODULE_DESCRIPTION ("Generic NAND flash driver code");

------=_Part_2020_19033482.1099561286164--