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