18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Linux driver for NAND Flash Translation Layer 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright © 1999 Machine Vision Holdings, Inc. 68c2ecf20Sopenharmony_ci * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define PRERELEASE 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <asm/errno.h> 148c2ecf20Sopenharmony_ci#include <asm/io.h> 158c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/hdreg.h> 208c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/kmod.h> 238c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 248c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 258c2ecf20Sopenharmony_ci#include <linux/mtd/nftl.h> 268c2ecf20Sopenharmony_ci#include <linux/mtd/blktrans.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* maximum number of loops while examining next block, to have a 298c2ecf20Sopenharmony_ci chance to detect consistency problems (they should never happen 308c2ecf20Sopenharmony_ci because of the checks done in the mounting */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define MAX_LOOPS 10000 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct NFTLrecord *nftl; 388c2ecf20Sopenharmony_ci unsigned long temp; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci if (!mtd_type_is_nand(mtd) || mtd->size > UINT_MAX) 418c2ecf20Sopenharmony_ci return; 428c2ecf20Sopenharmony_ci /* OK, this is moderately ugly. But probably safe. Alternatives? */ 438c2ecf20Sopenharmony_ci if (memcmp(mtd->name, "DiskOnChip", 10)) 448c2ecf20Sopenharmony_ci return; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci pr_debug("NFTL: add_mtd for %s\n", mtd->name); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci nftl = kzalloc(sizeof(struct NFTLrecord), GFP_KERNEL); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (!nftl) 518c2ecf20Sopenharmony_ci return; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci nftl->mbd.mtd = mtd; 548c2ecf20Sopenharmony_ci nftl->mbd.devnum = -1; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci nftl->mbd.tr = tr; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (NFTL_mount(nftl) < 0) { 598c2ecf20Sopenharmony_ci printk(KERN_WARNING "NFTL: could not mount device\n"); 608c2ecf20Sopenharmony_ci kfree(nftl); 618c2ecf20Sopenharmony_ci return; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* OK, it's a new one. Set up all the data structures. */ 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* Calculate geometry */ 678c2ecf20Sopenharmony_ci nftl->cylinders = 1024; 688c2ecf20Sopenharmony_ci nftl->heads = 16; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci temp = nftl->cylinders * nftl->heads; 718c2ecf20Sopenharmony_ci nftl->sectors = nftl->mbd.size / temp; 728c2ecf20Sopenharmony_ci if (nftl->mbd.size % temp) { 738c2ecf20Sopenharmony_ci nftl->sectors++; 748c2ecf20Sopenharmony_ci temp = nftl->cylinders * nftl->sectors; 758c2ecf20Sopenharmony_ci nftl->heads = nftl->mbd.size / temp; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (nftl->mbd.size % temp) { 788c2ecf20Sopenharmony_ci nftl->heads++; 798c2ecf20Sopenharmony_ci temp = nftl->heads * nftl->sectors; 808c2ecf20Sopenharmony_ci nftl->cylinders = nftl->mbd.size / temp; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) { 858c2ecf20Sopenharmony_ci /* 868c2ecf20Sopenharmony_ci Oh no we don't have 878c2ecf20Sopenharmony_ci mbd.size == heads * cylinders * sectors 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci printk(KERN_WARNING "NFTL: cannot calculate a geometry to " 908c2ecf20Sopenharmony_ci "match size of 0x%lx.\n", nftl->mbd.size); 918c2ecf20Sopenharmony_ci printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d " 928c2ecf20Sopenharmony_ci "(== 0x%lx sects)\n", 938c2ecf20Sopenharmony_ci nftl->cylinders, nftl->heads , nftl->sectors, 948c2ecf20Sopenharmony_ci (long)nftl->cylinders * (long)nftl->heads * 958c2ecf20Sopenharmony_ci (long)nftl->sectors ); 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (add_mtd_blktrans_dev(&nftl->mbd)) { 998c2ecf20Sopenharmony_ci kfree(nftl->ReplUnitTable); 1008c2ecf20Sopenharmony_ci kfree(nftl->EUNtable); 1018c2ecf20Sopenharmony_ci kfree(nftl); 1028c2ecf20Sopenharmony_ci return; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci#ifdef PSYCHO_DEBUG 1058c2ecf20Sopenharmony_ci printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a'); 1068c2ecf20Sopenharmony_ci#endif 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void nftl_remove_dev(struct mtd_blktrans_dev *dev) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct NFTLrecord *nftl = (void *)dev; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci pr_debug("NFTL: remove_dev (i=%d)\n", dev->devnum); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci del_mtd_blktrans_dev(dev); 1168c2ecf20Sopenharmony_ci kfree(nftl->ReplUnitTable); 1178c2ecf20Sopenharmony_ci kfree(nftl->EUNtable); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* 1218c2ecf20Sopenharmony_ci * Read oob data from flash 1228c2ecf20Sopenharmony_ci */ 1238c2ecf20Sopenharmony_ciint nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, 1248c2ecf20Sopenharmony_ci size_t *retlen, uint8_t *buf) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci loff_t mask = mtd->writesize - 1; 1278c2ecf20Sopenharmony_ci struct mtd_oob_ops ops; 1288c2ecf20Sopenharmony_ci int res; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci ops.mode = MTD_OPS_PLACE_OOB; 1318c2ecf20Sopenharmony_ci ops.ooboffs = offs & mask; 1328c2ecf20Sopenharmony_ci ops.ooblen = len; 1338c2ecf20Sopenharmony_ci ops.oobbuf = buf; 1348c2ecf20Sopenharmony_ci ops.datbuf = NULL; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci res = mtd_read_oob(mtd, offs & ~mask, &ops); 1378c2ecf20Sopenharmony_ci *retlen = ops.oobretlen; 1388c2ecf20Sopenharmony_ci return res; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/* 1428c2ecf20Sopenharmony_ci * Write oob data to flash 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ciint nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, 1458c2ecf20Sopenharmony_ci size_t *retlen, uint8_t *buf) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci loff_t mask = mtd->writesize - 1; 1488c2ecf20Sopenharmony_ci struct mtd_oob_ops ops; 1498c2ecf20Sopenharmony_ci int res; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci ops.mode = MTD_OPS_PLACE_OOB; 1528c2ecf20Sopenharmony_ci ops.ooboffs = offs & mask; 1538c2ecf20Sopenharmony_ci ops.ooblen = len; 1548c2ecf20Sopenharmony_ci ops.oobbuf = buf; 1558c2ecf20Sopenharmony_ci ops.datbuf = NULL; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci res = mtd_write_oob(mtd, offs & ~mask, &ops); 1588c2ecf20Sopenharmony_ci *retlen = ops.oobretlen; 1598c2ecf20Sopenharmony_ci return res; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci#ifdef CONFIG_NFTL_RW 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci/* 1658c2ecf20Sopenharmony_ci * Write data and oob to flash 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_cistatic int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len, 1688c2ecf20Sopenharmony_ci size_t *retlen, uint8_t *buf, uint8_t *oob) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci loff_t mask = mtd->writesize - 1; 1718c2ecf20Sopenharmony_ci struct mtd_oob_ops ops; 1728c2ecf20Sopenharmony_ci int res; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci ops.mode = MTD_OPS_PLACE_OOB; 1758c2ecf20Sopenharmony_ci ops.ooboffs = offs & mask; 1768c2ecf20Sopenharmony_ci ops.ooblen = mtd->oobsize; 1778c2ecf20Sopenharmony_ci ops.oobbuf = oob; 1788c2ecf20Sopenharmony_ci ops.datbuf = buf; 1798c2ecf20Sopenharmony_ci ops.len = len; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci res = mtd_write_oob(mtd, offs & ~mask, &ops); 1828c2ecf20Sopenharmony_ci *retlen = ops.retlen; 1838c2ecf20Sopenharmony_ci return res; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/* Actual NFTL access routines */ 1878c2ecf20Sopenharmony_ci/* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used 1888c2ecf20Sopenharmony_ci * when the give Virtual Unit Chain 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_cistatic u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate ) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci /* For a given Virtual Unit Chain: find or create a free block and 1938c2ecf20Sopenharmony_ci add it to the chain */ 1948c2ecf20Sopenharmony_ci /* We're passed the number of the last EUN in the chain, to save us from 1958c2ecf20Sopenharmony_ci having to look it up again */ 1968c2ecf20Sopenharmony_ci u16 pot = nftl->LastFreeEUN; 1978c2ecf20Sopenharmony_ci int silly = nftl->nb_blocks; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* Normally, we force a fold to happen before we run out of free blocks completely */ 2008c2ecf20Sopenharmony_ci if (!desperate && nftl->numfreeEUNs < 2) { 2018c2ecf20Sopenharmony_ci pr_debug("NFTL_findfreeblock: there are too few free EUNs\n"); 2028c2ecf20Sopenharmony_ci return BLOCK_NIL; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* Scan for a free block */ 2068c2ecf20Sopenharmony_ci do { 2078c2ecf20Sopenharmony_ci if (nftl->ReplUnitTable[pot] == BLOCK_FREE) { 2088c2ecf20Sopenharmony_ci nftl->LastFreeEUN = pot; 2098c2ecf20Sopenharmony_ci nftl->numfreeEUNs--; 2108c2ecf20Sopenharmony_ci return pot; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* This will probably point to the MediaHdr unit itself, 2148c2ecf20Sopenharmony_ci right at the beginning of the partition. But that unit 2158c2ecf20Sopenharmony_ci (and the backup unit too) should have the UCI set 2168c2ecf20Sopenharmony_ci up so that it's not selected for overwriting */ 2178c2ecf20Sopenharmony_ci if (++pot > nftl->lastEUN) 2188c2ecf20Sopenharmony_ci pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (!silly--) { 2218c2ecf20Sopenharmony_ci printk("Argh! No free blocks found! LastFreeEUN = %d, " 2228c2ecf20Sopenharmony_ci "FirstEUN = %d\n", nftl->LastFreeEUN, 2238c2ecf20Sopenharmony_ci le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN)); 2248c2ecf20Sopenharmony_ci return BLOCK_NIL; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci } while (pot != nftl->LastFreeEUN); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci return BLOCK_NIL; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock ) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct mtd_info *mtd = nftl->mbd.mtd; 2348c2ecf20Sopenharmony_ci u16 BlockMap[MAX_SECTORS_PER_UNIT]; 2358c2ecf20Sopenharmony_ci unsigned char BlockLastState[MAX_SECTORS_PER_UNIT]; 2368c2ecf20Sopenharmony_ci unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT]; 2378c2ecf20Sopenharmony_ci unsigned int thisEUN; 2388c2ecf20Sopenharmony_ci int block; 2398c2ecf20Sopenharmony_ci int silly; 2408c2ecf20Sopenharmony_ci unsigned int targetEUN; 2418c2ecf20Sopenharmony_ci struct nftl_oob oob; 2428c2ecf20Sopenharmony_ci int inplace = 1; 2438c2ecf20Sopenharmony_ci size_t retlen; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci memset(BlockMap, 0xff, sizeof(BlockMap)); 2468c2ecf20Sopenharmony_ci memset(BlockFreeFound, 0, sizeof(BlockFreeFound)); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci thisEUN = nftl->EUNtable[thisVUC]; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (thisEUN == BLOCK_NIL) { 2518c2ecf20Sopenharmony_ci printk(KERN_WARNING "Trying to fold non-existent " 2528c2ecf20Sopenharmony_ci "Virtual Unit Chain %d!\n", thisVUC); 2538c2ecf20Sopenharmony_ci return BLOCK_NIL; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* Scan to find the Erase Unit which holds the actual data for each 2578c2ecf20Sopenharmony_ci 512-byte block within the Chain. 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ci silly = MAX_LOOPS; 2608c2ecf20Sopenharmony_ci targetEUN = BLOCK_NIL; 2618c2ecf20Sopenharmony_ci while (thisEUN <= nftl->lastEUN ) { 2628c2ecf20Sopenharmony_ci unsigned int status, foldmark; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci targetEUN = thisEUN; 2658c2ecf20Sopenharmony_ci for (block = 0; block < nftl->EraseSize / 512; block ++) { 2668c2ecf20Sopenharmony_ci nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) + 2678c2ecf20Sopenharmony_ci (block * 512), 16 , &retlen, 2688c2ecf20Sopenharmony_ci (char *)&oob); 2698c2ecf20Sopenharmony_ci if (block == 2) { 2708c2ecf20Sopenharmony_ci foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1; 2718c2ecf20Sopenharmony_ci if (foldmark == FOLD_MARK_IN_PROGRESS) { 2728c2ecf20Sopenharmony_ci pr_debug("Write Inhibited on EUN %d\n", thisEUN); 2738c2ecf20Sopenharmony_ci inplace = 0; 2748c2ecf20Sopenharmony_ci } else { 2758c2ecf20Sopenharmony_ci /* There's no other reason not to do inplace, 2768c2ecf20Sopenharmony_ci except ones that come later. So we don't need 2778c2ecf20Sopenharmony_ci to preserve inplace */ 2788c2ecf20Sopenharmony_ci inplace = 1; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci status = oob.b.Status | oob.b.Status1; 2828c2ecf20Sopenharmony_ci BlockLastState[block] = status; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci switch(status) { 2858c2ecf20Sopenharmony_ci case SECTOR_FREE: 2868c2ecf20Sopenharmony_ci BlockFreeFound[block] = 1; 2878c2ecf20Sopenharmony_ci break; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci case SECTOR_USED: 2908c2ecf20Sopenharmony_ci if (!BlockFreeFound[block]) 2918c2ecf20Sopenharmony_ci BlockMap[block] = thisEUN; 2928c2ecf20Sopenharmony_ci else 2938c2ecf20Sopenharmony_ci printk(KERN_WARNING 2948c2ecf20Sopenharmony_ci "SECTOR_USED found after SECTOR_FREE " 2958c2ecf20Sopenharmony_ci "in Virtual Unit Chain %d for block %d\n", 2968c2ecf20Sopenharmony_ci thisVUC, block); 2978c2ecf20Sopenharmony_ci break; 2988c2ecf20Sopenharmony_ci case SECTOR_DELETED: 2998c2ecf20Sopenharmony_ci if (!BlockFreeFound[block]) 3008c2ecf20Sopenharmony_ci BlockMap[block] = BLOCK_NIL; 3018c2ecf20Sopenharmony_ci else 3028c2ecf20Sopenharmony_ci printk(KERN_WARNING 3038c2ecf20Sopenharmony_ci "SECTOR_DELETED found after SECTOR_FREE " 3048c2ecf20Sopenharmony_ci "in Virtual Unit Chain %d for block %d\n", 3058c2ecf20Sopenharmony_ci thisVUC, block); 3068c2ecf20Sopenharmony_ci break; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci case SECTOR_IGNORE: 3098c2ecf20Sopenharmony_ci break; 3108c2ecf20Sopenharmony_ci default: 3118c2ecf20Sopenharmony_ci printk("Unknown status for block %d in EUN %d: %x\n", 3128c2ecf20Sopenharmony_ci block, thisEUN, status); 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (!silly--) { 3178c2ecf20Sopenharmony_ci printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", 3188c2ecf20Sopenharmony_ci thisVUC); 3198c2ecf20Sopenharmony_ci return BLOCK_NIL; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci thisEUN = nftl->ReplUnitTable[thisEUN]; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (inplace) { 3268c2ecf20Sopenharmony_ci /* We're being asked to be a fold-in-place. Check 3278c2ecf20Sopenharmony_ci that all blocks which actually have data associated 3288c2ecf20Sopenharmony_ci with them (i.e. BlockMap[block] != BLOCK_NIL) are 3298c2ecf20Sopenharmony_ci either already present or SECTOR_FREE in the target 3308c2ecf20Sopenharmony_ci block. If not, we're going to have to fold out-of-place 3318c2ecf20Sopenharmony_ci anyway. 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_ci for (block = 0; block < nftl->EraseSize / 512 ; block++) { 3348c2ecf20Sopenharmony_ci if (BlockLastState[block] != SECTOR_FREE && 3358c2ecf20Sopenharmony_ci BlockMap[block] != BLOCK_NIL && 3368c2ecf20Sopenharmony_ci BlockMap[block] != targetEUN) { 3378c2ecf20Sopenharmony_ci pr_debug("Setting inplace to 0. VUC %d, " 3388c2ecf20Sopenharmony_ci "block %d was %x lastEUN, " 3398c2ecf20Sopenharmony_ci "and is in EUN %d (%s) %d\n", 3408c2ecf20Sopenharmony_ci thisVUC, block, BlockLastState[block], 3418c2ecf20Sopenharmony_ci BlockMap[block], 3428c2ecf20Sopenharmony_ci BlockMap[block]== targetEUN ? "==" : "!=", 3438c2ecf20Sopenharmony_ci targetEUN); 3448c2ecf20Sopenharmony_ci inplace = 0; 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) && 3508c2ecf20Sopenharmony_ci pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) && 3518c2ecf20Sopenharmony_ci BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] != 3528c2ecf20Sopenharmony_ci SECTOR_FREE) { 3538c2ecf20Sopenharmony_ci pr_debug("Pending write not free in EUN %d. " 3548c2ecf20Sopenharmony_ci "Folding out of place.\n", targetEUN); 3558c2ecf20Sopenharmony_ci inplace = 0; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (!inplace) { 3608c2ecf20Sopenharmony_ci pr_debug("Cannot fold Virtual Unit Chain %d in place. " 3618c2ecf20Sopenharmony_ci "Trying out-of-place\n", thisVUC); 3628c2ecf20Sopenharmony_ci /* We need to find a targetEUN to fold into. */ 3638c2ecf20Sopenharmony_ci targetEUN = NFTL_findfreeblock(nftl, 1); 3648c2ecf20Sopenharmony_ci if (targetEUN == BLOCK_NIL) { 3658c2ecf20Sopenharmony_ci /* Ouch. Now we're screwed. We need to do a 3668c2ecf20Sopenharmony_ci fold-in-place of another chain to make room 3678c2ecf20Sopenharmony_ci for this one. We need a better way of selecting 3688c2ecf20Sopenharmony_ci which chain to fold, because makefreeblock will 3698c2ecf20Sopenharmony_ci only ask us to fold the same one again. 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_ci printk(KERN_WARNING 3728c2ecf20Sopenharmony_ci "NFTL_findfreeblock(desperate) returns 0xffff.\n"); 3738c2ecf20Sopenharmony_ci return BLOCK_NIL; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci } else { 3768c2ecf20Sopenharmony_ci /* We put a fold mark in the chain we are folding only if we 3778c2ecf20Sopenharmony_ci fold in place to help the mount check code. If we do not fold in 3788c2ecf20Sopenharmony_ci place, it is possible to find the valid chain by selecting the 3798c2ecf20Sopenharmony_ci longer one */ 3808c2ecf20Sopenharmony_ci oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS); 3818c2ecf20Sopenharmony_ci oob.u.c.unused = 0xffffffff; 3828c2ecf20Sopenharmony_ci nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, 3838c2ecf20Sopenharmony_ci 8, &retlen, (char *)&oob.u); 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* OK. We now know the location of every block in the Virtual Unit Chain, 3878c2ecf20Sopenharmony_ci and the Erase Unit into which we are supposed to be copying. 3888c2ecf20Sopenharmony_ci Go for it. 3898c2ecf20Sopenharmony_ci */ 3908c2ecf20Sopenharmony_ci pr_debug("Folding chain %d into unit %d\n", thisVUC, targetEUN); 3918c2ecf20Sopenharmony_ci for (block = 0; block < nftl->EraseSize / 512 ; block++) { 3928c2ecf20Sopenharmony_ci unsigned char movebuf[512]; 3938c2ecf20Sopenharmony_ci int ret; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* If it's in the target EUN already, or if it's pending write, do nothing */ 3968c2ecf20Sopenharmony_ci if (BlockMap[block] == targetEUN || 3978c2ecf20Sopenharmony_ci (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) { 3988c2ecf20Sopenharmony_ci continue; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* copy only in non free block (free blocks can only 4028c2ecf20Sopenharmony_ci happen in case of media errors or deleted blocks) */ 4038c2ecf20Sopenharmony_ci if (BlockMap[block] == BLOCK_NIL) 4048c2ecf20Sopenharmony_ci continue; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci ret = mtd_read(mtd, 4078c2ecf20Sopenharmony_ci (nftl->EraseSize * BlockMap[block]) + (block * 512), 4088c2ecf20Sopenharmony_ci 512, 4098c2ecf20Sopenharmony_ci &retlen, 4108c2ecf20Sopenharmony_ci movebuf); 4118c2ecf20Sopenharmony_ci if (ret < 0 && !mtd_is_bitflip(ret)) { 4128c2ecf20Sopenharmony_ci ret = mtd_read(mtd, 4138c2ecf20Sopenharmony_ci (nftl->EraseSize * BlockMap[block]) + (block * 512), 4148c2ecf20Sopenharmony_ci 512, 4158c2ecf20Sopenharmony_ci &retlen, 4168c2ecf20Sopenharmony_ci movebuf); 4178c2ecf20Sopenharmony_ci if (ret != -EIO) 4188c2ecf20Sopenharmony_ci printk("Error went away on retry.\n"); 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci memset(&oob, 0xff, sizeof(struct nftl_oob)); 4218c2ecf20Sopenharmony_ci oob.b.Status = oob.b.Status1 = SECTOR_USED; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 4248c2ecf20Sopenharmony_ci (block * 512), 512, &retlen, movebuf, (char *)&oob); 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* add the header so that it is now a valid chain */ 4288c2ecf20Sopenharmony_ci oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); 4298c2ecf20Sopenharmony_ci oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = BLOCK_NIL; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8, 4328c2ecf20Sopenharmony_ci 8, &retlen, (char *)&oob.u); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */ 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* At this point, we have two different chains for this Virtual Unit, and no way to tell 4378c2ecf20Sopenharmony_ci them apart. If we crash now, we get confused. However, both contain the same data, so we 4388c2ecf20Sopenharmony_ci shouldn't actually lose data in this case. It's just that when we load up on a medium which 4398c2ecf20Sopenharmony_ci has duplicate chains, we need to free one of the chains because it's not necessary any more. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci thisEUN = nftl->EUNtable[thisVUC]; 4428c2ecf20Sopenharmony_ci pr_debug("Want to erase\n"); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* For each block in the old chain (except the targetEUN of course), 4458c2ecf20Sopenharmony_ci free it and make it available for future use */ 4468c2ecf20Sopenharmony_ci while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) { 4478c2ecf20Sopenharmony_ci unsigned int EUNtmp; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci EUNtmp = nftl->ReplUnitTable[thisEUN]; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (NFTL_formatblock(nftl, thisEUN) < 0) { 4528c2ecf20Sopenharmony_ci /* could not erase : mark block as reserved 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_ci nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED; 4558c2ecf20Sopenharmony_ci } else { 4568c2ecf20Sopenharmony_ci /* correctly erased : mark it as free */ 4578c2ecf20Sopenharmony_ci nftl->ReplUnitTable[thisEUN] = BLOCK_FREE; 4588c2ecf20Sopenharmony_ci nftl->numfreeEUNs++; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci thisEUN = EUNtmp; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* Make this the new start of chain for thisVUC */ 4648c2ecf20Sopenharmony_ci nftl->ReplUnitTable[targetEUN] = BLOCK_NIL; 4658c2ecf20Sopenharmony_ci nftl->EUNtable[thisVUC] = targetEUN; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci return targetEUN; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci /* This is the part that needs some cleverness applied. 4738c2ecf20Sopenharmony_ci For now, I'm doing the minimum applicable to actually 4748c2ecf20Sopenharmony_ci get the thing to work. 4758c2ecf20Sopenharmony_ci Wear-levelling and other clever stuff needs to be implemented 4768c2ecf20Sopenharmony_ci and we also need to do some assessment of the results when 4778c2ecf20Sopenharmony_ci the system loses power half-way through the routine. 4788c2ecf20Sopenharmony_ci */ 4798c2ecf20Sopenharmony_ci u16 LongestChain = 0; 4808c2ecf20Sopenharmony_ci u16 ChainLength = 0, thislen; 4818c2ecf20Sopenharmony_ci u16 chain, EUN; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) { 4848c2ecf20Sopenharmony_ci EUN = nftl->EUNtable[chain]; 4858c2ecf20Sopenharmony_ci thislen = 0; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci while (EUN <= nftl->lastEUN) { 4888c2ecf20Sopenharmony_ci thislen++; 4898c2ecf20Sopenharmony_ci //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN); 4908c2ecf20Sopenharmony_ci EUN = nftl->ReplUnitTable[EUN] & 0x7fff; 4918c2ecf20Sopenharmony_ci if (thislen > 0xff00) { 4928c2ecf20Sopenharmony_ci printk("Endless loop in Virtual Chain %d: Unit %x\n", 4938c2ecf20Sopenharmony_ci chain, EUN); 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci if (thislen > 0xff10) { 4968c2ecf20Sopenharmony_ci /* Actually, don't return failure. Just ignore this chain and 4978c2ecf20Sopenharmony_ci get on with it. */ 4988c2ecf20Sopenharmony_ci thislen = 0; 4998c2ecf20Sopenharmony_ci break; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if (thislen > ChainLength) { 5048c2ecf20Sopenharmony_ci //printk("New longest chain is %d with length %d\n", chain, thislen); 5058c2ecf20Sopenharmony_ci ChainLength = thislen; 5068c2ecf20Sopenharmony_ci LongestChain = chain; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (ChainLength < 2) { 5118c2ecf20Sopenharmony_ci printk(KERN_WARNING "No Virtual Unit Chains available for folding. " 5128c2ecf20Sopenharmony_ci "Failing request\n"); 5138c2ecf20Sopenharmony_ci return BLOCK_NIL; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return NFTL_foldchain (nftl, LongestChain, pendingblock); 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci/* NFTL_findwriteunit: Return the unit number into which we can write 5208c2ecf20Sopenharmony_ci for this block. Make it available if it isn't already 5218c2ecf20Sopenharmony_ci*/ 5228c2ecf20Sopenharmony_cistatic inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci u16 lastEUN; 5258c2ecf20Sopenharmony_ci u16 thisVUC = block / (nftl->EraseSize / 512); 5268c2ecf20Sopenharmony_ci struct mtd_info *mtd = nftl->mbd.mtd; 5278c2ecf20Sopenharmony_ci unsigned int writeEUN; 5288c2ecf20Sopenharmony_ci unsigned long blockofs = (block * 512) & (nftl->EraseSize -1); 5298c2ecf20Sopenharmony_ci size_t retlen; 5308c2ecf20Sopenharmony_ci int silly, silly2 = 3; 5318c2ecf20Sopenharmony_ci struct nftl_oob oob; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci do { 5348c2ecf20Sopenharmony_ci /* Scan the media to find a unit in the VUC which has 5358c2ecf20Sopenharmony_ci a free space for the block in question. 5368c2ecf20Sopenharmony_ci */ 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* This condition catches the 0x[7f]fff cases, as well as 5398c2ecf20Sopenharmony_ci being a sanity check for past-end-of-media access 5408c2ecf20Sopenharmony_ci */ 5418c2ecf20Sopenharmony_ci lastEUN = BLOCK_NIL; 5428c2ecf20Sopenharmony_ci writeEUN = nftl->EUNtable[thisVUC]; 5438c2ecf20Sopenharmony_ci silly = MAX_LOOPS; 5448c2ecf20Sopenharmony_ci while (writeEUN <= nftl->lastEUN) { 5458c2ecf20Sopenharmony_ci struct nftl_bci bci; 5468c2ecf20Sopenharmony_ci size_t retlen; 5478c2ecf20Sopenharmony_ci unsigned int status; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci lastEUN = writeEUN; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci nftl_read_oob(mtd, 5528c2ecf20Sopenharmony_ci (writeEUN * nftl->EraseSize) + blockofs, 5538c2ecf20Sopenharmony_ci 8, &retlen, (char *)&bci); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci pr_debug("Status of block %d in EUN %d is %x\n", 5568c2ecf20Sopenharmony_ci block , writeEUN, le16_to_cpu(bci.Status)); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci status = bci.Status | bci.Status1; 5598c2ecf20Sopenharmony_ci switch(status) { 5608c2ecf20Sopenharmony_ci case SECTOR_FREE: 5618c2ecf20Sopenharmony_ci return writeEUN; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci case SECTOR_DELETED: 5648c2ecf20Sopenharmony_ci case SECTOR_USED: 5658c2ecf20Sopenharmony_ci case SECTOR_IGNORE: 5668c2ecf20Sopenharmony_ci break; 5678c2ecf20Sopenharmony_ci default: 5688c2ecf20Sopenharmony_ci // Invalid block. Don't use it any more. Must implement. 5698c2ecf20Sopenharmony_ci break; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if (!silly--) { 5738c2ecf20Sopenharmony_ci printk(KERN_WARNING 5748c2ecf20Sopenharmony_ci "Infinite loop in Virtual Unit Chain 0x%x\n", 5758c2ecf20Sopenharmony_ci thisVUC); 5768c2ecf20Sopenharmony_ci return BLOCK_NIL; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* Skip to next block in chain */ 5808c2ecf20Sopenharmony_ci writeEUN = nftl->ReplUnitTable[writeEUN]; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci /* OK. We didn't find one in the existing chain, or there 5848c2ecf20Sopenharmony_ci is no existing chain. */ 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci /* Try to find an already-free block */ 5878c2ecf20Sopenharmony_ci writeEUN = NFTL_findfreeblock(nftl, 0); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci if (writeEUN == BLOCK_NIL) { 5908c2ecf20Sopenharmony_ci /* That didn't work - there were no free blocks just 5918c2ecf20Sopenharmony_ci waiting to be picked up. We're going to have to fold 5928c2ecf20Sopenharmony_ci a chain to make room. 5938c2ecf20Sopenharmony_ci */ 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci /* First remember the start of this chain */ 5968c2ecf20Sopenharmony_ci //u16 startEUN = nftl->EUNtable[thisVUC]; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC); 5998c2ecf20Sopenharmony_ci writeEUN = NFTL_makefreeblock(nftl, BLOCK_NIL); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci if (writeEUN == BLOCK_NIL) { 6028c2ecf20Sopenharmony_ci /* OK, we accept that the above comment is 6038c2ecf20Sopenharmony_ci lying - there may have been free blocks 6048c2ecf20Sopenharmony_ci last time we called NFTL_findfreeblock(), 6058c2ecf20Sopenharmony_ci but they are reserved for when we're 6068c2ecf20Sopenharmony_ci desperate. Well, now we're desperate. 6078c2ecf20Sopenharmony_ci */ 6088c2ecf20Sopenharmony_ci pr_debug("Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC); 6098c2ecf20Sopenharmony_ci writeEUN = NFTL_findfreeblock(nftl, 1); 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci if (writeEUN == BLOCK_NIL) { 6128c2ecf20Sopenharmony_ci /* Ouch. This should never happen - we should 6138c2ecf20Sopenharmony_ci always be able to make some room somehow. 6148c2ecf20Sopenharmony_ci If we get here, we've allocated more storage 6158c2ecf20Sopenharmony_ci space than actual media, or our makefreeblock 6168c2ecf20Sopenharmony_ci routine is missing something. 6178c2ecf20Sopenharmony_ci */ 6188c2ecf20Sopenharmony_ci printk(KERN_WARNING "Cannot make free space.\n"); 6198c2ecf20Sopenharmony_ci return BLOCK_NIL; 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci //printk("Restarting scan\n"); 6228c2ecf20Sopenharmony_ci lastEUN = BLOCK_NIL; 6238c2ecf20Sopenharmony_ci continue; 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci /* We've found a free block. Insert it into the chain. */ 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci if (lastEUN != BLOCK_NIL) { 6298c2ecf20Sopenharmony_ci thisVUC |= 0x8000; /* It's a replacement block */ 6308c2ecf20Sopenharmony_ci } else { 6318c2ecf20Sopenharmony_ci /* The first block in a new chain */ 6328c2ecf20Sopenharmony_ci nftl->EUNtable[thisVUC] = writeEUN; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci /* set up the actual EUN we're writing into */ 6368c2ecf20Sopenharmony_ci /* Both in our cache... */ 6378c2ecf20Sopenharmony_ci nftl->ReplUnitTable[writeEUN] = BLOCK_NIL; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci /* ... and on the flash itself */ 6408c2ecf20Sopenharmony_ci nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, 6418c2ecf20Sopenharmony_ci &retlen, (char *)&oob.u); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, 6468c2ecf20Sopenharmony_ci &retlen, (char *)&oob.u); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci /* we link the new block to the chain only after the 6498c2ecf20Sopenharmony_ci block is ready. It avoids the case where the chain 6508c2ecf20Sopenharmony_ci could point to a free block */ 6518c2ecf20Sopenharmony_ci if (lastEUN != BLOCK_NIL) { 6528c2ecf20Sopenharmony_ci /* Both in our cache... */ 6538c2ecf20Sopenharmony_ci nftl->ReplUnitTable[lastEUN] = writeEUN; 6548c2ecf20Sopenharmony_ci /* ... and on the flash itself */ 6558c2ecf20Sopenharmony_ci nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8, 6568c2ecf20Sopenharmony_ci 8, &retlen, (char *)&oob.u); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum 6598c2ecf20Sopenharmony_ci = cpu_to_le16(writeEUN); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8, 6628c2ecf20Sopenharmony_ci 8, &retlen, (char *)&oob.u); 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci return writeEUN; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci } while (silly2--); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n", 6708c2ecf20Sopenharmony_ci thisVUC); 6718c2ecf20Sopenharmony_ci return BLOCK_NIL; 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, 6758c2ecf20Sopenharmony_ci char *buffer) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci struct NFTLrecord *nftl = (void *)mbd; 6788c2ecf20Sopenharmony_ci u16 writeEUN; 6798c2ecf20Sopenharmony_ci unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); 6808c2ecf20Sopenharmony_ci size_t retlen; 6818c2ecf20Sopenharmony_ci struct nftl_oob oob; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci writeEUN = NFTL_findwriteunit(nftl, block); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (writeEUN == BLOCK_NIL) { 6868c2ecf20Sopenharmony_ci printk(KERN_WARNING 6878c2ecf20Sopenharmony_ci "NFTL_writeblock(): Cannot find block to write to\n"); 6888c2ecf20Sopenharmony_ci /* If we _still_ haven't got a block to use, we're screwed */ 6898c2ecf20Sopenharmony_ci return 1; 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci memset(&oob, 0xff, sizeof(struct nftl_oob)); 6938c2ecf20Sopenharmony_ci oob.b.Status = oob.b.Status1 = SECTOR_USED; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, 6968c2ecf20Sopenharmony_ci 512, &retlen, (char *)buffer, (char *)&oob); 6978c2ecf20Sopenharmony_ci return 0; 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci#endif /* CONFIG_NFTL_RW */ 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_cistatic int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, 7028c2ecf20Sopenharmony_ci char *buffer) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci struct NFTLrecord *nftl = (void *)mbd; 7058c2ecf20Sopenharmony_ci struct mtd_info *mtd = nftl->mbd.mtd; 7068c2ecf20Sopenharmony_ci u16 lastgoodEUN; 7078c2ecf20Sopenharmony_ci u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)]; 7088c2ecf20Sopenharmony_ci unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); 7098c2ecf20Sopenharmony_ci unsigned int status; 7108c2ecf20Sopenharmony_ci int silly = MAX_LOOPS; 7118c2ecf20Sopenharmony_ci size_t retlen; 7128c2ecf20Sopenharmony_ci struct nftl_bci bci; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci lastgoodEUN = BLOCK_NIL; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci if (thisEUN != BLOCK_NIL) { 7178c2ecf20Sopenharmony_ci while (thisEUN < nftl->nb_blocks) { 7188c2ecf20Sopenharmony_ci if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) + 7198c2ecf20Sopenharmony_ci blockofs, 8, &retlen, 7208c2ecf20Sopenharmony_ci (char *)&bci) < 0) 7218c2ecf20Sopenharmony_ci status = SECTOR_IGNORE; 7228c2ecf20Sopenharmony_ci else 7238c2ecf20Sopenharmony_ci status = bci.Status | bci.Status1; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci switch (status) { 7268c2ecf20Sopenharmony_ci case SECTOR_FREE: 7278c2ecf20Sopenharmony_ci /* no modification of a sector should follow a free sector */ 7288c2ecf20Sopenharmony_ci goto the_end; 7298c2ecf20Sopenharmony_ci case SECTOR_DELETED: 7308c2ecf20Sopenharmony_ci lastgoodEUN = BLOCK_NIL; 7318c2ecf20Sopenharmony_ci break; 7328c2ecf20Sopenharmony_ci case SECTOR_USED: 7338c2ecf20Sopenharmony_ci lastgoodEUN = thisEUN; 7348c2ecf20Sopenharmony_ci break; 7358c2ecf20Sopenharmony_ci case SECTOR_IGNORE: 7368c2ecf20Sopenharmony_ci break; 7378c2ecf20Sopenharmony_ci default: 7388c2ecf20Sopenharmony_ci printk("Unknown status for block %ld in EUN %d: %x\n", 7398c2ecf20Sopenharmony_ci block, thisEUN, status); 7408c2ecf20Sopenharmony_ci break; 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci if (!silly--) { 7448c2ecf20Sopenharmony_ci printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n", 7458c2ecf20Sopenharmony_ci block / (nftl->EraseSize / 512)); 7468c2ecf20Sopenharmony_ci return 1; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci thisEUN = nftl->ReplUnitTable[thisEUN]; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci the_end: 7538c2ecf20Sopenharmony_ci if (lastgoodEUN == BLOCK_NIL) { 7548c2ecf20Sopenharmony_ci /* the requested block is not on the media, return all 0x00 */ 7558c2ecf20Sopenharmony_ci memset(buffer, 0, 512); 7568c2ecf20Sopenharmony_ci } else { 7578c2ecf20Sopenharmony_ci loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs; 7588c2ecf20Sopenharmony_ci size_t retlen; 7598c2ecf20Sopenharmony_ci int res = mtd_read(mtd, ptr, 512, &retlen, buffer); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci if (res < 0 && !mtd_is_bitflip(res)) 7628c2ecf20Sopenharmony_ci return -EIO; 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci return 0; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cistatic int nftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) 7688c2ecf20Sopenharmony_ci{ 7698c2ecf20Sopenharmony_ci struct NFTLrecord *nftl = (void *)dev; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci geo->heads = nftl->heads; 7728c2ecf20Sopenharmony_ci geo->sectors = nftl->sectors; 7738c2ecf20Sopenharmony_ci geo->cylinders = nftl->cylinders; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci return 0; 7768c2ecf20Sopenharmony_ci} 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci/**************************************************************************** 7798c2ecf20Sopenharmony_ci * 7808c2ecf20Sopenharmony_ci * Module stuff 7818c2ecf20Sopenharmony_ci * 7828c2ecf20Sopenharmony_ci ****************************************************************************/ 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_cistatic struct mtd_blktrans_ops nftl_tr = { 7868c2ecf20Sopenharmony_ci .name = "nftl", 7878c2ecf20Sopenharmony_ci .major = NFTL_MAJOR, 7888c2ecf20Sopenharmony_ci .part_bits = NFTL_PARTN_BITS, 7898c2ecf20Sopenharmony_ci .blksize = 512, 7908c2ecf20Sopenharmony_ci .getgeo = nftl_getgeo, 7918c2ecf20Sopenharmony_ci .readsect = nftl_readblock, 7928c2ecf20Sopenharmony_ci#ifdef CONFIG_NFTL_RW 7938c2ecf20Sopenharmony_ci .writesect = nftl_writeblock, 7948c2ecf20Sopenharmony_ci#endif 7958c2ecf20Sopenharmony_ci .add_mtd = nftl_add_mtd, 7968c2ecf20Sopenharmony_ci .remove_dev = nftl_remove_dev, 7978c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 7988c2ecf20Sopenharmony_ci}; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_cistatic int __init init_nftl(void) 8018c2ecf20Sopenharmony_ci{ 8028c2ecf20Sopenharmony_ci return register_mtd_blktrans(&nftl_tr); 8038c2ecf20Sopenharmony_ci} 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_cistatic void __exit cleanup_nftl(void) 8068c2ecf20Sopenharmony_ci{ 8078c2ecf20Sopenharmony_ci deregister_mtd_blktrans(&nftl_tr); 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_cimodule_init(init_nftl); 8118c2ecf20Sopenharmony_cimodule_exit(cleanup_nftl); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 8148c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al."); 8158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium"); 8168c2ecf20Sopenharmony_ciMODULE_ALIAS_BLOCKDEV_MAJOR(NFTL_MAJOR); 817