[go: nahoru, domu]

11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * Linux driver for NAND Flash Translation Layer
3a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse *
4a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * Copyright © 1999 Machine Vision Holdings, Inc.
5a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org>
6a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse *
7a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * This program is free software; you can redistribute it and/or modify
8a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * it under the terms of the GNU General Public License as published by
9a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * the Free Software Foundation; either version 2 of the License, or
10a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * (at your option) any later version.
11a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse *
12a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * This program is distributed in the hope that it will be useful,
13a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * but WITHOUT ANY WARRANTY; without even the implied warranty of
14a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * GNU General Public License for more details.
16a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse *
17a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * You should have received a copy of the GNU General Public License
18a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * along with this program; if not, write to the Free Software
19a1452a3771c4eb85bd779790b040efdc36f4274eDavid Woodhouse * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PRERELEASE
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/errno.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/hdreg.h>
33e7f521636a2d6b1a012f98f6ec16898c5d6f1543Scott James Remnant#include <linux/blkdev.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kmod.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/mtd.h>
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/nand.h>
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/nftl.h>
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/blktrans.h>
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* maximum number of loops while examining next block, to have a
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   chance to detect consistency problems (they should never happen
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   because of the checks done in the mounting */
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MAX_LOOPS 10000
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct NFTLrecord *nftl;
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long temp;
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
53818b97392932ac4cecc36ab839957258367004a9Huang Shijie	if (!mtd_type_is_nand(mtd) || mtd->size > UINT_MAX)
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* OK, this is moderately ugly.  But probably safe.  Alternatives? */
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (memcmp(mtd->name, "DiskOnChip", 10))
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
59289c05222172b51401dbbb017115655f241d94abBrian Norris	pr_debug("NFTL: add_mtd for %s\n", mtd->name);
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6195b93a0cd46682c6d9e8eea803fda510cb6b863aBurman Yan	nftl = kzalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
630870066d7e6c85bbe37741137e4c4731501a98e0Brian Norris	if (!nftl)
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nftl->mbd.mtd = mtd;
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nftl->mbd.devnum = -1;
68191876729901d0c8dab8a331f9a1e4b73a56457bRichard Purdie
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nftl->mbd.tr = tr;
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        if (NFTL_mount(nftl) < 0) {
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING "NFTL: could not mount device\n");
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree(nftl);
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* OK, it's a new one. Set up all the data structures. */
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Calculate geometry */
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nftl->cylinders = 1024;
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nftl->heads = 16;
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	temp = nftl->cylinders * nftl->heads;
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nftl->sectors = nftl->mbd.size / temp;
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (nftl->mbd.size % temp) {
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nftl->sectors++;
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		temp = nftl->cylinders * nftl->sectors;
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nftl->heads = nftl->mbd.size / temp;
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (nftl->mbd.size % temp) {
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			nftl->heads++;
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			temp = nftl->heads * nftl->sectors;
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			nftl->cylinders = nftl->mbd.size / temp;
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) {
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
9997894cda5773e59bd13e87b72077751099419a9fThomas Gleixner		  Oh no we don't have
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   mbd.size == heads * cylinders * sectors
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*/
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING "NFTL: cannot calculate a geometry to "
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       "match size of 0x%lx.\n", nftl->mbd.size);
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d "
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			"(== 0x%lx sects)\n",
10697894cda5773e59bd13e87b72077751099419a9fThomas Gleixner			nftl->cylinders, nftl->heads , nftl->sectors,
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			(long)nftl->cylinders * (long)nftl->heads *
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			(long)nftl->sectors );
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (add_mtd_blktrans_dev(&nftl->mbd)) {
112fa671646f61182cd18234461a6e65f50c6558695Jesper Juhl		kfree(nftl->ReplUnitTable);
113fa671646f61182cd18234461a6e65f50c6558695Jesper Juhl		kfree(nftl->EUNtable);
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree(nftl);
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef PSYCHO_DEBUG
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void nftl_remove_dev(struct mtd_blktrans_dev *dev)
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct NFTLrecord *nftl = (void *)dev;
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
126289c05222172b51401dbbb017115655f241d94abBrian Norris	pr_debug("NFTL: remove_dev (i=%d)\n", dev->devnum);
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	del_mtd_blktrans_dev(dev);
129fa671646f61182cd18234461a6e65f50c6558695Jesper Juhl	kfree(nftl->ReplUnitTable);
130fa671646f61182cd18234461a6e65f50c6558695Jesper Juhl	kfree(nftl->EUNtable);
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1338593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner/*
1348593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner * Read oob data from flash
1358593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner */
1368593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixnerint nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
1378593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner		  size_t *retlen, uint8_t *buf)
1388593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner{
13916f05c2b68520f94e365f9d347a7076f4ff00ad5Dimitri Gorokhovik	loff_t mask = mtd->writesize - 1;
1408593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	struct mtd_oob_ops ops;
1418593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	int res;
1428593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner
1430612b9ddc2eeda014dd805c87c752b342d8f80f0Brian Norris	ops.mode = MTD_OPS_PLACE_OOB;
14416f05c2b68520f94e365f9d347a7076f4ff00ad5Dimitri Gorokhovik	ops.ooboffs = offs & mask;
1458593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	ops.ooblen = len;
1468593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	ops.oobbuf = buf;
1478593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	ops.datbuf = NULL;
1488593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner
149fd2819bbc92fc98bed5d612e4acbe16b6326f6bfArtem Bityutskiy	res = mtd_read_oob(mtd, offs & ~mask, &ops);
1507014568bad55c20b7ee4f439d78c9e875912d51fVitaly Wool	*retlen = ops.oobretlen;
1518593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	return res;
1528593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner}
1538593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner
1548593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner/*
1558593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner * Write oob data to flash
1568593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner */
1578593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixnerint nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
1588593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner		   size_t *retlen, uint8_t *buf)
1598593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner{
16016f05c2b68520f94e365f9d347a7076f4ff00ad5Dimitri Gorokhovik	loff_t mask = mtd->writesize - 1;
1618593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	struct mtd_oob_ops ops;
1628593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	int res;
1638593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner
1640612b9ddc2eeda014dd805c87c752b342d8f80f0Brian Norris	ops.mode = MTD_OPS_PLACE_OOB;
16516f05c2b68520f94e365f9d347a7076f4ff00ad5Dimitri Gorokhovik	ops.ooboffs = offs & mask;
1668593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	ops.ooblen = len;
1678593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	ops.oobbuf = buf;
1688593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	ops.datbuf = NULL;
1698593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner
170a2cc5ba075f9bc837d0b4d4ec7328dcefc11859dArtem Bityutskiy	res = mtd_write_oob(mtd, offs & ~mask, &ops);
1717014568bad55c20b7ee4f439d78c9e875912d51fVitaly Wool	*retlen = ops.oobretlen;
1728593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	return res;
1738593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner}
1748593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner
175553a8012088b3452c7d66ff60d2d06ad0c9bea00Frederik Deweerdt#ifdef CONFIG_NFTL_RW
176553a8012088b3452c7d66ff60d2d06ad0c9bea00Frederik Deweerdt
1778593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner/*
1788593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner * Write data and oob to flash
1798593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner */
1808593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixnerstatic int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
1818593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner		      size_t *retlen, uint8_t *buf, uint8_t *oob)
1828593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner{
18316f05c2b68520f94e365f9d347a7076f4ff00ad5Dimitri Gorokhovik	loff_t mask = mtd->writesize - 1;
1848593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	struct mtd_oob_ops ops;
1858593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	int res;
1868593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner
1870612b9ddc2eeda014dd805c87c752b342d8f80f0Brian Norris	ops.mode = MTD_OPS_PLACE_OOB;
18816f05c2b68520f94e365f9d347a7076f4ff00ad5Dimitri Gorokhovik	ops.ooboffs = offs & mask;
1898593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	ops.ooblen = mtd->oobsize;
1908593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	ops.oobbuf = oob;
1918593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	ops.datbuf = buf;
1928593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	ops.len = len;
1938593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner
194a2cc5ba075f9bc837d0b4d4ec7328dcefc11859dArtem Bityutskiy	res = mtd_write_oob(mtd, offs & ~mask, &ops);
1958593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	*retlen = ops.retlen;
1968593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	return res;
1978593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner}
1988593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Actual NFTL access routines */
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	when the give Virtual Unit Chain
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* For a given Virtual Unit Chain: find or create a free block and
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   add it to the chain */
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* We're passed the number of the last EUN in the chain, to save us from
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   having to look it up again */
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 pot = nftl->LastFreeEUN;
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int silly = nftl->nb_blocks;
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Normally, we force a fold to happen before we run out of free blocks completely */
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!desperate && nftl->numfreeEUNs < 2) {
214289c05222172b51401dbbb017115655f241d94abBrian Norris		pr_debug("NFTL_findfreeblock: there are too few free EUNs\n");
21570ec3bb8ea3f8c55b255f41d122c7d4d8c0d00b4Julia Lawall		return BLOCK_NIL;
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Scan for a free block */
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do {
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			nftl->LastFreeEUN = pot;
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			nftl->numfreeEUNs--;
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return pot;
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* This will probably point to the MediaHdr unit itself,
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   right at the beginning of the partition. But that unit
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   (and the backup unit too) should have the UCI set
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   up so that it's not selected for overwriting */
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (++pot > nftl->lastEUN)
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!silly--) {
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk("Argh! No free blocks found! LastFreeEUN = %d, "
23597894cda5773e59bd13e87b72077751099419a9fThomas Gleixner			       "FirstEUN = %d\n", nftl->LastFreeEUN,
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
23770ec3bb8ea3f8c55b255f41d122c7d4d8c0d00b4Julia Lawall			return BLOCK_NIL;
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} while (pot != nftl->LastFreeEUN);
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24170ec3bb8ea3f8c55b255f41d122c7d4d8c0d00b4Julia Lawall	return BLOCK_NIL;
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
246f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner	struct mtd_info *mtd = nftl->mbd.mtd;
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 BlockMap[MAX_SECTORS_PER_UNIT];
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int thisEUN;
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int block;
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int silly;
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int targetEUN;
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct nftl_oob oob;
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int inplace = 1;
256f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner	size_t retlen;
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(BlockMap, 0xff, sizeof(BlockMap));
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	thisEUN = nftl->EUNtable[thisVUC];
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (thisEUN == BLOCK_NIL) {
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING "Trying to fold non-existent "
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       "Virtual Unit Chain %d!\n", thisVUC);
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return BLOCK_NIL;
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
26897894cda5773e59bd13e87b72077751099419a9fThomas Gleixner
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Scan to find the Erase Unit which holds the actual data for each
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   512-byte block within the Chain.
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*/
272f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner	silly = MAX_LOOPS;
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	targetEUN = BLOCK_NIL;
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (thisEUN <= nftl->lastEUN ) {
275f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner		unsigned int status, foldmark;
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		targetEUN = thisEUN;
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (block = 0; block < nftl->EraseSize / 512; block ++) {
2798593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner			nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
280f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner				      (block * 512), 16 , &retlen,
281f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner				      (char *)&oob);
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (block == 2) {
283f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner				foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
284f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner				if (foldmark == FOLD_MARK_IN_PROGRESS) {
285289c05222172b51401dbbb017115655f241d94abBrian Norris					pr_debug("Write Inhibited on EUN %d\n", thisEUN);
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					inplace = 0;
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				} else {
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					/* There's no other reason not to do inplace,
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					   except ones that come later. So we don't need
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					   to preserve inplace */
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					inplace = 1;
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
294f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner			status = oob.b.Status | oob.b.Status1;
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			BlockLastState[block] = status;
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch(status) {
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case SECTOR_FREE:
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				BlockFreeFound[block] = 1;
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case SECTOR_USED:
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (!BlockFreeFound[block])
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					BlockMap[block] = thisEUN;
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				else
30697894cda5773e59bd13e87b72077751099419a9fThomas Gleixner					printk(KERN_WARNING
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					       "SECTOR_USED found after SECTOR_FREE "
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					       "in Virtual Unit Chain %d for block %d\n",
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					       thisVUC, block);
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case SECTOR_DELETED:
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (!BlockFreeFound[block])
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					BlockMap[block] = BLOCK_NIL;
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				else
31597894cda5773e59bd13e87b72077751099419a9fThomas Gleixner					printk(KERN_WARNING
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					       "SECTOR_DELETED found after SECTOR_FREE "
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					       "in Virtual Unit Chain %d for block %d\n",
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					       thisVUC, block);
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case SECTOR_IGNORE:
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			default:
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				printk("Unknown status for block %d in EUN %d: %x\n",
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				       block, thisEUN, status);
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!silly--) {
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       thisVUC);
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return BLOCK_NIL;
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
33497894cda5773e59bd13e87b72077751099419a9fThomas Gleixner
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		thisEUN = nftl->ReplUnitTable[thisEUN];
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (inplace) {
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* We're being asked to be a fold-in-place. Check
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   that all blocks which actually have data associated
34197894cda5773e59bd13e87b72077751099419a9fThomas Gleixner		   with them (i.e. BlockMap[block] != BLOCK_NIL) are
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   either already present or SECTOR_FREE in the target
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   block. If not, we're going to have to fold out-of-place
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   anyway.
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*/
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (block = 0; block < nftl->EraseSize / 512 ; block++) {
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (BlockLastState[block] != SECTOR_FREE &&
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    BlockMap[block] != BLOCK_NIL &&
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    BlockMap[block] != targetEUN) {
350289c05222172b51401dbbb017115655f241d94abBrian Norris				pr_debug("Setting inplace to 0. VUC %d, "
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				      "block %d was %x lastEUN, "
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				      "and is in EUN %d (%s) %d\n",
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				      thisVUC, block, BlockLastState[block],
35497894cda5773e59bd13e87b72077751099419a9fThomas Gleixner				      BlockMap[block],
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				      BlockMap[block]== targetEUN ? "==" : "!=",
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				      targetEUN);
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				inplace = 0;
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    SECTOR_FREE) {
366289c05222172b51401dbbb017115655f241d94abBrian Norris			pr_debug("Pending write not free in EUN %d. "
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      "Folding out of place.\n", targetEUN);
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			inplace = 0;
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
37197894cda5773e59bd13e87b72077751099419a9fThomas Gleixner
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!inplace) {
373289c05222172b51401dbbb017115655f241d94abBrian Norris		pr_debug("Cannot fold Virtual Unit Chain %d in place. "
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		      "Trying out-of-place\n", thisVUC);
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* We need to find a targetEUN to fold into. */
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		targetEUN = NFTL_findfreeblock(nftl, 1);
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (targetEUN == BLOCK_NIL) {
37897894cda5773e59bd13e87b72077751099419a9fThomas Gleixner			/* Ouch. Now we're screwed. We need to do a
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   fold-in-place of another chain to make room
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   for this one. We need a better way of selecting
38197894cda5773e59bd13e87b72077751099419a9fThomas Gleixner			   which chain to fold, because makefreeblock will
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   only ask us to fold the same one again.
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			*/
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_WARNING
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       "NFTL_findfreeblock(desperate) returns 0xffff.\n");
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return BLOCK_NIL;
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
389f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner		/* We put a fold mark in the chain we are folding only if we
390f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner               fold in place to help the mount check code. If we do not fold in
391f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner               place, it is possible to find the valid chain by selecting the
392f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner               longer one */
393f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner		oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
394f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner		oob.u.c.unused = 0xffffffff;
3958593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner		nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
396f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner			       8, &retlen, (char *)&oob.u);
397f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner	}
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* OK. We now know the location of every block in the Virtual Unit Chain,
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   and the Erase Unit into which we are supposed to be copying.
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   Go for it.
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*/
403289c05222172b51401dbbb017115655f241d94abBrian Norris	pr_debug("Folding chain %d into unit %d\n", thisVUC, targetEUN);
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (block = 0; block < nftl->EraseSize / 512 ; block++) {
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned char movebuf[512];
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int ret;
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* If it's in the target EUN already, or if it's pending write, do nothing */
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (BlockMap[block] == targetEUN ||
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
414f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner		/* copy only in non free block (free blocks can only
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                   happen in case of media errors or deleted blocks) */
416f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner		if (BlockMap[block] == BLOCK_NIL)
417f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner			continue;
418f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner
419329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy		ret = mtd_read(mtd,
420329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy			       (nftl->EraseSize * BlockMap[block]) + (block * 512),
421329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy			       512,
422329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy			       &retlen,
423329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy			       movebuf);
424d57f40544a41fdfe90fd863b6865138c5a82f1ccBrian Norris		if (ret < 0 && !mtd_is_bitflip(ret)) {
425329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy			ret = mtd_read(mtd,
426329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy				       (nftl->EraseSize * BlockMap[block]) + (block * 512),
427329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy				       512,
428329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy				       &retlen,
429329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy				       movebuf);
430f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner			if (ret != -EIO)
431f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner				printk("Error went away on retry.\n");
432f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner		}
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memset(&oob, 0xff, sizeof(struct nftl_oob));
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		oob.b.Status = oob.b.Status1 = SECTOR_USED;
4359223a456da8ed357bf7e0b128c853e2c8bd54614Thomas Gleixner
4368593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner		nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) +
4378593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner			   (block * 512), 512, &retlen, movebuf, (char *)&oob);
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
43997894cda5773e59bd13e87b72077751099419a9fThomas Gleixner
440f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner	/* add the header so that it is now a valid chain */
441f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner	oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
44270ec3bb8ea3f8c55b255f41d122c7d4d8c0d00b4Julia Lawall	oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = BLOCK_NIL;
44397894cda5773e59bd13e87b72077751099419a9fThomas Gleixner
4448593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
445f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner		       8, &retlen, (char *)&oob.u);
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
44997894cda5773e59bd13e87b72077751099419a9fThomas Gleixner	/* At this point, we have two different chains for this Virtual Unit, and no way to tell
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   them apart. If we crash now, we get confused. However, both contain the same data, so we
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   shouldn't actually lose data in this case. It's just that when we load up on a medium which
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   has duplicate chains, we need to free one of the chains because it's not necessary any more.
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*/
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	thisEUN = nftl->EUNtable[thisVUC];
455289c05222172b51401dbbb017115655f241d94abBrian Norris	pr_debug("Want to erase\n");
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
45797894cda5773e59bd13e87b72077751099419a9fThomas Gleixner	/* For each block in the old chain (except the targetEUN of course),
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   free it and make it available for future use */
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned int EUNtmp;
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
462f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner		EUNtmp = nftl->ReplUnitTable[thisEUN];
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
464f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner		if (NFTL_formatblock(nftl, thisEUN) < 0) {
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* could not erase : mark block as reserved
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
468f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner		} else {
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* correctly erased : mark it as free */
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			nftl->numfreeEUNs++;
472f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner		}
473f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner		thisEUN = EUNtmp;
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
47597894cda5773e59bd13e87b72077751099419a9fThomas Gleixner
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Make this the new start of chain for thisVUC */
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nftl->EUNtable[thisVUC] = targetEUN;
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return targetEUN;
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
48597894cda5773e59bd13e87b72077751099419a9fThomas Gleixner	/* This is the part that needs some cleverness applied.
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   For now, I'm doing the minimum applicable to actually
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   get the thing to work.
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   Wear-levelling and other clever stuff needs to be implemented
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   and we also need to do some assessment of the results when
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   the system loses power half-way through the routine.
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*/
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 LongestChain = 0;
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 ChainLength = 0, thislen;
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 chain, EUN;
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) {
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		EUN = nftl->EUNtable[chain];
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		thislen = 0;
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		while (EUN <= nftl->lastEUN) {
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			thislen++;
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			//printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (thislen > 0xff00) {
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				printk("Endless loop in Virtual Chain %d: Unit %x\n",
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				       chain, EUN);
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (thislen > 0xff10) {
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* Actually, don't return failure. Just ignore this chain and
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   get on with it. */
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				thislen = 0;
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (thislen > ChainLength) {
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			//printk("New longest chain is %d with length %d\n", chain, thislen);
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ChainLength = thislen;
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			LongestChain = chain;
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ChainLength < 2) {
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       "Failing request\n");
52670ec3bb8ea3f8c55b255f41d122c7d4d8c0d00b4Julia Lawall		return BLOCK_NIL;
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return NFTL_foldchain (nftl, LongestChain, pendingblock);
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
53297894cda5773e59bd13e87b72077751099419a9fThomas Gleixner/* NFTL_findwriteunit: Return the unit number into which we can write
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                       for this block. Make it available if it isn't already
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 lastEUN;
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 thisVUC = block / (nftl->EraseSize / 512);
539f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner	struct mtd_info *mtd = nftl->mbd.mtd;
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int writeEUN;
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t retlen;
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int silly, silly2 = 3;
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct nftl_oob oob;
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do {
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Scan the media to find a unit in the VUC which has
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   a free space for the block in question.
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*/
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
55197894cda5773e59bd13e87b72077751099419a9fThomas Gleixner		/* This condition catches the 0x[7f]fff cases, as well as
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   being a sanity check for past-end-of-media access
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*/
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		lastEUN = BLOCK_NIL;
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		writeEUN = nftl->EUNtable[thisVUC];
556f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner		silly = MAX_LOOPS;
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		while (writeEUN <= nftl->lastEUN) {
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct nftl_bci bci;
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			size_t retlen;
560f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner			unsigned int status;
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			lastEUN = writeEUN;
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5648593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner			nftl_read_oob(mtd,
565f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner				      (writeEUN * nftl->EraseSize) + blockofs,
566f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner				      8, &retlen, (char *)&bci);
56797894cda5773e59bd13e87b72077751099419a9fThomas Gleixner
568289c05222172b51401dbbb017115655f241d94abBrian Norris			pr_debug("Status of block %d in EUN %d is %x\n",
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      block , writeEUN, le16_to_cpu(bci.Status));
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
571f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner			status = bci.Status | bci.Status1;
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch(status) {
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case SECTOR_FREE:
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return writeEUN;
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case SECTOR_DELETED:
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case SECTOR_USED:
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case SECTOR_IGNORE:
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			default:
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				// Invalid block. Don't use it any more. Must implement.
58297894cda5773e59bd13e87b72077751099419a9fThomas Gleixner				break;
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
58497894cda5773e59bd13e87b72077751099419a9fThomas Gleixner
58597894cda5773e59bd13e87b72077751099419a9fThomas Gleixner			if (!silly--) {
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				printk(KERN_WARNING
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				       "Infinite loop in Virtual Unit Chain 0x%x\n",
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				       thisVUC);
58970ec3bb8ea3f8c55b255f41d122c7d4d8c0d00b4Julia Lawall				return BLOCK_NIL;
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Skip to next block in chain */
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			writeEUN = nftl->ReplUnitTable[writeEUN];
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
59697894cda5773e59bd13e87b72077751099419a9fThomas Gleixner		/* OK. We didn't find one in the existing chain, or there
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   is no existing chain. */
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Try to find an already-free block */
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		writeEUN = NFTL_findfreeblock(nftl, 0);
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (writeEUN == BLOCK_NIL) {
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* That didn't work - there were no free blocks just
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   waiting to be picked up. We're going to have to fold
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   a chain to make room.
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			*/
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* First remember the start of this chain */
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			//u16 startEUN = nftl->EUNtable[thisVUC];
61097894cda5773e59bd13e87b72077751099419a9fThomas Gleixner
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			//printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
61270ec3bb8ea3f8c55b255f41d122c7d4d8c0d00b4Julia Lawall			writeEUN = NFTL_makefreeblock(nftl, BLOCK_NIL);
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (writeEUN == BLOCK_NIL) {
61597894cda5773e59bd13e87b72077751099419a9fThomas Gleixner				/* OK, we accept that the above comment is
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   lying - there may have been free blocks
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   last time we called NFTL_findfreeblock(),
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   but they are reserved for when we're
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   desperate. Well, now we're desperate.
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				*/
621289c05222172b51401dbbb017115655f241d94abBrian Norris				pr_debug("Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				writeEUN = NFTL_findfreeblock(nftl, 1);
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (writeEUN == BLOCK_NIL) {
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* Ouch. This should never happen - we should
62697894cda5773e59bd13e87b72077751099419a9fThomas Gleixner				   always be able to make some room somehow.
62797894cda5773e59bd13e87b72077751099419a9fThomas Gleixner				   If we get here, we've allocated more storage
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   space than actual media, or our makefreeblock
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   routine is missing something.
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				*/
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				printk(KERN_WARNING "Cannot make free space.\n");
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return BLOCK_NIL;
63397894cda5773e59bd13e87b72077751099419a9fThomas Gleixner			}
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			//printk("Restarting scan\n");
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			lastEUN = BLOCK_NIL;
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* We've found a free block. Insert it into the chain. */
64097894cda5773e59bd13e87b72077751099419a9fThomas Gleixner
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (lastEUN != BLOCK_NIL) {
642f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner			thisVUC |= 0x8000; /* It's a replacement block */
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
644f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner			/* The first block in a new chain */
645f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner			nftl->EUNtable[thisVUC] = writeEUN;
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* set up the actual EUN we're writing into */
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Both in our cache... */
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* ... and on the flash itself */
6538593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner		nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
654f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner			      &retlen, (char *)&oob.u);
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6588593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner		nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
659f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner			       &retlen, (char *)&oob.u);
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
661f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner		/* we link the new block to the chain only after the
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                   block is ready. It avoids the case where the chain
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                   could point to a free block */
664f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner		if (lastEUN != BLOCK_NIL) {
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Both in our cache... */
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			nftl->ReplUnitTable[lastEUN] = writeEUN;
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* ... and on the flash itself */
6688593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner			nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
669f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner				      8, &retlen, (char *)&oob.u);
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				= cpu_to_le16(writeEUN);
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6748593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner			nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
675f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner				       8, &retlen, (char *)&oob.u);
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return writeEUN;
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} while (silly2--);
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       thisVUC);
68470ec3bb8ea3f8c55b255f41d122c7d4d8c0d00b4Julia Lawall	return BLOCK_NIL;
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   char *buffer)
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct NFTLrecord *nftl = (void *)mbd;
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 writeEUN;
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t retlen;
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct nftl_oob oob;
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeEUN = NFTL_findwriteunit(nftl, block);
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (writeEUN == BLOCK_NIL) {
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       "NFTL_writeblock(): Cannot find block to write to\n");
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* If we _still_ haven't got a block to use, we're screwed */
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 1;
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(&oob, 0xff, sizeof(struct nftl_oob));
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	oob.b.Status = oob.b.Status1 = SECTOR_USED;
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7088593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner	nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
7098593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner		   512, &retlen, (char *)buffer, (char *)&oob);
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* CONFIG_NFTL_RW */
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			  char *buffer)
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct NFTLrecord *nftl = (void *)mbd;
718f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner	struct mtd_info *mtd = nftl->mbd.mtd;
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 lastgoodEUN;
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
722f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner	unsigned int status;
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int silly = MAX_LOOPS;
724f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner	size_t retlen;
725f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner	struct nftl_bci bci;
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	lastgoodEUN = BLOCK_NIL;
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
729f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner	if (thisEUN != BLOCK_NIL) {
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		while (thisEUN < nftl->nb_blocks) {
7318593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner			if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
732f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner					  blockofs, 8, &retlen,
733f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner					  (char *)&bci) < 0)
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				status = SECTOR_IGNORE;
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			else
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				status = bci.Status | bci.Status1;
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch (status) {
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case SECTOR_FREE:
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* no modification of a sector should follow a free sector */
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto the_end;
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case SECTOR_DELETED:
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				lastgoodEUN = BLOCK_NIL;
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case SECTOR_USED:
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				lastgoodEUN = thisEUN;
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case SECTOR_IGNORE:
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			default:
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				printk("Unknown status for block %ld in EUN %d: %x\n",
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				       block, thisEUN, status);
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!silly--) {
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				       block / (nftl->EraseSize / 512));
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return 1;
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			thisEUN = nftl->ReplUnitTable[thisEUN];
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
763f4a43cfcecfcaeeaa40a9dbc1d1378298c22446eThomas Gleixner	}
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds the_end:
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (lastgoodEUN == BLOCK_NIL) {
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* the requested block is not on the media, return all 0x00 */
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memset(buffer, 0, 512);
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		size_t retlen;
772329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy		int res = mtd_read(mtd, ptr, 512, &retlen, buffer);
7739a1fcdfd4bee27c418424cac47abf7c049541297Thomas Gleixner
774d57f40544a41fdfe90fd863b6865138c5a82f1ccBrian Norris		if (res < 0 && !mtd_is_bitflip(res))
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EIO;
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int nftl_getgeo(struct mtd_blktrans_dev *dev,  struct hd_geometry *geo)
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct NFTLrecord *nftl = (void *)dev;
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	geo->heads = nftl->heads;
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	geo->sectors = nftl->sectors;
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	geo->cylinders = nftl->cylinders;
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/****************************************************************************
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Module stuff
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ****************************************************************************/
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct mtd_blktrans_ops nftl_tr = {
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name		= "nftl",
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.major		= NFTL_MAJOR,
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.part_bits	= NFTL_PARTN_BITS,
802191876729901d0c8dab8a331f9a1e4b73a56457bRichard Purdie	.blksize 	= 512,
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.getgeo		= nftl_getgeo,
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.readsect	= nftl_readblock,
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_NFTL_RW
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.writesect	= nftl_writeblock,
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.add_mtd	= nftl_add_mtd,
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.remove_dev	= nftl_remove_dev,
8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner		= THIS_MODULE,
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init init_nftl(void)
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return register_mtd_blktrans(&nftl_tr);
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit cleanup_nftl(void)
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	deregister_mtd_blktrans(&nftl_tr);
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(init_nftl);
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(cleanup_nftl);
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");
829e7f521636a2d6b1a012f98f6ec16898c5d6f1543Scott James RemnantMODULE_ALIAS_BLOCKDEV_MAJOR(NFTL_MAJOR);
830